tokentracker-cli 0.69.0 → 0.71.0

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 (56) hide show
  1. package/dashboard/dist/assets/{ActivityHeatmap-Bqy-meR1.js → ActivityHeatmap-Dk9JY1ug.js} +1 -1
  2. package/dashboard/dist/assets/{Card-DbzltTXg.js → Card-CNWjr8mp.js} +1 -1
  3. package/dashboard/dist/assets/{CommunityStatsModal-CumafhRo.js → CommunityStatsModal-C2qyGffN.js} +1 -1
  4. package/dashboard/dist/assets/{ConfirmModal-CfmWYFAl.js → ConfirmModal-DSu8Tu1r.js} +1 -1
  5. package/dashboard/dist/assets/{DashboardPage-beIJMVvO.js → DashboardPage-DIf7YTQ9.js} +1 -1
  6. package/dashboard/dist/assets/{DevicePage-Cb_cMnJj.js → DevicePage-CXQPPyMG.js} +1 -1
  7. package/dashboard/dist/assets/{DialogClose-245t1wj3.js → DialogClose-Cl6zG-Oy.js} +1 -1
  8. package/dashboard/dist/assets/DialogDescription-Bg5Mv-UG.js +1 -0
  9. package/dashboard/dist/assets/{DialogTitle-C_S_W-qN.js → DialogTitle-BRCsGR2a.js} +1 -1
  10. package/dashboard/dist/assets/{FadeIn-9qSEABKW.js → FadeIn-B3oaxBze.js} +1 -1
  11. package/dashboard/dist/assets/{HeaderGithubStar-C42NExcO.js → HeaderGithubStar-Dl3VFKq_.js} +1 -1
  12. package/dashboard/dist/assets/{IpCheckPage-C_LVvRK3.js → IpCheckPage-BJGOP6DZ.js} +1 -1
  13. package/dashboard/dist/assets/{LandingPage-1ng5HTtU.js → LandingPage-CM30tqFJ.js} +2 -2
  14. package/dashboard/dist/assets/{LeaderboardAvatar-Dud2isij.js → LeaderboardAvatar-P3H4JJH7.js} +1 -1
  15. package/dashboard/dist/assets/{LeaderboardPage-BVLHknpa.js → LeaderboardPage-CMgku-JV.js} +2 -2
  16. package/dashboard/dist/assets/{LeaderboardProfileModal-creqSCBC.js → LeaderboardProfileModal-1pDzkqlc.js} +1 -1
  17. package/dashboard/dist/assets/{LeaderboardProfilePage-CtCiW1ZR.js → LeaderboardProfilePage-CXv6v78X.js} +1 -1
  18. package/dashboard/dist/assets/{LimitsPage-DXhNfD6c.js → LimitsPage-DPmTlaMP.js} +1 -1
  19. package/dashboard/dist/assets/{LocalOnlyNotice-CupEXgLA.js → LocalOnlyNotice-DZ2JWpUi.js} +1 -1
  20. package/dashboard/dist/assets/{LoginPage-Bvb6khA_.js → LoginPage-yZfMo0Bn.js} +1 -1
  21. package/dashboard/dist/assets/{PopoverPopup-1RwMOuZR.js → PopoverPopup-C7ClU-a4.js} +1 -1
  22. package/dashboard/dist/assets/{ResetPasswordPage-C1t2TVSo.js → ResetPasswordPage-DRxzMEet.js} +1 -1
  23. package/dashboard/dist/assets/{Select-35dakfdf.js → Select-ShDjR08e.js} +1 -1
  24. package/dashboard/dist/assets/{SelectItemText-Bn80bCzm.js → SelectItemText-Cdb3AE2d.js} +1 -1
  25. package/dashboard/dist/assets/{SettingsPage-BtNI2EB5.js → SettingsPage-KhtHkiXb.js} +1 -1
  26. package/dashboard/dist/assets/{SkillsPage-DrtaOCt8.js → SkillsPage-BfvTyaan.js} +1 -1
  27. package/dashboard/dist/assets/{WidgetsPage-DBP3CcAv.js → WidgetsPage-B9WHyvqX.js} +1 -1
  28. package/dashboard/dist/assets/{WrappedPage-Dxzewd0b.js → WrappedPage-BVY8Z7e8.js} +1 -1
  29. package/dashboard/dist/assets/{agent-logos-DXWdLSyr.js → agent-logos-w8DphLG5.js} +1 -1
  30. package/dashboard/dist/assets/{arrow-up-right-CAv_ofGl.js → arrow-up-right-CCe_6-J8.js} +1 -1
  31. package/dashboard/dist/assets/check-BuuQsfIt.js +1 -0
  32. package/dashboard/dist/assets/{copy-CkAl55Gc.js → copy-B6Uad-NU.js} +1 -1
  33. package/dashboard/dist/assets/{download-CHuHPQRG.js → download-BYcyu1Bg.js} +1 -1
  34. package/dashboard/dist/assets/{format-tokens-BhF7ktma.js → format-tokens-BFn66jct.js} +1 -1
  35. package/dashboard/dist/assets/{info-4RqgE-Sk.js → info-CTKpV2vu.js} +1 -1
  36. package/dashboard/dist/assets/main-DXM2UPde.css +1 -0
  37. package/dashboard/dist/assets/{main-Cy1-zTjI.js → main-DbLbrnIE.js} +3 -3
  38. package/dashboard/dist/assets/{use-community-stats-BHARwgjJ.js → use-community-stats-Bumdx05F.js} +1 -1
  39. package/dashboard/dist/assets/{use-limits-display-prefs-D6TuBuXc.js → use-limits-display-prefs-DeSmHt-l.js} +1 -1
  40. package/dashboard/dist/assets/{use-native-settings-8aQ6BEe6.js → use-native-settings-CdTOidJ4.js} +1 -1
  41. package/dashboard/dist/assets/{use-ordered-list-B0XApham.js → use-ordered-list-DQoW_9df.js} +1 -1
  42. package/dashboard/dist/assets/{use-quality-per-dollar-pref-DNa5uaGP.js → use-quality-per-dollar-pref-CwFs7F06.js} +1 -1
  43. package/dashboard/dist/assets/{use-usage-limits-C1V3apqc.js → use-usage-limits-CKuCunJc.js} +1 -1
  44. package/dashboard/dist/assets/{useCurrency-BEkNDMgc.js → useCurrency-CG5fqlMR.js} +1 -1
  45. package/dashboard/dist/assets/{useScrollLock-BRpWM_0H.js → useScrollLock-DsnK0M2M.js} +1 -1
  46. package/dashboard/dist/index.html +2 -2
  47. package/dashboard/dist/share.html +2 -2
  48. package/package.json +1 -1
  49. package/src/commands/status.js +8 -5
  50. package/src/commands/sync.js +1 -1
  51. package/src/lib/pricing/seed-snapshot.json +1 -1
  52. package/src/lib/rollout.js +502 -110
  53. package/src/lib/usage-limits.js +80 -14
  54. package/dashboard/dist/assets/DialogDescription-CMG2f0Xr.js +0 -1
  55. package/dashboard/dist/assets/check-BzPtb9x7.js +0 -1
  56. package/dashboard/dist/assets/main-CjU-7igC.css +0 -1
