tokentracker-cli 0.98.0 → 1.0.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 (109) hide show
  1. package/README.de.md +6 -5
  2. package/README.ja.md +6 -5
  3. package/README.ko.md +6 -5
  4. package/README.md +7 -5
  5. package/README.zh-CN.md +6 -5
  6. package/dashboard/dist/assets/{AchievementBadge-DlZklawB.js → AchievementBadge-Dpf3VBcS.js} +1 -1
  7. package/dashboard/dist/assets/{AchievementsPage-CprbOKnt.js → AchievementsPage-BSGbLI5V.js} +2 -2
  8. package/dashboard/dist/assets/{AchievementsSection-BKLJBRKw.js → AchievementsSection-C10HXoq1.js} +1 -1
  9. package/dashboard/dist/assets/{BadgeDetailModal-CfVXLh_t.js → BadgeDetailModal-DajrEc3e.js} +1 -1
  10. package/dashboard/dist/assets/{BotAnimated-wzqeGbEY.js → BotAnimated-BpbTCJML.js} +1 -1
  11. package/dashboard/dist/assets/{Card-B-gcOAn7.js → Card-CDCj927L.js} +1 -1
  12. package/dashboard/dist/assets/{ClawdAnimated-1c5ezBBN.js → ClawdAnimated-CiTkm04x.js} +2 -2
  13. package/dashboard/dist/assets/{CommandPalette-CD8ggEF6.js → CommandPalette-CYHUUX7M.js} +1 -1
  14. package/dashboard/dist/assets/{CommunityStatsModal-b2fPhi1E.js → CommunityStatsModal-BHZehk5P.js} +1 -1
  15. package/dashboard/dist/assets/{ConfirmModal-ARBkT8AC.js → ConfirmModal-DZlT_xe_.js} +1 -1
  16. package/dashboard/dist/assets/{Controls-WFXDjIYG.js → Controls-Bcijikco.js} +1 -1
  17. package/dashboard/dist/assets/{DashboardPage-XtiZa2JN.js → DashboardPage-yREqx-tx.js} +1 -1
  18. package/dashboard/dist/assets/DevicePage-DzisRlts.js +1 -0
  19. package/dashboard/dist/assets/{DialogClose-D2PE0TAc.js → DialogClose-CQ9hFUNv.js} +1 -1
  20. package/dashboard/dist/assets/DialogDescription-CLXWH3lG.js +1 -0
  21. package/dashboard/dist/assets/{DialogTitle-Bby0zSCw.js → DialogTitle--bItxq3K.js} +1 -1
  22. package/dashboard/dist/assets/{FadeIn-Cx1hl4so.js → FadeIn-v5sAHZBu.js} +1 -1
  23. package/dashboard/dist/assets/{HeaderGithubStar-my4zR6Pp.js → HeaderGithubStar-Bg8R51fN.js} +1 -1
  24. package/dashboard/dist/assets/{HoverTooltip-DOiGAPLE.js → HoverTooltip-DToNFKkJ.js} +1 -1
  25. package/dashboard/dist/assets/{IpCheckPage-4SdHlFuA.js → IpCheckPage-BmFXYXZm.js} +1 -1
  26. package/dashboard/dist/assets/{LandingPage-bSWc9xiS.js → LandingPage-COpRacX0.js} +2 -2
  27. package/dashboard/dist/assets/{LeaderboardAvatar-EN4117E5.js → LeaderboardAvatar-D0l8_L_d.js} +1 -1
  28. package/dashboard/dist/assets/{LeaderboardPage-Bwjf-xZl.js → LeaderboardPage-BJHS44Fq.js} +2 -2
  29. package/dashboard/dist/assets/{LeaderboardProfileModal-B1mgVtfP.js → LeaderboardProfileModal-C1K9rSGn.js} +3 -3
  30. package/dashboard/dist/assets/{LeaderboardProfilePage-DVt5S3lY.js → LeaderboardProfilePage-CvR48D_y.js} +1 -1
  31. package/dashboard/dist/assets/{LimitsPage-BCXjJE1B.js → LimitsPage-BN79yMkb.js} +1 -1
  32. package/dashboard/dist/assets/{LocalOnlyNotice-CzeH4OoJ.js → LocalOnlyNotice-BwTJpkDW.js} +1 -1
  33. package/dashboard/dist/assets/{LoginCard-aiqSQInP.js → LoginCard-BMXAg1rZ.js} +1 -1
  34. package/dashboard/dist/assets/{LoginModal-DdGKIi8h.js → LoginModal-Bf0F_UPK.js} +1 -1
  35. package/dashboard/dist/assets/{LoginPage-D7xGSoMn.js → LoginPage-BylH2waa.js} +1 -1
  36. package/dashboard/dist/assets/{PetPage-Dlb6rNpF.js → PetPage-X4v_J-Uj.js} +1 -1
  37. package/dashboard/dist/assets/{PopoverPopup-Xbruj7Zi.js → PopoverPopup-yEQVU5C_.js} +1 -1
  38. package/dashboard/dist/assets/{ProviderIcon-BPxacBzj.js → ProviderIcon-KRXIghW0.js} +1 -1
  39. package/dashboard/dist/assets/{ResetPasswordPage-9wgPnLRj.js → ResetPasswordPage-DQQF2rsl.js} +1 -1
  40. package/dashboard/dist/assets/{SegmentedControl-CYFcZFNE.js → SegmentedControl-VkVwcoXk.js} +1 -1
  41. package/dashboard/dist/assets/{Select-zpFEMqpk.js → Select-CxW2C8OG.js} +1 -1
  42. package/dashboard/dist/assets/{SelectItemText-Bldm5Y0C.js → SelectItemText-D6vhaI0Q.js} +1 -1
  43. package/dashboard/dist/assets/{ServiceStatusPage-dgtwFazV.js → ServiceStatusPage-CCKqUZhI.js} +1 -1
  44. package/dashboard/dist/assets/{SessionsPage-DHfpv53M.js → SessionsPage-Huqt77Qt.js} +1 -1
  45. package/dashboard/dist/assets/{SettingsPage-DcGToOT7.js → SettingsPage-CpX8GYce.js} +2 -2
  46. package/dashboard/dist/assets/{SkillsPage-DiQDLw2l.js → SkillsPage-S1dtu_Cd.js} +1 -1
  47. package/dashboard/dist/assets/{TokenGalaxy-GDx7ftMU.js → TokenGalaxy-BayM0FOG.js} +161 -161
  48. package/dashboard/dist/assets/{ToolbarRootContext-PoCcP2AV.js → ToolbarRootContext-B1KG8B5q.js} +1 -1
  49. package/dashboard/dist/assets/{TrendMonitor-CIfq_yk8.js → TrendMonitor-DYqz9nLw.js} +1 -1
  50. package/dashboard/dist/assets/{WidgetsPage-Dwy-E3Lf.js → WidgetsPage-WuzVeCBG.js} +1 -1
  51. package/dashboard/dist/assets/{WrappedPage-Ce7NV6Ij.js → WrappedPage-Bpn9GYHz.js} +1 -1
  52. package/dashboard/dist/assets/{__vite-browser-external-D-WLjvZW.js → __vite-browser-external-B1XxVtUQ.js} +3 -3
  53. package/dashboard/dist/assets/{agent-logos-BY8n7b9g.js → agent-logos-DVE956SY.js} +1 -1
  54. package/dashboard/dist/assets/{arrow-up-right-BDiV6Wlk.js → arrow-up-right-B7aZy0wJ.js} +1 -1
  55. package/dashboard/dist/assets/{check-Chyo6ujc.js → check-55YI8PGo.js} +1 -1
  56. package/dashboard/dist/assets/{composite-DgBqMs_Z.js → composite--O2XKSlW.js} +1 -1
  57. package/dashboard/dist/assets/{copy-ENRv4pAy.js → copy-DDVFmqF5.js} +1 -1
  58. package/dashboard/dist/assets/{download-D3y3gd-z.js → download-DmIFrL1S.js} +1 -1
  59. package/dashboard/dist/assets/{external-link-VxSAxWnH.js → external-link-DwG_AAOo.js} +1 -1
  60. package/dashboard/dist/assets/{flame-CPdTWeRo.js → flame-ANwvantJ.js} +1 -1
  61. package/dashboard/dist/assets/{format-tokens-BF2G89Q7.js → format-tokens-BzZ_DfX2.js} +1 -1
  62. package/dashboard/dist/assets/{icons-D6rfdVyV.js → icons-CdyVgzlu.js} +1 -1
  63. package/dashboard/dist/assets/{index-mzMCUT_4.js → index-B-1uJU_R.js} +1 -1
  64. package/dashboard/dist/assets/{index-CgmpxGym.js → index-D0VhCSlC.js} +1 -1
  65. package/dashboard/dist/assets/{index-4bHcp2Ep.js → index-D8g1p_Ee.js} +1 -1
  66. package/dashboard/dist/assets/{info-BMso9PcY.js → info-n2XpcOlm.js} +1 -1
  67. package/dashboard/dist/assets/{link-2-Bg34XOcd.js → link-2-Bq9GUX22.js} +1 -1
  68. package/dashboard/dist/assets/{loader-circle-C4Vul1Yx.js → loader-circle-fkYFxXuJ.js} +1 -1
  69. package/dashboard/dist/assets/{maximize-2-BBqcwFFo.js → maximize-2-C5v08iPD.js} +1 -1
  70. package/dashboard/dist/assets/{modifiers.esm-B2F4Iiey.js → modifiers.esm-AG_bzlWZ.js} +1 -1
  71. package/dashboard/dist/assets/{monitor-smartphone-k1Ja3ngF.js → monitor-smartphone-BEjlJhuw.js} +1 -1
  72. package/dashboard/dist/assets/{plus-BaCIejxc.js → plus-BfdtMxnD.js} +1 -1
  73. package/dashboard/dist/assets/{provider-display-BYxmO9Bu.js → provider-display-BvjuoQty.js} +2 -2
  74. package/dashboard/dist/assets/{react-xGx5RIqz.js → react-f77D-LSn.js} +1 -1
  75. package/dashboard/dist/assets/{search-LZQC7j6a.js → search-Dmczow8i.js} +1 -1
  76. package/dashboard/dist/assets/{skills-api-yLfQkApo.js → skills-api-BuqPQei_.js} +1 -1
  77. package/dashboard/dist/assets/{sortable.esm-B8XRHrmA.js → sortable.esm-CDAVQaFQ.js} +1 -1
  78. package/dashboard/dist/assets/{store-DfUIKvz3.js → store-C2inKcM-.js} +1 -1
  79. package/dashboard/dist/assets/{terminal-CsFh6eZS.js → terminal-C1O422UZ.js} +1 -1
  80. package/dashboard/dist/assets/{trash-CVk10mDa.js → trash-CXOKPZfQ.js} +1 -1
  81. package/dashboard/dist/assets/{use-community-stats-DP7lJyDV.js → use-community-stats-CKfyxLvl.js} +1 -1
  82. package/dashboard/dist/assets/{use-latest-request-guard-B7uLArxV.js → use-latest-request-guard-C3rQR4BN.js} +1 -1
  83. package/dashboard/dist/assets/{use-limits-display-prefs-DG97cWcv.js → use-limits-display-prefs-Ic4GUPRY.js} +1 -1
  84. package/dashboard/dist/assets/{use-ordered-list-ChQc2v-Q.js → use-ordered-list-Cvbz_wcj.js} +1 -1
  85. package/dashboard/dist/assets/use-reduced-motion-DewlF5tk.js +1 -0
  86. package/dashboard/dist/assets/{use-session-efficiency-pref-CtF9JAlF.js → use-session-efficiency-pref-u_qW99gD.js} +1 -1
  87. package/dashboard/dist/assets/{use-transform-DVIBk7FO.js → use-transform-CVGiBxJK.js} +1 -1
  88. package/dashboard/dist/assets/{use-usage-limits-BYQuJ9Ud.js → use-usage-limits-MPu696v0.js} +1 -1
  89. package/dashboard/dist/assets/{useCurrency-CNkzJCP9.js → useCurrency-BOBz18If.js} +1 -1
  90. package/dashboard/dist/assets/{useTokenFormat-CU3MtXtS.js → useTokenFormat-BooCzJJZ.js} +1 -1
  91. package/dashboard/dist/assets/{zap-D5k595AR.js → zap-M_x9dAwo.js} +1 -1
  92. package/dashboard/dist/index.html +13 -11
  93. package/dashboard/dist/ip-check.html +1 -1
  94. package/dashboard/dist/leaderboard.html +1 -1
  95. package/dashboard/dist/llms.txt +3 -2
  96. package/dashboard/dist/share.html +1 -1
  97. package/package.json +2 -2
  98. package/src/commands/init.js +10 -0
  99. package/src/commands/status.js +13 -0
  100. package/src/commands/sync.js +33 -0
  101. package/src/lib/cloud-account.js +12 -1
  102. package/src/lib/heatmap-compact.js +114 -0
  103. package/src/lib/local-api.js +11 -10
  104. package/src/lib/pricing/seed-snapshot.json +1 -1
  105. package/src/lib/rollout.js +148 -20
  106. package/src/lib/skills-manager.js +2 -2
  107. package/dashboard/dist/assets/DevicePage-BCwSpNXa.js +0 -1
  108. package/dashboard/dist/assets/DialogDescription-5LunWukT.js +0 -1
  109. package/dashboard/dist/assets/use-reduced-motion-BoFF1l9U.js +0 -1
