diff --git a/custom-server.js b/custom-server.js index 764a2df6..6e39683f 100644 --- a/custom-server.js +++ b/custom-server.js @@ -10,10 +10,15 @@ http.createServer = (...args) => { const rest = args.filter((a) => typeof a !== "function"); if (!handler) return origCreate(...args); const wrapped = (req, res) => { - const ip = req.socket && req.socket.remoteAddress ? req.socket.remoteAddress : ""; - // Forwarding headers present = request arrived via a reverse proxy; loopback - // socket is the proxy hop, not the end-user, so it must not be trusted as local. - const viaProxy = !!(req.headers["x-forwarded-for"] || req.headers["x-real-ip"]); + const socketIp = req.socket && req.socket.remoteAddress ? req.socket.remoteAddress : ""; + const xff = req.headers["x-forwarded-for"]; + const xRealIp = req.headers["x-real-ip"]; + const viaProxy = !!(xff || xRealIp); + const isLoopbackProxy = socketIp === "127.0.0.1" || socketIp === "::1" || socketIp === "::ffff:127.0.0.1"; + // Trust forwarding headers only when the TCP peer is a local reverse proxy. + // Direct/public sockets remain keyed by the unspoofable peer address. + const proxyIp = xRealIp || (xff ? String(xff).split(",")[0].trim() : ""); + const ip = isLoopbackProxy && proxyIp ? proxyIp : socketIp; delete req.headers["x-9r-real-ip"]; delete req.headers["x-forwarded-for"]; delete req.headers["x-9r-via-proxy"]; diff --git a/open-sse/executors/codebuddy-cn.js b/open-sse/executors/codebuddy-cn.js new file mode 100644 index 00000000..46e51951 --- /dev/null +++ b/open-sse/executors/codebuddy-cn.js @@ -0,0 +1,36 @@ +import { DefaultExecutor } from "./default.js"; + +/** + * CodeBuddyExecutor — talks to https://copilot.tencent.com/v2/chat/completions + * + * CodeBuddy is OpenAI-compatible but rejects non-stream chat requests + * (HTTP 400, code 11101 "Non-stream chat request is currently not supported"). + * The same-format (openai→openai) translator path leaves body.stream as the + * client sent it, so we force it true here — 9router still re-aggregates the + * SSE into a JSON response for non-streaming clients. + */ +export class CodeBuddyExecutor extends DefaultExecutor { + constructor() { + super("codebuddy-cn"); + } + + transformRequest(model, body, stream, credentials) { + const transformed = super.transformRequest(model, body, stream, credentials); + transformed.stream = true; + + // CodeBuddy only surfaces model reasoning when the request carries the CLI's + // OpenAI-style params: reasoning_effort + reasoning_summary:"auto". 9router's + // thinking pipeline sets reasoning_effort only when the client asks, and never + // sets reasoning_summary — so reasoning never shows. Mirror the CLI here. + const eff = transformed.reasoning_effort; + if (eff === "none" || eff === "off") { + delete transformed.reasoning_effort; // gateway has no "none" — just omit + } else { + if (!eff) transformed.reasoning_effort = "medium"; + transformed.reasoning_summary = "auto"; + } + return transformed; + } +} + +export default CodeBuddyExecutor; diff --git a/open-sse/executors/index.js b/open-sse/executors/index.js index bac26447..77d12fb3 100644 --- a/open-sse/executors/index.js +++ b/open-sse/executors/index.js @@ -17,6 +17,7 @@ import { OllamaLocalExecutor } from "./ollama-local.js"; import { CommandCodeExecutor } from "./commandcode.js"; import { XiaomiTokenplanExecutor } from "./xiaomi-tokenplan.js"; import { MimoFreeExecutor } from "./mimo-free.js"; +import { CodeBuddyExecutor } from "./codebuddy-cn.js"; import { DefaultExecutor } from "./default.js"; const executors = { @@ -42,6 +43,7 @@ const executors = { "xiaomi-tokenplan": new XiaomiTokenplanExecutor(), "mimo-free": new MimoFreeExecutor(), mmf: new MimoFreeExecutor(), // Alias for mimo-free + "codebuddy-cn": new CodeBuddyExecutor(), }; const defaultCache = new Map(); @@ -77,3 +79,4 @@ export { OllamaLocalExecutor } from "./ollama-local.js"; export { CommandCodeExecutor } from "./commandcode.js"; export { XiaomiTokenplanExecutor } from "./xiaomi-tokenplan.js"; export { MimoFreeExecutor } from "./mimo-free.js"; +export { CodeBuddyExecutor } from "./codebuddy-cn.js"; diff --git a/open-sse/providers/capabilities.js b/open-sse/providers/capabilities.js index 77220317..c49536b9 100644 --- a/open-sse/providers/capabilities.js +++ b/open-sse/providers/capabilities.js @@ -92,7 +92,30 @@ export const MODEL_CAPABILITIES = { /** * Provider-specific capability overrides. Keyed by provider alias/id. */ -export const PROVIDER_CAPABILITIES = {}; +export const PROVIDER_CAPABILITIES = { + // CodeBuddy.cn — authoritative per-model metadata from the gateway's model + // config (contextWindow=maxInputTokens, maxOutput=maxOutputTokens, vision= + // supportsImages). Every model reasons via OpenAI-style reasoning_effort + // (see registry thinkingFormat). `onlyReasoning` models can't turn thinking + // off → thinkingCanDisable:false (clamped to minimal instead of disabled). + "codebuddy-cn": { + "glm-5.2": { reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 1000000, maxOutput: 48000 }, + "glm-5.1": { reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 200000, maxOutput: 48000 }, + "glm-5.0": { reasoning: true, thinkingFormat: "openai", contextWindow: 200000, maxOutput: 48000 }, + "glm-5.0-turbo": { reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 200000, maxOutput: 48000 }, + "glm-5v-turbo": { vision: true, reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 200000, maxOutput: 38000 }, + "glm-4.7": { reasoning: true, thinkingFormat: "openai", contextWindow: 200000, maxOutput: 48000 }, + "minimax-m3": { vision: true, reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 512000, maxOutput: 48000 }, + "minimax-m2.7": { vision: true, reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 200000, maxOutput: 48000 }, + "kimi-k2.7": { vision: true, reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 256000, maxOutput: 32000 }, + "kimi-k2.6": { vision: true, reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 256000, maxOutput: 32000 }, + "kimi-k2.5": { vision: true, reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 164000, maxOutput: 32000 }, + "hy3-preview": { vision: true, reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 192000, maxOutput: 64000 }, + "deepseek-v4-pro": { vision: true, reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 1000000, maxOutput: 50000 }, + "deepseek-v4-flash": { vision: true, reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 1000000, maxOutput: 50000 }, + "deepseek-v3-2-volc": { reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 96000, maxOutput: 32000 }, + }, +}; /** * Pattern fallback — glob (* = wildcard), matched case-insensitively and diff --git a/open-sse/providers/registry/codebuddy-cn.js b/open-sse/providers/registry/codebuddy-cn.js new file mode 100644 index 00000000..8b12a1a7 --- /dev/null +++ b/open-sse/providers/registry/codebuddy-cn.js @@ -0,0 +1,62 @@ +export default { + id: "codebuddy-cn", + hidden: false, + priority: 90, + display: { + name: "CodeBuddy CN", + icon: "smart_toy", + color: "#006EFF", + website: "https://copilot.tencent.com", + notice: { + signupUrl: "https://copilot.tencent.com", + }, + }, + category: "oauth", + transport: { + baseUrl: "https://copilot.tencent.com/v2/chat/completions", + forceStream: true, + // CodeBuddy is a unified OpenAI-compatible gateway: every model (GLM, Kimi, + // MiniMax, DeepSeek, Hunyuan) takes reasoning via OpenAI-style reasoning_effort, + // not its vendor-native thinking shape. Force the openai thinking format. + thinkingFormat: "openai", + headers: { + "User-Agent": "CLI/2.108.1 CodeBuddy/2.108.1", + "X-Product": "SaaS", + "X-IDE-Type": "CLI", + "X-IDE-Name": "CLI", + "x-requested-with": "XMLHttpRequest", + "x-codebuddy-request": "1", + }, + auth: { + combined: true, + header: "Authorization", + scheme: "bearer", + }, + }, + models: [ + { id: "glm-5.2", name: "GLM-5.2" }, + { id: "glm-5.1", name: "GLM-5.1" }, + { id: "glm-5.0", name: "GLM-5.0" }, + { id: "glm-5.0-turbo", name: "GLM-5.0-Turbo" }, + { id: "glm-5v-turbo", name: "GLM-5v-Turbo" }, + { id: "glm-4.7", name: "GLM-4.7" }, + { id: "minimax-m3", name: "MiniMax-M3" }, + { id: "minimax-m2.7", name: "MiniMax-M2.7" }, + { id: "kimi-k2.7", name: "Kimi-K2.7-Code" }, + { id: "kimi-k2.6", name: "Kimi-K2.6" }, + { id: "kimi-k2.5", name: "Kimi-K2.5" }, + { id: "hy3-preview", name: "Hy3 Preview" }, + { id: "deepseek-v4-pro", name: "DeepSeek-V4-Pro" }, + { id: "deepseek-v4-flash", name: "DeepSeek-V4-Flash" }, + { id: "deepseek-v3-2-volc", name: "DeepSeek-V3.2" }, + ], + oauth: { + baseUrl: "https://copilot.tencent.com", + stateUrl: "https://copilot.tencent.com/v2/plugin/auth/state", + tokenUrl: "https://copilot.tencent.com/v2/plugin/auth/token", + refreshUrl: "https://copilot.tencent.com/v2/plugin/auth/token/refresh", + userAgent: "CLI/2.63.2 CodeBuddy/2.63.2", + platform: "CLI", + pollInterval: 5000, + }, +}; diff --git a/open-sse/providers/registry/codebuddy.js b/open-sse/providers/registry/codebuddy.js deleted file mode 100644 index 4bf2c811..00000000 --- a/open-sse/providers/registry/codebuddy.js +++ /dev/null @@ -1,32 +0,0 @@ -export default { - id: "codebuddy", - hidden: true, - priority: 90, - display: { - name: "CodeBuddy", - icon: "smart_toy", - color: "#006EFF", - website: "https://copilot.tencent.com", - notice: { - signupUrl: "https://copilot.tencent.com", - }, - }, - category: "oauth", - transport: { - baseUrl: "https://copilot.tencent.com/v1/chat/completions", - auth: { - combined: true, - header: "Authorization", - scheme: "bearer", - }, - }, - oauth: { - baseUrl: "https://copilot.tencent.com", - stateUrl: "https://copilot.tencent.com/v2/plugin/auth/state", - tokenUrl: "https://copilot.tencent.com/v2/plugin/auth/token", - refreshUrl: "https://copilot.tencent.com/v2/plugin/auth/token/refresh", - userAgent: "CLI/2.63.2 CodeBuddy/2.63.2", - platform: "CLI", - pollInterval: 5000, - }, -}; diff --git a/open-sse/providers/registry/index.js b/open-sse/providers/registry/index.js index a45bb1e3..c9dca60d 100644 --- a/open-sse/providers/registry/index.js +++ b/open-sse/providers/registry/index.js @@ -16,7 +16,7 @@ import p13 from "./chutes.js"; import p14 from "./claude.js"; import p15 from "./cline.js"; import p16 from "./cloudflare-ai.js"; -import p17 from "./codebuddy.js"; +import p17 from "./codebuddy-cn.js"; import p18 from "./codex.js"; import p19 from "./cohere.js"; import p20 from "./comfyui.js"; diff --git a/open-sse/services/tokenRefresh.js b/open-sse/services/tokenRefresh.js index 10d8efea..f759493e 100644 --- a/open-sse/services/tokenRefresh.js +++ b/open-sse/services/tokenRefresh.js @@ -11,6 +11,7 @@ import { refreshIflowToken, refreshGitHubToken, refreshCopilotToken, + refreshCodebuddyToken, classifyOAuthRefreshError, } from "./tokenRefresh/providers.js"; @@ -25,6 +26,7 @@ export { refreshIflowToken, refreshGitHubToken, refreshCopilotToken, + refreshCodebuddyToken, classifyOAuthRefreshError, }; @@ -127,6 +129,7 @@ const REFRESH_HANDLERS = { github: (c, log) => refreshGitHubToken(c.refreshToken, log), kiro: (c, log) => refreshKiroToken(c.refreshToken, c.providerSpecificData, log), xai: (c, log) => refreshXaiToken(c.refreshToken, log), + "codebuddy-cn": (c, log) => refreshCodebuddyToken(c.refreshToken, log), vertex: vertexRefreshHandler, "vertex-partner": vertexRefreshHandler }; diff --git a/open-sse/services/tokenRefresh/providers.js b/open-sse/services/tokenRefresh/providers.js index 33d6baf6..b8352156 100644 --- a/open-sse/services/tokenRefresh/providers.js +++ b/open-sse/services/tokenRefresh/providers.js @@ -524,3 +524,57 @@ export async function refreshCopilotToken(githubAccessToken, log) { } }, log); } + +// CodeBuddy (Tencent) refresh — POST /v2/plugin/auth/token/refresh with the +// refresh token carried in the X-Refresh-Token header (not a form body), +// matching the official CodeBuddy CLI. Response: { code: 0, data: }. +export async function refreshCodebuddyToken(refreshToken, log) { + if (!refreshToken) return null; + return dedupRefresh("codebuddy-cn", refreshToken, async () => { + const oauth = PROVIDER_OAUTH["codebuddy-cn"] || {}; + const response = await fetch(oauth.refreshUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + "User-Agent": oauth.userAgent, + "X-Requested-With": "XMLHttpRequest", + "X-Domain": "copilot.tencent.com", + "X-Refresh-Token": refreshToken, + "X-Auth-Refresh-Source": "plugin", + "X-Product": "SaaS", + }, + body: "{}", + }); + + if (!response.ok) { + const errorText = await response.text(); + log?.error?.("TOKEN_REFRESH", "Failed to refresh CodeBuddy token", { + status: response.status, + error: errorText, + }); + return null; + } + + const data = await response.json(); + if (data.code !== 0 || !data.data?.accessToken) { + log?.error?.("TOKEN_REFRESH", "CodeBuddy token refresh returned no token", { + code: data.code, + msg: data.msg, + }); + return null; + } + + log?.info?.("TOKEN_REFRESH", "Successfully refreshed CodeBuddy token", { + hasNewAccessToken: !!data.data.accessToken, + hasNewRefreshToken: !!data.data.refreshToken, + expiresIn: data.data.expiresIn, + }); + + return { + accessToken: data.data.accessToken, + refreshToken: data.data.refreshToken || refreshToken, + expiresIn: data.data.expiresIn, + }; + }, log); +} diff --git a/public/providers/codebuddy-cn.png b/public/providers/codebuddy-cn.png new file mode 100644 index 00000000..eae3f4c1 Binary files /dev/null and b/public/providers/codebuddy-cn.png differ diff --git a/src/app/api/oauth/[provider]/[action]/route.js b/src/app/api/oauth/[provider]/[action]/route.js index a65b0b4c..78922c96 100644 --- a/src/app/api/oauth/[provider]/[action]/route.js +++ b/src/app/api/oauth/[provider]/[action]/route.js @@ -151,7 +151,7 @@ export async function GET(request, { params }) { : undefined; // Providers that don't use PKCE for device code - const noPkceDeviceProviders = ["github", "kiro", "kimi-coding", "kilocode", "codebuddy", "qoder"]; + const noPkceDeviceProviders = ["github", "kiro", "kimi-coding", "kilocode", "codebuddy-cn", "qoder"]; let deviceData; if (noPkceDeviceProviders.includes(provider)) { deviceData = await requestDeviceCode(provider, undefined, deviceOptions); @@ -271,7 +271,7 @@ export async function POST(request, { params }) { } // Providers that don't use PKCE for device code - const noPkceProviders = ["github", "kimi-coding", "kilocode", "codebuddy"]; + const noPkceProviders = ["github", "kimi-coding", "kilocode", "codebuddy-cn"]; let result; if (noPkceProviders.includes(provider)) { result = await pollForToken(provider, deviceCode); diff --git a/src/app/api/providers/[id]/test/testUtils.js b/src/app/api/providers/[id]/test/testUtils.js index 1d529467..7d57851b 100644 --- a/src/app/api/providers/[id]/test/testUtils.js +++ b/src/app/api/providers/[id]/test/testUtils.js @@ -90,7 +90,7 @@ const OAUTH_TEST_CONFIG = { authHeader: "Authorization", authPrefix: "Bearer ", }, - codebuddy: { tokenExists: true }, + "codebuddy-cn": { tokenExists: true }, }; async function probeClineAccessToken(accessToken) { diff --git a/src/lib/oauth/constants/oauth.js b/src/lib/oauth/constants/oauth.js index d26cac27..4ce0eb7d 100644 --- a/src/lib/oauth/constants/oauth.js +++ b/src/lib/oauth/constants/oauth.js @@ -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", }; diff --git a/src/lib/oauth/providers.js b/src/lib/oauth/providers.js index f2de1f6b..c66d25bb 100644 --- a/src/lib/oauth/providers.js +++ b/src/lib/oauth/providers.js @@ -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: {}, }), }, diff --git a/src/shared/components/OAuthModal.js b/src/shared/components/OAuthModal.js index 28879c58..424e23a1 100644 --- a/src/shared/components/OAuthModal.js +++ b/src/shared/components/OAuthModal.js @@ -157,7 +157,7 @@ export default function OAuthModal({ isOpen, provider, providerInfo, onSuccess, setError(null); // Device code flow providers - const deviceCodeProviders = ["github", "qwen", "kiro", "kimi-coding", "kilocode", "codebuddy", "qoder"]; + const deviceCodeProviders = ["github", "qwen", "kiro", "kimi-coding", "kilocode", "codebuddy-cn", "qoder"]; if (deviceCodeProviders.includes(provider)) { setIsDeviceCode(true); setStep("waiting"); diff --git a/tests/__baseline__/alias-baseline.json b/tests/__baseline__/alias-baseline.json index c980af4b..7c6c8baa 100644 --- a/tests/__baseline__/alias-baseline.json +++ b/tests/__baseline__/alias-baseline.json @@ -105,7 +105,7 @@ "claude": "cc", "cline": "cl", "cloudflare-ai": "cloudflare-ai", - "codebuddy": "codebuddy", + "codebuddy-cn": "codebuddy-cn", "codex": "cx", "cohere": "cohere", "commandcode": "commandcode", @@ -231,4 +231,4 @@ "xiaomi-mimo", "xiaomi-tokenplan" ] -} \ No newline at end of file +} diff --git a/tests/__baseline__/providers-baseline.json b/tests/__baseline__/providers-baseline.json index 0265b536..251e221e 100644 --- a/tests/__baseline__/providers-baseline.json +++ b/tests/__baseline__/providers-baseline.json @@ -349,8 +349,8 @@ "baseUrl": "https://gitlab.com/api/v4/chat/completions", "format": "openai" }, - "codebuddy": { - "baseUrl": "https://copilot.tencent.com/v1/chat/completions", + "codebuddy-cn": { + "baseUrl": "https://copilot.tencent.com/v2/chat/completions", "format": "openai" }, "opencode-go": { diff --git a/tests/translator/__snapshots__/golden-url-header.test.js.snap b/tests/translator/__snapshots__/golden-url-header.test.js.snap index 87a0a4b8..9482052c 100644 --- a/tests/translator/__snapshots__/golden-url-header.test.js.snap +++ b/tests/translator/__snapshots__/golden-url-header.test.js.snap @@ -287,21 +287,39 @@ exports[`GOLDEN buildHeaders (default executor providers) > cloudflare-ai → he } `; -exports[`GOLDEN buildHeaders (default executor providers) > codebuddy → headers (apiKey / oauth) 1`] = ` +exports[`GOLDEN buildHeaders (default executor providers) > codebuddy-cn → headers (apiKey / oauth) 1`] = ` { "apiKey": { "Accept": "text/event-stream", "Authorization": "Bearer ", "Content-Type": "application/json", + "User-Agent": "CLI/2.108.1 CodeBuddy/2.108.1", + "X-IDE-Name": "CLI", + "X-IDE-Type": "CLI", + "X-Product": "SaaS", + "x-codebuddy-request": "1", + "x-requested-with": "XMLHttpRequest", }, "nonStream": { "Authorization": "Bearer ", "Content-Type": "application/json", + "User-Agent": "CLI/2.108.1 CodeBuddy/2.108.1", + "X-IDE-Name": "CLI", + "X-IDE-Type": "CLI", + "X-Product": "SaaS", + "x-codebuddy-request": "1", + "x-requested-with": "XMLHttpRequest", }, "oauth": { "Accept": "text/event-stream", "Authorization": "Bearer ", "Content-Type": "application/json", + "User-Agent": "CLI/2.108.1 CodeBuddy/2.108.1", + "X-IDE-Name": "CLI", + "X-IDE-Type": "CLI", + "X-Product": "SaaS", + "x-codebuddy-request": "1", + "x-requested-with": "XMLHttpRequest", }, } `; @@ -1001,10 +1019,10 @@ exports[`GOLDEN buildUrl (default executor providers) > cloudflare-ai → url (s } `; -exports[`GOLDEN buildUrl (default executor providers) > codebuddy → url (stream + non-stream) 1`] = ` +exports[`GOLDEN buildUrl (default executor providers) > codebuddy-cn → url (stream + non-stream) 1`] = ` { - "nonStream": "https://copilot.tencent.com/v1/chat/completions", - "stream": "https://copilot.tencent.com/v1/chat/completions", + "nonStream": "https://copilot.tencent.com/v2/chat/completions", + "stream": "https://copilot.tencent.com/v2/chat/completions", } `;