@openclaw/acpx 2026.7.1-beta.6 → 2026.7.2-beta.1
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.
- package/dist/config-schema-lrk5nlcV.js +39 -0
- package/dist/index.js +1034 -2
- package/dist/{process-reaper-cncoDfwV.js → process-reaper-B29BzBk4.js} +15 -47
- package/dist/{register.runtime-Yz704dK5.js → register.runtime-DBqbPx5P.js} +1 -1
- package/dist/register.runtime.js +1 -1
- package/dist/{runtime-WjNm2DKO.js → runtime-D_fW1HnP.js} +20 -26
- package/dist/{service-CzJr7Koj.js → service-Dm8hpy-C.js} +51 -31
- package/dist/setup-api.js +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/openclaw.plugin.json +13 -3
- package/package.json +4 -4
|
@@ -1,13 +1,12 @@
|
|
|
1
|
+
import { t as AcpxPluginConfigSchema } from "./config-schema-lrk5nlcV.js";
|
|
1
2
|
import "./process-lease-DiKkFj6F.js";
|
|
2
3
|
import { createRequire } from "node:module";
|
|
3
4
|
import { formatPluginConfigIssue } from "openclaw/plugin-sdk/extension-shared";
|
|
4
|
-
import path from "node:path";
|
|
5
5
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
|
-
import { execFile } from "node:child_process";
|
|
7
|
-
import { promisify } from "node:util";
|
|
8
6
|
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { runExec } from "openclaw/plugin-sdk/process-runtime";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
|
-
import { z } from "zod";
|
|
11
10
|
//#region extensions/acpx/src/command-line.ts
|
|
12
11
|
/**
|
|
13
12
|
* Small shell-command helpers for ACPX-launched processes. Splitting supports
|
|
@@ -56,42 +55,6 @@ function splitCommandParts(value) {
|
|
|
56
55
|
return parts;
|
|
57
56
|
}
|
|
58
57
|
//#endregion
|
|
59
|
-
//#region extensions/acpx/src/config-schema.ts
|
|
60
|
-
/**
|
|
61
|
-
* ACPX plugin configuration schema and public config types. Runtime setup uses
|
|
62
|
-
* this file as the single source of truth for validation and defaulting.
|
|
63
|
-
*/
|
|
64
|
-
const ACPX_PERMISSION_MODES = [
|
|
65
|
-
"approve-all",
|
|
66
|
-
"approve-reads",
|
|
67
|
-
"deny-all"
|
|
68
|
-
];
|
|
69
|
-
const ACPX_NON_INTERACTIVE_POLICIES = ["deny", "fail"];
|
|
70
|
-
const nonEmptyTrimmedString = (message) => z.string({ error: message }).trim().min(1, { error: message });
|
|
71
|
-
const McpServerConfigSchema = z.object({
|
|
72
|
-
command: nonEmptyTrimmedString("command must be a non-empty string").describe("Command to run the MCP server"),
|
|
73
|
-
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"),
|
|
74
|
-
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")
|
|
75
|
-
});
|
|
76
|
-
/** Zod schema for validating raw ACPX plugin config from OpenClaw config. */
|
|
77
|
-
const AcpxPluginConfigSchema = z.strictObject({
|
|
78
|
-
cwd: nonEmptyTrimmedString("cwd must be a non-empty string").optional(),
|
|
79
|
-
stateDir: nonEmptyTrimmedString("stateDir must be a non-empty string").optional(),
|
|
80
|
-
probeAgent: nonEmptyTrimmedString("probeAgent must be a non-empty string").optional(),
|
|
81
|
-
permissionMode: z.enum(ACPX_PERMISSION_MODES, { error: `permissionMode must be one of: ${ACPX_PERMISSION_MODES.join(", ")}` }).optional(),
|
|
82
|
-
nonInteractivePermissions: z.enum(ACPX_NON_INTERACTIVE_POLICIES, { error: `nonInteractivePermissions must be one of: ${ACPX_NON_INTERACTIVE_POLICIES.join(", ")}` }).optional(),
|
|
83
|
-
pluginToolsMcpBridge: z.boolean({ error: "pluginToolsMcpBridge must be a boolean" }).optional(),
|
|
84
|
-
openClawToolsMcpBridge: z.boolean({ error: "openClawToolsMcpBridge must be a boolean" }).optional(),
|
|
85
|
-
strictWindowsCmdWrapper: z.boolean({ error: "strictWindowsCmdWrapper must be a boolean" }).optional(),
|
|
86
|
-
timeoutSeconds: z.number({ error: "timeoutSeconds must be a number >= 0.001" }).min(.001, { error: "timeoutSeconds must be a number >= 0.001" }).default(120),
|
|
87
|
-
queueOwnerTtlSeconds: z.number({ error: "queueOwnerTtlSeconds must be a number >= 0" }).min(0, { error: "queueOwnerTtlSeconds must be a number >= 0" }).optional(),
|
|
88
|
-
mcpServers: z.record(z.string(), McpServerConfigSchema).optional(),
|
|
89
|
-
agents: z.record(z.string(), z.strictObject({
|
|
90
|
-
command: nonEmptyTrimmedString("agents.<id>.command must be a non-empty string"),
|
|
91
|
-
args: z.array(z.string({ error: "args must be an array of strings" })).optional()
|
|
92
|
-
})).optional()
|
|
93
|
-
});
|
|
94
|
-
//#endregion
|
|
95
58
|
//#region extensions/acpx/src/config.ts
|
|
96
59
|
/**
|
|
97
60
|
* Resolves ACPX plugin config from raw user configuration. It locates the
|
|
@@ -260,7 +223,7 @@ function resolveAcpxPluginConfig(params) {
|
|
|
260
223
|
return {
|
|
261
224
|
cwd,
|
|
262
225
|
stateDir,
|
|
263
|
-
probeAgent:
|
|
226
|
+
probeAgent: normalized.probeAgent,
|
|
264
227
|
permissionMode: normalized.permissionMode ?? DEFAULT_PERMISSION_MODE,
|
|
265
228
|
nonInteractivePermissions: normalized.nonInteractivePermissions ?? DEFAULT_NON_INTERACTIVE_POLICY,
|
|
266
229
|
pluginToolsMcpBridge,
|
|
@@ -282,7 +245,6 @@ function resolveAcpxPluginConfig(params) {
|
|
|
282
245
|
* ACPX process ownership checks and cleanup. The reaper only terminates
|
|
283
246
|
* OpenClaw-owned wrapper trees after validating paths, packages, and lease ids.
|
|
284
247
|
*/
|
|
285
|
-
const execFileAsync = promisify(execFile);
|
|
286
248
|
const requireFromHere = createRequire(import.meta.url);
|
|
287
249
|
const GENERATED_WRAPPER_BASENAMES = /* @__PURE__ */ new Set(["codex-acp-wrapper.mjs", "claude-agent-acp-wrapper.mjs"]);
|
|
288
250
|
const OPENCLAW_PLUGIN_DEPS_MARKER = "/plugin-runtime-deps/";
|
|
@@ -375,11 +337,14 @@ function parseProcessList(stdout) {
|
|
|
375
337
|
const processes = [];
|
|
376
338
|
for (const line of stdout.split(/\r?\n/)) {
|
|
377
339
|
const match = /^\s*(?<pid>\d+)\s+(?<ppid>\d+)\s+(?<command>.+?)\s*$/.exec(line);
|
|
378
|
-
|
|
340
|
+
const pid = match?.groups?.pid;
|
|
341
|
+
const ppid = match?.groups?.ppid;
|
|
342
|
+
const command = match?.groups?.command;
|
|
343
|
+
if (!pid || !ppid || !command) continue;
|
|
379
344
|
processes.push({
|
|
380
|
-
pid: Number.parseInt(
|
|
381
|
-
ppid: Number.parseInt(
|
|
382
|
-
command
|
|
345
|
+
pid: Number.parseInt(pid, 10),
|
|
346
|
+
ppid: Number.parseInt(ppid, 10),
|
|
347
|
+
command
|
|
383
348
|
});
|
|
384
349
|
}
|
|
385
350
|
return processes;
|
|
@@ -387,7 +352,10 @@ function parseProcessList(stdout) {
|
|
|
387
352
|
/** List host processes in the compact shape needed by ACPX cleanup. */
|
|
388
353
|
async function listPlatformProcesses() {
|
|
389
354
|
if (process.platform === "win32") return [];
|
|
390
|
-
const { stdout } = await
|
|
355
|
+
const { stdout } = await runExec("ps", ["-axo", "pid=,ppid=,command="], {
|
|
356
|
+
logOutput: false,
|
|
357
|
+
maxBuffer: 8 * 1024 * 1024
|
|
358
|
+
});
|
|
391
359
|
return parseProcessList(stdout);
|
|
392
360
|
}
|
|
393
361
|
function collectProcessTree(processes, rootPid) {
|
|
@@ -200,7 +200,7 @@ function createLazyAcpRuntimeProxy(resolveRuntime) {
|
|
|
200
200
|
* immediately, then imports the heavier service only when a session needs it.
|
|
201
201
|
*/
|
|
202
202
|
const ACPX_BACKEND_ID = "acpx";
|
|
203
|
-
const loadServiceModule = createLazyRuntimeModule(() => import("./service-
|
|
203
|
+
const loadServiceModule = createLazyRuntimeModule(() => import("./service-Dm8hpy-C.js"));
|
|
204
204
|
async function startRealService(state) {
|
|
205
205
|
if (state.realRuntime) return state.realRuntime;
|
|
206
206
|
if (!state.ctx) throw new Error("ACPX runtime service is not started");
|
package/dist/register.runtime.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as createAcpxRuntimeService } from "./register.runtime-
|
|
1
|
+
import { t as createAcpxRuntimeService } from "./register.runtime-DBqbPx5P.js";
|
|
2
2
|
export { createAcpxRuntimeService };
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { i as createAcpxProcessLeaseId, o as hashAcpxProcessCommand, u as withAcpxLeaseEnvironment } from "./process-lease-DiKkFj6F.js";
|
|
2
2
|
import { AcpRuntimeError } from "./runtime-api.js";
|
|
3
|
-
import { c as splitCommandParts, n as isOpenClawLeaseAwareAcpxProcessCommand, t as cleanupOpenClawOwnedAcpxProcessTree } from "./process-reaper-
|
|
3
|
+
import { c as splitCommandParts, n as isOpenClawLeaseAwareAcpxProcessCommand, t as cleanupOpenClawOwnedAcpxProcessTree } from "./process-reaper-B29BzBk4.js";
|
|
4
|
+
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
5
|
+
import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
6
|
import fs from "node:fs/promises";
|
|
5
7
|
import path, { resolve } from "node:path";
|
|
6
|
-
import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
7
8
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
8
9
|
import { ACPX_BACKEND_ID, AcpxRuntime as AcpxRuntime$1, createAcpRuntime, createAgentRegistry, createFileSessionStore, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState, isRequestedModelUnsupportedError } from "acpx/runtime";
|
|
9
|
-
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
10
10
|
import { redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
|
|
11
|
+
import { sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
11
12
|
//#region extensions/acpx/src/runtime.ts
|
|
12
13
|
/**
|
|
13
14
|
* OpenClaw ACPX runtime adapter. It wraps the upstream acpx runtime with
|
|
@@ -48,7 +49,7 @@ function isGenericInternalAcpError(error) {
|
|
|
48
49
|
async function readCodexWrapperStderrTail(params) {
|
|
49
50
|
if (!params.wrapperRoot || !params.leaseId) return "";
|
|
50
51
|
try {
|
|
51
|
-
return compactDiagnosticText(redactSensitiveText((await fs.readFile(path.join(params.wrapperRoot, codexWrapperStderrLogFileName(params.leaseId)), "utf8")
|
|
52
|
+
return compactDiagnosticText(redactSensitiveText(sliceUtf16Safe(await fs.readFile(path.join(params.wrapperRoot, codexWrapperStderrLogFileName(params.leaseId)), "utf8"), -6e3)));
|
|
52
53
|
} catch {
|
|
53
54
|
return "";
|
|
54
55
|
}
|
|
@@ -213,9 +214,14 @@ function isEnvAssignment(value) {
|
|
|
213
214
|
return /^[A-Za-z_][A-Za-z0-9_]*=/.test(value);
|
|
214
215
|
}
|
|
215
216
|
function unwrapEnvCommand(parts) {
|
|
216
|
-
|
|
217
|
+
const command = parts.at(0);
|
|
218
|
+
if (!command || basename(command) !== "env") return parts;
|
|
217
219
|
let index = 1;
|
|
218
|
-
while (
|
|
220
|
+
while (true) {
|
|
221
|
+
const part = parts.at(index);
|
|
222
|
+
if (!part || !isEnvAssignment(part)) break;
|
|
223
|
+
index += 1;
|
|
224
|
+
}
|
|
219
225
|
return parts.slice(index);
|
|
220
226
|
}
|
|
221
227
|
function matchesExecutableName(value, executableName) {
|
|
@@ -362,13 +368,6 @@ function resolveAgentCommand(params) {
|
|
|
362
368
|
const resolvedCommand = params.agentRegistry.resolve(normalizedAgentName);
|
|
363
369
|
return typeof resolvedCommand === "string" ? resolvedCommand.trim() || void 0 : void 0;
|
|
364
370
|
}
|
|
365
|
-
function resolveProbeAgentName(options) {
|
|
366
|
-
const { probeAgent } = options;
|
|
367
|
-
return normalizeAgentName(typeof probeAgent === "string" ? probeAgent : void 0) ?? "codex";
|
|
368
|
-
}
|
|
369
|
-
function resolveAgentCommandForName(params) {
|
|
370
|
-
return resolveAgentCommand(params);
|
|
371
|
-
}
|
|
372
371
|
function shouldUseBridgeSafeDelegateForCommand(command) {
|
|
373
372
|
return isOpenClawBridgeCommand(command);
|
|
374
373
|
}
|
|
@@ -430,17 +429,12 @@ var AcpxRuntime = class {
|
|
|
430
429
|
...sharedOptions,
|
|
431
430
|
mcpServers: []
|
|
432
431
|
}, this.delegateTestOptions) : this.delegate;
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
resolveDelegateForAgent(agentName) {
|
|
436
|
-
const command = resolveAgentCommandForName({
|
|
437
|
-
agentName,
|
|
432
|
+
const probeCommand = resolveAgentCommand({
|
|
433
|
+
agentName: normalizeAgentName(options.probeAgent) ?? "codex",
|
|
438
434
|
agentRegistry: this.agentRegistry
|
|
439
435
|
});
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
resolveDelegateForCommand(command) {
|
|
443
|
-
return shouldUseBridgeSafeDelegateForCommand(command) ? this.bridgeSafeDelegate : this.delegate;
|
|
436
|
+
const useBridgeSafeProbe = this.openclawToolsMcpBridgeEnabled || shouldUseBridgeSafeDelegateForCommand(probeCommand);
|
|
437
|
+
this.probeDelegate = useBridgeSafeProbe ? this.bridgeSafeDelegate : this.delegate;
|
|
444
438
|
}
|
|
445
439
|
resolveDelegateForSession(params) {
|
|
446
440
|
if (shouldUseBridgeSafeDelegateForCommand(params.command)) return this.bridgeSafeDelegate;
|
|
@@ -479,7 +473,7 @@ var AcpxRuntime = class {
|
|
|
479
473
|
command: recordCommand,
|
|
480
474
|
sessionKey: handle.sessionKey
|
|
481
475
|
});
|
|
482
|
-
const command =
|
|
476
|
+
const command = resolveAgentCommand({
|
|
483
477
|
agentName: readAgentFromHandle(handle),
|
|
484
478
|
agentRegistry: this.agentRegistry
|
|
485
479
|
});
|
|
@@ -491,7 +485,7 @@ var AcpxRuntime = class {
|
|
|
491
485
|
async resolveCommandForHandle(handle) {
|
|
492
486
|
const recordCommand = readAgentCommandFromRecord(await this.sessionStore.load(handle.acpxRecordId ?? handle.sessionKey));
|
|
493
487
|
if (recordCommand) return recordCommand;
|
|
494
|
-
return
|
|
488
|
+
return resolveAgentCommand({
|
|
495
489
|
agentName: readAgentFromHandle(handle),
|
|
496
490
|
agentRegistry: this.agentRegistry
|
|
497
491
|
});
|
|
@@ -585,7 +579,7 @@ var AcpxRuntime = class {
|
|
|
585
579
|
await this.processLeaseStore?.markState(lease.leaseId, result.terminatedPids.length > 0 || result.skippedReason === "missing-root" ? "closed" : "lost");
|
|
586
580
|
return;
|
|
587
581
|
}
|
|
588
|
-
const rootCommand = readAgentCommandFromRecord(record) ??
|
|
582
|
+
const rootCommand = readAgentCommandFromRecord(record) ?? resolveAgentCommand({
|
|
589
583
|
agentName: readAgentFromHandle(handle),
|
|
590
584
|
agentRegistry: this.agentRegistry
|
|
591
585
|
});
|
|
@@ -611,7 +605,7 @@ var AcpxRuntime = class {
|
|
|
611
605
|
}
|
|
612
606
|
async ensureSession(input) {
|
|
613
607
|
assertSupportedRuntimeSessionMode(input.mode);
|
|
614
|
-
const command =
|
|
608
|
+
const command = resolveAgentCommand({
|
|
615
609
|
agentName: input.agent,
|
|
616
610
|
agentRegistry: this.agentRegistry
|
|
617
611
|
});
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import { n as createLazyAcpRuntimeProxy } from "./register.runtime-
|
|
1
|
+
import { n as createLazyAcpRuntimeProxy } from "./register.runtime-DBqbPx5P.js";
|
|
2
|
+
import "./config-schema-lrk5nlcV.js";
|
|
2
3
|
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.js";
|
|
3
4
|
import { registerAcpRuntimeBackend, unregisterAcpRuntimeBackend } from "./runtime-api.js";
|
|
4
|
-
import { a as resolveAcpxPluginRoot, c as splitCommandParts, i as resolveAcpxPluginConfig, o as toAcpMcpServers, r as reapStaleOpenClawOwnedAcpxOrphans, s as quoteCommandPart, t as cleanupOpenClawOwnedAcpxProcessTree } from "./process-reaper-
|
|
5
|
+
import { a as resolveAcpxPluginRoot, c as splitCommandParts, i as resolveAcpxPluginConfig, o as toAcpMcpServers, r as reapStaleOpenClawOwnedAcpxOrphans, s as quoteCommandPart, t as cleanupOpenClawOwnedAcpxProcessTree } from "./process-reaper-B29BzBk4.js";
|
|
5
6
|
import { createRequire } from "node:module";
|
|
7
|
+
import { finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
|
|
6
8
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
7
|
-
import
|
|
9
|
+
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
10
|
+
import fs from "node:fs";
|
|
11
|
+
import fs$1 from "node:fs/promises";
|
|
8
12
|
import path from "node:path";
|
|
13
|
+
import os from "node:os";
|
|
9
14
|
import { randomUUID } from "node:crypto";
|
|
10
|
-
import { finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
|
|
11
15
|
import { inspect } from "node:util";
|
|
12
|
-
import fs$1 from "node:fs";
|
|
13
16
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
14
|
-
import os from "node:os";
|
|
15
17
|
import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
|
|
16
18
|
//#region extensions/acpx/src/codex-trust-config.ts
|
|
17
19
|
/**
|
|
@@ -123,9 +125,11 @@ function extractTrustedCodexProjectPaths(configToml) {
|
|
|
123
125
|
continue;
|
|
124
126
|
}
|
|
125
127
|
const assignment = /^(?<key>"(?:\\.|[^"\\])*"|'[^']*'|[A-Za-z0-9_\-/.~:]+)\s*=\s*(?<value>.+)$/.exec(line);
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
128
|
+
const rawKey = assignment?.groups?.key;
|
|
129
|
+
const rawValue = assignment?.groups?.value;
|
|
130
|
+
if (!rawKey || rawValue === void 0) continue;
|
|
131
|
+
const key = parseTomlString(rawKey) ?? rawKey;
|
|
132
|
+
const value = rawValue.trim();
|
|
129
133
|
if (inProjectsTable && /^\{.*\}$/.test(value)) {
|
|
130
134
|
if (/\btrust_level\s*=\s*["']trusted["']/.test(value) && key) trusted.add(key);
|
|
131
135
|
continue;
|
|
@@ -229,7 +233,7 @@ const RUN_CONFIGURED_COMMAND_SENTINEL = "--openclaw-run-configured";
|
|
|
229
233
|
const requireFromHere = createRequire(import.meta.url);
|
|
230
234
|
function readSelfManifest() {
|
|
231
235
|
const manifestPath = path.join(resolveAcpxPluginRoot(import.meta.url), "package.json");
|
|
232
|
-
return JSON.parse(fs
|
|
236
|
+
return JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
233
237
|
}
|
|
234
238
|
function readManifestDependencyVersion(packageName) {
|
|
235
239
|
const version = readSelfManifest().dependencies?.[packageName];
|
|
@@ -254,7 +258,7 @@ async function resolveInstalledAcpPackageBinPath(packageName, binName) {
|
|
|
254
258
|
if (manifest.name !== packageName) return;
|
|
255
259
|
const binPath = resolvePackageBinPath(packageJsonPath, manifest, binName);
|
|
256
260
|
if (!binPath) return;
|
|
257
|
-
await fs.access(binPath);
|
|
261
|
+
await fs$1.access(binPath);
|
|
258
262
|
return binPath;
|
|
259
263
|
} catch {
|
|
260
264
|
return;
|
|
@@ -381,6 +385,7 @@ function buildAdapterWrapperScript(params) {
|
|
|
381
385
|
import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
382
386
|
import path from "node:path";
|
|
383
387
|
import { spawn } from "node:child_process";
|
|
388
|
+
import { StringDecoder } from "node:string_decoder";
|
|
384
389
|
import { fileURLToPath } from "node:url";
|
|
385
390
|
|
|
386
391
|
${params.envSetup}
|
|
@@ -431,7 +436,25 @@ function redactDiagnosticText(text) {
|
|
|
431
436
|
return redacted;
|
|
432
437
|
}
|
|
433
438
|
|
|
439
|
+
function tailUtf16Safe(text, maxChars) {
|
|
440
|
+
let start = Math.max(0, text.length - maxChars);
|
|
441
|
+
const startsInsideSurrogatePair =
|
|
442
|
+
start > 0 &&
|
|
443
|
+
start < text.length &&
|
|
444
|
+
text.charCodeAt(start) >= 0xdc00 &&
|
|
445
|
+
text.charCodeAt(start) <= 0xdfff &&
|
|
446
|
+
text.charCodeAt(start - 1) >= 0xd800 &&
|
|
447
|
+
text.charCodeAt(start - 1) <= 0xdbff;
|
|
448
|
+
if (startsInsideSurrogatePair) {
|
|
449
|
+
start += 1;
|
|
450
|
+
}
|
|
451
|
+
return text.slice(start);
|
|
452
|
+
}
|
|
453
|
+
|
|
434
454
|
let pendingStderrLogText = "";
|
|
455
|
+
// Pipe chunks can split a UTF-8 sequence. Preserve decoder state so diagnostic
|
|
456
|
+
// capture does not manufacture replacement characters between chunks.
|
|
457
|
+
const stderrDecoder = new StringDecoder("utf8");
|
|
435
458
|
const stderrPrivateKeyEndPattern = /-----END [A-Z ]*PRIVATE KEY-----/;
|
|
436
459
|
|
|
437
460
|
function hasUnclosedPrivateKeyBlock(text) {
|
|
@@ -456,7 +479,7 @@ function writeRedactedStderrLog(text) {
|
|
|
456
479
|
appendFileSync(stderrLogPath, redactDiagnosticText(text), "utf8");
|
|
457
480
|
const current = readFileSync(stderrLogPath, "utf8");
|
|
458
481
|
if (current.length > stderrLogMaxChars) {
|
|
459
|
-
writeFileSync(stderrLogPath, current
|
|
482
|
+
writeFileSync(stderrLogPath, tailUtf16Safe(current, stderrLogMaxChars), "utf8");
|
|
460
483
|
}
|
|
461
484
|
} catch {
|
|
462
485
|
// Stderr capture is diagnostic-only; never break the ACP adapter.
|
|
@@ -475,7 +498,7 @@ function flushFinalizedStderrLogText() {
|
|
|
475
498
|
const lastLineBreak = pendingStderrLogText.lastIndexOf("\\n");
|
|
476
499
|
if (lastLineBreak === -1) {
|
|
477
500
|
if (pendingStderrLogText.length > stderrLogMaxChars) {
|
|
478
|
-
pendingStderrLogText = pendingStderrLogText
|
|
501
|
+
pendingStderrLogText = tailUtf16Safe(pendingStderrLogText, stderrLogMaxChars);
|
|
479
502
|
}
|
|
480
503
|
return;
|
|
481
504
|
}
|
|
@@ -488,7 +511,7 @@ function flushFinalizedStderrLogText() {
|
|
|
488
511
|
}
|
|
489
512
|
if (flushEnd <= 0) {
|
|
490
513
|
if (pendingStderrLogText.length > stderrLogMaxChars) {
|
|
491
|
-
pendingStderrLogText = pendingStderrLogText
|
|
514
|
+
pendingStderrLogText = tailUtf16Safe(pendingStderrLogText, stderrLogMaxChars);
|
|
492
515
|
}
|
|
493
516
|
return;
|
|
494
517
|
}
|
|
@@ -498,7 +521,7 @@ function flushFinalizedStderrLogText() {
|
|
|
498
521
|
}
|
|
499
522
|
|
|
500
523
|
function appendStderrLog(chunk) {
|
|
501
|
-
const text =
|
|
524
|
+
const text = stderrDecoder.write(chunk);
|
|
502
525
|
if (!text) {
|
|
503
526
|
return;
|
|
504
527
|
}
|
|
@@ -507,6 +530,7 @@ function appendStderrLog(chunk) {
|
|
|
507
530
|
}
|
|
508
531
|
|
|
509
532
|
function finishStderrLog() {
|
|
533
|
+
pendingStderrLogText += stderrDecoder.end();
|
|
510
534
|
const text = redactIncompletePrivateKeyTail(pendingStderrLogText);
|
|
511
535
|
pendingStderrLogText = "";
|
|
512
536
|
writeRedactedStderrLog(text);
|
|
@@ -731,7 +755,7 @@ function buildClaudeAcpWrapperScript(installedBinPath) {
|
|
|
731
755
|
}
|
|
732
756
|
async function readSourceCodexConfig(codexHome) {
|
|
733
757
|
try {
|
|
734
|
-
return await fs.readFile(path.join(codexHome, "config.toml"), "utf8");
|
|
758
|
+
return await fs$1.readFile(path.join(codexHome, "config.toml"), "utf8");
|
|
735
759
|
} catch (error) {
|
|
736
760
|
if (error.code === "ENOENT") return;
|
|
737
761
|
throw error;
|
|
@@ -741,8 +765,8 @@ async function prepareIsolatedCodexHome(params) {
|
|
|
741
765
|
const sourceConfig = await readSourceCodexConfig(process.env.CODEX_HOME || path.join(os.homedir(), ".codex"));
|
|
742
766
|
const trustedProjectPaths = [...sourceConfig ? extractTrustedCodexProjectPaths(sourceConfig) : [], params.workspaceDir];
|
|
743
767
|
const codexHome = path.join(params.baseDir, "codex-home");
|
|
744
|
-
await fs.mkdir(codexHome, { recursive: true });
|
|
745
|
-
await fs.writeFile(path.join(codexHome, "config.toml"), renderIsolatedCodexConfig({
|
|
768
|
+
await fs$1.mkdir(codexHome, { recursive: true });
|
|
769
|
+
await fs$1.writeFile(path.join(codexHome, "config.toml"), renderIsolatedCodexConfig({
|
|
746
770
|
sourceConfigToml: sourceConfig,
|
|
747
771
|
projectPaths: trustedProjectPaths
|
|
748
772
|
}), "utf8");
|
|
@@ -750,20 +774,20 @@ async function prepareIsolatedCodexHome(params) {
|
|
|
750
774
|
}
|
|
751
775
|
async function makeGeneratedWrapperExecutableIfPossible(wrapperPath) {
|
|
752
776
|
try {
|
|
753
|
-
await fs.chmod(wrapperPath, 493);
|
|
777
|
+
await fs$1.chmod(wrapperPath, 493);
|
|
754
778
|
} catch {}
|
|
755
779
|
}
|
|
756
780
|
async function writeCodexAcpWrapper(baseDir, installedBinPath) {
|
|
757
|
-
await fs.mkdir(baseDir, { recursive: true });
|
|
781
|
+
await fs$1.mkdir(baseDir, { recursive: true });
|
|
758
782
|
const wrapperPath = path.join(baseDir, "codex-acp-wrapper.mjs");
|
|
759
|
-
await fs.writeFile(wrapperPath, buildCodexAcpWrapperScript(installedBinPath), { encoding: "utf8" });
|
|
783
|
+
await fs$1.writeFile(wrapperPath, buildCodexAcpWrapperScript(installedBinPath), { encoding: "utf8" });
|
|
760
784
|
await makeGeneratedWrapperExecutableIfPossible(wrapperPath);
|
|
761
785
|
return wrapperPath;
|
|
762
786
|
}
|
|
763
787
|
async function writeClaudeAcpWrapper(baseDir, installedBinPath) {
|
|
764
|
-
await fs.mkdir(baseDir, { recursive: true });
|
|
788
|
+
await fs$1.mkdir(baseDir, { recursive: true });
|
|
765
789
|
const wrapperPath = path.join(baseDir, "claude-agent-acp-wrapper.mjs");
|
|
766
|
-
await fs.writeFile(wrapperPath, buildClaudeAcpWrapperScript(installedBinPath), { encoding: "utf8" });
|
|
790
|
+
await fs$1.writeFile(wrapperPath, buildClaudeAcpWrapperScript(installedBinPath), { encoding: "utf8" });
|
|
767
791
|
await makeGeneratedWrapperExecutableIfPossible(wrapperPath);
|
|
768
792
|
return wrapperPath;
|
|
769
793
|
}
|
|
@@ -852,7 +876,7 @@ async function prepareAcpxCodexAuthConfig(params) {
|
|
|
852
876
|
const ENABLE_STARTUP_PROBE_ENV = "OPENCLAW_ACPX_RUNTIME_STARTUP_PROBE";
|
|
853
877
|
const SKIP_RUNTIME_PROBE_ENV = "OPENCLAW_SKIP_ACPX_RUNTIME_PROBE";
|
|
854
878
|
const ACPX_BACKEND_ID = "acpx";
|
|
855
|
-
const loadRuntimeModule = createLazyRuntimeModule(() => import("./runtime-
|
|
879
|
+
const loadRuntimeModule = createLazyRuntimeModule(() => import("./runtime-D_fW1HnP.js"));
|
|
856
880
|
/** Convert ACPX timeout seconds into timer-safe milliseconds. */
|
|
857
881
|
function resolveAcpxTimerTimeoutMs(timeoutSeconds) {
|
|
858
882
|
if (timeoutSeconds === void 0) return;
|
|
@@ -924,13 +948,9 @@ function formatDoctorFailureMessage(report) {
|
|
|
924
948
|
const detailText = report.details?.map(formatDoctorDetail).filter(Boolean).join("; ").trim();
|
|
925
949
|
return detailText ? `${report.message} (${detailText})` : report.message;
|
|
926
950
|
}
|
|
927
|
-
function normalizeProbeAgent(value) {
|
|
928
|
-
const normalized = value?.trim().toLowerCase();
|
|
929
|
-
return normalized ? normalized : void 0;
|
|
930
|
-
}
|
|
931
951
|
function resolveAllowedAgentsProbeAgent(ctx) {
|
|
932
952
|
for (const agent of ctx.config.acp?.allowedAgents ?? []) {
|
|
933
|
-
const normalized =
|
|
953
|
+
const normalized = normalizeLowercaseStringOrEmpty(agent);
|
|
934
954
|
if (normalized) return normalized;
|
|
935
955
|
}
|
|
936
956
|
}
|
|
@@ -1043,8 +1063,8 @@ function createAcpxRuntimeService(params = {}) {
|
|
|
1043
1063
|
}));
|
|
1044
1064
|
const wrapperRoot = path.join(ctx.stateDir, "acpx");
|
|
1045
1065
|
await measureAcpxStartup(ctx, "filesystem.prepare", async () => {
|
|
1046
|
-
await fs.mkdir(pluginConfig.stateDir, { recursive: true });
|
|
1047
|
-
await fs.mkdir(wrapperRoot, { recursive: true });
|
|
1066
|
+
await fs$1.mkdir(pluginConfig.stateDir, { recursive: true });
|
|
1067
|
+
await fs$1.mkdir(wrapperRoot, { recursive: true });
|
|
1048
1068
|
});
|
|
1049
1069
|
const gatewayInstanceId = await measureAcpxStartup(ctx, "gateway-instance-id", () => resolveGatewayInstanceId(openKeyedStore));
|
|
1050
1070
|
const processLeaseStore = createAcpxProcessLeaseStore({ store: openAcpxProcessLeaseStateStore(openKeyedStore) });
|
package/dist/setup-api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
2
1
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
2
|
+
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
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
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/acpx",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.2-beta.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/acpx",
|
|
9
|
-
"version": "2026.7.
|
|
9
|
+
"version": "2026.7.2-beta.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@agentclientprotocol/claude-agent-acp": "0.55.0",
|
|
12
12
|
"@zed-industries/codex-acp": "0.16.0",
|
package/openclaw.plugin.json
CHANGED
|
@@ -49,9 +49,15 @@
|
|
|
49
49
|
"type": "number",
|
|
50
50
|
"minimum": 0
|
|
51
51
|
},
|
|
52
|
-
"
|
|
53
|
-
"type": "
|
|
54
|
-
"
|
|
52
|
+
"piSessionCatalog": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"additionalProperties": false,
|
|
55
|
+
"properties": {
|
|
56
|
+
"enabled": {
|
|
57
|
+
"type": "boolean",
|
|
58
|
+
"default": true
|
|
59
|
+
}
|
|
60
|
+
}
|
|
55
61
|
},
|
|
56
62
|
"mcpServers": {
|
|
57
63
|
"type": "object",
|
|
@@ -138,6 +144,10 @@
|
|
|
138
144
|
"help": "Reserved compatibility field for the older embedded ACPX queue-owner path. Accepted for compatibility and logged as ignored.",
|
|
139
145
|
"advanced": true
|
|
140
146
|
},
|
|
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
|
+
},
|
|
141
151
|
"probeAgent": {
|
|
142
152
|
"label": "Health Probe Agent",
|
|
143
153
|
"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.
|
|
3
|
+
"version": "2026.7.2-beta.1",
|
|
4
4
|
"description": "OpenClaw ACP runtime backend with plugin-owned session and transport management.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
"minHostVersion": ">=2026.4.25"
|
|
27
27
|
},
|
|
28
28
|
"compat": {
|
|
29
|
-
"pluginApi": ">=2026.7.
|
|
29
|
+
"pluginApi": ">=2026.7.2-beta.1"
|
|
30
30
|
},
|
|
31
31
|
"build": {
|
|
32
|
-
"openclawVersion": "2026.7.
|
|
32
|
+
"openclawVersion": "2026.7.2-beta.1",
|
|
33
33
|
"staticAssets": [
|
|
34
34
|
{
|
|
35
35
|
"source": "./src/runtime-internals/mcp-proxy.mjs",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"skills/**"
|
|
59
59
|
],
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"openclaw": ">=2026.7.
|
|
61
|
+
"openclaw": ">=2026.7.2-beta.1"
|
|
62
62
|
},
|
|
63
63
|
"peerDependenciesMeta": {
|
|
64
64
|
"openclaw": {
|