@openclaw/acpx 2026.7.2-beta.7 → 2026.7.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- import { t as createAcpxRuntimeService } from "./register.runtime-BbS2JTTv.js";
1
+ import { t as createAcpxRuntimeService } from "./.setup/register.runtime-DjVBj71-.mjs";
2
2
  export { createAcpxRuntimeService };
package/dist/setup-api.js CHANGED
@@ -1,5 +1,5 @@
1
- import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
2
1
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
2
+ import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  //#region extensions/acpx/setup-api.ts
4
4
  /**
5
5
  * ACPX setup plugin entry. It auto-enables setup when ACP config already points
@@ -49,15 +49,9 @@
49
49
  "type": "number",
50
50
  "minimum": 0
51
51
  },
52
- "piSessionCatalog": {
53
- "type": "object",
54
- "additionalProperties": false,
55
- "properties": {
56
- "enabled": {
57
- "type": "boolean",
58
- "default": true
59
- }
60
- }
52
+ "probeAgent": {
53
+ "type": "string",
54
+ "minLength": 1
61
55
  },
62
56
  "mcpServers": {
63
57
  "type": "object",
@@ -144,10 +138,6 @@
144
138
  "help": "Reserved compatibility field for the older embedded ACPX queue-owner path. Accepted for compatibility and logged as ignored.",
145
139
  "advanced": true
146
140
  },
147
- "piSessionCatalog.enabled": {
148
- "label": "Pi Session Catalog",
149
- "help": "Auto-detect Pi sessions on the Gateway and paired nodes, then show them in the sessions sidebar."
150
- },
151
141
  "probeAgent": {
152
142
  "label": "Health Probe Agent",
153
143
  "help": "Agent id used for the embedded ACP runtime health probe. Defaults to the first `acp.allowedAgents` entry when that allowlist is set, otherwise to the runtime built-in probe agent (codex). Set this explicitly (for example `opencode` or `claude`) when the default probe agent is not installed or not authenticated, so the whole embedded ACP backend does not get marked unavailable.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/acpx",
3
- "version": "2026.7.2-beta.7",
3
+ "version": "2026.7.33",
4
4
  "description": "OpenClaw ACP runtime backend with plugin-owned session and transport management.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,10 +8,9 @@
8
8
  },
9
9
  "type": "module",
10
10
  "dependencies": {
11
- "@agentclientprotocol/claude-agent-acp": "0.62.0",
12
- "@agentclientprotocol/codex-acp": "1.1.7",
13
- "acpx": "0.13.0",
14
- "smol-toml": "1.7.1",
11
+ "@agentclientprotocol/claude-agent-acp": "0.55.0",
12
+ "@zed-industries/codex-acp": "0.16.0",
13
+ "acpx": "0.11.2",
15
14
  "zod": "4.4.3"
16
15
  },
17
16
  "devDependencies": {
@@ -24,29 +23,13 @@
24
23
  "install": {
25
24
  "npmSpec": "@openclaw/acpx",
26
25
  "defaultChoice": "npm",
27
- "minHostVersion": ">=2026.4.25",
28
- "requiredPlatformPackages": [
29
- "@anthropic-ai/claude-agent-sdk-linux-x64",
30
- "@anthropic-ai/claude-agent-sdk-linux-arm64",
31
- "@anthropic-ai/claude-agent-sdk-linux-x64-musl",
32
- "@anthropic-ai/claude-agent-sdk-linux-arm64-musl",
33
- "@anthropic-ai/claude-agent-sdk-darwin-x64",
34
- "@anthropic-ai/claude-agent-sdk-darwin-arm64",
35
- "@anthropic-ai/claude-agent-sdk-win32-x64",
36
- "@anthropic-ai/claude-agent-sdk-win32-arm64",
37
- "@openai/codex-linux-x64",
38
- "@openai/codex-linux-arm64",
39
- "@openai/codex-darwin-x64",
40
- "@openai/codex-darwin-arm64",
41
- "@openai/codex-win32-x64",
42
- "@openai/codex-win32-arm64"
43
- ]
26
+ "minHostVersion": ">=2026.4.25"
44
27
  },
45
28
  "compat": {
46
- "pluginApi": ">=2026.7.2-beta.7"
29
+ "pluginApi": ">=2026.7.33"
47
30
  },
48
31
  "build": {
49
- "openclawVersion": "2026.7.2-beta.7",
32
+ "openclawVersion": "2026.7.33",
50
33
  "staticAssets": [
51
34
  {
52
35
  "source": "./src/runtime-internals/mcp-proxy.mjs",
@@ -74,7 +57,7 @@
74
57
  "skills/**"
75
58
  ],
76
59
  "peerDependencies": {
77
- "openclaw": ">=2026.7.2-beta.7"
60
+ "openclaw": ">=2026.7.33"
78
61
  },
79
62
  "peerDependenciesMeta": {
80
63
  "openclaw": {
@@ -210,7 +210,7 @@ Defaults are:
210
210
 
211
211
  - `openclaw -> openclaw acp`
212
212
  - `claude -> bundled @agentclientprotocol/claude-agent-acp@0.55.0`
213
- - `codex -> bundled @agentclientprotocol/codex-acp@1.1.2 through OpenClaw's isolated CODEX_HOME wrapper`
213
+ - `codex -> bundled @zed-industries/codex-acp@0.16.0 through OpenClaw's isolated CODEX_HOME wrapper`
214
214
  - `copilot -> copilot --acp --stdio`
215
215
  - `cursor -> cursor-agent acp`
216
216
  - `droid -> droid exec --output-format acp`
@@ -1,39 +0,0 @@
1
- import { z } from "zod";
2
- //#region extensions/acpx/src/config-schema.ts
3
- /**
4
- * ACPX plugin configuration schema and public config types. Runtime setup uses
5
- * this file as the single source of truth for validation and defaulting.
6
- */
7
- const ACPX_PERMISSION_MODES = [
8
- "approve-all",
9
- "approve-reads",
10
- "deny-all"
11
- ];
12
- const ACPX_NON_INTERACTIVE_POLICIES = ["deny", "fail"];
13
- const nonEmptyTrimmedString = (message) => z.string({ error: message }).trim().min(1, { error: message });
14
- const McpServerConfigSchema = z.object({
15
- command: nonEmptyTrimmedString("command must be a non-empty string").describe("Command to run the MCP server"),
16
- args: z.array(z.string({ error: "args must be an array of strings" }), { error: "args must be an array of strings" }).optional().describe("Arguments to pass to the command"),
17
- env: z.record(z.string(), z.string({ error: "env values must be strings" }), { error: "env must be an object of strings" }).optional().describe("Environment variables for the MCP server")
18
- });
19
- /** Zod schema for validating raw ACPX plugin config from OpenClaw config. */
20
- const AcpxPluginConfigSchema = z.strictObject({
21
- cwd: nonEmptyTrimmedString("cwd must be a non-empty string").optional(),
22
- stateDir: nonEmptyTrimmedString("stateDir must be a non-empty string").optional(),
23
- probeAgent: nonEmptyTrimmedString("probeAgent must be a non-empty string").optional(),
24
- permissionMode: z.enum(ACPX_PERMISSION_MODES, { error: `permissionMode must be one of: ${ACPX_PERMISSION_MODES.join(", ")}` }).optional(),
25
- nonInteractivePermissions: z.enum(ACPX_NON_INTERACTIVE_POLICIES, { error: `nonInteractivePermissions must be one of: ${ACPX_NON_INTERACTIVE_POLICIES.join(", ")}` }).optional(),
26
- pluginToolsMcpBridge: z.boolean({ error: "pluginToolsMcpBridge must be a boolean" }).optional(),
27
- openClawToolsMcpBridge: z.boolean({ error: "openClawToolsMcpBridge must be a boolean" }).optional(),
28
- strictWindowsCmdWrapper: z.boolean({ error: "strictWindowsCmdWrapper must be a boolean" }).optional(),
29
- timeoutSeconds: z.number({ error: "timeoutSeconds must be a number >= 0.001" }).min(.001, { error: "timeoutSeconds must be a number >= 0.001" }).default(120),
30
- queueOwnerTtlSeconds: z.number({ error: "queueOwnerTtlSeconds must be a number >= 0" }).min(0, { error: "queueOwnerTtlSeconds must be a number >= 0" }).optional(),
31
- piSessionCatalog: z.strictObject({ enabled: z.boolean({ error: "piSessionCatalog.enabled must be a boolean" }).default(true) }).optional(),
32
- mcpServers: z.record(z.string(), McpServerConfigSchema).optional(),
33
- agents: z.record(z.string(), z.strictObject({
34
- command: nonEmptyTrimmedString("agents.<id>.command must be a non-empty string"),
35
- args: z.array(z.string({ error: "args must be an array of strings" })).optional()
36
- })).optional()
37
- });
38
- //#endregion
39
- export { AcpxPluginConfigSchema as t };
@@ -1,469 +0,0 @@
1
- import { t as AcpxPluginConfigSchema } from "./config-schema-lrk5nlcV.js";
2
- import { _ as splitCommandParts } from "./process-lease-DSLDgiNl.js";
3
- import { createRequire } from "node:module";
4
- import { formatPluginConfigIssue } from "openclaw/plugin-sdk/extension-shared";
5
- import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
6
- import fs from "node:fs";
7
- import path from "node:path";
8
- import { runExec } from "openclaw/plugin-sdk/process-runtime";
9
- import { fileURLToPath } from "node:url";
10
- //#region extensions/acpx/src/codex-adapter.ts
11
- const CODEX_ACP_PACKAGE = "@agentclientprotocol/codex-acp";
12
- const CODEX_ACP_BIN = "codex-acp";
13
- const LEGACY_CODEX_ACP_PACKAGE = "@zed-industries/codex-acp";
14
- const OPENCLAW_CODEX_CONFIG_ARG = "--openclaw-codex-config";
15
- //#endregion
16
- //#region extensions/acpx/src/config.ts
17
- /**
18
- * Resolves ACPX plugin config from raw user configuration. It locates the
19
- * plugin root, injects optional MCP bridge servers, and applies runtime defaults.
20
- */
21
- const ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME = "openclaw-plugin-tools";
22
- const ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME = "openclaw-tools";
23
- const requireFromHere$1 = createRequire(import.meta.url);
24
- function isAcpxPluginRoot(dir) {
25
- return fs.existsSync(path.join(dir, "openclaw.plugin.json")) && fs.existsSync(path.join(dir, "package.json"));
26
- }
27
- function resolveNearestAcpxPluginRoot(moduleUrl) {
28
- let cursor = path.dirname(fileURLToPath(moduleUrl));
29
- for (let i = 0; i < 3; i += 1) {
30
- if (isAcpxPluginRoot(cursor)) return cursor;
31
- const parent = path.dirname(cursor);
32
- if (parent === cursor) break;
33
- cursor = parent;
34
- }
35
- return path.resolve(path.dirname(fileURLToPath(moduleUrl)), "..");
36
- }
37
- function resolveWorkspaceAcpxPluginRoot(currentRoot) {
38
- if (path.basename(currentRoot) !== "acpx" || path.basename(path.dirname(currentRoot)) !== "extensions" || path.basename(path.dirname(path.dirname(currentRoot))) !== "dist") return null;
39
- const workspaceRoot = path.resolve(currentRoot, "..", "..", "..", "extensions", "acpx");
40
- return isAcpxPluginRoot(workspaceRoot) ? workspaceRoot : null;
41
- }
42
- function resolveRepoAcpxPluginRoot(currentRoot) {
43
- const workspaceRoot = path.join(currentRoot, "extensions", "acpx");
44
- return isAcpxPluginRoot(workspaceRoot) ? workspaceRoot : null;
45
- }
46
- function resolveAcpxPluginRootFromOpenClawLayout(moduleUrl) {
47
- let cursor = path.dirname(fileURLToPath(moduleUrl));
48
- for (let i = 0; i < 5; i += 1) {
49
- const candidates = [
50
- path.join(cursor, "extensions", "acpx"),
51
- path.join(cursor, "dist", "extensions", "acpx"),
52
- path.join(cursor, "dist-runtime", "extensions", "acpx")
53
- ];
54
- for (const candidate of candidates) if (isAcpxPluginRoot(candidate)) return candidate;
55
- const parent = path.dirname(cursor);
56
- if (parent === cursor) break;
57
- cursor = parent;
58
- }
59
- return null;
60
- }
61
- /** Resolve the ACPX plugin root across source, dist, and dist-runtime layouts. */
62
- function resolveAcpxPluginRoot(moduleUrl = import.meta.url) {
63
- const resolvedRoot = resolveNearestAcpxPluginRoot(moduleUrl);
64
- return resolveWorkspaceAcpxPluginRoot(resolvedRoot) ?? resolveRepoAcpxPluginRoot(resolvedRoot) ?? resolveAcpxPluginRootFromOpenClawLayout(moduleUrl) ?? resolvedRoot;
65
- }
66
- const DEFAULT_PERMISSION_MODE = "approve-reads";
67
- const DEFAULT_NON_INTERACTIVE_POLICY = "fail";
68
- const DEFAULT_QUEUE_OWNER_TTL_SECONDS = .1;
69
- const DEFAULT_STRICT_WINDOWS_CMD_WRAPPER = true;
70
- function parseAcpxPluginConfig(value) {
71
- if (value === void 0) return {
72
- ok: true,
73
- value: void 0
74
- };
75
- const parsed = AcpxPluginConfigSchema.safeParse(value);
76
- if (!parsed.success) return {
77
- ok: false,
78
- message: formatPluginConfigIssue(parsed.error.issues[0])
79
- };
80
- return {
81
- ok: true,
82
- value: parsed.data
83
- };
84
- }
85
- function resolveOpenClawRoot(currentRoot) {
86
- if (path.basename(currentRoot) === "acpx" && path.basename(path.dirname(currentRoot)) === "extensions") {
87
- const parent = path.dirname(path.dirname(currentRoot));
88
- if (path.basename(parent) === "dist") return path.dirname(parent);
89
- return parent;
90
- }
91
- return path.resolve(currentRoot, "..");
92
- }
93
- function resolveTsxImportSpecifier() {
94
- try {
95
- return requireFromHere$1.resolve("tsx");
96
- } catch {
97
- return "tsx";
98
- }
99
- }
100
- function shellQuoteCommandArg(arg) {
101
- if (!/[\s'"\\$|&;<>{}()*?[\]~`]/.test(arg)) return arg;
102
- return `'${arg.replace(/'/g, "'\"'\"'")}'`;
103
- }
104
- function resolvePluginToolsMcpServerConfig(moduleUrl = import.meta.url) {
105
- const openClawRoot = resolveOpenClawRoot(resolveAcpxPluginRoot(moduleUrl));
106
- const distEntry = path.join(openClawRoot, "dist", "mcp", "plugin-tools-serve.js");
107
- if (fs.existsSync(distEntry)) return {
108
- command: process.execPath,
109
- args: [distEntry]
110
- };
111
- const sourceEntry = path.join(openClawRoot, "src", "mcp", "plugin-tools-serve.ts");
112
- return {
113
- command: process.execPath,
114
- args: [
115
- "--import",
116
- resolveTsxImportSpecifier(),
117
- sourceEntry
118
- ]
119
- };
120
- }
121
- function resolveOpenClawToolsMcpServerConfig(moduleUrl = import.meta.url) {
122
- const openClawRoot = resolveOpenClawRoot(resolveAcpxPluginRoot(moduleUrl));
123
- const distEntry = path.join(openClawRoot, "dist", "mcp", "openclaw-tools-serve.js");
124
- if (fs.existsSync(distEntry)) return {
125
- command: process.execPath,
126
- args: [distEntry]
127
- };
128
- const sourceEntry = path.join(openClawRoot, "src", "mcp", "openclaw-tools-serve.ts");
129
- return {
130
- command: process.execPath,
131
- args: [
132
- "--import",
133
- resolveTsxImportSpecifier(),
134
- sourceEntry
135
- ]
136
- };
137
- }
138
- function resolveConfiguredMcpServers(params) {
139
- const resolved = { ...params.mcpServers };
140
- if (params.pluginToolsMcpBridge && resolved[ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME]) throw new Error(`mcpServers.${ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME} is reserved when pluginToolsMcpBridge=true`);
141
- if (params.openClawToolsMcpBridge && resolved[ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME]) throw new Error(`mcpServers.${ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME} is reserved when openClawToolsMcpBridge=true`);
142
- if (params.pluginToolsMcpBridge) resolved[ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME] = resolvePluginToolsMcpServerConfig(params.moduleUrl);
143
- if (params.openClawToolsMcpBridge) resolved[ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME] = resolveOpenClawToolsMcpServerConfig(params.moduleUrl);
144
- return resolved;
145
- }
146
- /** Convert OpenClaw MCP server config into ACPX runtime MCP server entries. */
147
- function toAcpMcpServers(mcpServers) {
148
- return Object.entries(mcpServers).map(([name, server]) => ({
149
- name,
150
- command: server.command,
151
- args: [...server.args ?? []],
152
- env: Object.entries(server.env ?? {}).map(([envName, value]) => ({
153
- name: envName,
154
- value
155
- }))
156
- }));
157
- }
158
- /** Validate and normalize raw ACPX plugin config for runtime startup. */
159
- function resolveAcpxPluginConfig(params) {
160
- const parsed = parseAcpxPluginConfig(params.rawConfig);
161
- if (!parsed.ok) throw new Error(parsed.message);
162
- const normalized = parsed.value ?? {};
163
- const workspaceDir = params.workspaceDir?.trim() || process.cwd();
164
- const fallbackCwd = workspaceDir;
165
- const cwd = path.resolve(normalized.cwd?.trim() || fallbackCwd);
166
- const stateDir = path.resolve(normalized.stateDir?.trim() || path.join(workspaceDir, "state"));
167
- const pluginToolsMcpBridge = normalized.pluginToolsMcpBridge === true;
168
- const openClawToolsMcpBridge = normalized.openClawToolsMcpBridge === true;
169
- const mcpServers = resolveConfiguredMcpServers({
170
- mcpServers: normalized.mcpServers,
171
- pluginToolsMcpBridge,
172
- openClawToolsMcpBridge,
173
- moduleUrl: params.moduleUrl
174
- });
175
- const agents = Object.fromEntries(Object.entries(normalized.agents ?? {}).map(([name, entry]) => {
176
- const cmd = entry.command.trim();
177
- const cmdArgs = entry.args ?? [];
178
- const fullCommand = cmdArgs.length > 0 ? `${cmd} ${cmdArgs.map(shellQuoteCommandArg).join(" ")}` : cmd;
179
- return [normalizeLowercaseStringOrEmpty(name), fullCommand];
180
- }));
181
- return {
182
- cwd,
183
- stateDir,
184
- probeAgent: normalized.probeAgent,
185
- permissionMode: normalized.permissionMode ?? DEFAULT_PERMISSION_MODE,
186
- nonInteractivePermissions: normalized.nonInteractivePermissions ?? DEFAULT_NON_INTERACTIVE_POLICY,
187
- pluginToolsMcpBridge,
188
- openClawToolsMcpBridge,
189
- strictWindowsCmdWrapper: normalized.strictWindowsCmdWrapper ?? DEFAULT_STRICT_WINDOWS_CMD_WRAPPER,
190
- timeoutSeconds: normalized.timeoutSeconds ?? 120,
191
- queueOwnerTtlSeconds: normalized.queueOwnerTtlSeconds ?? DEFAULT_QUEUE_OWNER_TTL_SECONDS,
192
- legacyCompatibilityConfig: {
193
- strictWindowsCmdWrapper: normalized.strictWindowsCmdWrapper,
194
- queueOwnerTtlSeconds: normalized.queueOwnerTtlSeconds
195
- },
196
- mcpServers,
197
- agents
198
- };
199
- }
200
- //#endregion
201
- //#region extensions/acpx/src/process-reaper.ts
202
- /**
203
- * ACPX process ownership checks and cleanup. The reaper only terminates
204
- * OpenClaw-owned wrapper trees after validating paths, packages, and lease ids.
205
- */
206
- const requireFromHere = createRequire(import.meta.url);
207
- const GENERATED_WRAPPER_BASENAMES = /* @__PURE__ */ new Set(["codex-acp-wrapper.mjs", "claude-agent-acp-wrapper.mjs"]);
208
- const OPENCLAW_PLUGIN_DEPS_MARKER = "/plugin-runtime-deps/";
209
- const ACPX_PROCESS_LIST_TIMEOUT_MS = 2e3;
210
- const OWNED_ACP_PACKAGE_NAMES = [
211
- CODEX_ACP_PACKAGE,
212
- LEGACY_CODEX_ACP_PACKAGE,
213
- "@zed-industries/codex-acp-darwin-arm64",
214
- "@zed-industries/codex-acp-darwin-x64",
215
- "@zed-industries/codex-acp-linux-arm64",
216
- "@zed-industries/codex-acp-linux-x64",
217
- "@zed-industries/codex-acp-win32-arm64",
218
- "@zed-industries/codex-acp-win32-x64",
219
- "@agentclientprotocol/claude-agent-acp",
220
- "acpx"
221
- ];
222
- const PLUGIN_DEPS_CODEX_PACKAGE_NAMES = [
223
- "@openai/codex",
224
- "@openai/codex-darwin-arm64",
225
- "@openai/codex-darwin-x64",
226
- "@openai/codex-linux-arm64",
227
- "@openai/codex-linux-x64",
228
- "@openai/codex-win32-arm64",
229
- "@openai/codex-win32-x64"
230
- ];
231
- const ACP_PACKAGE_MARKERS = [
232
- ...OWNED_ACP_PACKAGE_NAMES.map((packageName) => `/node_modules/${packageName}/`),
233
- ...PLUGIN_DEPS_CODEX_PACKAGE_NAMES.map((packageName) => `/node_modules/${packageName}/`),
234
- "/acpx/dist/"
235
- ];
236
- function normalizePathLike(value) {
237
- return value.replaceAll("\\", "/");
238
- }
239
- function resolvePackageRoot(packageName) {
240
- try {
241
- return normalizePathLike(path.dirname(requireFromHere.resolve(`${packageName}/package.json`)));
242
- } catch {
243
- return;
244
- }
245
- }
246
- function resolveOpenClawInstallRoot(pluginRoot) {
247
- if (path.basename(pluginRoot) === "acpx" && path.basename(path.dirname(pluginRoot)) === "extensions") {
248
- const parent = path.dirname(path.dirname(pluginRoot));
249
- return path.basename(parent) === "dist" ? path.dirname(parent) : parent;
250
- }
251
- return path.resolve(pluginRoot, "..");
252
- }
253
- function resolveOwnedAcpPackageRootCandidates(packageName) {
254
- const pluginRoot = resolveAcpxPluginRoot(import.meta.url);
255
- const openClawRoot = resolveOpenClawInstallRoot(pluginRoot);
256
- return [
257
- resolvePackageRoot(packageName),
258
- path.join(pluginRoot, "node_modules", packageName),
259
- path.join(openClawRoot, "node_modules", packageName)
260
- ].flatMap((root) => root ? [normalizePathLike(root)] : []);
261
- }
262
- const OWNED_ACP_PACKAGE_ROOTS = Array.from(new Set(OWNED_ACP_PACKAGE_NAMES.flatMap(resolveOwnedAcpPackageRootCandidates)));
263
- function commandBelongsToResolvedAcpPackage(command) {
264
- return OWNED_ACP_PACKAGE_ROOTS.some((root) => command.includes(`${root}/`));
265
- }
266
- function commandMentionsGeneratedWrapper(command) {
267
- return Array.from(GENERATED_WRAPPER_BASENAMES).some((basename) => command.includes(basename));
268
- }
269
- function commandWrapperBelongsToRoot(command, wrapperRoot) {
270
- if (!wrapperRoot) return true;
271
- const normalizedCommand = normalizePathLike(command);
272
- const normalizedRoot = normalizePathLike(wrapperRoot).replace(/\/+$/, "");
273
- return Array.from(GENERATED_WRAPPER_BASENAMES).some((basename) => normalizedCommand.includes(`${normalizedRoot}/${basename}`));
274
- }
275
- /** Check whether a command references an OpenClaw-generated ACPX wrapper path. */
276
- function isOpenClawLeaseAwareAcpxProcessCommand(params) {
277
- const command = params.command?.trim();
278
- if (!command) return false;
279
- const normalized = normalizePathLike(command);
280
- return commandMentionsGeneratedWrapper(normalized) && commandWrapperBelongsToRoot(normalized, params.wrapperRoot);
281
- }
282
- function commandsReferToSameRootCommand(liveCommand, storedCommand) {
283
- if (!storedCommand?.trim()) return true;
284
- return normalizePathLike(liveCommand).trim() === normalizePathLike(storedCommand).trim();
285
- }
286
- function commandOptionEquals(parts, option, expected) {
287
- if (!expected) return true;
288
- const index = parts.indexOf(option);
289
- return index >= 0 && parts[index + 1] === expected;
290
- }
291
- function liveCommandMatchesLeaseIdentity(params) {
292
- if (!params.expectedLeaseId && !params.expectedGatewayInstanceId) return true;
293
- const parts = splitCommandParts(params.command ?? "");
294
- return commandOptionEquals(parts, "--openclaw-acpx-lease-id", params.expectedLeaseId) && commandOptionEquals(parts, "--openclaw-gateway-instance-id", params.expectedGatewayInstanceId);
295
- }
296
- /** Check whether a command is owned by OpenClaw ACPX runtime packages or wrappers. */
297
- function isOpenClawOwnedAcpxProcessCommand(params) {
298
- const command = params.command?.trim();
299
- if (!command) return false;
300
- const normalized = normalizePathLike(command);
301
- if (isOpenClawLeaseAwareAcpxProcessCommand({
302
- command: normalized,
303
- wrapperRoot: params.wrapperRoot
304
- })) return true;
305
- if (commandBelongsToResolvedAcpPackage(normalized)) return true;
306
- if (!normalized.includes(OPENCLAW_PLUGIN_DEPS_MARKER)) return false;
307
- return ACP_PACKAGE_MARKERS.some((marker) => normalized.includes(marker));
308
- }
309
- function parseProcessList(stdout) {
310
- const processes = [];
311
- for (const line of stdout.split(/\r?\n/)) {
312
- const match = /^\s*(?<pid>\d+)\s+(?<ppid>\d+)\s+(?<command>.+?)\s*$/.exec(line);
313
- const pid = match?.groups?.pid;
314
- const ppid = match?.groups?.ppid;
315
- const command = match?.groups?.command;
316
- if (!pid || !ppid || !command) continue;
317
- processes.push({
318
- pid: Number.parseInt(pid, 10),
319
- ppid: Number.parseInt(ppid, 10),
320
- command
321
- });
322
- }
323
- return processes;
324
- }
325
- /** List host processes in the compact shape needed by ACPX cleanup. */
326
- async function listPlatformProcesses() {
327
- if (process.platform === "win32") return [];
328
- const { stdout } = await runExec("ps", ["-axo", "pid=,ppid=,command="], {
329
- logOutput: false,
330
- maxBuffer: 8 * 1024 * 1024,
331
- timeoutMs: ACPX_PROCESS_LIST_TIMEOUT_MS
332
- });
333
- return parseProcessList(stdout);
334
- }
335
- function collectProcessTree(processes, rootPid) {
336
- const childrenByParent = /* @__PURE__ */ new Map();
337
- for (const processInfo of processes) {
338
- const children = childrenByParent.get(processInfo.ppid) ?? [];
339
- children.push(processInfo);
340
- childrenByParent.set(processInfo.ppid, children);
341
- }
342
- const root = new Map(processes.map((processInfo) => [processInfo.pid, processInfo])).get(rootPid);
343
- const collected = [];
344
- if (root) collected.push(root);
345
- const queue = [...childrenByParent.get(rootPid) ?? []];
346
- while (queue.length > 0) {
347
- const next = queue.shift();
348
- if (!next || collected.some((processInfo) => processInfo.pid === next.pid)) continue;
349
- collected.push(next);
350
- queue.push(...childrenByParent.get(next.pid) ?? []);
351
- }
352
- return collected;
353
- }
354
- function uniquePids(processes) {
355
- return Array.from(new Set(processes.map((processInfo) => processInfo.pid).filter((pid) => Number.isInteger(pid) && pid > 0 && pid !== process.pid)));
356
- }
357
- function isProcessAlive(pid) {
358
- try {
359
- process.kill(pid, 0);
360
- return true;
361
- } catch {
362
- return false;
363
- }
364
- }
365
- async function terminatePids(pids, deps) {
366
- const killProcess = deps?.killProcess ?? ((pid, signal) => process.kill(pid, signal));
367
- const sleep = deps?.sleep ?? ((ms) => new Promise((resolve) => {
368
- setTimeout(resolve, ms);
369
- }));
370
- const terminated = [];
371
- for (const pid of pids) try {
372
- killProcess(pid, "SIGTERM");
373
- terminated.push(pid);
374
- } catch {}
375
- if (terminated.length === 0) return terminated;
376
- await sleep(750);
377
- for (const pid of terminated) if (deps?.killProcess || isProcessAlive(pid)) try {
378
- killProcess(pid, "SIGKILL");
379
- } catch {}
380
- return terminated;
381
- }
382
- /** Terminate one validated OpenClaw-owned ACPX wrapper process tree. */
383
- async function cleanupOpenClawOwnedAcpxProcessTree(params) {
384
- const rootPid = params.rootPid;
385
- if (!rootPid || rootPid <= 0 || rootPid === process.pid) return {
386
- inspectedPids: [],
387
- terminatedPids: [],
388
- skippedReason: "missing-root"
389
- };
390
- let processes;
391
- try {
392
- processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
393
- } catch {
394
- return {
395
- inspectedPids: [],
396
- terminatedPids: [],
397
- skippedReason: "process-list-unavailable"
398
- };
399
- }
400
- const listedTree = collectProcessTree(processes, rootPid);
401
- if (listedTree.length === 0) return {
402
- inspectedPids: [],
403
- terminatedPids: [],
404
- skippedReason: "unverified-root"
405
- };
406
- const rootCommand = listedTree[0]?.command ?? params.rootCommand;
407
- const liveCommandWasGeneratedWrapper = commandMentionsGeneratedWrapper(normalizePathLike(rootCommand ?? ""));
408
- const storedCommandWasGeneratedWrapper = commandMentionsGeneratedWrapper(normalizePathLike(params.rootCommand ?? ""));
409
- if (!liveCommandWasGeneratedWrapper && storedCommandWasGeneratedWrapper) return {
410
- inspectedPids: listedTree.map((processInfo) => processInfo.pid),
411
- terminatedPids: [],
412
- skippedReason: "not-openclaw-owned"
413
- };
414
- if (!liveCommandWasGeneratedWrapper && !commandsReferToSameRootCommand(rootCommand ?? "", params.rootCommand)) return {
415
- inspectedPids: listedTree.map((processInfo) => processInfo.pid),
416
- terminatedPids: [],
417
- skippedReason: "not-openclaw-owned"
418
- };
419
- if (!isOpenClawOwnedAcpxProcessCommand({
420
- command: rootCommand,
421
- wrapperRoot: params.wrapperRoot
422
- })) return {
423
- inspectedPids: listedTree.map((processInfo) => processInfo.pid),
424
- terminatedPids: [],
425
- skippedReason: "not-openclaw-owned"
426
- };
427
- if (!liveCommandMatchesLeaseIdentity({
428
- command: rootCommand,
429
- expectedLeaseId: params.expectedLeaseId,
430
- expectedGatewayInstanceId: params.expectedGatewayInstanceId
431
- })) return {
432
- inspectedPids: listedTree.map((processInfo) => processInfo.pid),
433
- terminatedPids: [],
434
- skippedReason: "not-openclaw-owned"
435
- };
436
- const pids = uniquePids(listedTree.toReversed());
437
- return {
438
- inspectedPids: uniquePids(listedTree),
439
- terminatedPids: await terminatePids(pids, params.deps)
440
- };
441
- }
442
- /** Reap orphaned OpenClaw-owned ACPX wrapper trees during runtime startup. */
443
- async function reapStaleOpenClawOwnedAcpxOrphans(params) {
444
- if (process.platform === "win32") return {
445
- inspectedPids: [],
446
- terminatedPids: [],
447
- skippedReason: "unsupported-platform"
448
- };
449
- let processes;
450
- try {
451
- processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
452
- } catch {
453
- return {
454
- inspectedPids: [],
455
- terminatedPids: [],
456
- skippedReason: "process-list-unavailable"
457
- };
458
- }
459
- const orphanTrees = processes.filter((processInfo) => processInfo.ppid === 1 && isOpenClawOwnedAcpxProcessCommand({
460
- command: processInfo.command,
461
- wrapperRoot: params.wrapperRoot
462
- })).map((orphan) => collectProcessTree(processes, orphan.pid));
463
- return {
464
- inspectedPids: uniquePids(orphanTrees.flat()),
465
- terminatedPids: await terminatePids(uniquePids(orphanTrees.flatMap((tree) => tree.toReversed())), params.deps)
466
- };
467
- }
468
- //#endregion
469
- export { resolveAcpxPluginRoot as a, CODEX_ACP_PACKAGE as c, resolveAcpxPluginConfig as i, LEGACY_CODEX_ACP_PACKAGE as l, isOpenClawLeaseAwareAcpxProcessCommand as n, toAcpMcpServers as o, reapStaleOpenClawOwnedAcpxOrphans as r, CODEX_ACP_BIN as s, cleanupOpenClawOwnedAcpxProcessTree as t, OPENCLAW_CODEX_CONFIG_ARG as u };