Files
9router/open-sse/services/tokenRefresh/providers.js
T
decoluaandClaude Fable 5 039c4dbc72 feat(providers): add trae/windsurf/zed/workbuddy/codebuddy-intl + icons
- New providers: trae, windsurf, zed, workbuddy, codebuddy-intl
  (registry + executor, wired into executors/index.js + registry/index.js)
- zed: port hosted cloud proxy from OmniRoute — RSA access-token → short-lived
  LLM token exchange (shared/zedAuth.js) + NDJSON {event}/{status}/[DONE]
  stream translated back to OpenAI via Claude/Gemini/OpenAI-Responses translators
- Provider icons (128x128 png) for the 5 new providers
- qoder + tokenRefresh provider tweaks

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:37:24 +07:00

866 lines
27 KiB
JavaScript

import { PROVIDERS, PROVIDER_OAUTH } from "../../config/providers.js";
import { OAUTH_ENDPOINTS, GITHUB_COPILOT, buildKimiHeaders } from "../../config/appConstants.js";
import { proxyAwareFetch } from "../../utils/proxyFetch.js";
import { dedupRefresh } from "./dedup.js";
import { buildExternalIdpRefreshParams } from "../../../src/lib/oauth/kiroExternalIdp.js";
let _xaiServiceSingleton = null;
export async function refreshXaiToken(refreshToken, log) {
if (!refreshToken) return null;
return dedupRefresh("xai", refreshToken, async () => {
try {
if (!_xaiServiceSingleton) {
const mod = await import("../../../src/lib/oauth/services/xai.js");
_xaiServiceSingleton = new mod.XaiService();
}
const tokens = await _xaiServiceSingleton.refreshAccessToken(refreshToken);
return {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token || refreshToken,
expiresIn: tokens.expires_in,
idToken: tokens.id_token,
};
} catch (e) {
log?.warn?.("TOKEN_REFRESH", `xai refresh failed: ${e?.message || e}`);
const msg = String(e?.message || "");
if (msg.includes("invalid_grant") || msg.includes("invalid_request")) {
return { error: "invalid_grant" };
}
return null;
}
}, log);
}
export async function refreshAccessToken(provider, refreshToken, credentials, log) {
const config = PROVIDERS[provider];
if (!config || !config.refreshUrl) {
log?.warn?.("TOKEN_REFRESH", `No refresh URL configured for provider: ${provider}`);
return null;
}
if (!refreshToken) {
log?.warn?.("TOKEN_REFRESH", `No refresh token available for provider: ${provider}`);
return null;
}
return dedupRefresh(provider, refreshToken, async () => {
try {
const response = await fetch(config.refreshUrl, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
},
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: config.clientId,
client_secret: config.clientSecret,
}),
});
if (!response.ok) {
const errorText = await response.text();
log?.error?.("TOKEN_REFRESH", `Failed to refresh token for ${provider}`, {
status: response.status,
error: errorText,
});
return null;
}
const tokens = await response.json();
log?.info?.("TOKEN_REFRESH", `Successfully refreshed token for ${provider}`, {
hasNewAccessToken: !!tokens.access_token,
hasNewRefreshToken: !!tokens.refresh_token,
expiresIn: tokens.expires_in,
});
return {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token || refreshToken,
expiresIn: tokens.expires_in,
};
} catch (error) {
log?.error?.("TOKEN_REFRESH", `Error refreshing token for ${provider}`, {
error: error.message,
});
return null;
}
}, log);
}
// CLIProxyAPI DeviceFlowClient.RefreshToken: form body (no client_secret) + X-Msh-* headers
export async function refreshKimiToken(refreshToken, credentials, log) {
const config = PROVIDERS.kimi;
if (!config?.refreshUrl || !config?.clientId) {
log?.warn?.("TOKEN_REFRESH", "No Kimi refresh URL/clientId configured");
return null;
}
if (!refreshToken) return null;
return dedupRefresh("kimi", refreshToken, async () => {
try {
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
...buildKimiHeaders(credentials?.providerSpecificData?.deviceId),
};
const response = await fetch(config.refreshUrl, {
method: "POST",
headers,
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: config.clientId,
}),
});
if (!response.ok) {
const errorText = await response.text();
log?.error?.("TOKEN_REFRESH", `Failed to refresh token for kimi`, {
status: response.status,
error: errorText,
});
return null;
}
const tokens = await response.json();
return {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token || refreshToken,
expiresIn: tokens.expires_in,
};
} catch (error) {
log?.error?.("TOKEN_REFRESH", `Error refreshing token for kimi`, { error: error.message });
return null;
}
}, log);
}
export async function refreshClaudeOAuthToken(refreshToken, log) {
if (!refreshToken) return null;
return dedupRefresh("claude", refreshToken, async () => {
try {
const response = await fetch(OAUTH_ENDPOINTS.anthropic.token, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: PROVIDERS.claude.clientId,
}),
});
if (!response.ok) {
const errorText = await response.text();
log?.error?.("TOKEN_REFRESH", "Failed to refresh Claude OAuth token", { status: response.status, error: errorText });
return null;
}
const tokens = await response.json();
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Claude OAuth token", { hasNewAccessToken: !!tokens.access_token, expiresIn: tokens.expires_in });
return { accessToken: tokens.access_token, refreshToken: tokens.refresh_token || refreshToken, expiresIn: tokens.expires_in };
} catch (error) {
log?.error?.("TOKEN_REFRESH", `Network error refreshing Claude token: ${error.message}`);
return null;
}
}, log);
}
export async function refreshGoogleToken(refreshToken, clientId, clientSecret, log) {
if (!refreshToken) return null;
return dedupRefresh(`google:${clientId}`, refreshToken, async () => {
try {
const response = await fetch(OAUTH_ENDPOINTS.google.token, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
},
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: clientId,
client_secret: clientSecret,
}),
});
if (!response.ok) {
const errorText = await response.text();
log?.error?.("TOKEN_REFRESH", "Failed to refresh Google token", { status: response.status, error: errorText });
return null;
}
const tokens = await response.json();
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Google token", { hasNewAccessToken: !!tokens.access_token, expiresIn: tokens.expires_in });
return { accessToken: tokens.access_token, refreshToken: tokens.refresh_token || refreshToken, expiresIn: tokens.expires_in };
} catch (error) {
log?.error?.("TOKEN_REFRESH", `Network error refreshing Google token: ${error.message}`);
return null;
}
}, log);
}
export async function refreshQwenToken(refreshToken, log) {
if (!refreshToken) return null;
return dedupRefresh("qwen", refreshToken, async () => {
const endpoint = OAUTH_ENDPOINTS.qwen.token;
try {
const response = await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
},
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: PROVIDERS.qwen.clientId,
}),
});
if (response.status === 200) {
const tokens = await response.json();
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Qwen token", {
hasNewAccessToken: !!tokens.access_token,
hasNewRefreshToken: !!tokens.refresh_token,
expiresIn: tokens.expires_in,
});
return {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token || refreshToken,
expiresIn: tokens.expires_in,
providerSpecificData: tokens.resource_url
? { resourceUrl: tokens.resource_url }
: undefined,
};
} else {
const errorText = await response.text().catch(() => "");
log?.warn?.("TOKEN_REFRESH", `Error with Qwen endpoint`, {
status: response.status,
error: errorText,
});
}
} catch (error) {
log?.warn?.("TOKEN_REFRESH", `Network error trying Qwen endpoint`, {
error: error.message,
});
}
log?.error?.("TOKEN_REFRESH", "Failed to refresh Qwen token");
return null;
}, log);
}
export function classifyOAuthRefreshError(errorText = "", status = 0) {
let parsed = null;
try {
parsed = errorText ? JSON.parse(errorText) : null;
} catch {
parsed = null;
}
const code = parsed?.error?.code || parsed?.error || parsed?.error_code || "";
const description = parsed?.error_description || parsed?.message || errorText || "";
const combined = `${code} ${description}`.toLowerCase();
const permanent = [
"refresh_token_expired",
"refresh_token_reused",
"refresh_token_invalidated",
"invalid_grant",
].some((marker) => combined.includes(marker));
return { status, code, description, permanent };
}
export async function refreshCodexToken(refreshToken, log) {
if (!refreshToken) return null;
return dedupRefresh("codex", refreshToken, async () => {
try {
const response = await fetch(OAUTH_ENDPOINTS.openai.token, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
client_id: PROVIDERS.codex.clientId,
grant_type: "refresh_token",
refresh_token: refreshToken,
}),
});
if (!response.ok) {
const errorText = await response.text();
const failure = classifyOAuthRefreshError(errorText, response.status);
if (failure.permanent) {
log?.error?.("TOKEN_REFRESH", "Codex refresh token already used or invalid. Re-auth required.", {
status: response.status,
code: failure.code,
});
return { error: "unrecoverable_refresh_error", code: failure.code };
}
log?.error?.("TOKEN_REFRESH", "Failed to refresh Codex token", {
status: response.status,
error: errorText,
code: failure.code,
permanent: failure.permanent,
});
return null;
}
const tokens = await response.json();
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Codex token", {
hasNewAccessToken: !!tokens.access_token,
hasNewRefreshToken: !!tokens.refresh_token,
hasIdToken: !!tokens.id_token,
expiresIn: tokens.expires_in,
});
return {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token || refreshToken,
idToken: tokens.id_token,
expiresIn: tokens.expires_in,
};
} catch (error) {
log?.error?.("TOKEN_REFRESH", `Network error refreshing Codex token: ${error.message}`);
return null;
}
}, log);
}
async function resolveKiroProfileArnPatch(providerSpecificData, accessToken, refreshedArn) {
if (providerSpecificData?.profileArn) return {};
let profileArn = refreshedArn?.trim?.() || null;
if (!profileArn) {
const { fetchKiroProfileArn } = await import("../../../src/lib/oauth/providers.js");
profileArn = await fetchKiroProfileArn(accessToken);
}
return profileArn ? { providerSpecificData: { profileArn } } : {};
}
export async function refreshKiroToken(refreshToken, providerSpecificData, log, proxyOptions = null) {
if (!refreshToken) return null;
return dedupRefresh("kiro", refreshToken, async () => {
const authMethod = providerSpecificData?.authMethod;
const clientId = providerSpecificData?.clientId;
const clientSecret = providerSpecificData?.clientSecret;
const region = providerSpecificData?.region;
if (authMethod === "external_idp") {
let refreshRequest;
try {
refreshRequest = buildExternalIdpRefreshParams(refreshToken, providerSpecificData);
} catch (error) {
log?.warn?.("TOKEN_REFRESH", `Invalid Kiro external_idp refresh config: ${error.message}`);
return null;
}
const response = await proxyAwareFetch(refreshRequest.tokenEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
},
body: refreshRequest.body,
}, proxyOptions);
if (!response.ok) {
const errorText = await response.text();
log?.error?.("TOKEN_REFRESH", "Failed to refresh Kiro external_idp token", {
status: response.status,
error: errorText,
});
return null;
}
const tokens = await response.json();
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Kiro external_idp token", {
hasNewAccessToken: !!tokens.access_token,
hasNewRefreshToken: !!tokens.refresh_token,
expiresIn: tokens.expires_in,
});
return {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token || refreshToken,
expiresIn: tokens.expires_in,
providerSpecificData: refreshRequest.providerSpecificData,
};
}
if (clientId && clientSecret) {
const isIDC = authMethod === "idc";
const endpoint = isIDC && region
? `https://oidc.${region}.amazonaws.com/token`
: "https://oidc.us-east-1.amazonaws.com/token";
const response = await proxyAwareFetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
clientId: clientId,
clientSecret: clientSecret,
refreshToken: refreshToken,
grantType: "refresh_token",
}),
}, proxyOptions);
if (!response.ok) {
const errorText = await response.text();
log?.error?.("TOKEN_REFRESH", "Failed to refresh Kiro AWS token", {
status: response.status,
error: errorText,
});
return null;
}
const tokens = await response.json();
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Kiro AWS token", {
hasNewAccessToken: !!tokens.accessToken,
expiresIn: tokens.expiresIn,
});
return {
accessToken: tokens.accessToken,
refreshToken: tokens.refreshToken || refreshToken,
expiresIn: tokens.expiresIn,
...(await resolveKiroProfileArnPatch(providerSpecificData, tokens.accessToken, tokens.profileArn)),
};
}
const response = await proxyAwareFetch(PROVIDERS.kiro.tokenUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"User-Agent": "kiro-cli/1.0.0",
},
body: JSON.stringify({
refreshToken: refreshToken,
}),
}, proxyOptions);
if (!response.ok) {
const errorText = await response.text();
log?.error?.("TOKEN_REFRESH", "Failed to refresh Kiro social token", {
status: response.status,
error: errorText,
});
return null;
}
const tokens = await response.json();
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Kiro social token", {
hasNewAccessToken: !!tokens.accessToken,
expiresIn: tokens.expiresIn,
});
return {
accessToken: tokens.accessToken,
refreshToken: tokens.refreshToken || refreshToken,
expiresIn: tokens.expiresIn,
...(await resolveKiroProfileArnPatch(providerSpecificData, tokens.accessToken, tokens.profileArn)),
};
}, log);
}
export async function refreshIflowToken(refreshToken, log) {
if (!refreshToken) return null;
return dedupRefresh("iflow", refreshToken, async () => {
const basicAuth = btoa(`${PROVIDERS.iflow.clientId}:${PROVIDERS.iflow.clientSecret}`);
const response = await fetch(OAUTH_ENDPOINTS.iflow.token, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
Authorization: `Basic ${basicAuth}`,
},
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: PROVIDERS.iflow.clientId,
client_secret: PROVIDERS.iflow.clientSecret,
}),
});
if (!response.ok) {
const errorText = await response.text();
log?.error?.("TOKEN_REFRESH", "Failed to refresh iFlow token", {
status: response.status,
error: errorText,
});
return null;
}
const tokens = await response.json();
log?.info?.("TOKEN_REFRESH", "Successfully refreshed iFlow token", {
hasNewAccessToken: !!tokens.access_token,
hasNewRefreshToken: !!tokens.refresh_token,
expiresIn: tokens.expires_in,
});
return {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token || refreshToken,
expiresIn: tokens.expires_in,
};
}, log);
}
export async function refreshGitHubToken(refreshToken, log) {
if (!refreshToken) return null;
return dedupRefresh("github", refreshToken, async () => {
const params = {
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: PROVIDERS.github.clientId,
};
if (PROVIDERS.github.clientSecret) {
params.client_secret = PROVIDERS.github.clientSecret;
}
const response = await fetch(OAUTH_ENDPOINTS.github.token, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
},
body: new URLSearchParams(params),
});
if (!response.ok) {
const errorText = await response.text();
log?.error?.("TOKEN_REFRESH", "Failed to refresh GitHub token", {
status: response.status,
error: errorText,
});
return null;
}
const tokens = await response.json();
log?.info?.("TOKEN_REFRESH", "Successfully refreshed GitHub token", {
hasNewAccessToken: !!tokens.access_token,
hasNewRefreshToken: !!tokens.refresh_token,
expiresIn: tokens.expires_in,
});
return {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token || refreshToken,
expiresIn: tokens.expires_in,
};
}, log);
}
export async function refreshCopilotToken(githubAccessToken, log) {
if (!githubAccessToken) return null;
return dedupRefresh("copilot", githubAccessToken, async () => {
try {
const response = await fetch(PROVIDER_OAUTH["github"]?.copilotTokenUrl, {
headers: {
"Authorization": `token ${githubAccessToken}`,
"User-Agent": GITHUB_COPILOT.USER_AGENT,
"Editor-Version": `vscode/${GITHUB_COPILOT.VSCODE_VERSION}`,
"Editor-Plugin-Version": `copilot-chat/${GITHUB_COPILOT.COPILOT_CHAT_VERSION}`,
"Accept": "application/json",
"x-github-api-version": GITHUB_COPILOT.API_VERSION
}
});
if (!response.ok) {
const errorText = await response.text();
log?.error?.("TOKEN_REFRESH", "Failed to refresh Copilot token", {
status: response.status,
error: errorText
});
return null;
}
const data = await response.json();
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Copilot token", {
hasToken: !!data.token,
expiresAt: data.expires_at
});
return {
token: data.token,
expiresAt: data.expires_at
};
} catch (error) {
log?.error?.("TOKEN_REFRESH", "Error refreshing Copilot token", {
error: error.message
});
return null;
}
}, 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);
}
export async function refreshCodebuddyIntlToken(refreshToken, log) {
if (!refreshToken) return null;
return dedupRefresh("codebuddy-intl", refreshToken, async () => {
const oauth = PROVIDER_OAUTH["codebuddy-intl"] || {};
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": "www.codebuddy.ai",
"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 intl 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 intl token refresh returned no token", {
code: data.code,
msg: data.msg,
});
return null;
}
log?.info?.("TOKEN_REFRESH", "Successfully refreshed CodeBuddy intl 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);
}
export async function refreshWorkbuddyToken(refreshToken, log) {
if (!refreshToken) return null;
return dedupRefresh("workbuddy", refreshToken, async () => {
const oauth = PROVIDER_OAUTH["workbuddy"] || {};
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": "www.codebuddy.cn",
"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 WorkBuddy token", {
status: response.status,
error: errorText,
});
return null;
}
const data = await response.json();
if (data.code !== 0 || !data.data?.accessToken) {
log?.error?.("TOKEN_REFRESH", "WorkBuddy token refresh returned no token", {
code: data.code,
msg: data.msg,
});
return null;
}
log?.info?.("TOKEN_REFRESH", "Successfully refreshed WorkBuddy 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);
}
// Trae refresh — POST ExchangeToken with JSON body {ClientID, RefreshToken, ClientSecret, UserID}.
// Response: {Result: {AccessToken, RefreshToken, TokenType, ExpiresAt}}.
// Source: cockpit-tools/src-tauri/src/modules/trae_oauth.rs (TRAE_EXCHANGE_TOKEN_PATH).
export async function refreshTraeToken(refreshToken, credentials, log) {
if (!refreshToken) return null;
const oauth = PROVIDER_OAUTH.trae || {};
const url = oauth.exchangeTokenUrl || oauth.tokenUrl;
if (!url) {
log?.warn?.("TOKEN_REFRESH", "No Trae exchangeTokenUrl configured");
return null;
}
return dedupRefresh("trae", refreshToken, async () => {
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"User-Agent": "Trae/1.0.0 antigravity-cockpit-tools",
},
body: JSON.stringify({
ClientID: oauth.clientId || "ono9krqynydwx5",
RefreshToken: refreshToken,
ClientSecret: oauth.clientSecret || "-",
UserID: "",
}),
});
if (!response.ok) {
const errorText = await response.text();
log?.error?.("TOKEN_REFRESH", "Failed to refresh Trae token", {
status: response.status,
error: errorText,
});
return null;
}
const payload = await response.json();
const result = payload?.Result || payload?.result || payload;
const accessToken = result?.AccessToken || result?.accessToken;
if (!accessToken) {
log?.error?.("TOKEN_REFRESH", "Trae refresh returned no AccessToken", { payload });
return null;
}
const newRefresh = result?.RefreshToken || result?.refreshToken || refreshToken;
const expiresAt = result?.ExpiresAt || result?.expiresAt;
let expiresIn;
if (typeof expiresAt === "number") {
expiresIn = Math.max(1, expiresAt - Math.floor(Date.now() / 1000));
} else if (typeof expiresAt === "string") {
const ms = new Date(expiresAt).getTime() - Date.now();
expiresIn = ms > 0 ? Math.floor(ms / 1000) : undefined;
}
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Trae token", {
hasNewAccessToken: !!accessToken,
hasNewRefreshToken: newRefresh !== refreshToken,
expiresIn,
});
return {
accessToken,
refreshToken: newRefresh,
expiresIn,
};
} catch (error) {
log?.error?.("TOKEN_REFRESH", `Error refreshing Trae token: ${error.message}`);
return null;
}
}, log);
}
// Zed access_token is long-lived; auth flow returns no refresh_token.
// No refresh possible — re-login required when token expires/revoked.
// Mirrors cursor/kilocode null-refresh pattern.
export function refreshZedToken() {
return null;
}
// Windsurf apiKey is the long-lived terminal credential (no OAuth2 refresh_token
// grant yields a fresh apiKey). Refresh handled out-of-band by the caller.
// TODO(firebase): if short-lived Firebase JWT credentials must be refreshed,
// re-run RegisterUser with the refreshed Firebase JWT (separate code path).
export async function refreshWindsurfToken(credentials, log) {
log?.info?.(
"TOKEN_REFRESH",
"windsurf: apiKey is long-lived (no refresh_token flow) — skipping"
);
return null;
}