tokentracker-cli 0.5.99 → 0.5.101
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.md +6 -5
- package/README.zh-CN.md +7 -5
- package/dashboard/dist/assets/{main-BhjD_pKB.js → main-Cqn4mbzU.js} +169 -168
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +2 -2
- package/src/commands/init.js +22 -0
- package/src/commands/status.js +73 -15
- package/src/commands/sync.js +52 -0
- package/src/lib/pricing/curated-overrides.json +4 -1
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +595 -0
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
]
|
|
136
136
|
}
|
|
137
137
|
</script>
|
|
138
|
-
<script type="module" crossorigin src="/assets/main-
|
|
138
|
+
<script type="module" crossorigin src="/assets/main-Cqn4mbzU.js"></script>
|
|
139
139
|
<link rel="stylesheet" crossorigin href="/assets/main-HLMqEvtH.css">
|
|
140
140
|
</head>
|
|
141
141
|
<body>
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"description": "Shareable Token Tracker dashboard snapshot."
|
|
52
52
|
}
|
|
53
53
|
</script>
|
|
54
|
-
<script type="module" crossorigin src="/assets/main-
|
|
54
|
+
<script type="module" crossorigin src="/assets/main-Cqn4mbzU.js"></script>
|
|
55
55
|
<link rel="stylesheet" crossorigin href="/assets/main-HLMqEvtH.css">
|
|
56
56
|
</head>
|
|
57
57
|
<body>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokentracker-cli",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "Token usage tracker for AI agent CLIs (Claude Code, Codex, Cursor,
|
|
3
|
+
"version": "0.5.101",
|
|
4
|
+
"description": "Token usage tracker for AI agent CLIs (Claude Code, Codex, Cursor, Gemini, Kiro, OpenCode, OpenClaw, Every Code, Hermes, GitHub Copilot, Kimi Code, CodeBuddy, oh-my-pi, Craft Agents)",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"tokentracker-cli": "bin/tracker.js",
|
package/src/commands/init.js
CHANGED
|
@@ -425,6 +425,28 @@ async function applyIntegrationSetup({ home, trackerDir, notifyPath, notifyOrigi
|
|
|
425
425
|
}
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
+
// oh-my-pi: passive reader — no hook installation needed.
|
|
429
|
+
// TokenTracker reads ~/.omp/agent/sessions/**/*.jsonl directly.
|
|
430
|
+
{
|
|
431
|
+
const ompHome = process.env.OMP_HOME ||
|
|
432
|
+
(process.env.PI_CONFIG_DIR ? path.join(home, process.env.PI_CONFIG_DIR) : path.join(home, ".omp"));
|
|
433
|
+
const ompAgentDir = process.env.PI_CODING_AGENT_DIR || path.join(ompHome, "agent");
|
|
434
|
+
const ompSessions = path.join(ompAgentDir, "sessions");
|
|
435
|
+
if (fssync.existsSync(ompSessions)) {
|
|
436
|
+
summary.push({ label: "oh-my-pi", status: "detected", detail: "Passive reader (no hook needed)" });
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Craft Agents: passive reader — no hook installation needed.
|
|
441
|
+
// TokenTracker reads ~/.craft-agent/workspaces/<id>/sessions/**/session.jsonl
|
|
442
|
+
// (and any user-relocated workspace listed in ~/.craft-agent/config.json).
|
|
443
|
+
{
|
|
444
|
+
const craftConfigDir = process.env.CRAFT_CONFIG_DIR || path.join(home, ".craft-agent");
|
|
445
|
+
if (fssync.existsSync(craftConfigDir)) {
|
|
446
|
+
summary.push({ label: "Craft Agents", status: "detected", detail: "Passive reader (no hook needed)" });
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
428
450
|
// CodeBuddy: Claude-Code fork. Install the SessionEnd hook so finished
|
|
429
451
|
// sessions trigger notify.cjs → tracker sync; passive scan still runs as a
|
|
430
452
|
// safety net for sessions that don't fire SessionEnd cleanly.
|
package/src/commands/status.js
CHANGED
|
@@ -16,19 +16,33 @@ const {
|
|
|
16
16
|
isGeminiHookConfigured,
|
|
17
17
|
buildGeminiHookCommand,
|
|
18
18
|
} = require("../lib/gemini-config");
|
|
19
|
-
const {
|
|
19
|
+
const {
|
|
20
|
+
resolveOpencodeConfigDir,
|
|
21
|
+
isOpencodePluginInstalled,
|
|
22
|
+
} = require("../lib/opencode-config");
|
|
20
23
|
const { collectLocalSubscriptions } = require("../lib/subscriptions");
|
|
21
|
-
const {
|
|
22
|
-
|
|
24
|
+
const {
|
|
25
|
+
describeCopilotOtelStatus,
|
|
26
|
+
readCopilotOauthToken,
|
|
27
|
+
} = require("../lib/usage-limits");
|
|
28
|
+
const {
|
|
29
|
+
normalizeState: normalizeUploadState,
|
|
30
|
+
} = require("../lib/upload-throttle");
|
|
23
31
|
const { collectTrackerDiagnostics } = require("../lib/diagnostics");
|
|
24
32
|
const { probeOpenclawHookState } = require("../lib/openclaw-hook");
|
|
25
|
-
const {
|
|
33
|
+
const {
|
|
34
|
+
probeOpenclawSessionPluginState,
|
|
35
|
+
} = require("../lib/openclaw-session-plugin");
|
|
26
36
|
const { resolveTrackerPaths } = require("../lib/tracker-paths");
|
|
27
37
|
const {
|
|
28
38
|
resolveKimiWireFiles,
|
|
29
39
|
resolveKiroCliDbPath,
|
|
30
40
|
resolveCodebuddyHome,
|
|
31
41
|
resolveCodebuddyProjectFiles,
|
|
42
|
+
resolveOmpSessionFiles,
|
|
43
|
+
resolveOmpAgentDir,
|
|
44
|
+
resolveCraftSessionFiles,
|
|
45
|
+
resolveCraftConfigDir,
|
|
32
46
|
} = require("../lib/rollout");
|
|
33
47
|
|
|
34
48
|
async function cmdStatus(argv = []) {
|
|
@@ -60,8 +74,13 @@ async function cmdStatus(argv = []) {
|
|
|
60
74
|
"settings.json",
|
|
61
75
|
);
|
|
62
76
|
const geminiConfigDir = resolveGeminiConfigDir({ home, env: process.env });
|
|
63
|
-
const geminiSettingsPath = resolveGeminiSettingsPath({
|
|
64
|
-
|
|
77
|
+
const geminiSettingsPath = resolveGeminiSettingsPath({
|
|
78
|
+
configDir: geminiConfigDir,
|
|
79
|
+
});
|
|
80
|
+
const opencodeConfigDir = resolveOpencodeConfigDir({
|
|
81
|
+
home,
|
|
82
|
+
env: process.env,
|
|
83
|
+
});
|
|
65
84
|
const notifyPath = path.join(binDir, "notify.cjs");
|
|
66
85
|
const claudeHookCommand = buildClaudeHookCommand(notifyPath);
|
|
67
86
|
const codebuddyHookCommand = buildHookCommand(notifyPath, "codebuddy");
|
|
@@ -70,20 +89,26 @@ async function cmdStatus(argv = []) {
|
|
|
70
89
|
const config = await readJson(configPath);
|
|
71
90
|
const cursors = await readJson(cursorsPath);
|
|
72
91
|
const queueState = (await readJson(queueStatePath)) || { offset: 0 };
|
|
73
|
-
const uploadThrottle = normalizeUploadState(
|
|
92
|
+
const uploadThrottle = normalizeUploadState(
|
|
93
|
+
await readJson(uploadThrottlePath),
|
|
94
|
+
);
|
|
74
95
|
const autoRetry = await readJson(autoRetryPath);
|
|
75
96
|
|
|
76
97
|
const queueSize = await safeStatSize(queuePath);
|
|
77
98
|
const pendingBytes = Math.max(0, queueSize - (queueState.offset || 0));
|
|
78
99
|
|
|
79
100
|
const lastNotify = (await safeReadText(notifySignalPath))?.trim() || null;
|
|
80
|
-
const lastOpenclawSync =
|
|
81
|
-
|
|
101
|
+
const lastOpenclawSync =
|
|
102
|
+
(await safeReadText(openclawSignalPath))?.trim() || null;
|
|
103
|
+
const lastNotifySpawn = parseEpochMsToIso(
|
|
104
|
+
(await safeReadText(throttlePath))?.trim() || null,
|
|
105
|
+
);
|
|
82
106
|
|
|
83
107
|
const codexNotify = await readCodexNotify(codexConfigPath);
|
|
84
108
|
const notifyConfigured = Array.isArray(codexNotify) && codexNotify.length > 0;
|
|
85
109
|
const everyCodeNotify = await readEveryCodeNotify(codeConfigPath);
|
|
86
|
-
const everyCodeConfigured =
|
|
110
|
+
const everyCodeConfigured =
|
|
111
|
+
Array.isArray(everyCodeNotify) && everyCodeNotify.length > 0;
|
|
87
112
|
const claudeHookConfigured = await isClaudeHookConfigured({
|
|
88
113
|
settingsPath: claudeSettingsPath,
|
|
89
114
|
hookCommand: claudeHookCommand,
|
|
@@ -104,7 +129,11 @@ async function cmdStatus(argv = []) {
|
|
|
104
129
|
trackerDir,
|
|
105
130
|
env: process.env,
|
|
106
131
|
});
|
|
107
|
-
const openclawHookState = await probeOpenclawHookState({
|
|
132
|
+
const openclawHookState = await probeOpenclawHookState({
|
|
133
|
+
home,
|
|
134
|
+
trackerDir,
|
|
135
|
+
env: process.env,
|
|
136
|
+
});
|
|
108
137
|
|
|
109
138
|
const lastUpload = uploadThrottle.lastSuccessMs
|
|
110
139
|
? parseEpochMsToIso(uploadThrottle.lastSuccessMs)
|
|
@@ -151,9 +180,22 @@ async function cmdStatus(argv = []) {
|
|
|
151
180
|
? resolveCodebuddyProjectFiles(process.env)
|
|
152
181
|
: [];
|
|
153
182
|
|
|
183
|
+
// oh-my-pi — passive scan only (no hooks).
|
|
184
|
+
const ompAgentDir = resolveOmpAgentDir(process.env);
|
|
185
|
+
const ompInstalled = fssync.existsSync(path.join(ompAgentDir, "sessions"));
|
|
186
|
+
const ompFiles = ompInstalled ? resolveOmpSessionFiles(process.env) : [];
|
|
187
|
+
|
|
188
|
+
// Craft Agents — passive scan only (no hooks).
|
|
189
|
+
const craftConfigDir = resolveCraftConfigDir(process.env);
|
|
190
|
+
const craftInstalled = fssync.existsSync(craftConfigDir);
|
|
191
|
+
const craftFiles = craftInstalled ? resolveCraftSessionFiles(process.env) : [];
|
|
192
|
+
|
|
154
193
|
const copilotToken = readCopilotOauthToken({ home });
|
|
155
194
|
const copilotOtel = describeCopilotOtelStatus({ home, env: process.env });
|
|
156
|
-
const copilotLines = formatCopilotLines({
|
|
195
|
+
const copilotLines = formatCopilotLines({
|
|
196
|
+
token: copilotToken,
|
|
197
|
+
otel: copilotOtel,
|
|
198
|
+
});
|
|
157
199
|
|
|
158
200
|
process.stdout.write(
|
|
159
201
|
[
|
|
@@ -186,6 +228,12 @@ async function cmdStatus(argv = []) {
|
|
|
186
228
|
codebuddyInstalled
|
|
187
229
|
? `- CodeBuddy hooks: ${codebuddyHookConfigured ? "set" : "unset"} (${codebuddyFiles.length} session jsonl file${codebuddyFiles.length !== 1 ? "s" : ""} found)`
|
|
188
230
|
: null,
|
|
231
|
+
ompInstalled
|
|
232
|
+
? `- oh-my-pi: passive reader (${ompFiles.length} session jsonl file${ompFiles.length !== 1 ? "s" : ""} found)`
|
|
233
|
+
: null,
|
|
234
|
+
craftInstalled
|
|
235
|
+
? `- Craft Agents: passive reader (${craftFiles.length} session jsonl file${craftFiles.length !== 1 ? "s" : ""} found)`
|
|
236
|
+
: null,
|
|
189
237
|
...copilotLines,
|
|
190
238
|
...subscriptionLines,
|
|
191
239
|
"",
|
|
@@ -197,7 +245,9 @@ async function cmdStatus(argv = []) {
|
|
|
197
245
|
|
|
198
246
|
function formatCopilotLines({ token, otel }) {
|
|
199
247
|
if (!token && !otel.otel_has_files) return [];
|
|
200
|
-
const limitsState = token
|
|
248
|
+
const limitsState = token
|
|
249
|
+
? "set (via GitHub OAuth)"
|
|
250
|
+
: "unset (no Copilot OAuth token found)";
|
|
201
251
|
const usageState = otel.otel_has_files
|
|
202
252
|
? `set (${otel.otel_path || otel.otel_default_dir})`
|
|
203
253
|
: otel.otel_enabled
|
|
@@ -235,7 +285,11 @@ function formatSubscriptionLine(entry = {}) {
|
|
|
235
285
|
|
|
236
286
|
if (!planType) return null;
|
|
237
287
|
|
|
238
|
-
if (
|
|
288
|
+
if (
|
|
289
|
+
tool === "claude" &&
|
|
290
|
+
provider === "anthropic" &&
|
|
291
|
+
product === "subscription"
|
|
292
|
+
) {
|
|
239
293
|
const suffix = rateLimitTier ? ` (rate limit tier: ${rateLimitTier})` : "";
|
|
240
294
|
return `- ${toolLabel} subscription: ${planType}${suffix}`;
|
|
241
295
|
}
|
|
@@ -249,7 +303,11 @@ function formatSubscriptionLine(entry = {}) {
|
|
|
249
303
|
}
|
|
250
304
|
|
|
251
305
|
function parseArgs(argv) {
|
|
252
|
-
const out = {
|
|
306
|
+
const out = {
|
|
307
|
+
diagnostics: false,
|
|
308
|
+
probeKeychain: false,
|
|
309
|
+
probeKeychainDetails: false,
|
|
310
|
+
};
|
|
253
311
|
|
|
254
312
|
for (let i = 0; i < argv.length; i++) {
|
|
255
313
|
const a = argv[i];
|
package/src/commands/sync.js
CHANGED
|
@@ -27,6 +27,10 @@ const {
|
|
|
27
27
|
parseCopilotIncremental,
|
|
28
28
|
resolveKimiWireFiles,
|
|
29
29
|
parseKimiIncremental,
|
|
30
|
+
resolveOmpSessionFiles,
|
|
31
|
+
parseOmpIncremental,
|
|
32
|
+
resolveCraftSessionFiles,
|
|
33
|
+
parseCraftIncremental,
|
|
30
34
|
resolveCodebuddyProjectFiles,
|
|
31
35
|
parseCodebuddyIncremental,
|
|
32
36
|
resolveKiroCliSessionFiles,
|
|
@@ -447,6 +451,50 @@ async function cmdSync(argv) {
|
|
|
447
451
|
});
|
|
448
452
|
}
|
|
449
453
|
|
|
454
|
+
// ── oh-my-pi (passive ~/.omp/agent/sessions/**/*.jsonl reader) ──
|
|
455
|
+
let ompResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
456
|
+
const ompFiles = resolveOmpSessionFiles(process.env);
|
|
457
|
+
if (ompFiles.length > 0) {
|
|
458
|
+
if (progress?.enabled) {
|
|
459
|
+
progress.start(`Parsing oh-my-pi ${renderBar(0)} | buckets 0`);
|
|
460
|
+
}
|
|
461
|
+
ompResult = await parseOmpIncremental({
|
|
462
|
+
sessionFiles: ompFiles,
|
|
463
|
+
cursors,
|
|
464
|
+
queuePath,
|
|
465
|
+
env: process.env,
|
|
466
|
+
onProgress: (p) => {
|
|
467
|
+
if (!progress?.enabled) return;
|
|
468
|
+
const pct = p.total > 0 ? p.index / p.total : 1;
|
|
469
|
+
progress.update(
|
|
470
|
+
`Parsing oh-my-pi ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(p.total)} files | buckets ${formatNumber(p.bucketsQueued)}`,
|
|
471
|
+
);
|
|
472
|
+
},
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// ── Craft Agents (passive ~/.craft-agent + workspaces session.jsonl reader) ──
|
|
477
|
+
let craftResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
478
|
+
const craftFiles = resolveCraftSessionFiles(process.env);
|
|
479
|
+
if (craftFiles.length > 0) {
|
|
480
|
+
if (progress?.enabled) {
|
|
481
|
+
progress.start(`Parsing Craft ${renderBar(0)} | buckets 0`);
|
|
482
|
+
}
|
|
483
|
+
craftResult = await parseCraftIncremental({
|
|
484
|
+
sessionFiles: craftFiles,
|
|
485
|
+
cursors,
|
|
486
|
+
queuePath,
|
|
487
|
+
env: process.env,
|
|
488
|
+
onProgress: (p) => {
|
|
489
|
+
if (!progress?.enabled) return;
|
|
490
|
+
const pct = p.total > 0 ? p.index / p.total : 1;
|
|
491
|
+
progress.update(
|
|
492
|
+
`Parsing Craft ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(p.total)} files | buckets ${formatNumber(p.bucketsQueued)}`,
|
|
493
|
+
);
|
|
494
|
+
},
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
450
498
|
// ── GitHub Copilot CLI (OTEL JSONL files) ──
|
|
451
499
|
let copilotResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
452
500
|
const copilotPaths = resolveCopilotOtelPaths(process.env);
|
|
@@ -566,6 +614,8 @@ async function cmdSync(argv) {
|
|
|
566
614
|
hermesResult.recordsProcessed +
|
|
567
615
|
kimiResult.recordsProcessed +
|
|
568
616
|
codebuddyResult.recordsProcessed +
|
|
617
|
+
ompResult.recordsProcessed +
|
|
618
|
+
craftResult.recordsProcessed +
|
|
569
619
|
copilotResult.recordsProcessed;
|
|
570
620
|
const totalBuckets =
|
|
571
621
|
parseResult.bucketsQueued +
|
|
@@ -579,6 +629,8 @@ async function cmdSync(argv) {
|
|
|
579
629
|
hermesResult.bucketsQueued +
|
|
580
630
|
kimiResult.bucketsQueued +
|
|
581
631
|
codebuddyResult.bucketsQueued +
|
|
632
|
+
ompResult.bucketsQueued +
|
|
633
|
+
craftResult.bucketsQueued +
|
|
582
634
|
copilotResult.bucketsQueued;
|
|
583
635
|
process.stdout.write(
|
|
584
636
|
[
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_meta": {
|
|
3
3
|
"note": "Curated price overrides. Always wins over LiteLLM. Two reasons to live here: (1) self-defined alias names that LiteLLM will never carry (kiro-*, hy3-*, composer-*, kimi-for-coding, free-tier OpenRouter routes); (2) prices we want to pin even if LiteLLM has the model (e.g. cache_write fields LiteLLM often omits). Units: USD per million tokens. Edit this file to override pricing without redeploying.",
|
|
4
|
-
"units": "usd_per_million_tokens"
|
|
4
|
+
"units": "usd_per_million_tokens",
|
|
5
|
+
"deepseek_v4_pro_discount_expiry": "2026-05-31T15:59:00Z — DeepSeek v4-pro is currently at a 75% promotional discount. After expiry the prices revert to 4x: input $1.74/M, output $3.48/M, cache_read $0.0145/M, cache_write $1.74/M. Update this file before the cutover."
|
|
5
6
|
},
|
|
6
7
|
"exact": {
|
|
7
8
|
"kiro-agent": { "input": 3, "output": 15, "cache_read": 0.3, "cache_write": 3.75 },
|
|
@@ -16,6 +17,8 @@
|
|
|
16
17
|
"MiniMax-M2.7-highspeed":{ "input": 0.6, "output": 2.4, "cache_read": 0.06, "cache_write": 0.375 },
|
|
17
18
|
"deepseek-v4-flash":{ "input": 0.14, "output": 0.28, "cache_read": 0.0028, "cache_write": 0.14 },
|
|
18
19
|
"deepseek-v4-pro": { "input": 0.435,"output": 0.87, "cache_read": 0.003625, "cache_write": 0.435 },
|
|
20
|
+
"deepseek-chat": { "input": 0.14, "output": 0.28, "cache_read": 0.0028, "cache_write": 0.14 },
|
|
21
|
+
"deepseek-reasoner":{ "input": 0.14, "output": 0.28, "cache_read": 0.0028, "cache_write": 0.14 },
|
|
19
22
|
"kimi-for-coding": { "input": 0.6, "output": 2, "cache_read": 0.15 },
|
|
20
23
|
"kimi-k2.5": { "input": 0.6, "output": 2, "cache_read": 0.15 },
|
|
21
24
|
"kimi-k2.5-free": { "input": 0, "output": 0, "cache_read": 0 },
|