135 lines · 5.7 KB · Swift
RawDownload
import Foundation public enum CopilotProviderDescriptor {    public static let descriptor: ProviderDescriptor = Self.makeDescriptor()     static func makeDescriptor() -> ProviderDescriptor {        ProviderDescriptor(            id: .copilot,            metadata: ProviderMetadata(                id: .copilot,                displayName: "Copilot",                sessionLabel: "Premium",                weeklyLabel: "Chat",                opusLabel: nil,                supportsOpus: false,                supportsCredits: false,                creditsHint: "",                toggleTitle: "Show Copilot usage",                cliName: "copilot",                defaultEnabled: false,                isPrimaryProvider: false,                usesAccountFallback: false,                browserCookieOrder: ProviderBrowserCookieDefaults.copilotCookieImportOrder,                dashboardURL: "https://github.com/settings/copilot",                statusPageURL: "https://www.githubstatus.com/"),            branding: ProviderBranding(                iconStyle: .copilot,                iconResourceName: "ProviderIcon-copilot",                color: ProviderColor(red: 168 / 255, green: 85 / 255, blue: 247 / 255)),            tokenCost: ProviderTokenCostConfig(                supportsTokenCost: false,                noDataMessage: { "Copilot cost summary is not supported." }),            fetchPlan: ProviderFetchPlan(                sourceModes: [.auto, .api],                pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [CopilotAPIFetchStrategy()] })),            cli: ProviderCLIConfig(                name: "copilot",                versionDetector: nil))    }} struct CopilotAPIFetchStrategy: ProviderFetchStrategy {    let id: String = "copilot.api"    let kind: ProviderFetchKind = .apiToken     func isAvailable(_ context: ProviderFetchContext) async -> Bool {        Self.resolveToken(context: context) != nil    }     func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult {        guard let token = Self.resolveToken(context: context), !token.isEmpty else {            throw URLError(.userAuthenticationRequired)        }        let fetcher = CopilotUsageFetcher(            token: token,            enterpriseHost: context.settings?.copilot?.enterpriseHost)        let usage = try await fetcher.fetch()        let snap = await self.addBudgetWindowsIfNeeded(to: usage, token: token, context: context)        return self.makeResult(            usage: snap,            sourceLabel: "api")    }     func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool {        false    }     private static func resolveToken(context: ProviderFetchContext) -> String? {        ProviderTokenResolver.copilotToken(environment: context.env)            ?? ProviderTokenResolver.copilotResolution(environment: [                "COPILOT_API_TOKEN": context.settings?.copilot?.apiToken ?? "",            ])?.token    }     private func addBudgetWindowsIfNeeded(        to usage: UsageSnapshot,        token: String,        context: ProviderFetchContext) async -> UsageSnapshot    {        guard let settings = context.settings?.copilot,              settings.budgetExtrasEnabled,              settings.budgetCookieSource != .off        else { return usage }         let manualCookieHeader = Self.budgetCookieHeaderOverride(from: settings)        if settings.budgetCookieSource == .manual, manualCookieHeader == nil {            return usage        }        do {            let expectedAccountIdentifier = try await self.expectedBudgetAccountIdentifier(                token: token,                settings: settings)            let extraRateWindows = try await CopilotBudgetWebFetcher(                cookieHeaderOverride: manualCookieHeader,                expectedGitHubAccountIdentifier: expectedAccountIdentifier,                browserDetection: context.browserDetection)                .fetchBudgetWindows()            guard !extraRateWindows.isEmpty else { return usage }            return usage.with(extraRateWindows: extraRateWindows)        } catch {            CodexBarLog.logger(LogCategories.providers).warning(                "Copilot budget extras unavailable",                metadata: ["error": "\(error.localizedDescription)"])            return usage        }    }     static func budgetCookieHeaderOverride(        from settings: ProviderSettingsSnapshot.CopilotProviderSettings) -> String?    {        guard settings.budgetCookieSource == .manual else { return nil }        return CookieHeaderNormalizer.normalize(settings.manualBudgetCookieHeader)    }     private func expectedBudgetAccountIdentifier(        token: String,        settings: ProviderSettingsSnapshot.CopilotProviderSettings) async throws -> String    {        let identity = try await CopilotUsageFetcher.fetchGitHubIdentity(token: token)        let tokenIdentifier = CopilotBudgetWebFetcher.normalizedGitHubAccountIdentifier(for: identity)        if let selectedIdentifier = Self.normalizedBudgetAccountIdentifier(settings.selectedAccountExternalIdentifier),           selectedIdentifier != tokenIdentifier.lowercased(),           selectedIdentifier != identity.login.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()        {            CodexBarLog.logger(LogCategories.providers).warning(                "Ignoring stale Copilot account identifier")        }        return tokenIdentifier    }     private static func normalizedBudgetAccountIdentifier(_ identifier: String?) -> String? {        let trimmed = identifier?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""        return trimmed.isEmpty ? nil : trimmed.lowercased()    }}