import Foundation public struct GrokUsageSnapshot: Sendable { public let billing: GrokBillingResponse? public let webBilling: GrokWebBillingSnapshot? public let credentials: GrokCredentials? public let localSummary: GrokLocalSessionSummary? public let cliVersion: String? public let diagnostic: String? public let updatedAt: Date public init( billing: GrokBillingResponse?, webBilling: GrokWebBillingSnapshot? = nil, credentials: GrokCredentials?, localSummary: GrokLocalSessionSummary?, cliVersion: String?, updatedAt: Date, diagnostic: String? = nil) { self.billing = billing self.webBilling = webBilling self.credentials = credentials self.localSummary = localSummary self.cliVersion = cliVersion self.diagnostic = diagnostic self.updatedAt = updatedAt } public func toUsageSnapshot() -> UsageSnapshot { // Primary window: credit usage (against included limit) from the CLI RPC, // falling back to the web billing RPC used by grok.com when the agent surface lacks billing. var primary: RateWindow? if let billing, let percent = billing.monthlyUsedPercent { primary = RateWindow( usedPercent: percent, windowMinutes: billing.billingPeriodMinutes, resetsAt: billing.billingPeriodEndDate, resetDescription: nil) } else if let webBilling, let percent = webBilling.usedPercent { primary = RateWindow( usedPercent: percent, windowMinutes: nil, resetsAt: webBilling.resetsAt, resetDescription: nil) } let identity = ProviderIdentitySnapshot( providerID: .grok, accountEmail: self.credentials?.email, accountOrganization: self.credentials?.teamId, loginMethod: self.credentials?.loginMethod) return UsageSnapshot( primary: primary, secondary: nil, tertiary: nil, updatedAt: self.updatedAt, identity: identity) }} public struct GrokStatusProbe: Sendable { public static let teamUsageUnavailableMessage = "Grok team usage is unavailable from the current billing surface; identity is still available." public init() {} public static func detectVersion(env: [String: String] = ProcessInfo.processInfo.environment) -> String? { guard let binary = BinaryLocator.resolveGrokBinary(env: env) else { return nil } guard let output = ProviderVersionDetector.run( path: binary, args: ["--version"], environment: env, mergeStandardError: true) else { return nil } // Output is like "grok 0.1.210 (8b63e9068c)" — strip the leading "grok " so // callers can prefix the CLI name themselves without duplicating it. let withoutPrefix = output.replacingOccurrences( of: #"^grok\s+"#, with: "", options: [.regularExpression]) .trimmingCharacters(in: .whitespacesAndNewlines) return withoutPrefix.isEmpty ? nil : withoutPrefix } public func fetch(env: [String: String] = ProcessInfo.processInfo.environment) async throws -> GrokUsageSnapshot { // Credentials are optional: we still show identity-less state if the user // hasn't logged in, with a clear hint via the RPC error. let credentials = try? GrokCredentialsStore.load(env: env) var billing: GrokBillingResponse? var rpcError: Error? var billingAttempted = false do { let client = try GrokRPCClient(environment: env) defer { client.shutdown() } try await client.initialize() billingAttempted = true billing = try await client.fetchBilling() } catch { rpcError = error } // Local fallback summary always succeeds (empty if no sessions yet). let localSummary = GrokLocalSessionScanner.summarize(env: env) let cliVersion = Self.detectVersion(env: env) // `localSummary` is *not* currently projected into a visible RateWindow or // identity field, so a stale `~/.grok/sessions/` directory must not // suppress the auth-required hint. CLI-only fetches need a billing // response; the provider pipeline owns the separate web fallback. if billing == nil, let credentials, Self.shouldUseIdentityOnlyFallback( credentials: credentials, billingAttempted: billingAttempted, error: rpcError) { return Self.identityOnlySnapshot( credentials: credentials, localSummary: localSummary, cliVersion: cliVersion) } if billing == nil { throw rpcError ?? GrokRPCError.notAuthenticated } return GrokUsageSnapshot( billing: billing, webBilling: nil, credentials: Self.credentialsForSnapshot( credentials: credentials, billing: billing, webBilling: nil), localSummary: localSummary, cliVersion: cliVersion, updatedAt: Date()) } static func identityOnlySnapshot( credentials: GrokCredentials, localSummary: GrokLocalSessionSummary?, cliVersion: String?, updatedAt: Date = .init()) -> GrokUsageSnapshot { GrokUsageSnapshot( billing: nil, webBilling: nil, credentials: credentials, localSummary: localSummary, cliVersion: cliVersion, updatedAt: updatedAt, diagnostic: GrokStatusProbe.teamUsageUnavailableMessage) } static func isBillingMethodUnavailable(_ error: Error?) -> Bool { guard let error, case let GrokRPCError.requestFailed(message) = error else { return false } let normalized = message.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() return normalized == "method not found" || normalized.hasPrefix("method not found:") } static func shouldUseIdentityOnlyFallback( credentials: GrokCredentials?, billingAttempted: Bool, error: Error?) -> Bool { guard billingAttempted, let credentials, !credentials.isExpired, credentials.isTeamPrincipal else { return false } return Self.isBillingMethodUnavailable(error) } static func credentialsForSnapshot( credentials: GrokCredentials?, billing: GrokBillingResponse?, webBilling: GrokWebBillingSnapshot? = nil) -> GrokCredentials? { // If remote usage succeeded, xAI accepted auth and the local // identity is still useful even when the persisted expires_at is stale. if billing != nil || webBilling != nil { return credentials } return credentials.flatMap { $0.isExpired ? nil : $0 } } static func shouldSurfaceRemoteAuthError(_ error: Error?) -> Bool { guard let error = error as? GrokWebBillingError else { return false } switch error { case let .requestFailed(status, _): return status == 401 || status == 403 case let .rpcFailed(status, _): return status == 16 case .missingCredentials, .emptyResponse, .invalidResponse, .teamUsageUnsupported, .parseFailed: return false } }}
import Foundation
public struct GrokUsageSnapshot: Sendable {
public let billing: GrokBillingResponse?
public let webBilling: GrokWebBillingSnapshot?
public let credentials: GrokCredentials?
public let localSummary: GrokLocalSessionSummary?
public let cliVersion: String?
public let diagnostic: String?
public let updatedAt: Date
public init(
billing: GrokBillingResponse?,
webBilling: GrokWebBillingSnapshot? = nil,
credentials: GrokCredentials?,
localSummary: GrokLocalSessionSummary?,
cliVersion: String?,
updatedAt: Date,
diagnostic: String? = nil)
{
self.billing = billing
self.webBilling = webBilling
self.credentials = credentials
self.localSummary = localSummary
self.cliVersion = cliVersion
self.diagnostic = diagnostic
self.updatedAt = updatedAt
}
public func toUsageSnapshot() -> UsageSnapshot {
// Primary window: credit usage (against included limit) from the CLI RPC,
// falling back to the web billing RPC used by grok.com when the agent surface lacks billing.
var primary: RateWindow?
if let billing,
let percent = billing.monthlyUsedPercent
{
primary = RateWindow(
usedPercent: percent,
windowMinutes: billing.billingPeriodMinutes,
resetsAt: billing.billingPeriodEndDate,
resetDescription: nil)
} else if let webBilling,
let percent = webBilling.usedPercent
{
primary = RateWindow(
usedPercent: percent,
windowMinutes: nil,
resetsAt: webBilling.resetsAt,
resetDescription: nil)
}
let identity = ProviderIdentitySnapshot(
providerID: .grok,
accountEmail: self.credentials?.email,
accountOrganization: self.credentials?.teamId,
loginMethod: self.credentials?.loginMethod)
return UsageSnapshot(
primary: primary,
secondary: nil,
tertiary: nil,
updatedAt: self.updatedAt,
identity: identity)
}
}
public struct GrokStatusProbe: Sendable {
public static let teamUsageUnavailableMessage =
"Grok team usage is unavailable from the current billing surface; identity is still available."
public init() {}
public static func detectVersion(env: [String: String] = ProcessInfo.processInfo.environment) -> String? {
guard let binary = BinaryLocator.resolveGrokBinary(env: env) else { return nil }
guard let output = ProviderVersionDetector.run(
path: binary,
args: ["--version"],
environment: env,
mergeStandardError: true)
else { return nil }
// Output is like "grok 0.1.210 (8b63e9068c)" — strip the leading "grok " so
// callers can prefix the CLI name themselves without duplicating it.
let withoutPrefix = output.replacingOccurrences(
of: #"^grok\s+"#,
with: "",
options: [.regularExpression])
.trimmingCharacters(in: .whitespacesAndNewlines)
return withoutPrefix.isEmpty ? nil : withoutPrefix
}
public func fetch(env: [String: String] = ProcessInfo.processInfo.environment) async throws -> GrokUsageSnapshot {
// Credentials are optional: we still show identity-less state if the user
// hasn't logged in, with a clear hint via the RPC error.
let credentials = try? GrokCredentialsStore.load(env: env)
var billing: GrokBillingResponse?
var rpcError: Error?
var billingAttempted = false
do {
let client = try GrokRPCClient(environment: env)
defer { client.shutdown() }
try await client.initialize()
billingAttempted = true
billing = try await client.fetchBilling()
} catch {
rpcError = error
}
// Local fallback summary always succeeds (empty if no sessions yet).
let localSummary = GrokLocalSessionScanner.summarize(env: env)
let cliVersion = Self.detectVersion(env: env)
// `localSummary` is *not* currently projected into a visible RateWindow or
// identity field, so a stale `~/.grok/sessions/` directory must not
// suppress the auth-required hint. CLI-only fetches need a billing
// response; the provider pipeline owns the separate web fallback.
if billing == nil,
let credentials,
Self.shouldUseIdentityOnlyFallback(
credentials: credentials,
billingAttempted: billingAttempted,
error: rpcError)
{
return Self.identityOnlySnapshot(
credentials: credentials,
localSummary: localSummary,
cliVersion: cliVersion)
}
if billing == nil {
throw rpcError ?? GrokRPCError.notAuthenticated
}
return GrokUsageSnapshot(
billing: billing,
webBilling: nil,
credentials: Self.credentialsForSnapshot(
credentials: credentials,
billing: billing,
webBilling: nil),
localSummary: localSummary,
cliVersion: cliVersion,
updatedAt: Date())
}
static func identityOnlySnapshot(
credentials: GrokCredentials,
localSummary: GrokLocalSessionSummary?,
cliVersion: String?,
updatedAt: Date = .init()) -> GrokUsageSnapshot
{
GrokUsageSnapshot(
billing: nil,
webBilling: nil,
credentials: credentials,
localSummary: localSummary,
cliVersion: cliVersion,
updatedAt: updatedAt,
diagnostic: GrokStatusProbe.teamUsageUnavailableMessage)
}
static func isBillingMethodUnavailable(_ error: Error?) -> Bool {
guard let error,
case let GrokRPCError.requestFailed(message) = error
else {
return false
}
let normalized = message.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
return normalized == "method not found" || normalized.hasPrefix("method not found:")
}
static func shouldUseIdentityOnlyFallback(
credentials: GrokCredentials?,
billingAttempted: Bool,
error: Error?) -> Bool
{
guard billingAttempted,
let credentials,
!credentials.isExpired,
credentials.isTeamPrincipal
else {
return false
}
return Self.isBillingMethodUnavailable(error)
}
static func credentialsForSnapshot(
credentials: GrokCredentials?,
billing: GrokBillingResponse?,
webBilling: GrokWebBillingSnapshot? = nil) -> GrokCredentials?
{
// If remote usage succeeded, xAI accepted auth and the local
// identity is still useful even when the persisted expires_at is stale.
if billing != nil || webBilling != nil { return credentials }
return credentials.flatMap { $0.isExpired ? nil : $0 }
}
static func shouldSurfaceRemoteAuthError(_ error: Error?) -> Bool {
guard let error = error as? GrokWebBillingError else { return false }
switch error {
case let .requestFailed(status, _):
return status == 401 || status == 403
case let .rpcFailed(status, _):
return status == 16
case .missingCredentials, .emptyResponse, .invalidResponse, .teamUsageUnsupported, .parseFailed:
return false
}
}
}