vibestats 1.0.7 → 1.0.9
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/dist/index.js +81 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -365,6 +365,46 @@ function parseCodexJsonl() {
|
|
|
365
365
|
}
|
|
366
366
|
return entries;
|
|
367
367
|
}
|
|
368
|
+
function parseStatsCacheJson() {
|
|
369
|
+
const cachePath = join(getClaudeDir(), "stats-cache.json");
|
|
370
|
+
if (!existsSync(cachePath)) return [];
|
|
371
|
+
try {
|
|
372
|
+
const cache = JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
373
|
+
const entries = [];
|
|
374
|
+
if (!cache.dailyModelTokens || !cache.modelUsage) return [];
|
|
375
|
+
for (const day of cache.dailyModelTokens) {
|
|
376
|
+
for (const [modelId, totalTokens] of Object.entries(day.tokensByModel)) {
|
|
377
|
+
const usage = cache.modelUsage[modelId];
|
|
378
|
+
if (!usage) continue;
|
|
379
|
+
const allTimeTotal = usage.inputTokens + usage.outputTokens + usage.cacheReadInputTokens + usage.cacheCreationInputTokens;
|
|
380
|
+
if (allTimeTotal === 0) continue;
|
|
381
|
+
const inputRatio = usage.inputTokens / allTimeTotal;
|
|
382
|
+
const outputRatio = usage.outputTokens / allTimeTotal;
|
|
383
|
+
const cacheReadRatio = usage.cacheReadInputTokens / allTimeTotal;
|
|
384
|
+
const cacheWriteRatio = usage.cacheCreationInputTokens / allTimeTotal;
|
|
385
|
+
const inputTokens = Math.round(totalTokens * inputRatio);
|
|
386
|
+
const outputTokens = Math.round(totalTokens * outputRatio);
|
|
387
|
+
const cacheReadTokens = Math.round(totalTokens * cacheReadRatio);
|
|
388
|
+
const cacheWriteTokens = Math.round(totalTokens * cacheWriteRatio);
|
|
389
|
+
const pricing = getModelPricing(modelId);
|
|
390
|
+
const cost = inputTokens * pricing.input / 1e6 + outputTokens * pricing.output / 1e6 + cacheWriteTokens * pricing.cacheWrite / 1e6 + cacheReadTokens * pricing.cacheRead / 1e6;
|
|
391
|
+
entries.push({
|
|
392
|
+
date: day.date,
|
|
393
|
+
model: getModelDisplayName(modelId),
|
|
394
|
+
inputTokens,
|
|
395
|
+
outputTokens,
|
|
396
|
+
cacheWriteTokens,
|
|
397
|
+
cacheReadTokens,
|
|
398
|
+
cost,
|
|
399
|
+
source: "claude"
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return entries;
|
|
404
|
+
} catch {
|
|
405
|
+
return [];
|
|
406
|
+
}
|
|
407
|
+
}
|
|
368
408
|
function filterByDateRange(entries, since, until) {
|
|
369
409
|
return entries.filter((e) => {
|
|
370
410
|
if (since && e.date < since) return false;
|
|
@@ -495,7 +535,12 @@ function loadUsageStats(options) {
|
|
|
495
535
|
const { aggregation, since, until, codexOnly, combined } = options;
|
|
496
536
|
let entries = [];
|
|
497
537
|
if (!codexOnly) {
|
|
498
|
-
|
|
538
|
+
const jsonlEntries = parseClaudeJsonl();
|
|
539
|
+
const cacheEntries = parseStatsCacheJson();
|
|
540
|
+
const jsonlDates = new Set(jsonlEntries.map((e) => e.date));
|
|
541
|
+
const supplementalEntries = cacheEntries.filter((e) => !jsonlDates.has(e.date));
|
|
542
|
+
entries = entries.concat(jsonlEntries);
|
|
543
|
+
entries = entries.concat(supplementalEntries);
|
|
499
544
|
}
|
|
500
545
|
if (codexOnly || combined) {
|
|
501
546
|
entries = entries.concat(parseCodexJsonl());
|
|
@@ -1447,21 +1492,49 @@ function getModelAbbrevFromDisplayName(displayName) {
|
|
|
1447
1492
|
};
|
|
1448
1493
|
return map[displayName] || displayName.slice(0, 5).toLowerCase();
|
|
1449
1494
|
}
|
|
1495
|
+
function aggregateRowsToMonthly(rows) {
|
|
1496
|
+
const monthMap = /* @__PURE__ */ new Map();
|
|
1497
|
+
for (const row of rows) {
|
|
1498
|
+
const month = row.key.slice(0, 7);
|
|
1499
|
+
const existing = monthMap.get(month);
|
|
1500
|
+
if (existing) {
|
|
1501
|
+
existing.inputTokens += row.inputTokens;
|
|
1502
|
+
existing.outputTokens += row.outputTokens;
|
|
1503
|
+
existing.cacheWriteTokens += row.cacheWriteTokens;
|
|
1504
|
+
existing.cacheReadTokens += row.cacheReadTokens;
|
|
1505
|
+
existing.totalTokens += row.totalTokens;
|
|
1506
|
+
existing.cost += row.cost;
|
|
1507
|
+
} else {
|
|
1508
|
+
monthMap.set(month, { ...row, key: month });
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
return Array.from(monthMap.values()).sort((a, b) => a.key.localeCompare(b.key));
|
|
1512
|
+
}
|
|
1450
1513
|
function encodeUsageToUrl(stats, baseUrl = "https://vibestats.wolfai.dev") {
|
|
1451
1514
|
const params = new URLSearchParams();
|
|
1452
|
-
const aggMap = { daily: "d", monthly: "m", model: "mo", total: "t" };
|
|
1453
|
-
params.set("agg", aggMap[stats.aggregation] || "d");
|
|
1454
1515
|
if (stats.source !== "claude") {
|
|
1455
1516
|
params.set("src", stats.source);
|
|
1456
1517
|
}
|
|
1457
1518
|
const formatDateCompact = (d) => d.replace(/-/g, "");
|
|
1458
|
-
const
|
|
1459
|
-
const
|
|
1460
|
-
const
|
|
1519
|
+
const startMs = new Date(stats.dateRange.start).getTime();
|
|
1520
|
+
const endMs = new Date(stats.dateRange.end).getTime();
|
|
1521
|
+
const daySpan = Math.ceil((endMs - startMs) / (1e3 * 60 * 60 * 24));
|
|
1522
|
+
const useMonthly = stats.aggregation === "daily" && daySpan > 31;
|
|
1523
|
+
const aggMap = { daily: "d", monthly: "m", model: "mo", total: "t" };
|
|
1524
|
+
const effectiveAgg = useMonthly ? "monthly" : stats.aggregation;
|
|
1525
|
+
params.set("agg", aggMap[effectiveAgg] || "d");
|
|
1526
|
+
let rowsToEncode;
|
|
1527
|
+
if (useMonthly) {
|
|
1528
|
+
rowsToEncode = aggregateRowsToMonthly(stats.rows);
|
|
1529
|
+
} else {
|
|
1530
|
+
rowsToEncode = stats.rows.slice(-31);
|
|
1531
|
+
}
|
|
1532
|
+
const startDate = useMonthly ? stats.dateRange.start : rowsToEncode[0]?.key || stats.dateRange.start;
|
|
1533
|
+
const endDate = useMonthly ? stats.dateRange.end : rowsToEncode[rowsToEncode.length - 1]?.key || stats.dateRange.end;
|
|
1461
1534
|
params.set("dr", `${formatDateCompact(startDate)}-${formatDateCompact(endDate)}`);
|
|
1462
|
-
const rows =
|
|
1535
|
+
const rows = rowsToEncode.map((row) => {
|
|
1463
1536
|
let key = row.key;
|
|
1464
|
-
if (
|
|
1537
|
+
if (effectiveAgg === "daily" && row.key.length === 10) {
|
|
1465
1538
|
key = row.key.slice(5).replace("-", "");
|
|
1466
1539
|
}
|
|
1467
1540
|
return [
|