From 7aca8d2d1ca8faeb78d62654cebd241dfd8a4cfe Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 6 Dec 2025 23:45:16 +0100 Subject: [PATCH] fix(mac): harden relay spawn path and show status --- apps/macos/Sources/Clawdis/AppMain.swift | 3 +- .../Sources/Clawdis/RelayProcessManager.swift | 30 +++++++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/apps/macos/Sources/Clawdis/AppMain.swift b/apps/macos/Sources/Clawdis/AppMain.swift index 091e3f593..99e564224 100644 --- a/apps/macos/Sources/Clawdis/AppMain.swift +++ b/apps/macos/Sources/Clawdis/AppMain.swift @@ -297,7 +297,8 @@ enum PermissionManager { let trusted = AXIsProcessTrusted() results[cap] = trusted if interactive, !trusted { - _ = AXIsProcessTrustedWithOptions(nil) + let opts: NSDictionary = ["AXTrustedCheckOptionPrompt": true] + _ = AXIsProcessTrustedWithOptions(opts) } case .screenRecording: diff --git a/apps/macos/Sources/Clawdis/RelayProcessManager.swift b/apps/macos/Sources/Clawdis/RelayProcessManager.swift index c279c3a67..d97e81bf2 100644 --- a/apps/macos/Sources/Clawdis/RelayProcessManager.swift +++ b/apps/macos/Sources/Clawdis/RelayProcessManager.swift @@ -87,7 +87,7 @@ final class RelayProcessManager: ObservableObject { let result = try await run( .name(command.first ?? "clawdis"), arguments: Arguments(Array(command.dropFirst())), - environment: .inherit, + environment: self.makeEnvironment(), workingDirectory: nil ) { execution, stdin, stdout, stderr in self.didStart(execution) @@ -145,8 +145,12 @@ final class RelayProcessManager: ObservableObject { private func handleError(_ error: any Error) async { self.execution = nil - self.appendLog("[relay] failed: \(error.localizedDescription)\n") - self.logger.error("relay failed: \(error.localizedDescription, privacy: .public)") + var message = error.localizedDescription + if let sp = error as? SubprocessError { + message = "SubprocessError \(sp.code.value): \(sp)" + } + self.appendLog("[relay] failed: \(message)\n") + self.logger.error("relay failed: \(message, privacy: .public)") if self.desiredActive && !self.shouldGiveUpAfterCrashes() { self.status = .restarting self.recentCrashes.append(Date()) @@ -190,6 +194,26 @@ final class RelayProcessManager: ObservableObject { { return override.split(separator: " ").map(String.init) } + if let found = self.preferredPaths() + .lazy + .map({ ($0 as NSString).appendingPathComponent("clawdis") }) + .first(where: { FileManager.default.isExecutableFile(atPath: $0) }) + { + return [found, "relay"] + } return ["clawdis", "relay"] } + + private func makeEnvironment() -> Environment { + let merged = self.preferredPaths().joined(separator: ":") + return .inherit.updating(["PATH": merged]) + } + + private func preferredPaths() -> [String] { + let current = ProcessInfo.processInfo.environment["PATH"]? + .split(separator: ":").map(String.init) ?? [] + let extras = ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"] + var seen = Set() + return (extras + current).filter { seen.insert($0).inserted } + } }