diff --git a/Sources/CopyCat/Broadcast.swift b/Sources/CopyCat/Broadcast.swift index 9118d90..b710faa 100644 --- a/Sources/CopyCat/Broadcast.swift +++ b/Sources/CopyCat/Broadcast.swift @@ -196,10 +196,11 @@ struct ShellResult: Sendable { /// Runs a process with stdin detached, capturing stdout and stderr separately /// so callers can distinguish "no output" from "failed silently". Returns /// `nil` only when the process couldn't be spawned at all (bad path, etc). -func runShell(_ exec: String, args: [String]) -> ShellResult? { +func runShell(_ exec: String, args: [String], env: [String: String]? = nil) -> ShellResult? { let p = Process() p.executableURL = URL(fileURLWithPath: exec) p.arguments = args + if let env { p.environment = env } let outPipe = Pipe() let errPipe = Pipe() p.standardOutput = outPipe diff --git a/Sources/CopyCat/TailscaleDiscovery.swift b/Sources/CopyCat/TailscaleDiscovery.swift index d14ae36..fd80003 100644 --- a/Sources/CopyCat/TailscaleDiscovery.swift +++ b/Sources/CopyCat/TailscaleDiscovery.swift @@ -24,7 +24,12 @@ enum TailscaleDiscovery { // hosts the user can pick from. static func allPeers() -> [TailscalePeer] { guard let bin = executablePath else { return [] } - guard let result = runShell(bin, args: ["status", "--json"]) else { + // TAILSCALE_BE_CLI=1: when the bundled Tailscale binary is invoked from + // a notarized app's subprocess (no TTY), it relaunches the GUI instead + // of running as CLI. The env var forces CLI mode. See + // tailscale/tailscale#16063 and #7140. + let env = ["TAILSCALE_BE_CLI": "1", "PATH": "/usr/bin:/bin"] + guard let result = runShell(bin, args: ["status", "--json"], env: env) else { Log.app.error("tailscale status: spawn failed for \(bin)") return [] } @@ -36,7 +41,9 @@ enum TailscaleDiscovery { Log.app.info("tailscale status exit=\(result.exitCode): \(stderr)") return [] } - return parseTailscalePeers(json: result.stdout) + let peers = parseTailscalePeers(json: result.stdout) + Log.app.info("tailscale status ok: \(peers.count) peers via \(bin)") + return peers } static func onlineHostnames() -> [String] {