mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix: keep mac gateway attached (#982) (thanks @wes-davis)
Co-authored-by: Wes <wesleydavis@me.com>
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
- Fix: guard model fallback against undefined provider/model values. (#954) — thanks @roshanasingh4.
|
||||
- Fix: refactor session store updates, add chat.inject, and harden subagent cleanup flow. (#944) — thanks @tyler6204.
|
||||
- Fix: clean up suspended CLI processes across backends. (#978) — thanks @Nachx639.
|
||||
- Fix: macOS gateway stays attached across restarts (no port flapping) and status probes pick the right auth. (#982) — thanks @wes-davis.
|
||||
- CLI: add `--json` output for `clawdbot daemon` lifecycle/install commands.
|
||||
- Memory: make `node-llama-cpp` an optional dependency (avoid Node 25 install failures) and improve local-embeddings fallback/errors.
|
||||
- Browser: add `snapshot refs=aria` (Playwright aria-ref ids) for self-resolving refs across `snapshot` → `act`.
|
||||
|
||||
@@ -178,7 +178,7 @@ enum GatewayEnvironment {
|
||||
let port = self.gatewayPort()
|
||||
if let gatewayBin {
|
||||
let bind = self.preferredGatewayBind() ?? "loopback"
|
||||
let cmd = [gatewayBin, "gateway-daemon", "--port", "\(port)", "--bind", bind]
|
||||
let cmd = [gatewayBin, "gateway", "--port", "\(port)", "--bind", bind]
|
||||
return GatewayCommandResolution(status: status, command: cmd)
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ enum GatewayEnvironment {
|
||||
case let .success(resolvedRuntime) = runtime
|
||||
{
|
||||
let bind = self.preferredGatewayBind() ?? "loopback"
|
||||
let cmd = [resolvedRuntime.path, entry, "gateway-daemon", "--port", "\(port)", "--bind", bind]
|
||||
let cmd = [resolvedRuntime.path, entry, "gateway", "--port", "\(port)", "--bind", bind]
|
||||
return GatewayCommandResolution(status: status, command: cmd)
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,14 @@ final class GatewayProcessManager {
|
||||
self.status = .stopped
|
||||
return
|
||||
}
|
||||
// Many surfaces can call `setActive(true)` in quick succession (startup, Canvas, health checks).
|
||||
// Avoid spawning multiple concurrent "start" tasks that can thrash launchd and flap the port.
|
||||
switch self.status {
|
||||
case .starting, .running, .attachedExisting:
|
||||
return
|
||||
case .stopped, .failed:
|
||||
break
|
||||
}
|
||||
self.status = .starting
|
||||
self.logger.debug("gateway start requested")
|
||||
|
||||
|
||||
@@ -351,10 +351,14 @@ actor PortGuardian {
|
||||
if port == GatewayEnvironment.gatewayPort() { return cmd.contains("ssh") }
|
||||
return false
|
||||
case .local:
|
||||
if !cmd.contains("clawdbot") { return false }
|
||||
if full.contains("gateway-daemon") { return true }
|
||||
// The gateway may listen as `clawdbot` or via its runtime (e.g. node).
|
||||
let hasGatewayArgs =
|
||||
full.contains(" gateway ") &&
|
||||
full.contains(" --port \(port)")
|
||||
if hasGatewayArgs { return true }
|
||||
// If args are unavailable, treat a clawdbot listener as expected.
|
||||
return full == cmd
|
||||
if cmd.contains("clawdbot"), full == cmd { return true }
|
||||
return false
|
||||
case .unconfigured:
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import Testing
|
||||
let url = FileManager.default.temporaryDirectory
|
||||
.appendingPathComponent("clawdbot-launchd-\(UUID().uuidString).plist")
|
||||
let plist: [String: Any] = [
|
||||
"ProgramArguments": ["clawdbot", "gateway-daemon", "--port", "18789", "--bind", "loopback"],
|
||||
"ProgramArguments": ["clawdbot", "gateway", "--port", "18789", "--bind", "loopback"],
|
||||
"EnvironmentVariables": [
|
||||
"CLAWDBOT_GATEWAY_TOKEN": " secret ",
|
||||
"CLAWDBOT_GATEWAY_PASSWORD": "pw",
|
||||
@@ -28,7 +28,7 @@ import Testing
|
||||
let url = FileManager.default.temporaryDirectory
|
||||
.appendingPathComponent("clawdbot-launchd-\(UUID().uuidString).plist")
|
||||
let plist: [String: Any] = [
|
||||
"ProgramArguments": ["clawdbot", "gateway-daemon", "--port", "18789"],
|
||||
"ProgramArguments": ["clawdbot", "gateway", "--port", "18789"],
|
||||
]
|
||||
let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)
|
||||
try data.write(to: url, options: [.atomic])
|
||||
|
||||
@@ -187,7 +187,7 @@ services:
|
||||
[
|
||||
"node",
|
||||
"dist/index.js",
|
||||
"gateway-daemon",
|
||||
"gateway",
|
||||
"--bind",
|
||||
"${CLAWDBOT_GATEWAY_BIND}",
|
||||
"--port",
|
||||
|
||||
@@ -206,6 +206,31 @@ if [[ "$NO_SIGN" -ne 1 && -f "${LAUNCHAGENT_DISABLE_MARKER}" ]]; then
|
||||
run_step "clear launchagent disable marker" /bin/rm -f "${LAUNCHAGENT_DISABLE_MARKER}"
|
||||
fi
|
||||
|
||||
# When unsigned, ensure the gateway LaunchAgent targets the repo CLI (before the app launches).
|
||||
# This reduces noisy "could not connect" errors during app startup.
|
||||
if [ "$NO_SIGN" -eq 1 ]; then
|
||||
run_step "install gateway launch agent (unsigned)" bash -lc "cd '${ROOT_DIR}' && node dist/entry.js daemon install --force --runtime node"
|
||||
run_step "restart gateway daemon (unsigned)" bash -lc "cd '${ROOT_DIR}' && node dist/entry.js daemon restart"
|
||||
if [[ "${GATEWAY_WAIT_SECONDS}" -gt 0 ]]; then
|
||||
run_step "wait for gateway (unsigned)" sleep "${GATEWAY_WAIT_SECONDS}"
|
||||
fi
|
||||
GATEWAY_PORT="$(
|
||||
node -e '
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
try {
|
||||
const raw = fs.readFileSync(path.join(process.env.HOME, ".clawdbot", "clawdbot.json"), "utf8");
|
||||
const cfg = JSON.parse(raw);
|
||||
const port = cfg && cfg.gateway && typeof cfg.gateway.port === "number" ? cfg.gateway.port : 18789;
|
||||
process.stdout.write(String(port));
|
||||
} catch {
|
||||
process.stdout.write("18789");
|
||||
}
|
||||
'
|
||||
)"
|
||||
run_step "verify gateway port ${GATEWAY_PORT} (unsigned)" bash -lc "lsof -iTCP:${GATEWAY_PORT} -sTCP:LISTEN | head -n 5 || true"
|
||||
fi
|
||||
|
||||
# 4) Launch the installed app in the foreground so the menu bar extra appears.
|
||||
# LaunchServices can inherit a huge environment from this shell (secrets, prompt vars, etc.).
|
||||
# That can cause launchd spawn failures and is undesirable for a GUI app anyway.
|
||||
@@ -226,13 +251,6 @@ else
|
||||
fail "App exited immediately. Check ${LOG_PATH} or Console.app (User Reports)."
|
||||
fi
|
||||
|
||||
# When unsigned, ensure the gateway LaunchAgent targets the repo CLI (after the app launches).
|
||||
if [ "$NO_SIGN" -eq 1 ]; then
|
||||
run_step "install gateway launch agent (unsigned)" bash -lc "cd '${ROOT_DIR}' && node dist/entry.js daemon install --force --runtime node"
|
||||
run_step "restart gateway daemon (unsigned)" bash -lc "cd '${ROOT_DIR}' && node dist/entry.js daemon restart"
|
||||
if [[ "${GATEWAY_WAIT_SECONDS}" -gt 0 ]]; then
|
||||
run_step "wait for gateway (unsigned)" sleep "${GATEWAY_WAIT_SECONDS}"
|
||||
fi
|
||||
run_step "verify gateway port 18789 (unsigned)" bash -lc "lsof -iTCP:18789 -sTCP:LISTEN | head -n 5 || true"
|
||||
run_step "show gateway launch agent args (unsigned)" bash -lc "/usr/bin/plutil -p '${HOME}/Library/LaunchAgents/com.clawdbot.gateway.plist' | head -n 40 || true"
|
||||
fi
|
||||
|
||||
@@ -53,13 +53,6 @@ export function registerGatewayCli(program: Command) {
|
||||
),
|
||||
);
|
||||
|
||||
// Back-compat: legacy launchd plists used gateway-daemon; keep hidden alias.
|
||||
addGatewayRunCommand(
|
||||
program
|
||||
.command("gateway-daemon", { hidden: true })
|
||||
.description("Run the WebSocket Gateway as a long-lived daemon"),
|
||||
);
|
||||
|
||||
gatewayCallOpts(
|
||||
gateway
|
||||
.command("call")
|
||||
|
||||
@@ -119,10 +119,11 @@ export async function statusAllCommand(
|
||||
|
||||
const localFallbackAuth = resolveProbeAuth("local");
|
||||
const remoteAuth = resolveProbeAuth("remote");
|
||||
const probeAuth = isRemoteMode && !remoteUrlMissing ? remoteAuth : localFallbackAuth;
|
||||
|
||||
const gatewayProbe = await probeGateway({
|
||||
url: connection.url,
|
||||
auth: remoteUrlMissing ? localFallbackAuth : remoteAuth,
|
||||
auth: probeAuth,
|
||||
timeoutMs: Math.min(5000, opts?.timeoutMs ?? 10_000),
|
||||
}).catch(() => null);
|
||||
const gatewayReachable = gatewayProbe?.ok === true;
|
||||
@@ -291,7 +292,7 @@ export async function statusAllCommand(
|
||||
? `unreachable (${gatewayProbe.error})`
|
||||
: "unreachable";
|
||||
const gatewayAuth = gatewayReachable
|
||||
? ` · auth ${formatGatewayAuthUsed(remoteUrlMissing ? localFallbackAuth : remoteAuth)}`
|
||||
? ` · auth ${formatGatewayAuthUsed(probeAuth)}`
|
||||
: "";
|
||||
const gatewaySelfLine =
|
||||
gatewaySelf?.host || gatewaySelf?.ip || gatewaySelf?.version || gatewaySelf?.platform
|
||||
|
||||
Reference in New Issue
Block a user