@openclaw/acpx 2026.9.8 → 2026.10.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/.setup/{config-Blz15uwJ.mjs → config-76krYh6C.mjs} +131 -3
- package/dist/.setup/{register.runtime-DByczkH_.mjs → register.runtime-C0nVOTzT.mjs} +1 -1
- package/dist/.setup/{process-lease-n38HFGdp.mjs → runtime-doctor-migrations-hxP-CXX7.mjs} +9 -129
- package/dist/.setup/{service-D5hGWl5U.mjs → service-BXgGdE56.mjs} +41 -74
- package/dist/.setup/{session-owner-migration-DsGrYq7Y.mjs → session-owner-migration-C73lfijV.mjs} +121 -5
- package/dist/.setup/{session-resource-B-4yPr-j.mjs → session-resource-DKJsUJPu.mjs} +1 -1
- package/dist/doctor-contract-api.js +4 -94
- package/dist/index.js +6 -5
- package/dist/register.runtime.js +1 -1
- package/openclaw.plugin.json +0 -1
- package/package.json +5 -15
- package/dist/.setup/command-line-B72femaL.mjs +0 -135
- package/dist/mcp-command-line.mjs +0 -128
- package/dist/mcp-proxy.mjs +0 -159
- package/dist/.setup/{pi-session-catalog-runtime-IKEanK3s.mjs → pi-session-catalog-runtime-7jc50ZJ4.mjs} +1 -1
package/dist/.setup/{session-owner-migration-DsGrYq7Y.mjs → session-owner-migration-C73lfijV.mjs}
RENAMED
|
@@ -1,9 +1,125 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as resolveAcpxPluginConfig } from "./config-
|
|
1
|
+
import { t as runtime_doctor_migrations_exports } from "./runtime-doctor-migrations-hxP-CXX7.mjs";
|
|
2
|
+
import { c as renderAgentCommand, t as resolveAcpxPluginConfig, u as splitCommandParts } from "./config-76krYh6C.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { asFiniteNumber, asOptionalObjectRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
4
6
|
import fs from "node:fs/promises";
|
|
5
7
|
import { isDeepStrictEqual } from "node:util";
|
|
6
8
|
import { movePathWithCopyFallback } from "openclaw/plugin-sdk/security-runtime";
|
|
9
|
+
//#region extensions/acpx/src/state.ts
|
|
10
|
+
const ACPX_PROCESS_LEASE_NAMESPACE = "process-leases";
|
|
11
|
+
const ACPX_PROCESS_LEASE_MAX_ENTRIES = 4096;
|
|
12
|
+
const ACPX_GATEWAY_INSTANCE_NAMESPACE = "gateway-instance";
|
|
13
|
+
const ACPX_GATEWAY_INSTANCE_KEY = "current";
|
|
14
|
+
function normalizeAcpxGatewayInstanceRecord(value) {
|
|
15
|
+
const record = asOptionalObjectRecord(value);
|
|
16
|
+
const instanceId = normalizeOptionalString(record?.instanceId);
|
|
17
|
+
if (!instanceId) return;
|
|
18
|
+
return {
|
|
19
|
+
instanceId,
|
|
20
|
+
createdAt: Math.trunc(asFiniteNumber(record?.createdAt) ?? 0)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region extensions/acpx/src/process-lease.ts
|
|
25
|
+
const OPENCLAW_ACPX_LEASE_ID_ARG = "--openclaw-acpx-lease-id";
|
|
26
|
+
const OPENCLAW_GATEWAY_INSTANCE_ID_ARG = "--openclaw-gateway-instance-id";
|
|
27
|
+
/** Synthetic session identity for generated-wrapper health probes. */
|
|
28
|
+
const ACPX_PROBE_LEASE_SESSION_KEY = "openclaw:acpx:probe";
|
|
29
|
+
function readAcpxProcessLeaseIdentity(command) {
|
|
30
|
+
const parts = typeof command === "string" ? Array.from(command.matchAll(/(?:^|\s)(--openclaw-(?:acpx-lease-id|gateway-instance-id))\s+(?:"([^"]*)"|'([^']*)'|(\S+))(?=\s|$)/g)).flatMap((match) => [match[1], match[2] ?? match[3] ?? match[4]]) : command ?? [];
|
|
31
|
+
const leaseIndex = parts.lastIndexOf(OPENCLAW_ACPX_LEASE_ID_ARG);
|
|
32
|
+
const gatewayIndex = parts.lastIndexOf(OPENCLAW_GATEWAY_INSTANCE_ID_ARG);
|
|
33
|
+
const leaseId = leaseIndex >= 0 ? parts[leaseIndex + 1]?.trim() : "";
|
|
34
|
+
const gatewayInstanceId = gatewayIndex >= 0 ? parts[gatewayIndex + 1]?.trim() : "";
|
|
35
|
+
if (!leaseId || !gatewayInstanceId) return;
|
|
36
|
+
return {
|
|
37
|
+
leaseId,
|
|
38
|
+
gatewayInstanceId
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function normalizeAcpxProcessLease(value) {
|
|
42
|
+
const record = asOptionalObjectRecord(value);
|
|
43
|
+
if (!record || typeof record.leaseId !== "string" || typeof record.gatewayInstanceId !== "string" || typeof record.sessionKey !== "string" || typeof record.wrapperRoot !== "string" || typeof record.wrapperPath !== "string" || typeof record.rootPid !== "number" || typeof record.commandHash !== "string" || typeof record.startedAt !== "number" || ![
|
|
44
|
+
"open",
|
|
45
|
+
"closing",
|
|
46
|
+
"closed",
|
|
47
|
+
"lost"
|
|
48
|
+
].includes(String(record.state))) return;
|
|
49
|
+
return {
|
|
50
|
+
leaseId: record.leaseId,
|
|
51
|
+
gatewayInstanceId: record.gatewayInstanceId,
|
|
52
|
+
sessionKey: record.sessionKey,
|
|
53
|
+
wrapperRoot: record.wrapperRoot,
|
|
54
|
+
wrapperPath: record.wrapperPath,
|
|
55
|
+
rootPid: record.rootPid,
|
|
56
|
+
...typeof record.processGroupId === "number" ? { processGroupId: record.processGroupId } : {},
|
|
57
|
+
commandHash: record.commandHash,
|
|
58
|
+
startedAt: record.startedAt,
|
|
59
|
+
state: record.state
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function openAcpxProcessLeaseStateStore(openKeyedStore) {
|
|
63
|
+
return openKeyedStore({
|
|
64
|
+
namespace: ACPX_PROCESS_LEASE_NAMESPACE,
|
|
65
|
+
maxEntries: ACPX_PROCESS_LEASE_MAX_ENTRIES,
|
|
66
|
+
overflowPolicy: "reject-new"
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function createAcpxProcessLeaseStore(params) {
|
|
70
|
+
let updateQueue = Promise.resolve();
|
|
71
|
+
async function update(mutator) {
|
|
72
|
+
const run = updateQueue.then(mutator);
|
|
73
|
+
updateQueue = run.catch(() => {});
|
|
74
|
+
await run;
|
|
75
|
+
}
|
|
76
|
+
async function readCurrent() {
|
|
77
|
+
await updateQueue;
|
|
78
|
+
return (await params.store.entries()).map((entry) => normalizeAcpxProcessLease(entry.value)).filter((lease) => Boolean(lease));
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
async load(leaseId) {
|
|
82
|
+
await updateQueue;
|
|
83
|
+
return normalizeAcpxProcessLease(await params.store.lookup(leaseId));
|
|
84
|
+
},
|
|
85
|
+
async listOpen(gatewayInstanceId) {
|
|
86
|
+
return (await readCurrent()).filter((lease) => (lease.state === "open" || lease.state === "closing") && (!gatewayInstanceId || lease.gatewayInstanceId === gatewayInstanceId));
|
|
87
|
+
},
|
|
88
|
+
async save(lease) {
|
|
89
|
+
await update(async () => {
|
|
90
|
+
await params.store.register(lease.leaseId, lease);
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
async markState(leaseId, state) {
|
|
94
|
+
await update(async () => {
|
|
95
|
+
if (state === "closed" || state === "lost") {
|
|
96
|
+
await params.store.delete(leaseId);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const lease = normalizeAcpxProcessLease(await params.store.lookup(leaseId));
|
|
100
|
+
if (lease) await params.store.register(leaseId, {
|
|
101
|
+
...lease,
|
|
102
|
+
state
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/** Hash a wrapper command so process leases can detect command drift. */
|
|
109
|
+
function hashAcpxProcessCommand(command) {
|
|
110
|
+
return createHash("sha256").update(renderAgentCommand(command)).digest("hex");
|
|
111
|
+
}
|
|
112
|
+
/** Append portable wrapper arguments without changing the executable or argument bytes. */
|
|
113
|
+
function withAcpxLeaseArgs(params) {
|
|
114
|
+
return [
|
|
115
|
+
...splitCommandParts(params.command),
|
|
116
|
+
OPENCLAW_ACPX_LEASE_ID_ARG,
|
|
117
|
+
params.leaseId,
|
|
118
|
+
OPENCLAW_GATEWAY_INSTANCE_ID_ARG,
|
|
119
|
+
params.gatewayInstanceId
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
7
123
|
//#region extensions/acpx/src/session-owner-migration.ts
|
|
8
124
|
function openStateDirectoryMarker(input) {
|
|
9
125
|
return input.openKeyedStore({
|
|
@@ -114,7 +230,7 @@ async function legacyRecords(input, directory = sessionDirectory(input)) {
|
|
|
114
230
|
directory,
|
|
115
231
|
ids
|
|
116
232
|
};
|
|
117
|
-
const { resolveAcpxSessionResource } = await import("./session-resource-
|
|
233
|
+
const { resolveAcpxSessionResource } = await import("./session-resource-DKJsUJPu.mjs").then((n) => n.n);
|
|
118
234
|
const evidence = await input.context.inspectAcpSessionClaims?.();
|
|
119
235
|
const { decodeAcpxRuntimeHandleState } = await import("acpx/runtime");
|
|
120
236
|
return {
|
|
@@ -157,7 +273,7 @@ async function migrateRecord(input, directory, oldId, claims, changes, warnings)
|
|
|
157
273
|
const raw = (0, runtime_doctor_migrations_exports.asObjectRecord)(JSON.parse(sourceBytes));
|
|
158
274
|
if (!raw || raw.acpx_record_id !== oldId || typeof raw.name !== "string" || !raw.name.trim() || raw.name !== oldId && !oldId.startsWith(`${raw.name}:oneshot:`)) throw new Error("record ID/name is not a recognized ACPX locator");
|
|
159
275
|
requireStoppedPid(raw.pid);
|
|
160
|
-
const { resolveAcpxSessionResource } = await import("./session-resource-
|
|
276
|
+
const { resolveAcpxSessionResource } = await import("./session-resource-DKJsUJPu.mjs").then((n) => n.n);
|
|
161
277
|
const { createFileSessionStore, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState } = await import("acpx/runtime");
|
|
162
278
|
const candidates = claims.filter((claim) => matchesClaimRecord(claim, raw, oldId, resolveAcpxSessionResource(claim), decodeAcpxRuntimeHandleState));
|
|
163
279
|
if (candidates.length !== 1) throw new Error("exactly one current canonical owner claim is required");
|
|
@@ -312,4 +428,4 @@ const acpxSessionOwnerMigration = {
|
|
|
312
428
|
}
|
|
313
429
|
};
|
|
314
430
|
//#endregion
|
|
315
|
-
export { acpxSessionOwnerMigration, adoptAcpxStateDirectory };
|
|
431
|
+
export { hashAcpxProcessCommand as a, acpxSessionOwnerMigration, adoptAcpxStateDirectory, withAcpxLeaseArgs as c, normalizeAcpxGatewayInstanceRecord as d, createAcpxProcessLeaseStore as i, ACPX_GATEWAY_INSTANCE_KEY as l, OPENCLAW_ACPX_LEASE_ID_ARG as n, openAcpxProcessLeaseStateStore as o, OPENCLAW_GATEWAY_INSTANCE_ID_ARG as r, readAcpxProcessLeaseIdentity as s, ACPX_PROBE_LEASE_SESSION_KEY as t, ACPX_GATEWAY_INSTANCE_NAMESPACE as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as __exportAll } from "./runtime-doctor-migrations-hxP-CXX7.mjs";
|
|
2
2
|
import { AcpRuntimeError } from "../runtime-api.js";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { normalizeAgentId, parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import fs from "node:fs/promises";
|
|
1
|
+
import { t as runtime_doctor_migrations_exports } from "./.setup/runtime-doctor-migrations-hxP-CXX7.mjs";
|
|
4
2
|
//#region extensions/acpx/doctor-contract-api.ts
|
|
5
3
|
const ACPX_CONFIG_PATH = [
|
|
6
4
|
"plugins",
|
|
@@ -36,107 +34,19 @@ function normalizeCompatibilityConfig({ cfg }) {
|
|
|
36
34
|
changes: [`Removed retired ACPX plugin config: ${retiredKeys.map((key) => [...ACPX_CONFIG_PATH, key].join(".")).join(", ")}.`]
|
|
37
35
|
};
|
|
38
36
|
}
|
|
39
|
-
function resolveLegacyGatewayInstancePath(stateDir) {
|
|
40
|
-
return path.join(stateDir, ACPX_LEGACY_GATEWAY_INSTANCE_FILE);
|
|
41
|
-
}
|
|
42
|
-
function resolveLegacyProcessLeasePath(stateDir) {
|
|
43
|
-
return path.join(stateDir, "acpx", ACPX_LEGACY_PROCESS_LEASE_FILE);
|
|
44
|
-
}
|
|
45
|
-
async function readLegacyGatewayInstanceId(filePath) {
|
|
46
|
-
try {
|
|
47
|
-
return (await fs.readFile(filePath, "utf8")).trim() || null;
|
|
48
|
-
} catch {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
async function readLegacyOpenProcessLeases(filePath) {
|
|
53
|
-
try {
|
|
54
|
-
return normalizeAcpxProcessLeaseFile(JSON.parse(await fs.readFile(filePath, "utf8"))).leases.filter((lease) => lease.state === "open" || lease.state === "closing");
|
|
55
|
-
} catch {
|
|
56
|
-
return [];
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
37
|
const stateMigrations = [{
|
|
60
|
-
id: "acpx-runtime-state-to-plugin-state",
|
|
61
|
-
label: "ACPX runtime state",
|
|
62
|
-
async detectLegacyState(params) {
|
|
63
|
-
const gatewayInstanceId = await readLegacyGatewayInstanceId(resolveLegacyGatewayInstancePath(params.stateDir));
|
|
64
|
-
const openLeases = await readLegacyOpenProcessLeases(resolveLegacyProcessLeasePath(params.stateDir));
|
|
65
|
-
if (!gatewayInstanceId && openLeases.length === 0) return null;
|
|
66
|
-
const preview = [];
|
|
67
|
-
if (gatewayInstanceId) preview.push(`- ACPX gateway instance id: ${resolveLegacyGatewayInstancePath(params.stateDir)} -> plugin state (${ACPX_GATEWAY_INSTANCE_NAMESPACE})`);
|
|
68
|
-
if (openLeases.length > 0) preview.push(`- ACPX process leases: ${resolveLegacyProcessLeasePath(params.stateDir)} -> plugin state (${openLeases.length} open lease(s))`);
|
|
69
|
-
return { preview };
|
|
70
|
-
},
|
|
71
|
-
async migrateLegacyState(params) {
|
|
72
|
-
const changes = [];
|
|
73
|
-
const warnings = [];
|
|
74
|
-
const gatewayInstancePath = resolveLegacyGatewayInstancePath(params.stateDir);
|
|
75
|
-
const gatewayInstanceId = await readLegacyGatewayInstanceId(gatewayInstancePath);
|
|
76
|
-
const processLeasePath = resolveLegacyProcessLeasePath(params.stateDir);
|
|
77
|
-
const openLeases = await readLegacyOpenProcessLeases(processLeasePath);
|
|
78
|
-
const processLeaseStore = openAcpxProcessLeaseStateStore(params.context.openPluginStateKeyedStore);
|
|
79
|
-
const gatewayStore = params.context.openPluginStateKeyedStore({
|
|
80
|
-
namespace: ACPX_GATEWAY_INSTANCE_NAMESPACE,
|
|
81
|
-
maxEntries: 1
|
|
82
|
-
});
|
|
83
|
-
const existingGateway = normalizeAcpxGatewayInstanceRecord(await gatewayStore.lookup(ACPX_GATEWAY_INSTANCE_KEY));
|
|
84
|
-
const existingLiveLeases = (await processLeaseStore.entries()).map((entry) => normalizeAcpxProcessLease(entry.value)).filter((lease) => lease != null && (lease.state === "open" || lease.state === "closing"));
|
|
85
|
-
const leaseGatewayIds = new Set(openLeases.map((lease) => lease.gatewayInstanceId));
|
|
86
|
-
const onlyLeaseGatewayId = leaseGatewayIds.size === 1 ? [...leaseGatewayIds][0] : null;
|
|
87
|
-
const canAdoptLegacyGateway = existingGateway && gatewayInstanceId && existingGateway.instanceId !== gatewayInstanceId && onlyLeaseGatewayId === gatewayInstanceId && existingLiveLeases.length === 0;
|
|
88
|
-
const canonicalGatewayInstanceId = canAdoptLegacyGateway || !existingGateway ? gatewayInstanceId ?? onlyLeaseGatewayId : existingGateway.instanceId;
|
|
89
|
-
if (openLeases.length > 0 && (!canonicalGatewayInstanceId || [...leaseGatewayIds].some((leaseGatewayId) => leaseGatewayId !== canonicalGatewayInstanceId))) {
|
|
90
|
-
warnings.push("Skipped ACPX process lease migration because legacy leases do not match the canonical gateway instance id; left legacy sources in place for manual cleanup");
|
|
91
|
-
return {
|
|
92
|
-
changes,
|
|
93
|
-
warnings
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
if (canonicalGatewayInstanceId && (canAdoptLegacyGateway || !existingGateway)) {
|
|
97
|
-
await gatewayStore.register(ACPX_GATEWAY_INSTANCE_KEY, {
|
|
98
|
-
instanceId: canonicalGatewayInstanceId,
|
|
99
|
-
createdAt: Date.now()
|
|
100
|
-
});
|
|
101
|
-
changes.push("Migrated ACPX gateway instance id -> plugin state");
|
|
102
|
-
} else if (gatewayInstanceId && existingGateway?.instanceId !== gatewayInstanceId) warnings.push("Skipped ACPX gateway instance id import because plugin state already differs");
|
|
103
|
-
if (gatewayInstanceId) await (0, runtime_doctor_migrations_exports.archiveLegacyStateSource)({
|
|
104
|
-
filePath: gatewayInstancePath,
|
|
105
|
-
label: "ACPX gateway-instance-id",
|
|
106
|
-
changes,
|
|
107
|
-
warnings
|
|
108
|
-
});
|
|
109
|
-
if (openLeases.length > 0) {
|
|
110
|
-
let imported = 0;
|
|
111
|
-
let alreadyPresent = 0;
|
|
112
|
-
for (const lease of openLeases) if (await processLeaseStore.registerIfAbsent(lease.leaseId, lease)) imported++;
|
|
113
|
-
else alreadyPresent++;
|
|
114
|
-
changes.push(`Migrated ACPX process leases -> plugin state (${imported} imported, ${alreadyPresent} already present)`);
|
|
115
|
-
await (0, runtime_doctor_migrations_exports.archiveLegacyStateSource)({
|
|
116
|
-
filePath: processLeasePath,
|
|
117
|
-
label: "ACPX process-leases",
|
|
118
|
-
changes,
|
|
119
|
-
warnings
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
return {
|
|
123
|
-
changes,
|
|
124
|
-
warnings
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
}, {
|
|
128
38
|
id: "acpx-session-owner-resources",
|
|
129
39
|
label: "ACP session owners",
|
|
130
40
|
doctorOnly: true,
|
|
131
41
|
phase: "after-session-repair",
|
|
132
42
|
async collectBackupResources(input) {
|
|
133
|
-
return (await import("./.setup/session-owner-migration-
|
|
43
|
+
return (await import("./.setup/session-owner-migration-C73lfijV.mjs")).acpxSessionOwnerMigration.collectBackupResources(input);
|
|
134
44
|
},
|
|
135
45
|
async detectLegacyState(input) {
|
|
136
|
-
return (await import("./.setup/session-owner-migration-
|
|
46
|
+
return (await import("./.setup/session-owner-migration-C73lfijV.mjs")).acpxSessionOwnerMigration.detectLegacyState(input);
|
|
137
47
|
},
|
|
138
48
|
async migrateLegacyState(input) {
|
|
139
|
-
return (await import("./.setup/session-owner-migration-
|
|
49
|
+
return (await import("./.setup/session-owner-migration-C73lfijV.mjs")).acpxSessionOwnerMigration.migrateLegacyState(input);
|
|
140
50
|
}
|
|
141
51
|
}];
|
|
142
52
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { t as createAcpxRuntimeService } from "./.setup/register.runtime-
|
|
2
|
-
import {
|
|
1
|
+
import { t as createAcpxRuntimeService } from "./.setup/register.runtime-C0nVOTzT.mjs";
|
|
2
|
+
import { g as AcpxNativeAgentsSchema, h as ACPX_NATIVE_AGENT_IDS, t as resolveAcpxPluginConfig } from "./.setup/config-76krYh6C.mjs";
|
|
3
3
|
import { a as PI_SESSIONS_CAPABILITY, c as PI_SESSION_READ_COMMAND, l as PI_TERMINAL_RESUME_COMMAND, o as PI_SESSIONS_LIST_COMMAND, r as piSessionStoreAvailable, s as PI_SESSION_ID_PATTERN } from "./.setup/pi-session-paths-CIvyk6KB.mjs";
|
|
4
4
|
import { createAgentRegistry } from "acpx/agent-registry";
|
|
5
5
|
import { tryDispatchAcpReplyHook } from "openclaw/plugin-sdk/acp-runtime-backend";
|
|
6
6
|
import { createLazyRuntimeModule, createLazyRuntimeSurface } from "openclaw/plugin-sdk/lazy-runtime";
|
|
7
7
|
import { inspectAgentModels } from "acpx/runtime";
|
|
8
|
+
import { createNativeSessionCommitFinalizer } from "openclaw/plugin-sdk/agent-harness-session-runtime";
|
|
8
9
|
import { finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
|
|
9
10
|
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
10
11
|
import { resolveNodeHostExecutable } from "openclaw/plugin-sdk/node-host";
|
|
@@ -227,14 +228,14 @@ function createAcpAgentHarness(params) {
|
|
|
227
228
|
async withSessionDeletion(input, run) {
|
|
228
229
|
let committed = false;
|
|
229
230
|
try {
|
|
230
|
-
return await run({
|
|
231
|
+
return await run(createNativeSessionCommitFinalizer({
|
|
231
232
|
commit: () => {
|
|
232
233
|
committed = true;
|
|
233
234
|
},
|
|
234
235
|
rollback: () => {
|
|
235
236
|
committed = false;
|
|
236
237
|
}
|
|
237
|
-
});
|
|
238
|
+
}));
|
|
238
239
|
} finally {
|
|
239
240
|
if (committed) await retire(input, input.assertCurrent);
|
|
240
241
|
}
|
|
@@ -247,7 +248,7 @@ function createAcpAgentHarness(params) {
|
|
|
247
248
|
}
|
|
248
249
|
//#endregion
|
|
249
250
|
//#region extensions/acpx/src/pi-session-catalog-plugin.ts
|
|
250
|
-
const loadPiSessionCatalogModule = createLazyRuntimeModule(() => import("./.setup/pi-session-catalog-runtime-
|
|
251
|
+
const loadPiSessionCatalogModule = createLazyRuntimeModule(() => import("./.setup/pi-session-catalog-runtime-7jc50ZJ4.mjs"));
|
|
251
252
|
function fullConfigCatalogEnabled(config) {
|
|
252
253
|
if (!isRecord(config) || !isRecord(config.plugins) || !isRecord(config.plugins.entries)) return true;
|
|
253
254
|
const entry = config.plugins.entries.acpx;
|
package/dist/register.runtime.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as createAcpxRuntimeService } from "./.setup/register.runtime-
|
|
1
|
+
import { t as createAcpxRuntimeService } from "./.setup/register.runtime-C0nVOTzT.mjs";
|
|
2
2
|
export { createAcpxRuntimeService };
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/acpx",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.10.1-beta.2",
|
|
4
4
|
"description": "OpenClaw ACP runtime backend with plugin-owned session and transport management.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@agentclientprotocol/claude-agent-acp": "0.79.0",
|
|
12
12
|
"@agentclientprotocol/codex-acp": "1.12.0",
|
|
13
|
-
"@openclaw/fs-safe": "0.
|
|
13
|
+
"@openclaw/fs-safe": "0.23.0",
|
|
14
14
|
"acpx": "0.19.1",
|
|
15
15
|
"smol-toml": "1.8.0",
|
|
16
16
|
"zod": "4.6.5"
|
|
@@ -45,20 +45,10 @@
|
|
|
45
45
|
]
|
|
46
46
|
},
|
|
47
47
|
"compat": {
|
|
48
|
-
"pluginApi": ">=2026.
|
|
48
|
+
"pluginApi": ">=2026.10.1-beta.2"
|
|
49
49
|
},
|
|
50
50
|
"build": {
|
|
51
|
-
"openclawVersion": "2026.
|
|
52
|
-
"staticAssets": [
|
|
53
|
-
{
|
|
54
|
-
"source": "./src/runtime-internals/mcp-proxy.mjs",
|
|
55
|
-
"output": "mcp-proxy.mjs"
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
"source": "./src/runtime-internals/mcp-command-line.mjs",
|
|
59
|
-
"output": "mcp-command-line.mjs"
|
|
60
|
-
}
|
|
61
|
-
]
|
|
51
|
+
"openclawVersion": "2026.10.1-beta.2"
|
|
62
52
|
},
|
|
63
53
|
"release": {
|
|
64
54
|
"publishToClawHub": true,
|
|
@@ -78,7 +68,7 @@
|
|
|
78
68
|
"skills/**"
|
|
79
69
|
],
|
|
80
70
|
"peerDependencies": {
|
|
81
|
-
"openclaw": ">=2026.
|
|
71
|
+
"openclaw": ">=2026.10.1-beta.2"
|
|
82
72
|
},
|
|
83
73
|
"peerDependenciesMeta": {
|
|
84
74
|
"openclaw": {
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
2
|
-
//#region extensions/acpx/src/codex-adapter.ts
|
|
3
|
-
const CODEX_ACP_PACKAGE = "@agentclientprotocol/codex-acp";
|
|
4
|
-
const CODEX_ACP_BIN = "codex-acp";
|
|
5
|
-
const LEGACY_CODEX_ACP_PACKAGE = "@zed-industries/codex-acp";
|
|
6
|
-
const OPENCLAW_CODEX_CONFIG_ARG = "--openclaw-codex-config";
|
|
7
|
-
//#endregion
|
|
8
|
-
//#region extensions/acpx/src/command-line.ts
|
|
9
|
-
/** Match ACPX's persisted argv identity; scalar records keep their original bytes. */
|
|
10
|
-
function renderAgentCommand(command) {
|
|
11
|
-
return typeof command === "string" ? command : command.map((part) => /^[A-Za-z0-9_@%+=:,./^~-]+$/.test(part) ? part : JSON.stringify(part)).join(" ");
|
|
12
|
-
}
|
|
13
|
-
/** Split a command string into argv-like parts using simple quote/backslash rules. */
|
|
14
|
-
function splitCommandParts(value) {
|
|
15
|
-
if (Array.isArray(value)) return value;
|
|
16
|
-
const windows = process.platform === "win32";
|
|
17
|
-
const parts = [];
|
|
18
|
-
let current = "";
|
|
19
|
-
let quote = null;
|
|
20
|
-
let escaping = false;
|
|
21
|
-
let hasPart = false;
|
|
22
|
-
for (const ch of value) {
|
|
23
|
-
if (escaping) {
|
|
24
|
-
current += ch;
|
|
25
|
-
escaping = false;
|
|
26
|
-
hasPart = true;
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
if (ch === "\\" && quote !== "'" && !windows) {
|
|
30
|
-
escaping = true;
|
|
31
|
-
hasPart = true;
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
if (windows && ch === "\"" && quote !== "'") {
|
|
35
|
-
const backslashes = current.match(/\\+$/)?.[0].length ?? 0;
|
|
36
|
-
current = current.slice(0, current.length - backslashes) + "\\".repeat(Math.floor(backslashes / 2));
|
|
37
|
-
if (backslashes % 2 === 1) {
|
|
38
|
-
current += "\"";
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
if (quote) {
|
|
43
|
-
if (ch === quote) quote = null;
|
|
44
|
-
else current += ch;
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (ch === "'" || ch === "\"") {
|
|
48
|
-
quote = ch;
|
|
49
|
-
hasPart = true;
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
if (/\s/.test(ch)) {
|
|
53
|
-
if (hasPart) {
|
|
54
|
-
parts.push(current);
|
|
55
|
-
current = "";
|
|
56
|
-
hasPart = false;
|
|
57
|
-
}
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
current += ch;
|
|
61
|
-
hasPart = true;
|
|
62
|
-
}
|
|
63
|
-
if (escaping) current += "\\";
|
|
64
|
-
if (quote) throw new Error("Invalid agent command: unterminated quote");
|
|
65
|
-
if (hasPart) parts.push(current);
|
|
66
|
-
return parts;
|
|
67
|
-
}
|
|
68
|
-
const OPENCLAW_BRIDGE_EXECUTABLE = "openclaw";
|
|
69
|
-
const OPENCLAW_BRIDGE_SUBCOMMAND = "acp";
|
|
70
|
-
function basename(value) {
|
|
71
|
-
return value.split(/[\\/]/).pop() ?? value;
|
|
72
|
-
}
|
|
73
|
-
function isEnvAssignment(value) {
|
|
74
|
-
return /^[A-Za-z_][A-Za-z0-9_]*=/.test(value);
|
|
75
|
-
}
|
|
76
|
-
function unwrapEnvCommand(parts) {
|
|
77
|
-
const command = parts.at(0);
|
|
78
|
-
if (!command || basename(command) !== "env") return parts;
|
|
79
|
-
let index = 1;
|
|
80
|
-
while (true) {
|
|
81
|
-
const part = parts.at(index);
|
|
82
|
-
if (!part || !isEnvAssignment(part)) break;
|
|
83
|
-
index += 1;
|
|
84
|
-
}
|
|
85
|
-
return parts.slice(index);
|
|
86
|
-
}
|
|
87
|
-
function matchesExecutableName(value, executableName) {
|
|
88
|
-
const normalized = basename(value).toLowerCase();
|
|
89
|
-
return normalized === executableName || normalized === `${executableName}.exe`;
|
|
90
|
-
}
|
|
91
|
-
function matchesPackageSpec(value, packageName) {
|
|
92
|
-
const normalized = value.trim().toLowerCase();
|
|
93
|
-
return normalized === packageName || normalized.startsWith(`${packageName}@`);
|
|
94
|
-
}
|
|
95
|
-
function stripModuleExtension(value) {
|
|
96
|
-
return value.replace(/\.[cm]?js$/i, "").toLowerCase();
|
|
97
|
-
}
|
|
98
|
-
function isAcpCommand(command, params) {
|
|
99
|
-
if (!command) return false;
|
|
100
|
-
const parts = unwrapEnvCommand(splitCommandParts(command));
|
|
101
|
-
if (!parts.length) return false;
|
|
102
|
-
if (parts.some((part) => matchesPackageSpec(part, params.packageName))) return true;
|
|
103
|
-
const commandName = basename(parts[0] ?? "");
|
|
104
|
-
if (matchesExecutableName(commandName, params.executableName)) return true;
|
|
105
|
-
if (!matchesExecutableName(commandName, "node")) return false;
|
|
106
|
-
const scriptName = stripModuleExtension(basename(parts[1] ?? ""));
|
|
107
|
-
return scriptName === params.executableName || scriptName === `${params.executableName}-wrapper`;
|
|
108
|
-
}
|
|
109
|
-
function isOpenClawBridgeCommand(command) {
|
|
110
|
-
if (!command) return false;
|
|
111
|
-
const parts = unwrapEnvCommand(splitCommandParts(command));
|
|
112
|
-
if (basename(parts[0] ?? "") === OPENCLAW_BRIDGE_EXECUTABLE) return parts[1] === OPENCLAW_BRIDGE_SUBCOMMAND;
|
|
113
|
-
if (basename(parts[0] ?? "") !== "node") return false;
|
|
114
|
-
const scriptName = basename(parts[1] ?? "");
|
|
115
|
-
return /^openclaw(?:\.[cm]?js)?$/i.test(scriptName) && parts[2] === OPENCLAW_BRIDGE_SUBCOMMAND;
|
|
116
|
-
}
|
|
117
|
-
function isCodexAcpCommand(command) {
|
|
118
|
-
return isAcpCommand(command, {
|
|
119
|
-
packageName: CODEX_ACP_PACKAGE,
|
|
120
|
-
executableName: "codex-acp"
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
function isClaudeAcpCommand(command) {
|
|
124
|
-
return isAcpCommand(command, {
|
|
125
|
-
packageName: "@agentclientprotocol/claude-agent-acp",
|
|
126
|
-
executableName: "claude-agent-acp"
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
function resolveAgentCommand(params) {
|
|
130
|
-
const normalizedAgentName = normalizeOptionalLowercaseString(params.agentName);
|
|
131
|
-
if (!normalizedAgentName) return;
|
|
132
|
-
return splitCommandParts(params.agentRegistry.resolve(normalizedAgentName));
|
|
133
|
-
}
|
|
134
|
-
//#endregion
|
|
135
|
-
export { resolveAgentCommand as a, CODEX_ACP_PACKAGE as c, renderAgentCommand as i, LEGACY_CODEX_ACP_PACKAGE as l, isCodexAcpCommand as n, splitCommandParts as o, isOpenClawBridgeCommand as r, CODEX_ACP_BIN as s, isClaudeAcpCommand as t, OPENCLAW_CODEX_CONFIG_ARG as u };
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Command-line parser for ACPX MCP proxy targets. It handles simple quoting and
|
|
3
|
-
* Windows executable paths before spawning the configured MCP target.
|
|
4
|
-
*/
|
|
5
|
-
const WINDOWS_DIRECT_EXECUTABLE_PATH_RE =
|
|
6
|
-
/^(?<command>(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/][^\\/]+[\\/]).*?\.(?:exe|com))(?=\s|$)(?:\s+(?<rest>.*))?$/i;
|
|
7
|
-
|
|
8
|
-
// Windows wrapper scripts need their host shell or interpreter (`cmd.exe`,
|
|
9
|
-
// `powershell.exe`, or `node`) instead of direct spawning.
|
|
10
|
-
const WINDOWS_WRAPPER_PATH_RE =
|
|
11
|
-
/^(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/][^\\/]+[\\/]).*?\.(?:bat|cmd|cjs|js|mjs|ps1)$/i;
|
|
12
|
-
|
|
13
|
-
function splitCommandParts(value, platform = process.platform) {
|
|
14
|
-
const parts = [];
|
|
15
|
-
let current = "";
|
|
16
|
-
let quote = null;
|
|
17
|
-
let escaping = false;
|
|
18
|
-
|
|
19
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
20
|
-
const ch = value[index];
|
|
21
|
-
const next = value[index + 1];
|
|
22
|
-
if (escaping) {
|
|
23
|
-
current += ch;
|
|
24
|
-
escaping = false;
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
if (ch === "\\") {
|
|
28
|
-
if (quote === "'") {
|
|
29
|
-
current += ch;
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
if (platform === "win32") {
|
|
33
|
-
if (quote === '"') {
|
|
34
|
-
if (next === '"' || next === "\\") {
|
|
35
|
-
escaping = true;
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
current += ch;
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
if (!quote) {
|
|
42
|
-
current += ch;
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
escaping = true;
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (quote) {
|
|
50
|
-
if (ch === quote) {
|
|
51
|
-
quote = null;
|
|
52
|
-
} else {
|
|
53
|
-
current += ch;
|
|
54
|
-
}
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
if (ch === "'" || ch === '"') {
|
|
58
|
-
quote = ch;
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
if (/\s/.test(ch)) {
|
|
62
|
-
if (current.length > 0) {
|
|
63
|
-
parts.push(current);
|
|
64
|
-
current = "";
|
|
65
|
-
}
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
current += ch;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (escaping) {
|
|
72
|
-
current += "\\";
|
|
73
|
-
}
|
|
74
|
-
if (quote) {
|
|
75
|
-
throw new Error("Invalid agent command: unterminated quote");
|
|
76
|
-
}
|
|
77
|
-
if (current.length > 0) {
|
|
78
|
-
parts.push(current);
|
|
79
|
-
}
|
|
80
|
-
return parts;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function splitWindowsExecutableCommand(value, platform = process.platform) {
|
|
84
|
-
if (platform !== "win32") {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
const trimmed = value.trim();
|
|
88
|
-
if (!trimmed || trimmed.startsWith('"') || trimmed.startsWith("'")) {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
const match = trimmed.match(WINDOWS_DIRECT_EXECUTABLE_PATH_RE);
|
|
92
|
-
if (!match?.groups?.command) {
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
const rest = match.groups.rest?.trim() ?? "";
|
|
96
|
-
return {
|
|
97
|
-
command: match.groups.command,
|
|
98
|
-
args: rest ? splitCommandParts(rest, platform) : [],
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function assertSupportedWindowsCommand(command, platform = process.platform) {
|
|
103
|
-
if (platform !== "win32" || !WINDOWS_WRAPPER_PATH_RE.test(command)) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
throw new Error(
|
|
107
|
-
`Unsupported Windows agent command wrapper: ${command}. ` +
|
|
108
|
-
"Invoke wrapper scripts through their shell or interpreter instead " +
|
|
109
|
-
"(for example `cmd.exe /c`, `powershell.exe -File`, or `node <script>`).",
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/** Split a configured command string into `{ command, args }` for child_process.spawn. */
|
|
114
|
-
export function splitCommandLine(value, platform = process.platform) {
|
|
115
|
-
const windowsCommand = splitWindowsExecutableCommand(value, platform);
|
|
116
|
-
const parts = windowsCommand ?? splitCommandParts(value, platform);
|
|
117
|
-
if (parts.length === 0) {
|
|
118
|
-
throw new Error("Invalid agent command: empty command");
|
|
119
|
-
}
|
|
120
|
-
const parsed = Array.isArray(parts)
|
|
121
|
-
? {
|
|
122
|
-
command: parts[0],
|
|
123
|
-
args: parts.slice(1),
|
|
124
|
-
}
|
|
125
|
-
: parts;
|
|
126
|
-
assertSupportedWindowsCommand(parsed.command, platform);
|
|
127
|
-
return parsed;
|
|
128
|
-
}
|