Skip to content

feat: cursor usage meter#68

Open
Rtas-17 wants to merge 2 commits into
CodeZeno:mainfrom
Rtas-17:feat/cursor-usage-meter-upstream
Open

feat: cursor usage meter#68
Rtas-17 wants to merge 2 commits into
CodeZeno:mainfrom
Rtas-17:feat/cursor-usage-meter-upstream

Conversation

@Rtas-17

@Rtas-17 Rtas-17 commented Jul 25, 2026

Copy link
Copy Markdown

Summary

Adds a Cursor usage meter next to Claude Code, Codex, and Antigravity.

Cursor already keeps the signed-in JWT in its local state.vscdb, and cursor.com/api/usage-summary returns JSON with Auto / API percentages for the current billing cycle. So this is a straight SQLite read + HTTP call — no HTML scraping, no separate login helper.

How auth works

  1. Read cursorAuth/accessToken from %APPDATA%\Cursor\User\globalStorage\state.vscdb (read-only; copies to a temp file if Cursor has the DB locked).
  2. Decode the JWT sub, take the id after the last |, and build WorkosCursorSessionToken=<id>%3A%3A<jwt>.
  3. Optional override: set CURSOR_SESSION_TOKEN to the raw cookie value or JWT if the IDE DB isn't available.

If there's no token the Cursor bar shows ! until you sign in to Cursor (or set the env var).

