@ken-jo/agent-connector 0.4.7 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{action-YCU53FNE.js → action-RWPRIZIC.js} +10 -10
- package/dist/{amazon-q-W574ZX35.js → amazon-q-5LJOB5L3.js} +6 -18
- package/dist/amazon-q-5LJOB5L3.js.map +1 -0
- package/dist/{amp-JPBQJJVM.js → amp-PU4LFSY6.js} +16 -48
- package/dist/amp-PU4LFSY6.js.map +1 -0
- package/dist/{antigravity-VK2Y3FGV.js → antigravity-DWW35HZO.js} +6 -5
- package/dist/{antigravity-cli-D4PCCUG2.js → antigravity-cli-VTQ6DNWL.js} +6 -5
- package/dist/{antigravity-cli-D4PCCUG2.js.map → antigravity-cli-VTQ6DNWL.js.map} +1 -1
- package/dist/{chunk-F5RX62T7.js → chunk-7QRUZAUG.js} +2 -2
- package/dist/{chunk-G5CQHVQG.js → chunk-7TVBFRVI.js} +12 -15
- package/dist/chunk-7TVBFRVI.js.map +1 -0
- package/dist/{chunk-CYOAH23F.js → chunk-A2T4Q7RM.js} +18 -18
- package/dist/{chunk-7ECYDK5T.js → chunk-CDVOBKSG.js} +4 -4
- package/dist/{chunk-DT6Y7XBI.js → chunk-CENEXP7K.js} +9 -38
- package/dist/chunk-CENEXP7K.js.map +1 -0
- package/dist/chunk-FBJQGRET.js +38 -0
- package/dist/chunk-FBJQGRET.js.map +1 -0
- package/dist/{chunk-WZA4CSGJ.js → chunk-H53QZQJI.js} +15 -2
- package/dist/chunk-H53QZQJI.js.map +1 -0
- package/dist/{chunk-MDNX7HTN.js → chunk-HOE2MN36.js} +2 -2
- package/dist/{chunk-7L6ITC4T.js → chunk-IEGR4TP3.js} +22 -6
- package/dist/chunk-IEGR4TP3.js.map +1 -0
- package/dist/{chunk-PFDUGDOA.js → chunk-KG6T3YNJ.js} +3 -3
- package/dist/{chunk-YQLQJZ7S.js → chunk-MOW2RKHX.js} +2 -2
- package/dist/{chunk-SDPIT4HW.js → chunk-NYR55UT7.js} +2 -2
- package/dist/{chunk-WFXWJITA.js → chunk-OZ5EXXQV.js} +2 -2
- package/dist/{chunk-I5MLE6S7.js → chunk-TO57WYBB.js} +15 -1
- package/dist/chunk-TO57WYBB.js.map +1 -0
- package/dist/chunk-UM5S7DKK.js +33 -0
- package/dist/chunk-UM5S7DKK.js.map +1 -0
- package/dist/{chunk-6RYPUR6O.js → chunk-VRKTVCYJ.js} +7 -7
- package/dist/{chunk-HAIMYUEY.js → chunk-WI6YSXOC.js} +3 -3
- package/dist/{chunk-2F5QH3D4.js → chunk-XQUUNAGC.js} +36 -36
- package/dist/{chunk-TZQBASI7.js → chunk-YS2NYJ6R.js} +13 -49
- package/dist/chunk-YS2NYJ6R.js.map +1 -0
- package/dist/{chunk-SPWFUHHK.js → chunk-YYISBXOM.js} +7 -7
- package/dist/{claude-code-ROSIILTM.js → claude-code-64X6RR2S.js} +15 -38
- package/dist/claude-code-64X6RR2S.js.map +1 -0
- package/dist/cli/sdk.js +4 -4
- package/dist/cli.js +1 -1
- package/dist/{cline-JUTO4V27.js → cline-T7ZDJYZQ.js} +15 -47
- package/dist/cline-T7ZDJYZQ.js.map +1 -0
- package/dist/{codebuff-2QUHL6HO.js → codebuff-PPPY3SJV.js} +9 -40
- package/dist/codebuff-PPPY3SJV.js.map +1 -0
- package/dist/{codex-YBBTIDD4.js → codex-EEVN43C2.js} +26 -62
- package/dist/codex-EEVN43C2.js.map +1 -0
- package/dist/{continue-W4TU4JSB.js → continue-VLLZO4PM.js} +9 -32
- package/dist/continue-VLLZO4PM.js.map +1 -0
- package/dist/{copilot-cli-Y74SOP5G.js → copilot-cli-A7G5WB4N.js} +15 -64
- package/dist/copilot-cli-A7G5WB4N.js.map +1 -0
- package/dist/{crush-GMEWLXK5.js → crush-AH24GDDB.js} +15 -42
- package/dist/crush-AH24GDDB.js.map +1 -0
- package/dist/{cursor-JBS4B3Q4.js → cursor-GJBSMXI6.js} +13 -51
- package/dist/cursor-GJBSMXI6.js.map +1 -0
- package/dist/{detect-2NIXILMZ.js → detect-YDCZNV4W.js} +4 -4
- package/dist/{doctor-QWJBWALA.js → doctor-DGC7P3P7.js} +13 -12
- package/dist/{doctor-QWJBWALA.js.map → doctor-DGC7P3P7.js.map} +1 -1
- package/dist/{droid-TJMTNCQE.js → droid-RBWCFKL3.js} +17 -40
- package/dist/droid-RBWCFKL3.js.map +1 -0
- package/dist/{gemini-cli-RAONGZRG.js → gemini-cli-CFPFEJEA.js} +15 -63
- package/dist/gemini-cli-CFPFEJEA.js.map +1 -0
- package/dist/{goose-G4KKM6YU.js → goose-UBJVQFUE.js} +18 -54
- package/dist/goose-UBJVQFUE.js.map +1 -0
- package/dist/{hermes-PJDTJE6F.js → hermes-6FXGXLNL.js} +14 -52
- package/dist/hermes-6FXGXLNL.js.map +1 -0
- package/dist/{hook-FDGIQPRC.js → hook-CDGSCASQ.js} +10 -10
- package/dist/index.js +2 -2
- package/dist/{install-TLG3QVP3.js → install-2D3UKEME.js} +13 -12
- package/dist/{install-TLG3QVP3.js.map → install-2D3UKEME.js.map} +1 -1
- package/dist/{jetbrains-copilot-DNXSLQE5.js → jetbrains-copilot-K26KMA3U.js} +10 -37
- package/dist/jetbrains-copilot-K26KMA3U.js.map +1 -0
- package/dist/{kilo-FERJ3X46.js → kilo-BKZJ26RM.js} +26 -90
- package/dist/kilo-BKZJ26RM.js.map +1 -0
- package/dist/{kilo-cli-TDCONH5G.js → kilo-cli-6UVWCXQD.js} +17 -48
- package/dist/kilo-cli-6UVWCXQD.js.map +1 -0
- package/dist/{kimi-RIRD4J5J.js → kimi-QALYPSRR.js} +10 -31
- package/dist/kimi-QALYPSRR.js.map +1 -0
- package/dist/{kiro-X7JVLAN7.js → kiro-J5J42I5Z.js} +10 -31
- package/dist/kiro-J5J42I5Z.js.map +1 -0
- package/dist/{leaderboard-MXGENYXN.js → leaderboard-VZQXNWTX.js} +2 -2
- package/dist/{mimo-code-RFLQ727H.js → mimo-code-BEE622P2.js} +22 -87
- package/dist/mimo-code-BEE622P2.js.map +1 -0
- package/dist/{mux-D77HBSBU.js → mux-5HTNKK4K.js} +9 -39
- package/dist/mux-5HTNKK4K.js.map +1 -0
- package/dist/{nemoclaw-LXUTMR56.js → nemoclaw-DNFIZFDF.js} +5 -5
- package/dist/{omp-CO7IXURG.js → omp-I3UTRBNP.js} +16 -48
- package/dist/omp-I3UTRBNP.js.map +1 -0
- package/dist/{openclaw-XWJLEW35.js → openclaw-QE2LMWIF.js} +5 -5
- package/dist/{opencode-QKZ5EIYU.js → opencode-ZFSUYJXI.js} +22 -88
- package/dist/opencode-ZFSUYJXI.js.map +1 -0
- package/dist/{package-IA56LSC3.js → package-BSCCMW4E.js} +8 -8
- package/dist/{pi-YGBXYWY5.js → pi-H2KBBDDJ.js} +4 -4
- package/dist/{qwen-code-E2MSEZK6.js → qwen-code-5LE34FNE.js} +10 -31
- package/dist/qwen-code-5LE34FNE.js.map +1 -0
- package/dist/{roo-code-AGGJ473T.js → roo-code-XH4GCYIS.js} +15 -52
- package/dist/roo-code-XH4GCYIS.js.map +1 -0
- package/dist/runtime/index.js +8 -8
- package/dist/sdk/index.js +3 -3
- package/dist/sdk/test.js +5 -5
- package/dist/{serve-RNUCK5F7.js → serve-MQNGPY2H.js} +10 -10
- package/dist/{status-JOX6RLJU.js → status-MRHVRDFY.js} +8 -7
- package/dist/{status-JOX6RLJU.js.map → status-MRHVRDFY.js.map} +1 -1
- package/dist/{statusline-ZVFI5DRT.js → statusline-IYWA75TR.js} +9 -9
- package/dist/{telemetry-3FN6HDVU.js → telemetry-IONIOMLC.js} +5 -5
- package/dist/{trae-6L226ZDU.js → trae-RVNRSV7X.js} +10 -39
- package/dist/trae-RVNRSV7X.js.map +1 -0
- package/dist/{uninstall-SCKZYB4J.js → uninstall-ESLSVCLE.js} +13 -12
- package/dist/{uninstall-SCKZYB4J.js.map → uninstall-ESLSVCLE.js.map} +1 -1
- package/dist/{upgrade-WU3KZWR4.js → upgrade-VICY5T64.js} +13 -12
- package/dist/{upgrade-WU3KZWR4.js.map → upgrade-VICY5T64.js.map} +1 -1
- package/dist/{usage-G72SF7JG.js → usage-TCUWV2YZ.js} +2 -2
- package/dist/{usage-event-DHSEBO5T.js → usage-event-CBZZP37Q.js} +9 -9
- package/dist/{vscode-copilot-6EOZDLXC.js → vscode-copilot-B2QBBHJW.js} +15 -65
- package/dist/vscode-copilot-B2QBBHJW.js.map +1 -0
- package/dist/{warp-N5YYN5BF.js → warp-5R54ST3N.js} +7 -19
- package/dist/warp-5R54ST3N.js.map +1 -0
- package/dist/{windsurf-4TGSJIVV.js → windsurf-L32UEOLY.js} +6 -18
- package/dist/windsurf-L32UEOLY.js.map +1 -0
- package/dist/{zed-6P2UFWXU.js → zed-FDJHJZKD.js} +11 -21
- package/dist/zed-FDJHJZKD.js.map +1 -0
- package/package.json +1 -1
- package/dist/amazon-q-W574ZX35.js.map +0 -1
- package/dist/amp-JPBQJJVM.js.map +0 -1
- package/dist/chunk-7L6ITC4T.js.map +0 -1
- package/dist/chunk-DT6Y7XBI.js.map +0 -1
- package/dist/chunk-G5CQHVQG.js.map +0 -1
- package/dist/chunk-I5MLE6S7.js.map +0 -1
- package/dist/chunk-TZQBASI7.js.map +0 -1
- package/dist/chunk-WZA4CSGJ.js.map +0 -1
- package/dist/claude-code-ROSIILTM.js.map +0 -1
- package/dist/cline-JUTO4V27.js.map +0 -1
- package/dist/codebuff-2QUHL6HO.js.map +0 -1
- package/dist/codex-YBBTIDD4.js.map +0 -1
- package/dist/continue-W4TU4JSB.js.map +0 -1
- package/dist/copilot-cli-Y74SOP5G.js.map +0 -1
- package/dist/crush-GMEWLXK5.js.map +0 -1
- package/dist/cursor-JBS4B3Q4.js.map +0 -1
- package/dist/droid-TJMTNCQE.js.map +0 -1
- package/dist/gemini-cli-RAONGZRG.js.map +0 -1
- package/dist/goose-G4KKM6YU.js.map +0 -1
- package/dist/hermes-PJDTJE6F.js.map +0 -1
- package/dist/jetbrains-copilot-DNXSLQE5.js.map +0 -1
- package/dist/kilo-FERJ3X46.js.map +0 -1
- package/dist/kilo-cli-TDCONH5G.js.map +0 -1
- package/dist/kimi-RIRD4J5J.js.map +0 -1
- package/dist/kiro-X7JVLAN7.js.map +0 -1
- package/dist/mimo-code-RFLQ727H.js.map +0 -1
- package/dist/mux-D77HBSBU.js.map +0 -1
- package/dist/omp-CO7IXURG.js.map +0 -1
- package/dist/opencode-QKZ5EIYU.js.map +0 -1
- package/dist/qwen-code-E2MSEZK6.js.map +0 -1
- package/dist/roo-code-AGGJ473T.js.map +0 -1
- package/dist/trae-6L226ZDU.js.map +0 -1
- package/dist/vscode-copilot-6EOZDLXC.js.map +0 -1
- package/dist/warp-N5YYN5BF.js.map +0 -1
- package/dist/windsurf-4TGSJIVV.js.map +0 -1
- package/dist/zed-6P2UFWXU.js.map +0 -1
- /package/dist/{action-YCU53FNE.js.map → action-RWPRIZIC.js.map} +0 -0
- /package/dist/{antigravity-VK2Y3FGV.js.map → antigravity-DWW35HZO.js.map} +0 -0
- /package/dist/{chunk-F5RX62T7.js.map → chunk-7QRUZAUG.js.map} +0 -0
- /package/dist/{chunk-CYOAH23F.js.map → chunk-A2T4Q7RM.js.map} +0 -0
- /package/dist/{chunk-7ECYDK5T.js.map → chunk-CDVOBKSG.js.map} +0 -0
- /package/dist/{chunk-MDNX7HTN.js.map → chunk-HOE2MN36.js.map} +0 -0
- /package/dist/{chunk-PFDUGDOA.js.map → chunk-KG6T3YNJ.js.map} +0 -0
- /package/dist/{chunk-YQLQJZ7S.js.map → chunk-MOW2RKHX.js.map} +0 -0
- /package/dist/{chunk-SDPIT4HW.js.map → chunk-NYR55UT7.js.map} +0 -0
- /package/dist/{chunk-WFXWJITA.js.map → chunk-OZ5EXXQV.js.map} +0 -0
- /package/dist/{chunk-6RYPUR6O.js.map → chunk-VRKTVCYJ.js.map} +0 -0
- /package/dist/{chunk-HAIMYUEY.js.map → chunk-WI6YSXOC.js.map} +0 -0
- /package/dist/{chunk-2F5QH3D4.js.map → chunk-XQUUNAGC.js.map} +0 -0
- /package/dist/{chunk-SPWFUHHK.js.map → chunk-YYISBXOM.js.map} +0 -0
- /package/dist/{detect-2NIXILMZ.js.map → detect-YDCZNV4W.js.map} +0 -0
- /package/dist/{hook-FDGIQPRC.js.map → hook-CDGSCASQ.js.map} +0 -0
- /package/dist/{leaderboard-MXGENYXN.js.map → leaderboard-VZQXNWTX.js.map} +0 -0
- /package/dist/{nemoclaw-LXUTMR56.js.map → nemoclaw-DNFIZFDF.js.map} +0 -0
- /package/dist/{openclaw-XWJLEW35.js.map → openclaw-QE2LMWIF.js.map} +0 -0
- /package/dist/{package-IA56LSC3.js.map → package-BSCCMW4E.js.map} +0 -0
- /package/dist/{pi-YGBXYWY5.js.map → pi-H2KBBDDJ.js.map} +0 -0
- /package/dist/{serve-RNUCK5F7.js.map → serve-MQNGPY2H.js.map} +0 -0
- /package/dist/{statusline-ZVFI5DRT.js.map → statusline-IYWA75TR.js.map} +0 -0
- /package/dist/{telemetry-3FN6HDVU.js.map → telemetry-IONIOMLC.js.map} +0 -0
- /package/dist/{usage-G72SF7JG.js.map → usage-TCUWV2YZ.js.map} +0 -0
- /package/dist/{usage-event-DHSEBO5T.js.map → usage-event-CBZZP37Q.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/marketplace-state.ts"],"sourcesContent":["/**\n * core/marketplace-state — pure read/write helpers for MARKETPLACE-method\n * install state. A LEAF module (fs + paths only, no adapters, no spawning) so\n * the installer, the marketplace orchestration, doctor, and status can all\n * import it without cycles.\n *\n * Two kinds of state live here:\n *\n * 1. The per-connector state record `connectorDir(id)/marketplace-installs.json`\n * — one entry per platform a connector was marketplace-installed on\n * ({ format, bundleDir, marketplace, scope, version, contentHash,\n * installedAt }). It drives `uninstall --method auto`, upgrade staleness\n * warnings, doctor, and the double-install guard.\n *\n * 2. Read-only fs EVIDENCE probes of the hosts' own plugin state (e.g.\n * Claude Code's `$CLAUDE_CONFIG_DIR/plugins/installed_plugins.json`).\n * These catch installs the user performed manually from `package`'s\n * printed instructions, which the state record alone would miss. Probes\n * read undocumented host internals, so every reader degrades to null/false\n * on absence or parse failure — the guard convention is \"refuse only on\n * positive evidence\" (docs: design risk 4).\n */\n\nimport { createHash } from \"node:crypto\";\nimport {\n existsSync,\n readdirSync,\n readFileSync,\n realpathSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport TOML from \"@iarna/toml\";\n\nimport type { InstallScope, PlatformId } from \"./types.js\";\nimport type { PackageFormat } from \"./package.js\";\nimport { connectorDir, connectorsDir, dataRoot, ensureDir } from \"./paths.js\";\nimport { codexConfigHome, xdgConfigHome } from \"./host-paths.js\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// State record: connectorDir(id)/marketplace-installs.json\n// ─────────────────────────────────────────────────────────────────────────\n\n/** The catalog name every staged local marketplace is registered under. */\nexport const MARKETPLACE_NAME = \"agent-connector\";\n\n/** One marketplace install of one connector on one platform. */\nexport interface MarketplaceInstallRecord {\n /** Bundle format that was staged + installed (e.g. \"claude-plugin\"). */\n format: PackageFormat;\n /** Absolute path of the staged plugin dir the host references/copied. */\n bundleDir: string;\n /** Marketplace/catalog name the install is keyed under ({@link MARKETPLACE_NAME}). */\n marketplace: string;\n /** Install scope (v1: always \"user\"). */\n scope: InstallScope;\n /** connector.version at install time (staleness warnings key off this). */\n version: string;\n /** sha256 over the staged plugin dir at install time (drift detection). */\n contentHash: string;\n /** ISO timestamp of the recorded install. */\n installedAt: string;\n}\n\n/** platform → record map persisted per connector. */\nexport type MarketplaceInstalls = Partial<\n Record<PlatformId, MarketplaceInstallRecord>\n>;\n\n/** Path of the per-connector marketplace state record. */\nexport function marketplaceInstallsPath(connectorId: string): string {\n return join(connectorDir(connectorId), \"marketplace-installs.json\");\n}\n\n/** Read the state record ({} on absence/parse failure — never throws). */\nexport function readMarketplaceInstalls(connectorId: string): MarketplaceInstalls {\n try {\n const raw = readFileSync(marketplaceInstallsPath(connectorId), \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as MarketplaceInstalls;\n }\n } catch {\n /* absent / unreadable → empty */\n }\n return {};\n}\n\n/** Write the state record; an empty map DELETES the file (uninstall leaves no husk). */\nexport function writeMarketplaceInstalls(\n connectorId: string,\n installs: MarketplaceInstalls,\n): void {\n const path = marketplaceInstallsPath(connectorId);\n if (Object.keys(installs).length === 0) {\n rmSync(path, { force: true });\n return;\n }\n ensureDir(dirname(path));\n writeFileSync(path, `${JSON.stringify(installs, null, 2)}\\n`, \"utf8\");\n}\n\n/** True when THIS connector has any recorded marketplace install. */\nexport function connectorHasMarketplaceRecords(connectorId: string): boolean {\n return Object.keys(readMarketplaceInstalls(connectorId)).length > 0;\n}\n\n/**\n * True when ANY registered connector (optionally excluding one) still records a\n * marketplace install. Guards home-bin removal on purge: an installed plugin's\n * hooks exec the home-bin OUTSIDE any adapter-managed config, so removing the\n * launcher while a marketplace install survives would silently kill its hooks.\n */\nexport function anyMarketplaceRecordsRemain(excludeConnectorId?: string): boolean {\n const dir = connectorsDir();\n if (!existsSync(dir)) return false;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return false;\n }\n return entries.some(\n (id) => id !== excludeConnectorId && connectorHasMarketplaceRecords(id),\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Staging roots — bundles staged by-reference must survive cwd changes and\n// `rm -rf dist-plugin`, so they live under the data-root, one shared root per\n// host family, holding every marketplace-installed connector's bundle plus ONE\n// regenerated catalog named \"agent-connector\" listing them all.\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Root for every marketplace staging tree: <dataRoot>/marketplace. */\nexport function marketplaceRoot(): string {\n return join(dataRoot(), \"marketplace\");\n}\n\n/** Shared Claude-family staging root: <dataRoot>/marketplace/claude. */\nexport function claudeStagingRoot(): string {\n return join(marketplaceRoot(), \"claude\");\n}\n\n/** Shared Codex-family staging root: <dataRoot>/marketplace/codex. */\nexport function codexStagingRoot(): string {\n return join(marketplaceRoot(), \"codex\");\n}\n\n/** Shared agy (Antigravity) staging root: <dataRoot>/marketplace/agy. */\nexport function agyStagingRoot(): string {\n return join(marketplaceRoot(), \"agy\");\n}\n\n/** Shared Gemini-CLI staging root: <dataRoot>/marketplace/gemini. */\nexport function geminiStagingRoot(): string {\n return join(marketplaceRoot(), \"gemini\");\n}\n\n/** Shared Qwen-Code staging root: <dataRoot>/marketplace/qwen. */\nexport function qwenStagingRoot(): string {\n return join(marketplaceRoot(), \"qwen\");\n}\n\n/** Shared droid (Factory) staging root: <dataRoot>/marketplace/droid. */\nexport function droidStagingRoot(): string {\n return join(marketplaceRoot(), \"droid\");\n}\n\n/**\n * Shared npm-local staging root: <dataRoot>/marketplace/npm. Every npm-plugin\n * host (opencode / kilo / kilo-cli) stages here; the host config's `plugin`\n * array references the bundle by `file://<this>/<id>`, so it MUST live under the\n * stable data-root (it survives cwd changes and `rm -rf dist-plugin`).\n */\nexport function npmStagingRoot(): string {\n return join(marketplaceRoot(), \"npm\");\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Claude Code host-state readers ($CLAUDE_CONFIG_DIR || ~/.claude)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Claude Code's config dir, honoring the CLAUDE_CONFIG_DIR override. */\nexport function claudeConfigDir(): string {\n const override = process.env.CLAUDE_CONFIG_DIR;\n if (override && override.trim() !== \"\") return resolve(override);\n return join(homedir(), \".claude\");\n}\n\n/** The installed_plugins.json key our marketplace install creates for `id`. */\nexport function claudePluginKey(connectorId: string): string {\n return `${connectorId}@${MARKETPLACE_NAME}`;\n}\n\nfunction readJsonFile(path: string): Record<string, unknown> | null {\n try {\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n /* absent / unreadable → null */\n }\n return null;\n}\n\n/** The `plugins` map from Claude's installed_plugins.json (null when unreadable). */\nexport function readClaudeInstalledPlugins(): Record<string, unknown> | null {\n const file = join(claudeConfigDir(), \"plugins\", \"installed_plugins.json\");\n const parsed = readJsonFile(file);\n const plugins = parsed?.plugins;\n if (plugins && typeof plugins === \"object\" && !Array.isArray(plugins)) {\n return plugins as Record<string, unknown>;\n }\n return null;\n}\n\n/** True when Claude's own state lists `<id>@agent-connector` as installed. */\nexport function claudePluginInstalled(connectorId: string): boolean {\n const plugins = readClaudeInstalledPlugins();\n if (!plugins) return false;\n const entry = plugins[claudePluginKey(connectorId)];\n return Array.isArray(entry) ? entry.length > 0 : entry != null;\n}\n\n/** True when ANY `*@agent-connector` plugin remains in Claude's state. */\nexport function anyClaudeAgentConnectorPlugins(): boolean {\n const plugins = readClaudeInstalledPlugins();\n if (!plugins) return false;\n return Object.entries(plugins).some(\n ([key, entry]) =>\n key.endsWith(`@${MARKETPLACE_NAME}`) &&\n (Array.isArray(entry) ? entry.length > 0 : entry != null),\n );\n}\n\n/**\n * The directory Claude's known_marketplaces.json records for `name`, or null\n * when not registered / unreadable. Used both as a presence probe and as the\n * NAME-COLLISION check: a registration pointing somewhere other than our\n * staging root belongs to the user and must never be touched.\n */\nexport function claudeKnownMarketplacePath(name: string): string | null {\n const file = join(claudeConfigDir(), \"plugins\", \"known_marketplaces.json\");\n const parsed = readJsonFile(file);\n const entry = parsed?.[name];\n if (!entry || typeof entry !== \"object\") return null;\n const e = entry as { installLocation?: unknown; source?: { path?: unknown } };\n if (typeof e.installLocation === \"string\") return e.installLocation;\n const sourcePath = e.source?.path;\n return typeof sourcePath === \"string\" ? sourcePath : null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Codex host-state readers ($CODEX_HOME || ~/.codex → config.toml)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** The config.toml key our marketplace install creates for `id` (== claude's). */\nexport function codexPluginKey(connectorId: string): string {\n return `${connectorId}@${MARKETPLACE_NAME}`;\n}\n\n/** Parse <CODEX_HOME>/config.toml ({} on absence/parse failure — never throws). */\nfunction readCodexConfig(): Record<string, unknown> {\n const file = join(codexConfigHome(), \"config.toml\");\n try {\n return TOML.parse(readFileSync(file, \"utf8\")) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\n/**\n * True when Codex's config.toml carries the `[plugins.\"<id>@agent-connector\"]`\n * table. The DEFINITIVE install probe — empty `plugins/cache/...` dirs linger\n * after uninstall, so the cache dir must NOT be used (docs: codex quirk).\n */\nexport function codexPluginInstalled(connectorId: string): boolean {\n const cfg = readCodexConfig();\n const plugins = cfg[\"plugins\"];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n return codexPluginKey(connectorId) in (plugins as Record<string, unknown>);\n}\n\n/** True when ANY `*@agent-connector` plugin remains in Codex's config.toml. */\nexport function anyCodexAgentConnectorPlugins(): boolean {\n const cfg = readCodexConfig();\n const plugins = cfg[\"plugins\"];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n return Object.keys(plugins as Record<string, unknown>).some((k) =>\n k.endsWith(`@${MARKETPLACE_NAME}`),\n );\n}\n\n/**\n * The directory config.toml records as `[marketplaces.agent-connector].source`,\n * or null when not registered / unreadable. Both a presence probe and the\n * NAME-COLLISION check: a registration pointing somewhere other than our\n * staging root belongs to the user and must never be touched.\n */\nexport function codexMarketplaceSource(name: string): string | null {\n const cfg = readCodexConfig();\n const marketplaces = cfg[\"marketplaces\"];\n if (!marketplaces || typeof marketplaces !== \"object\" || Array.isArray(marketplaces)) {\n return null;\n }\n const entry = (marketplaces as Record<string, unknown>)[name];\n if (!entry || typeof entry !== \"object\") return null;\n const source = (entry as { source?: unknown }).source;\n return typeof source === \"string\" ? source : null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// droid (Factory) host-state readers (~/.factory/settings.json, JSON, NO env)\n//\n// DOCS-ONLY: no droid binary is present on this box. The settings.json key\n// shapes below (`enabledPlugins[\"<id>@agent-connector\"] === true` +\n// `extraKnownMarketplaces[\"agent-connector\"].source`) follow the documented\n// factory plugin model — confirm the exact JSON shapes live when a droid binary\n// is available. Both readers read defensively (refuse only on positive evidence).\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Factory's settings file: ~/.factory/settings.json (NO dedicated env). */\nexport function factorySettingsPath(): string {\n return join(homedir(), \".factory\", \"settings.json\");\n}\n\n/** The settings.json key our marketplace install creates for `id` (== codex's). */\nexport function droidPluginKey(connectorId: string): string {\n return `${connectorId}@${MARKETPLACE_NAME}`;\n}\n\n/** The `enabledPlugins` map from droid's settings.json (null when unreadable). */\nfunction readDroidEnabledPlugins(): Record<string, unknown> | null {\n const parsed = readJsonFile(factorySettingsPath());\n const plugins = parsed?.enabledPlugins;\n if (plugins && typeof plugins === \"object\" && !Array.isArray(plugins)) {\n return plugins as Record<string, unknown>;\n }\n return null;\n}\n\n/**\n * True when droid's settings.json carries\n * `enabledPlugins[\"<id>@agent-connector\"] === true`. DOCS-only: the strict\n * `=== true` value shape is unverified live (a future droid may store an object).\n */\nexport function droidPluginInstalled(connectorId: string): boolean {\n const plugins = readDroidEnabledPlugins();\n if (!plugins) return false;\n return plugins[droidPluginKey(connectorId)] === true;\n}\n\n/** True when ANY `*@agent-connector` plugin remains enabled in droid's settings. */\nexport function anyDroidAgentConnectorPlugins(): boolean {\n const plugins = readDroidEnabledPlugins();\n if (!plugins) return false;\n return Object.entries(plugins).some(\n ([key, value]) => key.endsWith(`@${MARKETPLACE_NAME}`) && value === true,\n );\n}\n\n/**\n * The directory droid's settings.json records as\n * `extraKnownMarketplaces[\"<name>\"].source`, or null when not registered /\n * unreadable. Both a presence probe and the NAME-COLLISION check (a registration\n * pointing somewhere other than our staging root belongs to the user). DOCS-only:\n * the local-path `source` field shape is undocumented, so this reads defensively\n * — accepts either a bare string source or an object carrying a `path`/`source`\n * string, and NORMALIZES to a single string for the samePath compare.\n */\nexport function droidMarketplaceSource(name: string): string | null {\n const parsed = readJsonFile(factorySettingsPath());\n const known = parsed?.extraKnownMarketplaces;\n if (!known || typeof known !== \"object\" || Array.isArray(known)) return null;\n const entry = (known as Record<string, unknown>)[name];\n if (entry == null) return null;\n if (typeof entry === \"string\") return entry;\n if (typeof entry === \"object\" && !Array.isArray(entry)) {\n const e = entry as { source?: unknown; path?: unknown };\n if (typeof e.source === \"string\") return e.source;\n if (typeof e.path === \"string\") return e.path;\n }\n return null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// agy (Antigravity) host-state readers (~/.gemini/config/plugins, NO env)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** agy's config dir: ~/.gemini/config (NO dedicated env — HOME isolation). */\nexport function agyConfigDir(): string {\n return join(homedir(), \".gemini\", \"config\");\n}\n\n/** agy's plugins dir: ~/.gemini/config/plugins. */\nexport function agyPluginsDir(): string {\n return join(agyConfigDir(), \"plugins\");\n}\n\n/**\n * Candidate `import_manifest.json` locations. agy 1.0.7 records the manifest at\n * `<config>/plugins/import_manifest.json` on POSIX but at\n * `<config>/import_manifest.json` on Windows (live-confirmed on the my-window\n * box) — both are probed, then the copied plugin dir is the final fallback.\n */\nexport function agyImportManifestPaths(): string[] {\n return [\n join(agyConfigDir(), \"import_manifest.json\"), // win32 location\n join(agyPluginsDir(), \"import_manifest.json\"), // posix location\n ];\n}\n\n/** @deprecated single-location accessor — prefer {@link agyImportManifestPaths}. */\nexport function agyImportManifestPath(): string {\n return join(agyPluginsDir(), \"import_manifest.json\");\n}\n\n/**\n * True when `id` is installed per agy's import manifest (`imports[].name`),\n * checking BOTH the win32 and posix manifest locations, with a fallback probe of\n * the copied plugin dir's plugin.json. agy's uninstall removes the plugin dir\n * and clears the manifest entry (live-verified on Windows: install → dir +\n * manifest present, uninstall → both gone, re-install idempotent).\n */\nexport function agyPluginInstalled(connectorId: string): boolean {\n for (const manifest of agyImportManifestPaths()) {\n try {\n const parsed = JSON.parse(readFileSync(manifest, \"utf8\")) as {\n imports?: unknown;\n };\n const imports = parsed.imports;\n if (\n Array.isArray(imports) &&\n imports.some(\n (e) =>\n e != null &&\n typeof e === \"object\" &&\n (e as { name?: unknown }).name === connectorId,\n )\n ) {\n return true;\n }\n } catch {\n /* absent / unreadable manifest → try the next candidate */\n }\n }\n return existsSync(join(agyPluginsDir(), connectorId, \"plugin.json\"));\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Gemini CLI host-state readers (~/.gemini/extensions, NO env — HOME isolation)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Gemini CLI's config dir: ~/.gemini (NO dedicated config-dir env — isolation is\n * via HOME, live-confirmed on gemini 0.36.0). DISTINCT from agy's ~/.gemini/config\n * tree: gemini extensions land under ~/.gemini/extensions/<id>/.\n */\nexport function geminiConfigDir(): string {\n return join(homedir(), \".gemini\");\n}\n\n/**\n * True when `id` is installed per Gemini's own extension store: the marker file\n * ~/.gemini/extensions/<id>/gemini-extension.json exists. Gemini exits 0 even on\n * a logical failure, so this fs probe — not the exit code — is the source of\n * truth (the driver re-probes after every spawn).\n */\nexport function geminiExtensionInstalled(connectorId: string): boolean {\n return existsSync(\n join(geminiConfigDir(), \"extensions\", connectorId, \"gemini-extension.json\"),\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Qwen Code host-state readers (~/.qwen/extensions, NO env — HOME isolation)\n//\n// DOCS-ONLY: no qwen binary is present on this box. qwen-code is a gemini-cli\n// fork, so the extension store SHAPE (~/.qwen/extensions/<id>/qwen-extension.json)\n// mirrors gemini's — confirm the marker filename live when a qwen binary lands.\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Qwen Code's config dir: ~/.qwen (NO dedicated config-dir env — isolation is\n * via HOME, like its gemini-cli parent). DOCS-only: confirm live when a qwen\n * binary is available. DISTINCT from ~/.gemini (the gemini-cli store).\n */\nexport function qwenConfigDir(): string {\n return join(homedir(), \".qwen\");\n}\n\n/**\n * True when `id` is installed per Qwen's own extension store: the marker file\n * ~/.qwen/extensions/<id>/qwen-extension.json exists. Mirrors gemini's fs probe\n * (qwen is a gemini fork); the driver re-probes after every spawn. DOCS-only:\n * the marker filename (`qwen-extension.json`) is unverified live.\n */\nexport function qwenExtensionInstalled(connectorId: string): boolean {\n return existsSync(\n join(qwenConfigDir(), \"extensions\", connectorId, \"qwen-extension.json\"),\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// npm-local host-state readers (opencode / kilo / kilo-cli — XDG_CONFIG_HOME)\n//\n// These hosts have NO uninstall verb; installs are recorded as `file://<dir>`\n// entries in a `plugin` array inside an opencode-style config file, which the\n// driver EDITS to remove. This module is a spawn/shared-free LEAF, so a tiny\n// posix-resolve path comparison is inlined here rather than importing\n// shared.ts's samePath (and samePath does not strip the `file://` scheme).\n// ─────────────────────────────────────────────────────────────────────────\n\n/** opencode's config dir: $XDG_CONFIG_HOME/opencode (or ~/.config/opencode). */\nexport function opencodeConfigDir(): string {\n return join(xdgConfigHome(), \"opencode\");\n}\n\n/** kilo / kilo-cli's config dir: $XDG_CONFIG_HOME/kilo (or ~/.config/kilo). */\nexport function kiloConfigDir(): string {\n return join(xdgConfigHome(), \"kilo\");\n}\n\n/**\n * Candidate config filenames per npm-local host, most-preferred first. opencode\n * accepts opencode.jsonc / opencode.json / config.json (JSONC — comments\n * tolerated); kilo / kilo-cli use opencode.json (plain JSON).\n */\nfunction npmConfigCandidates(platform: PlatformId): string[] {\n return platform === \"opencode\"\n ? [\"opencode.jsonc\", \"opencode.json\", \"config.json\"]\n : [\"opencode.json\"];\n}\n\n/** The config DIR for an npm-local host (opencode vs kilo/kilo-cli). */\nfunction npmConfigDir(platform: PlatformId): string {\n return platform === \"opencode\" ? opencodeConfigDir() : kiloConfigDir();\n}\n\n/**\n * The config FILE path for `platform`: the first existing candidate, else the\n * most-preferred candidate (the path a fresh install would write). Never throws.\n */\nexport function npmConfigFilePath(platform: PlatformId): string {\n const dir = npmConfigDir(platform);\n const candidates = npmConfigCandidates(platform);\n for (const name of candidates) {\n if (existsSync(join(dir, name))) return join(dir, name);\n }\n return join(dir, candidates[0]!);\n}\n\n/** Strip a single leading `file://` (and `file://localhost/`) scheme prefix. */\nexport function stripFileScheme(entry: string): string {\n let s = entry;\n if (s.startsWith(\"file://localhost/\")) s = s.slice(\"file://localhost\".length);\n else if (s.startsWith(\"file://\")) s = s.slice(\"file://\".length);\n return s;\n}\n\n/**\n * Strip `//` line and `/* */` block comments from JSONC, STRING-AWARE: a `//`\n * or `/*` inside a JSON string literal (e.g. the `file://` in a plugin entry) is\n * left intact. Scans char-by-char tracking string + escape state — a naive\n * regex would corrupt `\"file:///dir\"` by eating from the embedded `//`.\n */\nfunction stripJsoncComments(text: string): string {\n let out = \"\";\n let inString = false;\n let inLine = false;\n let inBlock = false;\n for (let i = 0; i < text.length; i++) {\n const c = text[i]!;\n const next = i + 1 < text.length ? text[i + 1]! : \"\";\n if (inLine) {\n if (c === \"\\n\") {\n inLine = false;\n out += c;\n }\n continue;\n }\n if (inBlock) {\n if (c === \"*\" && next === \"/\") {\n inBlock = false;\n i++;\n }\n continue;\n }\n if (inString) {\n out += c;\n if (c === \"\\\\\") {\n // Copy the escaped char verbatim (covers \\\" and \\\\).\n if (next !== \"\") {\n out += next;\n i++;\n }\n } else if (c === '\"') {\n inString = false;\n }\n continue;\n }\n if (c === '\"') {\n inString = true;\n out += c;\n continue;\n }\n if (c === \"/\" && next === \"/\") {\n inLine = true;\n i++;\n continue;\n }\n if (c === \"/\" && next === \"*\") {\n inBlock = true;\n i++;\n continue;\n }\n out += c;\n }\n return out;\n}\n\n/** Tolerant JSONC → object: strip // line and block comments, then JSON.parse. */\nfunction parseJsonc(text: string): Record<string, unknown> | null {\n // A parse failure degrades to null (the \"refuse only on positive evidence\"\n // convention) rather than throwing.\n try {\n const parsed = JSON.parse(stripJsoncComments(text)) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n /* unreadable / not JSONC → null */\n }\n return null;\n}\n\n/** Read + parse an npm-local host's config file ({}-shaped or null). Never throws. */\nexport function readNpmConfig(platform: PlatformId): Record<string, unknown> | null {\n const file = npmConfigFilePath(platform);\n try {\n return parseJsonc(readFileSync(file, \"utf8\"));\n } catch {\n return null;\n }\n}\n\n/**\n * Convert an npm `plugin`-array entry to a native absolute path. opencode/kilo\n * record a proper `file://` URL — on Windows that is `file:///C:/Users/…` (drive\n * letter + forward slashes), which a textual `file://`-strip leaves as the broken\n * `/C:/Users/…`. `fileURLToPath` does the correct cross-platform decode\n * (`file:///C:/x`→`C:\\x`, `file:///tmp/x`→`/tmp/x`). Bare-path entries (also\n * accepted by the hosts) and any malformed URL fall back to a textual strip.\n */\nfunction npmEntryToPath(entry: string): string {\n if (/^file:\\/\\//i.test(entry)) {\n try {\n return fileURLToPath(entry);\n } catch {\n /* malformed file URL (e.g. backslashes) → manual strip below */\n }\n }\n return stripFileScheme(entry);\n}\n\n/**\n * Path equality for the npm probe. Resolve symlinks with `realpathSync.native`\n * when the path exists (catches macOS `/var/folders`→`/private/var/folders` and\n * win32 8.3 short paths — the host records the realpath, we hold the symlink),\n * else lexical `resolve()`; case-fold on win32. Inlined here (leaf module)\n * rather than importing shared.ts's samePath, which pulls in the spawn layer.\n */\nfunction npmPathEquals(a: string, b: string): boolean {\n const norm = (p: string): string => {\n let s: string;\n try {\n s = realpathSync.native(p);\n } catch {\n s = resolve(p);\n }\n return process.platform === \"win32\" ? s.toLowerCase() : s;\n };\n return norm(a) === norm(b);\n}\n\n/**\n * The `plugin`-array ENTRY (the raw string, including any `file://`) whose value\n * — after stripping a leading `file://` — path-equals `id`'s staged bundle dir,\n * or null when no entry references it. Used both as the install probe and to\n * locate the exact entry the uninstall edit must drop.\n */\nexport function npmPluginArrayEntry(\n platform: PlatformId,\n connectorId: string,\n): string | null {\n const cfg = readNpmConfig(platform);\n if (!cfg) return null;\n const arr = cfg[\"plugin\"];\n if (!Array.isArray(arr)) return null;\n const target = join(npmStagingRoot(), connectorId);\n for (const raw of arr) {\n if (typeof raw !== \"string\") continue;\n if (npmPathEquals(npmEntryToPath(raw), target)) return raw;\n }\n return null;\n}\n\n/** True when `platform`'s config `plugin` array references `id`'s staged bundle. */\nexport function npmPluginInstalled(platform: PlatformId, connectorId: string): boolean {\n return npmPluginArrayEntry(platform, connectorId) != null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Cross-host marketplace-install EVIDENCE (the double-install guard's probe)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Read-only evidence that `connectorId` is marketplace/plugin-installed on\n * `platform`: the state record first, then a cheap fs probe of the host's own\n * plugin state (catches manual installs from `package`'s printed instructions).\n * Returns a human-readable description of the evidence, or null when none.\n */\nexport function marketplaceEvidence(\n connectorId: string,\n platform: PlatformId,\n): string | null {\n if (readMarketplaceInstalls(connectorId)[platform]) {\n return \"recorded in marketplace-installs.json\";\n }\n switch (platform) {\n case \"claude-code\":\n if (claudePluginInstalled(connectorId)) {\n return `${claudePluginKey(connectorId)} listed in Claude's installed_plugins.json`;\n }\n return null;\n case \"gemini-cli\":\n return geminiExtensionInstalled(connectorId)\n ? `extension ${connectorId} listed in Gemini's extensions store`\n : null;\n case \"qwen-code\":\n return qwenExtensionInstalled(connectorId)\n ? `extension ${connectorId} listed in Qwen's extensions store`\n : null;\n case \"droid\":\n return droidPluginInstalled(connectorId)\n ? `${droidPluginKey(connectorId)} enabled in droid's settings.json`\n : null;\n case \"antigravity\":\n case \"antigravity-cli\": {\n const dir = join(homedir(), \".gemini\", \"config\", \"plugins\", connectorId);\n return existsSync(dir) ? `plugin dir ${dir} exists` : null;\n }\n case \"opencode\":\n case \"kilo\":\n case \"kilo-cli\":\n return npmPluginInstalled(platform, connectorId)\n ? `${connectorId} listed in ${platform}'s config plugin array`\n : null;\n case \"codex\": {\n const dir = join(\n codexConfigHome(),\n \"plugins\",\n \"cache\",\n MARKETPLACE_NAME,\n connectorId,\n );\n return existsSync(dir) ? `codex plugin cache ${dir} exists` : null;\n }\n default:\n return null;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Content hashing — staged-bundle drift detection\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Deterministic sha256 over every file under `dir` (sorted relative paths +\n * bytes). Empty string when the dir is missing/unreadable. Used to record the\n * staged bundle's content at install time and detect drift later (claude caches\n * a versioned COPY, so an edited connector goes silently stale without this).\n */\nexport function hashDirectory(dir: string): string {\n if (!existsSync(dir)) return \"\";\n const files: string[] = [];\n const walk = (d: string): void => {\n let entries: string[];\n try {\n entries = readdirSync(d);\n } catch {\n return;\n }\n for (const name of entries.sort()) {\n const full = join(d, name);\n let st;\n try {\n st = statSync(full);\n } catch {\n continue;\n }\n if (st.isDirectory()) walk(full);\n else if (st.isFile()) files.push(full);\n }\n };\n walk(dir);\n const hash = createHash(\"sha256\");\n for (const file of files.sort()) {\n hash.update(relative(dir, file).split(\"\\\\\").join(\"/\"));\n hash.update(\"\\0\");\n try {\n hash.update(readFileSync(file));\n } catch {\n /* unreadable file contributes only its path */\n }\n hash.update(\"\\0\");\n }\n return hash.digest(\"hex\");\n}\n"],"mappings":"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","names":[]}
|
|
@@ -73,36 +73,36 @@ function renderInstallResult(result, verb) {
|
|
|
73
73
|
return lines.join("\n");
|
|
74
74
|
}
|
|
75
75
|
var COMMANDS = {
|
|
76
|
-
detect: () => import("./detect-
|
|
77
|
-
install: () => import("./install-
|
|
78
|
-
uninstall: () => import("./uninstall-
|
|
76
|
+
detect: () => import("./detect-YDCZNV4W.js"),
|
|
77
|
+
install: () => import("./install-2D3UKEME.js"),
|
|
78
|
+
uninstall: () => import("./uninstall-ESLSVCLE.js"),
|
|
79
79
|
// `upgrade` is the single "bring everything current" verb (re-render host
|
|
80
80
|
// config + refresh the home pointer + managed channel guidance). `sync` and
|
|
81
81
|
// `update` are kept as back-compat aliases that route to the same module so
|
|
82
82
|
// existing scripts/docs keep working without a second concept to learn.
|
|
83
|
-
upgrade: () => import("./upgrade-
|
|
84
|
-
sync: () => import("./upgrade-
|
|
85
|
-
update: () => import("./upgrade-
|
|
86
|
-
package: () => import("./package-
|
|
87
|
-
doctor: () => import("./doctor-
|
|
88
|
-
status: () => import("./status-
|
|
89
|
-
telemetry: () => import("./telemetry-
|
|
90
|
-
usage: () => import("./usage-
|
|
91
|
-
leaderboard: () => import("./leaderboard-
|
|
92
|
-
hook: () => import("./hook-
|
|
83
|
+
upgrade: () => import("./upgrade-VICY5T64.js"),
|
|
84
|
+
sync: () => import("./upgrade-VICY5T64.js"),
|
|
85
|
+
update: () => import("./upgrade-VICY5T64.js"),
|
|
86
|
+
package: () => import("./package-BSCCMW4E.js"),
|
|
87
|
+
doctor: () => import("./doctor-DGC7P3P7.js"),
|
|
88
|
+
status: () => import("./status-MRHVRDFY.js"),
|
|
89
|
+
telemetry: () => import("./telemetry-IONIOMLC.js"),
|
|
90
|
+
usage: () => import("./usage-TCUWV2YZ.js"),
|
|
91
|
+
leaderboard: () => import("./leaderboard-VZQXNWTX.js"),
|
|
92
|
+
hook: () => import("./hook-CDGSCASQ.js"),
|
|
93
93
|
// Internal (omitted from USAGE): the statusline (HUD) entrypoint a host's
|
|
94
94
|
// status line config points at. Fail-safe: always exits 0 with at most a
|
|
95
95
|
// rendered line. See cli/commands/statusline.ts.
|
|
96
|
-
statusline: () => import("./statusline-
|
|
96
|
+
statusline: () => import("./statusline-IYWA75TR.js"),
|
|
97
97
|
// Internal (omitted from USAGE): the user-invokable action entrypoint a future
|
|
98
98
|
// host affordance points at. USER-TRIGGERED: surfaces errors (exit 1 + stderr),
|
|
99
99
|
// unlike the fail-open hook / fail-safe statusline. See cli/commands/action.ts.
|
|
100
|
-
action: () => import("./action-
|
|
101
|
-
serve: () => import("./serve-
|
|
100
|
+
action: () => import("./action-RWPRIZIC.js"),
|
|
101
|
+
serve: () => import("./serve-MQNGPY2H.js"),
|
|
102
102
|
// Hidden (omitted from USAGE): the opt-in host-native turn-usage entrypoint an
|
|
103
103
|
// AfterModel / PostInvocation hook points at. Always exits 0; records a
|
|
104
104
|
// distinct `model_turn` row. See cli/commands/usage-event.ts.
|
|
105
|
-
"usage-event": () => import("./usage-event-
|
|
105
|
+
"usage-event": () => import("./usage-event-CBZZP37Q.js")
|
|
106
106
|
};
|
|
107
107
|
var DEFAULT_PROGRAM_NAME = "agent-connector";
|
|
108
108
|
var COMMAND_USAGE = {
|
|
@@ -198,4 +198,4 @@ export {
|
|
|
198
198
|
renderInstallResult,
|
|
199
199
|
main
|
|
200
200
|
};
|
|
201
|
-
//# sourceMappingURL=chunk-
|
|
201
|
+
//# sourceMappingURL=chunk-A2T4Q7RM.js.map
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
isConcreteSemver,
|
|
8
8
|
isPlaceholderVersion,
|
|
9
9
|
registryTransportType
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-HOE2MN36.js";
|
|
11
11
|
import {
|
|
12
12
|
writeTomlString
|
|
13
13
|
} from "./chunk-OPXAP3AC.js";
|
|
@@ -15,12 +15,12 @@ import {
|
|
|
15
15
|
renderCommandMd,
|
|
16
16
|
renderSkillMd,
|
|
17
17
|
renderSubagentMd
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-IEGR4TP3.js";
|
|
19
19
|
import {
|
|
20
20
|
buildHomeBinHookCommand,
|
|
21
21
|
buildServeWrapperCommand,
|
|
22
22
|
shouldWrapForTelemetry
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-TO57WYBB.js";
|
|
24
24
|
import {
|
|
25
25
|
ensureDir,
|
|
26
26
|
homeBinPath
|
|
@@ -1041,4 +1041,4 @@ export {
|
|
|
1041
1041
|
packageConnectorAll,
|
|
1042
1042
|
installInstructions
|
|
1043
1043
|
};
|
|
1044
|
-
//# sourceMappingURL=chunk-
|
|
1044
|
+
//# sourceMappingURL=chunk-CDVOBKSG.js.map
|
|
@@ -5,11 +5,13 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
BaseAdapter,
|
|
7
7
|
parseJsonc
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-KG6T3YNJ.js";
|
|
9
9
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
renderSkillMd
|
|
11
|
+
} from "./chunk-IEGR4TP3.js";
|
|
12
|
+
import {
|
|
13
|
+
buildWrappedStdio
|
|
14
|
+
} from "./chunk-TO57WYBB.js";
|
|
13
15
|
|
|
14
16
|
// src/adapters/openclaw/index.ts
|
|
15
17
|
import {
|
|
@@ -251,18 +253,7 @@ var OpenClawAdapter = class extends BaseAdapter {
|
|
|
251
253
|
if (server.transport === "stdio") {
|
|
252
254
|
let command = server.command ?? "";
|
|
253
255
|
let args = [...server.args ?? []];
|
|
254
|
-
|
|
255
|
-
const wrapped = buildServeWrapperCommand(
|
|
256
|
-
ctx.homeBinPath,
|
|
257
|
-
ctx.connector.id,
|
|
258
|
-
command,
|
|
259
|
-
args,
|
|
260
|
-
ctx.scope,
|
|
261
|
-
this.id
|
|
262
|
-
);
|
|
263
|
-
command = wrapped.command;
|
|
264
|
-
args = wrapped.args;
|
|
265
|
-
}
|
|
256
|
+
({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));
|
|
266
257
|
const entry2 = {
|
|
267
258
|
command: resolveEnvRefsDeep(command)
|
|
268
259
|
};
|
|
@@ -637,7 +628,7 @@ var OpenClawAdapter = class extends BaseAdapter {
|
|
|
637
628
|
for (const skill of connector.skills) {
|
|
638
629
|
const dir = this.skillDir(ctx, skill.name);
|
|
639
630
|
changes.push(
|
|
640
|
-
this.writeContentFile(join(dir, "SKILL.md"),
|
|
631
|
+
this.writeContentFile(join(dir, "SKILL.md"), renderSkillMd(skill), ctx.dryRun)
|
|
641
632
|
);
|
|
642
633
|
for (const [rel, contents] of Object.entries(skill.resources ?? {})) {
|
|
643
634
|
const target = this.resolveWithin(dir, rel);
|
|
@@ -672,26 +663,6 @@ var OpenClawAdapter = class extends BaseAdapter {
|
|
|
672
663
|
}
|
|
673
664
|
return changes;
|
|
674
665
|
}
|
|
675
|
-
/**
|
|
676
|
-
* Render a skill's SKILL.md: frontmatter (name, description + optional model,
|
|
677
|
-
* allowed-tools, disable-model-invocation) + body. UNIFORM with codex /
|
|
678
|
-
* claude-code — only the parent dir differs (single-line frontmatter keys;
|
|
679
|
-
* OpenClaw follows the AgentSkills spec).
|
|
680
|
-
*/
|
|
681
|
-
renderSkill(skill) {
|
|
682
|
-
const frontmatter = {
|
|
683
|
-
name: skill.name,
|
|
684
|
-
description: skill.description
|
|
685
|
-
};
|
|
686
|
-
if (skill.model !== void 0) frontmatter.model = skill.model;
|
|
687
|
-
const allow = skill.tools?.allow;
|
|
688
|
-
if (allow && allow.length > 0) frontmatter["allowed-tools"] = allow.join(", ");
|
|
689
|
-
if (skill.disableModelInvocation !== void 0) {
|
|
690
|
-
frontmatter["disable-model-invocation"] = skill.disableModelInvocation;
|
|
691
|
-
}
|
|
692
|
-
if (skill.extra) Object.assign(frontmatter, skill.extra);
|
|
693
|
-
return this.renderFrontmatterMd(frontmatter, skill.body);
|
|
694
|
-
}
|
|
695
666
|
// ── ts-plugin synthesis ────────────────────────────────────────────────
|
|
696
667
|
/**
|
|
697
668
|
* Build ONE self-contained ESM plugin module for OpenClaw.
|
|
@@ -1060,4 +1031,4 @@ export {
|
|
|
1060
1031
|
adapter,
|
|
1061
1032
|
openclaw_default
|
|
1062
1033
|
};
|
|
1063
|
-
//# sourceMappingURL=chunk-
|
|
1034
|
+
//# sourceMappingURL=chunk-CENEXP7K.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 context (fires PER TURN; no blocking). ONE\n * handler serves two events via separate state: SessionStart\n * additionalContext via { appendSystemContext } ONCE (the\n * verified injection point — session_start itself returns no\n * context payload) AND UserPromptSubmit additionalContext via\n * { appendContext } EVERY turn (context-injection only — a\n * block/deny decision degrades to a no-op).\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 / userPromptSubmit / subagentStart /\n * subagentStop true; supportsNativeHooks 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; it has\n * no blocking ability, so userPromptSubmit is context-injection-only.\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 SubagentStartEvent,\n SubagentStopEvent,\n UserPromptSubmitEvent,\n} from \"../../core/types.js\";\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\nimport { parseJsonc } from \"../../core/jsonc.js\";\nimport {\n buildWrappedStdio,\n} from \"../../core/spawn.js\";\nimport { renderSkillMd } from \"../claude-code/render.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 *\n * UserPromptSubmit also maps to before_prompt_build, which fires PER TURN (every\n * prompt build) and can ONLY inject context (prependContext / appendContext /\n * *SystemContext) — it has NO blocking ability. So UserPromptSubmit here is\n * CONTEXT-INJECTION-ONLY: a connector handler returning a block/deny decision\n * DEGRADES to a no-op (the generated bridge injects nothing). The two events\n * coexist in one before_prompt_build handler via separate state (the once-only\n * SessionStart flag must never suppress the per-turn UserPromptSubmit injection).\n */\nconst EVENT_TO_OPENCLAW: Partial<Record<HookEventName, string>> = {\n PreToolUse: \"before_tool_call\",\n PostToolUse: \"after_tool_call\",\n SessionStart: \"session_start\",\n // Per-turn context injection at prompt assembly (context-only, no blocking).\n UserPromptSubmit: \"before_prompt_build\",\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 // UserPromptSubmit (before_prompt_build): the current prompt text.\n prompt?: 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\" | \"streamable-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 // before_prompt_build fires per-turn and injects context (no blocking), so\n // UserPromptSubmit is supported as CONTEXT-INJECTION-ONLY: a block/deny\n // decision degrades to a no-op (it cannot stop the prompt here).\n userPromptSubmit: true,\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 // stdio sidecars + remote servers. OpenClaw's validator accepts a remote\n // transport of \"sse\" | \"streamable-http\" (AC \"http\" → \"streamable-http\");\n // verified against OpenClaw 2026.6.1 + docs.openclaw.ai/gateway/configuration-reference.\n transports: [\"stdio\", \"sse\", \"http\"],\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 // Action surface: each declared action becomes a user-invokable slash\n // command registered INSIDE the generated plugin module (api.registerCommand),\n // shelling out to the home-bin action verb with the install target baked as\n // the host token (this.id), so a fork like NemoClaw emits `action nemoclaw`.\n // supportsActions -> the installActions/uninstallActions overrides below.\n supportsActions: true,\n // Native passthrough: OpenClaw lifecycle hook names with no canonical analog\n // (declared on platforms.<id>.nativeHooks) are bridged verbatim by the\n // generated plugin via the same on(...) helper → home-bin runNativeHook\n // dispatch (mirrors omp/opencode). Independent of hooks:false.\n supportsNativeHooks: 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\"|\"streamable-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 ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\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 → OpenClaw registers a remote URL sidecar. AC's canonical\n // \"http\" (streamable HTTP) maps to OpenClaw's literal \"streamable-http\" — its\n // validator rejects a bare \"http\" transport value.\n const transport: \"sse\" | \"streamable-http\" =\n server.transport === \"http\" ? \"streamable-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\n // `hooks: false` disables only the CANONICAL (normalized) events; nativeHooks\n // is a sibling, openclaw-scoped declaration that SURVIVES the opt-out (mirrors\n // omp/opencode). The canonicalOff guard in buildPluginSource still suppresses\n // the canonical api.on handlers, so a hooks:false + nativeHooks connector\n // wires ONLY the native passthrough.\n const canonicalDisabled = ctx.connector.platforms[this.id]?.hooks === false;\n const hasCanonical = !canonicalDisabled && ctx.connector.hookEvents.length > 0;\n const hasNative = this.nativeHookEvents(ctx).length > 0;\n\n // Empty-events gate: the plugin module is SHARED with the action surface, so\n // a connector with actions-but-no-hooks still needs it written + registered —\n // but that is installActions' job (it calls ensurePlugin too). installHooks\n // only writes when there ARE hooks (canonical or native); its skip detail\n // stays honest about why.\n if (!hasCanonical && !hasNative) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: pluginPath,\n detail: canonicalDisabled\n ? `hooks disabled for ${this.id}`\n : this.actionTriggers(ctx).length > 0\n ? \"no hooks (plugin written for actions)\"\n : \"connector declares no hooks\",\n },\n ];\n }\n\n return this.ensurePlugin(ctx, `openclaw plugin module (${this.hookDetail(ctx)})`);\n }\n\n // ── Action install / uninstall (slash commands in the shared plugin) ────\n\n /**\n * Each declared action becomes an `api.registerCommand(...)` inside the SAME\n * synthesized plugin module (see buildPluginSource). So installActions ensures\n * that shared module is on disk AND dual-registered: when hooks already wrote\n * the identical bytes the content-compare yields \"skip\" (no double material\n * write), and an actions-only connector gets the plugin written + registered\n * here. Honors platforms.<id>.actions === false and the empty-actions skip.\n */\n override installActions(ctx: InstallContext): ChangeRecord[] {\n const pluginPath = this.getHookConfigPath(ctx);\n if (ctx.connector.platforms[this.id]?.actions === false) {\n return [\n { platform: this.id, action: \"skip\", path: pluginPath, detail: `actions disabled for ${this.id}` },\n ];\n }\n if (this.actionTriggers(ctx).length === 0) {\n return [\n { platform: this.id, action: \"skip\", path: pluginPath, detail: \"connector declares no actions\" },\n ];\n }\n return this.ensurePlugin(ctx, \"openclaw plugin module (action commands)\");\n }\n\n /**\n * Actions live INSIDE the shared plugin module, which uninstallHooks removes\n * (file + manifest + dual-registration entry) unconditionally — it runs last\n * in the teardown pass. Removing the plugin here too would race uninstallHooks,\n * so this is a single informational skip.\n */\n override uninstallActions(ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: this.getHookConfigPath(ctx),\n detail: \"action commands are embedded in the plugin module; removed by uninstallHooks\",\n },\n ];\n }\n\n /**\n * Synthesize + content-compare-write the plugin module(s) AND upsert the dual-\n * registration entry. Shared by installHooks and installActions: the generator\n * is a pure function of ctx, so whichever surface runs second sees byte-\n * identical files (and an already-present entry) and reports \"skip\" — no double\n * material write. `moduleDetail` flavors the module ChangeRecord per surface.\n */\n private ensurePlugin(ctx: InstallContext, moduleDetail: string): ChangeRecord[] {\n const configPath = this.getServerConfigPath(ctx);\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: moduleDetail,\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 =\n ctx.connector.platforms[this.id]?.hooks === false\n ? []\n : 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 native = this.nativeHookEvents(ctx);\n const parts: string[] = [];\n if (mapped.length > 0) parts.push(mapped.join(\",\"));\n if (native.length > 0) parts.push(`native: ${native.join(\",\")}`);\n const base = parts.join(\"; \") || \"no canonical hooks\";\n return unsupported.length > 0\n ? `${base}; unsupported here: ${unsupported.join(\",\")}`\n : base;\n }\n\n /**\n * OpenClaw-native passthrough events declared on platforms.<id>.nativeHooks\n * (host lifecycle hook names with no canonical HookEventName). Emitted as\n * generic on(...) bridge registrations and dispatched host-generically by the\n * home-bin's runNativeHook. Independent of hooks:false (supportsNativeHooks).\n */\n private nativeHookEvents(ctx: InstallContext): string[] {\n return Object.keys(ctx.connector.platforms[this.id]?.nativeHooks ?? {});\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\"), renderSkillMd(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 // ── 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 // `platforms[host].hooks === false` disables the CANONICAL events. The\n // plugin module may still be synthesized for ACTIONS (installActions writes\n // it independently of hooks), so the canonical handler set collapses to []\n // here rather than letting a live before_tool_call/after_tool_call gate leak\n // into a hooks-disabled plugin. Mirrors omp/opencode. Uses this.id so the\n // NemoClaw fork checks platforms.nemoclaw.hooks.\n const canonicalOff = ctx.connector.platforms[this.id]?.hooks === false;\n const events = canonicalOff\n ? []\n : 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 (or native passthrough) event name\\n\" +\n \" * (PreToolUse|PostToolUse|SessionStart|UserPromptSubmit|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\n // before_prompt_build fires on EVERY prompt build. ONE handler serves both\n // context-injecting events that target it — they COEXIST via SEPARATE state\n // so the once-only SessionStart flag never suppresses the per-turn injection:\n // SessionStart → inject pendingContext ONCE (first build), as system ctx.\n // UserPromptSubmit → bridge EVERY turn, append the returned additionalContext.\n // before_prompt_build has NO blocking ability, so a UserPromptSubmit block/deny\n // decision DEGRADES to a no-op (context-injection only — never claim a block).\n if (has(\"SessionStart\") || has(\"UserPromptSubmit\")) {\n let body = \"\";\n if (has(\"SessionStart\")) {\n body +=\n \" // SessionStart: inject the session-start context exactly once.\\n\" +\n \" if (!contextInjected && pendingContext) {\\n\" +\n \" contextInjected = true;\\n\" +\n \" out.appendSystemContext = pendingContext;\\n\" +\n \" }\\n\";\n }\n if (has(\"UserPromptSubmit\")) {\n body +=\n \" // UserPromptSubmit: per-turn context injection. before_prompt_build\\n\" +\n \" // cannot block, so a deny/block decision degrades to a no-op.\\n\" +\n \" const e = event || {};\\n\" +\n \" const ures = bridge(\\\"UserPromptSubmit\\\", {\\n\" +\n ' prompt: typeof e.prompt === \"string\" ? e.prompt : \"\",\\n' +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n ' if (ures && typeof ures.additionalContext === \"string\" && ures.additionalContext) {\\n' +\n \" out.appendContext = ures.additionalContext;\\n\" +\n \" }\\n\";\n }\n reg.push(\n \"\\n\" +\n \" // before_prompt_build → coexisting SessionStart (once) + UserPromptSubmit\\n\" +\n \" // (per-turn) context injection. Context-only: it cannot block a prompt.\\n\" +\n ' on(\"before_prompt_build\", (event) => {\\n' +\n \" const out = {};\\n\" +\n body +\n \" return Object.keys(out).length > 0 ? out : undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n // NATIVE passthrough events: OpenClaw lifecycle hook names with no canonical\n // analog, declared on platforms.<id>.nativeHooks. Each registers an on(...)\n // handler that bridges the NATIVE event name verbatim to the home-bin →\n // runNativeHook dispatches it host-generically. Read straight off\n // platforms.<id>.nativeHooks (independent of hookEvents AND of canonicalOff),\n // so they install even with hooks:false. Mirrors omp/opencode.\n for (const ev of this.nativeHookEvents(ctx)) {\n const key = JSON.stringify(ev);\n reg.push(\n \"\\n\" +\n \" // nativeHooks passthrough → \" + ev + \"\\n\" +\n \" on(\" + key + \", (event) => {\\n\" +\n \" bridge(\" + key + \", { sessionId: SESSION_ID, projectDir: PROJECT_DIR, raw: event });\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n // Action surface: one api.registerCommand per declared action, registered in\n // the SAME register(api) body. The command shells out to the home-bin\n // `action <host>` verb with the host token baked as this.id (so a NemoClaw\n // fork emits `action nemoclaw`, NOT openclaw — exactly like the hook bridge\n // above). The win32 branch reuses the hook bridge's quoted-shell form (a .cmd\n // launcher throws EINVAL via execFileSync). Both id AND description are\n // JSON.stringify'd — descriptions are free-form connector text, so a raw \" /\n // backtick would break the generated module.\n for (const action of this.actionTriggers(ctx)) {\n const id = JSON.stringify(action.id);\n const description = JSON.stringify(action.description);\n reg.push(\n \"\\n\" +\n \" // Action /\" + action.id + \" -> run the home-bin action verb directly.\\n\" +\n \" api.registerCommand({\\n\" +\n \" name: \" + id + \",\\n\" +\n \" description: \" + description + \",\\n\" +\n \" handler: async (_ctx) => {\\n\" +\n \" try {\\n\" +\n ' const args = [\"action\", ' + JSON.stringify(this.id) + \", \" + id + ', \"--connector\", CONNECTOR_ID];\\n' +\n \" const out =\\n\" +\n ' process.platform === \"win32\"\\n' +\n \" ? execSync([HOME_BIN, ...args].map((a) => '\\\"' + a + '\\\"').join(\\\" \\\"), { encoding: \\\"utf8\\\" })\\n\" +\n ' : execFileSync(HOME_BIN, args, { encoding: \"utf8\" });\\n' +\n ' return { text: (out || \"\").trim() };\\n' +\n \" } catch (e) {\\n\" +\n \" return { text: \\\"action \\\" + \" + id + \" + \\\" failed: \\\" + (e && e.message ? e.message : \\\"error\\\") };\\n\" +\n \" }\\n\" +\n \" },\\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 \"UserPromptSubmit\": {\n const ev: UserPromptSubmitEvent = {\n ...base,\n prompt: typeof input.prompt === \"string\" ? input.prompt : \"\",\n };\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 // Actions are health-equivalent to hooks: both write the SAME plugin module\n // and the SAME dual-registration entry, so an actions-only connector must\n // still assert the plugin/extension is present + loaded.\n const hasHooks =\n ctx.connector.hookEvents.length > 0 ||\n this.nativeHookEvents(ctx).length > 0 ||\n this.actionTriggers(ctx).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"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/core/ts-plugin-bridge.ts
|
|
4
|
+
function renderBridgePrelude(opts) {
|
|
5
|
+
return `const HOME_BIN = ${JSON.stringify(opts.homeBin)};
|
|
6
|
+
const CONNECTOR_ID = ${JSON.stringify(opts.connectorId)};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Invoke the universal hook entrypoint for one event.
|
|
10
|
+
* @param {string} event canonical event name${opts.eventDoc ?? ""}
|
|
11
|
+
* @param {object} payload ${opts.payloadNoun}-shaped payload posted on stdin
|
|
12
|
+
* @returns {object|null} normalized HookResponse, or null on any failure
|
|
13
|
+
*/
|
|
14
|
+
function bridge(event, payload) {
|
|
15
|
+
try {
|
|
16
|
+
// On Windows HOME_BIN is the agent-connector.cmd launcher: Node cannot
|
|
17
|
+
// execFile a batch file, and shell+args is deprecated (DEP0190), so run one
|
|
18
|
+
// quoted command line via a shell. POSIX keeps the direct execFile (no shell).
|
|
19
|
+
const args = ["hook", ${JSON.stringify(opts.hookSlug)}, event, "--connector", CONNECTOR_ID];
|
|
20
|
+
const opts = { input: JSON.stringify(payload), encoding: "utf8" };
|
|
21
|
+
const stdout =
|
|
22
|
+
process.platform === "win32"
|
|
23
|
+
? execSync([HOME_BIN, ...args].map((a) => '"' + a + '"').join(" "), opts)
|
|
24
|
+
: execFileSync(HOME_BIN, args, opts);
|
|
25
|
+
const text = (stdout || "").trim();
|
|
26
|
+
if (text === "") return { decision: "allow" };
|
|
27
|
+
return JSON.parse(text);
|
|
28
|
+
} catch {
|
|
29
|
+
// ${opts.failOpenComment}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
renderBridgePrelude
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=chunk-FBJQGRET.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/ts-plugin-bridge.ts"],"sourcesContent":["/**\n * Pure string-builder for the byte-identical HEAD of every ts-plugin host's\n * generated plugin module: the `HOME_BIN`/`CONNECTOR_ID` consts plus the\n * cross-OS `bridge(event, payload)` entrypoint (win32 `execSync`-quoted vs\n * POSIX `execFileSync`, fail-open `JSON.parse`).\n *\n * This module emits SOURCE TEXT only — it imports nothing at runtime and is\n * never loaded by a generated plugin. The 6 adopting hosts (amp, kilo,\n * kilo-cli, mimo-code, omp, opencode) differ in this region by exactly four\n * host-specific tokens, captured below. Changing the emitted bytes here changes\n * every host's generated plugin in lockstep; each host's adapter suite runs the\n * generated plugin live and asserts source substrings, so it is the byte oracle.\n */\n\nexport interface BridgePreludeOptions {\n /** Absolute home-bin path (raw — JSON.stringified inside). */\n homeBin: string;\n /** Connector id (raw — JSON.stringified inside). */\n connectorId: string;\n /** Host hook subcommand slug used in the `hook <slug>` argv (e.g. \"opencode\", \"amp\"). */\n hookSlug: string;\n /** Host noun for the jsdoc `@param payload <noun>-shaped …` line (e.g. \"OpenCode\", \"amp\", \"OMP\"). */\n payloadNoun: string;\n /** Optional suffix on the jsdoc `@param event canonical event name<eventDoc>` line (e.g. \" (PreToolUse|PostToolUse|SessionStart)\"). Default \"\". */\n eventDoc?: string;\n /** The exact fail-open catch-comment line (host wording differs). */\n failOpenComment: string;\n}\n\n/**\n * Render the HOME_BIN/CONNECTOR_ID consts + the universal-entrypoint bridge() —\n * the byte-identical head of every ts-plugin host's generated plugin module.\n */\nexport function renderBridgePrelude(opts: BridgePreludeOptions): string {\n return `const HOME_BIN = ${JSON.stringify(opts.homeBin)};\nconst CONNECTOR_ID = ${JSON.stringify(opts.connectorId)};\n\n/**\n * Invoke the universal hook entrypoint for one event.\n * @param {string} event canonical event name${opts.eventDoc ?? \"\"}\n * @param {object} payload ${opts.payloadNoun}-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 one\n // quoted command line via a shell. POSIX keeps the direct execFile (no shell).\n const args = [\"hook\", ${JSON.stringify(opts.hookSlug)}, event, \"--connector\", CONNECTOR_ID];\n const opts = { input: JSON.stringify(payload), encoding: \"utf8\" };\n const stdout =\n process.platform === \"win32\"\n ? execSync([HOME_BIN, ...args].map((a) => '\"' + a + '\"').join(\" \"), opts)\n : execFileSync(HOME_BIN, args, opts);\n const text = (stdout || \"\").trim();\n if (text === \"\") return { decision: \"allow\" };\n return JSON.parse(text);\n } catch {\n // ${opts.failOpenComment}\n return null;\n }\n}`;\n}\n"],"mappings":";;;AAiCO,SAAS,oBAAoB,MAAoC;AACtE,SAAO,oBAAoB,KAAK,UAAU,KAAK,OAAO,CAAC;AAAA,uBAClC,KAAK,UAAU,KAAK,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA,+CAIR,KAAK,YAAY,EAAE;AAAA,6BACrC,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAQjB,KAAK,UAAU,KAAK,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAUhD,KAAK,eAAe;AAAA;AAAA;AAAA;AAI7B;","names":[]}
|
|
@@ -20,9 +20,22 @@ function toolResponseToString(value) {
|
|
|
20
20
|
return String(value);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
+
function normalizeSessionSource(source) {
|
|
24
|
+
switch (source) {
|
|
25
|
+
case "compact":
|
|
26
|
+
return "compact";
|
|
27
|
+
case "resume":
|
|
28
|
+
return "resume";
|
|
29
|
+
case "clear":
|
|
30
|
+
return "clear";
|
|
31
|
+
default:
|
|
32
|
+
return "startup";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
23
35
|
|
|
24
36
|
export {
|
|
25
37
|
extractSessionId,
|
|
26
|
-
toolResponseToString
|
|
38
|
+
toolResponseToString,
|
|
39
|
+
normalizeSessionSource
|
|
27
40
|
};
|
|
28
|
-
//# sourceMappingURL=chunk-
|
|
41
|
+
//# sourceMappingURL=chunk-H53QZQJI.js.map
|