70 lines · 2.1 KB · Swift
RawDownload
import Foundation final class CodexCLILaunchGate: @unchecked Sendable {    static let shared = CodexCLILaunchGate()    static let cooldown: TimeInterval = 30 * 60     private struct Entry {        let message: String        let expiresAt: Date    }     private let lock = NSLock()    private var entries: [String: Entry] = [:]     func backgroundSkipMessage(        binary: String,        now: Date = Date(),        interaction: ProviderInteraction = ProviderInteractionContext.current) -> String?    {        guard interaction == .background else { return nil }         self.lock.lock()        defer { self.lock.unlock() }         guard let entry = self.entries[binary] else { return nil }        guard entry.expiresAt > now else {            self.entries.removeValue(forKey: binary)            return nil        }        return entry.message    }     @discardableResult    func recordLaunchFailure(binary: String, message: String, now: Date = Date()) -> String? {        guard Self.shouldThrottleLaunchFailure(message) else { return nil }        let throttled = Self.throttledMessage(binary: binary, originalMessage: message)        let entry = Entry(            message: throttled,            expiresAt: now.addingTimeInterval(Self.cooldown))         self.lock.lock()        self.entries[binary] = entry        self.lock.unlock()         return throttled    }     func resetForTesting() {        self.lock.lock()        self.entries.removeAll()        self.lock.unlock()    }     static func shouldThrottleLaunchFailure(_ message: String) -> Bool {        let lower = message.lowercased()        if lower.contains("openpty") ||            lower.contains("write to pty") ||            lower.contains("app shutdown")        {            return false        }        return true    }     private static func throttledMessage(binary: String, originalMessage: String) -> String {        "Codex CLI launch failed; background refresh is paused for 30 minutes. " +            "Reinstall or unblock `\(binary)` in macOS security settings, then refresh manually. " +            "Last error: \(originalMessage)"    }}