mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-10-04 02:52:55 +08:00
- Introduced a new Grok authentication plugin, including essential files such as main.ts, provider.ts, and resources.ts. - Implemented OAuth2 device authorization flow in oauth.ts, allowing users to sign in with xAI. - Added model discovery and quota management functionalities in models.ts and resources.ts. - Created a JSON configuration file (plugin.json) for plugin metadata and permissions. - Included SVG asset for the Grok icon. - Developed comprehensive tests in grok_test.ts to ensure functionality and reliability of the plugin.
149 lines
4.7 KiB
TypeScript
149 lines
4.7 KiB
TypeScript
import type { JsonValue, PluginContext } from "cursor-byok:plugin";
|
|
import type { OAuth2AddMethod, OAuth2Begin, OAuth2Poll } from "cursor-byok:resource";
|
|
import { credentialDraft } from "./resources.ts";
|
|
|
|
const CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
|
|
const DEVICE_CODE_URL = "https://auth.x.ai/oauth2/device/code";
|
|
const TOKEN_URL = "https://auth.x.ai/oauth2/token";
|
|
const SCOPE = "openid profile email offline_access grok-cli:access api:access";
|
|
|
|
type Session = {
|
|
deviceCode: string;
|
|
};
|
|
|
|
function object(value: unknown): Record<string, unknown> | null {
|
|
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
? value as Record<string, unknown>
|
|
: null;
|
|
}
|
|
|
|
function text(value: unknown): string | null {
|
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
}
|
|
|
|
function number(value: unknown): number | null {
|
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
if (typeof value === "string" && value.trim()) {
|
|
const parsed = Number(value);
|
|
return Number.isFinite(parsed) ? parsed : null;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function parseBody(body: string): Record<string, unknown> {
|
|
try {
|
|
return object(JSON.parse(body)) ?? {};
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
function parseSession(value: JsonValue): Session {
|
|
const session = object(value);
|
|
const deviceCode = text(session?.deviceCode);
|
|
if (!deviceCode) throw new Error("Grok OAuth session is invalid");
|
|
return { deviceCode };
|
|
}
|
|
|
|
async function begin(context: PluginContext): Promise<OAuth2Begin> {
|
|
const response = await context.network.fetch(DEVICE_CODE_URL, {
|
|
method: "POST",
|
|
headers: {
|
|
accept: "application/json",
|
|
"content-type": "application/x-www-form-urlencoded",
|
|
},
|
|
body: new URLSearchParams({ client_id: CLIENT_ID, scope: SCOPE }).toString(),
|
|
});
|
|
const body = parseBody(response.body);
|
|
if (response.status < 200 || response.status >= 300) {
|
|
throw new Error(
|
|
`Failed to request xAI device code (HTTP ${response.status}): ${response.body}`,
|
|
);
|
|
}
|
|
const deviceCode = text(body.device_code);
|
|
const userCode = text(body.user_code);
|
|
const verificationUrl = text(body.verification_uri);
|
|
if (!deviceCode || !userCode || !verificationUrl) {
|
|
throw new Error("xAI device authorization response is incomplete");
|
|
}
|
|
const session: Session = { deviceCode };
|
|
return {
|
|
session: session as unknown as JsonValue,
|
|
userCode,
|
|
verificationUrl,
|
|
...(text(body.verification_uri_complete)
|
|
? { verificationUrlComplete: text(body.verification_uri_complete)! }
|
|
: {}),
|
|
expiresAtMs: Date.now() + Math.max(1, number(body.expires_in) ?? 900) * 1000,
|
|
pollIntervalMs: Math.max(1, number(body.interval) ?? 5) * 1000,
|
|
};
|
|
}
|
|
|
|
async function poll(sessionValue: JsonValue, context: PluginContext): Promise<OAuth2Poll> {
|
|
const session = parseSession(sessionValue);
|
|
const response = await context.network.fetch(TOKEN_URL, {
|
|
method: "POST",
|
|
headers: {
|
|
accept: "application/json",
|
|
"content-type": "application/x-www-form-urlencoded",
|
|
},
|
|
body: new URLSearchParams({
|
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
client_id: CLIENT_ID,
|
|
device_code: session.deviceCode,
|
|
}).toString(),
|
|
});
|
|
const body = parseBody(response.body);
|
|
if (response.status >= 200 && response.status < 300) {
|
|
const accessToken = text(body.access_token);
|
|
if (!accessToken) {
|
|
return { status: "failed", message: "xAI token response is missing access_token" };
|
|
}
|
|
return {
|
|
status: "completed",
|
|
resources: [
|
|
await credentialDraft({
|
|
accessToken,
|
|
refreshToken: text(body.refresh_token),
|
|
displayName: null,
|
|
}),
|
|
],
|
|
};
|
|
}
|
|
const code = text(body.error) ?? "";
|
|
const message = text(body.error_description);
|
|
switch (code) {
|
|
case "authorization_pending":
|
|
return { status: "pending" };
|
|
case "slow_down":
|
|
return { status: "slow-down" };
|
|
case "expired_token":
|
|
return { status: "failed", message: message ?? "Device authorization code expired" };
|
|
case "access_denied":
|
|
return { status: "denied", ...(message ? { message } : {}) };
|
|
default:
|
|
return {
|
|
status: "failed",
|
|
message: message ??
|
|
(code
|
|
? `OAuth error: ${code}`
|
|
: `xAI device authorization failed (HTTP ${response.status})`),
|
|
};
|
|
}
|
|
}
|
|
|
|
export const grokDeviceOAuth: OAuth2AddMethod = {
|
|
type: "oauth2.0",
|
|
id: "xai-device",
|
|
displayName: {
|
|
"en-US": "Sign in with xAI",
|
|
"zh-CN": "使用 xAI 登录",
|
|
},
|
|
description: {
|
|
"en-US": "Authorize this device with xAI, then add the resulting Grok account.",
|
|
"zh-CN": "在 xAI 完成设备授权后,自动添加对应的 Grok 账号。",
|
|
},
|
|
begin,
|
|
poll,
|
|
};
|