tokentracker-cli 0.5.97 → 0.5.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/tracker.js +14 -0
- package/dashboard/dist/assets/{main-h-vFy9lS.js → main-BhjD_pKB.js} +201 -194
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +2 -1
- package/src/commands/init.js +8 -1
- package/src/commands/serve.js +7 -0
- package/src/commands/sync.js +74 -1
- package/src/lib/cursor-config.js +15 -0
- package/src/lib/local-api.js +39 -16
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/proxy-env.js +81 -0
- package/src/lib/rollout.js +40 -20
- package/src/lib/source-metadata.js +46 -0
- package/src/lib/usage-limits.js +312 -40
package/src/lib/usage-limits.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const cp = require("node:child_process");
|
|
2
2
|
const fs = require("node:fs");
|
|
3
|
+
const os = require("node:os");
|
|
3
4
|
const path = require("node:path");
|
|
4
5
|
const http = require("node:http");
|
|
5
6
|
const https = require("node:https");
|
|
@@ -14,6 +15,7 @@ const {
|
|
|
14
15
|
// 2-minute in-memory cache
|
|
15
16
|
let cache = { data: null, fetchedAt: 0 };
|
|
16
17
|
const CACHE_TTL_MS = 2 * 60 * 1000;
|
|
18
|
+
const DEFAULT_PROVIDER_TIMEOUT_MS = 15_000;
|
|
17
19
|
|
|
18
20
|
function clampPercent(value) {
|
|
19
21
|
if (value === null || value === undefined || value === "") return null;
|
|
@@ -52,14 +54,44 @@ function sleepMs(ms) {
|
|
|
52
54
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
function mergeAbortSignals(signalA, signalB) {
|
|
58
|
+
if (!signalA) return signalB;
|
|
59
|
+
if (!signalB) return signalA;
|
|
60
|
+
if (typeof AbortSignal.any === "function") {
|
|
61
|
+
return AbortSignal.any([signalA, signalB]);
|
|
62
|
+
}
|
|
63
|
+
return signalA;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function withFetchTimeout(fetchImpl, timeoutMs) {
|
|
67
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return fetchImpl;
|
|
68
|
+
return (url, options = {}) => {
|
|
69
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
70
|
+
return fetchImpl(url, {
|
|
71
|
+
...options,
|
|
72
|
+
signal: mergeAbortSignals(options.signal, timeoutSignal),
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function withProviderTimeout(promise, label, timeoutMs) {
|
|
78
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return promise;
|
|
79
|
+
let timer;
|
|
80
|
+
const timeout = new Promise((_, reject) => {
|
|
81
|
+
timer = setTimeout(() => {
|
|
82
|
+
reject(new Error(`${label} usage request timed out.`));
|
|
83
|
+
}, timeoutMs);
|
|
84
|
+
});
|
|
85
|
+
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function fetchClaudeUsageLimits(accessToken, { fetchImpl = fetch, maxAttempts = 3 } = {}) {
|
|
56
89
|
const url = "https://api.anthropic.com/api/oauth/usage";
|
|
57
90
|
const headers = {
|
|
58
91
|
Authorization: `Bearer ${accessToken}`,
|
|
59
92
|
"anthropic-beta": "oauth-2025-04-20",
|
|
60
93
|
Accept: "application/json",
|
|
61
94
|
};
|
|
62
|
-
const maxAttempts = 3;
|
|
63
95
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
64
96
|
const res = await fetchImpl(url, { method: "GET", headers });
|
|
65
97
|
if (res.status === 401) {
|
|
@@ -202,6 +234,190 @@ async function fetchCursorLimits({ home, fetchImpl = fetch } = {}) {
|
|
|
202
234
|
}
|
|
203
235
|
}
|
|
204
236
|
|
|
237
|
+
function resolveKimiHome({ home, env } = {}) {
|
|
238
|
+
const explicit = typeof env?.KIMI_HOME === "string" ? env.KIMI_HOME.trim() : "";
|
|
239
|
+
return explicit ? path.resolve(explicit) : path.join(home || os.homedir(), ".kimi");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function loadKimiCredentials({ home, env } = {}) {
|
|
243
|
+
const kimiHome = resolveKimiHome({ home, env });
|
|
244
|
+
const credsPath = path.join(kimiHome, "credentials", "kimi-code.json");
|
|
245
|
+
if (!fs.existsSync(credsPath)) return null;
|
|
246
|
+
try {
|
|
247
|
+
return JSON.parse(fs.readFileSync(credsPath, "utf8"));
|
|
248
|
+
} catch (_error) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function saveKimiCredentials(creds, { home, env } = {}) {
|
|
254
|
+
const kimiHome = resolveKimiHome({ home, env });
|
|
255
|
+
const credsPath = path.join(kimiHome, "credentials", "kimi-code.json");
|
|
256
|
+
fs.mkdirSync(path.dirname(credsPath), { recursive: true });
|
|
257
|
+
fs.writeFileSync(credsPath, JSON.stringify(creds, null, 2));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function hasKimiConfig({ home, env } = {}) {
|
|
261
|
+
return fs.existsSync(path.join(resolveKimiHome({ home, env }), "config.toml"));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function kimiNumber(value) {
|
|
265
|
+
if (value === null || value === undefined || value === "") return null;
|
|
266
|
+
const n = Number(value);
|
|
267
|
+
return Number.isFinite(n) ? n : null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function kimiResetTime(value) {
|
|
271
|
+
if (typeof value !== "string" || !value) return null;
|
|
272
|
+
const ts = Date.parse(value);
|
|
273
|
+
return Number.isFinite(ts) && ts > 0 ? new Date(ts).toISOString() : null;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function kimiWindowFromUsage(data) {
|
|
277
|
+
if (!data || typeof data !== "object") return null;
|
|
278
|
+
const limit = kimiNumber(data.limit);
|
|
279
|
+
if (!Number.isFinite(limit) || limit <= 0) return null;
|
|
280
|
+
let used = kimiNumber(data.used);
|
|
281
|
+
if (used === null) {
|
|
282
|
+
const remaining = kimiNumber(data.remaining);
|
|
283
|
+
if (remaining !== null) used = limit - remaining;
|
|
284
|
+
}
|
|
285
|
+
if (!Number.isFinite(used)) return null;
|
|
286
|
+
return buildWindow({
|
|
287
|
+
usedPercent: (used / limit) * 100,
|
|
288
|
+
resetAt: kimiResetTime(data.resetTime || data.reset_at || data.resetAt),
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function normalizeKimiUsageResponse(body) {
|
|
293
|
+
const firstLimit = Array.isArray(body?.limits) ? body.limits[0] : null;
|
|
294
|
+
const detail = firstLimit?.detail && typeof firstLimit.detail === "object" ? firstLimit.detail : firstLimit;
|
|
295
|
+
const parallelLimit = kimiNumber(body?.parallel?.limit);
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
membership_level: typeof body?.user?.membership?.level === "string" ? body.user.membership.level : null,
|
|
299
|
+
subscription_type: typeof body?.subType === "string" ? body.subType : null,
|
|
300
|
+
parallel_limit: parallelLimit !== null ? parallelLimit : null,
|
|
301
|
+
primary_window: kimiWindowFromUsage(body?.usage),
|
|
302
|
+
secondary_window: kimiWindowFromUsage(detail),
|
|
303
|
+
tertiary_window: kimiWindowFromUsage(body?.totalQuota),
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function kimiCredentialsExpired(creds, nowMs = Date.now()) {
|
|
308
|
+
const expiresAt = Number(creds?.expires_at);
|
|
309
|
+
if (!Number.isFinite(expiresAt) || expiresAt <= 0) return false;
|
|
310
|
+
return expiresAt * 1000 <= nowMs + 30_000;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async function refreshKimiAccessToken({ refreshToken, home, env, fetchImpl = fetch } = {}) {
|
|
314
|
+
if (typeof refreshToken !== "string" || !refreshToken.trim()) {
|
|
315
|
+
throw new Error("Not logged in to Kimi. Run 'kimi' in Terminal to authenticate.");
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const body = new URLSearchParams({
|
|
319
|
+
client_id: "17e5f671-d194-4dfb-9706-5516cb48c098",
|
|
320
|
+
grant_type: "refresh_token",
|
|
321
|
+
refresh_token: refreshToken,
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
const res = await fetchImpl("https://auth.kimi.com/api/oauth/token", {
|
|
325
|
+
method: "POST",
|
|
326
|
+
headers: {
|
|
327
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
328
|
+
"X-Msh-Platform": "kimi_cli",
|
|
329
|
+
},
|
|
330
|
+
body,
|
|
331
|
+
});
|
|
332
|
+
if (res.status === 401 || res.status === 403) {
|
|
333
|
+
throw new Error("Not logged in to Kimi. Run 'kimi' in Terminal to authenticate.");
|
|
334
|
+
}
|
|
335
|
+
if (!res.ok) {
|
|
336
|
+
throw new Error(`Kimi token refresh failed (HTTP ${res.status})`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const json = await res.json();
|
|
340
|
+
if (!json?.access_token) {
|
|
341
|
+
throw new Error("Could not parse Kimi token refresh response");
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const expiresIn = Number(json.expires_in);
|
|
345
|
+
const next = {
|
|
346
|
+
access_token: String(json.access_token),
|
|
347
|
+
refresh_token: String(json.refresh_token || refreshToken),
|
|
348
|
+
expires_at: Date.now() / 1000 + (Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn : 900),
|
|
349
|
+
scope: String(json.scope || "kimi-code"),
|
|
350
|
+
token_type: String(json.token_type || "Bearer"),
|
|
351
|
+
expires_in: Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn : 900,
|
|
352
|
+
};
|
|
353
|
+
saveKimiCredentials(next, { home, env });
|
|
354
|
+
return next.access_token;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async function fetchKimiUsage(accessToken, { fetchImpl = fetch } = {}) {
|
|
358
|
+
const res = await fetchImpl("https://api.kimi.com/coding/v1/usages", {
|
|
359
|
+
method: "GET",
|
|
360
|
+
headers: {
|
|
361
|
+
Authorization: `Bearer ${accessToken}`,
|
|
362
|
+
Accept: "application/json",
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
if (res.status === 401) {
|
|
366
|
+
throw new Error("token_expired");
|
|
367
|
+
}
|
|
368
|
+
if (!res.ok) {
|
|
369
|
+
throw new Error(`Kimi API returned ${res.status}`);
|
|
370
|
+
}
|
|
371
|
+
return res.json();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async function fetchKimiLimits({ home, env, fetchImpl = fetch } = {}) {
|
|
375
|
+
if (!hasKimiConfig({ home, env })) {
|
|
376
|
+
return { configured: false };
|
|
377
|
+
}
|
|
378
|
+
const creds = loadKimiCredentials({ home, env });
|
|
379
|
+
let accessToken = typeof creds?.access_token === "string" ? creds.access_token.trim() : "";
|
|
380
|
+
if (!accessToken) {
|
|
381
|
+
return { configured: false };
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
if (kimiCredentialsExpired(creds) && creds?.refresh_token) {
|
|
385
|
+
accessToken = await refreshKimiAccessToken({
|
|
386
|
+
refreshToken: creds.refresh_token,
|
|
387
|
+
home,
|
|
388
|
+
env,
|
|
389
|
+
fetchImpl,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
let body;
|
|
393
|
+
try {
|
|
394
|
+
body = await fetchKimiUsage(accessToken, { fetchImpl });
|
|
395
|
+
} catch (error) {
|
|
396
|
+
if (error?.message === "token_expired" && creds?.refresh_token) {
|
|
397
|
+
accessToken = await refreshKimiAccessToken({
|
|
398
|
+
refreshToken: creds.refresh_token,
|
|
399
|
+
home,
|
|
400
|
+
env,
|
|
401
|
+
fetchImpl,
|
|
402
|
+
});
|
|
403
|
+
body = await fetchKimiUsage(accessToken, { fetchImpl });
|
|
404
|
+
} else {
|
|
405
|
+
throw error;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return {
|
|
409
|
+
configured: true,
|
|
410
|
+
error: null,
|
|
411
|
+
...normalizeKimiUsageResponse(body),
|
|
412
|
+
};
|
|
413
|
+
} catch (error) {
|
|
414
|
+
return {
|
|
415
|
+
configured: true,
|
|
416
|
+
error: error?.message || "Unknown error",
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
205
421
|
function resolveGeminiHome({ home, env } = {}) {
|
|
206
422
|
const explicit = typeof env?.GEMINI_HOME === "string" ? env.GEMINI_HOME.trim() : "";
|
|
207
423
|
return explicit ? path.resolve(explicit) : path.join(home, ".gemini");
|
|
@@ -229,39 +445,83 @@ function loadGeminiCredentials({ home, env } = {}) {
|
|
|
229
445
|
}
|
|
230
446
|
}
|
|
231
447
|
|
|
232
|
-
function
|
|
233
|
-
const result = runCommand(commandRunner, "which", ["gemini"], { timeout: 2000 });
|
|
234
|
-
const geminiPath = typeof result?.stdout === "string" ? result.stdout.trim() : "";
|
|
235
|
-
if (!geminiPath) return null;
|
|
236
|
-
|
|
237
|
-
let realPath = geminiPath;
|
|
448
|
+
function resolveSymlinkOnce(filePath) {
|
|
238
449
|
try {
|
|
239
|
-
const resolved = fs.readlinkSync(
|
|
240
|
-
|
|
450
|
+
const resolved = fs.readlinkSync(filePath);
|
|
451
|
+
return path.isAbsolute(resolved)
|
|
241
452
|
? resolved
|
|
242
|
-
: path.join(path.dirname(
|
|
453
|
+
: path.join(path.dirname(filePath), resolved);
|
|
454
|
+
} catch (_error) {
|
|
455
|
+
return filePath;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function expandGeminiExecutableCandidates({ home } = {}) {
|
|
460
|
+
const candidates = [];
|
|
461
|
+
const add = (filePath) => {
|
|
462
|
+
if (typeof filePath === "string" && filePath && !candidates.includes(filePath)) {
|
|
463
|
+
candidates.push(filePath);
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
const nvmDir = path.join(home || os.homedir(), ".nvm", "versions", "node");
|
|
468
|
+
try {
|
|
469
|
+
for (const version of fs.readdirSync(nvmDir)) {
|
|
470
|
+
add(path.join(nvmDir, version, "bin", "gemini"));
|
|
471
|
+
}
|
|
243
472
|
} catch (_error) {}
|
|
244
473
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
474
|
+
add(path.join(path.dirname(resolveSymlinkOnce(process.execPath)), "gemini"));
|
|
475
|
+
add("/opt/homebrew/bin/gemini");
|
|
476
|
+
add("/usr/local/bin/gemini");
|
|
477
|
+
|
|
478
|
+
return candidates;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function extractGeminiOauthClientCredentials({ commandRunner, home } = {}) {
|
|
482
|
+
const result = runCommand(commandRunner, "which", ["gemini"], { timeout: 2000 });
|
|
483
|
+
const geminiPath = typeof result?.stdout === "string" ? result.stdout.trim() : "";
|
|
484
|
+
|
|
485
|
+
const geminiPaths = [
|
|
486
|
+
...(geminiPath ? [geminiPath] : []),
|
|
487
|
+
...expandGeminiExecutableCandidates({ home }),
|
|
253
488
|
];
|
|
254
489
|
|
|
255
|
-
for (const
|
|
256
|
-
if (!fs.existsSync(
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
490
|
+
for (const candidateGeminiPath of geminiPaths) {
|
|
491
|
+
if (!fs.existsSync(candidateGeminiPath)) continue;
|
|
492
|
+
const realPath = resolveSymlinkOnce(candidateGeminiPath);
|
|
493
|
+
const binDir = path.dirname(realPath);
|
|
494
|
+
const baseDir = path.dirname(binDir);
|
|
495
|
+
const bundleDir = path.dirname(realPath);
|
|
496
|
+
const candidates = [
|
|
497
|
+
path.join(baseDir, "libexec/lib/node_modules/@google/gemini-cli/node_modules/@google/gemini-cli-core/dist/src/code_assist/oauth2.js"),
|
|
498
|
+
path.join(baseDir, "lib/node_modules/@google/gemini-cli/node_modules/@google/gemini-cli-core/dist/src/code_assist/oauth2.js"),
|
|
499
|
+
path.join(baseDir, "share/gemini-cli/node_modules/@google/gemini-cli-core/dist/src/code_assist/oauth2.js"),
|
|
500
|
+
path.join(baseDir, "../gemini-cli-core/dist/src/code_assist/oauth2.js"),
|
|
501
|
+
path.join(baseDir, "node_modules/@google/gemini-cli-core/dist/src/code_assist/oauth2.js"),
|
|
502
|
+
];
|
|
503
|
+
if (path.basename(bundleDir) === "bundle") {
|
|
504
|
+
candidates.push(realPath);
|
|
505
|
+
try {
|
|
506
|
+
for (const file of fs.readdirSync(bundleDir)) {
|
|
507
|
+
if (/^chunk-.*\.js$/.test(file)) {
|
|
508
|
+
candidates.push(path.join(bundleDir, file));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
} catch (_error) {}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
for (const candidate of candidates) {
|
|
515
|
+
if (!fs.existsSync(candidate)) continue;
|
|
516
|
+
try {
|
|
517
|
+
const content = fs.readFileSync(candidate, "utf8");
|
|
518
|
+
const clientId = content.match(/OAUTH_CLIENT_ID\s*=\s*['"]([^'"]+)['"]/)?.[1] || null;
|
|
519
|
+
const clientSecret = content.match(/OAUTH_CLIENT_SECRET\s*=\s*['"]([^'"]+)['"]/)?.[1] || null;
|
|
520
|
+
if (clientId && clientSecret) {
|
|
521
|
+
return { clientId, clientSecret };
|
|
522
|
+
}
|
|
523
|
+
} catch (_error) {}
|
|
524
|
+
}
|
|
265
525
|
}
|
|
266
526
|
return null;
|
|
267
527
|
}
|
|
@@ -273,7 +533,7 @@ async function refreshGeminiAccessToken({
|
|
|
273
533
|
fetchImpl = fetch,
|
|
274
534
|
commandRunner,
|
|
275
535
|
}) {
|
|
276
|
-
const oauthClient = extractGeminiOauthClientCredentials({ commandRunner });
|
|
536
|
+
const oauthClient = extractGeminiOauthClientCredentials({ commandRunner, home });
|
|
277
537
|
if (!oauthClient?.clientId || !oauthClient?.clientSecret) {
|
|
278
538
|
throw new Error("Gemini API error: Could not find Gemini CLI OAuth configuration");
|
|
279
539
|
}
|
|
@@ -990,14 +1250,15 @@ function antigravityCodeIsOk(code) {
|
|
|
990
1250
|
|
|
991
1251
|
function parseAntigravityDate(value) {
|
|
992
1252
|
if (typeof value === "string" && value) {
|
|
993
|
-
const iso = Date.parse(value);
|
|
994
|
-
if (Number.isFinite(iso)) return new Date(iso).toISOString();
|
|
995
1253
|
const numeric = Number(value);
|
|
996
|
-
if (Number.isFinite(numeric)) {
|
|
1254
|
+
if (Number.isFinite(numeric) && numeric > 0) {
|
|
997
1255
|
return new Date(numeric * 1000).toISOString();
|
|
998
1256
|
}
|
|
1257
|
+
if (Number.isFinite(numeric)) return null;
|
|
1258
|
+
const iso = Date.parse(value);
|
|
1259
|
+
if (Number.isFinite(iso) && iso > 0) return new Date(iso).toISOString();
|
|
999
1260
|
}
|
|
1000
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1261
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
1001
1262
|
return new Date(value * 1000).toISOString();
|
|
1002
1263
|
}
|
|
1003
1264
|
return null;
|
|
@@ -1203,6 +1464,7 @@ async function getUsageLimits({
|
|
|
1203
1464
|
commandRunner,
|
|
1204
1465
|
requestFn,
|
|
1205
1466
|
now = new Date(),
|
|
1467
|
+
providerTimeoutMs = DEFAULT_PROVIDER_TIMEOUT_MS,
|
|
1206
1468
|
} = {}) {
|
|
1207
1469
|
const nowMs = Date.now();
|
|
1208
1470
|
if (cache.data && nowMs - cache.fetchedAt < CACHE_TTL_MS) {
|
|
@@ -1214,24 +1476,30 @@ async function getUsageLimits({
|
|
|
1214
1476
|
readCodexAccessToken({ home, env }),
|
|
1215
1477
|
]);
|
|
1216
1478
|
|
|
1217
|
-
const
|
|
1479
|
+
const providerFetch = withFetchTimeout(fetchImpl, providerTimeoutMs);
|
|
1480
|
+
const [claudeResult, codexResult, cursor, kimi, gemini, kiro, antigravity, copilot] = await Promise.all([
|
|
1218
1481
|
claudeToken
|
|
1219
|
-
? fetchClaudeUsageLimits(claudeToken, { fetchImpl }).then(
|
|
1482
|
+
? withProviderTimeout(fetchClaudeUsageLimits(claudeToken, { fetchImpl: providerFetch, maxAttempts: 1 }), "Claude", providerTimeoutMs).then(
|
|
1220
1483
|
(value) => ({ status: "fulfilled", value }),
|
|
1221
1484
|
(reason) => ({ status: "rejected", reason }),
|
|
1222
1485
|
)
|
|
1223
1486
|
: Promise.resolve(null),
|
|
1224
1487
|
codexToken
|
|
1225
|
-
? fetchCodexUsageLimits(codexToken, { fetchImpl }).then(
|
|
1488
|
+
? withProviderTimeout(fetchCodexUsageLimits(codexToken, { fetchImpl: providerFetch }), "Codex", providerTimeoutMs).then(
|
|
1226
1489
|
(value) => ({ status: "fulfilled", value }),
|
|
1227
1490
|
(reason) => ({ status: "rejected", reason }),
|
|
1228
1491
|
)
|
|
1229
1492
|
: Promise.resolve(null),
|
|
1230
|
-
fetchCursorLimits({ home, fetchImpl }),
|
|
1231
|
-
|
|
1493
|
+
withProviderTimeout(fetchCursorLimits({ home, fetchImpl: providerFetch }), "Cursor", providerTimeoutMs)
|
|
1494
|
+
.catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
|
|
1495
|
+
withProviderTimeout(fetchKimiLimits({ home, env, fetchImpl: providerFetch }), "Kimi", providerTimeoutMs)
|
|
1496
|
+
.catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
|
|
1497
|
+
withProviderTimeout(fetchGeminiLimits({ home, env, fetchImpl: providerFetch, commandRunner }), "Gemini", providerTimeoutMs)
|
|
1498
|
+
.catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
|
|
1232
1499
|
Promise.resolve().then(() => fetchKiroLimits({ commandRunner, now })),
|
|
1233
1500
|
fetchAntigravityLimits({ commandRunner, requestFn }),
|
|
1234
|
-
fetchCopilotLimits({ home, env, fetchImpl }),
|
|
1501
|
+
withProviderTimeout(fetchCopilotLimits({ home, env, fetchImpl: providerFetch }), "GitHub Copilot", providerTimeoutMs)
|
|
1502
|
+
.catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
|
|
1235
1503
|
]);
|
|
1236
1504
|
|
|
1237
1505
|
let claude;
|
|
@@ -1269,6 +1537,7 @@ async function getUsageLimits({
|
|
|
1269
1537
|
claude,
|
|
1270
1538
|
codex,
|
|
1271
1539
|
cursor,
|
|
1540
|
+
kimi,
|
|
1272
1541
|
gemini,
|
|
1273
1542
|
kiro,
|
|
1274
1543
|
antigravity,
|
|
@@ -1286,8 +1555,11 @@ function resetUsageLimitsCache() {
|
|
|
1286
1555
|
module.exports = {
|
|
1287
1556
|
getUsageLimits,
|
|
1288
1557
|
resetUsageLimitsCache,
|
|
1558
|
+
extractGeminiOauthClientCredentials,
|
|
1559
|
+
loadKimiCredentials,
|
|
1289
1560
|
normalizeCursorUsageSummary,
|
|
1290
1561
|
normalizeGeminiQuotaResponse,
|
|
1562
|
+
normalizeKimiUsageResponse,
|
|
1291
1563
|
parseKiroUsageOutput,
|
|
1292
1564
|
normalizeAntigravityResponse,
|
|
1293
1565
|
parseListeningPorts,
|