@@ -28,9 +28,15 @@ const { fetchZcodeLimits } = require("./zcode-limits");
28
28
  const { fetchOpencodeGoLimits } = require("./opencode-go-limits");
29
29
  const { readSqliteJsonRows } = require("./sqlite-reader");
30
30
 
31
- // 2-minute in-memory cache
32
- let cache = { data: null, fetchedAt: 0 };
31
+ // 2-minute in-memory cache. It also expires early at the earliest upcoming window
32
+ // reset in the cached data (see cacheExpiresAtMs), floored so a provider reporting
33
+ // a reset "right now" can't turn every poll into a full upstream round.
34
+ let cache = { data: null, expiresAtMs: 0 };
33
35
  const CACHE_TTL_MS = 2 * 60 * 1000;
36
+ // Must stay below the macOS app's post-reset re-fetch grace (10s in
37
+ // DashboardViewModel.resetBoundaryGrace), or that targeted refresh would be
38
+ // served this cache's pre-reset snapshot and miss the rollover.
39
+ const CACHE_MIN_TTL_MS = 5 * 1000;
34
40
  const DEFAULT_PROVIDER_TIMEOUT_MS = 15_000;
35
41
  const ANTIGRAVITY_LIMITS_CACHE_FILE = "usage-limits-cache.json";
