























@@ -86,11 +86,35 @@ describe("restartGatewayProcessWithFreshPid", () => {
8686expect(spawnMock).not.toHaveBeenCalled();
8787});
888889-it("returns supervised when launchd hints are present on macOS (no kickstart)", () => {
89+it("returns supervised when OpenClaw launchd markers are present on macOS (no kickstart)", () => {
9090clearSupervisorHints();
9191expectLaunchdSupervisedWithoutKickstart({ launchJobLabel: "ai.openclaw.gateway" });
9292});
939394+it("returns supervised for a real gateway launchd job without the injected marker", () => {
95+clearSupervisorHints();
96+setPlatform("darwin");
97+process.env.LAUNCH_JOB_LABEL = "ai.openclaw.gateway";
98+99+const result = restartGatewayProcessWithFreshPid();
100+101+expect(result.mode).toBe("supervised");
102+expect(triggerOpenClawRestartMock).not.toHaveBeenCalled();
103+expect(spawnMock).not.toHaveBeenCalled();
104+});
105+106+it("returns supervised for a real gateway XPC launchd job without the injected marker", () => {
107+clearSupervisorHints();
108+setPlatform("darwin");
109+process.env.XPC_SERVICE_NAME = "ai.openclaw.gateway";
110+111+const result = restartGatewayProcessWithFreshPid();
112+113+expect(result.mode).toBe("supervised");
114+expect(triggerOpenClawRestartMock).not.toHaveBeenCalled();
115+expect(spawnMock).not.toHaveBeenCalled();
116+});
117+94118it("returns supervised on macOS when launchd label is set (no kickstart)", () => {
95119expectLaunchdSupervisedWithoutKickstart({ launchJobLabel: "ai.openclaw.gateway" });
96120});
@@ -123,12 +147,18 @@ describe("restartGatewayProcessWithFreshPid", () => {
123147expect(spawnMock).not.toHaveBeenCalled();
124148});
125149126-it("returns supervised when XPC_SERVICE_NAME is set by launchd", () => {
150+it("does not treat inherited XPC_SERVICE_NAME as launchd supervision", () => {
127151clearSupervisorHints();
128152setPlatform("darwin");
129-process.env.XPC_SERVICE_NAME = "ai.openclaw.gateway";
153+process.env.XPC_SERVICE_NAME = "ai.openclaw.mac";
154+process.env.OPENCLAW_PROFILE = "mac";
155+130156const result = restartGatewayProcessWithFreshPid();
131-expect(result.mode).toBe("supervised");
157+158+expect(result).toEqual({
159+mode: "disabled",
160+detail: "unmanaged: use in-process restart to keep custom supervisor PID tracking stable",
161+});
132162expect(triggerOpenClawRestartMock).not.toHaveBeenCalled();
133163expect(spawnMock).not.toHaveBeenCalled();
134164});
@@ -288,6 +318,29 @@ describe("respawnGatewayProcessForUpdate", () => {
288318);
289319});
290320321+it("spawns a detached update process when macOS only has inherited XPC state", () => {
322+clearSupervisorHints();
323+setPlatform("darwin");
324+process.env.XPC_SERVICE_NAME = "ai.openclaw.mac";
325+process.execArgv = [];
326+process.argv = ["/usr/local/bin/node", "/repo/dist/index.js", "gateway", "run"];
327+spawnMock.mockReturnValue({ pid: 6161, unref: vi.fn(), kill: vi.fn() });
328+329+const result = respawnGatewayProcessForUpdate();
330+331+expect(result.mode).toBe("spawned");
332+expect(result.pid).toBe(6161);
333+expect(spawnMock).toHaveBeenCalledWith(
334+process.execPath,
335+["/repo/dist/index.js", "gateway", "run"],
336+{
337+detached: true,
338+env: process.env,
339+stdio: "inherit",
340+},
341+);
342+});
343+291344it("returns failed when update detached respawn throws", () => {
292345delete process.env.OPENCLAW_NO_RESPAWN;
293346clearSupervisorHints();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。