@openclaw/acpx 2026.7.2-beta.7 → 2026.8.1-beta.3
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 +47 -1318
- package/dist/pi-session-catalog-runtime-BIu-8uRZ.js +887 -0
- package/dist/pi-session-paths-EMbd4Hkz.js +84 -0
- package/dist/{process-lease-DSLDgiNl.js → process-lease-Cwvj7WGe.js} +5 -2
- package/dist/{process-reaper-DFwbcdPa.js → process-reaper-DduWm_7N.js} +66 -24
- package/dist/register.runtime-Dj29TKFy.js +180 -0
- package/dist/register.runtime.js +1 -1
- package/dist/{runtime-By_lR8uk.js → runtime-BmOxa15I.js} +89 -67
- package/dist/{service-CBZSAymH.js → service-PRlUtXMf.js} +39 -45
- package/openclaw.plugin.json +5 -17
- package/package.json +5 -5
- package/dist/register.runtime-BbS2JTTv.js +0 -263
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
const PI_SESSIONS_CAPABILITY = "pi-sessions";
|
|
10
|
+
const PI_LOCAL_SESSION_HOST_ID = "gateway";
|
|
11
|
+
const PI_SESSION_ID_PATTERN = /^(?!-)[A-Za-z0-9._:-]{1,256}$/u;
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region extensions/acpx/src/pi-session-paths.ts
|
|
14
|
+
function piHome(env) {
|
|
15
|
+
return (process.platform === "win32" ? env.USERPROFILE?.trim() : env.HOME?.trim()) || os.homedir();
|
|
16
|
+
}
|
|
17
|
+
function isPiSessionCatalogPathAbsolute(value, platform = process.platform) {
|
|
18
|
+
if (platform !== "win32") return path.posix.isAbsolute(value);
|
|
19
|
+
const root = path.win32.parse(value).root;
|
|
20
|
+
return path.win32.isAbsolute(value) && root !== "\\" && root !== "/";
|
|
21
|
+
}
|
|
22
|
+
function resolveConfiguredPath(value, env, relativeBase) {
|
|
23
|
+
const home = piHome(env);
|
|
24
|
+
let resolved = value;
|
|
25
|
+
if (value === "~") resolved = home;
|
|
26
|
+
if (value.startsWith("~/") || value.startsWith("~\\")) resolved = path.join(home, value.slice(2));
|
|
27
|
+
if (!isPiSessionCatalogPathAbsolute(resolved)) {
|
|
28
|
+
if (relativeBase) return path.resolve(relativeBase, resolved);
|
|
29
|
+
throw new Error("Pi session catalog requires absolute or home-relative storage paths");
|
|
30
|
+
}
|
|
31
|
+
return path.resolve(resolved);
|
|
32
|
+
}
|
|
33
|
+
function settingsSessionDir(file) {
|
|
34
|
+
try {
|
|
35
|
+
const value = JSON.parse(readFileSync(file, "utf8"));
|
|
36
|
+
return isRecord(value) ? normalizeBoundedOptionalString(value.sessionDir, 4096) : void 0;
|
|
37
|
+
} catch {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function piSessionStore(env, cwd = process.cwd()) {
|
|
42
|
+
const customSessionDir = env.PI_CODING_AGENT_SESSION_DIR?.trim();
|
|
43
|
+
if (customSessionDir) return {
|
|
44
|
+
root: resolveConfiguredPath(customSessionDir, env),
|
|
45
|
+
flat: true,
|
|
46
|
+
usesProcessHomeFallback: false
|
|
47
|
+
};
|
|
48
|
+
const home = piHome(env);
|
|
49
|
+
const customAgentDir = env.PI_CODING_AGENT_DIR?.trim();
|
|
50
|
+
const agentDir = customAgentDir ? resolveConfiguredPath(customAgentDir, env) : path.join(home, ".pi", "agent");
|
|
51
|
+
const projectSessionDir = settingsSessionDir(path.join(cwd, ".pi", "settings.json"));
|
|
52
|
+
if (projectSessionDir) return {
|
|
53
|
+
root: resolveConfiguredPath(projectSessionDir, env, path.join(cwd, ".pi")),
|
|
54
|
+
flat: true,
|
|
55
|
+
usesProcessHomeFallback: false
|
|
56
|
+
};
|
|
57
|
+
const globalSessionDir = settingsSessionDir(path.join(agentDir, "settings.json"));
|
|
58
|
+
if (globalSessionDir) return {
|
|
59
|
+
root: resolveConfiguredPath(globalSessionDir, env, agentDir),
|
|
60
|
+
flat: true,
|
|
61
|
+
usesProcessHomeFallback: false
|
|
62
|
+
};
|
|
63
|
+
return {
|
|
64
|
+
root: path.join(agentDir, "sessions"),
|
|
65
|
+
flat: false,
|
|
66
|
+
usesProcessHomeFallback: !customAgentDir
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** Store root scanned by pi-acp@0.0.26 when resolving a native session id. */
|
|
70
|
+
function piAcpSessionStoreRoot(env) {
|
|
71
|
+
const configuredAgentDir = env.PI_CODING_AGENT_DIR?.trim();
|
|
72
|
+
if (configuredAgentDir && !isPiSessionCatalogPathAbsolute(configuredAgentDir)) return;
|
|
73
|
+
const agentDir = configuredAgentDir ? path.resolve(configuredAgentDir) : path.join(piHome(env), ".pi", "agent");
|
|
74
|
+
return path.join(agentDir, "sessions");
|
|
75
|
+
}
|
|
76
|
+
function piSessionStoreAvailable(env, store) {
|
|
77
|
+
try {
|
|
78
|
+
return statSync((store ?? piSessionStore(env)).root).isDirectory();
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { PI_SESSIONS_CAPABILITY as a, PI_SESSION_READ_COMMAND as c, PI_LOCAL_SESSION_HOST_ID as i, PI_TERMINAL_RESUME_COMMAND as l, piSessionStore as n, PI_SESSIONS_LIST_COMMAND as o, piSessionStoreAvailable as r, PI_SESSION_ID_PATTERN as s, piAcpSessionStoreRoot as t };
|
|
@@ -74,6 +74,8 @@ function normalizeAcpxGatewayInstanceRecord(value) {
|
|
|
74
74
|
const OPENCLAW_ACPX_LEASE_ID_ARG = "--openclaw-acpx-lease-id";
|
|
75
75
|
/** CLI argument carrying the owning gateway instance id. */
|
|
76
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";
|
|
77
79
|
/** Read OpenClaw lease identity from a generated wrapper command. */
|
|
78
80
|
function readAcpxProcessLeaseIdentity(command) {
|
|
79
81
|
const parts = splitCommandParts(command?.trim() ?? "");
|
|
@@ -119,7 +121,8 @@ function normalizeAcpxProcessLeaseFile(value) {
|
|
|
119
121
|
function openAcpxProcessLeaseStateStore(openKeyedStore) {
|
|
120
122
|
return openKeyedStore({
|
|
121
123
|
namespace: ACPX_PROCESS_LEASE_NAMESPACE,
|
|
122
|
-
maxEntries: ACPX_PROCESS_LEASE_MAX_ENTRIES
|
|
124
|
+
maxEntries: ACPX_PROCESS_LEASE_MAX_ENTRIES,
|
|
125
|
+
overflowPolicy: "reject-new"
|
|
123
126
|
});
|
|
124
127
|
}
|
|
125
128
|
/** Create a serialized SQLite-backed ACPX process lease store. */
|
|
@@ -189,4 +192,4 @@ function withAcpxLeaseEnvironment(params) {
|
|
|
189
192
|
return appendAcpxLeaseArgs(params);
|
|
190
193
|
}
|
|
191
194
|
//#endregion
|
|
192
|
-
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,12 +1,12 @@
|
|
|
1
|
-
import { t as AcpxPluginConfigSchema } from "./config-schema-
|
|
2
|
-
import {
|
|
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
|
-
import { formatPluginConfigIssue } from "openclaw/plugin-sdk/extension-shared";
|
|
5
4
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
5
|
import fs from "node:fs";
|
|
7
6
|
import path from "node:path";
|
|
8
|
-
import { runExec } from "openclaw/plugin-sdk/process-runtime";
|
|
7
|
+
import { isPidAlive, runExec } from "openclaw/plugin-sdk/process-runtime";
|
|
9
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { formatPluginConfigIssue } from "openclaw/plugin-sdk/extension-shared";
|
|
10
10
|
//#region extensions/acpx/src/codex-adapter.ts
|
|
11
11
|
const CODEX_ACP_PACKAGE = "@agentclientprotocol/codex-acp";
|
|
12
12
|
const CODEX_ACP_BIN = "codex-acp";
|
|
@@ -65,8 +65,6 @@ function resolveAcpxPluginRoot(moduleUrl = import.meta.url) {
|
|
|
65
65
|
}
|
|
66
66
|
const DEFAULT_PERMISSION_MODE = "approve-reads";
|
|
67
67
|
const DEFAULT_NON_INTERACTIVE_POLICY = "fail";
|
|
68
|
-
const DEFAULT_QUEUE_OWNER_TTL_SECONDS = .1;
|
|
69
|
-
const DEFAULT_STRICT_WINDOWS_CMD_WRAPPER = true;
|
|
70
68
|
function parseAcpxPluginConfig(value) {
|
|
71
69
|
if (value === void 0) return {
|
|
72
70
|
ok: true,
|
|
@@ -186,13 +184,7 @@ function resolveAcpxPluginConfig(params) {
|
|
|
186
184
|
nonInteractivePermissions: normalized.nonInteractivePermissions ?? DEFAULT_NON_INTERACTIVE_POLICY,
|
|
187
185
|
pluginToolsMcpBridge,
|
|
188
186
|
openClawToolsMcpBridge,
|
|
189
|
-
strictWindowsCmdWrapper: normalized.strictWindowsCmdWrapper ?? DEFAULT_STRICT_WINDOWS_CMD_WRAPPER,
|
|
190
187
|
timeoutSeconds: normalized.timeoutSeconds ?? 120,
|
|
191
|
-
queueOwnerTtlSeconds: normalized.queueOwnerTtlSeconds ?? DEFAULT_QUEUE_OWNER_TTL_SECONDS,
|
|
192
|
-
legacyCompatibilityConfig: {
|
|
193
|
-
strictWindowsCmdWrapper: normalized.strictWindowsCmdWrapper,
|
|
194
|
-
queueOwnerTtlSeconds: normalized.queueOwnerTtlSeconds
|
|
195
|
-
},
|
|
196
188
|
mcpServers,
|
|
197
189
|
agents
|
|
198
190
|
};
|
|
@@ -272,6 +264,15 @@ function commandWrapperBelongsToRoot(command, wrapperRoot) {
|
|
|
272
264
|
const normalizedRoot = normalizePathLike(wrapperRoot).replace(/\/+$/, "");
|
|
273
265
|
return Array.from(GENERATED_WRAPPER_BASENAMES).some((basename) => normalizedCommand.includes(`${normalizedRoot}/${basename}`));
|
|
274
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
|
+
}
|
|
275
276
|
/** Check whether a command references an OpenClaw-generated ACPX wrapper path. */
|
|
276
277
|
function isOpenClawLeaseAwareAcpxProcessCommand(params) {
|
|
277
278
|
const command = params.command?.trim();
|
|
@@ -354,14 +355,6 @@ function collectProcessTree(processes, rootPid) {
|
|
|
354
355
|
function uniquePids(processes) {
|
|
355
356
|
return Array.from(new Set(processes.map((processInfo) => processInfo.pid).filter((pid) => Number.isInteger(pid) && pid > 0 && pid !== process.pid)));
|
|
356
357
|
}
|
|
357
|
-
function isProcessAlive(pid) {
|
|
358
|
-
try {
|
|
359
|
-
process.kill(pid, 0);
|
|
360
|
-
return true;
|
|
361
|
-
} catch {
|
|
362
|
-
return false;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
358
|
async function terminatePids(pids, deps) {
|
|
366
359
|
const killProcess = deps?.killProcess ?? ((pid, signal) => process.kill(pid, signal));
|
|
367
360
|
const sleep = deps?.sleep ?? ((ms) => new Promise((resolve) => {
|
|
@@ -374,7 +367,7 @@ async function terminatePids(pids, deps) {
|
|
|
374
367
|
} catch {}
|
|
375
368
|
if (terminated.length === 0) return terminated;
|
|
376
369
|
await sleep(750);
|
|
377
|
-
for (const pid of terminated) if (deps?.killProcess ||
|
|
370
|
+
for (const pid of terminated) if (deps?.killProcess || isPidAlive(pid)) try {
|
|
378
371
|
killProcess(pid, "SIGKILL");
|
|
379
372
|
} catch {}
|
|
380
373
|
return terminated;
|
|
@@ -387,6 +380,11 @@ async function cleanupOpenClawOwnedAcpxProcessTree(params) {
|
|
|
387
380
|
terminatedPids: [],
|
|
388
381
|
skippedReason: "missing-root"
|
|
389
382
|
};
|
|
383
|
+
if ((params.deps?.platform ?? process.platform) === "win32") return {
|
|
384
|
+
inspectedPids: [],
|
|
385
|
+
terminatedPids: [],
|
|
386
|
+
skippedReason: "unsupported-platform"
|
|
387
|
+
};
|
|
390
388
|
let processes;
|
|
391
389
|
try {
|
|
392
390
|
processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
|
|
@@ -439,9 +437,53 @@ async function cleanupOpenClawOwnedAcpxProcessTree(params) {
|
|
|
439
437
|
terminatedPids: await terminatePids(pids, params.deps)
|
|
440
438
|
};
|
|
441
439
|
}
|
|
440
|
+
/** Recover a pending lease by matching its exact live wrapper identity. */
|
|
441
|
+
async function cleanupOpenClawOwnedAcpxPendingLease(params) {
|
|
442
|
+
if ((params.deps?.platform ?? process.platform) === "win32") return {
|
|
443
|
+
inspectedPids: [],
|
|
444
|
+
terminatedPids: [],
|
|
445
|
+
skippedReason: "unsupported-platform"
|
|
446
|
+
};
|
|
447
|
+
if (!params.wrapperPath || !wrapperPathBelongsToRoot(params.wrapperPath, params.wrapperRoot)) return {
|
|
448
|
+
inspectedPids: [],
|
|
449
|
+
terminatedPids: [],
|
|
450
|
+
skippedReason: "unverified-root"
|
|
451
|
+
};
|
|
452
|
+
let processes;
|
|
453
|
+
try {
|
|
454
|
+
processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
|
|
455
|
+
} catch {
|
|
456
|
+
return {
|
|
457
|
+
inspectedPids: [],
|
|
458
|
+
terminatedPids: [],
|
|
459
|
+
skippedReason: "process-list-unavailable"
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
const matchingRoots = processes.filter((processInfo) => commandContainsExactWrapperPath(processInfo.command, params.wrapperPath) && liveCommandMatchesLeaseIdentity({
|
|
463
|
+
command: processInfo.command,
|
|
464
|
+
expectedLeaseId: params.leaseId,
|
|
465
|
+
expectedGatewayInstanceId: params.gatewayInstanceId
|
|
466
|
+
}));
|
|
467
|
+
if (matchingRoots.length === 0) return {
|
|
468
|
+
inspectedPids: [],
|
|
469
|
+
terminatedPids: [],
|
|
470
|
+
skippedReason: "missing-root"
|
|
471
|
+
};
|
|
472
|
+
if (matchingRoots.length > 1) return {
|
|
473
|
+
inspectedPids: uniquePids(matchingRoots),
|
|
474
|
+
terminatedPids: [],
|
|
475
|
+
skippedReason: "ambiguous-root"
|
|
476
|
+
};
|
|
477
|
+
const listedTree = collectProcessTree(processes, matchingRoots[0].pid);
|
|
478
|
+
const pids = uniquePids(listedTree.toReversed());
|
|
479
|
+
return {
|
|
480
|
+
inspectedPids: uniquePids(listedTree),
|
|
481
|
+
terminatedPids: await terminatePids(pids, params.deps)
|
|
482
|
+
};
|
|
483
|
+
}
|
|
442
484
|
/** Reap orphaned OpenClaw-owned ACPX wrapper trees during runtime startup. */
|
|
443
485
|
async function reapStaleOpenClawOwnedAcpxOrphans(params) {
|
|
444
|
-
if (process.platform === "win32") return {
|
|
486
|
+
if ((params.deps?.platform ?? process.platform) === "win32") return {
|
|
445
487
|
inspectedPids: [],
|
|
446
488
|
terminatedPids: [],
|
|
447
489
|
skippedReason: "unsupported-platform"
|
|
@@ -456,7 +498,7 @@ async function reapStaleOpenClawOwnedAcpxOrphans(params) {
|
|
|
456
498
|
skippedReason: "process-list-unavailable"
|
|
457
499
|
};
|
|
458
500
|
}
|
|
459
|
-
const orphanTrees = processes.filter((processInfo) => processInfo.ppid === 1 && isOpenClawOwnedAcpxProcessCommand({
|
|
501
|
+
const orphanTrees = processes.filter((processInfo) => processInfo.ppid === 1 && !readAcpxProcessLeaseIdentity(processInfo.command) && isOpenClawOwnedAcpxProcessCommand({
|
|
460
502
|
command: processInfo.command,
|
|
461
503
|
wrapperRoot: params.wrapperRoot
|
|
462
504
|
})).map((orphan) => collectProcessTree(processes, orphan.pid));
|
|
@@ -466,4 +508,4 @@ async function reapStaleOpenClawOwnedAcpxOrphans(params) {
|
|
|
466
508
|
};
|
|
467
509
|
}
|
|
468
510
|
//#endregion
|
|
469
|
-
export {
|
|
511
|
+
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 };
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { getAcpRuntimeBackend, registerAcpRuntimeBackend, unregisterAcpRuntimeBackend } from "openclaw/plugin-sdk/acp-runtime-backend";
|
|
2
|
+
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
3
|
+
//#region extensions/acpx/src/runtime-proxy.ts
|
|
4
|
+
/** Start an ACP turn through a lazy runtime resolver without awaiting resolution up front. */
|
|
5
|
+
function lazyStartRuntimeTurn(resolveRuntime, input) {
|
|
6
|
+
const turnPromise = resolveRuntime().then((runtime) => runtime.startTurn(input));
|
|
7
|
+
return {
|
|
8
|
+
requestId: input.requestId,
|
|
9
|
+
get promptStarted() {
|
|
10
|
+
return turnPromise.then((turn) => turn.promptStarted);
|
|
11
|
+
},
|
|
12
|
+
events: { async *[Symbol.asyncIterator]() {
|
|
13
|
+
yield* (await turnPromise).events;
|
|
14
|
+
} },
|
|
15
|
+
result: turnPromise.then((turn) => turn.result),
|
|
16
|
+
cancel(inputArgs) {
|
|
17
|
+
return turnPromise.then((turn) => turn.cancel(inputArgs));
|
|
18
|
+
},
|
|
19
|
+
closeStream(inputArgs) {
|
|
20
|
+
return turnPromise.then((turn) => turn.closeStream(inputArgs));
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Create an ACP runtime facade backed by an async runtime resolver. */
|
|
25
|
+
function createLazyAcpRuntimeProxy(resolveRuntime) {
|
|
26
|
+
return {
|
|
27
|
+
async ensureSession(input) {
|
|
28
|
+
return await (await resolveRuntime()).ensureSession(input);
|
|
29
|
+
},
|
|
30
|
+
startTurn(input) {
|
|
31
|
+
return lazyStartRuntimeTurn(resolveRuntime, input);
|
|
32
|
+
},
|
|
33
|
+
async *runTurn(input) {
|
|
34
|
+
yield* (await resolveRuntime()).runTurn(input);
|
|
35
|
+
},
|
|
36
|
+
async getCapabilities(input) {
|
|
37
|
+
return await (await resolveRuntime()).getCapabilities(input);
|
|
38
|
+
},
|
|
39
|
+
async getStatus(input) {
|
|
40
|
+
return await (await resolveRuntime()).getStatus(input);
|
|
41
|
+
},
|
|
42
|
+
async setMode(input) {
|
|
43
|
+
await (await resolveRuntime()).setMode(input);
|
|
44
|
+
},
|
|
45
|
+
async setConfigOption(input) {
|
|
46
|
+
await (await resolveRuntime()).setConfigOption(input);
|
|
47
|
+
},
|
|
48
|
+
async doctor() {
|
|
49
|
+
return await (await resolveRuntime()).doctor();
|
|
50
|
+
},
|
|
51
|
+
async prepareFreshSession(input) {
|
|
52
|
+
await (await resolveRuntime()).prepareFreshSession(input);
|
|
53
|
+
},
|
|
54
|
+
async cancel(input) {
|
|
55
|
+
await (await resolveRuntime()).cancel(input);
|
|
56
|
+
},
|
|
57
|
+
async close(input) {
|
|
58
|
+
await (await resolveRuntime()).close(input);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region extensions/acpx/register.runtime.ts
|
|
64
|
+
/**
|
|
65
|
+
* Lazy ACPX runtime service registration. The plugin exposes an ACP backend
|
|
66
|
+
* immediately, then imports the heavier service only when a session needs it.
|
|
67
|
+
*/
|
|
68
|
+
const ACPX_BACKEND_ID = "acpx";
|
|
69
|
+
const loadServiceModule = createLazyRuntimeModule(() => import("./service-PRlUtXMf.js"));
|
|
70
|
+
function unregisterOwnedRuntime(runtime) {
|
|
71
|
+
if (runtime && getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime === runtime) unregisterAcpRuntimeBackend(ACPX_BACKEND_ID);
|
|
72
|
+
}
|
|
73
|
+
async function startRealService(state, lifecycleRevision, deferredRuntime) {
|
|
74
|
+
if (state.lifecycleRevision !== lifecycleRevision || !state.ctx) throw new Error("ACPX runtime service is not started");
|
|
75
|
+
if (state.realRuntime) return state.realRuntime;
|
|
76
|
+
if (state.startPromise) return await state.startPromise;
|
|
77
|
+
const ctx = state.ctx;
|
|
78
|
+
state.startPromise = (async () => {
|
|
79
|
+
let publishedRuntime = null;
|
|
80
|
+
const { createAcpxRuntimeService: createAcpxRuntimeServiceLocal } = await loadServiceModule();
|
|
81
|
+
const service = createAcpxRuntimeServiceLocal({
|
|
82
|
+
...state.params,
|
|
83
|
+
backendLifecycle: {
|
|
84
|
+
publish(backend) {
|
|
85
|
+
if (state.lifecycleRevision !== lifecycleRevision || state.ctx !== ctx) throw new Error("ACPX runtime service stopped during activation");
|
|
86
|
+
if (getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime !== deferredRuntime) throw new Error("ACPX runtime service lost registry ownership during activation");
|
|
87
|
+
registerAcpRuntimeBackend({
|
|
88
|
+
id: ACPX_BACKEND_ID,
|
|
89
|
+
...backend
|
|
90
|
+
});
|
|
91
|
+
publishedRuntime = backend.runtime;
|
|
92
|
+
state.ownedRuntime = backend.runtime;
|
|
93
|
+
},
|
|
94
|
+
retract(runtime) {
|
|
95
|
+
unregisterOwnedRuntime(runtime);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
state.realService = service;
|
|
100
|
+
await service.start(ctx);
|
|
101
|
+
if (state.lifecycleRevision !== lifecycleRevision || state.ctx !== ctx) throw new Error("ACPX runtime service stopped during activation");
|
|
102
|
+
if (!publishedRuntime) throw new Error("ACPX runtime service did not register an ACP backend");
|
|
103
|
+
if (getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime !== publishedRuntime) throw new Error("ACPX runtime service lost registry ownership during activation");
|
|
104
|
+
state.realRuntime = publishedRuntime;
|
|
105
|
+
return publishedRuntime;
|
|
106
|
+
})();
|
|
107
|
+
try {
|
|
108
|
+
return await state.startPromise;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (state.lifecycleRevision === lifecycleRevision) {
|
|
111
|
+
state.startPromise = null;
|
|
112
|
+
state.realService = null;
|
|
113
|
+
}
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function createDeferredRuntime(state, lifecycleRevision) {
|
|
118
|
+
const deferredRuntime = createLazyAcpRuntimeProxy(() => startRealService(state, lifecycleRevision, deferredRuntime));
|
|
119
|
+
return deferredRuntime;
|
|
120
|
+
}
|
|
121
|
+
/** Creates the plugin service that registers ACPX as an ACP runtime backend. */
|
|
122
|
+
function createAcpxRuntimeService(params = {}) {
|
|
123
|
+
const state = {
|
|
124
|
+
ctx: null,
|
|
125
|
+
lifecycleRevision: 0,
|
|
126
|
+
ownedRuntime: null,
|
|
127
|
+
params,
|
|
128
|
+
realRuntime: null,
|
|
129
|
+
realService: null,
|
|
130
|
+
startPromise: null,
|
|
131
|
+
stopPromise: null
|
|
132
|
+
};
|
|
133
|
+
return {
|
|
134
|
+
id: "acpx-runtime",
|
|
135
|
+
async start(ctx) {
|
|
136
|
+
if (process.env.OPENCLAW_SKIP_ACPX_RUNTIME === "1") {
|
|
137
|
+
ctx.logger.info("skipping embedded acpx runtime backend (OPENCLAW_SKIP_ACPX_RUNTIME=1)");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (state.stopPromise) await state.stopPromise;
|
|
141
|
+
state.lifecycleRevision += 1;
|
|
142
|
+
const lifecycleRevision = state.lifecycleRevision;
|
|
143
|
+
state.ctx = ctx;
|
|
144
|
+
const deferredRuntime = createDeferredRuntime(state, lifecycleRevision);
|
|
145
|
+
state.ownedRuntime = deferredRuntime;
|
|
146
|
+
registerAcpRuntimeBackend({
|
|
147
|
+
id: ACPX_BACKEND_ID,
|
|
148
|
+
runtime: deferredRuntime
|
|
149
|
+
});
|
|
150
|
+
ctx.logger.info("embedded acpx runtime backend registered lazily");
|
|
151
|
+
},
|
|
152
|
+
async stop(ctx) {
|
|
153
|
+
if (state.stopPromise) return await state.stopPromise;
|
|
154
|
+
state.lifecycleRevision += 1;
|
|
155
|
+
state.ctx = null;
|
|
156
|
+
const ownedRuntime = state.ownedRuntime;
|
|
157
|
+
unregisterOwnedRuntime(ownedRuntime);
|
|
158
|
+
const startPromise = state.startPromise;
|
|
159
|
+
state.stopPromise = (async () => {
|
|
160
|
+
await startPromise?.catch(() => void 0);
|
|
161
|
+
try {
|
|
162
|
+
await state.realService?.stop?.(ctx);
|
|
163
|
+
} finally {
|
|
164
|
+
unregisterOwnedRuntime(ownedRuntime);
|
|
165
|
+
state.ownedRuntime = null;
|
|
166
|
+
state.realRuntime = null;
|
|
167
|
+
state.realService = null;
|
|
168
|
+
state.startPromise = null;
|
|
169
|
+
}
|
|
170
|
+
})();
|
|
171
|
+
try {
|
|
172
|
+
await state.stopPromise;
|
|
173
|
+
} finally {
|
|
174
|
+
state.stopPromise = null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
export { createLazyAcpRuntimeProxy as n, createAcpxRuntimeService as t };
|
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-Dj29TKFy.js";
|
|
2
2
|
export { createAcpxRuntimeService };
|