import AppKit import CodexBarCore import SwiftUI struct CopilotProviderImplementation: ProviderImplementation { let id: UsageProvider = .copilot let supportsLoginFlow: Bool = true @MainActor func presentation(context _: ProviderPresentationContext) -> ProviderPresentation { ProviderPresentation { _ in "github api" } } @MainActor func observeSettings(_ settings: SettingsStore) { _ = settings.copilotAPIToken _ = settings.copilotEnterpriseHost _ = settings.copilotBudgetExtrasEnabled _ = settings.copilotBudgetCookieSource _ = settings.copilotBudgetCookieHeader } @MainActor func settingsSnapshot(context: ProviderSettingsSnapshotContext) -> ProviderSettingsSnapshotContribution? { .copilot(context.settings.copilotSettingsSnapshot(tokenOverride: context.tokenOverride)) } @MainActor func loginMenuAction(context _: ProviderMenuLoginContext) -> (label: String, action: MenuDescriptor.MenuAction)? { ("Add Account...", .addProviderAccount(.copilot)) } @MainActor func settingsToggles(context: ProviderSettingsContext) -> [ProviderSettingsToggleDescriptor] { let budgetExtrasBinding = Binding( get: { context.settings.copilotBudgetExtrasEnabled }, set: { enabled in context.settings.copilotBudgetExtrasEnabled = enabled }) let budgetExtrasStatus: () -> String? = { if context.store.snapshot(for: .copilot)?.extraRateWindows?.isEmpty == false { return nil } if context.settings.copilotBudgetCookieSource == .manual, context.settings.copilotBudgetCookieHeader.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { return [ "Paste a github.com Cookie header, then refresh Copilot.", "Copilot reauth does not provide the GitHub web cookie used for budgets.", ].joined(separator: " ") } return [ "Refresh Copilot to load budget bars.", "Budget extras require a logged-in github.com browser session or a manual Cookie header.", ].joined(separator: " ") } return [ ProviderSettingsToggleDescriptor( id: "copilot-budget-extras", title: "Budget extras", subtitle: [ "Optional.", "Turn this on to fetch configured GitHub Copilot budget limits and show them as extra bars.", ].joined(separator: " "), binding: budgetExtrasBinding, statusText: budgetExtrasStatus, actions: [], isVisible: nil, onChange: { enabled in if enabled { await context.store.refreshProvider(.copilot, allowDisabled: true) } else { context.store.clearCopilotBudgetExtras() } }, onAppDidBecomeActive: nil, onAppearWhenEnabled: nil), ] } @MainActor func settingsPickers(context: ProviderSettingsContext) -> [ProviderSettingsPickerDescriptor] { let extraWindows = context.store.snapshot(for: .copilot)?.extraRateWindows ?? [] let cookieBinding = Binding( get: { context.settings.copilotBudgetCookieSource.rawValue }, set: { raw in context.settings.copilotBudgetCookieSource = ProviderCookieSource(rawValue: raw) ?? .auto }) let cookieOptions = ProviderCookieSourceUI.options( allowsOff: false, keychainDisabled: context.settings.debugDisableKeychainAccess) let cookieSubtitle: () -> String? = { ProviderCookieSourceUI.subtitle( source: context.settings.copilotBudgetCookieSource, keychainDisabled: context.settings.debugDisableKeychainAccess, auto: "Automatically imports browser cookies for github.com budget extras.", manual: "Paste a Cookie header from github.com.", off: "GitHub cookies are disabled.") } let options = [ ProviderSettingsPickerOption( id: CopilotIconSecondaryWindowSelection.chat, title: "Chat"), ] + extraWindows.map { window in ProviderSettingsPickerOption(id: window.id, title: window.title) } return [ ProviderSettingsPickerDescriptor( id: "copilot-icon-secondary-window", title: "Menu bar secondary metric", subtitle: "Choose the second meter shown in the menu bar icon.", placement: .menuBar, dynamicSubtitle: { extraWindows.isEmpty ? "Budget options appear after a refresh finds configured Copilot budgets." : nil }, binding: Binding( get: { let selected = context.settings.copilotIconSecondaryWindowID if selected == CopilotIconSecondaryWindowSelection.chat { return selected } return extraWindows.contains(where: { $0.id == selected }) ? selected : CopilotIconSecondaryWindowSelection.chat }, set: { selection in context.settings.copilotIconSecondaryWindowID = selection }), options: options, isVisible: { context.settings.copilotBudgetExtrasEnabled }, onChange: nil), ProviderSettingsPickerDescriptor( id: "copilot-budget-cookie-source", title: "GitHub cookies", subtitle: "Automatically imports browser cookies for budget extras.", dynamicSubtitle: cookieSubtitle, binding: cookieBinding, options: cookieOptions, isVisible: { context.settings.copilotBudgetExtrasEnabled }, onChange: { _ in await context.store.refreshProvider(.copilot, allowDisabled: true) }, trailingText: { guard context.settings.copilotBudgetCookieSource != .manual else { return nil } return ProviderCookieSourceUI.cachedTrailingText(provider: .copilot) }), ] } @MainActor func settingsFields(context: ProviderSettingsContext) -> [ProviderSettingsFieldDescriptor] { [ ProviderSettingsFieldDescriptor( id: "copilot-budget-cookie-header", title: "Manual GitHub Cookie header", subtitle: "Paste a github.com Cookie header. Treat this value like a password.", kind: .secure, placeholder: "Cookie: ...", binding: context.stringBinding(\.copilotBudgetCookieHeader), actions: [ ProviderSettingsActionDescriptor( id: "refresh-copilot-budget-cookie", title: "Refresh budgets", style: .bordered, isVisible: nil, perform: { await context.store.refreshProvider(.copilot, allowDisabled: true) }), ], isVisible: { context.settings.copilotBudgetExtrasEnabled && context.settings.copilotBudgetCookieSource == .manual }, onActivate: nil), ProviderSettingsFieldDescriptor( id: "copilot-enterprise-host", title: "Enterprise host", subtitle: "Optional. Enter your GitHub Enterprise host, for example octocorp.ghe.com. " + "Leave blank for github.com.", kind: .plain, placeholder: "github.com", binding: context.stringBinding(\.copilotEnterpriseHost), actions: [], isVisible: nil, onActivate: nil), ProviderSettingsFieldDescriptor( id: "copilot-add-account", title: "GitHub Login", subtitle: "Add accounts via GitHub OAuth Device Flow on the selected host.", kind: .plain, placeholder: nil, binding: .constant(""), actions: [ ProviderSettingsActionDescriptor( id: "copilot-add-account-action", title: "Add Account", style: .bordered, isVisible: { true }, perform: { await CopilotLoginFlow.run(settings: context.settings) }), ], isVisible: nil, onActivate: nil), ] } @MainActor func runLoginFlow(context: ProviderLoginContext) async -> Bool { await CopilotLoginFlow.run(settings: context.controller.settings) return true } }