




















@@ -1,7 +1,9 @@
1+import path from "node:path";
12import { describe, expect, it, vi } from "vitest";
23import { OPENCLAW_ACPX_LEASE_ID_ARG, OPENCLAW_GATEWAY_INSTANCE_ID_ARG } from "./process-lease.js";
34import {
45cleanupOpenClawOwnedAcpxProcessTree,
6+isOpenClawLeaseAwareAcpxProcessCommand,
57isOpenClawOwnedAcpxProcessCommand,
68reapStaleOpenClawOwnedAcpxOrphans,
79type AcpxProcessInfo,
@@ -13,6 +15,12 @@ const CODEX_WRAPPER_COMMAND_WITH_LEASE = `${CODEX_WRAPPER_COMMAND} ${OPENCLAW_AC
1315const CLAUDE_WRAPPER_COMMAND = `node ${WRAPPER_ROOT}/claude-agent-acp-wrapper.mjs`;
1416const PLUGIN_DEPS_CODEX_COMMAND =
1517"node /tmp/openclaw/plugin-runtime-deps/node_modules/@zed-industries/codex-acp/bin/codex-acp.js";
18+const LOCAL_NODE_MODULES_CODEX_COMMAND = `node ${path.resolve(
19+ "node_modules/@zed-industries/codex-acp/bin/codex-acp.js",
20+)}`;
21+const LOCAL_NODE_MODULES_CODEX_PLATFORM_COMMAND = path.resolve(
22+"node_modules/@zed-industries/codex-acp-linux-x64/bin/codex-acp",
23+);
16241725function cleanupDeps(processes: AcpxProcessInfo[]) {
1826const killed: Array<{ pid: number; signal: NodeJS.Signals }> = [];
@@ -64,13 +72,39 @@ describe("process reaper", () => {
6472).toBe(false);
6573});
667475+it("only treats generated wrappers as launch-lease aware", () => {
76+expect(
77+isOpenClawLeaseAwareAcpxProcessCommand({
78+command: CODEX_WRAPPER_COMMAND,
79+wrapperRoot: WRAPPER_ROOT,
80+}),
81+).toBe(true);
82+expect(
83+isOpenClawLeaseAwareAcpxProcessCommand({ command: LOCAL_NODE_MODULES_CODEX_COMMAND }),
84+).toBe(false);
85+expect(isOpenClawLeaseAwareAcpxProcessCommand({ command: PLUGIN_DEPS_CODEX_COMMAND })).toBe(
86+false,
87+);
88+});
89+6790it("recognizes OpenClaw plugin-runtime-deps ACP adapter children", () => {
6891expect(isOpenClawOwnedAcpxProcessCommand({ command: PLUGIN_DEPS_CODEX_COMMAND })).toBe(true);
6992expect(isOpenClawOwnedAcpxProcessCommand({ command: "npx @zed-industries/codex-acp" })).toBe(
7093false,
7194);
7295});
739697+it("recognizes plugin-local ACP adapter package paths without trusting arbitrary installs", () => {
98+expect(isOpenClawOwnedAcpxProcessCommand({ command: LOCAL_NODE_MODULES_CODEX_COMMAND })).toBe(
99+true,
100+);
101+expect(
102+isOpenClawOwnedAcpxProcessCommand({
103+command: "node /tmp/other-project/node_modules/@zed-industries/codex-acp/bin/codex-acp.js",
104+}),
105+).toBe(false);
106+});
107+74108it("kills an owned recorded process tree children first", async () => {
75109const { deps, killed } = cleanupDeps([
76110{ pid: 100, ppid: 1, command: CODEX_WRAPPER_COMMAND },
@@ -260,6 +294,28 @@ describe("process reaper", () => {
260294).toEqual([402, 401, 400, 404, 403, 405]);
261295});
262296297+it("reaps plugin-local Codex ACP adapter orphans when the generated wrapper is already gone", async () => {
298+const { deps, killed } = cleanupDeps([
299+{ pid: 500, ppid: 1, command: LOCAL_NODE_MODULES_CODEX_COMMAND },
300+{ pid: 501, ppid: 500, command: LOCAL_NODE_MODULES_CODEX_PLATFORM_COMMAND },
301+]);
302+303+const result = await reapStaleOpenClawOwnedAcpxOrphans({
304+wrapperRoot: WRAPPER_ROOT,
305+ deps,
306+});
307+308+expect(result.skippedReason).toBeUndefined();
309+expect(result.inspectedPids).toEqual([500, 501]);
310+expect(
311+collectMatching(
312+killed,
313+(entry) => entry.signal === "SIGTERM",
314+(entry) => entry.pid,
315+),
316+).toEqual([501, 500]);
317+});
318+263319it("keeps startup scans quiet when process listing is unavailable", async () => {
264320const result = await reapStaleOpenClawOwnedAcpxOrphans({
265321wrapperRoot: WRAPPER_ROOT,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。