@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
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/marketplace-state.ts"],"sourcesContent":["/**\n * core/marketplace-state — pure read/write helpers for MARKETPLACE-method\n * install state. A LEAF module (fs + paths only, no adapters, no spawning) so\n * the installer, the marketplace orchestration, doctor, and status can all\n * import it without cycles.\n *\n * Two kinds of state live here:\n *\n * 1. The per-connector state record `connectorDir(id)/marketplace-installs.json`\n * — one entry per platform a connector was marketplace-installed on\n * ({ format, bundleDir, marketplace, scope, version, contentHash,\n * installedAt }). It drives `uninstall --method auto`, upgrade staleness\n * warnings, doctor, and the double-install guard.\n *\n * 2. Read-only fs EVIDENCE probes of the hosts' own plugin state (e.g.\n * Claude Code's `$CLAUDE_CONFIG_DIR/plugins/installed_plugins.json`).\n * These catch installs the user performed manually from `package`'s\n * printed instructions, which the state record alone would miss. Probes\n * read undocumented host internals, so every reader degrades to null/false\n * on absence or parse failure — the guard convention is \"refuse only on\n * positive evidence\" (docs: design risk 4).\n */\n\nimport { createHash } from \"node:crypto\";\nimport {\n existsSync,\n readdirSync,\n readFileSync,\n realpathSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport TOML from \"@iarna/toml\";\n\nimport type { InstallScope, PlatformId } from \"./types.js\";\nimport type { PackageFormat } from \"./package.js\";\nimport {\n assertNoSymlinkInPath,\n connectorDir,\n connectorsDir,\n dataRoot,\n ensureDir,\n} from \"./paths.js\";\nimport { codexConfigHome, xdgConfigHome } from \"./host-paths.js\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// State record: connectorDir(id)/marketplace-installs.json\n// ─────────────────────────────────────────────────────────────────────────\n\n/** The catalog name every staged local marketplace is registered under. */\nexport const MARKETPLACE_NAME = \"agent-connector\";\n\n/** One marketplace install of one connector on one platform. */\nexport interface MarketplaceInstallRecord {\n /** Bundle format that was staged + installed (e.g. \"claude-plugin\"). */\n format: PackageFormat;\n /** Absolute path of the staged plugin dir the host references/copied. */\n bundleDir: string;\n /** Marketplace/catalog name the install is keyed under ({@link MARKETPLACE_NAME}). */\n marketplace: string;\n /** Install scope (v1: always \"user\"). */\n scope: InstallScope;\n /** connector.version at install time (staleness warnings key off this). */\n version: string;\n /** sha256 over the staged plugin dir at install time (drift detection). */\n contentHash: string;\n /** ISO timestamp of the recorded install. */\n installedAt: string;\n}\n\n/** platform → record map persisted per connector. */\nexport type MarketplaceInstalls = Partial<\n Record<PlatformId, MarketplaceInstallRecord>\n>;\n\n/** Path of the per-connector marketplace state record. */\nexport function marketplaceInstallsPath(connectorId: string): string {\n return join(connectorDir(connectorId), \"marketplace-installs.json\");\n}\n\n/** Read the state record ({} on absence/parse failure — never throws). */\nexport function readMarketplaceInstalls(connectorId: string): MarketplaceInstalls {\n try {\n const raw = readFileSync(marketplaceInstallsPath(connectorId), \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as MarketplaceInstalls;\n }\n } catch {\n /* absent / unreadable → empty */\n }\n return {};\n}\n\n/** Write the state record; an empty map DELETES the file (uninstall leaves no husk). */\nexport function writeMarketplaceInstalls(\n connectorId: string,\n installs: MarketplaceInstalls,\n): void {\n const path = marketplaceInstallsPath(connectorId);\n assertNoSymlinkInPath(path);\n if (Object.keys(installs).length === 0) {\n rmSync(path, { force: true });\n return;\n }\n ensureDir(dirname(path));\n writeFileSync(path, `${JSON.stringify(installs, null, 2)}\\n`, \"utf8\");\n}\n\n/** True when THIS connector has any recorded marketplace install. */\nexport function connectorHasMarketplaceRecords(connectorId: string): boolean {\n return Object.keys(readMarketplaceInstalls(connectorId)).length > 0;\n}\n\n/**\n * True when ANY registered connector (optionally excluding one) still records a\n * marketplace install. Guards home-bin removal on purge: an installed plugin's\n * hooks exec the home-bin OUTSIDE any adapter-managed config, so removing the\n * launcher while a marketplace install survives would silently kill its hooks.\n */\nexport function anyMarketplaceRecordsRemain(excludeConnectorId?: string): boolean {\n const dir = connectorsDir();\n if (!existsSync(dir)) return false;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return false;\n }\n return entries.some(\n (id) => id !== excludeConnectorId && connectorHasMarketplaceRecords(id),\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Staging roots — bundles staged by-reference must survive cwd changes and\n// `rm -rf dist-plugin`, so they live under the data-root, one shared root per\n// host family, holding every marketplace-installed connector's bundle plus ONE\n// regenerated catalog named \"agent-connector\" listing them all.\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Root for every marketplace staging tree: <dataRoot>/marketplace. */\nexport function marketplaceRoot(): string {\n return join(dataRoot(), \"marketplace\");\n}\n\n/** Shared Claude-family staging root: <dataRoot>/marketplace/claude. */\nexport function claudeStagingRoot(): string {\n return join(marketplaceRoot(), \"claude\");\n}\n\n/** Shared Codex-family staging root: <dataRoot>/marketplace/codex. */\nexport function codexStagingRoot(): string {\n return join(marketplaceRoot(), \"codex\");\n}\n\n/** Shared GitHub Copilot CLI staging root: <dataRoot>/marketplace/copilot. */\nexport function copilotStagingRoot(): string {\n return join(marketplaceRoot(), \"copilot\");\n}\n\n/** Shared agy (Antigravity) staging root: <dataRoot>/marketplace/agy. */\nexport function agyStagingRoot(): string {\n return join(marketplaceRoot(), \"agy\");\n}\n\n/** Shared Gemini-CLI staging root: <dataRoot>/marketplace/gemini. */\nexport function geminiStagingRoot(): string {\n return join(marketplaceRoot(), \"gemini\");\n}\n\n/** Shared Qwen-Code staging root: <dataRoot>/marketplace/qwen. */\nexport function qwenStagingRoot(): string {\n return join(marketplaceRoot(), \"qwen\");\n}\n\n/** Shared droid (Factory) staging root: <dataRoot>/marketplace/droid. */\nexport function droidStagingRoot(): string {\n return join(marketplaceRoot(), \"droid\");\n}\n\n/**\n * Shared npm-local staging root: <dataRoot>/marketplace/npm. Every npm-plugin\n * host (opencode / kilo / kilo-cli) stages here; the host config's `plugin`\n * array references the bundle by `file://<this>/<id>`, so it MUST live under the\n * stable data-root (it survives cwd changes and `rm -rf dist-plugin`).\n */\nexport function npmStagingRoot(): string {\n return join(marketplaceRoot(), \"npm\");\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Claude Code host-state readers ($CLAUDE_CONFIG_DIR || ~/.claude)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Claude Code's config dir, honoring the CLAUDE_CONFIG_DIR override. */\nexport function claudeConfigDir(): string {\n const override = process.env.CLAUDE_CONFIG_DIR;\n if (override && override.trim() !== \"\") return resolve(override);\n return join(homedir(), \".claude\");\n}\n\n/** The installed_plugins.json key our marketplace install creates for `id`. */\nexport function claudePluginKey(connectorId: string): string {\n return `${connectorId}@${MARKETPLACE_NAME}`;\n}\n\nfunction readJsonFile(path: string): Record<string, unknown> | null {\n try {\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n /* absent / unreadable → null */\n }\n return null;\n}\n\n/** The `plugins` map from Claude's installed_plugins.json (null when unreadable). */\nexport function readClaudeInstalledPlugins(): Record<string, unknown> | null {\n const file = join(claudeConfigDir(), \"plugins\", \"installed_plugins.json\");\n const parsed = readJsonFile(file);\n const plugins = parsed?.plugins;\n if (plugins && typeof plugins === \"object\" && !Array.isArray(plugins)) {\n return plugins as Record<string, unknown>;\n }\n return null;\n}\n\n/** True when Claude's own state lists `<id>@agent-connector` as installed. */\nexport function claudePluginInstalled(connectorId: string): boolean {\n const plugins = readClaudeInstalledPlugins();\n if (!plugins) return false;\n const entry = plugins[claudePluginKey(connectorId)];\n return Array.isArray(entry) ? entry.length > 0 : entry != null;\n}\n\n/** True when ANY `*@agent-connector` plugin remains in Claude's state. */\nexport function anyClaudeAgentConnectorPlugins(): boolean {\n const plugins = readClaudeInstalledPlugins();\n if (!plugins) return false;\n return Object.entries(plugins).some(\n ([key, entry]) =>\n key.endsWith(`@${MARKETPLACE_NAME}`) &&\n (Array.isArray(entry) ? entry.length > 0 : entry != null),\n );\n}\n\n/**\n * The directory Claude's known_marketplaces.json records for `name`, or null\n * when not registered / unreadable. Used both as a presence probe and as the\n * NAME-COLLISION check: a registration pointing somewhere other than our\n * staging root belongs to the user and must never be touched.\n */\nexport function claudeKnownMarketplacePath(name: string): string | null {\n const file = join(claudeConfigDir(), \"plugins\", \"known_marketplaces.json\");\n const parsed = readJsonFile(file);\n const entry = parsed?.[name];\n if (!entry || typeof entry !== \"object\") return null;\n const e = entry as { installLocation?: unknown; source?: { path?: unknown } };\n if (typeof e.installLocation === \"string\") return e.installLocation;\n const sourcePath = e.source?.path;\n return typeof sourcePath === \"string\" ? sourcePath : null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Codex host-state readers ($CODEX_HOME || ~/.codex → config.toml)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** The config.toml key our marketplace install creates for `id` (== claude's). */\nexport function codexPluginKey(connectorId: string): string {\n return `${connectorId}@${MARKETPLACE_NAME}`;\n}\n\n/** Parse <CODEX_HOME>/config.toml ({} on absence/parse failure — never throws). */\nfunction readCodexConfig(): Record<string, unknown> {\n const file = join(codexConfigHome(), \"config.toml\");\n try {\n return TOML.parse(readFileSync(file, \"utf8\")) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\n/**\n * True when Codex's config.toml carries the `[plugins.\"<id>@agent-connector\"]`\n * table. The DEFINITIVE install probe — empty `plugins/cache/...` dirs linger\n * after uninstall, so the cache dir must NOT be used (docs: codex quirk).\n */\nexport function codexPluginInstalled(connectorId: string): boolean {\n const cfg = readCodexConfig();\n const plugins = cfg[\"plugins\"];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n return codexPluginKey(connectorId) in (plugins as Record<string, unknown>);\n}\n\n/** True when ANY `*@agent-connector` plugin remains in Codex's config.toml. */\nexport function anyCodexAgentConnectorPlugins(): boolean {\n const cfg = readCodexConfig();\n const plugins = cfg[\"plugins\"];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n return Object.keys(plugins as Record<string, unknown>).some((k) =>\n k.endsWith(`@${MARKETPLACE_NAME}`),\n );\n}\n\n/**\n * The directory config.toml records as `[marketplaces.agent-connector].source`,\n * or null when not registered / unreadable. Both a presence probe and the\n * NAME-COLLISION check: a registration pointing somewhere other than our\n * staging root belongs to the user and must never be touched.\n */\nexport function codexMarketplaceSource(name: string): string | null {\n const cfg = readCodexConfig();\n const marketplaces = cfg[\"marketplaces\"];\n if (!marketplaces || typeof marketplaces !== \"object\" || Array.isArray(marketplaces)) {\n return null;\n }\n const entry = (marketplaces as Record<string, unknown>)[name];\n if (!entry || typeof entry !== \"object\") return null;\n const source = (entry as { source?: unknown }).source;\n return typeof source === \"string\" ? source : null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// GitHub Copilot CLI host-state readers (~/.copilot, NO env — HOME isolation)\n//\n// LIVE-VERIFIED against GitHub Copilot CLI 1.0.63:\n// • ~/.copilot/config.json — JSONC (leading `// …` comment header, \"managed\n// automatically\") carrying `installedPlugins: [{ name, marketplace,\n// version, installed_at, enabled, cache_path }]`. The DEFINITIVE install\n// probe (an uninstall empties this array). Parsed JSONC-tolerantly so the\n// comment header does not break JSON.parse.\n// • ~/.copilot/settings.json — `extraKnownMarketplaces[\"<name>\"].source.path`\n// records the registered local marketplace dir (a `{source:\"directory\",\n// path}` object); `enabledPlugins[\"<id>@agent-connector\"]: true`. The\n// `.source.path` is both the presence probe and the NAME-COLLISION check.\n// Both readers degrade to false/null on absence/parse failure (refuse only on\n// positive evidence).\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Copilot CLI's config dir: ~/.copilot (NO dedicated env — HOME isolation). */\nexport function copilotConfigDir(): string {\n return join(homedir(), \".copilot\");\n}\n\n/** The state key our marketplace install creates for `id` (== claude/codex). */\nexport function copilotPluginKey(connectorId: string): string {\n return `${connectorId}@${MARKETPLACE_NAME}`;\n}\n\n/** The `installedPlugins` array from Copilot's config.json (JSONC; [] when unreadable). */\nfunction readCopilotInstalledPlugins(): Array<Record<string, unknown>> {\n let raw: string;\n try {\n raw = readFileSync(join(copilotConfigDir(), \"config.json\"), \"utf8\");\n } catch {\n return [];\n }\n const parsed = parseJsonc(raw);\n const plugins = parsed?.[\"installedPlugins\"];\n if (!Array.isArray(plugins)) return [];\n return plugins.filter(\n (e): e is Record<string, unknown> =>\n e != null && typeof e === \"object\" && !Array.isArray(e),\n );\n}\n\n/**\n * True when Copilot's config.json lists `id` installed from our marketplace\n * (`installedPlugins[]` carries an entry with `name === id`, `marketplace ===\n * agent-connector`, and not `enabled: false`). The DEFINITIVE probe — an\n * uninstall empties this array (live-verified on CLI 1.0.63).\n */\nexport function copilotPluginInstalled(connectorId: string): boolean {\n return readCopilotInstalledPlugins().some(\n (e) =>\n e[\"name\"] === connectorId &&\n e[\"marketplace\"] === MARKETPLACE_NAME &&\n e[\"enabled\"] !== false,\n );\n}\n\n/** True when ANY plugin from our marketplace remains in Copilot's config.json. */\nexport function anyCopilotAgentConnectorPlugins(): boolean {\n return readCopilotInstalledPlugins().some(\n (e) => e[\"marketplace\"] === MARKETPLACE_NAME && e[\"enabled\"] !== false,\n );\n}\n\n/**\n * The directory settings.json records as `extraKnownMarketplaces[\"<name>\"]\n * .source.path`, or null when not registered / unreadable. Both a presence probe\n * and the NAME-COLLISION check (a registration pointing somewhere other than our\n * staging root belongs to the user and must never be touched). `source` is a\n * `{ source: \"directory\", path }` object on CLI 1.0.63; a bare string source is\n * accepted defensively.\n */\nexport function copilotMarketplaceSource(name: string): string | null {\n const parsed = readJsonFile(join(copilotConfigDir(), \"settings.json\"));\n const known = parsed?.[\"extraKnownMarketplaces\"];\n if (!known || typeof known !== \"object\" || Array.isArray(known)) return null;\n const entry = (known as Record<string, unknown>)[name];\n if (entry == null) return null;\n const source = (entry as { source?: unknown }).source;\n if (typeof source === \"string\") return source;\n if (source && typeof source === \"object\" && !Array.isArray(source)) {\n const path = (source as { path?: unknown }).path;\n if (typeof path === \"string\") return path;\n }\n return null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// droid (Factory) host-state readers (~/.factory/settings.json, JSON, NO env)\n//\n// DOCS-ONLY: no droid binary is present on this box. The settings.json key\n// shapes below (`enabledPlugins[\"<id>@agent-connector\"] === true` +\n// `extraKnownMarketplaces[\"agent-connector\"].source`) follow the documented\n// factory plugin model — confirm the exact JSON shapes live when a droid binary\n// is available. Both readers read defensively (refuse only on positive evidence).\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Factory's settings file: ~/.factory/settings.json (NO dedicated env). */\nexport function factorySettingsPath(): string {\n return join(homedir(), \".factory\", \"settings.json\");\n}\n\n/** The settings.json key our marketplace install creates for `id` (== codex's). */\nexport function droidPluginKey(connectorId: string): string {\n return `${connectorId}@${MARKETPLACE_NAME}`;\n}\n\n/** The `enabledPlugins` map from droid's settings.json (null when unreadable). */\nfunction readDroidEnabledPlugins(): Record<string, unknown> | null {\n const parsed = readJsonFile(factorySettingsPath());\n const plugins = parsed?.enabledPlugins;\n if (plugins && typeof plugins === \"object\" && !Array.isArray(plugins)) {\n return plugins as Record<string, unknown>;\n }\n return null;\n}\n\n/**\n * True when droid's settings.json carries\n * `enabledPlugins[\"<id>@agent-connector\"] === true`. DOCS-only: the strict\n * `=== true` value shape is unverified live (a future droid may store an object).\n */\nexport function droidPluginInstalled(connectorId: string): boolean {\n const plugins = readDroidEnabledPlugins();\n if (!plugins) return false;\n return plugins[droidPluginKey(connectorId)] === true;\n}\n\n/** True when ANY `*@agent-connector` plugin remains enabled in droid's settings. */\nexport function anyDroidAgentConnectorPlugins(): boolean {\n const plugins = readDroidEnabledPlugins();\n if (!plugins) return false;\n return Object.entries(plugins).some(\n ([key, value]) => key.endsWith(`@${MARKETPLACE_NAME}`) && value === true,\n );\n}\n\n/**\n * The directory droid's settings.json records as\n * `extraKnownMarketplaces[\"<name>\"].source`, or null when not registered /\n * unreadable. Both a presence probe and the NAME-COLLISION check (a registration\n * pointing somewhere other than our staging root belongs to the user). DOCS-only:\n * the local-path `source` field shape is undocumented, so this reads defensively\n * — accepts either a bare string source or an object carrying a `path`/`source`\n * string, and NORMALIZES to a single string for the samePath compare.\n */\nexport function droidMarketplaceSource(name: string): string | null {\n const parsed = readJsonFile(factorySettingsPath());\n const known = parsed?.extraKnownMarketplaces;\n if (!known || typeof known !== \"object\" || Array.isArray(known)) return null;\n const entry = (known as Record<string, unknown>)[name];\n if (entry == null) return null;\n if (typeof entry === \"string\") return entry;\n if (typeof entry === \"object\" && !Array.isArray(entry)) {\n const e = entry as { source?: unknown; path?: unknown };\n if (typeof e.source === \"string\") return e.source;\n if (typeof e.path === \"string\") return e.path;\n }\n return null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// agy (Antigravity) host-state readers (~/.gemini/config/plugins, NO env)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** agy's config dir: ~/.gemini/config (NO dedicated env — HOME isolation). */\nexport function agyConfigDir(): string {\n return join(homedir(), \".gemini\", \"config\");\n}\n\n/** agy's plugins dir: ~/.gemini/config/plugins. */\nexport function agyPluginsDir(): string {\n return join(agyConfigDir(), \"plugins\");\n}\n\n/**\n * Candidate `import_manifest.json` locations. agy 1.0.7 records the manifest at\n * `<config>/plugins/import_manifest.json` on POSIX but at\n * `<config>/import_manifest.json` on Windows (live-confirmed on the my-window\n * box) — both are probed, then the copied plugin dir is the final fallback.\n */\nexport function agyImportManifestPaths(): string[] {\n return [\n join(agyConfigDir(), \"import_manifest.json\"), // win32 location\n join(agyPluginsDir(), \"import_manifest.json\"), // posix location\n ];\n}\n\n/** @deprecated single-location accessor — prefer {@link agyImportManifestPaths}. */\nexport function agyImportManifestPath(): string {\n return join(agyPluginsDir(), \"import_manifest.json\");\n}\n\n/**\n * True when `id` is installed per agy's import manifest (`imports[].name`),\n * checking BOTH the win32 and posix manifest locations, with a fallback probe of\n * the copied plugin dir's plugin.json. agy's uninstall removes the plugin dir\n * and clears the manifest entry (live-verified on Windows: install → dir +\n * manifest present, uninstall → both gone, re-install idempotent).\n */\nexport function agyPluginInstalled(connectorId: string): boolean {\n for (const manifest of agyImportManifestPaths()) {\n try {\n const parsed = JSON.parse(readFileSync(manifest, \"utf8\")) as {\n imports?: unknown;\n };\n const imports = parsed.imports;\n if (\n Array.isArray(imports) &&\n imports.some(\n (e) =>\n e != null &&\n typeof e === \"object\" &&\n (e as { name?: unknown }).name === connectorId,\n )\n ) {\n return true;\n }\n } catch {\n /* absent / unreadable manifest → try the next candidate */\n }\n }\n return existsSync(join(agyPluginsDir(), connectorId, \"plugin.json\"));\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Gemini CLI host-state readers (~/.gemini/extensions, NO env — HOME isolation)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Gemini CLI's config dir: ~/.gemini (NO dedicated config-dir env — isolation is\n * via HOME, live-confirmed on gemini 0.36.0). DISTINCT from agy's ~/.gemini/config\n * tree: gemini extensions land under ~/.gemini/extensions/<id>/.\n */\nexport function geminiConfigDir(): string {\n return join(homedir(), \".gemini\");\n}\n\n/**\n * True when `id` is installed per Gemini's own extension store: the marker file\n * ~/.gemini/extensions/<id>/gemini-extension.json exists. Gemini exits 0 even on\n * a logical failure, so this fs probe — not the exit code — is the source of\n * truth (the driver re-probes after every spawn).\n */\nexport function geminiExtensionInstalled(connectorId: string): boolean {\n return existsSync(\n join(geminiConfigDir(), \"extensions\", connectorId, \"gemini-extension.json\"),\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Qwen Code host-state readers (~/.qwen/extensions, NO env — HOME isolation)\n//\n// DOCS-ONLY: no qwen binary is present on this box. qwen-code is a gemini-cli\n// fork, so the extension store SHAPE (~/.qwen/extensions/<id>/qwen-extension.json)\n// mirrors gemini's — confirm the marker filename live when a qwen binary lands.\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Qwen Code's config dir: ~/.qwen (NO dedicated config-dir env — isolation is\n * via HOME, like its gemini-cli parent). DOCS-only: confirm live when a qwen\n * binary is available. DISTINCT from ~/.gemini (the gemini-cli store).\n */\nexport function qwenConfigDir(): string {\n return join(homedir(), \".qwen\");\n}\n\n/**\n * True when `id` is installed per Qwen's own extension store: the marker file\n * ~/.qwen/extensions/<id>/qwen-extension.json exists. Mirrors gemini's fs probe\n * (qwen is a gemini fork); the driver re-probes after every spawn. DOCS-only:\n * the marker filename (`qwen-extension.json`) is unverified live.\n */\nexport function qwenExtensionInstalled(connectorId: string): boolean {\n return existsSync(\n join(qwenConfigDir(), \"extensions\", connectorId, \"qwen-extension.json\"),\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// npm-local host-state readers (opencode / kilo / kilo-cli — XDG_CONFIG_HOME)\n//\n// These hosts have NO uninstall verb; installs are recorded as `file://<dir>`\n// entries in a `plugin` array inside an opencode-style config file, which the\n// driver EDITS to remove. This module is a spawn/shared-free LEAF, so a tiny\n// posix-resolve path comparison is inlined here rather than importing\n// shared.ts's samePath (and samePath does not strip the `file://` scheme).\n// ─────────────────────────────────────────────────────────────────────────\n\n/** opencode's config dir: $XDG_CONFIG_HOME/opencode (or ~/.config/opencode). */\nexport function opencodeConfigDir(): string {\n return join(xdgConfigHome(), \"opencode\");\n}\n\n/** kilo / kilo-cli's config dir: $XDG_CONFIG_HOME/kilo (or ~/.config/kilo). */\nexport function kiloConfigDir(): string {\n return join(xdgConfigHome(), \"kilo\");\n}\n\n/**\n * Candidate config filenames per npm-local host, most-preferred first. opencode\n * accepts opencode.jsonc / opencode.json / config.json (JSONC — comments\n * tolerated); kilo / kilo-cli use opencode.json (plain JSON).\n */\nfunction npmConfigCandidates(platform: PlatformId): string[] {\n return platform === \"opencode\"\n ? [\"opencode.jsonc\", \"opencode.json\", \"config.json\"]\n : [\"opencode.json\"];\n}\n\n/** The config DIR for an npm-local host (opencode vs kilo/kilo-cli). */\nfunction npmConfigDir(platform: PlatformId): string {\n return platform === \"opencode\" ? opencodeConfigDir() : kiloConfigDir();\n}\n\n/**\n * The config FILE path for `platform`: the first existing candidate, else the\n * most-preferred candidate (the path a fresh install would write). Never throws.\n */\nexport function npmConfigFilePath(platform: PlatformId): string {\n const dir = npmConfigDir(platform);\n const candidates = npmConfigCandidates(platform);\n for (const name of candidates) {\n if (existsSync(join(dir, name))) return join(dir, name);\n }\n return join(dir, candidates[0]!);\n}\n\n/** Strip a single leading `file://` (and `file://localhost/`) scheme prefix. */\nexport function stripFileScheme(entry: string): string {\n let s = entry;\n if (s.startsWith(\"file://localhost/\")) s = s.slice(\"file://localhost\".length);\n else if (s.startsWith(\"file://\")) s = s.slice(\"file://\".length);\n return s;\n}\n\n/**\n * Strip `//` line and `/* */` block comments from JSONC, STRING-AWARE: a `//`\n * or `/*` inside a JSON string literal (e.g. the `file://` in a plugin entry) is\n * left intact. Scans char-by-char tracking string + escape state — a naive\n * regex would corrupt `\"file:///dir\"` by eating from the embedded `//`.\n */\nfunction stripJsoncComments(text: string): string {\n let out = \"\";\n let inString = false;\n let inLine = false;\n let inBlock = false;\n for (let i = 0; i < text.length; i++) {\n const c = text[i]!;\n const next = i + 1 < text.length ? text[i + 1]! : \"\";\n if (inLine) {\n if (c === \"\\n\") {\n inLine = false;\n out += c;\n }\n continue;\n }\n if (inBlock) {\n if (c === \"*\" && next === \"/\") {\n inBlock = false;\n i++;\n }\n continue;\n }\n if (inString) {\n out += c;\n if (c === \"\\\\\") {\n // Copy the escaped char verbatim (covers \\\" and \\\\).\n if (next !== \"\") {\n out += next;\n i++;\n }\n } else if (c === '\"') {\n inString = false;\n }\n continue;\n }\n if (c === '\"') {\n inString = true;\n out += c;\n continue;\n }\n if (c === \"/\" && next === \"/\") {\n inLine = true;\n i++;\n continue;\n }\n if (c === \"/\" && next === \"*\") {\n inBlock = true;\n i++;\n continue;\n }\n out += c;\n }\n return out;\n}\n\n/** Tolerant JSONC → object: strip // line and block comments, then JSON.parse. */\nfunction parseJsonc(text: string): Record<string, unknown> | null {\n // A parse failure degrades to null (the \"refuse only on positive evidence\"\n // convention) rather than throwing.\n try {\n const parsed = JSON.parse(stripJsoncComments(text)) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n /* unreadable / not JSONC → null */\n }\n return null;\n}\n\n/** Read + parse an npm-local host's config file ({}-shaped or null). Never throws. */\nexport function readNpmConfig(platform: PlatformId): Record<string, unknown> | null {\n const file = npmConfigFilePath(platform);\n try {\n return parseJsonc(readFileSync(file, \"utf8\"));\n } catch {\n return null;\n }\n}\n\n/**\n * Convert an npm `plugin`-array entry to a native absolute path. opencode/kilo\n * record a proper `file://` URL — on Windows that is `file:///C:/Users/…` (drive\n * letter + forward slashes), which a textual `file://`-strip leaves as the broken\n * `/C:/Users/…`. `fileURLToPath` does the correct cross-platform decode\n * (`file:///C:/x`→`C:\\x`, `file:///tmp/x`→`/tmp/x`). Bare-path entries (also\n * accepted by the hosts) and any malformed URL fall back to a textual strip.\n */\nfunction npmEntryToPath(entry: string): string {\n if (/^file:\\/\\//i.test(entry)) {\n try {\n return fileURLToPath(entry);\n } catch {\n /* malformed file URL (e.g. backslashes) → manual strip below */\n }\n }\n return stripFileScheme(entry);\n}\n\n/**\n * Path equality for the npm probe. Resolve symlinks with `realpathSync.native`\n * when the path exists (catches macOS `/var/folders`→`/private/var/folders` and\n * win32 8.3 short paths — the host records the realpath, we hold the symlink),\n * else lexical `resolve()`; case-fold on win32. Inlined here (leaf module)\n * rather than importing shared.ts's samePath, which pulls in the spawn layer.\n */\nfunction npmPathEquals(a: string, b: string): boolean {\n const norm = (p: string): string => {\n let s: string;\n try {\n s = realpathSync.native(p);\n } catch {\n s = resolve(p);\n }\n return process.platform === \"win32\" ? s.toLowerCase() : s;\n };\n return norm(a) === norm(b);\n}\n\n/**\n * The `plugin`-array ENTRY (the raw string, including any `file://`) whose value\n * — after stripping a leading `file://` — path-equals `id`'s staged bundle dir,\n * or null when no entry references it. Used both as the install probe and to\n * locate the exact entry the uninstall edit must drop.\n */\nexport function npmPluginArrayEntry(\n platform: PlatformId,\n connectorId: string,\n): string | null {\n const cfg = readNpmConfig(platform);\n if (!cfg) return null;\n const arr = cfg[\"plugin\"];\n if (!Array.isArray(arr)) return null;\n const target = join(npmStagingRoot(), connectorId);\n for (const raw of arr) {\n if (typeof raw !== \"string\") continue;\n if (npmPathEquals(npmEntryToPath(raw), target)) return raw;\n }\n return null;\n}\n\n/** True when `platform`'s config `plugin` array references `id`'s staged bundle. */\nexport function npmPluginInstalled(platform: PlatformId, connectorId: string): boolean {\n return npmPluginArrayEntry(platform, connectorId) != null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Cross-host marketplace-install EVIDENCE (the double-install guard's probe)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Read-only evidence that `connectorId` is marketplace/plugin-installed on\n * `platform`: the state record first, then a cheap fs probe of the host's own\n * plugin state (catches manual installs from `package`'s printed instructions).\n * Returns a human-readable description of the evidence, or null when none.\n */\nexport function marketplaceEvidence(\n connectorId: string,\n platform: PlatformId,\n): string | null {\n if (readMarketplaceInstalls(connectorId)[platform]) {\n return \"recorded in marketplace-installs.json\";\n }\n switch (platform) {\n case \"claude-code\":\n if (claudePluginInstalled(connectorId)) {\n return `${claudePluginKey(connectorId)} listed in Claude's installed_plugins.json`;\n }\n return null;\n case \"gemini-cli\":\n return geminiExtensionInstalled(connectorId)\n ? `extension ${connectorId} listed in Gemini's extensions store`\n : null;\n case \"qwen-code\":\n return qwenExtensionInstalled(connectorId)\n ? `extension ${connectorId} listed in Qwen's extensions store`\n : null;\n case \"droid\":\n return droidPluginInstalled(connectorId)\n ? `${droidPluginKey(connectorId)} enabled in droid's settings.json`\n : null;\n case \"antigravity\":\n case \"antigravity-cli\": {\n const dir = join(homedir(), \".gemini\", \"config\", \"plugins\", connectorId);\n return existsSync(dir) ? `plugin dir ${dir} exists` : null;\n }\n case \"opencode\":\n case \"kilo\":\n case \"kilo-cli\":\n return npmPluginInstalled(platform, connectorId)\n ? `${connectorId} listed in ${platform}'s config plugin array`\n : null;\n case \"codex\": {\n const dir = join(\n codexConfigHome(),\n \"plugins\",\n \"cache\",\n MARKETPLACE_NAME,\n connectorId,\n );\n return existsSync(dir) ? `codex plugin cache ${dir} exists` : null;\n }\n case \"copilot-cli\":\n return copilotPluginInstalled(connectorId)\n ? `${copilotPluginKey(connectorId)} listed in Copilot's config.json`\n : null;\n default:\n return null;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Content hashing — staged-bundle drift detection\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Deterministic sha256 over every file under `dir` (sorted relative paths +\n * bytes). Empty string when the dir is missing/unreadable. Used to record the\n * staged bundle's content at install time and detect drift later (claude caches\n * a versioned COPY, so an edited connector goes silently stale without this).\n */\nexport function hashDirectory(dir: string): string {\n if (!existsSync(dir)) return \"\";\n const files: string[] = [];\n const walk = (d: string): void => {\n let entries: string[];\n try {\n entries = readdirSync(d);\n } catch {\n return;\n }\n for (const name of entries.sort()) {\n const full = join(d, name);\n let st;\n try {\n st = statSync(full);\n } catch {\n continue;\n }\n if (st.isDirectory()) walk(full);\n else if (st.isFile()) files.push(full);\n }\n };\n walk(dir);\n const hash = createHash(\"sha256\");\n for (const file of files.sort()) {\n hash.update(relative(dir, file).split(\"\\\\\").join(\"/\"));\n hash.update(\"\\0\");\n try {\n hash.update(readFileSync(file));\n } catch {\n /* unreadable file contributes only its path */\n }\n hash.update(\"\\0\");\n }\n return hash.digest(\"hex\");\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sdk/introspect.ts"],"sourcesContent":["/**\n * sdk/introspect — host-capability introspection over the adapter registry.\n *\n * A thin, READ-ONLY query layer above {@link loadAdapter} / {@link allAdapters}:\n * \"which hosts can honor surface X?\" and \"what can host Y do?\". It exists so a\n * connector author (or a docs/CLI surface) can answer those questions WITHOUT\n * reaching into adapter internals or re-deriving the per-surface capability\n * rules — the predicates below are the single source of truth for mapping the\n * raw {@link PlatformCapabilities} flags onto the developer-facing surface names.\n *\n * Everything is async because adapters load lazily (one entry per platform in\n * the registry, imported only when queried).\n */\n\nimport type {\n HookEventName,\n PlatformCapabilities,\n PlatformId,\n} from \"../core/types.js\";\nimport { allAdapters, loadAdapter } from \"../adapters/registry.js\";\n\n/**\n * The single source of truth mapping each normalized {@link HookEventName} onto\n * the {@link PlatformCapabilities} flag that says \"this host can natively FIRE\n * this event\". The optional flags (the newer E1 events + PostCompact) are read\n * as `?? false`, mirroring the supportsCommands precedent — a host that leaves a\n * flag unset cannot fire that event, so a declared hook for it skip-warns at\n * install and `explain` reports it as not honored.\n *\n * This is the per-event signal {@link explain}'s hooks row evaluates against the\n * connector's DECLARED events (so a Stop-only connector on a PreToolUse-only\n * host no longer falsely shows `native`), and the same signal `simulate`'s real\n * parse→handler→format chain expresses at runtime.\n */\nexport function hostCanFireEvent(\n c: PlatformCapabilities,\n event: HookEventName,\n): boolean {\n switch (event) {\n case \"SessionStart\":\n return c.sessionStart;\n case \"SessionEnd\":\n return c.sessionEnd;\n case \"UserPromptSubmit\":\n return c.userPromptSubmit;\n case \"PreToolUse\":\n return c.preToolUse;\n case \"PostToolUse\":\n return c.postToolUse;\n case \"PreCompact\":\n return c.preCompact;\n case \"Stop\":\n return c.stop;\n case \"Notification\":\n return c.notification;\n case \"PermissionRequest\":\n return c.permissionRequest ?? false;\n case \"PostToolUseFailure\":\n return c.postToolUseFailure ?? false;\n case \"SubagentStart\":\n return c.subagentStart ?? false;\n case \"SubagentStop\":\n return c.subagentStop ?? false;\n case \"PostCompact\":\n return c.postCompact ?? false;\n }\n}\n\n/** The full normalized hook-event union, in canonical order. */\nconst ALL_HOOK_EVENTS: HookEventName[] = [\n \"SessionStart\",\n \"SessionEnd\",\n \"UserPromptSubmit\",\n \"PreToolUse\",\n \"PostToolUse\",\n \"PreCompact\",\n \"Stop\",\n \"Notification\",\n \"PermissionRequest\",\n \"PostToolUseFailure\",\n \"SubagentStart\",\n \"SubagentStop\",\n \"PostCompact\",\n];\n\n/**\n * The developer-facing surface names a connector can declare. These mirror the\n * top-level {@link import(\"../core/types.js\").ConnectorConfig} keys (server,\n * hooks, commands, …) plus the platform-scoped `configPatch` / `nativeHooks`\n * escape hatches — the same vocabulary the install diff and `explain` use.\n */\nexport type SurfaceName =\n | \"server\"\n | \"hooks\"\n | \"commands\"\n | \"skills\"\n | \"subagents\"\n | \"memory\"\n | \"statusline\"\n | \"actions\"\n | \"configPatch\"\n | \"nativeHooks\";\n\n/**\n * Pure capability predicate per surface — `true` when the host can NATIVELY\n * honor that surface. The optional capability flags are read as `?? false`\n * (the supportsCommands precedent in core/types), so a host that simply leaves\n * a flag unset reports the surface as unsupported (the installer skip-warns it).\n *\n * `hooks` here is the COARSE host-capability predicate: `true` when the host can\n * fire AT LEAST ONE normalized event (it derives from {@link hostCanFireEvent},\n * the per-event source of truth, so the two never drift). It is the right answer\n * for the connector-less queries ({@link hostsSupporting} / {@link surfaceSupport}:\n * \"which hosts have a hook runtime at all?\"). It is deliberately NOT used by\n * {@link explain}, which must judge the hooks surface against the connector's\n * SPECIFIC declared events — a Stop-only connector on a PreToolUse-only host is\n * NOT honored even though the host has a hook runtime (see {@link explain}).\n */\nexport const SURFACE_PREDICATES: Record<\n SurfaceName,\n (c: PlatformCapabilities) => boolean\n> = {\n server: (c) => c.transports.length > 0,\n hooks: (c) => ALL_HOOK_EVENTS.some((e) => hostCanFireEvent(c, e)),\n commands: (c) => c.supportsCommands ?? false,\n skills: (c) => c.supportsSkills ?? false,\n subagents: (c) => c.supportsSubagents ?? false,\n memory: (c) => c.supportsMemory ?? false,\n statusline: (c) => c.supportsStatusline ?? false,\n // Emitters ship on droid + hermes + warp plus the ts-plugin slash-command\n // hosts omp + openclaw (+ the nemoclaw fork) — the hosts with a verifiable\n // target. Every other host leaves supportsActions unset, so explain() marks\n // actions skip-warn there — the honest state for a host with no emission target.\n actions: (c) => c.supportsActions ?? false,\n configPatch: (c) => c.supportsConfigPatch ?? false,\n nativeHooks: (c) => c.supportsNativeHooks ?? false,\n};\n\n/**\n * The {@link PlatformCapabilities} a host advertises, or `undefined` for an id\n * that is not in the registry (`\"unknown\"`, a usage-only id like `\"synthetic\"`,\n * or a typo) — callers branch on undefined rather than crash.\n */\nexport async function capabilitiesOf(\n host: PlatformId | string,\n): Promise<PlatformCapabilities | undefined> {\n const adapter = await loadAdapter(host);\n return adapter?.capabilities;\n}\n\n/**\n * Every registered host that can NATIVELY honor `surface`, as a sorted id list\n * (stable output for snapshots/docs). Loads all adapters in parallel and keeps\n * only those whose capabilities pass {@link SURFACE_PREDICATES}.\n */\nexport async function hostsSupporting(surface: SurfaceName): Promise<PlatformId[]> {\n const predicate = SURFACE_PREDICATES[surface];\n const adapters = await allAdapters();\n return adapters\n .filter((adapter) => predicate(adapter.capabilities))\n .map((adapter) => adapter.id)\n .sort();\n}\n\n/**\n * Convenience: does `host` natively support `surface`? `false` for an unknown\n * id (no capabilities → cannot support anything).\n */\nexport async function surfaceSupport(\n host: PlatformId | string,\n surface: SurfaceName,\n): Promise<boolean> {\n const capabilities = await capabilitiesOf(host);\n return capabilities ? SURFACE_PREDICATES[surface](capabilities) : false;\n}\n"],"mappings":";;;;;;;AAkCO,SAAS,iBACd,GACA,OACS;AACT,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE,qBAAqB;AAAA,IAChC,KAAK;AACH,aAAO,EAAE,sBAAsB;AAAA,IACjC,KAAK;AACH,aAAO,EAAE,iBAAiB;AAAA,IAC5B,KAAK;AACH,aAAO,EAAE,gBAAgB;AAAA,IAC3B,KAAK;AACH,aAAO,EAAE,eAAe;AAAA,EAC5B;AACF;AAGA,IAAM,kBAAmC;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAmCO,IAAM,qBAGT;AAAA,EACF,QAAQ,CAAC,MAAM,EAAE,WAAW,SAAS;AAAA,EACrC,OAAO,CAAC,MAAM,gBAAgB,KAAK,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAAA,EAChE,UAAU,CAAC,MAAM,EAAE,oBAAoB;AAAA,EACvC,QAAQ,CAAC,MAAM,EAAE,kBAAkB;AAAA,EACnC,WAAW,CAAC,MAAM,EAAE,qBAAqB;AAAA,EACzC,QAAQ,CAAC,MAAM,EAAE,kBAAkB;AAAA,EACnC,YAAY,CAAC,MAAM,EAAE,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK3C,SAAS,CAAC,MAAM,EAAE,mBAAmB;AAAA,EACrC,aAAa,CAAC,MAAM,EAAE,uBAAuB;AAAA,EAC7C,aAAa,CAAC,MAAM,EAAE,uBAAuB;AAC/C;AAOA,eAAsB,eACpB,MAC2C;AAC3C,QAAM,UAAU,MAAM,YAAY,IAAI;AACtC,SAAO,SAAS;AAClB;AAOA,eAAsB,gBAAgB,SAA6C;AACjF,QAAM,YAAY,mBAAmB,OAAO;AAC5C,QAAM,WAAW,MAAM,YAAY;AACnC,SAAO,SACJ,OAAO,CAAC,YAAY,UAAU,QAAQ,YAAY,CAAC,EACnD,IAAI,CAAC,YAAY,QAAQ,EAAE,EAC3B,KAAK;AACV;AAMA,eAAsB,eACpB,MACA,SACkB;AAClB,QAAM,eAAe,MAAM,eAAe,IAAI;AAC9C,SAAO,eAAe,mBAAmB,OAAO,EAAE,YAAY,IAAI;AACpE;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/spawn.ts"],"sourcesContent":["/**\n * core/spawn — cross-platform command construction.\n *\n * Borrowed from context-mode (issues #369/#372/#548/#738): bare `node` is\n * unreliable on Windows Git Bash (MSYS) and MSYS rewrites absolute paths on\n * non-C: drives. Always use the resolved runtime path, forward slashes, and\n * double-quoting. No-ops on macOS/Linux.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { isNonDefaultDataRoot } from \"./paths.js\";\nimport type { InstallScope, PlatformId, ServerDef } from \"./types.js\";\nimport type { InstallContext } from \"../adapters/spi.js\";\nimport type {\n LaunchMethod,\n TelemetryInstallScope,\n} from \"../telemetry/types.js\";\n\n/**\n * Narrow the framework's 5-value {@link InstallScope} down to the two telemetry\n * slicing buckets: only a `project`-local install is `project`; everything else\n * (system/user/profile/managed) is global and reads as `user`.\n */\nexport function narrowInstallScope(scope: InstallScope): TelemetryInstallScope {\n return scope === \"project\" ? \"project\" : \"user\";\n}\n\n/** Build `\"<nodePath>\" \"<scriptPath>\"`. */\nexport function buildNodeCommand(\n scriptPath: string,\n opts?: { nodePath?: string },\n): string {\n const nodePath = (opts?.nodePath ?? process.execPath).replace(/\\\\/g, \"/\");\n const safe = scriptPath.replace(/\\\\/g, \"/\");\n return `\"${nodePath}\" \"${safe}\"`;\n}\n\n/**\n * Strict inverse of buildNodeCommand. Returns the two quoted tokens, or null if\n * `cmd` was not produced by buildNodeCommand (no fragile last-whitespace tail\n * grabbing — that was the #548 doubled-path bug when paths contained spaces).\n */\nexport function parseNodeCommand(\n cmd: string,\n): { nodePath: string; scriptPath: string } | null {\n if (typeof cmd !== \"string\" || cmd.length === 0) return null;\n const m = cmd.match(/^\"([^\"]+)\"\\s+\"([^\"]+)\"\\s*$/);\n if (!m || !m[1] || !m[2]) return null;\n return { nodePath: m[1], scriptPath: m[2] };\n}\n\n/** Quote a single argument for inclusion in a host hook command string. */\nexport function quoteArg(arg: string): string {\n return `\"${arg.replace(/\\\\/g, \"/\")}\"`;\n}\n\n/**\n * True when `hay` (slash-normalized) has the quoted home-bin path IMMEDIATELY\n * followed by `verb` — i.e. the verb sits in its command SLOT (right after the\n * home binary), not merely somewhere in the argument list. This distinguishes\n * `\"<bin>\" statusline …` from `\"<bin>\" action … statusline …`: the action verb\n * takes a free-form `actionId` positional that can legitimately equal another\n * verb's name (`statusline` / `usage-event` / `hook`), and only the SLOT\n * position identifies the real verb. `homeBinNorm` is the slash-normalized\n * home-bin path the caller already derived.\n */\nfunction hasVerbSlot(hay: string, homeBinNorm: string, verb: string): boolean {\n return hay.includes(`\"${homeBinNorm}\" ${verb} `);\n}\n\n/**\n * Build the universal hook command a host config points at:\n * \"<homeBin>\" hook <platformId> <event> --connector <id>\n * Pointing every host at the one stable home binary is how a single update\n * propagates everywhere (docs/ARCHITECTURE.md §3 R1).\n */\nexport function buildHomeBinHookCommand(\n homeBinPath: string,\n platformId: string,\n event: string,\n connectorId: string,\n): string {\n return `${quoteArg(homeBinPath)} hook ${platformId} ${event} --connector ${connectorId}`;\n}\n\n/**\n * True when `command` is a home-bin hook command for exactly `connectorId`.\n *\n * Critical: the connector id is the LAST token of the hook command\n * (`... --connector <id>`), so a naive `command.includes(\"--connector \" + id)`\n * collides on shared-prefix ids — `--connector acme-db` contains\n * `--connector acme`, which would let uninstalling `acme` strip `acme-db`'s\n * hooks. We anchor the id token: the character after it must be end-of-string,\n * whitespace, or a closing double-quote (the JSON-embedded case). Connector ids\n * are kebab-case (ID_RE) so they never contain whitespace or quotes themselves.\n */\nexport function isHomeBinHookCommand(\n command: string | undefined,\n homeBinPath: string,\n connectorId: string,\n): boolean {\n if (!command) return false;\n // The stored command was forward-slashed by quoteArg(); homeBinPath() yields\n // NATIVE (back)slashes on Windows (node:path join). Normalize BOTH sides so the\n // ownership substring test holds on win32 (without this, detection always\n // returns false there → duplicate hooks on re-install + orphans on uninstall).\n // No-op on POSIX.\n const hay = command.replace(/\\\\/g, \"/\");\n if (!hay.includes(homeBinPath.replace(/\\\\/g, \"/\"))) return false;\n const token = `--connector ${connectorId}`;\n let from = 0;\n for (;;) {\n const idx = hay.indexOf(token, from);\n if (idx < 0) return false;\n const after = hay.charAt(idx + token.length);\n if (after === \"\" || after === '\"' || /\\s/.test(after)) return true;\n from = idx + token.length;\n }\n}\n\n/**\n * Build the OPT-IN host-native usage-event command an AfterModel / PostInvocation\n * hook points at:\n * \"<homeBin>\" usage-event <platformId> --connector <id>\n * Mirrors {@link buildHomeBinHookCommand} (same anchoring rules apply) but routes\n * to the hidden `usage-event` entrypoint, which records a DISTINCT `model_turn`\n * row instead of dispatching a connector handler.\n */\nexport function buildUsageEventCommand(\n homeBinPath: string,\n platformId: string,\n connectorId: string,\n): string {\n return `${quoteArg(homeBinPath)} usage-event ${platformId} --connector ${connectorId}`;\n}\n\n/**\n * True when `command` is OUR host-native usage-event command for exactly\n * `connectorId`. Same end-of-token anchoring as {@link isHomeBinHookCommand} so a\n * shared-prefix id can't collide; additionally requires the ` usage-event ` verb\n * so it is never confused with a plain `hook` command during uninstall.\n */\nexport function isUsageEventCommand(\n command: string | undefined,\n homeBinPath: string,\n connectorId: string,\n): boolean {\n if (!command) return false;\n // Normalize separators like isHomeBinHookCommand (win32 ownership detection).\n const hay = command.replace(/\\\\/g, \"/\");\n const homeBinNorm = homeBinPath.replace(/\\\\/g, \"/\");\n if (!hay.includes(homeBinNorm)) return false;\n if (!hasVerbSlot(hay, homeBinNorm, \"usage-event\")) return false;\n return isHomeBinHookCommand(command, homeBinPath, connectorId);\n}\n\n/**\n * Build the home-bin statusline command a host's status line config points at:\n * \"<homeBin>\" statusline <platformId> --connector <id>\n * Mirrors {@link buildHomeBinHookCommand} / {@link buildUsageEventCommand} (the\n * same single-home-binary indirection) but routes to the `statusline`\n * entrypoint, which renders the connector's HUD line for the host.\n */\nexport function buildHomeBinStatuslineCommand(\n homeBinPath: string,\n platformId: string,\n connectorId: string,\n): string {\n return `${quoteArg(homeBinPath)} statusline ${platformId} --connector ${connectorId}`;\n}\n\n/**\n * True when `command` is OUR home-bin statusline command for exactly\n * `connectorId`. Same end-of-token anchoring as {@link isHomeBinHookCommand} so a\n * shared-prefix id can't collide; additionally requires the ` statusline ` verb\n * so it is never confused with a plain `hook` / `usage-event` command.\n */\nexport function isHomeBinStatuslineCommand(\n command: string | undefined,\n homeBinPath: string,\n connectorId: string,\n): boolean {\n if (!command) return false;\n // Normalize separators like isHomeBinHookCommand (win32 ownership detection).\n const hay = command.replace(/\\\\/g, \"/\");\n const homeBinNorm = homeBinPath.replace(/\\\\/g, \"/\");\n if (!hay.includes(homeBinNorm)) return false;\n if (!hasVerbSlot(hay, homeBinNorm, \"statusline\")) return false;\n return isHomeBinHookCommand(command, homeBinPath, connectorId);\n}\n\n/**\n * Build the home-bin action command a future host affordance (slash command /\n * keybinding) points at:\n * \"<homeBin>\" action <platformId> <actionId> --connector <id>\n * Mirrors {@link buildHomeBinStatuslineCommand} (the same single-home-binary\n * indirection) but routes to the `action` entrypoint, which re-imports the\n * connector and runs the named action's run(ctx). v1 ships only this command\n * builder + the runtime verb; no adapter EMITS the affordance yet.\n */\nexport function buildHomeBinActionCommand(\n homeBinPath: string,\n platformId: string,\n actionId: string,\n connectorId: string,\n): string {\n return `${quoteArg(homeBinPath)} action ${platformId} ${actionId} --connector ${connectorId}`;\n}\n\n/**\n * True when `command` is OUR home-bin action command for exactly `connectorId`.\n * Same end-of-token anchoring as {@link isHomeBinHookCommand} so a shared-prefix\n * id can't collide; additionally requires the ` action ` verb so it is never\n * confused with a plain `hook` / `usage-event` / `statusline` command.\n */\nexport function isHomeBinActionCommand(\n command: string | undefined,\n homeBinPath: string,\n connectorId: string,\n): boolean {\n if (!command) return false;\n // Normalize separators like isHomeBinHookCommand (win32 ownership detection).\n const hay = command.replace(/\\\\/g, \"/\");\n const homeBinNorm = homeBinPath.replace(/\\\\/g, \"/\");\n if (!hay.includes(homeBinNorm)) return false;\n if (!hasVerbSlot(hay, homeBinNorm, \"action\")) return false;\n return isHomeBinHookCommand(command, homeBinPath, connectorId);\n}\n\n/**\n * Single source of truth for \"is OPT-IN host-native turn-usage capture enabled\n * for this connector?\" — read by the Gemini / Antigravity adapters at install\n * time to decide whether to ALSO write the AfterModel / PostInvocation usage hook.\n * Enabled when the connector opts in via telemetry.hostNativeUsage OR when the\n * install-time env switch AGENT_CONNECTOR_HOST_NATIVE=1 forces it on. Off by\n * default (privacy: host-native capture is never installed silently).\n */\nexport function isHostNativeUsageEnabled(\n telemetry: { enabled: boolean; hostNativeUsage?: boolean },\n env: NodeJS.ProcessEnv = process.env,\n): boolean {\n if (telemetry.enabled !== true) return false;\n if (env.AGENT_CONNECTOR_HOST_NATIVE === \"1\") return true;\n return telemetry.hostNativeUsage === true;\n}\n\n/**\n * Single source of truth for \"should this server be wrapped by `serve` for\n * telemetry?\" — so every adapter behaves identically even for a server that\n * skipped normalizeServer (e.g. a per-platform override). On by default for an\n * stdio server with a command, unless explicitly disabled or telemetry is off.\n */\nexport function shouldWrapForTelemetry(\n server: ServerDef,\n telemetry: { enabled: boolean },\n): boolean {\n return (\n server.wrapForTelemetry !== false &&\n telemetry.enabled === true &&\n server.transport === \"stdio\" &&\n typeof server.command === \"string\" &&\n server.command !== \"\"\n );\n}\n\n/**\n * Build the telemetry-wrapping server command:\n * \"<homeBin>\" serve --connector <id> --scope <scope> --host <platformId> -- <realCommand> <realArgs...>\n * Used when a stdio server opts into transparent telemetry capture.\n *\n * `scope` records the install dimension (global user vs project-local) on every\n * telemetry row so usage can be sliced by it later. It is OPTIONAL for backward\n * compatibility: when omitted, the `--scope` flag is not emitted and the runtime\n * treats the scope as \"unknown\".\n *\n * `platformId` bakes the install TARGET platform into the wrapper so the proxy\n * stamps `hostPlatform` correctly even under a headless spawn where runtime env\n * markers are absent (detectRuntimeHost only knows claude-code/cursor/codex and\n * otherwise mis-attributes). Emitted as `--host <platformId>` in the FLAG\n * section (before `--`). It is OPTIONAL + backward-compatible: when omitted the\n * `--host` flag is not emitted and the runtime falls back to env detection.\n *\n * `dataDir` pins the framework data-root the spawned `serve` child must use to\n * resolve the connector record (and write telemetry). Emitted as `--data-dir\n * <path>` in the FLAG section. It exists because some hosts (codex strips the\n * MCP child's environment) do NOT propagate `AGENT_CONNECTOR_DATA_DIR` to the\n * spawned server — so a child that relied on inheriting it would resolve the\n * DEFAULT `~/.agent-connector` and die with \"Connector not registered\" when the\n * install wrote the record under an OVERRIDDEN root. Passing it explicitly makes\n * the wrap env-independent for any env-stripping host. OPTIONAL + backward-\n * compatible: callers pass it only for a non-default root (a default-root install\n * stays flag-free and relies on the correct fallback), and when omitted the\n * `--data-dir` flag is not emitted and the runtime resolves the data-root the\n * usual way.\n */\nexport function buildServeWrapperCommand(\n homeBinPath: string,\n connectorId: string,\n realCommand: string,\n realArgs: string[],\n scope?: InstallScope,\n platformId?: PlatformId,\n dataDir?: string,\n): { command: string; args: string[] } {\n const flags = [\"serve\", \"--connector\", connectorId];\n if (scope !== undefined) flags.push(\"--scope\", narrowInstallScope(scope));\n if (platformId !== undefined) flags.push(\"--host\", platformId);\n if (dataDir !== undefined && dataDir !== \"\") flags.push(\"--data-dir\", dataDir);\n return {\n command: homeBinPath,\n args: [...flags, \"--\", realCommand, ...realArgs],\n };\n}\n\n/**\n * Telemetry serve-wrap for a stdio command: when the connector opts in, route\n * `command`/`args` through `<homeBin> serve --connector <id> -- …`; otherwise\n * return them unchanged. Consolidates the per-host wrap snippet — the caller\n * keeps its own command/args seeding + downstream env-resolution + entry shaping.\n *\n * When the install's framework data-root is NON-DEFAULT (overridden via\n * AGENT_CONNECTOR_DATA_DIR), the wrap also carries an explicit `--data-dir\n * <root>` so the spawned `serve` child resolves the connector record from that\n * root even on hosts that strip the child's environment (codex). A default-root\n * install emits no flag and relies on the correct runtime fallback.\n */\nexport function buildWrappedStdio(\n ctx: Pick<InstallContext, \"homeBinPath\" | \"connector\" | \"scope\" | \"dataRoot\">,\n server: ServerDef,\n platformId: PlatformId,\n command: string,\n args: string[],\n): { command: string; args: string[] } {\n if (!shouldWrapForTelemetry(server, ctx.connector.telemetry)) {\n return { command, args };\n }\n const dataDir = isNonDefaultDataRoot(ctx.dataRoot) ? ctx.dataRoot : undefined;\n return buildServeWrapperCommand(\n ctx.homeBinPath,\n ctx.connector.id,\n command,\n args,\n ctx.scope,\n platformId,\n dataDir,\n );\n}\n\n/** Injectable deps for {@link resolveSpawnCommand} (so the win32 branch is unit-testable on POSIX). */\nexport interface SpawnResolveDeps {\n platform?: NodeJS.Platform;\n /** PATH value (`;`-separated on Windows). */\n pathEnv?: string;\n /** PATHEXT value (`;`-separated). */\n pathExt?: string;\n /** File-existence probe (defaults to fs.existsSync). */\n exists?: (p: string) => boolean;\n}\n\n/** How a child command should be handed to spawn() on this platform. */\nexport interface SpawnResolution {\n /** The command to pass to spawn() (an absolute path on win32 when resolved). */\n file: string;\n /**\n * Whether spawn() MUST use `shell: true`. On native Windows a `.cmd`/`.bat`\n * shim (npx.cmd, pnpm.cmd, the agent-connector.cmd launcher) cannot be run by\n * raw CreateProcess — Node throws EINVAL since the 2024 security fix unless a\n * shell is used. `.exe` resolves directly with no shell.\n */\n needsShell: boolean;\n}\n\n/**\n * Resolve a child command for spawn() across platforms. NO-OP on macOS/Linux\n * (returns the command unchanged, no shell). On native Windows it fixes the two\n * spawn failure modes for bare package runners:\n * 1. raw CreateProcess ignores PATHEXT, so spawn(\"npx\") → ENOENT even though\n * `npx.cmd` is on PATH. We search PATH × PATHEXT and return the resolved\n * absolute path.\n * 2. a resolved `.cmd`/`.bat` still cannot be launched without a shell, so we\n * flag `needsShell` for those (and `.exe`/`.com` run directly).\n * A bare name that PATH search misses falls back to `{ file: command,\n * needsShell: true }` so cmd.exe's own PATHEXT lookup still finds it.\n */\nexport function resolveSpawnCommand(\n command: string,\n deps: SpawnResolveDeps = {},\n): SpawnResolution {\n const platform = deps.platform ?? process.platform;\n if (platform !== \"win32\") return { file: command, needsShell: false };\n\n const isBatch = (s: string): boolean => /\\.(cmd|bat)$/i.test(s);\n\n // Already a path or already has an extension → don't search; only decide shell.\n if (/[\\\\/]/.test(command) || /\\.[a-z0-9]+$/i.test(command)) {\n return { file: command, needsShell: isBatch(command) };\n }\n\n const exists = deps.exists ?? existsSync;\n const pathEnv = deps.pathEnv ?? process.env.PATH ?? process.env.Path ?? \"\";\n const pathExt = deps.pathExt ?? process.env.PATHEXT ?? \".COM;.EXE;.BAT;.CMD\";\n const dirs = pathEnv.split(\";\").filter((d) => d !== \"\");\n const exts = pathExt.split(\";\").filter((e) => e !== \"\");\n for (const dir of dirs) {\n for (const ext of exts) {\n const candidate = join(dir, command + ext);\n if (exists(candidate)) return { file: candidate, needsShell: isBatch(ext) };\n }\n }\n // Not found on PATH — let cmd.exe resolve it (handles shim dirs we may miss).\n return { file: command, needsShell: true };\n}\n\n/** Basenames (case-insensitive, sans common extensions) of the ephemeral package runners. */\nconst PACKAGE_RUNNERS: Record<string, Extract<LaunchMethod, \"npx\" | \"bunx\" | \"uvx\">> = {\n npx: \"npx\",\n bunx: \"bunx\",\n uvx: \"uvx\",\n};\n\n/** Basenames of language interpreters that launch a local script. */\nconst INTERPRETERS: ReadonlySet<string> = new Set([\"node\", \"bun\", \"deno\"]);\n\n/**\n * Strip a directory prefix and a trailing executable extension to get the bare\n * program name. Tolerates both POSIX and Windows separators (the wrapped command\n * may be an absolute Windows path) and lowercases for case-insensitive matching.\n */\nfunction commandBasename(realCommand: string): string {\n const noDir = realCommand.replace(/\\\\/g, \"/\").split(\"/\").pop() ?? realCommand;\n return noDir.replace(/\\.(exe|cmd|bat|com)$/i, \"\").toLowerCase();\n}\n\n/**\n * Classify how the real MCP server is launched, for the \"launch-method\" slicing\n * dimension. Package runners (npx/bunx/uvx) and interpreters (node/bun/deno) are\n * detected by the command basename. A remote (http) server is not launched\n * locally, so the caller — which alone knows the transport — passes\n * `isRemote: true` to force \"http\". Anything else resolves to \"binary\".\n */\nexport function detectLaunchMethod(\n realCommand: string,\n realArgs: string[],\n opts?: { isRemote?: boolean },\n): LaunchMethod {\n void realArgs; // reserved: args may later refine node-vs-script heuristics\n if (opts?.isRemote === true) return \"http\";\n if (typeof realCommand !== \"string\" || realCommand.trim() === \"\") {\n return \"unknown\";\n }\n const base = commandBasename(realCommand);\n if (base === \"\") return \"unknown\";\n const runner = PACKAGE_RUNNERS[base];\n if (runner !== undefined) return runner;\n if (INTERPRETERS.has(base)) return \"node\";\n return \"binary\";\n}\n"],"mappings":";;;;;;AASA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AAed,SAAS,mBAAmB,OAA4C;AAC7E,SAAO,UAAU,YAAY,YAAY;AAC3C;AA2BO,SAAS,SAAS,KAAqB;AAC5C,SAAO,IAAI,IAAI,QAAQ,OAAO,GAAG,CAAC;AACpC;AAYA,SAAS,YAAY,KAAa,aAAqB,MAAuB;AAC5E,SAAO,IAAI,SAAS,IAAI,WAAW,KAAK,IAAI,GAAG;AACjD;AAQO,SAAS,wBACd,aACA,YACA,OACA,aACQ;AACR,SAAO,GAAG,SAAS,WAAW,CAAC,SAAS,UAAU,IAAI,KAAK,gBAAgB,WAAW;AACxF;AAaO,SAAS,qBACd,SACA,aACA,aACS;AACT,MAAI,CAAC,QAAS,QAAO;AAMrB,QAAM,MAAM,QAAQ,QAAQ,OAAO,GAAG;AACtC,MAAI,CAAC,IAAI,SAAS,YAAY,QAAQ,OAAO,GAAG,CAAC,EAAG,QAAO;AAC3D,QAAM,QAAQ,eAAe,WAAW;AACxC,MAAI,OAAO;AACX,aAAS;AACP,UAAM,MAAM,IAAI,QAAQ,OAAO,IAAI;AACnC,QAAI,MAAM,EAAG,QAAO;AACpB,UAAM,QAAQ,IAAI,OAAO,MAAM,MAAM,MAAM;AAC3C,QAAI,UAAU,MAAM,UAAU,OAAO,KAAK,KAAK,KAAK,EAAG,QAAO;AAC9D,WAAO,MAAM,MAAM;AAAA,EACrB;AACF;AAUO,SAAS,uBACd,aACA,YACA,aACQ;AACR,SAAO,GAAG,SAAS,WAAW,CAAC,gBAAgB,UAAU,gBAAgB,WAAW;AACtF;AAQO,SAAS,oBACd,SACA,aACA,aACS;AACT,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,MAAM,QAAQ,QAAQ,OAAO,GAAG;AACtC,QAAM,cAAc,YAAY,QAAQ,OAAO,GAAG;AAClD,MAAI,CAAC,IAAI,SAAS,WAAW,EAAG,QAAO;AACvC,MAAI,CAAC,YAAY,KAAK,aAAa,aAAa,EAAG,QAAO;AAC1D,SAAO,qBAAqB,SAAS,aAAa,WAAW;AAC/D;AASO,SAAS,8BACd,aACA,YACA,aACQ;AACR,SAAO,GAAG,SAAS,WAAW,CAAC,eAAe,UAAU,gBAAgB,WAAW;AACrF;AAQO,SAAS,2BACd,SACA,aACA,aACS;AACT,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,MAAM,QAAQ,QAAQ,OAAO,GAAG;AACtC,QAAM,cAAc,YAAY,QAAQ,OAAO,GAAG;AAClD,MAAI,CAAC,IAAI,SAAS,WAAW,EAAG,QAAO;AACvC,MAAI,CAAC,YAAY,KAAK,aAAa,YAAY,EAAG,QAAO;AACzD,SAAO,qBAAqB,SAAS,aAAa,WAAW;AAC/D;AAWO,SAAS,0BACd,aACA,YACA,UACA,aACQ;AACR,SAAO,GAAG,SAAS,WAAW,CAAC,WAAW,UAAU,IAAI,QAAQ,gBAAgB,WAAW;AAC7F;AAQO,SAAS,uBACd,SACA,aACA,aACS;AACT,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,MAAM,QAAQ,QAAQ,OAAO,GAAG;AACtC,QAAM,cAAc,YAAY,QAAQ,OAAO,GAAG;AAClD,MAAI,CAAC,IAAI,SAAS,WAAW,EAAG,QAAO;AACvC,MAAI,CAAC,YAAY,KAAK,aAAa,QAAQ,EAAG,QAAO;AACrD,SAAO,qBAAqB,SAAS,aAAa,WAAW;AAC/D;AAUO,SAAS,yBACd,WACA,MAAyB,QAAQ,KACxB;AACT,MAAI,UAAU,YAAY,KAAM,QAAO;AACvC,MAAI,IAAI,gCAAgC,IAAK,QAAO;AACpD,SAAO,UAAU,oBAAoB;AACvC;AAQO,SAAS,uBACd,QACA,WACS;AACT,SACE,OAAO,qBAAqB,SAC5B,UAAU,YAAY,QACtB,OAAO,cAAc,WACrB,OAAO,OAAO,YAAY,YAC1B,OAAO,YAAY;AAEvB;AAgCO,SAAS,yBACd,aACA,aACA,aACA,UACA,OACA,YACA,SACqC;AACrC,QAAM,QAAQ,CAAC,SAAS,eAAe,WAAW;AAClD,MAAI,UAAU,OAAW,OAAM,KAAK,WAAW,mBAAmB,KAAK,CAAC;AACxE,MAAI,eAAe,OAAW,OAAM,KAAK,UAAU,UAAU;AAC7D,MAAI,YAAY,UAAa,YAAY,GAAI,OAAM,KAAK,cAAc,OAAO;AAC7E,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM,CAAC,GAAG,OAAO,MAAM,aAAa,GAAG,QAAQ;AAAA,EACjD;AACF;AAcO,SAAS,kBACd,KACA,QACA,YACA,SACA,MACqC;AACrC,MAAI,CAAC,uBAAuB,QAAQ,IAAI,UAAU,SAAS,GAAG;AAC5D,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AACA,QAAM,UAAU,qBAAqB,IAAI,QAAQ,IAAI,IAAI,WAAW;AACpE,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,IAAI,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA,IAAI;AAAA,IACJ;AAAA,IACA;AAAA,EACF;AACF;AAsCO,SAAS,oBACd,SACA,OAAyB,CAAC,GACT;AACjB,QAAM,WAAW,KAAK,YAAY,QAAQ;AAC1C,MAAI,aAAa,QAAS,QAAO,EAAE,MAAM,SAAS,YAAY,MAAM;AAEpE,QAAM,UAAU,CAAC,MAAuB,gBAAgB,KAAK,CAAC;AAG9D,MAAI,QAAQ,KAAK,OAAO,KAAK,gBAAgB,KAAK,OAAO,GAAG;AAC1D,WAAO,EAAE,MAAM,SAAS,YAAY,QAAQ,OAAO,EAAE;AAAA,EACvD;AAEA,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,UAAU,KAAK,WAAW,QAAQ,IAAI,QAAQ,QAAQ,IAAI,QAAQ;AACxE,QAAM,UAAU,KAAK,WAAW,QAAQ,IAAI,WAAW;AACvD,QAAM,OAAO,QAAQ,MAAM,GAAG,EAAE,OAAO,CAAC,MAAM,MAAM,EAAE;AACtD,QAAM,OAAO,QAAQ,MAAM,GAAG,EAAE,OAAO,CAAC,MAAM,MAAM,EAAE;AACtD,aAAW,OAAO,MAAM;AACtB,eAAW,OAAO,MAAM;AACtB,YAAM,YAAY,KAAK,KAAK,UAAU,GAAG;AACzC,UAAI,OAAO,SAAS,EAAG,QAAO,EAAE,MAAM,WAAW,YAAY,QAAQ,GAAG,EAAE;AAAA,IAC5E;AAAA,EACF;AAEA,SAAO,EAAE,MAAM,SAAS,YAAY,KAAK;AAC3C;AAGA,IAAM,kBAAiF;AAAA,EACrF,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AACP;AAGA,IAAM,eAAoC,oBAAI,IAAI,CAAC,QAAQ,OAAO,MAAM,CAAC;AAOzE,SAAS,gBAAgB,aAA6B;AACpD,QAAM,QAAQ,YAAY,QAAQ,OAAO,GAAG,EAAE,MAAM,GAAG,EAAE,IAAI,KAAK;AAClE,SAAO,MAAM,QAAQ,yBAAyB,EAAE,EAAE,YAAY;AAChE;AASO,SAAS,mBACd,aACA,UACA,MACc;AACd,OAAK;AACL,MAAI,MAAM,aAAa,KAAM,QAAO;AACpC,MAAI,OAAO,gBAAgB,YAAY,YAAY,KAAK,MAAM,IAAI;AAChE,WAAO;AAAA,EACT;AACA,QAAM,OAAO,gBAAgB,WAAW;AACxC,MAAI,SAAS,GAAI,QAAO;AACxB,QAAM,SAAS,gBAAgB,IAAI;AACnC,MAAI,WAAW,OAAW,QAAO;AACjC,MAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AACnC,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/package-metadata.ts","../src/core/define-connector.ts","../src/core/mcp-standard.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\n\nimport type {\n McpPackageIdentity,\n ResolvedMcpPackageIdentity,\n ServerDef,\n} from \"./types.js\";\n\nconst GENERIC_PACKAGE_BASENAMES = new Set([\n \"mcp\",\n \"server\",\n \"mcp-server\",\n \"connector\",\n \"tool\",\n \"tools\",\n \"cli\",\n]);\n\nexport interface ConnectorPackageMetadata {\n /** Package-derived MCP identity used to derive host aliases and install ids. */\n mcp?: McpPackageIdentity;\n /** package.json version used as the connector version default. */\n version?: string;\n}\n\nconst PACKAGE_METADATA_CONTEXT = new AsyncLocalStorage<ConnectorPackageMetadata>();\n\nfunction kebab(raw: string): string {\n return raw\n .trim()\n .replace(/^@/, \"\")\n .replace(/[\\\\/_.\\s]+/g, \"-\")\n .replace(/[^a-zA-Z0-9-]+/g, \"-\")\n .replace(/-{2,}/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .toLowerCase();\n}\n\nfunction scopedPackageParts(packageName: string): {\n scope?: string;\n basename: string;\n} {\n const raw = packageName.trim();\n if (raw.startsWith(\"@\")) {\n const [scope, basename] = raw.slice(1).split(\"/\");\n return { scope: scope ? kebab(scope) : undefined, basename: basename ?? \"\" };\n }\n return { basename: raw };\n}\n\nfunction cleanMcpBasename(raw: string): string {\n let name = kebab(raw);\n name = name.replace(/^mcp-server-/, \"\");\n name = name.replace(/^server-/, \"\");\n name = name.replace(/-mcp-server$/, \"\");\n name = name.replace(/-mcp$/, \"\");\n name = name.replace(/-server$/, \"\");\n return kebab(name);\n}\n\nfunction isGeneric(name: string | undefined): boolean {\n return !name || GENERIC_PACKAGE_BASENAMES.has(name);\n}\n\nfunction cleanString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction objectKeys(value: unknown): string[] {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return [];\n return Object.keys(value).filter((key) => key.trim());\n}\n\nfunction packageBinName(bin: unknown, packageName: string | undefined): string | undefined {\n if (typeof bin === \"string\") {\n return packageName ? deriveHostAliasFromPackageName(packageName) : undefined;\n }\n\n const keys = objectKeys(bin);\n if (keys.length === 0) return undefined;\n if (keys.length === 1) return keys[0];\n\n const packageAlias = packageName ? deriveHostAliasFromPackageName(packageName) : undefined;\n if (packageAlias && keys.includes(packageAlias)) return packageAlias;\n\n const unscoped = packageName?.startsWith(\"@\") ? packageName.split(\"/\")[1] : packageName;\n const cleanUnscoped = unscoped ? cleanMcpBasename(unscoped) : undefined;\n if (cleanUnscoped && keys.includes(cleanUnscoped)) return cleanUnscoped;\n\n return undefined;\n}\n\nexport function currentMcpPackageIdentity(): McpPackageIdentity | undefined {\n return currentConnectorPackageMetadata()?.mcp;\n}\n\nexport function currentConnectorPackageMetadata():\n | ConnectorPackageMetadata\n | undefined {\n return PACKAGE_METADATA_CONTEXT.getStore();\n}\n\nexport async function withConnectorPackageMetadata<T>(\n metadata: ConnectorPackageMetadata | undefined,\n run: () => Promise<T>,\n): Promise<T> {\n if (!metadata || Object.keys(metadata).length === 0) return run();\n return PACKAGE_METADATA_CONTEXT.run(metadata, run);\n}\n\nexport async function withMcpPackageIdentity<T>(\n identity: McpPackageIdentity | undefined,\n run: () => Promise<T>,\n): Promise<T> {\n if (!identity || Object.keys(identity).length === 0) return run();\n return withConnectorPackageMetadata({ mcp: identity }, run);\n}\n\nexport function findNearestPackageJson(startDir: string): string | undefined {\n let dir = resolve(startDir);\n for (;;) {\n const candidate = join(dir, \"package.json\");\n if (existsSync(candidate)) return candidate;\n\n const parent = dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\nexport function readMcpPackageIdentityFromPackageJson(\n packageJsonPath: string,\n): McpPackageIdentity {\n return readConnectorPackageMetadataFromPackageJson(packageJsonPath).mcp ?? {};\n}\n\nexport function readConnectorPackageMetadataFromPackageJson(\n packageJsonPath: string,\n): ConnectorPackageMetadata {\n const parsed = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as {\n name?: unknown;\n version?: unknown;\n mcpName?: unknown;\n mcp?: unknown;\n bin?: unknown;\n };\n const packageName = cleanString(parsed.name);\n const version = cleanString(parsed.version);\n const mcp =\n parsed.mcp && typeof parsed.mcp === \"object\" && !Array.isArray(parsed.mcp)\n ? (parsed.mcp as { name?: unknown; mcpName?: unknown })\n : undefined;\n const mcpName =\n cleanString(parsed.mcpName) ??\n cleanString(mcp?.mcpName) ??\n cleanString(mcp?.name);\n const bin = packageBinName(parsed.bin, packageName);\n\n const identity: McpPackageIdentity = {\n ...(packageName ? { packageName } : {}),\n ...(mcpName ? { mcpName } : {}),\n ...(bin ? { bin } : {}),\n };\n\n return {\n ...(Object.keys(identity).length > 0 ? { mcp: identity } : {}),\n ...(version ? { version } : {}),\n };\n}\n\nexport function readMcpPackageIdentityNearFile(\n filePath: string,\n): McpPackageIdentity | undefined {\n return readConnectorPackageMetadataNearFile(filePath)?.mcp;\n}\n\nexport function readConnectorPackageMetadataNearFile(\n filePath: string,\n): ConnectorPackageMetadata | undefined {\n const packageJsonPath = findNearestPackageJson(dirname(filePath));\n if (!packageJsonPath) return undefined;\n try {\n const metadata = readConnectorPackageMetadataFromPackageJson(packageJsonPath);\n return Object.keys(metadata).length > 0 ? metadata : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Derive the host-facing MCP server key from a package name. */\nexport function deriveHostAliasFromPackageName(\n packageName: string,\n): string | undefined {\n const { scope, basename } = scopedPackageParts(packageName);\n const fromBasename = cleanMcpBasename(basename);\n if (!isGeneric(fromBasename)) return fromBasename;\n const fromScope = scope ? cleanMcpBasename(scope) : undefined;\n return isGeneric(fromScope) ? undefined : fromScope;\n}\n\n/** Derive the host-facing MCP server key from an MCP registry name. */\nexport function deriveHostAliasFromMcpName(\n mcpName: string,\n): string | undefined {\n const [namespace, name] = mcpName.trim().split(\"/\");\n const fromName = cleanMcpBasename(name ?? namespace ?? \"\");\n if (!isGeneric(fromName)) return fromName;\n\n const namespaceParts = (namespace ?? \"\").split(\".\").filter(Boolean);\n const fromNamespace = cleanMcpBasename(namespaceParts.at(-1) ?? \"\");\n return isGeneric(fromNamespace) ? undefined : fromNamespace;\n}\n\n/** Infer an npm package from common npx-style stdio server definitions. */\nexport function inferNpmPackageFromServer(\n server: ServerDef | undefined,\n): string | undefined {\n if (!server || server.transport !== \"stdio\") return undefined;\n if (typeof server.command !== \"string\" || server.command.trim() === \"\") {\n return undefined;\n }\n const runner = kebab(server.command.split(/[\\\\/]/).pop() ?? server.command);\n if (![\"npx\", \"npm\", \"pnpm\", \"yarn\", \"bun\"].includes(runner)) return undefined;\n\n const args = server.args ?? [];\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i]!;\n if (arg === \"--\") continue;\n if (arg === \"exec\" || arg === \"dlx\" || arg === \"x\") continue;\n if (arg === \"-y\" || arg === \"--yes\" || arg === \"--no-install\") continue;\n if (arg === \"--package\" || arg === \"-p\") {\n i += 1;\n continue;\n }\n if (arg.startsWith(\"-\")) continue;\n return arg;\n }\n return undefined;\n}\n\nexport function resolveMcpPackageIdentity(input: {\n id?: string;\n mcp?: McpPackageIdentity;\n server?: ServerDef;\n}): ResolvedMcpPackageIdentity {\n const packageName =\n input.mcp?.packageName ?? inferNpmPackageFromServer(input.server);\n const hostAlias =\n input.mcp?.hostAlias ??\n (input.mcp?.mcpName\n ? deriveHostAliasFromMcpName(input.mcp.mcpName)\n : undefined) ??\n (packageName ? deriveHostAliasFromPackageName(packageName) : undefined) ??\n (input.mcp?.bin ? cleanMcpBasename(input.mcp.bin) : undefined);\n\n return {\n ...(packageName ? { packageName } : {}),\n ...(input.mcp?.mcpName ? { mcpName: input.mcp.mcpName } : {}),\n ...(input.mcp?.bin ? { bin: input.mcp.bin } : {}),\n ...(hostAlias ? { hostAlias } : {}),\n };\n}\n","/**\n * core/define-connector — the public write-once entry point.\n *\n * Validates a ConnectorConfig and normalizes it into a ResolvedConnector with\n * every optional field resolved to a default. Validation is intentionally\n * dependency-free (no zod) to keep the single-binary install lean.\n */\n\nimport { isAbsolute, normalize } from \"node:path\";\n\nimport type {\n ActionDef,\n CommandDef,\n ConfigPatchDef,\n ConnectorConfig,\n HookDefinition,\n HookEventName,\n HooksConfig,\n MemoryDef,\n NativeHookDef,\n PlatformId,\n PublishConfig,\n ResolvedConnector,\n SkillDef,\n StatuslineDef,\n SubagentDef,\n} from \"./types.js\";\nimport { REGISTERED_PLATFORM_IDS } from \"../adapters/registry.js\";\nimport { REGISTRY_NAMESPACE_RE } from \"./mcp-standard.js\";\nimport { CONNECTOR_ID_RE, isValidConnectorId } from \"./ids.js\";\nimport {\n currentConnectorPackageMetadata,\n resolveMcpPackageIdentity,\n} from \"./package-metadata.js\";\nimport {\n MANAGED_BLOCK_BEGIN_TOKEN,\n MANAGED_BLOCK_END_TOKEN,\n MEMORY_CONTENT_HARD_CAP_BYTES,\n} from \"./managed-block.js\";\nimport {\n CONFIG_PATCH_SEGMENT_RE,\n configPatchNamespaceViolation,\n isJsonValue,\n isValidConfigPatchKey,\n} from \"./config-patch-ledger.js\";\n\n/** kebab-case name regex shared by command/skill/subagent names. */\nconst SURFACE_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;\n\n/** Max length of a skill description (Agent Skills open standard). */\nconst SKILL_DESCRIPTION_MAX = 1024;\n\n// NOTE: canonical ordering — append new events at the END (hookEvents ordering\n// is pinned by tests and feeds install-file ordering). Exported so the\n// hook-dispatch gate + Claude-bundle event sets can be drift-tested against it.\nexport const ALL_EVENTS: HookEventName[] = [\n \"SessionStart\",\n \"SessionEnd\",\n \"UserPromptSubmit\",\n \"PreToolUse\",\n \"PostToolUse\",\n \"PreCompact\",\n \"Stop\",\n \"Notification\",\n \"PermissionRequest\",\n \"PostToolUseFailure\",\n \"SubagentStart\",\n \"SubagentStop\",\n \"PostCompact\",\n];\n\n/** Thrown on an invalid connector configuration. */\nexport class ConnectorConfigError extends Error {\n constructor(message: string) {\n super(`Invalid connector config: ${message}`);\n this.name = \"ConnectorConfigError\";\n }\n}\n\nfunction declaredEvents(hooks: HooksConfig | undefined): HookEventName[] {\n if (!hooks) return [];\n return ALL_EVENTS.filter((e) => typeof hooks[e]?.handler === \"function\");\n}\n\n/**\n * Validate + normalize a connector definition.\n * Returns a ResolvedConnector that adapters and the CLI consume directly.\n */\nexport function defineConnector(config: ConnectorConfig): ResolvedConnector {\n if (!config || typeof config !== \"object\") {\n throw new ConnectorConfigError(\"config must be an object\");\n }\n\n const packageMetadata = currentConnectorPackageMetadata();\n const packageIdentity = packageMetadata?.mcp;\n if (packageIdentity && Object.keys(packageIdentity).length > 0) {\n config = {\n ...config,\n mcp: {\n ...packageIdentity,\n ...config.mcp,\n },\n };\n }\n\n const mcpIdentity = resolveMcpPackageIdentity(config);\n const connectorId = config.id ?? mcpIdentity.hostAlias;\n if (!isValidConnectorId(connectorId)) {\n throw new ConnectorConfigError(\n `id must be kebab-case matching ${CONNECTOR_ID_RE} or derivable from mcp/package metadata (got ${JSON.stringify(config.id)})`,\n );\n }\n const hasCommands = Array.isArray(config.commands) && config.commands.length > 0;\n const hasSkills = Array.isArray(config.skills) && config.skills.length > 0;\n const hasSubagents = Array.isArray(config.subagents) && config.subagents.length > 0;\n const hasMemory = Array.isArray(config.memory) && config.memory.length > 0;\n // A statusline (a HUD) is a legitimate sole payload — a connector whose whole\n // job is rendering a status line. SINGULAR (one per connector), so a plain\n // presence check (object with a render handler — validated in normalizeStatusline).\n const hasStatusline = config.statusline != null;\n // Actions (each a user-invokable run handler) are a legitimate sole payload —\n // a connector whose whole job is shipping dispatchable actions.\n const hasActions = Array.isArray(config.actions) && config.actions.length > 0;\n // A platform-scoped nativeHooks declaration is a legitimate sole payload\n // (a hooks-only connector wired entirely through native passthrough events).\n const hasNativeHooks = Object.values(config.platforms ?? {}).some(\n (override) =>\n override?.nativeHooks != null && Object.keys(override.nativeHooks).length > 0,\n );\n // Likewise a platform-scoped configPatch declaration (a connector whose whole\n // job is asserting a host config key, e.g. an experimental feature flag).\n const hasConfigPatch = Object.values(config.platforms ?? {}).some(\n (override) => Array.isArray(override?.configPatch) && override.configPatch.length > 0,\n );\n if (\n !config.server &&\n !config.hooks &&\n !hasCommands &&\n !hasSkills &&\n !hasSubagents &&\n !hasMemory &&\n !hasStatusline &&\n !hasActions &&\n !hasNativeHooks &&\n !hasConfigPatch\n ) {\n throw new ConnectorConfigError(\n \"a connector must declare at least one of `server`, `hooks`, `commands`, `skills`, `subagents`, `memory`, `statusline`, `actions`, \" +\n \"or a per-platform `nativeHooks` / `configPatch` declaration\",\n );\n }\n\n if (config.server) {\n const s = config.server;\n if (s.transport === \"stdio\") {\n if (!s.command || typeof s.command !== \"string\") {\n throw new ConnectorConfigError(\n \"server.command is required for stdio transport\",\n );\n }\n } else {\n if (!s.url || typeof s.url !== \"string\") {\n throw new ConnectorConfigError(\n `server.url is required for ${s.transport} transport`,\n );\n }\n }\n }\n\n // Validate hook handlers are functions, plus any per-host override map.\n if (config.hooks) {\n for (const ev of ALL_EVENTS) {\n const def = config.hooks[ev];\n if (def != null && typeof def.handler !== \"function\") {\n throw new ConnectorConfigError(`hooks.${ev}.handler must be a function`);\n }\n // Per-host override map: every key must be a registered platform id and\n // every entry's handler a function (author-time hard error, not skip-warn).\n validateHostsMap(def?.hosts, `hooks.${ev}.hosts`, \"handler\");\n }\n }\n\n // Validate per-platform native passthrough hooks (handlers live in this same\n // config module, exactly like normalized hooks — the resolved connector keeps\n // `platforms` verbatim, so live handlers survive resolution and are recovered\n // at runtime by re-importing the module via the registry's modulePath).\n validateNativeHooks(config.platforms);\n\n // Validate per-platform declarative config patches (pure JSON — persisted\n // whole; semantics are FIXED set-if-absent/skip-warn, so only the shape is\n // validated here; the host adapter enforces its sensitive-key denylist).\n validateConfigPatches(config.platforms);\n\n const commands = normalizeCommands(config.commands);\n const skills = normalizeSkills(config.skills);\n const subagents = normalizeSubagents(config.subagents);\n const memory = normalizeMemory(config.memory);\n const statusline = normalizeStatusline(config.statusline);\n const actions = normalizeActions(config.actions);\n\n const t = config.telemetry ?? {};\n\n const resolved: ResolvedConnector = {\n id: connectorId,\n ...(Object.keys(mcpIdentity).length > 0 ? { mcp: mcpIdentity } : {}),\n displayName: config.displayName ?? connectorId,\n version: config.version ?? packageMetadata?.version ?? \"0.0.0\",\n ...(config.server ? { server: normalizeServer(config.server) } : {}),\n hooks: config.hooks ?? {},\n hookEvents: declaredEvents(config.hooks),\n telemetry: {\n enabled: t.enabled ?? true,\n modelFamilyHint: t.modelFamilyHint ?? \"auto\",\n measureToolDefs: t.measureToolDefs ?? true,\n // OPT-IN: host-native turn-usage capture is OFF unless explicitly enabled\n // in config (or forced on at install via AGENT_CONNECTOR_HOST_NATIVE=1).\n hostNativeUsage: t.hostNativeUsage ?? false,\n store: t.store ?? \"ndjson\",\n calibration: {\n anthropicCountTokens: t.calibration?.anthropicCountTokens ?? false,\n },\n },\n commands,\n skills,\n subagents,\n memory,\n ...(statusline ? { statusline } : {}),\n actions,\n platforms: config.platforms ?? {},\n targets: config.targets ?? \"auto\",\n ...(config.publish ? { publish: normalizePublish(config.publish) } : {}),\n };\n\n return resolved;\n}\n\n/** The 13 normalized event names, for the nativeHooks collision check. */\nconst NORMALIZED_EVENT_SET: ReadonlySet<string> = new Set(ALL_EVENTS);\n\n/**\n * Validate a per-host override map (`hosts?:`) declared on a hook definition or\n * on the statusline. Shared by the hook-validation loop and normalizeStatusline:\n * - the value must be a plain object keyed by platform id;\n * - every key MUST be a REGISTERED platform id (REGISTERED_PLATFORM_IDS) —\n * an unknown id is an author-time ConnectorConfigError (NOT a skip-warn:\n * a typo'd host would otherwise silently never fire);\n * - every entry's implementation field (`handler` for hooks, `render` for\n * statusline, `run` for actions) MUST be a function (it is re-imported from\n * the connector module at runtime, like the top-level handler/render/run).\n * `undefined` (no map) is valid and skipped.\n */\nfunction validateHostsMap(\n map: unknown,\n surfaceLabel: string,\n implField: \"handler\" | \"render\" | \"run\",\n): void {\n if (map == null) return;\n if (typeof map !== \"object\" || Array.isArray(map)) {\n throw new ConnectorConfigError(\n `${surfaceLabel} must be an object keyed by platform id`,\n );\n }\n for (const [platformId, entry] of Object.entries(map as Record<string, unknown>)) {\n if (!REGISTERED_PLATFORM_IDS.has(platformId as PlatformId)) {\n const valid = [...REGISTERED_PLATFORM_IDS].sort().join(\", \");\n throw new ConnectorConfigError(\n `unknown platform id \"${platformId}\" in hosts map for ${surfaceLabel}; valid ids: ${valid}`,\n );\n }\n if (\n entry == null ||\n typeof entry !== \"object\" ||\n typeof (entry as Record<string, unknown>)[implField] !== \"function\"\n ) {\n throw new ConnectorConfigError(\n `${surfaceLabel}.${platformId}.${implField} must be a function`,\n );\n }\n }\n}\n\n/**\n * Validate every `platforms[<id>].nativeHooks` declaration:\n * - the value must be an object keyed by HOST-NATIVE event name;\n * - an event name must NOT collide with the normalized {@link HookEventName}\n * union (those belong in the cross-platform `hooks` API, which gets\n * normalization, matcher evaluation, and HookResponse mapping);\n * - each handler must be a function (it is re-imported from the config module\n * at runtime, like normalized hook handlers);\n * - `matcher`, when present, must be a string (written verbatim into the\n * host's hook config — the host evaluates it).\n */\nfunction validateNativeHooks(platforms: ConnectorConfig[\"platforms\"]): void {\n if (platforms == null) return;\n for (const [platformId, override] of Object.entries(platforms)) {\n const native = override?.nativeHooks;\n if (native == null) continue;\n const where = `platforms.${platformId}.nativeHooks`;\n if (typeof native !== \"object\" || Array.isArray(native)) {\n throw new ConnectorConfigError(\n `${where} must be an object keyed by host-native event name`,\n );\n }\n for (const [event, def] of Object.entries(native)) {\n if (NORMALIZED_EVENT_SET.has(event)) {\n throw new ConnectorConfigError(\n `${where}.${event} collides with the normalized hook event \"${event}\"; ` +\n `declare it under \\`hooks.${event}\\` (the normalized, cross-platform hooks API) instead`,\n );\n }\n if (def == null || typeof def !== \"object\" || typeof def.handler !== \"function\") {\n throw new ConnectorConfigError(`${where}.${event}.handler must be a function`);\n }\n if (def.matcher !== undefined && typeof def.matcher !== \"string\") {\n throw new ConnectorConfigError(`${where}.${event}.matcher must be a string`);\n }\n }\n }\n}\n\n/**\n * Validate every `platforms[<id>].configPatch` declaration:\n * - the value must be an array of patch objects;\n * - `key` must be a dotted LEAF path whose segments match\n * {@link CONFIG_PATCH_SEGMENT_RE} — no dots-in-key, no array indices;\n * - `key` must not collide with the agent-connector-modeled namespace\n * (`hooks*` → hooks/nativeHooks; `mcpServers*` & friends → server/extra);\n * - duplicate keys within one platform's list are rejected (a duplicate\n * would double-apply / fight itself on refcounts);\n * - `value` must be JSON-serializable data (it is persisted whole in the\n * connector record and in the ownership ledger);\n * - `reason` is REQUIRED (printed in the install diff and every skip-warn);\n * - `docsUrl`, when present, must be a string.\n * The HOST-side sensitive-key denylist is deliberately NOT validated here —\n * it lives in (and is documented by) each supporting adapter.\n */\nfunction validateConfigPatches(platforms: ConnectorConfig[\"platforms\"]): void {\n if (platforms == null) return;\n for (const [platformId, override] of Object.entries(platforms)) {\n const patches = override?.configPatch;\n if (patches == null) continue;\n const where = `platforms.${platformId}.configPatch`;\n if (!Array.isArray(patches)) {\n throw new ConnectorConfigError(`${where} must be an array of patch objects`);\n }\n const seen = new Set<string>();\n patches.forEach((patch, i) => {\n if (patch == null || typeof patch !== \"object\" || Array.isArray(patch)) {\n throw new ConnectorConfigError(`${where}[${i}] must be an object`);\n }\n if (!isValidConfigPatchKey(patch.key)) {\n throw new ConnectorConfigError(\n `${where}[${i}].key must be a dotted LEAF path whose segments match ` +\n `${CONFIG_PATCH_SEGMENT_RE} (no dots-in-key, no array indices); ` +\n `got ${JSON.stringify(patch.key)}`,\n );\n }\n const violation = configPatchNamespaceViolation(patch.key);\n if (violation) {\n throw new ConnectorConfigError(`${where}[${i}].key ${violation}`);\n }\n if (seen.has(patch.key)) {\n throw new ConnectorConfigError(\n `${where}[${i}] duplicate key \"${patch.key}\"`,\n );\n }\n seen.add(patch.key);\n if (!isJsonValue(patch.value)) {\n throw new ConnectorConfigError(\n `${where}[${i}].value must be JSON-serializable data ` +\n `(string/finite number/boolean/null/array/plain object)`,\n );\n }\n if (typeof patch.reason !== \"string\" || patch.reason.trim() === \"\") {\n throw new ConnectorConfigError(\n `${where}[${i}].reason is required (a human-readable why, printed in ` +\n `the install diff and every skip-warn)`,\n );\n }\n if (patch.docsUrl !== undefined && typeof patch.docsUrl !== \"string\") {\n throw new ConnectorConfigError(`${where}[${i}].docsUrl must be a string`);\n }\n });\n }\n}\n\n/**\n * Validate the optional `publish` block (registry server.json + MCPB bundle\n * distribution metadata). Light, dependency-free: we only reject shapes that\n * would emit a clearly INVALID standard artifact. The per-format required-field\n * checks (e.g. server.json needs registryNamespace, mcpb needs author.name)\n * live in the emitters so a connector that never publishes pays nothing.\n */\nfunction normalizePublish(publish: PublishConfig): PublishConfig {\n if (typeof publish !== \"object\" || Array.isArray(publish)) {\n throw new ConnectorConfigError(\"publish must be an object\");\n }\n if (publish.registryNamespace !== undefined) {\n const ns = publish.registryNamespace;\n if (typeof ns !== \"string\" || !REGISTRY_NAMESPACE_RE.test(ns)) {\n throw new ConnectorConfigError(\n `publish.registryNamespace must be a reverse-DNS namespace matching ${REGISTRY_NAMESPACE_RE} ` +\n `(e.g. \"io.github.acme\" or \"com.acme\"); got ${JSON.stringify(ns)}`,\n );\n }\n }\n if (publish.packageName !== undefined && typeof publish.packageName !== \"string\") {\n throw new ConnectorConfigError(\"publish.packageName must be a string\");\n }\n if (publish.author !== undefined) {\n const a = publish.author;\n if (typeof a !== \"object\" || a == null || typeof a.name !== \"string\" || a.name === \"\") {\n throw new ConnectorConfigError(\"publish.author.name must be a non-empty string\");\n }\n }\n return { ...publish };\n}\n\n/** Validate a surface name (kebab-case) or throw a ConnectorConfigError. */\nfunction assertSurfaceName(\n surface: string,\n name: unknown,\n index: number,\n field = \"name\",\n): string {\n if (typeof name !== \"string\" || !SURFACE_NAME_RE.test(name)) {\n throw new ConnectorConfigError(\n `${surface}[${index}].${field} must be kebab-case matching ${SURFACE_NAME_RE} (got ${JSON.stringify(name)})`,\n );\n }\n return name;\n}\n\n/** Require a non-empty string field on a surface entry. */\nfunction assertNonEmptyString(\n surface: string,\n index: number,\n field: string,\n value: unknown,\n): string {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new ConnectorConfigError(`${surface}[${index}].${field} must be a non-empty string`);\n }\n return value;\n}\n\n/** Detect a duplicate surface name within one array, throwing on collision. */\nfunction assertNoDuplicate(\n surface: string,\n seen: Set<string>,\n name: string,\n index: number,\n field = \"name\",\n): void {\n if (seen.has(name)) {\n throw new ConnectorConfigError(`${surface}[${index}] duplicate ${field} \"${name}\"`);\n }\n seen.add(name);\n}\n\nfunction normalizeCommands(input: CommandDef[] | undefined): CommandDef[] {\n if (input == null) return [];\n if (!Array.isArray(input)) {\n throw new ConnectorConfigError(\"commands must be an array\");\n }\n const seen = new Set<string>();\n return input.map((cmd, i) => {\n if (!cmd || typeof cmd !== \"object\") {\n throw new ConnectorConfigError(`commands[${i}] must be an object`);\n }\n const name = assertSurfaceName(\"commands\", cmd.name, i);\n assertNoDuplicate(\"commands\", seen, name, i);\n assertNonEmptyString(\"commands\", i, \"prompt\", cmd.prompt);\n // Pass through verbatim (content); arrays are content, not handlers.\n return { ...cmd, name };\n });\n}\n\nfunction normalizeSkills(input: SkillDef[] | undefined): SkillDef[] {\n if (input == null) return [];\n if (!Array.isArray(input)) {\n throw new ConnectorConfigError(\"skills must be an array\");\n }\n const seen = new Set<string>();\n return input.map((skill, i) => {\n if (!skill || typeof skill !== \"object\") {\n throw new ConnectorConfigError(`skills[${i}] must be an object`);\n }\n const name = assertSurfaceName(\"skills\", skill.name, i);\n assertNoDuplicate(\"skills\", seen, name, i);\n const description = assertNonEmptyString(\"skills\", i, \"description\", skill.description);\n if (description.length > SKILL_DESCRIPTION_MAX) {\n throw new ConnectorConfigError(\n `skills[${i}].description exceeds ${SKILL_DESCRIPTION_MAX} chars (got ${description.length})`,\n );\n }\n assertNonEmptyString(\"skills\", i, \"body\", skill.body);\n assertSafeResourceKeys(\"skills\", i, skill.resources);\n return { ...skill, name };\n });\n}\n\n/**\n * Validate every `skill.resources` key as a SAFE relative path that stays inside\n * the skill dir. Each resource is later written/removed via join(skillDir, rel)\n * by every adapter, so an unvalidated key like \"../../settings.json\" would\n * escape the skill dir → arbitrary file write/delete. Reject (throw) when a key\n * is empty/\".\", absolute, or normalizes to a path that begins with \"..\" or\n * contains a \"/..\" (or platform-sep) traversal segment.\n */\nfunction assertSafeResourceKeys(\n surface: string,\n index: number,\n resources: Record<string, string> | undefined,\n): void {\n if (resources == null) return;\n if (typeof resources !== \"object\" || Array.isArray(resources)) {\n throw new ConnectorConfigError(`${surface}[${index}].resources must be an object`);\n }\n for (const rel of Object.keys(resources)) {\n if (rel === \"\" || rel === \".\" || rel.trim() === \"\") {\n throw new ConnectorConfigError(\n `${surface}[${index}].resources key must be a non-empty relative path (got ${JSON.stringify(rel)})`,\n );\n }\n if (isAbsolute(rel)) {\n throw new ConnectorConfigError(\n `${surface}[${index}].resources key must be a relative path inside the skill dir, not absolute (got ${JSON.stringify(rel)})`,\n );\n }\n // Normalize with BOTH posix and native separators flattened so a Windows\n // \"..\\\\x\" or a posix \"../x\" is caught regardless of the host OS.\n const norm = normalize(rel.replace(/\\\\/g, \"/\"));\n const segs = norm.split(/[/\\\\]/);\n if (norm === \"..\" || norm.startsWith(\"../\") || norm.startsWith(\"..\\\\\") || segs.includes(\"..\")) {\n throw new ConnectorConfigError(\n `${surface}[${index}].resources key must not escape the skill dir via \"..\" (got ${JSON.stringify(rel)})`,\n );\n }\n }\n}\n\n/** Default name for a single-entry memory declaration. */\nconst MEMORY_DEFAULT_NAME = \"memory\";\n\n/**\n * Validate + normalize `memory` entries (the managed-block content surface).\n * - `name` defaults to \"memory\", then must be kebab-case and unique — it is\n * half of the block marker id (`<connectorId>/<name>`) and must stay stable;\n * - `content` must be a non-empty string, ≤ 16 KiB (hard cap — memory files\n * are injected into every prompt of every session on the host), and must\n * NOT contain the literal marker tokens `agent-connector:begin` /\n * `agent-connector:end` (they would corrupt marker scanning in the shared\n * file — rephrase the guidance instead). The 4 KiB SOFT budget is reported\n * at install time as a `warn` ChangeRecord, not a config error\n * (defineConnector has no warning channel).\n */\nfunction normalizeMemory(input: MemoryDef[] | undefined): MemoryDef[] {\n if (input == null) return [];\n if (!Array.isArray(input)) {\n throw new ConnectorConfigError(\"memory must be an array\");\n }\n const seen = new Set<string>();\n return input.map((entry, i) => {\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) {\n throw new ConnectorConfigError(`memory[${i}] must be an object`);\n }\n const name =\n entry.name === undefined\n ? MEMORY_DEFAULT_NAME\n : assertSurfaceName(\"memory\", entry.name, i);\n assertNoDuplicate(\"memory\", seen, name, i);\n const content = assertNonEmptyString(\"memory\", i, \"content\", entry.content);\n const bytes = Buffer.byteLength(content, \"utf8\");\n if (bytes > MEMORY_CONTENT_HARD_CAP_BYTES) {\n throw new ConnectorConfigError(\n `memory[${i}].content exceeds the ${MEMORY_CONTENT_HARD_CAP_BYTES}-byte hard cap ` +\n `(got ${bytes} bytes); memory is inlined into every prompt of every targeted host — keep it terse`,\n );\n }\n for (const token of [MANAGED_BLOCK_BEGIN_TOKEN, MANAGED_BLOCK_END_TOKEN]) {\n if (content.includes(token)) {\n throw new ConnectorConfigError(\n `memory[${i}].content must not contain the literal marker token \"${token}\" ` +\n `(it would corrupt managed-block scanning in the shared memory file); rephrase the guidance`,\n );\n }\n }\n if (entry.description !== undefined && typeof entry.description !== \"string\") {\n throw new ConnectorConfigError(`memory[${i}].description must be a string`);\n }\n return { ...entry, name };\n });\n}\n\n/** Default name for the (singular) statusline declaration. */\nconst STATUSLINE_DEFAULT_NAME = \"statusline\";\n\n/**\n * Validate + normalize the SINGULAR `statusline` (a HUD handler surface):\n * - `render` MUST be a function (it is the renderer, re-imported at runtime\n * like a hook handler);\n * - `name` defaults to \"statusline\", then must be kebab-case (the shared\n * surface-name validator);\n * - `description`, when present, must be a string.\n * Returns undefined when no statusline is declared (it is optional and singular,\n * unlike the memory[] array).\n */\nfunction normalizeStatusline(\n input: StatuslineDef | undefined,\n): StatuslineDef | undefined {\n if (input == null) return undefined;\n if (typeof input !== \"object\" || Array.isArray(input)) {\n throw new ConnectorConfigError(\"statusline must be an object\");\n }\n if (typeof input.render !== \"function\") {\n throw new ConnectorConfigError(\"statusline.render must be a function\");\n }\n const name =\n input.name === undefined\n ? STATUSLINE_DEFAULT_NAME\n : assertSurfaceName(\"statusline\", input.name, 0);\n if (input.description !== undefined && typeof input.description !== \"string\") {\n throw new ConnectorConfigError(\"statusline.description must be a string\");\n }\n // Per-host render override map: registered platform ids only, each render a\n // function (author-time hard error, mirroring the hook hosts-map validation).\n validateHostsMap(input.hosts, \"statusline.hosts\", \"render\");\n return { ...input, name };\n}\n\n/**\n * Validate + normalize `actions` (the user-invokable action handler surface):\n * - the value must be an array;\n * - each entry's `id` must be kebab-case (the shared surface-name validator)\n * and UNIQUE within the connector (ConnectorConfigError on dup — the id is\n * the verb's positional arg, so a duplicate would be ambiguous);\n * - `run` MUST be a function (it is the handler, re-imported at runtime like a\n * hook handler / statusline render);\n * - `description`, when present, must be a string;\n * - the per-host `hosts:` override map is validated via the shared\n * validateHostsMap (registered platform ids only, each `run` a function).\n * Returns [] when no actions are declared (defaults applied, like the other\n * array surfaces).\n */\nfunction normalizeActions(input: ActionDef[] | undefined): ActionDef[] {\n if (input == null) return [];\n if (!Array.isArray(input)) {\n throw new ConnectorConfigError(\"actions must be an array\");\n }\n const seen = new Set<string>();\n return input.map((action, i) => {\n if (!action || typeof action !== \"object\" || Array.isArray(action)) {\n throw new ConnectorConfigError(`actions[${i}] must be an object`);\n }\n const id = assertSurfaceName(\"actions\", action.id, i, \"id\");\n assertNoDuplicate(\"actions\", seen, id, i, \"id\");\n if (typeof action.run !== \"function\") {\n throw new ConnectorConfigError(`actions[${i}].run must be a function`);\n }\n if (action.description !== undefined && typeof action.description !== \"string\") {\n throw new ConnectorConfigError(`actions[${i}].description must be a string`);\n }\n // Per-host run override map: registered platform ids only, each run a\n // function (author-time hard error, mirroring the statusline/hook hosts-map).\n validateHostsMap(action.hosts, `actions.${id}.hosts`, \"run\");\n return { ...action, id };\n });\n}\n\n/**\n * Typed identity helper for authoring a status line in its own module:\n * export const myStatusline = defineStatusline({ render: (ctx) => \"…\" });\n * Mirrors the (informal) defineX helpers; gives the developer full type\n * inference on {@link StatuslineContext} without importing the type by hand.\n */\nexport const defineStatusline = (def: StatuslineDef): StatuslineDef => def;\n\n// ─────────────────────────────────────────────────────────────────────────\n// Typed identity helpers (the `define*` authoring family)\n//\n// Each is a one-line identity function: it returns its argument UNCHANGED and\n// does NOT validate or mutate (validation still happens centrally in\n// defineConnector). Their only job is ergonomics — authoring a surface in its\n// own module with full type inference + autocomplete on the *Def shape, and a\n// single import site — mirroring the existing {@link defineStatusline}.\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Typed identity helper for authoring ONE normalized lifecycle hook in its own\n * module, EVENT-PARAMETERIZED so the handler's payload narrows to that event:\n *\n * export const onPre = defineHook(\"PreToolUse\", {\n * handler(evt) { evt.toolName // ← typed as a PreToolUseEvent, not the union\n * return { decision: \"deny\", reason: \"no\" };\n * },\n * });\n *\n * The leading `event` argument exists ONLY to infer `E`; the def is returned\n * unchanged (identity — validation still happens in defineConnector). Passing\n * `HookDefinition` without an event would widen the handler param to the full\n * {@link HookEventName} union and lose per-event narrowing, so the event tag is\n * required.\n */\nexport const defineHook = <E extends HookEventName>(\n event: E,\n def: HookDefinition<E>,\n): HookDefinition<E> => {\n void event; // tag-only: used solely to infer E (the def carries no event field)\n return def;\n};\n\n/**\n * Typed identity helper for authoring a slash {@link CommandDef} in its own\n * module. Returns the def unchanged (kebab-case name + prompt are validated by\n * defineConnector when the connector is assembled).\n */\nexport const defineCommand = (def: CommandDef): CommandDef => def;\n\n/**\n * Typed identity helper for authoring an Agent {@link SkillDef} in its own\n * module. Returns the def unchanged (name/description/body + safe resource keys\n * are validated by defineConnector).\n */\nexport const defineSkill = (def: SkillDef): SkillDef => def;\n\n/**\n * Typed identity helper for authoring a {@link SubagentDef} in its own module.\n * Returns the def unchanged (name/description/prompt validated by defineConnector).\n */\nexport const defineSubagent = (def: SubagentDef): SubagentDef => def;\n\n/**\n * Typed identity helper for authoring a {@link MemoryDef} (standing-guidance\n * managed block) in its own module. Returns the def unchanged (name/content +\n * byte budget + marker-token guard validated by defineConnector).\n */\nexport const defineMemory = (def: MemoryDef): MemoryDef => def;\n\n/**\n * Typed identity helper for authoring a user-invokable {@link ActionDef} in its\n * own module. Returns the def unchanged (kebab-case unique id + run-is-function\n * + the per-host hosts map validated by defineConnector when the connector is\n * assembled).\n */\nexport const defineAction = (def: ActionDef): ActionDef => def;\n\n/**\n * Typed identity helper for authoring a declarative {@link ConfigPatchDef}\n * (set-if-absent host-config key patch) in its own module. Returns the def\n * unchanged (leaf-path grammar + namespace guard + required reason validated by\n * defineConnector when it appears under `platforms[<id>].configPatch`).\n */\nexport const defineConfigPatch = (def: ConfigPatchDef): ConfigPatchDef => def;\n\n/**\n * Typed identity helper for authoring a {@link NativeHookDef} (host-native\n * passthrough hook) in its own module. Returns the def unchanged (event-name\n * collision + handler-is-function validated by defineConnector when it appears\n * under `platforms[<id>].nativeHooks`).\n */\nexport const defineNativeHook = (def: NativeHookDef): NativeHookDef => def;\n\nfunction normalizeSubagents(input: SubagentDef[] | undefined): SubagentDef[] {\n if (input == null) return [];\n if (!Array.isArray(input)) {\n throw new ConnectorConfigError(\"subagents must be an array\");\n }\n const seen = new Set<string>();\n return input.map((agent, i) => {\n if (!agent || typeof agent !== \"object\") {\n throw new ConnectorConfigError(`subagents[${i}] must be an object`);\n }\n const name = assertSurfaceName(\"subagents\", agent.name, i);\n assertNoDuplicate(\"subagents\", seen, name, i);\n assertNonEmptyString(\"subagents\", i, \"description\", agent.description);\n assertNonEmptyString(\"subagents\", i, \"prompt\", agent.prompt);\n return { ...agent, name };\n });\n}\n\nfunction normalizeServer(server: ConnectorConfig[\"server\"]): ResolvedConnector[\"server\"] {\n if (!server) return undefined;\n const wrapDefault = server.transport === \"stdio\";\n return {\n ...server,\n enabled: server.enabled ?? true,\n tools: server.tools ?? { include: [\"*\"] },\n wrapForTelemetry: server.wrapForTelemetry ?? wrapDefault,\n };\n}\n","/**\n * core/mcp-standard — pinned literals + guards for the official MCP standards\n * agent-connector EMITS (registry server.json, MCPB bundle) or SPEAKS (the\n * connection-lifecycle wire used by `doctor --probe`).\n *\n * Each pinned constant is the CURRENT released value verified 2026-06-05 against\n * the upstream spec/schema. They are deliberately centralized + annotated\n * MUST-VERIFY-AT-IMPLEMENTATION: a newer spec revision can supersede any of them\n * without breaking us, so the bump is a one-line change here, never scattered.\n *\n * Sources:\n * • server.schema.json 2025-12-11 — https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\n * • MCPB manifest 0.3 — github.com/anthropics/mcpb (MANIFEST.md)\n * • MCP protocol 2025-11-25 — modelcontextprotocol.io/specification/2025-11-25\n * (latest RELEASED revision. 2026-07-28 is a release-candidate only — do NOT\n * advance to it until it publishes as a stable dated revision. The probe\n * offers this version but ACCEPTS whatever a server negotiates, so the\n * offered literal being stale never breaks interop — only this constant.)\n */\n\nimport type { Transport } from \"./types.js\";\n\n/** Registry server.json JSON Schema URL (released 2025-12-11). MUST-VERIFY on bump. */\nexport const MCP_SERVER_SCHEMA_URL =\n \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\";\n\n/** Current MCPB bundle manifest_version (anthropics/mcpb). MUST-VERIFY on bump. */\nexport const MCPB_MANIFEST_VERSION = \"0.3\";\n\n/**\n * Latest RELEASED MCP protocol revision `doctor --probe` offers in `initialize`.\n * Current = 2025-11-25. MUST-VERIFY on bump; stay on the latest *released*\n * revision (2026-07-28 is RC-only as of 2026-06). Negotiation accepts whatever\n * the server returns, so this is the offered value, not a hard requirement.\n */\nexport const MCP_PROTOCOL_VERSION = \"2025-11-25\";\n\n/** Default npm registry base URL stamped into server.json npm packages. */\nexport const NPM_REGISTRY_BASE_URL = \"https://registry.npmjs.org\";\n\n/**\n * server.json transport.type enum — the EXACT values the 2025-12-11 schema\n * accepts (packages[].transport.type and remotes[].type). Our `http` Transport\n * maps to the spec slug \"streamable-http\"; \"ws\" is non-spec (see below).\n */\nexport type RegistryTransportType = \"stdio\" | \"streamable-http\" | \"sse\";\n\n/**\n * Map our {@link Transport} union to the registry/server.json transport slug.\n * Returns null for \"ws\" — WebSocket is NOT a spec transport (it can only exist\n * as a vendor custom transport, e.g. Claude Code's non-spec type:\"ws\"), so a\n * conformant artifact must REJECT it rather than emit an invalid type. The spec\n * defines exactly stdio + streamable-http, with sse the deprecated HTTP+SSE.\n */\nexport function registryTransportType(transport: Transport): RegistryTransportType | null {\n switch (transport) {\n case \"stdio\":\n return \"stdio\";\n case \"http\":\n return \"streamable-http\";\n case \"sse\":\n return \"sse\";\n case \"ws\":\n return null;\n }\n}\n\n/**\n * Is `v` a CONCRETE version the MCP Registry accepts? The registry rejects\n * ranges (\"^1.2.3\", \"~1.2.3\", \">=1.2.3\", \"1.x\", \"1.*\"); only a pinned SemVer\n * (optionally with a -prerelease / +build suffix) is valid. \"0.0.0\" is concrete\n * (so it passes this guard) but is the unset placeholder — callers warn on it\n * separately via {@link isPlaceholderVersion}.\n */\nexport function isConcreteSemver(v: string): boolean {\n return /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/.test(v);\n}\n\n/** The unset default version a connector ships with until the dev sets a real one. */\nexport function isPlaceholderVersion(v: string): boolean {\n return v === \"0.0.0\";\n}\n\n/**\n * Reverse-DNS registry namespace pattern (the part BEFORE the \"/\" in a\n * server.json `name`). The full name regex is\n * `^[a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+$`; the namespace side allows letters, digits,\n * dots and hyphens (NO underscore) — e.g. \"io.github.acme\" or \"com.acme\".\n */\nexport const REGISTRY_NAMESPACE_RE = /^[a-zA-Z0-9.-]+$/;\n\n/** The full server.json `name` regex (namespace \"/\" server-name), per the schema. */\nexport const REGISTRY_NAME_RE = /^[a-zA-Z0-9.-]+\\/[a-zA-Z0-9._-]+$/;\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/telemetry/surface-footprint.ts","../src/telemetry/leaderboard.ts"],"sourcesContent":["/**\n * telemetry/surface-footprint — the STATIC developer-axis surfaces.\n *\n * Commands, skills, and subagents are NOT runtime-intercepted: the host loads\n * them as context (slash-command bodies, SKILL.md + resources, subagent system\n * prompts) and we never sit in the byte path the way the serve-proxy does for a\n * server or the hook runtime does for a hook. So we do NOT (and must not) write\n * fake usage rows for them. Instead we compute a deterministic STATIC FOOTPRINT:\n * the token cost the surface IMPOSES on a host that loads it, tokenized with the\n * SAME shared tokenizer the proxy + hook runtime use.\n *\n * This is pure: the only input is the passed {@link ResolvedConnector}; there is\n * no IO beyond reading that object's already-loaded fields.\n */\n\nimport type { ResolvedConnector } from \"../core/types.js\";\nimport { getTokenizer, inferModelFamily } from \"./tokenizer.js\";\nimport type { ModelFamily, SurfaceKind, Tokenizer } from \"./types.js\";\n\n/**\n * One static surface footprint: a content surface (command/skill/subagent) and\n * the token cost it imposes on a host that loads it. `kind` is the surface kind\n * (never `server`/`hook` — those are runtime-measured), `name` is the surface's\n * declared name, and `tokens` is the tokenized whole-surface footprint.\n */\nexport interface SurfaceFootprint {\n surfaceKind: Extract<SurfaceKind, \"command\" | \"skill\" | \"subagent\">;\n name: string;\n tokens: number;\n}\n\n/** Tokenize a list of text fragments (joined with \"\\n\") under `family`. */\nfunction countParts(\n parts: readonly string[],\n family: ModelFamily,\n tok: Tokenizer,\n): number {\n // A surface's footprint is its concatenated content — joining with a newline\n // mirrors how the host renders the fields together as one context blob.\n return tok.count(parts.join(\"\\n\"), family).tokens;\n}\n\n/**\n * Compute the STATIC footprints of every content surface declared on a\n * connector, deterministically (same input → same output) and without IO.\n *\n * • command → description + prompt + argumentHint\n * • skill → description + body + every resource value (sorted by path for\n * deterministic ordering, so the joined text is stable)\n * • subagent → description + prompt\n *\n * The shared {@link Tokenizer} is used so a command/skill/subagent footprint is\n * directly comparable with the runtime `server`/`hook` token counts. The model\n * family is inferred from the connector's telemetry hint (no host context here).\n */\nexport function computeSurfaceFootprints(\n connector: ResolvedConnector,\n tok: Tokenizer = getTokenizer(),\n): SurfaceFootprint[] {\n const family = inferModelFamily(\"\", connector.telemetry.modelFamilyHint);\n const out: SurfaceFootprint[] = [];\n\n for (const cmd of connector.commands) {\n const parts: string[] = [];\n if (cmd.description !== undefined) parts.push(cmd.description);\n parts.push(cmd.prompt);\n if (cmd.argumentHint !== undefined) parts.push(cmd.argumentHint);\n out.push({\n surfaceKind: \"command\",\n name: cmd.name,\n tokens: countParts(parts, family, tok),\n });\n }\n\n for (const skill of connector.skills) {\n const parts: string[] = [skill.description, skill.body];\n if (skill.resources !== undefined) {\n // Sort by resource path so the joined text (and thus the count) is stable\n // regardless of object key insertion order.\n const paths = Object.keys(skill.resources).sort();\n for (const p of paths) {\n const value = skill.resources[p];\n if (value !== undefined) parts.push(value);\n }\n }\n out.push({\n surfaceKind: \"skill\",\n name: skill.name,\n tokens: countParts(parts, family, tok),\n });\n }\n\n for (const agent of connector.subagents) {\n out.push({\n surfaceKind: \"subagent\",\n name: agent.name,\n tokens: countParts([agent.description, agent.prompt], family, tok),\n });\n }\n\n return out;\n}\n","/**\n * telemetry/leaderboard — the PLUGIN / MCP leaderboard, derived from the per-MCP\n * serve-proxy telemetry store (src/telemetry/*).\n *\n * This is the signature MCP-dev metric: of the MCP servers WE wrap, which one\n * costs the most tokens — platform-independent, because it measures the server's\n * OWN bytes (tool args in, tool result out, tool-defs schemas) tokenized locally,\n * the only signal identical across every host. Origin is always `mcp-self`; it is\n * NEVER summed with host-native usage (src/usage/*), which measures a different\n * thing (whole-conversation tokens).\n *\n * Pure aggregation + pure formatting over {@link TelemetryStore.query}. The scope\n * dimension (installScope/launchMethod) is OPTIONAL on each row — rows written\n * before those fields existed are read as \"unknown\" and are honestly labeled.\n */\n\nimport type { ResolvedConnector } from \"../core/types.js\";\nimport { openStore } from \"./store.js\";\nimport { computeSurfaceFootprints } from \"./surface-footprint.js\";\nimport { worstConfidence } from \"./types.js\";\nimport type {\n ConfidenceSource,\n LaunchMethod,\n QueryFilter,\n SurfaceKind,\n TelemetryInstallScope,\n TelemetryStore,\n ToolEventRecord,\n} from \"./types.js\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// Options + scope filter\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * A scope filter value the leaderboards accept. The two install buckets\n * (`user`/`project`) and the launch methods (`npx`/`bunx`/`uvx`/`node`/`binary`/\n * `http`) plus the honest `unknown` fallback. A record matches a `user`/`project`\n * value by its `installScope`, and a launch-method value by its `launchMethod`.\n */\nexport type ScopeFilter =\n | TelemetryInstallScope\n | LaunchMethod;\n\n/** Every accepted {@link ScopeFilter} string, for CLI validation. */\nexport const SCOPE_FILTER_VALUES: readonly ScopeFilter[] = [\n \"user\",\n \"project\",\n \"npx\",\n \"bunx\",\n \"uvx\",\n \"node\",\n \"binary\",\n \"http\",\n \"unknown\",\n] as const;\n\n/** Options shared by every leaderboard query. */\nexport interface LeaderboardOptions {\n /** An open store to read from. Defaults to {@link openStore} on the data-root. */\n store?: TelemetryStore;\n /** Lower-bound epoch ms (inclusive) — forwarded as the store's sinceMs filter. */\n sinceMs?: number;\n /** Restrict to one connector. */\n connectorId?: string;\n /** Slice by install scope (user|project) or launch method (npx|binary|http|…). */\n scope?: ScopeFilter;\n}\n\n/** Is `value` one of the accepted scope-filter strings? */\nexport function isScopeFilter(value: string): value is ScopeFilter {\n return (SCOPE_FILTER_VALUES as readonly string[]).includes(value);\n}\n\n/** The install/launch values a `user`/`project` scope filter matches against. */\nconst INSTALL_SCOPES: ReadonlySet<string> = new Set<string>([\"user\", \"project\"]);\n\n/**\n * Does a record satisfy the requested scope slice? `user`/`project` match the\n * record's `installScope`; any concrete launch method matches the record's\n * `launchMethod`. The honest `unknown` filter is special: it matches ONLY a\n * record that lacks BOTH dimensions (a truly pre-scope row) — a record carrying\n * a known installScope but no launchMethod (or vice-versa) is NOT \"unknown\" and\n * must not be swept into that bucket.\n */\nfunction matchesScope(record: ToolEventRecord, scope: ScopeFilter): boolean {\n if (scope === \"unknown\") {\n return record.installScope === undefined && record.launchMethod === undefined;\n }\n if (INSTALL_SCOPES.has(scope)) {\n return record.installScope === scope;\n }\n return record.launchMethod === scope;\n}\n\n/** Build the store {@link QueryFilter} from the shared options. */\nfunction toFilter(opts: LeaderboardOptions): QueryFilter {\n const filter: QueryFilter = {};\n if (opts.sinceMs !== undefined) filter.sinceMs = opts.sinceMs;\n if (opts.connectorId !== undefined && opts.connectorId !== \"\") {\n filter.connectorId = opts.connectorId;\n }\n return filter;\n}\n\n/**\n * Query the store and apply the (in-memory) scope slice the store cannot. By\n * default EXCLUDES the host-native `model_turn` rows: this selector feeds the\n * per-MCP/plugin views (mcp/tool/scope), which measure per-MCP `call` + the\n * `tool_defs` overhead and must NEVER sum the whole-conversation host-native\n * turns. Pass `includeModelTurn: true` to read those rows instead (used by\n * {@link hostNativeTurns}).\n */\nfunction selectRecords(\n opts: LeaderboardOptions,\n includeModelTurn = false,\n): ToolEventRecord[] {\n const store = opts.store ?? openStore({});\n const owned = opts.store === undefined;\n try {\n let rows = store.query(toFilter(opts));\n rows = includeModelTurn\n ? rows.filter((r) => r.scope === \"model_turn\")\n : rows.filter((r) => r.scope !== \"model_turn\");\n if (opts.scope === undefined) return rows;\n const scope = opts.scope;\n return rows.filter((r) => matchesScope(r, scope));\n } finally {\n if (owned) store.close();\n }\n}\n\n// Confidence ranking + worst-of comparison live in ./types (the single source\n// of truth, imported as worstConfidence above) so a new ConfidenceSource value\n// orders correctly here as well.\n\n// ─────────────────────────────────────────────────────────────────────────\n// Leaderboard rows\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * One row of the MCP/plugin leaderboard — a connector ranked by total measured\n * tokens. `tools` is the count of distinct tool names seen (excluding the\n * tool-defs `*` pseudo-tool); `hostPlatforms` is the distinct set of hosts that\n * ran this server, evidence that the count aggregates across machines.\n */\nexport interface McpLeaderboardRow {\n connectorId: string;\n calls: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n /** Count of distinct real tool names (the tool-defs `*` row is excluded). */\n tools: number;\n /** Distinct host platforms that produced records, sorted for stable output. */\n hostPlatforms: string[];\n /** Worst (least-confident) source in the group, for honest labeling. */\n confidence: ConfidenceSource;\n lastTs: number;\n}\n\n/**\n * One row of the per-tool leaderboard — a (connector, tool) pair ranked by total\n * measured tokens. The tool-defs pseudo-tool (`*`, scope `tool_defs`) is kept as\n * its own row so the fixed schema overhead is visible rather than hidden.\n */\nexport interface ToolLeaderboardRow {\n connectorId: string;\n toolName: string;\n /** \"call\" round-trips vs the one-time \"tool_defs\" overhead measurement. */\n scope: ToolEventRecord[\"scope\"];\n calls: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n confidence: ConfidenceSource;\n lastTs: number;\n}\n\n/**\n * One row of the scope breakdown — usage grouped by (installScope, launchMethod),\n * both honestly reported as \"unknown\" when the underlying record lacks the field.\n */\nexport interface ScopeBreakdownRow {\n installScope: TelemetryInstallScope | \"unknown\";\n launchMethod: LaunchMethod;\n calls: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n confidence: ConfidenceSource;\n lastTs: number;\n}\n\n/**\n * One row of the host-native turns aggregation — a (host, session) pair ranked by\n * total host-reported tokens for that whole-conversation turn stream. This is the\n * THIRD origin (`host-native-live`): it comes from the opt-in AfterModel /\n * PostInvocation usage hook (scope `model_turn`, confidence `host-native`) and is\n * NEVER summed with the per-MCP `call` rows or the usage-reader host-scan numbers.\n */\nexport interface HostNativeTurnsRow {\n hostPlatform: string;\n sessionId: string;\n /** Number of recorded model turns (scope `model_turn` rows) in this group. */\n turns: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n /** Distinct connector ids that produced turns under this host/session. */\n connectors: string[];\n confidence: ConfidenceSource;\n lastTs: number;\n}\n\n/** Mutable accumulator for the host-native turns fold (set finalized to count). */\ninterface HostNativeAcc {\n hostPlatform: string;\n sessionId: string;\n turns: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n connectors: Set<string>;\n confidence: ConfidenceSource;\n lastTs: number;\n}\n\n/** Mutable accumulator for the MCP leaderboard fold (sets finalized to counts). */\ninterface McpAcc {\n connectorId: string;\n calls: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n tools: Set<string>;\n hostPlatforms: Set<string>;\n confidence: ConfidenceSource;\n lastTs: number;\n}\n\n/**\n * The PLUGIN / MCP leaderboard: rank connectors by total measured tokens desc\n * (ties broken by recency). This is the headline \"which MCP server costs the\n * most\" metric, platform-independent by construction.\n */\nexport function mcpLeaderboard(opts: LeaderboardOptions = {}): McpLeaderboardRow[] {\n const groups = new Map<string, McpAcc>();\n\n for (const r of selectRecords(opts)) {\n let g = groups.get(r.connectorId);\n if (g === undefined) {\n g = {\n connectorId: r.connectorId,\n calls: 0,\n inputTokens: 0,\n outputTokens: 0,\n totalTokens: 0,\n tools: new Set<string>(),\n hostPlatforms: new Set<string>(),\n confidence: r.confidenceSource,\n lastTs: r.ts,\n };\n groups.set(r.connectorId, g);\n }\n // A tool_defs row is a one-time schema overhead, not a call: count its\n // tokens but do not inflate the call count or the distinct-tool count.\n if (r.scope === \"call\") {\n g.calls += 1;\n g.tools.add(r.toolName);\n }\n g.inputTokens += r.inputTokens;\n g.outputTokens += r.outputTokens;\n g.totalTokens += r.inputTokens + r.outputTokens;\n g.hostPlatforms.add(r.hostPlatform);\n g.confidence = worstConfidence(g.confidence, r.confidenceSource);\n if (r.ts > g.lastTs) g.lastTs = r.ts;\n }\n\n const rows: McpLeaderboardRow[] = [];\n for (const g of groups.values()) {\n rows.push({\n connectorId: g.connectorId,\n calls: g.calls,\n inputTokens: g.inputTokens,\n outputTokens: g.outputTokens,\n totalTokens: g.totalTokens,\n tools: g.tools.size,\n hostPlatforms: [...g.hostPlatforms].sort(),\n confidence: g.confidence,\n lastTs: g.lastTs,\n });\n }\n rows.sort((a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs);\n return rows;\n}\n\n/**\n * The per-tool leaderboard: rank (connector, tool) pairs by total tokens desc\n * (ties broken by recency). Drills the MCP leaderboard down to which individual\n * tool is the expensive one.\n */\nexport function toolLeaderboard(opts: LeaderboardOptions = {}): ToolLeaderboardRow[] {\n const groups = new Map<string, ToolLeaderboardRow>();\n\n for (const r of selectRecords(opts)) {\n const key = `${r.connectorId}\\u0000${r.toolName}\\u0000${r.scope}`;\n const g = groups.get(key);\n if (g === undefined) {\n groups.set(key, {\n connectorId: r.connectorId,\n toolName: r.toolName,\n scope: r.scope,\n calls: r.scope === \"call\" ? 1 : 0,\n inputTokens: r.inputTokens,\n outputTokens: r.outputTokens,\n totalTokens: r.inputTokens + r.outputTokens,\n confidence: r.confidenceSource,\n lastTs: r.ts,\n });\n } else {\n if (r.scope === \"call\") g.calls += 1;\n g.inputTokens += r.inputTokens;\n g.outputTokens += r.outputTokens;\n g.totalTokens += r.inputTokens + r.outputTokens;\n g.confidence = worstConfidence(g.confidence, r.confidenceSource);\n if (r.ts > g.lastTs) g.lastTs = r.ts;\n }\n }\n\n const rows = [...groups.values()];\n rows.sort((a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs);\n return rows;\n}\n\n/**\n * The scope breakdown: group usage by (installScope, launchMethod). Both\n * dimensions read \"unknown\" when the source row lacks the field, so a pre-scope\n * row is honestly bucketed rather than dropped or mis-attributed.\n */\nexport function scopeBreakdown(opts: LeaderboardOptions = {}): ScopeBreakdownRow[] {\n const groups = new Map<string, ScopeBreakdownRow>();\n\n for (const r of selectRecords(opts)) {\n const installScope: TelemetryInstallScope | \"unknown\" = r.installScope ?? \"unknown\";\n const launchMethod: LaunchMethod = r.launchMethod ?? \"unknown\";\n const key = `${installScope}\\u0000${launchMethod}`;\n const g = groups.get(key);\n if (g === undefined) {\n groups.set(key, {\n installScope,\n launchMethod,\n calls: r.scope === \"call\" ? 1 : 0,\n inputTokens: r.inputTokens,\n outputTokens: r.outputTokens,\n totalTokens: r.inputTokens + r.outputTokens,\n confidence: r.confidenceSource,\n lastTs: r.ts,\n });\n } else {\n if (r.scope === \"call\") g.calls += 1;\n g.inputTokens += r.inputTokens;\n g.outputTokens += r.outputTokens;\n g.totalTokens += r.inputTokens + r.outputTokens;\n g.confidence = worstConfidence(g.confidence, r.confidenceSource);\n if (r.ts > g.lastTs) g.lastTs = r.ts;\n }\n }\n\n const rows = [...groups.values()];\n rows.sort((a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs);\n return rows;\n}\n\n/**\n * The THIRD origin: host-native turns. Aggregates ONLY the `model_turn` rows (the\n * opt-in AfterModel / PostInvocation host-native usage hook) by (host, session),\n * ranked by total host-reported tokens desc. This is whole-conversation usage the\n * host itself reported — exact, but a DIFFERENT thing than the per-MCP `call`\n * rows; it is surfaced separately and NEVER summed with the other two origins.\n */\nexport function hostNativeTurns(opts: LeaderboardOptions = {}): HostNativeTurnsRow[] {\n const groups = new Map<string, HostNativeAcc>();\n\n for (const r of selectRecords(opts, /* includeModelTurn */ true)) {\n const key = `${r.hostPlatform}\\u0000${r.sessionId}`;\n let g = groups.get(key);\n if (g === undefined) {\n g = {\n hostPlatform: r.hostPlatform,\n sessionId: r.sessionId,\n turns: 0,\n inputTokens: 0,\n outputTokens: 0,\n totalTokens: 0,\n connectors: new Set<string>(),\n confidence: r.confidenceSource,\n lastTs: r.ts,\n };\n groups.set(key, g);\n }\n g.turns += 1;\n g.inputTokens += r.inputTokens;\n g.outputTokens += r.outputTokens;\n g.totalTokens += r.inputTokens + r.outputTokens;\n g.connectors.add(r.connectorId);\n g.confidence = worstConfidence(g.confidence, r.confidenceSource);\n if (r.ts > g.lastTs) g.lastTs = r.ts;\n }\n\n const rows: HostNativeTurnsRow[] = [];\n for (const g of groups.values()) {\n rows.push({\n hostPlatform: g.hostPlatform,\n sessionId: g.sessionId,\n turns: g.turns,\n inputTokens: g.inputTokens,\n outputTokens: g.outputTokens,\n totalTokens: g.totalTokens,\n connectors: [...g.connectors].sort(),\n confidence: g.confidence,\n lastTs: g.lastTs,\n });\n }\n rows.sort((a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs);\n return rows;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Per-surface leaderboard (the FIVE developer-axis surfaces)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * One row of the per-surface developer breakdown — a (surfaceKind, name) pair.\n * Combines the RUNTIME-measured surfaces (server `call`+`tool_defs` rows and the\n * new `hook` rows from the telemetry store) with the STATIC content footprints\n * (command/skill/subagent) from {@link computeSurfaceFootprints}.\n *\n * • `kind` distinguishes RUNTIME (measured live) from STATIC (footprint of\n * context the host loads — never an intercepted usage row).\n * • For static rows `calls` is 0, `outputTokens` is 0, the cost sits in\n * `inputTokens`/`totalTokens`, and `confidence` is the tokenizer source for\n * the connector's family. For runtime rows everything aggregates the store.\n */\nexport interface SurfaceLeaderboardRow {\n surfaceKind: SurfaceKind;\n /** Per-item name: the tool name (server), the event name (hook), or the\n * command/skill/subagent name (static surfaces). */\n name: string;\n /** RUNTIME (server/hook store rows) vs STATIC (command/skill/subagent footprint). */\n kind: \"runtime\" | \"static\";\n calls: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n confidence: ConfidenceSource;\n lastTs: number;\n}\n\n/** Mutable accumulator for the runtime side of the per-surface fold. */\ninterface SurfaceAcc {\n surfaceKind: SurfaceKind;\n name: string;\n calls: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n confidence: ConfidenceSource;\n lastTs: number;\n}\n\n/**\n * A record's developer-axis surface kind, backward-compatibly. Rows written\n * before `surfaceKind` existed (every legacy serve-proxy `call`/`tool_defs` row)\n * lack it and are read as `server`; the `hook` runtime stamps it explicitly.\n */\nfunction recordSurfaceKind(r: ToolEventRecord): SurfaceKind {\n if (r.surfaceKind !== undefined) return r.surfaceKind;\n // No explicit kind → it predates this field → it is a serve-proxy server row.\n return \"server\";\n}\n\n/** Options for {@link surfaceLeaderboard}: the shared query opts + the connectors\n * whose STATIC footprints are folded in. */\nexport interface SurfaceLeaderboardOptions extends LeaderboardOptions {\n /** Registered connectors to compute static command/skill/subagent footprints\n * over. Omit/empty → only the runtime (server/hook) rows are reported. */\n connectors?: readonly ResolvedConnector[];\n}\n\n/**\n * The PER-SURFACE developer leaderboard across all FIVE surfaces.\n *\n * RUNTIME side: the store's `call`/`tool_defs` rows (surface `server`) and the\n * `hook` rows (surface `hook`), grouped by (surfaceKind, name) with IN/OUT/TOTAL\n * and a call count. (`model_turn` rows are excluded by `selectRecords`, exactly\n * as the per-MCP views exclude them.)\n *\n * STATIC side: one row per command/skill/subagent from\n * {@link computeSurfaceFootprints} over each passed connector, deduped by\n * (surfaceKind, name) and summed when the same surface is declared by more than\n * one connector. Static rows carry the footprint in `inputTokens` (the cost the\n * host pays to load that context); `outputTokens`/`calls` are 0.\n *\n * Sorted by total tokens desc, ties broken by recency (static rows have lastTs 0\n * so they sort after equal-token runtime rows).\n */\nexport function surfaceLeaderboard(\n opts: SurfaceLeaderboardOptions = {},\n): SurfaceLeaderboardRow[] {\n // ── RUNTIME: aggregate server + hook store rows by (surfaceKind, name) ──\n const runtime = new Map<string, SurfaceAcc>();\n for (const r of selectRecords(opts)) {\n const surfaceKind = recordSurfaceKind(r);\n const key = `${surfaceKind} ${r.toolName}`;\n let g = runtime.get(key);\n if (g === undefined) {\n g = {\n surfaceKind,\n name: r.toolName,\n calls: 0,\n inputTokens: 0,\n outputTokens: 0,\n totalTokens: 0,\n confidence: r.confidenceSource,\n lastTs: r.ts,\n };\n runtime.set(key, g);\n }\n // tool_defs is a one-time schema overhead, not a call — count tokens only.\n if (r.scope === \"call\" || r.scope === \"hook\") g.calls += 1;\n g.inputTokens += r.inputTokens;\n g.outputTokens += r.outputTokens;\n g.totalTokens += r.inputTokens + r.outputTokens;\n g.confidence = worstConfidence(g.confidence, r.confidenceSource);\n if (r.ts > g.lastTs) g.lastTs = r.ts;\n }\n\n const rows: SurfaceLeaderboardRow[] = [];\n for (const g of runtime.values()) {\n rows.push({\n surfaceKind: g.surfaceKind,\n name: g.name,\n kind: \"runtime\",\n calls: g.calls,\n inputTokens: g.inputTokens,\n outputTokens: g.outputTokens,\n totalTokens: g.totalTokens,\n confidence: g.confidence,\n lastTs: g.lastTs,\n });\n }\n\n // ── STATIC: fold in command/skill/subagent footprints per connector ──────\n const staticRows = new Map<\n string,\n { surfaceKind: SurfaceKind; name: string; tokens: number }\n >();\n for (const connector of opts.connectors ?? []) {\n for (const fp of computeSurfaceFootprints(connector)) {\n const key = `${fp.surfaceKind} ${fp.name}`;\n const existing = staticRows.get(key);\n if (existing === undefined) {\n staticRows.set(key, { surfaceKind: fp.surfaceKind, name: fp.name, tokens: fp.tokens });\n } else {\n existing.tokens += fp.tokens;\n }\n }\n }\n // A pure tokenizer footprint carries no host-reported truth — label it with the\n // tokenizer source the connector's family would yield. We derive that once via\n // a throwaway count so the static confidence is honest (approx vs exact).\n const staticConfidence: ConfidenceSource = staticFootprintConfidence(opts.connectors ?? []);\n for (const s of staticRows.values()) {\n rows.push({\n surfaceKind: s.surfaceKind,\n name: s.name,\n kind: \"static\",\n calls: 0,\n inputTokens: s.tokens,\n outputTokens: 0,\n totalTokens: s.tokens,\n confidence: staticConfidence,\n lastTs: 0,\n });\n }\n\n rows.sort((a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs);\n return rows;\n}\n\n/**\n * The confidence label for the static footprints: the tokenizer source for the\n * first connector's family (exact for openai, approx otherwise). Defaults to\n * `tokenizer-approx` when no connector is supplied — an honest estimate label.\n */\nfunction staticFootprintConfidence(\n connectors: readonly ResolvedConnector[],\n): ConfidenceSource {\n const first = connectors[0];\n if (first === undefined) return \"tokenizer-approx\";\n // openai family → exact BPE; everything else → approx. Mirrors tokenizer.bpeSource.\n return first.telemetry.modelFamilyHint === \"openai\"\n ? \"tokenizer-exact\"\n : \"tokenizer-approx\";\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Formatting (mirrors telemetry/report.ts: aligned table + honesty legend)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Sources whose counts are estimates (vs real / host-reported numbers).\n * `tokenizer-calibrated` is still an estimate — an approximation nudged toward\n * truth by a sampled Anthropic count_tokens factor — so it belongs here too.\n */\nconst ESTIMATE_SOURCES: ReadonlySet<ConfidenceSource> = new Set<ConfidenceSource>([\n \"heuristic\",\n \"tokenizer-approx\",\n \"tokenizer-calibrated\",\n]);\n\n/** Compact integer formatting with thousands separators. */\nfunction fmtInt(n: number): string {\n return n.toLocaleString(\"en-US\");\n}\n\n/** Right-pad (left-aligned columns). */\nfunction padRight(s: string, width: number): string {\n return s.length >= width ? s : s + \" \".repeat(width - s.length);\n}\n\n/** Left-pad (right-aligned numeric columns). */\nfunction padLeft(s: string, width: number): string {\n return s.length >= width ? s : \" \".repeat(width - s.length) + s;\n}\n\n/**\n * Render an aligned table given headers, the data cells, and which columns are\n * left-aligned (text) vs right-aligned (numbers). Adds rule lines and a TOTAL\n * footer. Shared by every leaderboard formatter for one consistent style.\n */\nfunction renderTable(\n headers: string[],\n dataRows: string[][],\n leftAlignedCols: ReadonlySet<number>,\n totalRow: string[],\n emptyMessage: string,\n): string[] {\n const widths = headers.map((h, col) => {\n let w = h.length;\n for (const row of dataRows) w = Math.max(w, (row[col] ?? \"\").length);\n w = Math.max(w, (totalRow[col] ?? \"\").length);\n return w;\n });\n\n const renderRow = (cells: string[]): string =>\n cells\n .map((c, col) =>\n leftAlignedCols.has(col)\n ? padRight(c, widths[col] ?? 0)\n : padLeft(c, widths[col] ?? 0),\n )\n .join(\" \")\n .trimEnd();\n\n const ruleWidth = widths.reduce((a, b) => a + b, 0) + 2 * (widths.length - 1);\n const rule = \"-\".repeat(ruleWidth);\n\n const lines: string[] = [];\n lines.push(renderRow(headers));\n lines.push(rule);\n if (dataRows.length === 0) {\n lines.push(emptyMessage);\n } else {\n for (const row of dataRows) lines.push(renderRow(row));\n }\n lines.push(rule);\n lines.push(renderRow(totalRow));\n return lines;\n}\n\n/** Append the estimate legend when any row carries an estimate confidence. */\nfunction appendEstimateLegend(\n lines: string[],\n confidences: ConfidenceSource[],\n): void {\n if (confidences.some((c) => ESTIMATE_SOURCES.has(c))) {\n lines.push(\"\");\n lines.push(\n \"note: heuristic and tokenizer-approx token counts are estimates, \" +\n \"not exact host-reported usage.\",\n );\n if (confidences.some((c) => c === \"tokenizer-calibrated\")) {\n lines.push(\n \"note: tokenizer-calibrated = local approx adjusted by a sampled \" +\n \"Anthropic count_tokens factor (opt-in; content sampled off-box only \" +\n \"when AGENT_CONNECTOR_CALIBRATE=anthropic).\",\n );\n }\n }\n}\n\n/**\n * Format the MCP/plugin leaderboard as an aligned table:\n * RANK | CONNECTOR | CALLS | TOOLS | IN | OUT | TOTAL | HOSTS | CONFIDENCE\n * sorted by total tokens desc, with a TOTAL footer and the honesty legend.\n */\nexport function formatMcpLeaderboard(rows: McpLeaderboardRow[]): string {\n const sorted = [...rows].sort(\n (a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs,\n );\n const headers = [\n \"RANK\",\n \"CONNECTOR\",\n \"CALLS\",\n \"TOOLS\",\n \"IN\",\n \"OUT\",\n \"TOTAL\",\n \"HOSTS\",\n \"CONFIDENCE\",\n ];\n const dataRows = sorted.map((r, i) => [\n `${i + 1}`,\n r.connectorId,\n fmtInt(r.calls),\n fmtInt(r.tools),\n fmtInt(r.inputTokens),\n fmtInt(r.outputTokens),\n fmtInt(r.totalTokens),\n r.hostPlatforms.length === 0 ? \"-\" : r.hostPlatforms.join(\",\"),\n r.confidence,\n ]);\n\n const totals = sorted.reduce(\n (acc, r) => {\n acc.calls += r.calls;\n acc.inputTokens += r.inputTokens;\n acc.outputTokens += r.outputTokens;\n acc.totalTokens += r.totalTokens;\n return acc;\n },\n { calls: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n );\n const totalRow = [\n \"\",\n \"TOTAL\",\n fmtInt(totals.calls),\n \"\",\n fmtInt(totals.inputTokens),\n fmtInt(totals.outputTokens),\n fmtInt(totals.totalTokens),\n \"\",\n \"\",\n ];\n\n // Left-align RANK (0), CONNECTOR (1), HOSTS (7), CONFIDENCE (8); numbers right.\n const leftCols = new Set<number>([0, 1, 7, 8]);\n const lines = renderTable(\n headers,\n dataRows,\n leftCols,\n totalRow,\n \"(no MCP telemetry recorded)\",\n );\n appendEstimateLegend(\n lines,\n sorted.map((r) => r.confidence),\n );\n return lines.join(\"\\n\");\n}\n\n/**\n * Format the per-tool leaderboard:\n * RANK | CONNECTOR | TOOL | SCOPE | CALLS | IN | OUT | TOTAL | CONFIDENCE\n * sorted by total tokens desc, with a TOTAL footer and the honesty legend.\n */\nexport function formatToolLeaderboard(rows: ToolLeaderboardRow[]): string {\n const sorted = [...rows].sort(\n (a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs,\n );\n const headers = [\n \"RANK\",\n \"CONNECTOR\",\n \"TOOL\",\n \"SCOPE\",\n \"CALLS\",\n \"IN\",\n \"OUT\",\n \"TOTAL\",\n \"CONFIDENCE\",\n ];\n const dataRows = sorted.map((r, i) => [\n `${i + 1}`,\n r.connectorId,\n r.toolName,\n r.scope,\n fmtInt(r.calls),\n fmtInt(r.inputTokens),\n fmtInt(r.outputTokens),\n fmtInt(r.totalTokens),\n r.confidence,\n ]);\n\n const totals = sorted.reduce(\n (acc, r) => {\n acc.calls += r.calls;\n acc.inputTokens += r.inputTokens;\n acc.outputTokens += r.outputTokens;\n acc.totalTokens += r.totalTokens;\n return acc;\n },\n { calls: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n );\n const totalRow = [\n \"\",\n \"TOTAL\",\n \"\",\n \"\",\n fmtInt(totals.calls),\n fmtInt(totals.inputTokens),\n fmtInt(totals.outputTokens),\n fmtInt(totals.totalTokens),\n \"\",\n ];\n\n // Left-align RANK (0), CONNECTOR (1), TOOL (2), SCOPE (3), CONFIDENCE (8).\n const leftCols = new Set<number>([0, 1, 2, 3, 8]);\n const lines = renderTable(\n headers,\n dataRows,\n leftCols,\n totalRow,\n \"(no MCP telemetry recorded)\",\n );\n appendEstimateLegend(\n lines,\n sorted.map((r) => r.confidence),\n );\n return lines.join(\"\\n\");\n}\n\n/**\n * Format the scope breakdown:\n * INSTALL | LAUNCH | CALLS | IN | OUT | TOTAL | CONFIDENCE\n * with a TOTAL footer and the honesty legend.\n */\nexport function formatScopeBreakdown(rows: ScopeBreakdownRow[]): string {\n const sorted = [...rows].sort(\n (a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs,\n );\n const headers = [\"INSTALL\", \"LAUNCH\", \"CALLS\", \"IN\", \"OUT\", \"TOTAL\", \"CONFIDENCE\"];\n const dataRows = sorted.map((r) => [\n r.installScope,\n r.launchMethod,\n fmtInt(r.calls),\n fmtInt(r.inputTokens),\n fmtInt(r.outputTokens),\n fmtInt(r.totalTokens),\n r.confidence,\n ]);\n\n const totals = sorted.reduce(\n (acc, r) => {\n acc.calls += r.calls;\n acc.inputTokens += r.inputTokens;\n acc.outputTokens += r.outputTokens;\n acc.totalTokens += r.totalTokens;\n return acc;\n },\n { calls: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n );\n const totalRow = [\n \"TOTAL\",\n \"\",\n fmtInt(totals.calls),\n fmtInt(totals.inputTokens),\n fmtInt(totals.outputTokens),\n fmtInt(totals.totalTokens),\n \"\",\n ];\n\n // Left-align INSTALL (0), LAUNCH (1), CONFIDENCE (6); numbers right-aligned.\n const leftCols = new Set<number>([0, 1, 6]);\n const lines = renderTable(\n headers,\n dataRows,\n leftCols,\n totalRow,\n \"(no MCP telemetry recorded)\",\n );\n appendEstimateLegend(\n lines,\n sorted.map((r) => r.confidence),\n );\n return lines.join(\"\\n\");\n}\n\n/**\n * Format the host-native turns aggregation (the THIRD origin):\n * RANK | HOST | SESSION | TURNS | IN | OUT | TOTAL | CONNECTORS | CONFIDENCE\n * sorted by total tokens desc, with a TOTAL footer. These are whole-conversation,\n * host-reported (exact) counts — a DIFFERENT thing than the per-MCP rows, so the\n * footer total is for THIS section alone and is never added to the others.\n */\nexport function formatHostNativeTurns(rows: HostNativeTurnsRow[]): string {\n const sorted = [...rows].sort(\n (a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs,\n );\n const headers = [\n \"RANK\",\n \"HOST\",\n \"SESSION\",\n \"TURNS\",\n \"IN\",\n \"OUT\",\n \"TOTAL\",\n \"CONNECTORS\",\n \"CONFIDENCE\",\n ];\n const dataRows = sorted.map((r, i) => [\n `${i + 1}`,\n r.hostPlatform,\n r.sessionId === \"\" ? \"-\" : r.sessionId,\n fmtInt(r.turns),\n fmtInt(r.inputTokens),\n fmtInt(r.outputTokens),\n fmtInt(r.totalTokens),\n r.connectors.length === 0 ? \"-\" : r.connectors.join(\",\"),\n r.confidence,\n ]);\n\n const totals = sorted.reduce(\n (acc, r) => {\n acc.turns += r.turns;\n acc.inputTokens += r.inputTokens;\n acc.outputTokens += r.outputTokens;\n acc.totalTokens += r.totalTokens;\n return acc;\n },\n { turns: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n );\n const totalRow = [\n \"\",\n \"TOTAL\",\n \"\",\n fmtInt(totals.turns),\n fmtInt(totals.inputTokens),\n fmtInt(totals.outputTokens),\n fmtInt(totals.totalTokens),\n \"\",\n \"\",\n ];\n\n // Left-align RANK (0), HOST (1), SESSION (2), CONNECTORS (7), CONFIDENCE (8).\n const leftCols = new Set<number>([0, 1, 2, 7, 8]);\n const lines = renderTable(\n headers,\n dataRows,\n leftCols,\n totalRow,\n \"(no host-native turns recorded — enable opt-in host-native usage)\",\n );\n return lines.join(\"\\n\");\n}\n\n/**\n * Format the per-surface developer breakdown:\n * SURFACE | NAME | IN | OUT | TOTAL | KIND\n * one row per (surfaceKind, name) across all FIVE surfaces — server + hook\n * (runtime, measured from the store) and command/skill/subagent (static\n * footprints). Sorted by total tokens desc, with a TOTAL footer and the honesty\n * legend (static footprints + any estimate-confidence runtime rows).\n *\n * NOTE: the TOTAL footer sums BOTH runtime and static tokens for at-a-glance\n * scale; the KIND column keeps the distinction explicit so the two are never\n * silently conflated (runtime = live usage; static = context-load footprint).\n */\nexport function formatSurfaceLeaderboard(rows: SurfaceLeaderboardRow[]): string {\n const sorted = [...rows].sort(\n (a, b) => b.totalTokens - a.totalTokens || b.lastTs - a.lastTs,\n );\n const headers = [\"SURFACE\", \"NAME\", \"IN\", \"OUT\", \"TOTAL\", \"KIND\"];\n const dataRows = sorted.map((r) => [\n r.surfaceKind,\n r.name,\n fmtInt(r.inputTokens),\n fmtInt(r.outputTokens),\n fmtInt(r.totalTokens),\n r.kind,\n ]);\n\n const totals = sorted.reduce(\n (acc, r) => {\n acc.inputTokens += r.inputTokens;\n acc.outputTokens += r.outputTokens;\n acc.totalTokens += r.totalTokens;\n return acc;\n },\n { inputTokens: 0, outputTokens: 0, totalTokens: 0 },\n );\n const totalRow = [\n \"TOTAL\",\n \"\",\n fmtInt(totals.inputTokens),\n fmtInt(totals.outputTokens),\n fmtInt(totals.totalTokens),\n \"\",\n ];\n\n // Left-align SURFACE (0), NAME (1), KIND (5); numbers right-aligned.\n const leftCols = new Set<number>([0, 1, 5]);\n const lines = renderTable(\n headers,\n dataRows,\n leftCols,\n totalRow,\n \"(no developer surfaces recorded)\",\n );\n if (sorted.some((r) => r.kind === \"static\")) {\n lines.push(\"\");\n lines.push(\n \"note: KIND=static rows are the tokenized FOOTPRINT a command/skill/subagent \" +\n \"imposes on a host that loads it as context — not intercepted usage rows.\",\n );\n }\n appendEstimateLegend(\n lines,\n sorted.map((r) => r.confidence),\n );\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
|