What this PR adds

  • src/models.rs: optional cursor field on AppUsageData
  • src/poller.rs: poll_cursor() / cookie helpers / fetch_cursor_usage() mapping autoPercentUsed → session bar and apiPercentUsed → weekly bar, with billingCycleEnd as the shared reset. Unit test for the mapping included.
  • src/window.rs: widget bar, Models → Cursor toggle, tray tooltip uses Auto/API wording. When Cursor is the only model enabled, row labels switch from 5h/7d to Auto/API — Cursor has a monthly pool, not a rolling 5-hour window.
  • src/localization/*: cursor_model, cursor_window_title, cursor_auto_window, cursor_api_window
  • Cargo.toml: rusqlite with the bundled feature so we can read the state DB without a system SQLite install

Test plan

  • cargo test passes (verified locally, including cursor_usage_maps_auto_and_api_percentages)
  • cargo build --release clean
  • Live: Models → Cursor shows/hides the bar and persists across restarts
  • Live: with Cursor signed in, Auto/API percentages match https://cursor.com/dashboard
  • Live: Cursor-only mode shows Auto/API labels instead of 5h/7d; mixed with Claude keeps 5h/7d

I checked this on Windows 11 against a Pro account — usage-summary returned Auto ~1%, API 0%, reset at billingCycleEnd.

Design notes

  • Cursor's plan is monthly included usage (Auto + API). There is no Claude-style 5-hour window in the API response, so both bars share billingCycleEnd.
  • Token is read from disk on each poll and never stored by this app. Requests go only to cursor.com over HTTPS.
  • Default build gains rusqlite (bundled). No webview / wry / tao.

What's not in this PR

Risks

  • Cursor changes the usage-summary shape — mitigated by optional fields + a unit test on the known response. Failures show ! rather than fake zeros.
  • state.vscdb locked while Cursor is open — we open read-only and fall back to a temp copy if needed.

Happy to tweak the Auto/API labeling or split anything out if you'd rather review differently.

Adds a Cursor usage meter alongside Claude Code, Codex, and Antigravity.
Cursor stores the signed-in JWT in its local state.vscdb and exposes a JSON
usage summary at cursor.com/api/usage-summary - no HTML scraping needed.

- src/models.rs: new optional 'cursor' field on AppUsageData
- src/poller.rs: read cursorAuth/accessToken from state.vscdb via rusqlite
  (bundled), with CURSOR_SESSION_TOKEN env fallback, build the
  WorkosCursorSessionToken cookie (user id decoded from the JWT sub claim
  joined with the token via ::), and map autoPercentUsed / apiPercentUsed
  onto the two bars. billingCycleEnd is the shared reset for both. Includes
  a unit test for the mapping (cursor_usage_maps_auto_and_api_percentages).
- src/window.rs: new bar in the widget, Models -> Cursor toggle, tray
  tooltip uses "Auto: ... | API: ..." wording. When Cursor is the only
  model shown, the row labels switch from 5h/7d to Auto/API since Cursor
  has no rolling 5-hour window. Accent is near-black, inverted for dark
  taskbars.
- src/localization/*: new cursor_model, cursor_window_title,
  cursor_auto_window, cursor_api_window strings across all locales
- Cargo.toml: rusqlite (bundled) to read the Cursor IDE state DB

Verified locally: cargo test --bin claude-code-usage-monitor passes
6/6 (including the new Cursor mapping test), release build unaffected.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copilot AI review requested due to automatic review settings July 25, 2026 20:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “Cursor” provider to the usage monitor, including polling Cursor’s usage-summary API using a session token sourced from the Cursor IDE’s local state DB (or an env var override), plus UI/toggle/localization updates to render the new meter.

Changes:

  • Extend the core usage model to include optional Cursor usage data.
  • Implement Cursor polling: read session token (env var or state.vscdb), call https://cursor.com/api/usage-summary, and map Auto/API percentages into the existing session/weekly bars.
  • Update the Windows UI/tray + settings + localization to add a Cursor toggle and display Auto/API labeling when Cursor is the only active model.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/window.rs Adds Cursor state fields, toggle plumbing, tray tooltip/icon entries, and bar rendering (including Cursor-only Auto/API labels).
src/poller.rs Adds Cursor poller: token resolution from env/SQLite, usage-summary HTTP fetch, and response mapping + unit test.
src/models.rs Adds AppUsageData.cursor: Option<UsageData> for Cursor usage.
src/localization/mod.rs Extends Strings with Cursor labels/titles (model name + Auto/API window labels + window title).
src/localization/english.rs Adds Cursor strings in English.
src/localization/dutch.rs Adds Cursor strings in Dutch.
src/localization/spanish.rs Adds Cursor strings in Spanish.
src/localization/french.rs Adds Cursor strings in French.
src/localization/german.rs Adds Cursor strings in German.
src/localization/japanese.rs Adds Cursor strings in Japanese.
src/localization/korean.rs Adds Cursor strings in Korean.
src/localization/traditional_chinese.rs Adds Cursor strings in Traditional Chinese.
src/localization/simplified_chinese.rs Adds Cursor strings in Simplified Chinese.
src/localization/russian.rs Adds Cursor strings in Russian.
src/localization/portuguese_brazil.rs Adds Cursor strings in Portuguese (Brazil).
Cargo.toml Adds rusqlite (bundled) dependency for reading Cursor’s local state DB.
Cargo.lock Locks new dependency graph for rusqlite/sqlite.
Comments suppressed due to low confidence (1)

src/window.rs:500

  • In the loading/error tray-icon state, Cursor is also assigned TrayIconKind::Codex, so hovering is the only way to distinguish it from Codex. If Cursor is meant to have its own tray indicator, it should use a dedicated icon kind.
            if s.show_cursor {
                icons.push(tray_icon::TrayIconData {
                    kind: tray_icon::TrayIconKind::Codex,
                    percent: None,
                    tooltip: s.language.strings().cursor_window_title.to_string(),
                });

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/window.rs
Comment thread src/window.rs
- Give Cursor its own TrayIconKind so it no longer shares Codex's tray
  slot/styling when both are enabled.
- Use localized cursor_auto_window / cursor_api_window strings in the
  Cursor tray tooltip instead of hard-coded English.
- Also wire Cursor-only auth balloon copy to the Cursor tray icon
  (same class of bug as the Ollama review).

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants