@ken-jo/agent-connector 0.3.0 → 0.4.0
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 +188 -20
- package/dist/action-PLZW5BGX.js +54 -0
- package/dist/action-PLZW5BGX.js.map +1 -0
- package/dist/{amp-YGV66GJT.js → amp-B524U6LO.js} +81 -5
- package/dist/amp-B524U6LO.js.map +1 -0
- package/dist/{antigravity-7IBYFO6P.js → antigravity-ANDKF3K7.js} +4 -4
- package/dist/{antigravity-cli-JEOUP2VN.js → antigravity-cli-LLRPDDOT.js} +4 -4
- package/dist/chunk-22Y24V7O.js +21 -0
- package/dist/chunk-22Y24V7O.js.map +1 -0
- package/dist/chunk-3PFFTFCU.js +190 -0
- package/dist/chunk-3PFFTFCU.js.map +1 -0
- package/dist/{chunk-SCVBYSF7.js → chunk-6XHB6BE3.js} +1 -1
- package/dist/chunk-76FUSVIA.js +43 -0
- package/dist/chunk-76FUSVIA.js.map +1 -0
- package/dist/{chunk-GSGDG2BV.js → chunk-ADTHT2FN.js} +109 -16
- package/dist/chunk-ADTHT2FN.js.map +1 -0
- package/dist/{chunk-ZAM4M7LI.js → chunk-BHUCB6LY.js} +135 -296
- package/dist/chunk-BHUCB6LY.js.map +1 -0
- package/dist/{chunk-MEH7GC7R.js → chunk-BXGFUENQ.js} +20 -3
- package/dist/chunk-BXGFUENQ.js.map +1 -0
- package/dist/chunk-CIRDOY2V.js +351 -0
- package/dist/chunk-CIRDOY2V.js.map +1 -0
- package/dist/{chunk-QADSG2JY.js → chunk-G3ER4SY5.js} +1152 -106
- package/dist/chunk-G3ER4SY5.js.map +1 -0
- package/dist/{chunk-44HUXXC6.js → chunk-G5CQHVQG.js} +232 -5
- package/dist/chunk-G5CQHVQG.js.map +1 -0
- package/dist/{chunk-PD4VFEBY.js → chunk-IW77FFNX.js} +39 -1
- package/dist/chunk-IW77FFNX.js.map +1 -0
- package/dist/chunk-J3D5W5T2.js +83 -0
- package/dist/chunk-J3D5W5T2.js.map +1 -0
- package/dist/{openclaw-UZ5BMCGQ.js → chunk-KGGBNKI4.js} +125 -33
- package/dist/chunk-KGGBNKI4.js.map +1 -0
- package/dist/{chunk-3JCRUROU.js → chunk-KGJWE6A2.js} +13 -4
- package/dist/chunk-KGJWE6A2.js.map +1 -0
- package/dist/{chunk-DS3LUFVN.js → chunk-KGXDYGNJ.js} +26 -16
- package/dist/chunk-KGXDYGNJ.js.map +1 -0
- package/dist/{chunk-KVAAFYRR.js → chunk-KHO5WNTP.js} +2 -18
- package/dist/chunk-KHO5WNTP.js.map +1 -0
- package/dist/{chunk-KQSNDFBN.js → chunk-LL4OBGRU.js} +3 -3
- package/dist/{chunk-WNREHWKL.js → chunk-MAH6QQTO.js} +3 -3
- package/dist/{claude-code-4275HQU4.js → claude-code-MLWS7CW6.js} +180 -28
- package/dist/claude-code-MLWS7CW6.js.map +1 -0
- package/dist/cli/sdk.js +4 -3
- package/dist/cli/sdk.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/{codebuff-LE3KBZGM.js → codebuff-SH2S3QAN.js} +102 -5
- package/dist/codebuff-SH2S3QAN.js.map +1 -0
- package/dist/{codex-4BY2ZJIW.js → codex-BXA4FIF4.js} +3 -3
- package/dist/{copilot-cli-IPISKO3V.js → copilot-cli-ROIBTTDT.js} +3 -3
- package/dist/{crush-X3E25EQG.js → crush-QLEFLHUC.js} +3 -3
- package/dist/{cursor-UH3W622V.js → cursor-ASJPWZ4R.js} +6 -3
- package/dist/cursor-ASJPWZ4R.js.map +1 -0
- package/dist/{detect-MNDHGLSV.js → detect-ZIIS55LD.js} +4 -3
- package/dist/{detect-MNDHGLSV.js.map → detect-ZIIS55LD.js.map} +1 -1
- package/dist/{doctor-N5KZDD55.js → doctor-TJUGSQ5C.js} +17 -13
- package/dist/doctor-TJUGSQ5C.js.map +1 -0
- package/dist/{droid-T5VBRXZF.js → droid-NCSS3B5Y.js} +170 -4
- package/dist/droid-NCSS3B5Y.js.map +1 -0
- package/dist/{gemini-cli-ZSUK55UA.js → gemini-cli-SLVWLXJW.js} +3 -3
- package/dist/{goose-M3RR3CZG.js → goose-PBA7KTE5.js} +100 -5
- package/dist/goose-PBA7KTE5.js.map +1 -0
- package/dist/{hermes-CUNKIAR5.js → hermes-ONDCECP2.js} +3 -3
- package/dist/{hook-7LUOKIXH.js → hook-OU522ABV.js} +12 -9
- package/dist/{hook-7LUOKIXH.js.map → hook-OU522ABV.js.map} +1 -1
- package/dist/index.d.ts +71 -3
- package/dist/index.js +22 -3
- package/dist/{install-TQTT4F3I.js → install-UDCBL7HP.js} +12 -10
- package/dist/{install-TQTT4F3I.js.map → install-UDCBL7HP.js.map} +1 -1
- package/dist/introspect-DuZSPSHB.d.ts +53 -0
- package/dist/{jetbrains-copilot-C3HDGGML.js → jetbrains-copilot-YABZ3HFA.js} +3 -3
- package/dist/{kilo-PKAJCWPX.js → kilo-F3ZDZKCT.js} +3 -3
- package/dist/{kilo-cli-QWUVXG2O.js → kilo-cli-3HWPWBY4.js} +3 -3
- package/dist/{kimi-EVD7YKXS.js → kimi-NV72FVAO.js} +3 -3
- package/dist/{kiro-UQRY6EIO.js → kiro-C5W6AGDR.js} +3 -3
- package/dist/{leaderboard-MUJ7DURM.js → leaderboard-TIBVONVT.js} +3 -3
- package/dist/mimo-code-IMM5YM3M.js +754 -0
- package/dist/mimo-code-IMM5YM3M.js.map +1 -0
- package/dist/{mux-5CI5FP2W.js → mux-DSSH4AFS.js} +3 -3
- package/dist/nemoclaw-3EVNKD3I.js +58 -0
- package/dist/nemoclaw-3EVNKD3I.js.map +1 -0
- package/dist/{omp-AXA7XY22.js → omp-Z6FRNDPN.js} +3 -3
- package/dist/openclaw-7G437NT7.js +18 -0
- package/dist/openclaw-7G437NT7.js.map +1 -0
- package/dist/{opencode-NZBRLJN3.js → opencode-Q6KBSAKW.js} +3 -3
- package/dist/{package-SYPA5POO.js → package-RMPN57YZ.js} +7 -6
- package/dist/{package-SYPA5POO.js.map → package-RMPN57YZ.js.map} +1 -1
- package/dist/{pi-WMTUX6ES.js → pi-LZT6WWAZ.js} +2 -2
- package/dist/{qwen-code-MRRIUQ3P.js → qwen-code-WQBKRPWJ.js} +3 -3
- package/dist/{roo-code-NI5UU6JB.js → roo-code-GWSNU3OW.js} +137 -4
- package/dist/roo-code-GWSNU3OW.js.map +1 -0
- package/dist/runtime/index.d.ts +96 -2
- package/dist/runtime/index.js +18 -10
- package/dist/sdk/index.d.ts +52 -0
- package/dist/sdk/index.js +81 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sdk/test.d.ts +74 -0
- package/dist/sdk/test.js +291 -0
- package/dist/sdk/test.js.map +1 -0
- package/dist/{serve-XJOSA7BY.js → serve-D3GO36UT.js} +12 -9
- package/dist/{serve-XJOSA7BY.js.map → serve-D3GO36UT.js.map} +1 -1
- package/dist/{status-DYSS6BOR.js → status-KOSKCETC.js} +10 -8
- package/dist/{status-DYSS6BOR.js.map → status-KOSKCETC.js.map} +1 -1
- package/dist/statusline-ZNTCL5YC.js +59 -0
- package/dist/statusline-ZNTCL5YC.js.map +1 -0
- package/dist/{telemetry-IJ4GP2Q3.js → telemetry-XIKUUGTQ.js} +5 -4
- package/dist/{telemetry-IJ4GP2Q3.js.map → telemetry-XIKUUGTQ.js.map} +1 -1
- package/dist/{trae-CQC72RHH.js → trae-NWT3TMSZ.js} +99 -5
- package/dist/trae-NWT3TMSZ.js.map +1 -0
- package/dist/{types-CNigfEuR.d.ts → types-BEKsAium.d.ts} +268 -6
- package/dist/{uninstall-3DXIBRCV.js → uninstall-2LBLQVFA.js} +12 -10
- package/dist/{uninstall-3DXIBRCV.js.map → uninstall-2LBLQVFA.js.map} +1 -1
- package/dist/{upgrade-GJEB3ITY.js → upgrade-ZC2S7VRN.js} +12 -10
- package/dist/{upgrade-GJEB3ITY.js.map → upgrade-ZC2S7VRN.js.map} +1 -1
- package/dist/{usage-2BJLM3GJ.js → usage-E2A4O2CY.js} +3 -3
- package/dist/{usage-event-472X3DOY.js → usage-event-JKMC4QC4.js} +11 -8
- package/dist/{usage-event-472X3DOY.js.map → usage-event-JKMC4QC4.js.map} +1 -1
- package/dist/{vscode-copilot-4RVRTCKV.js → vscode-copilot-ALV45XF6.js} +3 -3
- package/dist/{warp-X6MB67NT.js → warp-H6IX4U3J.js} +3 -3
- package/dist/{zed-NTCYW4V2.js → zed-RH34TNSW.js} +3 -3
- package/package.json +9 -1
- package/dist/amp-YGV66GJT.js.map +0 -1
- package/dist/chunk-3JCRUROU.js.map +0 -1
- package/dist/chunk-44HUXXC6.js.map +0 -1
- package/dist/chunk-DS3LUFVN.js.map +0 -1
- package/dist/chunk-GSGDG2BV.js.map +0 -1
- package/dist/chunk-KVAAFYRR.js.map +0 -1
- package/dist/chunk-MEH7GC7R.js.map +0 -1
- package/dist/chunk-NJU275US.js +0 -238
- package/dist/chunk-NJU275US.js.map +0 -1
- package/dist/chunk-PD4VFEBY.js.map +0 -1
- package/dist/chunk-QADSG2JY.js.map +0 -1
- package/dist/chunk-ZAM4M7LI.js.map +0 -1
- package/dist/claude-code-4275HQU4.js.map +0 -1
- package/dist/codebuff-LE3KBZGM.js.map +0 -1
- package/dist/cursor-UH3W622V.js.map +0 -1
- package/dist/doctor-N5KZDD55.js.map +0 -1
- package/dist/droid-T5VBRXZF.js.map +0 -1
- package/dist/goose-M3RR3CZG.js.map +0 -1
- package/dist/openclaw-UZ5BMCGQ.js.map +0 -1
- package/dist/roo-code-NI5UU6JB.js.map +0 -1
- package/dist/trae-CQC72RHH.js.map +0 -1
- /package/dist/{antigravity-7IBYFO6P.js.map → antigravity-ANDKF3K7.js.map} +0 -0
- /package/dist/{antigravity-cli-JEOUP2VN.js.map → antigravity-cli-LLRPDDOT.js.map} +0 -0
- /package/dist/{chunk-SCVBYSF7.js.map → chunk-6XHB6BE3.js.map} +0 -0
- /package/dist/{chunk-KQSNDFBN.js.map → chunk-LL4OBGRU.js.map} +0 -0
- /package/dist/{chunk-WNREHWKL.js.map → chunk-MAH6QQTO.js.map} +0 -0
- /package/dist/{codex-4BY2ZJIW.js.map → codex-BXA4FIF4.js.map} +0 -0
- /package/dist/{copilot-cli-IPISKO3V.js.map → copilot-cli-ROIBTTDT.js.map} +0 -0
- /package/dist/{crush-X3E25EQG.js.map → crush-QLEFLHUC.js.map} +0 -0
- /package/dist/{gemini-cli-ZSUK55UA.js.map → gemini-cli-SLVWLXJW.js.map} +0 -0
- /package/dist/{hermes-CUNKIAR5.js.map → hermes-ONDCECP2.js.map} +0 -0
- /package/dist/{jetbrains-copilot-C3HDGGML.js.map → jetbrains-copilot-YABZ3HFA.js.map} +0 -0
- /package/dist/{kilo-PKAJCWPX.js.map → kilo-F3ZDZKCT.js.map} +0 -0
- /package/dist/{kilo-cli-QWUVXG2O.js.map → kilo-cli-3HWPWBY4.js.map} +0 -0
- /package/dist/{kimi-EVD7YKXS.js.map → kimi-NV72FVAO.js.map} +0 -0
- /package/dist/{kiro-UQRY6EIO.js.map → kiro-C5W6AGDR.js.map} +0 -0
- /package/dist/{leaderboard-MUJ7DURM.js.map → leaderboard-TIBVONVT.js.map} +0 -0
- /package/dist/{mux-5CI5FP2W.js.map → mux-DSSH4AFS.js.map} +0 -0
- /package/dist/{omp-AXA7XY22.js.map → omp-Z6FRNDPN.js.map} +0 -0
- /package/dist/{opencode-NZBRLJN3.js.map → opencode-Q6KBSAKW.js.map} +0 -0
- /package/dist/{pi-WMTUX6ES.js.map → pi-LZT6WWAZ.js.map} +0 -0
- /package/dist/{qwen-code-MRRIUQ3P.js.map → qwen-code-WQBKRPWJ.js.map} +0 -0
- /package/dist/{usage-2BJLM3GJ.js.map → usage-E2A4O2CY.js.map} +0 -0
- /package/dist/{vscode-copilot-4RVRTCKV.js.map → vscode-copilot-ALV45XF6.js.map} +0 -0
- /package/dist/{warp-X6MB67NT.js.map → warp-H6IX4U3J.js.map} +0 -0
- /package/dist/{zed-NTCYW4V2.js.map → zed-RH34TNSW.js.map} +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ADAPTER_REGISTRY
|
|
4
|
+
} from "./chunk-3PFFTFCU.js";
|
|
5
|
+
|
|
6
|
+
// src/adapters/detect.ts
|
|
7
|
+
async function detectInstalledPlatforms(projectDir) {
|
|
8
|
+
const adapters = await Promise.all(
|
|
9
|
+
ADAPTER_REGISTRY.map(async (factory) => {
|
|
10
|
+
try {
|
|
11
|
+
return await factory.load();
|
|
12
|
+
} catch {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
);
|
|
17
|
+
const detected = [];
|
|
18
|
+
for (const adapter of adapters) {
|
|
19
|
+
if (!adapter) continue;
|
|
20
|
+
try {
|
|
21
|
+
const result = adapter.detectInstalled(projectDir);
|
|
22
|
+
if (result.installed) detected.push(result);
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return detected;
|
|
27
|
+
}
|
|
28
|
+
var RUNTIME_SIGNALS = [
|
|
29
|
+
{
|
|
30
|
+
platform: "claude-code",
|
|
31
|
+
vars: ["CLAUDE_CODE_ENTRYPOINT", "CLAUDE_PLUGIN_ROOT", "CLAUDE_PROJECT_DIR", "CLAUDE_SESSION_ID"],
|
|
32
|
+
note: "Claude Code env marker set"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
platform: "cursor",
|
|
36
|
+
vars: ["CURSOR_TRACE_ID", "CURSOR_CLI", "CURSOR_CWD"],
|
|
37
|
+
note: "Cursor env marker set"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
platform: "codex",
|
|
41
|
+
vars: ["CODEX_THREAD_ID", "CODEX_CI"],
|
|
42
|
+
note: "Codex env marker set"
|
|
43
|
+
}
|
|
44
|
+
];
|
|
45
|
+
function detectRuntimeHost(env = process.env) {
|
|
46
|
+
const override = env.AGENT_CONNECTOR_PLATFORM;
|
|
47
|
+
if (override && override.trim() !== "") {
|
|
48
|
+
if (REGISTERED_RUNTIME_IDS.has(override)) {
|
|
49
|
+
return {
|
|
50
|
+
platform: override,
|
|
51
|
+
confidence: "high",
|
|
52
|
+
reason: `AGENT_CONNECTOR_PLATFORM=${override} override`
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const rule of RUNTIME_SIGNALS) {
|
|
57
|
+
const hit = rule.vars.find((name) => {
|
|
58
|
+
const value = env[name];
|
|
59
|
+
return typeof value === "string" && value !== "";
|
|
60
|
+
});
|
|
61
|
+
if (hit) {
|
|
62
|
+
return {
|
|
63
|
+
platform: rule.platform,
|
|
64
|
+
confidence: "high",
|
|
65
|
+
reason: `${rule.note} (${hit})`
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
platform: "unknown",
|
|
71
|
+
confidence: "low",
|
|
72
|
+
reason: "no host runtime env marker detected"
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
var REGISTERED_RUNTIME_IDS = new Set(
|
|
76
|
+
RUNTIME_SIGNALS.map((rule) => rule.platform)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
export {
|
|
80
|
+
detectInstalledPlatforms,
|
|
81
|
+
detectRuntimeHost
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=chunk-J3D5W5T2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/detect.ts"],"sourcesContent":["/**\n * adapters/detect — two complementary detection answers.\n *\n * 1. detectInstalledPlatforms(projectDir): which hosts are INSTALLED on this\n * machine (probe each adapter's config dirs / marker files). Drives the\n * install planner (\"auto\" target expansion, `doctor`).\n *\n * 2. detectRuntimeHost(env): which host is EXECUTING right now (read env\n * markers the host runtime injects into its child processes). Drives the\n * universal hook entrypoint — it must know which adapter's wire format the\n * stdin payload is in before it can parse it.\n *\n * Both derive their platform set from `ADAPTER_REGISTRY` (the single source of\n * truth in registry.ts), generalizing context-mode's detection cascade\n * (env-var tier → config-dir tier → low-confidence fallback) to our\n * Adapter/AdapterFactory contract. The config-dir tier is delegated to each\n * adapter's `detectInstalled()` rather than duplicated here, so adding a\n * platform never requires editing this file.\n */\n\nimport type { DetectedPlatform, DetectionSignal, PlatformId } from \"../core/types.js\";\nimport { ADAPTER_REGISTRY, loadAdapter } from \"./registry.js\";\n\n/**\n * Detect every platform installed on this machine.\n *\n * Loads each registered adapter and calls its `detectInstalled(projectDir)`\n * (which probes the platform's native config dirs + marker files), returning\n * only those reported `installed === true`, in registry order. Adapters never\n * throw from detection, but we guard each call so one misbehaving adapter\n * cannot abort the whole probe.\n *\n * @param projectDir Resolved project root — lets project-scoped adapters look\n * for `<projectDir>/.mcp.json`, `<projectDir>/.cursor`, etc.\n */\nexport async function detectInstalledPlatforms(\n projectDir: string,\n): Promise<DetectedPlatform[]> {\n const adapters = await Promise.all(\n ADAPTER_REGISTRY.map(async (factory) => {\n try {\n return await factory.load();\n } catch {\n return undefined;\n }\n }),\n );\n\n const detected: DetectedPlatform[] = [];\n for (const adapter of adapters) {\n if (!adapter) continue;\n try {\n const result = adapter.detectInstalled(projectDir);\n if (result.installed) detected.push(result);\n } catch {\n // A single adapter's detection failure must not sink the whole probe.\n }\n }\n return detected;\n}\n\n/**\n * One env-marker rule for runtime-host detection. `vars` is checked in order;\n * the FIRST rule with any non-empty matching env var wins, so the table order\n * is the detection precedence (forks before parents). Each var is documented at\n * the rule so the source of the marker is auditable.\n */\ninterface RuntimeSignalRule {\n readonly platform: PlatformId;\n /** Env var names that prove \"this host is executing me\". */\n readonly vars: readonly string[];\n /** Human-readable note (printed in the DetectionSignal.reason). */\n readonly note: string;\n}\n\n/**\n * Ordered env-marker table. Forks BEFORE parents (cursor — a VS Code fork —\n * precedes any future vscode-copilot entry so a Cursor session inside a\n * VS Code-derived shell is not misclassified). Markers are verified against\n * each host's source:\n *\n * - claude-code: CLAUDE_CODE_ENTRYPOINT is set on every Claude Code session;\n * CLAUDE_PLUGIN_ROOT is set when a plugin is loaded. Both are CC-exclusive\n * and serve as the disambiguators when CC runs inside another IDE's shell.\n * - codex: CODEX_THREAD_ID is set per exec (codex-rs/core exec_env);\n * CODEX_CI is set in CI mode (unified_exec process_manager).\n * - cursor: CURSOR_TRACE_ID is the widely-observed Cursor marker (Cursor\n * CLI / agent); CURSOR_CLI marks the Cursor terminal; CURSOR_CWD is the\n * documented workspace var.\n */\nconst RUNTIME_SIGNALS: readonly RuntimeSignalRule[] = [\n {\n platform: \"claude-code\",\n vars: [\"CLAUDE_CODE_ENTRYPOINT\", \"CLAUDE_PLUGIN_ROOT\", \"CLAUDE_PROJECT_DIR\", \"CLAUDE_SESSION_ID\"],\n note: \"Claude Code env marker set\",\n },\n {\n platform: \"cursor\",\n vars: [\"CURSOR_TRACE_ID\", \"CURSOR_CLI\", \"CURSOR_CWD\"],\n note: \"Cursor env marker set\",\n },\n {\n platform: \"codex\",\n vars: [\"CODEX_THREAD_ID\", \"CODEX_CI\"],\n note: \"Codex env marker set\",\n },\n];\n\n/**\n * Identify which host runtime is executing the current process from environment\n * markers. High confidence on an env-marker hit; otherwise an explicit\n * `AGENT_CONNECTOR_PLATFORM` override (high confidence, validated against the\n * registry) is honored; failing both, returns `unknown` at low confidence so\n * the caller can decide on a safe default.\n *\n * Pure with respect to its `env` argument (defaults to `process.env`) so it is\n * trivially testable — pass a synthetic env to assert each rule.\n */\nexport function detectRuntimeHost(env: NodeJS.ProcessEnv = process.env): DetectionSignal {\n // ── Explicit override (highest priority, validated against the registry) ──\n const override = env.AGENT_CONNECTOR_PLATFORM;\n if (override && override.trim() !== \"\") {\n if (REGISTERED_RUNTIME_IDS.has(override as PlatformId)) {\n return {\n platform: override as PlatformId,\n confidence: \"high\",\n reason: `AGENT_CONNECTOR_PLATFORM=${override} override`,\n };\n }\n }\n\n // ── Env-marker tier (forks before parents; first hit wins) ──────────────\n for (const rule of RUNTIME_SIGNALS) {\n const hit = rule.vars.find((name) => {\n const value = env[name];\n return typeof value === \"string\" && value !== \"\";\n });\n if (hit) {\n return {\n platform: rule.platform,\n confidence: \"high\",\n reason: `${rule.note} (${hit})`,\n };\n }\n }\n\n // ── Fallback: no host marker present ────────────────────────────────────\n return {\n platform: \"unknown\",\n confidence: \"low\",\n reason: \"no host runtime env marker detected\",\n };\n}\n\n/** Platform ids the runtime-signal table knows how to detect. */\nconst REGISTERED_RUNTIME_IDS: ReadonlySet<PlatformId> = new Set(\n RUNTIME_SIGNALS.map((rule) => rule.platform),\n);\n\n// Re-export for callers that resolve an adapter straight from a detection\n// result without reaching into registry.ts.\nexport { loadAdapter };\n"],"mappings":";;;;;;AAmCA,eAAsB,yBACpB,YAC6B;AAC7B,QAAM,WAAW,MAAM,QAAQ;AAAA,IAC7B,iBAAiB,IAAI,OAAO,YAAY;AACtC,UAAI;AACF,eAAO,MAAM,QAAQ,KAAK;AAAA,MAC5B,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,WAA+B,CAAC;AACtC,aAAW,WAAW,UAAU;AAC9B,QAAI,CAAC,QAAS;AACd,QAAI;AACF,YAAM,SAAS,QAAQ,gBAAgB,UAAU;AACjD,UAAI,OAAO,UAAW,UAAS,KAAK,MAAM;AAAA,IAC5C,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AA+BA,IAAM,kBAAgD;AAAA,EACpD;AAAA,IACE,UAAU;AAAA,IACV,MAAM,CAAC,0BAA0B,sBAAsB,sBAAsB,mBAAmB;AAAA,IAChG,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM,CAAC,mBAAmB,cAAc,YAAY;AAAA,IACpD,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM,CAAC,mBAAmB,UAAU;AAAA,IACpC,MAAM;AAAA,EACR;AACF;AAYO,SAAS,kBAAkB,MAAyB,QAAQ,KAAsB;AAEvF,QAAM,WAAW,IAAI;AACrB,MAAI,YAAY,SAAS,KAAK,MAAM,IAAI;AACtC,QAAI,uBAAuB,IAAI,QAAsB,GAAG;AACtD,aAAO;AAAA,QACL,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,QAAQ,4BAA4B,QAAQ;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AAGA,aAAW,QAAQ,iBAAiB;AAClC,UAAM,MAAM,KAAK,KAAK,KAAK,CAAC,SAAS;AACnC,YAAM,QAAQ,IAAI,IAAI;AACtB,aAAO,OAAO,UAAU,YAAY,UAAU;AAAA,IAChD,CAAC;AACD,QAAI,KAAK;AACP,aAAO;AAAA,QACL,UAAU,KAAK;AAAA,QACf,YAAY;AAAA,QACZ,QAAQ,GAAG,KAAK,IAAI,KAAK,GAAG;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV;AACF;AAGA,IAAM,yBAAkD,IAAI;AAAA,EAC1D,gBAAgB,IAAI,CAAC,SAAS,KAAK,QAAQ;AAC7C;","names":[]}
|
|
@@ -5,14 +5,11 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
BaseAdapter,
|
|
7
7
|
parseJsonc
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-7L6ITC4T.js";
|
|
8
|
+
} from "./chunk-IW77FFNX.js";
|
|
10
9
|
import {
|
|
11
10
|
buildServeWrapperCommand,
|
|
12
11
|
shouldWrapForTelemetry
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-N6HH7K6P.js";
|
|
15
|
-
import "./chunk-TRQKHP7D.js";
|
|
12
|
+
} from "./chunk-BXGFUENQ.js";
|
|
16
13
|
|
|
17
14
|
// src/adapters/openclaw/index.ts
|
|
18
15
|
import {
|
|
@@ -48,6 +45,9 @@ var EVENT_TO_OPENCLAW = {
|
|
|
48
45
|
};
|
|
49
46
|
var OpenClawAdapter = class extends BaseAdapter {
|
|
50
47
|
id = HOST;
|
|
48
|
+
// Declared as `string` (not the literal "OpenClaw") so a fork adapter can
|
|
49
|
+
// override it — NemoClaw extends this class (precedent: AntigravityAdapter,
|
|
50
|
+
// whose name is likewise typed `string` for AntigravityCliAdapter).
|
|
51
51
|
name = "OpenClaw";
|
|
52
52
|
paradigm = "ts-plugin";
|
|
53
53
|
capabilities = {
|
|
@@ -82,7 +82,13 @@ var OpenClawAdapter = class extends BaseAdapter {
|
|
|
82
82
|
// injected via before_prompt_build at prompt assembly.
|
|
83
83
|
canInjectSessionContext: true,
|
|
84
84
|
// OpenClaw's MCP sidecar registration is stdio-only in practice.
|
|
85
|
-
transports: ["stdio"]
|
|
85
|
+
transports: ["stdio"],
|
|
86
|
+
// Content surface: OpenClaw follows the AgentSkills spec (dir-per-skill
|
|
87
|
+
// SKILL.md with {name, description} frontmatter + body). User scope →
|
|
88
|
+
// ~/.openclaw/skills (the `openclaw skills install --global` target);
|
|
89
|
+
// project scope → the resolved <workspace>/skills (the highest-priority
|
|
90
|
+
// documented skill root, the same workspace the memory surface anchors to).
|
|
91
|
+
supportsSkills: true
|
|
86
92
|
};
|
|
87
93
|
// ── Tolerant JSON5/JSONC read (override base strict JSON.parse) ──────────
|
|
88
94
|
/**
|
|
@@ -118,23 +124,32 @@ var OpenClawAdapter = class extends BaseAdapter {
|
|
|
118
124
|
memoryTargets(ctx) {
|
|
119
125
|
if (this.memoryOverride(ctx)?.path) return super.memoryTargets(ctx);
|
|
120
126
|
if (ctx.scope !== "project" && ctx.scope !== "user") return [];
|
|
121
|
-
const configPath = resolveOpenClawConfigPath();
|
|
122
|
-
const cfg = this.readJson(configPath);
|
|
123
|
-
const declared = cfg?.agents?.defaults?.workspace;
|
|
124
|
-
const workspace = typeof declared === "string" && declared.trim() !== "" ? declared.startsWith("~") ? resolve(homedir(), declared.replace(/^~[/\\]?/, "")) : resolve(declared) : join(dirname(configPath), "workspace");
|
|
125
127
|
return [
|
|
126
128
|
{
|
|
127
|
-
path: join(
|
|
129
|
+
path: join(this.resolveWorkspace(), "AGENTS.md"),
|
|
128
130
|
reason: "openclaw agent-workspace AGENTS.md (workspace-scoped operating instructions, not the project repo \u2014 both scopes map here)"
|
|
129
131
|
}
|
|
130
132
|
];
|
|
131
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Resolve OpenClaw's AGENT WORKSPACE — `agents.defaults.workspace` in
|
|
136
|
+
* openclaw.json when set, else `<stateDir>/workspace` (default
|
|
137
|
+
* ~/.openclaw/workspace). The same root the memory surface and the
|
|
138
|
+
* project-scope skills surface both anchor to, so they never diverge.
|
|
139
|
+
*/
|
|
140
|
+
resolveWorkspace() {
|
|
141
|
+
const configPath = resolveOpenClawConfigPath();
|
|
142
|
+
const cfg = this.readJson(configPath);
|
|
143
|
+
const declared = cfg?.agents?.defaults?.workspace;
|
|
144
|
+
return typeof declared === "string" && declared.trim() !== "" ? declared.startsWith("~") ? resolve(homedir(), declared.replace(/^~[/\\]?/, "")) : resolve(declared) : join(dirname(configPath), "workspace");
|
|
145
|
+
}
|
|
132
146
|
// ── Detection ──────────────────────────────────────────────────────────
|
|
133
147
|
detectInstalled(projectDir) {
|
|
134
148
|
const userConfig = resolveOpenClawConfigPath();
|
|
135
149
|
const userDir = join(homedir(), ".openclaw");
|
|
136
150
|
const projectConfig = join(projectDir, "openclaw.json");
|
|
137
|
-
const
|
|
151
|
+
const deferToNemoClaw = existsSync(join(homedir(), ".nemoclaw"));
|
|
152
|
+
const userInstalled = !deferToNemoClaw && (existsSync(userConfig) || existsSync(userDir));
|
|
138
153
|
const projInstalled = existsSync(projectConfig);
|
|
139
154
|
const installed = userInstalled || projInstalled;
|
|
140
155
|
const scope = projInstalled && !userInstalled ? "project" : "user";
|
|
@@ -198,7 +213,7 @@ var OpenClawAdapter = class extends BaseAdapter {
|
|
|
198
213
|
}
|
|
199
214
|
/** Resolve the per-platform server override into an effective ServerDef. */
|
|
200
215
|
effectiveServer(ctx) {
|
|
201
|
-
const override = ctx.connector.platforms[
|
|
216
|
+
const override = ctx.connector.platforms[this.id]?.server;
|
|
202
217
|
if (override === false) return void 0;
|
|
203
218
|
const base = ctx.connector.server;
|
|
204
219
|
if (!base) return void 0;
|
|
@@ -326,9 +341,9 @@ var OpenClawAdapter = class extends BaseAdapter {
|
|
|
326
341
|
installHooks(ctx) {
|
|
327
342
|
const pluginPath = this.getHookConfigPath(ctx);
|
|
328
343
|
const configPath = this.getServerConfigPath(ctx);
|
|
329
|
-
if (ctx.connector.platforms[
|
|
344
|
+
if (ctx.connector.platforms[this.id]?.hooks === false) {
|
|
330
345
|
return [
|
|
331
|
-
{ platform: this.id, action: "skip", path: pluginPath, detail:
|
|
346
|
+
{ platform: this.id, action: "skip", path: pluginPath, detail: `hooks disabled for ${this.id}` }
|
|
332
347
|
];
|
|
333
348
|
}
|
|
334
349
|
if (ctx.connector.hookEvents.length === 0) {
|
|
@@ -504,6 +519,96 @@ var OpenClawAdapter = class extends BaseAdapter {
|
|
|
504
519
|
loadObj[PLUGINS_LOAD_PATHS_KEY] = paths;
|
|
505
520
|
return paths;
|
|
506
521
|
}
|
|
522
|
+
// ── Content surface: skills (dir-per-skill SKILL.md) ────────────────────
|
|
523
|
+
// CONTENT-ONLY: pure native-file writers, mirroring codex/claude-code
|
|
524
|
+
// installSkills + renderSkill. No runtime dispatch, no home-bin pointer, no
|
|
525
|
+
// telemetry wrap. Each write is idempotent (byte-identical → skip) via
|
|
526
|
+
// writeContentFile and reversible via removeContentFile + removeDirIfEmpty
|
|
527
|
+
// (never rm -rf a shared dir). Honors platforms["openclaw"].skills === false.
|
|
528
|
+
//
|
|
529
|
+
// Native locations (AgentSkills spec — dir-per-skill SKILL.md + resources):
|
|
530
|
+
// user → ~/.openclaw/skills/<name>/SKILL.md (`openclaw skills install
|
|
531
|
+
// --global` targets this dir)
|
|
532
|
+
// project → <workspace>/skills/<name>/SKILL.md (the highest-priority
|
|
533
|
+
// documented skill root; <workspace> is the SAME root the memory
|
|
534
|
+
// surface resolves, so both content surfaces stay consistent).
|
|
535
|
+
/**
|
|
536
|
+
* Per-skill directory for ctx.scope:
|
|
537
|
+
* user → ~/.openclaw/skills/<name> (`--global` install target)
|
|
538
|
+
* project → <workspace>/skills/<name> (resolved agent workspace —
|
|
539
|
+
* consistent with this adapter's memoryTargets resolution).
|
|
540
|
+
*/
|
|
541
|
+
skillDir(ctx, name) {
|
|
542
|
+
const base = ctx.scope === "project" ? join(this.resolveWorkspace(), "skills") : join(dirname(resolveOpenClawConfigPath()), "skills");
|
|
543
|
+
return join(base, name);
|
|
544
|
+
}
|
|
545
|
+
installSkills(ctx) {
|
|
546
|
+
const { connector } = ctx;
|
|
547
|
+
if (connector.platforms[this.id]?.skills === false) {
|
|
548
|
+
return [{ platform: this.id, action: "skip", detail: `skills disabled for ${this.id}` }];
|
|
549
|
+
}
|
|
550
|
+
if (connector.skills.length === 0) {
|
|
551
|
+
return [{ platform: this.id, action: "skip", detail: "connector declares no skills" }];
|
|
552
|
+
}
|
|
553
|
+
const changes = [];
|
|
554
|
+
for (const skill of connector.skills) {
|
|
555
|
+
const dir = this.skillDir(ctx, skill.name);
|
|
556
|
+
changes.push(
|
|
557
|
+
this.writeContentFile(join(dir, "SKILL.md"), this.renderSkill(skill), ctx.dryRun)
|
|
558
|
+
);
|
|
559
|
+
for (const [rel, contents] of Object.entries(skill.resources ?? {})) {
|
|
560
|
+
const target = this.resolveWithin(dir, rel);
|
|
561
|
+
if (target === null) {
|
|
562
|
+
changes.push({
|
|
563
|
+
platform: this.id,
|
|
564
|
+
action: "warn",
|
|
565
|
+
detail: `skill resource "${rel}" escapes the skill dir; skipped`
|
|
566
|
+
});
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
changes.push(this.writeContentFile(target, contents, ctx.dryRun));
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return changes;
|
|
573
|
+
}
|
|
574
|
+
uninstallSkills(ctx) {
|
|
575
|
+
const { connector } = ctx;
|
|
576
|
+
if (connector.skills.length === 0) {
|
|
577
|
+
return [{ platform: this.id, action: "skip", detail: "connector declares no skills" }];
|
|
578
|
+
}
|
|
579
|
+
const changes = [];
|
|
580
|
+
for (const skill of connector.skills) {
|
|
581
|
+
const dir = this.skillDir(ctx, skill.name);
|
|
582
|
+
changes.push(this.removeContentFile(join(dir, "SKILL.md"), ctx.dryRun));
|
|
583
|
+
for (const rel of Object.keys(skill.resources ?? {})) {
|
|
584
|
+
const target = this.resolveWithin(dir, rel);
|
|
585
|
+
if (target === null) continue;
|
|
586
|
+
changes.push(this.removeContentFile(target, ctx.dryRun));
|
|
587
|
+
}
|
|
588
|
+
changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));
|
|
589
|
+
}
|
|
590
|
+
return changes;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Render a skill's SKILL.md: frontmatter (name, description + optional model,
|
|
594
|
+
* allowed-tools, disable-model-invocation) + body. UNIFORM with codex /
|
|
595
|
+
* claude-code — only the parent dir differs (single-line frontmatter keys;
|
|
596
|
+
* OpenClaw follows the AgentSkills spec).
|
|
597
|
+
*/
|
|
598
|
+
renderSkill(skill) {
|
|
599
|
+
const frontmatter = {
|
|
600
|
+
name: skill.name,
|
|
601
|
+
description: skill.description
|
|
602
|
+
};
|
|
603
|
+
if (skill.model !== void 0) frontmatter.model = skill.model;
|
|
604
|
+
const allow = skill.tools?.allow;
|
|
605
|
+
if (allow && allow.length > 0) frontmatter["allowed-tools"] = allow.join(", ");
|
|
606
|
+
if (skill.disableModelInvocation !== void 0) {
|
|
607
|
+
frontmatter["disable-model-invocation"] = skill.disableModelInvocation;
|
|
608
|
+
}
|
|
609
|
+
if (skill.extra) Object.assign(frontmatter, skill.extra);
|
|
610
|
+
return this.renderFrontmatterMd(frontmatter, skill.body);
|
|
611
|
+
}
|
|
507
612
|
// ── ts-plugin synthesis ────────────────────────────────────────────────
|
|
508
613
|
/**
|
|
509
614
|
* Build ONE self-contained ESM plugin module for OpenClaw.
|
|
@@ -562,21 +667,7 @@ var OpenClawAdapter = class extends BaseAdapter {
|
|
|
562
667
|
*/
|
|
563
668
|
import { execFileSync, execSync } from "node:child_process";
|
|
564
669
|
|
|
565
|
-
const HOME_BIN = ` + homeBin + ";\nconst CONNECTOR_ID = " + connectorId +
|
|
566
|
-
|
|
567
|
-
/**
|
|
568
|
-
* Invoke the universal hook entrypoint for one canonical event.
|
|
569
|
-
* @param {string} event canonical event name
|
|
570
|
-
* (PreToolUse|PostToolUse|SessionStart|SubagentStart|SubagentStop)
|
|
571
|
-
* @param {object} payload OpenClaw-shaped payload posted on stdin
|
|
572
|
-
* @returns {object|null} normalized HookResponse, or null on any failure
|
|
573
|
-
*/
|
|
574
|
-
function bridge(event, payload) {
|
|
575
|
-
try {
|
|
576
|
-
// On Windows HOME_BIN is the agent-connector.cmd launcher: Node cannot
|
|
577
|
-
// execFile a batch file, and shell+args is deprecated (DEP0190), so run
|
|
578
|
-
// one quoted command line via a shell. POSIX keeps the direct execFile.
|
|
579
|
-
const args = ["hook", "openclaw", event, "--connector", CONNECTOR_ID];
|
|
670
|
+
const HOME_BIN = ` + homeBin + ";\nconst CONNECTOR_ID = " + connectorId + ';\n\n/**\n * Invoke the universal hook entrypoint for one canonical event.\n * @param {string} event canonical event name\n * (PreToolUse|PostToolUse|SessionStart|SubagentStart|SubagentStop)\n * @param {object} payload OpenClaw-shaped payload posted on stdin\n * @returns {object|null} normalized HookResponse, or null on any failure\n */\nfunction bridge(event, payload) {\n try {\n // On Windows HOME_BIN is the agent-connector.cmd launcher: Node cannot\n // execFile a batch file, and shell+args is deprecated (DEP0190), so run\n // one quoted command line via a shell. POSIX keeps the direct execFile.\n const args = ["hook", ' + JSON.stringify(this.id) + `, event, "--connector", CONNECTOR_ID];
|
|
580
671
|
const opts = { input: JSON.stringify(payload), encoding: "utf8" };
|
|
581
672
|
const stdout =
|
|
582
673
|
process.platform === "win32"
|
|
@@ -659,7 +750,7 @@ let contextInjected = false;
|
|
|
659
750
|
parseEvent(event, raw) {
|
|
660
751
|
const input = raw ?? {};
|
|
661
752
|
const base = {
|
|
662
|
-
hostPlatform:
|
|
753
|
+
hostPlatform: this.id,
|
|
663
754
|
connectorId: "",
|
|
664
755
|
sessionId: typeof input.sessionId === "string" ? input.sessionId : "",
|
|
665
756
|
raw,
|
|
@@ -840,9 +931,10 @@ function isNonEmptyString(value) {
|
|
|
840
931
|
}
|
|
841
932
|
var adapter = new OpenClawAdapter();
|
|
842
933
|
var openclaw_default = adapter;
|
|
934
|
+
|
|
843
935
|
export {
|
|
844
936
|
OpenClawAdapter,
|
|
845
937
|
adapter,
|
|
846
|
-
openclaw_default
|
|
938
|
+
openclaw_default
|
|
847
939
|
};
|
|
848
|
-
//# sourceMappingURL=
|
|
940
|
+
//# sourceMappingURL=chunk-KGGBNKI4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/openclaw/index.ts"],"sourcesContent":["/**\n * adapters/openclaw — OpenClaw (Gateway) platform adapter for agent-connector.\n *\n * OpenClaw is a **ts-plugin** host, but with a twist no other ts-plugin host\n * has: a plugin only delivers its MCP tools to the agent when it is registered\n * in TWO places at once (DUAL REGISTRATION):\n *\n * (a) plugins.entries.<id> = { enabled: true } + plugins.load.paths: [dir]\n * — so the gateway DISCOVERS and LOADS the plugin\n * module (runs register(api), wiring the hooks).\n * NOTE: the entry is { enabled } only — a per-entry\n * `module` field is rejected by `openclaw config\n * validate`, so discovery goes through the\n * plugins.load.paths dir scan instead; and\n * (b) mcp.servers.<id> — nested under a top-level \"mcp\" object, so the\n * plugin's MCP tools SURFACE to the agent.\n *\n * If only (a) is present, the plugin loads but ZERO tools reach the agent.\n * If only (b) is present, the tools are declared but the hook bridge never\n * loads. context-mode's own adapter proved this empirically against OpenClaw\n * 2026.4.22 (src/adapters/openclaw/index.ts checkPluginRegistration: \"in\n * plugins.entries but missing from mcp.servers — plugin loads but no ctx_*\n * tools reach the agent\"). So getHealthChecks() FAILS when the two are\n * inconsistent (entries XOR mcp.servers).\n *\n * Why we BRIDGE instead of importing handlers (mirrors the OpenCode adapter):\n * context-mode could load ITS OWN plugin in-process because the handler code\n * shipped with the package. agent-connector cannot: the connector's hook\n * handlers are arbitrary developer code we must not import into the gateway's\n * runtime (wrong cwd, wrong deps, version skew). So installHooks synthesizes a\n * tiny, fully self-contained ESM plugin module — { id, name, configSchema,\n * register(api) } — that imports NOTHING from agent-connector and, on each\n * hook firing, shells out to the ONE stable home binary's universal entrypoint\n * <homeBin> hook openclaw <event> --connector <id>\n * feeding the event payload on stdin and JSON.parsing the normalized\n * HookResponse back from stdout. Fail-open: any bridge error → no-op. The same\n * universal json-stdio dispatcher every other host uses thus serves OpenClaw.\n *\n * Plugin module shape (grounded in context-mode build/adapters/openclaw/plugin.js):\n * export default { id, name, configSchema, register(api) }\n * register(api) wires typed lifecycle hooks via api.on(event, handler):\n * before_tool_call → block (return { block, blockReason }) / rewrite args\n * (mutate event.params in place); deny+ask both block.\n * after_tool_call → observe (capture result/output).\n * session_start → record the real session id.\n * before_prompt_build → inject SessionStart additionalContext via\n * { appendSystemContext } (the verified context-injection\n * point; session_start itself returns no context payload).\n * subagent_spawned / subagent_ended → observe the subagent lifecycle\n * (SubagentStart / SubagentStop; observe-only — no\n * decision or context payload).\n * api.on() is the correct API for typed lifecycle hooks; api.registerHook() is\n * for generic command hooks. We register defensively (api.on present check).\n *\n * Config file (JSON5 / JSONC — PARSE TOLERANTLY):\n * user scope → resolveOpenClawConfigPath():\n * $OPENCLAW_CONFIG_PATH\n * else $OPENCLAW_STATE_DIR/openclaw.json\n * else ~/.openclaw/openclaw.json\n * (mirrors context-mode openclawConfigPath — the file the\n * gateway actually loads, NOT process.cwd()).\n * project scope → <projectDir>/openclaw.json (also supported).\n * openclaw.json may contain comments + trailing commas (JSON5), so we NEVER\n * use strict JSON.parse — readJson is overridden to strip comments/commas\n * first via the shared core/jsonc parseJsonc. writes are strict JSON\n * (idempotent, comment-free).\n *\n * Plugin module location (project | user):\n * user → <stateDir>/extensions/<id>/index.mjs (stateDir = dir of openclaw.json)\n * project → <projectDir>/.openclaw/extensions/<id>/index.mjs\n * Beside index.mjs we also emit an openclaw.plugin.json manifest. The plugin's\n * DIRECTORY (not the file) is added to plugins.load.paths so the gateway scans\n * it and loads the module; the plugins.entries.<id> = { enabled: true } half\n * then activates it. (There is no per-entry \"module\" field — validate rejects it.)\n *\n * The gateway hot-reloads its config on SIGUSR1 — no action needed here; the\n * next reload (or restart) picks up our entries/servers/module.\n *\n * Capabilities (per OpenClaw hook API):\n * preToolUse / postToolUse / sessionStart / subagentStart / subagentStop\n * true; transports [\"stdio\"].\n * before_tool_call mutates event.params → canModifyArgs true.\n * after_tool_call cannot rewrite the tool result → canModifyOutput false.\n * before_prompt_build injects context → canInjectSessionContext true.\n */\n\nimport {\n chmodSync,\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\n\nimport { BaseAdapter } from \"../base.js\";\nimport type {\n Adapter,\n GeneratedPluginFile,\n HookReply,\n InstallContext,\n MemoryTarget,\n NormalizedEvent,\n} from \"../spi.js\";\nimport type {\n ChangeRecord,\n DetectedPlatform,\n HealthCheck,\n HookEventName,\n HookParadigm,\n HookResponse,\n PlatformCapabilities,\n PlatformId,\n PostToolUseEvent,\n PreToolUseEvent,\n ServerDef,\n SessionStartEvent,\n SkillDef,\n SubagentStartEvent,\n SubagentStopEvent,\n} from \"../../core/types.js\";\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\nimport { parseJsonc } from \"../../core/jsonc.js\";\nimport {\n buildServeWrapperCommand,\n shouldWrapForTelemetry,\n} from \"../../core/spawn.js\";\n\nconst HOST: PlatformId = \"openclaw\";\n\n/**\n * MCP servers live nested under the top-level \"mcp\" object: mcp.servers.<id>\n * (NOT a top-level \"mcpServers\" key). plugins.entries.<id> is the second half of\n * the dual registration.\n */\nconst MCP_ROOT_KEY = \"mcp\";\nconst MCP_SERVERS_KEY = \"servers\";\nconst PLUGINS_KEY = \"plugins\";\nconst PLUGINS_ENTRIES_KEY = \"entries\";\n/**\n * The gateway discovers a synthesized plugin module by scanning the directories\n * listed in plugins.load.paths — it does NOT take a per-entry `module` field\n * (that fails `openclaw config validate`). We add the plugin's own dir here.\n */\nconst PLUGINS_LOAD_KEY = \"load\";\nconst PLUGINS_LOAD_PATHS_KEY = \"paths\";\n/** Manifest the gateway reads inside a plugins.load.paths directory. */\nconst PLUGIN_MANIFEST_FILE = \"openclaw.plugin.json\";\n\n/**\n * Canonical → OpenClaw event name map. A connector hook event is only emitted by\n * the generated plugin when it appears here AND is declared by the connector.\n *\n * SessionStart maps to OpenClaw's session_start (to learn the real session id)\n * plus before_prompt_build (the verified context-injection point — session_start\n * itself returns no context payload upstream).\n */\nconst EVENT_TO_OPENCLAW: Partial<Record<HookEventName, string>> = {\n PreToolUse: \"before_tool_call\",\n PostToolUse: \"after_tool_call\",\n SessionStart: \"session_start\",\n // Subagent lifecycle: OpenClaw observes launch + completion (no decision\n // control on either). PermissionRequest is deliberately ABSENT — OpenClaw's\n // permission gate is the requireApproval RETURN VALUE of before_tool_call,\n // not an observable event; PostToolUseFailure is ABSENT too (a failure\n // arrives merged into after_tool_call, already surfaced as PostToolUse\n // isError). Unmapped declared events are reported as \"unsupported here\".\n SubagentStart: \"subagent_spawned\",\n SubagentStop: \"subagent_ended\",\n};\n\n/** Raw payload the generated plugin posts to the universal hook entrypoint. */\ninterface OpenClawBridgePayload {\n toolName?: string;\n toolInput?: Record<string, unknown>;\n toolOutput?: string;\n isError?: boolean;\n sessionId?: string;\n source?: string;\n projectDir?: string;\n // subagent_spawned / subagent_ended (the generated plugin normalizes the\n // host's field-name variants before posting; empty strings are omitted).\n agentId?: string;\n agentType?: string;\n lastAssistantMessage?: string;\n}\n\n/**\n * Native MCP server entry shapes OpenClaw accepts under mcp.servers.<id>.\n *\n * A stdio sidecar carries NO `transport` field: OpenClaw's config validator\n * only accepts `transport: \"sse\" | \"streamable-http\"` for remote servers and\n * REJECTS `transport: \"stdio\"` (verified against OpenClaw 2026.6.1). A stdio\n * server is INFERRED from the presence of `command`, so we must never write a\n * transport key here.\n */\ninterface OpenClawStdioServer {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n enabled?: boolean;\n}\ninterface OpenClawRemoteServer {\n url: string;\n transport: \"sse\" | \"http\";\n headers?: Record<string, string>;\n enabled?: boolean;\n}\n\n/**\n * plugins.entries.<id> reference shape.\n *\n * `openclaw config validate` REJECTS a `module` field here (it is not part of\n * the entry schema), so the entry is `{ enabled: true }` ONLY. Discovery of the\n * synthesized module is wired separately via `plugins.load.paths` (the dir that\n * holds the module + its openclaw.plugin.json manifest).\n */\ninterface OpenClawPluginEntry {\n enabled: boolean;\n}\n\nexport class OpenClawAdapter extends BaseAdapter implements Adapter {\n readonly id: PlatformId = HOST;\n // Declared as `string` (not the literal \"OpenClaw\") so a fork adapter can\n // override it — NemoClaw extends this class (precedent: AntigravityAdapter,\n // whose name is likewise typed `string` for AntigravityCliAdapter).\n readonly name: string = \"OpenClaw\";\n readonly paradigm: HookParadigm = \"ts-plugin\";\n\n readonly capabilities: PlatformCapabilities = {\n // Memory surface: AGENTS.md-first managed block. OpenClaw's AGENTS.md\n // lives in the AGENT WORKSPACE (default ~/.openclaw/workspace), NOT the\n // user's repo — memoryTargets below maps BOTH scopes there (R2:\n // platforms force scopes; personal-assistant semantics, no project file).\n supportsMemory: true,\n preToolUse: true,\n postToolUse: true,\n // OpenClaw compaction is owned by a context engine, not wired through the\n // universal bridge here.\n preCompact: false,\n sessionStart: true,\n sessionEnd: false,\n userPromptSubmit: false,\n stop: false,\n notification: false,\n // Newer events: OpenClaw observes the full subagent lifecycle via\n // subagent_spawned / subagent_ended (observe-only — no decision control,\n // so a SubagentStop \"deny\" cannot keep the subagent running here).\n // permissionRequest / postToolUseFailure stay unset: the permission gate\n // is a RETURN VALUE of before_tool_call (not an event) and tool failures\n // arrive merged into after_tool_call. Install reports the standard\n // \"unsupported here\" for unmapped declared events.\n subagentStart: true,\n subagentStop: true,\n // before_tool_call mutates event.params in place → input rewrite supported.\n canModifyArgs: true,\n // after_tool_call observes but cannot rewrite the tool result upstream.\n canModifyOutput: false,\n // injected via before_prompt_build at prompt assembly.\n canInjectSessionContext: true,\n // OpenClaw's MCP sidecar registration is stdio-only in practice.\n transports: [\"stdio\"],\n // Content surface: OpenClaw follows the AgentSkills spec (dir-per-skill\n // SKILL.md with {name, description} frontmatter + body). User scope →\n // ~/.openclaw/skills (the `openclaw skills install --global` target);\n // project scope → the resolved <workspace>/skills (the highest-priority\n // documented skill root, the same workspace the memory surface anchors to).\n supportsSkills: true,\n };\n\n // ── Tolerant JSON5/JSONC read (override base strict JSON.parse) ──────────\n\n /**\n * openclaw.json is officially JSON5 (comments + trailing commas allowed). A\n * strict JSON.parse false-fails a perfectly valid commented file, so we strip\n * comments and trailing commas before parsing. Writes remain strict JSON\n * (writeJson in BaseAdapter), which is itself valid JSON5 — so round-trips\n * stay safe and idempotent.\n */\n protected override readJson<T = Record<string, unknown>>(path: string): T | null {\n if (!existsSync(path)) return null;\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return null;\n }\n try {\n // Shared, string-aware JSONC stripper (the local stripJsonish whose\n // trailing-comma regex corrupted in-string \",]\"/\",}\"-like values is gone).\n return parseJsonc<T>(raw);\n } catch {\n return null;\n }\n }\n\n // ── Memory surface: agent-workspace AGENTS.md (both scopes) ─────────────\n /**\n * OpenClaw loads AGENTS.md (operating instructions) from its AGENT WORKSPACE\n * every session — `agents.defaults.workspace` in openclaw.json when set,\n * else `<stateDir>/workspace` (default ~/.openclaw/workspace). That file is\n * personal-assistant state, NOT a repo file, so a \"project scope\" write does\n * not apply: BOTH scopes map to the workspace (R2: platforms force scopes),\n * with the reason string flagging the semantics.\n */\n protected override memoryTargets(ctx: InstallContext): MemoryTarget[] {\n if (this.memoryOverride(ctx)?.path) return super.memoryTargets(ctx);\n if (ctx.scope !== \"project\" && ctx.scope !== \"user\") return [];\n return [\n {\n path: join(this.resolveWorkspace(), \"AGENTS.md\"),\n reason:\n \"openclaw agent-workspace AGENTS.md (workspace-scoped operating instructions, \" +\n \"not the project repo — both scopes map here)\",\n },\n ];\n }\n\n /**\n * Resolve OpenClaw's AGENT WORKSPACE — `agents.defaults.workspace` in\n * openclaw.json when set, else `<stateDir>/workspace` (default\n * ~/.openclaw/workspace). The same root the memory surface and the\n * project-scope skills surface both anchor to, so they never diverge.\n */\n private resolveWorkspace(): string {\n const configPath = resolveOpenClawConfigPath();\n const cfg = this.readJson<{ agents?: { defaults?: { workspace?: unknown } } }>(configPath);\n const declared = cfg?.agents?.defaults?.workspace;\n return typeof declared === \"string\" && declared.trim() !== \"\"\n ? declared.startsWith(\"~\")\n ? resolve(homedir(), declared.replace(/^~[/\\\\]?/, \"\"))\n : resolve(declared)\n : join(dirname(configPath), \"workspace\");\n }\n\n // ── Detection ──────────────────────────────────────────────────────────\n\n detectInstalled(projectDir: string): DetectedPlatform {\n const userConfig = resolveOpenClawConfigPath();\n const userDir = join(homedir(), \".openclaw\");\n const projectConfig = join(projectDir, \"openclaw.json\");\n\n // Fork-before-parent at DETECTION: NemoClaw DRIVES ~/.openclaw/openclaw.json,\n // so on a NemoClaw box (its ~/.nemoclaw/ marker present) the user-scope\n // OpenClaw config is nemoclaw-OWNED — defer so the planner does not double-\n // target the shared file as two platforms (an `uninstall openclaw` would\n // otherwise strip the connector's entries out from under nemoclaw). The\n // NemoClaw adapter overrides detectInstalled, so it is unaffected by this.\n const deferToNemoClaw = existsSync(join(homedir(), \".nemoclaw\"));\n const userInstalled =\n !deferToNemoClaw && (existsSync(userConfig) || existsSync(userDir));\n const projInstalled = existsSync(projectConfig);\n const installed = userInstalled || projInstalled;\n\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\n const configPath = scope === \"project\" ? projectConfig : userConfig;\n\n return {\n id: this.id,\n name: this.name,\n installed,\n paradigm: this.paradigm,\n capabilities: this.capabilities,\n configPath,\n scope,\n reason: installed\n ? scope === \"project\"\n ? `found project OpenClaw config at ${projectConfig}`\n : `found OpenClaw config at ${configPath}`\n : `no OpenClaw config at ${userConfig} or ${userDir}`,\n confidence: installed ? \"high\" : \"low\",\n };\n }\n\n // ── Native paths ───────────────────────────────────────────────────────\n\n getConfigDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? ctx.projectDir\n : dirname(resolveOpenClawConfigPath());\n }\n\n getServerConfigPath(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \"openclaw.json\")\n : resolveOpenClawConfigPath();\n }\n\n /**\n * For ts-plugin hosts the \"hook config path\" is the generated plugin FILE.\n * OpenClaw additionally needs the plugins.entries reference written into\n * openclaw.json (handled in installHooks); this is the module on disk.\n */\n getHookConfigPath(ctx: InstallContext): string {\n return join(this.pluginDir(ctx), this.pluginFileName());\n }\n\n /** Extensions directory the synthesized plugin module is written into. */\n private pluginDir(ctx: InstallContext): string {\n const base =\n ctx.scope === \"project\"\n ? join(ctx.projectDir, \".openclaw\")\n : dirname(resolveOpenClawConfigPath());\n return join(base, \"extensions\", ctx.connector.id);\n }\n\n /** Plugin module file name (ESM so the gateway loads it without a loader). */\n private pluginFileName(): string {\n return \"index.mjs\";\n }\n\n // ── MCP server install / uninstall (mcp.servers.<id>) ───────────────────\n\n installServer(ctx: InstallContext): ChangeRecord[] {\n const server = this.effectiveServer(ctx);\n const serverPath = this.getServerConfigPath(ctx);\n\n if (!server) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: serverPath,\n detail: ctx.connector.server\n ? \"server registration disabled for openclaw\"\n : \"connector declares no MCP server\",\n },\n ];\n }\n\n const entry = this.renderServerEntry(ctx, server);\n return [this.upsertNestedServer(serverPath, ctx.connector.id, entry, ctx.dryRun)];\n }\n\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\n const serverPath = this.getServerConfigPath(ctx);\n return [this.removeNestedServer(serverPath, ctx.connector.id, ctx.dryRun)];\n }\n\n /** Resolve the per-platform server override into an effective ServerDef. */\n private effectiveServer(ctx: InstallContext): ServerDef | undefined {\n const override = ctx.connector.platforms[this.id]?.server;\n if (override === false) return undefined;\n const base = ctx.connector.server;\n if (!base) return undefined;\n return override && typeof override === \"object\" ? { ...base, ...override } : base;\n }\n\n /**\n * Render a normalized ServerDef into OpenClaw's native mcp.servers.<id> entry.\n *\n * stdio → { command, args?, env? } (NO transport key — a stdio sidecar is\n * inferred from `command`; OpenClaw's validator rejects\n * transport:\"stdio\" and only accepts \"sse\"/\"streamable-http\")\n * remote → { url, transport: \"sse\"|\"http\", headers? }\n *\n * OpenClaw documents no native ${env:VAR} token, so refs resolve to literals.\n */\n private renderServerEntry(\n ctx: InstallContext,\n server: ServerDef,\n ): OpenClawStdioServer | OpenClawRemoteServer {\n if (server.transport === \"stdio\") {\n let command = server.command ?? \"\";\n let args = [...(server.args ?? [])];\n\n // Transparent telemetry wrapping: route the real command through\n // `<homeBin> serve --connector <id> -- <command> <args...>`.\n if (shouldWrapForTelemetry(server, ctx.connector.telemetry)) {\n const wrapped = buildServeWrapperCommand(\n ctx.homeBinPath,\n ctx.connector.id,\n command,\n args,\n ctx.scope,\n this.id,\n );\n command = wrapped.command;\n args = wrapped.args;\n }\n\n // NO `transport` key: OpenClaw infers a stdio sidecar from `command` and\n // its validator rejects transport:\"stdio\" (only \"sse\"/\"streamable-http\").\n const entry: OpenClawStdioServer = {\n command: resolveEnvRefsDeep(command),\n };\n const resolvedArgs = resolveEnvRefsDeep(args).filter((s) => s !== \"\");\n if (resolvedArgs.length > 0) entry.args = resolvedArgs;\n const env = this.renderEnv(server.env);\n if (env) entry.env = env;\n if (server.enabled === false) entry.enabled = false;\n return entry;\n }\n\n // sse / http / ws → OpenClaw registers a remote URL sidecar.\n const transport: \"sse\" | \"http\" = server.transport === \"http\" ? \"http\" : \"sse\";\n const entry: OpenClawRemoteServer = {\n url: resolveEnvRefsDeep(server.url ?? \"\"),\n transport,\n };\n if (server.headers && Object.keys(server.headers).length > 0) {\n entry.headers = resolveEnvRefsDeep({ ...server.headers });\n }\n if (server.enabled === false) entry.enabled = false;\n return entry;\n }\n\n /** Resolve every ${env:VAR} in env values to literals (no native token). */\n private renderEnv(\n env: Record<string, string> | undefined,\n ): Record<string, string> | undefined {\n if (!env || Object.keys(env).length === 0) return undefined;\n return resolveEnvRefsDeep({ ...env });\n }\n\n /** Upsert config.mcp.servers.<id> idempotently. */\n private upsertNestedServer(\n path: string,\n id: string,\n entry: unknown,\n dryRun: boolean,\n ): ChangeRecord {\n // OVERWRITE GUARD: never blank a present-but-unparseable openclaw.json.\n if (this.isPresentButUnparseable(path)) {\n return {\n platform: this.id,\n action: \"warn\",\n path,\n detail: `existing ${path} is not parseable; left untouched (back it up / fix it, then re-run)`,\n };\n }\n const cfg = this.readJson<Record<string, unknown>>(path) ?? {};\n const servers = this.nestedServersBucket(cfg);\n const before = JSON.stringify(servers[id]);\n const after = JSON.stringify(entry);\n let action: ChangeRecord[\"action\"];\n if (before === undefined) action = \"create\";\n else if (before === after) action = \"skip\";\n else action = \"update\";\n if (action !== \"skip\") {\n servers[id] = entry;\n this.writeJson(path, cfg, dryRun);\n }\n return {\n platform: this.id,\n action,\n path,\n detail: `${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id}`,\n };\n }\n\n /** Remove config.mcp.servers.<id>. */\n private removeNestedServer(path: string, id: string, dryRun: boolean): ChangeRecord {\n const cfg = this.readJson<Record<string, unknown>>(path);\n const mcp = cfg?.[MCP_ROOT_KEY];\n const servers =\n mcp && typeof mcp === \"object\" && !Array.isArray(mcp)\n ? ((mcp as Record<string, unknown>)[MCP_SERVERS_KEY] as\n | Record<string, unknown>\n | undefined)\n : undefined;\n if (!cfg || !servers || !(id in servers)) {\n return {\n platform: this.id,\n action: \"skip\",\n path,\n detail: `${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id} absent`,\n };\n }\n delete servers[id];\n this.writeJson(path, cfg, dryRun);\n return {\n platform: this.id,\n action: \"remove\",\n path,\n detail: `${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id}`,\n };\n }\n\n /** Get-or-create config.mcp.servers as a mutable object. */\n private nestedServersBucket(cfg: Record<string, unknown>): Record<string, unknown> {\n let mcp = cfg[MCP_ROOT_KEY];\n if (!mcp || typeof mcp !== \"object\" || Array.isArray(mcp)) {\n mcp = {};\n cfg[MCP_ROOT_KEY] = mcp;\n }\n const mcpObj = mcp as Record<string, unknown>;\n let servers = mcpObj[MCP_SERVERS_KEY];\n if (!servers || typeof servers !== \"object\" || Array.isArray(servers)) {\n servers = {};\n mcpObj[MCP_SERVERS_KEY] = servers;\n }\n return servers as Record<string, unknown>;\n }\n\n // ── Hook install / uninstall (ts-plugin + DUAL REGISTRATION) ────────────\n\n installHooks(ctx: InstallContext): ChangeRecord[] {\n const pluginPath = this.getHookConfigPath(ctx);\n const configPath = this.getServerConfigPath(ctx);\n\n if (ctx.connector.platforms[this.id]?.hooks === false) {\n return [\n { platform: this.id, action: \"skip\", path: pluginPath, detail: `hooks disabled for ${this.id}` },\n ];\n }\n if (ctx.connector.hookEvents.length === 0) {\n return [\n { platform: this.id, action: \"skip\", path: pluginPath, detail: \"connector declares no hooks\" },\n ];\n }\n\n const changes: ChangeRecord[] = [];\n\n // 1. Write the synthesized plugin module(s).\n for (const file of this.synthesizePlugin(ctx)) {\n const before = existsSync(file.path) ? this.safeRead(file.path) : undefined;\n let action: ChangeRecord[\"action\"];\n if (before === undefined) action = \"create\";\n else if (before === file.contents) action = \"skip\";\n else action = \"update\";\n\n if (action !== \"skip\" && !ctx.dryRun) {\n ensureDir(dirname(file.path));\n writeFileSync(file.path, file.contents, \"utf8\");\n chmodSync(file.path, file.executable ? 0o755 : 0o644);\n }\n changes.push({\n platform: this.id,\n action,\n path: file.path,\n detail: `openclaw plugin module (${this.hookDetail(ctx)})`,\n });\n }\n\n // 2. DUAL REGISTRATION half (a): enable plugins.entries.<id> AND add the\n // plugin DIR to plugins.load.paths so the gateway discovers + loads the\n // module. (half (b), mcp.servers.<id>, is written by installServer — both\n // are required for tools to reach the agent.)\n changes.push(this.upsertPluginEntry(configPath, ctx, this.pluginDir(ctx)));\n\n return changes;\n }\n\n /**\n * Human-facing summary of which declared events the synthesized module ACTUALLY\n * wires. Only events present in EVENT_TO_OPENCLAW are mapped/wired; any declared\n * event with no OpenClaw mapping (e.g. UserPromptSubmit) is reported separately\n * as \"unsupported here\" so the detail never overstates coverage.\n */\n private hookDetail(ctx: InstallContext): string {\n const declared = ctx.connector.hookEvents;\n const mapped = declared.filter((e) => EVENT_TO_OPENCLAW[e] !== undefined);\n const unsupported = declared.filter((e) => EVENT_TO_OPENCLAW[e] === undefined);\n const base = mapped.join(\",\");\n return unsupported.length > 0\n ? `${base}; unsupported here: ${unsupported.join(\",\")}`\n : base;\n }\n\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\n const pluginPath = this.getHookConfigPath(ctx);\n const configPath = this.getServerConfigPath(ctx);\n const changes: ChangeRecord[] = [];\n\n // 1. Remove the plugins.entries.<id> reference AND drop the plugin dir from\n // plugins.load.paths.\n changes.push(\n this.removePluginEntry(configPath, ctx.connector.id, this.pluginDir(ctx), ctx.dryRun),\n );\n\n // 2. Remove the plugin module + its manifest on disk.\n const manifestPath = join(this.pluginDir(ctx), PLUGIN_MANIFEST_FILE);\n for (const [path, label] of [\n [pluginPath, \"openclaw plugin module\"],\n [manifestPath, \"openclaw plugin manifest\"],\n ] as const) {\n if (existsSync(path)) {\n if (!ctx.dryRun) rmSync(path, { force: true });\n changes.push({ platform: this.id, action: \"remove\", path, detail: label });\n } else {\n changes.push({\n platform: this.id,\n action: \"skip\",\n path,\n detail: `no ${label} present`,\n });\n }\n }\n\n // 3. Drop the now-empty plugin dir (only if WE own its full contents).\n changes.push(this.removeDirIfEmpty(this.pluginDir(ctx), ctx.dryRun));\n\n return changes;\n }\n\n /**\n * Upsert plugins.entries.<id> = { enabled: true } AND add `pluginDir` to\n * plugins.load.paths idempotently.\n *\n * `openclaw config validate` rejects a per-entry `module` field, so the entry\n * carries ONLY `{ enabled: true }`. The gateway discovers the synthesized\n * module by scanning the directories in plugins.load.paths (the dir holding\n * index.mjs + openclaw.plugin.json), so the dir is added there.\n */\n private upsertPluginEntry(\n configPath: string,\n ctx: InstallContext,\n pluginDir: string,\n ): ChangeRecord {\n // OVERWRITE GUARD: never blank a present-but-unparseable openclaw.json.\n if (this.isPresentButUnparseable(configPath)) {\n return {\n platform: this.id,\n action: \"warn\",\n path: configPath,\n detail: `existing ${configPath} is not parseable; left untouched (back it up / fix it, then re-run)`,\n };\n }\n const cfg = this.readJson<Record<string, unknown>>(configPath) ?? {};\n const entries = this.pluginEntriesBucket(cfg);\n const id = ctx.connector.id;\n const desired: OpenClawPluginEntry = { enabled: true };\n const entryBefore = JSON.stringify(entries[id]);\n const entryAfter = JSON.stringify(desired);\n\n // plugins.load.paths: add the plugin dir if absent.\n const loadPaths = this.pluginLoadPathsBucket(cfg);\n const pathPresent = loadPaths.includes(pluginDir);\n\n const entryChanged = entryBefore !== entryAfter;\n const pathChanged = !pathPresent;\n let action: ChangeRecord[\"action\"];\n if (entryBefore === undefined) action = \"create\";\n else if (!entryChanged && !pathChanged) action = \"skip\";\n else action = \"update\";\n\n if (action !== \"skip\") {\n entries[id] = desired;\n if (!pathPresent) loadPaths.push(pluginDir);\n this.writeJson(configPath, cfg, ctx.dryRun);\n }\n return {\n platform: this.id,\n action,\n path: configPath,\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id} + ${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}`,\n };\n }\n\n /** Remove plugins.entries.<id> AND drop `pluginDir` from plugins.load.paths. */\n private removePluginEntry(\n configPath: string,\n id: string,\n pluginDir: string,\n dryRun: boolean,\n ): ChangeRecord {\n const cfg = this.readJson<Record<string, unknown>>(configPath);\n const plugins = cfg?.[PLUGINS_KEY];\n const pluginsObj =\n plugins && typeof plugins === \"object\" && !Array.isArray(plugins)\n ? (plugins as Record<string, unknown>)\n : undefined;\n const entries = pluginsObj?.[PLUGINS_ENTRIES_KEY] as\n | Record<string, unknown>\n | undefined;\n const load = pluginsObj?.[PLUGINS_LOAD_KEY] as Record<string, unknown> | undefined;\n const loadPaths =\n load && Array.isArray(load[PLUGINS_LOAD_PATHS_KEY])\n ? (load[PLUGINS_LOAD_PATHS_KEY] as unknown[])\n : undefined;\n\n const entryPresent = Boolean(entries && id in entries);\n const pathIdx = loadPaths ? loadPaths.indexOf(pluginDir) : -1;\n\n if (!cfg || (!entryPresent && pathIdx < 0)) {\n return {\n platform: this.id,\n action: \"skip\",\n path: configPath,\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id} absent`,\n };\n }\n if (entryPresent && entries) delete entries[id];\n if (loadPaths && pathIdx >= 0) loadPaths.splice(pathIdx, 1);\n this.writeJson(configPath, cfg, dryRun);\n return {\n platform: this.id,\n action: \"remove\",\n path: configPath,\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id} + ${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}`,\n };\n }\n\n /** Get-or-create config.plugins.entries as a mutable object. */\n private pluginEntriesBucket(cfg: Record<string, unknown>): Record<string, unknown> {\n let plugins = cfg[PLUGINS_KEY];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) {\n plugins = {};\n cfg[PLUGINS_KEY] = plugins;\n }\n const pluginsObj = plugins as Record<string, unknown>;\n let entries = pluginsObj[PLUGINS_ENTRIES_KEY];\n if (!entries || typeof entries !== \"object\" || Array.isArray(entries)) {\n entries = {};\n pluginsObj[PLUGINS_ENTRIES_KEY] = entries;\n }\n return entries as Record<string, unknown>;\n }\n\n /** Get-or-create config.plugins.load.paths as a mutable string array. */\n private pluginLoadPathsBucket(cfg: Record<string, unknown>): string[] {\n let plugins = cfg[PLUGINS_KEY];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) {\n plugins = {};\n cfg[PLUGINS_KEY] = plugins;\n }\n const pluginsObj = plugins as Record<string, unknown>;\n let load = pluginsObj[PLUGINS_LOAD_KEY];\n if (!load || typeof load !== \"object\" || Array.isArray(load)) {\n load = {};\n pluginsObj[PLUGINS_LOAD_KEY] = load;\n }\n const loadObj = load as Record<string, unknown>;\n const existing = loadObj[PLUGINS_LOAD_PATHS_KEY];\n // Normalize to a fresh string[] (drop any non-string a user may have placed\n // here) and write it back so the caller mutates the live array.\n const paths = Array.isArray(existing)\n ? existing.filter((p): p is string => typeof p === \"string\")\n : [];\n loadObj[PLUGINS_LOAD_PATHS_KEY] = paths;\n return paths;\n }\n\n // ── Content surface: skills (dir-per-skill SKILL.md) ────────────────────\n // CONTENT-ONLY: pure native-file writers, mirroring codex/claude-code\n // installSkills + renderSkill. No runtime dispatch, no home-bin pointer, no\n // telemetry wrap. Each write is idempotent (byte-identical → skip) via\n // writeContentFile and reversible via removeContentFile + removeDirIfEmpty\n // (never rm -rf a shared dir). Honors platforms[\"openclaw\"].skills === false.\n //\n // Native locations (AgentSkills spec — dir-per-skill SKILL.md + resources):\n // user → ~/.openclaw/skills/<name>/SKILL.md (`openclaw skills install\n // --global` targets this dir)\n // project → <workspace>/skills/<name>/SKILL.md (the highest-priority\n // documented skill root; <workspace> is the SAME root the memory\n // surface resolves, so both content surfaces stay consistent).\n\n /**\n * Per-skill directory for ctx.scope:\n * user → ~/.openclaw/skills/<name> (`--global` install target)\n * project → <workspace>/skills/<name> (resolved agent workspace —\n * consistent with this adapter's memoryTargets resolution).\n */\n private skillDir(ctx: InstallContext, name: string): string {\n const base =\n ctx.scope === \"project\"\n ? join(this.resolveWorkspace(), \"skills\")\n : join(dirname(resolveOpenClawConfigPath()), \"skills\");\n return join(base, name);\n }\n\n override installSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[this.id]?.skills === false) {\n return [{ platform: this.id, action: \"skip\", detail: `skills disabled for ${this.id}` }];\n }\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n changes.push(\n this.writeContentFile(join(dir, \"SKILL.md\"), this.renderSkill(skill), ctx.dryRun),\n );\n // Bundle any resource files beside SKILL.md (relative path → contents).\n // Defense-in-depth: skip+warn on any key that escapes the skill dir\n // (config-time validation already rejects these, but never trust input).\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) {\n changes.push({\n platform: this.id,\n action: \"warn\",\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\n });\n continue;\n }\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\n }\n }\n return changes;\n }\n\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n // Remove only the files we wrote (SKILL.md + declared resources), then the\n // skill dir itself when we own its full contents.\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\n for (const rel of Object.keys(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) continue; // never delete outside the skill dir\n changes.push(this.removeContentFile(target, ctx.dryRun));\n }\n // Only remove the skill dir when WE own its full contents — never rm -rf a\n // dir that still holds user-added / sibling-tool / shared files.\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\n }\n return changes;\n }\n\n /**\n * Render a skill's SKILL.md: frontmatter (name, description + optional model,\n * allowed-tools, disable-model-invocation) + body. UNIFORM with codex /\n * claude-code — only the parent dir differs (single-line frontmatter keys;\n * OpenClaw follows the AgentSkills spec).\n */\n private renderSkill(skill: SkillDef): string {\n const frontmatter: Record<string, unknown> = {\n name: skill.name,\n description: skill.description,\n };\n if (skill.model !== undefined) frontmatter.model = skill.model;\n const allow = skill.tools?.allow;\n if (allow && allow.length > 0) frontmatter[\"allowed-tools\"] = allow.join(\", \");\n if (skill.disableModelInvocation !== undefined) {\n frontmatter[\"disable-model-invocation\"] = skill.disableModelInvocation;\n }\n if (skill.extra) Object.assign(frontmatter, skill.extra);\n return this.renderFrontmatterMd(frontmatter, skill.body);\n }\n\n // ── ts-plugin synthesis ────────────────────────────────────────────────\n\n /**\n * Build ONE self-contained ESM plugin module for OpenClaw.\n *\n * The module imports nothing from agent-connector. It embeds two constants\n * (the absolute home-bin path and the connector id) and a `bridge()` helper\n * that shells out to the universal hook entrypoint via execFileSync, feeding\n * the OpenClaw event payload on stdin and JSON.parsing the normalized\n * HookResponse back from stdout (fail-open: any error → null). Its default\n * export is the OpenClaw plugin definition { id, name, configSchema,\n * register(api) }; register wires api.on(...) handlers for the events this\n * connector actually declares.\n */\n synthesizePlugin(ctx: InstallContext): GeneratedPluginFile[] {\n const path = this.getHookConfigPath(ctx);\n const contents = this.buildPluginSource(ctx);\n // The gateway scans plugins.load.paths dirs for a manifest, so emit an\n // openclaw.plugin.json beside index.mjs pointing at the module entry.\n const manifestPath = join(this.pluginDir(ctx), PLUGIN_MANIFEST_FILE);\n const manifest = this.buildPluginManifest(ctx);\n return [\n { path, contents, executable: false },\n { path: manifestPath, contents: manifest, executable: false },\n ];\n }\n\n /**\n * Build the openclaw.plugin.json manifest the gateway reads inside a\n * plugins.load.paths directory. It names the plugin and points at the ESM\n * module entry (index.mjs) so the loader knows what to import.\n */\n private buildPluginManifest(ctx: InstallContext): string {\n const manifest = {\n id: ctx.connector.id,\n name: ctx.connector.displayName || ctx.connector.id,\n main: this.pluginFileName(),\n enabled: true,\n };\n return `${JSON.stringify(manifest, null, 2)}\\n`;\n }\n\n /** Compose the generated plugin source with plain string concatenation. */\n private buildPluginSource(ctx: InstallContext): string {\n const homeBin = JSON.stringify(ctx.homeBinPath);\n const connectorId = JSON.stringify(ctx.connector.id);\n const connectorName = JSON.stringify(ctx.connector.displayName || ctx.connector.id);\n\n const events = ctx.connector.hookEvents.filter(\n (e): e is HookEventName => EVENT_TO_OPENCLAW[e] !== undefined,\n );\n const has = (e: HookEventName) => events.includes(e);\n\n const header =\n \"/**\\n\" +\n \" * AUTO-GENERATED by agent-connector — DO NOT EDIT.\\n\" +\n \" *\\n\" +\n \" * Self-contained OpenClaw plugin bridge for connector \" +\n ctx.connector.id +\n \".\\n\" +\n \" * It imports nothing from agent-connector: every hook invocation shells out\\n\" +\n \" * to the stable home binary's universal entrypoint and JSON-parses the\\n\" +\n \" * normalized response. Fail-open: any bridge error degrades to a no-op.\\n\" +\n \" *\\n\" +\n \" * Registered via DUAL REGISTRATION in openclaw.json: plugins.entries.<id>\\n\" +\n \" * (loads this module) + mcp.servers.<id> (surfaces the MCP tools). The\\n\" +\n \" * gateway hot-reloads its config on SIGUSR1.\\n\" +\n \" */\\n\" +\n 'import { execFileSync, execSync } from \"node:child_process\";\\n\\n' +\n \"const HOME_BIN = \" +\n homeBin +\n \";\\n\" +\n \"const CONNECTOR_ID = \" +\n connectorId +\n \";\\n\\n\" +\n \"/**\\n\" +\n \" * Invoke the universal hook entrypoint for one canonical event.\\n\" +\n \" * @param {string} event canonical event name\\n\" +\n \" * (PreToolUse|PostToolUse|SessionStart|SubagentStart|SubagentStop)\\n\" +\n \" * @param {object} payload OpenClaw-shaped payload posted on stdin\\n\" +\n \" * @returns {object|null} normalized HookResponse, or null on any failure\\n\" +\n \" */\\n\" +\n \"function bridge(event, payload) {\\n\" +\n \" try {\\n\" +\n \" // On Windows HOME_BIN is the agent-connector.cmd launcher: Node cannot\\n\" +\n \" // execFile a batch file, and shell+args is deprecated (DEP0190), so run\\n\" +\n \" // one quoted command line via a shell. POSIX keeps the direct execFile.\\n\" +\n ' const args = [\"hook\", ' + JSON.stringify(this.id) + ', event, \"--connector\", CONNECTOR_ID];\\n' +\n ' const opts = { input: JSON.stringify(payload), encoding: \"utf8\" };\\n' +\n \" const stdout =\\n\" +\n ' process.platform === \"win32\"\\n' +\n \" ? execSync([HOME_BIN, ...args].map((a) => '\\\"' + a + '\\\"').join(\\\" \\\"), opts)\\n\" +\n \" : execFileSync(HOME_BIN, args, opts);\\n\" +\n ' const text = (stdout || \"\").trim();\\n' +\n ' if (text === \"\") return { decision: \"allow\" };\\n' +\n \" return JSON.parse(text);\\n\" +\n \" } catch {\\n\" +\n \" // Fail-open — never wedge a tool call on a bridge error.\\n\" +\n \" return null;\\n\" +\n \" }\\n\" +\n \"}\\n\\n\" +\n \"// Module-scoped context the prompt-build hook injects at session start.\\n\" +\n \"// before_prompt_build is the verified injection point (session_start itself\\n\" +\n \"// returns no context payload). The flag prevents double-injection.\\n\" +\n \"let PROJECT_DIR = process.cwd();\\n\" +\n \"let SESSION_ID = \\\"\\\";\\n\" +\n \"let pendingContext = null;\\n\" +\n \"let contextInjected = false;\\n\\n\";\n\n // register(api) body — wire only the handlers this connector declares.\n const reg: string[] = [];\n\n reg.push(\n \" register(api) {\\n\" +\n \" PROJECT_DIR = process.cwd();\\n\" +\n \" const on = (event, handler) => {\\n\" +\n ' if (typeof api?.on === \"function\") {\\n' +\n \" try { api.on(event, handler); return; } catch { /* fall through */ }\\n\" +\n \" }\\n\" +\n ' if (typeof api?.registerHook === \"function\") {\\n' +\n \" try {\\n\" +\n ' api.registerHook(event, handler, { name: CONNECTOR_ID + \":\" + event, description: \"agent-connector bridge\" });\\n' +\n \" } catch { /* best effort */ }\\n\" +\n \" }\\n\" +\n \" };\\n\",\n );\n\n if (has(\"PreToolUse\")) {\n reg.push(\n \"\\n\" +\n \" // PreToolUse → before_tool_call: block (return { block, blockReason })\\n\" +\n \" // for deny/ask; rewrite args by mutating event.params in place for modify.\\n\" +\n ' on(\"before_tool_call\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" const toolInput = e.params || {};\\n\" +\n \" const res = bridge(\\\"PreToolUse\\\", {\\n\" +\n ' toolName: e.toolName || \"\",\\n' +\n \" toolInput,\\n\" +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" if (!res) return undefined;\\n\" +\n \" // OpenClaw has no separate ask gate — deny and ask both block (safe).\\n\" +\n ' if (res.decision === \"deny\" || res.decision === \"ask\") {\\n' +\n ' return { block: true, blockReason: res.reason || (\"Blocked by \" + CONNECTOR_ID) };\\n' +\n \" }\\n\" +\n ' if (res.decision === \"modify\" && res.updatedInput && typeof toolInput === \"object\") {\\n' +\n \" Object.assign(toolInput, res.updatedInput);\\n\" +\n \" }\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n if (has(\"PostToolUse\")) {\n reg.push(\n \"\\n\" +\n \" // PostToolUse → after_tool_call: observe the completed tool call.\\n\" +\n \" // OpenClaw exposes the result (v2+) or output (older builds); it cannot\\n\" +\n \" // rewrite the tool result, so updatedOutput is intentionally dropped.\\n\" +\n ' on(\"after_tool_call\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" const rawResult = e.result !== undefined ? e.result : e.output;\\n\" +\n \" const toolOutput =\\n\" +\n ' typeof rawResult === \"string\"\\n' +\n \" ? rawResult\\n\" +\n \" : rawResult != null\\n\" +\n \" ? JSON.stringify(rawResult)\\n\" +\n \" : undefined;\\n\" +\n \" bridge(\\\"PostToolUse\\\", {\\n\" +\n ' toolName: e.toolName || \"\",\\n' +\n \" toolInput: e.params || {},\\n\" +\n \" toolOutput,\\n\" +\n \" isError: Boolean(e.error || e.isError),\\n\" +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n if (has(\"SubagentStart\")) {\n reg.push(\n \"\\n\" +\n \" // SubagentStart → subagent_spawned: observe-only (OpenClaw exposes no\\n\" +\n \" // decision or context-injection payload for subagent launches). Field\\n\" +\n \" // names vary across builds, so the common variants are normalized;\\n\" +\n \" // empty values are omitted so the dispatcher never matcher-filters on\\n\" +\n ' // an empty agent type (fail-open).\\n' +\n ' on(\"subagent_spawned\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" bridge(\\\"SubagentStart\\\", {\\n\" +\n \" agentId: e.agentId || e.subagentId || undefined,\\n\" +\n \" agentType: e.agentType || e.subagentType || e.agent || undefined,\\n\" +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n if (has(\"SubagentStop\")) {\n reg.push(\n \"\\n\" +\n \" // SubagentStop → subagent_ended: observe-only completion. A normalized\\n\" +\n \" // 'deny' cannot keep the subagent running on OpenClaw, so the bridge\\n\" +\n \" // reply is intentionally ignored.\\n\" +\n ' on(\"subagent_ended\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" const rawResult = e.result !== undefined ? e.result : e.output;\\n\" +\n \" bridge(\\\"SubagentStop\\\", {\\n\" +\n \" agentId: e.agentId || e.subagentId || undefined,\\n\" +\n \" agentType: e.agentType || e.subagentType || e.agent || undefined,\\n\" +\n \" lastAssistantMessage:\\n\" +\n ' typeof rawResult === \"string\" && rawResult !== \"\" ? rawResult : undefined,\\n' +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n if (has(\"SessionStart\")) {\n reg.push(\n \"\\n\" +\n \" // SessionStart → session_start records the real session id and fetches\\n\" +\n \" // the additionalContext to inject; before_prompt_build performs the\\n\" +\n \" // injection (the verified context-injection point upstream).\\n\" +\n ' on(\"session_start\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" SESSION_ID = e.sessionId || e.sessionKey || SESSION_ID;\\n\" +\n \" const res = bridge(\\\"SessionStart\\\", {\\n\" +\n \" sessionId: SESSION_ID,\\n\" +\n ' source: e.resumedFrom ? \"resume\" : \"startup\",\\n' +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" contextInjected = false;\\n\" +\n \" pendingContext =\\n\" +\n ' res && typeof res.additionalContext === \"string\" && res.additionalContext\\n' +\n \" ? res.additionalContext\\n\" +\n \" : null;\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\" +\n \"\\n\" +\n \" // Inject the session-start context once, at prompt-build time.\\n\" +\n ' on(\"before_prompt_build\", () => {\\n' +\n \" if (contextInjected || !pendingContext) return undefined;\\n\" +\n \" contextInjected = true;\\n\" +\n \" return { appendSystemContext: pendingContext };\\n\" +\n \" });\\n\",\n );\n }\n\n reg.push(\" },\\n\");\n\n const definition =\n \"const plugin = {\\n\" +\n \" id: CONNECTOR_ID,\\n\" +\n \" name: \" +\n connectorName +\n \",\\n\" +\n \" configSchema: {\\n\" +\n ' type: \"object\",\\n' +\n \" properties: {\\n\" +\n \" enabled: {\\n\" +\n ' type: \"boolean\",\\n' +\n \" default: true,\\n\" +\n ' description: \"Enable or disable the \" + CONNECTOR_ID + \" plugin.\",\\n' +\n \" },\\n\" +\n \" },\\n\" +\n \" additionalProperties: false,\\n\" +\n \" },\\n\" +\n reg.join(\"\") +\n \"};\\n\\n\" +\n \"export default plugin;\\n\";\n\n return header + definition;\n }\n\n // ── Runtime: parse OUR bridge payload → normalized event ───────────────\n\n /**\n * `raw` is the payload OUR generated plugin posts (NOT a host-native shape):\n * { toolName, toolInput, toolOutput?, isError?, sessionId, source?, projectDir }\n * so this maps straight through.\n */\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\n const input = (raw ?? {}) as OpenClawBridgePayload;\n const base = {\n hostPlatform: this.id,\n connectorId: \"\",\n sessionId: typeof input.sessionId === \"string\" ? input.sessionId : \"\",\n raw,\n ...(typeof input.projectDir === \"string\" ? { projectDir: input.projectDir } : {}),\n } as const;\n\n switch (event) {\n case \"PreToolUse\": {\n const ev: PreToolUseEvent = {\n ...base,\n toolName: input.toolName ?? \"\",\n toolInput: input.toolInput ?? {},\n };\n return ev;\n }\n case \"PostToolUse\": {\n const ev: PostToolUseEvent = {\n ...base,\n toolName: input.toolName ?? \"\",\n toolInput: input.toolInput ?? {},\n ...(typeof input.toolOutput === \"string\" ? { toolOutput: input.toolOutput } : {}),\n ...(typeof input.isError === \"boolean\" ? { isError: input.isError } : {}),\n };\n return ev;\n }\n case \"SessionStart\": {\n const source: SessionStartEvent[\"source\"] =\n input.source === \"resume\"\n ? \"resume\"\n : input.source === \"compact\"\n ? \"compact\"\n : input.source === \"clear\"\n ? \"clear\"\n : \"startup\";\n const ev: SessionStartEvent = { ...base, source };\n return ev;\n }\n case \"SubagentStart\": {\n // Empty strings are dropped so the runtime matcher never filters a\n // real spawn on an unknown agent type (fail-open, per the dispatcher).\n const ev: SubagentStartEvent = {\n ...base,\n ...(isNonEmptyString(input.agentId) ? { agentId: input.agentId } : {}),\n ...(isNonEmptyString(input.agentType)\n ? { agentType: input.agentType }\n : {}),\n };\n return ev;\n }\n case \"SubagentStop\": {\n const ev: SubagentStopEvent = {\n ...base,\n ...(isNonEmptyString(input.agentId) ? { agentId: input.agentId } : {}),\n ...(isNonEmptyString(input.agentType)\n ? { agentType: input.agentType }\n : {}),\n ...(isNonEmptyString(input.lastAssistantMessage)\n ? { lastAssistantMessage: input.lastAssistantMessage }\n : {}),\n };\n return ev;\n }\n default:\n // Other canonical events are not surfaced by OpenClaw; treat as a\n // session-start-shaped no-op so the dispatcher fails open gracefully.\n return { ...base, source: \"startup\" } satisfies SessionStartEvent;\n }\n }\n\n // ── Runtime: normalized response → reply the generated bridge parses ───\n\n /**\n * Unlike json-stdio hosts (whose reply is the host's NATIVE control payload),\n * OUR generated bridge consumes this stdout directly. So the reply body is the\n * NORMALIZED HookResponse itself — the bridge JSON.parses it and reads\n * decision / updatedInput / additionalContext.\n */\n formatReply(_event: HookEventName, response: HookResponse): HookReply {\n return {\n exitCode: 0,\n stdout: JSON.stringify(response ?? { decision: \"allow\" }),\n };\n }\n\n // ── Diagnostics ────────────────────────────────────────────────────────\n\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\n const configPath = this.getServerConfigPath(ctx);\n const pluginPath = this.getHookConfigPath(ctx);\n const pluginDir = this.pluginDir(ctx);\n const id = ctx.connector.id;\n const hasHooks = ctx.connector.hookEvents.length > 0;\n const hasServer = Boolean(ctx.connector.server);\n\n return [\n {\n name: `${this.name}: openclaw.json present`,\n check: () =>\n existsSync(configPath)\n ? { status: \"OK\", detail: configPath }\n : { status: \"FAIL\", detail: `not found: ${configPath}` },\n },\n {\n // HARDEST PART — dual registration must be CONSISTENT. The plugin only\n // delivers tools when it is LOADED (plugins.entries.<id> enabled AND its\n // dir in plugins.load.paths) AND in mcp.servers. A config with one half\n // but not the other is a tool-less (or never-loaded) install, so FAIL on\n // loaded XOR mcp.servers. (We assert plugins.load.paths — NOT a per-entry\n // `module` field, which openclaw config validate rejects.)\n name: `${this.name}: dual registration (plugins.entries+load.paths + mcp.servers.${id})`,\n check: () => {\n // Only assert what the connector declares: a catalog-only connector\n // (no MCP server, no hooks) never writes either half of the dual\n // registration, so absence — even of the config file — is healthy.\n if (!hasServer && !hasHooks) {\n return { status: \"OK\", detail: \"no MCP server or hooks declared\" };\n }\n const cfg = this.readJson<Record<string, unknown>>(configPath);\n if (!cfg) return { status: \"FAIL\", detail: `cannot read ${configPath}` };\n\n const inEntries = this.hasPluginEntry(cfg, id) && this.hasLoadPath(cfg, pluginDir);\n const inMcp = this.hasMcpServer(cfg, id);\n\n // A hooks-only connector (no MCP server declared) does not need the\n // mcp.servers half — the load half alone is correct and complete.\n if (!hasServer) {\n return inEntries || !hasHooks\n ? { status: \"OK\", detail: \"hooks-only connector (no MCP server)\" }\n : {\n status: \"FAIL\",\n detail: `${id} not enabled in ${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY} + ${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY} (plugin will not load)`,\n };\n }\n\n if (inEntries && inMcp) {\n return {\n status: \"OK\",\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id}+${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY} + ${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id}`,\n };\n }\n if (inEntries && !inMcp) {\n return {\n status: \"FAIL\",\n detail: `loaded (${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}+${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}) but missing from ${MCP_ROOT_KEY}.${MCP_SERVERS_KEY} — plugin loads but no tools reach the agent`,\n };\n }\n if (!inEntries && inMcp) {\n return {\n status: \"FAIL\",\n detail: `in ${MCP_ROOT_KEY}.${MCP_SERVERS_KEY} but not loaded (missing ${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY} or ${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}) — tools declared but plugin never loads`,\n };\n }\n return {\n status: \"FAIL\",\n detail: `not loaded (${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}+${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}) and not in ${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}`,\n };\n },\n },\n {\n name: `${this.name}: plugin module present`,\n check: () => {\n if (!hasHooks) return { status: \"OK\", detail: \"no hooks declared\" };\n return existsSync(pluginPath)\n ? { status: \"OK\", detail: pluginPath }\n : { status: \"FAIL\", detail: `not found: ${pluginPath}` };\n },\n },\n ];\n }\n\n /** True when config.plugins.entries.<id> exists (and is not disabled). */\n private hasPluginEntry(cfg: Record<string, unknown>, id: string): boolean {\n const plugins = cfg[PLUGINS_KEY];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n const entries = (plugins as Record<string, unknown>)[PLUGINS_ENTRIES_KEY];\n if (!entries || typeof entries !== \"object\" || Array.isArray(entries)) return false;\n const entry = (entries as Record<string, unknown>)[id];\n if (entry === undefined) return false;\n // An explicit { enabled: false } means the gateway will not load it.\n return !(\n entry &&\n typeof entry === \"object\" &&\n (entry as Record<string, unknown>).enabled === false\n );\n }\n\n /** True when config.plugins.load.paths includes `pluginDir`. */\n private hasLoadPath(cfg: Record<string, unknown>, pluginDir: string): boolean {\n const plugins = cfg[PLUGINS_KEY];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n const load = (plugins as Record<string, unknown>)[PLUGINS_LOAD_KEY];\n if (!load || typeof load !== \"object\" || Array.isArray(load)) return false;\n const paths = (load as Record<string, unknown>)[PLUGINS_LOAD_PATHS_KEY];\n return Array.isArray(paths) && paths.includes(pluginDir);\n }\n\n /** True when config.mcp.servers.<id> exists. */\n private hasMcpServer(cfg: Record<string, unknown>, id: string): boolean {\n const mcp = cfg[MCP_ROOT_KEY];\n if (!mcp || typeof mcp !== \"object\" || Array.isArray(mcp)) return false;\n const servers = (mcp as Record<string, unknown>)[MCP_SERVERS_KEY];\n if (!servers || typeof servers !== \"object\" || Array.isArray(servers)) return false;\n return id in (servers as Record<string, unknown>);\n }\n\n /** Read a file, returning undefined on any error (idempotency compare). */\n private safeRead(path: string): string | undefined {\n try {\n return readFileSync(path, \"utf8\");\n } catch {\n return undefined;\n }\n }\n}\n\n/**\n * Resolve the openclaw.json the gateway actually loads (mirrors context-mode's\n * openclawConfigPath): $OPENCLAW_CONFIG_PATH, else $OPENCLAW_STATE_DIR/openclaw.json,\n * else ~/.openclaw/openclaw.json. The gateway never loads a CWD/project-local\n * openclaw.json for user scope, so this is the canonical user-scope path.\n */\nfunction resolveOpenClawConfigPath(env: NodeJS.ProcessEnv = process.env): string {\n if (env.OPENCLAW_CONFIG_PATH) return resolve(env.OPENCLAW_CONFIG_PATH);\n const stateDir = env.OPENCLAW_STATE_DIR\n ? resolve(env.OPENCLAW_STATE_DIR)\n : resolve(homedir(), \".openclaw\");\n return resolve(stateDir, \"openclaw.json\");\n}\n\n/** Create a directory (recursive) if it does not already exist. */\nfunction ensureDir(dir: string): void {\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n}\n\n/** Narrow to a non-empty string (the bridge omits empties, but stay defensive). */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value !== \"\";\n}\n\nexport const adapter = new OpenClawAdapter();\nexport default adapter;\n"],"mappings":"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","names":["entry"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
defineConnector
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ADTHT2FN.js";
|
|
5
5
|
import {
|
|
6
6
|
connectorDir,
|
|
7
7
|
connectorsDir,
|
|
@@ -73,7 +73,7 @@ function serializablePlatforms(platforms) {
|
|
|
73
73
|
}
|
|
74
74
|
return out;
|
|
75
75
|
}
|
|
76
|
-
function registerConnector(connector, modulePath) {
|
|
76
|
+
function registerConnector(connector, modulePath, scope) {
|
|
77
77
|
const meta = {
|
|
78
78
|
id: connector.id,
|
|
79
79
|
displayName: connector.displayName,
|
|
@@ -88,7 +88,11 @@ function registerConnector(connector, modulePath) {
|
|
|
88
88
|
subagents: connector.subagents,
|
|
89
89
|
memory: connector.memory,
|
|
90
90
|
targets: connector.targets,
|
|
91
|
-
platforms: serializablePlatforms(connector.platforms)
|
|
91
|
+
platforms: serializablePlatforms(connector.platforms),
|
|
92
|
+
// Persist the install scope when the caller supplied one (the installer
|
|
93
|
+
// passes its install scope). Omitted from the record when undefined so an
|
|
94
|
+
// ad-hoc register stays minimal and round-trips as "no scope known".
|
|
95
|
+
...scope !== void 0 ? { scope } : {}
|
|
92
96
|
};
|
|
93
97
|
const dir = connectorDir(connector.id);
|
|
94
98
|
ensureDir(dir);
|
|
@@ -140,6 +144,11 @@ function connectorFromMeta(meta) {
|
|
|
140
144
|
skills: meta.skills ?? [],
|
|
141
145
|
subagents: meta.subagents ?? [],
|
|
142
146
|
memory: meta.memory ?? [],
|
|
147
|
+
// Actions carry live `run` handlers, so they are NOT serialized into the
|
|
148
|
+
// record (like the statusline render). A handler-less meta-derived connector
|
|
149
|
+
// gets [] — inspection/uninstall never dispatch action handlers, and the
|
|
150
|
+
// BaseAdapter action default already tolerates an empty list.
|
|
151
|
+
actions: [],
|
|
143
152
|
platforms: meta.platforms ?? {},
|
|
144
153
|
targets: meta.targets
|
|
145
154
|
};
|
|
@@ -165,4 +174,4 @@ export {
|
|
|
165
174
|
connectorFromMeta,
|
|
166
175
|
listRegisteredConnectors
|
|
167
176
|
};
|
|
168
|
-
//# sourceMappingURL=chunk-
|
|
177
|
+
//# sourceMappingURL=chunk-KGJWE6A2.js.map
|