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
co-authored by Cursor
parent db4499d6df
commit efd20be8d8
18 changed files with 234 additions and 59 deletions
@@ -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: <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);
}