@openclaw/acpx 2026.9.7 → 2026.9.8
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/README.md +6 -0
- package/assets/icon.png +0 -0
- package/dist/.setup/{command-line-CPBLOiZM.mjs → command-line-B72femaL.mjs} +3 -6
- package/dist/.setup/{config-CdPsIued.mjs → config-Blz15uwJ.mjs} +15 -15
- package/dist/.setup/{pi-session-catalog-runtime-UhiQSaDw.mjs → pi-session-catalog-runtime-IKEanK3s.mjs} +20 -55
- package/dist/.setup/process-lease-n38HFGdp.mjs +311 -0
- package/dist/.setup/{register.runtime-Bm4HrMCp.mjs → register.runtime-DByczkH_.mjs} +15 -38
- package/dist/.setup/{service-DMae5pPH.mjs → service-D5hGWl5U.mjs} +78 -117
- package/dist/.setup/{session-owner-migration-CdJSYRk1.mjs → session-owner-migration-DsGrYq7Y.mjs} +132 -20
- package/dist/.setup/{session-resource-BaJ6bs5d.mjs → session-resource-B-4yPr-j.mjs} +1 -13
- package/dist/doctor-contract-api.js +13 -17
- package/dist/index.js +40 -20
- package/dist/register.runtime.js +1 -1
- package/package.json +7 -6
- package/skills/acp-router/SKILL.md +2 -2
- package/dist/.setup/process-lease-C3dNPtu8.mjs +0 -138
package/README.md
CHANGED
|
@@ -12,6 +12,12 @@ openclaw plugins install @openclaw/acpx
|
|
|
12
12
|
|
|
13
13
|
Restart the Gateway after installing or updating the plugin.
|
|
14
14
|
|
|
15
|
+
The generated Codex and Claude ACP wrappers prefer the installed adapter while its
|
|
16
|
+
entry file exists. If plugin capture cleanup removes that file, the next launch
|
|
17
|
+
uses the existing pinned `npm exec` / `npx` fallback. This may require registry
|
|
18
|
+
access when the pinned package is not cached. Restart the Gateway after updating
|
|
19
|
+
to regenerate wrappers written by an older plugin version.
|
|
20
|
+
|
|
15
21
|
## What it provides
|
|
16
22
|
|
|
17
23
|
- ACP-backed agent runtime sessions.
|
package/assets/icon.png
CHANGED
|
Binary file
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
1
2
|
//#region extensions/acpx/src/codex-adapter.ts
|
|
2
3
|
const CODEX_ACP_PACKAGE = "@agentclientprotocol/codex-acp";
|
|
3
4
|
const CODEX_ACP_BIN = "codex-acp";
|
|
@@ -66,10 +67,6 @@ function splitCommandParts(value) {
|
|
|
66
67
|
}
|
|
67
68
|
const OPENCLAW_BRIDGE_EXECUTABLE = "openclaw";
|
|
68
69
|
const OPENCLAW_BRIDGE_SUBCOMMAND = "acp";
|
|
69
|
-
function normalizeAgentName(value) {
|
|
70
|
-
const normalized = value?.trim().toLowerCase();
|
|
71
|
-
return normalized ? normalized : void 0;
|
|
72
|
-
}
|
|
73
70
|
function basename(value) {
|
|
74
71
|
return value.split(/[\\/]/).pop() ?? value;
|
|
75
72
|
}
|
|
@@ -130,9 +127,9 @@ function isClaudeAcpCommand(command) {
|
|
|
130
127
|
});
|
|
131
128
|
}
|
|
132
129
|
function resolveAgentCommand(params) {
|
|
133
|
-
const normalizedAgentName =
|
|
130
|
+
const normalizedAgentName = normalizeOptionalLowercaseString(params.agentName);
|
|
134
131
|
if (!normalizedAgentName) return;
|
|
135
132
|
return splitCommandParts(params.agentRegistry.resolve(normalizedAgentName));
|
|
136
133
|
}
|
|
137
134
|
//#endregion
|
|
138
|
-
export {
|
|
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,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as splitCommandParts } from "./command-line-B72femaL.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { formatPluginConfigIssue } from "openclaw/plugin-sdk/extension-shared";
|
|
8
|
+
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
8
9
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
9
10
|
//#region extensions/acpx/src/config-schema.ts
|
|
10
11
|
/**
|
|
@@ -57,12 +58,15 @@ const AcpxPluginConfigSchema = z.strictObject({
|
|
|
57
58
|
});
|
|
58
59
|
//#endregion
|
|
59
60
|
//#region extensions/acpx/src/config.ts
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
const MANAGED_MCP_BRIDGES = [[
|
|
62
|
+
"pluginToolsMcpBridge",
|
|
63
|
+
"openclaw-plugin-tools",
|
|
64
|
+
"plugin-tools-serve"
|
|
65
|
+
], [
|
|
66
|
+
"openClawToolsMcpBridge",
|
|
67
|
+
"openclaw-tools",
|
|
68
|
+
"openclaw-tools-serve"
|
|
69
|
+
]];
|
|
66
70
|
const requireFromHere = createRequire(import.meta.url);
|
|
67
71
|
function isAcpxPluginRoot(dir) {
|
|
68
72
|
return fs.existsSync(path.join(dir, "openclaw.plugin.json")) && fs.existsSync(path.join(dir, "package.json"));
|
|
@@ -142,13 +146,10 @@ function resolveManagedToolsMcpServerConfig(entryPoint, moduleUrl = import.meta.
|
|
|
142
146
|
}
|
|
143
147
|
function resolveConfiguredMcpServers(params) {
|
|
144
148
|
const resolved = { ...params.mcpServers };
|
|
145
|
-
if (params
|
|
146
|
-
|
|
147
|
-
if (params.pluginToolsMcpBridge) resolved[ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME] = resolveManagedToolsMcpServerConfig("plugin-tools-serve", params.moduleUrl);
|
|
148
|
-
if (params.openClawToolsMcpBridge) resolved[ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME] = resolveManagedToolsMcpServerConfig("openclaw-tools-serve", params.moduleUrl);
|
|
149
|
+
for (const [flag, name] of MANAGED_MCP_BRIDGES) if (params[flag] && resolved[name]) throw new Error(`mcpServers.${name} is reserved when ${flag}=true`);
|
|
150
|
+
for (const [flag, name, entryPoint] of MANAGED_MCP_BRIDGES) if (params[flag]) resolved[name] = resolveManagedToolsMcpServerConfig(entryPoint, params.moduleUrl);
|
|
149
151
|
return resolved;
|
|
150
152
|
}
|
|
151
|
-
/** Convert OpenClaw MCP server config into ACPX runtime MCP server entries. */
|
|
152
153
|
function toAcpMcpServers(mcpServers) {
|
|
153
154
|
return Object.entries(mcpServers).map(([name, server]) => ({
|
|
154
155
|
name,
|
|
@@ -160,15 +161,14 @@ function toAcpMcpServers(mcpServers) {
|
|
|
160
161
|
}))
|
|
161
162
|
}));
|
|
162
163
|
}
|
|
163
|
-
/** Validate and normalize raw ACPX plugin config for runtime startup. */
|
|
164
164
|
function resolveAcpxPluginConfig(params) {
|
|
165
165
|
const { rawConfig } = params;
|
|
166
166
|
const parsed = AcpxPluginConfigSchema.safeParse(rawConfig === void 0 ? {} : rawConfig);
|
|
167
167
|
if (!parsed.success) throw new Error(formatPluginConfigIssue(parsed.error.issues[0]));
|
|
168
168
|
const normalized = parsed.data;
|
|
169
169
|
const workspaceDir = params.workspaceDir?.trim() || process.cwd();
|
|
170
|
-
const cwd = path.resolve(normalized.cwd
|
|
171
|
-
const stateDir = path.resolve(normalized.stateDir
|
|
170
|
+
const cwd = path.resolve(normalized.cwd ?? workspaceDir);
|
|
171
|
+
const stateDir = path.resolve(normalized.stateDir ?? path.join(params.stateDir ?? resolveStateDir(), "acpx"));
|
|
172
172
|
const pluginToolsMcpBridge = normalized.pluginToolsMcpBridge === true;
|
|
173
173
|
const openClawToolsMcpBridge = normalized.openClawToolsMcpBridge === true;
|
|
174
174
|
const mcpServers = resolveConfiguredMcpServers({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as PI_SESSION_READ_COMMAND, i as PI_LOCAL_SESSION_HOST_ID, l as PI_TERMINAL_RESUME_COMMAND, n as piSessionStore, o as PI_SESSIONS_LIST_COMMAND, r as piSessionStoreAvailable, s as PI_SESSION_ID_PATTERN, t as piAcpSessionStoreRoot } from "./pi-session-paths-CIvyk6KB.mjs";
|
|
2
|
-
import { parseDateFirstTimestampMs } from "openclaw/plugin-sdk/number-runtime";
|
|
2
|
+
import { parseDateFirstTimestampMs, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
|
|
3
3
|
import { createReadStream } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { isRecord, normalizeBoundedOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
@@ -10,15 +10,9 @@ import { resolveAcpSessionAvailability } from "openclaw/plugin-sdk/acp-runtime";
|
|
|
10
10
|
import process from "node:process";
|
|
11
11
|
import { resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
|
|
12
12
|
import { sessionCatalogPaging } from "openclaw/plugin-sdk/session-catalog-paging";
|
|
13
|
-
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
13
|
+
import { safeParseJson, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
14
14
|
import { runTasksWithConcurrency } from "openclaw/plugin-sdk/concurrency-runtime";
|
|
15
15
|
import { isPathStrictlyInside, readFileRangeAsync } from "openclaw/plugin-sdk/file-access-runtime";
|
|
16
|
-
//#region extensions/acpx/src/pi-session-timestamp.ts
|
|
17
|
-
/** Preserve Pi JSONL's date-first string contract while accepting numeric millisecond values. */
|
|
18
|
-
function parsePiSessionTimestampMs(value) {
|
|
19
|
-
return parseDateFirstTimestampMs(value);
|
|
20
|
-
}
|
|
21
|
-
//#endregion
|
|
22
16
|
//#region extensions/acpx/src/pi-session-store.ts
|
|
23
17
|
const MAX_DISCOVERY_FILES = 1e4;
|
|
24
18
|
const SUMMARY_SCAN_BATCH_SIZE = 100;
|
|
@@ -29,7 +23,6 @@ const APPEND_PROOF_EDGE_BYTES = 65536;
|
|
|
29
23
|
const IO_CONCURRENCY = 8;
|
|
30
24
|
const PI_FILE_CANDIDATE_CACHE_TTL_MS = 32e3;
|
|
31
25
|
const PI_FILE_CANDIDATE_CACHE_MAX_ENTRIES = 8;
|
|
32
|
-
const SESSION_ID_PATTERN = /^(?!-)[A-Za-z0-9._:-]{1,256}$/u;
|
|
33
26
|
const summaryCache = /* @__PURE__ */ new Map();
|
|
34
27
|
const threadFileCache = /* @__PURE__ */ new Map();
|
|
35
28
|
const piFileCandidateCache = /* @__PURE__ */ new Map();
|
|
@@ -153,15 +146,11 @@ async function piFileCandidates(env) {
|
|
|
153
146
|
function parsePiJsonLines(content) {
|
|
154
147
|
return content.split(/\r?\n/u).flatMap((line) => {
|
|
155
148
|
if (!line.trim()) return [];
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return isRecord(value) ? [value] : [];
|
|
159
|
-
} catch {
|
|
160
|
-
return [];
|
|
161
|
-
}
|
|
149
|
+
const value = safeParseJson(line);
|
|
150
|
+
return isRecord(value) ? [value] : [];
|
|
162
151
|
});
|
|
163
152
|
}
|
|
164
|
-
function
|
|
153
|
+
function piMessageText(content) {
|
|
165
154
|
if (typeof content === "string") return content;
|
|
166
155
|
if (!Array.isArray(content)) return "";
|
|
167
156
|
return content.flatMap((part) => isRecord(part) && part.type === "text" && typeof part.text === "string" ? [part.text] : []).join("\n");
|
|
@@ -178,7 +167,7 @@ function processSummaryLine(state, line) {
|
|
|
178
167
|
return;
|
|
179
168
|
}
|
|
180
169
|
if (entry.type === "session_info") state.name = normalizeBoundedOptionalString(entry.name, 1e3);
|
|
181
|
-
else if (!state.firstMessage && entry.type === "message" && isRecord(entry.message) && entry.message.role === "user") state.firstMessage = normalizeBoundedOptionalString(
|
|
170
|
+
else if (!state.firstMessage && entry.type === "message" && isRecord(entry.message) && entry.message.role === "user") state.firstMessage = normalizeBoundedOptionalString(piMessageText(entry.message.content), 1e3);
|
|
182
171
|
}
|
|
183
172
|
function appendSummaryBytes(state, bytes) {
|
|
184
173
|
if (state.discarding || bytes.length === 0) return;
|
|
@@ -268,9 +257,9 @@ async function readPiSessionSummary(candidate) {
|
|
|
268
257
|
const { header, name, firstMessage } = projectedState;
|
|
269
258
|
const version = header?.type === "session" && typeof header.version === "number" ? header.version : 1;
|
|
270
259
|
const threadId = header?.type === "session" ? normalizeBoundedOptionalString(header.id, 256) : void 0;
|
|
271
|
-
if (header && threadId &&
|
|
260
|
+
if (header && threadId && PI_SESSION_ID_PATTERN.test(threadId)) {
|
|
272
261
|
const cwd = normalizeBoundedOptionalString(header.cwd, 4096);
|
|
273
|
-
const createdAt =
|
|
262
|
+
const createdAt = parseDateFirstTimestampMs(header.timestamp);
|
|
274
263
|
summary = {
|
|
275
264
|
file: candidate.file,
|
|
276
265
|
version,
|
|
@@ -383,7 +372,7 @@ async function readPiSessionById(threadId, env) {
|
|
|
383
372
|
//#region extensions/acpx/src/pi-session-catalog.ts
|
|
384
373
|
const MAX_SEARCH_LENGTH = 500;
|
|
385
374
|
const isExactPiSessionCursor = sessionCatalogPaging.isExactCursor;
|
|
386
|
-
function textFromContent
|
|
375
|
+
function textFromContent(content) {
|
|
387
376
|
if (typeof content === "string") return content;
|
|
388
377
|
if (!Array.isArray(content)) return "";
|
|
389
378
|
return content.flatMap((part) => {
|
|
@@ -421,12 +410,6 @@ async function listLocalPiSessionPage(value) {
|
|
|
421
410
|
...hasMore ? { nextCursor: sessionCatalogPaging.encodeCursor(offset + page.length) } : {}
|
|
422
411
|
};
|
|
423
412
|
}
|
|
424
|
-
function isoTimestamp(message, entry) {
|
|
425
|
-
const value = parsePiSessionTimestampMs(message.timestamp) ?? parsePiSessionTimestampMs(entry.timestamp);
|
|
426
|
-
if (value === void 0) return;
|
|
427
|
-
const date = new Date(value);
|
|
428
|
-
return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
|
|
429
|
-
}
|
|
430
413
|
function jsonText(value, maxLength = 2e4) {
|
|
431
414
|
try {
|
|
432
415
|
const text = JSON.stringify(value);
|
|
@@ -458,7 +441,7 @@ function piMessageItems(entry) {
|
|
|
458
441
|
const message = entry.message;
|
|
459
442
|
const role = message.role;
|
|
460
443
|
const id = normalizeBoundedOptionalString(entry.id, 256);
|
|
461
|
-
const timestamp =
|
|
444
|
+
const timestamp = timestampMsToIsoString(parseDateFirstTimestampMs(message.timestamp) ?? parseDateFirstTimestampMs(entry.timestamp));
|
|
462
445
|
const model = normalizeBoundedOptionalString(message.model, 256);
|
|
463
446
|
const provider = normalizeBoundedOptionalString(message.provider, 256);
|
|
464
447
|
const modelRef = provider && model ? `${provider}/${model}` : model;
|
|
@@ -468,7 +451,7 @@ function piMessageItems(entry) {
|
|
|
468
451
|
...modelRef ? { model: modelRef } : {}
|
|
469
452
|
};
|
|
470
453
|
if (role === "user") {
|
|
471
|
-
const text = textFromContent
|
|
454
|
+
const text = textFromContent(message.content);
|
|
472
455
|
return text ? [{
|
|
473
456
|
...common,
|
|
474
457
|
type: "userMessage",
|
|
@@ -477,7 +460,7 @@ function piMessageItems(entry) {
|
|
|
477
460
|
}
|
|
478
461
|
if (role === "toolResult") {
|
|
479
462
|
const toolName = normalizeBoundedOptionalString(message.toolName, 256);
|
|
480
|
-
const text = textFromContent
|
|
463
|
+
const text = textFromContent(message.content);
|
|
481
464
|
return [{
|
|
482
465
|
...common,
|
|
483
466
|
type: "toolResult",
|
|
@@ -502,7 +485,7 @@ function piMessageItems(entry) {
|
|
|
502
485
|
if (role === "custom" || role === "hookMessage") {
|
|
503
486
|
if (message.display !== true) return [];
|
|
504
487
|
const customType = normalizeBoundedOptionalString(message.customType, 256);
|
|
505
|
-
const text = textFromContent
|
|
488
|
+
const text = textFromContent(message.content);
|
|
506
489
|
return text ? [{
|
|
507
490
|
...common,
|
|
508
491
|
type: "other",
|
|
@@ -547,18 +530,13 @@ function piTranscriptItems(entries) {
|
|
|
547
530
|
...id ? { id } : {},
|
|
548
531
|
...timestamp ? { timestamp } : {}
|
|
549
532
|
};
|
|
550
|
-
if (entry.type === "compaction" && typeof entry.summary === "string") return [{
|
|
551
|
-
...common,
|
|
552
|
-
type: "other",
|
|
553
|
-
text: entry.summary
|
|
554
|
-
}];
|
|
555
|
-
if (entry.type === "branch_summary" && typeof entry.summary === "string") return [{
|
|
533
|
+
if ((entry.type === "compaction" || entry.type === "branch_summary") && typeof entry.summary === "string") return [{
|
|
556
534
|
...common,
|
|
557
535
|
type: "other",
|
|
558
536
|
text: entry.summary
|
|
559
537
|
}];
|
|
560
538
|
if (entry.type === "custom_message" && entry.display === true) {
|
|
561
|
-
const text = textFromContent
|
|
539
|
+
const text = textFromContent(entry.content);
|
|
562
540
|
return text ? [{
|
|
563
541
|
...common,
|
|
564
542
|
type: "other",
|
|
@@ -594,12 +572,10 @@ function parseCompletePiRows(tail) {
|
|
|
594
572
|
for (let index = 0; index < tail.length; index += 1) {
|
|
595
573
|
if (tail[index] !== 10) continue;
|
|
596
574
|
const line = tail.subarray(lineStart, index).toString("utf8").trim();
|
|
597
|
-
if (line)
|
|
598
|
-
const value =
|
|
575
|
+
if (line) {
|
|
576
|
+
const value = safeParseJson(line);
|
|
599
577
|
if (!isRecord(value)) break;
|
|
600
578
|
entries.push(value);
|
|
601
|
-
} catch {
|
|
602
|
-
break;
|
|
603
579
|
}
|
|
604
580
|
classifiedBytes = index + 1;
|
|
605
581
|
lineStart = index + 1;
|
|
@@ -609,11 +585,6 @@ function parseCompletePiRows(tail) {
|
|
|
609
585
|
classifiedBytes
|
|
610
586
|
};
|
|
611
587
|
}
|
|
612
|
-
function textFromContent(content) {
|
|
613
|
-
if (typeof content === "string") return content;
|
|
614
|
-
if (!Array.isArray(content)) return;
|
|
615
|
-
return content.flatMap((part) => isRecord(part) && part.type === "text" && typeof part.text === "string" ? [part.text] : []).join("\n") || void 0;
|
|
616
|
-
}
|
|
617
588
|
function readFilePath(probe) {
|
|
618
589
|
return isRecord(probe.upstreamRef) && typeof probe.upstreamRef.filePath === "string" ? probe.upstreamRef.filePath : void 0;
|
|
619
590
|
}
|
|
@@ -663,10 +634,10 @@ async function checkPiSessionUpstreamActivity(probe) {
|
|
|
663
634
|
let occurredAt;
|
|
664
635
|
for (const entry of entries) {
|
|
665
636
|
if (entry.type !== "message" || !isRecord(entry.message) || entry.message.role !== "user") continue;
|
|
666
|
-
const text =
|
|
637
|
+
const text = piMessageText(entry.message.content);
|
|
667
638
|
if (!isExternalUserText(probe, text)) continue;
|
|
668
639
|
humanTurns += 1;
|
|
669
|
-
occurredAt = Math.max(occurredAt ?? 0,
|
|
640
|
+
occurredAt = Math.max(occurredAt ?? 0, parseDateFirstTimestampMs(entry.message.timestamp) ?? parseDateFirstTimestampMs(entry.timestamp) ?? stat.mtimeMs);
|
|
670
641
|
}
|
|
671
642
|
const nextOffset = markerOffset + classifiedBytes;
|
|
672
643
|
return {
|
|
@@ -781,12 +752,6 @@ async function createAdoptedPiSession(params) {
|
|
|
781
752
|
function assertPiLocalAccess(hostId, allowProcessHomeFallback) {
|
|
782
753
|
if (hostId === "gateway" && allowProcessHomeFallback === false && piSessionStore(process.env).usesProcessHomeFallback) throw new Error("local Pi sessions are unavailable in isolated state");
|
|
783
754
|
}
|
|
784
|
-
async function listPiSessions(params) {
|
|
785
|
-
return await listLocalPiSessionPage(params);
|
|
786
|
-
}
|
|
787
|
-
async function readPiSession(params) {
|
|
788
|
-
return await readLocalPiTranscriptPage(params);
|
|
789
|
-
}
|
|
790
755
|
function createPiSessionCatalogRuntime(api) {
|
|
791
756
|
return createSessionCatalogFamily({
|
|
792
757
|
runtime: api.runtime,
|
|
@@ -880,4 +845,4 @@ function createPiSessionCatalogRuntime(api) {
|
|
|
880
845
|
}, isExactPiSessionCursor);
|
|
881
846
|
}
|
|
882
847
|
//#endregion
|
|
883
|
-
export { createPiSessionCatalogRuntime, listPiSessions, readPiSession, requireLocalPiSession };
|
|
848
|
+
export { createPiSessionCatalogRuntime, listLocalPiSessionPage as listPiSessions, readLocalPiTranscriptPage as readPiSession, requireLocalPiSession };
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { i as renderAgentCommand, o as splitCommandParts } from "./command-line-B72femaL.mjs";
|
|
2
|
+
import "node:module";
|
|
3
|
+
import { asFiniteNumber, asOptionalObjectRecord, asRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
//#region \0rolldown/runtime.js
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __exportAll = (all, no_symbols) => {
|
|
11
|
+
let target = {};
|
|
12
|
+
for (var name in all) __defProp(target, name, {
|
|
13
|
+
get: all[name],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
17
|
+
return target;
|
|
18
|
+
};
|
|
19
|
+
var __copyProps = (to, from, except, desc) => {
|
|
20
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
21
|
+
key = keys[i];
|
|
22
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
23
|
+
get: ((k) => from[k]).bind(null, key),
|
|
24
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region packages/normalization-core/src/record-coerce.ts
|
|
32
|
+
/** Type guard for non-array object records at browser-safe boundaries. */
|
|
33
|
+
function isRecord$1(value) {
|
|
34
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
35
|
+
}
|
|
36
|
+
/** Returns a non-array record or null. */
|
|
37
|
+
function asNullableRecord(value) {
|
|
38
|
+
return isRecord$1(value) ? value : null;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/config/channel-config-normalization.ts
|
|
42
|
+
/** Applies one channel-specific doctor migration to every object-shaped account. */
|
|
43
|
+
function normalizeChannelAccounts(params) {
|
|
44
|
+
const rawAccounts = asNullableRecord(params.entry.accounts);
|
|
45
|
+
if (!rawAccounts) return {
|
|
46
|
+
entry: params.entry,
|
|
47
|
+
changed: false
|
|
48
|
+
};
|
|
49
|
+
let changed = false;
|
|
50
|
+
const accounts = { ...rawAccounts };
|
|
51
|
+
for (const [accountId, value] of Object.entries(rawAccounts)) {
|
|
52
|
+
const account = asNullableRecord(value);
|
|
53
|
+
if (!account) continue;
|
|
54
|
+
const normalized = params.normalizeAccount({
|
|
55
|
+
account,
|
|
56
|
+
accountId,
|
|
57
|
+
pathPrefix: `${params.pathPrefix}.accounts.${accountId}`,
|
|
58
|
+
changes: params.changes
|
|
59
|
+
});
|
|
60
|
+
if (normalized.changed) {
|
|
61
|
+
accounts[accountId] = normalized.entry;
|
|
62
|
+
changed = true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return changed ? {
|
|
66
|
+
entry: {
|
|
67
|
+
...params.entry,
|
|
68
|
+
accounts
|
|
69
|
+
},
|
|
70
|
+
changed: true
|
|
71
|
+
} : {
|
|
72
|
+
entry: params.entry,
|
|
73
|
+
changed: false
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** Applies the same channel-specific doctor migration at root and account scope. */
|
|
77
|
+
function normalizeChannelConfigEntries(params) {
|
|
78
|
+
const changes = params.changes ?? [];
|
|
79
|
+
const channels = params.cfg.channels;
|
|
80
|
+
const entry = asNullableRecord(channels?.[params.channelId]);
|
|
81
|
+
if (!entry) return {
|
|
82
|
+
config: params.cfg,
|
|
83
|
+
changes
|
|
84
|
+
};
|
|
85
|
+
const channelPath = `channels.${params.channelId}`;
|
|
86
|
+
const root = params.normalizeEntry({
|
|
87
|
+
entry,
|
|
88
|
+
pathPrefix: channelPath,
|
|
89
|
+
changes
|
|
90
|
+
});
|
|
91
|
+
const accounts = normalizeChannelAccounts({
|
|
92
|
+
entry: root.entry,
|
|
93
|
+
pathPrefix: channelPath,
|
|
94
|
+
changes,
|
|
95
|
+
normalizeAccount: (accountParams) => params.normalizeEntry({
|
|
96
|
+
entry: accountParams.account,
|
|
97
|
+
accountId: accountParams.accountId,
|
|
98
|
+
pathPrefix: accountParams.pathPrefix,
|
|
99
|
+
changes: accountParams.changes
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
if (!root.changed && !accounts.changed) return {
|
|
103
|
+
config: params.cfg,
|
|
104
|
+
changes
|
|
105
|
+
};
|
|
106
|
+
return {
|
|
107
|
+
config: {
|
|
108
|
+
...params.cfg,
|
|
109
|
+
channels: {
|
|
110
|
+
...channels,
|
|
111
|
+
[params.channelId]: accounts.entry
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
changes
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/plugin-sdk/legacy-webhook-listener-migration.ts
|
|
119
|
+
/** Preserve explicitly configured webhook listeners while moving ingress onto Gateway routes. */
|
|
120
|
+
function createLegacyWebhookListenerDoctorContract(params) {
|
|
121
|
+
const portKey = params.portKey ?? "webhookPort";
|
|
122
|
+
const hostKey = params.hostKey === void 0 ? "webhookHost" : params.hostKey;
|
|
123
|
+
const source = (entry) => params.webhookKey ? asNullableRecord(entry[params.webhookKey]) : entry;
|
|
124
|
+
const hasLegacy = (value) => {
|
|
125
|
+
const entry = asNullableRecord(value);
|
|
126
|
+
const listener = entry && source(entry);
|
|
127
|
+
return Boolean(listener && (Object.hasOwn(listener, portKey) || hostKey && Object.hasOwn(listener, hostKey)));
|
|
128
|
+
};
|
|
129
|
+
const prefix = `channels.${params.channelKey}`;
|
|
130
|
+
return {
|
|
131
|
+
legacyConfigRules: [{
|
|
132
|
+
path: ["channels", params.channelKey],
|
|
133
|
+
message: `${prefix} webhook listeners moved to Gateway routes. Run "openclaw doctor --fix" to preserve explicitly configured listener settings as legacyWebhook.`,
|
|
134
|
+
match: (value) => {
|
|
135
|
+
const accounts = asNullableRecord(asNullableRecord(value)?.accounts);
|
|
136
|
+
return hasLegacy(value) || Object.values(accounts ?? {}).some(hasLegacy);
|
|
137
|
+
}
|
|
138
|
+
}],
|
|
139
|
+
normalizeCompatibilityConfig: ({ cfg }) => {
|
|
140
|
+
const root = asNullableRecord(asNullableRecord(cfg.channels)?.[params.channelKey]);
|
|
141
|
+
const inherited = root && source(root);
|
|
142
|
+
const canonicalRoot = asNullableRecord(root?.legacyWebhook);
|
|
143
|
+
return normalizeChannelConfigEntries({
|
|
144
|
+
cfg,
|
|
145
|
+
channelId: params.channelKey,
|
|
146
|
+
normalizeEntry: ({ entry, accountId, pathPrefix, changes }) => {
|
|
147
|
+
const listener = source(entry);
|
|
148
|
+
if (!listener || !hasLegacy(entry)) return {
|
|
149
|
+
entry,
|
|
150
|
+
changed: false
|
|
151
|
+
};
|
|
152
|
+
const next = { ...entry };
|
|
153
|
+
const port = Object.hasOwn(listener, portKey) ? listener[portKey] : (accountId ? canonicalRoot?.port ?? inherited?.[portKey] : void 0) ?? params.defaultPort;
|
|
154
|
+
const inheritedHost = accountId ? canonicalRoot ? canonicalRoot.host : (hostKey ? inherited?.[hostKey] : void 0) ?? params.defaultHost : params.defaultHost;
|
|
155
|
+
const host = hostKey ? listener[hostKey] ?? inheritedHost : inheritedHost;
|
|
156
|
+
const legacyPath = [pathPrefix, params.webhookKey].filter(Boolean).join(".");
|
|
157
|
+
if (Object.hasOwn(entry, "legacyWebhook")) changes.push(`Removed ${legacyPath} legacy listener keys; ${pathPrefix}.legacyWebhook is already configured.`);
|
|
158
|
+
else if (accountId && root?.legacyWebhook === false) changes.push(`Removed ${legacyPath} legacy listener keys; ${prefix}.legacyWebhook: false keeps this account's inherited listener disabled.`);
|
|
159
|
+
else {
|
|
160
|
+
next.legacyWebhook = {
|
|
161
|
+
port,
|
|
162
|
+
...host !== void 0 ? { host } : {}
|
|
163
|
+
};
|
|
164
|
+
changes.push(`Moved ${legacyPath} listener settings to ${pathPrefix}.legacyWebhook. Point the external callback or reverse proxy at the Gateway port and webhook path, verify delivery, then set legacyWebhook: false to disable legacy forwarding.`);
|
|
165
|
+
}
|
|
166
|
+
const updated = params.webhookKey ? { ...listener } : next;
|
|
167
|
+
delete updated[portKey];
|
|
168
|
+
if (hostKey) delete updated[hostKey];
|
|
169
|
+
if (params.webhookKey) {
|
|
170
|
+
if (Object.keys(updated).length) next[params.webhookKey] = updated;
|
|
171
|
+
else delete next[params.webhookKey];
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
entry: next,
|
|
175
|
+
changed: true
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region \0openclaw:bundled-sdk:openclaw/plugin-sdk/runtime-doctor-migrations
|
|
184
|
+
var runtime_doctor_migrations_exports = /* @__PURE__ */ __exportAll({ createLegacyWebhookListenerDoctorContract: () => createLegacyWebhookListenerDoctorContract });
|
|
185
|
+
import * as import_openclaw_plugin_sdk_runtime_doctor_migrations from "openclaw/plugin-sdk/runtime-doctor-migrations";
|
|
186
|
+
__reExport(runtime_doctor_migrations_exports, import_openclaw_plugin_sdk_runtime_doctor_migrations);
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region extensions/acpx/src/state.ts
|
|
189
|
+
const ACPX_PROCESS_LEASE_NAMESPACE = "process-leases";
|
|
190
|
+
const ACPX_PROCESS_LEASE_MAX_ENTRIES = 4096;
|
|
191
|
+
const ACPX_LEGACY_PROCESS_LEASE_FILE = "process-leases.json";
|
|
192
|
+
const ACPX_GATEWAY_INSTANCE_NAMESPACE = "gateway-instance";
|
|
193
|
+
const ACPX_GATEWAY_INSTANCE_KEY = "current";
|
|
194
|
+
const ACPX_LEGACY_GATEWAY_INSTANCE_FILE = "gateway-instance-id";
|
|
195
|
+
function normalizeAcpxGatewayInstanceRecord(value) {
|
|
196
|
+
const record = asOptionalObjectRecord(value);
|
|
197
|
+
const instanceId = normalizeOptionalString(record?.instanceId);
|
|
198
|
+
if (!instanceId) return;
|
|
199
|
+
return {
|
|
200
|
+
instanceId,
|
|
201
|
+
createdAt: Math.trunc(asFiniteNumber(record?.createdAt) ?? 0)
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region extensions/acpx/src/process-lease.ts
|
|
206
|
+
const OPENCLAW_ACPX_LEASE_ID_ARG = "--openclaw-acpx-lease-id";
|
|
207
|
+
const OPENCLAW_GATEWAY_INSTANCE_ID_ARG = "--openclaw-gateway-instance-id";
|
|
208
|
+
/** Synthetic session identity for generated-wrapper health probes. */
|
|
209
|
+
const ACPX_PROBE_LEASE_SESSION_KEY = "openclaw:acpx:probe";
|
|
210
|
+
function readAcpxProcessLeaseIdentity(command) {
|
|
211
|
+
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 ?? [];
|
|
212
|
+
const leaseIndex = parts.lastIndexOf(OPENCLAW_ACPX_LEASE_ID_ARG);
|
|
213
|
+
const gatewayIndex = parts.lastIndexOf(OPENCLAW_GATEWAY_INSTANCE_ID_ARG);
|
|
214
|
+
const leaseId = leaseIndex >= 0 ? parts[leaseIndex + 1]?.trim() : "";
|
|
215
|
+
const gatewayInstanceId = gatewayIndex >= 0 ? parts[gatewayIndex + 1]?.trim() : "";
|
|
216
|
+
if (!leaseId || !gatewayInstanceId) return;
|
|
217
|
+
return {
|
|
218
|
+
leaseId,
|
|
219
|
+
gatewayInstanceId
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function normalizeAcpxProcessLease(value) {
|
|
223
|
+
const record = asOptionalObjectRecord(value);
|
|
224
|
+
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" || ![
|
|
225
|
+
"open",
|
|
226
|
+
"closing",
|
|
227
|
+
"closed",
|
|
228
|
+
"lost"
|
|
229
|
+
].includes(String(record.state))) return;
|
|
230
|
+
return {
|
|
231
|
+
leaseId: record.leaseId,
|
|
232
|
+
gatewayInstanceId: record.gatewayInstanceId,
|
|
233
|
+
sessionKey: record.sessionKey,
|
|
234
|
+
wrapperRoot: record.wrapperRoot,
|
|
235
|
+
wrapperPath: record.wrapperPath,
|
|
236
|
+
rootPid: record.rootPid,
|
|
237
|
+
...typeof record.processGroupId === "number" ? { processGroupId: record.processGroupId } : {},
|
|
238
|
+
commandHash: record.commandHash,
|
|
239
|
+
startedAt: record.startedAt,
|
|
240
|
+
state: record.state
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function normalizeAcpxProcessLeaseFile(value) {
|
|
244
|
+
const root = asRecord(value);
|
|
245
|
+
return {
|
|
246
|
+
version: 1,
|
|
247
|
+
leases: Array.isArray(root.leases) ? root.leases.map(normalizeAcpxProcessLease).filter((lease) => Boolean(lease)) : []
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function openAcpxProcessLeaseStateStore(openKeyedStore) {
|
|
251
|
+
return openKeyedStore({
|
|
252
|
+
namespace: ACPX_PROCESS_LEASE_NAMESPACE,
|
|
253
|
+
maxEntries: ACPX_PROCESS_LEASE_MAX_ENTRIES,
|
|
254
|
+
overflowPolicy: "reject-new"
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function createAcpxProcessLeaseStore(params) {
|
|
258
|
+
let updateQueue = Promise.resolve();
|
|
259
|
+
async function update(mutator) {
|
|
260
|
+
const run = updateQueue.then(mutator);
|
|
261
|
+
updateQueue = run.catch(() => {});
|
|
262
|
+
await run;
|
|
263
|
+
}
|
|
264
|
+
async function readCurrent() {
|
|
265
|
+
await updateQueue;
|
|
266
|
+
return (await params.store.entries()).map((entry) => normalizeAcpxProcessLease(entry.value)).filter((lease) => Boolean(lease));
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
async load(leaseId) {
|
|
270
|
+
await updateQueue;
|
|
271
|
+
return normalizeAcpxProcessLease(await params.store.lookup(leaseId));
|
|
272
|
+
},
|
|
273
|
+
async listOpen(gatewayInstanceId) {
|
|
274
|
+
return (await readCurrent()).filter((lease) => (lease.state === "open" || lease.state === "closing") && (!gatewayInstanceId || lease.gatewayInstanceId === gatewayInstanceId));
|
|
275
|
+
},
|
|
276
|
+
async save(lease) {
|
|
277
|
+
await update(async () => {
|
|
278
|
+
await params.store.register(lease.leaseId, lease);
|
|
279
|
+
});
|
|
280
|
+
},
|
|
281
|
+
async markState(leaseId, state) {
|
|
282
|
+
await update(async () => {
|
|
283
|
+
if (state === "closed" || state === "lost") {
|
|
284
|
+
await params.store.delete(leaseId);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const lease = normalizeAcpxProcessLease(await params.store.lookup(leaseId));
|
|
288
|
+
if (lease) await params.store.register(leaseId, {
|
|
289
|
+
...lease,
|
|
290
|
+
state
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/** Hash a wrapper command so process leases can detect command drift. */
|
|
297
|
+
function hashAcpxProcessCommand(command) {
|
|
298
|
+
return createHash("sha256").update(renderAgentCommand(command)).digest("hex");
|
|
299
|
+
}
|
|
300
|
+
/** Append portable wrapper arguments without changing the executable or argument bytes. */
|
|
301
|
+
function withAcpxLeaseArgs(params) {
|
|
302
|
+
return [
|
|
303
|
+
...splitCommandParts(params.command),
|
|
304
|
+
OPENCLAW_ACPX_LEASE_ID_ARG,
|
|
305
|
+
params.leaseId,
|
|
306
|
+
OPENCLAW_GATEWAY_INSTANCE_ID_ARG,
|
|
307
|
+
params.gatewayInstanceId
|
|
308
|
+
];
|
|
309
|
+
}
|
|
310
|
+
//#endregion
|
|
311
|
+
export { __exportAll as _, hashAcpxProcessCommand as a, openAcpxProcessLeaseStateStore as c, ACPX_GATEWAY_INSTANCE_KEY as d, ACPX_GATEWAY_INSTANCE_NAMESPACE as f, runtime_doctor_migrations_exports as g, normalizeAcpxGatewayInstanceRecord as h, createAcpxProcessLeaseStore as i, readAcpxProcessLeaseIdentity as l, ACPX_LEGACY_PROCESS_LEASE_FILE as m, OPENCLAW_ACPX_LEASE_ID_ARG as n, normalizeAcpxProcessLease as o, ACPX_LEGACY_GATEWAY_INSTANCE_FILE as p, OPENCLAW_GATEWAY_INSTANCE_ID_ARG as r, normalizeAcpxProcessLeaseFile as s, ACPX_PROBE_LEASE_SESSION_KEY as t, withAcpxLeaseArgs as u };
|