import AppKitimport CodexBarCoreimport Observationimport ServiceManagement enum RefreshFrequency: String, CaseIterable, Identifiable { case manual case oneMinute case twoMinutes case fiveMinutes case fifteenMinutes var id: String { self.rawValue } var seconds: TimeInterval? { switch self { case .manual: nil case .oneMinute: 60 case .twoMinutes: 120 case .fiveMinutes: 300 case .fifteenMinutes: 900 } } var label: String { switch self { case .manual: "Manual" case .oneMinute: "1 min" case .twoMinutes: "2 min" case .fiveMinutes: "5 min" case .fifteenMinutes: "15 min" } }} enum MenuBarMetricPreference: String, CaseIterable, Identifiable { case automatic case primary case secondary case average var id: String { self.rawValue } var label: String { switch self { case .automatic: "Automatic" case .primary: "Primary" case .secondary: "Secondary" case .average: "Average" } }} @MainActor@Observablefinal class SettingsStore { private static let sharedDefaults = UserDefaults(suiteName: "group.com.steipete.codexbar") /// Persisted provider display order. /// /// Stored as raw `UsageProvider` strings so new providers can be appended automatically without breaking. private(set) var providerOrderRaw: [String] { didSet { self.userDefaults.set(self.providerOrderRaw, forKey: "providerOrder") } } var refreshFrequency: RefreshFrequency { didSet { self.userDefaults.set(self.refreshFrequency.rawValue, forKey: "refreshFrequency") } } var launchAtLogin: Bool { didSet { self.userDefaults.set(self.launchAtLogin, forKey: "launchAtLogin") LaunchAtLoginManager.setEnabled(self.launchAtLogin) } } /// Hidden toggle to reveal debug-only menu items (enable via defaults write com.steipete.CodexBar debugMenuEnabled /// -bool YES). var debugMenuEnabled: Bool { didSet { self.userDefaults.set(self.debugMenuEnabled, forKey: "debugMenuEnabled") } } /// Debug-only: disable all Keychain access and hide cookie-based web options. var debugDisableKeychainAccess: Bool { didSet { self.userDefaults.set(self.debugDisableKeychainAccess, forKey: "debugDisableKeychainAccess") Self.sharedDefaults?.set(self.debugDisableKeychainAccess, forKey: "debugDisableKeychainAccess") KeychainAccessGate.isDisabled = self.debugDisableKeychainAccess } } private var debugLoadingPatternRaw: String? { didSet { if let raw = self.debugLoadingPatternRaw { self.userDefaults.set(raw, forKey: "debugLoadingPattern") } else { self.userDefaults.removeObject(forKey: "debugLoadingPattern") } } } var statusChecksEnabled: Bool { didSet { self.userDefaults.set(self.statusChecksEnabled, forKey: "statusChecksEnabled") } } var sessionQuotaNotificationsEnabled: Bool { didSet { self.userDefaults.set(self.sessionQuotaNotificationsEnabled, forKey: "sessionQuotaNotificationsEnabled") } } /// When enabled, progress bars show "percent used" instead of "percent left". var usageBarsShowUsed: Bool { didSet { self.userDefaults.set(self.usageBarsShowUsed, forKey: "usageBarsShowUsed") } } /// Optional: show reset times as absolute clock values instead of countdowns. var resetTimesShowAbsolute: Bool { didSet { self.userDefaults.set(self.resetTimesShowAbsolute, forKey: "resetTimesShowAbsolute") } } /// Optional: use provider branding icons with a percentage in the menu bar. var menuBarShowsBrandIconWithPercent: Bool { didSet { self.userDefaults.set(self.menuBarShowsBrandIconWithPercent, forKey: "menuBarShowsBrandIconWithPercent") } } /// Optional: show all token accounts stacked in the menu (otherwise show a switcher bar). var showAllTokenAccountsInMenu: Bool { didSet { self.userDefaults.set(self.showAllTokenAccountsInMenu, forKey: "showAllTokenAccountsInMenu") } } /// Optional: choose which quota window drives the menu bar percentage. private(set) var menuBarMetricPreferencesRaw: [String: String] { didSet { self.userDefaults.set(self.menuBarMetricPreferencesRaw, forKey: "menuBarMetricPreferences") } } /// Optional: show provider cost summary from local usage logs (Codex + Claude). var costUsageEnabled: Bool { didSet { self.userDefaults.set(self.costUsageEnabled, forKey: "tokenCostUsageEnabled") } } /// Optional: hide personal info (emails) in menu bar + menu content. var hidePersonalInfo: Bool { didSet { self.userDefaults.set(self.hidePersonalInfo, forKey: "hidePersonalInfo") } } var randomBlinkEnabled: Bool { didSet { self.userDefaults.set(self.randomBlinkEnabled, forKey: "randomBlinkEnabled") } } /// Optional: augment Claude usage with claude.ai web API (via browser cookies), /// incl. "Extra usage" spend. var claudeWebExtrasEnabled: Bool { get { self.debugDisableKeychainAccess ? false : self.claudeWebExtrasEnabledRaw } set { self.claudeWebExtrasEnabledRaw = newValue } } private var claudeWebExtrasEnabledRaw: Bool { didSet { self.userDefaults.set(self.claudeWebExtrasEnabledRaw, forKey: "claudeWebExtrasEnabled") } } /// Optional: show Codex credits + Claude extra usage sections in the menu UI. var showOptionalCreditsAndExtraUsage: Bool { didSet { self.userDefaults.set(self.showOptionalCreditsAndExtraUsage, forKey: "showOptionalCreditsAndExtraUsage") } } /// Optional: fetch OpenAI web dashboard extras for Codex (browser cookies). var openAIWebAccessEnabled: Bool { didSet { self.userDefaults.set(self.openAIWebAccessEnabled, forKey: "openAIWebAccessEnabled") } } private var codexUsageDataSourceRaw: String? { didSet { if let raw = self.codexUsageDataSourceRaw { self.userDefaults.set(raw, forKey: "codexUsageDataSource") } else { self.userDefaults.removeObject(forKey: "codexUsageDataSource") } } } private var claudeUsageDataSourceRaw: String? { didSet { if let raw = self.claudeUsageDataSourceRaw { self.userDefaults.set(raw, forKey: "claudeUsageDataSource") } else { self.userDefaults.removeObject(forKey: "claudeUsageDataSource") } } } private var codexCookieSourceRaw: String? { didSet { if let raw = self.codexCookieSourceRaw { self.userDefaults.set(raw, forKey: "codexCookieSource") } else { self.userDefaults.removeObject(forKey: "codexCookieSource") } } } private var claudeCookieSourceRaw: String? { didSet { if let raw = self.claudeCookieSourceRaw { self.userDefaults.set(raw, forKey: "claudeCookieSource") } else { self.userDefaults.removeObject(forKey: "claudeCookieSource") } } } private var cursorCookieSourceRaw: String? { didSet { if let raw = self.cursorCookieSourceRaw { self.userDefaults.set(raw, forKey: "cursorCookieSource") } else { self.userDefaults.removeObject(forKey: "cursorCookieSource") } } } private var opencodeCookieSourceRaw: String? { didSet { if let raw = self.opencodeCookieSourceRaw { self.userDefaults.set(raw, forKey: "opencodeCookieSource") } else { self.userDefaults.removeObject(forKey: "opencodeCookieSource") } } } private var opencodeWorkspaceIDRaw: String? { didSet { if let raw = self.opencodeWorkspaceIDRaw, !raw.isEmpty { self.userDefaults.set(raw, forKey: "opencodeWorkspaceID") } else { self.userDefaults.removeObject(forKey: "opencodeWorkspaceID") } } } private var factoryCookieSourceRaw: String? { didSet { if let raw = self.factoryCookieSourceRaw { self.userDefaults.set(raw, forKey: "factoryCookieSource") } else { self.userDefaults.removeObject(forKey: "factoryCookieSource") } } } private var minimaxCookieSourceRaw: String? { didSet { if let raw = self.minimaxCookieSourceRaw { self.userDefaults.set(raw, forKey: "minimaxCookieSource") } else { self.userDefaults.removeObject(forKey: "minimaxCookieSource") } } } private var kimiCookieSourceRaw: String? { didSet { if let raw = self.kimiCookieSourceRaw { self.userDefaults.set(raw, forKey: "kimiCookieSource") } else { self.userDefaults.removeObject(forKey: "kimiCookieSource") } } } private var augmentCookieSourceRaw: String? { didSet { if let raw = self.augmentCookieSourceRaw { self.userDefaults.set(raw, forKey: "augmentCookieSource") } else { self.userDefaults.removeObject(forKey: "augmentCookieSource") } } } private var ampCookieSourceRaw: String? { didSet { if let raw = self.ampCookieSourceRaw { self.userDefaults.set(raw, forKey: "ampCookieSource") } else { self.userDefaults.removeObject(forKey: "ampCookieSource") } } } /// JetBrains IDE base path for quota file lookup. var jetbrainsIDEBasePath: String { didSet { self.userDefaults.set(self.jetbrainsIDEBasePath, forKey: "jetbrainsIDEBasePath") } } /// Optional: collapse provider icons into a single menu bar item with an in-menu switcher. var mergeIcons: Bool { didSet { self.userDefaults.set(self.mergeIcons, forKey: "mergeIcons") } } /// Optional: show provider icons in the in-menu switcher. var switcherShowsIcons: Bool { didSet { self.userDefaults.set(self.switcherShowsIcons, forKey: "switcherShowsIcons") } } /// MiniMax API region (stored in UserDefaults). var minimaxAPIRegion: MiniMaxAPIRegion { didSet { self.userDefaults.set(self.minimaxAPIRegion.rawValue, forKey: "minimaxAPIRegion") } } /// z.ai API region (stored in UserDefaults). var zaiAPIRegion: ZaiAPIRegion { didSet { self.userDefaults.set(self.zaiAPIRegion.rawValue, forKey: "zaiAPIRegion") } } /// z.ai API token (stored in Keychain). var zaiAPIToken: String { didSet { self.schedulePersistZaiAPIToken() } } /// Synthetic API key (stored in Keychain). var syntheticAPIToken: String { didSet { self.schedulePersistSyntheticAPIToken() } } /// Codex OpenAI cookie header (stored in Keychain). var codexCookieHeader: String { didSet { self.schedulePersistCodexCookieHeader() } } /// Claude session cookie header (stored in Keychain). var claudeCookieHeader: String { didSet { self.schedulePersistClaudeCookieHeader() } } /// Cursor session cookie header (stored in Keychain). var cursorCookieHeader: String { didSet { self.schedulePersistCursorCookieHeader() } } /// OpenCode session cookie header (stored in Keychain). var opencodeCookieHeader: String { didSet { self.schedulePersistOpenCodeCookieHeader() } } /// Optional OpenCode workspace ID override. var opencodeWorkspaceID: String { get { self.opencodeWorkspaceIDRaw ?? "" } set { let trimmed = newValue.trimmingCharacters(in: .whitespacesAndNewlines) self.opencodeWorkspaceIDRaw = trimmed.isEmpty ? nil : trimmed } } /// Factory session cookie header (stored in Keychain). var factoryCookieHeader: String { didSet { self.schedulePersistFactoryCookieHeader() } } /// MiniMax session cookie header (stored in Keychain). var minimaxCookieHeader: String { didSet { self.schedulePersistMiniMaxCookieHeader() } } /// MiniMax API token (stored in Keychain). var minimaxAPIToken: String { didSet { self.schedulePersistMiniMaxAPIToken() } } /// Augment session cookie header (stored in Keychain). var augmentCookieHeader: String { didSet { self.schedulePersistAugmentCookieHeader() } } /// Amp session cookie header (stored in Keychain). var ampCookieHeader: String { didSet { self.schedulePersistAmpCookieHeader() } } /// Copilot API token (stored in Keychain). var copilotAPIToken: String { didSet { self.schedulePersistCopilotAPIToken() } } /// Kimi auth token (stored in Keychain). var kimiManualCookieHeader: String { didSet { self.schedulePersistKimiAuthToken() } } /// Kimi K2 API token (stored in Keychain). var kimiK2APIToken: String { didSet { self.schedulePersistKimiK2APIToken() } } /// Token accounts loaded from the local config file. var tokenAccountsByProvider: [UsageProvider: ProviderTokenAccountData] { didSet { self.schedulePersistTokenAccounts() } } private var selectedMenuProviderRaw: String? { didSet { if let raw = self.selectedMenuProviderRaw { self.userDefaults.set(raw, forKey: "selectedMenuProvider") } else { self.userDefaults.removeObject(forKey: "selectedMenuProvider") } } } /// Optional override for the loading animation pattern, exposed via the Debug tab. var debugLoadingPattern: LoadingPattern? { get { self.debugLoadingPatternRaw.flatMap(LoadingPattern.init(rawValue:)) } set { self.debugLoadingPatternRaw = newValue?.rawValue } } var selectedMenuProvider: UsageProvider? { get { self.selectedMenuProviderRaw.flatMap(UsageProvider.init(rawValue:)) } set { self.selectedMenuProviderRaw = newValue?.rawValue } } var resetTimeDisplayStyle: ResetTimeDisplayStyle { self.resetTimesShowAbsolute ? .absolute : .countdown } var codexUsageDataSource: CodexUsageDataSource { get { CodexUsageDataSource(rawValue: self.codexUsageDataSourceRaw ?? "") ?? .auto } set { self.codexUsageDataSourceRaw = newValue.rawValue } } var claudeUsageDataSource: ClaudeUsageDataSource { get { ClaudeUsageDataSource(rawValue: self.claudeUsageDataSourceRaw ?? "") ?? .auto } set { self.claudeUsageDataSourceRaw = newValue.rawValue if newValue != .cli { self.claudeWebExtrasEnabled = false } } } var codexCookieSource: ProviderCookieSource { get { guard !self.debugDisableKeychainAccess else { return .off } return ProviderCookieSource(rawValue: self.codexCookieSourceRaw ?? "") ?? .auto } set { self.codexCookieSourceRaw = newValue.rawValue self.openAIWebAccessEnabled = newValue.isEnabled } } var claudeCookieSource: ProviderCookieSource { get { guard !self.debugDisableKeychainAccess else { return .off } return ProviderCookieSource(rawValue: self.claudeCookieSourceRaw ?? "") ?? .auto } set { self.claudeCookieSourceRaw = newValue.rawValue } } var cursorCookieSource: ProviderCookieSource { get { guard !self.debugDisableKeychainAccess else { return .off } return ProviderCookieSource(rawValue: self.cursorCookieSourceRaw ?? "") ?? .auto } set { self.cursorCookieSourceRaw = newValue.rawValue } } var opencodeCookieSource: ProviderCookieSource { get { guard !self.debugDisableKeychainAccess else { return .off } return ProviderCookieSource(rawValue: self.opencodeCookieSourceRaw ?? "") ?? .auto } set { self.opencodeCookieSourceRaw = newValue.rawValue } } var factoryCookieSource: ProviderCookieSource { get { guard !self.debugDisableKeychainAccess else { return .off } return ProviderCookieSource(rawValue: self.factoryCookieSourceRaw ?? "") ?? .auto } set { self.factoryCookieSourceRaw = newValue.rawValue } } var minimaxCookieSource: ProviderCookieSource { get { guard !self.debugDisableKeychainAccess else { return .off } return ProviderCookieSource(rawValue: self.minimaxCookieSourceRaw ?? "") ?? .auto } set { self.minimaxCookieSourceRaw = newValue.rawValue } } Lines 1–500 of 1667. Load more
import AppKit import CodexBarCore import Observation import ServiceManagement enum RefreshFrequency: String, CaseIterable, Identifiable { case manual case oneMinute case twoMinutes case fiveMinutes case fifteenMinutes var id: String { self.rawValue } var seconds: TimeInterval? { switch self { case .manual: nil case .oneMinute: 60 case .twoMinutes: 120 case .fiveMinutes: 300 case .fifteenMinutes: 900 } } var label: String { switch self { case .manual: "Manual" case .oneMinute: "1 min" case .twoMinutes: "2 min" case .fiveMinutes: "5 min" case .fifteenMinutes: "15 min" } } } enum MenuBarMetricPreference: String, CaseIterable, Identifiable { case automatic case primary case secondary case average var id: String { self.rawValue } var label: String { switch self { case .automatic: "Automatic" case .primary: "Primary" case .secondary: "Secondary" case .average: "Average" } } } @MainActor @Observable final class SettingsStore { private static let sharedDefaults = UserDefaults(suiteName: "group.com.steipete.codexbar") /// Persisted provider display order. /// /// Stored as raw `UsageProvider` strings so new providers can be appended automatically without breaking. private(set) var providerOrderRaw: [String] { didSet { self.userDefaults.set(self.providerOrderRaw, forKey: "providerOrder") } } var refreshFrequency: RefreshFrequency { didSet { self.userDefaults.set(self.refreshFrequency.rawValue, forKey: "refreshFrequency") } } var launchAtLogin: Bool { didSet { self.userDefaults.set(self.launchAtLogin, forKey: "launchAtLogin") LaunchAtLoginManager.setEnabled(self.launchAtLogin) } } /// Hidden toggle to reveal debug-only menu items (enable via defaults write com.steipete.CodexBar debugMenuEnabled /// -bool YES). var debugMenuEnabled: Bool { didSet { self.userDefaults.set(self.debugMenuEnabled, forKey: "debugMenuEnabled") } } /// Debug-only: disable all Keychain access and hide cookie-based web options. var debugDisableKeychainAccess: Bool { didSet { self.userDefaults.set(self.debugDisableKeychainAccess, forKey: "debugDisableKeychainAccess") Self.sharedDefaults?.set(self.debugDisableKeychainAccess, forKey: "debugDisableKeychainAccess") KeychainAccessGate.isDisabled = self.debugDisableKeychainAccess } } private var debugLoadingPatternRaw: String? { didSet { if let raw = self.debugLoadingPatternRaw { self.userDefaults.set(raw, forKey: "debugLoadingPattern") } else { self.userDefaults.removeObject(forKey: "debugLoadingPattern") } } } var statusChecksEnabled: Bool { didSet { self.userDefaults.set(self.statusChecksEnabled, forKey: "statusChecksEnabled") } } var sessionQuotaNotificationsEnabled: Bool { didSet { self.userDefaults.set(self.sessionQuotaNotificationsEnabled, forKey: "sessionQuotaNotificationsEnabled") } } /// When enabled, progress bars show "percent used" instead of "percent left". var usageBarsShowUsed: Bool { didSet { self.userDefaults.set(self.usageBarsShowUsed, forKey: "usageBarsShowUsed") } } /// Optional: show reset times as absolute clock values instead of countdowns. var resetTimesShowAbsolute: Bool { didSet { self.userDefaults.set(self.resetTimesShowAbsolute, forKey: "resetTimesShowAbsolute") } } /// Optional: use provider branding icons with a percentage in the menu bar. var menuBarShowsBrandIconWithPercent: Bool { didSet { self.userDefaults.set(self.menuBarShowsBrandIconWithPercent, forKey: "menuBarShowsBrandIconWithPercent") } } /// Optional: show all token accounts stacked in the menu (otherwise show a switcher bar). var showAllTokenAccountsInMenu: Bool { didSet { self.userDefaults.set(self.showAllTokenAccountsInMenu, forKey: "showAllTokenAccountsInMenu") } } /// Optional: choose which quota window drives the menu bar percentage. private(set) var menuBarMetricPreferencesRaw: [String: String] { didSet { self.userDefaults.set(self.menuBarMetricPreferencesRaw, forKey: "menuBarMetricPreferences") } } /// Optional: show provider cost summary from local usage logs (Codex + Claude). var costUsageEnabled: Bool { didSet { self.userDefaults.set(self.costUsageEnabled, forKey: "tokenCostUsageEnabled") } } /// Optional: hide personal info (emails) in menu bar + menu content. var hidePersonalInfo: Bool { didSet { self.userDefaults.set(self.hidePersonalInfo, forKey: "hidePersonalInfo") } } var randomBlinkEnabled: Bool { didSet { self.userDefaults.set(self.randomBlinkEnabled, forKey: "randomBlinkEnabled") } } /// Optional: augment Claude usage with claude.ai web API (via browser cookies), /// incl. "Extra usage" spend. var claudeWebExtrasEnabled: Bool { get { self.debugDisableKeychainAccess ? false : self.claudeWebExtrasEnabledRaw } set { self.claudeWebExtrasEnabledRaw = newValue } } private var claudeWebExtrasEnabledRaw: Bool { didSet { self.userDefaults.set(self.claudeWebExtrasEnabledRaw, forKey: "claudeWebExtrasEnabled") } } /// Optional: show Codex credits + Claude extra usage sections in the menu UI. var showOptionalCreditsAndExtraUsage: Bool { didSet { self.userDefaults.set(self.showOptionalCreditsAndExtraUsage, forKey: "showOptionalCreditsAndExtraUsage") } } /// Optional: fetch OpenAI web dashboard extras for Codex (browser cookies). var openAIWebAccessEnabled: Bool { didSet { self.userDefaults.set(self.openAIWebAccessEnabled, forKey: "openAIWebAccessEnabled") } } private var codexUsageDataSourceRaw: String? { didSet { if let raw = self.codexUsageDataSourceRaw { self.userDefaults.set(raw, forKey: "codexUsageDataSource") } else { self.userDefaults.removeObject(forKey: "codexUsageDataSource") } } } private var claudeUsageDataSourceRaw: String? { didSet { if let raw = self.claudeUsageDataSourceRaw { self.userDefaults.set(raw, forKey: "claudeUsageDataSource") } else { self.userDefaults.removeObject(forKey: "claudeUsageDataSource") } } } private var codexCookieSourceRaw: String? { didSet { if let raw = self.codexCookieSourceRaw { self.userDefaults.set(raw, forKey: "codexCookieSource") } else { self.userDefaults.removeObject(forKey: "codexCookieSource") } } } private var claudeCookieSourceRaw: String? { didSet { if let raw = self.claudeCookieSourceRaw { self.userDefaults.set(raw, forKey: "claudeCookieSource") } else { self.userDefaults.removeObject(forKey: "claudeCookieSource") } } } private var cursorCookieSourceRaw: String? { didSet { if let raw = self.cursorCookieSourceRaw { self.userDefaults.set(raw, forKey: "cursorCookieSource") } else { self.userDefaults.removeObject(forKey: "cursorCookieSource") } } } private var opencodeCookieSourceRaw: String? { didSet { if let raw = self.opencodeCookieSourceRaw { self.userDefaults.set(raw, forKey: "opencodeCookieSource") } else { self.userDefaults.removeObject(forKey: "opencodeCookieSource") } } } private var opencodeWorkspaceIDRaw: String? { didSet { if let raw = self.opencodeWorkspaceIDRaw, !raw.isEmpty { self.userDefaults.set(raw, forKey: "opencodeWorkspaceID") } else { self.userDefaults.removeObject(forKey: "opencodeWorkspaceID") } } } private var factoryCookieSourceRaw: String? { didSet { if let raw = self.factoryCookieSourceRaw { self.userDefaults.set(raw, forKey: "factoryCookieSource") } else { self.userDefaults.removeObject(forKey: "factoryCookieSource") } } } private var minimaxCookieSourceRaw: String? { didSet { if let raw = self.minimaxCookieSourceRaw { self.userDefaults.set(raw, forKey: "minimaxCookieSource") } else { self.userDefaults.removeObject(forKey: "minimaxCookieSource") } } } private var kimiCookieSourceRaw: String? { didSet { if let raw = self.kimiCookieSourceRaw { self.userDefaults.set(raw, forKey: "kimiCookieSource") } else { self.userDefaults.removeObject(forKey: "kimiCookieSource") } } } private var augmentCookieSourceRaw: String? { didSet { if let raw = self.augmentCookieSourceRaw { self.userDefaults.set(raw, forKey: "augmentCookieSource") } else { self.userDefaults.removeObject(forKey: "augmentCookieSource") } } } private var ampCookieSourceRaw: String? { didSet { if let raw = self.ampCookieSourceRaw { self.userDefaults.set(raw, forKey: "ampCookieSource") } else { self.userDefaults.removeObject(forKey: "ampCookieSource") } } } /// JetBrains IDE base path for quota file lookup. var jetbrainsIDEBasePath: String { didSet { self.userDefaults.set(self.jetbrainsIDEBasePath, forKey: "jetbrainsIDEBasePath") } } /// Optional: collapse provider icons into a single menu bar item with an in-menu switcher. var mergeIcons: Bool { didSet { self.userDefaults.set(self.mergeIcons, forKey: "mergeIcons") } } /// Optional: show provider icons in the in-menu switcher. var switcherShowsIcons: Bool { didSet { self.userDefaults.set(self.switcherShowsIcons, forKey: "switcherShowsIcons") } } /// MiniMax API region (stored in UserDefaults). var minimaxAPIRegion: MiniMaxAPIRegion { didSet { self.userDefaults.set(self.minimaxAPIRegion.rawValue, forKey: "minimaxAPIRegion") } } /// z.ai API region (stored in UserDefaults). var zaiAPIRegion: ZaiAPIRegion { didSet { self.userDefaults.set(self.zaiAPIRegion.rawValue, forKey: "zaiAPIRegion") } } /// z.ai API token (stored in Keychain). var zaiAPIToken: String { didSet { self.schedulePersistZaiAPIToken() } } /// Synthetic API key (stored in Keychain). var syntheticAPIToken: String { didSet { self.schedulePersistSyntheticAPIToken() } } /// Codex OpenAI cookie header (stored in Keychain). var codexCookieHeader: String { didSet { self.schedulePersistCodexCookieHeader() } } /// Claude session cookie header (stored in Keychain). var claudeCookieHeader: String { didSet { self.schedulePersistClaudeCookieHeader() } } /// Cursor session cookie header (stored in Keychain). var cursorCookieHeader: String { didSet { self.schedulePersistCursorCookieHeader() } } /// OpenCode session cookie header (stored in Keychain). var opencodeCookieHeader: String { didSet { self.schedulePersistOpenCodeCookieHeader() } } /// Optional OpenCode workspace ID override. var opencodeWorkspaceID: String { get { self.opencodeWorkspaceIDRaw ?? "" } set { let trimmed = newValue.trimmingCharacters(in: .whitespacesAndNewlines) self.opencodeWorkspaceIDRaw = trimmed.isEmpty ? nil : trimmed } } /// Factory session cookie header (stored in Keychain). var factoryCookieHeader: String { didSet { self.schedulePersistFactoryCookieHeader() } } /// MiniMax session cookie header (stored in Keychain). var minimaxCookieHeader: String { didSet { self.schedulePersistMiniMaxCookieHeader() } } /// MiniMax API token (stored in Keychain). var minimaxAPIToken: String { didSet { self.schedulePersistMiniMaxAPIToken() } } /// Augment session cookie header (stored in Keychain). var augmentCookieHeader: String { didSet { self.schedulePersistAugmentCookieHeader() } } /// Amp session cookie header (stored in Keychain). var ampCookieHeader: String { didSet { self.schedulePersistAmpCookieHeader() } } /// Copilot API token (stored in Keychain). var copilotAPIToken: String { didSet { self.schedulePersistCopilotAPIToken() } } /// Kimi auth token (stored in Keychain). var kimiManualCookieHeader: String { didSet { self.schedulePersistKimiAuthToken() } } /// Kimi K2 API token (stored in Keychain). var kimiK2APIToken: String { didSet { self.schedulePersistKimiK2APIToken() } } /// Token accounts loaded from the local config file. var tokenAccountsByProvider: [UsageProvider: ProviderTokenAccountData] { didSet { self.schedulePersistTokenAccounts() } } private var selectedMenuProviderRaw: String? { didSet { if let raw = self.selectedMenuProviderRaw { self.userDefaults.set(raw, forKey: "selectedMenuProvider") } else { self.userDefaults.removeObject(forKey: "selectedMenuProvider") } } } /// Optional override for the loading animation pattern, exposed via the Debug tab. var debugLoadingPattern: LoadingPattern? { get { self.debugLoadingPatternRaw.flatMap(LoadingPattern.init(rawValue:)) } set { self.debugLoadingPatternRaw = newValue?.rawValue } } var selectedMenuProvider: UsageProvider? { get { self.selectedMenuProviderRaw.flatMap(UsageProvider.init(rawValue:)) } set { self.selectedMenuProviderRaw = newValue?.rawValue } } var resetTimeDisplayStyle: ResetTimeDisplayStyle { self.resetTimesShowAbsolute ? .absolute : .countdown } var codexUsageDataSource: CodexUsageDataSource { get { CodexUsageDataSource(rawValue: self.codexUsageDataSourceRaw ?? "") ?? .auto } set { self.codexUsageDataSourceRaw = newValue.rawValue } } var claudeUsageDataSource: ClaudeUsageDataSource { get { ClaudeUsageDataSource(rawValue: self.claudeUsageDataSourceRaw ?? "") ?? .auto } set { self.claudeUsageDataSourceRaw = newValue.rawValue if newValue != .cli { self.claudeWebExtrasEnabled = false } } } var codexCookieSource: ProviderCookieSource { get { guard !self.debugDisableKeychainAccess else { return .off } return ProviderCookieSource(rawValue: self.codexCookieSourceRaw ?? "") ?? .auto } set { self.codexCookieSourceRaw = newValue.rawValue self.openAIWebAccessEnabled = newValue.isEnabled } } var claudeCookieSource: ProviderCookieSource { get { guard !self.debugDisableKeychainAccess else { return .off } return ProviderCookieSource(rawValue: self.claudeCookieSourceRaw ?? "") ?? .auto } set { self.claudeCookieSourceRaw = newValue.rawValue } } var cursorCookieSource: ProviderCookieSource { get { guard !self.debugDisableKeychainAccess else { return .off } return ProviderCookieSource(rawValue: self.cursorCookieSourceRaw ?? "") ?? .auto } set { self.cursorCookieSourceRaw = newValue.rawValue } } var opencodeCookieSource: ProviderCookieSource { get { guard !self.debugDisableKeychainAccess else { return .off } return ProviderCookieSource(rawValue: self.opencodeCookieSourceRaw ?? "") ?? .auto } set { self.opencodeCookieSourceRaw = newValue.rawValue } } var factoryCookieSource: ProviderCookieSource { get { guard !self.debugDisableKeychainAccess else { return .off } return ProviderCookieSource(rawValue: self.factoryCookieSourceRaw ?? "") ?? .auto } set { self.factoryCookieSourceRaw = newValue.rawValue } } var minimaxCookieSource: ProviderCookieSource { get { guard !self.debugDisableKeychainAccess else { return .off } return ProviderCookieSource(rawValue: self.minimaxCookieSourceRaw ?? "") ?? .auto } set { self.minimaxCookieSourceRaw = newValue.rawValue } }