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

usagent popover
Cx 64 · Cu 38
u/

agent usage monitor

Usage

live
CU

Cursor account

individual · plan

synced 2m ago
Premium - this month38%

your seat · individual usage

Default usage38%
On-demand usage100%
Team pool71%
Data stays local to this Mac.
Replica of the menu-bar popover, reproducing the live UI - tones, progress bars, freshness, and the per-provider tabs.

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.

Menu-bar trayCx 64 · Cu 38Usagent - Tauri runtime (Rust)Tauri commandsget_codex_usage · get_cursor_usageUsageCacheMutex<HashMap<provider, Usage>>UsageProvider traitnormalize → ProviderUsageCredentials stay in RustKeychain · CLI · never to JSCodexProviderspawn CLI · local reads - poll 3mCursorProviderKeychain token → api2.cursor.sh - remoteReact renderermenu-bar popover · tabs · freshnessIPC - normalized usage only · no tokens · no raw payloadsupdate tray title
The provider trait is the seam: each provider discovers its source, fetches, and normalizes into ProviderUsage, and everything the renderer ever sees is that normalized model - credentials and raw payloads never cross the IPC boundary.

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