fix(cli): skip respawn for foreground gateway

This commit is contained in:
Peter Steinberger
2026-04-27 15:00:30 +01:00
parent d69eeeb2a8
commit 22e2e45c57
4 changed files with 59 additions and 6 deletions

View File

@@ -26,6 +26,7 @@ Docs: https://docs.openclaw.ai
- Agents/subagents: enforce `subagents.allowAgents` for explicit same-agent `sessions_spawn(agentId=...)` calls instead of auto-allowing requester self-targets. Fixes #72827. Thanks @oiGaDio.
- ACP/sessions_spawn: let explicit `sessions_spawn(runtime="acp")` bootstrap turns run while `acp.dispatch.enabled=false` still blocks automatic ACP thread dispatch. Fixes #63591. Thanks @moeedahmed.
- Gateway: skip CLI startup self-respawn for foreground gateway runs so low-memory Linux/Node 24 hosts start through the same path as direct `dist/index.js` without hanging before logs. Fixes #72720. Thanks @sign-2025.
- Google Meet: grant Meet media permissions through browser control and pin local Chrome audio defaults to `BlackHole 2ch`, so joined agents no longer show `Permission needed` or use macOS default audio devices. Thanks @DougButdorf.
- Google Meet: route local Chrome joins through OpenClaw browser control instead of raw default Chrome, so agents use the configured OpenClaw browser profile when opening Meet. Thanks @oromeis.
- Plugins/discovery: follow symlinked plugin directories in global and workspace plugin roots while keeping broken links ignored and existing package safety checks in place. Fixes #36754; carries forward #72695 and #63206. Thanks @Quackstro, @ming1523, and @xsfX20.

View File

@@ -21,12 +21,22 @@ describe("shouldSkipRespawnForArgv", () => {
it.each([
{ argv: ["node", "openclaw", "--help"] },
{ argv: ["node", "openclaw", "-V"] },
{ argv: ["node", "openclaw", "gateway"] },
{ argv: ["node", "openclaw", "gateway", "--port", "14720", "--bind", "loopback"] },
{ argv: ["node", "openclaw", "gateway", "run", "--port=14720", "--bind", "loopback"] },
{
argv: ["node", "openclaw", "--profile", "server", "gateway", "run", "--allow-unconfigured"],
},
] as const)("skips respawn for argv %j", ({ argv }) => {
expect(shouldSkipRespawnForArgv([...argv]), argv.join(" ")).toBe(true);
});
it("keeps respawn path for normal commands", () => {
expect(shouldSkipRespawnForArgv(["node", "openclaw", "status"])).toBe(false);
it.each([
{ argv: ["node", "openclaw", "status"] },
{ argv: ["node", "openclaw", "gateway", "status"] },
{ argv: ["node", "openclaw", "gateway", "call", "health"] },
] as const)("keeps respawn path for argv %j", ({ argv }) => {
expect(shouldSkipRespawnForArgv([...argv]), argv.join(" ")).toBe(false);
});
});

View File

@@ -1,5 +1,47 @@
import { resolveCliArgvInvocation } from "./argv-invocation.js";
import { getCommandPositionalsWithRootOptions } from "./argv.js";
const GATEWAY_RUN_BOOLEAN_FLAGS = [
"--allow-unconfigured",
"--claude-cli-logs",
"--cli-backend-logs",
"--compact",
"--dev",
"--force",
"--raw-stream",
"--reset",
"--tailscale-reset-on-exit",
"--verbose",
] as const;
const GATEWAY_RUN_VALUE_FLAGS = [
"--auth",
"--bind",
"--password",
"--password-file",
"--port",
"--raw-stream-path",
"--tailscale",
"--token",
"--ws-log",
] as const;
function isForegroundGatewayRunArgv(argv: string[]): boolean {
const positionals = getCommandPositionalsWithRootOptions(argv, {
commandPath: ["gateway"],
booleanFlags: GATEWAY_RUN_BOOLEAN_FLAGS,
valueFlags: GATEWAY_RUN_VALUE_FLAGS,
});
if (!positionals) {
return false;
}
return positionals.length === 0 || (positionals.length === 1 && positionals[0] === "run");
}
export function shouldSkipRespawnForArgv(argv: string[]): boolean {
return resolveCliArgvInvocation(argv).hasHelpOrVersion;
const invocation = resolveCliArgvInvocation(argv);
return (
invocation.hasHelpOrVersion ||
(invocation.primary === "gateway" && isForegroundGatewayRunArgv(argv))
);
}

View File

@@ -36,7 +36,7 @@ describe("buildCliRespawnPlan", () => {
it("adds NODE_EXTRA_CA_CERTS and warning suppression in one respawn", () => {
const plan = buildCliRespawnPlan({
argv: ["node", "openclaw", "gateway", "run"],
argv: ["node", "openclaw", "status"],
env: {},
execArgv: [],
autoNodeExtraCaCerts: "/etc/ssl/certs/ca-certificates.crt",
@@ -52,7 +52,7 @@ describe("buildCliRespawnPlan", () => {
it("does not overwrite an existing NODE_EXTRA_CA_CERTS value", () => {
const plan = buildCliRespawnPlan({
argv: ["node", "openclaw", "gateway", "run"],
argv: ["node", "openclaw", "status"],
env: { NODE_EXTRA_CA_CERTS: "/custom/ca.pem" },
execArgv: [],
autoNodeExtraCaCerts: "/etc/ssl/certs/ca-certificates.crt",
@@ -64,7 +64,7 @@ describe("buildCliRespawnPlan", () => {
it("returns null when both respawn guards are already satisfied", () => {
expect(
buildCliRespawnPlan({
argv: ["node", "openclaw", "gateway", "run"],
argv: ["node", "openclaw", "status"],
env: {
[OPENCLAW_NODE_EXTRA_CA_CERTS_READY]: "1",
[OPENCLAW_NODE_OPTIONS_READY]: "1",