fix(oauth): refresh Cline tokens with extension JSON contract

This commit is contained in:
Paulo Schuller
2026-08-16 12:32:25 -03:00
committed by decolua
parent bb3cb43e09
commit 88676b3037
3 changed files with 78 additions and 9 deletions

View File

@@ -4,6 +4,7 @@ import {
refreshXaiToken,
refreshAccessToken,
refreshKimiToken,
refreshClineToken,
refreshClaudeOAuthToken,
refreshGoogleToken,
refreshCodexToken,
@@ -23,6 +24,7 @@ import {
export {
refreshAccessToken,
refreshKimiToken,
refreshClineToken,
refreshClaudeOAuthToken,
refreshGoogleToken,
refreshCodexToken,
@@ -145,6 +147,7 @@ const REFRESH_HANDLERS = {
"codebuddy-cn": (c, log) => refreshCodebuddyToken(c.refreshToken, log),
"codebuddy-intl": (c, log) => refreshCodebuddyIntlToken(c.refreshToken, log),
trae: (c, log) => refreshTraeToken(c.refreshToken, c, log),
cline: (c, log) => refreshClineToken(c.refreshToken, log),
zed: () => refreshZedToken(),
windsurf: (c, log) => refreshWindsurfToken(c, log),
// Kimi Code OAuth (merged into id `kimi`); legacy id still routes here

View File

@@ -147,6 +147,53 @@ export async function refreshKimiToken(refreshToken, credentials, log) {
return refreshAccessToken("kimi", refreshToken, credentials, log);
}
export async function refreshClineToken(refreshToken, log) {
if (!refreshToken) return null;
return dedupRefresh("cline", refreshToken, async () => {
try {
const response = await fetch(PROVIDERS.cline?.refreshUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
refreshToken,
grantType: "refresh_token",
clientType: "extension",
}),
});
if (!response.ok) {
const errorText = await response.text();
log?.error?.("TOKEN_REFRESH", "Failed to refresh Cline token", {
status: response.status,
error: errorText,
});
return null;
}
const body = await response.json();
const tokens = body?.data || body;
if (!tokens?.accessToken) return null;
const expiresIn = tokens.expiresAt
? Math.max(1, Math.floor((new Date(tokens.expiresAt).getTime() - Date.now()) / 1000))
: (tokens.expiresIn || tokens.expires_in || 3600);
return {
accessToken: tokens.accessToken,
refreshToken: tokens.refreshToken || refreshToken,
expiresIn,
};
} catch (error) {
log?.error?.("TOKEN_REFRESH", `Error refreshing Cline token: ${error.message}`);
return null;
}
}, log);
}
// Claude OAuth: JSON body, client_id only. Delegate to refreshAccessToken("claude", ...).
export async function refreshClaudeOAuthToken(refreshToken, log) {
return refreshAccessToken("claude", refreshToken, {}, log);