UsageTray #
a lightweight, native macos menu bar app that displays live quotas, rate limits, and usage balances for ai providers configured in prime agent (~/.prime/agent/auth.json).
built with native swift, appkit, and swiftui. zero daemons, no node or python at runtime, instant startup.
how it works #
the quota rules do not live here. they live in the usage core (usage-core, a zig
core with a c abi and this swift wrapper), the same implementation the agent's own usage command and
/usage view answer from. this app is the host: it resolves credentials, sends the requests the core
asks for, and draws the report.
that means a provider appears here as soon as the core has a feed for it and this machine has a credential for it — nothing in this repo decides which providers exist. today the core reports:
| provider | credential it uses | what it reports |
|---|---|---|
anthropic |
oauth (auto-refreshed here) | 5-hour session, 7-day window, plan, extra-usage state |
openai-codex |
oauth (auto-refreshed here) | weekly window, spark windows, plan, email |
google-antigravity |
oauth (auto-refreshed here) | gemini / claude / gpt-oss remaining, project |
opencode-go |
api key | session, weekly and monthly windows |
zai |
api key | request count, token quota |
deepseek |
api key | account balance in usd/cny |
hyper |
oauth or api key | plan credits used, balance, team |
alibaba |
its own bl cli, or a captured console session |
token-plan 5h / weekly windows |
a provider the app has no icon for still gets a card: the core names it, and the app falls back to a
generic symbol until a brand glyph is added to ProviderIcons.swift.
credentials #
- where: prime agent's
~/.prime/agent/auth.json— one entry per provider, either a single credential or a list of accounts. - refresh: oauth tokens for anthropic, codex and antigravity are refreshed through the same client ids their own cli clients use, and written back atomically. everything else is used as stored, because the core does not need anything else.
- several credentials per provider: they are tried in order until one actually reports. one
account's token can be valid for inference and invalid for its quota endpoint —
hyper's oauth token 404s on its teams api while its api key answers fine — and a card should not go blank over it. - sessions and clis: a provider whose api keys cannot answer its own quota (alibaba's token plan)
is asked for its own cli, or read from
~/.prime/agent/<provider>-session.jsonif you captured one.
building & running #
make bundle # builds the usage core (zig build), then the app, into dist/UsageTray.app
make install # builds and restarts /Applications/UsageTray.app
make dump # print provider usage to stdout without launching the gui
the core is a sibling checkout:
git clone git@knot.gaze.systems:did:plc:2lf7buutfnfcucljnmaypf7u ../usage-core
ui interactions #
- left-click menu bar icon: opens the glassmorphism popover showing provider cards, color-coded progress bars, countdown timers, and reload buttons.
- right-click menu bar icon: context menu with quick actions (
Refresh Now,Open auth.json,Quit).
configuration #
~/.config/usage-tray/config.json:
excludedProviders: providers whose quota is shared and should not count toward the pool total.provider5HourCapacities: what a provider's window is worth in tokens, for the menubar pool estimate.defaultProviderCapacity: used for a provider with no entry above, so a new provider still counts.costPerToken: dollar-per-token conversion for providers that report a balance instead of windows.