whoburnedmore 0.9.16 → 0.9.17

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.
Files changed (2) hide show
  1. package/dist/index.js +80 -74
  2. 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";
@@ -6618,6 +6617,31 @@ async function readFilesWithCache(opts) {
6618
6617
  return { itemsByFile, filesRead, timedOut: false };
6619
6618
  }
6620
6619
 
6620
+ // src/native/usage-date.ts
6621
+ var MIN_USAGE_TIMESTAMP_MS = Date.UTC(2020, 0, 1);
6622
+ var MAX_CLOCK_SKEW_MS = 2 * 864e5;
6623
+ function localUsageDate(ms, now = Date.now()) {
6624
+ if (!Number.isFinite(ms) || ms < MIN_USAGE_TIMESTAMP_MS || ms > now + MAX_CLOCK_SKEW_MS) {
6625
+ return null;
6626
+ }
6627
+ const d = new Date(ms);
6628
+ if (!Number.isFinite(d.getTime())) return null;
6629
+ const y = d.getFullYear();
6630
+ const m = String(d.getMonth() + 1).padStart(2, "0");
6631
+ const day = String(d.getDate()).padStart(2, "0");
6632
+ return `${y}-${m}-${day}`;
6633
+ }
6634
+ function plausibleUsageDate(value, now = Date.now()) {
6635
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
6636
+ if (!match) return false;
6637
+ const year = Number(match[1]);
6638
+ const month = Number(match[2]);
6639
+ const day = Number(match[3]);
6640
+ const ms = Date.UTC(year, month - 1, day);
6641
+ const parsed = new Date(ms);
6642
+ return parsed.getUTCFullYear() === year && parsed.getUTCMonth() === month - 1 && parsed.getUTCDate() === day && ms >= MIN_USAGE_TIMESTAMP_MS && ms <= now + MAX_CLOCK_SKEW_MS;
6643
+ }
6644
+
6621
6645
  // src/native/claude.ts
