1539 lines · 67.9 KB · Swift
RawDownload
import CodexBarCoreimport Foundation struct OpenAIWebRefreshGateContext {    let force: Bool    let accountDidChange: Bool    let lastError: String?    let lastSnapshotAt: Date?    let lastAttemptAt: Date?    let now: Date    let refreshInterval: TimeInterval} struct OpenAIWebRefreshPolicyContext {    let accessEnabled: Bool    let batterySaverEnabled: Bool    let force: Bool    let refreshPhase: ProviderRefreshPhase} // MARK: - OpenAI web lifecycle extension UsageStore {    private struct OpenAIDashboardRefreshContext {        let targetEmail: String?        let allowCurrentSnapshotFallback: Bool        let expectedGuard: CodexAccountScopedRefreshGuard?        let refreshTaskToken: UUID        let allowCodexUsageBackfill: Bool        let force: Bool    }     private struct OpenAIDashboardCookieImportRequest {        let normalizedTarget: String?        let allowAnyAccount: Bool        let cookieSource: ProviderCookieSource        let cacheScope: CookieHeaderCache.Scope?        let preferCachedCookieHeader: Bool?        let force: Bool    }     private static let openAIWebRefreshMultiplier: TimeInterval = 5    private static let openAIWebPrimaryFetchTimeout: TimeInterval = 25    private static let openAIWebRetryFetchTimeout: TimeInterval = 8    private static let openAIWebPostImportFetchTimeout: TimeInterval = 25     static func openAIWebDashboardFetchTimeout(didImportCookies: Bool) -> TimeInterval {        didImportCookies ? self.openAIWebPostImportFetchTimeout : self.openAIWebPrimaryFetchTimeout    }     static func openAIWebRetryDashboardFetchTimeout(afterCookieImport: Bool) -> TimeInterval {        afterCookieImport ? self.openAIWebPostImportFetchTimeout : self.openAIWebRetryFetchTimeout    }     nonisolated static func refreshPhase(        hasCompletedInitialRefresh: Bool) -> ProviderRefreshPhase    {        hasCompletedInitialRefresh ? .regular : .startup    }     nonisolated static func openAIWebRefreshPhase(        providerRefreshPhase: ProviderRefreshPhase,        startupConnectivityRetryAttempt: Int?) -> ProviderRefreshPhase    {        startupConnectivityRetryAttempt == nil ? providerRefreshPhase : .startup    }     private func openAIWebRefreshIntervalSeconds() -> TimeInterval {        let base = max(self.settings.refreshFrequency.seconds ?? 0, 120)        return base * Self.openAIWebRefreshMultiplier    }     func requestOpenAIDashboardRefreshIfStale(reason: String) {        guard self.isEnabled(.codex),              self.settings.openAIWebAccessEnabled,              self.settings.codexCookieSource.isEnabled        else { return }        let now = Date()        let refreshInterval = self.openAIWebRefreshIntervalSeconds()        let dashboard = self.openAIDashboard ?? self.lastOpenAIDashboardSnapshot        let lastUpdatedAt = dashboard?.updatedAt        let needsMenuHistoryRefresh = dashboard?.dailyBreakdown.isEmpty == true &&            dashboard?.usageBreakdown.isEmpty == true        if needsMenuHistoryRefresh,           Self.shouldSkipOpenAIWebEmptyHistoryRetry(.init(               force: false,               accountDidChange: self.openAIWebAccountDidChange,               lastError: self.lastOpenAIDashboardError,               lastSnapshotAt: lastUpdatedAt,               lastAttemptAt: self.lastOpenAIDashboardAttemptAt,               now: now,               refreshInterval: refreshInterval))        {            return        }        if let lastUpdatedAt, now.timeIntervalSince(lastUpdatedAt) < refreshInterval, !needsMenuHistoryRefresh {            return        }        let stamp = now.formatted(date: .abbreviated, time: .shortened)        self.logOpenAIWeb("[\(stamp)] OpenAI web refresh request: \(reason)")        let forceRefresh = Self.forceOpenAIWebRefreshForStaleRequest(            batterySaverEnabled: self.settings.openAIWebBatterySaverEnabled) || needsMenuHistoryRefresh        self.openAIWebLogger.info(            "OpenAI web stale refresh gate",            metadata: [                "reason": reason,                "force": forceRefresh ? "1" : "0",                "batterySaverEnabled": self.settings.openAIWebBatterySaverEnabled ? "1" : "0",                "interaction": ProviderInteractionContext.current == .userInitiated ? "user" : "background",            ])        let expectedGuard = self.freshCodexOpenAIWebRefreshGuard()        Task { await self.refreshOpenAIDashboardIfNeeded(force: forceRefresh, expectedGuard: expectedGuard) }    }     func applyOpenAIDashboard(        _ dash: OpenAIDashboardSnapshot,        targetEmail: String?,        expectedGuard: CodexAccountScopedRefreshGuard? = nil,        refreshTaskToken: UUID? = nil,        allowCodexUsageBackfill: Bool = true) async    {        guard self.shouldApplyOpenAIDashboardRefreshTask(token: refreshTaskToken) else { return }        self.settings.invalidateCodexAccountReconciliationSnapshotCache()        let authority = self.evaluateCodexDashboardAuthority(            dashboard: dash,            sourceKind: .liveWeb,            routingTargetEmail: targetEmail)        if let expectedGuard {            let shouldApply = switch authority.decision.disposition {            case .attach:                self.shouldApplyOpenAIDashboardRefreshGuard(                    expectedGuard: expectedGuard,                    routingTargetEmail: targetEmail)            case .displayOnly, .failClosed:                self.shouldApplyOpenAIDashboardPolicyResult(                    expectedGuard: expectedGuard,                    routingTargetEmail: targetEmail)            }            guard shouldApply else { return }        }         let attachedAccountEmail = self.codexDashboardAttachmentEmail(from: authority.input)         await self.applyOpenAIDashboardAuthorityDecision(            authority.decision,            dashboard: dash,            authorityInput: authority.input,            attachedAccountEmail: attachedAccountEmail,            allowCodexUsageBackfill: allowCodexUsageBackfill)    }     func applyOpenAIDashboardFailure(        message: String,        expectedGuard: CodexAccountScopedRefreshGuard? = nil,        refreshTaskToken: UUID? = nil,        routingTargetEmail: String? = nil) async    {        guard self.shouldApplyOpenAIDashboardRefreshTask(token: refreshTaskToken) else { return }        if let expectedGuard,           !self.shouldApplyOpenAIWebNonSuccessResult(               expectedGuard: expectedGuard,               routingTargetEmail: routingTargetEmail)        {            return        }        if self.openAIWebManagedTargetStoreIsUnreadable() {            await self.failClosedRefreshForUnreadableManagedCodexStore()            return        }        if self.openAIWebManagedTargetIsMissing() {            await self.failClosedRefreshForMissingManagedCodexTarget()            return        }        if self.openAIWebProfileTargetEmailIsMissing() {            await self.failClosedRefreshForMissingProfileCodexTarget()            return        }         OpenAIDashboardFetcher.evictAllCachedWebViews()        await MainActor.run {            if let cached = self.lastOpenAIDashboardSnapshot {                self.openAIDashboard = cached                self.openAIDashboardAttachmentAuthorized = self.lastOpenAIDashboardAttachmentAuthorized                let stamp = cached.updatedAt.formatted(date: .abbreviated, time: .shortened)                self.lastOpenAIDashboardError =                    "Last OpenAI dashboard refresh failed: \(message). Cached values from \(stamp)."            } else {                self.lastOpenAIDashboardError = message                self.openAIDashboard = nil                self.openAIDashboardAttachmentAuthorized = false            }        }    }     func applyOpenAIDashboardLoginRequiredFailure(        expectedGuard: CodexAccountScopedRefreshGuard? = nil,        refreshTaskToken: UUID? = nil,        routingTargetEmail: String? = nil) async    {        guard self.shouldApplyOpenAIDashboardRefreshTask(token: refreshTaskToken) else { return }        if let expectedGuard,           !self.shouldApplyOpenAIWebNonSuccessResult(               expectedGuard: expectedGuard,               routingTargetEmail: routingTargetEmail)        {            return        }        if self.openAIWebManagedTargetStoreIsUnreadable() {            await self.failClosedRefreshForUnreadableManagedCodexStore()            return        }        if self.openAIWebManagedTargetIsMissing() {            await self.failClosedRefreshForMissingManagedCodexTarget()            return        }        if self.openAIWebProfileTargetEmailIsMissing() {            await self.failClosedRefreshForMissingProfileCodexTarget()            return        }         OpenAIDashboardFetcher.evictAllCachedWebViews()        await MainActor.run {            self.lastOpenAIDashboardError = [                "OpenAI web access requires a signed-in chatgpt.com session.",                "Sign in using \(self.codexBrowserCookieOrder.loginHint), " +                    "then update OpenAI cookies in Providers → Codex.",            ].joined(separator: " ")            self.openAIDashboard = self.lastOpenAIDashboardSnapshot            self.openAIDashboardAttachmentAuthorized = self.lastOpenAIDashboardAttachmentAuthorized            self.openAIDashboardRequiresLogin = true        }    }     private func failClosedOpenAIDashboardSnapshot() {        self.applyOpenAIDashboardCleanup(Set(CodexDashboardCleanup.allCases), preserveVisibleDashboard: false)        self.openAIDashboardRequiresLogin = true    }     private func applyOpenAIDashboardAuthorityDecision(        _ decision: CodexDashboardAuthorityDecision,        dashboard: OpenAIDashboardSnapshot,        authorityInput: CodexDashboardAuthorityInput,        attachedAccountEmail: String?,        allowCodexUsageBackfill: Bool) async    {        switch decision.disposition {        case .attach:            self.openAIDashboard = dashboard            self.openAIDashboardAttachmentAuthorized = true            self.lastOpenAIDashboardSnapshot = dashboard            self.lastOpenAIDashboardAttachmentAuthorized = true            self.lastOpenAIDashboardError = nil            self.openAIDashboardRequiresLogin = false             if decision.allowedEffects.contains(.usageBackfill),               allowCodexUsageBackfill,               self.snapshots[.codex] == nil,               let usage = dashboard.toUsageSnapshot(provider: .codex, accountEmail: attachedAccountEmail)            {                self.snapshots[.codex] = usage                self.errors[.codex] = nil                self.failureGates[.codex]?.recordSuccess()                self.lastSourceLabels[.codex] = "openai-web"            }             if decision.allowedEffects.contains(.creditsAttachment),               self.credits == nil,               let credits = dashboard.toCreditsSnapshot()            {                self.credits = credits                self.lastCreditsSnapshot = credits                self.lastCreditsSnapshotAccountKey = Self.normalizeCodexAccountScopedKey(attachedAccountEmail)                self.lastCreditsSource = .dashboardWeb                self.lastCreditsError = nil                self.creditsFailureStreak = 0            }             if decision.allowedEffects.contains(.refreshGuardSeed) {                self.seedCodexAccountScopedRefreshGuard(accountEmail: attachedAccountEmail)            }             if let attachedAccountEmail, !attachedAccountEmail.isEmpty {                OpenAIDashboardCacheStore.save(OpenAIDashboardCache(                    accountEmail: attachedAccountEmail,                    snapshot: dashboard))            }             if decision.allowedEffects.contains(.historicalBackfill) {                self.backfillCodexHistoricalFromDashboardIfNeeded(                    dashboard,                    authorityDecision: decision,                    attachedAccountEmail: attachedAccountEmail)            }         case .displayOnly:            self.applyOpenAIDashboardCleanup(decision.cleanup, preserveVisibleDashboard: true)            self.openAIDashboard = dashboard            self.openAIDashboardAttachmentAuthorized = false            self.lastOpenAIDashboardSnapshot = dashboard            self.lastOpenAIDashboardAttachmentAuthorized = false            self.lastOpenAIDashboardError = nil            self.openAIDashboardRequiresLogin = false         case .failClosed:            self.applyOpenAIDashboardCleanup(decision.cleanup, preserveVisibleDashboard: false)            self.lastOpenAIDashboardError = self.openAIDashboardPolicyFailureMessage(                for: decision,                authorityInput: authorityInput)            self.openAIDashboardRequiresLogin = true        }    }     private func applyOpenAIDashboardCleanup(        _ cleanup: Set<CodexDashboardCleanup>,        preserveVisibleDashboard: Bool)    {        if cleanup.contains(.dashboardDerivedUsage) {            self.clearDashboardDerivedCodexUsageIfNeeded()        }        if cleanup.contains(.dashboardDerivedCredits) {            self.clearDashboardDerivedCreditsIfNeeded()        }        if cleanup.contains(.dashboardRefreshGuardSeed) {            self.clearDashboardRefreshGuardSeedIfNeeded()        }        if cleanup.contains(.dashboardCache) {            OpenAIDashboardCacheStore.clear()        }        if cleanup.contains(.dashboardSnapshot), !preserveVisibleDashboard {            self.openAIDashboard = nil            self.openAIDashboardAttachmentAuthorized = false            self.lastOpenAIDashboardSnapshot = nil            self.lastOpenAIDashboardAttachmentAuthorized = false        }    }     private func clearDashboardDerivedCodexUsageIfNeeded() {        guard self.lastSourceLabels[.codex] == "openai-web" else { return }        self.snapshots.removeValue(forKey: .codex)        self.errors[.codex] = nil        self.lastSourceLabels.removeValue(forKey: .codex)        self.lastFetchAttempts.removeValue(forKey: .codex)        self.accountSnapshots.removeValue(forKey: .codex)        self.codexAccountSnapshots = []        self.failureGates[.codex]?.reset()        self.lastKnownSessionRemaining.removeValue(forKey: .codex)        self.lastKnownSessionWindowSource.removeValue(forKey: .codex)    }     private func clearDashboardDerivedCreditsIfNeeded() {        guard self.lastCreditsSource == .dashboardWeb else { return }        self.credits = nil        self.lastCreditsError = nil        self.lastCreditsSnapshot = nil        self.lastCreditsSnapshotAccountKey = nil        self.lastCreditsSource = .none        self.creditsFailureStreak = 0    }     private func clearDashboardRefreshGuardSeedIfNeeded() {        self.lastCodexAccountScopedRefreshGuard = self.currentCodexAccountScopedRefreshGuard(            preferCurrentSnapshot: false,            allowLastKnownLiveFallback: false)    }     private func openAIDashboardPolicyFailureMessage(        for decision: CodexDashboardAuthorityDecision,        authorityInput: CodexDashboardAuthorityInput) -> String    {        switch decision.reason {        case let .wrongEmail(expected, actual):            [                "OpenAI dashboard signed in as \(actual ?? "unknown"), but Codex uses \(expected ?? "unknown").",                "Switch accounts in your browser and update OpenAI cookies in Providers → Codex.",            ].joined(separator: " ")        case let .sameEmailAmbiguity(email):            "OpenAI dashboard ownership is ambiguous for \(email); Codex will not attach dashboard data."        case .missingDashboardSignedInEmail:            "OpenAI dashboard did not report a signed-in account. Refresh OpenAI cookies and try again."        case .unresolvedWithoutTrustedEvidence:            "OpenAI dashboard ownership could not be verified for the active Codex account."        case .providerAccountMissingScopedEmail:            "Codex account ownership could not be verified because the scoped email is unavailable."        case .providerAccountLacksExactOwnershipProof:            [                "OpenAI dashboard ownership could not be matched to the active Codex account.",                "Refresh Codex account data, then retry OpenAI web access.",            ].joined(separator: " ")        case .exactProviderAccountMatch,             .trustedEmailMatchNoCompetingOwner,             .trustedContinuityNoCompetingOwner:            "OpenAI dashboard ownership policy blocked this dashboard."        }    }     func refreshOpenAIDashboardIfNeeded(        force: Bool = false,        expectedGuard: CodexAccountScopedRefreshGuard? = nil,        bypassCoalescing: Bool = false,        allowCodexUsageBackfill: Bool = true) async    {        self.syncOpenAIWebState()        guard self.isEnabled(.codex),              self.settings.openAIWebAccessEnabled,              self.settings.codexCookieSource.isEnabled        else { return }        if self.openAIWebManagedTargetStoreIsUnreadable() {            await self.failClosedRefreshForUnreadableManagedCodexStore()            return        }        if self.openAIWebManagedTargetIsMissing() {            await self.failClosedRefreshForMissingManagedCodexTarget()            return        }        if self.openAIWebProfileTargetEmailIsMissing() {            await self.failClosedRefreshForMissingProfileCodexTarget()            return        }         let allowCurrentSnapshotFallback = expectedGuard?.source == .liveSystem && expectedGuard?            .identity == .unresolved        let targetEmail = self.currentCodexOpenAIWebTargetEmail(            allowCurrentSnapshotFallback: allowCurrentSnapshotFallback,            allowLastKnownLiveFallback: expectedGuard?.identity != .unresolved)        let refreshKey = self.openAIDashboardRefreshKey(targetEmail: targetEmail, expectedGuard: expectedGuard)        if !bypassCoalescing,           let task = self.openAIDashboardRefreshTask,           self.openAIDashboardRefreshTaskKey == refreshKey        {            await task.value            return        }        self.handleOpenAIWebTargetEmailChangeIfNeeded(            targetEmail: targetEmail,            targetScope: self.codexCookieCacheScopeForOpenAIWeb())         let now = Date()        let minInterval = self.openAIWebRefreshIntervalSeconds()        let refreshGate = OpenAIWebRefreshGateContext(            force: force,            accountDidChange: self.openAIWebAccountDidChange,            lastError: self.lastOpenAIDashboardError,            lastSnapshotAt: self.lastOpenAIDashboardSnapshot?.updatedAt,            lastAttemptAt: self.lastOpenAIDashboardAttemptAt,            now: now,            refreshInterval: minInterval)        if Self.shouldSkipOpenAIWebRefresh(refreshGate) {            return        }        self.lastOpenAIDashboardAttemptAt = now         let taskToken = UUID()        let context = OpenAIDashboardRefreshContext(            targetEmail: targetEmail,            allowCurrentSnapshotFallback: allowCurrentSnapshotFallback,            expectedGuard: expectedGuard,            refreshTaskToken: taskToken,            allowCodexUsageBackfill: allowCodexUsageBackfill,            force: force)        let task = Task { [weak self] in            guard let self else { return }            await self.performOpenAIDashboardRefreshIfNeeded(context)        }        self.openAIDashboardRefreshTask = task        self.openAIDashboardRefreshTaskKey = refreshKey        self.openAIDashboardRefreshTaskToken = taskToken        await task.value        if self.openAIDashboardRefreshTaskToken == taskToken {            self.openAIDashboardRefreshTask = nil            self.openAIDashboardRefreshTaskKey = nil            self.openAIDashboardRefreshTaskToken = nil        }    }     func scheduleOpenAIDashboardRefreshIfNeeded(expectedGuard: CodexAccountScopedRefreshGuard? = nil) {        self.syncOpenAIWebState()        let allowCurrentSnapshotFallback = expectedGuard?.source == .liveSystem && expectedGuard?            .identity == .unresolved        let targetEmail = self.currentCodexOpenAIWebTargetEmail(            allowCurrentSnapshotFallback: allowCurrentSnapshotFallback,            allowLastKnownLiveFallback: expectedGuard?.identity != .unresolved)        let refreshKey = self.openAIDashboardRefreshKey(targetEmail: targetEmail, expectedGuard: expectedGuard)        if let task = self.openAIDashboardBackgroundRefreshTask,           !task.isCancelled,           self.openAIDashboardBackgroundRefreshTaskKey == refreshKey        {            return        }         if self.openAIDashboardBackgroundRefreshTaskKey != nil,           self.openAIDashboardBackgroundRefreshTaskKey != refreshKey        {            self.invalidateOpenAIDashboardRefreshTask()        }        self.openAIDashboardBackgroundRefreshTask?.cancel()        self.openAIDashboardBackgroundRefreshTaskKey = refreshKey        self.openAIDashboardBackgroundRefreshTask = Task(priority: .utility) { @MainActor [weak self] in            guard let self else { return }            defer {                if self.openAIDashboardBackgroundRefreshTaskKey == refreshKey {

Lines 1–500 of 1539.