diff --git a/Sources/CodexBar/ProviderRegistry.swift b/Sources/CodexBar/ProviderRegistry.swift index 56593f7cfb..19e2ba803a 100644 --- a/Sources/CodexBar/ProviderRegistry.swift +++ b/Sources/CodexBar/ProviderRegistry.swift @@ -122,9 +122,14 @@ struct ProviderRegistry { tokenOverride: tokenOverride, codexActiveSourceOverride: codexActiveSourceOverride) for implementation in ProviderCatalog.all { - if let contribution = implementation.settingsSnapshot(context: context) { - builder.apply(contribution) + 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)'") } + guard registration.accepts(contribution) else { + preconditionFailure("Mismatched settings snapshot section for provider '\(implementation.id.rawValue)'") + } + builder.apply(contribution) } return builder.build() } diff --git a/Sources/CodexBar/Providers/OpenRouter/OpenRouterProviderImplementation.swift b/Sources/CodexBar/Providers/OpenRouter/OpenRouterProviderImplementation.swift index e91337548c..db4736c007 100644 --- a/Sources/CodexBar/Providers/OpenRouter/OpenRouterProviderImplementation.swift +++ b/Sources/CodexBar/Providers/OpenRouter/OpenRouterProviderImplementation.swift @@ -16,12 +16,6 @@ struct OpenRouterProviderImplementation: ProviderImplementation { _ = settings.openRouterAPIToken } - @MainActor - func settingsSnapshot(context: ProviderSettingsSnapshotContext) -> ProviderSettingsSnapshotContribution? { - _ = context - return nil - } - @MainActor func isAvailable(context: ProviderAvailabilityContext) -> Bool { if OpenRouterSettingsReader.apiToken(environment: context.environment) != nil { diff --git a/Sources/CodexBar/Providers/Shared/ProviderImplementation.swift b/Sources/CodexBar/Providers/Shared/ProviderImplementation.swift index 84e709525a..65414f493a 100644 --- a/Sources/CodexBar/Providers/Shared/ProviderImplementation.swift +++ b/Sources/CodexBar/Providers/Shared/ProviderImplementation.swift @@ -160,7 +160,7 @@ extension ProviderImplementation { @MainActor func settingsSnapshot(context _: ProviderSettingsSnapshotContext) -> ProviderSettingsSnapshotContribution? { - nil + ProviderDescriptorRegistry.descriptor(for: self.id).settingsSection.defaultContribution } @MainActor diff --git a/Sources/CodexBarCLI/TokenAccountCLI.swift b/Sources/CodexBarCLI/TokenAccountCLI.swift index 19d2395c86..e45103d4be 100644 --- a/Sources/CodexBarCLI/TokenAccountCLI.swift +++ b/Sources/CodexBarCLI/TokenAccountCLI.swift @@ -296,36 +296,37 @@ struct TokenAccountCLIContext { zoommate: ProviderSettingsSnapshot.ZoomMateProviderSettings? = nil, notion: ProviderSettingsSnapshot.NotionProviderSettings? = nil) -> ProviderSettingsSnapshot { - ProviderSettingsSnapshot.make( - codex: codex, - claude: claude, - cursor: cursor, - opencode: opencode, - opencodego: opencodego, - alibaba: alibaba, - alibabaTokenPlan: alibabaTokenPlan, - qwenCloud: qwenCloud, - factory: factory, - minimax: minimax, - manus: manus, - zai: zai, - kilo: kilo, - kimi: kimi, - longcat: longcat, - augment: augment, - moonshot: moonshot, - amp: amp, - zoommate: zoommate, - notion: notion, - commandcode: commandcode, - ollama: ollama, - jetbrains: jetbrains, - perplexity: perplexity, - mimo: mimo, - abacus: abacus, - mistral: mistral, - qoder: qoder, - stepfun: stepfun) + ProviderSettingsSnapshot(contributions: [ + codex.map(ProviderSettingsSnapshotContribution.codex), + claude.map(ProviderSettingsSnapshotContribution.claude), + cursor.map(ProviderSettingsSnapshotContribution.cursor), + opencode.map(ProviderSettingsSnapshotContribution.opencode), + opencodego.map(ProviderSettingsSnapshotContribution.opencodego), + alibaba.map(ProviderSettingsSnapshotContribution.alibaba), + alibabaTokenPlan.map(ProviderSettingsSnapshotContribution.alibabaTokenPlan), + qwenCloud.map(ProviderSettingsSnapshotContribution.qwenCloud), + factory.map(ProviderSettingsSnapshotContribution.factory), + minimax.map(ProviderSettingsSnapshotContribution.minimax), + manus.map(ProviderSettingsSnapshotContribution.manus), + zai.map(ProviderSettingsSnapshotContribution.zai), + kilo.map(ProviderSettingsSnapshotContribution.kilo), + kimi.map(ProviderSettingsSnapshotContribution.kimi), + longcat.map(ProviderSettingsSnapshotContribution.longcat), + augment.map(ProviderSettingsSnapshotContribution.augment), + moonshot.map(ProviderSettingsSnapshotContribution.moonshot), + amp.map(ProviderSettingsSnapshotContribution.amp), + zoommate.map(ProviderSettingsSnapshotContribution.zoommate), + notion.map(ProviderSettingsSnapshotContribution.notion), + commandcode.map(ProviderSettingsSnapshotContribution.commandcode), + ollama.map(ProviderSettingsSnapshotContribution.ollama), + jetbrains.map(ProviderSettingsSnapshotContribution.jetbrains), + perplexity.map(ProviderSettingsSnapshotContribution.perplexity), + mimo.map(ProviderSettingsSnapshotContribution.mimo), + abacus.map(ProviderSettingsSnapshotContribution.abacus), + mistral.map(ProviderSettingsSnapshotContribution.mistral), + qoder.map(ProviderSettingsSnapshotContribution.qoder), + stepfun.map(ProviderSettingsSnapshotContribution.stepfun), + ].compactMap(\.self)) } private func makeCodexSettingsSnapshot( diff --git a/Sources/CodexBarCore/Providers/Abacus/AbacusProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Abacus/AbacusProviderDescriptor.swift index e5237bd552..a7ca745913 100644 --- a/Sources/CodexBarCore/Providers/Abacus/AbacusProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Abacus/AbacusProviderDescriptor.swift @@ -10,6 +10,7 @@ public enum AbacusProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .abacus, + settingsSection: .init(AbacusProviderSettingsKey.self), metadata: ProviderMetadata( id: .abacus, displayName: "Abacus AI", diff --git a/Sources/CodexBarCore/Providers/Abacus/AbacusProviderSettings.swift b/Sources/CodexBarCore/Providers/Abacus/AbacusProviderSettings.swift new file mode 100644 index 0000000000..464de73850 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Abacus/AbacusProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct AbacusProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum AbacusProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.abacus + public typealias Section = AbacusProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias AbacusProviderSettings = CodexBarCore.AbacusProviderSettings + public var abacus: AbacusProviderSettings? { + self[AbacusProviderSettingsKey.self] + } + + public static func make(abacus: AbacusProviderSettings?) -> Self { + self.make(abacus, for: AbacusProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func abacus(_ section: AbacusProviderSettings) -> Self { + Self(section, for: AbacusProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Alibaba/AlibabaCodingPlanProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Alibaba/AlibabaCodingPlanProviderDescriptor.swift index 484dff6885..cadfecdc7d 100644 --- a/Sources/CodexBarCore/Providers/Alibaba/AlibabaCodingPlanProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Alibaba/AlibabaCodingPlanProviderDescriptor.swift @@ -24,6 +24,7 @@ public enum AlibabaCodingPlanProviderDescriptor { return ProviderDescriptor( id: .alibaba, + settingsSection: .init(AlibabaCodingPlanProviderSettingsKey.self), metadata: ProviderMetadata( id: .alibaba, displayName: "Alibaba", diff --git a/Sources/CodexBarCore/Providers/Alibaba/AlibabaProviderSettings.swift b/Sources/CodexBarCore/Providers/Alibaba/AlibabaProviderSettings.swift new file mode 100644 index 0000000000..0da8ba2219 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Alibaba/AlibabaProviderSettings.swift @@ -0,0 +1,77 @@ +import Foundation + +public struct AlibabaCodingPlanProviderSettings: Sendable { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + public let apiRegion: AlibabaCodingPlanAPIRegion + + public init( + cookieSource: ProviderCookieSource = .auto, + manualCookieHeader: String? = nil, + apiRegion: AlibabaCodingPlanAPIRegion = .international) + { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + self.apiRegion = apiRegion + } +} + +public struct AlibabaTokenPlanProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + public let apiRegion: AlibabaTokenPlanAPIRegion + + public init( + cookieSource: ProviderCookieSource = .auto, + manualCookieHeader: String? = nil, + apiRegion: AlibabaTokenPlanAPIRegion = .international) + { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + self.apiRegion = apiRegion + } + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.init(cookieSource: cookieSource, manualCookieHeader: manualCookieHeader, apiRegion: .international) + } +} + +public enum AlibabaCodingPlanProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.alibaba + public typealias Section = AlibabaCodingPlanProviderSettings +} + +public enum AlibabaTokenPlanProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.alibabatokenplan + public typealias Section = AlibabaTokenPlanProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias AlibabaCodingPlanProviderSettings = CodexBarCore.AlibabaCodingPlanProviderSettings + public typealias AlibabaTokenPlanProviderSettings = CodexBarCore.AlibabaTokenPlanProviderSettings + public var alibaba: AlibabaCodingPlanProviderSettings? { + self[AlibabaCodingPlanProviderSettingsKey.self] + } + + public var alibabaTokenPlan: AlibabaTokenPlanProviderSettings? { + self[AlibabaTokenPlanProviderSettingsKey.self] + } + + public static func make(alibaba: AlibabaCodingPlanProviderSettings?) -> Self { + self.make(alibaba, for: AlibabaCodingPlanProviderSettingsKey.self) + } + + public static func make(alibabaTokenPlan: AlibabaTokenPlanProviderSettings?) -> Self { + self.make(alibabaTokenPlan, for: AlibabaTokenPlanProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func alibaba(_ section: AlibabaCodingPlanProviderSettings) -> Self { + Self(section, for: AlibabaCodingPlanProviderSettingsKey.self) + } + + public static func alibabaTokenPlan(_ section: AlibabaTokenPlanProviderSettings) -> Self { + Self(section, for: AlibabaTokenPlanProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Alibaba/AlibabaTokenPlanProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Alibaba/AlibabaTokenPlanProviderDescriptor.swift index f680f75642..9452eb670c 100644 --- a/Sources/CodexBarCore/Providers/Alibaba/AlibabaTokenPlanProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Alibaba/AlibabaTokenPlanProviderDescriptor.swift @@ -32,6 +32,7 @@ public enum AlibabaTokenPlanProviderDescriptor { return ProviderDescriptor( id: .alibabatokenplan, + settingsSection: .init(AlibabaTokenPlanProviderSettingsKey.self), metadata: ProviderMetadata( id: .alibabatokenplan, displayName: "Alibaba Token Plan", diff --git a/Sources/CodexBarCore/Providers/Amp/AmpProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Amp/AmpProviderDescriptor.swift index e1bc54e8e7..58fbf4fb3b 100644 --- a/Sources/CodexBarCore/Providers/Amp/AmpProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Amp/AmpProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum AmpProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .amp, + settingsSection: .init(AmpProviderSettingsKey.self), metadata: ProviderMetadata( id: .amp, displayName: "Amp", diff --git a/Sources/CodexBarCore/Providers/Amp/AmpProviderSettings.swift b/Sources/CodexBarCore/Providers/Amp/AmpProviderSettings.swift new file mode 100644 index 0000000000..7e638c5e90 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Amp/AmpProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct AmpProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum AmpProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.amp + public typealias Section = AmpProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias AmpProviderSettings = CodexBarCore.AmpProviderSettings + public var amp: AmpProviderSettings? { + self[AmpProviderSettingsKey.self] + } + + public static func make(amp: AmpProviderSettings?) -> Self { + self.make(amp, for: AmpProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func amp(_ section: AmpProviderSettings) -> Self { + Self(section, for: AmpProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Augment/AugmentProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Augment/AugmentProviderDescriptor.swift index a96fe2ec68..183617a015 100644 --- a/Sources/CodexBarCore/Providers/Augment/AugmentProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Augment/AugmentProviderDescriptor.swift @@ -30,6 +30,7 @@ public enum AugmentProviderDescriptor { return ProviderDescriptor( id: .augment, + settingsSection: .init(AugmentProviderSettingsKey.self), metadata: ProviderMetadata( id: .augment, displayName: "Augment", diff --git a/Sources/CodexBarCore/Providers/Augment/AugmentProviderSettings.swift b/Sources/CodexBarCore/Providers/Augment/AugmentProviderSettings.swift new file mode 100644 index 0000000000..7bcf6eb6f0 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Augment/AugmentProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct AugmentProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum AugmentProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.augment + public typealias Section = AugmentProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias AugmentProviderSettings = CodexBarCore.AugmentProviderSettings + public var augment: AugmentProviderSettings? { + self[AugmentProviderSettingsKey.self] + } + + public static func make(augment: AugmentProviderSettings?) -> Self { + self.make(augment, for: AugmentProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func augment(_ section: AugmentProviderSettings) -> Self { + Self(section, for: AugmentProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift index 563fa9125a..8d205271c4 100644 --- a/Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum ClaudeProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .claude, + settingsSection: .init(ClaudeProviderSettingsKey.self), metadata: ProviderMetadata( id: .claude, displayName: "Claude", diff --git a/Sources/CodexBarCore/Providers/Claude/ClaudeProviderSettings.swift b/Sources/CodexBarCore/Providers/Claude/ClaudeProviderSettings.swift new file mode 100644 index 0000000000..1af4c1f154 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Claude/ClaudeProviderSettings.swift @@ -0,0 +1,45 @@ +import Foundation + +public struct ClaudeProviderSettings: Sendable { + public let usageDataSource: ClaudeUsageDataSource + public let webExtrasEnabled: Bool + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + public let organizationID: String? + + public init( + usageDataSource: ClaudeUsageDataSource, + webExtrasEnabled: Bool, + cookieSource: ProviderCookieSource, + manualCookieHeader: String?, + organizationID: String? = nil) + { + self.usageDataSource = usageDataSource + self.webExtrasEnabled = webExtrasEnabled + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + self.organizationID = organizationID + } +} + +public enum ClaudeProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.claude + public typealias Section = ClaudeProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias ClaudeProviderSettings = CodexBarCore.ClaudeProviderSettings + public var claude: ClaudeProviderSettings? { + self[ClaudeProviderSettingsKey.self] + } + + public static func make(claude: ClaudeProviderSettings?) -> Self { + self.make(claude, for: ClaudeProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func claude(_ section: ClaudeProviderSettings) -> Self { + Self(section, for: ClaudeProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Codex/CodexProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Codex/CodexProviderDescriptor.swift index d4de65f6ba..73f2ed3a2e 100644 --- a/Sources/CodexBarCore/Providers/Codex/CodexProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Codex/CodexProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum CodexProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .codex, + settingsSection: .init(CodexProviderSettingsKey.self), metadata: ProviderMetadata( id: .codex, displayName: "Codex", diff --git a/Sources/CodexBarCore/Providers/Codex/CodexProviderSettings.swift b/Sources/CodexBarCore/Providers/Codex/CodexProviderSettings.swift new file mode 100644 index 0000000000..11d64c2764 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Codex/CodexProviderSettings.swift @@ -0,0 +1,54 @@ +import Foundation + +public struct CodexProviderSettings: Sendable { + public let usageDataSource: CodexUsageDataSource + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + public let managedAccountStoreUnreadable: Bool + public let managedAccountTargetUnavailable: Bool + public let profileAccountTargetUnavailable: Bool + public let openAIWebCacheScope: CookieHeaderCache.Scope? + public let dashboardAuthorityKnownOwners: [CodexDashboardKnownOwnerCandidate] + + public init( + usageDataSource: CodexUsageDataSource, + cookieSource: ProviderCookieSource, + manualCookieHeader: String?, + managedAccountStoreUnreadable: Bool = false, + managedAccountTargetUnavailable: Bool = false, + profileAccountTargetUnavailable: Bool = false, + openAIWebCacheScope: CookieHeaderCache.Scope? = nil, + dashboardAuthorityKnownOwners: [CodexDashboardKnownOwnerCandidate] = []) + { + self.usageDataSource = usageDataSource + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + self.managedAccountStoreUnreadable = managedAccountStoreUnreadable + self.managedAccountTargetUnavailable = managedAccountTargetUnavailable + self.profileAccountTargetUnavailable = profileAccountTargetUnavailable + self.openAIWebCacheScope = openAIWebCacheScope + self.dashboardAuthorityKnownOwners = dashboardAuthorityKnownOwners + } +} + +public enum CodexProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.codex + public typealias Section = CodexProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias CodexProviderSettings = CodexBarCore.CodexProviderSettings + public var codex: CodexProviderSettings? { + self[CodexProviderSettingsKey.self] + } + + public static func make(codex: CodexProviderSettings?) -> Self { + self.make(codex, for: CodexProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func codex(_ section: CodexProviderSettings) -> Self { + Self(section, for: CodexProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/CommandCode/CommandCodeProviderDescriptor.swift b/Sources/CodexBarCore/Providers/CommandCode/CommandCodeProviderDescriptor.swift index 0481bd85f9..0f0d21c122 100644 --- a/Sources/CodexBarCore/Providers/CommandCode/CommandCodeProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/CommandCode/CommandCodeProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum CommandCodeProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .commandcode, + settingsSection: .init(CommandCodeProviderSettingsKey.self), metadata: ProviderMetadata( id: .commandcode, displayName: "Command Code", diff --git a/Sources/CodexBarCore/Providers/CommandCode/CommandCodeProviderSettings.swift b/Sources/CodexBarCore/Providers/CommandCode/CommandCodeProviderSettings.swift new file mode 100644 index 0000000000..b4068c021b --- /dev/null +++ b/Sources/CodexBarCore/Providers/CommandCode/CommandCodeProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct CommandCodeProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum CommandCodeProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.commandcode + public typealias Section = CommandCodeProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias CommandCodeProviderSettings = CodexBarCore.CommandCodeProviderSettings + public var commandcode: CommandCodeProviderSettings? { + self[CommandCodeProviderSettingsKey.self] + } + + public static func make(commandcode: CommandCodeProviderSettings?) -> Self { + self.make(commandcode, for: CommandCodeProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func commandcode(_ section: CommandCodeProviderSettings) -> Self { + Self(section, for: CommandCodeProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Copilot/CopilotProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Copilot/CopilotProviderDescriptor.swift index 0652242b6e..2a44824862 100644 --- a/Sources/CodexBarCore/Providers/Copilot/CopilotProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Copilot/CopilotProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum CopilotProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .copilot, + settingsSection: .init(CopilotProviderSettingsKey.self), metadata: ProviderMetadata( id: .copilot, displayName: "Copilot", diff --git a/Sources/CodexBarCore/Providers/Copilot/CopilotProviderSettings.swift b/Sources/CodexBarCore/Providers/Copilot/CopilotProviderSettings.swift new file mode 100644 index 0000000000..74340776e6 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Copilot/CopilotProviderSettings.swift @@ -0,0 +1,48 @@ +import Foundation + +public struct CopilotProviderSettings: Sendable { + public let apiToken: String? + public let enterpriseHost: String? + public let selectedAccountExternalIdentifier: String? + public let budgetExtrasEnabled: Bool + public let budgetCookieSource: ProviderCookieSource + public let manualBudgetCookieHeader: String? + + public init( + apiToken: String? = nil, + enterpriseHost: String? = nil, + selectedAccountExternalIdentifier: String? = nil, + budgetExtrasEnabled: Bool = false, + budgetCookieSource: ProviderCookieSource = .auto, + manualBudgetCookieHeader: String? = nil) + { + self.apiToken = apiToken + self.enterpriseHost = enterpriseHost + self.selectedAccountExternalIdentifier = selectedAccountExternalIdentifier + self.budgetExtrasEnabled = budgetExtrasEnabled + self.budgetCookieSource = budgetCookieSource + self.manualBudgetCookieHeader = manualBudgetCookieHeader + } +} + +public enum CopilotProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.copilot + public typealias Section = CopilotProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias CopilotProviderSettings = CodexBarCore.CopilotProviderSettings + public var copilot: CopilotProviderSettings? { + self[CopilotProviderSettingsKey.self] + } + + public static func make(copilot: CopilotProviderSettings?) -> Self { + self.make(copilot, for: CopilotProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func copilot(_ section: CopilotProviderSettings) -> Self { + Self(section, for: CopilotProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Cursor/CursorProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Cursor/CursorProviderDescriptor.swift index 7907b76131..ebda00ca53 100644 --- a/Sources/CodexBarCore/Providers/Cursor/CursorProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Cursor/CursorProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum CursorProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .cursor, + settingsSection: .init(CursorProviderSettingsKey.self), metadata: ProviderMetadata( id: .cursor, displayName: "Cursor", diff --git a/Sources/CodexBarCore/Providers/Cursor/CursorProviderSettings.swift b/Sources/CodexBarCore/Providers/Cursor/CursorProviderSettings.swift new file mode 100644 index 0000000000..54a4d71216 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Cursor/CursorProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct CursorProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum CursorProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.cursor + public typealias Section = CursorProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias CursorProviderSettings = CodexBarCore.CursorProviderSettings + public var cursor: CursorProviderSettings? { + self[CursorProviderSettingsKey.self] + } + + public static func make(cursor: CursorProviderSettings?) -> Self { + self.make(cursor, for: CursorProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func cursor(_ section: CursorProviderSettings) -> Self { + Self(section, for: CursorProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Devin/DevinProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Devin/DevinProviderDescriptor.swift index 9ca539afd5..5b399276f7 100644 --- a/Sources/CodexBarCore/Providers/Devin/DevinProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Devin/DevinProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum DevinProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .devin, + settingsSection: .init(DevinProviderSettingsKey.self), metadata: ProviderMetadata( id: .devin, displayName: "Devin", diff --git a/Sources/CodexBarCore/Providers/Devin/DevinProviderSettings.swift b/Sources/CodexBarCore/Providers/Devin/DevinProviderSettings.swift new file mode 100644 index 0000000000..50a4be323a --- /dev/null +++ b/Sources/CodexBarCore/Providers/Devin/DevinProviderSettings.swift @@ -0,0 +1,35 @@ +import Foundation + +public struct DevinProviderSettings: Sendable { + public let cookieSource: ProviderCookieSource + public let manualBearerToken: String? + public let organization: String? + + public init(cookieSource: ProviderCookieSource, manualBearerToken: String?, organization: String?) { + self.cookieSource = cookieSource + self.manualBearerToken = manualBearerToken + self.organization = organization + } +} + +public enum DevinProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.devin + public typealias Section = DevinProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias DevinProviderSettings = CodexBarCore.DevinProviderSettings + public var devin: DevinProviderSettings? { + self[DevinProviderSettingsKey.self] + } + + public static func make(devin: DevinProviderSettings?) -> Self { + self.make(devin, for: DevinProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func devin(_ section: DevinProviderSettings) -> Self { + Self(section, for: DevinProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Factory/FactoryProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Factory/FactoryProviderDescriptor.swift index 1eae19e46e..c60cb7fbd6 100644 --- a/Sources/CodexBarCore/Providers/Factory/FactoryProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Factory/FactoryProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum FactoryProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .factory, + settingsSection: .init(FactoryProviderSettingsKey.self), metadata: ProviderMetadata( id: .factory, displayName: "Droid", diff --git a/Sources/CodexBarCore/Providers/Factory/FactoryProviderSettings.swift b/Sources/CodexBarCore/Providers/Factory/FactoryProviderSettings.swift new file mode 100644 index 0000000000..ad88615af1 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Factory/FactoryProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct FactoryProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum FactoryProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.factory + public typealias Section = FactoryProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias FactoryProviderSettings = CodexBarCore.FactoryProviderSettings + public var factory: FactoryProviderSettings? { + self[FactoryProviderSettingsKey.self] + } + + public static func make(factory: FactoryProviderSettings?) -> Self { + self.make(factory, for: FactoryProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func factory(_ section: FactoryProviderSettings) -> Self { + Self(section, for: FactoryProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/JetBrains/JetBrainsProviderDescriptor.swift b/Sources/CodexBarCore/Providers/JetBrains/JetBrainsProviderDescriptor.swift index 19ec4ebe4c..1d30bc4f2d 100644 --- a/Sources/CodexBarCore/Providers/JetBrains/JetBrainsProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/JetBrains/JetBrainsProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum JetBrainsProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .jetbrains, + settingsSection: .init(JetBrainsProviderSettingsKey.self), metadata: ProviderMetadata( id: .jetbrains, displayName: "JetBrains AI", diff --git a/Sources/CodexBarCore/Providers/JetBrains/JetBrainsProviderSettings.swift b/Sources/CodexBarCore/Providers/JetBrains/JetBrainsProviderSettings.swift new file mode 100644 index 0000000000..3a744e3401 --- /dev/null +++ b/Sources/CodexBarCore/Providers/JetBrains/JetBrainsProviderSettings.swift @@ -0,0 +1,35 @@ +import Foundation + +public struct JetBrainsProviderSettings: Sendable { + public let ideBasePath: String? + + public init(ideBasePath: String?) { + self.ideBasePath = ideBasePath + } +} + +public enum JetBrainsProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.jetbrains + public typealias Section = JetBrainsProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias JetBrainsProviderSettings = CodexBarCore.JetBrainsProviderSettings + public var jetbrains: JetBrainsProviderSettings? { + self[JetBrainsProviderSettingsKey.self] + } + + public var jetbrainsIDEBasePath: String? { + self.jetbrains?.ideBasePath + } + + public static func make(jetbrains: JetBrainsProviderSettings?) -> Self { + self.make(jetbrains, for: JetBrainsProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func jetbrains(_ section: JetBrainsProviderSettings) -> Self { + Self(section, for: JetBrainsProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Kilo/KiloProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Kilo/KiloProviderDescriptor.swift index 293632b3dd..88c17f7365 100644 --- a/Sources/CodexBarCore/Providers/Kilo/KiloProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Kilo/KiloProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum KiloProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .kilo, + settingsSection: .init(KiloProviderSettingsKey.self), metadata: ProviderMetadata( id: .kilo, displayName: "Kilo", diff --git a/Sources/CodexBarCore/Providers/Kilo/KiloProviderSettings.swift b/Sources/CodexBarCore/Providers/Kilo/KiloProviderSettings.swift new file mode 100644 index 0000000000..757881ea66 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Kilo/KiloProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct KiloProviderSettings: Sendable { + public let usageDataSource: KiloUsageDataSource + public let extrasEnabled: Bool + + public init(usageDataSource: KiloUsageDataSource, extrasEnabled: Bool) { + self.usageDataSource = usageDataSource + self.extrasEnabled = extrasEnabled + } +} + +public enum KiloProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.kilo + public typealias Section = KiloProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias KiloProviderSettings = CodexBarCore.KiloProviderSettings + public var kilo: KiloProviderSettings? { + self[KiloProviderSettingsKey.self] + } + + public static func make(kilo: KiloProviderSettings?) -> Self { + self.make(kilo, for: KiloProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func kilo(_ section: KiloProviderSettings) -> Self { + Self(section, for: KiloProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Kimi/KimiProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Kimi/KimiProviderDescriptor.swift index e9dfe77bdc..b436171994 100644 --- a/Sources/CodexBarCore/Providers/Kimi/KimiProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Kimi/KimiProviderDescriptor.swift @@ -8,6 +8,7 @@ public enum KimiProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .kimi, + settingsSection: .init(KimiProviderSettingsKey.self), metadata: ProviderMetadata( id: .kimi, displayName: "Kimi Code", diff --git a/Sources/CodexBarCore/Providers/Kimi/KimiProviderSettings.swift b/Sources/CodexBarCore/Providers/Kimi/KimiProviderSettings.swift new file mode 100644 index 0000000000..187096e992 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Kimi/KimiProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct KimiProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum KimiProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.kimi + public typealias Section = KimiProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias KimiProviderSettings = CodexBarCore.KimiProviderSettings + public var kimi: KimiProviderSettings? { + self[KimiProviderSettingsKey.self] + } + + public static func make(kimi: KimiProviderSettings?) -> Self { + self.make(kimi, for: KimiProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func kimi(_ section: KimiProviderSettings) -> Self { + Self(section, for: KimiProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/LongCat/LongCatProviderDescriptor.swift b/Sources/CodexBarCore/Providers/LongCat/LongCatProviderDescriptor.swift index 6e92cec79f..f1d4b0510b 100644 --- a/Sources/CodexBarCore/Providers/LongCat/LongCatProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/LongCat/LongCatProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum LongCatProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .longcat, + settingsSection: .init(LongCatProviderSettingsKey.self), metadata: ProviderMetadata( id: .longcat, displayName: "LongCat", diff --git a/Sources/CodexBarCore/Providers/LongCat/LongCatProviderSettings.swift b/Sources/CodexBarCore/Providers/LongCat/LongCatProviderSettings.swift new file mode 100644 index 0000000000..ba484bc556 --- /dev/null +++ b/Sources/CodexBarCore/Providers/LongCat/LongCatProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct LongCatProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum LongCatProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.longcat + public typealias Section = LongCatProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias LongCatProviderSettings = CodexBarCore.LongCatProviderSettings + public var longcat: LongCatProviderSettings? { + self[LongCatProviderSettingsKey.self] + } + + public static func make(longcat: LongCatProviderSettings?) -> Self { + self.make(longcat, for: LongCatProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func longcat(_ section: LongCatProviderSettings) -> Self { + Self(section, for: LongCatProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Manus/ManusProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Manus/ManusProviderDescriptor.swift index 58a4f0d41a..dd14c7a421 100644 --- a/Sources/CodexBarCore/Providers/Manus/ManusProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Manus/ManusProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum ManusProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .manus, + settingsSection: .init(ManusProviderSettingsKey.self), metadata: ProviderMetadata( id: .manus, displayName: "Manus", diff --git a/Sources/CodexBarCore/Providers/Manus/ManusProviderSettings.swift b/Sources/CodexBarCore/Providers/Manus/ManusProviderSettings.swift new file mode 100644 index 0000000000..6ae54181f6 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Manus/ManusProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct ManusProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum ManusProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.manus + public typealias Section = ManusProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias ManusProviderSettings = CodexBarCore.ManusProviderSettings + public var manus: ManusProviderSettings? { + self[ManusProviderSettingsKey.self] + } + + public static func make(manus: ManusProviderSettings?) -> Self { + self.make(manus, for: ManusProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func manus(_ section: ManusProviderSettings) -> Self { + Self(section, for: ManusProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/MiMo/MiMoProviderDescriptor.swift b/Sources/CodexBarCore/Providers/MiMo/MiMoProviderDescriptor.swift index 0b8aac38a2..3a1afa50df 100644 --- a/Sources/CodexBarCore/Providers/MiMo/MiMoProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/MiMo/MiMoProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum MiMoProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .mimo, + settingsSection: .init(MiMoProviderSettingsKey.self), metadata: ProviderMetadata( id: .mimo, displayName: "Xiaomi MiMo", diff --git a/Sources/CodexBarCore/Providers/MiMo/MiMoProviderSettings.swift b/Sources/CodexBarCore/Providers/MiMo/MiMoProviderSettings.swift new file mode 100644 index 0000000000..5c81aa5a07 --- /dev/null +++ b/Sources/CodexBarCore/Providers/MiMo/MiMoProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct MiMoProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum MiMoProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.mimo + public typealias Section = MiMoProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias MiMoProviderSettings = CodexBarCore.MiMoProviderSettings + public var mimo: MiMoProviderSettings? { + self[MiMoProviderSettingsKey.self] + } + + public static func make(mimo: MiMoProviderSettings?) -> Self { + self.make(mimo, for: MiMoProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func mimo(_ section: MiMoProviderSettings) -> Self { + Self(section, for: MiMoProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift b/Sources/CodexBarCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift index 757f200e4a..f700c43d38 100644 --- a/Sources/CodexBarCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum MiniMaxProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .minimax, + settingsSection: .init(MiniMaxProviderSettingsKey.self), metadata: ProviderMetadata( id: .minimax, displayName: "MiniMax", diff --git a/Sources/CodexBarCore/Providers/MiniMax/MiniMaxProviderSettings.swift b/Sources/CodexBarCore/Providers/MiniMax/MiniMaxProviderSettings.swift new file mode 100644 index 0000000000..c86f51a4d3 --- /dev/null +++ b/Sources/CodexBarCore/Providers/MiniMax/MiniMaxProviderSettings.swift @@ -0,0 +1,39 @@ +import Foundation + +public struct MiniMaxProviderSettings: Sendable { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + public let apiRegion: MiniMaxAPIRegion + + public init( + cookieSource: ProviderCookieSource, + manualCookieHeader: String?, + apiRegion: MiniMaxAPIRegion = .global) + { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + self.apiRegion = apiRegion + } +} + +public enum MiniMaxProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.minimax + public typealias Section = MiniMaxProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias MiniMaxProviderSettings = CodexBarCore.MiniMaxProviderSettings + public var minimax: MiniMaxProviderSettings? { + self[MiniMaxProviderSettingsKey.self] + } + + public static func make(minimax: MiniMaxProviderSettings?) -> Self { + self.make(minimax, for: MiniMaxProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func minimax(_ section: MiniMaxProviderSettings) -> Self { + Self(section, for: MiniMaxProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Mistral/MistralProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Mistral/MistralProviderDescriptor.swift index 11e00abb72..bd57abb148 100644 --- a/Sources/CodexBarCore/Providers/Mistral/MistralProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Mistral/MistralProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum MistralProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .mistral, + settingsSection: .init(MistralProviderSettingsKey.self), metadata: ProviderMetadata( id: .mistral, displayName: "Mistral", diff --git a/Sources/CodexBarCore/Providers/Mistral/MistralProviderSettings.swift b/Sources/CodexBarCore/Providers/Mistral/MistralProviderSettings.swift new file mode 100644 index 0000000000..f774ce4a55 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Mistral/MistralProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct MistralProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum MistralProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.mistral + public typealias Section = MistralProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias MistralProviderSettings = CodexBarCore.MistralProviderSettings + public var mistral: MistralProviderSettings? { + self[MistralProviderSettingsKey.self] + } + + public static func make(mistral: MistralProviderSettings?) -> Self { + self.make(mistral, for: MistralProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func mistral(_ section: MistralProviderSettings) -> Self { + Self(section, for: MistralProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Moonshot/MoonshotProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Moonshot/MoonshotProviderDescriptor.swift index 5e10db9248..9e6bf85a02 100644 --- a/Sources/CodexBarCore/Providers/Moonshot/MoonshotProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Moonshot/MoonshotProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum MoonshotProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .moonshot, + settingsSection: .init(MoonshotProviderSettingsKey.self), metadata: ProviderMetadata( id: .moonshot, displayName: "Moonshot / Kimi Open Platform", diff --git a/Sources/CodexBarCore/Providers/Moonshot/MoonshotProviderSettings.swift b/Sources/CodexBarCore/Providers/Moonshot/MoonshotProviderSettings.swift new file mode 100644 index 0000000000..2efb4764e0 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Moonshot/MoonshotProviderSettings.swift @@ -0,0 +1,31 @@ +import Foundation + +public struct MoonshotProviderSettings: Sendable { + public let region: MoonshotRegion? + + public init(region: MoonshotRegion? = nil) { + self.region = region + } +} + +public enum MoonshotProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.moonshot + public typealias Section = MoonshotProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias MoonshotProviderSettings = CodexBarCore.MoonshotProviderSettings + public var moonshot: MoonshotProviderSettings? { + self[MoonshotProviderSettingsKey.self] + } + + public static func make(moonshot: MoonshotProviderSettings?) -> Self { + self.make(moonshot, for: MoonshotProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func moonshot(_ section: MoonshotProviderSettings) -> Self { + Self(section, for: MoonshotProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Notion/NotionProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Notion/NotionProviderDescriptor.swift index 7cb04d47dd..849a943244 100644 --- a/Sources/CodexBarCore/Providers/Notion/NotionProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Notion/NotionProviderDescriptor.swift @@ -11,6 +11,7 @@ public enum NotionProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .notion, + settingsSection: .init(NotionProviderSettingsKey.self), metadata: ProviderMetadata( id: .notion, displayName: "Notion AI", diff --git a/Sources/CodexBarCore/Providers/Notion/NotionProviderSettings.swift b/Sources/CodexBarCore/Providers/Notion/NotionProviderSettings.swift new file mode 100644 index 0000000000..4b3a7fca24 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Notion/NotionProviderSettings.swift @@ -0,0 +1,37 @@ +import Foundation + +/// Deliberately not a `ProviderCookieSettings`: that protocol's initializer takes only the two +/// cookie fields, so conforming would let generic construction silently drop `workspaceID`. +public struct NotionProviderSettings: Sendable { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + public let workspaceID: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?, workspaceID: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + self.workspaceID = workspaceID + } +} + +public enum NotionProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.notion + public typealias Section = NotionProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias NotionProviderSettings = CodexBarCore.NotionProviderSettings + public var notion: NotionProviderSettings? { + self[NotionProviderSettingsKey.self] + } + + public static func make(notion: NotionProviderSettings?) -> Self { + self.make(notion, for: NotionProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func notion(_ section: NotionProviderSettings) -> Self { + Self(section, for: NotionProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Ollama/OllamaProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Ollama/OllamaProviderDescriptor.swift index f18b5122b7..e280e3bc28 100644 --- a/Sources/CodexBarCore/Providers/Ollama/OllamaProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Ollama/OllamaProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum OllamaProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .ollama, + settingsSection: .init(OllamaProviderSettingsKey.self), metadata: ProviderMetadata( id: .ollama, displayName: "Ollama", diff --git a/Sources/CodexBarCore/Providers/Ollama/OllamaProviderSettings.swift b/Sources/CodexBarCore/Providers/Ollama/OllamaProviderSettings.swift new file mode 100644 index 0000000000..d7d36d9562 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Ollama/OllamaProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct OllamaProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum OllamaProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.ollama + public typealias Section = OllamaProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias OllamaProviderSettings = CodexBarCore.OllamaProviderSettings + public var ollama: OllamaProviderSettings? { + self[OllamaProviderSettingsKey.self] + } + + public static func make(ollama: OllamaProviderSettings?) -> Self { + self.make(ollama, for: OllamaProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func ollama(_ section: OllamaProviderSettings) -> Self { + Self(section, for: OllamaProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/OpenCode/OpenCodeProviderDescriptor.swift b/Sources/CodexBarCore/Providers/OpenCode/OpenCodeProviderDescriptor.swift index f2c3f7154a..8b9d7d6e40 100644 --- a/Sources/CodexBarCore/Providers/OpenCode/OpenCodeProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/OpenCode/OpenCodeProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum OpenCodeProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .opencode, + settingsSection: .init(OpenCodeProviderSettingsKey.self), metadata: ProviderMetadata( id: .opencode, displayName: "OpenCode", diff --git a/Sources/CodexBarCore/Providers/OpenCode/OpenCodeProviderSettings.swift b/Sources/CodexBarCore/Providers/OpenCode/OpenCodeProviderSettings.swift new file mode 100644 index 0000000000..910ba56eb4 --- /dev/null +++ b/Sources/CodexBarCore/Providers/OpenCode/OpenCodeProviderSettings.swift @@ -0,0 +1,35 @@ +import Foundation + +public struct OpenCodeProviderSettings: Sendable { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + public let workspaceID: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?, workspaceID: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + self.workspaceID = workspaceID + } +} + +public enum OpenCodeProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.opencode + public typealias Section = OpenCodeProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias OpenCodeProviderSettings = CodexBarCore.OpenCodeProviderSettings + public var opencode: OpenCodeProviderSettings? { + self[OpenCodeProviderSettingsKey.self] + } + + public static func make(opencode: OpenCodeProviderSettings?) -> Self { + self.make(opencode, for: OpenCodeProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func opencode(_ section: OpenCodeProviderSettings) -> Self { + Self(section, for: OpenCodeProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/OpenCodeGo/OpenCodeGoProviderDescriptor.swift b/Sources/CodexBarCore/Providers/OpenCodeGo/OpenCodeGoProviderDescriptor.swift index b44a25a372..6ee3d31586 100644 --- a/Sources/CodexBarCore/Providers/OpenCodeGo/OpenCodeGoProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/OpenCodeGo/OpenCodeGoProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum OpenCodeGoProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .opencodego, + settingsSection: .init(OpenCodeGoProviderSettingsKey.self), metadata: ProviderMetadata( id: .opencodego, displayName: "OpenCode Go", diff --git a/Sources/CodexBarCore/Providers/OpenCodeGo/OpenCodeGoProviderSettings.swift b/Sources/CodexBarCore/Providers/OpenCodeGo/OpenCodeGoProviderSettings.swift new file mode 100644 index 0000000000..b6b4001a9e --- /dev/null +++ b/Sources/CodexBarCore/Providers/OpenCodeGo/OpenCodeGoProviderSettings.swift @@ -0,0 +1,22 @@ +import Foundation + +public enum OpenCodeGoProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.opencodego + public typealias Section = OpenCodeProviderSettings +} + +extension ProviderSettingsSnapshot { + public var opencodego: OpenCodeProviderSettings? { + self[OpenCodeGoProviderSettingsKey.self] + } + + public static func make(opencodego: OpenCodeProviderSettings?) -> Self { + self.make(opencodego, for: OpenCodeGoProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func opencodego(_ section: OpenCodeProviderSettings) -> Self { + Self(section, for: OpenCodeGoProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Perplexity/PerplexityProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Perplexity/PerplexityProviderDescriptor.swift index b2ebf70754..a32d34e7d0 100644 --- a/Sources/CodexBarCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Perplexity/PerplexityProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum PerplexityProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .perplexity, + settingsSection: .init(PerplexityProviderSettingsKey.self), metadata: ProviderMetadata( id: .perplexity, displayName: "Perplexity", diff --git a/Sources/CodexBarCore/Providers/Perplexity/PerplexityProviderSettings.swift b/Sources/CodexBarCore/Providers/Perplexity/PerplexityProviderSettings.swift new file mode 100644 index 0000000000..e69e944743 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Perplexity/PerplexityProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct PerplexityProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum PerplexityProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.perplexity + public typealias Section = PerplexityProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias PerplexityProviderSettings = CodexBarCore.PerplexityProviderSettings + public var perplexity: PerplexityProviderSettings? { + self[PerplexityProviderSettingsKey.self] + } + + public static func make(perplexity: PerplexityProviderSettings?) -> Self { + self.make(perplexity, for: PerplexityProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func perplexity(_ section: PerplexityProviderSettings) -> Self { + Self(section, for: PerplexityProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/ProviderDescriptor.swift b/Sources/CodexBarCore/Providers/ProviderDescriptor.swift index 67720cd4db..eaab61584e 100644 --- a/Sources/CodexBarCore/Providers/ProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/ProviderDescriptor.swift @@ -106,11 +106,13 @@ public struct ProviderDescriptor: Sendable { public let branding: ProviderBranding public let tokenCost: ProviderTokenCostConfig public let pace: ProviderPaceCapability + public let settingsSection: ProviderSettingsSectionRegistration public let fetchPlan: ProviderFetchPlan public let cli: ProviderCLIConfig public init( id: UsageProvider, + settingsSection: ProviderSettingsSectionRegistration? = nil, metadata: ProviderMetadata, branding: ProviderBranding, tokenCost: ProviderTokenCostConfig, @@ -119,6 +121,7 @@ public struct ProviderDescriptor: Sendable { cli: ProviderCLIConfig) { self.id = id + self.settingsSection = settingsSection ?? .empty(for: id.instanceID) self.metadata = metadata self.branding = branding self.tokenCost = tokenCost diff --git a/Sources/CodexBarCore/Providers/ProviderSettingsSnapshot.swift b/Sources/CodexBarCore/Providers/ProviderSettingsSnapshot.swift index ef213bc703..71b3855052 100644 --- a/Sources/CodexBarCore/Providers/ProviderSettingsSnapshot.swift +++ b/Sources/CodexBarCore/Providers/ProviderSettingsSnapshot.swift @@ -1,783 +1,142 @@ import Foundation -public protocol ProviderCookieSettings: Sendable { - var cookieSource: ProviderCookieSource { get } - var manualCookieHeader: String? { get } +/// Associates one provider instance with its concrete runtime settings payload. +public protocol ProviderSettingsSectionKey: Sendable { + associatedtype Section: Sendable - init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) + static var providerID: ProviderInstanceID { get } } public struct ProviderSettingsSnapshot: Sendable { - public static func make( + private let sections: [ProviderInstanceID: any Sendable] + + public let debugMenuEnabled: Bool + public let debugKeepCLISessionsAlive: Bool + + public init( debugMenuEnabled: Bool = false, debugKeepCLISessionsAlive: Bool = false, - codex: CodexProviderSettings? = nil, - claude: ClaudeProviderSettings? = nil, - cursor: CursorProviderSettings? = nil, - opencode: OpenCodeProviderSettings? = nil, - opencodego: OpenCodeProviderSettings? = nil, - alibaba: AlibabaCodingPlanProviderSettings? = nil, - alibabaTokenPlan: AlibabaTokenPlanProviderSettings? = nil, - qwenCloud: QwenCloudProviderSettings? = nil, - factory: FactoryProviderSettings? = nil, - minimax: MiniMaxProviderSettings? = nil, - manus: ManusProviderSettings? = nil, - zai: ZaiProviderSettings? = nil, - copilot: CopilotProviderSettings? = nil, - kilo: KiloProviderSettings? = nil, - kimi: KimiProviderSettings? = nil, - longcat: LongCatProviderSettings? = nil, - augment: AugmentProviderSettings? = nil, - moonshot: MoonshotProviderSettings? = nil, - amp: AmpProviderSettings? = nil, - t3chat: T3ChatProviderSettings? = nil, - zoommate: ZoomMateProviderSettings? = nil, - notion: NotionProviderSettings? = nil, - devin: DevinProviderSettings? = nil, - commandcode: CommandCodeProviderSettings? = nil, - ollama: OllamaProviderSettings? = nil, - jetbrains: JetBrainsProviderSettings? = nil, - windsurf: WindsurfProviderSettings? = nil, - perplexity: PerplexityProviderSettings? = nil, - mimo: MiMoProviderSettings? = nil, - abacus: AbacusProviderSettings? = nil, - mistral: MistralProviderSettings? = nil, - qoder: QoderProviderSettings? = nil, - stepfun: StepFunProviderSettings? = nil) -> ProviderSettingsSnapshot + contributions: [ProviderSettingsSnapshotContribution] = []) { - ProviderSettingsSnapshot( - debugMenuEnabled: debugMenuEnabled, - debugKeepCLISessionsAlive: debugKeepCLISessionsAlive, - codex: codex, - claude: claude, - cursor: cursor, - opencode: opencode, - opencodego: opencodego, - alibaba: alibaba, - alibabaTokenPlan: alibabaTokenPlan, - qwenCloud: qwenCloud, - factory: factory, - minimax: minimax, - manus: manus, - zai: zai, - copilot: copilot, - kilo: kilo, - kimi: kimi, - longcat: longcat, - augment: augment, - moonshot: moonshot, - amp: amp, - t3chat: t3chat, - zoommate: zoommate, - notion: notion, - devin: devin, - commandcode: commandcode, - ollama: ollama, - jetbrains: jetbrains, - windsurf: windsurf, - perplexity: perplexity, - mimo: mimo, - abacus: abacus, - mistral: mistral, - qoder: qoder, - stepfun: stepfun) - } - - public struct CodexProviderSettings: Sendable { - public let usageDataSource: CodexUsageDataSource - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - public let managedAccountStoreUnreadable: Bool - public let managedAccountTargetUnavailable: Bool - public let profileAccountTargetUnavailable: Bool - public let openAIWebCacheScope: CookieHeaderCache.Scope? - public let dashboardAuthorityKnownOwners: [CodexDashboardKnownOwnerCandidate] - - public init( - usageDataSource: CodexUsageDataSource, - cookieSource: ProviderCookieSource, - manualCookieHeader: String?, - managedAccountStoreUnreadable: Bool = false, - managedAccountTargetUnavailable: Bool = false, - profileAccountTargetUnavailable: Bool = false, - openAIWebCacheScope: CookieHeaderCache.Scope? = nil, - dashboardAuthorityKnownOwners: [CodexDashboardKnownOwnerCandidate] = []) - { - self.usageDataSource = usageDataSource - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - self.managedAccountStoreUnreadable = managedAccountStoreUnreadable - self.managedAccountTargetUnavailable = managedAccountTargetUnavailable - self.profileAccountTargetUnavailable = profileAccountTargetUnavailable - self.openAIWebCacheScope = openAIWebCacheScope - self.dashboardAuthorityKnownOwners = dashboardAuthorityKnownOwners - } - } - - public struct ClaudeProviderSettings: Sendable { - public let usageDataSource: ClaudeUsageDataSource - public let webExtrasEnabled: Bool - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - public let organizationID: String? - - public init( - usageDataSource: ClaudeUsageDataSource, - webExtrasEnabled: Bool, - cookieSource: ProviderCookieSource, - manualCookieHeader: String?, - organizationID: String? = nil) - { - self.usageDataSource = usageDataSource - self.webExtrasEnabled = webExtrasEnabled - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - self.organizationID = organizationID - } - } - - public struct CookieProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource = .auto, manualCookieHeader: String? = nil) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } - } - - public struct CursorProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } - } - - public struct OpenCodeProviderSettings: Sendable { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - public let workspaceID: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?, workspaceID: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - self.workspaceID = workspaceID - } - } - - public struct AlibabaCodingPlanProviderSettings: Sendable { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - public let apiRegion: AlibabaCodingPlanAPIRegion - - public init( - cookieSource: ProviderCookieSource = .auto, - manualCookieHeader: String? = nil, - apiRegion: AlibabaCodingPlanAPIRegion = .international) - { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - self.apiRegion = apiRegion - } - } - - public struct AlibabaTokenPlanProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - public let apiRegion: AlibabaTokenPlanAPIRegion - - public init( - cookieSource: ProviderCookieSource = .auto, - manualCookieHeader: String? = nil, - apiRegion: AlibabaTokenPlanAPIRegion = .international) - { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - self.apiRegion = apiRegion - } - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.init(cookieSource: cookieSource, manualCookieHeader: manualCookieHeader, apiRegion: .international) - } - } - - public struct QwenCloudProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource = .auto, manualCookieHeader: String? = nil) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } - } - - public struct FactoryProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } - } - - public struct MiniMaxProviderSettings: Sendable { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - public let apiRegion: MiniMaxAPIRegion - - public init( - cookieSource: ProviderCookieSource, - manualCookieHeader: String?, - apiRegion: MiniMaxAPIRegion = .global) - { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - self.apiRegion = apiRegion - } - } - - public struct ManusProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } - } - - public struct ZaiProviderSettings: Sendable { - public let apiRegion: ZaiAPIRegion - public let usageScope: ZaiUsageScope - public let teamContext: ZaiBigModelTeamContext? - - public init( - apiRegion: ZaiAPIRegion = .global, - usageScope: ZaiUsageScope = .personal, - teamContext: ZaiBigModelTeamContext? = nil) - { - self.apiRegion = apiRegion - self.usageScope = usageScope - self.teamContext = teamContext - } - } - - public struct CopilotProviderSettings: Sendable { - public let apiToken: String? - public let enterpriseHost: String? - public let selectedAccountExternalIdentifier: String? - public let budgetExtrasEnabled: Bool - public let budgetCookieSource: ProviderCookieSource - public let manualBudgetCookieHeader: String? - - public init( - apiToken: String? = nil, - enterpriseHost: String? = nil, - selectedAccountExternalIdentifier: String? = nil, - budgetExtrasEnabled: Bool = false, - budgetCookieSource: ProviderCookieSource = .auto, - manualBudgetCookieHeader: String? = nil) - { - self.apiToken = apiToken - self.enterpriseHost = enterpriseHost - self.selectedAccountExternalIdentifier = selectedAccountExternalIdentifier - self.budgetExtrasEnabled = budgetExtrasEnabled - self.budgetCookieSource = budgetCookieSource - self.manualBudgetCookieHeader = manualBudgetCookieHeader - } - } - - public struct KiloProviderSettings: Sendable { - public let usageDataSource: KiloUsageDataSource - public let extrasEnabled: Bool - - public init(usageDataSource: KiloUsageDataSource, extrasEnabled: Bool) { - self.usageDataSource = usageDataSource - self.extrasEnabled = extrasEnabled - } - } - - public struct KimiProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } - } - - public struct LongCatProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } - } - - public struct AugmentProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } - } - - public struct MoonshotProviderSettings: Sendable { - public let region: MoonshotRegion? - - public init(region: MoonshotRegion? = nil) { - self.region = region - } - } - - public struct JetBrainsProviderSettings: Sendable { - public let ideBasePath: String? - - public init(ideBasePath: String?) { - self.ideBasePath = ideBasePath - } - } - - public struct AmpProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } - } - - public struct T3ChatProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } - } - - public struct ZoomMateProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } - } - - /// Deliberately not a `ProviderCookieSettings`: that protocol's initializer takes only the two - /// cookie fields, so conforming would hand every generic construction path a way to build this - /// type with `workspaceID` silently dropped. Build it through the one initializer below. - public struct NotionProviderSettings: Sendable { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - /// Optional space id override for accounts that belong to more than one workspace. - public let workspaceID: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?, workspaceID: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - self.workspaceID = workspaceID - } + self.debugMenuEnabled = debugMenuEnabled + self.debugKeepCLISessionsAlive = debugKeepCLISessionsAlive + self.sections = Dictionary( + contributions.map { ($0.providerID, $0.section) }, + uniquingKeysWith: { _, new in new }) } - public struct DevinProviderSettings: Sendable { - public let cookieSource: ProviderCookieSource - public let manualBearerToken: String? - public let organization: String? - - public init(cookieSource: ProviderCookieSource, manualBearerToken: String?, organization: String?) { - self.cookieSource = cookieSource - self.manualBearerToken = manualBearerToken - self.organization = organization - } + public init( + _ section: Key.Section, + for key: Key.Type, + debugMenuEnabled: Bool = false, + debugKeepCLISessionsAlive: Bool = false) + { + self.init( + debugMenuEnabled: debugMenuEnabled, + debugKeepCLISessionsAlive: debugKeepCLISessionsAlive, + contributions: [ProviderSettingsSnapshotContribution(section, for: key)]) } - public struct CommandCodeProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } + public static func make( + _ section: Key.Section?, + for key: Key.Type) -> ProviderSettingsSnapshot + { + guard let section else { return ProviderSettingsSnapshot() } + return ProviderSettingsSnapshot(section, for: key) } - public struct OllamaProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } + public static func make() -> ProviderSettingsSnapshot { + ProviderSettingsSnapshot() } - public struct WindsurfProviderSettings: Sendable { - public let usageDataSource: WindsurfUsageDataSource - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init( - usageDataSource: WindsurfUsageDataSource, - cookieSource: ProviderCookieSource, - manualCookieHeader: String?) - { - self.usageDataSource = usageDataSource - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } + public subscript(key: Key.Type) -> Key.Section? { + self.sections[key.providerID] as? Key.Section } - public struct PerplexityProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } + func contains(_ registration: ProviderSettingsSectionRegistration) -> Bool { + guard let section = self.sections[registration.providerID] else { return false } + return ObjectIdentifier(type(of: section)) == registration.sectionTypeID } +} - public struct MiMoProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? +public struct ProviderSettingsSnapshotContribution: Sendable { + public let providerID: ProviderInstanceID + let section: any Sendable + let sectionTypeID: ObjectIdentifier - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } + public init(_ section: Key.Section, for key: Key.Type) { + self.providerID = key.providerID + self.section = section + self.sectionTypeID = ObjectIdentifier(Key.Section.self) } - public struct AbacusProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } + init(providerID: ProviderInstanceID, section: some Sendable) { + self.providerID = providerID + self.section = section + self.sectionTypeID = ObjectIdentifier(type(of: section)) } +} - public struct MistralProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? +public struct ProviderSettingsSectionRegistration: Sendable { + public let providerID: ProviderInstanceID + let sectionTypeID: ObjectIdentifier + public let defaultContribution: ProviderSettingsSnapshotContribution? - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } + public init(_ key: Key.Type) { + self.providerID = key.providerID + self.sectionTypeID = ObjectIdentifier(Key.Section.self) + self.defaultContribution = nil } - public struct QoderProviderSettings: ProviderCookieSettings { - public let cookieSource: ProviderCookieSource - public let manualCookieHeader: String? - - public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { - self.cookieSource = cookieSource - self.manualCookieHeader = manualCookieHeader - } + static func empty(for providerID: ProviderInstanceID) -> Self { + let contribution = ProviderSettingsSnapshotContribution( + providerID: providerID, + section: EmptyProviderSettingsSection()) + return Self( + providerID: providerID, + sectionTypeID: contribution.sectionTypeID, + defaultContribution: contribution) } - public struct StepFunProviderSettings: Sendable { - public let cookieSource: ProviderCookieSource - public let manualToken: String - public let username: String - public let password: String - - public init( - cookieSource: ProviderCookieSource = .auto, - manualToken: String = "", - username: String = "", - password: String = "") - { - self.cookieSource = cookieSource - self.manualToken = manualToken - self.username = username - self.password = password - } + private init( + providerID: ProviderInstanceID, + sectionTypeID: ObjectIdentifier, + defaultContribution: ProviderSettingsSnapshotContribution?) + { + self.providerID = providerID + self.sectionTypeID = sectionTypeID + self.defaultContribution = defaultContribution } - public let debugMenuEnabled: Bool - public let debugKeepCLISessionsAlive: Bool - public let codex: CodexProviderSettings? - public let claude: ClaudeProviderSettings? - public let cursor: CursorProviderSettings? - public let opencode: OpenCodeProviderSettings? - public let opencodego: OpenCodeProviderSettings? - public let alibaba: AlibabaCodingPlanProviderSettings? - public let alibabaTokenPlan: AlibabaTokenPlanProviderSettings? - public let qwenCloud: QwenCloudProviderSettings? - public let factory: FactoryProviderSettings? - public let minimax: MiniMaxProviderSettings? - public let manus: ManusProviderSettings? - public let zai: ZaiProviderSettings? - public let copilot: CopilotProviderSettings? - public let kilo: KiloProviderSettings? - public let kimi: KimiProviderSettings? - public let longcat: LongCatProviderSettings? - public let augment: AugmentProviderSettings? - public let moonshot: MoonshotProviderSettings? - public let amp: AmpProviderSettings? - public let t3chat: T3ChatProviderSettings? - public let zoommate: ZoomMateProviderSettings? - public let notion: NotionProviderSettings? - public let devin: DevinProviderSettings? - public let commandcode: CommandCodeProviderSettings? - public let ollama: OllamaProviderSettings? - public let jetbrains: JetBrainsProviderSettings? - public let windsurf: WindsurfProviderSettings? - public let perplexity: PerplexityProviderSettings? - public let mimo: MiMoProviderSettings? - public let abacus: AbacusProviderSettings? - public let mistral: MistralProviderSettings? - public let qoder: QoderProviderSettings? - public let stepfun: StepFunProviderSettings? - - public var jetbrainsIDEBasePath: String? { - self.jetbrains?.ideBasePath + public func accepts(_ contribution: ProviderSettingsSnapshotContribution) -> Bool { + contribution.providerID == self.providerID && contribution.sectionTypeID == self.sectionTypeID } - public init( - debugMenuEnabled: Bool, - debugKeepCLISessionsAlive: Bool, - codex: CodexProviderSettings?, - claude: ClaudeProviderSettings?, - cursor: CursorProviderSettings?, - opencode: OpenCodeProviderSettings?, - opencodego: OpenCodeProviderSettings?, - alibaba: AlibabaCodingPlanProviderSettings?, - alibabaTokenPlan: AlibabaTokenPlanProviderSettings? = nil, - qwenCloud: QwenCloudProviderSettings? = nil, - factory: FactoryProviderSettings?, - minimax: MiniMaxProviderSettings?, - manus: ManusProviderSettings?, - zai: ZaiProviderSettings?, - copilot: CopilotProviderSettings?, - kilo: KiloProviderSettings?, - kimi: KimiProviderSettings?, - longcat: LongCatProviderSettings? = nil, - augment: AugmentProviderSettings?, - moonshot: MoonshotProviderSettings? = nil, - amp: AmpProviderSettings?, - t3chat: T3ChatProviderSettings? = nil, - zoommate: ZoomMateProviderSettings? = nil, - notion: NotionProviderSettings? = nil, - devin: DevinProviderSettings? = nil, - commandcode: CommandCodeProviderSettings? = nil, - ollama: OllamaProviderSettings?, - jetbrains: JetBrainsProviderSettings? = nil, - windsurf: WindsurfProviderSettings? = nil, - perplexity: PerplexityProviderSettings? = nil, - mimo: MiMoProviderSettings? = nil, - abacus: AbacusProviderSettings? = nil, - mistral: MistralProviderSettings? = nil, - qoder: QoderProviderSettings? = nil, - stepfun: StepFunProviderSettings? = nil) - { - self.debugMenuEnabled = debugMenuEnabled - self.debugKeepCLISessionsAlive = debugKeepCLISessionsAlive - self.codex = codex - self.claude = claude - self.cursor = cursor - self.opencode = opencode - self.opencodego = opencodego - self.alibaba = alibaba - self.alibabaTokenPlan = alibabaTokenPlan - self.qwenCloud = qwenCloud - self.factory = factory - self.minimax = minimax - self.manus = manus - self.zai = zai - self.copilot = copilot - self.kilo = kilo - self.kimi = kimi - self.longcat = longcat - self.augment = augment - self.moonshot = moonshot - self.amp = amp - self.t3chat = t3chat - self.zoommate = zoommate - self.notion = notion - self.devin = devin - self.commandcode = commandcode - self.ollama = ollama - self.jetbrains = jetbrains - self.windsurf = windsurf - self.perplexity = perplexity - self.mimo = mimo - self.abacus = abacus - self.mistral = mistral - self.qoder = qoder - self.stepfun = stepfun + public func canRead(from snapshot: ProviderSettingsSnapshot) -> Bool { + snapshot.contains(self) } } -public enum ProviderSettingsSnapshotContribution: Sendable { - case codex(ProviderSettingsSnapshot.CodexProviderSettings) - case claude(ProviderSettingsSnapshot.ClaudeProviderSettings) - case cursor(ProviderSettingsSnapshot.CursorProviderSettings) - case opencode(ProviderSettingsSnapshot.OpenCodeProviderSettings) - case opencodego(ProviderSettingsSnapshot.OpenCodeProviderSettings) - case alibaba(ProviderSettingsSnapshot.AlibabaCodingPlanProviderSettings) - case alibabaTokenPlan(ProviderSettingsSnapshot.AlibabaTokenPlanProviderSettings) - case qwenCloud(ProviderSettingsSnapshot.QwenCloudProviderSettings) - case factory(ProviderSettingsSnapshot.FactoryProviderSettings) - case minimax(ProviderSettingsSnapshot.MiniMaxProviderSettings) - case manus(ProviderSettingsSnapshot.ManusProviderSettings) - case zai(ProviderSettingsSnapshot.ZaiProviderSettings) - case copilot(ProviderSettingsSnapshot.CopilotProviderSettings) - case kilo(ProviderSettingsSnapshot.KiloProviderSettings) - case kimi(ProviderSettingsSnapshot.KimiProviderSettings) - case longcat(ProviderSettingsSnapshot.LongCatProviderSettings) - case augment(ProviderSettingsSnapshot.AugmentProviderSettings) - case moonshot(ProviderSettingsSnapshot.MoonshotProviderSettings) - case amp(ProviderSettingsSnapshot.AmpProviderSettings) - case t3chat(ProviderSettingsSnapshot.T3ChatProviderSettings) - case zoommate(ProviderSettingsSnapshot.ZoomMateProviderSettings) - case notion(ProviderSettingsSnapshot.NotionProviderSettings) - case devin(ProviderSettingsSnapshot.DevinProviderSettings) - case commandcode(ProviderSettingsSnapshot.CommandCodeProviderSettings) - case ollama(ProviderSettingsSnapshot.OllamaProviderSettings) - case jetbrains(ProviderSettingsSnapshot.JetBrainsProviderSettings) - case windsurf(ProviderSettingsSnapshot.WindsurfProviderSettings) - case perplexity(ProviderSettingsSnapshot.PerplexityProviderSettings) - case mimo(ProviderSettingsSnapshot.MiMoProviderSettings) - case abacus(ProviderSettingsSnapshot.AbacusProviderSettings) - case mistral(ProviderSettingsSnapshot.MistralProviderSettings) - case qoder(ProviderSettingsSnapshot.QoderProviderSettings) - case stepfun(ProviderSettingsSnapshot.StepFunProviderSettings) -} - public struct ProviderSettingsSnapshotBuilder: Sendable { public var debugMenuEnabled: Bool public var debugKeepCLISessionsAlive: Bool - public var codex: ProviderSettingsSnapshot.CodexProviderSettings? - public var claude: ProviderSettingsSnapshot.ClaudeProviderSettings? - public var cursor: ProviderSettingsSnapshot.CursorProviderSettings? - public var opencode: ProviderSettingsSnapshot.OpenCodeProviderSettings? - public var opencodego: ProviderSettingsSnapshot.OpenCodeProviderSettings? - public var alibaba: ProviderSettingsSnapshot.AlibabaCodingPlanProviderSettings? - public var alibabaTokenPlan: ProviderSettingsSnapshot.AlibabaTokenPlanProviderSettings? - public var qwenCloud: ProviderSettingsSnapshot.QwenCloudProviderSettings? - public var factory: ProviderSettingsSnapshot.FactoryProviderSettings? - public var minimax: ProviderSettingsSnapshot.MiniMaxProviderSettings? - public var manus: ProviderSettingsSnapshot.ManusProviderSettings? - public var zai: ProviderSettingsSnapshot.ZaiProviderSettings? - public var copilot: ProviderSettingsSnapshot.CopilotProviderSettings? - public var kilo: ProviderSettingsSnapshot.KiloProviderSettings? - public var kimi: ProviderSettingsSnapshot.KimiProviderSettings? - public var longcat: ProviderSettingsSnapshot.LongCatProviderSettings? - public var augment: ProviderSettingsSnapshot.AugmentProviderSettings? - public var moonshot: ProviderSettingsSnapshot.MoonshotProviderSettings? - public var amp: ProviderSettingsSnapshot.AmpProviderSettings? - public var t3chat: ProviderSettingsSnapshot.T3ChatProviderSettings? - public var zoommate: ProviderSettingsSnapshot.ZoomMateProviderSettings? - public var notion: ProviderSettingsSnapshot.NotionProviderSettings? - public var devin: ProviderSettingsSnapshot.DevinProviderSettings? - public var commandcode: ProviderSettingsSnapshot.CommandCodeProviderSettings? - public var ollama: ProviderSettingsSnapshot.OllamaProviderSettings? - public var jetbrains: ProviderSettingsSnapshot.JetBrainsProviderSettings? - public var windsurf: ProviderSettingsSnapshot.WindsurfProviderSettings? - public var perplexity: ProviderSettingsSnapshot.PerplexityProviderSettings? - public var mimo: ProviderSettingsSnapshot.MiMoProviderSettings? - public var abacus: ProviderSettingsSnapshot.AbacusProviderSettings? - public var mistral: ProviderSettingsSnapshot.MistralProviderSettings? - public var qoder: ProviderSettingsSnapshot.QoderProviderSettings? - public var stepfun: ProviderSettingsSnapshot.StepFunProviderSettings? + private var contributions: [ProviderSettingsSnapshotContribution] = [] public init(debugMenuEnabled: Bool = false, debugKeepCLISessionsAlive: Bool = false) { self.debugMenuEnabled = debugMenuEnabled self.debugKeepCLISessionsAlive = debugKeepCLISessionsAlive } - // swiftlint:disable:next cyclomatic_complexity public mutating func apply(_ contribution: ProviderSettingsSnapshotContribution) { - switch contribution { - case let .codex(value): self.codex = value - case let .claude(value): self.claude = value - case let .cursor(value): self.cursor = value - case let .opencode(value): self.opencode = value - case let .opencodego(value): self.opencodego = value - case let .alibaba(value): self.alibaba = value - case let .alibabaTokenPlan(value): self.alibabaTokenPlan = value - case let .qwenCloud(value): self.qwenCloud = value - case let .factory(value): self.factory = value - case let .minimax(value): self.minimax = value - case let .manus(value): self.manus = value - case let .zai(value): self.zai = value - case let .copilot(value): self.copilot = value - case let .kilo(value): self.kilo = value - case let .kimi(value): self.kimi = value - case let .longcat(value): self.longcat = value - case let .augment(value): self.augment = value - case let .moonshot(value): self.moonshot = value - case let .amp(value): self.amp = value - case let .t3chat(value): self.t3chat = value - case let .zoommate(value): self.zoommate = value - case let .notion(value): self.notion = value - case let .devin(value): self.devin = value - case let .commandcode(value): self.commandcode = value - case let .ollama(value): self.ollama = value - case let .jetbrains(value): self.jetbrains = value - case let .windsurf(value): self.windsurf = value - case let .perplexity(value): self.perplexity = value - case let .mimo(value): self.mimo = value - case let .abacus(value): self.abacus = value - case let .mistral(value): self.mistral = value - case let .qoder(value): self.qoder = value - case let .stepfun(value): self.stepfun = value - } + self.contributions.append(contribution) } public func build() -> ProviderSettingsSnapshot { ProviderSettingsSnapshot( debugMenuEnabled: self.debugMenuEnabled, debugKeepCLISessionsAlive: self.debugKeepCLISessionsAlive, - codex: self.codex, - claude: self.claude, - cursor: self.cursor, - opencode: self.opencode, - opencodego: self.opencodego, - alibaba: self.alibaba, - alibabaTokenPlan: self.alibabaTokenPlan, - qwenCloud: self.qwenCloud, - factory: self.factory, - minimax: self.minimax, - manus: self.manus, - zai: self.zai, - copilot: self.copilot, - kilo: self.kilo, - kimi: self.kimi, - longcat: self.longcat, - augment: self.augment, - moonshot: self.moonshot, - amp: self.amp, - t3chat: self.t3chat, - zoommate: self.zoommate, - notion: self.notion, - devin: self.devin, - commandcode: self.commandcode, - ollama: self.ollama, - jetbrains: self.jetbrains, - windsurf: self.windsurf, - perplexity: self.perplexity, - mimo: self.mimo, - abacus: self.abacus, - mistral: self.mistral, - qoder: self.qoder, - stepfun: self.stepfun) + contributions: self.contributions) } } + +private struct EmptyProviderSettingsSection: Sendable {} diff --git a/Sources/CodexBarCore/Providers/Qoder/QoderProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Qoder/QoderProviderDescriptor.swift index 62843b4d4b..bb337c7f46 100644 --- a/Sources/CodexBarCore/Providers/Qoder/QoderProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Qoder/QoderProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum QoderProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .qoder, + settingsSection: .init(QoderProviderSettingsKey.self), metadata: ProviderMetadata( id: .qoder, displayName: "Qoder", diff --git a/Sources/CodexBarCore/Providers/Qoder/QoderProviderSettings.swift b/Sources/CodexBarCore/Providers/Qoder/QoderProviderSettings.swift new file mode 100644 index 0000000000..f047b4a6af --- /dev/null +++ b/Sources/CodexBarCore/Providers/Qoder/QoderProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct QoderProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum QoderProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.qoder + public typealias Section = QoderProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias QoderProviderSettings = CodexBarCore.QoderProviderSettings + public var qoder: QoderProviderSettings? { + self[QoderProviderSettingsKey.self] + } + + public static func make(qoder: QoderProviderSettings?) -> Self { + self.make(qoder, for: QoderProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func qoder(_ section: QoderProviderSettings) -> Self { + Self(section, for: QoderProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/QwenCloud/QwenCloudProviderDescriptor.swift b/Sources/CodexBarCore/Providers/QwenCloud/QwenCloudProviderDescriptor.swift index 1809b6bc6c..a7534056a7 100644 --- a/Sources/CodexBarCore/Providers/QwenCloud/QwenCloudProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/QwenCloud/QwenCloudProviderDescriptor.swift @@ -16,6 +16,7 @@ public enum QwenCloudProviderDescriptor { return ProviderDescriptor( id: .qwencloud, + settingsSection: .init(QwenCloudProviderSettingsKey.self), metadata: ProviderMetadata( id: .qwencloud, displayName: "Qwen Cloud", diff --git a/Sources/CodexBarCore/Providers/QwenCloud/QwenCloudProviderSettings.swift b/Sources/CodexBarCore/Providers/QwenCloud/QwenCloudProviderSettings.swift new file mode 100644 index 0000000000..e53d78d13e --- /dev/null +++ b/Sources/CodexBarCore/Providers/QwenCloud/QwenCloudProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct QwenCloudProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource = .auto, manualCookieHeader: String? = nil) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum QwenCloudProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.qwencloud + public typealias Section = QwenCloudProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias QwenCloudProviderSettings = CodexBarCore.QwenCloudProviderSettings + public var qwenCloud: QwenCloudProviderSettings? { + self[QwenCloudProviderSettingsKey.self] + } + + public static func make(qwenCloud: QwenCloudProviderSettings?) -> Self { + self.make(qwenCloud, for: QwenCloudProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func qwenCloud(_ section: QwenCloudProviderSettings) -> Self { + Self(section, for: QwenCloudProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Shared/ProviderCookieSettings.swift b/Sources/CodexBarCore/Providers/Shared/ProviderCookieSettings.swift new file mode 100644 index 0000000000..5e086c5105 --- /dev/null +++ b/Sources/CodexBarCore/Providers/Shared/ProviderCookieSettings.swift @@ -0,0 +1,22 @@ +import Foundation + +public protocol ProviderCookieSettings: Sendable { + var cookieSource: ProviderCookieSource { get } + var manualCookieHeader: String? { get } + + init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) +} + +public struct CookieProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource = .auto, manualCookieHeader: String? = nil) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +extension ProviderSettingsSnapshot { + public typealias CookieProviderSettings = CodexBarCore.CookieProviderSettings +} diff --git a/Sources/CodexBarCore/Providers/StepFun/StepFunProviderDescriptor.swift b/Sources/CodexBarCore/Providers/StepFun/StepFunProviderDescriptor.swift index 49445ad916..b7f136b662 100644 --- a/Sources/CodexBarCore/Providers/StepFun/StepFunProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/StepFun/StepFunProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum StepFunProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .stepfun, + settingsSection: .init(StepFunProviderSettingsKey.self), metadata: ProviderMetadata( id: .stepfun, displayName: "StepFun", diff --git a/Sources/CodexBarCore/Providers/StepFun/StepFunProviderSettings.swift b/Sources/CodexBarCore/Providers/StepFun/StepFunProviderSettings.swift new file mode 100644 index 0000000000..cdabf675f4 --- /dev/null +++ b/Sources/CodexBarCore/Providers/StepFun/StepFunProviderSettings.swift @@ -0,0 +1,42 @@ +import Foundation + +public struct StepFunProviderSettings: Sendable { + public let cookieSource: ProviderCookieSource + public let manualToken: String + public let username: String + public let password: String + + public init( + cookieSource: ProviderCookieSource = .auto, + manualToken: String = "", + username: String = "", + password: String = "") + { + self.cookieSource = cookieSource + self.manualToken = manualToken + self.username = username + self.password = password + } +} + +public enum StepFunProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.stepfun + public typealias Section = StepFunProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias StepFunProviderSettings = CodexBarCore.StepFunProviderSettings + public var stepfun: StepFunProviderSettings? { + self[StepFunProviderSettingsKey.self] + } + + public static func make(stepfun: StepFunProviderSettings?) -> Self { + self.make(stepfun, for: StepFunProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func stepfun(_ section: StepFunProviderSettings) -> Self { + Self(section, for: StepFunProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/T3Chat/T3ChatProviderDescriptor.swift b/Sources/CodexBarCore/Providers/T3Chat/T3ChatProviderDescriptor.swift index 548e250c14..84d8e5f255 100644 --- a/Sources/CodexBarCore/Providers/T3Chat/T3ChatProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/T3Chat/T3ChatProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum T3ChatProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .t3chat, + settingsSection: .init(T3ChatProviderSettingsKey.self), metadata: ProviderMetadata( id: .t3chat, displayName: "T3 Chat", diff --git a/Sources/CodexBarCore/Providers/T3Chat/T3ChatProviderSettings.swift b/Sources/CodexBarCore/Providers/T3Chat/T3ChatProviderSettings.swift new file mode 100644 index 0000000000..7320f06348 --- /dev/null +++ b/Sources/CodexBarCore/Providers/T3Chat/T3ChatProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct T3ChatProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum T3ChatProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.t3chat + public typealias Section = T3ChatProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias T3ChatProviderSettings = CodexBarCore.T3ChatProviderSettings + public var t3chat: T3ChatProviderSettings? { + self[T3ChatProviderSettingsKey.self] + } + + public static func make(t3chat: T3ChatProviderSettings?) -> Self { + self.make(t3chat, for: T3ChatProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func t3chat(_ section: T3ChatProviderSettings) -> Self { + Self(section, for: T3ChatProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Windsurf/WindsurfProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Windsurf/WindsurfProviderDescriptor.swift index 3db0266188..912618d7fe 100644 --- a/Sources/CodexBarCore/Providers/Windsurf/WindsurfProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Windsurf/WindsurfProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum WindsurfProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .windsurf, + settingsSection: .init(WindsurfProviderSettingsKey.self), metadata: ProviderMetadata( id: .windsurf, displayName: "Windsurf", diff --git a/Sources/CodexBarCore/Providers/Windsurf/WindsurfProviderSettings.swift b/Sources/CodexBarCore/Providers/Windsurf/WindsurfProviderSettings.swift new file mode 100644 index 0000000000..73dba9ebee --- /dev/null +++ b/Sources/CodexBarCore/Providers/Windsurf/WindsurfProviderSettings.swift @@ -0,0 +1,39 @@ +import Foundation + +public struct WindsurfProviderSettings: Sendable { + public let usageDataSource: WindsurfUsageDataSource + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init( + usageDataSource: WindsurfUsageDataSource, + cookieSource: ProviderCookieSource, + manualCookieHeader: String?) + { + self.usageDataSource = usageDataSource + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum WindsurfProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.windsurf + public typealias Section = WindsurfProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias WindsurfProviderSettings = CodexBarCore.WindsurfProviderSettings + public var windsurf: WindsurfProviderSettings? { + self[WindsurfProviderSettingsKey.self] + } + + public static func make(windsurf: WindsurfProviderSettings?) -> Self { + self.make(windsurf, for: WindsurfProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func windsurf(_ section: WindsurfProviderSettings) -> Self { + Self(section, for: WindsurfProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/Zai/ZaiProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Zai/ZaiProviderDescriptor.swift index cba37c77ac..dba94b60a7 100644 --- a/Sources/CodexBarCore/Providers/Zai/ZaiProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Zai/ZaiProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum ZaiProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .zai, + settingsSection: .init(ZaiProviderSettingsKey.self), metadata: ProviderMetadata( id: .zai, displayName: "z.ai / GLM", diff --git a/Sources/CodexBarCore/Providers/Zai/ZaiProviderSettings.swift b/Sources/CodexBarCore/Providers/Zai/ZaiProviderSettings.swift new file mode 100644 index 0000000000..d6f88b523c --- /dev/null +++ b/Sources/CodexBarCore/Providers/Zai/ZaiProviderSettings.swift @@ -0,0 +1,39 @@ +import Foundation + +public struct ZaiProviderSettings: Sendable { + public let apiRegion: ZaiAPIRegion + public let usageScope: ZaiUsageScope + public let teamContext: ZaiBigModelTeamContext? + + public init( + apiRegion: ZaiAPIRegion = .global, + usageScope: ZaiUsageScope = .personal, + teamContext: ZaiBigModelTeamContext? = nil) + { + self.apiRegion = apiRegion + self.usageScope = usageScope + self.teamContext = teamContext + } +} + +public enum ZaiProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.zai + public typealias Section = ZaiProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias ZaiProviderSettings = CodexBarCore.ZaiProviderSettings + public var zai: ZaiProviderSettings? { + self[ZaiProviderSettingsKey.self] + } + + public static func make(zai: ZaiProviderSettings?) -> Self { + self.make(zai, for: ZaiProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func zai(_ section: ZaiProviderSettings) -> Self { + Self(section, for: ZaiProviderSettingsKey.self) + } +} diff --git a/Sources/CodexBarCore/Providers/ZoomMate/ZoomMateProviderDescriptor.swift b/Sources/CodexBarCore/Providers/ZoomMate/ZoomMateProviderDescriptor.swift index fe77244db2..192066049c 100644 --- a/Sources/CodexBarCore/Providers/ZoomMate/ZoomMateProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/ZoomMate/ZoomMateProviderDescriptor.swift @@ -6,6 +6,7 @@ public enum ZoomMateProviderDescriptor { static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .zoommate, + settingsSection: .init(ZoomMateProviderSettingsKey.self), metadata: ProviderMetadata( id: .zoommate, displayName: "ZoomMate", diff --git a/Sources/CodexBarCore/Providers/ZoomMate/ZoomMateProviderSettings.swift b/Sources/CodexBarCore/Providers/ZoomMate/ZoomMateProviderSettings.swift new file mode 100644 index 0000000000..9c544e1164 --- /dev/null +++ b/Sources/CodexBarCore/Providers/ZoomMate/ZoomMateProviderSettings.swift @@ -0,0 +1,33 @@ +import Foundation + +public struct ZoomMateProviderSettings: ProviderCookieSettings { + public let cookieSource: ProviderCookieSource + public let manualCookieHeader: String? + + public init(cookieSource: ProviderCookieSource, manualCookieHeader: String?) { + self.cookieSource = cookieSource + self.manualCookieHeader = manualCookieHeader + } +} + +public enum ZoomMateProviderSettingsKey: ProviderSettingsSectionKey { + public static let providerID = ProviderInstanceID.zoommate + public typealias Section = ZoomMateProviderSettings +} + +extension ProviderSettingsSnapshot { + public typealias ZoomMateProviderSettings = CodexBarCore.ZoomMateProviderSettings + public var zoommate: ZoomMateProviderSettings? { + self[ZoomMateProviderSettingsKey.self] + } + + public static func make(zoommate: ZoomMateProviderSettings?) -> Self { + self.make(zoommate, for: ZoomMateProviderSettingsKey.self) + } +} + +extension ProviderSettingsSnapshotContribution { + public static func zoommate(_ section: ZoomMateProviderSettings) -> Self { + Self(section, for: ZoomMateProviderSettingsKey.self) + } +} diff --git a/Tests/CodexBarTests/CLIDiagnoseCommandTests.swift b/Tests/CodexBarTests/CLIDiagnoseCommandTests.swift index fd10e40f28..d580b77126 100644 --- a/Tests/CodexBarTests/CLIDiagnoseCommandTests.swift +++ b/Tests/CodexBarTests/CLIDiagnoseCommandTests.swift @@ -30,28 +30,11 @@ struct CLIDiagnoseCommandTests { } private func makeSettingsWithMiniMaxCookie(_ manualCookieHeader: String) -> ProviderSettingsSnapshot { - ProviderSettingsSnapshot( - debugMenuEnabled: false, - debugKeepCLISessionsAlive: false, - codex: nil, - claude: nil, - cursor: nil, - opencode: nil, - opencodego: nil, - alibaba: nil, - factory: nil, + ProviderSettingsSnapshot.make( minimax: ProviderSettingsSnapshot.MiniMaxProviderSettings( cookieSource: .manual, manualCookieHeader: manualCookieHeader, - apiRegion: .global), - manus: nil, - zai: nil, - copilot: nil, - kilo: nil, - kimi: nil, - augment: nil, - amp: nil, - ollama: nil) + apiRegion: .global)) } @Test diff --git a/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift b/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift index e5d6c41409..7a3848b489 100644 --- a/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift +++ b/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift @@ -23,6 +23,42 @@ struct ProviderArchitectureGatekeeperTests { "Missing implementation manifest entries: \(missingImplementations.joined(separator: ", "))") } + @Test + func `every provider can produce and read its registered settings section`() { + let settings = testSettingsStore(suiteName: "ProviderArchitectureGatekeeperTests-settings-sections") + let context = ProviderSettingsSnapshotContext(settings: settings, tokenOverride: nil) + var builder = ProviderSettingsSnapshotBuilder() + + for implementation in ProviderImplementationRegistry.all { + let providerName = implementation.id.rawValue + let registration = ProviderDescriptorRegistry.descriptor(for: implementation.id).settingsSection + guard let contribution = implementation.settingsSnapshot(context: context) else { + Issue.record("Missing settings-section contribution for provider '\(providerName)'.") + continue + } + #expect( + registration.accepts(contribution), + "Settings-section registration does not match provider '\(providerName)'.") + builder.apply(contribution) + } + + let snapshot = builder.build() + for descriptor in ProviderDescriptorRegistry.all { + #expect( + descriptor.settingsSection.canRead(from: snapshot), + "Could not read settings section for provider '\(descriptor.id.rawValue)'.") + } + } + + @Test + func `empty settings snapshot factory has no provider sections`() { + let snapshot = ProviderSettingsSnapshot.make() + + #expect(snapshot.abacus == nil) + #expect(!snapshot.debugMenuEnabled) + #expect(!snapshot.debugKeepCLISessionsAlive) + } + @Test func `every provider descriptor has a loadable SVG resource`() throws { let resources = try Self.repoRoot() diff --git a/docs/architecture.md b/docs/architecture.md index f3dc337bde..22259e8f57 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -22,6 +22,7 @@ read_when: ## Data flow - Background refresh → `UsageFetcher`/provider probes → `UsageStore` → menu/icon/widgets. - Settings toggles feed `SettingsStore` → `UsageStore` refresh cadence + feature flags. +- Runtime-only provider settings flow through typed, descriptor-registered sections in `ProviderSettingsSnapshot`. ## Concurrency & platform - Swift 6 strict concurrency enabled; prefer Sendable state and explicit MainActor hops. diff --git a/docs/provider.md b/docs/provider.md index c6b28b3e8b..f1ec1f7450 100644 --- a/docs/provider.md +++ b/docs/provider.md @@ -43,6 +43,12 @@ Provider behavior is descriptor-driven. Two flat first-party manifests form the `ProviderManifest` lists core descriptors and `ProviderImplementationManifest` lists app implementations. The registries retain thread-safe `register(_:)` methods for future dynamic providers. +Runtime settings follow the same boundary. A provider owns its `ProviderSettingsSectionKey` and section payload in its +core folder, registers that key on its descriptor, and contributes the payload from its app implementation. The generic +`ProviderSettingsSnapshot` container performs the sole type-erased cast behind its constrained subscript; provider-local +accessors keep fetch strategies fully typed. Providers that share a payload type still declare a distinct key for each +`ProviderInstanceID`, while providers with no runtime settings receive an empty section from the descriptor default. + ## Provider descriptor (source of truth) Introduce a single descriptor per provider: @@ -190,6 +196,10 @@ The mandatory registration checklist is intentionally short: 7. Add one case to the WidgetKit `ProviderChoice` `AppEnum`. New descriptors are widget-selectable by default; set `widgetSelectable: false` in the provider's descriptor only when the provider genuinely cannot appear in widgets. +If the provider has runtime settings, add its section key and payload beside the descriptor, pass the key as the +descriptor's `settingsSection`, and return a typed contribution from the app implementation. No central settings file +or builder switch changes are needed. + Everything else is derived from the descriptor: icon-style identity, log-category construction, display and compact labels, default enablement, fetch/CLI metadata, icon validation, and widget display representations. The provider architecture gatekeeper test reports missing descriptor, implementation, icon, or widget registrations by provider ID.