Skip to content

Commit 8f81363

Browse files
author
decolua
committed
Enhance token refresh functionality across multiple executors
- Updated refreshCredentials methods in various executors (Antigravity, Base, Default, Github, Kiro) to accept optional proxyOptions for improved proxy handling. - Modified token refresh logic to utilize proxy-aware fetch for better network management. - Enhanced usage retrieval functions to support proxy options, ensuring seamless integration with proxy configurations. - Updated ModelSelectModal and ProviderInfoCard components to incorporate kind filtering for improved user experience in model selection. - Added validation for API keys in the provider validation route, including support for webSearch/webFetch providers.
1 parent 1bb6213 commit 8f81363

45 files changed

Lines changed: 2913 additions & 278 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 194 additions & 138 deletions
Large diffs are not rendered by default.

open-sse/executors/antigravity.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,11 @@ export class AntigravityExecutor extends BaseExecutor {
114114
};
115115
}
116116

117-
async refreshCredentials(credentials, log) {
117+
async refreshCredentials(credentials, log, proxyOptions = null) {
118118
if (!credentials.refreshToken) return null;
119119

120120
try {
121-
const response = await fetch(OAUTH_ENDPOINTS.google.token, {
121+
const response = await proxyAwareFetch(OAUTH_ENDPOINTS.google.token, {
122122
method: "POST",
123123
headers: { "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json" },
124124
body: new URLSearchParams({
@@ -127,7 +127,7 @@ export class AntigravityExecutor extends BaseExecutor {
127127
client_id: this.config.clientId,
128128
client_secret: this.config.clientSecret
129129
})
130-
});
130+
}, proxyOptions);
131131

132132
if (!response.ok) return null;
133133

open-sse/executors/base.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export class BaseExecutor {
8585
}
8686

8787
// Override in subclass for provider-specific refresh
88-
async refreshCredentials(credentials, log) {
88+
async refreshCredentials(credentials, log, proxyOptions = null) {
8989
return null;
9090
}
9191

open-sse/executors/default.js

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { PROVIDERS } from "../config/providers.js";
33
import { OAUTH_ENDPOINTS, buildKimiHeaders } from "../config/appConstants.js";
44
import { buildClineHeaders } from "../../src/shared/utils/clineAuth.js";
55
import { getCachedClaudeHeaders } from "../utils/claudeHeaderCache.js";
6+
import { proxyAwareFetch } from "../utils/proxyFetch.js";
67

78
export class DefaultExecutor extends BaseExecutor {
89
constructor(provider) {
@@ -154,19 +155,19 @@ export class DefaultExecutor extends BaseExecutor {
154155
return headers;
155156
}
156157

157-
async refreshCredentials(credentials, log) {
158+
async refreshCredentials(credentials, log, proxyOptions = null) {
158159
if (!credentials.refreshToken) return null;
159160

160161
const refreshers = {
161-
claude: () => this.refreshWithJSON(OAUTH_ENDPOINTS.anthropic.token, { grant_type: "refresh_token", refresh_token: credentials.refreshToken, client_id: PROVIDERS.claude.clientId }),
162-
codex: () => this.refreshWithForm(OAUTH_ENDPOINTS.openai.token, { grant_type: "refresh_token", refresh_token: credentials.refreshToken, client_id: PROVIDERS.codex.clientId, scope: "openid profile email offline_access" }),
163-
qwen: () => this.refreshWithForm(OAUTH_ENDPOINTS.qwen.token, { grant_type: "refresh_token", refresh_token: credentials.refreshToken, client_id: PROVIDERS.qwen.clientId }),
164-
iflow: () => this.refreshIflow(credentials.refreshToken),
165-
gemini: () => this.refreshGoogle(credentials.refreshToken),
166-
kiro: () => this.refreshKiro(credentials.refreshToken),
167-
cline: () => this.refreshCline(credentials.refreshToken),
168-
"kimi-coding": () => this.refreshKimiCoding(credentials.refreshToken),
169-
kilocode: () => this.refreshKilocode(credentials.refreshToken)
162+
claude: () => this.refreshWithJSON(OAUTH_ENDPOINTS.anthropic.token, { grant_type: "refresh_token", refresh_token: credentials.refreshToken, client_id: PROVIDERS.claude.clientId }, proxyOptions),
163+
codex: () => this.refreshWithForm(OAUTH_ENDPOINTS.openai.token, { grant_type: "refresh_token", refresh_token: credentials.refreshToken, client_id: PROVIDERS.codex.clientId, scope: "openid profile email offline_access" }, proxyOptions),
164+
qwen: () => this.refreshWithForm(OAUTH_ENDPOINTS.qwen.token, { grant_type: "refresh_token", refresh_token: credentials.refreshToken, client_id: PROVIDERS.qwen.clientId }, proxyOptions),
165+
iflow: () => this.refreshIflow(credentials.refreshToken, proxyOptions),
166+
gemini: () => this.refreshGoogle(credentials.refreshToken, proxyOptions),
167+
kiro: () => this.refreshKiro(credentials.refreshToken, proxyOptions),
168+
cline: () => this.refreshCline(credentials.refreshToken, proxyOptions),
169+
"kimi-coding": () => this.refreshKimiCoding(credentials.refreshToken, proxyOptions),
170+
kilocode: () => this.refreshKilocode(credentials.refreshToken, proxyOptions)
170171
};
171172

172173
const refresher = refreshers[this.provider];
@@ -182,69 +183,69 @@ export class DefaultExecutor extends BaseExecutor {
182183
}
183184
}
184185

185-
async refreshWithJSON(url, body) {
186-
const response = await fetch(url, {
186+
async refreshWithJSON(url, body, proxyOptions = null) {
187+
const response = await proxyAwareFetch(url, {
187188
method: "POST",
188189
headers: { "Content-Type": "application/json", "Accept": "application/json" },
189190
body: JSON.stringify(body)
190-
});
191+
}, proxyOptions);
191192
if (!response.ok) return null;
192193
const tokens = await response.json();
193194
return { accessToken: tokens.access_token, refreshToken: tokens.refresh_token || body.refresh_token, expiresIn: tokens.expires_in };
194195
}
195196

196-
async refreshWithForm(url, params) {
197-
const response = await fetch(url, {
197+
async refreshWithForm(url, params, proxyOptions = null) {
198+
const response = await proxyAwareFetch(url, {
198199
method: "POST",
199200
headers: { "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json" },
200201
body: new URLSearchParams(params)
201-
});
202+
}, proxyOptions);
202203
if (!response.ok) return null;
203204
const tokens = await response.json();
204205
return { accessToken: tokens.access_token, refreshToken: tokens.refresh_token || params.refresh_token, expiresIn: tokens.expires_in };
205206
}
206207

207-
async refreshIflow(refreshToken) {
208+
async refreshIflow(refreshToken, proxyOptions = null) {
208209
const basicAuth = btoa(`${PROVIDERS.iflow.clientId}:${PROVIDERS.iflow.clientSecret}`);
209-
const response = await fetch(OAUTH_ENDPOINTS.iflow.token, {
210+
const response = await proxyAwareFetch(OAUTH_ENDPOINTS.iflow.token, {
210211
method: "POST",
211212
headers: { "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json", "Authorization": `Basic ${basicAuth}` },
212213
body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: PROVIDERS.iflow.clientId, client_secret: PROVIDERS.iflow.clientSecret })
213-
});
214+
}, proxyOptions);
214215
if (!response.ok) return null;
215216
const tokens = await response.json();
216217
return { accessToken: tokens.access_token, refreshToken: tokens.refresh_token || refreshToken, expiresIn: tokens.expires_in };
217218
}
218219

219-
async refreshGoogle(refreshToken) {
220-
const response = await fetch(OAUTH_ENDPOINTS.google.token, {
220+
async refreshGoogle(refreshToken, proxyOptions = null) {
221+
const response = await proxyAwareFetch(OAUTH_ENDPOINTS.google.token, {
221222
method: "POST",
222223
headers: { "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json" },
223224
body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: this.config.clientId, client_secret: this.config.clientSecret })
224-
});
225+
}, proxyOptions);
225226
if (!response.ok) return null;
226227
const tokens = await response.json();
227228
return { accessToken: tokens.access_token, refreshToken: tokens.refresh_token || refreshToken, expiresIn: tokens.expires_in };
228229
}
229230

230-
async refreshKiro(refreshToken) {
231-
const response = await fetch(PROVIDERS.kiro.tokenUrl, {
231+
async refreshKiro(refreshToken, proxyOptions = null) {
232+
const response = await proxyAwareFetch(PROVIDERS.kiro.tokenUrl, {
232233
method: "POST",
233234
headers: { "Content-Type": "application/json", "Accept": "application/json", "User-Agent": "kiro-cli/1.0.0" },
234235
body: JSON.stringify({ refreshToken })
235-
});
236+
}, proxyOptions);
236237
if (!response.ok) return null;
237238
const tokens = await response.json();
238239
return { accessToken: tokens.accessToken, refreshToken: tokens.refreshToken || refreshToken, expiresIn: tokens.expiresIn };
239240
}
240241

241-
async refreshCline(refreshToken) {
242+
async refreshCline(refreshToken, proxyOptions = null) {
242243
console.log('[DEBUG] Refreshing Cline token, refreshToken length:', refreshToken?.length);
243-
const response = await fetch("https://api.cline.bot/api/v1/auth/refresh", {
244+
const response = await proxyAwareFetch("https://api.cline.bot/api/v1/auth/refresh", {
244245
method: "POST",
245246
headers: { "Content-Type": "application/json", "Accept": "application/json" },
246247
body: JSON.stringify({ refreshToken, grantType: "refresh_token", clientType: "extension" })
247-
});
248+
}, proxyOptions);
248249
console.log('[DEBUG] Cline refresh response status:', response.status);
249250
if (!response.ok) {
250251
const errorText = await response.text();
@@ -260,23 +261,23 @@ export class DefaultExecutor extends BaseExecutor {
260261
return { accessToken: data?.accessToken, refreshToken: data?.refreshToken || refreshToken, expiresIn };
261262
}
262263

263-
async refreshKimiCoding(refreshToken) {
264+
async refreshKimiCoding(refreshToken, proxyOptions = null) {
264265
const kimiHeaders = buildKimiHeaders();
265-
const response = await fetch("https://auth.kimi.com/api/oauth/token", {
266+
const response = await proxyAwareFetch("https://auth.kimi.com/api/oauth/token", {
266267
method: "POST",
267268
headers: {
268269
"Content-Type": "application/x-www-form-urlencoded",
269270
"Accept": "application/json",
270271
...kimiHeaders
271272
},
272273
body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: "17e5f671-d194-4dfb-9706-5516cb48c098" })
273-
});
274+
}, proxyOptions);
274275
if (!response.ok) return null;
275276
const tokens = await response.json();
276277
return { accessToken: tokens.access_token, refreshToken: tokens.refresh_token || refreshToken, expiresIn: tokens.expires_in };
277278
}
278279

279-
async refreshKilocode(refreshToken) {
280+
async refreshKilocode(refreshToken, proxyOptions = null) {
280281
// Kilocode uses device code flow, no refresh token support
281282
return null;
282283
}

open-sse/executors/github.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,9 @@ export class GithubExecutor extends BaseExecutor {
271271
};
272272
}
273273

274-
async refreshCopilotToken(githubAccessToken, log) {
274+
async refreshCopilotToken(githubAccessToken, log, proxyOptions = null) {
275275
try {
276-
const response = await fetch("https://api.github.com/copilot_internal/v2/token", {
276+
const response = await proxyAwareFetch("https://api.github.com/copilot_internal/v2/token", {
277277
headers: {
278278
"Authorization": `token ${githubAccessToken}`,
279279
"User-Agent": GITHUB_COPILOT.USER_AGENT,
@@ -282,7 +282,7 @@ export class GithubExecutor extends BaseExecutor {
282282
"Accept": "application/json",
283283
"x-github-api-version": GITHUB_COPILOT.API_VERSION
284284
}
285-
});
285+
}, proxyOptions);
286286
if (!response.ok) {
287287
const errorText = await response.text();
288288
log?.error?.("TOKEN", `Copilot token refresh failed: ${response.status} ${errorText}`);
@@ -297,7 +297,7 @@ export class GithubExecutor extends BaseExecutor {
297297
}
298298
}
299299

300-
async refreshGitHubToken(refreshToken, log) {
300+
async refreshGitHubToken(refreshToken, log, proxyOptions = null) {
301301
try {
302302
const params = {
303303
grant_type: "refresh_token",
@@ -308,11 +308,11 @@ export class GithubExecutor extends BaseExecutor {
308308
params.client_secret = this.config.clientSecret;
309309
}
310310

311-
const response = await fetch(OAUTH_ENDPOINTS.github.token, {
311+
const response = await proxyAwareFetch(OAUTH_ENDPOINTS.github.token, {
312312
method: "POST",
313313
headers: { "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json" },
314314
body: new URLSearchParams(params)
315-
});
315+
}, proxyOptions);
316316
if (!response.ok) return null;
317317
const tokens = await response.json();
318318
log?.info?.("TOKEN", "GitHub token refreshed");
@@ -323,13 +323,13 @@ export class GithubExecutor extends BaseExecutor {
323323
}
324324
}
325325

326-
async refreshCredentials(credentials, log) {
327-
let copilotResult = await this.refreshCopilotToken(credentials.accessToken, log);
326+
async refreshCredentials(credentials, log, proxyOptions = null) {
327+
let copilotResult = await this.refreshCopilotToken(credentials.accessToken, log, proxyOptions);
328328

329329
if (!copilotResult && credentials.refreshToken) {
330-
const githubTokens = await this.refreshGitHubToken(credentials.refreshToken, log);
330+
const githubTokens = await this.refreshGitHubToken(credentials.refreshToken, log, proxyOptions);
331331
if (githubTokens?.accessToken) {
332-
copilotResult = await this.refreshCopilotToken(githubTokens.accessToken, log);
332+
copilotResult = await this.refreshCopilotToken(githubTokens.accessToken, log, proxyOptions);
333333
if (copilotResult) {
334334
return { ...githubTokens, copilotToken: copilotResult.token, copilotTokenExpiresAt: copilotResult.expiresAt };
335335
}

open-sse/executors/kiro.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,15 +378,16 @@ export class KiroExecutor extends BaseExecutor {
378378
});
379379
}
380380

381-
async refreshCredentials(credentials, log) {
381+
async refreshCredentials(credentials, log, proxyOptions = null) {
382382
if (!credentials.refreshToken) return null;
383383

384384
try {
385385
// Use centralized refreshKiroToken function (handles both AWS SSO OIDC and Social Auth)
386386
const result = await refreshKiroToken(
387387
credentials.refreshToken,
388388
credentials.providerSpecificData,
389-
log
389+
log,
390+
proxyOptions
390391
);
391392

392393
return result;

0 commit comments

Comments
 (0)