From d8c4fb4c7a3c2ca39673d3aa3ba4bf4c52a622da Mon Sep 17 00:00:00 2001 From: "o.akimov" Date: Mon, 17 Aug 2026 11:39:02 +0200 Subject: [PATCH 01/11] Add Grok SuperGrok paste, source picker, and Heavy plan --- CHANGELOG.md | 5 + .../CodexBar/PreferencesProvidersPane.swift | 3 + .../Grok/GrokProviderImplementation.swift | 120 ++++++++ .../Providers/Grok/GrokSettingsStore.swift | 50 ++++ .../Shared/ProviderImplementation.swift | 45 ++- .../Providers/Grok/GrokAuth.swift | 39 ++- .../Grok/GrokCredentialRouting.swift | 67 +++++ .../Grok/GrokCreditsProxyFetcher.swift | 10 +- .../Grok/GrokProviderDescriptor.swift | 268 ++++++++++++------ .../Providers/Grok/GrokProviderSettings.swift | 33 +++ .../Providers/Grok/GrokSettingsReader.swift | 36 +++ .../Providers/Grok/GrokStatusProbe.swift | 19 +- .../Grok/GrokWebBillingFetcher.swift | 85 +++--- Tests/CodexBarTests/GrokAuthTests.swift | 47 +++ .../GrokCreditsProxyFetcherTests.swift | 136 ++++++--- .../GrokSettingsReaderTests.swift | 89 ++++++ .../GrokWebBillingFetcherTests.swift | 8 +- docs/grok.md | 36 ++- .../grok-supergrok-oauth-settings.png | Bin 0 -> 292823 bytes 19 files changed, 899 insertions(+), 197 deletions(-) create mode 100644 Sources/CodexBar/Providers/Grok/GrokSettingsStore.swift create mode 100644 Sources/CodexBarCore/Providers/Grok/GrokCredentialRouting.swift create mode 100644 Sources/CodexBarCore/Providers/Grok/GrokProviderSettings.swift create mode 100644 Sources/CodexBarCore/Providers/Grok/GrokSettingsReader.swift create mode 100644 Tests/CodexBarTests/GrokSettingsReaderTests.swift create mode 100644 docs/screenshots/grok-supergrok-oauth-settings.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 44d07c0648..885c0a5c8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## 0.52.1 — Unreleased +### Added +- Grok: add an Auto / Grok CLI / SuperGrok OAuth / Browser cookies source picker, and paste a SuperGrok bearer or grok.com cookie into token accounts. OAuth and cookies stay separate paths. + +### Fixed +- Grok: Open token file opens `~/.grok/auth.json` instead of CodexBar's `config.json`. ## 0.52.0 — 2026-08-17 ### Added diff --git a/Sources/CodexBar/PreferencesProvidersPane.swift b/Sources/CodexBar/PreferencesProvidersPane.swift index 138b2e5a12..317fc775ba 100644 --- a/Sources/CodexBar/PreferencesProvidersPane.swift +++ b/Sources/CodexBar/PreferencesProvidersPane.swift @@ -465,6 +465,9 @@ struct ProvidersPane: View { } } }, openConfigFile: { + if implementation?.openTokenFile(context: context) == true { + return + } self.settings.openTokenAccountsFile() }, reloadFromDisk: { diff --git a/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift b/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift index 1d53f20a08..c6d337d9f4 100644 --- a/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift +++ b/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift @@ -1,6 +1,126 @@ +import AppKit import CodexBarCore import Foundation +import SwiftUI struct GrokProviderImplementation: ProviderImplementation { let id: UsageProvider = .grok + + @MainActor + func observeSettings(_ settings: SettingsStore) { + _ = settings.grokUsageDataSource + _ = settings.grokCookieSource + _ = settings.grokCookieHeader + } + + @MainActor + func sourceMode(context: ProviderSourceModeContext) -> ProviderSourceMode { + context.settings.grokUsageDataSource + } + + @MainActor + func openTokenFile(context _: ProviderSettingsContext) -> Bool { + let url = GrokCredentialsStore.tokenFileURLToOpen() + try? FileManager.default.createDirectory( + at: GrokCredentialsStore.grokHomeURL(), + withIntermediateDirectories: true) + NSWorkspace.shared.open(url) + return true + } + + @MainActor + func settingsSnapshot(context: ProviderSettingsSnapshotContext) + -> ProviderSettingsSnapshotContribution? + { + .grok(context.settings.grokSettingsSnapshot(tokenOverride: context.tokenOverride)) + } + + @MainActor + func settingsPickers(context: ProviderSettingsContext) -> [ProviderSettingsPickerDescriptor] { + let sourceBinding = Binding( + get: { context.settings.grokUsageDataSource.rawValue }, + set: { raw in + context.settings.grokUsageDataSource = ProviderSourceMode(rawValue: raw) ?? .auto + }) + let sourceOptions: [ProviderSettingsPickerOption] = [ + ProviderSettingsPickerOption(id: ProviderSourceMode.auto.rawValue, title: "Auto"), + ProviderSettingsPickerOption(id: ProviderSourceMode.cli.rawValue, title: "Grok CLI"), + ProviderSettingsPickerOption( + id: ProviderSourceMode.oauth.rawValue, + title: "SuperGrok OAuth"), + ProviderSettingsPickerOption( + id: ProviderSourceMode.web.rawValue, title: "Browser cookies"), + ] + let cookieBinding = Binding( + get: { context.settings.grokCookieSource.rawValue }, + set: { raw in + context.settings.grokCookieSource = ProviderCookieSource(rawValue: raw) ?? .auto + }) + let cookieOptions = ProviderCookieSourceUI.options( + allowsOff: true, + keychainDisabled: context.settings.debugDisableKeychainAccess) + + let cookieSubtitle: () -> String? = { + ProviderCookieSourceUI.subtitle( + source: context.settings.grokCookieSource, + keychainDisabled: context.settings.debugDisableKeychainAccess, + auto: "Automatic imports grok.com cookies from Chrome.", + manual: "Paste a Cookie header from a grok.com request.", + off: "Grok cookies are disabled.") + } + + return [ + ProviderSettingsPickerDescriptor( + id: "grok-usage-source", + title: "Usage source", + subtitle: "Auto tries the Grok CLI, SuperGrok OAuth, then browser cookies.", + binding: sourceBinding, + options: sourceOptions, + isVisible: nil, + onChange: nil), + ProviderSettingsPickerDescriptor( + id: "grok-cookie-source", + title: "Cookie source", + subtitle: "Automatic imports grok.com cookies from Chrome.", + dynamicSubtitle: cookieSubtitle, + binding: cookieBinding, + options: cookieOptions, + isVisible: { + context.settings.grokUsageDataSource == .auto + || context.settings.grokUsageDataSource == .web + }, + onChange: nil), + ] + } + + @MainActor + func settingsFields(context: ProviderSettingsContext) -> [ProviderSettingsFieldDescriptor] { + [ + ProviderSettingsFieldDescriptor( + id: "grok-cookie", + title: "", + subtitle: "", + kind: .secure, + placeholder: "Cookie: …", + binding: context.stringBinding(\.grokCookieHeader), + actions: [ + ProviderSettingsActionDescriptor( + id: "grok-open-usage", + title: "Open grok.com usage", + style: .link, + isVisible: nil, + perform: { + if let url = URL(string: "https://grok.com/?_s=usage") { + NSWorkspace.shared.open(url) + } + }), + ], + isVisible: { + (context.settings.grokUsageDataSource == .auto + || context.settings.grokUsageDataSource == .web) + && context.settings.grokCookieSource == .manual + }, + onActivate: { context.settings.ensureGrokCookieLoaded() }), + ] + } } diff --git a/Sources/CodexBar/Providers/Grok/GrokSettingsStore.swift b/Sources/CodexBar/Providers/Grok/GrokSettingsStore.swift new file mode 100644 index 0000000000..c9abf30d67 --- /dev/null +++ b/Sources/CodexBar/Providers/Grok/GrokSettingsStore.swift @@ -0,0 +1,50 @@ +import CodexBarCore +import Foundation + +extension SettingsStore { + var grokUsageDataSource: ProviderSourceMode { + get { self.configSnapshot.providerConfig(for: .grok)?.source ?? .auto } + set { + self.updateProviderConfig(provider: .grok) { entry in + entry.source = newValue == .auto ? nil : newValue + } + self.logProviderModeChange(provider: .grok, field: "source", value: newValue.rawValue) + } + } + + var grokCookieHeader: String { + get { self.configSnapshot.providerConfig(for: .grok)?.sanitizedCookieHeader ?? "" } + set { + self.updateProviderConfig(provider: .grok) { entry in + entry.cookieHeader = self.normalizedConfigValue(newValue) + } + self.logSecretUpdate(provider: .grok, field: "cookieHeader", value: newValue) + } + } + + var grokCookieSource: ProviderCookieSource { + get { self.resolvedCookieSource(provider: .grok, fallback: .auto) } + set { + self.updateProviderConfig(provider: .grok) { entry in + entry.cookieSource = newValue + } + self.logProviderModeChange( + provider: .grok, field: "cookieSource", value: newValue.rawValue) + } + } + + func ensureGrokCookieLoaded() {} +} + +extension SettingsStore { + func grokSettingsSnapshot(tokenOverride: TokenAccountOverride?) + -> ProviderSettingsSnapshot + .GrokProviderSettings + { + self.resolvedCookieSettings( + provider: .grok, + configuredSource: self.grokCookieSource, + configuredHeader: self.grokCookieHeader, + tokenOverride: tokenOverride) + } +} diff --git a/Sources/CodexBar/Providers/Shared/ProviderImplementation.swift b/Sources/CodexBar/Providers/Shared/ProviderImplementation.swift index 3e6613ee91..e333279605 100644 --- a/Sources/CodexBar/Providers/Shared/ProviderImplementation.swift +++ b/Sources/CodexBar/Providers/Shared/ProviderImplementation.swift @@ -52,35 +52,45 @@ protocol ProviderImplementation: Sendable { /// Optional provider-specific organizations selection rendered in the Providers pane. @MainActor - func settingsOrganizations(context: ProviderSettingsContext) -> ProviderSettingsOrganizationsDescriptor? + func settingsOrganizations(context: ProviderSettingsContext) + -> ProviderSettingsOrganizationsDescriptor? /// Optional visibility gate for token account settings. @MainActor - func tokenAccountsVisibility(context: ProviderSettingsContext, support: TokenAccountSupport) -> Bool + func tokenAccountsVisibility(context: ProviderSettingsContext, support: TokenAccountSupport) + -> Bool /// Optional provider-specific settings snapshot contribution. @MainActor - func settingsSnapshot(context: ProviderSettingsSnapshotContext) -> ProviderSettingsSnapshotContribution? + func settingsSnapshot(context: ProviderSettingsSnapshotContext) + -> ProviderSettingsSnapshotContribution? /// Optional primary action for the shared token-account editor. @MainActor func runTokenAccountPrimaryAction(context: ProviderSettingsContext) async + /// Return true if the provider opened its own token file. False keeps the CodexBar config. + @MainActor + func openTokenFile(context: ProviderSettingsContext) -> Bool + /// Optional hook to update provider settings when token accounts change. @MainActor func applyTokenAccountCookieSource(settings: SettingsStore) /// Optional provider-specific menu entries for the usage section. @MainActor - func appendUsageMenuEntries(context: ProviderMenuUsageContext, entries: inout [ProviderMenuEntry]) + func appendUsageMenuEntries( + context: ProviderMenuUsageContext, entries: inout [ProviderMenuEntry]) /// Optional provider-specific menu entries for the actions section. @MainActor - func appendActionMenuEntries(context: ProviderMenuActionContext, entries: inout [ProviderMenuEntry]) + func appendActionMenuEntries( + context: ProviderMenuActionContext, entries: inout [ProviderMenuEntry]) /// Optional override for the login/switch account menu action. @MainActor - func loginMenuAction(context: ProviderMenuLoginContext) -> (label: String, action: MenuDescriptor.MenuAction)? + func loginMenuAction(context: ProviderMenuLoginContext) -> ( + label: String, action: MenuDescriptor.MenuAction)? /// Optional provider-specific login flow. Returns whether to refresh after completion. @MainActor @@ -152,32 +162,45 @@ extension ProviderImplementation { } @MainActor - func settingsOrganizations(context _: ProviderSettingsContext) -> ProviderSettingsOrganizationsDescriptor? { + func settingsOrganizations(context _: ProviderSettingsContext) + -> ProviderSettingsOrganizationsDescriptor? + { nil } @MainActor - func tokenAccountsVisibility(context: ProviderSettingsContext, support: TokenAccountSupport) -> Bool { + func tokenAccountsVisibility(context: ProviderSettingsContext, support: TokenAccountSupport) + -> Bool + { guard support.requiresManualCookieSource else { return true } return !context.settings.tokenAccounts(for: context.provider).isEmpty } @MainActor - func settingsSnapshot(context _: ProviderSettingsSnapshotContext) -> ProviderSettingsSnapshotContribution? { + func settingsSnapshot(context _: ProviderSettingsSnapshotContext) + -> ProviderSettingsSnapshotContribution? + { ProviderDescriptorRegistry.descriptor(for: self.id).settingsSection.defaultContribution } @MainActor func runTokenAccountPrimaryAction(context _: ProviderSettingsContext) async {} + @MainActor + func openTokenFile(context _: ProviderSettingsContext) -> Bool { + false + } + @MainActor func applyTokenAccountCookieSource(settings _: SettingsStore) {} @MainActor - func appendUsageMenuEntries(context _: ProviderMenuUsageContext, entries _: inout [ProviderMenuEntry]) {} + func appendUsageMenuEntries( + context _: ProviderMenuUsageContext, entries _: inout [ProviderMenuEntry]) {} @MainActor - func appendActionMenuEntries(context _: ProviderMenuActionContext, entries _: inout [ProviderMenuEntry]) {} + func appendActionMenuEntries( + context _: ProviderMenuActionContext, entries _: inout [ProviderMenuEntry]) {} @MainActor func loginMenuAction(context _: ProviderMenuLoginContext) diff --git a/Sources/CodexBarCore/Providers/Grok/GrokAuth.swift b/Sources/CodexBarCore/Providers/Grok/GrokAuth.swift index e9081d47eb..e323a53bb1 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokAuth.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokAuth.swift @@ -50,6 +50,24 @@ public struct GrokCredentials: Sendable { self.createTime = createTime } + public static func pasted(accessToken: String) -> GrokCredentials { + GrokCredentials( + accessToken: accessToken, + refreshToken: nil, + scope: "", + authMode: "oidc", + userId: nil, + email: nil, + firstName: nil, + lastName: nil, + teamId: nil, + principalType: nil, + oidcIssuer: nil, + oidcClientId: nil, + expiresAt: nil, + createTime: nil) + } + public var displayName: String? { let parts = [self.firstName, self.lastName].compactMap { $0?.nilIfEmpty } guard !parts.isEmpty else { return nil } @@ -117,7 +135,22 @@ public enum GrokCredentialsStore { self.grokHomeURL(env: env, fileManager: fileManager).appendingPathComponent("auth.json") } - public static func load(env: [String: String] = ProcessInfo.processInfo.environment) throws -> GrokCredentials { + /// Prefer the Grok Build token file. If `grok login` has not created it yet, open `~/.grok` + /// instead of inventing an empty auth.json. + public static func tokenFileURLToOpen( + env: [String: String] = ProcessInfo.processInfo.environment, + fileManager: FileManager = .default) -> URL + { + let url = self.authFileURL(env: env, fileManager: fileManager) + if fileManager.fileExists(atPath: url.path) { + return url + } + return self.grokHomeURL(env: env, fileManager: fileManager) + } + + public static func load(env: [String: String] = ProcessInfo.processInfo.environment) throws + -> GrokCredentials + { let url = self.authFileURL(env: env) guard FileManager.default.fileExists(atPath: url.path) else { throw GrokCredentialsError.notFound @@ -164,7 +197,9 @@ public enum GrokCredentialsStore { createTime: Self.parseDate(entry["create_time"])) } - private static func selectPreferredEntry(in root: [String: Any]) -> (scope: String, entry: [String: Any])? { + private static func selectPreferredEntry(in root: [String: Any]) -> ( + scope: String, entry: [String: Any])? + { var oidcCandidate: (String, [String: Any])? var legacyCandidate: (String, [String: Any])? for (scope, value) in root { diff --git a/Sources/CodexBarCore/Providers/Grok/GrokCredentialRouting.swift b/Sources/CodexBarCore/Providers/Grok/GrokCredentialRouting.swift new file mode 100644 index 0000000000..dc1eb312d3 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Grok/GrokCredentialRouting.swift @@ -0,0 +1,67 @@ +import Foundation + +public enum GrokCredentialRouting: Sendable, Equatable { + case none + case oauth(accessToken: String) + case webCookie(header: String) + + public static func resolve(tokenAccountToken: String?, manualCookieHeader: String?) -> Self { + if let tokenAccountToken, let route = self.resolvePrimaryCredential(tokenAccountToken) { + return route + } + guard let cookieHeader = self.normalizedWebCookie(manualCookieHeader) else { + return .none + } + return .webCookie(header: cookieHeader) + } + + public var oauthAccessToken: String? { + guard case let .oauth(accessToken) = self else { return nil } + return accessToken + } + + public var manualCookieHeader: String? { + guard case let .webCookie(header) = self else { return nil } + return header + } + + public var sourceMode: ProviderSourceMode? { + switch self { + case .oauth: .oauth + case .webCookie: .web + case .none: nil + } + } + + private static func resolvePrimaryCredential(_ raw: String) -> Self? { + if let accessToken = self.normalizedOAuthToken(raw) { + return .oauth(accessToken: accessToken) + } + if let cookieHeader = self.normalizedWebCookie(raw) { + return .webCookie(header: cookieHeader) + } + return nil + } + + public static func normalizedOAuthToken(_ raw: String?) -> String? { + var token = raw?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" + if token.lowercased().hasPrefix("bearer ") { + token = String(token.dropFirst(7)).trimmingCharacters(in: .whitespacesAndNewlines) + } + guard !token.isEmpty else { return nil } + let lower = token.lowercased() + if lower.hasPrefix("cookie:") { return nil } + if lower.hasPrefix("xai-") { return nil } + if token.contains("=") { return nil } + return token + } + + public static func normalizedWebCookie(_ raw: String?) -> String? { + guard let normalized = CookieHeaderNormalizer.normalize(raw), + normalized.contains("=") + else { + return nil + } + return normalized + } +} diff --git a/Sources/CodexBarCore/Providers/Grok/GrokCreditsProxyFetcher.swift b/Sources/CodexBarCore/Providers/Grok/GrokCreditsProxyFetcher.swift index 062692f5fb..74ce605f5a 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokCreditsProxyFetcher.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokCreditsProxyFetcher.swift @@ -1,4 +1,5 @@ import Foundation + #if canImport(FoundationNetworking) import FoundationNetworking #endif @@ -6,7 +7,8 @@ import FoundationNetworking /// Fetches credits from the Grok CLI's billing backend. The grok.com gRPC-web endpoint now requires /// a browser-held WKE keypair (#2812), so the CLI proxy is the supported bearer-token path. public enum GrokCreditsProxyFetcher { - public static let defaultEndpoint = URL(string: "https://cli-chat-proxy.grok.com/v1/billing?format=credits")! + public static let defaultEndpoint = URL( + string: "https://cli-chat-proxy.grok.com/v1/billing?format=credits")! private static let requestTimeoutSeconds: TimeInterval = 15 public static func fetch( @@ -52,8 +54,10 @@ public enum GrokCreditsProxyFetcher { throw GrokWebBillingError.parseFailed } - let subscriptionTier = GrokPlan.displayName(from: config.subscriptionTier ?? response.subscriptionTier) - let resetsAt = config.currentPeriod?.end.flatMap(Self.parseISO8601) + let subscriptionTier = GrokPlan.displayName( + from: config.subscriptionTier ?? response.subscriptionTier) + let resetsAt = + config.currentPeriod?.end.flatMap(Self.parseISO8601) ?? config.billingPeriodEnd.flatMap(Self.parseISO8601) if let percent = config.creditUsagePercent, percent.isFinite { diff --git a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift index 7a63038677..037158f1b6 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift @@ -3,6 +3,26 @@ import SweetCookieKit public enum GrokProviderDescriptor { public static let descriptor: ProviderDescriptor = Self.makeDescriptor() + private static let credentials = ProviderCredentialAdapter( + tokenAccountSupport: TokenAccountSupport( + title: "SuperGrok tokens", + subtitle: + "Paste a SuperGrok bearer or grok.com cookie. Open token file opens ~/.grok/auth.json.", + placeholder: "Bearer … or Cookie: …", + injection: .environment(key: GrokSettingsReader.oauthTokenEnvironmentKey), + requiresManualCookieSource: false, + cookieName: nil, + environmentKeysToScrub: [GrokSettingsReader.oauthTokenEnvironmentKey], + environmentOverride: { token in + guard let oauth = GrokCredentialRouting.normalizedOAuthToken(token) else { return nil } + return [GrokSettingsReader.oauthTokenEnvironmentKey: oauth] + }), + selectedAccountSourceModeResolver: { base, account, _ in + guard let account else { return base } + return GrokCredentialRouting.resolve( + tokenAccountToken: account.token, + manualCookieHeader: nil).sourceMode ?? base + }) /// Grok is normally signed in through Chrome; avoid touching unrelated browser keychains. private static var browserCookieOrder: BrowserCookieImportOrder? { @@ -16,6 +36,23 @@ public enum GrokProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .grok, + settingsSection: .init( + GrokProviderSettingsKey.self, + cookieSettings: { settings in + CookieProviderSettings( + cookieSource: settings.cookieSource, + manualCookieHeader: settings.manualCookieHeader) + }, + credentialSettings: { context in + let cookies = context.cookieSettings(for: .grok) + let routing = GrokCredentialRouting.resolve( + tokenAccountToken: context.account?.token, + manualCookieHeader: context.account == nil ? cookies.manualCookieHeader : nil) + return GrokProviderSettings( + cookieSource: routing.manualCookieHeader != nil ? .manual : cookies.cookieSource, + manualCookieHeader: routing.manualCookieHeader ?? cookies.manualCookieHeader) + }), + credentials: self.credentials, metadata: ProviderMetadata( id: .grok, displayName: "Grok", @@ -49,16 +86,17 @@ public enum GrokProviderDescriptor { tokenCost: ProviderTokenCostConfig( supportsTokenCost: false, noDataMessage: { "Grok cost summary is not supported yet." }), - pace: ProviderPaceCapability(resetWindowPace: .custom { window, now in - guard Self.primaryLabel(window: window, now: now) == "Weekly", - let resetsAt = window.resetsAt - else { return false } - let windowMinutes = window.windowMinutes ?? 7 * 24 * 60 - let timeUntilReset = resetsAt.timeIntervalSince(now) - return windowMinutes > 0 - && timeUntilReset > 0 - && timeUntilReset <= TimeInterval(windowMinutes) * 60 - }), + pace: ProviderPaceCapability( + resetWindowPace: .custom { window, now in + guard Self.primaryLabel(window: window, now: now) == "Weekly", + let resetsAt = window.resetsAt + else { return false } + let windowMinutes = window.windowMinutes ?? 7 * 24 * 60 + let timeUntilReset = resetsAt.timeIntervalSince(now) + return windowMinutes > 0 + && timeUntilReset > 0 + && timeUntilReset <= TimeInterval(windowMinutes) * 60 + }), presentation: ProviderUsagePresentation(rateWindowLabeler: { metadata, snapshot, now in ProviderRateWindowLabels( primary: Self.displayLabel(window: snapshot.primary, now: now) ?? metadata.sessionLabel, @@ -67,7 +105,7 @@ public enum GrokProviderDescriptor { showsTertiary: metadata.supportsOpus) }), fetchPlan: ProviderFetchPlan( - sourceModes: [.auto, .cli, .web], + sourceModes: [.auto, .cli, .oauth, .web], pipeline: ProviderFetchPipeline(resolveStrategies: self.resolveStrategies)), cli: ProviderCLIConfig( name: "grok", @@ -75,15 +113,19 @@ public enum GrokProviderDescriptor { browserSupportExemption: { _, _, _ in true })) } - private static func resolveStrategies(context: ProviderFetchContext) async -> [any ProviderFetchStrategy] { + private static func resolveStrategies(context: ProviderFetchContext) async + -> [any ProviderFetchStrategy] + { switch context.sourceMode { case .auto: - [GrokCLIFetchStrategy(), GrokWebFetchStrategy()] + [GrokCLIFetchStrategy(), GrokOAuthFetchStrategy(), GrokWebFetchStrategy()] case .cli: [GrokCLIFetchStrategy()] + case .oauth: + [GrokOAuthFetchStrategy()] case .web: [GrokWebFetchStrategy()] - case .api, .oauth: + case .api: [] } } @@ -149,14 +191,50 @@ struct GrokCLIFetchStrategy: ProviderFetchStrategy { } } +struct GrokOAuthFetchStrategy: ProviderFetchStrategy { + let id: String = "grok.oauth" + let kind: ProviderFetchKind = .oauth + + func isAvailable(_ context: ProviderFetchContext) async -> Bool { + GrokSettingsReader.resolvedCredentials(environment: context.env) != nil + || FileManager.default.fileExists( + atPath: GrokCredentialsStore.authFileURL(env: context.env).path) + } + + func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { + try await GrokWebFetchStrategy().fetch(context) { + let credentials = try GrokWebFetchStrategy.resolvedCredentialsResult(context: context).get() + guard !credentials.isExpired else { + throw GrokWebBillingError.missingCredentials + } + do { + let snapshot = try await GrokCreditsProxyFetcher.fetch(credentials: credentials) + return (snapshot, "grok-cli-proxy", true) + } catch is CancellationError { + throw CancellationError() + } catch let error as URLError where error.code == .cancelled { + throw error + } catch { + let snapshot = try await GrokWebBillingFetcher.fetch(credentials: credentials) + return (snapshot, "grok-web", true) + } + } + } + + func shouldFallback(on _: Error, context: ProviderFetchContext) -> Bool { + context.sourceMode == .auto + } +} + struct GrokWebFetchStrategy: ProviderFetchStrategy { let id: String = "grok.web" let kind: ProviderFetchKind = .web typealias ProxyBillingFetch = @Sendable (GrokCredentials) async throws -> GrokWebBillingSnapshot - typealias WebBillingFetch = @Sendable () async throws -> ( - snapshot: GrokWebBillingSnapshot, - sourceLabel: String, - authenticatedByAuthFile: Bool) + typealias WebBillingFetch = + @Sendable () async throws -> ( + snapshot: GrokWebBillingSnapshot, + sourceLabel: String, + authenticatedByAuthFile: Bool) typealias SettingsTierFetch = @Sendable (GrokCredentials?) async throws -> String? /// Browser-cookie import must stay limited to surfaces where a person explicitly asked for it: @@ -164,29 +242,38 @@ struct GrokWebFetchStrategy: ProviderFetchStrategy { /// commands and app UI gestures), or the environment override. Scheduled and background work /// must keep the default `.background` context so it can never reach Chromium Keychain prompts. static func canImportBrowserCookies(runtime: ProviderRuntime, env: [String: String]) -> Bool { - runtime == .app || - ProviderInteractionContext.current == .userInitiated || - env["CODEXBAR_ALLOW_BROWSER_COOKIE_IMPORT"] == "1" + runtime == .app || ProviderInteractionContext.current == .userInitiated + || env["CODEXBAR_ALLOW_BROWSER_COOKIE_IMPORT"] == "1" } func isAvailable(_ context: ProviderFetchContext) async -> Bool { + let cookieSource = context.settings?.grok?.cookieSource ?? .auto #if os(macOS) - if CookieHeaderCache.load(provider: .grok) != nil { + if cookieSource != .off, CookieHeaderCache.load(provider: .grok) != nil { return true } - if Self.canImportBrowserCookies(runtime: context.runtime, env: context.env), + if cookieSource != .off, + let header = context.settings?.grok?.manualCookieHeader, + !header.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + { + return true + } + if cookieSource == .auto, + Self.canImportBrowserCookies(runtime: context.runtime, env: context.env), GrokCookieImporter.hasSession(browserDetection: context.browserDetection) { return true } #endif - return FileManager.default.fileExists(atPath: GrokCredentialsStore.authFileURL(env: context.env).path) + return false } func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { - try await self.fetch(context, webBilling: { [self] in - try await self.fetchWebBilling(context: context) - }) + try await self.fetch( + context, + webBilling: { [self] in + try await self.fetchWebBilling(context: context) + }) } func fetch( @@ -194,12 +281,14 @@ struct GrokWebFetchStrategy: ProviderFetchStrategy { webBilling fetchWebBilling: @escaping WebBillingFetch, settingsTier loadSettingsTier: SettingsTierFetch? = nil) async throws -> ProviderFetchResult { - let authCredentials = (try? GrokCredentialsStore.load(env: context.env)).flatMap { credentials in + let authCredentials = GrokSettingsReader.resolvedCredentials(environment: context.env).flatMap { + credentials in credentials.isExpired ? nil : credentials } - let resolveSettingsTier = loadSettingsTier ?? { credentials in - try await GrokStatusProbe.loadSettingsTier(credentials: credentials) - } + let resolveSettingsTier = + loadSettingsTier ?? { credentials in + try await GrokStatusProbe.loadSettingsTier(credentials: credentials) + } let webBilling: GrokWebBillingSnapshot let sourceLabel: String @@ -222,15 +311,16 @@ struct GrokWebFetchStrategy: ProviderFetchStrategy { diagnostic: identitySnapshot.diagnostic) } let credentials = Self.credentialsForWebBillingSnapshot( - credentials: authCredentials, + credentials: GrokSettingsReader.resolvedCredentials(environment: context.env), authenticatedByAuthFile: authenticatedByAuthFile) // Cookie/gRPC fallback is a different browser session. Never attach the // auth.json account's settings tier onto that usage. - let subscriptionTier: String? = if authenticatedByAuthFile { - try await resolveSettingsTier(authCredentials) - } else { - nil - } + let subscriptionTier: String? = + if authenticatedByAuthFile { + try await resolveSettingsTier(authCredentials) + } else { + nil + } let enrichedBilling = webBilling.applying(subscriptionTier: subscriptionTier) let snapshot = GrokUsageSnapshot( billing: nil, @@ -256,20 +346,9 @@ struct GrokWebFetchStrategy: ProviderFetchStrategy { sourceLabel: String, authenticatedByAuthFile: Bool) { - let credentialsResult: Result = Result { - try GrokCredentialsStore.load(env: context.env) - } - let browserCredentials = try? credentialsResult.get() - - return try await Self.fetchProxyFirst( - credentials: browserCredentials, - proxyBilling: proxyBilling) - { [self] in - try await self.fetchLegacyWebBilling( - context: context, - credentialsResult: credentialsResult, - browserCredentials: browserCredentials) - } + try await self.fetchLegacyWebBilling( + context: context, + browserCredentials: nil) } static func fetchProxyFirst( @@ -300,16 +379,20 @@ struct GrokWebFetchStrategy: ProviderFetchStrategy { private func fetchLegacyWebBilling( context: ProviderFetchContext, - credentialsResult: Result, browserCredentials: GrokCredentials?) async throws -> ( snapshot: GrokWebBillingSnapshot, sourceLabel: String, authenticatedByAuthFile: Bool) { + let cookieSettings = context.settings?.grok + let cookieSource = cookieSettings?.cookieSource ?? .auto + let manualHeader = cookieSettings?.manualCookieHeader? + .trimmingCharacters(in: .whitespacesAndNewlines) + #if os(macOS) var cacheObservation = CookieHeaderCache.observeForConditionalMutation(provider: .grok) var lastCookieError: Error? - if let cached = cacheObservation.entry { + if cookieSource != .off, let cached = cacheObservation.entry { do { let snapshot = try await Self.fetchValidCookieHeader( cached.cookieHeader, @@ -325,9 +408,28 @@ struct GrokWebFetchStrategy: ProviderFetchStrategy { } } - if Self.canImportBrowserCookies(runtime: context.runtime, env: context.env) { + if cookieSource != .off, + let manualHeader, !manualHeader.isEmpty + { + do { + let snapshot = try await Self.fetchValidCookieHeader( + manualHeader, + credentials: browserCredentials) + return (snapshot, "manual-cookie", false) + } catch { + lastCookieError = error + if cookieSource == .manual { + throw error + } + } + } + + if cookieSource == .auto, + Self.canImportBrowserCookies(runtime: context.runtime, env: context.env) + { do { - let sessions = try GrokCookieImporter.importSessions(browserDetection: context.browserDetection) + let sessions = try GrokCookieImporter.importSessions( + browserDetection: context.browserDetection) let (snapshot, sourceLabel) = try await Self.fetchFirstValidCookieSession( sessions, credentials: browserCredentials, @@ -336,23 +438,26 @@ struct GrokWebFetchStrategy: ProviderFetchStrategy { } catch { lastCookieError = error } - if browserCredentials == nil { - if FileManager.default.fileExists( - atPath: GrokCredentialsStore.authFileURL(env: context.env).path) - { - _ = try credentialsResult.get() - } - throw lastCookieError ?? GrokWebBillingError.missingCredentials - } + throw lastCookieError ?? GrokWebBillingError.missingCredentials } #endif - let authCredentials = try credentialsResult.get() - guard !authCredentials.isExpired else { - throw GrokWebBillingError.missingCredentials + throw GrokWebBillingError.missingCredentials + } + + static func resolvedCredentialsResult(context: ProviderFetchContext) -> Result< + GrokCredentials, Error, + > { + let fileResult: Result = Result { + try GrokCredentialsStore.load(env: context.env) + } + if let file = try? fileResult.get() { + return .success(file) + } + if let pasted = GrokSettingsReader.pastedCredentials(environment: context.env) { + return .success(pasted) } - let snapshot = try await GrokWebBillingFetcher.fetch(credentials: authCredentials) - return (snapshot, "grok-web", true) + return fileResult } static func credentialsForWebBillingSnapshot( @@ -370,11 +475,12 @@ struct GrokWebFetchStrategy: ProviderFetchStrategy { fetch: ((String, GrokCredentials?) async throws -> GrokWebBillingSnapshot)? = nil) async throws -> (GrokWebBillingSnapshot, String) { - let fetchSnapshot = fetch ?? { cookieHeader, credentials in - try await GrokWebBillingFetcher.fetch( - cookieHeader: cookieHeader, - credentials: credentials) - } + let fetchSnapshot = + fetch ?? { cookieHeader, credentials in + try await GrokWebBillingFetcher.fetch( + cookieHeader: cookieHeader, + credentials: credentials) + } var lastError: Error? var teamUsageUnsupportedError: Error? for session in sessions { @@ -412,11 +518,12 @@ struct GrokWebFetchStrategy: ProviderFetchStrategy { fetch: ((String, GrokCredentials?) async throws -> GrokWebBillingSnapshot)? = nil) async throws -> GrokWebBillingSnapshot { - let fetchSnapshot = fetch ?? { cookieHeader, credentials in - try await GrokWebBillingFetcher.fetch( - cookieHeader: cookieHeader, - credentials: credentials) - } + let fetchSnapshot = + fetch ?? { cookieHeader, credentials in + try await GrokWebBillingFetcher.fetch( + cookieHeader: cookieHeader, + credentials: credentials) + } var lastError: Error? var teamUsageUnsupportedError: Error? for authCredentials in Self.cookieAuthAttempts(credentials: credentials) { @@ -430,8 +537,9 @@ struct GrokWebFetchStrategy: ProviderFetchStrategy { } } if let teamUsageUnsupportedError { - let trailingAuthenticationFailure = preferTrailingAuthenticationFailure - && lastError.map(Self.isCookieAuthenticationFailure) == true + let trailingAuthenticationFailure = + preferTrailingAuthenticationFailure + && lastError.map(Self.isCookieAuthenticationFailure) == true if !trailingAuthenticationFailure { throw teamUsageUnsupportedError } diff --git a/Sources/CodexBarCore/Providers/Grok/GrokProviderSettings.swift b/Sources/CodexBarCore/Providers/Grok/GrokProviderSettings.swift new file mode 100644 index 0000000000..425d882052 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Grok/GrokProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct GrokProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum GrokProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.grok + public typealias Section = GrokProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias GrokProviderSettings = CodexBarCore.GrokProviderSettings + public var grok: GrokProviderSettings? { + self[GrokProviderSettingsKey.self] + } + + public static func make(grok: GrokProviderSettings?) -> Self { + self.make(grok, for: GrokProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func grok(_ section: GrokProviderSettings) -> Self { + Self(section, for: GrokProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Grok/GrokSettingsReader.swift b/Sources/CodexBarCore/Providers/Grok/GrokSettingsReader.swift new file mode 100644 index 0000000000..94ca5f69dc --- /dev/null +++ b/Sources/CodexBarCore/Providers/Grok/GrokSettingsReader.swift @@ -0,0 +1,36 @@ +import Foundation + +public enum GrokSettingsReader { + public static let oauthTokenEnvironmentKey = "GROK_OAUTH_TOKEN" + + public static func oauthAccessToken( + environment: [String: String], + settings _: ProviderSettingsSnapshot? = nil) -> String? + { + GrokCredentialRouting.normalizedOAuthToken(environment[self.oauthTokenEnvironmentKey]) + } + + public static func pastedCredentials( + environment: [String: String], + settings: ProviderSettingsSnapshot? = nil) -> GrokCredentials? + { + guard let token = self.oauthAccessToken(environment: environment, settings: settings) else { + return nil + } + return GrokCredentials.pasted(accessToken: token) + } + + public static func resolvedCredentials( + environment: [String: String], + settings: ProviderSettingsSnapshot? = nil) -> GrokCredentials? + { + if let file = try? GrokCredentialsStore.load(env: environment), !file.isExpired { + return file + } + return self.pastedCredentials(environment: environment, settings: settings) + } + + public static func normalizedOAuthToken(_ raw: String?) -> String? { + GrokCredentialRouting.normalizedOAuthToken(raw) + } +} diff --git a/Sources/CodexBarCore/Providers/Grok/GrokStatusProbe.swift b/Sources/CodexBarCore/Providers/Grok/GrokStatusProbe.swift index 8269c0c55a..f1fbf51797 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokStatusProbe.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokStatusProbe.swift @@ -77,13 +77,16 @@ public struct GrokStatusProbe: Sendable { public init() {} - public static func detectVersion(env: [String: String] = ProcessInfo.processInfo.environment) -> String? { + 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) + 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. @@ -95,7 +98,9 @@ public struct GrokStatusProbe: Sendable { return withoutPrefix.isEmpty ? nil : withoutPrefix } - public func fetch(env: [String: String] = ProcessInfo.processInfo.environment) async throws -> GrokUsageSnapshot { + 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) diff --git a/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift b/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift index 79ee2b4d8b..7f156bb96d 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift @@ -1,4 +1,5 @@ import Foundation + #if canImport(FoundationNetworking) import FoundationNetworking #endif @@ -64,8 +65,8 @@ public enum GrokWebBillingError: LocalizedError, Sendable { private static let reauthMessage = "Grok web billing rejected credentials. Sign in to grok.com in Chrome or run `grok login` to refresh xAI auth." private static let webKeyExchangeReauthMessage = - "grok.com billing no longer accepts browser-cookie sign-in for this endpoint. Run `grok login` so CodexBar " + - "can read usage via the Grok CLI token." + "grok.com billing no longer accepts browser-cookie sign-in for this endpoint. Run `grok login` so CodexBar " + + "can read usage via the Grok CLI token." static func isWebKeyExchangeCredentialRejection(status: Int, message: String) -> Bool { guard status == 16 else { return false } @@ -77,13 +78,11 @@ public enum GrokWebBillingError: LocalizedError, Sendable { if status == 16 { return true } guard status == 7 else { return false } let lower = message.lowercased() - return lower.contains("bad-credentials") || - lower.contains("unauthenticated") || - (lower.contains("oauth2") && lower.contains("could not be validated")) || - (lower.contains("access token") && - (lower.contains("invalid") || - lower.contains("expired") || - lower.contains("could not be validated"))) + return lower.contains("bad-credentials") || lower.contains("unauthenticated") + || (lower.contains("oauth2") && lower.contains("could not be validated")) + || (lower.contains("access token") + && (lower.contains("invalid") || lower.contains("expired") + || lower.contains("could not be validated"))) } } @@ -164,10 +163,11 @@ public enum GrokWebBillingFetcher { } private static func classified(_ error: Error, principalType: String?) -> Error { - guard principalType?.trimmingCharacters(in: .whitespacesAndNewlines) - .caseInsensitiveCompare("team") == .orderedSame, - case let GrokWebBillingError.rpcFailed(status, message) = error, - self.isTeamBillingUnavailable(status: status, message: message) + guard + principalType?.trimmingCharacters(in: .whitespacesAndNewlines) + .caseInsensitiveCompare("team") == .orderedSame, + case let GrokWebBillingError.rpcFailed(status, message) = error, + self.isTeamBillingUnavailable(status: status, message: message) else { return error } @@ -210,7 +210,8 @@ public enum GrokWebBillingFetcher { let body = String(data: response.data.prefix(400), encoding: .utf8) ?? "" throw GrokWebBillingError.requestFailed(response.statusCode, body) } - try Self.validateGRPCStatusFields(Self.grpcHeaderFields(from: response.response.allHeaderFields)) + try Self.validateGRPCStatusFields( + Self.grpcHeaderFields(from: response.response.allHeaderFields)) try Self.validateGRPCWebTrailers(response.data) return try Self.parseGRPCWebResponse(response.data) @@ -233,7 +234,9 @@ public enum GrokWebBillingFetcher { || message.localizedCaseInsensitiveContains("expired") } - static func parseGRPCWebResponse(_ data: Data, now: Date = Date()) throws -> GrokWebBillingSnapshot { + static func parseGRPCWebResponse(_ data: Data, now: Date = Date()) throws + -> GrokWebBillingSnapshot + { var payloads = Self.grpcWebDataFrames(from: data) if payloads.isEmpty, Self.looksLikeProtobufPayload(data) { payloads = [data] @@ -260,21 +263,21 @@ public enum GrokWebBillingFetcher { return (field.path, Date(timeIntervalSince1970: TimeInterval(raw))) } let futureResetFields = resetFields.filter { $0.date > now } - let reset = futureResetFields - .filter { $0.path == [1, 5, 1] } - .map(\.date) - .min() ?? futureResetFields - .map(\.date) - .min() + let reset = + futureResetFields + .filter { $0.path == [1, 5, 1] } + .map(\.date) + .min() + ?? futureResetFields + .map(\.date) + .min() let hasUsagePeriod = scan.varintFields.contains { field in - field.path.starts(with: [1, 6]) || - (field.path == [1, 8, 1] && (field.value == 1 || field.value == 2)) + field.path.starts(with: [1, 6]) + || (field.path == [1, 8, 1] && (field.value == 1 || field.value == 2)) } - let noUsageYet = parsedPercent == nil && - scan.fixed32Fields.isEmpty && - reset != nil && - hasUsagePeriod + let noUsageYet = + parsedPercent == nil && scan.fixed32Fields.isEmpty && reset != nil && hasUsagePeriod guard let percent = parsedPercent ?? (noUsageYet ? 0 : nil) else { throw GrokWebBillingError.parseFailed } @@ -295,7 +298,8 @@ public enum GrokWebBillingFetcher { while index < bytes.count { guard index + 5 <= bytes.count else { return [] } let flags = bytes[index] - let length = (Int(bytes[index + 1]) << 24) + let length = + (Int(bytes[index + 1]) << 24) | (Int(bytes[index + 2]) << 16) | (Int(bytes[index + 3]) << 8) | Int(bytes[index + 4]) @@ -337,7 +341,8 @@ public enum GrokWebBillingFetcher { .trimmingCharacters(in: .whitespacesAndNewlines) .lowercased() guard normalizedKey.hasPrefix("grpc-") else { continue } - fields[normalizedKey] = String(describing: value) + fields[normalizedKey] = + String(describing: value) .trimmingCharacters(in: .whitespacesAndNewlines) .removingPercentEncoding ?? "" } @@ -350,7 +355,8 @@ public enum GrokWebBillingFetcher { var index = 0 while index + 5 <= bytes.count { let flags = bytes[index] - let length = (Int(bytes[index + 1]) << 24) + let length = + (Int(bytes[index + 1]) << 24) | (Int(bytes[index + 2]) << 16) | (Int(bytes[index + 3]) << 8) | Int(bytes[index + 4]) @@ -363,9 +369,10 @@ public enum GrokWebBillingFetcher { let key = line[.. (HTTPURLResponse, Data) { let url = try #require(request.url) - let response = try #require(HTTPURLResponse( - url: url, - statusCode: statusCode, - httpVersion: nil, - headerFields: ["Content-Type": "application/json"])) + let response = try #require( + HTTPURLResponse( + url: url, + statusCode: statusCode, + httpVersion: nil, + headerFields: ["Content-Type": "application/json"])) return (response, Data(body.utf8)) } diff --git a/Tests/CodexBarTests/GrokSettingsReaderTests.swift b/Tests/CodexBarTests/GrokSettingsReaderTests.swift new file mode 100644 index 0000000000..03e56b447c --- /dev/null +++ b/Tests/CodexBarTests/GrokSettingsReaderTests.swift @@ -0,0 +1,89 @@ +import Foundation +import Testing +@testable import CodexBarCore + +struct GrokSettingsReaderTests { + @Test + func `normalizes a pasted SuperGrok bearer and rejects cookies`() { + #expect(GrokSettingsReader.normalizedOAuthToken(" Bearer abc.def.ghi ") == "abc.def.ghi") + #expect(GrokSettingsReader.normalizedOAuthToken("Cookie: sso=abc") == nil) + #expect(GrokSettingsReader.normalizedOAuthToken("sso=abc; sso-rw=def") == nil) + #expect(GrokSettingsReader.normalizedOAuthToken("xai-mgmt-key") == nil) + #expect(GrokSettingsReader.normalizedOAuthToken(" ") == nil) + } + + @Test + func `reads pasted SuperGrok credentials from GROK_OAUTH_TOKEN`() { + let env = [GrokSettingsReader.oauthTokenEnvironmentKey: "Bearer pasted-token"] + let creds = GrokSettingsReader.pastedCredentials(environment: env) + + #expect(creds?.accessToken == "pasted-token") + #expect(creds?.loginMethod == "SuperGrok") + #expect(GrokSettingsReader.oauthAccessToken(environment: [:]) == nil) + } + + @Test + func `descriptor exposes SuperGrok token accounts`() { + let support = GrokProviderDescriptor.descriptor.credentials?.tokenAccountSupport + #expect(support?.title == "SuperGrok tokens") + if case let .environment(key) = support?.injection { + #expect(key == GrokSettingsReader.oauthTokenEnvironmentKey) + } else { + Issue.record("expected environment token injection") + } + #expect( + support?.envOverride(token: "pasted-token") == [ + GrokSettingsReader.oauthTokenEnvironmentKey: "pasted-token", + ]) + #expect( + support?.envOverride(token: "Bearer abc.def.ghi") == [ + GrokSettingsReader.oauthTokenEnvironmentKey: "abc.def.ghi", + ]) + #expect(support?.envOverride(token: "Cookie: sso=abc") == nil) + } + + @Test + func `classifies bearer, cookie, and management-key secrets`() { + #expect( + GrokCredentialRouting.resolve( + tokenAccountToken: "Bearer abc.def", manualCookieHeader: nil) + == .oauth(accessToken: "abc.def")) + #expect( + GrokCredentialRouting.resolve( + tokenAccountToken: "Cookie: sso=abc", manualCookieHeader: nil) + == .webCookie(header: "sso=abc")) + #expect( + GrokCredentialRouting.resolve( + tokenAccountToken: "xai-mgmt-key", manualCookieHeader: nil) == .none) + #expect( + GrokCredentialRouting.resolve( + tokenAccountToken: nil, manualCookieHeader: "sso=abc; sso-rw=def") + == .webCookie(header: "sso=abc; sso-rw=def")) + } + + @Test + func `selected SuperGrok accounts remap to oauth or web, never an empty oauth pipeline`() { + let adapter = GrokProviderDescriptor.descriptor.credentials + let oauthAccount = ProviderTokenAccount( + id: UUID(), + label: "oauth", + token: "pasted-token", + addedAt: 0, + lastUsed: nil) + let cookieAccount = ProviderTokenAccount( + id: UUID(), + label: "cookie", + token: "Cookie: sso=abc", + addedAt: 0, + lastUsed: nil) + #expect( + adapter?.selectedAccountSourceMode(base: .auto, account: oauthAccount, config: nil) + == .oauth) + #expect( + adapter?.selectedAccountSourceMode(base: .auto, account: cookieAccount, config: nil) + == .web) + #expect(adapter?.selectedAccountSourceMode(base: .auto, account: nil, config: nil) == .auto) + #expect( + GrokProviderDescriptor.descriptor.fetchPlan.sourceModes == [.auto, .cli, .oauth, .web]) + } +} diff --git a/Tests/CodexBarTests/GrokWebBillingFetcherTests.swift b/Tests/CodexBarTests/GrokWebBillingFetcherTests.swift index 8afc1ff9f6..e6e036c593 100644 --- a/Tests/CodexBarTests/GrokWebBillingFetcherTests.swift +++ b/Tests/CodexBarTests/GrokWebBillingFetcherTests.swift @@ -24,7 +24,7 @@ struct GrokWebBillingFetcherTests { @Test func `provider exposes cli and web source modes`() { - #expect(GrokProviderDescriptor.descriptor.fetchPlan.sourceModes == [.auto, .cli, .web]) + #expect(GrokProviderDescriptor.descriptor.fetchPlan.sourceModes == [.auto, .cli, .oauth, .web]) } @Test @@ -214,7 +214,7 @@ struct GrokWebBillingFetcherTests { } @Test - func `web strategy preserves malformed auth file error`() async throws { + func `oauth strategy preserves malformed auth file error`() async throws { let grokHome = FileManager.default.temporaryDirectory .appendingPathComponent("CodexBar-GrokWebBilling-\(UUID().uuidString)", isDirectory: true) try FileManager.default.createDirectory(at: grokHome, withIntermediateDirectories: true) @@ -224,7 +224,7 @@ struct GrokWebBillingFetcherTests { let browserDetection = BrowserDetection(cacheTTL: 0) let context = ProviderFetchContext( runtime: .cli, - sourceMode: .web, + sourceMode: .oauth, includeCredits: true, webTimeout: 1, webDebugDumpHTML: false, @@ -236,7 +236,7 @@ struct GrokWebBillingFetcherTests { browserDetection: browserDetection) await #expect { - _ = try await GrokWebFetchStrategy().fetch(context) + _ = try await GrokOAuthFetchStrategy().fetch(context) } throws: { error in guard case GrokCredentialsError.decodeFailed = error else { return false } return true diff --git a/docs/grok.md b/docs/grok.md index 33ca5bee5d..171121888a 100644 --- a/docs/grok.md +++ b/docs/grok.md @@ -13,6 +13,15 @@ fetched via the ACP JSON-RPC `x.ai/billing` extension method over `grok agent st when available, then via the Grok CLI billing REST API using the local login token. The grok.com billing gRPC-web endpoint remains a best-effort fallback. +## Settings source picker + +- **Auto**: Grok CLI, then SuperGrok OAuth, then browser cookies. +- **Grok CLI**: `grok agent stdio` only. +- **SuperGrok OAuth**: `~/.grok/auth.json` or a pasted bearer / `GROK_OAUTH_TOKEN`. CLI-proxy credits, then bearer gRPC. No cookies. +- **Browser cookies**: grok.com Cookie header / Chrome import only. No OAuth bearer. +- Token accounts classify at fetch time: bearer → OAuth, `Cookie:` / `name=value` → cookies, `xai-` management keys rejected. +- Selecting a SuperGrok token account remaps Auto to OAuth or Web so it cannot hit an empty `.oauth` pipeline. + ## Data sources + fallback order 1) **`~/.grok/auth.json` (primary identity source)** @@ -49,18 +58,17 @@ The grok.com billing gRPC-web endpoint remains a best-effort fallback. without either value represents zero usage. The reset timestamp comes from `config.currentPeriod.end`, then `config.billingPeriodEnd`. - Plan name does not come from the credits payload. After a successful - auth-file web billing result (CLI-proxy) or the team identity-only path, - CodexBar GETs `https://cli-chat-proxy.grok.com/v1/settings` with the same - bearer headers and reads `subscription_tier_display` (`SuperGrok Heavy` vs - `SuperGrok`). Cookie/gRPC fallbacks are a different browser session and do - not reuse the auth-file settings tier. The request uses a 2-second timeout + auth-file or SuperGrok OAuth web billing result (CLI-proxy) or the team + identity-only path, CodexBar GETs `https://cli-chat-proxy.grok.com/v1/settings` + with the same bearer headers and reads `subscription_tier_display` + (`SuperGrok Heavy` vs `SuperGrok`). Cookie mode does not call the proxy. + If the proxy fails, OAuth retries the grok.com bearer gRPC path, still + without cookies. Cookie/gRPC fallbacks are a different browser session and + do not reuse the auth-file settings tier. The request uses a 2-second timeout and `BoundedTaskJoin`, so a stuck settings call cannot delay already-fetched usage by 15 seconds. Settings timeouts, request failures, and 200 responses that omit `subscription_tier_display` all drop the plan overlay and fall back to the OIDC SuperGrok label. There is no process-lifetime tier cache. - - This is the Grok CLI's supported token-authenticated billing backend. If it - fails, CodexBar continues through the existing browser-cookie and legacy - bearer fallbacks. 4) **grok.com billing gRPC-web fallback** (best-effort) - POSTs an empty gRPC-web protobuf request to `https://grok.com/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig`. @@ -95,7 +103,8 @@ The grok.com billing gRPC-web endpoint remains a best-effort fallback. ## OAuth credentials -- File: `~/.grok/auth.json` (path overridable via `GROK_HOME`). +- File: `~/.grok/auth.json` (path overridable via `GROK_HOME`). This remains + the preferred identity source when `grok login` has written a non-expired token. - Top-level keys are OIDC scope URLs. CodexBar prefers entries under `https://auth.x.ai::` (SuperGrok), falling back to `https://accounts.x.ai/sign-in` (legacy session). @@ -104,6 +113,15 @@ The grok.com billing gRPC-web endpoint remains a best-effort fallback. `principal_type` is optional because older auth files do not include it. - Tokens are issued by `grok login` and expire after ~7 days; refresh is handled by the CLI itself (CodexBar does not refresh; it just reads the cached credential). +- If `auth.json` is missing or expired, paste a SuperGrok bearer into Grok token + accounts or set `GROK_OAUTH_TOKEN`. Cookie-shaped values and `xai-` management + keys are rejected. The pasted token uses the same CLI-proxy credits URL. +- Settings also expose a cookie source (Auto / Manual / Off). Manual accepts a + grok.com Cookie header when Chrome Safe Storage is denied. Auto still imports + Chrome only. +- Credits `subscriptionTier` maps SuperGrok vs SuperGrok Heavy on the plan badge. + SuperGrok Heavy with no `creditUsagePercent` is unknown usage, not 0%. + ## JSON-RPC contract diff --git a/docs/screenshots/grok-supergrok-oauth-settings.png b/docs/screenshots/grok-supergrok-oauth-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..1266cf76ab7b3e817dc698fbfd1faf9f25ca38f4 GIT binary patch literal 292823 zcmeFZhgVbIwgyU-E=@ps6_8$(8j46$5m2O8L7EDoL+FTr)QCtENCcGLdkY;xO;mdC zgbtyE4lj;(&pCR2cf3E~jhnGEveNcid(An&HRt^1+L5~28Wf~Vq&PS@6pu7jpX1;V zKE}bh!9k3N{e<6S-WvOY>;7Cr1*ZhTvW|VDYHRe!?&(t;0c@KX2RFhIhv4^5uumrJ z69)(XD=rQ`_7(T{XJ7ID^)2D!ulWCJ-{AQD!?`U73mhC}oJXn;Uijc{!9Xr%V*#kC z^C(shTo?S~fS)R_=km-yJ2|1Fw^0Kp^#YN(8+RZwsamfsELOn1FP`!~(*KmK^ht}w z9%9o{VLO4&x?`dl`I-MY&z-1modLMS!8nnuu*~GCUH2WrFHdQB%J1>3oLF=e57iD|Kn>8JjsmUscqZ`fcSa+ z{lJ_c=HVb}D%`)15XZ+H&{_wOqWu?9kkV6N)|Y>tNNk8iX4J1X;=2CMuKiisr$EItR87)sW0y~;4S@vdNroRLTEg?J^RA-i4To1mY*td7 zG&hfrj#w4_efHgn!c0E@CDsr$&BrEoQSskbb}~S0RetOnfN1dcU$P(kQtfVGDg>&^ z|2cBvf|oCc%k_J)oJ@4ST+$kr#&c62^xhk>ujy z{F#frRUbe&3J`rjWMc;cFwJiFT8G$A045dLr1z(uono@iI3${DOOd;cCq6tMAxLXI#WHo%^&I~)yA)D926(C7vxdG~ zSoH{@p;30@PN5zr`$9jT2-WPQ-ZmOOZ#+|)ZE2~mCuCOq!kTcE;XOkK?)tTUFY2OZ z%6Y2NUX=bMDk`cgn%>3%Rp5@2vo3$YOSsdpDY8m7IXS75F7qI8fA8^l)7jaXO253k zyiSH(7vA~C#%qmP21Nm$6sA1E(xszOQ|IFRf-Jedmzd1r>X8AWwZB#A?r*#83f6BT z{-C^0JpTmxIOp91?s@TV*}r}wS8P|oz_az-nHlqd%bkLPjR-d1yqcOD8I}$* z-4|hG46Fd3;Jd+^JW(ES31AaWlvjJUSNm46ai!hC>fle8+jBT}>?zbf>)(?dYRFFr zQy%{bIa<_Mc({vKX#E==qLgiHI?}t<&DPL`P{~|Zc;jwG16yzGHK68lqd=hQ7_%); z6G^Z?m2y0^esvXS5(*I&6a`bvGaG!XK#ZN#opWfY)08G98rah8Z@_YGB zc2fm**TC5(kf&#L(E-p2NLD_}2HKTs+>w2AmCB*XLwkTi1}#SNr9Az>w>ZGmsTXNz z;TPMVN}M7VKcwc64%5gD$<_ z%{zP{5XP&MVP5;;0DD*i_KM*BW( zE2R@cE{F?7T>?J2usr*f9C&sBJ@PoVYCQHC+&_QIyLo&%5kjedy&@V^pIIJ4cVad& zH+KC?$70d{2fK^P<=mmii1Ht*8?ydXc|Xv1PN~|0!0r~VheX?+SWj}I+S9$JxAelX z9}#vE+AsV&K~JCFw}!0wUWl@gzC>y)S1MSD^TVCNsEhI?IG9aVB}DTqQP6NydRS7v zbt52I@MP9+cW17J{j$Yt@QVf;=oSOc<5t(;?HzB=?ftr^KLQZ*#L8;=Bl>kK(o**p zp}Rio*J~O|a-e-4RIye~-Rtkzx%-dpTSZ}KREOG#P7sev*B5Vm!(4>yj*ce43zGe= zt<6Xxm7_qxb${Y=XVesET>V;KhNZ1)C*>@DC)L9j9>==dR0;dRfgNwn+}Wt<=SL*t zQ|yx$R71FRYfG#@W&(CDkLT=r6_WiwYtz~Rcc$xqIxsq)I{4(W+)}(I(`b+MS;U@L zI65hn@bnZwdVMrzxeJ(Mj8xyFtOK+MW>Wv9uOE~jkvJ?HKTj9XB2cvwAh9xpN3)U4 z3{AZ$jYU!ez_)!=hwYE#!#%V%JUu0WGWr$y;>hAdzrK{nU;s&)M zryO|BFjGKwIS=A|aNFFbIDH`NUqJ7IkDbgpoSp(Kx_-S!kbdW|s6iuyR+hcgXW--n zXL}hM93Z5!LZYW~Sw1VNStjWL{}!qMagS_}Kwg0s z7BcU02ES0Z1QxzMsCd=;sf3Kdr9k6gcf6T3=-lC6)XZht+=UX=_$y_MO`$Yt zpz4AjexTOoZ460|y=qJGDd>nQ47`rhzIqB9T{#OlWOMaI6InpLCn{{2rQTZQDv+Q1 zZy*};zRG}q{WTwAbOf(pP|^YcJ*>D2h&je4`fl3Bxp^f`mzp-#)yWt5&BmLDx;fw^&@Uy{?GZB>PY z5;zmG{H)-2+z8kUA?jEVyFYKHYZmukBk&~P+0BhBevEZ4_!V%Q784>nHP{2*OI>-GP!kn#8%DZroWN7f} zz(9=UwG)s%hQc}!Y3nYSRBAL{^&^$rNGzzWfr9z@Iw$)9p|fF85Qi;N)aPww0HUva z>pEBI{BGuchA{f$ai-N?J{+2tU!>hBl4`)Oa5ItrFE5XqU^!h$Mdg|fm%479|OK01?bk*jQ3nDA)B9{r{~~U%avzMT}OpBIl!Q0YIECW&0Xtkx4E(7kyRBu1^Er9 z4`O)b=3HC3#9Gi?{~feakT{}0;y?emaeVP=FTNvX4mFqt&ozS&w4NTIj(27>1P2;? z<$Ituun3mf6J}}WSKbOHA8cALPRZy+EvkT)$fuP06u;Nse;p_3R3DG7+}*fGD5y`S zX_XUb1;EZ(dB$(S51I=L$x@$A`+0ReEG(TXdwIIQ@+3*9jTbDc@NF3d&CIlsZ#J;s;;hOmDxkTDTRfP zOZ9I#b--+3aJOBfUG%KRtWAn|{>%Bc$eI7cq zp!weONWJwOziWH-6;Nllzr{B-q1w|kQ#VBla6TXz2Kc;w%Ty-&{a?QN=a0b*_=Y6W zde43jUd)ytCS&?p&jM4V%`r|W6w z{M_7Ff1Zp_D4H0kUo|&3C-CVkIzlyh0E$bVz+26NmKFx1zL>Y06QfZT`vfagTBSi> zTS~$C?`PQk%YbvUnZqB;ry7l`c5R%lbtOQ}m691uXUp?^_*>m5bX9G&cz9UWJcSiSp8AkdPZr5%G<@mWVjdzFFTbARr>hkIRETqtbT zACB?aJy4=86t$-=S7MW-4>Y^b%jd!P=ia;6D=aK9F*QYBc$cVcRH{K;>8w11ZPC8Q z38eb`e+PmIJXr7SQE-D9f$DrWHGhv^0Z;5Vz7WD$9j)_pcOzal#Srw2^YXn?7VYzK z6q~*!eXQGT(Ra9|QnDmFNc6EYC4>9;J1BYB25}i&Uv*g8Z{`$VhIoDkd5BB4+Yv9O zN0RzBNl+Y-Am)tc)sgO7UslJjP5)*2)sMoz#r!^aIpC340TmXSnIC-pT06K1Dut8y zA}4)l#YB_u_f50dy$nDz=GxW_NZ}rp?b*1&8zGNy2R0ZhRwKAEva-f@O8$`nkb4y^ zAK^|bvBI;P-ybWgAeRV(`*F0-i}0*|;e_bK4^WTm5o#xB6%w<|-zqfxLqu4qkYWQ8 zy$AZXQNhGo!XY#TD~nm*W6yD_6{J%KtMJfDKon!9~YI z*K7qu{~V1Z1RDs&T$2+2yN`c2S`UABcDbAB`_Ge#EnzPnc~TmT^PdU%*B`Zlu#^3N z-Tf`U|C`MJQ*p^iblP+uCSNof{Vx>wH!f)5hM*n$?>YoW z{pnEs8!JD1Iv7PAbMLsz7Je-&8CCA$ z73AdxS!!hyIIgB>-KBS?w)wR^cG+z4Ge?}xuS;L<@8uOcLTOgq+Ns!Ys1XD8HIS)J zW={8O5!kx#A)}P$e)H$ORJo||{)-T0nOlpVLTZwC?=>FG2&9x`XK^3d| zYh6nqkwrl6#|0bs*!@Q%^8{he*U1HBunHg8p_L;HdiBpcL%i175eW$We@_% z>A#r&Ts3~TQ)&nhP^I1D1u)P;KtSMA?>G47Q(l^(nuk#1jytg`sJ3VP8#c?YV#RD*?Ja zqNuTPa8Ss>`^kG0qWgHxOj75ouO+m(6B82^TSTR9_rH?V%iRW+I2YdkZ_(;L$6MFB zv6f%gp}8iPs6g?nOR!5Wd| zTX(Dqxc*C`c09PFa`0k+$j_CPm30rMm!e_!yG7q&VC>v^&ngyZ7yXe11%*ZFbkSXI z_4)g~?B9JeX#Q)-4nb0fEXpjZ-g8e{q#fIGAMVQEj#|DXI1N{MS)w{?I5teBf~3a0 z51rRqNU{3afQ`og%qb`n*si^3sH$1Kti?u4pKjg=?Wzb`ea5H(zOxI*EtCQPMVYWKa9JD6 zzLTTc4=H8UK&S@)_=i4n>Ehvy>%uH3Gmpm8iU_&}gSmKi`Fs*sDgAj9jHtMVE(tg{ zB>r1r>f}OL)QA0N(c>+?A^{Rw%JBz$C;F|<;C7TX6fDYYaoPclj&_&01$}1n-&d__ zB}9-;u@^Hc5g{}*X3wiWW`FPXP4L)eIx1S~^^J^w8hi>lZmq`aH!K%C*hzU}{8teF zKlZtbbzFnoXBny=|D!u)g6;>{91}~~XZ`=>J7;aI-yw|(cvkpd!ukCc>&N|Hm;av= z+bSK`)F*%kCBrjN)KLOByWjHW2SP;^v#b8glZoYVnMeTuSA@aQ38zh-+-c%ZH$OE& zbELHgAso0GQ+^6;rDLcgrdViYk(tyRV_yp`s(g|E@{;Qqe`k)!y&pK$xe`zvzDOEe z4G#~e!ZaOm9R?`EKLt&^T8Mp<^!gu`+Kc5$pe#z}3bq6QHcEQW7v8;hyrt4&)wA{W z_2V^f;Rg!QZ*Fcb(Zws-!k)GLH}r^A6MVDJN!qOvoS`1U=H}Qo^IsJ3Y-?oMOYm=K z0wBuT5giM3f`;nt3mY3>zI=IIu(rOwy|s0lB_;y^`b6FX%(Y+&tr4v^&yYYN(vHvP zP7xKh{nq6^5~(K{bEh+HZ4Yv@-gwt9yHkvnXhZ)B8L(?(>us##45l8|>J4veYRa-6 zEi&fQPPYp2ebf+Jb9^3PZf+i;JYUkd6EGh_uaY zb%K{ZsB#q=l)QFyRQIv5VuvRhBBN&5oGeXE8*TfO<}Qlo1ftcEP^G!i+12AyDva6K z)X_nyTgjV=oN|K)kNNG0Eyx0>|OXh)iBi`{mF{s+t z)8a2n<$c(iAsuRT^G3P!)c6Ra;oRHeFXY?nZpF)&9`$Wckp zp%u;^p^}+S7HPSdf~gvupewD5zZY67v8A)+wJGoi0AjA$8M{PMG zd`Fs#xoyT80t+1=SZ<>x)(LbVXV&L}=5?R-{CUeBB;$TW6JGi&2+J;F8Kiw-&^p=& zaEo+l)A?QjtG_dUbmG+7bMVyCMH#*#`}2uSv^6t!Jva*poSt_iX+ahC8d~>Y^VEQg zJ#G}Dnq4W7h52f^8dgcjdX)3L_k6eIdeib+&e0^!S7W^8Xf&5`n|i^q<^7!NF=Q?&=l9bUOLU48HDspUO}70oW^b;8-?EoAZ${pONAR^ zd+dk!lgCOt?y)LLh+EZf>o`3Ezt2@JaOGAU@W2f-6(Oyy&F)*70i`<~^iT=8IU=UQ zrea;6cZ8*oWzhO&JpUc7N7cRxldqVjt|bO7&Sn}C-MG;nuc>cK*GIqp#5OJnp(_z~ z?DtkIyzb~Uz`yglqF!iq{TZ|Bvy+8N9z_yqkx5mSrvALw6^V*;%=aMX59I)TyW4H9xC9MwN!IK}Tr(|DtnwAi?HO*=^ ztB4G&ASMYIe4FYHy}dlJK3335RAQojV$Yf7<-XyJx%pKSm;-dn=e&Cgh2V6V1sD+M z-h{_zYZ#B{*c22N_RLcfq|iTDb{E@RXmz!46ei@=>-F1(Ni#)q9Z_GPRWF7@Bf5Xt zu2BE!n}i@g<7`akIjG{cx8?cASUIbXT$x^r7rMX4o=e=g82=8~e1yj>e4c)7h~;Odtu!3vOCwdux8>{+)8G4( zxWcpPR(kw&e&#sCUJluS;7@d)kA=g6NLOcsV`kMcS=#|x z`{MS}B=~`uwY9Upf-6JhiwoR{mSg0#|CK+VBl)ox9lZ*Z<6v*l3G~{%`-h6bx?(BBVxap9J}&1N#k{B zLH1*CwfN1FEdOR_<|Xe~Rn-pROt}Zj^gddA&YZZtZw%5wv~x0W%g-M2`%8D2f35}H z*N87*N9VO{HAz}pK|OAryL`GH5BFVSp`svi?aB233qI+5ElFvm!}oPB-&1uVZA;FX z92-0`cYvm==arpT+uUT6;wH zLJsSuoGg|5$;fcUQji()aG3@B3Xg@J`0Q;bC!;P|ss*7Kcs}I&iAR1#1t~+oRiB{X z+WkV3x|+gc6)E{)r61CtwfDDDTC7AMglJVEnyBHCk+62$;Q)PoI?iL@gbNvA$enpg z-7PG~-J|@X*;%r zSQRO)QLq7A_K;Uw$Vp^%#091yC$jv9{$JjYE*D$k<=(o>#yPm*JJ&HQK%sUPPKsnWPHw-_s)b19PyYH(Mm* zg^LG~Yat%W*T0M9L7>pu8aGN!U}sn7I}amh zCD{WuiGj5A2&0o{xgE3OP1)L87zan1HS1+TE?E!^5M2 z>rX6A>Kv%2hT{*Gz?Aa?I#<2bzK4b7C6(lCVcKux<>X07bxln-32JlIZ=Jj&B-_gq zHPd2(pQr-R3DAqqP69^RH9whKDh<~ghN8!2Ynd!bb3++$HousaOP}?G+Un|~#<@!o z3CR3)XlOYZLxYQj7y`3?PY+J8}!@l0=9B(ukc&8GY_*bqYgju6is*EyiXJ)+!F8TRrWgPk%vtjH$rrOUG z5Yoq)ESkz1)de$7W-c{K$B54}RF`4h_>|3U9Z}z5`xG%^lPmvUkB^}207K>@k#xgK zhpVElr_l;KRWPd75hbe4rXN#o7&BDZfNrz%LUF&{aW%Wbo+AGB+mbmUN-5mh{ zVdtGA`uk6NA5mdL9D=6&rdxNT`-G#_(S>{vK=_*~h0Q)=`<)KVpq=-{912bxwE9-E z?Z9`7VmX68(~g;$`2#G1y5->FRHr6T8BNUJ`#{*3%X^$ikM3ejb*s~NF7!yz8R!e`bkLExL_z#Ynlm}=;Yja&S`8fU zGaaSN&poH>+T2|vM8SyC0wE=zwI8ESQ!XjT^#QT&Og}L_KzatNIrx>%*EVR=VS)`j zj^k?AVT0Hy3d_>SbU(h@8xsqB)f<2-}j7Y-F5Yf(jt#kzkN+VJ`{BgT4D(#V1 zE0z~4>yo)>LMzhmxwRUI)0={@KclFac${!KHwhj#n=7>4!ztR!n{5!!^NNy>^1psm zEh9G0s_zy8lE^3yIhdX|D}RXHE9<7cNeQ>CBo2B-9@A2)m+e&O&E&{Te9(b1)?`1d zbH6mlGT1`*4%6mNeic0i(lvUnB-no5&|Yma_LX@RrZ+M1@Zp<;i60nq4Q=V#mFbYF z9nf-TTL2o8^tNVdg|PzvLj28Fx|1bXFKsWjRFe9&^eWSIJN3r+cz=IihRp8p=FvGU zko{bTMe$Xfa~#AO1H&@d#5VgZ@^0_fgEep4r*fn%XNJg*v&=G(ej8%Eb;ZzpqUQfI z!-DG7)j;ZyHWRD1HlISbVc>N3YNr7>RH68>{V!pAgdIgTwtM5ZxI4r5FnlXoGLr(5qyGs6B%k?>Qlt*3=C&dc`HI9Y8Zjz>m zTR0tM_#s=EF7JwO>-|8NM`VUCY59U}nK`3oAfEeL%?sj)4cBZX?)W%F9P5-fi>xof zU}PEm67rg=m-nm@_AR|@17P;CdOXkAZt32;u1%jFZr`9wLCd!3L7mzkbtNStzv>RT z!^!ygpe{V(Ol;8#4H-r_*kA4R_w`5e23O+HAEnzqlb5=HKrDFin3$P}Cw0IdFAr~k zvcG)P*%-qJDF3pd;T=DqG z>srkE?Z=|4)|EXpAnE8LQuW=ZRMmMJF0>9L{PCYKcw$ClWw;u2ynCg9C{wyzf1&_` z`D9w#^xM(uO-(fM-TzGX-VUWQQaf|>f=e%XK)uo!$cq{TIlTl<&2L?9lOoUz+b9Gm z8yU+GOnqa`pL?89kDDaCVX=P4^J$3X`L889+s{6;n-VlLX27T;nMz7K!e&>ZdCH@> zjzHgqbWPjA?P!MMmeaG4ZqSoA0uR}@ePx+it#j2*^v*r`KZ}4Ym0jKMynbDY5C6e?SOSGwY>j2T zHDUBGveQQ##UQl{?yO$3i=Umps&J{}E_mZVz~$xSjAoIly!{lT#ilLRr~8-Z$2J{c z)hzxXyvcZ4dD+xd#0kQ?IkdsPfGa_pmV~|6-2W>|Rz|WsT-K1T=~K#c*2jwv=Ci#% z1G;`~{SM|&&`U~JXmHz;1Qw5keC@G6k<34iM645=Te6ytOI%wqw1}j*X;->9XCgqH z_l8_g*09w!vDCQsj!8{Jtv!E67yD@90}bY0#fH#rTZ*#F=dn71eTr}>l6K&HqN*U1 ztJ>fBXw0Gxo}mcsC2zPCGOm_xV0SHRao}UQ8!u<90H9(gUN(QjV5kbUI3G%`QSV3H zE1sMlsu7eNZaaaPNMzW+8PA4J zT?X#sWnA;C+1CC%q{_Q@lBg*&3?)(*QIVOMF71>`h~Dy3dTz)m?zSj!t>(!L+eX~z z5c;ue4iG&|WZ0>ZaH`(|wFKog3AH+d?6liJti@C}mU~sXmJbXF{2vHbzY099L;|&q z*Eh~`Hp0I*>$0M+Z+E!onf(`{8;y``RCI@KR8O74)@xQ@}Yx|=OeuyamyL(e?2}W$rWNvbhSaExi$cru$%4c} z7jKlCNA-3eGGdj|Po_m(Lr$k`-Ox;ZM0-lN$H{5?JxPkB{Y{ZGkEnpH9oVs=|2~>m zxmD7sZN$UQ&e7nQY34AQPJWREA*XmH3m`Z$LOiOtYPQzMBNr;a_2Z+Gd;17yr$G^Q~ug5Kqs@`;0@CaxM~cCPIOTak@4NlKd}%M;>hgWIwLJ+tF@+lCrkGQe&3 z}E3MEZD1IlyxvXwJfii{d_M5s`%DaO86F$Obs2p$PI0p}UqJ`hzXqxS^_ z(qA@;KQ@7UBl(d$39m18Swsc+rG&-z*-f)9H9CH`X1^xSM=<=49-XaC5N_jt+bJJiFi4&+ku?bDqDILD>Lh z?I=$4>7rETiYDYj1(GPCtq*20M5p3Jct$s z*(0ZbEtfjin?V#|50^8SwusB#jEMw~<4rf|nk={^&AETd#aVe6Zhq*K^D%HOGXmmw z1*$2{)r!Lw(H<6BUYY>dmKfm`*R*`vWirs3_@SSek&%(Q#i5d8Y{@M{%L$?WR}@Gq zd%Ju=^0e#Whe=&M5y;^^KaGo3iVx< zx53y0$0uhIH!*9-@>}{np|}@lI2&95u?kXMm$ch93y+k9Xodl{o`4Sk25$=3fTv+9 zCGmMB1!sF5lye{ys~uxQL&HPP%H5gkOL&m~#_)PllAXx1W8Le)4ONM@j-GL(d#ZG8 zbG7?%Nq2X5k(x~FLPvSY!Kwr_&uOY=#``)zW}))CeJkoZbng06UdpL`FHYwQ8xp5D zf73H-7s7FBjSq3)3>(dwCE3z|z3;o8J#L2c^qd`SX4nBv=0ms2k2~0}%9^INhBp3O z$5YA)ZfkvcnyQPbXA%F_Cn)fNudDreclqsQnGDU&a^ZOVj$b)Xy+2+j8d}_C713Bo zE5md@ob3}fDwCng-_^N6v-4h%r6<*VWBg&iv8K|Qo7Ykxko}gmXs|RerXL0)gN^MI zn60b1`_~o~lh_g!-wQc3ve;#kUit{c2#t6S$oFC5DD-trQctxU5Nq%$B=-WmZSz3~a>0{26!}GKAwfllZ zMw?gY5HL3UxC*FA3Mfi=A%s{ zglaDAg5R{{J&SIR6ZhG9kxqLuBa2)YY z3y%g+T9LDHxGot1X6vmlGU8e|@{2FXu0cuII@5nN9J30tsDDj|WX++WWRpAG5G>uZ zT6D|uYKet8Fw zkO8pdYWo_|{mLDM@P0ejytI3@v+M9}j9u%d?FZG?mD}T>PrtPLGUErr>E*?7NLzeW zNEENz5AN%aybXY_XcNOU@AZYIzy&LFMPY|DIxnY*#vPEG6fg7*kdPU%+w3?uS88i( zE4QGD7jxHF$1O+2z(MGksz)d`H+Q`dbe64j5~|e75KO}0wmNu)C-*1cY{!DzT`S~f zUTHTtq!ScEy`G=I=e;{SD3gHRnCF-hJ00hWQtGZ{4>+WLIA*I0jf=l3|W8wAWw3Zp*x^W3VccvvYQ zm~?-X;?V3ivB65a=fpD3!j-u}rT6f7jz~&LA>xxLovv{R!Pt-@_4)knW4xYfP?=;weuw;g5}z`{g6-Lv*3#4|1d$ z8XIeyVNRQ)PUG8TVU=gF;$jM0#47?)DC-e_lu@wJy69@;d)ZUu^(GX|s&qBHq*Hc- zlPOlrz3m&G;lfF)#1*UjESBHFFNK*2M8g&1&WLWZY=Mv32sl1J;ARv@DTv*77-PE$W933R`U~{OgL8Ax z+x_xW3c3ft*vmdE|cfP;?IIBfzQ5IgVMU4y{#san~&Lne?_|gq7R2I0QKHUdFj#^FCnfh8>ABmO#m26E{LrRx=0*T%5NV$n8U>)WS z=9DxJKBp8RLi0Z>Ud1R#?n&L71`%20B?6n!-PknL2TG^X4zR>~dz|Yo(ZG1c?demt zNeFAr_@@6udoV#X)E^~~93MUT5J%RYb*RnZsT_nmaY{dOC% zF4oTW+SbY8kj~shL+me?a8z!TvKk4k8l}zs95*4W6+4$D$YN7m zJ?ej4EWf%>qiVv8^T1IUB?gJuFAPE2D)YQR8_Yi~O+VAc5 z@=e+f)zP97K~26AU?kDdOcD6o_XFUmw(xokXFiM&o5|kBZB5cCc~Q~iKYAeA6g=LY zg~4tblK1ihJj~E9?@AxwppQ;c;1N*_D)xxKAw|6|X;fmiVml%NY<=>QpxiyYIQy1( z!*sJm_A>&EF{a$Bk=Ef{@IY%ZrfYJ->IYKFAk)gTTT4N=d7Ikm))f!w==W@F5eR#1 zpS{P;qbJkyfYwJp2!0i5v#)JaXtbxhRt1z7h-x=B66nYkstY_t6Ojd4on=}R5Y7Bc<$E>-g;+Y;POh1k~E*5SaeJ})KO zg!~xfHutc3k&?PDC8|C8$;WwHrBJb@q5G>EJ~f{r-rxPGVIlS8)FE)|)WHuyu>hjgRGx?$V z(fSQ#Ooc4^WR;Mu(`P5Qjv2T55Eu&51!3UMj zUU|;ud~sInlCpQ68Vn^i<|S@aCGMACj(`2C0UdRr7(c`XnM@vs*mCNT-Vwsawle%M0 z$+HvlCDH(%KQ{h|!AQjRJIy*P43dH%iUFkd&o!nPe8V2>V7Cq#OT=3dDvD$wK%W6u znYT}W4iw#13y{YMQyN^l8t*!pO50AR%`%1p-n^mib!PBWLk&nBpPIG3w=-0*u3|3? zlnU9YcPXfD)jW-x6`#F6eFsB0*IGPQQQmLnxee6iw9|IJ7odGqL^HQPoU|gC)KYy^ ztiZn2){#zk(mfC8SYGQ1U0P(VKLmNBo1XuG)?T_}b{1=}%`QvH{Ba9WTd+$mp#)~OdzHeSEUV0g|4*9vB_;6IkF`Gx7 zewHENJ%==PC3SDyPslpHV)et(R2h((Y$Ogn??h+3vN~z;L$a*3uOv-F@t{rsePj^Pu zYJ>P|w`(z<6m3%FEQC$KP0C7SxTiG@P;?d3B@*7JF-5CK`p8oH&FDi)g({!kPZbps zeT=0w8YIl+PB*!yE9l=ClPGlI4P`csO^_h3Tx2>3I$041AW>%=gINa(ZUj0Veq*b$ zn|rU`%jg%w7n>aBnD^yRvW1odMlIeV>q)9A^UUs)YAcVP7gpADLSrt>p4#4Cwdsvt z5ix+QxyT%(M7Cyn=z|F3l$$(R)-!Teela4bTzBeGknQ>()mZ0CQdn5%q>bSN`TKY7 z6?7KH+1dcgSAarvsz+x)UT!S|w-WkbqI{^1WI}v{^fZ_2+4S7ve#VDGMD8ksWy%jh zo&oMbz*IUhOB`i@t0Q;9*W~jJa5_6=QmEwfJ?79ak~#E#Ox0yCded2Dqb9}N9{=9X z_V0i!g_?lHPu@-vJ~SKiojd*=ts=4Jk{fsxteV}W`k`;YDC060SZ~|0bvEHtTS#+mH2kOOVM@L z^ol2uyFpoTv2?~@eL(lpoJ8Ew68~$N!kHUGR+1=XM&r-^8o;7#zu?|?Z9Cdij1%{M zRk`1DQW{kkoYNyZOVK!dt`^OdS4<{-C zfV)^uy7?j0zrr3#RCKyv+_4nu*k0%a4)H~Cw#Kw#@7yp!BATkD%S%hD+P}54lj$S1 z$A3%lDed7}h0PZ))xke+0f8-vyluR5aCC%Oc+!7`A+L{qdOYTk312!o_unFkMeMHc zsBaw5%U|CYVLallEuW$+lp;`RMZtaqPTEs|%oS_t_E$SB1h+8n&2FE>27GKiWdJ`T z6H;6+2Lv+s0X6h?3iMdZn`c$|sXWe$s-ivjZ89RuSbeD<0qMp2v!gSAySFIb} zRItoUNL$U4Ky(fJ(WlKtHS)AOXuFqp8yD-BX0|iUxaRwAC%{%*r6Rk>ofp@pg2sf) z`0))qqDa2Y*g6`j!B2f?T3(X&+HbLLMM+rjIWEIanPvHTI%WeP%JxBhD-;;Y^y3B^ z^^r=B@j%wzyz8V-*sW}L9knZj4Ek-J03974tjMb^Bq`;(-t6|8weGbirE=Z8a9tfn z)56SW)|%nl zf%ch;i%&i_yD^p4wfJwmx~;z;U-`K!mS1t{Td6-x;yMxJVYfvP1^TmY9Ey)!k+}MG zG620bL;8t{Z^la0wksb&kTo3%{N%7``Di`irERrnhTrTycF$;=5`$w+ypD9Bw2B6~}nh3c_7xrS`!v7dM)|xI33@mK-*O zNH06D03U(d{I&Q;v4ywO-?G{Va_^rL5xtAwh+`XT>u~z%u%{w#&})z^pNO`VwBSqg zSd&4=2{)F-#ST&Qc6|X2tjjDwXnX~bE4jmRjdwm0g-#d1(CBPY}VT-b1<+ptB_qB3bq`!iG@5TT3<^-#7s*|;B zu#4~aN^E(Z%EJIyd5j?XV1#L4R!ecUEs%elIo1va;jHD0m{HVV@h_#m(Y*?6Jg_((O`+T0szULK;OJ38 z>hzB$r=U5{E!e3~;f!YpjHHWd^U48H<>_9;xu}OlPh-Sc!=w`8n=c^5e=_;$c~Q~5 zHBaDxs-(~6>rGzY?U-hS(^Nz1`@)hEZ5|q-G-!&Hh{0;-h-BkLy*c3SXtBBBeE&Y= z%NLW+N)`$Or?St}-^S&6KfXPRGF-b@q{UW(G+u`ged^_jb{ww_y@NBr+kCMv7#fOi zxA?Wk@~iHVC#{*!44{iK>fF@7Sw_J(G8r@0Yiai7Ve13{dFs*RB&74FjO8zXg_v|^<`z$IB8^ib)KV|499?fd6L9&Ij9xWc-!56WRtbe!omQ7Q zNB4E{jW>6qiKJ{+vy={g_Ph-0BqRJqoh)hwc}(G1RXtO2MXw{W#EEDzrhl_CVa8l< zbycSPoFrDvc4}mgJz>z9@A~CsfY|O`dDjB(b3``5#cO$3Y3YH_Q^rEN3VJz$@}*9a zx~N!*-NP|wPMB@hdqbhGIViaj4QxKo_kGF!>5Cq}RpRk#NF>aGry=mtH`gwjJ#pd- zS5Wh2dn7I2`T3bo#okKyE#xSZ4^DY;2KoRUSGQfZq+segNu(rCPbePRL+P?J4YIu2 zPsR^a3KFVBT^GUTuF-*ZoMhlMk_)?TzevzKvt2P{cznx~iUYf482bNV?;N``U$%DN zv29x&+fK*oI32rV+qP|W(CHZ6v2EM7>P-jJ@|-&vQP&d2!y|BO|x!u9{Uf=XL$2 zcD3)*X!WEIC7ZjM23Bg1v#!$MK3n)5%IbF3m&mgL z$1Nk?ud7yB?TriRvMH@jH`7sQUQnNRuua>IP%^$!w>GZVV99+=>RgDG!)t^>=dE$b z!lj93xOp@7_;s0$?`c#yD&>!l-dEKntGo2Ef4T8>NYA}#{J&KWA!ZQIux%d4eOXP- zB+0pv)GCc;C!Ce9%#Hf3W1H1AH3k4N>0#aV0=QuoOi_=&{*{Ypay)~NYy90#As_v&@3hiuOjcex&5C4N{OI=#!Be_h_X0FD39++dmOaZEFg?%_`^L#f>HyvJx% z!^I7IFp$}}>&hJx33rAss+(AJsO~$2krbFwURL?7^}9VeE)F*S=1~AAI$A*LF6|wj zggcAeNEYmoy>srtd-+lw)@Ux$4dB?XU>S?NIY3v{CVt0d6U4;Z?Uy^w4U!=t?xu;s zQ7q1)NvTaI|6KVneOlO2h|JeMc^@z727q{gMDfwtNza%u%Lc`x;`FlfNm@;Td!afY z^LrJij$Ti{5T9aRf5+4*`|!wcS9>5&E7u~y_1|^=GxB$b?IMc20bKh5*c2XdaPe6B0Igo_mOtj@{=&mPhuN# zUh99FBgQ}9SXD+-Q>7lksHf3@kVC3@fqJ$)336~Kd=i8lQBAp;^>QkGo8ttOcyq2< zygsj!FCdna2hy3AOBfS$_;w*Csqh1*y74{u2@i;&;ce3E3Q?I);)VFgtQ1b;C2Zi_ zADOIX!kRQax5Kiuv>q& zH7=w=Me_k=Gf<2YrLNf?^g_S8$^Cd8j3-NbrV8t9KTX)!!-lQRg*6)FOfcDvcbdw% z$TCnD6k|l1DarAQi2fv>!*wJEYs@$mFH^GH@yLIvU0zMmcq5LFW526x3 z-Fu8$Gq{dpKXC8KXl>P%!1P;WO}N`#qUZ_%r3+F@ zD&S~(c9Tal3Y}0&U8Qd}4xBZrblW`6%I3=5q>2(WG$Z%M{t~AX3CeTJDkU9;JC4O9 zKa1urgl2;IGBG>ir?YuQ^|W5O=XQf7$hPy{PKriHJEjqt+X%3_1m}*J7+rDw!EWGA zp+rRNua5A&$=oy$@?lDmsmu)ZlsnVWSQ*7N2az)BaiZA2S-@N9-3oRgs&e?>=IN?T)Z}zCGGc!7NbYRm16@OUqj^Vz2vD^j4BvQC*nK4h@n@n>^Gj;PFrtYQv#3DdhiqG)`g4%(7iB!O)4d>N1PTZ|kDBjJ)GD;U=nT5K z>;RZTS!B$zeu>iN;^yB#^1_Fboedk$*d?#LJn8^37K5+n;q+;9MnRCm*F2Xc7!sYT zG~h@!=li&0G&>j{Li%_866E5~$h*cuMQHGTztVP`aF3Gw3?<-sn+aILjRRuPG276- z`27Jgz%)p@SIO5bfrAOxP8kCX4$Cy5pWUia%l!UGiZN`_yIuKpKKO%mj}Hf{ut2cw zYr+smZ19-hIPKW)vHt3!sVgbL0~3gj6(>uY1ekDIS|jyVU915K+>Jr6NvDl&mnvrF zJQZI)-JnwnRv{qV?UED34m94;(Gw%Z3M$%R*yw1kUI&7_KQ4UwQ+b|4$e~uGH0%$l z+?6C=vR#AC$~8Nffyg5t;A@o-4*rofNziQSS2Cm0<=QzZPf60_ZW`7YCywijga=2V z%ueAKmW6H;Ax)9qyGQ;I0RQIwX1AXs{^k#O)wHn-OiUW(Qa*QX#Dw&1@~e*X1~w(K() zmWb&e>=u*T0IMF`Q`T_`r|2r=q{|UMq%4|^-rvNe;?%tot=ALWRk!HSULcJb7p&eT z@m{uYw~|zlJFOI=#G7W+LU0C43mY4YlbuK@BH!}^mu$B93;Kk9!3mo|A^EYSH=(y#vy{xu&r_}2vRPQuOaw3L%`w) zlMpqhD8|F@y$>Hb!F)`rocMDVaEJ~1i4@{HG0o7FAA=%?(R!vs=ola3qXv_N@!A6Jx$e6{X%~L z=q060OjSjMU&Tb;k%Kgi`fD~*+@0c9x>_M8d`n)E6gQdt!7KJ+W;BaEzvE@{&d$$H z&H!oTRzk&#SJ={K^R4ci>;ui|5IQPoPyxDA+Ai-c2OFYv&#@`>uPA_H$z86(Ur$GM zj0cfVV4N+NBmqJvauIaJoJsGC$7n~(pzS8pj{Hv35s*2(P1Ubxw2Dh)G*VS%*CW{% zyIk&^@^dkLG&iYxB^#(pumye2T`IL=X!$-Rk}sAeG-7+@ z_?1-?$`FY7z%oa0&C9sRW-iztIm7L4L|d>jog#>VDjd0 z{L|)Iv8hfMb=6snAHQ*hhev9DDyL|2nmj-(QQp#=n`^T2mGN9ipZ{%j?w36ic5fBJ z)l*aOx6*9!-6@oY797r~b+qK{A zYYiyiOH7eW)pU{SOuixsJQBn7^6?-GpNs~Pn3vC9T3{aUrbJwHJBu(O74(J)(G+3E zS^e)z6l0A$KSZ;o!JjiFX)kW)PX~a1V8iFd`c>``)Gqbp1h4)5t|}Msp_j%HWM?5l0dvCQo%%;%G z`c7I36p?=GrUR)5F0Q(Qj-)pSjQS_bfFP9 z!ea6j$)PX#hIRXTQh;jRC;vM|aT;LKMh7EQxcdHO*6JqR6rBzf(mJ-c+eZ>KJfO8V zZ9*O|tU2QtcV!{CfdT7|kK26&2_`@Cn$vS5U)?@L!E7=Ja9Nyt*_x3WZ0{B-^v_g= z?-pU=UIbZt-cCsC@U6Lt$FR(O<6Ru5=lcKWvCE91~<^kK0%sVXzQu9q%U{+Z-2Zwb}D%;)pDR zzk!{Dw>u}Ugv)JVw5(7ghzIWIimVf3drE?^sML)QgiQrs316zo{iY`{keQ8&-T+pA zD1idq`EZg%u@EfXG5->j5?6L-X&Ko%7ADsjZ5)@L2RlZ^A)gFo$mN3nVb%8o?63_0 z1UIs~HBDp+5@|06ANBbMLfIJrm`CIR@LPKRf(6M*fCo^e(-JcsH;>+ZI|c-bi>3qJ zltUtZ7kDimi+dml{NS(01moqY`)N)x1Co~g@qY0UvlIcy=K+8~Zp-BA>M6R6X%@7u z+m<`a4SON!z9A;PcGzM+kKCcUQ5#Y)M@@?N3;|%JEQ!heghjDA;&;q&>&*f)Xk2Hi ziCZO9Ox=z}uAyUC04CIB8MVGR0tE#*7!~ZhvlDB(S`R~+OZYyk`Gd(!eus5}FAySE z_fqFuV=q_8W~NXWh*>i1pQsAV>GJOC1MSja+;lU8=^t;a+?Cs&eV@JYp&E~5pP8U~ z-@JF@Lyzb-3{?1SpQMQ&iF|eq-i!FWcA0js;5HxNNGJTv_J)&Xy_hEYDa2Pi`DFR* zF>Wl)GBVSd9GFx2S)e#^44c*dy>sYOqIIt2WM78ah8hqO`0Wm)ojaOn;9_5DhKt>M zIm*1yRIQ90%h-l=xiTc0y?S97!|O2kh%^m4RB7rTI)?&|%PGaAOCUX7ez)CW{wg1V{yy>BzPV-nO7ZxQ16457B z9rAdSe6fP>T)HP*opla8xnnhxZ2@;H>8JCY>mnicI3li3wI+2ZrgtuPz+IUjI|N-~ zWy5(vhgPjzyUk&5Jezlh8?NOuh=@{nZ$Q%xpi!RUzKf<_SJ!nF2jSK;D5p2Mn14$!Ki+Zuj0f z189=`msbPh&6Zi~OBKjZ5~h{(Dq;Z*O8@~Rvyh3*uc}wU_ny^gw5npIcJC)J-Do7ovQYC+tTm*GG&&Q z3sridrQ*#U&&R*Gf_60C7mZ>I)`{GAzs}M)Bt=A}wb~gB#JEni<+WVAzx?65OWL^5 zYWLzM__&Jom5uUHp*{kHbV$Eg^f1e}y&QQaazEy18Ce~>*oFb_M*1T$DmVzSVl{#s z0$X`UM{E^Zip6rzEZ83hHEVihnQ>g!cXyh{V_wXMTWnPsI8>U|452wf^ep|0VMj+tX7!-rG@LQV} z9Hb!N{dIC=43kPh5?Dl&>AzTXg0dm5J2qpT3amCmEZWhAy3T3%#1+Wo+>ObSFWK0C zQStLv%cQ%{R6(#xM%X}ln$TkGaqH&h^nX~Ao%O_>VcX4T_%b;sm=1IBHt21dVZ%(b ziJT~5qrTqaQahtz1&HofXua0+4qM5wI$O`WfzIE?UfFs)SN?Qr8<-MA#E|~F{@{zWq>>n~^d&?IeF!*}^4?i3BW zxio6~APx>&UW?_@qC$)6#)2wbT)Zj5?|5PmsYZtMyArw8lsL+a90mhJI7KR9Eoy@t zMKCjPgshMHFJ7bC_yS(omfRfH^=$I{P)LN`L4>gB1PkWeOW#?4d*8C6CKacADU9g? z+?(TX0IxTZJ0jcqRZ)4+vpK5Xh=xyR(72WkOTt(QZuwoE@dNfKO!v!%ZbjgvQ8UP1 z*iI+lm{hO)rm3oP_fhviIr&w`togT!Dz1zlq!1V}n;&?ZdwseUU*MVk>0(T_c1fac z`g}xUL&A6)C+AT^^UFue%*R+Y&)Kcq^#NtO{jwDPK#WB77&0Dfderw{vRI{Y7Pi zQ3QaiF}l6a=#i!hnZ-ARRABATu~{9@O=xZ*8FJ`;IJdAM>}LnVK%y11Xoob3qUWay z3dK&EQUHT~;4sYd2jvI_N2*emw*+xsGde01GFg~==frfK>pG{7+L#VB%$JmoNt7uP zTn0@IyCkRWtlVQ01XrGR@VEh>anox)+T&$x;crrt-VB5)BH(mC1F!x4TU7NvH2ICH0 z5Atp@Sfom=L|2yqYC0A}cQekV?Td?Cl=_9tmy(4Plg*78o+E^7-2oQm*&$R!AhEAP zQmCKs35reaQAwvS;|m6mQ(9j#SM7d~9SpYq$$EL6w;V``|kzL12BwBN2GIZ2kT6kau#Ts+GKGI`#}|xy%KGb7or!q68#HDb%J77_Ew;S znvM=VBP|J>Qz-}Qft(qy-BOkwoB+9%$>dl?NeN+|eHYQ;1)6r#a=%yBM=%*9d9+OE zhBX(|2*Cm#CEkuMtiaViP4pW2WW| zza|$+C=l&t3lrf(xw|Qy`26s4cfRZVvdVVI?ggtC_2cI*L_m>r(?pRR-4hRRK;DYL z?RAZ|5wjpIl4aqnVrC7%w6;~ngc^I9Xw`Lz8+Pl~TtcKkqGcHvny{Zn^A4(}0s*y} zFHe_^7I42dhDJ1Fr1p}K68Y384>~OV^(HlVkGVE){t?cxT?zJchRkAVVa0Fz-Frj) z8k(O`b@77fVq*1^7V`pmW%Y zw}g(~Zs3~dbPUFy^1y0b5cS$G`w8TfU>3FqD(ffqTp={BWXL>S_kv6}36y+|{R@-K z)r~mEO|qWRp!~fHr33EX0 zON(-qMC!Uw;rSNBdrnN9;dTTVDUiw|Bxu4IO4`CWcbNO#Z5>?mdl$__!4e_)P1UU6 z9_^d2lliXXMUz)%*UkBk8<%1+Fi|&R+WUruY*Lue>ZWYBWNsa~DVX;NLzyg_YSf5q z45lxq*;y;}Z?TieDZYZ>2BRCF31MP&LfOoEO5Q5r3&PK`-$_Wa&TU99Rhl|NHNi^Q zTYNk23WVAWPY!aqN)(EjAw_k>a1I>m9hEhut>y!p+ZpKha_D8c+CqmF_d(t5H?@{h ziCyVxprD|*uwIWk3#_8n(4`p=NR3O7RzllU6orep0}XTCM5jAjW6M3iDW>)LQyc_s zh_LQ<6FrnjS)=Fl%4NCvRkap2yY&`oAA$}g?nl{Z>BZq{4-%v(@@rGdygg0`A!er$ z27d;O_&$x|2XF$7k&-c?_ul2t4$y*Uw&jy_T0+jD~I%DC{&Lar&dK^4-*TEwI|oOe0N|0Kr+~)*xl35OEL6Wd7K-V%eGb5*W&NLsLE8?m( ziTR#0)N^M@BD$@?z1z<}qZO1RpK4nL6vbjNmv}#&{SmzCMFYAF7~pbLS&Me0!{*10 zhPtLFeyaY?`HST2pdNhb{TbhyEl?rW5M5SGyOwKc9ZHGv2i3bByJ|2oY9?t z7z^Bsz1uXKt++S`-n-RR~e7BpT zyGSmrODa*QX0o~R_Jcc5H=0K8x+&R4astxiM4yL-eX+$#IrYB&=DM_h-*6al8tyLd z%+1Qm0(d6yEklh__LL$&iqgfS0Rr56z!P$(sQeh!fn4$0>{f!{w=}zmip|5RyZmT^ zvfi_HV~@U@lnXM-su^7Hd(jNA` zGNsuXMp#)IS%Y@t2$>a{mkR@a>^p#Q7T4hw&I5Ey`5S|GL1dFu@Up@9hvkd>rz+xF zsJ;N-*uKsvr0OD^FOOxw0w7$9I>`v*KH-fxYT2{q^j*17p|MkL$d_g)!q#` zNY3^QX1pN{Gt1jWQBSr&!0VA|7)KeD62?M=f(Bi)#rMM}6T4<0U)W2lJp1m#Z1b?h&f3nW2cic6|SqM5qQvNl*>`<_`uH{#l~1xTrXuh7vxA z$A9Am>2x4{6~prNfV}^qUH=_)wVTewthE;-`A~QLL0<7w2-rC3=0J=b1wQ@`1P3Di zLK>k^s8q0VJzNPhX_i{G2Z+@bf2hUw0fg-@B3DHqKCZc zyp0S^qPXz?P^MD2s4K`ZaqqWIT(rR@F0wTD)k|>AuA$|`MdG!0&byPv? zWwzsxPEkigy3Q*ZFJ4fLKVAj~Ag(|afZJ_39kH|3$p=#Fw@rTfsUm+2vcG~{V}zyV z+Sr0e82bvbpEAFoV62}nJef-PHiy-s-*|87S4~v_;T5uC(}J+Mh$uN)4=TO!+q-41 z6WXXO+7Q4Cg1|u*%tgvXpwyf!*b=W2dn%m=OudRtq$4!n3`O@atI}z^&4qla z!IQ?|?Gn{D`@;` zYHBeYMo?1?hJD$Ck}5NR7=;C`J6%Y?!wEJcmrS-4v5I4)kK~u#@->pKBj{lIxeZ#L zPke2b2+WRU-^Z|hQrXpmxLDfH3j^9;Uy1l$OT|*8L@69#coL0U(UY+9YeY2!a>rBn z?F;H+%Ny>oVmhM{q?2UoISjC}isZK(X(juu8(h(BTN9BAmX{9bEyo4A4#s_SM8TYm z9A%Lt<#5?~xPyNyW1g{CG|iMxq-JLaMt>q-L=1z3%1cDz782m*4pz7o{Ee#HfPDQ$ zFD|t)Bl|Y);`Ejk%73)y#H;vsO0}bdD0CgZU(En`08ODX%k(t2B%7ddDPb=3x0p$| zO{2QE-i-t~DHew>uXT_l`QUK-69cN_DsTr5o`F_5O!&;*tM)tu$-+rJASUA%1Ll!N z)+A8tLW5RtLHxg{6s1m44ibV>zLJALpa~h4^xoh?7+JG?Kqy{M=Eb6Q3IBkhP-1|? zOvv2QRM*qeG6M3Xw&oO^_54q)FdGC0KIFgsSM_}6UU9Xwyo1QO?q*~Db{py3gJH@r z6dLJFn=7~b&=#l}2Yfm2pM4=0NH2OK@yo6Wql0$))5GB@Yk342V>gWh(#?56J(p+A z2jyRfPB_aYtO~2*ipl%ihNzT=g^e3h*t65VTph0Fe*xbNVIfN=3tT#2V^u)VCXb$7ZYFHh7NH>P2`M7M2fkT6<+%){YTL^hOEr~d^@ubOhIjY zuef}&;djWqrS&ymF3AE9Le20zw}~#tW--AUMr~D7u*^7bx(*u}v+OuHyIsXtuUmnr z0>5YPDo7cI2T6gZ`$YM!Z&%Q4g*MB9p_WY}NMEZC@olK((7X!%VCx-IJ#;}E_?dLm zcPmdzN&T;j;!AJq0u{}a6dh>$pvVb+HJjP4>q`xmCG_#&t6Zqn@vOP8w|tpfrj*UGn=H0VCtbH5UmI?qSe~$Rmqi9WI@_-YZQD3Ddh%Y~H;tE^MCp$44)Tnd`xM#zL@V&5 z=V|#JWd;rC3njZN*kl0XAk8*16S@Xb&H;|As8G*;w?%IDb?-Icak_#pp(}oQ+C}~= z`-%{_ubWbBE$voKU|@CLPw8KIKxh_Y^Q1kc=9g`(q{dBsQODI?sF}IB z(+SEJZd_4wlwSUR(jv8s2ACpMD0vJWY#_I>7_4r5;SHuZUZ%{*pwal`X{}97Mb*`Q z&}JgX{9lFFheN`;Ev54sFwE5;9pmcJ{h-CV@94t8$1N96H6@(2h}!wTJM+3jYBHV~ zq72fELXp>*TX__6Pss2bB(+T*i;~R^((l#`Sahm$cnkBwucnVR<67TmWD3!ybGBmUOKbr^lYgY2|=p)#k|!Fm|$l6X`*gO-}k ztUuamPlyag_%)P-jwKo^g2rW=n2tRnSR#v$PondX=gyNs=Qp?`H|cR9(E*Q9_Ies( zfexbkesCRUiXz_sRsH^TfBryIw0l=&=_82H5OLJ)TYYIqH=mQ#Q@Xd!u-jTl&GP(3 zg$q9=j!1r((vN8%SaEnJ1B68^+cur)RQM{k-1X_u)H$t;OAlT1=bVLQrNPi_8((FndD|2lMY=0vuUT7_@I)5q?-nMsv!l@)l#Ryl1~ z_QyT)ZbyhWm#^9V3D;<`iClbgZu`qXuc`ky%nqgewh{wwJPn&h<7M2$7!$vhh~>qC z+x7DdOC%J>PCloVbLuP)jJ)t`{&`tBJE6jKr61c`zT7@-zv(&AWP=f?X|#c9_C8BD z+lUQPZ@5sWDf(N;_Jh8S2aGR7!Zhj7@RXo$)Y4GOb)wG65rj^EWHJ73VLHSD-AeR* zp%678{aY;g&&9#64Q9~Ow=MjkrnPI^zqQ}vz9q4#gXL64z*`ERG`tNaJ0@lqP z?|;VVzYqL{0eFSa;FILUxWPr%0Tdm*criyiHol< z!j?My&!4{$5gZUJRf^K|G(iJ^V7mt}e|?oCDh>{g&!5W*3wI!Ag}(fq#P-+X@4Tbf zym_j$_3x=}-_2~``9ZJOH~c)0U0MRV^eruEO=!73AXh(O(EQK&F8Wu8 zVTAUg3}1&pTfd0EFW0}Omp_THP}h^_Hb)uCKf?XL)=Ez%@Z8Jx4~w>ctipeN$~a=6 zDHpj-A@2X~|H0G>Ag7ToUaI~tU3lPOfV%Mif4hH3@9PF-hXjJQ)@U^>Hq}d?riYzmDam7|^7~eBZ|OmDQYP* z0w~SB1#+oA4@(sipfvIT6IXqpLa?epP$7+pgq4+*fkA-Lmc>8>@LAbg%Z-I6n#x>y zfcg6=>t4c|K{nM(hW5zw{(V&i=615jgkG&f1$0qFVohXm|C-r&8W4QV&(9YiAu$l@ zQc+XkyFM&@a(4&P92e!je||R6U_)1GH#p3XYRbvURkhuJ0~#(DKK+z(qw5liJ*bL5 z@N-b!SC$3=J7j9>fP=1)U1=J(^&#S zK8q_JFL~wIlHXxh(#pz8!ffEKJYg6&0oY${FRkCYvEC_ZX9D4;)LnNf-#!n$ zSem)9b6*xdynv;nu{t_9G`1HO-be^OZvk?q9Ydov8&^lbRsHtDZ}U(XDu_L|3~xkHx^krIRZf+FCCq?^bFSb zae2(AOXlFt10#RG-6V8|s>(8;>?Nk6g06RT&zHdDujHu2&cP8ykjdk6x)=n`*aclt zK@W~AmzDNK^LOq>+`-Y@Dv8cN?q@pCOwz`kLvbwF(Bza9eFJ^@K_|y2nvOB4)1Es! zPrE858xwwss=o}kcd)|}LcTIM?hi!ah$2S$Tb@{qeP1Gic7ecI?PoQNVWrU=uFP-u z?>o`VChTZa&C%}0blAy6>K~%}F~OBpS12BBI5-^t+}wN#aVB+exrur&6%`kA?FMLw zsXbmBudc4_;ln35vWu?K$AmHaY;9~vnB5_SxNkV`;!5Y$(9?%ar#{;FypW({Q!?iHAz?ATC{E^tku*ZXaJl~8}n#zS}$?AFiD!Pfpd zJTeeVba2#<7A<(YKJhVjaMVd;v+_PI!|2|od%eXxJ2`zlDT+j|+uFNh|8ZL)SW#N; z^aS<*G&mmBtW&)103@C2{Fg%Wz2Vfa)3vo70C8+)dIsDJWi)}@Hr39#*;}X8p_Cwq_YC_xKkj+h+1jWr9gZW z9%#qM%Knev1vq+On}Scap6f64j*hmrwk9Q^+lqkhC_!fR_4R#!Jen~xHvYkyvr8+w zH)Yg6n$C#@p{uJl-pIzu87eb*4~0Zf{MX)#PmaSE8-{EF$4eZtrA#4r9nha9 ztYF}dg{ZtiRM@zCw!uW10y=g-C^*iQzequ_w=T4jQYP3#tlvw1*O`swa9Dp9wGXPS znhTY*xoKdAFCPDCZca`~B(OBu@-_d@mHin2xw5-s&hb5|_%k@R^JHw^4?sl|m<)T1 zqwyqS38P==+1U}E2rgf67KwP=4j-=yz80eXL~G6_c{^RI+u3Og4u)_$#vV07%u8VG zc!fRjf&gV;Fw4qKN`R-e2rKU-b1Qi51tJ7AL!RM}4#&Rib#TU`hqD#e<5`**FhoDV zPdxB*Kov-M93aYf>|u%tfBDh}z=4}`$^A%%i3~cdmXt72B(%K%G3cVAhiKUjknc|P z6@62RVu7LRRM=QNTP18p=3tGo7~I#aGqqj128m^*OA@~s=toaYe!rv=l$Mw7*O^df zxN2zN2bhj}kC}`=ii;dDTm%U5N2f`8%1_IQ-Y1AK32AqK`nL~gG+FF@=CM!~C2P7m zEu<@l;q$w`zW%GKLCD|nc6I}F!Mbe*AiB=)uMoyz3)uv<6rzYT9 zXWs!*=C4H@a@4RN1mE@kmbN7(4Vch0{g?K7vUzDTxM08$Q}w;NB7_^_Ay*G`=%?e~fz#VY2j5E) zUUvEhPY4?!KKAjURqNHYrQVB$y!?HQ(Euzg?Bkv1askTXtwz8K%+gjA@;U)8i{l%EC+K4}uhCy*W;9O9cjbJwG`-y2nXttc zW75vTW3tUdW`VzeFMPA%e30Eq`XlS^IIVYB-%VPV(8x5TA(sDs@j}ndzfL>m9o{TL z3Fr#elBOiR-BZI4KB;Q$#smFt(hT;eC4Q9s{Mi?t3a2ZNGv{#h80WU%^SxGDb zZFyxSrKEoscRO1~hv`_4@9Rz^Hf}%{A}Ll>DI&O^R98_^kqXkBj!yv}J-zir5R^%erV-O&@20B~hHD zU$2np`o7wif$rtT=UlmNJn^y#^mky;s$dJBZl3HQt-x>HZKxx3h5 zYLO{WXvq{$DpK&B1w%jxzAQ~Bo;o`>+=}PG0!)vh2Ztd*#p?S!<6Bi(xs5Vp^uP3db6WW6tQ$eI^{b<3H>_TkB@hOwMz%uF~ z(*Sj(Le{SM{mb&;1`X}xl2kB%4z(DUk7t5?3QjEmzaNZsKnjwU5=i$*0 z>~k*X%fX>YoSE!GS=Kf@UXPdSZJszEErkWgCno@}Eq>~!QTn-_vfc}TCkBPkiq9`EsSJjV1|!d_L~ZsJP2_r;e0>+t}vTPkqCIgX5w{3P^G11 zF6pjQdExAx1o{JphhgDn%u`(;1As(@kYZZ^-2C=yCVxi z4KZ-*7dxm}Ftr_hdhb~l0yYCN(5p-E%gqCcwwX5fq}Rhx!ROeIMNGW%>uNvK_A7F@dz$`tFM#l6VQJ~^LAv8! zA0LGn>%R%KMT9^ynHeHG+6~3c9A`l=?^=D*U`{vbMD?3Ghl?$1Ur?s`yVND*v;XO- zmDdsA9PJIlB%l>3_gEzq6A09O?Vl;e<8*dg!HJ}#pxnCZ#tHF+>0mSZ2ATWaC!W5bob+qH^*3f`ELv3*#t|&W#k2 zm4)B!-xOC3eJ$wqIN4~AqXD2V>!Q@>gJh+DmX)nGo520T%1}k(<$)&6Q@q^n8Tbuq z{K-&Te;GpFbAQ)SH2?Tw1|58O)+PC`as{fkua0=CR*Mb6aQf4id@0_lsl$`zeNG_v&Eo%sY=s|EX`a|MMH=5>2xqB?a zyPW)Fy{4w0Qf80Bt&Qv6{&B{fCgxpgfPtQ7%Up&@FrBq9ToGUTbx6TS1ZjZ$tE#5d zp8Kl)KKZyAfDN**v2TxYx$Z5;#=~1UzmSzvAj-^9d%uDq^z|-JPWZ>31O+-YM8g$f z*S&*asvSXRe8jQ(O|D|B1X$MZNeR=v|gDB07jnJRn zUazR)y2Qf@f9a^~YD<0;cf4b0eWGxHRL6hD!HIqHyT)eXOvfmmXnmZ{MXq0juuGHJ zYNztTvc+zh3L+9iiPewsEErsJYX*bQGy;4EDE;^1=5<1EPh$iqQfmUQc2rQ%6nSDI zFxa`Q1b#Bu>5f5M?$gyiAQY$(3hiw8`NB17{WstI4;wYQk*ay4q@|@LFB9_e^HE|6 z=Bg>dT$aCi*9Qv=*lrp+fT-65UKt*Q@`mqDWTcs_ch1cdLO<42T(*CL&Xdpxg|i{b zeNMAVH})s-YeK~zpcb+dkRjYo>!p$Gynf<1+VKA?C8TKllEz5DPr=<(UXGtR|L*;A zSkU{14DUFQO$&XrWncZAuaNyyAew}po<7^09-oGhk<207Oj*J+)O{_R3RamSF*41n zhb#()_2vGe#rSBEjnO?THFy!?%)PP(jp(9pcHk!FoaeJWdX5*oCtvktXB`XCJ$Ftx z`6ZN>N@g`E`0K7zqTAw?oo188+!_cgmfs;6Biswrn+8HPw7$SDBgd4Cnh?}K!UR1C zWi+dQ(iVl$SIC0iKdjrQ$A=<^1^QacUZoZxspI{ z`-ylMXZANPoZUJr*vza(g15t*3}RQy!Zghp7UAWOf2pH1`}RW-+}Ko4Mtsf>jOug#MxM3s@!CS!iONr{Bogj67Le1Okd0aL_|p z;Jk5T+qm`H7ZJ32dc)D|A6f7G_MWVSy*#uT$hy}U^o(djAU*NVI@7XbghGEIhT^AB zy1Y8VkHl{;Vqu3WQ>O||FQasJt4|BzIR>ULOil{fG3X8b9{E4)y;WFT&Du7IyF(HP z9^BpCLvRW15Zv7*5D4zUnnnWz2=49{pmBGH;O@-I-v8{K_xol}=V-2rL%O?GtyQ(2 zdTt|yg75b4Laaz&66iyusbAa^SZvOiS`+_cibr1ZlWvX}c9)4n>~7H(hg zcqGGqm1`gnC3zap6)SjuYf5R(7k5TLaK=_SK6jPsGr8XX9HzSK)Ug@t-$jAejjzA( z+IBc31h%)gx{2hq__WE>QE;e~fq? zMN)DjYxkrm1iK$~gnuR~D;&vj=zARyh_3g(cc6oWh9Ebyu0p~N8&N&b3cd(dO*&s& z8!FXSugfyC+6npW_WX8c-X7svJrwW#6T(bijN=*iLpDPyr?Yy}ui!chC`ey⊂a zs$BSAU%1kh-!-r@7^STx4Wg`)V8fxKGa7X0cCa!#vU0e*D<|)0aXjCWb*v10mxEq+ zeow>;bZ}w&>t;dNgS^9Dac5YaCkii%6`g`IrDb&~6oFD@e)tFh#*6MD?|+R1?ug7V5r=D`fn6O zun4gaojJlY#Q_B+iSIbzOC;d)?bT-FA;TW>J`Kz&T})+7HDvAE88 zyhOn#&B#z)U6Q>_N-%LQRa`uV6Jw*Po*HKq=9@^WU_*qT>P83!2>OlCcrDs3U$o;_ zv{W5dumuw+l5H3zn{1aVj>Iys#^!y=Zp)IVD70NStP zmJ3DYiboQ$wnwUknr@1tfkTgss3|B?*-<9yHHEKiK9(#8!l(8sgyIb<0R9UeQapTRc_|EFU^a~PU za|VDWtm}D*FRcmVlgd7(?C^{=-F^O`C(b2a&`I_LVtQ0@yPI(Pk7N^ofsJ^v)@l_j z-|vJl8`cr240R-9sXrQUm=7Cm`y#}lWe`Yg6w8pF&lBRpyeIHY_}JHg_IRz=+wevz z!oL4UsI^oZ&ScD*jALBYd{6-@;YpOgDO(x`xLa8q8QqE_p|eUBl-oj zMR@C{Gha@QH;MozBF1|~<6`|G@IIyOlInGl#XJ7D4T(X#G3@Fucd;(bsnEmN!ch)U zXlLOnJoH995#=T2yMo`4KrS@{>cVqU&)m|1!#`O2y-tK=@Jl9XCpJwt6O{J?ED=O$ zhdb3FpEw0S#j}$K)gmMg2H_lq77Tz`q4AbrhM9ZTUD#}VE}CqpVJ@0Fmwnt#vDa{B z<}4!E?sy49@!z=*h6%o5=G<>IEbZujsFPJ8PfkD{bHq+| zX@pv#r{Xwdw$uww7xvZecz5*<5VAr4%bZ!K=BpgZd8rmW|K*`0O9NTNJCWg;PG239vc|4_utSeDNesV zP|1B`2TL1K{3I|{gO+ZepXW=GtJ{)JYD4a_V40BY&7jyNzP+spFWV){xk9Vc)nS(0 zaf$eG)w7a5?q7&~r}zHKag9m432zhr>ZZ1?3bfKC6*&-;3&f+JoyON&6>!nu{|>5@ znX_r!+Kd%BK1WRBZa#Q^80qpSM-&!i{ZTPS#*J8I^4;&;_`QhfJ=!;f?w44~b*d%a zYcko6-lrQvW!o7VCG@m1IeD?bkI0r@iWoOZ(ZC>v=6ddllwsa9^#{Jr8}3FXOAR(x zQJ4)Aa3@>d=N6O!`(103FaX9W`IuEPy%XENv`Z7xu3y#G0&MF-!^*Q!J`&v9t@g)c zmWzl7p!0X=C6~fzeBi(FH9icJ^!Xw(z}n_4`jWban}?L&(eXM;yAkrxD7UU1rg)3K8FBNqwo#dOCc%S>0U^BuGY? zl=DKeU#`sja)uiFrqXwB`DSTEm2mZebd`aYk=JvL%zaX-etAf>Xll3HZV{81H;^rN zQH{5S_vmkO0Gb30lkjSW&FVeS-#?0QE{q~h#6L$4dp9JcfF6x)|K^#}e1Rh>>TrXw zq(iDts=ufIRFy_aa;#{kOu5Iy#^skx^G&J21*?hMeAOpyZS9ugX!747PMhI^QqAY~ z?g)!$erNf*O6(K7c-^a?3)6FMag}@#&SIJM{U44T+RhMWbF)~yWKJtxzI9#{C-9-~$nrxepP%_6 zqOO+;PibF%Osm~KP8`l$qyr9JrRi>1nl|pF>SUF3b!A3UQWEROpj@%HN8x;c;z_&J zlBnZ+F#S%8=+MkqW38D|p7>A(*nXanF08BW3j^*0+FqVc#>w{~n8KG>+NXOts}nTZ ztg*_EhL4YLeyDa=C+(i0LM8zFu%zmPX8ZA?#DJ27K>pq~0aUl{rx z*%)S~CV$~N3-2eT=?e`*#f@&bUJbZcg8}Vbr&5BS=Bot(AVEL$qsiGuFJ83DrL7zX zf&FsbmB`D5h!<^YShmI#l1!=Jf3aZvYdHMuy5ySH(=Jp1DI#^yHL*p;K`0XNI5JD$ zJr_7Wt@o&@zvqoXOvPp&`qu7Q9G6~7e{w1@eDSVWMK-)eDgBp=i!J9b#t`4aghx zgReW}4w1w+a7!hjKAps6^A#UkxHLbuB&$=VU=5<3Z@_nNWT0 z4X!#3^I*u`)%=h@==4_4rR|nHTXH#&J0Y>=sim8F9ovSOs?=!+rDWR3t3z9N7k(Zfwk=-54N&wkq5nT1wThzA9i*M zD8evfiP%1Svqy*Zq;C5^M;aQbWU!JaRz2UZ7?zY2Yo$5dM-jk1g82N>Y0{7G>d5uP zVX;pERk#_7g2@IgNx)Q`*HteSmB{mWY`w#3=+AgMDB1up1)!_mnU~|l!^9>@ulXDY zuC4tDxsH92C=Z!}J4{NGX z@j>=OS5_D-X!|MpTDU#_;!Yn|1w!|FnqtOiXzB-A-QE4J$V=MJnEbor@V-yi!pRnE zBL|w2)MV?GL2Fz+6azkuo=k7lCBkJ#v%Xp?sY{qrOe>yxZ?>FG&0Asx&~Ui$061%z ze)IS)@iT_)YcI9)pzZ^2z-`c^LkTwyQwBB0WFY%POHsx&qGdsU3w1}!Ig|iARAK^7 zWj@(E+H^BT-7?oO!Oe}0JIV7?ukR_C-Iwud)Jg=QBW>5zxVoRyqL0%P6QlJ}#0Z(A z9>5|qjOO{LNjXX;_@+Zbp*-x2LMoxn8Ki8l*-KsP>MxT0*moJ8eOD6ux4*hG+C3ig zjl#lL+IgE=Anvfiabvy#>$gNF{$LT@9{^!UpY$q+HO2?N(>uGAR!yL}3+o?Fh055o zjHuAb{LZxpan&i7F#-F5tZAkSs$siH9vC+<;jWR6WZ zhc*}W&lS;H*wdZep;;1)+&4#Mfy|;5=e5|u$t#0c?VJ=J71_{s7?>G^Wdj2(kcI=l zrD|MDbb&RO5b0n}-V!7nvS$EPcUpJmGiuKtA`^km+D5UjZYh?_+(E`GaGz0&U=y$P zNqDBaucS(aRcg>`?f7+F|*BCEsL`pZth-F8Ywx|a8;cnCa!!MOBd+JRgk ztUIE$o75}@qz7fRz4IpXILOGHVlPv8RoM`7@rB=5Wiv5Gff7gLw+ke>jM+dQ;)KE4 z490cOdJbMO&vTdzV9jw_vM;TXp>g21F{g?jCdub@e}8f~ml7~nJz5w!efxQ*Q8cXH z5A5g7^`Ixm?!1F5q~zyWU=&+{`-PQf44LoKsxw6U0gts;pXlWz={xa%UnE#T>#~8C z?%&+SrE}xPE9GH6A@)B2$VjzI)i|i9EcbZq8cpnsoK0~@|5wO%-qBH$;xMaB70w+- zM-fuJwmM`}ESuetOGdmzW>aU!nHyo4$V}8`W}U`l>1c=aA=&R)6$ZIs3H0gYaR+S> zLdyD*kn_4}S}V z|K$c9h=e8p^PTGZ@Nck?4fp4Zc$pcj$OsE0l!gMVyW40CB3{d!OQ$(mljeN&_k(Y_ ze%oqPEtJ2x9wB?oMPK(Dzj=NZA775xGo8$aer>Q2Mx5VH|EdhTDYzkl-EkrIZSs3F z;m8+2Wyk76YUf}|RYpnpy_NdUcte!l{9SDVJWxQ4!TH#SPKaH0=XF==)352=jccH% zDld2qShAkNhV3eqHZ04>-QD@QRA45wWE5dQu0u}*jt(?%7bV5fc@_0w$I+>udGIC{ znYN>!hRzL^BnuF5KMcQpD7^@id-;sM-KSwc2LvvQ*xJ{=rT+)Jm4_7C987__3b-H} zTny`GX&q*EIJ+{Ims-TrMF%V~_sc0=>8~e_OOzeCFh8C? zRhTc_-5Dd^d-3>!h`?7;{SxL#K0Vy0Al>FLuXioqxp{O0$~Hdf*4DZ;^dITyyAL*< z#(R10r>wRY*&E9;4LX92X;>EA>=T8CW-|pm_>(&wAe5-pAF>u1*~mzrr4A9!BLN_6 zg4o$>(lk?1VC!um!r!>U5-SYS==*w=b47H{!y4Md5Ex6N{WYZDZhT+u!NkX4{gPOE zf=(=*x-n)fGP?jFhs3R?t9+q`Usc3zj`d=-f3t`2x8S_*>mgi4m@X#{I|TwqCJ9gQ z6#aKp@SfVF$s)U&?P(b~P0Px_gTsUOvPP%3;D5TrFs=Syz!ZTLS~^2L-RfF#I$9*) zb_5nk5JBuwXg56}Zu6#IRMSrR)$<0e+sA`riFWWIW1Sf-hW|A1NPAq4_;=a|nZLVY zl55Uz_|~~NfGNVR$@qid5?VWVOTHYMakrA;&C{tjxI}l4ba6~hawV+s9MPLN$Pxd| zPbVz*++Fd|>y`MPzFh&9BQ-65{JP&Y3|;}5q9yo$-QYhw>0el)RP0aB?8vSM1yoo~ zZo9{si`!Mtj~R7!5)CHCb=l-gAGj3$dvX66UFe^<5^_JEY2JT+c9VoKLPhH7*-&gXl_Ycq6`!M^4r$Z*_a47X|uooEztu*W*L_M zk(3B%Y~#!I0L|2H?k{iO{i6*MWVf|@+_;HxErA$=08jM;K~dG;e60T? zHVFFtuSm`R*AH^uv}Fa+|C25Xd=#L^wvK|@n+{#4St-a)o>-gLt;_z=&G;87!LiCc^VG>2XFJw zjIR}t13#|d!k~`*-&y=WR|X7H2Y47YpV!-=0EFUPEK_n36i>&s zv_pHm{&Er%b@}iV)RAoEbMSV*sp|W!z37Gdf4tuRJ(57A5bQm17&mx*<~J`DUoyw- z190M6PL$I927fgj!*-G#fG_QN5X(d>Wf02AoD2{0IREEZS;yc?i&N@Il}CZjSi}uZ z8pY0$!@|i)PY~%FQc@?X9R?fX2FW)5|A)H}1#tHbPclyCN>Y6w(r0kRdM6)>1pn7$ z2IWZoYq}tgG`_db(LbU;{2ev*?->Vnh)a_eF@M;Pf1N1*b2Gkvjcb`L|4Z@mpA~~3 z1gsdb0Mu!y|Mj!bK;Yi2!~g#wU>5(soQU>tf7s5lGC~8w14j<^%}^I*`2XCke=WNY z$PIc4nwmrM`wxkM{sB+2)hAw3($W)mcam}sPtT%c1BPt^6X|NU`A*Hthk88I3PNKy z0L7I;Iwt=^Qc0NlszZtD-|HSt7bfS-$IRBYlz6wB+?UL*|63f8uzR@%Xa$l5VW{Uy%@)P$tOkh3q}_ z^nB&zrvO?%pRn`RKwVuO^rtU;LFtlGQUE*8-PM(v((~>^xP3jN7%YlG+TW!Pnm0_4 zXb0K}g+6g6F$Iov1%%c+^}YA*PR_0hzi+SRo{ilXWJ3V~{rGF@hME5SOzW%DZ`HbzT-` z7=6`q2VnRw!6IVT)zksBl>>9Xy(8xsHMp$HgJ1dMbpIH1gFB4o5mws)sz~K1z-|DT zSOfr`0Wfj_u%E6d7S{CLUHNF9H1^a1JH;c|?N|ZN_uXseLphg$!rJej^!gfroOwT> zKM!zBXRE4I_0lzGJHS`7@heDrV~MTb$Ikp|M9aU%#7I|)+-)Ti5Ns9~)4yChywo** zr=nvUr;Fq?Ur{vMg2gP=Y4vuKm9@GH$6dGBe}S7YMWqbQ+FZV`KH54STOcuewIJ{6 zN4AFPXQJU+-Q7Ekw8p4CPqgP&>*gjHdtNASby170))NJlRnA3D9&~BTy-OP#7Ww$B z*oM;z2>^Q5RfC6;6m8BiBP8_?+(E*BL->+w^hj$6Hda>a;zXN=aHE)9nb3Cif=8lL zF>(YCRa1D#_(KT#Z$igNlO99i8M;b^VMNFycF)C<;%{$uaj-0Bid7d8u|qnpw0m*l*2i}BO0d9 zPzgc&xN5c>hYGog32X4q*(NpVaz^NVMYNb40pwBM`$r3>;jOZmKWH??ljGLf8lQxAs%On@LGBjw?Re}h28W{cXJ(c1yU zx!gt7=9l(}FV}d})rl8(Zx%}sPJ8Q>n_tXPOW z*rjO9ple5MQq`gLO2c|$Vo4rJ2u)Vr-`y61Px4z-PL2ZAIX-xO<{?VI%-ZHcrMtmE zW4zf6-A?Fg-tbSQ7rFIn%e2Wb|Li*e@muvp5geB4HCpeE>aS~$)$WN4ny9KxjvZ)W z0frt<#gjLC5w4r*=?inbd1YRgu0EVKH8l${{TOW?-#TUHcmM?iIPC5I52!mA5c7we zoitMt5A%6UIKH~TrltjeD;<6CQB%`cFPl2QYMMhS778Qa&6g@#sG(IeLQUDRX%V1ox##Q*sVQljjbOW=&+Pywq{RvvO}a8WFlF$*gB zN(ng+eNdy}L1<*w#4{wg$yFcO&WUGPVZWZY{jl^aDs1E`2TMn~F)Ekf*tuiNcmAtu zBI8!pi=?h-(OYv)yWOo&y{A4=Hd|@UQ>UPC%dt}03XaZ=U%f#9yUKfW5WI7Mh zPna%r)lyc!s@vx?@$*lGeUG(*lASu<4C$v`-sT3oSBZnVgUpZ+cv-_W9h*!>ZQ}YegJ^JAfEtSI#$Sx-T+ZvfAYK%R=N~YOeAJfVzKD zMNzq5pJinV7)tWJ*Z1@DGd3nK+&dDqw6?ZE+RPGhSSeD;LrUgz%58x{^w}e@V3Bd4 zp`ofY)HDPvFCv4C7HR;o$Pb^JV;gJxjiB+B<2+12I^h_H2Hed3N?X?t!#?;h(e)yD zCPrB{*U)Ahg{yrulv~@|G}Khw+?*5NfSx=EP-ju`%~P)>Vt|s90n(`6>X_(_d4Tv1 zTuhdrQ&sLpQlk7nlx*+D-oVg)28Ck^HXz$<%ZkNG zm;hmMyAt0M7uF^=(Q9DZu7|aatv?9jDIml}u_YL3w04xgOV3QsFhqqCXhRSLNbx#n z54uNAl6p_U)@0{^!)_$S&&KbKfRO0*xEW1!wZss19*z?hd!0p*;m42LpsKTLKw-6S zWd(ZY?J-ri_**i-Dbk2H>Wn zsDma_SA+cYJ%C+~!Cp6Y-@h~4Pm74p8IM2-OWEFjw4XFUK5Un*3-*f^1*b?3``jB; z6cpthQ?>r{9;@rx#}tT&AMpNS4}ZO4}M)kf<| zdB9VeY$6TuT>FdR^R{8?tRxoRYiRRLk*ldhz})z~oYG15W?lV*)qAx`K)-Ww|GT_d zsE>J|6Y%~y5?H2~Edptxk^Zkhh=TbR@;CC^w@RIZhz@>-n-P+ zUz%pTe5rL)GeC3=AzB@q>goK(MGjqs2BUV98GzS??wP%J`Z=-}Re)J%%ctW1S^ATO z1(R-@Ak}>7qJk42-~6P7{2yJN>)x6SsUI9lK!qvb%~a3QP8=laVwv54#JA|`-d}gb z*b-fmh1?~|?c8Iw(TD5+yY>u77Y? zwB6U13*Aj6UP5|Q!k+_4sgzEXl;mFe10D;@{fSbXN}J;#QAqcs&nH=BEHA^&4$LkvOW^BHEq zW$nZ1bT!IHZ|VDkY0NzRGZl_ov!W>cPk|AopQfJL1FqT$X4o450i@ujao>bzo>N~n zL@T|sIyomkzfgjsd;1c6TdAl}A|Q>W9+P`c1ot(*|54wrmiJQOL__ zw~ZD{ASxS`vR&8h#l;2nu0maXU!%-*i7;YJU`7VNVJ_rpxQ>np$ndpY)i zw4ZjJV3P25Ttrp_qy>2%T6%u&kN^_v^uIbXq9QmQG-lLkxNIayYfb&pJ&N-3oa5yH zfV*1j^M*rol7n4P#XQ+k-BR0&U|*%Ko~ex~;M@!+OW4I6zz7jB4$F&*MEf@;6+g&- zkd-xmC8UwVLPsa;xu@)JK;qAGZRmG3EwNMUPUYE%-?m{}2$OVNLrcwnKbhsv$l(Gr z%F#E0ws~{38W1|GLsOXTj6E!mSX5m04k^VNE?6HxaNm6nlrKUKUj@=)Kje5v<+9rh#xx3MBll#9rf!2RcBGoN=)Ob!ZuwsQlYnT0qE; zXv^4SX1C`I<)=-@Kys)1{om?rW);l@+kH%YP-2NCzFkoCfkL@F0}^5VaF~oSkN30f{s0q;GGGU$bhYM; z^m(1Mf?}h?VyFWE96ha-8EnQGX=xwpvz`fU{LiE2s(Qn|Y{(hjvnyc@#|e(oVl1vq zr%BnL+NquW-Gk%#3T3LFMAH3!LpK~^fkD@~ej5o;v~5TXCb9QKn2+bwP7b%Wwg+3H@v{eo z_0!N4fKP1n1;gfG4I`Pqf&#_JYgK$QrbuP;<9|hg1%`w{*e!(BtaKSFylVJV&Dy5 z{EeaSc$h2;K9LApb!}_3!3W)aH^Efyr>pXcjI5*5fx^a;T(U;a8ioGPY21sbmWtM@x;ylhQj-6_PYxa4at&FDD|i zGgAp4740$D#4~fg!2W>c;`6;6cp$Z%Cn?ROiO;Bte2b;|{Hk`ih>M@uUH)LMD0NR%(y^x~eg&?ahT3~E=7{RkcVcLf z2gXm+W3KMI;N?Tf{ywbqh0X|XWMut#Y?Vqj$y*LvbF;`gi@D)8yMX#28y#zUQ#Uxl zY8$}ylfaCl%#-MuklWrYTP{h!)GF_gLLVrrXrLFK2gWJjYS<$%Bwg9m%WQ0KEnkGM z^;PWq?)){TEHFy@yEQTDJlszSaAUJZTc`u~%eG?QR5au_6z$=I9pB0cjc);5k%)^v z#HJ=yj)?|13xfnUpy0N1BxEpVXQp#H!slv#0&1Z1 z4q>;`7$UJn72KB0^2+_vc7~4I_GjB}pd$sDnvtx+YiGc*KLn&ATO9ZVXDd55O0ys_ zvI-;^2Qo%F23Q}^>~B`Vux@ca%o#czfhTdmqY3aHze*ds171O$HcV-R5}L!mApNCZ z@qymK!`=2p+``4UOun(~aLc10@EAMyjZS6-q`zZJATQi!ojbT)SQ#JHA=a(8M=w{f zl0{B2mCU}l&?4_1ia@bNl!SgUQ{C!em=;ET?P0b+*S7CN1_o?xTVCs zflgOiRNz(2CO$k{%*@O*&a>pUY#=6SBAh#%+U_L!q0eUP`W5nOVOUteblYDquRtW< zchFnR!K%LMrSE5TP5K7q!4`oBHV!31YQZ+?Nj-y`?9e^+?hIXb*4u1ptR42YwrgC_3>r&=!u|rhaUu+;V+Ju5*-+mp3n}xc zzrq-~2PPs5^h{S@a_RD@NqN4_g0vVd3Yfr6?*Aq{V54Q!Vhz|CCa6g=ljk==V1qXn z7#JKhW)_t&mC$g1{e^uStc~gW4wuz8Tl-c5!}B>;ynGv#Md|?-2!!@uo4cm7m;vQK z*e%w%0dDPr!N+aC%d#G~Xc)Pa^08WL_HLcv8ri}d-(hdbQ|0DW#gW(;>2Mg7lqVa= zlozlD6lV2GrIO*Xc}J;n+w5@QhL*iA+;r;mPHy*m_GUj_$bwXYXe|v9WTDc^+x>A^k zLMi_T!4HIBTBXo(N(mf_UB2Gb8GGrZte1%PpSsxB456?%Y~07%h~mGo2Z8j0&(pHQ zqM1FngDL}zDJdeKP~saOEANzzIY7tk%r|ncd&CYp95dni%CL!}$pd-yn8_uCD>25D z^`7wXPNMtofH%i*1qfTfx@&L}qh%a}sPh~45nEdmn6OZ5E90K6xR4R$lOK#6npo#& zFQO>4baB~^clJqLI0$IBR!Ai_mEFt8ouFa1}G{gnrQ^Z#9Zq#DvG> zgzkvp;@WVKlV%hSYIKpHEc^{+l*K{inItiFUI>*}ODG5vkWfqzAY^n_WJ%qcypP)Z zq&`eXSUSr3Dkws77pY{7&EV7FkfDh2QQgy=UcO1ePSAL zqG=r5)C66r{1OrCQja66Y;~BUN_8wIF+M?RN57OB zD!UcDb&)E1HjfwFnT}P08083Sppa1|z$HSvvA&Mat^|*XGSjFA7MvlfV5JaE;%2L{ z9bK{jckOxGtG98SAw4}^+vW2scChz>tmG|_%Qb@IP5xyGaKAuwbs#)ejHTN6a_XZ| z_b}q%AFLl}`R>r=(#3rsOz!qWVXn-2cVYb^jT~(sQlyS1(GXF!SP7;NjYF|vyT4Gl z#c-+@KpKP{<&t;} zsD+Qrl&Jj|qp_)RS5$tY1v}+tKr~3JWe7SGpjv;)l@E7bYf!hcTT`XiR#o9p5yjwY z^1!J`Eg78qm{`yHtH~0c2FuSD;q1d%(|Lv!G?>Sk1pZtJFKrqpAhra zyOZW7U|utqmzO9h$o(uWe(a89QN>c{`$b1jw0$1;N)viAzqDK?szJ-d=<#;5Kb8We z2&q3u!JA&*$#;A57znnH2C-y`TChm0O^j2gG>R79r$C7qO7aOseA?Y>Z4nQCMGa^N zWL}Jg1+S&_?!Q{|_l2M166H@aH@S_s++C#Zb-hh?W%9=GvwC&%q}RwUK$9WA*E%X~ zCn5BDF)eb9;Io%~yI-UBo-k9iaMZy{v%`r;+lAo^i_ZzP;k484kz9Qd>PJ!dI4r4L zWZQJR2=oS{U>}^#ThcizlUq>-I0zeL*&+w{ulXw#0E z9|Z&y-YW2vD(X=C#mfw>kR`D&8}cAZN^-f?U|+5q5T6=2kKkot*jPp(Dj9!8KCLv) zSTA&r0XzV>^=$`isrS1)#5hNsHBr{79H+X>rw>)aV4|=T4~35FfJFFn zf7%`^_DK>HvHZe_`+NQn_v@|Ad~F^q1z$13dJ3ItWKDOCy6Gdn@Td{gGVO12KCD_; z@S-h32r%iAOdJ+FlX>2UuPql0Sh$fS&sy|W3V7fp5G>*b$FXUn%lED=Z!NDZ*YJRZ zGY_HjJqp{|tOSs})WGK9ORNRB!-ro{0QqMaPJI@=$fxcT3kWzGMHC9gVQmv93+M)J zQf6GhV!eV=q9j7P*k-!n{C2&zC1B_S)`(BEggL0Bwe2!4k_^DwJ6Ea`p;t>IMq-V) zWw5|Xm}_(i(>BkF>sY}@dnIqkQm+5&^;=t;fXXtkkcdkDFQrNbgJ;mZ%aCT08lEW~ zg;4O^Z6Vk@qJ9IL^(EHuq5tK+{Kdqd&7l+3KLMR9-Rg_Vh>(&|jlo$F|Gk5}b9@D(X!Ij-kFy$EGPunoM3wziSkQn% zgx-x3x)^SlM93SCjj-3}xTVo^c`)#Hq~!2DT=$ui(b;0`#)$4JlCaEjZReflOmB11 z?r3J%qo9kququCfyv%p1MW)EtN?F3e3MWNhQ(?rnWC5pIO^~nVg_cE_S$a4P$@b$vQ2n#>XMg z6KMJ~>o-vf&>+qSjL6woavvG@9!EVA^t@qg_u#V^ z2n$Q>H)SO3XS0sVetmTwi&5Lhi#=>DY3W$S?#dJqKYqg3Sg0*l7t|&vR^$;-9lMYW z2(=zykTLg0D-L3BGN(Le%);1J8a2ko3YWiviMBgxQ;8e*F`PgOXjylK311o`;E?## z-I%2t-Lmx|{WJ!fKyjFGuyYhBWyF*`*TL7f)-JYKvu6q2;oyXqXur(di#)Dl_AG{I zgsQ}pFU9=8tX>5RIh-svryw84P$OPX4wi(2g(X?;sdN(f5vH9@1C3!jIlAVnnH z9tQ&?d=VhXhr0ATAYRfo((A~!jQ%Vi(&Dz6Tw9(x%v^mZV@XKc@9&0_qnZ4-V=PvX z4UVXDn>h&$tT-J#14|GK?OeU>AddR|!-=7`#SHP4Lv>dX!Jv-X!*#2qmru} zojgsKwRwZOL&dCe|A%Yh7PBEb4o)#X$%S`ui2?fwRNIY>shCK;GKIb6F-XyJq;Cl7 zE1-zaz&8+!PZl|sJBp)HNaeKUYd9I#x_ntT5Zu8?Opx$|v(9G~G7gT5=j5AeN_R-v z^25yu&5KQWtD>M_Cp_=){i=O%)S#2V)raSL_aRYI3z5|)TQs3@LaNElqg8AW&c>pdVsH=;Op zpAP4oe*B=b3(~lT+58!T53`4TWxrT=blqE@H=UQCy*_C^ipwU}Z^v|vn;a4$4xwG% zg`yYb5D&Ji^*v-+srhtCpW`bGBSS?i}?yw6$M>MqT zPFbnZD`go}n=3fp1)^?RNJ)KicfT%*2b_~Av3NU3o@MCrx2zBYzUZrVf9Z2{3h@rzp z*x8KUp-`McG(#GfnVH#`^molL8qC!^D63wX=Pm;m2RGtF#P0*Y9{{-b9v>U`m4sr< zUXi6d8ju|XXbW#eS91D-`_2%V_VPT-#9yBVQHu7`C2l_TlSjkNbr3e+K=aPGB7Ra(XTxug0TmHIZY}~Nz zHA&()MmG?8l-*Q}@;z^S*71DmM+1Q7r)rt`&)`K7b8xf2`%LJx^KzFgf_v3b6`=mA zNQWs<2u54EgI@odE8BREaTLrK*%&knjYClhU}4NTqI`l!ux@Sc>10Z-j@3}Mh|7bk zR^;6W`7Jj*w@w_AnXj>6F91Ou+7a=0@0Vx4!?N)CSn|%A?Jcj(Xr2ndFs}{7rnHB# zJ3oelI*(eCW%17RlAXXsK5W3|<4%#i@nl*KZ0 ztEfkIgdhCFfT35@`B-Q0cIRo3^76_L{qQy_EHDpUxq}=L_4i7Qup|D8+;wCNG<+Z3 zuS~@rSolyq6Uul3M^sb8#|1q5;a^Ehx~Z{1w?<8zb$m6Qn5^_*yGZ?5KVO*4YAzEV$F6jZBbm~_&(!Pg>wFUcNiAv zha+#X)v4NHadoGY8(ZP+EP#tlku^p}Jv6zU4EMS+UxbgcdG#os+&H_3-#q9hXhK;D<1VNzYBA^ik7Ew}*uN+6B z?Pn~s3((~z*kkmuj1Pj*ZndGCKVSy)ln;gCiwhHbECjYfA(GSkRrpt11hKh!ww~sD zn8S*+NqUm^X<`gXhRIZ~iN%dSch_}AYb<9wa=(FMvxf0vHfRtHg!0BcswtDyt)0{^ zq~aDMA0;?98W5fB5EVTEU3_qr5%8qKJY-YqEtgG{AyVft+ zE!4sp>(Az=4Bu;FY36N0lpDvvGd~~nB>dIY1YGs=ygp6kgE}S$Mt7>XrN~GmYnd;{ zVWU|X+sOKBJb#S`j-SeMmhDOZEEJBhL>GF*qL(6z!tH-AnM|uF>L8}NkeuM1*h=qi z;rRQ&j7Deco^;bm-SQh(APwqCeU|zt^`p+2%OP-=>*{NpA|JOgvAV{DG^Mn9ffmCa zf>LhKgSMm6&J63+HTQ4qEz0Bs==nu6)V90`!Fc;ksCxctrm#p54Mzl9Q)_q}x`%cN z-0q<1aYR)9gLE^-an^_|uhw!<$vVkW+zz@Ix9187`P;sEviNCsA-AzeBQZ=3Gz%GW zn`+g3QS-DC?E7)?VXC6vTkB-DK}sk=95nN}k%T|u=bnoX=sy_wLGLQ?1g9fc_BrBNUc+=@D>Hc+kFd zS3kLS*r``bC$1k>9!)YUU+5C}*;UEN$}!JJdugB>**d19_Iy=!w{>vm>ehiYCt@k^zUe;iq?m2={V`{7W(9w% z>s4Zm-`T-@gEi-)M>;Iu{^eeJJwxz67WNVrOpeYcL#{yf)gKEtZEMFoCkJoEcyx{O z@3FXqFUuG{2A9+y9cW6(Y24o`rn*+S218+yfJds`NstP zyJfW^1cwI_d7t&s@NQZlX_GOda`y-)undbgO~7nVnOd`a*lRnD;p9NMwC!(h?bi-2 zWIZ|ygUxTh+qP}nwvEQNZQs*&n`fBpY%$hZ`W}WBpK91hzF-$l?L=CaERMn;^i*9e`I`H7;sbp-Yc#S=G zy=s27E`3#82A{Zzjj#UYxYl-yg$Rx*C>*0BGi8g%%k!gx$~Sgbc6vuK9kK2fsru7m zf30l)y@U4bl;AsjS$Ac5Kf$mt0`ab@=+CY7@6Vnf1Ozv3?+A)v{+Hk1*928{UTb$z zZ2D`c13KtPCX`Kn`4e*dY@7qzZarh>Sou5k>tEYCZ(omJPo0+}zy4l!|8)%e%piq# zt-|>@f2lP8B{A2kK#FkURY~(%{7;vA0?<}6f}6IvecSB6kLtfv{BY=?U(XjJ$pLzul?$O0~9Jy zqOJI!W%y72Ik>;a_d9BY)0&Cx+)o`7wWXgc<;nlc=@R&Okdu^d^C3YYB3i&6-U0$; zw{~70o;HzMYv=IGPu2fC(Pc|eiZ8bf8`M}`@W4P#AjY>Fspa->{cK*Lrhdh%C0HiC zX%p#)`S$nZ)sjWB98_EwAOCXb%KQE_%b|XF=Q1G{nY&hN(0L79F7>YmE?^bh0#=ed zZDohB>erU6Mt0+tk&ah59T!>Q-TQZq(NrAIR!ilX zhps9rd&VyQIGmI~tjT{M2!8m$H1gh0v={j~hjRll`CeZBA3xM2)d8)0dW$J9$Cn z*t9^LPm%og2z#vGsyr^t$i`7q+C5hspHWOi7xDYDlAK5%YPSW1gxEH(kTjU?hOckv z-T;IW-zQCOJX+DY`YuB!aZ3gK_6> z14-h#`oY6)9CwT5>c`hb(I-wQP^(PdeHlFS`FuuP0k6duPO8PukZB$U>%|J8aqk#~ z)YNdIl>uXF1qEAEQ%V1CBa%d(eCp~Su^Mt48%e2>+~r(BBcPc$IP_<>!o#td4}rqM zVyg+@*k9eh^~1P9|AAsz@@9m%(n{aX#PA$2YM+#DvKe)5j`n0X>GFa8q*PW?UcA(z zH=OAWIeE2$_fl#HnRfZ#gc-0d@L51*^b)xcC1tT&yGOUPb8svk-DH(OfV!z=eNeh+ zX1aIr%uov`82}32@Y}~yrG$6uTANF$_B7e6LqkJeUasc$h+L6SmD~Bg$g5D{OQGtE z+z#r89>~d#eVT;n_Y{`O^>3#(n`NxXvBoKEn zdA_oNm+}@0A4)77qGkO<-@v+YRUiD!AWeLHz~Rh!P3`CDav&->04?*GdZ*?+<+yzK zGjha|6oiE1BTM~?-O_zZO4Q~i&;eGQ`-N%Ns39yQy2aNjB7Nn=<&S{7T4R*Gv=bB- zcmc`%pd7a_${5hbvR7j?ZOKFUnz?F!^*tGW23x`hTHMTxREpTy)>%0bnuK%$eW?== zSpOcQ`Gf>wk#Miv;^%1~b(G^LmGXu3jlWZPari#OBh+~S6rQwT4J)WYG8bS-6S-mc z{qP0ec~faHot~JNlk*dvA}3AVMGza&NS0*P2y$aIIsUwR28LN}t-51&|B(xzbeFC*A^5JjoOBhQ#z``9pys1%2+?O64a zG(Ni`f+8COyVGln*jT8plgeuo%fGfQDtS``fnr{>YIir_^K2W+H1P zB^@OFQKUY;9p^@pe9B(nCS8yo>)B3F}pPGUKm;oII3)8Rhq*(VQFEw;()qp#^ zn>4X$r5~SiVA@ohDU;ZXz4!GcB}2>-URq=|MY1Y$7g%PbB;%KaM1VG?>_xI_(#eQ9 zT%~6RDU8yq5*0Jaj z1WqZWN13IBh;C%t0w^&Zq3S}Cy-6N0W`b+$rxGbS6hj*sA4wz`Y@*;ukeE}OEMaqV z^0GFzcxA*Tb0{utreZu4Oyh7c2~mx(JfrbZRtrQ?phj|}%DpZb(?!#keUYRI>FFy2 zHuJL9AMHdCNCooyfh2-Ez=K(Dc-Ks=zLGiCm(TN~ZO>|#4-ivKJx{0!pi}6K8nZ=ivNE_%Le1a57+ghhpW9&9=wvtJ0}xwIWaivT_q(o z`VZ`-%+-aTQmEfI^mK;8!^6jj(Y2dlvNs|(zk5^ekdVRS5le!AT#{*jJ}GW7tsJqH zeh~kkM=}5xLx6_ynShODFb!Z4_t$aiM|?z9Z?r8tR5$b<6cBaP<_DGFVGnm13^irm z+5X}u$3SOU%TS?o|m!(d9ue%d_FKj8K7fGl z-F+Cui|N*6cYon-*hu}>Xg}2>Rx(dZ{YlU0F=2$h(XlZxF;S8mkaF~g;p#wH`ImxA z;k+kAe%0v<85|nIj*solt(y8eD=vp{WhyM(G})Ip<%JG{Vj9N4VP-pa|Iu7bTCA|N zl<+FD8?cvgjCcs)w{A#@?#7~OJ;+NQ7(}bE&;&6!?&Ruv0D?kD$jRvqaJijwDk{#G z8*NlDFt4_M*lDeXga+pB0&7dv7?p36u`wbpkDzoO-J-gE<#1t9E8btXH6|OVu&L>t z9M(f*`ub8R10%zvsMS^0ERk4WZy)mLn4eTY-hP~66qG-%YG7Y7;}3puYv5pogmwS` z!4$$)OPqAnu}4)wY!)=*C95zmha}qug;mw@k?BJ%_~JywBu=3TlfW=4*6vTnv0(hMzgKWPZZmTU12Wkn=0G3GUK36vNzxhg4wrW^;+Z zEi8Nw2P`ib;@C6J-}|&VtD7I@(yBmG+yajZ@?n`wJz}ot&MI*5a1c2x%A{U2^-_v* z%SEK5411srWC(+#(+Pg*Zfy;tLA;)I+!GjdX??COSW#~otBrgHf&jAE;b;rOg1ipG z%)+|4nSI!|tN#mT*?HhL0Vm~fb88cb-FQ&dP;{+4&63x*wk~Qcu0)Qy9ptE|nA`(f zy~tnQ%PsCGM$%$w8Ja1hvBF3`aeci?H<*jwOLxb+aeKJj(Wkkprwe6>V7qEPb3I${ z^p5Gc@A2xJSF3Tj=zl#dpPHJYYq!F~A-@_1zyw``rHLlXu{F1+O&Qn2@&$MgXq+X& zKK82Cm)lhf*Q;da+FE$2Hu7{A>)xOzY2`E)RP%GTIUbiusj*iTAyl=D+ z3{JLlfu+KlKelBZp+p`7bDiF!VRu-Gi39#PtY$99u4e*LLZBo|fUZV5jq73ga7NI| zuMVAjB8pT(cFyWJo1dJ7>?1dT&TnrNkBW)%QZWR%nxAg#4W9~92-OPX?ojcZbo(Q= zKOQy>4F%=oY&Y8MepabKl1h`H828y9Pv^&alWivdwsY%n)JV@&)(LF<*RQ$TXi;iENV;ew&6*&Rt0SQ4F3XgZ4#I|0C^U9reU-Z~sx zs;{7+%E5>j8DB&_G6R$(O<}ieI=jHb+uy9 z-t5MIl+~Z;9P9C1LHM*@FqzHs92u^KL7-;Hib1OiNPeH;&2xyvw@|Jy@4|UiI^i==<>}v1AqU~GK;c7rk&s15vZ;0UGig1kJpR zn(tNM#=DoqI2+nLF!8)_vi#vu&XWP*x#Lihf&t;z?9na#ZclqaBbL2w6KHPz;nVx6 z*VN(DmZPy|%UwCPHY<53HA7}5qBmlnrLZLkbt8L%AkQ(PzUogP9Q+y@+{`Ft!R=;% zW^4GY(cg_dG(9D`*?MOPNU6bk^6J-F64spGdn#)_-P}?7 z`3WcLIfB3?0dM1wI1WVOi@NNOFJmI|mToEHvHJo*)aD)Gh$juqOwz^GqDi^gV$H#? zL3zeY1Fnl#awu129Ft^NQ!etGr_=Svw#4R9;~j5aH$&8IvM7=hAs2`aV?g=Fhs4?UxSYa6`kgb(6zX zbtuncYRdKL_M(jg zZJkp7k`5iv1h_BMyYou%$L5F`_Bw=XXX%_zVYDd9R@A@SK&?clVS5DgwccMW58#hNtZiER`mHwTaa|bQ8>X7eYVxSJ1iCn z`>0o4T4J?j{>>7{H841Qcwod~!xsZ7A^q`QKJGBw{Zi-Jm>lI*e1)fmXy9zV@&)iE z6g>cVBUp4A!S{S!GGb!Xw7f#nD45#&oJQ|1cTMrM_4OfU;k%INkj@uNSnJyzcaOiJ z!QjC-i5MGGAx2rI1y&u;U=`4tpgA>c(&L-<1bV z83rfOt1w6nI7Ub%gL_(^&;5CIZx9%J3v2U-hKJ?qM8dH{W6`P&^@K>zn7Im5> z8`nG9wtRilpO*J_$C5dZ^1dCTc`$_cyS+X?6jBySCQ;n#=7Di|!Gq##=ks0}k!hPK zG85ST`G8s`09B%C1yqU>jw>jtWe=eogHz5tBWZ^&8HxD3->;`WnIQ8ZqrnCTA7UR` ziEJ^Y(OGS_d4ZB}yRa(_2vR?9;lBl-?1!BzrE$wEd>x59#=^qNYg+fa6?EP5?oxgR zVpys`e?x4wd*juJ3P7`2`ZL-XP#nq{o$MZkag9Gxm---7Ih8iYcmYbr|6s%zC>A}+ zTbHrasI!+hnrw(U+I{0YCRW0^2T)5g#~ekJdME_z7SL$%F(n$!=!59^^G!dGpaolZ z;vOa^O@|FwQnv__ubB8BB6j;Ptr#LQhQm&Ad-SD!NF+Ic_;j0P6>OK?<%QCvH9<@s z_eanJJ{24_oCz)E23U2`0hheo{Egh~xtz(wi*3#we_(sHT5GZgxTTeNv;q`O35|+C z0Hu~QP4Q}UM*Gc;6jvTI;M^YilBFYFAFokTu4%VdHIHm{pu>O?M)tj+c|acwOfX|3 z+e+mojk-%3R}=YST%pE4Xecp)?qG&VWVdmoz&O*@^gw{pZ! z3``!MMSn6TD6mZM zg?aMiMhe;=XUf?IO|AmOx*i8WNvt=Q_Hs^Y|8mrTAOu0vTB1TXLgkfNzdO}vwhFXC zp`XIp66)`M3ay8h3Xd-6@B4CcL$ter-5y550Bo&MMbHjgr=@fyZVz<`S>w-* zh*MDzJl=))jbn+gM=!})MG8fdAZv@sauSh#Io9pq^C(D+O7W^02_?-X4JZ^GKxG{x zJqC5rTj1qXlT#3Of*7K*z6`vqs<&$w3N1C$ccuiJ1N03)2ooZreR_uH0QrJZsZ>pW zn!Fl$QOU(~eO;EEeZ#O@ZefgMw94|>SJ2z3Ec+B)O{yl(u@Obk8|eG|5 zm}|O?Tc_cU>Lhmr4Z)l{5swI1o$jDuJ`A;TQ7*D~b(;rJKAXwX^7FKH9EG@6{5-g# z0^@k&Y#4%X`c)5h?wO3?e_0m19!xaiqxW)Ka7V*>K;idA1*g1p3pXm^booa-^J=z8 zSkmC9t|Rl_5haT}?>ePC>O6i^qi+k}IPFdcMpH+SdJhNf;zO7qENyK`@)Yx8FXi)# zipTTvb%@N&V3>RpqxJXT;zo7AJA#au&STIXhgTEkW$8()OaofEg?oDCF@z%59#0Zm zvFe%9z_}vnCu@TE=Qql4y%K}z@gqW~?vuD#f*)MBaB!g8^DXti?^^AG=1Dgj2PArZ zt1T%B0o%EwHj5?uqt`Nl3))hhHnSHwDHo8vtNG;V<5ytReqmh8r&4Uiv2lLU@KC~| z*&V)>&EF~TmGm0-1nq$7=k@+T6pA3(#xbT~XZU;bs5?YX;qB2@06UnT+R1FB8`R4yz! zH9rZ<)8SSpuQPf=!~RRDe!8h$zHqL{cHV`{`M43EKUip{@v8G^+oI2MR4QBsogX3+ zras88pE&susoZ#+u+BzTNTJ(nvk-}Se6T}M%{xy?*a4cNSj-FuBUpB?n=dX*-T|%& z-U!g?snvjUedG(EEllRS&u1!m&qd20vd{#3lZF|NK>cg3jTBe`EgWwfm|lCJ_4>P4 zr#404?M@AnbW$qp0*nx1lL_}&&mv6WVcS#u33az@va+)V`^dXj&FefJ#~-iczsggh zN))Y#Ut2%WV1V4h8Tqj+GEaY`&Fwz!M^8$B7%`|Ap%1o-nlxXN>rdHSJ|f`=ky#x4 zNHgC;|OPbIKEh z*dxl1LbN82d>NH0if}9Q7a>JnPwu?d*X$R_UM^kb7UpG?MNy_fbkM~YY9MQN^+g!( z+NH*1sOMxvaa)*2z`|OEg6*QkjPvzZ^L)pY4Zf5<*08dXK8I^BI}|c4CasUEooh3I-^kHS2oZ!$VRkGcC< zt?H#mJt6{WoX*C%V8|>lb9VNEeXIRCmR9cUq3NpUOKiMy6)BIB$8kwXuKj1|*4Akz z&g)cO&0zS5ER~LSlR0zovQ~#53gT3-xlms_j6G&7hvP=Uh?!Ve>GfnWOe#4>IG596 z4C`jM@~4Arzy8rK&CrA<;K*82Qc}vodFqy|Xyrb{adkY9@8;Y_~T7dE5 zimg}{*NQFE$Odl>)8xKzxG=zn+TUmEN0l#G*{X=rX_WTf6DwJ3V}QD%d!ePGNIe zWq#h!pkhuUj^CQ3u^_vMe5I16q53t5$)W8P7s1_`7L@ArQ`x3!K9-(dZVOG&yoF1b z?s}~(Kv{%`vnI=EI3|?7@gvx!5plHU<;8^p%`E)62V(OyxqPyu`-0~tq!(vy-Cp=2v4`UbNk!H2DboF3B zKGy_s!#03%>l9h~Ru81NzB8iRMTv<&Jj8Yq`Y=`%c4{rxmWDd9xSPg!6wG;i-0_Hd zi$*x=;ke%kx$qF3VY|73g#mwaqi8Trtx{omFr^pDlips~xVc@p+n2{z%{#HI4Jh>z z^nQDXFfjV4ymTcIt^VxKvd_~Y%g)Lo`tHp2bKyV>vYv!BBwO z`BZgfe^I~EqzMBfc*WTISz))H-rq&W(S&o+nbdY!*Hd+Ur@mB)xXeTw&M3P2=tRA%&#rrX0lgd^ zbu!p%msvuvfzm=?q|4C|1Q5imSphb*=5{1MjB_aLLbA3GZ4{5%0^9%X1rRVz94)WE zW{c`XNlGx9wX`Ma1jIh#Yu>O~#P%x+i<(NA!7Dq~$IKKU+dDFTEIz&P)YCK2w64~; zt^F3*7P@mE@2)~I(HE!m!|)rOk+JZu8Q;*(jWs5Zio)mrUd*XjK6j2ATP)9b`q+ExYIW42AEF5V2;G)i=PoG@-shsazbIyn4?5rXmSfB8rb!dJY0crY+LD8*OoJ^ZuvRbH+_-{RUs`B+9ivX(vqBJ~U*l0Ovk5wlN{8Cc zLd@x~=HpcLIFFiZTinf+GZWOn)p5xN1PCjHw(VvwX13c9fS)aboehYcbn)3FB;L{dBo=w@GMW zX74pi>3k8ms}QB{O=L8gcf%yeU+s?Ryy_LZ z$7#rMcBe6O0Rbcg#r6pk-l)7#Bn`WPo^4pSMD3G>@QJVmD~ZYD@3V4 zTLZqUgn^l%B-915-vtTKC!>#INntjk$%A&50%fPs^vLicBT}^(72_{sBg4aLEr#@^ zS+p&>L8`&QL9X?Wo7&oYWVr1HK;du?sL^@=HS{4UGpc6`4*sE=bYK+uPQfw83R`1sseA()=}V2H`-f*Q!z zs&7`M=H*K9)Fva*dgh$B@_7=0dN&>)un|aidvs?UlNSklK9X^7+O#FLAp)ubu@B$t%>*USFW^AQRdxpMEzfQrS(&yj|eN>~=BYgGaC2AJci6)e=ZZ zfPjx(pcX_LP0$?Gv;5KQwgb_CxYaPrkRn1G8)sp8m2KMy@dG_iqagb{sDn3nA1IEy zZ;=cOf?wIE&s+^+!ld>;a1&T9Xjq_>RZN@>i6j>tWiH#mZx>HMQgU24~nt~)riDiZw zirr{FxheD+wAUmwIh7%DpK4qIkJAng1n~hW7g+<_pS_q38Jr#~<5SiKYOjD!=EN(5VW}lyV_2=3!p%M;2^*lMYeD`j(UHFPe zFZ83XLtv1Y7~ZnRspXDKp2Vd@=-oD#M7Y2w#U@^;h6Sih-XYvey~`<<62tw1W77j- zht;)VvsEjPE11XZ{xGS5K`hqU&(#Kn;fY<^I#|jr#%?cOMCQ$^C^)}!zkh)pZ|7S& z{@l^xu)gkDFhH@{HV~0!ev4oPYqC_~_Fm1qtib}@T}Z9oRIiJO{D-U^5B8S4zJ z^z{Id_kij^%@>)H^!i!C??j`7saY$e;19;nIE<98aOw;lQ%A)nVgf2oGSQbv7|J z-Mn!esQh8Jdt(t3=oJ*3d1*^p_l~NU8P|g4r%Z+>)Qmp@-eQ#=tZ@;uzyxuOFaWoL<++aMFi$oQ9zi23)D-~6@0!}4DEr3d+9~b%2_Xb1)@Rc1)3T6{W`J~jDdM#| zrT{a19}W?cJv~ja%yTzVnOM_aP+_=IeAF?pWnyY97}`I<#|oGWL`FsiHg0uzzGu<( zz9bB{ptTUkLlAXRJ-_{aS{2|#H6D#$43nO~;^b8RURQ;*>xyg#z8K+7(O}`oJ9WEa zX=gjcpyoc^iqLcKy@tw9XVq(j^E#1TXD%c9L73Cbj5-)VwvqaaYGCz)v@%d{O1v)Y=yLCjN zF2I|sYcru3jql#(yf-Ta{0!OI*(KfaWWY3Z%r|Ys&GF=!lP7>&Bj_69`mmnqj|WHrKQ2xFPM;bA(!oRHMqv-R&1)DoStIo)%ZR;bvrK3y;U;4+f1;aK z!E>r|I59KOv4F2i_frRS>OZtDdGCfd;~3Q15c9Z@2!C9Uky&SzXl6&kaNOc{?)rht z$DF&MxHpy=mi2>(TrD;3sv(so-sTZ*icFi_i8zW&qjHK&is9)iWNHQzPC5&XeU<@q z{@u)+j#US#a6P5J+C9_TMiL85Ii&AH+lM>CJCsbzThHZp(Ic zXB(`TLRhEd!8<}NKce^9K~R8i3u^5GzMi_0oJWT$TkG(uDyFS~YA+eb3z8-5b~{&T zp=#p#^^&N!@L-36VvwDcxw+VKoHg$R8oO>}>jYWb4rahavkt2Rg`pL4c6 zvSzd}3BietAAg461IsqLqSvZbTH1HGw)P076wa7+v>8P&H99+4{f9#Nupe2)BOHjo z>4Rs+ebk%PqzqYgEQjY-hU~=1W9w@F-hrA)I21Cp5q;dG{enA-mfJ_x_J>ly^Df%c zD0m#bJzWeEzrhBUR4XW3w-2RLLU1L)6pZr%LB8D41RRS3sTWTwnd>woFmhuw%--#a zBZSTm71H^M{(xSJvDJDCD>0@+umh{T-Pc-lev7Et)hC%U&@pTQCKxw6wN1;`bR4Dc zx<~mi3my=g`5e(H{^-(;%lvu_zbz>)p0gk+@bwJW{0}J=pF1j_M-QQohkpbXvg^ak zP=Fa33eWu_aq%iDR`@wq(P}p76(S)L7K#|6V)_(Czuc7}7buw*z7z2xC-kg?4MTx; zeP(|aIVz}!(-~_s|AX8m<)qz0$pZoyP@TVv5+)M~5ULl6+dY|N>lj5*TAg0@dv`!+ z=h1xJ%H+p23Ln3^V-BWX&Ca_QGGHvYV`mTr|1~4OPj_M-Go*(#_&M|_IWNI^K>Ktv z18v!Qd8~6z1kX(u=oue_cGxN8_L7qM%S}=yf3LH`fo_up+l%yR#AQ$rZ1&{GQ#Bug zi9>H$Qs~T6c-|5(Fu-$Sd7plaww}|tCRV-0TMm~Y{rsG_thA_AL}n%yl_9bZdL43g zThn@Y=@jCGj~7PeIZEnqV?9{s%SHSPgeDVe{*v0Pgiz_2Rn>2f0=AX2K;zIgd?GQw zU`rTTOW~Y+Shq7p5C`*TN@t=>)3XA*KjvUYLG@bK4eQ8U^{Yg^3%Rx?0VNn~Cs~ z%FkrkrLvK?Y6<$=kO!UsEa8M9>tRfi?6%BjsrRu-E4{Sa>w`^{ps%M?@y%ul*MQY6TuQ>Oe z6OQTsZouKi82KX!_*a@ia!o_B3A7F* zAmGM|mQfzxK=UC;|HL^SgWoOY1fsJ7fsVO_ed0O7<|fKew9eLR3oyT=eS~#6dqi1c z$$95WCysP;Y6?VGW460YMxWvfA>ujnz>1fm7f?4k8BGzZto*v&NL4<_ps*u5nS~U^ zW({g-tfB2NMX7Z?VEj4{JzQ?H;jG8RipTN7E~mz-jJ8=0z8m4FXCD_6+*eX+>Dvry zHpQ-}KNmBgcdwZIP`q;hYA`QBtI#SkU+4Duh|oVou|jN~Uow}iW>La6z-m*xG%Z?* zOJ7-FN?L|8r`1EEoszMududmV0t~#H1G!PZgm{;37e~OOey(1GMupMSahuB-jsnxd zOT=(PdXc0n^u&*0RKS8Y<~&w{D@o#Hq8xdW)Ug^V@@3f2RXd-g2LKhaNp%@r- z?neCVbp>hR+|ty1jY&VkcgUfycKi!ia$wAfeCe}FyHFza^|f*gTdS1mDKR4sjNX%` zNJAxN5DlXv1`;~Kb31xO=FE`~`rt^joMXv29GtQTX7U0*of#N#83nFx<+4B}5xcuo z%=GfeZ@mamo7D5wh-Y6F=i3#Gg*m{@J>4Bhvz-vpq%dpM0hf z^HFjl!P!G%`360{XyG%})x~dqIWx1QhWr>36jmGvii=#)sHl!kX zYU>9~BY0;mcea&NbYJgSICprCxJ1Y}J4wv?B{d-_`&^^3?y{ZP8s##XrAH(|c&w*Y z`_+tG_yL>0jf6=s?=p^@`^IKl{T&fXd`wK$5N;qYP#K6rNkjZ_kxpqUS2Jq0nwM!F!(p=nm`n5bDHWfp1LqL=nU z`jen0vw*JdMJe8@$TH}wwgkW>nbfySkKWt88tQf5%Z}p0Av$?-REW>#~%}g=^f2 zGj9BiXyY48aG|Gn7cQH$zM8f24aY)LB9xiIKDF!jHtLCoXiF_Wr9~N|$Aa2BZtYKJ zuHlv|_aiFs-$1u5U@L?uOtQS36&+3@j*NZQ?uCp0a=a3V^q9GpeU6ZvG_dl3&pDlw z%6dOR@)+tTbGP7ym-rFK|7P62+INTy)b%%Htr__-!`dMp0{MO(ybuwR2N^8PHOz;$Nchh_Nb0*sMp`+>Gku~qW?h`!+arO8`VZ}aO6xB!rVsr~XDLEY0*y z24#-8siG+;*57aWW2$r@k_T|V&f=sBK2M%5jn{2Q?Q zpCk2jBLw=QniWN|f84(R{xM9^-$d#E=Pzc|-o1F$KKU9`i{E{t%>VO9&It+FANqJq zF4`-fot2wg?pn9>wZf&lGD;Dl)$^uK)Vbr{fFT(z3k_!A)e@6U7r zQ2VWb`Nt0*fB&_0^a7`U4%xmtsF|7Bi{@ZO*%tuSad`IRE1o+F3<|n*Y6YTDSTn$j z{_Ohw*9?G&;}a0*?oeVoHkP-1t#IBxtoV<^>X`VQZwBv>XI@`iTRXFNC0FltWzq3o zHuj%KNw*D9mF=D33P&>L=kH~2{^S2ipeWefMY)83^8a%x3Tin3ye@A`db<zWe7<0<@;r zZ(8#LbLP+e;jIdsy+x2)3vg20 zy!vgnEA5n;39#Sp46Pd3HOy-Il>IenI@XCSa}{_S7FknNvugpk*@%%25MrpRsuB?q zEt`jrE3cs&!r#GZph~LYXzcPjiMxyS_V+idm+=wM1*8T2<1yl$_UyZ{6RP;|Q7yQAJXjVHTldn$S4{tX;5GBRR) zm~Yh|88EoNagJqe0xbyyw6Ttl2l>Axvh!UcJeSD3WN5EAD>O5I-`r=|p!>IY^-FV8 z3;@09eA-CPN?YDmhJa7_rAoNz)(J=su)?Hiy&9wcwl!O(u{1w{d3uNg$wR{t4E-Um zun=0F9{_5xUz`0_CX5TN3%*=)oi!avs|uV;nwpMK*N?odoW1%Zcd=m!O0_7-2i#gU zH#H}9oY?&sCJ2K%bG5b>Hz~GSglxTwFX}wb*Ge-XocsCbcx_0pyPP}(K+&c#+CG(( z948$bD?I^5YtGzOl2iNr(F8cOD}u_Tu8i@5Tk(97&wASS5ImqUJJ71xsLO^f}|v4;}(F;q60BqsERf6 zk_%EDo9G>=aCxx)>{VM^>;5=x6Ecr{aRF=IZ1$@_4nU^?H`F9ni&sE=AusrG2C zG!qlkI#n{abC`x(yTLe~kEJb0i*3NP=;HkpKo=w7;#Tk93U#JbBR7tx7I*csk+NbG zHa8T^pIGJR<^r|*Y_gA>b|qOYT^~*^>+fx@tgHLPP3NiKKYe`Ug<)U1Y<<|h62V<`VgM}48U#?+J#Ak;&9{BGVX+ojNauGGcjVE z0hIR(<8HUS?Js5k*uJCVG@QVei9zSgEQi z8VpAoD%&!~EK=^MsiF4lS8VF#&VnUmpZX2-BCGw*mx>OjqP~XDd7Hbhwcc3;av8z| zJVt(}1$irPe_gyi;WQmou7iyANgs?RM@{2$`bDAc2NcQs+dN*a_4HE@1b|naCnSA~ zAZ>Me)d?RPGD!kZ2`@KJC}wtcKI|2Z zG2grFFPRSXiznx|pZfv{I&eX(=p0@fYi~WMzp*9bs|6baiJ)Mth=yq_Ikt`8K5y=~ z)#;p0*HHFyy=!kT$^(8kWi>T9d3ETloQgo3%JqDZyz=d3{LD?H<((_>=lt7e$6szs zGOE6(W}_e~TVJE11H0-?4g%4%zJc(Hi46i?0xn0TD`;bnqd)_?rPz zf4}w-X^tx? zw?6OH9?g}uJ6vq$3`UWnsobxf{&>&h>96FlE4+`B;aQ^9@npqTvn@4wH6@=(nZb#>P|w_nSa@Dt z!^=73+>DQAOWAHJPb#zg2!pcB5r+)+t)FtQHfSV0KHqFmg_5Iqjx^hyJ_DCmUO~NQ z4jdMp=fd+XT#tm zCeY91_C?ibcHSF<(0pH6LxI*(=DhmsI1d}}y!JAC3eYr!hxJVLBL+8T%Z}TcfKtL3 zwiA@TvNA?MXrFlh+2?w*NWH~@zRm+M^WusZ66)2bbfXJcr)r(ZJt8N#d40tCt_+Pi z92FhCJDS`daXvG5w%VGUVcVtb@Com|{*+*t7$8{-`2?r`Svx#1^oNR>O8Z7W?~9S7 zBwX+bmK$JBDucrggNi$BWC)-BxsKcu#G>1z90&t)?;+}#11uXVV{x+)@;Q<(>FX?- zjPPh&FaHmFZy6P37_JM`AxL+Jgo@JLg0zH4NlOe$_mD$L!w}L9BHc(g(%s$N-7(+m zK6`&#J?pIX{ruKh`v+^yVi@Lq?x*g!?&~_5(>Mk?I-xGK^J}IFPZLGWuc_UZjMzum zXS8N=uXG*pEQQYT^z(c;N$8p3E0+A9lJi(E8e4e2dL*7|;Ir&J51@7-K^A!}5>ALX z*ruF>Vu5d zoQ?AVJ%0_)5TTW!z3>cseJH-nxMy^HoM7@ebB!0fbL)_U3f*r z%B!to8cynK1=}sy3l0vxu9toVF*$g7wcy4o&QZP8bUnB9$iv9~?Pl4HarK3gMzMBt zIOP#rs%=!_G&T%X^jag+9JpL^ai7zpeb1oKx%$>F=P5;i!fAozcL>{-7X9=bi@z~O}rOz z#l_6akjvFj-K_Pr$kg;*-h=)LwCiNy#3}gG2rs%9x^Dn9)GsE_)OYonMgwl;xZNX^ zXtQsV#G=zR-R;&v)j|&Nu{-p|S++RDp+sJ6klX&xJe{AQ!d8-Q(%+@Ww=mheWrKqF z=yC6~J}LP0&v&$b)V5pQqNhq=Xkir7l((wCxi1z>8o%3E~-V;Cc9gM@ zIH`@)&Ph1~jeDGXjoyi9Zv%jfRLERWGwk80%e7(^A3w0b5QNchjfpqDDQ52FZEOWn zb3g7leF_?!&Ui~`cCoJ%FcRJZ_UFCRD2Fhv5lO(cPkUz0an{@zwxG7nGS8FUv(1)S z_-A&@(hntzNVGit>4!Siubi%{+g>Pk1K#|sRPUrTYLVZQ{He_~@M%lK@az|eSt&p0 zHL{Nr+~KD(XaPXXagb@_M3)}=MLH4zi7-~0!aymJXP zs&sy{UARd5gnK`0iICXiIXUtalFN#amn%LhNrzW8Zyg$I&P+i(4toKI>`aoO!+)2!7B4Wqr%0+@)dWv zKeYrE6Tl8|av7bjpmUWwv+p`YlOxN#IxZg}x|XamAWntyst<{zg#?2n`~0iPM`y6h`?(aD{9=ob(y@OOxk+kS&`}=F#%BxQQ z&i+t^MP29ZVcKu*r>bacmK`21HK`bmiu;#o=>I|!`b!jl>;32a2ij(tr z`m`@@Nzie3oOcmywA6&4C)ZVcS4O}3Jni93xs=qguuMD*q-PKm0)#9mjKgRx05RZ~`znSw_d`htiuAci<(X&2xKUAH<74`sr5bN|(oTA;LCf3Kf zP|M7WR`2vbE2F=2FWl%jj(x4YIyxrT2c|;_ji1MUjAThojZe}nX02Bo5grvJ3W8qO zyV6gjiFlBKL^{f+xMBO^y_BXpSC=i$fs2X`9hY26lXhz5d zn`ReU5lA|?vXaYbs-UpYIKYLdMX>9Va<1B9p$>?id?s?%-z6l7M*lp)d_e$4S?_wB z>Y6U`;rtHB%7P6SnM7}Tusx0?0L?xcL~`lclj?fm(8b?^mkX(HY9d#zy4$gTceM*w z?Upgf$FbdqQwsYFgptF%QK{kqlpq++UnNFwy`DK60 zU;u7-=hXQ4v8h|}Ywu>59|K6M8R zgpbLBV^UB?J3w43%WU7OgjfR*HD80>Z;AfqM$mi!s6f&Hf{9n!Ri!~D>Q0lE)eHVP zDYktPKU#p~^*~bfa;y>{?qUF0({Czs>{IVI1*i@58o(-Ya!=^bwj=5HtF4RBL!`ne z0C{zd=36yA9ToMDQui=`USisfI-2SPVUM?Pi^$*kv+VN>zIZsm>${s7dK_5vLD+Y( zAQF4KeWPvy!WGd*`2{6uHQ*U^ZJx@?!pnFLU@g{5PlbU7&B_?DYYVy*i#)00h-?F(udN?bb$_ z26-V;O!dFYPY_r{8iy*VvmJAl7W5uN!CCE+zh}SM*qcnJ0%%pus>UC53qR{RaBsPwN?xS-=@J} zqOkofk&r`Sm(lggkI&7z`Ok5?*La=>bd6`9hW2PmG-ir23N_C?X`=LPj-_Tr)?-j!3tcG5# zM-Qb-t&2O5c1U7MP7^b|{}Q;tjHQyw%ys!nyLx#VlqCGxmSfwI6aMzu8+lW$!@ZK` zK~gUFbtg=uZ%{p)or}!clncL-g0da8!bC&Xba~`(NuiIs)>M4X{Tf z05>d{&bplBP`{+|*341v3LT#Ji*_0T*Zh;#r^ zjy#s`fS?4B41fTvFcs85+^}_7HK~q5tCl{&+O+26|8C`^%@-A`oSn+4JTgzStz3D^ zg_J>nvz5nuvhZf585Tjv{R5{E)zUIYF-5>>pU>Qy*<1KxZS~XhyR6&s92L~?iI;5>kOg^du| zg-)1qR~HH+MXz98i%DKxjxjEa@6}{u<)Vr@P7=XOtG{dmQzqo!@W`|!inR+-JG8?M zuniAK4==Qw_n$o-TPWmwxEO068yFQsM5M)u5>v-R^+`eQLboMaWjJZlPI{GBIi7js zyw|xA%+m8-E4c6SX!iM%z*VyS(8GcN?-s1-h(E4+Y|N!JYJ_yUtLE#6O>@tutrnLC z3j*pjXbR}vrXpG!;bLU-7tF?7Jdp=A8#8arNqs13g&B$Bv6(|6Lp2lY>Rd75D`TZN z9#Ta**U8#;b}~t(<8z~*KczVOx#mNxu_~XlRca^VFU+`()6!0zxi2f3Y_#5vG@R61 zdaTD*M3XZ6(_8~5OdnrJz)Zqk9%Cb@-`Xq}+l_{Fm3u;xSs53b%*UP&mymfd_l1W$lOe@5|sF(Is;%EKGo z>qNj8CRX~LQ#VM`$mz?gFFyS;MB`6I_}v3?F?qi89rYL)TKnJ1M1dQkEcou3`+^NJ zq`Ff!rYYq%@OpIgm%Ie+hYlBXbYCx|c%v~Nq0-qWpW{bfWrPl43t~eyYSTDK5h6W) zmB+~Eykk+A$uYT~mo?1CB!Ao1@4XbEfy84?U$j3mWp!-IU>JF;b{)l3aE_XbQtus#l{~G@H9JF{Loo2f!4q?aN zb;G~;Ix`SACLMnMz$>cMRLx$AwN8DYJ$hu#0>AM<-ZxjVf!htiG~xDKWOruxJ#~M~ zq5&ULatj;9$IY1koYwz-kAMNb@rr4sYF){;C(S;pIc=%%{(P!Yn6>{~%Dd>k_Ca&Q z;~O=CDoEzo!5zPOjg*)uszTIX|KVJRde?1!8EYi2DtasD5|30r6LYAVKi|B5ku=oF3hJSq4pc0+$%Ci!k zI_ELcHSCwngnja+tLA^NSl~~YKOsnH?Y(g4b_Cp^yv+v2UdB+&;GWd7y^h2uylg+P~)e+j0=1r-vJum)njFS zwqC^F&+EN{54N0qIL7#xDFdJeSfj`dIsao{{V^ahgs=F@i^PM2QGZSTZxegd(WW>2 z-}oU>bY8l#IXAq)U7!i@Z#yraGvEk%uoi(!SW@KP_V&Jb`LeICFS=(PhkP~(M9{rN z{mHgM_AmS4fql`}U&b9C9`auAgJyTWT`_HiueB&KswN9Y_h4otXJ=;{_j#17e*CiD zq1>Gy{nu&%XQ7{8qHv&ELaopVJ(~t0S=#oU$x1FTsiiM|9$Z9w1Lv2|+}`IIKjU~7 zle)yvHtqQZoRxHV^Up+LdW(I2W}_r^n*7auK=y(5gF);Y;KahnaH2E04hpzU#(8j% zMSm}PKZ0GRSSQIfCoAV~Ohodj$Ta$|?evW86zN#izqmKDWR?sHEC5lI9`YzJ?gEyN zop}+r8MD-@p9wD{kW6^~S%x42V14$~F7QJ=thUxG+biV$wo*jPXK^vmv20D4+-)@6 zg6cnS^A({pA1Uq4?RETP-H4V1aWNN&={%_9{a=;?E(t|*gx$~=ym#7+0>|){P%8wM^7dNP6SY=$K{LvZCO+@0+uMUE+J<6d%Xbr z66N>4v`uT2`^O-7|JM#FY)<>*$Hbk30qd8~Z}b=9{<82Bnqg3UNf1} z4t)|oPx*w_oEgq?5dPc6oG}xl&hKM8nuUc01r>#bEHGIDl>;b>>)szz#fON-R&2^g z8OE_44z$<2IxaYIdGTVR^rSNR6<&~&6XSGVaj|0dXaXjLQ-|z1y{ATfUEK@7#~jgo z5%VD@ly8w%J-30HE*E!@=)7uQs`0#jKP}6H1)OQ@YQIoYo|#!u5MlukD8FE3J=)m` z>M9uB8Aq$`+5K}{*=*tlO8LAZ@dA(AV5hwmCFCEW%F|vJOt~L7|QD9QH^%SXnP>u@NU_OrGA6{5(PW zY;Ap1J=@z0X4xhk(OGIoVgn6Q)X9M|E6VaByPiIOh~-xKYq68i!FSGCS{^XFRF%r8 zqjbp`Kfiyd8j$>RttTrYnGPfdIa}%+)PPMG!Vkm4FJogT zFE4rgPM$j_^cY7e-_Op@g4HH|q2nXuxYD(5P*?~B)`@x2pR$`IFFSq}k@-oyhON)(A(Z_3RfJ_=dVi?Fh^XUDSr`f@%5YOhcKP~Kb8N0#ljd@;HniQFwbQo2# zzB4y{ER!+B$^IjUNfr%n<<<1V%=k zqi$FGE*BFTX?oSgjYVN)&PGP`^7%E)rQI>*YVrYwcR7s&sX9_5_u6FI<({()5 z63%21ob@69bB=Co&@~Vyo%vu88I_Xo7RToEktyDpq_SGIUF=P*F@}DNyK;-Y7l3lZ zuk&(PkU#gv>^ToFq73ms3i1YiRNmWVZu+*CEMy_#DlcM7nvRd{d`b4DNqD%s9NXL8 zLh38UK~7UsI|+GR7x>z>hav9!Si({}5FvLeOX+dU1e!AB|*4o2ELSCM;E+q%g z--}Hc7VVRFXr&m8?A_Ew+SluI^VZ^KX8zBu-9q{X?(^+r^`JCR1^*GF@77 z;El3rfGxbtgKRWo^BUz*Zb1RVq}M9nuJgg{fO6W~i&FJ|{nF+AJdo1=!fM@g@-}(@ zq+?oW59uw5?rlej=qBn!_M7p~ct5?`UHll*l z7%tx(p5bk!I4=T-tVqJXIHgps&o`%p&36k&ySsZkOhOoXbJKm=AF-Ps)}O1k^}OjX z;IHzKoGOh#$!V`1dQa})Y2hZ+2R6t!h7MQ<|Fvn15VrL?+lvCQR$fDP^KyPOTu@lc zm^&N|rU;TS=|dhau15i})yB_fn?JDm1|4Jp2j^CW@@36M|fIYQD@Y z;lP81!|nCXJ2^RJ^WvFXTPwu#(I)LZfZkdgT;0o%DC+r;|D$fR;cO_0>+>g)p}S!T z?8fU=jPI(5t3qd825vCOHMZl=pEcWM`WEvw@DeCv$M!K-d7REF-=TeD7)Uo;!KD&? z_IO|8-8llpdv9FkA?<+6w8S?4kQSH*;Xz;myGa6CF!4bhY)(hBj(19)b~7e%v=>Z@ ziGht_Z$;2bdz|7ea(X&@(x+#v>UJ#0ekjqS1swKPECYyA4}Tr5Qb@-I0QG;m zG%23-44&UcfUgDKN%aB1*T~!KLO=?LE)eOd4$u-6)E~ED01W<3E@ztn(JzNI9bOn< zbSR$}A}W>vqrulgaD!c|k9~=zQ^(7ahH{s&&5EH^IWD-H$kv+q%flt5lOLOm^B0&C z^pT?J;o5ztU$c3ye(0&(TmpcJXJl{9R@ll za<8l!j|L!AHh*GY=L)4$nA2R zSN~xXkha8Q#(_e0Z71X!JQVtvm|VRbo8I?~?{XLL3v#n*7;J_n*l!MI{A@KppdazQ z!!A9i!SF+_0bl7k>9K_bX3+cCJnUo5{s4T5%*?1|G%`|Xm-GI#H~iPljhd&G66UA^ z#<#YBa|sPGbKo3ixD|%iO++1dnWVo`D+Wp8aNO;DN?y7?qTBu|@K`pdZ|y`Bb_QJn zuRT?G`lO((TlyR`?RpCUa?P=$bMf*{O->qmd@uRLNCKJEQe{-4Jwy|R(!vG6YPV7W zQUFg}UJ=Ua!||g7wm&ZLWiEQJ(aspfT$VSkD{b~ugl{er52+rb?Z7<% z_t{Rp z(PIBmNst$+0WA}b_3^(&F?Mrvlh)OxG`eP^iBoN;+nCr&)mDnGp%~>yPBRPG&3{30 z)ahHuEYFGewL=RY;Pu)ERHQT9hBC!L;Q?QGGHxgY&qr_<~T9IsITJf@HtKn2+?dwpNtvcQ7vQ zh_w41Hb&@zbKH<_%^BzaC|li{9Ot3S(B*r7l!2J*tK}NiG=@;s>e|YoWtBhU&E`5`)_}XnNgsl$`mRC1trR}F(S*`!eRg&I)HL0FY@A!nWB_OVWZX zP&nn6a9E5D`?M9~j)Y;LZqP64c4@)5vld`J5AkxW(QKi98x|7IP??JgyP34qCdZ~) zFPy8gX2Rh+zl`*DK?-{prrOK2#T(T-a?p>=DEWKAwPQo4JC_vKJ|m(a@1yB(d!qx# z6_+DWPkE+icfvr-+DJkJFe0|I#&64{Pk@G!k)N_q_QkS7+y}@8eJpxzX~tSsSj>nT zqRiZWkl25_Xx1olb=aGHHO+#=v(zfg~jPb1|G_1HH`-T*idV9lbB%@o2_3_t!WPxCuM|9#!o zbU+&o9^G|Tk=82;x%+m0Qi}9vP65MPsz`degnsD1Jug^~nc#C6 zBF@0jcV`9k8kCd9F}PFCD-oh5p!18EN&iL6x}27Dgla@ue`PRP1Yz#&#ZZwx?A~?O zUjdLO|LMqrD#cJ#RMgLic7Rn08fiN#Za9_u)uE=LdodcqN-LG_T{;B9yFHMlm4vMX zPy8NdOA67;m~WpIv9P9Q7ZSp32oGaa6582;1|ykVHFU2V0-j_a`S zA-KP_H%5G2o^>ek2y)~m#J3F;E{f<8F+4d#^ZEo?4N-4(7_Z%$G|J*sM0b+C`K~c>|k9Q?C_ou)O zlo1e=&9s`L>=GNtR7rJme~quSKUYs|2qh1t|!h=1wZc=Iw6P!uxsYe2?~Ir!gyZynoo11@5dA57_Og1X8)XSoh&tkF4N=MBiNC(*l~^1aHi?6RzexurV}3sh7mI zjQdK$lhGV4tk3o4kNWE}x}v;4iz+<%bbvv{b#v1-8EQf+Ooe(bEI<-tRJT^!8PwgG zQ=qfCW~cw(@!~QUqZby4?;Z1l$>TbUnwITHW??cMxhoBFkaxye)?9_hbJh2W586?AxyG*= zA5wJDHxW$agutotIK?tQqqRfO?m(F!{Cw7QcQ6A>yB#T5->w>7%*dP?cN>m znKl3wcIu_M?|IRzHOFnf1MiIRVvySL?4b?BlpXE$&p&y{FrjiiXR=p zry1*MEf4rKK)MSaO#gwv1DX$Ta2-juj5(IU%Ir|Ar;KOZ8QX^+d~zR1&bIb?aJFrz z$h~*i-i$v$U`H^Ij$Xp)o?asp2R2t$bB*D!GPGkHG@n2pgc3`KH&pM>g~MYtI(wux zkPtSCeNFCME95_IzMqf$BZt(oBL@c|iSw7T{A{`L^NdAeDM4m`z9NZmvn}t4g|RDY z0^qfouW(c1`_3pThzP%9A2=eYoz{Azf?)E1}1c8zi*337C}uEq+DX)p$`#U^u%6%NgD; zOal1K2prJ?i9MY&XD^MZ$&uRpB)T8(C3J9)7U_Qk>^}c^%1bh%eAunQ9cb` zKnUGLL#OqbkHf8*stUD>+S5RGR%m@Ykl3fDDjDg1Cx%g=?FoopwGm8PKR6(S>IGf$ zIEYYkSC@4%hG^k+w%ZZIF0ElzhY617?^B7__ROstFD64`bv}^w9Ema%w)A8s1|7b( z@beCP?fXqh1K>=187nF0qg3at$Li*>2BDS0L;7>SzucCCyoZwnXVXFFvq+z{Ui6Dzux&^#rap zlrP6_T;xUK)uN*lGUs}GSgFr%E~9a!D(C*RW`l#-oRb?BLTQ!pfq;*`7I|W(Q(-04 zs#;~RW${m7cEUqTKVzTnxT zeTC(+uAOcNUv6{trBkM(SF()6uk=C*S0dG*Y_)I=-s0Zi6?*L^4bhKLOmD)CkCzb8 zC^odk3`3In;9YSaDoR~{>t=|5Nhb!vF^_429Y3H|BeUXNl1K{k!)f_xVICB^ykzlA z<+!y|BNeyR$E|gV9)eQJN-!xT|7X%V-3*;8lzJ%XyLHnBksfZ?9{E8qQiowUD2a!@ zpHw{Sv&w9@wUf*iHf?1gxI;5>YDWdBgfEy<0&bQYvwT38HDPaWHxE^d>V>5wNBik> z)trboXqKw>_zCQk7denaLjbFWVB4ab( zWZ@Vl!NN6fIlXImXXD-uZYn1et@EK|!6?M?VoJ+y;<1=+`Ev6M8UJz!0|x#{wTV~U zSlAG%y8gJSxIT!62RI`%{VQq{IWWvJwnnD-Mpd$!nfhp91sC+&y?71+flf97s-ciM zt46{(TBXeW(NxV1Ip5~#nRp!~B;{kRH#S(vJz}D)Ci$2qL0G+K{=kFE0F&@O$T8DEQ zfw)@gyg#`4#GhQH!w(zBF(d=t@IFY%eYGO(p#VekvuMofz$Kz>r(O%)Jl}!JJ|B#T zMbidQjG;%3cNU9|xSggiUgkd6(35>Ci7#|De_ObW-4J?nOV|8(FjuX@H1%7Kq5M+B z&bNyFtthyt<^Co)@D}%s8D|hp>e>&(+bC%I_!MPvuIp`?&vLegjtJm!q&jenOa%G$ zJPe4&-O;~oS-y{?;}3P+hFkBnO0sg0ud4PS%BJp9LUwY~|l zmc5~sfYav{Kl1A;kCsVSvK@(=oSbL^u(NKLGoT)Y!I=sWr!D0g9KOMGJgkcze@{ll z#iOf5_p_>ySfFdr4M@pmba%Bjxn7XP4tHs`oO|lU@H#K*gg!z0%wZszo{EmoXds;q zmN(**pHZ2}VC!sZCYLxnhBcp$hF}l_Fpoiogn)xrN zQ(c_aeuS}9NpBdM-xZW2F*F0jY=ke>4SZ`hVztfYni}sI-=%U{?#@WQZm}HLooKLz zmg}&nz!`!?U$uImMZ6>DHO%=9(X*b?ZgM9T`1YNGpHzp=5K9sa^kxX+(cs;cYFYLC{yT1u$hw_}s6GeDo_)~q!f ze%3}lc4pDWzZlc*bz@fTad6Rk>*9jE*DiPu#H-6d=w6K|et1xsL31alzdnkD;c7La}MlYInIqMd7C;eDR@>>%CfQ)MA3>=Sm?m zW_zMtDN1E~P1-a9{kyxL-4T1mjX>s)_V%8ElHtuer4rq{?tHZd)1*tAx4G!6XDeQn z-t9HFvj8s3%V!GygZhWm=HrIJ%U=@M!hVr-h0Xdflf&^t0tGP_eYUzb41W)id*fLl z*4(XVZk{%?SkFBM5mbO*JxxWkIf2j4Cb5fk0nUK9#Xa@2Ak>U|U z$RULn8oF)=z?SVSSMsm1_4tMiNL-XY2>Du~ObAvTiG8eiLq%`7{?d0V1=V@c?Whrm z#L4xSBC<~IZ!ehd$*mi_g&N>OTm${Km{X^t7|2Z>V{02y>sADJvY8yrF0C;& z@8%b@p~G573L8M`u-TlcN89#3zgILDvY^M;6?-$62TNUowZmuLx@+B1n(&rDIJCvb zdg(*oQ#c%_qhtfS-Ew)*tAT?L0Hab_ZyLSwP>0XT`|ed%mS;Nw&|JB;YiobhW=k>H zq%rW8_;N?lkGu3-myH)q(3bo)ylK%{ALBLBOQJ+liI1K+WUiLXmI7=hJD!LWEl7z5 zWntiCX>3L~r{mkPmu#~mOl1cn*a?$S(N}AGdx8C3o?T}M1@H&o$Lzi_VTs>81%)wD zx(tEdHbhl>8Ap-s()Q*mRp|0JW8*ack)coKK_bEC#F943lF|A~W!r~vWa!7=e%C@t z8KnL}-S0HXxpvJO1DQW-JAXg(oc4)&NW1wvwY(3BWdQuUF#0EOdG>EDRl1BxV2d^w zbzLUrXQ|YgdF~_gHJ*%a8TbQK_asYWW6Db{0#3{(-w@iGaw@A^Z>>TdIjXge!o;so55Q z5hPxHBxhiv5QBq)-~LNXO*Ka`ET;U2384zR!>h?(Ik3?aWmtm-RBoENuePlV|EMgN zRFp@vH40UAm>v3Ye~|YP&pZw?>#B14))QY#6b?Ct)8A95m7&3tBUlr z1vg%CU&Y?-(Cr5uL3WUGY~R*6SNtrW>~;KM(PrO|`f_E{ie{q(P!7HM`C%rdS%8QCWmyP0>!tu`;kKdE0DF z+wrSXbe2XbSeNs5L))`e3pFK4gPso$e-aye<9`3{Noo&C6r8a>rK zws?XpDrn)lFM;Lx=OSH1BJmFR6w@aeLhq!t^Q18({GbQv1uXpep~ zJB{?uQ(5s}Y;WRlVC5D({YWhF9ea74+0r?^s3$Nr<`sjr@3kYI&Y96DA7Y`cLByCd zW3BI}jnRH`g{@6!W*9F=cQ!W>mU#3{_{TCnA78JetqB~BVq@>_{Rz?lFlnvT%HSON zQF!lte?2rm34v$@>V#Km@3650CZfaw#T9o!P|!~le=h~}m-~@;g_mDhIrsecIq~`R zozG(_E~Y_yCs|?$4=|P|h5es$(BSJlnkSo7zu)kv{<%iz#o)Y!5q_?#MQ(4WE4t4r zn1BuiM&SIi0^SI)YLp7D<{YZR&^-qd(8IN{i8;>uyNU)dnPk#SPIizADr)|WX#j8% zXEdCZrO)mS&}GXMvQlyAVJ~k_vb4w=5zQQWae3h;t#~21@sQ_LAvSrEKUU3uCPu?0T57Ml z;MWcRd^X@crZ--B*R`u}ALSZ!1Q+-T0RWsR;ZdSx>>nZa|9aUF4j^G1`TQ@C>61%B zD2UiO<^RU)kkEN6+gz9`j`pGWPxpT6{n5aBY_;xbEHyA&Caoq;1}pV8nSH6{cMpZJ=yJMT91v`Ng5 z=wCzK`d?4(4Ud*yHLSzmwF_6P zAR-4$=eM`;Zja+nyi9TbvD5=zi%LmJlRk%p?0MsrM8>!h)xOOAF-K!|7%KMU#6)CP zeZ8Q>8{A)E#^Fus6=|{5Nd%-D%EWW_ENAxwH|t}3PWmEU96d^rN+MxKR~z;$(f_Ak zfBUo+y;PO4k3IkRh&lAw6yYMxj|~f%K*F$v;K}aEX2%EJQVjL7|h5qVsnpSDrP$ZhN5Y zc>wM$Ki{=Kp8coe&-ge2LBWLJB_;LfKM^*+bNe*%G@*4{ob|{}Tc_+st1+#=wg8b& zXGceN@=i+_Eeka&W~7Hirs-v^Zd`x}=b|I402h8rN3}fi<;!PV4!A za&nWWQzs|^AnvH2WYcb-6N;n(z6v01JbcadJ&Ha>)_iqYa^wH--ya_S>+3oB(pIQq zwrql)Mv1!Df)91Ca$WTlhqw(^pXKL^&+1eLe7mA|F>j-$?%PV6*IzM1ox>(h zI3P`qcVFk^^KOOS-bD^ABcX`y9!ZP%;*;FxHdEhVDjFPTbzI(&HvH_OdR28vtnC)g zz#}79^TP97COo-oapDw8L)zIaog;vm6`}RNA7yxe2vP6dr#kYgzdVQkI7O6k0H}ia zG|T^ndi|dd{nvK+AM5=mRQG=j@jr(6ugm^_-HHFRBL084BBXMZKj&2&Idp$4_{;wW z0N5#BuBXO}hUe}qL~ERQVX<*>TfAQhurAJ@@y{+S3}gThP6tQFjV;?hYMTE!<2ZPM z=&R50whsoLMel@5X7Y}OxU#ac_Hi>tD>99J#&e4jZftBkpriz#y#R;1#cN-|f810c zbf5q(8?26=nFJn7R~Dj!ogIGnYa4BC?P6SRZtjBv%DfMDZGPwgbPPxbJpB0aV>mjk zk7rddVf3*e^!zf-D*x{R!J`u(0#1r-6Ndg{Z`=_7RcVgX-#!? zqA;l?r?I)mGsxpmAZ)?S335=N?{TvcXYV5QC1BfYbQIp$1F~h%1ZRu-K*|2@&$#LL z02Hy{w-(bV4r?nqGgaPNkUWh zJ6Bs!W+Zc3WVvRPeeC(_BKA+=nOwgL)mLgGa6FgoA7_N z`#n-&gupKZo+c(H2DpKeQ9aMt?{^%6zZpmd$6-T{yU&GwSw;;Q_7GG0MC_o+i&=OxD} z07j;=)8i2QC5bGlxNdiVciw?s9g*mAZ+CaT83F?mr@v|+*OeBhW=6dC9>WC>4Z`5= zVT6Q)$s73v#UBA-^m%^!L6JL)!9Ys*Em#TU;rQWdsmtvl7(4aV-|bh=Y-aJ4eN#SF zR|&?1IyyHWq#PMEHUjt=CRJGnZk6+BCZ#q*Phg?5YT6t&-|sbZQ3;WJkBcd)`O=~P zxS^lwIH~3Sw%z_GOS7nR>%5G$!z}>kemHanj4-hnW$?KXgGm244}e~-WS${jdCJR6 zNIcx%UAe~|L<5$-160GSeYx-(` z!I$`Rm|P24dh|b8tR}^Sn&o3y@q&Fa7)6D+& zK>E+fJ{X2!dNCZWixQ^OXcka?f;E{zBB z=-#kb_!%OKdv>+ZRp%UdT;2R|08$dE&#t{$&6Yp9-l1eEx|*zRas-H&{FXRF9s%VM z*cR;GUS9s?;v-OIibA^w(Eo{GM=V8Yw>$1BJwc;Qg-ux20ky~Z2r4z$>;3qB833?6 zJuNFdz6y74SX&A3^=Fq)0e@b2Z_(>L?9cEB+6P0j$$U0QEK9 z;9@UC)x1|+51>qhL7O_0LuvfxMB%sX2bO2V{YMH8cmUt#xYV!56z4}eqLBACb5%tb z7mi3<&J

*=PAeA_V|Ub09^YToZZucZbBRMl+z3t=|6xu#|N|F$@a3h87~=Ks9t7 zM&90gUpfyMkZ$v@bMM?rplq2>A0gOsm|fVuQ83O{RsF8+%!Ci_jGyLfDpb@-G)v?M>R3H%;bDnVJbJBJC@@yE|%h9@WC{PGEkw zKi7DbkTa!gJF`LQ5|$+FtiEtvryyI`kjbK_bpxWltpJMO#*^|&<8T6LLz;ySlZB)W zp(RZj8Jr_A8ne3p{>OUZWs)6^?$* z%Bp{*a15I4*J5^Yzph!0d#%+gbf09^fF9ZGISj3u(bQ6Mt#6d~1Qp@Hcf{*(2JXfXp4Qc#o$!*MNj zB8i%{maeW;eHNmyRg{uIsRUE#B32b3Q?ZxK3Y)TieCd5Pr3;~CZItM#IHy$foIf1{ zDd^iA`ji*MxTypKDFOfFCj<&S4Ea`|l<*(YR&t^T{myQRe?R0z-`&yW(bjMB_K@@b& z*$3Ke`g`kpM>3@!lDYh7&&O>R?oE)r0C(RJJi{#$w(rFJ4t5$8e9}ljt4mn2yT9vx z9T`9kS+%9u;rubAS?++2zo2qNc|ZwdYXF$z{Rq_4kJg|gn2;r>*{BQaizaOTRuE## zbeekXmU+H(8e8p`vxYPK*b%4q30-*by}m{>fYWQ3Po3qH_waa#3W=oldO2LKMTUVH>Ovmo7?Y2Pa2dkKqYYzTJcfrIgAF&PWkyvVw zH`JPjENyC#my1jJ9zcNHAHg1)t?Lh}DCeAMc3g^tADv={a|Ubew+`p3v4a%705tu@ z0Kle;vd{!n{&%qzkXsWyTd}=vU`f1|Sebk1HLM{Xmn9;7tM_JLpEJ>8n7aW0bxOYC zwp8+#@gw64-tlg^+u=0bU#PaeD%NdCgL?VI@ir*juFFQ7Ay%9OJ!cMyL6$$Iyb&Z| z-24;o^g9{ry@EGfB%t$&J{hu5m&5we4p%NUd$Xizlk5uw5UAv}nssPh?a*kx88`y; zVQoX{V{E^}Rv^`=1y~oaLzQ6pF(oP^}xj5Q)LJ^`c!?O}ia;ZaPc(HJIXfaRI3Jp<35&Y)VS| z^ou_BKz*wZV`6SL?{FVSG*kXrqDDYL9V&j>$#}n7T(3EnI($1gh@^7cr%|X`Zv6Zm z4NvOlLj}j9m6Xe;s;S`9pqy0K7kWLCSK&Z^6svZX#po~D?jvl-b~{Tdl-1+(TTiZs zX`d=UeO2tadW_!JHM2M}B2M%0o8|HZ3ZV#a=fj|3I_iM}MJn)d2+;5YfjL8z)e zi;LrYx5ED!lL{@)TWFYEhq?e4g5|+Mw`1~dAt#icl0CG0OW3JqRb{+p`2XSSEraT4 zx2zwzTy`P<3^;PkMsuU~T ztGn->_Z(wh!(w1o@ZqVT{dtG^%Gz!Dp20oFR6><~Co_G=V0b_|@ACNB4no zqBZbs!}+{YeNz)&4`jJVo?vk%W6Gzsg`uz%E(I0O&F8b@ebJwoOKZQK@6!)vQmA9~ zs@kG;yIOUC%&HVDruJ=p^B%hnU@k9;fSZfsm(msB&&Oue=xwQRWj< z?gSF1eZYG@-j4@PrMDqf*|i+Q`W{qN47)Cl0v$g#Sd;(rL?IE8!+@GvGDVzuCWRF% zWC^uGs|8zcBG4_5?LDybLir(o@42+m{jnlQu)OwO zU=Adxa&Xr_KX!$vSytp!SLf&>2M4sAwtK8JJB?0voUJ|~G)b&ZWU@aUmQ;+lw=rLC zV=7p$)o1vpuCDe6$W3&df0T2=VxG;_6UDzaW}8R-4Fcc4K0eOrxL(9)YDgjCbRj)x zH_U_gyK0U9daIc*_)Wt);G|dK=pf@n&t>3@%tjN0bP%0Aj;7Z1$JhvmZyB%Edp&`@ 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 Date: Mon, 17 Aug 2026 11:54:34 +0200 Subject: [PATCH 02/11] Address Codex review of Grok SuperGrok routing --- Sources/CodexBar/ProviderRegistry.swift | 57 +- .../CodexBar/UsageStore+TokenAccounts.swift | 312 +++-- .../Grok/GrokProviderDescriptor.swift | 1106 +++++++++-------- .../GrokSettingsReaderTests.swift | 21 + 4 files changed, 801 insertions(+), 695 deletions(-) diff --git a/Sources/CodexBar/ProviderRegistry.swift b/Sources/CodexBar/ProviderRegistry.swift index 769cb752ea..e4472d027b 100644 --- a/Sources/CodexBar/ProviderRegistry.swift +++ b/Sources/CodexBar/ProviderRegistry.swift @@ -41,9 +41,11 @@ struct ProviderRegistry { provider: provider, settings: settings, override: nil) - let sourceMode = ProviderCatalog.implementation(for: provider)? - .sourceMode(context: ProviderSourceModeContext(provider: provider, settings: settings)) - ?? .auto + let sourceMode = Self.resolvedSourceMode( + provider: provider, + settings: settings, + account: account) + let snapshot = Self.makeSettingsSnapshot(settings: settings, tokenOverride: nil) let env = Self.makeEnvironment( base: environmentBase, @@ -56,7 +58,8 @@ struct ProviderRegistry { runtime: .app, sourceMode: sourceMode, includeCredits: false, - includeOptionalUsage: ProviderTokenAccountSelection.shouldIncludeOptionalUsage( + includeOptionalUsage: + ProviderTokenAccountSelection.shouldIncludeOptionalUsage( provider: provider, settings: settings, override: nil), @@ -88,7 +91,8 @@ struct ProviderRegistry { costUsageHistoryDays: settings.costUsageHistoryDays, persistsCLISessions: true, persistentCLISessionIdleWindow: Self.persistentCLISessionIdleWindow( - refreshInterval: Self.nominalRefreshInterval(for: settings.refreshFrequency))) + refreshInterval: Self.nominalRefreshInterval( + for: settings.refreshFrequency))) }) specs[provider] = spec } @@ -105,7 +109,23 @@ struct ProviderRegistry { /// when specs are built, so `.adaptive` maps to the policy's nominal interval instead of a /// live decision; `.manual` stays nil. static func nominalRefreshInterval(for frequency: RefreshFrequency) -> TimeInterval? { - frequency.usesAdaptivePolicy ? AdaptiveRefreshPolicy.nominalIntervalForHeuristics : frequency.seconds + frequency.usesAdaptivePolicy + ? AdaptiveRefreshPolicy.nominalIntervalForHeuristics : frequency.seconds + } + + @MainActor + static func resolvedSourceMode( + provider: UsageProvider, + settings: SettingsStore, + account: ProviderTokenAccount?) -> ProviderSourceMode + { + let base = + ProviderCatalog.implementation(for: provider)? + .sourceMode(context: ProviderSourceModeContext(provider: provider, settings: settings)) + ?? .auto + let config = settings.configSnapshot.providerConfig(for: provider.instanceID) + return ProviderDescriptorRegistry.descriptor(for: provider).credentials? + .selectedAccountSourceMode(base: base, account: account, config: config) ?? base } @MainActor @@ -123,12 +143,15 @@ struct ProviderRegistry { tokenOverride: tokenOverride, codexActiveSourceOverride: codexActiveSourceOverride) for implementation in ProviderCatalog.all { - let registration = ProviderDescriptorRegistry.descriptor(for: implementation.id).settingsSection + let registration = ProviderDescriptorRegistry.descriptor(for: implementation.id) + .settingsSection guard let contribution = implementation.settingsSnapshot(context: context) else { - preconditionFailure("Missing settings snapshot section for provider '\(implementation.id.rawValue)'") + preconditionFailure( + "Missing settings snapshot section for provider '\(implementation.id.rawValue)'") } guard registration.accepts(contribution) else { - preconditionFailure("Mismatched settings snapshot section for provider '\(implementation.id.rawValue)'") + preconditionFailure( + "Mismatched settings snapshot section for provider '\(implementation.id.rawValue)'") } builder.apply(contribution) } @@ -159,18 +182,26 @@ struct ProviderRegistry { // Provider-specific by design: managed Codex account selection scopes the fetcher's CODEX_HOME. if provider == .codex { let codexActiveSource = codexActiveSourceOverride ?? settings.codexResolvedActiveSource - if let managedHomePath = settings.managedCodexRemoteHomePath(forActiveSource: codexActiveSource) { + if let managedHomePath = settings.managedCodexRemoteHomePath( + forActiveSource: codexActiveSource) + { env = CodexHomeScope.scopedEnvironment(base: env, codexHome: managedHomePath) - } else if let liveHomePath = settings.liveSystemCodexHomePath(forActiveSource: codexActiveSource) { + } else if let liveHomePath = settings.liveSystemCodexHomePath( + forActiveSource: codexActiveSource) + { env = CodexHomeScope.scopedEnvironment(base: env, codexHome: liveHomePath) - } else if let profileHomePath = settings.profileCodexHomePath(forActiveSource: codexActiveSource) { + } else if let profileHomePath = settings.profileCodexHomePath( + forActiveSource: codexActiveSource) + { env = CodexHomeScope.scopedEnvironment(base: env, codexHome: profileHomePath) } } return env } - static func makeFetcher(base: UsageFetcher, provider: UsageProvider, env: [String: String]) -> UsageFetcher { + static func makeFetcher(base: UsageFetcher, provider: UsageProvider, env: [String: String]) + -> UsageFetcher + { // Provider-specific by design: a Codex account scope needs a fetcher rebuilt with its selected CODEX_HOME. guard provider == .codex else { return base } return UsageFetcher(environment: env) diff --git a/Sources/CodexBar/UsageStore+TokenAccounts.swift b/Sources/CodexBar/UsageStore+TokenAccounts.swift index 6323f72aa3..0466b9d352 100644 --- a/Sources/CodexBar/UsageStore+TokenAccounts.swift +++ b/Sources/CodexBar/UsageStore+TokenAccounts.swift @@ -47,9 +47,11 @@ extension UsageStore { self.tokenAccountLiveStateProviders.insert(provider.instanceID) guard let account = self.uniqueTokenAccount(provider: provider, accountID: accountID), let cached = self.accountSnapshots[provider.instanceID]?.first(where: { - $0.account.id == accountID && $0.cacheKey == self.tokenAccountSnapshotCacheKey( - provider: provider, - account: account) + $0.account.id == accountID + && $0.cacheKey + == self.tokenAccountSnapshotCacheKey( + provider: provider, + account: account) }) else { self.accountSnapshots[provider.instanceID]?.removeAll { $0.account.id == accountID } @@ -90,7 +92,9 @@ extension UsageStore { { let support = TokenAccountSupportCatalog.support(for: provider) let cookieSource = self.settings.providerConfig(for: provider)?.cookieSource ?? .auto - guard support?.selectedAccountRequiresManualCookieSource != true || cookieSource != .auto else { return } + guard support?.selectedAccountRequiresManualCookieSource != true || cookieSource != .auto else { + return + } let cached = TokenAccountUsageSnapshot( account: account, snapshot: snapshot, @@ -208,8 +212,7 @@ extension UsageStore { func shouldFetchAllCodexVisibleAccounts() -> Bool { let projection = self.freshCodexVisibleAccountProjectionForAccountRefresh() - return self.settings.multiAccountMenuLayout == .stacked && - projection.visibleAccounts.count > 1 + return self.settings.multiAccountMenuLayout == .stacked && projection.visibleAccounts.count > 1 } func refreshCodexVisibleAccountsForMenu(generation: UInt64? = nil) async { @@ -286,10 +289,11 @@ extension UsageStore { requireLiveManagedAuthFor: managedAccountIDsWithReadableAuthAtStart) guard self.isCurrentProviderRefreshGeneration(.codex, generation: generation) else { return } let currentSnapshots = snapshots.compactMap { snapshot -> CodexAccountUsageSnapshot? in - guard let currentAccount = Self.currentCodexVisibleAccount( - matching: snapshot.account, - projection: currentProjection, - allowProviderAccountAuthFingerprintMismatch: snapshot.error == nil) + guard + let currentAccount = Self.currentCodexVisibleAccount( + matching: snapshot.account, + projection: currentProjection, + allowProviderAccountAuthFingerprintMismatch: snapshot.error == nil) else { return nil } @@ -331,12 +335,13 @@ extension UsageStore { return } - let allowSelectedAuthFingerprintMismatch = switch selectedOutcome.result { - case .success: - true - case .failure: - false - } + let allowSelectedAuthFingerprintMismatch = + switch selectedOutcome.result { + case .success: + true + case .failure: + false + } let currentSelectedAccount = Self.currentCodexVisibleAccount( matching: selectedAccount, projection: currentProjection, @@ -382,10 +387,13 @@ extension UsageStore { originalAccount, account: currentActiveAccount) } - guard let originalAccount, let currentActiveAccount, currentSelectionSource == originalSelectionSource else { + guard let originalAccount, let currentActiveAccount, + currentSelectionSource == originalSelectionSource + else { return false } - return Self.codexVisibleAccountMatchesCurrentProjection(originalAccount, account: currentActiveAccount) + return Self.codexVisibleAccountMatchesCurrentProjection( + originalAccount, account: currentActiveAccount) } private func freshCodexVisibleAccountProjectionForAccountRefresh( @@ -402,9 +410,10 @@ extension UsageStore { } private func codexManagedAccountIDsWithReadableAuth() -> Set { - Set(self.settings.codexAccountReconciliationSnapshot.storedAccounts.compactMap { account in - CodexAuthFingerprint.fingerprint(homePath: account.managedHomePath) == nil ? nil : account.id - }) + Set( + self.settings.codexAccountReconciliationSnapshot.storedAccounts.compactMap { account in + CodexAuthFingerprint.fingerprint(homePath: account.managedHomePath) == nil ? nil : account.id + }) } private nonisolated static func codexVisibleAccountProjectionWithFreshManagedAuthFingerprints( @@ -414,19 +423,22 @@ extension UsageStore { { let managedRuntimeStates = Dictionary( uniqueKeysWithValues: snapshot.storedAccounts.map { account in - let workspaceAccountID: String? = switch snapshot.runtimeIdentity(for: account) { - case let .providerAccount(id): - id - case .emailOnly, .unresolved: - nil - } + let workspaceAccountID: String? = + switch snapshot.runtimeIdentity(for: account) { + case let .providerAccount(id): + id + case .emailOnly, .unresolved: + nil + } let authFingerprint = CodexAuthFingerprint.fingerprint(homePath: account.managedHomePath) let requiresLiveAuth = accountIDs.contains(account.id) - return (account.id, CodexManagedVisibleAccountRuntimeState( - authFingerprint: authFingerprint ?? (requiresLiveAuth ? nil : account.authFingerprint), - workspaceAccountID: authFingerprint == nil && requiresLiveAuth - ? nil - : (workspaceAccountID ?? account.workspaceAccountID))) + return ( + account.id, + CodexManagedVisibleAccountRuntimeState( + authFingerprint: authFingerprint ?? (requiresLiveAuth ? nil : account.authFingerprint), + workspaceAccountID: authFingerprint == nil && requiresLiveAuth + ? nil + : (workspaceAccountID ?? account.workspaceAccountID))) }) let visibleAccounts = projection.visibleAccounts.map { account in guard case let .managedAccount(id) = account.selectionSource else { return account } @@ -435,8 +447,8 @@ extension UsageStore { let runtimeWorkspaceAccountID = managedRuntimeStates[id]?.workspaceAccountID .map(CodexOpenAIWorkspaceIdentity.normalizeWorkspaceAccountID) guard let runtimeState = managedRuntimeStates[id], - runtimeState.authFingerprint != account.authFingerprint || - runtimeWorkspaceAccountID != accountWorkspaceAccountID + runtimeState.authFingerprint != account.authFingerprint + || runtimeWorkspaceAccountID != accountWorkspaceAccountID else { return account } @@ -488,11 +500,12 @@ extension UsageStore { // Provider-specific by design: Codex managed profiles relabel fetched identity from reconciled workspace data. guard let snapshot else { return nil } let existing = snapshot.identity(for: .codex) - return snapshot.withIdentity(ProviderIdentitySnapshot( - providerID: .codex, - accountEmail: account.email, - accountOrganization: existing?.accountOrganization, - loginMethod: existing?.loginMethod ?? account.workspaceLabel)) + return snapshot.withIdentity( + ProviderIdentitySnapshot( + providerID: .codex, + accountEmail: account.email, + accountOrganization: existing?.accountOrganization, + loginMethod: existing?.loginMethod ?? account.workspaceLabel)) } private static func codexVisibleAccountMatchesCurrentProjection( @@ -516,7 +529,9 @@ extension UsageStore { if priorWorkspaceID != nil || accountWorkspaceID != nil { guard priorWorkspaceID == accountWorkspaceID else { return false } if !allowProviderAccountAuthFingerprintMismatch { - guard self.codexVisibleAccountAuthFingerprintMatches(prior, account: account) else { return false } + guard self.codexVisibleAccountAuthFingerprintMatches(prior, account: account) else { + return false + } } return true } @@ -579,7 +594,8 @@ extension UsageStore { self.activateCachedTokenAccountSnapshot(provider: provider, accountID: effectiveSelected.id) return self.accountSnapshots[provider.instanceID] ?? [] } - let priorByAccountID = Dictionary(uniqueKeysWithValues: priorSnapshots.map { ($0.account.id, $0) }) + let priorByAccountID = Dictionary( + uniqueKeysWithValues: priorSnapshots.map { ($0.account.id, $0) }) var snapshots: [TokenAccountUsageSnapshot] = [] var historySamples: [(account: ProviderTokenAccount, snapshot: UsageSnapshot)] = [] @@ -621,8 +637,8 @@ extension UsageStore { // If every fetch was cancelled (e.g. the user closed/reopened the menu mid-flight) // and we have no usable snapshots, leave the prior per-account state alone. // Wiping it would produce a menu of useless "cancelled" placeholders. - let shouldPreservePriorState = !sawAnyNonCancellationOutcome && - snapshots.allSatisfy { $0.snapshot == nil } + let shouldPreservePriorState = + !sawAnyNonCancellationOutcome && snapshots.allSatisfy { $0.snapshot == nil } if !shouldPreservePriorState { await MainActor.run { self.accountSnapshots[provider.instanceID] = snapshots @@ -662,8 +678,9 @@ extension UsageStore { { // Provider-specific by design: Codex account refresh rejects successful payloads for a different email owner. guard case let .success(result) = outcome.result else { return true } - guard let resultEmail = CodexIdentityResolver.normalizeEmail( - result.usage.scoped(to: .codex).accountEmail(for: .codex)) + guard + let resultEmail = CodexIdentityResolver.normalizeEmail( + result.usage.scoped(to: .codex).accountEmail(for: .codex)) else { return true } @@ -680,9 +697,8 @@ extension UsageStore { let message = error.localizedDescription .trimmingCharacters(in: .whitespacesAndNewlines) .lowercased() - return message == "cancelled" || - message.contains("cancellationerror") || - message.contains("cancelled") + return message == "cancelled" || message.contains("cancellationerror") + || message.contains("cancelled") } func limitedTokenAccounts( @@ -730,8 +746,10 @@ extension UsageStore { override: TokenAccountOverride?, codexActiveSourceOverride: CodexActiveSource? = nil) async -> ProviderFetchOutcome { - let descriptor = self.providerSpecs[provider]?.descriptor ?? ProviderDescriptorRegistry - .descriptor(for: provider) + let descriptor = + self.providerSpecs[provider]?.descriptor + ?? ProviderDescriptorRegistry + .descriptor(for: provider) let context = self.makeFetchContext( provider: provider, override: override, @@ -748,20 +766,25 @@ extension UsageStore { provider: UsageProvider, accounts: [ProviderTokenAccount]) async -> [TokenAccountFetchResult] { - let requests: [( - index: Int, - account: ProviderTokenAccount, - descriptor: ProviderDescriptor, - context: ProviderFetchContext)] = + let requests: + [( + index: Int, + account: ProviderTokenAccount, + descriptor: ProviderDescriptor, + context: ProviderFetchContext)] = accounts.enumerated().map { index, account in let override = TokenAccountOverride(provider: provider, account: account) - let descriptor = self.providerSpecs[provider]?.descriptor ?? ProviderDescriptorRegistry - .descriptor(for: provider) + let descriptor = + self.providerSpecs[provider]?.descriptor + ?? ProviderDescriptorRegistry + .descriptor(for: provider) let context = self.makeFetchContext(provider: provider, override: override) return (index, account, descriptor, context) } - if let delay = TokenAccountSupportCatalog.support(for: provider)?.minimumDelayBetweenAccountRefreshes { + if let delay = TokenAccountSupportCatalog.support(for: provider)? + .minimumDelayBetweenAccountRefreshes + { var results: [TokenAccountFetchResult] = [] results.reserveCapacity(requests.count) for request in requests { @@ -770,21 +793,23 @@ extension UsageStore { try await Task.sleep(for: delay) } catch { for pending in requests.dropFirst(results.count) { - results.append(TokenAccountFetchResult( - index: pending.index, - account: pending.account, - outcome: ProviderFetchOutcome( - result: .failure(CancellationError()), - attempts: []))) + results.append( + TokenAccountFetchResult( + index: pending.index, + account: pending.account, + outcome: ProviderFetchOutcome( + result: .failure(CancellationError()), + attempts: []))) } return results } } let outcome = await request.descriptor.fetchOutcome(context: request.context) - results.append(TokenAccountFetchResult( - index: request.index, - account: request.account, - outcome: outcome)) + results.append( + TokenAccountFetchResult( + index: request.index, + account: request.account, + outcome: outcome)) } return results } @@ -817,10 +842,13 @@ extension UsageStore { allVisibleAccounts: [CodexVisibleAccount], priorSnapshots: [CodexAccountUsageSnapshot], activeVisibleAccountID: String?) async - -> [CodexAccountFetchResult] { + -> [CodexAccountFetchResult] + { let requests: [CodexAccountFetchRequest] = accounts.enumerated().map { index, account in - let descriptor = self.providerSpecs[.codex]?.descriptor ?? ProviderDescriptorRegistry - .descriptor(for: .codex) + let descriptor = + self.providerSpecs[.codex]?.descriptor + ?? ProviderDescriptorRegistry + .descriptor(for: .codex) let context = self.makeFetchContext( provider: .codex, override: nil, @@ -831,13 +859,15 @@ extension UsageStore { let priorSnapshot = Self.codexPriorAccountSnapshot( matching: account, in: priorSnapshots) - let trustedBackfillSnapshots = limitResetOwnerKey == nil - ? [] - : self.codexResetBackfillSnapshots( - for: account, - priorSnapshot: priorSnapshot, - activeVisibleAccountID: activeVisibleAccountID) - let missingWindowBackfillSnapshot = Self.codexMergedResetBackfillSnapshot(trustedBackfillSnapshots) + let trustedBackfillSnapshots = + limitResetOwnerKey == nil + ? [] + : self.codexResetBackfillSnapshots( + for: account, + priorSnapshot: priorSnapshot, + activeVisibleAccountID: activeVisibleAccountID) + let missingWindowBackfillSnapshot = Self.codexMergedResetBackfillSnapshot( + trustedBackfillSnapshots) return CodexAccountFetchRequest( index: index, account: account, @@ -863,24 +893,25 @@ extension UsageStore { fetcher: request.resetCreditsFetcher) } let initialOutcome = await fetchOutcome() - let outcome: ProviderFetchOutcome? = if Self.codexUsageOutcomeMatchesVisibleAccount( - initialOutcome, - account: request.account) - { - if let admitted = await Self.codexOutcomeAdmittedForPublication( - initialOutcome: initialOutcome, - previousSnapshot: request.previousSnapshot, - missingWindowBackfillSnapshot: request.missingWindowBackfillSnapshot, - fetchConfirmation: fetchOutcome), - Self.codexUsageOutcomeMatchesVisibleAccount(admitted, account: request.account) + let outcome: ProviderFetchOutcome? = + if Self.codexUsageOutcomeMatchesVisibleAccount( + initialOutcome, + account: request.account) { - admitted + if let admitted = await Self.codexOutcomeAdmittedForPublication( + initialOutcome: initialOutcome, + previousSnapshot: request.previousSnapshot, + missingWindowBackfillSnapshot: request.missingWindowBackfillSnapshot, + fetchConfirmation: fetchOutcome), + Self.codexUsageOutcomeMatchesVisibleAccount(admitted, account: request.account) + { + admitted + } else { + nil + } } else { nil } - } else { - nil - } return CodexAccountFetchResult( index: request.index, account: request.account, @@ -909,7 +940,10 @@ extension UsageStore { provider: provider, settings: self.settings, override: override) - let sourceMode = self.sourceMode(for: provider) + let sourceMode = ProviderRegistry.resolvedSourceMode( + provider: provider, + settings: self.settings, + account: account) let snapshot = ProviderRegistry.makeSettingsSnapshot( settings: self.settings, tokenOverride: override, @@ -922,7 +956,8 @@ extension UsageStore { codexActiveSourceOverride: codexActiveSourceOverride) let fetcher = ProviderRegistry.makeFetcher(base: self.codexFetcher, provider: provider, env: env) let contextProvider = provider - let publicationGeneration = self.providerRefreshPublicationContexts[provider.instanceID]?.generation + let publicationGeneration = self.providerRefreshPublicationContexts[provider.instanceID]? + .generation let contextConfigRevision = self.settings.providerConfigRevision(for: provider) let originalAccountToken = account?.token let originalManualToken = provider == .stepfun ? self.settings.stepfunToken : nil @@ -951,10 +986,11 @@ extension UsageStore { else { return } - guard self.providerConfigMutationIsCurrent( - provider: provider, - generation: publicationGeneration, - originalConfigRevision: contextConfigRevision) + guard + self.providerConfigMutationIsCurrent( + provider: provider, + generation: publicationGeneration, + originalConfigRevision: contextConfigRevision) else { return } self.settings.updateTokenAccount( provider: provider, @@ -970,10 +1006,11 @@ extension UsageStore { guard let self, provider == .stepfun, self.settings.stepfunToken == originalManualToken else { return } - guard self.providerConfigMutationIsCurrent( - provider: provider, - generation: publicationGeneration, - originalConfigRevision: contextConfigRevision) + guard + self.providerConfigMutationIsCurrent( + provider: provider, + generation: publicationGeneration, + originalConfigRevision: contextConfigRevision) else { return } self.settings.stepfunToken = token self.advanceProviderRefreshConfigRevision( @@ -995,7 +1032,9 @@ extension UsageStore { { guard let generation else { return true } let currentConfigRevision = self.settings.providerConfigRevision(for: provider) - guard let publication = self.providerRefreshPublicationContexts[provider.instanceID] else { return false } + guard let publication = self.providerRefreshPublicationContexts[provider.instanceID] else { + return false + } if publication.generation == generation { return publication.configRevision == currentConfigRevision } @@ -1076,7 +1115,9 @@ extension UsageStore { return snapshot } - func codexLastKnownResetSnapshot(matching guardValue: CodexAccountScopedRefreshGuard?) -> UsageSnapshot? { + func codexLastKnownResetSnapshot(matching guardValue: CodexAccountScopedRefreshGuard?) + -> UsageSnapshot? + { guard let guardValue, let lastGuard = self.lastCodexUsagePublicationGuard, Self.codexScopedRefreshGuardAllowsResetBackfill(lastGuard, matching: guardValue) @@ -1192,9 +1233,12 @@ extension UsageStore { authFingerprint: account.authFingerprint) } - private nonisolated static func codexVisibleAccountIdentity(for account: CodexVisibleAccount) -> CodexIdentity { + private nonisolated static func codexVisibleAccountIdentity(for account: CodexVisibleAccount) + -> CodexIdentity + { if let workspaceAccountID = self.normalizedCodexVisibleAccountText(account.workspaceAccountID) { - return .providerAccount(id: CodexOpenAIWorkspaceIdentity.normalizeWorkspaceAccountID(workspaceAccountID)) + return .providerAccount( + id: CodexOpenAIWorkspaceIdentity.normalizeWorkspaceAccountID(workspaceAccountID)) } return CodexIdentityResolver.resolve(accountId: nil, email: account.email) } @@ -1260,7 +1304,8 @@ extension UsageStore { ClaudeUsageError.isClaudeOAuthUsageRateLimit(error), let priorSnapshot, priorSnapshot.sourceLabel == "oauth", - priorSnapshot.cacheKey == self.tokenAccountSnapshotCacheKey(provider: provider, account: account), + priorSnapshot.cacheKey + == self.tokenAccountSnapshotCacheKey(provider: provider, account: account), let priorUsage = priorSnapshot.snapshot { let snapshot = TokenAccountUsageSnapshot( @@ -1299,7 +1344,8 @@ extension UsageStore { sourceLabel: priorSnapshot?.sourceLabel) } let labeled = self.applyCodexVisibleAccountLabel(scoped, account: account) - let backfilled = Self.codexMergedResetBackfillSnapshot(resetBackfillSnapshots) + let backfilled = + Self.codexMergedResetBackfillSnapshot(resetBackfillSnapshots) .map { Self.codexBackfillingResetWindows(labeled, from: $0) } ?? labeled let snapshot = CodexAccountUsageSnapshot( account: account, @@ -1347,17 +1393,12 @@ extension UsageStore { private static func shouldPreserveCodexAccountSnapshotOnFailure(_ message: String) -> Bool { guard CodexAccountHealth.status(forError: message) == .unavailable else { return false } let normalized = message.lowercased() - return normalized.contains("network") || - normalized.contains("internet connection") || - normalized.contains("offline") || - normalized.contains("timed out") || - normalized.contains("timeout") || - normalized.contains("connection was lost") || - normalized.contains("could not connect") || - normalized.contains("not connected") || - normalized.contains("hostname") || - normalized.contains("dns") || - normalized.contains("temporarily unavailable") + return normalized.contains("network") || normalized.contains("internet connection") + || normalized.contains("offline") || normalized.contains("timed out") + || normalized.contains("timeout") || normalized.contains("connection was lost") + || normalized.contains("could not connect") || normalized.contains("not connected") + || normalized.contains("hostname") || normalized.contains("dns") + || normalized.contains("temporarily unavailable") } func applySelectedCodexVisibleAccountOutcome( @@ -1440,19 +1481,21 @@ extension UsageStore { switch outcome.result { case let .success(result): let scoped = result.usage.scoped(to: provider) - let labeled: UsageSnapshot = if let account { - self.applyAccountLabel(scoped, provider: provider, account: account) - } else { - scoped - } + let labeled: UsageSnapshot = + if let account { + self.applyAccountLabel(scoped, provider: provider, account: account) + } else { + scoped + } let backfilled = await MainActor.run { guard self.isCurrentProviderRefreshGeneration(provider, generation: generation) else { return nil as UsageSnapshot? } - let profileStable = provider == .deepseek - ? labeled.preservingDeepSeekPlatformProfiles( - from: self.presentationSnapshot(for: .deepseek)) - : labeled + let profileStable = + provider == .deepseek + ? labeled.preservingDeepSeekPlatformProfiles( + from: self.presentationSnapshot(for: .deepseek)) + : labeled let backfilled = profileStable.backfillingResetTimes( from: self.lastKnownResetSnapshots[provider.instanceID]) let warningAccountDiscriminator = Self.warningTokenAccountDiscriminator(account) @@ -1492,7 +1535,8 @@ extension UsageStore { let fallbackAccountSnapshot, fallbackAccountSnapshot.account.id == currentAccount.id, fallbackAccountSnapshot.sourceLabel == "oauth", - fallbackAccountSnapshot.cacheKey == self.tokenAccountSnapshotCacheKey( + fallbackAccountSnapshot.cacheKey + == self.tokenAccountSnapshotCacheKey( provider: provider, account: currentAccount), let fallback = fallbackAccountSnapshot.snapshot @@ -1518,8 +1562,9 @@ extension UsageStore { return } let hadPriorData = self.snapshots[provider.instanceID] != nil || fallbackSnapshot != nil - let shouldSurface = self.failureGates[provider.instanceID]? - .shouldSurfaceError(onFailureWithPriorData: hadPriorData) ?? true + let shouldSurface = + self.failureGates[provider.instanceID]? + .shouldSurfaceError(onFailureWithPriorData: hadPriorData) ?? true if shouldSurface { self.errors[provider.instanceID] = message self.snapshots.removeValue(forKey: provider.instanceID) @@ -1537,8 +1582,9 @@ extension UsageStore { -> (accounts: [ProviderTokenAccount], removesAccountAuthority: Bool) { let accounts = self.tokenAccounts(for: provider) - let removesAccountAuthority = self.tokenAccountLiveStateProviders.contains(provider.instanceID) && - self.settings.effectiveSelectedTokenAccount(for: provider) == nil + let removesAccountAuthority = + self.tokenAccountLiveStateProviders.contains(provider.instanceID) + && self.settings.effectiveSelectedTokenAccount(for: provider) == nil return (accounts, removesAccountAuthority) } } diff --git a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift index 037158f1b6..a03950340e 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift @@ -2,570 +2,578 @@ import Foundation import SweetCookieKit public enum GrokProviderDescriptor { - public static let descriptor: ProviderDescriptor = Self.makeDescriptor() - private static let credentials = ProviderCredentialAdapter( - tokenAccountSupport: TokenAccountSupport( - title: "SuperGrok tokens", - subtitle: - "Paste a SuperGrok bearer or grok.com cookie. Open token file opens ~/.grok/auth.json.", - placeholder: "Bearer … or Cookie: …", - injection: .environment(key: GrokSettingsReader.oauthTokenEnvironmentKey), - requiresManualCookieSource: false, - cookieName: nil, - environmentKeysToScrub: [GrokSettingsReader.oauthTokenEnvironmentKey], - environmentOverride: { token in - guard let oauth = GrokCredentialRouting.normalizedOAuthToken(token) else { return nil } - return [GrokSettingsReader.oauthTokenEnvironmentKey: oauth] - }), - selectedAccountSourceModeResolver: { base, account, _ in - guard let account else { return base } - return GrokCredentialRouting.resolve( - tokenAccountToken: account.token, - manualCookieHeader: nil).sourceMode ?? base - }) - - /// Grok is normally signed in through Chrome; avoid touching unrelated browser keychains. - private static var browserCookieOrder: BrowserCookieImportOrder? { - #if os(macOS) - [.chrome] - #else - nil - #endif - } - - static func makeDescriptor() -> ProviderDescriptor { - ProviderDescriptor( - id: .grok, - settingsSection: .init( - GrokProviderSettingsKey.self, - cookieSettings: { settings in - CookieProviderSettings( - cookieSource: settings.cookieSource, - manualCookieHeader: settings.manualCookieHeader) - }, - credentialSettings: { context in - let cookies = context.cookieSettings(for: .grok) - let routing = GrokCredentialRouting.resolve( - tokenAccountToken: context.account?.token, - manualCookieHeader: context.account == nil ? cookies.manualCookieHeader : nil) - return GrokProviderSettings( - cookieSource: routing.manualCookieHeader != nil ? .manual : cookies.cookieSource, - manualCookieHeader: routing.manualCookieHeader ?? cookies.manualCookieHeader) - }), - credentials: self.credentials, - metadata: ProviderMetadata( - id: .grok, - displayName: "Grok", - sessionLabel: "Credits", - weeklyLabel: "On-demand", - opusLabel: nil, - supportsOpus: false, - supportsCredits: false, - creditsHint: "", - toggleTitle: "Show Grok usage", - cliName: "grok", - defaultEnabled: false, - widgetSelectable: false, - isPrimaryProvider: false, - usesAccountFallback: false, - debugLogUnavailableMessage: "Grok debug log not yet implemented", - browserCookieOrder: self.browserCookieOrder, - dashboardURL: "https://grok.com/?_s=usage", - changelogURL: "https://x.ai/news", - statusPageURL: nil, - statusLinkURL: "https://status.x.ai"), - branding: ProviderBranding( - iconStyle: .init(provider: .grok), - iconResourceName: "ProviderIcon-grok", - color: ProviderColor(red: 16 / 255, green: 163 / 255, blue: 127 / 255), - confettiPalette: [ - ProviderColor(hex: 0x000000), - ProviderColor(hex: 0x868686), - ProviderColor(hex: 0xFDFDFD), - ]), - tokenCost: ProviderTokenCostConfig( - supportsTokenCost: false, - noDataMessage: { "Grok cost summary is not supported yet." }), - pace: ProviderPaceCapability( - resetWindowPace: .custom { window, now in - guard Self.primaryLabel(window: window, now: now) == "Weekly", - let resetsAt = window.resetsAt - else { return false } - let windowMinutes = window.windowMinutes ?? 7 * 24 * 60 - let timeUntilReset = resetsAt.timeIntervalSince(now) - return windowMinutes > 0 - && timeUntilReset > 0 - && timeUntilReset <= TimeInterval(windowMinutes) * 60 - }), - presentation: ProviderUsagePresentation(rateWindowLabeler: { metadata, snapshot, now in - ProviderRateWindowLabels( - primary: Self.displayLabel(window: snapshot.primary, now: now) ?? metadata.sessionLabel, - secondary: metadata.weeklyLabel, - tertiary: metadata.opusLabel ?? "Sonnet", - showsTertiary: metadata.supportsOpus) - }), - fetchPlan: ProviderFetchPlan( - sourceModes: [.auto, .cli, .oauth, .web], - pipeline: ProviderFetchPipeline(resolveStrategies: self.resolveStrategies)), - cli: ProviderCLIConfig( - name: "grok", - versionDetector: { _ in GrokStatusProbe.detectVersion() }, - browserSupportExemption: { _, _, _ in true })) - } - - private static func resolveStrategies(context: ProviderFetchContext) async - -> [any ProviderFetchStrategy] - { - switch context.sourceMode { - case .auto: - [GrokCLIFetchStrategy(), GrokOAuthFetchStrategy(), GrokWebFetchStrategy()] - case .cli: - [GrokCLIFetchStrategy()] - case .oauth: - [GrokOAuthFetchStrategy()] - case .web: - [GrokWebFetchStrategy()] - case .api: - [] - } - } - - /// Returns a contextual label for Grok's primary usage bar ("Weekly" or "Monthly"). - /// Prefer the billing period duration when available; fall back to reset distance for - /// web billing payloads that expose only a reset timestamp. - public static func primaryLabel(window: RateWindow?, now: Date = .now) -> String? { - if let minutes = window?.windowMinutes { - return self.primaryLabel(duration: TimeInterval(minutes) * 60) - } - return self.primaryLabel(resetsAt: window?.resetsAt, now: now) - } - - public static func primaryLabel(resetsAt: Date?, now: Date = .now) -> String? { - guard let resetsAt else { return nil } - return self.primaryLabel(duration: resetsAt.timeIntervalSince(now)) - } - - /// Grok's current untyped credits surface is the weekly credit pool (#2929). Keep explicit - /// durations authoritative, but do not lose the weekly label near the end of an untyped window. - public static func displayLabel(window: RateWindow?, now: Date = .now) -> String? { - guard let window else { return nil } - if let label = self.primaryLabel(window: window, now: now) { - return label - } - guard window.windowMinutes == nil, window.resetsAt != nil else { return nil } - return "Weekly" - } - - private static func primaryLabel(duration seconds: TimeInterval) -> String? { - guard seconds > 3600 else { return nil } - let days = Int((seconds / 86400).rounded(.toNearestOrAwayFromZero)) - if (4...12).contains(days) { - return "Weekly" - } - if (20...45).contains(days) { - return "Monthly" - } - return nil - } + public static let descriptor: ProviderDescriptor = Self.makeDescriptor() + private static let credentials = ProviderCredentialAdapter( + tokenAccountSupport: TokenAccountSupport( + title: "SuperGrok tokens", + subtitle: + "Paste a SuperGrok bearer or grok.com cookie. Open token file opens ~/.grok/auth.json.", + placeholder: "Bearer … or Cookie: …", + injection: .environment(key: GrokSettingsReader.oauthTokenEnvironmentKey), + requiresManualCookieSource: false, + cookieName: nil, + environmentKeysToScrub: [GrokSettingsReader.oauthTokenEnvironmentKey], + environmentOverride: { token in + guard let oauth = GrokCredentialRouting.normalizedOAuthToken(token) else { return nil } + return [GrokSettingsReader.oauthTokenEnvironmentKey: oauth] + }), + selectedAccountSourceModeResolver: { base, account, _ in + guard let account else { return base } + return GrokCredentialRouting.resolve( + tokenAccountToken: account.token, + manualCookieHeader: nil + ).sourceMode ?? base + }) + + /// Grok is normally signed in through Chrome; avoid touching unrelated browser keychains. + private static var browserCookieOrder: BrowserCookieImportOrder? { + #if os(macOS) + [.chrome] + #else + nil + #endif + } + + static func makeDescriptor() -> ProviderDescriptor { + ProviderDescriptor( + id: .grok, + settingsSection: .init( + GrokProviderSettingsKey.self, + cookieSettings: { settings in + CookieProviderSettings( + cookieSource: settings.cookieSource, + manualCookieHeader: settings.manualCookieHeader) + }, + credentialSettings: { context in + let cookies = context.cookieSettings(for: .grok) + let routing = GrokCredentialRouting.resolve( + tokenAccountToken: context.account?.token, + manualCookieHeader: context.account == nil ? cookies.manualCookieHeader : nil) + return GrokProviderSettings( + cookieSource: routing.manualCookieHeader != nil ? .manual : cookies.cookieSource, + manualCookieHeader: routing.manualCookieHeader ?? cookies.manualCookieHeader) + }), + credentials: self.credentials, + metadata: ProviderMetadata( + id: .grok, + displayName: "Grok", + sessionLabel: "Credits", + weeklyLabel: "On-demand", + opusLabel: nil, + supportsOpus: false, + supportsCredits: false, + creditsHint: "", + toggleTitle: "Show Grok usage", + cliName: "grok", + defaultEnabled: false, + widgetSelectable: false, + isPrimaryProvider: false, + usesAccountFallback: false, + debugLogUnavailableMessage: "Grok debug log not yet implemented", + browserCookieOrder: self.browserCookieOrder, + dashboardURL: "https://grok.com/?_s=usage", + changelogURL: "https://x.ai/news", + statusPageURL: nil, + statusLinkURL: "https://status.x.ai"), + branding: ProviderBranding( + iconStyle: .init(provider: .grok), + iconResourceName: "ProviderIcon-grok", + color: ProviderColor(red: 16 / 255, green: 163 / 255, blue: 127 / 255), + confettiPalette: [ + ProviderColor(hex: 0x000000), + ProviderColor(hex: 0x868686), + ProviderColor(hex: 0xFDFDFD), + ]), + tokenCost: ProviderTokenCostConfig( + supportsTokenCost: false, + noDataMessage: { "Grok cost summary is not supported yet." }), + pace: ProviderPaceCapability( + resetWindowPace: .custom { window, now in + guard Self.primaryLabel(window: window, now: now) == "Weekly", + let resetsAt = window.resetsAt + else { return false } + let windowMinutes = window.windowMinutes ?? 7 * 24 * 60 + let timeUntilReset = resetsAt.timeIntervalSince(now) + return windowMinutes > 0 + && timeUntilReset > 0 + && timeUntilReset <= TimeInterval(windowMinutes) * 60 + }), + presentation: ProviderUsagePresentation(rateWindowLabeler: { metadata, snapshot, now in + ProviderRateWindowLabels( + primary: Self.displayLabel(window: snapshot.primary, now: now) ?? metadata.sessionLabel, + secondary: metadata.weeklyLabel, + tertiary: metadata.opusLabel ?? "Sonnet", + showsTertiary: metadata.supportsOpus) + }), + fetchPlan: ProviderFetchPlan( + sourceModes: [.auto, .cli, .oauth, .web], + pipeline: ProviderFetchPipeline(resolveStrategies: self.resolveStrategies)), + cli: ProviderCLIConfig( + name: "grok", + versionDetector: { _ in GrokStatusProbe.detectVersion() }, + browserSupportExemption: { _, _, _ in true })) + } + + private static func resolveStrategies(context: ProviderFetchContext) async + -> [any ProviderFetchStrategy] + { + switch context.sourceMode { + case .auto: + [GrokCLIFetchStrategy(), GrokOAuthFetchStrategy(), GrokWebFetchStrategy()] + case .cli: + [GrokCLIFetchStrategy()] + case .oauth: + [GrokOAuthFetchStrategy()] + case .web: + [GrokWebFetchStrategy()] + case .api: + [] + } + } + + /// Returns a contextual label for Grok's primary usage bar ("Weekly" or "Monthly"). + /// Prefer the billing period duration when available; fall back to reset distance for + /// web billing payloads that expose only a reset timestamp. + public static func primaryLabel(window: RateWindow?, now: Date = .now) -> String? { + if let minutes = window?.windowMinutes { + return self.primaryLabel(duration: TimeInterval(minutes) * 60) + } + return self.primaryLabel(resetsAt: window?.resetsAt, now: now) + } + + public static func primaryLabel(resetsAt: Date?, now: Date = .now) -> String? { + guard let resetsAt else { return nil } + return self.primaryLabel(duration: resetsAt.timeIntervalSince(now)) + } + + /// Grok's current untyped credits surface is the weekly credit pool (#2929). Keep explicit + /// durations authoritative, but do not lose the weekly label near the end of an untyped window. + public static func displayLabel(window: RateWindow?, now: Date = .now) -> String? { + guard let window else { return nil } + if let label = self.primaryLabel(window: window, now: now) { + return label + } + guard window.windowMinutes == nil, window.resetsAt != nil else { return nil } + return "Weekly" + } + + private static func primaryLabel(duration seconds: TimeInterval) -> String? { + guard seconds > 3600 else { return nil } + let days = Int((seconds / 86400).rounded(.toNearestOrAwayFromZero)) + if (4...12).contains(days) { + return "Weekly" + } + if (20...45).contains(days) { + return "Monthly" + } + return nil + } } struct GrokCLIFetchStrategy: ProviderFetchStrategy { - let id: String = "grok.cli" - let kind: ProviderFetchKind = .cli - - func isAvailable(_ context: ProviderFetchContext) async -> Bool { - BinaryLocator.resolveGrokBinary(env: context.env) != nil - } - - func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { - let probe = GrokStatusProbe() - let snap = try await probe.fetch(env: context.env) - return self.makeResult( - usage: snap.toUsageSnapshot(), - sourceLabel: "grok-cli", - diagnostic: snap.diagnostic) - } - - func shouldFallback(on _: Error, context: ProviderFetchContext) -> Bool { - context.sourceMode == .auto - } + let id: String = "grok.cli" + let kind: ProviderFetchKind = .cli + + func isAvailable(_ context: ProviderFetchContext) async -> Bool { + BinaryLocator.resolveGrokBinary(env: context.env) != nil + } + + func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { + let probe = GrokStatusProbe() + let snap = try await probe.fetch(env: context.env) + return self.makeResult( + usage: snap.toUsageSnapshot(), + sourceLabel: "grok-cli", + diagnostic: snap.diagnostic) + } + + func shouldFallback(on _: Error, context: ProviderFetchContext) -> Bool { + context.sourceMode == .auto + } } struct GrokOAuthFetchStrategy: ProviderFetchStrategy { - let id: String = "grok.oauth" - let kind: ProviderFetchKind = .oauth - - func isAvailable(_ context: ProviderFetchContext) async -> Bool { - GrokSettingsReader.resolvedCredentials(environment: context.env) != nil - || FileManager.default.fileExists( - atPath: GrokCredentialsStore.authFileURL(env: context.env).path) - } - - func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { - try await GrokWebFetchStrategy().fetch(context) { - let credentials = try GrokWebFetchStrategy.resolvedCredentialsResult(context: context).get() - guard !credentials.isExpired else { - throw GrokWebBillingError.missingCredentials - } - do { - let snapshot = try await GrokCreditsProxyFetcher.fetch(credentials: credentials) - return (snapshot, "grok-cli-proxy", true) - } catch is CancellationError { - throw CancellationError() - } catch let error as URLError where error.code == .cancelled { - throw error - } catch { - let snapshot = try await GrokWebBillingFetcher.fetch(credentials: credentials) - return (snapshot, "grok-web", true) - } - } - } - - func shouldFallback(on _: Error, context: ProviderFetchContext) -> Bool { - context.sourceMode == .auto - } + let id: String = "grok.oauth" + let kind: ProviderFetchKind = .oauth + + func isAvailable(_ context: ProviderFetchContext) async -> Bool { + GrokSettingsReader.resolvedCredentials(environment: context.env) != nil + || FileManager.default.fileExists( + atPath: GrokCredentialsStore.authFileURL(env: context.env).path) + } + + func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { + try await GrokWebFetchStrategy().fetch(context) { + let credentials = try GrokWebFetchStrategy.resolvedCredentialsResult(context: context).get() + guard !credentials.isExpired else { + throw GrokWebBillingError.missingCredentials + } + do { + let snapshot = try await GrokCreditsProxyFetcher.fetch(credentials: credentials) + return (snapshot, "grok-cli-proxy", true) + } catch is CancellationError { + throw CancellationError() + } catch let error as URLError where error.code == .cancelled { + throw error + } catch { + let snapshot = try await GrokWebBillingFetcher.fetch(credentials: credentials) + return (snapshot, "grok-web", true) + } + } + } + + func shouldFallback(on _: Error, context: ProviderFetchContext) -> Bool { + context.sourceMode == .auto + } } struct GrokWebFetchStrategy: ProviderFetchStrategy { - let id: String = "grok.web" - let kind: ProviderFetchKind = .web - typealias ProxyBillingFetch = @Sendable (GrokCredentials) async throws -> GrokWebBillingSnapshot - typealias WebBillingFetch = - @Sendable () async throws -> ( - snapshot: GrokWebBillingSnapshot, - sourceLabel: String, - authenticatedByAuthFile: Bool) - typealias SettingsTierFetch = @Sendable (GrokCredentials?) async throws -> String? - - /// Browser-cookie import must stay limited to surfaces where a person explicitly asked for it: - /// the menu-bar app runtime, a `userInitiated` interaction (set only by explicit refresh - /// commands and app UI gestures), or the environment override. Scheduled and background work - /// must keep the default `.background` context so it can never reach Chromium Keychain prompts. - static func canImportBrowserCookies(runtime: ProviderRuntime, env: [String: String]) -> Bool { - runtime == .app || ProviderInteractionContext.current == .userInitiated - || env["CODEXBAR_ALLOW_BROWSER_COOKIE_IMPORT"] == "1" - } - - func isAvailable(_ context: ProviderFetchContext) async -> Bool { - let cookieSource = context.settings?.grok?.cookieSource ?? .auto - #if os(macOS) - if cookieSource != .off, CookieHeaderCache.load(provider: .grok) != nil { - return true - } - if cookieSource != .off, - let header = context.settings?.grok?.manualCookieHeader, - !header.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty - { - return true - } - if cookieSource == .auto, - Self.canImportBrowserCookies(runtime: context.runtime, env: context.env), - GrokCookieImporter.hasSession(browserDetection: context.browserDetection) - { - return true - } - #endif - return false - } - - func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { - try await self.fetch( - context, - webBilling: { [self] in - try await self.fetchWebBilling(context: context) - }) - } - - func fetch( - _ context: ProviderFetchContext, - webBilling fetchWebBilling: @escaping WebBillingFetch, - settingsTier loadSettingsTier: SettingsTierFetch? = nil) async throws -> ProviderFetchResult - { - let authCredentials = GrokSettingsReader.resolvedCredentials(environment: context.env).flatMap { - credentials in - credentials.isExpired ? nil : credentials - } - let resolveSettingsTier = - loadSettingsTier ?? { credentials in - try await GrokStatusProbe.loadSettingsTier(credentials: credentials) - } - - let webBilling: GrokWebBillingSnapshot - let sourceLabel: String - let authenticatedByAuthFile: Bool - do { - (webBilling, sourceLabel, authenticatedByAuthFile) = try await fetchWebBilling() - } catch GrokWebBillingError.teamUsageUnsupported { - guard let authState = authCredentials, authState.isTeamPrincipal else { - throw GrokWebBillingError.teamUsageUnsupported - } - let subscriptionTier = try await resolveSettingsTier(authState) - let identitySnapshot = GrokStatusProbe.identityOnlySnapshot( - credentials: authState, - localSummary: GrokLocalSessionScanner.summarize(env: context.env), - cliVersion: GrokStatusProbe.detectVersion(env: context.env), - subscriptionTier: subscriptionTier) - return self.makeResult( - usage: identitySnapshot.toUsageSnapshot(), - sourceLabel: "grok-web", - diagnostic: identitySnapshot.diagnostic) - } - let credentials = Self.credentialsForWebBillingSnapshot( - credentials: GrokSettingsReader.resolvedCredentials(environment: context.env), - authenticatedByAuthFile: authenticatedByAuthFile) - // Cookie/gRPC fallback is a different browser session. Never attach the - // auth.json account's settings tier onto that usage. - let subscriptionTier: String? = - if authenticatedByAuthFile { - try await resolveSettingsTier(authCredentials) - } else { - nil - } - let enrichedBilling = webBilling.applying(subscriptionTier: subscriptionTier) - let snapshot = GrokUsageSnapshot( - billing: nil, - webBilling: enrichedBilling, - credentials: GrokStatusProbe.credentialsForSnapshot( - credentials: credentials, - billing: nil, - webBilling: enrichedBilling), - localSummary: GrokLocalSessionScanner.summarize(env: context.env), - cliVersion: GrokStatusProbe.detectVersion(env: context.env), - updatedAt: Date(), - subscriptionTier: subscriptionTier ?? enrichedBilling.subscriptionTier) - return self.makeResult( - usage: snapshot.toUsageSnapshot(), - sourceLabel: sourceLabel) - } - - func fetchWebBilling( - context: ProviderFetchContext, - proxyBilling: ProxyBillingFetch = { try await GrokCreditsProxyFetcher.fetch(credentials: $0) }) async throws - -> ( - snapshot: GrokWebBillingSnapshot, - sourceLabel: String, - authenticatedByAuthFile: Bool) - { - try await self.fetchLegacyWebBilling( - context: context, - browserCredentials: nil) + let id: String = "grok.web" + let kind: ProviderFetchKind = .web + typealias ProxyBillingFetch = @Sendable (GrokCredentials) async throws -> GrokWebBillingSnapshot + typealias WebBillingFetch = + @Sendable () async throws -> ( + snapshot: GrokWebBillingSnapshot, + sourceLabel: String, + authenticatedByAuthFile: Bool + ) + typealias SettingsTierFetch = @Sendable (GrokCredentials?) async throws -> String? + + /// Browser-cookie import must stay limited to surfaces where a person explicitly asked for it: + /// the menu-bar app runtime, a `userInitiated` interaction (set only by explicit refresh + /// commands and app UI gestures), or the environment override. Scheduled and background work + /// must keep the default `.background` context so it can never reach Chromium Keychain prompts. + static func canImportBrowserCookies(runtime: ProviderRuntime, env: [String: String]) -> Bool { + runtime == .app || ProviderInteractionContext.current == .userInitiated + || env["CODEXBAR_ALLOW_BROWSER_COOKIE_IMPORT"] == "1" + } + + func isAvailable(_ context: ProviderFetchContext) async -> Bool { + let cookieSource = context.settings?.grok?.cookieSource ?? .auto + if cookieSource != .off, + GrokCredentialRouting.normalizedWebCookie( + context.settings?.grok?.manualCookieHeader) != nil + { + return true + } + #if os(macOS) + if cookieSource == .auto, CookieHeaderCache.load(provider: .grok) != nil { + return true + } + if cookieSource == .auto, + Self.canImportBrowserCookies(runtime: context.runtime, env: context.env), + GrokCookieImporter.hasSession(browserDetection: context.browserDetection) + { + return true + } + #endif + return false + } + + func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { + try await self.fetch( + context, + webBilling: { [self] in + try await self.fetchWebBilling(context: context) + }) + } + + func fetch( + _ context: ProviderFetchContext, + webBilling fetchWebBilling: @escaping WebBillingFetch, + settingsTier loadSettingsTier: SettingsTierFetch? = nil + ) async throws -> ProviderFetchResult { + let authCredentials = GrokSettingsReader.resolvedCredentials(environment: context.env).flatMap { + credentials in + credentials.isExpired ? nil : credentials + } + let resolveSettingsTier = + loadSettingsTier ?? { credentials in + try await GrokStatusProbe.loadSettingsTier(credentials: credentials) + } + + let webBilling: GrokWebBillingSnapshot + let sourceLabel: String + let authenticatedByAuthFile: Bool + do { + (webBilling, sourceLabel, authenticatedByAuthFile) = try await fetchWebBilling() + } catch GrokWebBillingError.teamUsageUnsupported { + guard let authState = authCredentials, authState.isTeamPrincipal else { + throw GrokWebBillingError.teamUsageUnsupported + } + let subscriptionTier = try await resolveSettingsTier(authState) + let identitySnapshot = GrokStatusProbe.identityOnlySnapshot( + credentials: authState, + localSummary: GrokLocalSessionScanner.summarize(env: context.env), + cliVersion: GrokStatusProbe.detectVersion(env: context.env), + subscriptionTier: subscriptionTier) + return self.makeResult( + usage: identitySnapshot.toUsageSnapshot(), + sourceLabel: "grok-web", + diagnostic: identitySnapshot.diagnostic) + } + let credentials = Self.credentialsForWebBillingSnapshot( + credentials: GrokSettingsReader.resolvedCredentials(environment: context.env), + authenticatedByAuthFile: authenticatedByAuthFile) + // Cookie/gRPC fallback is a different browser session. Never attach the + // auth.json account's settings tier onto that usage. + let subscriptionTier: String? = + if authenticatedByAuthFile { + try await resolveSettingsTier(authCredentials) + } else { + nil + } + let enrichedBilling = webBilling.applying(subscriptionTier: subscriptionTier) + let snapshot = GrokUsageSnapshot( + billing: nil, + webBilling: enrichedBilling, + credentials: GrokStatusProbe.credentialsForSnapshot( + credentials: credentials, + billing: nil, + webBilling: enrichedBilling), + localSummary: GrokLocalSessionScanner.summarize(env: context.env), + cliVersion: GrokStatusProbe.detectVersion(env: context.env), + updatedAt: Date(), + subscriptionTier: subscriptionTier ?? enrichedBilling.subscriptionTier) + return self.makeResult( + usage: snapshot.toUsageSnapshot(), + sourceLabel: sourceLabel) + } + + func fetchWebBilling( + context: ProviderFetchContext, + proxyBilling: ProxyBillingFetch = { try await GrokCreditsProxyFetcher.fetch(credentials: $0) } + ) async throws + -> ( + snapshot: GrokWebBillingSnapshot, + sourceLabel: String, + authenticatedByAuthFile: Bool + ) + { + try await self.fetchLegacyWebBilling( + context: context, + browserCredentials: nil) + } + + static func fetchProxyFirst( + credentials: GrokCredentials?, + proxyBilling: ProxyBillingFetch, + legacyBilling: () async throws -> ( + snapshot: GrokWebBillingSnapshot, + sourceLabel: String, + authenticatedByAuthFile: Bool + ) + ) async throws -> ( + snapshot: GrokWebBillingSnapshot, + sourceLabel: String, + authenticatedByAuthFile: Bool + ) { + if let credentials, !credentials.isExpired { + do { + let snapshot = try await proxyBilling(credentials) + return (snapshot, "grok-cli-proxy", true) + } catch is CancellationError { + throw CancellationError() + } catch let error as URLError where error.code == .cancelled { + throw error + } catch { + // The legacy cookie and bearer paths remain available when the CLI proxy fails. + } + } + return try await legacyBilling() + } + + private func fetchLegacyWebBilling( + context: ProviderFetchContext, + browserCredentials: GrokCredentials? + ) async throws -> ( + snapshot: GrokWebBillingSnapshot, + sourceLabel: String, + authenticatedByAuthFile: Bool + ) { + let cookieSettings = context.settings?.grok + let cookieSource = cookieSettings?.cookieSource ?? .auto + let manualHeader = GrokCredentialRouting.normalizedWebCookie(cookieSettings?.manualCookieHeader) + var lastCookieError: Error? + + if cookieSource != .off, + let manualHeader, !manualHeader.isEmpty + { + do { + let snapshot = try await GrokWebBillingFetcher.fetch( + cookieHeader: manualHeader, + credentials: browserCredentials) + return (snapshot, "manual-cookie", false) + } catch { + lastCookieError = error + if cookieSource == .manual { + throw error } - - static func fetchProxyFirst( - credentials: GrokCredentials?, - proxyBilling: ProxyBillingFetch, - legacyBilling: () async throws -> ( - snapshot: GrokWebBillingSnapshot, - sourceLabel: String, - authenticatedByAuthFile: Bool)) async throws -> ( - snapshot: GrokWebBillingSnapshot, - sourceLabel: String, - authenticatedByAuthFile: Bool) - { - if let credentials, !credentials.isExpired { - do { - let snapshot = try await proxyBilling(credentials) - return (snapshot, "grok-cli-proxy", true) - } catch is CancellationError { - throw CancellationError() - } catch let error as URLError where error.code == .cancelled { - throw error - } catch { - // The legacy cookie and bearer paths remain available when the CLI proxy fails. - } - } - return try await legacyBilling() + } + } + + #if os(macOS) + var cacheObservation = CookieHeaderCache.observeForConditionalMutation(provider: .grok) + if cookieSource == .auto, let cached = cacheObservation.entry { + do { + let snapshot = try await Self.fetchValidCookieHeader( + cached.cookieHeader, + credentials: browserCredentials, + preferTrailingAuthenticationFailure: true) + return (snapshot, cached.sourceLabel, false) + } catch { + guard Self.isCookieAuthenticationFailure(error) else { throw error } + if CookieHeaderCache.clearIfCurrent(provider: .grok, expected: cached) { + cacheObservation = cacheObservation.afterOwnedClear() + } + lastCookieError = error } - - private func fetchLegacyWebBilling( - context: ProviderFetchContext, - browserCredentials: GrokCredentials?) async throws -> ( - snapshot: GrokWebBillingSnapshot, - sourceLabel: String, - authenticatedByAuthFile: Bool) - { - let cookieSettings = context.settings?.grok - let cookieSource = cookieSettings?.cookieSource ?? .auto - let manualHeader = cookieSettings?.manualCookieHeader? - .trimmingCharacters(in: .whitespacesAndNewlines) - - #if os(macOS) - var cacheObservation = CookieHeaderCache.observeForConditionalMutation(provider: .grok) - var lastCookieError: Error? - if cookieSource != .off, let cached = cacheObservation.entry { - do { - let snapshot = try await Self.fetchValidCookieHeader( - cached.cookieHeader, - credentials: browserCredentials, - preferTrailingAuthenticationFailure: true) - return (snapshot, cached.sourceLabel, false) - } catch { - guard Self.isCookieAuthenticationFailure(error) else { throw error } - if CookieHeaderCache.clearIfCurrent(provider: .grok, expected: cached) { - cacheObservation = cacheObservation.afterOwnedClear() - } - lastCookieError = error - } - } - - if cookieSource != .off, - let manualHeader, !manualHeader.isEmpty - { - do { - let snapshot = try await Self.fetchValidCookieHeader( - manualHeader, - credentials: browserCredentials) - return (snapshot, "manual-cookie", false) - } catch { - lastCookieError = error - if cookieSource == .manual { - throw error - } - } - } - - if cookieSource == .auto, - Self.canImportBrowserCookies(runtime: context.runtime, env: context.env) - { - do { - let sessions = try GrokCookieImporter.importSessions( - browserDetection: context.browserDetection) - let (snapshot, sourceLabel) = try await Self.fetchFirstValidCookieSession( - sessions, - credentials: browserCredentials, - cacheObservation: cacheObservation) - return (snapshot, sourceLabel, false) - } catch { - lastCookieError = error - } - throw lastCookieError ?? GrokWebBillingError.missingCredentials - } - #endif - - throw GrokWebBillingError.missingCredentials + } + + if cookieSource == .auto, + Self.canImportBrowserCookies(runtime: context.runtime, env: context.env) + { + do { + let sessions = try GrokCookieImporter.importSessions( + browserDetection: context.browserDetection) + let (snapshot, sourceLabel) = try await Self.fetchFirstValidCookieSession( + sessions, + credentials: browserCredentials, + cacheObservation: cacheObservation) + return (snapshot, sourceLabel, false) + } catch { + lastCookieError = error } - - static func resolvedCredentialsResult(context: ProviderFetchContext) -> Result< - GrokCredentials, Error, - > { - let fileResult: Result = Result { - try GrokCredentialsStore.load(env: context.env) - } - if let file = try? fileResult.get() { - return .success(file) - } - if let pasted = GrokSettingsReader.pastedCredentials(environment: context.env) { - return .success(pasted) - } - return fileResult + throw lastCookieError ?? GrokWebBillingError.missingCredentials + } + #endif + + throw lastCookieError ?? GrokWebBillingError.missingCredentials + } + + static func resolvedCredentialsResult(context: ProviderFetchContext) -> Result< + GrokCredentials, Error, + > { + let fileResult: Result = Result { + try GrokCredentialsStore.load(env: context.env) + } + if let file = try? fileResult.get(), !file.isExpired { + return .success(file) + } + if let pasted = GrokSettingsReader.pastedCredentials(environment: context.env) { + return .success(pasted) + } + return fileResult + } + + static func credentialsForWebBillingSnapshot( + credentials: GrokCredentials?, + authenticatedByAuthFile: Bool + ) -> GrokCredentials? { + authenticatedByAuthFile ? credentials : nil + } + + #if os(macOS) + static func fetchFirstValidCookieSession( + _ sessions: [GrokCookieImporter.SessionInfo], + credentials: GrokCredentials? = nil, + cacheObservation: CookieHeaderCache.ConditionalMutationObservation? = nil, + fetch: ((String, GrokCredentials?) async throws -> GrokWebBillingSnapshot)? = nil + ) async throws + -> (GrokWebBillingSnapshot, String) + { + let fetchSnapshot = + fetch ?? { cookieHeader, credentials in + try await GrokWebBillingFetcher.fetch( + cookieHeader: cookieHeader, + credentials: credentials) } - - static func credentialsForWebBillingSnapshot( - credentials: GrokCredentials?, - authenticatedByAuthFile: Bool) -> GrokCredentials? - { - authenticatedByAuthFile ? credentials : nil + var lastError: Error? + var teamUsageUnsupportedError: Error? + for session in sessions { + do { + let snapshot = try await Self.fetchValidCookieHeader( + session.cookieHeader, + credentials: credentials, + fetch: fetchSnapshot) + if let cacheObservation { + CookieHeaderCache.storeIfObservationCurrent( + provider: .grok, + expected: cacheObservation, + cookieHeader: session.cookieHeader, + sourceLabel: session.sourceLabel) + } + return (snapshot, session.sourceLabel) + } catch { + if case GrokWebBillingError.teamUsageUnsupported = error { + teamUsageUnsupportedError = error + } + lastError = error } - - #if os(macOS) - static func fetchFirstValidCookieSession( - _ sessions: [GrokCookieImporter.SessionInfo], - credentials: GrokCredentials? = nil, - cacheObservation: CookieHeaderCache.ConditionalMutationObservation? = nil, - fetch: ((String, GrokCredentials?) async throws -> GrokWebBillingSnapshot)? = nil) async throws - -> (GrokWebBillingSnapshot, String) - { - let fetchSnapshot = - fetch ?? { cookieHeader, credentials in - try await GrokWebBillingFetcher.fetch( - cookieHeader: cookieHeader, - credentials: credentials) - } - var lastError: Error? - var teamUsageUnsupportedError: Error? - for session in sessions { - do { - let snapshot = try await Self.fetchValidCookieHeader( - session.cookieHeader, - credentials: credentials, - fetch: fetchSnapshot) - if let cacheObservation { - CookieHeaderCache.storeIfObservationCurrent( - provider: .grok, - expected: cacheObservation, - cookieHeader: session.cookieHeader, - sourceLabel: session.sourceLabel) - } - return (snapshot, session.sourceLabel) - } catch { - if case GrokWebBillingError.teamUsageUnsupported = error { - teamUsageUnsupportedError = error - } - lastError = error - } - } - throw teamUsageUnsupportedError ?? lastError ?? GrokWebBillingError.missingCredentials + } + throw teamUsageUnsupportedError ?? lastError ?? GrokWebBillingError.missingCredentials + } + + /// `preferTrailingAuthenticationFailure` lets a cached-cookie caller surface a trailing + /// 401/403 over the team classification so stale sessions still trigger cache eviction. + /// Non-authentication trailing errors keep `teamUsageUnsupported` so team principals + /// degrade to identity-only data instead of failing outright. + static func fetchValidCookieHeader( + _ cookieHeader: String, + credentials: GrokCredentials? = nil, + preferTrailingAuthenticationFailure: Bool = false, + fetch: ((String, GrokCredentials?) async throws -> GrokWebBillingSnapshot)? = nil + ) async throws + -> GrokWebBillingSnapshot + { + let fetchSnapshot = + fetch ?? { cookieHeader, credentials in + try await GrokWebBillingFetcher.fetch( + cookieHeader: cookieHeader, + credentials: credentials) } - - /// `preferTrailingAuthenticationFailure` lets a cached-cookie caller surface a trailing - /// 401/403 over the team classification so stale sessions still trigger cache eviction. - /// Non-authentication trailing errors keep `teamUsageUnsupported` so team principals - /// degrade to identity-only data instead of failing outright. - static func fetchValidCookieHeader( - _ cookieHeader: String, - credentials: GrokCredentials? = nil, - preferTrailingAuthenticationFailure: Bool = false, - fetch: ((String, GrokCredentials?) async throws -> GrokWebBillingSnapshot)? = nil) async throws - -> GrokWebBillingSnapshot - { - let fetchSnapshot = - fetch ?? { cookieHeader, credentials in - try await GrokWebBillingFetcher.fetch( - cookieHeader: cookieHeader, - credentials: credentials) - } - var lastError: Error? - var teamUsageUnsupportedError: Error? - for authCredentials in Self.cookieAuthAttempts(credentials: credentials) { - do { - return try await fetchSnapshot(cookieHeader, authCredentials) - } catch { - if case GrokWebBillingError.teamUsageUnsupported = error { - teamUsageUnsupportedError = error - } - lastError = error - } - } - if let teamUsageUnsupportedError { - let trailingAuthenticationFailure = - preferTrailingAuthenticationFailure - && lastError.map(Self.isCookieAuthenticationFailure) == true - if !trailingAuthenticationFailure { - throw teamUsageUnsupportedError - } - } - throw lastError ?? GrokWebBillingError.missingCredentials + var lastError: Error? + var teamUsageUnsupportedError: Error? + for authCredentials in Self.cookieAuthAttempts(credentials: credentials) { + do { + return try await fetchSnapshot(cookieHeader, authCredentials) + } catch { + if case GrokWebBillingError.teamUsageUnsupported = error { + teamUsageUnsupportedError = error + } + lastError = error } - - static func cookieAuthAttempts(credentials: GrokCredentials?) -> [GrokCredentials?] { - guard let credentials, !credentials.isExpired else { return [nil] } - return [credentials, nil] - } - - static func isCookieAuthenticationFailure(_ 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, message): - return GrokWebBillingError.isAuthenticationFailure(status: status, message: message) - case .missingCredentials, .emptyResponse, .invalidResponse, .teamUsageUnsupported, .parseFailed: - return false - } - } - #endif - - func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool { - false + } + if let teamUsageUnsupportedError { + let trailingAuthenticationFailure = + preferTrailingAuthenticationFailure + && lastError.map(Self.isCookieAuthenticationFailure) == true + if !trailingAuthenticationFailure { + throw teamUsageUnsupportedError } + } + throw lastError ?? GrokWebBillingError.missingCredentials + } + + static func cookieAuthAttempts(credentials: GrokCredentials?) -> [GrokCredentials?] { + guard let credentials, !credentials.isExpired else { return [nil] } + return [credentials, nil] + } + + static func isCookieAuthenticationFailure(_ error: Error) -> Bool { + guard let error = error as? GrokWebBillingError else { return false } + switch error { + case .requestFailed(let status, _): + return status == 401 || status == 403 + case .rpcFailed(let status, let message): + return GrokWebBillingError.isAuthenticationFailure(status: status, message: message) + case .missingCredentials, .emptyResponse, .invalidResponse, .teamUsageUnsupported, .parseFailed: + return false + } + } + #endif + + func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool { + false + } } diff --git a/Tests/CodexBarTests/GrokSettingsReaderTests.swift b/Tests/CodexBarTests/GrokSettingsReaderTests.swift index 03e56b447c..9b736ce7e9 100644 --- a/Tests/CodexBarTests/GrokSettingsReaderTests.swift +++ b/Tests/CodexBarTests/GrokSettingsReaderTests.swift @@ -22,6 +22,27 @@ struct GrokSettingsReaderTests { #expect(GrokSettingsReader.oauthAccessToken(environment: [:]) == nil) } + @Test + func `prefers a pasted SuperGrok token when auth json is expired`() throws { + let home = FileManager.default.temporaryDirectory + .appendingPathComponent("CodexBar-GrokExpiredAuth-\(UUID().uuidString)", isDirectory: true) + try FileManager.default.createDirectory(at: home, withIntermediateDirectories: true) + defer { try? FileManager.default.removeItem(at: home) } + try Data(""" + { + "https://auth.x.ai::client": { + "key": "stale-file-token", + "auth_mode": "oidc", + "expires_at": "2020-01-01T00:00:00Z" + } + } + """.utf8).write(to: home.appendingPathComponent("auth.json")) + + var env = [GrokSettingsReader.oauthTokenEnvironmentKey: "pasted-token"] + env["GROK_HOME"] = home.path + #expect(GrokSettingsReader.resolvedCredentials(environment: env)?.accessToken == "pasted-token") + } + @Test func `descriptor exposes SuperGrok token accounts`() { let support = GrokProviderDescriptor.descriptor.credentials?.tokenAccountSupport From 5da46bbbdc3582e66809baef55626845b46fa46b Mon Sep 17 00:00:00 2001 From: "o.akimov" Date: Mon, 17 Aug 2026 12:06:39 +0200 Subject: [PATCH 03/11] Restore four-space indent in Grok descriptor --- .../Grok/GrokProviderDescriptor.swift | 1104 ++++++++--------- 1 file changed, 547 insertions(+), 557 deletions(-) diff --git a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift index a03950340e..7876abe10d 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift @@ -2,578 +2,568 @@ import Foundation import SweetCookieKit public enum GrokProviderDescriptor { - public static let descriptor: ProviderDescriptor = Self.makeDescriptor() - private static let credentials = ProviderCredentialAdapter( - tokenAccountSupport: TokenAccountSupport( - title: "SuperGrok tokens", - subtitle: - "Paste a SuperGrok bearer or grok.com cookie. Open token file opens ~/.grok/auth.json.", - placeholder: "Bearer … or Cookie: …", - injection: .environment(key: GrokSettingsReader.oauthTokenEnvironmentKey), - requiresManualCookieSource: false, - cookieName: nil, - environmentKeysToScrub: [GrokSettingsReader.oauthTokenEnvironmentKey], - environmentOverride: { token in - guard let oauth = GrokCredentialRouting.normalizedOAuthToken(token) else { return nil } - return [GrokSettingsReader.oauthTokenEnvironmentKey: oauth] - }), - selectedAccountSourceModeResolver: { base, account, _ in - guard let account else { return base } - return GrokCredentialRouting.resolve( - tokenAccountToken: account.token, - manualCookieHeader: nil - ).sourceMode ?? base - }) - - /// Grok is normally signed in through Chrome; avoid touching unrelated browser keychains. - private static var browserCookieOrder: BrowserCookieImportOrder? { - #if os(macOS) - [.chrome] - #else - nil - #endif - } - - static func makeDescriptor() -> ProviderDescriptor { - ProviderDescriptor( - id: .grok, - settingsSection: .init( - GrokProviderSettingsKey.self, - cookieSettings: { settings in - CookieProviderSettings( - cookieSource: settings.cookieSource, - manualCookieHeader: settings.manualCookieHeader) - }, - credentialSettings: { context in - let cookies = context.cookieSettings(for: .grok) - let routing = GrokCredentialRouting.resolve( - tokenAccountToken: context.account?.token, - manualCookieHeader: context.account == nil ? cookies.manualCookieHeader : nil) - return GrokProviderSettings( - cookieSource: routing.manualCookieHeader != nil ? .manual : cookies.cookieSource, - manualCookieHeader: routing.manualCookieHeader ?? cookies.manualCookieHeader) - }), - credentials: self.credentials, - metadata: ProviderMetadata( - id: .grok, - displayName: "Grok", - sessionLabel: "Credits", - weeklyLabel: "On-demand", - opusLabel: nil, - supportsOpus: false, - supportsCredits: false, - creditsHint: "", - toggleTitle: "Show Grok usage", - cliName: "grok", - defaultEnabled: false, - widgetSelectable: false, - isPrimaryProvider: false, - usesAccountFallback: false, - debugLogUnavailableMessage: "Grok debug log not yet implemented", - browserCookieOrder: self.browserCookieOrder, - dashboardURL: "https://grok.com/?_s=usage", - changelogURL: "https://x.ai/news", - statusPageURL: nil, - statusLinkURL: "https://status.x.ai"), - branding: ProviderBranding( - iconStyle: .init(provider: .grok), - iconResourceName: "ProviderIcon-grok", - color: ProviderColor(red: 16 / 255, green: 163 / 255, blue: 127 / 255), - confettiPalette: [ - ProviderColor(hex: 0x000000), - ProviderColor(hex: 0x868686), - ProviderColor(hex: 0xFDFDFD), - ]), - tokenCost: ProviderTokenCostConfig( - supportsTokenCost: false, - noDataMessage: { "Grok cost summary is not supported yet." }), - pace: ProviderPaceCapability( - resetWindowPace: .custom { window, now in - guard Self.primaryLabel(window: window, now: now) == "Weekly", - let resetsAt = window.resetsAt - else { return false } - let windowMinutes = window.windowMinutes ?? 7 * 24 * 60 - let timeUntilReset = resetsAt.timeIntervalSince(now) - return windowMinutes > 0 - && timeUntilReset > 0 - && timeUntilReset <= TimeInterval(windowMinutes) * 60 - }), - presentation: ProviderUsagePresentation(rateWindowLabeler: { metadata, snapshot, now in - ProviderRateWindowLabels( - primary: Self.displayLabel(window: snapshot.primary, now: now) ?? metadata.sessionLabel, - secondary: metadata.weeklyLabel, - tertiary: metadata.opusLabel ?? "Sonnet", - showsTertiary: metadata.supportsOpus) - }), - fetchPlan: ProviderFetchPlan( - sourceModes: [.auto, .cli, .oauth, .web], - pipeline: ProviderFetchPipeline(resolveStrategies: self.resolveStrategies)), - cli: ProviderCLIConfig( - name: "grok", - versionDetector: { _ in GrokStatusProbe.detectVersion() }, - browserSupportExemption: { _, _, _ in true })) - } - - private static func resolveStrategies(context: ProviderFetchContext) async - -> [any ProviderFetchStrategy] - { - switch context.sourceMode { - case .auto: - [GrokCLIFetchStrategy(), GrokOAuthFetchStrategy(), GrokWebFetchStrategy()] - case .cli: - [GrokCLIFetchStrategy()] - case .oauth: - [GrokOAuthFetchStrategy()] - case .web: - [GrokWebFetchStrategy()] - case .api: - [] - } - } - - /// Returns a contextual label for Grok's primary usage bar ("Weekly" or "Monthly"). - /// Prefer the billing period duration when available; fall back to reset distance for - /// web billing payloads that expose only a reset timestamp. - public static func primaryLabel(window: RateWindow?, now: Date = .now) -> String? { - if let minutes = window?.windowMinutes { - return self.primaryLabel(duration: TimeInterval(minutes) * 60) - } - return self.primaryLabel(resetsAt: window?.resetsAt, now: now) - } - - public static func primaryLabel(resetsAt: Date?, now: Date = .now) -> String? { - guard let resetsAt else { return nil } - return self.primaryLabel(duration: resetsAt.timeIntervalSince(now)) - } - - /// Grok's current untyped credits surface is the weekly credit pool (#2929). Keep explicit - /// durations authoritative, but do not lose the weekly label near the end of an untyped window. - public static func displayLabel(window: RateWindow?, now: Date = .now) -> String? { - guard let window else { return nil } - if let label = self.primaryLabel(window: window, now: now) { - return label - } - guard window.windowMinutes == nil, window.resetsAt != nil else { return nil } - return "Weekly" - } - - private static func primaryLabel(duration seconds: TimeInterval) -> String? { - guard seconds > 3600 else { return nil } - let days = Int((seconds / 86400).rounded(.toNearestOrAwayFromZero)) - if (4...12).contains(days) { - return "Weekly" - } - if (20...45).contains(days) { - return "Monthly" - } - return nil - } + public static let descriptor: ProviderDescriptor = Self.makeDescriptor() + private static let credentials = ProviderCredentialAdapter( + tokenAccountSupport: TokenAccountSupport( + title: "SuperGrok tokens", + subtitle: + "Paste a SuperGrok bearer or grok.com cookie. Open token file opens ~/.grok/auth.json.", + placeholder: "Bearer … or Cookie: …", + injection: .environment(key: GrokSettingsReader.oauthTokenEnvironmentKey), + requiresManualCookieSource: false, + cookieName: nil, + environmentKeysToScrub: [GrokSettingsReader.oauthTokenEnvironmentKey], + environmentOverride: { token in + guard let oauth = GrokCredentialRouting.normalizedOAuthToken(token) else { return nil } + return [GrokSettingsReader.oauthTokenEnvironmentKey: oauth] + }), + selectedAccountSourceModeResolver: { base, account, _ in + guard let account else { return base } + return GrokCredentialRouting.resolve( + tokenAccountToken: account.token, + manualCookieHeader: nil).sourceMode ?? base + }) + + /// Grok is normally signed in through Chrome; avoid touching unrelated browser keychains. + private static var browserCookieOrder: BrowserCookieImportOrder? { + #if os(macOS) + [.chrome] + #else + nil + #endif + } + + static func makeDescriptor() -> ProviderDescriptor { + ProviderDescriptor( + id: .grok, + settingsSection: .init( + GrokProviderSettingsKey.self, + cookieSettings: { settings in + CookieProviderSettings( + cookieSource: settings.cookieSource, + manualCookieHeader: settings.manualCookieHeader) + }, + credentialSettings: { context in + let cookies = context.cookieSettings(for: .grok) + let routing = GrokCredentialRouting.resolve( + tokenAccountToken: context.account?.token, + manualCookieHeader: context.account == nil ? cookies.manualCookieHeader : nil) + return GrokProviderSettings( + cookieSource: routing.manualCookieHeader != nil ? .manual : cookies.cookieSource, + manualCookieHeader: routing.manualCookieHeader ?? cookies.manualCookieHeader) + }), + credentials: self.credentials, + metadata: ProviderMetadata( + id: .grok, + displayName: "Grok", + sessionLabel: "Credits", + weeklyLabel: "On-demand", + opusLabel: nil, + supportsOpus: false, + supportsCredits: false, + creditsHint: "", + toggleTitle: "Show Grok usage", + cliName: "grok", + defaultEnabled: false, + widgetSelectable: false, + isPrimaryProvider: false, + usesAccountFallback: false, + debugLogUnavailableMessage: "Grok debug log not yet implemented", + browserCookieOrder: self.browserCookieOrder, + dashboardURL: "https://grok.com/?_s=usage", + changelogURL: "https://x.ai/news", + statusPageURL: nil, + statusLinkURL: "https://status.x.ai"), + branding: ProviderBranding( + iconStyle: .init(provider: .grok), + iconResourceName: "ProviderIcon-grok", + color: ProviderColor(red: 16 / 255, green: 163 / 255, blue: 127 / 255), + confettiPalette: [ + ProviderColor(hex: 0x000000), + ProviderColor(hex: 0x868686), + ProviderColor(hex: 0xFDFDFD), + ]), + tokenCost: ProviderTokenCostConfig( + supportsTokenCost: false, + noDataMessage: { "Grok cost summary is not supported yet." }), + pace: ProviderPaceCapability( + resetWindowPace: .custom { window, now in + guard Self.primaryLabel(window: window, now: now) == "Weekly", + let resetsAt = window.resetsAt + else { return false } + let windowMinutes = window.windowMinutes ?? 7 * 24 * 60 + let timeUntilReset = resetsAt.timeIntervalSince(now) + return windowMinutes > 0 + && timeUntilReset > 0 + && timeUntilReset <= TimeInterval(windowMinutes) * 60 + }), + presentation: ProviderUsagePresentation(rateWindowLabeler: { metadata, snapshot, now in + ProviderRateWindowLabels( + primary: Self.displayLabel(window: snapshot.primary, now: now) ?? metadata.sessionLabel, + secondary: metadata.weeklyLabel, + tertiary: metadata.opusLabel ?? "Sonnet", + showsTertiary: metadata.supportsOpus) + }), + fetchPlan: ProviderFetchPlan( + sourceModes: [.auto, .cli, .oauth, .web], + pipeline: ProviderFetchPipeline(resolveStrategies: self.resolveStrategies)), + cli: ProviderCLIConfig( + name: "grok", + versionDetector: { _ in GrokStatusProbe.detectVersion() }, + browserSupportExemption: { _, _, _ in true })) + } + + private static func resolveStrategies(context: ProviderFetchContext) async + -> [any ProviderFetchStrategy] + { + switch context.sourceMode { + case .auto: + [GrokCLIFetchStrategy(), GrokOAuthFetchStrategy(), GrokWebFetchStrategy()] + case .cli: + [GrokCLIFetchStrategy()] + case .oauth: + [GrokOAuthFetchStrategy()] + case .web: + [GrokWebFetchStrategy()] + case .api: + [] + } + } + + /// Returns a contextual label for Grok's primary usage bar ("Weekly" or "Monthly"). + /// Prefer the billing period duration when available; fall back to reset distance for + /// web billing payloads that expose only a reset timestamp. + public static func primaryLabel(window: RateWindow?, now: Date = .now) -> String? { + if let minutes = window?.windowMinutes { + return self.primaryLabel(duration: TimeInterval(minutes) * 60) + } + return self.primaryLabel(resetsAt: window?.resetsAt, now: now) + } + + public static func primaryLabel(resetsAt: Date?, now: Date = .now) -> String? { + guard let resetsAt else { return nil } + return self.primaryLabel(duration: resetsAt.timeIntervalSince(now)) + } + + /// Grok's current untyped credits surface is the weekly credit pool (#2929). Keep explicit + /// durations authoritative, but do not lose the weekly label near the end of an untyped window. + public static func displayLabel(window: RateWindow?, now: Date = .now) -> String? { + guard let window else { return nil } + if let label = self.primaryLabel(window: window, now: now) { + return label + } + guard window.windowMinutes == nil, window.resetsAt != nil else { return nil } + return "Weekly" + } + + private static func primaryLabel(duration seconds: TimeInterval) -> String? { + guard seconds > 3600 else { return nil } + let days = Int((seconds / 86400).rounded(.toNearestOrAwayFromZero)) + if (4...12).contains(days) { + return "Weekly" + } + if (20...45).contains(days) { + return "Monthly" + } + return nil + } } struct GrokCLIFetchStrategy: ProviderFetchStrategy { - let id: String = "grok.cli" - let kind: ProviderFetchKind = .cli - - func isAvailable(_ context: ProviderFetchContext) async -> Bool { - BinaryLocator.resolveGrokBinary(env: context.env) != nil - } - - func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { - let probe = GrokStatusProbe() - let snap = try await probe.fetch(env: context.env) - return self.makeResult( - usage: snap.toUsageSnapshot(), - sourceLabel: "grok-cli", - diagnostic: snap.diagnostic) - } - - func shouldFallback(on _: Error, context: ProviderFetchContext) -> Bool { - context.sourceMode == .auto - } + let id: String = "grok.cli" + let kind: ProviderFetchKind = .cli + + func isAvailable(_ context: ProviderFetchContext) async -> Bool { + BinaryLocator.resolveGrokBinary(env: context.env) != nil + } + + func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { + let probe = GrokStatusProbe() + let snap = try await probe.fetch(env: context.env) + return self.makeResult( + usage: snap.toUsageSnapshot(), + sourceLabel: "grok-cli", + diagnostic: snap.diagnostic) + } + + func shouldFallback(on _: Error, context: ProviderFetchContext) -> Bool { + context.sourceMode == .auto + } } struct GrokOAuthFetchStrategy: ProviderFetchStrategy { - let id: String = "grok.oauth" - let kind: ProviderFetchKind = .oauth - - func isAvailable(_ context: ProviderFetchContext) async -> Bool { - GrokSettingsReader.resolvedCredentials(environment: context.env) != nil - || FileManager.default.fileExists( - atPath: GrokCredentialsStore.authFileURL(env: context.env).path) - } - - func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { - try await GrokWebFetchStrategy().fetch(context) { - let credentials = try GrokWebFetchStrategy.resolvedCredentialsResult(context: context).get() - guard !credentials.isExpired else { - throw GrokWebBillingError.missingCredentials - } - do { - let snapshot = try await GrokCreditsProxyFetcher.fetch(credentials: credentials) - return (snapshot, "grok-cli-proxy", true) - } catch is CancellationError { - throw CancellationError() - } catch let error as URLError where error.code == .cancelled { - throw error - } catch { - let snapshot = try await GrokWebBillingFetcher.fetch(credentials: credentials) - return (snapshot, "grok-web", true) - } - } - } - - func shouldFallback(on _: Error, context: ProviderFetchContext) -> Bool { - context.sourceMode == .auto - } + let id: String = "grok.oauth" + let kind: ProviderFetchKind = .oauth + + func isAvailable(_ context: ProviderFetchContext) async -> Bool { + GrokSettingsReader.resolvedCredentials(environment: context.env) != nil + || FileManager.default.fileExists( + atPath: GrokCredentialsStore.authFileURL(env: context.env).path) + } + + func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { + try await GrokWebFetchStrategy().fetch(context) { + let credentials = try GrokWebFetchStrategy.resolvedCredentialsResult(context: context).get() + guard !credentials.isExpired else { + throw GrokWebBillingError.missingCredentials + } + do { + let snapshot = try await GrokCreditsProxyFetcher.fetch(credentials: credentials) + return (snapshot, "grok-cli-proxy", true) + } catch is CancellationError { + throw CancellationError() + } catch let error as URLError where error.code == .cancelled { + throw error + } catch { + let snapshot = try await GrokWebBillingFetcher.fetch(credentials: credentials) + return (snapshot, "grok-web", true) + } + } + } + + func shouldFallback(on _: Error, context: ProviderFetchContext) -> Bool { + context.sourceMode == .auto + } } struct GrokWebFetchStrategy: ProviderFetchStrategy { - let id: String = "grok.web" - let kind: ProviderFetchKind = .web - typealias ProxyBillingFetch = @Sendable (GrokCredentials) async throws -> GrokWebBillingSnapshot - typealias WebBillingFetch = - @Sendable () async throws -> ( - snapshot: GrokWebBillingSnapshot, - sourceLabel: String, - authenticatedByAuthFile: Bool - ) - typealias SettingsTierFetch = @Sendable (GrokCredentials?) async throws -> String? - - /// Browser-cookie import must stay limited to surfaces where a person explicitly asked for it: - /// the menu-bar app runtime, a `userInitiated` interaction (set only by explicit refresh - /// commands and app UI gestures), or the environment override. Scheduled and background work - /// must keep the default `.background` context so it can never reach Chromium Keychain prompts. - static func canImportBrowserCookies(runtime: ProviderRuntime, env: [String: String]) -> Bool { - runtime == .app || ProviderInteractionContext.current == .userInitiated - || env["CODEXBAR_ALLOW_BROWSER_COOKIE_IMPORT"] == "1" - } - - func isAvailable(_ context: ProviderFetchContext) async -> Bool { - let cookieSource = context.settings?.grok?.cookieSource ?? .auto - if cookieSource != .off, - GrokCredentialRouting.normalizedWebCookie( - context.settings?.grok?.manualCookieHeader) != nil - { - return true - } - #if os(macOS) - if cookieSource == .auto, CookieHeaderCache.load(provider: .grok) != nil { - return true - } - if cookieSource == .auto, - Self.canImportBrowserCookies(runtime: context.runtime, env: context.env), - GrokCookieImporter.hasSession(browserDetection: context.browserDetection) - { - return true - } - #endif - return false - } - - func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { - try await self.fetch( - context, - webBilling: { [self] in - try await self.fetchWebBilling(context: context) - }) - } - - func fetch( - _ context: ProviderFetchContext, - webBilling fetchWebBilling: @escaping WebBillingFetch, - settingsTier loadSettingsTier: SettingsTierFetch? = nil - ) async throws -> ProviderFetchResult { - let authCredentials = GrokSettingsReader.resolvedCredentials(environment: context.env).flatMap { - credentials in - credentials.isExpired ? nil : credentials - } - let resolveSettingsTier = - loadSettingsTier ?? { credentials in - try await GrokStatusProbe.loadSettingsTier(credentials: credentials) - } - - let webBilling: GrokWebBillingSnapshot - let sourceLabel: String - let authenticatedByAuthFile: Bool - do { - (webBilling, sourceLabel, authenticatedByAuthFile) = try await fetchWebBilling() - } catch GrokWebBillingError.teamUsageUnsupported { - guard let authState = authCredentials, authState.isTeamPrincipal else { - throw GrokWebBillingError.teamUsageUnsupported - } - let subscriptionTier = try await resolveSettingsTier(authState) - let identitySnapshot = GrokStatusProbe.identityOnlySnapshot( - credentials: authState, - localSummary: GrokLocalSessionScanner.summarize(env: context.env), - cliVersion: GrokStatusProbe.detectVersion(env: context.env), - subscriptionTier: subscriptionTier) - return self.makeResult( - usage: identitySnapshot.toUsageSnapshot(), - sourceLabel: "grok-web", - diagnostic: identitySnapshot.diagnostic) - } - let credentials = Self.credentialsForWebBillingSnapshot( - credentials: GrokSettingsReader.resolvedCredentials(environment: context.env), - authenticatedByAuthFile: authenticatedByAuthFile) - // Cookie/gRPC fallback is a different browser session. Never attach the - // auth.json account's settings tier onto that usage. - let subscriptionTier: String? = - if authenticatedByAuthFile { - try await resolveSettingsTier(authCredentials) - } else { - nil - } - let enrichedBilling = webBilling.applying(subscriptionTier: subscriptionTier) - let snapshot = GrokUsageSnapshot( - billing: nil, - webBilling: enrichedBilling, - credentials: GrokStatusProbe.credentialsForSnapshot( - credentials: credentials, - billing: nil, - webBilling: enrichedBilling), - localSummary: GrokLocalSessionScanner.summarize(env: context.env), - cliVersion: GrokStatusProbe.detectVersion(env: context.env), - updatedAt: Date(), - subscriptionTier: subscriptionTier ?? enrichedBilling.subscriptionTier) - return self.makeResult( - usage: snapshot.toUsageSnapshot(), - sourceLabel: sourceLabel) - } - - func fetchWebBilling( - context: ProviderFetchContext, - proxyBilling: ProxyBillingFetch = { try await GrokCreditsProxyFetcher.fetch(credentials: $0) } - ) async throws - -> ( - snapshot: GrokWebBillingSnapshot, - sourceLabel: String, - authenticatedByAuthFile: Bool - ) - { - try await self.fetchLegacyWebBilling( - context: context, - browserCredentials: nil) - } - - static func fetchProxyFirst( - credentials: GrokCredentials?, - proxyBilling: ProxyBillingFetch, - legacyBilling: () async throws -> ( - snapshot: GrokWebBillingSnapshot, - sourceLabel: String, - authenticatedByAuthFile: Bool - ) - ) async throws -> ( - snapshot: GrokWebBillingSnapshot, - sourceLabel: String, - authenticatedByAuthFile: Bool - ) { - if let credentials, !credentials.isExpired { - do { - let snapshot = try await proxyBilling(credentials) - return (snapshot, "grok-cli-proxy", true) - } catch is CancellationError { - throw CancellationError() - } catch let error as URLError where error.code == .cancelled { - throw error - } catch { - // The legacy cookie and bearer paths remain available when the CLI proxy fails. - } - } - return try await legacyBilling() - } - - private func fetchLegacyWebBilling( - context: ProviderFetchContext, - browserCredentials: GrokCredentials? - ) async throws -> ( - snapshot: GrokWebBillingSnapshot, - sourceLabel: String, - authenticatedByAuthFile: Bool - ) { - let cookieSettings = context.settings?.grok - let cookieSource = cookieSettings?.cookieSource ?? .auto - let manualHeader = GrokCredentialRouting.normalizedWebCookie(cookieSettings?.manualCookieHeader) - var lastCookieError: Error? - - if cookieSource != .off, - let manualHeader, !manualHeader.isEmpty - { - do { - let snapshot = try await GrokWebBillingFetcher.fetch( - cookieHeader: manualHeader, - credentials: browserCredentials) - return (snapshot, "manual-cookie", false) - } catch { - lastCookieError = error - if cookieSource == .manual { - throw error + let id: String = "grok.web" + let kind: ProviderFetchKind = .web + typealias ProxyBillingFetch = @Sendable (GrokCredentials) async throws -> GrokWebBillingSnapshot + typealias WebBillingFetch = + @Sendable () async throws -> ( + snapshot: GrokWebBillingSnapshot, + sourceLabel: String, + authenticatedByAuthFile: Bool) + typealias SettingsTierFetch = @Sendable (GrokCredentials?) async throws -> String? + + /// Browser-cookie import must stay limited to surfaces where a person explicitly asked for it: + /// the menu-bar app runtime, a `userInitiated` interaction (set only by explicit refresh + /// commands and app UI gestures), or the environment override. Scheduled and background work + /// must keep the default `.background` context so it can never reach Chromium Keychain prompts. + static func canImportBrowserCookies(runtime: ProviderRuntime, env: [String: String]) -> Bool { + runtime == .app || ProviderInteractionContext.current == .userInitiated + || env["CODEXBAR_ALLOW_BROWSER_COOKIE_IMPORT"] == "1" + } + + func isAvailable(_ context: ProviderFetchContext) async -> Bool { + let cookieSource = context.settings?.grok?.cookieSource ?? .auto + if cookieSource != .off, + GrokCredentialRouting.normalizedWebCookie( + context.settings?.grok?.manualCookieHeader) != nil + { + return true + } + #if os(macOS) + if cookieSource == .auto, CookieHeaderCache.load(provider: .grok) != nil { + return true + } + if cookieSource == .auto, + Self.canImportBrowserCookies(runtime: context.runtime, env: context.env), + GrokCookieImporter.hasSession(browserDetection: context.browserDetection) + { + return true + } + #endif + return false + } + + func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { + try await self.fetch( + context, + webBilling: { [self] in + try await self.fetchWebBilling(context: context) + }) + } + + func fetch( + _ context: ProviderFetchContext, + webBilling fetchWebBilling: @escaping WebBillingFetch, + settingsTier loadSettingsTier: SettingsTierFetch? = nil) async throws -> ProviderFetchResult + { + let authCredentials = GrokSettingsReader.resolvedCredentials(environment: context.env).flatMap { credentials in + credentials.isExpired ? nil : credentials + } + let resolveSettingsTier = + loadSettingsTier ?? { credentials in + try await GrokStatusProbe.loadSettingsTier(credentials: credentials) + } + + let webBilling: GrokWebBillingSnapshot + let sourceLabel: String + let authenticatedByAuthFile: Bool + do { + (webBilling, sourceLabel, authenticatedByAuthFile) = try await fetchWebBilling() + } catch GrokWebBillingError.teamUsageUnsupported { + guard let authState = authCredentials, authState.isTeamPrincipal else { + throw GrokWebBillingError.teamUsageUnsupported + } + let subscriptionTier = try await resolveSettingsTier(authState) + let identitySnapshot = GrokStatusProbe.identityOnlySnapshot( + credentials: authState, + localSummary: GrokLocalSessionScanner.summarize(env: context.env), + cliVersion: GrokStatusProbe.detectVersion(env: context.env), + subscriptionTier: subscriptionTier) + return self.makeResult( + usage: identitySnapshot.toUsageSnapshot(), + sourceLabel: "grok-web", + diagnostic: identitySnapshot.diagnostic) + } + let credentials = Self.credentialsForWebBillingSnapshot( + credentials: GrokSettingsReader.resolvedCredentials(environment: context.env), + authenticatedByAuthFile: authenticatedByAuthFile) + // Cookie/gRPC fallback is a different browser session. Never attach the + // auth.json account's settings tier onto that usage. + let subscriptionTier: String? = + if authenticatedByAuthFile { + try await resolveSettingsTier(authCredentials) + } else { + nil + } + let enrichedBilling = webBilling.applying(subscriptionTier: subscriptionTier) + let snapshot = GrokUsageSnapshot( + billing: nil, + webBilling: enrichedBilling, + credentials: GrokStatusProbe.credentialsForSnapshot( + credentials: credentials, + billing: nil, + webBilling: enrichedBilling), + localSummary: GrokLocalSessionScanner.summarize(env: context.env), + cliVersion: GrokStatusProbe.detectVersion(env: context.env), + updatedAt: Date(), + subscriptionTier: subscriptionTier ?? enrichedBilling.subscriptionTier) + return self.makeResult( + usage: snapshot.toUsageSnapshot(), + sourceLabel: sourceLabel) + } + + func fetchWebBilling( + context: ProviderFetchContext, + proxyBilling: ProxyBillingFetch = { try await GrokCreditsProxyFetcher.fetch(credentials: $0) }) async throws + -> ( + snapshot: GrokWebBillingSnapshot, + sourceLabel: String, + authenticatedByAuthFile: Bool) + { + try await self.fetchLegacyWebBilling( + context: context, + browserCredentials: nil) } - } - } - - #if os(macOS) - var cacheObservation = CookieHeaderCache.observeForConditionalMutation(provider: .grok) - if cookieSource == .auto, let cached = cacheObservation.entry { - do { - let snapshot = try await Self.fetchValidCookieHeader( - cached.cookieHeader, - credentials: browserCredentials, - preferTrailingAuthenticationFailure: true) - return (snapshot, cached.sourceLabel, false) - } catch { - guard Self.isCookieAuthenticationFailure(error) else { throw error } - if CookieHeaderCache.clearIfCurrent(provider: .grok, expected: cached) { - cacheObservation = cacheObservation.afterOwnedClear() - } - lastCookieError = error + + static func fetchProxyFirst( + credentials: GrokCredentials?, + proxyBilling: ProxyBillingFetch, + legacyBilling: () async throws -> ( + snapshot: GrokWebBillingSnapshot, + sourceLabel: String, + authenticatedByAuthFile: Bool)) async throws -> ( + snapshot: GrokWebBillingSnapshot, + sourceLabel: String, + authenticatedByAuthFile: Bool) + { + if let credentials, !credentials.isExpired { + do { + let snapshot = try await proxyBilling(credentials) + return (snapshot, "grok-cli-proxy", true) + } catch is CancellationError { + throw CancellationError() + } catch let error as URLError where error.code == .cancelled { + throw error + } catch { + // The legacy cookie and bearer paths remain available when the CLI proxy fails. + } + } + return try await legacyBilling() } - } - - if cookieSource == .auto, - Self.canImportBrowserCookies(runtime: context.runtime, env: context.env) - { - do { - let sessions = try GrokCookieImporter.importSessions( - browserDetection: context.browserDetection) - let (snapshot, sourceLabel) = try await Self.fetchFirstValidCookieSession( - sessions, - credentials: browserCredentials, - cacheObservation: cacheObservation) - return (snapshot, sourceLabel, false) - } catch { - lastCookieError = error + + private func fetchLegacyWebBilling( + context: ProviderFetchContext, + browserCredentials: GrokCredentials?) async throws -> ( + snapshot: GrokWebBillingSnapshot, + sourceLabel: String, + authenticatedByAuthFile: Bool) + { + let cookieSettings = context.settings?.grok + let cookieSource = cookieSettings?.cookieSource ?? .auto + let manualHeader = GrokCredentialRouting.normalizedWebCookie(cookieSettings?.manualCookieHeader) + var lastCookieError: Error? + + if cookieSource != .off, + let manualHeader, !manualHeader.isEmpty + { + do { + let snapshot = try await GrokWebBillingFetcher.fetch( + cookieHeader: manualHeader, + credentials: browserCredentials) + return (snapshot, "manual-cookie", false) + } catch { + lastCookieError = error + if cookieSource == .manual { + throw error + } + } + } + + #if os(macOS) + var cacheObservation = CookieHeaderCache.observeForConditionalMutation(provider: .grok) + if cookieSource == .auto, let cached = cacheObservation.entry { + do { + let snapshot = try await Self.fetchValidCookieHeader( + cached.cookieHeader, + credentials: browserCredentials, + preferTrailingAuthenticationFailure: true) + return (snapshot, cached.sourceLabel, false) + } catch { + guard Self.isCookieAuthenticationFailure(error) else { throw error } + if CookieHeaderCache.clearIfCurrent(provider: .grok, expected: cached) { + cacheObservation = cacheObservation.afterOwnedClear() + } + lastCookieError = error + } + } + + if cookieSource == .auto, + Self.canImportBrowserCookies(runtime: context.runtime, env: context.env) + { + do { + let sessions = try GrokCookieImporter.importSessions( + browserDetection: context.browserDetection) + let (snapshot, sourceLabel) = try await Self.fetchFirstValidCookieSession( + sessions, + credentials: browserCredentials, + cacheObservation: cacheObservation) + return (snapshot, sourceLabel, false) + } catch { + lastCookieError = error + } + throw lastCookieError ?? GrokWebBillingError.missingCredentials + } + #endif + + throw lastCookieError ?? GrokWebBillingError.missingCredentials } - throw lastCookieError ?? GrokWebBillingError.missingCredentials - } - #endif - - throw lastCookieError ?? GrokWebBillingError.missingCredentials - } - - static func resolvedCredentialsResult(context: ProviderFetchContext) -> Result< - GrokCredentials, Error, - > { - let fileResult: Result = Result { - try GrokCredentialsStore.load(env: context.env) - } - if let file = try? fileResult.get(), !file.isExpired { - return .success(file) - } - if let pasted = GrokSettingsReader.pastedCredentials(environment: context.env) { - return .success(pasted) - } - return fileResult - } - - static func credentialsForWebBillingSnapshot( - credentials: GrokCredentials?, - authenticatedByAuthFile: Bool - ) -> GrokCredentials? { - authenticatedByAuthFile ? credentials : nil - } - - #if os(macOS) - static func fetchFirstValidCookieSession( - _ sessions: [GrokCookieImporter.SessionInfo], - credentials: GrokCredentials? = nil, - cacheObservation: CookieHeaderCache.ConditionalMutationObservation? = nil, - fetch: ((String, GrokCredentials?) async throws -> GrokWebBillingSnapshot)? = nil - ) async throws - -> (GrokWebBillingSnapshot, String) - { - let fetchSnapshot = - fetch ?? { cookieHeader, credentials in - try await GrokWebBillingFetcher.fetch( - cookieHeader: cookieHeader, - credentials: credentials) + + static func resolvedCredentialsResult(context: ProviderFetchContext) -> Result< + GrokCredentials, Error, + > { + let fileResult: Result = Result { + try GrokCredentialsStore.load(env: context.env) + } + if let file = try? fileResult.get(), !file.isExpired { + return .success(file) + } + if let pasted = GrokSettingsReader.pastedCredentials(environment: context.env) { + return .success(pasted) + } + return fileResult } - var lastError: Error? - var teamUsageUnsupportedError: Error? - for session in sessions { - do { - let snapshot = try await Self.fetchValidCookieHeader( - session.cookieHeader, - credentials: credentials, - fetch: fetchSnapshot) - if let cacheObservation { - CookieHeaderCache.storeIfObservationCurrent( - provider: .grok, - expected: cacheObservation, - cookieHeader: session.cookieHeader, - sourceLabel: session.sourceLabel) - } - return (snapshot, session.sourceLabel) - } catch { - if case GrokWebBillingError.teamUsageUnsupported = error { - teamUsageUnsupportedError = error - } - lastError = error + + static func credentialsForWebBillingSnapshot( + credentials: GrokCredentials?, + authenticatedByAuthFile: Bool) -> GrokCredentials? + { + authenticatedByAuthFile ? credentials : nil } - } - throw teamUsageUnsupportedError ?? lastError ?? GrokWebBillingError.missingCredentials - } - - /// `preferTrailingAuthenticationFailure` lets a cached-cookie caller surface a trailing - /// 401/403 over the team classification so stale sessions still trigger cache eviction. - /// Non-authentication trailing errors keep `teamUsageUnsupported` so team principals - /// degrade to identity-only data instead of failing outright. - static func fetchValidCookieHeader( - _ cookieHeader: String, - credentials: GrokCredentials? = nil, - preferTrailingAuthenticationFailure: Bool = false, - fetch: ((String, GrokCredentials?) async throws -> GrokWebBillingSnapshot)? = nil - ) async throws - -> GrokWebBillingSnapshot - { - let fetchSnapshot = - fetch ?? { cookieHeader, credentials in - try await GrokWebBillingFetcher.fetch( - cookieHeader: cookieHeader, - credentials: credentials) + + #if os(macOS) + static func fetchFirstValidCookieSession( + _ sessions: [GrokCookieImporter.SessionInfo], + credentials: GrokCredentials? = nil, + cacheObservation: CookieHeaderCache.ConditionalMutationObservation? = nil, + fetch: ((String, GrokCredentials?) async throws -> GrokWebBillingSnapshot)? = nil) async throws + -> (GrokWebBillingSnapshot, String) + { + let fetchSnapshot = + fetch ?? { cookieHeader, credentials in + try await GrokWebBillingFetcher.fetch( + cookieHeader: cookieHeader, + credentials: credentials) + } + var lastError: Error? + var teamUsageUnsupportedError: Error? + for session in sessions { + do { + let snapshot = try await Self.fetchValidCookieHeader( + session.cookieHeader, + credentials: credentials, + fetch: fetchSnapshot) + if let cacheObservation { + CookieHeaderCache.storeIfObservationCurrent( + provider: .grok, + expected: cacheObservation, + cookieHeader: session.cookieHeader, + sourceLabel: session.sourceLabel) + } + return (snapshot, session.sourceLabel) + } catch { + if case GrokWebBillingError.teamUsageUnsupported = error { + teamUsageUnsupportedError = error + } + lastError = error + } + } + throw teamUsageUnsupportedError ?? lastError ?? GrokWebBillingError.missingCredentials } - var lastError: Error? - var teamUsageUnsupportedError: Error? - for authCredentials in Self.cookieAuthAttempts(credentials: credentials) { - do { - return try await fetchSnapshot(cookieHeader, authCredentials) - } catch { - if case GrokWebBillingError.teamUsageUnsupported = error { - teamUsageUnsupportedError = error - } - lastError = error + + /// `preferTrailingAuthenticationFailure` lets a cached-cookie caller surface a trailing + /// 401/403 over the team classification so stale sessions still trigger cache eviction. + /// Non-authentication trailing errors keep `teamUsageUnsupported` so team principals + /// degrade to identity-only data instead of failing outright. + static func fetchValidCookieHeader( + _ cookieHeader: String, + credentials: GrokCredentials? = nil, + preferTrailingAuthenticationFailure: Bool = false, + fetch: ((String, GrokCredentials?) async throws -> GrokWebBillingSnapshot)? = nil) async throws + -> GrokWebBillingSnapshot + { + let fetchSnapshot = + fetch ?? { cookieHeader, credentials in + try await GrokWebBillingFetcher.fetch( + cookieHeader: cookieHeader, + credentials: credentials) + } + var lastError: Error? + var teamUsageUnsupportedError: Error? + for authCredentials in Self.cookieAuthAttempts(credentials: credentials) { + do { + return try await fetchSnapshot(cookieHeader, authCredentials) + } catch { + if case GrokWebBillingError.teamUsageUnsupported = error { + teamUsageUnsupportedError = error + } + lastError = error + } + } + if let teamUsageUnsupportedError { + let trailingAuthenticationFailure = + preferTrailingAuthenticationFailure + && lastError.map(Self.isCookieAuthenticationFailure) == true + if !trailingAuthenticationFailure { + throw teamUsageUnsupportedError + } + } + throw lastError ?? GrokWebBillingError.missingCredentials } - } - if let teamUsageUnsupportedError { - let trailingAuthenticationFailure = - preferTrailingAuthenticationFailure - && lastError.map(Self.isCookieAuthenticationFailure) == true - if !trailingAuthenticationFailure { - throw teamUsageUnsupportedError + + static func cookieAuthAttempts(credentials: GrokCredentials?) -> [GrokCredentials?] { + guard let credentials, !credentials.isExpired else { return [nil] } + return [credentials, nil] + } + + static func isCookieAuthenticationFailure(_ 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, message): + return GrokWebBillingError.isAuthenticationFailure(status: status, message: message) + case .missingCredentials, .emptyResponse, .invalidResponse, .teamUsageUnsupported, .parseFailed: + return false + } + } + #endif + + func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool { + false } - } - throw lastError ?? GrokWebBillingError.missingCredentials - } - - static func cookieAuthAttempts(credentials: GrokCredentials?) -> [GrokCredentials?] { - guard let credentials, !credentials.isExpired else { return [nil] } - return [credentials, nil] - } - - static func isCookieAuthenticationFailure(_ error: Error) -> Bool { - guard let error = error as? GrokWebBillingError else { return false } - switch error { - case .requestFailed(let status, _): - return status == 401 || status == 403 - case .rpcFailed(let status, let message): - return GrokWebBillingError.isAuthenticationFailure(status: status, message: message) - case .missingCredentials, .emptyResponse, .invalidResponse, .teamUsageUnsupported, .parseFailed: - return false - } - } - #endif - - func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool { - false - } } From 6982cfa00d10e045f9d7860e5966ba04605ee978 Mon Sep 17 00:00:00 2001 From: "o.akimov" Date: Mon, 17 Aug 2026 12:14:22 +0200 Subject: [PATCH 04/11] Honor explicit Grok source and selected bearer Keep Auto-only remapping for selected SuperGrok accounts so CLI/web stay authoritative. Prefer GROK_OAUTH_TOKEN over a valid auth.json so a selected pasted bearer cannot display the local grok login account. --- .../Grok/GrokProviderDescriptor.swift | 14 ++++------- .../Providers/Grok/GrokSettingsReader.swift | 5 +++- .../GrokSettingsReaderTests.swift | 24 +++++++++++++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift index 7876abe10d..bef9e22a47 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift @@ -18,7 +18,7 @@ public enum GrokProviderDescriptor { return [GrokSettingsReader.oauthTokenEnvironmentKey: oauth] }), selectedAccountSourceModeResolver: { base, account, _ in - guard let account else { return base } + guard base == .auto, let account else { return base } return GrokCredentialRouting.resolve( tokenAccountToken: account.token, manualCookieHeader: nil).sourceMode ?? base @@ -446,16 +446,12 @@ struct GrokWebFetchStrategy: ProviderFetchStrategy { static func resolvedCredentialsResult(context: ProviderFetchContext) -> Result< GrokCredentials, Error, > { - let fileResult: Result = Result { - try GrokCredentialsStore.load(env: context.env) - } - if let file = try? fileResult.get(), !file.isExpired { - return .success(file) + if let credentials = GrokSettingsReader.resolvedCredentials(environment: context.env) { + return .success(credentials) } - if let pasted = GrokSettingsReader.pastedCredentials(environment: context.env) { - return .success(pasted) + return Result { + try GrokCredentialsStore.load(env: context.env) } - return fileResult } static func credentialsForWebBillingSnapshot( diff --git a/Sources/CodexBarCore/Providers/Grok/GrokSettingsReader.swift b/Sources/CodexBarCore/Providers/Grok/GrokSettingsReader.swift index 94ca5f69dc..817dd91577 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokSettingsReader.swift @@ -24,10 +24,13 @@ public enum GrokSettingsReader { environment: [String: String], settings: ProviderSettingsSnapshot? = nil) -> GrokCredentials? { + if let pasted = self.pastedCredentials(environment: environment, settings: settings) { + return pasted + } if let file = try? GrokCredentialsStore.load(env: environment), !file.isExpired { return file } - return self.pastedCredentials(environment: environment, settings: settings) + return nil } public static func normalizedOAuthToken(_ raw: String?) -> String? { diff --git a/Tests/CodexBarTests/GrokSettingsReaderTests.swift b/Tests/CodexBarTests/GrokSettingsReaderTests.swift index 9b736ce7e9..10c10ff65f 100644 --- a/Tests/CodexBarTests/GrokSettingsReaderTests.swift +++ b/Tests/CodexBarTests/GrokSettingsReaderTests.swift @@ -104,7 +104,31 @@ struct GrokSettingsReaderTests { adapter?.selectedAccountSourceMode(base: .auto, account: cookieAccount, config: nil) == .web) #expect(adapter?.selectedAccountSourceMode(base: .auto, account: nil, config: nil) == .auto) + #expect(adapter?.selectedAccountSourceMode(base: .cli, account: oauthAccount, config: nil) == .cli) + #expect(adapter?.selectedAccountSourceMode(base: .web, account: oauthAccount, config: nil) == .web) #expect( GrokProviderDescriptor.descriptor.fetchPlan.sourceModes == [.auto, .cli, .oauth, .web]) } + + @Test + func `selected pasted SuperGrok token wins over a valid auth json file`() throws { + let home = FileManager.default.temporaryDirectory + .appendingPathComponent("CodexBar-GrokSelectedBearer-\(UUID().uuidString)", isDirectory: true) + try FileManager.default.createDirectory(at: home, withIntermediateDirectories: true) + defer { try? FileManager.default.removeItem(at: home) } + try Data(""" + { + "https://auth.x.ai::client": { + "key": "file-token", + "auth_mode": "oidc" + } + } + """.utf8).write(to: home.appendingPathComponent("auth.json")) + + var env = [GrokSettingsReader.oauthTokenEnvironmentKey: "pasted-token"] + env["GROK_HOME"] = home.path + #expect(GrokSettingsReader.resolvedCredentials(environment: env)?.accessToken == "pasted-token") + env.removeValue(forKey: GrokSettingsReader.oauthTokenEnvironmentKey) + #expect(GrokSettingsReader.resolvedCredentials(environment: env)?.accessToken == "file-token") + } } From 588595e69f402dc7364191c739abe504929afe95 Mon Sep 17 00:00:00 2001 From: "o.akimov" Date: Mon, 17 Aug 2026 12:22:38 +0200 Subject: [PATCH 05/11] Route selected Grok cookie accounts into settings Project Cookie: token accounts into the Grok settings snapshot so Auto and Web fetches use the selected header instead of a configured or browser cookie. --- .../Providers/Grok/GrokSettingsStore.swift | 10 ++++++-- .../Grok/GrokCredentialRouting.swift | 16 +++++++++++++ .../Grok/GrokProviderDescriptor.swift | 11 +++++---- .../GrokSettingsReaderTests.swift | 24 +++++++++++++++++++ 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/Sources/CodexBar/Providers/Grok/GrokSettingsStore.swift b/Sources/CodexBar/Providers/Grok/GrokSettingsStore.swift index c9abf30d67..3902c06d13 100644 --- a/Sources/CodexBar/Providers/Grok/GrokSettingsStore.swift +++ b/Sources/CodexBar/Providers/Grok/GrokSettingsStore.swift @@ -41,10 +41,16 @@ extension SettingsStore { -> ProviderSettingsSnapshot .GrokProviderSettings { - self.resolvedCookieSettings( + let account = ProviderTokenAccountSelection.selectedAccount( provider: .grok, + settings: self, + override: tokenOverride) + let resolved = GrokCredentialRouting.cookieSettings( configuredSource: self.grokCookieSource, configuredHeader: self.grokCookieHeader, - tokenOverride: tokenOverride) + selectedAccountToken: account?.token) + return GrokProviderSettings( + cookieSource: resolved.cookieSource, + manualCookieHeader: resolved.manualCookieHeader) } } diff --git a/Sources/CodexBarCore/Providers/Grok/GrokCredentialRouting.swift b/Sources/CodexBarCore/Providers/Grok/GrokCredentialRouting.swift index dc1eb312d3..f8de8459f4 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokCredentialRouting.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokCredentialRouting.swift @@ -33,6 +33,22 @@ public enum GrokCredentialRouting: Sendable, Equatable { } } + public static func cookieSettings( + configuredSource: ProviderCookieSource, + configuredHeader: String?, + selectedAccountToken: String?) -> CookieProviderSettings + { + if let header = self.resolve( + tokenAccountToken: selectedAccountToken, + manualCookieHeader: nil).manualCookieHeader + { + return CookieProviderSettings(cookieSource: .manual, manualCookieHeader: header) + } + return CookieProviderSettings( + cookieSource: configuredSource, + manualCookieHeader: configuredHeader) + } + private static func resolvePrimaryCredential(_ raw: String) -> Self? { if let accessToken = self.normalizedOAuthToken(raw) { return .oauth(accessToken: accessToken) diff --git a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift index bef9e22a47..ba13621c14 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift @@ -45,12 +45,13 @@ public enum GrokProviderDescriptor { }, credentialSettings: { context in let cookies = context.cookieSettings(for: .grok) - let routing = GrokCredentialRouting.resolve( - tokenAccountToken: context.account?.token, - manualCookieHeader: context.account == nil ? cookies.manualCookieHeader : nil) + let resolved = GrokCredentialRouting.cookieSettings( + configuredSource: cookies.cookieSource, + configuredHeader: cookies.manualCookieHeader, + selectedAccountToken: context.account?.token) return GrokProviderSettings( - cookieSource: routing.manualCookieHeader != nil ? .manual : cookies.cookieSource, - manualCookieHeader: routing.manualCookieHeader ?? cookies.manualCookieHeader) + cookieSource: resolved.cookieSource, + manualCookieHeader: resolved.manualCookieHeader) }), credentials: self.credentials, metadata: ProviderMetadata( diff --git a/Tests/CodexBarTests/GrokSettingsReaderTests.swift b/Tests/CodexBarTests/GrokSettingsReaderTests.swift index 10c10ff65f..75a0aa9ba4 100644 --- a/Tests/CodexBarTests/GrokSettingsReaderTests.swift +++ b/Tests/CodexBarTests/GrokSettingsReaderTests.swift @@ -131,4 +131,28 @@ struct GrokSettingsReaderTests { env.removeValue(forKey: GrokSettingsReader.oauthTokenEnvironmentKey) #expect(GrokSettingsReader.resolvedCredentials(environment: env)?.accessToken == "file-token") } + + @Test + func `selected cookie account overrides the configured Grok cookie header`() { + let selected = GrokCredentialRouting.cookieSettings( + configuredSource: .auto, + configuredHeader: "sso=configured", + selectedAccountToken: "Cookie: sso=selected") + #expect(selected.cookieSource == .manual) + #expect(selected.manualCookieHeader == "sso=selected") + + let bearerKeepsConfigured = GrokCredentialRouting.cookieSettings( + configuredSource: .auto, + configuredHeader: "sso=configured", + selectedAccountToken: "pasted-token") + #expect(bearerKeepsConfigured.cookieSource == .auto) + #expect(bearerKeepsConfigured.manualCookieHeader == "sso=configured") + + let none = GrokCredentialRouting.cookieSettings( + configuredSource: .auto, + configuredHeader: "sso=configured", + selectedAccountToken: nil) + #expect(none.cookieSource == .auto) + #expect(none.manualCookieHeader == "sso=configured") + } } From 44bf7cf7e7833e0d79da296b9e0e0b275120ebe1 Mon Sep 17 00:00:00 2001 From: "o.akimov" Date: Mon, 17 Aug 2026 12:30:14 +0200 Subject: [PATCH 06/11] Keep Auto cookies before SuperGrok OAuth Preserve the existing Grok Auto fallback: CLI, then browser cookies, then OAuth. Selected pasted bearers and explicit SuperGrok OAuth still use the OAuth path only. --- .../Grok/GrokProviderDescriptor.swift | 2 +- .../GrokSettingsReaderTests.swift | 20 +++++++++++++++++++ docs/grok.md | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift index ba13621c14..000838b426 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift @@ -119,7 +119,7 @@ public enum GrokProviderDescriptor { { switch context.sourceMode { case .auto: - [GrokCLIFetchStrategy(), GrokOAuthFetchStrategy(), GrokWebFetchStrategy()] + [GrokCLIFetchStrategy(), GrokWebFetchStrategy(), GrokOAuthFetchStrategy()] case .cli: [GrokCLIFetchStrategy()] case .oauth: diff --git a/Tests/CodexBarTests/GrokSettingsReaderTests.swift b/Tests/CodexBarTests/GrokSettingsReaderTests.swift index 75a0aa9ba4..d8bdb5ff82 100644 --- a/Tests/CodexBarTests/GrokSettingsReaderTests.swift +++ b/Tests/CodexBarTests/GrokSettingsReaderTests.swift @@ -155,4 +155,24 @@ struct GrokSettingsReaderTests { #expect(none.cookieSource == .auto) #expect(none.manualCookieHeader == "sso=configured") } + + @Test + func `auto keeps cookies before SuperGrok OAuth`() async { + let browserDetection = BrowserDetection(cacheTTL: 0) + let context = ProviderFetchContext( + runtime: .app, + sourceMode: .auto, + includeCredits: false, + webTimeout: 1, + webDebugDumpHTML: false, + verbose: false, + env: [:], + settings: nil, + fetcher: UsageFetcher(), + claudeFetcher: ClaudeUsageFetcher(browserDetection: browserDetection), + browserDetection: browserDetection) + let strategies = await GrokProviderDescriptor.descriptor.fetchPlan.pipeline + .resolveStrategies(context) + #expect(strategies.map(\.id) == ["grok.cli", "grok.web", "grok.oauth"]) + } } diff --git a/docs/grok.md b/docs/grok.md index 171121888a..7485f453fe 100644 --- a/docs/grok.md +++ b/docs/grok.md @@ -15,7 +15,7 @@ The grok.com billing gRPC-web endpoint remains a best-effort fallback. ## Settings source picker -- **Auto**: Grok CLI, then SuperGrok OAuth, then browser cookies. +- **Auto**: Grok CLI, then browser cookies, then SuperGrok OAuth. - **Grok CLI**: `grok agent stdio` only. - **SuperGrok OAuth**: `~/.grok/auth.json` or a pasted bearer / `GROK_OAUTH_TOKEN`. CLI-proxy credits, then bearer gRPC. No cookies. - **Browser cookies**: grok.com Cookie header / Chrome import only. No OAuth bearer. From a8a001c8b84fc765ab26a53315227954153abf24 Mon Sep 17 00:00:00 2001 From: "o.akimov" Date: Mon, 17 Aug 2026 12:35:39 +0200 Subject: [PATCH 07/11] Align Auto picker subtitle with fallback order --- .../Providers/Grok/GrokProviderImplementation.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift b/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift index c6d337d9f4..1b9275a9be 100644 --- a/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift +++ b/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift @@ -73,7 +73,7 @@ struct GrokProviderImplementation: ProviderImplementation { ProviderSettingsPickerDescriptor( id: "grok-usage-source", title: "Usage source", - subtitle: "Auto tries the Grok CLI, SuperGrok OAuth, then browser cookies.", + subtitle: "Auto tries the Grok CLI, browser cookies, then SuperGrok OAuth.", binding: sourceBinding, options: sourceOptions, isVisible: nil, @@ -113,14 +113,14 @@ struct GrokProviderImplementation: ProviderImplementation { if let url = URL(string: "https://grok.com/?_s=usage") { NSWorkspace.shared.open(url) } - }), + }) ], isVisible: { (context.settings.grokUsageDataSource == .auto || context.settings.grokUsageDataSource == .web) && context.settings.grokCookieSource == .manual }, - onActivate: { context.settings.ensureGrokCookieLoaded() }), + onActivate: { context.settings.ensureGrokCookieLoaded() }) ] } } From ebc643a363c3106048b718daba4e550d7c92e78a Mon Sep 17 00:00:00 2001 From: "o.akimov" Date: Mon, 17 Aug 2026 12:43:44 +0200 Subject: [PATCH 08/11] Match main Auto: auth-file proxy before cookies Auto is CLI, then SuperGrok OAuth CLI-proxy, then browser cookies. A failed Auto proxy no longer falls through to bearer gRPC, so cookies still run next. Explicit SuperGrok OAuth keeps proxy-then-gRPC. --- .../Grok/GrokProviderImplementation.swift | 6 +-- .../Grok/GrokProviderDescriptor.swift | 4 +- .../GrokSettingsReaderTests.swift | 46 ++++++++++--------- docs/grok.md | 2 +- 4 files changed, 32 insertions(+), 26 deletions(-) diff --git a/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift b/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift index 1b9275a9be..c6d337d9f4 100644 --- a/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift +++ b/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift @@ -73,7 +73,7 @@ struct GrokProviderImplementation: ProviderImplementation { ProviderSettingsPickerDescriptor( id: "grok-usage-source", title: "Usage source", - subtitle: "Auto tries the Grok CLI, browser cookies, then SuperGrok OAuth.", + subtitle: "Auto tries the Grok CLI, SuperGrok OAuth, then browser cookies.", binding: sourceBinding, options: sourceOptions, isVisible: nil, @@ -113,14 +113,14 @@ struct GrokProviderImplementation: ProviderImplementation { if let url = URL(string: "https://grok.com/?_s=usage") { NSWorkspace.shared.open(url) } - }) + }), ], isVisible: { (context.settings.grokUsageDataSource == .auto || context.settings.grokUsageDataSource == .web) && context.settings.grokCookieSource == .manual }, - onActivate: { context.settings.ensureGrokCookieLoaded() }) + onActivate: { context.settings.ensureGrokCookieLoaded() }), ] } } diff --git a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift index 000838b426..f3e7522f53 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift @@ -119,7 +119,7 @@ public enum GrokProviderDescriptor { { switch context.sourceMode { case .auto: - [GrokCLIFetchStrategy(), GrokWebFetchStrategy(), GrokOAuthFetchStrategy()] + [GrokCLIFetchStrategy(), GrokOAuthFetchStrategy(), GrokWebFetchStrategy()] case .cli: [GrokCLIFetchStrategy()] case .oauth: @@ -216,6 +216,8 @@ struct GrokOAuthFetchStrategy: ProviderFetchStrategy { } catch let error as URLError where error.code == .cancelled { throw error } catch { + // Auto must reach browser cookies before bearer gRPC, matching main. + guard context.sourceMode != .auto else { throw error } let snapshot = try await GrokWebBillingFetcher.fetch(credentials: credentials) return (snapshot, "grok-web", true) } diff --git a/Tests/CodexBarTests/GrokSettingsReaderTests.swift b/Tests/CodexBarTests/GrokSettingsReaderTests.swift index d8bdb5ff82..4c24364921 100644 --- a/Tests/CodexBarTests/GrokSettingsReaderTests.swift +++ b/Tests/CodexBarTests/GrokSettingsReaderTests.swift @@ -28,15 +28,16 @@ struct GrokSettingsReaderTests { .appendingPathComponent("CodexBar-GrokExpiredAuth-\(UUID().uuidString)", isDirectory: true) try FileManager.default.createDirectory(at: home, withIntermediateDirectories: true) defer { try? FileManager.default.removeItem(at: home) } - try Data(""" - { - "https://auth.x.ai::client": { - "key": "stale-file-token", - "auth_mode": "oidc", - "expires_at": "2020-01-01T00:00:00Z" - } - } - """.utf8).write(to: home.appendingPathComponent("auth.json")) + try Data( + """ + { + "https://auth.x.ai::client": { + "key": "stale-file-token", + "auth_mode": "oidc", + "expires_at": "2020-01-01T00:00:00Z" + } + } + """.utf8).write(to: home.appendingPathComponent("auth.json")) var env = [GrokSettingsReader.oauthTokenEnvironmentKey: "pasted-token"] env["GROK_HOME"] = home.path @@ -104,8 +105,10 @@ struct GrokSettingsReaderTests { adapter?.selectedAccountSourceMode(base: .auto, account: cookieAccount, config: nil) == .web) #expect(adapter?.selectedAccountSourceMode(base: .auto, account: nil, config: nil) == .auto) - #expect(adapter?.selectedAccountSourceMode(base: .cli, account: oauthAccount, config: nil) == .cli) - #expect(adapter?.selectedAccountSourceMode(base: .web, account: oauthAccount, config: nil) == .web) + #expect( + adapter?.selectedAccountSourceMode(base: .cli, account: oauthAccount, config: nil) == .cli) + #expect( + adapter?.selectedAccountSourceMode(base: .web, account: oauthAccount, config: nil) == .web) #expect( GrokProviderDescriptor.descriptor.fetchPlan.sourceModes == [.auto, .cli, .oauth, .web]) } @@ -116,14 +119,15 @@ struct GrokSettingsReaderTests { .appendingPathComponent("CodexBar-GrokSelectedBearer-\(UUID().uuidString)", isDirectory: true) try FileManager.default.createDirectory(at: home, withIntermediateDirectories: true) defer { try? FileManager.default.removeItem(at: home) } - try Data(""" - { - "https://auth.x.ai::client": { - "key": "file-token", - "auth_mode": "oidc" - } - } - """.utf8).write(to: home.appendingPathComponent("auth.json")) + try Data( + """ + { + "https://auth.x.ai::client": { + "key": "file-token", + "auth_mode": "oidc" + } + } + """.utf8).write(to: home.appendingPathComponent("auth.json")) var env = [GrokSettingsReader.oauthTokenEnvironmentKey: "pasted-token"] env["GROK_HOME"] = home.path @@ -157,7 +161,7 @@ struct GrokSettingsReaderTests { } @Test - func `auto keeps cookies before SuperGrok OAuth`() async { + func `auto tries SuperGrok OAuth before cookies and after the CLI`() async { let browserDetection = BrowserDetection(cacheTTL: 0) let context = ProviderFetchContext( runtime: .app, @@ -173,6 +177,6 @@ struct GrokSettingsReaderTests { browserDetection: browserDetection) let strategies = await GrokProviderDescriptor.descriptor.fetchPlan.pipeline .resolveStrategies(context) - #expect(strategies.map(\.id) == ["grok.cli", "grok.web", "grok.oauth"]) + #expect(strategies.map(\.id) == ["grok.cli", "grok.oauth", "grok.web"]) } } diff --git a/docs/grok.md b/docs/grok.md index 7485f453fe..288e232dee 100644 --- a/docs/grok.md +++ b/docs/grok.md @@ -15,7 +15,7 @@ The grok.com billing gRPC-web endpoint remains a best-effort fallback. ## Settings source picker -- **Auto**: Grok CLI, then browser cookies, then SuperGrok OAuth. +- **Auto**: Grok CLI, then SuperGrok OAuth (`auth.json` / CLI-proxy), then browser cookies. - **Grok CLI**: `grok agent stdio` only. - **SuperGrok OAuth**: `~/.grok/auth.json` or a pasted bearer / `GROK_OAUTH_TOKEN`. CLI-proxy credits, then bearer gRPC. No cookies. - **Browser cookies**: grok.com Cookie header / Chrome import only. No OAuth bearer. From f7f1d3afb64ddebcd688dd7168225a3f64ae77a0 Mon Sep 17 00:00:00 2001 From: "o.akimov" Date: Mon, 17 Aug 2026 12:56:34 +0200 Subject: [PATCH 09/11] Keep Auto bearer gRPC after cookie fallback Auto is now CLI, SuperGrok OAuth CLI-proxy, browser cookies, then bearer gRPC. Explicit SuperGrok OAuth stays cookie-free proxy-then-gRPC. --- .../Grok/GrokProviderImplementation.swift | 3 +- .../Grok/GrokProviderDescriptor.swift | 53 ++++++++++++++----- .../GrokSettingsReaderTests.swift | 2 +- docs/grok.md | 2 +- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift b/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift index c6d337d9f4..89163fe294 100644 --- a/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift +++ b/Sources/CodexBar/Providers/Grok/GrokProviderImplementation.swift @@ -73,7 +73,8 @@ struct GrokProviderImplementation: ProviderImplementation { ProviderSettingsPickerDescriptor( id: "grok-usage-source", title: "Usage source", - subtitle: "Auto tries the Grok CLI, SuperGrok OAuth, then browser cookies.", + subtitle: + "Auto tries the Grok CLI, SuperGrok OAuth, browser cookies, then bearer gRPC.", binding: sourceBinding, options: sourceOptions, isVisible: nil, diff --git a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift index f3e7522f53..e140519e4c 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift @@ -119,7 +119,12 @@ public enum GrokProviderDescriptor { { switch context.sourceMode { case .auto: - [GrokCLIFetchStrategy(), GrokOAuthFetchStrategy(), GrokWebFetchStrategy()] + [ + GrokCLIFetchStrategy(), + GrokOAuthFetchStrategy(mode: .proxy), + GrokWebFetchStrategy(), + GrokOAuthFetchStrategy(mode: .grpc), + ] case .cli: [GrokCLIFetchStrategy()] case .oauth: @@ -193,9 +198,26 @@ struct GrokCLIFetchStrategy: ProviderFetchStrategy { } struct GrokOAuthFetchStrategy: ProviderFetchStrategy { - let id: String = "grok.oauth" + enum Mode: Sendable { + case proxyThenGrpc + case proxy + case grpc + } + + let mode: Mode let kind: ProviderFetchKind = .oauth + init(mode: Mode = .proxyThenGrpc) { + self.mode = mode + } + + var id: String { + switch self.mode { + case .proxyThenGrpc, .proxy: "grok.oauth" + case .grpc: "grok.oauth-grpc" + } + } + func isAvailable(_ context: ProviderFetchContext) async -> Bool { GrokSettingsReader.resolvedCredentials(environment: context.env) != nil || FileManager.default.fileExists( @@ -208,18 +230,25 @@ struct GrokOAuthFetchStrategy: ProviderFetchStrategy { guard !credentials.isExpired else { throw GrokWebBillingError.missingCredentials } - do { - let snapshot = try await GrokCreditsProxyFetcher.fetch(credentials: credentials) - return (snapshot, "grok-cli-proxy", true) - } catch is CancellationError { - throw CancellationError() - } catch let error as URLError where error.code == .cancelled { - throw error - } catch { - // Auto must reach browser cookies before bearer gRPC, matching main. - guard context.sourceMode != .auto else { throw error } + switch self.mode { + case .grpc: let snapshot = try await GrokWebBillingFetcher.fetch(credentials: credentials) return (snapshot, "grok-web", true) + case .proxy: + let snapshot = try await GrokCreditsProxyFetcher.fetch(credentials: credentials) + return (snapshot, "grok-cli-proxy", true) + case .proxyThenGrpc: + do { + let snapshot = try await GrokCreditsProxyFetcher.fetch(credentials: credentials) + return (snapshot, "grok-cli-proxy", true) + } catch is CancellationError { + throw CancellationError() + } catch let error as URLError where error.code == .cancelled { + throw error + } catch { + let snapshot = try await GrokWebBillingFetcher.fetch(credentials: credentials) + return (snapshot, "grok-web", true) + } } } } diff --git a/Tests/CodexBarTests/GrokSettingsReaderTests.swift b/Tests/CodexBarTests/GrokSettingsReaderTests.swift index 4c24364921..f27aa39bf7 100644 --- a/Tests/CodexBarTests/GrokSettingsReaderTests.swift +++ b/Tests/CodexBarTests/GrokSettingsReaderTests.swift @@ -177,6 +177,6 @@ struct GrokSettingsReaderTests { browserDetection: browserDetection) let strategies = await GrokProviderDescriptor.descriptor.fetchPlan.pipeline .resolveStrategies(context) - #expect(strategies.map(\.id) == ["grok.cli", "grok.oauth", "grok.web"]) + #expect(strategies.map(\.id) == ["grok.cli", "grok.oauth", "grok.web", "grok.oauth-grpc"]) } } diff --git a/docs/grok.md b/docs/grok.md index 288e232dee..f26f710705 100644 --- a/docs/grok.md +++ b/docs/grok.md @@ -15,7 +15,7 @@ The grok.com billing gRPC-web endpoint remains a best-effort fallback. ## Settings source picker -- **Auto**: Grok CLI, then SuperGrok OAuth (`auth.json` / CLI-proxy), then browser cookies. +- **Auto**: Grok CLI, then SuperGrok OAuth CLI-proxy, then browser cookies, then bearer gRPC. - **Grok CLI**: `grok agent stdio` only. - **SuperGrok OAuth**: `~/.grok/auth.json` or a pasted bearer / `GROK_OAUTH_TOKEN`. CLI-proxy credits, then bearer gRPC. No cookies. - **Browser cookies**: grok.com Cookie header / Chrome import only. No OAuth bearer. From 3c9dbee06dc59fd463f1d40827bfbe9626220a97 Mon Sep 17 00:00:00 2001 From: "o.akimov" Date: Mon, 17 Aug 2026 13:03:33 +0200 Subject: [PATCH 10/11] Continue Auto after cookie failure to bearer gRPC --- .../Grok/GrokProviderDescriptor.swift | 4 +- .../GrokSettingsReaderTests.swift | 38 ++++++++++++------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift index e140519e4c..7596107917 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift @@ -591,7 +591,7 @@ struct GrokWebFetchStrategy: ProviderFetchStrategy { } #endif - func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool { - false + func shouldFallback(on _: Error, context: ProviderFetchContext) -> Bool { + context.sourceMode == .auto } } diff --git a/Tests/CodexBarTests/GrokSettingsReaderTests.swift b/Tests/CodexBarTests/GrokSettingsReaderTests.swift index f27aa39bf7..1f967b86b0 100644 --- a/Tests/CodexBarTests/GrokSettingsReaderTests.swift +++ b/Tests/CodexBarTests/GrokSettingsReaderTests.swift @@ -163,20 +163,32 @@ struct GrokSettingsReaderTests { @Test func `auto tries SuperGrok OAuth before cookies and after the CLI`() async { let browserDetection = BrowserDetection(cacheTTL: 0) - let context = ProviderFetchContext( - runtime: .app, - sourceMode: .auto, - includeCredits: false, - webTimeout: 1, - webDebugDumpHTML: false, - verbose: false, - env: [:], - settings: nil, - fetcher: UsageFetcher(), - claudeFetcher: ClaudeUsageFetcher(browserDetection: browserDetection), - browserDetection: browserDetection) + func makeContext(sourceMode: ProviderSourceMode) -> ProviderFetchContext { + ProviderFetchContext( + runtime: .app, + sourceMode: sourceMode, + includeCredits: false, + webTimeout: 1, + webDebugDumpHTML: false, + verbose: false, + env: [:], + settings: nil, + fetcher: UsageFetcher(), + claudeFetcher: ClaudeUsageFetcher(browserDetection: browserDetection), + browserDetection: browserDetection) + } + let auto = makeContext(sourceMode: .auto) + let web = makeContext(sourceMode: .web) let strategies = await GrokProviderDescriptor.descriptor.fetchPlan.pipeline - .resolveStrategies(context) + .resolveStrategies(auto) #expect(strategies.map(\.id) == ["grok.cli", "grok.oauth", "grok.web", "grok.oauth-grpc"]) + #expect( + GrokWebFetchStrategy().shouldFallback( + on: GrokWebBillingError.missingCredentials, + context: auto)) + #expect( + !GrokWebFetchStrategy().shouldFallback( + on: GrokWebBillingError.missingCredentials, + context: web)) } } From a4f1fbf8d73a5598c6758a75422b984142372e96 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 17 Aug 2026 09:52:51 -0700 Subject: [PATCH 11/11] fix: repair Grok CI gates Co-authored-by: Oleksiy Akimov --- CHANGELOG.md | 5 +- .../Grok/GrokCredentialRouting.swift | 12 +++-- .../Providers/Grok/GrokStatusProbe.swift | 4 +- .../Grok/GrokWebBillingFetcher.swift | 16 ++++-- .../ProviderArchitectureGatekeeperTests.swift | 52 +++++++++---------- ...viderCredentialCharacterizationTests.swift | 1 + 6 files changed, 54 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaa3f7210a..ecfdbf8109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ ### Added - Usage & Spend: add an All time range alongside 7d/30d, backed by 365 days of local history with dedicated Claude and Cursor spend snapshot slots (#3009). Thanks @Yuxin-Qiao! - General settings: turn Low Power Mode into an Off/On/Automatic preference, with Automatic following the system Low Power Mode state (#2995). Thanks @elijahfriedman! +- Grok: add an Auto / Grok CLI / SuperGrok OAuth / Browser cookies source picker, support pasted SuperGrok bearers and grok.com cookies in token accounts, and open `~/.grok/auth.json` from Open token file (#3010). Thanks @oakimov! + ### Fixed - Claude spend: price bare first-party model IDs from their models.dev vendor catalog, preserve explicit routes, and leave ambiguous cross-vendor matches unpriced (#3002). Thanks @Yuxin-Qiao! - Menu: prevent the system menu highlight from painting behind provider detail cards on macOS 27 beta (#2998). Thanks @Tan1103! @@ -13,8 +15,7 @@ - Usage & Spend: keep unpriced named models visible in the model breakdown list instead of dropping the whole source (#3004). Thanks @Yuxin-Qiao! - Usage & Spend: keep priced Cursor days visible when some events omit totalCents, and stop invalid Cursor model costs from reviving on later events (#3005). Thanks @Yuxin-Qiao! - Settings: style the Quit CodexBar button as a prominent accent-color button and remove its stray section background (#2992). Thanks @elijahfriedman! -- Grok: add an Auto / Grok CLI / SuperGrok OAuth / Browser cookies source picker, and paste a SuperGrok bearer or grok.com cookie into token accounts. OAuth and cookies stay separate paths. -- Grok: Open token file opens `~/.grok/auth.json` instead of CodexBar's `config.json`. + ## 0.52.0 — 2026-08-17 ### Added diff --git a/Sources/CodexBarCore/Providers/Grok/GrokCredentialRouting.swift b/Sources/CodexBarCore/Providers/Grok/GrokCredentialRouting.swift index f8de8459f4..c08887477d 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokCredentialRouting.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokCredentialRouting.swift @@ -66,9 +66,15 @@ public enum GrokCredentialRouting: Sendable, Equatable { } guard !token.isEmpty else { return nil } let lower = token.lowercased() - if lower.hasPrefix("cookie:") { return nil } - if lower.hasPrefix("xai-") { return nil } - if token.contains("=") { return nil } + if lower.hasPrefix("cookie:") { + return nil + } + if lower.hasPrefix("xai-") { + return nil + } + if token.contains("=") { + return nil + } return token } diff --git a/Sources/CodexBarCore/Providers/Grok/GrokStatusProbe.swift b/Sources/CodexBarCore/Providers/Grok/GrokStatusProbe.swift index f1fbf51797..00542b2357 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokStatusProbe.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokStatusProbe.swift @@ -239,7 +239,9 @@ public struct GrokStatusProbe: Sendable { { // 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 } + if billing != nil || webBilling != nil { + return credentials + } return credentials.flatMap { $0.isExpired ? nil : $0 } } diff --git a/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift b/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift index 7f156bb96d..bac87741cd 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokWebBillingFetcher.swift @@ -75,7 +75,9 @@ public enum GrokWebBillingError: LocalizedError, Sendable { } static func isAuthenticationFailure(status: Int, message: String) -> Bool { - if status == 16 { return true } + if status == 16 { + return true + } guard status == 7 else { return false } let lower = message.lowercased() return lower.contains("bad-credentials") || lower.contains("unauthenticated") @@ -222,12 +224,16 @@ public enum GrokWebBillingFetcher { return urlError.code == .timedOut || urlError.code == .networkConnectionLost } if case let GrokWebBillingError.requestFailed(status, body) = error { - if [408, 502, 503, 504].contains(status) { return true } + if [408, 502, 503, 504].contains(status) { + return true + } return body.localizedCaseInsensitiveContains("timeout") || body.localizedCaseInsensitiveContains("deadline") } guard case let GrokWebBillingError.rpcFailed(status, message) = error else { return false } - if status == 4 { return true } + if status == 4 { + return true + } guard status == 1 else { return false } return message.localizedCaseInsensitiveContains("timeout") || message.localizedCaseInsensitiveContains("deadline") @@ -485,7 +491,9 @@ public enum GrokWebBillingFetcher { let byte = bytes[index] index += 1 value |= UInt64(byte & 0x7F) << shift - if byte & 0x80 == 0 { return value } + if byte & 0x80 == 0 { + return value + } shift += 7 } return nil diff --git a/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift b/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift index 9d8c9d496d..895c6aacaa 100644 --- a/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift +++ b/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift @@ -1136,61 +1136,61 @@ struct ProviderArchitectureGatekeeperTests { reason: "This Codex account projection passes its fixed provider identity to shared spend infrastructure."), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 823, + line: 851, anchor: ".descriptor(for: .codex)", expectedProviderIDs: ["codex"], reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 825, + line: 853, anchor: "provider: .codex,", expectedProviderIDs: ["codex"], reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1292, + line: 1337, anchor: "let scoped = result.usage.scoped(to: .codex)", expectedProviderIDs: ["codex"], reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1380, + line: 1421, anchor: "provider: .codex,", expectedProviderIDs: ["codex"], reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1384, + line: 1425, anchor: "provider: .codex,", expectedProviderIDs: ["codex"], reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1387, + line: 1428, anchor: "self.handlePredictivePaceWarningTransitions(provider: .codex, snapshot: snapshot)", expectedProviderIDs: ["codex"], reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1397, + line: 1438, anchor: "self.rememberLiveSystemCodexEmailIfNeeded(snapshot.accountEmail(for: .codex))", expectedProviderIDs: ["codex"], reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1400, + line: 1441, anchor: "provider: .codex,", expectedProviderIDs: ["codex"], reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1420, + line: 1461, anchor: "self.snapshots.removeValue(forKey: .codex)", expectedProviderIDs: ["codex"], reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1454, + line: 1497, anchor: "from: self.presentationSnapshot(for: .deepseek))", expectedProviderIDs: ["deepseek"], reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), @@ -2923,7 +2923,7 @@ struct ProviderArchitectureGatekeeperTests { reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 319, + line: 323, anchor: "self.snapshots[.codex] = snapshot", expectedProviderIDs: ["codex"], expectedReferenceCount: 2, @@ -2931,7 +2931,7 @@ struct ProviderArchitectureGatekeeperTests { reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 740, + line: 758, anchor: "guard provider == .codex else { return outcome }", expectedProviderIDs: ["codex"], expectedReferenceCount: 1, @@ -2939,15 +2939,15 @@ struct ProviderArchitectureGatekeeperTests { reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 822, - anchor: "let descriptor = self.providerSpecs[.codex]?.descriptor ?? ProviderDescriptorRegistry", + line: 849, + anchor: "self.providerSpecs[.codex]?.descriptor", expectedProviderIDs: ["codex"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 928, + line: 963, anchor: "let originalManualToken = provider == .stepfun ? self.settings.stepfunToken : nil", expectedProviderIDs: ["stepfun"], expectedReferenceCount: 1, @@ -2955,7 +2955,7 @@ struct ProviderArchitectureGatekeeperTests { reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 970, + line: 1006, anchor: "guard let self, provider == .stepfun,", expectedProviderIDs: ["stepfun"], expectedReferenceCount: 1, @@ -2963,7 +2963,7 @@ struct ProviderArchitectureGatekeeperTests { reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1070, + line: 1109, anchor: "guard let snapshot = self.lastKnownResetSnapshots[.codex],", expectedProviderIDs: ["codex"], expectedReferenceCount: 1, @@ -2971,7 +2971,7 @@ struct ProviderArchitectureGatekeeperTests { reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1086, + line: 1127, anchor: "return self.lastKnownResetSnapshots[.codex]", expectedProviderIDs: ["codex"], expectedReferenceCount: 2, @@ -2979,7 +2979,7 @@ struct ProviderArchitectureGatekeeperTests { reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1293, + line: 1338, anchor: "if let resultEmail = CodexIdentityResolver.normalizeEmail(scoped.accountEmail(for: .codex)),", expectedProviderIDs: ["codex"], expectedReferenceCount: 1, @@ -2987,7 +2987,7 @@ struct ProviderArchitectureGatekeeperTests { reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1371, + line: 1412, anchor: "guard self.isCurrentProviderRefreshGeneration(.codex, generation: generation) else { return }", expectedProviderIDs: ["codex"], expectedReferenceCount: 9, @@ -3005,7 +3005,7 @@ struct ProviderArchitectureGatekeeperTests { reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1413, + line: 1454, anchor: "self.lastFetchAttempts[.codex] = outcome.attempts", expectedProviderIDs: ["codex"], expectedReferenceCount: 5, @@ -3013,15 +3013,15 @@ struct ProviderArchitectureGatekeeperTests { reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1452, - anchor: "let profileStable = provider == .deepseek", + line: 1495, + anchor: "provider == .deepseek", expectedProviderIDs: ["deepseek"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["deepseek@0"], reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1467, + line: 1510, anchor: "accountDiscriminatorOverride: provider == .claude ? warningAccountDiscriminator : nil)", expectedProviderIDs: ["claude", "deepseek"], expectedReferenceCount: 2, @@ -3029,7 +3029,7 @@ struct ProviderArchitectureGatekeeperTests { reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1488, + line: 1531, anchor: "if provider == .claude,", expectedProviderIDs: ["claude"], expectedReferenceCount: 1, @@ -3037,7 +3037,7 @@ struct ProviderArchitectureGatekeeperTests { reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1513, + line: 1557, anchor: "if provider == .deepseek {", expectedProviderIDs: ["deepseek"], expectedReferenceCount: 1, diff --git a/Tests/CodexBarTests/ProviderCredentialCharacterizationTests.swift b/Tests/CodexBarTests/ProviderCredentialCharacterizationTests.swift index 09374afd3c..7dab269a5b 100644 --- a/Tests/CodexBarTests/ProviderCredentialCharacterizationTests.swift +++ b/Tests/CodexBarTests/ProviderCredentialCharacterizationTests.swift @@ -211,6 +211,7 @@ struct ProviderCredentialCharacterizationTests { (.litellm, "LITELLM_API_KEY"), (.sub2api, "SUB2API_API_KEY"), (.ibmbob, "BOBSHELL_API_KEY"), + (.grok, "GROK_OAUTH_TOKEN"), ] let cookieProviders: [UsageProvider] = [ .claude, .cursor, .opencode, .opencodego, .factory, .minimax, .manus,