6622
6646
  function num(n) {
6623
6647
  const v = Math.round(Number(n));
@@ -6626,15 +6650,6 @@ function num(n) {
6626
6650
  function reqTokens(r) {
6627
6651
  return r.inputTokens + r.outputTokens + r.cacheCreationTokens + r.cacheReadTokens;
6628
6652
  }
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
6653
  var syntheticCounter = 0;
6639
6654
  function parseClaudeLine(raw) {
6640
6655
  const trimmed = raw.trim();
@@ -6650,10 +6665,10 @@ function parseClaudeLine(raw) {
6650
6665
  const usage = message.usage;
6651
6666
  if (!usage || typeof usage !== "object") return null;
6652
6667
  if (message.role !== void 0 && message.role !== "assistant") return null;
6653
- const date = localDate(String(obj.timestamp ?? ""));
6654
- if (!date) return null;
6655
6668
  const tsParsed = Date.parse(String(obj.timestamp ?? ""));
6656
- const ts = Number.isFinite(tsParsed) ? tsParsed : 0;
6669
+ const date = localUsageDate(tsParsed);
6670
+ if (!date) return null;
6671
+ const ts = tsParsed;
6657
6672
  const messageId = typeof message.id === "string" ? message.id : "";
6658
6673
  const requestId = typeof obj.requestId === "string" ? obj.requestId : "";
6659
6674
  const hasRealId = messageId !== "" || requestId !== "";
@@ -6747,7 +6762,7 @@ async function listJsonl(dir) {
6747
6762
  return out;
6748
6763
  }
6749
6764
  var NATIVE_READ_BUDGET_MS = 45e3;
6750
- var CLAUDE_CACHE_VERSION = 1;
6765
+ var CLAUDE_CACHE_VERSION = 2;
6751
6766
  function toCached(r) {
6752
6767
  return [
6753
6768
  r.key,
@@ -7190,13 +7205,6 @@ function toEpochMs(ts) {
7190
7205
  }
7191
7206
  return null;
7192
7207
  }
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
7208
  function mapContinueRecords(records) {
7201
7209
  const byKey = /* @__PURE__ */ new Map();
7202
7210
  for (const raw of records) {
@@ -7208,10 +7216,11 @@ function mapContinueRecords(records) {
7208
7216
  if (!model) continue;
7209
7217
  const ms = toEpochMs(rec.timestamp ?? rec.eventTimestamp);
7210
7218
  if (ms === null) continue;
7219
+ const date = localUsageDate(ms);
7220
+ if (!date) continue;
7211
7221
  const inputTokens = num2(rec.promptTokens ?? rec.prompt_tokens);
7212
7222
  const outputTokens = num2(rec.generatedTokens ?? rec.generated_tokens);
7213
7223
  if (inputTokens + outputTokens === 0) continue;
7214
- const date = localDate2(ms);
7215
7224
  const key = `${date}|${model}`;
7216
7225
  let b = byKey.get(key);
7217
7226
  if (!b) {
@@ -7269,11 +7278,11 @@ async function listJsonl2(dir) {
7269
7278
  for (const d of dirents) {
7270
7279
  const full = join6(dir, d.name);
7271
7280
  if (d.isDirectory()) out.push(...await listJsonl2(full));
7272
- else if (d.isFile() && d.name.endsWith(".jsonl")) out.push(full);
7281
+ else if (d.isFile() && d.name === "tokensGenerated.jsonl") out.push(full);
7273
7282
  }
7274
7283
  return out;
7275
7284
  }
7276
- var CONTINUE_CACHE_VERSION = 1;
7285
+ var CONTINUE_CACHE_VERSION = 2;
7277
7286
  async function collectContinue(opts = {}) {
7278
7287
  const env = opts.env ?? process.env;
7279
7288
  const home = opts.continueDir ?? join6(env.HOME || homedir5(), ".continue");
@@ -7493,7 +7502,8 @@ function mapCursorEvents(events) {
7493
7502
  const ms = Number(e.timestamp);
7494
7503
  if (!tu || !Number.isFinite(ms)) continue;
7495
7504
  const d = new Date(ms);
7496
- const date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
7505
+ const date = localUsageDate(ms);
7506
+ if (!date) continue;
7497
7507
  const model = e.model || "cursor";
7498
7508
  const input = num4(tu.inputTokens);
7499
7509
  const output = num4(tu.outputTokens);
@@ -7590,15 +7600,6 @@ function num5(n) {
7590
7600
  const v = Math.round(Number(n));
7591
7601
  return Number.isFinite(v) && v > 0 ? v : 0;
7592
7602
  }
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
7603
  function readTokenFields(payload) {
7603
7604
  const info = payload.info;
7604
7605
  const src = info?.total_token_usage ?? payload;
@@ -7608,8 +7609,7 @@ function readTokenFields(payload) {
7608
7609
  return {
7609
7610
  input: num5(input),
7610
7611
  cached: num5(src.cached_input_tokens),
7611
- output: num5(output),
7612
- reasoning: num5(src.reasoning_output_tokens)
7612
+ output: num5(output)
7613
7613
  };
7614
7614
  }
7615
7615
  function parseCodexRollout(lines) {
@@ -7634,7 +7634,7 @@ function parseCodexRollout(lines) {
7634
7634
  if (payload.type === "token_count") {
7635
7635
  const fields = readTokenFields(payload);
7636
7636
  if (!fields) continue;
7637
- const parsed = localDate3(String(obj.timestamp ?? ""));
7637
+ const parsed = localUsageDate(Date.parse(String(obj.timestamp ?? "")));
7638
7638
  const day = parsed ?? lastSeenDate;
7639
7639
  if (!day) continue;
7640
7640
  lastSeenDate = day;
@@ -7650,17 +7650,16 @@ function parseCodexRollout(lines) {
7650
7650
  if (perDay.size === 0) return [];
7651
7651
  const dates = [...perDay.keys()].sort();
7652
7652
  const out = [];
7653
- let prev = { input: 0, cached: 0, output: 0, reasoning: 0 };
7653
+ let prev = { input: 0, cached: 0, output: 0 };
7654
7654
  for (const date of dates) {
7655
7655
  const { cum, turns } = perDay.get(date);
7656
7656
  const dInput = Math.max(0, cum.input - prev.input);
7657
7657
  const dCached = Math.max(0, cum.cached - prev.cached);
7658
7658
  const dOutput = Math.max(0, cum.output - prev.output);
7659
- const dReasoning = Math.max(0, cum.reasoning - prev.reasoning);
7660
7659
  prev = cum;
7661
7660
  const cacheReadTokens = dCached;
7662
7661
  const inputTokens = Math.max(0, dInput - dCached);
7663
- const outputTokens = dOutput + dReasoning;
7662
+ const outputTokens = dOutput;
7664
7663
  if (inputTokens + outputTokens + cacheReadTokens === 0) continue;
7665
7664
  out.push({
7666
7665
  date,
@@ -7746,7 +7745,7 @@ function* splitLines3(content) {
7746
7745
  if (start < content.length) yield content.slice(start);
7747
7746
  }
7748
7747
  var NATIVE_READ_BUDGET_MS2 = 45e3;
7749
- var CODEX_CACHE_VERSION = 1;
7748
+ var CODEX_CACHE_VERSION = 2;
7750
7749
  function toCachedSession(s) {
7751
7750
  return [
7752
7751
  s.date,
@@ -7813,17 +7812,9 @@ function num6(n) {
7813
7812
  const v = Math.round(Number(n));
7814
7813
  return Number.isFinite(v) && v > 0 ? v : 0;
7815
7814
  }
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
7815
  function parseApiReqMessage(tool, msg) {
7825
7816
  if (msg.say !== "api_req_started") return null;
7826
- const date = localDate4(Number(msg.ts));
7817
+ const date = localUsageDate(Number(msg.ts));
7827
7818
  if (!date) return null;
7828
7819
  let payload = null;
7829
7820
  const text = msg.text;
@@ -7947,7 +7938,7 @@ async function listTaskFiles(roots, extIds) {
7947
7938
  }
7948
7939
  return files;
7949
7940
  }
7950
- var VSCODE_CACHE_VERSION = 1;
7941
+ var VSCODE_CACHE_VERSION = 2;
7951
7942
  function entryToRow(e) {
7952
7943
  return [
7953
7944
  e.date,
@@ -8181,14 +8172,14 @@ function normCost(n) {
8181
8172
  const v = Number(n);
8182
8173
  return Number.isFinite(v) && v > 0 ? v : 0;
8183
8174
  }
8184
- function mapCcusageDaily(tool, json) {
8175
+ function mapCcusageDaily(tool, json, now = Date.now()) {
8185
8176
  const daily = json?.daily;
8186
8177
  if (!Array.isArray(daily)) return [];
8187
8178
  const entries = [];
8188
8179
  for (const rawDay of daily) {
8189
8180
  const day = rawDay;
8190
8181
  const date = day.date ?? day.period;
8191
- if (typeof date !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(date)) continue;
8182
+ if (typeof date !== "string" || !plausibleUsageDate(date, now)) continue;
8192
8183
  const breakdowns = Array.isArray(day.modelBreakdowns) ? day.modelBreakdowns : [];
8193
8184
  const modelsMap = day.models && typeof day.models === "object" && !Array.isArray(day.models) ? day.models : null;
8194
8185
  const dayCost = normCost(day.totalCost ?? day.costUSD);
@@ -8390,8 +8381,8 @@ async function runCcusage(cmd, args, env) {
8390
8381
  return (await runCcusageOnce(cmd, args, env)).json;
8391
8382
  }
8392
8383
  var COLLECT_STAGES = SOURCES.length + 4 + VSCODE_AGENTS.length + 1;
8393
- function isAuthoritativeScan(attributionComplete, nativeClaude, nativeCodex) {
8394
- return attributionComplete && nativeClaude.timedOut !== true && nativeCodex.timedOut !== true;
8384
+ function isAuthoritativeScan(attributionComplete, ...fingerprintReaders) {
8385
+ return attributionComplete && fingerprintReaders.every((reader) => reader.timedOut !== true);
8395
8386
  }
8396
8387
  async function collectAll(onProgress) {
8397
8388
  await loadLivePricing().catch(() => {
@@ -8500,7 +8491,13 @@ async function collectAll(onProgress) {
8500
8491
  ...messageCount ? { messageCount } : {}
8501
8492
  };
8502
8493
  });
8503
- const scanComplete = isAuthoritativeScan(complete, nativeClaude, nativeCodex);
8494
+ const scanComplete = isAuthoritativeScan(
8495
+ complete,
8496
+ nativeClaude,
8497
+ nativeCodex,
8498
+ ...vscodeResults.map(({ result }) => result),
8499
+ continueResult
8500
+ );
8504
8501
  const storePath = provenanceStorePath();
8505
8502
  const reconciled = reconcileProvenance(
8506
8503
  dedupeDaily(entries),
@@ -8545,6 +8542,26 @@ function antigravityNoticeLines() {
8545
8542
  ];
8546
8543
  }
8547
8544
 
8545
+ // src/verify-upload.ts
8546
+ import { createHash } from "node:crypto";
8547
+ function prepareVerifyUpload(requests, scanTimedOut, cap = 5e4) {
8548
+ const sorted = requests.slice().sort((a, b) => b.ts - a.ts);
8549
+ const truncated = scanTimedOut || sorted.length > cap;
8550
+ const capped = sorted.length > cap ? sorted.slice(0, cap) : sorted;
8551
+ const records = capped.map((r) => ({
8552
+ date: r.date,
8553
+ ts: r.ts,
8554
+ tool: "claude",
8555
+ model: r.model.trim().slice(0, 128) || "unknown",
8556
+ inputTokens: r.inputTokens,
8557
+ outputTokens: r.outputTokens,
8558
+ cacheCreationTokens: r.cacheCreationTokens,
8559
+ cacheReadTokens: r.cacheReadTokens,
8560
+ reqHash: createHash("sha256").update(r.key).digest("hex").slice(0, 32)
8561
+ }));
8562
+ return { records, truncated };
8563
+ }
8564
+
8548
8565
  // src/status.ts
8549
8566
  function ago(ms) {
8550
8567
  const mins = Math.round(ms / 6e4);
@@ -9338,7 +9355,7 @@ async function runVerify() {
9338
9355
  }
9339
9356
  }
9340
9357
  console.log(pc2.dim(" Reading your local Claude Code logs\u2026"));
9341
- const { requests, found } = await collectClaudeRequests();
9358
+ const { requests, found, timedOut } = await collectClaudeRequests();
9342
9359
  if (!found || requests.length === 0) {
9343
9360
  console.log(
9344
9361
  pc2.yellow(" No local Claude Code logs found on this machine to verify.")
@@ -9350,21 +9367,10 @@ async function runVerify() {
9350
9367
  );
9351
9368
  return;
9352
9369
  }
9353
- const CAP = 5e4;
9354
- const sorted = requests.slice().sort((a, b) => b.ts - a.ts);
9355
- const truncated = sorted.length > CAP;
9356
- const capped = truncated ? sorted.slice(0, CAP) : sorted;
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
- }));
9370
+ const { records, truncated } = prepareVerifyUpload(
9371
+ requests,
9372
+ timedOut === true
9373
+ );
9368
9374
  console.log(
9369
9375
  pc2.dim(
9370
9376
  ` Sending ${records.length} request records for analysis${truncated ? " (most recent, sampled)" : ""}\u2026`
@@ -9397,7 +9403,7 @@ async function runVerify() {
9397
9403
  console.log(result.verdict === "fail" ? pc2.yellow(line) : line);
9398
9404
  console.log(
9399
9405
  pc2.dim(
9400
- " We'll relist you automatically if it checks out \u2014 re-run `npx whoburnedmore` anytime to check."
9406
+ " A reviewer can clear the moderation after checking this evidence \u2014 re-run `npx whoburnedmore` anytime to check."
9401
9407
  )
9402
9408
  );
9403
9409
  }
@@ -9586,7 +9592,7 @@ function printHelp() {
9586
9592
  npx whoburnedmore private take yourself off the public leaderboard
9587
9593
  npx whoburnedmore public put yourself back on it
9588
9594
  npx whoburnedmore remove delete your usage data and stop background sync
9589
- npx whoburnedmore verify delisted? re-verify your usage to get back on (sends a detailed breakdown)
9595
+ npx whoburnedmore verify submit detailed usage evidence for review (never prompts or code)
9590
9596
  npx whoburnedmore status check background-sync health (last sync, staleness)
9591
9597
  npx whoburnedmore uninstall-sync turn off the background sync
9592
9598
  npx whoburnedmore install-sync turn it back on after uninstalling
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whoburnedmore",
3
- "version": "0.9.16",
3
+ "version": "0.9.17",
4
4
  "description": "Find out who burned more — submit your AI coding-agent token usage to the public leaderboard at whoburnedmore.com",
5
5
  "type": "module",
6
6
  "bin": {