whoburnedmore 0.9.16 → 0.9.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +171 -113
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,7 +7,6 @@ var __export = (target, all) => {
|
|
|
7
7
|
|
|
8
8
|
// src/index.ts
|
|
9
9
|
import { spawn } from "node:child_process";
|
|
10
|
-
import { createHash } from "node:crypto";
|
|
11
10
|
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
12
11
|
import { createRequire as createRequire4 } from "node:module";
|
|
13
12
|
import { platform as platform4 } from "node:os";
|
|
@@ -418,9 +417,19 @@ ${envEntries}
|
|
|
418
417
|
interval. Submits are idempotent server-side, so an extra run is safe. -->
|
|
419
418
|
<key>RunAtLoad</key>
|
|
420
419
|
<true/>
|
|
421
|
-
<!--
|
|
420
|
+
<!-- Standard, NOT Background. ProcessType=Background opts the job into
|
|
421
|
+
macOS I/O throttling, which is disastrous for a job whose work is almost
|
|
422
|
+
entirely reading thousands of transcript files: a full collect measured
|
|
423
|
+
~51s unthrottled vs ~197s throttled on the same machine. That blows past
|
|
424
|
+
the collector's own budgets (25s per ccusage child, 45s for the native
|
|
425
|
+
reader), so every source times out at once, entries comes back empty and
|
|
426
|
+
the tick submits nothing \u2014 it logged "Nothing to burn yet" on 206 of 473
|
|
427
|
+
runs (~44%) until this was changed. Nice=0 keeps CPU priority neighbourly
|
|
428
|
+
without throttling the reads. -->
|
|
422
429
|
<key>ProcessType</key>
|
|
423
|
-
<string>
|
|
430
|
+
<string>Standard</string>
|
|
431
|
+
<key>Nice</key>
|
|
432
|
+
<integer>0</integer>
|
|
424
433
|
<key>StandardOutPath</key>
|
|
425
434
|
<string>${xmlEscape(logPath)}</string>
|
|
426
435
|
<key>StandardErrorPath</key>
|
|
@@ -6618,6 +6627,31 @@ async function readFilesWithCache(opts) {
|
|
|
6618
6627
|
return { itemsByFile, filesRead, timedOut: false };
|
|
6619
6628
|
}
|
|
6620
6629
|
|
|
6630
|
+
// src/native/usage-date.ts
|
|
6631
|
+
var MIN_USAGE_TIMESTAMP_MS = Date.UTC(2020, 0, 1);
|
|
6632
|
+
var MAX_CLOCK_SKEW_MS = 2 * 864e5;
|
|
6633
|
+
function localUsageDate(ms, now = Date.now()) {
|
|
6634
|
+
if (!Number.isFinite(ms) || ms < MIN_USAGE_TIMESTAMP_MS || ms > now + MAX_CLOCK_SKEW_MS) {
|
|
6635
|
+
return null;
|
|
6636
|
+
}
|
|
6637
|
+
const d = new Date(ms);
|
|
6638
|
+
if (!Number.isFinite(d.getTime())) return null;
|
|
6639
|
+
const y = d.getFullYear();
|
|
6640
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
6641
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
6642
|
+
return `${y}-${m}-${day}`;
|
|
6643
|
+
}
|
|
6644
|
+
function plausibleUsageDate(value, now = Date.now()) {
|
|
6645
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
6646
|
+
if (!match) return false;
|
|
6647
|
+
const year = Number(match[1]);
|
|
6648
|
+
const month = Number(match[2]);
|
|
6649
|
+
const day = Number(match[3]);
|
|
6650
|
+
const ms = Date.UTC(year, month - 1, day);
|
|
6651
|
+
const parsed = new Date(ms);
|
|
6652
|
+
return parsed.getUTCFullYear() === year && parsed.getUTCMonth() === month - 1 && parsed.getUTCDate() === day && ms >= MIN_USAGE_TIMESTAMP_MS && ms <= now + MAX_CLOCK_SKEW_MS;
|
|
6653
|
+
}
|
|
6654
|
+
|
|
6621
6655
|
// src/native/claude.ts
|
|
6622
6656
|
function num(n) {
|
|
6623
6657
|
const v = Math.round(Number(n));
|
|
@@ -6626,15 +6660,6 @@ function num(n) {
|
|
|
6626
6660
|
function reqTokens(r) {
|
|
6627
6661
|
return r.inputTokens + r.outputTokens + r.cacheCreationTokens + r.cacheReadTokens;
|
|
6628
6662
|
}
|
|
6629
|
-
function localDate(iso) {
|
|
6630
|
-
const t = Date.parse(iso);
|
|
6631
|
-
if (!Number.isFinite(t)) return null;
|
|
6632
|
-
const d = new Date(t);
|
|
6633
|
-
const y = d.getFullYear();
|
|
6634
|
-
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
6635
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
6636
|
-
return `${y}-${m}-${day}`;
|
|
6637
|
-
}
|
|
6638
6663
|
var syntheticCounter = 0;
|
|
6639
6664
|
function parseClaudeLine(raw) {
|
|
6640
6665
|
const trimmed = raw.trim();
|
|
@@ -6650,10 +6675,10 @@ function parseClaudeLine(raw) {
|
|
|
6650
6675
|
const usage = message.usage;
|
|
6651
6676
|
if (!usage || typeof usage !== "object") return null;
|
|
6652
6677
|
if (message.role !== void 0 && message.role !== "assistant") return null;
|
|
6653
|
-
const date = localDate(String(obj.timestamp ?? ""));
|
|
6654
|
-
if (!date) return null;
|
|
6655
6678
|
const tsParsed = Date.parse(String(obj.timestamp ?? ""));
|
|
6656
|
-
const
|
|
6679
|
+
const date = localUsageDate(tsParsed);
|
|
6680
|
+
if (!date) return null;
|
|
6681
|
+
const ts = tsParsed;
|
|
6657
6682
|
const messageId = typeof message.id === "string" ? message.id : "";
|
|
6658
6683
|
const requestId = typeof obj.requestId === "string" ? obj.requestId : "";
|
|
6659
6684
|
const hasRealId = messageId !== "" || requestId !== "";
|
|
@@ -6747,7 +6772,7 @@ async function listJsonl(dir) {
|
|
|
6747
6772
|
return out;
|
|
6748
6773
|
}
|
|
6749
6774
|
var NATIVE_READ_BUDGET_MS = 45e3;
|
|
6750
|
-
var CLAUDE_CACHE_VERSION =
|
|
6775
|
+
var CLAUDE_CACHE_VERSION = 2;
|
|
6751
6776
|
function toCached(r) {
|
|
6752
6777
|
return [
|
|
6753
6778
|
r.key,
|
|
@@ -7190,13 +7215,6 @@ function toEpochMs(ts) {
|
|
|
7190
7215
|
}
|
|
7191
7216
|
return null;
|
|
7192
7217
|
}
|
|
7193
|
-
function localDate2(ms) {
|
|
7194
|
-
const d = new Date(ms);
|
|
7195
|
-
const y = d.getFullYear();
|
|
7196
|
-
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
7197
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
7198
|
-
return `${y}-${m}-${day}`;
|
|
7199
|
-
}
|
|
7200
7218
|
function mapContinueRecords(records) {
|
|
7201
7219
|
const byKey = /* @__PURE__ */ new Map();
|
|
7202
7220
|
for (const raw of records) {
|
|
@@ -7208,10 +7226,11 @@ function mapContinueRecords(records) {
|
|
|
7208
7226
|
if (!model) continue;
|
|
7209
7227
|
const ms = toEpochMs(rec.timestamp ?? rec.eventTimestamp);
|
|
7210
7228
|
if (ms === null) continue;
|
|
7229
|
+
const date = localUsageDate(ms);
|
|
7230
|
+
if (!date) continue;
|
|
7211
7231
|
const inputTokens = num2(rec.promptTokens ?? rec.prompt_tokens);
|
|
7212
7232
|
const outputTokens = num2(rec.generatedTokens ?? rec.generated_tokens);
|
|
7213
7233
|
if (inputTokens + outputTokens === 0) continue;
|
|
7214
|
-
const date = localDate2(ms);
|
|
7215
7234
|
const key = `${date}|${model}`;
|
|
7216
7235
|
let b = byKey.get(key);
|
|
7217
7236
|
if (!b) {
|
|
@@ -7269,11 +7288,11 @@ async function listJsonl2(dir) {
|
|
|
7269
7288
|
for (const d of dirents) {
|
|
7270
7289
|
const full = join6(dir, d.name);
|
|
7271
7290
|
if (d.isDirectory()) out.push(...await listJsonl2(full));
|
|
7272
|
-
else if (d.isFile() && d.name
|
|
7291
|
+
else if (d.isFile() && d.name === "tokensGenerated.jsonl") out.push(full);
|
|
7273
7292
|
}
|
|
7274
7293
|
return out;
|
|
7275
7294
|
}
|
|
7276
|
-
var CONTINUE_CACHE_VERSION =
|
|
7295
|
+
var CONTINUE_CACHE_VERSION = 2;
|
|
7277
7296
|
async function collectContinue(opts = {}) {
|
|
7278
7297
|
const env = opts.env ?? process.env;
|
|
7279
7298
|
const home = opts.continueDir ?? join6(env.HOME || homedir5(), ".continue");
|
|
@@ -7493,7 +7512,8 @@ function mapCursorEvents(events) {
|
|
|
7493
7512
|
const ms = Number(e.timestamp);
|
|
7494
7513
|
if (!tu || !Number.isFinite(ms)) continue;
|
|
7495
7514
|
const d = new Date(ms);
|
|
7496
|
-
const date =
|
|
7515
|
+
const date = localUsageDate(ms);
|
|
7516
|
+
if (!date) continue;
|
|
7497
7517
|
const model = e.model || "cursor";
|
|
7498
7518
|
const input = num4(tu.inputTokens);
|
|
7499
7519
|
const output = num4(tu.outputTokens);
|
|
@@ -7590,15 +7610,6 @@ function num5(n) {
|
|
|
7590
7610
|
const v = Math.round(Number(n));
|
|
7591
7611
|
return Number.isFinite(v) && v > 0 ? v : 0;
|
|
7592
7612
|
}
|
|
7593
|
-
function localDate3(iso) {
|
|
7594
|
-
const t = Date.parse(iso);
|
|
7595
|
-
if (!Number.isFinite(t)) return null;
|
|
7596
|
-
const d = new Date(t);
|
|
7597
|
-
const y = d.getFullYear();
|
|
7598
|
-
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
7599
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
7600
|
-
return `${y}-${m}-${day}`;
|
|
7601
|
-
}
|
|
7602
7613
|
function readTokenFields(payload) {
|
|
7603
7614
|
const info = payload.info;
|
|
7604
7615
|
const src = info?.total_token_usage ?? payload;
|
|
@@ -7608,8 +7619,7 @@ function readTokenFields(payload) {
|
|
|
7608
7619
|
return {
|
|
7609
7620
|
input: num5(input),
|
|
7610
7621
|
cached: num5(src.cached_input_tokens),
|
|
7611
|
-
output: num5(output)
|
|
7612
|
-
reasoning: num5(src.reasoning_output_tokens)
|
|
7622
|
+
output: num5(output)
|
|
7613
7623
|
};
|
|
7614
7624
|
}
|
|
7615
7625
|
function parseCodexRollout(lines) {
|
|
@@ -7634,7 +7644,7 @@ function parseCodexRollout(lines) {
|
|
|
7634
7644
|
if (payload.type === "token_count") {
|
|
7635
7645
|
const fields = readTokenFields(payload);
|
|
7636
7646
|
if (!fields) continue;
|
|
7637
|
-
const parsed =
|
|
7647
|
+
const parsed = localUsageDate(Date.parse(String(obj.timestamp ?? "")));
|
|
7638
7648
|
const day = parsed ?? lastSeenDate;
|
|
7639
7649
|
if (!day) continue;
|
|
7640
7650
|
lastSeenDate = day;
|
|
@@ -7650,17 +7660,16 @@ function parseCodexRollout(lines) {
|
|
|
7650
7660
|
if (perDay.size === 0) return [];
|
|
7651
7661
|
const dates = [...perDay.keys()].sort();
|
|
7652
7662
|
const out = [];
|
|
7653
|
-
let prev = { input: 0, cached: 0, output: 0
|
|
7663
|
+
let prev = { input: 0, cached: 0, output: 0 };
|
|
7654
7664
|
for (const date of dates) {
|
|
7655
7665
|
const { cum, turns } = perDay.get(date);
|
|
7656
7666
|
const dInput = Math.max(0, cum.input - prev.input);
|
|
7657
7667
|
const dCached = Math.max(0, cum.cached - prev.cached);
|
|
7658
7668
|
const dOutput = Math.max(0, cum.output - prev.output);
|
|
7659
|
-
const dReasoning = Math.max(0, cum.reasoning - prev.reasoning);
|
|
7660
7669
|
prev = cum;
|
|
7661
7670
|
const cacheReadTokens = dCached;
|
|
7662
7671
|
const inputTokens = Math.max(0, dInput - dCached);
|
|
7663
|
-
const outputTokens = dOutput
|
|
7672
|
+
const outputTokens = dOutput;
|
|
7664
7673
|
if (inputTokens + outputTokens + cacheReadTokens === 0) continue;
|
|
7665
7674
|
out.push({
|
|
7666
7675
|
date,
|
|
@@ -7716,9 +7725,12 @@ function finalizeCodexEntries(acc) {
|
|
|
7716
7725
|
}
|
|
7717
7726
|
return entries;
|
|
7718
7727
|
}
|
|
7719
|
-
function
|
|
7720
|
-
|
|
7721
|
-
|
|
7728
|
+
function resolveCodexHome(env = process.env) {
|
|
7729
|
+
return env.CODEX_HOME && env.CODEX_HOME.trim() ? env.CODEX_HOME.trim() : join9(homedir7(), ".codex");
|
|
7730
|
+
}
|
|
7731
|
+
function resolveCodexSessionsDirs(env = process.env) {
|
|
7732
|
+
const home = resolveCodexHome(env);
|
|
7733
|
+
return [join9(home, "sessions"), join9(home, "archived_sessions")];
|
|
7722
7734
|
}
|
|
7723
7735
|
async function listJsonl3(dir) {
|
|
7724
7736
|
let dirents;
|
|
@@ -7746,7 +7758,7 @@ function* splitLines3(content) {
|
|
|
7746
7758
|
if (start < content.length) yield content.slice(start);
|
|
7747
7759
|
}
|
|
7748
7760
|
var NATIVE_READ_BUDGET_MS2 = 45e3;
|
|
7749
|
-
var CODEX_CACHE_VERSION =
|
|
7761
|
+
var CODEX_CACHE_VERSION = 2;
|
|
7750
7762
|
function toCachedSession(s) {
|
|
7751
7763
|
return [
|
|
7752
7764
|
s.date,
|
|
@@ -7770,8 +7782,8 @@ function fromCachedSession(t) {
|
|
|
7770
7782
|
};
|
|
7771
7783
|
}
|
|
7772
7784
|
async function collectCodexNative(env = process.env, opts = {}) {
|
|
7773
|
-
const
|
|
7774
|
-
const files = await listJsonl3(
|
|
7785
|
+
const dirs = resolveCodexSessionsDirs(env);
|
|
7786
|
+
const files = (await Promise.all(dirs.map(listJsonl3))).flat();
|
|
7775
7787
|
if (files.length === 0) return { entries: [], found: false, filesScanned: 0 };
|
|
7776
7788
|
const now = opts.now ?? Date.now;
|
|
7777
7789
|
const res = await readFilesWithCache({
|
|
@@ -7813,17 +7825,9 @@ function num6(n) {
|
|
|
7813
7825
|
const v = Math.round(Number(n));
|
|
7814
7826
|
return Number.isFinite(v) && v > 0 ? v : 0;
|
|
7815
7827
|
}
|
|
7816
|
-
function localDate4(ms) {
|
|
7817
|
-
if (!Number.isFinite(ms) || ms <= 0) return null;
|
|
7818
|
-
const d = new Date(ms);
|
|
7819
|
-
const y = d.getFullYear();
|
|
7820
|
-
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
7821
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
7822
|
-
return `${y}-${m}-${day}`;
|
|
7823
|
-
}
|
|
7824
7828
|
function parseApiReqMessage(tool, msg) {
|
|
7825
7829
|
if (msg.say !== "api_req_started") return null;
|
|
7826
|
-
const date =
|
|
7830
|
+
const date = localUsageDate(Number(msg.ts));
|
|
7827
7831
|
if (!date) return null;
|
|
7828
7832
|
let payload = null;
|
|
7829
7833
|
const text = msg.text;
|
|
@@ -7947,7 +7951,7 @@ async function listTaskFiles(roots, extIds) {
|
|
|
7947
7951
|
}
|
|
7948
7952
|
return files;
|
|
7949
7953
|
}
|
|
7950
|
-
var VSCODE_CACHE_VERSION =
|
|
7954
|
+
var VSCODE_CACHE_VERSION = 2;
|
|
7951
7955
|
function entryToRow(e) {
|
|
7952
7956
|
return [
|
|
7953
7957
|
e.date,
|
|
@@ -8156,6 +8160,10 @@ function reconcileProvenance(entries, agent, complete, store) {
|
|
|
8156
8160
|
|
|
8157
8161
|
// src/collect.ts
|
|
8158
8162
|
var execFileAsync = promisify(execFile);
|
|
8163
|
+
var NATIVE_COVERED_SOURCES = /* @__PURE__ */ new Set(["claude", "codex"]);
|
|
8164
|
+
var CCUSAGE_TIMEOUT_MS = 25e3;
|
|
8165
|
+
var CCUSAGE_FALLBACK_TIMEOUT_MS = NATIVE_READ_BUDGET_MS;
|
|
8166
|
+
var CCUSAGE_AGGREGATE_TIMEOUT_MS = 18e4;
|
|
8159
8167
|
var SOURCES = [
|
|
8160
8168
|
"claude",
|
|
8161
8169
|
"codex",
|
|
@@ -8181,14 +8189,14 @@ function normCost(n) {
|
|
|
8181
8189
|
const v = Number(n);
|
|
8182
8190
|
return Number.isFinite(v) && v > 0 ? v : 0;
|
|
8183
8191
|
}
|
|
8184
|
-
function mapCcusageDaily(tool, json) {
|
|
8192
|
+
function mapCcusageDaily(tool, json, now = Date.now()) {
|
|
8185
8193
|
const daily = json?.daily;
|
|
8186
8194
|
if (!Array.isArray(daily)) return [];
|
|
8187
8195
|
const entries = [];
|
|
8188
8196
|
for (const rawDay of daily) {
|
|
8189
8197
|
const day = rawDay;
|
|
8190
8198
|
const date = day.date ?? day.period;
|
|
8191
|
-
if (typeof date !== "string" ||
|
|
8199
|
+
if (typeof date !== "string" || !plausibleUsageDate(date, now)) continue;
|
|
8192
8200
|
const breakdowns = Array.isArray(day.modelBreakdowns) ? day.modelBreakdowns : [];
|
|
8193
8201
|
const modelsMap = day.models && typeof day.models === "object" && !Array.isArray(day.models) ? day.models : null;
|
|
8194
8202
|
const dayCost = normCost(day.totalCost ?? day.costUSD);
|
|
@@ -8351,25 +8359,39 @@ function dedupeBlocks(blocks) {
|
|
|
8351
8359
|
return [...byStart.values()];
|
|
8352
8360
|
}
|
|
8353
8361
|
function selectSourceEntries(source, ccusageEntries, native) {
|
|
8354
|
-
if (source === "claude" && native.claude
|
|
8362
|
+
if (source === "claude" && nativeReaderWon(native.claude))
|
|
8355
8363
|
return native.claude.entries;
|
|
8356
|
-
if (source === "codex" && native.codex
|
|
8364
|
+
if (source === "codex" && nativeReaderWon(native.codex))
|
|
8357
8365
|
return native.codex.entries;
|
|
8358
8366
|
return ccusageEntries;
|
|
8359
8367
|
}
|
|
8368
|
+
function nativeReaderWon(result) {
|
|
8369
|
+
return result.found && result.entries.length > 0;
|
|
8370
|
+
}
|
|
8371
|
+
function ccusageFallbackSources(native) {
|
|
8372
|
+
return SOURCES.filter(
|
|
8373
|
+
(s) => NATIVE_COVERED_SOURCES.has(s) && !nativeReaderWon(s === "claude" ? native.claude : native.codex)
|
|
8374
|
+
);
|
|
8375
|
+
}
|
|
8360
8376
|
function ccusageClaudeEnv(env = process.env) {
|
|
8361
8377
|
if (env.CLAUDE_CONFIG_DIR && env.CLAUDE_CONFIG_DIR.trim()) return env;
|
|
8362
8378
|
return { ...env, CLAUDE_CONFIG_DIR: resolveClaudeConfigRoots(env).join(",") };
|
|
8363
8379
|
}
|
|
8364
|
-
|
|
8380
|
+
function isRetryableCcusageFailure(err) {
|
|
8381
|
+
const e = err;
|
|
8382
|
+
if (e.killed === true) return false;
|
|
8383
|
+
return e.signal != null || typeof e.code === "string";
|
|
8384
|
+
}
|
|
8385
|
+
async function runCcusageOnce(cmd, args, env, timeoutMs = CCUSAGE_TIMEOUT_MS) {
|
|
8365
8386
|
try {
|
|
8366
8387
|
const { stdout } = await execFileAsync(cmd, args, {
|
|
8367
8388
|
encoding: "utf8",
|
|
8368
8389
|
maxBuffer: 64 * 1024 * 1024,
|
|
8369
|
-
// A single source shouldn't be able to hang the whole run
|
|
8370
|
-
//
|
|
8371
|
-
//
|
|
8372
|
-
|
|
8390
|
+
// A single source shouldn't be able to hang the whole run: a hung source
|
|
8391
|
+
// gets killed and (if transient) retried once below rather than stalling
|
|
8392
|
+
// everything for minutes. The claude/codex fallback passes a longer cap —
|
|
8393
|
+
// see CCUSAGE_FALLBACK_TIMEOUT_MS.
|
|
8394
|
+
timeout: timeoutMs,
|
|
8373
8395
|
...env ? { env } : {}
|
|
8374
8396
|
});
|
|
8375
8397
|
if (!stdout) return { json: null, transient: false };
|
|
@@ -8379,19 +8401,17 @@ async function runCcusageOnce(cmd, args, env) {
|
|
|
8379
8401
|
return { json: null, transient: false };
|
|
8380
8402
|
}
|
|
8381
8403
|
} catch (err) {
|
|
8382
|
-
|
|
8383
|
-
const transient = e.killed === true || e.signal != null || typeof e.code === "string";
|
|
8384
|
-
return { json: null, transient };
|
|
8404
|
+
return { json: null, transient: isRetryableCcusageFailure(err) };
|
|
8385
8405
|
}
|
|
8386
8406
|
}
|
|
8387
|
-
async function runCcusage(cmd, args, env) {
|
|
8388
|
-
const first = await runCcusageOnce(cmd, args, env);
|
|
8407
|
+
async function runCcusage(cmd, args, env, timeoutMs = CCUSAGE_TIMEOUT_MS) {
|
|
8408
|
+
const first = await runCcusageOnce(cmd, args, env, timeoutMs);
|
|
8389
8409
|
if (first.json !== null || !first.transient) return first.json;
|
|
8390
|
-
return (await runCcusageOnce(cmd, args, env)).json;
|
|
8410
|
+
return (await runCcusageOnce(cmd, args, env, timeoutMs)).json;
|
|
8391
8411
|
}
|
|
8392
8412
|
var COLLECT_STAGES = SOURCES.length + 4 + VSCODE_AGENTS.length + 1;
|
|
8393
|
-
function isAuthoritativeScan(attributionComplete,
|
|
8394
|
-
return attributionComplete &&
|
|
8413
|
+
function isAuthoritativeScan(attributionComplete, ...fingerprintReaders) {
|
|
8414
|
+
return attributionComplete && fingerprintReaders.every((reader) => reader.timedOut !== true);
|
|
8395
8415
|
}
|
|
8396
8416
|
async function collectAll(onProgress) {
|
|
8397
8417
|
await loadLivePricing().catch(() => {
|
|
@@ -8406,27 +8426,39 @@ async function collectAll(onProgress) {
|
|
|
8406
8426
|
() => ({ entries: [], found: false, filesScanned: 0, timedOut: true })
|
|
8407
8427
|
);
|
|
8408
8428
|
const sourceTasks = SOURCES.map(async (source) => {
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8429
|
+
if (NATIVE_COVERED_SOURCES.has(source)) {
|
|
8430
|
+
await (source === "claude" ? nativeClaudeTask : nativeCodexTask);
|
|
8431
|
+
tick();
|
|
8432
|
+
return { source, mapped: [] };
|
|
8433
|
+
}
|
|
8434
|
+
const json = await runCcusage(cmd, [
|
|
8435
|
+
...prefixArgs,
|
|
8436
|
+
source,
|
|
8437
|
+
"daily",
|
|
8438
|
+
"--json",
|
|
8439
|
+
"--offline"
|
|
8440
|
+
]);
|
|
8415
8441
|
tick();
|
|
8416
8442
|
return { source, mapped: json ? mapCcusageDaily(source, json) : [] };
|
|
8417
8443
|
});
|
|
8418
|
-
const sessionTask = runCcusage(
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
)
|
|
8424
|
-
|
|
8425
|
-
(json)
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8444
|
+
const sessionTask = runCcusage(
|
|
8445
|
+
cmd,
|
|
8446
|
+
[...prefixArgs, "session", "--json", "--offline"],
|
|
8447
|
+
void 0,
|
|
8448
|
+
CCUSAGE_AGGREGATE_TIMEOUT_MS
|
|
8449
|
+
).then((json) => {
|
|
8450
|
+
tick();
|
|
8451
|
+
return json ? mapCcusageSessions(json) : [];
|
|
8452
|
+
});
|
|
8453
|
+
const blockTask = runCcusage(
|
|
8454
|
+
cmd,
|
|
8455
|
+
[...prefixArgs, "blocks", "--json", "--offline"],
|
|
8456
|
+
void 0,
|
|
8457
|
+
CCUSAGE_AGGREGATE_TIMEOUT_MS
|
|
8458
|
+
).then((json) => {
|
|
8459
|
+
tick();
|
|
8460
|
+
return json ? mapCcusageBlocks(json) : [];
|
|
8461
|
+
});
|
|
8430
8462
|
const cursorTask = collectCursor().then((c) => {
|
|
8431
8463
|
tick();
|
|
8432
8464
|
return c;
|
|
@@ -8467,10 +8499,21 @@ async function collectAll(onProgress) {
|
|
|
8467
8499
|
continueTask
|
|
8468
8500
|
]);
|
|
8469
8501
|
const native = { claude: nativeClaude, codex: nativeCodex };
|
|
8502
|
+
const fallbacks = /* @__PURE__ */ new Map();
|
|
8503
|
+
for (const source of ccusageFallbackSources(native)) {
|
|
8504
|
+
const json = await runCcusage(
|
|
8505
|
+
cmd,
|
|
8506
|
+
[...prefixArgs, source, "daily", "--json", "--offline"],
|
|
8507
|
+
// For Claude, force ccusage to scan both config roots (dual-dir hardening).
|
|
8508
|
+
source === "claude" ? ccusageClaudeEnv() : void 0,
|
|
8509
|
+
CCUSAGE_FALLBACK_TIMEOUT_MS
|
|
8510
|
+
);
|
|
8511
|
+
fallbacks.set(source, json ? mapCcusageDaily(source, json) : []);
|
|
8512
|
+
}
|
|
8470
8513
|
const entries = [];
|
|
8471
8514
|
const toolsFound = [];
|
|
8472
8515
|
for (const { source, mapped } of sourceResults) {
|
|
8473
|
-
const chosen = selectSourceEntries(source, mapped, native);
|
|
8516
|
+
const chosen = selectSourceEntries(source, fallbacks.get(source) ?? mapped, native);
|
|
8474
8517
|
if (chosen.length > 0) {
|
|
8475
8518
|
entries.push(...chosen);
|
|
8476
8519
|
toolsFound.push(source);
|
|
@@ -8500,7 +8543,13 @@ async function collectAll(onProgress) {
|
|
|
8500
8543
|
...messageCount ? { messageCount } : {}
|
|
8501
8544
|
};
|
|
8502
8545
|
});
|
|
8503
|
-
const scanComplete = isAuthoritativeScan(
|
|
8546
|
+
const scanComplete = isAuthoritativeScan(
|
|
8547
|
+
complete,
|
|
8548
|
+
nativeClaude,
|
|
8549
|
+
nativeCodex,
|
|
8550
|
+
...vscodeResults.map(({ result }) => result),
|
|
8551
|
+
continueResult
|
|
8552
|
+
);
|
|
8504
8553
|
const storePath = provenanceStorePath();
|
|
8505
8554
|
const reconciled = reconcileProvenance(
|
|
8506
8555
|
dedupeDaily(entries),
|
|
@@ -8545,6 +8594,26 @@ function antigravityNoticeLines() {
|
|
|
8545
8594
|
];
|
|
8546
8595
|
}
|
|
8547
8596
|
|
|
8597
|
+
// src/verify-upload.ts
|
|
8598
|
+
import { createHash } from "node:crypto";
|
|
8599
|
+
function prepareVerifyUpload(requests, scanTimedOut, cap = 5e4) {
|
|
8600
|
+
const sorted = requests.slice().sort((a, b) => b.ts - a.ts);
|
|
8601
|
+
const truncated = scanTimedOut || sorted.length > cap;
|
|
8602
|
+
const capped = sorted.length > cap ? sorted.slice(0, cap) : sorted;
|
|
8603
|
+
const records = capped.map((r) => ({
|
|
8604
|
+
date: r.date,
|
|
8605
|
+
ts: r.ts,
|
|
8606
|
+
tool: "claude",
|
|
8607
|
+
model: r.model.trim().slice(0, 128) || "unknown",
|
|
8608
|
+
inputTokens: r.inputTokens,
|
|
8609
|
+
outputTokens: r.outputTokens,
|
|
8610
|
+
cacheCreationTokens: r.cacheCreationTokens,
|
|
8611
|
+
cacheReadTokens: r.cacheReadTokens,
|
|
8612
|
+
reqHash: createHash("sha256").update(r.key).digest("hex").slice(0, 32)
|
|
8613
|
+
}));
|
|
8614
|
+
return { records, truncated };
|
|
8615
|
+
}
|
|
8616
|
+
|
|
8548
8617
|
// src/status.ts
|
|
8549
8618
|
function ago(ms) {
|
|
8550
8619
|
const mins = Math.round(ms / 6e4);
|
|
@@ -9338,7 +9407,7 @@ async function runVerify() {
|
|
|
9338
9407
|
}
|
|
9339
9408
|
}
|
|
9340
9409
|
console.log(pc2.dim(" Reading your local Claude Code logs\u2026"));
|
|
9341
|
-
const { requests, found } = await collectClaudeRequests();
|
|
9410
|
+
const { requests, found, timedOut } = await collectClaudeRequests();
|
|
9342
9411
|
if (!found || requests.length === 0) {
|
|
9343
9412
|
console.log(
|
|
9344
9413
|
pc2.yellow(" No local Claude Code logs found on this machine to verify.")
|
|
@@ -9350,21 +9419,10 @@ async function runVerify() {
|
|
|
9350
9419
|
);
|
|
9351
9420
|
return;
|
|
9352
9421
|
}
|
|
9353
|
-
const
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
const records = capped.map((r) => ({
|
|
9358
|
-
date: r.date,
|
|
9359
|
-
ts: r.ts,
|
|
9360
|
-
tool: "claude",
|
|
9361
|
-
model: r.model,
|
|
9362
|
-
inputTokens: r.inputTokens,
|
|
9363
|
-
outputTokens: r.outputTokens,
|
|
9364
|
-
cacheCreationTokens: r.cacheCreationTokens,
|
|
9365
|
-
cacheReadTokens: r.cacheReadTokens,
|
|
9366
|
-
reqHash: createHash("sha256").update(r.key).digest("hex").slice(0, 32)
|
|
9367
|
-
}));
|
|
9422
|
+
const { records, truncated } = prepareVerifyUpload(
|
|
9423
|
+
requests,
|
|
9424
|
+
timedOut === true
|
|
9425
|
+
);
|
|
9368
9426
|
console.log(
|
|
9369
9427
|
pc2.dim(
|
|
9370
9428
|
` Sending ${records.length} request records for analysis${truncated ? " (most recent, sampled)" : ""}\u2026`
|
|
@@ -9397,7 +9455,7 @@ async function runVerify() {
|
|
|
9397
9455
|
console.log(result.verdict === "fail" ? pc2.yellow(line) : line);
|
|
9398
9456
|
console.log(
|
|
9399
9457
|
pc2.dim(
|
|
9400
|
-
"
|
|
9458
|
+
" A reviewer can clear the moderation after checking this evidence \u2014 re-run `npx whoburnedmore` anytime to check."
|
|
9401
9459
|
)
|
|
9402
9460
|
);
|
|
9403
9461
|
}
|
|
@@ -9586,7 +9644,7 @@ function printHelp() {
|
|
|
9586
9644
|
npx whoburnedmore private take yourself off the public leaderboard
|
|
9587
9645
|
npx whoburnedmore public put yourself back on it
|
|
9588
9646
|
npx whoburnedmore remove delete your usage data and stop background sync
|
|
9589
|
-
npx whoburnedmore verify
|
|
9647
|
+
npx whoburnedmore verify submit detailed usage evidence for review (never prompts or code)
|
|
9590
9648
|
npx whoburnedmore status check background-sync health (last sync, staleness)
|
|
9591
9649
|
npx whoburnedmore uninstall-sync turn off the background sync
|
|
9592
9650
|
npx whoburnedmore install-sync turn it back on after uninstalling
|
package/package.json
CHANGED