@zhangferry-dev/tokendash 1.5.0 → 1.6.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.
@@ -255,20 +255,24 @@ var TokenUsageSchema = import_zod2.z.object({
255
255
  }).default({ input_tokens: 0, cached_input_tokens: 0, output_tokens: 0, reasoning_output_tokens: 0, total_tokens: 0 });
256
256
  var TokenCountInfoSchema = import_zod2.z.object({
257
257
  total_token_usage: TokenUsageSchema,
258
- last_token_usage: TokenUsageSchema
258
+ last_token_usage: TokenUsageSchema.optional()
259
259
  }).nullable().default(null);
260
260
  var TokenCountPayloadSchema = import_zod2.z.object({
261
261
  type: import_zod2.z.literal("token_count"),
262
262
  info: TokenCountInfoSchema
263
263
  });
264
- function tokenUsageKey(usage) {
265
- return [
266
- usage.input_tokens,
267
- usage.cached_input_tokens,
268
- usage.output_tokens,
269
- usage.reasoning_output_tokens,
270
- usage.total_tokens
271
- ].join(":");
264
+ function subtractTokenUsage(current, previous) {
265
+ return {
266
+ timestamp: "",
267
+ inputTokens: Math.max(0, current.input_tokens - (previous?.input_tokens ?? 0)),
268
+ cachedInputTokens: Math.max(0, current.cached_input_tokens - (previous?.cached_input_tokens ?? 0)),
269
+ outputTokens: Math.max(0, current.output_tokens - (previous?.output_tokens ?? 0)),
270
+ reasoningOutputTokens: Math.max(0, current.reasoning_output_tokens - (previous?.reasoning_output_tokens ?? 0)),
271
+ totalTokens: Math.max(0, current.total_tokens - (previous?.total_tokens ?? 0))
272
+ };
273
+ }
274
+ function displayInputTokens(inputTokens, cachedInputTokens) {
275
+ return Math.max(0, inputTokens - cachedInputTokens);
272
276
  }
273
277
  function getSessionsDir() {
274
278
  return (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".codex", "sessions");
@@ -314,7 +318,9 @@ function parseCodexSession(filepath) {
314
318
  let model = "";
315
319
  let createdAt = "";
316
320
  const tokenEvents = [];
321
+ let previousTotalUsage = null;
317
322
  const seenTotalUsageSnapshots = /* @__PURE__ */ new Set();
323
+ const seenUsageEvents = /* @__PURE__ */ new Set();
318
324
  for (const line of lines) {
319
325
  const trimmed = line.trim();
320
326
  if (!trimmed) continue;
@@ -348,18 +354,40 @@ function parseCodexSession(filepath) {
348
354
  }
349
355
  const info = parseResult.data.info;
350
356
  if (!info) continue;
351
- const totalUsageKey = tokenUsageKey(info.total_token_usage);
357
+ const totalUsageKey = [
358
+ info.total_token_usage.input_tokens,
359
+ info.total_token_usage.cached_input_tokens,
360
+ info.total_token_usage.output_tokens,
361
+ info.total_token_usage.reasoning_output_tokens,
362
+ info.total_token_usage.total_tokens
363
+ ].join(":");
352
364
  if (seenTotalUsageSnapshots.has(totalUsageKey)) continue;
353
365
  seenTotalUsageSnapshots.add(totalUsageKey);
354
- const last = info.last_token_usage;
355
- tokenEvents.push({
366
+ const last = info.last_token_usage ?? info.total_token_usage;
367
+ const rawEvent = info.last_token_usage ? subtractTokenUsage(last, null) : subtractTokenUsage(last, previousTotalUsage);
368
+ previousTotalUsage = info.total_token_usage;
369
+ if (rawEvent.inputTokens === 0 && rawEvent.cachedInputTokens === 0 && rawEvent.outputTokens === 0 && rawEvent.reasoningOutputTokens === 0) {
370
+ continue;
371
+ }
372
+ const event = {
373
+ ...rawEvent,
356
374
  timestamp,
357
- inputTokens: last.input_tokens,
358
- cachedInputTokens: last.cached_input_tokens,
359
- outputTokens: last.output_tokens,
360
- reasoningOutputTokens: last.reasoning_output_tokens,
361
- totalTokens: last.total_tokens
362
- });
375
+ cachedInputTokens: Math.min(rawEvent.cachedInputTokens, rawEvent.inputTokens)
376
+ };
377
+ const eventKey = [
378
+ timestamp,
379
+ model,
380
+ event.inputTokens,
381
+ event.cachedInputTokens,
382
+ event.outputTokens,
383
+ event.reasoningOutputTokens,
384
+ event.totalTokens
385
+ ].join(":");
386
+ if (seenUsageEvents.has(eventKey)) {
387
+ continue;
388
+ }
389
+ seenUsageEvents.add(eventKey);
390
+ tokenEvents.push(event);
363
391
  }
364
392
  }
365
393
  }
@@ -409,6 +437,12 @@ function addAcc(a, ev) {
409
437
  a.reasoningOutputTokens += ev.reasoningOutputTokens;
410
438
  a.totalTokens += ev.totalTokens;
411
439
  }
440
+ function displayAcc(acc) {
441
+ return {
442
+ ...acc,
443
+ inputTokens: displayInputTokens(acc.inputTokens, acc.cachedInputTokens)
444
+ };
445
+ }
412
446
  function mergeAcc(a, b) {
413
447
  a.inputTokens += b.inputTokens;
414
448
  a.cachedInputTokens += b.cachedInputTokens;
@@ -423,30 +457,34 @@ function addAccToBucket(bucket, ev, model) {
423
457
  addAcc(bucket.models.get(model), ev);
424
458
  }
425
459
  function accToEntry(date, acc, modelAccs) {
460
+ const display = displayAcc(acc);
426
461
  const modelNames = [...modelAccs.keys()];
427
462
  const modelBreakdowns = buildModelBreakdowns(modelAccs);
428
463
  const totalCost = modelBreakdowns.reduce((sum, model) => sum + model.cost, 0);
429
464
  return {
430
465
  date,
431
- inputTokens: acc.inputTokens,
432
- outputTokens: acc.outputTokens,
466
+ inputTokens: display.inputTokens,
467
+ outputTokens: display.outputTokens,
433
468
  cacheCreationTokens: 0,
434
- cacheReadTokens: acc.cachedInputTokens,
435
- totalTokens: acc.totalTokens,
469
+ cacheReadTokens: display.cachedInputTokens,
470
+ totalTokens: display.totalTokens,
436
471
  totalCost,
437
472
  modelsUsed: modelNames,
438
473
  modelBreakdowns
439
474
  };
440
475
  }
441
476
  function buildModelBreakdowns(modelAccs) {
442
- return [...modelAccs.entries()].map(([modelName, acc]) => ({
443
- modelName,
444
- inputTokens: acc.inputTokens,
445
- outputTokens: acc.outputTokens,
446
- cacheCreationTokens: 0,
447
- cacheReadTokens: acc.cachedInputTokens,
448
- cost: calculateCost(acc, /* @__PURE__ */ new Set([modelName]))
449
- }));
477
+ return [...modelAccs.entries()].map(([modelName, acc]) => {
478
+ const display = displayAcc(acc);
479
+ return {
480
+ modelName,
481
+ inputTokens: display.inputTokens,
482
+ outputTokens: display.outputTokens,
483
+ cacheCreationTokens: 0,
484
+ cacheReadTokens: display.cachedInputTokens,
485
+ cost: calculateCost(acc, /* @__PURE__ */ new Set([modelName]))
486
+ };
487
+ });
450
488
  }
451
489
  function groupSessions(sessions, options) {
452
490
  const tz = options.timezone || "Asia/Shanghai";
@@ -501,7 +539,7 @@ function buildDailyResponse(sessions, options) {
501
539
  return {
502
540
  daily,
503
541
  totals: {
504
- inputTokens: totalsAcc.inputTokens,
542
+ inputTokens: displayInputTokens(totalsAcc.inputTokens, totalsAcc.cachedInputTokens),
505
543
  outputTokens: totalsAcc.outputTokens,
506
544
  cacheCreationTokens: 0,
507
545
  cacheReadTokens: totalsAcc.cachedInputTokens,
@@ -552,7 +590,7 @@ function buildBlocksResponse(sessions, options) {
552
590
  isGap: false,
553
591
  entries: acc.totalTokens > 0 ? 1 : 0,
554
592
  tokenCounts: {
555
- inputTokens: acc.inputTokens,
593
+ inputTokens: displayInputTokens(acc.inputTokens, acc.cachedInputTokens),
556
594
  outputTokens: acc.outputTokens,
557
595
  cacheCreationInputTokens: 0,
558
596
  cacheReadInputTokens: acc.cachedInputTokens
@@ -1165,18 +1203,18 @@ var import_node_path5 = require("node:path");
1165
1203
  var import_node_os5 = require("node:os");
1166
1204
  var MODEL_PRICING2 = {
1167
1205
  // Claude 4.6
1168
- "claude-opus-4-6": { inputPer1M: 15, cacheReadPer1M: 1.5, outputPer1M: 75 },
1169
- "claude-sonnet-4-6": { inputPer1M: 3, cacheReadPer1M: 0.3, outputPer1M: 15 },
1206
+ "claude-opus-4-6": { inputPer1M: 15, cacheCreationPer1M: 18.75, cacheReadPer1M: 1.5, outputPer1M: 75 },
1207
+ "claude-sonnet-4-6": { inputPer1M: 3, cacheCreationPer1M: 3.75, cacheReadPer1M: 0.3, outputPer1M: 15 },
1170
1208
  // Claude 4.5
1171
- "claude-sonnet-4-5-20250514": { inputPer1M: 3, cacheReadPer1M: 0.3, outputPer1M: 15 },
1172
- "claude-haiku-4-5-20251001": { inputPer1M: 0.8, cacheReadPer1M: 0.08, outputPer1M: 4 },
1209
+ "claude-sonnet-4-5-20250514": { inputPer1M: 3, cacheCreationPer1M: 3.75, cacheReadPer1M: 0.3, outputPer1M: 15 },
1210
+ "claude-haiku-4-5-20251001": { inputPer1M: 0.8, cacheCreationPer1M: 1, cacheReadPer1M: 0.08, outputPer1M: 4 },
1173
1211
  // Older Claude models
1174
- "claude-3-5-sonnet-20241022": { inputPer1M: 3, cacheReadPer1M: 0.3, outputPer1M: 15 },
1175
- "claude-3-5-haiku-20241022": { inputPer1M: 0.8, cacheReadPer1M: 0.08, outputPer1M: 4 },
1176
- "claude-3-opus-20240229": { inputPer1M: 15, cacheReadPer1M: 1.5, outputPer1M: 75 },
1177
- "claude-3-haiku-20240307": { inputPer1M: 0.25, cacheReadPer1M: 0.03, outputPer1M: 1.25 }
1212
+ "claude-3-5-sonnet-20241022": { inputPer1M: 3, cacheCreationPer1M: 3.75, cacheReadPer1M: 0.3, outputPer1M: 15 },
1213
+ "claude-3-5-haiku-20241022": { inputPer1M: 0.8, cacheCreationPer1M: 1, cacheReadPer1M: 0.08, outputPer1M: 4 },
1214
+ "claude-3-opus-20240229": { inputPer1M: 15, cacheCreationPer1M: 18.75, cacheReadPer1M: 1.5, outputPer1M: 75 },
1215
+ "claude-3-haiku-20240307": { inputPer1M: 0.25, cacheCreationPer1M: 0.3, cacheReadPer1M: 0.03, outputPer1M: 1.25 }
1178
1216
  };
1179
- var DEFAULT_PRICING2 = { inputPer1M: 3, cacheReadPer1M: 0.3, outputPer1M: 15 };
1217
+ var DEFAULT_PRICING2 = { inputPer1M: 3, cacheCreationPer1M: 3.75, cacheReadPer1M: 0.3, outputPer1M: 15 };
1180
1218
  function getPricing(model) {
1181
1219
  if (MODEL_PRICING2[model]) return MODEL_PRICING2[model];
1182
1220
  const lower = model.toLowerCase();
@@ -1185,10 +1223,12 @@ function getPricing(model) {
1185
1223
  }
1186
1224
  return DEFAULT_PRICING2;
1187
1225
  }
1188
- function calculateCost2(inputTokens, cacheReadTokens, outputTokens, model) {
1226
+ function calculateCost2(inputTokens, cacheReadTokens, outputTokens, model, cacheCreationTokens = 0) {
1189
1227
  const p = getPricing(model);
1190
- const nonCachedInput = Math.max(inputTokens - cacheReadTokens, 0);
1191
- return nonCachedInput / 1e6 * p.inputPer1M + cacheReadTokens / 1e6 * p.cacheReadPer1M + outputTokens / 1e6 * p.outputPer1M;
1228
+ return inputTokens / 1e6 * p.inputPer1M + cacheCreationTokens / 1e6 * p.cacheCreationPer1M + cacheReadTokens / 1e6 * p.cacheReadPer1M + outputTokens / 1e6 * p.outputPer1M;
1229
+ }
1230
+ function totalClaudeTokens(inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens) {
1231
+ return inputTokens + outputTokens + cacheCreationTokens + cacheReadTokens;
1192
1232
  }
1193
1233
  var CLAUDE_PROJECTS_DIR = (0, import_node_path5.join)((0, import_node_os5.homedir)(), ".claude", "projects");
1194
1234
  var fileCache = /* @__PURE__ */ new Map();
@@ -1293,7 +1333,7 @@ function parseAllSessions2(project) {
1293
1333
  const outputTokens = usage.output_tokens || 0;
1294
1334
  const cacheCreationTokens = usage.cache_creation_input_tokens || 0;
1295
1335
  const cacheReadTokens = usage.cache_read_input_tokens || 0;
1296
- const totalTokens = inputTokens + outputTokens + cacheReadTokens;
1336
+ const totalTokens = totalClaudeTokens(inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens);
1297
1337
  if (totalTokens === 0) continue;
1298
1338
  entries.push({
1299
1339
  timestamp: obj.timestamp,
@@ -1377,8 +1417,8 @@ function getDailyResponse4(project, tz = DEFAULT_TZ) {
1377
1417
  agg.outputTokens += e.outputTokens;
1378
1418
  agg.cacheCreationTokens += e.cacheCreationTokens;
1379
1419
  agg.cacheReadTokens += e.cacheReadTokens;
1380
- agg.totalTokens += e.inputTokens + e.outputTokens + e.cacheReadTokens;
1381
- const cost = calculateCost2(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model);
1420
+ agg.totalTokens += totalClaudeTokens(e.inputTokens, e.outputTokens, e.cacheCreationTokens, e.cacheReadTokens);
1421
+ const cost = calculateCost2(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model, e.cacheCreationTokens);
1382
1422
  agg.totalCost += cost;
1383
1423
  if (!agg.models.has(e.model)) {
1384
1424
  agg.models.set(e.model, { input: 0, output: 0, cacheCreation: 0, cacheRead: 0, cost: 0 });
@@ -1428,8 +1468,8 @@ function getProjectsResponse4(tz = DEFAULT_TZ) {
1428
1468
  agg.outputTokens += e.outputTokens;
1429
1469
  agg.cacheCreationTokens += e.cacheCreationTokens;
1430
1470
  agg.cacheReadTokens += e.cacheReadTokens;
1431
- agg.totalTokens += e.inputTokens + e.outputTokens + e.cacheReadTokens;
1432
- const cost = calculateCost2(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model);
1471
+ agg.totalTokens += totalClaudeTokens(e.inputTokens, e.outputTokens, e.cacheCreationTokens, e.cacheReadTokens);
1472
+ const cost = calculateCost2(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model, e.cacheCreationTokens);
1433
1473
  agg.totalCost += cost;
1434
1474
  if (!agg.models.has(e.model)) {
1435
1475
  agg.models.set(e.model, { input: 0, output: 0, cacheCreation: 0, cacheRead: 0, cost: 0 });
@@ -1467,13 +1507,13 @@ function getBlocksResponse4(project, tz = DEFAULT_TZ) {
1467
1507
  bucket.outputTokens += e.outputTokens;
1468
1508
  bucket.cacheCreationTokens += e.cacheCreationTokens;
1469
1509
  bucket.cacheReadTokens += e.cacheReadTokens;
1470
- bucket.costUSD += calculateCost2(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model);
1510
+ bucket.costUSD += calculateCost2(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model, e.cacheCreationTokens);
1471
1511
  bucket.models.add(e.model);
1472
1512
  }
1473
1513
  const blocks = [];
1474
1514
  let idx = 0;
1475
1515
  for (const [hourKey, bucket] of hourMap) {
1476
- const totalTokens = bucket.inputTokens + bucket.outputTokens + bucket.cacheReadTokens;
1516
+ const totalTokens = totalClaudeTokens(bucket.inputTokens, bucket.outputTokens, bucket.cacheCreationTokens, bucket.cacheReadTokens);
1477
1517
  blocks.push({
1478
1518
  id: `claude-${idx}`,
1479
1519
  startTime: `${hourKey}:00:00`,