From a4d5b68134aaf8980b6006eee31cc7fd32a896b7 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 9 Dec 2025 17:40:44 +0000 Subject: [PATCH] mac: honor local relay path --- .../Sources/Clawdis/RelayEnvironment.swift | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/apps/macos/Sources/Clawdis/RelayEnvironment.swift b/apps/macos/Sources/Clawdis/RelayEnvironment.swift index a8e9f13cc..c0232706f 100644 --- a/apps/macos/Sources/Clawdis/RelayEnvironment.swift +++ b/apps/macos/Sources/Clawdis/RelayEnvironment.swift @@ -73,6 +73,8 @@ enum RelayEnvironment { static func check() -> RelayEnvironmentStatus { let expected = self.expectedRelayVersion() + let projectRoot = CommandResolver.projectRoot() + let projectEntrypoint = CommandResolver.relayEntrypoint(in: projectRoot) switch RuntimeLocator.resolve(searchPaths: CommandResolver.preferredPaths()) { case let .failure(err): @@ -83,7 +85,9 @@ enum RelayEnvironment { requiredRelay: expected?.description, message: RuntimeLocator.describeFailure(err)) case let .success(runtime): - guard let relayBin = CommandResolver.clawdisExecutable() else { + let relayBin = CommandResolver.clawdisExecutable() + + if relayBin == nil, projectEntrypoint == nil { return RelayEnvironmentStatus( kind: .missingRelay, nodeVersion: runtime.version.description, @@ -92,7 +96,9 @@ enum RelayEnvironment { message: "clawdis CLI not found in PATH; install the global package.") } - let installedRelay = self.readRelayVersion(binary: relayBin) + let installedRelay = relayBin.flatMap { self.readRelayVersion(binary: $0) } + ?? self.readLocalRelayVersion(projectRoot: projectRoot) + if let expected, let installed = installedRelay, !installed.compatible(with: expected) { return RelayEnvironmentStatus( kind: .incompatible(found: installed.description, required: expected.description), @@ -102,24 +108,42 @@ enum RelayEnvironment { message: "Relay version \(installed.description) is incompatible with app \(expected.description); install/update the global package.") } + let relayLabel = relayBin != nil ? "global" : "local" + let relayVersionText = installedRelay?.description ?? "unknown" return RelayEnvironmentStatus( kind: .ok, nodeVersion: runtime.version.description, - relayVersion: installedRelay?.description, + relayVersion: relayVersionText, requiredRelay: expected?.description, - message: "Node \(runtime.version.description); relay \(installedRelay?.description ?? "unknown")") + message: "Node \(runtime.version.description); relay \(relayVersionText) (\(relayLabel))") } } static func resolveGatewayCommand() -> RelayCommandResolution { + let projectRoot = CommandResolver.projectRoot() + let projectEntrypoint = CommandResolver.relayEntrypoint(in: projectRoot) let status = self.check() - guard case .ok = status.kind, let relayBin = CommandResolver.clawdisExecutable() else { + let relayBin = CommandResolver.clawdisExecutable() + let runtime = RuntimeLocator.resolve(searchPaths: CommandResolver.preferredPaths()) + + guard case .ok = status.kind else { return RelayCommandResolution(status: status, command: nil) } let port = self.gatewayPort() - let cmd = [relayBin, "gateway", "--port", "\(port)"] - return RelayCommandResolution(status: status, command: cmd) + if let relayBin { + let cmd = [relayBin, "gateway", "--port", "\(port)"] + return RelayCommandResolution(status: status, command: cmd) + } + + if let entry = projectEntrypoint, + case let .success(resolvedRuntime) = runtime + { + let cmd = [resolvedRuntime.path, entry, "gateway", "--port", "\(port)"] + return RelayCommandResolution(status: status, command: cmd) + } + + return RelayCommandResolution(status: status, command: nil) } static func installGlobal(version: Semver?, statusHandler: @escaping @Sendable (String) -> Void) async { @@ -159,4 +183,14 @@ enum RelayEnvironment { return nil } } + + private static func readLocalRelayVersion(projectRoot: URL) -> Semver? { + let pkg = projectRoot.appendingPathComponent("package.json") + guard let data = try? Data(contentsOf: pkg) else { return nil } + guard + let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + let version = json["version"] as? String + else { return nil } + return Semver.parse(version) + } }