159 lines · 7.1 KB · Swift
RawDownload
import CodexBarCoreimport Foundation struct ProviderSpec {    let style: IconStyle    let isEnabled: @MainActor () -> Bool    let descriptor: ProviderDescriptor    let makeFetchContext: @MainActor () -> ProviderFetchContext} struct ProviderRegistry {    let metadata: [UsageProvider: ProviderMetadata]     static let shared: ProviderRegistry = .init()     init(metadata: [UsageProvider: ProviderMetadata] = ProviderDescriptorRegistry.metadata) {        self.metadata = metadata    }     @MainActor    func specs(        settings: SettingsStore,        metadata: [UsageProvider: ProviderMetadata],        codexFetcher: UsageFetcher,        claudeFetcher: any ClaudeUsageFetching,        browserDetection: BrowserDetection,        environmentBase: [String: String] = ProcessInfo.processInfo.environment) -> [UsageProvider: ProviderSpec]    {        var specs: [UsageProvider: ProviderSpec] = [:]        specs.reserveCapacity(UsageProvider.allCases.count)         for provider in UsageProvider.allCases {            let descriptor = ProviderDescriptorRegistry.descriptor(for: provider)            let meta = metadata[provider]!            let spec = ProviderSpec(                style: descriptor.branding.iconStyle,                isEnabled: { settings.isProviderEnabled(provider: provider, metadata: meta) },                descriptor: descriptor,                makeFetchContext: {                    let account = ProviderTokenAccountSelection.selectedAccount(                        provider: provider,                        settings: settings,                        override: nil)                    let sourceMode = ProviderCatalog.implementation(for: provider)?                        .sourceMode(context: ProviderSourceModeContext(provider: provider, settings: settings))                        ?? .auto                    let snapshot = Self.makeSettingsSnapshot(settings: settings, tokenOverride: nil)                    let env = Self.makeEnvironment(                        base: environmentBase,                        provider: provider,                        settings: settings,                        tokenOverride: nil)                    let fetcher = Self.makeFetcher(base: codexFetcher, provider: provider, env: env)                    let verbose = settings.isVerboseLoggingEnabled                    return ProviderFetchContext(                        runtime: .app,                        sourceMode: sourceMode,                        includeCredits: false,                        includeOptionalUsage: settings.showOptionalCreditsAndExtraUsage,                        webTimeout: 60,                        webDebugDumpHTML: false,                        verbose: verbose,                        env: env,                        settings: snapshot,                        fetcher: fetcher,                        claudeFetcher: claudeFetcher,                        browserDetection: browserDetection,                        selectedTokenAccountID: account?.id,                        tokenAccountTokenUpdater: { provider, accountID, token in                            await MainActor.run {                                settings.updateTokenAccount(                                    provider: provider,                                    accountID: accountID,                                    token: token)                            }                        },                        providerManualTokenUpdater: { provider, token in                            await MainActor.run {                                if provider == .stepfun {                                    settings.stepfunToken = token                                }                            }                        },                        costUsageHistoryDays: settings.costUsageHistoryDays,                        persistsCLISessions: true,                        persistentCLISessionIdleWindow: Self.persistentCLISessionIdleWindow(                            refreshInterval: settings.refreshFrequency.seconds))                })            specs[provider] = spec        }         return specs    }     static func persistentCLISessionIdleWindow(refreshInterval: TimeInterval?) -> TimeInterval {        max(180, (refreshInterval ?? 120) + 60)    }     @MainActor    static func makeSettingsSnapshot(        settings: SettingsStore,        tokenOverride: TokenAccountOverride?,        codexActiveSourceOverride: CodexActiveSource? = nil) -> ProviderSettingsSnapshot    {        settings.ensureTokenAccountsLoaded()        var builder = ProviderSettingsSnapshotBuilder(            debugMenuEnabled: settings.debugMenuEnabled,            debugKeepCLISessionsAlive: settings.debugKeepCLISessionsAlive)        let context = ProviderSettingsSnapshotContext(            settings: settings,            tokenOverride: tokenOverride,            codexActiveSourceOverride: codexActiveSourceOverride)        for implementation in ProviderCatalog.all {            if let contribution = implementation.settingsSnapshot(context: context) {                builder.apply(contribution)            }        }        return builder.build()    }     @MainActor    static func makeEnvironment(        base: [String: String],        provider: UsageProvider,        settings: SettingsStore,        tokenOverride: TokenAccountOverride?,        codexActiveSourceOverride: CodexActiveSource? = nil) -> [String: String]    {        let account = ProviderTokenAccountSelection.selectedAccount(            provider: provider,            settings: settings,            override: tokenOverride)        var env = ProviderEnvironmentResolver.resolve(            base: base,            provider: provider,            config: settings.providerConfig(for: provider),            selectedAccount: account)        // Codex account routing scopes remote account fetches such as identity, plan,        // quotas, and dashboard data. Token-cost/session history is intentionally handled        // separately because it is provider-level local telemetry from this Mac's Codex sessions,        // not account-owned remote state.        if provider == .codex {            let codexActiveSource = codexActiveSourceOverride ?? settings.codexResolvedActiveSource            if let managedHomePath = settings.managedCodexRemoteHomePath(forActiveSource: codexActiveSource) {                env = CodexHomeScope.scopedEnvironment(base: env, codexHome: managedHomePath)            } else if let liveHomePath = settings.liveSystemCodexHomePath(forActiveSource: codexActiveSource) {                env = CodexHomeScope.scopedEnvironment(base: env, codexHome: liveHomePath)            } 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 {        guard provider == .codex else { return base }        return UsageFetcher(environment: env)    }}