fix(update): detect nested macOS gateway ancestry (#85391)

* fix(update): detect nested macOS gateway ancestry

* fix(release): refresh shrinkwrap for CI npm

* fix(update): inherit gateway runtime pid for update guard
This commit is contained in:
Vincent Koc
2026-05-23 00:00:38 +08:00
committed by GitHub
parent faf2a6cb9e
commit adc6adccd8
20 changed files with 286 additions and 24 deletions

View File

@@ -29,7 +29,11 @@ import { asResolvedSourceConfig, asRuntimeConfig } from "../../config/materializ
import { CONFIG_PATH, resolveIncludeRoots } from "../../config/paths.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import type { PluginInstallRecord } from "../../config/types.plugins.js";
import { GATEWAY_SERVICE_KIND, GATEWAY_SERVICE_MARKER } from "../../daemon/constants.js";
import {
GATEWAY_SERVICE_KIND,
GATEWAY_SERVICE_MARKER,
GATEWAY_SERVICE_RUNTIME_PID_ENV,
} from "../../daemon/constants.js";
import { resolveGatewayInstallEntrypoint } from "../../daemon/gateway-entrypoint.js";
import { disableCurrentOpenClawUpdateLaunchdJob } from "../../daemon/launchd.js";
import { resolveGatewayRestartLogPath } from "../../daemon/restart-logs.js";
@@ -770,8 +774,40 @@ Gateway PID ${pid} is an ancestor of this process, so this updater cannot safely
Run \`${replaceCliName(formatCliCommand("openclaw update"), CLI_NAME)}\` from a shell outside the gateway service, or stop the gateway service first and then update.`;
}
function isGatewayAncestorPid(pid: unknown): pid is number {
return typeof pid === "number" && pid > 0 && getSelfAndAncestorPidsSync().has(pid);
function parsePositivePid(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
return Math.floor(value);
}
if (typeof value !== "string") {
return null;
}
const trimmed = value.trim();
if (!/^\d+$/u.test(trimmed)) {
return null;
}
const parsed = Number.parseInt(trimmed, 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
}
function isInheritedGatewayRuntimePid(
pid: number,
env: Record<string, string | undefined> = process.env,
): boolean {
if (!isRunningInsideGatewayService(env)) {
return false;
}
return parsePositivePid(env[GATEWAY_SERVICE_RUNTIME_PID_ENV]) === pid;
}
function isGatewayAncestorPid(
pid: unknown,
env: Record<string, string | undefined> = process.env,
): pid is number {
const parsed = parsePositivePid(pid);
if (parsed === null) {
return false;
}
return isInheritedGatewayRuntimePid(parsed, env) || getSelfAndAncestorPidsSync().has(parsed);
}
function gatewayAncestryBlockMessage(pid: unknown): string | undefined {
@@ -1048,6 +1084,7 @@ function stripGatewayServiceMarkerEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv
const resolvedEnv = { ...env };
delete resolvedEnv.OPENCLAW_SERVICE_MARKER;
delete resolvedEnv.OPENCLAW_SERVICE_KIND;
delete resolvedEnv[GATEWAY_SERVICE_RUNTIME_PID_ENV];
return resolvedEnv;
}