@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/ts-plugin-bridge.ts"],"sourcesContent":["/**\n * Pure string-builder for the byte-identical HEAD of every ts-plugin host's\n * generated plugin module: the `HOME_BIN`/`CONNECTOR_ID` consts plus the\n * cross-OS `bridge(event, payload)` entrypoint (win32 `execSync`-quoted vs\n * POSIX `execFileSync`, fail-open `JSON.parse`).\n *\n * This module emits SOURCE TEXT only — it imports nothing at runtime and is\n * never loaded by a generated plugin. The 6 adopting hosts (amp, kilo,\n * kilo-cli, mimo-code, omp, opencode) differ in this region by exactly four\n * host-specific tokens, captured below. Changing the emitted bytes here changes\n * every host's generated plugin in lockstep; each host's adapter suite runs the\n * generated plugin live and asserts source substrings, so it is the byte oracle.\n */\n\nexport interface BridgePreludeOptions {\n /** Absolute home-bin path (raw — JSON.stringified inside). */\n homeBin: string;\n /** Connector id (raw — JSON.stringified inside). */\n connectorId: string;\n /** Host hook subcommand slug used in the `hook <slug>` argv (e.g. \"opencode\", \"amp\"). */\n hookSlug: string;\n /** Host noun for the jsdoc `@param payload <noun>-shaped …` line (e.g. \"OpenCode\", \"amp\", \"OMP\"). */\n payloadNoun: string;\n /** Optional suffix on the jsdoc `@param event canonical event name<eventDoc>` line (e.g. \" (PreToolUse|PostToolUse|SessionStart)\"). Default \"\". */\n eventDoc?: string;\n /** The exact fail-open catch-comment line (host wording differs). */\n failOpenComment: string;\n}\n\n/**\n * Render the HOME_BIN/CONNECTOR_ID consts + the universal-entrypoint bridge() —\n * the byte-identical head of every ts-plugin host's generated plugin module.\n */\nexport function renderBridgePrelude(opts: BridgePreludeOptions): string {\n return `const HOME_BIN = ${JSON.stringify(opts.homeBin)};\nconst CONNECTOR_ID = ${JSON.stringify(opts.connectorId)};\n\n/**\n * Invoke the universal hook entrypoint for one event.\n * @param {string} event canonical event name${opts.eventDoc ?? \"\"}\n * @param {object} payload ${opts.payloadNoun}-shaped payload posted on stdin\n * @returns {object|null} normalized HookResponse, or null on any failure\n */\nfunction bridge(event, payload) {\n try {\n // On Windows HOME_BIN is the agent-connector.cmd launcher: Node cannot\n // execFile a batch file, and shell+args is deprecated (DEP0190), so run one\n // quoted command line via a shell. POSIX keeps the direct execFile (no shell).\n const args = [\"hook\", ${JSON.stringify(opts.hookSlug)}, event, \"--connector\", CONNECTOR_ID];\n const opts = { input: JSON.stringify(payload), encoding: \"utf8\" };\n const stdout =\n process.platform === \"win32\"\n ? execSync([HOME_BIN, ...args].map((a) => '\"' + a + '\"').join(\" \"), opts)\n : execFileSync(HOME_BIN, args, opts);\n const text = (stdout || \"\").trim();\n if (text === \"\") return { decision: \"allow\" };\n return JSON.parse(text);\n } catch {\n // ${opts.failOpenComment}\n return null;\n }\n}`;\n}\n"],"mappings":";;;AAiCO,SAAS,oBAAoB,MAAoC;AACtE,SAAO,oBAAoB,KAAK,UAAU,KAAK,OAAO,CAAC;AAAA,uBAClC,KAAK,UAAU,KAAK,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA,+CAIR,KAAK,YAAY,EAAE;AAAA,6BACrC,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAQjB,KAAK,UAAU,KAAK,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAUhD,KAAK,eAAe;AAAA;AAAA;AAAA;AAI7B;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/yaml.ts"],"sourcesContent":["/**\n * core/yaml — minimal YAML config IO.\n *\n * The YAML analog of BaseAdapter's JSON helpers (readJson/writeJson). Adapters\n * whose native config is YAML (Goose's config.yaml, Hermes's config.yaml) cannot\n * use the JSON helpers, so they reach for these two functions instead — same\n * existsSync-guard / parse-failure-tolerant / dryRun-respecting contract, just\n * over the `yaml` package's parse()/stringify() rather than JSON.parse/stringify.\n *\n * Deliberately tiny: read-or-null and ensureDir+write. All merge/upsert/idempotency\n * logic lives in each adapter (the shapes differ per platform), exactly as the\n * JSON adapters layer their own upsert on top of writeJson.\n */\n\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\nimport { parse, stringify } from \"yaml\";\n\nimport type { ObjectMapCodec } from \"./object-map.js\";\nimport { ensureDir } from \"./paths.js\";\n\n/**\n * Read and parse a YAML file. Returns `null` when the file is missing OR when it\n * fails to parse (mirrors BaseAdapter.readJson's fail-soft behavior so a corrupt\n * file degrades to \"treat as absent\" rather than throwing mid-install).\n */\nexport function readYaml<T = Record<string, unknown>>(path: string): T | null {\n if (!existsSync(path)) return null;\n try {\n const parsed = parse(readFileSync(path, \"utf8\")) as T | null | undefined;\n // An empty YAML document parses to null/undefined — normalize to null.\n return parsed == null ? null : parsed;\n } catch {\n return null;\n }\n}\n\n/**\n * Serialize `data` to YAML and write it to `path`, creating parent dirs as\n * needed. Honors `dryRun` (no write). Mirrors BaseAdapter.writeJson's signature.\n */\nexport function writeYaml(path: string, data: unknown, dryRun = false): void {\n if (dryRun) return;\n ensureDir(dirname(path));\n writeFileSync(path, stringify(data), \"utf8\");\n}\n\n/**\n * An ObjectMapCodec over YAML for the core/object-map engine. Shared by the YAML\n * object-map server hosts (Goose's `extensions`, Hermes's `mcp_servers`) and any\n * future YAML host. `isPresentButUnparseable` is `() => false`: YAML server hosts\n * historically coerce/overwrite (readYaml(path) ?? {}) rather than warn-skip on an\n * unparseable file — preserved here for byte-identical behavior.\n */\nexport function yamlObjectMapCodec(): ObjectMapCodec {\n return {\n parse: (path) => readYaml<Record<string, unknown>>(path),\n serialize: (path, data, dryRun) => writeYaml(path, data, dryRun),\n isPresentButUnparseable: () => false,\n };\n}\n"],"mappings":";;;;;;AAcA,SAAS,YAAY,cAAc,qBAAqB;AACxD,SAAS,eAAe;AAExB,SAAS,OAAO,iBAAiB;AAU1B,SAAS,SAAsC,MAAwB;AAC5E,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,UAAM,SAAS,MAAM,aAAa,MAAM,MAAM,CAAC;AAE/C,WAAO,UAAU,OAAO,OAAO;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,UAAU,MAAc,MAAe,SAAS,OAAa;AAC3E,MAAI,OAAQ;AACZ,YAAU,QAAQ,IAAI,CAAC;AACvB,gBAAc,MAAM,UAAU,IAAI,GAAG,MAAM;AAC7C;AASO,SAAS,qBAAqC;AACnD,SAAO;AAAA,IACL,OAAO,CAAC,SAAS,SAAkC,IAAI;AAAA,IACvD,WAAW,CAAC,MAAM,MAAM,WAAW,UAAU,MAAM,MAAM,MAAM;AAAA,IAC/D,yBAAyB,MAAM;AAAA,EACjC;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/ids.ts","../src/core/paths.ts"],"sourcesContent":["/** Shared connector-id grammar for author-time configs and runtime ids. */\nexport const CONNECTOR_ID_RE = /^[a-z0-9][a-z0-9-]*$/;\n\nexport function isValidConnectorId(id: unknown): id is string {\n return typeof id === \"string\" && CONNECTOR_ID_RE.test(id);\n}\n\nexport function assertConnectorId(id: unknown, label = \"connector id\"): string {\n if (isValidConnectorId(id)) return id;\n throw new Error(\n `${label} must be kebab-case matching ${CONNECTOR_ID_RE} (got ${JSON.stringify(id)})`,\n );\n}\n","/**\n * core/paths — home data-root, stable home binary, and project identity.\n *\n * Operating model (docs/ARCHITECTURE.md §3):\n * • One home data-root holds the single binary + shared telemetry + state.\n * Override with AGENT_CONNECTOR_DATA_DIR (the cross-agent shared-DB key).\n * This relocates FRAMEWORK state only — never platform-native config files.\n * • The home binary lives at a STABLE path so every host's pointer config\n * keeps working across updates (no versioned cache dir → no cache-heal bug).\n * • Per-project data is keyed by a stable project identity (git remote ||\n * normalized absolute path), hashed — not by code location.\n */\n\nimport { createHash } from \"node:crypto\";\nimport {\n chmodSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { basename, isAbsolute, join, parse, relative, resolve, sep } from \"node:path\";\n\nimport { assertConnectorId } from \"./ids.js\";\n\n/** The built-in default framework data-root (`~/.agent-connector`), ignoring\n * any AGENT_CONNECTOR_DATA_DIR override. */\nexport function defaultDataRoot(): string {\n return join(homedir(), \".agent-connector\");\n}\n\n/** Framework data-root: AGENT_CONNECTOR_DATA_DIR || ~/.agent-connector. */\nexport function dataRoot(): string {\n const override = process.env.AGENT_CONNECTOR_DATA_DIR;\n if (override && override.trim() !== \"\") return resolve(override);\n return defaultDataRoot();\n}\n\n/**\n * True when `root` is a NON-DEFAULT data root — i.e. it differs from the\n * built-in `~/.agent-connector`. Compared after `resolve()` so two spellings of\n * the same dir read as equal. Used at install time to decide whether the\n * telemetry serve-wrap must carry an explicit `--data-dir` flag: a default-root\n * install relies on the (correct) fallback and stays flag-free, but an overridden\n * root must be passed explicitly because some hosts (codex) strip the child env,\n * so the spawned `serve` would otherwise resolve the default root and fail to\n * find the connector record written under the overridden one.\n */\nexport function isNonDefaultDataRoot(root: string): boolean {\n return resolve(root) !== resolve(defaultDataRoot());\n}\n\nexport function connectorsDir(): string {\n return join(dataRoot(), \"connectors\");\n}\n/**\n * Stable cache root for REMOTE connector sources fetched by `install <source>`\n * (one subdir per `<owner>__<repo>[__<ref>]`). Lives under the data-root — NOT a\n * temp dir — so a fetched connector whose server is a LOCAL stdio command\n * (`node <path-in-the-clone>`) keeps resolving after install across reboots.\n */\nexport function sourcesDir(): string {\n return join(dataRoot(), \"sources\");\n}\nexport function connectorDir(id: string): string {\n const root = resolve(connectorsDir());\n const safeId = assertConnectorId(id);\n const dir = resolve(root, safeId);\n const rel = relative(root, dir);\n if (rel === \"\" || rel.startsWith(\"..\") || isAbsolute(rel)) {\n throw new Error(`connector record path escapes ${root}: ${id}`);\n }\n return dir;\n}\nexport function backupsDir(): string {\n return join(dataRoot(), \"backups\");\n}\nexport function logsDir(): string {\n return join(dataRoot(), \"logs\");\n}\n\n/** Telemetry store path for the selected backend. */\nexport function telemetryPath(store: \"ndjson\" | \"sqlite\" = \"ndjson\"): string {\n return join(dataRoot(), store === \"sqlite\" ? \"telemetry.db\" : \"telemetry.ndjson\");\n}\n\n/** Stable, OS-correct path to the single home binary pointer. */\nexport function homeBinPath(): string {\n const name = process.platform === \"win32\" ? \"agent-connector.cmd\" : \"agent-connector\";\n return join(dataRoot(), \"bin\", name);\n}\n\nexport function ensureDir(dir: string): void {\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n}\n\n/**\n * Return the first existing symbolic-link component in `path`, or null when the\n * existing prefix is symlink-free. Stops at the first missing component so new\n * directories/files can still be created normally.\n */\nexport function firstSymlinkInPath(path: string): string | null {\n const abs = resolve(path);\n const root = parse(abs).root;\n const rest = relative(root, abs);\n if (rest === \"\") return null;\n\n let cur = root;\n for (const part of rest.split(sep)) {\n if (part === \"\") continue;\n cur = join(cur, part);\n try {\n if (lstatSync(cur).isSymbolicLink()) return cur;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") return null;\n throw err;\n }\n }\n return null;\n}\n\nexport function assertNoSymlinkInPath(path: string): void {\n const symlink = firstSymlinkInPath(path);\n if (symlink !== null) {\n throw new Error(`${basename(path)} skipped: symbolic link in path (${symlink})`);\n }\n}\n\n/**\n * Write a stable launcher at homeBinPath() that execs the real CLI entry.\n * Avoids symlinks (Windows constraint) and versioned paths. Idempotent.\n *\n * @param cliEntry absolute path to the installed cli.js\n * @param nodePath node runtime to exec with (defaults to current process.execPath)\n * @returns the stable launcher path\n */\nexport function ensureHomeBin(cliEntry: string, nodePath: string = process.execPath): string {\n const binPath = homeBinPath();\n assertNoSymlinkInPath(binPath);\n ensureDir(join(dataRoot(), \"bin\"));\n const node = nodePath.replace(/\\\\/g, \"/\");\n const cli = cliEntry.replace(/\\\\/g, \"/\");\n if (process.platform === \"win32\") {\n writeFileSync(binPath, `@echo off\\r\\n\"${node}\" \"${cli}\" %*\\r\\n`, \"utf8\");\n } else {\n writeFileSync(binPath, `#!/bin/sh\\nexec \"${node}\" \"${cli}\" \"$@\"\\n`, \"utf8\");\n chmodSync(binPath, 0o755);\n }\n return binPath;\n}\n\n/** Read remote.origin.url from a project's .git/config without spawning git. */\nfunction gitRemoteUrl(projectDir: string): string | null {\n const cfg = join(projectDir, \".git\", \"config\");\n if (!existsSync(cfg)) return null;\n try {\n const text = readFileSync(cfg, \"utf8\");\n // Find [remote \"origin\"] ... url = X\n const lines = text.split(/\\r?\\n/);\n let inOrigin = false;\n for (const line of lines) {\n const s = line.trim();\n if (s.startsWith(\"[\")) inOrigin = /^\\[remote\\s+\"origin\"\\]$/.test(s);\n else if (inOrigin) {\n const m = s.match(/^url\\s*=\\s*(.+)$/);\n if (m && m[1]) return m[1].trim();\n }\n }\n } catch {\n /* ignore */\n }\n return null;\n}\n\nexport interface ProjectIdentity {\n /** Hashed stable key used to partition telemetry/state. */\n key: string;\n /** Human-readable label (git remote or directory name). */\n label: string;\n /** Absolute project directory. */\n dir: string;\n}\n\n/**\n * Derive a stable project identity. Prefers the git origin URL (stable across\n * clones/locations); falls back to the normalized absolute path.\n */\nexport function projectIdentity(projectDir: string): ProjectIdentity {\n const dir = resolve(projectDir);\n const remote = gitRemoteUrl(dir);\n const basis = remote ?? dir;\n const key = createHash(\"sha256\").update(basis).digest(\"hex\").slice(0, 16);\n return { key, label: remote ?? basename(dir), dir };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/app.ts","../src/cli/banner.ts"],"sourcesContent":["/**\n * cli/app — shared CLI helpers + command dispatch (NO side effects).\n *\n * Command modules import the shared helpers (print, fail, parse-flags,\n * renderInstallResult) from here, and main() dispatches the first positional to a\n * command module.\n * This module never auto-runs — `cli/index.ts` is the thin bin entry that calls\n * `main()`. Keeping the auto-run out of here is what prevents a dispatch recursion\n * (a command module importing a helper must not re-trigger the program) and makes\n * it robust to bundler entry-splitting (the old import.meta.url entry-guard broke\n * once tsup hoisted this code into a shared chunk).\n */\n\nimport { createRequire } from \"node:module\";\nimport { homedir } from \"node:os\";\n\nimport { PALETTES, renderBrandBanner, resolveColorMode, shouldShowBanner } from \"./banner.js\";\nimport type {\n ChangeRecord,\n ConnectorSummary,\n InstallResult,\n InstallScope,\n PlatformId,\n ResolvedConnector,\n} from \"../core/types.js\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// Shared helpers (imported by the command modules)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Print a line to stdout (machine-readable payloads must stay on stdout). */\nexport function print(line: string): void {\n process.stdout.write(`${line}\\n`);\n}\n\n/**\n * The brand active for THIS invocation. main() sets it from\n * {@link MainOptions.programName} before dispatch, so error messages printed by\n * command modules via {@link fail} read as the embedding tool (e.g. `acme-db:`)\n * instead of always `agent-connector:`.\n */\nlet activeProgramName = \"agent-connector\";\n\n/** Print an error to stderr (branded) and return a non-zero exit code (default 2). */\nexport function fail(message: string, code = 2): number {\n process.stderr.write(`${activeProgramName}: ${message}\\n`);\n return code;\n}\n\n/**\n * The brand active for THIS invocation (what the banner renders in ASCII art).\n * main() sets it from {@link MainOptions.programName} before dispatch; reading it\n * here keeps the banner branded for an embedding SDK CLI without each command\n * module needing the program name threaded through.\n */\nexport function getActiveProgramName(): string {\n return activeProgramName;\n}\n\n/**\n * Print the branded ASCII banner once, atop install / uninstall / upgrade /\n * doctor — but ONLY for an interactive TTY (suppressed when piped/redirected,\n * under --json, or under --quiet) so scripts and pipelines see byte-identical\n * command output. The pure render lives in cli/banner; this thin wrapper reads\n * the real stdout/env and feeds it in.\n */\nexport function maybePrintBanner(flags: { json?: boolean; quiet?: boolean }): void {\n const isTTY = process.stdout.isTTY === true;\n const noColor = process.env.NO_COLOR != null && process.env.NO_COLOR !== \"\";\n if (!shouldShowBanner({ isTTY, noColor, json: flags.json === true, quiet: flags.quiet === true })) {\n return;\n }\n const color = resolveColorMode({\n noColor,\n colorterm: process.env.COLORTERM,\n term: process.env.TERM,\n });\n // Coerce an unknown/zero width to a conservative 80 so an unknown-width TTY\n // shows the safe one-line title rather than the 129-col block art.\n const c = process.stdout.columns;\n const columns = typeof c === \"number\" && c > 0 ? c : 80;\n // Rotate the gradient per run so each real invocation reads differently. The\n // random pick lives HERE (the impure wrapper) so the pure renderer stays\n // deterministic for tests/direct callers.\n const palette = PALETTES[Math.floor(Math.random() * PALETTES.length)]!;\n print(renderBrandBanner(activeProgramName, { color, columns, palette }));\n print(\"\");\n}\n\n/**\n * Resolve agent-connector's own package version at runtime. Works from both the\n * bundled dist layout (dist/*.js → ../package.json) and the src layout under\n * tsx/vitest (src/cli/ → ../../package.json); the name check guards against\n * accidentally reading some other package.json on the walk.\n */\nexport function resolveOwnVersion(): string {\n const req = createRequire(import.meta.url);\n for (const rel of [\"../package.json\", \"../../package.json\", \"../../../package.json\"]) {\n try {\n const pkg = req(rel) as { name?: string; version?: string };\n if (pkg.name === \"@ken-jo/agent-connector\" && typeof pkg.version === \"string\") {\n return pkg.version;\n }\n } catch {\n /* keep walking */\n }\n }\n return \"0.0.0\";\n}\n\n/** Parse a --scope value the CLI accepts (user|project) into an InstallScope. */\nexport function parseScope(value: string | undefined): InstallScope | null {\n if (value === \"user\" || value === \"project\") return value;\n return null;\n}\n\n/**\n * Parse a comma-separated --targets value into PlatformId[]. Returns undefined\n * when the flag is absent/empty so callers can fall back to connector/detection.\n */\nexport function parseTargets(value: string | undefined): PlatformId[] | undefined {\n if (value == null || value.trim() === \"\") return undefined;\n const ids = value\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s !== \"\") as PlatformId[];\n return ids.length > 0 ? ids : undefined;\n}\n\n/**\n * Build the render-only {@link ConnectorSummary} from a resolved connector. The\n * command modules call this on the {@link ResolvedConnector} they already loaded\n * and attach the result to {@link InstallResult.connector}, so the friendly\n * renderer can print a header describing WHAT was deployed without importing the\n * heavyweight connector type or its live handlers.\n */\nexport function buildConnectorSummary(connector: ResolvedConnector): ConnectorSummary {\n let server: ConnectorSummary[\"server\"];\n if (connector.server) {\n const s = connector.server;\n // stdio → \"node server.mjs\"; remote → the URL (best-effort one-liner).\n // HOME-relativize each part so a bundled absolute server path reads short.\n const cmd =\n s.transport === \"stdio\"\n ? [s.command, ...(s.args ?? [])]\n .filter((p): p is string => p != null && p !== \"\")\n .map((p) => tildify(p))\n .join(\" \")\n : (s.url ?? \"\");\n server = { transport: s.transport, command: cmd };\n }\n return {\n id: connector.id,\n version: connector.version,\n displayName: connector.displayName,\n server,\n hookEvents: [...connector.hookEvents],\n telemetryEnabled: connector.telemetry.enabled,\n commands: connector.commands.length,\n skills: connector.skills.length,\n subagents: connector.subagents.length,\n memory: connector.memory.length,\n hasStatusline: connector.statusline != null,\n actions: connector.actions.length,\n };\n}\n\n/**\n * Rewrite an absolute path under the user's home dir to a leading `~/…` so the\n * per-host paths read short. Leaves non-home and relative paths untouched.\n */\nfunction tildify(path: string): string {\n // Normalize both to forward slashes so the home-prefix check is separator-\n // robust (Windows mixes `\\` and `/`) and the displayed path reads `~/…`\n // consistently on every platform (Windows terminals accept `/` fine).\n const home = homedir().replace(/\\\\/g, \"/\");\n const np = path.replace(/\\\\/g, \"/\");\n if (home && (np === home || np.startsWith(home + \"/\"))) {\n return \"~\" + np.slice(home.length);\n }\n return np;\n}\n\n/** Truncate a long single-line command for the header (keeps it scannable). */\nfunction truncateCommand(cmd: string, max = 64): string {\n if (cmd.length <= max) return cmd;\n return cmd.slice(0, max - 1) + \"…\";\n}\n\n/**\n * The surface a ChangeRecord touched, derived from its detail (primary signal)\n * then its path (fallback for content files whose detail is a bare basename).\n */\ntype Surface = \"mcp\" | \"hooks\" | \"commands\" | \"skills\" | \"subagents\" | \"memory\" | \"statusline\" | \"actions\" | \"backup\" | \"other\";\n\nfunction classifySurface(c: ChangeRecord): Surface {\n const detail = c.detail.toLowerCase();\n // A safety backup the installer wrote before a destructive change.\n if (detail.startsWith(\"backed up\") || detail.includes(\"backup\")) return \"backup\";\n // MCP server entry: mcpServers.<id> / mcp_servers.<id> / mcp.servers.<id>.\n if (/^mcp[._]?servers?\\./.test(detail) || detail.startsWith(\"mcp.\")) return \"mcp\";\n // Normalized + native hook entries: hooks.<Event> (case varies per host).\n if (detail.startsWith(\"hooks.\") || detail.startsWith(\"hook \")) return \"hooks\";\n if (detail.includes(\"nativehooks\")) return \"hooks\";\n if (detail.startsWith(\"memory\")) return \"memory\";\n if (detail.startsWith(\"statusline\")) return \"statusline\";\n if (detail.startsWith(\"actions\") || detail.startsWith(\"action \")) return \"actions\";\n if (detail.includes(\"configpatch\")) return \"other\";\n // Path-based fallback for content-file surfaces (detail is a bare basename).\n const p = (c.path ?? \"\").replace(/\\\\/g, \"/\").toLowerCase();\n if (/\\/commands?\\//.test(p)) return \"commands\";\n if (/\\/skills?\\//.test(p)) return \"skills\";\n if (/\\/(subagents?|agents?)\\//.test(p)) return \"subagents\";\n // Detail mentions a surface noun (skip/warn lines like \"no skills\").\n if (/\\bcommands?\\b/.test(detail)) return \"commands\";\n if (/\\bskills?\\b/.test(detail)) return \"skills\";\n if (/\\bsubagents?\\b/.test(detail)) return \"subagents\";\n return \"other\";\n}\n\nconst SURFACE_LABEL_SINGULAR: Record<Surface, string> = {\n mcp: \"MCP server\",\n hooks: \"hook\",\n commands: \"command\",\n skills: \"skill\",\n subagents: \"subagent\",\n memory: \"memory block\",\n statusline: \"status line\",\n actions: \"action\",\n backup: \"backup\",\n other: \"change\",\n};\n\n/** Pluralize a surface count, e.g. (2,\"hooks\") → \"2 hooks\". */\nfunction countLabel(n: number, surface: Surface): string {\n const base = SURFACE_LABEL_SINGULAR[surface];\n if (surface === \"mcp\") return \"MCP server\"; // singular by construction\n return n === 1 ? `1 ${base}` : `${n} ${base}s`;\n}\n\n/** Verb-appropriate glyph for a host's NET status. */\nfunction hostGlyph(status: \"ok\" | \"skip\" | \"warn\"): string {\n return status === \"warn\" ? \"!\" : status === \"skip\" ? \"=\" : \"✓\";\n}\n\n/**\n * One host's collapsed view: its net status, a surface summary line, the\n * deduped HOME-relativized paths it touched, and any inline warnings.\n */\ninterface HostGroup {\n platform: PlatformId;\n status: \"ok\" | \"skip\" | \"warn\";\n summary: string;\n paths: string[];\n warnings: string[];\n}\n\n/**\n * Collapse the per-(host,surface) ChangeRecords into ONE entry per host:\n * - status: warn if any record warned; ok if any created/updated/removed;\n * else skip (everything was a no-op).\n * - summary: the active (non-skip) surfaces, joined (\"MCP server + 2 hooks\").\n * When every record skipped, the reason is surfaced instead.\n */\nfunction groupByHost(changes: ChangeRecord[], verb: Verb): HostGroup[] {\n const order: PlatformId[] = [];\n const byHost = new Map<PlatformId, ChangeRecord[]>();\n for (const c of changes) {\n if (!byHost.has(c.platform)) {\n byHost.set(c.platform, []);\n order.push(c.platform);\n }\n byHost.get(c.platform)!.push(c);\n }\n\n const groups: HostGroup[] = [];\n for (const platform of order) {\n const recs = byHost.get(platform)!;\n // A safety backup the installer wrote is INCIDENTAL, not an installed\n // surface: it must not flip an otherwise-no-op host to \"changed\" (an\n // idempotent re-install that only re-backs-up should still read \"already\n // current\"). It is shown only ALONGSIDE a real surface change.\n const written = recs.filter((c) => c.action !== \"skip\" && c.action !== \"warn\");\n const realChanges = written.filter((c) => classifySurface(c) !== \"backup\");\n const warned = recs.filter((c) => c.action === \"warn\");\n const status: HostGroup[\"status\"] =\n warned.length > 0 ? \"warn\" : realChanges.length > 0 ? \"ok\" : \"skip\";\n\n // Surface summary from the written records (created/updated/removed),\n // preserving a stable surface order and collapsing per-surface counts. The\n // incidental `backup` surface is included ONLY when a real surface changed.\n const SURFACE_ORDER: Surface[] = [\n \"mcp\",\n \"hooks\",\n \"commands\",\n \"skills\",\n \"subagents\",\n \"memory\",\n \"statusline\",\n \"actions\",\n \"backup\",\n \"other\",\n ];\n const counts = new Map<Surface, number>();\n const summarySource = realChanges.length > 0 ? written : realChanges;\n for (const c of summarySource) {\n const s = classifySurface(c);\n counts.set(s, (counts.get(s) ?? 0) + 1);\n }\n const parts = SURFACE_ORDER.filter((s) => counts.has(s)).map((s) =>\n countLabel(counts.get(s)!, s),\n );\n\n let summary: string;\n if (parts.length > 0) {\n summary = parts.join(\" + \");\n } else if (status === \"warn\") {\n // No real surface change but a warning — the warning line carries detail.\n summary = \"\";\n } else {\n // Nothing real changed: surface a skip reason (or note the incidental\n // backup) for context.\n const reason =\n recs.find((c) => c.action === \"skip\")?.detail ??\n (written.length > 0 ? \"already current (re-backed-up only)\" : \"no changes\");\n summary = verb === \"uninstall\" ? `nothing to remove (${reason})` : `unchanged (${reason})`;\n }\n\n const paths = [\n ...new Set(\n recs\n .filter((c) => c.path != null && c.path !== \"\")\n .map((c) => tildify(c.path!)),\n ),\n ];\n const warnings = warned.map((c) => c.detail);\n\n groups.push({ platform, status, summary, paths, warnings });\n }\n return groups;\n}\n\ntype Verb = \"install\" | \"upgrade\" | \"uninstall\";\n\n/**\n * Render an InstallResult as a friendly, grouped report (the DEFAULT output):\n * 1. a connector header (id/version/displayName + server/hooks/telemetry +\n * one line per shipped content surface), when the summary is threaded in;\n * 2. one line per host (net glyph + surface summary + deduped ~/… paths),\n * with any host warnings inline beneath it;\n * 3. a friendly closing line + restart hint + doctor/uninstall next steps.\n *\n * Used by install / upgrade / uninstall. The exit-non-zero-on-warn convention\n * is enforced by the callers (this function only renders).\n */\nexport function renderInstallResult(result: InstallResult, verb: Verb): string {\n const lines: string[] = [];\n const summary = result.connector;\n\n // ── 1. Connector header ───────────────────────────────────────────────────\n if (summary) {\n const head = [`${summary.id} v${summary.version}`];\n if (summary.displayName && summary.displayName !== summary.id) {\n head.push(summary.displayName);\n }\n lines.push(head.join(\" · \"));\n\n if (summary.server) {\n lines.push(\n ` server ${summary.server.transport} · ${truncateCommand(summary.server.command)}`,\n );\n } else {\n lines.push(\" server (hooks-only)\");\n }\n if (summary.hookEvents.length > 0) {\n lines.push(` hooks ${summary.hookEvents.join(\", \")}`);\n }\n lines.push(` telemetry ${summary.telemetryEnabled ? \"on\" : \"off\"}`);\n if (summary.commands > 0)\n lines.push(` commands ${summary.commands}`);\n if (summary.skills > 0) lines.push(` skills ${summary.skills}`);\n if (summary.subagents > 0)\n lines.push(` subagents ${summary.subagents}`);\n if (summary.memory > 0) lines.push(` memory ${summary.memory}`);\n if (summary.hasStatusline) lines.push(` statusline 1`);\n if (summary.actions > 0) lines.push(` actions ${summary.actions}`);\n lines.push(\"\");\n } else {\n // No summary threaded (back-compat): keep a minimal verb header.\n const mode = result.dryRun ? \" (dry-run — nothing written)\" : \"\";\n lines.push(`${verb} \"${result.connectorId}\"${mode}`);\n }\n\n // ── 2. Per-host grouping ──────────────────────────────────────────────────\n const groups = groupByHost(result.changes, verb);\n if (groups.length === 0) {\n lines.push(\"(no changes)\");\n } else {\n const verbed = verb === \"uninstall\" ? \"removed from\" : verb === \"upgrade\" ? \"synced to\" : \"detected\";\n lines.push(`→ ${groups.length} agent ${groups.length === 1 ? \"CLI\" : \"CLIs\"} ${verbed}:`);\n // Width-align the platform column for a scannable table; the summary column\n // gets a fixed min width but always keeps a 2-space gap before the paths\n // even when an unusually long summary overflows it.\n const pad = Math.max(...groups.map((g) => g.platform.length));\n for (const g of groups) {\n const glyph = hostGlyph(g.status);\n const name = g.platform.padEnd(pad);\n const pathsCol = g.paths.join(\", \");\n const gap = pathsCol === \"\" ? \"\" : \" \";\n const summaryCol = pathsCol === \"\" ? g.summary : g.summary.padEnd(26) + gap;\n lines.push(` ${glyph} ${name} ${summaryCol}${pathsCol}`.trimEnd());\n for (const w of g.warnings) lines.push(` ! ${w}`);\n }\n }\n\n // ── 3. Closing summary + next steps ───────────────────────────────────────\n // The headline file tally counts only REAL surface writes — an incidental\n // safety backup must not masquerade as an installed file, so a re-install\n // whose only write was a backup honestly reports 0 files / \"already current\".\n const tally = result.changes.reduce(\n (acc, c) => {\n const isWrite = c.action === \"create\" || c.action === \"update\" || c.action === \"remove\";\n if (isWrite && classifySurface(c) !== \"backup\") acc.files += 1;\n if (c.action === \"warn\") acc.warns += 1;\n return acc;\n },\n { files: 0, warns: 0 },\n );\n const cliCount = groups.length;\n const cliWord = cliCount === 1 ? \"CLI\" : \"CLIs\";\n const fileWord = tally.files === 1 ? \"file\" : \"files\";\n\n lines.push(\"\");\n if (verb === \"uninstall\") {\n if (result.dryRun) {\n lines.push(`✓ Would remove ${summary?.id ?? result.connectorId} from ${cliCount} ${cliWord} · ${tally.files} ${fileWord} (dry-run — nothing written).`);\n } else if (tally.files === 0 && tally.warns === 0) {\n lines.push(`✓ Nothing to remove — ${summary?.id ?? result.connectorId} was not installed (zero residue).`);\n } else {\n lines.push(`✓ Removed ${summary?.id ?? result.connectorId} from ${cliCount} ${cliWord} · ${tally.files} ${fileWord} cleaned${tally.warns === 0 ? \" (zero residue)\" : \"\"}.`);\n }\n } else {\n const what = verb === \"upgrade\" ? \"Synced\" : \"Installed\";\n const wouldWhat = verb === \"upgrade\" ? \"sync\" : \"install\";\n if (result.dryRun) {\n lines.push(`✓ Would ${wouldWhat} ${summary?.id ?? result.connectorId} to ${cliCount} ${cliWord} · ${tally.files} ${fileWord} (dry-run — nothing written).`);\n } else if (tally.files === 0) {\n lines.push(`✓ ${summary?.id ?? result.connectorId}: already current on ${cliCount} ${cliWord} — nothing to write.`);\n } else {\n lines.push(`✓ ${what} ${summary?.id ?? result.connectorId} to ${cliCount} ${cliWord} · ${tally.files} ${fileWord}. Restart each CLI to load it.`);\n }\n }\n\n // Warnings block (kept explicit — callers exit non-zero on any warn). A\n // remote-transport connector pushes the SAME string to both a warn\n // ChangeRecord (already shown inline on its host line) and result.warnings, so\n // skip any bottom-block entry already surfaced inline to show each warning ONCE.\n const inlineWarnings = new Set(groups.flatMap((g) => g.warnings));\n const blockWarnings = result.warnings.filter((w) => !inlineWarnings.has(w));\n if (blockWarnings.length > 0) {\n lines.push(\"\");\n lines.push(\"warnings:\");\n for (const w of blockWarnings) lines.push(` ! ${w}`);\n }\n\n // Next-step hints (the verbs read as the active brand).\n if (verb === \"uninstall\") {\n lines.push(` Verify it's gone: ${activeProgramName} doctor`);\n } else {\n lines.push(\n ` Verify: ${activeProgramName} doctor · Remove: ${activeProgramName} uninstall`,\n );\n }\n\n return lines.join(\"\\n\");\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Command dispatch\n// ─────────────────────────────────────────────────────────────────────────\n\n/** A command module: takes the post-command argv slice, returns an exit code. */\ntype CommandModule = { run: (argv: string[]) => Promise<number> | number };\n\n/** Lazy command loaders — keyed by the first positional. Imported on demand. */\nconst COMMANDS: Record<string, () => Promise<CommandModule>> = {\n detect: () => import(\"./commands/detect.js\"),\n install: () => import(\"./commands/install.js\"),\n uninstall: () => import(\"./commands/uninstall.js\"),\n // `upgrade` is the single \"bring everything current\" verb (re-render host\n // config + refresh the home pointer + managed channel guidance). `sync` and\n // `update` are kept as back-compat aliases that route to the same module so\n // existing scripts/docs keep working without a second concept to learn.\n upgrade: () => import(\"./commands/upgrade.js\"),\n sync: () => import(\"./commands/upgrade.js\"),\n update: () => import(\"./commands/upgrade.js\"),\n package: () => import(\"./commands/package.js\"),\n doctor: () => import(\"./commands/doctor.js\"),\n status: () => import(\"./commands/status.js\"),\n telemetry: () => import(\"./commands/telemetry.js\"),\n usage: () => import(\"./commands/usage.js\"),\n leaderboard: () => import(\"./commands/leaderboard.js\"),\n hook: () => import(\"./commands/hook.js\"),\n // Internal (omitted from USAGE): the statusline (HUD) entrypoint a host's\n // status line config points at. Fail-safe: always exits 0 with at most a\n // rendered line. See cli/commands/statusline.ts.\n statusline: () => import(\"./commands/statusline.js\"),\n // Internal (omitted from USAGE): the user-invokable action entrypoint a future\n // host affordance points at. USER-TRIGGERED: surfaces errors (exit 1 + stderr),\n // unlike the fail-open hook / fail-safe statusline. See cli/commands/action.ts.\n action: () => import(\"./commands/action.js\"),\n serve: () => import(\"./commands/serve.js\"),\n // Hidden (omitted from USAGE): the opt-in host-native turn-usage entrypoint an\n // AfterModel / PostInvocation hook points at. Always exits 0; records a\n // distinct `model_turn` row. See cli/commands/usage-event.ts.\n \"usage-event\": () => import(\"./commands/usage-event.js\"),\n};\n\n/** Default program name; an embedding SDK CLI overrides it via {@link MainOptions}. */\nexport const DEFAULT_PROGRAM_NAME = \"agent-connector\";\n\n/**\n * One-line usage signature per command — the single central copy behind\n * `<command> --help` and the friendly bad-flag error. Command modules use\n * node:util parseArgs (strict), which THROWS on an unknown flag; without the\n * dispatcher-level handling in {@link main}, `agent-connector install --help`\n * would die with a raw ERR_PARSE_ARGS stack trace — the very invocation the\n * root help tells users to run.\n */\nconst COMMAND_USAGE: Record<string, string> = {\n detect: \"detect [--json] [--project <dir>]\",\n install:\n \"install [<source>] [--method direct|marketplace] [--connector <source>] [--scope user|project] [--targets a,b] [--project <dir>] [--dry-run] [--force] [--quiet]\\n\" +\n \" <source> a local path OR a remote GitHub connector: owner/repo[/subpath][#ref], a github.com URL (incl. /tree/<ref>/<sub>), git@github.com:owner/repo, or github:owner/repo. Remote sources are fetched to ~/.agent-connector/sources/ and must be an agent-connector package (have an agent-connector.config.{mjs,js,json}).\",\n uninstall:\n \"uninstall [--method auto|direct|marketplace] [--connector <path>] [--connector-id <id>] [--scope user|project] [--targets a,b] [--project <dir>] [--dry-run] [--purge] [--quiet]\",\n upgrade:\n \"upgrade [--method direct|marketplace] [--channel stable|latest] [--connector <path>] [--scope user|project] [--targets a,b] [--project <dir>] [--dry-run] [--quiet]\",\n sync: \"sync — alias of upgrade (see `upgrade --help`)\",\n update: \"update — alias of upgrade (see `upgrade --help`)\",\n package:\n \"package [--connector <path>] [--format <fmt>|all] [--out <dir>] [--project <dir>] [--dry-run]\",\n doctor:\n \"doctor [--targets a,b] [--connector <path>] [--scope user|project] [--project <dir>] [--probe] [--explain] [--json] [--heal] [--dry-run] [--quiet]\\n\" +\n \" --explain per-(host,event) hook honor matrix (honored/degraded/dropped). Exit non-zero ONLY when an explicitly-targeted host DEGRADES a declared event (fires it but silently won't honor the reply); dropped/mcp-only hosts and fleet-wide (targets:auto) gaps are informational (exit 0).\",\n status: \"status [--connector <path>] [--scope user|project] [--project <dir>] [--json]\",\n telemetry:\n \"telemetry report|export|leaderboard [--by <dim>] [--since <dur>] [--connector <id>] [--scope <slice>] [--format csv|json] [--out <file>] [--json]\",\n usage:\n \"usage report|export|leaderboard [--by <dim>] [--since <dur>] [--platform <id>] [--format csv|json] [--out <file>] [--json]\",\n leaderboard: \"leaderboard [--since <dur>] [--connector <id>] [--scope <slice>] [--json]\",\n hook: \"hook <platform> <event> --connector <id> (internal — host hook configs point here)\",\n statusline:\n \"statusline <platform> --connector <id> (internal — host status line configs point here)\",\n action:\n \"action <platform> <actionId> --connector <id> (internal — host action affordances point here)\",\n serve:\n \"serve --connector <id> [--scope user|project] [--host <platformId>] -- <command> [args...] (internal — host MCP entries point here)\",\n};\n\n/**\n * Build the top-level usage string for a given program name. The brand replaces\n * \"agent-connector\" in the title, the `usage:` line, and the per-command help\n * footer so an embedded CLI (e.g. `acme-db`) reads as its own tool.\n */\nfunction buildUsage(programName: string): string {\n return `${programName} — write your MCP server + hooks once, install everywhere.\n\nusage: ${programName} <command> [flags]\n\ncommands:\n detect List the AI-agent platforms installed on this machine.\n install Deploy a connector across its target platforms (--method direct writes host config; --method marketplace drives the host's plugin install).\n uninstall Remove a connector's registrations (--method auto reverses whichever method is actually installed).\n upgrade Bring everything current: re-render host config + heal the home pointer + managed update guidance (alias: update, sync).\n package Emit a marketplace/extension bundle (10 host formats, or the standard artifacts mcp-server-json | mcpb).\n doctor Health-check every detected platform; non-zero exit on any failure.\n status Light install-state summary: which connectors are present on which hosts (always exits 0).\n telemetry Inspect local per-tool token telemetry (report | export | leaderboard).\n usage Inspect host-native token usage from agent CLI logs (report | export | leaderboard).\n leaderboard Three leaderboards: 🔌 MCP/plugin (mcp-self) + 🖥️ host/user (host-scan-logs) + 🛰️ host-native turns (host-native-live) — never summed.\n hook Universal json-stdio hook entrypoint (hosts call this).\n serve Telemetry-wrapping MCP stdio proxy (wraps a real server command).\n\nRun \\`${programName} <command> --help\\` for command-specific flags.`;\n}\n\n/** Options accepted by {@link main}. */\nexport interface MainOptions {\n /**\n * The brand shown in usage/help text. Defaults to {@link DEFAULT_PROGRAM_NAME}.\n * An embedding SDK CLI (see cli/sdk.ts) passes its own bin name so every\n * subcommand's help reads as the developer's tool.\n */\n programName?: string;\n /**\n * Version shown by --version. Defaults to agent-connector's own package version.\n * Embedded branded CLIs pass their package.json version.\n */\n programVersion?: string;\n}\n\nexport async function main(argv: string[], opts: MainOptions = {}): Promise<number> {\n const programName = opts.programName ?? DEFAULT_PROGRAM_NAME;\n activeProgramName = programName; // brand every fail() in this invocation\n const usage = buildUsage(programName);\n const command = argv[0];\n\n if (command == null || command === \"--help\" || command === \"-h\" || command === \"help\") {\n print(usage);\n return command == null ? 1 : 0;\n }\n if (command === \"--version\" || command === \"-v\") {\n print(`${programName} ${opts.programVersion ?? resolveOwnVersion()}`);\n return 0;\n }\n\n const loader = COMMANDS[command];\n if (!loader) {\n process.stderr.write(`${programName}: unknown command \"${command}\"\\n\\n`);\n process.stderr.write(`${usage}\\n`);\n return 2;\n }\n\n // Per-command help: no command module defines a --help flag (strict parseArgs\n // would throw on it), so answer it centrally from COMMAND_USAGE.\n const rest = argv.slice(1);\n if (rest.includes(\"--help\") || rest.includes(\"-h\")) {\n const sig = COMMAND_USAGE[command];\n if (sig) {\n print(`usage: ${programName} ${sig}`);\n return 0;\n }\n }\n\n const mod = await loader();\n try {\n return await mod.run(rest);\n } catch (err) {\n // Friendly bad-flag errors: strict parseArgs throws ERR_PARSE_ARGS_* for an\n // unknown/malformed option — print the message + the usage line instead of\n // letting a raw stack trace reach the user. Everything else still rethrows\n // (the bin entry reports those as fatal).\n const code = (err as { code?: string }).code;\n if (typeof code === \"string\" && code.startsWith(\"ERR_PARSE_ARGS\")) {\n const message = err instanceof Error ? err.message : String(err);\n const sig = COMMAND_USAGE[command];\n if (sig) process.stderr.write(`usage: ${programName} ${sig}\\n`);\n return fail(message);\n }\n throw err;\n }\n}\n","/**\n * cli/banner — the branded ASCII-art header shown once atop install / uninstall\n * / upgrade / doctor.\n *\n * Two PURE functions drive everything (no process.* reads here — the command\n * wiring injects the real env so tests can call these directly without a TTY):\n *\n * renderBrandBanner(name, { color, columns, palette }) → the banner string\n * shouldShowBanner({ isTTY, noColor, json, quiet }) → whether to show it\n *\n * RESPONSIVE (one line, NEVER stacked), two forms:\n * • WIDE — a 6-row \"ANSI Shadow\" block font (solid `█` + `╗╔╝╚║═` shadow,\n * ~8 cols/char, hand-transcribed + fixed-width so it tiles cleanly) when its\n * render fits the terminal width.\n * • NARROW — a single-line gradient TITLE `◆ AGENT-CONNECTOR` (the brand\n * uppercased, each char colored along the palette + bold). ~18 cols, so it\n * fits any real terminal; it is the universal narrow form.\n * The WIDE font covers the connector-id charset [A-Za-z0-9 ._-].\n *\n * COLOR: a palette gradient (three RGB stops, a {@link Palette}) — swept per\n * column across the block art, or per character across the title. The render is\n * PURE and palette-parameterized — the caller picks the palette (the impure\n * app.ts wrapper rotates {@link PALETTES} per run). truecolor→256→16→none tiers\n * all honor the palette; NO_COLOR = zero ANSI.\n */\n\n/** Color capability the host terminal advertises, highest → lowest. */\nexport type ColorMode = \"truecolor\" | \"ansi256\" | \"ansi16\" | \"none\";\n\n/** A 24-bit RGB triple. */\nexport type RGB = readonly [number, number, number];\n\n/** A 3-stop gradient: the sweep lerps stop0 → stop1 → stop2 across the columns. */\nexport type Palette = readonly [RGB, RGB, RGB];\n\n/**\n * The 10 rotation palettes. The impure {@link maybePrintBanner} wrapper picks one\n * at random per run so each real invocation shows a different color; the pure\n * renderer defaults to PALETTES[0] so direct callers + tests stay deterministic.\n */\nexport const PALETTES: readonly Palette[] = [\n [[255, 210, 0], [255, 110, 10], [220, 30, 25]], // Sunset\n [[255, 225, 90], [255, 120, 0], [200, 20, 20]], // Lava\n [[230, 30, 30], [255, 120, 0], [255, 220, 0]], // Fire\n [[255, 225, 120], [255, 170, 40], [225, 110, 20]], // Amber gold\n [[255, 205, 150], [255, 150, 130], [255, 120, 175]], // Peach pastel\n [[255, 80, 170], [200, 40, 200], [100, 60, 210]], // Berry\n [[0, 220, 180], [0, 160, 230], [60, 90, 230]], // Ocean\n [[80, 230, 140], [0, 200, 210], [150, 90, 235]], // Aurora\n [[170, 230, 90], [60, 200, 120], [20, 150, 140]], // Forest\n [[0, 200, 200], [40, 80, 230], [200, 40, 200]], // Synthwave\n] as const;\n\n/** Inputs to {@link renderBrandBanner} — all injected, never read from process. */\nexport interface BannerOptions {\n /** Color capability to render at. \"none\" emits zero ANSI codes. */\n color: ColorMode;\n /** Terminal width in columns; selects the form (wide block art → one-line title). */\n columns: number;\n /** The 3-stop gradient. Defaults to {@link PALETTES}[0] (Sunset) when omitted. */\n palette?: Palette;\n}\n\n/** Inputs to {@link shouldShowBanner} — the real env, injected by the caller. */\nexport interface BannerGate {\n /** process.stdout.isTTY (banner is suppressed entirely when not a TTY). */\n isTTY: boolean;\n /** NO_COLOR is set (the banner still shows, but with zero color codes). */\n noColor: boolean;\n /** A machine-readable --json was requested (suppress entirely). */\n json: boolean;\n /** An explicit --quiet was passed (suppress entirely). */\n quiet: boolean;\n}\n\n/**\n * Whether to print the banner at all. The banner is decorative, so it shows ONLY\n * for an interactive TTY and never when output is being consumed by a script or\n * pipeline: not when piped/redirected (`!isTTY`), not under `--json`, and not\n * under `--quiet`. `NO_COLOR` does NOT suppress it — the banner renders without\n * color in that case (handled by {@link resolveColorMode}).\n */\nexport function shouldShowBanner(gate: BannerGate): boolean {\n return gate.isTTY && !gate.json && !gate.quiet;\n}\n\n/**\n * Resolve the color tier from the (injected) terminal env. 24-bit when the\n * terminal advertises it (COLORTERM=truecolor|24bit), 256-color when TERM names\n * a 256 palette, else 16-color — and PLAIN (no color) under NO_COLOR or a dumb /\n * absent TERM.\n */\nexport function resolveColorMode(env: {\n noColor: boolean;\n colorterm?: string | undefined;\n term?: string | undefined;\n}): ColorMode {\n if (env.noColor) return \"none\";\n const term = (env.term ?? \"\").toLowerCase();\n if (term === \"\" || term === \"dumb\") return \"none\";\n const colorterm = (env.colorterm ?? \"\").toLowerCase();\n if (colorterm === \"truecolor\" || colorterm === \"24bit\") return \"truecolor\";\n if (term.includes(\"256\")) return \"ansi256\";\n return \"ansi16\";\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// WIDE font — \"ANSI Shadow\" (6 rows). Hand-transcribed bold figlet glyphs —\n// solid `█` with a `╗╔╝╚║═` 3D shadow — fixed-width per glyph so they TILE\n// cleanly when concatenated (~8 cols/char → \"agent-connector\" ≈ 126 cols on\n// one line). Used only when the terminal is wide enough.\n// ─────────────────────────────────────────────────────────────────────────\n\nconst WIDE_FONT: Record<string, readonly string[]> = {\n A: [\" █████╗ \", \"██╔══██╗\", \"███████║\", \"██╔══██║\", \"██║ ██║\", \"╚═╝ ╚═╝\"],\n B: [\"██████╗ \", \"██╔══██╗\", \"██████╔╝\", \"██╔══██╗\", \"██████╔╝\", \"╚═════╝ \"],\n C: [\" ██████╗\", \"██╔════╝\", \"██║ \", \"██║ \", \"╚██████╗\", \" ╚═════╝\"],\n D: [\"██████╗ \", \"██╔══██╗\", \"██║ ██║\", \"██║ ██║\", \"██████╔╝\", \"╚═════╝ \"],\n E: [\"███████╗\", \"██╔════╝\", \"█████╗ \", \"██╔══╝ \", \"███████╗\", \"╚══════╝\"],\n F: [\"███████╗\", \"██╔════╝\", \"█████╗ \", \"██╔══╝ \", \"██║ \", \"╚═╝ \"],\n G: [\" ██████╗ \", \"██╔════╝ \", \"██║ ███╗\", \"██║ ██║\", \"╚██████╔╝\", \" ╚═════╝ \"],\n H: [\"██╗ ██╗\", \"██║ ██║\", \"███████║\", \"██╔══██║\", \"██║ ██║\", \"╚═╝ ╚═╝\"],\n I: [\"██╗\", \"██║\", \"██║\", \"██║\", \"██║\", \"╚═╝\"],\n J: [\" ██╗\", \" ██║\", \" ██║\", \"██ ██║\", \"╚█████╔╝\", \" ╚════╝ \"],\n K: [\"██╗ ██╗\", \"██║ ██╔╝\", \"█████╔╝ \", \"██╔═██╗ \", \"██║ ██╗\", \"╚═╝ ╚═╝\"],\n L: [\"██╗ \", \"██║ \", \"██║ \", \"██║ \", \"███████╗\", \"╚══════╝\"],\n M: [\"███╗ ███╗\", \"████╗ ████║\", \"██╔████╔██║\", \"██║╚██╔╝██║\", \"██║ ╚═╝ ██║\", \"╚═╝ ╚═╝\"],\n N: [\"███╗ ██╗\", \"████╗ ██║\", \"██╔██╗ ██║\", \"██║╚██╗██║\", \"██║ ╚████║\", \"╚═╝ ╚═══╝\"],\n O: [\" ██████╗ \", \"██╔═══██╗\", \"██║ ██║\", \"██║ ██║\", \"╚██████╔╝\", \" ╚═════╝ \"],\n P: [\"██████╗ \", \"██╔══██╗\", \"██████╔╝\", \"██╔═══╝ \", \"██║ \", \"╚═╝ \"],\n Q: [\" ██████╗ \", \"██╔═══██╗\", \"██║ ██║\", \"██║▄▄ ██║\", \"╚██████╔╝\", \" ╚══▀▀═╝ \"],\n R: [\"██████╗ \", \"██╔══██╗\", \"██████╔╝\", \"██╔══██╗\", \"██║ ██║\", \"╚═╝ ╚═╝\"],\n S: [\"███████╗\", \"██╔════╝\", \"███████╗\", \"╚════██║\", \"███████║\", \"╚══════╝\"],\n T: [\"████████╗\", \"╚══██╔══╝\", \" ██║ \", \" ██║ \", \" ██║ \", \" ╚═╝ \"],\n U: [\"██╗ ██╗\", \"██║ ██║\", \"██║ ██║\", \"██║ ██║\", \"╚██████╔╝\", \" ╚═════╝ \"],\n V: [\"██╗ ██╗\", \"██║ ██║\", \"██║ ██║\", \"╚██╗ ██╔╝\", \" ╚████╔╝ \", \" ╚═══╝ \"],\n W: [\"██╗ ██╗\", \"██║ ██║\", \"██║ █╗ ██║\", \"██║███╗██║\", \"╚███╔███╔╝\", \" ╚══╝╚══╝ \"],\n X: [\"██╗ ██╗\", \"╚██╗██╔╝\", \" ╚███╔╝ \", \" ██╔██╗ \", \"██╔╝ ██╗\", \"╚═╝ ╚═╝\"],\n Y: [\"██╗ ██╗\", \"╚██╗ ██╔╝\", \" ╚████╔╝ \", \" ╚██╔╝ \", \" ██║ \", \" ╚═╝ \"],\n Z: [\"███████╗\", \"╚══███╔╝\", \" ███╔╝ \", \" ███╔╝ \", \"███████╗\", \"╚══════╝\"],\n \"0\": [\" ██████╗ \", \"██╔═████╗\", \"██║██╔██║\", \"████╔╝██║\", \"╚██████╔╝\", \" ╚═════╝ \"],\n \"1\": [\" ██╗\", \"███║\", \"╚██║\", \" ██║\", \" ██║\", \" ╚═╝\"],\n \"2\": [\"██████╗ \", \"╚════██╗\", \" █████╔╝\", \"██╔═══╝ \", \"███████╗\", \"╚══════╝\"],\n \"3\": [\"██████╗ \", \"╚════██╗\", \" █████╔╝\", \" ╚═══██╗\", \"██████╔╝\", \"╚═════╝ \"],\n \"4\": [\"██╗ ██╗\", \"██║ ██║\", \"███████║\", \"╚════██║\", \" ██║\", \" ╚═╝\"],\n \"5\": [\"███████╗\", \"██╔════╝\", \"███████╗\", \"╚════██║\", \"███████║\", \"╚══════╝\"],\n \"6\": [\" ██████╗ \", \"██╔════╝ \", \"███████╗ \", \"██╔═══██╗\", \"╚██████╔╝\", \" ╚═════╝ \"],\n \"7\": [\"███████╗\", \"╚════██║\", \" ██╔╝\", \" ██╔╝ \", \" ██║ \", \" ╚═╝ \"],\n \"8\": [\" █████╗ \", \"██╔══██╗\", \"╚█████╔╝\", \"██╔══██╗\", \"╚█████╔╝\", \" ╚════╝ \"],\n \"9\": [\" █████╗ \", \"██╔══██╗\", \"╚██████║\", \" ╚═══██║\", \" █████╔╝\", \" ╚════╝ \"],\n \"-\": [\" \", \" \", \"█████╗\", \"╚════╝\", \" \", \" \"],\n \"_\": [\" \", \" \", \" \", \" \", \"███████╗\", \"╚══════╝\"],\n \".\": [\" \", \" \", \" \", \" \", \"██╗\", \"╚═╝\"],\n \" \": [\" \", \" \", \" \", \" \", \" \", \" \"],\n};\n\nconst GLYPH_ROWS = 6;\n\n/** Visible column count of a string (full-block + box-drawing are 1 cell each). */\nfunction vlen(s: string): number {\n return [...s].length;\n}\n\n/**\n * Render `name` into the fixed-width `font` as GLYPH_ROWS lines. The ANSI-Shadow\n * glyphs already carry their own trailing shadow column and tile cleanly with NO\n * inter-glyph gap. Returns null when the name has no renderable characters; the\n * glyphs are width-consistent per glyph, so the rows stay aligned.\n */\nfunction renderWithFont(name: string, font: Record<string, readonly string[]>): string[] | null {\n const chars = [...name];\n if (chars.length === 0) return null;\n const blank = font[\" \"]!;\n const rows: string[] = Array.from({ length: GLYPH_ROWS }, () => \"\");\n for (const ch of chars) {\n const glyph = font[ch.toUpperCase()] ?? blank;\n for (let r = 0; r < GLYPH_ROWS; r++) {\n rows[r] += glyph[r] ?? \"\";\n }\n }\n // Drop any fully-blank bottom rows so a name built from narrower glyphs (e.g.\n // \".\" / \"_\") never pads the banner with trailing empty lines.\n while (rows.length > 1 && rows[rows.length - 1]!.trim() === \"\") rows.pop();\n return rows;\n}\n\n/** Max visible width across an art block's rows. */\nfunction blockWidth(rows: string[]): number {\n return rows.length === 0 ? 0 : Math.max(...rows.map(vlen));\n}\n\n/**\n * The WIDE ANSI-Shadow block art for `name` when it fits `columns` (ONE line,\n * never stacked); else null so the caller renders the narrow one-line title.\n * `columns <= 0` means \"no constraint\" → always the WIDE art.\n */\nfunction selectWideArt(name: string, columns: number): string[] | null {\n const limit = columns > 0 ? columns : Infinity;\n const wide = renderWithFont(name, WIDE_FONT);\n return wide != null && blockWidth(wide) <= limit ? wide : null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Palette-parameterized gradient. The sweep lerps the three palette stops\n// across each row's columns (t∈[0,1]). truecolor uses the exact RGB; 256 and 16\n// derive the nearest representable code FROM the same RGB, so every palette\n// works in every tier. \"none\" emits the bare char.\n// ─────────────────────────────────────────────────────────────────────────\n\n/** The interpolated RGB of `palette` at gradient position t∈[0,1]. */\nfunction gradientRGB(t: number, palette: Palette): RGB {\n const clamp = Math.max(0, Math.min(1, t));\n const seg = clamp >= 0.5 ? 1 : 0;\n const local = (clamp - seg * 0.5) / 0.5;\n const a = palette[seg]!;\n const b = palette[seg + 1]!;\n return [\n Math.round(a[0] + (b[0] - a[0]) * local),\n Math.round(a[1] + (b[1] - a[1]) * local),\n Math.round(a[2] + (b[2] - a[2]) * local),\n ];\n}\n\n/** Nearest xterm-256 cube index (16..231) for an RGB triple. */\nfunction rgbTo256(r: number, g: number, b: number): number {\n const q = (v: number): number => (v < 48 ? 0 : v < 115 ? 1 : Math.round((v - 35) / 40));\n return 16 + 36 * q(r) + 6 * q(g) + q(b);\n}\n\n/** Nearest of the 8 bright 16-color fg codes (90..97) for an RGB triple. */\nfunction rgbTo16(r: number, g: number, b: number): number {\n // Threshold each channel; map the on/off triple to its bright SGR code.\n const hi = (v: number): number => (v >= 128 ? 1 : 0);\n const key = (hi(r) << 2) | (hi(g) << 1) | hi(b);\n // index: RGB bits → code. 0:black(90) 1:blue(94) 2:green(92) 3:cyan(96)\n // 4:red(91) 5:magenta(95) 6:yellow(93) 7:white(97)\n return [90, 94, 92, 96, 91, 95, 93, 97][key]!;\n}\n\nconst RESET = \"\\x1b[0m\";\n\n/**\n * Wrap a single grapheme `ch` at gradient position `t` in the SGR escape for the\n * given color tier + palette. Whitespace is never colored (no point paying for\n * an escape around a space) and \"none\" returns the bare char.\n */\nfunction colorChar(ch: string, t: number, color: ColorMode, palette: Palette): string {\n if (color === \"none\" || ch.trim() === \"\") return ch;\n const [r, g, b] = gradientRGB(t, palette);\n if (color === \"truecolor\") return `\\x1b[38;2;${r};${g};${b}m${ch}${RESET}`;\n if (color === \"ansi256\") return `\\x1b[38;5;${rgbTo256(r, g, b)}m${ch}${RESET}`;\n return `\\x1b[${rgbTo16(r, g, b)}m${ch}${RESET}`;\n}\n\n/** Apply the left-to-right gradient across one art row (by visible column). */\nfunction colorizeRow(row: string, color: ColorMode, palette: Palette): string {\n if (color === \"none\") return row;\n const chars = [...row];\n const width = Math.max(1, chars.length - 1);\n let out = \"\";\n for (let i = 0; i < chars.length; i++) {\n out += colorChar(chars[i]!, i / width, color, palette);\n }\n return out;\n}\n\n/** A single BOLD grapheme at gradient position `t` (used by the narrow title). */\nfunction boldColorChar(ch: string, t: number, color: ColorMode, palette: Palette): string {\n if (color === \"none\") return ch;\n const [r, g, b] = gradientRGB(t, palette);\n const fg =\n color === \"truecolor\"\n ? `38;2;${r};${g};${b}`\n : color === \"ansi256\"\n ? `38;5;${rgbTo256(r, g, b)}`\n : `${rgbTo16(r, g, b)}`;\n return `\\x1b[1;${fg}m${ch}${RESET}`;\n}\n\n/**\n * The NARROW one-line title `◆ AGENT-CONNECTOR`: a colored leading diamond (the\n * palette mid stop), two spaces, then the UPPERCASED brand with each character\n * colored along the palette gradient (i → t = i/(len-1)) and bold. Under \"none\"\n * it is the plain text with ZERO ANSI. ~18 cols for \"agent-connector\", so it\n * fits any real terminal.\n */\nfunction renderTitle(name: string, color: ColorMode, palette: Palette): string {\n const upper = name.toUpperCase();\n if (color === \"none\") return `◆ ${upper}`;\n const diamond = boldColorChar(\"◆\", 0.5, color, palette);\n const chars = [...upper];\n const span = Math.max(1, chars.length - 1);\n let title = \"\";\n for (let i = 0; i < chars.length; i++) {\n title += boldColorChar(chars[i]!, i / span, color, palette);\n }\n return `${diamond} ${title}`;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Footer (engine attribution). Shown on BOTH the global and branded CLIs — the\n// \"powered by\" wording is intentional.\n// ─────────────────────────────────────────────────────────────────────────\n\nconst FOOTER_LINES = [\n \"powered by @ken-jo/agent-connector\",\n \"more → https://github.com/ken-jo/agent-connector\",\n] as const;\n\n/** Dim (de-emphasized) footer line; plain under \"none\". */\nfunction dim(text: string, color: ColorMode): string {\n return color === \"none\" ? text : `\\x1b[2m${text}${RESET}`;\n}\n\n/**\n * Render the full banner: a responsive brand title with a palette-driven\n * gradient, then a two-line dim footer directly below.\n *\n * • WIDE — the ANSI-Shadow block art when it fits `columns` (ONE line, never\n * stacked; `columns <= 0` = no constraint).\n * • NARROW — the one-line `◆ AGENT-CONNECTOR` gradient title (the universal\n * narrow form, ~18 cols) when the block art would overflow.\n * • `palette` defaults to PALETTES[0] so direct callers + tests are\n * deterministic; the impure app.ts wrapper rotates the palettes per run.\n * • Under `color: \"none\"` (NO_COLOR / dumb term) the output carries zero ANSI\n * escape codes — only the raw art/title + footer text.\n *\n * The result has NO trailing newline; the caller prints it with one.\n */\nexport function renderBrandBanner(name: string, opts: BannerOptions): string {\n const { color, columns } = opts;\n const palette = opts.palette ?? PALETTES[0]!;\n const footer = FOOTER_LINES.map((l) => dim(l, color)).join(\"\\n\");\n\n const art = selectWideArt(name, columns);\n const body =\n art != null\n ? art.map((row) => colorizeRow(row, color, palette)).join(\"\\n\")\n : renderTitle(name, color, palette);\n return `${body}\\n${footer}`;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/claude-code/wire.ts"],"sourcesContent":["/**\n * adapters/claude-code/wire — Claude Code hook wire protocol.\n *\n * Claude Code pipes a JSON object to a hook command on stdin and reads a JSON\n * object (or empty) from stdout, interpreting the exit code as well. This module\n * captures the exact stdin shape and the canonical reply shape so the adapter's\n * parse/format methods are a single source of truth.\n *\n * Stdin (documented Claude Code hook input):\n * {\n * session_id, transcript_path, cwd, hook_event_name,\n * tool_name, tool_input, tool_response, // tool events\n * permission_suggestions, // PermissionRequest\n * tool_use_id, error, is_interrupt, duration_ms, // PostToolUseFailure\n * agent_id, agent_type, // SubagentStart/Stop\n * agent_transcript_path, last_assistant_message, // SubagentStop\n * source, // SessionStart\n * reason, // SessionEnd\n * prompt, // UserPromptSubmit\n * trigger, // PreCompact\n * stop_hook_active, // Stop / SubagentStop\n * message // Notification\n * }\n *\n * Stdout (canonical reply): a `hookSpecificOutput` object keyed by\n * `hookEventName`, carrying `permissionDecision` (allow|deny|ask) +\n * `permissionDecisionReason`, `additionalContext`, and (PreToolUse)\n * `updatedInput`; PermissionRequest instead takes a nested\n * `decision: { behavior: \"allow\"|\"deny\", ... }`; Stop-class blocks\n * (Stop / SubagentStop / UserPromptSubmit / PostToolUse) take the TOP-LEVEL\n * `{\"decision\":\"block\",\"reason\"}`. Exit 0 = proceed; the JSON refines the\n * decision.\n */\n\nimport type { SessionStartEvent } from \"../../core/types.js\";\n\n/** Canonical Claude Code hook event names (the values of `hook_event_name`). */\nexport const CLAUDE_HOOK_EVENTS = [\n \"PreToolUse\",\n \"PostToolUse\",\n \"PreCompact\",\n \"SessionStart\",\n \"SessionEnd\",\n \"UserPromptSubmit\",\n \"Stop\",\n \"Notification\",\n \"PermissionRequest\",\n \"PostToolUseFailure\",\n \"SubagentStart\",\n \"SubagentStop\",\n] as const;\n\nexport type ClaudeHookEvent = (typeof CLAUDE_HOOK_EVENTS)[number];\n\n/**\n * Raw Claude Code hook stdin payload. Every field is optional — a given event\n * only populates the subset relevant to it. `connector` is NOT a Claude field;\n * it is threaded in by the universal entrypoint so parseEvent can recover the\n * connector id (the home binary passes `--connector <id>` and may inject it).\n */\nexport interface ClaudeWireInput {\n session_id?: string;\n transcript_path?: string;\n cwd?: string;\n hook_event_name?: string;\n\n // tool events\n tool_name?: string;\n tool_input?: Record<string, unknown>;\n /** PostToolUse result payload (string or structured). */\n tool_response?: unknown;\n\n // PermissionRequest — permission-update entries the dialog would offer.\n // NOTE: the docs explicitly state PermissionRequest has NO tool_use_id.\n permission_suggestions?: unknown[];\n\n // PostToolUseFailure\n tool_use_id?: string;\n error?: string;\n is_interrupt?: boolean;\n duration_ms?: number;\n\n // SubagentStart / SubagentStop — agent_type is unreliable on SubagentStop\n // (the SDK may not populate it); treat both as optional everywhere.\n agent_id?: string;\n agent_type?: string;\n // SubagentStop — the subagent's OWN transcript (transcript_path stays the\n // parent session's) + the text of its final response.\n agent_transcript_path?: string;\n last_assistant_message?: string;\n\n // SessionStart\n source?: string;\n // SessionEnd\n reason?: string;\n // UserPromptSubmit\n prompt?: string;\n // PreCompact\n trigger?: string;\n // Stop / SubagentStop\n stop_hook_active?: boolean;\n // Notification\n message?: string;\n\n /** Injected by the entrypoint so the runtime knows which connector to dispatch. */\n connector?: string;\n}\n\n/**\n * Extract a stable session id from a Claude wire payload.\n * Priority mirrors the proven adapter: transcript UUID > session_id > \"\".\n * (The framework's normalized event uses \"\" when no id is available, per\n * core/types BaseEvent.sessionId docstring — no ppid fabrication here.)\n */\nexport function extractSessionId(input: ClaudeWireInput): string {\n if (typeof input.transcript_path === \"string\") {\n const m = input.transcript_path.match(/([a-f0-9-]{36})\\.jsonl$/);\n if (m && m[1]) return m[1];\n }\n if (typeof input.session_id === \"string\" && input.session_id !== \"\") {\n return input.session_id;\n }\n return \"\";\n}\n\n/** Coerce a Claude PostToolUse `tool_response` into a string for the normalized event. */\nexport function toolResponseToString(value: unknown): string | undefined {\n if (value == null) return undefined;\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/** Map a host's raw session-start \"source\" to the canonical SessionStart source. */\nexport function normalizeSessionSource(source: string | undefined): SessionStartEvent[\"source\"] {\n switch (source) {\n case \"compact\":\n return \"compact\";\n case \"resume\":\n return \"resume\";\n case \"clear\":\n return \"clear\";\n default:\n return \"startup\";\n }\n}\n"],"mappings":";;;AAkHO,SAAS,iBAAiB,OAAgC;AAC/D,MAAI,OAAO,MAAM,oBAAoB,UAAU;AAC7C,UAAM,IAAI,MAAM,gBAAgB,MAAM,yBAAyB;AAC/D,QAAI,KAAK,EAAE,CAAC,EAAG,QAAO,EAAE,CAAC;AAAA,EAC3B;AACA,MAAI,OAAO,MAAM,eAAe,YAAY,MAAM,eAAe,IAAI;AACnE,WAAO,MAAM;AAAA,EACf;AACA,SAAO;AACT;AAGO,SAAS,qBAAqB,OAAoC;AACvE,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;AAGO,SAAS,uBAAuB,QAAyD;AAC9F,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime/telemetry-accessor.ts","../src/telemetry/measure.ts","../src/runtime/hook-entrypoint.ts"],"sourcesContent":["/**\n * runtime/telemetry-accessor — build the handler-facing {@link TelemetryAccessor}.\n *\n * Every runtime entrypoint (hook / statusline / action) stamps `ctx.telemetry`\n * (or `evt.telemetry`) with the accessor built here so a connector handler can\n * read ITS OWN recorded usage on demand (`await ctx.telemetry?.()`), without the\n * handler reaching into the telemetry store directly.\n *\n * The accessor is LAZY (it opens the store only when called) and DEFENSIVE:\n * - AGENT_CONNECTOR_TELEMETRY=0 → resolves zeros without touching disk;\n * - any read error → resolves zeros (NEVER throws into a handler — the hook\n * runtime is fail-open, the statusline fail-safe, and even the action\n * surface must not have a usage read crash an otherwise-fine run).\n *\n * COST: each accessor call, or the first read of a lazy `ctx.usage`, reads the\n * connector's telemetry file in full (the NDJSON store has no since/limit bound)\n * and linear-scans it. Cheap for normal stores, but a handler on a HOT path\n * should call it only when it needs usage.\n */\n\nimport type { TelemetryAccessor, TelemetryUsageSummary } from \"../core/types.js\";\nimport { openStore } from \"../telemetry/store.js\";\n\n/** The empty summary returned when telemetry is off or a read fails. */\nconst ZERO: TelemetryUsageSummary = {\n inputTokens: 0,\n outputTokens: 0,\n totalTokens: 0,\n calls: 0,\n};\n\n/**\n * Synchronously sum every stored row's inputTokens/outputTokens for the\n * connector and count rows (`calls`). Honors the AGENT_CONNECTOR_TELEMETRY=0\n * kill switch (zeros, no disk touch) and swallows any error (zeros).\n */\nexport function readTelemetryUsageSummary(connectorId: string): TelemetryUsageSummary {\n if (process.env.AGENT_CONNECTOR_TELEMETRY === \"0\") return { ...ZERO };\n try {\n const store = openStore({});\n try {\n let inputTokens = 0;\n let outputTokens = 0;\n let calls = 0;\n // COERCE each addend to a finite number: query() tolerates malformed\n // LINES but does not validate field shapes, so a legacy/hand-edited row\n // with a missing (→ NaN) or string (→ concat) token field must not\n // poison the summary's numeric contract. A non-finite token reads as 0.\n for (const row of store.query({ connectorId })) {\n inputTokens += Number.isFinite(row.inputTokens) ? row.inputTokens : 0;\n outputTokens += Number.isFinite(row.outputTokens) ? row.outputTokens : 0;\n calls += 1;\n }\n return {\n inputTokens,\n outputTokens,\n totalTokens: inputTokens + outputTokens,\n calls,\n };\n } finally {\n store.close();\n }\n } catch {\n // Never throw into a handler — a usage read failure resolves to zeros.\n return { ...ZERO };\n }\n}\n\n/**\n * Build a {@link TelemetryAccessor} bound to `connectorId`. The returned async\n * function uses {@link readTelemetryUsageSummary}; it never throws.\n */\nexport function buildTelemetryAccessor(connectorId: string): TelemetryAccessor {\n return async (): Promise<TelemetryUsageSummary> => {\n return readTelemetryUsageSummary(connectorId);\n };\n}\n\n/**\n * Install a cached lazy `usage` property on a statusline context. This preserves\n * synchronous `ctx.usage` reads for render functions without forcing every\n * status refresh to scan the telemetry store when the render never uses usage.\n */\nexport function attachLazyTelemetryUsage(\n ctx: { usage?: TelemetryUsageSummary },\n connectorId: string,\n read: (connectorId: string) => TelemetryUsageSummary = readTelemetryUsageSummary,\n): void {\n let cached: TelemetryUsageSummary | undefined;\n let hasCached = false;\n\n Object.defineProperty(ctx, \"usage\", {\n configurable: true,\n enumerable: true,\n get() {\n if (!hasCached) {\n cached = read(connectorId);\n hasCached = true;\n }\n return cached;\n },\n set(value: TelemetryUsageSummary | undefined) {\n cached = value;\n hasCached = true;\n },\n });\n}\n","/**\n * telemetry/measure — pure measurement helpers over a single MCP tool call.\n *\n * The framework tokenizes its OWN bytes: the tool-call arguments going in, the\n * tool result coming out, and the fixed tool-definition schemas. These helpers\n * turn those into {@link TokenCount}s using the default {@link Tokenizer}.\n *\n * Honesty rules:\n * • Binary modalities (image/audio/resource blobs) are NOT tokenized — base64\n * would massively over-count. Each gets a flat per-modality estimate, and\n * because that estimate is itself an approximation the combined confidence\n * is pulled down to at least \"tokenizer-approx\".\n * • When combining several counts, the reported confidence is the WORST\n * (least-confident) of the inputs — never report \"exact\" for a mixed bag.\n *\n * Everything here is pure: no IO, no mutation of inputs.\n */\n\nimport { getTokenizer } from \"./tokenizer.js\";\nimport { worstConfidence } from \"./types.js\";\nimport type {\n ConfidenceSource,\n ModelFamily,\n TokenCount,\n Tokenizer,\n} from \"./types.js\";\n\n/** Per-call measurement: tokens in, tokens out, and combined confidence. */\nexport interface ToolCallMeasurement {\n inputTokens: number;\n outputTokens: number;\n source: ConfidenceSource;\n}\n\n/**\n * Flat token estimates for non-text content blocks. These mirror common host\n * accounting (an image tile is roughly ~85 tokens at low detail); they are\n * deliberately coarse — we never tokenize the underlying base64 payload.\n */\nconst MODALITY_ESTIMATE: Record<string, number> = {\n image: 85,\n audio: 85,\n resource: 85,\n};\n/** Fallback estimate for an unknown non-text block type. */\nconst UNKNOWN_BLOCK_ESTIMATE = 85;\n\n// Confidence ordering + the worst-of comparison live in ./types (the single\n// source of truth, imported as `worstConfidence` above). Re-exported here so\n// callers that already import `worstConfidence` from this module keep working\n// as a new ConfidenceSource value (e.g. tokenizer-calibrated) slots into the\n// shared ordering.\nexport { worstConfidence };\n\n/** Reduce a list of sources to the single worst (least-confident) one. */\nfunction combineSources(sources: readonly ConfidenceSource[]): ConfidenceSource {\n // Default to the most-confident floor so an empty list combines to the only\n // other source it is paired with; in practice callers always pass ≥1 source.\n let worst: ConfidenceSource = \"host-native\";\n for (const s of sources) worst = worstConfidence(worst, s);\n return worst;\n}\n\n// ── Structural shapes we read from a tool result (kept local + narrow) ───────\n\n/** A text content block: `{ type: \"text\", text: \"...\" }`. */\ninterface TextBlock {\n type: \"text\";\n text?: unknown;\n}\n/** Any content block; only text blocks expose a `text` field we tokenize. */\ninterface ContentBlock {\n type?: unknown;\n text?: unknown;\n}\n/** The portion of an MCP tool result we measure. */\ninterface ToolResultLike {\n content?: unknown;\n structuredContent?: unknown;\n}\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null;\n}\n\nfunction isTextBlock(block: ContentBlock): block is TextBlock {\n return block.type === \"text\";\n}\n\n/**\n * Measure one tool call.\n *\n * Input = canonical JSON of `args`.\n * Output = tokens over the TEXTUAL parts of `result`:\n * • If `result.content` is an array of blocks, text blocks are tokenized via\n * their `text` field and non-text blocks add a flat per-modality estimate\n * (no base64 tokenization).\n * • `result.structuredContent`, when present, is tokenized as canonical JSON.\n * • Anything else (string / arbitrary object) is tokenized as canonical JSON.\n *\n * The reported `source` is the worst of the input and output sources.\n */\nexport function measureToolCall(\n args: unknown,\n result: unknown,\n family: ModelFamily,\n tok: Tokenizer = getTokenizer(),\n): ToolCallMeasurement {\n const input = tok.countValue(args, family);\n const output = measureResult(result, family, tok);\n return {\n inputTokens: input.tokens,\n outputTokens: output.tokens,\n source: worstConfidence(input.source, output.source),\n };\n}\n\n/** Tokenize the textual + structured parts of a tool result. */\nfunction measureResult(\n result: unknown,\n family: ModelFamily,\n tok: Tokenizer,\n): TokenCount {\n if (!isObject(result)) {\n // Plain string or scalar result — count its canonical form directly.\n return tok.countValue(result, family);\n }\n\n const res = result as ToolResultLike;\n const sources: ConfidenceSource[] = [];\n let tokens = 0;\n\n if (Array.isArray(res.content)) {\n for (const raw of res.content) {\n if (!isObject(raw)) continue;\n const block = raw as ContentBlock;\n if (isTextBlock(block)) {\n const text = typeof block.text === \"string\" ? block.text : \"\";\n const c = tok.count(text, family);\n tokens += c.tokens;\n sources.push(c.source);\n } else {\n // image / audio / resource / unknown → flat estimate, no base64 BPE.\n const key = typeof block.type === \"string\" ? block.type : \"\";\n tokens += MODALITY_ESTIMATE[key] ?? UNKNOWN_BLOCK_ESTIMATE;\n // A flat estimate is an approximation, never \"exact\".\n sources.push(\"tokenizer-approx\");\n }\n }\n } else if (res.content !== undefined) {\n // content present but not an array (e.g. a bare string) → count it whole.\n const c = tok.countValue(res.content, family);\n tokens += c.tokens;\n sources.push(c.source);\n }\n\n if (res.structuredContent !== undefined) {\n const c = tok.countValue(res.structuredContent, family);\n tokens += c.tokens;\n sources.push(c.source);\n }\n\n // Nothing measurable found → count the whole object as a fallback so we never\n // silently report zero for a non-empty result.\n if (sources.length === 0) {\n return tok.countValue(result, family);\n }\n\n return { tokens, source: combineSources(sources) };\n}\n\n/**\n * Measure the fixed tool-definition overhead: tokenize the JSON of the entire\n * `tools/list` array once. `tools` is the raw array of tool descriptors as the\n * server advertises them (shape is host/SDK-specific, so it is treated as\n * opaque JSON here).\n */\nexport function measureToolDefs(\n tools: readonly unknown[],\n family: ModelFamily,\n tok: Tokenizer = getTokenizer(),\n): TokenCount {\n return tok.countValue(tools, family);\n}\n\n/**\n * Measure one RUNTIME hook dispatch (the new `hook` developer-axis surface).\n *\n * Input = canonical JSON of the inbound normalized event payload the handler\n * reads (the host-provided fields: toolInput / prompt / raw / etc.).\n * Output = canonical JSON of what the handler RETURNS that becomes context or a\n * decision the host consumes (additionalContext / updatedInput / reason\n * and the rest of the normalized {@link HookResponse}).\n *\n * Uses the SAME tokenizer + worst-of confidence combination as the proxy. Pure:\n * no IO, no mutation. The two values are tokenized whole (a hook payload has no\n * binary content blocks to special-case, unlike a tool result).\n */\nexport function measureHook(\n eventPayload: unknown,\n handlerReturn: unknown,\n family: ModelFamily,\n tok: Tokenizer = getTokenizer(),\n): ToolCallMeasurement {\n const input = tok.countValue(eventPayload, family);\n const output = tok.countValue(handlerReturn, family);\n return {\n inputTokens: input.tokens,\n outputTokens: output.tokens,\n source: worstConfidence(input.source, output.source),\n };\n}\n","/**\n * runtime/hook-entrypoint — the universal json-stdio hook dispatcher.\n *\n * Every json-stdio host points its hook config at the single stable home binary\n * (`<homeBin> hook <platformId> <event> --connector <id>`, built by\n * core/spawn.buildHomeBinHookCommand). The CLI reads stdin and calls\n * {@link runHook} with the parsed flags + the raw stdin string. This module:\n *\n * 1. Loads the registered connector (live handlers) and the host adapter.\n * 2. Parses the host's stdin payload into a normalized event via the adapter.\n * 3. Finds the connector's handler for the event, honoring the tool matcher.\n * 4. Runs the handler, normalizes its return, and formats the adapter's native\n * reply.\n *\n * Fail-open is the safety contract: ANY error degrades to exit 0 (\"allow\") so a\n * framework or handler bug can never wedge a host's tool call — EXCEPT when the\n * event is PreToolUse or PermissionRequest and the handler explicitly denied,\n * in which case the deny (a deliberate security decision) is preserved rather\n * than swallowed.\n */\n\nimport type {\n HookEventName,\n HookResponse,\n NativeHookDef,\n NativeHookEvent,\n PlatformId,\n ResolvedConnector,\n} from \"../core/types.js\";\nimport { log } from \"../core/logger.js\";\nimport {\n loadRegisteredConnector,\n readRegisteredMeta,\n} from \"../core/load-connector.js\";\nimport { loadAdapter, REGISTERED_PLATFORM_IDS } from \"../adapters/registry.js\";\nimport { buildTelemetryAccessor } from \"./telemetry-accessor.js\";\nimport type { NormalizedEvent } from \"../adapters/spi.js\";\nimport { projectIdentity } from \"../core/paths.js\";\nimport { measureHook } from \"../telemetry/measure.js\";\nimport { openStore, newRecordId } from \"../telemetry/store.js\";\nimport { getTokenizer, inferModelFamily } from \"../telemetry/tokenizer.js\";\n\n/** Flags + stdin the CLI hands to {@link runHook}. */\nexport interface RunHookOptions {\n /** Host platform id from the hook command (`hook <platformId> ...`). */\n platformId: string;\n /** Canonical lifecycle event name from the hook command. */\n event: HookEventName;\n /** Connector id from `--connector <id>`. */\n connectorId: string;\n /** Raw stdin payload (host-native JSON). Empty string is tolerated → `{}`. */\n stdin: string;\n}\n\n/** Process-level result the CLI translates into exit code + stdout/stderr. */\nexport interface RunHookResult {\n exitCode: number;\n stdout?: string;\n stderr?: string;\n}\n\n/** A pass-through \"allow\" reply with exit 0 and no payload. */\nconst ALLOW: RunHookResult = { exitCode: 0 };\n\n/**\n * The string a connector matcher filters on, when the event carries one:\n * tool events (PreToolUse / PostToolUse / PermissionRequest /\n * PostToolUseFailure) expose `toolName`; subagent events expose `agentType`.\n * Returns undefined when the event has neither (no filtering) — including a\n * SubagentStop arriving WITHOUT agent_type, which some hosts fail to populate;\n * filtering out such an event would silently drop a real stop, so we run the\n * handler instead (fail-open).\n */\nexport function eventMatcherSubject(evt: NormalizedEvent): string | undefined {\n const name = (evt as { toolName?: unknown }).toolName;\n if (typeof name === \"string\") return name;\n const agentType = (evt as { agentType?: unknown }).agentType;\n if (typeof agentType === \"string\") return agentType;\n return undefined;\n}\n\n/**\n * Tolerantly parse the host's stdin payload. Empty / whitespace-only input is a\n * legitimate \"no payload\" signal from some hosts and resolves to `{}`. Malformed\n * JSON also degrades to `{}` rather than throwing — fail-open is the contract.\n */\nfunction parseStdin(stdin: string): unknown {\n const trimmed = stdin.trim();\n if (trimmed === \"\") return {};\n try {\n return JSON.parse(trimmed);\n } catch {\n return {};\n }\n}\n\n/**\n * Compile the connector's matcher (a regex string) for a tool event. A missing\n * or empty matcher matches everything (returns null → no filtering). An invalid\n * regex degrades to \"match everything\" so a typo never blocks tool calls.\n */\nexport function compileMatcher(matcher: string | undefined): RegExp | null {\n if (!matcher || matcher === \"\") return null;\n try {\n return new RegExp(matcher);\n } catch {\n return null;\n }\n}\n\n/**\n * Normalize a handler return into a concrete HookResponse. A void/non-object\n * return becomes `{}` — NO decision — which every adapter formats as\n * pass-through allow (`response.decision ?? \"allow\"`). Deliberately NOT\n * `{decision:\"allow\"}`: on PermissionRequest an explicit \"allow\" is an ACTIVE\n * grant that suppresses the host's permission dialog, and a handler that\n * returns nothing must fall through to the dialog, never silently grant.\n */\nfunction normalizeResponse(value: HookResponse | void): HookResponse {\n if (value == null || typeof value !== \"object\") return {};\n return value;\n}\n\n/**\n * Resolve the host platform to stamp on a hook telemetry row. Mirrors the serve\n * `--host` plumbing: the install-target platform id is baked into the hook\n * command (`hook <platformId> <event> …`) so `opts.platformId` is authoritative,\n * but an explicit env override (AGENT_CONNECTOR_HOST) wins when it names a known\n * platform. Falls back to the event's adapter-stamped hostPlatform, then\n * \"unknown\" — never throws.\n */\nfunction resolveHookHostPlatform(\n platformId: string,\n evt: NormalizedEvent | NativeHookEvent,\n): PlatformId {\n const override = process.env.AGENT_CONNECTOR_HOST;\n if (\n override !== undefined &&\n REGISTERED_PLATFORM_IDS.has(override as PlatformId)\n ) {\n return override as PlatformId;\n }\n if (REGISTERED_PLATFORM_IDS.has(platformId as PlatformId)) {\n return platformId as PlatformId;\n }\n const fromEvent = (evt as { hostPlatform?: unknown }).hostPlatform;\n if (\n typeof fromEvent === \"string\" &&\n REGISTERED_PLATFORM_IDS.has(fromEvent as PlatformId)\n ) {\n return fromEvent as PlatformId;\n }\n return \"unknown\";\n}\n\n/**\n * Record one RUNTIME hook dispatch as a `hook` developer-axis telemetry row.\n *\n * input = the inbound event payload the handler reads (normalized event, or\n * the {@link NativeHookEvent} envelope for native passthrough hooks);\n * output = what the handler returned (a normalized {@link HookResponse}, or the\n * verbatim native reply object). Tokenized with the SAME tokenizer the\n * proxy uses. For a native hook `opts.event` is the host-native name.\n *\n * MUST be fail-open (the hook runtime is fail-open by contract): any error here\n * is swallowed so a measurement bug can NEVER break a host's hook. Honors the\n * AGENT_CONNECTOR_TELEMETRY=0 kill switch (skips entirely; the store's append is\n * already a no-op under it, but we also skip the tokenize work).\n */\nfunction recordHookTelemetry(\n opts: { platformId: string; event: string; connectorId: string },\n connector: ResolvedConnector,\n evt: NormalizedEvent | NativeHookEvent,\n response: unknown,\n): void {\n if (process.env.AGENT_CONNECTOR_TELEMETRY === \"0\") return;\n try {\n const family = inferModelFamily(\"\", connector.telemetry.modelFamilyHint);\n const measurement = measureHook(evt, response, family, getTokenizer());\n\n const sessionId =\n typeof (evt as { sessionId?: unknown }).sessionId === \"string\"\n ? ((evt as { sessionId: string }).sessionId)\n : \"\";\n const evtProjectDir = (evt as { projectDir?: unknown }).projectDir;\n const projectDir =\n typeof evtProjectDir === \"string\" && evtProjectDir !== \"\"\n ? evtProjectDir\n : process.cwd();\n const id = projectIdentity(projectDir);\n\n const store = openStore({});\n try {\n store.append({\n id: newRecordId(0),\n ts: Date.now(),\n connectorId: opts.connectorId,\n toolName: opts.event, // for a hook row the per-item name IS the event\n scope: \"hook\",\n surfaceKind: \"hook\",\n hostPlatform: resolveHookHostPlatform(opts.platformId, evt),\n sessionId,\n projectKey: id.key,\n projectDir: id.dir,\n inputTokens: measurement.inputTokens,\n outputTokens: measurement.outputTokens,\n confidenceSource: measurement.source,\n isError: false,\n });\n } finally {\n store.close();\n }\n } catch {\n // Fail-open: a telemetry error must NEVER break the hook runtime.\n }\n}\n\n/**\n * Dispatch one host hook invocation. Always resolves (never rejects): every\n * failure path returns a concrete {@link RunHookResult} so the CLI can exit\n * cleanly. See the module header for the fail-open contract.\n */\nexport async function runHook(opts: RunHookOptions): Promise<RunHookResult> {\n const { platformId, event, connectorId, stdin } = opts;\n\n try {\n const connector = await loadRegisteredConnector(connectorId);\n const adapter = await loadAdapter(platformId);\n\n // mcp-only hosts (and any adapter without runtime dispatch) cannot parse a\n // hook payload — nothing to do, allow.\n if (!adapter || !adapter.parseEvent || !adapter.formatReply) {\n return ALLOW;\n }\n\n const raw = parseStdin(stdin);\n const evt = adapter.parseEvent(event, raw);\n // The hook command carries the authoritative connector id; stamp it on the\n // event so handlers see the connector they were dispatched for.\n evt.connectorId = connectorId;\n // Unify the hook event with HostCtx: the adapter's parseEvent has no\n // capabilities/scope/telemetry at parse time (they are runtime-only), so the\n // runtime ALWAYS backfills them before the handler runs. capabilities comes\n // from the adapter; scope is recovered from the registered metadata (sync);\n // telemetry is the lazy per-connector usage accessor. The SAME three\n // assignments run on the error path (failOpenOrPreserveDeny) for symmetry.\n evt.capabilities = adapter.capabilities;\n evt.scope = readRegisteredMeta(connectorId)?.scope;\n evt.telemetry = buildTelemetryAccessor(connectorId);\n\n const definition = connector.hooks[event];\n if (!definition || typeof definition.handler !== \"function\") {\n // The connector declares no handler for this event — allow.\n return ALLOW;\n }\n\n // Per-host handler override: when dispatching for host X, `hosts[X].handler`\n // wins over the top-level handler; a host not listed (or a per-host entry\n // that is somehow not a function) falls back to the top-level handler.\n // Selection NEVER throws — fail-open is preserved.\n // NOTE: keyed on the RAW install-baked platformId (the host this entrypoint\n // was registered for), NOT resolveHookHostPlatform's AGENT_CONNECTOR_HOST\n // override — that override only re-labels the telemetry row. The error path\n // (failOpenOrPreserveDeny) resolves the IDENTICAL handler the same way, so a\n // per-host deny is preserved consistently.\n const perHost = definition.hosts?.[platformId as PlatformId]?.handler;\n const handler = typeof perHost === \"function\" ? perHost : definition.handler;\n\n // Matcher: if set and the event's subject (tool name, or agent type for\n // Subagent* events) does not match, allow (the hook is simply not\n // interested in this tool/agent). The matcher is the definition's — a\n // per-host handler shares the top-level matcher.\n const subject = eventMatcherSubject(evt);\n if (subject !== undefined) {\n const re = compileMatcher(definition.matcher);\n if (re && !re.test(subject)) return ALLOW;\n }\n\n // Run the resolved handler. Its return is normalized (void → allow) and\n // handed to the adapter to render the host-native reply.\n const handlerResult = await handler(\n // The adapter parsed `evt` as exactly this event's payload type; the\n // HooksConfig handler for the same event expects that payload.\n evt as never,\n );\n const response = normalizeResponse(handlerResult);\n\n // Developer-axis telemetry for the RUNTIME `hook` surface. Fail-open: never\n // lets a measurement error break the hook (handled inside the helper).\n recordHookTelemetry(opts, connector, evt, response);\n\n const reply = adapter.formatReply(event, response);\n return {\n exitCode: reply.exitCode,\n ...(reply.stdout !== undefined ? { stdout: reply.stdout } : {}),\n ...(reply.stderr !== undefined ? { stderr: reply.stderr } : {}),\n };\n } catch (err) {\n // Fail-open: a framework/handler error must not wedge the host's tool call.\n // The ONE exception is a PreToolUse / PermissionRequest deny — a deliberate\n // block we honor even if a later step threw. We re-derive that decision\n // defensively from the adapter so a throw AFTER the deny still surfaces it.\n const message = err instanceof Error ? err.message : String(err);\n log.error(`hook ${platformId}/${event} (${connectorId}) failed:`, message);\n return failOpenOrPreserveDeny(opts, message);\n }\n}\n\n/**\n * Events whose explicit deny survives the fail-open error path. Both are\n * permission gates — a deny there is a deliberate security decision that must\n * never be silently downgraded to allow. (Stop-class denies are persistence\n * conveniences, not security boundaries, so they stay fail-open.)\n */\nconst DENY_PRESERVE_EVENTS: ReadonlySet<HookEventName> = new Set([\n \"PreToolUse\",\n \"PermissionRequest\",\n]);\n\n/**\n * Error path. Returns fail-open allow, UNLESS this is a deny-preserving event\n * (PreToolUse / PermissionRequest) whose handler explicitly denied — in which\n * case the deny is reconstructed and preserved (a security decision is never\n * silently downgraded to allow).\n *\n * Best-effort: if anything in the deny reconstruction itself throws, we fall\n * back to allow (we never escalate an error into a spurious block).\n */\nasync function failOpenOrPreserveDeny(\n opts: RunHookOptions,\n errorMessage: string,\n): Promise<RunHookResult> {\n if (!DENY_PRESERVE_EVENTS.has(opts.event)) return ALLOW;\n try {\n const connector = await loadRegisteredConnector(opts.connectorId);\n const adapter = await loadAdapter(opts.platformId);\n if (!adapter?.parseEvent || !adapter.formatReply) return ALLOW;\n\n const definition = connector.hooks[opts.event];\n if (!definition || typeof definition.handler !== \"function\") return ALLOW;\n\n // Reconstruct the deny from the SAME handler the main path would have run —\n // the per-host override if one is declared for this host, else the top-level\n // handler. Otherwise a per-host deny could be silently downgraded on the\n // error path.\n const perHost = definition.hosts?.[opts.platformId as PlatformId]?.handler;\n const handler = typeof perHost === \"function\" ? perHost : definition.handler;\n\n const raw = parseStdin(opts.stdin);\n const evt = adapter.parseEvent(opts.event, raw);\n evt.connectorId = opts.connectorId;\n // Backfill the HostCtx fields here too (symmetry with the main path), so a\n // per-host deny reconstructed on the error path still carries capabilities/\n // scope/telemetry — a handler that branches on them denies identically.\n evt.capabilities = adapter.capabilities;\n evt.scope = readRegisteredMeta(opts.connectorId)?.scope;\n evt.telemetry = buildTelemetryAccessor(opts.connectorId);\n\n const subject = eventMatcherSubject(evt);\n if (subject !== undefined) {\n const re = compileMatcher(definition.matcher);\n if (re && !re.test(subject)) return ALLOW;\n }\n\n const response = normalizeResponse(await handler(evt as never));\n if (response.decision !== \"deny\") return ALLOW;\n\n const reply = adapter.formatReply(opts.event, response);\n return {\n exitCode: reply.exitCode,\n ...(reply.stdout !== undefined ? { stdout: reply.stdout } : {}),\n ...(reply.stderr !== undefined\n ? { stderr: reply.stderr }\n : { stderr: errorMessage }),\n };\n } catch {\n // Reconstruction failed — never escalate an internal error into a block.\n return ALLOW;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Native (passthrough) hooks — host events OUTSIDE the normalized union\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Flags + stdin for {@link runNativeHook}. `event` is HOST-NATIVE (non-union). */\nexport interface RunNativeHookOptions {\n /** Host platform id from the hook command (`hook <platformId> ...`). */\n platformId: string;\n /** Host-native event name, verbatim (e.g. \"TaskCreated\"). */\n event: string;\n /** Connector id from `--connector <id>`. */\n connectorId: string;\n /** Raw stdin payload (host-native JSON). Empty string is tolerated → `{}`. */\n stdin: string;\n}\n\n/** The connector's native hook definition for `platformId`/`event`, if any. */\nfunction nativeHookDef(\n connector: ResolvedConnector,\n platformId: string,\n event: string,\n): NativeHookDef | undefined {\n return connector.platforms[platformId as PlatformId]?.nativeHooks?.[event];\n}\n\n/**\n * True when the registered connector declares `event` under\n * `platforms[platformId].nativeHooks` with a live handler. Used by the hook CLI\n * to decide whether a non-union event name is accepted (declared → dispatch via\n * {@link runNativeHook}) or rejected with the strict unknown-event error.\n * Fail-safe: any load error reads as \"not declared\".\n */\nexport async function isNativeHookDeclared(\n platformId: string,\n event: string,\n connectorId: string,\n): Promise<boolean> {\n try {\n const connector = await loadRegisteredConnector(connectorId);\n return typeof nativeHookDef(connector, platformId, event)?.handler === \"function\";\n } catch {\n return false;\n }\n}\n\n/**\n * Dispatch one NATIVE (passthrough) hook invocation. No normalized parse, no\n * HookResponse mapping, no runtime matcher evaluation (the host's own matcher\n * already filtered — the def's matcher string was written verbatim into the\n * host config at install):\n *\n * stdin JSON → NativeHookEvent{raw} → handler → VERBATIM JSON stdout (exit 0).\n *\n * void/undefined return → exit 0 with no output. Fail-open: ANY throw degrades\n * to exit 0 with no output — exit-2 blocking semantics are not modeled in v1.\n * Telemetry: records the same scope:\"hook\" developer-axis row as normalized\n * hooks, with the NATIVE event name as the per-item name.\n */\nexport async function runNativeHook(\n opts: RunNativeHookOptions,\n): Promise<RunHookResult> {\n const { platformId, event, connectorId, stdin } = opts;\n try {\n const connector = await loadRegisteredConnector(connectorId);\n const def = nativeHookDef(connector, platformId, event);\n if (!def || typeof def.handler !== \"function\") {\n // Not declared for this platform — nothing to do, allow.\n return ALLOW;\n }\n\n const raw = parseStdin(stdin);\n // Best-effort session/project extraction from the json-stdio common shape\n // (Claude Code: session_id / cwd). Everything else stays in `raw` untouched.\n const sessionId =\n typeof (raw as { session_id?: unknown })?.session_id === \"string\"\n ? (raw as { session_id: string }).session_id\n : \"\";\n const cwd = (raw as { cwd?: unknown })?.cwd;\n const evt: NativeHookEvent = {\n event,\n hostPlatform: REGISTERED_PLATFORM_IDS.has(platformId as PlatformId)\n ? (platformId as PlatformId)\n : \"unknown\",\n sessionId,\n ...(typeof cwd === \"string\" && cwd !== \"\" ? { projectDir: cwd } : {}),\n raw,\n };\n\n const result = await def.handler(evt);\n\n // Same scope:\"hook\" telemetry row as normalized hooks (fail-open inside).\n recordHookTelemetry(opts, connector, evt, result === undefined ? {} : result);\n\n if (result === undefined) return ALLOW; // void → exit 0, no output\n const json = JSON.stringify(result);\n // Non-JSON-serializable returns (functions/symbols) degrade to silence.\n if (json === undefined) return ALLOW;\n return { exitCode: 0, stdout: json };\n } catch (err) {\n // Fail-open, unconditionally: native hooks have no deny-preserve carve-out\n // (there is no normalized decision to reconstruct).\n const message = err instanceof Error ? err.message : String(err);\n log.error(\n `native hook ${platformId}/${event} (${connectorId}) failed:`,\n message,\n );\n return ALLOW;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/detect.ts"],"sourcesContent":["/**\n * adapters/detect — two complementary detection answers.\n *\n * 1. detectInstalledPlatforms(projectDir): which hosts are INSTALLED on this\n * machine (probe each adapter's config dirs / marker files). Drives the\n * install planner (\"auto\" target expansion, `doctor`).\n *\n * 2. detectRuntimeHost(env): which host is EXECUTING right now (read env\n * markers the host runtime injects into its child processes). Drives the\n * universal hook entrypoint — it must know which adapter's wire format the\n * stdin payload is in before it can parse it.\n *\n * Both derive their platform set from `ADAPTER_REGISTRY` (the single source of\n * truth in registry.ts), generalizing context-mode's detection cascade\n * (env-var tier → config-dir tier → low-confidence fallback) to our\n * Adapter/AdapterFactory contract. The config-dir tier is delegated to each\n * adapter's `detectInstalled()` rather than duplicated here, so adding a\n * platform never requires editing this file.\n */\n\nimport type { DetectedPlatform, DetectionSignal, PlatformId } from \"../core/types.js\";\nimport { ADAPTER_REGISTRY, loadAdapter } from \"./registry.js\";\n\n/**\n * Detect every platform installed on this machine.\n *\n * Loads each registered adapter and calls its `detectInstalled(projectDir)`\n * (which probes the platform's native config dirs + marker files), returning\n * only those reported `installed === true`, in registry order. Adapters never\n * throw from detection, but we guard each call so one misbehaving adapter\n * cannot abort the whole probe.\n *\n * @param projectDir Resolved project root — lets project-scoped adapters look\n * for `<projectDir>/.mcp.json`, `<projectDir>/.cursor`, etc.\n */\nexport async function detectInstalledPlatforms(\n projectDir: string,\n): Promise<DetectedPlatform[]> {\n const adapters = await Promise.all(\n ADAPTER_REGISTRY.map(async (factory) => {\n try {\n return await factory.load();\n } catch {\n return undefined;\n }\n }),\n );\n\n const detected: DetectedPlatform[] = [];\n for (const adapter of adapters) {\n if (!adapter) continue;\n try {\n const result = adapter.detectInstalled(projectDir);\n if (result.installed) detected.push(result);\n } catch {\n // A single adapter's detection failure must not sink the whole probe.\n }\n }\n return detected;\n}\n\n/**\n * One env-marker rule for runtime-host detection. `vars` is checked in order;\n * the FIRST rule with any non-empty matching env var wins, so the table order\n * is the detection precedence (forks before parents). Each var is documented at\n * the rule so the source of the marker is auditable.\n */\ninterface RuntimeSignalRule {\n readonly platform: PlatformId;\n /** Env var names that prove \"this host is executing me\". */\n readonly vars: readonly string[];\n /** Human-readable note (printed in the DetectionSignal.reason). */\n readonly note: string;\n}\n\n/**\n * Ordered env-marker table. Forks BEFORE parents (cursor — a VS Code fork —\n * precedes any future vscode-copilot entry so a Cursor session inside a\n * VS Code-derived shell is not misclassified). Markers are verified against\n * each host's source:\n *\n * - codebuddy: CODEBUDDY_SESSION_ID / CODEBUDDY_PROJECT_DIR / CODEBUDDY_PLUGIN_ROOT\n * are CodeBuddy-exclusive (verified in @tencent-ai/codebuddy-code@2.109.0).\n * CodeBuddy is a Claude Code fork that ALSO exports CLAUDE_SESSION_ID\n * (its bundle sets both), so it MUST precede claude-code or a CodeBuddy\n * session would be misclassified as Claude Code (fork-before-parent).\n * - claude-code: CLAUDE_CODE_ENTRYPOINT is set on every Claude Code session;\n * CLAUDE_PLUGIN_ROOT is set when a plugin is loaded. Both are CC-exclusive\n * and serve as the disambiguators when CC runs inside another IDE's shell.\n * - codex: CODEX_THREAD_ID is set per exec (codex-rs/core exec_env);\n * CODEX_CI is set in CI mode (unified_exec process_manager).\n * - cursor: CURSOR_TRACE_ID is the widely-observed Cursor marker (Cursor\n * CLI / agent); CURSOR_CLI marks the Cursor terminal; CURSOR_CWD is the\n * documented workspace var.\n */\nconst RUNTIME_SIGNALS: readonly RuntimeSignalRule[] = [\n {\n platform: \"codebuddy\",\n vars: [\"CODEBUDDY_SESSION_ID\", \"CODEBUDDY_PROJECT_DIR\", \"CODEBUDDY_PLUGIN_ROOT\"],\n note: \"CodeBuddy env marker set\",\n },\n {\n platform: \"claude-code\",\n vars: [\"CLAUDE_CODE_ENTRYPOINT\", \"CLAUDE_PLUGIN_ROOT\", \"CLAUDE_PROJECT_DIR\", \"CLAUDE_SESSION_ID\"],\n note: \"Claude Code env marker set\",\n },\n {\n platform: \"cursor\",\n vars: [\"CURSOR_TRACE_ID\", \"CURSOR_CLI\", \"CURSOR_CWD\"],\n note: \"Cursor env marker set\",\n },\n {\n platform: \"codex\",\n vars: [\"CODEX_THREAD_ID\", \"CODEX_CI\"],\n note: \"Codex env marker set\",\n },\n];\n\n/**\n * Identify which host runtime is executing the current process from environment\n * markers. High confidence on an env-marker hit; otherwise an explicit\n * `AGENT_CONNECTOR_PLATFORM` override (high confidence, validated against the\n * registry) is honored; failing both, returns `unknown` at low confidence so\n * the caller can decide on a safe default.\n *\n * Pure with respect to its `env` argument (defaults to `process.env`) so it is\n * trivially testable — pass a synthetic env to assert each rule.\n */\nexport function detectRuntimeHost(env: NodeJS.ProcessEnv = process.env): DetectionSignal {\n // ── Explicit override (highest priority, validated against the registry) ──\n const override = env.AGENT_CONNECTOR_PLATFORM;\n if (override && override.trim() !== \"\") {\n if (REGISTERED_RUNTIME_IDS.has(override as PlatformId)) {\n return {\n platform: override as PlatformId,\n confidence: \"high\",\n reason: `AGENT_CONNECTOR_PLATFORM=${override} override`,\n };\n }\n }\n\n // ── Env-marker tier (forks before parents; first hit wins) ──────────────\n for (const rule of RUNTIME_SIGNALS) {\n const hit = rule.vars.find((name) => {\n const value = env[name];\n return typeof value === \"string\" && value !== \"\";\n });\n if (hit) {\n return {\n platform: rule.platform,\n confidence: \"high\",\n reason: `${rule.note} (${hit})`,\n };\n }\n }\n\n // ── Fallback: no host marker present ────────────────────────────────────\n return {\n platform: \"unknown\",\n confidence: \"low\",\n reason: \"no host runtime env marker detected\",\n };\n}\n\n/** Platform ids the runtime-signal table knows how to detect. */\nconst REGISTERED_RUNTIME_IDS: ReadonlySet<PlatformId> = new Set(\n RUNTIME_SIGNALS.map((rule) => rule.platform),\n);\n\n// Re-export for callers that resolve an adapter straight from a detection\n// result without reaching into registry.ts.\nexport { loadAdapter };\n"],"mappings":";;;;;;AAmCA,eAAsB,yBACpB,YAC6B;AAC7B,QAAM,WAAW,MAAM,QAAQ;AAAA,IAC7B,iBAAiB,IAAI,OAAO,YAAY;AACtC,UAAI;AACF,eAAO,MAAM,QAAQ,KAAK;AAAA,MAC5B,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,WAA+B,CAAC;AACtC,aAAW,WAAW,UAAU;AAC9B,QAAI,CAAC,QAAS;AACd,QAAI;AACF,YAAM,SAAS,QAAQ,gBAAgB,UAAU;AACjD,UAAI,OAAO,UAAW,UAAS,KAAK,MAAM;AAAA,IAC5C,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAoCA,IAAM,kBAAgD;AAAA,EACpD;AAAA,IACE,UAAU;AAAA,IACV,MAAM,CAAC,wBAAwB,yBAAyB,uBAAuB;AAAA,IAC/E,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM,CAAC,0BAA0B,sBAAsB,sBAAsB,mBAAmB;AAAA,IAChG,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM,CAAC,mBAAmB,cAAc,YAAY;AAAA,IACpD,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM,CAAC,mBAAmB,UAAU;AAAA,IACpC,MAAM;AAAA,EACR;AACF;AAYO,SAAS,kBAAkB,MAAyB,QAAQ,KAAsB;AAEvF,QAAM,WAAW,IAAI;AACrB,MAAI,YAAY,SAAS,KAAK,MAAM,IAAI;AACtC,QAAI,uBAAuB,IAAI,QAAsB,GAAG;AACtD,aAAO;AAAA,QACL,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,QAAQ,4BAA4B,QAAQ;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AAGA,aAAW,QAAQ,iBAAiB;AAClC,UAAM,MAAM,KAAK,KAAK,KAAK,CAAC,SAAS;AACnC,YAAM,QAAQ,IAAI,IAAI;AACtB,aAAO,OAAO,UAAU,YAAY,UAAU;AAAA,IAChD,CAAC;AACD,QAAI,KAAK;AACP,aAAO;AAAA,QACL,UAAU,KAAK;AAAA,QACf,YAAY;AAAA,QACZ,QAAQ,GAAG,KAAK,IAAI,KAAK,GAAG;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV;AACF;AAGA,IAAM,yBAAkD,IAAI;AAAA,EAC1D,gBAAgB,IAAI,CAAC,SAAS,KAAK,QAAQ;AAC7C;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/telemetry/store.ts","../src/telemetry/types.ts","../src/telemetry/tokenizer.ts"],"sourcesContent":["/**\n * telemetry/store — the default NDJSON {@link TelemetryStore} implementation.\n *\n * Local-first, dependency-free persistence. One JSON object per line; appends\n * are atomic enough for concurrent multi-host writers (a single `appendFileSync`\n * of a `<line>\\n` chunk is written in one `write(2)` for small payloads, so\n * interleaving across processes does not corrupt individual records). A\n * SQLite/WAL backend is a drop-in upgrade behind the same interface.\n *\n * Telemetry stores AGGREGATE COUNTS ONLY — raw tool arguments/results are never\n * persisted (enforced by the {@link ToolEventRecord} shape, which carries no\n * content fields).\n *\n * Global kill switch: AGENT_CONNECTOR_TELEMETRY=0 makes `append` a no-op so a\n * disabled telemetry layer can never touch disk or break a host's tool call.\n */\n\nimport { appendFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\nimport { ensureDir, telemetryPath } from \"../core/paths.js\";\nimport { worstConfidence } from \"./types.js\";\nimport type {\n QueryFilter,\n RollupRow,\n TelemetryStore,\n ToolEventRecord,\n} from \"./types.js\";\n\n/** Options for {@link openStore}. */\nexport interface OpenStoreOptions {\n /** Override the NDJSON file path. Defaults to telemetryPath(\"ndjson\"). */\n path?: string;\n}\n\n// Confidence ranking + worst-of comparison live in ./types (the single source\n// of truth) so a new ConfidenceSource value orders correctly everywhere.\n\n/** Is `process.env.AGENT_CONNECTOR_TELEMETRY` an explicit off switch? */\nfunction telemetryDisabled(): boolean {\n return process.env.AGENT_CONNECTOR_TELEMETRY === \"0\";\n}\n\n/**\n * A unique-enough record id. Combines the wall clock with a caller-supplied\n * sequence so two records written in the same millisecond still differ.\n */\nexport function newRecordId(seq: number): string {\n return `${Date.now()}-${seq}`;\n}\n\nclass NdjsonStore implements TelemetryStore {\n private readonly path: string;\n\n constructor(path: string) {\n this.path = path;\n }\n\n append(record: ToolEventRecord): void {\n if (telemetryDisabled()) return;\n ensureDir(dirname(this.path));\n appendFileSync(this.path, `${JSON.stringify(record)}\\n`, \"utf8\");\n }\n\n query(filter: QueryFilter): ToolEventRecord[] {\n if (!existsSync(this.path)) return [];\n\n let text: string;\n try {\n text = readFileSync(this.path, \"utf8\");\n } catch {\n return [];\n }\n\n const out: ToolEventRecord[] = [];\n for (const line of text.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed === \"\") continue;\n\n let record: ToolEventRecord;\n try {\n record = JSON.parse(trimmed) as ToolEventRecord;\n } catch {\n continue; // skip malformed lines\n }\n\n if (matches(record, filter)) out.push(record);\n }\n return out;\n }\n\n rollup(by: \"tool\" | \"session\" | \"project\", filter: QueryFilter): RollupRow[] {\n const groups = new Map<string, RollupRow>();\n\n for (const record of this.query(filter)) {\n const key = groupKey(record, by);\n const existing = groups.get(key);\n if (existing === undefined) {\n groups.set(key, {\n key,\n calls: 1,\n inputTokens: record.inputTokens,\n outputTokens: record.outputTokens,\n totalTokens: record.inputTokens + record.outputTokens,\n confidence: record.confidenceSource,\n lastTs: record.ts,\n });\n } else {\n existing.calls += 1;\n existing.inputTokens += record.inputTokens;\n existing.outputTokens += record.outputTokens;\n existing.totalTokens += record.inputTokens + record.outputTokens;\n existing.confidence = worstConfidence(\n existing.confidence,\n record.confidenceSource,\n );\n if (record.ts > existing.lastTs) existing.lastTs = record.ts;\n }\n }\n\n return [...groups.values()];\n }\n\n close(): void {\n /* no-op for the NDJSON backend */\n }\n}\n\n/** Does a record satisfy every set field of the filter? */\nfunction matches(record: ToolEventRecord, filter: QueryFilter): boolean {\n if (\n filter.connectorId !== undefined &&\n record.connectorId !== filter.connectorId\n ) {\n return false;\n }\n if (\n filter.projectKey !== undefined &&\n record.projectKey !== filter.projectKey\n ) {\n return false;\n }\n if (filter.sessionId !== undefined && record.sessionId !== filter.sessionId) {\n return false;\n }\n if (filter.toolName !== undefined && record.toolName !== filter.toolName) {\n return false;\n }\n if (filter.sinceMs !== undefined && record.ts < filter.sinceMs) {\n return false;\n }\n return true;\n}\n\n/** Resolve the grouping key value for a record under the requested dimension. */\nfunction groupKey(\n record: ToolEventRecord,\n by: \"tool\" | \"session\" | \"project\",\n): string {\n switch (by) {\n case \"tool\":\n return record.toolName;\n case \"session\":\n return record.sessionId;\n case \"project\":\n return record.projectDir;\n }\n}\n\n/**\n * Open the default local telemetry store. NDJSON backend; no native deps. The\n * returned store is safe to use even when telemetry is disabled — `append`\n * becomes a no-op and reads of a missing file return empty results.\n */\nexport function openStore(opts?: OpenStoreOptions): TelemetryStore {\n const path = opts?.path ?? telemetryPath(\"ndjson\");\n return new NdjsonStore(path);\n}\n","/**\n * telemetry/types — runtime telemetry contracts.\n *\n * The framework measures the MCP server's OWN bytes (tool args in, tool result\n * out, tool-definition schemas) and tokenizes them locally — the only signal\n * identical across every host (no host reports per-tool usage to a server).\n * Records are aggregate counts only; raw arguments/results are NEVER stored.\n */\n\nimport type { PlatformId } from \"../core/types.js\";\n\n/** Provenance of a token count — surfaced so \"estimate\" is never read as \"exact\". */\nexport type ConfidenceSource =\n | \"tokenizer-exact\" // real BPE for a matching model family\n | \"tokenizer-calibrated\" // approx adjusted by a sampled Anthropic count_tokens factor\n | \"tokenizer-approx\" // real BPE used as a documented approximation (e.g. Anthropic)\n | \"heuristic\" // chars/4 fallback\n | \"host-native\"; // a host actually reported usage (e.g. Gemini AfterModel)\n\n/**\n * Confidence ranking, least-trustworthy (0) → most-trustworthy. The single\n * source of truth for ordering {@link ConfidenceSource} values; every\n * worst-of comparison across the telemetry subsystem (store rollup, both\n * leaderboards, report, measure) reads through {@link rankOf} /\n * {@link worstConfidence} so a new value orders correctly everywhere.\n *\n * heuristic < tokenizer-approx < tokenizer-calibrated < tokenizer-exact < host-native\n *\n * `tokenizer-calibrated` sits between the raw approximation and an exact\n * family-matched BPE count: it is an approximation nudged toward truth by a\n * sampled real count_tokens factor, so it is more trustworthy than bare approx\n * but still not the exact encoding for the target family.\n */\nexport const CONFIDENCE_RANK: Record<ConfidenceSource, number> = {\n heuristic: 0,\n \"tokenizer-approx\": 1,\n \"tokenizer-calibrated\": 2,\n \"tokenizer-exact\": 3,\n \"host-native\": 4,\n};\n\n/** Trust rank of a single {@link ConfidenceSource} (higher = more trustworthy). */\nexport function rankOf(c: ConfidenceSource): number {\n return CONFIDENCE_RANK[c];\n}\n\n/** Return whichever source is the worse (least-confident) of the two. */\nexport function worstConfidence(\n a: ConfidenceSource,\n b: ConfidenceSource,\n): ConfidenceSource {\n return CONFIDENCE_RANK[b] < CONFIDENCE_RANK[a] ? b : a;\n}\n\nexport type ModelFamily = \"openai\" | \"anthropic\" | \"generic\";\n\nexport interface TokenCount {\n tokens: number;\n source: ConfidenceSource;\n}\n\n/** Pluggable tokenizer. Default impl uses gpt-tokenizer; fallback is chars/4. */\nexport interface Tokenizer {\n /** Count tokens for a string under the given model family. */\n count(text: string, family: ModelFamily): TokenCount;\n /** Count tokens for an arbitrary JSON value (serialized canonically first). */\n countValue(value: unknown, family: ModelFamily): TokenCount;\n}\n\n/**\n * What a record measures. DISTINCT origins that must never be summed:\n * • `call` — one per-MCP `tools/call` round-trip (serve-proxy bytes).\n * • `tool_defs` — the one-time `tools/list` schema overhead (serve-proxy).\n * • `model_turn` — a WHOLE-CONVERSATION host-native turn reported by the host\n * (e.g. Gemini/Antigravity AfterModel `usageMetadata`). This is NOT per-MCP:\n * it covers the entire model turn, so the MCP leaderboard EXCLUDES it (just\n * as it treats `tool_defs` specially) and it is surfaced as its own labeled\n * section rather than added to the per-MCP `call` totals or to the\n * usage-reader host-scan numbers.\n * • `hook` — one RUNTIME hook dispatch through the home-bin `hook`\n * entrypoint (src/runtime/hook-entrypoint). The framework tokenizes the\n * inbound normalized event payload the handler reads (input) and what the\n * handler returns that becomes context/decision (output). The per-item\n * `toolName` is the hook EVENT name (e.g. \"SessionStart\"). This is the\n * developer-axis \"hook\" surface — measured live, like `call`.\n */\nexport type EventScope = \"call\" | \"tool_defs\" | \"model_turn\" | \"hook\";\n\n/**\n * Which of the FIVE developer-axis surfaces a record (or footprint) belongs to.\n * `server`/`hook` are RUNTIME-measured surfaces that produce {@link ToolEventRecord}\n * store rows; `command`/`skill`/`subagent` are STATIC footprints computed from the\n * registered connector (the host loads them as context — we never intercept them,\n * so they are NEVER written as usage rows). OPTIONAL on the record so rows written\n * before this field existed (every legacy serve-proxy row) read as `server`.\n */\nexport type SurfaceKind = \"server\" | \"hook\" | \"command\" | \"skill\" | \"subagent\";\n\n/**\n * The install scope a wrapped server was deployed under, narrowed to the two\n * dimensions that matter for slicing telemetry: a `user`-global install vs a\n * `project`-local one. The framework's broader {@link InstallScope}\n * (`system|user|project|profile|managed`) is mapped down to this at wrap time —\n * everything that is not project-local reads as `user`. Optional on the record\n * so rows written before this field existed are treated as \"unknown\".\n */\nexport type TelemetryInstallScope = \"user\" | \"project\";\n\n/**\n * How the real MCP server underneath the proxy was launched. `npx`/`bunx`/`uvx`\n * are ephemeral package runners; `node`/`bun`/`deno` are interpreters running a\n * local script; `binary` is a resolved executable on PATH; `http` marks a remote\n * server reached over the network (no local launch); `unknown` is the honest\n * fallback. Optional on the record so older rows read as \"unknown\".\n */\nexport type LaunchMethod =\n | \"npx\"\n | \"bunx\"\n | \"uvx\"\n | \"node\"\n | \"binary\"\n | \"http\"\n | \"unknown\";\n\n/**\n * One telemetry row. Aggregate counts only — no content. `projectKey` is the\n * hashed stable project identity (git remote || normalized abs path); `projectDir`\n * is kept human-readable for reports but is the same partition.\n */\nexport interface ToolEventRecord {\n /** Monotonic-ish unique id (e.g. `${ts}-${seq}`). */\n id: string;\n /** Epoch milliseconds. */\n ts: number;\n connectorId: string;\n toolName: string;\n scope: EventScope;\n hostPlatform: PlatformId;\n sessionId: string;\n projectKey: string;\n projectDir: string;\n inputTokens: number;\n outputTokens: number;\n confidenceSource: ConfidenceSource;\n isError: boolean;\n /**\n * The (narrowed) install scope the wrapped server was deployed under — a\n * slicing dimension for global(user) vs project usage. OPTIONAL: rows written\n * before this field existed lack it and must be read as \"unknown\".\n */\n installScope?: TelemetryInstallScope;\n /**\n * How the real server was launched (npx/bunx/uvx/node/binary/http) — the\n * \"launch-method\" slicing dimension. OPTIONAL: older rows lack it → \"unknown\".\n */\n launchMethod?: LaunchMethod;\n /**\n * Which developer-axis surface produced this row. OPTIONAL and\n * backward-compatible: rows written before this field existed (every legacy\n * serve-proxy `call`/`tool_defs` row) lack it and MUST be read as `server`.\n * Runtime rows stamp it explicitly (`server` for the proxy, `hook` for the\n * hook runtime); the static command/skill/subagent surfaces never produce\n * store rows (they are reported as footprints — see surface-footprint.ts).\n */\n surfaceKind?: SurfaceKind;\n}\n\nexport interface QueryFilter {\n connectorId?: string;\n projectKey?: string;\n sessionId?: string;\n toolName?: string;\n /** Lower bound epoch ms (inclusive). */\n sinceMs?: number;\n}\n\n/** A grouped rollup row for reports. */\nexport interface RollupRow {\n /** The grouping key value (tool name, session id, or project dir). */\n key: string;\n calls: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n /** Worst (least-confident) source seen in the group, for honest labeling. */\n confidence: ConfidenceSource;\n lastTs: number;\n}\n\n/**\n * Local-first telemetry store. The default NDJSON implementation appends one\n * line per record (append is atomic enough for concurrent multi-host writers);\n * a SQLite/WAL implementation is a drop-in upgrade behind this same interface.\n */\nexport interface TelemetryStore {\n append(record: ToolEventRecord): void;\n query(filter: QueryFilter): ToolEventRecord[];\n /** Group + aggregate by tool | session | project. */\n rollup(by: \"tool\" | \"session\" | \"project\", filter: QueryFilter): RollupRow[];\n close(): void;\n}\n","/**\n * telemetry/tokenizer — the default {@link Tokenizer} implementation.\n *\n * Token counts are the only signal that is identical across every host: no host\n * reports a server's per-tool usage back to the server, so the framework\n * tokenizes its OWN bytes locally. We use gpt-tokenizer's `o200k_base` encoding\n * (the GPT-4o / o1 BPE) for all families:\n * • openai → exact BPE for the target family → \"tokenizer-exact\"\n * • anthropic → o200k_base used as a documented stand-in → \"tokenizer-approx\"\n * • generic → o200k_base used as a generic approximation → \"tokenizer-approx\"\n *\n * gpt-tokenizer is loaded LAZILY on first use, inside a try/catch. If the module\n * (or its BPE data) fails to load we degrade to a chars/4 heuristic that never\n * throws, so telemetry can never break a host's tool call.\n *\n * Pure + side-effect-free apart from the one-time lazy encoder load. No IO.\n */\n\nimport { createRequire } from \"node:module\";\n\nimport type {\n ConfidenceSource,\n ModelFamily,\n TokenCount,\n Tokenizer,\n} from \"./types.js\";\n\n/**\n * A CommonJS `require` bound to this module's URL. gpt-tokenizer ships its\n * encoding entry points as CJS (the package `exports` `require` condition maps\n * `./encoding/*` to `./cjs/encoding/*.js`), so we resolve them synchronously via\n * createRequire — this keeps the {@link Tokenizer} surface synchronous and works\n * under ESM / NodeNext without baking an absolute path.\n */\nconst requireCjs = createRequire(import.meta.url);\n\n/** Minimal shape we use from a gpt-tokenizer encoding module. */\ninterface EncodingModule {\n encode(text: string): number[];\n}\n\n/**\n * Lazy-load state for the shared o200k_base encoder.\n * - undefined → not attempted yet\n * - null → load attempted and failed (stick to the heuristic)\n * - object → loaded encoder\n */\nlet encoderState: EncodingModule | null | undefined;\n\n/** Average characters-per-token for the chars/4 fallback heuristic. */\nconst HEURISTIC_CHARS_PER_TOKEN = 4;\n\n/**\n * Lazily import the o200k_base encoding. Returns the encoder, or null if the\n * module/data could not be loaded (in which case callers use the heuristic).\n *\n * The dynamic import resolves to `gpt-tokenizer/esm/encoding/o200k_base.js` via\n * the package `exports` map and exposes a named `encode(text) => number[]`.\n */\nfunction loadEncoder(): EncodingModule | null {\n if (encoderState !== undefined) return encoderState;\n try {\n // Synchronous require keeps count() a sync function (per the Tokenizer\n // interface). The package's `require` export condition resolves this to\n // gpt-tokenizer/cjs/encoding/o200k_base.js, which exports a named `encode`.\n const mod = requireCjs(\"gpt-tokenizer/encoding/o200k_base\") as {\n encode?: (text: string) => number[];\n };\n if (mod && typeof mod.encode === \"function\") {\n encoderState = { encode: mod.encode.bind(mod) };\n } else {\n encoderState = null;\n }\n } catch {\n encoderState = null;\n }\n return encoderState;\n}\n\n/** Heuristic chars/4 count — used when no encoder is available. Never throws. */\nfunction heuristicCount(text: string): TokenCount {\n return {\n tokens: Math.ceil(text.length / HEURISTIC_CHARS_PER_TOKEN),\n source: \"heuristic\",\n };\n}\n\n/**\n * Confidence label for a successful BPE count: exact only for the OpenAI family\n * (the encoding actually matches), approximate for everything else.\n */\nfunction bpeSource(family: ModelFamily): ConfidenceSource {\n return family === \"openai\" ? \"tokenizer-exact\" : \"tokenizer-approx\";\n}\n\n/**\n * The default tokenizer. A single shared instance is fine: the encoder is\n * stateless across calls and lazy-loaded once.\n */\nclass DefaultTokenizer implements Tokenizer {\n count(text: string, family: ModelFamily): TokenCount {\n const encoder = loadEncoder();\n if (!encoder) return heuristicCount(text);\n try {\n return { tokens: encoder.encode(text).length, source: bpeSource(family) };\n } catch {\n // A per-call encode failure (e.g. a pathological input) still degrades\n // gracefully rather than breaking the host's tool call.\n return heuristicCount(text);\n }\n }\n\n countValue(value: unknown, family: ModelFamily): TokenCount {\n return this.count(canonicalString(value), family);\n }\n}\n\n/**\n * Canonical string form of an arbitrary JSON value for counting.\n * `undefined` (and anything that JSON.stringify drops to undefined, e.g. a bare\n * function or symbol) serializes to the empty string.\n */\nfunction canonicalString(value: unknown): string {\n if (typeof value === \"string\") return value;\n const json = JSON.stringify(value);\n return json ?? \"\";\n}\n\nconst SHARED_TOKENIZER: Tokenizer = new DefaultTokenizer();\n\n/** Return the default {@link Tokenizer} (shared singleton). */\nexport function getTokenizer(): Tokenizer {\n return SHARED_TOKENIZER;\n}\n\n/**\n * Infer the {@link ModelFamily} for token counting.\n *\n * An explicit hint other than \"auto\" wins outright. Otherwise we sniff the\n * client/host name: Anthropic/Claude → anthropic, OpenAI/GPT/Codex → openai,\n * Gemini and everything unrecognized → generic.\n */\nexport function inferModelFamily(\n clientName: string,\n hint: \"auto\" | ModelFamily,\n): ModelFamily {\n if (hint !== \"auto\") return hint;\n const name = clientName.toLowerCase();\n if (name.includes(\"claude\") || name.includes(\"anthropic\")) return \"anthropic\";\n if (\n name.includes(\"codex\") ||\n name.includes(\"gpt\") ||\n name.includes(\"openai\")\n ) {\n return \"openai\";\n }\n // gemini and all others fall through to the generic approximation.\n return \"generic\";\n}\n"],"mappings":"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","names":[]}
|