tokentracker-cli 0.79.8 → 0.80.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 (77) hide show
  1. package/dashboard/dist/assets/{AchievementBadge-Ctu3c_8t.js → AchievementBadge-D9jOMhlD.js} +1 -1
  2. package/dashboard/dist/assets/{AchievementsPage-B3kBTHDB.js → AchievementsPage-DuswZa4g.js} +2 -2
  3. package/dashboard/dist/assets/{AchievementsSection-CAnTXGpC.js → AchievementsSection-BJPeE1Uq.js} +1 -1
  4. package/dashboard/dist/assets/{BadgeDetailModal-oTUXEQk9.js → BadgeDetailModal-nNPIsy6i.js} +1 -1
  5. package/dashboard/dist/assets/{Card-BHt5f5cD.js → Card-VENq8eEp.js} +1 -1
  6. package/dashboard/dist/assets/{ClawdAnimated-ChxXY3l0.js → ClawdAnimated-Cq7TW0vv.js} +1 -1
  7. package/dashboard/dist/assets/{CommunityStatsModal-CVLlItP1.js → CommunityStatsModal-y2VftQuq.js} +1 -1
  8. package/dashboard/dist/assets/{ConfirmModal-ClrILpb3.js → ConfirmModal-CWgnJ4hm.js} +1 -1
  9. package/dashboard/dist/assets/{Controls-D_XBBpIh.js → Controls-BUQ_iZZq.js} +1 -1
  10. package/dashboard/dist/assets/DashboardPage-WkiygMmq.js +19 -0
  11. package/dashboard/dist/assets/{DevicePage-J7DW7yeX.js → DevicePage-Dh3XDtNx.js} +1 -1
  12. package/dashboard/dist/assets/{DialogClose-B1r9gzUF.js → DialogClose-KTFU_B3F.js} +1 -1
  13. package/dashboard/dist/assets/DialogDescription-DIra288n.js +1 -0
  14. package/dashboard/dist/assets/{DialogTitle-BL2puDA7.js → DialogTitle-bza2pIek.js} +1 -1
  15. package/dashboard/dist/assets/{FadeIn-Cd-YT4oT.js → FadeIn-Dm3FVNfr.js} +1 -1
  16. package/dashboard/dist/assets/{HeaderGithubStar-BAtl4a-K.js → HeaderGithubStar-CbSElzLt.js} +1 -1
  17. package/dashboard/dist/assets/{IpCheckPage-BXK_cYN2.js → IpCheckPage-BDON7VCU.js} +1 -1
  18. package/dashboard/dist/assets/{LandingPage-C_16AbpF.js → LandingPage-Ck3CwZrt.js} +1 -1
  19. package/dashboard/dist/assets/{LeaderboardAvatar-B0vUuCYU.js → LeaderboardAvatar-GVUy0Ynq.js} +1 -1
  20. package/dashboard/dist/assets/{LeaderboardPage-Duhdaq09.js → LeaderboardPage-C19Rkt44.js} +2 -2
  21. package/dashboard/dist/assets/{LeaderboardProfileModal-CFFyHRTB.js → LeaderboardProfileModal-QcEq_o6E.js} +3 -3
  22. package/dashboard/dist/assets/{LeaderboardProfilePage-CjTelV-4.js → LeaderboardProfilePage-pFhI6292.js} +1 -1
  23. package/dashboard/dist/assets/{LimitsPage-DIcXRgSv.js → LimitsPage-ChV3Qezd.js} +1 -1
  24. package/dashboard/dist/assets/{LocalOnlyNotice-DC7wXWds.js → LocalOnlyNotice-ALvpjznF.js} +1 -1
  25. package/dashboard/dist/assets/{LoginPage--8hYT3aq.js → LoginPage-CL-QrJxU.js} +1 -1
  26. package/dashboard/dist/assets/{PetPage-ByWUW7-V.js → PetPage-DSuvdGgd.js} +1 -1
  27. package/dashboard/dist/assets/{PopoverPopup-g_v1fmhH.js → PopoverPopup-jSDQ87Bi.js} +1 -1
  28. package/dashboard/dist/assets/{ResetPasswordPage-BfunstIi.js → ResetPasswordPage-Dk2IDAJq.js} +1 -1
  29. package/dashboard/dist/assets/{Select-C4ShPt1t.js → Select-Cz6WTj7D.js} +1 -1
  30. package/dashboard/dist/assets/{SelectItemText-gw_TdvY6.js → SelectItemText-BZyRjWCM.js} +1 -1
  31. package/dashboard/dist/assets/{SettingsPage-BFCFrDuA.js → SettingsPage-CANz8HMM.js} +1 -1
  32. package/dashboard/dist/assets/{SkillsPage-BNQDySeK.js → SkillsPage-WZkQG_83.js} +1 -1
  33. package/dashboard/dist/assets/{WidgetsPage-B3bHP3Vk.js → WidgetsPage-BEyJ62NV.js} +1 -1
  34. package/dashboard/dist/assets/{WrappedPage-6amxWnWT.js → WrappedPage-Ck_dOx_w.js} +1 -1
  35. package/dashboard/dist/assets/{agent-logos-DPIQzYms.js → agent-logos-DpjHiAbZ.js} +1 -1
  36. package/dashboard/dist/assets/{arrow-up-right-CKcE2mif.js → arrow-up-right-C4iYPBtK.js} +1 -1
  37. package/dashboard/dist/assets/check-BkPUxPZV.js +1 -0
  38. package/dashboard/dist/assets/{copy-vi-V2M_I.js → copy-CHJ-38K6.js} +1 -1
  39. package/dashboard/dist/assets/{download-BH0Spg15.js → download-BSeG8u17.js} +1 -1
  40. package/dashboard/dist/assets/{flame-C3jUAnqA.js → flame-DeHwKcE-.js} +1 -1
  41. package/dashboard/dist/assets/{format-tokens-tnfJT09G.js → format-tokens-B-MJYJyp.js} +1 -1
  42. package/dashboard/dist/assets/{info-CGCZ5JvP.js → info-DA_0xr_o.js} +1 -1
  43. package/dashboard/dist/assets/{link-2-CXULWwZY.js → link-2-noUH83Lc.js} +1 -1
  44. package/dashboard/dist/assets/{loader-circle-B324hSa3.js → loader-circle-wL7KZtm0.js} +1 -1
  45. package/dashboard/dist/assets/{main-vGUuyRq-.js → main-BHfJDFcu.js} +20 -20
  46. package/dashboard/dist/assets/{provider-display-CeoAhoCb.js → provider-display-DxIClOGM.js} +1 -1
  47. package/dashboard/dist/assets/{trash-2-L9vxbDeP.js → trash-2-B8TPUmxZ.js} +1 -1
  48. package/dashboard/dist/assets/{use-community-stats-DLE30Lh0.js → use-community-stats-CXD3MRRE.js} +1 -1
  49. package/dashboard/dist/assets/{use-limits-display-prefs-BxN--PEW.js → use-limits-display-prefs-C2pJVYTn.js} +1 -1
  50. package/dashboard/dist/assets/{use-native-settings-Dn5gWQkJ.js → use-native-settings-BeOBLEZa.js} +1 -1
  51. package/dashboard/dist/assets/{use-ordered-list-DYoz2NJ9.js → use-ordered-list-kAtW8pJm.js} +1 -1
  52. package/dashboard/dist/assets/{use-quality-per-dollar-pref-JXorBQsT.js → use-quality-per-dollar-pref-CFZUZM3S.js} +1 -1
  53. package/dashboard/dist/assets/{use-transform-BTTF5vPC.js → use-transform-B9YwpyKi.js} +1 -1
  54. package/dashboard/dist/assets/{use-usage-limits-BUemSWQ9.js → use-usage-limits-DSX5iKPE.js} +1 -1
  55. package/dashboard/dist/assets/{useCurrency-Cxv4RR3U.js → useCurrency-CKg5K4Nj.js} +1 -1
  56. package/dashboard/dist/assets/{useScrollLock-DTPnHKfF.js → useScrollLock-DyyY_Vyt.js} +1 -1
  57. package/dashboard/dist/assets/useTokenFormat-FfTjEH_m.js +1 -0
  58. package/dashboard/dist/assets/{zap-CG8zuY9S.js → zap-B69x9NCT.js} +1 -1
  59. package/dashboard/dist/index.html +1 -1
  60. package/dashboard/dist/share.html +1 -1
  61. package/package.json +1 -1
  62. package/src/commands/init.js +89 -12
  63. package/src/commands/serve.js +65 -4
  64. package/src/commands/status.js +2 -1
  65. package/src/commands/sync.js +170 -7
  66. package/src/commands/uninstall.js +10 -2
  67. package/src/lib/claude-config.js +69 -28
  68. package/src/lib/diagnostics.js +2 -2
  69. package/src/lib/local-api.js +19 -6
  70. package/src/lib/openclaw-session-plugin.js +13 -0
  71. package/src/lib/pricing/seed-snapshot.json +1 -1
  72. package/src/lib/rollout.js +63 -2
  73. package/src/lib/usage-limits.js +9 -5
  74. package/dashboard/dist/assets/DashboardPage-CizTJox2.js +0 -19
  75. package/dashboard/dist/assets/DialogDescription-Bd9-kaCS.js +0 -1
  76. package/dashboard/dist/assets/check-B5V4_ezt.js +0 -1
  77. package/dashboard/dist/assets/useTokenFormat-DMAb2ec3.js +0 -1
