mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 20:00:47 +00:00
feat(provider): add CodeBuddy CN provider (copilot.tencent.com)
Add Tencent CodeBuddy CN (codebuddy-cn) OAuth provider with full support: OAuth login (GET poll with state query param), token refresh, 15-model catalog, /v2 inference endpoint, forced streaming, OpenAI-style reasoning, and per-model capabilities. Renamed from codebuddy to codebuddy-cn to allow a future codebuddy-ai variant. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
decolua
co-authored by
Cursor
parent
db4499d6df
commit
efd20be8d8
@@ -106,7 +106,7 @@ export const CLINE_CONFIG = { ...PROVIDER_OAUTH["cline"] };
|
||||
export const GITLAB_CONFIG = { ...PROVIDER_OAUTH["gitlab"] };
|
||||
|
||||
// CodeBuddy (Tencent) OAuth Configuration (Browser OAuth Polling Flow)
|
||||
export const CODEBUDDY_CONFIG = { ...PROVIDER_OAUTH["codebuddy"] };
|
||||
export const CODEBUDDY_CONFIG = { ...PROVIDER_OAUTH["codebuddy-cn"] };
|
||||
|
||||
// OAuth timeout (5 minutes)
|
||||
export const OAUTH_TIMEOUT = 300000;
|
||||
@@ -128,5 +128,5 @@ export const PROVIDERS = {
|
||||
KILOCODE: "kilocode",
|
||||
CLINE: "cline",
|
||||
GITLAB: "gitlab",
|
||||
CODEBUDDY: "codebuddy",
|
||||
CODEBUDDY: "codebuddy-cn",
|
||||
};
|
||||
|
||||
@@ -1180,7 +1180,7 @@ const PROVIDERS = {
|
||||
// 1. POST stateUrl → get { state, authUrl }
|
||||
// 2. Open authUrl in browser
|
||||
// 3. Poll tokenUrl with state until success (code 0) or timeout
|
||||
codebuddy: {
|
||||
"codebuddy-cn": {
|
||||
config: CODEBUDDY_CONFIG,
|
||||
flowType: "device_code",
|
||||
requestDeviceCode: async (config) => {
|
||||
@@ -1212,23 +1212,25 @@ const PROVIDERS = {
|
||||
};
|
||||
},
|
||||
pollToken: async (config, deviceCode) => {
|
||||
const response = await fetch(config.tokenUrl, {
|
||||
method: "POST",
|
||||
// CodeBuddy polls the token endpoint via GET with the state as a query
|
||||
// param (not POST/body) — matches the official CLI's /v2/plugin/auth/token?state=...
|
||||
const response = await fetch(`${config.tokenUrl}?state=${encodeURIComponent(deviceCode)}`, {
|
||||
method: "GET",
|
||||
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-No-Enterprise-Id": "true",
|
||||
"X-No-Department-Info": "true",
|
||||
"X-Product": "SaaS",
|
||||
},
|
||||
body: JSON.stringify({ state: deviceCode }),
|
||||
});
|
||||
if (!response.ok) return { ok: false, data: { error: "request_failed" } };
|
||||
const data = await response.json();
|
||||
// code 11217 = pending, code 0 = success
|
||||
// code 11217 = pending (RetryFetchToken), code 0 = success
|
||||
if (data.code === 0 && data.data?.accessToken) {
|
||||
return {
|
||||
ok: true,
|
||||
@@ -1236,6 +1238,7 @@ const PROVIDERS = {
|
||||
access_token: data.data.accessToken,
|
||||
refresh_token: data.data.refreshToken || "",
|
||||
token_type: data.data.tokenType || "Bearer",
|
||||
expires_in: data.data.expiresIn,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1245,7 +1248,7 @@ const PROVIDERS = {
|
||||
mapTokens: (tokens) => ({
|
||||
accessToken: tokens.access_token,
|
||||
refreshToken: tokens.refresh_token,
|
||||
expiresIn: 86400,
|
||||
expiresIn: tokens.expires_in || 86400,
|
||||
providerSpecificData: {},
|
||||
}),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user