mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-07 03:33:38 +00:00
* feat(macos): automate local gateway setup * fix(macos): auto-approve the local Mac node * chore(macos): refresh generated setup metadata * chore(macos): refresh generated setup metadata * chore(macos): refresh generated setup metadata * chore(macos): refresh generated setup metadata * chore(macos): refresh generated setup metadata
102 lines
3.5 KiB
Swift
102 lines
3.5 KiB
Swift
import AppKit
|
|
import Foundation
|
|
import OSLog
|
|
|
|
@MainActor
|
|
final class CLIInstallPrompter {
|
|
static let shared = CLIInstallPrompter()
|
|
private let logger = Logger(subsystem: "ai.openclaw", category: "cli.prompt")
|
|
private var isPrompting = false
|
|
|
|
func checkAndPromptIfNeeded(reason: String) {
|
|
guard !self.isPrompting else { return }
|
|
self.isPrompting = true
|
|
Task { @MainActor in
|
|
await self.checkAndPromptIfNeededAsync(reason: reason)
|
|
self.isPrompting = false
|
|
}
|
|
}
|
|
|
|
private func checkAndPromptIfNeededAsync(reason: String) async {
|
|
guard AppStateStore.shared.onboardingSeen else { return }
|
|
guard AppStateStore.shared.connectionMode == .local else { return }
|
|
guard let version = Self.appVersion() else { return }
|
|
let status = await CLIInstaller.status()
|
|
guard AppStateStore.shared.onboardingSeen else { return }
|
|
guard AppStateStore.shared.connectionMode == .local else { return }
|
|
guard !status.isReady else { return }
|
|
let lastPrompt = UserDefaults.standard.string(forKey: cliInstallPromptedVersionKey)
|
|
guard lastPrompt != version else { return }
|
|
UserDefaults.standard.set(version, forKey: cliInstallPromptedVersionKey)
|
|
|
|
let alert = NSAlert()
|
|
alert.messageText = "Install OpenClaw CLI?"
|
|
alert.informativeText = "Local mode needs the CLI so launchd can run the gateway."
|
|
alert.addButton(withTitle: "Install CLI")
|
|
alert.addButton(withTitle: "Not now")
|
|
alert.addButton(withTitle: "Open Settings")
|
|
let response = alert.runModal()
|
|
|
|
switch response {
|
|
case .alertFirstButtonReturn:
|
|
Task { await self.installCLI() }
|
|
case .alertThirdButtonReturn:
|
|
self.openSettings(tab: .connection)
|
|
default:
|
|
break
|
|
}
|
|
|
|
self.logger.debug("cli install prompt handled reason=\(reason, privacy: .public)")
|
|
}
|
|
|
|
private func installCLI() async {
|
|
let status = StatusBox()
|
|
let installed = await CLIInstaller.install { message in
|
|
await status.set(message)
|
|
}
|
|
if installed {
|
|
await status.set("Starting OpenClaw Gateway…")
|
|
let activation = await CLIInstaller.activateLocalGateway()
|
|
let message = switch activation {
|
|
case .ready:
|
|
"OpenClaw Gateway is ready."
|
|
case .deferred:
|
|
"OpenClaw is installed. The Gateway will start when This Mac is active and resumed."
|
|
case .failed:
|
|
"OpenClaw was installed, but the Gateway did not start. Open Settings to retry."
|
|
}
|
|
await status.set(message)
|
|
}
|
|
if let message = await status.get() {
|
|
let alert = NSAlert()
|
|
alert.messageText = "CLI install finished"
|
|
alert.informativeText = message
|
|
alert.runModal()
|
|
}
|
|
}
|
|
|
|
private func openSettings(tab: SettingsTab) {
|
|
SettingsTabRouter.request(tab)
|
|
SettingsWindowOpener.shared.open()
|
|
DispatchQueue.main.async {
|
|
NotificationCenter.default.post(name: .openclawSelectSettingsTab, object: tab)
|
|
}
|
|
}
|
|
|
|
private static func appVersion() -> String? {
|
|
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
|
}
|
|
}
|
|
|
|
private actor StatusBox {
|
|
private var value: String?
|
|
|
|
func set(_ value: String) {
|
|
self.value = value
|
|
}
|
|
|
|
func get() -> String? {
|
|
self.value
|
|
}
|
|
}
|