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:
Janu Yoga
2026-06-19 15:33:26 +07:00
committed by decolua
parent db4499d6df
commit efd20be8d8
18 changed files with 234 additions and 59 deletions

View File

@@ -10,10 +10,15 @@ http.createServer = (...args) => {
const rest = args.filter((a) => typeof a !== "function"); const rest = args.filter((a) => typeof a !== "function");
if (!handler) return origCreate(...args); if (!handler) return origCreate(...args);
const wrapped = (req, res) => { const wrapped = (req, res) => {
const ip = req.socket && req.socket.remoteAddress ? req.socket.remoteAddress : ""; const socketIp = req.socket && req.socket.remoteAddress ? req.socket.remoteAddress : "";
// Forwarding headers present = request arrived via a reverse proxy; loopback const xff = req.headers["x-forwarded-for"];
// socket is the proxy hop, not the end-user, so it must not be trusted as local. const xRealIp = req.headers["x-real-ip"];
const viaProxy = !!(req.headers["x-forwarded-for"] || 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-9r-real-ip"];
delete req.headers["x-forwarded-for"]; delete req.headers["x-forwarded-for"];
delete req.headers["x-9r-via-proxy"]; delete req.headers["x-9r-via-proxy"];

View File

@@ -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;

View File

@@ -17,6 +17,7 @@ import { OllamaLocalExecutor } from "./ollama-local.js";
import { CommandCodeExecutor } from "./commandcode.js"; import { CommandCodeExecutor } from "./commandcode.js";
import { XiaomiTokenplanExecutor } from "./xiaomi-tokenplan.js"; import { XiaomiTokenplanExecutor } from "./xiaomi-tokenplan.js";
import { MimoFreeExecutor } from "./mimo-free.js"; import { MimoFreeExecutor } from "./mimo-free.js";
import { CodeBuddyExecutor } from "./codebuddy-cn.js";
import { DefaultExecutor } from "./default.js"; import { DefaultExecutor } from "./default.js";
const executors = { const executors = {
@@ -42,6 +43,7 @@ const executors = {
"xiaomi-tokenplan": new XiaomiTokenplanExecutor(), "xiaomi-tokenplan": new XiaomiTokenplanExecutor(),
"mimo-free": new MimoFreeExecutor(), "mimo-free": new MimoFreeExecutor(),
mmf: new MimoFreeExecutor(), // Alias for mimo-free mmf: new MimoFreeExecutor(), // Alias for mimo-free
"codebuddy-cn": new CodeBuddyExecutor(),
}; };
const defaultCache = new Map(); const defaultCache = new Map();
@@ -77,3 +79,4 @@ export { OllamaLocalExecutor } from "./ollama-local.js";
export { CommandCodeExecutor } from "./commandcode.js"; export { CommandCodeExecutor } from "./commandcode.js";
export { XiaomiTokenplanExecutor } from "./xiaomi-tokenplan.js"; export { XiaomiTokenplanExecutor } from "./xiaomi-tokenplan.js";
export { MimoFreeExecutor } from "./mimo-free.js"; export { MimoFreeExecutor } from "./mimo-free.js";
export { CodeBuddyExecutor } from "./codebuddy-cn.js";

View File

@@ -92,7 +92,30 @@ export const MODEL_CAPABILITIES = {
/** /**
* Provider-specific capability overrides. Keyed by provider alias/id. * 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 * Pattern fallback — glob (* = wildcard), matched case-insensitively and

View File

@@ -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,
},
};

View File

@@ -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,
},
};

View File

@@ -16,7 +16,7 @@ import p13 from "./chutes.js";
import p14 from "./claude.js"; import p14 from "./claude.js";
import p15 from "./cline.js"; import p15 from "./cline.js";
import p16 from "./cloudflare-ai.js"; import p16 from "./cloudflare-ai.js";
import p17 from "./codebuddy.js"; import p17 from "./codebuddy-cn.js";
import p18 from "./codex.js"; import p18 from "./codex.js";
import p19 from "./cohere.js"; import p19 from "./cohere.js";
import p20 from "./comfyui.js"; import p20 from "./comfyui.js";

View File

@@ -11,6 +11,7 @@ import {
refreshIflowToken, refreshIflowToken,
refreshGitHubToken, refreshGitHubToken,
refreshCopilotToken, refreshCopilotToken,
refreshCodebuddyToken,
classifyOAuthRefreshError, classifyOAuthRefreshError,
} from "./tokenRefresh/providers.js"; } from "./tokenRefresh/providers.js";
@@ -25,6 +26,7 @@ export {
refreshIflowToken, refreshIflowToken,
refreshGitHubToken, refreshGitHubToken,
refreshCopilotToken, refreshCopilotToken,
refreshCodebuddyToken,
classifyOAuthRefreshError, classifyOAuthRefreshError,
}; };
@@ -127,6 +129,7 @@ const REFRESH_HANDLERS = {
github: (c, log) => refreshGitHubToken(c.refreshToken, log), github: (c, log) => refreshGitHubToken(c.refreshToken, log),
kiro: (c, log) => refreshKiroToken(c.refreshToken, c.providerSpecificData, log), kiro: (c, log) => refreshKiroToken(c.refreshToken, c.providerSpecificData, log),
xai: (c, log) => refreshXaiToken(c.refreshToken, log), xai: (c, log) => refreshXaiToken(c.refreshToken, log),
"codebuddy-cn": (c, log) => refreshCodebuddyToken(c.refreshToken, log),
vertex: vertexRefreshHandler, vertex: vertexRefreshHandler,
"vertex-partner": vertexRefreshHandler "vertex-partner": vertexRefreshHandler
}; };

View File

@@ -524,3 +524,57 @@ export async function refreshCopilotToken(githubAccessToken, log) {
} }
}, 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: <token> }.
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);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -151,7 +151,7 @@ export async function GET(request, { params }) {
: undefined; : undefined;
// Providers that don't use PKCE for device code // 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; let deviceData;
if (noPkceDeviceProviders.includes(provider)) { if (noPkceDeviceProviders.includes(provider)) {
deviceData = await requestDeviceCode(provider, undefined, deviceOptions); 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 // 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; let result;
if (noPkceProviders.includes(provider)) { if (noPkceProviders.includes(provider)) {
result = await pollForToken(provider, deviceCode); result = await pollForToken(provider, deviceCode);

View File

@@ -90,7 +90,7 @@ const OAUTH_TEST_CONFIG = {
authHeader: "Authorization", authHeader: "Authorization",
authPrefix: "Bearer ", authPrefix: "Bearer ",
}, },
codebuddy: { tokenExists: true }, "codebuddy-cn": { tokenExists: true },
}; };
async function probeClineAccessToken(accessToken) { async function probeClineAccessToken(accessToken) {

View File

@@ -106,7 +106,7 @@ export const CLINE_CONFIG = { ...PROVIDER_OAUTH["cline"] };
export const GITLAB_CONFIG = { ...PROVIDER_OAUTH["gitlab"] }; export const GITLAB_CONFIG = { ...PROVIDER_OAUTH["gitlab"] };
// CodeBuddy (Tencent) OAuth Configuration (Browser OAuth Polling Flow) // 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) // OAuth timeout (5 minutes)
export const OAUTH_TIMEOUT = 300000; export const OAUTH_TIMEOUT = 300000;
@@ -128,5 +128,5 @@ export const PROVIDERS = {
KILOCODE: "kilocode", KILOCODE: "kilocode",
CLINE: "cline", CLINE: "cline",
GITLAB: "gitlab", GITLAB: "gitlab",
CODEBUDDY: "codebuddy", CODEBUDDY: "codebuddy-cn",
}; };

View File

@@ -1180,7 +1180,7 @@ const PROVIDERS = {
// 1. POST stateUrl → get { state, authUrl } // 1. POST stateUrl → get { state, authUrl }
// 2. Open authUrl in browser // 2. Open authUrl in browser
// 3. Poll tokenUrl with state until success (code 0) or timeout // 3. Poll tokenUrl with state until success (code 0) or timeout
codebuddy: { "codebuddy-cn": {
config: CODEBUDDY_CONFIG, config: CODEBUDDY_CONFIG,
flowType: "device_code", flowType: "device_code",
requestDeviceCode: async (config) => { requestDeviceCode: async (config) => {
@@ -1212,23 +1212,25 @@ const PROVIDERS = {
}; };
}, },
pollToken: async (config, deviceCode) => { pollToken: async (config, deviceCode) => {
const response = await fetch(config.tokenUrl, { // CodeBuddy polls the token endpoint via GET with the state as a query
method: "POST", // 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: { headers: {
"Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
"User-Agent": config.userAgent, "User-Agent": config.userAgent,
"X-Requested-With": "XMLHttpRequest", "X-Requested-With": "XMLHttpRequest",
"X-Domain": "copilot.tencent.com", "X-Domain": "copilot.tencent.com",
"X-No-Authorization": "true", "X-No-Authorization": "true",
"X-No-User-Id": "true", "X-No-User-Id": "true",
"X-No-Enterprise-Id": "true",
"X-No-Department-Info": "true",
"X-Product": "SaaS", "X-Product": "SaaS",
}, },
body: JSON.stringify({ state: deviceCode }),
}); });
if (!response.ok) return { ok: false, data: { error: "request_failed" } }; if (!response.ok) return { ok: false, data: { error: "request_failed" } };
const data = await response.json(); 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) { if (data.code === 0 && data.data?.accessToken) {
return { return {
ok: true, ok: true,
@@ -1236,6 +1238,7 @@ const PROVIDERS = {
access_token: data.data.accessToken, access_token: data.data.accessToken,
refresh_token: data.data.refreshToken || "", refresh_token: data.data.refreshToken || "",
token_type: data.data.tokenType || "Bearer", token_type: data.data.tokenType || "Bearer",
expires_in: data.data.expiresIn,
}, },
}; };
} }
@@ -1245,7 +1248,7 @@ const PROVIDERS = {
mapTokens: (tokens) => ({ mapTokens: (tokens) => ({
accessToken: tokens.access_token, accessToken: tokens.access_token,
refreshToken: tokens.refresh_token, refreshToken: tokens.refresh_token,
expiresIn: 86400, expiresIn: tokens.expires_in || 86400,
providerSpecificData: {}, providerSpecificData: {},
}), }),
}, },

View File

@@ -157,7 +157,7 @@ export default function OAuthModal({ isOpen, provider, providerInfo, onSuccess,
setError(null); setError(null);
// Device code flow providers // 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)) { if (deviceCodeProviders.includes(provider)) {
setIsDeviceCode(true); setIsDeviceCode(true);
setStep("waiting"); setStep("waiting");

View File

@@ -105,7 +105,7 @@
"claude": "cc", "claude": "cc",
"cline": "cl", "cline": "cl",
"cloudflare-ai": "cloudflare-ai", "cloudflare-ai": "cloudflare-ai",
"codebuddy": "codebuddy", "codebuddy-cn": "codebuddy-cn",
"codex": "cx", "codex": "cx",
"cohere": "cohere", "cohere": "cohere",
"commandcode": "commandcode", "commandcode": "commandcode",
@@ -231,4 +231,4 @@
"xiaomi-mimo", "xiaomi-mimo",
"xiaomi-tokenplan" "xiaomi-tokenplan"
] ]
} }

View File

@@ -349,8 +349,8 @@
"baseUrl": "https://gitlab.com/api/v4/chat/completions", "baseUrl": "https://gitlab.com/api/v4/chat/completions",
"format": "openai" "format": "openai"
}, },
"codebuddy": { "codebuddy-cn": {
"baseUrl": "https://copilot.tencent.com/v1/chat/completions", "baseUrl": "https://copilot.tencent.com/v2/chat/completions",
"format": "openai" "format": "openai"
}, },
"opencode-go": { "opencode-go": {

View File

@@ -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": { "apiKey": {
"Accept": "text/event-stream", "Accept": "text/event-stream",
"Authorization": "Bearer <TOK>", "Authorization": "Bearer <TOK>",
"Content-Type": "application/json", "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": { "nonStream": {
"Authorization": "Bearer <TOK>", "Authorization": "Bearer <TOK>",
"Content-Type": "application/json", "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": { "oauth": {
"Accept": "text/event-stream", "Accept": "text/event-stream",
"Authorization": "Bearer <TOK>", "Authorization": "Bearer <TOK>",
"Content-Type": "application/json", "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", "nonStream": "https://copilot.tencent.com/v2/chat/completions",
"stream": "https://copilot.tencent.com/v1/chat/completions", "stream": "https://copilot.tencent.com/v2/chat/completions",
} }
`; `;