tokentracker-cli 0.2.27 → 0.3.3
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/README.md +61 -28
- package/dashboard/dist/assets/DashboardPage-DwwjNQP7.js +12 -0
- package/dashboard/dist/assets/{LandingExtras-BUwK_c-9.js → LandingExtras-BsrCd4pK.js} +1 -1
- package/dashboard/dist/assets/LeaderboardPage-ZIRnfiXX.js +1 -0
- package/dashboard/dist/assets/LeaderboardProfilePage-CoLDY-lR.js +1 -0
- package/dashboard/dist/assets/{MatrixRain-D7Pm88QF.js → MatrixRain-B2DIJ0ID.js} +1 -1
- package/dashboard/dist/assets/MatrixShell-Byc-iwIr.js +1 -0
- package/dashboard/dist/assets/{main-oIbJf8pA.js → main-C3QpHBOP.js} +10 -8
- package/dashboard/dist/assets/main-DJvWJCv5.css +1 -0
- package/dashboard/dist/assets/vibeusage-api-EQ444MvB.js +1 -0
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +2 -2
- package/src/commands/init.js +22 -1
- package/src/commands/sync.js +78 -2
- package/src/lib/cursor-config.js +328 -0
- package/src/lib/local-api.js +108 -12
- package/src/lib/rollout.js +316 -2
- package/dashboard/dist/assets/AsciiBox-BZ2xYyXa.js +0 -1
- package/dashboard/dist/assets/DashboardPage-ynq92wZf.js +0 -12
- package/dashboard/dist/assets/LeaderboardPage-IzN8LibU.js +0 -1
- package/dashboard/dist/assets/LeaderboardProfilePage-3JjGYRaz.js +0 -1
- package/dashboard/dist/assets/MatrixShell-BtjokX5f.js +0 -1
- package/dashboard/dist/assets/main-hwTpulbk.css +0 -1
package/src/lib/rollout.js
CHANGED
|
@@ -836,8 +836,39 @@ async function parseClaudeFile({
|
|
|
836
836
|
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
837
837
|
|
|
838
838
|
let eventsAggregated = 0;
|
|
839
|
+
const isMainSession = !filePath.includes("/subagents/");
|
|
839
840
|
for await (const line of rl) {
|
|
840
|
-
if (!line
|
|
841
|
+
if (!line) continue;
|
|
842
|
+
|
|
843
|
+
// Count user-typed messages as conversations (main sessions only).
|
|
844
|
+
// Exclude tool_result messages — those are auto-generated by tool calls,
|
|
845
|
+
// not manually typed by the user. Only count messages with a "text" block.
|
|
846
|
+
if (isMainSession && line.includes('"type":"user"')) {
|
|
847
|
+
let userObj;
|
|
848
|
+
try {
|
|
849
|
+
userObj = JSON.parse(line);
|
|
850
|
+
} catch (_e) {
|
|
851
|
+
/* skip */
|
|
852
|
+
}
|
|
853
|
+
if (userObj?.type === "user") {
|
|
854
|
+
const content = userObj?.message?.content;
|
|
855
|
+
const hasText =
|
|
856
|
+
typeof content === "string" ||
|
|
857
|
+
(Array.isArray(content) && content.some((b) => b?.type === "text"));
|
|
858
|
+
if (hasText) {
|
|
859
|
+
const userTs = typeof userObj?.timestamp === "string" ? userObj.timestamp : null;
|
|
860
|
+
const userBucketStart = userTs ? toUtcHalfHourStart(userTs) : null;
|
|
861
|
+
if (userBucketStart) {
|
|
862
|
+
const userModel = DEFAULT_MODEL;
|
|
863
|
+
const userBucket = getHourlyBucket(hourlyState, source, userModel, userBucketStart);
|
|
864
|
+
userBucket.totals.conversation_count += 1;
|
|
865
|
+
touchedBuckets.add(bucketKey(source, userModel, userBucketStart));
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (!line.includes('"usage"')) continue;
|
|
841
872
|
let obj;
|
|
842
873
|
try {
|
|
843
874
|
obj = JSON.parse(line);
|
|
@@ -864,7 +895,7 @@ async function parseClaudeFile({
|
|
|
864
895
|
|
|
865
896
|
const delta = normalizeClaudeUsage(usage);
|
|
866
897
|
if (!delta || isAllZeroUsage(delta)) continue;
|
|
867
|
-
delta.conversation_count =
|
|
898
|
+
delta.conversation_count = 0;
|
|
868
899
|
|
|
869
900
|
const bucketStart = toUtcHalfHourStart(tokenTimestamp);
|
|
870
901
|
if (!bucketStart) continue;
|
|
@@ -2483,16 +2514,299 @@ async function resolveProjectContextForDb({
|
|
|
2483
2514
|
});
|
|
2484
2515
|
}
|
|
2485
2516
|
|
|
2517
|
+
// ── Cursor (API-based) ──
|
|
2518
|
+
|
|
2519
|
+
/**
|
|
2520
|
+
* Incremental parser for Cursor usage data fetched via API.
|
|
2521
|
+
*
|
|
2522
|
+
* Unlike other parsers that read local files, this one receives pre-parsed
|
|
2523
|
+
* CSV records from cursor-config.js and aggregates them into 30-min buckets.
|
|
2524
|
+
*
|
|
2525
|
+
* Incremental state is tracked in `cursors.cursorApi.lastRecordTimestamp`.
|
|
2526
|
+
*/
|
|
2527
|
+
async function parseCursorApiIncremental({
|
|
2528
|
+
records,
|
|
2529
|
+
cursors,
|
|
2530
|
+
queuePath,
|
|
2531
|
+
onProgress,
|
|
2532
|
+
source,
|
|
2533
|
+
}) {
|
|
2534
|
+
await ensureDir(path.dirname(queuePath));
|
|
2535
|
+
const defaultSource = normalizeSourceInput(source) || "cursor";
|
|
2536
|
+
const hourlyState = normalizeHourlyState(cursors?.hourly);
|
|
2537
|
+
const touchedBuckets = new Set();
|
|
2538
|
+
|
|
2539
|
+
// Incremental: skip records we already processed
|
|
2540
|
+
const lastTs = cursors?.cursorApi?.lastRecordTimestamp || null;
|
|
2541
|
+
let latestTs = lastTs;
|
|
2542
|
+
let eventsAggregated = 0;
|
|
2543
|
+
const cb = typeof onProgress === "function" ? onProgress : null;
|
|
2544
|
+
const total = records.length;
|
|
2545
|
+
|
|
2546
|
+
for (let i = 0; i < records.length; i++) {
|
|
2547
|
+
const record = records[i];
|
|
2548
|
+
const recordDate = record.date;
|
|
2549
|
+
if (!recordDate) continue;
|
|
2550
|
+
|
|
2551
|
+
// Skip records we already processed (CSV is ordered newest-first)
|
|
2552
|
+
if (lastTs && recordDate <= lastTs) continue;
|
|
2553
|
+
|
|
2554
|
+
const { normalizeCursorUsage } = require("./cursor-config");
|
|
2555
|
+
const delta = normalizeCursorUsage(record);
|
|
2556
|
+
if (isAllZeroUsage(delta)) continue;
|
|
2557
|
+
|
|
2558
|
+
delta.conversation_count = 1;
|
|
2559
|
+
|
|
2560
|
+
const bucketStart = toUtcHalfHourStart(recordDate);
|
|
2561
|
+
if (!bucketStart) continue;
|
|
2562
|
+
|
|
2563
|
+
const model = normalizeModelInput(record.model) || DEFAULT_MODEL;
|
|
2564
|
+
const bucket = getHourlyBucket(hourlyState, defaultSource, model, bucketStart);
|
|
2565
|
+
addTotals(bucket.totals, delta);
|
|
2566
|
+
touchedBuckets.add(bucketKey(defaultSource, model, bucketStart));
|
|
2567
|
+
|
|
2568
|
+
eventsAggregated += 1;
|
|
2569
|
+
|
|
2570
|
+
// Track latest timestamp
|
|
2571
|
+
if (!latestTs || recordDate > latestTs) {
|
|
2572
|
+
latestTs = recordDate;
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
if (cb && (i % 200 === 0 || i === records.length - 1)) {
|
|
2576
|
+
cb({
|
|
2577
|
+
index: i + 1,
|
|
2578
|
+
total,
|
|
2579
|
+
eventsAggregated,
|
|
2580
|
+
bucketsQueued: touchedBuckets.size,
|
|
2581
|
+
});
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
const bucketsQueued = await enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets });
|
|
2586
|
+
hourlyState.updatedAt = new Date().toISOString();
|
|
2587
|
+
cursors.hourly = hourlyState;
|
|
2588
|
+
|
|
2589
|
+
// Update cursor state
|
|
2590
|
+
if (!cursors.cursorApi) cursors.cursorApi = {};
|
|
2591
|
+
if (latestTs && latestTs !== lastTs) {
|
|
2592
|
+
cursors.cursorApi.lastRecordTimestamp = latestTs;
|
|
2593
|
+
}
|
|
2594
|
+
cursors.cursorApi.updatedAt = new Date().toISOString();
|
|
2595
|
+
|
|
2596
|
+
return { recordsProcessed: total, eventsAggregated, bucketsQueued };
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2599
|
+
// ---------------------------------------------------------------------------
|
|
2600
|
+
// Kiro token tracking (reads from devdata.sqlite)
|
|
2601
|
+
// ---------------------------------------------------------------------------
|
|
2602
|
+
|
|
2603
|
+
function resolveKiroBasePath() {
|
|
2604
|
+
const home = require("node:os").homedir();
|
|
2605
|
+
return path.join(
|
|
2606
|
+
home,
|
|
2607
|
+
"Library",
|
|
2608
|
+
"Application Support",
|
|
2609
|
+
"Kiro",
|
|
2610
|
+
"User",
|
|
2611
|
+
"globalStorage",
|
|
2612
|
+
"kiro.kiroagent",
|
|
2613
|
+
);
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
function resolveKiroDbPath() {
|
|
2617
|
+
return path.join(resolveKiroBasePath(), "dev_data", "devdata.sqlite");
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
function readKiroDbTokens(dbPath, sinceId) {
|
|
2621
|
+
if (!dbPath || !fssync.existsSync(dbPath)) return [];
|
|
2622
|
+
const minId = Number.isFinite(sinceId) && sinceId > 0 ? sinceId : 0;
|
|
2623
|
+
const sql = `SELECT id, model, provider, tokens_prompt, tokens_generated, timestamp FROM tokens_generated WHERE id > ${minId} ORDER BY id ASC`;
|
|
2624
|
+
let raw;
|
|
2625
|
+
try {
|
|
2626
|
+
raw = cp.execFileSync("sqlite3", ["-json", dbPath, sql], {
|
|
2627
|
+
encoding: "utf8",
|
|
2628
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
2629
|
+
timeout: 15_000,
|
|
2630
|
+
});
|
|
2631
|
+
} catch (_e) {
|
|
2632
|
+
return [];
|
|
2633
|
+
}
|
|
2634
|
+
if (!raw || !raw.trim()) return [];
|
|
2635
|
+
let rows;
|
|
2636
|
+
try {
|
|
2637
|
+
rows = JSON.parse(raw);
|
|
2638
|
+
} catch (_e) {
|
|
2639
|
+
return [];
|
|
2640
|
+
}
|
|
2641
|
+
return Array.isArray(rows) ? rows : [];
|
|
2642
|
+
}
|
|
2643
|
+
|
|
2644
|
+
// Build a sorted timeline of model usage from Kiro .chat metadata files
|
|
2645
|
+
function buildKiroModelTimeline(basePath) {
|
|
2646
|
+
const timeline = []; // [{ startMs, endMs, model }]
|
|
2647
|
+
if (!basePath || !fssync.existsSync(basePath)) return timeline;
|
|
2648
|
+
let dirs;
|
|
2649
|
+
try {
|
|
2650
|
+
dirs = fssync.readdirSync(basePath, { withFileTypes: true });
|
|
2651
|
+
} catch (_e) {
|
|
2652
|
+
return timeline;
|
|
2653
|
+
}
|
|
2654
|
+
for (const entry of dirs) {
|
|
2655
|
+
if (!entry.isDirectory()) continue;
|
|
2656
|
+
const dirPath = path.join(basePath, entry.name);
|
|
2657
|
+
let files;
|
|
2658
|
+
try {
|
|
2659
|
+
files = fssync.readdirSync(dirPath).filter((f) => f.endsWith(".chat"));
|
|
2660
|
+
} catch (_e) {
|
|
2661
|
+
continue;
|
|
2662
|
+
}
|
|
2663
|
+
for (const file of files) {
|
|
2664
|
+
try {
|
|
2665
|
+
const raw = fssync.readFileSync(path.join(dirPath, file), "utf8");
|
|
2666
|
+
const data = JSON.parse(raw);
|
|
2667
|
+
const meta = data?.metadata;
|
|
2668
|
+
if (!meta?.modelId || !meta?.startTime) continue;
|
|
2669
|
+
timeline.push({
|
|
2670
|
+
startMs: meta.startTime,
|
|
2671
|
+
endMs: meta.endTime || meta.startTime,
|
|
2672
|
+
model: String(meta.modelId),
|
|
2673
|
+
});
|
|
2674
|
+
} catch (_e) {}
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
timeline.sort((a, b) => a.startMs - b.startMs);
|
|
2678
|
+
return timeline;
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
// Find the model for a given UTC timestamp string using the .chat timeline
|
|
2682
|
+
function resolveKiroModel(timeline, utcTimestamp) {
|
|
2683
|
+
if (!timeline.length || !utcTimestamp) return null;
|
|
2684
|
+
const ts = new Date(utcTimestamp).getTime();
|
|
2685
|
+
if (!Number.isFinite(ts)) return null;
|
|
2686
|
+
|
|
2687
|
+
// Binary search for the closest .chat entry
|
|
2688
|
+
let lo = 0;
|
|
2689
|
+
let hi = timeline.length - 1;
|
|
2690
|
+
let best = null;
|
|
2691
|
+
let bestDist = Infinity;
|
|
2692
|
+
while (lo <= hi) {
|
|
2693
|
+
const mid = (lo + hi) >>> 1;
|
|
2694
|
+
const entry = timeline[mid];
|
|
2695
|
+
// Check if timestamp falls within the .chat execution window
|
|
2696
|
+
if (ts >= entry.startMs && ts <= entry.endMs) return entry.model;
|
|
2697
|
+
const dist = Math.min(Math.abs(ts - entry.startMs), Math.abs(ts - entry.endMs));
|
|
2698
|
+
if (dist < bestDist) {
|
|
2699
|
+
bestDist = dist;
|
|
2700
|
+
best = entry.model;
|
|
2701
|
+
}
|
|
2702
|
+
if (ts < entry.startMs) hi = mid - 1;
|
|
2703
|
+
else lo = mid + 1;
|
|
2704
|
+
}
|
|
2705
|
+
// Only match if within 10 minutes
|
|
2706
|
+
return bestDist < 10 * 60 * 1000 ? best : null;
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
// Normalize Kiro internal model IDs to readable names
|
|
2710
|
+
// e.g. "CLAUDE_SONNET_4_20250514_V1_0" → "claude-sonnet-4"
|
|
2711
|
+
function normalizeKiroModelName(raw) {
|
|
2712
|
+
if (!raw || typeof raw !== "string") return null;
|
|
2713
|
+
let name = raw.trim();
|
|
2714
|
+
if (!name) return null;
|
|
2715
|
+
// Already lowercase with dashes (e.g. "claude-opus-4.5") → keep as-is
|
|
2716
|
+
if (name === name.toLowerCase() && name.includes("-")) return name;
|
|
2717
|
+
// UPPER_SNAKE_CASE internal names: strip date/version suffixes, convert to lowercase-dash
|
|
2718
|
+
name = name
|
|
2719
|
+
.replace(/_\d{8}_V\d+_\d+$/i, "") // remove _20250514_V1_0
|
|
2720
|
+
.replace(/_V\d+$/i, "") // remove _V1
|
|
2721
|
+
.toLowerCase()
|
|
2722
|
+
.replace(/_/g, "-");
|
|
2723
|
+
return name || null;
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2726
|
+
async function parseKiroIncremental({ dbPath, cursors, queuePath, onProgress }) {
|
|
2727
|
+
await ensureDir(path.dirname(queuePath));
|
|
2728
|
+
const kiroState = cursors.kiro && typeof cursors.kiro === "object" ? cursors.kiro : {};
|
|
2729
|
+
const lastId = typeof kiroState.lastId === "number" ? kiroState.lastId : 0;
|
|
2730
|
+
|
|
2731
|
+
const resolvedDbPath = dbPath || resolveKiroDbPath();
|
|
2732
|
+
const rows = readKiroDbTokens(resolvedDbPath, lastId);
|
|
2733
|
+
if (rows.length === 0) {
|
|
2734
|
+
return { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
// Build model timeline from .chat files for model name resolution
|
|
2738
|
+
const basePath = resolveKiroBasePath();
|
|
2739
|
+
const modelTimeline = buildKiroModelTimeline(basePath);
|
|
2740
|
+
|
|
2741
|
+
const hourlyState = normalizeHourlyState(cursors?.hourly);
|
|
2742
|
+
const touchedBuckets = new Set();
|
|
2743
|
+
const cb = typeof onProgress === "function" ? onProgress : null;
|
|
2744
|
+
let eventsAggregated = 0;
|
|
2745
|
+
let maxId = lastId;
|
|
2746
|
+
|
|
2747
|
+
for (let i = 0; i < rows.length; i++) {
|
|
2748
|
+
const row = rows[i];
|
|
2749
|
+
const inputTokens = toNonNegativeInt(row.tokens_prompt);
|
|
2750
|
+
const outputTokens = toNonNegativeInt(row.tokens_generated);
|
|
2751
|
+
if (inputTokens === 0 && outputTokens === 0) continue;
|
|
2752
|
+
|
|
2753
|
+
// timestamp format: "2026-01-09 15:25:30" (UTC from SQLite DEFAULT CURRENT_TIMESTAMP)
|
|
2754
|
+
const ts = row.timestamp ? row.timestamp.replace(" ", "T") + "Z" : null;
|
|
2755
|
+
const bucketStart = ts ? toUtcHalfHourStart(ts) : null;
|
|
2756
|
+
if (!bucketStart) continue;
|
|
2757
|
+
|
|
2758
|
+
// Resolve actual model from .chat timeline, fallback to "kiro-agent"
|
|
2759
|
+
const resolvedModel = resolveKiroModel(modelTimeline, ts);
|
|
2760
|
+
const model = normalizeKiroModelName(resolvedModel) || "kiro-agent";
|
|
2761
|
+
|
|
2762
|
+
const delta = {
|
|
2763
|
+
input_tokens: inputTokens,
|
|
2764
|
+
cached_input_tokens: 0,
|
|
2765
|
+
output_tokens: outputTokens,
|
|
2766
|
+
reasoning_output_tokens: 0,
|
|
2767
|
+
total_tokens: inputTokens + outputTokens,
|
|
2768
|
+
conversation_count: 1,
|
|
2769
|
+
};
|
|
2770
|
+
|
|
2771
|
+
const bucket = getHourlyBucket(hourlyState, "kiro", model, bucketStart);
|
|
2772
|
+
addTotals(bucket.totals, delta);
|
|
2773
|
+
touchedBuckets.add(bucketKey("kiro", model, bucketStart));
|
|
2774
|
+
eventsAggregated++;
|
|
2775
|
+
|
|
2776
|
+
if (row.id && row.id > maxId) maxId = row.id;
|
|
2777
|
+
|
|
2778
|
+
if (cb) {
|
|
2779
|
+
cb({
|
|
2780
|
+
index: i + 1,
|
|
2781
|
+
total: rows.length,
|
|
2782
|
+
recordsProcessed: i + 1,
|
|
2783
|
+
eventsAggregated,
|
|
2784
|
+
bucketsQueued: touchedBuckets.size,
|
|
2785
|
+
});
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2789
|
+
const bucketsQueued = await enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets });
|
|
2790
|
+
hourlyState.updatedAt = new Date().toISOString();
|
|
2791
|
+
cursors.hourly = hourlyState;
|
|
2792
|
+
cursors.kiro = { lastId: maxId, updatedAt: new Date().toISOString() };
|
|
2793
|
+
|
|
2794
|
+
return { recordsProcessed: rows.length, eventsAggregated, bucketsQueued };
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2486
2797
|
module.exports = {
|
|
2487
2798
|
listRolloutFiles,
|
|
2488
2799
|
listClaudeProjectFiles,
|
|
2489
2800
|
listGeminiSessionFiles,
|
|
2490
2801
|
listOpencodeMessageFiles,
|
|
2491
2802
|
readOpencodeDbMessages,
|
|
2803
|
+
resolveKiroDbPath,
|
|
2492
2804
|
parseRolloutIncremental,
|
|
2493
2805
|
parseClaudeIncremental,
|
|
2494
2806
|
parseGeminiIncremental,
|
|
2495
2807
|
parseOpencodeIncremental,
|
|
2496
2808
|
parseOpencodeDbIncremental,
|
|
2497
2809
|
parseOpenclawIncremental,
|
|
2810
|
+
parseCursorApiIncremental,
|
|
2811
|
+
parseKiroIncremental,
|
|
2498
2812
|
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as k,ad as Z,d as ue,ae as z,af as L,ag as le,ah as de,ai as fe,aj as me,ak as pe,al as ge,am as ke,_ as ye,an as he,ao as Te,ap as we,m}from"./main-oIbJf8pA.js";const xe="Backend runtime unavailable (InsForge). Please retry later.",p={usageSummary:"vibeusage-usage-summary",usageDaily:"vibeusage-usage-daily",usageHourly:"vibeusage-usage-hourly",usageMonthly:"vibeusage-usage-monthly",usageHeatmap:"vibeusage-usage-heatmap",usageModelBreakdown:"vibeusage-usage-model-breakdown",projectUsageSummary:"vibeusage-project-usage-summary",leaderboard:"vibeusage-leaderboard",leaderboardProfile:"vibeusage-leaderboard-profile",userStatus:"vibeusage-user-status",linkCodeInit:"vibeusage-link-code-init",publicViewProfile:"vibeusage-public-view-profile",publicVisibility:"vibeusage-public-visibility"},J="/functions",be="/api/functions",h={business:"business",probe:"probe"};let D=null;async function f(e){return await ue(e)}async function Ee({baseUrl:e,accessToken:s,signal:t}={}){const a=await f(s),n=we(new Date);return await y({baseUrl:e,accessToken:a,slug:p.usageSummary,params:{from:n,to:n},fetchOptions:{cache:"no-store",signal:t},retry:!1,requestKind:h.probe}),{status:200}}async function Ce({baseUrl:e,accessToken:s,from:t,to:a,source:n,model:r,timeZone:l,tzOffsetMinutes:i,rolling:c=!1}={}){const u=await f(s);if(k())return de({from:t,to:a,seed:u,rolling:c});const o=M({timeZone:l,tzOffsetMinutes:i}),d=P({source:n,model:r}),v=c?{rolling:"1"}:{};return y({baseUrl:e,accessToken:u,slug:p.usageSummary,params:{from:t,to:a,...d,...o,...v}})}async function je({baseUrl:e,accessToken:s,from:t,to:a,source:n,limit:r,timeZone:l,tzOffsetMinutes:i}={}){const c=await f(s);if(k())return me({seed:c,limit:r});const u=M({timeZone:l,tzOffsetMinutes:i}),d={...P({source:n}),...u};return t&&(d.from=t),a&&(d.to=a),r!=null&&(d.limit=String(r)),y({baseUrl:e,accessToken:c,slug:p.projectUsageSummary,params:d})}async function Ie({baseUrl:e,accessToken:s,period:t,metric:a,limit:n,offset:r}={}){const l=await f(s);if(k())return Z({seed:l,period:t,metric:a,limit:n,offset:r});const c=(typeof t=="string"?t:"week").trim().toLowerCase(),o={period:c==="month"||c==="total"||c==="week"?c:"week"};return a&&(o.metric=String(a)),n!=null&&(o.limit=String(n)),r!=null&&(o.offset=String(r)),y({baseUrl:e,accessToken:l,slug:p.leaderboard,params:o})}async function Ne({baseUrl:e,accessToken:s}={}){const t=await f(s);return k()?{enabled:!1,updated_at:null,share_token:null}:y({baseUrl:e,accessToken:t,slug:p.publicVisibility})}async function De({baseUrl:e,accessToken:s,enabled:t}={}){const a=await f(s);return k()?{enabled:!!t,updated_at:new Date().toISOString(),share_token:t?"pv1-mock-token":null}:X({baseUrl:e,accessToken:a,slug:p.publicVisibility,body:{enabled:!!t}})}async function Le({baseUrl:e,accessToken:s,userId:t,period:a}={}){const n=await f(s);if(k()){const r=Z({seed:n,period:a,metric:"all",limit:250,offset:0}),i=(Array.isArray(r?.entries)?r.entries:[]).find(c=>c?.user_id===t)||null;return{period:r?.period??"week",from:r?.from??null,to:r?.to??null,generated_at:r?.generated_at??new Date().toISOString(),entry:i?{user_id:i.user_id??null,display_name:i.display_name??null,avatar_url:i.avatar_url??null,rank:i.rank??null,gpt_tokens:i.gpt_tokens??"0",claude_tokens:i.claude_tokens??"0",other_tokens:i.other_tokens??"0",total_tokens:i.total_tokens??"0"}:null}}return y({baseUrl:e,accessToken:n,slug:p.leaderboardProfile,params:{user_id:String(t||""),period:String(a||"")}})}async function He({baseUrl:e,accessToken:s}={}){const t=await f(s);if(k()){const a=new Date().toISOString();return{user_id:"mock-user",created_at:a,pro:{active:!0,sources:["mock"],expires_at:null,partial:!1,as_of:a},subscriptions:{partial:!1,as_of:a,items:[{tool:"codex",provider:"openai",product:"chatgpt",plan_type:"pro",updated_at:a},{tool:"claude",provider:"anthropic",product:"subscription",plan_type:"max",rate_limit_tier:"default_claude_max_5x",updated_at:a}]},install:{partial:!1,as_of:a,has_active_device_token:!1,has_active_device:!1,active_device_tokens:0,active_devices:0,latest_token_activity_at:null,latest_device_seen_at:null}}}return y({baseUrl:e,accessToken:t,slug:p.userStatus})}async function ze({signal:e}={}){const s=await fetch("/functions/vibeusage-local-sync",{method:"POST",headers:{Accept:"application/json"},cache:"no-store",signal:e}),t=await s.json().catch(()=>({ok:!1,error:`Local sync request failed with HTTP ${s.status}`}));if(!s.ok||t?.ok===!1){const a=t?.error||t?.message||`Local sync request failed with HTTP ${s.status}`,n=new Error(a);throw n.status=s.status,n.statusCode=s.status,n.payload=t,n}return t}async function Ue({baseUrl:e,accessToken:s,from:t,to:a,source:n,timeZone:r,tzOffsetMinutes:l}={}){const i=await f(s);if(k())return fe({from:t,to:a,seed:i});const c=M({timeZone:r,tzOffsetMinutes:l}),u=P({source:n});return y({baseUrl:e,accessToken:i,slug:p.usageModelBreakdown,params:{from:t,to:a,...u,...c}})}async function Be({baseUrl:e,accessToken:s,from:t,to:a,source:n,model:r,timeZone:l,tzOffsetMinutes:i}={}){const c=await f(s);if(k())return le({from:t,to:a,seed:c});const u=M({timeZone:l,tzOffsetMinutes:i}),o=P({source:n,model:r});return y({baseUrl:e,accessToken:c,slug:p.usageDaily,params:{from:t,to:a,...o,...u}})}async function Oe({baseUrl:e,accessToken:s,day:t,source:a,model:n,timeZone:r,tzOffsetMinutes:l}={}){const i=await f(s);if(k())return pe({day:t,seed:i});const c=M({timeZone:r,tzOffsetMinutes:l}),u=P({source:a,model:n});return y({baseUrl:e,accessToken:i,slug:p.usageHourly,params:t?{day:t,...u,...c}:{...u,...c}})}async function Fe({baseUrl:e,accessToken:s,months:t,to:a,source:n,model:r,timeZone:l,tzOffsetMinutes:i}={}){const c=await f(s);if(k())return ge({months:t,to:a,seed:c});const u=M({timeZone:l,tzOffsetMinutes:i}),o=P({source:n,model:r});return y({baseUrl:e,accessToken:c,slug:p.usageMonthly,params:{...t?{months:String(t)}:{},...a?{to:a}:{},...o,...u}})}async function qe({baseUrl:e,accessToken:s,weeks:t,to:a,weekStartsOn:n,source:r,model:l,timeZone:i,tzOffsetMinutes:c}={}){const u=await f(s);if(k())return ke({weeks:t,to:a,weekStartsOn:n,seed:u});const o=M({timeZone:i,tzOffsetMinutes:c}),d=P({source:r,model:l});return y({baseUrl:e,accessToken:u,slug:p.usageHeatmap,params:{weeks:String(t),to:a,week_starts_on:n,...d,...o}})}async function Ve({baseUrl:e,accessToken:s}={}){const t=await f(s);return k()?{link_code:"mock_link_code",expires_at:new Date(Date.now()+10*6e4).toISOString()}:X({baseUrl:e,accessToken:t,slug:p.linkCodeInit,body:{}})}async function $e({baseUrl:e,accessToken:s}={}){const t=await f(s);return y({baseUrl:e,accessToken:t,slug:p.publicViewProfile})}function M({timeZone:e,tzOffsetMinutes:s}={}){const t={},a=typeof e=="string"?e.trim():"";return a&&(t.tz=a),Number.isFinite(s)&&(t.tz_offset_minutes=String(Math.trunc(s))),t}function P({source:e,model:s}={}){const t={},a=typeof e=="string"?e.trim().toLowerCase():"";a&&(t.source=a);const n=typeof s=="string"?s.trim():"";return n&&(t.model=n),t}async function y({baseUrl:e,accessToken:s,slug:t,params:a,fetchOptions:n,errorPrefix:r,retry:l,requestKind:i=h.business,skipSessionExpiry:c=!1,allowRefresh:u=!0}={}){let o=await f(s),d=E(o),v=z({baseUrl:e,accessToken:o??void 0}).getHttpClient();const C=ae(l,"GET"),b=c?h.probe:i;let A=0;const{primaryPath:j,fallbackPath:I}=Q(t);for(;;)try{const S=await G({http:v,primaryPath:j,fallbackPath:I,params:a,fetchOptions:n});return U({hadAccessToken:d,accessToken:o}),S}catch(S){const T=S;if(T?.name==="AbortError")throw S;let w=null;const B=T?.statusCode??T?.status;if(u&&se({status:B,requestKind:b,hadAccessToken:d,accessToken:o})){const g=(await te())?.accessToken??null;if(E(g)){const N=z({baseUrl:e,accessToken:g}).getHttpClient();o=g,d=!0,v=N;try{const x=await G({http:N,primaryPath:j,fallbackPath:I,params:a,fetchOptions:n});return U({hadAccessToken:!0,accessToken:g}),x}catch(x){const F=x?.statusCode??x?.status;$({status:F,hadAccessToken:!0,accessToken:g,skipSessionExpiry:b===h.probe})&&L(),w=R(x,{errorPrefix:r,hadAccessToken:!0,accessToken:g,skipSessionExpiry:!0})}}else H({hadAccessToken:d,accessToken:o,skipSessionExpiry:b===h.probe})&&L();w??=R(T,{errorPrefix:r,hadAccessToken:d,accessToken:o,skipSessionExpiry:!0})}if(w??=R(T,{errorPrefix:r,hadAccessToken:d,accessToken:o,skipSessionExpiry:b===h.probe}),!ne({err:w,attempt:A,retryOptions:C}))throw w;const O=re({retryOptions:C,attempt:A});await ie(O),A+=1}}async function X({baseUrl:e,accessToken:s,slug:t,body:a,fetchOptions:n,errorPrefix:r,retry:l,requestKind:i=h.business,skipSessionExpiry:c=!1,allowRefresh:u=!0}={}){let o=await f(s),d=E(o),v=z({baseUrl:e,accessToken:o??void 0}).getHttpClient();const C=ae(l,"POST"),b=c?h.probe:i;let A=0;const{primaryPath:j,fallbackPath:I}=Q(t);for(;;)try{const S=await K({http:v,primaryPath:j,fallbackPath:I,body:a,fetchOptions:n});return U({hadAccessToken:d,accessToken:o}),S}catch(S){const T=S;if(T?.name==="AbortError")throw S;let w=null;const B=T?.statusCode??T?.status;if(u&&se({status:B,requestKind:b,hadAccessToken:d,accessToken:o})){const g=(await te())?.accessToken??null;if(E(g)){const N=z({baseUrl:e,accessToken:g}).getHttpClient();o=g,d=!0,v=N;try{const x=await K({http:N,primaryPath:j,fallbackPath:I,body:a,fetchOptions:n});return U({hadAccessToken:!0,accessToken:g}),x}catch(x){const F=x?.statusCode??x?.status;$({status:F,hadAccessToken:!0,accessToken:g,skipSessionExpiry:b===h.probe})&&L(),w=R(x,{errorPrefix:r,hadAccessToken:!0,accessToken:g,skipSessionExpiry:!0})}}else H({hadAccessToken:d,accessToken:o,skipSessionExpiry:b===h.probe})&&L();w??=R(T,{errorPrefix:r,hadAccessToken:d,accessToken:o,skipSessionExpiry:!0})}if(w??=R(T,{errorPrefix:r,hadAccessToken:d,accessToken:o,skipSessionExpiry:b===h.probe}),!ne({err:w,attempt:A,retryOptions:C}))throw w;const O=re({retryOptions:C,attempt:A});await ie(O),A+=1}}function Q(e){const s=Se(e),t=`${V(J)}/${s}`,a=`${V(be)}/${s}`;return{primaryPath:t,fallbackPath:a}}function Se(e){return(typeof e=="string"?e.trim():"").replace(/^\/+/,"")}function V(e){const s=typeof e=="string"?e.trim():"";return s.endsWith("/")?s.slice(0,-1):s}async function G({http:e,primaryPath:s,fallbackPath:t,params:a,fetchOptions:n}={}){try{return await e.get(s,{params:a,...n||{}})}catch(r){if(!Y(r,s))throw r;return await e.get(t,{params:a,...n||{}})}}async function K({http:e,primaryPath:s,fallbackPath:t,body:a,fetchOptions:n}={}){try{return await W({http:e,path:s,body:a,fetchOptions:n})}catch(r){if(!Y(r,s))throw r;return await W({http:e,path:t,body:a,fetchOptions:n})}}async function W({http:e,path:s,body:t,fetchOptions:a}={}){return await e.post(s,t,{...a||{}})}function Y(e,s){return!s||!s.startsWith(`${V(J)}/`)?!1:(e?.statusCode??e?.status)===404}function R(e,{errorPrefix:s,hadAccessToken:t,accessToken:a,skipSessionExpiry:n}={}){const r=typeof e?.message=="string"?e.message.trim():"",l=typeof e?.error=="string"?e.error.trim():"",i=r||l||String(e||"Unknown error"),c=ve(i),u=new Error(s?`${s}: ${c}`:c);u.cause=e;const o=e?.statusCode??e?.status;return $({status:o,hadAccessToken:t,accessToken:a,skipSessionExpiry:n})&&L(),typeof o=="number"&&(u.status=o,u.statusCode=o),u.retryable=Ae(o)||Me(i),c!==i&&(u.originalMessage=i),e?.nextActions&&(u.nextActions=e.nextActions),e?.error&&(u.error=e.error),u}function H({hadAccessToken:e,accessToken:s,skipSessionExpiry:t}={}){return t||!e||!E(s)?!1:Pe(s)}function $({status:e,hadAccessToken:s,accessToken:t,skipSessionExpiry:a}={}){return e!==401?!1:H({hadAccessToken:s,accessToken:t,skipSessionExpiry:a})}function _e({hadAccessToken:e,accessToken:s}={}){return H({hadAccessToken:e,accessToken:s})}function U({hadAccessToken:e,accessToken:s}={}){_e({hadAccessToken:e,accessToken:s})&&he()}function ve(e){return ee(e)?xe:String(e||"Unknown error")}function ee(e){const s=String(e||"").toLowerCase();return s?!!(s.includes("deno:")||s.includes("deno")||s.includes("econnreset")||s.includes("econnrefused")||s.includes("etimedout")||s.includes("timeout")&&s.includes("request")||s.includes("upstream")&&(s.includes("deno")||s.includes("connect"))):!1}function se({status:e,requestKind:s,hadAccessToken:t,accessToken:a}={}){return e!==401||s!==h.business?!1:H({hadAccessToken:t,accessToken:a})}async function te(){return D||(D=Te.auth.getCurrentSession().then(({data:e})=>e?.session??null).catch(()=>null).finally(()=>{D=null}),D)}function Ae(e){return e===502||e===503||e===504}function Me(e){const s=String(e||"").toLowerCase();return s?!!(ee(s)||s.includes("econnreset")||s.includes("econnrefused")||s.includes("etimedout")||s.includes("timeout")||s.includes("networkerror")||s.includes("failed to fetch")||s.includes("socket hang up")||s.includes("connection reset")):!1}function ae(e,s){const a=(s||"GET").toUpperCase()==="GET"?{maxRetries:2,baseDelayMs:300,maxDelayMs:1500,jitterRatio:.2}:{maxRetries:0,baseDelayMs:0,maxDelayMs:0,jitterRatio:0};if(e==null)return a;if(e===!1)return{maxRetries:0,baseDelayMs:0,maxDelayMs:0,jitterRatio:0};const n=q(e.maxRetries??a.maxRetries,0,10),r=q(e.baseDelayMs??a.baseDelayMs,50,6e4),l=q(e.maxDelayMs??a.maxDelayMs,r,12e4),i=typeof e.jitterRatio=="number"?Math.max(0,Math.min(.5,e.jitterRatio)):a.jitterRatio;return{maxRetries:n,baseDelayMs:r,maxDelayMs:l,jitterRatio:i}}function E(e){return!!ye(e)}function Pe(e){if(!E(e))return!1;const s=e.split(".");return s.length!==3?!1:s.every(t=>/^[A-Za-z0-9_-]+$/.test(t))}function ne({err:e,attempt:s,retryOptions:t}={}){return!t||t.maxRetries<=0||s>=t.maxRetries?!1:!!(e&&e.retryable)}function re({retryOptions:e,attempt:s}={}){if(!e||e.maxRetries<=0)return 0;const t=e.baseDelayMs*Math.pow(2,s),a=Math.min(e.maxDelayMs,t),n=a*e.jitterRatio*Math.random();return Math.round(a+n)}function q(e,s,t){const a=Number(e);return Number.isFinite(a)?Math.min(t,Math.max(s,Math.floor(a))):s}function ie(e){return!e||e<=0?Promise.resolve():new Promise(s=>setTimeout(s,e))}const _={TOP_LEFT:"┌",TOP_RIGHT:"┐",BOTTOM_LEFT:"└",BOTTOM_RIGHT:"┘",HORIZONTAL:"─",VERTICAL:"│"};function Ge({title:e,subtitle:s,children:t,className:a="",bodyClassName:n=""}){return m.jsxs("div",{className:`relative flex flex-col matrix-panel ${a}`,children:[m.jsxs("div",{className:"flex items-center leading-none",children:[m.jsx("span",{className:"shrink-0 text-matrix-dim",children:_.TOP_LEFT}),m.jsx("span",{className:"mx-3 shrink-0 text-heading uppercase text-matrix-primary px-2 py-1 bg-matrix-panelStrong border border-matrix-ghost",children:e}),s?m.jsxs("span",{className:"text-caption text-matrix-muted mr-2 uppercase",children:["[",s,"]"]}):null,m.jsx("span",{className:"flex-1 overflow-hidden whitespace-nowrap text-matrix-ghost",children:_.HORIZONTAL.repeat(100)}),m.jsx("span",{className:"shrink-0 text-matrix-dim",children:_.TOP_RIGHT})]}),m.jsxs("div",{className:"flex flex-1",children:[m.jsx("div",{className:"shrink-0 w-3 flex justify-center text-matrix-ghost",children:_.VERTICAL}),m.jsx("div",{className:`flex-1 min-w-0 py-5 px-4 relative z-10 ${n}`,children:t}),m.jsx("div",{className:"shrink-0 w-3 flex justify-center text-matrix-ghost",children:_.VERTICAL})]}),m.jsxs("div",{className:"flex items-center leading-none text-matrix-ghost",children:[m.jsx("span",{className:"shrink-0",children:_.BOTTOM_LEFT}),m.jsx("span",{className:"flex-1 overflow-hidden whitespace-nowrap",children:_.HORIZONTAL.repeat(100)}),m.jsx("span",{className:"shrink-0",children:_.BOTTOM_RIGHT})]})]})}export{Ge as A,Be as a,je as b,Oe as c,Fe as d,Ce as e,Ue as f,qe as g,Ne as h,$e as i,He as j,Ie as k,Le as l,Ee as p,Ve as r,De as s,ze as t};
|