tokentracker-cli 0.2.27 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -28
- package/dashboard/dist/assets/DashboardPage-CdsMxGgP.js +12 -0
- package/dashboard/dist/assets/{LandingExtras-BUwK_c-9.js → LandingExtras-BPCBL0OB.js} +1 -1
- package/dashboard/dist/assets/LeaderboardPage-T0DeP6QE.js +1 -0
- package/dashboard/dist/assets/LeaderboardProfilePage-DeXNLaq5.js +1 -0
- package/dashboard/dist/assets/{MatrixRain-D7Pm88QF.js → MatrixRain-DNvnkted.js} +1 -1
- package/dashboard/dist/assets/MatrixShell-p6MtAS33.js +1 -0
- package/dashboard/dist/assets/{main-oIbJf8pA.js → main-CvjKTqMk.js} +10 -8
- package/dashboard/dist/assets/main-DJvWJCv5.css +1 -0
- package/dashboard/dist/assets/vibeusage-api-By1xP_lg.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 +52 -2
- package/src/lib/cursor-config.js +328 -0
- package/src/lib/local-api.js +108 -12
- package/src/lib/rollout.js +116 -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/local-api.js
CHANGED
|
@@ -57,6 +57,111 @@ function aggregateByDay(rows) {
|
|
|
57
57
|
return Array.from(byDay.values()).sort((a, b) => a.day.localeCompare(b.day));
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
function getTimeZoneContext(url) {
|
|
61
|
+
const tz = String(url.searchParams.get("tz") || "").trim();
|
|
62
|
+
const rawOffset = Number(url.searchParams.get("tz_offset_minutes"));
|
|
63
|
+
return {
|
|
64
|
+
timeZone: tz || null,
|
|
65
|
+
offsetMinutes: Number.isFinite(rawOffset) ? Math.trunc(rawOffset) : null,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getZonedParts(date, { timeZone, offsetMinutes } = {}) {
|
|
70
|
+
const dt = date instanceof Date ? date : new Date(date);
|
|
71
|
+
if (!Number.isFinite(dt.getTime())) return null;
|
|
72
|
+
|
|
73
|
+
if (timeZone && typeof Intl !== "undefined" && Intl.DateTimeFormat) {
|
|
74
|
+
try {
|
|
75
|
+
const formatter = new Intl.DateTimeFormat("en-CA", {
|
|
76
|
+
timeZone,
|
|
77
|
+
year: "numeric",
|
|
78
|
+
month: "2-digit",
|
|
79
|
+
day: "2-digit",
|
|
80
|
+
hour: "2-digit",
|
|
81
|
+
minute: "2-digit",
|
|
82
|
+
second: "2-digit",
|
|
83
|
+
hourCycle: "h23",
|
|
84
|
+
});
|
|
85
|
+
const parts = formatter.formatToParts(dt);
|
|
86
|
+
const values = parts.reduce((acc, part) => {
|
|
87
|
+
if (part.type && part.value) acc[part.type] = part.value;
|
|
88
|
+
return acc;
|
|
89
|
+
}, {});
|
|
90
|
+
const year = Number(values.year);
|
|
91
|
+
const month = Number(values.month);
|
|
92
|
+
const day = Number(values.day);
|
|
93
|
+
const hour = Number(values.hour);
|
|
94
|
+
const minute = Number(values.minute);
|
|
95
|
+
const second = Number(values.second);
|
|
96
|
+
if ([year, month, day, hour, minute, second].every(Number.isFinite)) {
|
|
97
|
+
return { year, month, day, hour, minute, second };
|
|
98
|
+
}
|
|
99
|
+
} catch (_e) {
|
|
100
|
+
// fall through
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (Number.isFinite(offsetMinutes)) {
|
|
105
|
+
const shifted = new Date(dt.getTime() + offsetMinutes * 60 * 1000);
|
|
106
|
+
return {
|
|
107
|
+
year: shifted.getUTCFullYear(),
|
|
108
|
+
month: shifted.getUTCMonth() + 1,
|
|
109
|
+
day: shifted.getUTCDate(),
|
|
110
|
+
hour: shifted.getUTCHours(),
|
|
111
|
+
minute: shifted.getUTCMinutes(),
|
|
112
|
+
second: shifted.getUTCSeconds(),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
year: dt.getFullYear(),
|
|
118
|
+
month: dt.getMonth() + 1,
|
|
119
|
+
day: dt.getDate(),
|
|
120
|
+
hour: dt.getHours(),
|
|
121
|
+
minute: dt.getMinutes(),
|
|
122
|
+
second: dt.getSeconds(),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function formatPartsDayKey(parts) {
|
|
127
|
+
if (!parts) return "";
|
|
128
|
+
return `${parts.year}-${String(parts.month).padStart(2, "0")}-${String(parts.day).padStart(2, "0")}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function aggregateHourlyByDay(rows, dayKey, timeZoneContext) {
|
|
132
|
+
const byHour = new Map();
|
|
133
|
+
for (const row of rows) {
|
|
134
|
+
if (!row.hour_start) continue;
|
|
135
|
+
const parts = getZonedParts(new Date(row.hour_start), timeZoneContext);
|
|
136
|
+
if (!parts) continue;
|
|
137
|
+
if (formatPartsDayKey(parts) !== dayKey) continue;
|
|
138
|
+
const hourKey = `${dayKey}T${String(parts.hour).padStart(2, "0")}:00:00`;
|
|
139
|
+
if (!byHour.has(hourKey)) {
|
|
140
|
+
byHour.set(hourKey, {
|
|
141
|
+
hour: hourKey,
|
|
142
|
+
total_tokens: 0,
|
|
143
|
+
billable_total_tokens: 0,
|
|
144
|
+
input_tokens: 0,
|
|
145
|
+
output_tokens: 0,
|
|
146
|
+
cached_input_tokens: 0,
|
|
147
|
+
cache_creation_input_tokens: 0,
|
|
148
|
+
reasoning_output_tokens: 0,
|
|
149
|
+
conversation_count: 0,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
const bucket = byHour.get(hourKey);
|
|
153
|
+
bucket.total_tokens += row.total_tokens || 0;
|
|
154
|
+
bucket.billable_total_tokens += row.total_tokens || 0;
|
|
155
|
+
bucket.input_tokens += row.input_tokens || 0;
|
|
156
|
+
bucket.output_tokens += row.output_tokens || 0;
|
|
157
|
+
bucket.cached_input_tokens += row.cached_input_tokens || 0;
|
|
158
|
+
bucket.cache_creation_input_tokens += row.cache_creation_input_tokens || 0;
|
|
159
|
+
bucket.reasoning_output_tokens += row.reasoning_output_tokens || 0;
|
|
160
|
+
bucket.conversation_count += row.conversation_count || 0;
|
|
161
|
+
}
|
|
162
|
+
return Array.from(byHour.values()).sort((a, b) => a.hour.localeCompare(b.hour));
|
|
163
|
+
}
|
|
164
|
+
|
|
60
165
|
// ---------------------------------------------------------------------------
|
|
61
166
|
// Sync helper
|
|
62
167
|
// ---------------------------------------------------------------------------
|
|
@@ -461,18 +566,9 @@ function createLocalApiHandler({ queuePath }) {
|
|
|
461
566
|
// --- usage-hourly (stub for day-view) ---
|
|
462
567
|
if (p === "/functions/vibeusage-usage-hourly") {
|
|
463
568
|
const day = url.searchParams.get("day") || new Date().toISOString().slice(0, 10);
|
|
464
|
-
const
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
total_tokens: r.total_tokens || 0,
|
|
468
|
-
billable_total_tokens: r.total_tokens || 0,
|
|
469
|
-
input_tokens: r.input_tokens || 0,
|
|
470
|
-
output_tokens: r.output_tokens || 0,
|
|
471
|
-
cached_input_tokens: r.cached_input_tokens || 0,
|
|
472
|
-
cache_creation_input_tokens: r.cache_creation_input_tokens || 0,
|
|
473
|
-
reasoning_output_tokens: r.reasoning_output_tokens || 0,
|
|
474
|
-
conversation_count: r.conversation_count || 0,
|
|
475
|
-
}));
|
|
569
|
+
const timeZoneContext = getTimeZoneContext(url);
|
|
570
|
+
const rows = readQueueData(qp);
|
|
571
|
+
const data = aggregateHourlyByDay(rows, day, timeZoneContext);
|
|
476
572
|
json(res, { day, data });
|
|
477
573
|
return true;
|
|
478
574
|
}
|
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,6 +2514,88 @@ 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
|
+
|
|
2486
2599
|
module.exports = {
|
|
2487
2600
|
listRolloutFiles,
|
|
2488
2601
|
listClaudeProjectFiles,
|
|
@@ -2495,4 +2608,5 @@ module.exports = {
|
|
|
2495
2608
|
parseOpencodeIncremental,
|
|
2496
2609
|
parseOpencodeDbIncremental,
|
|
2497
2610
|
parseOpenclawIncremental,
|
|
2611
|
+
parseCursorApiIncremental,
|
|
2498
2612
|
};
|
|
@@ -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};
|