Add Grok Bot Support to Cursor Card (#3127)

* feat(cursor): show Grok Bot weekly included usage

Grok Bot is billed on the Cursor session, so surface it as a fourth
Cursor card bar instead of a separate provider.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): keep stalled Grok Bot fetch from failing login

Cap the best-effort Sand request at 5s and do not fail Cursor login
after usage-summary has already succeeded.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
Kyle Varga
2026-08-21 19:39:25 -04:00
committed by GitHub
parent 5c71095247
commit c87c35becb
11 changed files with 410 additions and 8 deletions
+1
View File
@@ -2,6 +2,7 @@
## 0.54.1 — Unreleased
- Cursor: show Grok Bot weekly included usage as a fourth card bar from `get-sand-usage-status`, using the same session as Total / Cursor / Third Party.
- Codex: persist the priority-turn scan cursor across relaunches, so the first refresh after a restart resumes incrementally instead of re-scanning the whole trace database (~2.5s CPU saved per relaunch, minutes on a cold page cache) (#3130). Thanks @olddonkey!
- Spend dashboard: load provider baselines and Codex multi-account scans in parallel and memoize currency conversion and calendar buckets, cutting cold opens from multiple seconds to roughly the slowest single provider (#3105). Thanks @Yuxin-Qiao!
@@ -476,6 +476,20 @@ extension UsageStore {
title: metadata?.opusLabel ?? "Opus",
percentLeft: snapshot.tertiary?.remainingPercent))
}
// Provider-specific by design: Cursor Grok Bot weekly included usage is a named extraRateWindow.
if provider == .cursor {
rows.append(contentsOf: (snapshot.extraRateWindows ?? []).compactMap { namedWindow in
guard namedWindow.id == CursorSandUsageStatus.extraWindowID, namedWindow.usageKnown else {
return nil
}
return WidgetSnapshot.WidgetUsageRowSnapshot(
id: namedWindow.id,
title: namedWindow.title,
percentLeft: namedWindow.window.remainingPercent,
window: namedWindow.window)
})
}
if provider == .claude, self.settings.claudeModelScopedWeeklyUsageVisible {
// Claude fetchers place model-scoped weekly quotas (for example, Fable) in extraRateWindows.
// Keep the widget projection generic so newly surfaced Claude model quotas appear without UI changes.
@@ -76,6 +76,9 @@ public enum CursorProviderDescriptor {
"invoice."),
pace: ProviderPaceCapability(resetWindowPace: .windowDurationPresent),
presentation: ProviderUsagePresentation(
extraRateWindowSelector: { snapshot in
(snapshot.extraRateWindows ?? []).filter { $0.id == CursorSandUsageStatus.extraWindowID }
},
requestedMenuBarLaneOrders: [
.tertiary: [.tertiary, .secondary, .primary],
],
@@ -116,7 +119,10 @@ public enum CursorProviderDescriptor {
{
guard context.metric == .automatic else { return .unhandled }
let total = context.snapshot.primary
let subquotas = [context.snapshot.secondary, context.snapshot.tertiary].compactMap(\.self)
let grokBot = context.snapshot.extraRateWindows?.first {
$0.id == CursorSandUsageStatus.extraWindowID && $0.usageKnown
}?.window
let subquotas = [context.snapshot.secondary, context.snapshot.tertiary, grokBot].compactMap(\.self)
let usableSubquotas = subquotas.filter { $0.remainingPercent > 0 }
if let total, total.remainingPercent <= 0 {
return .resolved(total)
@@ -0,0 +1,61 @@
import Foundation
/// Grok Bot (internally "Sand") weekly included usage from Cursor's dashboard.
///
/// `POST /api/dashboard/get-sand-usage-status` with the same session cookie as
/// `/api/usage-summary`. Missing or failed responses must not fail Cursor usage.
public struct CursorSandUsageStatus: Decodable, Sendable, Equatable {
public static let extraWindowID = "cursor-grok-bot"
public static let extraWindowTitle = "Grok Bot"
public static let endpointPath = "/api/dashboard/get-sand-usage-status"
public let currentPeriodStart: String?
public let nextResetTimestampUtc: String?
public let usagePercent: Double?
public let hasAvailableUsage: Bool?
public let hasNonZeroIncludedLimit: Bool?
public init(
currentPeriodStart: String?,
nextResetTimestampUtc: String?,
usagePercent: Double?,
hasAvailableUsage: Bool?,
hasNonZeroIncludedLimit: Bool?)
{
self.currentPeriodStart = currentPeriodStart
self.nextResetTimestampUtc = nextResetTimestampUtc
self.usagePercent = usagePercent
self.hasAvailableUsage = hasAvailableUsage
self.hasNonZeroIncludedLimit = hasNonZeroIncludedLimit
}
/// Weekly Grok Bot bar, or `nil` when the account has no included Bot allowance.
public func extraRateWindow(resetDescription: (Date) -> String) -> NamedRateWindow? {
guard self.hasNonZeroIncludedLimit == true, let usagePercent = self.usagePercent else {
return nil
}
let start = Self.parseISO8601(self.currentPeriodStart)
let resetsAt = Self.parseISO8601(self.nextResetTimestampUtc)
return NamedRateWindow(
id: Self.extraWindowID,
title: Self.extraWindowTitle,
window: RateWindow(
usedPercent: UsagePercent(raw: usagePercent).displayClamped,
windowMinutes: Self.windowMinutes(start: start, end: resetsAt),
resetsAt: resetsAt,
resetDescription: resetsAt.map(resetDescription)))
}
static func parseISO8601(_ raw: String?) -> Date? {
guard let raw else { return nil }
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return formatter.date(from: raw) ?? ISO8601DateFormatter().date(from: raw)
}
static func windowMinutes(start: Date?, end: Date?) -> Int? {
guard let start, let end else { return nil }
let minutes = Int((end.timeIntervalSince(start) / 60).rounded())
return minutes > 0 ? minutes : nil
}
}
@@ -411,6 +411,8 @@ public struct CursorStatusSnapshot: Sendable {
public let accountName: String?
/// Raw API response for debugging
public let rawJSON: String?
/// Grok Bot weekly included usage from `/api/dashboard/get-sand-usage-status`.
public let sandUsage: CursorSandUsageStatus?
// MARK: - Legacy Plan (Request-Based) Fields
@@ -441,6 +443,7 @@ public struct CursorStatusSnapshot: Sendable {
accountID: String? = nil,
accountName: String?,
rawJSON: String?,
sandUsage: CursorSandUsageStatus? = nil,
requestsUsed: Int? = nil,
requestsLimit: Int? = nil)
{
@@ -460,6 +463,7 @@ public struct CursorStatusSnapshot: Sendable {
self.accountID = accountID
self.accountName = accountName
self.rawJSON = rawJSON
self.sandUsage = sandUsage
self.requestsUsed = requestsUsed
self.requestsLimit = requestsLimit
}
@@ -509,6 +513,17 @@ public struct CursorStatusSnapshot: Sendable {
resetDescription: self.billingCycleEnd.map { Self.formatResetDate($0) })
}
// Grok Bot is a weekly included allowance on the same Cursor account, not the monthly
// Total/Cursor/Third Party bars. Hide it on legacy request plans so it cannot sit next
// to a request quota that does not share that token-based breakdown.
let extraRateWindows: [NamedRateWindow]? = if cursorRequests != nil {
nil
} else {
self.sandUsage.flatMap { status in
status.extraRateWindow(resetDescription: Self.formatResetDate)
}.map { [$0] }
}
// Prefer a personal cap. Team accounts with no user cap expose only the shared on-demand budget.
let resolvedOnDemandUsed: Double
let resolvedOnDemandLimit: Double?
@@ -558,6 +573,7 @@ public struct CursorStatusSnapshot: Sendable {
primary: primary,
secondary: secondary,
tertiary: tertiary,
extraRateWindows: extraRateWindows,
providerCost: providerCost,
details: cursorRequests.map { requests in
[.makeSection(title: "Usage", rows: [
@@ -1344,12 +1360,15 @@ public struct CursorStatusProbe: Sendable {
enum FetchPart: Sendable {
case usageSummary((CursorUsageSummary, String))
case userInfo(Result<CursorUserInfo, Error>)
case sandUsage(Result<(CursorSandUsageStatus, String), Error>)
}
try Self.checkBrowserLoginDeadline(deadline)
var usageSummaryResult: (CursorUsageSummary, String)?
var userInfo: CursorUserInfo?
var sandUsage: CursorSandUsageStatus?
var sandUsageRawJSON: String?
try await withThrowingTaskGroup(of: FetchPart.self) { group in
group.addTask {
@@ -1364,6 +1383,15 @@ public struct CursorStatusProbe: Sendable {
return .userInfo(.failure(error))
}
}
group.addTask {
do {
return try await .sandUsage(.success(self.fetchSandUsage(
cookieHeader: cookieHeader,
deadline: deadline)))
} catch {
return .sandUsage(.failure(error))
}
}
while let result = try await group.next() {
switch result {
@@ -1371,12 +1399,22 @@ public struct CursorStatusProbe: Sendable {
usageSummaryResult = value
case let .userInfo(value):
userInfo = try? value.get()
case let .sandUsage(value):
if let (status, rawJSON) = try? value.get() {
sandUsage = status
sandUsageRawJSON = rawJSON
}
}
// Required usage-summary is enough to finish login. Cancel leftover optional
// work if the interactive deadline has already elapsed.
if usageSummaryResult != nil, let deadline, deadline.timeIntervalSinceNow <= 0 {
group.cancelAll()
}
}
}
try Self.checkBrowserLoginDeadline(deadline)
guard let usageSummaryResult else {
try Self.checkBrowserLoginDeadline(deadline)
throw CursorStatusProbeError.networkError("Cursor usage summary fetch did not complete")
}
@@ -1404,12 +1442,17 @@ public struct CursorStatusProbe: Sendable {
if let usageJSON = requestUsageRawJSON {
combinedRawJSON = (combinedRawJSON ?? "") + "\n\n--- /api/usage response ---\n" + usageJSON
}
if let sandJSON = sandUsageRawJSON {
combinedRawJSON = (combinedRawJSON ?? "") + "\n\n--- /api/dashboard/get-sand-usage-status ---\n"
+ sandJSON
}
return self.parseUsageSummary(
usageSummary,
userInfo: userInfo,
rawJSON: combinedRawJSON,
requestUsage: requestUsage,
sandUsage: sandUsage,
identityFallback: identityFallback)
}
@@ -1449,6 +1492,58 @@ public struct CursorStatusProbe: Sendable {
}
}
private func fetchSandUsage(
cookieHeader: String,
deadline: Date?) async throws -> (CursorSandUsageStatus, String)
{
let url = self.baseURL.appendingPathComponent(CursorSandUsageStatus.endpointPath)
var request = URLRequest(url: url)
request.httpMethod = "POST"
// Best-effort: cap wait so a stalled Sand endpoint cannot consume the login deadline.
guard let sandTimeout = self.optionalRequestTimeout(
deadline: deadline,
budget: Self.sandUsageTimeout)
else {
throw CursorStatusProbeError.networkError("Sand usage skipped after login deadline")
}
request.timeoutInterval = sandTimeout
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(self.originHeader, forHTTPHeaderField: "Origin")
request.setValue(cookieHeader, forHTTPHeaderField: "Cookie")
request.httpBody = Data("{}".utf8)
let (data, response) = try await self.urlSession.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw CursorStatusProbeError.networkError("Invalid response")
}
if httpResponse.statusCode == 401 || httpResponse.statusCode == 403 {
throw CursorStatusProbeError.notLoggedIn
}
guard httpResponse.statusCode == 200 else {
throw CursorStatusProbeError.networkError("HTTP \(httpResponse.statusCode)")
}
let rawJSON = String(data: data, encoding: .utf8) ?? "<binary>"
do {
let status = try JSONDecoder().decode(CursorSandUsageStatus.self, from: data)
return (status, rawJSON)
} catch {
throw CursorStatusProbeError
.parseFailed("Sand usage decode failed: \(error.localizedDescription). Raw: \(rawJSON.prefix(200))")
}
}
private var originHeader: String {
guard let scheme = self.baseURL.scheme, let host = self.baseURL.host else {
return "https://cursor.com"
}
return "\(scheme)://\(host)"
}
private func fetchUserInfo(cookieHeader: String, deadline: Date?) async throws -> CursorUserInfo {
let url = self.baseURL.appendingPathComponent("/api/auth/me")
var request = URLRequest(url: url)
@@ -1490,6 +1585,8 @@ public struct CursorStatusProbe: Sendable {
return (usage, rawJSON)
}
private static let sandUsageTimeout: TimeInterval = 5
private func requestTimeout(deadline: Date?) throws -> TimeInterval {
guard let deadline else { return self.timeout }
let remainingTime = deadline.timeIntervalSinceNow
@@ -1497,6 +1594,14 @@ public struct CursorStatusProbe: Sendable {
return min(self.timeout, remainingTime)
}
private func optionalRequestTimeout(deadline: Date?, budget: TimeInterval) -> TimeInterval? {
let capped = min(self.timeout, budget)
guard let deadline else { return capped }
let remainingTime = deadline.timeIntervalSinceNow
guard remainingTime > 0 else { return nil }
return min(capped, remainingTime)
}
private static func checkBrowserLoginDeadline(_ deadline: Date?) throws {
guard let deadline else { return }
guard deadline.timeIntervalSinceNow > 0 else { throw self.browserLoginTimeoutError() }
@@ -1511,6 +1616,7 @@ public struct CursorStatusProbe: Sendable {
userInfo: CursorUserInfo?,
rawJSON: String?,
requestUsage: CursorUsageResponse? = nil,
sandUsage: CursorSandUsageStatus? = nil,
identityFallback: CursorSessionIdentity? = nil) -> CursorStatusSnapshot
{
func parseBillingCycleDate(_ dateString: String?) -> Date? {
@@ -1617,6 +1723,7 @@ public struct CursorStatusProbe: Sendable {
accountID: userInfo?.sub ?? identityFallback?.subject,
accountName: userInfo?.name,
rawJSON: rawJSON,
sandUsage: sandUsage,
requestsUsed: requestsUsed,
requestsLimit: requestsLimit)
}
@@ -238,4 +238,51 @@ struct CursorMenuCardModelTests {
#expect(model.metrics.map(\.title) == ["Requests"])
#expect(model.metrics.first?.detailText == "Request quota: 347 / 500")
}
@Test
func `grok bot extra window renders after monthly bars`() throws {
let now = Date(timeIntervalSince1970: 0)
let monthlyReset = now.addingTimeInterval(26 * 24 * 3600)
let weeklyReset = now.addingTimeInterval(3 * 24 * 3600)
let snapshot = UsageSnapshot(
primary: RateWindow(usedPercent: 1, windowMinutes: 43200, resetsAt: monthlyReset, resetDescription: nil),
secondary: RateWindow(usedPercent: 1, windowMinutes: 43200, resetsAt: monthlyReset, resetDescription: nil),
tertiary: RateWindow(usedPercent: 0, windowMinutes: 43200, resetsAt: monthlyReset, resetDescription: nil),
extraRateWindows: [
NamedRateWindow(
id: CursorSandUsageStatus.extraWindowID,
title: CursorSandUsageStatus.extraWindowTitle,
window: RateWindow(
usedPercent: 100,
windowMinutes: 10080,
resetsAt: weeklyReset,
resetDescription: nil)),
],
updatedAt: now,
identity: nil)
let metadata = try #require(ProviderDefaults.metadata[.cursor])
let model = UsageMenuCardView.Model.make(.init(
provider: .cursor,
metadata: metadata,
snapshot: snapshot,
credits: nil,
creditsError: nil,
dashboard: nil,
dashboardError: nil,
tokenSnapshot: nil,
tokenError: nil,
account: AccountInfo(email: nil, plan: nil),
isRefreshing: false,
lastError: nil,
usageBarsShowUsed: false,
resetTimeDisplayStyle: .countdown,
tokenCostUsageEnabled: false,
showOptionalCreditsAndExtraUsage: true,
hidePersonalInfo: false,
now: now))
#expect(model.metrics.map(\.title) == ["Total", "Cursor", "Third Party", "Grok Bot"])
#expect(model.metrics.last?.percentLabel == "0% left")
}
}
@@ -0,0 +1,140 @@
import Foundation
import Testing
@testable import CodexBarCore
@Suite(.serialized)
struct CursorSandUsageTests {
@Test
func `parses sand usage status`() throws {
let json = """
{
"currentPeriodStart": "2026-08-17T07:57:50.647Z",
"nextResetTimestampUtc": "2026-08-24T07:57:50.647Z",
"usagePercent": 100,
"hasAvailableUsage": true,
"hasNonZeroIncludedLimit": true
}
"""
let data = try #require(json.data(using: .utf8))
let status = try JSONDecoder().decode(CursorSandUsageStatus.self, from: data)
#expect(status.usagePercent == 100)
#expect(status.hasAvailableUsage == true)
#expect(status.hasNonZeroIncludedLimit == true)
let window = try #require(status.extraRateWindow(resetDescription: { _ in "Resets" }))
#expect(window.id == CursorSandUsageStatus.extraWindowID)
#expect(window.title == "Grok Bot")
#expect(window.window.usedPercent == 100)
#expect(window.window.windowMinutes == 10080)
#expect(window.window.resetsAt != nil)
}
@Test
func `hides grok bot extra window without an included limit`() {
let status = CursorSandUsageStatus(
currentPeriodStart: "2026-08-17T07:57:50.647Z",
nextResetTimestampUtc: "2026-08-24T07:57:50.647Z",
usagePercent: 100,
hasAvailableUsage: false,
hasNonZeroIncludedLimit: false)
#expect(status.extraRateWindow(resetDescription: { _ in "Resets" }) == nil)
}
@Test
func `maps sand usage onto a grok bot extra window`() {
let snapshot = CursorStatusSnapshot(
planPercentUsed: 0.6,
autoPercentUsed: 0.75,
apiPercentUsed: 0,
planUsedUSD: 14.99,
planLimitUSD: 400.0,
onDemandUsedUSD: 0,
onDemandLimitUSD: nil,
teamOnDemandUsedUSD: nil,
teamOnDemandLimitUSD: nil,
billingCycleEnd: nil,
membershipType: "ultra",
accountEmail: nil,
accountName: nil,
rawJSON: nil,
sandUsage: CursorSandUsageStatus(
currentPeriodStart: "2026-08-17T07:57:50.647Z",
nextResetTimestampUtc: "2026-08-24T07:57:50.647Z",
usagePercent: 100,
hasAvailableUsage: true,
hasNonZeroIncludedLimit: true))
let usageSnapshot = snapshot.toUsageSnapshot()
let grokBot = usageSnapshot.extraRateWindows?.first { $0.id == CursorSandUsageStatus.extraWindowID }
#expect(grokBot?.title == "Grok Bot")
#expect(grokBot?.window.usedPercent == 100)
#expect(grokBot?.window.windowMinutes == 10080)
#expect(grokBot?.window.resetsAt != nil)
}
@Test
func `fetch maps sand usage status onto grok bot extra window`() async throws {
let testSession = CursorStatusProbeTestSession { request in
let requestURL = try #require(request.url)
switch requestURL.path {
case "/api/usage-summary":
#expect(request.timeoutInterval == 15)
return makeCursorStatusProbeResponse(
url: requestURL,
body: """
{
"membershipType": "ultra",
"individualUsage": {
"plan": {
"used": 1499,
"limit": 40000,
"totalPercentUsed": 0.6
}
}
}
""",
statusCode: 200)
case "/api/auth/me":
return makeCursorStatusProbeResponse(
url: requestURL,
body: #"{"error":"nope"}"#,
statusCode: 500)
case "/api/dashboard/get-sand-usage-status":
#expect(request.httpMethod == "POST")
#expect(request.timeoutInterval == 5)
#expect(request.value(forHTTPHeaderField: "Origin") == "https://cursor.test")
#expect(request.value(forHTTPHeaderField: "Cookie") == "auth=test")
return makeCursorStatusProbeResponse(
url: requestURL,
body: """
{
"currentPeriodStart": "2026-08-17T07:57:50.647Z",
"nextResetTimestampUtc": "2026-08-24T07:57:50.647Z",
"usagePercent": 100,
"hasAvailableUsage": true,
"hasNonZeroIncludedLimit": true
}
""",
statusCode: 200)
default:
throw URLError(.badURL)
}
}
let baseURL = try #require(URL(string: "https://cursor.test"))
let snapshot = try await CursorStatusProbe(
baseURL: baseURL,
browserDetection: BrowserDetection(cacheTTL: 0),
urlSession: testSession.urlSession).fetchWithManualCookies("auth=test")
#expect(snapshot.sandUsage?.usagePercent == 100)
#expect(snapshot.sandUsage?.hasNonZeroIncludedLimit == true)
let grokBot = snapshot.toUsageSnapshot().extraRateWindows?.first {
$0.id == CursorSandUsageStatus.extraWindowID
}
#expect(grokBot?.window.usedPercent == 100)
#expect(grokBot?.window.windowMinutes == 10080)
#expect(snapshot.rawJSON?.contains("get-sand-usage-status") == true)
}
}
@@ -389,6 +389,7 @@ struct CursorStatusProbeTests {
#expect(usageSnapshot.providerCost?.used == 5.0)
#expect(usageSnapshot.providerCost?.limit == 100.0)
#expect(usageSnapshot.providerCost?.currencyCode == "USD")
#expect(usageSnapshot.extraRateWindows == nil)
let roundTripped = try JSONDecoder().decode(
UsageSnapshot.self,
@@ -748,7 +749,7 @@ struct CursorStatusProbeTests {
}
}
private final class CursorStatusProbeTestSession {
final class CursorStatusProbeTestSession {
let urlSession: URLSession
private let sessionID: String
@@ -778,7 +779,7 @@ private final class CursorStatusProbeTestSession {
}
}
private func makeCursorStatusProbeResponse(
func makeCursorStatusProbeResponse(
url: URL,
body: String,
statusCode: Int,
@@ -886,7 +887,9 @@ extension CursorStatusProbeTests {
#expect(snapshot.planPercentUsed == 30.0)
#expect(snapshot.accountEmail == nil)
#expect(testSession.requestCount == 2)
#expect(snapshot.sandUsage == nil)
#expect(testSession.requestCount == 3)
#expect(testSession.requestPaths.contains(CursorSandUsageStatus.endpointPath))
}
@Test
@@ -945,6 +948,10 @@ extension CursorStatusProbeTests {
let requestURL = try #require(request.url)
#expect(request.value(forHTTPHeaderField: "Authorization") == nil)
#expect(request.value(forHTTPHeaderField: "Cookie") == expectedCookie)
if requestURL.path == CursorSandUsageStatus.endpointPath {
#expect(request.httpMethod == "POST")
return makeCursorStatusProbeResponse(url: requestURL, body: "{}", statusCode: 404)
}
#expect(request.httpMethod == "GET")
switch requestURL.path {
@@ -1009,6 +1016,7 @@ extension CursorStatusProbeTests {
#expect(snapshot.accountName == "Test User")
#expect(testSession.requestPaths.sorted() == [
"/api/auth/me",
"/api/dashboard/get-sand-usage-status",
"/api/usage",
"/api/usage-summary",
])
@@ -1207,6 +1215,8 @@ extension CursorStatusProbeTests {
url: requestURL,
body: #"{"gpt-4":{}}"#,
statusCode: 200)
case "/api/dashboard/get-sand-usage-status":
return makeCursorStatusProbeResponse(url: requestURL, body: "{}", statusCode: 404)
default:
Issue.record("App-session precedence test unexpectedly requested \(requestURL.path)")
throw URLError(.badURL)
@@ -1228,6 +1238,7 @@ extension CursorStatusProbeTests {
#expect(snapshot.accountEmail == "app@example.com")
#expect(testSession.requestPaths.sorted() == [
"/api/auth/me",
"/api/dashboard/get-sand-usage-status",
"/api/usage",
"/api/usage-summary",
])
@@ -1291,6 +1302,7 @@ extension CursorStatusProbeTests {
#expect(snapshot.accountEmail == nil)
#expect(testSession.requestPaths.sorted() == [
"/api/auth/me",
"/api/dashboard/get-sand-usage-status",
"/api/usage",
"/api/usage-summary",
])
@@ -3271,7 +3271,15 @@ struct ProviderArchitectureGatekeeperTests {
reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."),
AllowedProviderConstruct(
path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift",
line: 479,
line: 480,
anchor: "if provider == .cursor {",
expectedProviderIDs: ["cursor"],
expectedReferenceCount: 1,
expectedReferenceFingerprint: ["cursor@0"],
reason: "Cursor Grok Bot weekly included usage is a named extraRateWindow on the shared widget projection."),
AllowedProviderConstruct(
path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift",
line: 493,
anchor: "if provider == .claude, self.settings.claudeModelScopedWeeklyUsageVisible {",
expectedProviderIDs: ["claude"],
expectedReferenceCount: 1,
@@ -3279,7 +3287,7 @@ struct ProviderArchitectureGatekeeperTests {
reason: "Claude's opt-in widget projection adds provider-owned model-scoped weekly quota rows."),
AllowedProviderConstruct(
path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift",
line: 493,
line: 507,
anchor: "if provider == .kimi {",
expectedProviderIDs: ["kimi"],
expectedReferenceCount: 1,
+6 -1
View File
@@ -69,6 +69,9 @@ Manual option:
- Stable user ID, email, and name.
- `GET https://cursor.com/api/usage?user=ID`
- Legacy request-based plan usage (request counts + limits).
- `POST https://cursor.com/api/dashboard/get-sand-usage-status`
- Grok Bot weekly included usage (`usagePercent`, `nextResetTimestampUtc`). Same session cookie;
requires `Origin: https://cursor.com`. Best-effort: a failure leaves Cursor's monthly bars intact.
## Cookie file paths
- Safari: `~/Library/Cookies/Cookies.binarycookies`
@@ -117,12 +120,14 @@ Caching: the app holds the snapshot for an in-memory hourly TTL, keyed by the hi
- Primary: plan usage percent (included plan).
- Secondary: Cursor (Cursor models) usage percent.
- Tertiary: Third Party usage percent.
- Extra: Grok Bot weekly included usage from `get-sand-usage-status` when the account has a non-zero Bot allowance.
- Provider cost: Extra usage USD. A capped individual budget wins; team accounts without a user cap use the shared team on-demand budget.
- Reset: billing cycle end date.
- Reset: billing cycle end date for monthly bars; Grok Bot uses `nextResetTimestampUtc` (weekly).
## Key files
- `Sources/CodexBarCore/Providers/Cursor/CursorAppAuth.swift`
- `Sources/CodexBarCore/Providers/Cursor/CursorStatusProbe.swift`
- `Sources/CodexBarCore/Providers/Cursor/CursorSandUsage.swift` (Grok Bot weekly included usage)
- `Sources/CodexBar/CursorLoginRunner.swift` (login flow)
- `Sources/CodexBar/Providers/Cursor/CursorLoginFlow.swift` (menu integration)
- `Sources/CodexBar/CursorLoginBrowserRouter.swift` (browser routing and selection)
+1
View File
@@ -209,6 +209,7 @@ complete when the available scan window covers fewer days.
- Web API via browser cookies (`cursor.com` + `cursor.sh`).
- Fallbacks: a legacy stored session, then Cursor.app local auth.
- Add Account and Switch Account open Cursor's authenticator in a supported browser; Switch Account prefers stable account IDs and falls back to normalized email when IDs are unavailable. CodexBar uses the supported system HTTPS handler when possible and otherwise asks the user to choose an eligible supported browser.
- Grok Bot weekly included usage is a fourth Cursor card bar from `POST /api/dashboard/get-sand-usage-status` (same session). Accounts without a Bot allowance omit the bar.
- Status: Statuspage.io (Cursor).
- Details: `docs/cursor.md`.