mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
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>
This commit is contained in:
co-authored by
Claude Fable 5
parent
79918c7830
commit
039c4dbc72
@@ -668,3 +668,198 @@ export async function refreshCodebuddyToken(refreshToken, log) {
|
||||
};
|
||||
}, 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user