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.
- package/README.de.md +1 -0
- package/README.ja.md +1 -0
- package/README.ko.md +1 -0
- package/README.md +1 -0
- package/README.zh-CN.md +1 -0
- package/dashboard/dist/assets/{AchievementBadge-Ddtn6vg2.js → AchievementBadge-BhAc3c3S.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-AMo1Ib5I.js → AchievementsPage-waGlLQtl.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-CUMjcYst.js → AchievementsSection-T-v65Q1T.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-CA03iBRx.js → BadgeDetailModal-5FcY9olH.js} +1 -1
- package/dashboard/dist/assets/{Card-DscI10IE.js → Card-BBCwyki4.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-5cIXJ0NQ.js → ClawdAnimated-BZwEWFcR.js} +1 -1
- package/dashboard/dist/assets/{CommandPalette-qCpUMoAO.js → CommandPalette-tKp20y4V.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-FT_T1UzH.js → CommunityStatsModal-s9IKLboA.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-GI2RxzAE.js → ConfirmModal-_Mz-U7UW.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-CkA3pfjj.js → DashboardPage-BZ2Z_lqN.js} +1 -1
- package/dashboard/dist/assets/DevicePage-Bx5FaEYs.js +1 -0
- package/dashboard/dist/assets/{DialogClose-cfdVTNyN.js → DialogClose-DaB9Ae7L.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-BFVeSjTD.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-CtptgR8Z.js → DialogTitle-mSFue3N1.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-CPpE5UGk.js → FadeIn-D4PsEvSg.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-vN3e1w0o.js → HeaderGithubStar-8sRKKeCh.js} +1 -1
- package/dashboard/dist/assets/HoverTooltip-8s49xhej.js +1 -0
- package/dashboard/dist/assets/{IpCheckPage-BqJPwFGV.js → IpCheckPage-DwoeRCBI.js} +1 -1
- package/dashboard/dist/assets/LandingPage-DCsOsDK3.js +2 -0
- package/dashboard/dist/assets/{LeaderboardAvatar-BRWPXDZl.js → LeaderboardAvatar-CRO38fgs.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-Dy55Qbr0.js → LeaderboardPage-DlyrceM-.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-Dm5bW2pX.js → LeaderboardProfileModal-DwpalEE6.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-CWmLrKsM.js → LeaderboardProfilePage-BqyfnJa1.js} +1 -1
- package/dashboard/dist/assets/LimitsPage-DS0_zKji.js +4 -0
- package/dashboard/dist/assets/{LocalOnlyNotice-Dso8I7Y4.js → LocalOnlyNotice-BZolN8xC.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-CnghY3oG.js → LoginCard-CyOwV4vy.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-B30O2N_x.js → LoginModal-C6CAcIUw.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-DglsaA23.js → LoginPage-BqG9CVw7.js} +1 -1
- package/dashboard/dist/assets/{PetPage-C7-xrZix.js → PetPage-BtXcBqm3.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-CTbuRUy2.js → PopoverPopup-JsU4A3UY.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-D-sr7Qko.js → ProviderIcon-C8fKhRQt.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-CIbj5Hpp.js → ResetPasswordPage-CKmgCNNp.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-dxer1vVZ.js → SegmentedControl-LnVjL9no.js} +1 -1
- package/dashboard/dist/assets/{Select-BSQkd3VW.js → Select-DKmJyUtS.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-EGKNiN7_.js → SelectItemText-BZFLz6Op.js} +1 -1
- package/dashboard/dist/assets/SessionsPage-D3kT552d.js +2 -0
- package/dashboard/dist/assets/{SettingsPage-3MQDH9bF.js → SettingsPage-D2BLe1ZU.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-DZ8k3xK4.js → SkillsPage-CSmQ6G7z.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-B5q_U7lQ.js → TokenGalaxy-D6D4Uvo5.js} +2 -2
- package/dashboard/dist/assets/{ToolbarRootContext-DIy-0I9L.js → ToolbarRootContext-Bs1pZOOn.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-Bj558t-a.js → TrendMonitor-C7g8DZSW.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-t9EgVnVK.js → WidgetsPage-Cz1mLk9N.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-B-8f6-oI.js → WrappedPage-COBjnmTi.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-DwFlHgQR.js → __vite-browser-external-CeOnjjgl.js} +27 -17
- package/dashboard/dist/assets/{__vite-browser-external-CvhQU1Bj.css → __vite-browser-external-WKilYZnq.css} +1 -1
- package/dashboard/dist/assets/{agent-logos-C6ooyoY_.js → agent-logos-B9ssYD4q.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-CWOhbEwU.js → arrow-up-right-DAWmHSlU.js} +1 -1
- package/dashboard/dist/assets/{check-DqrkOjUC.js → check-DPhmDgw5.js} +1 -1
- package/dashboard/dist/assets/{chevron-right-DlP0Hiu-.js → chevron-right-kOKJJLco.js} +1 -1
- package/dashboard/dist/assets/{copy-Dh_ZalIb.js → copy-gCs3DnsV.js} +1 -1
- package/dashboard/dist/assets/{download-fssIVO33.js → download-CH_YcQ58.js} +1 -1
- package/dashboard/dist/assets/{flame-COf56i7e.js → flame-BEU-cqaV.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-DA4cG58O.js → format-tokens-DJ-Y0jv5.js} +1 -1
- package/dashboard/dist/assets/host-mode-Bc_iTzHk.js +1 -0
- package/dashboard/dist/assets/{icons-DigJtAfR.js → icons-mjdAc-2x.js} +1 -1
- package/dashboard/dist/assets/{index-b_fO7MtI.js → index-BX-ZVFrB.js} +1 -1
- package/dashboard/dist/assets/{index-DIEScXsc.js → index-DfAyiuyv.js} +1 -1
- package/dashboard/dist/assets/{index-BR90lSkB.js → index-DtrsMn3Y.js} +1 -1
- package/dashboard/dist/assets/{info-mwER43h2.js → info-CkFEh5Zu.js} +1 -1
- package/dashboard/dist/assets/{limits-providers-DyEdxJUi.js → limits-providers-DoHPHfeA.js} +1 -1
- package/dashboard/dist/assets/{link-2-Bfgtp0co.js → link-2-Cx8hG5wc.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-B8ASQdW3.js → loader-circle-9rHTQTld.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-88sF_sdM.js → maximize-2-6nkX0TnZ.js} +1 -1
- package/dashboard/dist/assets/{provider-display-CYWy8Wjh.js → provider-display-RBujrp08.js} +1 -1
- package/dashboard/dist/assets/{react-CcvGEOfz.js → react-CgmQzSd0.js} +1 -1
- package/dashboard/dist/assets/{search-C3gFd5W0.js → search-BpgAnH3M.js} +1 -1
- package/dashboard/dist/assets/{skills-api-BkjJfFxG.js → skills-api-CcfxTvl9.js} +1 -1
- package/dashboard/dist/assets/{store-CGE8neJG.js → store-BLjK5TrP.js} +1 -1
- package/dashboard/dist/assets/{terminal-CjoZ2jlv.js → terminal-Dbn_YmPM.js} +1 -1
- package/dashboard/dist/assets/{trash-2-Buwry_AW.js → trash-2-CUUFECPP.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-DZW_XQoS.js → use-community-stats-CQQhhlw9.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-BD0CXAYR.js → use-limits-display-prefs-CSxCVsC3.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-CPVIdR4z.js → use-native-settings-DlDAj7ZJ.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-_yETN2gW.js → use-ordered-list-BTBKsubs.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-DUgAHZln.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-mOQHAb6j.js → use-session-efficiency-pref-C_65WyiQ.js} +1 -1
- package/dashboard/dist/assets/{use-transform-CvmlOKm0.js → use-transform-OnxHQlyh.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-9W0OuOdp.js → use-usage-limits-Mnco2YgQ.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-BZVRsDTW.js → useCurrency-BS_xyhhg.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-Dw-jQnFB.js → useScrollLock-Ct5GyoVg.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-BHw_Jkes.js → useTokenFormat-RvsCI_oE.js} +1 -1
- package/dashboard/dist/assets/{zap-CeYmAnn-.js → zap-D0DQz21w.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/ip-check.html +2 -2
- package/dashboard/dist/leaderboard.html +2 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +1 -1
- package/src/commands/init.js +5 -6
- package/src/commands/status.js +10 -9
- package/src/lib/codex-config.js +45 -4
- package/src/lib/git-outcomes.js +67 -5
- package/src/lib/local-api.js +4 -1
- package/src/lib/pricing/curated-overrides.json +4 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/session-analytics.js +154 -32
- package/dashboard/dist/assets/DevicePage-CdtlanVo.js +0 -1
- package/dashboard/dist/assets/DialogDescription-CKjbQpir.js +0 -1
- package/dashboard/dist/assets/LandingPage-DrjAIdTL.js +0 -2
- package/dashboard/dist/assets/LimitsPage-B538PSle.js +0 -4
- package/dashboard/dist/assets/SessionsPage-CugLdscs.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-BOf4gJaX.js +0 -1
package/src/lib/git-outcomes.js
CHANGED
|
@@ -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
|
-
|
|
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({
|
|
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
|
|
package/src/lib/local-api.js
CHANGED
|
@@ -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
|
-
|
|
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" },
|