@ken-jo/agent-connector 0.4.98 → 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-5JM23CFJ.js → action-5OEM3QC4.js} +17 -16
- package/dist/action-5OEM3QC4.js.map +1 -0
- package/dist/{amazon-q-LFZNGN27.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-S22XTPEQ.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-PN474FXM.js → antigravity-cli-JHQ7JGNX.js} +10 -9
- package/dist/antigravity-cli-JHQ7JGNX.js.map +1 -0
- package/dist/{audit-2H7E3Z4N.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-B6PKHS5I.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-RPWD5H6Y.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-KWC5DQ5Z.js → chunk-6WN37NVN.js} +113 -10
- package/dist/chunk-6WN37NVN.js.map +1 -0
- package/dist/{chunk-DWZ5IWF4.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-HATCOSQT.js → chunk-BV5WK6VQ.js} +19 -19
- package/dist/chunk-BV5WK6VQ.js.map +1 -0
- package/dist/{chunk-KTBAB556.js → chunk-BXPSDNIB.js} +2 -2
- 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-6XWORQM6.js → chunk-FKW5A6HE.js} +23 -7
- package/dist/chunk-FKW5A6HE.js.map +1 -0
- package/dist/{chunk-TOSNS2BY.js → chunk-G5HAIYRM.js} +43 -43
- package/dist/chunk-G5HAIYRM.js.map +1 -0
- package/dist/{chunk-RNVIK5YJ.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-UNU2KPYJ.js → chunk-JTN3AKZO.js} +23 -15
- 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-S25MVWBP.js → chunk-KLZBRNFP.js} +12 -12
- package/dist/chunk-KLZBRNFP.js.map +1 -0
- package/dist/{chunk-DOPFBUCX.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-LZL2YITN.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-LD5HAN6O.js → chunk-PWCBWAM4.js} +6 -4
- package/dist/chunk-PWCBWAM4.js.map +1 -0
- package/dist/{chunk-W7AGWAMH.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-7VSZXOXJ.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-FJ6F5U3W.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-TA53454O.js → claude-code-LJ6DVXWQ.js} +22 -12
- 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-GZFN32BM.js → cline-JAPJNOZQ.js} +6 -6
- package/dist/cline-JAPJNOZQ.js.map +1 -0
- package/dist/{codebuddy-IYS3TQZ3.js → codebuddy-GC7VAE2Y.js} +7 -7
- package/dist/codebuddy-GC7VAE2Y.js.map +1 -0
- package/dist/{codebuff-D3MIKJHC.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-ZKLP5IXZ.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-I34FNSTJ.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-NWPM5GE6.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-MDGPZSZU.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-B7tLHVnx.d.ts → define-connector-odRHEC4H.d.ts} +1 -1
- package/dist/{detect-FKJC36P4.js → detect-ALP4G7WG.js} +4 -4
- package/dist/detect-ALP4G7WG.js.map +1 -0
- package/dist/{devin-TZMRC57O.js → devin-WPDN3MXE.js} +7 -7
- package/dist/devin-WPDN3MXE.js.map +1 -0
- package/dist/{doctor-PGVYUQYG.js → doctor-YEQWCWZJ.js} +27 -26
- package/dist/doctor-YEQWCWZJ.js.map +1 -0
- package/dist/{droid-SCFWDJR4.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-MLDGOR2L.js → gemini-cli-VJ6X5DNF.js} +8 -8
- package/dist/gemini-cli-VJ6X5DNF.js.map +1 -0
- package/dist/{goose-5DBHDSJW.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-LX6Q6RUY.js → grok-cli-4VFVF2CQ.js} +7 -7
- package/dist/grok-cli-4VFVF2CQ.js.map +1 -0
- package/dist/{hermes-LJB3SJKB.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-PVGBQUQI.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-HPDIK7F6.js → install-23WRRFBY.js} +21 -21
- package/dist/install-23WRRFBY.js.map +1 -0
- package/dist/{introspect-DWCu0Krk.d.ts → introspect-gTx6GzpW.d.ts} +1 -1
- package/dist/{jetbrains-copilot-2DSVAJMJ.js → jetbrains-copilot-XBO2JZRO.js} +6 -6
- package/dist/jetbrains-copilot-XBO2JZRO.js.map +1 -0
- package/dist/{junie-GUBDRADP.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-HRLWMENX.js → kilo-T4OXNUYY.js} +8 -8
- package/dist/kilo-T4OXNUYY.js.map +1 -0
- package/dist/{kilo-cli-IQMFMYZF.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-EPD46UJK.js → kimi-VLSUFLYV.js} +7 -7
- package/dist/kimi-VLSUFLYV.js.map +1 -0
- package/dist/{kiro-A7DL52FX.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-KCT7XPCI.js → leaderboard-YBD3IKC2.js} +7 -7
- package/dist/leaderboard-YBD3IKC2.js.map +1 -0
- package/dist/{mimo-code-NDGLBXNC.js → mimo-code-KETL7W7D.js} +7 -7
- package/dist/mimo-code-KETL7W7D.js.map +1 -0
- package/dist/{mistral-vibe-LLWASF5X.js → mistral-vibe-3AU7N2CA.js} +6 -6
- package/dist/mistral-vibe-3AU7N2CA.js.map +1 -0
- package/dist/{mux-ON4QK3OC.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-45R5MFBF.js → nemoclaw-WA7RSKF2.js} +7 -7
- package/dist/nemoclaw-WA7RSKF2.js.map +1 -0
- package/dist/{omp-ROCTFV3A.js → omp-UDYJ2PYY.js} +9 -7
- package/dist/omp-UDYJ2PYY.js.map +1 -0
- package/dist/{open-interpreter-V6CN542J.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-YYWQ45LF.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-OFFMWCQW.js → openhands-QGXMQR6I.js} +7 -7
- package/dist/openhands-QGXMQR6I.js.map +1 -0
- package/dist/{package-TALOSRQ4.js → package-TAUTPP7Z.js} +15 -15
- package/dist/package-TAUTPP7Z.js.map +1 -0
- package/dist/{pi-4W74ETTI.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-JOBFNEVT.js → qwen-code-DNQTGJOF.js} +28 -10
- 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-RHIGJZNY.js → roo-code-VI2ZKTZC.js} +6 -6
- package/dist/roo-code-VI2ZKTZC.js.map +1 -0
- package/dist/runtime/index.d.ts +1 -1
- 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-4TEHK726.js → serve-6MDLRQS2.js} +17 -16
- package/dist/serve-6MDLRQS2.js.map +1 -0
- package/dist/{status-DVD4EHMX.js → status-IUP75XHP.js} +11 -11
- package/dist/status-IUP75XHP.js.map +1 -0
- package/dist/{statusline-ZVNH7XBK.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-NWUJPIK3.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-TLAFSQRQ.js → trae-VOJXUYRN.js} +6 -6
- package/dist/trae-VOJXUYRN.js.map +1 -0
- package/dist/{types-C6j18a6R.d.ts → types-DWXnnx-N.d.ts} +83 -8
- package/dist/{uninstall-DZVFV7KE.js → uninstall-BSIJ4QXN.js} +21 -21
- package/dist/uninstall-BSIJ4QXN.js.map +1 -0
- package/dist/{upgrade-OUE4ED52.js → upgrade-ZSICU3NE.js} +21 -21
- package/dist/upgrade-ZSICU3NE.js.map +1 -0
- package/dist/{usage-NT3ZXALZ.js → usage-EQUNVSCT.js} +4 -4
- package/dist/usage-EQUNVSCT.js.map +1 -0
- package/dist/{usage-event-IELZE6WO.js → usage-event-J5CQ6LNT.js} +16 -15
- package/dist/usage-event-J5CQ6LNT.js.map +1 -0
- package/dist/{vscode-copilot-VZ5XYSTK.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-P2HWXIKT.js → warp-S444B2S3.js} +9 -7
- package/dist/warp-S444B2S3.js.map +1 -0
- package/dist/{windsurf-3JC73GZK.js → windsurf-R6D2JUTN.js} +6 -6
- package/dist/windsurf-R6D2JUTN.js.map +1 -0
- package/dist/{zed-6CMUKXLI.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-5JM23CFJ.js.map +0 -1
- package/dist/amazon-q-LFZNGN27.js.map +0 -1
- package/dist/amp-CEWOSWQO.js.map +0 -1
- package/dist/amp-S22XTPEQ.js.map +0 -1
- package/dist/antigravity-LHFEFNMC.js.map +0 -1
- package/dist/antigravity-PZXYAO3X.js +0 -18
- package/dist/antigravity-cli-MOKFSWOU.js.map +0 -1
- package/dist/antigravity-cli-PN474FXM.js.map +0 -1
- package/dist/chunk-4GH2KA7V.js.map +0 -1
- package/dist/chunk-6XWORQM6.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-7VSZXOXJ.js.map +0 -1
- package/dist/chunk-B6PKHS5I.js.map +0 -1
- package/dist/chunk-C747DYNT.js.map +0 -1
- package/dist/chunk-DOPFBUCX.js.map +0 -1
- package/dist/chunk-DWZ5IWF4.js.map +0 -1
- package/dist/chunk-FJ6F5U3W.js.map +0 -1
- package/dist/chunk-HATCOSQT.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-KTBAB556.js.map +0 -1
- package/dist/chunk-KWC5DQ5Z.js.map +0 -1
- package/dist/chunk-LD5HAN6O.js.map +0 -1
- package/dist/chunk-LFWYYWLV.js.map +0 -1
- package/dist/chunk-LZL2YITN.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-RNVIK5YJ.js.map +0 -1
- package/dist/chunk-RPWD5H6Y.js.map +0 -1
- package/dist/chunk-S25MVWBP.js.map +0 -1
- package/dist/chunk-THI4PI4H.js.map +0 -1
- package/dist/chunk-TOSNS2BY.js.map +0 -1
- package/dist/chunk-UNU2KPYJ.js.map +0 -1
- package/dist/chunk-UZZTWWGG.js.map +0 -1
- package/dist/chunk-W7AGWAMH.js.map +0 -1
- package/dist/chunk-XN7S44U4.js.map +0 -1
- package/dist/chunk-XXLCMSD3.js.map +0 -1
- package/dist/chunk-XZWMJLIT.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-TA53454O.js.map +0 -1
- package/dist/cline-GZFN32BM.js.map +0 -1
- package/dist/codebuddy-IYS3TQZ3.js.map +0 -1
- package/dist/codebuff-6KWBW6CT.js.map +0 -1
- package/dist/codebuff-D3MIKJHC.js.map +0 -1
- package/dist/codex-DKBAAUCO.js.map +0 -1
- package/dist/codex-ZKLP5IXZ.js.map +0 -1
- package/dist/continue-I34FNSTJ.js.map +0 -1
- package/dist/copilot-cli-CLBJACQ5.js.map +0 -1
- package/dist/copilot-cli-NWPM5GE6.js.map +0 -1
- package/dist/crush-A5C5USRL.js.map +0 -1
- package/dist/crush-MDGPZSZU.js.map +0 -1
- package/dist/cursor-D5SWDVSW.js.map +0 -1
- package/dist/cursor-JRVK56QA.js +0 -22
- package/dist/detect-FKJC36P4.js.map +0 -1
- package/dist/devin-TZMRC57O.js.map +0 -1
- package/dist/doctor-PGVYUQYG.js.map +0 -1
- package/dist/droid-PFQ2PZSF.js.map +0 -1
- package/dist/droid-SCFWDJR4.js.map +0 -1
- package/dist/gemini-cli-MLDGOR2L.js.map +0 -1
- package/dist/gemini-cli-Y6RJMU24.js.map +0 -1
- package/dist/goose-5DBHDSJW.js.map +0 -1
- package/dist/goose-K2HKUTBK.js.map +0 -1
- package/dist/grok-cli-LX6Q6RUY.js.map +0 -1
- package/dist/hermes-LJB3SJKB.js.map +0 -1
- package/dist/hermes-MF3NX4ZY.js.map +0 -1
- package/dist/hook-PVGBQUQI.js.map +0 -1
- package/dist/install-HPDIK7F6.js.map +0 -1
- package/dist/jetbrains-copilot-2DSVAJMJ.js.map +0 -1
- package/dist/junie-GUBDRADP.js.map +0 -1
- package/dist/kilo-HRLWMENX.js.map +0 -1
- package/dist/kilo-SMHPOHJ5.js.map +0 -1
- package/dist/kilo-cli-IQMFMYZF.js.map +0 -1
- package/dist/kilo-cli-T2SRRRJS.js.map +0 -1
- package/dist/kimi-EPD46UJK.js.map +0 -1
- package/dist/kimi-MSIDEMCI.js.map +0 -1
- package/dist/kiro-A7DL52FX.js.map +0 -1
- package/dist/kiro-NYRRBIKW.js.map +0 -1
- package/dist/leaderboard-KCT7XPCI.js.map +0 -1
- package/dist/mimo-code-NDGLBXNC.js.map +0 -1
- package/dist/mistral-vibe-LLWASF5X.js.map +0 -1
- package/dist/mux-7PIJ7HUQ.js.map +0 -1
- package/dist/mux-ON4QK3OC.js.map +0 -1
- package/dist/nemoclaw-45R5MFBF.js.map +0 -1
- package/dist/omp-ROCTFV3A.js.map +0 -1
- package/dist/open-interpreter-V6CN542J.js.map +0 -1
- package/dist/openclaw-AOKFJGRQ.js +0 -17
- package/dist/openclaw-KAFTNLR2.js.map +0 -1
- package/dist/opencode-YYWQ45LF.js.map +0 -1
- package/dist/opencode-Z65JYILL.js.map +0 -1
- package/dist/openhands-OFFMWCQW.js.map +0 -1
- package/dist/package-TALOSRQ4.js.map +0 -1
- package/dist/pi-4W74ETTI.js.map +0 -1
- package/dist/pi-DLKCPOJE.js.map +0 -1
- package/dist/qwen-code-26CSFDX6.js.map +0 -1
- package/dist/qwen-code-JOBFNEVT.js.map +0 -1
- package/dist/roo-code-HSAE2CJU.js.map +0 -1
- package/dist/roo-code-RHIGJZNY.js.map +0 -1
- package/dist/serve-4TEHK726.js.map +0 -1
- package/dist/status-DVD4EHMX.js.map +0 -1
- package/dist/statusline-ZVNH7XBK.js.map +0 -1
- package/dist/synthetic-LUT6V6SD.js.map +0 -1
- package/dist/telemetry-NWUJPIK3.js.map +0 -1
- package/dist/trae-SIQI3C66.js.map +0 -1
- package/dist/trae-TLAFSQRQ.js.map +0 -1
- package/dist/uninstall-DZVFV7KE.js.map +0 -1
- package/dist/upgrade-OUE4ED52.js.map +0 -1
- package/dist/usage-NT3ZXALZ.js.map +0 -1
- package/dist/usage-event-IELZE6WO.js.map +0 -1
- package/dist/vscode-copilot-VZ5XYSTK.js.map +0 -1
- package/dist/warp-5YBSP7FP.js.map +0 -1
- package/dist/warp-P2HWXIKT.js.map +0 -1
- package/dist/windsurf-3JC73GZK.js.map +0 -1
- package/dist/zed-6CMUKXLI.js.map +0 -1
- package/dist/zed-XXCNYOIN.js.map +0 -1
- /package/dist/{antigravity-PZXYAO3X.js.map → antigravity-VXHL6ZPM.js.map} +0 -0
- /package/dist/{audit-2H7E3Z4N.js.map → audit-BO7JQYIH.js.map} +0 -0
- /package/dist/{cursor-JRVK56QA.js.map → cursor-RSLHA2CY.js.map} +0 -0
- /package/dist/{openclaw-AOKFJGRQ.js.map → openclaw-3DHVHX5P.js.map} +0 -0
package/dist/amp-S22XTPEQ.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/amp/index.ts"],"sourcesContent":["/**\r\n * adapters/amp — Amp (Sourcegraph / AmpCode) platform adapter for agent-connector.\r\n *\r\n * Amp is a **ts-plugin** host: alongside MCP it loads TypeScript plugin modules\r\n * from `.amp/plugins/<name>.ts`, each default-exporting a function `(amp) => void`\r\n * that registers lifecycle handlers via `amp.on(\"<event>\", async (event, ctx) =>\r\n * …)` (ampcode.com/manual → Plugins). This adapter wires the five amp.on events\r\n * that have a canonical analog and bridges them to the universal home-bin\r\n * entrypoint, exactly like the OMP / OpenCode ts-plugin adapters.\r\n *\r\n * Event mapping (only events the connector declares are subscribed):\r\n * SessionStart → amp.on(\"session.start\", …) (thread begins; id = event.thread.id)\r\n * UserPromptSubmit → amp.on(\"agent.start\", …) (user submits a prompt; observe-only)\r\n * PreToolUse → amp.on(\"tool.call\", …) (before a tool runs; deny → return\r\n * { action: \"reject-and-continue\" }, else { action: \"allow\" })\r\n * PostToolUse → amp.on(\"tool.result\", …) (after a tool finishes; observe-only)\r\n * Stop → amp.on(\"agent.end\", …) (turn ends; observe-only)\r\n * Amp documents NO session.end event, so SessionEnd is an honest gap (warn-skip\r\n * via the unsupported-here detail). tool.call's decision surface is the\r\n * { action } union (canModifyArgs:false — the \"modify\" input shape is not\r\n * documented); tool.result CAN return a replacement but the manual never\r\n * documents its object shape, so PostToolUse is observe-only (canModifyOutput:\r\n * false) rather than ship a guessed mutation; session.start has no documented\r\n * context-injection surface (canInjectSessionContext:false).\r\n *\r\n * Why we SYNTHESIZE a self-contained module instead of importing handlers:\r\n * the connector's hook handlers are arbitrary developer code we must not import\r\n * into Amp's (Bun) runtime — wrong cwd, wrong deps, version skew. So, like the\r\n * OMP / OpenCode adapters, we generate a tiny ESM/TS plugin that imports NOTHING\r\n * from agent-connector: each `amp.on(...)` handler shells out to the ONE stable\r\n * home binary's universal entrypoint (`<homeBin> hook amp <event> --connector\r\n * <id>`) over child_process, feeds it the amp-shaped payload as JSON on stdin,\r\n * and JSON.parses the normalized HookResponse back from stdout. Fail-open: any\r\n * bridge error → no-op. One entrypoint, every paradigm.\r\n *\r\n * Plugin scope: the `.amp/plugins/` dir is documented at PROJECT scope only; Amp\r\n * documents no user-scope plugins dir, so installHooks writes the plugin for\r\n * project scope and warn-skips user scope (the MCP + skills surfaces keep their\r\n * own user-scope paths). MCP config (report §2 / §5.3):\r\n * - user scope → ~/.config/amp/settings.json\r\n * - project scope → <projectDir>/.amp/settings.json\r\n * Both are JSONC (we write plain JSON, which is valid JSONC). The settings file\r\n * is SHARED with the rest of Amp's configuration, so we MERGE our entry into\r\n * the existing object and never clobber unrelated keys.\r\n *\r\n * QUIRK 1 — dotted top-level key: Amp does NOT use a nested `mcpServers` object.\r\n * The MCP registry is a single FLAT settings key literally named\r\n * `\"amp.mcpServers\"`, whose value is an object of `{ id: { command, args, env } }`.\r\n * We therefore set `settings[\"amp.mcpServers\"][connectorId] = entry`. Because the\r\n * base JSON helpers index `config[rootKey][serverId]`, passing the dotted string\r\n * as the root key writes exactly that flat key while preserving every sibling\r\n * setting (true merge).\r\n *\r\n * QUIRK 2 — native interpolation: Amp expands `${VAR_NAME}` in stdio entries at\r\n * runtime, so we KEEP env/header/url refs native by rewriting the portable\r\n * `${env:VAR}` syntax to Amp's `${VAR}` token rather than resolving to literals\r\n * (secrets stay out of the settings file).\r\n *\r\n * Skills surface: Amp reads SKILL.md files (dir-per-skill, same shape as\r\n * claude-code) from a skill root that is NOT under the config dir\r\n * (`~/.config/amp`):\r\n * - user scope → ~/.config/agents/skills/<name>/SKILL.md (the sibling\r\n * cross-agent `agents` dir — `~/.config/amp/skills/` is also documented-native,\r\n * but the `agents` root is the cross-agent standard we standardize on)\r\n * - project scope → <projectDir>/.agents/skills/<name>/SKILL.md\r\n * Because that root differs from getConfigDir, skills use a dedicated skillDir()\r\n * helper rather than reusing getConfigDir.\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 } from \"../base.js\";\r\nimport type {\r\n Adapter,\r\n GeneratedPluginFile,\r\n HookReply,\r\n InstallContext,\r\n NormalizedEvent,\r\n} from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookEventName,\r\n HookParadigm,\r\n HookResponse,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PostToolUseEvent,\r\n PreToolUseEvent,\r\n ServerDef,\r\n SessionStartEvent,\r\n 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 { renderBridgePrelude } from \"../../core/ts-plugin-bridge.js\";\r\nimport {\r\n buildWrappedStdio,\r\n} from \"../../core/spawn.js\";\r\nimport { renderSkillMd } from \"../claude-code/render.js\";\r\n\r\nconst HOST: PlatformId = \"amp\";\r\n/**\r\n * QUIRK: a single FLAT, dotted settings key — NOT a nested object. The base\r\n * JSON helpers treat this as `config[\"amp.mcpServers\"][serverId]`.\r\n */\r\nconst MCP_ROOT_KEY = \"amp.mcpServers\";\r\n\r\n/**\r\n * Canonical → Amp plugin event name map. A connector hook event is only\r\n * subscribed by the generated plugin when it appears here AND is declared by the\r\n * connector. Names verified against ampcode.com/manual (Plugins → amp.on events).\r\n * Amp documents NO `session.end`, so SessionEnd has no entry (honest gap).\r\n */\r\nconst EVENT_TO_AMP: Partial<Record<HookEventName, string>> = {\r\n SessionStart: \"session.start\",\r\n UserPromptSubmit: \"agent.start\",\r\n PreToolUse: \"tool.call\",\r\n PostToolUse: \"tool.result\",\r\n Stop: \"agent.end\",\r\n};\r\n\r\n/** Raw payload the generated plugin posts to the universal hook entrypoint. */\r\ninterface AmpBridgePayload {\r\n toolName?: string;\r\n toolInput?: Record<string, unknown>;\r\n // ToolResultEvent.output is typed `unknown` by the host (@ampcode/plugin\r\n // index.d.ts:947 — \"Tool output/result if available\"), so the bridge may\r\n // forward a string OR a structured value; parseEvent stringifies non-strings.\r\n toolOutput?: unknown;\r\n isError?: boolean;\r\n prompt?: string;\r\n sessionId?: string;\r\n projectDir?: string;\r\n}\r\n\r\n/** Native MCP server entry shapes Amp accepts under `amp.mcpServers`. */\r\ninterface AmpStdioServer {\r\n command: string;\r\n args?: string[];\r\n env?: Record<string, string>;\r\n}\r\ninterface AmpHttpServer {\r\n url: string;\r\n headers?: Record<string, string>;\r\n}\r\n\r\nexport class AmpAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name = \"Amp\";\r\n readonly paradigm: HookParadigm = \"ts-plugin\";\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 // amp.on lifecycle events with a canonical analog (EVENT_TO_AMP):\r\n // tool.call / tool.result / session.start / agent.start / agent.end.\r\n preToolUse: true,\r\n postToolUse: true,\r\n // Amp has no documented compaction hook.\r\n preCompact: false,\r\n sessionStart: true,\r\n // Amp documents NO session.end event — SessionEnd is an honest gap.\r\n sessionEnd: false,\r\n userPromptSubmit: true,\r\n stop: true,\r\n notification: false,\r\n // Newer events: Amp documents no permission/approval, post-tool-failure, or\r\n // subagent lifecycle hook with a canonical analog, so permissionRequest /\r\n // postToolUseFailure / subagentStart / subagentStop stay unset — the\r\n // generated bridge never references them and install reports them as\r\n // \"unsupported here\".\r\n // tool.call's documented decision surface is { action: \"allow\" |\r\n // \"reject-and-continue\" | \"modify\" | \"synthesize\" } (ampcode.com/manual →\r\n // Plugins → tool.call). We wire allow + reject-and-continue (block); the\r\n // \"modify\" input-rewrite shape is not byte-documented, so canModifyArgs:false.\r\n canModifyArgs: false,\r\n // tool.result says \"return a replacement status/output\" but the manual never\r\n // documents the replacement OBJECT SHAPE (which keys). Rather than guess it,\r\n // PostToolUse is observe-only and canModifyOutput stays false until the shape\r\n // is primary-source-verified (honesty bar: under-claim, never emit a guessed\r\n // mutation contract).\r\n canModifyOutput: false,\r\n // session.start has no documented context-injection surface.\r\n canInjectSessionContext: false,\r\n // Amp registers stdio and Streamable HTTP MCP servers.\r\n transports: [\"stdio\", \"http\"],\r\n // Server env/url use Amp's NATIVE ${env:VAR} interpolation (rewriteEnvRefs)\r\n // — the token survives into the config, never a baked literal — so the\r\n // installer's unset-env-ref bake warn does not apply here.\r\n nativeServerEnvInterpolation: true,\r\n // Skills: Amp reads SKILL.md from ~/.config/agents/skills/<name>/SKILL.md\r\n // (user) and <projectDir>/.agents/skills/<name>/SKILL.md (project) — a root\r\n // OUTSIDE the config dir, so the skillDir() helper resolves it explicitly.\r\n supportsSkills: true,\r\n // Native passthrough hooks: amp.on events with no canonical HookEventName\r\n // analog are reachable via platforms[\"amp\"].nativeHooks; the generated plugin\r\n // registers + bridges each declared native event verbatim and the home-bin\r\n // runtime dispatches it host-generically via runNativeHook.\r\n supportsNativeHooks: true,\r\n };\r\n\r\n // ── Detection ────────────────────────────────────────────────────────────\r\n\r\n detectInstalled(projectDir: string): DetectedPlatform {\r\n const userDir = join(homedir(), \".config\", \"amp\");\r\n const userSettings = join(userDir, \"settings.json\");\r\n const projectDirAmp = join(projectDir, \".amp\");\r\n const projectSettings = join(projectDirAmp, \"settings.json\");\r\n\r\n const userInstalled = existsSync(userDir) || existsSync(userSettings);\r\n const projectInstalled = existsSync(projectDirAmp) || existsSync(projectSettings);\r\n const installed = userInstalled || projectInstalled;\r\n const scope = projectInstalled && !userInstalled ? \"project\" : \"user\";\r\n const configPath = scope === \"project\" ? projectSettings : userSettings;\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 Amp config (${scope}) at ${configPath}`\r\n : `no Amp config at ${userSettings} or ${projectSettings}`,\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, \".amp\")\r\n : join(homedir(), \".config\", \"amp\");\r\n }\r\n\r\n getServerConfigPath(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"settings.json\");\r\n }\r\n\r\n /**\r\n * For this ts-plugin host the \"hook config path\" is the generated plugin\r\n * MODULE. Amp loads every `.ts` file in `.amp/plugins/`, so writing this file\r\n * IS the registration. The `.amp/plugins/` dir is documented at PROJECT scope\r\n * (Amp documents no user-scope plugins dir — see pluginsDir); the user-scope\r\n * path is returned for doctor/uninstall symmetry but installHooks warn-skips it.\r\n */\r\n getHookConfigPath(ctx: InstallContext): string {\r\n return join(this.pluginsDir(ctx), `${ctx.connector.id}.ts`);\r\n }\r\n\r\n /**\r\n * Plugin directory Amp auto-loads `.ts` modules from. PROJECT scope is the\r\n * documented `<projectDir>/.amp/plugins`; user scope mirrors the MCP config dir\r\n * (`~/.config/amp/plugins`) for path symmetry only — installHooks never writes\r\n * there because Amp documents no user-scope plugins dir.\r\n */\r\n private pluginsDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".amp\", \"plugins\")\r\n : join(this.getConfigDir(ctx), \"plugins\");\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 amp\"\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 // Upsert into the flat \"amp.mcpServers\" key, merging into existing settings.\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 Amp's native `amp.mcpServers` entry. */\r\n private renderServerEntry(\r\n ctx: InstallContext,\r\n server: ServerDef,\r\n ): AmpStdioServer | AmpHttpServer {\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 // Amp expands ${VAR_NAME} natively, so keep refs native (no literals).\r\n const entry: AmpStdioServer = { command: this.rewrite(command) };\r\n if (args.length > 0) entry.args = args.map((a) => this.rewrite(a));\r\n const env = this.renderEnv(server.env);\r\n if (env) entry.env = env;\r\n return entry;\r\n }\r\n\r\n // http (and any other remote transport we surface) — Amp registers a URL.\r\n const entry: AmpHttpServer = { url: this.rewrite(server.url ?? \"\") };\r\n const headers = this.renderEnv(server.headers);\r\n if (headers) entry.headers = headers;\r\n return entry;\r\n }\r\n\r\n /**\r\n * Render env/header values. Amp supports native `${VAR_NAME}` interpolation,\r\n * so translate `${env:VAR}` refs to that native token rather than baking\r\n * secrets into the file. Literals pass through unchanged.\r\n */\r\n private renderEnv(\r\n env: Record<string, string> | undefined,\r\n ): Record<string, string> | undefined {\r\n if (!env || Object.keys(env).length === 0) return undefined;\r\n const out: Record<string, string> = {};\r\n for (const [k, v] of Object.entries(env)) out[k] = this.rewrite(v);\r\n return out;\r\n }\r\n\r\n /** Translate `${env:VAR(:-default)}` to Amp's native `${VAR}` token. */\r\n private rewrite(value: string): string {\r\n return rewriteEnvRefs(value, ampEnvToken);\r\n }\r\n\r\n // ── Skills surface ───────────────────────────────────────────────────────\r\n // CONTENT-ONLY: pure native-file writers under the skill root. No runtime\r\n // dispatch, no home-bin pointer, no telemetry wrap. Idempotent (byte-identical\r\n // → skip) via writeContentFile and reversible via removeContentFile. Honors\r\n // platforms[\"amp\"].skills === false to skip.\r\n //\r\n // QUIRK: the skill root is NOT under getConfigDir (~/.config/amp). Amp reads\r\n // SKILL.md from the sibling cross-agent `agents` tree:\r\n // user scope → ~/.config/agents/skills/<name>/SKILL.md\r\n // project scope → <projectDir>/.agents/skills/<name>/SKILL.md\r\n // so skillDir() resolves it explicitly rather than reusing getConfigDir.\r\n\r\n /** Skill root: user `~/.config/agents/skills`, project `<projectDir>/.agents/skills`. */\r\n private skillsDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".agents\", \"skills\")\r\n : join(homedir(), \".config\", \"agents\", \"skills\");\r\n }\r\n\r\n /** Native skill dir: <skillRoot>/<name>. */\r\n private skillDir(ctx: InstallContext, name: string): string {\r\n return join(this.skillsDir(ctx), 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 amp\" }];\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 /** Render a skill's SKILL.md (delegates to the shared renderer). */\r\n private renderSkill(skill: SkillDef): string {\r\n return renderSkillMd(skill);\r\n }\r\n\r\n // ── Hook install / uninstall (ts-plugin module) ──────────────────────────\r\n\r\n installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const pluginPath = this.getHookConfigPath(ctx);\r\n\r\n // Amp documents no user-scope plugins dir — only `.amp/plugins/` at project\r\n // scope. Degradation is never silent: warn-skip rather than write to an\r\n // unverified user path.\r\n if (ctx.scope !== \"project\") {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"warn\",\r\n path: pluginPath,\r\n detail:\r\n \"amp plugins are project-scoped (.amp/plugins/) — no user-scope plugins dir is documented; skipped\",\r\n },\r\n ];\r\n }\r\n\r\n // Native passthrough events are a sibling, amp-scoped declaration: `hooks:\r\n // false` disables only the CANONICAL events, and the generated native loop\r\n // reads platforms.amp.nativeHooks directly (independent of hookEvents). So\r\n // whenever native events exist the plugin must still be synthesized.\r\n const canonicalDisabled = ctx.connector.platforms[HOST]?.hooks === false;\r\n const hasCanonical = !canonicalDisabled && ctx.connector.hookEvents.length > 0;\r\n const hasNative = this.nativeHookEvents(ctx).length > 0;\r\n\r\n if (!hasCanonical && !hasNative) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: pluginPath,\r\n detail: canonicalDisabled\r\n ? \"hooks disabled for amp\"\r\n : \"connector declares no hooks\",\r\n },\r\n ];\r\n }\r\n\r\n const files = this.synthesizePlugin(ctx);\r\n const changes: ChangeRecord[] = [];\r\n\r\n for (const file of files) {\r\n changes.push(\r\n this.writeManagedFile(\r\n file.path,\r\n file.contents,\r\n ctx.dryRun,\r\n `amp plugin module (${this.hookDetail(ctx)})`,\r\n file.executable,\r\n ),\r\n );\r\n }\r\n\r\n return changes;\r\n }\r\n\r\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\r\n const pluginPath = this.getHookConfigPath(ctx);\r\n return [\r\n this.removeManagedFile(pluginPath, ctx.dryRun, \"amp plugin module\", \"no amp plugin module present\"),\r\n ];\r\n }\r\n\r\n /**\r\n * Human-facing summary of which declared events the synthesized module ACTUALLY\r\n * wires. Only events present in EVENT_TO_AMP are mapped/wired; any declared\r\n * event with no Amp mapping (e.g. SessionEnd) is reported separately as\r\n * \"unsupported here\" so the detail never overstates coverage.\r\n */\r\n private hookDetail(ctx: InstallContext): string {\r\n // `hooks: false` suppresses the canonical handlers, so the detail must not\r\n // claim them; native events are still reported below.\r\n const canonicalOff = ctx.connector.platforms[HOST]?.hooks === false;\r\n const declared = canonicalOff ? [] : ctx.connector.hookEvents;\r\n const mapped = declared.filter((e) => EVENT_TO_AMP[e] !== undefined);\r\n const unsupported = declared.filter((e) => EVENT_TO_AMP[e] === undefined);\r\n const native = this.nativeHookEvents(ctx);\r\n const parts: string[] = [];\r\n if (mapped.length > 0) parts.push(mapped.join(\",\"));\r\n if (native.length > 0) parts.push(`native: ${native.join(\",\")}`);\r\n const base = parts.join(\"; \") || \"no canonical hooks\";\r\n return unsupported.length > 0\r\n ? `${base}; unsupported here: ${unsupported.join(\",\")}`\r\n : base;\r\n }\r\n\r\n /**\r\n * Amp-native passthrough events declared on platforms[\"amp\"].nativeHooks\r\n * (amp.on event names with no canonical HookEventName analog). Emitted as\r\n * generic bridge registrations and dispatched host-generically by the home-bin's\r\n * runNativeHook.\r\n */\r\n private nativeHookEvents(ctx: InstallContext): string[] {\r\n return Object.keys(ctx.connector.platforms[HOST]?.nativeHooks ?? {});\r\n }\r\n\r\n // ── ts-plugin synthesis ──────────────────────────────────────────────────\r\n\r\n /**\r\n * Build ONE self-contained Amp plugin module (`.amp/plugins/<id>.ts`). It\r\n * imports nothing from agent-connector; each `amp.on(...)` handler shells out to\r\n * the universal hook entrypoint via child_process and applies the normalized\r\n * HookResponse. Its default export is the Amp plugin function `(amp) => void`.\r\n */\r\n synthesizePlugin(ctx: InstallContext): GeneratedPluginFile[] {\r\n const path = this.getHookConfigPath(ctx);\r\n const contents = this.buildPluginSource(ctx);\r\n return [{ path, contents, executable: false }];\r\n }\r\n\r\n /** Compose the generated plugin source with plain string concatenation. */\r\n private buildPluginSource(ctx: InstallContext): string {\r\n // `platforms.amp.hooks === false` disables only the CANONICAL events (the\r\n // native loop below reads platforms.amp.nativeHooks directly and is a sibling,\r\n // unaffected). When canonical hooks are off the module may still be\r\n // synthesized for native events, so the canonical handler set collapses to\r\n // empty here rather than short-circuiting the whole plugin.\r\n const canonicalOff = ctx.connector.platforms[HOST]?.hooks === false;\r\n const events = canonicalOff\r\n ? []\r\n : ctx.connector.hookEvents.filter(\r\n (e): e is HookEventName => EVENT_TO_AMP[e] !== undefined,\r\n );\r\n const has = (e: HookEventName) => events.includes(e);\r\n\r\n const header = `/**\r\n * AUTO-GENERATED by agent-connector — DO NOT EDIT.\r\n *\r\n * Self-contained Amp plugin for connector ${ctx.connector.id}.\r\n * It imports nothing from agent-connector: every hook invocation shells out to\r\n * the stable home binary's universal entrypoint and JSON-parses the normalized\r\n * response. Fail-open: any bridge error degrades to a no-op (never wedges Amp).\r\n *\r\n * Amp loads this module from .amp/plugins/${ctx.connector.id}.ts; the default\r\n * export is the plugin function (amp) => void called once with the PluginAPI.\r\n */\r\nimport type { PluginAPI } from \"@ampcode/plugin\";\r\nimport { execFileSync, execSync } from \"node:child_process\";\r\n\r\n${renderBridgePrelude({\r\n homeBin: ctx.homeBinPath,\r\n connectorId: ctx.connector.id,\r\n hookSlug: \"amp\",\r\n payloadNoun: \"amp\",\r\n failOpenComment: \"Fail-open — never wedge an Amp tool call / lifecycle event.\",\r\n})}\r\n\r\n// Amp documents no project-dir env var; the plugin process runs in the workspace\r\n// root, so resolve once at load. Session id is rebound on each session.start.\r\nconst PROJECT_DIR = process.cwd();\r\nlet SESSION_ID = \"\";\r\n`;\r\n\r\n const handlers: string[] = [];\r\n\r\n if (has(\"SessionStart\")) {\r\n handlers.push(` // SessionStart → rebind the session id and notify the connector.\r\n // Amp's session.start event carries event.thread.id (ampcode.com/manual →\r\n // Plugins → session.start: \"Example session.start for \\${event.thread.id}\").\r\n amp.on(\"session.start\", async (event) => {\r\n SESSION_ID =\r\n (event && event.thread && event.thread.id) || \"amp-session\";\r\n bridge(\"SessionStart\", { sessionId: SESSION_ID, projectDir: PROJECT_DIR });\r\n });`);\r\n }\r\n\r\n if (has(\"UserPromptSubmit\")) {\r\n handlers.push(` // UserPromptSubmit → agent.start \"fires when the user submits a prompt\"\r\n // (ampcode.com/manual). The host DOES carry the prompt: AgentStartEvent.message\r\n // is \"The user's prompt message\" (@ampcode/plugin index.d.ts:1011-1012), so we\r\n // PROJECT event.message into the bridge payload's prompt key (the prior version\r\n // dropped it). Observe-only return surface — any deny/context decision is a no-op.\r\n amp.on(\"agent.start\", async (event) => {\r\n bridge(\"UserPromptSubmit\", {\r\n prompt: (event && typeof event.message === \"string\" && event.message) || \"\",\r\n sessionId: SESSION_ID,\r\n projectDir: PROJECT_DIR,\r\n raw: event,\r\n });\r\n });`);\r\n }\r\n\r\n if (has(\"PreToolUse\")) {\r\n handlers.push(` // PreToolUse → Amp's tool.call returns a decision union (ampcode.com/manual →\r\n // Plugins → tool.call): { action: \"allow\" } to run, or { action:\r\n // \"reject-and-continue\", message } to block and let the agent continue. The\r\n // tool name is event.tool (verified); the input field is not byte-documented,\r\n // so event.input is a best-effort hint for the connector's matcher (the matcher\r\n // keys on the verified tool name). A deny/ask decision maps to reject-and-\r\n // continue; everything else allows.\r\n amp.on(\"tool.call\", async (event) => {\r\n const payload = {\r\n toolName: (event && event.tool) || \"\",\r\n toolInput: (event && event.input) || {},\r\n sessionId: SESSION_ID,\r\n projectDir: PROJECT_DIR,\r\n };\r\n const res = bridge(\"PreToolUse\", payload);\r\n if (res && (res.decision === \"deny\" || res.decision === \"ask\")) {\r\n return {\r\n action: \"reject-and-continue\",\r\n message: res.reason || \"Blocked by ${ctx.connector.id}\",\r\n };\r\n }\r\n return { action: \"allow\" };\r\n });`);\r\n }\r\n\r\n if (has(\"PostToolUse\")) {\r\n handlers.push(` // PostToolUse → tool.result \"fires after a tool finishes and before the result\r\n // is sent back to the model\" (ampcode.com/manual). The tool name is event.tool\r\n // and the error signal is event.status === \"error\" (both verified). The host DOES\r\n // carry the output: ToolResultEvent extends ToolResult.output (typed unknown —\r\n // \"Tool output/result if available\", @ampcode/plugin index.d.ts:946-947,976), so\r\n // we PROJECT event.output into the bridge payload's toolOutput (prior version\r\n // dropped it). Replacement still observe-only (canModifyOutput:false) — the\r\n // replacement OBJECT shape is undocumented, so we never return a guessed mutation.\r\n amp.on(\"tool.result\", async (event) => {\r\n bridge(\"PostToolUse\", {\r\n toolName: (event && event.tool) || \"\",\r\n toolOutput: event && event.output,\r\n isError: !!(event && event.status === \"error\"),\r\n sessionId: SESSION_ID,\r\n projectDir: PROJECT_DIR,\r\n raw: event,\r\n });\r\n });`);\r\n }\r\n\r\n if (has(\"Stop\")) {\r\n handlers.push(` // Stop → observe turn end. Amp's agent.end exposes no decision surface, so\r\n // this is observe-only (a deny decision degrades to a no-op).\r\n amp.on(\"agent.end\", async (event) => {\r\n bridge(\"Stop\", { sessionId: SESSION_ID, projectDir: PROJECT_DIR, raw: event });\r\n });`);\r\n }\r\n\r\n // NATIVE passthrough events: amp.on event names with no canonical analog,\r\n // declared on platforms.amp.nativeHooks (independent of hookEvents, so they\r\n // install even with hooks:false). Each bridges the NATIVE event name verbatim\r\n // to the home-bin → runNativeHook dispatches it host-generically.\r\n for (const ev of this.nativeHookEvents(ctx)) {\r\n const key = JSON.stringify(ev);\r\n handlers.push(` // nativeHooks passthrough → ${ev}\r\n amp.on(${key}, async (event) => {\r\n bridge(${key}, { sessionId: SESSION_ID, projectDir: PROJECT_DIR, raw: event });\r\n });`);\r\n }\r\n\r\n const factory = `\r\nexport default function plugin(amp: PluginAPI) {\r\n${handlers.join(\"\\n\\n\")}\r\n}\r\n`;\r\n\r\n return header + factory;\r\n }\r\n\r\n // ── Runtime: parse OUR bridge payload → normalized event ─────────────────\r\n\r\n /**\r\n * `raw` is the payload OUR generated plugin posts (NOT a host-native shape):\r\n * { toolName?, toolInput?, toolOutput?, isError?, prompt?, sessionId?, projectDir? }\r\n * so this maps straight through.\r\n */\r\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\r\n const input = (raw ?? {}) as AmpBridgePayload;\r\n const base = {\r\n hostPlatform: HOST,\r\n connectorId: \"\",\r\n sessionId: typeof input.sessionId === \"string\" ? input.sessionId : \"\",\r\n raw,\r\n ...(typeof input.projectDir === \"string\"\r\n ? { projectDir: input.projectDir }\r\n : {}),\r\n } as const;\r\n\r\n switch (event) {\r\n case \"PreToolUse\": {\r\n const ev: PreToolUseEvent = {\r\n ...base,\r\n toolName: input.toolName ?? \"\",\r\n toolInput: input.toolInput ?? {},\r\n };\r\n return ev;\r\n }\r\n case \"PostToolUse\": {\r\n // ToolResultEvent.output is typed `unknown` by the host (@ampcode/plugin\r\n // index.d.ts:947), so the bridge can forward a string OR a structured value.\r\n // Accept both: pass strings through, JSON-stringify other non-nullish\r\n // values (the old `typeof === \"string\"` guard silently dropped the latter).\r\n const out = coerceToolOutput(input.toolOutput);\r\n const ev: PostToolUseEvent = {\r\n ...base,\r\n toolName: input.toolName ?? \"\",\r\n toolInput: input.toolInput ?? {},\r\n ...(out !== undefined ? { toolOutput: out } : {}),\r\n ...(typeof input.isError === \"boolean\"\r\n ? { isError: input.isError }\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 = { ...base };\r\n return ev;\r\n }\r\n case \"SessionStart\": {\r\n const ev: SessionStartEvent = { ...base, source: \"startup\" };\r\n return ev;\r\n }\r\n default:\r\n // Other canonical events are not surfaced by Amp; treat as a\r\n // session-start-shaped no-op so the dispatcher fails open gracefully.\r\n return { ...base, source: \"startup\" } satisfies SessionStartEvent;\r\n }\r\n }\r\n\r\n // ── Runtime: normalized response → reply the generated bridge parses ─────\r\n\r\n /**\r\n * Unlike json-stdio hosts (whose reply is the host's NATIVE control payload),\r\n * OUR generated bridge consumes this stdout directly. So the reply body is the\r\n * NORMALIZED HookResponse itself — the bridge JSON.parses it and, for tool.call,\r\n * maps a deny/ask decision → { action: \"reject-and-continue\" } (else allow).\r\n * Other events are observe-only, so their reply is ignored by the handler.\r\n */\r\n formatReply(_event: HookEventName, response: HookResponse): HookReply {\r\n return {\r\n exitCode: 0,\r\n stdout: JSON.stringify(response ?? { decision: \"allow\" }),\r\n };\r\n }\r\n\r\n // ── Diagnostics ──────────────────────────────────────────────────────────\r\n\r\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\r\n const settingsPath = this.getServerConfigPath(ctx);\r\n const connectorId = ctx.connector.id;\r\n return [\r\n {\r\n name: `${this.name}: settings.json present`,\r\n check: () =>\r\n existsSync(settingsPath)\r\n ? { status: \"OK\", detail: settingsPath }\r\n : { status: \"FAIL\", detail: `not found: ${settingsPath}` },\r\n },\r\n {\r\n name: `${this.name}: server entry registered`,\r\n check: () => {\r\n // Only assert what the connector declares: a server-less connector\r\n // never writes a server entry, so its absence is healthy.\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> }>(settingsPath);\r\n const bucket = cfg?.[MCP_ROOT_KEY];\r\n if (!cfg || !bucket) {\r\n return { status: \"FAIL\", detail: `no \"${MCP_ROOT_KEY}\" in ${settingsPath}` };\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 ${settingsPath}`,\r\n };\r\n },\r\n },\r\n {\r\n name: `${this.name}: plugin module present`,\r\n check: () => {\r\n const hasHooks =\r\n ctx.connector.hookEvents.length > 0 ||\r\n this.nativeHookEvents(ctx).length > 0;\r\n if (!hasHooks) return { status: \"OK\", detail: \"no hooks declared\" };\r\n // Amp plugins are project-scoped; a user-scope install warn-skips the\r\n // plugin, so its absence there is healthy.\r\n if (ctx.scope !== \"project\") {\r\n return {\r\n status: \"OK\",\r\n detail: \"amp plugins are project-scoped (user scope skipped)\",\r\n };\r\n }\r\n const pluginPath = this.getHookConfigPath(ctx);\r\n return existsSync(pluginPath)\r\n ? { status: \"OK\", detail: pluginPath }\r\n : { status: \"FAIL\", detail: `not found: ${pluginPath}` };\r\n },\r\n },\r\n ];\r\n }\r\n\r\n /** Read a file, returning undefined on any error (idempotency compare). */\r\n}\r\n\r\n/**\r\n * Coerce Amp's `ToolResultEvent.output` (typed `unknown` upstream) into the\r\n * normalized event's `toolOutput: string`. Strings pass through; other\r\n * non-nullish values (objects / arrays / numbers) are JSON-stringified so a\r\n * structured tool result is no longer silently dropped. `undefined`/`null`\r\n * yields `undefined` (the key is omitted by the caller).\r\n */\r\nfunction coerceToolOutput(value: unknown): string | undefined {\r\n if (typeof value === \"string\") return value;\r\n if (value === undefined || value === null) return undefined;\r\n try {\r\n return JSON.stringify(value);\r\n } catch {\r\n return String(value);\r\n }\r\n}\r\n\r\n/**\r\n * Amp native interpolation token: `${env:VAR}` → `${VAR}`.\r\n *\r\n * When the portable ref carried a default (`${env:VAR:-fallback}`), Amp's native\r\n * `${VAR}` token has no way to express it — so a bare native token would silently\r\n * DROP the default. Instead, resolve the default at install time: emit the live\r\n * value when VAR is set and non-empty, else the literal fallback. The native\r\n * token is only emitted when there is no default to preserve.\r\n */\r\nfunction ampEnvToken(name: string, def?: string): string {\r\n if (def !== undefined) {\r\n const v = process.env[name];\r\n return v != null && v !== \"\" ? v : def;\r\n }\r\n return `\\${${name}}`;\r\n}\r\n\r\nexport const adapter = new AmpAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":["entry"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/usage/readers/antigravity.ts"],"sourcesContent":["/**\r\n * usage/readers/antigravity — Antigravity IDE usage reader (SYNCED platform).\r\n *\r\n * CONFIRMED-BY-INSTALL (2026-06-03, docs/research/antigravity-paths-confirmed.md):\r\n * the native Antigravity store is `~/.gemini/antigravity/conversations/<uuid>.pb`\r\n * — PROTOBUF with NO public schema — and `brain/<uuid>/` holds only media +\r\n * `*.metadata.json`. There are NO `transcript*.jsonl` files. The earlier\r\n * \"read native brain transcript*.jsonl with usage_metadata\" approach was based on\r\n * a shape that does not exist on disk.\r\n *\r\n * So this reader does NOT attempt to parse `.pb` (no schema). Like the other\r\n * SYNCED platforms (cursor/trae/warp), it reads only the tokscale synced-cache\r\n * if a separate tokscale run already produced one; otherwise it returns [] and\r\n * the scan layer reports \"requires sync (no local cache found)\" — i.e. the native\r\n * store is protobuf (.pb), not readable.\r\n *\r\n * - cache present → parse the tokscale cache (manifest.json + sessions/*.jsonl,\r\n * plus any loose ~/antigravity* brain/conversations *.jsonl dumps) using the\r\n * tokscale Rust line schema (`session_meta` / `usage` rows);\r\n * - cache absent → [] (kind:\"synced\" → scan notes \"requires sync\").\r\n *\r\n * The native `~/.gemini/antigravity/` dir is used ONLY for platform detection (in\r\n * the adapter), NOT for usage parsing here. The cached rows carry real host token\r\n * counts → \"host-reported\".\r\n *\r\n * Fail-open: no cache → []; unreadable/malformed file or line → skipped; we NEVER\r\n * touch `.pb` protobuf.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { basename, isAbsolute, join, resolve, sep } 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, isDir, listSubdirs, walkFiles } from \"../paths.js\";\r\nimport { nonEmptyStr, resolveAlias, toSafeInt } from \"./antigravity-shared.js\";\r\n\r\nconst PLATFORM_ID = \"antigravity\" as const;\r\nconst DEFAULT_MODEL = \"unknown\";\r\nconst DEFAULT_PROVIDER = \"antigravity\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// TOKSCALE CACHE — port of tokscale parse_antigravity_file\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Fields we read off a tokscale-cache Antigravity JSONL line (all optional). */\r\ninterface AntigravityLine {\r\n type?: unknown;\r\n modelId?: unknown;\r\n providerId?: unknown;\r\n sessionId?: unknown;\r\n timestamp?: unknown;\r\n input?: unknown;\r\n output?: unknown;\r\n cacheRead?: unknown;\r\n cacheWrite?: unknown;\r\n reasoning?: unknown;\r\n responseId?: unknown;\r\n}\r\n\r\n/**\r\n * Parse one tokscale-cache Antigravity JSONL file into usage records (port of\r\n * parse_antigravity_file + parse_usage_row). A `session_meta` line updates the\r\n * running fallback model; a `usage` line emits a record when valid.\r\n */\r\nfunction parseCacheFile(path: string): UsageRecord[] {\r\n const lines = readJsonlLines(path);\r\n if (lines.length === 0) return [];\r\n\r\n const out: UsageRecord[] = [];\r\n let sessionModel: string | undefined;\r\n\r\n for (const raw of lines) {\r\n if (typeof raw !== \"object\" || raw === null) continue;\r\n const line = raw as AntigravityLine;\r\n\r\n const rowType = typeof line.type === \"string\" ? line.type : \"\";\r\n if (rowType === \"session_meta\") {\r\n const meta = nonEmptyStr(line.modelId);\r\n if (meta !== undefined) sessionModel = meta;\r\n continue;\r\n }\r\n if (rowType !== \"usage\") continue;\r\n\r\n const record = parseCacheUsageRow(line, sessionModel);\r\n if (record !== undefined) out.push(record);\r\n }\r\n\r\n return out;\r\n}\r\n\r\n/** Port of parse_usage_row: build a UsageRecord from a \"usage\" line, or undefined. */\r\nfunction parseCacheUsageRow(line: AntigravityLine, fallbackModel: string | undefined): UsageRecord | undefined {\r\n const sessionId = nonEmptyStr(line.sessionId);\r\n if (sessionId === undefined) return undefined;\r\n\r\n const timestamp = toSafeInt(line.timestamp);\r\n if (timestamp <= 0) return undefined;\r\n\r\n const rawModel = nonEmptyStr(line.modelId) ?? fallbackModel ?? DEFAULT_MODEL;\r\n const modelId = resolveAlias(rawModel) ?? rawModel;\r\n const providerId = nonEmptyStr(line.providerId) ?? inferProvider(modelId) ?? DEFAULT_PROVIDER;\r\n\r\n const input = toSafeInt(line.input);\r\n const output = toSafeInt(line.output);\r\n const cacheRead = toSafeInt(line.cacheRead);\r\n const cacheWrite = toSafeInt(line.cacheWrite);\r\n const reasoning = toSafeInt(line.reasoning);\r\n if (input === 0 && output === 0 && cacheRead === 0 && cacheWrite === 0 && reasoning === 0) {\r\n return undefined;\r\n }\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 const record: UsageRecord = {\r\n platformId: PLATFORM_ID,\r\n modelId,\r\n providerId,\r\n sessionId,\r\n tokens,\r\n ts: timestamp,\r\n messageCount: 1,\r\n confidence: \"host-reported\",\r\n };\r\n const dedupKey = nonEmptyStr(line.responseId);\r\n if (dedupKey !== undefined) record.dedupKey = dedupKey;\r\n return record;\r\n}\r\n\r\n/** A manifest entry's relative artifact path, tolerant of the field spelling. */\r\ninterface ManifestEntry {\r\n artifact_path?: unknown;\r\n artifactPath?: unknown;\r\n}\r\n\r\n/**\r\n * Read manifest.json (if present) and return the cache-relative artifact paths it\r\n * lists, resolved to absolute paths that stay INSIDE the cache dir (a traversal\r\n * or absolute path in the manifest is rejected, fail-open).\r\n */\r\nfunction manifestArtifacts(cacheDir: string): string[] {\r\n const manifestPath = join(cacheDir, \"manifest.json\");\r\n if (!existsSync(manifestPath)) return [];\r\n const data = readJsonFile(manifestPath);\r\n if (data === undefined || data === null) return [];\r\n\r\n let entries: unknown[];\r\n if (Array.isArray(data)) {\r\n entries = data;\r\n } else if (typeof data === \"object\") {\r\n const obj = data as { sessions?: unknown; entries?: unknown };\r\n entries = Array.isArray(obj.sessions) ? obj.sessions : Array.isArray(obj.entries) ? obj.entries : [];\r\n } else {\r\n return [];\r\n }\r\n\r\n const cacheRootResolved = resolve(cacheDir);\r\n const out: string[] = [];\r\n for (const entry of entries) {\r\n if (typeof entry !== \"object\" || entry === null) continue;\r\n const e = entry as ManifestEntry;\r\n const rel = nonEmptyStr(e.artifact_path) ?? nonEmptyStr(e.artifactPath);\r\n if (rel === undefined) continue;\r\n if (isAbsolute(rel)) continue;\r\n const abs = resolve(cacheDir, rel);\r\n if (abs !== cacheRootResolved && !abs.startsWith(cacheRootResolved + sep)) continue;\r\n out.push(abs);\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Loose local-filesystem trajectory dumps: ~/antigravity-prefixed dirs, each with\r\n * a brain/ and/or conversations/ subdir (the local half of tokscale's merge).\r\n * Returns every *.jsonl under those dirs (`.pb` is naturally excluded — these are\r\n * tokscale-produced JSONL mirrors, NOT the native protobuf store).\r\n */\r\nfunction filesystemArtifacts(): string[] {\r\n const home = homedir();\r\n if (!isDir(home)) return [];\r\n\r\n const out: string[] = [];\r\n for (const child of listSubdirs(home)) {\r\n const name = basename(child);\r\n if (!name.startsWith(\"antigravity\")) continue;\r\n for (const sub of [\"brain\", \"conversations\"]) {\r\n const dir = join(child, sub);\r\n if (!isDir(dir)) continue;\r\n for (const f of walkFiles(dir, (fname) => fname.endsWith(\".jsonl\"))) {\r\n out.push(f);\r\n }\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n/** Collect tokscale-cache records (manifest ∪ sessions/*.jsonl ∪ loose ~/antigravity*). */\r\nfunction readTokscaleCache(): UsageRecord[] {\r\n const cacheRoot = firstExistingRoot(PLATFORM_ID);\r\n\r\n const files = new Set<string>();\r\n if (cacheRoot !== undefined) {\r\n for (const f of manifestArtifacts(cacheRoot)) files.add(f);\r\n const sessionsDir = join(cacheRoot, \"sessions\");\r\n if (isDir(sessionsDir)) {\r\n for (const f of walkFiles(sessionsDir, (name) => name.endsWith(\".jsonl\"))) files.add(f);\r\n }\r\n }\r\n for (const f of filesystemArtifacts()) files.add(f);\r\n\r\n if (files.size === 0) return [];\r\n\r\n const out: UsageRecord[] = [];\r\n for (const file of files) {\r\n if (!existsSync(file)) continue; // stale manifest reference → skip\r\n for (const row of parseCacheFile(file)) out.push(row);\r\n }\r\n return out;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Reader\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * The Antigravity IDE usage reader singleton (SYNCED). The native store is\r\n * protobuf (.pb) with no public schema → not parseable; we read only the tokscale\r\n * synced-cache if present, else [] (scan reports \"requires sync\").\r\n */\r\nconst antigravityReader: UsageReader = {\r\n platformId: PLATFORM_ID,\r\n kind: \"synced\",\r\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\r\n let cacheRows: UsageRecord[];\r\n try {\r\n cacheRows = readTokscaleCache();\r\n } catch {\r\n cacheRows = []; // fail-open\r\n }\r\n\r\n const records: UsageRecord[] = [];\r\n for (const row of cacheRows) {\r\n if (sinceMs !== undefined && row.ts < sinceMs) continue;\r\n records.push(row);\r\n }\r\n return records;\r\n },\r\n};\r\n\r\nexport default antigravityReader;\r\n"],"mappings":"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","names":[]}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
AntigravityAdapter,
|
|
4
|
-
adapter,
|
|
5
|
-
antigravity_default
|
|
6
|
-
} from "./chunk-RPWD5H6Y.js";
|
|
7
|
-
import "./chunk-PSGTHJQU.js";
|
|
8
|
-
import "./chunk-XXLCMSD3.js";
|
|
9
|
-
import "./chunk-UNU2KPYJ.js";
|
|
10
|
-
import "./chunk-W7AGWAMH.js";
|
|
11
|
-
import "./chunk-ILWHLGIC.js";
|
|
12
|
-
import "./chunk-QYOUQCC6.js";
|
|
13
|
-
export {
|
|
14
|
-
AntigravityAdapter,
|
|
15
|
-
adapter,
|
|
16
|
-
antigravity_default as default
|
|
17
|
-
};
|
|
18
|
-
//# sourceMappingURL=antigravity-PZXYAO3X.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/usage/readers/antigravity-cli.ts"],"sourcesContent":["/**\r\n * usage/readers/antigravity-cli — Antigravity CLI (`agy`) usage reader (SYNCED).\r\n *\r\n * CONFIRMED-BY-INSTALL (2026-06-03, docs/research/antigravity-paths-confirmed.md):\r\n * the `agy` CLI v1.0.0 has NO separate config/storage dir — it SHARES the IDE's\r\n * `~/.gemini/antigravity/` tree, whose native conversation store is\r\n * `conversations/<uuid>.pb` (PROTOBUF, no public schema) with `brain/<uuid>/`\r\n * holding only media + `*.metadata.json`. There are NO `transcript*.jsonl` files\r\n * and NO separate `~/.gemini/antigravity-cli/` dir. The earlier \"read native\r\n * brain transcript*.jsonl with usage_metadata\" approach targeted a non-existent\r\n * shape.\r\n *\r\n * So this reader does NOT attempt to parse `.pb` (no schema). Like the IDE reader\r\n * (and the other SYNCED platforms cursor/trae/warp), it reads only the tokscale\r\n * synced-cache if a separate tokscale run already produced one; otherwise it\r\n * returns [] and the scan layer reports \"requires sync (no local cache found)\"\r\n * — i.e. the native store is protobuf (.pb), not readable.\r\n *\r\n * The native `~/.gemini/antigravity/` dir (shared with the IDE) is used ONLY for\r\n * platform detection (in the adapter), NOT for usage parsing here. Cached rows\r\n * carry real host token counts → \"host-reported\".\r\n *\r\n * Fail-open: no cache → []; unreadable/malformed file or line → skipped; we NEVER\r\n * touch `.pb` protobuf.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { join } from \"node:path\";\r\n\r\nimport type { UsageReader, UsageRecord } from \"../types.js\";\r\nimport { emptyTokens } from \"../aggregate.js\";\r\nimport { readJsonlLines } from \"../jsonl.js\";\r\nimport { inferProvider } from \"../normalize.js\";\r\nimport { firstExistingRoot, isDir, walkFiles } from \"../paths.js\";\r\nimport { nonEmptyStr, resolveAlias, toSafeInt } from \"./antigravity-shared.js\";\r\n\r\nconst PLATFORM_ID = \"antigravity-cli\" as const;\r\nconst DEFAULT_MODEL = \"unknown\";\r\nconst DEFAULT_PROVIDER = \"antigravity\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// TOKSCALE CACHE — same line schema as the IDE reader\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Fields we read off a tokscale-cache Antigravity JSONL line (all optional). */\r\ninterface AntigravityLine {\r\n type?: unknown;\r\n modelId?: unknown;\r\n providerId?: unknown;\r\n sessionId?: unknown;\r\n timestamp?: unknown;\r\n input?: unknown;\r\n output?: unknown;\r\n cacheRead?: unknown;\r\n cacheWrite?: unknown;\r\n reasoning?: unknown;\r\n responseId?: unknown;\r\n}\r\n\r\n/** Parse one tokscale-cache JSONL file into usage records (session_meta + usage rows). */\r\nfunction parseCacheFile(path: string): UsageRecord[] {\r\n const lines = readJsonlLines(path);\r\n if (lines.length === 0) return [];\r\n\r\n const out: UsageRecord[] = [];\r\n let sessionModel: string | undefined;\r\n\r\n for (const raw of lines) {\r\n if (typeof raw !== \"object\" || raw === null) continue;\r\n const line = raw as AntigravityLine;\r\n\r\n const rowType = typeof line.type === \"string\" ? line.type : \"\";\r\n if (rowType === \"session_meta\") {\r\n const meta = nonEmptyStr(line.modelId);\r\n if (meta !== undefined) sessionModel = meta;\r\n continue;\r\n }\r\n if (rowType !== \"usage\") continue;\r\n\r\n const record = parseCacheUsageRow(line, sessionModel);\r\n if (record !== undefined) out.push(record);\r\n }\r\n\r\n return out;\r\n}\r\n\r\n/** Build a UsageRecord from a \"usage\" line, or undefined when invalid/empty. */\r\nfunction parseCacheUsageRow(line: AntigravityLine, fallbackModel: string | undefined): UsageRecord | undefined {\r\n const sessionId = nonEmptyStr(line.sessionId);\r\n if (sessionId === undefined) return undefined;\r\n\r\n const timestamp = toSafeInt(line.timestamp);\r\n if (timestamp <= 0) return undefined;\r\n\r\n const rawModel = nonEmptyStr(line.modelId) ?? fallbackModel ?? DEFAULT_MODEL;\r\n const modelId = resolveAlias(rawModel) ?? rawModel;\r\n const providerId = nonEmptyStr(line.providerId) ?? inferProvider(modelId) ?? DEFAULT_PROVIDER;\r\n\r\n const input = toSafeInt(line.input);\r\n const output = toSafeInt(line.output);\r\n const cacheRead = toSafeInt(line.cacheRead);\r\n const cacheWrite = toSafeInt(line.cacheWrite);\r\n const reasoning = toSafeInt(line.reasoning);\r\n if (input === 0 && output === 0 && cacheRead === 0 && cacheWrite === 0 && reasoning === 0) {\r\n return undefined;\r\n }\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 const record: UsageRecord = {\r\n platformId: PLATFORM_ID,\r\n modelId,\r\n providerId,\r\n sessionId,\r\n tokens,\r\n ts: timestamp,\r\n messageCount: 1,\r\n confidence: \"host-reported\",\r\n };\r\n const dedupKey = nonEmptyStr(line.responseId);\r\n if (dedupKey !== undefined) record.dedupKey = dedupKey;\r\n return record;\r\n}\r\n\r\n/** Collect tokscale-cache records (sessions/*.jsonl under the cache root). */\r\nfunction readTokscaleCache(): UsageRecord[] {\r\n const cacheRoot = firstExistingRoot(PLATFORM_ID);\r\n if (cacheRoot === undefined) return [];\r\n\r\n const files = new Set<string>();\r\n const sessionsDir = join(cacheRoot, \"sessions\");\r\n if (isDir(sessionsDir)) {\r\n for (const f of walkFiles(sessionsDir, (name) => name.endsWith(\".jsonl\"))) files.add(f);\r\n }\r\n // Also tolerate loose *.jsonl directly under the cache root.\r\n for (const f of walkFiles(cacheRoot, (name) => name.endsWith(\".jsonl\"))) files.add(f);\r\n\r\n if (files.size === 0) return [];\r\n\r\n const out: UsageRecord[] = [];\r\n for (const file of files) {\r\n if (!existsSync(file)) continue;\r\n for (const row of parseCacheFile(file)) out.push(row);\r\n }\r\n return out;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Reader\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * The Antigravity CLI usage reader singleton (SYNCED). `agy` shares the IDE's\r\n * protobuf (.pb) native store with no public schema → not parseable; we read only\r\n * the tokscale synced-cache if present, else [] (scan reports \"requires sync\").\r\n */\r\nconst antigravityCliReader: UsageReader = {\r\n platformId: PLATFORM_ID,\r\n kind: \"synced\",\r\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\r\n let cacheRows: UsageRecord[];\r\n try {\r\n cacheRows = readTokscaleCache();\r\n } catch {\r\n cacheRows = []; // fail-open\r\n }\r\n\r\n const records: UsageRecord[] = [];\r\n for (const row of cacheRows) {\r\n if (sinceMs !== undefined && row.ts < sinceMs) continue;\r\n records.push(row);\r\n }\r\n return records;\r\n },\r\n};\r\n\r\nexport default antigravityCliReader;\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/antigravity-cli/index.ts"],"sourcesContent":["/**\r\n * adapters/antigravity-cli — Google Antigravity CLI (`agy`) platform adapter.\r\n *\r\n * Antigravity ships in two distinct runtimes that share the Gemini-family\r\n * `~/.gemini` tree and ALL of the same native formats (the JSON `mcp_config.json`\r\n * shape, the `hooks.json` lifecycle-hook shape, markdown Workflows, and Agent\r\n * Skills `SKILL.md`): the IDE/desktop app (the parent `AntigravityAdapter`,\r\n * id \"antigravity\") and this standalone CLI binary `agy` (id \"antigravity-cli\").\r\n *\r\n * This adapter is a thin fork of the IDE adapter. It REUSES every render / hook /\r\n * parse / surface path from {@link AntigravityAdapter} unchanged — the only\r\n * differences are identity and detection:\r\n *\r\n * - id = \"antigravity-cli\", name = \"Antigravity CLI\". Identity flows into the\r\n * universal hook command (`<homeBin> hook antigravity-cli <event> …`) and the\r\n * `hostPlatform` stamp, so a CLI-installed hook dispatches back to THIS\r\n * adapter at runtime — the reason the parent's install/parse logic was made\r\n * to read `this.id` rather than a fixed host constant.\r\n * - Detection probes the `agy` binary at ~/.local/bin/agy and/or the SHARED\r\n * ~/.gemini/antigravity/ presence (distinct runtime markers from the IDE).\r\n *\r\n * CONFIRMED-BY-INSTALL (2026-06-03, docs/research/antigravity-paths-confirmed.md):\r\n * the `agy` CLI v1.0.0 has NO separate config dir — `~/.gemini/antigravity-cli/`,\r\n * `~/.config/antigravity*`, and `~/.agy` are ALL ABSENT. `agy` SHARES the IDE's\r\n * `~/.gemini/antigravity/` tree (mcp_config.json, hooks.json, workflows, skills).\r\n * So the user-scope config resolution here is IDENTICAL to the IDE adapter (the\r\n * inherited USER_CONFIG_CANDIDATES) — installing both the IDE and CLI connectors\r\n * therefore writes the SAME files and is idempotent (observed as skip), which is\r\n * expected and correct. `agy`'s own extension surface is the `agy plugin` system\r\n * (install/uninstall/list/enable/disable) — future work would deploy as an agy\r\n * plugin; for now the MCP/workflows/skills surfaces ride the shared IDE files.\r\n *\r\n * LIVE-TEST RESOLUTION (2026-06-04, real `agy` v1.0.5 login + spawn): the prior\r\n * \"shares the IDE's ~/.gemini/antigravity/ file\" claim was WRONG for the CLI. The\r\n * standalone `agy` reads user MCP from **~/.gemini/config/mcp_config.json**\r\n * (root key `mcpServers`; project scope <proj>/.agents/mcp_config.json) — PROVEN:\r\n * a live `agy -p` session spawned an MCP server placed in `config/` and completed\r\n * a real initialize+tools/list+tools/call handshake; a negative control in\r\n * `antigravity/` was ignored. The binary carries \"failed to read mcp_config.json\"\r\n * + a literal \"/.gemini/config\" path; there is NO `--mcp-config` flag and `agy\r\n * mcp` is TUI-only. So this adapter now OVERRIDES userConfigCandidates to prefer\r\n * `~/.gemini/config/mcp_config.json` (canonical) over the inherited IDE\r\n * `antigravity/` default (kept as a prefer-existing fallback). Telemetry-wrap is\r\n * compatible: agy spawns command+args verbatim, so the home-bin serve wrapper is\r\n * spawned as written — i.e. a real agy session now emits a per-MCP telemetry row.\r\n * (agy also keeps its own home ~/.gemini/antigravity-cli/ for auth/state, but MCP\r\n * config lives in the shared ~/.gemini/config/.)\r\n *\r\n * Project scope is IDENTICAL to the IDE adapter (`<proj>/.agents/…`), as is every\r\n * hook/command/skill render and the runtime parse/format — all inherited.\r\n *\r\n * The user-scope config paths shared with the IDE (hooks.json + the global skills\r\n * dir) remain MEDIUM-CONFIDENCE / PATH-PROBED in the parent (not present on the\r\n * observed install) and surfaced by the doctor; user-scope mcp_config + workflows\r\n * are CONFIRMED at ~/.gemini/antigravity/.\r\n *\r\n * STATUSLINE SURFACE (LIVE-VERIFIED 2026-06-21, agy v1.0.10): the `agy` CLI ships\r\n * a first-party custom status line — `/statusline <command>` configures a shell\r\n * command that agy execs on each refresh, piping a JSON status payload on stdin.\r\n * The config is persisted in the GLOBAL `~/.gemini/antigravity-cli/settings.json`\r\n * under the top-level `statusLine` key, value shape `{ enabled: true, command }`\r\n * — NOT claude-code's `{ type:\"command\", command }` and NOT qwen-code's nested\r\n * `ui.statusLine`. This was confirmed by a real headless `agy -p` turn with the\r\n * statusLine command pointed at a stdin-logging script: the captured payload is\r\n * { cwd, session_id, conversation_id, transcript_path,\r\n * model:{ id, display_name },\r\n * workspace:{ current_dir, project_dir },\r\n * version,\r\n * context_window:{ total_input_tokens, total_output_tokens,\r\n * context_window_size, used_percentage,\r\n * remaining_percentage,\r\n * current_usage:{ input_tokens, output_tokens, … } },\r\n * exceeds_200k_tokens, product, quota, agent_state, vcs, sandbox,\r\n * plan_tier, email, terminal_width }\r\n * Only the antigravity-cli FORK wires this (the IDE app's payload is not verified),\r\n * so the CLI adapter overrides `capabilities` to add `supportsStatusline: true`\r\n * and the statusline parse/format/install/uninstall here — every other path stays\r\n * inherited from {@link AntigravityAdapter}.\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 { AntigravityAdapter, adapter as antigravityIdeAdapter } from \"../antigravity/index.js\";\r\nimport type { Adapter, HookReply, InstallContext } from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookEventName,\r\n JsonValue,\r\n PlatformCapabilities,\r\n PlatformId,\r\n StatuslineContext,\r\n} from \"../../core/types.js\";\r\nimport {\r\n buildHomeBinStatuslineCommand,\r\n isHomeBinStatuslineCommand,\r\n} from \"../../core/spawn.js\";\r\nimport {\r\n type ConfigPatchLedgerEntry,\r\n addLedgerOwner,\r\n createLedgerEntry,\r\n describeJsonValue,\r\n dropLedgerEntry,\r\n findLedgerEntry,\r\n hashJsonValue,\r\n jsonDeepEquals,\r\n ledgerEntriesOwnedBy,\r\n loadConfigPatchLedger,\r\n removeLedgerOwner,\r\n saveConfigPatchLedger,\r\n} from \"../../core/config-patch-ledger.js\";\r\n\r\nconst HOST: PlatformId = \"antigravity-cli\";\r\n\r\n/**\r\n * settings.json key the statusline surface owns on `agy`. The agy CLI stores its\r\n * custom status line under the TOP-LEVEL `statusLine` key (a single segment —\r\n * unlike qwen-code's nested `ui.statusLine`). LIVE-VERIFIED against agy v1.0.10\r\n * (binary StatusLineConfig JSON tags `statusLine`/`command`/`enabled`; a real turn\r\n * accepted and fired this shape). We own the `statusLine` leaf via the SAME\r\n * refcounted ownership ledger as configPatch — never clobbering a `statusLine` a\r\n * user (or another tool) already set.\r\n */\r\nconst STATUSLINE_KEY = \"statusLine\";\r\n\r\n/**\r\n * The `agy` statusLine custom-command stdin payload (the documented status-line\r\n * input agy pipes on stdin, read once). Every field optional — a refresh only\r\n * carries what the host knows. LIVE-CAPTURED (agy v1.0.10, 2026-06-21). Unmodeled\r\n * fields (version, agent_state, vcs, plan_tier, quota, product, email,\r\n * terminal_width) stay in StatuslineContext.raw.\r\n *\r\n * NOTE the divergence from qwen-code: agy's `context_window.current_usage` is an\r\n * OBJECT (`{ input_tokens, output_tokens, … }`), NOT a number — so used tokens are\r\n * derived from the top-level `total_input_tokens + total_output_tokens`, and the\r\n * percent from `used_percentage`.\r\n */\r\ninterface AgyStatuslineInput {\r\n cwd?: string;\r\n session_id?: string;\r\n conversation_id?: string;\r\n transcript_path?: string;\r\n version?: string;\r\n model?: { id?: string; display_name?: string };\r\n workspace?: { current_dir?: string; project_dir?: string };\r\n context_window?: {\r\n total_input_tokens?: number;\r\n total_output_tokens?: number;\r\n context_window_size?: number;\r\n used_percentage?: number;\r\n remaining_percentage?: number;\r\n current_usage?: {\r\n input_tokens?: number;\r\n output_tokens?: number;\r\n cache_creation_input_tokens?: number;\r\n cache_read_input_tokens?: number;\r\n };\r\n };\r\n}\r\n\r\nexport class AntigravityCliAdapter extends AntigravityAdapter implements Adapter {\r\n override readonly id: PlatformId = HOST;\r\n override readonly name = \"Antigravity CLI\";\r\n\r\n /**\r\n * Capabilities = the inherited IDE surface, with the two CLI-specific\r\n * divergences applied on top:\r\n *\r\n * - `supportsStatusline: true` — the `agy` custom status line, live-verified\r\n * above (the IDE app does NOT advertise statusline; its payload is unverified).\r\n * - `sessionStart: false` — the `agy` CLI does NOT recognize a SessionStart\r\n * hook. LIVE-VERIFIED (agy): the binary's recognized hook events are exactly\r\n * PreToolUse / PostToolUse / PreInvocation / PostInvocation / Stop — the\r\n * `/hooks` UI lists only those five and writing both SessionStart + PreToolUse\r\n * into hooks.json loads ONLY PreToolUse. Declaring SessionStart on the IDE\r\n * `antigravity` adapter is left UNCHANGED (this finding is agy-CLI-specific\r\n * and was NOT verified for the IDE app). This flag is the source of truth for\r\n * the docs hooks-matrix + the fleet-wide never-silent-drop contract; the\r\n * matching install-time warn-skip is driven by {@link supportedHookEvents}.\r\n *\r\n * Everything else is spread from the IDE adapter's own capabilities so the two\r\n * surfaces stay in lock-step (a parent field added later flows in automatically).\r\n * `super.capabilities` is unusable here: the parent's `capabilities` is an\r\n * instance FIELD (not on the prototype), so it resolves to undefined in a\r\n * subclass field initializer — spread the IDE singleton instead.\r\n */\r\n override readonly capabilities: PlatformCapabilities = {\r\n ...antigravityIdeAdapter.capabilities,\r\n supportsStatusline: true,\r\n sessionStart: false,\r\n };\r\n\r\n /**\r\n * The hook events the `agy` CLI actually recognizes. LIVE-VERIFIED: agy's hook\r\n * event tokens are exactly PreToolUse / PostToolUse / PreInvocation /\r\n * PostInvocation / Stop — confirmed by the `/hooks` UI listing only those five,\r\n * by the binary's hook-event tokens, and by agy loading ONLY PreToolUse when\r\n * both SessionStart + PreToolUse are written into hooks.json. SessionStart is\r\n * therefore DROPPED: a declared SessionStart hook warn-skips at install rather\r\n * than writing an inert hooks.json entry that never loads / never fires / never\r\n * shows in `/hooks`. NOTE: PreInvocation / PostInvocation are agy-only events\r\n * with no canonical AC analog — left unmapped here (a possible future\r\n * nativeHooks enhancement, NOT this fix). This narrows the IDE's\r\n * {@link AntigravityAdapter.supportedHookEvents} (which still includes\r\n * SessionStart for the IDE app, where this finding was not verified).\r\n */\r\n private static readonly CLI_SUPPORTED_EVENTS: ReadonlySet<HookEventName> =\r\n new Set<HookEventName>([\"PreToolUse\", \"PostToolUse\", \"Stop\"]);\r\n\r\n protected override supportedHookEvents(): ReadonlySet<HookEventName> {\r\n return AntigravityCliAdapter.CLI_SUPPORTED_EVENTS;\r\n }\r\n\r\n // ── Detection ────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Probe the CLI's runtime markers, DISTINCT from the IDE app even though the\r\n * two share `~/.gemini/antigravity/`: the `agy` binary at ~/.local/bin/agy\r\n * (the definitive CLI marker, CONFIRMED present) and the shared\r\n * ~/.gemini/antigravity/ tree (or a user-scope mcp_config candidate / the\r\n * project config). Reported before the IDE adapter in the registry so the\r\n * more-specific CLI marker (the `agy` binary) wins host detection. Note: a\r\n * machine with ONLY the IDE (no `agy` binary) will not match here, while one\r\n * with the CLI matches on the binary regardless of the shared config dir.\r\n */\r\n override detectInstalled(projectDir: string): DetectedPlatform {\r\n const home = homedir();\r\n const userConfig = this.resolveUserConfigPath();\r\n const agyBin = join(home, \".local\", \"bin\", \"agy\");\r\n const sharedDir = join(home, \".gemini\", \"antigravity\");\r\n const projectConfig = join(projectDir, \".agents\", \"mcp_config.json\");\r\n\r\n // The `agy` binary is the definitive CLI marker. The shared antigravity dir\r\n // and the user-scope mcp_config candidates are secondary signals (they also\r\n // match a pure-IDE install, so the binary is what truly distinguishes the CLI).\r\n const cliMarker = existsSync(agyBin);\r\n const userInstalled =\r\n cliMarker ||\r\n existsSync(sharedDir) ||\r\n this.userConfigCandidates().some((parts) => existsSync(join(home, ...parts)));\r\n const projInstalled = existsSync(projectConfig);\r\n const installed = userInstalled || projInstalled;\r\n\r\n // Report the scope/path that actually matched, so a project-only install\r\n // isn't misreported as a (non-existent) user install.\r\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\r\n const configPath = scope === \"project\" ? projectConfig : userConfig;\r\n\r\n return {\r\n id: this.id,\r\n name: this.name,\r\n installed,\r\n paradigm: this.paradigm,\r\n capabilities: this.capabilities,\r\n configPath,\r\n scope,\r\n reason: installed\r\n ? scope === \"project\"\r\n ? `found project Antigravity CLI config at ${projectConfig}`\r\n : `found Antigravity CLI (agy) at ${agyBin} / shared ${sharedDir}`\r\n : `no Antigravity CLI install at ${agyBin}, ${sharedDir}, or ${projectConfig}`,\r\n // The `agy` binary is a high-confidence CLI marker; matching only the shared\r\n // config dir is weaker (it could be a pure-IDE install).\r\n confidence: cliMarker ? \"high\" : installed ? \"medium\" : \"low\",\r\n };\r\n }\r\n\r\n /**\r\n * MCP user-config path — LIVE-PROVEN for `agy` v1.0.5 (2026-06-04). The\r\n * standalone CLI reads user MCP servers from `~/.gemini/config/mcp_config.json`\r\n * (root key `mcpServers`), NOT the IDE's `~/.gemini/antigravity/mcp_config.json`:\r\n * a real `agy -p` session spawned an MCP server ONLY when it was placed in\r\n * `config/` (negative control: a server in `antigravity/` was ignored), and the\r\n * binary carries the error string \"failed to read mcp_config.json\" + a literal\r\n * \"/.gemini/config\" path. `agy mcp` is TUI-only (no headless add) and there is\r\n * no `--mcp-config` flag. So the CLI must PREFER `config/` over the inherited\r\n * `antigravity/` default; the IDE path stays as a prefer-existing fallback.\r\n * (Skills/workflows/hooks dirs remain inherited; project scope\r\n * `<proj>/.agents/mcp_config.json` is already correct and live-proven.)\r\n */\r\n private static readonly CLI_USER_CONFIG_CANDIDATES = [\r\n [\".gemini\", \"config\", \"mcp_config.json\"], // CONFIRMED canonical for agy v1.0.5\r\n [\".gemini\", \"antigravity\", \"mcp_config.json\"], // legacy IDE layout (prefer-existing)\r\n ] as const;\r\n\r\n protected override userConfigCandidates(): ReadonlyArray<readonly string[]> {\r\n return AntigravityCliAdapter.CLI_USER_CONFIG_CANDIDATES;\r\n }\r\n\r\n // ── Statusline surface (the `agy` custom status line) ─────────────────────\r\n // Wires settings.json `statusLine` = { enabled:true, command:<home-bin\r\n // statusline cmd> } through the SAME refcounted ownership ledger as configPatch\r\n // (the ledger primitives are surface-agnostic). antigravity-cli does NOT\r\n // advertise supportsConfigPatch, so — like qwen-code — this adapter performs the\r\n // set-if-absent write directly with the exported ledger primitives. Semantics\r\n // are identical to claude-code/qwen-code: never clobber a `statusLine`\r\n // agent-connector does not own (skip-warn), record prior state + owner\r\n // (refcounted across connectors), reversible by uninstallStatusline\r\n // (last-owner-verified delete).\r\n //\r\n // The home-bin command makes agy exec\r\n // `<homeBin> statusline antigravity-cli --connector <id>` on every refresh,\r\n // which re-imports the connector module and renders the line (runtime/\r\n // statusline-entrypoint). The status line lives in the GLOBAL\r\n // ~/.gemini/antigravity-cli/settings.json (agy has no project-scope statusLine),\r\n // so this surface is user-scope regardless of the install scope. No telemetry\r\n // in v1.\r\n\r\n /**\r\n * The `agy` settings.json that holds the statusLine config. LIVE-VERIFIED at\r\n * ~/.gemini/antigravity-cli/settings.json (the CLI's own global home — distinct\r\n * from the shared ~/.gemini/config/mcp_config.json the MCP surface writes). agy\r\n * stores statusLine as a GLOBAL UI setting with no project-scope variant, so the\r\n * path is scope-independent.\r\n */\r\n private statuslineSettingsPath(): string {\r\n return join(homedir(), \".gemini\", \"antigravity-cli\", \"settings.json\");\r\n }\r\n\r\n /** The statusLine config value agent-connector writes at `statusLine`. */\r\n private statuslineValue(ctx: InstallContext): JsonValue {\r\n const command = buildHomeBinStatuslineCommand(ctx.homeBinPath, HOST, ctx.connector.id);\r\n return { enabled: true, command };\r\n }\r\n\r\n override installStatusline(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.statusline == null) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no statusline\" }];\r\n }\r\n if (connector.platforms[HOST]?.statusline === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: `statusline disabled for ${HOST}` }];\r\n }\r\n\r\n const filePath = this.statuslineSettingsPath();\r\n const symlink = this.symlinkPathWarning(filePath);\r\n if (symlink) return [symlink];\r\n\r\n // OVERWRITE GUARD (upsertServerInJson precedent): never round-trip a\r\n // present-but-unparseable settings file into `{}`.\r\n if (this.isPresentButUnparseable(filePath)) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"warn\",\r\n path: filePath,\r\n detail: `existing ${filePath} is not parseable; statusline left unapplied (back it up / fix it, then re-run)`,\r\n },\r\n ];\r\n }\r\n const settings = this.readJson<Record<string, unknown>>(filePath) ?? {};\r\n if (typeof settings !== \"object\" || Array.isArray(settings)) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"warn\",\r\n path: filePath,\r\n detail: `existing ${filePath} is not a JSON object; statusline left unapplied`,\r\n },\r\n ];\r\n }\r\n\r\n const ledger = loadConfigPatchLedger(ctx.dataRoot);\r\n const desired = this.statuslineValue(ctx);\r\n const leaf = settings[STATUSLINE_KEY];\r\n const entry = findLedgerEntry(ledger, HOST, filePath, STATUSLINE_KEY);\r\n const changes: ChangeRecord[] = [];\r\n\r\n if (leaf === undefined) {\r\n // SET-IF-ABSENT: the one write path.\r\n settings[STATUSLINE_KEY] = desired;\r\n this.writeJson(filePath, settings, ctx.dryRun);\r\n if (entry) {\r\n // Stale ledger row (key deleted out from under us): re-assert the value,\r\n // keep existing owners (they still rely on the key), record what we wrote.\r\n entry.writtenValue = desired;\r\n entry.writtenValueHash = hashJsonValue(desired);\r\n addLedgerOwner(entry, connector.id, connector.version);\r\n } else {\r\n createLedgerEntry(ledger, {\r\n platform: HOST,\r\n file: filePath,\r\n key: STATUSLINE_KEY,\r\n value: desired,\r\n connectorId: connector.id,\r\n connectorVersion: connector.version,\r\n });\r\n }\r\n if (!ctx.dryRun) saveConfigPatchLedger(ctx.dataRoot, ledger);\r\n changes.push({\r\n platform: this.id,\r\n action: \"create\",\r\n path: filePath,\r\n detail: `statusline ${STATUSLINE_KEY}: <absent> → ${describeJsonValue(desired)}`,\r\n });\r\n return changes;\r\n }\r\n\r\n // Key PRESENT — never overwrite; the only question is ownership/refcount.\r\n const leafValue = leaf as JsonValue;\r\n if (!entry) {\r\n // User- (or other-tool-) owned. No ownership is taken even when the values\r\n // happen to match — uninstall must never delete a key we did not create.\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"warn\",\r\n path: filePath,\r\n detail:\r\n `statusline ${STATUSLINE_KEY} skipped: already set to ${describeJsonValue(leafValue)} ` +\r\n `(not created by agent-connector) — left untouched`,\r\n },\r\n ];\r\n }\r\n\r\n if (!jsonDeepEquals(leafValue, entry.writtenValue)) {\r\n // DRIFT: the user edited the value after we wrote it. Never revert.\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"warn\",\r\n path: filePath,\r\n detail:\r\n `statusline ${STATUSLINE_KEY}: value changed since install ` +\r\n `(current ${describeJsonValue(leafValue)}, wrote ${describeJsonValue(entry.writtenValue)}); ` +\r\n `leaving in place`,\r\n },\r\n ];\r\n }\r\n\r\n if (jsonDeepEquals(desired, leafValue)) {\r\n // Same value we own: register as co-owner (refcount++) or idempotent skip.\r\n const owners = entry.owners.map((o) => o.connectorId);\r\n if (addLedgerOwner(entry, connector.id, connector.version)) {\r\n if (!ctx.dryRun) saveConfigPatchLedger(ctx.dataRoot, ledger);\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: filePath,\r\n detail: `statusline ${STATUSLINE_KEY} already installed; registered as co-owner (co-owned with ${owners.join(\", \")})`,\r\n },\r\n ];\r\n }\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: filePath,\r\n detail: `statusline ${STATUSLINE_KEY} already installed`,\r\n },\r\n ];\r\n }\r\n\r\n // FIRST-WRITER-WINS: another connector owns the key with a different value.\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"warn\",\r\n path: filePath,\r\n detail:\r\n `statusline ${STATUSLINE_KEY} skipped: already owned by ${entry.owners\r\n .map((o) => o.connectorId)\r\n .join(\", \")} with a different value — left untouched`,\r\n },\r\n ];\r\n }\r\n\r\n override uninstallStatusline(ctx: InstallContext): ChangeRecord[] {\r\n const ledger = loadConfigPatchLedger(ctx.dataRoot);\r\n // Release ONLY the statusLine ledger row this connector owns (keyed off the\r\n // ledger, not the declaration, so an id-only synthetic uninstall still reclaims\r\n // it). Last-owner-verified delete: remove the key ONLY when last-owner ∧\r\n // value-unchanged ∧ prior-absent (else skip-warn + leave the key).\r\n const owned = ledgerEntriesOwnedBy(ledger, HOST, ctx.connector.id).filter(\r\n (e) => e.key === STATUSLINE_KEY,\r\n );\r\n if (owned.length === 0) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n detail: \"statusline: no ownership recorded; left untouched\",\r\n },\r\n ];\r\n }\r\n\r\n const changes: ChangeRecord[] = [];\r\n let ledgerMutated = false;\r\n\r\n // Group by file (one global settings.json in practice, but stay robust to a\r\n // scope drift between install and uninstall).\r\n const byFile = new Map<string, ConfigPatchLedgerEntry[]>();\r\n for (const entry of owned) {\r\n const bucket = byFile.get(entry.file) ?? [];\r\n bucket.push(entry);\r\n byFile.set(entry.file, bucket);\r\n }\r\n\r\n for (const [filePath, entries] of byFile) {\r\n const symlink = this.symlinkPathWarning(filePath);\r\n if (symlink) {\r\n changes.push(symlink);\r\n continue;\r\n }\r\n\r\n const unparseable = this.isPresentButUnparseable(filePath);\r\n const settings = unparseable ? null : this.readJson<Record<string, unknown>>(filePath);\r\n let fileMutated = false;\r\n\r\n for (const entry of entries) {\r\n const { lastOwner } = removeLedgerOwner(entry, ctx.connector.id);\r\n ledgerMutated = true;\r\n\r\n if (!lastOwner) {\r\n // Shared-flag case: A uninstalls, B still relies on the key.\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path: filePath,\r\n detail: `statusline ${entry.key} retained: still owned by ${entry.owners\r\n .map((o) => o.connectorId)\r\n .join(\", \")}`,\r\n });\r\n continue;\r\n }\r\n\r\n // Last owner out → the ledger row is dropped on every branch below; the\r\n // KEY is removed only on the fully-verified branch.\r\n dropLedgerEntry(ledger, entry);\r\n\r\n if (unparseable) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n path: filePath,\r\n detail: `statusline ${entry.key}: ${filePath} is not parseable; key left in place (ownership released)`,\r\n });\r\n continue;\r\n }\r\n if (!settings || typeof settings !== \"object\" || Array.isArray(settings)) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path: filePath,\r\n detail: `statusline ${entry.key} already absent (no settings file); ownership record dropped`,\r\n });\r\n continue;\r\n }\r\n const leaf = settings[entry.key];\r\n if (leaf === undefined) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path: filePath,\r\n detail: `statusline ${entry.key} already absent; ownership record dropped`,\r\n });\r\n continue;\r\n }\r\n if (entry.prior?.present !== false || !jsonDeepEquals(leaf as JsonValue, entry.writtenValue)) {\r\n // User edited the value after install (or the row predates the\r\n // set-if-absent guarantee): deleting would clobber them. Leave it.\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n path: filePath,\r\n detail:\r\n `statusline ${entry.key}: value changed since install ` +\r\n `(current ${describeJsonValue(leaf as JsonValue)}, wrote ${describeJsonValue(entry.writtenValue)}); ` +\r\n `left in place`,\r\n });\r\n continue;\r\n }\r\n\r\n // VERIFIED: last owner + current === writtenValue + prior absent.\r\n delete settings[entry.key];\r\n fileMutated = true;\r\n changes.push({\r\n platform: this.id,\r\n action: \"remove\",\r\n path: filePath,\r\n detail: `statusline ${entry.key} removed (was ${describeJsonValue(entry.writtenValue)})`,\r\n });\r\n }\r\n\r\n if (fileMutated && settings) this.writeJson(filePath, settings, ctx.dryRun);\r\n }\r\n\r\n if (ledgerMutated && !ctx.dryRun) saveConfigPatchLedger(ctx.dataRoot, ledger);\r\n return changes;\r\n }\r\n\r\n /**\r\n * Parse the `agy` statusLine stdin JSON into the normalized\r\n * {@link StatuslineContext}. agy pipes a JSON object on stdin\r\n * (model/context_window/workspace/…); fields the payload omits stay undefined.\r\n * `raw` keeps the verbatim payload (incl. version/agent_state/vcs/plan_tier/\r\n * quota/email/terminal_width). agy has no cost analog, so `ctx.cost` is left\r\n * undefined. LIVE-CAPTURED against agy v1.0.10 (see header / AgyStatuslineInput).\r\n */\r\n parseStatusInput(raw: unknown): StatuslineContext {\r\n const input = (raw ?? {}) as AgyStatuslineInput;\r\n\r\n const ctx: StatuslineContext = {\r\n host: HOST,\r\n capabilities: this.capabilities,\r\n raw,\r\n };\r\n // Stable session id: prefer conversation_id (the active agent conversation),\r\n // fall back to session_id (the capture showed them equal).\r\n const sessionId =\r\n typeof input.conversation_id === \"string\" && input.conversation_id !== \"\"\r\n ? input.conversation_id\r\n : typeof input.session_id === \"string\" && input.session_id !== \"\"\r\n ? input.session_id\r\n : undefined;\r\n if (sessionId !== undefined) ctx.sessionId = sessionId;\r\n // cwd: prefer the top-level cwd, else the workspace.current_dir.\r\n const cwd =\r\n typeof input.cwd === \"string\"\r\n ? input.cwd\r\n : typeof input.workspace?.current_dir === \"string\"\r\n ? input.workspace.current_dir\r\n : undefined;\r\n if (cwd !== undefined) ctx.cwd = cwd;\r\n if (typeof input.transcript_path === \"string\") ctx.transcriptPath = input.transcript_path;\r\n\r\n const model: { id?: string; displayName?: string } = {};\r\n if (typeof input.model?.id === \"string\") model.id = input.model.id;\r\n if (typeof input.model?.display_name === \"string\") model.displayName = input.model.display_name;\r\n if (model.id !== undefined || model.displayName !== undefined) ctx.model = model;\r\n\r\n const cw = input.context_window;\r\n if (cw) {\r\n const context: { usedTokens?: number; maxTokens?: number; percent?: number } = {};\r\n if (typeof cw.context_window_size === \"number\") context.maxTokens = cw.context_window_size;\r\n // agy's current_usage is an OBJECT (not a number like qwen) — used tokens are\r\n // the documented top-level total_input_tokens + total_output_tokens sum.\r\n if (\r\n typeof cw.total_input_tokens === \"number\" ||\r\n typeof cw.total_output_tokens === \"number\"\r\n ) {\r\n context.usedTokens = (cw.total_input_tokens ?? 0) + (cw.total_output_tokens ?? 0);\r\n }\r\n if (typeof cw.used_percentage === \"number\") context.percent = cw.used_percentage;\r\n if (\r\n context.usedTokens !== undefined ||\r\n context.maxTokens !== undefined ||\r\n context.percent !== undefined\r\n ) {\r\n ctx.context = context;\r\n }\r\n }\r\n // ctx.cost stays undefined: agy's status payload has no cost analog.\r\n return ctx;\r\n }\r\n\r\n /** Format the rendered status line into agy's native reply: stdout = line, exit 0. */\r\n formatStatusOutput(rendered: string): HookReply {\r\n return { exitCode: 0, stdout: rendered };\r\n }\r\n\r\n // ── Diagnostics: add the statusline check on top of the inherited IDE checks ─\r\n\r\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\r\n const checks = [...super.getHealthChecks(ctx)];\r\n\r\n // Statusline check: assert it when the connector declares a statusline AND it\r\n // is not disabled for this host, OR when the ownership ledger holds a\r\n // statusLine row this connector owns (the REGISTERED-connector path:\r\n // connectorFromMeta can't re-expose the render fn, so statusline comes back\r\n // undefined — but the ledger row proves the surface was wired). Mirrors\r\n // claude-code/qwen-code's \"statusline wired\" check.\r\n const settingsPath = this.statuslineSettingsPath();\r\n const connectorId = ctx.connector.id;\r\n const homeBin = ctx.homeBinPath;\r\n const statuslineLedgerOwned = ledgerEntriesOwnedBy(\r\n loadConfigPatchLedger(ctx.dataRoot),\r\n HOST,\r\n connectorId,\r\n ).some((e) => e.key === STATUSLINE_KEY);\r\n if (\r\n (ctx.connector.statusline != null || statuslineLedgerOwned) &&\r\n ctx.connector.platforms[HOST]?.statusline !== false\r\n ) {\r\n checks.push({\r\n name: `${this.name}: statusline wired`,\r\n check: () => {\r\n const settings = this.readJson<{ statusLine?: { command?: unknown } }>(settingsPath);\r\n const command = settings?.statusLine?.command;\r\n if (command === undefined) {\r\n return { status: \"FAIL\", detail: `statusLine not set in ${settingsPath}` };\r\n }\r\n if (\r\n typeof command === \"string\" &&\r\n isHomeBinStatuslineCommand(command, homeBin, connectorId)\r\n ) {\r\n return { status: \"OK\", detail: \"statusLine command present\" };\r\n }\r\n // Present but not ours — a non-AC statusLine we must never clobber.\r\n return {\r\n status: \"FAIL\",\r\n detail: `statusLine in ${settingsPath} is not agent-connector's (left untouched)`,\r\n };\r\n },\r\n });\r\n }\r\n return checks;\r\n }\r\n}\r\n\r\nexport const adapter = new AntigravityCliAdapter();\r\nexport default adapter;\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgFA,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,YAAY;AAiCrB,IAAM,OAAmB;AAWzB,IAAM,iBAAiB;AAqChB,IAAM,wBAAN,MAAM,+BAA8B,mBAAsC;AAAA,EAC7D,KAAiB;AAAA,EACjB,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBP,eAAqC;AAAA,IACrD,GAAG,QAAsB;AAAA,IACzB,oBAAoB;AAAA,IACpB,cAAc;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,OAAwB,uBACtB,oBAAI,IAAmB,CAAC,cAAc,eAAe,MAAM,CAAC;AAAA,EAE3C,sBAAkD;AACnE,WAAO,uBAAsB;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcS,gBAAgB,YAAsC;AAC7D,UAAM,OAAO,QAAQ;AACrB,UAAM,aAAa,KAAK,sBAAsB;AAC9C,UAAM,SAAS,KAAK,MAAM,UAAU,OAAO,KAAK;AAChD,UAAM,YAAY,KAAK,MAAM,WAAW,aAAa;AACrD,UAAM,gBAAgB,KAAK,YAAY,WAAW,iBAAiB;AAKnE,UAAM,YAAY,WAAW,MAAM;AACnC,UAAM,gBACJ,aACA,WAAW,SAAS,KACpB,KAAK,qBAAqB,EAAE,KAAK,CAAC,UAAU,WAAW,KAAK,MAAM,GAAG,KAAK,CAAC,CAAC;AAC9E,UAAM,gBAAgB,WAAW,aAAa;AAC9C,UAAM,YAAY,iBAAiB;AAInC,UAAM,QAAQ,iBAAiB,CAAC,gBAAgB,YAAY;AAC5D,UAAM,aAAa,UAAU,YAAY,gBAAgB;AAEzD,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,MACX;AAAA,MACA,UAAU,KAAK;AAAA,MACf,cAAc,KAAK;AAAA,MACnB;AAAA,MACA;AAAA,MACA,QAAQ,YACJ,UAAU,YACR,2CAA2C,aAAa,KACxD,kCAAkC,MAAM,aAAa,SAAS,KAChE,iCAAiC,MAAM,KAAK,SAAS,QAAQ,aAAa;AAAA;AAAA;AAAA,MAG9E,YAAY,YAAY,SAAS,YAAY,WAAW;AAAA,IAC1D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,OAAwB,6BAA6B;AAAA,IACnD,CAAC,WAAW,UAAU,iBAAiB;AAAA;AAAA,IACvC,CAAC,WAAW,eAAe,iBAAiB;AAAA;AAAA,EAC9C;AAAA,EAEmB,uBAAyD;AAC1E,WAAO,uBAAsB;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BQ,yBAAiC;AACvC,WAAO,KAAK,QAAQ,GAAG,WAAW,mBAAmB,eAAe;AAAA,EACtE;AAAA;AAAA,EAGQ,gBAAgB,KAAgC;AACtD,UAAM,UAAU,8BAA8B,IAAI,aAAa,MAAM,IAAI,UAAU,EAAE;AACrF,WAAO,EAAE,SAAS,MAAM,QAAQ;AAAA,EAClC;AAAA,EAES,kBAAkB,KAAqC;AAC9D,UAAM,EAAE,UAAU,IAAI;AACtB,QAAI,UAAU,cAAc,MAAM;AAChC,aAAO,CAAC,EAAE,UAAU,KAAK,IAAI,QAAQ,QAAQ,QAAQ,mCAAmC,CAAC;AAAA,IAC3F;AACA,QAAI,UAAU,UAAU,IAAI,GAAG,eAAe,OAAO;AACnD,aAAO,CAAC,EAAE,UAAU,KAAK,IAAI,QAAQ,QAAQ,QAAQ,2BAA2B,IAAI,GAAG,CAAC;AAAA,IAC1F;AAEA,UAAM,WAAW,KAAK,uBAAuB;AAC7C,UAAM,UAAU,KAAK,mBAAmB,QAAQ;AAChD,QAAI,QAAS,QAAO,CAAC,OAAO;AAI5B,QAAI,KAAK,wBAAwB,QAAQ,GAAG;AAC1C,aAAO;AAAA,QACL;AAAA,UACE,UAAU,KAAK;AAAA,UACf,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,QAAQ,YAAY,QAAQ;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AACA,UAAM,WAAW,KAAK,SAAkC,QAAQ,KAAK,CAAC;AACtE,QAAI,OAAO,aAAa,YAAY,MAAM,QAAQ,QAAQ,GAAG;AAC3D,aAAO;AAAA,QACL;AAAA,UACE,UAAU,KAAK;AAAA,UACf,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,QAAQ,YAAY,QAAQ;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAEA,UAAM,SAAS,sBAAsB,IAAI,QAAQ;AACjD,UAAM,UAAU,KAAK,gBAAgB,GAAG;AACxC,UAAM,OAAO,SAAS,cAAc;AACpC,UAAM,QAAQ,gBAAgB,QAAQ,MAAM,UAAU,cAAc;AACpE,UAAM,UAA0B,CAAC;AAEjC,QAAI,SAAS,QAAW;AAEtB,eAAS,cAAc,IAAI;AAC3B,WAAK,UAAU,UAAU,UAAU,IAAI,MAAM;AAC7C,UAAI,OAAO;AAGT,cAAM,eAAe;AACrB,cAAM,mBAAmB,cAAc,OAAO;AAC9C,uBAAe,OAAO,UAAU,IAAI,UAAU,OAAO;AAAA,MACvD,OAAO;AACL,0BAAkB,QAAQ;AAAA,UACxB,UAAU;AAAA,UACV,MAAM;AAAA,UACN,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa,UAAU;AAAA,UACvB,kBAAkB,UAAU;AAAA,QAC9B,CAAC;AAAA,MACH;AACA,UAAI,CAAC,IAAI,OAAQ,uBAAsB,IAAI,UAAU,MAAM;AAC3D,cAAQ,KAAK;AAAA,QACX,UAAU,KAAK;AAAA,QACf,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,QAAQ,cAAc,cAAc,qBAAgB,kBAAkB,OAAO,CAAC;AAAA,MAChF,CAAC;AACD,aAAO;AAAA,IACT;AAGA,UAAM,YAAY;AAClB,QAAI,CAAC,OAAO;AAGV,aAAO;AAAA,QACL;AAAA,UACE,UAAU,KAAK;AAAA,UACf,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,QACE,cAAc,cAAc,4BAA4B,kBAAkB,SAAS,CAAC;AAAA,QAExF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,eAAe,WAAW,MAAM,YAAY,GAAG;AAElD,aAAO;AAAA,QACL;AAAA,UACE,UAAU,KAAK;AAAA,UACf,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,QACE,cAAc,cAAc,0CAChB,kBAAkB,SAAS,CAAC,WAAW,kBAAkB,MAAM,YAAY,CAAC;AAAA,QAE5F;AAAA,MACF;AAAA,IACF;AAEA,QAAI,eAAe,SAAS,SAAS,GAAG;AAEtC,YAAM,SAAS,MAAM,OAAO,IAAI,CAAC,MAAM,EAAE,WAAW;AACpD,UAAI,eAAe,OAAO,UAAU,IAAI,UAAU,OAAO,GAAG;AAC1D,YAAI,CAAC,IAAI,OAAQ,uBAAsB,IAAI,UAAU,MAAM;AAC3D,eAAO;AAAA,UACL;AAAA,YACE,UAAU,KAAK;AAAA,YACf,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,QAAQ,cAAc,cAAc,6DAA6D,OAAO,KAAK,IAAI,CAAC;AAAA,UACpH;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,QACL;AAAA,UACE,UAAU,KAAK;AAAA,UACf,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,QAAQ,cAAc,cAAc;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AAGA,WAAO;AAAA,MACL;AAAA,QACE,UAAU,KAAK;AAAA,QACf,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,QACE,cAAc,cAAc,8BAA8B,MAAM,OAC7D,IAAI,CAAC,MAAM,EAAE,WAAW,EACxB,KAAK,IAAI,CAAC;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAAA,EAES,oBAAoB,KAAqC;AAChE,UAAM,SAAS,sBAAsB,IAAI,QAAQ;AAKjD,UAAM,QAAQ,qBAAqB,QAAQ,MAAM,IAAI,UAAU,EAAE,EAAE;AAAA,MACjE,CAAC,MAAM,EAAE,QAAQ;AAAA,IACnB;AACA,QAAI,MAAM,WAAW,GAAG;AACtB,aAAO;AAAA,QACL;AAAA,UACE,UAAU,KAAK;AAAA,UACf,QAAQ;AAAA,UACR,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAEA,UAAM,UAA0B,CAAC;AACjC,QAAI,gBAAgB;AAIpB,UAAM,SAAS,oBAAI,IAAsC;AACzD,eAAW,SAAS,OAAO;AACzB,YAAM,SAAS,OAAO,IAAI,MAAM,IAAI,KAAK,CAAC;AAC1C,aAAO,KAAK,KAAK;AACjB,aAAO,IAAI,MAAM,MAAM,MAAM;AAAA,IAC/B;AAEA,eAAW,CAAC,UAAU,OAAO,KAAK,QAAQ;AACxC,YAAM,UAAU,KAAK,mBAAmB,QAAQ;AAChD,UAAI,SAAS;AACX,gBAAQ,KAAK,OAAO;AACpB;AAAA,MACF;AAEA,YAAM,cAAc,KAAK,wBAAwB,QAAQ;AACzD,YAAM,WAAW,cAAc,OAAO,KAAK,SAAkC,QAAQ;AACrF,UAAI,cAAc;AAElB,iBAAW,SAAS,SAAS;AAC3B,cAAM,EAAE,UAAU,IAAI,kBAAkB,OAAO,IAAI,UAAU,EAAE;AAC/D,wBAAgB;AAEhB,YAAI,CAAC,WAAW;AAEd,kBAAQ,KAAK;AAAA,YACX,UAAU,KAAK;AAAA,YACf,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,QAAQ,cAAc,MAAM,GAAG,6BAA6B,MAAM,OAC/D,IAAI,CAAC,MAAM,EAAE,WAAW,EACxB,KAAK,IAAI,CAAC;AAAA,UACf,CAAC;AACD;AAAA,QACF;AAIA,wBAAgB,QAAQ,KAAK;AAE7B,YAAI,aAAa;AACf,kBAAQ,KAAK;AAAA,YACX,UAAU,KAAK;AAAA,YACf,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,QAAQ,cAAc,MAAM,GAAG,KAAK,QAAQ;AAAA,UAC9C,CAAC;AACD;AAAA,QACF;AACA,YAAI,CAAC,YAAY,OAAO,aAAa,YAAY,MAAM,QAAQ,QAAQ,GAAG;AACxE,kBAAQ,KAAK;AAAA,YACX,UAAU,KAAK;AAAA,YACf,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,QAAQ,cAAc,MAAM,GAAG;AAAA,UACjC,CAAC;AACD;AAAA,QACF;AACA,cAAM,OAAO,SAAS,MAAM,GAAG;AAC/B,YAAI,SAAS,QAAW;AACtB,kBAAQ,KAAK;AAAA,YACX,UAAU,KAAK;AAAA,YACf,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,QAAQ,cAAc,MAAM,GAAG;AAAA,UACjC,CAAC;AACD;AAAA,QACF;AACA,YAAI,MAAM,OAAO,YAAY,SAAS,CAAC,eAAe,MAAmB,MAAM,YAAY,GAAG;AAG5F,kBAAQ,KAAK;AAAA,YACX,UAAU,KAAK;AAAA,YACf,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,QACE,cAAc,MAAM,GAAG,0CACX,kBAAkB,IAAiB,CAAC,WAAW,kBAAkB,MAAM,YAAY,CAAC;AAAA,UAEpG,CAAC;AACD;AAAA,QACF;AAGA,eAAO,SAAS,MAAM,GAAG;AACzB,sBAAc;AACd,gBAAQ,KAAK;AAAA,UACX,UAAU,KAAK;AAAA,UACf,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,QAAQ,cAAc,MAAM,GAAG,iBAAiB,kBAAkB,MAAM,YAAY,CAAC;AAAA,QACvF,CAAC;AAAA,MACH;AAEA,UAAI,eAAe,SAAU,MAAK,UAAU,UAAU,UAAU,IAAI,MAAM;AAAA,IAC5E;AAEA,QAAI,iBAAiB,CAAC,IAAI,OAAQ,uBAAsB,IAAI,UAAU,MAAM;AAC5E,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,iBAAiB,KAAiC;AAChD,UAAM,QAAS,OAAO,CAAC;AAEvB,UAAM,MAAyB;AAAA,MAC7B,MAAM;AAAA,MACN,cAAc,KAAK;AAAA,MACnB;AAAA,IACF;AAGA,UAAM,YACJ,OAAO,MAAM,oBAAoB,YAAY,MAAM,oBAAoB,KACnE,MAAM,kBACN,OAAO,MAAM,eAAe,YAAY,MAAM,eAAe,KAC3D,MAAM,aACN;AACR,QAAI,cAAc,OAAW,KAAI,YAAY;AAE7C,UAAM,MACJ,OAAO,MAAM,QAAQ,WACjB,MAAM,MACN,OAAO,MAAM,WAAW,gBAAgB,WACtC,MAAM,UAAU,cAChB;AACR,QAAI,QAAQ,OAAW,KAAI,MAAM;AACjC,QAAI,OAAO,MAAM,oBAAoB,SAAU,KAAI,iBAAiB,MAAM;AAE1E,UAAM,QAA+C,CAAC;AACtD,QAAI,OAAO,MAAM,OAAO,OAAO,SAAU,OAAM,KAAK,MAAM,MAAM;AAChE,QAAI,OAAO,MAAM,OAAO,iBAAiB,SAAU,OAAM,cAAc,MAAM,MAAM;AACnF,QAAI,MAAM,OAAO,UAAa,MAAM,gBAAgB,OAAW,KAAI,QAAQ;AAE3E,UAAM,KAAK,MAAM;AACjB,QAAI,IAAI;AACN,YAAM,UAAyE,CAAC;AAChF,UAAI,OAAO,GAAG,wBAAwB,SAAU,SAAQ,YAAY,GAAG;AAGvE,UACE,OAAO,GAAG,uBAAuB,YACjC,OAAO,GAAG,wBAAwB,UAClC;AACA,gBAAQ,cAAc,GAAG,sBAAsB,MAAM,GAAG,uBAAuB;AAAA,MACjF;AACA,UAAI,OAAO,GAAG,oBAAoB,SAAU,SAAQ,UAAU,GAAG;AACjE,UACE,QAAQ,eAAe,UACvB,QAAQ,cAAc,UACtB,QAAQ,YAAY,QACpB;AACA,YAAI,UAAU;AAAA,MAChB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,mBAAmB,UAA6B;AAC9C,WAAO,EAAE,UAAU,GAAG,QAAQ,SAAS;AAAA,EACzC;AAAA;AAAA,EAIS,gBAAgB,KAA6C;AACpE,UAAM,SAAS,CAAC,GAAG,MAAM,gBAAgB,GAAG,CAAC;AAQ7C,UAAM,eAAe,KAAK,uBAAuB;AACjD,UAAM,cAAc,IAAI,UAAU;AAClC,UAAM,UAAU,IAAI;AACpB,UAAM,wBAAwB;AAAA,MAC5B,sBAAsB,IAAI,QAAQ;AAAA,MAClC;AAAA,MACA;AAAA,IACF,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,cAAc;AACtC,SACG,IAAI,UAAU,cAAc,QAAQ,0BACrC,IAAI,UAAU,UAAU,IAAI,GAAG,eAAe,OAC9C;AACA,aAAO,KAAK;AAAA,QACV,MAAM,GAAG,KAAK,IAAI;AAAA,QAClB,OAAO,MAAM;AACX,gBAAM,WAAW,KAAK,SAAiD,YAAY;AACnF,gBAAM,UAAU,UAAU,YAAY;AACtC,cAAI,YAAY,QAAW;AACzB,mBAAO,EAAE,QAAQ,QAAQ,QAAQ,yBAAyB,YAAY,GAAG;AAAA,UAC3E;AACA,cACE,OAAO,YAAY,YACnB,2BAA2B,SAAS,SAAS,WAAW,GACxD;AACA,mBAAO,EAAE,QAAQ,MAAM,QAAQ,6BAA6B;AAAA,UAC9D;AAEA,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,QAAQ,iBAAiB,YAAY;AAAA,UACvC;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACF;AAEO,IAAMA,WAAU,IAAI,sBAAsB;AACjD,IAAO,0BAAQA;","names":["adapter"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/logger.ts"],"sourcesContent":["/**\r\n * core/logger — minimal leveled logger. Writes human output to stderr so stdout\r\n * stays clean for machine-readable payloads (hook replies, --json output).\r\n */\r\n\r\nexport type LogLevel = \"silent\" | \"error\" | \"warn\" | \"info\" | \"debug\";\r\n\r\nconst ORDER: Record<LogLevel, number> = {\r\n silent: 0,\r\n error: 1,\r\n warn: 2,\r\n info: 3,\r\n debug: 4,\r\n};\r\n\r\nfunction currentLevel(): LogLevel {\r\n const env = (process.env.AGENT_CONNECTOR_LOG ?? \"info\").toLowerCase();\r\n return (env in ORDER ? env : \"info\") as LogLevel;\r\n}\r\n\r\nfunction emit(level: Exclude<LogLevel, \"silent\">, args: unknown[]): void {\r\n if (ORDER[currentLevel()] < ORDER[level]) return;\r\n const prefix = { error: \"✗\", warn: \"!\", info: \"›\", debug: \"·\" }[level];\r\n // eslint-disable-next-line no-console\r\n console.error(prefix, ...args);\r\n}\r\n\r\nexport const log = {\r\n error: (...args: unknown[]) => emit(\"error\", args),\r\n warn: (...args: unknown[]) => emit(\"warn\", args),\r\n info: (...args: unknown[]) => emit(\"info\", args),\r\n debug: (...args: unknown[]) => emit(\"debug\", args),\r\n};\r\n"],"mappings":";;;AAOA,IAAM,QAAkC;AAAA,EACtC,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEA,SAAS,eAAyB;AAChC,QAAM,OAAO,QAAQ,IAAI,uBAAuB,QAAQ,YAAY;AACpE,SAAQ,OAAO,QAAQ,MAAM;AAC/B;AAEA,SAAS,KAAK,OAAoC,MAAuB;AACvE,MAAI,MAAM,aAAa,CAAC,IAAI,MAAM,KAAK,EAAG;AAC1C,QAAM,SAAS,EAAE,OAAO,UAAK,MAAM,KAAK,MAAM,UAAK,OAAO,OAAI,EAAE,KAAK;AAErE,UAAQ,MAAM,QAAQ,GAAG,IAAI;AAC/B;AAEO,IAAM,MAAM;AAAA,EACjB,OAAO,IAAI,SAAoB,KAAK,SAAS,IAAI;AAAA,EACjD,MAAM,IAAI,SAAoB,KAAK,QAAQ,IAAI;AAAA,EAC/C,MAAM,IAAI,SAAoB,KAAK,QAAQ,IAAI;AAAA,EAC/C,OAAO,IAAI,SAAoB,KAAK,SAAS,IAAI;AACnD;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sdk/test-harness.ts"],"sourcesContent":["/**\r\n * sdk/test-harness — the SHIPPED, OFFLINE behavioral harness.\r\n *\r\n * Two read-only tools a connector author runs WITHOUT installing anything or\r\n * touching the host's filesystem:\r\n *\r\n * • `explain(connector)` — the static per-host × per-DECLARED-surface matrix:\r\n * for every registered host and every surface the connector actually ships,\r\n * is it `native` (the host can honor it), `disabled` (the connector opted\r\n * out for that host via `platforms[host].<surface> === false`), or\r\n * `skip-warn` (the host cannot honor it → install would skip-warn)?\r\n *\r\n * • `simulate(connector, opts)` — runs the REAL adapter parse → handler →\r\n * format chain INLINE against a host-shaped raw payload and reports whether\r\n * the host actually HONORS the connector's response (the trustworthy part:\r\n * it surfaces the documented degradations, e.g. Codex silently dropping a\r\n * UserPromptSubmit \"context\" injection that has no stdout path).\r\n *\r\n * It deliberately does NOT import the runtime entrypoints (runHook /\r\n * runStatusline) — those load the REGISTERED connector from disk and record\r\n * telemetry. The harness instead re-implements the tiny parse→handler→format\r\n * chain against the in-memory connector + the adapter, mirroring the runtime's\r\n * input tolerance (empty/malformed payload → `{}`) and its fail-safe envelope.\r\n */\r\n\r\nimport type {\r\n HookEventName,\r\n HookResponse,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PlatformOverride,\r\n ResolvedConnector,\r\n} from \"../core/types.js\";\r\nimport { allAdapters, loadAdapter } from \"../adapters/registry.js\";\r\nimport type { NormalizedEvent } from \"../adapters/spi.js\";\r\nimport {\r\n compileMatcher,\r\n eventMatcherSubject,\r\n} from \"../runtime/hook-entrypoint.js\";\r\nimport type { SurfaceName } from \"./introspect.js\";\r\nimport { hostCanFireEvent, SURFACE_PREDICATES } from \"./introspect.js\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// explain — the static per-host × per-declared-surface matrix\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** One cell of the {@link explain} matrix: a host × a declared surface. */\r\nexport interface ExplainRow {\r\n host: PlatformId;\r\n surface: SurfaceName;\r\n /**\r\n * - \"native\" — the host's capabilities pass the surface predicate.\r\n * - \"disabled\" — the connector opted out for this host\r\n * (`platforms[host].<surface> === false`).\r\n * - \"skip-warn\" — the host cannot honor it; install would skip-warn.\r\n */\r\n support: \"native\" | \"skip-warn\" | \"disabled\";\r\n reason: string;\r\n}\r\n\r\n/**\r\n * The GLOBAL surfaces a connector declares (these apply to every host — only\r\n * declared surfaces get a matrix row). The two platform-scoped surfaces\r\n * (`configPatch` / `nativeHooks`) are deliberately EXCLUDED here: they are\r\n * declared under one specific `platforms[host]`, so each is added PER HOST in\r\n * {@link explain} (see {@link platformScopedSurfacesFor}) — adding them globally\r\n * would falsely promise a configPatch row on hosts that never declared it.\r\n */\r\nfunction declaredGlobalSurfaces(connector: ResolvedConnector): SurfaceName[] {\r\n const out: SurfaceName[] = [];\r\n if (connector.server) out.push(\"server\");\r\n if (Object.keys(connector.hooks).length > 0) out.push(\"hooks\");\r\n if (connector.commands.length > 0) out.push(\"commands\");\r\n if (connector.skills.length > 0) out.push(\"skills\");\r\n if (connector.subagents.length > 0) out.push(\"subagents\");\r\n if (connector.memory.length > 0) out.push(\"memory\");\r\n if (connector.statusline) out.push(\"statusline\");\r\n if (connector.actions.length > 0) out.push(\"actions\");\r\n return out;\r\n}\r\n\r\n/**\r\n * The platform-scoped surfaces (`configPatch` / `nativeHooks`) THIS host\r\n * actually declares — keyed off the host's own `platforms[host]` override, so a\r\n * configPatch declared under claude-code never shows up as a row for codex.\r\n */\r\nfunction platformScopedSurfacesFor(\r\n override: PlatformOverride | undefined,\r\n): SurfaceName[] {\r\n const out: SurfaceName[] = [];\r\n if (!override) return out;\r\n if ((override.configPatch?.length ?? 0) > 0) out.push(\"configPatch\");\r\n if (override.nativeHooks != null && Object.keys(override.nativeHooks).length > 0) {\r\n out.push(\"nativeHooks\");\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Is the surface explicitly DISABLED for this host? Mirrors the per-host opt-out\r\n * the installer honors: a boolean-or-object surface override is \"disabled\" only\r\n * when set to the literal `false` (the object form is a tuning, not an opt-out).\r\n * `configPatch` / `nativeHooks` have no boolean opt-out form — declaring them is\r\n * the opt-in, so they are never \"disabled\".\r\n */\r\nfunction isDisabledForHost(\r\n connector: ResolvedConnector,\r\n host: PlatformId,\r\n surface: SurfaceName,\r\n): boolean {\r\n const override = connector.platforms[host];\r\n if (!override) return false;\r\n switch (surface) {\r\n case \"server\":\r\n return override.server === false;\r\n case \"hooks\":\r\n return override.hooks === false;\r\n case \"commands\":\r\n return override.commands === false;\r\n case \"skills\":\r\n return override.skills === false;\r\n case \"subagents\":\r\n return override.subagents === false;\r\n case \"memory\":\r\n return override.memory === false;\r\n case \"statusline\":\r\n return override.statusline === false;\r\n case \"actions\":\r\n case \"configPatch\":\r\n case \"nativeHooks\":\r\n // No per-host boolean opt-out form (declaring them is the opt-in).\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * The static support matrix: every registered host × every surface the\r\n * connector declares. Rows are sorted by host then surface for stable output.\r\n * Pure read of the adapter capabilities — installs nothing, writes nothing.\r\n */\r\nexport async function explain(connector: ResolvedConnector): Promise<ExplainRow[]> {\r\n const globalSurfaces = declaredGlobalSurfaces(connector);\r\n const adapters = await allAdapters();\r\n const rows: ExplainRow[] = [];\r\n\r\n for (const adapter of adapters) {\r\n // Per-host surface set = the global surfaces every host gets a row for, plus\r\n // the platform-scoped surfaces THIS host actually declares (configPatch /\r\n // nativeHooks live under platforms[host], so they are host-local).\r\n const override = connector.platforms[adapter.id];\r\n const surfaces = [\r\n ...globalSurfaces,\r\n ...platformScopedSurfacesFor(override),\r\n ];\r\n for (const surface of surfaces) {\r\n if (isDisabledForHost(connector, adapter.id, surface)) {\r\n rows.push({\r\n host: adapter.id,\r\n surface,\r\n support: \"disabled\",\r\n reason: `connector opted out for ${adapter.id} (platforms.${adapter.id}.${surface} = false)`,\r\n });\r\n continue;\r\n }\r\n // The hooks surface is judged against the connector's SPECIFIC declared\r\n // events, not the coarse \"host has a hook runtime\" predicate — otherwise a\r\n // Stop-only connector falsely shows `native` on a PreToolUse-only host\r\n // (the host CAN fire a hook, just not the one this connector declared).\r\n if (surface === \"hooks\") {\r\n rows.push(hooksRow(connector, adapter.id, adapter.capabilities));\r\n continue;\r\n }\r\n const native = SURFACE_PREDICATES[surface](adapter.capabilities);\r\n rows.push({\r\n host: adapter.id,\r\n surface,\r\n support: native ? \"native\" : \"skip-warn\",\r\n reason: explainReason(adapter.id, surface, native),\r\n });\r\n }\r\n }\r\n\r\n rows.sort((a, b) =>\r\n a.host === b.host ? a.surface.localeCompare(b.surface) : a.host.localeCompare(b.host),\r\n );\r\n return rows;\r\n}\r\n\r\n/**\r\n * The per-DECLARED-event hooks verdict for one host. A connector's hooks surface\r\n * is `native` only when EVERY event it declares can fire on the host; if any\r\n * declared event has no native equivalent the row is `skip-warn` and the reason\r\n * names the specific dead events (the same events install skip-warns). This is\r\n * the fix for the OR-across-all-events false green: a Stop-only connector on a\r\n * host that cannot fire Stop now reports skip-warn, not native.\r\n *\r\n * Reuses {@link hostCanFireEvent} — the single per-event source of truth shared\r\n * with {@link SURFACE_PREDICATES} and simulate's runtime verdict — so explain\r\n * never re-derives the per-event rules.\r\n */\r\nfunction hooksRow(\r\n connector: ResolvedConnector,\r\n host: PlatformId,\r\n capabilities: PlatformCapabilities,\r\n): ExplainRow {\r\n const declared = connector.hookEvents;\r\n const dead = declared.filter((e) => !hostCanFireEvent(capabilities, e));\r\n if (dead.length === 0) {\r\n return {\r\n host,\r\n surface: \"hooks\",\r\n support: \"native\",\r\n reason: `${host} can fire every declared hook event (${declared.join(\", \")})`,\r\n };\r\n }\r\n const fireable = declared.filter((e) => hostCanFireEvent(capabilities, e));\r\n const reason =\r\n fireable.length === 0\r\n ? `${host} cannot fire any declared hook event (${dead.join(\", \")}); install skip-warns each`\r\n : `${host} cannot fire ${dead.join(\", \")} (install skip-warns these); honors ${fireable.join(\", \")}`;\r\n return { host, surface: \"hooks\", support: \"skip-warn\", reason };\r\n}\r\n\r\n/**\r\n * The human-readable reason for a native/skip-warn cell. `server` is a special\r\n * case: the predicate is CAPABILITY-based (the host advertises ≥1 MCP\r\n * transport), but advertising a transport does NOT guarantee a writable\r\n * registration — e.g. jetbrains-copilot advertises stdio yet its installServer\r\n * skip-warns because MCP is managed in IDE settings. So the server reason states\r\n * the verdict is capability-based and registration may be host-managed, rather\r\n * than promising a write. (A precise per-adapter installServer dry-run is a\r\n * Phase-2 enhancement — until then this avoids over-promising.)\r\n */\r\nfunction explainReason(host: PlatformId, surface: SurfaceName, native: boolean): string {\r\n if (surface === \"server\") {\r\n return native\r\n ? `${host} advertises an MCP transport (capability-based; registration may be host-managed)`\r\n : `${host} advertises no MCP transport; install skip-warns`;\r\n }\r\n return native\r\n ? `${host} natively supports ${surface}`\r\n : `${host} cannot honor ${surface}; install skip-warns`;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// simulate — the real adapter parse → handler → format chain, inline\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** What to drive through the harness: one host, one surface, one raw payload. */\r\nexport interface SimulateOptions {\r\n surface: \"hooks\" | \"statusline\";\r\n host: PlatformId | string;\r\n /** REQUIRED when `surface === \"hooks\"`: which normalized lifecycle event. */\r\n event?: HookEventName;\r\n /** The host-shaped RAW payload (what the host would pipe in on stdin). */\r\n input: unknown;\r\n}\r\n\r\n/** The harness verdict: did the host actually honor the connector's response? */\r\nexport interface SimulateResult {\r\n /** True when the host's native reply carries the connector's intent. */\r\n honored: boolean;\r\n /** The host-native stdout the adapter would emit (when there is one). */\r\n hostReply?: string;\r\n /** Human-readable why — names the documented degradation when honored:false. */\r\n reason: string;\r\n}\r\n\r\n/**\r\n * Tolerantly coerce the raw payload, mirroring the runtime's parseStdin: a\r\n * string is JSON-parsed (empty/malformed → `{}`); a nullish value → `{}`; any\r\n * other value (already-parsed object) is passed through untouched.\r\n */\r\nfunction coerceInput(input: unknown): unknown {\r\n if (input == null) return {};\r\n if (typeof input === \"string\") {\r\n const trimmed = input.trim();\r\n if (trimmed === \"\") return {};\r\n try {\r\n return JSON.parse(trimmed);\r\n } catch {\r\n return {};\r\n }\r\n }\r\n return input;\r\n}\r\n\r\n/** Normalize a handler's return into a concrete response (void/non-object → `{}`). */\r\nfunction normalizeResponse(value: HookResponse | void): HookResponse {\r\n if (value == null || typeof value !== \"object\") return {};\r\n return value;\r\n}\r\n\r\n/**\r\n * The decoded shape of a host's native hook reply on stdout. Every json-stdio\r\n * adapter writes a SUBSET of these fields (claude-code / codex share the\r\n * `hookSpecificOutput` envelope; Stop-class continuation uses the TOP-LEVEL\r\n * `decision:\"block\"`). We read the DECODED fields — never substring-match the\r\n * serialized JSON, which a path/newline/quote in the payload would break.\r\n */\r\ninterface ParsedReply {\r\n /** Top-level Stop-class continuation: {\"decision\":\"block\",\"reason\"}. */\r\n decision?: unknown;\r\n reason?: unknown;\r\n /** Top-level Claude context channel (no adapter emits it yet; future-proofing). */\r\n systemMessage?: unknown;\r\n hookSpecificOutput?: {\r\n hookEventName?: unknown;\r\n additionalContext?: unknown;\r\n permissionDecision?: unknown;\r\n permissionDecisionReason?: unknown;\r\n updatedInput?: unknown;\r\n /** PermissionRequest nested envelope: decision:{behavior:\"allow\"|\"deny\", updatedInput?}. */\r\n decision?: { behavior?: unknown; updatedInput?: unknown };\r\n };\r\n}\r\n\r\n/**\r\n * Tolerantly decode a reply's stdout into {@link ParsedReply}. Empty / absent /\r\n * unparseable stdout is a legitimate \"no payload\" signal (the adapter wrote a\r\n * bare exit-0) → `undefined`, the honest \"this decision had no stdout path\"\r\n * verdict input.\r\n */\r\nfunction parseReplyStdout(stdout: string | undefined): ParsedReply | undefined {\r\n if (stdout == null || stdout.trim() === \"\") return undefined;\r\n try {\r\n const value = JSON.parse(stdout);\r\n return value != null && typeof value === \"object\"\r\n ? (value as ParsedReply)\r\n : undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/** Structural deep-equal for plain JSON values (objects/arrays/primitives). */\r\nfunction deepEqual(a: unknown, b: unknown): boolean {\r\n if (a === b) return true;\r\n if (typeof a !== typeof b || a == null || b == null) return false;\r\n if (typeof a !== \"object\") return false;\r\n const aArr = Array.isArray(a);\r\n if (aArr !== Array.isArray(b)) return false;\r\n if (aArr) {\r\n const x = a as unknown[];\r\n const y = b as unknown[];\r\n return x.length === y.length && x.every((v, i) => deepEqual(v, y[i]));\r\n }\r\n const x = a as Record<string, unknown>;\r\n const y = b as Record<string, unknown>;\r\n const xk = Object.keys(x);\r\n const yk = Object.keys(y);\r\n return xk.length === yk.length && xk.every((k) => deepEqual(x[k], y[k]));\r\n}\r\n\r\n/**\r\n * Decide whether the host HONORED a decision-bearing response, judging the\r\n * PARSED reply against each adapter's ACTUAL (event, decision) contract — never\r\n * by substring-matching the serialized JSON. `allow`/no-decision is always\r\n * pass-through honored; every other decision is honored only when the decoded\r\n * reply carries that exact intent, and the verdict names the documented\r\n * degradation when it does not.\r\n */\r\nfunction judgeHookHonor(\r\n host: string,\r\n event: HookEventName,\r\n response: HookResponse,\r\n stdout: string | undefined,\r\n): { honored: boolean; reason: string } {\r\n const decision = response.decision;\r\n if (decision === undefined || decision === \"allow\") {\r\n return { honored: true, reason: \"pass-through allow\" };\r\n }\r\n\r\n const reply = parseReplyStdout(stdout);\r\n const hso = reply?.hookSpecificOutput;\r\n\r\n if (decision === \"context\") {\r\n // Honored only when the DECODED additionalContext equals what the handler\r\n // asked to inject (claude-code's hookSpecificOutput.additionalContext, or a\r\n // future top-level systemMessage). codex only emits this on\r\n // SessionStart/PostToolUse — every other event silently drops it.\r\n const want = response.additionalContext;\r\n const got = hso?.additionalContext ?? reply?.systemMessage;\r\n if (reply && got === want) {\r\n return { honored: true, reason: `${host} injects context on ${event}` };\r\n }\r\n return {\r\n honored: false,\r\n reason: `${host} drops context on ${event} (no stdout path)`,\r\n };\r\n }\r\n\r\n if (decision === \"deny\") {\r\n if (!reply) {\r\n return {\r\n honored: false,\r\n reason: `${host} drops deny on ${event} (deny not honored; fails open to allow)`,\r\n };\r\n }\r\n // A real block: PreToolUse's permissionDecision:\"deny\", or the\r\n // PermissionRequest nested decision.behavior:\"deny\".\r\n if (hso?.permissionDecision === \"deny\" || hso?.decision?.behavior === \"deny\") {\r\n return { honored: true, reason: `${host} blocks ${event}` };\r\n }\r\n // Stop-class continuation: top-level decision:\"block\" KEEPS the session /\r\n // subagent running (persistence) — the SEMANTIC OPPOSITE of a block. Honored\r\n // (the reply carried the intent), but the reason must not say \"blocks\".\r\n if (reply.decision === \"block\") {\r\n return {\r\n honored: true,\r\n reason: `${host} continues the session/subagent on ${event} (persistence) — NOT a block`,\r\n };\r\n }\r\n // SubagentStart / PostToolUseFailure DEGRADE a deny to a context note — the\r\n // spawn/failure is not actually blocked.\r\n if (hso?.additionalContext != null) {\r\n return {\r\n honored: false,\r\n reason: `${host} degrades deny to a context note on ${event}; spawn/failure not blocked`,\r\n };\r\n }\r\n return {\r\n honored: false,\r\n reason: `${host} drops deny on ${event} (deny not honored; fails open to allow)`,\r\n };\r\n }\r\n\r\n if (decision === \"ask\") {\r\n if (hso?.permissionDecision === \"ask\") {\r\n return { honored: true, reason: `${host} asks on ${event}` };\r\n }\r\n // On PermissionRequest, ask/void falls through to the host's NATIVE dialog\r\n // (the dialog IS the ask), which the adapter expresses as a bare exit-0.\r\n if (event === \"PermissionRequest\" && !reply) {\r\n return {\r\n honored: true,\r\n reason: `${host} shows the native confirmation dialog on ${event} (the ask)`,\r\n };\r\n }\r\n return {\r\n honored: false,\r\n reason: `${host} drops ask on ${event} (no ask path)`,\r\n };\r\n }\r\n\r\n // decision === \"modify\": honored only when the decoded updatedInput deep-equals\r\n // what the handler asked to write (hookSpecificOutput.updatedInput, or the\r\n // PermissionRequest nested decision.updatedInput).\r\n const wrote = hso?.updatedInput ?? hso?.decision?.updatedInput;\r\n if (reply && wrote !== undefined && deepEqual(wrote, response.updatedInput)) {\r\n return { honored: true, reason: `${host} rewrites tool input on ${event}` };\r\n }\r\n return {\r\n honored: false,\r\n reason: `${host} drops modify on ${event} (modify not honored)`,\r\n };\r\n}\r\n\r\n/**\r\n * Run the connector against ONE host-shaped payload through the REAL adapter\r\n * path and report whether the host honors the response. Self-contained (does not\r\n * touch the registered-connector loader or telemetry) and fail-safe: any throw\r\n * in the chain degrades to `honored:false` with the error message, never rejects.\r\n */\r\nexport async function simulate(\r\n connector: ResolvedConnector,\r\n opts: SimulateOptions,\r\n): Promise<SimulateResult> {\r\n try {\r\n const adapter = await loadAdapter(opts.host);\r\n if (!adapter) {\r\n return { honored: false, reason: `unknown host \"${opts.host}\"` };\r\n }\r\n\r\n if (opts.surface === \"statusline\") {\r\n if (\r\n !(adapter.capabilities.supportsStatusline ?? false) ||\r\n !adapter.parseStatusInput ||\r\n !adapter.formatStatusOutput\r\n ) {\r\n return { honored: false, reason: `${opts.host} has no statusline surface` };\r\n }\r\n // Guarding on the TOP-LEVEL render is sufficient: a `hosts`-only statusline\r\n // (a hosts: map with no top-level render) is rejected at defineConnector, so\r\n // a connector that targets `opts.host` only via hosts[] still has a top-level\r\n // render here. (If that author-time invariant ever relaxes, revisit this.)\r\n if (!connector.statusline || typeof connector.statusline.render !== \"function\") {\r\n return { honored: false, reason: \"connector declares no statusline\" };\r\n }\r\n const ctx = adapter.parseStatusInput(coerceInput(opts.input));\r\n ctx.connectorId = connector.id;\r\n // Defensive: parseStatusInput now sets ctx.capabilities (it is required on\r\n // StatuslineContext), but a future adapter could omit it — backfill from\r\n // the adapter so render() can always read ctx.capabilities.\r\n if (ctx.capabilities == null) ctx.capabilities = adapter.capabilities;\r\n // Per-host render override, mirroring the runtime: hosts[host].render wins\r\n // over the top-level render; an unlisted host falls back to top-level.\r\n const perHost = connector.statusline.hosts?.[opts.host as PlatformId]?.render;\r\n const render =\r\n typeof perHost === \"function\" ? perHost : connector.statusline.render;\r\n const rendered = await render(ctx);\r\n const line = rendered == null ? \"\" : String(rendered);\r\n const reply = adapter.formatStatusOutput(line);\r\n return {\r\n honored: true,\r\n ...(reply.stdout !== undefined ? { hostReply: reply.stdout } : {}),\r\n reason: \"rendered\",\r\n };\r\n }\r\n\r\n // surface === \"hooks\"\r\n const event = opts.event;\r\n if (!event) {\r\n return { honored: false, reason: \"event is required when surface === \\\"hooks\\\"\" };\r\n }\r\n if (!adapter.parseEvent || !adapter.formatReply) {\r\n return { honored: false, reason: `${opts.host} has no hook runtime (mcp-only)` };\r\n }\r\n\r\n const evt = adapter.parseEvent(event, coerceInput(opts.input));\r\n evt.connectorId = connector.id;\r\n // Mirror the runtime: runHook backfills evt.capabilities from the adapter so\r\n // a handler reading evt.capabilities behaves the same here. (scope/telemetry\r\n // are runtime-only — recovered from the registered meta / store, which the\r\n // offline harness deliberately never touches — so they stay undefined.)\r\n evt.capabilities = adapter.capabilities;\r\n\r\n // Guarding on the TOP-LEVEL handler is sufficient: a `hosts`-only hook (a\r\n // hosts: map with no top-level handler) is rejected at defineConnector, so a\r\n // host targeted only via hosts[] still has a top-level handler here. (If that\r\n // author-time invariant ever relaxes, revisit this.)\r\n const definition = connector.hooks[event];\r\n if (!definition || typeof definition.handler !== \"function\") {\r\n return { honored: false, reason: `connector declares no ${event} handler` };\r\n }\r\n\r\n // Matcher filtering, in lockstep with runHook: when the connector's matcher\r\n // is set and the event's subject (tool name, or agent type for Subagent*\r\n // events) does not match, the host never runs the handler — so the harness\r\n // must NOT run it either, and reports the (honored) exclusion. Reuses the\r\n // runtime helpers directly so the two stay byte-for-byte aligned.\r\n const subject = eventMatcherSubject(evt as NormalizedEvent);\r\n if (subject !== undefined) {\r\n const re = compileMatcher(definition.matcher);\r\n if (re && !re.test(subject)) {\r\n return {\r\n honored: true,\r\n reason: `matcher excludes ${subject} (handler not run)`,\r\n };\r\n }\r\n }\r\n\r\n // Per-host handler override, mirroring runHook: hosts[host].handler wins\r\n // over the top-level handler; an unlisted host falls back to top-level.\r\n const perHost = definition.hosts?.[opts.host as PlatformId]?.handler;\r\n const handler = typeof perHost === \"function\" ? perHost : definition.handler;\r\n const response = normalizeResponse(\r\n await handler(evt as never),\r\n );\r\n const reply = adapter.formatReply(event, response);\r\n const verdict = judgeHookHonor(String(opts.host), event, response, reply.stdout);\r\n return {\r\n honored: verdict.honored,\r\n ...(reply.stdout !== undefined ? { hostReply: reply.stdout } : {}),\r\n reason: verdict.reason,\r\n };\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n const verb = opts.surface === \"statusline\" ? \"render\" : \"handler\";\r\n return { honored: false, reason: `${verb} threw: ${message}` };\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// explainHooks — the per-(host, event) honor verdict (powers doctor --explain)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * One per-(host, event) cell of {@link explainHooks}.\r\n *\r\n * - \"honored\" — the host fires the event AND carries the connector's response\r\n * intent (simulate's runtime verdict, e.g. a real deny/block).\r\n * - \"degraded\" — the host fires the event but silently DROPS the response's\r\n * decision (e.g. codex fires UserPromptSubmit but has no stdout\r\n * path for a context injection, so the injection vanishes).\r\n * - \"dropped\" — the host cannot fire the event at all (no native equivalent /\r\n * mcp-only host); install skip-warns it and it never runs.\r\n */\r\nexport interface HookEventVerdict {\r\n host: PlatformId;\r\n event: HookEventName;\r\n verdict: \"honored\" | \"degraded\" | \"dropped\";\r\n /** The simulate()-grade reason string (names the documented degradation). */\r\n reason: string;\r\n}\r\n\r\n/**\r\n * A subject (tool name / agent type) that SATISFIES the connector's matcher for\r\n * this event, so a matcher-gated hook actually runs and the verdict reflects the\r\n * real honor — not a spurious \"matcher excludes my arbitrary synthetic tool\".\r\n * A plain-literal matcher (no regex metacharacters — the common case, e.g.\r\n * `acme_query`) IS the subject. A missing/empty matcher matches everything, and\r\n * a complex regex falls back to the generic subject (then an honest \"matcher\r\n * excludes\" verdict surfaces — the dev's matcher genuinely would not fire here).\r\n */\r\nfunction matchingSubject(matcher: string | undefined, fallback: string): string {\r\n if (!matcher || matcher === \"\") return fallback;\r\n return /^[\\w.-]+$/.test(matcher) ? matcher : fallback;\r\n}\r\n\r\n/**\r\n * A minimal host-shaped synthetic payload so the real parse→handler→format\r\n * chain has something to run on. The harness coerces a `{}` to a valid event,\r\n * but tool/subagent events read a subject (tool name / agent type) the connector\r\n * matcher may filter on — so we plant a subject that SATISFIES the connector's\r\n * declared matcher ({@link matchingSubject}) to exercise the honor path.\r\n */\r\nfunction syntheticPayloadFor(\r\n event: HookEventName,\r\n matcher: string | undefined,\r\n): Record<string, unknown> {\r\n switch (event) {\r\n case \"PreToolUse\":\r\n case \"PostToolUse\":\r\n case \"PostToolUseFailure\":\r\n case \"PermissionRequest\": {\r\n const tool = matchingSubject(matcher, \"Bash\");\r\n return { tool_name: tool, tool_input: { command: \"echo hi\" } };\r\n }\r\n case \"SubagentStart\":\r\n case \"SubagentStop\": {\r\n const agent = matchingSubject(matcher, \"general\");\r\n return { agent_type: agent, subagent_type: agent };\r\n }\r\n case \"UserPromptSubmit\":\r\n return { prompt: \"hello\" };\r\n default:\r\n return {};\r\n }\r\n}\r\n\r\n/**\r\n * The per-(host, event) hook honor matrix for the connector's DECLARED events\r\n * across `hosts` — the trustworthy detail behind `doctor --explain`.\r\n *\r\n * For each (host, declared event): when the host cannot fire the event\r\n * ({@link hostCanFireEvent} is false, OR the host has no hook runtime at all)\r\n * the cell is `dropped`; otherwise it RUNS the real chain via {@link simulate}\r\n * and reports `honored` (simulate honored the response) or `degraded` (the host\r\n * fired but dropped the decision). Reuses the per-event signal + simulate's\r\n * verdict — no per-event honor logic is re-derived here.\r\n */\r\nexport async function explainHooks(\r\n connector: ResolvedConnector,\r\n hosts: (PlatformId | string)[],\r\n): Promise<HookEventVerdict[]> {\r\n const events = connector.hookEvents;\r\n const rows: HookEventVerdict[] = [];\r\n\r\n for (const host of hosts) {\r\n const adapter = await loadAdapter(host);\r\n if (!adapter) {\r\n for (const event of events) {\r\n rows.push({\r\n host: host as PlatformId,\r\n event,\r\n verdict: \"dropped\",\r\n reason: `unknown host \"${host}\"`,\r\n });\r\n }\r\n continue;\r\n }\r\n\r\n // An mcp-only host (no hook runtime) drops every declared event.\r\n const hasHookRuntime = !!adapter.parseEvent && !!adapter.formatReply;\r\n\r\n for (const event of events) {\r\n if (!hasHookRuntime) {\r\n rows.push({\r\n host: adapter.id,\r\n event,\r\n verdict: \"dropped\",\r\n reason: `${adapter.id} has no hook runtime (mcp-only) — ${event} never fires`,\r\n });\r\n continue;\r\n }\r\n if (!hostCanFireEvent(adapter.capabilities, event)) {\r\n rows.push({\r\n host: adapter.id,\r\n event,\r\n verdict: \"dropped\",\r\n reason: `${adapter.id} has no ${event} equivalent — install skip-warns it; never fires`,\r\n });\r\n continue;\r\n }\r\n const sim = await simulate(connector, {\r\n surface: \"hooks\",\r\n host: adapter.id,\r\n event,\r\n input: syntheticPayloadFor(event, connector.hooks[event]?.matcher),\r\n });\r\n rows.push({\r\n host: adapter.id,\r\n event,\r\n verdict: sim.honored ? \"honored\" : \"degraded\",\r\n reason: sim.reason,\r\n });\r\n }\r\n }\r\n\r\n rows.sort((a, b) =>\r\n a.host === b.host ? a.event.localeCompare(b.event) : a.host.localeCompare(b.host),\r\n );\r\n return rows;\r\n}\r\n"],"mappings":"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","names":["x","y","perHost","reply"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/marketplace-state.ts"],"sourcesContent":["/**\r\n * core/marketplace-state — pure read/write helpers for MARKETPLACE-method\r\n * install state. A LEAF module (fs + paths only, no adapters, no spawning) so\r\n * the installer, the marketplace orchestration, doctor, and status can all\r\n * import it without cycles.\r\n *\r\n * Two kinds of state live here:\r\n *\r\n * 1. The per-connector state record `connectorDir(id)/marketplace-installs.json`\r\n * — one entry per platform a connector was marketplace-installed on\r\n * ({ format, bundleDir, marketplace, scope, version, contentHash,\r\n * installedAt }). It drives `uninstall --method auto`, upgrade staleness\r\n * warnings, doctor, and the double-install guard.\r\n *\r\n * 2. Read-only fs EVIDENCE probes of the hosts' own plugin state (e.g.\r\n * Claude Code's `$CLAUDE_CONFIG_DIR/plugins/installed_plugins.json`).\r\n * These catch installs the user performed manually from `package`'s\r\n * printed instructions, which the state record alone would miss. Probes\r\n * read undocumented host internals, so every reader degrades to null/false\r\n * on absence or parse failure — the guard convention is \"refuse only on\r\n * positive evidence\" (docs: design risk 4).\r\n */\r\n\r\nimport { createHash } from \"node:crypto\";\r\nimport {\r\n existsSync,\r\n readdirSync,\r\n readFileSync,\r\n realpathSync,\r\n rmSync,\r\n statSync,\r\n writeFileSync,\r\n} from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { dirname, join, relative, resolve } from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\n\r\nimport TOML from \"@iarna/toml\";\r\n\r\nimport type { InstallScope, PlatformId } from \"./types.js\";\r\nimport type { PackageFormat } from \"./package.js\";\r\nimport {\r\n assertNoSymlinkInPath,\r\n connectorDir,\r\n connectorsDir,\r\n dataRoot,\r\n ensureDir,\r\n} from \"./paths.js\";\r\nimport { codexConfigHome, xdgConfigHome } from \"./host-paths.js\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// State record: connectorDir(id)/marketplace-installs.json\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** The catalog name every staged local marketplace is registered under. */\r\nexport const MARKETPLACE_NAME = \"agent-connector\";\r\n\r\n/** One marketplace install of one connector on one platform. */\r\nexport interface MarketplaceInstallRecord {\r\n /** Bundle format that was staged + installed (e.g. \"claude-plugin\"). */\r\n format: PackageFormat;\r\n /** Absolute path of the staged plugin dir the host references/copied. */\r\n bundleDir: string;\r\n /** Marketplace/catalog name the install is keyed under ({@link MARKETPLACE_NAME}). */\r\n marketplace: string;\r\n /** Install scope (v1: always \"user\"). */\r\n scope: InstallScope;\r\n /** connector.version at install time (staleness warnings key off this). */\r\n version: string;\r\n /** sha256 over the staged plugin dir at install time (drift detection). */\r\n contentHash: string;\r\n /** ISO timestamp of the recorded install. */\r\n installedAt: string;\r\n}\r\n\r\n/** platform → record map persisted per connector. */\r\nexport type MarketplaceInstalls = Partial<\r\n Record<PlatformId, MarketplaceInstallRecord>\r\n>;\r\n\r\n/** Path of the per-connector marketplace state record. */\r\nexport function marketplaceInstallsPath(connectorId: string): string {\r\n return join(connectorDir(connectorId), \"marketplace-installs.json\");\r\n}\r\n\r\n/** Read the state record ({} on absence/parse failure — never throws). */\r\nexport function readMarketplaceInstalls(connectorId: string): MarketplaceInstalls {\r\n try {\r\n const raw = readFileSync(marketplaceInstallsPath(connectorId), \"utf8\");\r\n const parsed = JSON.parse(raw) as unknown;\r\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\r\n return parsed as MarketplaceInstalls;\r\n }\r\n } catch {\r\n /* absent / unreadable → empty */\r\n }\r\n return {};\r\n}\r\n\r\n/** Write the state record; an empty map DELETES the file (uninstall leaves no husk). */\r\nexport function writeMarketplaceInstalls(\r\n connectorId: string,\r\n installs: MarketplaceInstalls,\r\n): void {\r\n const path = marketplaceInstallsPath(connectorId);\r\n assertNoSymlinkInPath(path);\r\n if (Object.keys(installs).length === 0) {\r\n rmSync(path, { force: true });\r\n return;\r\n }\r\n ensureDir(dirname(path));\r\n writeFileSync(path, `${JSON.stringify(installs, null, 2)}\\n`, \"utf8\");\r\n}\r\n\r\n/** True when THIS connector has any recorded marketplace install. */\r\nexport function connectorHasMarketplaceRecords(connectorId: string): boolean {\r\n return Object.keys(readMarketplaceInstalls(connectorId)).length > 0;\r\n}\r\n\r\n/**\r\n * True when ANY registered connector (optionally excluding one) still records a\r\n * marketplace install. Guards home-bin removal on purge: an installed plugin's\r\n * hooks exec the home-bin OUTSIDE any adapter-managed config, so removing the\r\n * launcher while a marketplace install survives would silently kill its hooks.\r\n */\r\nexport function anyMarketplaceRecordsRemain(excludeConnectorId?: string): boolean {\r\n const dir = connectorsDir();\r\n if (!existsSync(dir)) return false;\r\n let entries: string[];\r\n try {\r\n entries = readdirSync(dir);\r\n } catch {\r\n return false;\r\n }\r\n return entries.some(\r\n (id) => id !== excludeConnectorId && connectorHasMarketplaceRecords(id),\r\n );\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Staging roots — bundles staged by-reference must survive cwd changes and\r\n// `rm -rf dist-plugin`, so they live under the data-root, one shared root per\r\n// host family, holding every marketplace-installed connector's bundle plus ONE\r\n// regenerated catalog named \"agent-connector\" listing them all.\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Root for every marketplace staging tree: <dataRoot>/marketplace. */\r\nexport function marketplaceRoot(): string {\r\n return join(dataRoot(), \"marketplace\");\r\n}\r\n\r\n/** Shared Claude-family staging root: <dataRoot>/marketplace/claude. */\r\nexport function claudeStagingRoot(): string {\r\n return join(marketplaceRoot(), \"claude\");\r\n}\r\n\r\n/** Shared Codex-family staging root: <dataRoot>/marketplace/codex. */\r\nexport function codexStagingRoot(): string {\r\n return join(marketplaceRoot(), \"codex\");\r\n}\r\n\r\n/** Shared GitHub Copilot CLI staging root: <dataRoot>/marketplace/copilot. */\r\nexport function copilotStagingRoot(): string {\r\n return join(marketplaceRoot(), \"copilot\");\r\n}\r\n\r\n/** Shared agy (Antigravity) staging root: <dataRoot>/marketplace/agy. */\r\nexport function agyStagingRoot(): string {\r\n return join(marketplaceRoot(), \"agy\");\r\n}\r\n\r\n/** Shared Gemini-CLI staging root: <dataRoot>/marketplace/gemini. */\r\nexport function geminiStagingRoot(): string {\r\n return join(marketplaceRoot(), \"gemini\");\r\n}\r\n\r\n/** Shared Qwen-Code staging root: <dataRoot>/marketplace/qwen. */\r\nexport function qwenStagingRoot(): string {\r\n return join(marketplaceRoot(), \"qwen\");\r\n}\r\n\r\n/** Shared droid (Factory) staging root: <dataRoot>/marketplace/droid. */\r\nexport function droidStagingRoot(): string {\r\n return join(marketplaceRoot(), \"droid\");\r\n}\r\n\r\n/**\r\n * Shared npm-local staging root: <dataRoot>/marketplace/npm. Every npm-plugin\r\n * host (opencode / kilo / kilo-cli) stages here; the host config's `plugin`\r\n * array references the bundle by `file://<this>/<id>`, so it MUST live under the\r\n * stable data-root (it survives cwd changes and `rm -rf dist-plugin`).\r\n */\r\nexport function npmStagingRoot(): string {\r\n return join(marketplaceRoot(), \"npm\");\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Claude Code host-state readers ($CLAUDE_CONFIG_DIR || ~/.claude)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Claude Code's config dir, honoring the CLAUDE_CONFIG_DIR override. */\r\nexport function claudeConfigDir(): string {\r\n const override = process.env.CLAUDE_CONFIG_DIR;\r\n if (override && override.trim() !== \"\") return resolve(override);\r\n return join(homedir(), \".claude\");\r\n}\r\n\r\n/** The installed_plugins.json key our marketplace install creates for `id`. */\r\nexport function claudePluginKey(connectorId: string): string {\r\n return `${connectorId}@${MARKETPLACE_NAME}`;\r\n}\r\n\r\nfunction readJsonFile(path: string): Record<string, unknown> | null {\r\n try {\r\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\r\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\r\n return parsed as Record<string, unknown>;\r\n }\r\n } catch {\r\n /* absent / unreadable → null */\r\n }\r\n return null;\r\n}\r\n\r\n/** The `plugins` map from Claude's installed_plugins.json (null when unreadable). */\r\nexport function readClaudeInstalledPlugins(): Record<string, unknown> | null {\r\n const file = join(claudeConfigDir(), \"plugins\", \"installed_plugins.json\");\r\n const parsed = readJsonFile(file);\r\n const plugins = parsed?.plugins;\r\n if (plugins && typeof plugins === \"object\" && !Array.isArray(plugins)) {\r\n return plugins as Record<string, unknown>;\r\n }\r\n return null;\r\n}\r\n\r\n/** True when Claude's own state lists `<id>@agent-connector` as installed. */\r\nexport function claudePluginInstalled(connectorId: string): boolean {\r\n const plugins = readClaudeInstalledPlugins();\r\n if (!plugins) return false;\r\n const entry = plugins[claudePluginKey(connectorId)];\r\n return Array.isArray(entry) ? entry.length > 0 : entry != null;\r\n}\r\n\r\n/** True when ANY `*@agent-connector` plugin remains in Claude's state. */\r\nexport function anyClaudeAgentConnectorPlugins(): boolean {\r\n const plugins = readClaudeInstalledPlugins();\r\n if (!plugins) return false;\r\n return Object.entries(plugins).some(\r\n ([key, entry]) =>\r\n key.endsWith(`@${MARKETPLACE_NAME}`) &&\r\n (Array.isArray(entry) ? entry.length > 0 : entry != null),\r\n );\r\n}\r\n\r\n/**\r\n * The directory Claude's known_marketplaces.json records for `name`, or null\r\n * when not registered / unreadable. Used both as a presence probe and as the\r\n * NAME-COLLISION check: a registration pointing somewhere other than our\r\n * staging root belongs to the user and must never be touched.\r\n */\r\nexport function claudeKnownMarketplacePath(name: string): string | null {\r\n const file = join(claudeConfigDir(), \"plugins\", \"known_marketplaces.json\");\r\n const parsed = readJsonFile(file);\r\n const entry = parsed?.[name];\r\n if (!entry || typeof entry !== \"object\") return null;\r\n const e = entry as { installLocation?: unknown; source?: { path?: unknown } };\r\n if (typeof e.installLocation === \"string\") return e.installLocation;\r\n const sourcePath = e.source?.path;\r\n return typeof sourcePath === \"string\" ? sourcePath : null;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Codex host-state readers ($CODEX_HOME || ~/.codex → config.toml)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** The config.toml key our marketplace install creates for `id` (== claude's). */\r\nexport function codexPluginKey(connectorId: string): string {\r\n return `${connectorId}@${MARKETPLACE_NAME}`;\r\n}\r\n\r\n/** Parse <CODEX_HOME>/config.toml ({} on absence/parse failure — never throws). */\r\nfunction readCodexConfig(): Record<string, unknown> {\r\n const file = join(codexConfigHome(), \"config.toml\");\r\n try {\r\n return TOML.parse(readFileSync(file, \"utf8\")) as Record<string, unknown>;\r\n } catch {\r\n return {};\r\n }\r\n}\r\n\r\n/**\r\n * True when Codex's config.toml carries the `[plugins.\"<id>@agent-connector\"]`\r\n * table. The DEFINITIVE install probe — empty `plugins/cache/...` dirs linger\r\n * after uninstall, so the cache dir must NOT be used (docs: codex quirk).\r\n */\r\nexport function codexPluginInstalled(connectorId: string): boolean {\r\n const cfg = readCodexConfig();\r\n const plugins = cfg[\"plugins\"];\r\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\r\n return codexPluginKey(connectorId) in (plugins as Record<string, unknown>);\r\n}\r\n\r\n/** True when ANY `*@agent-connector` plugin remains in Codex's config.toml. */\r\nexport function anyCodexAgentConnectorPlugins(): boolean {\r\n const cfg = readCodexConfig();\r\n const plugins = cfg[\"plugins\"];\r\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\r\n return Object.keys(plugins as Record<string, unknown>).some((k) =>\r\n k.endsWith(`@${MARKETPLACE_NAME}`),\r\n );\r\n}\r\n\r\n/**\r\n * The directory config.toml records as `[marketplaces.agent-connector].source`,\r\n * or null when not registered / unreadable. Both a presence probe and the\r\n * NAME-COLLISION check: a registration pointing somewhere other than our\r\n * staging root belongs to the user and must never be touched.\r\n */\r\nexport function codexMarketplaceSource(name: string): string | null {\r\n const cfg = readCodexConfig();\r\n const marketplaces = cfg[\"marketplaces\"];\r\n if (!marketplaces || typeof marketplaces !== \"object\" || Array.isArray(marketplaces)) {\r\n return null;\r\n }\r\n const entry = (marketplaces as Record<string, unknown>)[name];\r\n if (!entry || typeof entry !== \"object\") return null;\r\n const source = (entry as { source?: unknown }).source;\r\n return typeof source === \"string\" ? source : null;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// GitHub Copilot CLI host-state readers (~/.copilot, NO env — HOME isolation)\r\n//\r\n// LIVE-VERIFIED against GitHub Copilot CLI 1.0.63:\r\n// • ~/.copilot/config.json — JSONC (leading `// …` comment header, \"managed\r\n// automatically\") carrying `installedPlugins: [{ name, marketplace,\r\n// version, installed_at, enabled, cache_path }]`. The DEFINITIVE install\r\n// probe (an uninstall empties this array). Parsed JSONC-tolerantly so the\r\n// comment header does not break JSON.parse.\r\n// • ~/.copilot/settings.json — `extraKnownMarketplaces[\"<name>\"].source.path`\r\n// records the registered local marketplace dir (a `{source:\"directory\",\r\n// path}` object); `enabledPlugins[\"<id>@agent-connector\"]: true`. The\r\n// `.source.path` is both the presence probe and the NAME-COLLISION check.\r\n// Both readers degrade to false/null on absence/parse failure (refuse only on\r\n// positive evidence).\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Copilot CLI's config dir: ~/.copilot (NO dedicated env — HOME isolation). */\r\nexport function copilotConfigDir(): string {\r\n return join(homedir(), \".copilot\");\r\n}\r\n\r\n/** The state key our marketplace install creates for `id` (== claude/codex). */\r\nexport function copilotPluginKey(connectorId: string): string {\r\n return `${connectorId}@${MARKETPLACE_NAME}`;\r\n}\r\n\r\n/** The `installedPlugins` array from Copilot's config.json (JSONC; [] when unreadable). */\r\nfunction readCopilotInstalledPlugins(): Array<Record<string, unknown>> {\r\n let raw: string;\r\n try {\r\n raw = readFileSync(join(copilotConfigDir(), \"config.json\"), \"utf8\");\r\n } catch {\r\n return [];\r\n }\r\n const parsed = parseJsonc(raw);\r\n const plugins = parsed?.[\"installedPlugins\"];\r\n if (!Array.isArray(plugins)) return [];\r\n return plugins.filter(\r\n (e): e is Record<string, unknown> =>\r\n e != null && typeof e === \"object\" && !Array.isArray(e),\r\n );\r\n}\r\n\r\n/**\r\n * True when Copilot's config.json lists `id` installed from our marketplace\r\n * (`installedPlugins[]` carries an entry with `name === id`, `marketplace ===\r\n * agent-connector`, and not `enabled: false`). The DEFINITIVE probe — an\r\n * uninstall empties this array (live-verified on CLI 1.0.63).\r\n */\r\nexport function copilotPluginInstalled(connectorId: string): boolean {\r\n return readCopilotInstalledPlugins().some(\r\n (e) =>\r\n e[\"name\"] === connectorId &&\r\n e[\"marketplace\"] === MARKETPLACE_NAME &&\r\n e[\"enabled\"] !== false,\r\n );\r\n}\r\n\r\n/** True when ANY plugin from our marketplace remains in Copilot's config.json. */\r\nexport function anyCopilotAgentConnectorPlugins(): boolean {\r\n return readCopilotInstalledPlugins().some(\r\n (e) => e[\"marketplace\"] === MARKETPLACE_NAME && e[\"enabled\"] !== false,\r\n );\r\n}\r\n\r\n/**\r\n * The directory settings.json records as `extraKnownMarketplaces[\"<name>\"]\r\n * .source.path`, or null when not registered / unreadable. Both a presence probe\r\n * and the NAME-COLLISION check (a registration pointing somewhere other than our\r\n * staging root belongs to the user and must never be touched). `source` is a\r\n * `{ source: \"directory\", path }` object on CLI 1.0.63; a bare string source is\r\n * accepted defensively.\r\n */\r\nexport function copilotMarketplaceSource(name: string): string | null {\r\n const parsed = readJsonFile(join(copilotConfigDir(), \"settings.json\"));\r\n const known = parsed?.[\"extraKnownMarketplaces\"];\r\n if (!known || typeof known !== \"object\" || Array.isArray(known)) return null;\r\n const entry = (known as Record<string, unknown>)[name];\r\n if (entry == null) return null;\r\n const source = (entry as { source?: unknown }).source;\r\n if (typeof source === \"string\") return source;\r\n if (source && typeof source === \"object\" && !Array.isArray(source)) {\r\n const path = (source as { path?: unknown }).path;\r\n if (typeof path === \"string\") return path;\r\n }\r\n return null;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// droid (Factory) host-state readers (~/.factory/settings.json, JSON, NO env)\r\n//\r\n// DOCS-ONLY: no droid binary is present on this box. The settings.json key\r\n// shapes below (`enabledPlugins[\"<id>@agent-connector\"] === true` +\r\n// `extraKnownMarketplaces[\"agent-connector\"].source`) follow the documented\r\n// factory plugin model — confirm the exact JSON shapes live when a droid binary\r\n// is available. Both readers read defensively (refuse only on positive evidence).\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Factory's settings file: ~/.factory/settings.json (NO dedicated env). */\r\nexport function factorySettingsPath(): string {\r\n return join(homedir(), \".factory\", \"settings.json\");\r\n}\r\n\r\n/** The settings.json key our marketplace install creates for `id` (== codex's). */\r\nexport function droidPluginKey(connectorId: string): string {\r\n return `${connectorId}@${MARKETPLACE_NAME}`;\r\n}\r\n\r\n/** The `enabledPlugins` map from droid's settings.json (null when unreadable). */\r\nfunction readDroidEnabledPlugins(): Record<string, unknown> | null {\r\n const parsed = readJsonFile(factorySettingsPath());\r\n const plugins = parsed?.enabledPlugins;\r\n if (plugins && typeof plugins === \"object\" && !Array.isArray(plugins)) {\r\n return plugins as Record<string, unknown>;\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * True when droid's settings.json carries\r\n * `enabledPlugins[\"<id>@agent-connector\"] === true`. DOCS-only: the strict\r\n * `=== true` value shape is unverified live (a future droid may store an object).\r\n */\r\nexport function droidPluginInstalled(connectorId: string): boolean {\r\n const plugins = readDroidEnabledPlugins();\r\n if (!plugins) return false;\r\n return plugins[droidPluginKey(connectorId)] === true;\r\n}\r\n\r\n/** True when ANY `*@agent-connector` plugin remains enabled in droid's settings. */\r\nexport function anyDroidAgentConnectorPlugins(): boolean {\r\n const plugins = readDroidEnabledPlugins();\r\n if (!plugins) return false;\r\n return Object.entries(plugins).some(\r\n ([key, value]) => key.endsWith(`@${MARKETPLACE_NAME}`) && value === true,\r\n );\r\n}\r\n\r\n/**\r\n * The directory droid's settings.json records as\r\n * `extraKnownMarketplaces[\"<name>\"].source`, or null when not registered /\r\n * unreadable. Both a presence probe and the NAME-COLLISION check (a registration\r\n * pointing somewhere other than our staging root belongs to the user). DOCS-only:\r\n * the local-path `source` field shape is undocumented, so this reads defensively\r\n * — accepts either a bare string source or an object carrying a `path`/`source`\r\n * string, and NORMALIZES to a single string for the samePath compare.\r\n */\r\nexport function droidMarketplaceSource(name: string): string | null {\r\n const parsed = readJsonFile(factorySettingsPath());\r\n const known = parsed?.extraKnownMarketplaces;\r\n if (!known || typeof known !== \"object\" || Array.isArray(known)) return null;\r\n const entry = (known as Record<string, unknown>)[name];\r\n if (entry == null) return null;\r\n if (typeof entry === \"string\") return entry;\r\n if (typeof entry === \"object\" && !Array.isArray(entry)) {\r\n const e = entry as { source?: unknown; path?: unknown };\r\n if (typeof e.source === \"string\") return e.source;\r\n if (typeof e.path === \"string\") return e.path;\r\n }\r\n return null;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// agy (Antigravity) host-state readers (~/.gemini/config/plugins, NO env)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** agy's config dir: ~/.gemini/config (NO dedicated env — HOME isolation). */\r\nexport function agyConfigDir(): string {\r\n return join(homedir(), \".gemini\", \"config\");\r\n}\r\n\r\n/** agy's plugins dir: ~/.gemini/config/plugins. */\r\nexport function agyPluginsDir(): string {\r\n return join(agyConfigDir(), \"plugins\");\r\n}\r\n\r\n/**\r\n * Candidate `import_manifest.json` locations. agy 1.0.7 records the manifest at\r\n * `<config>/plugins/import_manifest.json` on POSIX but at\r\n * `<config>/import_manifest.json` on Windows (live-confirmed on the my-window\r\n * box) — both are probed, then the copied plugin dir is the final fallback.\r\n */\r\nexport function agyImportManifestPaths(): string[] {\r\n return [\r\n join(agyConfigDir(), \"import_manifest.json\"), // win32 location\r\n join(agyPluginsDir(), \"import_manifest.json\"), // posix location\r\n ];\r\n}\r\n\r\n/** @deprecated single-location accessor — prefer {@link agyImportManifestPaths}. */\r\nexport function agyImportManifestPath(): string {\r\n return join(agyPluginsDir(), \"import_manifest.json\");\r\n}\r\n\r\n/**\r\n * True when `id` is installed per agy's import manifest (`imports[].name`),\r\n * checking BOTH the win32 and posix manifest locations, with a fallback probe of\r\n * the copied plugin dir's plugin.json. agy's uninstall removes the plugin dir\r\n * and clears the manifest entry (live-verified on Windows: install → dir +\r\n * manifest present, uninstall → both gone, re-install idempotent).\r\n */\r\nexport function agyPluginInstalled(connectorId: string): boolean {\r\n for (const manifest of agyImportManifestPaths()) {\r\n try {\r\n const parsed = JSON.parse(readFileSync(manifest, \"utf8\")) as {\r\n imports?: unknown;\r\n };\r\n const imports = parsed.imports;\r\n if (\r\n Array.isArray(imports) &&\r\n imports.some(\r\n (e) =>\r\n e != null &&\r\n typeof e === \"object\" &&\r\n (e as { name?: unknown }).name === connectorId,\r\n )\r\n ) {\r\n return true;\r\n }\r\n } catch {\r\n /* absent / unreadable manifest → try the next candidate */\r\n }\r\n }\r\n return existsSync(join(agyPluginsDir(), connectorId, \"plugin.json\"));\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Gemini CLI host-state readers (~/.gemini/extensions, NO env — HOME isolation)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Gemini CLI's config dir: ~/.gemini (NO dedicated config-dir env — isolation is\r\n * via HOME, live-confirmed on gemini 0.36.0). DISTINCT from agy's ~/.gemini/config\r\n * tree: gemini extensions land under ~/.gemini/extensions/<id>/.\r\n */\r\nexport function geminiConfigDir(): string {\r\n return join(homedir(), \".gemini\");\r\n}\r\n\r\n/**\r\n * True when `id` is installed per Gemini's own extension store: the marker file\r\n * ~/.gemini/extensions/<id>/gemini-extension.json exists. Gemini exits 0 even on\r\n * a logical failure, so this fs probe — not the exit code — is the source of\r\n * truth (the driver re-probes after every spawn).\r\n */\r\nexport function geminiExtensionInstalled(connectorId: string): boolean {\r\n return existsSync(\r\n join(geminiConfigDir(), \"extensions\", connectorId, \"gemini-extension.json\"),\r\n );\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Qwen Code host-state readers (~/.qwen/extensions, NO env — HOME isolation)\r\n//\r\n// DOCS-ONLY: no qwen binary is present on this box. qwen-code is a gemini-cli\r\n// fork, so the extension store SHAPE (~/.qwen/extensions/<id>/qwen-extension.json)\r\n// mirrors gemini's — confirm the marker filename live when a qwen binary lands.\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Qwen Code's config dir: ~/.qwen (NO dedicated config-dir env — isolation is\r\n * via HOME, like its gemini-cli parent). DOCS-only: confirm live when a qwen\r\n * binary is available. DISTINCT from ~/.gemini (the gemini-cli store).\r\n */\r\nexport function qwenConfigDir(): string {\r\n return join(homedir(), \".qwen\");\r\n}\r\n\r\n/**\r\n * True when `id` is installed per Qwen's own extension store: the marker file\r\n * ~/.qwen/extensions/<id>/qwen-extension.json exists. Mirrors gemini's fs probe\r\n * (qwen is a gemini fork); the driver re-probes after every spawn. DOCS-only:\r\n * the marker filename (`qwen-extension.json`) is unverified live.\r\n */\r\nexport function qwenExtensionInstalled(connectorId: string): boolean {\r\n return existsSync(\r\n join(qwenConfigDir(), \"extensions\", connectorId, \"qwen-extension.json\"),\r\n );\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// npm-local host-state readers (opencode / kilo / kilo-cli — XDG_CONFIG_HOME)\r\n//\r\n// These hosts have NO uninstall verb; installs are recorded as `file://<dir>`\r\n// entries in a `plugin` array inside an opencode-style config file, which the\r\n// driver EDITS to remove. This module is a spawn/shared-free LEAF, so a tiny\r\n// posix-resolve path comparison is inlined here rather than importing\r\n// shared.ts's samePath (and samePath does not strip the `file://` scheme).\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** opencode's config dir: $XDG_CONFIG_HOME/opencode (or ~/.config/opencode). */\r\nexport function opencodeConfigDir(): string {\r\n return join(xdgConfigHome(), \"opencode\");\r\n}\r\n\r\n/** kilo / kilo-cli's config dir: $XDG_CONFIG_HOME/kilo (or ~/.config/kilo). */\r\nexport function kiloConfigDir(): string {\r\n return join(xdgConfigHome(), \"kilo\");\r\n}\r\n\r\n/**\r\n * Candidate config filenames per npm-local host, most-preferred first. opencode\r\n * accepts opencode.jsonc / opencode.json / config.json (JSONC — comments\r\n * tolerated); kilo / kilo-cli use opencode.json (plain JSON).\r\n */\r\nfunction npmConfigCandidates(platform: PlatformId): string[] {\r\n return platform === \"opencode\"\r\n ? [\"opencode.jsonc\", \"opencode.json\", \"config.json\"]\r\n : [\"opencode.json\"];\r\n}\r\n\r\n/** The config DIR for an npm-local host (opencode vs kilo/kilo-cli). */\r\nfunction npmConfigDir(platform: PlatformId): string {\r\n return platform === \"opencode\" ? opencodeConfigDir() : kiloConfigDir();\r\n}\r\n\r\n/**\r\n * The config FILE path for `platform`: the first existing candidate, else the\r\n * most-preferred candidate (the path a fresh install would write). Never throws.\r\n */\r\nexport function npmConfigFilePath(platform: PlatformId): string {\r\n const dir = npmConfigDir(platform);\r\n const candidates = npmConfigCandidates(platform);\r\n for (const name of candidates) {\r\n if (existsSync(join(dir, name))) return join(dir, name);\r\n }\r\n return join(dir, candidates[0]!);\r\n}\r\n\r\n/** Strip a single leading `file://` (and `file://localhost/`) scheme prefix. */\r\nexport function stripFileScheme(entry: string): string {\r\n let s = entry;\r\n if (s.startsWith(\"file://localhost/\")) s = s.slice(\"file://localhost\".length);\r\n else if (s.startsWith(\"file://\")) s = s.slice(\"file://\".length);\r\n return s;\r\n}\r\n\r\n/**\r\n * Strip `//` line and `/* */` block comments from JSONC, STRING-AWARE: a `//`\r\n * or `/*` inside a JSON string literal (e.g. the `file://` in a plugin entry) is\r\n * left intact. Scans char-by-char tracking string + escape state — a naive\r\n * regex would corrupt `\"file:///dir\"` by eating from the embedded `//`.\r\n */\r\nfunction stripJsoncComments(text: string): string {\r\n let out = \"\";\r\n let inString = false;\r\n let inLine = false;\r\n let inBlock = false;\r\n for (let i = 0; i < text.length; i++) {\r\n const c = text[i]!;\r\n const next = i + 1 < text.length ? text[i + 1]! : \"\";\r\n if (inLine) {\r\n if (c === \"\\n\") {\r\n inLine = false;\r\n out += c;\r\n }\r\n continue;\r\n }\r\n if (inBlock) {\r\n if (c === \"*\" && next === \"/\") {\r\n inBlock = false;\r\n i++;\r\n }\r\n continue;\r\n }\r\n if (inString) {\r\n out += c;\r\n if (c === \"\\\\\") {\r\n // Copy the escaped char verbatim (covers \\\" and \\\\).\r\n if (next !== \"\") {\r\n out += next;\r\n i++;\r\n }\r\n } else if (c === '\"') {\r\n inString = false;\r\n }\r\n continue;\r\n }\r\n if (c === '\"') {\r\n inString = true;\r\n out += c;\r\n continue;\r\n }\r\n if (c === \"/\" && next === \"/\") {\r\n inLine = true;\r\n i++;\r\n continue;\r\n }\r\n if (c === \"/\" && next === \"*\") {\r\n inBlock = true;\r\n i++;\r\n continue;\r\n }\r\n out += c;\r\n }\r\n return out;\r\n}\r\n\r\n/** Tolerant JSONC → object: strip // line and block comments, then JSON.parse. */\r\nfunction parseJsonc(text: string): Record<string, unknown> | null {\r\n // A parse failure degrades to null (the \"refuse only on positive evidence\"\r\n // convention) rather than throwing.\r\n try {\r\n const parsed = JSON.parse(stripJsoncComments(text)) as unknown;\r\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\r\n return parsed as Record<string, unknown>;\r\n }\r\n } catch {\r\n /* unreadable / not JSONC → null */\r\n }\r\n return null;\r\n}\r\n\r\n/** Read + parse an npm-local host's config file ({}-shaped or null). Never throws. */\r\nexport function readNpmConfig(platform: PlatformId): Record<string, unknown> | null {\r\n const file = npmConfigFilePath(platform);\r\n try {\r\n return parseJsonc(readFileSync(file, \"utf8\"));\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Convert an npm `plugin`-array entry to a native absolute path. opencode/kilo\r\n * record a proper `file://` URL — on Windows that is `file:///C:/Users/…` (drive\r\n * letter + forward slashes), which a textual `file://`-strip leaves as the broken\r\n * `/C:/Users/…`. `fileURLToPath` does the correct cross-platform decode\r\n * (`file:///C:/x`→`C:\\x`, `file:///tmp/x`→`/tmp/x`). Bare-path entries (also\r\n * accepted by the hosts) and any malformed URL fall back to a textual strip.\r\n */\r\nfunction npmEntryToPath(entry: string): string {\r\n if (/^file:\\/\\//i.test(entry)) {\r\n try {\r\n return fileURLToPath(entry);\r\n } catch {\r\n /* malformed file URL (e.g. backslashes) → manual strip below */\r\n }\r\n }\r\n return stripFileScheme(entry);\r\n}\r\n\r\n/**\r\n * Path equality for the npm probe. Resolve symlinks with `realpathSync.native`\r\n * when the path exists (catches macOS `/var/folders`→`/private/var/folders` and\r\n * win32 8.3 short paths — the host records the realpath, we hold the symlink),\r\n * else lexical `resolve()`; case-fold on win32. Inlined here (leaf module)\r\n * rather than importing shared.ts's samePath, which pulls in the spawn layer.\r\n */\r\nfunction npmPathEquals(a: string, b: string): boolean {\r\n const norm = (p: string): string => {\r\n let s: string;\r\n try {\r\n s = realpathSync.native(p);\r\n } catch {\r\n s = resolve(p);\r\n }\r\n return process.platform === \"win32\" ? s.toLowerCase() : s;\r\n };\r\n return norm(a) === norm(b);\r\n}\r\n\r\n/**\r\n * The `plugin`-array ENTRY (the raw string, including any `file://`) whose value\r\n * — after stripping a leading `file://` — path-equals `id`'s staged bundle dir,\r\n * or null when no entry references it. Used both as the install probe and to\r\n * locate the exact entry the uninstall edit must drop.\r\n */\r\nexport function npmPluginArrayEntry(\r\n platform: PlatformId,\r\n connectorId: string,\r\n): string | null {\r\n const cfg = readNpmConfig(platform);\r\n if (!cfg) return null;\r\n const arr = cfg[\"plugin\"];\r\n if (!Array.isArray(arr)) return null;\r\n const target = join(npmStagingRoot(), connectorId);\r\n for (const raw of arr) {\r\n if (typeof raw !== \"string\") continue;\r\n if (npmPathEquals(npmEntryToPath(raw), target)) return raw;\r\n }\r\n return null;\r\n}\r\n\r\n/** True when `platform`'s config `plugin` array references `id`'s staged bundle. */\r\nexport function npmPluginInstalled(platform: PlatformId, connectorId: string): boolean {\r\n return npmPluginArrayEntry(platform, connectorId) != null;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Cross-host marketplace-install EVIDENCE (the double-install guard's probe)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Read-only evidence that `connectorId` is marketplace/plugin-installed on\r\n * `platform`: the state record first, then a cheap fs probe of the host's own\r\n * plugin state (catches manual installs from `package`'s printed instructions).\r\n * Returns a human-readable description of the evidence, or null when none.\r\n */\r\nexport function marketplaceEvidence(\r\n connectorId: string,\r\n platform: PlatformId,\r\n): string | null {\r\n if (readMarketplaceInstalls(connectorId)[platform]) {\r\n return \"recorded in marketplace-installs.json\";\r\n }\r\n switch (platform) {\r\n case \"claude-code\":\r\n if (claudePluginInstalled(connectorId)) {\r\n return `${claudePluginKey(connectorId)} listed in Claude's installed_plugins.json`;\r\n }\r\n return null;\r\n case \"gemini-cli\":\r\n return geminiExtensionInstalled(connectorId)\r\n ? `extension ${connectorId} listed in Gemini's extensions store`\r\n : null;\r\n case \"qwen-code\":\r\n return qwenExtensionInstalled(connectorId)\r\n ? `extension ${connectorId} listed in Qwen's extensions store`\r\n : null;\r\n case \"droid\":\r\n return droidPluginInstalled(connectorId)\r\n ? `${droidPluginKey(connectorId)} enabled in droid's settings.json`\r\n : null;\r\n case \"antigravity\":\r\n case \"antigravity-cli\": {\r\n const dir = join(homedir(), \".gemini\", \"config\", \"plugins\", connectorId);\r\n return existsSync(dir) ? `plugin dir ${dir} exists` : null;\r\n }\r\n case \"opencode\":\r\n case \"kilo\":\r\n case \"kilo-cli\":\r\n return npmPluginInstalled(platform, connectorId)\r\n ? `${connectorId} listed in ${platform}'s config plugin array`\r\n : null;\r\n case \"codex\": {\r\n const dir = join(\r\n codexConfigHome(),\r\n \"plugins\",\r\n \"cache\",\r\n MARKETPLACE_NAME,\r\n connectorId,\r\n );\r\n return existsSync(dir) ? `codex plugin cache ${dir} exists` : null;\r\n }\r\n case \"copilot-cli\":\r\n return copilotPluginInstalled(connectorId)\r\n ? `${copilotPluginKey(connectorId)} listed in Copilot's config.json`\r\n : null;\r\n default:\r\n return null;\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Content hashing — staged-bundle drift detection\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Deterministic sha256 over every file under `dir` (sorted relative paths +\r\n * bytes). Empty string when the dir is missing/unreadable. Used to record the\r\n * staged bundle's content at install time and detect drift later (claude caches\r\n * a versioned COPY, so an edited connector goes silently stale without this).\r\n */\r\nexport function hashDirectory(dir: string): string {\r\n if (!existsSync(dir)) return \"\";\r\n const files: string[] = [];\r\n const walk = (d: string): void => {\r\n let entries: string[];\r\n try {\r\n entries = readdirSync(d);\r\n } catch {\r\n return;\r\n }\r\n for (const name of entries.sort()) {\r\n const full = join(d, name);\r\n let st;\r\n try {\r\n st = statSync(full);\r\n } catch {\r\n continue;\r\n }\r\n if (st.isDirectory()) walk(full);\r\n else if (st.isFile()) files.push(full);\r\n }\r\n };\r\n walk(dir);\r\n const hash = createHash(\"sha256\");\r\n for (const file of files.sort()) {\r\n hash.update(relative(dir, file).split(\"\\\\\").join(\"/\"));\r\n hash.update(\"\\0\");\r\n try {\r\n hash.update(readFileSync(file));\r\n } catch {\r\n /* unreadable file contributes only its path */\r\n }\r\n hash.update(\"\\0\");\r\n }\r\n return hash.digest(\"hex\");\r\n}\r\n"],"mappings":"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","names":[]}
|