@@ -11,6 +11,7 @@
11
11
  // schema exactly, so the popover renders the cloud payload unchanged.
12
12
 
13
13
  const { DEFAULT_BASE_URL, DEFAULT_ANON_KEY } = require("./runtime-config");
14
+ const { expandHeatmapCompact } = require("./heatmap-compact");
14
15
 
15
16
  // usage-* (local CLI) → account-* (cloud) slug map. Only these have a
16
17
  // cross-device cloud equivalent; project-usage / usage-limits / category
@@ -28,6 +29,14 @@ function accountSlugFor(usageSlug) {
28
29
  return USAGE_TO_ACCOUNT_SLUG[usageSlug] || null;
29
30
  }
30
31
 
32
+ // The heatmap renders a 52-week grid out of a handful of active days, so the
33
+ // dense form is mostly zero-filled cells and repeated keys: ~42 KB on the wire
34
+ // for ~12 KB of data, and this proxy accounts for 94% of those reads. Asking
35
+ // for the sparse rows and rebuilding the grid in `expandHeatmapCompact` gives
36
+ // the caller a byte-identical payload. An edge that predates the compact branch
37
+ // ignores the param and answers densely, which that helper passes through.
38
+ const HEATMAP_ACCOUNT_SLUG = "tokentracker-account-heatmap";
39
+
31
40
  // Mirror of dashboard/src/contexts/InsforgeAuthContext.jsx
