@openclaw/acpx 2026.7.2-beta.5 → 2026.8.1-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.
- package/dist/{config-schema-lrk5nlcV.js → config-schema-DN_uAi4R.js} +0 -2
- package/dist/doctor-contract-api.js +36 -4
- package/dist/index.js +21 -1248
- package/dist/pi-session-catalog-runtime-BiTQtT7s.js +1220 -0
- package/dist/pi-session-paths-DgYt8LUg.js +81 -0
- package/dist/{process-lease-CU1cFI4I.js → process-lease-Cwvj7WGe.js} +66 -2
- package/dist/{process-reaper-BIOtp_Ek.js → process-reaper-DzVuCxl3.js} +68 -62
- package/dist/{register.runtime-Dajn8myc.js → register.runtime-C29AY8LI.js} +75 -30
- package/dist/register.runtime.js +1 -1
- package/dist/{runtime-CN4JQkdV.js → runtime-BmzHUTK8.js} +329 -107
- package/dist/{service-DOsKw_R1.js → service-LTeZyc7q.js} +38 -45
- package/openclaw.plugin.json +5 -17
- package/package.json +7 -7
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { isRecord, normalizeBoundedOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
2
|
+
import { readFileSync, statSync } from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
//#region extensions/acpx/src/pi-session-catalog-shared.ts
|
|
6
|
+
const PI_SESSIONS_LIST_COMMAND = "acpx.pi.sessions.list.v1";
|
|
7
|
+
const PI_SESSION_READ_COMMAND = "acpx.pi.sessions.read.v1";
|
|
8
|
+
const PI_TERMINAL_RESUME_COMMAND = "acpx.pi.terminal.resume.v1";
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region extensions/acpx/src/pi-session-paths.ts
|
|
11
|
+
function piHome(env) {
|
|
12
|
+
return (process.platform === "win32" ? env.USERPROFILE?.trim() : env.HOME?.trim()) || os.homedir();
|
|
13
|
+
}
|
|
14
|
+
function isPiSessionCatalogPathAbsolute(value, platform = process.platform) {
|
|
15
|
+
if (platform !== "win32") return path.posix.isAbsolute(value);
|
|
16
|
+
const root = path.win32.parse(value).root;
|
|
17
|
+
return path.win32.isAbsolute(value) && root !== "\\" && root !== "/";
|
|
18
|
+
}
|
|
19
|
+
function resolveConfiguredPath(value, env, relativeBase) {
|
|
20
|
+
const home = piHome(env);
|
|
21
|
+
let resolved = value;
|
|
22
|
+
if (value === "~") resolved = home;
|
|
23
|
+
if (value.startsWith("~/") || value.startsWith("~\\")) resolved = path.join(home, value.slice(2));
|
|
24
|
+
if (!isPiSessionCatalogPathAbsolute(resolved)) {
|
|
25
|
+
if (relativeBase) return path.resolve(relativeBase, resolved);
|
|
26
|
+
throw new Error("Pi session catalog requires absolute or home-relative storage paths");
|
|
27
|
+
}
|
|
28
|
+
return path.resolve(resolved);
|
|
29
|
+
}
|
|
30
|
+
function settingsSessionDir(file) {
|
|
31
|
+
try {
|
|
32
|
+
const value = JSON.parse(readFileSync(file, "utf8"));
|
|
33
|
+
return isRecord(value) ? normalizeBoundedOptionalString(value.sessionDir, 4096) : void 0;
|
|
34
|
+
} catch {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function piSessionStore(env, cwd = process.cwd()) {
|
|
39
|
+
const customSessionDir = env.PI_CODING_AGENT_SESSION_DIR?.trim();
|
|
40
|
+
if (customSessionDir) return {
|
|
41
|
+
root: resolveConfiguredPath(customSessionDir, env),
|
|
42
|
+
flat: true,
|
|
43
|
+
usesProcessHomeFallback: false
|
|
44
|
+
};
|
|
45
|
+
const home = piHome(env);
|
|
46
|
+
const customAgentDir = env.PI_CODING_AGENT_DIR?.trim();
|
|
47
|
+
const agentDir = customAgentDir ? resolveConfiguredPath(customAgentDir, env) : path.join(home, ".pi", "agent");
|
|
48
|
+
const projectSessionDir = settingsSessionDir(path.join(cwd, ".pi", "settings.json"));
|
|
49
|
+
if (projectSessionDir) return {
|
|
50
|
+
root: resolveConfiguredPath(projectSessionDir, env, path.join(cwd, ".pi")),
|
|
51
|
+
flat: true,
|
|
52
|
+
usesProcessHomeFallback: false
|
|
53
|
+
};
|
|
54
|
+
const globalSessionDir = settingsSessionDir(path.join(agentDir, "settings.json"));
|
|
55
|
+
if (globalSessionDir) return {
|
|
56
|
+
root: resolveConfiguredPath(globalSessionDir, env, agentDir),
|
|
57
|
+
flat: true,
|
|
58
|
+
usesProcessHomeFallback: false
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
root: path.join(agentDir, "sessions"),
|
|
62
|
+
flat: false,
|
|
63
|
+
usesProcessHomeFallback: !customAgentDir
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Store root scanned by pi-acp@0.0.26 when resolving a native session id. */
|
|
67
|
+
function piAcpSessionStoreRoot(env) {
|
|
68
|
+
const configuredAgentDir = env.PI_CODING_AGENT_DIR?.trim();
|
|
69
|
+
if (configuredAgentDir && !isPiSessionCatalogPathAbsolute(configuredAgentDir)) return;
|
|
70
|
+
const agentDir = configuredAgentDir ? path.resolve(configuredAgentDir) : path.join(piHome(env), ".pi", "agent");
|
|
71
|
+
return path.join(agentDir, "sessions");
|
|
72
|
+
}
|
|
73
|
+
function piSessionStoreAvailable(env, store) {
|
|
74
|
+
try {
|
|
75
|
+
return statSync((store ?? piSessionStore(env)).root).isDirectory();
|
|
76
|
+
} catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { PI_SESSION_READ_COMMAND as a, PI_SESSIONS_LIST_COMMAND as i, piSessionStore as n, PI_TERMINAL_RESUME_COMMAND as o, piSessionStoreAvailable as r, piAcpSessionStoreRoot as t };
|
|
@@ -1,4 +1,52 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
//#region extensions/acpx/src/command-line.ts
|
|
3
|
+
/**
|
|
4
|
+
* Small shell-command helpers for ACPX-launched processes. Splitting supports
|
|
5
|
+
* simple quoted command strings from config without invoking a shell parser.
|
|
6
|
+
*/
|
|
7
|
+
/** Quote one command argument for display or config serialization. */
|
|
8
|
+
function quoteCommandPart(value) {
|
|
9
|
+
return JSON.stringify(value);
|
|
10
|
+
}
|
|
11
|
+
/** Split a command string into argv-like parts using simple quote/backslash rules. */
|
|
12
|
+
function splitCommandParts(value) {
|
|
13
|
+
const parts = [];
|
|
14
|
+
let current = "";
|
|
15
|
+
let quote = null;
|
|
16
|
+
let escaping = false;
|
|
17
|
+
for (const ch of value) {
|
|
18
|
+
if (escaping) {
|
|
19
|
+
current += ch;
|
|
20
|
+
escaping = false;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (ch === "\\" && quote !== "'") {
|
|
24
|
+
escaping = true;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (quote) {
|
|
28
|
+
if (ch === quote) quote = null;
|
|
29
|
+
else current += ch;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (ch === "'" || ch === "\"") {
|
|
33
|
+
quote = ch;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (/\s/.test(ch)) {
|
|
37
|
+
if (current) {
|
|
38
|
+
parts.push(current);
|
|
39
|
+
current = "";
|
|
40
|
+
}
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
current += ch;
|
|
44
|
+
}
|
|
45
|
+
if (escaping) current += "\\";
|
|
46
|
+
if (current) parts.push(current);
|
|
47
|
+
return parts;
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
2
50
|
//#region extensions/acpx/src/state.ts
|
|
3
51
|
const ACPX_PROCESS_LEASE_NAMESPACE = "process-leases";
|
|
4
52
|
const ACPX_PROCESS_LEASE_MAX_ENTRIES = 4096;
|
|
@@ -26,6 +74,21 @@ function normalizeAcpxGatewayInstanceRecord(value) {
|
|
|
26
74
|
const OPENCLAW_ACPX_LEASE_ID_ARG = "--openclaw-acpx-lease-id";
|
|
27
75
|
/** CLI argument carrying the owning gateway instance id. */
|
|
28
76
|
const OPENCLAW_GATEWAY_INSTANCE_ID_ARG = "--openclaw-gateway-instance-id";
|
|
77
|
+
/** Synthetic session identity for generated-wrapper health probes. */
|
|
78
|
+
const ACPX_PROBE_LEASE_SESSION_KEY = "openclaw:acpx:probe";
|
|
79
|
+
/** Read OpenClaw lease identity from a generated wrapper command. */
|
|
80
|
+
function readAcpxProcessLeaseIdentity(command) {
|
|
81
|
+
const parts = splitCommandParts(command?.trim() ?? "");
|
|
82
|
+
const leaseIndex = parts.lastIndexOf(OPENCLAW_ACPX_LEASE_ID_ARG);
|
|
83
|
+
const gatewayIndex = parts.lastIndexOf(OPENCLAW_GATEWAY_INSTANCE_ID_ARG);
|
|
84
|
+
const leaseId = leaseIndex >= 0 ? parts[leaseIndex + 1]?.trim() : "";
|
|
85
|
+
const gatewayInstanceId = gatewayIndex >= 0 ? parts[gatewayIndex + 1]?.trim() : "";
|
|
86
|
+
if (!leaseId || !gatewayInstanceId) return;
|
|
87
|
+
return {
|
|
88
|
+
leaseId,
|
|
89
|
+
gatewayInstanceId
|
|
90
|
+
};
|
|
91
|
+
}
|
|
29
92
|
function normalizeAcpxProcessLease(value) {
|
|
30
93
|
if (typeof value !== "object" || value === null) return;
|
|
31
94
|
const record = value;
|
|
@@ -58,7 +121,8 @@ function normalizeAcpxProcessLeaseFile(value) {
|
|
|
58
121
|
function openAcpxProcessLeaseStateStore(openKeyedStore) {
|
|
59
122
|
return openKeyedStore({
|
|
60
123
|
namespace: ACPX_PROCESS_LEASE_NAMESPACE,
|
|
61
|
-
maxEntries: ACPX_PROCESS_LEASE_MAX_ENTRIES
|
|
124
|
+
maxEntries: ACPX_PROCESS_LEASE_MAX_ENTRIES,
|
|
125
|
+
overflowPolicy: "reject-new"
|
|
62
126
|
});
|
|
63
127
|
}
|
|
64
128
|
/** Create a serialized SQLite-backed ACPX process lease store. */
|
|
@@ -128,4 +192,4 @@ function withAcpxLeaseEnvironment(params) {
|
|
|
128
192
|
return appendAcpxLeaseArgs(params);
|
|
129
193
|
}
|
|
130
194
|
//#endregion
|
|
131
|
-
export {
|
|
195
|
+
export { quoteCommandPart as _, createAcpxProcessLeaseStore as a, normalizeAcpxProcessLeaseFile as c, withAcpxLeaseEnvironment as d, ACPX_GATEWAY_INSTANCE_KEY as f, normalizeAcpxGatewayInstanceRecord as g, ACPX_LEGACY_PROCESS_LEASE_FILE as h, createAcpxProcessLeaseId as i, openAcpxProcessLeaseStateStore as l, ACPX_LEGACY_GATEWAY_INSTANCE_FILE as m, OPENCLAW_ACPX_LEASE_ID_ARG as n, hashAcpxProcessCommand as o, ACPX_GATEWAY_INSTANCE_NAMESPACE as p, OPENCLAW_GATEWAY_INSTANCE_ID_ARG as r, normalizeAcpxProcessLease as s, ACPX_PROBE_LEASE_SESSION_KEY as t, readAcpxProcessLeaseIdentity as u, splitCommandParts as v };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as AcpxPluginConfigSchema } from "./config-schema-
|
|
2
|
-
import "./process-lease-
|
|
1
|
+
import { t as AcpxPluginConfigSchema } from "./config-schema-DN_uAi4R.js";
|
|
2
|
+
import { u as readAcpxProcessLeaseIdentity, v as splitCommandParts } from "./process-lease-Cwvj7WGe.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { formatPluginConfigIssue } from "openclaw/plugin-sdk/extension-shared";
|
|
5
5
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
@@ -13,54 +13,6 @@ const CODEX_ACP_BIN = "codex-acp";
|
|
|
13
13
|
const LEGACY_CODEX_ACP_PACKAGE = "@zed-industries/codex-acp";
|
|
14
14
|
const OPENCLAW_CODEX_CONFIG_ARG = "--openclaw-codex-config";
|
|
15
15
|
//#endregion
|
|
16
|
-
//#region extensions/acpx/src/command-line.ts
|
|
17
|
-
/**
|
|
18
|
-
* Small shell-command helpers for ACPX-launched processes. Splitting supports
|
|
19
|
-
* simple quoted command strings from config without invoking a shell parser.
|
|
20
|
-
*/
|
|
21
|
-
/** Quote one command argument for display or config serialization. */
|
|
22
|
-
function quoteCommandPart(value) {
|
|
23
|
-
return JSON.stringify(value);
|
|
24
|
-
}
|
|
25
|
-
/** Split a command string into argv-like parts using simple quote/backslash rules. */
|
|
26
|
-
function splitCommandParts(value) {
|
|
27
|
-
const parts = [];
|
|
28
|
-
let current = "";
|
|
29
|
-
let quote = null;
|
|
30
|
-
let escaping = false;
|
|
31
|
-
for (const ch of value) {
|
|
32
|
-
if (escaping) {
|
|
33
|
-
current += ch;
|
|
34
|
-
escaping = false;
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
if (ch === "\\" && quote !== "'") {
|
|
38
|
-
escaping = true;
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
if (quote) {
|
|
42
|
-
if (ch === quote) quote = null;
|
|
43
|
-
else current += ch;
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
if (ch === "'" || ch === "\"") {
|
|
47
|
-
quote = ch;
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
if (/\s/.test(ch)) {
|
|
51
|
-
if (current) {
|
|
52
|
-
parts.push(current);
|
|
53
|
-
current = "";
|
|
54
|
-
}
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
current += ch;
|
|
58
|
-
}
|
|
59
|
-
if (escaping) current += "\\";
|
|
60
|
-
if (current) parts.push(current);
|
|
61
|
-
return parts;
|
|
62
|
-
}
|
|
63
|
-
//#endregion
|
|
64
16
|
//#region extensions/acpx/src/config.ts
|
|
65
17
|
/**
|
|
66
18
|
* Resolves ACPX plugin config from raw user configuration. It locates the
|
|
@@ -113,8 +65,6 @@ function resolveAcpxPluginRoot(moduleUrl = import.meta.url) {
|
|
|
113
65
|
}
|
|
114
66
|
const DEFAULT_PERMISSION_MODE = "approve-reads";
|
|
115
67
|
const DEFAULT_NON_INTERACTIVE_POLICY = "fail";
|
|
116
|
-
const DEFAULT_QUEUE_OWNER_TTL_SECONDS = .1;
|
|
117
|
-
const DEFAULT_STRICT_WINDOWS_CMD_WRAPPER = true;
|
|
118
68
|
function parseAcpxPluginConfig(value) {
|
|
119
69
|
if (value === void 0) return {
|
|
120
70
|
ok: true,
|
|
@@ -234,13 +184,7 @@ function resolveAcpxPluginConfig(params) {
|
|
|
234
184
|
nonInteractivePermissions: normalized.nonInteractivePermissions ?? DEFAULT_NON_INTERACTIVE_POLICY,
|
|
235
185
|
pluginToolsMcpBridge,
|
|
236
186
|
openClawToolsMcpBridge,
|
|
237
|
-
strictWindowsCmdWrapper: normalized.strictWindowsCmdWrapper ?? DEFAULT_STRICT_WINDOWS_CMD_WRAPPER,
|
|
238
187
|
timeoutSeconds: normalized.timeoutSeconds ?? 120,
|
|
239
|
-
queueOwnerTtlSeconds: normalized.queueOwnerTtlSeconds ?? DEFAULT_QUEUE_OWNER_TTL_SECONDS,
|
|
240
|
-
legacyCompatibilityConfig: {
|
|
241
|
-
strictWindowsCmdWrapper: normalized.strictWindowsCmdWrapper,
|
|
242
|
-
queueOwnerTtlSeconds: normalized.queueOwnerTtlSeconds
|
|
243
|
-
},
|
|
244
188
|
mcpServers,
|
|
245
189
|
agents
|
|
246
190
|
};
|
|
@@ -320,6 +264,15 @@ function commandWrapperBelongsToRoot(command, wrapperRoot) {
|
|
|
320
264
|
const normalizedRoot = normalizePathLike(wrapperRoot).replace(/\/+$/, "");
|
|
321
265
|
return Array.from(GENERATED_WRAPPER_BASENAMES).some((basename) => normalizedCommand.includes(`${normalizedRoot}/${basename}`));
|
|
322
266
|
}
|
|
267
|
+
function commandContainsExactWrapperPath(command, wrapperPath) {
|
|
268
|
+
const expectedPath = normalizePathLike(wrapperPath);
|
|
269
|
+
return splitCommandParts(command).some((part) => normalizePathLike(part) === expectedPath);
|
|
270
|
+
}
|
|
271
|
+
function wrapperPathBelongsToRoot(wrapperPath, wrapperRoot) {
|
|
272
|
+
const normalizedPath = normalizePathLike(wrapperPath);
|
|
273
|
+
const normalizedRoot = normalizePathLike(wrapperRoot).replace(/\/+$/, "");
|
|
274
|
+
return GENERATED_WRAPPER_BASENAMES.has(path.posix.basename(normalizedPath)) && normalizedPath.startsWith(`${normalizedRoot}/`);
|
|
275
|
+
}
|
|
323
276
|
/** Check whether a command references an OpenClaw-generated ACPX wrapper path. */
|
|
324
277
|
function isOpenClawLeaseAwareAcpxProcessCommand(params) {
|
|
325
278
|
const command = params.command?.trim();
|
|
@@ -435,11 +388,20 @@ async function cleanupOpenClawOwnedAcpxProcessTree(params) {
|
|
|
435
388
|
terminatedPids: [],
|
|
436
389
|
skippedReason: "missing-root"
|
|
437
390
|
};
|
|
391
|
+
if ((params.deps?.platform ?? process.platform) === "win32") return {
|
|
392
|
+
inspectedPids: [],
|
|
393
|
+
terminatedPids: [],
|
|
394
|
+
skippedReason: "unsupported-platform"
|
|
395
|
+
};
|
|
438
396
|
let processes;
|
|
439
397
|
try {
|
|
440
398
|
processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
|
|
441
399
|
} catch {
|
|
442
|
-
|
|
400
|
+
return {
|
|
401
|
+
inspectedPids: [],
|
|
402
|
+
terminatedPids: [],
|
|
403
|
+
skippedReason: "process-list-unavailable"
|
|
404
|
+
};
|
|
443
405
|
}
|
|
444
406
|
const listedTree = collectProcessTree(processes, rootPid);
|
|
445
407
|
if (listedTree.length === 0) return {
|
|
@@ -483,9 +445,53 @@ async function cleanupOpenClawOwnedAcpxProcessTree(params) {
|
|
|
483
445
|
terminatedPids: await terminatePids(pids, params.deps)
|
|
484
446
|
};
|
|
485
447
|
}
|
|
448
|
+
/** Recover a pending lease by matching its exact live wrapper identity. */
|
|
449
|
+
async function cleanupOpenClawOwnedAcpxPendingLease(params) {
|
|
450
|
+
if ((params.deps?.platform ?? process.platform) === "win32") return {
|
|
451
|
+
inspectedPids: [],
|
|
452
|
+
terminatedPids: [],
|
|
453
|
+
skippedReason: "unsupported-platform"
|
|
454
|
+
};
|
|
455
|
+
if (!params.wrapperPath || !wrapperPathBelongsToRoot(params.wrapperPath, params.wrapperRoot)) return {
|
|
456
|
+
inspectedPids: [],
|
|
457
|
+
terminatedPids: [],
|
|
458
|
+
skippedReason: "unverified-root"
|
|
459
|
+
};
|
|
460
|
+
let processes;
|
|
461
|
+
try {
|
|
462
|
+
processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
|
|
463
|
+
} catch {
|
|
464
|
+
return {
|
|
465
|
+
inspectedPids: [],
|
|
466
|
+
terminatedPids: [],
|
|
467
|
+
skippedReason: "process-list-unavailable"
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
const matchingRoots = processes.filter((processInfo) => commandContainsExactWrapperPath(processInfo.command, params.wrapperPath) && liveCommandMatchesLeaseIdentity({
|
|
471
|
+
command: processInfo.command,
|
|
472
|
+
expectedLeaseId: params.leaseId,
|
|
473
|
+
expectedGatewayInstanceId: params.gatewayInstanceId
|
|
474
|
+
}));
|
|
475
|
+
if (matchingRoots.length === 0) return {
|
|
476
|
+
inspectedPids: [],
|
|
477
|
+
terminatedPids: [],
|
|
478
|
+
skippedReason: "missing-root"
|
|
479
|
+
};
|
|
480
|
+
if (matchingRoots.length > 1) return {
|
|
481
|
+
inspectedPids: uniquePids(matchingRoots),
|
|
482
|
+
terminatedPids: [],
|
|
483
|
+
skippedReason: "ambiguous-root"
|
|
484
|
+
};
|
|
485
|
+
const listedTree = collectProcessTree(processes, matchingRoots[0].pid);
|
|
486
|
+
const pids = uniquePids(listedTree.toReversed());
|
|
487
|
+
return {
|
|
488
|
+
inspectedPids: uniquePids(listedTree),
|
|
489
|
+
terminatedPids: await terminatePids(pids, params.deps)
|
|
490
|
+
};
|
|
491
|
+
}
|
|
486
492
|
/** Reap orphaned OpenClaw-owned ACPX wrapper trees during runtime startup. */
|
|
487
493
|
async function reapStaleOpenClawOwnedAcpxOrphans(params) {
|
|
488
|
-
if (process.platform === "win32") return {
|
|
494
|
+
if ((params.deps?.platform ?? process.platform) === "win32") return {
|
|
489
495
|
inspectedPids: [],
|
|
490
496
|
terminatedPids: [],
|
|
491
497
|
skippedReason: "unsupported-platform"
|
|
@@ -500,7 +506,7 @@ async function reapStaleOpenClawOwnedAcpxOrphans(params) {
|
|
|
500
506
|
skippedReason: "process-list-unavailable"
|
|
501
507
|
};
|
|
502
508
|
}
|
|
503
|
-
const orphanTrees = processes.filter((processInfo) => processInfo.ppid === 1 && isOpenClawOwnedAcpxProcessCommand({
|
|
509
|
+
const orphanTrees = processes.filter((processInfo) => processInfo.ppid === 1 && !readAcpxProcessLeaseIdentity(processInfo.command) && isOpenClawOwnedAcpxProcessCommand({
|
|
504
510
|
command: processInfo.command,
|
|
505
511
|
wrapperRoot: params.wrapperRoot
|
|
506
512
|
})).map((orphan) => collectProcessTree(processes, orphan.pid));
|
|
@@ -510,4 +516,4 @@ async function reapStaleOpenClawOwnedAcpxOrphans(params) {
|
|
|
510
516
|
};
|
|
511
517
|
}
|
|
512
518
|
//#endregion
|
|
513
|
-
export {
|
|
519
|
+
export { resolveAcpxPluginConfig as a, CODEX_ACP_BIN as c, OPENCLAW_CODEX_CONFIG_ARG as d, reapStaleOpenClawOwnedAcpxOrphans as i, CODEX_ACP_PACKAGE as l, cleanupOpenClawOwnedAcpxProcessTree as n, resolveAcpxPluginRoot as o, isOpenClawLeaseAwareAcpxProcessCommand as r, toAcpMcpServers as s, cleanupOpenClawOwnedAcpxPendingLease as t, LEGACY_CODEX_ACP_PACKAGE as u };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getAcpRuntimeBackend, registerAcpRuntimeBackend, unregisterAcpRuntimeBackend } from "openclaw/plugin-sdk/acp-runtime-backend";
|
|
2
2
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
3
|
+
import { toErrorObject } from "openclaw/plugin-sdk/error-runtime";
|
|
3
4
|
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
|
4
5
|
//#region extensions/acpx/src/runtime-turn.ts
|
|
5
6
|
/**
|
|
@@ -41,7 +42,7 @@ var LegacyRunTurnEventQueue = class {
|
|
|
41
42
|
async next() {
|
|
42
43
|
const item = this.items.shift();
|
|
43
44
|
if (item) return item;
|
|
44
|
-
if (this.error) throw
|
|
45
|
+
if (this.error) throw toErrorObject(this.error, "Non-Error thrown");
|
|
45
46
|
if (this.closed) return null;
|
|
46
47
|
return await new Promise((resolve, reject) => {
|
|
47
48
|
this.waits.push({
|
|
@@ -143,13 +144,6 @@ function lazyStartRuntimeTurn(resolveRuntime, input) {
|
|
|
143
144
|
}
|
|
144
145
|
};
|
|
145
146
|
}
|
|
146
|
-
function toLintErrorObject(value, fallbackMessage) {
|
|
147
|
-
if (value instanceof Error) return value;
|
|
148
|
-
if (typeof value === "string") return new Error(value);
|
|
149
|
-
const error = new Error(fallbackMessage, { cause: value });
|
|
150
|
-
if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
|
|
151
|
-
return error;
|
|
152
|
-
}
|
|
153
147
|
//#endregion
|
|
154
148
|
//#region extensions/acpx/src/runtime-proxy.ts
|
|
155
149
|
/** Create an ACP runtime facade backed by an async runtime resolver. */
|
|
@@ -200,40 +194,69 @@ function createLazyAcpRuntimeProxy(resolveRuntime) {
|
|
|
200
194
|
* immediately, then imports the heavier service only when a session needs it.
|
|
201
195
|
*/
|
|
202
196
|
const ACPX_BACKEND_ID = "acpx";
|
|
203
|
-
const loadServiceModule = createLazyRuntimeModule(() => import("./service-
|
|
204
|
-
|
|
197
|
+
const loadServiceModule = createLazyRuntimeModule(() => import("./service-LTeZyc7q.js"));
|
|
198
|
+
function unregisterOwnedRuntime(runtime) {
|
|
199
|
+
if (runtime && getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime === runtime) unregisterAcpRuntimeBackend(ACPX_BACKEND_ID);
|
|
200
|
+
}
|
|
201
|
+
async function startRealService(state, lifecycleRevision, deferredRuntime) {
|
|
202
|
+
if (state.lifecycleRevision !== lifecycleRevision || !state.ctx) throw new Error("ACPX runtime service is not started");
|
|
205
203
|
if (state.realRuntime) return state.realRuntime;
|
|
206
|
-
if (
|
|
207
|
-
|
|
204
|
+
if (state.startPromise) return await state.startPromise;
|
|
205
|
+
const ctx = state.ctx;
|
|
206
|
+
state.startPromise = (async () => {
|
|
207
|
+
let publishedRuntime = null;
|
|
208
208
|
const { createAcpxRuntimeService: createAcpxRuntimeServiceLocal } = await loadServiceModule();
|
|
209
|
-
const service = createAcpxRuntimeServiceLocal(
|
|
209
|
+
const service = createAcpxRuntimeServiceLocal({
|
|
210
|
+
...state.params,
|
|
211
|
+
backendLifecycle: {
|
|
212
|
+
publish(backend) {
|
|
213
|
+
if (state.lifecycleRevision !== lifecycleRevision || state.ctx !== ctx) throw new Error("ACPX runtime service stopped during activation");
|
|
214
|
+
if (getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime !== deferredRuntime) throw new Error("ACPX runtime service lost registry ownership during activation");
|
|
215
|
+
registerAcpRuntimeBackend({
|
|
216
|
+
id: ACPX_BACKEND_ID,
|
|
217
|
+
...backend
|
|
218
|
+
});
|
|
219
|
+
publishedRuntime = backend.runtime;
|
|
220
|
+
state.ownedRuntime = backend.runtime;
|
|
221
|
+
},
|
|
222
|
+
retract(runtime) {
|
|
223
|
+
unregisterOwnedRuntime(runtime);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
});
|
|
210
227
|
state.realService = service;
|
|
211
|
-
await service.start(
|
|
212
|
-
|
|
213
|
-
if (!
|
|
214
|
-
|
|
215
|
-
|
|
228
|
+
await service.start(ctx);
|
|
229
|
+
if (state.lifecycleRevision !== lifecycleRevision || state.ctx !== ctx) throw new Error("ACPX runtime service stopped during activation");
|
|
230
|
+
if (!publishedRuntime) throw new Error("ACPX runtime service did not register an ACP backend");
|
|
231
|
+
if (getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime !== publishedRuntime) throw new Error("ACPX runtime service lost registry ownership during activation");
|
|
232
|
+
state.realRuntime = publishedRuntime;
|
|
233
|
+
return publishedRuntime;
|
|
216
234
|
})();
|
|
217
235
|
try {
|
|
218
236
|
return await state.startPromise;
|
|
219
237
|
} catch (error) {
|
|
220
|
-
state.
|
|
221
|
-
|
|
238
|
+
if (state.lifecycleRevision === lifecycleRevision) {
|
|
239
|
+
state.startPromise = null;
|
|
240
|
+
state.realService = null;
|
|
241
|
+
}
|
|
222
242
|
throw error;
|
|
223
243
|
}
|
|
224
244
|
}
|
|
225
|
-
function createDeferredRuntime(state) {
|
|
226
|
-
const
|
|
227
|
-
return
|
|
245
|
+
function createDeferredRuntime(state, lifecycleRevision) {
|
|
246
|
+
const deferredRuntime = createLazyAcpRuntimeProxy(() => startRealService(state, lifecycleRevision, deferredRuntime));
|
|
247
|
+
return deferredRuntime;
|
|
228
248
|
}
|
|
229
249
|
/** Creates the plugin service that registers ACPX as an ACP runtime backend. */
|
|
230
250
|
function createAcpxRuntimeService(params = {}) {
|
|
231
251
|
const state = {
|
|
232
252
|
ctx: null,
|
|
253
|
+
lifecycleRevision: 0,
|
|
254
|
+
ownedRuntime: null,
|
|
233
255
|
params,
|
|
234
256
|
realRuntime: null,
|
|
235
257
|
realService: null,
|
|
236
|
-
startPromise: null
|
|
258
|
+
startPromise: null,
|
|
259
|
+
stopPromise: null
|
|
237
260
|
};
|
|
238
261
|
return {
|
|
239
262
|
id: "acpx-runtime",
|
|
@@ -242,20 +265,42 @@ function createAcpxRuntimeService(params = {}) {
|
|
|
242
265
|
ctx.logger.info("skipping embedded acpx runtime backend (OPENCLAW_SKIP_ACPX_RUNTIME=1)");
|
|
243
266
|
return;
|
|
244
267
|
}
|
|
268
|
+
if (state.stopPromise) await state.stopPromise;
|
|
269
|
+
state.lifecycleRevision += 1;
|
|
270
|
+
const lifecycleRevision = state.lifecycleRevision;
|
|
245
271
|
state.ctx = ctx;
|
|
272
|
+
const deferredRuntime = createDeferredRuntime(state, lifecycleRevision);
|
|
273
|
+
state.ownedRuntime = deferredRuntime;
|
|
246
274
|
registerAcpRuntimeBackend({
|
|
247
275
|
id: ACPX_BACKEND_ID,
|
|
248
|
-
runtime:
|
|
276
|
+
runtime: deferredRuntime
|
|
249
277
|
});
|
|
250
278
|
ctx.logger.info("embedded acpx runtime backend registered lazily");
|
|
251
279
|
},
|
|
252
280
|
async stop(ctx) {
|
|
253
|
-
if (state.
|
|
254
|
-
|
|
281
|
+
if (state.stopPromise) return await state.stopPromise;
|
|
282
|
+
state.lifecycleRevision += 1;
|
|
255
283
|
state.ctx = null;
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
284
|
+
const ownedRuntime = state.ownedRuntime;
|
|
285
|
+
unregisterOwnedRuntime(ownedRuntime);
|
|
286
|
+
const startPromise = state.startPromise;
|
|
287
|
+
state.stopPromise = (async () => {
|
|
288
|
+
await startPromise?.catch(() => void 0);
|
|
289
|
+
try {
|
|
290
|
+
await state.realService?.stop?.(ctx);
|
|
291
|
+
} finally {
|
|
292
|
+
unregisterOwnedRuntime(ownedRuntime);
|
|
293
|
+
state.ownedRuntime = null;
|
|
294
|
+
state.realRuntime = null;
|
|
295
|
+
state.realService = null;
|
|
296
|
+
state.startPromise = null;
|
|
297
|
+
}
|
|
298
|
+
})();
|
|
299
|
+
try {
|
|
300
|
+
await state.stopPromise;
|
|
301
|
+
} finally {
|
|
302
|
+
state.stopPromise = null;
|
|
303
|
+
}
|
|
259
304
|
}
|
|
260
305
|
};
|
|
261
306
|
}
|
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-C29AY8LI.js";
|
|
2
2
|
export { createAcpxRuntimeService };
|