@ipv9/tokentracker-cli 0.39.34 → 0.39.36

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.
Files changed (46) hide show
  1. package/README.md +48 -5
  2. package/dashboard/dist/assets/{ActivityHeatmap-ChFDzpdS.js → ActivityHeatmap-BZbe6OVf.js} +1 -1
  3. package/dashboard/dist/assets/{Card-ijF07sCs.js → Card-DZDffviN.js} +1 -1
  4. package/dashboard/dist/assets/{DashboardPage-CSzPtd5f.js → DashboardPage-DvrBOnfG.js} +1 -1
  5. package/dashboard/dist/assets/{DevicePage-C1k0oqsA.js → DevicePage-DNrsGqbS.js} +1 -1
  6. package/dashboard/dist/assets/{DialogTitle-BFFfzcmZ.js → DialogTitle-DMgnAEO9.js} +1 -1
  7. package/dashboard/dist/assets/{FadeIn-BgK1lNmX.js → FadeIn-DpHBH3x-.js} +1 -1
  8. package/dashboard/dist/assets/{InsforgeUserHeaderControls-2VB98X4S.js → InsforgeUserHeaderControls-DkKxxuF4.js} +1 -1
  9. package/dashboard/dist/assets/{IpCheckPage-DzbaCMvc.js → IpCheckPage-CjcJwf3_.js} +1 -1
  10. package/dashboard/dist/assets/{LandingPage-BgG9N4uz.js → LandingPage-BoVGYS64.js} +1 -1
  11. package/dashboard/dist/assets/{LeaderboardAvatar-ShOySfzi.js → LeaderboardAvatar-DQ8ynVhp.js} +1 -1
  12. package/dashboard/dist/assets/{LeaderboardPage-D2IP8z5a.js → LeaderboardPage-JYAYCp0V.js} +3 -3
  13. package/dashboard/dist/assets/{LeaderboardProfileModal-DsFdGEx3.js → LeaderboardProfileModal-Dy-QGydj.js} +1 -1
  14. package/dashboard/dist/assets/{LeaderboardProfilePage-CnZRsiia.js → LeaderboardProfilePage-DM5JCs97.js} +1 -1
  15. package/dashboard/dist/assets/LimitsPage-hRivQP36.js +2 -0
  16. package/dashboard/dist/assets/{LocalOnlyNotice-BLIekBBt.js → LocalOnlyNotice-jYtTsZPC.js} +1 -1
  17. package/dashboard/dist/assets/{LoginPage-CBGEh6Yy.js → LoginPage-CamCWjrS.js} +1 -1
  18. package/dashboard/dist/assets/{PopoverPopup-C09O8D7u.js → PopoverPopup-OsVVgo9j.js} +1 -1
  19. package/dashboard/dist/assets/{Select-CCh3LC5S.js → Select-CPShxNj8.js} +1 -1
  20. package/dashboard/dist/assets/{SelectItemText-kCuvclJY.js → SelectItemText-Euh3FNnr.js} +1 -1
  21. package/dashboard/dist/assets/{SettingsPage-DoOB-uyq.js → SettingsPage-GgHfE5Ux.js} +1 -1
  22. package/dashboard/dist/assets/{SkillsPage-CVGJm49x.js → SkillsPage-B5VnJZGi.js} +1 -1
  23. package/dashboard/dist/assets/{WidgetsPage-DC1wk1qB.js → WidgetsPage-DLgvjves.js} +1 -1
  24. package/dashboard/dist/assets/{WrappedPage-ajs88eqx.js → WrappedPage-Cn9hW775.js} +1 -1
  25. package/dashboard/dist/assets/{arrow-up-right-CjOHnUd-.js → arrow-up-right-DbC_nfsr.js} +1 -1
  26. package/dashboard/dist/assets/{download-aF04AJYI.js → download-CcPm5jr5.js} +1 -1
  27. package/dashboard/dist/assets/{info-D0oZ0eak.js → info-DCynm2Zt.js} +1 -1
  28. package/dashboard/dist/assets/{main-fV3uYMtM.js → main-BRziFxTy.js} +6 -2
  29. package/dashboard/dist/assets/use-limits-display-prefs-Z0jXcttr.js +1 -0
  30. package/dashboard/dist/assets/{use-native-settings-Bi7E0UR8.js → use-native-settings-C2hcqfEA.js} +1 -1
  31. package/dashboard/dist/assets/{use-spring-BOREQ4Dn.js → use-spring-CvCkOHy_.js} +1 -1
  32. package/dashboard/dist/assets/{use-usage-limits-BGYp5WOe.js → use-usage-limits-ClV5RR82.js} +1 -1
  33. package/dashboard/dist/assets/{useCurrency-VxllGPBM.js → useCurrency-DV0oi8od.js} +1 -1
  34. package/dashboard/dist/assets/{useOpenInteractionType-CzmlePCG.js → useOpenInteractionType-C6kbIbxP.js} +1 -1
  35. package/dashboard/dist/index.html +1 -1
  36. package/dashboard/dist/share.html +1 -1
  37. package/package.json +1 -1
  38. package/src/cli.js +3 -3
  39. package/src/commands/doctor.js +5 -0
  40. package/src/commands/serve.js +3 -1
  41. package/src/lib/browser-auth.js +40 -13
  42. package/src/lib/doctor.js +104 -1
  43. package/src/lib/pricing/seed-snapshot.json +1 -1
  44. package/src/lib/usage-limits.js +234 -18
  45. package/dashboard/dist/assets/LimitsPage-L68WyhKt.js +0 -2
  46. package/dashboard/dist/assets/use-limits-display-prefs-DGaESD6L.js +0 -1
@@ -29,6 +29,8 @@ const DEFAULT_PROVIDER_TIMEOUT_MS = 15_000;
29
29
  const ANTIGRAVITY_LIMITS_CACHE_FILE = "usage-limits-cache.json";
