agent/claude-usage/backfill-history.js runs by hand from the repo (never
from the cron install, the agent is unchanged). It rebuilds the closed days
with the agent's own aggregate() on past windows and writes { days } to the
--out file, which the server merges with `claude-usage-history.js --import`
(an already archived date wins).
Each 30-day window keeps only its 28 inner days and the next steps back 28
days, so a request straddling a window boundary is deduplicated by the
window that keeps it and counted once. The first window stops at D-2. The
walk is bounded by --max-windows (default 24) rather than guessed from file
mtimes, which say nothing of the oldest line a resumed session holds.
It never reads the credentials file, opens no connection, writes only the
--out file (mode 600, never overwritten) and prints counts only.
Refs #55
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #56: making ENOENT an empty scan on every directory, not only the
root, left the suite green. A vanished subdirectory is now a pinned read
error. The header also names the other known limit of the midnight fix:
the mtime threshold may skip a subagent transcript holding the only copy
of a straddling request's earlier line.
Refs #54
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- aggregate: frozen days are dropped after deduplication, not before. A
request straddling midnight whose best line sat on a day frozen since
was counted again on the open day. On equal output the earliest line now
wins (it was the first one read), so the choice no longer depends on
which files a beat reads.
- aggregate: unreadable directories, failed stats (ENOENT included) and
stream errors are counted in `readErrors`; a missing root is still an
empty scan. The beat then freezes no new day (nextState freeze: false),
still pushes, and logs `read_errors=N` with no path.
- limits: the response is read chunk by chunk and counted in bytes; past
256 KiB the stream is cancelled instead of being read in full and
measured in characters.
- state: a test now covers the chmodSync of a leftover temp file.
Each fix was checked by mutation (13 mutations, 13 red).
Refs #54
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
limits.js reads the Claude Code OAuth credentials read-only and calls the
pinned Anthropic usage endpoint once (redirect refused, 10 s timeout, no
retry, never a refresh); detail codes come from a fixed enum only.
state.js freezes every Toronto day before yesterday, empty ones included,
prunes past 35 days, keeps the last good limits, and writes atomically in
mode 600. push-claude-usage.js writes the cache before pushing, refuses a
non-https URL (exit 1) and a body over 64 KiB (exit 2, nothing sent), and
prints no token. run-claude-usage.sh takes a non-blocking flock so a beat
that overlaps the previous one exits 0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The threshold came from the most recent frozen key: a frozen day in the
future, or a gap in the frozen run, pushed it past open days whose files
were then never read, and the agent would have frozen them empty for good.
It is now the oldest open day of the window (the same day as before for the
agent's contiguous frozen prefix).
Project, model and day maps are now Object.create(null): a project named
__proto__ or constructor wrote NaN counters onto Object.prototype and
dropped out of the top.
Resolves#49
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pure, dependency-free module for the claude-usage agent. It walks
~/.claude/projects/**/*.jsonl (subagent transcripts included), keeps
assistant lines with message.usage, drops <synthetic> and truncated
lines, deduplicates on (message.id, requestId) across all files at the
largest output_tokens, and buckets by America/Toronto day computed as
strings only (DST-safe).
Returns { openDays, days, projects, claudeCodeVersion }: the recomputed
non-frozen days, the merged 30-day series (at most 12 models per day,
the rest folded into "other"), the top 8 projects by work tokens plus
"(autres)", and the version of the newest line. Files whose mtime day
is before the day after the most recent frozen day are never opened.
Resolves#49
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>