@@ -1514,6 +1514,16 @@ async function parseRolloutFile({
1514
1514
  let currentCwd = null;
1515
1515
  let currentDate = null;
1516
1516
  let isForkedRollout = false;
1517
+ // The replay prefix only ever exists at the head of a freshly-forked file. A
1518
+ // resumed scan (startOffset > 0) can never see it — and never re-reads the
1519
+ // session_meta line, so isForkedRollout stays false there anyway — but gate
1520
+ // on the offset explicitly so correctness does not hinge on that distant
1521
+ // invariant. Known accepted limitation: if a sync's read races the fork's
1522
+ // single-flush write and persists a cursor INSIDE the burst, the resumed
1523
+ // scan counts the remaining burst tail (bounded over-count, ms-wide window;
1524
+ // self-heals on any inode-changing rescan and never drops genuine usage).
1525
+ let replayPrefixActive = startOffset === 0;
1526
+ let prevForkedTokenMs = null;
1517
1527
  const rolloutDate = rolloutDateFromPath(filePath);
1518
1528
  let currentProjectRef = projectRef || null;
1519
1529
  let currentProjectKey = projectKey || null;
@@ -1591,8 +1601,50 @@ async function parseRolloutFile({
1591
1601
  totals = totalUsage;
1592
1602
  }
1593
1603
 
1594
- // date matching is conservative; same-day fork replays are still counted.
1595
- if (isForkedReplayToken({ isForkedRollout, rolloutDate, currentDate })) continue;
1604
+ // Forked Codex rollouts replay the parent session's entire token history
1605
+ // into the child file the moment the fork is created. The date guard below
1606
+ // (current_date < rolloutDate) catches cross-day forks, but same-day forks
1607
+ // share the parent's current_date and slip through it. The replay is written
1608
+ // in a single flush, so its rows carry near-identical timestamps (sub-ms to a
1609
+ // few ms apart across observed 0.129–0.137 samples), while the first genuine
1610
+ // live turn lands seconds later (≥11s in every sampled fork). We therefore
1611
+ // skip the *leading* run of densely-spaced token_count rows and latch off
1612
+ // permanently at the first multi-second gap: a lone fast turn (always
1613
+ // preceded by a large gap) is never dropped, and an arbitrarily large replay
1614
+ // is fully skipped regardless of length. The threshold sits far above the
1615
+ // flush spacing and well below genuine turn cadence (≥~4.6s observed). The
1616
+ // first replayed row cannot be identified without lookahead, so it is still
1617
+ // counted (a bounded, <1% residual over-count); dropping real usage is the
1618
+ // worse failure, so we bias against it. `totals` is already advanced above,
1619
+ // keeping the cumulative-delta baseline correct for the live turns we keep.
1620
+ // Scoped to forked codex rollouts. (issue #169 follow-up.)
1621
+ let forkedReplaySkip = false;
1622
+ if (isForkedRollout && source === DEFAULT_SOURCE && replayPrefixActive) {
1623
+ const tokenMs = Date.parse(tokenTimestamp);
1624
+ // Fail open on anything the burst heuristic was not measured against:
1625
+ // an unparseable timestamp or a backwards clock step permanently ends
1626
+ // the prefix, so no later row can be skipped off a stale baseline.
1627
+ // Replay flushes are monotonic in every sampled rollout (2,116 gaps,
1628
+ // min 0ms), so this never fires on genuine replays.
1629
+ if (!Number.isFinite(tokenMs) || (prevForkedTokenMs !== null && tokenMs < prevForkedTokenMs)) {
1630
+ replayPrefixActive = false;
1631
+ } else {
1632
+ if (prevForkedTokenMs !== null && tokenMs - prevForkedTokenMs >= CODEX_FORK_REPLAY_GAP_MS) {
1633
+ replayPrefixActive = false;
1634
+ }
1635
+ forkedReplaySkip =
1636
+ replayPrefixActive &&
1637
+ prevForkedTokenMs !== null &&
1638
+ tokenMs - prevForkedTokenMs < CODEX_FORK_REPLAY_GAP_MS;
1639
+ prevForkedTokenMs = tokenMs;
1640
+ }
1641
+ }
1642
+
1643
+ // date matching is conservative; same-day fork replays are caught by the
1644
+ // burst detector above rather than this cross-day date guard.
1645
+ if (forkedReplaySkip || isForkedReplayToken({ isForkedRollout, rolloutDate, currentDate })) {
1646
+ continue;
1647
+ }
1596
1648
 
1597
1649
  const bucketStart = toUtcHalfHourStart(tokenTimestamp);
1598
1650
  if (!bucketStart) continue;
@@ -2694,6 +2746,15 @@ function normalizeIsoDate(value) {
2694
2746
  return match ? match[1] : null;
2695
2747
  }
2696
2748
 
2749
+ // Max inter-row gap (ms) still treated as part of a forked rollout's replay
2750
+ // burst. Empirically the replay flush spaces rows sub-ms to a few ms apart while
2751
+ // genuine live turns arrive ≥~4.6s apart and the replay→live break is ≥11s, so
2752
+ // any value in the ~0.25–2s band separates them with a wide margin; 500ms biases
2753
+ // toward the low end because merging a real fast turn (under-count) is worse than
2754
+ // leaving a slow replay counted (bounded over-count). See the skip site in
2755
+ // parseRolloutFile. (issue #169 follow-up.)
2756
+ const CODEX_FORK_REPLAY_GAP_MS = 500;
2757
+
2697
2758
  function isForkedReplayToken({ isForkedRollout, rolloutDate, currentDate }) {
2698
2759
  return Boolean(isForkedRollout && rolloutDate && currentDate && currentDate < rolloutDate);
2699
2760
  }
@@ -2823,6 +2823,7 @@ async function fetchUsageLimitsUncached({
2823
2823
  requestFn,
2824
2824
  now = new Date(),
2825
2825
  providerTimeoutMs = DEFAULT_PROVIDER_TIMEOUT_MS,
2826
+ forceRefresh = false,
2826
2827
  } = {}) {
2827
2828
  const nowMs = Date.now();
2828
2829
 
@@ -2870,11 +2871,14 @@ async function fetchUsageLimitsUncached({
2870
2871
  // Skip the upstream Claude call entirely while a 429 cooldown is active — calling again
2871
2872
  // just renews the penalty. The result handling below serves cache or a cooldown message.
2872
2873
  const claudeRetryAtMs = claudeToken ? readClaudeRateLimitRetryAtMs({ home, nowMs }) : null;
2873
- // Also avoid cross-process hammering after a recent successful read. The macOS app can
2874
- // restart its embedded Node server or force-refresh the limits page, both of which clear
2875
- // the in-memory cache; the disk cache keeps those paths from immediately spending
2876
- // another Claude OAuth usage request.
2877
- const freshClaudeCache = claudeToken ? readFreshClaudeLimitsCache({ home, nowMs }) : null;
2874
+ // Also avoid cross-process hammering after a recent successful read: embedded-server
2875
+ // restarts and background polls read the disk cache instead of spending another Claude
2876
+ // OAuth usage request. An explicit user refresh (refresh=1 forceRefresh) punches
2877
+ // through this cache but never through the 429 cooldown above, which is exactly the
2878
+ // hammering the cooldown exists to prevent.
2879
+ const freshClaudeCache = claudeToken && !forceRefresh
2880
+ ? readFreshClaudeLimitsCache({ home, nowMs })
2881
+ : null;
2878
2882
 
2879
2883
  const providerFetch = withFetchTimeout(fetchImpl, providerTimeoutMs);
2880
2884
  const [claudeResult, codexResult, cursor, kimi, gemini, kiro, antigravity, copilot, grok, zcode, opencodeGo] = await Promise.all([