71 lines
2.1 KiB
Swift
71 lines
2.1 KiB
Swift
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)"
|
|
}
|
|
}
|