507 lines · 19.0 KB · Swift
RawDownload
import AppKitimport CodexBarCoreimport KeyboardShortcutsimport Observationimport QuartzCoreimport Securityimport SwiftUI @mainstruct CodexBarApp: App {    @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate    @State private var settings: SettingsStore    @State private var store: UsageStore    @State private var managedCodexAccountCoordinator: ManagedCodexAccountCoordinator    @State private var codexAccountPromotionCoordinator: CodexAccountPromotionCoordinator    private let preferencesSelection: PreferencesSelection    private let account: AccountInfo     init() {        let env = ProcessInfo.processInfo.environment        let storedLevel = CodexBarLog.parseLevel(UserDefaults.standard.string(forKey: "debugLogLevel")) ?? .verbose        let level = CodexBarLog.parseLevel(env["CODEXBAR_LOG_LEVEL"]) ?? storedLevel        CodexBarLog.bootstrapIfNeeded(.init(            destination: .oslog(subsystem: "com.steipete.codexbar"),            level: level,            json: false))         let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "unknown"        let build = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "unknown"        let gitCommit = Bundle.main.object(forInfoDictionaryKey: "CodexGitCommit") as? String ?? "unknown"        let buildTimestamp = Bundle.main.object(forInfoDictionaryKey: "CodexBuildTimestamp") as? String ?? "unknown"        CodexBarLog.logger(LogCategories.app).info(            "CodexBar starting",            metadata: [                "version": version,                "build": build,                "git": gitCommit,                "built": buildTimestamp,            ])         KeychainAccessGate.isDisabled = UserDefaults.standard.bool(forKey: "debugDisableKeychainAccess")        KeychainPromptCoordinator.install()        if MainThreadHangWatchdog.isEnabledForCurrentProcess {            MainThreadHangWatchdog.shared.start()        }         let preferencesSelection = PreferencesSelection()        let settings = SettingsStore()        Self.applyLanguagePreference(from: settings)        configureUsageFormatterLocalizationProvider()        let managedCodexAccountCoordinator = ManagedCodexAccountCoordinator()        managedCodexAccountCoordinator.onManagedAccountsDidChange = {            _ = settings.refreshCodexAccountReconciliationAfterManagedAccountsDidChange()        }        _ = settings.persistResolvedCodexActiveSourceCorrectionIfNeeded()        let fetcher = UsageFetcher()        let browserDetection = BrowserDetection(cacheTTL: BrowserDetection.defaultCacheTTL)        let account = fetcher.loadAccountInfo()        let store = UsageStore(fetcher: fetcher, browserDetection: browserDetection, settings: settings)        let codexAccountPromotionCoordinator = CodexAccountPromotionCoordinator(            settingsStore: settings,            usageStore: store,            managedAccountCoordinator: managedCodexAccountCoordinator)        self.preferencesSelection = preferencesSelection        _settings = State(wrappedValue: settings)        _store = State(wrappedValue: store)        _managedCodexAccountCoordinator = State(wrappedValue: managedCodexAccountCoordinator)        _codexAccountPromotionCoordinator = State(wrappedValue: codexAccountPromotionCoordinator)        self.account = account        CodexBarLog.setLogLevel(settings.debugLogLevel)        self.appDelegate.configure(.init(            store: store,            settings: settings,            account: account,            selection: preferencesSelection,            managedCodexAccountCoordinator: managedCodexAccountCoordinator,            codexAccountPromotionCoordinator: codexAccountPromotionCoordinator))    }     @SceneBuilder    var body: some Scene {        // Hidden 1×1 window to keep SwiftUI's lifecycle alive so `Settings` scene        // shows the native toolbar tabs even though the UI is AppKit-based.        WindowGroup("CodexBarLifecycleKeepalive") {            HiddenWindowView()        }        .defaultSize(width: 20, height: 20)        .windowStyle(.hiddenTitleBar)         Settings {            PreferencesView(                settings: self.settings,                store: self.store,                updater: self.appDelegate.updaterController,                selection: self.preferencesSelection,                managedCodexAccountCoordinator: self.managedCodexAccountCoordinator,                codexAccountPromotionCoordinator: self.codexAccountPromotionCoordinator,                runProviderLoginFlow: { provider in                    await self.appDelegate.runProviderLoginFlow(provider)                })        }        .defaultSize(width: PreferencesTab.general.preferredWidth, height: PreferencesTab.general.preferredHeight)        .windowResizability(.contentSize)    }     private func openSettings(tab: PreferencesTab) {        self.preferencesSelection.tab = tab        NSApp.activate(ignoringOtherApps: true)        _ = NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil)    }     private static func applyLanguagePreference(from settings: SettingsStore) {        let language = settings.appLanguage        if language.isEmpty {            UserDefaults.standard.removeObject(forKey: "AppleLanguages")        } else {            UserDefaults.standard.set([language], forKey: "AppleLanguages")        }    }} // MARK: - Updater abstraction @MainActorprotocol UpdaterProviding: AnyObject {    var automaticallyChecksForUpdates: Bool { get set }    var automaticallyDownloadsUpdates: Bool { get set }    var isAvailable: Bool { get }    var unavailableReason: String? { get }    var updateStatus: UpdateStatus { get }    func checkForUpdates(_ sender: Any?)    func installUpdate()} /// No-op updater used for debug builds and non-bundled runs to suppress Sparkle dialogs.final class DisabledUpdaterController: UpdaterProviding {    var automaticallyChecksForUpdates: Bool = false    var automaticallyDownloadsUpdates: Bool = false    let isAvailable: Bool = false    let unavailableReason: String?    let updateStatus = UpdateStatus()     init(unavailableReason: String? = nil) {        self.unavailableReason = unavailableReason    }     func checkForUpdates(_ sender: Any?) {}    func installUpdate() {}} @MainActor@Observablefinal class UpdateStatus {    static let disabled = UpdateStatus()    var isUpdateReady: Bool     init(isUpdateReady: Bool = false) {        self.isUpdateReady = isUpdateReady    }} #if canImport(Sparkle) && ENABLE_SPARKLEimport Sparkle @MainActorfinal class SparkleUpdaterController: NSObject, UpdaterProviding, SPUUpdaterDelegate {    private final class ImmediateInstallHandler: @unchecked Sendable {        private let handler: () -> Void         init(_ handler: @escaping () -> Void) {            self.handler = handler        }         func install() {            self.handler()        }    }     private lazy var controller = SPUStandardUpdaterController(        startingUpdater: false,        updaterDelegate: self,        userDriverDelegate: nil)    let updateStatus = UpdateStatus()    let unavailableReason: String? = nil    private var immediateInstallHandler: ImmediateInstallHandler?     init(savedAutoUpdate: Bool) {        super.init()        let updater = self.controller.updater        updater.automaticallyChecksForUpdates = savedAutoUpdate        updater.automaticallyDownloadsUpdates = savedAutoUpdate        self.controller.startUpdater()    }     var automaticallyChecksForUpdates: Bool {        get { self.controller.updater.automaticallyChecksForUpdates }        set { self.controller.updater.automaticallyChecksForUpdates = newValue }    }     var automaticallyDownloadsUpdates: Bool {        get { self.controller.updater.automaticallyDownloadsUpdates }        set { self.controller.updater.automaticallyDownloadsUpdates = newValue }    }     var isAvailable: Bool {        true    }     func checkForUpdates(_ sender: Any?) {        self.controller.checkForUpdates(sender)    }     func installUpdate() {        guard let immediateInstallHandler else {            self.controller.checkForUpdates(nil)            return        }         immediateInstallHandler.install()    }     nonisolated func updater(_ updater: SPUUpdater, didDownloadUpdate item: SUAppcastItem) {        _ = updater        _ = item    }     nonisolated func updater(_ updater: SPUUpdater, failedToDownloadUpdate item: SUAppcastItem, error: Error) {        _ = updater        _ = item        _ = error        Task { @MainActor in            self.immediateInstallHandler = nil            self.updateStatus.isUpdateReady = false        }    }     nonisolated func userDidCancelDownload(_ updater: SPUUpdater) {        _ = updater        Task { @MainActor in            self.immediateInstallHandler = nil            self.updateStatus.isUpdateReady = false        }    }     nonisolated func updater(        _ updater: SPUUpdater,        willInstallUpdateOnQuit item: SUAppcastItem,        immediateInstallationBlock immediateInstallHandler: @escaping () -> Void)        -> Bool    {        _ = updater        _ = item        let installHandler = ImmediateInstallHandler(immediateInstallHandler)        Task { @MainActor in            self.immediateInstallHandler = installHandler            self.updateStatus.isUpdateReady = true        }        return true    }     nonisolated func updater(_ updater: SPUUpdater, didAbortWithError error: Error) {        _ = updater        _ = error        Task { @MainActor in            self.immediateInstallHandler = nil            self.updateStatus.isUpdateReady = false        }    }     nonisolated func updater(        _ updater: SPUUpdater,        userDidMake choice: SPUUserUpdateChoice,        forUpdate updateItem: SUAppcastItem,        state: SPUUserUpdateState)    {        let downloaded = state.stage == .downloaded        Task { @MainActor in            switch choice {            case .install, .skip:                self.immediateInstallHandler = nil                self.updateStatus.isUpdateReady = false            case .dismiss:                self.updateStatus.isUpdateReady = downloaded            @unknown default:                self.immediateInstallHandler = nil                self.updateStatus.isUpdateReady = false            }        }    }     nonisolated func allowedChannels(for updater: SPUUpdater) -> Set<String> {        UpdateChannel.current.allowedSparkleChannels    }} private func isDeveloperIDSigned(bundleURL: URL) -> Bool {    var staticCode: SecStaticCode?    guard SecStaticCodeCreateWithPath(bundleURL as CFURL, SecCSFlags(), &staticCode) == errSecSuccess,          let code = staticCode else { return false }     var infoCF: CFDictionary?    guard SecCodeCopySigningInformation(code, SecCSFlags(rawValue: kSecCSSigningInformation), &infoCF) == errSecSuccess,          let info = infoCF as? [String: Any],          let certs = info[kSecCodeInfoCertificates as String] as? [SecCertificate],          let leaf = certs.first else { return false }     if let summary = SecCertificateCopySubjectSummary(leaf) as String? {        return summary.hasPrefix("Developer ID Application:")    }    return false} @MainActorprivate func makeUpdaterController() -> UpdaterProviding {    let bundleURL = Bundle.main.bundleURL    let isBundledApp = bundleURL.pathExtension == "app"    guard isBundledApp else {        return DisabledUpdaterController(unavailableReason: "Updates unavailable in this build.")    }     if InstallOrigin.isHomebrewCask(appBundleURL: bundleURL) {        return DisabledUpdaterController(            unavailableReason: "Updates managed by Homebrew. Run: brew upgrade --cask steipete/tap/codexbar")    }     guard isDeveloperIDSigned(bundleURL: bundleURL) else {        return DisabledUpdaterController(unavailableReason: "Updates unavailable in this build.")    }     let defaults = UserDefaults.standard    let autoUpdateKey = "autoUpdateEnabled"    // Default to true for first launch; fall back to saved preference thereafter.    let savedAutoUpdate = (defaults.object(forKey: autoUpdateKey) as? Bool) ?? true    return SparkleUpdaterController(savedAutoUpdate: savedAutoUpdate)}#elseprivate func makeUpdaterController() -> UpdaterProviding {    DisabledUpdaterController()}#endif @MainActorfinal class AppDelegate: NSObject, NSApplicationDelegate {    struct Dependencies {        let store: UsageStore        let settings: SettingsStore        let account: AccountInfo        let selection: PreferencesSelection        let managedCodexAccountCoordinator: ManagedCodexAccountCoordinator        let codexAccountPromotionCoordinator: CodexAccountPromotionCoordinator    }     let updaterController: UpdaterProviding = makeUpdaterController()    private let confettiOverlayController = ScreenConfettiOverlayController()    private let confettiLogger = CodexBarLog.logger(LogCategories.confetti)    private var statusController: StatusItemControlling?    private var store: UsageStore?    private var settings: SettingsStore?    private var account: AccountInfo?    private var preferencesSelection: PreferencesSelection?    private var managedCodexAccountCoordinator: ManagedCodexAccountCoordinator?    private var codexAccountPromotionCoordinator: CodexAccountPromotionCoordinator?    private var hasInstalledWeeklyLimitResetObserver = false    var terminateActiveProcessesForAppShutdown: () -> Void = {        TTYCommandRunner.terminateActiveProcessesForAppShutdown()    }     func configure(_ dependencies: Dependencies) {        self.store = dependencies.store        self.settings = dependencies.settings        self.account = dependencies.account        self.preferencesSelection = dependencies.selection        self.managedCodexAccountCoordinator = dependencies.managedCodexAccountCoordinator        self.codexAccountPromotionCoordinator = dependencies.codexAccountPromotionCoordinator    }     func applicationWillFinishLaunching(_ notification: Notification) {        self.configureAppIconForMacOSVersion()    }     func applicationDidFinishLaunching(_ notification: Notification) {        AppNotifications.shared.requestAuthorizationOnStartup()        self.ensureStatusController()        KeyboardShortcuts.onKeyUp(for: .openMenu) { [weak self] in            // KeyboardShortcuts dispatches both normal and menu-tracking hotkeys on the main event loop.            MainActor.assumeIsolated {                self?.statusController?.openMenuFromShortcut()            }        }        if !self.hasInstalledWeeklyLimitResetObserver {            NotificationCenter.default.addObserver(                self,                selector: #selector(self.handleWeeklyLimitResetNotification(_:)),                name: .codexbarWeeklyLimitReset,                object: nil)            self.hasInstalledWeeklyLimitResetObserver = true        }    }     func applicationWillTerminate(_ notification: Notification) {        self.statusController?.prepareForAppShutdown()        self.confettiOverlayController.dismiss()        self.dismissAppKitWindowsForShutdown()        self.terminateActiveProcessesForAppShutdown()    }     func runProviderLoginFlow(_ provider: UsageProvider) async {        self.ensureStatusController()        guard let statusController else { return }        await statusController.runLoginFlowFromSettings(provider: provider)    }     @objc private func handleWeeklyLimitResetNotification(_ notification: Notification) {        guard let event = notification.object as? WeeklyLimitResetEvent else { return }        guard self.settings?.confettiOnWeeklyLimitResetsEnabled == true else { return }        let origin = self.statusController?.celebrationOriginPoint(for: event.provider)        self.confettiLogger.info(            "Triggering confetti",            metadata: [                "provider": event.provider.rawValue,                "accountIdentifier": event.accountIdentifier,                "originKnown": origin == nil ? "0" : "1",            ])        self.confettiOverlayController.play(originInScreen: origin)    }     /// Use the classic (non-Liquid Glass) app icon on macOS versions before 26.    private func configureAppIconForMacOSVersion() {        if #unavailable(macOS 26) {            self.applyClassicAppIcon()        }    }     private func applyClassicAppIcon() {        guard let classicIcon = Self.loadClassicIcon() else { return }        NSApp.applicationIconImage = classicIcon    }     private static func loadClassicIcon() -> NSImage? {        guard let url = self.classicIconURL(),              let image = NSImage(contentsOf: url)        else {            return nil        }        return image    }     private static func classicIconURL() -> URL? {        Bundle.main.url(forResource: "Icon-classic", withExtension: "icns")    }     private func dismissAppKitWindowsForShutdown() {        guard let app = NSApp else { return }        for window in app.windows {            window.orderOut(nil)        }    }     private func ensureStatusController() {        if self.statusController != nil { return }         if let store,           let settings,           let account,           let selection = self.preferencesSelection,           let managedCodexAccountCoordinator,           let codexAccountPromotionCoordinator        {            self.statusController = StatusItemController.factory(                store,                settings,                account,                self.updaterController,                selection,                managedCodexAccountCoordinator,                codexAccountPromotionCoordinator)            return        }         // Defensive fallback: this should not be hit in normal app lifecycle.        CodexBarLog.logger(LogCategories.app)            .error("StatusItemController fallback path used; settings/store mismatch likely.")        assertionFailure("StatusItemController fallback path used; check app lifecycle wiring.")        let fallbackSettings = SettingsStore()        let fetcher = UsageFetcher()        let browserDetection = BrowserDetection(cacheTTL: BrowserDetection.defaultCacheTTL)        let fallbackAccount = fetcher.loadAccountInfo()        let fallbackStore = UsageStore(fetcher: fetcher, browserDetection: browserDetection, settings: fallbackSettings)        let fallbackManagedCodexAccountCoordinator = ManagedCodexAccountCoordinator()        let fallbackCodexAccountPromotionCoordinator = CodexAccountPromotionCoordinator(            settingsStore: fallbackSettings,            usageStore: fallbackStore,            managedAccountCoordinator: fallbackManagedCodexAccountCoordinator)        self.statusController = StatusItemController.factory(            fallbackStore,            fallbackSettings,            fallbackAccount,            self.updaterController,            PreferencesSelection(),            fallbackManagedCodexAccountCoordinator,

Lines 1–500 of 507.