import CodexBarCore import Foundation import SwiftUI struct CodexProviderImplementation: ProviderImplementation { let id: UsageProvider = .codex let supportsLoginFlow: Bool = true @MainActor func presentation(context _: ProviderPresentationContext) -> ProviderPresentation { ProviderPresentation { context in context.store.version(for: context.provider) ?? "not detected" } } @MainActor func observeSettings(_ settings: SettingsStore) { _ = settings.codexUsageDataSource _ = settings.codexCookieSource _ = settings.codexCookieHeader } @MainActor func settingsSnapshot(context: ProviderSettingsSnapshotContext) -> ProviderSettingsSnapshotContribution? { .codex(context.settings.codexSettingsSnapshot( tokenOverride: context.tokenOverride, activeSourceOverride: context.codexActiveSourceOverride)) } @MainActor func defaultSourceLabel(context: ProviderSourceLabelContext) -> String? { context.settings.codexUsageDataSource.rawValue } @MainActor func decorateSourceLabel(context: ProviderSourceLabelContext, baseLabel: String) -> String { if context.settings.codexCookieSource.isEnabled, context.store.openAIDashboard != nil, !context.store.openAIDashboardRequiresLogin, !baseLabel.contains("openai-web") { return "\(baseLabel) + openai-web" } return baseLabel } @MainActor func sourceMode(context: ProviderSourceModeContext) -> ProviderSourceMode { switch context.settings.codexUsageDataSource { case .auto: .auto case .oauth: .oauth case .cli: .cli } } func makeRuntime() -> (any ProviderRuntime)? { CodexProviderRuntime() } @MainActor func settingsToggles(context: ProviderSettingsContext) -> [ProviderSettingsToggleDescriptor] { let extrasBinding = Binding( get: { context.settings.openAIWebAccessEnabled }, set: { enabled in context.settings.openAIWebAccessEnabled = enabled Task { @MainActor in await context.store.performRuntimeAction( .openAIWebAccessToggled(enabled), for: .codex) } }) let batterySaverBinding = context.boolBinding(\.openAIWebBatterySaverEnabled) let historicalTrackingSubtitle = [ L("Stores local Codex usage history (8 weeks) to personalize Pace predictions."), "[\(L("weekly_progress_work_days_title")) = \(L("Automatic"))]", ].joined(separator: " ") return [ ProviderSettingsToggleDescriptor( id: "codex-local-session-cost-ledger", title: "Local session cost estimates", subtitle: [ "Uses this Mac's Codex sessions instead of the selected managed account's session history.", "Works with organization API keys and does not require OpenAI billing or administrator access.", "Uses locally cached or bundled model prices without making a network request.", "This provider-specific toggle does not enable cost summaries for other providers.", ].joined(separator: " "), binding: context.boolBinding(\.codexLocalSessionCostLedgerEnabled), statusText: nil, actions: [], isVisible: nil, onChange: nil, onAppDidBecomeActive: nil, onAppearWhenEnabled: nil), ProviderSettingsToggleDescriptor( id: "codex-historical-tracking", title: "Historical tracking", subtitle: historicalTrackingSubtitle, binding: context.boolBinding(\.historicalTrackingEnabled), statusText: nil, actions: [], isVisible: nil, onChange: nil, onAppDidBecomeActive: nil, onAppearWhenEnabled: nil), ProviderSettingsToggleDescriptor( id: "codex-spark-usage-visible", title: "Show Codex Spark usage", subtitle: [ "Shows Codex Spark quota rows in the menu and provider preview.", "Requires optional credits and extra usage in Display settings.", ].joined(separator: " "), binding: context.boolBinding(\.codexSparkUsageVisible), statusText: nil, actions: [], isVisible: nil, isEnabled: { context.settings.showOptionalCreditsAndExtraUsage }, onChange: nil, onAppDidBecomeActive: nil, onAppearWhenEnabled: nil), ProviderSettingsToggleDescriptor( id: "codex-openai-web-extras", title: "OpenAI web extras", subtitle: [ "Optional.", "Turn this on to show code review, usage breakdown, and credits history via chatgpt.com.", ].joined(separator: " "), binding: extrasBinding, statusText: nil, actions: [], isVisible: nil, onChange: nil, onAppDidBecomeActive: nil, onAppearWhenEnabled: nil), ProviderSettingsToggleDescriptor( id: "codex-openai-web-battery-saver", title: "Battery Saver", subtitle: [ "Limits background chatgpt.com refreshes to reduce battery and network usage.", "Dashboard extras may stay stale until you refresh them manually.", ].joined(separator: " "), binding: batterySaverBinding, statusText: nil, actions: [], isVisible: { context.settings.openAIWebAccessEnabled }, onChange: nil, onAppDidBecomeActive: nil, onAppearWhenEnabled: nil), ] } @MainActor func settingsPickers(context: ProviderSettingsContext) -> [ProviderSettingsPickerDescriptor] { let usageBinding = Binding( get: { context.settings.codexUsageDataSource.rawValue }, set: { raw in context.settings.codexUsageDataSource = CodexUsageDataSource(rawValue: raw) ?? .auto }) let cookieBinding = Binding( get: { context.settings.codexCookieSource.rawValue }, set: { raw in context.settings.codexCookieSource = ProviderCookieSource(rawValue: raw) ?? .auto }) let usageOptions = CodexUsageDataSource.allCases.map { ProviderSettingsPickerOption(id: $0.rawValue, title: $0.displayName) } let cookieOptions = ProviderCookieSourceUI.options( allowsOff: true, keychainDisabled: context.settings.debugDisableKeychainAccess) let cookieSubtitle: () -> String? = { ProviderCookieSourceUI.subtitle( source: context.settings.codexCookieSource, keychainDisabled: context.settings.debugDisableKeychainAccess, auto: "Automatic imports browser cookies for dashboard extras.", manual: "Paste a Cookie header from a chatgpt.com request.", off: "Disable OpenAI dashboard cookie usage.") } return [ ProviderSettingsPickerDescriptor( id: "codex-usage-source", title: "Quota usage source", subtitle: [ "Controls live session and weekly quota fetching only.", "Local session cost estimates work independently.", ].joined(separator: " "), binding: usageBinding, options: usageOptions, isVisible: nil, onChange: nil, trailingText: { guard context.settings.codexUsageDataSource == .auto else { return nil } let label = context.store.sourceLabel(for: .codex) return label == "auto" ? nil : label }), ProviderSettingsPickerDescriptor( id: "codex-cookie-source", title: "OpenAI cookies", subtitle: "Automatic imports browser cookies for dashboard extras.", dynamicSubtitle: cookieSubtitle, binding: cookieBinding, options: cookieOptions, isVisible: { context.settings.openAIWebAccessEnabled }, onChange: nil, trailingText: { ProviderCookieSourceUI.cachedTrailingText(provider: .codex) }), ] } @MainActor func settingsFields(context: ProviderSettingsContext) -> [ProviderSettingsFieldDescriptor] { [ ProviderSettingsFieldDescriptor( id: "codex-cookie-header", title: "", subtitle: "", kind: .secure, placeholder: "Cookie: …", binding: context.stringBinding(\.codexCookieHeader), actions: [], isVisible: { context.settings.codexCookieSource == .manual }, onActivate: { context.settings.ensureCodexCookieLoaded() }), ] } @MainActor func appendUsageMenuEntries(context: ProviderMenuUsageContext, entries: inout [ProviderMenuEntry]) { guard context.settings.showOptionalCreditsAndExtraUsage, context.metadata.supportsCredits else { return } if let credits = context.store.credits { let remaining = credits.codexCreditLimit?.remaining ?? credits.remaining entries.append(.text( String(format: L("credits_remaining"), UsageFormatter.creditsString(from: remaining)), .primary)) if let limit = credits.codexCreditLimit { var parts = [ L("%@ used", UsageFormatter.creditsNumberString(from: limit.used)), ] if let resetsAt = limit.resetsAt { parts.append(L("resets %@", UsageFormatter.resetDescription(from: resetsAt))) } entries.append(.text(parts.joined(separator: " · "), .secondary)) } if let latest = credits.events.first { entries.append(.text( String(format: L("last_spend"), UsageFormatter.creditEventSummary(latest)), .secondary)) } } else { let hint = context.store.userFacingLastCreditsError ?? context.metadata.creditsHint entries.append(.text(hint, .secondary)) } } @MainActor func loginMenuAction(context _: ProviderMenuLoginContext) -> (label: String, action: MenuDescriptor.MenuAction)? { ("Add Account...", .addCodexAccount) } @MainActor func appendActionMenuEntries(context: ProviderMenuActionContext, entries: inout [ProviderMenuEntry]) { let projection = context.settings.codexVisibleAccountProjection guard !projection.visibleAccounts.isEmpty else { return } let isInteractionBlocked = context.codexAccountPromotionCoordinator?.isInteractionBlocked() ?? false let submenuItems = projection.visibleAccounts.map { account in let isChecked = account.id == projection.liveVisibleAccountID let isEnabled = !isInteractionBlocked && !isChecked && account.storedAccountID != nil let action = account.storedAccountID.map(MenuDescriptor.MenuAction.requestCodexSystemPromotion) return MenuDescriptor.SubmenuItem( title: account.displayName, action: action, isEnabled: isEnabled, isChecked: isChecked) } guard submenuItems.count > 1 || submenuItems.contains(where: { $0.isEnabled && $0.action != nil }) else { return } entries.append(.submenu( "System Account", MenuDescriptor.MenuActionSystemImage.systemAccount.rawValue, submenuItems)) } @MainActor func runLoginFlow(context: ProviderLoginContext) async -> Bool { await context.controller.runCodexLoginFlow() return true } }