@ken-jo/agent-connector 0.4.6 → 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-EV63GAPY.js → action-RWPRIZIC.js} +10 -10
- package/dist/{amazon-q-COAL3764.js → amazon-q-5LJOB5L3.js} +6 -18
- package/dist/amazon-q-5LJOB5L3.js.map +1 -0
- package/dist/{amp-XJQ4TDAU.js → amp-PU4LFSY6.js} +16 -48
- package/dist/amp-PU4LFSY6.js.map +1 -0
- package/dist/{antigravity-AMKKKHS4.js → antigravity-DWW35HZO.js} +6 -5
- package/dist/{antigravity-cli-XERXEH5W.js → antigravity-cli-VTQ6DNWL.js} +6 -5
- package/dist/{antigravity-cli-XERXEH5W.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-QC5A3TFQ.js → chunk-A2T4Q7RM.js} +18 -18
- package/dist/{chunk-2X4EH2D7.js → chunk-CDVOBKSG.js} +4 -4
- package/dist/{chunk-RQPKL62M.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-DCK5FADX.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-CPLK7GTE.js → chunk-KG6T3YNJ.js} +353 -36
- package/dist/chunk-KG6T3YNJ.js.map +1 -0
- package/dist/{chunk-MZF2YCXX.js → chunk-MOW2RKHX.js} +2 -2
- package/dist/{chunk-EXLS7BSI.js → chunk-NLFPH4S2.js} +10 -2
- package/dist/chunk-NLFPH4S2.js.map +1 -0
- package/dist/{chunk-EROJDXMW.js → chunk-NYR55UT7.js} +2 -2
- package/dist/{chunk-SRVAA2VN.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-5G4EPJ4Y.js → chunk-VRKTVCYJ.js} +7 -7
- package/dist/{chunk-QOGMDOUX.js → chunk-WI6YSXOC.js} +3 -3
- package/dist/{chunk-BMD7ANKF.js → chunk-XQUUNAGC.js} +36 -36
- package/dist/{chunk-LQRAPXS4.js → chunk-YS2NYJ6R.js} +15 -49
- package/dist/chunk-YS2NYJ6R.js.map +1 -0
- package/dist/{chunk-H67V7DEL.js → chunk-YYISBXOM.js} +7 -7
- package/dist/{claude-code-T2LPFNC3.js → claude-code-64X6RR2S.js} +46 -133
- 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-WRYMNS7Z.js → cline-T7ZDJYZQ.js} +15 -47
- package/dist/cline-T7ZDJYZQ.js.map +1 -0
- package/dist/{codebuff-PODYPOOP.js → codebuff-PPPY3SJV.js} +9 -40
- package/dist/codebuff-PPPY3SJV.js.map +1 -0
- package/dist/{codex-O4G7D7KK.js → codex-EEVN43C2.js} +115 -172
- package/dist/codex-EEVN43C2.js.map +1 -0
- package/dist/{continue-K43SUHLQ.js → continue-VLLZO4PM.js} +12 -33
- package/dist/continue-VLLZO4PM.js.map +1 -0
- package/dist/{copilot-cli-Q3SSPT4V.js → copilot-cli-A7G5WB4N.js} +17 -64
- package/dist/copilot-cli-A7G5WB4N.js.map +1 -0
- package/dist/{crush-MBEH2R3I.js → crush-AH24GDDB.js} +17 -42
- package/dist/crush-AH24GDDB.js.map +1 -0
- package/dist/{cursor-TMBXDYR4.js → cursor-GJBSMXI6.js} +61 -183
- package/dist/cursor-GJBSMXI6.js.map +1 -0
- package/dist/{detect-AHJIUMBO.js → detect-YDCZNV4W.js} +4 -4
- package/dist/{doctor-G5MLCPAB.js → doctor-DGC7P3P7.js} +13 -12
- package/dist/{doctor-G5MLCPAB.js.map → doctor-DGC7P3P7.js.map} +1 -1
- package/dist/{droid-JC2NT6S4.js → droid-RBWCFKL3.js} +63 -144
- package/dist/droid-RBWCFKL3.js.map +1 -0
- package/dist/{gemini-cli-ILR7M5X3.js → gemini-cli-CFPFEJEA.js} +20 -63
- package/dist/gemini-cli-CFPFEJEA.js.map +1 -0
- package/dist/{goose-SFO5BBGW.js → goose-UBJVQFUE.js} +107 -183
- package/dist/goose-UBJVQFUE.js.map +1 -0
- package/dist/{hermes-3N7WN4WS.js → hermes-6FXGXLNL.js} +41 -83
- package/dist/hermes-6FXGXLNL.js.map +1 -0
- package/dist/{hook-76JBKTUX.js → hook-CDGSCASQ.js} +10 -10
- package/dist/index.js +2 -2
- package/dist/{install-R7252QFH.js → install-2D3UKEME.js} +13 -12
- package/dist/{install-R7252QFH.js.map → install-2D3UKEME.js.map} +1 -1
- package/dist/{jetbrains-copilot-NRZHI5PC.js → jetbrains-copilot-K26KMA3U.js} +12 -37
- package/dist/jetbrains-copilot-K26KMA3U.js.map +1 -0
- package/dist/{kilo-IX4T3OHJ.js → kilo-BKZJ26RM.js} +26 -90
- package/dist/kilo-BKZJ26RM.js.map +1 -0
- package/dist/{kilo-cli-2AYCXBQD.js → kilo-cli-6UVWCXQD.js} +17 -48
- package/dist/kilo-cli-6UVWCXQD.js.map +1 -0
- package/dist/{kimi-2YFK2GBA.js → kimi-QALYPSRR.js} +20 -31
- package/dist/kimi-QALYPSRR.js.map +1 -0
- package/dist/{kiro-YL6AWUYG.js → kiro-J5J42I5Z.js} +12 -31
- package/dist/kiro-J5J42I5Z.js.map +1 -0
- package/dist/{leaderboard-V6VWA5ZV.js → leaderboard-VZQXNWTX.js} +2 -2
- package/dist/{mimo-code-FTMGSKMQ.js → mimo-code-BEE622P2.js} +22 -87
- package/dist/mimo-code-BEE622P2.js.map +1 -0
- package/dist/{mux-5XZFB44C.js → mux-5HTNKK4K.js} +10 -54
- package/dist/mux-5HTNKK4K.js.map +1 -0
- package/dist/{nemoclaw-CQYXZVQN.js → nemoclaw-DNFIZFDF.js} +5 -5
- package/dist/{omp-2QMEY6HD.js → omp-I3UTRBNP.js} +16 -48
- package/dist/omp-I3UTRBNP.js.map +1 -0
- package/dist/{openclaw-UZC437RW.js → openclaw-QE2LMWIF.js} +5 -5
- package/dist/{opencode-L4BGFP25.js → opencode-ZFSUYJXI.js} +22 -88
- package/dist/opencode-ZFSUYJXI.js.map +1 -0
- package/dist/{package-DNLQTPNN.js → package-BSCCMW4E.js} +8 -8
- package/dist/{pi-PVVPVWKG.js → pi-H2KBBDDJ.js} +4 -4
- package/dist/{qwen-code-G4CXKUHR.js → qwen-code-5LE34FNE.js} +63 -152
- package/dist/qwen-code-5LE34FNE.js.map +1 -0
- package/dist/{roo-code-67TYJIQR.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-EOVWYBGZ.js → serve-MQNGPY2H.js} +10 -10
- package/dist/{status-VTD6XXLW.js → status-MRHVRDFY.js} +8 -7
- package/dist/{status-VTD6XXLW.js.map → status-MRHVRDFY.js.map} +1 -1
- package/dist/{statusline-3ET5YPQ5.js → statusline-IYWA75TR.js} +9 -9
- package/dist/{telemetry-QCTMKDBQ.js → telemetry-IONIOMLC.js} +5 -5
- package/dist/{trae-7CZZGOGI.js → trae-RVNRSV7X.js} +10 -39
- package/dist/trae-RVNRSV7X.js.map +1 -0
- package/dist/{uninstall-FL3SH2DT.js → uninstall-ESLSVCLE.js} +13 -12
- package/dist/{uninstall-FL3SH2DT.js.map → uninstall-ESLSVCLE.js.map} +1 -1
- package/dist/{upgrade-SLFG2FCT.js → upgrade-VICY5T64.js} +13 -12
- package/dist/{upgrade-SLFG2FCT.js.map → upgrade-VICY5T64.js.map} +1 -1
- package/dist/{usage-IRBLVUZM.js → usage-TCUWV2YZ.js} +2 -2
- package/dist/{usage-event-VFF77WYY.js → usage-event-CBZZP37Q.js} +9 -9
- package/dist/{vscode-copilot-MF2CDNMT.js → vscode-copilot-B2QBBHJW.js} +17 -65
- package/dist/vscode-copilot-B2QBBHJW.js.map +1 -0
- package/dist/{warp-VKG55TAT.js → warp-5R54ST3N.js} +7 -19
- package/dist/warp-5R54ST3N.js.map +1 -0
- package/dist/{windsurf-FU5G4SHF.js → windsurf-L32UEOLY.js} +6 -18
- package/dist/windsurf-L32UEOLY.js.map +1 -0
- package/dist/{zed-SFUHL3GS.js → zed-FDJHJZKD.js} +11 -21
- package/dist/zed-FDJHJZKD.js.map +1 -0
- package/package.json +1 -1
- package/dist/amazon-q-COAL3764.js.map +0 -1
- package/dist/amp-XJQ4TDAU.js.map +0 -1
- package/dist/chunk-7L6ITC4T.js.map +0 -1
- package/dist/chunk-CPLK7GTE.js.map +0 -1
- package/dist/chunk-EXLS7BSI.js.map +0 -1
- package/dist/chunk-G5CQHVQG.js.map +0 -1
- package/dist/chunk-I5MLE6S7.js.map +0 -1
- package/dist/chunk-LQRAPXS4.js.map +0 -1
- package/dist/chunk-RQPKL62M.js.map +0 -1
- package/dist/chunk-WZA4CSGJ.js.map +0 -1
- package/dist/claude-code-T2LPFNC3.js.map +0 -1
- package/dist/cline-WRYMNS7Z.js.map +0 -1
- package/dist/codebuff-PODYPOOP.js.map +0 -1
- package/dist/codex-O4G7D7KK.js.map +0 -1
- package/dist/continue-K43SUHLQ.js.map +0 -1
- package/dist/copilot-cli-Q3SSPT4V.js.map +0 -1
- package/dist/crush-MBEH2R3I.js.map +0 -1
- package/dist/cursor-TMBXDYR4.js.map +0 -1
- package/dist/droid-JC2NT6S4.js.map +0 -1
- package/dist/gemini-cli-ILR7M5X3.js.map +0 -1
- package/dist/goose-SFO5BBGW.js.map +0 -1
- package/dist/hermes-3N7WN4WS.js.map +0 -1
- package/dist/jetbrains-copilot-NRZHI5PC.js.map +0 -1
- package/dist/kilo-IX4T3OHJ.js.map +0 -1
- package/dist/kilo-cli-2AYCXBQD.js.map +0 -1
- package/dist/kimi-2YFK2GBA.js.map +0 -1
- package/dist/kiro-YL6AWUYG.js.map +0 -1
- package/dist/mimo-code-FTMGSKMQ.js.map +0 -1
- package/dist/mux-5XZFB44C.js.map +0 -1
- package/dist/omp-2QMEY6HD.js.map +0 -1
- package/dist/opencode-L4BGFP25.js.map +0 -1
- package/dist/qwen-code-G4CXKUHR.js.map +0 -1
- package/dist/roo-code-67TYJIQR.js.map +0 -1
- package/dist/trae-7CZZGOGI.js.map +0 -1
- package/dist/vscode-copilot-MF2CDNMT.js.map +0 -1
- package/dist/warp-VKG55TAT.js.map +0 -1
- package/dist/windsurf-FU5G4SHF.js.map +0 -1
- package/dist/zed-SFUHL3GS.js.map +0 -1
- /package/dist/{action-EV63GAPY.js.map → action-RWPRIZIC.js.map} +0 -0
- /package/dist/{antigravity-AMKKKHS4.js.map → antigravity-DWW35HZO.js.map} +0 -0
- /package/dist/{chunk-F5RX62T7.js.map → chunk-7QRUZAUG.js.map} +0 -0
- /package/dist/{chunk-QC5A3TFQ.js.map → chunk-A2T4Q7RM.js.map} +0 -0
- /package/dist/{chunk-2X4EH2D7.js.map → chunk-CDVOBKSG.js.map} +0 -0
- /package/dist/{chunk-DCK5FADX.js.map → chunk-HOE2MN36.js.map} +0 -0
- /package/dist/{chunk-MZF2YCXX.js.map → chunk-MOW2RKHX.js.map} +0 -0
- /package/dist/{chunk-EROJDXMW.js.map → chunk-NYR55UT7.js.map} +0 -0
- /package/dist/{chunk-SRVAA2VN.js.map → chunk-OZ5EXXQV.js.map} +0 -0
- /package/dist/{chunk-5G4EPJ4Y.js.map → chunk-VRKTVCYJ.js.map} +0 -0
- /package/dist/{chunk-QOGMDOUX.js.map → chunk-WI6YSXOC.js.map} +0 -0
- /package/dist/{chunk-BMD7ANKF.js.map → chunk-XQUUNAGC.js.map} +0 -0
- /package/dist/{chunk-H67V7DEL.js.map → chunk-YYISBXOM.js.map} +0 -0
- /package/dist/{detect-AHJIUMBO.js.map → detect-YDCZNV4W.js.map} +0 -0
- /package/dist/{hook-76JBKTUX.js.map → hook-CDGSCASQ.js.map} +0 -0
- /package/dist/{leaderboard-V6VWA5ZV.js.map → leaderboard-VZQXNWTX.js.map} +0 -0
- /package/dist/{nemoclaw-CQYXZVQN.js.map → nemoclaw-DNFIZFDF.js.map} +0 -0
- /package/dist/{openclaw-UZC437RW.js.map → openclaw-QE2LMWIF.js.map} +0 -0
- /package/dist/{package-DNLQTPNN.js.map → package-BSCCMW4E.js.map} +0 -0
- /package/dist/{pi-PVVPVWKG.js.map → pi-H2KBBDDJ.js.map} +0 -0
- /package/dist/{serve-EOVWYBGZ.js.map → serve-MQNGPY2H.js.map} +0 -0
- /package/dist/{statusline-3ET5YPQ5.js.map → statusline-IYWA75TR.js.map} +0 -0
- /package/dist/{telemetry-QCTMKDBQ.js.map → telemetry-IONIOMLC.js.map} +0 -0
- /package/dist/{usage-IRBLVUZM.js.map → usage-TCUWV2YZ.js.map} +0 -0
- /package/dist/{usage-event-VFF77WYY.js.map → usage-event-CBZZP37Q.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/claude-code/wire.ts"],"sourcesContent":["/**\n * adapters/claude-code/wire — Claude Code hook wire protocol.\n *\n * Claude Code pipes a JSON object to a hook command on stdin and reads a JSON\n * object (or empty) from stdout, interpreting the exit code as well. This module\n * captures the exact stdin shape and the canonical reply shape so the adapter's\n * parse/format methods are a single source of truth.\n *\n * Stdin (documented Claude Code hook input):\n * {\n * session_id, transcript_path, cwd, hook_event_name,\n * tool_name, tool_input, tool_response, // tool events\n * permission_suggestions, // PermissionRequest\n * tool_use_id, error, is_interrupt, duration_ms, // PostToolUseFailure\n * agent_id, agent_type, // SubagentStart/Stop\n * agent_transcript_path, last_assistant_message, // SubagentStop\n * source, // SessionStart\n * reason, // SessionEnd\n * prompt, // UserPromptSubmit\n * trigger, // PreCompact\n * stop_hook_active, // Stop / SubagentStop\n * message // Notification\n * }\n *\n * Stdout (canonical reply): a `hookSpecificOutput` object keyed by\n * `hookEventName`, carrying `permissionDecision` (allow|deny|ask) +\n * `permissionDecisionReason`, `additionalContext`, and (PreToolUse)\n * `updatedInput`; PermissionRequest instead takes a nested\n * `decision: { behavior: \"allow\"|\"deny\", ... }`; Stop-class blocks\n * (Stop / SubagentStop / UserPromptSubmit / PostToolUse) take the TOP-LEVEL\n * `{\"decision\":\"block\",\"reason\"}`. Exit 0 = proceed; the JSON refines the\n * decision.\n */\n\nimport type { SessionStartEvent } from \"../../core/types.js\";\n\n/** Canonical Claude Code hook event names (the values of `hook_event_name`). */\nexport const CLAUDE_HOOK_EVENTS = [\n \"PreToolUse\",\n \"PostToolUse\",\n \"PreCompact\",\n \"SessionStart\",\n \"SessionEnd\",\n \"UserPromptSubmit\",\n \"Stop\",\n \"Notification\",\n \"PermissionRequest\",\n \"PostToolUseFailure\",\n \"SubagentStart\",\n \"SubagentStop\",\n] as const;\n\nexport type ClaudeHookEvent = (typeof CLAUDE_HOOK_EVENTS)[number];\n\n/**\n * Raw Claude Code hook stdin payload. Every field is optional — a given event\n * only populates the subset relevant to it. `connector` is NOT a Claude field;\n * it is threaded in by the universal entrypoint so parseEvent can recover the\n * connector id (the home binary passes `--connector <id>` and may inject it).\n */\nexport interface ClaudeWireInput {\n session_id?: string;\n transcript_path?: string;\n cwd?: string;\n hook_event_name?: string;\n\n // tool events\n tool_name?: string;\n tool_input?: Record<string, unknown>;\n /** PostToolUse result payload (string or structured). */\n tool_response?: unknown;\n\n // PermissionRequest — permission-update entries the dialog would offer.\n // NOTE: the docs explicitly state PermissionRequest has NO tool_use_id.\n permission_suggestions?: unknown[];\n\n // PostToolUseFailure\n tool_use_id?: string;\n error?: string;\n is_interrupt?: boolean;\n duration_ms?: number;\n\n // SubagentStart / SubagentStop — agent_type is unreliable on SubagentStop\n // (the SDK may not populate it); treat both as optional everywhere.\n agent_id?: string;\n agent_type?: string;\n // SubagentStop — the subagent's OWN transcript (transcript_path stays the\n // parent session's) + the text of its final response.\n agent_transcript_path?: string;\n last_assistant_message?: string;\n\n // SessionStart\n source?: string;\n // SessionEnd\n reason?: string;\n // UserPromptSubmit\n prompt?: string;\n // PreCompact\n trigger?: string;\n // Stop / SubagentStop\n stop_hook_active?: boolean;\n // Notification\n message?: string;\n\n /** Injected by the entrypoint so the runtime knows which connector to dispatch. */\n connector?: string;\n}\n\n/**\n * Extract a stable session id from a Claude wire payload.\n * Priority mirrors the proven adapter: transcript UUID > session_id > \"\".\n * (The framework's normalized event uses \"\" when no id is available, per\n * core/types BaseEvent.sessionId docstring — no ppid fabrication here.)\n */\nexport function extractSessionId(input: ClaudeWireInput): string {\n if (typeof input.transcript_path === \"string\") {\n const m = input.transcript_path.match(/([a-f0-9-]{36})\\.jsonl$/);\n if (m && m[1]) return m[1];\n }\n if (typeof input.session_id === \"string\" && input.session_id !== \"\") {\n return input.session_id;\n }\n return \"\";\n}\n\n/** Coerce a Claude PostToolUse `tool_response` into a string for the normalized event. */\nexport function toolResponseToString(value: unknown): string | undefined {\n if (value == null) return undefined;\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/** Map a host's raw session-start \"source\" to the canonical SessionStart source. */\nexport function normalizeSessionSource(source: string | undefined): SessionStartEvent[\"source\"] {\n switch (source) {\n case \"compact\":\n return \"compact\";\n case \"resume\":\n return \"resume\";\n case \"clear\":\n return \"clear\";\n default:\n return \"startup\";\n }\n}\n"],"mappings":";;;AAkHO,SAAS,iBAAiB,OAAgC;AAC/D,MAAI,OAAO,MAAM,oBAAoB,UAAU;AAC7C,UAAM,IAAI,MAAM,gBAAgB,MAAM,yBAAyB;AAC/D,QAAI,KAAK,EAAE,CAAC,EAAG,QAAO,EAAE,CAAC;AAAA,EAC3B;AACA,MAAI,OAAO,MAAM,eAAe,YAAY,MAAM,eAAe,IAAI;AACnE,WAAO,MAAM;AAAA,EACf;AACA,SAAO;AACT;AAGO,SAAS,qBAAqB,OAAoC;AACvE,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;AAGO,SAAS,uBAAuB,QAAyD;AAC9F,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
REGISTERED_PLATFORM_IDS
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XQUUNAGC.js";
|
|
5
5
|
import {
|
|
6
6
|
CONFIG_PATCH_SEGMENT_RE,
|
|
7
7
|
configPatchNamespaceViolation,
|
|
@@ -495,4 +495,4 @@ export {
|
|
|
495
495
|
defineConfigPatch,
|
|
496
496
|
defineNativeHook
|
|
497
497
|
};
|
|
498
|
-
//# sourceMappingURL=chunk-
|
|
498
|
+
//# sourceMappingURL=chunk-HOE2MN36.js.map
|
|
@@ -9,13 +9,16 @@ ${stringifyYaml(frontmatter)}---
|
|
|
9
9
|
${body}
|
|
10
10
|
`;
|
|
11
11
|
}
|
|
12
|
-
function renderCommandMd(cmd) {
|
|
12
|
+
function renderCommandMd(cmd, opts = {}) {
|
|
13
|
+
const { includeToolsAndModel = true } = opts;
|
|
13
14
|
const frontmatter = {};
|
|
14
15
|
if (cmd.description !== void 0) frontmatter.description = cmd.description;
|
|
15
16
|
if (cmd.argumentHint !== void 0) frontmatter["argument-hint"] = cmd.argumentHint;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
if (includeToolsAndModel) {
|
|
18
|
+
const allow = cmd.tools?.allow;
|
|
19
|
+
if (allow && allow.length > 0) frontmatter["allowed-tools"] = allow.join(", ");
|
|
20
|
+
if (cmd.model !== void 0) frontmatter.model = cmd.model;
|
|
21
|
+
}
|
|
19
22
|
if (cmd.extra) Object.assign(frontmatter, cmd.extra);
|
|
20
23
|
return renderFrontmatterMd(frontmatter, cmd.prompt);
|
|
21
24
|
}
|
|
@@ -44,11 +47,24 @@ function renderSubagentMd(agent) {
|
|
|
44
47
|
if (agent.extra) Object.assign(frontmatter, agent.extra);
|
|
45
48
|
return renderFrontmatterMd(frontmatter, agent.prompt);
|
|
46
49
|
}
|
|
50
|
+
function renderOpenCodeSubagentMd(agent) {
|
|
51
|
+
const frontmatter = {
|
|
52
|
+
description: agent.description,
|
|
53
|
+
mode: "subagent"
|
|
54
|
+
};
|
|
55
|
+
if (agent.model !== void 0) frontmatter.model = agent.model;
|
|
56
|
+
if (agent.readonly === true) {
|
|
57
|
+
frontmatter.permission = { edit: "deny", bash: "deny" };
|
|
58
|
+
}
|
|
59
|
+
if (agent.extra) Object.assign(frontmatter, agent.extra);
|
|
60
|
+
return renderFrontmatterMd(frontmatter, agent.prompt);
|
|
61
|
+
}
|
|
47
62
|
|
|
48
63
|
export {
|
|
49
64
|
renderFrontmatterMd,
|
|
50
65
|
renderCommandMd,
|
|
51
66
|
renderSkillMd,
|
|
52
|
-
renderSubagentMd
|
|
67
|
+
renderSubagentMd,
|
|
68
|
+
renderOpenCodeSubagentMd
|
|
53
69
|
};
|
|
54
|
-
//# sourceMappingURL=chunk-
|
|
70
|
+
//# sourceMappingURL=chunk-IEGR4TP3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/claude-code/render.ts"],"sourcesContent":["/**\n * adapters/claude-code/render — shared content-surface renderers.\n *\n * The single source of truth for how a connector's commands / skills / subagents\n * are serialized into Claude Code's native `md + YAML-frontmatter` documents.\n * BOTH the live claude-code adapter (which writes into ~/.claude or .claude when\n * installing) AND `core/package` (which emits a marketplace-installable plugin\n * bundle) import these so the two paths produce BYTE-IDENTICAL output — there is\n * no second, subtly-different copy to drift.\n *\n * The frontmatter serializer reuses the `yaml` package (the same serializer\n * core/yaml and BaseAdapter.renderFrontmatterMd use): a `---` fence, the YAML\n * block, a closing `---`, a blank line, then the markdown body and a trailing\n * newline.\n */\n\nimport { stringify as stringifyYaml } from \"yaml\";\n\nimport type { CommandDef, SkillDef, SubagentDef } from \"../../core/types.js\";\n\n/**\n * Render a YAML-frontmatter + markdown body document:\n * \"---\\n\" + <yaml> + \"---\\n\\n\" + <body> + \"\\n\".\n * Identical to BaseAdapter.renderFrontmatterMd (which now delegates here).\n */\nexport function renderFrontmatterMd(\n frontmatter: Record<string, unknown>,\n body: string,\n): string {\n return `---\\n${stringifyYaml(frontmatter)}---\\n\\n${body}\\n`;\n}\n\n/**\n * Render a command to md+frontmatter\n * (description, argument-hint, allowed-tools, model + verbatim extra).\n *\n * `opts.includeToolsAndModel` (default `true`) controls the allowed-tools and\n * model insertions. Hosts whose command frontmatter is only\n * (description, argument-hint + verbatim extra) — i.e. they have no native\n * allowed-tools/model keys — pass `{ includeToolsAndModel: false }` to suppress\n * both; the default reproduces the full shape byte-for-byte.\n */\nexport function renderCommandMd(\n cmd: CommandDef,\n opts: { includeToolsAndModel?: boolean } = {},\n): string {\n const { includeToolsAndModel = true } = opts;\n const frontmatter: Record<string, unknown> = {};\n if (cmd.description !== undefined) frontmatter.description = cmd.description;\n if (cmd.argumentHint !== undefined) frontmatter[\"argument-hint\"] = cmd.argumentHint;\n if (includeToolsAndModel) {\n const allow = cmd.tools?.allow;\n if (allow && allow.length > 0) frontmatter[\"allowed-tools\"] = allow.join(\", \");\n if (cmd.model !== undefined) frontmatter.model = cmd.model;\n }\n if (cmd.extra) Object.assign(frontmatter, cmd.extra);\n return renderFrontmatterMd(frontmatter, cmd.prompt);\n}\n\n/**\n * Render a skill's SKILL.md: frontmatter (name, description + optional model,\n * allowed-tools, disable-model-invocation, verbatim extra) + body.\n */\nexport function renderSkillMd(skill: SkillDef): string {\n const frontmatter: Record<string, unknown> = {\n name: skill.name,\n description: skill.description,\n };\n if (skill.model !== undefined) frontmatter.model = skill.model;\n const allow = skill.tools?.allow;\n if (allow && allow.length > 0) frontmatter[\"allowed-tools\"] = allow.join(\", \");\n if (skill.disableModelInvocation !== undefined) {\n frontmatter[\"disable-model-invocation\"] = skill.disableModelInvocation;\n }\n if (skill.extra) Object.assign(frontmatter, skill.extra);\n return renderFrontmatterMd(frontmatter, skill.body);\n}\n\n/** Render a subagent to md+frontmatter (name, description, tools, model) + prompt body. */\nexport function renderSubagentMd(agent: SubagentDef): string {\n const frontmatter: Record<string, unknown> = {\n name: agent.name,\n description: agent.description,\n };\n const allow = agent.tools?.allow;\n if (allow && allow.length > 0) frontmatter.tools = allow.join(\", \");\n if (agent.model !== undefined) frontmatter.model = agent.model;\n if (agent.extra) Object.assign(frontmatter, agent.extra);\n return renderFrontmatterMd(frontmatter, agent.prompt);\n}\n\n/**\n * Render a subagent in OpenCode's shape (shared by opencode/kilo/mimo-code):\n * frontmatter (description, mode:\"subagent\", model?, permission? + verbatim\n * extra) with the system prompt as the body. `name` is NOT a frontmatter\n * field — it comes from the filename. `permission` is derived from the coarse\n * `readonly` knob: a readonly agent gets a per-tool deny map (edit/bash) so it\n * cannot mutate the workspace.\n */\nexport function renderOpenCodeSubagentMd(agent: SubagentDef): string {\n const frontmatter: Record<string, unknown> = {\n description: agent.description,\n mode: \"subagent\",\n };\n if (agent.model !== undefined) frontmatter.model = agent.model;\n if (agent.readonly === true) {\n // Per-tool permission map: deny mutating tools for a readonly subagent.\n frontmatter.permission = { edit: \"deny\", bash: \"deny\" };\n }\n if (agent.extra) Object.assign(frontmatter, agent.extra);\n return renderFrontmatterMd(frontmatter, agent.prompt);\n}\n"],"mappings":";;;AAgBA,SAAS,aAAa,qBAAqB;AASpC,SAAS,oBACd,aACA,MACQ;AACR,SAAO;AAAA,EAAQ,cAAc,WAAW,CAAC;AAAA;AAAA,EAAU,IAAI;AAAA;AACzD;AAYO,SAAS,gBACd,KACA,OAA2C,CAAC,GACpC;AACR,QAAM,EAAE,uBAAuB,KAAK,IAAI;AACxC,QAAM,cAAuC,CAAC;AAC9C,MAAI,IAAI,gBAAgB,OAAW,aAAY,cAAc,IAAI;AACjE,MAAI,IAAI,iBAAiB,OAAW,aAAY,eAAe,IAAI,IAAI;AACvE,MAAI,sBAAsB;AACxB,UAAM,QAAQ,IAAI,OAAO;AACzB,QAAI,SAAS,MAAM,SAAS,EAAG,aAAY,eAAe,IAAI,MAAM,KAAK,IAAI;AAC7E,QAAI,IAAI,UAAU,OAAW,aAAY,QAAQ,IAAI;AAAA,EACvD;AACA,MAAI,IAAI,MAAO,QAAO,OAAO,aAAa,IAAI,KAAK;AACnD,SAAO,oBAAoB,aAAa,IAAI,MAAM;AACpD;AAMO,SAAS,cAAc,OAAyB;AACrD,QAAM,cAAuC;AAAA,IAC3C,MAAM,MAAM;AAAA,IACZ,aAAa,MAAM;AAAA,EACrB;AACA,MAAI,MAAM,UAAU,OAAW,aAAY,QAAQ,MAAM;AACzD,QAAM,QAAQ,MAAM,OAAO;AAC3B,MAAI,SAAS,MAAM,SAAS,EAAG,aAAY,eAAe,IAAI,MAAM,KAAK,IAAI;AAC7E,MAAI,MAAM,2BAA2B,QAAW;AAC9C,gBAAY,0BAA0B,IAAI,MAAM;AAAA,EAClD;AACA,MAAI,MAAM,MAAO,QAAO,OAAO,aAAa,MAAM,KAAK;AACvD,SAAO,oBAAoB,aAAa,MAAM,IAAI;AACpD;AAGO,SAAS,iBAAiB,OAA4B;AAC3D,QAAM,cAAuC;AAAA,IAC3C,MAAM,MAAM;AAAA,IACZ,aAAa,MAAM;AAAA,EACrB;AACA,QAAM,QAAQ,MAAM,OAAO;AAC3B,MAAI,SAAS,MAAM,SAAS,EAAG,aAAY,QAAQ,MAAM,KAAK,IAAI;AAClE,MAAI,MAAM,UAAU,OAAW,aAAY,QAAQ,MAAM;AACzD,MAAI,MAAM,MAAO,QAAO,OAAO,aAAa,MAAM,KAAK;AACvD,SAAO,oBAAoB,aAAa,MAAM,MAAM;AACtD;AAUO,SAAS,yBAAyB,OAA4B;AACnE,QAAM,cAAuC;AAAA,IAC3C,aAAa,MAAM;AAAA,IACnB,MAAM;AAAA,EACR;AACA,MAAI,MAAM,UAAU,OAAW,aAAY,QAAQ,MAAM;AACzD,MAAI,MAAM,aAAa,MAAM;AAE3B,gBAAY,aAAa,EAAE,MAAM,QAAQ,MAAM,OAAO;AAAA,EACxD;AACA,MAAI,MAAM,MAAO,QAAO,OAAO,aAAa,MAAM,KAAK;AACvD,SAAO,oBAAoB,aAAa,MAAM,MAAM;AACtD;","names":[]}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
renderFrontmatterMd
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-IEGR4TP3.js";
|
|
5
5
|
import {
|
|
6
|
-
buildHomeBinActionCommand
|
|
7
|
-
|
|
6
|
+
buildHomeBinActionCommand,
|
|
7
|
+
buildHomeBinHookCommand
|
|
8
|
+
} from "./chunk-TO57WYBB.js";
|
|
8
9
|
import {
|
|
9
10
|
MEMORY_CONTENT_SOFT_BUDGET_BYTES,
|
|
10
11
|
listManagedBlocks,
|
|
@@ -102,6 +103,88 @@ function parseJsonc(text) {
|
|
|
102
103
|
return JSON.parse(stripJsonc(text));
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
// src/core/object-map.ts
|
|
107
|
+
function isMalformedRootValue(value) {
|
|
108
|
+
return value != null && (typeof value !== "object" || Array.isArray(value));
|
|
109
|
+
}
|
|
110
|
+
function unparseableWarn(platform, configPath) {
|
|
111
|
+
return {
|
|
112
|
+
platform,
|
|
113
|
+
action: "warn",
|
|
114
|
+
path: configPath,
|
|
115
|
+
detail: `existing ${configPath} is not parseable; left untouched (back it up / fix it, then re-run)`
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function malformedWarn(platform, rootKey, configPath) {
|
|
119
|
+
return {
|
|
120
|
+
platform,
|
|
121
|
+
action: "warn",
|
|
122
|
+
path: configPath,
|
|
123
|
+
detail: `existing "${rootKey}" in ${configPath} is not an object map; left untouched (fix it, then re-run)`
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function upsertInObjectMap(o) {
|
|
127
|
+
const policy = o.policy ?? "warn-skip";
|
|
128
|
+
if (o.codec.isPresentButUnparseable(o.configPath)) {
|
|
129
|
+
return unparseableWarn(o.platform, o.configPath);
|
|
130
|
+
}
|
|
131
|
+
const cfg = o.codec.parse(o.configPath) ?? {};
|
|
132
|
+
if (isMalformedRootValue(cfg[o.rootKey])) {
|
|
133
|
+
if (policy === "warn-skip") return malformedWarn(o.platform, o.rootKey, o.configPath);
|
|
134
|
+
cfg[o.rootKey] = {};
|
|
135
|
+
}
|
|
136
|
+
const bucket = cfg[o.rootKey] ?? (cfg[o.rootKey] = {});
|
|
137
|
+
const before = JSON.stringify(bucket[o.entryId]);
|
|
138
|
+
const after = JSON.stringify(o.entry);
|
|
139
|
+
let action;
|
|
140
|
+
if (before === void 0) action = "create";
|
|
141
|
+
else if (before === after) action = "skip";
|
|
142
|
+
else action = "update";
|
|
143
|
+
if (action !== "skip") {
|
|
144
|
+
bucket[o.entryId] = o.entry;
|
|
145
|
+
o.codec.serialize(o.configPath, cfg, o.dryRun ?? false);
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
platform: o.platform,
|
|
149
|
+
action,
|
|
150
|
+
path: o.configPath,
|
|
151
|
+
detail: `${o.rootKey}.${o.entryId}`
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function removeFromObjectMap(o) {
|
|
155
|
+
const policy = o.policy ?? "warn-skip";
|
|
156
|
+
if (o.codec.isPresentButUnparseable(o.configPath)) {
|
|
157
|
+
return unparseableWarn(o.platform, o.configPath);
|
|
158
|
+
}
|
|
159
|
+
const cfg = o.codec.parse(o.configPath);
|
|
160
|
+
if (cfg && isMalformedRootValue(cfg[o.rootKey])) {
|
|
161
|
+
if (policy === "warn-skip") return malformedWarn(o.platform, o.rootKey, o.configPath);
|
|
162
|
+
return {
|
|
163
|
+
platform: o.platform,
|
|
164
|
+
action: "skip",
|
|
165
|
+
path: o.configPath,
|
|
166
|
+
detail: `${o.rootKey}.${o.entryId} absent`
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const bucket = cfg?.[o.rootKey];
|
|
170
|
+
if (!cfg || !bucket || !(o.entryId in bucket)) {
|
|
171
|
+
return {
|
|
172
|
+
platform: o.platform,
|
|
173
|
+
action: "skip",
|
|
174
|
+
path: o.configPath,
|
|
175
|
+
detail: `${o.rootKey}.${o.entryId} absent`
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
delete bucket[o.entryId];
|
|
179
|
+
o.codec.serialize(o.configPath, cfg, o.dryRun ?? false);
|
|
180
|
+
return {
|
|
181
|
+
platform: o.platform,
|
|
182
|
+
action: "remove",
|
|
183
|
+
path: o.configPath,
|
|
184
|
+
detail: `${o.rootKey}.${o.entryId}`
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
105
188
|
// src/adapters/base.ts
|
|
106
189
|
import {
|
|
107
190
|
copyFileSync,
|
|
@@ -114,6 +197,36 @@ import {
|
|
|
114
197
|
} from "fs";
|
|
115
198
|
import { homedir } from "os";
|
|
116
199
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "path";
|
|
200
|
+
|
|
201
|
+
// src/core/hook-array.ts
|
|
202
|
+
function upsertInArray(array, candidate, ownsEntry) {
|
|
203
|
+
const idx = array.findIndex(ownsEntry);
|
|
204
|
+
if (idx >= 0) {
|
|
205
|
+
if (JSON.stringify(array[idx]) === JSON.stringify(candidate)) {
|
|
206
|
+
return { array, action: "skip" };
|
|
207
|
+
}
|
|
208
|
+
const next = array.slice();
|
|
209
|
+
next[idx] = candidate;
|
|
210
|
+
return { array: next, action: "update" };
|
|
211
|
+
}
|
|
212
|
+
return { array: [...array, candidate], action: "create" };
|
|
213
|
+
}
|
|
214
|
+
function removeFromArray(array, ownsEntry, stripInner) {
|
|
215
|
+
if (!stripInner) {
|
|
216
|
+
const kept2 = array.filter((e) => !ownsEntry(e));
|
|
217
|
+
return { array: kept2, removed: array.length - kept2.length };
|
|
218
|
+
}
|
|
219
|
+
const kept = [];
|
|
220
|
+
let removed = 0;
|
|
221
|
+
for (const e of array) {
|
|
222
|
+
const r = stripInner(e);
|
|
223
|
+
removed += r.removed;
|
|
224
|
+
if (r.next !== null) kept.push(r.next);
|
|
225
|
+
}
|
|
226
|
+
return { array: kept, removed };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/adapters/base.ts
|
|
117
230
|
var AGENTS_MD_USER_PATHS = {
|
|
118
231
|
// codex resolves its own user path in-adapter ($CODEX_HOME with ~ expansion
|
|
119
232
|
// + the AGENTS.override.md shadow probe) — deliberately no row here.
|
|
@@ -735,51 +848,253 @@ var BaseAdapter = class {
|
|
|
735
848
|
`, "utf8");
|
|
736
849
|
}
|
|
737
850
|
/**
|
|
738
|
-
*
|
|
739
|
-
*
|
|
740
|
-
*
|
|
851
|
+
* True when a PRESENT rootKey value is not a plain object map (the user
|
|
852
|
+
* hand-edited it to an array / string / number / boolean). Assigning a server
|
|
853
|
+
* entry into such a value would corrupt the file silently (a named property on
|
|
854
|
+
* an Array is dropped by JSON.stringify) or throw under strict mode (a property
|
|
855
|
+
* write / `in` operator against a primitive), so callers warn-and-skip. A
|
|
856
|
+
* `null`/`undefined` value is NOT malformed — it is the well-formed "absent"
|
|
857
|
+
* case the helpers create fresh.
|
|
741
858
|
*/
|
|
742
|
-
|
|
743
|
-
|
|
859
|
+
isMalformedRootValue(value) {
|
|
860
|
+
return isMalformedRootValue(value);
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Warn-skip ChangeRecord when an existing JSON `hooks` config is present-but-
|
|
864
|
+
* malformed (root hand-edited to an array/primitive, or an existing event
|
|
865
|
+
* bucket is a non-array) — installing a hook into it would silently drop the
|
|
866
|
+
* entry (a named property on an Array is dropped by JSON.stringify) or throw
|
|
867
|
+
* under strict mode (a `.push`/property write against a primitive or non-array
|
|
868
|
+
* bucket). Returns null when the hooks config is well-formed, including the
|
|
869
|
+
* absent case the installers create fresh (proceed normally). Mirrors the
|
|
870
|
+
* server-root guard (isMalformedRootValue). All targeted hosts use the Claude-
|
|
871
|
+
* style `{ Event: Matcher[] }` shape, so every present event bucket must be an
|
|
872
|
+
* array; a non-array bucket is the same malformed class as a wrong-typed root.
|
|
873
|
+
*/
|
|
874
|
+
malformedHookRootSkip(configPath, hooksRoot) {
|
|
875
|
+
if (hooksRoot == null) return null;
|
|
876
|
+
if (this.isMalformedRootValue(hooksRoot)) {
|
|
744
877
|
return {
|
|
745
878
|
platform: this.id,
|
|
746
879
|
action: "warn",
|
|
747
880
|
path: configPath,
|
|
748
|
-
detail: `existing ${configPath} is not
|
|
881
|
+
detail: `existing "hooks" in ${configPath} is not an object map; left untouched (fix it, then re-run)`
|
|
749
882
|
};
|
|
750
883
|
}
|
|
751
|
-
const
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
bucket[serverId] = entry;
|
|
761
|
-
this.writeJson(configPath, cfg, dryRun);
|
|
884
|
+
for (const v of Object.values(hooksRoot)) {
|
|
885
|
+
if (v !== void 0 && !Array.isArray(v)) {
|
|
886
|
+
return {
|
|
887
|
+
platform: this.id,
|
|
888
|
+
action: "warn",
|
|
889
|
+
path: configPath,
|
|
890
|
+
detail: `existing "hooks" in ${configPath} has a non-array event bucket; left untouched (fix it, then re-run)`
|
|
891
|
+
};
|
|
892
|
+
}
|
|
762
893
|
}
|
|
763
|
-
return
|
|
894
|
+
return null;
|
|
764
895
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
896
|
+
// ── Shared object-map hook-array merge engine ────────────────────────────
|
|
897
|
+
// installHooks/uninstallHooks for every json-stdio host that stores hooks as
|
|
898
|
+
// `{ <event>: HookEntry[] }` share ONE create/update/skip + strip/remove
|
|
899
|
+
// orchestration. The host supplies a {@link HookMergeDescriptor} carrying
|
|
900
|
+
// EVERY observable string and ownership predicate — the engine body hardcodes
|
|
901
|
+
// none of them, so per-host wording (skip/remove/absent/no-match details) and
|
|
902
|
+
// FLAT-vs-NESTED removal stay host-controlled and never drift to a lowest-
|
|
903
|
+
// common-denominator phrasing the suites (which assert by action) would miss.
|
|
904
|
+
// The write-gate is the `mutated` bool, matching the migrated host exactly.
|
|
905
|
+
/**
|
|
906
|
+
* Install: upsert each `{ event, matcher }` in `pending` into the object-map
|
|
907
|
+
* `hooks` root at `configPath`. Reproduces the canonical host's loop: read
|
|
908
|
+
* `?? {}`, warn-skip on a malformed `hooks` root, then per pending item render
|
|
909
|
+
* the entry, find the owned slot, and create/update/skip. Writes once at the
|
|
910
|
+
* end iff anything mutated.
|
|
911
|
+
*
|
|
912
|
+
* Hosts that fold their `nativeHooks` surface into `pending` (claude-code) pass
|
|
913
|
+
* no `native` arg. Hosts that keep the native surface SEPARATE — verbatim
|
|
914
|
+
* event keys, EXACT-command ownership, a distinct `(native)` detail — pass it
|
|
915
|
+
* as the optional `native` list (cursor); that pass runs after the normalized
|
|
916
|
+
* loop and before the single write. Omitting it is byte-identical to before.
|
|
917
|
+
*/
|
|
918
|
+
/**
|
|
919
|
+
* Resolve the per-event hook bucket. With "coerce" policy a present-but-non-
|
|
920
|
+
* array bucket (hand-edited) is replaced by a fresh array; otherwise `??=` is
|
|
921
|
+
* used (the warn-skip path already rejected any non-array bucket via
|
|
922
|
+
* malformedHookRootSkip, so `??=` only ever fills the absent case).
|
|
923
|
+
*/
|
|
924
|
+
coerceHookBucket(hooks, event, coerce) {
|
|
925
|
+
if (coerce) {
|
|
926
|
+
return Array.isArray(hooks[event]) ? hooks[event] : hooks[event] = [];
|
|
927
|
+
}
|
|
928
|
+
return hooks[event] ??= [];
|
|
929
|
+
}
|
|
930
|
+
upsertHookEntries(ctx, configPath, pending, descriptor, native) {
|
|
931
|
+
const file = this.readJson(configPath) ?? {};
|
|
932
|
+
const coerce = descriptor.malformedPolicy === "coerce";
|
|
933
|
+
let hooks;
|
|
934
|
+
if (coerce) {
|
|
935
|
+
const existing = file.hooks;
|
|
936
|
+
hooks = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : file.hooks = {};
|
|
937
|
+
} else {
|
|
938
|
+
const skip = this.malformedHookRootSkip(configPath, file.hooks);
|
|
939
|
+
if (skip) return [skip];
|
|
940
|
+
hooks = file.hooks ??= {};
|
|
941
|
+
}
|
|
942
|
+
const changes = [];
|
|
943
|
+
let mutated = false;
|
|
944
|
+
for (const { event, matcher } of pending) {
|
|
945
|
+
const hostEvent = descriptor.mapEvent ? descriptor.mapEvent(event) : event;
|
|
946
|
+
if (hostEvent === void 0) {
|
|
947
|
+
changes.push({
|
|
948
|
+
platform: this.id,
|
|
949
|
+
action: "warn",
|
|
950
|
+
path: configPath,
|
|
951
|
+
detail: descriptor.unmappedWarnDetail(event)
|
|
952
|
+
});
|
|
953
|
+
continue;
|
|
954
|
+
}
|
|
955
|
+
const command = buildHomeBinHookCommand(
|
|
956
|
+
ctx.homeBinPath,
|
|
957
|
+
this.id,
|
|
958
|
+
event,
|
|
959
|
+
ctx.connector.id
|
|
960
|
+
);
|
|
961
|
+
const entry = descriptor.renderEntry(hostEvent, matcher, command);
|
|
962
|
+
const bucket = this.coerceHookBucket(hooks, hostEvent, coerce);
|
|
963
|
+
const r = upsertInArray(
|
|
964
|
+
bucket,
|
|
965
|
+
entry,
|
|
966
|
+
(e) => descriptor.entryOwnsCommand(e, command)
|
|
967
|
+
);
|
|
968
|
+
if (r.action !== "skip") {
|
|
969
|
+
hooks[hostEvent] = r.array;
|
|
970
|
+
mutated = true;
|
|
971
|
+
}
|
|
972
|
+
changes.push({
|
|
769
973
|
platform: this.id,
|
|
770
|
-
action:
|
|
974
|
+
action: r.action,
|
|
771
975
|
path: configPath,
|
|
772
|
-
detail:
|
|
773
|
-
};
|
|
976
|
+
detail: r.action === "skip" ? descriptor.skipDetail(hostEvent) : `hooks.${hostEvent}`
|
|
977
|
+
});
|
|
774
978
|
}
|
|
775
|
-
const
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
979
|
+
for (const { event, matcher } of native ?? []) {
|
|
980
|
+
const command = buildHomeBinHookCommand(
|
|
981
|
+
ctx.homeBinPath,
|
|
982
|
+
this.id,
|
|
983
|
+
event,
|
|
984
|
+
ctx.connector.id
|
|
985
|
+
);
|
|
986
|
+
const entry = descriptor.renderEntry(event, matcher, command);
|
|
987
|
+
const bucket = this.coerceHookBucket(hooks, event, coerce);
|
|
988
|
+
const r = upsertInArray(
|
|
989
|
+
bucket,
|
|
990
|
+
entry,
|
|
991
|
+
(e) => descriptor.nativeOwnsCommand(e, command)
|
|
992
|
+
);
|
|
993
|
+
if (r.action !== "skip") {
|
|
994
|
+
hooks[event] = r.array;
|
|
995
|
+
mutated = true;
|
|
996
|
+
}
|
|
997
|
+
changes.push({
|
|
998
|
+
platform: this.id,
|
|
999
|
+
action: r.action,
|
|
1000
|
+
path: configPath,
|
|
1001
|
+
detail: r.action === "skip" ? descriptor.nativeSkipDetail(event) : descriptor.nativeMutateDetail(event)
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
if (mutated) {
|
|
1005
|
+
descriptor.onMutate?.(file);
|
|
1006
|
+
this.writeJson(configPath, file, ctx.dryRun);
|
|
779
1007
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
1008
|
+
return changes;
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Uninstall: strip owned content from every event bucket in the object-map
|
|
1012
|
+
* `hooks` root at `configPath`. NESTED hosts pass `descriptor.stripInner`
|
|
1013
|
+
* (strip owned inner commands, drop emptied entries; `removed` = inner
|
|
1014
|
+
* commands); FLAT hosts omit it (drop whole owned entries). Reproduces the
|
|
1015
|
+
* canonical host: read (NO `?? {}`), `[skip]` (absentDetail) when no hooks
|
|
1016
|
+
* section, per-event remove → delete-or-replace bucket, write once iff
|
|
1017
|
+
* mutated, and a single `[skip]` (noMatchDetail) when nothing matched.
|
|
1018
|
+
*/
|
|
1019
|
+
removeHookEntries(ctx, configPath, descriptor) {
|
|
1020
|
+
const file = this.readJson(configPath);
|
|
1021
|
+
const hooks = file?.hooks;
|
|
1022
|
+
if (!file || !hooks) {
|
|
1023
|
+
return [
|
|
1024
|
+
{
|
|
1025
|
+
platform: this.id,
|
|
1026
|
+
action: "skip",
|
|
1027
|
+
path: configPath,
|
|
1028
|
+
detail: descriptor.absentDetail
|
|
1029
|
+
}
|
|
1030
|
+
];
|
|
1031
|
+
}
|
|
1032
|
+
const changes = [];
|
|
1033
|
+
let mutated = false;
|
|
1034
|
+
const stripInner = descriptor.stripInner?.(ctx);
|
|
1035
|
+
for (const event of descriptor.removeEventKeys ?? Object.keys(hooks)) {
|
|
1036
|
+
const bucket = hooks[event];
|
|
1037
|
+
if (!Array.isArray(bucket)) continue;
|
|
1038
|
+
const ownsEntry = descriptor.ownsEntryForRemove(ctx, event);
|
|
1039
|
+
const r = removeFromArray(bucket, ownsEntry, stripInner);
|
|
1040
|
+
if (r.removed > 0) {
|
|
1041
|
+
if (r.array.length > 0) hooks[event] = r.array;
|
|
1042
|
+
else delete hooks[event];
|
|
1043
|
+
changes.push({
|
|
1044
|
+
platform: this.id,
|
|
1045
|
+
action: "remove",
|
|
1046
|
+
path: configPath,
|
|
1047
|
+
detail: descriptor.removeDetail(event, r.removed)
|
|
1048
|
+
});
|
|
1049
|
+
mutated = true;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
if (mutated) this.writeJson(configPath, file, ctx.dryRun);
|
|
1053
|
+
if (changes.length === 0) {
|
|
1054
|
+
changes.push({
|
|
1055
|
+
platform: this.id,
|
|
1056
|
+
action: "skip",
|
|
1057
|
+
path: configPath,
|
|
1058
|
+
detail: descriptor.noMatchDetail
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
return changes;
|
|
1062
|
+
}
|
|
1063
|
+
/** Bind BaseAdapter's JSON leaf IO (readJson/writeJson + the unparseable
|
|
1064
|
+
* overwrite guard) as a Codec for the object-map engine. */
|
|
1065
|
+
jsonObjectMapCodec() {
|
|
1066
|
+
return {
|
|
1067
|
+
parse: (path) => this.readJson(path),
|
|
1068
|
+
serialize: (path, data, dryRun) => this.writeJson(path, data, dryRun),
|
|
1069
|
+
isPresentButUnparseable: (path) => this.isPresentButUnparseable(path)
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Upsert `entry` at `config[rootKey][serverId]` in a JSON file, creating the
|
|
1074
|
+
* file/object as needed. Returns a ChangeRecord describing create/update/skip.
|
|
1075
|
+
* Idempotent: an identical entry yields "skip".
|
|
1076
|
+
*/
|
|
1077
|
+
upsertServerInJson(configPath, rootKey, serverId, entry, dryRun = false) {
|
|
1078
|
+
return upsertInObjectMap({
|
|
1079
|
+
codec: this.jsonObjectMapCodec(),
|
|
1080
|
+
rootKey,
|
|
1081
|
+
platform: this.id,
|
|
1082
|
+
configPath,
|
|
1083
|
+
entryId: serverId,
|
|
1084
|
+
entry,
|
|
1085
|
+
dryRun
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
/** Remove `config[rootKey][serverId]` from a JSON file. */
|
|
1089
|
+
removeServerFromJson(configPath, rootKey, serverId, dryRun = false) {
|
|
1090
|
+
return removeFromObjectMap({
|
|
1091
|
+
codec: this.jsonObjectMapCodec(),
|
|
1092
|
+
rootKey,
|
|
1093
|
+
platform: this.id,
|
|
1094
|
+
configPath,
|
|
1095
|
+
entryId: serverId,
|
|
1096
|
+
dryRun
|
|
1097
|
+
});
|
|
783
1098
|
}
|
|
784
1099
|
// ── Backup ───────────────────────────────────────────────────────────────
|
|
785
1100
|
backupSettings(ctx) {
|
|
@@ -829,6 +1144,8 @@ var BaseAdapter = class {
|
|
|
829
1144
|
|
|
830
1145
|
export {
|
|
831
1146
|
parseJsonc,
|
|
1147
|
+
upsertInObjectMap,
|
|
1148
|
+
removeFromObjectMap,
|
|
832
1149
|
BaseAdapter
|
|
833
1150
|
};
|
|
834
|
-
//# sourceMappingURL=chunk-
|
|
1151
|
+
//# sourceMappingURL=chunk-KG6T3YNJ.js.map
|