30
30
  const ANTIGRAVITY_LIMITS_CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
31
31
  const ANTIGRAVITY_LIMITS_CACHE_UNKNOWN_RESET_TTL_MS = 12 * 60 * 60 * 1000;
32
+ const CLAUDE_LIMITS_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
33
+ const CLAUDE_USAGE_RATE_LIMITED = "CLAUDE_USAGE_RATE_LIMITED";
32
34
 
33
35
  function clampPercent(value) {
34
36
  if (value === null || value === undefined || value === "") return null;
@@ -119,9 +121,12 @@ async function fetchClaudeUsageLimits(accessToken, { fetchImpl = fetch, maxAttem
119
121
  }
120
122
  if (!res.ok) {
121
123
  if (res.status === 429) {
122
- throw new Error(
124
+ const error = new Error(
123
125
  "Claude API rate limited (429) — wait ~1 minute and refresh.",
124
126
  );
127
+ error.code = CLAUDE_USAGE_RATE_LIMITED;
128
+ error.transient = true;
129
+ throw error;
125
130
  }
126
131
  throw new Error(`Claude API returned ${res.status}`);
127
132
  }
@@ -135,6 +140,13 @@ async function fetchClaudeUsageLimits(accessToken, { fetchImpl = fetch, maxAttem
135
140
  }
136
141
  }
137
142
 
143
+ function isClaudeTransientUsageError(error) {
144
+ if (!error) return false;
145
+ if (error.code === CLAUDE_USAGE_RATE_LIMITED || error.transient === true) return true;
146
+ const message = String(error.message || "");
147
+ return /rate limited|timed out/i.test(message);
148
+ }
149
+
138
150
  // Classify a wham window by `limit_window_seconds` rather than its slot name.
139
151
  // 18000s = 5h session window. 604800s = 7d weekly window. Free-tier accounts only get a
140
152
  // weekly window, often delivered in the `primary_window` slot — naive position-based
@@ -494,6 +506,133 @@ async function fetchKimiLimits({ home, env, fetchImpl = fetch } = {}) {
494
506
  }
495
507
  }
496
508
 
509
+ function resolveZaiAuth({ env = process.env } = {}) {
510
+ const anthropicBaseUrl = typeof env?.ANTHROPIC_BASE_URL === "string" ? env.ANTHROPIC_BASE_URL.trim() : "";
511
+ const anthropicToken = typeof env?.ANTHROPIC_AUTH_TOKEN === "string" ? env.ANTHROPIC_AUTH_TOKEN.trim() : "";
512
+ if (anthropicBaseUrl && anthropicToken) {
513
+ try {
514
+ const host = new URL(anthropicBaseUrl).hostname.toLowerCase();
515
+ if (host === "api.z.ai" || host === "open.bigmodel.cn") {
516
+ return {
517
+ endpoint: `${new URL(anthropicBaseUrl).origin}/api/monitor/usage/quota/limit`,
518
+ authorization: anthropicToken,
519
+ };
520
+ }
521
+ } catch (_error) {}
522
+ }
523
+
524
+ const apiKey = [
525
+ env?.TOKENTRACKER_ZAI_API_KEY,
526
+ env?.ZAI_API_KEY,
527
+ env?.ZHIPU_API_KEY,
528
+ ].find((value) => typeof value === "string" && value.trim());
529
+ if (!apiKey) return null;
530
+ const trimmed = apiKey.trim();
531
+ return {
532
+ endpoint: "https://api.z.ai/api/monitor/usage/quota/limit",
533
+ authorization: /^Bearer\s+/i.test(trimmed) ? trimmed : `Bearer ${trimmed}`,
534
+ };
535
+ }
536
+
537
+ function zaiResetTime(value) {
538
+ if (value === null || value === undefined || value === "") return null;
539
+ const n = Number(value);
540
+ if (Number.isFinite(n) && n > 0) {
541
+ const ms = n > 10_000_000_000 ? n : n * 1000;
542
+ const date = new Date(ms);
543
+ return Number.isFinite(date.getTime()) ? date.toISOString() : null;
544
+ }
545
+ const ts = Date.parse(String(value));
546
+ return Number.isFinite(ts) ? new Date(ts).toISOString() : null;
547
+ }
548
+
549
+ function zaiWindowFromLimit(limit) {
550
+ if (!limit || typeof limit !== "object") return null;
551
+ let usedPercent = clampPercent(limit.percentage);
552
+ if (usedPercent === null) {
553
+ const current = Number(limit.currentValue ?? limit.usage);
554
+ const remaining = Number(limit.remaining);
555
+ if (Number.isFinite(current) && current >= 0 && Number.isFinite(remaining) && remaining >= 0) {
556
+ usedPercent = clampPercent((current / (current + remaining)) * 100);
557
+ }
558
+ }
559
+ return buildWindow({
560
+ usedPercent,
561
+ resetAt: zaiResetTime(limit.nextResetTime),
562
+ });
563
+ }
564
+
565
+ function normalizeZaiUsageResponse(body) {
566
+ const data = body?.data && typeof body.data === "object" ? body.data : {};
567
+ const limits = Array.isArray(data.limits) ? data.limits : [];
568
+ let fiveHour = null;
569
+ let weekly = null;
570
+ let monthly = null;
571
+
572
+ for (const limit of limits) {
573
+ const type = typeof limit?.type === "string" ? limit.type : "";
574
+ const unit = Number(limit?.unit);
575
+ const number = Number(limit?.number);
576
+ if (type === "TOKENS_LIMIT" && unit === 3 && number === 5 && !fiveHour) {
577
+ fiveHour = zaiWindowFromLimit(limit);
578
+ } else if (type === "TOKENS_LIMIT" && unit === 6 && !weekly) {
579
+ weekly = zaiWindowFromLimit(limit);
580
+ } else if (type === "TIME_LIMIT" && !monthly) {
581
+ monthly = zaiWindowFromLimit(limit);
582
+ }
583
+ }
584
+
585
+ return {
586
+ account_plan: typeof data.planName === "string"
587
+ ? data.planName
588
+ : typeof data.plan === "string"
589
+ ? data.plan
590
+ : typeof data.level === "string"
591
+ ? data.level
592
+ : data.level != null
593
+ ? String(data.level)
594
+ : null,
595
+ primary_window: fiveHour,
596
+ secondary_window: weekly,
597
+ tertiary_window: monthly,
598
+ };
599
+ }
600
+
601
+ async function fetchZaiLimits({ env = process.env, fetchImpl = fetch } = {}) {
602
+ const auth = resolveZaiAuth({ env });
603
+ if (!auth) return { configured: false };
604
+
605
+ try {
606
+ const res = await fetchImpl(auth.endpoint, {
607
+ method: "GET",
608
+ headers: {
609
+ Authorization: auth.authorization,
610
+ Accept: "application/json",
611
+ },
612
+ });
613
+ if (res.status === 401 || res.status === 403) {
614
+ throw new Error("Z.AI token rejected. Check ZAI_API_KEY or ZHIPU_API_KEY.");
615
+ }
616
+ if (!res.ok) {
617
+ throw new Error(`Z.AI API returned ${res.status}`);
618
+ }
619
+ const json = await res.json();
620
+ if (json?.success === false) {
621
+ throw new Error(json?.msg || "Z.AI API returned an unsuccessful response.");
622
+ }
623
+ return {
624
+ configured: true,
625
+ error: null,
626
+ ...normalizeZaiUsageResponse(json),
627
+ };
628
+ } catch (error) {
629
+ return {
630
+ configured: true,
631
+ error: error?.message || "Unknown error",
632
+ };
633
+ }
634
+ }
635
+
497
636
  function resolveGeminiHome({ home, env } = {}) {
498
637
  const explicit = typeof env?.GEMINI_HOME === "string" ? env.GEMINI_HOME.trim() : "";
499
638
  return explicit ? path.resolve(explicit) : path.join(home, ".gemini");
@@ -1197,11 +1336,34 @@ function detectAntigravityProcess({ commandRunner } = {}) {
1197
1336
  return { configured: false };
1198
1337
  }
1199
1338
 
1200
- function resolveAntigravityLimitsCachePath({ home } = {}) {
1339
+ function resolveUsageLimitsCachePath({ home } = {}) {
1201
1340
  return path.join(home || os.homedir(), ".tokentracker", "tracker", ANTIGRAVITY_LIMITS_CACHE_FILE);
1202
1341
  }
1203
1342
 
1343
+ function readUsageLimitsCacheFile({ home } = {}) {
1344
+ try {
1345
+ return JSON.parse(fs.readFileSync(resolveUsageLimitsCachePath({ home }), "utf8"));
1346
+ } catch (_error) {
1347
+ return {};
1348
+ }
1349
+ }
1350
+
1351
+ function writeUsageLimitsCacheFile(update, { home } = {}) {
1352
+ const cachePath = resolveUsageLimitsCachePath({ home });
1353
+ try {
1354
+ const current = readUsageLimitsCacheFile({ home });
1355
+ fs.mkdirSync(path.dirname(cachePath), { recursive: true });
1356
+ const tmpPath = `${cachePath}.${process.pid}.tmp`;
1357
+ fs.writeFileSync(tmpPath, JSON.stringify({ ...current, ...update }, null, 2), { encoding: "utf8", mode: 0o600 });
1358
+ fs.renameSync(tmpPath, cachePath);
1359
+ } catch (_error) {}
1360
+ }
1361
+
1204
1362
  function parseTimeMs(value) {
1363
+ if (typeof value === "number") {
1364
+ if (!Number.isFinite(value) || value <= 0) return null;
1365
+ return value < 1_000_000_000_000 ? value * 1000 : value;
1366
+ }
1205
1367
  if (typeof value !== "string" || !value) return null;
1206
1368
  const ts = Date.parse(value);
1207
1369
  return Number.isFinite(ts) && ts > 0 ? ts : null;
@@ -1240,18 +1402,12 @@ function normalizeAntigravityCachedLimits(raw, { nowMs = Date.now() } = {}) {
1240
1402
  }
1241
1403
 
1242
1404
  function readAntigravityLimitsCache({ home, nowMs = Date.now() } = {}) {
1243
- const cachePath = resolveAntigravityLimitsCachePath({ home });
1244
- try {
1245
- const parsed = JSON.parse(fs.readFileSync(cachePath, "utf8"));
1246
- return normalizeAntigravityCachedLimits(parsed?.antigravity, { nowMs });
1247
- } catch (_error) {
1248
- return null;
1249
- }
1405
+ const parsed = readUsageLimitsCacheFile({ home });
1406
+ return normalizeAntigravityCachedLimits(parsed?.antigravity, { nowMs });
1250
1407
  }
1251
1408
 
1252
1409
  function writeAntigravityLimitsCache(limits, { home, nowMs = Date.now() } = {}) {
1253
1410
  if (!limits?.configured || limits.error || !hasAntigravityWindow(limits)) return;
1254
- const cachePath = resolveAntigravityLimitsCachePath({ home });
1255
1411
  const payload = {
1256
1412
  antigravity: {
1257
1413
  account_email: limits.account_email || null,
@@ -1262,12 +1418,62 @@ function writeAntigravityLimitsCache(limits, { home, nowMs = Date.now() } = {})
1262
1418
  cached_at: new Date(nowMs).toISOString(),
1263
1419
  },
1264
1420
  };
1265
- try {
1266
- fs.mkdirSync(path.dirname(cachePath), { recursive: true });
1267
- const tmpPath = `${cachePath}.${process.pid}.tmp`;
1268
- fs.writeFileSync(tmpPath, JSON.stringify(payload, null, 2), { encoding: "utf8", mode: 0o600 });
1269
- fs.renameSync(tmpPath, cachePath);
1270
- } catch (_error) {}
1421
+ writeUsageLimitsCacheFile(payload, { home });
1422
+ }
1423
+
1424
+ function hasClaudeWindow(limits) {
1425
+ return Boolean(limits?.five_hour || limits?.seven_day || limits?.seven_day_opus);
1426
+ }
1427
+
1428
+ function isClaudeCacheWindowUsable(window, { cachedAtMs, nowMs } = {}) {
1429
+ if (!window || typeof window !== "object") return false;
1430
+ const resetAtMs = parseTimeMs(window.resets_at);
1431
+ if (resetAtMs !== null) return resetAtMs > nowMs;
1432
+ return Number.isFinite(cachedAtMs)
1433
+ && nowMs - cachedAtMs <= CLAUDE_LIMITS_CACHE_MAX_AGE_MS;
1434
+ }
1435
+
1436
+ function normalizeClaudeCachedLimits(raw, { nowMs = Date.now() } = {}) {
1437
+ const cachedAtMs = parseTimeMs(raw?.cached_at);
1438
+ if (!Number.isFinite(cachedAtMs)) return null;
1439
+ if (cachedAtMs > nowMs + 60_000) return null;
1440
+ if (nowMs - cachedAtMs > CLAUDE_LIMITS_CACHE_MAX_AGE_MS) return null;
1441
+
1442
+ const cached = {
1443
+ configured: true,
1444
+ error: null,
1445
+ five_hour: isClaudeCacheWindowUsable(raw?.five_hour, { cachedAtMs, nowMs }) ? raw.five_hour : null,
1446
+ seven_day: isClaudeCacheWindowUsable(raw?.seven_day, { cachedAtMs, nowMs }) ? raw.seven_day : null,
1447
+ seven_day_opus: isClaudeCacheWindowUsable(raw?.seven_day_opus, { cachedAtMs, nowMs }) ? raw.seven_day_opus : null,
1448
+ extra_usage: raw?.extra_usage || null,
1449
+ cached: true,
1450
+ cached_at: raw.cached_at,
1451
+ stale_reason: typeof raw?.stale_reason === "string" ? raw.stale_reason : null,
1452
+ };
1453
+ return hasClaudeWindow(cached) ? cached : null;
1454
+ }
1455
+
1456
+ function readClaudeLimitsCache({ home, nowMs = Date.now(), staleReason = null } = {}) {
1457
+ const parsed = readUsageLimitsCacheFile({ home });
1458
+ const cached = normalizeClaudeCachedLimits(parsed?.claude, { nowMs });
1459
+ if (!cached) return null;
1460
+ return {
1461
+ ...cached,
1462
+ stale_reason: typeof staleReason === "string" && staleReason ? staleReason : cached.stale_reason,
1463
+ };
1464
+ }
1465
+
1466
+ function writeClaudeLimitsCache(limits, { home, nowMs = Date.now() } = {}) {
1467
+ if (!limits?.configured || limits.error || !hasClaudeWindow(limits)) return;
1468
+ writeUsageLimitsCacheFile({
1469
+ claude: {
1470
+ five_hour: limits.five_hour || null,
1471
+ seven_day: limits.seven_day || null,
1472
+ seven_day_opus: limits.seven_day_opus || null,
1473
+ extra_usage: limits.extra_usage || null,
1474
+ cached_at: new Date(nowMs).toISOString(),
1475
+ },
1476
+ }, { home });
1271
1477
  }
1272
1478
 
1273
1479
  function resolveLsofBinary({ commandRunner } = {}) {
@@ -1698,7 +1904,7 @@ async function getUsageLimits({
1698
1904
  const codexPlanType = codexAuthRefreshed?.planType || null;
1699
1905
 
1700
1906
  const providerFetch = withFetchTimeout(fetchImpl, providerTimeoutMs);
1701
- const [claudeResult, codexResult, cursor, kimi, gemini, kiro, antigravity, copilot] = await Promise.all([
1907
+ const [claudeResult, codexResult, cursor, kimi, zai, gemini, kiro, antigravity, copilot] = await Promise.all([
1702
1908
  claudeToken
1703
1909
  ? withProviderTimeout(fetchClaudeUsageLimits(claudeToken, { fetchImpl: providerFetch, maxAttempts: 1 }), "Claude", providerTimeoutMs).then(
1704
1910
  (value) => ({ status: "fulfilled", value }),
@@ -1719,6 +1925,8 @@ async function getUsageLimits({
1719
1925
  .catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
1720
1926
  withProviderTimeout(fetchKimiLimits({ home, env, fetchImpl: providerFetch }), "Kimi", providerTimeoutMs)
1721
1927
  .catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
1928
+ withProviderTimeout(fetchZaiLimits({ env, fetchImpl: providerFetch }), "Z.AI", providerTimeoutMs)
1929
+ .catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
1722
1930
  withProviderTimeout(fetchGeminiLimits({ home, env, fetchImpl: providerFetch, commandRunner }), "Gemini", providerTimeoutMs)
1723
1931
  .catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
1724
1932
  Promise.resolve().then(() => fetchKiroLimits({ commandRunner, now })),
@@ -1731,7 +1939,11 @@ async function getUsageLimits({
1731
1939
  if (!claudeToken) {
1732
1940
  claude = { configured: false };
1733
1941
  } else if (!claudeResult || claudeResult.status === "rejected") {
1734
- claude = { configured: true, error: claudeResult?.reason?.message || "Unknown error" };
1942
+ const reason = claudeResult?.reason;
1943
+ const cached = isClaudeTransientUsageError(reason)
1944
+ ? readClaudeLimitsCache({ home, nowMs, staleReason: reason?.message })
1945
+ : null;
1946
+ claude = cached || { configured: true, error: reason?.message || "Unknown error" };
1735
1947
  } else {
1736
1948
  claude = {
1737
1949
  configured: true,
@@ -1741,6 +1953,7 @@ async function getUsageLimits({
1741
1953
  seven_day_opus: claudeResult.value.seven_day_opus,
1742
1954
  extra_usage: claudeResult.value.extra_usage,
1743
1955
  };
1956
+ writeClaudeLimitsCache(claude, { home, nowMs });
1744
1957
  }
1745
1958
 
1746
1959
  let codex;
@@ -1772,6 +1985,7 @@ async function getUsageLimits({
1772
1985
  codex: withPlanLabel(codex, codex.plan_type, "Codex"),
1773
1986
  cursor: withPlanLabel(cursor, cursor.membership_type, "Cursor"),
1774
1987
  kimi: withPlanLabel(kimi, kimi.subscription_type || kimi.membership_level, "Kimi"),
1988
+ zai: withPlanLabel(zai, zai.account_plan, "Z.AI"),
1775
1989
  gemini: withPlanLabel(gemini, gemini.account_plan, "Gemini"),
1776
1990
  kiro: withPlanLabel(kiro, kiro.plan_name, "Kiro"),
1777
1991
  antigravity: withPlanLabel(antigravity, antigravity.account_plan, "Antigravity"),
@@ -1795,6 +2009,8 @@ module.exports = {
1795
2009
  normalizeCursorUsageSummary,
1796
2010
  normalizeGeminiQuotaResponse,
1797
2011
  normalizeKimiUsageResponse,
2012
+ normalizeZaiUsageResponse,
2013
+ fetchZaiLimits,
1798
2014
  parseKiroUsageOutput,
1799
2015
  normalizeAntigravityResponse,
1800
2016
  parseListeningPorts,
@@ -1,2 +0,0 @@
1
- import{j as i,O as f,A as s,r as E,az as I,i as P,L as O,aA as R}from"./main-fV3uYMtM.js";import{u as C}from"./use-usage-limits-BGYp5WOe.js";import{L as u,u as M}from"./use-limits-display-prefs-DGaESD6L.js";import{C as j}from"./Card-ijF07sCs.js";import{F as T}from"./FadeIn-BgK1lNmX.js";import{L as A}from"./LocalOnlyNotice-BLIekBBt.js";import"./arrow-up-right-CjOHnUd-.js";import"./download-aF04AJYI.js";const S=[3,2,3,3,2,2,3];function x({className:n}){return i.jsx("div",{className:f("rounded bg-oai-gray-200/70 dark:bg-oai-gray-800/70 animate-pulse",n)})}function D(){return i.jsxs("div",{className:"flex items-center gap-2",children:[i.jsx(x,{className:"h-3 w-12 shrink-0"}),i.jsx(x,{className:"flex-1 h-1.5 rounded-full min-w-0"}),i.jsx(x,{className:"h-3 w-[30px] shrink-0"}),i.jsx(x,{className:"h-3 w-6 shrink-0"})]})}function $({bars:n,index:e}){const r=e%3===0?"w-24":e%3===1?"w-20":"w-[4.5rem]";return i.jsxs("div",{className:"flex flex-col gap-1.5",children:[i.jsxs("div",{className:"flex items-center gap-1.5",children:[i.jsx(x,{className:"h-[14px] w-[14px] rounded shrink-0"}),i.jsx(x,{className:f("h-4",r)})]}),Array.from({length:n},(t,o)=>i.jsx(D,{},o))]})}function G(){return i.jsx(j,{children:i.jsxs("div",{className:"flex flex-col gap-3",children:[i.jsx(x,{className:"h-3.5 w-28"}),S.map((n,e)=>i.jsx($,{bars:n,index:e},e))]})})}function l(n){if(!n)return null;const e=typeof n=="number"?n*1e3:Date.parse(n);if(!Number.isFinite(e))return null;const r=e-Date.now();if(r<=0)return s("shared.time.now");const t=Math.floor(r/6e4);if(t<60)return`${t}m`;const o=Math.floor(t/60);return o<24?`${o}h`:`${Math.floor(o/24)}d`}function H(n,e){const r=e===u.REMAINING?100-n:n;return r>=90?"bg-red-500":r>=70?"bg-amber-500":"bg-emerald-500"}function a({label:n,pct:e,reset:r,mode:t=u.USED}){const o=Math.max(0,Math.min(100,Number(e)||0)),d=t===u.REMAINING?100-o:o,p=Math.round(d),y=d>0&&p===0?Math.max(d,.35):d,w=d>0&&p===0?"<1":String(p);return i.jsxs("div",{className:"flex items-center gap-2",children:[i.jsx("span",{className:"text-[11px] text-oai-gray-500 dark:text-oai-gray-400 w-12 shrink-0",children:n}),i.jsx("div",{className:"flex-1 bg-oai-gray-100 dark:bg-oai-gray-700/50 rounded-full h-1.5 overflow-hidden",children:i.jsx("div",{className:`${H(d,t)} rounded-full h-full transition-[width] duration-500 ease-out`,style:{width:`${y}%`,minWidth:d>0?"3px":0}})}),i.jsxs("span",{className:"text-[11px] tabular-nums text-oai-gray-500 dark:text-oai-gray-400 w-9 text-right shrink-0 whitespace-nowrap",children:[w,"%"]}),r?i.jsx("span",{className:"text-[10px] text-oai-gray-400 dark:text-oai-gray-500 w-6 text-right shrink-0",children:r}):null]})}function m({name:n,icon:e,children:r}){const o=e==="/brand-logos/cursor.svg"||e==="/brand-logos/kiro.svg"||e==="/brand-logos/copilot.svg"||e==="/brand-logos/kimi.svg"?"w-[14px] h-[14px] dark:invert":"w-[14px] h-[14px]";return i.jsxs("div",{className:"flex flex-col gap-1.5",children:[i.jsxs("div",{className:"flex items-center gap-1.5",children:[e?i.jsx("img",{src:e,alt:"",className:o}):null,i.jsx("span",{className:"text-sm font-medium text-oai-black dark:text-oai-white",children:n})]}),r]})}const F=["claude","codex","cursor","gemini","kimi","kiro","copilot","antigravity"],B={claude:{name:"Claude",icon:"/brand-logos/claude-code.svg"},codex:{name:"Codex",icon:"/brand-logos/codex.svg"},cursor:{name:"Cursor",icon:"/brand-logos/cursor.svg"},gemini:{name:"Gemini",icon:"/brand-logos/gemini.svg"},kimi:{name:"Kimi",icon:"/brand-logos/kimi.svg"},kiro:{name:"Kiro",icon:"/brand-logos/kiro.svg"},copilot:{name:"GitHub Copilot",icon:"/brand-logos/copilot.svg"},antigravity:{name:"Antigravity",icon:"/brand-logos/antigravity.svg"}};function c({children:n,tone:e="neutral"}){const r=e==="error"?"text-red-600 dark:text-red-400":"text-oai-gray-500 dark:text-oai-gray-400";return i.jsx("div",{className:`text-[11px] leading-snug ${r}`,children:n})}function U(n,e,r){const t=B[n];if(!t)return null;if(!e?.configured)return i.jsx(m,{name:t.name,icon:t.icon,children:i.jsx(c,{children:s("limits.status.not_connected")})},n);if(e.error)return i.jsx(m,{name:t.name,icon:t.icon,children:i.jsx(c,{tone:"error",children:s("shared.error.prefix",{error:e.error})})},n);const o=e.plan_label?`${t.name} ${e.plan_label}`:t.name;switch(n){case"claude":return i.jsxs(m,{name:o,icon:t.icon,children:[e.five_hour?i.jsx(a,{label:"5h",pct:e.five_hour.utilization,reset:l(e.five_hour.resets_at),mode:r}):null,e.seven_day?i.jsx(a,{label:"7d",pct:e.seven_day.utilization,reset:l(e.seven_day.resets_at),mode:r}):null,e.seven_day_opus?i.jsx(a,{label:"Opus",pct:e.seven_day_opus.utilization,reset:l(e.seven_day_opus.resets_at),mode:r}):null,!e.five_hour&&!e.seven_day&&!e.seven_day_opus?i.jsx(c,{children:s("limits.status.no_data")}):null]},"claude");case"codex":return i.jsxs(m,{name:o,icon:t.icon,children:[e.primary_window?i.jsx(a,{label:"5h",pct:e.primary_window.used_percent,reset:l(e.primary_window.reset_at),mode:r}):null,e.secondary_window?i.jsx(a,{label:"7d",pct:e.secondary_window.used_percent,reset:l(e.secondary_window.reset_at),mode:r}):null,!e.primary_window&&!e.secondary_window?i.jsx(c,{children:s("limits.status.no_data")}):null]},"codex");case"cursor":return i.jsxs(m,{name:o,icon:t.icon,children:[e.primary_window?i.jsx(a,{label:s("limits.label.cursor_plan"),pct:e.primary_window.used_percent,reset:l(e.primary_window.reset_at),mode:r}):null,e.secondary_window?i.jsx(a,{label:s("limits.label.cursor_auto"),pct:e.secondary_window.used_percent,reset:l(e.secondary_window.reset_at),mode:r}):null,e.tertiary_window?i.jsx(a,{label:s("limits.label.cursor_api"),pct:e.tertiary_window.used_percent,reset:l(e.tertiary_window.reset_at),mode:r}):null,!e.primary_window&&!e.secondary_window&&!e.tertiary_window?i.jsx(c,{children:s("limits.status.no_data")}):null]},"cursor");case"gemini":return i.jsxs(m,{name:o,icon:t.icon,children:[e.primary_window?i.jsx(a,{label:"Pro",pct:e.primary_window.used_percent,reset:l(e.primary_window.reset_at),mode:r}):null,e.secondary_window?i.jsx(a,{label:"Flash",pct:e.secondary_window.used_percent,reset:l(e.secondary_window.reset_at),mode:r}):null,e.tertiary_window?i.jsx(a,{label:"Lite",pct:e.tertiary_window.used_percent,reset:l(e.tertiary_window.reset_at),mode:r}):null,!e.primary_window&&!e.secondary_window&&!e.tertiary_window?i.jsx(c,{children:s("limits.status.no_data")}):null]},"gemini");case"kimi":return i.jsxs(m,{name:o,icon:t.icon,children:[e.primary_window?i.jsx(a,{label:s("limits.label.kimi_weekly"),pct:e.primary_window.used_percent,reset:l(e.primary_window.reset_at),mode:r}):null,e.secondary_window?i.jsx(a,{label:s("limits.label.kimi_5h"),pct:e.secondary_window.used_percent,reset:l(e.secondary_window.reset_at),mode:r}):null,e.tertiary_window?i.jsx(a,{label:s("limits.label.kimi_total"),pct:e.tertiary_window.used_percent,reset:l(e.tertiary_window.reset_at),mode:r}):null,e.parallel_limit?i.jsx(c,{children:s("limits.label.kimi_parallel",{count:e.parallel_limit})}):null,!e.primary_window&&!e.secondary_window&&!e.tertiary_window?i.jsx(c,{children:s("limits.status.no_data")}):null]},"kimi");case"kiro":return i.jsxs(m,{name:o,icon:t.icon,children:[e.primary_window?i.jsx(a,{label:s("limits.label.kiro_month"),pct:e.primary_window.used_percent,reset:l(e.primary_window.reset_at),mode:r}):null,e.secondary_window?i.jsx(a,{label:s("limits.label.kiro_bonus"),pct:e.secondary_window.used_percent,reset:l(e.secondary_window.reset_at),mode:r}):null,!e.primary_window&&!e.secondary_window?i.jsx(c,{children:s("limits.status.no_data")}):null]},"kiro");case"antigravity":return i.jsxs(m,{name:o,icon:t.icon,children:[e.primary_window?i.jsx(a,{label:"Claude",pct:e.primary_window.used_percent,reset:l(e.primary_window.reset_at),mode:r}):null,e.secondary_window?i.jsx(a,{label:"G Pro",pct:e.secondary_window.used_percent,reset:l(e.secondary_window.reset_at),mode:r}):null,e.tertiary_window?i.jsx(a,{label:"Flash",pct:e.tertiary_window.used_percent,reset:l(e.tertiary_window.reset_at),mode:r}):null,!e.primary_window&&!e.secondary_window&&!e.tertiary_window?i.jsx(c,{children:s("limits.status.no_data")}):null]},"antigravity");case"copilot":return i.jsxs(m,{name:o,icon:t.icon,children:[e.primary_window?i.jsx(a,{label:s("limits.label.copilot_premium"),pct:e.primary_window.used_percent,reset:l(e.primary_window.reset_at),mode:r}):null,e.secondary_window?i.jsx(a,{label:s("limits.label.copilot_chat"),pct:e.secondary_window.used_percent,reset:l(e.secondary_window.reset_at),mode:r}):null,!e.primary_window&&!e.secondary_window?i.jsx(c,{children:s("limits.status.no_data")}):null,e.otel_has_files||e.otel_enabled?null:i.jsx(z,{defaultDir:e.otel_default_dir})]},"copilot");default:return null}}function z({defaultDir:n}){const[e,r]=E.useState(!1),o=["export COPILOT_OTEL_ENABLED=true","export COPILOT_OTEL_EXPORTER_TYPE=file",`export COPILOT_OTEL_FILE_EXPORTER_PATH="${n||"$HOME/.copilot/otel"}/copilot-otel-$(date +%Y%m%d).jsonl"`].join(`
2
- `),d=async()=>{try{await navigator.clipboard.writeText(o),r(!0),setTimeout(()=>r(!1),1600)}catch{}};return i.jsxs("div",{className:"mt-1 rounded-md border border-amber-300/60 dark:border-amber-700/40 bg-amber-50/50 dark:bg-amber-900/10 px-2.5 py-2 text-[11px] text-oai-gray-600 dark:text-oai-gray-300",children:[i.jsx("div",{className:"font-medium text-oai-gray-700 dark:text-oai-gray-200",children:s("limits.copilot.otelHint.title")}),i.jsx("div",{className:"mt-0.5 leading-snug",children:s("limits.copilot.otelHint.body")}),i.jsx("pre",{className:"mt-1.5 overflow-x-auto rounded bg-oai-gray-100 dark:bg-oai-gray-900/60 px-2 py-1.5 font-mono text-[10.5px] leading-tight whitespace-pre",children:o}),i.jsx("button",{type:"button",onClick:d,className:"mt-1 inline-flex items-center gap-1 rounded border border-oai-gray-300 dark:border-oai-gray-700 px-1.5 py-0.5 text-[10.5px] text-oai-gray-700 dark:text-oai-gray-200 hover:bg-oai-gray-100 dark:hover:bg-oai-gray-800 transition-colors",children:e?s("limits.copilot.otelHint.copied"):s("limits.copilot.otelHint.copy")})]})}function Y({claude:n,codex:e,cursor:r,gemini:t,kimi:o,kiro:d,antigravity:p,copilot:y,order:w,visibility:h,displayMode:v}){const k={claude:n,codex:e,cursor:r,gemini:t,kimi:o,kiro:d,antigravity:p,copilot:y},N=Array.isArray(w)&&w.length>0?w:F,g=v===u.REMAINING?u.REMAINING:u.USED,L=g===u.REMAINING?s("limits.settings.display_mode_remaining"):s("limits.settings.display_mode_used"),b=N.filter(_=>!h||h[_]!==!1).map(_=>U(_,k[_],g)).filter(Boolean);return i.jsx(T,{delay:.15,children:i.jsx(j,{children:i.jsxs("div",{className:"flex flex-col gap-3",children:[i.jsxs("h3",{className:"text-sm font-medium text-oai-gray-500 dark:text-oai-gray-300 uppercase tracking-wide",children:[s("limits.panel.title"),s("limits.panel.mode_separator"),L]}),b.length>0?b:i.jsx(c,{children:s("limits.status.all_hidden")})]})})})}const K=typeof window<"u"&&(window.location.hostname==="localhost"||window.location.hostname==="127.0.0.1");function ie(){const n=I(),{data:e,error:r,isLoading:t}=C(n?{initialRefresh:!0,initialState:n,publishToPreloadCache:!0}:{initialRefresh:!0,publishToPreloadCache:!0}),o=M();return!K&&!P()?i.jsx("div",{className:"flex flex-col flex-1 text-oai-black dark:text-oai-white font-oai antialiased",children:i.jsx(A,{})}):i.jsx("div",{className:"flex flex-col flex-1 text-oai-black dark:text-oai-white font-oai antialiased",children:i.jsx("main",{className:"flex-1 pt-8 sm:pt-10 pb-12 sm:pb-16",children:i.jsxs("div",{className:"mx-auto max-w-6xl px-4 sm:px-6",children:[i.jsxs("div",{className:"flex flex-row items-start justify-between gap-4 mb-8",children:[i.jsxs("div",{className:"min-w-0",children:[i.jsx("h1",{className:"text-3xl sm:text-4xl font-semibold tracking-tight text-oai-black dark:text-white mb-3",children:s("nav.limits")}),i.jsx("p",{className:"text-oai-gray-500 dark:text-oai-gray-400 text-sm sm:text-base",children:s("limits.page.subtitle")})]}),i.jsx(O,{to:"/settings","aria-label":s("limits.page.openSettings"),title:s("limits.page.openSettings"),className:"shrink-0 inline-flex h-9 w-9 items-center justify-center rounded-lg border border-oai-gray-200 dark:border-oai-gray-800 text-oai-gray-600 dark:text-oai-gray-400 hover:bg-oai-gray-100 dark:hover:bg-oai-gray-800 hover:text-oai-black dark:hover:text-white transition-colors no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-oai-brand-500",children:i.jsx(R,{className:"h-4 w-4","aria-hidden":!0})})]}),t?i.jsx(G,{}):i.jsxs(i.Fragment,{children:[r?i.jsx("p",{className:"mb-4 text-sm text-red-500 dark:text-red-400",children:s("shared.error.prefix",{error:r})}):null,i.jsx(Y,{claude:e?.claude,codex:e?.codex,cursor:e?.cursor,gemini:e?.gemini,kimi:e?.kimi,kiro:e?.kiro,antigravity:e?.antigravity,copilot:e?.copilot,order:o.order,visibility:o.visibility,displayMode:o.displayMode})]})]})})})}export{ie as LimitsPage};
@@ -1 +0,0 @@
1
- import{r as o,aS as I,aT as h,aU as C,aV as V}from"./main-fV3uYMtM.js";const d=["claude","codex","cursor","gemini","kimi","kiro","copilot","antigravity"],Y={claude:"Claude",codex:"Codex",cursor:"Cursor",gemini:"Gemini",kimi:"Kimi",kiro:"Kiro",copilot:"GitHub Copilot",antigravity:"Antigravity"},P={claude:"/brand-logos/claude-code.svg",codex:"/brand-logos/codex.svg",cursor:"/brand-logos/cursor.svg",gemini:"/brand-logos/gemini.svg",kimi:"/brand-logos/kimi.svg",kiro:"/brand-logos/kiro.svg",copilot:"/brand-logos/copilot.svg",antigravity:"/brand-logos/antigravity.svg"},w="tt.limits.providerOrder",m="tt.limits.providerVisibility",S="tt.limits.displayMode",p="limitsDisplayMode",f=Object.freeze({USED:"used",REMAINING:"remaining"}),E=new Set(Object.values(f));function O(){if(typeof window>"u")return[...d];try{const n=window.localStorage.getItem(w);if(!n)return[...d];const a=JSON.parse(n);if(!Array.isArray(a))return[...d];const r=a.filter(c=>d.includes(c));for(const c of d)r.includes(c)||r.push(c);return r}catch{return[...d]}}function D(){const n=Object.fromEntries(d.map(a=>[a,!0]));if(typeof window>"u")return n;try{const a=window.localStorage.getItem(m);if(!a)return n;const r=JSON.parse(a);if(!r||typeof r!="object")return n;const c={...n};for(const u of d)typeof r[u]=="boolean"&&(c[u]=r[u]);return c}catch{return n}}function v(){if(typeof window>"u")return f.USED;try{const n=window.localStorage.getItem(S);return E.has(n)?n:f.USED}catch{return f.USED}}function T(){const[n,a]=o.useState(O),[r,c]=o.useState(D),[u,l]=o.useState(v),g=o.useCallback(t=>{E.has(t)&&(l(t),I()&&h(p,t))},[]);o.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(w,JSON.stringify(n))}catch{}},[n]),o.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(m,JSON.stringify(r))}catch{}},[r]),o.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(S,u)}catch{}},[u]),o.useEffect(()=>{if(!I())return;const t=C(e=>{const i=e?.[p];E.has(i)&&l(i)});return V(),t},[]),o.useEffect(()=>{if(typeof window>"u")return;const t=e=>{e.key===w&&a(O()),e.key===m&&c(D()),e.key===S&&l(v())};return window.addEventListener("storage",t),()=>window.removeEventListener("storage",t)},[]);const k=o.useCallback(t=>{c(e=>({...e,[t]:!e[t]}))},[]),x=o.useCallback(t=>{a(e=>{const i=e.indexOf(t);if(i<=0)return e;const s=[...e];return[s[i-1],s[i]]=[s[i],s[i-1]],s})},[]),M=o.useCallback(t=>{a(e=>{const i=e.indexOf(t);if(i<0||i>=e.length-1)return e;const s=[...e];return[s[i],s[i+1]]=[s[i+1],s[i]],s})},[]),_=o.useCallback((t,e)=>{t!==e&&a(i=>{const s=i.indexOf(t),b=i.indexOf(e);if(s<0||b<0)return i;const y=[...i],[A]=y.splice(s,1);return y.splice(b,0,A),y})},[]),L=o.useCallback(()=>{a([...d]),c(Object.fromEntries(d.map(t=>[t,!0]))),g(f.USED)},[g]),N=o.useMemo(()=>n.filter(t=>r[t]!==!1),[n,r]);return{order:n,visibility:r,displayMode:u,setDisplayMode:g,visibleOrdered:N,toggle:k,moveUp:x,moveDown:M,moveToward:_,reset:L}}export{f as L,P as a,Y as b,T as u};