@ken-jo/agent-connector 0.4.95 → 0.4.97
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/LICENSE +202 -202
- package/NOTICE +6 -6
- package/README.md +605 -599
- package/dist/{action-Q7GGO4PA.js → action-IPFDPOZU.js} +16 -16
- package/dist/action-IPFDPOZU.js.map +1 -0
- package/dist/{amazon-q-ETMAGY3D.js → amazon-q-IT44LEWV.js} +6 -6
- package/dist/amazon-q-IT44LEWV.js.map +1 -0
- package/dist/{amp-BQZOL5GR.js → amp-CEWOSWQO.js} +4 -4
- package/dist/amp-CEWOSWQO.js.map +1 -0
- package/dist/{amp-67BZONGF.js → amp-P35FRU2Z.js} +7 -7
- package/dist/amp-P35FRU2Z.js.map +1 -0
- package/dist/{antigravity-EA4L225L.js → antigravity-LHFEFNMC.js} +5 -5
- package/dist/antigravity-LHFEFNMC.js.map +1 -0
- package/dist/antigravity-TWIWXTQH.js +18 -0
- package/dist/{antigravity-cli-B2HXZFYQ.js → antigravity-cli-MOKFSWOU.js} +5 -5
- package/dist/antigravity-cli-MOKFSWOU.js.map +1 -0
- package/dist/{antigravity-cli-OM6SC3PE.js → antigravity-cli-MPE27LPI.js} +9 -9
- package/dist/antigravity-cli-MPE27LPI.js.map +1 -0
- package/dist/audit-3E5JAOP2.js +311 -0
- package/dist/audit-3E5JAOP2.js.map +1 -0
- package/dist/{chunk-PW42ZXCB.js → chunk-3EP5V2F4.js} +3 -3
- package/dist/chunk-3EP5V2F4.js.map +1 -0
- package/dist/{chunk-MGTDQLJJ.js → chunk-3FINQ2ZF.js} +4 -4
- package/dist/chunk-3FINQ2ZF.js.map +1 -0
- package/dist/{chunk-KHO5WNTP.js → chunk-4GH2KA7V.js} +1 -1
- package/dist/chunk-4GH2KA7V.js.map +1 -0
- package/dist/{chunk-2Y2MZRLP.js → chunk-7C2IPIIB.js} +3 -3
- package/dist/chunk-7C2IPIIB.js.map +1 -0
- package/dist/{chunk-NLE2J5P4.js → chunk-7HHW23AC.js} +29 -29
- package/dist/chunk-7HHW23AC.js.map +1 -0
- package/dist/{chunk-XD7MY6HB.js → chunk-7PSNXUFK.js} +1 -1
- package/dist/chunk-7PSNXUFK.js.map +1 -0
- package/dist/{chunk-5IOTP7LQ.js → chunk-7ZFK2WEO.js} +7 -5
- package/dist/chunk-7ZFK2WEO.js.map +1 -0
- package/dist/{chunk-XC5SJ35F.js → chunk-C63AAXK7.js} +5 -5
- package/dist/chunk-C63AAXK7.js.map +1 -0
- package/dist/{chunk-UM5S7DKK.js → chunk-C747DYNT.js} +1 -1
- package/dist/chunk-C747DYNT.js.map +1 -0
- package/dist/{chunk-YPAXJ7EW.js → chunk-FHYR5Q5T.js} +13 -13
- package/dist/chunk-FHYR5Q5T.js.map +1 -0
- package/dist/{chunk-I5UXHIDT.js → chunk-GCPEPUD7.js} +8 -8
- package/dist/chunk-GCPEPUD7.js.map +1 -0
- package/dist/{chunk-ISEJWBFF.js → chunk-GZZCHJDP.js} +2 -2
- package/dist/chunk-GZZCHJDP.js.map +1 -0
- package/dist/{chunk-UMQ6NBQR.js → chunk-ILWHLGIC.js} +2 -2
- package/dist/chunk-ILWHLGIC.js.map +1 -0
- package/dist/{chunk-IVAWP6HH.js → chunk-IOJDZJ72.js} +2 -2
- package/dist/chunk-IOJDZJ72.js.map +1 -0
- package/dist/{chunk-NBYKLUF6.js → chunk-KFPSJRHR.js} +2 -2
- package/dist/chunk-KFPSJRHR.js.map +1 -0
- package/dist/{chunk-O7SDRBVB.js → chunk-KM2YXO6V.js} +1 -1
- package/dist/chunk-KM2YXO6V.js.map +1 -0
- package/dist/{chunk-7F5KHLEP.js → chunk-L2C5UMKW.js} +4 -4
- package/dist/chunk-L2C5UMKW.js.map +1 -0
- package/dist/{chunk-OP4KBXUO.js → chunk-LFWYYWLV.js} +1 -1
- package/dist/chunk-LFWYYWLV.js.map +1 -0
- package/dist/{chunk-APZDTY76.js → chunk-LQ55E7QI.js} +7 -7
- package/dist/chunk-LQ55E7QI.js.map +1 -0
- package/dist/{chunk-PAPMUAR5.js → chunk-MLU7NDAZ.js} +12 -12
- package/dist/chunk-MLU7NDAZ.js.map +1 -0
- package/dist/{chunk-H33M77DD.js → chunk-N4ENLZEU.js} +22 -19
- package/dist/chunk-N4ENLZEU.js.map +1 -0
- package/dist/{chunk-D4ELPBB5.js → chunk-NDMNMAKP.js} +43 -43
- package/dist/chunk-NDMNMAKP.js.map +1 -0
- package/dist/{chunk-H53QZQJI.js → chunk-PSGTHJQU.js} +1 -1
- package/dist/chunk-PSGTHJQU.js.map +1 -0
- package/dist/{chunk-FBJQGRET.js → chunk-Q4ISQHUQ.js} +1 -1
- package/dist/chunk-Q4ISQHUQ.js.map +1 -0
- package/dist/{chunk-6TF7MHG5.js → chunk-Q7PR2WGT.js} +2 -2
- package/dist/chunk-Q7PR2WGT.js.map +1 -0
- package/dist/{chunk-GZGLCCEE.js → chunk-QYOUQCC6.js} +1 -1
- package/dist/chunk-QYOUQCC6.js.map +1 -0
- package/dist/{chunk-Z2MRSS2I.js → chunk-SLJI5BOM.js} +2 -2
- package/dist/chunk-SLJI5BOM.js.map +1 -0
- package/dist/{chunk-JKKU2WKG.js → chunk-TGZFDXNH.js} +4 -4
- package/dist/chunk-TGZFDXNH.js.map +1 -0
- package/dist/{chunk-FJCUKXGX.js → chunk-THI4PI4H.js} +2 -2
- package/dist/chunk-THI4PI4H.js.map +1 -0
- package/dist/{chunk-MQPNU2CY.js → chunk-UZZTWWGG.js} +1 -1
- package/dist/chunk-UZZTWWGG.js.map +1 -0
- package/dist/{chunk-3NH2ARIS.js → chunk-WPTNMYYM.js} +2 -2
- package/dist/chunk-WPTNMYYM.js.map +1 -0
- package/dist/{chunk-OPXAP3AC.js → chunk-XN7S44U4.js} +1 -1
- package/dist/chunk-XN7S44U4.js.map +1 -0
- package/dist/{chunk-6WZODR3Z.js → chunk-XVEJAFKW.js} +5 -5
- package/dist/chunk-XVEJAFKW.js.map +1 -0
- package/dist/{chunk-BEFVJ2OE.js → chunk-XXLCMSD3.js} +1 -1
- package/dist/chunk-XXLCMSD3.js.map +1 -0
- package/dist/{chunk-YT43MUSJ.js → chunk-XZWMJLIT.js} +1 -1
- package/dist/chunk-XZWMJLIT.js.map +1 -0
- package/dist/{chunk-4NHXLXSD.js → chunk-Y3K6KJMS.js} +2 -2
- package/dist/chunk-Y3K6KJMS.js.map +1 -0
- package/dist/{chunk-RK6VBLXP.js → chunk-ZSOS2KOR.js} +1 -1
- package/dist/chunk-ZSOS2KOR.js.map +1 -0
- package/dist/{claude-code-NMRKTAP7.js → claude-code-FS42PXZO.js} +4 -4
- package/dist/claude-code-FS42PXZO.js.map +1 -0
- package/dist/{claude-code-GRWO6XBZ.js → claude-code-QBWDT5RT.js} +11 -11
- package/dist/claude-code-QBWDT5RT.js.map +1 -0
- package/dist/cli/sdk.js +9 -8
- package/dist/cli/sdk.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/{cline-25KW25IN.js → cline-JKEAYWVR.js} +6 -6
- package/dist/cline-JKEAYWVR.js.map +1 -0
- package/dist/{codebuddy-ZNYQACMF.js → codebuddy-EB6EYPC3.js} +7 -7
- package/dist/codebuddy-EB6EYPC3.js.map +1 -0
- package/dist/{codebuff-VR6JUDIY.js → codebuff-5RSXFU6G.js} +6 -6
- package/dist/codebuff-5RSXFU6G.js.map +1 -0
- package/dist/{codebuff-ETYT5UBL.js → codebuff-6KWBW6CT.js} +4 -4
- package/dist/codebuff-6KWBW6CT.js.map +1 -0
- package/dist/{codex-CPNKTXG3.js → codex-ART46CDD.js} +9 -9
- package/dist/codex-ART46CDD.js.map +1 -0
- package/dist/{codex-YAORPCOD.js → codex-DKBAAUCO.js} +4 -4
- package/dist/codex-DKBAAUCO.js.map +1 -0
- package/dist/{continue-WU2IB5KS.js → continue-JHZCMSPH.js} +8 -8
- package/dist/continue-JHZCMSPH.js.map +1 -0
- package/dist/{copilot-cli-MZTAWMPW.js → copilot-cli-CLBJACQ5.js} +4 -4
- package/dist/copilot-cli-CLBJACQ5.js.map +1 -0
- package/dist/{copilot-cli-PAP377OM.js → copilot-cli-QVAU5CFM.js} +7 -7
- package/dist/copilot-cli-QVAU5CFM.js.map +1 -0
- package/dist/{crush-3AVQA4YU.js → crush-7TORBYUL.js} +7 -7
- package/dist/crush-7TORBYUL.js.map +1 -0
- package/dist/{crush-QBXLNN7T.js → crush-A5C5USRL.js} +4 -4
- package/dist/crush-A5C5USRL.js.map +1 -0
- package/dist/{cursor-DIW43TRZ.js → cursor-D5SWDVSW.js} +3 -3
- package/dist/cursor-D5SWDVSW.js.map +1 -0
- package/dist/cursor-W7I4QUFO.js +22 -0
- package/dist/{detect-6V4LC3MA.js → detect-JHSA3G3U.js} +4 -4
- package/dist/detect-JHSA3G3U.js.map +1 -0
- package/dist/{devin-QMEM3OFA.js → devin-SSR7S4WX.js} +7 -7
- package/dist/devin-SSR7S4WX.js.map +1 -0
- package/dist/{doctor-2YDUVSAU.js → doctor-CQJ7TZPA.js} +26 -26
- package/dist/doctor-CQJ7TZPA.js.map +1 -0
- package/dist/{droid-MOKV53J4.js → droid-45PJ7JGF.js} +7 -7
- package/dist/droid-45PJ7JGF.js.map +1 -0
- package/dist/{droid-U3ZBLQFO.js → droid-PFQ2PZSF.js} +4 -4
- package/dist/droid-PFQ2PZSF.js.map +1 -0
- package/dist/{gemini-cli-47ZHCOZR.js → gemini-cli-24PFDK7D.js} +8 -8
- package/dist/gemini-cli-24PFDK7D.js.map +1 -0
- package/dist/{gemini-cli-NMVVBO3K.js → gemini-cli-Y6RJMU24.js} +4 -4
- package/dist/gemini-cli-Y6RJMU24.js.map +1 -0
- package/dist/{goose-64V46Y2W.js → goose-K2HKUTBK.js} +4 -4
- package/dist/goose-K2HKUTBK.js.map +1 -0
- package/dist/{goose-4EI5MYTI.js → goose-U5D3LCPC.js} +9 -9
- package/dist/goose-U5D3LCPC.js.map +1 -0
- package/dist/{grok-cli-QYQPDSVB.js → grok-cli-L2MHAPBY.js} +7 -7
- package/dist/grok-cli-L2MHAPBY.js.map +1 -0
- package/dist/{hermes-F75OSK3Y.js → hermes-2S5JUTTJ.js} +7 -7
- package/dist/hermes-2S5JUTTJ.js.map +1 -0
- package/dist/{hermes-F3EGTK6N.js → hermes-MF3NX4ZY.js} +4 -4
- package/dist/hermes-MF3NX4ZY.js.map +1 -0
- package/dist/{hook-OEVRDMXD.js → hook-VHICXHXL.js} +16 -16
- package/dist/hook-VHICXHXL.js.map +1 -0
- package/dist/index.js +5 -5
- package/dist/{install-5NMCUDG5.js → install-WLENR4KF.js} +192 -28
- package/dist/install-WLENR4KF.js.map +1 -0
- package/dist/{jetbrains-copilot-ZGIWI6YV.js → jetbrains-copilot-Z4FC3RM3.js} +6 -6
- package/dist/jetbrains-copilot-Z4FC3RM3.js.map +1 -0
- package/dist/{junie-DIFN2P22.js → junie-ULGJJIHB.js} +6 -6
- package/dist/junie-ULGJJIHB.js.map +1 -0
- package/dist/{kilo-SW7BH6EG.js → kilo-SMHPOHJ5.js} +4 -4
- package/dist/kilo-SMHPOHJ5.js.map +1 -0
- package/dist/{kilo-EMZJBEBB.js → kilo-WWIJOVWD.js} +8 -8
- package/dist/kilo-WWIJOVWD.js.map +1 -0
- package/dist/{kilo-cli-S3GXBPAE.js → kilo-cli-QKE2EQLS.js} +8 -8
- package/dist/kilo-cli-QKE2EQLS.js.map +1 -0
- package/dist/{kilo-cli-ZF272ZHN.js → kilo-cli-T2SRRRJS.js} +5 -5
- package/dist/kilo-cli-T2SRRRJS.js.map +1 -0
- package/dist/{kimi-IQJOIY6B.js → kimi-JMUFTVSF.js} +7 -7
- package/dist/kimi-JMUFTVSF.js.map +1 -0
- package/dist/{kimi-5FHEXGZD.js → kimi-MSIDEMCI.js} +4 -4
- package/dist/kimi-MSIDEMCI.js.map +1 -0
- package/dist/{kiro-WGOI5CGT.js → kiro-NYRRBIKW.js} +4 -4
- package/dist/kiro-NYRRBIKW.js.map +1 -0
- package/dist/{kiro-BYX4HEAJ.js → kiro-UWUCRKR3.js} +7 -7
- package/dist/kiro-UWUCRKR3.js.map +1 -0
- package/dist/{leaderboard-XEKS7B3H.js → leaderboard-NJWKGMXP.js} +7 -7
- package/dist/leaderboard-NJWKGMXP.js.map +1 -0
- package/dist/{mimo-code-7R53QDHB.js → mimo-code-BPNYYIVM.js} +7 -7
- package/dist/mimo-code-BPNYYIVM.js.map +1 -0
- package/dist/{mistral-vibe-LHJ55FSB.js → mistral-vibe-LT3DAJ3L.js} +6 -6
- package/dist/mistral-vibe-LT3DAJ3L.js.map +1 -0
- package/dist/{mux-XM6AP35L.js → mux-7PIJ7HUQ.js} +4 -4
- package/dist/mux-7PIJ7HUQ.js.map +1 -0
- package/dist/{mux-NYCNGL43.js → mux-AHNUAY5Z.js} +6 -6
- package/dist/mux-AHNUAY5Z.js.map +1 -0
- package/dist/{nemoclaw-WSIPSE7U.js → nemoclaw-RE5RZASZ.js} +7 -7
- package/dist/nemoclaw-RE5RZASZ.js.map +1 -0
- package/dist/{omp-562NK5BJ.js → omp-D2EXEPH7.js} +7 -7
- package/dist/omp-D2EXEPH7.js.map +1 -0
- package/dist/{open-interpreter-I2CNOEXY.js → open-interpreter-SSGBEUKX.js} +6 -6
- package/dist/open-interpreter-SSGBEUKX.js.map +1 -0
- package/dist/openclaw-IJ33K45M.js +17 -0
- package/dist/{openclaw-5EQFGOWL.js → openclaw-KAFTNLR2.js} +4 -4
- package/dist/openclaw-KAFTNLR2.js.map +1 -0
- package/dist/{opencode-YS4WX2E2.js → opencode-YKUKZY3J.js} +7 -7
- package/dist/opencode-YKUKZY3J.js.map +1 -0
- package/dist/{opencode-QGP4ALQY.js → opencode-Z65JYILL.js} +4 -4
- package/dist/opencode-Z65JYILL.js.map +1 -0
- package/dist/{openhands-QMYIKNOS.js → openhands-SEZMCUC3.js} +7 -7
- package/dist/openhands-SEZMCUC3.js.map +1 -0
- package/dist/{package-G25GVTH2.js → package-JECVQEFT.js} +15 -15
- package/dist/package-JECVQEFT.js.map +1 -0
- package/dist/{pi-GGDQREVG.js → pi-DLKCPOJE.js} +4 -4
- package/dist/pi-DLKCPOJE.js.map +1 -0
- package/dist/{pi-644FIN3A.js → pi-YPYV2ULH.js} +6 -6
- package/dist/pi-YPYV2ULH.js.map +1 -0
- package/dist/{qwen-code-GQILBJ5B.js → qwen-code-26CSFDX6.js} +4 -4
- package/dist/qwen-code-26CSFDX6.js.map +1 -0
- package/dist/{qwen-code-STDMX7EM.js → qwen-code-ZHCLCYMC.js} +9 -9
- package/dist/qwen-code-ZHCLCYMC.js.map +1 -0
- package/dist/{roo-code-DLPIHHQ6.js → roo-code-7F4HYB34.js} +6 -6
- package/dist/roo-code-7F4HYB34.js.map +1 -0
- package/dist/{roo-code-SUWI4BBN.js → roo-code-HSAE2CJU.js} +4 -4
- package/dist/roo-code-HSAE2CJU.js.map +1 -0
- package/dist/runtime/index.js +14 -14
- package/dist/sdk/index.js +6 -6
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/test.js +11 -11
- package/dist/{serve-XTMIUHKK.js → serve-LYSBH43G.js} +16 -16
- package/dist/serve-LYSBH43G.js.map +1 -0
- package/dist/{status-VJAGO4Z7.js → status-PHKKGEW5.js} +11 -11
- package/dist/status-PHKKGEW5.js.map +1 -0
- package/dist/{statusline-EY2E2UGF.js → statusline-ZDD4OZ4F.js} +15 -15
- package/dist/statusline-ZDD4OZ4F.js.map +1 -0
- package/dist/{synthetic-6FRA7P5Q.js → synthetic-LUT6V6SD.js} +4 -4
- package/dist/synthetic-LUT6V6SD.js.map +1 -0
- package/dist/{telemetry-OLZQBADT.js → telemetry-HDGMI2BK.js} +10 -10
- package/dist/telemetry-HDGMI2BK.js.map +1 -0
- package/dist/{trae-CPPYASR2.js → trae-SIQI3C66.js} +4 -4
- package/dist/trae-SIQI3C66.js.map +1 -0
- package/dist/{trae-UOYD6JZZ.js → trae-Y67HSDOW.js} +6 -6
- package/dist/trae-Y67HSDOW.js.map +1 -0
- package/dist/{uninstall-AZEUPAND.js → uninstall-RWO2GM2I.js} +21 -21
- package/dist/uninstall-RWO2GM2I.js.map +1 -0
- package/dist/{upgrade-CAUUHSSC.js → upgrade-YJCXNUEE.js} +21 -21
- package/dist/upgrade-YJCXNUEE.js.map +1 -0
- package/dist/{usage-3KHZ4RYZ.js → usage-IWO4RQN6.js} +4 -4
- package/dist/usage-IWO4RQN6.js.map +1 -0
- package/dist/{usage-event-TZWOZPRJ.js → usage-event-AFFKCLHE.js} +15 -15
- package/dist/usage-event-AFFKCLHE.js.map +1 -0
- package/dist/{vscode-copilot-NIMH2DGU.js → vscode-copilot-5CI5B3X6.js} +7 -7
- package/dist/vscode-copilot-5CI5B3X6.js.map +1 -0
- package/dist/{warp-NRBBJQR2.js → warp-5YBSP7FP.js} +4 -4
- package/dist/warp-5YBSP7FP.js.map +1 -0
- package/dist/{warp-QYJGKURM.js → warp-INAKVGBV.js} +6 -6
- package/dist/warp-INAKVGBV.js.map +1 -0
- package/dist/{windsurf-75LLZ37C.js → windsurf-4HM57QAY.js} +6 -6
- package/dist/windsurf-4HM57QAY.js.map +1 -0
- package/dist/{zed-XJFOOKCO.js → zed-XXCNYOIN.js} +4 -4
- package/dist/zed-XXCNYOIN.js.map +1 -0
- package/dist/{zed-HKAZXEJW.js → zed-YN5X37PQ.js} +7 -7
- package/dist/zed-YN5X37PQ.js.map +1 -0
- package/package.json +92 -88
- package/dist/action-Q7GGO4PA.js.map +0 -1
- package/dist/amazon-q-ETMAGY3D.js.map +0 -1
- package/dist/amp-67BZONGF.js.map +0 -1
- package/dist/amp-BQZOL5GR.js.map +0 -1
- package/dist/antigravity-EA4L225L.js.map +0 -1
- package/dist/antigravity-VXLDLTO7.js +0 -18
- package/dist/antigravity-cli-B2HXZFYQ.js.map +0 -1
- package/dist/antigravity-cli-OM6SC3PE.js.map +0 -1
- package/dist/chunk-2Y2MZRLP.js.map +0 -1
- package/dist/chunk-3NH2ARIS.js.map +0 -1
- package/dist/chunk-4NHXLXSD.js.map +0 -1
- package/dist/chunk-5IOTP7LQ.js.map +0 -1
- package/dist/chunk-6TF7MHG5.js.map +0 -1
- package/dist/chunk-6WZODR3Z.js.map +0 -1
- package/dist/chunk-7F5KHLEP.js.map +0 -1
- package/dist/chunk-APZDTY76.js.map +0 -1
- package/dist/chunk-BEFVJ2OE.js.map +0 -1
- package/dist/chunk-D4ELPBB5.js.map +0 -1
- package/dist/chunk-FBJQGRET.js.map +0 -1
- package/dist/chunk-FJCUKXGX.js.map +0 -1
- package/dist/chunk-GZGLCCEE.js.map +0 -1
- package/dist/chunk-H33M77DD.js.map +0 -1
- package/dist/chunk-H53QZQJI.js.map +0 -1
- package/dist/chunk-I5UXHIDT.js.map +0 -1
- package/dist/chunk-ISEJWBFF.js.map +0 -1
- package/dist/chunk-IVAWP6HH.js.map +0 -1
- package/dist/chunk-JKKU2WKG.js.map +0 -1
- package/dist/chunk-KHO5WNTP.js.map +0 -1
- package/dist/chunk-MGTDQLJJ.js.map +0 -1
- package/dist/chunk-MQPNU2CY.js.map +0 -1
- package/dist/chunk-NBYKLUF6.js.map +0 -1
- package/dist/chunk-NLE2J5P4.js.map +0 -1
- package/dist/chunk-O7SDRBVB.js.map +0 -1
- package/dist/chunk-OP4KBXUO.js.map +0 -1
- package/dist/chunk-OPXAP3AC.js.map +0 -1
- package/dist/chunk-PAPMUAR5.js.map +0 -1
- package/dist/chunk-PW42ZXCB.js.map +0 -1
- package/dist/chunk-RK6VBLXP.js.map +0 -1
- package/dist/chunk-UM5S7DKK.js.map +0 -1
- package/dist/chunk-UMQ6NBQR.js.map +0 -1
- package/dist/chunk-XC5SJ35F.js.map +0 -1
- package/dist/chunk-XD7MY6HB.js.map +0 -1
- package/dist/chunk-YPAXJ7EW.js.map +0 -1
- package/dist/chunk-YT43MUSJ.js.map +0 -1
- package/dist/chunk-Z2MRSS2I.js.map +0 -1
- package/dist/claude-code-GRWO6XBZ.js.map +0 -1
- package/dist/claude-code-NMRKTAP7.js.map +0 -1
- package/dist/cline-25KW25IN.js.map +0 -1
- package/dist/codebuddy-ZNYQACMF.js.map +0 -1
- package/dist/codebuff-ETYT5UBL.js.map +0 -1
- package/dist/codebuff-VR6JUDIY.js.map +0 -1
- package/dist/codex-CPNKTXG3.js.map +0 -1
- package/dist/codex-YAORPCOD.js.map +0 -1
- package/dist/continue-WU2IB5KS.js.map +0 -1
- package/dist/copilot-cli-MZTAWMPW.js.map +0 -1
- package/dist/copilot-cli-PAP377OM.js.map +0 -1
- package/dist/crush-3AVQA4YU.js.map +0 -1
- package/dist/crush-QBXLNN7T.js.map +0 -1
- package/dist/cursor-DIW43TRZ.js.map +0 -1
- package/dist/cursor-GSLUET3V.js +0 -22
- package/dist/detect-6V4LC3MA.js.map +0 -1
- package/dist/devin-QMEM3OFA.js.map +0 -1
- package/dist/doctor-2YDUVSAU.js.map +0 -1
- package/dist/droid-MOKV53J4.js.map +0 -1
- package/dist/droid-U3ZBLQFO.js.map +0 -1
- package/dist/gemini-cli-47ZHCOZR.js.map +0 -1
- package/dist/gemini-cli-NMVVBO3K.js.map +0 -1
- package/dist/goose-4EI5MYTI.js.map +0 -1
- package/dist/goose-64V46Y2W.js.map +0 -1
- package/dist/grok-cli-QYQPDSVB.js.map +0 -1
- package/dist/hermes-F3EGTK6N.js.map +0 -1
- package/dist/hermes-F75OSK3Y.js.map +0 -1
- package/dist/hook-OEVRDMXD.js.map +0 -1
- package/dist/install-5NMCUDG5.js.map +0 -1
- package/dist/jetbrains-copilot-ZGIWI6YV.js.map +0 -1
- package/dist/junie-DIFN2P22.js.map +0 -1
- package/dist/kilo-EMZJBEBB.js.map +0 -1
- package/dist/kilo-SW7BH6EG.js.map +0 -1
- package/dist/kilo-cli-S3GXBPAE.js.map +0 -1
- package/dist/kilo-cli-ZF272ZHN.js.map +0 -1
- package/dist/kimi-5FHEXGZD.js.map +0 -1
- package/dist/kimi-IQJOIY6B.js.map +0 -1
- package/dist/kiro-BYX4HEAJ.js.map +0 -1
- package/dist/kiro-WGOI5CGT.js.map +0 -1
- package/dist/leaderboard-XEKS7B3H.js.map +0 -1
- package/dist/mimo-code-7R53QDHB.js.map +0 -1
- package/dist/mistral-vibe-LHJ55FSB.js.map +0 -1
- package/dist/mux-NYCNGL43.js.map +0 -1
- package/dist/mux-XM6AP35L.js.map +0 -1
- package/dist/nemoclaw-WSIPSE7U.js.map +0 -1
- package/dist/omp-562NK5BJ.js.map +0 -1
- package/dist/open-interpreter-I2CNOEXY.js.map +0 -1
- package/dist/openclaw-5EQFGOWL.js.map +0 -1
- package/dist/openclaw-7EYBQS4R.js +0 -17
- package/dist/opencode-QGP4ALQY.js.map +0 -1
- package/dist/opencode-YS4WX2E2.js.map +0 -1
- package/dist/openhands-QMYIKNOS.js.map +0 -1
- package/dist/package-G25GVTH2.js.map +0 -1
- package/dist/pi-644FIN3A.js.map +0 -1
- package/dist/pi-GGDQREVG.js.map +0 -1
- package/dist/qwen-code-GQILBJ5B.js.map +0 -1
- package/dist/qwen-code-STDMX7EM.js.map +0 -1
- package/dist/roo-code-DLPIHHQ6.js.map +0 -1
- package/dist/roo-code-SUWI4BBN.js.map +0 -1
- package/dist/serve-XTMIUHKK.js.map +0 -1
- package/dist/status-VJAGO4Z7.js.map +0 -1
- package/dist/statusline-EY2E2UGF.js.map +0 -1
- package/dist/synthetic-6FRA7P5Q.js.map +0 -1
- package/dist/telemetry-OLZQBADT.js.map +0 -1
- package/dist/trae-CPPYASR2.js.map +0 -1
- package/dist/trae-UOYD6JZZ.js.map +0 -1
- package/dist/uninstall-AZEUPAND.js.map +0 -1
- package/dist/upgrade-CAUUHSSC.js.map +0 -1
- package/dist/usage-3KHZ4RYZ.js.map +0 -1
- package/dist/usage-event-TZWOZPRJ.js.map +0 -1
- package/dist/vscode-copilot-NIMH2DGU.js.map +0 -1
- package/dist/warp-NRBBJQR2.js.map +0 -1
- package/dist/warp-QYJGKURM.js.map +0 -1
- package/dist/windsurf-75LLZ37C.js.map +0 -1
- package/dist/zed-HKAZXEJW.js.map +0 -1
- package/dist/zed-XJFOOKCO.js.map +0 -1
- /package/dist/{antigravity-VXLDLTO7.js.map → antigravity-TWIWXTQH.js.map} +0 -0
- /package/dist/{cursor-GSLUET3V.js.map → cursor-W7I4QUFO.js.map} +0 -0
- /package/dist/{openclaw-7EYBQS4R.js.map → openclaw-IJ33K45M.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/claude-code/wire.ts"],"sourcesContent":["/**\r\n * adapters/claude-code/wire — Claude Code hook wire protocol.\r\n *\r\n * Claude Code pipes a JSON object to a hook command on stdin and reads a JSON\r\n * object (or empty) from stdout, interpreting the exit code as well. This module\r\n * captures the exact stdin shape and the canonical reply shape so the adapter's\r\n * parse/format methods are a single source of truth.\r\n *\r\n * Stdin (documented Claude Code hook input):\r\n * {\r\n * session_id, transcript_path, cwd, hook_event_name,\r\n * tool_name, tool_input, tool_response, // tool events\r\n * permission_suggestions, // PermissionRequest\r\n * tool_use_id, error, is_interrupt, duration_ms, // PostToolUseFailure\r\n * agent_id, agent_type, // SubagentStart/Stop\r\n * agent_transcript_path, last_assistant_message, // SubagentStop\r\n * source, // SessionStart\r\n * reason, // SessionEnd\r\n * prompt, // UserPromptSubmit\r\n * trigger, // PreCompact\r\n * stop_hook_active, // Stop / SubagentStop\r\n * message // Notification\r\n * }\r\n *\r\n * Stdout (canonical reply): a `hookSpecificOutput` object keyed by\r\n * `hookEventName`, carrying `permissionDecision` (allow|deny|ask) +\r\n * `permissionDecisionReason`, `additionalContext`, and (PreToolUse)\r\n * `updatedInput`; PermissionRequest instead takes a nested\r\n * `decision: { behavior: \"allow\"|\"deny\", ... }`; Stop-class blocks\r\n * (Stop / SubagentStop / UserPromptSubmit / PostToolUse) take the TOP-LEVEL\r\n * `{\"decision\":\"block\",\"reason\"}`. Exit 0 = proceed; the JSON refines the\r\n * decision.\r\n */\r\n\r\nimport type { SessionStartEvent } from \"../../core/types.js\";\r\n\r\n/** Canonical Claude Code hook event names (the values of `hook_event_name`). */\r\nexport const CLAUDE_HOOK_EVENTS = [\r\n \"PreToolUse\",\r\n \"PostToolUse\",\r\n \"PreCompact\",\r\n \"SessionStart\",\r\n \"SessionEnd\",\r\n \"UserPromptSubmit\",\r\n \"Stop\",\r\n \"Notification\",\r\n \"PermissionRequest\",\r\n \"PostToolUseFailure\",\r\n \"SubagentStart\",\r\n \"SubagentStop\",\r\n] as const;\r\n\r\nexport type ClaudeHookEvent = (typeof CLAUDE_HOOK_EVENTS)[number];\r\n\r\n/**\r\n * Raw Claude Code hook stdin payload. Every field is optional — a given event\r\n * only populates the subset relevant to it. `connector` is NOT a Claude field;\r\n * it is threaded in by the universal entrypoint so parseEvent can recover the\r\n * connector id (the home binary passes `--connector <id>` and may inject it).\r\n */\r\nexport interface ClaudeWireInput {\r\n session_id?: string;\r\n transcript_path?: string;\r\n cwd?: string;\r\n hook_event_name?: string;\r\n\r\n // tool events\r\n tool_name?: string;\r\n tool_input?: Record<string, unknown>;\r\n /** PostToolUse result payload (string or structured). */\r\n tool_response?: unknown;\r\n\r\n // PermissionRequest — permission-update entries the dialog would offer.\r\n // NOTE: the docs explicitly state PermissionRequest has NO tool_use_id.\r\n permission_suggestions?: unknown[];\r\n\r\n // PostToolUseFailure\r\n tool_use_id?: string;\r\n error?: string;\r\n is_interrupt?: boolean;\r\n duration_ms?: number;\r\n\r\n // SubagentStart / SubagentStop — agent_type is unreliable on SubagentStop\r\n // (the SDK may not populate it); treat both as optional everywhere.\r\n agent_id?: string;\r\n agent_type?: string;\r\n // SubagentStop — the subagent's OWN transcript (transcript_path stays the\r\n // parent session's) + the text of its final response.\r\n agent_transcript_path?: string;\r\n last_assistant_message?: string;\r\n\r\n // SessionStart\r\n source?: string;\r\n // SessionEnd\r\n reason?: string;\r\n // UserPromptSubmit\r\n prompt?: string;\r\n // PreCompact\r\n trigger?: string;\r\n // Stop / SubagentStop\r\n stop_hook_active?: boolean;\r\n // Notification\r\n message?: string;\r\n\r\n /** Injected by the entrypoint so the runtime knows which connector to dispatch. */\r\n connector?: string;\r\n}\r\n\r\n/**\r\n * Extract a stable session id from a Claude wire payload.\r\n * Priority mirrors the proven adapter: transcript UUID > session_id > \"\".\r\n * (The framework's normalized event uses \"\" when no id is available, per\r\n * core/types BaseEvent.sessionId docstring — no ppid fabrication here.)\r\n */\r\nexport function extractSessionId(input: ClaudeWireInput): string {\r\n if (typeof input.transcript_path === \"string\") {\r\n const m = input.transcript_path.match(/([a-f0-9-]{36})\\.jsonl$/);\r\n if (m && m[1]) return m[1];\r\n }\r\n if (typeof input.session_id === \"string\" && input.session_id !== \"\") {\r\n return input.session_id;\r\n }\r\n return \"\";\r\n}\r\n\r\n/** Coerce a Claude PostToolUse `tool_response` into a string for the normalized event. */\r\nexport function toolResponseToString(value: unknown): string | undefined {\r\n if (value == null) return undefined;\r\n if (typeof value === \"string\") return value;\r\n try {\r\n return JSON.stringify(value);\r\n } catch {\r\n return String(value);\r\n }\r\n}\r\n\r\n/** Map a host's raw session-start \"source\" to the canonical SessionStart source. */\r\nexport function normalizeSessionSource(source: string | undefined): SessionStartEvent[\"source\"] {\r\n switch (source) {\r\n case \"compact\":\r\n return \"compact\";\r\n case \"resume\":\r\n return \"resume\";\r\n case \"clear\":\r\n return \"clear\";\r\n default:\r\n return \"startup\";\r\n }\r\n}\r\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":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/ts-plugin-bridge.ts"],"sourcesContent":["/**\r\n * Pure string-builder for the byte-identical HEAD of every ts-plugin host's\r\n * generated plugin module: the `HOME_BIN`/`CONNECTOR_ID` consts plus the\r\n * cross-OS `bridge(event, payload)` entrypoint (win32 `execSync`-quoted vs\r\n * POSIX `execFileSync`, fail-open `JSON.parse`).\r\n *\r\n * This module emits SOURCE TEXT only — it imports nothing at runtime and is\r\n * never loaded by a generated plugin. The 6 adopting hosts (amp, kilo,\r\n * kilo-cli, mimo-code, omp, opencode) differ in this region by exactly four\r\n * host-specific tokens, captured below. Changing the emitted bytes here changes\r\n * every host's generated plugin in lockstep; each host's adapter suite runs the\r\n * generated plugin live and asserts source substrings, so it is the byte oracle.\r\n */\r\n\r\nexport interface BridgePreludeOptions {\r\n /** Absolute home-bin path (raw — JSON.stringified inside). */\r\n homeBin: string;\r\n /** Connector id (raw — JSON.stringified inside). */\r\n connectorId: string;\r\n /** Host hook subcommand slug used in the `hook <slug>` argv (e.g. \"opencode\", \"amp\"). */\r\n hookSlug: string;\r\n /** Host noun for the jsdoc `@param payload <noun>-shaped …` line (e.g. \"OpenCode\", \"amp\", \"OMP\"). */\r\n payloadNoun: string;\r\n /** Optional suffix on the jsdoc `@param event canonical event name<eventDoc>` line (e.g. \" (PreToolUse|PostToolUse|SessionStart)\"). Default \"\". */\r\n eventDoc?: string;\r\n /** The exact fail-open catch-comment line (host wording differs). */\r\n failOpenComment: string;\r\n}\r\n\r\n/**\r\n * Render the HOME_BIN/CONNECTOR_ID consts + the universal-entrypoint bridge() —\r\n * the byte-identical head of every ts-plugin host's generated plugin module.\r\n */\r\nexport function renderBridgePrelude(opts: BridgePreludeOptions): string {\r\n return `const HOME_BIN = ${JSON.stringify(opts.homeBin)};\r\nconst CONNECTOR_ID = ${JSON.stringify(opts.connectorId)};\r\n\r\n/**\r\n * Invoke the universal hook entrypoint for one event.\r\n * @param {string} event canonical event name${opts.eventDoc ?? \"\"}\r\n * @param {object} payload ${opts.payloadNoun}-shaped payload posted on stdin\r\n * @returns {object|null} normalized HookResponse, or null on any failure\r\n */\r\nfunction bridge(event, payload) {\r\n try {\r\n // On Windows HOME_BIN is the agent-connector.cmd launcher: Node cannot\r\n // execFile a batch file, and shell+args is deprecated (DEP0190), so run one\r\n // quoted command line via a shell. POSIX keeps the direct execFile (no shell).\r\n const args = [\"hook\", ${JSON.stringify(opts.hookSlug)}, event, \"--connector\", CONNECTOR_ID];\r\n const opts = { input: JSON.stringify(payload), encoding: \"utf8\" };\r\n const stdout =\r\n process.platform === \"win32\"\r\n ? execSync([HOME_BIN, ...args].map((a) => '\"' + a + '\"').join(\" \"), opts)\r\n : execFileSync(HOME_BIN, args, opts);\r\n const text = (stdout || \"\").trim();\r\n if (text === \"\") return { decision: \"allow\" };\r\n return JSON.parse(text);\r\n } catch {\r\n // ${opts.failOpenComment}\r\n return null;\r\n }\r\n}`;\r\n}\r\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":[]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
inferModelFamily,
|
|
5
5
|
openStore,
|
|
6
6
|
worstConfidence
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-IOJDZJ72.js";
|
|
8
8
|
|
|
9
9
|
// src/telemetry/surface-footprint.ts
|
|
10
10
|
function countParts(parts, family, tok) {
|
|
@@ -590,4 +590,4 @@ export {
|
|
|
590
590
|
formatHostNativeTurns,
|
|
591
591
|
formatSurfaceLeaderboard
|
|
592
592
|
};
|
|
593
|
-
//# sourceMappingURL=chunk-
|
|
593
|
+
//# sourceMappingURL=chunk-Q7PR2WGT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/telemetry/surface-footprint.ts","../src/telemetry/leaderboard.ts"],"sourcesContent":["/**\r\n * telemetry/surface-footprint — the STATIC developer-axis surfaces.\r\n *\r\n * Commands, skills, and subagents are NOT runtime-intercepted: the host loads\r\n * them as context (slash-command bodies, SKILL.md + resources, subagent system\r\n * prompts) and we never sit in the byte path the way the serve-proxy does for a\r\n * server or the hook runtime does for a hook. So we do NOT (and must not) write\r\n * fake usage rows for them. Instead we compute a deterministic STATIC FOOTPRINT:\r\n * the token cost the surface IMPOSES on a host that loads it, tokenized with the\r\n * SAME shared tokenizer the proxy + hook runtime use.\r\n *\r\n * This is pure: the only input is the passed {@link ResolvedConnector}; there is\r\n * no IO beyond reading that object's already-loaded fields.\r\n */\r\n\r\nimport type { ResolvedConnector } from \"../core/types.js\";\r\nimport { getTokenizer, inferModelFamily } from \"./tokenizer.js\";\r\nimport type { ModelFamily, SurfaceKind, Tokenizer } from \"./types.js\";\r\n\r\n/**\r\n * One static surface footprint: a content surface (command/skill/subagent) and\r\n * the token cost it imposes on a host that loads it. `kind` is the surface kind\r\n * (never `server`/`hook` — those are runtime-measured), `name` is the surface's\r\n * declared name, and `tokens` is the tokenized whole-surface footprint.\r\n */\r\nexport interface SurfaceFootprint {\r\n surfaceKind: Extract<SurfaceKind, \"command\" | \"skill\" | \"subagent\">;\r\n name: string;\r\n tokens: number;\r\n}\r\n\r\n/** Tokenize a list of text fragments (joined with \"\\n\") under `family`. */\r\nfunction countParts(\r\n parts: readonly string[],\r\n family: ModelFamily,\r\n tok: Tokenizer,\r\n): number {\r\n // A surface's footprint is its concatenated content — joining with a newline\r\n // mirrors how the host renders the fields together as one context blob.\r\n return tok.count(parts.join(\"\\n\"), family).tokens;\r\n}\r\n\r\n/**\r\n * Compute the STATIC footprints of every content surface declared on a\r\n * connector, deterministically (same input → same output) and without IO.\r\n *\r\n * • command → description + prompt + argumentHint\r\n * • skill → description + body + every resource value (sorted by path for\r\n * deterministic ordering, so the joined text is stable)\r\n * • subagent → description + prompt\r\n *\r\n * The shared {@link Tokenizer} is used so a command/skill/subagent footprint is\r\n * directly comparable with the runtime `server`/`hook` token counts. The model\r\n * family is inferred from the connector's telemetry hint (no host context here).\r\n */\r\nexport function computeSurfaceFootprints(\r\n connector: ResolvedConnector,\r\n tok: Tokenizer = getTokenizer(),\r\n): SurfaceFootprint[] {\r\n const family = inferModelFamily(\"\", connector.telemetry.modelFamilyHint);\r\n const out: SurfaceFootprint[] = [];\r\n\r\n for (const cmd of connector.commands) {\r\n const parts: string[] = [];\r\n if (cmd.description !== undefined) parts.push(cmd.description);\r\n parts.push(cmd.prompt);\r\n if (cmd.argumentHint !== undefined) parts.push(cmd.argumentHint);\r\n out.push({\r\n surfaceKind: \"command\",\r\n name: cmd.name,\r\n tokens: countParts(parts, family, tok),\r\n });\r\n }\r\n\r\n for (const skill of connector.skills) {\r\n const parts: string[] = [skill.description, skill.body];\r\n if (skill.resources !== undefined) {\r\n // Sort by resource path so the joined text (and thus the count) is stable\r\n // regardless of object key insertion order.\r\n const paths = Object.keys(skill.resources).sort();\r\n for (const p of paths) {\r\n const value = skill.resources[p];\r\n if (value !== undefined) parts.push(value);\r\n }\r\n }\r\n out.push({\r\n surfaceKind: \"skill\",\r\n name: skill.name,\r\n tokens: countParts(parts, family, tok),\r\n });\r\n }\r\n\r\n for (const agent of connector.subagents) {\r\n out.push({\r\n surfaceKind: \"subagent\",\r\n name: agent.name,\r\n tokens: countParts([agent.description, agent.prompt], family, tok),\r\n });\r\n }\r\n\r\n return out;\r\n}\r\n","/**\r\n * telemetry/leaderboard — the PLUGIN / MCP leaderboard, derived from the per-MCP\r\n * serve-proxy telemetry store (src/telemetry/*).\r\n *\r\n * This is the signature MCP-dev metric: of the MCP servers WE wrap, which one\r\n * costs the most tokens — platform-independent, because it measures the server's\r\n * OWN bytes (tool args in, tool result out, tool-defs schemas) tokenized locally,\r\n * the only signal identical across every host. Origin is always `mcp-self`; it is\r\n * NEVER summed with host-native usage (src/usage/*), which measures a different\r\n * thing (whole-conversation tokens).\r\n *\r\n * Pure aggregation + pure formatting over {@link TelemetryStore.query}. The scope\r\n * dimension (installScope/launchMethod) is OPTIONAL on each row — rows written\r\n * before those fields existed are read as \"unknown\" and are honestly labeled.\r\n */\r\n\r\nimport type { ResolvedConnector } from \"../core/types.js\";\r\nimport { openStore } from \"./store.js\";\r\nimport { computeSurfaceFootprints } from \"./surface-footprint.js\";\r\nimport { worstConfidence } from \"./types.js\";\r\nimport type {\r\n ConfidenceSource,\r\n LaunchMethod,\r\n QueryFilter,\r\n SurfaceKind,\r\n TelemetryInstallScope,\r\n TelemetryStore,\r\n ToolEventRecord,\r\n} from \"./types.js\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Options + scope filter\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * A scope filter value the leaderboards accept. The two install buckets\r\n * (`user`/`project`) and the launch methods (`npx`/`bunx`/`uvx`/`node`/`binary`/\r\n * `http`) plus the honest `unknown` fallback. A record matches a `user`/`project`\r\n * value by its `installScope`, and a launch-method value by its `launchMethod`.\r\n */\r\nexport type ScopeFilter =\r\n | TelemetryInstallScope\r\n | LaunchMethod;\r\n\r\n/** Every accepted {@link ScopeFilter} string, for CLI validation. */\r\nexport const SCOPE_FILTER_VALUES: readonly ScopeFilter[] = [\r\n \"user\",\r\n \"project\",\r\n \"npx\",\r\n \"bunx\",\r\n \"uvx\",\r\n \"node\",\r\n \"binary\",\r\n \"http\",\r\n \"unknown\",\r\n] as const;\r\n\r\n/** Options shared by every leaderboard query. */\r\nexport interface LeaderboardOptions {\r\n /** An open store to read from. Defaults to {@link openStore} on the data-root. */\r\n store?: TelemetryStore;\r\n /** Lower-bound epoch ms (inclusive) — forwarded as the store's sinceMs filter. */\r\n sinceMs?: number;\r\n /** Restrict to one connector. */\r\n connectorId?: string;\r\n /** Slice by install scope (user|project) or launch method (npx|binary|http|…). */\r\n scope?: ScopeFilter;\r\n}\r\n\r\n/** Is `value` one of the accepted scope-filter strings? */\r\nexport function isScopeFilter(value: string): value is ScopeFilter {\r\n return (SCOPE_FILTER_VALUES as readonly string[]).includes(value);\r\n}\r\n\r\n/** The install/launch values a `user`/`project` scope filter matches against. */\r\nconst INSTALL_SCOPES: ReadonlySet<string> = new Set<string>([\"user\", \"project\"]);\r\n\r\n/**\r\n * Does a record satisfy the requested scope slice? `user`/`project` match the\r\n * record's `installScope`; any concrete launch method matches the record's\r\n * `launchMethod`. The honest `unknown` filter is special: it matches ONLY a\r\n * record that lacks BOTH dimensions (a truly pre-scope row) — a record carrying\r\n * a known installScope but no launchMethod (or vice-versa) is NOT \"unknown\" and\r\n * must not be swept into that bucket.\r\n */\r\nfunction matchesScope(record: ToolEventRecord, scope: ScopeFilter): boolean {\r\n if (scope === \"unknown\") {\r\n return record.installScope === undefined && record.launchMethod === undefined;\r\n }\r\n if (INSTALL_SCOPES.has(scope)) {\r\n return record.installScope === scope;\r\n }\r\n return record.launchMethod === scope;\r\n}\r\n\r\n/** Build the store {@link QueryFilter} from the shared options. */\r\nfunction toFilter(opts: LeaderboardOptions): QueryFilter {\r\n const filter: QueryFilter = {};\r\n if (opts.sinceMs !== undefined) filter.sinceMs = opts.sinceMs;\r\n if (opts.connectorId !== undefined && opts.connectorId !== \"\") {\r\n filter.connectorId = opts.connectorId;\r\n }\r\n return filter;\r\n}\r\n\r\n/**\r\n * Query the store and apply the (in-memory) scope slice the store cannot. By\r\n * default EXCLUDES the host-native `model_turn` rows: this selector feeds the\r\n * per-MCP/plugin views (mcp/tool/scope), which measure per-MCP `call` + the\r\n * `tool_defs` overhead and must NEVER sum the whole-conversation host-native\r\n * turns. Pass `includeModelTurn: true` to read those rows instead (used by\r\n * {@link hostNativeTurns}).\r\n */\r\nfunction selectRecords(\r\n opts: LeaderboardOptions,\r\n includeModelTurn = false,\r\n): ToolEventRecord[] {\r\n const store = opts.store ?? openStore({});\r\n const owned = opts.store === undefined;\r\n try {\r\n let rows = store.query(toFilter(opts));\r\n rows = includeModelTurn\r\n ? rows.filter((r) => r.scope === \"model_turn\")\r\n : rows.filter((r) => r.scope !== \"model_turn\");\r\n if (opts.scope === undefined) return rows;\r\n const scope = opts.scope;\r\n return rows.filter((r) => matchesScope(r, scope));\r\n } finally {\r\n if (owned) store.close();\r\n }\r\n}\r\n\r\n// Confidence ranking + worst-of comparison live in ./types (the single source\r\n// of truth, imported as worstConfidence above) so a new ConfidenceSource value\r\n// orders correctly here as well.\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Leaderboard rows\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * One row of the MCP/plugin leaderboard — a connector ranked by total measured\r\n * tokens. `tools` is the count of distinct tool names seen (excluding the\r\n * tool-defs `*` pseudo-tool); `hostPlatforms` is the distinct set of hosts that\r\n * ran this server, evidence that the count aggregates across machines.\r\n */\r\nexport interface McpLeaderboardRow {\r\n connectorId: string;\r\n calls: number;\r\n inputTokens: number;\r\n outputTokens: number;\r\n totalTokens: number;\r\n /** Count of distinct real tool names (the tool-defs `*` row is excluded). */\r\n tools: number;\r\n /** Distinct host platforms that produced records, sorted for stable output. */\r\n hostPlatforms: string[];\r\n /** Worst (least-confident) source in the group, for honest labeling. */\r\n confidence: ConfidenceSource;\r\n lastTs: number;\r\n}\r\n\r\n/**\r\n * One row of the per-tool leaderboard — a (connector, tool) pair ranked by total\r\n * measured tokens. The tool-defs pseudo-tool (`*`, scope `tool_defs`) is kept as\r\n * its own row so the fixed schema overhead is visible rather than hidden.\r\n */\r\nexport interface ToolLeaderboardRow {\r\n connectorId: string;\r\n toolName: string;\r\n /** \"call\" round-trips vs the one-time \"tool_defs\" overhead measurement. */\r\n scope: ToolEventRecord[\"scope\"];\r\n calls: number;\r\n inputTokens: number;\r\n outputTokens: number;\r\n totalTokens: number;\r\n confidence: ConfidenceSource;\r\n lastTs: number;\r\n}\r\n\r\n/**\r\n * One row of the scope breakdown — usage grouped by (installScope, launchMethod),\r\n * both honestly reported as \"unknown\" when the underlying record lacks the field.\r\n */\r\nexport interface ScopeBreakdownRow {\r\n installScope: TelemetryInstallScope | \"unknown\";\r\n launchMethod: LaunchMethod;\r\n calls: number;\r\n inputTokens: number;\r\n outputTokens: number;\r\n totalTokens: number;\r\n confidence: ConfidenceSource;\r\n lastTs: number;\r\n}\r\n\r\n/**\r\n * One row of the host-native turns aggregation — a (host, session) pair ranked by\r\n * total host-reported tokens for that whole-conversation turn stream. This is the\r\n * THIRD origin (`host-native-live`): it comes from the opt-in AfterModel /\r\n * PostInvocation usage hook (scope `model_turn`, confidence `host-native`) and is\r\n * NEVER summed with the per-MCP `call` rows or the usage-reader host-scan numbers.\r\n */\r\nexport interface HostNativeTurnsRow {\r\n hostPlatform: string;\r\n sessionId: string;\r\n /** Number of recorded model turns (scope `model_turn` rows) in this group. */\r\n turns: number;\r\n inputTokens: number;\r\n outputTokens: number;\r\n totalTokens: number;\r\n /** Distinct connector ids that produced turns under this host/session. */\r\n connectors: string[];\r\n confidence: ConfidenceSource;\r\n lastTs: number;\r\n}\r\n\r\n/** Mutable accumulator for the host-native turns fold (set finalized to count). */\r\ninterface HostNativeAcc {\r\n hostPlatform: string;\r\n sessionId: string;\r\n turns: number;\r\n inputTokens: number;\r\n outputTokens: number;\r\n totalTokens: number;\r\n connectors: Set<string>;\r\n confidence: ConfidenceSource;\r\n lastTs: number;\r\n}\r\n\r\n/** Mutable accumulator for the MCP leaderboard fold (sets finalized to counts). */\r\ninterface McpAcc {\r\n connectorId: string;\r\n calls: number;\r\n inputTokens: number;\r\n outputTokens: number;\r\n totalTokens: number;\r\n tools: Set<string>;\r\n hostPlatforms: Set<string>;\r\n confidence: ConfidenceSource;\r\n lastTs: number;\r\n}\r\n\r\n/**\r\n * The PLUGIN / MCP leaderboard: rank connectors by total measured tokens desc\r\n * (ties broken by recency). This is the headline \"which MCP server costs the\r\n * most\" metric, platform-independent by construction.\r\n */\r\nexport function mcpLeaderboard(opts: LeaderboardOptions = {}): McpLeaderboardRow[] {\r\n const groups = new Map<string, McpAcc>();\r\n\r\n for (const r of selectRecords(opts)) {\r\n let g = groups.get(r.connectorId);\r\n if (g === undefined) {\r\n g = {\r\n connectorId: r.connectorId,\r\n calls: 0,\r\n inputTokens: 0,\r\n outputTokens: 0,\r\n totalTokens: 0,\r\n tools: new Set<string>(),\r\n hostPlatforms: new Set<string>(),\r\n confidence: r.confidenceSource,\r\n lastTs: r.ts,\r\n };\r\n groups.set(r.connectorId, g);\r\n }\r\n // A tool_defs row is a one-time schema overhead, not a call: count its\r\n // tokens but do not inflate the call count or the distinct-tool count.\r\n if (r.scope === \"call\") {\r\n g.calls += 1;\r\n g.tools.add(r.toolName);\r\n }\r\n g.inputTokens += r.inputTokens;\r\n g.outputTokens += r.outputTokens;\r\n g.totalTokens += r.inputTokens + r.outputTokens;\r\n g.hostPlatforms.add(r.hostPlatform);\r\n g.confidence = worstConfidence(g.confidence, r.confidenceSource);\r\n if (r.ts > g.lastTs) g.lastTs = r.ts;\r\n }\r\n\r\n const rows: McpLeaderboardRow[] = [];\r\n for (const g of groups.values()) {\r\n rows.push({\r\n connectorId: g.connectorId,\r\n calls: g.calls,\r\n inputTokens: g.inputTokens,\r\n outputTokens: g.outputTokens,\r\n totalTokens: g.totalTokens,\r\n tools: g.tools.size,\r\n hostPlatforms: [...g.hostPlatforms].sort(),\r\n confidence: g.confidence,\r\n lastTs: g.lastTs,\r\n });\r\n }\r\n rows.sort((a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs);\r\n return rows;\r\n}\r\n\r\n/**\r\n * The per-tool leaderboard: rank (connector, tool) pairs by total tokens desc\r\n * (ties broken by recency). Drills the MCP leaderboard down to which individual\r\n * tool is the expensive one.\r\n */\r\nexport function toolLeaderboard(opts: LeaderboardOptions = {}): ToolLeaderboardRow[] {\r\n const groups = new Map<string, ToolLeaderboardRow>();\r\n\r\n for (const r of selectRecords(opts)) {\r\n const key = `${r.connectorId}\\u0000${r.toolName}\\u0000${r.scope}`;\r\n const g = groups.get(key);\r\n if (g === undefined) {\r\n groups.set(key, {\r\n connectorId: r.connectorId,\r\n toolName: r.toolName,\r\n scope: r.scope,\r\n calls: r.scope === \"call\" ? 1 : 0,\r\n inputTokens: r.inputTokens,\r\n outputTokens: r.outputTokens,\r\n totalTokens: r.inputTokens + r.outputTokens,\r\n confidence: r.confidenceSource,\r\n lastTs: r.ts,\r\n });\r\n } else {\r\n if (r.scope === \"call\") g.calls += 1;\r\n g.inputTokens += r.inputTokens;\r\n g.outputTokens += r.outputTokens;\r\n g.totalTokens += r.inputTokens + r.outputTokens;\r\n g.confidence = worstConfidence(g.confidence, r.confidenceSource);\r\n if (r.ts > g.lastTs) g.lastTs = r.ts;\r\n }\r\n }\r\n\r\n const rows = [...groups.values()];\r\n rows.sort((a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs);\r\n return rows;\r\n}\r\n\r\n/**\r\n * The scope breakdown: group usage by (installScope, launchMethod). Both\r\n * dimensions read \"unknown\" when the source row lacks the field, so a pre-scope\r\n * row is honestly bucketed rather than dropped or mis-attributed.\r\n */\r\nexport function scopeBreakdown(opts: LeaderboardOptions = {}): ScopeBreakdownRow[] {\r\n const groups = new Map<string, ScopeBreakdownRow>();\r\n\r\n for (const r of selectRecords(opts)) {\r\n const installScope: TelemetryInstallScope | \"unknown\" = r.installScope ?? \"unknown\";\r\n const launchMethod: LaunchMethod = r.launchMethod ?? \"unknown\";\r\n const key = `${installScope}\\u0000${launchMethod}`;\r\n const g = groups.get(key);\r\n if (g === undefined) {\r\n groups.set(key, {\r\n installScope,\r\n launchMethod,\r\n calls: r.scope === \"call\" ? 1 : 0,\r\n inputTokens: r.inputTokens,\r\n outputTokens: r.outputTokens,\r\n totalTokens: r.inputTokens + r.outputTokens,\r\n confidence: r.confidenceSource,\r\n lastTs: r.ts,\r\n });\r\n } else {\r\n if (r.scope === \"call\") g.calls += 1;\r\n g.inputTokens += r.inputTokens;\r\n g.outputTokens += r.outputTokens;\r\n g.totalTokens += r.inputTokens + r.outputTokens;\r\n g.confidence = worstConfidence(g.confidence, r.confidenceSource);\r\n if (r.ts > g.lastTs) g.lastTs = r.ts;\r\n }\r\n }\r\n\r\n const rows = [...groups.values()];\r\n rows.sort((a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs);\r\n return rows;\r\n}\r\n\r\n/**\r\n * The THIRD origin: host-native turns. Aggregates ONLY the `model_turn` rows (the\r\n * opt-in AfterModel / PostInvocation host-native usage hook) by (host, session),\r\n * ranked by total host-reported tokens desc. This is whole-conversation usage the\r\n * host itself reported — exact, but a DIFFERENT thing than the per-MCP `call`\r\n * rows; it is surfaced separately and NEVER summed with the other two origins.\r\n */\r\nexport function hostNativeTurns(opts: LeaderboardOptions = {}): HostNativeTurnsRow[] {\r\n const groups = new Map<string, HostNativeAcc>();\r\n\r\n for (const r of selectRecords(opts, /* includeModelTurn */ true)) {\r\n const key = `${r.hostPlatform}\\u0000${r.sessionId}`;\r\n let g = groups.get(key);\r\n if (g === undefined) {\r\n g = {\r\n hostPlatform: r.hostPlatform,\r\n sessionId: r.sessionId,\r\n turns: 0,\r\n inputTokens: 0,\r\n outputTokens: 0,\r\n totalTokens: 0,\r\n connectors: new Set<string>(),\r\n confidence: r.confidenceSource,\r\n lastTs: r.ts,\r\n };\r\n groups.set(key, g);\r\n }\r\n g.turns += 1;\r\n g.inputTokens += r.inputTokens;\r\n g.outputTokens += r.outputTokens;\r\n g.totalTokens += r.inputTokens + r.outputTokens;\r\n g.connectors.add(r.connectorId);\r\n g.confidence = worstConfidence(g.confidence, r.confidenceSource);\r\n if (r.ts > g.lastTs) g.lastTs = r.ts;\r\n }\r\n\r\n const rows: HostNativeTurnsRow[] = [];\r\n for (const g of groups.values()) {\r\n rows.push({\r\n hostPlatform: g.hostPlatform,\r\n sessionId: g.sessionId,\r\n turns: g.turns,\r\n inputTokens: g.inputTokens,\r\n outputTokens: g.outputTokens,\r\n totalTokens: g.totalTokens,\r\n connectors: [...g.connectors].sort(),\r\n confidence: g.confidence,\r\n lastTs: g.lastTs,\r\n });\r\n }\r\n rows.sort((a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs);\r\n return rows;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Per-surface leaderboard (the FIVE developer-axis surfaces)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * One row of the per-surface developer breakdown — a (surfaceKind, name) pair.\r\n * Combines the RUNTIME-measured surfaces (server `call`+`tool_defs` rows and the\r\n * new `hook` rows from the telemetry store) with the STATIC content footprints\r\n * (command/skill/subagent) from {@link computeSurfaceFootprints}.\r\n *\r\n * • `kind` distinguishes RUNTIME (measured live) from STATIC (footprint of\r\n * context the host loads — never an intercepted usage row).\r\n * • For static rows `calls` is 0, `outputTokens` is 0, the cost sits in\r\n * `inputTokens`/`totalTokens`, and `confidence` is the tokenizer source for\r\n * the connector's family. For runtime rows everything aggregates the store.\r\n */\r\nexport interface SurfaceLeaderboardRow {\r\n surfaceKind: SurfaceKind;\r\n /** Per-item name: the tool name (server), the event name (hook), or the\r\n * command/skill/subagent name (static surfaces). */\r\n name: string;\r\n /** RUNTIME (server/hook store rows) vs STATIC (command/skill/subagent footprint). */\r\n kind: \"runtime\" | \"static\";\r\n calls: number;\r\n inputTokens: number;\r\n outputTokens: number;\r\n totalTokens: number;\r\n confidence: ConfidenceSource;\r\n lastTs: number;\r\n}\r\n\r\n/** Mutable accumulator for the runtime side of the per-surface fold. */\r\ninterface SurfaceAcc {\r\n surfaceKind: SurfaceKind;\r\n name: string;\r\n calls: number;\r\n inputTokens: number;\r\n outputTokens: number;\r\n totalTokens: number;\r\n confidence: ConfidenceSource;\r\n lastTs: number;\r\n}\r\n\r\n/**\r\n * A record's developer-axis surface kind, backward-compatibly. Rows written\r\n * before `surfaceKind` existed (every legacy serve-proxy `call`/`tool_defs` row)\r\n * lack it and are read as `server`; the `hook` runtime stamps it explicitly.\r\n */\r\nfunction recordSurfaceKind(r: ToolEventRecord): SurfaceKind {\r\n if (r.surfaceKind !== undefined) return r.surfaceKind;\r\n // No explicit kind → it predates this field → it is a serve-proxy server row.\r\n return \"server\";\r\n}\r\n\r\n/** Options for {@link surfaceLeaderboard}: the shared query opts + the connectors\r\n * whose STATIC footprints are folded in. */\r\nexport interface SurfaceLeaderboardOptions extends LeaderboardOptions {\r\n /** Registered connectors to compute static command/skill/subagent footprints\r\n * over. Omit/empty → only the runtime (server/hook) rows are reported. */\r\n connectors?: readonly ResolvedConnector[];\r\n}\r\n\r\n/**\r\n * The PER-SURFACE developer leaderboard across all FIVE surfaces.\r\n *\r\n * RUNTIME side: the store's `call`/`tool_defs` rows (surface `server`) and the\r\n * `hook` rows (surface `hook`), grouped by (surfaceKind, name) with IN/OUT/TOTAL\r\n * and a call count. (`model_turn` rows are excluded by `selectRecords`, exactly\r\n * as the per-MCP views exclude them.)\r\n *\r\n * STATIC side: one row per command/skill/subagent from\r\n * {@link computeSurfaceFootprints} over each passed connector, deduped by\r\n * (surfaceKind, name) and summed when the same surface is declared by more than\r\n * one connector. Static rows carry the footprint in `inputTokens` (the cost the\r\n * host pays to load that context); `outputTokens`/`calls` are 0.\r\n *\r\n * Sorted by total tokens desc, ties broken by recency (static rows have lastTs 0\r\n * so they sort after equal-token runtime rows).\r\n */\r\nexport function surfaceLeaderboard(\r\n opts: SurfaceLeaderboardOptions = {},\r\n): SurfaceLeaderboardRow[] {\r\n // ── RUNTIME: aggregate server + hook store rows by (surfaceKind, name) ──\r\n const runtime = new Map<string, SurfaceAcc>();\r\n for (const r of selectRecords(opts)) {\r\n const surfaceKind = recordSurfaceKind(r);\r\n const key = `${surfaceKind} ${r.toolName}`;\r\n let g = runtime.get(key);\r\n if (g === undefined) {\r\n g = {\r\n surfaceKind,\r\n name: r.toolName,\r\n calls: 0,\r\n inputTokens: 0,\r\n outputTokens: 0,\r\n totalTokens: 0,\r\n confidence: r.confidenceSource,\r\n lastTs: r.ts,\r\n };\r\n runtime.set(key, g);\r\n }\r\n // tool_defs is a one-time schema overhead, not a call — count tokens only.\r\n if (r.scope === \"call\" || r.scope === \"hook\") g.calls += 1;\r\n g.inputTokens += r.inputTokens;\r\n g.outputTokens += r.outputTokens;\r\n g.totalTokens += r.inputTokens + r.outputTokens;\r\n g.confidence = worstConfidence(g.confidence, r.confidenceSource);\r\n if (r.ts > g.lastTs) g.lastTs = r.ts;\r\n }\r\n\r\n const rows: SurfaceLeaderboardRow[] = [];\r\n for (const g of runtime.values()) {\r\n rows.push({\r\n surfaceKind: g.surfaceKind,\r\n name: g.name,\r\n kind: \"runtime\",\r\n calls: g.calls,\r\n inputTokens: g.inputTokens,\r\n outputTokens: g.outputTokens,\r\n totalTokens: g.totalTokens,\r\n confidence: g.confidence,\r\n lastTs: g.lastTs,\r\n });\r\n }\r\n\r\n // ── STATIC: fold in command/skill/subagent footprints per connector ──────\r\n const staticRows = new Map<\r\n string,\r\n { surfaceKind: SurfaceKind; name: string; tokens: number }\r\n >();\r\n for (const connector of opts.connectors ?? []) {\r\n for (const fp of computeSurfaceFootprints(connector)) {\r\n const key = `${fp.surfaceKind} ${fp.name}`;\r\n const existing = staticRows.get(key);\r\n if (existing === undefined) {\r\n staticRows.set(key, { surfaceKind: fp.surfaceKind, name: fp.name, tokens: fp.tokens });\r\n } else {\r\n existing.tokens += fp.tokens;\r\n }\r\n }\r\n }\r\n // A pure tokenizer footprint carries no host-reported truth — label it with the\r\n // tokenizer source the connector's family would yield. We derive that once via\r\n // a throwaway count so the static confidence is honest (approx vs exact).\r\n const staticConfidence: ConfidenceSource = staticFootprintConfidence(opts.connectors ?? []);\r\n for (const s of staticRows.values()) {\r\n rows.push({\r\n surfaceKind: s.surfaceKind,\r\n name: s.name,\r\n kind: \"static\",\r\n calls: 0,\r\n inputTokens: s.tokens,\r\n outputTokens: 0,\r\n totalTokens: s.tokens,\r\n confidence: staticConfidence,\r\n lastTs: 0,\r\n });\r\n }\r\n\r\n rows.sort((a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs);\r\n return rows;\r\n}\r\n\r\n/**\r\n * The confidence label for the static footprints: the tokenizer source for the\r\n * first connector's family (exact for openai, approx otherwise). Defaults to\r\n * `tokenizer-approx` when no connector is supplied — an honest estimate label.\r\n */\r\nfunction staticFootprintConfidence(\r\n connectors: readonly ResolvedConnector[],\r\n): ConfidenceSource {\r\n const first = connectors[0];\r\n if (first === undefined) return \"tokenizer-approx\";\r\n // openai family → exact BPE; everything else → approx. Mirrors tokenizer.bpeSource.\r\n return first.telemetry.modelFamilyHint === \"openai\"\r\n ? \"tokenizer-exact\"\r\n : \"tokenizer-approx\";\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Formatting (mirrors telemetry/report.ts: aligned table + honesty legend)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Sources whose counts are estimates (vs real / host-reported numbers).\r\n * `tokenizer-calibrated` is still an estimate — an approximation nudged toward\r\n * truth by a sampled Anthropic count_tokens factor — so it belongs here too.\r\n */\r\nconst ESTIMATE_SOURCES: ReadonlySet<ConfidenceSource> = new Set<ConfidenceSource>([\r\n \"heuristic\",\r\n \"tokenizer-approx\",\r\n \"tokenizer-calibrated\",\r\n]);\r\n\r\n/** Compact integer formatting with thousands separators. */\r\nfunction fmtInt(n: number): string {\r\n return n.toLocaleString(\"en-US\");\r\n}\r\n\r\n/** Right-pad (left-aligned columns). */\r\nfunction padRight(s: string, width: number): string {\r\n return s.length >= width ? s : s + \" \".repeat(width - s.length);\r\n}\r\n\r\n/** Left-pad (right-aligned numeric columns). */\r\nfunction padLeft(s: string, width: number): string {\r\n return s.length >= width ? s : \" \".repeat(width - s.length) + s;\r\n}\r\n\r\n/**\r\n * Render an aligned table given headers, the data cells, and which columns are\r\n * left-aligned (text) vs right-aligned (numbers). Adds rule lines and a TOTAL\r\n * footer. Shared by every leaderboard formatter for one consistent style.\r\n */\r\nfunction renderTable(\r\n headers: string[],\r\n dataRows: string[][],\r\n leftAlignedCols: ReadonlySet<number>,\r\n totalRow: string[],\r\n emptyMessage: string,\r\n): string[] {\r\n const widths = headers.map((h, col) => {\r\n let w = h.length;\r\n for (const row of dataRows) w = Math.max(w, (row[col] ?? \"\").length);\r\n w = Math.max(w, (totalRow[col] ?? \"\").length);\r\n return w;\r\n });\r\n\r\n const renderRow = (cells: string[]): string =>\r\n cells\r\n .map((c, col) =>\r\n leftAlignedCols.has(col)\r\n ? padRight(c, widths[col] ?? 0)\r\n : padLeft(c, widths[col] ?? 0),\r\n )\r\n .join(\" \")\r\n .trimEnd();\r\n\r\n const ruleWidth = widths.reduce((a, b) => a + b, 0) + 2 * (widths.length - 1);\r\n const rule = \"-\".repeat(ruleWidth);\r\n\r\n const lines: string[] = [];\r\n lines.push(renderRow(headers));\r\n lines.push(rule);\r\n if (dataRows.length === 0) {\r\n lines.push(emptyMessage);\r\n } else {\r\n for (const row of dataRows) lines.push(renderRow(row));\r\n }\r\n lines.push(rule);\r\n lines.push(renderRow(totalRow));\r\n return lines;\r\n}\r\n\r\n/** Append the estimate legend when any row carries an estimate confidence. */\r\nfunction appendEstimateLegend(\r\n lines: string[],\r\n confidences: ConfidenceSource[],\r\n): void {\r\n if (confidences.some((c) => ESTIMATE_SOURCES.has(c))) {\r\n lines.push(\"\");\r\n lines.push(\r\n \"note: heuristic and tokenizer-approx token counts are estimates, \" +\r\n \"not exact host-reported usage.\",\r\n );\r\n if (confidences.some((c) => c === \"tokenizer-calibrated\")) {\r\n lines.push(\r\n \"note: tokenizer-calibrated = local approx adjusted by a sampled \" +\r\n \"Anthropic count_tokens factor (opt-in; content sampled off-box only \" +\r\n \"when AGENT_CONNECTOR_CALIBRATE=anthropic).\",\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Format the MCP/plugin leaderboard as an aligned table:\r\n * RANK | CONNECTOR | CALLS | TOOLS | IN | OUT | TOTAL | HOSTS | CONFIDENCE\r\n * sorted by total tokens desc, with a TOTAL footer and the honesty legend.\r\n */\r\nexport function formatMcpLeaderboard(rows: McpLeaderboardRow[]): string {\r\n const sorted = [...rows].sort(\r\n (a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs,\r\n );\r\n const headers = [\r\n \"RANK\",\r\n \"CONNECTOR\",\r\n \"CALLS\",\r\n \"TOOLS\",\r\n \"IN\",\r\n \"OUT\",\r\n \"TOTAL\",\r\n \"HOSTS\",\r\n \"CONFIDENCE\",\r\n ];\r\n const dataRows = sorted.map((r, i) => [\r\n `${i + 1}`,\r\n r.connectorId,\r\n fmtInt(r.calls),\r\n fmtInt(r.tools),\r\n fmtInt(r.inputTokens),\r\n fmtInt(r.outputTokens),\r\n fmtInt(r.totalTokens),\r\n r.hostPlatforms.length === 0 ? \"-\" : r.hostPlatforms.join(\",\"),\r\n r.confidence,\r\n ]);\r\n\r\n const totals = sorted.reduce(\r\n (acc, r) => {\r\n acc.calls += r.calls;\r\n acc.inputTokens += r.inputTokens;\r\n acc.outputTokens += r.outputTokens;\r\n acc.totalTokens += r.totalTokens;\r\n return acc;\r\n },\r\n { calls: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0 },\r\n );\r\n const totalRow = [\r\n \"\",\r\n \"TOTAL\",\r\n fmtInt(totals.calls),\r\n \"\",\r\n fmtInt(totals.inputTokens),\r\n fmtInt(totals.outputTokens),\r\n fmtInt(totals.totalTokens),\r\n \"\",\r\n \"\",\r\n ];\r\n\r\n // Left-align RANK (0), CONNECTOR (1), HOSTS (7), CONFIDENCE (8); numbers right.\r\n const leftCols = new Set<number>([0, 1, 7, 8]);\r\n const lines = renderTable(\r\n headers,\r\n dataRows,\r\n leftCols,\r\n totalRow,\r\n \"(no MCP telemetry recorded)\",\r\n );\r\n appendEstimateLegend(\r\n lines,\r\n sorted.map((r) => r.confidence),\r\n );\r\n return lines.join(\"\\n\");\r\n}\r\n\r\n/**\r\n * Format the per-tool leaderboard:\r\n * RANK | CONNECTOR | TOOL | SCOPE | CALLS | IN | OUT | TOTAL | CONFIDENCE\r\n * sorted by total tokens desc, with a TOTAL footer and the honesty legend.\r\n */\r\nexport function formatToolLeaderboard(rows: ToolLeaderboardRow[]): string {\r\n const sorted = [...rows].sort(\r\n (a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs,\r\n );\r\n const headers = [\r\n \"RANK\",\r\n \"CONNECTOR\",\r\n \"TOOL\",\r\n \"SCOPE\",\r\n \"CALLS\",\r\n \"IN\",\r\n \"OUT\",\r\n \"TOTAL\",\r\n \"CONFIDENCE\",\r\n ];\r\n const dataRows = sorted.map((r, i) => [\r\n `${i + 1}`,\r\n r.connectorId,\r\n r.toolName,\r\n r.scope,\r\n fmtInt(r.calls),\r\n fmtInt(r.inputTokens),\r\n fmtInt(r.outputTokens),\r\n fmtInt(r.totalTokens),\r\n r.confidence,\r\n ]);\r\n\r\n const totals = sorted.reduce(\r\n (acc, r) => {\r\n acc.calls += r.calls;\r\n acc.inputTokens += r.inputTokens;\r\n acc.outputTokens += r.outputTokens;\r\n acc.totalTokens += r.totalTokens;\r\n return acc;\r\n },\r\n { calls: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0 },\r\n );\r\n const totalRow = [\r\n \"\",\r\n \"TOTAL\",\r\n \"\",\r\n \"\",\r\n fmtInt(totals.calls),\r\n fmtInt(totals.inputTokens),\r\n fmtInt(totals.outputTokens),\r\n fmtInt(totals.totalTokens),\r\n \"\",\r\n ];\r\n\r\n // Left-align RANK (0), CONNECTOR (1), TOOL (2), SCOPE (3), CONFIDENCE (8).\r\n const leftCols = new Set<number>([0, 1, 2, 3, 8]);\r\n const lines = renderTable(\r\n headers,\r\n dataRows,\r\n leftCols,\r\n totalRow,\r\n \"(no MCP telemetry recorded)\",\r\n );\r\n appendEstimateLegend(\r\n lines,\r\n sorted.map((r) => r.confidence),\r\n );\r\n return lines.join(\"\\n\");\r\n}\r\n\r\n/**\r\n * Format the scope breakdown:\r\n * INSTALL | LAUNCH | CALLS | IN | OUT | TOTAL | CONFIDENCE\r\n * with a TOTAL footer and the honesty legend.\r\n */\r\nexport function formatScopeBreakdown(rows: ScopeBreakdownRow[]): string {\r\n const sorted = [...rows].sort(\r\n (a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs,\r\n );\r\n const headers = [\"INSTALL\", \"LAUNCH\", \"CALLS\", \"IN\", \"OUT\", \"TOTAL\", \"CONFIDENCE\"];\r\n const dataRows = sorted.map((r) => [\r\n r.installScope,\r\n r.launchMethod,\r\n fmtInt(r.calls),\r\n fmtInt(r.inputTokens),\r\n fmtInt(r.outputTokens),\r\n fmtInt(r.totalTokens),\r\n r.confidence,\r\n ]);\r\n\r\n const totals = sorted.reduce(\r\n (acc, r) => {\r\n acc.calls += r.calls;\r\n acc.inputTokens += r.inputTokens;\r\n acc.outputTokens += r.outputTokens;\r\n acc.totalTokens += r.totalTokens;\r\n return acc;\r\n },\r\n { calls: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0 },\r\n );\r\n const totalRow = [\r\n \"TOTAL\",\r\n \"\",\r\n fmtInt(totals.calls),\r\n fmtInt(totals.inputTokens),\r\n fmtInt(totals.outputTokens),\r\n fmtInt(totals.totalTokens),\r\n \"\",\r\n ];\r\n\r\n // Left-align INSTALL (0), LAUNCH (1), CONFIDENCE (6); numbers right-aligned.\r\n const leftCols = new Set<number>([0, 1, 6]);\r\n const lines = renderTable(\r\n headers,\r\n dataRows,\r\n leftCols,\r\n totalRow,\r\n \"(no MCP telemetry recorded)\",\r\n );\r\n appendEstimateLegend(\r\n lines,\r\n sorted.map((r) => r.confidence),\r\n );\r\n return lines.join(\"\\n\");\r\n}\r\n\r\n/**\r\n * Format the host-native turns aggregation (the THIRD origin):\r\n * RANK | HOST | SESSION | TURNS | IN | OUT | TOTAL | CONNECTORS | CONFIDENCE\r\n * sorted by total tokens desc, with a TOTAL footer. These are whole-conversation,\r\n * host-reported (exact) counts — a DIFFERENT thing than the per-MCP rows, so the\r\n * footer total is for THIS section alone and is never added to the others.\r\n */\r\nexport function formatHostNativeTurns(rows: HostNativeTurnsRow[]): string {\r\n const sorted = [...rows].sort(\r\n (a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs,\r\n );\r\n const headers = [\r\n \"RANK\",\r\n \"HOST\",\r\n \"SESSION\",\r\n \"TURNS\",\r\n \"IN\",\r\n \"OUT\",\r\n \"TOTAL\",\r\n \"CONNECTORS\",\r\n \"CONFIDENCE\",\r\n ];\r\n const dataRows = sorted.map((r, i) => [\r\n `${i + 1}`,\r\n r.hostPlatform,\r\n r.sessionId === \"\" ? \"-\" : r.sessionId,\r\n fmtInt(r.turns),\r\n fmtInt(r.inputTokens),\r\n fmtInt(r.outputTokens),\r\n fmtInt(r.totalTokens),\r\n r.connectors.length === 0 ? \"-\" : r.connectors.join(\",\"),\r\n r.confidence,\r\n ]);\r\n\r\n const totals = sorted.reduce(\r\n (acc, r) => {\r\n acc.turns += r.turns;\r\n acc.inputTokens += r.inputTokens;\r\n acc.outputTokens += r.outputTokens;\r\n acc.totalTokens += r.totalTokens;\r\n return acc;\r\n },\r\n { turns: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0 },\r\n );\r\n const totalRow = [\r\n \"\",\r\n \"TOTAL\",\r\n \"\",\r\n fmtInt(totals.turns),\r\n fmtInt(totals.inputTokens),\r\n fmtInt(totals.outputTokens),\r\n fmtInt(totals.totalTokens),\r\n \"\",\r\n \"\",\r\n ];\r\n\r\n // Left-align RANK (0), HOST (1), SESSION (2), CONNECTORS (7), CONFIDENCE (8).\r\n const leftCols = new Set<number>([0, 1, 2, 7, 8]);\r\n const lines = renderTable(\r\n headers,\r\n dataRows,\r\n leftCols,\r\n totalRow,\r\n \"(no host-native turns recorded — enable opt-in host-native usage)\",\r\n );\r\n return lines.join(\"\\n\");\r\n}\r\n\r\n/**\r\n * Format the per-surface developer breakdown:\r\n * SURFACE | NAME | IN | OUT | TOTAL | KIND\r\n * one row per (surfaceKind, name) across all FIVE surfaces — server + hook\r\n * (runtime, measured from the store) and command/skill/subagent (static\r\n * footprints). Sorted by total tokens desc, with a TOTAL footer and the honesty\r\n * legend (static footprints + any estimate-confidence runtime rows).\r\n *\r\n * NOTE: the TOTAL footer sums BOTH runtime and static tokens for at-a-glance\r\n * scale; the KIND column keeps the distinction explicit so the two are never\r\n * silently conflated (runtime = live usage; static = context-load footprint).\r\n */\r\nexport function formatSurfaceLeaderboard(rows: SurfaceLeaderboardRow[]): string {\r\n const sorted = [...rows].sort(\r\n (a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs,\r\n );\r\n const headers = [\"SURFACE\", \"NAME\", \"IN\", \"OUT\", \"TOTAL\", \"KIND\"];\r\n const dataRows = sorted.map((r) => [\r\n r.surfaceKind,\r\n r.name,\r\n fmtInt(r.inputTokens),\r\n fmtInt(r.outputTokens),\r\n fmtInt(r.totalTokens),\r\n r.kind,\r\n ]);\r\n\r\n const totals = sorted.reduce(\r\n (acc, r) => {\r\n acc.inputTokens += r.inputTokens;\r\n acc.outputTokens += r.outputTokens;\r\n acc.totalTokens += r.totalTokens;\r\n return acc;\r\n },\r\n { inputTokens: 0, outputTokens: 0, totalTokens: 0 },\r\n );\r\n const totalRow = [\r\n \"TOTAL\",\r\n \"\",\r\n fmtInt(totals.inputTokens),\r\n fmtInt(totals.outputTokens),\r\n fmtInt(totals.totalTokens),\r\n \"\",\r\n ];\r\n\r\n // Left-align SURFACE (0), NAME (1), KIND (5); numbers right-aligned.\r\n const leftCols = new Set<number>([0, 1, 5]);\r\n const lines = renderTable(\r\n headers,\r\n dataRows,\r\n leftCols,\r\n totalRow,\r\n \"(no developer surfaces recorded)\",\r\n );\r\n if (sorted.some((r) => r.kind === \"static\")) {\r\n lines.push(\"\");\r\n lines.push(\r\n \"note: KIND=static rows are the tokenized FOOTPRINT a command/skill/subagent \" +\r\n \"imposes on a host that loads it as context — not intercepted usage rows.\",\r\n );\r\n }\r\n appendEstimateLegend(\r\n lines,\r\n sorted.map((r) => r.confidence),\r\n );\r\n return lines.join(\"\\n\");\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/ids.ts","../src/core/paths.ts"],"sourcesContent":["/** Shared connector-id grammar for author-time configs and runtime ids. */\r\nexport const CONNECTOR_ID_RE = /^[a-z0-9][a-z0-9-]*$/;\r\n\r\nexport function isValidConnectorId(id: unknown): id is string {\r\n return typeof id === \"string\" && CONNECTOR_ID_RE.test(id);\r\n}\r\n\r\nexport function assertConnectorId(id: unknown, label = \"connector id\"): string {\r\n if (isValidConnectorId(id)) return id;\r\n throw new Error(\r\n `${label} must be kebab-case matching ${CONNECTOR_ID_RE} (got ${JSON.stringify(id)})`,\r\n );\r\n}\r\n","/**\r\n * core/paths — home data-root, stable home binary, and project identity.\r\n *\r\n * Operating model (docs/ARCHITECTURE.md §3):\r\n * • One home data-root holds the single binary + shared telemetry + state.\r\n * Override with AGENT_CONNECTOR_DATA_DIR (the cross-agent shared-DB key).\r\n * This relocates FRAMEWORK state only — never platform-native config files.\r\n * • The home binary lives at a STABLE path so every host's pointer config\r\n * keeps working across updates (no versioned cache dir → no cache-heal bug).\r\n * • Per-project data is keyed by a stable project identity (git remote ||\r\n * normalized absolute path), hashed — not by code location.\r\n */\r\n\r\nimport { createHash } from \"node:crypto\";\r\nimport {\r\n chmodSync,\r\n existsSync,\r\n lstatSync,\r\n mkdirSync,\r\n readFileSync,\r\n writeFileSync,\r\n} from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { basename, isAbsolute, join, parse, relative, resolve, sep } from \"node:path\";\r\n\r\nimport { assertConnectorId } from \"./ids.js\";\r\n\r\n/** The built-in default framework data-root (`~/.agent-connector`), ignoring\r\n * any AGENT_CONNECTOR_DATA_DIR override. */\r\nexport function defaultDataRoot(): string {\r\n return join(homedir(), \".agent-connector\");\r\n}\r\n\r\n/** Framework data-root: AGENT_CONNECTOR_DATA_DIR || ~/.agent-connector. */\r\nexport function dataRoot(): string {\r\n const override = process.env.AGENT_CONNECTOR_DATA_DIR;\r\n if (override && override.trim() !== \"\") return resolve(override);\r\n return defaultDataRoot();\r\n}\r\n\r\n/**\r\n * True when `root` is a NON-DEFAULT data root — i.e. it differs from the\r\n * built-in `~/.agent-connector`. Compared after `resolve()` so two spellings of\r\n * the same dir read as equal. Used at install time to decide whether the\r\n * telemetry serve-wrap must carry an explicit `--data-dir` flag: a default-root\r\n * install relies on the (correct) fallback and stays flag-free, but an overridden\r\n * root must be passed explicitly because some hosts (codex) strip the child env,\r\n * so the spawned `serve` would otherwise resolve the default root and fail to\r\n * find the connector record written under the overridden one.\r\n */\r\nexport function isNonDefaultDataRoot(root: string): boolean {\r\n return resolve(root) !== resolve(defaultDataRoot());\r\n}\r\n\r\nexport function connectorsDir(): string {\r\n return join(dataRoot(), \"connectors\");\r\n}\r\n/**\r\n * Stable cache root for REMOTE connector sources fetched by `install <source>`\r\n * (one subdir per `<owner>__<repo>[__<ref>]`). Lives under the data-root — NOT a\r\n * temp dir — so a fetched connector whose server is a LOCAL stdio command\r\n * (`node <path-in-the-clone>`) keeps resolving after install across reboots.\r\n */\r\nexport function sourcesDir(): string {\r\n return join(dataRoot(), \"sources\");\r\n}\r\nexport function connectorDir(id: string): string {\r\n const root = resolve(connectorsDir());\r\n const safeId = assertConnectorId(id);\r\n const dir = resolve(root, safeId);\r\n const rel = relative(root, dir);\r\n if (rel === \"\" || rel.startsWith(\"..\") || isAbsolute(rel)) {\r\n throw new Error(`connector record path escapes ${root}: ${id}`);\r\n }\r\n return dir;\r\n}\r\nexport function backupsDir(): string {\r\n return join(dataRoot(), \"backups\");\r\n}\r\nexport function logsDir(): string {\r\n return join(dataRoot(), \"logs\");\r\n}\r\n\r\n/** Telemetry store path for the selected backend. */\r\nexport function telemetryPath(store: \"ndjson\" | \"sqlite\" = \"ndjson\"): string {\r\n return join(dataRoot(), store === \"sqlite\" ? \"telemetry.db\" : \"telemetry.ndjson\");\r\n}\r\n\r\n/** Stable, OS-correct path to the single home binary pointer. */\r\nexport function homeBinPath(): string {\r\n const name = process.platform === \"win32\" ? \"agent-connector.cmd\" : \"agent-connector\";\r\n return join(dataRoot(), \"bin\", name);\r\n}\r\n\r\nexport function ensureDir(dir: string): void {\r\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\r\n}\r\n\r\n/**\r\n * Return the first existing symbolic-link component in `path`, or null when the\r\n * existing prefix is symlink-free. Stops at the first missing component so new\r\n * directories/files can still be created normally.\r\n */\r\nexport function firstSymlinkInPath(path: string): string | null {\r\n const abs = resolve(path);\r\n const root = parse(abs).root;\r\n const rest = relative(root, abs);\r\n if (rest === \"\") return null;\r\n\r\n let cur = root;\r\n for (const part of rest.split(sep)) {\r\n if (part === \"\") continue;\r\n cur = join(cur, part);\r\n try {\r\n if (lstatSync(cur).isSymbolicLink()) return cur;\r\n } catch (err) {\r\n const code = (err as NodeJS.ErrnoException).code;\r\n if (code === \"ENOENT\") return null;\r\n throw err;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nexport function assertNoSymlinkInPath(path: string): void {\r\n const symlink = firstSymlinkInPath(path);\r\n if (symlink !== null) {\r\n throw new Error(`${basename(path)} skipped: symbolic link in path (${symlink})`);\r\n }\r\n}\r\n\r\n/**\r\n * Write a stable launcher at homeBinPath() that execs the real CLI entry.\r\n * Avoids symlinks (Windows constraint) and versioned paths. Idempotent.\r\n *\r\n * @param cliEntry absolute path to the installed cli.js\r\n * @param nodePath node runtime to exec with (defaults to current process.execPath)\r\n * @returns the stable launcher path\r\n */\r\nexport function ensureHomeBin(cliEntry: string, nodePath: string = process.execPath): string {\r\n const binPath = homeBinPath();\r\n assertNoSymlinkInPath(binPath);\r\n ensureDir(join(dataRoot(), \"bin\"));\r\n const node = nodePath.replace(/\\\\/g, \"/\");\r\n const cli = cliEntry.replace(/\\\\/g, \"/\");\r\n if (process.platform === \"win32\") {\r\n writeFileSync(binPath, `@echo off\\r\\n\"${node}\" \"${cli}\" %*\\r\\n`, \"utf8\");\r\n } else {\r\n writeFileSync(binPath, `#!/bin/sh\\nexec \"${node}\" \"${cli}\" \"$@\"\\n`, \"utf8\");\r\n chmodSync(binPath, 0o755);\r\n }\r\n return binPath;\r\n}\r\n\r\n/** Read remote.origin.url from a project's .git/config without spawning git. */\r\nfunction gitRemoteUrl(projectDir: string): string | null {\r\n const cfg = join(projectDir, \".git\", \"config\");\r\n if (!existsSync(cfg)) return null;\r\n try {\r\n const text = readFileSync(cfg, \"utf8\");\r\n // Find [remote \"origin\"] ... url = X\r\n const lines = text.split(/\\r?\\n/);\r\n let inOrigin = false;\r\n for (const line of lines) {\r\n const s = line.trim();\r\n if (s.startsWith(\"[\")) inOrigin = /^\\[remote\\s+\"origin\"\\]$/.test(s);\r\n else if (inOrigin) {\r\n const m = s.match(/^url\\s*=\\s*(.+)$/);\r\n if (m && m[1]) return m[1].trim();\r\n }\r\n }\r\n } catch {\r\n /* ignore */\r\n }\r\n return null;\r\n}\r\n\r\nexport interface ProjectIdentity {\r\n /** Hashed stable key used to partition telemetry/state. */\r\n key: string;\r\n /** Human-readable label (git remote or directory name). */\r\n label: string;\r\n /** Absolute project directory. */\r\n dir: string;\r\n}\r\n\r\n/**\r\n * Derive a stable project identity. Prefers the git origin URL (stable across\r\n * clones/locations); falls back to the normalized absolute path.\r\n */\r\nexport function projectIdentity(projectDir: string): ProjectIdentity {\r\n const dir = resolve(projectDir);\r\n const remote = gitRemoteUrl(dir);\r\n const basis = remote ?? dir;\r\n const key = createHash(\"sha256\").update(basis).digest(\"hex\").slice(0, 16);\r\n return { key, label: remote ?? basename(dir), dir };\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveSpawnCommand
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Y3K6KJMS.js";
|
|
5
5
|
|
|
6
6
|
// src/core/spawn-child.ts
|
|
7
7
|
import { spawn } from "child_process";
|
|
@@ -18,4 +18,4 @@ function spawnChild(command, args, options) {
|
|
|
18
18
|
export {
|
|
19
19
|
spawnChild
|
|
20
20
|
};
|
|
21
|
-
//# sourceMappingURL=chunk-
|
|
21
|
+
//# sourceMappingURL=chunk-SLJI5BOM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/spawn-child.ts"],"sourcesContent":["/**\r\n * core/spawn-child — the cross-platform child_process.spawn wrapper.\r\n *\r\n * Kept SEPARATE from core/spawn.ts (pure string/path helpers, imported widely\r\n * and early) so that pulling in `node:child_process` here does not perturb the\r\n * module-eval order of test suites that vi.mock(\"node:child_process\").\r\n */\r\n\r\nimport { type ChildProcess, type SpawnOptions, spawn } from \"node:child_process\";\r\n\r\nimport { resolveSpawnCommand } from \"./spawn.js\";\r\n\r\n/**\r\n * Quote one token for a cmd.exe command line: wrap in double quotes, doubling any\r\n * embedded double quote. Sufficient for the paths / package names / flags we\r\n * spawn (connector config, not untrusted end-user input) — we intentionally do\r\n * not escape cmd metacharacters (& | ^ %), which never appear in those tokens.\r\n */\r\nfunction winQuoteArg(s: string): string {\r\n return `\"${s.replace(/\"/g, '\"\"')}\"`;\r\n}\r\n\r\n/**\r\n * Cross-platform child spawn that fixes native-Windows package-runner launches.\r\n * Direct spawn on macOS/Linux. On win32 it routes through\r\n * {@link resolveSpawnCommand}; an `.exe` runs directly, while a `.cmd`/`.bat`\r\n * (npx.cmd, the agent-connector.cmd launcher) is launched via a SINGLE quoted\r\n * command line with `shell: true` — a string rather than an args array, which\r\n * Node requires for a batch file AND which avoids the DEP0190 deprecation that\r\n * `shell: true` + an args array triggers on Node ≥ 24.\r\n */\r\nexport function spawnChild(\r\n command: string,\r\n args: string[],\r\n options: SpawnOptions,\r\n): ChildProcess {\r\n const resolved = resolveSpawnCommand(command);\r\n if (!resolved.needsShell) return spawn(resolved.file, args, options);\r\n const line = [resolved.file, ...args].map(winQuoteArg).join(\" \");\r\n return spawn(line, { ...options, shell: true });\r\n}\r\n"],"mappings":";;;;;;AAQA,SAA+C,aAAa;AAU5D,SAAS,YAAY,GAAmB;AACtC,SAAO,IAAI,EAAE,QAAQ,MAAM,IAAI,CAAC;AAClC;AAWO,SAAS,WACd,SACA,MACA,SACc;AACd,QAAM,WAAW,oBAAoB,OAAO;AAC5C,MAAI,CAAC,SAAS,WAAY,QAAO,MAAM,SAAS,MAAM,MAAM,OAAO;AACnE,QAAM,OAAO,CAAC,SAAS,MAAM,GAAG,IAAI,EAAE,IAAI,WAAW,EAAE,KAAK,GAAG;AAC/D,SAAO,MAAM,MAAM,EAAE,GAAG,SAAS,OAAO,KAAK,CAAC;AAChD;","names":[]}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveEnvRefsDeep
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XXLCMSD3.js";
|
|
5
5
|
import {
|
|
6
6
|
BaseAdapter,
|
|
7
7
|
parseJsonc,
|
|
8
8
|
renderSkillMd
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-3FINQ2ZF.js";
|
|
10
10
|
import {
|
|
11
11
|
buildWrappedStdio
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-Y3K6KJMS.js";
|
|
13
13
|
|
|
14
14
|
// src/adapters/openclaw/index.ts
|
|
15
15
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -1008,4 +1008,4 @@ export {
|
|
|
1008
1008
|
adapter,
|
|
1009
1009
|
openclaw_default
|
|
1010
1010
|
};
|
|
1011
|
-
//# sourceMappingURL=chunk-
|
|
1011
|
+
//# sourceMappingURL=chunk-TGZFDXNH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/openclaw/index.ts"],"sourcesContent":["/**\r\n * adapters/openclaw — OpenClaw (Gateway) platform adapter for agent-connector.\r\n *\r\n * OpenClaw is a **ts-plugin** host, but with a twist no other ts-plugin host\r\n * has: a plugin only delivers its MCP tools to the agent when it is registered\r\n * in TWO places at once (DUAL REGISTRATION):\r\n *\r\n * (a) plugins.entries.<id> = { enabled: true } + plugins.load.paths: [dir]\r\n * — so the gateway DISCOVERS and LOADS the plugin\r\n * module (runs register(api), wiring the hooks).\r\n * NOTE: the entry is { enabled } only — a per-entry\r\n * `module` field is rejected by `openclaw config\r\n * validate`, so discovery goes through the\r\n * plugins.load.paths dir scan instead; and\r\n * (b) mcp.servers.<id> — nested under a top-level \"mcp\" object, so the\r\n * plugin's MCP tools SURFACE to the agent.\r\n *\r\n * If only (a) is present, the plugin loads but ZERO tools reach the agent.\r\n * If only (b) is present, the tools are declared but the hook bridge never\r\n * loads. context-mode's own adapter proved this empirically against OpenClaw\r\n * 2026.4.22 (src/adapters/openclaw/index.ts checkPluginRegistration: \"in\r\n * plugins.entries but missing from mcp.servers — plugin loads but no ctx_*\r\n * tools reach the agent\"). So getHealthChecks() FAILS when the two are\r\n * inconsistent (entries XOR mcp.servers).\r\n *\r\n * Why we BRIDGE instead of importing handlers (mirrors the OpenCode adapter):\r\n * context-mode could load ITS OWN plugin in-process because the handler code\r\n * shipped with the package. agent-connector cannot: the connector's hook\r\n * handlers are arbitrary developer code we must not import into the gateway's\r\n * runtime (wrong cwd, wrong deps, version skew). So installHooks synthesizes a\r\n * tiny, fully self-contained ESM plugin module — { id, name, configSchema,\r\n * register(api) } — that imports NOTHING from agent-connector and, on each\r\n * hook firing, shells out to the ONE stable home binary's universal entrypoint\r\n * <homeBin> hook openclaw <event> --connector <id>\r\n * feeding the event payload on stdin and JSON.parsing the normalized\r\n * HookResponse back from stdout. Fail-open: any bridge error → no-op. The same\r\n * universal json-stdio dispatcher every other host uses thus serves OpenClaw.\r\n *\r\n * Plugin module shape (grounded in context-mode build/adapters/openclaw/plugin.js):\r\n * export default { id, name, configSchema, register(api) }\r\n * register(api) wires typed lifecycle hooks via api.on(event, handler):\r\n * before_tool_call → block (return { block, blockReason }) / rewrite args\r\n * (mutate event.params in place); deny+ask both block.\r\n * after_tool_call → observe (capture result/output).\r\n * session_start → record the real session id.\r\n * before_prompt_build → inject context (fires PER TURN; no blocking). ONE\r\n * handler serves two events via separate state: SessionStart\r\n * additionalContext via { appendSystemContext } ONCE (the\r\n * verified injection point — session_start itself returns no\r\n * context payload) AND UserPromptSubmit additionalContext via\r\n * { appendContext } EVERY turn (context-injection only — a\r\n * block/deny decision degrades to a no-op).\r\n * subagent_spawned / subagent_ended → observe the subagent lifecycle\r\n * (SubagentStart / SubagentStop; observe-only — no\r\n * decision or context payload).\r\n * api.on() is the correct API for typed lifecycle hooks; api.registerHook() is\r\n * for generic command hooks. We register defensively (api.on present check).\r\n *\r\n * Config file (JSON5 / JSONC — PARSE TOLERANTLY):\r\n * user scope → resolveOpenClawConfigPath():\r\n * $OPENCLAW_CONFIG_PATH\r\n * else $OPENCLAW_STATE_DIR/openclaw.json\r\n * else ~/.openclaw/openclaw.json\r\n * (mirrors context-mode openclawConfigPath — the file the\r\n * gateway actually loads, NOT process.cwd()).\r\n * project scope → <projectDir>/openclaw.json (also supported).\r\n * openclaw.json may contain comments + trailing commas (JSON5), so we NEVER\r\n * use strict JSON.parse — readJson is overridden to strip comments/commas\r\n * first via the shared core/jsonc parseJsonc. writes are strict JSON\r\n * (idempotent, comment-free).\r\n *\r\n * Plugin module location (project | user):\r\n * user → <stateDir>/extensions/<id>/index.mjs (stateDir = dir of openclaw.json)\r\n * project → <projectDir>/.openclaw/extensions/<id>/index.mjs\r\n * Beside index.mjs we also emit an openclaw.plugin.json manifest\r\n * { id, name, main, enabled, configSchema }. `configSchema` is REQUIRED —\r\n * `openclaw config validate` rejects a manifest without it (\"plugin manifest\r\n * requires configSchema\"); our plugin takes no user config, so it is the empty\r\n * closed object schema { type: \"object\", additionalProperties: false }. The\r\n * plugin's DIRECTORY (not the file) is added to plugins.load.paths so the\r\n * gateway scans it and loads the module; the plugins.entries.<id> =\r\n * { enabled: true } half then activates it. (There is no per-entry \"module\"\r\n * field — validate rejects it.)\r\n *\r\n * The gateway hot-reloads its config on SIGUSR1 — no action needed here; the\r\n * next reload (or restart) picks up our entries/servers/module.\r\n *\r\n * Capabilities (per OpenClaw hook API):\r\n * preToolUse / postToolUse / sessionStart / userPromptSubmit / subagentStart /\r\n * subagentStop true; supportsNativeHooks true; transports [\"stdio\"].\r\n * before_tool_call mutates event.params → canModifyArgs true.\r\n * after_tool_call cannot rewrite the tool result → canModifyOutput false.\r\n * before_prompt_build injects context → canInjectSessionContext true; it has\r\n * no blocking ability, so userPromptSubmit is context-injection-only.\r\n */\r\n\r\nimport { existsSync, readFileSync } from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { dirname, join, resolve } from \"node:path\";\r\n\r\nimport { BaseAdapter } from \"../base.js\";\r\nimport type {\r\n Adapter,\r\n GeneratedPluginFile,\r\n HookReply,\r\n InstallContext,\r\n MemoryTarget,\r\n NormalizedEvent,\r\n} from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookEventName,\r\n HookParadigm,\r\n HookResponse,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PostToolUseEvent,\r\n PreToolUseEvent,\r\n ServerDef,\r\n SessionStartEvent,\r\n SubagentStartEvent,\r\n SubagentStopEvent,\r\n UserPromptSubmitEvent,\r\n} from \"../../core/types.js\";\r\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\r\nimport { parseJsonc } from \"../../core/jsonc.js\";\r\nimport {\r\n buildWrappedStdio,\r\n} from \"../../core/spawn.js\";\r\nimport { renderSkillMd } from \"../claude-code/render.js\";\r\n\r\nconst HOST: PlatformId = \"openclaw\";\r\n\r\n/**\r\n * MCP servers live nested under the top-level \"mcp\" object: mcp.servers.<id>\r\n * (NOT a top-level \"mcpServers\" key). plugins.entries.<id> is the second half of\r\n * the dual registration.\r\n */\r\nconst MCP_ROOT_KEY = \"mcp\";\r\nconst MCP_SERVERS_KEY = \"servers\";\r\nconst PLUGINS_KEY = \"plugins\";\r\nconst PLUGINS_ENTRIES_KEY = \"entries\";\r\n/**\r\n * The gateway discovers a synthesized plugin module by scanning the directories\r\n * listed in plugins.load.paths — it does NOT take a per-entry `module` field\r\n * (that fails `openclaw config validate`). We add the plugin's own dir here.\r\n */\r\nconst PLUGINS_LOAD_KEY = \"load\";\r\nconst PLUGINS_LOAD_PATHS_KEY = \"paths\";\r\n/** Manifest the gateway reads inside a plugins.load.paths directory. */\r\nconst PLUGIN_MANIFEST_FILE = \"openclaw.plugin.json\";\r\n\r\n/**\r\n * Canonical → OpenClaw event name map. A connector hook event is only emitted by\r\n * the generated plugin when it appears here AND is declared by the connector.\r\n *\r\n * SessionStart maps to OpenClaw's session_start (to learn the real session id)\r\n * plus before_prompt_build (the verified context-injection point — session_start\r\n * itself returns no context payload upstream).\r\n *\r\n * UserPromptSubmit also maps to before_prompt_build, which fires PER TURN (every\r\n * prompt build) and can ONLY inject context (prependContext / appendContext /\r\n * *SystemContext) — it has NO blocking ability. So UserPromptSubmit here is\r\n * CONTEXT-INJECTION-ONLY: a connector handler returning a block/deny decision\r\n * DEGRADES to a no-op (the generated bridge injects nothing). The two events\r\n * coexist in one before_prompt_build handler via separate state (the once-only\r\n * SessionStart flag must never suppress the per-turn UserPromptSubmit injection).\r\n */\r\nconst EVENT_TO_OPENCLAW: Partial<Record<HookEventName, string>> = {\r\n PreToolUse: \"before_tool_call\",\r\n PostToolUse: \"after_tool_call\",\r\n SessionStart: \"session_start\",\r\n // Per-turn context injection at prompt assembly (context-only, no blocking).\r\n UserPromptSubmit: \"before_prompt_build\",\r\n // Subagent lifecycle: OpenClaw observes launch + completion (no decision\r\n // control on either). PermissionRequest is deliberately ABSENT — OpenClaw's\r\n // permission gate is the requireApproval RETURN VALUE of before_tool_call,\r\n // not an observable event; PostToolUseFailure is ABSENT too (a failure\r\n // arrives merged into after_tool_call, already surfaced as PostToolUse\r\n // isError). Unmapped declared events are reported as \"unsupported here\".\r\n SubagentStart: \"subagent_spawned\",\r\n SubagentStop: \"subagent_ended\",\r\n};\r\n\r\n/** Raw payload the generated plugin posts to the universal hook entrypoint. */\r\ninterface OpenClawBridgePayload {\r\n toolName?: string;\r\n toolInput?: Record<string, unknown>;\r\n toolOutput?: string;\r\n isError?: boolean;\r\n sessionId?: string;\r\n source?: string;\r\n projectDir?: string;\r\n // UserPromptSubmit (before_prompt_build): the current prompt text.\r\n prompt?: string;\r\n // subagent_spawned / subagent_ended. The generated plugin projects the host's\r\n // REAL native fields here (verified against openclaw/openclaw\r\n // src/plugins/hook-types.ts): SubagentStart agentId←e.agentId,\r\n // agentType←e.label (the host's only descriptor); SubagentStop\r\n // agentId←e.targetSessionKey (its ONLY identity). The host emits no agentType\r\n // and no result/output on subagent_ended, so those slots are simply omitted —\r\n // we never post invented field names. Empty strings are dropped.\r\n agentId?: string;\r\n agentType?: string;\r\n lastAssistantMessage?: string;\r\n}\r\n\r\n/**\r\n * Native MCP server entry shapes OpenClaw accepts under mcp.servers.<id>.\r\n *\r\n * A stdio sidecar carries NO `transport` field: OpenClaw's config validator\r\n * only accepts `transport: \"sse\" | \"streamable-http\"` for remote servers and\r\n * REJECTS `transport: \"stdio\"` (verified against OpenClaw 2026.6.1). A stdio\r\n * server is INFERRED from the presence of `command`, so we must never write a\r\n * transport key here.\r\n */\r\ninterface OpenClawStdioServer {\r\n command: string;\r\n args?: string[];\r\n env?: Record<string, string>;\r\n enabled?: boolean;\r\n}\r\ninterface OpenClawRemoteServer {\r\n url: string;\r\n transport: \"sse\" | \"streamable-http\";\r\n headers?: Record<string, string>;\r\n enabled?: boolean;\r\n}\r\n\r\n/**\r\n * plugins.entries.<id> reference shape.\r\n *\r\n * `openclaw config validate` REJECTS a `module` field here (it is not part of\r\n * the entry schema), so the entry is `{ enabled: true }` ONLY. Discovery of the\r\n * synthesized module is wired separately via `plugins.load.paths` (the dir that\r\n * holds the module + its openclaw.plugin.json manifest).\r\n */\r\ninterface OpenClawPluginEntry {\r\n enabled: boolean;\r\n}\r\n\r\nexport class OpenClawAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n // Declared as `string` (not the literal \"OpenClaw\") so a fork adapter can\r\n // override it — NemoClaw extends this class (precedent: AntigravityAdapter,\r\n // whose name is likewise typed `string` for AntigravityCliAdapter).\r\n readonly name: string = \"OpenClaw\";\r\n readonly paradigm: HookParadigm = \"ts-plugin\";\r\n\r\n readonly capabilities: PlatformCapabilities = {\r\n // Memory surface: AGENTS.md-first managed block. OpenClaw's AGENTS.md\r\n // lives in the AGENT WORKSPACE (default ~/.openclaw/workspace), NOT the\r\n // user's repo — memoryTargets below maps BOTH scopes there (R2:\r\n // platforms force scopes; personal-assistant semantics, no project file).\r\n supportsMemory: true,\r\n preToolUse: true,\r\n postToolUse: true,\r\n // OpenClaw compaction is owned by a context engine, not wired through the\r\n // universal bridge here.\r\n preCompact: false,\r\n sessionStart: true,\r\n sessionEnd: false,\r\n // before_prompt_build fires per-turn and injects context (no blocking), so\r\n // UserPromptSubmit is supported as CONTEXT-INJECTION-ONLY: a block/deny\r\n // decision degrades to a no-op (it cannot stop the prompt here).\r\n userPromptSubmit: true,\r\n stop: false,\r\n notification: false,\r\n // Newer events: OpenClaw observes the full subagent lifecycle via\r\n // subagent_spawned / subagent_ended (observe-only — no decision control,\r\n // so a SubagentStop \"deny\" cannot keep the subagent running here).\r\n // permissionRequest / postToolUseFailure stay unset: the permission gate\r\n // is a RETURN VALUE of before_tool_call (not an event) and tool failures\r\n // arrive merged into after_tool_call. Install reports the standard\r\n // \"unsupported here\" for unmapped declared events.\r\n subagentStart: true,\r\n subagentStop: true,\r\n // before_tool_call mutates event.params in place → input rewrite supported.\r\n canModifyArgs: true,\r\n // after_tool_call observes but cannot rewrite the tool result upstream.\r\n canModifyOutput: false,\r\n // injected via before_prompt_build at prompt assembly.\r\n canInjectSessionContext: true,\r\n // stdio sidecars + remote servers. OpenClaw's validator accepts a remote\r\n // transport of \"sse\" | \"streamable-http\" (AC \"http\" → \"streamable-http\");\r\n // verified against OpenClaw 2026.6.1 + docs.openclaw.ai/gateway/configuration-reference.\r\n transports: [\"stdio\", \"sse\", \"http\"],\r\n // Content surface: OpenClaw follows the AgentSkills spec (dir-per-skill\r\n // SKILL.md with {name, description} frontmatter + body). User scope →\r\n // ~/.openclaw/skills (the `openclaw skills install --global` target);\r\n // project scope → the resolved <workspace>/skills (the highest-priority\r\n // documented skill root, the same workspace the memory surface anchors to).\r\n supportsSkills: true,\r\n // Action surface: each declared action becomes a user-invokable slash\r\n // command registered INSIDE the generated plugin module (api.registerCommand),\r\n // shelling out to the home-bin action verb with the install target baked as\r\n // the host token (this.id), so a fork like NemoClaw emits `action nemoclaw`.\r\n // supportsActions -> the installActions/uninstallActions overrides below.\r\n supportsActions: true,\r\n // Native passthrough: OpenClaw lifecycle hook names with no canonical analog\r\n // (declared on platforms.<id>.nativeHooks) are bridged verbatim by the\r\n // generated plugin via the same on(...) helper → home-bin runNativeHook\r\n // dispatch (mirrors omp/opencode). Independent of hooks:false.\r\n supportsNativeHooks: true,\r\n };\r\n\r\n // ── Tolerant JSON5/JSONC read (override base strict JSON.parse) ──────────\r\n\r\n /**\r\n * openclaw.json is officially JSON5 (comments + trailing commas allowed). A\r\n * strict JSON.parse false-fails a perfectly valid commented file, so we strip\r\n * comments and trailing commas before parsing. Writes remain strict JSON\r\n * (writeJson in BaseAdapter), which is itself valid JSON5 — so round-trips\r\n * stay safe and idempotent.\r\n */\r\n protected override readJson<T = Record<string, unknown>>(path: string): T | null {\r\n if (!existsSync(path)) return null;\r\n let raw: string;\r\n try {\r\n raw = readFileSync(path, \"utf8\");\r\n } catch {\r\n return null;\r\n }\r\n try {\r\n // Shared, string-aware JSONC stripper (the local stripJsonish whose\r\n // trailing-comma regex corrupted in-string \",]\"/\",}\"-like values is gone).\r\n return parseJsonc<T>(raw);\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n // ── Memory surface: agent-workspace AGENTS.md (both scopes) ─────────────\r\n /**\r\n * OpenClaw loads AGENTS.md (operating instructions) from its AGENT WORKSPACE\r\n * every session — `agents.defaults.workspace` in openclaw.json when set,\r\n * else `<stateDir>/workspace` (default ~/.openclaw/workspace). That file is\r\n * personal-assistant state, NOT a repo file, so a \"project scope\" write does\r\n * not apply: BOTH scopes map to the workspace (R2: platforms force scopes),\r\n * with the reason string flagging the semantics.\r\n */\r\n protected override memoryTargets(ctx: InstallContext): MemoryTarget[] {\r\n if (this.memoryOverride(ctx)?.path) return super.memoryTargets(ctx);\r\n if (ctx.scope !== \"project\" && ctx.scope !== \"user\") return [];\r\n return [\r\n {\r\n path: join(this.resolveWorkspace(), \"AGENTS.md\"),\r\n reason:\r\n \"openclaw agent-workspace AGENTS.md (workspace-scoped operating instructions, \" +\r\n \"not the project repo — both scopes map here)\",\r\n },\r\n ];\r\n }\r\n\r\n /**\r\n * Resolve OpenClaw's AGENT WORKSPACE — `agents.defaults.workspace` in\r\n * openclaw.json when set, else `<stateDir>/workspace` (default\r\n * ~/.openclaw/workspace). The same root the memory surface and the\r\n * project-scope skills surface both anchor to, so they never diverge.\r\n */\r\n private resolveWorkspace(): string {\r\n const configPath = resolveOpenClawConfigPath();\r\n const cfg = this.readJson<{ agents?: { defaults?: { workspace?: unknown } } }>(configPath);\r\n const declared = cfg?.agents?.defaults?.workspace;\r\n return typeof declared === \"string\" && declared.trim() !== \"\"\r\n ? declared.startsWith(\"~\")\r\n ? resolve(homedir(), declared.replace(/^~[/\\\\]?/, \"\"))\r\n : resolve(declared)\r\n : join(dirname(configPath), \"workspace\");\r\n }\r\n\r\n // ── Detection ──────────────────────────────────────────────────────────\r\n\r\n detectInstalled(projectDir: string): DetectedPlatform {\r\n const userConfig = resolveOpenClawConfigPath();\r\n const userDir = join(homedir(), \".openclaw\");\r\n const projectConfig = join(projectDir, \"openclaw.json\");\r\n\r\n // Fork-before-parent at DETECTION: NemoClaw DRIVES ~/.openclaw/openclaw.json,\r\n // so on a NemoClaw box (its ~/.nemoclaw/ marker present) the user-scope\r\n // OpenClaw config is nemoclaw-OWNED — defer so the planner does not double-\r\n // target the shared file as two platforms (an `uninstall openclaw` would\r\n // otherwise strip the connector's entries out from under nemoclaw). The\r\n // NemoClaw adapter overrides detectInstalled, so it is unaffected by this.\r\n const deferToNemoClaw = existsSync(join(homedir(), \".nemoclaw\"));\r\n const userInstalled =\r\n !deferToNemoClaw && (existsSync(userConfig) || existsSync(userDir));\r\n const projInstalled = existsSync(projectConfig);\r\n const installed = userInstalled || projInstalled;\r\n\r\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\r\n const configPath = scope === \"project\" ? projectConfig : userConfig;\r\n\r\n return {\r\n id: this.id,\r\n name: this.name,\r\n installed,\r\n paradigm: this.paradigm,\r\n capabilities: this.capabilities,\r\n configPath,\r\n scope,\r\n reason: installed\r\n ? scope === \"project\"\r\n ? `found project OpenClaw config at ${projectConfig}`\r\n : `found OpenClaw config at ${configPath}`\r\n : `no OpenClaw config at ${userConfig} or ${userDir}`,\r\n confidence: installed ? \"high\" : \"low\",\r\n };\r\n }\r\n\r\n // ── Native paths ───────────────────────────────────────────────────────\r\n\r\n getConfigDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? ctx.projectDir\r\n : dirname(resolveOpenClawConfigPath());\r\n }\r\n\r\n getServerConfigPath(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \"openclaw.json\")\r\n : resolveOpenClawConfigPath();\r\n }\r\n\r\n /**\r\n * For ts-plugin hosts the \"hook config path\" is the generated plugin FILE.\r\n * OpenClaw additionally needs the plugins.entries reference written into\r\n * openclaw.json (handled in installHooks); this is the module on disk.\r\n */\r\n getHookConfigPath(ctx: InstallContext): string {\r\n return join(this.pluginDir(ctx), this.pluginFileName());\r\n }\r\n\r\n /** Extensions directory the synthesized plugin module is written into. */\r\n private pluginDir(ctx: InstallContext): string {\r\n const base =\r\n ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".openclaw\")\r\n : dirname(resolveOpenClawConfigPath());\r\n return join(base, \"extensions\", ctx.connector.id);\r\n }\r\n\r\n /** Plugin module file name (ESM so the gateway loads it without a loader). */\r\n private pluginFileName(): string {\r\n return \"index.mjs\";\r\n }\r\n\r\n // ── MCP server install / uninstall (mcp.servers.<id>) ───────────────────\r\n\r\n installServer(ctx: InstallContext): ChangeRecord[] {\r\n const server = this.effectiveServer(ctx);\r\n const serverPath = this.getServerConfigPath(ctx);\r\n\r\n if (!server) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: serverPath,\r\n detail: ctx.connector.server\r\n ? \"server registration disabled for openclaw\"\r\n : \"connector declares no MCP server\",\r\n },\r\n ];\r\n }\r\n\r\n const entry = this.renderServerEntry(ctx, server);\r\n return [this.upsertNestedServer(serverPath, ctx.connector.id, entry, ctx.dryRun)];\r\n }\r\n\r\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\r\n const serverPath = this.getServerConfigPath(ctx);\r\n return [this.removeNestedServer(serverPath, ctx.connector.id, ctx.dryRun)];\r\n }\r\n\r\n /** Resolve the per-platform server override into an effective ServerDef. */\r\n private effectiveServer(ctx: InstallContext): ServerDef | undefined {\r\n const override = ctx.connector.platforms[this.id]?.server;\r\n if (override === false) return undefined;\r\n const base = ctx.connector.server;\r\n if (!base) return undefined;\r\n return override && typeof override === \"object\" ? { ...base, ...override } : base;\r\n }\r\n\r\n /**\r\n * Render a normalized ServerDef into OpenClaw's native mcp.servers.<id> entry.\r\n *\r\n * stdio → { command, args?, env? } (NO transport key — a stdio sidecar is\r\n * inferred from `command`; OpenClaw's validator rejects\r\n * transport:\"stdio\" and only accepts \"sse\"/\"streamable-http\")\r\n * remote → { url, transport: \"sse\"|\"streamable-http\", headers? }\r\n *\r\n * OpenClaw documents no native ${env:VAR} token, so refs resolve to literals.\r\n */\r\n private renderServerEntry(\r\n ctx: InstallContext,\r\n server: ServerDef,\r\n ): OpenClawStdioServer | OpenClawRemoteServer {\r\n if (server.transport === \"stdio\") {\r\n let command = server.command ?? \"\";\r\n let args = [...(server.args ?? [])];\r\n\r\n // Transparent telemetry wrapping: route the real command through\r\n // `<homeBin> serve --connector <id> -- <command> <args...>`.\r\n ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\r\n\r\n // NO `transport` key: OpenClaw infers a stdio sidecar from `command` and\r\n // its validator rejects transport:\"stdio\" (only \"sse\"/\"streamable-http\").\r\n const entry: OpenClawStdioServer = {\r\n command: resolveEnvRefsDeep(command),\r\n };\r\n const resolvedArgs = resolveEnvRefsDeep(args).filter((s) => s !== \"\");\r\n if (resolvedArgs.length > 0) entry.args = resolvedArgs;\r\n const env = this.renderEnv(server.env);\r\n if (env) entry.env = env;\r\n if (server.enabled === false) entry.enabled = false;\r\n return entry;\r\n }\r\n\r\n // sse / http → OpenClaw registers a remote URL sidecar. AC's canonical\r\n // \"http\" (streamable HTTP) maps to OpenClaw's literal \"streamable-http\" — its\r\n // validator rejects a bare \"http\" transport value.\r\n const transport: \"sse\" | \"streamable-http\" =\r\n server.transport === \"http\" ? \"streamable-http\" : \"sse\";\r\n const entry: OpenClawRemoteServer = {\r\n url: resolveEnvRefsDeep(server.url ?? \"\"),\r\n transport,\r\n };\r\n if (server.headers && Object.keys(server.headers).length > 0) {\r\n entry.headers = resolveEnvRefsDeep({ ...server.headers });\r\n }\r\n if (server.enabled === false) entry.enabled = false;\r\n return entry;\r\n }\r\n\r\n /** Resolve every ${env:VAR} in env values to literals (no native token). */\r\n private renderEnv(\r\n env: Record<string, string> | undefined,\r\n ): Record<string, string> | undefined {\r\n if (!env || Object.keys(env).length === 0) return undefined;\r\n return resolveEnvRefsDeep({ ...env });\r\n }\r\n\r\n /** Upsert config.mcp.servers.<id> idempotently. */\r\n private upsertNestedServer(\r\n path: string,\r\n id: string,\r\n entry: unknown,\r\n dryRun: boolean,\r\n ): ChangeRecord {\r\n // OVERWRITE GUARD: never blank a present-but-unparseable openclaw.json.\r\n if (this.isPresentButUnparseable(path)) {\r\n return {\r\n platform: this.id,\r\n action: \"warn\",\r\n path,\r\n detail: `existing ${path} is not parseable; left untouched (back it up / fix it, then re-run)`,\r\n };\r\n }\r\n const cfg = this.readJson<Record<string, unknown>>(path) ?? {};\r\n const servers = this.nestedServersBucket(cfg);\r\n const before = JSON.stringify(servers[id]);\r\n const after = JSON.stringify(entry);\r\n let action: ChangeRecord[\"action\"];\r\n if (before === undefined) action = \"create\";\r\n else if (before === after) action = \"skip\";\r\n else action = \"update\";\r\n if (action !== \"skip\") {\r\n servers[id] = entry;\r\n this.writeJson(path, cfg, dryRun);\r\n }\r\n return {\r\n platform: this.id,\r\n action,\r\n path,\r\n detail: `${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id}`,\r\n };\r\n }\r\n\r\n /** Remove config.mcp.servers.<id>. */\r\n private removeNestedServer(path: string, id: string, dryRun: boolean): ChangeRecord {\r\n const cfg = this.readJson<Record<string, unknown>>(path);\r\n const mcp = cfg?.[MCP_ROOT_KEY];\r\n const servers =\r\n mcp && typeof mcp === \"object\" && !Array.isArray(mcp)\r\n ? ((mcp as Record<string, unknown>)[MCP_SERVERS_KEY] as\r\n | Record<string, unknown>\r\n | undefined)\r\n : undefined;\r\n if (!cfg || !servers || !(id in servers)) {\r\n return {\r\n platform: this.id,\r\n action: \"skip\",\r\n path,\r\n detail: `${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id} absent`,\r\n };\r\n }\r\n delete servers[id];\r\n this.writeJson(path, cfg, dryRun);\r\n return {\r\n platform: this.id,\r\n action: \"remove\",\r\n path,\r\n detail: `${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id}`,\r\n };\r\n }\r\n\r\n /** Get-or-create config.mcp.servers as a mutable object. */\r\n private nestedServersBucket(cfg: Record<string, unknown>): Record<string, unknown> {\r\n let mcp = cfg[MCP_ROOT_KEY];\r\n if (!mcp || typeof mcp !== \"object\" || Array.isArray(mcp)) {\r\n mcp = {};\r\n cfg[MCP_ROOT_KEY] = mcp;\r\n }\r\n const mcpObj = mcp as Record<string, unknown>;\r\n let servers = mcpObj[MCP_SERVERS_KEY];\r\n if (!servers || typeof servers !== \"object\" || Array.isArray(servers)) {\r\n servers = {};\r\n mcpObj[MCP_SERVERS_KEY] = servers;\r\n }\r\n return servers as Record<string, unknown>;\r\n }\r\n\r\n // ── Hook install / uninstall (ts-plugin + DUAL REGISTRATION) ────────────\r\n\r\n installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const pluginPath = this.getHookConfigPath(ctx);\r\n\r\n // `hooks: false` disables only the CANONICAL (normalized) events; nativeHooks\r\n // is a sibling, openclaw-scoped declaration that SURVIVES the opt-out (mirrors\r\n // omp/opencode). The canonicalOff guard in buildPluginSource still suppresses\r\n // the canonical api.on handlers, so a hooks:false + nativeHooks connector\r\n // wires ONLY the native passthrough.\r\n const canonicalDisabled = ctx.connector.platforms[this.id]?.hooks === false;\r\n const hasCanonical = !canonicalDisabled && ctx.connector.hookEvents.length > 0;\r\n const hasNative = this.nativeHookEvents(ctx).length > 0;\r\n\r\n // Empty-events gate: the plugin module is SHARED with the action surface, so\r\n // a connector with actions-but-no-hooks still needs it written + registered —\r\n // but that is installActions' job (it calls ensurePlugin too). installHooks\r\n // only writes when there ARE hooks (canonical or native); its skip detail\r\n // stays honest about why.\r\n if (!hasCanonical && !hasNative) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: pluginPath,\r\n detail: canonicalDisabled\r\n ? `hooks disabled for ${this.id}`\r\n : this.actionTriggers(ctx).length > 0\r\n ? \"no hooks (plugin written for actions)\"\r\n : \"connector declares no hooks\",\r\n },\r\n ];\r\n }\r\n\r\n return this.ensurePlugin(ctx, `openclaw plugin module (${this.hookDetail(ctx)})`);\r\n }\r\n\r\n // ── Action install / uninstall (slash commands in the shared plugin) ────\r\n\r\n /**\r\n * Each declared action becomes an `api.registerCommand(...)` inside the SAME\r\n * synthesized plugin module (see buildPluginSource). So installActions ensures\r\n * that shared module is on disk AND dual-registered: when hooks already wrote\r\n * the identical bytes the content-compare yields \"skip\" (no double material\r\n * write), and an actions-only connector gets the plugin written + registered\r\n * here. Honors platforms.<id>.actions === false and the empty-actions skip.\r\n */\r\n override installActions(ctx: InstallContext): ChangeRecord[] {\r\n const pluginPath = this.getHookConfigPath(ctx);\r\n if (ctx.connector.platforms[this.id]?.actions === false) {\r\n return [\r\n { platform: this.id, action: \"skip\", path: pluginPath, detail: `actions disabled for ${this.id}` },\r\n ];\r\n }\r\n if (this.actionTriggers(ctx).length === 0) {\r\n return [\r\n { platform: this.id, action: \"skip\", path: pluginPath, detail: \"connector declares no actions\" },\r\n ];\r\n }\r\n return this.ensurePlugin(ctx, \"openclaw plugin module (action commands)\");\r\n }\r\n\r\n /**\r\n * Actions live INSIDE the shared plugin module, which uninstallHooks removes\r\n * (file + manifest + dual-registration entry) unconditionally — it runs last\r\n * in the teardown pass. Removing the plugin here too would race uninstallHooks,\r\n * so this is a single informational skip.\r\n */\r\n override uninstallActions(ctx: InstallContext): ChangeRecord[] {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: this.getHookConfigPath(ctx),\r\n detail: \"action commands are embedded in the plugin module; removed by uninstallHooks\",\r\n },\r\n ];\r\n }\r\n\r\n /**\r\n * Synthesize + content-compare-write the plugin module(s) AND upsert the dual-\r\n * registration entry. Shared by installHooks and installActions: the generator\r\n * is a pure function of ctx, so whichever surface runs second sees byte-\r\n * identical files (and an already-present entry) and reports \"skip\" — no double\r\n * material write. `moduleDetail` flavors the module ChangeRecord per surface.\r\n */\r\n private ensurePlugin(ctx: InstallContext, moduleDetail: string): ChangeRecord[] {\r\n const configPath = this.getServerConfigPath(ctx);\r\n const changes: ChangeRecord[] = [];\r\n\r\n // 1. Write the synthesized plugin module(s).\r\n for (const file of this.synthesizePlugin(ctx)) {\r\n changes.push(\r\n this.writeManagedFile(file.path, file.contents, ctx.dryRun, moduleDetail, file.executable),\r\n );\r\n }\r\n\r\n // 2. DUAL REGISTRATION half (a): enable plugins.entries.<id> AND add the\r\n // plugin DIR to plugins.load.paths so the gateway discovers + loads the\r\n // module. (half (b), mcp.servers.<id>, is written by installServer — both\r\n // are required for tools to reach the agent.)\r\n changes.push(this.upsertPluginEntry(configPath, ctx, this.pluginDir(ctx)));\r\n\r\n return changes;\r\n }\r\n\r\n /**\r\n * Human-facing summary of which declared events the synthesized module ACTUALLY\r\n * wires. Only events present in EVENT_TO_OPENCLAW are mapped/wired; any declared\r\n * event with no OpenClaw mapping (e.g. UserPromptSubmit) is reported separately\r\n * as \"unsupported here\" so the detail never overstates coverage.\r\n */\r\n private hookDetail(ctx: InstallContext): string {\r\n const declared =\r\n ctx.connector.platforms[this.id]?.hooks === false\r\n ? []\r\n : ctx.connector.hookEvents;\r\n const mapped = declared.filter((e) => EVENT_TO_OPENCLAW[e] !== undefined);\r\n const unsupported = declared.filter((e) => EVENT_TO_OPENCLAW[e] === undefined);\r\n const native = this.nativeHookEvents(ctx);\r\n const parts: string[] = [];\r\n if (mapped.length > 0) parts.push(mapped.join(\",\"));\r\n if (native.length > 0) parts.push(`native: ${native.join(\",\")}`);\r\n const base = parts.join(\"; \") || \"no canonical hooks\";\r\n return unsupported.length > 0\r\n ? `${base}; unsupported here: ${unsupported.join(\",\")}`\r\n : base;\r\n }\r\n\r\n /**\r\n * OpenClaw-native passthrough events declared on platforms.<id>.nativeHooks\r\n * (host lifecycle hook names with no canonical HookEventName). Emitted as\r\n * generic on(...) bridge registrations and dispatched host-generically by the\r\n * home-bin's runNativeHook. Independent of hooks:false (supportsNativeHooks).\r\n */\r\n private nativeHookEvents(ctx: InstallContext): string[] {\r\n return Object.keys(ctx.connector.platforms[this.id]?.nativeHooks ?? {});\r\n }\r\n\r\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\r\n const pluginPath = this.getHookConfigPath(ctx);\r\n const configPath = this.getServerConfigPath(ctx);\r\n const changes: ChangeRecord[] = [];\r\n\r\n // 1. Remove the plugins.entries.<id> reference AND drop the plugin dir from\r\n // plugins.load.paths.\r\n changes.push(\r\n this.removePluginEntry(configPath, ctx.connector.id, this.pluginDir(ctx), ctx.dryRun),\r\n );\r\n\r\n // 2. Remove the plugin module + its manifest on disk.\r\n const manifestPath = join(this.pluginDir(ctx), PLUGIN_MANIFEST_FILE);\r\n for (const [path, label] of [\r\n [pluginPath, \"openclaw plugin module\"],\r\n [manifestPath, \"openclaw plugin manifest\"],\r\n ] as const) {\r\n if (existsSync(path)) {\r\n changes.push(this.removeManagedFile(path, ctx.dryRun, label, `no ${label} present`));\r\n } else {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path,\r\n detail: `no ${label} present`,\r\n });\r\n }\r\n }\r\n\r\n // 3. Drop the now-empty plugin dir (only if WE own its full contents).\r\n changes.push(this.removeDirIfEmpty(this.pluginDir(ctx), ctx.dryRun));\r\n\r\n return changes;\r\n }\r\n\r\n /**\r\n * Upsert plugins.entries.<id> = { enabled: true } AND add `pluginDir` to\r\n * plugins.load.paths idempotently.\r\n *\r\n * `openclaw config validate` rejects a per-entry `module` field, so the entry\r\n * carries ONLY `{ enabled: true }`. The gateway discovers the synthesized\r\n * module by scanning the directories in plugins.load.paths (the dir holding\r\n * index.mjs + openclaw.plugin.json), so the dir is added there.\r\n */\r\n private upsertPluginEntry(\r\n configPath: string,\r\n ctx: InstallContext,\r\n pluginDir: string,\r\n ): ChangeRecord {\r\n // OVERWRITE GUARD: never blank a present-but-unparseable openclaw.json.\r\n if (this.isPresentButUnparseable(configPath)) {\r\n return {\r\n platform: this.id,\r\n action: \"warn\",\r\n path: configPath,\r\n detail: `existing ${configPath} is not parseable; left untouched (back it up / fix it, then re-run)`,\r\n };\r\n }\r\n const cfg = this.readJson<Record<string, unknown>>(configPath) ?? {};\r\n const entries = this.pluginEntriesBucket(cfg);\r\n const id = ctx.connector.id;\r\n const desired: OpenClawPluginEntry = { enabled: true };\r\n const entryBefore = JSON.stringify(entries[id]);\r\n const entryAfter = JSON.stringify(desired);\r\n\r\n // plugins.load.paths: add the plugin dir if absent.\r\n const loadPaths = this.pluginLoadPathsBucket(cfg);\r\n const pathPresent = loadPaths.includes(pluginDir);\r\n\r\n const entryChanged = entryBefore !== entryAfter;\r\n const pathChanged = !pathPresent;\r\n let action: ChangeRecord[\"action\"];\r\n if (entryBefore === undefined) action = \"create\";\r\n else if (!entryChanged && !pathChanged) action = \"skip\";\r\n else action = \"update\";\r\n\r\n if (action !== \"skip\") {\r\n entries[id] = desired;\r\n if (!pathPresent) loadPaths.push(pluginDir);\r\n this.writeJson(configPath, cfg, ctx.dryRun);\r\n }\r\n return {\r\n platform: this.id,\r\n action,\r\n path: configPath,\r\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id} + ${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}`,\r\n };\r\n }\r\n\r\n /** Remove plugins.entries.<id> AND drop `pluginDir` from plugins.load.paths. */\r\n private removePluginEntry(\r\n configPath: string,\r\n id: string,\r\n pluginDir: string,\r\n dryRun: boolean,\r\n ): ChangeRecord {\r\n const cfg = this.readJson<Record<string, unknown>>(configPath);\r\n const plugins = cfg?.[PLUGINS_KEY];\r\n const pluginsObj =\r\n plugins && typeof plugins === \"object\" && !Array.isArray(plugins)\r\n ? (plugins as Record<string, unknown>)\r\n : undefined;\r\n const entries = pluginsObj?.[PLUGINS_ENTRIES_KEY] as\r\n | Record<string, unknown>\r\n | undefined;\r\n const load = pluginsObj?.[PLUGINS_LOAD_KEY] as Record<string, unknown> | undefined;\r\n const loadPaths =\r\n load && Array.isArray(load[PLUGINS_LOAD_PATHS_KEY])\r\n ? (load[PLUGINS_LOAD_PATHS_KEY] as unknown[])\r\n : undefined;\r\n\r\n const entryPresent = Boolean(entries && id in entries);\r\n const pathIdx = loadPaths ? loadPaths.indexOf(pluginDir) : -1;\r\n\r\n if (!cfg || (!entryPresent && pathIdx < 0)) {\r\n return {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: configPath,\r\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id} absent`,\r\n };\r\n }\r\n if (entryPresent && entries) delete entries[id];\r\n if (loadPaths && pathIdx >= 0) loadPaths.splice(pathIdx, 1);\r\n this.writeJson(configPath, cfg, dryRun);\r\n return {\r\n platform: this.id,\r\n action: \"remove\",\r\n path: configPath,\r\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id} + ${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}`,\r\n };\r\n }\r\n\r\n /** Get-or-create config.plugins.entries as a mutable object. */\r\n private pluginEntriesBucket(cfg: Record<string, unknown>): Record<string, unknown> {\r\n let plugins = cfg[PLUGINS_KEY];\r\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) {\r\n plugins = {};\r\n cfg[PLUGINS_KEY] = plugins;\r\n }\r\n const pluginsObj = plugins as Record<string, unknown>;\r\n let entries = pluginsObj[PLUGINS_ENTRIES_KEY];\r\n if (!entries || typeof entries !== \"object\" || Array.isArray(entries)) {\r\n entries = {};\r\n pluginsObj[PLUGINS_ENTRIES_KEY] = entries;\r\n }\r\n return entries as Record<string, unknown>;\r\n }\r\n\r\n /** Get-or-create config.plugins.load.paths as a mutable string array. */\r\n private pluginLoadPathsBucket(cfg: Record<string, unknown>): string[] {\r\n let plugins = cfg[PLUGINS_KEY];\r\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) {\r\n plugins = {};\r\n cfg[PLUGINS_KEY] = plugins;\r\n }\r\n const pluginsObj = plugins as Record<string, unknown>;\r\n let load = pluginsObj[PLUGINS_LOAD_KEY];\r\n if (!load || typeof load !== \"object\" || Array.isArray(load)) {\r\n load = {};\r\n pluginsObj[PLUGINS_LOAD_KEY] = load;\r\n }\r\n const loadObj = load as Record<string, unknown>;\r\n const existing = loadObj[PLUGINS_LOAD_PATHS_KEY];\r\n // Normalize to a fresh string[] (drop any non-string a user may have placed\r\n // here) and write it back so the caller mutates the live array.\r\n const paths = Array.isArray(existing)\r\n ? existing.filter((p): p is string => typeof p === \"string\")\r\n : [];\r\n loadObj[PLUGINS_LOAD_PATHS_KEY] = paths;\r\n return paths;\r\n }\r\n\r\n // ── Content surface: skills (dir-per-skill SKILL.md) ────────────────────\r\n // CONTENT-ONLY: pure native-file writers, mirroring codex/claude-code\r\n // installSkills + renderSkill. No runtime dispatch, no home-bin pointer, no\r\n // telemetry wrap. Each write is idempotent (byte-identical → skip) via\r\n // writeContentFile and reversible via removeContentFile + removeDirIfEmpty\r\n // (never rm -rf a shared dir). Honors platforms[\"openclaw\"].skills === false.\r\n //\r\n // Native locations (AgentSkills spec — dir-per-skill SKILL.md + resources):\r\n // user → ~/.openclaw/skills/<name>/SKILL.md (`openclaw skills install\r\n // --global` targets this dir)\r\n // project → <workspace>/skills/<name>/SKILL.md (the highest-priority\r\n // documented skill root; <workspace> is the SAME root the memory\r\n // surface resolves, so both content surfaces stay consistent).\r\n\r\n /**\r\n * Per-skill directory for ctx.scope:\r\n * user → ~/.openclaw/skills/<name> (`--global` install target)\r\n * project → <workspace>/skills/<name> (resolved agent workspace —\r\n * consistent with this adapter's memoryTargets resolution).\r\n */\r\n private skillDir(ctx: InstallContext, name: string): string {\r\n const base =\r\n ctx.scope === \"project\"\r\n ? join(this.resolveWorkspace(), \"skills\")\r\n : join(dirname(resolveOpenClawConfigPath()), \"skills\");\r\n return join(base, name);\r\n }\r\n\r\n override installSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[this.id]?.skills === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: `skills disabled for ${this.id}` }];\r\n }\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n changes.push(\r\n this.writeContentFile(join(dir, \"SKILL.md\"), renderSkillMd(skill), ctx.dryRun),\r\n );\r\n // Bundle any resource files beside SKILL.md (relative path → contents).\r\n // Defense-in-depth: skip+warn on any key that escapes the skill dir\r\n // (config-time validation already rejects these, but never trust input).\r\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\r\n });\r\n continue;\r\n }\r\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\r\n }\r\n }\r\n return changes;\r\n }\r\n\r\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n // Remove only the files we wrote (SKILL.md + declared resources), then the\r\n // skill dir itself when we own its full contents.\r\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\r\n for (const rel of Object.keys(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) continue; // never delete outside the skill dir\r\n changes.push(this.removeContentFile(target, ctx.dryRun));\r\n }\r\n // Only remove the skill dir when WE own its full contents — never rm -rf a\r\n // dir that still holds user-added / sibling-tool / shared files.\r\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\r\n }\r\n return changes;\r\n }\r\n\r\n // ── ts-plugin synthesis ────────────────────────────────────────────────\r\n\r\n /**\r\n * Build ONE self-contained ESM plugin module for OpenClaw.\r\n *\r\n * The module imports nothing from agent-connector. It embeds two constants\r\n * (the absolute home-bin path and the connector id) and a `bridge()` helper\r\n * that shells out to the universal hook entrypoint via execFileSync, feeding\r\n * the OpenClaw event payload on stdin and JSON.parsing the normalized\r\n * HookResponse back from stdout (fail-open: any error → null). Its default\r\n * export is the OpenClaw plugin definition { id, name, configSchema,\r\n * register(api) }; register wires api.on(...) handlers for the events this\r\n * connector actually declares.\r\n */\r\n synthesizePlugin(ctx: InstallContext): GeneratedPluginFile[] {\r\n const path = this.getHookConfigPath(ctx);\r\n const contents = this.buildPluginSource(ctx);\r\n // The gateway scans plugins.load.paths dirs for a manifest, so emit an\r\n // openclaw.plugin.json beside index.mjs pointing at the module entry.\r\n const manifestPath = join(this.pluginDir(ctx), PLUGIN_MANIFEST_FILE);\r\n const manifest = this.buildPluginManifest(ctx);\r\n return [\r\n { path, contents, executable: false },\r\n { path: manifestPath, contents: manifest, executable: false },\r\n ];\r\n }\r\n\r\n /**\r\n * Build the openclaw.plugin.json manifest the gateway reads inside a\r\n * plugins.load.paths directory. It names the plugin and points at the ESM\r\n * module entry (index.mjs) so the loader knows what to import.\r\n *\r\n * `configSchema` is REQUIRED: `openclaw config validate` (and `openclaw mcp\r\n * list`) REJECT a manifest without it (\"plugin manifest requires\r\n * configSchema\"). Our plugin takes no user config, so we emit the empty\r\n * closed object schema from openclaw's own minimal manifest example\r\n * ({ type: \"object\", additionalProperties: false }).\r\n */\r\n private buildPluginManifest(ctx: InstallContext): string {\r\n const manifest = {\r\n id: ctx.connector.id,\r\n name: ctx.connector.displayName || ctx.connector.id,\r\n main: this.pluginFileName(),\r\n enabled: true,\r\n configSchema: { type: \"object\", additionalProperties: false },\r\n };\r\n return `${JSON.stringify(manifest, null, 2)}\\n`;\r\n }\r\n\r\n /** Compose the generated plugin source with plain string concatenation. */\r\n private buildPluginSource(ctx: InstallContext): string {\r\n const homeBin = JSON.stringify(ctx.homeBinPath);\r\n const connectorId = JSON.stringify(ctx.connector.id);\r\n const connectorName = JSON.stringify(ctx.connector.displayName || ctx.connector.id);\r\n\r\n // `platforms[host].hooks === false` disables the CANONICAL events. The\r\n // plugin module may still be synthesized for ACTIONS (installActions writes\r\n // it independently of hooks), so the canonical handler set collapses to []\r\n // here rather than letting a live before_tool_call/after_tool_call gate leak\r\n // into a hooks-disabled plugin. Mirrors omp/opencode. Uses this.id so the\r\n // NemoClaw fork checks platforms.nemoclaw.hooks.\r\n const canonicalOff = ctx.connector.platforms[this.id]?.hooks === false;\r\n const events = canonicalOff\r\n ? []\r\n : ctx.connector.hookEvents.filter(\r\n (e): e is HookEventName => EVENT_TO_OPENCLAW[e] !== undefined,\r\n );\r\n const has = (e: HookEventName) => events.includes(e);\r\n\r\n const header =\r\n \"/**\\n\" +\r\n \" * AUTO-GENERATED by agent-connector — DO NOT EDIT.\\n\" +\r\n \" *\\n\" +\r\n \" * Self-contained OpenClaw plugin bridge for connector \" +\r\n ctx.connector.id +\r\n \".\\n\" +\r\n \" * It imports nothing from agent-connector: every hook invocation shells out\\n\" +\r\n \" * to the stable home binary's universal entrypoint and JSON-parses the\\n\" +\r\n \" * normalized response. Fail-open: any bridge error degrades to a no-op.\\n\" +\r\n \" *\\n\" +\r\n \" * Registered via DUAL REGISTRATION in openclaw.json: plugins.entries.<id>\\n\" +\r\n \" * (loads this module) + mcp.servers.<id> (surfaces the MCP tools). The\\n\" +\r\n \" * gateway hot-reloads its config on SIGUSR1.\\n\" +\r\n \" */\\n\" +\r\n 'import { execFileSync, execSync } from \"node:child_process\";\\n\\n' +\r\n \"const HOME_BIN = \" +\r\n homeBin +\r\n \";\\n\" +\r\n \"const CONNECTOR_ID = \" +\r\n connectorId +\r\n \";\\n\\n\" +\r\n \"/**\\n\" +\r\n \" * Invoke the universal hook entrypoint for one canonical event.\\n\" +\r\n \" * @param {string} event canonical (or native passthrough) event name\\n\" +\r\n \" * (PreToolUse|PostToolUse|SessionStart|UserPromptSubmit|SubagentStart|SubagentStop)\\n\" +\r\n \" * @param {object} payload OpenClaw-shaped payload posted on stdin\\n\" +\r\n \" * @returns {object|null} normalized HookResponse, or null on any failure\\n\" +\r\n \" */\\n\" +\r\n \"function bridge(event, payload) {\\n\" +\r\n \" try {\\n\" +\r\n \" // On Windows HOME_BIN is the agent-connector.cmd launcher: Node cannot\\n\" +\r\n \" // execFile a batch file, and shell+args is deprecated (DEP0190), so run\\n\" +\r\n \" // one quoted command line via a shell. POSIX keeps the direct execFile.\\n\" +\r\n ' const args = [\"hook\", ' + JSON.stringify(this.id) + ', event, \"--connector\", CONNECTOR_ID];\\n' +\r\n ' const opts = { input: JSON.stringify(payload), encoding: \"utf8\" };\\n' +\r\n \" const stdout =\\n\" +\r\n ' process.platform === \"win32\"\\n' +\r\n \" ? execSync([HOME_BIN, ...args].map((a) => '\\\"' + a + '\\\"').join(\\\" \\\"), opts)\\n\" +\r\n \" : execFileSync(HOME_BIN, args, opts);\\n\" +\r\n ' const text = (stdout || \"\").trim();\\n' +\r\n ' if (text === \"\") return { decision: \"allow\" };\\n' +\r\n \" return JSON.parse(text);\\n\" +\r\n \" } catch {\\n\" +\r\n \" // Fail-open — never wedge a tool call on a bridge error.\\n\" +\r\n \" return null;\\n\" +\r\n \" }\\n\" +\r\n \"}\\n\\n\" +\r\n \"// Module-scoped context the prompt-build hook injects at session start.\\n\" +\r\n \"// before_prompt_build is the verified injection point (session_start itself\\n\" +\r\n \"// returns no context payload). The flag prevents double-injection.\\n\" +\r\n \"let PROJECT_DIR = process.cwd();\\n\" +\r\n \"let SESSION_ID = \\\"\\\";\\n\" +\r\n \"let pendingContext = null;\\n\" +\r\n \"let contextInjected = false;\\n\\n\";\r\n\r\n // register(api) body — wire only the handlers this connector declares.\r\n const reg: string[] = [];\r\n\r\n reg.push(\r\n \" register(api) {\\n\" +\r\n \" PROJECT_DIR = process.cwd();\\n\" +\r\n \" const on = (event, handler) => {\\n\" +\r\n ' if (typeof api?.on === \"function\") {\\n' +\r\n \" try { api.on(event, handler); return; } catch { /* fall through */ }\\n\" +\r\n \" }\\n\" +\r\n ' if (typeof api?.registerHook === \"function\") {\\n' +\r\n \" try {\\n\" +\r\n ' api.registerHook(event, handler, { name: CONNECTOR_ID + \":\" + event, description: \"agent-connector bridge\" });\\n' +\r\n \" } catch { /* best effort */ }\\n\" +\r\n \" }\\n\" +\r\n \" };\\n\",\r\n );\r\n\r\n if (has(\"PreToolUse\")) {\r\n reg.push(\r\n \"\\n\" +\r\n \" // PreToolUse → before_tool_call: block (return { block, blockReason })\\n\" +\r\n \" // for deny/ask; rewrite args by mutating event.params in place for modify.\\n\" +\r\n ' on(\"before_tool_call\", async (event) => {\\n' +\r\n \" const e = event || {};\\n\" +\r\n \" const toolInput = e.params || {};\\n\" +\r\n \" const res = bridge(\\\"PreToolUse\\\", {\\n\" +\r\n ' toolName: e.toolName || \"\",\\n' +\r\n \" toolInput,\\n\" +\r\n \" sessionId: SESSION_ID,\\n\" +\r\n \" projectDir: PROJECT_DIR,\\n\" +\r\n \" });\\n\" +\r\n \" if (!res) return undefined;\\n\" +\r\n \" // OpenClaw has no separate ask gate — deny and ask both block (safe).\\n\" +\r\n ' if (res.decision === \"deny\" || res.decision === \"ask\") {\\n' +\r\n ' return { block: true, blockReason: res.reason || (\"Blocked by \" + CONNECTOR_ID) };\\n' +\r\n \" }\\n\" +\r\n ' if (res.decision === \"modify\" && res.updatedInput && typeof toolInput === \"object\") {\\n' +\r\n \" Object.assign(toolInput, res.updatedInput);\\n\" +\r\n \" }\\n\" +\r\n \" return undefined;\\n\" +\r\n \" });\\n\",\r\n );\r\n }\r\n\r\n if (has(\"PostToolUse\")) {\r\n reg.push(\r\n \"\\n\" +\r\n \" // PostToolUse → after_tool_call: observe the completed tool call.\\n\" +\r\n \" // OpenClaw exposes the result (v2+) or output (older builds); it cannot\\n\" +\r\n \" // rewrite the tool result, so updatedOutput is intentionally dropped.\\n\" +\r\n ' on(\"after_tool_call\", async (event) => {\\n' +\r\n \" const e = event || {};\\n\" +\r\n \" const rawResult = e.result !== undefined ? e.result : e.output;\\n\" +\r\n \" const toolOutput =\\n\" +\r\n ' typeof rawResult === \"string\"\\n' +\r\n \" ? rawResult\\n\" +\r\n \" : rawResult != null\\n\" +\r\n \" ? JSON.stringify(rawResult)\\n\" +\r\n \" : undefined;\\n\" +\r\n \" bridge(\\\"PostToolUse\\\", {\\n\" +\r\n ' toolName: e.toolName || \"\",\\n' +\r\n \" toolInput: e.params || {},\\n\" +\r\n \" toolOutput,\\n\" +\r\n \" isError: Boolean(e.error || e.isError),\\n\" +\r\n \" sessionId: SESSION_ID,\\n\" +\r\n \" projectDir: PROJECT_DIR,\\n\" +\r\n \" });\\n\" +\r\n \" return undefined;\\n\" +\r\n \" });\\n\",\r\n );\r\n }\r\n\r\n if (has(\"SubagentStart\")) {\r\n reg.push(\r\n \"\\n\" +\r\n \" // SubagentStart → subagent_spawned: observe-only (OpenClaw exposes no\\n\" +\r\n \" // decision or context-injection payload for subagent launches).\\n\" +\r\n \" // PRIMARY SOURCE (openclaw/openclaw src/plugins/hook-types.ts:720-806,\\n\" +\r\n \" // PluginHookSubagentSpawnedEvent = PluginHookSubagentSpawnBase): the\\n\" +\r\n \" // native event carries `agentId` (the agent identity) + an OPTIONAL\\n\" +\r\n \" // human `label` — and NO subagentId/subagentType/agent/agentType. So we\\n\" +\r\n \" // read the real e.agentId and surface the real e.label as the type-ish\\n\" +\r\n \" // descriptor; the former subagentId/subagentType/agent reads were\\n\" +\r\n \" // false-friends (host emits none). Empty values are omitted so the\\n\" +\r\n \" // dispatcher never matcher-filters on an empty descriptor (fail-open).\\n\" +\r\n ' on(\"subagent_spawned\", async (event) => {\\n' +\r\n \" const e = event || {};\\n\" +\r\n \" bridge(\\\"SubagentStart\\\", {\\n\" +\r\n \" agentId: e.agentId || undefined,\\n\" +\r\n \" agentType: e.label || undefined,\\n\" +\r\n \" sessionId: SESSION_ID,\\n\" +\r\n \" projectDir: PROJECT_DIR,\\n\" +\r\n \" });\\n\" +\r\n \" return undefined;\\n\" +\r\n \" });\\n\",\r\n );\r\n }\r\n\r\n if (has(\"SubagentStop\")) {\r\n reg.push(\r\n \"\\n\" +\r\n \" // SubagentStop → subagent_ended: observe-only completion. A normalized\\n\" +\r\n \" // 'deny' cannot keep the subagent running on OpenClaw, so the bridge\\n\" +\r\n \" // reply is intentionally ignored.\\n\" +\r\n \" // PRIMARY SOURCE (openclaw/openclaw src/plugins/hook-types.ts:808-818\\n\" +\r\n \" // PluginHookSubagentEndedEvent + wired-hooks-subagent.test.ts:118-126):\\n\" +\r\n \" // the native event's ONLY identity is `targetSessionKey` — it carries\\n\" +\r\n \" // NO agentId/subagentId, NO agentType/subagentType/agent, and NO\\n\" +\r\n \" // result/output. The old e.agentId||e.subagentId / e.agentType||… /\\n\" +\r\n \" // e.result||e.output reads were ALL false-friends (host emits none →\\n\" +\r\n \" // agentId was always undefined). We read the real e.targetSessionKey as\\n\" +\r\n \" // agentId and omit the type/message slots the host never provides.\\n\" +\r\n ' on(\"subagent_ended\", async (event) => {\\n' +\r\n \" const e = event || {};\\n\" +\r\n \" bridge(\\\"SubagentStop\\\", {\\n\" +\r\n \" agentId: e.targetSessionKey || undefined,\\n\" +\r\n \" sessionId: SESSION_ID,\\n\" +\r\n \" projectDir: PROJECT_DIR,\\n\" +\r\n \" });\\n\" +\r\n \" return undefined;\\n\" +\r\n \" });\\n\",\r\n );\r\n }\r\n\r\n if (has(\"SessionStart\")) {\r\n reg.push(\r\n \"\\n\" +\r\n \" // SessionStart → session_start records the real session id and fetches\\n\" +\r\n \" // the additionalContext to inject; before_prompt_build performs the\\n\" +\r\n \" // injection (the verified context-injection point upstream).\\n\" +\r\n ' on(\"session_start\", async (event) => {\\n' +\r\n \" const e = event || {};\\n\" +\r\n \" SESSION_ID = e.sessionId || e.sessionKey || SESSION_ID;\\n\" +\r\n \" const res = bridge(\\\"SessionStart\\\", {\\n\" +\r\n \" sessionId: SESSION_ID,\\n\" +\r\n ' source: e.resumedFrom ? \"resume\" : \"startup\",\\n' +\r\n \" projectDir: PROJECT_DIR,\\n\" +\r\n \" });\\n\" +\r\n \" contextInjected = false;\\n\" +\r\n \" pendingContext =\\n\" +\r\n ' res && typeof res.additionalContext === \"string\" && res.additionalContext\\n' +\r\n \" ? res.additionalContext\\n\" +\r\n \" : null;\\n\" +\r\n \" return undefined;\\n\" +\r\n \" });\\n\",\r\n );\r\n }\r\n\r\n // before_prompt_build fires on EVERY prompt build. ONE handler serves both\r\n // context-injecting events that target it — they COEXIST via SEPARATE state\r\n // so the once-only SessionStart flag never suppresses the per-turn injection:\r\n // SessionStart → inject pendingContext ONCE (first build), as system ctx.\r\n // UserPromptSubmit → bridge EVERY turn, append the returned additionalContext.\r\n // before_prompt_build has NO blocking ability, so a UserPromptSubmit block/deny\r\n // decision DEGRADES to a no-op (context-injection only — never claim a block).\r\n if (has(\"SessionStart\") || has(\"UserPromptSubmit\")) {\r\n let body = \"\";\r\n if (has(\"SessionStart\")) {\r\n body +=\r\n \" // SessionStart: inject the session-start context exactly once.\\n\" +\r\n \" if (!contextInjected && pendingContext) {\\n\" +\r\n \" contextInjected = true;\\n\" +\r\n \" out.appendSystemContext = pendingContext;\\n\" +\r\n \" }\\n\";\r\n }\r\n if (has(\"UserPromptSubmit\")) {\r\n body +=\r\n \" // UserPromptSubmit: per-turn context injection. before_prompt_build\\n\" +\r\n \" // cannot block, so a deny/block decision degrades to a no-op.\\n\" +\r\n \" const e = event || {};\\n\" +\r\n \" const ures = bridge(\\\"UserPromptSubmit\\\", {\\n\" +\r\n ' prompt: typeof e.prompt === \"string\" ? e.prompt : \"\",\\n' +\r\n \" sessionId: SESSION_ID,\\n\" +\r\n \" projectDir: PROJECT_DIR,\\n\" +\r\n \" });\\n\" +\r\n ' if (ures && typeof ures.additionalContext === \"string\" && ures.additionalContext) {\\n' +\r\n \" out.appendContext = ures.additionalContext;\\n\" +\r\n \" }\\n\";\r\n }\r\n reg.push(\r\n \"\\n\" +\r\n \" // before_prompt_build → coexisting SessionStart (once) + UserPromptSubmit\\n\" +\r\n \" // (per-turn) context injection. Context-only: it cannot block a prompt.\\n\" +\r\n ' on(\"before_prompt_build\", (event) => {\\n' +\r\n \" const out = {};\\n\" +\r\n body +\r\n \" return Object.keys(out).length > 0 ? out : undefined;\\n\" +\r\n \" });\\n\",\r\n );\r\n }\r\n\r\n // NATIVE passthrough events: OpenClaw lifecycle hook names with no canonical\r\n // analog, declared on platforms.<id>.nativeHooks. Each registers an on(...)\r\n // handler that bridges the NATIVE event name verbatim to the home-bin →\r\n // runNativeHook dispatches it host-generically. Read straight off\r\n // platforms.<id>.nativeHooks (independent of hookEvents AND of canonicalOff),\r\n // so they install even with hooks:false. Mirrors omp/opencode.\r\n for (const ev of this.nativeHookEvents(ctx)) {\r\n const key = JSON.stringify(ev);\r\n reg.push(\r\n \"\\n\" +\r\n \" // nativeHooks passthrough → \" + ev + \"\\n\" +\r\n \" on(\" + key + \", (event) => {\\n\" +\r\n \" bridge(\" + key + \", { sessionId: SESSION_ID, projectDir: PROJECT_DIR, raw: event });\\n\" +\r\n \" return undefined;\\n\" +\r\n \" });\\n\",\r\n );\r\n }\r\n\r\n // Action surface: one api.registerCommand per declared action, registered in\r\n // the SAME register(api) body. The command shells out to the home-bin\r\n // `action <host>` verb with the host token baked as this.id (so a NemoClaw\r\n // fork emits `action nemoclaw`, NOT openclaw — exactly like the hook bridge\r\n // above). The win32 branch reuses the hook bridge's quoted-shell form (a .cmd\r\n // launcher throws EINVAL via execFileSync). Both id AND description are\r\n // JSON.stringify'd — descriptions are free-form connector text, so a raw \" /\r\n // backtick would break the generated module.\r\n for (const action of this.actionTriggers(ctx)) {\r\n const id = JSON.stringify(action.id);\r\n const description = JSON.stringify(action.description);\r\n reg.push(\r\n \"\\n\" +\r\n \" // Action /\" + action.id + \" -> run the home-bin action verb directly.\\n\" +\r\n \" api.registerCommand({\\n\" +\r\n \" name: \" + id + \",\\n\" +\r\n \" description: \" + description + \",\\n\" +\r\n \" handler: async (_ctx) => {\\n\" +\r\n \" try {\\n\" +\r\n ' const args = [\"action\", ' + JSON.stringify(this.id) + \", \" + id + ', \"--connector\", CONNECTOR_ID];\\n' +\r\n \" const out =\\n\" +\r\n ' process.platform === \"win32\"\\n' +\r\n \" ? execSync([HOME_BIN, ...args].map((a) => '\\\"' + a + '\\\"').join(\\\" \\\"), { encoding: \\\"utf8\\\" })\\n\" +\r\n ' : execFileSync(HOME_BIN, args, { encoding: \"utf8\" });\\n' +\r\n ' return { text: (out || \"\").trim() };\\n' +\r\n \" } catch (e) {\\n\" +\r\n \" return { text: \\\"action \\\" + \" + id + \" + \\\" failed: \\\" + (e && e.message ? e.message : \\\"error\\\") };\\n\" +\r\n \" }\\n\" +\r\n \" },\\n\" +\r\n \" });\\n\",\r\n );\r\n }\r\n\r\n reg.push(\" },\\n\");\r\n\r\n const definition =\r\n \"const plugin = {\\n\" +\r\n \" id: CONNECTOR_ID,\\n\" +\r\n \" name: \" +\r\n connectorName +\r\n \",\\n\" +\r\n \" configSchema: {\\n\" +\r\n ' type: \"object\",\\n' +\r\n \" properties: {\\n\" +\r\n \" enabled: {\\n\" +\r\n ' type: \"boolean\",\\n' +\r\n \" default: true,\\n\" +\r\n ' description: \"Enable or disable the \" + CONNECTOR_ID + \" plugin.\",\\n' +\r\n \" },\\n\" +\r\n \" },\\n\" +\r\n \" additionalProperties: false,\\n\" +\r\n \" },\\n\" +\r\n reg.join(\"\") +\r\n \"};\\n\\n\" +\r\n \"export default plugin;\\n\";\r\n\r\n return header + definition;\r\n }\r\n\r\n // ── Runtime: parse OUR bridge payload → normalized event ───────────────\r\n\r\n /**\r\n * `raw` is the payload OUR generated plugin posts (NOT a host-native shape):\r\n * { toolName, toolInput, toolOutput?, isError?, sessionId, source?, projectDir }\r\n * so this maps straight through.\r\n */\r\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\r\n const input = (raw ?? {}) as OpenClawBridgePayload;\r\n const base = {\r\n hostPlatform: this.id,\r\n connectorId: \"\",\r\n sessionId: typeof input.sessionId === \"string\" ? input.sessionId : \"\",\r\n raw,\r\n ...(typeof input.projectDir === \"string\" ? { projectDir: input.projectDir } : {}),\r\n } as const;\r\n\r\n switch (event) {\r\n case \"PreToolUse\": {\r\n const ev: PreToolUseEvent = {\r\n ...base,\r\n toolName: input.toolName ?? \"\",\r\n toolInput: input.toolInput ?? {},\r\n };\r\n return ev;\r\n }\r\n case \"PostToolUse\": {\r\n const ev: PostToolUseEvent = {\r\n ...base,\r\n toolName: input.toolName ?? \"\",\r\n toolInput: input.toolInput ?? {},\r\n ...(typeof input.toolOutput === \"string\" ? { toolOutput: input.toolOutput } : {}),\r\n ...(typeof input.isError === \"boolean\" ? { isError: input.isError } : {}),\r\n };\r\n return ev;\r\n }\r\n case \"SessionStart\": {\r\n const source: SessionStartEvent[\"source\"] =\r\n input.source === \"resume\"\r\n ? \"resume\"\r\n : input.source === \"compact\"\r\n ? \"compact\"\r\n : input.source === \"clear\"\r\n ? \"clear\"\r\n : \"startup\";\r\n const ev: SessionStartEvent = { ...base, source };\r\n return ev;\r\n }\r\n case \"UserPromptSubmit\": {\r\n const ev: UserPromptSubmitEvent = {\r\n ...base,\r\n prompt: typeof input.prompt === \"string\" ? input.prompt : \"\",\r\n };\r\n return ev;\r\n }\r\n case \"SubagentStart\": {\r\n // Empty strings are dropped so the runtime matcher never filters a\r\n // real spawn on an unknown agent type (fail-open, per the dispatcher).\r\n const ev: SubagentStartEvent = {\r\n ...base,\r\n ...(isNonEmptyString(input.agentId) ? { agentId: input.agentId } : {}),\r\n ...(isNonEmptyString(input.agentType)\r\n ? { agentType: input.agentType }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"SubagentStop\": {\r\n const ev: SubagentStopEvent = {\r\n ...base,\r\n ...(isNonEmptyString(input.agentId) ? { agentId: input.agentId } : {}),\r\n ...(isNonEmptyString(input.agentType)\r\n ? { agentType: input.agentType }\r\n : {}),\r\n ...(isNonEmptyString(input.lastAssistantMessage)\r\n ? { lastAssistantMessage: input.lastAssistantMessage }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n default:\r\n // Other canonical events are not surfaced by OpenClaw; treat as a\r\n // session-start-shaped no-op so the dispatcher fails open gracefully.\r\n return { ...base, source: \"startup\" } satisfies SessionStartEvent;\r\n }\r\n }\r\n\r\n // ── Runtime: normalized response → reply the generated bridge parses ───\r\n\r\n /**\r\n * Unlike json-stdio hosts (whose reply is the host's NATIVE control payload),\r\n * OUR generated bridge consumes this stdout directly. So the reply body is the\r\n * NORMALIZED HookResponse itself — the bridge JSON.parses it and reads\r\n * decision / updatedInput / additionalContext.\r\n */\r\n formatReply(_event: HookEventName, response: HookResponse): HookReply {\r\n return {\r\n exitCode: 0,\r\n stdout: JSON.stringify(response ?? { decision: \"allow\" }),\r\n };\r\n }\r\n\r\n // ── Diagnostics ────────────────────────────────────────────────────────\r\n\r\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\r\n const configPath = this.getServerConfigPath(ctx);\r\n const pluginPath = this.getHookConfigPath(ctx);\r\n const pluginDir = this.pluginDir(ctx);\r\n const id = ctx.connector.id;\r\n // Actions are health-equivalent to hooks: both write the SAME plugin module\r\n // and the SAME dual-registration entry, so an actions-only connector must\r\n // still assert the plugin/extension is present + loaded.\r\n const hasHooks =\r\n ctx.connector.hookEvents.length > 0 ||\r\n this.nativeHookEvents(ctx).length > 0 ||\r\n this.actionTriggers(ctx).length > 0;\r\n const hasServer = Boolean(ctx.connector.server);\r\n\r\n return [\r\n {\r\n name: `${this.name}: openclaw.json present`,\r\n check: () =>\r\n existsSync(configPath)\r\n ? { status: \"OK\", detail: configPath }\r\n : { status: \"FAIL\", detail: `not found: ${configPath}` },\r\n },\r\n {\r\n // HARDEST PART — dual registration must be CONSISTENT. The plugin only\r\n // delivers tools when it is LOADED (plugins.entries.<id> enabled AND its\r\n // dir in plugins.load.paths) AND in mcp.servers. A config with one half\r\n // but not the other is a tool-less (or never-loaded) install, so FAIL on\r\n // loaded XOR mcp.servers. (We assert plugins.load.paths — NOT a per-entry\r\n // `module` field, which openclaw config validate rejects.)\r\n name: `${this.name}: dual registration (plugins.entries+load.paths + mcp.servers.${id})`,\r\n check: () => {\r\n // Only assert what the connector declares: a catalog-only connector\r\n // (no MCP server, no hooks) never writes either half of the dual\r\n // registration, so absence — even of the config file — is healthy.\r\n if (!hasServer && !hasHooks) {\r\n return { status: \"OK\", detail: \"no MCP server or hooks declared\" };\r\n }\r\n const cfg = this.readJson<Record<string, unknown>>(configPath);\r\n if (!cfg) return { status: \"FAIL\", detail: `cannot read ${configPath}` };\r\n\r\n const inEntries = this.hasPluginEntry(cfg, id) && this.hasLoadPath(cfg, pluginDir);\r\n const inMcp = this.hasMcpServer(cfg, id);\r\n\r\n // A hooks-only connector (no MCP server declared) does not need the\r\n // mcp.servers half — the load half alone is correct and complete.\r\n if (!hasServer) {\r\n return inEntries || !hasHooks\r\n ? { status: \"OK\", detail: \"hooks-only connector (no MCP server)\" }\r\n : {\r\n status: \"FAIL\",\r\n detail: `${id} not enabled in ${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY} + ${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY} (plugin will not load)`,\r\n };\r\n }\r\n\r\n if (inEntries && inMcp) {\r\n return {\r\n status: \"OK\",\r\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id}+${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY} + ${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id}`,\r\n };\r\n }\r\n if (inEntries && !inMcp) {\r\n return {\r\n status: \"FAIL\",\r\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`,\r\n };\r\n }\r\n if (!inEntries && inMcp) {\r\n return {\r\n status: \"FAIL\",\r\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`,\r\n };\r\n }\r\n return {\r\n status: \"FAIL\",\r\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}`,\r\n };\r\n },\r\n },\r\n {\r\n name: `${this.name}: plugin module present`,\r\n check: () => {\r\n if (!hasHooks) return { status: \"OK\", detail: \"no hooks declared\" };\r\n return existsSync(pluginPath)\r\n ? { status: \"OK\", detail: pluginPath }\r\n : { status: \"FAIL\", detail: `not found: ${pluginPath}` };\r\n },\r\n },\r\n ];\r\n }\r\n\r\n /** True when config.plugins.entries.<id> exists (and is not disabled). */\r\n private hasPluginEntry(cfg: Record<string, unknown>, id: string): boolean {\r\n const plugins = cfg[PLUGINS_KEY];\r\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\r\n const entries = (plugins as Record<string, unknown>)[PLUGINS_ENTRIES_KEY];\r\n if (!entries || typeof entries !== \"object\" || Array.isArray(entries)) return false;\r\n const entry = (entries as Record<string, unknown>)[id];\r\n if (entry === undefined) return false;\r\n // An explicit { enabled: false } means the gateway will not load it.\r\n return !(\r\n entry &&\r\n typeof entry === \"object\" &&\r\n (entry as Record<string, unknown>).enabled === false\r\n );\r\n }\r\n\r\n /** True when config.plugins.load.paths includes `pluginDir`. */\r\n private hasLoadPath(cfg: Record<string, unknown>, pluginDir: string): boolean {\r\n const plugins = cfg[PLUGINS_KEY];\r\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\r\n const load = (plugins as Record<string, unknown>)[PLUGINS_LOAD_KEY];\r\n if (!load || typeof load !== \"object\" || Array.isArray(load)) return false;\r\n const paths = (load as Record<string, unknown>)[PLUGINS_LOAD_PATHS_KEY];\r\n return Array.isArray(paths) && paths.includes(pluginDir);\r\n }\r\n\r\n /** True when config.mcp.servers.<id> exists. */\r\n private hasMcpServer(cfg: Record<string, unknown>, id: string): boolean {\r\n const mcp = cfg[MCP_ROOT_KEY];\r\n if (!mcp || typeof mcp !== \"object\" || Array.isArray(mcp)) return false;\r\n const servers = (mcp as Record<string, unknown>)[MCP_SERVERS_KEY];\r\n if (!servers || typeof servers !== \"object\" || Array.isArray(servers)) return false;\r\n return id in (servers as Record<string, unknown>);\r\n }\r\n\r\n}\r\n\r\n/**\r\n * Resolve the openclaw.json the gateway actually loads (mirrors context-mode's\r\n * openclawConfigPath): $OPENCLAW_CONFIG_PATH, else $OPENCLAW_STATE_DIR/openclaw.json,\r\n * else ~/.openclaw/openclaw.json. The gateway never loads a CWD/project-local\r\n * openclaw.json for user scope, so this is the canonical user-scope path.\r\n */\r\nfunction resolveOpenClawConfigPath(env: NodeJS.ProcessEnv = process.env): string {\r\n if (env.OPENCLAW_CONFIG_PATH) return resolve(env.OPENCLAW_CONFIG_PATH);\r\n const stateDir = env.OPENCLAW_STATE_DIR\r\n ? resolve(env.OPENCLAW_STATE_DIR)\r\n : resolve(homedir(), \".openclaw\");\r\n return resolve(stateDir, \"openclaw.json\");\r\n}\r\n\r\n/** Narrow to a non-empty string (the bridge omits empties, but stay defensive). */\r\nfunction isNonEmptyString(value: unknown): value is string {\r\n return typeof value === \"string\" && value !== \"\";\r\n}\r\n\r\nexport const adapter = new OpenClawAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":["entry"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ensureDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-QYOUQCC6.js";
|
|
5
5
|
|
|
6
6
|
// src/core/yaml.ts
|
|
7
7
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -34,4 +34,4 @@ export {
|
|
|
34
34
|
writeYaml,
|
|
35
35
|
yamlObjectMapCodec
|
|
36
36
|
};
|
|
37
|
-
//# sourceMappingURL=chunk-
|
|
37
|
+
//# sourceMappingURL=chunk-THI4PI4H.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/yaml.ts"],"sourcesContent":["/**\r\n * core/yaml — minimal YAML config IO.\r\n *\r\n * The YAML analog of BaseAdapter's JSON helpers (readJson/writeJson). Adapters\r\n * whose native config is YAML (Goose's config.yaml, Hermes's config.yaml) cannot\r\n * use the JSON helpers, so they reach for these two functions instead — same\r\n * existsSync-guard / parse-failure-tolerant / dryRun-respecting contract, just\r\n * over the `yaml` package's parse()/stringify() rather than JSON.parse/stringify.\r\n *\r\n * Deliberately tiny: read-or-null and ensureDir+write. All merge/upsert/idempotency\r\n * logic lives in each adapter (the shapes differ per platform), exactly as the\r\n * JSON adapters layer their own upsert on top of writeJson.\r\n */\r\n\r\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\r\nimport { dirname } from \"node:path\";\r\n\r\nimport { parse, stringify } from \"yaml\";\r\n\r\nimport type { ObjectMapCodec } from \"./object-map.js\";\r\nimport { ensureDir } from \"./paths.js\";\r\n\r\n/**\r\n * Read and parse a YAML file. Returns `null` when the file is missing OR when it\r\n * fails to parse (mirrors BaseAdapter.readJson's fail-soft behavior so a corrupt\r\n * file degrades to \"treat as absent\" rather than throwing mid-install).\r\n */\r\nexport function readYaml<T = Record<string, unknown>>(path: string): T | null {\r\n if (!existsSync(path)) return null;\r\n try {\r\n const parsed = parse(readFileSync(path, \"utf8\")) as T | null | undefined;\r\n // An empty YAML document parses to null/undefined — normalize to null.\r\n return parsed == null ? null : parsed;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Serialize `data` to YAML and write it to `path`, creating parent dirs as\r\n * needed. Honors `dryRun` (no write). Mirrors BaseAdapter.writeJson's signature.\r\n */\r\nexport function writeYaml(path: string, data: unknown, dryRun = false): void {\r\n if (dryRun) return;\r\n ensureDir(dirname(path));\r\n writeFileSync(path, stringify(data), \"utf8\");\r\n}\r\n\r\n/**\r\n * An ObjectMapCodec over YAML for the core/object-map engine. Shared by the YAML\r\n * object-map server hosts (Goose's `extensions`, Hermes's `mcp_servers`) and any\r\n * future YAML host. `isPresentButUnparseable` is `() => false`: YAML server hosts\r\n * historically coerce/overwrite (readYaml(path) ?? {}) rather than warn-skip on an\r\n * unparseable file — preserved here for byte-identical behavior.\r\n */\r\nexport function yamlObjectMapCodec(): ObjectMapCodec {\r\n return {\r\n parse: (path) => readYaml<Record<string, unknown>>(path),\r\n serialize: (path, data, dryRun) => writeYaml(path, data, dryRun),\r\n isPresentButUnparseable: () => false,\r\n };\r\n}\r\n"],"mappings":";;;;;;AAcA,SAAS,YAAY,cAAc,qBAAqB;AACxD,SAAS,eAAe;AAExB,SAAS,OAAO,iBAAiB;AAU1B,SAAS,SAAsC,MAAwB;AAC5E,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,UAAM,SAAS,MAAM,aAAa,MAAM,MAAM,CAAC;AAE/C,WAAO,UAAU,OAAO,OAAO;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,UAAU,MAAc,MAAe,SAAS,OAAa;AAC3E,MAAI,OAAQ;AACZ,YAAU,QAAQ,IAAI,CAAC;AACvB,gBAAc,MAAM,UAAU,IAAI,GAAG,MAAM;AAC7C;AASO,SAAS,qBAAqC;AACnD,SAAO;AAAA,IACL,OAAO,CAAC,SAAS,SAAkC,IAAI;AAAA,IACvD,WAAW,CAAC,MAAM,MAAM,WAAW,UAAU,MAAM,MAAM,MAAM;AAAA,IAC/D,yBAAyB,MAAM;AAAA,EACjC;AACF;","names":[]}
|