feat(quota): add Cursor quota provider and fix null tasks encoding - #442
feat(quota): add Cursor quota provider and fix null tasks encoding#442davidfarah2003 wants to merge 1 commit into
Conversation
Add Cursor quota refresh through CPA api-call using the DashboardService endpoints Cursor's own dashboard uses: aggregated usage events for the per-model table plus plan/subscription state; period is required, plan and weekly agent quotas optional. Fix the Auth Files crash "e.tasks is not iterable": initialize Tasks and Rejected as empty slices so JSON encodes [] instead of null, and guard the frontend with response.tasks ?? [].
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7c1d4e264e
ℹ️ 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".
| } | ||
| return &CursorAgentPayload{ | ||
| UsagePercent: floatPtrField(object, "usagePercent", "usage_percent"), | ||
| HasNonZeroIncludedLimit: boolPtrField(object, "hasNonZeroIncludedLimit", "has_non_zero_included_limit"), |
There was a problem hiding this comment.
Parse Cursor's nonzero-limit field with its actual casing
Cursor's GetSandUsageStatus response uses hasNonzeroIncludedLimit (and the corresponding snake-case form is has_nonzero_included_limit), but this parser accepts only spellings that split “nonzero” into two words. As a result, an explicit false is parsed as nil, so cursorWeeklyQuotaRow cannot apply its suppression check and accounts without an included agent limit can be shown a misleading weekly quota row.
Useful? React with 👍 / 👎.
| agent = p.requestAgent(ctx, input) | ||
| }() | ||
| } | ||
| wg.Wait() |
There was a problem hiding this comment.
Bound optional Cursor requests independently
When either optional plan or agent endpoint hangs, this unconditional wait blocks the otherwise usable period result until the shared 20-second refresh-task context expires; it even delays returning an already-known period error. Because these calls are explicitly optional and their errors are discarded, they should use shorter child deadlines or be canceled once the required request completes, otherwise Cursor refreshes can occupy the shared worker pool for the full timeout and delay unrelated quota refreshes.
Useful? React with 👍 / 👎.
| BillingCycleStart: stringField(object, "billingCycleStart", "billing_cycle_start"), | ||
| BillingCycleEnd: stringField(object, "billingCycleEnd", "billing_cycle_end"), | ||
| } | ||
| if planUsage := objectField(object, "planUsage", "plan_usage"); planUsage != nil { |
There was a problem hiding this comment.
Preserve Cursor on-demand usage from the period response
Cursor's current-period response can include an onDemandUsage sibling containing its own spend, remaining amount, and cap, but this parser retains only planUsage. For accounts with pay-as-you-go enabled, normalization consequently marks the monthly plan as exhausted and sets Allowed to false as soon as included usage reaches its limit even when on-demand capacity remains, while also hiding that capacity entirely; the on-demand payload needs to be parsed and reflected in the billing rows or the monthly availability calculation.
Useful? React with 👍 / 👎.
Two changes:
Cursor quota provider. Adds Cursor to the quota refresh pipeline through the CPA management
api-callendpoint, using the sameaiserver.v1.DashboardServicecalls Cursor's own dashboard makes. The period read is required; plan info and weekly agent usage are optional enrichment. Includes provider registration, normalization, subscription state, and tests.e.tasks is not iterablecrash fix. The refresh endpoint encoded nil Go slices as"tasks": null, and the Auth Files frontend iteratedresponse.tasksdirectly. Both sides are fixed:Tasks/Rejectedinitialize as empty slices so JSON encodes[], and the frontend guards withresponse.tasks ?? [].Verification: Go quota tests and the full vitest suite (1011 tests) pass; the null-tasks fix was mutation-tested (reverting either half turns the suite red on the named assertions).