vibestats 1.0.6 → 1.0.8

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 (2) hide show
  1. package/dist/index.js +69 -30
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -315,37 +315,48 @@ function parseCodexJsonl() {
315
315
  const entries = [];
316
316
  const codexDir = getCodexDir();
317
317
  const sessionsDir = join(codexDir, "sessions");
318
- if (!existsSync(sessionsDir)) return entries;
319
- const jsonlFiles = findJsonlFiles(sessionsDir);
318
+ const archivedDir = join(codexDir, "archived_sessions");
319
+ const jsonlFiles = [
320
+ ...findJsonlFiles(sessionsDir),
321
+ ...findJsonlFiles(archivedDir)
322
+ ];
323
+ if (jsonlFiles.length === 0) return entries;
320
324
  for (const filePath of jsonlFiles) {
321
325
  try {
322
326
  const content = readFileSync(filePath, "utf-8");
323
327
  const lines = content.split("\n");
328
+ let currentModel = "gpt-5";
324
329
  for (const line of lines) {
325
330
  if (!line.trim()) continue;
326
331
  try {
327
332
  const entry = JSON.parse(line);
328
- if (entry.type !== "response" || !entry.response?.usage) continue;
329
- const usage = entry.response.usage;
330
- const model = entry.response.model || "unknown";
331
- const timestamp = entry.timestamp;
332
- if (!timestamp) continue;
333
- const date = timestamp.split("T")[0];
334
- const pricing = getCodexModelPricing(model);
335
- const inputTokens = usage.input_tokens || 0;
336
- const outputTokens = usage.output_tokens || 0;
337
- const cachedInputTokens = usage.input_tokens_details?.cached_tokens || 0;
338
- const cost = (inputTokens - cachedInputTokens) * pricing.input / 1e6 + outputTokens * pricing.output / 1e6 + cachedInputTokens * pricing.cachedInput / 1e6;
339
- entries.push({
340
- date,
341
- model: getCodexModelDisplayName(model),
342
- inputTokens,
343
- outputTokens,
344
- cacheWriteTokens: 0,
345
- cacheReadTokens: cachedInputTokens,
346
- cost,
347
- source: "codex"
348
- });
333
+ if (entry.type === "turn_context" && entry.payload?.model) {
334
+ currentModel = entry.payload.model;
335
+ continue;
336
+ }
337
+ if (entry.type === "event_msg" && entry.payload?.type === "token_count") {
338
+ const info = entry.payload.info;
339
+ if (!info?.last_token_usage) continue;
340
+ const usage = info.last_token_usage;
341
+ const timestamp = entry.timestamp;
342
+ if (!timestamp) continue;
343
+ const date = timestamp.split("T")[0];
344
+ const pricing = getCodexModelPricing(currentModel);
345
+ const inputTokens = usage.input_tokens || 0;
346
+ const outputTokens = usage.output_tokens || 0;
347
+ const cachedInputTokens = usage.cached_input_tokens || 0;
348
+ const cost = (inputTokens - cachedInputTokens) * pricing.input / 1e6 + outputTokens * pricing.output / 1e6 + cachedInputTokens * pricing.cachedInput / 1e6;
349
+ entries.push({
350
+ date,
351
+ model: getCodexModelDisplayName(currentModel),
352
+ inputTokens,
353
+ outputTokens,
354
+ cacheWriteTokens: 0,
355
+ cacheReadTokens: cachedInputTokens,
356
+ cost,
357
+ source: "codex"
358
+ });
359
+ }
349
360
  } catch {
350
361
  }
351
362
  }
@@ -1436,21 +1447,49 @@ function getModelAbbrevFromDisplayName(displayName) {
1436
1447
  };
1437
1448
  return map[displayName] || displayName.slice(0, 5).toLowerCase();
1438
1449
  }
1450
+ function aggregateRowsToMonthly(rows) {
1451
+ const monthMap = /* @__PURE__ */ new Map();
1452
+ for (const row of rows) {
1453
+ const month = row.key.slice(0, 7);
1454
+ const existing = monthMap.get(month);
1455
+ if (existing) {
1456
+ existing.inputTokens += row.inputTokens;
1457
+ existing.outputTokens += row.outputTokens;
1458
+ existing.cacheWriteTokens += row.cacheWriteTokens;
1459
+ existing.cacheReadTokens += row.cacheReadTokens;
1460
+ existing.totalTokens += row.totalTokens;
1461
+ existing.cost += row.cost;
1462
+ } else {
1463
+ monthMap.set(month, { ...row, key: month });
1464
+ }
1465
+ }
1466
+ return Array.from(monthMap.values()).sort((a, b) => a.key.localeCompare(b.key));
1467
+ }
1439
1468
  function encodeUsageToUrl(stats, baseUrl = "https://vibestats.wolfai.dev") {
1440
1469
  const params = new URLSearchParams();
1441
- const aggMap = { daily: "d", monthly: "m", model: "mo", total: "t" };
1442
- params.set("agg", aggMap[stats.aggregation] || "d");
1443
1470
  if (stats.source !== "claude") {
1444
1471
  params.set("src", stats.source);
1445
1472
  }
1446
1473
  const formatDateCompact = (d) => d.replace(/-/g, "");
1447
- const limitedRows = stats.rows.slice(-31);
1448
- const startDate = limitedRows[0]?.key || stats.dateRange.start;
1449
- const endDate = limitedRows[limitedRows.length - 1]?.key || stats.dateRange.end;
1474
+ const startMs = new Date(stats.dateRange.start).getTime();
1475
+ const endMs = new Date(stats.dateRange.end).getTime();
1476
+ const daySpan = Math.ceil((endMs - startMs) / (1e3 * 60 * 60 * 24));
1477
+ const useMonthly = stats.aggregation === "daily" && daySpan > 31;
1478
+ const aggMap = { daily: "d", monthly: "m", model: "mo", total: "t" };
1479
+ const effectiveAgg = useMonthly ? "monthly" : stats.aggregation;
1480
+ params.set("agg", aggMap[effectiveAgg] || "d");
1481
+ let rowsToEncode;
1482
+ if (useMonthly) {
1483
+ rowsToEncode = aggregateRowsToMonthly(stats.rows);
1484
+ } else {
1485
+ rowsToEncode = stats.rows.slice(-31);
1486
+ }
1487
+ const startDate = useMonthly ? stats.dateRange.start : rowsToEncode[0]?.key || stats.dateRange.start;
1488
+ const endDate = useMonthly ? stats.dateRange.end : rowsToEncode[rowsToEncode.length - 1]?.key || stats.dateRange.end;
1450
1489
  params.set("dr", `${formatDateCompact(startDate)}-${formatDateCompact(endDate)}`);
1451
- const rows = limitedRows.map((row) => {
1490
+ const rows = rowsToEncode.map((row) => {
1452
1491
  let key = row.key;
1453
- if (stats.aggregation === "daily" && row.key.length === 10) {
1492
+ if (effectiveAgg === "daily" && row.key.length === 10) {
1454
1493
  key = row.key.slice(5).replace("-", "");
1455
1494
  }
1456
1495
  return [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibestats",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "AI coding stats - usage tracking and annual wrapped for Claude Code & Codex",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",