import Foundationpublic 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 updatedAt: Date public init( billing: GrokBillingResponse?, webBilling: GrokWebBillingSnapshot? = nil, credentials: GrokCredentials?, localSummary: GrokLocalSessionSummary?, cliVersion: String?, updatedAt: Date) { self.billing = billing self.webBilling = webBilling self.credentials = credentials self.localSummary = localSummary self.cliVersion = cliVersion 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 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? do { let client = try GrokRPCClient(environment: env) defer { client.shutdown() } try await client.initialize() 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 { 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 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, .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 updatedAt: Date
public init(
billing: GrokBillingResponse?,
webBilling: GrokWebBillingSnapshot? = nil,
credentials: GrokCredentials?,
localSummary: GrokLocalSessionSummary?,
cliVersion: String?,
updatedAt: Date)
{
self.billing = billing
self.webBilling = webBilling
self.credentials = credentials
self.localSummary = localSummary
self.cliVersion = cliVersion
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 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?
do {
let client = try GrokRPCClient(environment: env)
defer { client.shutdown() }
try await client.initialize()
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 {
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 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, .parseFailed:
return false
}
}
}