Usage: cross-machine token and cost dashboard - #134
Merged
Conversation
Server-side scan of the provider CLIs' own session transcripts (~/.claude/projects, ~/.codex/sessions), so usage covers turns driven from any tool sharing the provider home, not just Threadlines. Pure line parsers handle Claude's per-content-block usage repeats (dedupe by message/request pair) and Codex forked rollouts, whose copied parent history would otherwise double count — a first-class concern here since forking is a native feature. Parsed records are memoised per file by (size, mtime) in an atomic on-disk cache; a usage.summary RPC returns timezone-aware daily buckets for a bounded window. Costs are API list-price equivalents priced from LiteLLM's public rate table (24h disk cache, stale fallback offline), never billed spend; records price as provider-reported, model-priced, or unpriced, and the summary carries the quality split. packages/shared gains pure usageMerge/usageFormat modules that combine per-environment summaries with fingerprint-based duplicate-source detection, ready for the phase-2 page.
/usage renders the merged cross-machine picture: totals strip, a flat stacked-bar daily chart (empty days kept - gaps are information), a per-model table, and a machines section that names every environment the app knows about, including offline ones as "Not reporting" and stale scans as "Out of date". Every cost is labeled API list-price equivalent; subscription plans bill separately. Entry points: a live sidebar-footer meter with today's compact token total (falls back to a plain "Usage" label without data), a one-line usage row in settings > providers, and a command palette action. The meter owns its own query so refetches re-render one line of chrome, sharing a key family with the page so neither double-fetches. Servers predating usage.summary read as unreachable rather than erroring.
The page now leads with the figures: a two-column hero with the API-equivalent cost as the one display-size element (asterisked to its "if billed at full API rates" footnote) over per-provider share bars, beside a smooth overlapping-area daily chart with a cost/tokens toggle. Claude and Codex get the app's first brand hues (--provider-claude terracotta, --provider-codex neutral, both themes). Below: a five-stat band with context sublines (per active day, share of observed input, cache writes, reasoning, savings multiple) and the models table with provider icons, zero-token placeholder rows filtered out. Window switching is now instant: one 90-day scan per environment set, and 7/30/90 (default 30) are derived client-side by narrowing buckets and re-merging, so the selector never refetches or shows loading. The settings row shares the same cache. The sidebar meter says what it is: Usage, then today's compact figure.
The header wraps as a whole row instead of breaking the date range across three lines, model rows drop the share column below sm so the model name keeps its room, and stat sublines wrap instead of clipping. Desktop keeps the single-row band and the four-column table.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The toggle used to switch only the chart while the headline stayed in dollars. Now the hero follows it: the big figure, its label, and the provider rows all show the selected measure, with the share bars sized by token share in tokens mode. The cost asterisk moves to a superscript and keeps its footnote only in cost mode, where the caveat means something. The breakdown table gains a Model|Day toggle: day rows list each active day newest first with tokens, cost, and share of the window's cost.
The native title tooltip made the reader wait and gave one line of text. Hovering the plot now draws a hairline through the hovered day and pins a card beside it: the day, one row per charted provider with its figure in the chart's mode, and the total. The card flips sides past the midline so it never leaves the plot, and everything it shows is precomputed on the column, so hover work is a lookup.
The plain text row read as page furniture and never said the figures covered 30 days. It is now a clickable card in the same family as the provider entries: a window-stamped label over the token and cost figures, with the view-usage affordance on the right.
The settings sidebar replaced it with nothing, so the one screen where quota bars live lost the live token count. Same component, same position: bottom-left means "today's usage" on both surfaces.
The tile's mixed-brightness line made the dim words drop their bottom anti-aliased pixel and read a hair higher than the numbers; the whole line is now one color and the caps label above carries the hierarchy. The chart legend swaps its swatch dots for the provider icons, whose built-in brand fills already match the series hues.
The advanced section was a native details element that React also controlled: the browser toggled open instantly, and any render committed before the onToggle state update re-applied the stale open and snapped it shut - stuck closed, since the revert also fired onToggle(false). Summary clicks now prevent the native toggle and flip React state, the only driver. Fixes the CI-only failure on the verified-usage settings test.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a Usage surface: how many tokens (and what they would cost at API list prices) Claude Code and Codex have burned, across every machine the app knows about — covering all activity in the provider homes, not just Threadlines sessions.
How it works
~/.claude/projects,~/.codex/sessions) with pure line parsers: Claude's per-content-block usage repeats dedupe by message/request pair; Codex forked rollouts (a first-class Threadlines feature) suppress their re-stamped parent-history prefix so fork copies never double count. Records memoise per file by (size, mtime) in an atomic cache; a cold 90-day scan of real data ran ~8s, warm reads 60–160ms.usage.summaryout to every connected environment and merges in shared pure code, with fingerprint dedupe so two servers scanning one directory can't double count. Machines that can't be reached stay visible as "Not reporting" — totals never silently omit a computer.The page
/usage: two-column hero (API-equivalent cost as the page's one display figure with an "if billed at full API rates" footnote, per-provider share bars), a smooth overlapping-area daily chart with a cost/tokens toggle (hand-rolled SVG, monotone-cubic so curves can't overshoot the data), a five-stat band with context sublines, a models table with provider icons, and the machines/freshness section. Claude and Codex get the app's first brand hues (terracotta / strong neutral, both themes).One 90-day scan feeds all windows: 7/30/90 (default 30) are derived client-side by narrowing daily buckets, so switching is instant — verified zero network activity while flipping windows.
Entry points: a live sidebar-footer meter (
Usage · 74M today, plainUsageuntil data), a usage row in settings → providers sharing the same cache, and a command palette action.Verification
cache_creationnesting and real Codex fork timings), scan cache, aggregation windows, pricing normalisation (with a determinism fix over the reference implementation), merge/dedupe, window derivation. Server 71, shared 20, plus RPC surface via server tests.vp fmt/vp lint/vp run typecheckclean; full web unit and browser suites green.usage.summaryrefetch, chart toggle and peak/axis spot-checks, stat band, model filtering, machines freshness, settings + palette entry points, zero console errors), with light/dark visual review at 1440px and 380px.