tokenleak 2.0.0 → 2.1.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 +39 -6
- package/package.json +1 -1
- package/tokenleak +1092 -195
package/tokenleak
CHANGED
|
@@ -1657,6 +1657,179 @@ var init_focus = __esm(() => {
|
|
|
1657
1657
|
};
|
|
1658
1658
|
});
|
|
1659
1659
|
|
|
1660
|
+
// packages/core/dist/aggregation/replay.js
|
|
1661
|
+
function parseIsoTime2(value) {
|
|
1662
|
+
const parsed = Date.parse(value);
|
|
1663
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
1664
|
+
}
|
|
1665
|
+
function truncateToMinute(iso) {
|
|
1666
|
+
const date = new Date(iso);
|
|
1667
|
+
date.setSeconds(0, 0);
|
|
1668
|
+
return date.toISOString();
|
|
1669
|
+
}
|
|
1670
|
+
function labelFlowBlock(durationMs, eventCount) {
|
|
1671
|
+
if (durationMs >= DEEP_FLOW_DURATION_MS || eventCount >= DEEP_FLOW_EVENT_COUNT) {
|
|
1672
|
+
return "Deep Flow";
|
|
1673
|
+
}
|
|
1674
|
+
if (durationMs <= QUICK_LOOKUP_DURATION_MS && eventCount <= QUICK_LOOKUP_EVENT_COUNT) {
|
|
1675
|
+
return "Quick Lookup";
|
|
1676
|
+
}
|
|
1677
|
+
return "Moderate Session";
|
|
1678
|
+
}
|
|
1679
|
+
function computeDominantModel(events) {
|
|
1680
|
+
const byModel = new Map;
|
|
1681
|
+
for (const event of events) {
|
|
1682
|
+
byModel.set(event.model, (byModel.get(event.model) ?? 0) + event.totalTokens);
|
|
1683
|
+
}
|
|
1684
|
+
let best = "";
|
|
1685
|
+
let bestTokens = -1;
|
|
1686
|
+
for (const [model, tokens] of byModel) {
|
|
1687
|
+
if (tokens > bestTokens || tokens === bestTokens && model < best) {
|
|
1688
|
+
best = model;
|
|
1689
|
+
bestTokens = tokens;
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
return best;
|
|
1693
|
+
}
|
|
1694
|
+
function countModelSwitches(events) {
|
|
1695
|
+
let switches = 0;
|
|
1696
|
+
for (let i = 1;i < events.length; i++) {
|
|
1697
|
+
if (events[i].model !== events[i - 1].model) {
|
|
1698
|
+
switches++;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
return switches;
|
|
1702
|
+
}
|
|
1703
|
+
function computeCacheHitRateTrend(events) {
|
|
1704
|
+
return events.map((event) => {
|
|
1705
|
+
const denominator = event.inputTokens + event.cacheReadTokens;
|
|
1706
|
+
return denominator > 0 ? event.cacheReadTokens / denominator : 0;
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
function clusterIntoFlowBlocks(sortedEvents) {
|
|
1710
|
+
if (sortedEvents.length === 0) {
|
|
1711
|
+
return [];
|
|
1712
|
+
}
|
|
1713
|
+
const blocks = [];
|
|
1714
|
+
let currentEvents = [sortedEvents[0]];
|
|
1715
|
+
for (let i = 1;i < sortedEvents.length; i++) {
|
|
1716
|
+
const prevTime = parseIsoTime2(sortedEvents[i - 1].timestamp);
|
|
1717
|
+
const currTime = parseIsoTime2(sortedEvents[i].timestamp);
|
|
1718
|
+
if (prevTime !== null && currTime !== null && currTime - prevTime >= FLOW_BLOCK_GAP_MS) {
|
|
1719
|
+
blocks.push(buildFlowBlock(currentEvents, blocks.length));
|
|
1720
|
+
currentEvents = [];
|
|
1721
|
+
}
|
|
1722
|
+
currentEvents.push(sortedEvents[i]);
|
|
1723
|
+
}
|
|
1724
|
+
blocks.push(buildFlowBlock(currentEvents, blocks.length));
|
|
1725
|
+
return blocks;
|
|
1726
|
+
}
|
|
1727
|
+
function buildFlowBlock(events, blockIndex) {
|
|
1728
|
+
const startTime = parseIsoTime2(events[0].timestamp) ?? 0;
|
|
1729
|
+
const endTime = parseIsoTime2(events[events.length - 1].timestamp) ?? 0;
|
|
1730
|
+
const durationMs = endTime - startTime;
|
|
1731
|
+
let inputTokens = 0;
|
|
1732
|
+
let outputTokens = 0;
|
|
1733
|
+
let cacheReadTokens = 0;
|
|
1734
|
+
let cacheWriteTokens = 0;
|
|
1735
|
+
let totalTokens = 0;
|
|
1736
|
+
let cost = 0;
|
|
1737
|
+
for (const event of events) {
|
|
1738
|
+
inputTokens += event.inputTokens;
|
|
1739
|
+
outputTokens += event.outputTokens;
|
|
1740
|
+
cacheReadTokens += event.cacheReadTokens;
|
|
1741
|
+
cacheWriteTokens += event.cacheWriteTokens;
|
|
1742
|
+
totalTokens += event.totalTokens;
|
|
1743
|
+
cost += event.cost;
|
|
1744
|
+
}
|
|
1745
|
+
return {
|
|
1746
|
+
blockIndex,
|
|
1747
|
+
label: labelFlowBlock(durationMs, events.length),
|
|
1748
|
+
start: events[0].timestamp,
|
|
1749
|
+
end: events[events.length - 1].timestamp,
|
|
1750
|
+
durationMs,
|
|
1751
|
+
eventCount: events.length,
|
|
1752
|
+
inputTokens,
|
|
1753
|
+
outputTokens,
|
|
1754
|
+
cacheReadTokens,
|
|
1755
|
+
cacheWriteTokens,
|
|
1756
|
+
totalTokens,
|
|
1757
|
+
cost,
|
|
1758
|
+
dominantModel: computeDominantModel(events),
|
|
1759
|
+
events,
|
|
1760
|
+
modelSwitches: countModelSwitches(events),
|
|
1761
|
+
cacheHitRateTrend: computeCacheHitRateTrend(events)
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1764
|
+
function buildTokenVelocity(sortedEvents) {
|
|
1765
|
+
const buckets = new Map;
|
|
1766
|
+
for (const event of sortedEvents) {
|
|
1767
|
+
const minute = truncateToMinute(event.timestamp);
|
|
1768
|
+
buckets.set(minute, (buckets.get(minute) ?? 0) + event.totalTokens);
|
|
1769
|
+
}
|
|
1770
|
+
return [...buckets.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([minute, tokens]) => ({
|
|
1771
|
+
minute,
|
|
1772
|
+
tokensPerMinute: tokens / (VELOCITY_BUCKET_MS / 1000 / 60)
|
|
1773
|
+
}));
|
|
1774
|
+
}
|
|
1775
|
+
function buildDaySummary(sortedEvents, flowBlocks, tokenVelocity) {
|
|
1776
|
+
const sessionIds = new Set;
|
|
1777
|
+
for (const event of sortedEvents) {
|
|
1778
|
+
if (event.sessionId) {
|
|
1779
|
+
sessionIds.add(event.sessionId);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
let flowTimeMs = 0;
|
|
1783
|
+
for (const block of flowBlocks) {
|
|
1784
|
+
flowTimeMs += block.durationMs;
|
|
1785
|
+
}
|
|
1786
|
+
let thinkTimeMs = 0;
|
|
1787
|
+
for (let i = 1;i < flowBlocks.length; i++) {
|
|
1788
|
+
const prevEnd = parseIsoTime2(flowBlocks[i - 1].end);
|
|
1789
|
+
const currStart = parseIsoTime2(flowBlocks[i].start);
|
|
1790
|
+
if (prevEnd !== null && currStart !== null) {
|
|
1791
|
+
thinkTimeMs += currStart - prevEnd;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
const totalTimeMs = flowTimeMs + thinkTimeMs;
|
|
1795
|
+
const flowThinkRatio = totalTimeMs > 0 ? flowTimeMs / totalTimeMs : 0;
|
|
1796
|
+
let peakMinute = null;
|
|
1797
|
+
for (const point of tokenVelocity) {
|
|
1798
|
+
if (peakMinute === null || point.tokensPerMinute > peakMinute.tokensPerMinute) {
|
|
1799
|
+
peakMinute = point;
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
return {
|
|
1803
|
+
totalSessions: sessionIds.size,
|
|
1804
|
+
totalEvents: sortedEvents.length,
|
|
1805
|
+
flowTimeMs,
|
|
1806
|
+
thinkTimeMs,
|
|
1807
|
+
flowThinkRatio,
|
|
1808
|
+
peakMinute
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
function buildReplayReport(providers, targetDate) {
|
|
1812
|
+
const allEvents = providers.flatMap((provider) => provider.events ?? []);
|
|
1813
|
+
const dayEvents = allEvents.filter((event) => event.date === targetDate).sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
1814
|
+
const flowBlocks = clusterIntoFlowBlocks(dayEvents);
|
|
1815
|
+
const tokenVelocity = buildTokenVelocity(dayEvents);
|
|
1816
|
+
const summary = buildDaySummary(dayEvents, flowBlocks, tokenVelocity);
|
|
1817
|
+
return {
|
|
1818
|
+
date: targetDate,
|
|
1819
|
+
events: dayEvents,
|
|
1820
|
+
flowBlocks,
|
|
1821
|
+
tokenVelocity,
|
|
1822
|
+
summary
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1825
|
+
var FLOW_BLOCK_GAP_MS, DEEP_FLOW_DURATION_MS, DEEP_FLOW_EVENT_COUNT = 5, QUICK_LOOKUP_DURATION_MS, QUICK_LOOKUP_EVENT_COUNT = 2, VELOCITY_BUCKET_MS;
|
|
1826
|
+
var init_replay = __esm(() => {
|
|
1827
|
+
FLOW_BLOCK_GAP_MS = 15 * 60 * 1000;
|
|
1828
|
+
DEEP_FLOW_DURATION_MS = 45 * 60 * 1000;
|
|
1829
|
+
QUICK_LOOKUP_DURATION_MS = 10 * 60 * 1000;
|
|
1830
|
+
VELOCITY_BUCKET_MS = 60 * 1000;
|
|
1831
|
+
});
|
|
1832
|
+
|
|
1660
1833
|
// packages/core/dist/aggregation/index.js
|
|
1661
1834
|
var init_aggregation = __esm(() => {
|
|
1662
1835
|
init_streaks();
|
|
@@ -1668,6 +1841,7 @@ var init_aggregation = __esm(() => {
|
|
|
1668
1841
|
init_more();
|
|
1669
1842
|
init_explain();
|
|
1670
1843
|
init_focus();
|
|
1844
|
+
init_replay();
|
|
1671
1845
|
init_analytics();
|
|
1672
1846
|
});
|
|
1673
1847
|
|
|
@@ -1989,7 +2163,7 @@ var init_advisor2 = __esm(() => {
|
|
|
1989
2163
|
});
|
|
1990
2164
|
|
|
1991
2165
|
// packages/core/dist/index.js
|
|
1992
|
-
var VERSION = "2.
|
|
2166
|
+
var VERSION = "2.1.0";
|
|
1993
2167
|
var init_dist = __esm(() => {
|
|
1994
2168
|
init_constants();
|
|
1995
2169
|
init_aggregation();
|
|
@@ -2006,12 +2180,165 @@ var init_normalizer = __esm(() => {
|
|
|
2006
2180
|
DATE_SUFFIX_PATTERN = /-\d{8}$/;
|
|
2007
2181
|
});
|
|
2008
2182
|
|
|
2183
|
+
// packages/registry/dist/models/litellm.js
|
|
2184
|
+
function parseLiteLLMData(raw) {
|
|
2185
|
+
const result = {};
|
|
2186
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
2187
|
+
if (value === null || typeof value !== "object")
|
|
2188
|
+
continue;
|
|
2189
|
+
const entry = value;
|
|
2190
|
+
const inputPerToken = entry.input_cost_per_token;
|
|
2191
|
+
const outputPerToken = entry.output_cost_per_token;
|
|
2192
|
+
if (typeof inputPerToken !== "number" || inputPerToken <= 0)
|
|
2193
|
+
continue;
|
|
2194
|
+
if (typeof outputPerToken !== "number")
|
|
2195
|
+
continue;
|
|
2196
|
+
const pricing = {
|
|
2197
|
+
input: inputPerToken * PER_TOKEN_TO_PER_MILLION,
|
|
2198
|
+
output: outputPerToken * PER_TOKEN_TO_PER_MILLION,
|
|
2199
|
+
cacheRead: typeof entry.cache_read_input_token_cost === "number" ? entry.cache_read_input_token_cost * PER_TOKEN_TO_PER_MILLION : 0,
|
|
2200
|
+
cacheWrite: typeof entry.cache_creation_input_token_cost === "number" ? entry.cache_creation_input_token_cost * PER_TOKEN_TO_PER_MILLION : 0
|
|
2201
|
+
};
|
|
2202
|
+
result[key] = pricing;
|
|
2203
|
+
const slashIndex = key.indexOf("/");
|
|
2204
|
+
const unprefixed = slashIndex !== -1 ? key.slice(slashIndex + 1) : key;
|
|
2205
|
+
result[unprefixed] = pricing;
|
|
2206
|
+
const normalized = normalizeModelName2(unprefixed);
|
|
2207
|
+
result[normalized] = pricing;
|
|
2208
|
+
}
|
|
2209
|
+
return result;
|
|
2210
|
+
}
|
|
2211
|
+
async function fetchLiteLLMPricing() {
|
|
2212
|
+
const controller = new AbortController;
|
|
2213
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
2214
|
+
try {
|
|
2215
|
+
const response = await fetch(LITELLM_URL, { signal: controller.signal });
|
|
2216
|
+
if (!response.ok) {
|
|
2217
|
+
throw new Error(`LiteLLM fetch failed: HTTP ${response.status}`);
|
|
2218
|
+
}
|
|
2219
|
+
const raw = await response.json();
|
|
2220
|
+
return parseLiteLLMData(raw);
|
|
2221
|
+
} finally {
|
|
2222
|
+
clearTimeout(timeout);
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
var LITELLM_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json", FETCH_TIMEOUT_MS = 1e4, PER_TOKEN_TO_PER_MILLION = 1e6;
|
|
2226
|
+
var init_litellm = __esm(() => {
|
|
2227
|
+
init_normalizer();
|
|
2228
|
+
});
|
|
2229
|
+
|
|
2230
|
+
// packages/registry/dist/models/pricing-cache.js
|
|
2231
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
2232
|
+
import { homedir, tmpdir } from "os";
|
|
2233
|
+
import { join } from "path";
|
|
2234
|
+
function defaultCacheDir() {
|
|
2235
|
+
try {
|
|
2236
|
+
const home = homedir();
|
|
2237
|
+
return join(home, ".cache", "tokenleak");
|
|
2238
|
+
} catch {
|
|
2239
|
+
return join(tmpdir(), "tokenleak-cache");
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
function cachePath(cacheDir) {
|
|
2243
|
+
return join(cacheDir ?? defaultCacheDir(), CACHE_FILENAME);
|
|
2244
|
+
}
|
|
2245
|
+
function isCacheValid(envelope) {
|
|
2246
|
+
return envelope.version === CACHE_VERSION && Date.now() - envelope.fetchedAt < CACHE_TTL_MS;
|
|
2247
|
+
}
|
|
2248
|
+
function readEnvelope(cacheDir) {
|
|
2249
|
+
const path = cachePath(cacheDir);
|
|
2250
|
+
try {
|
|
2251
|
+
if (!existsSync(path))
|
|
2252
|
+
return null;
|
|
2253
|
+
const raw = readFileSync(path, "utf-8");
|
|
2254
|
+
const parsed = JSON.parse(raw);
|
|
2255
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.version !== "number" || typeof parsed.fetchedAt !== "number" || typeof parsed.data !== "object") {
|
|
2256
|
+
return null;
|
|
2257
|
+
}
|
|
2258
|
+
return parsed;
|
|
2259
|
+
} catch {
|
|
2260
|
+
return null;
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
function readPricingCache(cacheDir) {
|
|
2264
|
+
const envelope = readEnvelope(cacheDir);
|
|
2265
|
+
if (envelope && isCacheValid(envelope))
|
|
2266
|
+
return envelope;
|
|
2267
|
+
return null;
|
|
2268
|
+
}
|
|
2269
|
+
function readStalePricingCache(cacheDir) {
|
|
2270
|
+
const envelope = readEnvelope(cacheDir);
|
|
2271
|
+
return envelope?.data ?? null;
|
|
2272
|
+
}
|
|
2273
|
+
function writePricingCache(data, cacheDir) {
|
|
2274
|
+
const dir = cacheDir ?? defaultCacheDir();
|
|
2275
|
+
const path = join(dir, CACHE_FILENAME);
|
|
2276
|
+
const tmpPath = `${path}.tmp`;
|
|
2277
|
+
try {
|
|
2278
|
+
mkdirSync(dir, { recursive: true });
|
|
2279
|
+
const envelope = {
|
|
2280
|
+
version: CACHE_VERSION,
|
|
2281
|
+
fetchedAt: Date.now(),
|
|
2282
|
+
data
|
|
2283
|
+
};
|
|
2284
|
+
writeFileSync(tmpPath, JSON.stringify(envelope), "utf-8");
|
|
2285
|
+
renameSync(tmpPath, path);
|
|
2286
|
+
} catch {
|
|
2287
|
+
try {
|
|
2288
|
+
rmSync(tmpPath, { force: true });
|
|
2289
|
+
} catch {}
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
var CACHE_FILENAME = "pricing.json", CACHE_TTL_MS = 3600000, CACHE_VERSION = 1;
|
|
2293
|
+
var init_pricing_cache = () => {};
|
|
2294
|
+
|
|
2295
|
+
// packages/registry/dist/models/pricing-resolver.js
|
|
2296
|
+
async function initPricing() {
|
|
2297
|
+
if (initialized)
|
|
2298
|
+
return;
|
|
2299
|
+
try {
|
|
2300
|
+
const cached = readPricingCache();
|
|
2301
|
+
if (cached) {
|
|
2302
|
+
remotePricing = cached.data;
|
|
2303
|
+
initialized = true;
|
|
2304
|
+
return;
|
|
2305
|
+
}
|
|
2306
|
+
} catch {}
|
|
2307
|
+
try {
|
|
2308
|
+
const data = await fetchLiteLLMPricing();
|
|
2309
|
+
remotePricing = data;
|
|
2310
|
+
try {
|
|
2311
|
+
writePricingCache(data);
|
|
2312
|
+
} catch {}
|
|
2313
|
+
initialized = true;
|
|
2314
|
+
return;
|
|
2315
|
+
} catch {}
|
|
2316
|
+
try {
|
|
2317
|
+
const stale = readStalePricingCache();
|
|
2318
|
+
if (stale) {
|
|
2319
|
+
remotePricing = stale;
|
|
2320
|
+
initialized = true;
|
|
2321
|
+
return;
|
|
2322
|
+
}
|
|
2323
|
+
} catch {}
|
|
2324
|
+
initialized = true;
|
|
2325
|
+
}
|
|
2326
|
+
function getRemotePricing(model) {
|
|
2327
|
+
return remotePricing?.[model];
|
|
2328
|
+
}
|
|
2329
|
+
var remotePricing = null, initialized = false;
|
|
2330
|
+
var init_pricing_resolver = __esm(() => {
|
|
2331
|
+
init_litellm();
|
|
2332
|
+
init_pricing_cache();
|
|
2333
|
+
});
|
|
2334
|
+
|
|
2009
2335
|
// packages/registry/dist/models/pricing.js
|
|
2010
2336
|
function getModelPricing(model) {
|
|
2011
|
-
return MODEL_PRICING[model];
|
|
2337
|
+
return getRemotePricing(model) ?? MODEL_PRICING[model];
|
|
2012
2338
|
}
|
|
2013
2339
|
var TOKENS_PER_MILLION3 = 1e6, MODEL_PRICING;
|
|
2014
2340
|
var init_pricing = __esm(() => {
|
|
2341
|
+
init_pricing_resolver();
|
|
2015
2342
|
MODEL_PRICING = {
|
|
2016
2343
|
"claude-3-haiku": {
|
|
2017
2344
|
input: 0.25,
|
|
@@ -2217,6 +2544,7 @@ var init_models = __esm(() => {
|
|
|
2217
2544
|
init_normalizer();
|
|
2218
2545
|
init_pricing();
|
|
2219
2546
|
init_cost();
|
|
2547
|
+
init_pricing_resolver();
|
|
2220
2548
|
});
|
|
2221
2549
|
|
|
2222
2550
|
// packages/registry/dist/registry.js
|
|
@@ -2313,24 +2641,24 @@ function isInRange(date, range) {
|
|
|
2313
2641
|
}
|
|
2314
2642
|
|
|
2315
2643
|
// packages/registry/dist/providers/claude-code.js
|
|
2316
|
-
import { existsSync, readdirSync, statSync } from "fs";
|
|
2317
|
-
import { dirname as dirname2, join, relative as relative2, sep } from "path";
|
|
2318
|
-
import { homedir } from "os";
|
|
2644
|
+
import { existsSync as existsSync2, readdirSync, statSync } from "fs";
|
|
2645
|
+
import { dirname as dirname2, join as join2, relative as relative2, sep } from "path";
|
|
2646
|
+
import { homedir as homedir2 } from "os";
|
|
2319
2647
|
function resolveBaseDir(baseDir) {
|
|
2320
2648
|
if (baseDir) {
|
|
2321
2649
|
return baseDir;
|
|
2322
2650
|
}
|
|
2323
2651
|
const configDir = process.env["CLAUDE_CONFIG_DIR"];
|
|
2324
|
-
return
|
|
2652
|
+
return join2(configDir && configDir.length > 0 ? configDir : DEFAULT_CONFIG_DIR, "projects");
|
|
2325
2653
|
}
|
|
2326
2654
|
function collectJsonlFiles(dir) {
|
|
2327
2655
|
const results = [];
|
|
2328
|
-
if (!
|
|
2656
|
+
if (!existsSync2(dir)) {
|
|
2329
2657
|
return results;
|
|
2330
2658
|
}
|
|
2331
2659
|
const entries = readdirSync(dir);
|
|
2332
2660
|
for (const entry of entries) {
|
|
2333
|
-
const fullPath =
|
|
2661
|
+
const fullPath = join2(dir, entry);
|
|
2334
2662
|
const stat = statSync(fullPath);
|
|
2335
2663
|
if (stat.isDirectory()) {
|
|
2336
2664
|
results.push(...collectJsonlFiles(fullPath));
|
|
@@ -2467,7 +2795,7 @@ class ClaudeCodeProvider {
|
|
|
2467
2795
|
}
|
|
2468
2796
|
async isAvailable() {
|
|
2469
2797
|
try {
|
|
2470
|
-
return
|
|
2798
|
+
return existsSync2(this.baseDir);
|
|
2471
2799
|
} catch {
|
|
2472
2800
|
return false;
|
|
2473
2801
|
}
|
|
@@ -2537,7 +2865,7 @@ var init_claude_code = __esm(() => {
|
|
|
2537
2865
|
init_jsonl_splitter();
|
|
2538
2866
|
init_normalizer();
|
|
2539
2867
|
init_cost();
|
|
2540
|
-
DEFAULT_CONFIG_DIR =
|
|
2868
|
+
DEFAULT_CONFIG_DIR = join2(homedir2(), ".claude");
|
|
2541
2869
|
CLAUDE_CODE_COLORS = {
|
|
2542
2870
|
primary: "#ff6b35",
|
|
2543
2871
|
secondary: "#ffa366",
|
|
@@ -2546,9 +2874,9 @@ var init_claude_code = __esm(() => {
|
|
|
2546
2874
|
});
|
|
2547
2875
|
|
|
2548
2876
|
// packages/registry/dist/providers/codex.js
|
|
2549
|
-
import { existsSync as
|
|
2550
|
-
import { dirname as dirname3, join as
|
|
2551
|
-
import { homedir as
|
|
2877
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
2878
|
+
import { dirname as dirname3, join as join3, relative as relative3, sep as sep2 } from "path";
|
|
2879
|
+
import { homedir as homedir3 } from "os";
|
|
2552
2880
|
function parseResponseEvent(record) {
|
|
2553
2881
|
if (typeof record !== "object" || record === null || !("type" in record)) {
|
|
2554
2882
|
return null;
|
|
@@ -2593,12 +2921,12 @@ function toCachePricing2(pricing) {
|
|
|
2593
2921
|
};
|
|
2594
2922
|
}
|
|
2595
2923
|
function collectJsonlFiles2(dir) {
|
|
2596
|
-
if (!
|
|
2924
|
+
if (!existsSync3(dir)) {
|
|
2597
2925
|
return [];
|
|
2598
2926
|
}
|
|
2599
2927
|
const files = [];
|
|
2600
2928
|
for (const entry of readdirSync2(dir)) {
|
|
2601
|
-
const fullPath =
|
|
2929
|
+
const fullPath = join3(dir, entry);
|
|
2602
2930
|
const stats = statSync2(fullPath);
|
|
2603
2931
|
if (stats.isDirectory()) {
|
|
2604
2932
|
files.push(...collectJsonlFiles2(fullPath));
|
|
@@ -2758,7 +3086,7 @@ class CodexProvider {
|
|
|
2758
3086
|
}
|
|
2759
3087
|
async isAvailable() {
|
|
2760
3088
|
try {
|
|
2761
|
-
return
|
|
3089
|
+
return existsSync3(this.sessionsDir);
|
|
2762
3090
|
} catch {
|
|
2763
3091
|
return false;
|
|
2764
3092
|
}
|
|
@@ -2879,16 +3207,16 @@ var init_codex = __esm(() => {
|
|
|
2879
3207
|
secondary: "#4ade80",
|
|
2880
3208
|
gradient: ["#10a37f", "#4ade80"]
|
|
2881
3209
|
};
|
|
2882
|
-
DEFAULT_SESSIONS_DIR =
|
|
3210
|
+
DEFAULT_SESSIONS_DIR = join3(process.env["CODEX_HOME"] ?? join3(homedir3(), ".codex"), "sessions");
|
|
2883
3211
|
DASHED_DATE_SUFFIX = /-(\d{4})-(\d{2})-(\d{2})$/;
|
|
2884
3212
|
});
|
|
2885
3213
|
|
|
2886
3214
|
// packages/registry/dist/providers/cursor.js
|
|
2887
|
-
import { existsSync as
|
|
2888
|
-
import { join as
|
|
2889
|
-
import { homedir as
|
|
3215
|
+
import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync2, statSync as statSync3 } from "fs";
|
|
3216
|
+
import { join as join4 } from "path";
|
|
3217
|
+
import { homedir as homedir4 } from "os";
|
|
2890
3218
|
function resolveCacheDir(baseDir) {
|
|
2891
|
-
return baseDir ??
|
|
3219
|
+
return baseDir ?? join4(process.env["TOKENLEAK_CURSOR_DIR"] ?? join4(homedir4(), ".config", "tokenleak"), "cursor-cache");
|
|
2892
3220
|
}
|
|
2893
3221
|
function isCursorUsageFile(name) {
|
|
2894
3222
|
if (name === "usage.csv") {
|
|
@@ -2901,7 +3229,7 @@ function isCursorUsageFile(name) {
|
|
|
2901
3229
|
return stem.length > 0;
|
|
2902
3230
|
}
|
|
2903
3231
|
function collectUsageFiles(dir) {
|
|
2904
|
-
if (!
|
|
3232
|
+
if (!existsSync4(dir)) {
|
|
2905
3233
|
return [];
|
|
2906
3234
|
}
|
|
2907
3235
|
const files = [];
|
|
@@ -2909,7 +3237,7 @@ function collectUsageFiles(dir) {
|
|
|
2909
3237
|
if (entry === "archive") {
|
|
2910
3238
|
continue;
|
|
2911
3239
|
}
|
|
2912
|
-
const fullPath =
|
|
3240
|
+
const fullPath = join4(dir, entry);
|
|
2913
3241
|
const stats = statSync3(fullPath);
|
|
2914
3242
|
if (stats.isFile() && isCursorUsageFile(entry)) {
|
|
2915
3243
|
files.push(fullPath);
|
|
@@ -2985,7 +3313,7 @@ function toCachePricing3(pricing) {
|
|
|
2985
3313
|
function parseUsageFile(filePath) {
|
|
2986
3314
|
let raw;
|
|
2987
3315
|
try {
|
|
2988
|
-
raw =
|
|
3316
|
+
raw = readFileSync2(filePath, "utf8");
|
|
2989
3317
|
} catch {
|
|
2990
3318
|
return [];
|
|
2991
3319
|
}
|
|
@@ -3173,9 +3501,9 @@ var init_cursor = __esm(() => {
|
|
|
3173
3501
|
});
|
|
3174
3502
|
|
|
3175
3503
|
// packages/registry/dist/providers/open-code.js
|
|
3176
|
-
import { existsSync as
|
|
3177
|
-
import { join as
|
|
3178
|
-
import { homedir as
|
|
3504
|
+
import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync3 } from "fs";
|
|
3505
|
+
import { join as join5 } from "path";
|
|
3506
|
+
import { homedir as homedir5 } from "os";
|
|
3179
3507
|
import { Database } from "bun:sqlite";
|
|
3180
3508
|
function resolveBaseDir2(baseDir) {
|
|
3181
3509
|
if (baseDir) {
|
|
@@ -3186,7 +3514,7 @@ function resolveBaseDir2(baseDir) {
|
|
|
3186
3514
|
LEGACY_DEFAULT_BASE_DIR,
|
|
3187
3515
|
CONFIG_DEFAULT_BASE_DIR
|
|
3188
3516
|
]) {
|
|
3189
|
-
if (
|
|
3517
|
+
if (existsSync5(candidate)) {
|
|
3190
3518
|
return candidate;
|
|
3191
3519
|
}
|
|
3192
3520
|
}
|
|
@@ -3370,7 +3698,7 @@ function loadFromLegacyJson(sessionsDir, range) {
|
|
|
3370
3698
|
const records = [];
|
|
3371
3699
|
for (const file of files) {
|
|
3372
3700
|
try {
|
|
3373
|
-
const content =
|
|
3701
|
+
const content = readFileSync3(join5(sessionsDir, file), "utf-8");
|
|
3374
3702
|
const session = JSON.parse(content);
|
|
3375
3703
|
if (!Array.isArray(session.messages)) {
|
|
3376
3704
|
continue;
|
|
@@ -3401,14 +3729,14 @@ function loadFromLegacyJson(sessionsDir, range) {
|
|
|
3401
3729
|
return records;
|
|
3402
3730
|
}
|
|
3403
3731
|
function loadFromCurrentStorage(baseDir, range) {
|
|
3404
|
-
const messagesRoot =
|
|
3405
|
-
if (!
|
|
3732
|
+
const messagesRoot = join5(baseDir, "storage", "message");
|
|
3733
|
+
if (!existsSync5(messagesRoot)) {
|
|
3406
3734
|
return [];
|
|
3407
3735
|
}
|
|
3408
3736
|
const recordsById = new Map;
|
|
3409
3737
|
const recordsWithoutId = [];
|
|
3410
3738
|
for (const sessionDir of readdirSync4(messagesRoot)) {
|
|
3411
|
-
const sessionPath =
|
|
3739
|
+
const sessionPath = join5(messagesRoot, sessionDir);
|
|
3412
3740
|
let messageFiles;
|
|
3413
3741
|
try {
|
|
3414
3742
|
messageFiles = readdirSync4(sessionPath).filter((file) => file.endsWith(".json"));
|
|
@@ -3417,7 +3745,7 @@ function loadFromCurrentStorage(baseDir, range) {
|
|
|
3417
3745
|
}
|
|
3418
3746
|
for (const file of messageFiles) {
|
|
3419
3747
|
try {
|
|
3420
|
-
const content =
|
|
3748
|
+
const content = readFileSync3(join5(sessionPath, file), "utf-8");
|
|
3421
3749
|
const message = JSON.parse(content);
|
|
3422
3750
|
if (message.role !== "assistant") {
|
|
3423
3751
|
continue;
|
|
@@ -3480,32 +3808,32 @@ class OpenCodeProvider {
|
|
|
3480
3808
|
}
|
|
3481
3809
|
async isAvailable() {
|
|
3482
3810
|
try {
|
|
3483
|
-
if (!
|
|
3811
|
+
if (!existsSync5(this.baseDir)) {
|
|
3484
3812
|
return false;
|
|
3485
3813
|
}
|
|
3486
|
-
const hasCurrentStorage =
|
|
3487
|
-
const hasLegacyDb =
|
|
3488
|
-
const hasLegacySessionsDir =
|
|
3814
|
+
const hasCurrentStorage = existsSync5(join5(this.baseDir, "storage", "message"));
|
|
3815
|
+
const hasLegacyDb = existsSync5(join5(this.baseDir, "opencode.db")) || existsSync5(join5(this.baseDir, "sessions.db"));
|
|
3816
|
+
const hasLegacySessionsDir = existsSync5(join5(this.baseDir, "sessions"));
|
|
3489
3817
|
return hasCurrentStorage || hasLegacyDb || hasLegacySessionsDir;
|
|
3490
3818
|
} catch {
|
|
3491
3819
|
return false;
|
|
3492
3820
|
}
|
|
3493
3821
|
}
|
|
3494
3822
|
async load(range) {
|
|
3495
|
-
const currentMessagesRoot =
|
|
3496
|
-
if (
|
|
3823
|
+
const currentMessagesRoot = join5(this.baseDir, "storage", "message");
|
|
3824
|
+
if (existsSync5(currentMessagesRoot)) {
|
|
3497
3825
|
const currentRecords = loadFromCurrentStorage(this.baseDir, range);
|
|
3498
3826
|
return buildProviderData2(currentRecords);
|
|
3499
3827
|
}
|
|
3500
|
-
const opencodeDbPath =
|
|
3501
|
-
const sessionsDbPath =
|
|
3502
|
-
const sessionsDir =
|
|
3828
|
+
const opencodeDbPath = join5(this.baseDir, "opencode.db");
|
|
3829
|
+
const sessionsDbPath = join5(this.baseDir, "sessions.db");
|
|
3830
|
+
const sessionsDir = join5(this.baseDir, "sessions");
|
|
3503
3831
|
let records = [];
|
|
3504
|
-
if (
|
|
3832
|
+
if (existsSync5(opencodeDbPath)) {
|
|
3505
3833
|
records = loadFromSqlite(opencodeDbPath, range);
|
|
3506
|
-
} else if (
|
|
3834
|
+
} else if (existsSync5(sessionsDbPath)) {
|
|
3507
3835
|
records = loadFromSqlite(sessionsDbPath, range);
|
|
3508
|
-
} else if (
|
|
3836
|
+
} else if (existsSync5(sessionsDir)) {
|
|
3509
3837
|
records = loadFromLegacyJson(sessionsDir, range);
|
|
3510
3838
|
}
|
|
3511
3839
|
return buildProviderData2(records);
|
|
@@ -3520,16 +3848,16 @@ var init_open_code = __esm(() => {
|
|
|
3520
3848
|
secondary: "#a78bfa",
|
|
3521
3849
|
gradient: ["#6366f1", "#a78bfa"]
|
|
3522
3850
|
};
|
|
3523
|
-
CURRENT_DEFAULT_BASE_DIR =
|
|
3524
|
-
LEGACY_DEFAULT_BASE_DIR =
|
|
3525
|
-
CONFIG_DEFAULT_BASE_DIR =
|
|
3851
|
+
CURRENT_DEFAULT_BASE_DIR = join5(homedir5(), ".local", "share", "opencode");
|
|
3852
|
+
LEGACY_DEFAULT_BASE_DIR = join5(homedir5(), ".opencode");
|
|
3853
|
+
CONFIG_DEFAULT_BASE_DIR = join5(homedir5(), ".config", "opencode");
|
|
3526
3854
|
});
|
|
3527
3855
|
|
|
3528
3856
|
// packages/registry/dist/providers/pi.js
|
|
3529
|
-
import { existsSync as
|
|
3857
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3530
3858
|
import { readdir } from "fs/promises";
|
|
3531
|
-
import { homedir as
|
|
3532
|
-
import { join as
|
|
3859
|
+
import { homedir as homedir6 } from "os";
|
|
3860
|
+
import { join as join6, relative as relative4, sep as sep3 } from "path";
|
|
3533
3861
|
function resolveAgentDir(baseDir) {
|
|
3534
3862
|
if (baseDir) {
|
|
3535
3863
|
return baseDir;
|
|
@@ -3537,15 +3865,15 @@ function resolveAgentDir(baseDir) {
|
|
|
3537
3865
|
return process.env["PI_CODING_AGENT_DIR"] ?? DEFAULT_AGENT_DIR;
|
|
3538
3866
|
}
|
|
3539
3867
|
function getSessionsDir(agentDir) {
|
|
3540
|
-
return
|
|
3868
|
+
return join6(agentDir, "sessions");
|
|
3541
3869
|
}
|
|
3542
3870
|
async function collectJsonlFiles3(dir) {
|
|
3543
|
-
if (!
|
|
3871
|
+
if (!existsSync6(dir)) {
|
|
3544
3872
|
return [];
|
|
3545
3873
|
}
|
|
3546
3874
|
const files = [];
|
|
3547
3875
|
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
3548
|
-
const fullPath =
|
|
3876
|
+
const fullPath = join6(dir, entry.name);
|
|
3549
3877
|
if (entry.isDirectory()) {
|
|
3550
3878
|
files.push(...await collectJsonlFiles3(fullPath));
|
|
3551
3879
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
@@ -3747,7 +4075,7 @@ class PiProvider {
|
|
|
3747
4075
|
}
|
|
3748
4076
|
async isAvailable() {
|
|
3749
4077
|
try {
|
|
3750
|
-
return
|
|
4078
|
+
return existsSync6(getSessionsDir(this.agentDir));
|
|
3751
4079
|
} catch {
|
|
3752
4080
|
return false;
|
|
3753
4081
|
}
|
|
@@ -3778,7 +4106,7 @@ var init_pi = __esm(() => {
|
|
|
3778
4106
|
init_jsonl_splitter();
|
|
3779
4107
|
init_normalizer();
|
|
3780
4108
|
init_cost();
|
|
3781
|
-
DEFAULT_AGENT_DIR =
|
|
4109
|
+
DEFAULT_AGENT_DIR = join6(homedir6(), ".pi", "agent");
|
|
3782
4110
|
PI_COLORS = {
|
|
3783
4111
|
primary: "#0ea5e9",
|
|
3784
4112
|
secondary: "#67e8f9",
|
|
@@ -3797,22 +4125,22 @@ var init_providers = __esm(() => {
|
|
|
3797
4125
|
});
|
|
3798
4126
|
|
|
3799
4127
|
// packages/registry/dist/cursor-auth.js
|
|
3800
|
-
import { chmodSync, existsSync as
|
|
4128
|
+
import { chmodSync, existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync4, readdirSync as readdirSync5, renameSync as renameSync2, rmSync as rmSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
3801
4129
|
import { createHash } from "crypto";
|
|
3802
|
-
import { homedir as
|
|
3803
|
-
import { basename as basename2, dirname as dirname4, join as
|
|
4130
|
+
import { homedir as homedir7 } from "os";
|
|
4131
|
+
import { basename as basename2, dirname as dirname4, join as join7 } from "path";
|
|
3804
4132
|
function getCursorRootDir() {
|
|
3805
|
-
return process.env["TOKENLEAK_CURSOR_DIR"] ??
|
|
4133
|
+
return process.env["TOKENLEAK_CURSOR_DIR"] ?? join7(homedir7(), ".config", "tokenleak");
|
|
3806
4134
|
}
|
|
3807
4135
|
function getCursorCredentialsPath() {
|
|
3808
|
-
return
|
|
4136
|
+
return join7(getCursorRootDir(), "cursor-credentials.json");
|
|
3809
4137
|
}
|
|
3810
4138
|
function getCursorCacheDir() {
|
|
3811
|
-
return
|
|
4139
|
+
return join7(getCursorRootDir(), "cursor-cache");
|
|
3812
4140
|
}
|
|
3813
4141
|
function ensureDir(dirPath, mode) {
|
|
3814
|
-
if (!
|
|
3815
|
-
|
|
4142
|
+
if (!existsSync7(dirPath)) {
|
|
4143
|
+
mkdirSync2(dirPath, { recursive: true });
|
|
3816
4144
|
}
|
|
3817
4145
|
if (mode !== undefined && process.platform !== "win32") {
|
|
3818
4146
|
chmodSync(dirPath, mode);
|
|
@@ -3821,12 +4149,12 @@ function ensureDir(dirPath, mode) {
|
|
|
3821
4149
|
function atomicWriteFile(path, contents, mode) {
|
|
3822
4150
|
const dir = dirname4(path);
|
|
3823
4151
|
ensureDir(dir);
|
|
3824
|
-
const tempPath =
|
|
3825
|
-
|
|
4152
|
+
const tempPath = join7(dir, `.tmp-${basename2(path)}-${process.pid}`);
|
|
4153
|
+
writeFileSync2(tempPath, contents, "utf8");
|
|
3826
4154
|
if (mode !== undefined && process.platform !== "win32") {
|
|
3827
4155
|
chmodSync(tempPath, mode);
|
|
3828
4156
|
}
|
|
3829
|
-
|
|
4157
|
+
renameSync2(tempPath, path);
|
|
3830
4158
|
}
|
|
3831
4159
|
async function fetchWithTimeout(url, init) {
|
|
3832
4160
|
const controller = new AbortController;
|
|
@@ -3881,13 +4209,13 @@ function countCursorCsvRows(csvText) {
|
|
|
3881
4209
|
return rows.length > 0 ? rows.length - 1 : 0;
|
|
3882
4210
|
}
|
|
3883
4211
|
function archiveCacheFile(path, label) {
|
|
3884
|
-
if (!
|
|
4212
|
+
if (!existsSync7(path)) {
|
|
3885
4213
|
return;
|
|
3886
4214
|
}
|
|
3887
|
-
const archiveDir =
|
|
4215
|
+
const archiveDir = join7(getCursorCacheDir(), "archive");
|
|
3888
4216
|
ensureDir(archiveDir, 448);
|
|
3889
4217
|
const timestamp = new Date().toISOString().replaceAll(":", "-");
|
|
3890
|
-
|
|
4218
|
+
renameSync2(path, join7(archiveDir, `${sanitizeAccountIdForFilename(label)}-${timestamp}.csv`));
|
|
3891
4219
|
}
|
|
3892
4220
|
function resolveAccountId(store, nameOrId) {
|
|
3893
4221
|
const needle = nameOrId.trim();
|
|
@@ -3926,11 +4254,11 @@ function isCursorAuthFailureReason(reason) {
|
|
|
3926
4254
|
}
|
|
3927
4255
|
function loadCursorCredentialsStore() {
|
|
3928
4256
|
const path = getCursorCredentialsPath();
|
|
3929
|
-
if (!
|
|
4257
|
+
if (!existsSync7(path)) {
|
|
3930
4258
|
return null;
|
|
3931
4259
|
}
|
|
3932
4260
|
try {
|
|
3933
|
-
const parsed = JSON.parse(
|
|
4261
|
+
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
3934
4262
|
if (typeof parsed !== "object" || parsed === null || typeof parsed.activeAccountId !== "string" || typeof parsed.accounts !== "object" || parsed.accounts === null) {
|
|
3935
4263
|
return null;
|
|
3936
4264
|
}
|
|
@@ -3968,7 +4296,7 @@ function listCursorAccounts() {
|
|
|
3968
4296
|
}
|
|
3969
4297
|
function hasCursorUsageCache() {
|
|
3970
4298
|
const cacheDir = getCursorCacheDir();
|
|
3971
|
-
if (!
|
|
4299
|
+
if (!existsSync7(cacheDir)) {
|
|
3972
4300
|
return false;
|
|
3973
4301
|
}
|
|
3974
4302
|
return readdirSync5(cacheDir).some((entry) => {
|
|
@@ -4021,16 +4349,16 @@ function removeCursorAccount(nameOrId, purgeCache) {
|
|
|
4021
4349
|
}
|
|
4022
4350
|
const wasActive = accountId === store.activeAccountId;
|
|
4023
4351
|
const cacheDir = getCursorCacheDir();
|
|
4024
|
-
const accountCachePath =
|
|
4025
|
-
const activeCachePath =
|
|
4026
|
-
if (
|
|
4352
|
+
const accountCachePath = join7(cacheDir, `usage.${sanitizeAccountIdForFilename(accountId)}.csv`);
|
|
4353
|
+
const activeCachePath = join7(cacheDir, "usage.csv");
|
|
4354
|
+
if (existsSync7(accountCachePath)) {
|
|
4027
4355
|
if (purgeCache) {
|
|
4028
4356
|
unlinkSync(accountCachePath);
|
|
4029
4357
|
} else {
|
|
4030
4358
|
archiveCacheFile(accountCachePath, `usage.${accountId}`);
|
|
4031
4359
|
}
|
|
4032
4360
|
}
|
|
4033
|
-
if (wasActive &&
|
|
4361
|
+
if (wasActive && existsSync7(activeCachePath)) {
|
|
4034
4362
|
if (purgeCache) {
|
|
4035
4363
|
unlinkSync(activeCachePath);
|
|
4036
4364
|
} else {
|
|
@@ -4040,7 +4368,7 @@ function removeCursorAccount(nameOrId, purgeCache) {
|
|
|
4040
4368
|
delete store.accounts[accountId];
|
|
4041
4369
|
const remainingIds = Object.keys(store.accounts);
|
|
4042
4370
|
if (remainingIds.length === 0) {
|
|
4043
|
-
if (
|
|
4371
|
+
if (existsSync7(getCursorCredentialsPath())) {
|
|
4044
4372
|
unlinkSync(getCursorCredentialsPath());
|
|
4045
4373
|
}
|
|
4046
4374
|
return;
|
|
@@ -4048,34 +4376,34 @@ function removeCursorAccount(nameOrId, purgeCache) {
|
|
|
4048
4376
|
if (wasActive) {
|
|
4049
4377
|
const nextAccountId = remainingIds[0];
|
|
4050
4378
|
store.activeAccountId = nextAccountId;
|
|
4051
|
-
const nextCachePath =
|
|
4052
|
-
if (
|
|
4053
|
-
|
|
4379
|
+
const nextCachePath = join7(cacheDir, `usage.${sanitizeAccountIdForFilename(nextAccountId)}.csv`);
|
|
4380
|
+
if (existsSync7(nextCachePath)) {
|
|
4381
|
+
renameSync2(nextCachePath, activeCachePath);
|
|
4054
4382
|
}
|
|
4055
4383
|
}
|
|
4056
4384
|
saveCursorCredentialsStore(store);
|
|
4057
4385
|
}
|
|
4058
4386
|
function removeAllCursorAccounts(purgeCache) {
|
|
4059
4387
|
const cacheDir = getCursorCacheDir();
|
|
4060
|
-
if (
|
|
4388
|
+
if (existsSync7(cacheDir)) {
|
|
4061
4389
|
for (const entry of readdirSync5(cacheDir)) {
|
|
4062
4390
|
if (entry === "archive") {
|
|
4063
4391
|
if (purgeCache) {
|
|
4064
|
-
|
|
4392
|
+
rmSync2(join7(cacheDir, entry), { recursive: true, force: true });
|
|
4065
4393
|
}
|
|
4066
4394
|
continue;
|
|
4067
4395
|
}
|
|
4068
4396
|
if (entry === "usage.csv" || /^usage\.[^.].*\.csv$/.test(entry)) {
|
|
4069
|
-
const fullPath =
|
|
4397
|
+
const fullPath = join7(cacheDir, entry);
|
|
4070
4398
|
if (purgeCache) {
|
|
4071
|
-
|
|
4399
|
+
rmSync2(fullPath, { force: true });
|
|
4072
4400
|
} else {
|
|
4073
4401
|
archiveCacheFile(fullPath, `usage.all.${entry}`);
|
|
4074
4402
|
}
|
|
4075
4403
|
}
|
|
4076
4404
|
}
|
|
4077
4405
|
}
|
|
4078
|
-
if (
|
|
4406
|
+
if (existsSync7(getCursorCredentialsPath())) {
|
|
4079
4407
|
unlinkSync(getCursorCredentialsPath());
|
|
4080
4408
|
}
|
|
4081
4409
|
}
|
|
@@ -4095,17 +4423,17 @@ function setActiveCursorAccount(nameOrId) {
|
|
|
4095
4423
|
return;
|
|
4096
4424
|
}
|
|
4097
4425
|
const cacheDir = getCursorCacheDir();
|
|
4098
|
-
const activeCachePath =
|
|
4099
|
-
const oldActivePath =
|
|
4100
|
-
const newActivePath =
|
|
4101
|
-
if (
|
|
4102
|
-
if (
|
|
4426
|
+
const activeCachePath = join7(cacheDir, "usage.csv");
|
|
4427
|
+
const oldActivePath = join7(cacheDir, `usage.${sanitizeAccountIdForFilename(store.activeAccountId)}.csv`);
|
|
4428
|
+
const newActivePath = join7(cacheDir, `usage.${sanitizeAccountIdForFilename(accountId)}.csv`);
|
|
4429
|
+
if (existsSync7(activeCachePath)) {
|
|
4430
|
+
if (existsSync7(oldActivePath)) {
|
|
4103
4431
|
archiveCacheFile(oldActivePath, `usage.${store.activeAccountId}`);
|
|
4104
4432
|
}
|
|
4105
|
-
|
|
4433
|
+
renameSync2(activeCachePath, oldActivePath);
|
|
4106
4434
|
}
|
|
4107
|
-
if (
|
|
4108
|
-
|
|
4435
|
+
if (existsSync7(newActivePath)) {
|
|
4436
|
+
renameSync2(newActivePath, activeCachePath);
|
|
4109
4437
|
}
|
|
4110
4438
|
store.activeAccountId = accountId;
|
|
4111
4439
|
saveCursorCredentialsStore(store);
|
|
@@ -4214,11 +4542,11 @@ async function syncCursorCache() {
|
|
|
4214
4542
|
for (const [accountId, credentials] of Object.entries(store.accounts)) {
|
|
4215
4543
|
try {
|
|
4216
4544
|
const csvText = await fetchCursorUsageCsv(credentials.sessionToken);
|
|
4217
|
-
const filePath = accountId === store.activeAccountId ?
|
|
4545
|
+
const filePath = accountId === store.activeAccountId ? join7(cacheDir, "usage.csv") : join7(cacheDir, `usage.${sanitizeAccountIdForFilename(accountId)}.csv`);
|
|
4218
4546
|
atomicWriteFile(filePath, csvText, process.platform === "win32" ? undefined : 384);
|
|
4219
4547
|
if (accountId === store.activeAccountId) {
|
|
4220
|
-
const activeDupPath =
|
|
4221
|
-
if (
|
|
4548
|
+
const activeDupPath = join7(cacheDir, `usage.${sanitizeAccountIdForFilename(store.activeAccountId)}.csv`);
|
|
4549
|
+
if (existsSync7(activeDupPath)) {
|
|
4222
4550
|
unlinkSync(activeDupPath);
|
|
4223
4551
|
}
|
|
4224
4552
|
}
|
|
@@ -10234,11 +10562,15 @@ function createInitialState() {
|
|
|
10234
10562
|
cursorSetupMessage: null,
|
|
10235
10563
|
cursorSetupSubmitting: false,
|
|
10236
10564
|
cursorSetupStatusOverride: null,
|
|
10565
|
+
replayDate: null,
|
|
10566
|
+
replayScrollOffset: 0,
|
|
10567
|
+
replayExpandedBlocks: new Set,
|
|
10237
10568
|
cachedAdvisorReport: null,
|
|
10238
10569
|
cachedFocusReport: null,
|
|
10239
10570
|
cachedExplainReport: null,
|
|
10240
10571
|
cachedCompareOutput: null,
|
|
10241
|
-
cachedMoreStats: null
|
|
10572
|
+
cachedMoreStats: null,
|
|
10573
|
+
cachedReplayReport: null
|
|
10242
10574
|
};
|
|
10243
10575
|
}
|
|
10244
10576
|
var WINDOW_LABELS, WINDOW_DAYS;
|
|
@@ -10411,6 +10743,22 @@ function ensureMoreStats(state) {
|
|
|
10411
10743
|
state.cachedMoreStats = more;
|
|
10412
10744
|
return more;
|
|
10413
10745
|
}
|
|
10746
|
+
function ensureReplayReport(state) {
|
|
10747
|
+
if (!state.data)
|
|
10748
|
+
return null;
|
|
10749
|
+
if (state.cachedReplayReport && state.cachedReplayReport.date === state.replayDate) {
|
|
10750
|
+
return state.cachedReplayReport;
|
|
10751
|
+
}
|
|
10752
|
+
if (!state.replayDate) {
|
|
10753
|
+
state.replayDate = todayStr();
|
|
10754
|
+
}
|
|
10755
|
+
const scoped = getScopedWindowData(state);
|
|
10756
|
+
if (!scoped)
|
|
10757
|
+
return null;
|
|
10758
|
+
const report = buildReplayReport(scoped.scopedProviders, state.replayDate);
|
|
10759
|
+
state.cachedReplayReport = report;
|
|
10760
|
+
return report;
|
|
10761
|
+
}
|
|
10414
10762
|
function getDayOfWeekForWindow(state) {
|
|
10415
10763
|
if (!state.data)
|
|
10416
10764
|
return [];
|
|
@@ -10424,7 +10772,7 @@ var init_data = __esm(() => {
|
|
|
10424
10772
|
});
|
|
10425
10773
|
|
|
10426
10774
|
// packages/tui/dist/lib/format.js
|
|
10427
|
-
function
|
|
10775
|
+
function formatTokens15(n) {
|
|
10428
10776
|
if (n >= 1e9)
|
|
10429
10777
|
return `${(n / 1e9).toFixed(1)}B`;
|
|
10430
10778
|
if (n >= 1e6)
|
|
@@ -10433,7 +10781,7 @@ function formatTokens14(n) {
|
|
|
10433
10781
|
return `${(n / 1000).toFixed(1)}K`;
|
|
10434
10782
|
return n.toLocaleString("en-US");
|
|
10435
10783
|
}
|
|
10436
|
-
function
|
|
10784
|
+
function formatCost15(n) {
|
|
10437
10785
|
return `$${n.toFixed(2)}`;
|
|
10438
10786
|
}
|
|
10439
10787
|
function formatPercent5(n) {
|
|
@@ -10445,7 +10793,7 @@ function padRight(s, width) {
|
|
|
10445
10793
|
function padLeft(s, width) {
|
|
10446
10794
|
return s.length >= width ? s.slice(0, width) : " ".repeat(width - s.length) + s;
|
|
10447
10795
|
}
|
|
10448
|
-
function
|
|
10796
|
+
function truncate3(s, maxLen) {
|
|
10449
10797
|
if (maxLen <= 0)
|
|
10450
10798
|
return "";
|
|
10451
10799
|
if (maxLen === 1)
|
|
@@ -10482,10 +10830,10 @@ function formatCompactTime() {
|
|
|
10482
10830
|
return `${month}/${day} ${hours}:${minutes}`;
|
|
10483
10831
|
}
|
|
10484
10832
|
function buildHeader(state, renderer, onViewSwitch) {
|
|
10485
|
-
const costStr = state.data ?
|
|
10833
|
+
const costStr = state.data ? formatCost15(state.data.allTimeStats.totalCost) : "$...";
|
|
10486
10834
|
const windowIdx = state.selectedWindowIndex;
|
|
10487
10835
|
const stats = state.data?.windows[windowIdx]?.stats;
|
|
10488
|
-
const windowCost = stats ?
|
|
10836
|
+
const windowCost = stats ? formatCost15(stats.totalCost) : costStr;
|
|
10489
10837
|
const tabParts = [];
|
|
10490
10838
|
for (let i = 0;i < WINDOW_LABELS.length; i++) {
|
|
10491
10839
|
const label = WINDOW_LABELS[i];
|
|
@@ -10540,7 +10888,8 @@ var init_header = __esm(() => {
|
|
|
10540
10888
|
{ key: "5", label: "Explain", mode: "explain" },
|
|
10541
10889
|
{ key: "6", label: "Compare", mode: "compare" },
|
|
10542
10890
|
{ key: "7", label: "Export", mode: "export" },
|
|
10543
|
-
{ key: "8", label: "Wrapped", mode: "wrapped" }
|
|
10891
|
+
{ key: "8", label: "Wrapped", mode: "wrapped" },
|
|
10892
|
+
{ key: "9", label: "Replay", mode: "replay" }
|
|
10544
10893
|
];
|
|
10545
10894
|
});
|
|
10546
10895
|
|
|
@@ -10578,11 +10927,11 @@ function buildChart(daily, chartWidth, chartHeight) {
|
|
|
10578
10927
|
const threshold = row / chartHeight * maxTokens;
|
|
10579
10928
|
let yLabel = "";
|
|
10580
10929
|
if (row === chartHeight - 1) {
|
|
10581
|
-
yLabel =
|
|
10930
|
+
yLabel = formatTokens15(maxTokens);
|
|
10582
10931
|
} else if (row === 0) {
|
|
10583
10932
|
yLabel = "0";
|
|
10584
10933
|
} else if (row === Math.floor(chartHeight / 2)) {
|
|
10585
|
-
yLabel =
|
|
10934
|
+
yLabel = formatTokens15(maxTokens / 2);
|
|
10586
10935
|
}
|
|
10587
10936
|
const labelPadded = yLabel.padStart(yAxisWidth - 1) + "\u2502";
|
|
10588
10937
|
let barStr = "";
|
|
@@ -10602,7 +10951,7 @@ function buildChart(daily, chartWidth, chartHeight) {
|
|
|
10602
10951
|
rows.push(Box2({ width: "100%", height: 1 }));
|
|
10603
10952
|
const legendParts = [];
|
|
10604
10953
|
for (const cm of chartModels) {
|
|
10605
|
-
legendParts.push(Text2({ content: `\u25CF ${
|
|
10954
|
+
legendParts.push(Text2({ content: `\u25CF ${truncate3(cm.model, 18)} `, fg: cm.color }));
|
|
10606
10955
|
}
|
|
10607
10956
|
rows.push(Box2({ flexDirection: "row", width: "100%", height: 1 }, Text2({ content: " ".repeat(yAxisWidth), fg: COLORS2.dimWhite }), ...legendParts));
|
|
10608
10957
|
return Box2({ flexDirection: "column", width: "100%" }, ...rows);
|
|
@@ -10631,7 +10980,7 @@ function buildXLabels(days, width) {
|
|
|
10631
10980
|
}
|
|
10632
10981
|
function buildCompactList(daily, chartModels) {
|
|
10633
10982
|
const sorted = [...daily].sort((a, b) => b.totalTokens - a.totalTokens).slice(0, 5);
|
|
10634
|
-
const rows = sorted.map((d) => Box2({ flexDirection: "row", width: "100%" }, Text2({ content: `${formatShortDate(d.date)} `, fg: COLORS2.dimWhite }), Text2({ content:
|
|
10983
|
+
const rows = sorted.map((d) => Box2({ flexDirection: "row", width: "100%" }, Text2({ content: `${formatShortDate(d.date)} `, fg: COLORS2.dimWhite }), Text2({ content: formatTokens15(d.totalTokens), fg: COLORS2.green, attributes: BOLD3 })));
|
|
10635
10984
|
const legendParts = chartModels.map((cm) => Text2({ content: `\u25CF ${cm.model} `, fg: cm.color }));
|
|
10636
10985
|
return Box2({ flexDirection: "column", width: "100%" }, Text2({ content: "Top 5 Days", fg: COLORS2.amber, attributes: BOLD3 }), ...rows, Box2({ flexDirection: "row", width: "100%", paddingTop: 1 }, ...legendParts));
|
|
10637
10986
|
}
|
|
@@ -10702,7 +11051,7 @@ function createStatsRow(state, stats) {
|
|
|
10702
11051
|
height: 1,
|
|
10703
11052
|
justifyContent: "flex-start",
|
|
10704
11053
|
gap: 0
|
|
10705
|
-
}, statCard("Tokens",
|
|
11054
|
+
}, statCard("Tokens", formatTokens15(stats.totalTokens), COLORS2.green), sep4(), statCard("Cost", formatCost15(stats.totalCost), COLORS2.amber), sep4(), statCard("Active", `${stats.activeDays}/${stats.totalDays}d`, COLORS2.cyan), sep4(), statCard("Streak", `${stats.currentStreak}d`, COLORS2.green), sep4(), statCard("Cache", formatPercent5(stats.cacheHitRate), COLORS2.cyan));
|
|
10706
11055
|
}
|
|
10707
11056
|
var init_stats_row = __esm(() => {
|
|
10708
11057
|
init_theme2();
|
|
@@ -10736,7 +11085,7 @@ function createModelList(state, stats) {
|
|
|
10736
11085
|
fg: COLORS2.amber,
|
|
10737
11086
|
attributes: BOLD3
|
|
10738
11087
|
}), Text5({
|
|
10739
|
-
content: `Total: ${
|
|
11088
|
+
content: `Total: ${formatCost15(totalCost)}`,
|
|
10740
11089
|
fg: COLORS2.amber,
|
|
10741
11090
|
attributes: BOLD3
|
|
10742
11091
|
})));
|
|
@@ -10752,9 +11101,9 @@ function createModelList(state, stats) {
|
|
|
10752
11101
|
const colorIdx = sortedModels.indexOf(m);
|
|
10753
11102
|
const color2 = MODEL_COLORS2[colorIdx % MODEL_COLORS2.length];
|
|
10754
11103
|
const pct = `(${m.percentage.toFixed(1)}%)`;
|
|
10755
|
-
const nameStr =
|
|
10756
|
-
const costStr =
|
|
10757
|
-
const tokStr =
|
|
11104
|
+
const nameStr = truncate3(m.model, 30);
|
|
11105
|
+
const costStr = formatCost15(m.cost);
|
|
11106
|
+
const tokStr = formatTokens15(m.tokens);
|
|
10758
11107
|
children.push(Box5({ flexDirection: "row", width: "100%" }, Text5({ content: "\u25CF ", fg: color2 }), Text5({
|
|
10759
11108
|
content: padRight(nameStr, 31),
|
|
10760
11109
|
fg: colorIdx === 0 ? COLORS2.amber : COLORS2.white,
|
|
@@ -10954,7 +11303,7 @@ function buildStatusBar(state) {
|
|
|
10954
11303
|
}, Text7({ content: "tab:field enter:token submit esc:close", fg: COLORS2.dimWhite }), Text7({ content: `Updated ${formatUpdateTime()}`, fg: COLORS2.dimWhite }));
|
|
10955
11304
|
}
|
|
10956
11305
|
const helpHint = "?:help";
|
|
10957
|
-
const nav = `\u2190\u2192:view tab/\u21E7tab:period 1-
|
|
11306
|
+
const nav = `\u2190\u2192:view tab/\u21E7tab:period 1-9:view`;
|
|
10958
11307
|
const cursorHint = getCursorBannerText(state) ? " c:cursor" : "";
|
|
10959
11308
|
let keys;
|
|
10960
11309
|
if (state.selectedView === "overview") {
|
|
@@ -10963,6 +11312,8 @@ function buildStatusBar(state) {
|
|
|
10963
11312
|
keys = `${nav} [/]:page r:refresh${cursorHint} ${helpHint} q:quit`;
|
|
10964
11313
|
} else if (state.selectedView === "explain") {
|
|
10965
11314
|
keys = `${nav} h/l:date r:refresh${cursorHint} ${helpHint} q:quit`;
|
|
11315
|
+
} else if (state.selectedView === "replay") {
|
|
11316
|
+
keys = `${nav} h/l:date j/k:scroll enter:expand r:refresh${cursorHint} ${helpHint} q:quit`;
|
|
10966
11317
|
} else if (state.selectedView === "advisor" || state.selectedView === "focus" || state.selectedView === "compare" || state.selectedView === "wrapped") {
|
|
10967
11318
|
keys = `${nav} j/k:scroll r:refresh${cursorHint} ${helpHint} q:quit`;
|
|
10968
11319
|
} else if (state.selectedView === "export") {
|
|
@@ -11024,7 +11375,7 @@ function createTimeWindowsPanel(props) {
|
|
|
11024
11375
|
const avgTokenValues = windows.map((w) => w.stats.averageDailyTokens);
|
|
11025
11376
|
const avgCostValues = windows.map((w) => w.stats.averageDailyCost);
|
|
11026
11377
|
const activeDaysValues = windows.map((w) => w.stats.activeDays);
|
|
11027
|
-
children.push(headerRow(labels), Text8({ content: "\u2500".repeat(66), fg: COLORS2.dimWhite }), dataRow("Tokens", tokenValues.map(
|
|
11378
|
+
children.push(headerRow(labels), Text8({ content: "\u2500".repeat(66), fg: COLORS2.dimWhite }), dataRow("Tokens", tokenValues.map(formatTokens15), findMaxIndex(tokenValues)), dataRow("Cost", costValues.map(formatCost15), findMaxIndex(costValues)), dataRow("Avg Daily Tokens", avgTokenValues.map(formatTokens15), findMaxIndex(avgTokenValues)), dataRow("Avg Daily Cost", avgCostValues.map(formatCost15), findMaxIndex(avgCostValues)), dataRow("Active Days", activeDaysValues.map((v) => v.toString()), findMaxIndex(activeDaysValues)), dataRow("Cache Hit Rate", windows.map((w) => `${(w.stats.cacheHitRate * 100).toFixed(1)}%`), findMaxIndex(windows.map((w) => w.stats.cacheHitRate))), dataRow("Current Streak", windows.map((w) => `${w.stats.currentStreak}d`), findMaxIndex(windows.map((w) => w.stats.currentStreak))));
|
|
11028
11379
|
}
|
|
11029
11380
|
return Box8({
|
|
11030
11381
|
flexDirection: "column",
|
|
@@ -11049,7 +11400,7 @@ function createHourOfDayPanel(hourOfDay) {
|
|
|
11049
11400
|
const ratio = entry.tokens / maxTokens;
|
|
11050
11401
|
const hourLabel = String(entry.hour).padStart(2, "0");
|
|
11051
11402
|
const barColor = entry.tokens === maxTokens ? COLORS2.amber : COLORS2.green;
|
|
11052
|
-
children.push(Box9({ flexDirection: "row", width: "100%" }, Text9({ content: `${hourLabel} `, fg: COLORS2.dimWhite }), Text9({ content: asciiBar(ratio, 16), fg: barColor }), Text9({ content: ` ${padLeft(
|
|
11403
|
+
children.push(Box9({ flexDirection: "row", width: "100%" }, Text9({ content: `${hourLabel} `, fg: COLORS2.dimWhite }), Text9({ content: asciiBar(ratio, 16), fg: barColor }), Text9({ content: ` ${padLeft(formatTokens15(entry.tokens), 8)}`, fg: COLORS2.green })));
|
|
11053
11404
|
}
|
|
11054
11405
|
return Box9({
|
|
11055
11406
|
flexDirection: "column",
|
|
@@ -11065,7 +11416,7 @@ function createDayOfWeekPanel(dayOfWeek) {
|
|
|
11065
11416
|
for (const entry of dayOfWeek) {
|
|
11066
11417
|
const ratio = entry.tokens / maxTokens;
|
|
11067
11418
|
const barColor = entry.tokens === maxTokens ? COLORS2.amber : COLORS2.green;
|
|
11068
|
-
children.push(Box9({ flexDirection: "row", width: "100%" }, Text9({ content: padRight(entry.label, 4), fg: COLORS2.dimWhite }), Text9({ content: asciiBar(ratio, 16), fg: barColor }), Text9({ content: ` ${padLeft(
|
|
11419
|
+
children.push(Box9({ flexDirection: "row", width: "100%" }, Text9({ content: padRight(entry.label, 4), fg: COLORS2.dimWhite }), Text9({ content: asciiBar(ratio, 16), fg: barColor }), Text9({ content: ` ${padLeft(formatTokens15(entry.tokens), 8)}`, fg: COLORS2.green })));
|
|
11069
11420
|
}
|
|
11070
11421
|
return Box9({
|
|
11071
11422
|
flexDirection: "column",
|
|
@@ -11095,17 +11446,17 @@ function createInputOutputPanel(io, stats) {
|
|
|
11095
11446
|
borderColor: COLORS2.cyan,
|
|
11096
11447
|
padding: 1,
|
|
11097
11448
|
flexGrow: 1
|
|
11098
|
-
}, Text10({ content: " INPUT / OUTPUT ", fg: COLORS2.cyan, attributes: BOLD3 }), statRow("Input", `${
|
|
11449
|
+
}, Text10({ content: " INPUT / OUTPUT ", fg: COLORS2.cyan, attributes: BOLD3 }), statRow("Input", `${formatTokens15(stats.totalInputTokens)} (${inputPct}%)`), statRow("Output", `${formatTokens15(stats.totalOutputTokens)} (${outputPct}%)`), statRow("I/O Ratio", `${ioRatio}:1`), statRow("Output Share", `${(io.outputShare * 100).toFixed(1)}%`, COLORS2.amber));
|
|
11099
11450
|
}
|
|
11100
11451
|
function createMonthlyBurnPanel(burn) {
|
|
11101
|
-
const burnRate = burn.observedDays > 0 ?
|
|
11452
|
+
const burnRate = burn.observedDays > 0 ? formatCost15(burn.projectedCost / (burn.calendarDays || 30)) : "$0.00";
|
|
11102
11453
|
return Box10({
|
|
11103
11454
|
flexDirection: "column",
|
|
11104
11455
|
borderStyle: "single",
|
|
11105
11456
|
borderColor: COLORS2.amber,
|
|
11106
11457
|
padding: 1,
|
|
11107
11458
|
flexGrow: 1
|
|
11108
|
-
}, Text10({ content: " MONTHLY BURN ", fg: COLORS2.amber, attributes: BOLD3 }), statRow("Projected Tokens",
|
|
11459
|
+
}, Text10({ content: " MONTHLY BURN ", fg: COLORS2.amber, attributes: BOLD3 }), statRow("Projected Tokens", formatTokens15(burn.projectedTokens)), statRow("Projected Cost", formatCost15(burn.projectedCost), COLORS2.amber), statRow("Observed", `${burn.observedDays}/${burn.calendarDays} days`), statRow("Burn Rate", `${burnRate}/day`, COLORS2.red));
|
|
11109
11460
|
}
|
|
11110
11461
|
var init_matrix_io = __esm(() => {
|
|
11111
11462
|
init_theme2();
|
|
@@ -11124,7 +11475,7 @@ function createCacheEconomicsPanel(cache) {
|
|
|
11124
11475
|
borderColor: COLORS2.cyan,
|
|
11125
11476
|
padding: 1,
|
|
11126
11477
|
flexGrow: 1
|
|
11127
|
-
}, Text11({ content: " CACHE ECONOMICS ", fg: COLORS2.cyan, attributes: BOLD3 }), statRow2("Read Tokens",
|
|
11478
|
+
}, Text11({ content: " CACHE ECONOMICS ", fg: COLORS2.cyan, attributes: BOLD3 }), statRow2("Read Tokens", formatTokens15(cache.readTokens)), statRow2("Write Tokens", formatTokens15(cache.writeTokens)), statRow2("Read Coverage", `${(cache.readCoverage * 100).toFixed(1)}%`, COLORS2.amber), statRow2("Reuse Ratio", reuseStr));
|
|
11128
11479
|
}
|
|
11129
11480
|
function createCacheRoiPanel(roi) {
|
|
11130
11481
|
if (!roi) {
|
|
@@ -11144,7 +11495,7 @@ function createCacheRoiPanel(roi) {
|
|
|
11144
11495
|
borderColor: COLORS2.green,
|
|
11145
11496
|
padding: 1,
|
|
11146
11497
|
flexGrow: 1
|
|
11147
|
-
}, Text11({ content: " CACHE ROI ", fg: COLORS2.green, attributes: BOLD3 }), statRow2("Read Savings",
|
|
11498
|
+
}, Text11({ content: " CACHE ROI ", fg: COLORS2.green, attributes: BOLD3 }), statRow2("Read Savings", formatCost15(summary.readSavings), COLORS2.green), statRow2("Write Cost", formatCost15(summary.writeCost), COLORS2.red), statRow2("Net Savings", formatCost15(summary.netSavings), summary.netSavings >= 0 ? COLORS2.green : COLORS2.red), statRow2("Payback Ratio", paybackStr, COLORS2.amber));
|
|
11148
11499
|
}
|
|
11149
11500
|
var init_matrix_cache = __esm(() => {
|
|
11150
11501
|
init_theme2();
|
|
@@ -11155,7 +11506,7 @@ import { Box as Box12, Text as Text12 } from "@opentui/core";
|
|
|
11155
11506
|
function statRow3(label, value, valueColor = COLORS2.green) {
|
|
11156
11507
|
return Box12({ flexDirection: "row", width: "100%" }, Text12({ content: label, fg: COLORS2.dimWhite }), Text12({ content: " " }), Text12({ content: value, fg: valueColor, attributes: BOLD3 }));
|
|
11157
11508
|
}
|
|
11158
|
-
function
|
|
11509
|
+
function formatDuration5(ms) {
|
|
11159
11510
|
if (ms === null || ms <= 0)
|
|
11160
11511
|
return "N/A";
|
|
11161
11512
|
const totalMinutes = Math.floor(ms / 60000);
|
|
@@ -11166,14 +11517,14 @@ function formatDuration4(ms) {
|
|
|
11166
11517
|
return `${hours}h ${minutes}m`;
|
|
11167
11518
|
}
|
|
11168
11519
|
function createSessionsPanel(sessions) {
|
|
11169
|
-
const longestDuration = sessions.longestSession ?
|
|
11520
|
+
const longestDuration = sessions.longestSession ? formatDuration5(sessions.longestSession.durationMs) : "N/A";
|
|
11170
11521
|
return Box12({
|
|
11171
11522
|
flexDirection: "column",
|
|
11172
11523
|
borderStyle: "single",
|
|
11173
11524
|
borderColor: COLORS2.cyan,
|
|
11174
11525
|
padding: 1,
|
|
11175
11526
|
flexGrow: 1
|
|
11176
|
-
}, Text12({ content: " SESSIONS ", fg: COLORS2.cyan, attributes: BOLD3 }), statRow3("Total", `${sessions.totalSessions} sessions`), statRow3("Avg Tokens", `${
|
|
11527
|
+
}, Text12({ content: " SESSIONS ", fg: COLORS2.cyan, attributes: BOLD3 }), statRow3("Total", `${sessions.totalSessions} sessions`), statRow3("Avg Tokens", `${formatTokens15(sessions.averageTokens)}/sess`), statRow3("Avg Cost", `${formatCost15(sessions.averageCost)}/sess`), statRow3("Longest", longestDuration), statRow3("Projects", `${sessions.projectCount}`));
|
|
11177
11528
|
}
|
|
11178
11529
|
function createTopProjectsPanel(projects) {
|
|
11179
11530
|
const children = [];
|
|
@@ -11184,13 +11535,13 @@ function createTopProjectsPanel(projects) {
|
|
|
11184
11535
|
for (let i = 0;i < top8.length; i++) {
|
|
11185
11536
|
const p = top8[i];
|
|
11186
11537
|
const rank = `${i + 1}.`;
|
|
11187
|
-
const name =
|
|
11538
|
+
const name = truncate3(p.directory ?? p.projectId, 20);
|
|
11188
11539
|
const isTop = i === 0;
|
|
11189
11540
|
children.push(Box12({ flexDirection: "row", width: "100%" }, Text12({ content: padRight(rank, 3), fg: COLORS2.dimWhite }), Text12({
|
|
11190
11541
|
content: padRight(name, 21),
|
|
11191
11542
|
fg: isTop ? COLORS2.amber : COLORS2.green,
|
|
11192
11543
|
attributes: isTop ? BOLD3 : undefined
|
|
11193
|
-
}), Text12({ content: padLeft(
|
|
11544
|
+
}), Text12({ content: padLeft(formatTokens15(p.totalTokens), 8), fg: COLORS2.green }), Text12({ content: padLeft(formatCost15(p.cost), 10), fg: COLORS2.amber })));
|
|
11194
11545
|
}
|
|
11195
11546
|
}
|
|
11196
11547
|
return Box12({
|
|
@@ -11222,7 +11573,7 @@ function createModelEfficiencyPanel(efficiency) {
|
|
|
11222
11573
|
const oi = r.outputInputRatio.toFixed(2);
|
|
11223
11574
|
const cache = `${(r.cacheCoverage * 100).toFixed(0)}%`;
|
|
11224
11575
|
children.push(Box13({ flexDirection: "row", width: "100%" }, Text13({ content: padRight(`${i + 1}.`, 3), fg: COLORS2.dimWhite }), Text13({
|
|
11225
|
-
content: padRight(
|
|
11576
|
+
content: padRight(truncate3(r.model, 21), 22),
|
|
11226
11577
|
fg: isTop ? COLORS2.amber : COLORS2.green,
|
|
11227
11578
|
attributes: isTop ? BOLD3 : undefined
|
|
11228
11579
|
}), Text13({ content: padLeft(r.score.toFixed(2), 7), fg: COLORS2.cyan, attributes: BOLD3 }), Text13({ content: padLeft(costPerM, 8), fg: COLORS2.amber }), Text13({ content: padLeft(oi, 6), fg: COLORS2.green }), Text13({ content: padLeft(cache, 7), fg: COLORS2.magenta })));
|
|
@@ -11248,10 +11599,10 @@ function createAttributionPanel(attribution) {
|
|
|
11248
11599
|
const c = top8[i];
|
|
11249
11600
|
const isTop = i === 0;
|
|
11250
11601
|
children.push(Box13({ flexDirection: "row", width: "100%" }, Text13({
|
|
11251
|
-
content: padRight(
|
|
11602
|
+
content: padRight(truncate3(c.label, 15), 16),
|
|
11252
11603
|
fg: isTop ? COLORS2.amber : COLORS2.green,
|
|
11253
11604
|
attributes: isTop ? BOLD3 : undefined
|
|
11254
|
-
}), Text13({ content: padRight(c.taskStyle, 10), fg: COLORS2.cyan }), Text13({ content: padLeft(
|
|
11605
|
+
}), Text13({ content: padRight(c.taskStyle, 10), fg: COLORS2.cyan }), Text13({ content: padLeft(formatTokens15(c.tokens), 10), fg: COLORS2.green }), Text13({ content: padLeft(String(c.sessionCount), 10), fg: COLORS2.white })));
|
|
11255
11606
|
}
|
|
11256
11607
|
}
|
|
11257
11608
|
return Box13({
|
|
@@ -11262,7 +11613,7 @@ function createAttributionPanel(attribution) {
|
|
|
11262
11613
|
flexGrow: 1
|
|
11263
11614
|
}, Text13({ content: " ATTRIBUTION ", fg: COLORS2.magenta, attributes: BOLD3 }), ...children);
|
|
11264
11615
|
}
|
|
11265
|
-
function
|
|
11616
|
+
function formatDuration6(ms) {
|
|
11266
11617
|
if (ms === null || ms <= 0)
|
|
11267
11618
|
return "-";
|
|
11268
11619
|
const hours = Math.floor(ms / 3600000);
|
|
@@ -11284,10 +11635,10 @@ function createTopSessionsPanel(sessions) {
|
|
|
11284
11635
|
const name = s.directory ?? s.label;
|
|
11285
11636
|
const isTop = i === 0;
|
|
11286
11637
|
children.push(Box13({ flexDirection: "row", width: "100%" }, Text13({ content: padRight(`${i + 1}.`, 3), fg: COLORS2.dimWhite }), Text13({
|
|
11287
|
-
content: padRight(
|
|
11638
|
+
content: padRight(truncate3(name, 18), 20),
|
|
11288
11639
|
fg: isTop ? COLORS2.amber : COLORS2.green,
|
|
11289
11640
|
attributes: isTop ? BOLD3 : undefined
|
|
11290
|
-
}), Text13({ content: padLeft(
|
|
11641
|
+
}), Text13({ content: padLeft(formatTokens15(s.totalTokens), 9), fg: COLORS2.green }), Text13({ content: padLeft(formatCost15(s.cost), 9), fg: COLORS2.amber }), Text13({ content: padLeft(formatDuration6(s.durationMs), 9), fg: COLORS2.cyan })));
|
|
11291
11642
|
}
|
|
11292
11643
|
}
|
|
11293
11644
|
return Box13({
|
|
@@ -11311,10 +11662,10 @@ function createCacheRoiByModelPanel(cacheRoi) {
|
|
|
11311
11662
|
const isTop = i === 0;
|
|
11312
11663
|
const payback = m.paybackRatio !== null ? `${m.paybackRatio.toFixed(1)}x` : "-";
|
|
11313
11664
|
children.push(Box13({ flexDirection: "row", width: "100%" }, Text13({
|
|
11314
|
-
content: padRight(
|
|
11665
|
+
content: padRight(truncate3(m.label, 18), 20),
|
|
11315
11666
|
fg: isTop ? COLORS2.amber : COLORS2.green,
|
|
11316
11667
|
attributes: isTop ? BOLD3 : undefined
|
|
11317
|
-
}), Text13({ content: padLeft(
|
|
11668
|
+
}), Text13({ content: padLeft(formatCost15(m.netSavings), 10), fg: COLORS2.green }), Text13({ content: padLeft(payback, 10), fg: COLORS2.cyan })));
|
|
11318
11669
|
}
|
|
11319
11670
|
}
|
|
11320
11671
|
return Box13({
|
|
@@ -11341,7 +11692,7 @@ function createOverviewPanel(state) {
|
|
|
11341
11692
|
if (!stats || stats.totalTokens === 0 && stats.totalCost === 0) {
|
|
11342
11693
|
children.push(Text14({ content: "No usage data for this period", fg: COLORS2.dimWhite }));
|
|
11343
11694
|
} else {
|
|
11344
|
-
children.push(statRow4("Total Tokens",
|
|
11695
|
+
children.push(statRow4("Total Tokens", formatTokens15(stats.totalTokens)), statRow4("Total Cost", formatCost15(stats.totalCost), COLORS2.amber), statRow4("Active / Total Days", `${stats.activeDays} / ${stats.totalDays}`), statRow4("Current Streak", `${stats.currentStreak}d`), statRow4("Longest Streak", `${stats.longestStreak}d`), statRow4("Cache Hit Rate", formatPercent5(stats.cacheHitRate), COLORS2.cyan), statRow4("Avg Daily Tokens", formatTokens15(stats.averageDailyTokens)), statRow4("Avg Daily Cost", formatCost15(stats.averageDailyCost), COLORS2.amber), statRow4("Providers", `${providers.length} active`), statRow4("Peak Day", stats.peakDay ? `${stats.peakDay.date} (${formatTokens15(stats.peakDay.tokens)})` : "N/A"), statRow4("Input Tokens", formatTokens15(stats.totalInputTokens)), statRow4("Output Tokens", formatTokens15(stats.totalOutputTokens)));
|
|
11345
11696
|
}
|
|
11346
11697
|
return Box14({
|
|
11347
11698
|
flexDirection: "column",
|
|
@@ -11385,7 +11736,7 @@ function createProvidersPanel(state) {
|
|
|
11385
11736
|
const p = sorted[i];
|
|
11386
11737
|
const share = totalTokens > 0 ? p.windowTokens / totalTokens : 0;
|
|
11387
11738
|
const color2 = getProviderColor3(p.provider, i);
|
|
11388
|
-
children.push(Box14({ flexDirection: "row", width: "100%" }, Text14({ content: padRight(p.displayName, 14), fg: color2, attributes: BOLD3 }), Text14({ content: padLeft(
|
|
11739
|
+
children.push(Box14({ flexDirection: "row", width: "100%" }, Text14({ content: padRight(p.displayName, 14), fg: color2, attributes: BOLD3 }), Text14({ content: padLeft(formatTokens15(p.windowTokens), 10), fg: COLORS2.green }), Text14({ content: padLeft(formatCost15(p.windowCost), 10), fg: COLORS2.amber }), Text14({
|
|
11389
11740
|
content: padLeft(`${(share * 100).toFixed(1)}%`, 8),
|
|
11390
11741
|
fg: COLORS2.white
|
|
11391
11742
|
}), Text14({ content: " " }), Text14({ content: asciiBar(share, 10), fg: color2 })));
|
|
@@ -11415,10 +11766,10 @@ function createTopModelsPanel(state) {
|
|
|
11415
11766
|
const isTop = i === 0;
|
|
11416
11767
|
const nameColor = isTop ? COLORS2.amber : COLORS2.green;
|
|
11417
11768
|
children.push(Box14({ flexDirection: "row", width: "100%" }, Text14({ content: padRight(`${i + 1}.`, 3), fg: COLORS2.dimWhite }), Text14({
|
|
11418
|
-
content: padRight(
|
|
11769
|
+
content: padRight(truncate3(m.model, 27), 28),
|
|
11419
11770
|
fg: nameColor,
|
|
11420
11771
|
attributes: isTop ? BOLD3 : undefined
|
|
11421
|
-
}), Text14({ content: padLeft(
|
|
11772
|
+
}), Text14({ content: padLeft(formatTokens15(m.tokens), 10), fg: COLORS2.green }), Text14({ content: padLeft(formatCost15(m.cost), 10), fg: COLORS2.amber }), Text14({
|
|
11422
11773
|
content: padLeft(`${m.percentage.toFixed(1)}%`, 8),
|
|
11423
11774
|
fg: COLORS2.white
|
|
11424
11775
|
})));
|
|
@@ -11531,7 +11882,7 @@ function renderRecommendation2(rec) {
|
|
|
11531
11882
|
fg: confidenceColor2(rec.confidence),
|
|
11532
11883
|
attributes: BOLD3
|
|
11533
11884
|
})), Text15({
|
|
11534
|
-
content: ` ${rec.description}. Saves ${
|
|
11885
|
+
content: ` ${rec.description}. Saves ${formatCost15(rec.monthlySavings)}/mo`,
|
|
11535
11886
|
fg: COLORS2.dimWhite
|
|
11536
11887
|
}), Text15({ content: "", fg: COLORS2.dimWhite }));
|
|
11537
11888
|
}
|
|
@@ -11552,13 +11903,13 @@ function createAdvisorPanel(state, report) {
|
|
|
11552
11903
|
const offset = Math.min(state.advisorScrollOffset, maxOffset);
|
|
11553
11904
|
const visibleRecs = recs.slice(offset, offset + VISIBLE_ROWS);
|
|
11554
11905
|
const summaryRow = Box15({ flexDirection: "row", width: "100%", paddingLeft: 1, paddingRight: 1 }, Text15({
|
|
11555
|
-
content: `Current: ${
|
|
11906
|
+
content: `Current: ${formatCost15(report.totalCurrentMonthlyCost)}/mo`,
|
|
11556
11907
|
fg: COLORS2.white
|
|
11557
11908
|
}), Text15({ content: " \u2192 ", fg: COLORS2.dimWhite }), Text15({
|
|
11558
|
-
content: `Projected: ${
|
|
11909
|
+
content: `Projected: ${formatCost15(report.totalProjectedMonthlyCost)}/mo`,
|
|
11559
11910
|
fg: COLORS2.green
|
|
11560
11911
|
}), Text15({ content: " | ", fg: COLORS2.dimWhite }), Text15({
|
|
11561
|
-
content: `Savings: ${
|
|
11912
|
+
content: `Savings: ${formatCost15(report.totalMonthlySavings)}/mo`,
|
|
11562
11913
|
fg: COLORS2.amber,
|
|
11563
11914
|
attributes: BOLD3
|
|
11564
11915
|
}));
|
|
@@ -11592,7 +11943,7 @@ function formatHours(ms) {
|
|
|
11592
11943
|
return `${(ms / 3600000).toFixed(1)}h`;
|
|
11593
11944
|
}
|
|
11594
11945
|
function formatTokPerHour(tokPerHour) {
|
|
11595
|
-
return `${
|
|
11946
|
+
return `${formatTokens15(tokPerHour)}/hr`;
|
|
11596
11947
|
}
|
|
11597
11948
|
function renderEntry(entry, rank, sessionColWidth) {
|
|
11598
11949
|
const barWidth = 10;
|
|
@@ -11600,11 +11951,11 @@ function renderEntry(entry, rank, sessionColWidth) {
|
|
|
11600
11951
|
const bar = asciiBar(ratio, barWidth);
|
|
11601
11952
|
const rankStr = padLeft(`${rank}.`, 3);
|
|
11602
11953
|
const scoreStr = padLeft(`${Math.round(entry.score)}`, 4);
|
|
11603
|
-
const label =
|
|
11954
|
+
const label = truncate3(entry.label || entry.sessionId, sessionColWidth);
|
|
11604
11955
|
const startDate = entry.start ? formatShortDate(entry.start.slice(0, 10)) : "";
|
|
11605
11956
|
const duration = formatHours(entry.durationMs);
|
|
11606
11957
|
const density = formatTokPerHour(entry.tokensPerHour);
|
|
11607
|
-
const cost =
|
|
11958
|
+
const cost = formatCost15(entry.cost);
|
|
11608
11959
|
return Box16({ flexDirection: "column", width: "100%", paddingLeft: 1, paddingRight: 1 }, Box16({ flexDirection: "row", width: "100%" }, Text16({ content: `${rankStr} `, fg: COLORS2.dimWhite }), Text16({ content: bar, fg: COLORS2.green }), Text16({ content: ` ${scoreStr} `, fg: COLORS2.amber, attributes: BOLD3 }), Text16({ content: padRight(label, sessionColWidth + 2), fg: COLORS2.white }), Text16({ content: padLeft(startDate, 8), fg: COLORS2.dimWhite }), Text16({ content: padLeft(duration, 7), fg: COLORS2.cyan }), Text16({ content: padLeft(density, 13), fg: COLORS2.green }), Text16({ content: padLeft(cost, 10), fg: COLORS2.amber })), Text16({
|
|
11609
11960
|
content: ` \u2192 ${entry.rationale.join("; ")}`,
|
|
11610
11961
|
fg: COLORS2.dimWhite
|
|
@@ -11664,7 +12015,7 @@ function renderEvidenceTable2(title, rows) {
|
|
|
11664
12015
|
if (rows.length === 0)
|
|
11665
12016
|
return Box17({ flexDirection: "column", width: "100%" });
|
|
11666
12017
|
const header = Box17({ flexDirection: "row", width: "100%", paddingLeft: 1, paddingRight: 1 }, Text17({ content: padRight(title, 22), fg: COLORS2.amber, attributes: BOLD3 }), Text17({ content: padLeft("Tokens", 12), fg: COLORS2.dimWhite }), Text17({ content: padLeft("Share", 10), fg: COLORS2.dimWhite }), Text17({ content: padLeft("Cost", 12), fg: COLORS2.dimWhite }));
|
|
11667
|
-
const rowNodes = rows.slice(0, 8).map((row) => Box17({ flexDirection: "row", width: "100%", paddingLeft: 1, paddingRight: 1 }, Text17({ content: padRight(row.label, 22), fg: COLORS2.white }), Text17({ content: padLeft(
|
|
12018
|
+
const rowNodes = rows.slice(0, 8).map((row) => Box17({ flexDirection: "row", width: "100%", paddingLeft: 1, paddingRight: 1 }, Text17({ content: padRight(row.label, 22), fg: COLORS2.white }), Text17({ content: padLeft(formatTokens15(row.tokens), 12), fg: COLORS2.green }), Text17({ content: padLeft(formatPercent5(row.share), 10), fg: COLORS2.cyan }), Text17({ content: padLeft(formatCost15(row.cost), 12), fg: COLORS2.amber })));
|
|
11668
12019
|
return Box17({ flexDirection: "column", width: "100%" }, header, ...rowNodes, Text17({ content: "", fg: COLORS2.dimWhite }));
|
|
11669
12020
|
}
|
|
11670
12021
|
function renderAnomalies(anomalies) {
|
|
@@ -11708,7 +12059,7 @@ function createExplainPanel(report, explainDate) {
|
|
|
11708
12059
|
fg: COLORS2.cyan,
|
|
11709
12060
|
attributes: BOLD3
|
|
11710
12061
|
}), Text17({ content: "", fg: COLORS2.dimWhite }), Text17({
|
|
11711
|
-
content: `Tokens: ${
|
|
12062
|
+
content: `Tokens: ${formatTokens15(report.totalTokens)} Cost: ${formatCost15(report.totalCost)} vs 7d avg: ${report.comparedTo7dAverage >= 0 ? "+" : ""}${(report.comparedTo7dAverage * 100).toFixed(0)}% vs 30d avg: ${report.comparedTo30dAverage >= 0 ? "+" : ""}${(report.comparedTo30dAverage * 100).toFixed(0)}%`,
|
|
11712
12063
|
fg: COLORS2.white
|
|
11713
12064
|
}), Text17({ content: "", fg: COLORS2.dimWhite }), Text17({ content: "Summary:", fg: COLORS2.amber, attributes: BOLD3 }), ...summaryNodes, Text17({ content: "", fg: COLORS2.dimWhite })), renderEvidenceTable2("Top Providers", report.topProviders), renderEvidenceTable2("Top Models", report.topModels), renderAnomalies(report.anomalies));
|
|
11714
12065
|
}
|
|
@@ -11734,16 +12085,16 @@ function buildMetricRows(output2) {
|
|
|
11734
12085
|
return [
|
|
11735
12086
|
{
|
|
11736
12087
|
label: "Tokens",
|
|
11737
|
-
current:
|
|
11738
|
-
previous:
|
|
12088
|
+
current: formatTokens15(a.totalTokens),
|
|
12089
|
+
previous: formatTokens15(b.totalTokens),
|
|
11739
12090
|
delta: d.tokens,
|
|
11740
12091
|
deltaLabel: `${deltaArrow(d.tokens)} ${deltaStr(d.tokens, true)}`,
|
|
11741
12092
|
invertColor: false
|
|
11742
12093
|
},
|
|
11743
12094
|
{
|
|
11744
12095
|
label: "Cost",
|
|
11745
|
-
current:
|
|
11746
|
-
previous:
|
|
12096
|
+
current: formatCost15(a.totalCost),
|
|
12097
|
+
previous: formatCost15(b.totalCost),
|
|
11747
12098
|
delta: d.cost,
|
|
11748
12099
|
deltaLabel: `${deltaArrow(d.cost)} ${deltaStr(d.cost, true)}`,
|
|
11749
12100
|
invertColor: true
|
|
@@ -11758,8 +12109,8 @@ function buildMetricRows(output2) {
|
|
|
11758
12109
|
},
|
|
11759
12110
|
{
|
|
11760
12111
|
label: "Avg Daily Tokens",
|
|
11761
|
-
current:
|
|
11762
|
-
previous:
|
|
12112
|
+
current: formatTokens15(a.averageDailyTokens),
|
|
12113
|
+
previous: formatTokens15(b.averageDailyTokens),
|
|
11763
12114
|
delta: d.averageDailyTokens,
|
|
11764
12115
|
deltaLabel: `${deltaArrow(d.averageDailyTokens)} ${deltaStr(d.averageDailyTokens, true)}`,
|
|
11765
12116
|
invertColor: false
|
|
@@ -11900,7 +12251,7 @@ function createWrappedPanel(stats, achievements, providers, scrollOffset, more)
|
|
|
11900
12251
|
const activePct = `${stats.activeDays} / ${totalDays}`;
|
|
11901
12252
|
const contentRows = [];
|
|
11902
12253
|
const statRow5 = (...cells) => Box20({ flexDirection: "row", width: "100%", justifyContent: "space-evenly" }, ...cells);
|
|
11903
|
-
contentRows.push(Text20({ content: "", fg: COLORS2.dimWhite }), statRow5(bigStat("TOTAL TOKENS",
|
|
12254
|
+
contentRows.push(Text20({ content: "", fg: COLORS2.dimWhite }), statRow5(bigStat("TOTAL TOKENS", formatTokens15(stats.totalTokens), COLORS2.green), bigStat("TOTAL COST", formatCost15(stats.totalCost), COLORS2.amber), bigStat("ACTIVE DAYS", activePct, COLORS2.cyan)), Text20({ content: "", fg: COLORS2.dimWhite }), statRow5(bigStat("STREAK", `${stats.currentStreak} days`, COLORS2.green), bigStat("CACHE HIT", `${(stats.cacheHitRate * 100).toFixed(1)}%`, COLORS2.cyan), bigStat("AVG DAILY", formatTokens15(stats.averageDailyTokens) + " tok", COLORS2.green)), Text20({ content: "", fg: COLORS2.dimWhite }), statRow5(bigStat("INPUT TOKENS", formatTokens15(stats.totalInputTokens), COLORS2.green), bigStat("OUTPUT TOKENS", formatTokens15(stats.totalOutputTokens), COLORS2.amber), bigStat("PEAK DAY", stats.peakDay ? `${stats.peakDay.date}` : "N/A", COLORS2.cyan)));
|
|
11904
12255
|
if (achievements.length > 0) {
|
|
11905
12256
|
contentRows.push(Text20({
|
|
11906
12257
|
content: "\u2500\u2500\u2500 ACHIEVEMENTS " + "\u2500".repeat(40),
|
|
@@ -11924,8 +12275,8 @@ function createWrappedPanel(stats, achievements, providers, scrollOffset, more)
|
|
|
11924
12275
|
const cacheReuse = more.cacheEconomics.reuseRatio !== null ? `${more.cacheEconomics.reuseRatio.toFixed(1)}x` : "-";
|
|
11925
12276
|
contentRows.push(Box20({ flexDirection: "row", width: "100%" }, Text20({ content: " I/O Ratio: ", fg: COLORS2.dimWhite }), Text20({ content: padRight(ioRatio, 10), fg: COLORS2.green }), Text20({ content: "Output Share: ", fg: COLORS2.dimWhite }), Text20({ content: padRight(outputShare, 10), fg: COLORS2.green }), Text20({ content: "Cache Reuse: ", fg: COLORS2.dimWhite }), Text20({ content: cacheReuse, fg: COLORS2.cyan })));
|
|
11926
12277
|
const burn = more.monthlyBurn;
|
|
11927
|
-
const projCost =
|
|
11928
|
-
const dailyRate = burn.observedDays > 0 ?
|
|
12278
|
+
const projCost = formatCost15(burn.projectedCost);
|
|
12279
|
+
const dailyRate = burn.observedDays > 0 ? formatCost15(burn.projectedCost / (burn.calendarDays || 30)) : "-";
|
|
11929
12280
|
contentRows.push(Box20({ flexDirection: "row", width: "100%" }, Text20({ content: " Monthly Burn: ", fg: COLORS2.dimWhite }), Text20({ content: `${projCost} projected`, fg: COLORS2.amber }), Text20({ content: " Burn Rate: ", fg: COLORS2.dimWhite }), Text20({ content: `${dailyRate}/day`, fg: COLORS2.amber })));
|
|
11930
12281
|
}
|
|
11931
12282
|
const models = stats.topModels.slice(0, 5);
|
|
@@ -11944,7 +12295,7 @@ function createWrappedPanel(stats, achievements, providers, scrollOffset, more)
|
|
|
11944
12295
|
content: padRight(m.model, 22),
|
|
11945
12296
|
fg: isTop ? COLORS2.amber : COLORS2.green,
|
|
11946
12297
|
attributes: isTop ? BOLD3 : undefined
|
|
11947
|
-
}), Text20({ content: asciiBar(ratio, 15), fg: isTop ? COLORS2.amber : COLORS2.green }), Text20({ content: ` ${m.percentage.toFixed(1)}%`, fg: COLORS2.white }), Text20({ content: padLeft(
|
|
12298
|
+
}), Text20({ content: asciiBar(ratio, 15), fg: isTop ? COLORS2.amber : COLORS2.green }), Text20({ content: ` ${m.percentage.toFixed(1)}%`, fg: COLORS2.white }), Text20({ content: padLeft(formatCost15(m.cost), 10), fg: COLORS2.amber })));
|
|
11948
12299
|
}
|
|
11949
12300
|
}
|
|
11950
12301
|
if (providers.length > 0) {
|
|
@@ -11956,7 +12307,7 @@ function createWrappedPanel(stats, achievements, providers, scrollOffset, more)
|
|
|
11956
12307
|
}));
|
|
11957
12308
|
for (const p of providers.slice(0, 5)) {
|
|
11958
12309
|
const pct = (p.totalTokens / totalTokens * 100).toFixed(0);
|
|
11959
|
-
contentRows.push(Box20({ flexDirection: "row", width: "100%" }, Text20({ content: ` ${padRight(p.displayName, 16)}`, fg: COLORS2.green, attributes: BOLD3 }), Text20({ content: padLeft(`${
|
|
12310
|
+
contentRows.push(Box20({ flexDirection: "row", width: "100%" }, Text20({ content: ` ${padRight(p.displayName, 16)}`, fg: COLORS2.green, attributes: BOLD3 }), Text20({ content: padLeft(`${formatTokens15(p.totalTokens)} tokens`, 16), fg: COLORS2.green }), Text20({ content: padLeft(formatCost15(p.totalCost), 10), fg: COLORS2.amber }), Text20({ content: padLeft(`${pct}%`, 7), fg: COLORS2.white })));
|
|
11960
12311
|
}
|
|
11961
12312
|
}
|
|
11962
12313
|
const visible = contentRows.slice(scrollOffset);
|
|
@@ -12046,12 +12397,169 @@ var init_help = __esm(() => {
|
|
|
12046
12397
|
init_theme2();
|
|
12047
12398
|
});
|
|
12048
12399
|
|
|
12400
|
+
// packages/tui/dist/panels/replay.js
|
|
12401
|
+
import { Box as Box22, Text as Text22 } from "@opentui/core";
|
|
12402
|
+
function formatTime2(iso) {
|
|
12403
|
+
const date = new Date(iso);
|
|
12404
|
+
return `${String(date.getUTCHours()).padStart(2, "0")}:${String(date.getUTCMinutes()).padStart(2, "0")}`;
|
|
12405
|
+
}
|
|
12406
|
+
function formatDuration7(ms) {
|
|
12407
|
+
if (ms <= 0)
|
|
12408
|
+
return "0s";
|
|
12409
|
+
const hours = Math.floor(ms / 3600000);
|
|
12410
|
+
const minutes = Math.floor(ms % 3600000 / 60000);
|
|
12411
|
+
if (hours > 0)
|
|
12412
|
+
return `${hours}h ${minutes}m`;
|
|
12413
|
+
return `${minutes}m`;
|
|
12414
|
+
}
|
|
12415
|
+
function renderActivityBar2(report) {
|
|
12416
|
+
if (report.events.length === 0) {
|
|
12417
|
+
return Text22({ content: " (no events)", fg: COLORS2.dimWhite });
|
|
12418
|
+
}
|
|
12419
|
+
const slotTokens = new Array(HEATMAP_SLOTS2).fill(0);
|
|
12420
|
+
for (const event of report.events) {
|
|
12421
|
+
const date = new Date(event.timestamp);
|
|
12422
|
+
const slot = Math.min(date.getUTCHours() * 2 + Math.floor(date.getUTCMinutes() / 30), HEATMAP_SLOTS2 - 1);
|
|
12423
|
+
slotTokens[slot] += event.totalTokens;
|
|
12424
|
+
}
|
|
12425
|
+
const maxTokens = Math.max(...slotTokens);
|
|
12426
|
+
let bar = "";
|
|
12427
|
+
for (let i = 0;i < HEATMAP_SLOTS2; i++) {
|
|
12428
|
+
const level = maxTokens > 0 ? Math.round(slotTokens[i] / maxTokens * (HEATMAP_BLOCKS3.length - 1)) : 0;
|
|
12429
|
+
bar += HEATMAP_BLOCKS3[level];
|
|
12430
|
+
}
|
|
12431
|
+
const firstTime = formatTime2(report.events[0].timestamp);
|
|
12432
|
+
const lastTime = formatTime2(report.events[report.events.length - 1].timestamp);
|
|
12433
|
+
return Box22({ flexDirection: "column", width: "100%", paddingLeft: 1, paddingRight: 1 }, Text22({ content: bar, fg: COLORS2.green }), Text22({ content: `${firstTime}${" ".repeat(Math.max(1, HEATMAP_SLOTS2 - firstTime.length - lastTime.length))}${lastTime}`, fg: COLORS2.dimWhite }));
|
|
12434
|
+
}
|
|
12435
|
+
function renderFlowBlockCard2(block, expanded) {
|
|
12436
|
+
const timeRange = `${formatTime2(block.start)}\u2013${formatTime2(block.end)}`;
|
|
12437
|
+
const headerText = `${timeRange} ${block.label} | ${block.eventCount} events | ${formatTokens15(block.totalTokens)} tok | ${formatCost15(block.cost)}`;
|
|
12438
|
+
const expandIcon = expanded ? "\u25BC" : "\u25B6";
|
|
12439
|
+
const headerLine = Text22({
|
|
12440
|
+
content: ` ${expandIcon} ${headerText}`,
|
|
12441
|
+
fg: block.label === "Deep Flow" ? COLORS2.cyan : block.label === "Quick Lookup" ? COLORS2.dimWhite : COLORS2.white,
|
|
12442
|
+
attributes: BOLD3
|
|
12443
|
+
});
|
|
12444
|
+
if (!expanded) {
|
|
12445
|
+
return Box22({ flexDirection: "column", width: "100%" }, headerLine);
|
|
12446
|
+
}
|
|
12447
|
+
const children = [headerLine];
|
|
12448
|
+
children.push(Text22({
|
|
12449
|
+
content: ` Model: ${block.dominantModel}${block.modelSwitches > 0 ? ` (${block.modelSwitches} switch${block.modelSwitches === 1 ? "" : "es"})` : ""}`,
|
|
12450
|
+
fg: COLORS2.white
|
|
12451
|
+
}));
|
|
12452
|
+
for (const event of block.events) {
|
|
12453
|
+
const time = formatTime2(event.timestamp);
|
|
12454
|
+
const cacheRate = event.inputTokens + event.cacheReadTokens > 0 ? event.cacheReadTokens / (event.inputTokens + event.cacheReadTokens) : 0;
|
|
12455
|
+
const line2 = ` ${padRight(time, 7)} ${padRight(truncate3(event.model, 18), 19)} ${padLeft(formatTokens15(event.totalTokens), 8)} cache:${formatPercent5(cacheRate).padStart(4)} ${formatCost15(event.cost).padStart(8)}`;
|
|
12456
|
+
children.push(Text22({ content: line2, fg: COLORS2.white }));
|
|
12457
|
+
}
|
|
12458
|
+
const trend = block.cacheHitRateTrend;
|
|
12459
|
+
if (trend.length > 1) {
|
|
12460
|
+
const first = (trend[0] * 100).toFixed(0);
|
|
12461
|
+
const last = (trend[trend.length - 1] * 100).toFixed(0);
|
|
12462
|
+
if (first !== last) {
|
|
12463
|
+
const direction = Number(last) > Number(first) ? "\u2191" : "\u2193";
|
|
12464
|
+
children.push(Text22({
|
|
12465
|
+
content: ` Cache: ${first}% \u2192 ${last}% ${direction}`,
|
|
12466
|
+
fg: Number(last) > Number(first) ? COLORS2.green : COLORS2.red
|
|
12467
|
+
}));
|
|
12468
|
+
}
|
|
12469
|
+
}
|
|
12470
|
+
children.push(Text22({ content: "", fg: COLORS2.dimWhite }));
|
|
12471
|
+
return Box22({ flexDirection: "column", width: "100%" }, ...children);
|
|
12472
|
+
}
|
|
12473
|
+
function renderPulseChart2(velocity) {
|
|
12474
|
+
if (velocity.length === 0) {
|
|
12475
|
+
return Box22({ flexDirection: "column", width: "100%", paddingLeft: 1, paddingRight: 1 }, Text22({ content: "Pulse", fg: COLORS2.amber, attributes: BOLD3 }), Text22({ content: " (no data)", fg: COLORS2.dimWhite }));
|
|
12476
|
+
}
|
|
12477
|
+
const maxTpm = Math.max(...velocity.map((v) => v.tokensPerMinute));
|
|
12478
|
+
const chartWidth = Math.min(velocity.length, 60);
|
|
12479
|
+
const chartHeight = 5;
|
|
12480
|
+
const step = velocity.length / chartWidth;
|
|
12481
|
+
const rows = [];
|
|
12482
|
+
for (let row = 0;row < chartHeight; row++) {
|
|
12483
|
+
let line2 = "";
|
|
12484
|
+
for (let col = 0;col < chartWidth; col++) {
|
|
12485
|
+
const idx = Math.floor(col * step);
|
|
12486
|
+
const tpm = velocity[idx].tokensPerMinute;
|
|
12487
|
+
const normalizedHeight = maxTpm > 0 ? tpm / maxTpm * (chartHeight - 1) : 0;
|
|
12488
|
+
const threshold = chartHeight - 1 - row;
|
|
12489
|
+
line2 += normalizedHeight >= threshold ? "\u2588" : " ";
|
|
12490
|
+
}
|
|
12491
|
+
rows.push(line2);
|
|
12492
|
+
}
|
|
12493
|
+
const children = [
|
|
12494
|
+
Text22({ content: "Pulse (tok/min)", fg: COLORS2.amber, attributes: BOLD3 })
|
|
12495
|
+
];
|
|
12496
|
+
for (let i = 0;i < rows.length; i++) {
|
|
12497
|
+
const label = i === 0 ? padLeft(formatTokens15(maxTpm), 7) : i === rows.length - 1 ? padLeft("0", 7) : " ";
|
|
12498
|
+
children.push(Text22({ content: `${label} \u2502${rows[i]}`, fg: COLORS2.green }));
|
|
12499
|
+
}
|
|
12500
|
+
return Box22({ flexDirection: "column", width: "100%", paddingLeft: 1, paddingRight: 1 }, ...children);
|
|
12501
|
+
}
|
|
12502
|
+
function renderDaySummary(report) {
|
|
12503
|
+
const s = report.summary;
|
|
12504
|
+
const parts = [
|
|
12505
|
+
`Sessions: ${s.totalSessions}`,
|
|
12506
|
+
`Events: ${s.totalEvents}`,
|
|
12507
|
+
`Flow: ${formatDuration7(s.flowTimeMs)}`,
|
|
12508
|
+
`Think: ${formatDuration7(s.thinkTimeMs)}`,
|
|
12509
|
+
`Ratio: ${(s.flowThinkRatio * 100).toFixed(0)}%`
|
|
12510
|
+
];
|
|
12511
|
+
if (s.peakMinute) {
|
|
12512
|
+
parts.push(`Peak: ${formatTokens15(s.peakMinute.tokensPerMinute)} tok/min at ${formatTime2(s.peakMinute.minute)}`);
|
|
12513
|
+
}
|
|
12514
|
+
return Box22({ flexDirection: "column", width: "100%", paddingLeft: 1, paddingRight: 1 }, Text22({ content: parts.join(" | "), fg: COLORS2.white }));
|
|
12515
|
+
}
|
|
12516
|
+
function createReplayPanel(report, replayDate, expandedBlocks, scrollOffset) {
|
|
12517
|
+
const dateLabel = replayDate ? formatShortDate(replayDate) : "\u2014";
|
|
12518
|
+
if (!report) {
|
|
12519
|
+
return Box22({
|
|
12520
|
+
flexDirection: "column",
|
|
12521
|
+
width: "100%",
|
|
12522
|
+
flexGrow: 1,
|
|
12523
|
+
borderStyle: "single",
|
|
12524
|
+
borderColor: COLORS2.dimWhite,
|
|
12525
|
+
paddingLeft: 1,
|
|
12526
|
+
paddingRight: 1
|
|
12527
|
+
}, Text22({
|
|
12528
|
+
content: ` Replay: ${dateLabel} \u25C4 \u25BA `,
|
|
12529
|
+
fg: COLORS2.amber,
|
|
12530
|
+
attributes: BOLD3
|
|
12531
|
+
}), Text22({ content: "", fg: COLORS2.dimWhite }), Text22({ content: "No data available for this date", fg: COLORS2.dimWhite }));
|
|
12532
|
+
}
|
|
12533
|
+
const totalCost = report.events.reduce((sum, e) => sum + e.cost, 0);
|
|
12534
|
+
const blockCards = report.flowBlocks.slice(scrollOffset, scrollOffset + 20).map((block) => renderFlowBlockCard2(block, expandedBlocks.has(block.blockIndex)));
|
|
12535
|
+
return Box22({
|
|
12536
|
+
flexDirection: "column",
|
|
12537
|
+
width: "100%",
|
|
12538
|
+
flexGrow: 1,
|
|
12539
|
+
borderStyle: "single",
|
|
12540
|
+
borderColor: COLORS2.dimWhite
|
|
12541
|
+
}, Text22({
|
|
12542
|
+
content: ` Replay: ${dateLabel} \u25C4 \u25BA `,
|
|
12543
|
+
fg: COLORS2.amber,
|
|
12544
|
+
attributes: BOLD3
|
|
12545
|
+
}), Box22({ flexDirection: "row", width: "100%", paddingLeft: 1, paddingRight: 1 }, Text22({ content: `Total: ${formatCost15(totalCost)}`, fg: COLORS2.green })), Text22({ content: "", fg: COLORS2.dimWhite }), renderActivityBar2(report), Text22({ content: "", fg: COLORS2.dimWhite }), Box22({ flexDirection: "column", width: "100%", paddingLeft: 1, paddingRight: 1 }, Text22({
|
|
12546
|
+
content: `Flow Blocks (${report.flowBlocks.length})`,
|
|
12547
|
+
fg: COLORS2.amber,
|
|
12548
|
+
attributes: BOLD3
|
|
12549
|
+
})), ...blockCards, Text22({ content: "", fg: COLORS2.dimWhite }), renderPulseChart2(report.tokenVelocity), Text22({ content: "", fg: COLORS2.dimWhite }), renderDaySummary(report));
|
|
12550
|
+
}
|
|
12551
|
+
var HEATMAP_BLOCKS3, HEATMAP_SLOTS2 = 48;
|
|
12552
|
+
var init_replay2 = __esm(() => {
|
|
12553
|
+
init_theme2();
|
|
12554
|
+
HEATMAP_BLOCKS3 = [" ", "\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
12555
|
+
});
|
|
12556
|
+
|
|
12049
12557
|
// packages/tui/dist/index.js
|
|
12050
12558
|
var exports_dist2 = {};
|
|
12051
12559
|
__export(exports_dist2, {
|
|
12052
12560
|
main: () => main
|
|
12053
12561
|
});
|
|
12054
|
-
import { Box as
|
|
12562
|
+
import { Box as Box23, Text as Text23, createCliRenderer } from "@opentui/core";
|
|
12055
12563
|
function clearRoot(renderer) {
|
|
12056
12564
|
const children = renderer.root.getChildren();
|
|
12057
12565
|
for (const child of children) {
|
|
@@ -12085,7 +12593,7 @@ function buildContent(state, renderer) {
|
|
|
12085
12593
|
const daily = state.data ? getDailyForWindow(state.data, state.selectedWindowIndex) : [];
|
|
12086
12594
|
switch (state.selectedView) {
|
|
12087
12595
|
case "overview":
|
|
12088
|
-
return
|
|
12596
|
+
return Box23({ flexDirection: "column", width: "100%", flexGrow: 1 }, createChartPanel(state, daily), createStatsRow(state, windowStats), createModelList(state, windowStats));
|
|
12089
12597
|
case "matrix":
|
|
12090
12598
|
return createMatrixView(state);
|
|
12091
12599
|
case "advisor":
|
|
@@ -12098,6 +12606,8 @@ function buildContent(state, renderer) {
|
|
|
12098
12606
|
return createComparePanel(state, ensureCompareOutput(state));
|
|
12099
12607
|
case "export":
|
|
12100
12608
|
return createExportPanel(state);
|
|
12609
|
+
case "replay":
|
|
12610
|
+
return createReplayPanel(ensureReplayReport(state), state.replayDate, state.replayExpandedBlocks, state.replayScrollOffset);
|
|
12101
12611
|
case "wrapped": {
|
|
12102
12612
|
const output2 = buildTokenleakOutput(state);
|
|
12103
12613
|
const achievements = output2 ? computeAchievements(output2) : [];
|
|
@@ -12109,7 +12619,7 @@ function buildContent(state, renderer) {
|
|
|
12109
12619
|
return createWrappedPanel(windowStats, achievements, providers, state.wrappedScrollOffset, ensureMoreStats(state));
|
|
12110
12620
|
}
|
|
12111
12621
|
default:
|
|
12112
|
-
return
|
|
12622
|
+
return Box23({ flexDirection: "column", width: "100%", flexGrow: 1 });
|
|
12113
12623
|
}
|
|
12114
12624
|
}
|
|
12115
12625
|
function buildTokenleakOutput(state) {
|
|
@@ -12164,6 +12674,9 @@ function applyLoadedData(state, freshData) {
|
|
|
12164
12674
|
state.focusScrollOffset = 0;
|
|
12165
12675
|
state.compareScrollOffset = 0;
|
|
12166
12676
|
state.wrappedScrollOffset = 0;
|
|
12677
|
+
state.replayScrollOffset = 0;
|
|
12678
|
+
state.replayExpandedBlocks = new Set;
|
|
12679
|
+
state.replayDate = null;
|
|
12167
12680
|
state.explainDate = null;
|
|
12168
12681
|
state.cursorSetupStatusOverride = null;
|
|
12169
12682
|
}
|
|
@@ -12253,6 +12766,8 @@ function handleViewSwitch(mode) {
|
|
|
12253
12766
|
currentState.focusScrollOffset = 0;
|
|
12254
12767
|
currentState.compareScrollOffset = 0;
|
|
12255
12768
|
currentState.wrappedScrollOffset = 0;
|
|
12769
|
+
currentState.replayScrollOffset = 0;
|
|
12770
|
+
currentState.replayExpandedBlocks = new Set;
|
|
12256
12771
|
if (mode === "matrix") {
|
|
12257
12772
|
currentState.matrixPage = 0;
|
|
12258
12773
|
}
|
|
@@ -12261,7 +12776,7 @@ function handleViewSwitch(mode) {
|
|
|
12261
12776
|
}
|
|
12262
12777
|
function buildLayout(state, renderer) {
|
|
12263
12778
|
const cursorBanner = buildCursorBanner(state);
|
|
12264
|
-
return
|
|
12779
|
+
return Box23({
|
|
12265
12780
|
flexDirection: "column",
|
|
12266
12781
|
width: "100%",
|
|
12267
12782
|
height: "100%",
|
|
@@ -12290,7 +12805,9 @@ function invalidateWindowCaches(state) {
|
|
|
12290
12805
|
state.cachedFocusReport = null;
|
|
12291
12806
|
state.cachedExplainReport = null;
|
|
12292
12807
|
state.cachedMoreStats = null;
|
|
12808
|
+
state.cachedReplayReport = null;
|
|
12293
12809
|
state.explainDate = null;
|
|
12810
|
+
state.replayDate = null;
|
|
12294
12811
|
}
|
|
12295
12812
|
function invalidateAllCaches(state) {
|
|
12296
12813
|
state.cachedAdvisorReport = null;
|
|
@@ -12298,6 +12815,17 @@ function invalidateAllCaches(state) {
|
|
|
12298
12815
|
state.cachedExplainReport = null;
|
|
12299
12816
|
state.cachedCompareOutput = null;
|
|
12300
12817
|
state.cachedMoreStats = null;
|
|
12818
|
+
state.cachedReplayReport = null;
|
|
12819
|
+
}
|
|
12820
|
+
function shiftReplayDate(state, direction) {
|
|
12821
|
+
if (!state.replayDate)
|
|
12822
|
+
return;
|
|
12823
|
+
const d = new Date(state.replayDate + "T12:00:00Z");
|
|
12824
|
+
d.setUTCDate(d.getUTCDate() + direction);
|
|
12825
|
+
state.replayDate = d.toISOString().slice(0, 10);
|
|
12826
|
+
state.cachedReplayReport = null;
|
|
12827
|
+
state.replayScrollOffset = 0;
|
|
12828
|
+
state.replayExpandedBlocks = new Set;
|
|
12301
12829
|
}
|
|
12302
12830
|
function shiftExplainDate(state, direction) {
|
|
12303
12831
|
if (!state.explainDate)
|
|
@@ -12319,6 +12847,8 @@ function getScrollableItemCount(state) {
|
|
|
12319
12847
|
return 6;
|
|
12320
12848
|
case "wrapped":
|
|
12321
12849
|
return 30;
|
|
12850
|
+
case "replay":
|
|
12851
|
+
return ensureReplayReport(state)?.flowBlocks.length ?? 0;
|
|
12322
12852
|
default:
|
|
12323
12853
|
return 0;
|
|
12324
12854
|
}
|
|
@@ -12333,6 +12863,8 @@ function getVisibleCount(view) {
|
|
|
12333
12863
|
return 6;
|
|
12334
12864
|
case "wrapped":
|
|
12335
12865
|
return 20;
|
|
12866
|
+
case "replay":
|
|
12867
|
+
return 15;
|
|
12336
12868
|
default:
|
|
12337
12869
|
return 10;
|
|
12338
12870
|
}
|
|
@@ -12347,6 +12879,8 @@ function getScrollOffset(state) {
|
|
|
12347
12879
|
return state.compareScrollOffset;
|
|
12348
12880
|
case "wrapped":
|
|
12349
12881
|
return state.wrappedScrollOffset;
|
|
12882
|
+
case "replay":
|
|
12883
|
+
return state.replayScrollOffset;
|
|
12350
12884
|
default:
|
|
12351
12885
|
return 0;
|
|
12352
12886
|
}
|
|
@@ -12365,6 +12899,9 @@ function setScrollOffset(state, value) {
|
|
|
12365
12899
|
case "wrapped":
|
|
12366
12900
|
state.wrappedScrollOffset = value;
|
|
12367
12901
|
break;
|
|
12902
|
+
case "replay":
|
|
12903
|
+
state.replayScrollOffset = value;
|
|
12904
|
+
break;
|
|
12368
12905
|
}
|
|
12369
12906
|
}
|
|
12370
12907
|
async function handleExport(key, state, renderer) {
|
|
@@ -12388,18 +12925,18 @@ async function handleExport(key, state, renderer) {
|
|
|
12388
12925
|
noColor: false,
|
|
12389
12926
|
output: null
|
|
12390
12927
|
});
|
|
12391
|
-
const { writeFileSync:
|
|
12928
|
+
const { writeFileSync: writeFileSync3 } = await import("fs");
|
|
12392
12929
|
const outputPath = "tokenleak.png";
|
|
12393
|
-
|
|
12930
|
+
writeFileSync3(outputPath, buffer);
|
|
12394
12931
|
state.exportStatus = `Saved to ${outputPath}`;
|
|
12395
12932
|
} else if (key === "w") {
|
|
12396
12933
|
state.exportStatus = "Rendering Wrapped PNG...";
|
|
12397
12934
|
render(state, renderer);
|
|
12398
12935
|
const { renderWrappedPng: renderWrappedPng2 } = await Promise.resolve().then(() => (init_dist3(), exports_dist));
|
|
12399
12936
|
const buffer = await renderWrappedPng2(output2, { theme: "dark" });
|
|
12400
|
-
const { writeFileSync:
|
|
12937
|
+
const { writeFileSync: writeFileSync3 } = await import("fs");
|
|
12401
12938
|
const outputPath = "tokenleak-wrapped.png";
|
|
12402
|
-
|
|
12939
|
+
writeFileSync3(outputPath, buffer);
|
|
12403
12940
|
state.exportStatus = `Saved to ${outputPath}`;
|
|
12404
12941
|
} else if (key === "l") {
|
|
12405
12942
|
state.exportStatus = "Starting live server...";
|
|
@@ -12547,11 +13084,31 @@ async function main() {
|
|
|
12547
13084
|
render(state, renderer);
|
|
12548
13085
|
return true;
|
|
12549
13086
|
}
|
|
13087
|
+
if (sequence === "h" && state.selectedView === "replay") {
|
|
13088
|
+
shiftReplayDate(state, -1);
|
|
13089
|
+
render(state, renderer);
|
|
13090
|
+
return true;
|
|
13091
|
+
}
|
|
12550
13092
|
if (sequence === "l" && state.selectedView === "explain") {
|
|
12551
13093
|
shiftExplainDate(state, 1);
|
|
12552
13094
|
render(state, renderer);
|
|
12553
13095
|
return true;
|
|
12554
13096
|
}
|
|
13097
|
+
if (sequence === "l" && state.selectedView === "replay") {
|
|
13098
|
+
shiftReplayDate(state, 1);
|
|
13099
|
+
render(state, renderer);
|
|
13100
|
+
return true;
|
|
13101
|
+
}
|
|
13102
|
+
if (sequence === "\r" && state.selectedView === "replay") {
|
|
13103
|
+
const blockIndex = state.replayScrollOffset;
|
|
13104
|
+
if (state.replayExpandedBlocks.has(blockIndex)) {
|
|
13105
|
+
state.replayExpandedBlocks.delete(blockIndex);
|
|
13106
|
+
} else {
|
|
13107
|
+
state.replayExpandedBlocks.add(blockIndex);
|
|
13108
|
+
}
|
|
13109
|
+
render(state, renderer);
|
|
13110
|
+
return true;
|
|
13111
|
+
}
|
|
12555
13112
|
if (sequence === "s") {
|
|
12556
13113
|
state.sortMode = state.sortMode === "cost" ? "tokens" : "cost";
|
|
12557
13114
|
state.modelScrollOffset = 0;
|
|
@@ -12570,21 +13127,21 @@ async function main() {
|
|
|
12570
13127
|
});
|
|
12571
13128
|
} catch (err) {
|
|
12572
13129
|
clearRoot(renderer);
|
|
12573
|
-
renderer.root.add(
|
|
13130
|
+
renderer.root.add(Box23({
|
|
12574
13131
|
flexDirection: "column",
|
|
12575
13132
|
width: "100%",
|
|
12576
13133
|
height: "100%",
|
|
12577
13134
|
backgroundColor: COLORS2.bg,
|
|
12578
13135
|
justifyContent: "center",
|
|
12579
13136
|
alignItems: "center"
|
|
12580
|
-
},
|
|
13137
|
+
}, Text23({
|
|
12581
13138
|
content: "TOKENLEAK TUI",
|
|
12582
13139
|
fg: COLORS2.amber,
|
|
12583
13140
|
attributes: BOLD3
|
|
12584
|
-
}),
|
|
13141
|
+
}), Text23({
|
|
12585
13142
|
content: `Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
12586
13143
|
fg: COLORS2.red
|
|
12587
|
-
}),
|
|
13144
|
+
}), Text23({
|
|
12588
13145
|
content: "Press q to quit",
|
|
12589
13146
|
fg: COLORS2.dimWhite
|
|
12590
13147
|
})));
|
|
@@ -12619,6 +13176,7 @@ var init_dist4 = __esm(() => {
|
|
|
12619
13176
|
init_export();
|
|
12620
13177
|
init_wrapped2();
|
|
12621
13178
|
init_help();
|
|
13179
|
+
init_replay2();
|
|
12622
13180
|
init_cursor_setup();
|
|
12623
13181
|
VIEW_KEYS = {
|
|
12624
13182
|
"1": "overview",
|
|
@@ -12628,7 +13186,8 @@ var init_dist4 = __esm(() => {
|
|
|
12628
13186
|
"5": "explain",
|
|
12629
13187
|
"6": "compare",
|
|
12630
13188
|
"7": "export",
|
|
12631
|
-
"8": "wrapped"
|
|
13189
|
+
"8": "wrapped",
|
|
13190
|
+
"9": "replay"
|
|
12632
13191
|
};
|
|
12633
13192
|
VIEW_ORDER = [
|
|
12634
13193
|
"overview",
|
|
@@ -12638,9 +13197,10 @@ var init_dist4 = __esm(() => {
|
|
|
12638
13197
|
"explain",
|
|
12639
13198
|
"compare",
|
|
12640
13199
|
"export",
|
|
12641
|
-
"wrapped"
|
|
13200
|
+
"wrapped",
|
|
13201
|
+
"replay"
|
|
12642
13202
|
];
|
|
12643
|
-
SCROLLABLE_VIEWS = new Set(["advisor", "focus", "compare", "wrapped"]);
|
|
13203
|
+
SCROLLABLE_VIEWS = new Set(["advisor", "focus", "compare", "wrapped", "replay"]);
|
|
12644
13204
|
});
|
|
12645
13205
|
|
|
12646
13206
|
// node_modules/citty/dist/_chunks/libs/scule.mjs
|
|
@@ -13059,17 +13619,17 @@ async function runMain(cmd, opts = {}) {
|
|
|
13059
13619
|
init_dist();
|
|
13060
13620
|
init_dist2();
|
|
13061
13621
|
init_dist3();
|
|
13062
|
-
import { writeFileSync as
|
|
13622
|
+
import { writeFileSync as writeFileSync3 } from "fs";
|
|
13063
13623
|
|
|
13064
13624
|
// packages/cli/src/config.ts
|
|
13065
|
-
import { readFileSync as
|
|
13066
|
-
import { join as
|
|
13067
|
-
import { homedir as
|
|
13625
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
13626
|
+
import { join as join8 } from "path";
|
|
13627
|
+
import { homedir as homedir8 } from "os";
|
|
13068
13628
|
var CONFIG_FILENAME = ".tokenleakrc";
|
|
13069
13629
|
function loadConfig() {
|
|
13070
13630
|
try {
|
|
13071
|
-
const configPath =
|
|
13072
|
-
const raw =
|
|
13631
|
+
const configPath = join8(homedir8(), CONFIG_FILENAME);
|
|
13632
|
+
const raw = readFileSync5(configPath, "utf-8");
|
|
13073
13633
|
const parsed = JSON.parse(raw);
|
|
13074
13634
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
13075
13635
|
return parsed;
|
|
@@ -13591,6 +14151,189 @@ function buildExplainHelpText() {
|
|
|
13591
14151
|
`);
|
|
13592
14152
|
}
|
|
13593
14153
|
|
|
14154
|
+
// packages/cli/src/replay.ts
|
|
14155
|
+
function formatTokens14(tokens) {
|
|
14156
|
+
if (tokens >= 1e6) {
|
|
14157
|
+
return `${(tokens / 1e6).toFixed(1)}M`;
|
|
14158
|
+
}
|
|
14159
|
+
if (tokens >= 1000) {
|
|
14160
|
+
return `${(tokens / 1000).toFixed(1)}K`;
|
|
14161
|
+
}
|
|
14162
|
+
return `${Math.round(tokens)}`;
|
|
14163
|
+
}
|
|
14164
|
+
function formatCost14(cost) {
|
|
14165
|
+
return `$${cost.toFixed(2)}`;
|
|
14166
|
+
}
|
|
14167
|
+
function formatDuration4(ms) {
|
|
14168
|
+
if (ms <= 0) {
|
|
14169
|
+
return "0s";
|
|
14170
|
+
}
|
|
14171
|
+
const hours = Math.floor(ms / 3600000);
|
|
14172
|
+
const minutes = Math.floor(ms % 3600000 / 60000);
|
|
14173
|
+
const seconds = Math.floor(ms % 60000 / 1000);
|
|
14174
|
+
if (hours > 0) {
|
|
14175
|
+
return `${hours}h ${minutes}m`;
|
|
14176
|
+
}
|
|
14177
|
+
if (minutes > 0) {
|
|
14178
|
+
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
14179
|
+
}
|
|
14180
|
+
return `${seconds}s`;
|
|
14181
|
+
}
|
|
14182
|
+
function formatTime(iso) {
|
|
14183
|
+
const date = new Date(iso);
|
|
14184
|
+
return `${String(date.getUTCHours()).padStart(2, "0")}:${String(date.getUTCMinutes()).padStart(2, "0")}`;
|
|
14185
|
+
}
|
|
14186
|
+
function truncate2(value, width) {
|
|
14187
|
+
if (value.length <= width) {
|
|
14188
|
+
return value;
|
|
14189
|
+
}
|
|
14190
|
+
if (width <= 3) {
|
|
14191
|
+
return ".".repeat(Math.max(0, width));
|
|
14192
|
+
}
|
|
14193
|
+
return `${value.slice(0, width - 3)}...`;
|
|
14194
|
+
}
|
|
14195
|
+
var HEATMAP_SLOTS = 48;
|
|
14196
|
+
var HEATMAP_BLOCKS2 = [" ", "\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
14197
|
+
function renderActivityBar(report, width) {
|
|
14198
|
+
if (report.events.length === 0) {
|
|
14199
|
+
return ["Activity", " (no events)"];
|
|
14200
|
+
}
|
|
14201
|
+
const slotTokens = new Array(HEATMAP_SLOTS).fill(0);
|
|
14202
|
+
for (const event of report.events) {
|
|
14203
|
+
const date = new Date(event.timestamp);
|
|
14204
|
+
const hour = date.getUTCHours();
|
|
14205
|
+
const minute = date.getUTCMinutes();
|
|
14206
|
+
const slot = Math.min(hour * 2 + Math.floor(minute / 30), HEATMAP_SLOTS - 1);
|
|
14207
|
+
slotTokens[slot] += event.totalTokens;
|
|
14208
|
+
}
|
|
14209
|
+
const maxTokens = Math.max(...slotTokens);
|
|
14210
|
+
const barWidth = Math.min(HEATMAP_SLOTS, width - 4);
|
|
14211
|
+
const step = HEATMAP_SLOTS / barWidth;
|
|
14212
|
+
let bar = " ";
|
|
14213
|
+
for (let i = 0;i < barWidth; i++) {
|
|
14214
|
+
const slotIndex = Math.floor(i * step);
|
|
14215
|
+
const tokens = slotTokens[slotIndex];
|
|
14216
|
+
const level = maxTokens > 0 ? Math.round(tokens / maxTokens * (HEATMAP_BLOCKS2.length - 1)) : 0;
|
|
14217
|
+
bar += HEATMAP_BLOCKS2[level];
|
|
14218
|
+
}
|
|
14219
|
+
const firstTime = formatTime(report.events[0].timestamp);
|
|
14220
|
+
const lastTime = formatTime(report.events[report.events.length - 1].timestamp);
|
|
14221
|
+
const timeLabel = ` ${firstTime}${" ".repeat(Math.max(1, barWidth - firstTime.length - lastTime.length))}${lastTime}`;
|
|
14222
|
+
return ["Activity", bar, timeLabel];
|
|
14223
|
+
}
|
|
14224
|
+
function renderFlowBlockCard(block, width) {
|
|
14225
|
+
const timeRange = `${formatTime(block.start)}-${formatTime(block.end)}`;
|
|
14226
|
+
const header = ` [${timeRange}] ${block.label} | ${block.eventCount} events | ${formatTokens14(block.totalTokens)} tok | ${formatCost14(block.cost)}`;
|
|
14227
|
+
const lines = [truncate2(header, width)];
|
|
14228
|
+
const modelInfo = ` Model: ${block.dominantModel}${block.modelSwitches > 0 ? ` (${block.modelSwitches} switch${block.modelSwitches === 1 ? "" : "es"})` : ""}`;
|
|
14229
|
+
lines.push(truncate2(modelInfo, width));
|
|
14230
|
+
const trend = block.cacheHitRateTrend;
|
|
14231
|
+
if (trend.length > 0) {
|
|
14232
|
+
const firstRate = (trend[0] * 100).toFixed(0);
|
|
14233
|
+
const lastRate = (trend[trend.length - 1] * 100).toFixed(0);
|
|
14234
|
+
if (trend.length > 1 && firstRate !== lastRate) {
|
|
14235
|
+
lines.push(` Cache: ${firstRate}% -> ${lastRate}%`);
|
|
14236
|
+
} else {
|
|
14237
|
+
lines.push(` Cache: ${firstRate}%`);
|
|
14238
|
+
}
|
|
14239
|
+
}
|
|
14240
|
+
return lines;
|
|
14241
|
+
}
|
|
14242
|
+
function renderPulseChart(velocity, width) {
|
|
14243
|
+
if (velocity.length === 0) {
|
|
14244
|
+
return ["Pulse", " (no data)"];
|
|
14245
|
+
}
|
|
14246
|
+
const maxTpm = Math.max(...velocity.map((v) => v.tokensPerMinute));
|
|
14247
|
+
const chartWidth = Math.min(velocity.length, width - 10);
|
|
14248
|
+
const chartHeight = 5;
|
|
14249
|
+
const step = velocity.length / chartWidth;
|
|
14250
|
+
const grid = [];
|
|
14251
|
+
for (let row = 0;row < chartHeight; row++) {
|
|
14252
|
+
grid.push(new Array(chartWidth).fill(" "));
|
|
14253
|
+
}
|
|
14254
|
+
for (let col = 0;col < chartWidth; col++) {
|
|
14255
|
+
const idx = Math.floor(col * step);
|
|
14256
|
+
const tpm = velocity[idx].tokensPerMinute;
|
|
14257
|
+
const height = maxTpm > 0 ? Math.round(tpm / maxTpm * (chartHeight - 1)) : 0;
|
|
14258
|
+
for (let row = chartHeight - 1;row >= chartHeight - 1 - height; row--) {
|
|
14259
|
+
if (row >= 0) {
|
|
14260
|
+
grid[row][col] = "\u2588";
|
|
14261
|
+
}
|
|
14262
|
+
}
|
|
14263
|
+
}
|
|
14264
|
+
const lines = ["Pulse (tokens/min)"];
|
|
14265
|
+
const maxLabel = formatTokens14(maxTpm);
|
|
14266
|
+
for (let row = 0;row < chartHeight; row++) {
|
|
14267
|
+
const label = row === 0 ? maxLabel.padStart(7) : row === chartHeight - 1 ? " 0" : " ";
|
|
14268
|
+
lines.push(`${label} |${grid[row].join("")}`);
|
|
14269
|
+
}
|
|
14270
|
+
return lines;
|
|
14271
|
+
}
|
|
14272
|
+
function renderReplayTerminal(report, width = 80) {
|
|
14273
|
+
const lines = [
|
|
14274
|
+
`Session Replay: ${report.date}`,
|
|
14275
|
+
""
|
|
14276
|
+
];
|
|
14277
|
+
lines.push(...renderActivityBar(report, width));
|
|
14278
|
+
lines.push("");
|
|
14279
|
+
if (report.flowBlocks.length === 0) {
|
|
14280
|
+
lines.push("Flow Blocks", " (no activity)");
|
|
14281
|
+
} else {
|
|
14282
|
+
lines.push(`Flow Blocks (${report.flowBlocks.length})`);
|
|
14283
|
+
for (const block of report.flowBlocks) {
|
|
14284
|
+
lines.push(...renderFlowBlockCard(block, width));
|
|
14285
|
+
lines.push("");
|
|
14286
|
+
}
|
|
14287
|
+
}
|
|
14288
|
+
lines.push(...renderPulseChart(report.tokenVelocity, width));
|
|
14289
|
+
lines.push("");
|
|
14290
|
+
const s = report.summary;
|
|
14291
|
+
const summaryParts = [
|
|
14292
|
+
`Sessions: ${s.totalSessions}`,
|
|
14293
|
+
`Events: ${s.totalEvents}`,
|
|
14294
|
+
`Flow: ${formatDuration4(s.flowTimeMs)}`,
|
|
14295
|
+
`Think: ${formatDuration4(s.thinkTimeMs)}`,
|
|
14296
|
+
`Ratio: ${(s.flowThinkRatio * 100).toFixed(0)}%`
|
|
14297
|
+
];
|
|
14298
|
+
if (s.peakMinute) {
|
|
14299
|
+
summaryParts.push(`Peak: ${formatTokens14(s.peakMinute.tokensPerMinute)} tok/min at ${formatTime(s.peakMinute.minute)}`);
|
|
14300
|
+
}
|
|
14301
|
+
lines.push(truncate2(summaryParts.join(" | "), width));
|
|
14302
|
+
return lines.join(`
|
|
14303
|
+
`);
|
|
14304
|
+
}
|
|
14305
|
+
function buildReplayHelpText() {
|
|
14306
|
+
return [
|
|
14307
|
+
"Usage:",
|
|
14308
|
+
" tokenleak replay [date] [flags]",
|
|
14309
|
+
"",
|
|
14310
|
+
"Arguments:",
|
|
14311
|
+
" date Date to replay in YYYY-MM-DD format (defaults to today)",
|
|
14312
|
+
"",
|
|
14313
|
+
"Replay Flags:",
|
|
14314
|
+
" -f, --format <format> Output format: terminal, json",
|
|
14315
|
+
" -o, --output <path> Write output to a file and infer format from extension",
|
|
14316
|
+
" -w, --width <number> Terminal render width",
|
|
14317
|
+
" -p, --provider <list> Provider filter list, comma-separated",
|
|
14318
|
+
" --claude Only include Claude Code",
|
|
14319
|
+
" --codex Only include Codex",
|
|
14320
|
+
" --cursor Only include Cursor",
|
|
14321
|
+
" --pi Only include Pi",
|
|
14322
|
+
" --open-code Only include OpenCode",
|
|
14323
|
+
" --all-providers Ignore provider filters and use every available provider",
|
|
14324
|
+
" --no-color Accepted for parity with terminal output",
|
|
14325
|
+
" --help Show replay help",
|
|
14326
|
+
"",
|
|
14327
|
+
"Examples:",
|
|
14328
|
+
" tokenleak replay",
|
|
14329
|
+
" tokenleak replay 2026-03-10",
|
|
14330
|
+
" tokenleak replay 2026-03-10 --format json",
|
|
14331
|
+
" tokenleak replay --provider claude --output replay.json",
|
|
14332
|
+
""
|
|
14333
|
+
].join(`
|
|
14334
|
+
`);
|
|
14335
|
+
}
|
|
14336
|
+
|
|
13594
14337
|
// packages/cli/src/flags.ts
|
|
13595
14338
|
var CLI_FLAG_ORDER = [
|
|
13596
14339
|
"format",
|
|
@@ -15173,11 +15916,11 @@ async function uploadToGist(content, filename, description) {
|
|
|
15173
15916
|
if (!available) {
|
|
15174
15917
|
throw new Error("GitHub CLI (gh) is not installed or not authenticated. " + "Install it from https://cli.github.com and run `gh auth login`.");
|
|
15175
15918
|
}
|
|
15176
|
-
const { join:
|
|
15177
|
-
const { tmpdir } = await import("os");
|
|
15178
|
-
const { writeFileSync:
|
|
15179
|
-
const tmpPath =
|
|
15180
|
-
|
|
15919
|
+
const { join: join9 } = await import("path");
|
|
15920
|
+
const { tmpdir: tmpdir2 } = await import("os");
|
|
15921
|
+
const { writeFileSync: writeFileSync3, unlinkSync: unlinkSync2 } = await import("fs");
|
|
15922
|
+
const tmpPath = join9(tmpdir2(), `tokenleak-gist-${Date.now()}-${filename}`);
|
|
15923
|
+
writeFileSync3(tmpPath, content, "utf-8");
|
|
15181
15924
|
try {
|
|
15182
15925
|
const proc = Bun.spawn(["gh", "gist", "create", tmpPath, "--desc", description, "--public"], {
|
|
15183
15926
|
stdout: "pipe",
|
|
@@ -15594,11 +16337,13 @@ function buildHelpText() {
|
|
|
15594
16337
|
" tokenleak [flags]",
|
|
15595
16338
|
" tokenleak explain <date> [flags]",
|
|
15596
16339
|
" tokenleak focus [flags]",
|
|
16340
|
+
" tokenleak replay [date] [flags]",
|
|
15597
16341
|
" tokenleak cursor <command>",
|
|
15598
16342
|
"",
|
|
15599
16343
|
"Subcommands:",
|
|
15600
16344
|
" explain <date> Explain what drove usage on one day",
|
|
15601
16345
|
" focus Rank sessions by deep-work score",
|
|
16346
|
+
" replay [date] Replay a day's session timeline (defaults to today)",
|
|
15602
16347
|
" cursor Manage Cursor auth and cache sync",
|
|
15603
16348
|
"",
|
|
15604
16349
|
"Provider Shortcuts:",
|
|
@@ -15648,6 +16393,8 @@ function buildHelpText() {
|
|
|
15648
16393
|
" tokenleak explain 2026-03-10",
|
|
15649
16394
|
" tokenleak explain 2026-03-10 --format json",
|
|
15650
16395
|
" tokenleak focus --provider codex --days 30",
|
|
16396
|
+
" tokenleak replay",
|
|
16397
|
+
" tokenleak replay 2026-03-10 --format json",
|
|
15651
16398
|
"",
|
|
15652
16399
|
"Version:",
|
|
15653
16400
|
` CLI ${VERSION}`,
|
|
@@ -16361,7 +17108,7 @@ async function runFocus(cliArgs) {
|
|
|
16361
17108
|
if (events.length === 0) {
|
|
16362
17109
|
const emptyMsg = config.format === "json" ? JSON.stringify({ method: "No event data", entries: [] }, null, 2) : renderFocusReport({ method: "No event-level data found for focus analysis.", entries: [] }, config.width, config.noColor);
|
|
16363
17110
|
if (config.output) {
|
|
16364
|
-
|
|
17111
|
+
writeFileSync3(config.output, emptyMsg);
|
|
16365
17112
|
} else {
|
|
16366
17113
|
process.stdout.write(`${emptyMsg}
|
|
16367
17114
|
`);
|
|
@@ -16371,7 +17118,7 @@ async function runFocus(cliArgs) {
|
|
|
16371
17118
|
const report = buildFocusReport(events);
|
|
16372
17119
|
const rendered = config.format === "json" ? JSON.stringify(report, null, 2) : renderFocusReport(report, config.width, config.noColor);
|
|
16373
17120
|
if (config.output) {
|
|
16374
|
-
|
|
17121
|
+
writeFileSync3(config.output, rendered);
|
|
16375
17122
|
} else {
|
|
16376
17123
|
process.stdout.write(`${rendered}
|
|
16377
17124
|
`);
|
|
@@ -16420,7 +17167,7 @@ async function run(cliArgs) {
|
|
|
16420
17167
|
const rendered3 = await renderer2.render(compareResult.output, renderOptions2);
|
|
16421
17168
|
if (config.output) {
|
|
16422
17169
|
const data = typeof rendered3 === "string" ? rendered3 : Buffer.from(rendered3);
|
|
16423
|
-
|
|
17170
|
+
writeFileSync3(config.output, data);
|
|
16424
17171
|
} else {
|
|
16425
17172
|
const text2 = typeof rendered3 === "string" ? rendered3 : rendered3.toString("utf-8");
|
|
16426
17173
|
process.stdout.write(text2 + `
|
|
@@ -16441,7 +17188,7 @@ async function run(cliArgs) {
|
|
|
16441
17188
|
};
|
|
16442
17189
|
const rendered3 = await renderer2.render(compareResult.output, renderOptions2);
|
|
16443
17190
|
if (config.output) {
|
|
16444
|
-
|
|
17191
|
+
writeFileSync3(config.output, rendered3);
|
|
16445
17192
|
} else {
|
|
16446
17193
|
process.stdout.write(`${rendered3}
|
|
16447
17194
|
`);
|
|
@@ -16454,7 +17201,7 @@ async function run(cliArgs) {
|
|
|
16454
17201
|
}
|
|
16455
17202
|
const rendered2 = JSON.stringify(compareResult.compareOutput, null, 2);
|
|
16456
17203
|
if (config.output) {
|
|
16457
|
-
|
|
17204
|
+
writeFileSync3(config.output, rendered2);
|
|
16458
17205
|
} else {
|
|
16459
17206
|
process.stdout.write(rendered2 + `
|
|
16460
17207
|
`);
|
|
@@ -16497,7 +17244,7 @@ async function run(cliArgs) {
|
|
|
16497
17244
|
if (config.format === "json") {
|
|
16498
17245
|
const rendered3 = JSON.stringify(advisorReport, null, 2);
|
|
16499
17246
|
if (config.output) {
|
|
16500
|
-
|
|
17247
|
+
writeFileSync3(config.output, rendered3);
|
|
16501
17248
|
} else {
|
|
16502
17249
|
process.stdout.write(rendered3 + `
|
|
16503
17250
|
`);
|
|
@@ -16509,7 +17256,7 @@ async function run(cliArgs) {
|
|
|
16509
17256
|
noColor: config.noColor
|
|
16510
17257
|
});
|
|
16511
17258
|
if (config.output) {
|
|
16512
|
-
|
|
17259
|
+
writeFileSync3(config.output, rendered2);
|
|
16513
17260
|
} else {
|
|
16514
17261
|
process.stdout.write(rendered2 + `
|
|
16515
17262
|
`);
|
|
@@ -16519,7 +17266,7 @@ async function run(cliArgs) {
|
|
|
16519
17266
|
if (config.format === "wrapped") {
|
|
16520
17267
|
const outputPath = config.output ?? "tokenleak-wrapped.png";
|
|
16521
17268
|
const wrappedBuffer = await renderWrappedPng(output2, { theme: config.theme });
|
|
16522
|
-
|
|
17269
|
+
writeFileSync3(outputPath, wrappedBuffer);
|
|
16523
17270
|
process.stderr.write(`Wrapped PNG written to ${outputPath}
|
|
16524
17271
|
`);
|
|
16525
17272
|
if (config.clipboard) {
|
|
@@ -16628,7 +17375,7 @@ Shutting down wrapped live server...
|
|
|
16628
17375
|
const rendered = await renderer.render(output2, renderOptions);
|
|
16629
17376
|
if (config.output) {
|
|
16630
17377
|
const data = typeof rendered === "string" ? rendered : Buffer.from(rendered);
|
|
16631
|
-
|
|
17378
|
+
writeFileSync3(config.output, data);
|
|
16632
17379
|
} else {
|
|
16633
17380
|
const text2 = typeof rendered === "string" ? rendered : rendered.toString("utf-8");
|
|
16634
17381
|
process.stdout.write(text2 + `
|
|
@@ -16759,6 +17506,138 @@ function parseExplainArgs(argv) {
|
|
|
16759
17506
|
}
|
|
16760
17507
|
return { date, cliArgs };
|
|
16761
17508
|
}
|
|
17509
|
+
function parseReplayArgs(argv) {
|
|
17510
|
+
let date = null;
|
|
17511
|
+
if (argv.length > 0 && !argv[0].startsWith("-")) {
|
|
17512
|
+
date = argv[0];
|
|
17513
|
+
if (!isValidDateArgument(date)) {
|
|
17514
|
+
throw new TokenleakError("tokenleak replay date must be in YYYY-MM-DD format");
|
|
17515
|
+
}
|
|
17516
|
+
}
|
|
17517
|
+
if (date === null) {
|
|
17518
|
+
date = new Date().toISOString().slice(0, 10);
|
|
17519
|
+
}
|
|
17520
|
+
const cliArgs = {};
|
|
17521
|
+
let index = argv[0]?.startsWith("-") ? 0 : 1;
|
|
17522
|
+
while (index < argv.length) {
|
|
17523
|
+
const arg = argv[index];
|
|
17524
|
+
switch (arg) {
|
|
17525
|
+
case "--help":
|
|
17526
|
+
case "-h":
|
|
17527
|
+
cliArgs["help"] = true;
|
|
17528
|
+
index += 1;
|
|
17529
|
+
break;
|
|
17530
|
+
case "--version":
|
|
17531
|
+
case "-v":
|
|
17532
|
+
cliArgs["version"] = true;
|
|
17533
|
+
index += 1;
|
|
17534
|
+
break;
|
|
17535
|
+
case "--format":
|
|
17536
|
+
case "-f":
|
|
17537
|
+
if (argv[index + 1] === undefined) {
|
|
17538
|
+
throw new TokenleakError(`${arg} requires a value`);
|
|
17539
|
+
}
|
|
17540
|
+
cliArgs["format"] = argv[index + 1];
|
|
17541
|
+
index += 2;
|
|
17542
|
+
break;
|
|
17543
|
+
case "--output":
|
|
17544
|
+
case "-o":
|
|
17545
|
+
if (argv[index + 1] === undefined) {
|
|
17546
|
+
throw new TokenleakError(`${arg} requires a value`);
|
|
17547
|
+
}
|
|
17548
|
+
cliArgs["output"] = argv[index + 1];
|
|
17549
|
+
index += 2;
|
|
17550
|
+
break;
|
|
17551
|
+
case "--width":
|
|
17552
|
+
case "-w":
|
|
17553
|
+
if (argv[index + 1] === undefined) {
|
|
17554
|
+
throw new TokenleakError(`${arg} requires a value`);
|
|
17555
|
+
}
|
|
17556
|
+
cliArgs["width"] = Number(argv[index + 1]);
|
|
17557
|
+
index += 2;
|
|
17558
|
+
break;
|
|
17559
|
+
case "--provider":
|
|
17560
|
+
case "-p":
|
|
17561
|
+
if (argv[index + 1] === undefined) {
|
|
17562
|
+
throw new TokenleakError(`${arg} requires a value`);
|
|
17563
|
+
}
|
|
17564
|
+
cliArgs["provider"] = argv[index + 1];
|
|
17565
|
+
index += 2;
|
|
17566
|
+
break;
|
|
17567
|
+
case "--claude":
|
|
17568
|
+
cliArgs["claude"] = true;
|
|
17569
|
+
index += 1;
|
|
17570
|
+
break;
|
|
17571
|
+
case "--codex":
|
|
17572
|
+
cliArgs["codex"] = true;
|
|
17573
|
+
index += 1;
|
|
17574
|
+
break;
|
|
17575
|
+
case "--cursor":
|
|
17576
|
+
cliArgs["cursor"] = true;
|
|
17577
|
+
index += 1;
|
|
17578
|
+
break;
|
|
17579
|
+
case "--pi":
|
|
17580
|
+
cliArgs["pi"] = true;
|
|
17581
|
+
index += 1;
|
|
17582
|
+
break;
|
|
17583
|
+
case "--openCode":
|
|
17584
|
+
case "--open-code":
|
|
17585
|
+
cliArgs["openCode"] = true;
|
|
17586
|
+
index += 1;
|
|
17587
|
+
break;
|
|
17588
|
+
case "--allProviders":
|
|
17589
|
+
case "--all-providers":
|
|
17590
|
+
cliArgs["allProviders"] = true;
|
|
17591
|
+
index += 1;
|
|
17592
|
+
break;
|
|
17593
|
+
case "--noColor":
|
|
17594
|
+
case "--no-color":
|
|
17595
|
+
cliArgs["noColor"] = true;
|
|
17596
|
+
index += 1;
|
|
17597
|
+
break;
|
|
17598
|
+
default:
|
|
17599
|
+
throw new TokenleakError(`Unknown replay flag "${arg}"`);
|
|
17600
|
+
}
|
|
17601
|
+
}
|
|
17602
|
+
return { date, cliArgs };
|
|
17603
|
+
}
|
|
17604
|
+
function resolveReplayFormat(cliArgs) {
|
|
17605
|
+
if (typeof cliArgs["format"] === "string") {
|
|
17606
|
+
const format = cliArgs["format"];
|
|
17607
|
+
if (format === "json" || format === "terminal") {
|
|
17608
|
+
return format;
|
|
17609
|
+
}
|
|
17610
|
+
throw new TokenleakError("tokenleak replay only supports --format terminal or --format json");
|
|
17611
|
+
}
|
|
17612
|
+
if (typeof cliArgs["output"] === "string") {
|
|
17613
|
+
const inferred = inferFormatFromPath(cliArgs["output"]);
|
|
17614
|
+
if (inferred === "json") {
|
|
17615
|
+
return "json";
|
|
17616
|
+
}
|
|
17617
|
+
}
|
|
17618
|
+
return "terminal";
|
|
17619
|
+
}
|
|
17620
|
+
async function runReplay(date, cliArgs) {
|
|
17621
|
+
const config = resolveConfig(cliArgs);
|
|
17622
|
+
const format = resolveReplayFormat(cliArgs);
|
|
17623
|
+
if (config.allProviders && (config.provider || config.claude || config.codex || config.cursor || config.pi || config.openCode)) {
|
|
17624
|
+
throw new TokenleakError("--all-providers cannot be combined with provider filters");
|
|
17625
|
+
}
|
|
17626
|
+
const replayRange = computeDateRange({ since: date, until: date });
|
|
17627
|
+
const available = await selectAvailableProviders(config);
|
|
17628
|
+
if (available.length === 0) {
|
|
17629
|
+
throw new TokenleakError("No provider data found");
|
|
17630
|
+
}
|
|
17631
|
+
const replayOutput = await loadTokenleakData(available, replayRange);
|
|
17632
|
+
const report = buildReplayReport(replayOutput.providers, date);
|
|
17633
|
+
const rendered = format === "json" ? JSON.stringify(report, null, 2) : renderReplayTerminal(report, config.width);
|
|
17634
|
+
if (config.output) {
|
|
17635
|
+
writeFileSync3(config.output, rendered);
|
|
17636
|
+
} else {
|
|
17637
|
+
process.stdout.write(rendered + `
|
|
17638
|
+
`);
|
|
17639
|
+
}
|
|
17640
|
+
}
|
|
16762
17641
|
function resolveExplainFormat(cliArgs) {
|
|
16763
17642
|
if (typeof cliArgs["format"] === "string") {
|
|
16764
17643
|
const format = cliArgs["format"];
|
|
@@ -16790,7 +17669,7 @@ async function runExplain(date, cliArgs) {
|
|
|
16790
17669
|
const report = buildExplainReport(explainOutput.providers, date);
|
|
16791
17670
|
const rendered = format === "json" ? JSON.stringify(report, null, 2) : renderExplainTerminal(report, config.width);
|
|
16792
17671
|
if (config.output) {
|
|
16793
|
-
|
|
17672
|
+
writeFileSync3(config.output, rendered);
|
|
16794
17673
|
} else {
|
|
16795
17674
|
process.stdout.write(rendered + `
|
|
16796
17675
|
`);
|
|
@@ -17116,6 +17995,7 @@ var focusMain = defineCommand({
|
|
|
17116
17995
|
});
|
|
17117
17996
|
var isDirectExecution = typeof Bun !== "undefined" ? Bun.main === import.meta.path : process.argv[1] !== undefined && import.meta.url.endsWith(process.argv[1].replace(/\\/g, "/"));
|
|
17118
17997
|
if (isDirectExecution) {
|
|
17998
|
+
await initPricing();
|
|
17119
17999
|
const normalizedArgv = normalizeCliArgv(process.argv.slice(2));
|
|
17120
18000
|
const argv = normalizedArgv;
|
|
17121
18001
|
if (argv[0] === "explain") {
|
|
@@ -17135,6 +18015,23 @@ if (isDirectExecution) {
|
|
|
17135
18015
|
handleError(error);
|
|
17136
18016
|
}
|
|
17137
18017
|
}
|
|
18018
|
+
if (argv[0] === "replay") {
|
|
18019
|
+
try {
|
|
18020
|
+
const { date, cliArgs } = parseReplayArgs(argv.slice(1));
|
|
18021
|
+
if (cliArgs["help"]) {
|
|
18022
|
+
process.stdout.write(buildReplayHelpText());
|
|
18023
|
+
process.exit(0);
|
|
18024
|
+
}
|
|
18025
|
+
if (cliArgs["version"]) {
|
|
18026
|
+
process.stdout.write(buildVersionText());
|
|
18027
|
+
process.exit(0);
|
|
18028
|
+
}
|
|
18029
|
+
await runReplay(date, cliArgs);
|
|
18030
|
+
process.exit(0);
|
|
18031
|
+
} catch (error) {
|
|
18032
|
+
handleError(error);
|
|
18033
|
+
}
|
|
18034
|
+
}
|
|
17138
18035
|
if (argv[0] === "focus") {
|
|
17139
18036
|
const focusArgv = argv.slice(1);
|
|
17140
18037
|
process.argv = [...process.argv.slice(0, 2), ...focusArgv];
|