01Personal · Open source2026
Usagent - AI coding-agent usage, always visible
Usagent is a macOS menu-bar utility that keeps your AI coding-agent usage in view at all times. It reads Codex and Cursor usage, normalizes both into a single model behind one provider interface, and renders a compact per-provider quota view - no dashboards, no /usage commands.
- Tauri
- Rust
- React
- TypeScript
- 2
- providers behind one interface
- 0
- tokens reaching the UI
- 3m
- local poll cadence for Codex
- 30m
- backoff cap on failures
01Why I built it
I spend my day in Codex and Cursor from the CLI. Checking how much headroom
I have left meant running /usage, or opening a dashboard
mid-flow - and I kept forgetting until something hit a limit at the worst
moment. The pain was small and perfectly recurring, which is exactly when a
tiny tool earns its place.
I didn’t want a dashboard. I wanted one glance, always visible.
02What it does
Usagent runs as a menu-bar accessory. It reads usage from each provider, normalizes it into a shared usage model, and renders a compact quota view in a popover - per-provider tabs, remaining percentages, reset times, and a freshness signal so you always know how much headroom you have left, and how confident you can be in what you're looking at.
03The product
agent usage monitor
Usage
Cursor account
individual · plan
your seat · individual usage
The tray title itself is the product: a tiny Cx 64 · Cu 38 in the menu bar updates as usage changes, so the answer is visible without even opening the popover.
04Architecture
Usagent is a Tauri app - a Rust core and a React renderer. All provider work, from discovery to fetch to normalization, happens on the native side; the renderer is a consumer of one normalized type.
05Provider abstraction
The core of the design is a single trait:
- UsageProvider: fetch one normalized ProviderUsage - provider, account label, observedAt, and a list of UsageLimit entries.
- Every provider is an adapter: discovery, source access, and protocol differences live entirely behind that trait.
- Codex and Cursor have nothing in common until they reach the trait - one reads local CLI state, the other calls a remote API - but the renderer never knows the difference.
- Adding a third provider means adding one module that satisfies the trait; nothing in the UI or cache changes.
06Native vs renderer boundary
- Rust owns discovery (finding the Codex CLI, reading the Cursor keychain item), the HTTP calls, and normalization.
- The renderer only receives ProviderUsage objects across the IPC boundary - a flat, serializable, normalized payload.
- Refresh-in-progress state, staleness, and errors are the renderer’s problems; raw responses and credentials are the native side’s problems - and never the other way around.
07Credentials & security
- Cursor’s session token is read from the macOS Keychain via the security CLI, passed directly into the provider, and never persisted by the app.
- Codex needs no stored credential at all - Usagent just discovers and spawns the local CLI it already respects.
- No token, no raw payload, and no account config ever crosses into JavaScript. The IPC contract is the security boundary: only usage numbers leave Rust.
08Refresh, polling & backoff
- Codex usage is cheap and local, so it polls in the background every 3 minutes - plus on popover open and manual refresh.
- Cursor usage hits a remote endpoint, so it is fetched on demand and throttled rather than polled; manual refresh always bypasses the throttle.
- Failed background refreshes back off exponentially, 3m → 30m cap, so a broken provider doesn’t hammer anything.
- Overlapping refreshes are skipped with an in-flight guard - no duplicate work, no stale response clobbering a fresh one.
- Stale snapshots stay visible and are explicitly aged ("synced 2m ago", "stale") instead of the UI going blank on a failure.
09macOS problems worth knowing about
- GUI vs Shell environments see different PATHs - a codex binary found in iTerm is not guaranteed visible to the menu-bar process; discovery walks PATH plus known install locations, with an env override.
- Tray popover focus - making the popover open reliably on click, and close when it loses focus, needed explicit handling rather than the default toggle.
- Live tray title - updating the tray title from async commands is a real integration detail, not a config flag.
- Keychain access from a signed app triggers permissions prompts; the token read is scoped and minimal by design.
10Extensibility
The provider trait plus the normalized model mean the app's growth is a set of localized additions: a new provider module, a discovery step, and a protocol implementation - with the shared model, cache, backoff, and renderer untouched. Individual usage, team pools, and metered limits already flow through the same schema, so hybrid plans compose instead of special-case.
11Links