@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,21 +1,546 @@
1
- import { n as createLazyAcpRuntimeProxy } from "./register.runtime-BbS2JTTv.js";
2
- import "./config-schema-lrk5nlcV.js";
3
- import { _ as splitCommandParts, c as openAcpxProcessLeaseStateStore, d as ACPX_GATEWAY_INSTANCE_KEY, f as ACPX_GATEWAY_INSTANCE_NAMESPACE, g as quoteCommandPart, h as normalizeAcpxGatewayInstanceRecord, i as createAcpxProcessLeaseStore, n as OPENCLAW_GATEWAY_INSTANCE_ID_ARG, t as OPENCLAW_ACPX_LEASE_ID_ARG } from "./process-lease-DSLDgiNl.js";
4
- import { registerAcpRuntimeBackend, unregisterAcpRuntimeBackend } from "./runtime-api.js";
5
- import { a as resolveAcpxPluginRoot, c as CODEX_ACP_PACKAGE, i as resolveAcpxPluginConfig, l as LEGACY_CODEX_ACP_PACKAGE, o as toAcpMcpServers, r as reapStaleOpenClawOwnedAcpxOrphans, s as CODEX_ACP_BIN, t as cleanupOpenClawOwnedAcpxProcessTree, u as OPENCLAW_CODEX_CONFIG_ARG } from "./process-reaper-DFwbcdPa.js";
1
+ import { n as createLazyAcpRuntimeProxy } from "./register.runtime-DjVBj71-.mjs";
2
+ import { a as createAcpxProcessLeaseStore, d as ACPX_GATEWAY_INSTANCE_KEY, f as ACPX_GATEWAY_INSTANCE_NAMESPACE, h as normalizeAcpxGatewayInstanceRecord, l as openAcpxProcessLeaseStateStore, n as OPENCLAW_ACPX_LEASE_ID_ENV, r as OPENCLAW_GATEWAY_INSTANCE_ID_ARG, t as OPENCLAW_ACPX_LEASE_ID_ARG } from "./process-lease-DiKkFj6F.mjs";
3
+ import { registerAcpRuntimeBackend, unregisterAcpRuntimeBackend } from "../runtime-api.js";
6
4
  import { createRequire } from "node:module";
