vibestats 1.3.4 → 1.3.6
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 +833 -321
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,121 +3,6 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { defineCommand, runMain } from "citty";
|
|
5
5
|
|
|
6
|
-
// src/usage/loader.ts
|
|
7
|
-
import { promises as fs } from "fs";
|
|
8
|
-
import { homedir } from "os";
|
|
9
|
-
import { join, basename } from "path";
|
|
10
|
-
|
|
11
|
-
// src/pricing.ts
|
|
12
|
-
var MODEL_PRICING = {
|
|
13
|
-
// Opus 4.6 (same pricing as Opus 4.5)
|
|
14
|
-
"claude-opus-4-6-20260101": {
|
|
15
|
-
input: 5,
|
|
16
|
-
output: 25,
|
|
17
|
-
cacheWrite: 6.25,
|
|
18
|
-
cacheRead: 0.5
|
|
19
|
-
},
|
|
20
|
-
// Opus 4.5 (cheaper than Opus 4.1)
|
|
21
|
-
"claude-opus-4-5-20251101": {
|
|
22
|
-
input: 5,
|
|
23
|
-
output: 25,
|
|
24
|
-
cacheWrite: 6.25,
|
|
25
|
-
cacheRead: 0.5
|
|
26
|
-
},
|
|
27
|
-
// Sonnet 4.6 (same pricing as Sonnet 4.5)
|
|
28
|
-
"claude-sonnet-4-6-20260101": {
|
|
29
|
-
input: 3,
|
|
30
|
-
output: 15,
|
|
31
|
-
cacheWrite: 3.75,
|
|
32
|
-
cacheRead: 0.3
|
|
33
|
-
},
|
|
34
|
-
// Sonnet 4.5
|
|
35
|
-
"claude-sonnet-4-5-20250929": {
|
|
36
|
-
input: 3,
|
|
37
|
-
output: 15,
|
|
38
|
-
cacheWrite: 3.75,
|
|
39
|
-
cacheRead: 0.3
|
|
40
|
-
},
|
|
41
|
-
// Opus 4.1 (MORE expensive than Opus 4.5)
|
|
42
|
-
"claude-opus-4-1-20250805": {
|
|
43
|
-
input: 15,
|
|
44
|
-
output: 75,
|
|
45
|
-
cacheWrite: 18.75,
|
|
46
|
-
cacheRead: 1.5
|
|
47
|
-
},
|
|
48
|
-
// Haiku 4.5
|
|
49
|
-
"claude-haiku-4-5-20251001": {
|
|
50
|
-
input: 1,
|
|
51
|
-
output: 5,
|
|
52
|
-
cacheWrite: 1.25,
|
|
53
|
-
cacheRead: 0.1
|
|
54
|
-
},
|
|
55
|
-
// Sonnet 3.5 (legacy - same as Sonnet 4.5)
|
|
56
|
-
"claude-3-5-sonnet-20241022": {
|
|
57
|
-
input: 3,
|
|
58
|
-
output: 15,
|
|
59
|
-
cacheWrite: 3.75,
|
|
60
|
-
cacheRead: 0.3
|
|
61
|
-
},
|
|
62
|
-
"claude-3-5-sonnet-20240620": {
|
|
63
|
-
input: 3,
|
|
64
|
-
output: 15,
|
|
65
|
-
cacheWrite: 3.75,
|
|
66
|
-
cacheRead: 0.3
|
|
67
|
-
},
|
|
68
|
-
// Haiku 3.5 (legacy - same as Haiku 4.5)
|
|
69
|
-
"claude-3-5-haiku-20241022": {
|
|
70
|
-
input: 1,
|
|
71
|
-
output: 5,
|
|
72
|
-
cacheWrite: 1.25,
|
|
73
|
-
cacheRead: 0.1
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
function getModelPricing(modelName) {
|
|
77
|
-
if (MODEL_PRICING[modelName]) {
|
|
78
|
-
return MODEL_PRICING[modelName];
|
|
79
|
-
}
|
|
80
|
-
if (modelName.includes("opus-4-6") || modelName.includes("opus-4.6")) {
|
|
81
|
-
return MODEL_PRICING["claude-opus-4-6-20260101"];
|
|
82
|
-
}
|
|
83
|
-
if (modelName.includes("opus-4-5") || modelName.includes("opus-4.5")) {
|
|
84
|
-
return MODEL_PRICING["claude-opus-4-5-20251101"];
|
|
85
|
-
}
|
|
86
|
-
if (modelName.includes("opus-4-1") || modelName.includes("opus-4.1") || modelName.includes("opus-4")) {
|
|
87
|
-
return MODEL_PRICING["claude-opus-4-1-20250805"];
|
|
88
|
-
}
|
|
89
|
-
if (modelName.includes("sonnet-4-6") || modelName.includes("sonnet-4.6")) {
|
|
90
|
-
return MODEL_PRICING["claude-sonnet-4-6-20260101"];
|
|
91
|
-
}
|
|
92
|
-
if (modelName.includes("sonnet-4-5") || modelName.includes("sonnet-4.5")) {
|
|
93
|
-
return MODEL_PRICING["claude-sonnet-4-5-20250929"];
|
|
94
|
-
}
|
|
95
|
-
if (modelName.includes("sonnet")) {
|
|
96
|
-
return MODEL_PRICING["claude-3-5-sonnet-20241022"];
|
|
97
|
-
}
|
|
98
|
-
if (modelName.includes("haiku-4-5") || modelName.includes("haiku-4.5")) {
|
|
99
|
-
return MODEL_PRICING["claude-haiku-4-5-20251001"];
|
|
100
|
-
}
|
|
101
|
-
if (modelName.includes("haiku")) {
|
|
102
|
-
return MODEL_PRICING["claude-3-5-haiku-20241022"];
|
|
103
|
-
}
|
|
104
|
-
return MODEL_PRICING["claude-sonnet-4-5-20250929"];
|
|
105
|
-
}
|
|
106
|
-
function getModelDisplayName(modelName) {
|
|
107
|
-
if (modelName.includes("opus-4-6") || modelName.includes("opus-4.6")) return "Opus 4.6";
|
|
108
|
-
if (modelName.includes("opus-4-5") || modelName.includes("opus-4.5")) return "Opus 4.5";
|
|
109
|
-
if (modelName.includes("opus-4-1") || modelName.includes("opus-4.1")) return "Opus 4.1";
|
|
110
|
-
if (modelName.includes("opus")) return "Opus";
|
|
111
|
-
if (modelName.includes("sonnet-4-6") || modelName.includes("sonnet-4.6")) return "Sonnet 4.6";
|
|
112
|
-
if (modelName.includes("sonnet-4-5") || modelName.includes("sonnet-4.5")) return "Sonnet 4.5";
|
|
113
|
-
if (modelName.includes("sonnet-3-5") || modelName.includes("sonnet-3.5")) return "Sonnet 3.5";
|
|
114
|
-
if (modelName.includes("sonnet")) return "Sonnet";
|
|
115
|
-
if (modelName.includes("haiku-4-5") || modelName.includes("haiku-4.5")) return "Haiku 4.5";
|
|
116
|
-
if (modelName.includes("haiku-3-5") || modelName.includes("haiku-3.5")) return "Haiku 3.5";
|
|
117
|
-
if (modelName.includes("haiku")) return "Haiku";
|
|
118
|
-
return modelName;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
6
|
// src/codex-pricing.ts
|
|
122
7
|
var GPT_54_PRICING = { input: 2.5, output: 15, cachedInput: 0.25 };
|
|
123
8
|
var GPT_53_PRICING = { input: 1.75, output: 14, cachedInput: 0.175 };
|
|
@@ -395,6 +280,501 @@ function calculateCodexCost(modelName, inputTokens, outputTokens, cachedInputTok
|
|
|
395
280
|
return inputCost + cachedCost + outputCost;
|
|
396
281
|
}
|
|
397
282
|
|
|
283
|
+
// src/url-encoder.ts
|
|
284
|
+
function formatCompactNumber(num) {
|
|
285
|
+
if (num >= 1e9) {
|
|
286
|
+
return `${(num / 1e9).toFixed(1)}B`;
|
|
287
|
+
}
|
|
288
|
+
if (num >= 1e6) {
|
|
289
|
+
return `${(num / 1e6).toFixed(1)}M`;
|
|
290
|
+
}
|
|
291
|
+
if (num >= 1e3) {
|
|
292
|
+
return `${(num / 1e3).toFixed(1)}K`;
|
|
293
|
+
}
|
|
294
|
+
return num.toString();
|
|
295
|
+
}
|
|
296
|
+
var dayToNumber = {
|
|
297
|
+
Sunday: 0,
|
|
298
|
+
Monday: 1,
|
|
299
|
+
Tuesday: 2,
|
|
300
|
+
Wednesday: 3,
|
|
301
|
+
Thursday: 4,
|
|
302
|
+
Friday: 5,
|
|
303
|
+
Saturday: 6
|
|
304
|
+
};
|
|
305
|
+
function encodeStatsToUrl(stats, baseUrl = "https://vibestats.wolfai.dev") {
|
|
306
|
+
const params = new URLSearchParams();
|
|
307
|
+
params.set("s", stats.sessions.toString());
|
|
308
|
+
params.set("t", formatCompactNumber(stats.totalTokens));
|
|
309
|
+
params.set("c", stats.totalCost.toFixed(2));
|
|
310
|
+
params.set("d", stats.daysActive.toString());
|
|
311
|
+
params.set("ls", stats.longestStreak.toString());
|
|
312
|
+
params.set("cs", stats.currentStreak.toString());
|
|
313
|
+
params.set("ph", stats.peakHour.toString());
|
|
314
|
+
params.set("pd", (dayToNumber[stats.peakDay] ?? 0).toString());
|
|
315
|
+
params.set("fm", getModelTokenFromDisplayName(stats.favoriteModel));
|
|
316
|
+
const mbParts = stats.modelBreakdown.slice(0, 3).map((m) => {
|
|
317
|
+
const abbr = getModelTokenFromDisplayName(m.model);
|
|
318
|
+
return `${abbr}:${m.percentage}`;
|
|
319
|
+
});
|
|
320
|
+
params.set("mb", mbParts.join(","));
|
|
321
|
+
if (stats.topTools && stats.topTools.length > 0) {
|
|
322
|
+
params.set("tt", stats.topTools.slice(0, 5).join(","));
|
|
323
|
+
}
|
|
324
|
+
if (stats.developerStyle) {
|
|
325
|
+
const styleMap = {
|
|
326
|
+
reader: "r",
|
|
327
|
+
writer: "w",
|
|
328
|
+
executor: "e",
|
|
329
|
+
balanced: "b"
|
|
330
|
+
};
|
|
331
|
+
params.set("st", styleMap[stats.developerStyle] || "b");
|
|
332
|
+
}
|
|
333
|
+
if (stats.topProject) {
|
|
334
|
+
params.set("tp", stats.topProject);
|
|
335
|
+
}
|
|
336
|
+
if (stats.projectCount) {
|
|
337
|
+
params.set("pc", stats.projectCount.toString());
|
|
338
|
+
}
|
|
339
|
+
params.set("wg", formatCompactNumber(stats.wordsGenerated));
|
|
340
|
+
const firstDate = new Date(stats.firstSessionDate);
|
|
341
|
+
params.set("fad", firstDate.toISOString().split("T")[0].replace(/-/g, ""));
|
|
342
|
+
if (stats.source && stats.source !== "claude") {
|
|
343
|
+
params.set("src", stats.source);
|
|
344
|
+
}
|
|
345
|
+
if (stats.activity) {
|
|
346
|
+
params.set("act", encodePayload(stats.activity));
|
|
347
|
+
}
|
|
348
|
+
return `${baseUrl}/wrapped/?${params.toString()}`;
|
|
349
|
+
}
|
|
350
|
+
function encodeActivityToUrl(payload, baseUrl = "https://vibestats.wolfai.dev") {
|
|
351
|
+
const params = new URLSearchParams();
|
|
352
|
+
params.set("activity", encodePayload(payload));
|
|
353
|
+
if (payload.source !== "claude") {
|
|
354
|
+
params.set("src", payload.source);
|
|
355
|
+
}
|
|
356
|
+
return `${baseUrl}/activity?${params.toString()}`;
|
|
357
|
+
}
|
|
358
|
+
var CLAUDE_DISPLAY_TO_TOKEN = {
|
|
359
|
+
"Opus 4.6": "o46",
|
|
360
|
+
"Opus 4.5": "o45",
|
|
361
|
+
"Opus 4.1": "o41",
|
|
362
|
+
Opus: "opus",
|
|
363
|
+
"Sonnet 4.6": "s46",
|
|
364
|
+
"Sonnet 4.5": "s45",
|
|
365
|
+
"Sonnet 3.5": "s35",
|
|
366
|
+
Sonnet: "sonnet",
|
|
367
|
+
"Haiku 4.5": "h45",
|
|
368
|
+
"Haiku 3.5": "h35",
|
|
369
|
+
Haiku: "haiku"
|
|
370
|
+
};
|
|
371
|
+
function getModelTokenFromDisplayName(displayName) {
|
|
372
|
+
if (CLAUDE_DISPLAY_TO_TOKEN[displayName]) {
|
|
373
|
+
return CLAUDE_DISPLAY_TO_TOKEN[displayName];
|
|
374
|
+
}
|
|
375
|
+
const normalized = displayName.toLowerCase();
|
|
376
|
+
if (normalized.includes("opus") && normalized.includes("4.6")) return "o46";
|
|
377
|
+
if (normalized.includes("opus") && normalized.includes("4.5")) return "o45";
|
|
378
|
+
if (normalized.includes("opus") && normalized.includes("4.1")) return "o41";
|
|
379
|
+
if (normalized.includes("opus")) return "opus";
|
|
380
|
+
if (normalized.includes("sonnet") && normalized.includes("4.6")) return "s46";
|
|
381
|
+
if (normalized.includes("sonnet") && normalized.includes("4.5")) return "s45";
|
|
382
|
+
if (normalized.includes("sonnet") && normalized.includes("3.5")) return "s35";
|
|
383
|
+
if (normalized.includes("sonnet")) return "sonnet";
|
|
384
|
+
if (normalized.includes("haiku") && normalized.includes("4.5")) return "h45";
|
|
385
|
+
if (normalized.includes("haiku") && normalized.includes("3.5")) return "h35";
|
|
386
|
+
if (normalized.includes("haiku")) return "haiku";
|
|
387
|
+
return getCodexModelAbbreviation(displayName);
|
|
388
|
+
}
|
|
389
|
+
function aggregateRowsToMonthly(rows) {
|
|
390
|
+
const monthMap = /* @__PURE__ */ new Map();
|
|
391
|
+
for (const row of rows) {
|
|
392
|
+
const month = row.key.slice(0, 7);
|
|
393
|
+
const existing = monthMap.get(month);
|
|
394
|
+
if (existing) {
|
|
395
|
+
existing.inputTokens += row.inputTokens;
|
|
396
|
+
existing.outputTokens += row.outputTokens;
|
|
397
|
+
existing.cacheWriteTokens += row.cacheWriteTokens;
|
|
398
|
+
existing.cacheReadTokens += row.cacheReadTokens;
|
|
399
|
+
existing.totalTokens += row.totalTokens;
|
|
400
|
+
existing.cost += row.cost;
|
|
401
|
+
} else {
|
|
402
|
+
monthMap.set(month, { ...row, key: month });
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return Array.from(monthMap.values()).sort((a, b) => a.key.localeCompare(b.key));
|
|
406
|
+
}
|
|
407
|
+
function encodeUsageToUrl(stats, baseUrl = "https://vibestats.wolfai.dev") {
|
|
408
|
+
const params = new URLSearchParams();
|
|
409
|
+
if (stats.source !== "claude") {
|
|
410
|
+
params.set("src", stats.source);
|
|
411
|
+
}
|
|
412
|
+
const formatDateCompact = (d) => d.replace(/-/g, "");
|
|
413
|
+
const startMs = new Date(stats.dateRange.start).getTime();
|
|
414
|
+
const endMs = new Date(stats.dateRange.end).getTime();
|
|
415
|
+
const daySpan = Math.ceil((endMs - startMs) / (1e3 * 60 * 60 * 24));
|
|
416
|
+
const useMonthly = stats.aggregation === "daily" && daySpan > 31;
|
|
417
|
+
const aggMap = { daily: "d", monthly: "m", model: "mo", total: "t" };
|
|
418
|
+
const effectiveAgg = useMonthly ? "monthly" : stats.aggregation;
|
|
419
|
+
params.set("agg", aggMap[effectiveAgg] || "d");
|
|
420
|
+
let rowsToEncode;
|
|
421
|
+
if (useMonthly) {
|
|
422
|
+
rowsToEncode = aggregateRowsToMonthly(stats.rows);
|
|
423
|
+
} else {
|
|
424
|
+
rowsToEncode = stats.rows.slice(-31);
|
|
425
|
+
}
|
|
426
|
+
const startDate = useMonthly ? stats.dateRange.start : rowsToEncode[0]?.key || stats.dateRange.start;
|
|
427
|
+
const endDate = useMonthly ? stats.dateRange.end : rowsToEncode[rowsToEncode.length - 1]?.key || stats.dateRange.end;
|
|
428
|
+
params.set("dr", `${formatDateCompact(startDate)}-${formatDateCompact(endDate)}`);
|
|
429
|
+
const rows = rowsToEncode.map((row) => {
|
|
430
|
+
let key = row.key;
|
|
431
|
+
if (effectiveAgg === "daily" && row.key.length === 10) {
|
|
432
|
+
key = row.key.slice(5).replace("-", "");
|
|
433
|
+
}
|
|
434
|
+
return [
|
|
435
|
+
key,
|
|
436
|
+
formatCompactNumber(row.inputTokens),
|
|
437
|
+
formatCompactNumber(row.outputTokens),
|
|
438
|
+
formatCompactNumber(row.cacheWriteTokens),
|
|
439
|
+
formatCompactNumber(row.cacheReadTokens),
|
|
440
|
+
formatCompactNumber(row.totalTokens),
|
|
441
|
+
row.cost.toFixed(2)
|
|
442
|
+
].join(":");
|
|
443
|
+
});
|
|
444
|
+
params.set("rows", rows.join("|"));
|
|
445
|
+
const t = stats.totals;
|
|
446
|
+
params.set("tot", [
|
|
447
|
+
formatCompactNumber(t.inputTokens),
|
|
448
|
+
formatCompactNumber(t.outputTokens),
|
|
449
|
+
formatCompactNumber(t.cacheWriteTokens),
|
|
450
|
+
formatCompactNumber(t.cacheReadTokens),
|
|
451
|
+
formatCompactNumber(t.totalTokens),
|
|
452
|
+
t.cost.toFixed(2)
|
|
453
|
+
].join(":"));
|
|
454
|
+
if (stats.modelBreakdown.length > 0) {
|
|
455
|
+
const mb = stats.modelBreakdown.slice(0, 5).map((m) => {
|
|
456
|
+
const abbr = getModelTokenFromDisplayName(m.model);
|
|
457
|
+
return `${abbr}:${m.percentage}:${m.cost.toFixed(2)}`;
|
|
458
|
+
});
|
|
459
|
+
params.set("mb", mb.join(","));
|
|
460
|
+
}
|
|
461
|
+
return `${baseUrl}?${params.toString()}`;
|
|
462
|
+
}
|
|
463
|
+
function encodePayload(payload) {
|
|
464
|
+
return Buffer.from(JSON.stringify(payload), "utf-8").toString("base64url");
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// src/activity.ts
|
|
468
|
+
var WEEKDAY_LABELS = ["Mon", "", "", "", "", "", "Sun"];
|
|
469
|
+
var MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
470
|
+
function toDateKey(date) {
|
|
471
|
+
const year = date.getFullYear();
|
|
472
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
473
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
474
|
+
return `${year}-${month}-${day}`;
|
|
475
|
+
}
|
|
476
|
+
function startOfDay(date) {
|
|
477
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 12, 0, 0, 0);
|
|
478
|
+
}
|
|
479
|
+
function addDays(date, days) {
|
|
480
|
+
return new Date(date.getTime() + days * MS_PER_DAY);
|
|
481
|
+
}
|
|
482
|
+
function startOfWeekMonday(date) {
|
|
483
|
+
const current = startOfDay(date);
|
|
484
|
+
const day = current.getDay();
|
|
485
|
+
const delta = day === 0 ? -6 : 1 - day;
|
|
486
|
+
return addDays(current, delta);
|
|
487
|
+
}
|
|
488
|
+
function endOfWeekSunday(date) {
|
|
489
|
+
const monday = startOfWeekMonday(date);
|
|
490
|
+
return addDays(monday, 6);
|
|
491
|
+
}
|
|
492
|
+
function quantile(sortedValues, ratio) {
|
|
493
|
+
if (sortedValues.length === 0) return 0;
|
|
494
|
+
const index = Math.min(
|
|
495
|
+
sortedValues.length - 1,
|
|
496
|
+
Math.max(0, Math.floor((sortedValues.length - 1) * ratio))
|
|
497
|
+
);
|
|
498
|
+
return sortedValues[index] ?? 0;
|
|
499
|
+
}
|
|
500
|
+
function getMetricValue(day, metric) {
|
|
501
|
+
switch (metric) {
|
|
502
|
+
case "messages":
|
|
503
|
+
return day.messageCount;
|
|
504
|
+
case "sessions":
|
|
505
|
+
return day.sessionCount;
|
|
506
|
+
case "tokens":
|
|
507
|
+
default:
|
|
508
|
+
return day.totalTokens;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
function buildLegend(thresholds) {
|
|
512
|
+
return [
|
|
513
|
+
{ intensity: 0, min: 0, max: 0, label: "No activity" },
|
|
514
|
+
{ intensity: 1, min: 1, max: thresholds[0], label: "Low" },
|
|
515
|
+
{ intensity: 2, min: thresholds[0] + 1, max: thresholds[1], label: "Steady" },
|
|
516
|
+
{ intensity: 3, min: thresholds[1] + 1, max: thresholds[2], label: "Strong" },
|
|
517
|
+
{ intensity: 4, min: thresholds[2] + 1, max: Number.MAX_SAFE_INTEGER, label: "Peak" }
|
|
518
|
+
];
|
|
519
|
+
}
|
|
520
|
+
function getIntensity(value, thresholds) {
|
|
521
|
+
if (value <= 0) return 0;
|
|
522
|
+
if (value <= thresholds[0]) return 1;
|
|
523
|
+
if (value <= thresholds[1]) return 2;
|
|
524
|
+
if (value <= thresholds[2]) return 3;
|
|
525
|
+
return 4;
|
|
526
|
+
}
|
|
527
|
+
function computeStreaks(days) {
|
|
528
|
+
let current = 0;
|
|
529
|
+
let longest = 0;
|
|
530
|
+
let streak = 0;
|
|
531
|
+
let activeDays = 0;
|
|
532
|
+
for (const day of days) {
|
|
533
|
+
if (day.value > 0) {
|
|
534
|
+
streak += 1;
|
|
535
|
+
activeDays += 1;
|
|
536
|
+
longest = Math.max(longest, streak);
|
|
537
|
+
} else {
|
|
538
|
+
streak = 0;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
for (let index = days.length - 1; index >= 0; index -= 1) {
|
|
542
|
+
if ((days[index]?.value ?? 0) > 0) {
|
|
543
|
+
current += 1;
|
|
544
|
+
} else if (current > 0) {
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return { current, longest, activeDays };
|
|
549
|
+
}
|
|
550
|
+
function buildActivityGraph(stats, metric, requestedDays = 365) {
|
|
551
|
+
const today = startOfDay(/* @__PURE__ */ new Date());
|
|
552
|
+
const endDate = stats.dateRange.end ? startOfDay(new Date(stats.dateRange.end)) : today;
|
|
553
|
+
const effectiveEnd = endDate > today ? today : endDate;
|
|
554
|
+
const effectiveStart = addDays(effectiveEnd, -(requestedDays - 1));
|
|
555
|
+
const paddedStart = startOfWeekMonday(effectiveStart);
|
|
556
|
+
const paddedEnd = endOfWeekSunday(effectiveEnd);
|
|
557
|
+
const dayMap = new Map(stats.days.map((day) => [day.date, day]));
|
|
558
|
+
const orderedDays = [];
|
|
559
|
+
const positiveValues = [];
|
|
560
|
+
for (let cursor = new Date(paddedStart); cursor <= paddedEnd; cursor = addDays(cursor, 1)) {
|
|
561
|
+
const key = toDateKey(cursor);
|
|
562
|
+
const sourceDay = dayMap.get(key);
|
|
563
|
+
const value = sourceDay ? getMetricValue(sourceDay, metric) : 0;
|
|
564
|
+
if (cursor >= effectiveStart && cursor <= effectiveEnd && value > 0) {
|
|
565
|
+
positiveValues.push(value);
|
|
566
|
+
}
|
|
567
|
+
orderedDays.push({
|
|
568
|
+
date: key,
|
|
569
|
+
value,
|
|
570
|
+
intensity: 0,
|
|
571
|
+
inputTokens: sourceDay?.inputTokens ?? 0,
|
|
572
|
+
outputTokens: sourceDay?.outputTokens ?? 0,
|
|
573
|
+
cacheWriteTokens: sourceDay?.cacheWriteTokens ?? 0,
|
|
574
|
+
cacheReadTokens: sourceDay?.cacheReadTokens ?? 0,
|
|
575
|
+
totalTokens: sourceDay?.totalTokens ?? 0,
|
|
576
|
+
sessionCount: sourceDay?.sessionCount ?? 0,
|
|
577
|
+
messageCount: sourceDay?.messageCount ?? 0
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
positiveValues.sort((a, b) => a - b);
|
|
581
|
+
const thresholds = [
|
|
582
|
+
Math.max(1, quantile(positiveValues, 0.25)),
|
|
583
|
+
Math.max(1, quantile(positiveValues, 0.5)),
|
|
584
|
+
Math.max(1, quantile(positiveValues, 0.75))
|
|
585
|
+
];
|
|
586
|
+
for (const day of orderedDays) {
|
|
587
|
+
day.intensity = getIntensity(day.value, thresholds);
|
|
588
|
+
}
|
|
589
|
+
const weeks = [];
|
|
590
|
+
for (let index = 0; index < orderedDays.length; index += 7) {
|
|
591
|
+
weeks.push(orderedDays.slice(index, index + 7));
|
|
592
|
+
}
|
|
593
|
+
const monthLabels = [];
|
|
594
|
+
const seenMonths = /* @__PURE__ */ new Set();
|
|
595
|
+
for (let weekIndex = 0; weekIndex < weeks.length; weekIndex += 1) {
|
|
596
|
+
const week = weeks[weekIndex];
|
|
597
|
+
const firstRealDay = week?.find((day) => day.date >= toDateKey(effectiveStart) && day.date <= toDateKey(effectiveEnd));
|
|
598
|
+
if (!firstRealDay) continue;
|
|
599
|
+
const monthKey = firstRealDay.date.slice(0, 7);
|
|
600
|
+
if (seenMonths.has(monthKey)) continue;
|
|
601
|
+
seenMonths.add(monthKey);
|
|
602
|
+
const monthDate = /* @__PURE__ */ new Date(`${monthKey}-01T12:00:00`);
|
|
603
|
+
monthLabels.push({
|
|
604
|
+
weekIndex,
|
|
605
|
+
label: monthDate.toLocaleString("en-US", { month: "short" })
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
const visibleDays = orderedDays.filter((day) => day.date >= toDateKey(effectiveStart) && day.date <= toDateKey(effectiveEnd));
|
|
609
|
+
const streaks = computeStreaks(visibleDays);
|
|
610
|
+
const recent30Total = visibleDays.slice(-30).reduce((sum, day) => sum + day.value, 0);
|
|
611
|
+
const favoriteModel = stats.modelBreakdown[0];
|
|
612
|
+
return {
|
|
613
|
+
metric,
|
|
614
|
+
startDate: toDateKey(effectiveStart),
|
|
615
|
+
endDate: toDateKey(effectiveEnd),
|
|
616
|
+
weeks,
|
|
617
|
+
monthLabels,
|
|
618
|
+
weekdayLabels: WEEKDAY_LABELS,
|
|
619
|
+
legend: buildLegend(thresholds),
|
|
620
|
+
summary: {
|
|
621
|
+
activeDays: streaks.activeDays,
|
|
622
|
+
currentStreak: streaks.current,
|
|
623
|
+
longestStreak: streaks.longest,
|
|
624
|
+
recent30DayTotal: recent30Total,
|
|
625
|
+
favoriteModel: favoriteModel?.model || "Unknown",
|
|
626
|
+
favoriteModelTokens: favoriteModel?.tokens || 0,
|
|
627
|
+
inputTokens: stats.totals.inputTokens,
|
|
628
|
+
outputTokens: stats.totals.outputTokens,
|
|
629
|
+
cacheWriteTokens: stats.totals.cacheWriteTokens,
|
|
630
|
+
cacheReadTokens: stats.totals.cacheReadTokens,
|
|
631
|
+
totalTokens: stats.totals.totalTokens,
|
|
632
|
+
totalMessages: stats.totals.messageCount,
|
|
633
|
+
totalSessions: stats.totals.sessionCount
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function buildActivityTitle(source, metric) {
|
|
638
|
+
const sourceLabel = source === "codex" ? "Codex" : source === "combined" ? "AI Coding" : "Claude";
|
|
639
|
+
const metricLabel = metric === "tokens" ? "Activity" : metric === "sessions" ? "Session Activity" : "Message Activity";
|
|
640
|
+
return `${sourceLabel} ${metricLabel}`;
|
|
641
|
+
}
|
|
642
|
+
function buildActivityArtifactPayload(stats, metric, requestedDays = 365) {
|
|
643
|
+
return {
|
|
644
|
+
title: buildActivityTitle(stats.source, metric),
|
|
645
|
+
source: stats.source,
|
|
646
|
+
activity: buildActivityGraph(stats, metric, requestedDays)
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
function buildActivityArtifact(stats, metric, requestedDays = 365) {
|
|
650
|
+
return {
|
|
651
|
+
type: "activity",
|
|
652
|
+
schemaVersion: 1,
|
|
653
|
+
source: stats.source,
|
|
654
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
655
|
+
renderOptions: {
|
|
656
|
+
canonicalPath: "activity",
|
|
657
|
+
theme: "light"
|
|
658
|
+
},
|
|
659
|
+
payload: buildActivityArtifactPayload(stats, metric, requestedDays)
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// src/usage/loader.ts
|
|
664
|
+
import { promises as fs } from "fs";
|
|
665
|
+
import { homedir } from "os";
|
|
666
|
+
import { join, basename } from "path";
|
|
667
|
+
|
|
668
|
+
// src/pricing.ts
|
|
669
|
+
var MODEL_PRICING = {
|
|
670
|
+
// Opus 4.6 (same pricing as Opus 4.5)
|
|
671
|
+
"claude-opus-4-6-20260101": {
|
|
672
|
+
input: 5,
|
|
673
|
+
output: 25,
|
|
674
|
+
cacheWrite: 6.25,
|
|
675
|
+
cacheRead: 0.5
|
|
676
|
+
},
|
|
677
|
+
// Opus 4.5 (cheaper than Opus 4.1)
|
|
678
|
+
"claude-opus-4-5-20251101": {
|
|
679
|
+
input: 5,
|
|
680
|
+
output: 25,
|
|
681
|
+
cacheWrite: 6.25,
|
|
682
|
+
cacheRead: 0.5
|
|
683
|
+
},
|
|
684
|
+
// Sonnet 4.6 (same pricing as Sonnet 4.5)
|
|
685
|
+
"claude-sonnet-4-6-20260101": {
|
|
686
|
+
input: 3,
|
|
687
|
+
output: 15,
|
|
688
|
+
cacheWrite: 3.75,
|
|
689
|
+
cacheRead: 0.3
|
|
690
|
+
},
|
|
691
|
+
// Sonnet 4.5
|
|
692
|
+
"claude-sonnet-4-5-20250929": {
|
|
693
|
+
input: 3,
|
|
694
|
+
output: 15,
|
|
695
|
+
cacheWrite: 3.75,
|
|
696
|
+
cacheRead: 0.3
|
|
697
|
+
},
|
|
698
|
+
// Opus 4.1 (MORE expensive than Opus 4.5)
|
|
699
|
+
"claude-opus-4-1-20250805": {
|
|
700
|
+
input: 15,
|
|
701
|
+
output: 75,
|
|
702
|
+
cacheWrite: 18.75,
|
|
703
|
+
cacheRead: 1.5
|
|
704
|
+
},
|
|
705
|
+
// Haiku 4.5
|
|
706
|
+
"claude-haiku-4-5-20251001": {
|
|
707
|
+
input: 1,
|
|
708
|
+
output: 5,
|
|
709
|
+
cacheWrite: 1.25,
|
|
710
|
+
cacheRead: 0.1
|
|
711
|
+
},
|
|
712
|
+
// Sonnet 3.5 (legacy - same as Sonnet 4.5)
|
|
713
|
+
"claude-3-5-sonnet-20241022": {
|
|
714
|
+
input: 3,
|
|
715
|
+
output: 15,
|
|
716
|
+
cacheWrite: 3.75,
|
|
717
|
+
cacheRead: 0.3
|
|
718
|
+
},
|
|
719
|
+
"claude-3-5-sonnet-20240620": {
|
|
720
|
+
input: 3,
|
|
721
|
+
output: 15,
|
|
722
|
+
cacheWrite: 3.75,
|
|
723
|
+
cacheRead: 0.3
|
|
724
|
+
},
|
|
725
|
+
// Haiku 3.5 (legacy - same as Haiku 4.5)
|
|
726
|
+
"claude-3-5-haiku-20241022": {
|
|
727
|
+
input: 1,
|
|
728
|
+
output: 5,
|
|
729
|
+
cacheWrite: 1.25,
|
|
730
|
+
cacheRead: 0.1
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
function getModelPricing(modelName) {
|
|
734
|
+
if (MODEL_PRICING[modelName]) {
|
|
735
|
+
return MODEL_PRICING[modelName];
|
|
736
|
+
}
|
|
737
|
+
if (modelName.includes("opus-4-6") || modelName.includes("opus-4.6")) {
|
|
738
|
+
return MODEL_PRICING["claude-opus-4-6-20260101"];
|
|
739
|
+
}
|
|
740
|
+
if (modelName.includes("opus-4-5") || modelName.includes("opus-4.5")) {
|
|
741
|
+
return MODEL_PRICING["claude-opus-4-5-20251101"];
|
|
742
|
+
}
|
|
743
|
+
if (modelName.includes("opus-4-1") || modelName.includes("opus-4.1") || modelName.includes("opus-4")) {
|
|
744
|
+
return MODEL_PRICING["claude-opus-4-1-20250805"];
|
|
745
|
+
}
|
|
746
|
+
if (modelName.includes("sonnet-4-6") || modelName.includes("sonnet-4.6")) {
|
|
747
|
+
return MODEL_PRICING["claude-sonnet-4-6-20260101"];
|
|
748
|
+
}
|
|
749
|
+
if (modelName.includes("sonnet-4-5") || modelName.includes("sonnet-4.5")) {
|
|
750
|
+
return MODEL_PRICING["claude-sonnet-4-5-20250929"];
|
|
751
|
+
}
|
|
752
|
+
if (modelName.includes("sonnet")) {
|
|
753
|
+
return MODEL_PRICING["claude-3-5-sonnet-20241022"];
|
|
754
|
+
}
|
|
755
|
+
if (modelName.includes("haiku-4-5") || modelName.includes("haiku-4.5")) {
|
|
756
|
+
return MODEL_PRICING["claude-haiku-4-5-20251001"];
|
|
757
|
+
}
|
|
758
|
+
if (modelName.includes("haiku")) {
|
|
759
|
+
return MODEL_PRICING["claude-3-5-haiku-20241022"];
|
|
760
|
+
}
|
|
761
|
+
return MODEL_PRICING["claude-sonnet-4-5-20250929"];
|
|
762
|
+
}
|
|
763
|
+
function getModelDisplayName(modelName) {
|
|
764
|
+
if (modelName.includes("opus-4-6") || modelName.includes("opus-4.6")) return "Opus 4.6";
|
|
765
|
+
if (modelName.includes("opus-4-5") || modelName.includes("opus-4.5")) return "Opus 4.5";
|
|
766
|
+
if (modelName.includes("opus-4-1") || modelName.includes("opus-4.1")) return "Opus 4.1";
|
|
767
|
+
if (modelName.includes("opus")) return "Opus";
|
|
768
|
+
if (modelName.includes("sonnet-4-6") || modelName.includes("sonnet-4.6")) return "Sonnet 4.6";
|
|
769
|
+
if (modelName.includes("sonnet-4-5") || modelName.includes("sonnet-4.5")) return "Sonnet 4.5";
|
|
770
|
+
if (modelName.includes("sonnet-3-5") || modelName.includes("sonnet-3.5")) return "Sonnet 3.5";
|
|
771
|
+
if (modelName.includes("sonnet")) return "Sonnet";
|
|
772
|
+
if (modelName.includes("haiku-4-5") || modelName.includes("haiku-4.5")) return "Haiku 4.5";
|
|
773
|
+
if (modelName.includes("haiku-3-5") || modelName.includes("haiku-3.5")) return "Haiku 3.5";
|
|
774
|
+
if (modelName.includes("haiku")) return "Haiku";
|
|
775
|
+
return modelName;
|
|
776
|
+
}
|
|
777
|
+
|
|
398
778
|
// src/usage/loader.ts
|
|
399
779
|
var MAX_RECURSION_DEPTH = 10;
|
|
400
780
|
function toLocalDateString(isoTimestamp) {
|
|
@@ -515,6 +895,7 @@ async function parseClaudeJsonl(projectFilter) {
|
|
|
515
895
|
cacheWriteTokens,
|
|
516
896
|
cacheReadTokens,
|
|
517
897
|
cost,
|
|
898
|
+
messageCount: 1,
|
|
518
899
|
source: "claude",
|
|
519
900
|
sessionId,
|
|
520
901
|
timestamp
|
|
@@ -571,7 +952,10 @@ async function parseCodexJsonl() {
|
|
|
571
952
|
cacheWriteTokens: 0,
|
|
572
953
|
cacheReadTokens: cachedInputTokens,
|
|
573
954
|
cost,
|
|
574
|
-
|
|
955
|
+
messageCount: 1,
|
|
956
|
+
source: "codex",
|
|
957
|
+
sessionId: basename(filePath, ".jsonl"),
|
|
958
|
+
timestamp
|
|
575
959
|
});
|
|
576
960
|
}
|
|
577
961
|
} catch {
|
|
@@ -772,6 +1156,76 @@ function computeModelBreakdown(entries) {
|
|
|
772
1156
|
percentage: totalTokens > 0 ? Math.round(data.tokens / totalTokens * 100) : 0
|
|
773
1157
|
})).sort((a, b) => b.tokens - a.tokens);
|
|
774
1158
|
}
|
|
1159
|
+
function computeActivityStats(entries, source) {
|
|
1160
|
+
const dayMap = /* @__PURE__ */ new Map();
|
|
1161
|
+
for (const entry of entries) {
|
|
1162
|
+
const existing = dayMap.get(entry.date);
|
|
1163
|
+
if (existing) {
|
|
1164
|
+
existing.inputTokens += entry.inputTokens;
|
|
1165
|
+
existing.outputTokens += entry.outputTokens;
|
|
1166
|
+
existing.cacheWriteTokens += entry.cacheWriteTokens;
|
|
1167
|
+
existing.cacheReadTokens += entry.cacheReadTokens;
|
|
1168
|
+
existing.totalTokens += entry.inputTokens + entry.outputTokens + entry.cacheWriteTokens + entry.cacheReadTokens;
|
|
1169
|
+
existing.messageCount += entry.messageCount;
|
|
1170
|
+
if (entry.sessionId) {
|
|
1171
|
+
existing.sessionCount += 0;
|
|
1172
|
+
}
|
|
1173
|
+
} else {
|
|
1174
|
+
dayMap.set(entry.date, {
|
|
1175
|
+
date: entry.date,
|
|
1176
|
+
inputTokens: entry.inputTokens,
|
|
1177
|
+
outputTokens: entry.outputTokens,
|
|
1178
|
+
cacheWriteTokens: entry.cacheWriteTokens,
|
|
1179
|
+
cacheReadTokens: entry.cacheReadTokens,
|
|
1180
|
+
totalTokens: entry.inputTokens + entry.outputTokens + entry.cacheWriteTokens + entry.cacheReadTokens,
|
|
1181
|
+
sessionCount: 0,
|
|
1182
|
+
messageCount: entry.messageCount
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
const sessionMap = /* @__PURE__ */ new Map();
|
|
1187
|
+
for (const entry of entries) {
|
|
1188
|
+
if (!entry.sessionId) continue;
|
|
1189
|
+
const sessions = sessionMap.get(entry.date) || /* @__PURE__ */ new Set();
|
|
1190
|
+
sessions.add(entry.sessionId);
|
|
1191
|
+
sessionMap.set(entry.date, sessions);
|
|
1192
|
+
}
|
|
1193
|
+
const days = Array.from(dayMap.values()).map((day) => ({
|
|
1194
|
+
...day,
|
|
1195
|
+
sessionCount: sessionMap.get(day.date)?.size || 0
|
|
1196
|
+
})).sort((a, b) => a.date.localeCompare(b.date));
|
|
1197
|
+
const totals = days.reduce(
|
|
1198
|
+
(acc, day) => ({
|
|
1199
|
+
inputTokens: acc.inputTokens + day.inputTokens,
|
|
1200
|
+
outputTokens: acc.outputTokens + day.outputTokens,
|
|
1201
|
+
cacheWriteTokens: acc.cacheWriteTokens + day.cacheWriteTokens,
|
|
1202
|
+
cacheReadTokens: acc.cacheReadTokens + day.cacheReadTokens,
|
|
1203
|
+
totalTokens: acc.totalTokens + day.totalTokens,
|
|
1204
|
+
sessionCount: acc.sessionCount + day.sessionCount,
|
|
1205
|
+
messageCount: acc.messageCount + day.messageCount
|
|
1206
|
+
}),
|
|
1207
|
+
{
|
|
1208
|
+
inputTokens: 0,
|
|
1209
|
+
outputTokens: 0,
|
|
1210
|
+
cacheWriteTokens: 0,
|
|
1211
|
+
cacheReadTokens: 0,
|
|
1212
|
+
totalTokens: 0,
|
|
1213
|
+
sessionCount: 0,
|
|
1214
|
+
messageCount: 0
|
|
1215
|
+
}
|
|
1216
|
+
);
|
|
1217
|
+
const dates = days.map((entry) => entry.date);
|
|
1218
|
+
return {
|
|
1219
|
+
source,
|
|
1220
|
+
dateRange: {
|
|
1221
|
+
start: dates[0] || "",
|
|
1222
|
+
end: dates[dates.length - 1] || ""
|
|
1223
|
+
},
|
|
1224
|
+
days,
|
|
1225
|
+
totals,
|
|
1226
|
+
modelBreakdown: computeModelBreakdown(entries)
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
775
1229
|
async function loadUsageStats(options) {
|
|
776
1230
|
const { aggregation, since, until, codexOnly, combined, projectFilter } = options;
|
|
777
1231
|
let entries = [];
|
|
@@ -813,19 +1267,41 @@ async function loadUsageStats(options) {
|
|
|
813
1267
|
rows = aggregateByDay(entries);
|
|
814
1268
|
break;
|
|
815
1269
|
}
|
|
816
|
-
const totals = aggregation === "total" ? computeTotals(aggregateByDay(entries)) : computeTotals(rows);
|
|
817
|
-
const modelBreakdown = computeModelBreakdown(entries);
|
|
1270
|
+
const totals = aggregation === "total" ? computeTotals(aggregateByDay(entries)) : computeTotals(rows);
|
|
1271
|
+
const modelBreakdown = computeModelBreakdown(entries);
|
|
1272
|
+
let source = "claude";
|
|
1273
|
+
if (codexOnly) source = "codex";
|
|
1274
|
+
else if (combined) source = "combined";
|
|
1275
|
+
return {
|
|
1276
|
+
rows,
|
|
1277
|
+
totals,
|
|
1278
|
+
source,
|
|
1279
|
+
aggregation,
|
|
1280
|
+
dateRange,
|
|
1281
|
+
modelBreakdown
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
async function loadActivityStats(options) {
|
|
1285
|
+
const { since, until, codexOnly, combined, projectFilter } = options;
|
|
1286
|
+
let entries = [];
|
|
1287
|
+
if (!codexOnly) {
|
|
1288
|
+
entries = entries.concat(await parseClaudeJsonl(projectFilter));
|
|
1289
|
+
}
|
|
1290
|
+
if (codexOnly || combined) {
|
|
1291
|
+
entries = entries.concat(await parseCodexJsonl());
|
|
1292
|
+
}
|
|
1293
|
+
if (entries.length === 0) {
|
|
1294
|
+
return null;
|
|
1295
|
+
}
|
|
1296
|
+
entries = filterByDateRange(entries, since, until);
|
|
1297
|
+
entries = entries.filter((entry) => !entry.model.toLowerCase().includes("synthetic"));
|
|
1298
|
+
if (entries.length === 0) {
|
|
1299
|
+
return null;
|
|
1300
|
+
}
|
|
818
1301
|
let source = "claude";
|
|
819
1302
|
if (codexOnly) source = "codex";
|
|
820
1303
|
else if (combined) source = "combined";
|
|
821
|
-
return
|
|
822
|
-
rows,
|
|
823
|
-
totals,
|
|
824
|
-
source,
|
|
825
|
-
aggregation,
|
|
826
|
-
dateRange,
|
|
827
|
-
modelBreakdown
|
|
828
|
-
};
|
|
1304
|
+
return computeActivityStats(entries, source);
|
|
829
1305
|
}
|
|
830
1306
|
|
|
831
1307
|
// src/usage/table.ts
|
|
@@ -1698,176 +2174,6 @@ function combineWrappedStats(claude, codex) {
|
|
|
1698
2174
|
};
|
|
1699
2175
|
}
|
|
1700
2176
|
|
|
1701
|
-
// src/url-encoder.ts
|
|
1702
|
-
function formatCompactNumber(num) {
|
|
1703
|
-
if (num >= 1e9) {
|
|
1704
|
-
return `${(num / 1e9).toFixed(1)}B`;
|
|
1705
|
-
}
|
|
1706
|
-
if (num >= 1e6) {
|
|
1707
|
-
return `${(num / 1e6).toFixed(1)}M`;
|
|
1708
|
-
}
|
|
1709
|
-
if (num >= 1e3) {
|
|
1710
|
-
return `${(num / 1e3).toFixed(1)}K`;
|
|
1711
|
-
}
|
|
1712
|
-
return num.toString();
|
|
1713
|
-
}
|
|
1714
|
-
var dayToNumber = {
|
|
1715
|
-
Sunday: 0,
|
|
1716
|
-
Monday: 1,
|
|
1717
|
-
Tuesday: 2,
|
|
1718
|
-
Wednesday: 3,
|
|
1719
|
-
Thursday: 4,
|
|
1720
|
-
Friday: 5,
|
|
1721
|
-
Saturday: 6
|
|
1722
|
-
};
|
|
1723
|
-
function encodeStatsToUrl(stats, baseUrl = "https://vibestats.wolfai.dev") {
|
|
1724
|
-
const params = new URLSearchParams();
|
|
1725
|
-
params.set("s", stats.sessions.toString());
|
|
1726
|
-
params.set("t", formatCompactNumber(stats.totalTokens));
|
|
1727
|
-
params.set("c", stats.totalCost.toFixed(2));
|
|
1728
|
-
params.set("d", stats.daysActive.toString());
|
|
1729
|
-
params.set("ls", stats.longestStreak.toString());
|
|
1730
|
-
params.set("cs", stats.currentStreak.toString());
|
|
1731
|
-
params.set("ph", stats.peakHour.toString());
|
|
1732
|
-
params.set("pd", (dayToNumber[stats.peakDay] ?? 0).toString());
|
|
1733
|
-
params.set("fm", getModelTokenFromDisplayName(stats.favoriteModel));
|
|
1734
|
-
const mbParts = stats.modelBreakdown.slice(0, 3).map((m) => {
|
|
1735
|
-
const abbr = getModelTokenFromDisplayName(m.model);
|
|
1736
|
-
return `${abbr}:${m.percentage}`;
|
|
1737
|
-
});
|
|
1738
|
-
params.set("mb", mbParts.join(","));
|
|
1739
|
-
if (stats.topTools && stats.topTools.length > 0) {
|
|
1740
|
-
params.set("tt", stats.topTools.slice(0, 5).join(","));
|
|
1741
|
-
}
|
|
1742
|
-
if (stats.developerStyle) {
|
|
1743
|
-
const styleMap = {
|
|
1744
|
-
reader: "r",
|
|
1745
|
-
writer: "w",
|
|
1746
|
-
executor: "e",
|
|
1747
|
-
balanced: "b"
|
|
1748
|
-
};
|
|
1749
|
-
params.set("st", styleMap[stats.developerStyle] || "b");
|
|
1750
|
-
}
|
|
1751
|
-
if (stats.topProject) {
|
|
1752
|
-
params.set("tp", stats.topProject);
|
|
1753
|
-
}
|
|
1754
|
-
if (stats.projectCount) {
|
|
1755
|
-
params.set("pc", stats.projectCount.toString());
|
|
1756
|
-
}
|
|
1757
|
-
params.set("wg", formatCompactNumber(stats.wordsGenerated));
|
|
1758
|
-
const firstDate = new Date(stats.firstSessionDate);
|
|
1759
|
-
params.set("fad", firstDate.toISOString().split("T")[0].replace(/-/g, ""));
|
|
1760
|
-
if (stats.source && stats.source !== "claude") {
|
|
1761
|
-
params.set("src", stats.source);
|
|
1762
|
-
}
|
|
1763
|
-
return `${baseUrl}/wrapped/?${params.toString()}`;
|
|
1764
|
-
}
|
|
1765
|
-
var CLAUDE_DISPLAY_TO_TOKEN = {
|
|
1766
|
-
"Opus 4.6": "o46",
|
|
1767
|
-
"Opus 4.5": "o45",
|
|
1768
|
-
"Opus 4.1": "o41",
|
|
1769
|
-
Opus: "opus",
|
|
1770
|
-
"Sonnet 4.6": "s46",
|
|
1771
|
-
"Sonnet 4.5": "s45",
|
|
1772
|
-
"Sonnet 3.5": "s35",
|
|
1773
|
-
Sonnet: "sonnet",
|
|
1774
|
-
"Haiku 4.5": "h45",
|
|
1775
|
-
"Haiku 3.5": "h35",
|
|
1776
|
-
Haiku: "haiku"
|
|
1777
|
-
};
|
|
1778
|
-
function getModelTokenFromDisplayName(displayName) {
|
|
1779
|
-
if (CLAUDE_DISPLAY_TO_TOKEN[displayName]) {
|
|
1780
|
-
return CLAUDE_DISPLAY_TO_TOKEN[displayName];
|
|
1781
|
-
}
|
|
1782
|
-
const normalized = displayName.toLowerCase();
|
|
1783
|
-
if (normalized.includes("opus") && normalized.includes("4.6")) return "o46";
|
|
1784
|
-
if (normalized.includes("opus") && normalized.includes("4.5")) return "o45";
|
|
1785
|
-
if (normalized.includes("opus") && normalized.includes("4.1")) return "o41";
|
|
1786
|
-
if (normalized.includes("opus")) return "opus";
|
|
1787
|
-
if (normalized.includes("sonnet") && normalized.includes("4.6")) return "s46";
|
|
1788
|
-
if (normalized.includes("sonnet") && normalized.includes("4.5")) return "s45";
|
|
1789
|
-
if (normalized.includes("sonnet") && normalized.includes("3.5")) return "s35";
|
|
1790
|
-
if (normalized.includes("sonnet")) return "sonnet";
|
|
1791
|
-
if (normalized.includes("haiku") && normalized.includes("4.5")) return "h45";
|
|
1792
|
-
if (normalized.includes("haiku") && normalized.includes("3.5")) return "h35";
|
|
1793
|
-
if (normalized.includes("haiku")) return "haiku";
|
|
1794
|
-
return getCodexModelAbbreviation(displayName);
|
|
1795
|
-
}
|
|
1796
|
-
function aggregateRowsToMonthly(rows) {
|
|
1797
|
-
const monthMap = /* @__PURE__ */ new Map();
|
|
1798
|
-
for (const row of rows) {
|
|
1799
|
-
const month = row.key.slice(0, 7);
|
|
1800
|
-
const existing = monthMap.get(month);
|
|
1801
|
-
if (existing) {
|
|
1802
|
-
existing.inputTokens += row.inputTokens;
|
|
1803
|
-
existing.outputTokens += row.outputTokens;
|
|
1804
|
-
existing.cacheWriteTokens += row.cacheWriteTokens;
|
|
1805
|
-
existing.cacheReadTokens += row.cacheReadTokens;
|
|
1806
|
-
existing.totalTokens += row.totalTokens;
|
|
1807
|
-
existing.cost += row.cost;
|
|
1808
|
-
} else {
|
|
1809
|
-
monthMap.set(month, { ...row, key: month });
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
return Array.from(monthMap.values()).sort((a, b) => a.key.localeCompare(b.key));
|
|
1813
|
-
}
|
|
1814
|
-
function encodeUsageToUrl(stats, baseUrl = "https://vibestats.wolfai.dev") {
|
|
1815
|
-
const params = new URLSearchParams();
|
|
1816
|
-
if (stats.source !== "claude") {
|
|
1817
|
-
params.set("src", stats.source);
|
|
1818
|
-
}
|
|
1819
|
-
const formatDateCompact = (d) => d.replace(/-/g, "");
|
|
1820
|
-
const startMs = new Date(stats.dateRange.start).getTime();
|
|
1821
|
-
const endMs = new Date(stats.dateRange.end).getTime();
|
|
1822
|
-
const daySpan = Math.ceil((endMs - startMs) / (1e3 * 60 * 60 * 24));
|
|
1823
|
-
const useMonthly = stats.aggregation === "daily" && daySpan > 31;
|
|
1824
|
-
const aggMap = { daily: "d", monthly: "m", model: "mo", total: "t" };
|
|
1825
|
-
const effectiveAgg = useMonthly ? "monthly" : stats.aggregation;
|
|
1826
|
-
params.set("agg", aggMap[effectiveAgg] || "d");
|
|
1827
|
-
let rowsToEncode;
|
|
1828
|
-
if (useMonthly) {
|
|
1829
|
-
rowsToEncode = aggregateRowsToMonthly(stats.rows);
|
|
1830
|
-
} else {
|
|
1831
|
-
rowsToEncode = stats.rows.slice(-31);
|
|
1832
|
-
}
|
|
1833
|
-
const startDate = useMonthly ? stats.dateRange.start : rowsToEncode[0]?.key || stats.dateRange.start;
|
|
1834
|
-
const endDate = useMonthly ? stats.dateRange.end : rowsToEncode[rowsToEncode.length - 1]?.key || stats.dateRange.end;
|
|
1835
|
-
params.set("dr", `${formatDateCompact(startDate)}-${formatDateCompact(endDate)}`);
|
|
1836
|
-
const rows = rowsToEncode.map((row) => {
|
|
1837
|
-
let key = row.key;
|
|
1838
|
-
if (effectiveAgg === "daily" && row.key.length === 10) {
|
|
1839
|
-
key = row.key.slice(5).replace("-", "");
|
|
1840
|
-
}
|
|
1841
|
-
return [
|
|
1842
|
-
key,
|
|
1843
|
-
formatCompactNumber(row.inputTokens),
|
|
1844
|
-
formatCompactNumber(row.outputTokens),
|
|
1845
|
-
formatCompactNumber(row.cacheWriteTokens),
|
|
1846
|
-
formatCompactNumber(row.cacheReadTokens),
|
|
1847
|
-
formatCompactNumber(row.totalTokens),
|
|
1848
|
-
row.cost.toFixed(2)
|
|
1849
|
-
].join(":");
|
|
1850
|
-
});
|
|
1851
|
-
params.set("rows", rows.join("|"));
|
|
1852
|
-
const t = stats.totals;
|
|
1853
|
-
params.set("tot", [
|
|
1854
|
-
formatCompactNumber(t.inputTokens),
|
|
1855
|
-
formatCompactNumber(t.outputTokens),
|
|
1856
|
-
formatCompactNumber(t.cacheWriteTokens),
|
|
1857
|
-
formatCompactNumber(t.cacheReadTokens),
|
|
1858
|
-
formatCompactNumber(t.totalTokens),
|
|
1859
|
-
t.cost.toFixed(2)
|
|
1860
|
-
].join(":"));
|
|
1861
|
-
if (stats.modelBreakdown.length > 0) {
|
|
1862
|
-
const mb = stats.modelBreakdown.slice(0, 5).map((m) => {
|
|
1863
|
-
const abbr = getModelTokenFromDisplayName(m.model);
|
|
1864
|
-
return `${abbr}:${m.percentage}:${m.cost.toFixed(2)}`;
|
|
1865
|
-
});
|
|
1866
|
-
params.set("mb", mb.join(","));
|
|
1867
|
-
}
|
|
1868
|
-
return `${baseUrl}?${params.toString()}`;
|
|
1869
|
-
}
|
|
1870
|
-
|
|
1871
2177
|
// src/display.ts
|
|
1872
2178
|
var colors2 = {
|
|
1873
2179
|
reset: "\x1B[0m",
|
|
@@ -1966,27 +2272,85 @@ function formatHour(hour) {
|
|
|
1966
2272
|
if (hour === 12) return "12pm";
|
|
1967
2273
|
return `${hour - 12}pm`;
|
|
1968
2274
|
}
|
|
2275
|
+
function displayActivityStats(artifact, url, options) {
|
|
2276
|
+
const c = getColors2(options?.theme);
|
|
2277
|
+
const summary = artifact.activity.summary;
|
|
2278
|
+
console.log();
|
|
2279
|
+
console.log(`${c.cyan}${c.bold}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557${c.reset}`);
|
|
2280
|
+
console.log(`${c.cyan}${c.bold}\u2551${c.reset} ${c.white}${c.bold}${artifact.title}${c.reset} ${c.cyan}${c.bold}\u2551${c.reset}`);
|
|
2281
|
+
console.log(`${c.cyan}${c.bold}\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D${c.reset}`);
|
|
2282
|
+
console.log();
|
|
2283
|
+
console.log(`${c.bold}\u{1F4C8} Activity Heatmap${c.reset}`);
|
|
2284
|
+
console.log(`${c.gray}${"\u2500".repeat(60)}${c.reset}`);
|
|
2285
|
+
console.log(` Window: ${artifact.activity.startDate} \u2192 ${artifact.activity.endDate}`);
|
|
2286
|
+
console.log(` Metric: ${artifact.activity.metric}`);
|
|
2287
|
+
console.log(` Active days: ${c.cyan}${c.bold}${summary.activeDays}${c.reset}`);
|
|
2288
|
+
console.log();
|
|
2289
|
+
console.log(`${c.bold}\u26A1 Highlights${c.reset}`);
|
|
2290
|
+
console.log(`${c.gray}${"\u2500".repeat(60)}${c.reset}`);
|
|
2291
|
+
console.log(` Current streak: ${c.green}${summary.currentStreak} days${c.reset}`);
|
|
2292
|
+
console.log(` Longest streak: ${c.cyan}${summary.longestStreak} days${c.reset}`);
|
|
2293
|
+
console.log(` Recent 30 days: ${c.white}${c.bold}${formatCompactNumber(summary.recent30DayTotal)}${c.reset}`);
|
|
2294
|
+
console.log(` Favorite model: ${c.amber}${summary.favoriteModel}${c.reset}`);
|
|
2295
|
+
console.log();
|
|
2296
|
+
console.log(`${c.bold}\u{1F517} Share Activity${c.reset}`);
|
|
2297
|
+
console.log(`${c.gray}${"\u2500".repeat(60)}${c.reset}`);
|
|
2298
|
+
if (options?.shortUrl) {
|
|
2299
|
+
console.log(` ${c.cyan}${c.bold}${options.shortUrl}${c.reset}`);
|
|
2300
|
+
console.log(` ${c.dim}(Canonical URL: ${url})${c.reset}`);
|
|
2301
|
+
} else {
|
|
2302
|
+
console.log(` ${c.cyan}${url}${c.reset}`);
|
|
2303
|
+
}
|
|
2304
|
+
if (options?.imageUrl) {
|
|
2305
|
+
console.log(` Image: ${c.white}${options.imageUrl}${c.reset}`);
|
|
2306
|
+
}
|
|
2307
|
+
console.log();
|
|
2308
|
+
}
|
|
1969
2309
|
|
|
1970
|
-
// src/
|
|
1971
|
-
|
|
1972
|
-
|
|
2310
|
+
// src/share-client.ts
|
|
2311
|
+
var VIBESTATS_PUBLIC_HOST = "vibestats.wolfai.dev";
|
|
2312
|
+
var VIBESTATS_SHARE_API_ORIGIN = "https://api.wolfai.dev";
|
|
2313
|
+
function extractLegacyQuery(legacyUrl) {
|
|
2314
|
+
if (!legacyUrl) return null;
|
|
2315
|
+
const queryIndex = legacyUrl.indexOf("?");
|
|
2316
|
+
if (queryIndex === -1) return null;
|
|
2317
|
+
return legacyUrl.slice(queryIndex + 1) || null;
|
|
2318
|
+
}
|
|
2319
|
+
function resolveShareApiBaseUrl(baseUrl) {
|
|
2320
|
+
try {
|
|
2321
|
+
const parsed = new URL(baseUrl);
|
|
2322
|
+
if (parsed.hostname === VIBESTATS_PUBLIC_HOST) {
|
|
2323
|
+
return VIBESTATS_SHARE_API_ORIGIN;
|
|
2324
|
+
}
|
|
2325
|
+
return parsed.origin;
|
|
2326
|
+
} catch {
|
|
2327
|
+
return baseUrl;
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
async function publishArtifact(artifact, baseUrl, legacyUrl) {
|
|
1973
2331
|
try {
|
|
1974
|
-
const apiUrl = new URL("/
|
|
2332
|
+
const apiUrl = new URL("/vibestats/shares", resolveShareApiBaseUrl(baseUrl));
|
|
1975
2333
|
const response = await fetch(apiUrl.toString(), {
|
|
1976
2334
|
method: "POST",
|
|
1977
2335
|
headers: {
|
|
1978
2336
|
"Content-Type": "application/json"
|
|
1979
2337
|
},
|
|
1980
|
-
body: JSON.stringify({
|
|
2338
|
+
body: JSON.stringify({
|
|
2339
|
+
artifact,
|
|
2340
|
+
legacyQuery: extractLegacyQuery(legacyUrl)
|
|
2341
|
+
})
|
|
1981
2342
|
});
|
|
1982
2343
|
if (!response.ok) {
|
|
1983
2344
|
return null;
|
|
1984
2345
|
}
|
|
1985
2346
|
const data = await response.json();
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
2347
|
+
return {
|
|
2348
|
+
id: data.id || data.slug,
|
|
2349
|
+
slug: data.slug,
|
|
2350
|
+
url: data.url,
|
|
2351
|
+
shortUrl: data.shortUrl,
|
|
2352
|
+
imageUrl: data.imageUrl || null
|
|
2353
|
+
};
|
|
1990
2354
|
} catch {
|
|
1991
2355
|
return null;
|
|
1992
2356
|
}
|
|
@@ -2140,10 +2504,69 @@ function parseLastDaysFlag(args) {
|
|
|
2140
2504
|
shorthandDays.sort((a, b) => a - b);
|
|
2141
2505
|
return shorthandDays[0];
|
|
2142
2506
|
}
|
|
2507
|
+
function parseActivityMetric(value) {
|
|
2508
|
+
if (value === "sessions" || value === "messages" || value === "tokens") {
|
|
2509
|
+
return value;
|
|
2510
|
+
}
|
|
2511
|
+
return "tokens";
|
|
2512
|
+
}
|
|
2513
|
+
function parseActivityDays(value) {
|
|
2514
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
2515
|
+
return Math.round(value);
|
|
2516
|
+
}
|
|
2517
|
+
if (typeof value === "string") {
|
|
2518
|
+
const parsed = Number.parseInt(value, 10);
|
|
2519
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
2520
|
+
return parsed;
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
return 365;
|
|
2524
|
+
}
|
|
2525
|
+
function buildUsageArtifact(stats) {
|
|
2526
|
+
return {
|
|
2527
|
+
type: "usage",
|
|
2528
|
+
schemaVersion: 1,
|
|
2529
|
+
source: stats.source,
|
|
2530
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2531
|
+
renderOptions: {
|
|
2532
|
+
canonicalPath: "usage",
|
|
2533
|
+
theme: "light"
|
|
2534
|
+
},
|
|
2535
|
+
payload: stats
|
|
2536
|
+
};
|
|
2537
|
+
}
|
|
2538
|
+
function buildWrappedArtifact(stats) {
|
|
2539
|
+
return {
|
|
2540
|
+
type: "wrapped",
|
|
2541
|
+
schemaVersion: 1,
|
|
2542
|
+
source: stats.source || "claude",
|
|
2543
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2544
|
+
renderOptions: {
|
|
2545
|
+
canonicalPath: "wrapped",
|
|
2546
|
+
theme: "light"
|
|
2547
|
+
},
|
|
2548
|
+
payload: stats
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
async function publishArtifactWithFallback(artifact, baseUrl, fallbackUrl, preferCanonical = false) {
|
|
2552
|
+
const published = await publishArtifact(artifact, baseUrl, fallbackUrl);
|
|
2553
|
+
if (!published) {
|
|
2554
|
+
return {
|
|
2555
|
+
canonicalUrl: fallbackUrl,
|
|
2556
|
+
shareUrl: fallbackUrl,
|
|
2557
|
+
imageUrl: null
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
return {
|
|
2561
|
+
canonicalUrl: published.url,
|
|
2562
|
+
shareUrl: preferCanonical ? published.url : published.shortUrl,
|
|
2563
|
+
imageUrl: published.imageUrl || null
|
|
2564
|
+
};
|
|
2565
|
+
}
|
|
2143
2566
|
var main = defineCommand({
|
|
2144
2567
|
meta: {
|
|
2145
2568
|
name: "vibestats",
|
|
2146
|
-
version: "1.3.
|
|
2569
|
+
version: "1.3.6",
|
|
2147
2570
|
description: "AI coding stats - usage tracking and annual wrapped for Claude Code & Codex"
|
|
2148
2571
|
},
|
|
2149
2572
|
args: {
|
|
@@ -2154,6 +2577,11 @@ var main = defineCommand({
|
|
|
2154
2577
|
description: "Show annual wrapped summary instead of usage stats",
|
|
2155
2578
|
default: false
|
|
2156
2579
|
},
|
|
2580
|
+
activity: {
|
|
2581
|
+
type: "boolean",
|
|
2582
|
+
description: "Show the GitHub-style activity graph summary",
|
|
2583
|
+
default: false
|
|
2584
|
+
},
|
|
2157
2585
|
// Data source
|
|
2158
2586
|
codex: {
|
|
2159
2587
|
type: "boolean",
|
|
@@ -2220,6 +2648,14 @@ var main = defineCommand({
|
|
|
2220
2648
|
description: "Use compact table format (hide cache columns)",
|
|
2221
2649
|
default: false
|
|
2222
2650
|
},
|
|
2651
|
+
metric: {
|
|
2652
|
+
type: "string",
|
|
2653
|
+
description: "Activity metric: tokens, sessions, or messages"
|
|
2654
|
+
},
|
|
2655
|
+
days: {
|
|
2656
|
+
type: "string",
|
|
2657
|
+
description: "Number of days to include in the activity graph"
|
|
2658
|
+
},
|
|
2223
2659
|
// Share option for usage mode
|
|
2224
2660
|
share: {
|
|
2225
2661
|
type: "boolean",
|
|
@@ -2272,7 +2708,9 @@ var main = defineCommand({
|
|
|
2272
2708
|
console.log(JSON.stringify(config, null, 2));
|
|
2273
2709
|
return;
|
|
2274
2710
|
}
|
|
2275
|
-
if (args.
|
|
2711
|
+
if (args.activity) {
|
|
2712
|
+
await runActivity(args, config);
|
|
2713
|
+
} else if (args.wrapped) {
|
|
2276
2714
|
await runWrapped(args, config);
|
|
2277
2715
|
} else {
|
|
2278
2716
|
await runUsage(args, config);
|
|
@@ -2343,10 +2781,13 @@ async function runUsage(args, config) {
|
|
|
2343
2781
|
let shareUrl = null;
|
|
2344
2782
|
if (args.share) {
|
|
2345
2783
|
const fullUrl = encodeUsageToUrl(stats, baseUrl);
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2784
|
+
const shared = await publishArtifactWithFallback(
|
|
2785
|
+
buildUsageArtifact(stats),
|
|
2786
|
+
baseUrl,
|
|
2787
|
+
fullUrl,
|
|
2788
|
+
Boolean(args["no-short"])
|
|
2789
|
+
);
|
|
2790
|
+
shareUrl = shared.shareUrl;
|
|
2350
2791
|
}
|
|
2351
2792
|
if (args.quiet && args.share && shareUrl) {
|
|
2352
2793
|
console.log(shareUrl);
|
|
@@ -2379,9 +2820,14 @@ async function runUsage(args, config) {
|
|
|
2379
2820
|
}
|
|
2380
2821
|
async function runWrapped(args, config) {
|
|
2381
2822
|
const options = resolveOptions(args, config);
|
|
2823
|
+
const metric = parseActivityMetric(args.metric);
|
|
2824
|
+
const days = parseActivityDays(args.days);
|
|
2382
2825
|
const spinner = createSpinner("Preparing wrapped...");
|
|
2383
|
-
const data = await spinner.whilePromise(
|
|
2384
|
-
|
|
2826
|
+
const [data, activityStats] = await spinner.whilePromise(
|
|
2827
|
+
Promise.all([
|
|
2828
|
+
loadData({ codexOnly: args.codex, combined: args.combined }),
|
|
2829
|
+
loadActivityStats({ codexOnly: args.codex, combined: args.combined })
|
|
2830
|
+
])
|
|
2385
2831
|
);
|
|
2386
2832
|
validateData(data, { codexOnly: args.codex, combined: args.combined });
|
|
2387
2833
|
let claudeStats = null;
|
|
@@ -2400,23 +2846,89 @@ async function runWrapped(args, config) {
|
|
|
2400
2846
|
} else {
|
|
2401
2847
|
stats = claudeStats;
|
|
2402
2848
|
}
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
if (!args["no-short"]) {
|
|
2406
|
-
shortUrl = await createShortlink(url, options.baseUrl);
|
|
2849
|
+
if (activityStats) {
|
|
2850
|
+
stats.activity = buildActivityGraph(activityStats, metric, days);
|
|
2407
2851
|
}
|
|
2852
|
+
const legacyUrl = encodeStatsToUrl(stats, options.baseUrl);
|
|
2853
|
+
const published = await publishArtifactWithFallback(
|
|
2854
|
+
buildWrappedArtifact(stats),
|
|
2855
|
+
options.baseUrl,
|
|
2856
|
+
legacyUrl,
|
|
2857
|
+
Boolean(args["no-short"])
|
|
2858
|
+
);
|
|
2408
2859
|
if (options.outputFormat === "json") {
|
|
2409
|
-
console.log(
|
|
2860
|
+
console.log(
|
|
2861
|
+
JSON.stringify(
|
|
2862
|
+
{
|
|
2863
|
+
...stats,
|
|
2864
|
+
url: published.canonicalUrl,
|
|
2865
|
+
shortUrl: published.shareUrl === published.canonicalUrl ? null : published.shareUrl,
|
|
2866
|
+
imageUrl: published.imageUrl
|
|
2867
|
+
},
|
|
2868
|
+
null,
|
|
2869
|
+
2
|
|
2870
|
+
)
|
|
2871
|
+
);
|
|
2410
2872
|
} else if (options.outputFormat === "quiet") {
|
|
2411
|
-
console.log(
|
|
2873
|
+
console.log(published.shareUrl);
|
|
2412
2874
|
} else {
|
|
2413
|
-
displayWrappedStats(stats,
|
|
2875
|
+
displayWrappedStats(stats, published.canonicalUrl, {
|
|
2414
2876
|
theme: options.theme,
|
|
2415
2877
|
hideCost: options.hideCost,
|
|
2416
|
-
shortUrl
|
|
2878
|
+
shortUrl: published.shareUrl === published.canonicalUrl ? null : published.shareUrl,
|
|
2879
|
+
imageUrl: published.imageUrl
|
|
2417
2880
|
});
|
|
2418
2881
|
}
|
|
2419
2882
|
}
|
|
2883
|
+
async function runActivity(args, config) {
|
|
2884
|
+
const metric = parseActivityMetric(args.metric);
|
|
2885
|
+
const days = parseActivityDays(args.days);
|
|
2886
|
+
const spinner = createSpinner("Preparing activity graph...");
|
|
2887
|
+
const stats = await spinner.whilePromise(
|
|
2888
|
+
loadActivityStats({
|
|
2889
|
+
codexOnly: args.codex,
|
|
2890
|
+
combined: args.combined,
|
|
2891
|
+
projectFilter: args.project ? process.cwd() : void 0,
|
|
2892
|
+
since: args.since,
|
|
2893
|
+
until: args.until
|
|
2894
|
+
})
|
|
2895
|
+
);
|
|
2896
|
+
if (!stats) {
|
|
2897
|
+
console.error("Error: No activity data found.");
|
|
2898
|
+
process.exit(1);
|
|
2899
|
+
}
|
|
2900
|
+
const artifact = buildActivityArtifact(stats, metric, days);
|
|
2901
|
+
const baseUrl = args.url || config.baseUrl || "https://vibestats.wolfai.dev";
|
|
2902
|
+
const fallbackUrl = encodeActivityToUrl(artifact.payload, baseUrl);
|
|
2903
|
+
let shared = null;
|
|
2904
|
+
if (args.share) {
|
|
2905
|
+
shared = await publishArtifactWithFallback(artifact, baseUrl, fallbackUrl, Boolean(args["no-short"]));
|
|
2906
|
+
}
|
|
2907
|
+
if (args.json) {
|
|
2908
|
+
console.log(
|
|
2909
|
+
JSON.stringify(
|
|
2910
|
+
{
|
|
2911
|
+
...artifact.payload,
|
|
2912
|
+
url: shared?.canonicalUrl || null,
|
|
2913
|
+
shortUrl: shared && shared.shareUrl !== shared.canonicalUrl ? shared.shareUrl : null,
|
|
2914
|
+
imageUrl: shared?.imageUrl || null
|
|
2915
|
+
},
|
|
2916
|
+
null,
|
|
2917
|
+
2
|
|
2918
|
+
)
|
|
2919
|
+
);
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
if (args.quiet) {
|
|
2923
|
+
console.log(shared?.shareUrl || fallbackUrl);
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2926
|
+
displayActivityStats(artifact.payload, shared?.canonicalUrl || fallbackUrl, {
|
|
2927
|
+
theme: config.theme,
|
|
2928
|
+
shortUrl: shared && shared.shareUrl !== shared.canonicalUrl ? shared.shareUrl : null,
|
|
2929
|
+
imageUrl: shared?.imageUrl || null
|
|
2930
|
+
});
|
|
2931
|
+
}
|
|
2420
2932
|
function formatNumber2(n) {
|
|
2421
2933
|
if (n >= 1e9) return `${(n / 1e9).toFixed(1)}B`;
|
|
2422
2934
|
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|