fix(macos): restore gateway launch agent build

This commit is contained in:
Peter Steinberger
2026-01-12 04:07:28 +00:00
parent 986ff8c59f
commit d26518687a
2 changed files with 39 additions and 37 deletions

View File

@@ -39,7 +39,6 @@ final class CLIInstallPrompter {
guard !self.isPrompting else { return false } guard !self.isPrompting else { return false }
guard AppStateStore.shared.onboardingSeen else { return false } guard AppStateStore.shared.onboardingSeen else { return false }
guard AppStateStore.shared.connectionMode == .local else { return false } guard AppStateStore.shared.connectionMode == .local else { return false }
guard !AppStateStore.shared.attachExistingGatewayOnly else { return false }
guard CLIInstaller.installedLocation() == nil else { return false } guard CLIInstaller.installedLocation() == nil else { return false }
guard let version = Self.appVersion() else { return false } guard let version = Self.appVersion() else { return false }
let lastPrompt = UserDefaults.standard.string(forKey: cliInstallPromptedVersionKey) let lastPrompt = UserDefaults.standard.string(forKey: cliInstallPromptedVersionKey)
@@ -47,11 +46,11 @@ final class CLIInstallPrompter {
} }
private func installCLI() async { private func installCLI() async {
var lastStatus: String? let status = StatusBox()
await CLIInstaller.install { message in await CLIInstaller.install { message in
lastStatus = message await status.set(message)
} }
if let message = lastStatus { if let message = await status.get() {
let alert = NSAlert() let alert = NSAlert()
alert.messageText = "CLI install finished" alert.messageText = "CLI install finished"
alert.informativeText = message alert.informativeText = message
@@ -71,3 +70,15 @@ final class CLIInstallPrompter {
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? 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
}
}

View File

@@ -7,16 +7,15 @@ enum GatewayLaunchAgentManager {
private static let disableLaunchAgentMarker = ".clawdbot/disable-launchagent" private static let disableLaunchAgentMarker = ".clawdbot/disable-launchagent"
private enum GatewayProgramArgumentsError: LocalizedError { private enum GatewayProgramArgumentsError: LocalizedError {
case cliNotFound case message(String)
var errorDescription: String? { var errorDescription: String? {
switch self { switch self {
case .cliNotFound: case let .message(message):
"clawdbot CLI not found in PATH; install the CLI." return message
} }
} }
} }
private static var plistURL: URL { private static var plistURL: URL {
FileManager.default.homeDirectoryForCurrentUser FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent("Library/LaunchAgents/\(gatewayLaunchdLabel).plist") .appendingPathComponent("Library/LaunchAgents/\(gatewayLaunchdLabel).plist")
@@ -29,10 +28,10 @@ enum GatewayLaunchAgentManager {
private static func gatewayProgramArguments( private static func gatewayProgramArguments(
port: Int, port: Int,
bind: String) -> Result<[String], GatewayProgramArgumentsError> bind: String,
{ ) -> Result<[String], GatewayProgramArgumentsError> {
#if DEBUG
let projectRoot = CommandResolver.projectRoot() let projectRoot = CommandResolver.projectRoot()
#if DEBUG
if let localBin = CommandResolver.projectClawdbotExecutable(projectRoot: projectRoot) { if let localBin = CommandResolver.projectClawdbotExecutable(projectRoot: projectRoot) {
return .success([localBin, "gateway-daemon", "--port", "\(port)", "--bind", bind]) return .success([localBin, "gateway-daemon", "--port", "\(port)", "--bind", bind])
} }
@@ -55,22 +54,18 @@ enum GatewayLaunchAgentManager {
return .success([gatewayBin, "gateway-daemon", "--port", "\(port)", "--bind", bind]) return .success([gatewayBin, "gateway-daemon", "--port", "\(port)", "--bind", bind])
} }
let fallbackProjectRoot = CommandResolver.projectRoot() if let entry = CommandResolver.gatewayEntrypoint(in: projectRoot),
if let entry = CommandResolver.gatewayEntrypoint(in: fallbackProjectRoot) { case let .success(runtime) = CommandResolver.runtimeResolution(searchPaths: searchPaths)
switch CommandResolver.runtimeResolution(searchPaths: searchPaths) { {
case let .success(runtime): let cmd = CommandResolver.makeRuntimeCommand(
let cmd = CommandResolver.makeRuntimeCommand( runtime: runtime,
runtime: runtime, entrypoint: entry,
entrypoint: entry, subcommand: "gateway-daemon",
subcommand: "gateway-daemon", extraArgs: ["--port", "\(port)", "--bind", bind])
extraArgs: ["--port", "\(port)", "--bind", bind]) return .success(cmd)
return .success(cmd)
case .failure:
break
}
} }
return .failure(.cliNotFound) return .failure(.message("clawdbot CLI not found in PATH; install the CLI."))
} }
static func isLoaded() async -> Bool { static func isLoaded() async -> Bool {
@@ -82,7 +77,7 @@ enum GatewayLaunchAgentManager {
static func set(enabled: Bool, bundlePath: String, port: Int) async -> String? { static func set(enabled: Bool, bundlePath: String, port: Int) async -> String? {
_ = bundlePath _ = bundlePath
if enabled, self.isLaunchAgentWriteDisabled() { if enabled, self.isLaunchAgentWriteDisabled() {
self.logger.info("launchd enable skipped (attach-only or disable marker set)") self.logger.info("launchd enable skipped (disable marker set)")
return nil return nil
} }
if enabled { if enabled {
@@ -98,14 +93,13 @@ enum GatewayLaunchAgentManager {
token: desiredToken, token: desiredToken,
password: desiredPassword) password: desiredPassword)
let programArgumentsResult = self.gatewayProgramArguments(port: port, bind: desiredBind) let programArgumentsResult = self.gatewayProgramArguments(port: port, bind: desiredBind)
let programArguments: [String] guard case let .success(programArguments) = programArgumentsResult else {
switch programArgumentsResult { if case let .failure(error) = programArgumentsResult {
case let .success(args): let message = error.localizedDescription
programArguments = args self.logger.error("launchd enable failed: \(message)")
case let .failure(error): return message
let message = error.errorDescription ?? "Failed to resolve gateway CLI" }
self.logger.error("launchd enable failed: \(message)") return "Failed to resolve gateway command."
return message
} }
// If launchd already loaded the job (common on login), avoid `bootout` unless we must // If launchd already loaded the job (common on login), avoid `bootout` unless we must
@@ -336,9 +330,6 @@ enum GatewayLaunchAgentManager {
extension GatewayLaunchAgentManager { extension GatewayLaunchAgentManager {
private static func isLaunchAgentWriteDisabled() -> Bool { private static func isLaunchAgentWriteDisabled() -> Bool {
if UserDefaults.standard.bool(forKey: attachExistingGatewayOnlyKey) {
return true
}
let marker = FileManager.default.homeDirectoryForCurrentUser let marker = FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent(self.disableLaunchAgentMarker) .appendingPathComponent(self.disableLaunchAgentMarker)
return FileManager.default.fileExists(atPath: marker.path) return FileManager.default.fileExists(atPath: marker.path)