@openclaw/acpx 2026.5.12 → 2026.5.14-beta.2
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,6 +1,6 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
-
import fs from "node:fs/promises";
|
|
4
4
|
import { readJsonFileWithFallback, writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
|
|
5
5
|
import { execFile } from "node:child_process";
|
|
6
6
|
import { promisify } from "node:util";
|
|
@@ -90,12 +90,8 @@ function hashAcpxProcessCommand(command) {
|
|
|
90
90
|
function quoteEnvValue(value) {
|
|
91
91
|
return /^[A-Za-z0-9_./:=@+-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
|
|
92
92
|
}
|
|
93
|
-
function
|
|
94
|
-
if ((params.platform ?? process.platform) === "win32") return params.command;
|
|
93
|
+
function appendAcpxLeaseArgs(params) {
|
|
95
94
|
return [
|
|
96
|
-
"env",
|
|
97
|
-
`${OPENCLAW_ACPX_LEASE_ID_ENV}=${quoteEnvValue(params.leaseId)}`,
|
|
98
|
-
`${OPENCLAW_GATEWAY_INSTANCE_ID_ENV}=${quoteEnvValue(params.gatewayInstanceId)}`,
|
|
99
95
|
params.command,
|
|
100
96
|
OPENCLAW_ACPX_LEASE_ID_ARG,
|
|
101
97
|
quoteEnvValue(params.leaseId),
|
|
@@ -103,6 +99,15 @@ function withAcpxLeaseEnvironment(params) {
|
|
|
103
99
|
quoteEnvValue(params.gatewayInstanceId)
|
|
104
100
|
].join(" ");
|
|
105
101
|
}
|
|
102
|
+
function withAcpxLeaseEnvironment(params) {
|
|
103
|
+
if ((params.platform ?? process.platform) === "win32") return appendAcpxLeaseArgs(params);
|
|
104
|
+
return [
|
|
105
|
+
"env",
|
|
106
|
+
`${OPENCLAW_ACPX_LEASE_ID_ENV}=${quoteEnvValue(params.leaseId)}`,
|
|
107
|
+
`${OPENCLAW_GATEWAY_INSTANCE_ID_ENV}=${quoteEnvValue(params.gatewayInstanceId)}`,
|
|
108
|
+
appendAcpxLeaseArgs(params)
|
|
109
|
+
].join(" ");
|
|
110
|
+
}
|
|
106
111
|
//#endregion
|
|
107
112
|
//#region extensions/acpx/src/process-reaper.ts
|
|
108
113
|
const execFileAsync = promisify(execFile);
|
|
@@ -328,4 +333,4 @@ async function reapStaleOpenClawOwnedAcpxOrphans(params) {
|
|
|
328
333
|
};
|
|
329
334
|
}
|
|
330
335
|
//#endregion
|
|
331
|
-
export {
|
|
336
|
+
export { OPENCLAW_ACPX_LEASE_ID_ENV as a, createAcpxProcessLeaseStore as c, OPENCLAW_ACPX_LEASE_ID_ARG as i, hashAcpxProcessCommand as l, isOpenClawOwnedAcpxProcessCommand as n, OPENCLAW_GATEWAY_INSTANCE_ID_ARG as o, reapStaleOpenClawOwnedAcpxOrphans as r, createAcpxProcessLeaseId as s, cleanupOpenClawOwnedAcpxProcessTree as t, withAcpxLeaseEnvironment as u };
|
package/dist/register.runtime.js
CHANGED
|
@@ -5,7 +5,7 @@ const ENABLE_STARTUP_PROBE_ENV = "OPENCLAW_ACPX_RUNTIME_STARTUP_PROBE";
|
|
|
5
5
|
const SKIP_RUNTIME_PROBE_ENV = "OPENCLAW_SKIP_ACPX_RUNTIME_PROBE";
|
|
6
6
|
let serviceModulePromise = null;
|
|
7
7
|
function loadServiceModule() {
|
|
8
|
-
serviceModulePromise ??= import("./service-
|
|
8
|
+
serviceModulePromise ??= import("./service-DPrf6fF8.js");
|
|
9
9
|
return serviceModulePromise;
|
|
10
10
|
}
|
|
11
11
|
function shouldRunStartupProbe(env = process.env) {
|
|
@@ -1,9 +1,37 @@
|
|
|
1
1
|
import { AcpRuntimeError } from "./runtime-api.js";
|
|
2
|
-
import {
|
|
2
|
+
import { l as hashAcpxProcessCommand, n as isOpenClawOwnedAcpxProcessCommand, s as createAcpxProcessLeaseId, t as cleanupOpenClawOwnedAcpxProcessTree, u as withAcpxLeaseEnvironment } from "./process-reaper-biYb35VD.js";
|
|
3
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
-
import
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import path, { resolve } from "node:path";
|
|
5
6
|
import { ACPX_BACKEND_ID, AcpxRuntime as AcpxRuntime$1, createAcpRuntime, createAgentRegistry, createFileSessionStore, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState } from "acpx/runtime";
|
|
7
|
+
import { redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
|
|
6
8
|
//#region extensions/acpx/src/runtime.ts
|
|
9
|
+
const CODEX_WRAPPER_STDERR_LOG_PREFIX = "codex-acp-wrapper.stderr";
|
|
10
|
+
const CODEX_WRAPPER_ERROR_TAIL_MAX_CHARS = 6e3;
|
|
11
|
+
function safeDiagnosticFilePart(value) {
|
|
12
|
+
return value.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 120) || "unknown";
|
|
13
|
+
}
|
|
14
|
+
function codexWrapperStderrLogFileName(leaseId) {
|
|
15
|
+
return `${CODEX_WRAPPER_STDERR_LOG_PREFIX}.${safeDiagnosticFilePart(leaseId)}.log`;
|
|
16
|
+
}
|
|
17
|
+
function compactDiagnosticText(value) {
|
|
18
|
+
return value.replace(/\s+/g, " ").trim();
|
|
19
|
+
}
|
|
20
|
+
function isGenericInternalAcpErrorMessage(message) {
|
|
21
|
+
return message.trim() === "Internal error";
|
|
22
|
+
}
|
|
23
|
+
function isGenericInternalAcpError(error) {
|
|
24
|
+
if (!(error instanceof Error)) return false;
|
|
25
|
+
return isGenericInternalAcpErrorMessage(error.message);
|
|
26
|
+
}
|
|
27
|
+
async function readCodexWrapperStderrTail(params) {
|
|
28
|
+
if (!params.wrapperRoot || !params.leaseId) return "";
|
|
29
|
+
try {
|
|
30
|
+
return compactDiagnosticText(redactSensitiveText((await fs.readFile(path.join(params.wrapperRoot, codexWrapperStderrLogFileName(params.leaseId)), "utf8")).slice(-CODEX_WRAPPER_ERROR_TAIL_MAX_CHARS)));
|
|
31
|
+
} catch {
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
7
35
|
function readSessionRecordName(record) {
|
|
8
36
|
if (typeof record !== "object" || record === null) return "";
|
|
9
37
|
const { name } = record;
|
|
@@ -200,6 +228,28 @@ function unwrapEnvCommand(parts) {
|
|
|
200
228
|
while (index < parts.length && isEnvAssignment(parts[index])) index += 1;
|
|
201
229
|
return parts.slice(index);
|
|
202
230
|
}
|
|
231
|
+
function matchesExecutableName(value, executableName) {
|
|
232
|
+
const normalized = basename(value).toLowerCase();
|
|
233
|
+
return normalized === executableName || normalized === `${executableName}.exe`;
|
|
234
|
+
}
|
|
235
|
+
function matchesPackageSpec(value, packageName) {
|
|
236
|
+
const normalized = value.trim().toLowerCase();
|
|
237
|
+
return normalized === packageName || normalized.startsWith(`${packageName}@`);
|
|
238
|
+
}
|
|
239
|
+
function stripModuleExtension(value) {
|
|
240
|
+
return value.replace(/\.[cm]?js$/i, "").toLowerCase();
|
|
241
|
+
}
|
|
242
|
+
function isAcpCommand(command, params) {
|
|
243
|
+
if (!command) return false;
|
|
244
|
+
const parts = unwrapEnvCommand(splitCommandParts(command.trim()));
|
|
245
|
+
if (!parts.length) return false;
|
|
246
|
+
if (parts.some((part) => matchesPackageSpec(part, params.packageName))) return true;
|
|
247
|
+
const commandName = basename(parts[0] ?? "");
|
|
248
|
+
if (matchesExecutableName(commandName, params.executableName)) return true;
|
|
249
|
+
if (!matchesExecutableName(commandName, "node")) return false;
|
|
250
|
+
const scriptName = stripModuleExtension(basename(parts[1] ?? ""));
|
|
251
|
+
return scriptName === params.executableName || scriptName === `${params.executableName}-wrapper`;
|
|
252
|
+
}
|
|
203
253
|
function isOpenClawBridgeCommand(command) {
|
|
204
254
|
if (!command) return false;
|
|
205
255
|
const parts = unwrapEnvCommand(splitCommandParts(command.trim()));
|
|
@@ -208,24 +258,23 @@ function isOpenClawBridgeCommand(command) {
|
|
|
208
258
|
const scriptName = basename(parts[1] ?? "");
|
|
209
259
|
return /^openclaw(?:\.[cm]?js)?$/i.test(scriptName) && parts[2] === OPENCLAW_BRIDGE_SUBCOMMAND;
|
|
210
260
|
}
|
|
211
|
-
function isCodexAcpPackageSpec(value) {
|
|
212
|
-
return /^@zed-industries\/codex-acp(?:@.+)?$/i.test(value.trim());
|
|
213
|
-
}
|
|
214
261
|
function isCodexAcpCommand(command) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
262
|
+
return isAcpCommand(command, {
|
|
263
|
+
packageName: "@zed-industries/codex-acp",
|
|
264
|
+
executableName: "codex-acp"
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
function isClaudeAcpCommand(command) {
|
|
268
|
+
return isAcpCommand(command, {
|
|
269
|
+
packageName: "@agentclientprotocol/claude-agent-acp",
|
|
270
|
+
executableName: "claude-agent-acp"
|
|
271
|
+
});
|
|
224
272
|
}
|
|
225
273
|
function failUnsupportedCodexAcpModel(rawModel, detail) {
|
|
226
274
|
throw new AcpRuntimeError("ACP_INVALID_RUNTIME_OPTION", detail ?? `Codex ACP model "${rawModel}" is not supported. Use openai-codex/<model> or <model>/<reasoning-effort>.`);
|
|
227
275
|
}
|
|
228
276
|
const SUPPORTED_RUNTIME_SESSION_MODES = new Set(["persistent", "oneshot"]);
|
|
277
|
+
const WIRE_TIMEOUT_CONFIG_KEYS = new Set(["timeout", "timeout_seconds"]);
|
|
229
278
|
function assertSupportedRuntimeSessionMode(mode) {
|
|
230
279
|
if (typeof mode === "string" && SUPPORTED_RUNTIME_SESSION_MODES.has(mode)) return;
|
|
231
280
|
const supported = Array.from(SUPPORTED_RUNTIME_SESSION_MODES).join(", ");
|
|
@@ -410,6 +459,26 @@ var AcpxRuntime = class {
|
|
|
410
459
|
});
|
|
411
460
|
return await this.launchLeaseScope.run(launch, params.run);
|
|
412
461
|
}
|
|
462
|
+
async withCodexWrapperDiagnostics(params) {
|
|
463
|
+
try {
|
|
464
|
+
return await params.run();
|
|
465
|
+
} catch (error) {
|
|
466
|
+
if (!isCodexAcpCommand(params.command) || !isGenericInternalAcpError(error)) throw error;
|
|
467
|
+
const stderrTail = await readCodexWrapperStderrTail({
|
|
468
|
+
wrapperRoot: this.wrapperRoot,
|
|
469
|
+
leaseId: this.launchLeaseScope.getStore()?.leaseId
|
|
470
|
+
});
|
|
471
|
+
if (!stderrTail) throw error;
|
|
472
|
+
throw new AcpRuntimeError(params.fallbackCode, `Internal error: ${stderrTail}`, { cause: error });
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
async readCodexTurnFailureStderr(params) {
|
|
476
|
+
const record = await this.sessionStore.load(params.handle.acpxRecordId ?? params.handle.sessionKey);
|
|
477
|
+
return readCodexWrapperStderrTail({
|
|
478
|
+
wrapperRoot: this.wrapperRoot,
|
|
479
|
+
leaseId: readOpenClawLeaseIdFromRecord(record)
|
|
480
|
+
});
|
|
481
|
+
}
|
|
413
482
|
async cleanupProcessTreeForRecord(handle, record) {
|
|
414
483
|
const leaseId = readOpenClawLeaseIdFromRecord(record);
|
|
415
484
|
const rootPid = readAgentPidFromRecord(record);
|
|
@@ -475,7 +544,11 @@ var AcpxRuntime = class {
|
|
|
475
544
|
sessionKey: input.sessionKey,
|
|
476
545
|
command: stableLaunchCommand,
|
|
477
546
|
enabled: shouldStartWithLease,
|
|
478
|
-
run: () =>
|
|
547
|
+
run: () => this.withCodexWrapperDiagnostics({
|
|
548
|
+
command: stableLaunchCommand,
|
|
549
|
+
fallbackCode: "ACP_SESSION_INIT_FAILED",
|
|
550
|
+
run: () => delegate.ensureSession(input)
|
|
551
|
+
})
|
|
479
552
|
});
|
|
480
553
|
const normalizedInput = {
|
|
481
554
|
...input,
|
|
@@ -485,11 +558,39 @@ var AcpxRuntime = class {
|
|
|
485
558
|
sessionKey: input.sessionKey,
|
|
486
559
|
command: stableLaunchCommand,
|
|
487
560
|
enabled: shouldStartWithLease,
|
|
488
|
-
run: () => this.codexAcpModelOverrideScope.run(codexModelOverride, () =>
|
|
561
|
+
run: () => this.codexAcpModelOverrideScope.run(codexModelOverride, () => this.withCodexWrapperDiagnostics({
|
|
562
|
+
command: stableLaunchCommand,
|
|
563
|
+
fallbackCode: "ACP_SESSION_INIT_FAILED",
|
|
564
|
+
run: () => delegate.ensureSession(normalizedInput)
|
|
565
|
+
}))
|
|
489
566
|
});
|
|
490
567
|
}
|
|
491
568
|
async *runTurn(input) {
|
|
492
|
-
|
|
569
|
+
const command = await this.resolveCommandForHandle(input.handle);
|
|
570
|
+
const delegate = await this.resolveDelegateForHandle(input.handle);
|
|
571
|
+
try {
|
|
572
|
+
for await (const event of delegate.runTurn(input)) {
|
|
573
|
+
if (event.type !== "error" || !isCodexAcpCommand(command) || !isGenericInternalAcpErrorMessage(event.message)) {
|
|
574
|
+
yield event;
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
const stderrTail = await this.readCodexTurnFailureStderr({ handle: input.handle });
|
|
578
|
+
if (!stderrTail) {
|
|
579
|
+
yield event;
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
yield {
|
|
583
|
+
...event,
|
|
584
|
+
code: "ACP_TURN_FAILED",
|
|
585
|
+
message: `Internal error: ${stderrTail}`
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
} catch (error) {
|
|
589
|
+
if (!isCodexAcpCommand(command) || !isGenericInternalAcpError(error)) throw error;
|
|
590
|
+
const stderrTail = await this.readCodexTurnFailureStderr({ handle: input.handle });
|
|
591
|
+
if (!stderrTail) throw error;
|
|
592
|
+
throw new AcpRuntimeError("ACP_TURN_FAILED", `Internal error: ${stderrTail}`, { cause: error });
|
|
593
|
+
}
|
|
493
594
|
}
|
|
494
595
|
getCapabilities() {
|
|
495
596
|
return this.delegate.getCapabilities();
|
|
@@ -504,8 +605,9 @@ var AcpxRuntime = class {
|
|
|
504
605
|
const delegate = await this.resolveDelegateForHandle(input.handle);
|
|
505
606
|
const command = await this.resolveCommandForHandle(input.handle);
|
|
506
607
|
const key = input.key.trim().toLowerCase();
|
|
507
|
-
|
|
508
|
-
|
|
608
|
+
const isCodexAcp = isCodexAcpCommand(command);
|
|
609
|
+
if (WIRE_TIMEOUT_CONFIG_KEYS.has(key) && (isCodexAcp || isClaudeAcpCommand(command))) return;
|
|
610
|
+
if (isCodexAcp) {
|
|
509
611
|
if (key === "model" || key === "thinking" || key === "thought_level" || key === "reasoning_effort") {
|
|
510
612
|
const override = key === "model" ? normalizeCodexAcpModelOverride(input.value) : normalizeCodexAcpModelOverride(void 0, input.value);
|
|
511
613
|
if (!override && key !== "model") return;
|
|
@@ -553,6 +655,7 @@ const __testing = {
|
|
|
553
655
|
appendCodexAcpConfigOverrides,
|
|
554
656
|
assertSupportedRuntimeSessionMode,
|
|
555
657
|
codexAcpSessionModelId,
|
|
658
|
+
isClaudeAcpCommand,
|
|
556
659
|
isCodexAcpCommand,
|
|
557
660
|
normalizeCodexAcpModelOverride
|
|
558
661
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { registerAcpRuntimeBackend, unregisterAcpRuntimeBackend } from "./runtime-api.js";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as OPENCLAW_ACPX_LEASE_ID_ENV, c as createAcpxProcessLeaseStore, i as OPENCLAW_ACPX_LEASE_ID_ARG, o as OPENCLAW_GATEWAY_INSTANCE_ID_ARG, r as reapStaleOpenClawOwnedAcpxOrphans, t as cleanupOpenClawOwnedAcpxProcessTree } from "./process-reaper-biYb35VD.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
5
|
+
import fs from "node:fs/promises";
|
|
5
6
|
import path from "node:path";
|
|
6
7
|
import { randomUUID } from "node:crypto";
|
|
7
|
-
import fs from "node:fs/promises";
|
|
8
8
|
import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
|
|
9
9
|
import { inspect } from "node:util";
|
|
10
10
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
@@ -129,16 +129,85 @@ function extractTrustedCodexProjectPaths(configToml) {
|
|
|
129
129
|
}
|
|
130
130
|
return Array.from(trusted);
|
|
131
131
|
}
|
|
132
|
-
|
|
132
|
+
const INHERITED_TOP_LEVEL_CODEX_CONFIG_KEYS = new Set([
|
|
133
|
+
"model",
|
|
134
|
+
"model_provider",
|
|
135
|
+
"model_reasoning_effort",
|
|
136
|
+
"sandbox_mode"
|
|
137
|
+
]);
|
|
138
|
+
const INHERITED_MODEL_PROVIDER_CONFIG_KEYS = new Set([
|
|
139
|
+
"name",
|
|
140
|
+
"base_url",
|
|
141
|
+
"wire_api",
|
|
142
|
+
"env_key",
|
|
143
|
+
"env_key_instructions",
|
|
144
|
+
"requires_openai_auth",
|
|
145
|
+
"request_max_retries",
|
|
146
|
+
"stream_max_retries",
|
|
147
|
+
"stream_idle_timeout_ms"
|
|
148
|
+
]);
|
|
149
|
+
function parseTableHeader(line) {
|
|
150
|
+
const trimmed = line.trim();
|
|
151
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]") || trimmed.startsWith("[[")) return;
|
|
152
|
+
return parseTomlDottedKey(trimmed.slice(1, -1));
|
|
153
|
+
}
|
|
154
|
+
function isInheritedModelProviderTable(parts) {
|
|
155
|
+
return parts?.[0] === "model_providers" && parts.length === 2;
|
|
156
|
+
}
|
|
157
|
+
function parseTopLevelAssignmentKey(line) {
|
|
158
|
+
return /^(?<key>[A-Za-z0-9_-]+)\s*=\s*(?<value>.+)$/.exec(line)?.groups?.key;
|
|
159
|
+
}
|
|
160
|
+
function extractInheritedCodexRuntimeConfig(configToml) {
|
|
161
|
+
const inheritedLines = [];
|
|
162
|
+
let inAnyTable = false;
|
|
163
|
+
let inInheritedTable = false;
|
|
164
|
+
let pendingInheritedTableHeader = "";
|
|
165
|
+
function flushInheritedTableHeader() {
|
|
166
|
+
if (!pendingInheritedTableHeader) return;
|
|
167
|
+
if (inheritedLines.length > 0 && inheritedLines[inheritedLines.length - 1] !== "") inheritedLines.push("");
|
|
168
|
+
inheritedLines.push(pendingInheritedTableHeader);
|
|
169
|
+
pendingInheritedTableHeader = "";
|
|
170
|
+
}
|
|
171
|
+
for (const rawLine of configToml.split(/\r?\n/)) {
|
|
172
|
+
const trimmedLine = rawLine.trim();
|
|
173
|
+
const semanticLine = stripTomlComment(rawLine).trim();
|
|
174
|
+
if (trimmedLine.startsWith("[")) {
|
|
175
|
+
const tableParts = parseTableHeader(trimmedLine);
|
|
176
|
+
inAnyTable = true;
|
|
177
|
+
inInheritedTable = isInheritedModelProviderTable(tableParts);
|
|
178
|
+
if (inInheritedTable) pendingInheritedTableHeader = rawLine.trimEnd();
|
|
179
|
+
else pendingInheritedTableHeader = "";
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (inInheritedTable) {
|
|
183
|
+
if (!semanticLine) continue;
|
|
184
|
+
const key = parseTopLevelAssignmentKey(semanticLine);
|
|
185
|
+
if (!key || !INHERITED_MODEL_PROVIDER_CONFIG_KEYS.has(key)) continue;
|
|
186
|
+
flushInheritedTableHeader();
|
|
187
|
+
inheritedLines.push(rawLine.trimEnd());
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (inAnyTable) continue;
|
|
191
|
+
const key = parseTopLevelAssignmentKey(semanticLine);
|
|
192
|
+
if (!key) continue;
|
|
193
|
+
if (!INHERITED_TOP_LEVEL_CODEX_CONFIG_KEYS.has(key)) continue;
|
|
194
|
+
inheritedLines.push(rawLine.trimEnd());
|
|
195
|
+
}
|
|
196
|
+
while (inheritedLines.length > 0 && inheritedLines[inheritedLines.length - 1] === "") inheritedLines.pop();
|
|
197
|
+
return inheritedLines.join("\n");
|
|
198
|
+
}
|
|
199
|
+
function renderIsolatedCodexConfig(params) {
|
|
200
|
+
const normalized = Array.from(new Set(params.projectPaths.map((projectPath) => projectPath.trim()).filter(Boolean).map((projectPath) => path.resolve(projectPath)))).toSorted((left, right) => left.localeCompare(right));
|
|
133
201
|
return [
|
|
134
202
|
"# Generated by OpenClaw for Codex ACP sessions.",
|
|
135
|
-
|
|
203
|
+
params.sourceConfigToml ? extractInheritedCodexRuntimeConfig(params.sourceConfigToml) : "",
|
|
204
|
+
...normalized.flatMap((projectPath) => [
|
|
136
205
|
"",
|
|
137
206
|
`[projects.${JSON.stringify(projectPath)}]`,
|
|
138
207
|
"trust_level = \"trusted\""
|
|
139
208
|
]),
|
|
140
209
|
""
|
|
141
|
-
].join("\n");
|
|
210
|
+
].filter((line, index, lines) => !(line === "" && lines[index - 1] === "")).join("\n");
|
|
142
211
|
}
|
|
143
212
|
//#endregion
|
|
144
213
|
//#region extensions/acpx/src/config-schema.ts
|
|
@@ -436,19 +505,252 @@ async function resolveInstalledCodexAcpBinPath() {
|
|
|
436
505
|
async function resolveInstalledClaudeAcpBinPath() {
|
|
437
506
|
return await resolveInstalledAcpPackageBinPath(CLAUDE_ACP_PACKAGE, CLAUDE_ACP_BIN);
|
|
438
507
|
}
|
|
508
|
+
const DIAGNOSTIC_REDACTION_RULES = [
|
|
509
|
+
{
|
|
510
|
+
source: String.raw`(authorization\s*[:=]\s*bearer\s+)[^\s'"<>]+`,
|
|
511
|
+
flags: "gi",
|
|
512
|
+
replacement: "$1[REDACTED]"
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
source: String.raw`((?:api[_-]?key|apiKey|access[_-]?token|refresh[_-]?token|client[_-]?secret|token|secret|password|passwd|credential)\s*[:=]\s*)[^\s'"<>]+`,
|
|
516
|
+
flags: "gi",
|
|
517
|
+
replacement: "$1[REDACTED]"
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
source: String.raw`("(?:apiKey|token|secret|password|passwd|accessToken|refreshToken)"\s*:\s*")[^"]+`,
|
|
521
|
+
flags: "g",
|
|
522
|
+
replacement: "$1[REDACTED]"
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
source: String.raw`(["']?(?:api[-_]?key|apiKey|access[-_]?token|accessToken|refresh[-_]?token|refreshToken|id[-_]?token|idToken|auth[-_]?token|authToken|client[-_]?secret|clientSecret|app[-_]?secret|appSecret|token|secret|password|passwd|credential)["']?\s*[:=]\s*["']?)[^"',}\s<>]+`,
|
|
526
|
+
flags: "gi",
|
|
527
|
+
replacement: "$1[REDACTED]"
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
source: String.raw`([?&](?:access[-_]?token|auth[-_]?token|refresh[-_]?token|api[-_]?key|client[-_]?secret|token|key|secret|password|pass|passwd|auth|signature)=)[^&\s'"<>]+`,
|
|
531
|
+
flags: "gi",
|
|
532
|
+
replacement: "$1[REDACTED]"
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
source: String.raw`(--(?:api[-_]?key|token|secret|password|passwd)\s+)[^\s'"]+`,
|
|
536
|
+
flags: "gi",
|
|
537
|
+
replacement: "$1[REDACTED]"
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
source: String.raw`-----BEGIN [A-Z ]*PRI` + String.raw`VATE KEY-----[\s\S]+?-----END [A-Z ]*PRI` + String.raw`VATE KEY-----`,
|
|
541
|
+
flags: "g",
|
|
542
|
+
replacement: "[REDACTED_PRIVATE_KEY]"
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
source: String.raw`\b(sk-[A-Za-z0-9_-]{8,})\b`,
|
|
546
|
+
flags: "g",
|
|
547
|
+
replacement: "[REDACTED_OPENAI_KEY]"
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
source: String.raw`\b(gh[pousr]_[A-Za-z0-9_]{20,})\b`,
|
|
551
|
+
flags: "g",
|
|
552
|
+
replacement: "[REDACTED_GITHUB_TOKEN]"
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
source: String.raw`\b(github_pat_[A-Za-z0-9_]{20,})\b`,
|
|
556
|
+
flags: "g",
|
|
557
|
+
replacement: "[REDACTED_GITHUB_TOKEN]"
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
source: String.raw`\b(xox[baprs]-[A-Za-z0-9-]{10,})\b`,
|
|
561
|
+
flags: "g",
|
|
562
|
+
replacement: "[REDACTED_SLACK_TOKEN]"
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
source: String.raw`\b(gsk_[A-Za-z0-9_-]{10,})\b`,
|
|
566
|
+
flags: "g",
|
|
567
|
+
replacement: "[REDACTED_API_KEY]"
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
source: String.raw`\b(AIza[0-9A-Za-z\-_]{20,})\b`,
|
|
571
|
+
flags: "g",
|
|
572
|
+
replacement: "[REDACTED_GOOGLE_KEY]"
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
source: String.raw`\b(ya29\.[0-9A-Za-z_\-./+=]{10,})\b`,
|
|
576
|
+
flags: "g",
|
|
577
|
+
replacement: "[REDACTED_GOOGLE_TOKEN]"
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
source: String.raw`\b(eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,})\b`,
|
|
581
|
+
flags: "g",
|
|
582
|
+
replacement: "[REDACTED_JWT]"
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
source: String.raw`\b(pplx-[A-Za-z0-9_-]{10,})\b`,
|
|
586
|
+
flags: "g",
|
|
587
|
+
replacement: "[REDACTED_API_KEY]"
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
source: String.raw`\b(npm_[A-Za-z0-9]{10,})\b`,
|
|
591
|
+
flags: "g",
|
|
592
|
+
replacement: "[REDACTED_NPM_TOKEN]"
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
source: String.raw`\b(LTAI[A-Za-z0-9]{10,})\b`,
|
|
596
|
+
flags: "g",
|
|
597
|
+
replacement: "[REDACTED_ACCESS_KEY]"
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
source: String.raw`\b(hf_[A-Za-z0-9]{10,})\b`,
|
|
601
|
+
flags: "g",
|
|
602
|
+
replacement: "[REDACTED_API_KEY]"
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
source: String.raw`\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b`,
|
|
606
|
+
flags: "g",
|
|
607
|
+
replacement: "bot[REDACTED_TELEGRAM_TOKEN]"
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
source: String.raw`\b(\d{6,}:[A-Za-z0-9_-]{20,})\b`,
|
|
611
|
+
flags: "g",
|
|
612
|
+
replacement: "[REDACTED_TELEGRAM_TOKEN]"
|
|
613
|
+
}
|
|
614
|
+
];
|
|
615
|
+
function renderDiagnosticRedactionRuleSpecs() {
|
|
616
|
+
return JSON.stringify(DIAGNOSTIC_REDACTION_RULES);
|
|
617
|
+
}
|
|
439
618
|
function buildAdapterWrapperScript(params) {
|
|
440
619
|
return `#!/usr/bin/env node
|
|
441
|
-
import { existsSync } from "node:fs";
|
|
620
|
+
import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
442
621
|
import path from "node:path";
|
|
443
622
|
import { spawn } from "node:child_process";
|
|
444
623
|
import { fileURLToPath } from "node:url";
|
|
445
624
|
|
|
446
625
|
${params.envSetup}
|
|
626
|
+
const stderrLogFileNamePrefix = ${params.stderrLogFileNamePrefix ? JSON.stringify(params.stderrLogFileNamePrefix) : "undefined"};
|
|
627
|
+
const stderrLogMaxChars = 256 * 1024;
|
|
628
|
+
|
|
447
629
|
const openClawWrapperArgs = new Set([
|
|
448
630
|
${quoteCommandPart(OPENCLAW_ACPX_LEASE_ID_ARG)},
|
|
449
631
|
${quoteCommandPart(OPENCLAW_GATEWAY_INSTANCE_ID_ARG)},
|
|
450
632
|
]);
|
|
451
633
|
|
|
634
|
+
function readOpenClawWrapperArg(args, name) {
|
|
635
|
+
const index = args.indexOf(name);
|
|
636
|
+
if (index < 0) {
|
|
637
|
+
return undefined;
|
|
638
|
+
}
|
|
639
|
+
const value = args[index + 1];
|
|
640
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function safeDiagnosticFilePart(value) {
|
|
644
|
+
const sanitized = String(value || "").replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 120);
|
|
645
|
+
return sanitized || "pid-" + process.pid;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function resolveStderrLogPath(args) {
|
|
649
|
+
if (!stderrLogFileNamePrefix) {
|
|
650
|
+
return undefined;
|
|
651
|
+
}
|
|
652
|
+
const leaseId =
|
|
653
|
+
process.env[${JSON.stringify(OPENCLAW_ACPX_LEASE_ID_ENV)}] ||
|
|
654
|
+
readOpenClawWrapperArg(args, ${quoteCommandPart(OPENCLAW_ACPX_LEASE_ID_ARG)}) ||
|
|
655
|
+
"pid-" + process.pid;
|
|
656
|
+
const fileName = stderrLogFileNamePrefix + "." + safeDiagnosticFilePart(leaseId) + ".log";
|
|
657
|
+
return fileURLToPath(new URL("./" + fileName, import.meta.url));
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const diagnosticRedactionRules = ${renderDiagnosticRedactionRuleSpecs()}.map((rule) => [
|
|
661
|
+
new RegExp(rule.source, rule.flags),
|
|
662
|
+
rule.replacement,
|
|
663
|
+
]);
|
|
664
|
+
|
|
665
|
+
function redactDiagnosticText(text) {
|
|
666
|
+
let redacted = text;
|
|
667
|
+
for (const [pattern, replacement] of diagnosticRedactionRules) {
|
|
668
|
+
redacted = redacted.replace(pattern, replacement);
|
|
669
|
+
}
|
|
670
|
+
return redacted;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
let pendingStderrLogText = "";
|
|
674
|
+
const stderrPrivateKeyEndPattern = /-----END [A-Z ]*PRIVATE KEY-----/;
|
|
675
|
+
|
|
676
|
+
function hasUnclosedPrivateKeyBlock(text) {
|
|
677
|
+
let lastBeginIndex = -1;
|
|
678
|
+
for (const match of text.matchAll(/-----BEGIN [A-Z ]*PRIVATE KEY-----/g)) {
|
|
679
|
+
lastBeginIndex = match.index ?? lastBeginIndex;
|
|
680
|
+
}
|
|
681
|
+
if (lastBeginIndex === -1) {
|
|
682
|
+
return -1;
|
|
683
|
+
}
|
|
684
|
+
return stderrPrivateKeyEndPattern.test(text.slice(lastBeginIndex)) ? -1 : lastBeginIndex;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function writeRedactedStderrLog(text) {
|
|
688
|
+
if (!stderrLogPath) {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (!text) {
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
try {
|
|
695
|
+
appendFileSync(stderrLogPath, redactDiagnosticText(text), "utf8");
|
|
696
|
+
const current = readFileSync(stderrLogPath, "utf8");
|
|
697
|
+
if (current.length > stderrLogMaxChars) {
|
|
698
|
+
writeFileSync(stderrLogPath, current.slice(-stderrLogMaxChars), "utf8");
|
|
699
|
+
}
|
|
700
|
+
} catch {
|
|
701
|
+
// Stderr capture is diagnostic-only; never break the ACP adapter.
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function redactIncompletePrivateKeyTail(text) {
|
|
706
|
+
const unclosedPrivateKeyStart = hasUnclosedPrivateKeyBlock(text);
|
|
707
|
+
if (unclosedPrivateKeyStart === -1) {
|
|
708
|
+
return text;
|
|
709
|
+
}
|
|
710
|
+
return text.slice(0, unclosedPrivateKeyStart) + "[REDACTED_PRIVATE_KEY]";
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
function flushFinalizedStderrLogText() {
|
|
714
|
+
const lastLineBreak = pendingStderrLogText.lastIndexOf("\\n");
|
|
715
|
+
if (lastLineBreak === -1) {
|
|
716
|
+
if (pendingStderrLogText.length > stderrLogMaxChars) {
|
|
717
|
+
pendingStderrLogText = pendingStderrLogText.slice(-stderrLogMaxChars);
|
|
718
|
+
}
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
let flushEnd = lastLineBreak + 1;
|
|
722
|
+
const unclosedPrivateKeyStart = hasUnclosedPrivateKeyBlock(
|
|
723
|
+
pendingStderrLogText.slice(0, flushEnd),
|
|
724
|
+
);
|
|
725
|
+
if (unclosedPrivateKeyStart !== -1) {
|
|
726
|
+
flushEnd = unclosedPrivateKeyStart;
|
|
727
|
+
}
|
|
728
|
+
if (flushEnd <= 0) {
|
|
729
|
+
if (pendingStderrLogText.length > stderrLogMaxChars) {
|
|
730
|
+
pendingStderrLogText = pendingStderrLogText.slice(-stderrLogMaxChars);
|
|
731
|
+
}
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
const finalizedText = pendingStderrLogText.slice(0, flushEnd);
|
|
735
|
+
pendingStderrLogText = pendingStderrLogText.slice(flushEnd);
|
|
736
|
+
writeRedactedStderrLog(finalizedText);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function appendStderrLog(chunk) {
|
|
740
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
741
|
+
if (!text) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
pendingStderrLogText += text;
|
|
745
|
+
flushFinalizedStderrLogText();
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
function finishStderrLog() {
|
|
749
|
+
const text = redactIncompletePrivateKeyTail(pendingStderrLogText);
|
|
750
|
+
pendingStderrLogText = "";
|
|
751
|
+
writeRedactedStderrLog(text);
|
|
752
|
+
}
|
|
753
|
+
|
|
452
754
|
function stripOpenClawWrapperArgs(args) {
|
|
453
755
|
const stripped = [];
|
|
454
756
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -462,7 +764,18 @@ function stripOpenClawWrapperArgs(args) {
|
|
|
462
764
|
return stripped;
|
|
463
765
|
}
|
|
464
766
|
|
|
465
|
-
const
|
|
767
|
+
const rawConfiguredArgs = process.argv.slice(2);
|
|
768
|
+
const stderrLogPath = resolveStderrLogPath(rawConfiguredArgs);
|
|
769
|
+
|
|
770
|
+
try {
|
|
771
|
+
if (stderrLogPath) {
|
|
772
|
+
writeFileSync(stderrLogPath, "", "utf8");
|
|
773
|
+
}
|
|
774
|
+
} catch {
|
|
775
|
+
// Stderr capture is diagnostic-only; never break the ACP adapter.
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
const configuredArgs = stripOpenClawWrapperArgs(rawConfiguredArgs);
|
|
466
779
|
|
|
467
780
|
function resolveNpmCliPath() {
|
|
468
781
|
const candidate = path.resolve(
|
|
@@ -506,12 +819,18 @@ if (!command) {
|
|
|
506
819
|
const child = spawn(command, args, {
|
|
507
820
|
detached: process.platform !== "win32",
|
|
508
821
|
env,
|
|
509
|
-
stdio: "inherit",
|
|
822
|
+
stdio: ["inherit", "inherit", "pipe"],
|
|
510
823
|
windowsHide: true,
|
|
511
824
|
});
|
|
512
825
|
|
|
826
|
+
child.stderr?.on("data", (chunk) => {
|
|
827
|
+
appendStderrLog(chunk);
|
|
828
|
+
process.stderr.write(chunk);
|
|
829
|
+
});
|
|
830
|
+
|
|
513
831
|
let forceKillTimer;
|
|
514
832
|
let orphanCleanupStarted = false;
|
|
833
|
+
let childExitCode = 1;
|
|
515
834
|
|
|
516
835
|
function killChildTree(signal, options = {}) {
|
|
517
836
|
if (!child.pid || (!options.force && child.killed)) {
|
|
@@ -556,7 +875,7 @@ const parentWatcher =
|
|
|
556
875
|
// a forced fallback signal after SIGTERM.
|
|
557
876
|
forceKillTimer = setTimeout(() => {
|
|
558
877
|
killChildTree("SIGKILL", { force: true });
|
|
559
|
-
|
|
878
|
+
childExitCode = 1;
|
|
560
879
|
}, 1_500);
|
|
561
880
|
}, 1_000);
|
|
562
881
|
parentWatcher?.unref?.();
|
|
@@ -577,9 +896,15 @@ child.on("exit", (code, signal) => {
|
|
|
577
896
|
clearTimeout(forceKillTimer);
|
|
578
897
|
}
|
|
579
898
|
if (code !== null) {
|
|
580
|
-
|
|
899
|
+
childExitCode = code;
|
|
900
|
+
return;
|
|
581
901
|
}
|
|
582
|
-
|
|
902
|
+
childExitCode = signal ? 1 : 0;
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
child.on("close", () => {
|
|
906
|
+
finishStderrLog();
|
|
907
|
+
process.exit(childExitCode);
|
|
583
908
|
});
|
|
584
909
|
`;
|
|
585
910
|
}
|
|
@@ -589,6 +914,7 @@ function buildCodexAcpWrapperScript(installedBinPath) {
|
|
|
589
914
|
packageSpec: `${CODEX_ACP_PACKAGE}@${CODEX_ACP_PACKAGE_VERSION}`,
|
|
590
915
|
binName: CODEX_ACP_BIN,
|
|
591
916
|
installedBinPath,
|
|
917
|
+
stderrLogFileNamePrefix: "codex-acp-wrapper.stderr",
|
|
592
918
|
envSetup: `const codexHome = fileURLToPath(new URL("./codex-home/", import.meta.url));
|
|
593
919
|
const env = {
|
|
594
920
|
...process.env,
|
|
@@ -620,7 +946,10 @@ async function prepareIsolatedCodexHome(params) {
|
|
|
620
946
|
const trustedProjectPaths = [...sourceConfig ? extractTrustedCodexProjectPaths(sourceConfig) : [], params.workspaceDir];
|
|
621
947
|
const codexHome = path.join(params.baseDir, "codex-home");
|
|
622
948
|
await fs.mkdir(codexHome, { recursive: true });
|
|
623
|
-
await fs.writeFile(path.join(codexHome, "config.toml"),
|
|
949
|
+
await fs.writeFile(path.join(codexHome, "config.toml"), renderIsolatedCodexConfig({
|
|
950
|
+
sourceConfigToml: sourceConfig,
|
|
951
|
+
projectPaths: trustedProjectPaths
|
|
952
|
+
}), "utf8");
|
|
624
953
|
return codexHome;
|
|
625
954
|
}
|
|
626
955
|
async function makeGeneratedWrapperExecutableIfPossible(wrapperPath) {
|
|
@@ -724,7 +1053,7 @@ const SKIP_RUNTIME_PROBE_ENV = "OPENCLAW_SKIP_ACPX_RUNTIME_PROBE";
|
|
|
724
1053
|
const ACPX_BACKEND_ID = "acpx";
|
|
725
1054
|
let runtimeModulePromise = null;
|
|
726
1055
|
function loadRuntimeModule() {
|
|
727
|
-
runtimeModulePromise ??= import("./runtime-
|
|
1056
|
+
runtimeModulePromise ??= import("./runtime-HSzTNfOq.js");
|
|
728
1057
|
return runtimeModulePromise;
|
|
729
1058
|
}
|
|
730
1059
|
function createLazyDefaultRuntime(params) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/acpx",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.14-beta.2",
|
|
4
4
|
"description": "OpenClaw ACP runtime backend",
|
|
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.5.
|
|
29
|
+
"pluginApi": ">=2026.5.14-beta.2"
|
|
30
30
|
},
|
|
31
31
|
"build": {
|
|
32
|
-
"openclawVersion": "2026.5.
|
|
32
|
+
"openclawVersion": "2026.5.14-beta.2",
|
|
33
33
|
"staticAssets": [
|
|
34
34
|
{
|
|
35
35
|
"source": "./src/runtime-internals/mcp-proxy.mjs",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"skills/**"
|
|
60
60
|
],
|
|
61
61
|
"peerDependencies": {
|
|
62
|
-
"openclaw": ">=2026.5.
|
|
62
|
+
"openclaw": ">=2026.5.14-beta.2"
|
|
63
63
|
},
|
|
64
64
|
"peerDependenciesMeta": {
|
|
65
65
|
"openclaw": {
|