Fix enterprise/team Cursor usage via usage-summary fallback#832
Fix enterprise/team Cursor usage via usage-summary fallback#832andyli-star wants to merge 2 commits into
Conversation
Closes robinebers#829 When GetCurrentPeriodUsage returns unusable planUsage for enterprise or team accounts, fetch cursor.com/api/usage-summary (the same endpoint the dashboard uses) before falling back to request-based /api/usage. Maps pooled/on-demand team spend and individual overall usage as dollar meters, auto/API percents from display messages, and billing cycle dates. Co-authored-by: Cursor <cursoragent@cursor.com>
| private static func percentFromDisplayMessage(_ message: String?) -> Double? { | ||
| guard let message else { return nil } | ||
| let pattern = #"(\d+(?:\.\d+)?)\s*%"# | ||
| guard let regex = try? NSRegularExpression(pattern: pattern), | ||
| let match = regex.firstMatch(in: message, range: NSRange(message.startIndex..., in: message)), | ||
| let range = Range(match.range(at: 1), in: message) | ||
| else { | ||
| return nil | ||
| } | ||
| return Double(message[range]) | ||
| } |
There was a problem hiding this comment.
The
NSRegularExpression is compiled from scratch on every call to percentFromDisplayMessage. The pattern is static, so a one-time static let avoids repeated compilation.
| private static func percentFromDisplayMessage(_ message: String?) -> Double? { | |
| guard let message else { return nil } | |
| let pattern = #"(\d+(?:\.\d+)?)\s*%"# | |
| guard let regex = try? NSRegularExpression(pattern: pattern), | |
| let match = regex.firstMatch(in: message, range: NSRange(message.startIndex..., in: message)), | |
| let range = Range(match.range(at: 1), in: message) | |
| else { | |
| return nil | |
| } | |
| return Double(message[range]) | |
| } | |
| private static let percentRegex = try? NSRegularExpression(pattern: #"(\d+(?:\.\d+)?)\s*%"#) | |
| private static func percentFromDisplayMessage(_ message: String?) -> Double? { | |
| guard let message, | |
| let regex = percentRegex, | |
| let match = regex.firstMatch(in: message, range: NSRange(message.startIndex..., in: message)), | |
| let range = Range(match.range(at: 1), in: message) | |
| else { | |
| return nil | |
| } | |
| return Double(message[range]) | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: Sources/OpenUsage/Providers/Cursor/CursorUsageMapper.swift
Line: 311-321
Comment:
The `NSRegularExpression` is compiled from scratch on every call to `percentFromDisplayMessage`. The pattern is static, so a one-time `static let` avoids repeated compilation.
```suggestion
private static let percentRegex = try? NSRegularExpression(pattern: #"(\d+(?:\.\d+)?)\s*%"#)
private static func percentFromDisplayMessage(_ message: String?) -> Double? {
guard let message,
let regex = percentRegex,
let match = regex.firstMatch(in: message, range: NSRange(message.startIndex..., in: message)),
let range = Range(match.range(at: 1), in: message)
else {
return nil
}
return Double(message[range])
}
```
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| if CursorUsageMapper.shouldUseUsageSummaryFallback(usage: usage, planName: planName), | ||
| let mapped = try? await usageSummaryResult(accessToken: currentToken, planName: planName) { | ||
| return snapshot(mapped) | ||
| } |
There was a problem hiding this comment.
Silent suppression of connectivity errors from
usageSummaryResult
try? discards any error thrown by usageSummaryResult — including CursorUsageError.connectionFailed if cursor.com is reachable from the gRPC endpoint (api2.cursor.sh) but not from the REST endpoint. When that happens the provider silently falls through to requestBasedResult, which will also fail and ultimately surface "Enterprise usage data unavailable" rather than "Connection failed". This is consistent with the shouldTryGenericRequestFallback block above, but there the suppressed path is a speculative optimistic attempt; here it is the primary enterprise code path. Consider propagating at least connection-class errors instead of uniformly swallowing them.
Prompt To Fix With AI
This is a comment left during a code review.
Path: Sources/OpenUsage/Providers/Cursor/CursorProvider.swift
Line: 112-115
Comment:
**Silent suppression of connectivity errors from `usageSummaryResult`**
`try?` discards any error thrown by `usageSummaryResult` — including `CursorUsageError.connectionFailed` if `cursor.com` is reachable from the gRPC endpoint (`api2.cursor.sh`) but not from the REST endpoint. When that happens the provider silently falls through to `requestBasedResult`, which will also fail and ultimately surface "Enterprise usage data unavailable" rather than "Connection failed". This is consistent with the `shouldTryGenericRequestFallback` block above, but there the suppressed path is a speculative optimistic attempt; here it is the primary enterprise code path. Consider propagating at least connection-class errors instead of uniformly swallowing them.
How can I resolve this? If you propose a fix, please make it concise.|
Closing for now — still iterating on enterprise usage mapping (adding individual monthly usage). Will reopen when ready. |
Show individualUsage.overall as Total Usage (matching Cursor dashboard), expose teamUsage.pooled as a separate Team Pool metric, and document the mapping for enterprise usage-summary accounts. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Thanks for the PR! Closing for two reasons:
- No approved issue — OpenUsage reviews external PRs only when they implement an issue a maintainer has already approved with the
approvedlabel. This links #829, which is open but not labeledapproved. Please wait for approval, then reopen this (or open a focused PR) linking it withFixes #<issue>. - Missing screenshots — This changes user-visible behavior (usage meters and layout defaults), but the description has no images and no Screenshots section saying "Not applicable". Please add before/after screenshots when you resubmit.
Closing for now — happy to take a look once it's linked to an approved issue and includes screenshots.
Powered by Cursor Automations
Sent by Cursor Automation: OpenUsage PR Gatekeeper
|
Hi @andyli-star — thanks a lot for digging into this and for the PR, genuinely appreciated. 🙏🏻 I have to be upfront: we landed on the same approach in parallel and just opened #846, which covers the same core fix (usage-summary fallback for enterprise/team via the existing session-token cookie, pooled/on-demand mapped as dollar meters, request-based path as the safety net). So rather than merge both, I'd like to consolidate on #846 and close this one — but your enterprise account is exactly the verification we're missing, so your input would be very valuable there. A few notes on why I'm not taking this version as-is:
Two asks, if you're up for it:
Thanks again — this issue would still be open without your and the reporter's legwork. |


TL;DR
Enterprise and team Cursor accounts no longer fail with "Enterprise usage data unavailable" — OpenUsage now falls back to
cursor.com/api/usage-summarywhen dashboard usage is empty.Closes #829
What was happening
GetCurrentPeriodUsagereturns no usableplanUsagefor Enterprise/team (TOKEN_BASED_CONTRACT) accounts/api/usagefallback also returns nullmaxRequestUsageWhat this changes
GET cursor.com/api/usage-summaryclient method (same cookie auth as other REST calls)/api/usageteamUsage.pooledandteamUsage.onDemandas dollar progress meters (cents → dollars)individualUsage.overallwhen pooled team usage is absentbillingCycleStart/billingCycleEndfor reset countdownsdocs/providers/cursor.mdHeads-up
/api/usageremains the fallback for other account types.Tests
CursorUsageMapperTests.testMapsEnterpriseUsageSummaryCursorUsageMapperTests.testShouldUseUsageSummaryFallbackForEnterpriseCursorProviderTests.testRefreshFallsBackToUsageSummaryForEnterpriseTest plan
planUsage: usage-summary metrics appearteamUsage.onDemandhas a limitMade with Cursor
Confidence Score: 4/5
Safe to merge; the new code path only activates for enterprise/team accounts that already surfaced an error, and any failure falls back to the existing request-based path.
The implementation is consistent with existing codebase patterns and the tests verify both the mapper logic and the provider integration path. The one concern is that
try?onusageSummaryResultswallows all errors — including connectivity failures — before falling through torequestBasedResult, which can obscure the true cause of failure for enterprise users whencursor.comis unreachable. There are no data-loss or auth-boundary issues.The error-suppression in
CursorProvider.swiftlines 112-115 is worth a second look to decide whether connectivity errors should propagate instead of being silently absorbed into the subsequent fallback.Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "Fix enterprise/team Cursor usage via usa..." | Re-trigger Greptile