@ken-jo/agent-connector 0.4.97 → 0.4.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (387) hide show
  1. package/LICENSE +202 -202
  2. package/NOTICE +6 -6
  3. package/README.md +604 -596
  4. package/dist/{action-IPFDPOZU.js → action-5OEM3QC4.js} +17 -16
  5. package/dist/action-5OEM3QC4.js.map +1 -0
  6. package/dist/{amazon-q-IT44LEWV.js → amazon-q-7DPVJXJN.js} +6 -6
  7. package/dist/amazon-q-7DPVJXJN.js.map +1 -0
  8. package/dist/{amp-CEWOSWQO.js → amp-BQZOL5GR.js} +4 -4
  9. package/dist/amp-BQZOL5GR.js.map +1 -0
  10. package/dist/{amp-P35FRU2Z.js → amp-VYXZWHXW.js} +7 -7
  11. package/dist/amp-VYXZWHXW.js.map +1 -0
  12. package/dist/{antigravity-LHFEFNMC.js → antigravity-EA4L225L.js} +5 -5
  13. package/dist/antigravity-EA4L225L.js.map +1 -0
  14. package/dist/antigravity-VXHL6ZPM.js +18 -0
  15. package/dist/{antigravity-cli-MOKFSWOU.js → antigravity-cli-B2HXZFYQ.js} +5 -5
  16. package/dist/antigravity-cli-B2HXZFYQ.js.map +1 -0
  17. package/dist/{antigravity-cli-MPE27LPI.js → antigravity-cli-JHQ7JGNX.js} +10 -9
  18. package/dist/antigravity-cli-JHQ7JGNX.js.map +1 -0
  19. package/dist/{audit-3E5JAOP2.js → audit-BO7JQYIH.js} +8 -8
  20. package/dist/{chunk-7C2IPIIB.js → chunk-2Y2MZRLP.js} +3 -3
  21. package/dist/chunk-2Y2MZRLP.js.map +1 -0
  22. package/dist/{chunk-GZZCHJDP.js → chunk-4KIXXJ7B.js} +2 -2
  23. package/dist/chunk-4KIXXJ7B.js.map +1 -0
  24. package/dist/chunk-4NFDNY3C.js +34 -0
  25. package/dist/chunk-4NFDNY3C.js.map +1 -0
  26. package/dist/{chunk-C63AAXK7.js → chunk-4QXCNZMD.js} +5 -5
  27. package/dist/chunk-4QXCNZMD.js.map +1 -0
  28. package/dist/{chunk-Q7PR2WGT.js → chunk-6TF7MHG5.js} +2 -2
  29. package/dist/chunk-6TF7MHG5.js.map +1 -0
  30. package/dist/{chunk-7ZFK2WEO.js → chunk-6WN37NVN.js} +113 -10
  31. package/dist/chunk-6WN37NVN.js.map +1 -0
  32. package/dist/{chunk-GCPEPUD7.js → chunk-6XMV2OOA.js} +6 -6
  33. package/dist/chunk-6XMV2OOA.js.map +1 -0
  34. package/dist/{chunk-XXLCMSD3.js → chunk-BEFVJ2OE.js} +1 -1
  35. package/dist/chunk-BEFVJ2OE.js.map +1 -0
  36. package/dist/{chunk-N4ENLZEU.js → chunk-BV5WK6VQ.js} +19 -19
  37. package/dist/chunk-BV5WK6VQ.js.map +1 -0
  38. package/dist/{chunk-WPTNMYYM.js → chunk-BXPSDNIB.js} +6 -6
  39. package/dist/chunk-BXPSDNIB.js.map +1 -0
  40. package/dist/{chunk-Q4ISQHUQ.js → chunk-FBJQGRET.js} +1 -1
  41. package/dist/chunk-FBJQGRET.js.map +1 -0
  42. package/dist/{chunk-THI4PI4H.js → chunk-FJCUKXGX.js} +2 -2
  43. package/dist/chunk-FJCUKXGX.js.map +1 -0
  44. package/dist/{chunk-L2C5UMKW.js → chunk-FKW5A6HE.js} +23 -7
  45. package/dist/chunk-FKW5A6HE.js.map +1 -0
  46. package/dist/{chunk-NDMNMAKP.js → chunk-G5HAIYRM.js} +43 -43
  47. package/dist/chunk-G5HAIYRM.js.map +1 -0
  48. package/dist/{chunk-SLJI5BOM.js → chunk-GDIAHWNY.js} +2 -2
  49. package/dist/chunk-GDIAHWNY.js.map +1 -0
  50. package/dist/{chunk-QYOUQCC6.js → chunk-GZGLCCEE.js} +1 -1
  51. package/dist/chunk-GZGLCCEE.js.map +1 -0
  52. package/dist/{chunk-PSGTHJQU.js → chunk-H53QZQJI.js} +1 -1
  53. package/dist/chunk-H53QZQJI.js.map +1 -0
  54. package/dist/{chunk-7HHW23AC.js → chunk-IRUYJD23.js} +29 -29
  55. package/dist/chunk-IRUYJD23.js.map +1 -0
  56. package/dist/{chunk-IOJDZJ72.js → chunk-IVAWP6HH.js} +2 -2
  57. package/dist/chunk-IVAWP6HH.js.map +1 -0
  58. package/dist/{chunk-3FINQ2ZF.js → chunk-JTN3AKZO.js} +30 -20
  59. package/dist/chunk-JTN3AKZO.js.map +1 -0
  60. package/dist/{chunk-4GH2KA7V.js → chunk-KHO5WNTP.js} +1 -1
  61. package/dist/chunk-KHO5WNTP.js.map +1 -0
  62. package/dist/{chunk-MLU7NDAZ.js → chunk-KLZBRNFP.js} +12 -12
  63. package/dist/chunk-KLZBRNFP.js.map +1 -0
  64. package/dist/{chunk-3EP5V2F4.js → chunk-M6OS4OLU.js} +3 -3
  65. package/dist/chunk-M6OS4OLU.js.map +1 -0
  66. package/dist/{chunk-UZZTWWGG.js → chunk-MQPNU2CY.js} +1 -1
  67. package/dist/chunk-MQPNU2CY.js.map +1 -0
  68. package/dist/{chunk-LQ55E7QI.js → chunk-N4STCQGP.js} +7 -7
  69. package/dist/chunk-N4STCQGP.js.map +1 -0
  70. package/dist/{chunk-KFPSJRHR.js → chunk-NBYKLUF6.js} +2 -2
  71. package/dist/chunk-NBYKLUF6.js.map +1 -0
  72. package/dist/{chunk-KM2YXO6V.js → chunk-O7SDRBVB.js} +1 -1
  73. package/dist/chunk-O7SDRBVB.js.map +1 -0
  74. package/dist/{chunk-LFWYYWLV.js → chunk-OP4KBXUO.js} +1 -1
  75. package/dist/chunk-OP4KBXUO.js.map +1 -0
  76. package/dist/{chunk-XN7S44U4.js → chunk-OPXAP3AC.js} +1 -1
  77. package/dist/chunk-OPXAP3AC.js.map +1 -0
  78. package/dist/{chunk-TGZFDXNH.js → chunk-PWCBWAM4.js} +6 -4
  79. package/dist/chunk-PWCBWAM4.js.map +1 -0
  80. package/dist/{chunk-Y3K6KJMS.js → chunk-QSY74NAP.js} +2 -2
  81. package/dist/chunk-QSY74NAP.js.map +1 -0
  82. package/dist/{chunk-ZSOS2KOR.js → chunk-RK6VBLXP.js} +1 -1
  83. package/dist/chunk-RK6VBLXP.js.map +1 -0
  84. package/dist/{chunk-FHYR5Q5T.js → chunk-SFY6YLVK.js} +19 -12
  85. package/dist/chunk-SFY6YLVK.js.map +1 -0
  86. package/dist/{chunk-C747DYNT.js → chunk-UM5S7DKK.js} +1 -1
  87. package/dist/chunk-UM5S7DKK.js.map +1 -0
  88. package/dist/{chunk-ILWHLGIC.js → chunk-UMQ6NBQR.js} +2 -2
  89. package/dist/chunk-UMQ6NBQR.js.map +1 -0
  90. package/dist/{chunk-7PSNXUFK.js → chunk-XD7MY6HB.js} +1 -1
  91. package/dist/chunk-XD7MY6HB.js.map +1 -0
  92. package/dist/{chunk-XVEJAFKW.js → chunk-XY3N2U6L.js} +5 -5
  93. package/dist/chunk-XY3N2U6L.js.map +1 -0
  94. package/dist/{chunk-XZWMJLIT.js → chunk-YT43MUSJ.js} +1 -1
  95. package/dist/chunk-YT43MUSJ.js.map +1 -0
  96. package/dist/{claude-code-QBWDT5RT.js → claude-code-LJ6DVXWQ.js} +24 -14
  97. package/dist/claude-code-LJ6DVXWQ.js.map +1 -0
  98. package/dist/{claude-code-FS42PXZO.js → claude-code-NMRKTAP7.js} +4 -4
  99. package/dist/claude-code-NMRKTAP7.js.map +1 -0
  100. package/dist/cli/sdk.js +7 -7
  101. package/dist/cli/sdk.js.map +1 -1
  102. package/dist/cli.js +1 -1
  103. package/dist/cli.js.map +1 -1
  104. package/dist/{cline-JKEAYWVR.js → cline-JAPJNOZQ.js} +6 -6
  105. package/dist/cline-JAPJNOZQ.js.map +1 -0
  106. package/dist/{codebuddy-EB6EYPC3.js → codebuddy-GC7VAE2Y.js} +7 -7
  107. package/dist/codebuddy-GC7VAE2Y.js.map +1 -0
  108. package/dist/{codebuff-5RSXFU6G.js → codebuff-3VODFPD4.js} +6 -6
  109. package/dist/codebuff-3VODFPD4.js.map +1 -0
  110. package/dist/{codebuff-6KWBW6CT.js → codebuff-ETYT5UBL.js} +4 -4
  111. package/dist/codebuff-ETYT5UBL.js.map +1 -0
  112. package/dist/{codex-ART46CDD.js → codex-RXIS5BHM.js} +9 -9
  113. package/dist/codex-RXIS5BHM.js.map +1 -0
  114. package/dist/{codex-DKBAAUCO.js → codex-YAORPCOD.js} +4 -4
  115. package/dist/codex-YAORPCOD.js.map +1 -0
  116. package/dist/{continue-JHZCMSPH.js → continue-MECVDV4Z.js} +8 -8
  117. package/dist/continue-MECVDV4Z.js.map +1 -0
  118. package/dist/{copilot-cli-CLBJACQ5.js → copilot-cli-MZTAWMPW.js} +4 -4
  119. package/dist/copilot-cli-MZTAWMPW.js.map +1 -0
  120. package/dist/{copilot-cli-QVAU5CFM.js → copilot-cli-WVWAWNSM.js} +7 -7
  121. package/dist/copilot-cli-WVWAWNSM.js.map +1 -0
  122. package/dist/{crush-A5C5USRL.js → crush-QBXLNN7T.js} +4 -4
  123. package/dist/crush-QBXLNN7T.js.map +1 -0
  124. package/dist/{crush-7TORBYUL.js → crush-YB3SK6NK.js} +7 -7
  125. package/dist/crush-YB3SK6NK.js.map +1 -0
  126. package/dist/{cursor-D5SWDVSW.js → cursor-DIW43TRZ.js} +3 -3
  127. package/dist/cursor-DIW43TRZ.js.map +1 -0
  128. package/dist/cursor-RSLHA2CY.js +22 -0
  129. package/dist/{define-connector-DTWb46UB.d.ts → define-connector-odRHEC4H.d.ts} +1 -1
  130. package/dist/{detect-JHSA3G3U.js → detect-ALP4G7WG.js} +4 -4
  131. package/dist/detect-ALP4G7WG.js.map +1 -0
  132. package/dist/{devin-SSR7S4WX.js → devin-WPDN3MXE.js} +7 -7
  133. package/dist/devin-WPDN3MXE.js.map +1 -0
  134. package/dist/{doctor-CQJ7TZPA.js → doctor-YEQWCWZJ.js} +27 -26
  135. package/dist/doctor-YEQWCWZJ.js.map +1 -0
  136. package/dist/{droid-45PJ7JGF.js → droid-HS6MX3IL.js} +10 -8
  137. package/dist/droid-HS6MX3IL.js.map +1 -0
  138. package/dist/{droid-PFQ2PZSF.js → droid-U3ZBLQFO.js} +4 -4
  139. package/dist/droid-U3ZBLQFO.js.map +1 -0
  140. package/dist/{gemini-cli-Y6RJMU24.js → gemini-cli-NMVVBO3K.js} +4 -4
  141. package/dist/gemini-cli-NMVVBO3K.js.map +1 -0
  142. package/dist/{gemini-cli-24PFDK7D.js → gemini-cli-VJ6X5DNF.js} +8 -8
  143. package/dist/gemini-cli-VJ6X5DNF.js.map +1 -0
  144. package/dist/{goose-U5D3LCPC.js → goose-3X7ID7YW.js} +9 -9
  145. package/dist/goose-3X7ID7YW.js.map +1 -0
  146. package/dist/{goose-K2HKUTBK.js → goose-64V46Y2W.js} +4 -4
  147. package/dist/goose-64V46Y2W.js.map +1 -0
  148. package/dist/{grok-cli-L2MHAPBY.js → grok-cli-4VFVF2CQ.js} +7 -7
  149. package/dist/grok-cli-4VFVF2CQ.js.map +1 -0
  150. package/dist/{hermes-2S5JUTTJ.js → hermes-AIVUE7LX.js} +10 -8
  151. package/dist/hermes-AIVUE7LX.js.map +1 -0
  152. package/dist/{hermes-MF3NX4ZY.js → hermes-F3EGTK6N.js} +4 -4
  153. package/dist/hermes-F3EGTK6N.js.map +1 -0
  154. package/dist/{hook-VHICXHXL.js → hook-F63I3SD5.js} +17 -16
  155. package/dist/hook-F63I3SD5.js.map +1 -0
  156. package/dist/index.d.ts +3 -3
  157. package/dist/index.js +5 -5
  158. package/dist/{install-WLENR4KF.js → install-23WRRFBY.js} +21 -21
  159. package/dist/install-23WRRFBY.js.map +1 -0
  160. package/dist/{introspect-dNekqPDY.d.ts → introspect-gTx6GzpW.d.ts} +1 -1
  161. package/dist/{jetbrains-copilot-Z4FC3RM3.js → jetbrains-copilot-XBO2JZRO.js} +6 -6
  162. package/dist/jetbrains-copilot-XBO2JZRO.js.map +1 -0
  163. package/dist/{junie-ULGJJIHB.js → junie-UCOS6Q37.js} +6 -6
  164. package/dist/junie-UCOS6Q37.js.map +1 -0
  165. package/dist/{kilo-SMHPOHJ5.js → kilo-SW7BH6EG.js} +4 -4
  166. package/dist/kilo-SW7BH6EG.js.map +1 -0
  167. package/dist/{kilo-WWIJOVWD.js → kilo-T4OXNUYY.js} +8 -8
  168. package/dist/kilo-T4OXNUYY.js.map +1 -0
  169. package/dist/{kilo-cli-QKE2EQLS.js → kilo-cli-I2S3W245.js} +8 -8
  170. package/dist/kilo-cli-I2S3W245.js.map +1 -0
  171. package/dist/{kilo-cli-T2SRRRJS.js → kilo-cli-ZF272ZHN.js} +5 -5
  172. package/dist/kilo-cli-ZF272ZHN.js.map +1 -0
  173. package/dist/{kimi-MSIDEMCI.js → kimi-5FHEXGZD.js} +4 -4
  174. package/dist/kimi-5FHEXGZD.js.map +1 -0
  175. package/dist/{kimi-JMUFTVSF.js → kimi-VLSUFLYV.js} +7 -7
  176. package/dist/kimi-VLSUFLYV.js.map +1 -0
  177. package/dist/{kiro-UWUCRKR3.js → kiro-AQFXGOJX.js} +10 -8
  178. package/dist/kiro-AQFXGOJX.js.map +1 -0
  179. package/dist/{kiro-NYRRBIKW.js → kiro-WGOI5CGT.js} +4 -4
  180. package/dist/kiro-WGOI5CGT.js.map +1 -0
  181. package/dist/{leaderboard-NJWKGMXP.js → leaderboard-YBD3IKC2.js} +7 -7
  182. package/dist/leaderboard-YBD3IKC2.js.map +1 -0
  183. package/dist/{mimo-code-BPNYYIVM.js → mimo-code-KETL7W7D.js} +7 -7
  184. package/dist/mimo-code-KETL7W7D.js.map +1 -0
  185. package/dist/{mistral-vibe-LT3DAJ3L.js → mistral-vibe-3AU7N2CA.js} +6 -6
  186. package/dist/mistral-vibe-3AU7N2CA.js.map +1 -0
  187. package/dist/{mux-AHNUAY5Z.js → mux-BO2L5CTW.js} +6 -6
  188. package/dist/mux-BO2L5CTW.js.map +1 -0
  189. package/dist/{mux-7PIJ7HUQ.js → mux-XM6AP35L.js} +4 -4
  190. package/dist/mux-XM6AP35L.js.map +1 -0
  191. package/dist/{nemoclaw-RE5RZASZ.js → nemoclaw-WA7RSKF2.js} +7 -7
  192. package/dist/nemoclaw-WA7RSKF2.js.map +1 -0
  193. package/dist/{omp-D2EXEPH7.js → omp-UDYJ2PYY.js} +9 -7
  194. package/dist/omp-UDYJ2PYY.js.map +1 -0
  195. package/dist/{open-interpreter-SSGBEUKX.js → open-interpreter-WMBNM4MQ.js} +6 -6
  196. package/dist/open-interpreter-WMBNM4MQ.js.map +1 -0
  197. package/dist/openclaw-3DHVHX5P.js +17 -0
  198. package/dist/{openclaw-KAFTNLR2.js → openclaw-5EQFGOWL.js} +4 -4
  199. package/dist/openclaw-5EQFGOWL.js.map +1 -0
  200. package/dist/{opencode-YKUKZY3J.js → opencode-IKMI3RI7.js} +7 -7
  201. package/dist/opencode-IKMI3RI7.js.map +1 -0
  202. package/dist/{opencode-Z65JYILL.js → opencode-QGP4ALQY.js} +4 -4
  203. package/dist/opencode-QGP4ALQY.js.map +1 -0
  204. package/dist/{openhands-SEZMCUC3.js → openhands-QGXMQR6I.js} +7 -7
  205. package/dist/openhands-QGXMQR6I.js.map +1 -0
  206. package/dist/{package-JECVQEFT.js → package-TAUTPP7Z.js} +15 -15
  207. package/dist/package-TAUTPP7Z.js.map +1 -0
  208. package/dist/{pi-YPYV2ULH.js → pi-A2HCGRJG.js} +9 -7
  209. package/dist/pi-A2HCGRJG.js.map +1 -0
  210. package/dist/{pi-DLKCPOJE.js → pi-GGDQREVG.js} +4 -4
  211. package/dist/pi-GGDQREVG.js.map +1 -0
  212. package/dist/{qwen-code-ZHCLCYMC.js → qwen-code-DNQTGJOF.js} +35 -17
  213. package/dist/qwen-code-DNQTGJOF.js.map +1 -0
  214. package/dist/{qwen-code-26CSFDX6.js → qwen-code-GQILBJ5B.js} +4 -4
  215. package/dist/qwen-code-GQILBJ5B.js.map +1 -0
  216. package/dist/{roo-code-HSAE2CJU.js → roo-code-SUWI4BBN.js} +4 -4
  217. package/dist/roo-code-SUWI4BBN.js.map +1 -0
  218. package/dist/{roo-code-7F4HYB34.js → roo-code-VI2ZKTZC.js} +6 -6
  219. package/dist/roo-code-VI2ZKTZC.js.map +1 -0
  220. package/dist/runtime/index.d.ts +9 -8
  221. package/dist/runtime/index.js +15 -14
  222. package/dist/sdk/index.d.ts +4 -4
  223. package/dist/sdk/index.js +6 -6
  224. package/dist/sdk/index.js.map +1 -1
  225. package/dist/sdk/test.d.ts +2 -2
  226. package/dist/sdk/test.js +12 -11
  227. package/dist/{serve-LYSBH43G.js → serve-6MDLRQS2.js} +17 -16
  228. package/dist/serve-6MDLRQS2.js.map +1 -0
  229. package/dist/{status-PHKKGEW5.js → status-IUP75XHP.js} +11 -11
  230. package/dist/status-IUP75XHP.js.map +1 -0
  231. package/dist/{statusline-ZDD4OZ4F.js → statusline-PRODZV2J.js} +16 -15
  232. package/dist/statusline-PRODZV2J.js.map +1 -0
  233. package/dist/{synthetic-LUT6V6SD.js → synthetic-6FRA7P5Q.js} +4 -4
  234. package/dist/synthetic-6FRA7P5Q.js.map +1 -0
  235. package/dist/{telemetry-HDGMI2BK.js → telemetry-X4PLZEDB.js} +10 -10
  236. package/dist/telemetry-X4PLZEDB.js.map +1 -0
  237. package/dist/{trae-SIQI3C66.js → trae-CPPYASR2.js} +4 -4
  238. package/dist/trae-CPPYASR2.js.map +1 -0
  239. package/dist/{trae-Y67HSDOW.js → trae-VOJXUYRN.js} +6 -6
  240. package/dist/trae-VOJXUYRN.js.map +1 -0
  241. package/dist/{types-JyhpJHJa.d.ts → types-DWXnnx-N.d.ts} +96 -22
  242. package/dist/{uninstall-RWO2GM2I.js → uninstall-BSIJ4QXN.js} +21 -21
  243. package/dist/uninstall-BSIJ4QXN.js.map +1 -0
  244. package/dist/{upgrade-YJCXNUEE.js → upgrade-ZSICU3NE.js} +21 -21
  245. package/dist/upgrade-ZSICU3NE.js.map +1 -0
  246. package/dist/{usage-IWO4RQN6.js → usage-EQUNVSCT.js} +4 -4
  247. package/dist/usage-EQUNVSCT.js.map +1 -0
  248. package/dist/{usage-event-AFFKCLHE.js → usage-event-J5CQ6LNT.js} +16 -15
  249. package/dist/usage-event-J5CQ6LNT.js.map +1 -0
  250. package/dist/{vscode-copilot-5CI5B3X6.js → vscode-copilot-ZBXRPKGG.js} +7 -7
  251. package/dist/vscode-copilot-ZBXRPKGG.js.map +1 -0
  252. package/dist/{warp-5YBSP7FP.js → warp-NRBBJQR2.js} +4 -4
  253. package/dist/warp-NRBBJQR2.js.map +1 -0
  254. package/dist/{warp-INAKVGBV.js → warp-S444B2S3.js} +9 -7
  255. package/dist/warp-S444B2S3.js.map +1 -0
  256. package/dist/{windsurf-4HM57QAY.js → windsurf-R6D2JUTN.js} +6 -6
  257. package/dist/windsurf-R6D2JUTN.js.map +1 -0
  258. package/dist/{zed-YN5X37PQ.js → zed-DQV3ZUCU.js} +10 -8
  259. package/dist/zed-DQV3ZUCU.js.map +1 -0
  260. package/dist/{zed-XXCNYOIN.js → zed-XJFOOKCO.js} +4 -4
  261. package/dist/zed-XJFOOKCO.js.map +1 -0
  262. package/package.json +88 -88
  263. package/dist/action-IPFDPOZU.js.map +0 -1
  264. package/dist/amazon-q-IT44LEWV.js.map +0 -1
  265. package/dist/amp-CEWOSWQO.js.map +0 -1
  266. package/dist/amp-P35FRU2Z.js.map +0 -1
  267. package/dist/antigravity-LHFEFNMC.js.map +0 -1
  268. package/dist/antigravity-TWIWXTQH.js +0 -18
  269. package/dist/antigravity-cli-MOKFSWOU.js.map +0 -1
  270. package/dist/antigravity-cli-MPE27LPI.js.map +0 -1
  271. package/dist/chunk-3EP5V2F4.js.map +0 -1
  272. package/dist/chunk-3FINQ2ZF.js.map +0 -1
  273. package/dist/chunk-4GH2KA7V.js.map +0 -1
  274. package/dist/chunk-7C2IPIIB.js.map +0 -1
  275. package/dist/chunk-7HHW23AC.js.map +0 -1
  276. package/dist/chunk-7PSNXUFK.js.map +0 -1
  277. package/dist/chunk-7ZFK2WEO.js.map +0 -1
  278. package/dist/chunk-C63AAXK7.js.map +0 -1
  279. package/dist/chunk-C747DYNT.js.map +0 -1
  280. package/dist/chunk-FHYR5Q5T.js.map +0 -1
  281. package/dist/chunk-GCPEPUD7.js.map +0 -1
  282. package/dist/chunk-GZZCHJDP.js.map +0 -1
  283. package/dist/chunk-ILWHLGIC.js.map +0 -1
  284. package/dist/chunk-IOJDZJ72.js.map +0 -1
  285. package/dist/chunk-KFPSJRHR.js.map +0 -1
  286. package/dist/chunk-KM2YXO6V.js.map +0 -1
  287. package/dist/chunk-L2C5UMKW.js.map +0 -1
  288. package/dist/chunk-LFWYYWLV.js.map +0 -1
  289. package/dist/chunk-LQ55E7QI.js.map +0 -1
  290. package/dist/chunk-MLU7NDAZ.js.map +0 -1
  291. package/dist/chunk-N4ENLZEU.js.map +0 -1
  292. package/dist/chunk-NDMNMAKP.js.map +0 -1
  293. package/dist/chunk-PSGTHJQU.js.map +0 -1
  294. package/dist/chunk-Q4ISQHUQ.js.map +0 -1
  295. package/dist/chunk-Q7PR2WGT.js.map +0 -1
  296. package/dist/chunk-QYOUQCC6.js.map +0 -1
  297. package/dist/chunk-SLJI5BOM.js.map +0 -1
  298. package/dist/chunk-TGZFDXNH.js.map +0 -1
  299. package/dist/chunk-THI4PI4H.js.map +0 -1
  300. package/dist/chunk-UZZTWWGG.js.map +0 -1
  301. package/dist/chunk-WPTNMYYM.js.map +0 -1
  302. package/dist/chunk-XN7S44U4.js.map +0 -1
  303. package/dist/chunk-XVEJAFKW.js.map +0 -1
  304. package/dist/chunk-XXLCMSD3.js.map +0 -1
  305. package/dist/chunk-XZWMJLIT.js.map +0 -1
  306. package/dist/chunk-Y3K6KJMS.js.map +0 -1
  307. package/dist/chunk-ZSOS2KOR.js.map +0 -1
  308. package/dist/claude-code-FS42PXZO.js.map +0 -1
  309. package/dist/claude-code-QBWDT5RT.js.map +0 -1
  310. package/dist/cline-JKEAYWVR.js.map +0 -1
  311. package/dist/codebuddy-EB6EYPC3.js.map +0 -1
  312. package/dist/codebuff-5RSXFU6G.js.map +0 -1
  313. package/dist/codebuff-6KWBW6CT.js.map +0 -1
  314. package/dist/codex-ART46CDD.js.map +0 -1
  315. package/dist/codex-DKBAAUCO.js.map +0 -1
  316. package/dist/continue-JHZCMSPH.js.map +0 -1
  317. package/dist/copilot-cli-CLBJACQ5.js.map +0 -1
  318. package/dist/copilot-cli-QVAU5CFM.js.map +0 -1
  319. package/dist/crush-7TORBYUL.js.map +0 -1
  320. package/dist/crush-A5C5USRL.js.map +0 -1
  321. package/dist/cursor-D5SWDVSW.js.map +0 -1
  322. package/dist/cursor-W7I4QUFO.js +0 -22
  323. package/dist/detect-JHSA3G3U.js.map +0 -1
  324. package/dist/devin-SSR7S4WX.js.map +0 -1
  325. package/dist/doctor-CQJ7TZPA.js.map +0 -1
  326. package/dist/droid-45PJ7JGF.js.map +0 -1
  327. package/dist/droid-PFQ2PZSF.js.map +0 -1
  328. package/dist/gemini-cli-24PFDK7D.js.map +0 -1
  329. package/dist/gemini-cli-Y6RJMU24.js.map +0 -1
  330. package/dist/goose-K2HKUTBK.js.map +0 -1
  331. package/dist/goose-U5D3LCPC.js.map +0 -1
  332. package/dist/grok-cli-L2MHAPBY.js.map +0 -1
  333. package/dist/hermes-2S5JUTTJ.js.map +0 -1
  334. package/dist/hermes-MF3NX4ZY.js.map +0 -1
  335. package/dist/hook-VHICXHXL.js.map +0 -1
  336. package/dist/install-WLENR4KF.js.map +0 -1
  337. package/dist/jetbrains-copilot-Z4FC3RM3.js.map +0 -1
  338. package/dist/junie-ULGJJIHB.js.map +0 -1
  339. package/dist/kilo-SMHPOHJ5.js.map +0 -1
  340. package/dist/kilo-WWIJOVWD.js.map +0 -1
  341. package/dist/kilo-cli-QKE2EQLS.js.map +0 -1
  342. package/dist/kilo-cli-T2SRRRJS.js.map +0 -1
  343. package/dist/kimi-JMUFTVSF.js.map +0 -1
  344. package/dist/kimi-MSIDEMCI.js.map +0 -1
  345. package/dist/kiro-NYRRBIKW.js.map +0 -1
  346. package/dist/kiro-UWUCRKR3.js.map +0 -1
  347. package/dist/leaderboard-NJWKGMXP.js.map +0 -1
  348. package/dist/mimo-code-BPNYYIVM.js.map +0 -1
  349. package/dist/mistral-vibe-LT3DAJ3L.js.map +0 -1
  350. package/dist/mux-7PIJ7HUQ.js.map +0 -1
  351. package/dist/mux-AHNUAY5Z.js.map +0 -1
  352. package/dist/nemoclaw-RE5RZASZ.js.map +0 -1
  353. package/dist/omp-D2EXEPH7.js.map +0 -1
  354. package/dist/open-interpreter-SSGBEUKX.js.map +0 -1
  355. package/dist/openclaw-IJ33K45M.js +0 -17
  356. package/dist/openclaw-KAFTNLR2.js.map +0 -1
  357. package/dist/opencode-YKUKZY3J.js.map +0 -1
  358. package/dist/opencode-Z65JYILL.js.map +0 -1
  359. package/dist/openhands-SEZMCUC3.js.map +0 -1
  360. package/dist/package-JECVQEFT.js.map +0 -1
  361. package/dist/pi-DLKCPOJE.js.map +0 -1
  362. package/dist/pi-YPYV2ULH.js.map +0 -1
  363. package/dist/qwen-code-26CSFDX6.js.map +0 -1
  364. package/dist/qwen-code-ZHCLCYMC.js.map +0 -1
  365. package/dist/roo-code-7F4HYB34.js.map +0 -1
  366. package/dist/roo-code-HSAE2CJU.js.map +0 -1
  367. package/dist/serve-LYSBH43G.js.map +0 -1
  368. package/dist/status-PHKKGEW5.js.map +0 -1
  369. package/dist/statusline-ZDD4OZ4F.js.map +0 -1
  370. package/dist/synthetic-LUT6V6SD.js.map +0 -1
  371. package/dist/telemetry-HDGMI2BK.js.map +0 -1
  372. package/dist/trae-SIQI3C66.js.map +0 -1
  373. package/dist/trae-Y67HSDOW.js.map +0 -1
  374. package/dist/uninstall-RWO2GM2I.js.map +0 -1
  375. package/dist/upgrade-YJCXNUEE.js.map +0 -1
  376. package/dist/usage-IWO4RQN6.js.map +0 -1
  377. package/dist/usage-event-AFFKCLHE.js.map +0 -1
  378. package/dist/vscode-copilot-5CI5B3X6.js.map +0 -1
  379. package/dist/warp-5YBSP7FP.js.map +0 -1
  380. package/dist/warp-INAKVGBV.js.map +0 -1
  381. package/dist/windsurf-4HM57QAY.js.map +0 -1
  382. package/dist/zed-XXCNYOIN.js.map +0 -1
  383. package/dist/zed-YN5X37PQ.js.map +0 -1
  384. /package/dist/{antigravity-TWIWXTQH.js.map → antigravity-VXHL6ZPM.js.map} +0 -0
  385. /package/dist/{audit-3E5JAOP2.js.map → audit-BO7JQYIH.js.map} +0 -0
  386. /package/dist/{cursor-W7I4QUFO.js.map → cursor-RSLHA2CY.js.map} +0 -0
  387. /package/dist/{openclaw-IJ33K45M.js.map → openclaw-3DHVHX5P.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/omp/index.ts"],"sourcesContent":["/**\r\n * adapters/omp — Oh My Pi (OMP) platform adapter for agent-connector.\r\n *\r\n * OMP is a ts-plugin host with NATIVE MCP. The two surfaces are split:\r\n *\r\n * • MCP (native, real file) — OMP reads a real `mcp.json`:\r\n * user scope → <agentDir>/mcp.json (agentDir = ~/.omp/agent)\r\n * project scope → <projectDir>/.omp/mcp.json\r\n * JSON, root key \"mcpServers\", stdio entry { command, args, env } — the\r\n * portable field names. Verified against can1357/oh-my-pi\r\n * (packages/utils/src/dirs.ts getMCPConfigPath + docs/mcp-config.md). OMP\r\n * documents no native ${env:VAR} token, so env refs resolve to literals at\r\n * install time (resolveEnvRefsDeep).\r\n *\r\n * • Hooks (ts-plugin, in-process) — OMP loads EXTENSION PACKAGES. Its plugin\r\n * loader reads a package whose package.json carries an `omp` (or `pi`)\r\n * manifest field; the manifest's `extensions` array points at the plugin\r\n * module, whose DEFAULT export is a HookFactory `(pi) => void` that calls\r\n * `pi.on(event, handler)` (upstream loader.ts:75\r\n * `pluginPkg.omp || pluginPkg.pi`; hook factory types.ts:809). context-mode\r\n * proved this by dropping an extension under\r\n * `<agentDir>/extensions/<name>/{package.json,index.js}` whose index.js\r\n * default-exports the OMP plugin factory (src/setup.ts applyPlatformInstall\r\n * \"omp\"; src/adapters/omp/plugin.ts).\r\n *\r\n * Why we SYNTHESIZE instead of importing context-mode's plugin:\r\n * context-mode could load its own plugin module in-process because the handler\r\n * code shipped with the package. agent-connector cannot — the connector's hook\r\n * handlers are arbitrary developer code we must not import into OMP's runtime\r\n * (wrong cwd, wrong deps, version skew, the cache-heal bug class). So, exactly\r\n * like the OpenCode adapter, we generate a tiny self-contained ESM extension\r\n * that imports NOTHING from agent-connector: each `pi.on(...)` handler shells\r\n * out to the ONE stable home binary's universal entrypoint\r\n * (`<homeBin> hook omp <event> --connector <id>`) over child_process, feeds it\r\n * the OMP-shaped payload as JSON on stdin, and JSON.parses the normalized\r\n * HookResponse back from stdout. Fail-open: any bridge error → no-op. One\r\n * entrypoint, every paradigm.\r\n *\r\n * Event mapping (only events the connector declares are subscribed):\r\n * PreToolUse → pi.on(\"tool_call\", …) block via { block:true, reason }\r\n * (OMP tool_call cannot mutate args → canModifyArgs:false)\r\n * PostToolUse → pi.on(\"tool_result\", …) observe-only (no output rewrite hook)\r\n * SessionStart → pi.on(\"session_start\", …) (no context-injection surface)\r\n * PreCompact → pi.on(\"session_before_compact\", …) observe-only\r\n *\r\n * Distinct storage root from Pi: OMP lives under ~/.omp (NOT ~/.pi); the\r\n * PI_CODING_AGENT_DIR env var (the only OMP runtime dir override — OMP mirrors\r\n * any .env `OMP_*` keys to `PI_*` before process.env is read) takes precedence.\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 PreCompactEvent,\r\n PreToolUseEvent,\r\n ServerDef,\r\n SessionStartEvent,\r\n Transport,\r\n} from \"../../core/types.js\";\r\nimport { resolveEnvRefsDeep } 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\n\r\nconst HOST: PlatformId = \"omp\";\r\nconst MCP_ROOT_KEY = \"mcpServers\";\r\n\r\n/**\r\n * Canonical → OMP event name map. A connector hook event is only subscribed by\r\n * the generated plugin when it appears here AND is declared by the connector.\r\n * Names verified against the proven context-mode OMP plugin (pi.on targets).\r\n */\r\nconst EVENT_TO_OMP: Partial<Record<HookEventName, string>> = {\r\n PreToolUse: \"tool_call\",\r\n PostToolUse: \"tool_result\",\r\n SessionStart: \"session_start\",\r\n PreCompact: \"session_before_compact\",\r\n};\r\n\r\n/** Raw payload the generated plugin posts to the universal hook entrypoint. */\r\ninterface OmpBridgePayload {\r\n toolName?: string;\r\n toolInput?: Record<string, unknown>;\r\n toolOutput?: string;\r\n isError?: boolean;\r\n sessionId?: string;\r\n projectDir?: string;\r\n}\r\n\r\n/** Native MCP server entry shapes OMP accepts under \"mcpServers\". */\r\ninterface OmpStdioServer {\r\n command: string;\r\n args?: string[];\r\n env?: Record<string, string>;\r\n}\r\ninterface OmpRemoteServer {\r\n type: \"http\" | \"sse\";\r\n url: string;\r\n headers?: Record<string, string>;\r\n}\r\n\r\nexport class OMPAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name = \"Oh My Pi (OMP)\";\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 preToolUse: true,\r\n postToolUse: true,\r\n // session_before_compact is wired as an observe-only PreCompact surface.\r\n preCompact: true,\r\n sessionStart: true,\r\n sessionEnd: false,\r\n userPromptSubmit: false,\r\n stop: false,\r\n notification: false,\r\n // Newer events: OMP's agent_start/agent_end/turn_* hooks are the MAIN loop\r\n // (no subagent lifecycle events), there is no permission/approval surface,\r\n // and tool failure is merged into tool_result (isError) rather than a\r\n // dedicated event. None are wired, 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 // OMP tool_call gates via { block, reason } only — it does not hand the\r\n // handler a mutable args object, so input rewrite is unsupported.\r\n canModifyArgs: false,\r\n // No tool_result output-rewrite surface upstream.\r\n canModifyOutput: false,\r\n // No session_start context-injection surface upstream.\r\n canInjectSessionContext: false,\r\n // OMP registers stdio and remote (streamable http + sse) MCP servers.\r\n transports: [\"stdio\", \"sse\", \"http\"],\r\n // Action surface: each declared action becomes a user-invokable slash\r\n // command registered INSIDE the generated plugin module (pi.registerCommand),\r\n // shelling out to the home-bin `action` verb. supportsActions → the\r\n // installActions/uninstallActions overrides below.\r\n supportsActions: true,\r\n // Native passthrough hooks: OMP's main-loop lifecycle events —\r\n // agent_start, agent_end, turn_start, turn_end — have NO canonical\r\n // HookEventName analog (verified in oh-my-pi shared-events.ts). A connector\r\n // reaches them by declaring platforms[\"omp\"].nativeHooks; the generated\r\n // plugin registers + bridges each declared native event verbatim\r\n // (buildPluginSource native loop), and the home-bin runtime dispatches it\r\n // host-generically via runNativeHook.\r\n supportsNativeHooks: true,\r\n };\r\n\r\n // ── Native paths ───────────────────────────────────────────────────────\r\n\r\n /**\r\n * Resolve the OMP agent dir, honoring the PI_CODING_AGENT_DIR override (the\r\n * only OMP runtime dir env), else ~/.omp/agent. This is the user-scope root.\r\n */\r\n private agentDir(): string {\r\n const override = process.env.PI_CODING_AGENT_DIR;\r\n return override && override !== \"\"\r\n ? override\r\n : join(homedir(), \".omp\", \"agent\");\r\n }\r\n\r\n getConfigDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".omp\")\r\n : this.agentDir();\r\n }\r\n\r\n /** Native MCP file: <agentDir>/mcp.json (user) or <projectDir>/.omp/mcp.json. */\r\n getServerConfigPath(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"mcp.json\");\r\n }\r\n\r\n /**\r\n * For this ts-plugin host the \"hook config path\" is the generated extension\r\n * entrypoint module (index.js). OMP loads the extension package by reading its\r\n * package.json manifest, so the package.json is written alongside (see\r\n * synthesizePlugin); this returns the entry module the manifest points at.\r\n */\r\n getHookConfigPath(ctx: InstallContext): string {\r\n return join(this.extensionDir(ctx), \"index.js\");\r\n }\r\n\r\n /** Extension package directory OMP loads the plugin from, per scope. */\r\n private extensionDir(ctx: InstallContext): string {\r\n const base =\r\n ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".omp\")\r\n : this.agentDir();\r\n return join(base, \"extensions\", ctx.connector.id);\r\n }\r\n\r\n // ── Detection ──────────────────────────────────────────────────────────\r\n\r\n detectInstalled(projectDir: string): DetectedPlatform {\r\n const userAgentDir = this.agentDir();\r\n const userConfig = join(userAgentDir, \"mcp.json\");\r\n const projectConfig = join(projectDir, \".omp\", \"mcp.json\");\r\n\r\n const userInstalled = existsSync(userAgentDir) || existsSync(userConfig);\r\n const projInstalled =\r\n existsSync(join(projectDir, \".omp\")) || existsSync(projectConfig);\r\n const installed = userInstalled || projInstalled;\r\n\r\n // Report the marker that actually matched.\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 OMP dir at ${join(projectDir, \".omp\")}`\r\n : `found OMP agent dir at ${userAgentDir}`\r\n : `no OMP agent dir at ${userAgentDir}`,\r\n confidence: installed ? \"high\" : \"low\",\r\n };\r\n }\r\n\r\n // ── MCP server install / uninstall (native mcp.json) ────────────────────\r\n\r\n installServer(ctx: InstallContext): ChangeRecord[] {\r\n const server = this.effectiveServer(ctx);\r\n const serverPath = this.getServerConfigPath(ctx);\r\n\r\n if (!server) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: serverPath,\r\n detail: ctx.connector.server\r\n ? \"server registration disabled for omp\"\r\n : \"connector declares no MCP server\",\r\n },\r\n ];\r\n }\r\n\r\n const changes: ChangeRecord[] = [];\r\n // Honesty: OMP documents stdio/sse/http. A connector declaring another\r\n // transport (e.g. the non-spec \"ws\") is still rendered best-effort — the\r\n // remote branch coerces it to type:\"http\" (OMP rejects an unknown `type`) —\r\n // but the mismatch is REPORTED, never silently downgraded (same \"degradation\r\n // is never silent\" posture as the hook warn-skips).\r\n if (!this.capabilities.transports.includes(server.transport)) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n path: serverPath,\r\n detail: `transport \"${server.transport}\" is not an OMP MCP transport (stdio/sse/http); registered best-effort as type:\"http\"`,\r\n });\r\n }\r\n\r\n const entry = this.renderServerEntry(ctx, server);\r\n changes.push(\r\n this.upsertServerInJson(\r\n serverPath,\r\n MCP_ROOT_KEY,\r\n ctx.connector.id,\r\n entry,\r\n ctx.dryRun,\r\n ),\r\n );\r\n return changes;\r\n }\r\n\r\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\r\n const serverPath = this.getServerConfigPath(ctx);\r\n return [\r\n this.removeServerFromJson(\r\n serverPath,\r\n MCP_ROOT_KEY,\r\n ctx.connector.id,\r\n ctx.dryRun,\r\n ),\r\n ];\r\n }\r\n\r\n /** Resolve the per-platform server override into an effective ServerDef. */\r\n private effectiveServer(ctx: InstallContext): ServerDef | undefined {\r\n const override = ctx.connector.platforms[HOST]?.server;\r\n if (override === false) return undefined;\r\n const base = ctx.connector.server;\r\n if (!base) return undefined;\r\n return override && typeof override === \"object\"\r\n ? { ...base, ...override }\r\n : base;\r\n }\r\n\r\n /**\r\n * Render a normalized ServerDef into OMP's native \"mcpServers\" entry.\r\n *\r\n * stdio → { command, args, env } (portable field names; `type` omitted —\r\n * OMP defaults an entry with no `type` to stdio)\r\n * remote → { type: \"http\"|\"sse\", url, headers? } (`type` is REQUIRED for a\r\n * URL server — without it OMP would mis-parse the entry as stdio)\r\n *\r\n * Telemetry wrapping routes the real command through\r\n * `<homeBin> serve --connector <id> -- <command> <args...>`. OMP has no native\r\n * env interpolation token, so every ${env:VAR} ref resolves to a literal.\r\n */\r\n private renderServerEntry(\r\n ctx: InstallContext,\r\n server: ServerDef,\r\n ): OmpStdioServer | OmpRemoteServer {\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 ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\r\n\r\n const entry: OmpStdioServer = {\r\n command: resolveEnvRefsDeep(command),\r\n };\r\n const resolvedArgs = resolveEnvRefsDeep(args).filter((s) => s !== \"\");\r\n if (resolvedArgs.length > 0) entry.args = resolvedArgs;\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 // remote (http / sse) — OMP registers a URL with an explicit `type`\r\n // discriminator. OMP defaults a type-less entry to stdio (docs/mcp-config.md\r\n // \"stdio is the default when type is omitted\"), so a URL server MUST carry\r\n // its type or it is mis-parsed as a command.\r\n const entry: OmpRemoteServer = {\r\n type: server.transport === \"sse\" ? \"sse\" : \"http\",\r\n url: resolveEnvRefsDeep(server.url ?? \"\"),\r\n };\r\n const headers = this.renderEnv(server.headers);\r\n if (headers) entry.headers = headers;\r\n return entry;\r\n }\r\n\r\n /** Resolve every ${env:VAR} ref to a literal (OMP has no native token). */\r\n private renderEnv(\r\n env: Record<string, string> | undefined,\r\n ): Record<string, string> | undefined {\r\n if (!env || Object.keys(env).length === 0) return undefined;\r\n return resolveEnvRefsDeep({ ...env });\r\n }\r\n\r\n // ── Hook install / uninstall (ts-plugin extension package) ──────────────\r\n\r\n installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const entryPath = this.getHookConfigPath(ctx);\r\n\r\n // Native passthrough events are a sibling, omp-scoped declaration: `hooks:\r\n // false` disables only the CANONICAL events, and the generated native loop\r\n // reads platforms.omp.nativeHooks directly (independent of hookEvents). So\r\n // whenever native events exist the plugin must still be synthesized.\r\n const nativeEvents = Object.keys(\r\n ctx.connector.platforms[HOST]?.nativeHooks ?? {},\r\n );\r\n\r\n if (ctx.connector.platforms[HOST]?.hooks === false && nativeEvents.length === 0) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: entryPath,\r\n detail: \"hooks disabled for omp\",\r\n },\r\n ];\r\n }\r\n // Empty-events gate: the plugin module is SHARED with the action surface, so\r\n // a connector with actions-but-no-hooks still needs it written — but that is\r\n // installActions' job. installHooks itself only writes when there ARE hooks,\r\n // so its skip detail stays honest about why (no hooks) while admitting the\r\n // plugin may still exist for actions. Native events are a third reason to\r\n // write it, so they suppress the skip too.\r\n if (ctx.connector.hookEvents.length === 0 && nativeEvents.length === 0) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: entryPath,\r\n detail:\r\n this.actionTriggers(ctx).length > 0\r\n ? \"no hooks (plugin written for actions)\"\r\n : \"connector declares no hooks\",\r\n },\r\n ];\r\n }\r\n\r\n return this.ensurePlugin(ctx, (file) =>\r\n file.path.endsWith(\"package.json\")\r\n ? \"omp extension manifest\"\r\n : `omp plugin module (${this.hookDetail(ctx)})`,\r\n );\r\n }\r\n\r\n // ── Action install / uninstall (slash commands in the shared plugin) ────\r\n\r\n /**\r\n * Each declared action becomes a `pi.registerCommand(...)` inside the SAME\r\n * generated extension module (see buildPluginSource). So installActions just\r\n * ensures that shared module is on disk: when hooks already wrote the identical\r\n * bytes the content-compare yields \"skip\" (no double material write), and an\r\n * actions-only connector gets the plugin written here. Honors\r\n * platforms.omp.actions === false and the empty-actions skip.\r\n */\r\n override installActions(ctx: InstallContext): ChangeRecord[] {\r\n const entryPath = this.getHookConfigPath(ctx);\r\n if (ctx.connector.platforms[HOST]?.actions === false) {\r\n return [\r\n { platform: this.id, action: \"skip\", path: entryPath, detail: \"actions disabled for omp\" },\r\n ];\r\n }\r\n if (this.actionTriggers(ctx).length === 0) {\r\n return [\r\n { platform: this.id, action: \"skip\", path: entryPath, detail: \"connector declares no actions\" },\r\n ];\r\n }\r\n return this.ensurePlugin(ctx, (file) =>\r\n file.path.endsWith(\"package.json\")\r\n ? \"omp extension manifest\"\r\n : \"omp plugin module (action commands)\",\r\n );\r\n }\r\n\r\n /**\r\n * Actions live INSIDE the shared plugin module, which uninstallHooks removes\r\n * unconditionally (it runs last in the teardown pass). Removing it here too\r\n * would race uninstallHooks, so this is a single informational skip.\r\n */\r\n override uninstallActions(ctx: InstallContext): ChangeRecord[] {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: this.getHookConfigPath(ctx),\r\n detail: \"action commands are embedded in the plugin module; removed by uninstallHooks\",\r\n },\r\n ];\r\n }\r\n\r\n /**\r\n * Synthesize + content-compare-write the extension package (package.json +\r\n * index.js). Shared by installHooks and installActions: the generator is a pure\r\n * function of ctx, so whichever surface runs second sees byte-identical files\r\n * and reports \"skip\" — no double material write. `detailFor` flavors the\r\n * ChangeRecord detail per surface.\r\n */\r\n private ensurePlugin(\r\n ctx: InstallContext,\r\n detailFor: (file: GeneratedPluginFile) => string,\r\n ): ChangeRecord[] {\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 detailFor(file),\r\n file.executable,\r\n ),\r\n );\r\n }\r\n\r\n return changes;\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_OMP are mapped/wired; any declared\r\n * event with no OMP mapping (e.g. UserPromptSubmit) 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 (buildPluginSource), so\r\n // the detail must not 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_OMP[e] !== undefined);\r\n const unsupported = declared.filter((e) => EVENT_TO_OMP[e] === undefined);\r\n const nativeEvents = Object.keys(\r\n ctx.connector.platforms[HOST]?.nativeHooks ?? {},\r\n );\r\n const parts = [mapped.join(\",\")].filter((p) => p !== \"\");\r\n if (nativeEvents.length > 0) {\r\n parts.push(`native: ${nativeEvents.join(\",\")}`);\r\n }\r\n const base = parts.join(\"; \");\r\n return unsupported.length > 0\r\n ? `${base}; unsupported here: ${unsupported.join(\",\")}`\r\n : base;\r\n }\r\n\r\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\r\n const entryPath = this.getHookConfigPath(ctx);\r\n const manifestPath = join(this.extensionDir(ctx), \"package.json\");\r\n const present = [entryPath, manifestPath].filter((p) => existsSync(p));\r\n\r\n if (present.length === 0) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: entryPath,\r\n detail: \"no omp extension present\",\r\n },\r\n ];\r\n }\r\n\r\n const changes: ChangeRecord[] = [];\r\n for (const p of present) {\r\n const detail = p.endsWith(\"package.json\") ? \"omp extension manifest\" : \"omp plugin module\";\r\n changes.push(this.removeManagedFile(p, ctx.dryRun, detail, `no ${detail} present`));\r\n }\r\n return changes;\r\n }\r\n\r\n // ── ts-plugin synthesis ────────────────────────────────────────────────\r\n\r\n /**\r\n * Build the OMP extension package: TWO files.\r\n *\r\n * 1. package.json — the manifest OMP's loader reads. It carries the `omp`\r\n * field (`pluginPkg.omp || pluginPkg.pi`) whose `extensions` array points\r\n * at the entry module, plus `type:\"module\"` and `main:\"index.js\"`.\r\n * 2. index.js — a self-contained ESM plugin that imports nothing from\r\n * agent-connector. Its default export is the OMP HookFactory `(pi)=>void`;\r\n * each declared event registers a `pi.on(...)` handler that bridges to the\r\n * home binary via execFileSync (fail-open) and applies the normalized\r\n * response.\r\n */\r\n synthesizePlugin(ctx: InstallContext): GeneratedPluginFile[] {\r\n const dir = this.extensionDir(ctx);\r\n return [\r\n {\r\n path: join(dir, \"package.json\"),\r\n contents: this.buildManifest(ctx),\r\n executable: false,\r\n },\r\n {\r\n path: join(dir, \"index.js\"),\r\n contents: this.buildPluginSource(ctx),\r\n executable: false,\r\n },\r\n ];\r\n }\r\n\r\n /** The extension package.json manifest OMP's plugin loader reads. */\r\n private buildManifest(ctx: InstallContext): string {\r\n const manifest = {\r\n name: ctx.connector.id,\r\n version: ctx.connector.version || \"0.0.0\",\r\n description: `${ctx.connector.displayName} OMP extension (generated by agent-connector)`,\r\n type: \"module\",\r\n main: \"index.js\",\r\n // pluginPkg.omp || pluginPkg.pi → manifest. extensions points at the entry.\r\n omp: {\r\n extensions: [\"./index.js\"],\r\n },\r\n };\r\n return `${JSON.stringify(manifest, null, 2)}\\n`;\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.omp.hooks === false` disables only the CANONICAL events (the\r\n // native loop below reads platforms.omp.nativeHooks directly and is a\r\n // sibling, unaffected). When canonical hooks are off the module may still be\r\n // synthesized for native events and/or actions, so the canonical handler set\r\n // collapses to 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_OMP[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 Oh My Pi (OMP) plugin extension 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 OMP).\r\n *\r\n * OMP loads this module via the sibling package.json \"omp\" manifest field; the\r\n * default export is the HookFactory (pi) => void called once at load time.\r\n */\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: \"omp\",\r\n payloadNoun: \"OMP\",\r\n failOpenComment: \"Fail-open — never break an OMP tool call / lifecycle event.\",\r\n})}\r\n\r\n// OMP exposes the project dir via PI_PROJECT_DIR (PI_*-prefixed only); fall\r\n// through to cwd. The plugin process is long-lived, so resolve once at load.\r\nconst PROJECT_DIR = process.env.PI_PROJECT_DIR || process.cwd();\r\n\r\n// Derive a stable session id from OMP's session manager when available\r\n// (ctx.sessionManager.getSessionFile()), else a wall-clock token. The id is\r\n// rebound on each session_start so multi-session reuse stays attributed.\r\nlet SESSION_ID = \"\";\r\nfunction deriveSessionId(ctx) {\r\n try {\r\n const f = ctx && ctx.sessionManager && typeof ctx.sessionManager.getSessionFile === \"function\"\r\n ? ctx.sessionManager.getSessionFile()\r\n : \"\";\r\n if (f && typeof f === \"string\") return f;\r\n } catch {\r\n // best effort\r\n }\r\n return \"omp-\" + Date.now();\r\n}\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 pi.on(\"session_start\", (_event, ctx) => {\r\n SESSION_ID = deriveSessionId(ctx);\r\n bridge(\"SessionStart\", { sessionId: SESSION_ID, projectDir: PROJECT_DIR });\r\n return undefined;\r\n });`);\r\n }\r\n\r\n if (has(\"PreToolUse\")) {\r\n handlers.push(` // PreToolUse → block via { block, reason }. OMP tool_call cannot mutate\r\n // args, so \"ask\" degrades to a block (the safe direction); \"modify\" degrades\r\n // to ALLOW (no-block), matching opencode/claude-code — only deny/ask block.\r\n pi.on(\"tool_call\", (event) => {\r\n const payload = {\r\n toolName: (event && event.toolName) || \"\",\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) return undefined;\r\n if (res.decision === \"deny\" || res.decision === \"ask\") {\r\n return { block: true, reason: res.reason || \"Blocked by ${ctx.connector.id}\" };\r\n }\r\n return undefined;\r\n });`);\r\n }\r\n\r\n if (has(\"PostToolUse\")) {\r\n handlers.push(` // PostToolUse → observe tool results (no output-rewrite surface upstream).\r\n pi.on(\"tool_result\", (event) => {\r\n const content = (event && Array.isArray(event.content)) ? event.content : [];\r\n const toolOutput = content\r\n .filter((c) => c && c.type === \"text\" && typeof c.text === \"string\")\r\n .map((c) => c.text)\r\n .join(\"\\\\n\");\r\n const payload = {\r\n toolName: (event && event.toolName) || \"\",\r\n toolInput: (event && event.input) || {},\r\n toolOutput,\r\n isError: !!(event && event.isError),\r\n sessionId: SESSION_ID,\r\n projectDir: PROJECT_DIR,\r\n };\r\n bridge(\"PostToolUse\", payload);\r\n return undefined;\r\n });`);\r\n }\r\n\r\n if (has(\"PreCompact\")) {\r\n handlers.push(` // PreCompact → notify before OMP compacts the context window.\r\n pi.on(\"session_before_compact\", () => {\r\n bridge(\"PreCompact\", { sessionId: SESSION_ID, projectDir: PROJECT_DIR });\r\n return undefined;\r\n });`);\r\n }\r\n\r\n // NATIVE passthrough events: OMP's main-loop lifecycle hooks (agent_start,\r\n // agent_end, turn_start, turn_end) have NO canonical analog, so they are not\r\n // in EVENT_TO_OMP. Each declared native event registers a pi.on(...) handler\r\n // that bridges the NATIVE event name verbatim to the home-bin → runNativeHook\r\n // dispatches it host-generically. Read straight off platforms.omp.nativeHooks\r\n // (independent of hookEvents), so they install even with hooks:false.\r\n const nativeEvents = Object.keys(\r\n ctx.connector.platforms[HOST]?.nativeHooks ?? {},\r\n );\r\n for (const ev of nativeEvents) {\r\n handlers.push(` // nativeHooks passthrough → ${ev}\r\n pi.on(${JSON.stringify(ev)}, (event) => {\r\n bridge(${JSON.stringify(ev)}, { sessionId: SESSION_ID, projectDir: PROJECT_DIR, raw: event });\r\n return undefined;\r\n });`);\r\n }\r\n\r\n // Action surface: one pi.registerCommand per declared action. The command\r\n // shells out to the home-bin `action` verb (host token literal \"omp\" — OMP\r\n // has no fork). Fail-open: a launcher error degrades to a no-op (never wedges\r\n // OMP). Both id AND description are JSON.stringify'd — descriptions are\r\n // free-form connector text, so a raw \" / backtick would break the module.\r\n const actionHandlers: string[] = [];\r\n for (const action of this.actionTriggers(ctx)) {\r\n const id = JSON.stringify(action.id);\r\n const description = JSON.stringify(action.description);\r\n actionHandlers.push(` // Action /${action.id} → run the home-bin action verb directly.\r\n pi.registerCommand(${id}, {\r\n description: ${description},\r\n handler: async (_args, _ctx) => {\r\n try {\r\n const args = [\"action\", \"omp\", ${id}, \"--connector\", CONNECTOR_ID];\r\n process.platform === \"win32\"\r\n ? execSync([HOME_BIN, ...args].map((a) => '\"' + a + '\"').join(\" \"), { stdio: \"inherit\" })\r\n : execFileSync(HOME_BIN, args, { stdio: \"inherit\" });\r\n } catch { /* fail-open */ }\r\n return undefined;\r\n },\r\n });`);\r\n }\r\n\r\n // When hook events are empty but actions exist, the factory body is just the\r\n // action registrations (still valid JS — an empty handlers[] yields \"\").\r\n const factory = `\r\nexport default function plugin(pi) {\r\n${[...handlers, ...actionHandlers].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?, 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 OmpBridgePayload;\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 const ev: PostToolUseEvent = {\r\n ...base,\r\n toolName: input.toolName ?? \"\",\r\n toolInput: input.toolInput ?? {},\r\n ...(typeof input.toolOutput === \"string\"\r\n ? { toolOutput: input.toolOutput }\r\n : {}),\r\n ...(typeof input.isError === \"boolean\"\r\n ? { isError: input.isError }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"PreCompact\": {\r\n const ev: PreCompactEvent = { ...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 OMP; 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 maps decision\r\n * → OMP's { block, reason } for tool_call (and ignores it otherwise).\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 serverPath = this.getServerConfigPath(ctx);\r\n const entryPath = this.getHookConfigPath(ctx);\r\n const manifestPath = join(this.extensionDir(ctx), \"package.json\");\r\n const connectorId = ctx.connector.id;\r\n // Actions are health-equivalent to hooks: both write the SAME extension\r\n // package, so an actions-only connector must still assert it is present.\r\n const hasHooks =\r\n ctx.connector.hookEvents.length > 0 || this.actionTriggers(ctx).length > 0;\r\n\r\n return [\r\n {\r\n name: `${this.name}: mcp.json present`,\r\n check: () =>\r\n existsSync(serverPath)\r\n ? { status: \"OK\", detail: serverPath }\r\n : { status: \"FAIL\", detail: `not found: ${serverPath}` },\r\n },\r\n {\r\n name: `${this.name}: ${MCP_ROOT_KEY}.${connectorId} registered`,\r\n check: () => {\r\n if (!ctx.connector.server) {\r\n return { status: \"OK\", detail: \"no MCP server declared\" };\r\n }\r\n const cfg = this.readJson<{ [k: string]: Record<string, unknown> }>(\r\n serverPath,\r\n );\r\n const bucket = cfg?.[MCP_ROOT_KEY];\r\n if (!cfg || !bucket) {\r\n return {\r\n status: \"FAIL\",\r\n detail: `no ${MCP_ROOT_KEY} in ${serverPath}`,\r\n };\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 ${serverPath}`,\r\n };\r\n },\r\n },\r\n {\r\n name: `${this.name}: extension package present`,\r\n check: () => {\r\n if (!hasHooks) return { status: \"OK\", detail: \"no hooks declared\" };\r\n if (!existsSync(manifestPath)) {\r\n return { status: \"FAIL\", detail: `not found: ${manifestPath}` };\r\n }\r\n return existsSync(entryPath)\r\n ? { status: \"OK\", detail: entryPath }\r\n : { status: \"FAIL\", detail: `not found: ${entryPath}` };\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\nexport const adapter = new OMPAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":["entry"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/open-interpreter/index.ts"],"sourcesContent":["/**\r\n * adapters/open-interpreter — Open Interpreter (the new Rust `interpreter` / `i`\r\n * CLI) platform adapter for agent-connector.\r\n *\r\n * Open Interpreter is a FORK of OpenAI's Codex. The project's own README states\r\n * verbatim: \"This is the new Rust version of Open Interpreter … Open Interpreter\r\n * is a fork of OpenAI's Codex, with a focus on emulating the agent harness that\r\n * gets the best performance out of low-cost models.\" (The original Python project\r\n * lives on as the community fork endolith/open-interpreter.) The repo at\r\n * github.com/openinterpreter/open-interpreter IS the codex-rs source tree.\r\n *\r\n * Because it is Codex, the native MCP config is Codex's:\r\n * - config file → <home>/config.toml (CONFIG_TOML_FILE = \"config.toml\";\r\n * codex-rs/config/src/lib.rs:33), TOML.\r\n * - MCP table → `[mcp_servers.<id>]` (codex-rs/config/src/mcp_edit.rs:\r\n * replace_mcp_servers inserts root key \"mcp_servers\"). stdio entry shape is\r\n * { command, args, env } and the streamable-HTTP entry is { url,\r\n * bearer_token_env_var?, http_headers? } (codex-rs/config/src/mcp_types.rs\r\n * RawMcpServerConfig). TOML has NO native interpolation, so `${env:VAR}` refs\r\n * resolve to LITERALS at install time (same rule as the codex adapter).\r\n *\r\n * Config HOME (byte-confirmed against codex-rs/utils/home-dir/src/lib.rs):\r\n * - The `interpreter` binary deliberately does NOT honor $CODEX_HOME (\"sharing\r\n * the Codex home leaks Codex config, update caches, and credentials into the\r\n * Interpreter identity\"). The ONLY honored override is $INTERPRETER_HOME, and\r\n * the default is ~/.openinterpreter. The install script\r\n * (scripts/install/install-open-interpreter.sh) confirms both: it sets\r\n * CODEX_COMMAND_NAME=interpreter and CODEX_HOME=\"${INTERPRETER_HOME:-$HOME/.openinterpreter}\".\r\n *\r\n * Paradigm: mcp-only. Codex's hook subsystem (codex-rs/hooks) is present in the\r\n * fork, but the `interpreter` PRODUCT's live hook wire contract is not first-party\r\n * verified here (a live authenticated run, which we cannot perform). Following the\r\n * project rule \"MCP-only unless hooks byte-confirmed\", this adapter registers the\r\n * MCP server only and reports hooks unavailable.\r\n *\r\n * Mirrors the codex adapter's TOML object-map machinery (shared @iarna/toml codec\r\n * + the core/object-map engine, policy \"coerce\"), trimmed to the MCP surface.\r\n */\r\n\r\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { dirname, join, resolve } from \"node:path\";\r\n\r\nimport TOML from \"@iarna/toml\";\r\n\r\nimport type {\r\n ChangeRecord,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookParadigm,\r\n PlatformCapabilities,\r\n PlatformId,\r\n ServerDef,\r\n} from \"../../core/types.js\";\r\nimport { ensureDir } from \"../../core/paths.js\";\r\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\r\nimport {\r\n removeFromObjectMap,\r\n upsertInObjectMap,\r\n type ObjectMapCodec,\r\n} from \"../../core/object-map.js\";\r\nimport { buildWrappedStdio } from \"../../core/spawn.js\";\r\nimport { BaseAdapter } from \"../base.js\";\r\nimport type { InstallContext } from \"../spi.js\";\r\n\r\nconst HOST: PlatformId = \"open-interpreter\";\r\nconst MCP_ROOT_KEY = \"mcp_servers\";\r\n\r\n/** Rendered `[mcp_servers.<id>]` table — string env table, no interpolation. */\r\ninterface OpenInterpreterMcpEntry {\r\n // stdio transport\r\n command?: string;\r\n args?: string[];\r\n env?: Record<string, string>;\r\n // streamable HTTP transport (remote): transport is inferred from `url`\r\n // (no explicit transport key), exactly like codex.\r\n url?: string;\r\n bearer_token_env_var?: string;\r\n http_headers?: Record<string, string>;\r\n}\r\n\r\nexport class OpenInterpreterAdapter extends BaseAdapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name = \"Open Interpreter\";\r\n readonly paradigm: HookParadigm = \"mcp-only\";\r\n\r\n readonly capabilities: PlatformCapabilities = {\r\n // mcp-only: every hook flag is false (no first-party-verified hook surface\r\n // for the `interpreter` product).\r\n preToolUse: false,\r\n postToolUse: false,\r\n preCompact: false,\r\n sessionStart: false,\r\n sessionEnd: false,\r\n userPromptSubmit: false,\r\n stop: false,\r\n notification: false,\r\n canModifyArgs: false,\r\n canModifyOutput: false,\r\n canInjectSessionContext: false,\r\n // config.toml [mcp_servers] supports stdio (command) + streamable HTTP (url),\r\n // inheriting codex's transport support.\r\n transports: [\"stdio\", \"http\"],\r\n };\r\n\r\n // ── Detection ──────────────────────────────────────────────────────────\r\n\r\n detectInstalled(_projectDir: string): DetectedPlatform {\r\n const userDir = this.userConfigDir();\r\n const userCfg = join(userDir, \"config.toml\");\r\n const installed = existsSync(userDir) || existsSync(userCfg);\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: userCfg,\r\n scope: \"user\",\r\n reason: installed\r\n ? `Found Open Interpreter config dir (${userDir})`\r\n : `No Open Interpreter config dir at ${userDir}`,\r\n confidence: installed ? \"high\" : \"low\",\r\n };\r\n }\r\n\r\n // ── Native paths ───────────────────────────────────────────────────────\r\n // User scope only — Open Interpreter (like codex's user scope) keeps its config\r\n // under the home dir. The home dir is $INTERPRETER_HOME (when set & non-empty)\r\n // or ~/.openinterpreter; $CODEX_HOME is deliberately NOT consulted (the fork\r\n // isolates the two identities — codex-rs/utils/home-dir/src/lib.rs).\r\n\r\n override getConfigDir(_ctx: InstallContext): string {\r\n return this.userConfigDir();\r\n }\r\n\r\n override getServerConfigPath(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"config.toml\");\r\n }\r\n\r\n /** Open Interpreter has no separate hook file — alias the hook config path to\r\n * the MCP file so the generic doctor/backup helpers behave sensibly (the\r\n * amazon-q / windsurf idiom for mcp-only hosts). */\r\n override getHookConfigPath(ctx: InstallContext): string {\r\n return this.getServerConfigPath(ctx);\r\n }\r\n\r\n /** $INTERPRETER_HOME (tilde-expanded, then resolved) when set & non-empty,\r\n * else ~/.openinterpreter. Byte-confirmed against codex-rs/utils/home-dir. */\r\n private userConfigDir(): string {\r\n const env = process.env.INTERPRETER_HOME;\r\n if (env && env.trim() !== \"\") {\r\n if (env.startsWith(\"~\")) return join(homedir(), env.replace(/^~[/\\\\]?/, \"\"));\r\n return resolve(env);\r\n }\r\n return join(homedir(), \".openinterpreter\");\r\n }\r\n\r\n // ── TOML config IO (config.toml is TOML, not JSON) ───────────────────────\r\n\r\n private readToml(path: string): Record<string, unknown> {\r\n if (!existsSync(path)) return {};\r\n try {\r\n return TOML.parse(readFileSync(path, \"utf8\")) as Record<string, unknown>;\r\n } catch {\r\n return {};\r\n }\r\n }\r\n\r\n private writeToml(path: string, data: Record<string, unknown>, dryRun: boolean): void {\r\n if (dryRun) return;\r\n ensureDir(dirname(path));\r\n // @iarna/toml's stringify type wants its JsonMap; our object is structurally compatible.\r\n writeFileSync(path, TOML.stringify(data as never), \"utf8\");\r\n }\r\n\r\n /** An ObjectMapCodec over config.toml for the core/object-map engine.\r\n * `isPresentButUnparseable` is `() => false`: readToml fail-softs to {} and the\r\n * server path coerces/overwrites rather than warn-skip on an unparseable file —\r\n * matching the codex adapter's coerce policy. */\r\n private tomlObjectMapCodec(): ObjectMapCodec {\r\n return {\r\n parse: (path) => this.readToml(path),\r\n serialize: (path, data, dryRun) => this.writeToml(path, data, dryRun),\r\n isPresentButUnparseable: () => false,\r\n };\r\n }\r\n\r\n // ── Install server (config.toml → [mcp_servers.<id>]) ───────────────────\r\n\r\n override installServer(ctx: InstallContext): ChangeRecord[] {\r\n const { connector, dryRun } = ctx;\r\n const server = this.effectiveServer(ctx);\r\n const path = this.getServerConfigPath(ctx);\r\n\r\n if (!server) {\r\n return [{ platform: this.id, action: \"skip\", path, detail: \"no server declared\" }];\r\n }\r\n const isStdio = server.transport === \"stdio\" && !!server.command;\r\n const isHttp = server.transport === \"http\" && !!server.url;\r\n if (!isStdio && !isHttp) {\r\n // config.toml [mcp_servers] supports stdio (command) + streamable HTTP\r\n // (url). Other remote transports (sse/ws) have no analog.\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path,\r\n detail: `transport \"${server.transport}\" not registrable in config.toml (stdio + streamable-http only)`,\r\n },\r\n ];\r\n }\r\n\r\n const symlink = this.symlinkPathWarning(path);\r\n if (symlink) return [symlink];\r\n\r\n const entry = this.renderMcpEntry(ctx, server);\r\n\r\n return [\r\n upsertInObjectMap({\r\n codec: this.tomlObjectMapCodec(),\r\n rootKey: MCP_ROOT_KEY,\r\n policy: \"coerce\",\r\n platform: this.id,\r\n configPath: path,\r\n entryId: connector.id,\r\n entry,\r\n dryRun,\r\n }),\r\n ];\r\n }\r\n\r\n override uninstallServer(ctx: InstallContext): ChangeRecord[] {\r\n const { connector, dryRun } = ctx;\r\n const path = this.getServerConfigPath(ctx);\r\n const symlink = this.symlinkPathWarning(path);\r\n if (symlink) return [symlink];\r\n\r\n return [\r\n removeFromObjectMap({\r\n codec: this.tomlObjectMapCodec(),\r\n rootKey: MCP_ROOT_KEY,\r\n policy: \"coerce\",\r\n platform: this.id,\r\n configPath: path,\r\n entryId: connector.id,\r\n dryRun,\r\n }),\r\n ];\r\n }\r\n\r\n // ── Hooks (unavailable — Open Interpreter is mcp-only here) ──────────────\r\n\r\n override installHooks(_ctx: InstallContext): ChangeRecord[] {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n detail: \"hooks unavailable (Open Interpreter is mcp-only)\",\r\n },\r\n ];\r\n }\r\n\r\n override uninstallHooks(_ctx: InstallContext): ChangeRecord[] {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n detail: \"hooks unavailable (Open Interpreter is mcp-only)\",\r\n },\r\n ];\r\n }\r\n\r\n // ── Health checks (default doctor renders these) ────────────────────────\r\n\r\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\r\n const path = this.getServerConfigPath(ctx);\r\n const id = ctx.connector.id;\r\n return [\r\n {\r\n name: `${this.name}: config.toml exists`,\r\n check: () =>\r\n existsSync(path)\r\n ? { status: \"OK\", detail: path }\r\n : { status: \"FAIL\", detail: `not found: ${path}` },\r\n },\r\n {\r\n name: `${this.name}: ${MCP_ROOT_KEY}.${id} registered`,\r\n check: () => {\r\n // Only assert what the connector declares: a server-less connector\r\n // never writes an [mcp_servers.<id>] table, 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.readToml(path);\r\n const bucket = cfg[MCP_ROOT_KEY];\r\n const present =\r\n typeof bucket === \"object\" &&\r\n bucket !== null &&\r\n id in (bucket as Record<string, unknown>);\r\n return present\r\n ? { status: \"OK\", detail: `${MCP_ROOT_KEY}.${id}` }\r\n : { status: \"FAIL\", detail: `${MCP_ROOT_KEY}.${id} not found in ${path}` };\r\n },\r\n },\r\n ];\r\n }\r\n\r\n // ── Internal helpers ────────────────────────────────────────────────────\r\n\r\n /** Resolve the per-platform server override into an effective ServerDef. */\r\n private effectiveServer(ctx: InstallContext): ServerDef | undefined {\r\n const override = ctx.connector.platforms[this.id]?.server;\r\n if (override === false) return undefined;\r\n const base = ctx.connector.server;\r\n if (!base) return undefined;\r\n return override ? { ...base, ...override } : base;\r\n }\r\n\r\n /**\r\n * Render the `[mcp_servers.<id>]` table. TOML has NO interpolation, so every\r\n * `${env:VAR}` is resolved to a literal at install time. Honors the telemetry\r\n * serve-wrapper for stdio.\r\n */\r\n private renderMcpEntry(ctx: InstallContext, server: ServerDef): OpenInterpreterMcpEntry {\r\n // Streamable HTTP server: transport is inferred from `url` (no explicit\r\n // transport key), exactly like codex. Telemetry serve-wrapping is stdio-only\r\n // (remote cannot be intercepted). installServer's guard only lets stdio+command\r\n // or http+url reach here, so this branch is exactly the streamable-HTTP case.\r\n if (server.transport === \"http\") {\r\n const remote: OpenInterpreterMcpEntry = { url: resolveEnvRefsDeep(server.url ?? \"\") };\r\n if (server.auth?.type === \"bearerEnv\" && server.auth.bearerEnvVar) {\r\n remote.bearer_token_env_var = server.auth.bearerEnvVar;\r\n }\r\n if (server.headers && Object.keys(server.headers).length > 0) {\r\n const headers: Record<string, string> = {};\r\n for (const [k, v] of Object.entries(resolveEnvRefsDeep(server.headers))) {\r\n headers[k] = String(v);\r\n }\r\n remote.http_headers = headers;\r\n }\r\n return remote;\r\n }\r\n\r\n let command = server.command as string;\r\n let args = [...(server.args ?? [])];\r\n\r\n ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\r\n\r\n // Resolve env-refs to literals (TOML cannot interpolate).\r\n command = resolveEnvRefsDeep(command);\r\n args = resolveEnvRefsDeep(args);\r\n\r\n const entry: OpenInterpreterMcpEntry = { command };\r\n if (args.length > 0) entry.args = args;\r\n\r\n if (server.env && Object.keys(server.env).length > 0) {\r\n const env: Record<string, string> = {};\r\n for (const [k, v] of Object.entries(resolveEnvRefsDeep(server.env))) {\r\n env[k] = String(v);\r\n }\r\n entry.env = env;\r\n }\r\n return entry;\r\n }\r\n}\r\n\r\nexport const adapter = new OpenInterpreterAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":[]}
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- OpenClawAdapter,
4
- adapter,
5
- openclaw_default
6
- } from "./chunk-TGZFDXNH.js";
7
- import "./chunk-XXLCMSD3.js";
8
- import "./chunk-3FINQ2ZF.js";
9
- import "./chunk-Y3K6KJMS.js";
10
- import "./chunk-ILWHLGIC.js";
11
- import "./chunk-QYOUQCC6.js";
12
- export {
13
- OpenClawAdapter,
14
- adapter,
15
- openclaw_default as default
16
- };
17
- //# sourceMappingURL=openclaw-IJ33K45M.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/usage/readers/openclaw.ts"],"sourcesContent":["/**\r\n * usage/readers/openclaw — OpenClaw (Gateway) native transcript reader.\r\n *\r\n * Faithful port of tokscale sessions/openclaw.rs (the transcript-direct path).\r\n * OpenClaw stores one JSONL transcript per session under an agent directory:\r\n * ~/.openclaw/agents/**\\/*.jsonl (current)\r\n * ~/.clawdbot/agents/**\\/*.jsonl (legacy: Clawd → Moltbot → OpenClaw rebrand)\r\n * ~/.moltbot/agents/**\\/*.jsonl (legacy)\r\n * ~/.moldbot/agents/**\\/*.jsonl (legacy)\r\n * The glob is `*.jsonl*` in tokscale (clients.rs OpenClaw.pattern), so archived\r\n * copies are also picked up: `name.jsonl`, `name.jsonl.deleted.<ts>`, and\r\n * `name.jsonl.reset.<ISO8601>`. The session id is the filename stem before the\r\n * first `.jsonl`, so all three forms map to the same session id.\r\n *\r\n * Each line is one entry with a `type`:\r\n * - \"model_change\" → snapshot of the current model/provider (entry.modelId /\r\n * entry.provider). STATEFUL: applies to later messages.\r\n * - \"custom\" + customType==\"model-snapshot\" → same, from entry.data.{modelId,\r\n * provider}. STATEFUL.\r\n * - \"message\" → only `role:\"assistant\"` lines with a `usage` block count.\r\n * Tokens come straight from message.usage.{input, output,\r\n * cacheRead, cacheWrite}; cost from usage.cost.total.\r\n * Model/provider prefer the inline message.{model,provider}\r\n * (when non-empty), else fall back to the tracked current\r\n * model/provider state; provider defaults to \"unknown\".\r\n * reasoning is always 0 (OpenClaw does not report it).\r\n *\r\n * DEDUP — this is where double-counting hides. tokscale's PRODUCTION scan parses\r\n * ONLY the transcripts via parse_openclaw_transcript (one `*.jsonl*` file at a\r\n * time, each scan root visited once). The legacy `sessions.json` index parser\r\n * (parse_openclaw_index) is NOT wired into the live scan — it points at the SAME\r\n * `.jsonl` files the walk already visits, so parsing both would double-count.\r\n * We therefore mirror production: walk transcripts only, never the index. The\r\n * per-record dedupKey (`openclaw:<sessionId>:<entry.id|ordinal>`) collapses the\r\n * same transcript line if it is ever seen twice (e.g. a `.jsonl` plus its\r\n * `.deleted`/`.reset` archive of the same session), keeping totals honest while\r\n * never merging genuinely distinct messages.\r\n *\r\n * Project/workspace attribution: not exposed in the OpenClaw transcript format,\r\n * so projectKey/projectLabel are left unset (matching the Rust parser, which\r\n * never sets workspace on these rows).\r\n *\r\n * Confidence: \"host-reported\" (real token counts logged by the host).\r\n *\r\n * Fail-open: no agent root present → []; an unreadable/malformed file or line is\r\n * skipped (never thrown). Read-only — this reader only enumerates and reads.\r\n */\r\n\r\nimport { basename, isAbsolute, join, resolve } from \"node:path\";\r\nimport { homedir } from \"node:os\";\r\n\r\nimport type { UsageReader, UsageRecord } from \"../types.js\";\r\nimport { emptyTokens } from \"../aggregate.js\";\r\nimport { fileMtimeMs, readJsonlLines } from \"../jsonl.js\";\r\nimport { expandHome, walkFiles } from \"../paths.js\";\r\n\r\nconst PLATFORM_ID = \"openclaw\" as const;\r\nconst DEFAULT_PROVIDER = \"unknown\";\r\n\r\n/** One OpenClaw JSONL entry (every field optional / unknown). */\r\ninterface OpenClawEntry {\r\n type?: unknown;\r\n /** Stable per-entry id (e.g. \"msg1\") — used for the dedup key when present. */\r\n id?: unknown;\r\n message?: OpenClawMessage;\r\n /** \"custom\" entry discriminator (e.g. \"model-snapshot\"). */\r\n customType?: unknown;\r\n /** Payload for a \"custom\" model-snapshot entry. */\r\n data?: { provider?: unknown; modelId?: unknown };\r\n /** Set on a \"model_change\" entry. */\r\n modelId?: unknown;\r\n provider?: unknown;\r\n}\r\n\r\n/** The `message` object on a \"message\" entry. */\r\ninterface OpenClawMessage {\r\n role?: unknown;\r\n usage?: OpenClawUsage;\r\n timestamp?: unknown;\r\n provider?: unknown;\r\n model?: unknown;\r\n}\r\n\r\n/** The `message.usage` token/cost block. */\r\ninterface OpenClawUsage {\r\n input?: unknown;\r\n output?: unknown;\r\n cacheRead?: unknown;\r\n cacheWrite?: unknown;\r\n cost?: { total?: unknown };\r\n}\r\n\r\n/** Coerce an unknown to a non-negative integer (0 on absence/garbage). */\r\nfunction toNonNegInt(v: unknown): number {\r\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\r\n if (!Number.isFinite(n)) return 0;\r\n return Math.max(0, Math.trunc(n));\r\n}\r\n\r\n/** Coerce an unknown to a finite non-negative number, else undefined. */\r\nfunction toNonNegFloat(v: unknown): number | undefined {\r\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\r\n if (!Number.isFinite(n)) return undefined;\r\n return Math.max(0, n);\r\n}\r\n\r\n/** A non-empty trimmed string, or undefined. */\r\nfunction nonEmptyString(v: unknown): string | undefined {\r\n if (typeof v !== \"string\") return undefined;\r\n return v === \"\" ? undefined : v;\r\n}\r\n\r\n/**\r\n * Parse the OpenClaw `timestamp` (i64 milliseconds) to epoch ms, or null when\r\n * unusable. The Rust parser treats it as raw millis; we accept a numeric string\r\n * too and tolerate a seconds-scale value defensively.\r\n */\r\nfunction parseTs(v: unknown): number | null {\r\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\r\n if (!Number.isFinite(n) || n <= 0) return null;\r\n return n >= 1e12 ? n : n * 1000;\r\n}\r\n\r\n/**\r\n * Session id = the filename stem before the FIRST `.jsonl` (port of\r\n * parse_openclaw_transcript's `split_once(\".jsonl\")`). Handles archived suffixes:\r\n * \"abc.jsonl\" → \"abc\"\r\n * \"abc.jsonl.deleted.1700000000000\" → \"abc\"\r\n * \"abc.jsonl.reset.2026-03-20T06-34-44.520Z\" → \"abc\"\r\n * Returns null when there is no `.jsonl` segment or the stem is empty.\r\n */\r\nfunction sessionIdFromFilename(path: string): string | null {\r\n const name = basename(path);\r\n const idx = name.indexOf(\".jsonl\");\r\n if (idx < 0) return null;\r\n const id = name.slice(0, idx);\r\n return id === \"\" ? null : id;\r\n}\r\n\r\n/**\r\n * Parse one OpenClaw transcript into usage records (port of\r\n * parse_openclaw_session). Tracks current model/provider statefully across the\r\n * file and emits one record per assistant message that carries a usage block.\r\n */\r\nfunction parseOpenclawTranscript(path: string): UsageRecord[] {\r\n const sessionId = sessionIdFromFilename(path);\r\n if (sessionId === null) return [];\r\n\r\n const lines = readJsonlLines(path);\r\n if (lines.length === 0) return [];\r\n\r\n const mtime = fileMtimeMs(path);\r\n const out: UsageRecord[] = [];\r\n\r\n let currentModel: string | undefined;\r\n let currentProvider: string | undefined;\r\n let ordinal = 0; // fallback per-message identity when entry.id is absent\r\n\r\n for (const raw of lines) {\r\n if (typeof raw !== \"object\" || raw === null) continue;\r\n const entry = raw as OpenClawEntry;\r\n const type = entry.type;\r\n\r\n if (type === \"model_change\") {\r\n const model = nonEmptyString(entry.modelId);\r\n if (model !== undefined) currentModel = model;\r\n const provider = nonEmptyString(entry.provider);\r\n if (provider !== undefined) currentProvider = provider;\r\n continue;\r\n }\r\n\r\n if (type === \"custom\") {\r\n if (entry.customType !== \"model-snapshot\") continue;\r\n const data = entry.data;\r\n if (data !== undefined && data !== null) {\r\n const model = nonEmptyString(data.modelId);\r\n if (model !== undefined) currentModel = model;\r\n const provider = nonEmptyString(data.provider);\r\n if (provider !== undefined) currentProvider = provider;\r\n }\r\n continue;\r\n }\r\n\r\n if (type !== \"message\") continue;\r\n\r\n const msg = entry.message;\r\n if (msg === undefined || msg === null) continue;\r\n if (msg.role !== \"assistant\") continue;\r\n\r\n const usage = msg.usage;\r\n if (usage === undefined || usage === null) continue;\r\n\r\n // Model: inline message.model (non-empty) → tracked current model.\r\n // No model anywhere → skip (port: `None => continue`).\r\n const model = nonEmptyString(msg.model) ?? currentModel;\r\n if (model === undefined) continue;\r\n\r\n // Provider: inline message.provider (non-empty) → tracked current provider →\r\n // \"unknown\" fallback (port of the unwrap_or(\"unknown\")).\r\n const provider = nonEmptyString(msg.provider) ?? currentProvider ?? DEFAULT_PROVIDER;\r\n\r\n // The parser persists the resolved model/provider as the new state.\r\n currentModel = model;\r\n currentProvider = provider;\r\n\r\n const ts = parseTs(msg.timestamp) ?? mtime;\r\n\r\n const tokens = emptyTokens();\r\n tokens.input = toNonNegInt(usage.input);\r\n tokens.output = toNonNegInt(usage.output);\r\n tokens.cacheRead = toNonNegInt(usage.cacheRead);\r\n tokens.cacheWrite = toNonNegInt(usage.cacheWrite);\r\n // reasoning stays 0 (OpenClaw does not report it).\r\n\r\n ordinal += 1;\r\n const entryId = nonEmptyString(entry.id);\r\n const dedupKey = `openclaw:${sessionId}:${entryId ?? `#${ordinal}`}`;\r\n\r\n const record: UsageRecord = {\r\n platformId: PLATFORM_ID,\r\n modelId: model,\r\n providerId: provider,\r\n sessionId,\r\n tokens,\r\n ts,\r\n messageCount: 1,\r\n dedupKey,\r\n confidence: \"host-reported\",\r\n };\r\n\r\n // NOTE: provider is stored verbatim (host value or \"unknown\"); the Rust\r\n // parser never applies model-based provider inference here, so we do not\r\n // either — provider grouping happens at aggregation time in the infra.\r\n const cost = toNonNegFloat(usage.cost?.total);\r\n if (cost !== undefined && cost > 0) record.cost = cost;\r\n\r\n out.push(record);\r\n }\r\n\r\n return out;\r\n}\r\n\r\n/**\r\n * Candidate OpenClaw agent roots, most-preferred first. paths.ts has no\r\n * `openclaw` case (the framework's hostRoots table only covers the simpler\r\n * single-root platforms), so the agent dirs are resolved here directly, mirroring\r\n * tokscale scanner.rs: the current `~/.openclaw/agents` plus the legacy rebrand\r\n * dirs. An AGENT_CONNECTOR_OPENCLAW_DIR override (non-empty) is honored first.\r\n */\r\nfunction openclawRoots(): string[] {\r\n const out: string[] = [];\r\n const override = process.env.AGENT_CONNECTOR_OPENCLAW_DIR;\r\n if (override != null && override.trim() !== \"\") {\r\n const expanded = expandHome(override.trim());\r\n out.push(isAbsolute(expanded) ? expanded : resolve(expanded));\r\n }\r\n const home = homedir();\r\n out.push(join(home, \".openclaw\", \"agents\"));\r\n out.push(join(home, \".clawdbot\", \"agents\"));\r\n out.push(join(home, \".moltbot\", \"agents\"));\r\n out.push(join(home, \".moldbot\", \"agents\"));\r\n return out;\r\n}\r\n\r\n/** The OpenClaw transcript usage reader singleton. */\r\nconst openclawReader: UsageReader = {\r\n platformId: PLATFORM_ID,\r\n kind: \"local\",\r\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\r\n const records: UsageRecord[] = [];\r\n\r\n // Walk every agent root (current + legacy). walkFiles is fail-open: a\r\n // non-existent root yields []. If NO root exists, this stays empty → [].\r\n for (const root of openclawRoots()) {\r\n // Glob `*.jsonl*`: `.jsonl`, `.jsonl.deleted.<ts>`, `.jsonl.reset.<ISO>`.\r\n const files = walkFiles(root, (name) => name.includes(\".jsonl\"));\r\n for (const file of files) {\r\n const rows = parseOpenclawTranscript(file);\r\n for (const row of rows) {\r\n if (sinceMs !== undefined && row.ts < sinceMs) continue;\r\n records.push(row);\r\n }\r\n }\r\n }\r\n\r\n return records;\r\n },\r\n};\r\n\r\nexport default openclawReader;\r\n"],"mappings":";;;;;;;;;;;;;;AAgDA,SAAS,UAAU,YAAY,MAAM,eAAe;AACpD,SAAS,eAAe;AAOxB,IAAM,cAAc;AACpB,IAAM,mBAAmB;AAoCzB,SAAS,YAAY,GAAoB;AACvC,QAAM,IAAI,OAAO,MAAM,WAAW,IAAI,OAAO,MAAM,WAAW,OAAO,CAAC,IAAI;AAC1E,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC;AAClC;AAGA,SAAS,cAAc,GAAgC;AACrD,QAAM,IAAI,OAAO,MAAM,WAAW,IAAI,OAAO,MAAM,WAAW,OAAO,CAAC,IAAI;AAC1E,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,GAAG,CAAC;AACtB;AAGA,SAAS,eAAe,GAAgC;AACtD,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,SAAO,MAAM,KAAK,SAAY;AAChC;AAOA,SAAS,QAAQ,GAA2B;AAC1C,QAAM,IAAI,OAAO,MAAM,WAAW,IAAI,OAAO,MAAM,WAAW,OAAO,CAAC,IAAI;AAC1E,MAAI,CAAC,OAAO,SAAS,CAAC,KAAK,KAAK,EAAG,QAAO;AAC1C,SAAO,KAAK,OAAO,IAAI,IAAI;AAC7B;AAUA,SAAS,sBAAsB,MAA6B;AAC1D,QAAM,OAAO,SAAS,IAAI;AAC1B,QAAM,MAAM,KAAK,QAAQ,QAAQ;AACjC,MAAI,MAAM,EAAG,QAAO;AACpB,QAAM,KAAK,KAAK,MAAM,GAAG,GAAG;AAC5B,SAAO,OAAO,KAAK,OAAO;AAC5B;AAOA,SAAS,wBAAwB,MAA6B;AAC5D,QAAM,YAAY,sBAAsB,IAAI;AAC5C,MAAI,cAAc,KAAM,QAAO,CAAC;AAEhC,QAAM,QAAQ,eAAe,IAAI;AACjC,MAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAEhC,QAAM,QAAQ,YAAY,IAAI;AAC9B,QAAM,MAAqB,CAAC;AAE5B,MAAI;AACJ,MAAI;AACJ,MAAI,UAAU;AAEd,aAAW,OAAO,OAAO;AACvB,QAAI,OAAO,QAAQ,YAAY,QAAQ,KAAM;AAC7C,UAAM,QAAQ;AACd,UAAM,OAAO,MAAM;AAEnB,QAAI,SAAS,gBAAgB;AAC3B,YAAMA,SAAQ,eAAe,MAAM,OAAO;AAC1C,UAAIA,WAAU,OAAW,gBAAeA;AACxC,YAAMC,YAAW,eAAe,MAAM,QAAQ;AAC9C,UAAIA,cAAa,OAAW,mBAAkBA;AAC9C;AAAA,IACF;AAEA,QAAI,SAAS,UAAU;AACrB,UAAI,MAAM,eAAe,iBAAkB;AAC3C,YAAM,OAAO,MAAM;AACnB,UAAI,SAAS,UAAa,SAAS,MAAM;AACvC,cAAMD,SAAQ,eAAe,KAAK,OAAO;AACzC,YAAIA,WAAU,OAAW,gBAAeA;AACxC,cAAMC,YAAW,eAAe,KAAK,QAAQ;AAC7C,YAAIA,cAAa,OAAW,mBAAkBA;AAAA,MAChD;AACA;AAAA,IACF;AAEA,QAAI,SAAS,UAAW;AAExB,UAAM,MAAM,MAAM;AAClB,QAAI,QAAQ,UAAa,QAAQ,KAAM;AACvC,QAAI,IAAI,SAAS,YAAa;AAE9B,UAAM,QAAQ,IAAI;AAClB,QAAI,UAAU,UAAa,UAAU,KAAM;AAI3C,UAAM,QAAQ,eAAe,IAAI,KAAK,KAAK;AAC3C,QAAI,UAAU,OAAW;AAIzB,UAAM,WAAW,eAAe,IAAI,QAAQ,KAAK,mBAAmB;AAGpE,mBAAe;AACf,sBAAkB;AAElB,UAAM,KAAK,QAAQ,IAAI,SAAS,KAAK;AAErC,UAAM,SAAS,YAAY;AAC3B,WAAO,QAAQ,YAAY,MAAM,KAAK;AACtC,WAAO,SAAS,YAAY,MAAM,MAAM;AACxC,WAAO,YAAY,YAAY,MAAM,SAAS;AAC9C,WAAO,aAAa,YAAY,MAAM,UAAU;AAGhD,eAAW;AACX,UAAM,UAAU,eAAe,MAAM,EAAE;AACvC,UAAM,WAAW,YAAY,SAAS,IAAI,WAAW,IAAI,OAAO,EAAE;AAElE,UAAM,SAAsB;AAAA,MAC1B,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA,YAAY;AAAA,IACd;AAKA,UAAM,OAAO,cAAc,MAAM,MAAM,KAAK;AAC5C,QAAI,SAAS,UAAa,OAAO,EAAG,QAAO,OAAO;AAElD,QAAI,KAAK,MAAM;AAAA,EACjB;AAEA,SAAO;AACT;AASA,SAAS,gBAA0B;AACjC,QAAM,MAAgB,CAAC;AACvB,QAAM,WAAW,QAAQ,IAAI;AAC7B,MAAI,YAAY,QAAQ,SAAS,KAAK,MAAM,IAAI;AAC9C,UAAM,WAAW,WAAW,SAAS,KAAK,CAAC;AAC3C,QAAI,KAAK,WAAW,QAAQ,IAAI,WAAW,QAAQ,QAAQ,CAAC;AAAA,EAC9D;AACA,QAAM,OAAO,QAAQ;AACrB,MAAI,KAAK,KAAK,MAAM,aAAa,QAAQ,CAAC;AAC1C,MAAI,KAAK,KAAK,MAAM,aAAa,QAAQ,CAAC;AAC1C,MAAI,KAAK,KAAK,MAAM,YAAY,QAAQ,CAAC;AACzC,MAAI,KAAK,KAAK,MAAM,YAAY,QAAQ,CAAC;AACzC,SAAO;AACT;AAGA,IAAM,iBAA8B;AAAA,EAClC,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM,KAAK,EAAE,QAAQ,GAAiD;AACpE,UAAM,UAAyB,CAAC;AAIhC,eAAW,QAAQ,cAAc,GAAG;AAElC,YAAM,QAAQ,UAAU,MAAM,CAAC,SAAS,KAAK,SAAS,QAAQ,CAAC;AAC/D,iBAAW,QAAQ,OAAO;AACxB,cAAM,OAAO,wBAAwB,IAAI;AACzC,mBAAW,OAAO,MAAM;AACtB,cAAI,YAAY,UAAa,IAAI,KAAK,QAAS;AAC/C,kBAAQ,KAAK,GAAG;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAEA,IAAO,mBAAQ;","names":["model","provider"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/opencode/index.ts"],"sourcesContent":["/**\r\n * adapters/opencode — OpenCode (SST) platform adapter for agent-connector.\r\n *\r\n * OpenCode is the reference **ts-plugin** host: it has no JSON hook table.\r\n * Instead it auto-loads JS/TS modules from a plugin directory; each module\r\n * default-exports an async factory that returns an object keyed by OpenCode\r\n * event names (\"tool.execute.before\", \"tool.execute.after\", …). The factory\r\n * runs in-process inside OpenCode, mutates the args/output objects it is handed,\r\n * and throws to block a tool call.\r\n *\r\n * The novel part — why this PROVES the ts-plugin paradigm:\r\n * context-mode could load ITS OWN plugin module in-process because the handler\r\n * code shipped with the package. agent-connector cannot: the connector's hook\r\n * handlers are arbitrary developer code we must not import into OpenCode's\r\n * runtime (wrong cwd, wrong deps, version skew, the cache-heal bug class).\r\n * So instead of importing handlers, we synthesize a tiny, fully self-contained\r\n * bridge module that shells out to the ONE stable home binary\r\n * (`<homeBin> hook opencode <event> --connector <id>`) over child_process,\r\n * feeds it the OpenCode payload as JSON on stdin, and JSON.parses the\r\n * normalized HookResponse back from stdout. The same universal json-stdio\r\n * dispatcher every other host uses (runtime/hook-entrypoint) thus serves the\r\n * ts-plugin host too — one entrypoint, every paradigm.\r\n *\r\n * MCP registration:\r\n * - config file: opencode.json\r\n * user scope → ~/.config/opencode/opencode.json\r\n * project scope → <projectDir>/opencode.json\r\n * - root key: \"mcp\" (NOT \"mcpServers\").\r\n * - stdio server → { type: \"local\", command: [exe, ...args], environment }\r\n * (command is an ARRAY, env key is \"environment\").\r\n * - remote server → { type: \"remote\", url }.\r\n * OpenCode documents no native ${env:VAR} interpolation token, so env/url refs\r\n * are resolved to literals at install time (resolveEnvRefsDeep).\r\n *\r\n * Hooks (ts-plugin):\r\n * - synthesizePlugin(ctx) builds one self-contained ESM bridge module.\r\n * - installHooks(ctx) writes it into the OpenCode plugin dir\r\n * user scope → ~/.config/opencode/plugin/<id>.js\r\n * project scope → <projectDir>/.opencode/plugin/<id>.js\r\n * OpenCode auto-loads every file in the plugin dir, so writing the file is\r\n * sufficient — we do NOT also edit the opencode.json \"plugin\" array.\r\n *\r\n * Event mapping (only the events the connector declares are emitted):\r\n * PreToolUse → \"tool.execute.before\" (can mutate output.args; throw to deny)\r\n * PostToolUse → \"tool.execute.after\" (can mutate output.output)\r\n * SessionStart → \"experimental.chat.system.transform\" (no real SessionStart\r\n * hook upstream — #14808/#5409; this is the verified surrogate context-mode\r\n * uses; we inject additionalContext into output.system).\r\n * PermissionRequest → \"permission.ask\" (decision-capable gate; the handler\r\n * MUTATES output.status (\"ask\"|\"deny\"|\"allow\") rather than returning — like\r\n * tool.execute.before mutates output.args. Verified against\r\n * anomalyco/opencode packages/plugin/src/index.ts).\r\n *\r\n * Native passthrough hooks (supportsNativeHooks):\r\n * - Host-specific OpenCode plugin events with no normalized HookEventName\r\n * (chat.message, session.idle, permission.replied, command.execute.before,\r\n * shell.env, …) are reachable via platforms[\"opencode\"].nativeHooks. The\r\n * generated plugin emits a fire-and-forget bridge registration for each; the\r\n * home-bin's runNativeHook dispatches them host-generically.\r\n *\r\n * Capability degradations (documented, never thrown):\r\n * - For tool.execute.before OpenCode has no \"ask\" gate. A decision of \"ask\"\r\n * degrades to a block (throw with the reason) — the safe direction.\r\n * (permission.ask DOES have a real \"ask\" status, so PermissionRequest \"ask\"\r\n * is honored verbatim there.)\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 MemoryTarget,\r\n NormalizedEvent,\r\n} from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n CommandDef,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookEventName,\r\n HookParadigm,\r\n HookResponse,\r\n PermissionRequestEvent,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PostToolUseEvent,\r\n PreToolUseEvent,\r\n ServerDef,\r\n SessionStartEvent,\r\n Transport,\r\n} from \"../../core/types.js\";\r\nimport { resolveEnvRefsDeep } 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 { renderOpenCodeSubagentMd, renderSkillMd } from \"../claude-code/render.js\";\r\n\r\nconst HOST: PlatformId = \"opencode\";\r\nconst MCP_ROOT_KEY = \"mcp\";\r\n\r\n/**\r\n * Canonical → OpenCode event name map. A connector hook event is only emitted\r\n * by the generated plugin when it appears here AND is declared by the connector.\r\n */\r\nconst EVENT_TO_OPENCODE: Partial<Record<HookEventName, string>> = {\r\n PreToolUse: \"tool.execute.before\",\r\n PostToolUse: \"tool.execute.after\",\r\n // OpenCode lacks a real SessionStart hook (#14808 / #5409). The verified\r\n // surrogate is experimental.chat.system.transform, which receives the system\r\n // prompt array; we inject additionalContext there.\r\n // TODO(opencode-upstream): when anomalyco/opencode ships a native\r\n // session.start / session.created hook (#14808 / #5409), remap SessionStart to\r\n // it and retire the experimental.chat.system.transform surrogate.\r\n SessionStart: \"experimental.chat.system.transform\",\r\n // OpenCode's decision-capable permission gate. The \"permission.ask\" plugin\r\n // hook MUTATES output.status (\"ask\"|\"deny\"|\"allow\") rather than returning a\r\n // value (mirrors tool.execute.before mutating output.args); verified against\r\n // anomalyco/opencode packages/plugin/src/index.ts.\r\n PermissionRequest: \"permission.ask\",\r\n};\r\n\r\n/** Raw payload the generated plugin posts to the universal hook entrypoint. */\r\ninterface OpenCodeBridgePayload {\r\n toolName?: string;\r\n toolInput?: Record<string, unknown>;\r\n toolOutput?: string;\r\n isError?: boolean;\r\n sessionId?: string;\r\n projectDir?: string;\r\n}\r\n\r\n/** Native MCP server entry shapes OpenCode accepts under the \"mcp\" key. */\r\ninterface OpenCodeLocalServer {\r\n type: \"local\";\r\n command: string[];\r\n environment?: Record<string, string>;\r\n enabled?: boolean;\r\n}\r\ninterface OpenCodeRemoteServer {\r\n type: \"remote\";\r\n url: string;\r\n enabled?: boolean;\r\n}\r\n\r\nexport class OpenCodeAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name = \"OpenCode\";\r\n readonly paradigm: HookParadigm = \"ts-plugin\";\r\n\r\n readonly capabilities: PlatformCapabilities = {\r\n // Memory surface: AGENTS.md-first managed block. memoryTargets below probes\r\n // project scope (CLAUDE.md is opencode's fallback ONLY when AGENTS.md is\r\n // absent — creating AGENTS.md beside it would shadow the user's rules).\r\n supportsMemory: true,\r\n preToolUse: true,\r\n postToolUse: true,\r\n // OpenCode's compaction hook is experimental and not wired here.\r\n preCompact: false,\r\n sessionStart: true,\r\n sessionEnd: false,\r\n userPromptSubmit: false,\r\n stop: false,\r\n notification: false,\r\n // Newer events: OpenCode HAS a decision-capable `permission.ask` plugin\r\n // hook upstream — now wired (PermissionRequest → permission.ask, which\r\n // mutates output.status). subagents run as child sessions (bus events only\r\n // — no dedicated lifecycle hook) and tool failure is merged into\r\n // tool.execute.after / session.error, so postToolUseFailure / subagentStart\r\n // / subagentStop stay unset — the generated bridge never references them and\r\n // install reports them as \"unsupported here\".\r\n permissionRequest: true,\r\n // tool.execute.before mutates output.args → input rewrite supported.\r\n canModifyArgs: true,\r\n // tool.execute.after mutates output.output → output rewrite supported.\r\n canModifyOutput: true,\r\n // injected via experimental.chat.system.transform at session start.\r\n canInjectSessionContext: true,\r\n transports: [\"stdio\", \"sse\", \"http\"],\r\n // Content surfaces: OpenCode authors all three natively under <ocDir>.\r\n supportsCommands: true,\r\n supportsSkills: true,\r\n supportsSubagents: true,\r\n // Native passthrough hooks: OpenCode's host-specific plugin events that have\r\n // NO normalized HookEventName (chat.message, session.idle, permission.replied,\r\n // command.execute.before, shell.env, …) are reachable by declaring\r\n // platforms[\"opencode\"].nativeHooks. The generated plugin emits a bridge\r\n // registration for each, and the home-bin's runNativeHook dispatches them\r\n // host-generically (raw payload → handler → verbatim reply).\r\n supportsNativeHooks: true,\r\n };\r\n\r\n // ── Detection ──────────────────────────────────────────────────────────\r\n\r\n detectInstalled(projectDir: string): DetectedPlatform {\r\n const userConfigDir = join(homedir(), \".config\", \"opencode\");\r\n const userConfig = join(userConfigDir, \"opencode.json\");\r\n const projectConfig = join(projectDir, \"opencode.json\");\r\n\r\n const userInstalled = existsSync(userConfigDir) || existsSync(userConfig);\r\n const projInstalled = existsSync(projectConfig);\r\n const installed = userInstalled || projInstalled;\r\n\r\n // Report the marker that actually matched so a project-only install isn't\r\n // 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 OpenCode config at ${projectConfig}`\r\n : `found OpenCode config under ${userConfigDir}`\r\n : `no OpenCode config at ${userConfigDir}`,\r\n confidence: installed ? \"high\" : \"low\",\r\n };\r\n }\r\n\r\n // ── Memory surface: AGENTS.md / CLAUDE.md-fallback probe (project) ──────\r\n // OpenCode reads project AGENTS.md, falling back to CLAUDE.md ONLY when no\r\n // AGENTS.md exists. If the user relies on that CLAUDE.md fallback, CREATING\r\n // AGENTS.md beside it would shadow their own rules — so the block goes into\r\n // the file opencode actually reads today; AGENTS.md is created only when\r\n // neither exists. User scope (~/.config/opencode/AGENTS.md) stays on the\r\n // BaseAdapter default.\r\n protected override memoryTargets(ctx: InstallContext): MemoryTarget[] {\r\n if (this.memoryOverride(ctx)?.path || ctx.scope !== \"project\") {\r\n return super.memoryTargets(ctx);\r\n }\r\n const agentsMd = join(ctx.projectDir, \"AGENTS.md\");\r\n if (existsSync(agentsMd)) {\r\n return [{ path: agentsMd, reason: \"AGENTS.md (opencode's primary project rules file)\" }];\r\n }\r\n const claudeMd = join(ctx.projectDir, \"CLAUDE.md\");\r\n if (existsSync(claudeMd)) {\r\n return [\r\n {\r\n path: claudeMd,\r\n reason:\r\n \"CLAUDE.md (opencode's fallback rules file — creating AGENTS.md would shadow it)\",\r\n },\r\n ];\r\n }\r\n return [{ path: agentsMd, reason: \"AGENTS.md standard (created; no opencode rules file yet)\" }];\r\n }\r\n\r\n // ── Native paths ───────────────────────────────────────────────────────\r\n\r\n getConfigDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? ctx.projectDir\r\n : join(homedir(), \".config\", \"opencode\");\r\n }\r\n\r\n getServerConfigPath(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"opencode.json\");\r\n }\r\n\r\n /**\r\n * For ts-plugin hosts the \"hook config path\" is the generated plugin FILE.\r\n * OpenCode auto-loads every file in the plugin dir; writing this file IS the\r\n * registration.\r\n */\r\n getHookConfigPath(ctx: InstallContext): string {\r\n return join(this.pluginDir(ctx), this.pluginFileName(ctx));\r\n }\r\n\r\n /** Plugin directory OpenCode auto-loads modules from, per scope. */\r\n private pluginDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".opencode\", \"plugin\")\r\n : join(homedir(), \".config\", \"opencode\", \"plugin\");\r\n }\r\n\r\n /** Plugin module file name (one per connector, kebab-case id). */\r\n private pluginFileName(ctx: InstallContext): string {\r\n return `${ctx.connector.id}.js`;\r\n }\r\n\r\n // ── MCP server install / uninstall ─────────────────────────────────────\r\n\r\n installServer(ctx: InstallContext): ChangeRecord[] {\r\n const server = this.effectiveServer(ctx);\r\n const serverPath = this.getServerConfigPath(ctx);\r\n\r\n if (!server) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: serverPath,\r\n detail: ctx.connector.server\r\n ? \"server registration disabled for opencode\"\r\n : \"connector declares no MCP server\",\r\n },\r\n ];\r\n }\r\n\r\n const entry = this.renderServerEntry(ctx, server);\r\n return [\r\n this.upsertServerInJson(\r\n serverPath,\r\n MCP_ROOT_KEY,\r\n ctx.connector.id,\r\n entry,\r\n ctx.dryRun,\r\n ),\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(\r\n serverPath,\r\n MCP_ROOT_KEY,\r\n ctx.connector.id,\r\n ctx.dryRun,\r\n ),\r\n ];\r\n }\r\n\r\n /** Resolve the per-platform server override into an effective ServerDef. */\r\n private effectiveServer(ctx: InstallContext): ServerDef | undefined {\r\n const override = ctx.connector.platforms[HOST]?.server;\r\n if (override === false) return undefined;\r\n const base = ctx.connector.server;\r\n if (!base) return undefined;\r\n return override && typeof override === \"object\" ? { ...base, ...override } : base;\r\n }\r\n\r\n /**\r\n * Render a normalized ServerDef into OpenCode's native \"mcp\" entry.\r\n *\r\n * stdio → { type: \"local\", command: [exe, ...args], environment }\r\n * remote → { type: \"remote\", url }\r\n *\r\n * OpenCode flattens command+args into a single ARRAY. When telemetry wrapping\r\n * applies, the wrapper's command+args (which already include the real command\r\n * tail after `--`) become the head of that array.\r\n */\r\n private renderServerEntry(\r\n ctx: InstallContext,\r\n server: ServerDef,\r\n ): OpenCodeLocalServer | OpenCodeRemoteServer {\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 // OpenCode has no native interpolation token → resolve to literals.\r\n const commandArray = resolveEnvRefsDeep([command, ...args]).filter(\r\n (s) => s !== \"\",\r\n );\r\n const entry: OpenCodeLocalServer = {\r\n type: \"local\",\r\n command: commandArray,\r\n };\r\n const environment = this.renderEnv(server.env);\r\n if (environment) entry.environment = environment;\r\n if (server.enabled === false) entry.enabled = false;\r\n return entry;\r\n }\r\n\r\n // sse / http / ws (any remote transport) — OpenCode registers a URL.\r\n const entry: OpenCodeRemoteServer = {\r\n type: \"remote\",\r\n url: resolveEnvRefsDeep(server.url ?? \"\"),\r\n };\r\n if (server.enabled === false) entry.enabled = false;\r\n return entry;\r\n }\r\n\r\n /**\r\n * Render env values. OpenCode documents no native interpolation token, so\r\n * resolve every ${env:VAR} reference to a literal at install time.\r\n */\r\n private renderEnv(\r\n env: Record<string, string> | undefined,\r\n ): Record<string, string> | undefined {\r\n if (!env || Object.keys(env).length === 0) return undefined;\r\n return resolveEnvRefsDeep({ ...env });\r\n }\r\n\r\n // ── Hook install / uninstall (ts-plugin) ───────────────────────────────\r\n\r\n installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const pluginPath = this.getHookConfigPath(ctx);\r\n\r\n // `hooks: false` disables only the canonical (normalized) events; nativeHooks\r\n // is a sibling, opencode-scoped declaration on the same override.\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 opencode\"\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 `opencode 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 /**\r\n * Human-facing summary of which declared events the synthesized module ACTUALLY\r\n * wires. Only events present in EVENT_TO_OPENCODE are mapped/wired; any declared\r\n * event with no OpenCode mapping (e.g. UserPromptSubmit) is reported separately\r\n * as \"unsupported here\" so the detail never overstates coverage.\r\n */\r\n private hookDetail(ctx: InstallContext): string {\r\n const declared = ctx.connector.platforms[HOST]?.hooks === false\r\n ? []\r\n : ctx.connector.hookEvents;\r\n const mapped = declared.filter((e) => EVENT_TO_OPENCODE[e] !== undefined);\r\n const unsupported = declared.filter((e) => EVENT_TO_OPENCODE[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 * OpenCode-native passthrough events declared on platforms[\"opencode\"]\r\n * .nativeHooks (host event names with no normalized HookEventName, e.g.\r\n * session.idle / chat.message). Emitted as generic bridge registrations and\r\n * dispatched host-generically by the home-bin's 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 uninstallHooks(ctx: InstallContext): ChangeRecord[] {\r\n const pluginPath = this.getHookConfigPath(ctx);\r\n return [\r\n this.removeManagedFile(\r\n pluginPath,\r\n ctx.dryRun,\r\n \"opencode plugin module\",\r\n \"no opencode plugin module present\",\r\n ),\r\n ];\r\n }\r\n\r\n // ── Content surfaces: commands / skills / subagents ──────────────────────\r\n // CONTENT-ONLY: pure native-file writers under <ocDir>/{commands,skills,\r\n // agent}. No runtime dispatch, no home-bin pointer, no telemetry wrap. Each\r\n // method is idempotent (byte-identical → skip) via BaseAdapter.writeContentFile\r\n // and reversible via removeContentFile. Honors platforms[\"opencode\"]\r\n // per-surface false to skip.\r\n //\r\n // <ocDir> is NOT getConfigDir for project scope: opencode.json lives at the\r\n // project ROOT, but content surfaces are read from <projectDir>/.opencode/\r\n // (e.g. .opencode/skills/<name>/SKILL.md per opencode.ai/docs/skills) — the\r\n // 0.1.0 adapter wrote <projectDir>/skills, which OpenCode never reads.\r\n //\r\n // NOTE on dirs: commands live in commands/<name>.md and skills in\r\n // skills/<name>/SKILL.md, but subagents use the SINGULAR \"agent/\" dir\r\n // (agent/<name>.md) — the OpenCode loader/CLI historically uses singular\r\n // (anomalyco/opencode#14410); singular is the safe choice.\r\n\r\n private contentRootDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".opencode\")\r\n : this.getConfigDir(ctx);\r\n }\r\n private commandsDir(ctx: InstallContext): string {\r\n return join(this.contentRootDir(ctx), \"commands\");\r\n }\r\n private skillsDir(ctx: InstallContext): string {\r\n return join(this.contentRootDir(ctx), \"skills\");\r\n }\r\n private agentDir(ctx: InstallContext): string {\r\n return join(this.contentRootDir(ctx), \"agent\");\r\n }\r\n\r\n /** Native command file path: <ocDir>/commands/<name>.md. */\r\n private commandPath(ctx: InstallContext, name: string): string {\r\n return join(this.commandsDir(ctx), `${name}.md`);\r\n }\r\n /** Native skill dir: <ocDir>/skills/<name>. */\r\n private skillDir(ctx: InstallContext, name: string): string {\r\n return join(this.skillsDir(ctx), name);\r\n }\r\n /** Native subagent file path: <ocDir>/agent/<name>.md (SINGULAR dir). */\r\n private subagentPath(ctx: InstallContext, name: string): string {\r\n return join(this.agentDir(ctx), `${name}.md`);\r\n }\r\n\r\n // ── Commands ──────────────────────────────────────────────────────────────\r\n\r\n override installCommands(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.commands === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"commands disabled for opencode\" }];\r\n }\r\n if (connector.commands.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\r\n }\r\n return connector.commands.map((cmd) =>\r\n this.writeContentFile(\r\n this.commandPath(ctx, cmd.name),\r\n this.renderCommand(cmd),\r\n ctx.dryRun,\r\n ),\r\n );\r\n }\r\n\r\n override uninstallCommands(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.commands.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\r\n }\r\n return connector.commands.map((cmd) =>\r\n this.removeContentFile(this.commandPath(ctx, cmd.name), ctx.dryRun),\r\n );\r\n }\r\n\r\n /** Render a command to md+frontmatter (description, agent, model, subtask). */\r\n private renderCommand(cmd: CommandDef): string {\r\n const frontmatter: Record<string, unknown> = {};\r\n if (cmd.description !== undefined) frontmatter.description = cmd.description;\r\n if (cmd.model !== undefined) frontmatter.model = cmd.model;\r\n if (cmd.subtask !== undefined) frontmatter.subtask = cmd.subtask;\r\n // `agent` is not a core CommandDef field; it arrives via `extra` (escape\r\n // hatch) and is merged last so a connector can pin a specific OpenCode agent.\r\n if (cmd.extra) Object.assign(frontmatter, cmd.extra);\r\n return this.renderFrontmatterMd(frontmatter, cmd.prompt);\r\n }\r\n\r\n // ── Skills ────────────────────────────────────────────────────────────────\r\n\r\n override installSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.skills === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"skills disabled for opencode\" }];\r\n }\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n changes.push(\r\n this.writeContentFile(join(dir, \"SKILL.md\"), renderSkillMd(skill), ctx.dryRun),\r\n );\r\n // Bundle any resource files beside SKILL.md (relative path → contents).\r\n // Defense-in-depth: skip+warn on any key that escapes the skill dir\r\n // (config-time validation already rejects these, but never trust input).\r\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\r\n });\r\n continue;\r\n }\r\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\r\n }\r\n }\r\n return changes;\r\n }\r\n\r\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n // Remove only the files we wrote (SKILL.md + declared resources), then the\r\n // skill dir itself when we own its full contents.\r\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\r\n for (const rel of Object.keys(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) continue; // never delete outside the skill dir\r\n changes.push(this.removeContentFile(target, ctx.dryRun));\r\n }\r\n // Only remove the skill dir when WE own its full contents — never rm -rf a\r\n // dir that still holds user-added / sibling-tool / shared files.\r\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\r\n }\r\n return changes;\r\n }\r\n\r\n // ── Subagents ───────────────────────────────────────────────────────────────\r\n\r\n override installSubagents(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.subagents === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"subagents disabled for opencode\" }];\r\n }\r\n if (connector.subagents.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no subagents\" }];\r\n }\r\n return connector.subagents.map((agent) =>\r\n this.writeContentFile(\r\n this.subagentPath(ctx, agent.name),\r\n renderOpenCodeSubagentMd(agent),\r\n ctx.dryRun,\r\n ),\r\n );\r\n }\r\n\r\n override uninstallSubagents(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.subagents.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no subagents\" }];\r\n }\r\n return connector.subagents.map((agent) =>\r\n this.removeContentFile(this.subagentPath(ctx, agent.name), ctx.dryRun),\r\n );\r\n }\r\n\r\n // ── ts-plugin synthesis ────────────────────────────────────────────────\r\n\r\n /**\r\n * Build ONE self-contained ESM bridge module for OpenCode.\r\n *\r\n * The module imports nothing from agent-connector. It embeds two constants\r\n * (the absolute home-bin path and the connector id) and a `bridge()` helper\r\n * that shells out to the universal hook entrypoint via execFileSync, feeding\r\n * the OpenCode payload on stdin and JSON.parsing the normalized HookResponse\r\n * back from stdout (fail-open: any error → null). Its default export is the\r\n * OpenCode plugin factory returning an object keyed only by the OpenCode event\r\n * names for the events this connector actually declares.\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 // The OpenCode event keys this connector declares (and that we can map).\r\n // `hooks: false` disables the canonical events but leaves nativeHooks (a\r\n // sibling, opencode-scoped declaration) intact.\r\n const canonicalDisabled = ctx.connector.platforms[HOST]?.hooks === false;\r\n const events = canonicalDisabled\r\n ? []\r\n : ctx.connector.hookEvents.filter(\r\n (e): e is HookEventName => EVENT_TO_OPENCODE[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 OpenCode plugin bridge 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 \"allow\".\r\n */\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: \"opencode\",\r\n payloadNoun: \"OpenCode\",\r\n eventDoc: \" (PreToolUse|PostToolUse|SessionStart)\",\r\n failOpenComment: \"Fail-open — never wedge a tool call on a bridge error.\",\r\n})}\r\n`;\r\n\r\n const handlers: string[] = [];\r\n\r\n if (has(\"PreToolUse\")) {\r\n handlers.push(` // PreToolUse → block (throw) / rewrite args (mutate output.args).\r\n \"tool.execute.before\": async (input, output) => {\r\n const payload = {\r\n toolName: input.tool ?? \"\",\r\n toolInput: (output && output.args) ?? {},\r\n sessionId: input.sessionID ?? \"\",\r\n projectDir: PROJECT_DIR,\r\n };\r\n const res = bridge(\"PreToolUse\", payload);\r\n if (!res) return;\r\n // OpenCode has no \"ask\" gate — degrade \"ask\" to a block (safe direction).\r\n if (res.decision === \"deny\" || res.decision === \"ask\") {\r\n throw new Error(res.reason || \"Blocked by ${ctx.connector.id}\");\r\n }\r\n if (res.updatedInput && output && output.args) {\r\n Object.assign(output.args, res.updatedInput);\r\n }\r\n },`);\r\n }\r\n\r\n if (has(\"PostToolUse\")) {\r\n handlers.push(` // PostToolUse → observe / rewrite tool output (mutate output.output).\r\n \"tool.execute.after\": async (input, output) => {\r\n const payload = {\r\n toolName: input.tool ?? \"\",\r\n toolInput: input.args ?? {},\r\n toolOutput: output ? output.output : undefined,\r\n sessionId: input.sessionID ?? \"\",\r\n projectDir: PROJECT_DIR,\r\n };\r\n const res = bridge(\"PostToolUse\", payload);\r\n if (!res) return;\r\n if (typeof res.updatedOutput === \"string\" && output) {\r\n output.output = res.updatedOutput;\r\n }\r\n },`);\r\n }\r\n\r\n if (has(\"SessionStart\")) {\r\n handlers.push(` // SessionStart surrogate → inject context into the system prompt.\r\n // OpenCode has no real SessionStart hook (#14808 / #5409); the\r\n // experimental.chat.system.transform hook is the verified injection point.\r\n \"experimental.chat.system.transform\": async (input, output) => {\r\n const payload = {\r\n sessionId: (input && input.sessionID) ?? \"\",\r\n projectDir: PROJECT_DIR,\r\n };\r\n const res = bridge(\"SessionStart\", payload);\r\n if (!res) return;\r\n if (res.additionalContext && output && Array.isArray(output.system)) {\r\n // Insert at index 1 (after the header) to preserve OpenCode's\r\n // prompt-cache fold (header must remain system[0]).\r\n output.system.splice(1, 0, res.additionalContext);\r\n }\r\n },`);\r\n }\r\n\r\n if (has(\"PermissionRequest\")) {\r\n handlers.push(` // PermissionRequest → decision-capable permission gate.\r\n // OpenCode's \"permission.ask\" MUTATES output.status (\"ask\"|\"deny\"|\"allow\")\r\n // — it does NOT return a value (mirrors tool.execute.before/output.args).\r\n // A normalized \"deny\" maps to output.status \"deny\"; \"ask\" to \"ask\";\r\n // allow/void leave the default status untouched.\r\n \"permission.ask\": async (input, output) => {\r\n const payload = {\r\n toolName: (input && (input.type || input.tool)) ?? \"\",\r\n toolInput: (input && (input.metadata || input.args)) ?? {},\r\n sessionId: (input && input.sessionID) ?? \"\",\r\n projectDir: PROJECT_DIR,\r\n };\r\n const res = bridge(\"PermissionRequest\", payload);\r\n if (!res || !output) return;\r\n if (res.decision === \"deny\") output.status = \"deny\";\r\n else if (res.decision === \"ask\") output.status = \"ask\";\r\n },`);\r\n }\r\n\r\n // NATIVE PASSTHROUGH events: OpenCode-native plugin event names declared on\r\n // platforms[\"opencode\"].nativeHooks (chat.message, session.idle,\r\n // permission.replied, command.execute.before, shell.env, …). No canonical\r\n // mapping — each is emitted as a fire-and-forget bridge registration; the\r\n // home-bin's runNativeHook dispatches them host-generically (raw payload →\r\n // handler → verbatim reply). Keys are computed → emitted as quoted keys.\r\n for (const nativeEvent of this.nativeHookEvents(ctx)) {\r\n const key = JSON.stringify(nativeEvent);\r\n handlers.push(` // native passthrough → ${nativeEvent} (runNativeHook host-generic dispatch).\r\n [${key}]: async (input, output) => {\r\n bridge(${key}, {\r\n sessionId: (input && input.sessionID) ?? \"\",\r\n projectDir: PROJECT_DIR,\r\n raw: input,\r\n });\r\n },`);\r\n }\r\n\r\n const factory = `\r\nexport default async function (ctx) {\r\n // ctx.directory is the OpenCode project root; fall back to cwd.\r\n const PROJECT_DIR =\r\n (ctx && (ctx.directory || (ctx.worktree && ctx.worktree.path))) ||\r\n process.cwd();\r\n\r\n return {\r\n${handlers.join(\"\\n\")}\r\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?, 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 OpenCodeBridgePayload;\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 const ev: PostToolUseEvent = {\r\n ...base,\r\n toolName: input.toolName ?? \"\",\r\n toolInput: input.toolInput ?? {},\r\n ...(typeof input.toolOutput === \"string\"\r\n ? { toolOutput: input.toolOutput }\r\n : {}),\r\n ...(typeof input.isError === \"boolean\"\r\n ? { isError: input.isError }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"PermissionRequest\": {\r\n // Our generated bridge posts the same {toolName,toolInput,...} shape it\r\n // posts for PreToolUse, so this maps straight through (no host-native\r\n // permission_suggestions are surfaced through the plugin payload).\r\n const ev: PermissionRequestEvent = {\r\n ...base,\r\n toolName: input.toolName ?? \"\",\r\n toolInput: input.toolInput ?? {},\r\n };\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 OpenCode; 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 reads\r\n * decision / updatedInput / updatedOutput / additionalContext.\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 serverPath = this.getServerConfigPath(ctx);\r\n const pluginPath = this.getHookConfigPath(ctx);\r\n const connectorId = ctx.connector.id;\r\n const hasHooks = ctx.connector.hookEvents.length > 0;\r\n\r\n const checks: HealthCheck[] = [\r\n {\r\n name: `${this.name}: opencode.json present`,\r\n check: () =>\r\n existsSync(serverPath)\r\n ? { status: \"OK\", detail: serverPath }\r\n : { status: \"FAIL\", detail: `not found: ${serverPath}` },\r\n },\r\n {\r\n name: `${this.name}: mcp.${connectorId} registered`,\r\n check: () => {\r\n if (!ctx.connector.server) {\r\n return { status: \"OK\", detail: \"no MCP server declared\" };\r\n }\r\n const cfg = this.readJson<{ [k: string]: Record<string, unknown> }>(\r\n serverPath,\r\n );\r\n const bucket = cfg?.[MCP_ROOT_KEY];\r\n if (!cfg || !bucket) {\r\n return { status: \"FAIL\", detail: `no ${MCP_ROOT_KEY} in ${serverPath}` };\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 ${serverPath}`,\r\n };\r\n },\r\n },\r\n {\r\n name: `${this.name}: plugin module present`,\r\n check: () => {\r\n if (!hasHooks) return { status: \"OK\", detail: \"no hooks declared\" };\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 // Content-surface checks: only assert presence of the files this connector\r\n // declares (skip silently for surfaces it never asked for).\r\n for (const cmd of ctx.connector.commands) {\r\n const p = this.commandPath(ctx, cmd.name);\r\n checks.push({\r\n name: `${this.name}: command ${cmd.name} present`,\r\n check: () =>\r\n existsSync(p) ? { status: \"OK\", detail: p } : { status: \"FAIL\", detail: `not found: ${p}` },\r\n });\r\n }\r\n for (const skill of ctx.connector.skills) {\r\n const p = join(this.skillDir(ctx, skill.name), \"SKILL.md\");\r\n checks.push({\r\n name: `${this.name}: skill ${skill.name} present`,\r\n check: () =>\r\n existsSync(p) ? { status: \"OK\", detail: p } : { status: \"FAIL\", detail: `not found: ${p}` },\r\n });\r\n }\r\n for (const agent of ctx.connector.subagents) {\r\n const p = this.subagentPath(ctx, agent.name);\r\n checks.push({\r\n name: `${this.name}: subagent ${agent.name} present`,\r\n check: () =>\r\n existsSync(p) ? { status: \"OK\", detail: p } : { status: \"FAIL\", detail: `not found: ${p}` },\r\n });\r\n }\r\n return checks;\r\n }\r\n\r\n /** Read a file, returning undefined on any error (idempotency compare). */\r\n}\r\n\r\nexport const adapter = new OpenCodeAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":["entry"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/usage/readers/opencode.ts"],"sourcesContent":["/**\r\n * usage/readers/opencode — OpenCode SQLite session reader.\r\n *\r\n * Faithful port of tokscale sessions/opencode.rs (parse_opencode_sqlite, lines\r\n * 192-336). OpenCode 1.2+ stores every message as one row in a `message` table\r\n * whose `data` column is the raw message JSON; this reader reads that DB\r\n * read-only and reports the assistant rows' token usage. (The Rust crate also\r\n * walks a legacy `storage/message/*.json` tree and reconciles it with SQLite via\r\n * a migration cache — that legacy/JSON path and its cache are out of scope here:\r\n * SQLite is the system of record on every supported OpenCode version, and the\r\n * cross-source overlap it guards against is collapsed by the same dedupKey this\r\n * reader emits.)\r\n *\r\n * DB path: ~/.local/share/opencode/opencode.db (XDG_DATA_HOME / the\r\n * AGENT_CONNECTOR_OPENCODE_DIR override are honored via paths.ts hostRoots).\r\n *\r\n * SQL (modern, with the session table for workspace attribution):\r\n * SELECT m.id, m.session_id, m.data, NULLIF(s.directory, '') AS workspace_root\r\n * FROM message m\r\n * LEFT JOIN session s ON s.id = m.session_id\r\n * WHERE json_extract(m.data, '$.role') = 'assistant'\r\n * AND json_extract(m.data, '$.tokens') IS NOT NULL\r\n * ORDER BY m.id, m.session_id\r\n * When the `session` table is absent (legacy schema) the join cannot run, so we\r\n * fall back to the same query without it (workspace_root NULL), mirroring the\r\n * Rust `prepare(modern).or_else(prepare(legacy))`.\r\n *\r\n * Tokens (off the parsed `data` JSON, each clamped ≥ 0 — defense-in-depth):\r\n * tokens.input → input\r\n * tokens.output → output\r\n * tokens.cache.read → cacheRead\r\n * tokens.cache.write → cacheWrite\r\n * tokens.reasoning → reasoning (optional; 0 when absent)\r\n * cost is `data.$.cost` (clamped ≥ 0), emitted only when > 0.\r\n *\r\n * model / provider: data.$.modelID (row skipped when absent) and data.$.providerID\r\n * (default \"unknown\"). agent prefers data.$.mode then data.$.agent, normalized via\r\n * normalizeOpencodeAgentName BEFORE the dedup fingerprint (matching opencode.rs:272)\r\n * so fork-copied history that differs only in a raw agent string collapses.\r\n *\r\n * session: the m.session_id column (the message→session linkage). Workspace\r\n * (project): session.directory (modern) else the embedded data.$.path.root\r\n * (legacy), normalized to a stable key + repo-name label.\r\n *\r\n * timestamp: data.$.time.created (Unix MILLISECONDS, float). Only assistant rows\r\n * with a tokens block are emitted.\r\n *\r\n * DEDUP — fingerprint, to drop SQLite-vs-legacy-JSON overlap and fork-copied\r\n * history that OpenCode duplicates across sessions. The fingerprint is the tuple\r\n * (created, completed, modelID, providerID, input, output, reasoning, cacheRead,\r\n * cacheWrite, cost, agent); the first row for a fingerprint wins. A later row that\r\n * carries an EMBEDDED data.$.id promotes the survivor's dedupKey to that id, so\r\n * the SQLite/JSON overlap (keyed on the same message id) keeps deduplicating\r\n * cross-source via the global dedupKey backstop. dedupKey itself prefers the\r\n * embedded data.$.id, falling back to the m.id row id. Duplicate rows that point\r\n * at a DIFFERENT workspace collapse the survivor's workspace to none (a forked\r\n * copy is genuinely ambiguous about which repo it belongs to).\r\n *\r\n * confidence: \"host-reported\" (OpenCode logs real token counts).\r\n *\r\n * Fail-open: DB missing/locked/unreadable/corrupt → openSqlite() returns null →\r\n * []; a malformed row → skipped. Never throws. Read-only.\r\n */\r\n\r\nimport type { SqliteDb } from \"../sqlite.js\";\r\nimport type { UsageReader, UsageRecord } from \"../types.js\";\r\nimport { emptyTokens } from \"../aggregate.js\";\r\nimport {\r\n normalizeOpencodeAgentName,\r\n normalizeWorkspaceKey,\r\n workspaceLabelFromKey,\r\n} from \"../normalize.js\";\r\nimport { firstExistingRoot } from \"../paths.js\";\r\nimport { openSqlite } from \"../sqlite.js\";\r\n\r\nconst PLATFORM_ID = \"opencode\" as const;\r\nconst DEFAULT_PROVIDER = \"unknown\";\r\n\r\nconst MODERN_QUERY = `\r\n SELECT m.id, m.session_id, m.data, NULLIF(s.directory, '') AS workspace_root\r\n FROM message m\r\n LEFT JOIN session s ON s.id = m.session_id\r\n WHERE json_extract(m.data, '$.role') = 'assistant'\r\n AND json_extract(m.data, '$.tokens') IS NOT NULL\r\n ORDER BY m.id, m.session_id\r\n`;\r\n\r\nconst LEGACY_QUERY = `\r\n SELECT m.id, m.session_id, m.data, NULL AS workspace_root\r\n FROM message m\r\n WHERE json_extract(m.data, '$.role') = 'assistant'\r\n AND json_extract(m.data, '$.tokens') IS NOT NULL\r\n ORDER BY m.id, m.session_id\r\n`;\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Value coercion\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** A non-negative integer token field (port of `.max(0)` on an i64). */\r\nfunction tokenField(v: unknown): number {\r\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\r\n if (!Number.isFinite(n)) return 0;\r\n return Math.max(0, Math.trunc(n));\r\n}\r\n\r\n/** A finite f64, or undefined (port of Option<f64> on time/cost). */\r\nfunction asF64(v: unknown): number | undefined {\r\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\r\n return Number.isFinite(n) ? n : undefined;\r\n}\r\n\r\n/** A non-empty string, or undefined. */\r\nfunction asNonEmptyString(v: unknown): string | undefined {\r\n return typeof v === \"string\" && v !== \"\" ? v : undefined;\r\n}\r\n\r\n/** A SELECT column that may come back as Uint8Array (BLOB) / number — to string. */\r\nfunction asString(v: unknown): string | undefined {\r\n if (typeof v === \"string\") return v;\r\n if (typeof v === \"number\" && Number.isFinite(v)) return String(v);\r\n return undefined;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Workspace (port of workspace_from_root)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\ninterface Workspace {\r\n key?: string;\r\n label?: string;\r\n}\r\n\r\n/** Normalize a root dir into a stable workspace key + repo-name label. */\r\nfunction workspaceFromRoot(root: string | undefined): Workspace {\r\n if (root === undefined) return {};\r\n const key = normalizeWorkspaceKey(root);\r\n if (key === undefined) return {};\r\n const label = workspaceLabelFromKey(key);\r\n return label === undefined ? { key } : { key, label };\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// The parsed `data` column (the OpenCodeMessage subset we read)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\ninterface OpenCodeTokens {\r\n input: number;\r\n output: number;\r\n reasoning: number;\r\n cacheRead: number;\r\n cacheWrite: number;\r\n}\r\n\r\ninterface ParsedMessage {\r\n /** Embedded message id (preferred dedup key); undefined when absent. */\r\n id: string | undefined;\r\n modelId: string;\r\n providerId: string;\r\n agent: string | undefined;\r\n tokens: OpenCodeTokens;\r\n cost: number;\r\n created: number;\r\n completed: number | undefined;\r\n /** Embedded workspace root (data.$.path.root) — legacy fallback. */\r\n embeddedRoot: string | undefined;\r\n}\r\n\r\n/**\r\n * Parse the `data` JSON of a row. Returns undefined when the row is not an\r\n * assistant message, lacks a tokens block, or lacks a modelID (the Rust\r\n * `continue` cases). `path` is read only as an object with a `root` string (a\r\n * non-object `path`, e.g. a bare string, is ignored, not rejected).\r\n */\r\nfunction parseData(dataJson: string): ParsedMessage | undefined {\r\n let value: unknown;\r\n try {\r\n value = JSON.parse(dataJson);\r\n } catch {\r\n return undefined;\r\n }\r\n if (typeof value !== \"object\" || value === null) return undefined;\r\n const obj = value as Record<string, unknown>;\r\n\r\n if (obj[\"role\"] !== \"assistant\") return undefined;\r\n\r\n const tokensRaw = obj[\"tokens\"];\r\n if (typeof tokensRaw !== \"object\" || tokensRaw === null) return undefined;\r\n const t = tokensRaw as Record<string, unknown>;\r\n\r\n const modelId = asNonEmptyString(obj[\"modelID\"]);\r\n if (modelId === undefined) return undefined;\r\n\r\n const time = obj[\"time\"];\r\n const created = typeof time === \"object\" && time !== null\r\n ? asF64((time as Record<string, unknown>)[\"created\"])\r\n : undefined;\r\n if (created === undefined) return undefined;\r\n const completed = typeof time === \"object\" && time !== null\r\n ? asF64((time as Record<string, unknown>)[\"completed\"])\r\n : undefined;\r\n\r\n const cache = t[\"cache\"];\r\n const cacheObj = typeof cache === \"object\" && cache !== null ? (cache as Record<string, unknown>) : {};\r\n\r\n // agent prefers mode then agent (Rust: msg.mode.or(msg.agent)), then is run\r\n // through normalize_opencode_agent_name BEFORE the dedup fingerprint so fork\r\n // copies (which differ only in a raw agent string) collapse (opencode.rs:272).\r\n const rawAgent = asNonEmptyString(obj[\"mode\"]) ?? asNonEmptyString(obj[\"agent\"]);\r\n const agent = rawAgent === undefined ? undefined : normalizeOpencodeAgentName(rawAgent);\r\n\r\n const pathRaw = obj[\"path\"];\r\n const embeddedRoot = typeof pathRaw === \"object\" && pathRaw !== null\r\n ? asNonEmptyString((pathRaw as Record<string, unknown>)[\"root\"])\r\n : undefined;\r\n\r\n return {\r\n id: asNonEmptyString(obj[\"id\"]),\r\n modelId,\r\n providerId: asNonEmptyString(obj[\"providerID\"]) ?? DEFAULT_PROVIDER,\r\n agent,\r\n tokens: {\r\n input: tokenField(t[\"input\"]),\r\n output: tokenField(t[\"output\"]),\r\n reasoning: tokenField(t[\"reasoning\"]),\r\n cacheRead: tokenField(cacheObj[\"read\"]),\r\n cacheWrite: tokenField(cacheObj[\"write\"]),\r\n },\r\n cost: Math.max(0, asF64(obj[\"cost\"]) ?? 0),\r\n created,\r\n completed,\r\n embeddedRoot,\r\n };\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Fingerprint dedup (port of OpenCodeSqliteFingerprint + dedup state)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * A stable fingerprint string for a parsed message. Uses the float values\r\n * verbatim (created/completed/cost) so distinct calls that merely share a\r\n * creation timestamp stay distinct, while byte-identical fork copies collapse —\r\n * the JS analog of the Rust f64::to_bits() tuple.\r\n */\r\nfunction fingerprint(m: ParsedMessage): string {\r\n return [\r\n m.created,\r\n m.completed ?? \"\",\r\n m.modelId,\r\n m.providerId,\r\n m.tokens.input,\r\n m.tokens.output,\r\n m.tokens.reasoning,\r\n m.tokens.cacheRead,\r\n m.tokens.cacheWrite,\r\n m.cost,\r\n m.agent ?? \"\",\r\n ].join(\"\\u0000\");\r\n}\r\n\r\n/** Per-survivor dedup bookkeeping (port of OpenCodeSqliteDedupState). */\r\ninterface DedupState {\r\n hasEmbeddedMessageId: boolean;\r\n hasWorkspaceConflict: boolean;\r\n}\r\n\r\n/** Apply a workspace to a record (clearing both fields together when undefined). */\r\nfunction setWorkspace(record: UsageRecord, ws: Workspace): void {\r\n if (ws.key !== undefined) record.projectKey = ws.key;\r\n else delete record.projectKey;\r\n if (ws.label !== undefined) record.projectLabel = ws.label;\r\n else delete record.projectLabel;\r\n}\r\n\r\n/**\r\n * Reconcile a duplicate row's workspace into the survivor (port of\r\n * merge_duplicate_workspace): fill a missing workspace from the candidate; on a\r\n * genuine conflict (two different repos) drop to none and latch the conflict.\r\n */\r\nfunction mergeDuplicateWorkspace(\r\n record: UsageRecord,\r\n state: DedupState,\r\n candidate: Workspace,\r\n): void {\r\n if (state.hasWorkspaceConflict) return;\r\n const existing = record.projectKey;\r\n if (existing === undefined) {\r\n if (candidate.key !== undefined) setWorkspace(record, candidate);\r\n return;\r\n }\r\n if (candidate.key !== undefined && existing !== candidate.key) {\r\n state.hasWorkspaceConflict = true;\r\n setWorkspace(record, {});\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// DB read\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** The four selected columns of one row. */\r\ninterface Row {\r\n id: string;\r\n sessionId: string;\r\n data: string;\r\n workspaceRoot: string | undefined;\r\n}\r\n\r\n/**\r\n * Run the modern query; if it yields no rows fall back to the legacy query (no\r\n * session join). With sql.js a query against a missing `session` table returns\r\n * [] rather than throwing, so an empty modern result is the signal to retry\r\n * without the join — faithfully covering the legacy schema either way.\r\n */\r\nfunction selectRows(db: SqliteDb): Row[] {\r\n let raw = db.all(MODERN_QUERY);\r\n if (raw.length === 0) raw = db.all(LEGACY_QUERY);\r\n\r\n const rows: Row[] = [];\r\n for (const r of raw) {\r\n const id = asString(r[\"id\"]);\r\n const sessionId = asString(r[\"session_id\"]);\r\n const data = asString(r[\"data\"]);\r\n if (id === undefined || sessionId === undefined || data === undefined) continue;\r\n rows.push({ id, sessionId, data, workspaceRoot: asString(r[\"workspace_root\"]) });\r\n }\r\n return rows;\r\n}\r\n\r\n/** Parse the OpenCode SQLite DB into deduplicated usage records. */\r\nfunction parseDb(db: SqliteDb): UsageRecord[] {\r\n const records: UsageRecord[] = [];\r\n const indexByFingerprint = new Map<string, number>();\r\n const states: DedupState[] = [];\r\n\r\n for (const row of selectRows(db)) {\r\n const msg = parseData(row.data);\r\n if (msg === undefined) continue;\r\n\r\n // Workspace: session.directory (modern) else embedded data.$.path.root (legacy).\r\n const ws = workspaceFromRoot(row.workspaceRoot ?? msg.embeddedRoot);\r\n\r\n // dedupKey prefers the embedded message id; falls back to the row id.\r\n const dedupKey = msg.id ?? row.id;\r\n const fp = fingerprint(msg);\r\n\r\n const existingIndex = indexByFingerprint.get(fp);\r\n if (existingIndex !== undefined) {\r\n const state = states[existingIndex] as DedupState;\r\n const survivor = records[existingIndex] as UsageRecord;\r\n // Promote to the embedded id so SQLite/JSON cross-source overlap dedups.\r\n if (msg.id !== undefined && !state.hasEmbeddedMessageId) {\r\n state.hasEmbeddedMessageId = true;\r\n survivor.dedupKey = dedupKey;\r\n }\r\n mergeDuplicateWorkspace(survivor, state, ws);\r\n continue;\r\n }\r\n\r\n const tokens = emptyTokens();\r\n tokens.input = msg.tokens.input;\r\n tokens.output = msg.tokens.output;\r\n tokens.cacheRead = msg.tokens.cacheRead;\r\n tokens.cacheWrite = msg.tokens.cacheWrite;\r\n tokens.reasoning = msg.tokens.reasoning;\r\n\r\n const record: UsageRecord = {\r\n platformId: PLATFORM_ID,\r\n modelId: msg.modelId,\r\n providerId: msg.providerId,\r\n sessionId: row.sessionId,\r\n tokens,\r\n ts: Math.trunc(msg.created),\r\n messageCount: 1,\r\n dedupKey,\r\n confidence: \"host-reported\",\r\n };\r\n if (msg.cost > 0) record.cost = msg.cost;\r\n if (msg.agent !== undefined) record.agent = msg.agent;\r\n setWorkspace(record, ws);\r\n\r\n states.push({ hasEmbeddedMessageId: msg.id !== undefined, hasWorkspaceConflict: false });\r\n indexByFingerprint.set(fp, records.length);\r\n records.push(record);\r\n }\r\n\r\n return records;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Reader singleton\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** The OpenCode usage reader singleton. */\r\nconst opencodeReader: UsageReader = {\r\n platformId: PLATFORM_ID,\r\n kind: \"local\",\r\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\r\n const dbPath = firstExistingRoot(PLATFORM_ID);\r\n if (dbPath === undefined) return []; // no opencode.db → fail-open\r\n\r\n const db = await openSqlite(dbPath);\r\n if (db === null) return []; // missing/locked/unreadable/corrupt → fail-open\r\n\r\n try {\r\n const rows = parseDb(db);\r\n if (sinceMs === undefined) return rows;\r\n return rows.filter((r) => r.ts >= sinceMs);\r\n } catch {\r\n return []; // any unexpected error → fail-open\r\n } finally {\r\n db.close();\r\n }\r\n },\r\n};\r\n\r\nexport default opencodeReader;\r\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/openhands/index.ts"],"sourcesContent":["/**\r\n * adapters/openhands — OpenHands (All-Hands-AI, ex-OpenDevin) platform adapter.\r\n *\r\n * OpenHands is a **json-stdio** host (the `openhands` CLI, installed via\r\n * `uv tool install openhands`). It exposes TWO native config surfaces that live\r\n * in DIFFERENT files, so getServerConfigPath ≠ getHookConfigPath:\r\n *\r\n * 1. MCP servers — `mcp.json` (root key `mcpServers`), in the CLI persistence\r\n * dir, which is USER-scoped only:\r\n * - persistence dir → ~/.openhands (override: $OPENHANDS_PERSISTENCE_DIR)\r\n * - file → <persistence>/mcp.json\r\n * Byte-confirmed from openhands_cli/locations.py (MCP_CONFIG_FILE=\"mcp.json\",\r\n * get_persistence_dir() → ~/.openhands, env $OPENHANDS_PERSISTENCE_DIR) and\r\n * openhands_cli/mcp/mcp_utils.py (MCPConfig.from_dict({\"mcpServers\": {}}),\r\n * config.mcpServers[name]; the error path literally suggests \"create\r\n * ~/.openhands/mcp.json manually\"). Native server entry shape is FastMCP's\r\n * StdioMCPServer { command, args, env, transport:\"stdio\", cwd? } and\r\n * RemoteMCPServer { url, transport, headers } (fastmcp.mcp_config).\r\n * There is NO project-scoped mcp.json for the CLI — it reads the single\r\n * persistence-dir file — so installServer ALWAYS targets the user file.\r\n *\r\n * 2. Hooks — a SEPARATE `.openhands/hooks.json` (root key `hooks`, the\r\n * Claude-Code-plugin-compatible wrapper). OpenHands ships a hook system\r\n * that the SDK explicitly documents as honoring \"Claude Code plugin hook\r\n * files\" (openhands-sdk/openhands/sdk/hooks/config.py — the\r\n * `_normalize_hooks_input` validator unwraps {\"hooks\": {...}} and accepts\r\n * PascalCase keys). HookConfig.load() searches, in order:\r\n * - <working_dir>/.openhands/hooks.json (project scope)\r\n * - ~/.openhands/hooks.json (user scope)\r\n * first-found wins, so BOTH scopes are valid; project takes precedence.\r\n * Registrations use the Claude NESTED-rule shape:\r\n * { hooks: { <Event>: [ { matcher?, hooks:[{ type:\"command\", command }] } ] } }\r\n * which is exactly what the shared hook-merge engine writes.\r\n *\r\n * Supported events (Claude-compatible, byte-confirmed from\r\n * openhands-sdk/openhands/sdk/hooks/types.py HookEventType): PreToolUse,\r\n * PostToolUse, UserPromptSubmit, SessionStart, SessionEnd, Stop. OpenHands has\r\n * NO Notification / PreCompact / SubagentStop / PermissionRequest /\r\n * PostToolUseFailure / SubagentStart, so those degrade to a warn/skip at install.\r\n *\r\n * Runtime wire DIVERGES from Claude (do NOT assume Claude field names — verified\r\n * against HookEvent.model_dump_json() with use_enum_values=True in types.py):\r\n * - `event_type` (PascalCase string) — NOT `hook_event_name`\r\n * - `tool_name`, `tool_input` — Claude-identical\r\n * - `tool_response` is a DICT — Claude-identical key, object value\r\n * - `message` (the prompt text) — NOT `prompt`\r\n * - `session_id` — Claude-identical\r\n * - `working_dir` (the cwd) — NOT `cwd`\r\n * - `metadata` (free dict) — no normalized field\r\n *\r\n * Reply protocol (byte-confirmed from hooks/executor.py + the 33_hooks example):\r\n * exit 0 → success; stdout is parsed as a FLAT JSON object with keys\r\n * `decision` (\"allow\"|\"deny\"), `reason`, `additionalContext`. There is NO\r\n * `hookSpecificOutput` envelope and NO \"ask\" decision (HookDecision is\r\n * allow|deny; ASK is commented-out/future). exit 2 also blocks. So:\r\n * - deny → {\"decision\":\"deny\",\"reason\":...} (exit 0)\r\n * - context → {\"additionalContext\":...} (exit 0)\r\n * - ask → unsupported; degrades to a deny-style block with the reason\r\n * (OpenHands has no native confirm dialog on the hook wire)\r\n * canModifyOutput is false (it cannot rewrite already-emitted tool output);\r\n * canInjectSessionContext is true (additionalContext is honored).\r\n *\r\n * Env handling: env/header/url refs are resolved to literals at install time via\r\n * resolveEnvRefsDeep — the safe default matching the Droid/Kiro/Qwen adapters.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { join } from \"node:path\";\r\n\r\nimport { BaseAdapter, type HookMergeDescriptor } from \"../base.js\";\r\nimport type { Adapter, HookReply, InstallContext, NormalizedEvent } from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n 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 SessionEndEvent,\r\n SessionStartEvent,\r\n StopEvent,\r\n Transport,\r\n UserPromptSubmitEvent,\r\n} from \"../../core/types.js\";\r\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\r\nimport {\r\n buildWrappedStdio,\r\n isHomeBinHookCommand,\r\n} from \"../../core/spawn.js\";\r\nimport { normalizeSessionSource } from \"../claude-code/wire.js\";\r\n\r\nconst HOST: PlatformId = \"openhands\";\r\nconst MCP_ROOT_KEY = \"mcpServers\";\r\n\r\n/**\r\n * Resolve the OpenHands CLI persistence dir. Byte-confirmed from\r\n * openhands_cli/locations.py: `os.environ.get(\"OPENHANDS_PERSISTENCE_DIR\",\r\n * os.path.expanduser(\"~/.openhands\"))`. The MCP `mcp.json` lives here regardless\r\n * of install scope (the CLI has no project-scoped mcp.json).\r\n */\r\nfunction persistenceDir(): string {\r\n return process.env.OPENHANDS_PERSISTENCE_DIR ?? join(homedir(), \".openhands\");\r\n}\r\n\r\n/**\r\n * Canonical events OpenHands actually fires (HookEventType in types.py). Event\r\n * names are Claude-identical PascalCase, so each canonical name registers 1:1.\r\n * OpenHands has no Notification / PreCompact / SubagentStop / PermissionRequest /\r\n * PostToolUseFailure / SubagentStart — those warn/skip at install time.\r\n */\r\nconst SUPPORTED_EVENTS: ReadonlySet<HookEventName> = new Set<HookEventName>([\r\n \"PreToolUse\",\r\n \"PostToolUse\",\r\n \"UserPromptSubmit\",\r\n \"SessionStart\",\r\n \"SessionEnd\",\r\n \"Stop\",\r\n]);\r\n\r\n/**\r\n * Native MCP server entry shapes OpenHands accepts under `mcpServers` (FastMCP\r\n * StdioMCPServer / RemoteMCPServer). A stdio entry carries an explicit\r\n * `transport:\"stdio\"` discriminator; remote transports register a URL.\r\n */\r\ninterface OpenHandsStdioServer {\r\n command: string;\r\n args?: string[];\r\n env?: Record<string, string>;\r\n transport: \"stdio\";\r\n}\r\ninterface OpenHandsRemoteServer {\r\n url: string;\r\n transport: \"http\" | \"sse\";\r\n headers?: Record<string, string>;\r\n}\r\n\r\n/** A single OpenHands native hook registration entry (Claude-shaped, nested). */\r\ninterface OpenHandsHookEntry {\r\n matcher: string;\r\n hooks: Array<{ type: \"command\"; command: string }>;\r\n}\r\n\r\n/** The shape of OpenHands' hooks.json (only the parts we touch). */\r\ninterface OpenHandsHooksFile {\r\n hooks?: Record<string, OpenHandsHookEntry[]>;\r\n [key: string]: unknown;\r\n}\r\n\r\n/**\r\n * Raw OpenHands CLI hook stdin payload. These are the EXACT serialized field\r\n * names of the SDK's HookEvent (use_enum_values=True), NOT Claude's wire —\r\n * see the header note on the divergences (`event_type`, `working_dir`,\r\n * `message`, `tool_response` dict).\r\n */\r\ninterface OpenHandsWireInput {\r\n /** PascalCase event string (\"PreToolUse\", …). */\r\n event_type?: string;\r\n tool_name?: string;\r\n tool_input?: Record<string, unknown>;\r\n /** Tool result is a dict on the OpenHands wire (Claude sends it too). */\r\n tool_response?: Record<string, unknown> | null;\r\n /** UserPromptSubmit text rides on `message` (NOT `prompt`). */\r\n message?: string;\r\n session_id?: string;\r\n /** The cwd rides on `working_dir` (NOT `cwd`). */\r\n working_dir?: string;\r\n /** Free-form dict; no normalized field. */\r\n metadata?: Record<string, unknown>;\r\n /** Injected by the entrypoint so the runtime knows which connector to dispatch. */\r\n connector?: unknown;\r\n}\r\n\r\nexport class OpenHandsAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name = \"OpenHands\";\r\n readonly paradigm: HookParadigm = \"json-stdio\";\r\n\r\n readonly capabilities: PlatformCapabilities = {\r\n // Memory surface: AGENTS.md-first managed block via the BaseAdapter default.\r\n supportsMemory: true,\r\n preToolUse: true,\r\n postToolUse: true,\r\n userPromptSubmit: true,\r\n sessionStart: true,\r\n sessionEnd: true,\r\n stop: true,\r\n // OpenHands fires NONE of these on its hook wire (HookEventType has only the\r\n // six above) — install reports the standard skip-warn for any of them.\r\n // The two REQUIRED PlatformCapabilities flags below are explicitly false;\r\n // subagentStop / permissionRequest / postToolUseFailure / subagentStart are\r\n // optional and stay unset (read as ?? false) — no OpenHands analog.\r\n preCompact: false,\r\n notification: false,\r\n\r\n // PreToolUse/UserPromptSubmit/Stop can block (exit 2 or {\"decision\":\"deny\"}),\r\n // but the hook wire cannot rewrite tool ARGS or already-emitted tool OUTPUT.\r\n canModifyArgs: false,\r\n canModifyOutput: false,\r\n // additionalContext on the stdout reply is honored (executor.py parses it).\r\n canInjectSessionContext: true,\r\n // OpenHands registers stdio + remote (Streamable HTTP / SSE) MCP servers.\r\n transports: [\"stdio\", \"http\"],\r\n // Content surfaces (commands/skills/subagents/statusline/actions) are NOT\r\n // wired: OpenHands' CLI uses microagents + an interactive TUI rather than a\r\n // Claude-style file-per-command/skill/subagent tree, and no first-party\r\n // file layout for those was byte-confirmed. Left unset (honest CEILING).\r\n };\r\n\r\n // ── Detection ────────────────────────────────────────────────────────────\r\n\r\n detectInstalled(projectDir: string): DetectedPlatform {\r\n const userDir = persistenceDir();\r\n const userMcp = join(userDir, \"mcp.json\");\r\n const projDir = join(projectDir, \".openhands\");\r\n const projHooks = join(projDir, \"hooks.json\");\r\n\r\n const userInstalled = existsSync(userDir) || existsSync(userMcp);\r\n const projInstalled = existsSync(projDir) || existsSync(projHooks);\r\n const installed = userInstalled || projInstalled;\r\n // MCP only lives at the user persistence dir; project scope only carries\r\n // hooks. Default the reported scope to user (where the MCP config lives).\r\n const scope = userInstalled || !projInstalled ? \"user\" : \"project\";\r\n const configPath = scope === \"user\" ? userMcp : projHooks;\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 OpenHands config (${scope}) at ${configPath}`\r\n : `no OpenHands config at ${userDir} or ${projDir}`,\r\n confidence: installed ? \"high\" : \"low\",\r\n };\r\n }\r\n\r\n // ── Native paths ─────────────────────────────────────────────────────────\r\n\r\n getConfigDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".openhands\")\r\n : persistenceDir();\r\n }\r\n\r\n /**\r\n * MCP `mcp.json` lives in the CLI persistence dir, which is USER-scoped only —\r\n * the CLI reads exactly one `<persistence>/mcp.json` and has no project\r\n * variant. So this ALWAYS resolves to the user persistence dir regardless of\r\n * ctx.scope (writing a project `.openhands/mcp.json` the CLI never reads would\r\n * be a silent no-op).\r\n */\r\n getServerConfigPath(_ctx: InstallContext): string {\r\n return join(persistenceDir(), \"mcp.json\");\r\n }\r\n\r\n /**\r\n * Hooks live in a SEPARATE `.openhands/hooks.json`. HookConfig.load searches\r\n * <working_dir>/.openhands/hooks.json then ~/.openhands/hooks.json, so both\r\n * scopes are honored — project scope → <projectDir>/.openhands, user scope →\r\n * the persistence dir.\r\n */\r\n getHookConfigPath(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"hooks.json\");\r\n }\r\n\r\n // ── MCP server install / uninstall ───────────────────────────────────────\r\n\r\n installServer(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n const override = connector.platforms[HOST]?.server;\r\n if (!connector.server || override === false) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n detail: connector.server\r\n ? \"server registration disabled for openhands\"\r\n : \"connector declares no MCP server\",\r\n },\r\n ];\r\n }\r\n\r\n // Shallow-merge any per-platform server override into the base ServerDef.\r\n const server: ServerDef =\r\n override && typeof override === \"object\"\r\n ? { ...connector.server, ...override }\r\n : connector.server;\r\n\r\n const serverPath = this.getServerConfigPath(ctx);\r\n const entry = this.renderServerEntry(ctx, server);\r\n\r\n return [\r\n this.upsertServerInJson(serverPath, MCP_ROOT_KEY, connector.id, entry, ctx.dryRun),\r\n ];\r\n }\r\n\r\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\r\n const serverPath = this.getServerConfigPath(ctx);\r\n return [\r\n this.removeServerFromJson(serverPath, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun),\r\n ];\r\n }\r\n\r\n /** Render a normalized ServerDef into OpenHands' native mcpServers entry. */\r\n private renderServerEntry(\r\n ctx: InstallContext,\r\n server: ServerDef,\r\n ): OpenHandsStdioServer | OpenHandsRemoteServer {\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 // Resolve every ${env:VAR} to a literal at install time (safe path).\r\n const entry: OpenHandsStdioServer = {\r\n command: resolveEnvRefsDeep(command),\r\n transport: \"stdio\",\r\n };\r\n if (args.length > 0) entry.args = resolveEnvRefsDeep(args);\r\n const env = this.renderEnv(server.env);\r\n if (env) entry.env = env;\r\n return entry;\r\n }\r\n\r\n // http / sse — OpenHands registers a URL (FastMCP RemoteMCPServer). Default\r\n // a non-stdio transport to \"http\" (the recommended Streamable HTTP form);\r\n // honor an explicit \"sse\".\r\n const entry: OpenHandsRemoteServer = {\r\n url: resolveEnvRefsDeep(server.url ?? \"\"),\r\n transport: transport === \"sse\" ? \"sse\" : \"http\",\r\n };\r\n const headers = this.renderEnv(server.headers);\r\n if (headers) entry.headers = headers;\r\n return entry;\r\n }\r\n\r\n /**\r\n * Render env/header values. Resolve `${env:VAR}` references to literals at\r\n * install time (the safe path matching Droid).\r\n */\r\n private renderEnv(\r\n env: Record<string, string> | undefined,\r\n ): Record<string, string> | undefined {\r\n if (!env || Object.keys(env).length === 0) return undefined;\r\n return resolveEnvRefsDeep({ ...env });\r\n }\r\n\r\n // ── Hook install / uninstall (separate hooks.json, nested-rule shape) ─────\r\n\r\n installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.hooks === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"hooks disabled for openhands\" }];\r\n }\r\n if (connector.hookEvents.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no hooks\" }];\r\n }\r\n\r\n const pending = connector.hookEvents.map((event) => ({\r\n event,\r\n matcher: connector.hooks[event]?.matcher ?? \"\",\r\n }));\r\n return this.upsertHookEntries(\r\n ctx,\r\n this.getHookConfigPath(ctx),\r\n pending,\r\n this.hookDescriptor(ctx),\r\n );\r\n }\r\n\r\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\r\n return this.removeHookEntries(ctx, this.getHookConfigPath(ctx), this.hookDescriptor(ctx));\r\n }\r\n\r\n /**\r\n * OpenHands' hook-merge descriptor (NESTED shape `{ matcher, hooks: [...] }`).\r\n * Ownership is CONNECTOR-GENERIC (ANY of our commands in the entry's inner\r\n * `hooks`, not a specific-command match), identical to Droid:\r\n * - mapEvent = drop events outside SUPPORTED_EVENTS; supported event names are\r\n * Claude-identical, so they map 1:1.\r\n * - entryOwnsCommand reuses isOurCommand over the inner commands.\r\n * - uninstall = inner-strip ONLY (ownsEntryForRemove never-true).\r\n */\r\n private hookDescriptor(ctx: InstallContext): HookMergeDescriptor<OpenHandsHookEntry> {\r\n return {\r\n mapEvent: (e) => (SUPPORTED_EVENTS.has(e as HookEventName) ? e : undefined),\r\n unmappedWarnDetail: (e) => `${e} has no OpenHands hook equivalent — skipped`,\r\n renderEntry: (_event, matcher, command) => ({\r\n matcher,\r\n hooks: [{ type: \"command\", command }],\r\n }),\r\n entryOwnsCommand: (entry, _command) =>\r\n (entry.hooks ?? []).some((h) => this.isOurCommand(h.command, ctx)),\r\n ownsEntryForRemove: () => () => false,\r\n stripInner: (c) => (entry) => {\r\n const innerBefore = entry.hooks?.length ?? 0;\r\n const inner = (entry.hooks ?? []).filter((h) => !this.isOurCommand(h.command, c));\r\n return {\r\n next: inner.length > 0 ? { matcher: entry.matcher ?? \"\", hooks: inner } : null,\r\n removed: innerBefore - inner.length,\r\n };\r\n },\r\n skipDetail: (event) => `hooks.${event} already registered`,\r\n removeDetail: (event, removed) => `hooks.${event} (${removed})`,\r\n absentDetail: \"no hooks section present\",\r\n noMatchDetail: \"no matching hook entries\",\r\n };\r\n }\r\n\r\n /** True when a hook command references our home binary AND this connector id\r\n * (anchored so a shared-prefix id can't collide — see isHomeBinHookCommand). */\r\n private isOurCommand(command: string | undefined, ctx: InstallContext): boolean {\r\n return isHomeBinHookCommand(command, ctx.homeBinPath, ctx.connector.id);\r\n }\r\n\r\n // ── Diagnostics ──────────────────────────────────────────────────────────\r\n\r\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\r\n const mcpPath = this.getServerConfigPath(ctx);\r\n const hookPath = this.getHookConfigPath(ctx);\r\n const connectorId = ctx.connector.id;\r\n const homeBin = ctx.homeBinPath;\r\n const hookEvents = ctx.connector.hookEvents;\r\n return [\r\n {\r\n name: `${this.name}: mcp.json present`,\r\n check: () =>\r\n existsSync(mcpPath)\r\n ? { status: \"OK\", detail: mcpPath }\r\n : { status: \"FAIL\", detail: `not found: ${mcpPath}` },\r\n },\r\n {\r\n name: `${this.name}: server entry registered`,\r\n check: () => {\r\n if (!ctx.connector.server) {\r\n return { status: \"OK\", detail: \"no MCP server declared\" };\r\n }\r\n const cfg = this.readJson<{ [k: string]: Record<string, unknown> }>(mcpPath);\r\n const bucket = cfg?.[MCP_ROOT_KEY];\r\n if (!cfg || !bucket) {\r\n return { status: \"FAIL\", detail: `no ${MCP_ROOT_KEY} in ${mcpPath}` };\r\n }\r\n return connectorId in bucket\r\n ? { status: \"OK\", detail: `${MCP_ROOT_KEY}.${connectorId} present` }\r\n : {\r\n status: \"FAIL\",\r\n detail: `no ${MCP_ROOT_KEY}.${connectorId} in ${mcpPath}`,\r\n };\r\n },\r\n },\r\n {\r\n name: `${this.name}: hook command registered`,\r\n check: () => {\r\n if (hookEvents.length === 0) {\r\n return { status: \"OK\", detail: \"no hooks declared\" };\r\n }\r\n const file = this.readJson<OpenHandsHooksFile>(hookPath);\r\n if (!file) return { status: \"FAIL\", detail: `cannot read ${hookPath}` };\r\n const hooks = file.hooks ?? {};\r\n const registered = Object.values(hooks).some((entries) =>\r\n (entries ?? []).some((e) =>\r\n (e.hooks ?? []).some((h) =>\r\n isHomeBinHookCommand(h.command, homeBin, connectorId),\r\n ),\r\n ),\r\n );\r\n return registered\r\n ? { status: \"OK\", detail: \"hook command present\" }\r\n : { status: \"FAIL\", detail: `no hook for ${connectorId} in ${hookPath}` };\r\n },\r\n },\r\n ];\r\n }\r\n\r\n // ── Runtime: parse OpenHands stdin JSON → normalized event ─────────────────\r\n // The wire field names DIVERGE from Claude (event_type / working_dir / message\r\n // / tool_response-dict) — see the header. Reading Claude's names here would be\r\n // a silent false-friend (the kimi-code bug class).\r\n\r\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\r\n const input = (raw ?? {}) as OpenHandsWireInput;\r\n const connectorId = typeof input.connector === \"string\" ? input.connector : \"\";\r\n const sessionId = typeof input.session_id === \"string\" ? input.session_id : \"\";\r\n const projectDir =\r\n typeof input.working_dir === \"string\" ? input.working_dir : undefined;\r\n\r\n const base = {\r\n hostPlatform: HOST,\r\n connectorId,\r\n sessionId,\r\n raw,\r\n ...(projectDir !== undefined ? { projectDir } : {}),\r\n } as const;\r\n\r\n switch (event) {\r\n case \"PreToolUse\": {\r\n const ev: PreToolUseEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n };\r\n return ev;\r\n }\r\n case \"PostToolUse\": {\r\n const ev: PostToolUseEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n ...(toolResponseToString(input.tool_response) !== undefined\r\n ? { toolOutput: toolResponseToString(input.tool_response) }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"UserPromptSubmit\": {\r\n // The prompt text rides on `message` (NOT `prompt`).\r\n const ev: UserPromptSubmitEvent = {\r\n ...base,\r\n prompt: typeof input.message === \"string\" ? input.message : \"\",\r\n };\r\n return ev;\r\n }\r\n case \"SessionStart\": {\r\n // OpenHands SessionStart carries no documented `source` discriminator on\r\n // the HookEvent wire; normalizeSessionSource(undefined) → the safe\r\n // \"startup\" default.\r\n const ev: SessionStartEvent = {\r\n ...base,\r\n source: normalizeSessionSource(undefined),\r\n };\r\n return ev;\r\n }\r\n case \"SessionEnd\": {\r\n // No documented `reason` enum on the HookEvent wire — omit the field.\r\n const ev: SessionEndEvent = { ...base };\r\n return ev;\r\n }\r\n case \"Stop\": {\r\n // No stop_hook_active loop-guard field on the OpenHands wire.\r\n const ev: StopEvent = { ...base };\r\n return ev;\r\n }\r\n default: {\r\n // OpenHands never delivers Notification / PreCompact / SubagentStop /\r\n // PermissionRequest / PostToolUseFailure / SubagentStart (no native\r\n // equivalent — see SUPPORTED_EVENTS). Surface it loudly rather than\r\n // silently mis-parse.\r\n throw new Error(`unsupported openhands hook event: ${String(event)}`);\r\n }\r\n }\r\n }\r\n\r\n // ── Runtime: normalized response → OpenHands native (FLAT JSON) hook reply ──\r\n // Reply is a FLAT top-level object (NO hookSpecificOutput envelope) parsed by\r\n // hooks/executor.py: keys `decision` (allow|deny), `reason`, `additionalContext`.\r\n // There is NO \"ask\" decision (HookDecision is allow|deny only).\r\n\r\n formatReply(event: HookEventName, response: HookResponse): HookReply {\r\n const decision = response.decision ?? \"allow\";\r\n\r\n // SessionStart / SessionEnd are observe-only (cannot block); they carry no\r\n // block surface. SessionStart still honors additionalContext, so it falls\r\n // through to the `context` branch; SessionEnd is an unconditional no-op.\r\n if (event === \"SessionEnd\") {\r\n return { exitCode: 0 };\r\n }\r\n\r\n // deny → block with a reason. OpenHands has no \"ask\" on the hook wire, so an\r\n // \"ask\" decision degrades to a deny-style block carrying the reason (the\r\n // closest honest mapping — a hook that wants confirmation blocks and tells\r\n // the agent why, rather than silently passing through).\r\n if (decision === \"deny\" || decision === \"ask\") {\r\n return this.stdout({\r\n decision: \"deny\",\r\n reason:\r\n response.reason ??\r\n (decision === \"ask\" ? \"Confirmation required by hook\" : \"Blocked by hook\"),\r\n });\r\n }\r\n\r\n // context → inject soft guidance (OpenHands honors additionalContext).\r\n if (decision === \"context\" && response.additionalContext) {\r\n return this.stdout({ additionalContext: response.additionalContext });\r\n }\r\n\r\n // allow / modify (unsupported — exit-code/decision protocol only) / void →\r\n // pass through with exit 0.\r\n return { exitCode: 0 };\r\n }\r\n\r\n private stdout(payload: unknown): HookReply {\r\n return { exitCode: 0, stdout: JSON.stringify(payload) };\r\n }\r\n}\r\n\r\n/** Coerce an OpenHands PostToolUse `tool_response` (a dict) into a string. */\r\nfunction toolResponseToString(value: unknown): string | undefined {\r\n if (value == null) return undefined;\r\n if (typeof value === \"string\") return value;\r\n try {\r\n return JSON.stringify(value);\r\n } catch {\r\n return String(value);\r\n }\r\n}\r\n\r\nexport const adapter = new OpenHandsAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":["entry"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/commands/package.ts"],"sourcesContent":["/**\r\n * cli/commands/package — emit a marketplace/extension-installable connector bundle.\r\n *\r\n * Resolves the connector config (--connector <path>, else findConnectorConfig\r\n * walking up from the project dir, exactly like install.ts), packages it for the\r\n * requested --format into --out (default <cwd>/dist-plugin), then prints the\r\n * emitted file tree plus per-format install instructions.\r\n *\r\n * `--format <x>` accepts the full union; `--format all` emits EVERY feasible\r\n * format, each into <out>/<format>/..., printing install instructions per format.\r\n * Default --format claude-plugin.\r\n */\r\n\r\nimport { parseArgs } from \"node:util\";\r\nimport { join, relative } from \"node:path\";\r\n\r\nimport { findConnectorConfig, loadConnectorFromPath } from \"../../core/load-connector.js\";\r\nimport {\r\n ALL_FORMATS,\r\n FEASIBLE_FORMATS,\r\n installInstructions,\r\n isPackageFormat,\r\n packageConnector,\r\n packageConnectorAll,\r\n type PackageFormat,\r\n type PackageResult,\r\n} from \"../../core/package.js\";\r\nimport { fail, print } from \"../app.js\";\r\n\r\nconst HELP = `agent-connector package — emit a plugin/extension/marketplace bundle.\r\n\r\nusage: agent-connector package [flags]\r\n\r\nflags:\r\n --connector <path> Connector config to package (else auto-discovered).\r\n --format <fmt> Output format (default: claude-plugin). One of:\r\n ${ALL_FORMATS.join(\", \")}\r\n or \"all\" to emit every feasible format into <out>/<fmt>/.\r\n --out <dir> Output directory. Default: <cwd>/dist-plugin.\r\n --project <dir> Project root for connector discovery (default: cwd).\r\n --dry-run Compute the file tree without writing anything.\r\n --help Show this help.`;\r\n\r\n/** Print the file tree + the per-format install instructions for one bundle. */\r\nfunction printFormatResult(\r\n format: PackageFormat,\r\n connectorId: string,\r\n outDir: string,\r\n result: PackageResult,\r\n): void {\r\n print(` pluginDir: ${result.pluginDir}`);\r\n if (result.marketplacePath) {\r\n print(` marketplace: ${relative(outDir, result.marketplacePath)}`);\r\n }\r\n print(\" emitted files:\");\r\n for (const f of result.files) {\r\n print(` + ${relative(outDir, f)}`);\r\n }\r\n if (result.notes && result.notes.length > 0) {\r\n print(\" notes:\");\r\n for (const n of result.notes) print(` ! ${n}`);\r\n }\r\n print(\" install:\");\r\n for (const line of installInstructions(format, connectorId, outDir)) {\r\n print(` ${line}`);\r\n }\r\n // `package` stays a pure emitter (no host CLIs spawned) — but for hosts whose\r\n // marketplace flow agent-connector can DRIVE, point at the lifecycle verb.\r\n if (format === \"claude-plugin\") {\r\n print(\r\n \" (or let agent-connector drive it: agent-connector install --method marketplace --targets claude-code)\",\r\n );\r\n }\r\n print(\"\");\r\n}\r\n\r\nexport async function run(argv: string[]): Promise<number> {\r\n const { values } = parseArgs({\r\n args: argv,\r\n options: {\r\n connector: { type: \"string\" },\r\n format: { type: \"string\", default: \"claude-plugin\" },\r\n out: { type: \"string\" },\r\n project: { type: \"string\" },\r\n \"dry-run\": { type: \"boolean\", default: false },\r\n help: { type: \"boolean\", default: false },\r\n },\r\n allowPositionals: false,\r\n });\r\n\r\n if (values.help) {\r\n print(HELP);\r\n return 0;\r\n }\r\n\r\n const formatArg = values.format ?? \"claude-plugin\";\r\n const isAll = formatArg === \"all\";\r\n if (!isAll && !isPackageFormat(formatArg)) {\r\n return fail(\r\n `invalid --format \"${formatArg}\" (expected one of: ${ALL_FORMATS.join(\", \")}, or \"all\")`,\r\n );\r\n }\r\n\r\n const projectDir = values.project ?? process.cwd();\r\n const configPath = values.connector ?? findConnectorConfig(projectDir);\r\n if (!configPath) {\r\n return fail(\r\n \"no connector config found. Pass --connector <path> or add an \" +\r\n \"agent-connector.config.{mjs,js,json} to your project.\",\r\n );\r\n }\r\n\r\n const outDir = values.out ?? `${process.cwd()}/dist-plugin`;\r\n const dryRun = values[\"dry-run\"];\r\n const mode = dryRun ? \" (dry-run — nothing written)\" : \"\";\r\n\r\n const { connector } = await loadConnectorFromPath(configPath);\r\n\r\n if (isAll) {\r\n print(`package \"${connector.id}\" → all formats${mode}`);\r\n print(` outDir: ${outDir}`);\r\n print(\"\");\r\n const results = packageConnectorAll(connector, { outDir, dryRun });\r\n for (const { format, result } of results) {\r\n const formatOut = join(outDir, format);\r\n print(`── ${format} ──`);\r\n printFormatResult(format, connector.id, formatOut, result);\r\n }\r\n print(`emitted ${results.length} formats: ${FEASIBLE_FORMATS.join(\", \")}`);\r\n return 0;\r\n }\r\n\r\n const format: PackageFormat = formatArg;\r\n let result: PackageResult;\r\n try {\r\n result = packageConnector(connector, { outDir, format, dryRun });\r\n } catch (err) {\r\n // Emitter validation errors (e.g. mcp-server-json without\r\n // publish.registryNamespace) are actionable one-liners — print them as a\r\n // normal CLI error, never a stack trace.\r\n const message = err instanceof Error ? err.message : String(err);\r\n return fail(message);\r\n }\r\n\r\n print(`package \"${connector.id}\" → ${format}${mode}`);\r\n print(` outDir: ${outDir}`);\r\n print(\"\");\r\n printFormatResult(format, connector.id, outDir, result);\r\n return 0;\r\n}\r\n"],"mappings":"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","names":["format","result"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/usage/readers/pi.ts"],"sourcesContent":["/**\r\n * usage/readers/pi — Pi (badlogic/pi-mono) + OMP (oh-my-pi fork) session reader.\r\n *\r\n * Faithful port of tokscale sessions/pi.rs. Pi-format JSONL lives under TWO\r\n * roots that share the identical schema (tokscale scans both under ClientId::Pi):\r\n * - ~/.pi/agent/sessions/<encoded-cwd>/*.jsonl → emitted as platformId \"pi\"\r\n * - ~/.omp/agent/sessions/<encoded-cwd>/*.jsonl → emitted as platformId \"omp\"\r\n * (Oh My Pi fork, https://github.com/can1357/oh-my-pi — same format, new root)\r\n * The pi root honors the PI_CODING_AGENT_DIR override (verbatim when non-empty);\r\n * the omp root is the fixed ~/.omp default.\r\n *\r\n * File shape (port of parse_pi_file):\r\n * - Line 0 MUST be the session header `{type:\"session\", id, cwd?, timestamp?}`.\r\n * If the first non-empty line is not a parseable `session` header, the WHOLE\r\n * file yields nothing (mirrors the Rust early-return on header failure).\r\n * - Subsequent lines are entries; only `type:\"message\"` with\r\n * `message.role==\"assistant\"` AND a present `message.usage`, `message.model`,\r\n * and `message.provider` produce a record. Any of those missing → skip line.\r\n *\r\n * Tokens (message.usage): input, output, cacheRead→cacheRead, cacheWrite→cacheWrite;\r\n * reasoning is always 0 (Pi does not report it). Each token clamped ≥ 0.\r\n * Provider is the explicit `message.provider` field (NOT inferred). Timestamp is\r\n * the entry's RFC3339 `timestamp` (→ epoch ms), falling back to the file mtime.\r\n * Session id is the header `id`; workspace is the header `cwd` normalized once.\r\n *\r\n * Dedup: pi.rs performs NO dedup (one row per assistant line; file path + line\r\n * index are implicitly unique within a session). We still emit a stable\r\n * dedupKey `<platformId>:<sessionId>:<lineIndex>` so the global insertion-order\r\n * de-dup backstop has a per-record key to key on.\r\n *\r\n * Confidence: \"host-reported\" (Pi logs real token counts).\r\n *\r\n * Fail-open: no root → []; unreadable/malformed file or line → skipped; a file\r\n * whose header is missing/invalid contributes nothing (never throws).\r\n */\r\n\r\nimport { homedir } from \"node:os\";\r\nimport { isAbsolute, join, resolve } from \"node:path\";\r\n\r\nimport type { PlatformId } from \"../../core/types.js\";\r\nimport type { UsageReader, UsageRecord } from \"../types.js\";\r\nimport { emptyTokens } from \"../aggregate.js\";\r\nimport { fileMtimeMs, readJsonlLines } from \"../jsonl.js\";\r\nimport { normalizeWorkspaceKey, workspaceLabelFromKey } from \"../normalize.js\";\r\nimport { isDir, walkFiles } from \"../paths.js\";\r\n\r\nconst PI_PLATFORM_ID: PlatformId = \"pi\";\r\nconst OMP_PLATFORM_ID: PlatformId = \"omp\";\r\n\r\n/** The two Pi-format roots, each tagged with the platformId it emits. */\r\ninterface PiRoot {\r\n readonly dir: string;\r\n readonly platformId: PlatformId;\r\n}\r\n\r\n/** Session header (first JSONL line). */\r\ninterface PiSessionHeader {\r\n type?: unknown;\r\n id?: unknown;\r\n timestamp?: unknown;\r\n cwd?: unknown;\r\n}\r\n\r\n/** Subsequent JSONL entry. */\r\ninterface PiSessionEntry {\r\n type?: unknown;\r\n timestamp?: unknown;\r\n message?: {\r\n role?: unknown;\r\n model?: unknown;\r\n provider?: unknown;\r\n usage?: {\r\n input?: unknown;\r\n output?: unknown;\r\n cacheRead?: unknown;\r\n cacheWrite?: unknown;\r\n };\r\n };\r\n}\r\n\r\n/** Coerce an unknown to a non-negative integer (0 on absence/garbage). */\r\nfunction toNonNegInt(v: unknown): number {\r\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\r\n if (!Number.isFinite(n)) return 0;\r\n return Math.max(0, Math.trunc(n));\r\n}\r\n\r\n/** Parse an RFC3339 / numeric timestamp to epoch ms, or null when unusable. */\r\nfunction parseTs(v: unknown): number | null {\r\n if (typeof v === \"string\") {\r\n const ms = Date.parse(v);\r\n if (!Number.isNaN(ms)) return ms;\r\n const num = Number(v);\r\n if (Number.isFinite(num) && num > 0) return num >= 1e12 ? num : num * 1000;\r\n return null;\r\n }\r\n if (typeof v === \"number\" && Number.isFinite(v) && v > 0) {\r\n return v >= 1e12 ? v : v * 1000;\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Read an env override, treating empty/blank as unset (tokscale's\r\n * is_config_dir_overridden contract: an empty override must NOT resolve to \"\").\r\n * Relative paths resolve against the process CWD; \"~\" is expanded.\r\n */\r\nfunction envOverrideDir(name: string): string | undefined {\r\n const raw = process.env[name];\r\n if (raw == null || raw.trim() === \"\") return undefined;\r\n let p = raw.trim();\r\n if (p === \"~\") p = homedir();\r\n else if (p.startsWith(\"~/\") || p.startsWith(\"~\\\\\")) p = join(homedir(), p.slice(2));\r\n return isAbsolute(p) ? p : resolve(p);\r\n}\r\n\r\n/**\r\n * Resolve the two Pi-format roots, most-specific first. The pi root honors the\r\n * PI_CODING_AGENT_DIR override; the omp root is the fixed ~/.omp default. Roots\r\n * that are not existing directories are dropped (fail-open).\r\n */\r\nfunction piRoots(): PiRoot[] {\r\n const piDir = envOverrideDir(\"PI_CODING_AGENT_DIR\") ?? join(homedir(), \".pi\", \"agent\", \"sessions\");\r\n const ompDir = join(homedir(), \".omp\", \"agent\", \"sessions\");\r\n const candidates: PiRoot[] = [\r\n { dir: piDir, platformId: PI_PLATFORM_ID },\r\n { dir: ompDir, platformId: OMP_PLATFORM_ID },\r\n ];\r\n return candidates.filter((c) => isDir(c.dir));\r\n}\r\n\r\n/**\r\n * Parse one Pi JSONL session file into usage records (port of parse_pi_file).\r\n * Returns [] when the header is missing/invalid (whole-file early return) or no\r\n * assistant line qualifies. `platformId` tags which root the file came from.\r\n */\r\nfunction parsePiFile(path: string, platformId: PlatformId): UsageRecord[] {\r\n const lines = readJsonlLines(path);\r\n if (lines.length === 0) return [];\r\n\r\n const fallbackTs = fileMtimeMs(path);\r\n\r\n // ── Header (first non-empty parseable line) ────────────────────────────\r\n // readJsonlLines already drops blank/malformed lines; index 0 is the first\r\n // successfully-parsed line. It MUST be a `session` header or the file is void.\r\n const headerRaw = lines[0];\r\n if (typeof headerRaw !== \"object\" || headerRaw === null) return [];\r\n const header = headerRaw as PiSessionHeader;\r\n if (header.type !== \"session\") return [];\r\n if (typeof header.id !== \"string\" || header.id === \"\") return [];\r\n const sessionId = header.id;\r\n\r\n let projectKey: string | undefined;\r\n let projectLabel: string | undefined;\r\n if (typeof header.cwd === \"string\" && header.cwd !== \"\") {\r\n const key = normalizeWorkspaceKey(header.cwd);\r\n if (key !== undefined) {\r\n projectKey = key;\r\n projectLabel = workspaceLabelFromKey(key);\r\n }\r\n }\r\n\r\n const out: UsageRecord[] = [];\r\n\r\n // ── Entries (every line after the header) ──────────────────────────────\r\n for (let i = 1; i < lines.length; i++) {\r\n const raw = lines[i];\r\n if (typeof raw !== \"object\" || raw === null) continue;\r\n const entry = raw as PiSessionEntry;\r\n\r\n if (entry.type !== \"message\") continue;\r\n const message = entry.message;\r\n if (message === undefined || message === null) continue;\r\n if (message.role !== \"assistant\") continue;\r\n\r\n const usage = message.usage;\r\n if (usage === undefined || usage === null) continue;\r\n if (typeof message.model !== \"string\" || message.model === \"\") continue;\r\n if (typeof message.provider !== \"string\" || message.provider === \"\") continue;\r\n\r\n const modelId = message.model;\r\n const providerId = message.provider;\r\n\r\n const ts = parseTs(entry.timestamp) ?? fallbackTs;\r\n\r\n const tokens = emptyTokens();\r\n tokens.input = toNonNegInt(usage.input);\r\n tokens.output = toNonNegInt(usage.output);\r\n tokens.cacheRead = toNonNegInt(usage.cacheRead);\r\n tokens.cacheWrite = toNonNegInt(usage.cacheWrite);\r\n // reasoning stays 0 (Pi does not report it).\r\n\r\n const record: UsageRecord = {\r\n platformId,\r\n modelId,\r\n providerId,\r\n sessionId,\r\n tokens,\r\n ts,\r\n messageCount: 1,\r\n // No host dedup key in Pi; synthesize a per-record key (path-implicit\r\n // uniqueness within a session, mirrored by session id + line index).\r\n dedupKey: `${platformId}:${sessionId}:${i}`,\r\n confidence: \"host-reported\",\r\n };\r\n if (projectKey !== undefined) record.projectKey = projectKey;\r\n if (projectLabel !== undefined) record.projectLabel = projectLabel;\r\n out.push(record);\r\n }\r\n\r\n return out;\r\n}\r\n\r\n/** The Pi / OMP usage reader singleton (registry lists it under \"pi\"). */\r\nconst piReader: UsageReader = {\r\n platformId: PI_PLATFORM_ID,\r\n kind: \"local\",\r\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\r\n const roots = piRoots();\r\n if (roots.length === 0) return []; // no ~/.pi or ~/.omp → fail-open\r\n\r\n const records: UsageRecord[] = [];\r\n for (const root of roots) {\r\n // <root>/<encoded-cwd>/*.jsonl\r\n const files = walkFiles(root.dir, (name) => name.endsWith(\".jsonl\"));\r\n for (const file of files) {\r\n const rows = parsePiFile(file, root.platformId);\r\n for (const row of rows) {\r\n if (sinceMs !== undefined && row.ts < sinceMs) continue;\r\n records.push(row);\r\n }\r\n }\r\n }\r\n return records;\r\n },\r\n};\r\n\r\nexport default piReader;\r\n"],"mappings":"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","names":[]}