32
41
  // `accessTokenFromRefreshPayload`: the refresh response may put the token at the
33
42
  // top level or nested under `session`, in camelCase or snake_case.
@@ -340,6 +349,7 @@ async function fetchAccountFunction({
340
349
  if (value != null && value !== "") url.searchParams.set(key, String(value));
341
350
  }
342
351
  }
352
+ if (slug === HEATMAP_ACCOUNT_SLUG) url.searchParams.set("format", "compact");
343
353
  const headers = { Accept: "application/json", Authorization: `Bearer ${accessToken}` };
344
354
  if (anonKey) headers.apikey = anonKey;
345
355
 
@@ -361,7 +371,7 @@ async function fetchAccountFunction({
361
371
  throw err;
362
372
  }
363
373
  const data = await res.json();
364
- return data;
374
+ return slug === HEATMAP_ACCOUNT_SLUG ? expandHeatmapCompact(data) : data;
365
375
  } finally {
366
376
  if (timeoutId) {
367
377
  clearTimeout(timeoutId);
@@ -450,6 +460,7 @@ module.exports = {
450
460
  AccountAuthError,
451
461
  USAGE_TO_ACCOUNT_SLUG,
452
462
  PAYLOAD_TTL_MS,
463
+ HEATMAP_ACCOUNT_SLUG,
453
464
  accountSlugFor,
454
465
  accessTokenFromRefreshPayload,
455
466
  refreshTokenFromRefreshPayload,
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+
3
+ // Expand the `format=compact` activity-heatmap payload back into the schema the
4
+ // dashboard, popover and widget already render.
5
+ //
6
+ // The heatmap is the single largest cloud read we serve: 2.55 GB/day on
7
+ // 2026-09-21, 31% of all egress, and 94% of it proxied through this CLI. The
8
+ // bytes were never the data — `account_heatmap_compact` hands the edge ~12 KB
9
+ // of sparse rows, and the edge blew that up to ~42 KB by materialising a
10
+ // 52-week × 7-day grid of five-key objects, where `billable_total_tokens`
11
+ // simply repeats `total_tokens` and `level` is derived from the window maximum.
12
+ //
13
+ // So the grid gets rebuilt here instead, one hop later, off the same sparse
14
+ // rows. The rendered payload is identical; it just stops crossing the network.
15
+ //
16
+ // Model names are the other half of it. A year of daily rows repeats the same
17
+ // ~160 names ~1900 times, which was 57% of even the sparse payload, so they are
18
+ // sent once in `model_names` and referenced by index from flat
19
+ // [index, tokens, index, tokens, ...] pairs.
20
+ //
21
+ // This mirrors the tail of dashboard/edge-patches/tokentracker-account-heatmap.ts
22
+ // step for step, including the details that look incidental but are not:
23
+ // coercion through Number(), last-row-wins on a repeated day, and `{}` (not
24
+ // null) for a present row whose models map is empty.
25
+
26
+ /** Next YYYY-MM-DD, treating the key as a UTC calendar day (same as the edge). */
27
+ function nextDayKey(dayKey) {
28
+ const d = new Date(`${dayKey}T00:00:00Z`);
29
+ d.setUTCDate(d.getUTCDate() + 1);
30
+ return d.toISOString().slice(0, 10);
31
+ }
32
+
33
+ const DAY_KEY_RE = /^\d{4}-\d{2}-\d{2}$/;
34
+
35
+ // 104 weeks is the edge's own `weeks` clamp; the guard is here so a malformed
36
+ // `to` cannot spin the grid loop forever.
37
+ const MAX_GRID_DAYS = 104 * 7 + 7;
38
+
39
+ /**
40
+ * @param {unknown} payload a `tokentracker-account-heatmap` response.
41
+ * @returns {unknown} the dense payload, or `payload` itself when it is not a
42
+ * well-formed compact response — which is also how a new CLI stays correct
43
+ * against an edge that predates the compact branch.
44
+ */
45
+ function expandHeatmapCompact(payload) {
46
+ if (!payload || typeof payload !== "object") return payload;
47
+ if (payload.format !== "compact") return payload;
48
+ if (!Array.isArray(payload.days)) return payload;
49
+ // Without the dictionary the indices resolve to nothing and every model
50
+ // breakdown would render empty, so refuse the payload instead of silently
51
+ // dropping data.
52
+ if (!Array.isArray(payload.model_names)) return payload;
53
+ if (!DAY_KEY_RE.test(String(payload.from)) || !DAY_KEY_RE.test(String(payload.to))) return payload;
54
+
55
+ const names = payload.model_names;
56
+ const byDay = new Map();
57
+ for (const row of payload.days) {
58
+ if (!Array.isArray(row)) continue;
59
+ const [day, total, pairs] = row;
60
+ if (!DAY_KEY_RE.test(String(day))) continue;
61
+ const mdl = {};
62
+ if (Array.isArray(pairs)) {
63
+ // `i + 1 < length` drops a trailing index with no value rather than
64
+ // recording it as 0.
65
+ for (let i = 0; i + 1 < pairs.length; i += 2) {
66
+ const name = names[pairs[i]];
67
+ if (typeof name === "string") mdl[name] = Number(pairs[i + 1]) || 0;
68
+ }
69
+ }
70
+ // Last row wins, as Map.set did on the edge.
71
+ byDay.set(String(day), { total: Number(total) || 0, models: mdl });
72
+ }
73
+
74
+ const maxValue = Number(payload.max_value) || 0;
75
+ const levelOf = (v) => {
76
+ if (v <= 0) return 0;
77
+ if (maxValue === 0) return 1;
78
+ const r = v / maxValue;
79
+ if (r <= 0.25) return 1;
80
+ if (r <= 0.5) return 2;
81
+ if (r <= 0.75) return 3;
82
+ return 4;
83
+ };
84
+
85
+ const cells = [];
86
+ const to = String(payload.to);
87
+ let cursor = String(payload.from);
88
+ while (cursor <= to && cells.length < MAX_GRID_DAYS) {
89
+ const hit = byDay.get(cursor);
90
+ const total = hit ? hit.total : 0;
91
+ cells.push({
92
+ day: cursor,
93
+ total_tokens: total,
94
+ billable_total_tokens: total,
95
+ level: levelOf(total),
96
+ models: hit ? hit.models : null,
97
+ });
98
+ cursor = nextDayKey(cursor);
99
+ }
100
+
101
+ const weeks = [];
102
+ for (let i = 0; i < cells.length; i += 7) weeks.push(cells.slice(i, i + 7));
103
+
104
+ return {
105
+ from: payload.from,
106
+ to: payload.to,
107
+ week_starts_on: payload.week_starts_on,
108
+ active_days: payload.active_days,
109
+ streak_days: payload.streak_days,
110
+ weeks,
111
+ };
112
+ }
113
+
114
+ module.exports = { expandHeatmapCompact };
@@ -2380,20 +2380,21 @@ function createLocalApiHandler({ queuePath }) {
2380
2380
  ma.totals.cached_input_tokens += row.cached_input_tokens || 0;
2381
2381
  ma.totals.cache_creation_input_tokens += row.cache_creation_input_tokens || 0;
2382
2382
  ma.totals.reasoning_output_tokens += row.reasoning_output_tokens || 0;
2383
- ma.totals.total_cost_usd = Number(ma.totals.total_cost_usd || 0)
2384
- + (Number(row.total_cost_usd) || 0);
2383
+ // Price each row, exactly as aggregateByDay does for the hero (#653).
2384
+ // computeRowCost is not linear for models with a Fast tier or
2385
+ // long-context rates: priority_* / long_context_* mark a subset of the
2386
+ // base columns rather than adding volume, so they survive only while the
2387
+ // row is intact. Summing the base columns first and pricing once silently
2388
+ // dropped every premium — same tokens, fewer dollars than the hero.
2389
+ ma.totals.total_cost_usd = Number(ma.totals.total_cost_usd || 0) + computeRowCost(row);
2385
2390
  }
2386
2391
 
2387
2392
  const sources = Array.from(bySource.values()).map((s) => {
2388
2393
  s.models = Array.from(s.models.values())
2389
- .map((m) => {
2390
- const cost = computeRowCost({
2391
- ...m.totals,
2392
- model: m.model,
2393
- source: s.source,
2394
- });
2395
- return { ...m, totals: { ...m.totals, total_cost_usd: cost.toFixed(6) } };
2396
- })
2394
+ .map((m) => ({
2395
+ ...m,
2396
+ totals: { ...m.totals, total_cost_usd: Number(m.totals.total_cost_usd || 0).toFixed(6) },
2397
+ }))
2397
2398
  .sort((a, b) => b.totals.total_tokens - a.totals.total_tokens);
2398
2399
  const sourceCost = s.models.reduce((sum, m) => sum + Number(m.totals.total_cost_usd), 0);
2399
2400
  s.totals.total_cost_usd = sourceCost.toFixed(6);