Spaces:
Runtime error
Runtime error
File size: 4,002 Bytes
cd8bd0a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | import { CODEBUDDY_CN_CONFIG } from "../constants/oauth";
/**
* CodeBuddy CN (Tencent — copilot.tencent.com) — custom device-auth flow.
*
* 1. POST stateUrl → { code: 0, data: { state, authUrl } }
* 2. Open authUrl in the browser
* 3. GET tokenUrl?state=<state> until { code: 0, data.accessToken } (11217 = pending)
*
* Mirrors the official CodeBuddy CLI's distinguishing detail: poll is GET with
* the state as a query param, NOT POST/body.
*/
type CodeBuddyConfig = typeof CODEBUDDY_CN_CONFIG;
interface CodeBuddyDeviceCodeResponse {
device_code: string;
user_code: string;
verification_uri: string;
verification_uri_complete: string;
expires_in: number;
interval: number;
}
interface CodeBuddyTokens {
access_token: string;
refresh_token: string;
token_type: string;
expires_in?: number;
}
interface CodeBuddyPollResult {
ok: boolean;
data: Record<string, unknown> | CodeBuddyTokens;
}
export const codebuddyCn = {
config: CODEBUDDY_CN_CONFIG,
flowType: "device_code" as const,
requestDeviceCode: async (config: CodeBuddyConfig): Promise<CodeBuddyDeviceCodeResponse> => {
const response = await fetch(config.stateUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"User-Agent": config.userAgent,
"X-Requested-With": "XMLHttpRequest",
"X-Domain": "copilot.tencent.com",
"X-No-Authorization": "true",
"X-No-User-Id": "true",
"X-Product": "SaaS",
},
body: JSON.stringify({ platform: config.platform }),
});
if (!response.ok) {
const err = await response.text();
throw new Error(`CodeBuddy state request failed (${response.status})`);
}
const json = (await response.json()) as { code?: number; data?: any; msg?: string };
if (json.code !== 0 || !json.data?.state) {
throw new Error(`CodeBuddy state error: ${json.msg || "no state in response"}`);
}
const state = String(json.data.state);
const authUrl = String(json.data.authUrl || json.data.url || "");
return {
device_code: state,
user_code: state,
verification_uri: authUrl,
verification_uri_complete: authUrl,
expires_in: 600,
interval: Math.max(1, Math.floor((config.pollInterval || 5000) / 1000)),
};
},
pollToken: async (config: CodeBuddyConfig, deviceCode: string): Promise<CodeBuddyPollResult> => {
// GET with state as a query param (not POST/body) — matches the official CLI's
// /v2/plugin/auth/token?state=... endpoint shape.
const response = await fetch(
`${config.tokenUrl}?state=${encodeURIComponent(deviceCode)}`,
{
method: "GET",
headers: {
Accept: "application/json",
"User-Agent": config.userAgent,
"X-Requested-With": "XMLHttpRequest",
"X-Domain": "copilot.tencent.com",
"X-No-Authorization": "true",
"X-No-User-Id": "true",
"X-No-Enterprise-Id": "true",
"X-No-Department-Info": "true",
"X-Product": "SaaS",
},
}
);
if (!response.ok) return { ok: false, data: { error: "request_failed" } };
const data = (await response.json()) as { code?: number; data?: any; msg?: string };
// code 11217 = pending (RetryFetchToken), code 0 = success
if (data.code === 0 && data.data?.accessToken) {
return {
ok: true,
data: {
access_token: data.data.accessToken,
refresh_token: data.data.refreshToken || "",
token_type: data.data.tokenType || "Bearer",
expires_in: data.data.expiresIn,
},
};
}
return { ok: false, data: { code: data.code, msg: data.msg } };
},
mapTokens: (tokens: CodeBuddyTokens) => ({
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
expiresIn: tokens.expires_in || 86400,
providerSpecificData: {},
}),
};
export default codebuddyCn;
|