tokentracker-cli 0.83.11 → 0.84.1

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 (106) hide show
  1. package/README.de.md +1 -0
  2. package/README.ja.md +1 -0
  3. package/README.ko.md +1 -0
  4. package/README.md +1 -0
  5. package/README.zh-CN.md +1 -0
  6. package/dashboard/dist/assets/{AchievementBadge-Ddtn6vg2.js → AchievementBadge-BhAc3c3S.js} +1 -1
  7. package/dashboard/dist/assets/{AchievementsPage-AMo1Ib5I.js → AchievementsPage-waGlLQtl.js} +2 -2
  8. package/dashboard/dist/assets/{AchievementsSection-CUMjcYst.js → AchievementsSection-T-v65Q1T.js} +1 -1
  9. package/dashboard/dist/assets/{BadgeDetailModal-CA03iBRx.js → BadgeDetailModal-5FcY9olH.js} +1 -1
  10. package/dashboard/dist/assets/{Card-DscI10IE.js → Card-BBCwyki4.js} +1 -1
  11. package/dashboard/dist/assets/{ClawdAnimated-5cIXJ0NQ.js → ClawdAnimated-BZwEWFcR.js} +1 -1
  12. package/dashboard/dist/assets/{CommandPalette-qCpUMoAO.js → CommandPalette-tKp20y4V.js} +1 -1
  13. package/dashboard/dist/assets/{CommunityStatsModal-FT_T1UzH.js → CommunityStatsModal-s9IKLboA.js} +1 -1
  14. package/dashboard/dist/assets/{ConfirmModal-GI2RxzAE.js → ConfirmModal-_Mz-U7UW.js} +1 -1
  15. package/dashboard/dist/assets/{DashboardPage-CkA3pfjj.js → DashboardPage-BZ2Z_lqN.js} +1 -1
  16. package/dashboard/dist/assets/DevicePage-Bx5FaEYs.js +1 -0
  17. package/dashboard/dist/assets/{DialogClose-cfdVTNyN.js → DialogClose-DaB9Ae7L.js} +1 -1
  18. package/dashboard/dist/assets/DialogDescription-BFVeSjTD.js +1 -0
  19. package/dashboard/dist/assets/{DialogTitle-CtptgR8Z.js → DialogTitle-mSFue3N1.js} +1 -1
  20. package/dashboard/dist/assets/{FadeIn-CPpE5UGk.js → FadeIn-D4PsEvSg.js} +1 -1
  21. package/dashboard/dist/assets/{HeaderGithubStar-vN3e1w0o.js → HeaderGithubStar-8sRKKeCh.js} +1 -1
  22. package/dashboard/dist/assets/HoverTooltip-8s49xhej.js +1 -0
  23. package/dashboard/dist/assets/{IpCheckPage-BqJPwFGV.js → IpCheckPage-DwoeRCBI.js} +1 -1
  24. package/dashboard/dist/assets/LandingPage-DCsOsDK3.js +2 -0
  25. package/dashboard/dist/assets/{LeaderboardAvatar-BRWPXDZl.js → LeaderboardAvatar-CRO38fgs.js} +1 -1
  26. package/dashboard/dist/assets/{LeaderboardPage-Dy55Qbr0.js → LeaderboardPage-DlyrceM-.js} +2 -2
  27. package/dashboard/dist/assets/{LeaderboardProfileModal-Dm5bW2pX.js → LeaderboardProfileModal-DwpalEE6.js} +3 -3
  28. package/dashboard/dist/assets/{LeaderboardProfilePage-CWmLrKsM.js → LeaderboardProfilePage-BqyfnJa1.js} +1 -1
  29. package/dashboard/dist/assets/LimitsPage-DS0_zKji.js +4 -0
  30. package/dashboard/dist/assets/{LocalOnlyNotice-Dso8I7Y4.js → LocalOnlyNotice-BZolN8xC.js} +1 -1
  31. package/dashboard/dist/assets/{LoginCard-CnghY3oG.js → LoginCard-CyOwV4vy.js} +1 -1
  32. package/dashboard/dist/assets/{LoginModal-B30O2N_x.js → LoginModal-C6CAcIUw.js} +1 -1
  33. package/dashboard/dist/assets/{LoginPage-DglsaA23.js → LoginPage-BqG9CVw7.js} +1 -1
  34. package/dashboard/dist/assets/{PetPage-C7-xrZix.js → PetPage-BtXcBqm3.js} +1 -1
  35. package/dashboard/dist/assets/{PopoverPopup-CTbuRUy2.js → PopoverPopup-JsU4A3UY.js} +1 -1
  36. package/dashboard/dist/assets/{ProviderIcon-D-sr7Qko.js → ProviderIcon-C8fKhRQt.js} +1 -1
  37. package/dashboard/dist/assets/{ResetPasswordPage-CIbj5Hpp.js → ResetPasswordPage-CKmgCNNp.js} +1 -1
  38. package/dashboard/dist/assets/{SegmentedControl-dxer1vVZ.js → SegmentedControl-LnVjL9no.js} +1 -1
  39. package/dashboard/dist/assets/{Select-BSQkd3VW.js → Select-DKmJyUtS.js} +1 -1
  40. package/dashboard/dist/assets/{SelectItemText-EGKNiN7_.js → SelectItemText-BZFLz6Op.js} +1 -1
  41. package/dashboard/dist/assets/SessionsPage-D3kT552d.js +2 -0
  42. package/dashboard/dist/assets/{SettingsPage-3MQDH9bF.js → SettingsPage-D2BLe1ZU.js} +1 -1
  43. package/dashboard/dist/assets/{SkillsPage-DZ8k3xK4.js → SkillsPage-CSmQ6G7z.js} +1 -1
  44. package/dashboard/dist/assets/{TokenGalaxy-B5q_U7lQ.js → TokenGalaxy-D6D4Uvo5.js} +2 -2
  45. package/dashboard/dist/assets/{ToolbarRootContext-DIy-0I9L.js → ToolbarRootContext-Bs1pZOOn.js} +1 -1
  46. package/dashboard/dist/assets/{TrendMonitor-Bj558t-a.js → TrendMonitor-C7g8DZSW.js} +1 -1
  47. package/dashboard/dist/assets/{WidgetsPage-t9EgVnVK.js → WidgetsPage-Cz1mLk9N.js} +1 -1
  48. package/dashboard/dist/assets/{WrappedPage-B-8f6-oI.js → WrappedPage-COBjnmTi.js} +1 -1
  49. package/dashboard/dist/assets/{__vite-browser-external-DwFlHgQR.js → __vite-browser-external-CeOnjjgl.js} +27 -17
  50. package/dashboard/dist/assets/{__vite-browser-external-CvhQU1Bj.css → __vite-browser-external-WKilYZnq.css} +1 -1
  51. package/dashboard/dist/assets/{agent-logos-C6ooyoY_.js → agent-logos-B9ssYD4q.js} +1 -1
  52. package/dashboard/dist/assets/{arrow-up-right-CWOhbEwU.js → arrow-up-right-DAWmHSlU.js} +1 -1
  53. package/dashboard/dist/assets/{check-DqrkOjUC.js → check-DPhmDgw5.js} +1 -1
  54. package/dashboard/dist/assets/{chevron-right-DlP0Hiu-.js → chevron-right-kOKJJLco.js} +1 -1
  55. package/dashboard/dist/assets/{copy-Dh_ZalIb.js → copy-gCs3DnsV.js} +1 -1
  56. package/dashboard/dist/assets/{download-fssIVO33.js → download-CH_YcQ58.js} +1 -1
  57. package/dashboard/dist/assets/{flame-COf56i7e.js → flame-BEU-cqaV.js} +1 -1
  58. package/dashboard/dist/assets/{format-tokens-DA4cG58O.js → format-tokens-DJ-Y0jv5.js} +1 -1
  59. package/dashboard/dist/assets/host-mode-Bc_iTzHk.js +1 -0
  60. package/dashboard/dist/assets/{icons-DigJtAfR.js → icons-mjdAc-2x.js} +1 -1
  61. package/dashboard/dist/assets/{index-b_fO7MtI.js → index-BX-ZVFrB.js} +1 -1
  62. package/dashboard/dist/assets/{index-DIEScXsc.js → index-DfAyiuyv.js} +1 -1
  63. package/dashboard/dist/assets/{index-BR90lSkB.js → index-DtrsMn3Y.js} +1 -1
  64. package/dashboard/dist/assets/{info-mwER43h2.js → info-CkFEh5Zu.js} +1 -1
  65. package/dashboard/dist/assets/{limits-providers-DyEdxJUi.js → limits-providers-DoHPHfeA.js} +1 -1
  66. package/dashboard/dist/assets/{link-2-Bfgtp0co.js → link-2-Cx8hG5wc.js} +1 -1
  67. package/dashboard/dist/assets/{loader-circle-B8ASQdW3.js → loader-circle-9rHTQTld.js} +1 -1
  68. package/dashboard/dist/assets/{maximize-2-88sF_sdM.js → maximize-2-6nkX0TnZ.js} +1 -1
  69. package/dashboard/dist/assets/{provider-display-CYWy8Wjh.js → provider-display-RBujrp08.js} +1 -1
  70. package/dashboard/dist/assets/{react-CcvGEOfz.js → react-CgmQzSd0.js} +1 -1
  71. package/dashboard/dist/assets/{search-C3gFd5W0.js → search-BpgAnH3M.js} +1 -1
  72. package/dashboard/dist/assets/{skills-api-BkjJfFxG.js → skills-api-CcfxTvl9.js} +1 -1
  73. package/dashboard/dist/assets/{store-CGE8neJG.js → store-BLjK5TrP.js} +1 -1
  74. package/dashboard/dist/assets/{terminal-CjoZ2jlv.js → terminal-Dbn_YmPM.js} +1 -1
  75. package/dashboard/dist/assets/{trash-2-Buwry_AW.js → trash-2-CUUFECPP.js} +1 -1
  76. package/dashboard/dist/assets/{use-community-stats-DZW_XQoS.js → use-community-stats-CQQhhlw9.js} +1 -1
  77. package/dashboard/dist/assets/{use-limits-display-prefs-BD0CXAYR.js → use-limits-display-prefs-CSxCVsC3.js} +1 -1
  78. package/dashboard/dist/assets/{use-native-settings-CPVIdR4z.js → use-native-settings-DlDAj7ZJ.js} +1 -1
  79. package/dashboard/dist/assets/{use-ordered-list-_yETN2gW.js → use-ordered-list-BTBKsubs.js} +1 -1
  80. package/dashboard/dist/assets/use-reduced-motion-DUgAHZln.js +1 -0
  81. package/dashboard/dist/assets/{use-session-efficiency-pref-mOQHAb6j.js → use-session-efficiency-pref-C_65WyiQ.js} +1 -1
  82. package/dashboard/dist/assets/{use-transform-CvmlOKm0.js → use-transform-OnxHQlyh.js} +1 -1
  83. package/dashboard/dist/assets/{use-usage-limits-9W0OuOdp.js → use-usage-limits-Mnco2YgQ.js} +1 -1
  84. package/dashboard/dist/assets/{useCurrency-BZVRsDTW.js → useCurrency-BS_xyhhg.js} +1 -1
  85. package/dashboard/dist/assets/{useScrollLock-Dw-jQnFB.js → useScrollLock-Ct5GyoVg.js} +1 -1
  86. package/dashboard/dist/assets/{useTokenFormat-BHw_Jkes.js → useTokenFormat-RvsCI_oE.js} +1 -1
  87. package/dashboard/dist/assets/{zap-CeYmAnn-.js → zap-D0DQz21w.js} +1 -1
  88. package/dashboard/dist/index.html +2 -2
  89. package/dashboard/dist/ip-check.html +2 -2
  90. package/dashboard/dist/leaderboard.html +2 -2
  91. package/dashboard/dist/share.html +2 -2
  92. package/package.json +1 -1
  93. package/src/commands/init.js +5 -6
  94. package/src/commands/status.js +10 -9
  95. package/src/lib/codex-config.js +45 -4
  96. package/src/lib/git-outcomes.js +67 -5
  97. package/src/lib/local-api.js +4 -1
  98. package/src/lib/pricing/curated-overrides.json +4 -0
  99. package/src/lib/pricing/seed-snapshot.json +1 -1
  100. package/src/lib/session-analytics.js +154 -32
  101. package/dashboard/dist/assets/DevicePage-CdtlanVo.js +0 -1
  102. package/dashboard/dist/assets/DialogDescription-CKjbQpir.js +0 -1
  103. package/dashboard/dist/assets/LandingPage-DrjAIdTL.js +0 -2
  104. package/dashboard/dist/assets/LimitsPage-B538PSle.js +0 -4
  105. package/dashboard/dist/assets/SessionsPage-CugLdscs.js +0 -1
  106. package/dashboard/dist/assets/use-reduced-motion-BOf4gJaX.js +0 -1
