* feat(quota): Command Code quota and ClinePass from the Cline sign-in
Command Code: reads the CLI's ~/.commandcode/auth.json key and the billing
credits and subscription endpoints. 5-hour and weekly windows as used of
cap, remaining credits as a note, plan from planId. Live in the Mac dock
from the local CLI, in codeburn quota and the Windows dock.
ClinePass: without a saved or env key, falls back to the Cline CLI's
providers.json session (accessToken, then apiKey, then auth.apiKey, with
Cline's path overrides). Read-only; an expired session stops before the
network and says to run cline. The Mac catalog adds the local CLI source
so Automatic connects without a key.
* fix(quota): keep retrying after an expired Cline sign-in
Running cline refreshes the session file CodeBurn only reads, so an
expired or rejected Cline session is now a retryable failure that keeps
the last quota and shows the guidance, on the Mac and in the CLI. A
rejected API key and a missing credential stay terminal.
* test(quota): build expected Cline paths with path.join for Windows
Adds a live quota gauge for the z.ai ZCode coding plan on all three surfaces: the CLI plan view, the desktop Plans page, and the mac menu bar. It reads the provider's own remaining-allowance endpoint, so it is quota-only and touches no cost or spend paths. One decoder is shared between Z.ai and ZCode, and ZCode is hidden when a Z.ai login already covers the same endpoint. The fetch is bounded with graceful disconnected, waiting and error states, and no socket is opened without a login. The logo shows on every surface, and ZCode is listed in the desktop supported-provider list to match the menu bar catalog.
Groundwork for #1219. The menubar app hardcoded ~530 English strings across
Views/, the presentation structs, the status-item menu and the update alerts,
so there was nothing for a translator to translate.
Every user-facing literal now goes through `L(_:)` / `L(_:_:)`, which look the
string up in `Localizable.strings` in the SwiftPM target resource bundle. The
key *is* the English copy, so English stays the development language and a
missing translation degrades to correct English instead of a dotted
identifier. `en.lproj` is therefore an identity table; it exists so the bundle
advertises `en` and so a translation can be diffed against it.
Why the lookups are explicit rather than relying on SwiftUI's implicit
`LocalizedStringKey`: SwiftPM emits target resources into a sibling bundle
(`CodeBurnMenubar_CodeBurnMenubar.bundle`) that the packaging scripts copy into
`Contents/Resources`. `Bundle.main` has no `.lproj` at all, so `Text("literal")`
would always miss. Naming `Bundle.module` is the one form that resolves the
same way in `swift run`, in `swift test` and in the packaged `.app`.
Enum raw values that double as identity (`Period`, `MenubarScope`,
`InsightMode`, `AccentPreset`, `ProviderFilter`) keep their raw value and gain
a `displayLabel`, so persistence and cache keys are untouched by translation.
Three display-only date formatters that were pinned to `en_US_POSIX` with fixed
patterns now use `setLocalizedDateFormatFromTemplate`, and the calendar
popover's weekday row comes from the locale's own symbols. The `yyyy-MM-dd`
formatter stays POSIX: it parses and builds data keys, not display text.
Not extracted, deliberately: provider, model and plan names; units; currency
codes; shell commands and paths; quota window labels that policy code matches
on by English substring (`QuotaSummary.headlineWindow`); and anything the
`codeburn` CLI produces.
Also copies the SwiftPM resource bundle into the app in build-local.sh, using
the named-bundle + `[[ -d ]]` pattern package-app.sh already uses. Without it
the assembled app finds no strings table (and already trapped on first icon
load, the resource-bundle half of #1262).
The resting rail shows one number per provider, and that number was always
the weekly (else monthly) billing window. A user watching Claude's 5-hour
limit had to expand the dock and read the glance card every time, which
defeats the point of an always-visible usage monitor.
Clicking the provider that already rests in the rail now switches its gauge
to the provider's short rolling window and back. The preference is stored per
provider rather than as a Claude-only switch: the dock's collapsed row is
provider-agnostic, and Codex publishes a 5-hour and a weekly window too, so
the question is "which horizon for this provider", stored under
CodeBurnCapacityDockGlanceWindows alongside the other dock preferences.
The two horizons are named rather than labelled, so the policy works for any
adapter: billing reuses QuotaSummary.headlineWindow, the app's single
definition of the weekly/monthly glance, and burst matches the short rolling
rows the adapters emit (5-hour, Hourly, Daily, session) while excluding
anything weekly or monthly, so Claude's per-model "Weekly · Opus" row can
never be read as a short window. CapacityDockGlanceWindow keeps that policy
pure and testable, including the fallbacks: a provider with one window shows
it whichever horizon is stored, and a stored horizon the provider stops
reporting resolves to the one it does report instead of blanking the gauge.
The click cannot fight the hover expansion: this branch only ever pins, never
collapses, and it is reached only when the provider actually has two windows,
so a single-window provider keeps the existing click-to-pin toggle. Escape
and an outside click still unpin. Keyboard and VoiceOver users get the same
switch as a named accessibility action on the provider cell, and the cell's
accessibility value now names the window feeding the ring. The rail's
geometry is untouched.
Closes#1243
The glance's Today row was scoped to the hovered provider by matching one
catalog id against one payload row. Cursor read $0.00 with 14 calls
recorded, because the CLI splits that one subscription into `cursor` for
the editor and `cursor-agent` for the agent, and the tile saw only the
first. Droid read zero for a plainer reason: the tile carries the account's
id, `factory`, while the CLI records the product, `droid`.
A tile now maps to a list of payload rows and sums them: Cursor over both
its surfaces, ClinePass over `cline` and `cline-cli`, Droid onto `droid`.
Every other tile still stands for exactly one row, and no row belongs to
two tiles, so the per-tile figures still reconcile to the machine total.
A summed tile keeps a token field absent unless some row reports it, so an
older CLI that sends no per-provider tokens still reads as "no breakdown"
rather than as a zero the account did not earn.
Checked every one of the 69 tiles against the provider registry by id and
by display name; these three were the only mismatches. Mistral and Qwen
Cloud look similar but are not confirmed to be the same account as the
CLI's `mistral-vibe` and `qwen`, so they are left alone deliberately.
The Capacity Dock's glance popover rendered the machine-wide Today block in
every provider's card, so hovering Claude showed the total across every
provider as if it were Claude's.
CLI (add-only): ProviderCost and providerDetails now carry optional
inputTokens, outputTokens and sessions, summed per provider in all three
branches that build the provider list. They stay ABSENT when no day in the
period reported a breakdown, so a consumer can tell "no data" from zero.
STATUS_SNAPSHOT_RENDER_VERSION 4 -> 5, since a v4 snapshot's payload predates
the new fields; the daily cache already holds the tokens, so its version is
untouched.
App: the Today row reads the hovered provider's row out of providerDetails
(matched on the CLI provider id, which the dock's kimi entry bridges to
kimicode). Against a CLI that omits per-provider tokens the arrows drop out
rather than falling back to the global pair; a provider absent from a
breakdown that IS present reads as a real $0.00. The row keeps its fixed
frame, so the panel's computed height is unchanged either way.
A credential-less adapter left in the dock selection was silently
reconnected by the next scheduled refresh, undoing the user's explicit
disconnect. Cursor is the first adapter that needs no saved credential,
so the disconnect intent now persists: the provider leaves the stored
selection without latching manual-selection mode.
* feat(menubar): add native capacity dock
* fix(menubar): harden dock interaction boundaries
* feat(menubar): polish native Capacity Dock for launch
- Flush top-edge docking via NSHostingView safe-area opt-out; shielding
window level so the rail necks into the physical top like the system notch
- Liquid Glass legibility scrim so labels stay readable on light wallpapers
- Weekly-else-monthly headline window; unified severity colour bands
(green <50 / yellow / orange / red) across rings and detail bars
- Squircle gauge + graphite theme defaults; auto-show connected
subscriptions (up to 5) until the user edits the set, then respect it
- Capacity Dock Settings menu item; re-centre the settings window; info
glyph for the About row
- Trim dead provider-credential override fields down to apiKey only
- Reconcile and extend tests for all of the above
---------
Co-authored-by: iamtoruk <hello@agentseal.org>