feat(cursor): add token cost report with Cursor-metered total#1745
feat(cursor): add token cost report with Cursor-metered total#1745EClinick wants to merge 16 commits into
Conversation
Cursor previously surfaced only status/quota, not per-model token cost. Add a cost report sourced from Cursor's cookie-authenticated dashboard API, reusing the existing Cursor session resolution. Core: - CursorUsageEventsFetcher pages get-filtered-usage-events, dedupes, and shapes events into an API-rate per-day/per-model report plus a Cursor-metered window total (sum of chargedCents). One fetch backs both numbers so they always cover the same window. - CursorStatusProbe.resolveSession generalizes the auth path so status and cost share one session-resolution flow; fetchCostReport runs on it. - CostUsageFetcher gains a macOS-only Cursor branch and a fetchAllHistory option (all-time window plus "All time" label when set). - Enable Cursor tokenCost capability and add a Cursor cost-estimate hint. CLI: - codexbar cost --provider cursor with --days and a new --all flag. - Text adds a "Cursor-metered" window line; JSON adds meteredCostUSD. App: - Menu cost card shows the Cursor-metered line; Preferences gains a global "Fetch full Cursor cost history" toggle wired through the settings store and the UsageStore refresh scope. Tests: - CursorUsageEventsFetcher pagination/dedupe/metered-cents and daily report mapping; CursorStatusProbe cost-report path.
|
Codex review: needs real behavior proof before merge. Reviewed July 1, 2026, 4:27 AM ET / 08:27 UTC. Summary Reproducibility: yes. for the review blockers from source inspection: PR head keys Auto-mode cost scope only by Review metrics: 3 noteworthy metrics.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Mantis proof suggestion Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Land a documented, explicitly accepted Cursor dashboard cost source after fixing Auto-mode cache identity, refreshing redacted packaged menu/CLI proof, and resolving the owner’s auth/privacy decision and provider-menu overlap with #1736. Do we have a high-confidence way to reproduce the issue? Yes for the review blockers from source inspection: PR head keys Auto-mode cost scope only by Is this the best way to solve the issue? No, not yet. The implementation is close and has focused tests, but the best merge path needs owner auth/privacy acceptance, resolved Cursor identity in the cost cache scope, user-facing docs, and refreshed final UI/CLI proof. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 46b493fc45bd. Label changesLabel changes:
Label justifications:
Evidence reviewedSecurity concerns:
Acceptance criteria:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2209a9b639
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… compiling Adding meteredLine to TokenUsageSection without a default made the synthesized memberwise initializer require it at every call site, breaking existing callers and tests that predate the Cursor-metered line. Add an explicit initializer that defaults meteredLine to nil so those call sites keep compiling.
The Cursor cost fetch always auto-resolved cookies, ignoring the cookie-source setting the status path respects. Thread a cursorCookieHeaderOverride from UsageStore through CostUsageFetcher into the Cursor cost report: forward the manual header when the source is Manual, skip the fetch entirely when it is Off, and fall back to auto resolution otherwise. Include the cookie source in the cost scope signature so toggling re-fetches.
…ping totalUsageEventsCount used a strict Int decode, so a string-encoded count (the API serializes some numbers as strings) became nil and could short-circuit pagination. Route it through the lenient CursorEventNumber decoder like every other numeric field. Also fix the paginate/dedupe test whose third event timestamp crossed into the next UTC day: it expected one day-entry while the correct grouping produced two. Move it onto the same UTC day so it exercises single-day grouping and dedup as intended.
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
- Add Cursor to the inline cost-dashboard allowlist and key the Preferences cost-status line off supportsTokenCost so the Cursor card shows cost like Claude/Codex. - Drop the cursor-specific all-time history feature (the "Fetch full Cursor cost history" toggle, its settings plumbing, the CLI --all flag, the "All time" label, and the history-span sizing). Cursor now uses the shared "History window: N days" setting, so the inline chart draws legible windowed bars instead of the full account history. - Keep the Cursor-metered total, now scoped to the selected window.
|
@openclaw-mantis visual task: verify CodexBar shows Cursor token cost with a Cursor-metered line and dashboard-source copy using redacted Cursor account data. |
loadCursorTokenSnapshot now derives the session value from the current local day instead of the latest history entry, so a stale day is never labeled "Today" in the menu or CLI. This matches the Codex/Claude cost window behavior. Adds a focused test covering the stale-latest case.
tokenUsageHint had no Cursor case, so the shared menu/inline cost card fell back to the generic local-logs estimate copy for dashboard-derived Cursor data. Return the Cursor dashboard hint instead.
CLI `cost --provider cursor` now resolves the configured Cursor cookie source the same way the usage path does: it skips the fetch (with a notice) when cookies are Off and forwards the Manual header so the dashboard request uses the configured session instead of auto-resolving a different one. Also gates Cursor in costSupportedProviders to macOS, since supportsTokenSnapshot(.cursor) is macOS-only and the CLI otherwise advertised Cursor cost on platforms where it can only fail.
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8632505417
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@clawsweeper re-review |
Extracts the window-start normalization into CostUsageFetcher.cursorWindowStart and adds a focused regression test, including the historyDays == 1 case where `since` equals now and the window must still cover all of today rather than an empty exact-instant range.
|
@clawsweeper rereview |
|
🦞🧹 I asked ClawSweeper to review this item again. |
Pull the Cursor cookie-policy logic out of runCost into reusable type-level helpers so the serve /cost route can apply the same rules: - costSupportedProviderNames(): one source of truth for the supported provider list used in user-facing messages - cursorCostShouldSkip(_:settings:): true when the cookie source is Off - cursorCostHeaderOverride(_:settings:): normalized Manual header or nil - cursorCookieSettings(...) is now internal and documented as shared No behavior change for the cost command; this only reshapes the code so the serve route can reuse it without duplication.
The local serve /cost route fetched Cursor cost unconditionally, so it ignored the configured cookie source. Apply the shared gating helpers: - skip Cursor when the cookie source is Off - forward the Manual cookie header so served data matches the session - report supported providers dynamically via costSupportedProviderNames() This closes the merge-gate finding that serve /cost bypassed the same policy already enforced by the cost command.
|
Pushed a fix for the [P1] serve
To avoid duplication, the policy was extracted into shared helpers ( Flagging the other two P1s for maintainers:
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
😎 |
|
@steipete 🐐 how's this looking? |
Resolve conflicts in two files by combining both intents: - InlineUsageDashboardContent: keep Cursor in the cost-history provider list and adopt main's more specific tokenCostInlineDashboardEnabled gate. - PreferencesGeneralPane: keep main's cost-summary display-style picker and padded VStack wrapper, and add the Cursor cost status line. Keep `make check` green after the merge: - DRY UsageStore.refreshTokenUsage via a resetTokenState(for:resetFetchMarkers:) helper so the body stays under the function_body_length limit. - Scope line_length disables to the Cursor usage-event JSON fixtures, matching the existing MistralUsageParserTests convention.
|
Maintainer review against current The data path is substantially stronger than the older cost proposals: it reuses Cursor auth resolution, paginates and deduplicates events, preserves local-day “Today” semantics, and keeps API-rate estimates separate from Cursor-metered totals. Focused tests are green: I am holding the merge for the remaining product/auth decision and documentation gap. This adds an authenticated remote dashboard source to the app, CLI, and server, but Recommendation: approve as an opt-in Cursor dashboard cost source once the auth/privacy choice is explicit and docs/proof match the final UI. No merge from this review. |
|
Maintainer follow-up: pushed The fix closes the concrete CLI/server contract gap found during review: when Cursor cost is requested while the Cursor cookie source is Off, Exact-head proof:
Recommendation remains: keep this open rather than merge yet. The implementation is strong, but enabling authenticated Cursor dashboard access still needs the product/privacy decision, user-facing docs ( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 43c87553e1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } else if let override = cursorCookieHeaderOverride { | ||
| "|cursorCookie=manual:\(override.hashValue)" | ||
| } else { | ||
| "|cursorCookie=\(cursorCookieSource.rawValue)" |
There was a problem hiding this comment.
Include Cursor auto session identity in the cost scope
When Cursor is in Auto mode this scope records only cursorCookie=auto, so a browser/cached session change to a different Cursor account does not invalidate the cost TTL. If the old cookie is rejected and status refresh imports a new account, the cost refresh below can return early with the previous account's token-cost snapshot until the TTL expires or the user forces a refresh; include an account/session fingerprint for auto resolution or clear these markers when the Cursor identity changes.
Useful? React with 👍 / 👎.
Owner decision — product/privacy hold
Decision requested: Should CodexBar fetch detailed Cursor usage-event history through the existing cookie-authenticated dashboard session to calculate per-day/per-model cost estimates, or should Cursor remain limited to its summary usage/status data?
Tradeoff: The report makes otherwise hidden API-rate and Cursor-metered totals useful in the app and CLI. It also expands an authenticated dashboard integration into detailed event-history collection, depends on a private endpoint contract, and presents estimates that users may read as billing truth.
Recommendation: Do not land the current behavior yet. Prefer an explicit opt-in plus approved privacy/source wording before enabling this network-backed history fetch. If the owner accepts the existing-auth/no-new-setting model, the implementation below is prepared and exact-head CI is the remaining mechanical gate.
This PR is intentionally held for that owner decision; green CI does not make it merge-ready.
Summary
Adds a Cursor token-cost report to CodexBar, sourced from Cursor's cookie-authenticated dashboard API and reusing the existing Cursor session resolution (the same auth path as the status probe). Each usage event carries both a vendor list-price token cost (
tokenUsage.totalCents) and the amount Cursor's plan actually deducts (chargedCents), so a single fetch yields both an API-rate per-day/per-model breakdown and a "Cursor-metered" window total covering the exact same window.Core
CursorUsageEventsFetcher: pagesPOST /api/dashboard/get-filtered-usage-events, dedupes events on their natural key, and shapes them into aCostUsageDailyReport(API-rate) plus a metered window total (sum ofchargedCents). Lenient numeric decoding handles Cursor serializing some numbers as strings, and the CSRF-protected POST sends the requiredOriginheader.CursorStatusProbe: generalizedresolveSessionso status and cost share one session-resolution flow (manual cookie, cached cookie, browser cookies, stored session, Cursor.app fallback); addedfetchCostReporton top of it.CostUsageFetcher: macOS-only Cursor branch over a rollinghistoryDayswindow (like Codex/Claude), with the session line tied to the current local day.tokenCostcapability and added a Cursor-specific cost-estimate hint.CLI
codexbar cost --provider cursor, honoring--days(1…365).Cursor-metered: $X (window)line; JSON addsmeteredCostUSD. The unsupported-provider error lists supported providers dynamically.App (UI)
Cursor-meteredline alongside the API-rate estimate, the Cursor dashboard hint, and an inline windowed cost chart consistent with other providers.Live proof —
codexbar cost --provider cursorsource: "web"confirms the live dashboard fetch; the Cursor-metered total and the per-model breakdown are both present. (Real token counts/amounts, lightly trimmed.)Tests
CursorUsageEventsFetcherTests: pagination/dedupe, metered-cents summing (including nil-vs-zero), per-day/per-model mapping, and current-local-day session selection.CursorStatusProbeTests: cost-report path over the shared session resolution.Commands run
swift build --product CodexBarCLI— clean.swift test --filter CursorUsageEventsFetcherTests— 10/10 passing (full Xcode toolchain).make start— full app build, sign, and launch clean.Screenshots
Notes