feat(macos): prompt for CLI install
This commit is contained in:
@@ -64,19 +64,6 @@ struct GatewayCommandResolution {
|
||||
enum GatewayEnvironment {
|
||||
private static let logger = Logger(subsystem: "com.clawdbot", category: "gateway.env")
|
||||
private static let supportedBindModes: Set<String> = ["loopback", "tailnet", "lan", "auto"]
|
||||
private static let bundledGatewayLabel = "Bundled gateway"
|
||||
|
||||
static func bundledGatewayExecutable() -> String? {
|
||||
guard let res = Bundle.main.resourceURL else { return nil }
|
||||
let path = res.appendingPathComponent("Relay/clawdbot").path
|
||||
return FileManager.default.isExecutableFile(atPath: path) ? path : nil
|
||||
}
|
||||
|
||||
static func bundledNodeExecutable() -> String? {
|
||||
guard let res = Bundle.main.resourceURL else { return nil }
|
||||
let path = res.appendingPathComponent("Relay/node").path
|
||||
return FileManager.default.isExecutableFile(atPath: path) ? path : nil
|
||||
}
|
||||
|
||||
static func gatewayPort() -> Int {
|
||||
if let raw = ProcessInfo.processInfo.environment["CLAWDBOT_GATEWAY_PORT"] {
|
||||
@@ -112,32 +99,6 @@ enum GatewayEnvironment {
|
||||
}
|
||||
let expected = self.expectedGatewayVersion()
|
||||
|
||||
if let bundled = self.bundledGatewayExecutable() {
|
||||
let installed = self.readGatewayVersion(binary: bundled)
|
||||
let bundledNode = self.bundledNodeExecutable()
|
||||
let bundledNodeVersion = bundledNode.flatMap { self.readNodeVersion(binary: $0) }
|
||||
if let expected, let installed, !installed.compatible(with: expected) {
|
||||
let message = self.bundledGatewayIncompatibleMessage(
|
||||
installed: installed,
|
||||
expected: expected)
|
||||
return GatewayEnvironmentStatus(
|
||||
kind: .incompatible(found: installed.description, required: expected.description),
|
||||
nodeVersion: bundledNodeVersion,
|
||||
gatewayVersion: installed.description,
|
||||
requiredGateway: expected.description,
|
||||
message: message)
|
||||
}
|
||||
let gatewayVersionText = installed?.description ?? "unknown"
|
||||
return GatewayEnvironmentStatus(
|
||||
kind: .ok,
|
||||
nodeVersion: bundledNodeVersion,
|
||||
gatewayVersion: gatewayVersionText,
|
||||
requiredGateway: expected?.description,
|
||||
message: self.bundledGatewayStatusMessage(
|
||||
gatewayVersion: gatewayVersionText,
|
||||
nodeVersion: bundledNodeVersion))
|
||||
}
|
||||
|
||||
let projectRoot = CommandResolver.projectRoot()
|
||||
let projectEntrypoint = CommandResolver.gatewayEntrypoint(in: projectRoot)
|
||||
|
||||
@@ -208,7 +169,6 @@ enum GatewayEnvironment {
|
||||
let projectEntrypoint = CommandResolver.gatewayEntrypoint(in: projectRoot)
|
||||
let status = self.check()
|
||||
let gatewayBin = CommandResolver.clawdbotExecutable()
|
||||
let bundled = self.bundledGatewayExecutable()
|
||||
let runtime = RuntimeLocator.resolve(searchPaths: CommandResolver.preferredPaths())
|
||||
|
||||
guard case .ok = status.kind else {
|
||||
@@ -216,20 +176,17 @@ enum GatewayEnvironment {
|
||||
}
|
||||
|
||||
let port = self.gatewayPort()
|
||||
if let bundled {
|
||||
let bind = self.preferredGatewayBind() ?? "loopback"
|
||||
let cmd = [bundled, "gateway-daemon", "--port", "\(port)", "--bind", bind]
|
||||
return GatewayCommandResolution(status: status, command: cmd)
|
||||
}
|
||||
if let gatewayBin {
|
||||
let cmd = [gatewayBin, "gateway", "--port", "\(port)"]
|
||||
let bind = self.preferredGatewayBind() ?? "loopback"
|
||||
let cmd = [gatewayBin, "gateway-daemon", "--port", "\(port)", "--bind", bind]
|
||||
return GatewayCommandResolution(status: status, command: cmd)
|
||||
}
|
||||
|
||||
if let entry = projectEntrypoint,
|
||||
case let .success(resolvedRuntime) = runtime
|
||||
{
|
||||
let cmd = [resolvedRuntime.path, entry, "gateway", "--port", "\(port)"]
|
||||
let bind = self.preferredGatewayBind() ?? "loopback"
|
||||
let cmd = [resolvedRuntime.path, entry, "gateway-daemon", "--port", "\(port)", "--bind", bind]
|
||||
return GatewayCommandResolution(status: status, command: cmd)
|
||||
}
|
||||
|
||||
@@ -363,40 +320,4 @@ enum GatewayEnvironment {
|
||||
return Semver.parse(version)
|
||||
}
|
||||
|
||||
private static func readNodeVersion(binary: String) -> String? {
|
||||
let process = Process()
|
||||
process.executableURL = URL(fileURLWithPath: binary)
|
||||
process.arguments = ["--version"]
|
||||
process.environment = ["PATH": CommandResolver.preferredPaths().joined(separator: ":")]
|
||||
|
||||
let pipe = Pipe()
|
||||
process.standardOutput = pipe
|
||||
process.standardError = pipe
|
||||
do {
|
||||
try process.run()
|
||||
process.waitUntilExit()
|
||||
let data = pipe.fileHandleForReading.readToEndSafely()
|
||||
let raw = String(data: data, encoding: .utf8)?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
.replacingOccurrences(of: "^v", with: "", options: .regularExpression)
|
||||
return raw?.isEmpty == false ? raw : nil
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private static func bundledGatewayStatusMessage(
|
||||
gatewayVersion: String,
|
||||
nodeVersion: String?) -> String
|
||||
{
|
||||
"\(self.bundledGatewayLabel) \(gatewayVersion) (node \(nodeVersion ?? "unknown"))"
|
||||
}
|
||||
|
||||
private static func bundledGatewayIncompatibleMessage(
|
||||
installed: Semver,
|
||||
expected: Semver) -> String
|
||||
{
|
||||
"\(self.bundledGatewayLabel) \(installed.description) is incompatible with app " +
|
||||
"\(expected.description); rebuild the app bundle."
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user