36
42
  const ANTIGRAVITY_LIMITS_CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
@@ -1921,24 +1927,48 @@ function normalizeClaudeCachedLimits(
1921
1927
  return hasClaudeWindow(cached) ? cached : null;
1922
1928
  }
1923
1929
 
1930
+ function readClaudeLimitsCacheRaw({ home } = {}) {
1931
+ try {
1932
+ const parsed = JSON.parse(fs.readFileSync(resolveClaudeLimitsCachePath({ home }), "utf8"));
1933
+ return parsed?.claude ?? null;
1934
+ } catch (_error) {
1935
+ return null;
1936
+ }
1937
+ }
1938
+
1924
1939
  function readClaudeLimitsCache({
1925
1940
  home,
1926
1941
  nowMs = Date.now(),
1927
1942
  maxAgeMs = CLAUDE_LIMITS_CACHE_MAX_AGE_MS,
1928
1943
  stale = true,
1929
1944
  } = {}) {
1930
- const cachePath = resolveClaudeLimitsCachePath({ home });
1931
- try {
1932
- const parsed = JSON.parse(fs.readFileSync(cachePath, "utf8"));
1933
- return normalizeClaudeCachedLimits(parsed?.claude, { nowMs, maxAgeMs, stale });
1934
- } catch (_error) {
1935
- return null;
1936
- }
1945
+ return normalizeClaudeCachedLimits(readClaudeLimitsCacheRaw({ home }), { nowMs, maxAgeMs, stale });
1946
+ }
1947
+
1948
+ // A cached snapshot stops being "fresh" the moment any of its windows crosses the
1949
+ // reset boundary it was written with: serving it past that point hides the rollover
1950
+ // from reset detection (the menu bar confetti) for up to the fresh TTL. Only resets
1951
+ // that were still ahead at write time count — a provider stamping already-past reset
1952
+ // times must not force a live call on every poll.
1953
+ function claudeCacheCrossedReset(raw, { nowMs } = {}) {
1954
+ const cachedAtMs = parseTimeMs(raw?.cached_at);
1955
+ if (!Number.isFinite(cachedAtMs)) return false;
1956
+ const windows = [
1957
+ raw?.five_hour,
1958
+ raw?.seven_day,
1959
+ raw?.seven_day_opus,
1960
+ ...(Array.isArray(raw?.weekly_scoped) ? raw.weekly_scoped : []),
1961
+ ];
1962
+ return windows.some((window) => {
1963
+ const resetMs = parseTimeMs(window?.resets_at);
1964
+ return resetMs !== null && resetMs > cachedAtMs && resetMs <= nowMs;
1965
+ });
1937
1966
  }
1938
1967
 