@@ -60,10 +60,63 @@ async function writeAtomic(filePath, content) {
60
60
  await fsp.rename(temp, filePath);
61
61
  }
62
62
 
63
+ // Attribution walks into the working directory of every recent session and
64
+ // runs git there. On macOS each distinct protected location that gets touched
65
+ // (~/Documents, ~/Downloads, another app's container) raises its own TCC
66
+ // consent prompt, so probing must happen only when there is new work to
67
+ // attribute — never on every dashboard poll.
68
+ const GIT_PROBE_TTL_MS = 30 * 60 * 1000;
69
+
70
+ // A data-only fingerprint of the sessions we would attribute. Computing it
71
+ // touches no filesystem and spawns no git, so an unchanged fingerprint lets us
72
+ // serve the cached sidecar without entering a single project directory.
73
+ function sessionsFingerprint(sessions, cutoff, maxAgeDays) {
74
+ const hash = crypto.createHash("sha256");
75
+ hash.update(`v1\0${maxAgeDays}\n`);
76
+ const rows = [];
77
+ for (const session of sessions || []) {
78
+ if (!session?.project_ref || !session.started_at || !session.ended_at) continue;
79
+ if (Date.parse(session.ended_at) < cutoff) continue;
80
+ rows.push(`${session.project_ref}\0${session.session_hash}\0${session.started_at}\0${session.ended_at}`);
81
+ }
82
+ for (const row of rows.sort()) hash.update(`${row}\n`);
83
+ return hash.digest("hex");
84
+ }
85
+
86
+ function gitAttributionDisabled() {
87
+ return ["1", "true"].includes(String(process.env.TOKENTRACKER_DISABLE_GIT_ATTRIBUTION || "").toLowerCase());
88
+ }
89
+
63
90
  async function buildGitOutcomesInternal(sessions, { home = os.homedir(), force = false, maxAgeDays = 90 } = {}) {
91
+ const outputPath = resolveAutoOutcomesPath(home);
92
+ const metaPath = `${outputPath}.meta.json`;
93
+ const cutoff = Date.now() - Math.max(1, Number(maxAgeDays) || 90) * 86400_000;
94
+
95
+ // Escape hatch for anyone who would rather keep TokenTracker out of their
96
+ // project directories entirely. Serves whatever was already attributed.
97
+ if (gitAttributionDisabled()) return readJsonl(outputPath);
98
+
99
+ // Cheap gate, before any filesystem access: same sessions as last time and
100
+ // the last probe is recent, so there is nothing new to attribute.
101
+ const sessionSignature = sessionsFingerprint(sessions, cutoff, maxAgeDays);
102
+ if (!force) {
103
+ try {
104
+ const meta = JSON.parse(fs.readFileSync(metaPath, "utf8"));
105
+ const age = Date.now() - Date.parse(meta.generated_at || "");
106
+ if (
107
+ meta.version === 2
108
+ && meta.session_signature === sessionSignature
109
+ && Number.isFinite(age)
110
+ && age >= 0
111
+ && age < GIT_PROBE_TTL_MS
112
+ ) {
113
+ return readJsonl(outputPath);
114
+ }
115
+ } catch { /* first run, or a v1 sidecar written before this gate existed */ }
116
+ }
117
+
64
118
  const groups = new Map();
65
119
  const rootCache = new Map();
66
- const cutoff = Date.now() - Math.max(1, Number(maxAgeDays) || 90) * 86400_000;
67
120
  for (const session of sessions || []) {
68
121
  if (!session?.project_ref || !session.started_at || !session.ended_at) continue;
69
122
  if (Date.parse(session.ended_at) < cutoff) continue;
@@ -77,8 +130,6 @@ async function buildGitOutcomesInternal(sessions, { home = os.homedir(), force =
77
130
  groups.get(root).push(session);
78
131
  }
79
132
 
80
- const outputPath = resolveAutoOutcomesPath(home);
81
- const metaPath = `${outputPath}.meta.json`;
82
133
  const signatureHash = crypto.createHash("sha256");
83
134
  for (const [root, repoSessions] of [...groups.entries()].sort(([a], [b]) => a.localeCompare(b))) {
84
135
  signatureHash.update(`${root}\0${await runGit(root, ["for-each-ref", "--format=%(objectname)"])}\n`);
@@ -88,7 +139,12 @@ async function buildGitOutcomesInternal(sessions, { home = os.homedir(), force =
88
139
  if (!force) {
89
140
  try {
90
141
  const meta = JSON.parse(fs.readFileSync(metaPath, "utf8"));
91
- if (meta.version === 1 && meta.signature === signature) return readJsonl(outputPath);
142
+ // Git state is unchanged too refresh the timestamp so the cheap gate
143
+ // above can short-circuit the next poll instead of re-probing.
144
+ if (meta.signature === signature) {
145
+ await writeAtomic(metaPath, `${JSON.stringify({ ...meta, version: 2, session_signature: sessionSignature, generated_at: new Date().toISOString() })}\n`);
146
+ return readJsonl(outputPath);
147
+ }
92
148
  } catch { /* first run */ }
93
149
  }
94
150
 
@@ -123,7 +179,13 @@ async function buildGitOutcomesInternal(sessions, { home = os.homedir(), force =
123
179
  }
124
180
  outcomes.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
125
181
  await writeAtomic(outputPath, outcomes.map((row) => JSON.stringify(row)).join("\n") + (outcomes.length ? "\n" : ""));
126
- await writeAtomic(metaPath, `${JSON.stringify({ version: 1, signature, generated_at: new Date().toISOString(), max_age_days: maxAgeDays })}\n`);
182
+ await writeAtomic(metaPath, `${JSON.stringify({
183
+ version: 2,
184
+ signature,
185
+ session_signature: sessionSignature,
186
+ generated_at: new Date().toISOString(),
187
+ max_age_days: maxAgeDays,
188
+ })}\n`);
127
189
  return outcomes;
128
190
  }
129
191
 
@@ -2169,7 +2169,10 @@ function createLocalApiHandler({ queuePath }) {
2169
2169
  const result = listSessionsForBrowser(sessions, { from, to, limit });
2170
2170
  json(res, { from, to, ...result });
2171
2171
  } catch (error) {
2172
- json(res, { available: false, error: error?.message || "Session browser failed" }, 500);
2172
+ // Node fs errors embed the absolute path ("EACCES ... open '/Users/…'").
2173
+ // Keep that out of the HTTP body and log it locally instead.
2174
+ console.warn("[local-api] session browser failed:", error?.message || error);
2175
+ json(res, { available: false, error: "Session browser failed" }, 500);
2173
2176
  }
2174
2177
  return true;
2175
2178
  }
@@ -6,6 +6,8 @@
6
6
  },
7
7
  "exact": {
8
8
  "claude-fable-5": { "input": 10, "output": 50, "cache_read": 1, "cache_write": 12.5, "note": "Fable 5 — Anthropic's top tier above Opus. Not yet in LiteLLM. $10/$50 per MTok; cache_read 0.1x, cache_write 1.25x. Remove once LiteLLM carries it." },
9
+ "claude-opus-5": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25, "note": "Opus 5 (public 2026-07). Standard Opus tier, unchanged from 4.6/4.7/4.8: $5/$25 per MTok, cache_read 0.1x, cache_write 1.25x. Not yet in LiteLLM. Fast mode is billed at $10/$50 — see claude-opus-5-fast. Remove once LiteLLM carries it." },
10
+ "claude-opus-5-fast":{ "input": 10, "output": 50, "cache_read": 1, "cache_write": 12.5, "note": "Opus 5 fast mode (speed=\"fast\", beta fast-mode-2026-02-01) is priced at $10/$50 per MTok — 2x standard Opus. Kept separate so a -fast model id is not silently priced at the standard tier. See claude-opus-5." },
9
11
  "claude-opus-4-8": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25, "note": "Opus 4.8 not yet in LiteLLM. Pin to the standard Opus tier (same as 4.6/4.7); remove once LiteLLM carries it." },
10
12
  "claude-sonnet-5": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75, "note": "Sonnet 5. LiteLLM added this model on ~2026-06-30 with matching rates, but pin explicitly so pricing is correct immediately rather than depending on the 24h local pricing-cache refresh cycle." },
11
13
  "claude-haiku-4-5": { "input": 1, "output": 5, "cache_read": 0.1, "cache_write": 1.25, "note": "Canonical undated alias emitted by Cursor. Pin to the same rates as claude-haiku-4-5-20251001 so local cost does not depend on which dated LiteLLM aliases are present in the current cache." },
@@ -79,6 +81,8 @@
79
81
  { "match": "kiro", "ref": "kiro-cli-agent" },
80
82
  { "match": "hy3", "ref": "hy3-preview-agent" },
81
83
  { "match": "composer", "ref": "composer-1" },
84
+ { "match": "claude-opus-5-fast", "ref": "claude-opus-5-fast" },
85
+ { "match": "claude-opus-5", "ref": "claude-opus-5" },
82
86
  { "match": "claude-opus-4-8", "ref": "claude-opus-4-8" },
83
87
  { "match": "minimax-m3", "ref": "minimax-m3" },
84
88
  { "match": "minimax-m2.7-highspeed", "ref": "MiniMax-M2.7-highspeed" },