7
- import { finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
8
5
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
9
- import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
10
- import fs from "node:fs";
11
- import fs$1 from "node:fs/promises";
6
+ import { formatPluginConfigIssue } from "openclaw/plugin-sdk/extension-shared";
7
+ import fs from "node:fs/promises";
12
8
  import path from "node:path";
13
- import os from "node:os";
14
9
  import { randomUUID } from "node:crypto";
15
- import { inspect } from "node:util";
10
+ import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
11
+ import { finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
12
+ import { execFile } from "node:child_process";
13
+ import { inspect, promisify } from "node:util";
14
+ import fs$1 from "node:fs";
15
+ import { fileURLToPath } from "node:url";
16
+ import { z } from "zod";
16
17
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
18
+ import os from "node:os";
17
19
  import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
18
- import { parse, stringify } from "smol-toml";
20
+ //#region \0rolldown/runtime.js
21
+ var __defProp = Object.defineProperty;
22
+ var __exportAll = (all, no_symbols) => {
23
+ let target = {};
24
+ for (var name in all) __defProp(target, name, {
25
+ get: all[name],
26
+ enumerable: true
27
+ });
28
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
29
+ return target;
30
+ };
31
+ //#endregion
32
+ //#region extensions/acpx/src/command-line.ts
33
+ /**
34
+ * Small shell-command helpers for ACPX-launched processes. Splitting supports
35
+ * simple quoted command strings from config without invoking a shell parser.
36
+ */
37
+ /** Quote one command argument for display or config serialization. */
38
+ function quoteCommandPart(value) {
39
+ return JSON.stringify(value);
40
+ }
41
+ /** Split a command string into argv-like parts using simple quote/backslash rules. */
42
+ function splitCommandParts(value) {
43
+ const parts = [];
44
+ let current = "";
45
+ let quote = null;
46
+ let escaping = false;
47
+ for (const ch of value) {
48
+ if (escaping) {
49
+ current += ch;
50
+ escaping = false;
51
+ continue;
52
+ }
53
+ if (ch === "\\" && quote !== "'") {
54
+ escaping = true;
55
+ continue;
56
+ }
57
+ if (quote) {
58
+ if (ch === quote) quote = null;
59
+ else current += ch;
60
+ continue;
61
+ }
62
+ if (ch === "'" || ch === "\"") {
63
+ quote = ch;
64
+ continue;
65
+ }
66
+ if (/\s/.test(ch)) {
67
+ if (current) {
68
+ parts.push(current);
69
+ current = "";
70
+ }
71
+ continue;
72
+ }
73
+ current += ch;
74
+ }
75
+ if (escaping) current += "\\";
76
+ if (current) parts.push(current);
77
+ return parts;
78
+ }
79
+ //#endregion
80
+ //#region extensions/acpx/src/config-schema.ts
81
+ /**
82
+ * ACPX plugin configuration schema and public config types. Runtime setup uses
83
+ * this file as the single source of truth for validation and defaulting.
84
+ */
85
+ const ACPX_PERMISSION_MODES = [
86
+ "approve-all",
87
+ "approve-reads",
88
+ "deny-all"
89
+ ];
90
+ const ACPX_NON_INTERACTIVE_POLICIES = ["deny", "fail"];
91
+ const nonEmptyTrimmedString = (message) => z.string({ error: message }).trim().min(1, { error: message });
92
+ const McpServerConfigSchema = z.object({
93
+ command: nonEmptyTrimmedString("command must be a non-empty string").describe("Command to run the MCP server"),
94
+ 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"),
95
+ 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")
96
+ });
97
+ /** Zod schema for validating raw ACPX plugin config from OpenClaw config. */
98
+ const AcpxPluginConfigSchema = z.strictObject({
99
+ cwd: nonEmptyTrimmedString("cwd must be a non-empty string").optional(),
100
+ stateDir: nonEmptyTrimmedString("stateDir must be a non-empty string").optional(),
101
+ probeAgent: nonEmptyTrimmedString("probeAgent must be a non-empty string").optional(),
102
+ permissionMode: z.enum(ACPX_PERMISSION_MODES, { error: `permissionMode must be one of: ${ACPX_PERMISSION_MODES.join(", ")}` }).optional(),
103
+ nonInteractivePermissions: z.enum(ACPX_NON_INTERACTIVE_POLICIES, { error: `nonInteractivePermissions must be one of: ${ACPX_NON_INTERACTIVE_POLICIES.join(", ")}` }).optional(),
104
+ pluginToolsMcpBridge: z.boolean({ error: "pluginToolsMcpBridge must be a boolean" }).optional(),
105
+ openClawToolsMcpBridge: z.boolean({ error: "openClawToolsMcpBridge must be a boolean" }).optional(),
106
+ strictWindowsCmdWrapper: z.boolean({ error: "strictWindowsCmdWrapper must be a boolean" }).optional(),
107
+ timeoutSeconds: z.number({ error: "timeoutSeconds must be a number >= 0.001" }).min(.001, { error: "timeoutSeconds must be a number >= 0.001" }).default(120),
108
+ queueOwnerTtlSeconds: z.number({ error: "queueOwnerTtlSeconds must be a number >= 0" }).min(0, { error: "queueOwnerTtlSeconds must be a number >= 0" }).optional(),
109
+ mcpServers: z.record(z.string(), McpServerConfigSchema).optional(),
110
+ agents: z.record(z.string(), z.strictObject({
111
+ command: nonEmptyTrimmedString("agents.<id>.command must be a non-empty string"),
112
+ args: z.array(z.string({ error: "args must be an array of strings" })).optional()
113
+ })).optional()
114
+ });
115
+ //#endregion
116
+ //#region extensions/acpx/src/config.ts
117
+ /**
118
+ * Resolves ACPX plugin config from raw user configuration. It locates the
119
+ * plugin root, injects optional MCP bridge servers, and applies runtime defaults.
120
+ */
121
+ const ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME = "openclaw-plugin-tools";
122
+ const ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME = "openclaw-tools";
123
+ const requireFromHere$2 = createRequire(import.meta.url);
124
+ function isAcpxPluginRoot(dir) {
125
+ return fs$1.existsSync(path.join(dir, "openclaw.plugin.json")) && fs$1.existsSync(path.join(dir, "package.json"));
126
+ }
127
+ function resolveNearestAcpxPluginRoot(moduleUrl) {
128
+ let cursor = path.dirname(fileURLToPath(moduleUrl));
129
+ for (let i = 0; i < 3; i += 1) {
130
+ if (isAcpxPluginRoot(cursor)) return cursor;
131
+ const parent = path.dirname(cursor);
132
+ if (parent === cursor) break;
133
+ cursor = parent;
134
+ }
135
+ return path.resolve(path.dirname(fileURLToPath(moduleUrl)), "..");
136
+ }
137
+ function resolveWorkspaceAcpxPluginRoot(currentRoot) {
138
+ if (path.basename(currentRoot) !== "acpx" || path.basename(path.dirname(currentRoot)) !== "extensions" || path.basename(path.dirname(path.dirname(currentRoot))) !== "dist") return null;
139
+ const workspaceRoot = path.resolve(currentRoot, "..", "..", "..", "extensions", "acpx");
140
+ return isAcpxPluginRoot(workspaceRoot) ? workspaceRoot : null;
141
+ }
142
+ function resolveRepoAcpxPluginRoot(currentRoot) {
143
+ const workspaceRoot = path.join(currentRoot, "extensions", "acpx");
144
+ return isAcpxPluginRoot(workspaceRoot) ? workspaceRoot : null;
145
+ }
146
+ function resolveAcpxPluginRootFromOpenClawLayout(moduleUrl) {
147
+ let cursor = path.dirname(fileURLToPath(moduleUrl));
148
+ for (let i = 0; i < 5; i += 1) {
149
+ const candidates = [
150
+ path.join(cursor, "extensions", "acpx"),
151
+ path.join(cursor, "dist", "extensions", "acpx"),
152
+ path.join(cursor, "dist-runtime", "extensions", "acpx")
153
+ ];
154
+ for (const candidate of candidates) if (isAcpxPluginRoot(candidate)) return candidate;
155
+ const parent = path.dirname(cursor);
156
+ if (parent === cursor) break;
157
+ cursor = parent;
158
+ }
159
+ return null;
160
+ }
161
+ /** Resolve the ACPX plugin root across source, dist, and dist-runtime layouts. */
162
+ function resolveAcpxPluginRoot(moduleUrl = import.meta.url) {
163
+ const resolvedRoot = resolveNearestAcpxPluginRoot(moduleUrl);
164
+ return resolveWorkspaceAcpxPluginRoot(resolvedRoot) ?? resolveRepoAcpxPluginRoot(resolvedRoot) ?? resolveAcpxPluginRootFromOpenClawLayout(moduleUrl) ?? resolvedRoot;
165
+ }
166
+ const DEFAULT_PERMISSION_MODE = "approve-reads";
167
+ const DEFAULT_NON_INTERACTIVE_POLICY = "fail";
168
+ const DEFAULT_QUEUE_OWNER_TTL_SECONDS = .1;
169
+ const DEFAULT_STRICT_WINDOWS_CMD_WRAPPER = true;
170
+ function parseAcpxPluginConfig(value) {
171
+ if (value === void 0) return {
172
+ ok: true,
173
+ value: void 0
174
+ };
175
+ const parsed = AcpxPluginConfigSchema.safeParse(value);
176
+ if (!parsed.success) return {
177
+ ok: false,
178
+ message: formatPluginConfigIssue(parsed.error.issues[0])
179
+ };
180
+ return {
181
+ ok: true,
182
+ value: parsed.data
183
+ };
184
+ }
185
+ function resolveOpenClawRoot(currentRoot) {
186
+ if (path.basename(currentRoot) === "acpx" && path.basename(path.dirname(currentRoot)) === "extensions") {
187
+ const parent = path.dirname(path.dirname(currentRoot));
188
+ if (path.basename(parent) === "dist") return path.dirname(parent);
189
+ return parent;
190
+ }
191
+ return path.resolve(currentRoot, "..");
192
+ }
193
+ function resolveTsxImportSpecifier() {
194
+ try {
195
+ return requireFromHere$2.resolve("tsx");
196
+ } catch {
197
+ return "tsx";
198
+ }
199
+ }
200
+ function shellQuoteCommandArg(arg) {
201
+ if (!/[\s'"\\$|&;<>{}()*?[\]~`]/.test(arg)) return arg;
202
+ return `'${arg.replace(/'/g, "'\"'\"'")}'`;
203
+ }
204
+ function resolvePluginToolsMcpServerConfig(moduleUrl = import.meta.url) {
205
+ const openClawRoot = resolveOpenClawRoot(resolveAcpxPluginRoot(moduleUrl));
206
+ const distEntry = path.join(openClawRoot, "dist", "mcp", "plugin-tools-serve.js");
207
+ if (fs$1.existsSync(distEntry)) return {
208
+ command: process.execPath,
209
+ args: [distEntry]
210
+ };
211
+ const sourceEntry = path.join(openClawRoot, "src", "mcp", "plugin-tools-serve.ts");
212
+ return {
213
+ command: process.execPath,
214
+ args: [
215
+ "--import",
216
+ resolveTsxImportSpecifier(),
217
+ sourceEntry
218
+ ]
219
+ };
220
+ }
221
+ function resolveOpenClawToolsMcpServerConfig(moduleUrl = import.meta.url) {
222
+ const openClawRoot = resolveOpenClawRoot(resolveAcpxPluginRoot(moduleUrl));
223
+ const distEntry = path.join(openClawRoot, "dist", "mcp", "openclaw-tools-serve.js");
224
+ if (fs$1.existsSync(distEntry)) return {
225
+ command: process.execPath,
226
+ args: [distEntry]
227
+ };
228
+ const sourceEntry = path.join(openClawRoot, "src", "mcp", "openclaw-tools-serve.ts");
229
+ return {
230
+ command: process.execPath,
231
+ args: [
232
+ "--import",
233
+ resolveTsxImportSpecifier(),
234
+ sourceEntry
235
+ ]
236
+ };
237
+ }
238
+ function resolveConfiguredMcpServers(params) {
239
+ const resolved = { ...params.mcpServers };
240
+ 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`);
241
+ 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`);
242
+ if (params.pluginToolsMcpBridge) resolved[ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME] = resolvePluginToolsMcpServerConfig(params.moduleUrl);
243
+ if (params.openClawToolsMcpBridge) resolved[ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME] = resolveOpenClawToolsMcpServerConfig(params.moduleUrl);
244
+ return resolved;
245
+ }
246
+ /** Convert OpenClaw MCP server config into ACPX runtime MCP server entries. */
247
+ function toAcpMcpServers(mcpServers) {
248
+ return Object.entries(mcpServers).map(([name, server]) => ({
249
+ name,
250
+ command: server.command,
251
+ args: [...server.args ?? []],
252
+ env: Object.entries(server.env ?? {}).map(([envName, value]) => ({
253
+ name: envName,
254
+ value
255
+ }))
256
+ }));
257
+ }
258
+ /** Validate and normalize raw ACPX plugin config for runtime startup. */
259
+ function resolveAcpxPluginConfig(params) {
260
+ const parsed = parseAcpxPluginConfig(params.rawConfig);
261
+ if (!parsed.ok) throw new Error(parsed.message);
262
+ const normalized = parsed.value ?? {};
263
+ const workspaceDir = params.workspaceDir?.trim() || process.cwd();
264
+ const fallbackCwd = workspaceDir;
265
+ const cwd = path.resolve(normalized.cwd?.trim() || fallbackCwd);
266
+ const stateDir = path.resolve(normalized.stateDir?.trim() || path.join(workspaceDir, "state"));
267
+ const pluginToolsMcpBridge = normalized.pluginToolsMcpBridge === true;
268
+ const openClawToolsMcpBridge = normalized.openClawToolsMcpBridge === true;
269
+ const mcpServers = resolveConfiguredMcpServers({
270
+ mcpServers: normalized.mcpServers,
271
+ pluginToolsMcpBridge,
272
+ openClawToolsMcpBridge,
273
+ moduleUrl: params.moduleUrl
274
+ });
275
+ const agents = Object.fromEntries(Object.entries(normalized.agents ?? {}).map(([name, entry]) => {
276
+ const cmd = entry.command.trim();
277
+ const cmdArgs = entry.args ?? [];
278
+ const fullCommand = cmdArgs.length > 0 ? `${cmd} ${cmdArgs.map(shellQuoteCommandArg).join(" ")}` : cmd;
279
+ return [normalizeLowercaseStringOrEmpty(name), fullCommand];
280
+ }));
281
+ return {
282
+ cwd,
283
+ stateDir,
284
+ probeAgent: normalizeLowercaseStringOrEmpty(normalized.probeAgent) || void 0,
285
+ permissionMode: normalized.permissionMode ?? DEFAULT_PERMISSION_MODE,
286
+ nonInteractivePermissions: normalized.nonInteractivePermissions ?? DEFAULT_NON_INTERACTIVE_POLICY,
287
+ pluginToolsMcpBridge,
288
+ openClawToolsMcpBridge,
289
+ strictWindowsCmdWrapper: normalized.strictWindowsCmdWrapper ?? DEFAULT_STRICT_WINDOWS_CMD_WRAPPER,
290
+ timeoutSeconds: normalized.timeoutSeconds ?? 120,
291
+ queueOwnerTtlSeconds: normalized.queueOwnerTtlSeconds ?? DEFAULT_QUEUE_OWNER_TTL_SECONDS,
292
+ legacyCompatibilityConfig: {
293
+ strictWindowsCmdWrapper: normalized.strictWindowsCmdWrapper,
294
+ queueOwnerTtlSeconds: normalized.queueOwnerTtlSeconds
295
+ },
296
+ mcpServers,
297
+ agents
298
+ };
299
+ }
300
+ //#endregion
301
+ //#region extensions/acpx/src/process-reaper.ts
302
+ /**
303
+ * ACPX process ownership checks and cleanup. The reaper only terminates
304
+ * OpenClaw-owned wrapper trees after validating paths, packages, and lease ids.
305
+ */
306
+ const execFileAsync = promisify(execFile);
307
+ const requireFromHere$1 = createRequire(import.meta.url);
308
+ const GENERATED_WRAPPER_BASENAMES = /* @__PURE__ */ new Set(["codex-acp-wrapper.mjs", "claude-agent-acp-wrapper.mjs"]);
309
+ const OPENCLAW_PLUGIN_DEPS_MARKER = "/plugin-runtime-deps/";
310
+ const OWNED_ACP_PACKAGE_NAMES = [
311
+ "@zed-industries/codex-acp",
312
+ "@zed-industries/codex-acp-darwin-arm64",
313
+ "@zed-industries/codex-acp-darwin-x64",
314
+ "@zed-industries/codex-acp-linux-arm64",
315
+ "@zed-industries/codex-acp-linux-x64",
316
+ "@zed-industries/codex-acp-win32-arm64",
317
+ "@zed-industries/codex-acp-win32-x64",
318
+ "@agentclientprotocol/claude-agent-acp",
319
+ "acpx"
320
+ ];
321
+ const ACP_PACKAGE_MARKERS = [...OWNED_ACP_PACKAGE_NAMES.map((packageName) => `/node_modules/${packageName}/`), "/acpx/dist/"];
322
+ function normalizePathLike(value) {
323
+ return value.replaceAll("\\", "/");
324
+ }
325
+ function resolvePackageRoot(packageName) {
326
+ try {
327
+ return normalizePathLike(path.dirname(requireFromHere$1.resolve(`${packageName}/package.json`)));
328
+ } catch {
329
+ return;
330
+ }
331
+ }
332
+ function resolveOpenClawInstallRoot(pluginRoot) {
333
+ if (path.basename(pluginRoot) === "acpx" && path.basename(path.dirname(pluginRoot)) === "extensions") {
334
+ const parent = path.dirname(path.dirname(pluginRoot));
335
+ return path.basename(parent) === "dist" ? path.dirname(parent) : parent;
336
+ }
337
+ return path.resolve(pluginRoot, "..");
338
+ }
339
+ function resolveOwnedAcpPackageRootCandidates(packageName) {
340
+ const pluginRoot = resolveAcpxPluginRoot(import.meta.url);
341
+ const openClawRoot = resolveOpenClawInstallRoot(pluginRoot);
342
+ return [
343
+ resolvePackageRoot(packageName),
344
+ path.join(pluginRoot, "node_modules", packageName),
345
+ path.join(openClawRoot, "node_modules", packageName)
346
+ ].flatMap((root) => root ? [normalizePathLike(root)] : []);
347
+ }
348
+ const OWNED_ACP_PACKAGE_ROOTS = Array.from(new Set(OWNED_ACP_PACKAGE_NAMES.flatMap(resolveOwnedAcpPackageRootCandidates)));
349
+ function commandBelongsToResolvedAcpPackage(command) {
350
+ return OWNED_ACP_PACKAGE_ROOTS.some((root) => command.includes(`${root}/`));
351
+ }
352
+ function commandMentionsGeneratedWrapper(command) {
353
+ return Array.from(GENERATED_WRAPPER_BASENAMES).some((basename) => command.includes(basename));
354
+ }
355
+ function commandWrapperBelongsToRoot(command, wrapperRoot) {
356
+ if (!wrapperRoot) return true;
357
+ const normalizedCommand = normalizePathLike(command);
358
+ const normalizedRoot = normalizePathLike(wrapperRoot).replace(/\/+$/, "");
359
+ return Array.from(GENERATED_WRAPPER_BASENAMES).some((basename) => normalizedCommand.includes(`${normalizedRoot}/${basename}`));
360
+ }
361
+ /** Check whether a command references an OpenClaw-generated ACPX wrapper path. */
362
+ function isOpenClawLeaseAwareAcpxProcessCommand(params) {
363
+ const command = params.command?.trim();
364
+ if (!command) return false;
365
+ const normalized = normalizePathLike(command);
366
+ return commandMentionsGeneratedWrapper(normalized) && commandWrapperBelongsToRoot(normalized, params.wrapperRoot);
367
+ }
368
+ function commandsReferToSameRootCommand(liveCommand, storedCommand) {
369
+ if (!storedCommand?.trim()) return true;
370
+ return normalizePathLike(liveCommand).trim() === normalizePathLike(storedCommand).trim();
371
+ }
372
+ function commandOptionEquals(parts, option, expected) {
373
+ if (!expected) return true;
374
+ const index = parts.indexOf(option);
375
+ return index >= 0 && parts[index + 1] === expected;
376
+ }
377
+ function liveCommandMatchesLeaseIdentity(params) {
378
+ if (!params.expectedLeaseId && !params.expectedGatewayInstanceId) return true;
379
+ const parts = splitCommandParts(params.command ?? "");
380
+ return commandOptionEquals(parts, "--openclaw-acpx-lease-id", params.expectedLeaseId) && commandOptionEquals(parts, "--openclaw-gateway-instance-id", params.expectedGatewayInstanceId);
381
+ }
382
+ /** Check whether a command is owned by OpenClaw ACPX runtime packages or wrappers. */
383
+ function isOpenClawOwnedAcpxProcessCommand(params) {
384
+ const command = params.command?.trim();
385
+ if (!command) return false;
386
+ const normalized = normalizePathLike(command);
387
+ if (isOpenClawLeaseAwareAcpxProcessCommand({
388
+ command: normalized,
389
+ wrapperRoot: params.wrapperRoot
390
+ })) return true;
391
+ if (commandBelongsToResolvedAcpPackage(normalized)) return true;
392
+ if (!normalized.includes(OPENCLAW_PLUGIN_DEPS_MARKER)) return false;
393
+ return ACP_PACKAGE_MARKERS.some((marker) => normalized.includes(marker));
394
+ }
395
+ function parseProcessList(stdout) {
396
+ const processes = [];
397
+ for (const line of stdout.split(/\r?\n/)) {
398
+ const match = /^\s*(?<pid>\d+)\s+(?<ppid>\d+)\s+(?<command>.+?)\s*$/.exec(line);
399
+ if (!match?.groups) continue;
400
+ processes.push({
401
+ pid: Number.parseInt(match.groups.pid, 10),
402
+ ppid: Number.parseInt(match.groups.ppid, 10),
403
+ command: match.groups.command
404
+ });
405
+ }
406
+ return processes;
407
+ }
408
+ /** List host processes in the compact shape needed by ACPX cleanup. */
409
+ async function listPlatformProcesses() {
410
+ if (process.platform === "win32") return [];
411
+ const { stdout } = await execFileAsync("ps", ["-axo", "pid=,ppid=,command="], { maxBuffer: 8388608 });
412
+ return parseProcessList(stdout);
413
+ }
414
+ function collectProcessTree(processes, rootPid) {
415
+ const childrenByParent = /* @__PURE__ */ new Map();
416
+ for (const processInfo of processes) {
417
+ const children = childrenByParent.get(processInfo.ppid) ?? [];
418
+ children.push(processInfo);
419
+ childrenByParent.set(processInfo.ppid, children);
420
+ }
421
+ const root = new Map(processes.map((processInfo) => [processInfo.pid, processInfo])).get(rootPid);
422
+ const collected = [];
423
+ if (root) collected.push(root);
424
+ const queue = [...childrenByParent.get(rootPid) ?? []];
425
+ while (queue.length > 0) {
426
+ const next = queue.shift();
427
+ if (!next || collected.some((processInfo) => processInfo.pid === next.pid)) continue;
428
+ collected.push(next);
429
+ queue.push(...childrenByParent.get(next.pid) ?? []);
430
+ }
431
+ return collected;
432
+ }
433
+ function uniquePids(processes) {
434
+ return Array.from(new Set(processes.map((processInfo) => processInfo.pid).filter((pid) => Number.isInteger(pid) && pid > 0 && pid !== process.pid)));
435
+ }
436
+ function isProcessAlive(pid) {
437
+ try {
438
+ process.kill(pid, 0);
439
+ return true;
440
+ } catch {
441
+ return false;
442
+ }
443
+ }
444
+ async function terminatePids(pids, deps) {
445
+ const killProcess = deps?.killProcess ?? ((pid, signal) => process.kill(pid, signal));
446
+ const sleep = deps?.sleep ?? ((ms) => new Promise((resolve) => {
447
+ setTimeout(resolve, ms);
448
+ }));
449
+ const terminated = [];
450
+ for (const pid of pids) try {
451
+ killProcess(pid, "SIGTERM");
452
+ terminated.push(pid);
453
+ } catch {}
454
+ if (terminated.length === 0) return terminated;
455
+ await sleep(750);
456
+ for (const pid of terminated) if (deps?.killProcess || isProcessAlive(pid)) try {
457
+ killProcess(pid, "SIGKILL");
458
+ } catch {}
459
+ return terminated;
460
+ }
461
+ /** Terminate one validated OpenClaw-owned ACPX wrapper process tree. */
462
+ async function cleanupOpenClawOwnedAcpxProcessTree(params) {
463
+ const rootPid = params.rootPid;
464
+ if (!rootPid || rootPid <= 0 || rootPid === process.pid) return {
465
+ inspectedPids: [],
466
+ terminatedPids: [],
467
+ skippedReason: "missing-root"
468
+ };
469
+ let processes;
470
+ try {
471
+ processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
472
+ } catch {
473
+ processes = [];
474
+ }
475
+ const listedTree = collectProcessTree(processes, rootPid);
476
+ if (listedTree.length === 0) return {
477
+ inspectedPids: [],
478
+ terminatedPids: [],
479
+ skippedReason: "unverified-root"
480
+ };
481
+ const rootCommand = listedTree[0]?.command ?? params.rootCommand;
482
+ const liveCommandWasGeneratedWrapper = commandMentionsGeneratedWrapper(normalizePathLike(rootCommand ?? ""));
483
+ const storedCommandWasGeneratedWrapper = commandMentionsGeneratedWrapper(normalizePathLike(params.rootCommand ?? ""));
484
+ if (!liveCommandWasGeneratedWrapper && storedCommandWasGeneratedWrapper) return {
485
+ inspectedPids: listedTree.map((processInfo) => processInfo.pid),
486
+ terminatedPids: [],
487
+ skippedReason: "not-openclaw-owned"
488
+ };
489
+ if (!liveCommandWasGeneratedWrapper && !commandsReferToSameRootCommand(rootCommand ?? "", params.rootCommand)) return {
490
+ inspectedPids: listedTree.map((processInfo) => processInfo.pid),
491
+ terminatedPids: [],
492
+ skippedReason: "not-openclaw-owned"
493
+ };
494
+ if (!isOpenClawOwnedAcpxProcessCommand({
495
+ command: rootCommand,
496
+ wrapperRoot: params.wrapperRoot
497
+ })) return {
498
+ inspectedPids: listedTree.map((processInfo) => processInfo.pid),
499
+ terminatedPids: [],
500
+ skippedReason: "not-openclaw-owned"
501
+ };
502
+ if (!liveCommandMatchesLeaseIdentity({
503
+ command: rootCommand,
504
+ expectedLeaseId: params.expectedLeaseId,
505
+ expectedGatewayInstanceId: params.expectedGatewayInstanceId
506
+ })) return {
507
+ inspectedPids: listedTree.map((processInfo) => processInfo.pid),
508
+ terminatedPids: [],
509
+ skippedReason: "not-openclaw-owned"
510
+ };
511
+ const pids = uniquePids(listedTree.toReversed());
512
+ return {
513
+ inspectedPids: uniquePids(listedTree),
514
+ terminatedPids: await terminatePids(pids, params.deps)
515
+ };
516
+ }
517
+ /** Reap orphaned OpenClaw-owned ACPX wrapper trees during runtime startup. */
518
+ async function reapStaleOpenClawOwnedAcpxOrphans(params) {
519
+ if (process.platform === "win32") return {
520
+ inspectedPids: [],
521
+ terminatedPids: [],
522
+ skippedReason: "unsupported-platform"
523
+ };
524
+ let processes;
525
+ try {
526
+ processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
527
+ } catch {
528
+ return {
529
+ inspectedPids: [],
530
+ terminatedPids: [],
531
+ skippedReason: "process-list-unavailable"
532
+ };
533
+ }
534
+ const orphanTrees = processes.filter((processInfo) => processInfo.ppid === 1 && isOpenClawOwnedAcpxProcessCommand({
535
+ command: processInfo.command,
536
+ wrapperRoot: params.wrapperRoot
537
+ })).map((orphan) => collectProcessTree(processes, orphan.pid));
538
+ return {
539
+ inspectedPids: uniquePids(orphanTrees.flat()),
540
+ terminatedPids: await terminatePids(uniquePids(orphanTrees.flatMap((tree) => tree.toReversed())), params.deps)
541
+ };
542
+ }
543
+ //#endregion
19
544
  //#region extensions/acpx/src/codex-trust-config.ts
20
545
  /**
21
546
  * Builds isolated Codex config for ACPX sessions. It preserves safe inherited
@@ -126,11 +651,9 @@ function extractTrustedCodexProjectPaths(configToml) {
126
651
  continue;
127
652
  }
128
653
  const assignment = /^(?<key>"(?:\\.|[^"\\])*"|'[^']*'|[A-Za-z0-9_\-/.~:]+)\s*=\s*(?<value>.+)$/.exec(line);
129
- const rawKey = assignment?.groups?.key;
130
- const rawValue = assignment?.groups?.value;
131
- if (!rawKey || rawValue === void 0) continue;
132
- const key = parseTomlString(rawKey) ?? rawKey;
133
- const value = rawValue.trim();
654
+ if (!assignment?.groups) continue;
655
+ const key = parseTomlString(assignment.groups.key) ?? assignment.groups.key;
656
+ const value = assignment.groups.value.trim();
134
657
  if (inProjectsTable && /^\{.*\}$/.test(value)) {
135
658
  if (/\btrust_level\s*=\s*["']trusted["']/.test(value) && key) trusted.add(key);
136
659
  continue;
@@ -226,13 +749,15 @@ function renderIsolatedCodexConfig(params) {
226
749
  * Prepares isolated Codex and Claude ACP wrapper commands for ACPX. The bridge
227
750
  * copies safe auth/config state into plugin-owned homes and redacts diagnostics.
228
751
  */
752
+ const CODEX_ACP_PACKAGE = "@zed-industries/codex-acp";
753
+ const CODEX_ACP_BIN = "codex-acp";
229
754
  const CLAUDE_ACP_PACKAGE = "@agentclientprotocol/claude-agent-acp";
230
755
  const CLAUDE_ACP_BIN = "claude-agent-acp";
231
756
  const RUN_CONFIGURED_COMMAND_SENTINEL = "--openclaw-run-configured";
232
757
  const requireFromHere = createRequire(import.meta.url);
233
758
  function readSelfManifest() {
234
759
  const manifestPath = path.join(resolveAcpxPluginRoot(import.meta.url), "package.json");
235
- return JSON.parse(fs.readFileSync(manifestPath, "utf8"));
760
+ return JSON.parse(fs$1.readFileSync(manifestPath, "utf8"));
236
761
  }
237
762
  function readManifestDependencyVersion(packageName) {
238
763
  const version = readSelfManifest().dependencies?.[packageName];
@@ -257,7 +782,7 @@ async function resolveInstalledAcpPackageBinPath(packageName, binName) {
257
782
  if (manifest.name !== packageName) return;
258
783
  const binPath = resolvePackageBinPath(packageJsonPath, manifest, binName);
259
784
  if (!binPath) return;
260
- await fs$1.access(binPath);
785
+ await fs.access(binPath);
261
786
  return binPath;
262
787
  } catch {
263
788
  return;
@@ -381,10 +906,9 @@ function renderDiagnosticRedactionRuleSpecs() {
381
906
  }
382
907
  function buildAdapterWrapperScript(params) {
383
908
  return `#!/usr/bin/env node
384
- import { appendFileSync, existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
909
+ import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
385
910
  import path from "node:path";
386
911
  import { spawn } from "node:child_process";
387
- import { StringDecoder } from "node:string_decoder";
388
912
  import { fileURLToPath } from "node:url";
389
913
 
390
914
  ${params.envSetup}
@@ -394,7 +918,6 @@ const stderrLogMaxChars = 256 * 1024;
394
918
  const openClawWrapperArgs = new Set([
395
919
  ${quoteCommandPart(OPENCLAW_ACPX_LEASE_ID_ARG)},
396
920
  ${quoteCommandPart(OPENCLAW_GATEWAY_INSTANCE_ID_ARG)},
397
- ${(params.openClawWrapperArgs ?? []).map(quoteCommandPart).join(",\n ")}
398
921
  ]);
399
922
 
400
923
  function readOpenClawWrapperArg(args, name) {
@@ -406,21 +929,6 @@ function readOpenClawWrapperArg(args, name) {
406
929
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
407
930
  }
408
931
 
409
- function readOpenClawWrapperArgs(args, name) {
410
- const values = [];
411
- for (let index = 0; index < args.length; index += 1) {
412
- if (args[index] !== name) {
413
- continue;
414
- }
415
- const value = args[index + 1];
416
- if (typeof value === "string" && value.trim()) {
417
- values.push(value.trim());
418
- }
419
- index += 1;
420
- }
421
- return values;
422
- }
423
-
424
932
  function safeDiagnosticFilePart(value) {
425
933
  const sanitized = String(value || "").replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 120);
426
934
  return sanitized || "pid-" + process.pid;
@@ -431,6 +939,7 @@ function resolveStderrLogPath(args) {
431
939
  return undefined;
432
940
  }
433
941
  const leaseId =
942
+ process.env[${JSON.stringify(OPENCLAW_ACPX_LEASE_ID_ENV)}] ||
434
943
  readOpenClawWrapperArg(args, ${quoteCommandPart(OPENCLAW_ACPX_LEASE_ID_ARG)}) ||
435
944
  "pid-" + process.pid;
436
945
  const fileName = stderrLogFileNamePrefix + "." + safeDiagnosticFilePart(leaseId) + ".log";
@@ -450,25 +959,7 @@ function redactDiagnosticText(text) {
450
959
  return redacted;
451
960
  }
452
961
 
453
- function tailUtf16Safe(text, maxChars) {
454
- let start = Math.max(0, text.length - maxChars);
455
- const startsInsideSurrogatePair =
456
- start > 0 &&
457
- start < text.length &&
458
- text.charCodeAt(start) >= 0xdc00 &&
459
- text.charCodeAt(start) <= 0xdfff &&
460
- text.charCodeAt(start - 1) >= 0xd800 &&
461
- text.charCodeAt(start - 1) <= 0xdbff;
462
- if (startsInsideSurrogatePair) {
463
- start += 1;
464
- }
465
- return text.slice(start);
466
- }
467
-
468
962
  let pendingStderrLogText = "";
469
- // Pipe chunks can split a UTF-8 sequence. Preserve decoder state so diagnostic
470
- // capture does not manufacture replacement characters between chunks.
471
- const stderrDecoder = new StringDecoder("utf8");
472
963
  const stderrPrivateKeyEndPattern = /-----END [A-Z ]*PRIVATE KEY-----/;
473
964
 
474
965
  function hasUnclosedPrivateKeyBlock(text) {
@@ -493,7 +984,7 @@ function writeRedactedStderrLog(text) {
493
984
  appendFileSync(stderrLogPath, redactDiagnosticText(text), "utf8");
494
985
  const current = readFileSync(stderrLogPath, "utf8");
495
986
  if (current.length > stderrLogMaxChars) {
496
- writeFileSync(stderrLogPath, tailUtf16Safe(current, stderrLogMaxChars), "utf8");
987
+ writeFileSync(stderrLogPath, current.slice(-stderrLogMaxChars), "utf8");
497
988
  }
498
989
  } catch {
499
990
  // Stderr capture is diagnostic-only; never break the ACP adapter.
@@ -512,7 +1003,7 @@ function flushFinalizedStderrLogText() {
512
1003
  const lastLineBreak = pendingStderrLogText.lastIndexOf("\\n");
513
1004
  if (lastLineBreak === -1) {
514
1005
  if (pendingStderrLogText.length > stderrLogMaxChars) {
515
- pendingStderrLogText = tailUtf16Safe(pendingStderrLogText, stderrLogMaxChars);
1006
+ pendingStderrLogText = pendingStderrLogText.slice(-stderrLogMaxChars);
516
1007
  }
517
1008
  return;
518
1009
  }
@@ -525,7 +1016,7 @@ function flushFinalizedStderrLogText() {
525
1016
  }
526
1017
  if (flushEnd <= 0) {
527
1018
  if (pendingStderrLogText.length > stderrLogMaxChars) {
528
- pendingStderrLogText = tailUtf16Safe(pendingStderrLogText, stderrLogMaxChars);
1019
+ pendingStderrLogText = pendingStderrLogText.slice(-stderrLogMaxChars);
529
1020
  }
530
1021
  return;
531
1022
  }
@@ -535,7 +1026,7 @@ function flushFinalizedStderrLogText() {
535
1026
  }
536
1027
 
537
1028
  function appendStderrLog(chunk) {
538
- const text = stderrDecoder.write(chunk);
1029
+ const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
539
1030
  if (!text) {
540
1031
  return;
541
1032
  }
@@ -544,7 +1035,6 @@ function appendStderrLog(chunk) {
544
1035
  }
545
1036
 
546
1037
  function finishStderrLog() {
547
- pendingStderrLogText += stderrDecoder.end();
548
1038
  const text = redactIncompletePrivateKeyTail(pendingStderrLogText);
549
1039
  pendingStderrLogText = "";
550
1040
  writeRedactedStderrLog(text);
@@ -564,14 +1054,14 @@ function stripOpenClawWrapperArgs(args) {
564
1054
  }
565
1055
 
566
1056
  const rawConfiguredArgs = process.argv.slice(2);
567
- ${params.envConfigSetup ?? ""}
568
1057
  const stderrLogPath = resolveStderrLogPath(rawConfiguredArgs);
569
- if (stderrLogPath) {
570
- try {
571
- rmSync(stderrLogPath, { force: true });
572
- } catch {
573
- // Diagnostic cleanup must never prevent the adapter from starting.
1058
+
1059
+ try {
1060
+ if (stderrLogPath) {
1061
+ writeFileSync(stderrLogPath, "", "utf8");
574
1062
  }
1063
+ } catch {
1064
+ // Stderr capture is diagnostic-only; never break the ACP adapter.
575
1065
  }
576
1066
 
577
1067
  const configuredArgs = stripOpenClawWrapperArgs(rawConfiguredArgs);
@@ -720,7 +1210,6 @@ function buildCodexAcpWrapperScript(installedBinPath) {
720
1210
  binName: CODEX_ACP_BIN,
721
1211
  installedBinPath,
722
1212
  stderrLogFileNamePrefix: "codex-acp-wrapper.stderr",
723
- openClawWrapperArgs: [OPENCLAW_CODEX_CONFIG_ARG],
724
1213
  envSetup: `const codexHome = fileURLToPath(new URL("./codex-home/", import.meta.url));
725
1214
  const codexAuthPath = fileURLToPath(new URL("./codex-home/auth.json", import.meta.url));
726
1215
  const codexApiKey = (process.env.CODEX_API_KEY || process.env.OPENAI_API_KEY || "").trim();
@@ -754,59 +1243,7 @@ if (shouldWriteCodexApiKeyAuth) {
754
1243
  const env = {
755
1244
  ...process.env,
756
1245
  CODEX_HOME: codexHome,
757
- };`,
758
- envConfigSetup: `function isCodexConfigObject(value) {
759
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
760
- }
761
-
762
- function mergeCodexConfig(base, override) {
763
- const merged = Object.assign(Object.create(null), base);
764
- for (const [key, value] of Object.entries(override)) {
765
- const existing = merged[key];
766
- merged[key] =
767
- isCodexConfigObject(existing) && isCodexConfigObject(value)
768
- ? mergeCodexConfig(existing, value)
769
- : value;
770
- }
771
- return merged;
772
- }
773
-
774
- const openClawCodexConfigs = readOpenClawWrapperArgs(
775
- rawConfiguredArgs,
776
- ${quoteCommandPart(OPENCLAW_CODEX_CONFIG_ARG)},
777
- );
778
- if (openClawCodexConfigs.length > 0) {
779
- let existingCodexConfig = {};
780
- if (typeof env.CODEX_CONFIG === "string" && env.CODEX_CONFIG.trim()) {
781
- try {
782
- const parsedCodexConfig = JSON.parse(env.CODEX_CONFIG);
783
- if (!parsedCodexConfig || typeof parsedCodexConfig !== "object" || Array.isArray(parsedCodexConfig)) {
784
- throw new Error("CODEX_CONFIG must be a JSON object");
785
- }
786
- existingCodexConfig = parsedCodexConfig;
787
- } catch {
788
- console.error("[openclaw] CODEX_CONFIG must be a valid JSON object");
789
- process.exit(1);
790
- }
791
- }
792
- for (const openClawCodexConfig of openClawCodexConfigs) {
793
- try {
794
- const parsedOpenClawCodexConfig = JSON.parse(openClawCodexConfig);
795
- if (
796
- !parsedOpenClawCodexConfig ||
797
- typeof parsedOpenClawCodexConfig !== "object" ||
798
- Array.isArray(parsedOpenClawCodexConfig)
799
- ) {
800
- throw new Error("invalid OpenClaw Codex config");
801
- }
802
- existingCodexConfig = mergeCodexConfig(existingCodexConfig, parsedOpenClawCodexConfig);
803
- } catch {
804
- console.error("[openclaw] invalid generated Codex ACP startup config");
805
- process.exit(1);
806
- }
807
- }
808
- env.CODEX_CONFIG = JSON.stringify(existingCodexConfig);
809
- }`
1246
+ };`
810
1247
  });
811
1248
  }
812
1249
  function buildClaudeAcpWrapperScript(installedBinPath) {
@@ -822,7 +1259,7 @@ function buildClaudeAcpWrapperScript(installedBinPath) {
822
1259
  }
823
1260
  async function readSourceCodexConfig(codexHome) {
824
1261
  try {
825
- return await fs$1.readFile(path.join(codexHome, "config.toml"), "utf8");
1262
+ return await fs.readFile(path.join(codexHome, "config.toml"), "utf8");
826
1263
  } catch (error) {
827
1264
  if (error.code === "ENOENT") return;
828
1265
  throw error;
@@ -832,8 +1269,8 @@ async function prepareIsolatedCodexHome(params) {
832
1269
  const sourceConfig = await readSourceCodexConfig(process.env.CODEX_HOME || path.join(os.homedir(), ".codex"));
833
1270
  const trustedProjectPaths = [...sourceConfig ? extractTrustedCodexProjectPaths(sourceConfig) : [], params.workspaceDir];
834
1271
  const codexHome = path.join(params.baseDir, "codex-home");
835
- await fs$1.mkdir(codexHome, { recursive: true });
836
- await fs$1.writeFile(path.join(codexHome, "config.toml"), renderIsolatedCodexConfig({
1272
+ await fs.mkdir(codexHome, { recursive: true });
1273
+ await fs.writeFile(path.join(codexHome, "config.toml"), renderIsolatedCodexConfig({
837
1274
  sourceConfigToml: sourceConfig,
838
1275
  projectPaths: trustedProjectPaths
839
1276
  }), "utf8");
@@ -841,20 +1278,20 @@ async function prepareIsolatedCodexHome(params) {
841
1278
  }
842
1279
  async function makeGeneratedWrapperExecutableIfPossible(wrapperPath) {
843
1280
  try {
844
- await fs$1.chmod(wrapperPath, 493);
1281
+ await fs.chmod(wrapperPath, 493);
845
1282
  } catch {}
846
1283
  }
847
1284
  async function writeCodexAcpWrapper(baseDir, installedBinPath) {
848
- await fs$1.mkdir(baseDir, { recursive: true });
1285
+ await fs.mkdir(baseDir, { recursive: true });
849
1286
  const wrapperPath = path.join(baseDir, "codex-acp-wrapper.mjs");
850
- await fs$1.writeFile(wrapperPath, buildCodexAcpWrapperScript(installedBinPath), { encoding: "utf8" });
1287
+ await fs.writeFile(wrapperPath, buildCodexAcpWrapperScript(installedBinPath), { encoding: "utf8" });
851
1288
  await makeGeneratedWrapperExecutableIfPossible(wrapperPath);
852
1289
  return wrapperPath;
853
1290
  }
854
1291
  async function writeClaudeAcpWrapper(baseDir, installedBinPath) {
855
- await fs$1.mkdir(baseDir, { recursive: true });
1292
+ await fs.mkdir(baseDir, { recursive: true });
856
1293
  const wrapperPath = path.join(baseDir, "claude-agent-acp-wrapper.mjs");
857
- await fs$1.writeFile(wrapperPath, buildClaudeAcpWrapperScript(installedBinPath), { encoding: "utf8" });
1294
+ await fs.writeFile(wrapperPath, buildClaudeAcpWrapperScript(installedBinPath), { encoding: "utf8" });
858
1295
  await makeGeneratedWrapperExecutableIfPossible(wrapperPath);
859
1296
  return wrapperPath;
860
1297
  }
@@ -893,94 +1330,15 @@ function extractConfiguredAdapterArgs(params) {
893
1330
  if (isAcpBinName(parts[0] ?? "", params.binName)) return parts.slice(1);
894
1331
  if (basename(parts[0] ?? "") === "node" && isAcpBinName(parts[1] ?? "", params.binName)) return parts.slice(2);
895
1332
  }
896
- function isConfigRecord(value) {
897
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
898
- }
899
- function mergeConfigRecords(base, override) {
900
- const merged = { ...base };
901
- for (const [key, value] of Object.entries(override)) {
902
- const existing = merged[key];
903
- const nextValue = isConfigRecord(existing) && isConfigRecord(value) ? mergeConfigRecords(existing, value) : value;
904
- Object.defineProperty(merged, key, {
905
- value: nextValue,
906
- configurable: true,
907
- enumerable: true,
908
- writable: true
909
- });
910
- }
911
- return merged;
912
- }
913
- function parseLegacyCodexConfigAssignment(assignment) {
914
- const separator = assignment.indexOf("=");
915
- if (separator <= 0) throw new Error(`Invalid legacy Codex ACP config override: ${assignment}`);
916
- const rawKey = assignment.slice(0, separator).trim();
917
- const key = rawKey === "use_legacy_landlock" ? "features.use_legacy_landlock" : rawKey;
918
- const rawValue = assignment.slice(separator + 1).trim();
919
- try {
920
- return parse(`${key} = ${rawValue}`);
921
- } catch {
922
- const literal = rawValue.replace(/^["']+|["']+$/g, "");
923
- return parse(`${key} = ${JSON.stringify(literal)}`);
924
- }
925
- }
926
- function migrateLegacyCodexArgs(args) {
927
- let config = {};
928
- const forwardedArgs = [];
929
- let hadOverrides = false;
930
- for (let index = 0; index < args.length; index += 1) {
931
- const arg = args[index] ?? "";
932
- let assignment;
933
- if (arg === "-c" || arg === "--config") assignment = args[index += 1];
934
- else if (arg.startsWith("--config=")) assignment = arg.slice(9);
935
- else if (arg.startsWith("-c=")) assignment = arg.slice(3);
936
- else if (arg.startsWith("-c") && arg.length > 2) assignment = arg.slice(2);
937
- else {
938
- forwardedArgs.push(arg);
939
- continue;
940
- }
941
- if (!assignment) throw new Error(`Missing value for legacy Codex ACP option ${arg}`);
942
- hadOverrides = true;
943
- config = mergeConfigRecords(config, parseLegacyCodexConfigAssignment(assignment));
944
- }
945
- return {
946
- config,
947
- forwardedArgs,
948
- hadOverrides
949
- };
950
- }
951
- function resolveCodexAdapterLaunch(configuredCommand) {
952
- const legacyAdapterArgs = extractConfiguredAdapterArgs({
953
- configuredCommand,
954
- packageName: LEGACY_CODEX_ACP_PACKAGE,
955
- binName: CODEX_ACP_BIN
956
- });
957
- if (legacyAdapterArgs) {
958
- const migration = migrateLegacyCodexArgs(legacyAdapterArgs);
959
- return {
960
- args: [...migration.hadOverrides ? [OPENCLAW_CODEX_CONFIG_ARG, JSON.stringify(migration.config)] : [], ...migration.forwardedArgs],
961
- ...migration.hadOverrides ? { migratedConfig: migration.config } : {}
962
- };
963
- }
964
- const maintainedAdapterArgs = extractConfiguredAdapterArgs({
1333
+ function buildCodexAcpWrapperCommand(wrapperPath, configuredCommand) {
1334
+ const configuredAdapterArgs = extractConfiguredAdapterArgs({
965
1335
  configuredCommand,
966
1336
  packageName: CODEX_ACP_PACKAGE,
967
1337
  binName: CODEX_ACP_BIN
968
1338
  });
969
- if (!maintainedAdapterArgs) return;
970
- return { args: maintainedAdapterArgs };
971
- }
972
- function buildCodexAcpWrapperCommand(wrapperPath, configuredCommand) {
973
- const launch = resolveCodexAdapterLaunch(configuredCommand);
974
- if (launch) return buildWrapperCommand(wrapperPath, launch.args);
1339
+ if (configuredAdapterArgs) return buildWrapperCommand(wrapperPath, configuredAdapterArgs);
975
1340
  return buildWrapperCommand(wrapperPath, [RUN_CONFIGURED_COMMAND_SENTINEL, ...splitCommandParts(configuredCommand?.trim() ?? "")]);
976
1341
  }
977
- async function persistMigratedCodexMcpConfig(params) {
978
- const mcpServers = params.migratedConfig?.mcp_servers;
979
- if (!isConfigRecord(mcpServers)) return;
980
- const configPath = path.join(params.codexHome, "config.toml");
981
- const merged = mergeConfigRecords(parse(await fs$1.readFile(configPath, "utf8")), { mcp_servers: mcpServers });
982
- await fs$1.writeFile(configPath, stringify(merged), "utf8");
983
- }
984
1342
  function buildClaudeAcpWrapperCommand(wrapperPath, configuredCommand) {
985
1343
  const configuredAdapterArgs = extractConfiguredAdapterArgs({
986
1344
  configuredCommand,
@@ -994,20 +1352,16 @@ function buildClaudeAcpWrapperCommand(wrapperPath, configuredCommand) {
994
1352
  async function prepareAcpxCodexAuthConfig(params) {
995
1353
  params.logger;
996
1354
  const codexBaseDir = path.join(params.stateDir, "acpx");
997
- const configuredCodexCommand = params.pluginConfig.agents.codex;
998
- const configuredClaudeCommand = params.pluginConfig.agents.claude;
999
- const codexLaunch = resolveCodexAdapterLaunch(configuredCodexCommand);
1000
- await persistMigratedCodexMcpConfig({
1001
- codexHome: await prepareIsolatedCodexHome({
1002
- baseDir: codexBaseDir,
1003
- workspaceDir: params.pluginConfig.cwd
1004
- }),
1005
- migratedConfig: codexLaunch?.migratedConfig
1355
+ await prepareIsolatedCodexHome({
1356
+ baseDir: codexBaseDir,
1357
+ workspaceDir: params.pluginConfig.cwd
1006
1358
  });
1007
1359
  const installedCodexBinPath = await (params.resolveInstalledCodexAcpBinPath ?? resolveInstalledCodexAcpBinPath)();
1008
1360
  const installedClaudeBinPath = await (params.resolveInstalledClaudeAcpBinPath ?? resolveInstalledClaudeAcpBinPath)();
1009
1361
  const wrapperPath = await writeCodexAcpWrapper(codexBaseDir, installedCodexBinPath);
1010
1362
  const claudeWrapperPath = await writeClaudeAcpWrapper(codexBaseDir, installedClaudeBinPath);
1363
+ const configuredCodexCommand = params.pluginConfig.agents.codex;
1364
+ const configuredClaudeCommand = params.pluginConfig.agents.claude;
1011
1365
  return {
1012
1366
  ...params.pluginConfig,
1013
1367
  agents: {
@@ -1023,10 +1377,14 @@ async function prepareAcpxCodexAuthConfig(params) {
1023
1377
  * ACPX plugin service lifecycle. It resolves config, prepares isolated adapter
1024
1378
  * wrappers, registers the ACP backend, and manages startup/cleanup probes.
1025
1379
  */
1380
+ var service_exports = /* @__PURE__ */ __exportAll({
1381
+ createAcpxRuntimeService: () => createAcpxRuntimeService,
1382
+ resolveAcpxTimerTimeoutMs: () => resolveAcpxTimerTimeoutMs
1383
+ });
1026
1384
  const ENABLE_STARTUP_PROBE_ENV = "OPENCLAW_ACPX_RUNTIME_STARTUP_PROBE";
1027
1385
  const SKIP_RUNTIME_PROBE_ENV = "OPENCLAW_SKIP_ACPX_RUNTIME_PROBE";
1028
1386
  const ACPX_BACKEND_ID = "acpx";
1029
- const loadRuntimeModule = createLazyRuntimeModule(() => import("./runtime-By_lR8uk.js"));
1387
+ const loadRuntimeModule = createLazyRuntimeModule(() => import("./runtime-FxGnBh3l.mjs"));
1030
1388
  /** Convert ACPX timeout seconds into timer-safe milliseconds. */
1031
1389
  function resolveAcpxTimerTimeoutMs(timeoutSeconds) {
1032
1390
  if (timeoutSeconds === void 0) return;
@@ -1047,7 +1405,6 @@ function createLazyDefaultRuntime(params) {
1047
1405
  agentRegistry: module.createAgentRegistry({ overrides: params.pluginConfig.agents }),
1048
1406
  probeAgent: params.pluginConfig.probeAgent,
1049
1407
  mcpServers: toAcpMcpServers(params.pluginConfig.mcpServers),
1050
- pluginToolsMcpBridgeEnabled: params.pluginConfig.pluginToolsMcpBridge,
1051
1408
  openclawToolsMcpBridgeEnabled: params.pluginConfig.openClawToolsMcpBridge,
1052
1409
  permissionMode: params.pluginConfig.permissionMode,
1053
1410
  nonInteractivePermissions: params.pluginConfig.nonInteractivePermissions,
@@ -1099,9 +1456,13 @@ function formatDoctorFailureMessage(report) {
1099
1456
  const detailText = report.details?.map(formatDoctorDetail).filter(Boolean).join("; ").trim();
1100
1457
  return detailText ? `${report.message} (${detailText})` : report.message;
1101
1458
  }
1459
+ function normalizeProbeAgent(value) {
1460
+ const normalized = value?.trim().toLowerCase();
1461
+ return normalized ? normalized : void 0;
1462
+ }
1102
1463
  function resolveAllowedAgentsProbeAgent(ctx) {
1103
1464
  for (const agent of ctx.config.acp?.allowedAgents ?? []) {
1104
- const normalized = normalizeLowercaseStringOrEmpty(agent);
1465
+ const normalized = normalizeProbeAgent(agent);
1105
1466
  if (normalized) return normalized;
1106
1467
  }
1107
1468
  }
@@ -1179,7 +1540,7 @@ async function reapOpenAcpxProcessLeases(params) {
1179
1540
  });
1180
1541
  inspectedPids.push(...result.inspectedPids);
1181
1542
  terminatedPids.push(...result.terminatedPids);
1182
- await params.leaseStore.markState(lease.leaseId, result.skippedReason === "process-list-unavailable" ? "open" : result.terminatedPids.length > 0 ? "closed" : "lost");
1543
+ await params.leaseStore.markState(lease.leaseId, result.terminatedPids.length > 0 ? "closed" : "lost");
1183
1544
  }
1184
1545
  return {
1185
1546
  inspectedPids,
@@ -1214,8 +1575,8 @@ function createAcpxRuntimeService(params = {}) {
1214
1575
  }));
1215
1576
  const wrapperRoot = path.join(ctx.stateDir, "acpx");
1216
1577
  await measureAcpxStartup(ctx, "filesystem.prepare", async () => {
1217
- await fs$1.mkdir(pluginConfig.stateDir, { recursive: true });
1218
- await fs$1.mkdir(wrapperRoot, { recursive: true });
1578
+ await fs.mkdir(pluginConfig.stateDir, { recursive: true });
1579
+ await fs.mkdir(wrapperRoot, { recursive: true });
1219
1580
  });
1220
1581
  const gatewayInstanceId = await measureAcpxStartup(ctx, "gateway-instance-id", () => resolveGatewayInstanceId(openKeyedStore));
1221
1582
  const processLeaseStore = createAcpxProcessLeaseStore({ store: openAcpxProcessLeaseStateStore(openKeyedStore) });
@@ -1285,4 +1646,4 @@ function createAcpxRuntimeService(params = {}) {
1285
1646
  };
1286
1647
  }
1287
1648
  //#endregion
1288
- export { createAcpxRuntimeService, resolveAcpxTimerTimeoutMs };
1649
+ export { createAcpxRuntimeService, splitCommandParts as i, cleanupOpenClawOwnedAcpxProcessTree as n, isOpenClawLeaseAwareAcpxProcessCommand as r, resolveAcpxTimerTimeoutMs, service_exports as t };