1939
1968
  function readFreshClaudeLimitsCache({ home, nowMs = Date.now() } = {}) {
1940
- return readClaudeLimitsCache({
1941
- home,
1969
+ const raw = readClaudeLimitsCacheRaw({ home });
1970
+ if (!raw || claudeCacheCrossedReset(raw, { nowMs })) return null;
1971
+ return normalizeClaudeCachedLimits(raw, {
1942
1972
  nowMs,
1943
1973
  maxAgeMs: CLAUDE_LIMITS_CACHE_FRESH_TTL_MS,
1944
1974
  stale: false,
@@ -2564,9 +2594,44 @@ function withPlanLabel(obj, raw, brand) {
2564
2594
  // in-flight fetch and returns its result.
2565
2595
  let inFlightFetch = null;
2566
2596
 
2597
+ // Codex stamps reset_at as unix seconds; every other provider (and Claude's
2598
+ // resets_at) uses ISO strings. Numbers that look like epoch milliseconds are
2599
+ // accepted as-is so a future provider can't silently regress this.
2600
+ function resetValueToMs(value) {
2601
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
2602
+ return value < 1e12 ? value * 1000 : value;
2603
+ }
2604
+ return parseTimeMs(value);
2605
+ }
2606
+
2607
+ // Walk the aggregated response and collect every window reset timestamp,
2608
+ // whatever the provider-specific nesting looks like.
2609
+ function collectResetTimesMs(node, out) {
2610
+ if (!node || typeof node !== "object") return;
2611
+ for (const [key, value] of Object.entries(node)) {
2612
+ if (key === "reset_at" || key === "resets_at") {
2613
+ const ms = resetValueToMs(value);
2614
+ if (ms !== null) out.push(ms);
2615
+ } else if (value && typeof value === "object") {
2616
+ collectResetTimesMs(value, out);
2617
+ }
2618
+ }
2619
+ }
2620
+
2621
+ // The moment any window rolls over, cached data stops being current — reset
2622
+ // detection (the menu bar confetti) would otherwise lag by up to the full TTL.
2623
+ function cacheExpiresAtMs(data, fetchedAtMs) {
2624
+ const times = [];
2625
+ collectResetTimesMs(data, times);
2626
+ const upcoming = times.filter((t) => t > fetchedAtMs);
2627
+ const ttlExpiry = fetchedAtMs + CACHE_TTL_MS;
2628
+ if (upcoming.length === 0) return ttlExpiry;
2629
+ return Math.min(ttlExpiry, Math.max(Math.min(...upcoming), fetchedAtMs + CACHE_MIN_TTL_MS));
2630
+ }
2631
+
2567
2632
  async function getUsageLimits(options = {}) {
2568
2633
  const nowMs = Date.now();
2569
- if (cache.data && nowMs - cache.fetchedAt < CACHE_TTL_MS) {
2634
+ if (cache.data && nowMs < cache.expiresAtMs) {
2570
2635
  return cache.data;
2571
2636
  }
2572
2637
  if (inFlightFetch) {
@@ -2775,18 +2840,19 @@ async function fetchUsageLimitsUncached({
2775
2840
  opencodeGo: withPlanLabel(opencodeGo, opencodeGo?.plan_label, "OpenCode Go"),
2776
2841
  };
2777
2842
 
2778
- cache = { data, fetchedAt: nowMs };
2843
+ cache = { data, expiresAtMs: cacheExpiresAtMs(data, nowMs) };
2779
2844
  return data;
2780
2845
  }
2781
2846
 
2782
2847
  function resetUsageLimitsCache() {
2783
- cache = { data: null, fetchedAt: 0 };
2848
+ cache = { data: null, expiresAtMs: 0 };
2784
2849
  }
2785
2850
 
2786
2851
  module.exports = {
2787
2852
  getUsageLimits,
2788
2853
  normalizePlanLabel,
2789
2854
  resetUsageLimitsCache,
2855
+ cacheExpiresAtMs,
2790
2856
  runCommand,
2791
2857
  extractGeminiOauthClientCredentials,
2792
2858
  loadKimiCredentials,
@@ -1 +0,0 @@
1
- import{r as n,b9 as a}from"./main-Cy1-zTjI.js";import{u as p}from"./DialogTitle-C_S_W-qN.js";import{u as c}from"./useScrollLock-BRpWM_0H.js";const x=n.forwardRef(function(e,r){const{render:l,className:u,style:m,id:s,...t}=e,{store:i}=p(),o=c(s);return i.useSyncedValueWithCleanup("descriptionElementId",o),a("p",e,{ref:r,props:[{id:o},t]})});export{x as D};
@@ -1 +0,0 @@
1
- import{c}from"./main-Cy1-zTjI.js";const e=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],t=c("check",e);export{t as C};