@intframe/usage-recover
v0.1.0
Published
Recover deleted Claude Code and Codex usage history from public leaderboard aggregates, back into ccusage cc.json format
Downloads
56
Maintainers
Readme
usage-recover
Recover deleted Claude Code and Codex usage history from a public leaderboard profile, back into the cc.json shape ccusage emits.
npx @intframe/usage-recover <username> -o cc.jsonWhy
Claude Code deletes session transcripts after cleanupPeriodDays, which defaults to 30. Once they are gone the usage they represent is gone with them: ccusage reads raw session files, so it cannot report a month it can no longer see, and neither can anything built on top of it.
We lost four months this way across six accounts, on a machine that was decommissioned before anyone thought about it. What made the history recoverable was that aggregates had been submitted to viberank months earlier. This tool is that recovery, packaged.
What it recovers, and how exactly
| | | |---|---| | per-day cost | exact | | per-model cost | exact | | token totals, all four buckets | exact | | per-day token split | derived from cost |
A profile publishes cost per day but only aggregate token counts, so the daily token split does not exist in the source and cannot be recovered. It is apportioned by cost, which keeps every total truthful and every day's relative weight intact. The output states this in a fidelity field rather than leaving a consumer to assume the whole file is measured.
Getting the token totals exactly
The interesting part is that the totals come out exact rather than rounded.
A profile prints each token bucket twice. Once as a label, 36.2B, which at that scale covers a 50-million-wide interval and is useless on its own. Once as a bar width, 95.05060355502121%, which carries full float precision but is a ratio with no absolute anchor.
Together they pin the integers. Every percentage is bucket / total computed from whole numbers, so scanning candidates for the smallest bucket and testing whether the other three land on integers isolates one consistent solution. On the profile above it converges with zero residual:
label 36.2B 1.0B 202.6M 657.2M
bar 95.0506035% 2.6933182% 0.5315882% 1.7244898%
solved 36,223,082,857 1,026,403,707 202,584,370 657,190,346
total 38,109,261,280Output
Standard cc.json, so anything that reads ccusage daily --json reads this: leaderboard submissions, importers, your own scripts.
Every conversion is checked before it is written. Token buckets must sum to their totals at both day and profile level, model costs must sum to the day, dates must be ISO, and cost per token must land inside the band leaderboards accept. A file that fails any of these is not emitted, because the errors it would produce downstream point at the payload rather than at the conversion that made it.
Library
import { fetchProfile, buildCcJson, verify } from "@intframe/usage-recover";
const profile = await fetchProfile("aron-intframe");
const cc = buildCcJson(profile, new Date().toISOString());
const problems = verify(cc);
if (problems.length) throw new Error(problems.join("\n"));recoverTotals is exported separately if you only want the integer-recovery step.
Limits
Only public viberank profiles are supported. It reads the rendered page, so a markup change upstream can break parsing; the verification step is there so that surfaces as a clear failure rather than as plausible wrong numbers.
Recovered aggregates are not a substitute for measured sessions. If your transcripts still exist, run ccusage against them.
License
MIT
