@ken-jo/agent-connector 0.4.94 → 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 -485
- package/dist/{action-VW5SACXY.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-KSPMSRVU.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-KJLFGQQM.js → chunk-7ZFK2WEO.js} +8 -6
- 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-EHP7WISI.js → chunk-FHYR5Q5T.js} +19 -14
- package/dist/chunk-FHYR5Q5T.js.map +1 -0
- package/dist/{chunk-I5TO42QC.js → chunk-GCPEPUD7.js} +8 -8
- package/dist/chunk-GCPEPUD7.js.map +1 -0
- package/dist/{chunk-ZMAJ2XFD.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-P23RVIUY.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-B5W3RHTI.js → chunk-LQ55E7QI.js} +7 -7
- package/dist/chunk-LQ55E7QI.js.map +1 -0
- package/dist/{chunk-TTKSYJRY.js → chunk-MLU7NDAZ.js} +12 -12
- package/dist/chunk-MLU7NDAZ.js.map +1 -0
- package/dist/{chunk-TQRLMA2C.js → chunk-N4ENLZEU.js} +22 -19
- package/dist/chunk-N4ENLZEU.js.map +1 -0
- package/dist/{chunk-SILKSP2Y.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-2RIFORQS.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-DUZNOAS2.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/{define-connector-LQFJL_at.d.ts → define-connector-DTWb46UB.d.ts} +1 -1
- package/dist/{detect-P5OA5UYM.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-2O2Q6MIB.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-4N3BMKIK.js → hook-VHICXHXL.js} +16 -16
- package/dist/hook-VHICXHXL.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +5 -5
- package/dist/{install-NPN77N3S.js → install-WLENR4KF.js} +192 -28
- package/dist/install-WLENR4KF.js.map +1 -0
- package/dist/{introspect-D4fEGuhN.d.ts → introspect-dNekqPDY.d.ts} +1 -1
- 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-QKB36OBR.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-Q6DV5247.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.d.ts +1 -1
- package/dist/runtime/index.js +14 -14
- package/dist/sdk/index.d.ts +4 -4
- package/dist/sdk/index.js +6 -6
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/test.d.ts +2 -2
- package/dist/sdk/test.js +11 -11
- package/dist/{serve-MJTTXDX4.js → serve-LYSBH43G.js} +16 -16
- package/dist/serve-LYSBH43G.js.map +1 -0
- package/dist/{status-Q3UY4NZU.js → status-PHKKGEW5.js} +11 -11
- package/dist/status-PHKKGEW5.js.map +1 -0
- package/dist/{statusline-QF4G724E.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-JHVRDDNK.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/{types-BhcUEIJJ.d.ts → types-JyhpJHJa.d.ts} +4 -2
- package/dist/{uninstall-H2SOV3AP.js → uninstall-RWO2GM2I.js} +21 -21
- package/dist/uninstall-RWO2GM2I.js.map +1 -0
- package/dist/{upgrade-UCSG7L5U.js → upgrade-YJCXNUEE.js} +21 -21
- package/dist/upgrade-YJCXNUEE.js.map +1 -0
- package/dist/{usage-4EGIJ4TM.js → usage-IWO4RQN6.js} +4 -4
- package/dist/usage-IWO4RQN6.js.map +1 -0
- package/dist/{usage-event-GRQE7XWT.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-VW5SACXY.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-2RIFORQS.js.map +0 -1
- package/dist/chunk-2Y2MZRLP.js.map +0 -1
- package/dist/chunk-4NHXLXSD.js.map +0 -1
- package/dist/chunk-6TF7MHG5.js.map +0 -1
- package/dist/chunk-6WZODR3Z.js.map +0 -1
- package/dist/chunk-B5W3RHTI.js.map +0 -1
- package/dist/chunk-BEFVJ2OE.js.map +0 -1
- package/dist/chunk-EHP7WISI.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-H53QZQJI.js.map +0 -1
- package/dist/chunk-I5TO42QC.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-KJLFGQQM.js.map +0 -1
- package/dist/chunk-KSPMSRVU.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-P23RVIUY.js.map +0 -1
- package/dist/chunk-RK6VBLXP.js.map +0 -1
- package/dist/chunk-SILKSP2Y.js.map +0 -1
- package/dist/chunk-TQRLMA2C.js.map +0 -1
- package/dist/chunk-TTKSYJRY.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-YT43MUSJ.js.map +0 -1
- package/dist/chunk-Z2MRSS2I.js.map +0 -1
- package/dist/chunk-ZMAJ2XFD.js.map +0 -1
- package/dist/claude-code-DUZNOAS2.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-P5OA5UYM.js.map +0 -1
- package/dist/devin-QMEM3OFA.js.map +0 -1
- package/dist/doctor-2O2Q6MIB.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-4N3BMKIK.js.map +0 -1
- package/dist/install-NPN77N3S.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-QKB36OBR.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-Q6DV5247.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-MJTTXDX4.js.map +0 -1
- package/dist/status-Q3UY4NZU.js.map +0 -1
- package/dist/statusline-QF4G724E.js.map +0 -1
- package/dist/synthetic-6FRA7P5Q.js.map +0 -1
- package/dist/telemetry-JHVRDDNK.js.map +0 -1
- package/dist/trae-CPPYASR2.js.map +0 -1
- package/dist/trae-UOYD6JZZ.js.map +0 -1
- package/dist/uninstall-H2SOV3AP.js.map +0 -1
- package/dist/upgrade-UCSG7L5U.js.map +0 -1
- package/dist/usage-4EGIJ4TM.js.map +0 -1
- package/dist/usage-event-GRQE7XWT.js.map +0 -1
- package/dist/vscode-copilot-NIMH2DGU.js.map +0 -1
- package/dist/warp-NRBBJQR2.js.map +0 -1
- package/dist/warp-QYJGKURM.js.map +0 -1
- package/dist/windsurf-75LLZ37C.js.map +0 -1
- package/dist/zed-HKAZXEJW.js.map +0 -1
- package/dist/zed-XJFOOKCO.js.map +0 -1
- /package/dist/{antigravity-VXLDLTO7.js.map → antigravity-TWIWXTQH.js.map} +0 -0
- /package/dist/{cursor-GSLUET3V.js.map → cursor-W7I4QUFO.js.map} +0 -0
- /package/dist/{openclaw-7EYBQS4R.js.map → openclaw-IJ33K45M.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/usage/readers/antigravity-shared.ts"],"sourcesContent":["/**\r\n * usage/readers/antigravity-shared — logic shared by the Antigravity IDE\r\n * (`antigravity`) and Antigravity CLI (`antigravity-cli`) native usage readers.\r\n *\r\n * Both surfaces persist per-conversation transcripts as `transcript*.jsonl`\r\n * under a `brain/` subtree of their global dir. Each assistant turn embeds a\r\n * Gemini-style `usage_metadata` block (camelCase, same field names the Gemini\r\n * API returns):\r\n * promptTokenCount → input (cache-INCLUSIVE, like the Gemini API)\r\n * candidatesTokenCount → output\r\n * cachedContentTokenCount → cacheRead (the cached portion already inside prompt)\r\n * thoughtsTokenCount → reasoning\r\n * cacheWrite is not reported by Gemini → always 0.\r\n *\r\n * Because promptTokenCount is cache-inclusive, net input = prompt - cached\r\n * (clamped ≥ 0), mirroring the gemini-cli reader's subtract_cached_overlap.\r\n *\r\n * Confidence is MEDIUM on the native JSONL shape (Antigravity is fast-moving and\r\n * its docs are JS-rendered): the field names above are the documented/observed\r\n * shape but versions may differ, so extraction is BEST-EFFORT and every caller\r\n * fails open to [] when the store is absent or a row does not match. We also\r\n * tolerate the snake_case spellings as a secondary alias set.\r\n *\r\n * The model-alias table is shared with — and kept in sync with — the tokscale\r\n * pricing::aliases::resolve_alias port (sessions/antigravity.rs).\r\n */\r\n\r\nimport { emptyTokens } from \"../aggregate.js\";\r\nimport { inferProvider } from \"../normalize.js\";\r\nimport type { PlatformId } from \"../../core/types.js\";\r\nimport type { UsageRecord } from \"../types.js\";\r\n\r\nconst DEFAULT_MODEL = \"unknown\";\r\nconst DEFAULT_PROVIDER = \"antigravity\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Model alias table (port of tokscale pricing::aliases::resolve_alias).\r\n// Keys are lowercased; lookup lowercases the input. Resolves placeholder /\r\n// reseller model ids (e.g. Antigravity's MODEL_PLACEHOLDER_*) to canonical ids.\r\n// Shared by both Antigravity readers.\r\n// ─────────────────────────────────────────────────────────────────────────\r\nexport const MODEL_ALIASES: ReadonlyMap<string, string> = new Map([\r\n [\"big-pickle\", \"glm-4.7\"],\r\n [\"big pickle\", \"glm-4.7\"],\r\n [\"bigpickle\", \"glm-4.7\"],\r\n [\"k2p5\", \"kimi-k2-thinking\"],\r\n [\"k2-p5\", \"kimi-k2-thinking\"],\r\n [\"k2p6\", \"kimi-k2.6\"],\r\n [\"k2-p6\", \"kimi-k2.6\"],\r\n [\"kimi-k2p6\", \"kimi-k2.6\"],\r\n [\"kimi-k2.5-thinking\", \"kimi-k2-thinking\"],\r\n [\"kimi-for-coding\", \"kimi-k2.5\"],\r\n [\"model_placeholder_m26\", \"claude-opus-4-6\"],\r\n [\"model_placeholder_m35\", \"claude-sonnet-4-6\"],\r\n [\"model_placeholder_m36\", \"gemini-3.1-pro\"],\r\n [\"model_placeholder_m37\", \"gemini-3.1-pro\"],\r\n [\"model_placeholder_m47\", \"gemini-3-flash-preview\"],\r\n [\"model_openai_gpt_oss_120b_medium\", \"gpt-oss-120b-medium\"],\r\n [\"claude-opus-4-6-thinking\", \"claude-opus-4-6\"],\r\n [\"claude-sonnet-4-6-thinking\", \"claude-sonnet-4-6\"],\r\n [\"claude-opus-4.6-thinking\", \"claude-opus-4-6\"],\r\n [\"claude-sonnet-4.6-thinking\", \"claude-sonnet-4-6\"],\r\n [\"claude-opus-4-6\", \"claude-opus-4-6\"],\r\n [\"claude-sonnet-4-6\", \"claude-sonnet-4-6\"],\r\n [\"claude-haiku-4-6\", \"claude-haiku-4-6\"],\r\n [\"claude-opus-4.6\", \"claude-opus-4-6\"],\r\n [\"claude-sonnet-4.6\", \"claude-sonnet-4-6\"],\r\n [\"claude-haiku-4.6\", \"claude-haiku-4-6\"],\r\n [\"anthropic/claude-4-5-opus\", \"claude-opus-4-5\"],\r\n [\"anthropic/claude-4-5-sonnet\", \"claude-sonnet-4-5\"],\r\n [\"anthropic/claude-4-5-haiku\", \"claude-haiku-4-5\"],\r\n [\"anthropic/claude-4-6-opus\", \"claude-opus-4-6\"],\r\n [\"anthropic/claude-4-6-sonnet\", \"claude-sonnet-4-6\"],\r\n [\"anthropic/claude-4-6-haiku\", \"claude-haiku-4-6\"],\r\n [\"gemini-3.1-pro-high\", \"gemini-3.1-pro\"],\r\n [\"gemini-3.1-pro-low\", \"gemini-3.1-pro\"],\r\n [\"gemini-3-pro-high\", \"gemini-3-pro\"],\r\n [\"gemini-3-pro-low\", \"gemini-3-pro\"],\r\n [\"gemini-3-flash\", \"gemini-3-flash-preview\"],\r\n [\"gemini-3-flash-c\", \"gemini-3-flash-preview\"],\r\n [\"kimi-k2.5-nvfp4\", \"kimi-k2.5\"],\r\n [\"kimi-k2-instruct-0905\", \"kimi-k2.5\"],\r\n]);\r\n\r\n/** Resolve a model alias to its canonical id, or undefined when none matches. */\r\nexport function resolveAlias(modelId: string): string | undefined {\r\n return MODEL_ALIASES.get(modelId.toLowerCase());\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Scalar coercion (shared with the tokscale to_safe_i64 contract)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Coerce an unknown to a non-negative i64-safe integer (port of to_safe_i64):\r\n * accepts number or numeric string, floors toward zero, clamps at 0; 0 otherwise.\r\n */\r\nexport function toSafeInt(v: unknown): number {\r\n const n =\r\n typeof v === \"number\"\r\n ? v\r\n : typeof v === \"string\"\r\n ? Number(v.trim())\r\n : NaN;\r\n if (!Number.isFinite(n)) return 0;\r\n return Math.max(0, Math.trunc(n));\r\n}\r\n\r\n/** A non-empty trimmed string, or undefined (port of the `.filter(!is_empty)` guards). */\r\nexport function nonEmptyStr(v: unknown): string | undefined {\r\n if (typeof v !== \"string\") return undefined;\r\n return v.trim() === \"\" ? undefined : v;\r\n}\r\n\r\n/** First key whose value coerces to a positive-or-zero integer (ignoring absent/zero). */\r\nfunction firstInt(obj: Record<string, unknown>, keys: readonly string[]): number {\r\n for (const k of keys) {\r\n const v = obj[k];\r\n if (v === undefined || v === null) continue;\r\n const n = toSafeInt(v);\r\n if (n > 0) return n;\r\n }\r\n return 0;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Native-transcript usage_metadata extraction (best-effort, MEDIUM confidence)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** The camelCase Gemini field names + snake_case secondary aliases. */\r\nconst PROMPT_KEYS = [\"promptTokenCount\", \"prompt_token_count\"] as const;\r\nconst CANDIDATES_KEYS = [\"candidatesTokenCount\", \"candidates_token_count\"] as const;\r\nconst CACHED_KEYS = [\"cachedContentTokenCount\", \"cached_content_token_count\"] as const;\r\nconst THOUGHTS_KEYS = [\"thoughtsTokenCount\", \"thoughts_token_count\"] as const;\r\n\r\n/** A `usage_metadata` object pulled off a transcript row (any spelling). */\r\nfunction findUsageMetadata(row: Record<string, unknown>): Record<string, unknown> | undefined {\r\n for (const k of [\"usage_metadata\", \"usageMetadata\"]) {\r\n const v = row[k];\r\n if (typeof v === \"object\" && v !== null && !Array.isArray(v)) {\r\n return v as Record<string, unknown>;\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\n/** Pull a model id off a transcript row, tolerant of common spellings. */\r\nfunction findModelId(row: Record<string, unknown>): string | undefined {\r\n return (\r\n nonEmptyStr(row.modelId) ??\r\n nonEmptyStr(row.model) ??\r\n nonEmptyStr(row.model_id) ??\r\n nonEmptyStr(row.modelName) ??\r\n nonEmptyStr(row.model_name)\r\n );\r\n}\r\n\r\n/** Pull a session/conversation id off a row, tolerant of common spellings. */\r\nfunction findSessionId(row: Record<string, unknown>): string | undefined {\r\n return (\r\n nonEmptyStr(row.sessionId) ??\r\n nonEmptyStr(row.session_id) ??\r\n nonEmptyStr(row.conversationId) ??\r\n nonEmptyStr(row.conversation_id)\r\n );\r\n}\r\n\r\n/** Options carried per-file so a transcript with no per-row model/session can borrow them. */\r\nexport interface UsageMetadataContext {\r\n /** Platform attribution for emitted records. */\r\n platformId: PlatformId;\r\n /** Session id to attribute rows to when the row itself carries none. */\r\n fallbackSessionId: string;\r\n /** Per-conversation model fallback (e.g. from a session_meta row), if known. */\r\n fallbackModel?: string;\r\n /** Timestamp (epoch ms) to use when the row carries none — never drops a row for it. */\r\n fallbackTs: number;\r\n}\r\n\r\n/**\r\n * Best-effort parse of one native brain-transcript JSONL row into a usage record,\r\n * or undefined when the row has no `usage_metadata` / no tokens. Mirrors the\r\n * gemini-cli cache-inclusive normalization: net input = prompt - cached (≥ 0).\r\n *\r\n * Fail-soft: any shape that does not match yields undefined (the caller skips it).\r\n */\r\nexport function parseUsageMetadataRow(\r\n row: unknown,\r\n ctx: UsageMetadataContext,\r\n): UsageRecord | undefined {\r\n if (typeof row !== \"object\" || row === null || Array.isArray(row)) return undefined;\r\n const obj = row as Record<string, unknown>;\r\n\r\n const meta = findUsageMetadata(obj);\r\n if (meta === undefined) return undefined;\r\n\r\n const promptTokens = firstInt(meta, PROMPT_KEYS);\r\n const output = firstInt(meta, CANDIDATES_KEYS);\r\n const cacheRead = firstInt(meta, CACHED_KEYS);\r\n const reasoning = firstInt(meta, THOUGHTS_KEYS);\r\n\r\n // promptTokenCount is cache-inclusive → net input = prompt - cached (≥ 0).\r\n const input = Math.max(0, promptTokens - Math.min(cacheRead, promptTokens));\r\n\r\n if (input === 0 && output === 0 && cacheRead === 0 && reasoning === 0) return undefined;\r\n\r\n const rawModel = findModelId(obj) ?? ctx.fallbackModel ?? DEFAULT_MODEL;\r\n const modelId = resolveAlias(rawModel) ?? rawModel;\r\n const providerId = inferProvider(modelId) ?? DEFAULT_PROVIDER;\r\n\r\n const sessionId = findSessionId(obj) ?? ctx.fallbackSessionId;\r\n\r\n const ts = parseRowTimestamp(obj) ?? ctx.fallbackTs;\r\n\r\n const tokens = emptyTokens();\r\n tokens.input = input;\r\n tokens.output = output;\r\n tokens.cacheRead = cacheRead;\r\n tokens.cacheWrite = 0;\r\n tokens.reasoning = reasoning;\r\n\r\n const record: UsageRecord = {\r\n platformId: ctx.platformId,\r\n modelId,\r\n providerId,\r\n sessionId,\r\n tokens,\r\n ts,\r\n messageCount: 1,\r\n confidence: \"host-reported\",\r\n };\r\n\r\n const dedupKey = nonEmptyStr(obj.responseId) ?? nonEmptyStr(obj.response_id) ?? nonEmptyStr(obj.id);\r\n if (dedupKey !== undefined) record.dedupKey = `${sessionId}:${dedupKey}`;\r\n\r\n return record;\r\n}\r\n\r\n/** Parse a per-row timestamp (epoch ms / s, or RFC3339 string), else undefined. */\r\nfunction parseRowTimestamp(obj: Record<string, unknown>): number | undefined {\r\n const v = obj.timestamp ?? obj.ts ?? obj.created_at ?? obj.createdAt;\r\n if (typeof v === \"number\" && Number.isFinite(v)) {\r\n const n = Math.trunc(v);\r\n if (n <= 0) return undefined;\r\n return n >= 1_000_000_000_000 ? n : n * 1000;\r\n }\r\n if (typeof v === \"string\") {\r\n const ms = Date.parse(v);\r\n if (!Number.isNaN(ms)) return ms;\r\n const numeric = Number(v.trim());\r\n if (Number.isInteger(numeric) && numeric > 0) {\r\n return numeric >= 1_000_000_000_000 ? numeric : numeric * 1000;\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\n/** A per-conversation model fallback off a `session_meta` row, else undefined. */\r\nexport function sessionMetaModel(row: unknown): string | undefined {\r\n if (typeof row !== \"object\" || row === null || Array.isArray(row)) return undefined;\r\n const obj = row as Record<string, unknown>;\r\n const rowType = nonEmptyStr(obj.type);\r\n if (rowType !== \"session_meta\") return undefined;\r\n return findModelId(obj);\r\n}\r\n"],"mappings":";;;AAyCO,IAAM,gBAA6C,oBAAI,IAAI;AAAA,EAChE,CAAC,cAAc,SAAS;AAAA,EACxB,CAAC,cAAc,SAAS;AAAA,EACxB,CAAC,aAAa,SAAS;AAAA,EACvB,CAAC,QAAQ,kBAAkB;AAAA,EAC3B,CAAC,SAAS,kBAAkB;AAAA,EAC5B,CAAC,QAAQ,WAAW;AAAA,EACpB,CAAC,SAAS,WAAW;AAAA,EACrB,CAAC,aAAa,WAAW;AAAA,EACzB,CAAC,sBAAsB,kBAAkB;AAAA,EACzC,CAAC,mBAAmB,WAAW;AAAA,EAC/B,CAAC,yBAAyB,iBAAiB;AAAA,EAC3C,CAAC,yBAAyB,mBAAmB;AAAA,EAC7C,CAAC,yBAAyB,gBAAgB;AAAA,EAC1C,CAAC,yBAAyB,gBAAgB;AAAA,EAC1C,CAAC,yBAAyB,wBAAwB;AAAA,EAClD,CAAC,oCAAoC,qBAAqB;AAAA,EAC1D,CAAC,4BAA4B,iBAAiB;AAAA,EAC9C,CAAC,8BAA8B,mBAAmB;AAAA,EAClD,CAAC,4BAA4B,iBAAiB;AAAA,EAC9C,CAAC,8BAA8B,mBAAmB;AAAA,EAClD,CAAC,mBAAmB,iBAAiB;AAAA,EACrC,CAAC,qBAAqB,mBAAmB;AAAA,EACzC,CAAC,oBAAoB,kBAAkB;AAAA,EACvC,CAAC,mBAAmB,iBAAiB;AAAA,EACrC,CAAC,qBAAqB,mBAAmB;AAAA,EACzC,CAAC,oBAAoB,kBAAkB;AAAA,EACvC,CAAC,6BAA6B,iBAAiB;AAAA,EAC/C,CAAC,+BAA+B,mBAAmB;AAAA,EACnD,CAAC,8BAA8B,kBAAkB;AAAA,EACjD,CAAC,6BAA6B,iBAAiB;AAAA,EAC/C,CAAC,+BAA+B,mBAAmB;AAAA,EACnD,CAAC,8BAA8B,kBAAkB;AAAA,EACjD,CAAC,uBAAuB,gBAAgB;AAAA,EACxC,CAAC,sBAAsB,gBAAgB;AAAA,EACvC,CAAC,qBAAqB,cAAc;AAAA,EACpC,CAAC,oBAAoB,cAAc;AAAA,EACnC,CAAC,kBAAkB,wBAAwB;AAAA,EAC3C,CAAC,oBAAoB,wBAAwB;AAAA,EAC7C,CAAC,mBAAmB,WAAW;AAAA,EAC/B,CAAC,yBAAyB,WAAW;AACvC,CAAC;AAGM,SAAS,aAAa,SAAqC;AAChE,SAAO,cAAc,IAAI,QAAQ,YAAY,CAAC;AAChD;AAUO,SAAS,UAAU,GAAoB;AAC5C,QAAM,IACJ,OAAO,MAAM,WACT,IACA,OAAO,MAAM,WACX,OAAO,EAAE,KAAK,CAAC,IACf;AACR,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC;AAClC;AAGO,SAAS,YAAY,GAAgC;AAC1D,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,SAAO,EAAE,KAAK,MAAM,KAAK,SAAY;AACvC;","names":[]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
allAdapters,
|
|
4
4
|
loadAdapter
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NDMNMAKP.js";
|
|
6
6
|
|
|
7
7
|
// src/sdk/introspect.ts
|
|
8
8
|
function hostCanFireEvent(c, event) {
|
|
@@ -87,4 +87,4 @@ export {
|
|
|
87
87
|
hostsSupporting,
|
|
88
88
|
surfaceSupport
|
|
89
89
|
};
|
|
90
|
-
//# sourceMappingURL=chunk-
|
|
90
|
+
//# sourceMappingURL=chunk-WPTNMYYM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sdk/introspect.ts"],"sourcesContent":["/**\r\n * sdk/introspect — host-capability introspection over the adapter registry.\r\n *\r\n * A thin, READ-ONLY query layer above {@link loadAdapter} / {@link allAdapters}:\r\n * \"which hosts can honor surface X?\" and \"what can host Y do?\". It exists so a\r\n * connector author (or a docs/CLI surface) can answer those questions WITHOUT\r\n * reaching into adapter internals or re-deriving the per-surface capability\r\n * rules — the predicates below are the single source of truth for mapping the\r\n * raw {@link PlatformCapabilities} flags onto the developer-facing surface names.\r\n *\r\n * Everything is async because adapters load lazily (one entry per platform in\r\n * the registry, imported only when queried).\r\n */\r\n\r\nimport type {\r\n HookEventName,\r\n PlatformCapabilities,\r\n PlatformId,\r\n} from \"../core/types.js\";\r\nimport { allAdapters, loadAdapter } from \"../adapters/registry.js\";\r\n\r\n/**\r\n * The single source of truth mapping each normalized {@link HookEventName} onto\r\n * the {@link PlatformCapabilities} flag that says \"this host can natively FIRE\r\n * this event\". The optional flags (the newer E1 events + PostCompact) are read\r\n * as `?? false`, mirroring the supportsCommands precedent — a host that leaves a\r\n * flag unset cannot fire that event, so a declared hook for it skip-warns at\r\n * install and `explain` reports it as not honored.\r\n *\r\n * This is the per-event signal {@link explain}'s hooks row evaluates against the\r\n * connector's DECLARED events (so a Stop-only connector on a PreToolUse-only\r\n * host no longer falsely shows `native`), and the same signal `simulate`'s real\r\n * parse→handler→format chain expresses at runtime.\r\n */\r\nexport function hostCanFireEvent(\r\n c: PlatformCapabilities,\r\n event: HookEventName,\r\n): boolean {\r\n switch (event) {\r\n case \"SessionStart\":\r\n return c.sessionStart;\r\n case \"SessionEnd\":\r\n return c.sessionEnd;\r\n case \"UserPromptSubmit\":\r\n return c.userPromptSubmit;\r\n case \"PreToolUse\":\r\n return c.preToolUse;\r\n case \"PostToolUse\":\r\n return c.postToolUse;\r\n case \"PreCompact\":\r\n return c.preCompact;\r\n case \"Stop\":\r\n return c.stop;\r\n case \"Notification\":\r\n return c.notification;\r\n case \"PermissionRequest\":\r\n return c.permissionRequest ?? false;\r\n case \"PostToolUseFailure\":\r\n return c.postToolUseFailure ?? false;\r\n case \"SubagentStart\":\r\n return c.subagentStart ?? false;\r\n case \"SubagentStop\":\r\n return c.subagentStop ?? false;\r\n case \"PostCompact\":\r\n return c.postCompact ?? false;\r\n }\r\n}\r\n\r\n/** The full normalized hook-event union, in canonical order. */\r\nconst ALL_HOOK_EVENTS: HookEventName[] = [\r\n \"SessionStart\",\r\n \"SessionEnd\",\r\n \"UserPromptSubmit\",\r\n \"PreToolUse\",\r\n \"PostToolUse\",\r\n \"PreCompact\",\r\n \"Stop\",\r\n \"Notification\",\r\n \"PermissionRequest\",\r\n \"PostToolUseFailure\",\r\n \"SubagentStart\",\r\n \"SubagentStop\",\r\n \"PostCompact\",\r\n];\r\n\r\n/**\r\n * The developer-facing surface names a connector can declare. These mirror the\r\n * top-level {@link import(\"../core/types.js\").ConnectorConfig} keys (server,\r\n * hooks, commands, …) plus the platform-scoped `configPatch` / `nativeHooks`\r\n * escape hatches — the same vocabulary the install diff and `explain` use.\r\n */\r\nexport type SurfaceName =\r\n | \"server\"\r\n | \"hooks\"\r\n | \"commands\"\r\n | \"skills\"\r\n | \"subagents\"\r\n | \"memory\"\r\n | \"statusline\"\r\n | \"actions\"\r\n | \"configPatch\"\r\n | \"nativeHooks\";\r\n\r\n/**\r\n * Pure capability predicate per surface — `true` when the host can NATIVELY\r\n * honor that surface. The optional capability flags are read as `?? false`\r\n * (the supportsCommands precedent in core/types), so a host that simply leaves\r\n * a flag unset reports the surface as unsupported (the installer skip-warns it).\r\n *\r\n * `hooks` here is the COARSE host-capability predicate: `true` when the host can\r\n * fire AT LEAST ONE normalized event (it derives from {@link hostCanFireEvent},\r\n * the per-event source of truth, so the two never drift). It is the right answer\r\n * for the connector-less queries ({@link hostsSupporting} / {@link surfaceSupport}:\r\n * \"which hosts have a hook runtime at all?\"). It is deliberately NOT used by\r\n * {@link explain}, which must judge the hooks surface against the connector's\r\n * SPECIFIC declared events — a Stop-only connector on a PreToolUse-only host is\r\n * NOT honored even though the host has a hook runtime (see {@link explain}).\r\n */\r\nexport const SURFACE_PREDICATES: Record<\r\n SurfaceName,\r\n (c: PlatformCapabilities) => boolean\r\n> = {\r\n server: (c) => c.transports.length > 0,\r\n hooks: (c) => ALL_HOOK_EVENTS.some((e) => hostCanFireEvent(c, e)),\r\n commands: (c) => c.supportsCommands ?? false,\r\n skills: (c) => c.supportsSkills ?? false,\r\n subagents: (c) => c.supportsSubagents ?? false,\r\n memory: (c) => c.supportsMemory ?? false,\r\n statusline: (c) => c.supportsStatusline ?? false,\r\n // Emitters ship on droid + hermes + warp plus the ts-plugin slash-command\r\n // hosts omp + openclaw (+ the nemoclaw fork) — the hosts with a verifiable\r\n // target. Every other host leaves supportsActions unset, so explain() marks\r\n // actions skip-warn there — the honest state for a host with no emission target.\r\n actions: (c) => c.supportsActions ?? false,\r\n configPatch: (c) => c.supportsConfigPatch ?? false,\r\n nativeHooks: (c) => c.supportsNativeHooks ?? false,\r\n};\r\n\r\n/**\r\n * The {@link PlatformCapabilities} a host advertises, or `undefined` for an id\r\n * that is not in the registry (`\"unknown\"`, a usage-only id like `\"synthetic\"`,\r\n * or a typo) — callers branch on undefined rather than crash.\r\n */\r\nexport async function capabilitiesOf(\r\n host: PlatformId | string,\r\n): Promise<PlatformCapabilities | undefined> {\r\n const adapter = await loadAdapter(host);\r\n return adapter?.capabilities;\r\n}\r\n\r\n/**\r\n * Every registered host that can NATIVELY honor `surface`, as a sorted id list\r\n * (stable output for snapshots/docs). Loads all adapters in parallel and keeps\r\n * only those whose capabilities pass {@link SURFACE_PREDICATES}.\r\n */\r\nexport async function hostsSupporting(surface: SurfaceName): Promise<PlatformId[]> {\r\n const predicate = SURFACE_PREDICATES[surface];\r\n const adapters = await allAdapters();\r\n return adapters\r\n .filter((adapter) => predicate(adapter.capabilities))\r\n .map((adapter) => adapter.id)\r\n .sort();\r\n}\r\n\r\n/**\r\n * Convenience: does `host` natively support `surface`? `false` for an unknown\r\n * id (no capabilities → cannot support anything).\r\n */\r\nexport async function surfaceSupport(\r\n host: PlatformId | string,\r\n surface: SurfaceName,\r\n): Promise<boolean> {\r\n const capabilities = await capabilitiesOf(host);\r\n return capabilities ? SURFACE_PREDICATES[surface](capabilities) : false;\r\n}\r\n"],"mappings":";;;;;;;AAkCO,SAAS,iBACd,GACA,OACS;AACT,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE;AAAA,IACX,KAAK;AACH,aAAO,EAAE,qBAAqB;AAAA,IAChC,KAAK;AACH,aAAO,EAAE,sBAAsB;AAAA,IACjC,KAAK;AACH,aAAO,EAAE,iBAAiB;AAAA,IAC5B,KAAK;AACH,aAAO,EAAE,gBAAgB;AAAA,IAC3B,KAAK;AACH,aAAO,EAAE,eAAe;AAAA,EAC5B;AACF;AAGA,IAAM,kBAAmC;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAmCO,IAAM,qBAGT;AAAA,EACF,QAAQ,CAAC,MAAM,EAAE,WAAW,SAAS;AAAA,EACrC,OAAO,CAAC,MAAM,gBAAgB,KAAK,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAAA,EAChE,UAAU,CAAC,MAAM,EAAE,oBAAoB;AAAA,EACvC,QAAQ,CAAC,MAAM,EAAE,kBAAkB;AAAA,EACnC,WAAW,CAAC,MAAM,EAAE,qBAAqB;AAAA,EACzC,QAAQ,CAAC,MAAM,EAAE,kBAAkB;AAAA,EACnC,YAAY,CAAC,MAAM,EAAE,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK3C,SAAS,CAAC,MAAM,EAAE,mBAAmB;AAAA,EACrC,aAAa,CAAC,MAAM,EAAE,uBAAuB;AAAA,EAC7C,aAAa,CAAC,MAAM,EAAE,uBAAuB;AAC/C;AAOA,eAAsB,eACpB,MAC2C;AAC3C,QAAM,UAAU,MAAM,YAAY,IAAI;AACtC,SAAO,SAAS;AAClB;AAOA,eAAsB,gBAAgB,SAA6C;AACjF,QAAM,YAAY,mBAAmB,OAAO;AAC5C,QAAM,WAAW,MAAM,YAAY;AACnC,SAAO,SACJ,OAAO,CAAC,YAAY,UAAU,QAAQ,YAAY,CAAC,EACnD,IAAI,CAAC,YAAY,QAAQ,EAAE,EAC3B,KAAK;AACV;AAMA,eAAsB,eACpB,MACA,SACkB;AAClB,QAAM,eAAe,MAAM,eAAe,IAAI;AAC9C,SAAO,eAAe,mBAAmB,OAAO,EAAE,YAAY,IAAI;AACpE;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/toml.ts"],"sourcesContent":["/**\r\n * core/toml — minimal TOML string IO.\r\n *\r\n * The TOML analog of core/yaml's read/write helpers. Lifted from the codex\r\n * adapter's private readToml/writeToml so the Group-B command renderers for\r\n * Gemini/Qwen/Codex (which author `.toml` command files) share one\r\n * implementation rather than each re-importing @iarna/toml.\r\n *\r\n * Deliberately tiny and string-oriented: these are pure (de)serializers — no\r\n * filesystem touches, no dryRun, no merge logic. Callers own reading/writing\r\n * and any idempotency/upsert (the shapes differ per platform), exactly as the\r\n * adapters layer their own logic on top of the JSON/YAML helpers.\r\n */\r\n\r\nimport TOML from \"@iarna/toml\";\r\n\r\n/**\r\n * Serialize a plain object to a TOML string.\r\n *\r\n * @iarna/toml's `stringify` is typed to accept its own `JsonMap`; our objects\r\n * are structurally compatible, so we cast at the boundary (same approach the\r\n * codex adapter used). Returns the TOML text — the caller writes it.\r\n */\r\nexport function writeTomlString(obj: Record<string, unknown>): string {\r\n return TOML.stringify(obj as never);\r\n}\r\n\r\n/**\r\n * Parse a TOML string into a typed object. Throws on malformed TOML (callers\r\n * that want fail-soft behavior should wrap in try/catch, mirroring how the\r\n * codex adapter degrades a corrupt config.toml to `{}`).\r\n */\r\nexport function readTomlString<T = Record<string, unknown>>(text: string): T {\r\n return TOML.parse(text) as T;\r\n}\r\n"],"mappings":";;;AAcA,OAAO,UAAU;AASV,SAAS,gBAAgB,KAAsC;AACpE,SAAO,KAAK,UAAU,GAAY;AACpC;","names":[]}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
normalizeSessionSource
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PSGTHJQU.js";
|
|
5
5
|
import {
|
|
6
6
|
rewriteEnvRefs
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-XXLCMSD3.js";
|
|
8
8
|
import {
|
|
9
9
|
BaseAdapter,
|
|
10
10
|
renderSkillMd
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-3FINQ2ZF.js";
|
|
12
12
|
import {
|
|
13
13
|
buildWrappedStdio,
|
|
14
14
|
isHomeBinHookCommand
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-Y3K6KJMS.js";
|
|
16
16
|
|
|
17
17
|
// src/adapters/cursor/index.ts
|
|
18
18
|
import { existsSync } from "fs";
|
|
@@ -650,4 +650,4 @@ export {
|
|
|
650
650
|
adapter,
|
|
651
651
|
cursor_default
|
|
652
652
|
};
|
|
653
|
-
//# sourceMappingURL=chunk-
|
|
653
|
+
//# sourceMappingURL=chunk-XVEJAFKW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/cursor/index.ts"],"sourcesContent":["/**\r\n * adapters/cursor — Cursor platform adapter for agent-connector.\r\n *\r\n * Generalized from context-mode's proven Cursor adapter: the served identity is\r\n * now `ctx.connector` (not a hardcoded \"context-mode\"), and every hook command\r\n * points at the single stable home binary (the shared hook-merge engine builds\r\n * the command) so one framework update propagates everywhere.\r\n *\r\n * Cursor is a json-stdio host:\r\n * - MCP servers: user scope → ~/.cursor/mcp.json (\"mcpServers\"); project scope\r\n * → <projectDir>/.cursor/mcp.json (\"mcpServers\").\r\n * - Hooks: <configDir>/hooks.json with shape `{ version, hooks: { <cursorEvent>:\r\n * [ { command, matcher? } ] } }`. Unlike Claude, each entry is a FLAT command\r\n * object (no `{ matcher, hooks:[...] }` wrapper).\r\n * - Reply: a JSON object on stdout (exit 0). deny/ask carry `permission` +\r\n * `user_message`; modify carries `updated_input`; context injects via\r\n * `agent_message` (PreToolUse) or `additional_context` (Post/SessionStart).\r\n *\r\n * Cursor supports its own `${env:VAR}` native interpolation, so env/header/url\r\n * values are rewritten to that native token rather than baked into the file.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { join } from \"node:path\";\r\n\r\nimport { BaseAdapter, type HookMergeDescriptor } from \"../base.js\";\r\nimport type { Adapter, HookReply, InstallContext, NormalizedEvent } from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n CommandDef,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookEventName,\r\n HookParadigm,\r\n HookResponse,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PostToolUseEvent,\r\n PostToolUseFailureEvent,\r\n PreCompactEvent,\r\n PreToolUseEvent,\r\n ServerDef,\r\n SessionEndEvent,\r\n SessionStartEvent,\r\n StopEvent,\r\n SubagentDef,\r\n SubagentStartEvent,\r\n SubagentStopEvent,\r\n Transport,\r\n UserPromptSubmitEvent,\r\n} from \"../../core/types.js\";\r\nimport { rewriteEnvRefs } from \"../../core/interpolate.js\";\r\nimport {\r\n buildWrappedStdio,\r\n isHomeBinHookCommand,\r\n} from \"../../core/spawn.js\";\r\nimport { renderSkillMd } from \"../claude-code/render.js\";\r\nimport { normalizeSessionSource } from \"../claude-code/wire.js\";\r\n\r\nconst HOST: PlatformId = \"cursor\";\r\nconst MCP_ROOT_KEY = \"mcpServers\";\r\n\r\n/**\r\n * Native hooks.json version Cursor expects. Exported as the SINGLE source of\r\n * truth for Cursor's hooks-file version, shared with the `cursor-plugin`\r\n * package emitter so the live install + the packaged bundle can never stamp\r\n * different versions (see core/package-formats/cursor.ts).\r\n */\r\nexport const CURSOR_HOOKS_VERSION = 1;\r\n\r\n/**\r\n * Cursor-native hook event names, in the lower-camel form Cursor reads from\r\n * hooks.json. The first four are the events the proven context-mode adapter\r\n * registers; postToolUseFailure / subagentStart / subagentStop are Cursor's\r\n * documented \"Subagent (Task tool) lifecycle\" + tool-failure hooks.\r\n */\r\nconst CURSOR_EVENT = {\r\n PreToolUse: \"preToolUse\",\r\n PostToolUse: \"postToolUse\",\r\n SessionStart: \"sessionStart\",\r\n Stop: \"stop\",\r\n PostToolUseFailure: \"postToolUseFailure\",\r\n SubagentStart: \"subagentStart\",\r\n SubagentStop: \"subagentStop\",\r\n // Lifecycle/prompt events Cursor 1.7+ documents (cursor.com/docs/hooks):\r\n // sessionEnd is fire-and-forget; preCompact is observational (cannot block);\r\n // beforeSubmitPrompt fires before a prompt is sent and CAN prevent submission\r\n // (Cursor itself matches beforeSubmitPrompt against the value \"UserPromptSubmit\").\r\n SessionEnd: \"sessionEnd\",\r\n PreCompact: \"preCompact\",\r\n UserPromptSubmit: \"beforeSubmitPrompt\",\r\n} as const;\r\n\r\n/**\r\n * Map our normalized event names to Cursor's native hook event names. Only the\r\n * events Cursor actually supports are present; everything else has no Cursor\r\n * equivalent and is reported as a skip/warn at install time.\r\n *\r\n * PermissionRequest is deliberately ABSENT: Cursor has no permission-dialog\r\n * event — its permission gate is the OUTPUT field `permission: \"allow\"|\"deny\"|\r\n * \"ask\"` of the before* hooks (beforeShellExecution / beforeMCPExecution /\r\n * beforeReadFile / preToolUse), not an observable event. Install reports the\r\n * standard skip-warn for it.\r\n *\r\n * Exported as the SINGLE source of truth for canonical→Cursor-native event\r\n * mapping, shared with the `cursor-plugin` package emitter (see\r\n * core/package-formats/cursor.ts) so the packaged bundle's hooks.json keys can\r\n * never silently diverge from the live install adapter's.\r\n */\r\nexport const CURSOR_EVENT_MAP: Partial<Record<HookEventName, string>> = {\r\n PreToolUse: CURSOR_EVENT.PreToolUse,\r\n PostToolUse: CURSOR_EVENT.PostToolUse,\r\n SessionStart: CURSOR_EVENT.SessionStart,\r\n Stop: CURSOR_EVENT.Stop,\r\n PostToolUseFailure: CURSOR_EVENT.PostToolUseFailure,\r\n SubagentStart: CURSOR_EVENT.SubagentStart,\r\n SubagentStop: CURSOR_EVENT.SubagentStop,\r\n SessionEnd: CURSOR_EVENT.SessionEnd,\r\n PreCompact: CURSOR_EVENT.PreCompact,\r\n UserPromptSubmit: CURSOR_EVENT.UserPromptSubmit,\r\n};\r\n\r\n/** A single Cursor native hook entry — a flat command object. */\r\ninterface CursorHookEntry {\r\n command: string;\r\n matcher?: string;\r\n}\r\n\r\n/** Native MCP server entry shapes Cursor accepts under `mcpServers`. */\r\ninterface CursorStdioServer {\r\n command: string;\r\n args?: string[];\r\n env?: Record<string, string>;\r\n cwd?: string;\r\n}\r\ninterface CursorHttpServer {\r\n url: string;\r\n headers?: Record<string, string>;\r\n}\r\n\r\n/** Raw Cursor hook stdin payload (snake_case wire fields). */\r\ninterface CursorWireInput {\r\n connector?: unknown;\r\n tool_name?: string;\r\n tool_input?: Record<string, unknown>;\r\n tool_output?: string;\r\n error_message?: string;\r\n cwd?: string;\r\n workspace_roots?: string[];\r\n conversation_id?: string;\r\n session_id?: string;\r\n generation_id?: string;\r\n trigger?: string;\r\n status?: string;\r\n loop_count?: number;\r\n stop_hook_active?: boolean;\r\n\r\n // sessionStart — Cursor's stdin is EXACTLY { session_id, is_background_agent,\r\n // composer_mode } (cursor.com/docs/hooks ~1278-1297): there is NO source/trigger\r\n // start-source discriminator. composer_mode is the editor MODE (agent|ask|edit),\r\n // NOT our normalized SessionStart.source enum (startup|compact|resume|clear), so\r\n // it does not map; surfaced here only so the host shape is documented (no\r\n // normalized field exists for either — see core/types.ts SessionStartEvent).\r\n is_background_agent?: boolean;\r\n composer_mode?: string;\r\n\r\n // beforeSubmitPrompt (UserPromptSubmit) — the submitted prompt text.\r\n // sessionEnd — the documented end reason. Both field names are taken\r\n // verbatim from cursor.com/docs/hooks (snake_case wire).\r\n prompt?: string;\r\n reason?: string;\r\n\r\n // postToolUseFailure — Cursor's existing error vocabulary is error_message;\r\n // the Claude-compatible names are accepted defensively (unverified wire).\r\n error?: string;\r\n tool_use_id?: string;\r\n is_interrupt?: boolean;\r\n // postToolUse / postToolUseFailure carry a BARE `duration` (ms), not\r\n // `duration_ms` (cursor.com/docs/hooks ~870,883,904,918). `duration_ms` is the\r\n // distinct field on sessionEnd (~1315) and subagentStop (~973,994), kept below.\r\n duration?: number;\r\n duration_ms?: number;\r\n\r\n // subagentStart / subagentStop (Task-tool lifecycle). Both name families are\r\n // parsed defensively: agent_* (Claude-compatible) and subagent_* (Cursor-ish).\r\n agent_id?: string;\r\n agent_type?: string;\r\n subagent_id?: string;\r\n subagent_type?: string;\r\n // subagentStop carries the subagent's output text under `summary`, NOT\r\n // last_assistant_message (cursor.com/docs/hooks ~972,993). This is read into\r\n // the normalized lastAssistantMessage.\r\n summary?: string;\r\n}\r\n\r\nexport class CursorAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name = \"Cursor\";\r\n readonly paradigm: HookParadigm = \"json-stdio\";\r\n\r\n readonly capabilities: PlatformCapabilities = {\r\n // Memory surface: AGENTS.md-first managed block via the BaseAdapter default\r\n // (memoryTargets: project <projectDir>/AGENTS.md; user scope where documented).\r\n supportsMemory: true,\r\n // Cursor natively supports pre/post tool-use, session start/end, prompt\r\n // submission, context compaction, and stop (cursor.com/docs/hooks, v1.7+).\r\n preToolUse: true,\r\n postToolUse: true,\r\n preCompact: true,\r\n sessionStart: true,\r\n sessionEnd: true,\r\n userPromptSubmit: true,\r\n stop: true,\r\n notification: false,\r\n // Newer events: Cursor has dedicated postToolUseFailure + subagentStart/\r\n // subagentStop hooks. permissionRequest stays unset — Cursor's permission\r\n // gate is an OUTPUT field of its before* hooks, not an observable event\r\n // (see the EVENT_MAP note).\r\n postToolUseFailure: true,\r\n subagentStart: true,\r\n subagentStop: true,\r\n // Cursor's preToolUse can rewrite tool input (updated_input) but cannot\r\n // rewrite already-emitted tool output.\r\n canModifyArgs: true,\r\n canModifyOutput: false,\r\n canInjectSessionContext: true,\r\n transports: [\"stdio\", \"http\"],\r\n // Server env/url use Cursor's NATIVE ${env:VAR} interpolation (rewriteEnvRefs)\r\n // — the token survives into mcp.json, never a baked literal — so the\r\n // installer's unset-env-ref bake warn does not apply here.\r\n nativeServerEnvInterpolation: true,\r\n // Content surfaces: Cursor supports all three (commands, skills, subagents).\r\n supportsCommands: true,\r\n supportsSkills: true,\r\n supportsSubagents: true,\r\n // Native passthrough hooks: Cursor documents many granular hooks with NO\r\n // canonical analog — beforeShellExecution / afterShellExecution /\r\n // beforeMCPExecution / afterMCPExecution / beforeReadFile / afterFileEdit /\r\n // beforeTabFileRead / afterTabFileEdit / workspaceOpen / afterAgentResponse /\r\n // afterAgentThought (cursor.com/docs/hooks). They are\r\n // host-specific (below the >=3-host core bar; see\r\n // docs/research/host-specific-hook-events-design.md). A connector reaches them\r\n // by declaring platforms[\"cursor\"].nativeHooks; installHooks writes those\r\n // event-name keys verbatim into hooks.json, and the generic uninstall reverses\r\n // them.\r\n supportsNativeHooks: true,\r\n // Cursor has a command-driven status line (`/statusline` → `statusLine` in\r\n // ~/.cursor/cli-config.json, GLOBAL scope only) per the Cursor changelog, but\r\n // the config-key shape + the Claude-parity stdin payload are sourced from a\r\n // community tool (agent-status-pills), not first-party Cursor docs — payload\r\n // field names unverified. Deferred pending a live cursor-agent capture; not a\r\n // permanent gap; user-scope only when wired.\r\n };\r\n\r\n // ── Detection ────────────────────────────────────────────────────────────\r\n\r\n detectInstalled(projectDir: string): DetectedPlatform {\r\n const userDir = join(homedir(), \".cursor\");\r\n const userMcp = join(userDir, \"mcp.json\");\r\n const userHooks = join(userDir, \"hooks.json\");\r\n const projectDirCursor = join(projectDir, \".cursor\");\r\n const installed =\r\n existsSync(userDir) ||\r\n existsSync(userMcp) ||\r\n existsSync(userHooks) ||\r\n existsSync(projectDirCursor);\r\n return {\r\n id: this.id,\r\n name: this.name,\r\n installed,\r\n paradigm: this.paradigm,\r\n capabilities: this.capabilities,\r\n configPath: userMcp,\r\n scope: \"user\",\r\n reason: installed\r\n ? `found Cursor config under ${userDir}`\r\n : `no Cursor config at ${userDir}`,\r\n confidence: installed ? \"high\" : \"low\",\r\n };\r\n }\r\n\r\n // ── Native paths ─────────────────────────────────────────────────────────\r\n\r\n getConfigDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".cursor\")\r\n : join(homedir(), \".cursor\");\r\n }\r\n\r\n getServerConfigPath(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"mcp.json\");\r\n }\r\n\r\n getHookConfigPath(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"hooks.json\");\r\n }\r\n\r\n // ── MCP server install / uninstall ───────────────────────────────────────\r\n\r\n installServer(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n const override = connector.platforms[HOST]?.server;\r\n if (!connector.server || override === false) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n detail: connector.server\r\n ? \"server registration disabled for cursor\"\r\n : \"connector declares no MCP server\",\r\n },\r\n ];\r\n }\r\n\r\n // Shallow-merge any per-platform server override into the base ServerDef.\r\n const server: ServerDef =\r\n override && typeof override === \"object\"\r\n ? { ...connector.server, ...override }\r\n : connector.server;\r\n\r\n const serverPath = this.getServerConfigPath(ctx);\r\n const entry = this.renderServerEntry(ctx, server);\r\n\r\n return [\r\n this.upsertServerInJson(serverPath, MCP_ROOT_KEY, connector.id, entry, ctx.dryRun),\r\n ];\r\n }\r\n\r\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\r\n const serverPath = this.getServerConfigPath(ctx);\r\n return [\r\n this.removeServerFromJson(serverPath, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun),\r\n ];\r\n }\r\n\r\n /** Render a normalized ServerDef into Cursor's native mcpServers entry. */\r\n private renderServerEntry(\r\n ctx: InstallContext,\r\n server: ServerDef,\r\n ): CursorStdioServer | CursorHttpServer {\r\n const transport: Transport = server.transport;\r\n\r\n if (transport === \"stdio\") {\r\n let command = server.command ?? \"\";\r\n let args = [...(server.args ?? [])];\r\n\r\n // Transparent telemetry wrapping: route the real command through\r\n // `<homeBin> serve --connector <id> -- <command> <args...>`.\r\n ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\r\n\r\n const entry: CursorStdioServer = { command: this.rewrite(command) };\r\n if (args.length > 0) entry.args = args.map((a) => this.rewrite(a));\r\n const env = this.renderEnv(server.env);\r\n if (env) entry.env = env;\r\n if (server.cwd) entry.cwd = this.rewrite(server.cwd);\r\n return entry;\r\n }\r\n\r\n // http (and any other remote transport we surface) — Cursor registers a URL.\r\n const entry: CursorHttpServer = { url: this.rewrite(server.url ?? \"\") };\r\n const headers = this.renderEnv(server.headers);\r\n if (headers) entry.headers = headers;\r\n return entry;\r\n }\r\n\r\n /**\r\n * Render env/header values. Cursor supports its own `${env:VAR}` native\r\n * interpolation, so translate `${env:VAR}` refs to that native token rather\r\n * than baking secrets into the file. Literals pass through unchanged.\r\n */\r\n private renderEnv(\r\n env: Record<string, string> | undefined,\r\n ): Record<string, string> | undefined {\r\n if (!env || Object.keys(env).length === 0) return undefined;\r\n const out: Record<string, string> = {};\r\n for (const [k, v] of Object.entries(env)) out[k] = this.rewrite(v);\r\n return out;\r\n }\r\n\r\n /** Translate `${env:VAR(:-default)}` to Cursor's native `${env:VAR}` token. */\r\n private rewrite(value: string): string {\r\n return rewriteEnvRefs(value, cursorEnvToken);\r\n }\r\n\r\n // ── Hook install / uninstall ─────────────────────────────────────────────\r\n\r\n installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n const override = connector.platforms[HOST];\r\n\r\n // `hooks: false` disables only the NORMALIZED events. nativeHooks is a\r\n // sibling, cursor-scoped declaration on the same override and installs\r\n // regardless.\r\n const normalizedEvents = override?.hooks === false ? [] : connector.hookEvents;\r\n const nativeHooks = override?.nativeHooks ?? {};\r\n const nativeEvents = Object.keys(nativeHooks);\r\n\r\n if (normalizedEvents.length === 0 && nativeEvents.length === 0) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n detail: override?.hooks === false ? \"hooks disabled for cursor\" : \"connector declares no hooks\",\r\n },\r\n ];\r\n }\r\n\r\n // Normalized events go through the engine's mapEvent/warn path; native\r\n // event-name keys ride the separate `native` pass (verbatim keys, EXACT\r\n // command, the `(native)` detail) — see the descriptor + base.upsertHookEntries.\r\n const pending = normalizedEvents.map((event) => ({\r\n event,\r\n matcher: connector.hooks[event]?.matcher ?? \"\",\r\n }));\r\n const native = nativeEvents.map((event) => ({\r\n event,\r\n matcher: nativeHooks[event]?.matcher ?? \"\",\r\n }));\r\n return this.upsertHookEntries(\r\n ctx,\r\n this.getHookConfigPath(ctx),\r\n pending,\r\n this.hookDescriptor(ctx),\r\n native,\r\n );\r\n }\r\n\r\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\r\n return this.removeHookEntries(ctx, this.getHookConfigPath(ctx), this.hookDescriptor(ctx));\r\n }\r\n\r\n /**\r\n * Cursor's hook-merge descriptor (FLAT shape `{ command, matcher? }`):\r\n * - mapEvent renames canonical → Cursor's lower-camel native key\r\n * (CURSOR_EVENT_MAP); an unmapped event warn-skips (no Cursor equivalent).\r\n * - renderEntry omits the `matcher` key when the matcher is falsy — this\r\n * `{command}` vs `{command,matcher}` distinction is load-bearing on-disk\r\n * bytes (Cursor's flat entry shape), reproduced exactly here.\r\n * - install find is connector-generic (ANY of our commands at the flat\r\n * `entry.command`); uninstall drops whole owned entries (flat — NO\r\n * stripInner). The version stamp is install-only via onMutate.\r\n * - the native pass uses EXACT-command ownership so a native key coinciding\r\n * with a normalized event's mapped key adds a SECOND distinct command\r\n * rather than clobbering the normalized entry.\r\n */\r\n private hookDescriptor(ctx: InstallContext): HookMergeDescriptor<CursorHookEntry> {\r\n return {\r\n mapEvent: (e) => CURSOR_EVENT_MAP[e as HookEventName],\r\n unmappedWarnDetail: (e) => `${e} has no Cursor hook equivalent — skipped`,\r\n renderEntry: (_event, matcher, command) =>\r\n matcher ? { command, matcher } : { command },\r\n entryOwnsCommand: (entry, _command) => this.isOurCommand(entry.command, ctx),\r\n ownsEntryForRemove: (c) => (entry) => this.isOurCommand(entry.command, c),\r\n skipDetail: (e) => `hooks.${e} already registered`,\r\n removeDetail: (e, n) => `hooks.${e} (${n})`,\r\n absentDetail: \"no hooks section present\",\r\n noMatchDetail: \"no matching hook entries\",\r\n onMutate: (file) => {\r\n (file as { version?: number }).version = CURSOR_HOOKS_VERSION;\r\n },\r\n nativeOwnsCommand: (entry, command) => entry.command === command,\r\n nativeSkipDetail: (e) => `hooks.${e} (native) already registered`,\r\n nativeMutateDetail: (e) => `hooks.${e} (native)`,\r\n };\r\n }\r\n\r\n /** True when a hook command references our home binary AND this connector id\r\n * (anchored so a shared-prefix id can't collide — see isHomeBinHookCommand). */\r\n private isOurCommand(command: string | undefined, ctx: InstallContext): boolean {\r\n return isHomeBinHookCommand(command, ctx.homeBinPath, ctx.connector.id);\r\n }\r\n\r\n // ── Content surfaces: commands / skills / subagents ──────────────────────\r\n // CONTENT-ONLY: pure native-file writers under <configDir>/{commands,skills,\r\n // agents}. No runtime dispatch, no home-bin pointer, no telemetry wrap. Each\r\n // method is idempotent (byte-identical → skip) via BaseAdapter.writeContentFile\r\n // and reversible via removeContentFile. Honors platforms[\"cursor\"] per-surface\r\n // false to skip. Cursor commands are BODY-ONLY markdown (no frontmatter);\r\n // skills are the uniform <name>/SKILL.md + resources; subagents are md+fm.\r\n\r\n private commandsDir(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"commands\");\r\n }\r\n private skillsDir(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"skills\");\r\n }\r\n private agentsDir(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"agents\");\r\n }\r\n\r\n /** Native command file path: <configDir>/commands/<name>.md. */\r\n private commandPath(ctx: InstallContext, name: string): string {\r\n return join(this.commandsDir(ctx), `${name}.md`);\r\n }\r\n /** Native skill dir: <configDir>/skills/<name>. */\r\n private skillDir(ctx: InstallContext, name: string): string {\r\n return join(this.skillsDir(ctx), name);\r\n }\r\n /** Native subagent file path: <configDir>/agents/<name>.md. */\r\n private subagentPath(ctx: InstallContext, name: string): string {\r\n return join(this.agentsDir(ctx), `${name}.md`);\r\n }\r\n\r\n // ── Commands ──────────────────────────────────────────────────────────────\r\n\r\n override installCommands(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.commands === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"commands disabled for cursor\" }];\r\n }\r\n if (connector.commands.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\r\n }\r\n return connector.commands.map((cmd) =>\r\n this.writeContentFile(\r\n this.commandPath(ctx, cmd.name),\r\n this.renderCommand(cmd),\r\n ctx.dryRun,\r\n ),\r\n );\r\n }\r\n\r\n override uninstallCommands(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.commands.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\r\n }\r\n return connector.commands.map((cmd) =>\r\n this.removeContentFile(this.commandPath(ctx, cmd.name), ctx.dryRun),\r\n );\r\n }\r\n\r\n /**\r\n * Render a Cursor command: BODY-ONLY markdown (no frontmatter). Cursor reads\r\n * the file as the command prompt verbatim. We prepend the one-line description\r\n * as an HTML comment header when present so authoring intent survives the\r\n * round-trip without leaking into the rendered prompt. A description containing\r\n * \"-->\" would prematurely CLOSE the HTML comment and leak the remainder into\r\n * the prompt, so we neutralize that sequence before embedding it.\r\n */\r\n private renderCommand(cmd: CommandDef): string {\r\n if (!cmd.description) return `${cmd.prompt}\\n`;\r\n // Break any literal comment-close so it cannot terminate our header early.\r\n const safe = cmd.description.replace(/--+>/g, (m) => m.replace(/>/g, \">\"));\r\n return `<!-- ${safe} -->\\n\\n${cmd.prompt}\\n`;\r\n }\r\n\r\n // ── Skills ────────────────────────────────────────────────────────────────\r\n\r\n override installSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.skills === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"skills disabled for cursor\" }];\r\n }\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n changes.push(\r\n this.writeContentFile(join(dir, \"SKILL.md\"), renderSkillMd(skill), ctx.dryRun),\r\n );\r\n // Bundle any resource files beside SKILL.md (relative path → contents).\r\n // Defense-in-depth: skip+warn on any key that escapes the skill dir\r\n // (config-time validation already rejects these, but never trust input).\r\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\r\n });\r\n continue;\r\n }\r\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\r\n }\r\n }\r\n return changes;\r\n }\r\n\r\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n // Remove only the files we wrote (SKILL.md + declared resources), then the\r\n // skill dir itself when we own its full contents.\r\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\r\n for (const rel of Object.keys(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) continue; // never delete outside the skill dir\r\n changes.push(this.removeContentFile(target, ctx.dryRun));\r\n }\r\n // Only remove the skill dir when WE own its full contents — never rm -rf a\r\n // dir that still holds user-added / sibling-tool / shared files.\r\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\r\n }\r\n return changes;\r\n }\r\n\r\n // ── Subagents ───────────────────────────────────────────────────────────────\r\n\r\n override installSubagents(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.subagents === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"subagents disabled for cursor\" }];\r\n }\r\n if (connector.subagents.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no subagents\" }];\r\n }\r\n return connector.subagents.map((agent) =>\r\n this.writeContentFile(\r\n this.subagentPath(ctx, agent.name),\r\n this.renderSubagent(agent),\r\n ctx.dryRun,\r\n ),\r\n );\r\n }\r\n\r\n override uninstallSubagents(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.subagents.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no subagents\" }];\r\n }\r\n return connector.subagents.map((agent) =>\r\n this.removeContentFile(this.subagentPath(ctx, agent.name), ctx.dryRun),\r\n );\r\n }\r\n\r\n /** Render a Cursor subagent to md+frontmatter (name, description, model, readonly) + prompt body. */\r\n private renderSubagent(agent: SubagentDef): string {\r\n const frontmatter: Record<string, unknown> = {\r\n name: agent.name,\r\n description: agent.description,\r\n };\r\n if (agent.model !== undefined) frontmatter.model = agent.model;\r\n if (agent.readonly !== undefined) frontmatter.readonly = agent.readonly;\r\n if (agent.extra) Object.assign(frontmatter, agent.extra);\r\n return this.renderFrontmatterMd(frontmatter, agent.prompt);\r\n }\r\n\r\n // ── Diagnostics ──────────────────────────────────────────────────────────\r\n\r\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\r\n const mcpPath = this.getServerConfigPath(ctx);\r\n const hooksPath = this.getHookConfigPath(ctx);\r\n const checks: HealthCheck[] = [\r\n {\r\n name: `${this.name}: mcp.json present`,\r\n check: () =>\r\n existsSync(mcpPath)\r\n ? { status: \"OK\", detail: mcpPath }\r\n : { status: \"FAIL\", detail: `not found: ${mcpPath}` },\r\n },\r\n {\r\n name: `${this.name}: hooks.json present`,\r\n check: () => {\r\n // Same \"only assert what the connector declares\" rule as the\r\n // content-surface checks below: a hookless connector never writes\r\n // hooks.json, so its absence is healthy, not a failure.\r\n if (ctx.connector.hookEvents.length === 0) {\r\n return { status: \"OK\", detail: \"no hooks declared\" };\r\n }\r\n return existsSync(hooksPath)\r\n ? { status: \"OK\", detail: hooksPath }\r\n : { status: \"FAIL\", detail: `not found: ${hooksPath}` };\r\n },\r\n },\r\n ];\r\n\r\n // Content-surface checks: only assert presence of the files this connector\r\n // declares (skip silently for surfaces it never asked for).\r\n for (const cmd of ctx.connector.commands) {\r\n const p = this.commandPath(ctx, cmd.name);\r\n checks.push({\r\n name: `${this.name}: command ${cmd.name} present`,\r\n check: () =>\r\n existsSync(p) ? { status: \"OK\", detail: p } : { status: \"FAIL\", detail: `not found: ${p}` },\r\n });\r\n }\r\n for (const skill of ctx.connector.skills) {\r\n const p = join(this.skillDir(ctx, skill.name), \"SKILL.md\");\r\n checks.push({\r\n name: `${this.name}: skill ${skill.name} present`,\r\n check: () =>\r\n existsSync(p) ? { status: \"OK\", detail: p } : { status: \"FAIL\", detail: `not found: ${p}` },\r\n });\r\n }\r\n for (const agent of ctx.connector.subagents) {\r\n const p = this.subagentPath(ctx, agent.name);\r\n checks.push({\r\n name: `${this.name}: subagent ${agent.name} present`,\r\n check: () =>\r\n existsSync(p) ? { status: \"OK\", detail: p } : { status: \"FAIL\", detail: `not found: ${p}` },\r\n });\r\n }\r\n return checks;\r\n }\r\n\r\n // ── Runtime: parse Cursor stdin JSON → normalized event ──────────────────\r\n\r\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\r\n const input = (raw ?? {}) as CursorWireInput;\r\n const connectorId = typeof input.connector === \"string\" ? input.connector : \"\";\r\n const sessionId = extractSessionId(input);\r\n const projectDir = this.getProjectDir(input);\r\n\r\n const base = {\r\n hostPlatform: HOST,\r\n connectorId,\r\n sessionId,\r\n raw,\r\n ...(projectDir !== undefined ? { projectDir } : {}),\r\n } as const;\r\n\r\n switch (event) {\r\n case \"PreToolUse\": {\r\n const ev: PreToolUseEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n };\r\n return ev;\r\n }\r\n case \"PostToolUse\": {\r\n const toolOutput = input.tool_output ?? input.error_message;\r\n const ev: PostToolUseEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n ...(toolOutput !== undefined ? { toolOutput } : {}),\r\n ...(input.error_message ? { isError: true } : {}),\r\n };\r\n return ev;\r\n }\r\n case \"SessionStart\": {\r\n // Cursor's sessionStart stdin is exactly { session_id, is_background_agent,\r\n // composer_mode } — there is NO source/trigger start-source discriminator\r\n // (cursor.com/docs/hooks ~1278-1297). composer_mode is the editor MODE\r\n // (agent|ask|edit), a different concept from our normalized source enum\r\n // (startup|compact|resume|clear), so it does NOT map. With no discriminator\r\n // available, the normalized source stays at its 'startup' default (via\r\n // normalizeSessionSource(undefined)). is_background_agent / composer_mode\r\n // ride along on base.raw; there is no normalized field for either.\r\n const ev: SessionStartEvent = {\r\n ...base,\r\n source: normalizeSessionSource(undefined),\r\n };\r\n return ev;\r\n }\r\n case \"Stop\": {\r\n const ev: StopEvent = {\r\n ...base,\r\n ...(typeof input.stop_hook_active === \"boolean\"\r\n ? { stopHookActive: input.stop_hook_active }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"SessionEnd\": {\r\n // Cursor sessionEnd: { session_id, reason, duration_ms, ... }. session_id\r\n // is already folded into base.sessionId via extractSessionId; only the\r\n // documented `reason` enum maps onto the normalized SessionEnd shape.\r\n const ev: SessionEndEvent = {\r\n ...base,\r\n ...(typeof input.reason === \"string\" ? { reason: input.reason } : {}),\r\n };\r\n return ev;\r\n }\r\n case \"UserPromptSubmit\": {\r\n // Cursor beforeSubmitPrompt: { prompt, attachments }. attachments has no\r\n // normalized field and is preserved via base.raw; only `prompt` maps.\r\n const ev: UserPromptSubmitEvent = {\r\n ...base,\r\n prompt: typeof input.prompt === \"string\" ? input.prompt : \"\",\r\n };\r\n return ev;\r\n }\r\n case \"PreCompact\": {\r\n // Cursor preCompact: { trigger: \"auto\"|\"manual\", context_*… }. Only the\r\n // documented `trigger` enum maps onto the normalized PreCompact shape.\r\n const ev: PreCompactEvent = {\r\n ...base,\r\n ...(input.trigger === \"auto\" || input.trigger === \"manual\"\r\n ? { trigger: input.trigger }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"PostToolUseFailure\": {\r\n // Cursor's established error field is error_message; fall back to the\r\n // Claude-compatible `error` defensively.\r\n const error = input.error_message ?? input.error ?? \"\";\r\n const ev: PostToolUseFailureEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n error,\r\n ...(typeof input.tool_use_id === \"string\"\r\n ? { toolUseId: input.tool_use_id }\r\n : {}),\r\n ...(typeof input.is_interrupt === \"boolean\"\r\n ? { isInterrupt: input.is_interrupt }\r\n : {}),\r\n // postToolUseFailure reports a BARE `duration` (ms), not `duration_ms`\r\n // (cursor.com/docs/hooks ~904,918). duration_ms only exists on\r\n // sessionEnd/subagentStop.\r\n ...(typeof input.duration === \"number\"\r\n ? { durationMs: input.duration }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"SubagentStart\": {\r\n const agentId = input.agent_id ?? input.subagent_id;\r\n const agentType = input.agent_type ?? input.subagent_type;\r\n const ev: SubagentStartEvent = {\r\n ...base,\r\n ...(typeof agentId === \"string\" ? { agentId } : {}),\r\n ...(typeof agentType === \"string\" ? { agentType } : {}),\r\n };\r\n return ev;\r\n }\r\n case \"SubagentStop\": {\r\n const agentId = input.agent_id ?? input.subagent_id;\r\n const agentType = input.agent_type ?? input.subagent_type;\r\n const ev: SubagentStopEvent = {\r\n ...base,\r\n ...(typeof agentId === \"string\" ? { agentId } : {}),\r\n ...(typeof agentType === \"string\" ? { agentType } : {}),\r\n // Cursor's subagentStop carries the subagent's output text under\r\n // `summary`, NOT last_assistant_message (cursor.com/docs/hooks ~972,993).\r\n ...(typeof input.summary === \"string\"\r\n ? { lastAssistantMessage: input.summary }\r\n : {}),\r\n ...(typeof input.stop_hook_active === \"boolean\"\r\n ? { stopHookActive: input.stop_hook_active }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n default: {\r\n // Cursor never delivers Notification / PermissionRequest (no native\r\n // equivalent — permission is an OUTPUT field of its before* hooks, not\r\n // an event). If the runtime dispatches one anyway, fail loudly.\r\n throw new Error(`unsupported cursor hook event: ${String(event)}`);\r\n }\r\n }\r\n }\r\n\r\n private getProjectDir(input: CursorWireInput): string | undefined {\r\n return input.cwd ?? input.workspace_roots?.[0] ?? undefined;\r\n }\r\n\r\n // ── Runtime: normalized response → Cursor native hook reply ──────────────\r\n\r\n formatReply(event: HookEventName, response: HookResponse): HookReply {\r\n const decision = response.decision ?? \"allow\";\r\n\r\n // postToolUseFailure (feedback beside the error) and subagentStart (context\r\n // injected into the SUBAGENT's conversation) are observe/context-only on\r\n // Cursor: \"context\" emits additional_context, and a \"deny\" DEGRADES to the\r\n // same shape carrying the reason (the tool already failed / the spawn is\r\n // not blockable). Everything else is a minimal no-op payload (Cursor\r\n // rejects empty stdout as \"no valid response\").\r\n if (event === \"PostToolUseFailure\" || event === \"SubagentStart\") {\r\n const context =\r\n decision === \"context\"\r\n ? response.additionalContext\r\n : decision === \"deny\"\r\n ? response.reason ?? response.additionalContext\r\n : undefined;\r\n return this.stdout({ additional_context: context ?? \"\" });\r\n }\r\n\r\n // beforeSubmitPrompt (UserPromptSubmit) is a BLOCK gate, not a permission/\r\n // context surface: its only documented output is { continue, user_message }\r\n // (cursor.com/docs/hooks). A \"deny\" prevents submission (continue:false +\r\n // reason); it cannot inject context (no additional_context/agent_message\r\n // field), so every non-deny decision is a no-op continue:true.\r\n if (event === \"UserPromptSubmit\") {\r\n if (decision === \"deny\") {\r\n return this.stdout({\r\n continue: false,\r\n user_message: response.reason ?? \"Blocked by hook\",\r\n });\r\n }\r\n return this.stdout({ continue: true });\r\n }\r\n\r\n // sessionEnd is fire-and-forget (the response is logged but not used) and\r\n // preCompact is observational (cannot block or modify compaction) per\r\n // cursor.com/docs/hooks — neither carries a decision Cursor honors, so both\r\n // are an unconditional no-op passthrough (mirrors Stop).\r\n if (event === \"SessionEnd\" || event === \"PreCompact\") {\r\n return { exitCode: 0 };\r\n }\r\n\r\n // deny → block the action with a user-facing message. (On SubagentStop\r\n // this follows the adapter's Stop idiom — the deny carries Stop semantics.)\r\n if (decision === \"deny\") {\r\n return this.stdout({\r\n permission: \"deny\",\r\n user_message: response.reason ?? \"Blocked by hook\",\r\n });\r\n }\r\n\r\n // ask → prompt the user to confirm.\r\n if (decision === \"ask\") {\r\n return this.stdout({\r\n permission: \"ask\",\r\n user_message: response.reason ?? \"Confirmation required by hook\",\r\n });\r\n }\r\n\r\n // modify → rewrite PreToolUse input (only where Cursor supports it).\r\n if (decision === \"modify\") {\r\n if (event === \"PreToolUse\" && response.updatedInput) {\r\n return this.stdout({ updated_input: response.updatedInput });\r\n }\r\n // Output rewrite is unsupported on Cursor; fall through to allow.\r\n }\r\n\r\n // context → inject soft guidance. PreToolUse uses `agent_message`; Post and\r\n // SessionStart use `additional_context`.\r\n if (decision === \"context\" && response.additionalContext) {\r\n return event === \"PreToolUse\"\r\n ? this.stdout({ agent_message: response.additionalContext })\r\n : this.stdout({ additional_context: response.additionalContext });\r\n }\r\n\r\n // SessionStart always emits valid JSON even on a no-op (Cursor rejects empty\r\n // stdout as \"no valid response\").\r\n if (event === \"SessionStart\") {\r\n return this.stdout({ additional_context: response.additionalContext ?? \"\" });\r\n }\r\n\r\n // allow / void → minimal no-op payload (Cursor rejects empty stdout).\r\n if (event === \"PreToolUse\") return this.stdout({ agent_message: \"\" });\r\n if (event === \"PostToolUse\") return this.stdout({ additional_context: \"\" });\r\n return { exitCode: 0 };\r\n }\r\n\r\n private stdout(payload: unknown): HookReply {\r\n return { exitCode: 0, stdout: JSON.stringify(payload) };\r\n }\r\n}\r\n\r\n/** Cursor native interpolation token: `${env:VAR}` → `${env:VAR}` (passthrough). */\r\nfunction cursorEnvToken(name: string): string {\r\n return `\\${env:${name}}`;\r\n}\r\n\r\nfunction extractSessionId(input: CursorWireInput): string {\r\n if (typeof input.conversation_id === \"string\") return input.conversation_id;\r\n if (typeof input.session_id === \"string\") return input.session_id;\r\n return \"\";\r\n}\r\n\r\nexport const adapter = new CursorAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":["entry"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/interpolate.ts"],"sourcesContent":["/**\r\n * core/interpolate — universal ${env:VAR} interpolation.\r\n *\r\n * The developer writes one portable syntax in their connector config:\r\n * \"${env:ACME_KEY}\" → value of process.env.ACME_KEY\r\n * \"${env:ACME_URL:-https://x}\" → value, or the default after \":-\" if unset/empty\r\n *\r\n * Two consumers:\r\n * • resolveEnvRefs / resolveEnvRefsDeep — resolve to literals at INSTALL time,\r\n * for hosts with NO native interpolation (e.g. Codex TOML).\r\n * • rewriteEnvRefs — translate to a host's NATIVE interpolation token (e.g.\r\n * Cursor/VS Code \"${env:VAR}\", Claude \"${VAR}\") so secrets are never baked\r\n * into config files. Adapters choose which to use.\r\n */\r\n\r\n/** Matches ${env:NAME} and ${env:NAME:-default}. NAME is [A-Za-z_][A-Za-z0-9_]*. */\r\nexport const ENV_REF_RE = /\\$\\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\\}/g;\r\n\r\n/** Resolve every ${env:VAR} in `input` to a literal from `env`. */\r\nexport function resolveEnvRefs(\r\n input: string,\r\n env: NodeJS.ProcessEnv = process.env,\r\n): string {\r\n return input.replace(ENV_REF_RE, (_m, name: string, def?: string) => {\r\n const v = env[name];\r\n if (v != null && v !== \"\") return v;\r\n return def ?? \"\";\r\n });\r\n}\r\n\r\n/** Recursively resolve ${env:VAR} in all strings of a JSON-ish value. */\r\nexport function resolveEnvRefsDeep<T>(value: T, env: NodeJS.ProcessEnv = process.env): T {\r\n if (typeof value === \"string\") return resolveEnvRefs(value, env) as unknown as T;\r\n if (Array.isArray(value)) {\r\n return value.map((v) => resolveEnvRefsDeep(v, env)) as unknown as T;\r\n }\r\n if (value && typeof value === \"object\") {\r\n const out: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(value)) out[k] = resolveEnvRefsDeep(v, env);\r\n return out as T;\r\n }\r\n return value;\r\n}\r\n\r\n/**\r\n * Translate every ${env:VAR(:-default)} via `render` into a host-native token.\r\n * The renderer receives the var name and optional default and returns the\r\n * replacement string (e.g. `(name) => \"${\" + name + \"}\"` for Claude).\r\n */\r\nexport function rewriteEnvRefs(\r\n input: string,\r\n render: (name: string, def?: string) => string,\r\n): string {\r\n return input.replace(ENV_REF_RE, (_m, name: string, def?: string) =>\r\n render(name, def),\r\n );\r\n}\r\n\r\n/** True if the string contains at least one ${env:...} reference. */\r\nexport function hasEnvRef(input: string): boolean {\r\n ENV_REF_RE.lastIndex = 0;\r\n return ENV_REF_RE.test(input);\r\n}\r\n\r\n/**\r\n * Names of every ${env:VAR} reference in a JSON-ish value that\r\n * {@link resolveEnvRefsDeep} would bake to an EMPTY literal — i.e. the var is\r\n * unset/empty in `env` AND the reference carries no `:-default`. These are the\r\n * silent-empty-secret bakes on hosts WITHOUT native interpolation: the install\r\n * succeeds but writes `\"\"` where a secret was meant, surfacing only as a\r\n * runtime auth failure inside the MCP server. Returns deduped names in\r\n * first-seen order; uses the SAME unset/default semantics as resolveEnvRefs so\r\n * the two can never disagree. A ref WITH a `:-default` is intentional and never\r\n * reported (even when the default itself is empty — the dev opted into \"\").\r\n */\r\nexport function findUnsetEnvRefs<T>(\r\n value: T,\r\n env: NodeJS.ProcessEnv = process.env,\r\n): string[] {\r\n const seen = new Set<string>();\r\n const order: string[] = [];\r\n const scanString = (input: string): void => {\r\n // A FRESH regex per scan — the shared ENV_REF_RE carries /g lastIndex state\r\n // (hasEnvRef / matchAll would otherwise start mid-string and miss matches).\r\n const re = new RegExp(ENV_REF_RE.source, \"g\");\r\n for (const m of input.matchAll(re)) {\r\n const name = m[1] as string;\r\n const def = m[2] as string | undefined;\r\n // Mirrors resolveEnvRefs: a present, non-empty value resolves to itself;\r\n // otherwise the default is used (no bake of \"\"). Only an unset/empty var\r\n // WITHOUT a default bakes \"\" — the case we report.\r\n const v = env[name];\r\n if (v != null && v !== \"\") continue;\r\n if (def !== undefined) continue;\r\n if (!seen.has(name)) {\r\n seen.add(name);\r\n order.push(name);\r\n }\r\n }\r\n };\r\n const walk = (v: unknown): void => {\r\n if (typeof v === \"string\") scanString(v);\r\n else if (Array.isArray(v)) v.forEach(walk);\r\n else if (v && typeof v === \"object\") Object.values(v).forEach(walk);\r\n };\r\n walk(value);\r\n return order;\r\n}\r\n"],"mappings":";;;AAgBO,IAAM,aAAa;AAGnB,SAAS,eACd,OACA,MAAyB,QAAQ,KACzB;AACR,SAAO,MAAM,QAAQ,YAAY,CAAC,IAAI,MAAc,QAAiB;AACnE,UAAM,IAAI,IAAI,IAAI;AAClB,QAAI,KAAK,QAAQ,MAAM,GAAI,QAAO;AAClC,WAAO,OAAO;AAAA,EAChB,CAAC;AACH;AAGO,SAAS,mBAAsB,OAAU,MAAyB,QAAQ,KAAQ;AACvF,MAAI,OAAO,UAAU,SAAU,QAAO,eAAe,OAAO,GAAG;AAC/D,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAAA,EACpD;AACA,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,EAAG,KAAI,CAAC,IAAI,mBAAmB,GAAG,GAAG;AAC9E,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAOO,SAAS,eACd,OACA,QACQ;AACR,SAAO,MAAM;AAAA,IAAQ;AAAA,IAAY,CAAC,IAAI,MAAc,QAClD,OAAO,MAAM,GAAG;AAAA,EAClB;AACF;AAmBO,SAAS,iBACd,OACA,MAAyB,QAAQ,KACvB;AACV,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,QAAkB,CAAC;AACzB,QAAM,aAAa,CAAC,UAAwB;AAG1C,UAAM,KAAK,IAAI,OAAO,WAAW,QAAQ,GAAG;AAC5C,eAAW,KAAK,MAAM,SAAS,EAAE,GAAG;AAClC,YAAM,OAAO,EAAE,CAAC;AAChB,YAAM,MAAM,EAAE,CAAC;AAIf,YAAM,IAAI,IAAI,IAAI;AAClB,UAAI,KAAK,QAAQ,MAAM,GAAI;AAC3B,UAAI,QAAQ,OAAW;AACvB,UAAI,CAAC,KAAK,IAAI,IAAI,GAAG;AACnB,aAAK,IAAI,IAAI;AACb,cAAM,KAAK,IAAI;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AACA,QAAM,OAAO,CAAC,MAAqB;AACjC,QAAI,OAAO,MAAM,SAAU,YAAW,CAAC;AAAA,aAC9B,MAAM,QAAQ,CAAC,EAAG,GAAE,QAAQ,IAAI;AAAA,aAChC,KAAK,OAAO,MAAM,SAAU,QAAO,OAAO,CAAC,EAAE,QAAQ,IAAI;AAAA,EACpE;AACA,OAAK,KAAK;AACV,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/usage/sqlite.ts"],"sourcesContent":["/**\r\n * usage/sqlite — a lazy, read-only sql.js (WASM) loader for SQLite readers.\r\n *\r\n * Why sql.js: pure WASM, zero native build / node-gyp / prebuilt-binary matrix,\r\n * so it installs identically on Linux/macOS/Windows and in CI (the decisive\r\n * constraint — see docs/research/usage-design.md §2). We never write the host DB:\r\n * we load its bytes and run SELECTs, then discard.\r\n *\r\n * Read-only by construction:\r\n * • open by reading the file into a Uint8Array → new SQL.Database(bytes);\r\n * • if a -wal/-shm sidecar exists, the on-disk file may be a half-flushed\r\n * snapshot, so we copy the db (+ sidecars) into an os.tmpdir temp path,\r\n * open the copy, and clean it up afterward. This avoids reading a torn page\r\n * of a live DB (Crush/OpenCode can be running).\r\n *\r\n * Fail-open: ANY failure (missing wasm, unreadable file, corrupt db, bad SQL)\r\n * returns null / [] — a SQLite reader degrades to zero rows, never throws.\r\n *\r\n * The WASM module is initialized once (lazy singleton) so report runs that touch\r\n * no SQLite reader pay nothing.\r\n */\r\n\r\nimport { copyFileSync, existsSync, mkdtempSync, readFileSync, rmSync } from \"node:fs\";\r\nimport { createRequire } from \"node:module\";\r\nimport { tmpdir } from \"node:os\";\r\nimport { basename, dirname, join } from \"node:path\";\r\n\r\n/** Minimal handle a SQLite reader needs: run a SELECT, then close. */\r\nexport interface SqliteDb {\r\n /** Run a SQL statement and return all rows as plain objects (column → value). */\r\n all(sql: string): Array<Record<string, unknown>>;\r\n /** Release the in-memory database. Idempotent. */\r\n close(): void;\r\n}\r\n\r\n/** The sql.js factory signature we depend on (a tiny structural subset). */\r\ninterface SqlJsStatic {\r\n Database: new (data: Uint8Array) => SqlJsDatabase;\r\n}\r\ninterface SqlJsDatabase {\r\n exec(sql: string): Array<{ columns: string[]; values: unknown[][] }>;\r\n close(): void;\r\n}\r\ntype InitSqlJs = (config?: { locateFile?: (file: string) => string }) => Promise<SqlJsStatic>;\r\n\r\nconst require = createRequire(import.meta.url);\r\n\r\n/** Lazy singleton: the initialized sql.js module (or null if it failed to load). */\r\nlet sqlPromise: Promise<SqlJsStatic | null> | undefined;\r\n\r\n/** Resolve the sql-wasm.wasm next to the resolved sql.js package entry. */\r\nfunction locateWasm(file: string): string {\r\n try {\r\n const entry = require.resolve(\"sql.js\"); // → node_modules/sql.js/dist/sql-wasm.js\r\n return join(dirname(entry), file);\r\n } catch {\r\n return file;\r\n }\r\n}\r\n\r\n/** Initialize sql.js once. Returns null (cached) when the module cannot load. */\r\nfunction loadSqlJs(): Promise<SqlJsStatic | null> {\r\n if (sqlPromise === undefined) {\r\n sqlPromise = (async () => {\r\n try {\r\n // sql.js's default export is the initializer (a CommonJS module).\r\n const mod = require(\"sql.js\") as InitSqlJs | { default: InitSqlJs };\r\n const init: InitSqlJs = typeof mod === \"function\" ? mod : mod.default;\r\n return await init({ locateFile: locateWasm });\r\n } catch {\r\n return null;\r\n }\r\n })();\r\n }\r\n return sqlPromise;\r\n}\r\n\r\n/** SQLite WAL/SHM sidecar suffixes that signal a possibly-live database file. */\r\nconst SIDECAR_SUFFIXES = [\"-wal\", \"-shm\"] as const;\r\n\r\n/**\r\n * Open a SQLite file read-only via sql.js. Returns a small {@link SqliteDb} or\r\n * null on any failure. When a -wal/-shm sidecar is present the db is copied to a\r\n * temp dir first (and the copy is removed when the handle is closed) to avoid\r\n * reading a torn snapshot of a live database.\r\n */\r\nexport async function openSqlite(path: string): Promise<SqliteDb | null> {\r\n if (!existsSync(path)) return null;\r\n\r\n const SQL = await loadSqlJs();\r\n if (SQL === null) return null;\r\n\r\n const hasSidecar = SIDECAR_SUFFIXES.some((s) => existsSync(path + s));\r\n let openPath = path;\r\n let tempDir: string | undefined;\r\n\r\n if (hasSidecar) {\r\n try {\r\n tempDir = mkdtempSync(join(tmpdir(), \"agent-connector-usage-\"));\r\n const dst = join(tempDir, basename(path));\r\n copyFileSync(path, dst);\r\n for (const suffix of SIDECAR_SUFFIXES) {\r\n const side = path + suffix;\r\n if (existsSync(side)) copyFileSync(side, dst + suffix);\r\n }\r\n openPath = dst;\r\n } catch {\r\n // If the copy fails, fall back to opening the original bytes directly.\r\n openPath = path;\r\n if (tempDir) {\r\n cleanupTemp(tempDir);\r\n tempDir = undefined;\r\n }\r\n }\r\n }\r\n\r\n let bytes: Uint8Array;\r\n try {\r\n bytes = readFileSync(openPath);\r\n } catch {\r\n if (tempDir) cleanupTemp(tempDir);\r\n return null;\r\n }\r\n\r\n // Reject anything that is not a SQLite file up front (the 16-byte header magic\r\n // \"SQLite format 3\\0\"). sql.js would otherwise accept arbitrary bytes and only\r\n // fail at query time; this keeps the documented \"null on non-db\" contract.\r\n if (!hasSqliteMagic(bytes)) {\r\n if (tempDir) cleanupTemp(tempDir);\r\n return null;\r\n }\r\n\r\n let db: SqlJsDatabase;\r\n try {\r\n db = new SQL.Database(bytes);\r\n } catch {\r\n if (tempDir) cleanupTemp(tempDir);\r\n return null;\r\n }\r\n\r\n let closed = false;\r\n return {\r\n all(sql: string): Array<Record<string, unknown>> {\r\n if (closed) return [];\r\n let result: Array<{ columns: string[]; values: unknown[][] }>;\r\n try {\r\n result = db.exec(sql);\r\n } catch {\r\n return []; // bad SQL / schema mismatch → no rows\r\n }\r\n const out: Array<Record<string, unknown>> = [];\r\n for (const block of result) {\r\n const { columns, values } = block;\r\n for (const row of values) {\r\n const obj: Record<string, unknown> = {};\r\n for (let i = 0; i < columns.length; i++) {\r\n const col = columns[i];\r\n if (col !== undefined) obj[col] = row[i];\r\n }\r\n out.push(obj);\r\n }\r\n }\r\n return out;\r\n },\r\n close(): void {\r\n if (closed) return;\r\n closed = true;\r\n try {\r\n db.close();\r\n } catch {\r\n /* ignore */\r\n }\r\n if (tempDir) cleanupTemp(tempDir);\r\n },\r\n };\r\n}\r\n\r\n/** The 16-byte SQLite file header: \"SQLite format 3\\0\". */\r\nconst SQLITE_MAGIC = Uint8Array.from([\r\n 0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00,\r\n]);\r\n\r\n/** Does `bytes` begin with the SQLite file-header magic? */\r\nfunction hasSqliteMagic(bytes: Uint8Array): boolean {\r\n if (bytes.length < SQLITE_MAGIC.length) return false;\r\n for (let i = 0; i < SQLITE_MAGIC.length; i++) {\r\n if (bytes[i] !== SQLITE_MAGIC[i]) return false;\r\n }\r\n return true;\r\n}\r\n\r\nfunction cleanupTemp(dir: string): void {\r\n try {\r\n rmSync(dir, { recursive: true, force: true });\r\n } catch {\r\n /* best-effort */\r\n }\r\n}\r\n"],"mappings":"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","names":["require"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
isNonDefaultDataRoot
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-QYOUQCC6.js";
|
|
5
5
|
|
|
6
6
|
// src/core/spawn.ts
|
|
7
7
|
import { existsSync } from "fs";
|
|
@@ -158,4 +158,4 @@ export {
|
|
|
158
158
|
resolveSpawnCommand,
|
|
159
159
|
detectLaunchMethod
|
|
160
160
|
};
|
|
161
|
-
//# sourceMappingURL=chunk-
|
|
161
|
+
//# sourceMappingURL=chunk-Y3K6KJMS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/spawn.ts"],"sourcesContent":["/**\r\n * core/spawn — cross-platform command construction.\r\n *\r\n * Borrowed from context-mode (issues #369/#372/#548/#738): bare `node` is\r\n * unreliable on Windows Git Bash (MSYS) and MSYS rewrites absolute paths on\r\n * non-C: drives. Always use the resolved runtime path, forward slashes, and\r\n * double-quoting. No-ops on macOS/Linux.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { join } from \"node:path\";\r\n\r\nimport { isNonDefaultDataRoot } from \"./paths.js\";\r\nimport type { InstallScope, PlatformId, ServerDef } from \"./types.js\";\r\nimport type { InstallContext } from \"../adapters/spi.js\";\r\nimport type {\r\n LaunchMethod,\r\n TelemetryInstallScope,\r\n} from \"../telemetry/types.js\";\r\n\r\n/**\r\n * Narrow the framework's 5-value {@link InstallScope} down to the two telemetry\r\n * slicing buckets: only a `project`-local install is `project`; everything else\r\n * (system/user/profile/managed) is global and reads as `user`.\r\n */\r\nexport function narrowInstallScope(scope: InstallScope): TelemetryInstallScope {\r\n return scope === \"project\" ? \"project\" : \"user\";\r\n}\r\n\r\n/** Build `\"<nodePath>\" \"<scriptPath>\"`. */\r\nexport function buildNodeCommand(\r\n scriptPath: string,\r\n opts?: { nodePath?: string },\r\n): string {\r\n const nodePath = (opts?.nodePath ?? process.execPath).replace(/\\\\/g, \"/\");\r\n const safe = scriptPath.replace(/\\\\/g, \"/\");\r\n return `\"${nodePath}\" \"${safe}\"`;\r\n}\r\n\r\n/**\r\n * Strict inverse of buildNodeCommand. Returns the two quoted tokens, or null if\r\n * `cmd` was not produced by buildNodeCommand (no fragile last-whitespace tail\r\n * grabbing — that was the #548 doubled-path bug when paths contained spaces).\r\n */\r\nexport function parseNodeCommand(\r\n cmd: string,\r\n): { nodePath: string; scriptPath: string } | null {\r\n if (typeof cmd !== \"string\" || cmd.length === 0) return null;\r\n const m = cmd.match(/^\"([^\"]+)\"\\s+\"([^\"]+)\"\\s*$/);\r\n if (!m || !m[1] || !m[2]) return null;\r\n return { nodePath: m[1], scriptPath: m[2] };\r\n}\r\n\r\n/** Quote a single argument for inclusion in a host hook command string. */\r\nexport function quoteArg(arg: string): string {\r\n return `\"${arg.replace(/\\\\/g, \"/\")}\"`;\r\n}\r\n\r\n/**\r\n * True when `hay` (slash-normalized) has the quoted home-bin path IMMEDIATELY\r\n * followed by `verb` — i.e. the verb sits in its command SLOT (right after the\r\n * home binary), not merely somewhere in the argument list. This distinguishes\r\n * `\"<bin>\" statusline …` from `\"<bin>\" action … statusline …`: the action verb\r\n * takes a free-form `actionId` positional that can legitimately equal another\r\n * verb's name (`statusline` / `usage-event` / `hook`), and only the SLOT\r\n * position identifies the real verb. `homeBinNorm` is the slash-normalized\r\n * home-bin path the caller already derived.\r\n */\r\nfunction hasVerbSlot(hay: string, homeBinNorm: string, verb: string): boolean {\r\n return hay.includes(`\"${homeBinNorm}\" ${verb} `);\r\n}\r\n\r\n/**\r\n * Build the universal hook command a host config points at:\r\n * \"<homeBin>\" hook <platformId> <event> --connector <id>\r\n * Pointing every host at the one stable home binary is how a single update\r\n * propagates everywhere (docs/ARCHITECTURE.md §3 R1).\r\n */\r\nexport function buildHomeBinHookCommand(\r\n homeBinPath: string,\r\n platformId: string,\r\n event: string,\r\n connectorId: string,\r\n): string {\r\n return `${quoteArg(homeBinPath)} hook ${platformId} ${event} --connector ${connectorId}`;\r\n}\r\n\r\n/**\r\n * True when `command` is a home-bin hook command for exactly `connectorId`.\r\n *\r\n * Critical: the connector id is the LAST token of the hook command\r\n * (`... --connector <id>`), so a naive `command.includes(\"--connector \" + id)`\r\n * collides on shared-prefix ids — `--connector acme-db` contains\r\n * `--connector acme`, which would let uninstalling `acme` strip `acme-db`'s\r\n * hooks. We anchor the id token: the character after it must be end-of-string,\r\n * whitespace, or a closing double-quote (the JSON-embedded case). Connector ids\r\n * are kebab-case (ID_RE) so they never contain whitespace or quotes themselves.\r\n */\r\nexport function isHomeBinHookCommand(\r\n command: string | undefined,\r\n homeBinPath: string,\r\n connectorId: string,\r\n): boolean {\r\n if (!command) return false;\r\n // The stored command was forward-slashed by quoteArg(); homeBinPath() yields\r\n // NATIVE (back)slashes on Windows (node:path join). Normalize BOTH sides so the\r\n // ownership substring test holds on win32 (without this, detection always\r\n // returns false there → duplicate hooks on re-install + orphans on uninstall).\r\n // No-op on POSIX.\r\n const hay = command.replace(/\\\\/g, \"/\");\r\n if (!hay.includes(homeBinPath.replace(/\\\\/g, \"/\"))) return false;\r\n const token = `--connector ${connectorId}`;\r\n let from = 0;\r\n for (;;) {\r\n const idx = hay.indexOf(token, from);\r\n if (idx < 0) return false;\r\n const after = hay.charAt(idx + token.length);\r\n if (after === \"\" || after === '\"' || /\\s/.test(after)) return true;\r\n from = idx + token.length;\r\n }\r\n}\r\n\r\n/**\r\n * Build the OPT-IN host-native usage-event command an AfterModel / PostInvocation\r\n * hook points at:\r\n * \"<homeBin>\" usage-event <platformId> --connector <id>\r\n * Mirrors {@link buildHomeBinHookCommand} (same anchoring rules apply) but routes\r\n * to the hidden `usage-event` entrypoint, which records a DISTINCT `model_turn`\r\n * row instead of dispatching a connector handler.\r\n */\r\nexport function buildUsageEventCommand(\r\n homeBinPath: string,\r\n platformId: string,\r\n connectorId: string,\r\n): string {\r\n return `${quoteArg(homeBinPath)} usage-event ${platformId} --connector ${connectorId}`;\r\n}\r\n\r\n/**\r\n * True when `command` is OUR host-native usage-event command for exactly\r\n * `connectorId`. Same end-of-token anchoring as {@link isHomeBinHookCommand} so a\r\n * shared-prefix id can't collide; additionally requires the ` usage-event ` verb\r\n * so it is never confused with a plain `hook` command during uninstall.\r\n */\r\nexport function isUsageEventCommand(\r\n command: string | undefined,\r\n homeBinPath: string,\r\n connectorId: string,\r\n): boolean {\r\n if (!command) return false;\r\n // Normalize separators like isHomeBinHookCommand (win32 ownership detection).\r\n const hay = command.replace(/\\\\/g, \"/\");\r\n const homeBinNorm = homeBinPath.replace(/\\\\/g, \"/\");\r\n if (!hay.includes(homeBinNorm)) return false;\r\n if (!hasVerbSlot(hay, homeBinNorm, \"usage-event\")) return false;\r\n return isHomeBinHookCommand(command, homeBinPath, connectorId);\r\n}\r\n\r\n/**\r\n * Build the home-bin statusline command a host's status line config points at:\r\n * \"<homeBin>\" statusline <platformId> --connector <id>\r\n * Mirrors {@link buildHomeBinHookCommand} / {@link buildUsageEventCommand} (the\r\n * same single-home-binary indirection) but routes to the `statusline`\r\n * entrypoint, which renders the connector's HUD line for the host.\r\n */\r\nexport function buildHomeBinStatuslineCommand(\r\n homeBinPath: string,\r\n platformId: string,\r\n connectorId: string,\r\n): string {\r\n return `${quoteArg(homeBinPath)} statusline ${platformId} --connector ${connectorId}`;\r\n}\r\n\r\n/**\r\n * True when `command` is OUR home-bin statusline command for exactly\r\n * `connectorId`. Same end-of-token anchoring as {@link isHomeBinHookCommand} so a\r\n * shared-prefix id can't collide; additionally requires the ` statusline ` verb\r\n * so it is never confused with a plain `hook` / `usage-event` command.\r\n */\r\nexport function isHomeBinStatuslineCommand(\r\n command: string | undefined,\r\n homeBinPath: string,\r\n connectorId: string,\r\n): boolean {\r\n if (!command) return false;\r\n // Normalize separators like isHomeBinHookCommand (win32 ownership detection).\r\n const hay = command.replace(/\\\\/g, \"/\");\r\n const homeBinNorm = homeBinPath.replace(/\\\\/g, \"/\");\r\n if (!hay.includes(homeBinNorm)) return false;\r\n if (!hasVerbSlot(hay, homeBinNorm, \"statusline\")) return false;\r\n return isHomeBinHookCommand(command, homeBinPath, connectorId);\r\n}\r\n\r\n/**\r\n * Build the home-bin action command a future host affordance (slash command /\r\n * keybinding) points at:\r\n * \"<homeBin>\" action <platformId> <actionId> --connector <id>\r\n * Mirrors {@link buildHomeBinStatuslineCommand} (the same single-home-binary\r\n * indirection) but routes to the `action` entrypoint, which re-imports the\r\n * connector and runs the named action's run(ctx). v1 ships only this command\r\n * builder + the runtime verb; no adapter EMITS the affordance yet.\r\n */\r\nexport function buildHomeBinActionCommand(\r\n homeBinPath: string,\r\n platformId: string,\r\n actionId: string,\r\n connectorId: string,\r\n): string {\r\n return `${quoteArg(homeBinPath)} action ${platformId} ${actionId} --connector ${connectorId}`;\r\n}\r\n\r\n/**\r\n * True when `command` is OUR home-bin action command for exactly `connectorId`.\r\n * Same end-of-token anchoring as {@link isHomeBinHookCommand} so a shared-prefix\r\n * id can't collide; additionally requires the ` action ` verb so it is never\r\n * confused with a plain `hook` / `usage-event` / `statusline` command.\r\n */\r\nexport function isHomeBinActionCommand(\r\n command: string | undefined,\r\n homeBinPath: string,\r\n connectorId: string,\r\n): boolean {\r\n if (!command) return false;\r\n // Normalize separators like isHomeBinHookCommand (win32 ownership detection).\r\n const hay = command.replace(/\\\\/g, \"/\");\r\n const homeBinNorm = homeBinPath.replace(/\\\\/g, \"/\");\r\n if (!hay.includes(homeBinNorm)) return false;\r\n if (!hasVerbSlot(hay, homeBinNorm, \"action\")) return false;\r\n return isHomeBinHookCommand(command, homeBinPath, connectorId);\r\n}\r\n\r\n/**\r\n * Single source of truth for \"is OPT-IN host-native turn-usage capture enabled\r\n * for this connector?\" — read by the Gemini / Antigravity adapters at install\r\n * time to decide whether to ALSO write the AfterModel / PostInvocation usage hook.\r\n * Enabled when the connector opts in via telemetry.hostNativeUsage OR when the\r\n * install-time env switch AGENT_CONNECTOR_HOST_NATIVE=1 forces it on. Off by\r\n * default (privacy: host-native capture is never installed silently).\r\n */\r\nexport function isHostNativeUsageEnabled(\r\n telemetry: { enabled: boolean; hostNativeUsage?: boolean },\r\n env: NodeJS.ProcessEnv = process.env,\r\n): boolean {\r\n if (telemetry.enabled !== true) return false;\r\n if (env.AGENT_CONNECTOR_HOST_NATIVE === \"1\") return true;\r\n return telemetry.hostNativeUsage === true;\r\n}\r\n\r\n/**\r\n * Single source of truth for \"should this server be wrapped by `serve` for\r\n * telemetry?\" — so every adapter behaves identically even for a server that\r\n * skipped normalizeServer (e.g. a per-platform override). On by default for an\r\n * stdio server with a command, unless explicitly disabled or telemetry is off.\r\n */\r\nexport function shouldWrapForTelemetry(\r\n server: ServerDef,\r\n telemetry: { enabled: boolean },\r\n): boolean {\r\n return (\r\n server.wrapForTelemetry !== false &&\r\n telemetry.enabled === true &&\r\n server.transport === \"stdio\" &&\r\n typeof server.command === \"string\" &&\r\n server.command !== \"\"\r\n );\r\n}\r\n\r\n/**\r\n * Build the telemetry-wrapping server command:\r\n * \"<homeBin>\" serve --connector <id> --scope <scope> --host <platformId> -- <realCommand> <realArgs...>\r\n * Used when a stdio server opts into transparent telemetry capture.\r\n *\r\n * `scope` records the install dimension (global user vs project-local) on every\r\n * telemetry row so usage can be sliced by it later. It is OPTIONAL for backward\r\n * compatibility: when omitted, the `--scope` flag is not emitted and the runtime\r\n * treats the scope as \"unknown\".\r\n *\r\n * `platformId` bakes the install TARGET platform into the wrapper so the proxy\r\n * stamps `hostPlatform` correctly even under a headless spawn where runtime env\r\n * markers are absent (detectRuntimeHost only knows claude-code/cursor/codex and\r\n * otherwise mis-attributes). Emitted as `--host <platformId>` in the FLAG\r\n * section (before `--`). It is OPTIONAL + backward-compatible: when omitted the\r\n * `--host` flag is not emitted and the runtime falls back to env detection.\r\n *\r\n * `dataDir` pins the framework data-root the spawned `serve` child must use to\r\n * resolve the connector record (and write telemetry). Emitted as `--data-dir\r\n * <path>` in the FLAG section. It exists because some hosts (codex strips the\r\n * MCP child's environment) do NOT propagate `AGENT_CONNECTOR_DATA_DIR` to the\r\n * spawned server — so a child that relied on inheriting it would resolve the\r\n * DEFAULT `~/.agent-connector` and die with \"Connector not registered\" when the\r\n * install wrote the record under an OVERRIDDEN root. Passing it explicitly makes\r\n * the wrap env-independent for any env-stripping host. OPTIONAL + backward-\r\n * compatible: callers pass it only for a non-default root (a default-root install\r\n * stays flag-free and relies on the correct fallback), and when omitted the\r\n * `--data-dir` flag is not emitted and the runtime resolves the data-root the\r\n * usual way.\r\n */\r\nexport function buildServeWrapperCommand(\r\n homeBinPath: string,\r\n connectorId: string,\r\n realCommand: string,\r\n realArgs: string[],\r\n scope?: InstallScope,\r\n platformId?: PlatformId,\r\n dataDir?: string,\r\n): { command: string; args: string[] } {\r\n const flags = [\"serve\", \"--connector\", connectorId];\r\n if (scope !== undefined) flags.push(\"--scope\", narrowInstallScope(scope));\r\n if (platformId !== undefined) flags.push(\"--host\", platformId);\r\n if (dataDir !== undefined && dataDir !== \"\") flags.push(\"--data-dir\", dataDir);\r\n return {\r\n command: homeBinPath,\r\n args: [...flags, \"--\", realCommand, ...realArgs],\r\n };\r\n}\r\n\r\n/**\r\n * Telemetry serve-wrap for a stdio command: when the connector opts in, route\r\n * `command`/`args` through `<homeBin> serve --connector <id> -- …`; otherwise\r\n * return them unchanged. Consolidates the per-host wrap snippet — the caller\r\n * keeps its own command/args seeding + downstream env-resolution + entry shaping.\r\n *\r\n * When the install's framework data-root is NON-DEFAULT (overridden via\r\n * AGENT_CONNECTOR_DATA_DIR), the wrap also carries an explicit `--data-dir\r\n * <root>` so the spawned `serve` child resolves the connector record from that\r\n * root even on hosts that strip the child's environment (codex). A default-root\r\n * install emits no flag and relies on the correct runtime fallback.\r\n */\r\nexport function buildWrappedStdio(\r\n ctx: Pick<InstallContext, \"homeBinPath\" | \"connector\" | \"scope\" | \"dataRoot\">,\r\n server: ServerDef,\r\n platformId: PlatformId,\r\n command: string,\r\n args: string[],\r\n): { command: string; args: string[] } {\r\n if (!shouldWrapForTelemetry(server, ctx.connector.telemetry)) {\r\n return { command, args };\r\n }\r\n const dataDir = isNonDefaultDataRoot(ctx.dataRoot) ? ctx.dataRoot : undefined;\r\n return buildServeWrapperCommand(\r\n ctx.homeBinPath,\r\n ctx.connector.id,\r\n command,\r\n args,\r\n ctx.scope,\r\n platformId,\r\n dataDir,\r\n );\r\n}\r\n\r\n/** Injectable deps for {@link resolveSpawnCommand} (so the win32 branch is unit-testable on POSIX). */\r\nexport interface SpawnResolveDeps {\r\n platform?: NodeJS.Platform;\r\n /** PATH value (`;`-separated on Windows). */\r\n pathEnv?: string;\r\n /** PATHEXT value (`;`-separated). */\r\n pathExt?: string;\r\n /** File-existence probe (defaults to fs.existsSync). */\r\n exists?: (p: string) => boolean;\r\n}\r\n\r\n/** How a child command should be handed to spawn() on this platform. */\r\nexport interface SpawnResolution {\r\n /** The command to pass to spawn() (an absolute path on win32 when resolved). */\r\n file: string;\r\n /**\r\n * Whether spawn() MUST use `shell: true`. On native Windows a `.cmd`/`.bat`\r\n * shim (npx.cmd, pnpm.cmd, the agent-connector.cmd launcher) cannot be run by\r\n * raw CreateProcess — Node throws EINVAL since the 2024 security fix unless a\r\n * shell is used. `.exe` resolves directly with no shell.\r\n */\r\n needsShell: boolean;\r\n}\r\n\r\n/**\r\n * Resolve a child command for spawn() across platforms. NO-OP on macOS/Linux\r\n * (returns the command unchanged, no shell). On native Windows it fixes the two\r\n * spawn failure modes for bare package runners:\r\n * 1. raw CreateProcess ignores PATHEXT, so spawn(\"npx\") → ENOENT even though\r\n * `npx.cmd` is on PATH. We search PATH × PATHEXT and return the resolved\r\n * absolute path.\r\n * 2. a resolved `.cmd`/`.bat` still cannot be launched without a shell, so we\r\n * flag `needsShell` for those (and `.exe`/`.com` run directly).\r\n * A bare name that PATH search misses falls back to `{ file: command,\r\n * needsShell: true }` so cmd.exe's own PATHEXT lookup still finds it.\r\n */\r\nexport function resolveSpawnCommand(\r\n command: string,\r\n deps: SpawnResolveDeps = {},\r\n): SpawnResolution {\r\n const platform = deps.platform ?? process.platform;\r\n if (platform !== \"win32\") return { file: command, needsShell: false };\r\n\r\n const isBatch = (s: string): boolean => /\\.(cmd|bat)$/i.test(s);\r\n\r\n // Already a path or already has an extension → don't search; only decide shell.\r\n if (/[\\\\/]/.test(command) || /\\.[a-z0-9]+$/i.test(command)) {\r\n return { file: command, needsShell: isBatch(command) };\r\n }\r\n\r\n const exists = deps.exists ?? existsSync;\r\n const pathEnv = deps.pathEnv ?? process.env.PATH ?? process.env.Path ?? \"\";\r\n const pathExt = deps.pathExt ?? process.env.PATHEXT ?? \".COM;.EXE;.BAT;.CMD\";\r\n const dirs = pathEnv.split(\";\").filter((d) => d !== \"\");\r\n const exts = pathExt.split(\";\").filter((e) => e !== \"\");\r\n for (const dir of dirs) {\r\n for (const ext of exts) {\r\n const candidate = join(dir, command + ext);\r\n if (exists(candidate)) return { file: candidate, needsShell: isBatch(ext) };\r\n }\r\n }\r\n // Not found on PATH — let cmd.exe resolve it (handles shim dirs we may miss).\r\n return { file: command, needsShell: true };\r\n}\r\n\r\n/** Basenames (case-insensitive, sans common extensions) of the ephemeral package runners. */\r\nconst PACKAGE_RUNNERS: Record<string, Extract<LaunchMethod, \"npx\" | \"bunx\" | \"uvx\">> = {\r\n npx: \"npx\",\r\n bunx: \"bunx\",\r\n uvx: \"uvx\",\r\n};\r\n\r\n/** Basenames of language interpreters that launch a local script. */\r\nconst INTERPRETERS: ReadonlySet<string> = new Set([\"node\", \"bun\", \"deno\"]);\r\n\r\n/**\r\n * Strip a directory prefix and a trailing executable extension to get the bare\r\n * program name. Tolerates both POSIX and Windows separators (the wrapped command\r\n * may be an absolute Windows path) and lowercases for case-insensitive matching.\r\n */\r\nfunction commandBasename(realCommand: string): string {\r\n const noDir = realCommand.replace(/\\\\/g, \"/\").split(\"/\").pop() ?? realCommand;\r\n return noDir.replace(/\\.(exe|cmd|bat|com)$/i, \"\").toLowerCase();\r\n}\r\n\r\n/**\r\n * Classify how the real MCP server is launched, for the \"launch-method\" slicing\r\n * dimension. Package runners (npx/bunx/uvx) and interpreters (node/bun/deno) are\r\n * detected by the command basename. A remote (http) server is not launched\r\n * locally, so the caller — which alone knows the transport — passes\r\n * `isRemote: true` to force \"http\". Anything else resolves to \"binary\".\r\n */\r\nexport function detectLaunchMethod(\r\n realCommand: string,\r\n realArgs: string[],\r\n opts?: { isRemote?: boolean },\r\n): LaunchMethod {\r\n void realArgs; // reserved: args may later refine node-vs-script heuristics\r\n if (opts?.isRemote === true) return \"http\";\r\n if (typeof realCommand !== \"string\" || realCommand.trim() === \"\") {\r\n return \"unknown\";\r\n }\r\n const base = commandBasename(realCommand);\r\n if (base === \"\") return \"unknown\";\r\n const runner = PACKAGE_RUNNERS[base];\r\n if (runner !== undefined) return runner;\r\n if (INTERPRETERS.has(base)) return \"node\";\r\n return \"binary\";\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/usage/jsonl.ts"],"sourcesContent":["/**\r\n * usage/jsonl — tolerant JSONL / JSON readers + a file-mtime helper.\r\n *\r\n * Every reader is fail-open: a malformed line is skipped (never thrown), a\r\n * missing/unreadable file yields no rows. Mirrors the NdjsonStore.query and host\r\n * parser behavior — bad input degrades to empty, it does not crash a report.\r\n *\r\n * Ports tokscale sessions/utils.rs file_modified_timestamp_ms (mtime fallback)\r\n * and the per-line skip-malformed loop used by every JSONL parser.\r\n */\r\n\r\nimport { readFileSync, statSync } from \"node:fs\";\r\n\r\n/**\r\n * Parse a JSONL file into objects, skipping blank/malformed lines. Returns [] on\r\n * any read error (missing file, permissions). Each yielded value is whatever\r\n * `JSON.parse` produced — callers narrow it. Order matches the file.\r\n *\r\n * Returns an array (not a generator) so callers can index/slice; for the file\r\n * sizes here (session logs) a full read is acceptable, matching the Rust\r\n * BufReader-per-file approach.\r\n */\r\nexport function readJsonlLines(path: string): unknown[] {\r\n let text: string;\r\n try {\r\n text = readFileSync(path, \"utf8\");\r\n } catch {\r\n return [];\r\n }\r\n\r\n const out: unknown[] = [];\r\n for (const line of text.split(\"\\n\")) {\r\n const trimmed = line.trim();\r\n if (trimmed === \"\") continue;\r\n try {\r\n out.push(JSON.parse(trimmed));\r\n } catch {\r\n continue; // skip malformed line\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Read and parse a whole JSON file. Returns undefined on any read/parse error\r\n * (the fail-open contract for JSON readers). Caller narrows the unknown result.\r\n */\r\nexport function readJsonFile(path: string): unknown {\r\n let text: string;\r\n try {\r\n text = readFileSync(path, \"utf8\");\r\n } catch {\r\n return undefined;\r\n }\r\n try {\r\n return JSON.parse(text);\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * File modification time in epoch ms, used as a timestamp fallback when a log\r\n * line carries none. Port of file_modified_timestamp_ms: falls back to \"now\"\r\n * when the file cannot be stat'd so a row is never dropped for lack of a ts.\r\n */\r\nexport function fileMtimeMs(path: string): number {\r\n try {\r\n return Math.floor(statSync(path).mtimeMs);\r\n } catch {\r\n return Date.now();\r\n }\r\n}\r\n"],"mappings":";;;AAWA,SAAS,cAAc,gBAAgB;AAWhC,SAAS,eAAe,MAAyB;AACtD,MAAI;AACJ,MAAI;AACF,WAAO,aAAa,MAAM,MAAM;AAAA,EAClC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,MAAiB,CAAC;AACxB,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,YAAY,GAAI;AACpB,QAAI;AACF,UAAI,KAAK,KAAK,MAAM,OAAO,CAAC;AAAA,IAC9B,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,aAAa,MAAuB;AAClD,MAAI;AACJ,MAAI;AACF,WAAO,aAAa,MAAM,MAAM;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOO,SAAS,YAAY,MAAsB;AAChD,MAAI;AACF,WAAO,KAAK,MAAM,SAAS,IAAI,EAAE,OAAO;AAAA,EAC1C,QAAQ;AACN,WAAO,KAAK,IAAI;AAAA,EAClB;AACF;","names":[]}
|
|
@@ -3,18 +3,18 @@ import {
|
|
|
3
3
|
fileMtimeMs,
|
|
4
4
|
readJsonFile,
|
|
5
5
|
readJsonlLines
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZSOS2KOR.js";
|
|
7
7
|
import {
|
|
8
8
|
firstExistingRoot,
|
|
9
9
|
isFile,
|
|
10
10
|
walkFiles
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-KM2YXO6V.js";
|
|
12
12
|
import {
|
|
13
13
|
emptyTokens,
|
|
14
14
|
inferProvider,
|
|
15
15
|
normalizeWorkspaceKey,
|
|
16
16
|
workspaceLabelFromKey
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-7PSNXUFK.js";
|
|
18
18
|
|
|
19
19
|
// src/usage/readers/claude-code.ts
|
|
20
20
|
import { basename, dirname, sep } from "path";
|
|
@@ -649,4 +649,4 @@ var claude_code_default = claudeCodeReader;
|
|
|
649
649
|
export {
|
|
650
650
|
claude_code_default as default
|
|
651
651
|
};
|
|
652
|
-
//# sourceMappingURL=claude-code-
|
|
652
|
+
//# sourceMappingURL=claude-code-FS42PXZO.js.map
|