@ken-jo/agent-connector 0.4.97 → 0.4.99
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 +604 -596
- package/dist/{action-IPFDPOZU.js → action-5OEM3QC4.js} +17 -16
- package/dist/action-5OEM3QC4.js.map +1 -0
- package/dist/{amazon-q-IT44LEWV.js → amazon-q-7DPVJXJN.js} +6 -6
- package/dist/amazon-q-7DPVJXJN.js.map +1 -0
- package/dist/{amp-CEWOSWQO.js → amp-BQZOL5GR.js} +4 -4
- package/dist/amp-BQZOL5GR.js.map +1 -0
- package/dist/{amp-P35FRU2Z.js → amp-VYXZWHXW.js} +7 -7
- package/dist/amp-VYXZWHXW.js.map +1 -0
- package/dist/{antigravity-LHFEFNMC.js → antigravity-EA4L225L.js} +5 -5
- package/dist/antigravity-EA4L225L.js.map +1 -0
- package/dist/antigravity-VXHL6ZPM.js +18 -0
- package/dist/{antigravity-cli-MOKFSWOU.js → antigravity-cli-B2HXZFYQ.js} +5 -5
- package/dist/antigravity-cli-B2HXZFYQ.js.map +1 -0
- package/dist/{antigravity-cli-MPE27LPI.js → antigravity-cli-JHQ7JGNX.js} +10 -9
- package/dist/antigravity-cli-JHQ7JGNX.js.map +1 -0
- package/dist/{audit-3E5JAOP2.js → audit-BO7JQYIH.js} +8 -8
- package/dist/{chunk-7C2IPIIB.js → chunk-2Y2MZRLP.js} +3 -3
- package/dist/chunk-2Y2MZRLP.js.map +1 -0
- package/dist/{chunk-GZZCHJDP.js → chunk-4KIXXJ7B.js} +2 -2
- package/dist/chunk-4KIXXJ7B.js.map +1 -0
- package/dist/chunk-4NFDNY3C.js +34 -0
- package/dist/chunk-4NFDNY3C.js.map +1 -0
- package/dist/{chunk-C63AAXK7.js → chunk-4QXCNZMD.js} +5 -5
- package/dist/chunk-4QXCNZMD.js.map +1 -0
- package/dist/{chunk-Q7PR2WGT.js → chunk-6TF7MHG5.js} +2 -2
- package/dist/chunk-6TF7MHG5.js.map +1 -0
- package/dist/{chunk-7ZFK2WEO.js → chunk-6WN37NVN.js} +113 -10
- package/dist/chunk-6WN37NVN.js.map +1 -0
- package/dist/{chunk-GCPEPUD7.js → chunk-6XMV2OOA.js} +6 -6
- package/dist/chunk-6XMV2OOA.js.map +1 -0
- package/dist/{chunk-XXLCMSD3.js → chunk-BEFVJ2OE.js} +1 -1
- package/dist/chunk-BEFVJ2OE.js.map +1 -0
- package/dist/{chunk-N4ENLZEU.js → chunk-BV5WK6VQ.js} +19 -19
- package/dist/chunk-BV5WK6VQ.js.map +1 -0
- package/dist/{chunk-WPTNMYYM.js → chunk-BXPSDNIB.js} +6 -6
- package/dist/chunk-BXPSDNIB.js.map +1 -0
- package/dist/{chunk-Q4ISQHUQ.js → chunk-FBJQGRET.js} +1 -1
- package/dist/chunk-FBJQGRET.js.map +1 -0
- package/dist/{chunk-THI4PI4H.js → chunk-FJCUKXGX.js} +2 -2
- package/dist/chunk-FJCUKXGX.js.map +1 -0
- package/dist/{chunk-L2C5UMKW.js → chunk-FKW5A6HE.js} +23 -7
- package/dist/chunk-FKW5A6HE.js.map +1 -0
- package/dist/{chunk-NDMNMAKP.js → chunk-G5HAIYRM.js} +43 -43
- package/dist/chunk-G5HAIYRM.js.map +1 -0
- package/dist/{chunk-SLJI5BOM.js → chunk-GDIAHWNY.js} +2 -2
- package/dist/chunk-GDIAHWNY.js.map +1 -0
- package/dist/{chunk-QYOUQCC6.js → chunk-GZGLCCEE.js} +1 -1
- package/dist/chunk-GZGLCCEE.js.map +1 -0
- package/dist/{chunk-PSGTHJQU.js → chunk-H53QZQJI.js} +1 -1
- package/dist/chunk-H53QZQJI.js.map +1 -0
- package/dist/{chunk-7HHW23AC.js → chunk-IRUYJD23.js} +29 -29
- package/dist/chunk-IRUYJD23.js.map +1 -0
- package/dist/{chunk-IOJDZJ72.js → chunk-IVAWP6HH.js} +2 -2
- package/dist/chunk-IVAWP6HH.js.map +1 -0
- package/dist/{chunk-3FINQ2ZF.js → chunk-JTN3AKZO.js} +30 -20
- package/dist/chunk-JTN3AKZO.js.map +1 -0
- package/dist/{chunk-4GH2KA7V.js → chunk-KHO5WNTP.js} +1 -1
- package/dist/chunk-KHO5WNTP.js.map +1 -0
- package/dist/{chunk-MLU7NDAZ.js → chunk-KLZBRNFP.js} +12 -12
- package/dist/chunk-KLZBRNFP.js.map +1 -0
- package/dist/{chunk-3EP5V2F4.js → chunk-M6OS4OLU.js} +3 -3
- package/dist/chunk-M6OS4OLU.js.map +1 -0
- package/dist/{chunk-UZZTWWGG.js → chunk-MQPNU2CY.js} +1 -1
- package/dist/chunk-MQPNU2CY.js.map +1 -0
- package/dist/{chunk-LQ55E7QI.js → chunk-N4STCQGP.js} +7 -7
- package/dist/chunk-N4STCQGP.js.map +1 -0
- package/dist/{chunk-KFPSJRHR.js → chunk-NBYKLUF6.js} +2 -2
- package/dist/chunk-NBYKLUF6.js.map +1 -0
- package/dist/{chunk-KM2YXO6V.js → chunk-O7SDRBVB.js} +1 -1
- package/dist/chunk-O7SDRBVB.js.map +1 -0
- package/dist/{chunk-LFWYYWLV.js → chunk-OP4KBXUO.js} +1 -1
- package/dist/chunk-OP4KBXUO.js.map +1 -0
- package/dist/{chunk-XN7S44U4.js → chunk-OPXAP3AC.js} +1 -1
- package/dist/chunk-OPXAP3AC.js.map +1 -0
- package/dist/{chunk-TGZFDXNH.js → chunk-PWCBWAM4.js} +6 -4
- package/dist/chunk-PWCBWAM4.js.map +1 -0
- package/dist/{chunk-Y3K6KJMS.js → chunk-QSY74NAP.js} +2 -2
- package/dist/chunk-QSY74NAP.js.map +1 -0
- package/dist/{chunk-ZSOS2KOR.js → chunk-RK6VBLXP.js} +1 -1
- package/dist/chunk-RK6VBLXP.js.map +1 -0
- package/dist/{chunk-FHYR5Q5T.js → chunk-SFY6YLVK.js} +19 -12
- package/dist/chunk-SFY6YLVK.js.map +1 -0
- package/dist/{chunk-C747DYNT.js → chunk-UM5S7DKK.js} +1 -1
- package/dist/chunk-UM5S7DKK.js.map +1 -0
- package/dist/{chunk-ILWHLGIC.js → chunk-UMQ6NBQR.js} +2 -2
- package/dist/chunk-UMQ6NBQR.js.map +1 -0
- package/dist/{chunk-7PSNXUFK.js → chunk-XD7MY6HB.js} +1 -1
- package/dist/chunk-XD7MY6HB.js.map +1 -0
- package/dist/{chunk-XVEJAFKW.js → chunk-XY3N2U6L.js} +5 -5
- package/dist/chunk-XY3N2U6L.js.map +1 -0
- package/dist/{chunk-XZWMJLIT.js → chunk-YT43MUSJ.js} +1 -1
- package/dist/chunk-YT43MUSJ.js.map +1 -0
- package/dist/{claude-code-QBWDT5RT.js → claude-code-LJ6DVXWQ.js} +24 -14
- package/dist/claude-code-LJ6DVXWQ.js.map +1 -0
- package/dist/{claude-code-FS42PXZO.js → claude-code-NMRKTAP7.js} +4 -4
- package/dist/claude-code-NMRKTAP7.js.map +1 -0
- package/dist/cli/sdk.js +7 -7
- package/dist/cli/sdk.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/{cline-JKEAYWVR.js → cline-JAPJNOZQ.js} +6 -6
- package/dist/cline-JAPJNOZQ.js.map +1 -0
- package/dist/{codebuddy-EB6EYPC3.js → codebuddy-GC7VAE2Y.js} +7 -7
- package/dist/codebuddy-GC7VAE2Y.js.map +1 -0
- package/dist/{codebuff-5RSXFU6G.js → codebuff-3VODFPD4.js} +6 -6
- package/dist/codebuff-3VODFPD4.js.map +1 -0
- package/dist/{codebuff-6KWBW6CT.js → codebuff-ETYT5UBL.js} +4 -4
- package/dist/codebuff-ETYT5UBL.js.map +1 -0
- package/dist/{codex-ART46CDD.js → codex-RXIS5BHM.js} +9 -9
- package/dist/codex-RXIS5BHM.js.map +1 -0
- package/dist/{codex-DKBAAUCO.js → codex-YAORPCOD.js} +4 -4
- package/dist/codex-YAORPCOD.js.map +1 -0
- package/dist/{continue-JHZCMSPH.js → continue-MECVDV4Z.js} +8 -8
- package/dist/continue-MECVDV4Z.js.map +1 -0
- package/dist/{copilot-cli-CLBJACQ5.js → copilot-cli-MZTAWMPW.js} +4 -4
- package/dist/copilot-cli-MZTAWMPW.js.map +1 -0
- package/dist/{copilot-cli-QVAU5CFM.js → copilot-cli-WVWAWNSM.js} +7 -7
- package/dist/copilot-cli-WVWAWNSM.js.map +1 -0
- package/dist/{crush-A5C5USRL.js → crush-QBXLNN7T.js} +4 -4
- package/dist/crush-QBXLNN7T.js.map +1 -0
- package/dist/{crush-7TORBYUL.js → crush-YB3SK6NK.js} +7 -7
- package/dist/crush-YB3SK6NK.js.map +1 -0
- package/dist/{cursor-D5SWDVSW.js → cursor-DIW43TRZ.js} +3 -3
- package/dist/cursor-DIW43TRZ.js.map +1 -0
- package/dist/cursor-RSLHA2CY.js +22 -0
- package/dist/{define-connector-DTWb46UB.d.ts → define-connector-odRHEC4H.d.ts} +1 -1
- package/dist/{detect-JHSA3G3U.js → detect-ALP4G7WG.js} +4 -4
- package/dist/detect-ALP4G7WG.js.map +1 -0
- package/dist/{devin-SSR7S4WX.js → devin-WPDN3MXE.js} +7 -7
- package/dist/devin-WPDN3MXE.js.map +1 -0
- package/dist/{doctor-CQJ7TZPA.js → doctor-YEQWCWZJ.js} +27 -26
- package/dist/doctor-YEQWCWZJ.js.map +1 -0
- package/dist/{droid-45PJ7JGF.js → droid-HS6MX3IL.js} +10 -8
- package/dist/droid-HS6MX3IL.js.map +1 -0
- package/dist/{droid-PFQ2PZSF.js → droid-U3ZBLQFO.js} +4 -4
- package/dist/droid-U3ZBLQFO.js.map +1 -0
- package/dist/{gemini-cli-Y6RJMU24.js → gemini-cli-NMVVBO3K.js} +4 -4
- package/dist/gemini-cli-NMVVBO3K.js.map +1 -0
- package/dist/{gemini-cli-24PFDK7D.js → gemini-cli-VJ6X5DNF.js} +8 -8
- package/dist/gemini-cli-VJ6X5DNF.js.map +1 -0
- package/dist/{goose-U5D3LCPC.js → goose-3X7ID7YW.js} +9 -9
- package/dist/goose-3X7ID7YW.js.map +1 -0
- package/dist/{goose-K2HKUTBK.js → goose-64V46Y2W.js} +4 -4
- package/dist/goose-64V46Y2W.js.map +1 -0
- package/dist/{grok-cli-L2MHAPBY.js → grok-cli-4VFVF2CQ.js} +7 -7
- package/dist/grok-cli-4VFVF2CQ.js.map +1 -0
- package/dist/{hermes-2S5JUTTJ.js → hermes-AIVUE7LX.js} +10 -8
- package/dist/hermes-AIVUE7LX.js.map +1 -0
- package/dist/{hermes-MF3NX4ZY.js → hermes-F3EGTK6N.js} +4 -4
- package/dist/hermes-F3EGTK6N.js.map +1 -0
- package/dist/{hook-VHICXHXL.js → hook-F63I3SD5.js} +17 -16
- package/dist/hook-F63I3SD5.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +5 -5
- package/dist/{install-WLENR4KF.js → install-23WRRFBY.js} +21 -21
- package/dist/install-23WRRFBY.js.map +1 -0
- package/dist/{introspect-dNekqPDY.d.ts → introspect-gTx6GzpW.d.ts} +1 -1
- package/dist/{jetbrains-copilot-Z4FC3RM3.js → jetbrains-copilot-XBO2JZRO.js} +6 -6
- package/dist/jetbrains-copilot-XBO2JZRO.js.map +1 -0
- package/dist/{junie-ULGJJIHB.js → junie-UCOS6Q37.js} +6 -6
- package/dist/junie-UCOS6Q37.js.map +1 -0
- package/dist/{kilo-SMHPOHJ5.js → kilo-SW7BH6EG.js} +4 -4
- package/dist/kilo-SW7BH6EG.js.map +1 -0
- package/dist/{kilo-WWIJOVWD.js → kilo-T4OXNUYY.js} +8 -8
- package/dist/kilo-T4OXNUYY.js.map +1 -0
- package/dist/{kilo-cli-QKE2EQLS.js → kilo-cli-I2S3W245.js} +8 -8
- package/dist/kilo-cli-I2S3W245.js.map +1 -0
- package/dist/{kilo-cli-T2SRRRJS.js → kilo-cli-ZF272ZHN.js} +5 -5
- package/dist/kilo-cli-ZF272ZHN.js.map +1 -0
- package/dist/{kimi-MSIDEMCI.js → kimi-5FHEXGZD.js} +4 -4
- package/dist/kimi-5FHEXGZD.js.map +1 -0
- package/dist/{kimi-JMUFTVSF.js → kimi-VLSUFLYV.js} +7 -7
- package/dist/kimi-VLSUFLYV.js.map +1 -0
- package/dist/{kiro-UWUCRKR3.js → kiro-AQFXGOJX.js} +10 -8
- package/dist/kiro-AQFXGOJX.js.map +1 -0
- package/dist/{kiro-NYRRBIKW.js → kiro-WGOI5CGT.js} +4 -4
- package/dist/kiro-WGOI5CGT.js.map +1 -0
- package/dist/{leaderboard-NJWKGMXP.js → leaderboard-YBD3IKC2.js} +7 -7
- package/dist/leaderboard-YBD3IKC2.js.map +1 -0
- package/dist/{mimo-code-BPNYYIVM.js → mimo-code-KETL7W7D.js} +7 -7
- package/dist/mimo-code-KETL7W7D.js.map +1 -0
- package/dist/{mistral-vibe-LT3DAJ3L.js → mistral-vibe-3AU7N2CA.js} +6 -6
- package/dist/mistral-vibe-3AU7N2CA.js.map +1 -0
- package/dist/{mux-AHNUAY5Z.js → mux-BO2L5CTW.js} +6 -6
- package/dist/mux-BO2L5CTW.js.map +1 -0
- package/dist/{mux-7PIJ7HUQ.js → mux-XM6AP35L.js} +4 -4
- package/dist/mux-XM6AP35L.js.map +1 -0
- package/dist/{nemoclaw-RE5RZASZ.js → nemoclaw-WA7RSKF2.js} +7 -7
- package/dist/nemoclaw-WA7RSKF2.js.map +1 -0
- package/dist/{omp-D2EXEPH7.js → omp-UDYJ2PYY.js} +9 -7
- package/dist/omp-UDYJ2PYY.js.map +1 -0
- package/dist/{open-interpreter-SSGBEUKX.js → open-interpreter-WMBNM4MQ.js} +6 -6
- package/dist/open-interpreter-WMBNM4MQ.js.map +1 -0
- package/dist/openclaw-3DHVHX5P.js +17 -0
- package/dist/{openclaw-KAFTNLR2.js → openclaw-5EQFGOWL.js} +4 -4
- package/dist/openclaw-5EQFGOWL.js.map +1 -0
- package/dist/{opencode-YKUKZY3J.js → opencode-IKMI3RI7.js} +7 -7
- package/dist/opencode-IKMI3RI7.js.map +1 -0
- package/dist/{opencode-Z65JYILL.js → opencode-QGP4ALQY.js} +4 -4
- package/dist/opencode-QGP4ALQY.js.map +1 -0
- package/dist/{openhands-SEZMCUC3.js → openhands-QGXMQR6I.js} +7 -7
- package/dist/openhands-QGXMQR6I.js.map +1 -0
- package/dist/{package-JECVQEFT.js → package-TAUTPP7Z.js} +15 -15
- package/dist/package-TAUTPP7Z.js.map +1 -0
- package/dist/{pi-YPYV2ULH.js → pi-A2HCGRJG.js} +9 -7
- package/dist/pi-A2HCGRJG.js.map +1 -0
- package/dist/{pi-DLKCPOJE.js → pi-GGDQREVG.js} +4 -4
- package/dist/pi-GGDQREVG.js.map +1 -0
- package/dist/{qwen-code-ZHCLCYMC.js → qwen-code-DNQTGJOF.js} +35 -17
- package/dist/qwen-code-DNQTGJOF.js.map +1 -0
- package/dist/{qwen-code-26CSFDX6.js → qwen-code-GQILBJ5B.js} +4 -4
- package/dist/qwen-code-GQILBJ5B.js.map +1 -0
- package/dist/{roo-code-HSAE2CJU.js → roo-code-SUWI4BBN.js} +4 -4
- package/dist/roo-code-SUWI4BBN.js.map +1 -0
- package/dist/{roo-code-7F4HYB34.js → roo-code-VI2ZKTZC.js} +6 -6
- package/dist/roo-code-VI2ZKTZC.js.map +1 -0
- package/dist/runtime/index.d.ts +9 -8
- package/dist/runtime/index.js +15 -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 +12 -11
- package/dist/{serve-LYSBH43G.js → serve-6MDLRQS2.js} +17 -16
- package/dist/serve-6MDLRQS2.js.map +1 -0
- package/dist/{status-PHKKGEW5.js → status-IUP75XHP.js} +11 -11
- package/dist/status-IUP75XHP.js.map +1 -0
- package/dist/{statusline-ZDD4OZ4F.js → statusline-PRODZV2J.js} +16 -15
- package/dist/statusline-PRODZV2J.js.map +1 -0
- package/dist/{synthetic-LUT6V6SD.js → synthetic-6FRA7P5Q.js} +4 -4
- package/dist/synthetic-6FRA7P5Q.js.map +1 -0
- package/dist/{telemetry-HDGMI2BK.js → telemetry-X4PLZEDB.js} +10 -10
- package/dist/telemetry-X4PLZEDB.js.map +1 -0
- package/dist/{trae-SIQI3C66.js → trae-CPPYASR2.js} +4 -4
- package/dist/trae-CPPYASR2.js.map +1 -0
- package/dist/{trae-Y67HSDOW.js → trae-VOJXUYRN.js} +6 -6
- package/dist/trae-VOJXUYRN.js.map +1 -0
- package/dist/{types-JyhpJHJa.d.ts → types-DWXnnx-N.d.ts} +96 -22
- package/dist/{uninstall-RWO2GM2I.js → uninstall-BSIJ4QXN.js} +21 -21
- package/dist/uninstall-BSIJ4QXN.js.map +1 -0
- package/dist/{upgrade-YJCXNUEE.js → upgrade-ZSICU3NE.js} +21 -21
- package/dist/upgrade-ZSICU3NE.js.map +1 -0
- package/dist/{usage-IWO4RQN6.js → usage-EQUNVSCT.js} +4 -4
- package/dist/usage-EQUNVSCT.js.map +1 -0
- package/dist/{usage-event-AFFKCLHE.js → usage-event-J5CQ6LNT.js} +16 -15
- package/dist/usage-event-J5CQ6LNT.js.map +1 -0
- package/dist/{vscode-copilot-5CI5B3X6.js → vscode-copilot-ZBXRPKGG.js} +7 -7
- package/dist/vscode-copilot-ZBXRPKGG.js.map +1 -0
- package/dist/{warp-5YBSP7FP.js → warp-NRBBJQR2.js} +4 -4
- package/dist/warp-NRBBJQR2.js.map +1 -0
- package/dist/{warp-INAKVGBV.js → warp-S444B2S3.js} +9 -7
- package/dist/warp-S444B2S3.js.map +1 -0
- package/dist/{windsurf-4HM57QAY.js → windsurf-R6D2JUTN.js} +6 -6
- package/dist/windsurf-R6D2JUTN.js.map +1 -0
- package/dist/{zed-YN5X37PQ.js → zed-DQV3ZUCU.js} +10 -8
- package/dist/zed-DQV3ZUCU.js.map +1 -0
- package/dist/{zed-XXCNYOIN.js → zed-XJFOOKCO.js} +4 -4
- package/dist/zed-XJFOOKCO.js.map +1 -0
- package/package.json +88 -88
- package/dist/action-IPFDPOZU.js.map +0 -1
- package/dist/amazon-q-IT44LEWV.js.map +0 -1
- package/dist/amp-CEWOSWQO.js.map +0 -1
- package/dist/amp-P35FRU2Z.js.map +0 -1
- package/dist/antigravity-LHFEFNMC.js.map +0 -1
- package/dist/antigravity-TWIWXTQH.js +0 -18
- package/dist/antigravity-cli-MOKFSWOU.js.map +0 -1
- package/dist/antigravity-cli-MPE27LPI.js.map +0 -1
- package/dist/chunk-3EP5V2F4.js.map +0 -1
- package/dist/chunk-3FINQ2ZF.js.map +0 -1
- package/dist/chunk-4GH2KA7V.js.map +0 -1
- package/dist/chunk-7C2IPIIB.js.map +0 -1
- package/dist/chunk-7HHW23AC.js.map +0 -1
- package/dist/chunk-7PSNXUFK.js.map +0 -1
- package/dist/chunk-7ZFK2WEO.js.map +0 -1
- package/dist/chunk-C63AAXK7.js.map +0 -1
- package/dist/chunk-C747DYNT.js.map +0 -1
- package/dist/chunk-FHYR5Q5T.js.map +0 -1
- package/dist/chunk-GCPEPUD7.js.map +0 -1
- package/dist/chunk-GZZCHJDP.js.map +0 -1
- package/dist/chunk-ILWHLGIC.js.map +0 -1
- package/dist/chunk-IOJDZJ72.js.map +0 -1
- package/dist/chunk-KFPSJRHR.js.map +0 -1
- package/dist/chunk-KM2YXO6V.js.map +0 -1
- package/dist/chunk-L2C5UMKW.js.map +0 -1
- package/dist/chunk-LFWYYWLV.js.map +0 -1
- package/dist/chunk-LQ55E7QI.js.map +0 -1
- package/dist/chunk-MLU7NDAZ.js.map +0 -1
- package/dist/chunk-N4ENLZEU.js.map +0 -1
- package/dist/chunk-NDMNMAKP.js.map +0 -1
- package/dist/chunk-PSGTHJQU.js.map +0 -1
- package/dist/chunk-Q4ISQHUQ.js.map +0 -1
- package/dist/chunk-Q7PR2WGT.js.map +0 -1
- package/dist/chunk-QYOUQCC6.js.map +0 -1
- package/dist/chunk-SLJI5BOM.js.map +0 -1
- package/dist/chunk-TGZFDXNH.js.map +0 -1
- package/dist/chunk-THI4PI4H.js.map +0 -1
- package/dist/chunk-UZZTWWGG.js.map +0 -1
- package/dist/chunk-WPTNMYYM.js.map +0 -1
- package/dist/chunk-XN7S44U4.js.map +0 -1
- package/dist/chunk-XVEJAFKW.js.map +0 -1
- package/dist/chunk-XXLCMSD3.js.map +0 -1
- package/dist/chunk-XZWMJLIT.js.map +0 -1
- package/dist/chunk-Y3K6KJMS.js.map +0 -1
- package/dist/chunk-ZSOS2KOR.js.map +0 -1
- package/dist/claude-code-FS42PXZO.js.map +0 -1
- package/dist/claude-code-QBWDT5RT.js.map +0 -1
- package/dist/cline-JKEAYWVR.js.map +0 -1
- package/dist/codebuddy-EB6EYPC3.js.map +0 -1
- package/dist/codebuff-5RSXFU6G.js.map +0 -1
- package/dist/codebuff-6KWBW6CT.js.map +0 -1
- package/dist/codex-ART46CDD.js.map +0 -1
- package/dist/codex-DKBAAUCO.js.map +0 -1
- package/dist/continue-JHZCMSPH.js.map +0 -1
- package/dist/copilot-cli-CLBJACQ5.js.map +0 -1
- package/dist/copilot-cli-QVAU5CFM.js.map +0 -1
- package/dist/crush-7TORBYUL.js.map +0 -1
- package/dist/crush-A5C5USRL.js.map +0 -1
- package/dist/cursor-D5SWDVSW.js.map +0 -1
- package/dist/cursor-W7I4QUFO.js +0 -22
- package/dist/detect-JHSA3G3U.js.map +0 -1
- package/dist/devin-SSR7S4WX.js.map +0 -1
- package/dist/doctor-CQJ7TZPA.js.map +0 -1
- package/dist/droid-45PJ7JGF.js.map +0 -1
- package/dist/droid-PFQ2PZSF.js.map +0 -1
- package/dist/gemini-cli-24PFDK7D.js.map +0 -1
- package/dist/gemini-cli-Y6RJMU24.js.map +0 -1
- package/dist/goose-K2HKUTBK.js.map +0 -1
- package/dist/goose-U5D3LCPC.js.map +0 -1
- package/dist/grok-cli-L2MHAPBY.js.map +0 -1
- package/dist/hermes-2S5JUTTJ.js.map +0 -1
- package/dist/hermes-MF3NX4ZY.js.map +0 -1
- package/dist/hook-VHICXHXL.js.map +0 -1
- package/dist/install-WLENR4KF.js.map +0 -1
- package/dist/jetbrains-copilot-Z4FC3RM3.js.map +0 -1
- package/dist/junie-ULGJJIHB.js.map +0 -1
- package/dist/kilo-SMHPOHJ5.js.map +0 -1
- package/dist/kilo-WWIJOVWD.js.map +0 -1
- package/dist/kilo-cli-QKE2EQLS.js.map +0 -1
- package/dist/kilo-cli-T2SRRRJS.js.map +0 -1
- package/dist/kimi-JMUFTVSF.js.map +0 -1
- package/dist/kimi-MSIDEMCI.js.map +0 -1
- package/dist/kiro-NYRRBIKW.js.map +0 -1
- package/dist/kiro-UWUCRKR3.js.map +0 -1
- package/dist/leaderboard-NJWKGMXP.js.map +0 -1
- package/dist/mimo-code-BPNYYIVM.js.map +0 -1
- package/dist/mistral-vibe-LT3DAJ3L.js.map +0 -1
- package/dist/mux-7PIJ7HUQ.js.map +0 -1
- package/dist/mux-AHNUAY5Z.js.map +0 -1
- package/dist/nemoclaw-RE5RZASZ.js.map +0 -1
- package/dist/omp-D2EXEPH7.js.map +0 -1
- package/dist/open-interpreter-SSGBEUKX.js.map +0 -1
- package/dist/openclaw-IJ33K45M.js +0 -17
- package/dist/openclaw-KAFTNLR2.js.map +0 -1
- package/dist/opencode-YKUKZY3J.js.map +0 -1
- package/dist/opencode-Z65JYILL.js.map +0 -1
- package/dist/openhands-SEZMCUC3.js.map +0 -1
- package/dist/package-JECVQEFT.js.map +0 -1
- package/dist/pi-DLKCPOJE.js.map +0 -1
- package/dist/pi-YPYV2ULH.js.map +0 -1
- package/dist/qwen-code-26CSFDX6.js.map +0 -1
- package/dist/qwen-code-ZHCLCYMC.js.map +0 -1
- package/dist/roo-code-7F4HYB34.js.map +0 -1
- package/dist/roo-code-HSAE2CJU.js.map +0 -1
- package/dist/serve-LYSBH43G.js.map +0 -1
- package/dist/status-PHKKGEW5.js.map +0 -1
- package/dist/statusline-ZDD4OZ4F.js.map +0 -1
- package/dist/synthetic-LUT6V6SD.js.map +0 -1
- package/dist/telemetry-HDGMI2BK.js.map +0 -1
- package/dist/trae-SIQI3C66.js.map +0 -1
- package/dist/trae-Y67HSDOW.js.map +0 -1
- package/dist/uninstall-RWO2GM2I.js.map +0 -1
- package/dist/upgrade-YJCXNUEE.js.map +0 -1
- package/dist/usage-IWO4RQN6.js.map +0 -1
- package/dist/usage-event-AFFKCLHE.js.map +0 -1
- package/dist/vscode-copilot-5CI5B3X6.js.map +0 -1
- package/dist/warp-5YBSP7FP.js.map +0 -1
- package/dist/warp-INAKVGBV.js.map +0 -1
- package/dist/windsurf-4HM57QAY.js.map +0 -1
- package/dist/zed-XXCNYOIN.js.map +0 -1
- package/dist/zed-YN5X37PQ.js.map +0 -1
- /package/dist/{antigravity-TWIWXTQH.js.map → antigravity-VXHL6ZPM.js.map} +0 -0
- /package/dist/{audit-3E5JAOP2.js.map → audit-BO7JQYIH.js.map} +0 -0
- /package/dist/{cursor-W7I4QUFO.js.map → cursor-RSLHA2CY.js.map} +0 -0
- /package/dist/{openclaw-IJ33K45M.js.map → openclaw-3DHVHX5P.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/usage/readers/cursor.ts"],"sourcesContent":["/**\r\n * usage/readers/cursor — Cursor IDE usage reader (SYNCED platform).\r\n *\r\n * Faithful port of tokscale sessions/cursor.rs. Cursor's per-event token usage\r\n * lives behind the Cursor dashboard export API; tokscale fetches it and caches\r\n * the result as CSV under ~/.config/tokscale/cursor-cache/. THIS reader never\r\n * authenticates and never calls that API — it only parses the cached CSV when a\r\n * tokscale run has already produced it. Hence kind:\"synced\":\r\n * - cache present → parse the CSV(s) and emit one record per data row;\r\n * - cache absent → return [] (the scan layer notes \"requires sync, skipped\").\r\n *\r\n * Cache layout (per docs/research/usage-readers.json + design §3d):\r\n * ~/.config/tokscale/cursor-cache/usage.csv (active account)\r\n * ~/.config/tokscale/cursor-cache/usage.<account>.csv (secondary accounts)\r\n * ~/.config/tokscale/cursor-cache/archive/*.csv (rotated exports)\r\n * paths.ts resolves the cache dir (env override AGENT_CONNECTOR_TOKSCALE_DIR\r\n * first, the tokscale ~/.config/tokscale default second).\r\n *\r\n * CSV formats (column maps mirror cursor.rs exactly):\r\n * v1 (no \"Kind\"): Date,Model,Input(w/CacheWrite),Input(w/oCacheWrite),CacheRead,Output,Total,Cost,CostToYou\r\n * → model 1, in+cw 2, in 3, cacheRead 4, output 5, cost 7\r\n * v2 (has \"Kind\", <11 cols): Date,Kind,Model,MaxMode,Input(w/CW),Input(w/oCW),CacheRead,Output,Total,Cost\r\n * → model 2, in+cw 4, in 5, cacheRead 6, output 7, cost 9\r\n * v3 (has \"Kind\", ≥11 cols): Date,CloudAgentID,AutomationID,Kind,Model,MaxMode,Input(w/CW),Input(w/oCW),CacheRead,Output,Total,Cost\r\n * → model 4, in+cw 6, in 7, cacheRead 8, output 9, cost 11\r\n *\r\n * Per-row token math (port of parse_cursor_file):\r\n * input = Input(w/o CacheWrite) (clamped ≥ 0)\r\n * cacheWrite = Input(w/ CacheWrite) − Input(w/o CacheWrite) (clamped ≥ 0)\r\n * cacheRead = Cache Read (clamped ≥ 0)\r\n * output = Output Tokens (clamped ≥ 0)\r\n * reasoning = 0 (Cursor does not report it)\r\n * Cost is parsed from the Cost column (\"$1,234.56\" → 1234.56; \"Included\"/\"-\"/\r\n * \"NaN\"/\"\" → 0). Timestamp parses the Date column (ISO 8601 with/without ms/Z,\r\n * or date-only → noon UTC so local-day filtering never shifts the row).\r\n *\r\n * Attribution: provider inferred from the model (defaults to \"cursor\" for\r\n * Cursor-only ids like \"auto\"/\"composer-2\"); sessionId is the composite\r\n * `cursor-<account>-<date>` where account derives from the filename\r\n * (usage.csv → \"active\", usage.<x>.csv → sanitized \"<x>\"). No project scope\r\n * (account-level usage). Cursor ships no dedup key; the composite session id is\r\n * the implicit dedup, so dedupKey mirrors it. Confidence is \"host-reported\"\r\n * (the cached data carries real host token counts).\r\n *\r\n * Fail-open: no cache dir → []; unreadable/malformed file → skipped; a row\r\n * missing columns, with an empty model, or an unparseable date → skipped.\r\n */\r\n\r\nimport { basename } from \"node:path\";\r\n\r\nimport type { TokenBreakdown, UsageReader, UsageRecord } from \"../types.js\";\r\nimport { emptyTokens } from \"../aggregate.js\";\r\nimport { inferProvider } from \"../normalize.js\";\r\nimport { firstExistingRoot, walkFiles } from \"../paths.js\";\r\n\r\nimport { readFileSync } from \"node:fs\";\r\n\r\nconst PLATFORM_ID = \"cursor\" as const;\r\nconst DEFAULT_PROVIDER = \"cursor\";\r\n\r\n/** Column-index map for one CSV format (mirrors cursor.rs tuple order). */\r\ninterface ColumnMap {\r\n model: number;\r\n inputCacheWrite: number;\r\n inputNoCache: number;\r\n cacheRead: number;\r\n output: number;\r\n cost: number;\r\n}\r\n\r\nconst V1_COLUMNS: ColumnMap = {\r\n model: 1,\r\n inputCacheWrite: 2,\r\n inputNoCache: 3,\r\n cacheRead: 4,\r\n output: 5,\r\n cost: 7,\r\n};\r\nconst V2_COLUMNS: ColumnMap = {\r\n model: 2,\r\n inputCacheWrite: 4,\r\n inputNoCache: 5,\r\n cacheRead: 6,\r\n output: 7,\r\n cost: 9,\r\n};\r\nconst V3_COLUMNS: ColumnMap = {\r\n model: 4,\r\n inputCacheWrite: 6,\r\n inputNoCache: 7,\r\n cacheRead: 8,\r\n output: 9,\r\n cost: 11,\r\n};\r\n\r\n/**\r\n * Derive the account id from a cache filename (port of\r\n * account_id_from_cursor_cache_path):\r\n * - \"usage.csv\" → \"active\";\r\n * - \"usage.<x>.csv\" → \"<x>\" with non-[A-Za-z0-9-_.] chars replaced by '-'\r\n * (empty after cleaning → \"unknown\");\r\n * - anything else → \"unknown\".\r\n */\r\nfunction accountIdFromPath(path: string): string {\r\n const fileName = basename(path) || \"usage.csv\";\r\n if (fileName === \"usage.csv\") return \"active\";\r\n\r\n if (fileName.startsWith(\"usage.\") && fileName.endsWith(\".csv\")) {\r\n const stem = fileName.slice(\"usage.\".length, fileName.length - \".csv\".length);\r\n let cleaned = \"\";\r\n for (const ch of stem) {\r\n cleaned += /[A-Za-z0-9._-]/.test(ch) ? ch : \"-\";\r\n }\r\n return cleaned === \"\" ? \"unknown\" : cleaned;\r\n }\r\n\r\n return \"unknown\";\r\n}\r\n\r\n/** Coerce a CSV field to a non-negative integer (0 on absence/garbage). */\r\nfunction toNonNegInt(field: string): number {\r\n const trimmed = field.trim().replace(/^\"|\"$/g, \"\").trim();\r\n const n = Number(trimmed);\r\n if (!Number.isFinite(n)) return 0;\r\n return Math.max(0, Math.trunc(n));\r\n}\r\n\r\n/**\r\n * Parse a cost string like \"$0.50\" / \"1,234.56\" to a number. Port of parse_cost:\r\n * strip '$' and ',', then 0 for empty / \"NaN\" / values with no ASCII digit\r\n * (e.g. \"Included\", \"-\"). Negative results are clamped to 0 by the caller.\r\n */\r\nfunction parseCost(costStr: string): number {\r\n const cleaned = costStr.replace(/[$,]/g, \"\");\r\n const trimmed = cleaned.trim();\r\n if (trimmed === \"\" || trimmed.toLowerCase() === \"nan\" || !/[0-9]/.test(trimmed)) {\r\n return 0;\r\n }\r\n const n = Number(trimmed);\r\n return Number.isFinite(n) ? n : 0;\r\n}\r\n\r\n/**\r\n * Simple CSV line splitter that respects quoted fields (port of parse_csv_line):\r\n * a '\"' toggles quote state, a ',' outside quotes ends a field. Quote characters\r\n * are kept in the slice (callers strip surrounding quotes per field), matching\r\n * the Rust byte-index behavior.\r\n */\r\nfunction parseCsvLine(line: string): string[] {\r\n const fields: string[] = [];\r\n let start = 0;\r\n let inQuotes = false;\r\n for (let i = 0; i < line.length; i++) {\r\n const ch = line[i];\r\n if (ch === '\"') {\r\n inQuotes = !inQuotes;\r\n } else if (ch === \",\" && !inQuotes) {\r\n fields.push(line.slice(start, i));\r\n start = i + 1;\r\n }\r\n }\r\n fields.push(line.slice(start));\r\n return fields;\r\n}\r\n\r\n/** Trim a field and strip a single layer of surrounding double quotes. */\r\nfunction unquote(field: string): string {\r\n return field.trim().replace(/^\"|\"$/g, \"\");\r\n}\r\n\r\n/**\r\n * Parse a Cursor Date string to epoch ms (port of parse_date_to_timestamp).\r\n * Tries ISO 8601 with/without ms and trailing Z, then date-only (→ noon UTC so\r\n * the local date is stable for any tz from UTC-12..UTC+14). Returns null when\r\n * unparseable (the row is then skipped, matching the Rust ts==0 skip).\r\n */\r\nfunction parseDateToMs(dateStr: string): number | null {\r\n // ISO forms with explicit time: Date.parse handles\r\n // \"YYYY-MM-DDTHH:MM:SS\", \"...SS.mmm\", and either with a trailing \"Z\".\r\n if (dateStr.includes(\"T\")) {\r\n const ms = Date.parse(dateStr);\r\n return Number.isNaN(ms) ? null : ms;\r\n }\r\n\r\n // Date-only \"YYYY-MM-DD\" → noon UTC (12:00:00Z).\r\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(dateStr);\r\n if (m !== null) {\r\n const year = Number(m[1]);\r\n const month = Number(m[2]);\r\n const day = Number(m[3]);\r\n const ms = Date.UTC(year, month - 1, day, 12, 0, 0, 0);\r\n return Number.isNaN(ms) ? null : ms;\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Pick the column map for a header line (port of the cursor.rs format detect):\r\n * a \"Kind\" column with ≥11 total columns is v3; \"Kind\" with fewer is v2; no\r\n * \"Kind\" is v1. Returns null when the header is not a recognizable Cursor CSV\r\n * (must contain both \"Date\" and \"Model\").\r\n */\r\nfunction detectColumns(header: string): ColumnMap | null {\r\n if (!header.includes(\"Date\") || !header.includes(\"Model\")) return null;\r\n\r\n const fields = parseCsvLine(header);\r\n const hasKind = fields.some((f) => f.trim() === \"Kind\");\r\n const columnCount = fields.length;\r\n\r\n if (hasKind && columnCount >= 11) return V3_COLUMNS;\r\n if (hasKind) return V2_COLUMNS;\r\n return V1_COLUMNS;\r\n}\r\n\r\n/** Parse one cached Cursor CSV file into usage records (port of parse_cursor_file). */\r\nfunction parseCursorFile(path: string): UsageRecord[] {\r\n let content: string;\r\n try {\r\n content = readFileSync(path, \"utf8\");\r\n } catch {\r\n return []; // unreadable → fail-open\r\n }\r\n\r\n const lines = content.split(\"\\n\");\r\n if (lines.length === 0) return [];\r\n\r\n const header = lines[0];\r\n if (header === undefined) return [];\r\n\r\n const columns = detectColumns(header);\r\n if (columns === null) return []; // not a Cursor CSV\r\n\r\n const minFields = columns.cost + 1;\r\n const accountId = accountIdFromPath(path);\r\n const out: UsageRecord[] = [];\r\n\r\n for (let i = 1; i < lines.length; i++) {\r\n const rawLine = lines[i];\r\n if (rawLine === undefined) continue;\r\n // Strip a trailing CR (CRLF files) before the empty-line check.\r\n const line = rawLine.replace(/\\r$/, \"\");\r\n if (line.trim() === \"\") continue;\r\n\r\n const fields = parseCsvLine(line);\r\n if (fields.length < minFields) continue; // not enough columns for this format\r\n\r\n const dateStr = unquote(fields[0] ?? \"\");\r\n const model = unquote(fields[columns.model] ?? \"\");\r\n if (model === \"\") continue; // skip empty/errored entries\r\n\r\n const ts = parseDateToMs(dateStr);\r\n if (ts === null) continue; // unparseable date → skip (Rust ts==0)\r\n\r\n const inputWithCacheWrite = toNonNegInt(fields[columns.inputCacheWrite] ?? \"\");\r\n const inputWithoutCacheWrite = toNonNegInt(fields[columns.inputNoCache] ?? \"\");\r\n const cacheReadTokens = toNonNegInt(fields[columns.cacheRead] ?? \"\");\r\n const outputTokens = toNonNegInt(fields[columns.output] ?? \"\");\r\n const cost = Math.max(0, parseCost(unquote(fields[columns.cost] ?? \"\")));\r\n\r\n // cacheWrite = (w/ cache write) − (w/o cache write), clamped ≥ 0.\r\n const cacheWrite = Math.max(0, inputWithCacheWrite - inputWithoutCacheWrite);\r\n const input = Math.max(0, inputWithoutCacheWrite);\r\n\r\n const providerId = inferProvider(model) ?? DEFAULT_PROVIDER;\r\n // Composite session id is also the implicit dedup key (Cursor has none).\r\n const sessionId = `cursor-${accountId}-${dateStr}`;\r\n\r\n const tokens: TokenBreakdown = emptyTokens();\r\n tokens.input = input;\r\n tokens.output = outputTokens;\r\n tokens.cacheRead = cacheReadTokens;\r\n tokens.cacheWrite = cacheWrite;\r\n // reasoning stays 0 (Cursor does not report it).\r\n\r\n const record: UsageRecord = {\r\n platformId: PLATFORM_ID,\r\n modelId: model,\r\n providerId,\r\n sessionId,\r\n tokens,\r\n cost,\r\n ts,\r\n messageCount: 1,\r\n dedupKey: sessionId,\r\n confidence: \"host-reported\",\r\n };\r\n out.push(record);\r\n }\r\n\r\n return out;\r\n}\r\n\r\n/** The Cursor synced usage reader singleton. */\r\nconst cursorReader: UsageReader = {\r\n platformId: PLATFORM_ID,\r\n kind: \"synced\",\r\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\r\n // The cache dir is ~/.config/tokscale/cursor-cache (env override first).\r\n // Absent → [] so the scan layer reports \"requires sync, skipped\".\r\n const root = firstExistingRoot(PLATFORM_ID);\r\n if (root === undefined) return [];\r\n\r\n // Enumerate cached exports: usage*.csv at the cache root AND any archive/\r\n // subdir (walkFiles recurses), covering the active + secondary accounts.\r\n const files = walkFiles(root, (name) => {\r\n const lower = name.toLowerCase();\r\n return lower.startsWith(\"usage\") && lower.endsWith(\".csv\");\r\n });\r\n\r\n const records: UsageRecord[] = [];\r\n for (const file of files) {\r\n const rows = parseCursorFile(file);\r\n for (const row of rows) {\r\n if (sinceMs !== undefined && row.ts < sinceMs) continue;\r\n records.push(row);\r\n }\r\n }\r\n return records;\r\n },\r\n};\r\n\r\nexport default cursorReader;\r\n"],"mappings":";;;;;;;;;;;AAgDA,SAAS,gBAAgB;AAOzB,SAAS,oBAAoB;AAE7B,IAAM,cAAc;AACpB,IAAM,mBAAmB;AAYzB,IAAM,aAAwB;AAAA,EAC5B,OAAO;AAAA,EACP,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,MAAM;AACR;AACA,IAAM,aAAwB;AAAA,EAC5B,OAAO;AAAA,EACP,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,MAAM;AACR;AACA,IAAM,aAAwB;AAAA,EAC5B,OAAO;AAAA,EACP,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,MAAM;AACR;AAUA,SAAS,kBAAkB,MAAsB;AAC/C,QAAM,WAAW,SAAS,IAAI,KAAK;AACnC,MAAI,aAAa,YAAa,QAAO;AAErC,MAAI,SAAS,WAAW,QAAQ,KAAK,SAAS,SAAS,MAAM,GAAG;AAC9D,UAAM,OAAO,SAAS,MAAM,SAAS,QAAQ,SAAS,SAAS,OAAO,MAAM;AAC5E,QAAI,UAAU;AACd,eAAW,MAAM,MAAM;AACrB,iBAAW,iBAAiB,KAAK,EAAE,IAAI,KAAK;AAAA,IAC9C;AACA,WAAO,YAAY,KAAK,YAAY;AAAA,EACtC;AAEA,SAAO;AACT;AAGA,SAAS,YAAY,OAAuB;AAC1C,QAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,UAAU,EAAE,EAAE,KAAK;AACxD,QAAM,IAAI,OAAO,OAAO;AACxB,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC;AAClC;AAOA,SAAS,UAAU,SAAyB;AAC1C,QAAM,UAAU,QAAQ,QAAQ,SAAS,EAAE;AAC3C,QAAM,UAAU,QAAQ,KAAK;AAC7B,MAAI,YAAY,MAAM,QAAQ,YAAY,MAAM,SAAS,CAAC,QAAQ,KAAK,OAAO,GAAG;AAC/E,WAAO;AAAA,EACT;AACA,QAAM,IAAI,OAAO,OAAO;AACxB,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAQA,SAAS,aAAa,MAAwB;AAC5C,QAAM,SAAmB,CAAC;AAC1B,MAAI,QAAQ;AACZ,MAAI,WAAW;AACf,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,KAAK,KAAK,CAAC;AACjB,QAAI,OAAO,KAAK;AACd,iBAAW,CAAC;AAAA,IACd,WAAW,OAAO,OAAO,CAAC,UAAU;AAClC,aAAO,KAAK,KAAK,MAAM,OAAO,CAAC,CAAC;AAChC,cAAQ,IAAI;AAAA,IACd;AAAA,EACF;AACA,SAAO,KAAK,KAAK,MAAM,KAAK,CAAC;AAC7B,SAAO;AACT;AAGA,SAAS,QAAQ,OAAuB;AACtC,SAAO,MAAM,KAAK,EAAE,QAAQ,UAAU,EAAE;AAC1C;AAQA,SAAS,cAAc,SAAgC;AAGrD,MAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,UAAM,KAAK,KAAK,MAAM,OAAO;AAC7B,WAAO,OAAO,MAAM,EAAE,IAAI,OAAO;AAAA,EACnC;AAGA,QAAM,IAAI,4BAA4B,KAAK,OAAO;AAClD,MAAI,MAAM,MAAM;AACd,UAAM,OAAO,OAAO,EAAE,CAAC,CAAC;AACxB,UAAM,QAAQ,OAAO,EAAE,CAAC,CAAC;AACzB,UAAM,MAAM,OAAO,EAAE,CAAC,CAAC;AACvB,UAAM,KAAK,KAAK,IAAI,MAAM,QAAQ,GAAG,KAAK,IAAI,GAAG,GAAG,CAAC;AACrD,WAAO,OAAO,MAAM,EAAE,IAAI,OAAO;AAAA,EACnC;AAEA,SAAO;AACT;AAQA,SAAS,cAAc,QAAkC;AACvD,MAAI,CAAC,OAAO,SAAS,MAAM,KAAK,CAAC,OAAO,SAAS,OAAO,EAAG,QAAO;AAElE,QAAM,SAAS,aAAa,MAAM;AAClC,QAAM,UAAU,OAAO,KAAK,CAAC,MAAM,EAAE,KAAK,MAAM,MAAM;AACtD,QAAM,cAAc,OAAO;AAE3B,MAAI,WAAW,eAAe,GAAI,QAAO;AACzC,MAAI,QAAS,QAAO;AACpB,SAAO;AACT;AAGA,SAAS,gBAAgB,MAA6B;AACpD,MAAI;AACJ,MAAI;AACF,cAAU,aAAa,MAAM,MAAM;AAAA,EACrC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,MAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAEhC,QAAM,SAAS,MAAM,CAAC;AACtB,MAAI,WAAW,OAAW,QAAO,CAAC;AAElC,QAAM,UAAU,cAAc,MAAM;AACpC,MAAI,YAAY,KAAM,QAAO,CAAC;AAE9B,QAAM,YAAY,QAAQ,OAAO;AACjC,QAAM,YAAY,kBAAkB,IAAI;AACxC,QAAM,MAAqB,CAAC;AAE5B,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,UAAU,MAAM,CAAC;AACvB,QAAI,YAAY,OAAW;AAE3B,UAAM,OAAO,QAAQ,QAAQ,OAAO,EAAE;AACtC,QAAI,KAAK,KAAK,MAAM,GAAI;AAExB,UAAM,SAAS,aAAa,IAAI;AAChC,QAAI,OAAO,SAAS,UAAW;AAE/B,UAAM,UAAU,QAAQ,OAAO,CAAC,KAAK,EAAE;AACvC,UAAM,QAAQ,QAAQ,OAAO,QAAQ,KAAK,KAAK,EAAE;AACjD,QAAI,UAAU,GAAI;AAElB,UAAM,KAAK,cAAc,OAAO;AAChC,QAAI,OAAO,KAAM;AAEjB,UAAM,sBAAsB,YAAY,OAAO,QAAQ,eAAe,KAAK,EAAE;AAC7E,UAAM,yBAAyB,YAAY,OAAO,QAAQ,YAAY,KAAK,EAAE;AAC7E,UAAM,kBAAkB,YAAY,OAAO,QAAQ,SAAS,KAAK,EAAE;AACnE,UAAM,eAAe,YAAY,OAAO,QAAQ,MAAM,KAAK,EAAE;AAC7D,UAAM,OAAO,KAAK,IAAI,GAAG,UAAU,QAAQ,OAAO,QAAQ,IAAI,KAAK,EAAE,CAAC,CAAC;AAGvE,UAAM,aAAa,KAAK,IAAI,GAAG,sBAAsB,sBAAsB;AAC3E,UAAM,QAAQ,KAAK,IAAI,GAAG,sBAAsB;AAEhD,UAAM,aAAa,cAAc,KAAK,KAAK;AAE3C,UAAM,YAAY,UAAU,SAAS,IAAI,OAAO;AAEhD,UAAM,SAAyB,YAAY;AAC3C,WAAO,QAAQ;AACf,WAAO,SAAS;AAChB,WAAO,YAAY;AACnB,WAAO,aAAa;AAGpB,UAAM,SAAsB;AAAA,MAC1B,YAAY;AAAA,MACZ,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd,UAAU;AAAA,MACV,YAAY;AAAA,IACd;AACA,QAAI,KAAK,MAAM;AAAA,EACjB;AAEA,SAAO;AACT;AAGA,IAAM,eAA4B;AAAA,EAChC,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM,KAAK,EAAE,QAAQ,GAAiD;AAGpE,UAAM,OAAO,kBAAkB,WAAW;AAC1C,QAAI,SAAS,OAAW,QAAO,CAAC;AAIhC,UAAM,QAAQ,UAAU,MAAM,CAAC,SAAS;AACtC,YAAM,QAAQ,KAAK,YAAY;AAC/B,aAAO,MAAM,WAAW,OAAO,KAAK,MAAM,SAAS,MAAM;AAAA,IAC3D,CAAC;AAED,UAAM,UAAyB,CAAC;AAChC,eAAW,QAAQ,OAAO;AACxB,YAAM,OAAO,gBAAgB,IAAI;AACjC,iBAAW,OAAO,MAAM;AACtB,YAAI,YAAY,UAAa,IAAI,KAAK,QAAS;AAC/C,gBAAQ,KAAK,GAAG;AAAA,MAClB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;AAEA,IAAO,iBAAQ;","names":[]}
|
package/dist/cursor-W7I4QUFO.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
CURSOR_EVENT_MAP,
|
|
4
|
-
CURSOR_HOOKS_VERSION,
|
|
5
|
-
CursorAdapter,
|
|
6
|
-
adapter,
|
|
7
|
-
cursor_default
|
|
8
|
-
} from "./chunk-XVEJAFKW.js";
|
|
9
|
-
import "./chunk-PSGTHJQU.js";
|
|
10
|
-
import "./chunk-XXLCMSD3.js";
|
|
11
|
-
import "./chunk-3FINQ2ZF.js";
|
|
12
|
-
import "./chunk-Y3K6KJMS.js";
|
|
13
|
-
import "./chunk-ILWHLGIC.js";
|
|
14
|
-
import "./chunk-QYOUQCC6.js";
|
|
15
|
-
export {
|
|
16
|
-
CURSOR_EVENT_MAP,
|
|
17
|
-
CURSOR_HOOKS_VERSION,
|
|
18
|
-
CursorAdapter,
|
|
19
|
-
adapter,
|
|
20
|
-
cursor_default as default
|
|
21
|
-
};
|
|
22
|
-
//# sourceMappingURL=cursor-W7I4QUFO.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/detect.ts"],"sourcesContent":["/**\r\n * cli/commands/detect — list the AI-agent platforms installed on this machine.\r\n *\r\n * `detect` probes every registered adapter (via detectInstalledPlatforms) and\r\n * prints, for each installed host: id, name, hook paradigm, install scope, the\r\n * native config path that would be written, and a one-line capabilities summary.\r\n * `--json` emits the raw DetectedPlatform[] for scripting.\r\n */\r\n\r\nimport { parseArgs } from \"node:util\";\r\n\r\nimport type { DetectedPlatform, PlatformCapabilities } from \"../../core/types.js\";\r\nimport { detectInstalledPlatforms } from \"../../adapters/detect.js\";\r\nimport { print } from \"../app.js\";\r\n\r\n/** Compact, human one-line summary of what a host can honor. */\r\nfunction capabilitySummary(caps: PlatformCapabilities): string {\r\n const events: string[] = [];\r\n if (caps.preToolUse) events.push(\"PreToolUse\");\r\n if (caps.postToolUse) events.push(\"PostToolUse\");\r\n if (caps.sessionStart) events.push(\"SessionStart\");\r\n if (caps.sessionEnd) events.push(\"SessionEnd\");\r\n if (caps.userPromptSubmit) events.push(\"UserPromptSubmit\");\r\n if (caps.preCompact) events.push(\"PreCompact\");\r\n if (caps.stop) events.push(\"Stop\");\r\n if (caps.notification) events.push(\"Notification\");\r\n // Newer optional per-event flags (absent ⇒ unsupported, read as `?? false`).\r\n if (caps.permissionRequest ?? false) events.push(\"PermissionRequest\");\r\n if (caps.postToolUseFailure ?? false) events.push(\"PostToolUseFailure\");\r\n if (caps.subagentStart ?? false) events.push(\"SubagentStart\");\r\n if (caps.subagentStop ?? false) events.push(\"SubagentStop\");\r\n if (caps.postCompact ?? false) events.push(\"PostCompact\");\r\n\r\n const extras: string[] = [];\r\n if (caps.canModifyArgs) extras.push(\"modifyArgs\");\r\n if (caps.canModifyOutput) extras.push(\"modifyOutput\");\r\n if (caps.canInjectSessionContext) extras.push(\"injectContext\");\r\n\r\n const transports = caps.transports.length > 0 ? caps.transports.join(\"/\") : \"none\";\r\n const parts = [\r\n `events: ${events.length > 0 ? events.join(\",\") : \"none\"}`,\r\n `transports: ${transports}`,\r\n ];\r\n if (extras.length > 0) parts.push(extras.join(\",\"));\r\n return parts.join(\" | \");\r\n}\r\n\r\nexport async function run(argv: string[]): Promise<number> {\r\n const { values } = parseArgs({\r\n args: argv,\r\n options: {\r\n json: { type: \"boolean\", default: false },\r\n project: { type: \"string\" },\r\n },\r\n allowPositionals: false,\r\n });\r\n\r\n const projectDir = values.project ?? process.cwd();\r\n const detected: DetectedPlatform[] = await detectInstalledPlatforms(projectDir);\r\n\r\n if (values.json) {\r\n print(JSON.stringify(detected, null, 2));\r\n return 0;\r\n }\r\n\r\n if (detected.length === 0) {\r\n print(\"No supported AI-agent platforms detected on this machine.\");\r\n return 0;\r\n }\r\n\r\n print(`Detected ${detected.length} platform(s):\\n`);\r\n for (const p of detected) {\r\n print(`${p.name} (${p.id})`);\r\n print(` paradigm: ${p.paradigm}`);\r\n print(` scope: ${p.scope}`);\r\n print(` configPath: ${p.configPath}`);\r\n print(` confidence: ${p.confidence} (${p.reason})`);\r\n print(` capabilities: ${capabilitySummary(p.capabilities)}`);\r\n print(\"\");\r\n }\r\n return 0;\r\n}\r\n"],"mappings":";;;;;;;;;;AASA,SAAS,iBAAiB;AAO1B,SAAS,kBAAkB,MAAoC;AAC7D,QAAM,SAAmB,CAAC;AAC1B,MAAI,KAAK,WAAY,QAAO,KAAK,YAAY;AAC7C,MAAI,KAAK,YAAa,QAAO,KAAK,aAAa;AAC/C,MAAI,KAAK,aAAc,QAAO,KAAK,cAAc;AACjD,MAAI,KAAK,WAAY,QAAO,KAAK,YAAY;AAC7C,MAAI,KAAK,iBAAkB,QAAO,KAAK,kBAAkB;AACzD,MAAI,KAAK,WAAY,QAAO,KAAK,YAAY;AAC7C,MAAI,KAAK,KAAM,QAAO,KAAK,MAAM;AACjC,MAAI,KAAK,aAAc,QAAO,KAAK,cAAc;AAEjD,MAAI,KAAK,qBAAqB,MAAO,QAAO,KAAK,mBAAmB;AACpE,MAAI,KAAK,sBAAsB,MAAO,QAAO,KAAK,oBAAoB;AACtE,MAAI,KAAK,iBAAiB,MAAO,QAAO,KAAK,eAAe;AAC5D,MAAI,KAAK,gBAAgB,MAAO,QAAO,KAAK,cAAc;AAC1D,MAAI,KAAK,eAAe,MAAO,QAAO,KAAK,aAAa;AAExD,QAAM,SAAmB,CAAC;AAC1B,MAAI,KAAK,cAAe,QAAO,KAAK,YAAY;AAChD,MAAI,KAAK,gBAAiB,QAAO,KAAK,cAAc;AACpD,MAAI,KAAK,wBAAyB,QAAO,KAAK,eAAe;AAE7D,QAAM,aAAa,KAAK,WAAW,SAAS,IAAI,KAAK,WAAW,KAAK,GAAG,IAAI;AAC5E,QAAM,QAAQ;AAAA,IACZ,WAAW,OAAO,SAAS,IAAI,OAAO,KAAK,GAAG,IAAI,MAAM;AAAA,IACxD,eAAe,UAAU;AAAA,EAC3B;AACA,MAAI,OAAO,SAAS,EAAG,OAAM,KAAK,OAAO,KAAK,GAAG,CAAC;AAClD,SAAO,MAAM,KAAK,KAAK;AACzB;AAEA,eAAsB,IAAI,MAAiC;AACzD,QAAM,EAAE,OAAO,IAAI,UAAU;AAAA,IAC3B,MAAM;AAAA,IACN,SAAS;AAAA,MACP,MAAM,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACxC,SAAS,EAAE,MAAM,SAAS;AAAA,IAC5B;AAAA,IACA,kBAAkB;AAAA,EACpB,CAAC;AAED,QAAM,aAAa,OAAO,WAAW,QAAQ,IAAI;AACjD,QAAM,WAA+B,MAAM,yBAAyB,UAAU;AAE9E,MAAI,OAAO,MAAM;AACf,UAAM,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AACvC,WAAO;AAAA,EACT;AAEA,MAAI,SAAS,WAAW,GAAG;AACzB,UAAM,2DAA2D;AACjE,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,SAAS,MAAM;AAAA,CAAiB;AAClD,aAAW,KAAK,UAAU;AACxB,UAAM,GAAG,EAAE,IAAI,KAAK,EAAE,EAAE,GAAG;AAC3B,UAAM,kBAAkB,EAAE,QAAQ,EAAE;AACpC,UAAM,kBAAkB,EAAE,KAAK,EAAE;AACjC,UAAM,kBAAkB,EAAE,UAAU,EAAE;AACtC,UAAM,kBAAkB,EAAE,UAAU,MAAM,EAAE,MAAM,GAAG;AACrD,UAAM,mBAAmB,kBAAkB,EAAE,YAAY,CAAC,EAAE;AAC5D,UAAM,EAAE;AAAA,EACV;AACA,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/devin/index.ts"],"sourcesContent":["/**\r\n * adapters/devin — Devin CLI (Cognition) platform adapter for agent-connector.\r\n *\r\n * Devin CLI is a Rust binary (`devin`, installed via `curl cli.devin.ai/install.sh`\r\n * → symlinked at `~/.local/bin/devin`; XDG namespace `devin`). It is a\r\n * **json-stdio** host: it ships a FULL Claude-Code-compatible lifecycle hook\r\n * system alongside MCP server registration. Both surfaces live in ONE JSON\r\n * config file per scope:\r\n *\r\n * - user scope → ~/.config/devin/config.json\r\n * (%APPDATA%\\devin\\config.json on Windows)\r\n * - project scope → <projectDir>/.devin/config.json\r\n *\r\n * MCP servers — root key `mcpServers` (object map keyed by server name; the\r\n * Claude-Desktop dialect). Native stdio entry { command, args?, env? } (NO\r\n * `type`/`disabled` discriminator); remote entry { url, transport?, headers?,\r\n * oauthClientId?, oauthClientSecret? } where transport is \"http\" (Streamable\r\n * HTTP, the URL default) or \"sse\" (legacy). Devin natively interpolates\r\n * `${env:VAR}` and `${file:/path}` in secret-bearing fields, so we PASS the\r\n * ${env:VAR} token THROUGH (nativeServerEnvInterpolation) rather than baking a\r\n * literal at install time. Primary source:\r\n * docs.devin.ai/cli/extensibility/mcp/configuration.\r\n *\r\n * Hooks — the `\"hooks\"` key IN the same config.json (root key `hooks`,\r\n * Claude NESTED-rule shape). Devin's hook format is documented as\r\n * \"compatible with Claude Code hooks\" — PascalCase event names, Claude\r\n * snake_case stdin wire fields. We deliberately write the `\"hooks\"` key in\r\n * config.json (a first-party-documented hook location whose object-map shape\r\n * fits the shared hook-merge engine exactly) rather than the alternative\r\n * standalone `.devin/hooks.v1.json` (whose event map is the WHOLE file with NO\r\n * `hooks` wrapper — incompatible with the engine's `file.hooks` contract). Both\r\n * are documented; config.json keeps us on the shared, Windows-safe engine with\r\n * zero hand-rolled file handling. Primary source:\r\n * docs.devin.ai/cli/extensibility/hooks/overview (\"Where Hooks Live\").\r\n *\r\n * Supported events (docs.devin.ai/cli/extensibility/hooks/lifecycle-hooks):\r\n * PreToolUse, PostToolUse, PermissionRequest, UserPromptSubmit, Stop,\r\n * PostCompaction (canonical PostCompact), SessionStart, SessionEnd. Devin\r\n * exposes no Notification / PreCompact / SubagentStart / SubagentStop /\r\n * PostToolUseFailure, so those degrade to a warn/skip at install time.\r\n *\r\n * Reply protocol — Devin's stdout reply is the SIMPLE top-level\r\n * { \"decision\": \"approve\"|\"block\"|\"deny\", \"reason\" } form (NOT Claude's\r\n * `hookSpecificOutput.permissionDecision` envelope). exit 0 = success,\r\n * exit 2 = block. \"block\" denies a tool/stop with a reason; on PermissionRequest\r\n * \"approve\"/\"deny\" are the grant/refuse verbs. Devin documents no\r\n * output-rewrite or context-injection reply channel, so canModifyOutput /\r\n * canInjectSessionContext stay false. Primary source:\r\n * docs.devin.ai/cli/extensibility/hooks/overview (\"Command Hooks\" output +\r\n * \"Exit Codes\") + lifecycle-hooks (PermissionRequest / Stop examples).\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { homedir, platform as osPlatform } 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, MemoryTarget, NormalizedEvent } from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookEventName,\r\n HookParadigm,\r\n HookResponse,\r\n PermissionRequestEvent,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PostCompactEvent,\r\n PostToolUseEvent,\r\n PreToolUseEvent,\r\n ServerDef,\r\n SessionEndEvent,\r\n SessionStartEvent,\r\n SkillDef,\r\n StopEvent,\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 { normalizeSessionSource } from \"../claude-code/wire.js\";\r\n\r\nconst HOST: PlatformId = \"devin\";\r\nconst MCP_ROOT_KEY = \"mcpServers\";\r\n\r\n/**\r\n * Resolve the Devin user config directory: ~/.config/devin (POSIX) or\r\n * %APPDATA%\\devin (Windows). Mirrors the documented user-scope path in\r\n * docs.devin.ai/cli/extensibility/mcp/configuration.\r\n */\r\nfunction devinUserConfigDir(): string {\r\n if (osPlatform() === \"win32\") {\r\n const appData = process.env.APPDATA ?? join(homedir(), \"AppData\", \"Roaming\");\r\n return join(appData, \"devin\");\r\n }\r\n return join(homedir(), \".config\", \"devin\");\r\n}\r\n\r\n/**\r\n * Canonical events Devin actually fires. Devin's hook event names are\r\n * Claude-identical PascalCase, so each canonical name is registered directly\r\n * EXCEPT PostCompaction, which Devin spells \"PostCompaction\" on the wire while\r\n * agent-connector's canonical name is \"PostCompact\" — handled by the descriptor\r\n * mapEvent / parseEvent so the home binary still dispatches on the canonical\r\n * \"PostCompact\" command.\r\n */\r\nconst SUPPORTED_EVENTS: ReadonlySet<HookEventName> = new Set<HookEventName>([\r\n \"PreToolUse\",\r\n \"PostToolUse\",\r\n \"PermissionRequest\",\r\n \"UserPromptSubmit\",\r\n \"Stop\",\r\n \"PostCompact\",\r\n \"SessionStart\",\r\n \"SessionEnd\",\r\n]);\r\n\r\n/** Devin's on-the-wire event name for the canonical PostCompact event. */\r\nconst DEVIN_POSTCOMPACT = \"PostCompaction\";\r\n\r\n/**\r\n * Native MCP server entry shapes Devin accepts under `mcpServers`.\r\n * A stdio entry is { command, args?, env? } — no type/disabled discriminator;\r\n * remote transports register a URL with an optional `transport` (\"http\"|\"sse\").\r\n */\r\ninterface DevinStdioServer {\r\n command: string;\r\n args?: string[];\r\n env?: Record<string, string>;\r\n}\r\ninterface DevinHttpServer {\r\n url: string;\r\n transport?: \"http\" | \"sse\";\r\n headers?: Record<string, string>;\r\n}\r\n\r\n/** A single Devin native hook registration entry (Claude-shaped, nested). */\r\ninterface DevinHookEntry {\r\n matcher: string;\r\n hooks: Array<{ type: \"command\"; command: string }>;\r\n}\r\n\r\n/** The shape of Devin's config.json hooks section (only the parts we touch). */\r\ninterface DevinHooksFile {\r\n hooks?: Record<string, DevinHookEntry[]>;\r\n [key: string]: unknown;\r\n}\r\n\r\n/** Raw Devin CLI hook stdin payload (Claude-compatible snake_case wire fields). */\r\ninterface DevinWireInput {\r\n hook_event_name?: string;\r\n tool_name?: string;\r\n tool_input?: Record<string, unknown>;\r\n /** PostToolUse — object { success, output, error }. */\r\n tool_response?: unknown;\r\n /** UserPromptSubmit — the user's message text. */\r\n prompt?: string;\r\n /** SessionStart — how the session was started. */\r\n source?: string;\r\n /** SessionEnd — why the session ended. */\r\n reason?: string;\r\n /** Stop loop guard. */\r\n stop_hook_active?: boolean;\r\n /** PostCompaction — compactor summary (may be null). Rides on raw; no normalized field. */\r\n summary?: unknown;\r\n /** Injected by the entrypoint so the runtime knows which connector to dispatch. */\r\n connector?: unknown;\r\n /** Session/cwd are not documented Devin wire fields; read defensively. */\r\n session_id?: string;\r\n cwd?: string;\r\n}\r\n\r\nexport class DevinAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name = \"Devin CLI (Cognition)\";\r\n readonly paradigm: HookParadigm = \"json-stdio\";\r\n\r\n readonly capabilities: PlatformCapabilities = {\r\n // Memory surface: AGENTS.md-first managed block — project <projectDir>/AGENTS.md\r\n // via the base default; user scope → ~/.config/devin/AGENTS.md (override below).\r\n // Devin reads AGENTS.md / AGENT.md / CLAUDE.md identically as always-on rules\r\n // (docs.devin.ai/cli/extensibility/rules).\r\n supportsMemory: true,\r\n preToolUse: true,\r\n postToolUse: true,\r\n userPromptSubmit: true,\r\n stop: true,\r\n sessionStart: true,\r\n sessionEnd: true,\r\n // PermissionRequest is a documented Devin hook event (decision-capable).\r\n permissionRequest: true,\r\n // PostCompaction (canonical PostCompact) — observe-only post-compaction hook.\r\n postCompact: true,\r\n // Devin documents NO Notification / PreCompact / SubagentStart / SubagentStop /\r\n // PostToolUseFailure event — those stay unset (install reports the skip-warn).\r\n notification: false,\r\n preCompact: false,\r\n // Devin's PreToolUse/PermissionRequest can block/deny via the simple\r\n // {decision} reply, but it documents no tool-arg/-output rewrite and no\r\n // context-injection reply channel.\r\n canModifyArgs: false,\r\n canModifyOutput: false,\r\n canInjectSessionContext: false,\r\n // Devin registers stdio and remote MCP servers. Remote is Streamable HTTP\r\n // (\"http\", the URL default) with a legacy \"sse\" fallback — both map to our\r\n // remote-transport handling; advertise both.\r\n transports: [\"stdio\", \"http\", \"sse\"],\r\n // Devin natively interpolates ${env:VAR} / ${file:/path} in secret-bearing\r\n // server fields, so the token survives into the host config (pass-through).\r\n nativeServerEnvInterpolation: true,\r\n // Content surfaces. Devin reads Agent Skills from its `.devin` config tree\r\n // at <configDir>/skills/<name>/SKILL.md — the SKILL.md format + frontmatter\r\n // are byte-confirmed (docs.devin.ai/cli/extensibility/skills/creating-skills),\r\n // so skills IS wired.\r\n supportsSkills: true,\r\n // Devin ALSO documents native slash commands (/cli/reference/commands) and\r\n // subagents (/cli/subagents), but their exact on-disk dir names are NOT\r\n // byte-confirmed from a first-party config reference — to avoid fabricating a\r\n // path, commands/subagents stay unset (BaseAdapter skip-warn) in v1. See the\r\n // module header CEILING note.\r\n };\r\n\r\n // ── Detection ────────────────────────────────────────────────────────────\r\n\r\n detectInstalled(projectDir: string): DetectedPlatform {\r\n const userDir = devinUserConfigDir();\r\n const userCfg = join(userDir, \"config.json\");\r\n const projDir = join(projectDir, \".devin\");\r\n const projCfg = join(projDir, \"config.json\");\r\n // The installed binary symlink (~/.local/bin/devin) is a strong marker even\r\n // before any config file is written.\r\n const binMarker = join(homedir(), \".local\", \"bin\", \"devin\");\r\n\r\n const userInstalled = existsSync(userDir) || existsSync(userCfg) || existsSync(binMarker);\r\n const projInstalled = existsSync(projDir) || existsSync(projCfg);\r\n const installed = userInstalled || projInstalled;\r\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\r\n const configPath = scope === \"project\" ? projCfg : userCfg;\r\n\r\n return {\r\n id: this.id,\r\n name: this.name,\r\n installed,\r\n paradigm: this.paradigm,\r\n capabilities: this.capabilities,\r\n configPath,\r\n scope,\r\n reason: installed\r\n ? `found Devin CLI config (${scope}) at ${configPath}`\r\n : `no Devin CLI config at ${userDir} or ${projDir}`,\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, \".devin\")\r\n : devinUserConfigDir();\r\n }\r\n\r\n getServerConfigPath(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"config.json\");\r\n }\r\n\r\n /** Hooks live under the `\"hooks\"` key in the SAME config.json. */\r\n getHookConfigPath(ctx: InstallContext): string {\r\n return this.getServerConfigPath(ctx);\r\n }\r\n\r\n // ── Memory surface: user-scope AGENTS.md ─────────────────────────────────\r\n // Project scope rides on the AGENTS.md base default (project root). User scope\r\n // targets ~/.config/devin/AGENTS.md (%APPDATA%\\devin\\AGENTS.md on Windows) —\r\n // Devin's documented global-rules file (docs.devin.ai/cli/extensibility/rules).\r\n protected override memoryTargets(ctx: InstallContext): MemoryTarget[] {\r\n if (this.memoryOverride(ctx)?.path || ctx.scope !== \"user\") {\r\n return super.memoryTargets(ctx);\r\n }\r\n return [\r\n {\r\n path: join(devinUserConfigDir(), \"AGENTS.md\"),\r\n reason: \"Devin CLI global rules file (~/.config/devin/AGENTS.md)\",\r\n },\r\n ];\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 devin\"\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 Devin's native mcpServers entry. */\r\n private renderServerEntry(\r\n ctx: InstallContext,\r\n server: ServerDef,\r\n ): DevinStdioServer | DevinHttpServer {\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 // command/args are passed verbatim (mirrors claude-code — they route\r\n // through the serve wrapper and rarely carry secrets).\r\n const entry: DevinStdioServer = { command };\r\n if (args.length > 0) entry.args = args;\r\n const env = this.renderEnv(server.env);\r\n if (env) entry.env = env;\r\n return entry;\r\n }\r\n\r\n // http / sse (and any other remote transport) — Devin registers a URL.\r\n // Map our \"http\" transport to Streamable HTTP (the URL default) and \"sse\" to\r\n // legacy SSE; omit `transport` for the default-http case to keep the entry\r\n // minimal (Devin infers http from a URL). ${env:VAR} survives into the\r\n // config via Devin's native interpolation (rewriteEnvRefs/devinEnvToken).\r\n const entry: DevinHttpServer = {\r\n url: rewriteEnvRefs(server.url ?? \"\", devinEnvToken),\r\n };\r\n if (transport === \"sse\") entry.transport = \"sse\";\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. Devin interpolates ${env:VAR} natively, so each\r\n * ${env:VAR} token is passed THROUGH (rewriteEnvRefs/devinEnvToken) rather\r\n * than baked to a literal — secrets never land in the committed config.\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)) {\r\n out[k] = rewriteEnvRefs(v, devinEnvToken);\r\n }\r\n return out;\r\n }\r\n\r\n // ── Hook install / uninstall (config.json \"hooks\" key, nested-rule shape) ──\r\n\r\n installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.hooks === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"hooks disabled for devin\" }];\r\n }\r\n if (connector.hookEvents.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no hooks\" }];\r\n }\r\n\r\n const pending = connector.hookEvents.map((event) => ({\r\n event,\r\n matcher: connector.hooks[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 );\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 * Devin's hook-merge descriptor (NESTED shape `{ matcher, hooks: [...] }`).\r\n * Bound to `ctx` because Devin's ownership is CONNECTOR-GENERIC (ANY of our\r\n * commands in the entry's inner `hooks`):\r\n * - mapEvent = drop events outside SUPPORTED_EVENTS; the canonical\r\n * PostCompact is rewritten to Devin's on-wire \"PostCompaction\" key while\r\n * all other supported events map 1:1.\r\n * - entryOwnsCommand reuses isOurCommand over the inner commands.\r\n * - uninstall = inner-strip ONLY (ownsEntryForRemove never-true).\r\n */\r\n private hookDescriptor(ctx: InstallContext): HookMergeDescriptor<DevinHookEntry> {\r\n return {\r\n mapEvent: (e) => {\r\n if (!SUPPORTED_EVENTS.has(e as HookEventName)) return undefined;\r\n return e === \"PostCompact\" ? DEVIN_POSTCOMPACT : e;\r\n },\r\n unmappedWarnDetail: (e) => `${e} has no Devin hook equivalent — skipped`,\r\n renderEntry: (_event, matcher, command) => ({\r\n matcher,\r\n hooks: [{ type: \"command\", command }],\r\n }),\r\n entryOwnsCommand: (entry, _command) =>\r\n (entry.hooks ?? []).some((h) => this.isOurCommand(h.command, ctx)),\r\n ownsEntryForRemove: () => () => false,\r\n stripInner: (c) => (entry) => {\r\n const innerBefore = entry.hooks?.length ?? 0;\r\n const inner = (entry.hooks ?? []).filter((h) => !this.isOurCommand(h.command, c));\r\n return {\r\n next: inner.length > 0 ? { matcher: entry.matcher ?? \"\", hooks: inner } : null,\r\n removed: innerBefore - inner.length,\r\n };\r\n },\r\n skipDetail: (event) => `hooks.${event} already registered`,\r\n removeDetail: (event, removed) => `hooks.${event} (${removed})`,\r\n absentDetail: \"no hooks section present\",\r\n noMatchDetail: \"no matching hook entries\",\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: skills ──────────────────────────────────────────────\r\n // CEILING (v1): only the byte-confirmed SKILL.md layout is wired. Devin\r\n // documents the SKILL.md format + frontmatter\r\n // (docs.devin.ai/cli/extensibility/skills/creating-skills) under the `.devin`\r\n // tree (<configDir>/skills/<name>/SKILL.md). Commands/subagents on-disk dir\r\n // names are NOT byte-confirmed from a first-party config reference, so they\r\n // stay on the BaseAdapter skip-warn rather than guessing a path.\r\n\r\n /** Native skill dir: <configDir>/skills/<name>. */\r\n private skillDir(ctx: InstallContext, name: string): string {\r\n return join(this.getConfigDir(ctx), \"skills\", name);\r\n }\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 devin\" }];\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\"), this.renderSkill(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 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 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.\r\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\r\n }\r\n return changes;\r\n }\r\n\r\n /**\r\n * Render a skill's SKILL.md: frontmatter (name, description) + body. Devin's\r\n * SKILL.md frontmatter documents name/description; extra keys pass through\r\n * verbatim. No model/allowed-tools skill field is emitted.\r\n */\r\n private renderSkill(skill: SkillDef): string {\r\n const frontmatter: Record<string, unknown> = {\r\n name: skill.name,\r\n description: skill.description,\r\n };\r\n if (skill.extra) Object.assign(frontmatter, skill.extra);\r\n return this.renderFrontmatterMd(frontmatter, skill.body);\r\n }\r\n\r\n // ── Diagnostics ──────────────────────────────────────────────────────────\r\n\r\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\r\n const cfgPath = this.getServerConfigPath(ctx);\r\n const connectorId = ctx.connector.id;\r\n const homeBin = ctx.homeBinPath;\r\n const hookEvents = ctx.connector.hookEvents;\r\n return [\r\n {\r\n name: `${this.name}: config.json present`,\r\n check: () =>\r\n existsSync(cfgPath)\r\n ? { status: \"OK\", detail: cfgPath }\r\n : { status: \"FAIL\", detail: `not found: ${cfgPath}` },\r\n },\r\n {\r\n name: `${this.name}: server entry registered`,\r\n check: () => {\r\n if (!ctx.connector.server) {\r\n return { status: \"OK\", detail: \"no MCP server declared\" };\r\n }\r\n const cfg = this.readJson<{ [k: string]: Record<string, unknown> }>(cfgPath);\r\n const bucket = cfg?.[MCP_ROOT_KEY];\r\n if (!cfg || !bucket) {\r\n return { status: \"FAIL\", detail: `no ${MCP_ROOT_KEY} in ${cfgPath}` };\r\n }\r\n return connectorId in bucket\r\n ? { status: \"OK\", detail: `${MCP_ROOT_KEY}.${connectorId} present` }\r\n : {\r\n status: \"FAIL\",\r\n detail: `no ${MCP_ROOT_KEY}.${connectorId} in ${cfgPath}`,\r\n };\r\n },\r\n },\r\n {\r\n name: `${this.name}: hook command registered`,\r\n check: () => {\r\n if (hookEvents.length === 0) {\r\n return { status: \"OK\", detail: \"no hooks declared\" };\r\n }\r\n const file = this.readJson<DevinHooksFile>(cfgPath);\r\n if (!file) return { status: \"FAIL\", detail: `cannot read ${cfgPath}` };\r\n const hooks = file.hooks ?? {};\r\n const registered = Object.values(hooks).some((entries) =>\r\n (entries ?? []).some((e) =>\r\n (e.hooks ?? []).some((h) =>\r\n isHomeBinHookCommand(h.command, homeBin, connectorId),\r\n ),\r\n ),\r\n );\r\n return registered\r\n ? { status: \"OK\", detail: \"hook command present\" }\r\n : { status: \"FAIL\", detail: `no hook for ${connectorId} in ${cfgPath}` };\r\n },\r\n },\r\n ];\r\n }\r\n\r\n // ── Runtime: parse Devin stdin JSON → normalized event ─────────────────────\r\n\r\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\r\n const input = (raw ?? {}) as DevinWireInput;\r\n const connectorId = typeof input.connector === \"string\" ? input.connector : \"\";\r\n const sessionId = typeof input.session_id === \"string\" ? input.session_id : \"\";\r\n const projectDir = typeof input.cwd === \"string\" ? input.cwd : undefined;\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 ev: PostToolUseEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n ...(toolResponseToString(input.tool_response) !== undefined\r\n ? { toolOutput: toolResponseToString(input.tool_response) }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"PermissionRequest\": {\r\n // Devin PermissionRequest stdin: { tool_name, tool_input }. No\r\n // permission-suggestion entries are documented.\r\n const ev: PermissionRequestEvent = {\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 \"UserPromptSubmit\": {\r\n const ev: UserPromptSubmitEvent = {\r\n ...base,\r\n prompt: typeof input.prompt === \"string\" ? input.prompt : \"\",\r\n };\r\n return ev;\r\n }\r\n case \"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 \"PostCompact\": {\r\n // Devin's PostCompaction wire carries { summary } (no trigger enum);\r\n // summary rides on base.raw (no normalized field).\r\n const ev: PostCompactEvent = { ...base };\r\n return ev;\r\n }\r\n case \"SessionStart\": {\r\n const ev: SessionStartEvent = {\r\n ...base,\r\n source: normalizeSessionSource(input.source),\r\n };\r\n return ev;\r\n }\r\n case \"SessionEnd\": {\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 default: {\r\n // Devin never delivers Notification / PreCompact / SubagentStart /\r\n // SubagentStop / PostToolUseFailure (see SUPPORTED_EVENTS). Surface it\r\n // loudly rather than silently mis-parse.\r\n throw new Error(`unsupported devin hook event: ${String(event)}`);\r\n }\r\n }\r\n }\r\n\r\n // ── Runtime: normalized response → Devin native (simple {decision}) reply ──\r\n\r\n formatReply(event: HookEventName, response: HookResponse): HookReply {\r\n const decision = response.decision ?? \"allow\";\r\n\r\n // PostCompaction / SessionEnd are observe-only — no Decision Control. Every\r\n // decision is an unconditional no-op passthrough.\r\n if (event === \"PostCompact\" || event === \"SessionEnd\") {\r\n return { exitCode: 0 };\r\n }\r\n\r\n // PermissionRequest: an explicit allow is an active GRANT (Devin's \"approve\"\r\n // verb); deny refuses the tool. ask/context/void fall through to the native\r\n // dialog (exit 0, no decision output).\r\n if (event === \"PermissionRequest\") {\r\n if (decision === \"deny\") {\r\n return this.stdout({\r\n decision: \"deny\",\r\n reason: response.reason ?? \"Denied by hook\",\r\n });\r\n }\r\n if (decision === \"allow\") {\r\n return this.stdout({ decision: \"approve\" });\r\n }\r\n return { exitCode: 0 };\r\n }\r\n\r\n // deny → block the action with a reason (Devin's top-level {decision:\"block\"}\r\n // form, NOT Claude's hookSpecificOutput envelope). Covers PreToolUse and\r\n // Stop (Stop-block keeps the agent running with `reason` as guidance).\r\n if (decision === \"deny\") {\r\n return this.stdout({\r\n decision: \"block\",\r\n reason: response.reason ?? \"Blocked by hook\",\r\n });\r\n }\r\n\r\n // ask is not a distinct Devin reply verb (only approve/block/deny) — fall\r\n // through to allow. allow / modify (no rewrite channel) / context (no\r\n // injection channel) / void → pass through with exit 0.\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/**\r\n * Devin's native interpolation token reconstructor. Devin reads the SAME\r\n * `${env:VAR}` (and `${env:VAR:-default}`) form the connector author writes, so\r\n * the rewrite is a faithful round-trip of the token (Devin resolves it at\r\n * runtime — the secret never lands in the committed config).\r\n */\r\nfunction devinEnvToken(name: string, def?: string): string {\r\n return def !== undefined ? `\\${env:${name}:-${def}}` : `\\${env:${name}}`;\r\n}\r\n\r\n/** Coerce a Devin PostToolUse `tool_response` into a string for the normalized event. */\r\nfunction toolResponseToString(value: unknown): string | undefined {\r\n if (value == null) return undefined;\r\n if (typeof value === \"string\") return value;\r\n try {\r\n return JSON.stringify(value);\r\n } catch {\r\n return String(value);\r\n }\r\n}\r\n\r\nexport const adapter = new DevinAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":["entry"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/doctor.ts","../src/runtime/probe.ts"],"sourcesContent":["/**\r\n * cli/commands/doctor — health-check every detected platform.\r\n *\r\n * For each detected host (or the explicit --targets list) we load its adapter,\r\n * build a uniform InstallContext, and run adapter.doctor(ctx). Results are\r\n * printed as pass/warn/fail with any suggested fix. Exit code is non-zero when\r\n * any check fails (warns alone do not fail the command).\r\n *\r\n * The connector context comes from the local config when present; otherwise a\r\n * minimal id-only connector is used so path-only checks still run.\r\n */\r\n\r\nimport { parseArgs } from \"node:util\";\r\n\r\nimport type {\r\n DiagnosticResult,\r\n InstallScope,\r\n PlatformId,\r\n ResolvedConnector,\r\n} from \"../../core/types.js\";\r\nimport type { InstallContext } from \"../../adapters/spi.js\";\r\nimport { detectInstalledPlatforms } from \"../../adapters/detect.js\";\r\nimport { loadAdapter, REGISTERED_PLATFORM_IDS } from \"../../adapters/registry.js\";\r\nimport {\r\n findConnectorConfig,\r\n listRegisteredConnectors,\r\n loadConnectorFromPath,\r\n readRegisteredMeta,\r\n} from \"../../core/load-connector.js\";\r\nimport { syncConnector } from \"../../core/installer.js\";\r\nimport { marketplaceDoctorChecks } from \"../../core/marketplace.js\";\r\nimport { readMarketplaceInstalls } from \"../../core/marketplace-state.js\";\r\nimport { dataRoot, homeBinPath } from \"../../core/paths.js\";\r\nimport { probeStdioServer } from \"../../runtime/probe.js\";\r\nimport { explainHooks } from \"../../sdk/test-harness.js\";\r\nimport type { HookEventVerdict } from \"../../sdk/test-harness.js\";\r\nimport { fail, maybePrintBanner, parseScope, parseTargets, print } from \"../app.js\";\r\n\r\nconst STATUS_GLYPH: Record<DiagnosticResult[\"status\"], string> = {\r\n pass: \"[pass]\",\r\n warn: \"[warn]\",\r\n fail: \"[FAIL]\",\r\n};\r\n\r\n/** A resolved connector together with the path to its source module. */\r\ninterface ConnectorEntry {\r\n connector: ResolvedConnector;\r\n /** Absolute path to the connector source (JSON or JS/TS). Used by syncConnector. */\r\n modulePath: string;\r\n}\r\n\r\n/**\r\n * Resolve which connector(s) doctor should health-check, in precedence order:\r\n * 1. An explicit --connector path, or a local agent-connector.config.* file.\r\n * 2. Every connector registered under the data-root (what is actually\r\n * installed). This is the common case — `doctor` from anywhere reports on\r\n * the real installs, not a guess from the working directory.\r\n * 3. A minimal id-only placeholder so path-only checks still run.\r\n */\r\nasync function resolveDoctorConnectors(\r\n connectorPath: string | undefined,\r\n projectDir: string,\r\n): Promise<ConnectorEntry[] | { error: string }> {\r\n // When the user explicitly passed --connector <path>, any load failure is an\r\n // error they need to see — silently falling through to registered connectors\r\n // would report on a completely different set of installs with no indication\r\n // that the requested connector was never loaded.\r\n if (connectorPath !== undefined) {\r\n const { connector, modulePath } = await loadConnectorFromPath(connectorPath);\r\n return [{ connector, modulePath }];\r\n }\r\n\r\n const configPath = findConnectorConfig(projectDir);\r\n if (configPath) {\r\n try {\r\n const { connector, modulePath } = await loadConnectorFromPath(configPath);\r\n return [{ connector, modulePath }];\r\n } catch {\r\n /* fall through — implicit discovery failure is a convenience, not an error */\r\n }\r\n }\r\n\r\n const registered = listRegisteredConnectors();\r\n if (registered.length > 0) {\r\n return registered.map((connector) => {\r\n const meta = readRegisteredMeta(connector.id);\r\n return { connector, modulePath: meta?.modulePath ?? \"\" };\r\n });\r\n }\r\n\r\n const fallback: ResolvedConnector = {\r\n id: \"agent-connector\",\r\n displayName: \"agent-connector\",\r\n version: \"0.0.0\",\r\n hooks: {},\r\n hookEvents: [],\r\n telemetry: {\r\n enabled: true,\r\n modelFamilyHint: \"auto\",\r\n measureToolDefs: true,\r\n hostNativeUsage: false,\r\n store: \"ndjson\",\r\n calibration: { anthropicCountTokens: false },\r\n },\r\n commands: [],\r\n skills: [],\r\n subagents: [],\r\n memory: [],\r\n actions: [],\r\n platforms: {},\r\n targets: \"auto\",\r\n };\r\n return [{ connector: fallback, modulePath: \"\" }];\r\n}\r\n\r\nfunction buildContext(\r\n connector: ResolvedConnector,\r\n id: PlatformId,\r\n scope: InstallScope,\r\n projectDir: string,\r\n): InstallContext {\r\n return {\r\n connector,\r\n scope: connector.platforms[id]?.scope ?? scope,\r\n projectDir,\r\n homeBinPath: homeBinPath(),\r\n dataRoot: dataRoot(),\r\n dryRun: true,\r\n };\r\n}\r\n\r\n/** Collected diagnostics tagged with the connector entry they came from. */\r\ninterface TaggedResult {\r\n result: DiagnosticResult;\r\n /** Index into the entries array — used to group by connector for heal. */\r\n entryIndex: number;\r\n}\r\n\r\n/**\r\n * Run all diagnostic checks for the given entries/ids and return tagged results\r\n * plus the flat byPlatform buckets for display.\r\n */\r\nasync function collectDiagnostics(\r\n entries: ConnectorEntry[],\r\n ids: PlatformId[],\r\n scope: InstallScope,\r\n projectDir: string,\r\n): Promise<{\r\n byPlatform: { platform: PlatformId; results: DiagnosticResult[] }[];\r\n tagged: TaggedResult[];\r\n anyFail: boolean;\r\n}> {\r\n const multi = entries.length > 1;\r\n const byPlatform: { platform: PlatformId; results: DiagnosticResult[] }[] = [];\r\n const tagged: TaggedResult[] = [];\r\n let anyFail = false;\r\n\r\n for (const id of ids) {\r\n const adapter = await loadAdapter(id);\r\n if (!adapter) {\r\n const r: DiagnosticResult = {\r\n check: `${id}: adapter`,\r\n status: \"fail\",\r\n message: `no adapter registered for ${id}`,\r\n };\r\n byPlatform.push({ platform: id, results: [r] });\r\n anyFail = true;\r\n continue;\r\n }\r\n const results: DiagnosticResult[] = [];\r\n for (let i = 0; i < entries.length; i++) {\r\n const { connector } = entries[i]!;\r\n // A connector with an explicit target list is only checked on those hosts.\r\n if (connector.targets !== \"auto\" && !connector.targets.includes(id)) continue;\r\n\r\n // MARKETPLACE-installed on this host: its surfaces are delivered via the\r\n // host's plugin (in the plugin cache), NOT the direct-config locations the\r\n // adapter's doctor() inspects — so the direct checks would falsely FAIL\r\n // (mcp_servers/command/skill \"not found\"). Skip them; the marketplace-method\r\n // checks below cover this install's real health.\r\n if (readMarketplaceInstalls(connector.id)[id]) {\r\n const note: DiagnosticResult = {\r\n check: `${connector.id}: ${id} delivery`,\r\n status: \"pass\",\r\n message: \"surfaces delivered via the marketplace plugin — see marketplace checks\",\r\n };\r\n const tagged_note = multi ? { ...note, check: `(${connector.id}) ${note.check}` } : note;\r\n results.push(tagged_note);\r\n tagged.push({ result: tagged_note, entryIndex: i });\r\n continue;\r\n }\r\n\r\n const ctx = buildContext(connector, id, scope, projectDir);\r\n let r: DiagnosticResult[];\r\n try {\r\n r = adapter.doctor(ctx);\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n r = [{ check: `${adapter.name}: doctor`, status: \"fail\", message }];\r\n }\r\n // When checking multiple connectors, tag each check with the connector id.\r\n const tagged_r = multi ? r.map((d) => ({ ...d, check: `(${connector.id}) ${d.check}` })) : r;\r\n results.push(...tagged_r);\r\n for (const tr of tagged_r) tagged.push({ result: tr, entryIndex: i });\r\n }\r\n if (results.some((d) => d.status === \"fail\")) anyFail = true;\r\n byPlatform.push({ platform: id, results });\r\n }\r\n\r\n // ── Marketplace-method checks ─────────────────────────────────────────────\r\n for (let i = 0; i < entries.length; i++) {\r\n const { connector } = entries[i]!;\r\n const groups = await marketplaceDoctorChecks(connector, scope, projectDir);\r\n for (const group of groups) {\r\n const tagged_g = multi\r\n ? group.results.map((d) => ({ ...d, check: `(${connector.id}) ${d.check}` }))\r\n : group.results;\r\n if (tagged_g.some((d) => d.status === \"fail\")) anyFail = true;\r\n for (const tg of tagged_g) tagged.push({ result: tg, entryIndex: i });\r\n const bucket = byPlatform.find((b) => b.platform === group.platform);\r\n if (bucket) bucket.results.push(...tagged_g);\r\n else byPlatform.push({ platform: group.platform, results: tagged_g });\r\n }\r\n }\r\n\r\n return { byPlatform, tagged, anyFail };\r\n}\r\n\r\nconst VERDICT_GLYPH: Record<HookEventVerdict[\"verdict\"], string> = {\r\n honored: \"[honored]\",\r\n degraded: \"[degraded]\",\r\n dropped: \"[dropped]\",\r\n};\r\n\r\n/**\r\n * Resolve which hosts `--explain` reports a connector against: an explicit\r\n * --targets list (intersected with the registry), else the connector's declared\r\n * targets, else (targets:\"auto\") every registered host. Detection-independent —\r\n * the per-event honor verdict is a static/offline property a dev wants BEFORE\r\n * installing, not gated on what happens to be installed.\r\n */\r\nfunction explainHostsFor(\r\n connector: ResolvedConnector,\r\n explicit: PlatformId[] | undefined,\r\n): PlatformId[] {\r\n if (explicit && explicit.length > 0) {\r\n return explicit.filter((id) => REGISTERED_PLATFORM_IDS.has(id));\r\n }\r\n if (connector.targets !== \"auto\") {\r\n return (connector.targets as PlatformId[]).filter((id) =>\r\n REGISTERED_PLATFORM_IDS.has(id),\r\n );\r\n }\r\n return [...REGISTERED_PLATFORM_IDS].sort();\r\n}\r\n\r\n/**\r\n * The exit rule for a single connector's per-event matrix. The principle: a\r\n * non-zero exit means \"something is wrong with YOUR connector\", NOT \"some host\r\n * cannot do hooks as designed\". So:\r\n * • `dropped` (mcp-only host / no native equivalent) is EXPECTED and NEVER\r\n * fails — it mirrors install's `skip` (= exit 0) for the very same\r\n * no-equivalent case the never-silent-skip work ships;\r\n * • `degraded` (the host FIRES the event but silently won't honor the reply,\r\n * e.g. a deny that fails open) is genuine silent misbehavior — but only a\r\n * finding worth FAILING on when the dev EXPLICITLY targeted that host\r\n * (`--targets`, or the connector's own `targets:[...]`). Under `targets:\"auto\"`\r\n * the matrix spans the whole fleet, where such gaps are expected, so it stays\r\n * informational (exit 0) — matching plain-doctor's scope-aware non-failure.\r\n */\r\nfunction explainConnectorFails(\r\n rows: HookEventVerdict[],\r\n scoped: boolean,\r\n): boolean {\r\n if (!scoped) return false;\r\n return rows.some((row) => row.verdict === \"degraded\");\r\n}\r\n\r\n/**\r\n * The `doctor --explain` body: for each resolved connector, the per-(host,\r\n * event) honor matrix from {@link explainHooks} (honored / degraded / dropped\r\n * with the simulate()-grade reason). ALL rows are always printed (visibility is\r\n * never gated on the exit rule). The exit code follows {@link explainConnectorFails}:\r\n * only a `degraded` cell on an EXPLICITLY-targeted host fails the command — a\r\n * `dropped` cell (a host that architecturally cannot fire hooks) never does.\r\n */\r\nasync function runExplain(\r\n entries: ConnectorEntry[],\r\n explicit: PlatformId[] | undefined,\r\n json: boolean,\r\n): Promise<number> {\r\n const report: {\r\n connector: string;\r\n rows: HookEventVerdict[];\r\n scoped: boolean;\r\n }[] = [];\r\n for (const { connector } of entries) {\r\n // \"scoped\" = the dev narrowed the host set deliberately (an explicit\r\n // --targets list, or the connector's own targets:[...]). Under targets:\"auto\"\r\n // the matrix is fleet-wide and degraded cells stay informational.\r\n const scoped =\r\n (explicit != null && explicit.length > 0) || connector.targets !== \"auto\";\r\n if (connector.hookEvents.length === 0) {\r\n report.push({ connector: connector.id, rows: [], scoped });\r\n continue;\r\n }\r\n const hosts = explainHostsFor(connector, explicit);\r\n const rows = await explainHooks(connector, hosts);\r\n report.push({ connector: connector.id, rows, scoped });\r\n }\r\n\r\n const anyFail = report.some((r) => explainConnectorFails(r.rows, r.scoped));\r\n\r\n if (json) {\r\n print(JSON.stringify(report.map(({ connector, rows }) => ({ connector, rows })), null, 2));\r\n return anyFail ? 1 : 0;\r\n }\r\n\r\n for (const { connector, rows } of report) {\r\n print(`${connector} — per-event hook honor:`);\r\n if (rows.length === 0) {\r\n print(\" (connector declares no hooks)\");\r\n print(\"\");\r\n continue;\r\n }\r\n for (const row of rows) {\r\n print(` ${VERDICT_GLYPH[row.verdict]} ${row.host} / ${row.event} — ${row.reason}`);\r\n }\r\n print(\"\");\r\n }\r\n // Footer states the verdict AND the exit rule, so a `dropped`-only run that\r\n // exits 0 (the common default-targets case) is not surprising.\r\n if (anyFail) {\r\n print(\r\n \"doctor --explain: a declared hook event is DEGRADED on an explicitly-targeted host (the host fires it but silently won't honor the reply).\",\r\n );\r\n } else if (report.some((r) => r.rows.some((row) => row.verdict !== \"honored\"))) {\r\n print(\r\n \"doctor --explain: some hosts drop or degrade declared events (expected for mcp-only / fleet-wide gaps) — informational, exit 0.\",\r\n );\r\n } else {\r\n print(\"doctor --explain: every declared hook event is honored on its target hosts.\");\r\n }\r\n return anyFail ? 1 : 0;\r\n}\r\n\r\nexport async function run(argv: string[]): Promise<number> {\r\n const { values } = parseArgs({\r\n args: argv,\r\n options: {\r\n scope: { type: \"string\", default: \"user\" },\r\n targets: { type: \"string\" },\r\n connector: { type: \"string\" },\r\n project: { type: \"string\" },\r\n json: { type: \"boolean\", default: false },\r\n probe: { type: \"boolean\", default: false },\r\n heal: { type: \"boolean\", default: false },\r\n explain: { type: \"boolean\", default: false },\r\n \"dry-run\": { type: \"boolean\", default: false },\r\n // Decorative-banner suppressor (no effect on command output bytes).\r\n quiet: { type: \"boolean\", default: false },\r\n },\r\n allowPositionals: false,\r\n });\r\n\r\n // Decorative header — suppressed under --json (machine output) and --quiet,\r\n // and whenever stdout is not a TTY (so piped/redirected doctor stays clean).\r\n maybePrintBanner({ json: values.json, quiet: values.quiet });\r\n\r\n const projectDir = values.project ?? process.cwd();\r\n\r\n const scope = parseScope(values.scope);\r\n if (scope == null) return fail(`invalid --scope \"${values.scope}\" (use user|project)`);\r\n\r\n const resolved = await resolveDoctorConnectors(values.connector, projectDir).catch(\r\n (err: unknown) => ({\r\n error: `cannot load connector \"${values.connector}\": ${err instanceof Error ? err.message : String(err)}`,\r\n }),\r\n );\r\n if (\"error\" in resolved) return fail(resolved.error);\r\n const entries = resolved;\r\n const connectors = entries.map((e) => e.connector);\r\n\r\n // ── Per-event explain path (--explain) ───────────────────────────────────\r\n // Additive, OFFLINE detail layer: the trustworthy per-(host, event) honor\r\n // matrix (honored / degraded / dropped) the coarse \"any hook present = OK\"\r\n // doctor cannot give. Resolves hosts from the connector's OWN targets (or\r\n // --targets), NOT from detection — a dev wants this BEFORE installing.\r\n if (values.explain) {\r\n return runExplain(entries, parseTargets(values.targets), values.json ?? false);\r\n }\r\n\r\n // Target set: explicit --targets (intersected with the registry), else the\r\n // same chain install uses — connector-declared targets ∩ detected platforms\r\n // (\"auto\" = everything detected). Without this, doctor red-flags every\r\n // detected host the connector never targeted (found dogfooding context-mode:\r\n // targets:[5 ids] installed clean, doctor FAILed the other 9 detected hosts).\r\n const explicit = parseTargets(values.targets);\r\n let ids: PlatformId[];\r\n if (explicit && explicit.length > 0) {\r\n ids = explicit.filter((id) => REGISTERED_PLATFORM_IDS.has(id));\r\n } else {\r\n const detected = await detectInstalledPlatforms(projectDir);\r\n ids = detected.map((p) => p.id);\r\n const anyAuto = connectors.some((c) => c.targets === \"auto\");\r\n if (!anyAuto) {\r\n const targeted = new Set(connectors.flatMap((c) => c.targets as PlatformId[]));\r\n ids = ids.filter((id) => targeted.has(id));\r\n }\r\n }\r\n\r\n if (ids.length === 0 && !values.probe) {\r\n print(\"doctor: no target platforms (none detected; pass --targets to force).\");\r\n return 0;\r\n }\r\n\r\n // ── Heal path ─────────────────────────────────────────────────────────────\r\n if (values.heal) {\r\n const { tagged, anyFail: preFail } = await collectDiagnostics(\r\n entries,\r\n ids,\r\n scope,\r\n projectDir,\r\n );\r\n\r\n // Partition: fixable vs deferred (non-pass, not fixable)\r\n const fixableByEntry = new Map<number, DiagnosticResult[]>();\r\n const deferredByEntry = new Map<number, DiagnosticResult[]>();\r\n for (const { result, entryIndex } of tagged) {\r\n if (result.status === \"pass\") continue;\r\n // A finding is only HEALABLE if it is fixable AND its connector has a\r\n // module path we can re-sync from. A placeholder/unregistered connector\r\n // (empty modulePath) must NEVER be synced — doing so would fabricate a\r\n // bogus registry record via registerConnector(connector, \"\"). Route its\r\n // fixable findings to deferred instead.\r\n const canSync = !!entries[entryIndex]!.modulePath;\r\n if (result.fixable && canSync) {\r\n if (!fixableByEntry.has(entryIndex)) fixableByEntry.set(entryIndex, []);\r\n fixableByEntry.get(entryIndex)!.push(result);\r\n } else {\r\n if (!deferredByEntry.has(entryIndex)) deferredByEntry.set(entryIndex, []);\r\n deferredByEntry.get(entryIndex)!.push(result);\r\n }\r\n }\r\n\r\n if (values[\"dry-run\"]) {\r\n // Dry-run: print what would happen, write nothing.\r\n for (const [idx, fixable] of fixableByEntry) {\r\n const { connector } = entries[idx]!;\r\n print(\r\n `would heal via sync (${connector.id}): ${fixable.map((r) => r.check).join(\", \")}`,\r\n );\r\n }\r\n for (const [idx, deferred] of deferredByEntry) {\r\n const { connector } = entries[idx]!;\r\n for (const r of deferred) {\r\n print(\r\n `would defer (${connector.id}): ${r.check} — ${r.fix ?? \"no auto-fix available\"}`,\r\n );\r\n }\r\n }\r\n if (fixableByEntry.size === 0 && deferredByEntry.size === 0) {\r\n print(\"doctor --heal --dry-run: nothing to heal.\");\r\n }\r\n return 0;\r\n }\r\n\r\n // Real heal: sync each connector that has fixable findings.\r\n for (const [idx] of fixableByEntry) {\r\n const { connector, modulePath } = entries[idx]!;\r\n await syncConnector({\r\n connector,\r\n modulePath,\r\n scope,\r\n projectDir,\r\n targets: ids,\r\n dryRun: false,\r\n });\r\n }\r\n\r\n // Re-run diagnostics to compute what changed.\r\n const { tagged: postTagged, anyFail: postFail } = await collectDiagnostics(\r\n entries,\r\n ids,\r\n scope,\r\n projectDir,\r\n );\r\n\r\n // Classify post-heal results.\r\n const preNonPass = new Set(\r\n tagged\r\n .filter((t) => t.result.status !== \"pass\")\r\n .map((t) => t.result.check),\r\n );\r\n const postNonPass = new Set(\r\n postTagged\r\n .filter((t) => t.result.status !== \"pass\")\r\n .map((t) => t.result.check),\r\n );\r\n\r\n const healed: string[] = [];\r\n const stillFailing: DiagnosticResult[] = [];\r\n const deferred: DiagnosticResult[] = [];\r\n\r\n // Healed = was non-pass before, now pass\r\n for (const check of preNonPass) {\r\n if (!postNonPass.has(check)) healed.push(check);\r\n }\r\n // Post-heal non-pass findings. Classify consistently with the pre-heal\r\n // partition: a fixable finding we could not sync (no modulePath) is deferred,\r\n // not \"still failing after sync\" — we never attempted a sync for it.\r\n for (const { result, entryIndex } of postTagged) {\r\n if (result.status === \"pass\") continue;\r\n const canSync = !!entries[entryIndex]!.modulePath;\r\n if (result.fixable && canSync) {\r\n stillFailing.push(result);\r\n } else {\r\n deferred.push(result);\r\n }\r\n }\r\n\r\n if (values.json) {\r\n print(JSON.stringify({ healed, deferred, stillFailing }, null, 2));\r\n return postFail ? 1 : 0;\r\n }\r\n\r\n if (healed.length > 0) {\r\n print(`healed (${healed.length}):`);\r\n for (const c of healed) print(` [pass] ${c}`);\r\n print(\"\");\r\n }\r\n if (stillFailing.length > 0) {\r\n print(`still failing after sync (${stillFailing.length}):`);\r\n for (const r of stillFailing) {\r\n print(` ${STATUS_GLYPH[r.status]} ${r.check} — ${r.message}`);\r\n if (r.fix) print(` fix: ${r.fix}`);\r\n }\r\n print(\"\");\r\n }\r\n if (deferred.length > 0) {\r\n print(`deferred — requires manual action (${deferred.length}):`);\r\n for (const r of deferred) {\r\n print(` ${STATUS_GLYPH[r.status]} ${r.check} — ${r.message}`);\r\n if (r.fix) print(` fix: ${r.fix}`);\r\n }\r\n print(\"\");\r\n }\r\n if (healed.length === 0 && stillFailing.length === 0 && deferred.length === 0 && !preFail) {\r\n print(\"doctor --heal: nothing to heal, all checks passed.\");\r\n } else if (stillFailing.length === 0 && deferred.length === 0) {\r\n print(\"doctor --heal: all fixable findings resolved.\");\r\n }\r\n return postFail ? 1 : 0;\r\n }\r\n\r\n // ── Normal diagnose path (no --heal) ─────────────────────────────────────\r\n const { byPlatform, anyFail } = await collectDiagnostics(entries, ids, scope, projectDir);\r\n\r\n // ── Live MCP probe (--probe): connector-scoped, not platform-scoped ──────\r\n // Spawns each connector's REAL stdio server and runs initialize → ping →\r\n // tools/list. Probe FAILs fold into the doctor exit code.\r\n const probes: { connector: string; results: DiagnosticResult[] }[] = [];\r\n if (values.probe) {\r\n for (const { connector } of entries) {\r\n const s = connector.server;\r\n if (!s || s.transport !== \"stdio\" || !s.command) {\r\n probes.push({\r\n connector: connector.id,\r\n results: [\r\n {\r\n check: `${connector.id}: MCP probe`,\r\n status: \"warn\",\r\n message: s\r\n ? `transport \"${s.transport}\" is not stdio — live probe skipped`\r\n : \"no server to probe\",\r\n },\r\n ],\r\n });\r\n continue;\r\n }\r\n const results = await probeStdioServer(s.command, s.args ?? [], {\r\n label: connector.id,\r\n ...(s.env ? { env: s.env } : {}),\r\n });\r\n probes.push({ connector: connector.id, results });\r\n }\r\n }\r\n\r\n const probesFail = probes.some((p) => p.results.some((r) => r.status === \"fail\"));\r\n\r\n if (values.json) {\r\n print(JSON.stringify(values.probe ? { platforms: byPlatform, probes } : byPlatform, null, 2));\r\n return anyFail || probesFail ? 1 : 0;\r\n }\r\n\r\n for (const { platform, results } of byPlatform) {\r\n print(`${platform}:`);\r\n for (const r of results) {\r\n print(` ${STATUS_GLYPH[r.status]} ${r.check} — ${r.message}`);\r\n if (r.fix) print(` fix: ${r.fix}`);\r\n }\r\n print(\"\");\r\n }\r\n for (const { connector, results } of probes) {\r\n print(`probe ${connector}:`);\r\n for (const r of results) {\r\n print(` ${STATUS_GLYPH[r.status]} ${r.check} — ${r.message}`);\r\n if (r.fix) print(` fix: ${r.fix}`);\r\n }\r\n print(\"\");\r\n }\r\n print(anyFail || probesFail ? \"doctor: one or more checks FAILED.\" : \"doctor: all checks passed.\");\r\n return anyFail || probesFail ? 1 : 0;\r\n}\r\n","/**\r\n * runtime/probe — a live MCP connection-lifecycle probe for `doctor --probe`.\r\n *\r\n * Where `doctor` checks files on disk, this spawns the connector's REAL stdio\r\n * server and speaks the MCP wire protocol to it: initialize → (read negotiated\r\n * protocolVersion + capabilities + serverInfo) → notifications/initialized →\r\n * ping (liveness) → tools/list (gated on the tools capability). It reuses the\r\n * SAME newline-delimited JSON-RPC framing + id-correlation as the telemetry\r\n * proxy (telemetry/jsonrpc), so the probe and the serve path read the wire\r\n * identically. Each step becomes a DiagnosticResult so doctor renders/aggregates\r\n * it uniformly; a dead/unlaunchable server FAILs (non-zero doctor exit).\r\n *\r\n * Negotiation rule: we OFFER {@link MCP_PROTOCOL_VERSION} but ACCEPT whatever\r\n * version the server returns (we only report it), per the spec — never a\r\n * hardcoded equality. There is no MCP shutdown message; we close stdin then\r\n * SIGTERM/SIGKILL.\r\n */\r\n\r\nimport type { DiagnosticResult } from \"../core/types.js\";\r\nimport { MCP_PROTOCOL_VERSION } from \"../core/mcp-standard.js\";\r\nimport { spawnChild } from \"../core/spawn-child.js\";\r\nimport { type JsonRpcMessage, LineBuffer, idKey, isObject } from \"../telemetry/jsonrpc.js\";\r\n\r\nconst DEFAULT_TIMEOUT_MS = 5000;\r\n\r\nexport interface ProbeOptions {\r\n /** Per-step timeout in ms (default 5000). */\r\n timeoutMs?: number;\r\n /** Prefix for each DiagnosticResult.check (e.g. the connector id). */\r\n label?: string;\r\n /** Env merged over process.env for the spawned server (${env:VAR} resolved). */\r\n env?: Record<string, string>;\r\n /** Protocol version offered in initialize. Default {@link MCP_PROTOCOL_VERSION}. */\r\n protocolVersion?: string;\r\n}\r\n\r\nfunction diag(\r\n status: DiagnosticResult[\"status\"],\r\n check: string,\r\n message: string,\r\n fix?: string,\r\n): DiagnosticResult {\r\n return { check, status, message, ...(fix ? { fix } : {}) };\r\n}\r\n\r\nfunction errMsg(e: unknown): string {\r\n return e instanceof Error ? e.message : String(e);\r\n}\r\n\r\n/** Resolve `${env:VAR}` references in env values against the current process env. */\r\nfunction resolveEnv(env: Record<string, string> | undefined): Record<string, string> {\r\n const out: Record<string, string> = {};\r\n for (const [k, v] of Object.entries(env ?? {})) {\r\n out[k] = v.replace(/\\$\\{env:([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_m, name) => process.env[name] ?? \"\");\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Spawn the stdio MCP server `command args` and run a real lifecycle probe.\r\n * Always resolves (never rejects) with one DiagnosticResult per step.\r\n */\r\nexport async function probeStdioServer(\r\n command: string,\r\n args: string[],\r\n opts: ProbeOptions = {},\r\n): Promise<DiagnosticResult[]> {\r\n const label = opts.label ? `${opts.label}: ` : \"\";\r\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\r\n const protocolVersion = opts.protocolVersion ?? MCP_PROTOCOL_VERSION;\r\n const results: DiagnosticResult[] = [];\r\n\r\n // spawnChild resolves a bare Windows package runner (npx/uvx → .cmd/.exe) so\r\n // the probe agrees with the live serve path; no-op on macOS/Linux.\r\n const child = spawnChild(command, args, {\r\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\r\n env: { ...process.env, ...resolveEnv(opts.env) },\r\n });\r\n\r\n interface Pending {\r\n resolve: (m: JsonRpcMessage) => void;\r\n reject: (e: Error) => void;\r\n }\r\n const pending = new Map<string, Pending>();\r\n let spawnErr: Error | null = null;\r\n let exitCode: number | null = null;\r\n\r\n const rejectAll = (e: Error): void => {\r\n for (const p of pending.values()) p.reject(e);\r\n pending.clear();\r\n };\r\n child.on(\"error\", (e) => {\r\n spawnErr = e;\r\n rejectAll(e);\r\n });\r\n child.on(\"exit\", (code) => {\r\n exitCode = code ?? 0;\r\n rejectAll(new Error(`server exited (code ${exitCode}) before responding`));\r\n });\r\n\r\n const lines = new LineBuffer((line) => {\r\n let parsed: unknown;\r\n try {\r\n parsed = JSON.parse(line);\r\n } catch {\r\n return; // non-JSON stdout noise — ignore\r\n }\r\n if (!isObject(parsed)) return;\r\n const k = idKey((parsed as JsonRpcMessage).id);\r\n if (k && pending.has(k)) {\r\n const p = pending.get(k)!;\r\n pending.delete(k);\r\n p.resolve(parsed as JsonRpcMessage);\r\n }\r\n });\r\n child.stdout?.on(\"data\", (c: Buffer) => lines.push(c));\r\n\r\n const send = (m: JsonRpcMessage): void => {\r\n if (child.stdin && child.stdin.writable) child.stdin.write(`${JSON.stringify(m)}\\n`);\r\n };\r\n const request = (id: number | string, method: string, params?: unknown): Promise<JsonRpcMessage> => {\r\n const key = idKey(id)!;\r\n return new Promise<JsonRpcMessage>((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n pending.delete(key);\r\n reject(new Error(`no response within ${timeoutMs}ms`));\r\n }, timeoutMs);\r\n if (typeof timer.unref === \"function\") timer.unref();\r\n pending.set(key, {\r\n resolve: (m) => {\r\n clearTimeout(timer);\r\n resolve(m);\r\n },\r\n reject: (e) => {\r\n clearTimeout(timer);\r\n reject(e);\r\n },\r\n });\r\n send({ jsonrpc: \"2.0\", id, method, ...(params !== undefined ? { params } : {}) });\r\n });\r\n };\r\n\r\n const cleanup = (): DiagnosticResult[] => {\r\n try {\r\n if (child.stdin && child.stdin.writable) child.stdin.end();\r\n } catch {\r\n /* ignore */\r\n }\r\n try {\r\n child.kill(\"SIGTERM\");\r\n } catch {\r\n /* ignore */\r\n }\r\n const killTimer = setTimeout(() => {\r\n try {\r\n child.kill(\"SIGKILL\");\r\n } catch {\r\n /* ignore */\r\n }\r\n }, 500);\r\n if (typeof killTimer.unref === \"function\") killTimer.unref();\r\n return results;\r\n };\r\n\r\n // 1. initialize\r\n let initResult: Record<string, unknown>;\r\n try {\r\n const res = await request(1, \"initialize\", {\r\n protocolVersion,\r\n capabilities: {},\r\n clientInfo: { name: \"agent-connector-probe\", version: \"1\" },\r\n });\r\n if (isObject(res.error)) {\r\n results.push(\r\n diag(\"fail\", `${label}MCP initialize`, `server returned error: ${JSON.stringify(res.error)}`),\r\n );\r\n return cleanup();\r\n }\r\n initResult = isObject(res.result) ? res.result : {};\r\n } catch (e) {\r\n if (spawnErr) {\r\n results.push(\r\n diag(\r\n \"fail\",\r\n `${label}MCP initialize`,\r\n `server not launchable: ${errMsg(spawnErr)}`,\r\n `check that \"${command}\" is installed and on PATH`,\r\n ),\r\n );\r\n } else if (exitCode !== null) {\r\n // On Windows an unresolvable command surfaces as an early non-zero exit\r\n // (cmd.exe \"command not found\") rather than a spawn 'error' event, so fold\r\n // the not-launchable hint in here too — useful whether the command is\r\n // missing or the server genuinely crashed before initialize.\r\n results.push(\r\n diag(\r\n \"fail\",\r\n `${label}MCP initialize`,\r\n `server not launchable, or it exited (code ${exitCode}) before initialize`,\r\n `check that \"${command}\" is installed and on PATH`,\r\n ),\r\n );\r\n } else {\r\n results.push(diag(\"fail\", `${label}MCP initialize`, errMsg(e)));\r\n }\r\n return cleanup();\r\n }\r\n\r\n const serverInfo = isObject(initResult.serverInfo) ? initResult.serverInfo : {};\r\n const sName = typeof serverInfo.name === \"string\" ? serverInfo.name : \"unknown\";\r\n const sVer = typeof serverInfo.version === \"string\" ? serverInfo.version : \"?\";\r\n const negotiated =\r\n typeof initResult.protocolVersion === \"string\" ? initResult.protocolVersion : \"(none)\";\r\n results.push(diag(\"pass\", `${label}MCP initialize`, `serverInfo ${sName}@${sVer}, protocol ${negotiated}`));\r\n\r\n // capabilities (presence = feature offered)\r\n const caps = isObject(initResult.capabilities) ? initResult.capabilities : {};\r\n const capNames = [\"tools\", \"resources\", \"prompts\", \"logging\", \"completions\"].filter((c) => c in caps);\r\n results.push(\r\n diag(\"pass\", `${label}capabilities`, capNames.length > 0 ? capNames.join(\", \") : \"(none advertised)\"),\r\n );\r\n\r\n // 2. initialized notification (required before normal requests)\r\n send({ jsonrpc: \"2.0\", method: \"notifications/initialized\" });\r\n\r\n // 3. ping — liveness confirm (warn, not fail, on timeout)\r\n try {\r\n await request(\"probe-ping\", \"ping\");\r\n results.push(diag(\"pass\", `${label}ping`, \"alive\"));\r\n } catch (e) {\r\n results.push(diag(\"warn\", `${label}ping`, `no ping response (${errMsg(e)})`));\r\n }\r\n\r\n // 4. tools/list — only if the server advertised a tools capability\r\n if (\"tools\" in caps) {\r\n try {\r\n const res = await request(2, \"tools/list\", {});\r\n const r = isObject(res.result) ? res.result : {};\r\n const tools = Array.isArray(r.tools) ? r.tools : [];\r\n results.push(diag(\"pass\", `${label}tools/list`, `${tools.length} tool(s)`));\r\n } catch (e) {\r\n results.push(diag(\"fail\", `${label}tools/list`, `no tools/list response (${errMsg(e)})`));\r\n }\r\n } else {\r\n results.push(\r\n diag(\"warn\", `${label}tools/list`, \"skipped — server advertised no tools capability\"),\r\n );\r\n }\r\n\r\n return cleanup();\r\n}\r\n"],"mappings":"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","names":["deferred"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/droid/index.ts"],"sourcesContent":["/**\r\n * adapters/droid — Droid (Factory AI) platform adapter for agent-connector.\r\n *\r\n * Droid is a **json-stdio** host with TWO native config surfaces that live in\r\n * DIFFERENT files (so getServerConfigPath ≠ getHookConfigPath):\r\n *\r\n * 1. MCP servers — `mcp.json` (root key `mcpServers`):\r\n * - user scope → ~/.factory/mcp.json\r\n * - project scope → <projectDir>/.factory/mcp.json\r\n * Native stdio entry shape: { type:\"stdio\", command, args, env, disabled };\r\n * a remote server registers a URL ({ type:\"http\", url, headers, disabled }).\r\n *\r\n * 2. Hooks — a SEPARATE `hooks.json` (root key `hooks`) under the same\r\n * `.factory` dir. Droid ships a FULL Claude-compatible lifecycle hook\r\n * system (live-confirmed): PascalCase event names, Claude snake_case stdin\r\n * wire fields, and a Claude-shaped `hookSpecificOutput` reply. Hook\r\n * registrations use the Claude NESTED-rule shape:\r\n * { hooks: { <Event>: [ { matcher?, hooks:[{ type:\"command\", command }] } ] } }\r\n *\r\n * Supported events (Claude-compatible): PreToolUse, PostToolUse,\r\n * UserPromptSubmit, Stop, SubagentStop (stop-only — no SubagentStart), plus the\r\n * full lifecycle set Notification, PreCompact, SessionStart, SessionEnd\r\n * (docs.factory.ai/reference/hooks-reference — Claude-identical PascalCase 1:1\r\n * with the same snake_case stdin wire). Droid exposes no PermissionRequest /\r\n * PostToolUseFailure / SubagentStart, so those degrade to a warn/skip at\r\n * install time.\r\n *\r\n * Reply protocol is Claude-shaped JSON on stdout (exit 0 + `hookSpecificOutput`\r\n * with permissionDecision allow|deny|ask, plus additionalContext). Droid cannot\r\n * rewrite already-emitted tool output, so canModifyOutput is false; it CAN\r\n * inject session context (additionalContext) so canInjectSessionContext is true\r\n * (SessionStart honors hookSpecificOutput.additionalContext per the same docs).\r\n *\r\n * Env handling: env/header/url refs are resolved to literals at install time via\r\n * resolveEnvRefsDeep — the safe default matching the Kiro/Qwen adapters. Droid\r\n * also accepts native ${VAR}, but resolve-to-literal avoids surprises.\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 { renderCommandMd } from \"../claude-code/render.js\";\r\nimport type { Adapter, HookReply, InstallContext, NormalizedEvent } from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookEventName,\r\n HookParadigm,\r\n HookResponse,\r\n NotificationEvent,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PostToolUseEvent,\r\n PreCompactEvent,\r\n PreToolUseEvent,\r\n ServerDef,\r\n SessionEndEvent,\r\n SessionStartEvent,\r\n SkillDef,\r\n StopEvent,\r\n SubagentDef,\r\n SubagentStopEvent,\r\n Transport,\r\n UserPromptSubmitEvent,\r\n} from \"../../core/types.js\";\r\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\r\nimport {\r\n buildWrappedStdio,\r\n isHomeBinHookCommand,\r\n} from \"../../core/spawn.js\";\r\nimport { normalizeSessionSource } from \"../claude-code/wire.js\";\r\n\r\nconst HOST: PlatformId = \"droid\";\r\nconst MCP_ROOT_KEY = \"mcpServers\";\r\n\r\n/**\r\n * Canonical events Droid actually fires. Droid's hook event names are\r\n * Claude-identical (PascalCase), so the canonical name is registered directly.\r\n * Notification / PreCompact / SessionStart / SessionEnd are all documented\r\n * Droid lifecycle hooks (docs.factory.ai/reference/hooks-reference) and so are\r\n * wired here too.\r\n *\r\n * Droid is a STOP-ONLY subagent host: its live hooks-reference lists\r\n * SubagentStop but NO SubagentStart, and it has no permission-dialog\r\n * (PermissionRequest) or tool-failure (PostToolUseFailure) events — those\r\n * three warn/skip at install time.\r\n */\r\nconst SUPPORTED_EVENTS: ReadonlySet<HookEventName> = new Set<HookEventName>([\r\n \"PreToolUse\",\r\n \"PostToolUse\",\r\n \"UserPromptSubmit\",\r\n \"Stop\",\r\n \"SubagentStop\",\r\n \"Notification\",\r\n \"PreCompact\",\r\n \"SessionStart\",\r\n \"SessionEnd\",\r\n]);\r\n\r\n/**\r\n * Native MCP server entry shapes Droid accepts under `mcpServers`.\r\n * A stdio entry carries an explicit `type: \"stdio\"` discriminator and a\r\n * `disabled` flag; remote transports register a URL.\r\n */\r\ninterface DroidStdioServer {\r\n type: \"stdio\";\r\n command: string;\r\n args?: string[];\r\n env?: Record<string, string>;\r\n disabled: boolean;\r\n}\r\ninterface DroidHttpServer {\r\n type: \"http\";\r\n url: string;\r\n headers?: Record<string, string>;\r\n disabled: boolean;\r\n}\r\n\r\n/** A single Droid native hook registration entry (Claude-shaped, nested). */\r\ninterface DroidHookEntry {\r\n matcher: string;\r\n hooks: Array<{ type: \"command\"; command: string }>;\r\n}\r\n\r\n/** The shape of Droid's hooks.json (only the parts we touch). */\r\ninterface DroidHooksFile {\r\n hooks?: Record<string, DroidHookEntry[]>;\r\n [key: string]: unknown;\r\n}\r\n\r\n/** Raw Droid CLI hook stdin payload (Claude-compatible snake_case wire fields). */\r\ninterface DroidWireInput {\r\n session_id?: string;\r\n transcript_path?: string;\r\n cwd?: string;\r\n hook_event_name?: string;\r\n tool_name?: string;\r\n tool_input?: Record<string, unknown>;\r\n tool_response?: unknown;\r\n prompt?: string;\r\n /** Notification — the user-facing notification text. */\r\n message?: string;\r\n /** PreCompact — compaction trigger (manual = /compact, auto = full context). */\r\n trigger?: \"auto\" | \"manual\";\r\n /** SessionStart — startup | resume | clear | compact (matcher source). */\r\n source?: string;\r\n /** SessionEnd — clear | logout | prompt_input_exit | other. */\r\n reason?: string;\r\n /** Stop / SubagentStop loop guard. */\r\n stop_hook_active?: boolean;\r\n // NO SubagentStop agent_id / agent_type / agent_transcript_path /\r\n // last_assistant_message: Factory documents a SINGLE shared \"Stop and\r\n // SubagentStop Input\" whose COMPLETE shape is { session_id, transcript_path,\r\n // cwd, permission_mode, hook_event_name, stop_hook_active } — there is no\r\n // per-subagent identity, no subagent transcript, and no final-response field\r\n // on the wire (the final response is reachable only via transcript_path). We\r\n // previously read all four; they were false-friends that never populated.\r\n // Primary source: docs.factory.ai/reference/hooks-reference\r\n // (Stop and SubagentStop Input — verified zero occurrences of each field).\r\n /** Injected by the entrypoint so the runtime knows which connector to dispatch. */\r\n connector?: unknown;\r\n}\r\n\r\nexport class DroidAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name = \"Droid (Factory)\";\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 preToolUse: true,\r\n postToolUse: true,\r\n // Notification / PreCompact / SessionStart / SessionEnd are documented Droid\r\n // lifecycle hooks (docs.factory.ai/reference/hooks-reference) with the same\r\n // Claude-shaped snake_case stdin wire — all four fire natively.\r\n preCompact: true,\r\n sessionStart: true,\r\n sessionEnd: true,\r\n userPromptSubmit: true,\r\n stop: true,\r\n notification: true,\r\n // Newer events: Droid ships SubagentStop (stop-only — no SubagentStart).\r\n // permissionRequest / postToolUseFailure / subagentStart stay unset (no\r\n // Droid analog); install reports the standard skip-warn for them.\r\n subagentStop: true,\r\n // Droid's PreToolUse can deny/ask (Claude-shaped), but it cannot rewrite\r\n // already-emitted tool output. canModifyArgs left false until confirmed.\r\n canModifyArgs: false,\r\n canModifyOutput: false,\r\n // Droid honors additionalContext on the stdout reply.\r\n canInjectSessionContext: true,\r\n // Droid registers stdio and Streamable HTTP MCP servers.\r\n transports: [\"stdio\", \"http\"],\r\n // Droid HAS a confirmed command-driven status line: `statusLine.command`\r\n // (object {command, padding?, maxRows?}) in ~/.factory/settings.json, stdout\r\n // rendered above the input (docs.factory.ai/cli/configuration/settings). BUT\r\n // Factory publishes NO statusline stdin payload schema (the model/cost/\r\n // context_window fields circulating online are Claude Code's, not\r\n // Factory-captured), so the payload→StatuslineContext mapping can't be\r\n // authored without a live stdin capture (blocked: needs an authenticated\r\n // droid session). NOT a permanent gap — supportsStatusline stays unset until\r\n // the payload is captured (set statusLine.command to a stdin-dumping script\r\n // on a real droid box to confirm).\r\n // Content surfaces: Droid implements all three (live-confirmed Factory dirs).\r\n // command → <configDir>/commands/<name>.md (md+frontmatter: description, argument-hint)\r\n // skill → <configDir>/skills/<name>/SKILL.md (+ resources)\r\n // subagent → <configDir>/droids/<name>.md (MARKDOWN — folder is droids/, NOT agents/)\r\n supportsCommands: true,\r\n supportsSkills: true,\r\n supportsSubagents: true,\r\n // Actions: Droid runs EXECUTABLE files placed at <configDir>/commands/<id>\r\n // (NO .md extension) via `/<id>` — \"Executable files must start with a valid\r\n // shebang so the CLI can call the interpreter\" and stdout is returned to the\r\n // transcript (docs.factory.ai/cli/configuration/custom-slash-commands). We\r\n // write a POSIX shebang script that execs the home-bin action verb. The\r\n // exec-file interpreter resolution is UNVERIFIED on win32, so the emitter\r\n // skip-warns there (see installActions).\r\n supportsActions: true,\r\n };\r\n\r\n // ── Detection ────────────────────────────────────────────────────────────\r\n\r\n detectInstalled(projectDir: string): DetectedPlatform {\r\n const userDir = join(homedir(), \".factory\");\r\n const userMcp = join(userDir, \"mcp.json\");\r\n const projDir = join(projectDir, \".factory\");\r\n const projMcp = join(projDir, \"mcp.json\");\r\n\r\n const userInstalled = existsSync(userDir) || existsSync(userMcp);\r\n const projInstalled = existsSync(projDir) || existsSync(projMcp);\r\n const installed = userInstalled || projInstalled;\r\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\r\n const configPath = scope === \"project\" ? projMcp : userMcp;\r\n\r\n return {\r\n id: this.id,\r\n name: this.name,\r\n installed,\r\n paradigm: this.paradigm,\r\n capabilities: this.capabilities,\r\n configPath,\r\n scope,\r\n reason: installed\r\n ? `found Droid config (${scope}) at ${configPath}`\r\n : `no .factory config at ${userDir} or ${projDir}`,\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, \".factory\")\r\n : join(homedir(), \".factory\");\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 /** Hooks live in a SEPARATE hooks.json under the same `.factory` dir. */\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 droid\"\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 Droid's native mcpServers entry. */\r\n private renderServerEntry(\r\n ctx: InstallContext,\r\n server: ServerDef,\r\n ): DroidStdioServer | DroidHttpServer {\r\n const transport: Transport = server.transport;\r\n const disabled = server.enabled === false;\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 // Resolve every ${env:VAR} to a literal at install time (safe path).\r\n const entry: DroidStdioServer = {\r\n type: \"stdio\",\r\n command: resolveEnvRefsDeep(command),\r\n disabled,\r\n };\r\n if (args.length > 0) entry.args = resolveEnvRefsDeep(args);\r\n const env = this.renderEnv(server.env);\r\n if (env) entry.env = env;\r\n return entry;\r\n }\r\n\r\n // http (and any other remote transport) — Droid registers a URL.\r\n const entry: DroidHttpServer = {\r\n type: \"http\",\r\n url: resolveEnvRefsDeep(server.url ?? \"\"),\r\n disabled,\r\n };\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. Resolve `${env:VAR}` references to literals at\r\n * install time (the safe path; Droid would also accept native ${VAR}).\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 return resolveEnvRefsDeep({ ...env });\r\n }\r\n\r\n // ── Hook install / uninstall (separate hooks.json, nested-rule shape) ─────\r\n\r\n installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.hooks === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"hooks disabled for droid\" }];\r\n }\r\n if (connector.hookEvents.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no hooks\" }];\r\n }\r\n\r\n const pending = connector.hookEvents.map((event) => ({\r\n event,\r\n matcher: connector.hooks[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 );\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 * Droid's hook-merge descriptor (NESTED shape `{ matcher, hooks: [...] }`).\r\n * Bound to `ctx` because droid's ownership is CONNECTOR-GENERIC (ANY of our\r\n * commands in the entry's inner `hooks`, not a specific-command match):\r\n * - mapEvent = drop events outside SUPPORTED_EVENTS (Droid has no equivalent\r\n * for PermissionRequest / PostToolUseFailure / SubagentStart / PostCompact);\r\n * droid's event names are Claude-identical, so supported events map 1:1.\r\n * - entryOwnsCommand ignores the specific command — it reuses isOurCommand\r\n * over the inner commands (the old entryHasOurCommand, folded in here).\r\n * - uninstall = inner-strip ONLY (ownsEntryForRemove never-true); every detail\r\n * string is the literal droid wording.\r\n */\r\n private hookDescriptor(ctx: InstallContext): HookMergeDescriptor<DroidHookEntry> {\r\n return {\r\n mapEvent: (e) => (SUPPORTED_EVENTS.has(e as HookEventName) ? e : undefined),\r\n unmappedWarnDetail: (e) => `${e} has no Droid hook equivalent — skipped`,\r\n renderEntry: (_event, matcher, command) => ({\r\n matcher,\r\n hooks: [{ type: \"command\", command }],\r\n }),\r\n entryOwnsCommand: (entry, _command) =>\r\n (entry.hooks ?? []).some((h) => this.isOurCommand(h.command, ctx)),\r\n ownsEntryForRemove: () => () => false,\r\n stripInner: (c) => (entry) => {\r\n const innerBefore = entry.hooks?.length ?? 0;\r\n const inner = (entry.hooks ?? []).filter((h) => !this.isOurCommand(h.command, c));\r\n return {\r\n next: inner.length > 0 ? { matcher: entry.matcher ?? \"\", hooks: inner } : null,\r\n removed: innerBefore - inner.length,\r\n };\r\n },\r\n skipDetail: (event) => `hooks.${event} already registered`,\r\n removeDetail: (event, removed) => `hooks.${event} (${removed})`,\r\n absentDetail: \"no hooks section present\",\r\n noMatchDetail: \"no matching hook entries\",\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 // droids}. No runtime dispatch, no home-bin pointer, no telemetry wrap. Each\r\n // method is idempotent (byte-identical → skip) via writeContentFile and\r\n // reversible via removeContentFile. Honors platforms[\"droid\"] per-surface\r\n // false to skip. Both user and project scope (getConfigDir resolves either).\r\n //\r\n // Native locations (live-confirmed Factory dirs):\r\n // command → <configDir>/commands/<name>.md md+frontmatter(description, argument-hint)\r\n // skill → <configDir>/skills/<name>/SKILL.md (+ resources)\r\n // subagent → <configDir>/droids/<name>.md MARKDOWN — folder is droids/, NOT agents/\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.getConfigDir(ctx), \"commands\", `${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.getConfigDir(ctx), \"skills\", name);\r\n }\r\n /** Native subagent file path: <configDir>/droids/<name>.md (folder droids/, NOT agents/). */\r\n private subagentPath(ctx: InstallContext, name: string): string {\r\n return join(this.getConfigDir(ctx), \"droids\", `${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 droid\" }];\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 renderCommandMd(cmd, { includeToolsAndModel: false }),\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 // ── 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 droid\" }];\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\"), this.renderSkill(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 /**\r\n * Render a skill's SKILL.md: frontmatter (name, description + optional\r\n * disable-model-invocation) + body. Droid documents disable-model-invocation\r\n * as a skill field; it has NO model/allowed-tools skill field, so those are\r\n * never emitted (unlike the uniform claude/codex renderer).\r\n */\r\n private renderSkill(skill: SkillDef): string {\r\n const frontmatter: Record<string, unknown> = {\r\n name: skill.name,\r\n description: skill.description,\r\n };\r\n if (skill.disableModelInvocation !== undefined) {\r\n frontmatter[\"disable-model-invocation\"] = skill.disableModelInvocation;\r\n }\r\n if (skill.extra) Object.assign(frontmatter, skill.extra);\r\n return this.renderFrontmatterMd(frontmatter, skill.body);\r\n }\r\n\r\n // ── Subagents (MARKDOWN — folder droids/, NOT agents/) ────────────────────\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 droid\" }];\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 /**\r\n * Render a Droid subagent to a MARKDOWN file (NOT TOML — codex emits TOML,\r\n * which is wrong for droid): YAML frontmatter { name, description?, model? }\r\n * then the prompt as the markdown body.\r\n */\r\n private renderSubagent(agent: SubagentDef): string {\r\n const frontmatter: Record<string, unknown> = { name: agent.name };\r\n if (agent.description !== undefined) frontmatter.description = agent.description;\r\n if (agent.model !== undefined) frontmatter.model = agent.model;\r\n if (agent.extra) Object.assign(frontmatter, agent.extra);\r\n return this.renderFrontmatterMd(frontmatter, agent.prompt);\r\n }\r\n\r\n // ── Action surface (executable command file, NO .md extension) ────────────\r\n // Each action emits an EXECUTABLE script at <configDir>/commands/<id> (no\r\n // extension) whose shebang lets Droid's CLI exec it; it execs the home-bin\r\n // action verb and forwards \"$@\". Invoked via `/<id>`, stdout → transcript\r\n // (docs.factory.ai/cli/configuration/custom-slash-commands). chmod 0o755.\r\n //\r\n // COLLISION NOTE: the COMMAND surface writes commands/<name>.md; actions write\r\n // commands/<id> (no extension) — different filenames, both register `/<name>`.\r\n // A connector reusing one id for a command AND an action is the author's call.\r\n //\r\n // CROSS-OS: the shebang exec-file is POSIX. On win32 Droid's exec-file\r\n // interpreter resolution is UNVERIFIED, so we skip-warn there and write nothing\r\n // (honest degradation, like other Windows caveats in the codebase).\r\n\r\n /** Native action file path: <configDir>/commands/<id> (NO .md extension). */\r\n private actionPath(ctx: InstallContext, id: string): string {\r\n return join(this.getConfigDir(ctx), \"commands\", id);\r\n }\r\n\r\n override installActions(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.actions === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"actions disabled for droid\" }];\r\n }\r\n const triggers = this.actionTriggers(ctx);\r\n if (triggers.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no actions\" }];\r\n }\r\n if (process.platform === \"win32\") {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"warn\",\r\n detail: `droid action exec-files are unverified on Windows; ${triggers.length} skipped`,\r\n },\r\n ];\r\n }\r\n return triggers.map((trigger) => {\r\n const path = this.actionPath(ctx, trigger.id);\r\n return this.writeManagedFile(path, this.renderActionScript(trigger.command), ctx.dryRun, undefined, true);\r\n });\r\n }\r\n\r\n override uninstallActions(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.actions.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no actions\" }];\r\n }\r\n return connector.actions.map((action) =>\r\n this.removeContentFile(this.actionPath(ctx, action.id), ctx.dryRun),\r\n );\r\n }\r\n\r\n /** Render the POSIX exec-file: shebang + `exec <action verb> \"$@\"`. */\r\n private renderActionScript(command: string): string {\r\n return `#!/usr/bin/env sh\\nexec ${command} \"$@\"\\n`;\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 hookPath = this.getHookConfigPath(ctx);\r\n const connectorId = ctx.connector.id;\r\n const homeBin = ctx.homeBinPath;\r\n const hookEvents = ctx.connector.hookEvents;\r\n return [\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}: server entry registered`,\r\n check: () => {\r\n if (!ctx.connector.server) {\r\n return { status: \"OK\", detail: \"no MCP server declared\" };\r\n }\r\n const cfg = this.readJson<{ [k: string]: Record<string, unknown> }>(mcpPath);\r\n const bucket = cfg?.[MCP_ROOT_KEY];\r\n if (!cfg || !bucket) {\r\n return { status: \"FAIL\", detail: `no ${MCP_ROOT_KEY} in ${mcpPath}` };\r\n }\r\n return connectorId in bucket\r\n ? { status: \"OK\", detail: `${MCP_ROOT_KEY}.${connectorId} present` }\r\n : {\r\n status: \"FAIL\",\r\n detail: `no ${MCP_ROOT_KEY}.${connectorId} in ${mcpPath}`,\r\n };\r\n },\r\n },\r\n {\r\n name: `${this.name}: hook command registered`,\r\n check: () => {\r\n if (hookEvents.length === 0) {\r\n return { status: \"OK\", detail: \"no hooks declared\" };\r\n }\r\n const file = this.readJson<DroidHooksFile>(hookPath);\r\n if (!file) return { status: \"FAIL\", detail: `cannot read ${hookPath}` };\r\n const hooks = file.hooks ?? {};\r\n const registered = Object.values(hooks).some((entries) =>\r\n (entries ?? []).some((e) =>\r\n (e.hooks ?? []).some((h) =>\r\n isHomeBinHookCommand(h.command, homeBin, connectorId),\r\n ),\r\n ),\r\n );\r\n return registered\r\n ? { status: \"OK\", detail: \"hook command present\" }\r\n : { status: \"FAIL\", detail: `no hook for ${connectorId} in ${hookPath}` };\r\n },\r\n },\r\n ];\r\n }\r\n\r\n // ── Runtime: parse Droid stdin JSON → normalized event ────────────────────\r\n\r\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\r\n const input = (raw ?? {}) as DroidWireInput;\r\n const connectorId = typeof input.connector === \"string\" ? input.connector : \"\";\r\n const sessionId = typeof input.session_id === \"string\" ? input.session_id : \"\";\r\n const projectDir = typeof input.cwd === \"string\" ? input.cwd : undefined;\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 ev: PostToolUseEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n ...(toolResponseToString(input.tool_response) !== undefined\r\n ? { toolOutput: toolResponseToString(input.tool_response) }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"UserPromptSubmit\": {\r\n const ev: UserPromptSubmitEvent = {\r\n ...base,\r\n prompt: typeof input.prompt === \"string\" ? input.prompt : \"\",\r\n };\r\n return ev;\r\n }\r\n case \"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 \"SubagentStop\": {\r\n // Droid sends ONLY the documented Stop/SubagentStop wire:\r\n // { session_id, transcript_path, cwd, permission_mode, hook_event_name,\r\n // stop_hook_active }. It emits NO agent_id / agent_type /\r\n // agent_transcript_path / last_assistant_message — reading those was a\r\n // false-friend (they never populated). The subagent's final response is\r\n // reachable only through transcript_path, which rides on base.raw.\r\n // Primary source: docs.factory.ai/reference/hooks-reference\r\n // (Stop and SubagentStop Input).\r\n const ev: SubagentStopEvent = {\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 \"Notification\": {\r\n // Droid Notification: { …, hook_event_name:\"Notification\", message }\r\n // (docs.factory.ai/reference/hooks-reference). Only `message` maps.\r\n const ev: NotificationEvent = {\r\n ...base,\r\n message: typeof input.message === \"string\" ? input.message : \"\",\r\n };\r\n return ev;\r\n }\r\n case \"PreCompact\": {\r\n // Droid PreCompact: { …, trigger:\"manual\"|\"auto\", custom_instructions }.\r\n // Only the documented `trigger` enum maps; custom_instructions rides on\r\n // base.raw (no normalized field).\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 \"SessionStart\": {\r\n // Droid SessionStart: { …, source:\"startup\"|\"resume\"|\"clear\"|\"compact\" }.\r\n // Coerce the documented matcher source onto the normalized enum.\r\n const ev: SessionStartEvent = {\r\n ...base,\r\n source: normalizeSessionSource(input.source),\r\n };\r\n return ev;\r\n }\r\n case \"SessionEnd\": {\r\n // Droid SessionEnd: { …, reason:\"clear\"|\"logout\"|\"prompt_input_exit\"|\r\n // \"other\" }. Only the documented `reason` maps.\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 default: {\r\n // Droid never delivers PermissionRequest / PostToolUseFailure /\r\n // SubagentStart (no native equivalent — see SUPPORTED_EVENTS). If the\r\n // runtime dispatches one anyway, surface it loudly rather than silently\r\n // mis-parse.\r\n throw new Error(`unsupported droid hook event: ${String(event)}`);\r\n }\r\n }\r\n }\r\n\r\n // ── Runtime: normalized response → Droid native (Claude-shaped) hook reply ─\r\n\r\n formatReply(event: HookEventName, response: HookResponse): HookReply {\r\n const hookEventName = event;\r\n const decision = response.decision ?? \"allow\";\r\n\r\n // Notification / PreCompact / SessionEnd are observe-only: per\r\n // docs.factory.ai/reference/hooks-reference their Decision Control is \"N/A\"\r\n // (cannot block) and they carry no context-injection surface, so every\r\n // decision is an unconditional no-op passthrough (mirrors Stop). SessionStart\r\n // is deliberately NOT here — it honors hookSpecificOutput.additionalContext,\r\n // so it falls through to the generic `context` branch below.\r\n if (event === \"Notification\" || event === \"PreCompact\" || event === \"SessionEnd\") {\r\n return { exitCode: 0 };\r\n }\r\n\r\n // deny → block the action with a reason (exit 0; JSON carries the decision).\r\n if (decision === \"deny\") {\r\n // SubagentStop deny carries Stop semantics — it keeps the subagent\r\n // running with `reason` as its next instruction — and (Claude-compatible)\r\n // is honored only as the TOP-LEVEL {\"decision\":\"block\",\"reason\"}, not as\r\n // a permissionDecision envelope.\r\n if (event === \"SubagentStop\") {\r\n return this.stdout({\r\n decision: \"block\",\r\n reason: response.reason ?? \"Blocked by hook\",\r\n });\r\n }\r\n return this.stdout({\r\n hookSpecificOutput: {\r\n hookEventName,\r\n permissionDecision: \"deny\",\r\n permissionDecisionReason: response.reason ?? \"Blocked by hook\",\r\n },\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 hookSpecificOutput: {\r\n hookEventName,\r\n permissionDecision: \"ask\",\r\n permissionDecisionReason: response.reason ?? \"Confirmation required by hook\",\r\n },\r\n });\r\n }\r\n\r\n // context → inject soft guidance (Droid honors additionalContext).\r\n if (decision === \"context\" && response.additionalContext) {\r\n return this.stdout({\r\n hookSpecificOutput: { hookEventName, additionalContext: response.additionalContext },\r\n });\r\n }\r\n\r\n // allow / modify (unsupported — exit-code/decision protocol only) / void →\r\n // pass through with exit 0.\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/** Coerce a Droid PostToolUse `tool_response` into a string for the normalized event. */\r\nfunction toolResponseToString(value: unknown): string | undefined {\r\n if (value == null) return undefined;\r\n if (typeof value === \"string\") return value;\r\n try {\r\n return JSON.stringify(value);\r\n } catch {\r\n return String(value);\r\n }\r\n}\r\n\r\nexport const adapter = new DroidAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":["entry"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/usage/readers/droid.ts"],"sourcesContent":["/**\r\n * usage/readers/droid — Droid (Factory.ai) native session-settings reader.\r\n *\r\n * Faithful port of tokscale sessions/droid.rs. Reads\r\n * ~/.factory/sessions/*.settings.json. Each settings file is a single JSON\r\n * object carrying a cumulative `tokenUsage` block for one session:\r\n * tokenUsage.inputTokens → input\r\n * tokenUsage.outputTokens → output\r\n * tokenUsage.cacheReadTokens → cacheRead\r\n * tokenUsage.cacheCreationTokens → cacheWrite\r\n * tokenUsage.thinkingTokens → reasoning\r\n * A file with no tokenUsage, or whose five dimensions sum to 0, yields no row.\r\n *\r\n * Model id comes from the `model` field run through Droid's custom normalization\r\n * (strip a `custom:` prefix, remove [bracket] groups, trim trailing hyphens,\r\n * lowercase, dots→hyphens, collapse repeated hyphens — e.g.\r\n * \"custom:Claude-Opus-4.5-Thinking-[Anthropic]-0\" → \"claude-opus-4-5-thinking-0\").\r\n * When `model` is absent it falls back to scanning the sibling .jsonl\r\n * (<id>.settings.json → <id>.jsonl) for a `Model:` system-reminder pattern, and\r\n * finally to a provider-based default (anthropic→claude-unknown, openai→gpt-unknown,\r\n * google→gemini-unknown, xai→grok-unknown, else <provider>-unknown).\r\n *\r\n * Provider prefers the `providerLock` field; otherwise it is inferred from the\r\n * model (defaulting to \"unknown\", matching the Rust `unwrap_or(\"unknown\")`).\r\n *\r\n * Session id is the filename stem with `.settings` stripped\r\n * (uuid.settings.json → uuid). Timestamp prefers `providerLockTimestamp`\r\n * (RFC3339 → epoch ms), falling back to the file mtime; a row with no usable\r\n * timestamp (0) is dropped — to match the Rust mtime-or-0 path, an unreadable\r\n * mtime is treated as 0 (not \"now\"), so we do not synthesize a timestamp here.\r\n *\r\n * No dedup (the session id is the primary key) and no project attribution\r\n * (Droid settings carry neither). Confidence is \"host-reported\".\r\n *\r\n * Fail-open: no root → []; unreadable/malformed file → skipped.\r\n */\r\n\r\nimport { statSync } from \"node:fs\";\r\nimport { basename } from \"node:path\";\r\n\r\nimport type { UsageReader, UsageRecord } from \"../types.js\";\r\nimport { emptyTokens } from \"../aggregate.js\";\r\nimport { readJsonFile, readJsonlLines } from \"../jsonl.js\";\r\nimport { inferProvider } from \"../normalize.js\";\r\nimport { firstExistingRoot, walkFiles } from \"../paths.js\";\r\n\r\nconst PLATFORM_ID = \"droid\" as const;\r\nconst DEFAULT_PROVIDER = \"unknown\";\r\n/** Cap matching the Rust scan (avoids slurping a huge sibling .jsonl). */\r\nconst JSONL_MODEL_SCAN_LIMIT = 500;\r\n\r\n/** The fields we read off a Droid settings.json file (all optional / unknown). */\r\ninterface DroidSettings {\r\n model?: unknown;\r\n providerLock?: unknown;\r\n providerLockTimestamp?: unknown;\r\n tokenUsage?: {\r\n inputTokens?: unknown;\r\n outputTokens?: unknown;\r\n cacheCreationTokens?: unknown;\r\n cacheReadTokens?: unknown;\r\n thinkingTokens?: unknown;\r\n };\r\n}\r\n\r\n/** Coerce an unknown to a non-negative integer (0 on absence/garbage). */\r\nfunction toNonNegInt(v: unknown): number {\r\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\r\n if (!Number.isFinite(n)) return 0;\r\n return Math.max(0, Math.trunc(n));\r\n}\r\n\r\n/**\r\n * Normalize a model name from Droid's custom format (port of\r\n * droid.rs normalize_model_name). Order is load-bearing:\r\n * 1. strip a leading `custom:` prefix;\r\n * 2. remove every `[...]` bracket group (vendor tags like \"[Anthropic]\");\r\n * 3. trim trailing hyphens (NOT trailing digits — those are kept);\r\n * 4. lowercase;\r\n * 5. replace dots with hyphens;\r\n * 6. collapse runs of hyphens into one.\r\n */\r\nfunction normalizeModelName(model: string): string {\r\n let normalized = model.startsWith(\"custom:\") ? model.slice(\"custom:\".length) : model;\r\n\r\n // Remove [anything] groups (mirrors /\\[.*?\\]/g — bracket-depth-agnostic, just\r\n // drops chars while inside a bracket span).\r\n let stripped = \"\";\r\n let inBracket = false;\r\n for (const ch of normalized) {\r\n if (ch === \"[\") inBracket = true;\r\n else if (ch === \"]\") inBracket = false;\r\n else if (!inBracket) stripped += ch;\r\n }\r\n normalized = stripped;\r\n\r\n // Trim trailing hyphens only (mirrors /-+$/), keep trailing digits.\r\n normalized = normalized.replace(/-+$/, \"\");\r\n\r\n normalized = normalized.toLowerCase();\r\n normalized = normalized.replace(/\\./g, \"-\");\r\n\r\n // Collapse consecutive hyphens (mirrors /-+/g → \"-\").\r\n normalized = normalized.replace(/-+/g, \"-\");\r\n\r\n return normalized;\r\n}\r\n\r\n/**\r\n * Canonicalize a provider segment (port of the subset of\r\n * provider_identity.rs canonicalize_provider_segment needed by\r\n * get_default_model_from_provider). Returns the input lowercased/`-`→`_` when no\r\n * canonical alias applies, so the default-model switch can match cleanly.\r\n */\r\nfunction canonicalizeProviderSegment(segment: string): string | undefined {\r\n const normalized = segment.trim().replace(/\\/+$/, \"\").toLowerCase().replace(/-/g, \"_\");\r\n if (normalized.startsWith(\"<\") && normalized.endsWith(\">\")) return undefined;\r\n switch (normalized) {\r\n case \"\":\r\n case \"unknown\":\r\n return undefined;\r\n case \"x_ai\":\r\n case \"xai\":\r\n return \"xai\";\r\n case \"z_ai\":\r\n case \"zai\":\r\n return \"zai\";\r\n case \"moonshot\":\r\n case \"moonshotai\":\r\n return \"moonshotai\";\r\n case \"meta\":\r\n case \"meta_llama\":\r\n return \"meta_llama\";\r\n case \"azure\":\r\n case \"azure_ai\":\r\n return \"azure_ai\";\r\n case \"anthropic\":\r\n case \"vertex\":\r\n case \"vertex_ai\":\r\n return \"anthropic\";\r\n case \"together\":\r\n case \"together_ai\":\r\n return \"together_ai\";\r\n case \"fireworks\":\r\n case \"fireworks_ai\":\r\n return \"fireworks_ai\";\r\n case \"google\":\r\n case \"gemini\":\r\n return \"google\";\r\n case \"openai\":\r\n case \"openai_codex\":\r\n return \"openai\";\r\n case \"minimax\":\r\n case \"minimaxai\":\r\n case \"minimax_ai\":\r\n return \"minimax\";\r\n case \"mistral\":\r\n case \"mistralai\":\r\n return \"mistralai\";\r\n case \"ai21\":\r\n return \"ai21\";\r\n default:\r\n // Reject segments containing digits (model-name fragments, not providers).\r\n if (/[0-9]/.test(normalized)) return undefined;\r\n return normalized;\r\n }\r\n}\r\n\r\n/** Port of canonical_provider: first non-empty canonical tag, or the raw fallback. */\r\nfunction canonicalProvider(raw: string): string {\r\n for (const segment of raw.trim().replace(/\\/+$/, \"\").split(\"/\")) {\r\n const tag = canonicalizeProviderSegment(segment);\r\n if (tag !== undefined) return tag;\r\n if (segment.includes(\".\")) {\r\n for (const dotted of segment.split(\".\")) {\r\n const dottedTag = canonicalizeProviderSegment(dotted);\r\n if (dottedTag !== undefined) return dottedTag;\r\n }\r\n }\r\n }\r\n return raw;\r\n}\r\n\r\n/**\r\n * Provider-based default model when no model id is available (port of\r\n * get_default_model_from_provider).\r\n */\r\nfunction defaultModelFromProvider(provider: string): string {\r\n switch (canonicalProvider(provider)) {\r\n case \"anthropic\":\r\n return \"claude-unknown\";\r\n case \"openai\":\r\n return \"gpt-unknown\";\r\n case \"google\":\r\n return \"gemini-unknown\";\r\n case \"xai\":\r\n return \"grok-unknown\";\r\n default:\r\n return `${provider}-unknown`;\r\n }\r\n}\r\n\r\n/**\r\n * Try to extract a model name from the sibling .jsonl by scanning early lines\r\n * for a `Model:` system-reminder (port of extract_model_from_jsonl). The value\r\n * runs from after `Model:` up to the first `[`, `\\`, or `\"`, trimmed; the result\r\n * is run through normalizeModelName. Returns undefined when nothing matches.\r\n */\r\nfunction extractModelFromJsonl(jsonlPath: string): string | undefined {\r\n // readJsonlLines is fail-open ([] on a missing/unreadable file); we scan the\r\n // raw text of each parsed line's stringification is not what Rust does — Rust\r\n // scans raw file lines, so we read the lines as-is via the tolerant reader and\r\n // search their original text. Since readJsonlLines parses JSON, re-serialize is\r\n // lossy; instead search the parsed objects' stringified form, which preserves\r\n // the embedded \"Model:\" text that lives inside string fields.\r\n const lines = readJsonlLines(jsonlPath);\r\n const scan = lines.slice(0, JSONL_MODEL_SCAN_LIMIT);\r\n for (const obj of scan) {\r\n const text = typeof obj === \"string\" ? obj : JSON.stringify(obj);\r\n const pos = text.indexOf(\"Model:\");\r\n if (pos < 0) continue;\r\n const after = text.slice(pos + \"Model:\".length);\r\n let modelPart = \"\";\r\n for (const ch of after) {\r\n if (ch === \"[\" || ch === \"\\\\\" || ch === '\"') break;\r\n modelPart += ch;\r\n }\r\n const trimmed = modelPart.trim();\r\n if (trimmed !== \"\") return normalizeModelName(trimmed);\r\n }\r\n return undefined;\r\n}\r\n\r\n/** File mtime in epoch ms, or 0 when unreadable (matches the Rust mtime-or-0 path). */\r\nfunction fileMtimeOrZero(path: string): number {\r\n try {\r\n return Math.floor(statSync(path).mtimeMs);\r\n } catch {\r\n return 0;\r\n }\r\n}\r\n\r\n/** Session id from a settings filename: stem with a trailing `.settings` removed. */\r\nfunction sessionIdFromPath(path: string): string {\r\n const name = basename(path);\r\n // Drop a final extension (the `.json`), then strip `.settings`.\r\n const dot = name.lastIndexOf(\".\");\r\n const stem = dot > 0 ? name.slice(0, dot) : name;\r\n const id = stem.replace(/\\.settings$/, \"\");\r\n return id === \"\" ? \"unknown\" : id;\r\n}\r\n\r\n/** Parse one Droid settings.json file into usage records (port of parse_droid_file). */\r\nfunction parseDroidFile(path: string): UsageRecord[] {\r\n const raw = readJsonFile(path);\r\n if (typeof raw !== \"object\" || raw === null) return [];\r\n const settings = raw as DroidSettings;\r\n\r\n const usage = settings.tokenUsage;\r\n if (usage === undefined || usage === null || typeof usage !== \"object\") return [];\r\n\r\n const input = toNonNegInt(usage.inputTokens);\r\n const output = toNonNegInt(usage.outputTokens);\r\n const cacheWrite = toNonNegInt(usage.cacheCreationTokens);\r\n const cacheRead = toNonNegInt(usage.cacheReadTokens);\r\n const reasoning = toNonNegInt(usage.thinkingTokens);\r\n\r\n // Skip sessions with no usage at all.\r\n if (input + output + cacheWrite + cacheRead + reasoning === 0) return [];\r\n\r\n const sessionId = sessionIdFromPath(path);\r\n\r\n // Provider: providerLock if a non-empty string, else inferred from the model\r\n // (defaulting to \"unknown\" — matches the Rust unwrap_or(\"unknown\")).\r\n const providerLock =\r\n typeof settings.providerLock === \"string\" && settings.providerLock !== \"\"\r\n ? settings.providerLock\r\n : undefined;\r\n const modelField = typeof settings.model === \"string\" ? settings.model : undefined;\r\n const providerId =\r\n providerLock ?? inferProvider(modelField ?? \"\") ?? DEFAULT_PROVIDER;\r\n\r\n // Model: normalize the model field, else scan the sibling .jsonl, else default.\r\n let modelId: string;\r\n if (modelField !== undefined) {\r\n modelId = normalizeModelName(modelField);\r\n } else {\r\n const jsonlPath = path.replace(/\\.settings\\.json$/, \".jsonl\");\r\n modelId = jsonlPath !== path\r\n ? extractModelFromJsonl(jsonlPath) ?? defaultModelFromProvider(providerId)\r\n : defaultModelFromProvider(providerId);\r\n }\r\n\r\n // Timestamp: providerLockTimestamp (RFC3339 → ms) else file mtime; 0 → drop.\r\n let ts = 0;\r\n if (typeof settings.providerLockTimestamp === \"string\") {\r\n const parsed = Date.parse(settings.providerLockTimestamp);\r\n if (!Number.isNaN(parsed)) ts = parsed;\r\n }\r\n if (ts === 0) ts = fileMtimeOrZero(path);\r\n if (ts === 0) return [];\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 = cacheWrite;\r\n tokens.reasoning = reasoning;\r\n\r\n return [\r\n {\r\n platformId: PLATFORM_ID,\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}\r\n\r\n/** The Droid (Factory.ai) usage reader singleton. */\r\nconst droidReader: UsageReader = {\r\n platformId: PLATFORM_ID,\r\n kind: \"local\",\r\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\r\n const root = firstExistingRoot(PLATFORM_ID);\r\n if (root === undefined) return []; // no ~/.factory/sessions → fail-open\r\n\r\n // ~/.factory/sessions/*.settings.json\r\n const files = walkFiles(root, (name) => name.endsWith(\".settings.json\"));\r\n\r\n const records: UsageRecord[] = [];\r\n for (const file of files) {\r\n const rows = parseDroidFile(file);\r\n for (const row of rows) {\r\n if (sinceMs !== undefined && row.ts < sinceMs) continue;\r\n records.push(row);\r\n }\r\n }\r\n return records;\r\n },\r\n};\r\n\r\nexport default droidReader;\r\n"],"mappings":"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","names":[]}
|