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.
@@ -135,7 +135,7 @@
135
135
  ]
136
136
  }
137
137
  </script>
138
- <script type="module" crossorigin src="/assets/main-BhjD_pKB.js"></script>
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-BhjD_pKB.js"></script>
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.99",
4
- "description": "Token usage tracker for AI agent CLIs (Claude Code, Codex, Cursor, Kiro, Gemini, OpenCode, OpenClaw, Hermes, GitHub Copilot)",
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",
@@ -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.
@@ -16,19 +16,33 @@ const {
16
16
  isGeminiHookConfigured,
17
17
  buildGeminiHookCommand,
18
18
  } = require("../lib/gemini-config");
19
- const { resolveOpencodeConfigDir, isOpencodePluginInstalled } = require("../lib/opencode-config");
19
+ const {
20
+ resolveOpencodeConfigDir,
21
+ isOpencodePluginInstalled,
22
+ } = require("../lib/opencode-config");
20
23
  const { collectLocalSubscriptions } = require("../lib/subscriptions");
21
- const { describeCopilotOtelStatus, readCopilotOauthToken } = require("../lib/usage-limits");
22
- const { normalizeState: normalizeUploadState } = require("../lib/upload-throttle");
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 { probeOpenclawSessionPluginState } = require("../lib/openclaw-session-plugin");
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({ configDir: geminiConfigDir });
64
- const opencodeConfigDir = resolveOpencodeConfigDir({ home, env: process.env });
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(await readJson(uploadThrottlePath));
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 = (await safeReadText(openclawSignalPath))?.trim() || null;
81
- const lastNotifySpawn = parseEpochMsToIso((await safeReadText(throttlePath))?.trim() || null);
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 = Array.isArray(everyCodeNotify) && everyCodeNotify.length > 0;
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({ home, trackerDir, env: process.env });
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({ token: copilotToken, otel: copilotOtel });
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 ? "set (via GitHub OAuth)" : "unset (no Copilot OAuth token found)";
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 (tool === "claude" && provider === "anthropic" && product === "subscription") {
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 = { diagnostics: false, probeKeychain: false, probeKeychainDetails: false };
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];
@@ -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 },