@ken-jo/agent-connector 0.4.95 → 0.4.98

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 (385) hide show
  1. package/LICENSE +202 -202
  2. package/NOTICE +6 -6
  3. package/README.md +605 -599
  4. package/dist/{action-Q7GGO4PA.js → action-5JM23CFJ.js} +16 -16
  5. package/dist/action-5JM23CFJ.js.map +1 -0
  6. package/dist/{amazon-q-ETMAGY3D.js → amazon-q-LFZNGN27.js} +6 -6
  7. package/dist/amazon-q-LFZNGN27.js.map +1 -0
  8. package/dist/{amp-BQZOL5GR.js → amp-CEWOSWQO.js} +4 -4
  9. package/dist/amp-CEWOSWQO.js.map +1 -0
  10. package/dist/{amp-67BZONGF.js → amp-S22XTPEQ.js} +7 -7
  11. package/dist/amp-S22XTPEQ.js.map +1 -0
  12. package/dist/{antigravity-EA4L225L.js → antigravity-LHFEFNMC.js} +5 -5
  13. package/dist/antigravity-LHFEFNMC.js.map +1 -0
  14. package/dist/antigravity-PZXYAO3X.js +18 -0
  15. package/dist/{antigravity-cli-B2HXZFYQ.js → antigravity-cli-MOKFSWOU.js} +5 -5
  16. package/dist/antigravity-cli-MOKFSWOU.js.map +1 -0
  17. package/dist/{antigravity-cli-OM6SC3PE.js → antigravity-cli-PN474FXM.js} +9 -9
  18. package/dist/antigravity-cli-PN474FXM.js.map +1 -0
  19. package/dist/audit-2H7E3Z4N.js +311 -0
  20. package/dist/audit-2H7E3Z4N.js.map +1 -0
  21. package/dist/{chunk-KHO5WNTP.js → chunk-4GH2KA7V.js} +1 -1
  22. package/dist/chunk-4GH2KA7V.js.map +1 -0
  23. package/dist/{chunk-7F5KHLEP.js → chunk-6XWORQM6.js} +4 -4
  24. package/dist/chunk-6XWORQM6.js.map +1 -0
  25. package/dist/{chunk-2Y2MZRLP.js → chunk-7C2IPIIB.js} +3 -3
  26. package/dist/chunk-7C2IPIIB.js.map +1 -0
  27. package/dist/{chunk-NLE2J5P4.js → chunk-7HHW23AC.js} +29 -29
  28. package/dist/chunk-7HHW23AC.js.map +1 -0
  29. package/dist/{chunk-XD7MY6HB.js → chunk-7PSNXUFK.js} +1 -1
  30. package/dist/chunk-7PSNXUFK.js.map +1 -0
  31. package/dist/{chunk-YPAXJ7EW.js → chunk-7VSZXOXJ.js} +13 -13
  32. package/dist/chunk-7VSZXOXJ.js.map +1 -0
  33. package/dist/{chunk-ISEJWBFF.js → chunk-B6PKHS5I.js} +2 -2
  34. package/dist/chunk-B6PKHS5I.js.map +1 -0
  35. package/dist/{chunk-UM5S7DKK.js → chunk-C747DYNT.js} +1 -1
  36. package/dist/chunk-C747DYNT.js.map +1 -0
  37. package/dist/{chunk-PW42ZXCB.js → chunk-DOPFBUCX.js} +3 -3
  38. package/dist/chunk-DOPFBUCX.js.map +1 -0
  39. package/dist/{chunk-I5UXHIDT.js → chunk-DWZ5IWF4.js} +8 -8
  40. package/dist/chunk-DWZ5IWF4.js.map +1 -0
  41. package/dist/{chunk-6WZODR3Z.js → chunk-FJ6F5U3W.js} +5 -5
  42. package/dist/chunk-FJ6F5U3W.js.map +1 -0
  43. package/dist/{chunk-H33M77DD.js → chunk-HATCOSQT.js} +22 -19
  44. package/dist/chunk-HATCOSQT.js.map +1 -0
  45. package/dist/{chunk-UMQ6NBQR.js → chunk-ILWHLGIC.js} +2 -2
  46. package/dist/chunk-ILWHLGIC.js.map +1 -0
  47. package/dist/{chunk-IVAWP6HH.js → chunk-IOJDZJ72.js} +2 -2
  48. package/dist/chunk-IOJDZJ72.js.map +1 -0
  49. package/dist/{chunk-NBYKLUF6.js → chunk-KFPSJRHR.js} +2 -2
  50. package/dist/chunk-KFPSJRHR.js.map +1 -0
  51. package/dist/{chunk-O7SDRBVB.js → chunk-KM2YXO6V.js} +1 -1
  52. package/dist/chunk-KM2YXO6V.js.map +1 -0
  53. package/dist/{chunk-3NH2ARIS.js → chunk-KTBAB556.js} +6 -6
  54. package/dist/chunk-KTBAB556.js.map +1 -0
  55. package/dist/{chunk-5IOTP7LQ.js → chunk-KWC5DQ5Z.js} +7 -5
  56. package/dist/chunk-KWC5DQ5Z.js.map +1 -0
  57. package/dist/{chunk-JKKU2WKG.js → chunk-LD5HAN6O.js} +4 -4
  58. package/dist/chunk-LD5HAN6O.js.map +1 -0
  59. package/dist/{chunk-OP4KBXUO.js → chunk-LFWYYWLV.js} +1 -1
  60. package/dist/chunk-LFWYYWLV.js.map +1 -0
  61. package/dist/{chunk-APZDTY76.js → chunk-LZL2YITN.js} +7 -7
  62. package/dist/chunk-LZL2YITN.js.map +1 -0
  63. package/dist/{chunk-H53QZQJI.js → chunk-PSGTHJQU.js} +1 -1
  64. package/dist/chunk-PSGTHJQU.js.map +1 -0
  65. package/dist/{chunk-FBJQGRET.js → chunk-Q4ISQHUQ.js} +1 -1
  66. package/dist/chunk-Q4ISQHUQ.js.map +1 -0
  67. package/dist/{chunk-6TF7MHG5.js → chunk-Q7PR2WGT.js} +2 -2
  68. package/dist/chunk-Q7PR2WGT.js.map +1 -0
  69. package/dist/{chunk-GZGLCCEE.js → chunk-QYOUQCC6.js} +1 -1
  70. package/dist/chunk-QYOUQCC6.js.map +1 -0
  71. package/dist/{chunk-Z2MRSS2I.js → chunk-RNVIK5YJ.js} +2 -2
  72. package/dist/chunk-RNVIK5YJ.js.map +1 -0
  73. package/dist/{chunk-XC5SJ35F.js → chunk-RPWD5H6Y.js} +5 -5
  74. package/dist/chunk-RPWD5H6Y.js.map +1 -0
  75. package/dist/{chunk-PAPMUAR5.js → chunk-S25MVWBP.js} +12 -12
  76. package/dist/chunk-S25MVWBP.js.map +1 -0
  77. package/dist/{chunk-FJCUKXGX.js → chunk-THI4PI4H.js} +2 -2
  78. package/dist/chunk-THI4PI4H.js.map +1 -0
  79. package/dist/{chunk-D4ELPBB5.js → chunk-TOSNS2BY.js} +43 -43
  80. package/dist/chunk-TOSNS2BY.js.map +1 -0
  81. package/dist/{chunk-MGTDQLJJ.js → chunk-UNU2KPYJ.js} +11 -9
  82. package/dist/chunk-UNU2KPYJ.js.map +1 -0
  83. package/dist/{chunk-MQPNU2CY.js → chunk-UZZTWWGG.js} +1 -1
  84. package/dist/chunk-UZZTWWGG.js.map +1 -0
  85. package/dist/{chunk-4NHXLXSD.js → chunk-W7AGWAMH.js} +2 -2
  86. package/dist/chunk-W7AGWAMH.js.map +1 -0
  87. package/dist/{chunk-OPXAP3AC.js → chunk-XN7S44U4.js} +1 -1
  88. package/dist/chunk-XN7S44U4.js.map +1 -0
  89. package/dist/{chunk-BEFVJ2OE.js → chunk-XXLCMSD3.js} +1 -1
  90. package/dist/chunk-XXLCMSD3.js.map +1 -0
  91. package/dist/{chunk-YT43MUSJ.js → chunk-XZWMJLIT.js} +1 -1
  92. package/dist/chunk-XZWMJLIT.js.map +1 -0
  93. package/dist/{chunk-RK6VBLXP.js → chunk-ZSOS2KOR.js} +1 -1
  94. package/dist/chunk-ZSOS2KOR.js.map +1 -0
  95. package/dist/{claude-code-NMRKTAP7.js → claude-code-FS42PXZO.js} +4 -4
  96. package/dist/claude-code-FS42PXZO.js.map +1 -0
  97. package/dist/{claude-code-GRWO6XBZ.js → claude-code-TA53454O.js} +13 -13
  98. package/dist/claude-code-TA53454O.js.map +1 -0
  99. package/dist/cli/sdk.js +9 -8
  100. package/dist/cli/sdk.js.map +1 -1
  101. package/dist/cli.js +1 -1
  102. package/dist/cli.js.map +1 -1
  103. package/dist/{cline-25KW25IN.js → cline-GZFN32BM.js} +6 -6
  104. package/dist/cline-GZFN32BM.js.map +1 -0
  105. package/dist/{codebuddy-ZNYQACMF.js → codebuddy-IYS3TQZ3.js} +7 -7
  106. package/dist/codebuddy-IYS3TQZ3.js.map +1 -0
  107. package/dist/{codebuff-ETYT5UBL.js → codebuff-6KWBW6CT.js} +4 -4
  108. package/dist/codebuff-6KWBW6CT.js.map +1 -0
  109. package/dist/{codebuff-VR6JUDIY.js → codebuff-D3MIKJHC.js} +6 -6
  110. package/dist/codebuff-D3MIKJHC.js.map +1 -0
  111. package/dist/{codex-YAORPCOD.js → codex-DKBAAUCO.js} +4 -4
  112. package/dist/codex-DKBAAUCO.js.map +1 -0
  113. package/dist/{codex-CPNKTXG3.js → codex-ZKLP5IXZ.js} +9 -9
  114. package/dist/codex-ZKLP5IXZ.js.map +1 -0
  115. package/dist/{continue-WU2IB5KS.js → continue-I34FNSTJ.js} +8 -8
  116. package/dist/continue-I34FNSTJ.js.map +1 -0
  117. package/dist/{copilot-cli-MZTAWMPW.js → copilot-cli-CLBJACQ5.js} +4 -4
  118. package/dist/copilot-cli-CLBJACQ5.js.map +1 -0
  119. package/dist/{copilot-cli-PAP377OM.js → copilot-cli-NWPM5GE6.js} +7 -7
  120. package/dist/copilot-cli-NWPM5GE6.js.map +1 -0
  121. package/dist/{crush-QBXLNN7T.js → crush-A5C5USRL.js} +4 -4
  122. package/dist/crush-A5C5USRL.js.map +1 -0
  123. package/dist/{crush-3AVQA4YU.js → crush-MDGPZSZU.js} +7 -7
  124. package/dist/crush-MDGPZSZU.js.map +1 -0
  125. package/dist/{cursor-DIW43TRZ.js → cursor-D5SWDVSW.js} +3 -3
  126. package/dist/cursor-D5SWDVSW.js.map +1 -0
  127. package/dist/cursor-JRVK56QA.js +22 -0
  128. package/dist/{define-connector-DTWb46UB.d.ts → define-connector-B7tLHVnx.d.ts} +1 -1
  129. package/dist/{detect-6V4LC3MA.js → detect-FKJC36P4.js} +4 -4
  130. package/dist/detect-FKJC36P4.js.map +1 -0
  131. package/dist/{devin-QMEM3OFA.js → devin-TZMRC57O.js} +7 -7
  132. package/dist/devin-TZMRC57O.js.map +1 -0
  133. package/dist/{doctor-2YDUVSAU.js → doctor-PGVYUQYG.js} +26 -26
  134. package/dist/doctor-PGVYUQYG.js.map +1 -0
  135. package/dist/{droid-U3ZBLQFO.js → droid-PFQ2PZSF.js} +4 -4
  136. package/dist/droid-PFQ2PZSF.js.map +1 -0
  137. package/dist/{droid-MOKV53J4.js → droid-SCFWDJR4.js} +7 -7
  138. package/dist/droid-SCFWDJR4.js.map +1 -0
  139. package/dist/{gemini-cli-47ZHCOZR.js → gemini-cli-MLDGOR2L.js} +8 -8
  140. package/dist/gemini-cli-MLDGOR2L.js.map +1 -0
  141. package/dist/{gemini-cli-NMVVBO3K.js → gemini-cli-Y6RJMU24.js} +4 -4
  142. package/dist/gemini-cli-Y6RJMU24.js.map +1 -0
  143. package/dist/{goose-4EI5MYTI.js → goose-5DBHDSJW.js} +9 -9
  144. package/dist/goose-5DBHDSJW.js.map +1 -0
  145. package/dist/{goose-64V46Y2W.js → goose-K2HKUTBK.js} +4 -4
  146. package/dist/goose-K2HKUTBK.js.map +1 -0
  147. package/dist/{grok-cli-QYQPDSVB.js → grok-cli-LX6Q6RUY.js} +7 -7
  148. package/dist/grok-cli-LX6Q6RUY.js.map +1 -0
  149. package/dist/{hermes-F75OSK3Y.js → hermes-LJB3SJKB.js} +7 -7
  150. package/dist/hermes-LJB3SJKB.js.map +1 -0
  151. package/dist/{hermes-F3EGTK6N.js → hermes-MF3NX4ZY.js} +4 -4
  152. package/dist/hermes-MF3NX4ZY.js.map +1 -0
  153. package/dist/{hook-OEVRDMXD.js → hook-PVGBQUQI.js} +16 -16
  154. package/dist/hook-PVGBQUQI.js.map +1 -0
  155. package/dist/index.d.ts +3 -3
  156. package/dist/index.js +5 -5
  157. package/dist/{install-5NMCUDG5.js → install-HPDIK7F6.js} +192 -28
  158. package/dist/install-HPDIK7F6.js.map +1 -0
  159. package/dist/{introspect-dNekqPDY.d.ts → introspect-DWCu0Krk.d.ts} +1 -1
  160. package/dist/{jetbrains-copilot-ZGIWI6YV.js → jetbrains-copilot-2DSVAJMJ.js} +6 -6
  161. package/dist/jetbrains-copilot-2DSVAJMJ.js.map +1 -0
  162. package/dist/{junie-DIFN2P22.js → junie-GUBDRADP.js} +6 -6
  163. package/dist/junie-GUBDRADP.js.map +1 -0
  164. package/dist/{kilo-EMZJBEBB.js → kilo-HRLWMENX.js} +8 -8
  165. package/dist/kilo-HRLWMENX.js.map +1 -0
  166. package/dist/{kilo-SW7BH6EG.js → kilo-SMHPOHJ5.js} +4 -4
  167. package/dist/kilo-SMHPOHJ5.js.map +1 -0
  168. package/dist/{kilo-cli-S3GXBPAE.js → kilo-cli-IQMFMYZF.js} +8 -8
  169. package/dist/kilo-cli-IQMFMYZF.js.map +1 -0
  170. package/dist/{kilo-cli-ZF272ZHN.js → kilo-cli-T2SRRRJS.js} +5 -5
  171. package/dist/kilo-cli-T2SRRRJS.js.map +1 -0
  172. package/dist/{kimi-IQJOIY6B.js → kimi-EPD46UJK.js} +7 -7
  173. package/dist/kimi-EPD46UJK.js.map +1 -0
  174. package/dist/{kimi-5FHEXGZD.js → kimi-MSIDEMCI.js} +4 -4
  175. package/dist/kimi-MSIDEMCI.js.map +1 -0
  176. package/dist/{kiro-BYX4HEAJ.js → kiro-A7DL52FX.js} +7 -7
  177. package/dist/kiro-A7DL52FX.js.map +1 -0
  178. package/dist/{kiro-WGOI5CGT.js → kiro-NYRRBIKW.js} +4 -4
  179. package/dist/kiro-NYRRBIKW.js.map +1 -0
  180. package/dist/{leaderboard-XEKS7B3H.js → leaderboard-KCT7XPCI.js} +7 -7
  181. package/dist/leaderboard-KCT7XPCI.js.map +1 -0
  182. package/dist/{mimo-code-7R53QDHB.js → mimo-code-NDGLBXNC.js} +7 -7
  183. package/dist/mimo-code-NDGLBXNC.js.map +1 -0
  184. package/dist/{mistral-vibe-LHJ55FSB.js → mistral-vibe-LLWASF5X.js} +6 -6
  185. package/dist/mistral-vibe-LLWASF5X.js.map +1 -0
  186. package/dist/{mux-XM6AP35L.js → mux-7PIJ7HUQ.js} +4 -4
  187. package/dist/mux-7PIJ7HUQ.js.map +1 -0
  188. package/dist/{mux-NYCNGL43.js → mux-ON4QK3OC.js} +6 -6
  189. package/dist/mux-ON4QK3OC.js.map +1 -0
  190. package/dist/{nemoclaw-WSIPSE7U.js → nemoclaw-45R5MFBF.js} +7 -7
  191. package/dist/nemoclaw-45R5MFBF.js.map +1 -0
  192. package/dist/{omp-562NK5BJ.js → omp-ROCTFV3A.js} +7 -7
  193. package/dist/omp-ROCTFV3A.js.map +1 -0
  194. package/dist/{open-interpreter-I2CNOEXY.js → open-interpreter-V6CN542J.js} +6 -6
  195. package/dist/open-interpreter-V6CN542J.js.map +1 -0
  196. package/dist/openclaw-AOKFJGRQ.js +17 -0
  197. package/dist/{openclaw-5EQFGOWL.js → openclaw-KAFTNLR2.js} +4 -4
  198. package/dist/openclaw-KAFTNLR2.js.map +1 -0
  199. package/dist/{opencode-YS4WX2E2.js → opencode-YYWQ45LF.js} +7 -7
  200. package/dist/opencode-YYWQ45LF.js.map +1 -0
  201. package/dist/{opencode-QGP4ALQY.js → opencode-Z65JYILL.js} +4 -4
  202. package/dist/opencode-Z65JYILL.js.map +1 -0
  203. package/dist/{openhands-QMYIKNOS.js → openhands-OFFMWCQW.js} +7 -7
  204. package/dist/openhands-OFFMWCQW.js.map +1 -0
  205. package/dist/{package-G25GVTH2.js → package-TALOSRQ4.js} +15 -15
  206. package/dist/package-TALOSRQ4.js.map +1 -0
  207. package/dist/{pi-644FIN3A.js → pi-4W74ETTI.js} +6 -6
  208. package/dist/pi-4W74ETTI.js.map +1 -0
  209. package/dist/{pi-GGDQREVG.js → pi-DLKCPOJE.js} +4 -4
  210. package/dist/pi-DLKCPOJE.js.map +1 -0
  211. package/dist/{qwen-code-GQILBJ5B.js → qwen-code-26CSFDX6.js} +4 -4
  212. package/dist/qwen-code-26CSFDX6.js.map +1 -0
  213. package/dist/{qwen-code-STDMX7EM.js → qwen-code-JOBFNEVT.js} +16 -16
  214. package/dist/qwen-code-JOBFNEVT.js.map +1 -0
  215. package/dist/{roo-code-SUWI4BBN.js → roo-code-HSAE2CJU.js} +4 -4
  216. package/dist/roo-code-HSAE2CJU.js.map +1 -0
  217. package/dist/{roo-code-DLPIHHQ6.js → roo-code-RHIGJZNY.js} +6 -6
  218. package/dist/roo-code-RHIGJZNY.js.map +1 -0
  219. package/dist/runtime/index.d.ts +9 -8
  220. package/dist/runtime/index.js +14 -14
  221. package/dist/sdk/index.d.ts +4 -4
  222. package/dist/sdk/index.js +6 -6
  223. package/dist/sdk/index.js.map +1 -1
  224. package/dist/sdk/test.d.ts +2 -2
  225. package/dist/sdk/test.js +11 -11
  226. package/dist/{serve-XTMIUHKK.js → serve-4TEHK726.js} +16 -16
  227. package/dist/serve-4TEHK726.js.map +1 -0
  228. package/dist/{status-VJAGO4Z7.js → status-DVD4EHMX.js} +11 -11
  229. package/dist/status-DVD4EHMX.js.map +1 -0
  230. package/dist/{statusline-EY2E2UGF.js → statusline-ZVNH7XBK.js} +15 -15
  231. package/dist/statusline-ZVNH7XBK.js.map +1 -0
  232. package/dist/{synthetic-6FRA7P5Q.js → synthetic-LUT6V6SD.js} +4 -4
  233. package/dist/synthetic-LUT6V6SD.js.map +1 -0
  234. package/dist/{telemetry-OLZQBADT.js → telemetry-NWUJPIK3.js} +10 -10
  235. package/dist/telemetry-NWUJPIK3.js.map +1 -0
  236. package/dist/{trae-CPPYASR2.js → trae-SIQI3C66.js} +4 -4
  237. package/dist/trae-SIQI3C66.js.map +1 -0
  238. package/dist/{trae-UOYD6JZZ.js → trae-TLAFSQRQ.js} +6 -6
  239. package/dist/trae-TLAFSQRQ.js.map +1 -0
  240. package/dist/{types-JyhpJHJa.d.ts → types-C6j18a6R.d.ts} +13 -14
  241. package/dist/{uninstall-AZEUPAND.js → uninstall-DZVFV7KE.js} +21 -21
  242. package/dist/uninstall-DZVFV7KE.js.map +1 -0
  243. package/dist/{upgrade-CAUUHSSC.js → upgrade-OUE4ED52.js} +21 -21
  244. package/dist/upgrade-OUE4ED52.js.map +1 -0
  245. package/dist/{usage-3KHZ4RYZ.js → usage-NT3ZXALZ.js} +4 -4
  246. package/dist/usage-NT3ZXALZ.js.map +1 -0
  247. package/dist/{usage-event-TZWOZPRJ.js → usage-event-IELZE6WO.js} +15 -15
  248. package/dist/usage-event-IELZE6WO.js.map +1 -0
  249. package/dist/{vscode-copilot-NIMH2DGU.js → vscode-copilot-VZ5XYSTK.js} +7 -7
  250. package/dist/vscode-copilot-VZ5XYSTK.js.map +1 -0
  251. package/dist/{warp-NRBBJQR2.js → warp-5YBSP7FP.js} +4 -4
  252. package/dist/warp-5YBSP7FP.js.map +1 -0
  253. package/dist/{warp-QYJGKURM.js → warp-P2HWXIKT.js} +6 -6
  254. package/dist/warp-P2HWXIKT.js.map +1 -0
  255. package/dist/{windsurf-75LLZ37C.js → windsurf-3JC73GZK.js} +6 -6
  256. package/dist/windsurf-3JC73GZK.js.map +1 -0
  257. package/dist/{zed-HKAZXEJW.js → zed-6CMUKXLI.js} +7 -7
  258. package/dist/zed-6CMUKXLI.js.map +1 -0
  259. package/dist/{zed-XJFOOKCO.js → zed-XXCNYOIN.js} +4 -4
  260. package/dist/zed-XXCNYOIN.js.map +1 -0
  261. package/package.json +92 -88
  262. package/dist/action-Q7GGO4PA.js.map +0 -1
  263. package/dist/amazon-q-ETMAGY3D.js.map +0 -1
  264. package/dist/amp-67BZONGF.js.map +0 -1
  265. package/dist/amp-BQZOL5GR.js.map +0 -1
  266. package/dist/antigravity-EA4L225L.js.map +0 -1
  267. package/dist/antigravity-VXLDLTO7.js +0 -18
  268. package/dist/antigravity-cli-B2HXZFYQ.js.map +0 -1
  269. package/dist/antigravity-cli-OM6SC3PE.js.map +0 -1
  270. package/dist/chunk-2Y2MZRLP.js.map +0 -1
  271. package/dist/chunk-3NH2ARIS.js.map +0 -1
  272. package/dist/chunk-4NHXLXSD.js.map +0 -1
  273. package/dist/chunk-5IOTP7LQ.js.map +0 -1
  274. package/dist/chunk-6TF7MHG5.js.map +0 -1
  275. package/dist/chunk-6WZODR3Z.js.map +0 -1
  276. package/dist/chunk-7F5KHLEP.js.map +0 -1
  277. package/dist/chunk-APZDTY76.js.map +0 -1
  278. package/dist/chunk-BEFVJ2OE.js.map +0 -1
  279. package/dist/chunk-D4ELPBB5.js.map +0 -1
  280. package/dist/chunk-FBJQGRET.js.map +0 -1
  281. package/dist/chunk-FJCUKXGX.js.map +0 -1
  282. package/dist/chunk-GZGLCCEE.js.map +0 -1
  283. package/dist/chunk-H33M77DD.js.map +0 -1
  284. package/dist/chunk-H53QZQJI.js.map +0 -1
  285. package/dist/chunk-I5UXHIDT.js.map +0 -1
  286. package/dist/chunk-ISEJWBFF.js.map +0 -1
  287. package/dist/chunk-IVAWP6HH.js.map +0 -1
  288. package/dist/chunk-JKKU2WKG.js.map +0 -1
  289. package/dist/chunk-KHO5WNTP.js.map +0 -1
  290. package/dist/chunk-MGTDQLJJ.js.map +0 -1
  291. package/dist/chunk-MQPNU2CY.js.map +0 -1
  292. package/dist/chunk-NBYKLUF6.js.map +0 -1
  293. package/dist/chunk-NLE2J5P4.js.map +0 -1
  294. package/dist/chunk-O7SDRBVB.js.map +0 -1
  295. package/dist/chunk-OP4KBXUO.js.map +0 -1
  296. package/dist/chunk-OPXAP3AC.js.map +0 -1
  297. package/dist/chunk-PAPMUAR5.js.map +0 -1
  298. package/dist/chunk-PW42ZXCB.js.map +0 -1
  299. package/dist/chunk-RK6VBLXP.js.map +0 -1
  300. package/dist/chunk-UM5S7DKK.js.map +0 -1
  301. package/dist/chunk-UMQ6NBQR.js.map +0 -1
  302. package/dist/chunk-XC5SJ35F.js.map +0 -1
  303. package/dist/chunk-XD7MY6HB.js.map +0 -1
  304. package/dist/chunk-YPAXJ7EW.js.map +0 -1
  305. package/dist/chunk-YT43MUSJ.js.map +0 -1
  306. package/dist/chunk-Z2MRSS2I.js.map +0 -1
  307. package/dist/claude-code-GRWO6XBZ.js.map +0 -1
  308. package/dist/claude-code-NMRKTAP7.js.map +0 -1
  309. package/dist/cline-25KW25IN.js.map +0 -1
  310. package/dist/codebuddy-ZNYQACMF.js.map +0 -1
  311. package/dist/codebuff-ETYT5UBL.js.map +0 -1
  312. package/dist/codebuff-VR6JUDIY.js.map +0 -1
  313. package/dist/codex-CPNKTXG3.js.map +0 -1
  314. package/dist/codex-YAORPCOD.js.map +0 -1
  315. package/dist/continue-WU2IB5KS.js.map +0 -1
  316. package/dist/copilot-cli-MZTAWMPW.js.map +0 -1
  317. package/dist/copilot-cli-PAP377OM.js.map +0 -1
  318. package/dist/crush-3AVQA4YU.js.map +0 -1
  319. package/dist/crush-QBXLNN7T.js.map +0 -1
  320. package/dist/cursor-DIW43TRZ.js.map +0 -1
  321. package/dist/cursor-GSLUET3V.js +0 -22
  322. package/dist/detect-6V4LC3MA.js.map +0 -1
  323. package/dist/devin-QMEM3OFA.js.map +0 -1
  324. package/dist/doctor-2YDUVSAU.js.map +0 -1
  325. package/dist/droid-MOKV53J4.js.map +0 -1
  326. package/dist/droid-U3ZBLQFO.js.map +0 -1
  327. package/dist/gemini-cli-47ZHCOZR.js.map +0 -1
  328. package/dist/gemini-cli-NMVVBO3K.js.map +0 -1
  329. package/dist/goose-4EI5MYTI.js.map +0 -1
  330. package/dist/goose-64V46Y2W.js.map +0 -1
  331. package/dist/grok-cli-QYQPDSVB.js.map +0 -1
  332. package/dist/hermes-F3EGTK6N.js.map +0 -1
  333. package/dist/hermes-F75OSK3Y.js.map +0 -1
  334. package/dist/hook-OEVRDMXD.js.map +0 -1
  335. package/dist/install-5NMCUDG5.js.map +0 -1
  336. package/dist/jetbrains-copilot-ZGIWI6YV.js.map +0 -1
  337. package/dist/junie-DIFN2P22.js.map +0 -1
  338. package/dist/kilo-EMZJBEBB.js.map +0 -1
  339. package/dist/kilo-SW7BH6EG.js.map +0 -1
  340. package/dist/kilo-cli-S3GXBPAE.js.map +0 -1
  341. package/dist/kilo-cli-ZF272ZHN.js.map +0 -1
  342. package/dist/kimi-5FHEXGZD.js.map +0 -1
  343. package/dist/kimi-IQJOIY6B.js.map +0 -1
  344. package/dist/kiro-BYX4HEAJ.js.map +0 -1
  345. package/dist/kiro-WGOI5CGT.js.map +0 -1
  346. package/dist/leaderboard-XEKS7B3H.js.map +0 -1
  347. package/dist/mimo-code-7R53QDHB.js.map +0 -1
  348. package/dist/mistral-vibe-LHJ55FSB.js.map +0 -1
  349. package/dist/mux-NYCNGL43.js.map +0 -1
  350. package/dist/mux-XM6AP35L.js.map +0 -1
  351. package/dist/nemoclaw-WSIPSE7U.js.map +0 -1
  352. package/dist/omp-562NK5BJ.js.map +0 -1
  353. package/dist/open-interpreter-I2CNOEXY.js.map +0 -1
  354. package/dist/openclaw-5EQFGOWL.js.map +0 -1
  355. package/dist/openclaw-7EYBQS4R.js +0 -17
  356. package/dist/opencode-QGP4ALQY.js.map +0 -1
  357. package/dist/opencode-YS4WX2E2.js.map +0 -1
  358. package/dist/openhands-QMYIKNOS.js.map +0 -1
  359. package/dist/package-G25GVTH2.js.map +0 -1
  360. package/dist/pi-644FIN3A.js.map +0 -1
  361. package/dist/pi-GGDQREVG.js.map +0 -1
  362. package/dist/qwen-code-GQILBJ5B.js.map +0 -1
  363. package/dist/qwen-code-STDMX7EM.js.map +0 -1
  364. package/dist/roo-code-DLPIHHQ6.js.map +0 -1
  365. package/dist/roo-code-SUWI4BBN.js.map +0 -1
  366. package/dist/serve-XTMIUHKK.js.map +0 -1
  367. package/dist/status-VJAGO4Z7.js.map +0 -1
  368. package/dist/statusline-EY2E2UGF.js.map +0 -1
  369. package/dist/synthetic-6FRA7P5Q.js.map +0 -1
  370. package/dist/telemetry-OLZQBADT.js.map +0 -1
  371. package/dist/trae-CPPYASR2.js.map +0 -1
  372. package/dist/trae-UOYD6JZZ.js.map +0 -1
  373. package/dist/uninstall-AZEUPAND.js.map +0 -1
  374. package/dist/upgrade-CAUUHSSC.js.map +0 -1
  375. package/dist/usage-3KHZ4RYZ.js.map +0 -1
  376. package/dist/usage-event-TZWOZPRJ.js.map +0 -1
  377. package/dist/vscode-copilot-NIMH2DGU.js.map +0 -1
  378. package/dist/warp-NRBBJQR2.js.map +0 -1
  379. package/dist/warp-QYJGKURM.js.map +0 -1
  380. package/dist/windsurf-75LLZ37C.js.map +0 -1
  381. package/dist/zed-HKAZXEJW.js.map +0 -1
  382. package/dist/zed-XJFOOKCO.js.map +0 -1
  383. /package/dist/{antigravity-VXLDLTO7.js.map → antigravity-PZXYAO3X.js.map} +0 -0
  384. /package/dist/{cursor-GSLUET3V.js.map → cursor-JRVK56QA.js.map} +0 -0
  385. /package/dist/{openclaw-7EYBQS4R.js.map → openclaw-AOKFJGRQ.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sdk/test-harness.ts"],"sourcesContent":["/**\r\n * sdk/test-harness — the SHIPPED, OFFLINE behavioral harness.\r\n *\r\n * Two read-only tools a connector author runs WITHOUT installing anything or\r\n * touching the host's filesystem:\r\n *\r\n * • `explain(connector)` — the static per-host × per-DECLARED-surface matrix:\r\n * for every registered host and every surface the connector actually ships,\r\n * is it `native` (the host can honor it), `disabled` (the connector opted\r\n * out for that host via `platforms[host].<surface> === false`), or\r\n * `skip-warn` (the host cannot honor it → install would skip-warn)?\r\n *\r\n * • `simulate(connector, opts)` — runs the REAL adapter parse → handler →\r\n * format chain INLINE against a host-shaped raw payload and reports whether\r\n * the host actually HONORS the connector's response (the trustworthy part:\r\n * it surfaces the documented degradations, e.g. Codex silently dropping a\r\n * UserPromptSubmit \"context\" injection that has no stdout path).\r\n *\r\n * It deliberately does NOT import the runtime entrypoints (runHook /\r\n * runStatusline) — those load the REGISTERED connector from disk and record\r\n * telemetry. The harness instead re-implements the tiny parse→handler→format\r\n * chain against the in-memory connector + the adapter, mirroring the runtime's\r\n * input tolerance (empty/malformed payload → `{}`) and its fail-safe envelope.\r\n */\r\n\r\nimport type {\r\n HookEventName,\r\n HookResponse,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PlatformOverride,\r\n ResolvedConnector,\r\n} from \"../core/types.js\";\r\nimport { allAdapters, loadAdapter } from \"../adapters/registry.js\";\r\nimport type { NormalizedEvent } from \"../adapters/spi.js\";\r\nimport {\r\n compileMatcher,\r\n eventMatcherSubject,\r\n} from \"../runtime/hook-entrypoint.js\";\r\nimport type { SurfaceName } from \"./introspect.js\";\r\nimport { hostCanFireEvent, SURFACE_PREDICATES } from \"./introspect.js\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// explain — the static per-host × per-declared-surface matrix\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** One cell of the {@link explain} matrix: a host × a declared surface. */\r\nexport interface ExplainRow {\r\n host: PlatformId;\r\n surface: SurfaceName;\r\n /**\r\n * - \"native\" — the host's capabilities pass the surface predicate.\r\n * - \"disabled\" — the connector opted out for this host\r\n * (`platforms[host].<surface> === false`).\r\n * - \"skip-warn\" — the host cannot honor it; install would skip-warn.\r\n */\r\n support: \"native\" | \"skip-warn\" | \"disabled\";\r\n reason: string;\r\n}\r\n\r\n/**\r\n * The GLOBAL surfaces a connector declares (these apply to every host — only\r\n * declared surfaces get a matrix row). The two platform-scoped surfaces\r\n * (`configPatch` / `nativeHooks`) are deliberately EXCLUDED here: they are\r\n * declared under one specific `platforms[host]`, so each is added PER HOST in\r\n * {@link explain} (see {@link platformScopedSurfacesFor}) — adding them globally\r\n * would falsely promise a configPatch row on hosts that never declared it.\r\n */\r\nfunction declaredGlobalSurfaces(connector: ResolvedConnector): SurfaceName[] {\r\n const out: SurfaceName[] = [];\r\n if (connector.server) out.push(\"server\");\r\n if (Object.keys(connector.hooks).length > 0) out.push(\"hooks\");\r\n if (connector.commands.length > 0) out.push(\"commands\");\r\n if (connector.skills.length > 0) out.push(\"skills\");\r\n if (connector.subagents.length > 0) out.push(\"subagents\");\r\n if (connector.memory.length > 0) out.push(\"memory\");\r\n if (connector.statusline) out.push(\"statusline\");\r\n if (connector.actions.length > 0) out.push(\"actions\");\r\n return out;\r\n}\r\n\r\n/**\r\n * The platform-scoped surfaces (`configPatch` / `nativeHooks`) THIS host\r\n * actually declares — keyed off the host's own `platforms[host]` override, so a\r\n * configPatch declared under claude-code never shows up as a row for codex.\r\n */\r\nfunction platformScopedSurfacesFor(\r\n override: PlatformOverride | undefined,\r\n): SurfaceName[] {\r\n const out: SurfaceName[] = [];\r\n if (!override) return out;\r\n if ((override.configPatch?.length ?? 0) > 0) out.push(\"configPatch\");\r\n if (override.nativeHooks != null && Object.keys(override.nativeHooks).length > 0) {\r\n out.push(\"nativeHooks\");\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Is the surface explicitly DISABLED for this host? Mirrors the per-host opt-out\r\n * the installer honors: a boolean-or-object surface override is \"disabled\" only\r\n * when set to the literal `false` (the object form is a tuning, not an opt-out).\r\n * `configPatch` / `nativeHooks` have no boolean opt-out form — declaring them is\r\n * the opt-in, so they are never \"disabled\".\r\n */\r\nfunction isDisabledForHost(\r\n connector: ResolvedConnector,\r\n host: PlatformId,\r\n surface: SurfaceName,\r\n): boolean {\r\n const override = connector.platforms[host];\r\n if (!override) return false;\r\n switch (surface) {\r\n case \"server\":\r\n return override.server === false;\r\n case \"hooks\":\r\n return override.hooks === false;\r\n case \"commands\":\r\n return override.commands === false;\r\n case \"skills\":\r\n return override.skills === false;\r\n case \"subagents\":\r\n return override.subagents === false;\r\n case \"memory\":\r\n return override.memory === false;\r\n case \"statusline\":\r\n return override.statusline === false;\r\n case \"actions\":\r\n case \"configPatch\":\r\n case \"nativeHooks\":\r\n // No per-host boolean opt-out form (declaring them is the opt-in).\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * The static support matrix: every registered host × every surface the\r\n * connector declares. Rows are sorted by host then surface for stable output.\r\n * Pure read of the adapter capabilities — installs nothing, writes nothing.\r\n */\r\nexport async function explain(connector: ResolvedConnector): Promise<ExplainRow[]> {\r\n const globalSurfaces = declaredGlobalSurfaces(connector);\r\n const adapters = await allAdapters();\r\n const rows: ExplainRow[] = [];\r\n\r\n for (const adapter of adapters) {\r\n // Per-host surface set = the global surfaces every host gets a row for, plus\r\n // the platform-scoped surfaces THIS host actually declares (configPatch /\r\n // nativeHooks live under platforms[host], so they are host-local).\r\n const override = connector.platforms[adapter.id];\r\n const surfaces = [\r\n ...globalSurfaces,\r\n ...platformScopedSurfacesFor(override),\r\n ];\r\n for (const surface of surfaces) {\r\n if (isDisabledForHost(connector, adapter.id, surface)) {\r\n rows.push({\r\n host: adapter.id,\r\n surface,\r\n support: \"disabled\",\r\n reason: `connector opted out for ${adapter.id} (platforms.${adapter.id}.${surface} = false)`,\r\n });\r\n continue;\r\n }\r\n // The hooks surface is judged against the connector's SPECIFIC declared\r\n // events, not the coarse \"host has a hook runtime\" predicate — otherwise a\r\n // Stop-only connector falsely shows `native` on a PreToolUse-only host\r\n // (the host CAN fire a hook, just not the one this connector declared).\r\n if (surface === \"hooks\") {\r\n rows.push(hooksRow(connector, adapter.id, adapter.capabilities));\r\n continue;\r\n }\r\n const native = SURFACE_PREDICATES[surface](adapter.capabilities);\r\n rows.push({\r\n host: adapter.id,\r\n surface,\r\n support: native ? \"native\" : \"skip-warn\",\r\n reason: explainReason(adapter.id, surface, native),\r\n });\r\n }\r\n }\r\n\r\n rows.sort((a, b) =>\r\n a.host === b.host ? a.surface.localeCompare(b.surface) : a.host.localeCompare(b.host),\r\n );\r\n return rows;\r\n}\r\n\r\n/**\r\n * The per-DECLARED-event hooks verdict for one host. A connector's hooks surface\r\n * is `native` only when EVERY event it declares can fire on the host; if any\r\n * declared event has no native equivalent the row is `skip-warn` and the reason\r\n * names the specific dead events (the same events install skip-warns). This is\r\n * the fix for the OR-across-all-events false green: a Stop-only connector on a\r\n * host that cannot fire Stop now reports skip-warn, not native.\r\n *\r\n * Reuses {@link hostCanFireEvent} — the single per-event source of truth shared\r\n * with {@link SURFACE_PREDICATES} and simulate's runtime verdict — so explain\r\n * never re-derives the per-event rules.\r\n */\r\nfunction hooksRow(\r\n connector: ResolvedConnector,\r\n host: PlatformId,\r\n capabilities: PlatformCapabilities,\r\n): ExplainRow {\r\n const declared = connector.hookEvents;\r\n const dead = declared.filter((e) => !hostCanFireEvent(capabilities, e));\r\n if (dead.length === 0) {\r\n return {\r\n host,\r\n surface: \"hooks\",\r\n support: \"native\",\r\n reason: `${host} can fire every declared hook event (${declared.join(\", \")})`,\r\n };\r\n }\r\n const fireable = declared.filter((e) => hostCanFireEvent(capabilities, e));\r\n const reason =\r\n fireable.length === 0\r\n ? `${host} cannot fire any declared hook event (${dead.join(\", \")}); install skip-warns each`\r\n : `${host} cannot fire ${dead.join(\", \")} (install skip-warns these); honors ${fireable.join(\", \")}`;\r\n return { host, surface: \"hooks\", support: \"skip-warn\", reason };\r\n}\r\n\r\n/**\r\n * The human-readable reason for a native/skip-warn cell. `server` is a special\r\n * case: the predicate is CAPABILITY-based (the host advertises ≥1 MCP\r\n * transport), but advertising a transport does NOT guarantee a writable\r\n * registration — e.g. jetbrains-copilot advertises stdio yet its installServer\r\n * skip-warns because MCP is managed in IDE settings. So the server reason states\r\n * the verdict is capability-based and registration may be host-managed, rather\r\n * than promising a write. (A precise per-adapter installServer dry-run is a\r\n * Phase-2 enhancement — until then this avoids over-promising.)\r\n */\r\nfunction explainReason(host: PlatformId, surface: SurfaceName, native: boolean): string {\r\n if (surface === \"server\") {\r\n return native\r\n ? `${host} advertises an MCP transport (capability-based; registration may be host-managed)`\r\n : `${host} advertises no MCP transport; install skip-warns`;\r\n }\r\n return native\r\n ? `${host} natively supports ${surface}`\r\n : `${host} cannot honor ${surface}; install skip-warns`;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// simulate — the real adapter parse → handler → format chain, inline\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** What to drive through the harness: one host, one surface, one raw payload. */\r\nexport interface SimulateOptions {\r\n surface: \"hooks\" | \"statusline\";\r\n host: PlatformId | string;\r\n /** REQUIRED when `surface === \"hooks\"`: which normalized lifecycle event. */\r\n event?: HookEventName;\r\n /** The host-shaped RAW payload (what the host would pipe in on stdin). */\r\n input: unknown;\r\n}\r\n\r\n/** The harness verdict: did the host actually honor the connector's response? */\r\nexport interface SimulateResult {\r\n /** True when the host's native reply carries the connector's intent. */\r\n honored: boolean;\r\n /** The host-native stdout the adapter would emit (when there is one). */\r\n hostReply?: string;\r\n /** Human-readable why — names the documented degradation when honored:false. */\r\n reason: string;\r\n}\r\n\r\n/**\r\n * Tolerantly coerce the raw payload, mirroring the runtime's parseStdin: a\r\n * string is JSON-parsed (empty/malformed → `{}`); a nullish value → `{}`; any\r\n * other value (already-parsed object) is passed through untouched.\r\n */\r\nfunction coerceInput(input: unknown): unknown {\r\n if (input == null) return {};\r\n if (typeof input === \"string\") {\r\n const trimmed = input.trim();\r\n if (trimmed === \"\") return {};\r\n try {\r\n return JSON.parse(trimmed);\r\n } catch {\r\n return {};\r\n }\r\n }\r\n return input;\r\n}\r\n\r\n/** Normalize a handler's return into a concrete response (void/non-object → `{}`). */\r\nfunction normalizeResponse(value: HookResponse | void): HookResponse {\r\n if (value == null || typeof value !== \"object\") return {};\r\n return value;\r\n}\r\n\r\n/**\r\n * The decoded shape of a host's native hook reply on stdout. Every json-stdio\r\n * adapter writes a SUBSET of these fields (claude-code / codex share the\r\n * `hookSpecificOutput` envelope; Stop-class continuation uses the TOP-LEVEL\r\n * `decision:\"block\"`). We read the DECODED fields — never substring-match the\r\n * serialized JSON, which a path/newline/quote in the payload would break.\r\n */\r\ninterface ParsedReply {\r\n /** Top-level Stop-class continuation: {\"decision\":\"block\",\"reason\"}. */\r\n decision?: unknown;\r\n reason?: unknown;\r\n /** Top-level Claude context channel (no adapter emits it yet; future-proofing). */\r\n systemMessage?: unknown;\r\n hookSpecificOutput?: {\r\n hookEventName?: unknown;\r\n additionalContext?: unknown;\r\n permissionDecision?: unknown;\r\n permissionDecisionReason?: unknown;\r\n updatedInput?: unknown;\r\n /** PermissionRequest nested envelope: decision:{behavior:\"allow\"|\"deny\", updatedInput?}. */\r\n decision?: { behavior?: unknown; updatedInput?: unknown };\r\n };\r\n}\r\n\r\n/**\r\n * Tolerantly decode a reply's stdout into {@link ParsedReply}. Empty / absent /\r\n * unparseable stdout is a legitimate \"no payload\" signal (the adapter wrote a\r\n * bare exit-0) → `undefined`, the honest \"this decision had no stdout path\"\r\n * verdict input.\r\n */\r\nfunction parseReplyStdout(stdout: string | undefined): ParsedReply | undefined {\r\n if (stdout == null || stdout.trim() === \"\") return undefined;\r\n try {\r\n const value = JSON.parse(stdout);\r\n return value != null && typeof value === \"object\"\r\n ? (value as ParsedReply)\r\n : undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/** Structural deep-equal for plain JSON values (objects/arrays/primitives). */\r\nfunction deepEqual(a: unknown, b: unknown): boolean {\r\n if (a === b) return true;\r\n if (typeof a !== typeof b || a == null || b == null) return false;\r\n if (typeof a !== \"object\") return false;\r\n const aArr = Array.isArray(a);\r\n if (aArr !== Array.isArray(b)) return false;\r\n if (aArr) {\r\n const x = a as unknown[];\r\n const y = b as unknown[];\r\n return x.length === y.length && x.every((v, i) => deepEqual(v, y[i]));\r\n }\r\n const x = a as Record<string, unknown>;\r\n const y = b as Record<string, unknown>;\r\n const xk = Object.keys(x);\r\n const yk = Object.keys(y);\r\n return xk.length === yk.length && xk.every((k) => deepEqual(x[k], y[k]));\r\n}\r\n\r\n/**\r\n * Decide whether the host HONORED a decision-bearing response, judging the\r\n * PARSED reply against each adapter's ACTUAL (event, decision) contract — never\r\n * by substring-matching the serialized JSON. `allow`/no-decision is always\r\n * pass-through honored; every other decision is honored only when the decoded\r\n * reply carries that exact intent, and the verdict names the documented\r\n * degradation when it does not.\r\n */\r\nfunction judgeHookHonor(\r\n host: string,\r\n event: HookEventName,\r\n response: HookResponse,\r\n stdout: string | undefined,\r\n): { honored: boolean; reason: string } {\r\n const decision = response.decision;\r\n if (decision === undefined || decision === \"allow\") {\r\n return { honored: true, reason: \"pass-through allow\" };\r\n }\r\n\r\n const reply = parseReplyStdout(stdout);\r\n const hso = reply?.hookSpecificOutput;\r\n\r\n if (decision === \"context\") {\r\n // Honored only when the DECODED additionalContext equals what the handler\r\n // asked to inject (claude-code's hookSpecificOutput.additionalContext, or a\r\n // future top-level systemMessage). codex only emits this on\r\n // SessionStart/PostToolUse — every other event silently drops it.\r\n const want = response.additionalContext;\r\n const got = hso?.additionalContext ?? reply?.systemMessage;\r\n if (reply && got === want) {\r\n return { honored: true, reason: `${host} injects context on ${event}` };\r\n }\r\n return {\r\n honored: false,\r\n reason: `${host} drops context on ${event} (no stdout path)`,\r\n };\r\n }\r\n\r\n if (decision === \"deny\") {\r\n if (!reply) {\r\n return {\r\n honored: false,\r\n reason: `${host} drops deny on ${event} (deny not honored; fails open to allow)`,\r\n };\r\n }\r\n // A real block: PreToolUse's permissionDecision:\"deny\", or the\r\n // PermissionRequest nested decision.behavior:\"deny\".\r\n if (hso?.permissionDecision === \"deny\" || hso?.decision?.behavior === \"deny\") {\r\n return { honored: true, reason: `${host} blocks ${event}` };\r\n }\r\n // Stop-class continuation: top-level decision:\"block\" KEEPS the session /\r\n // subagent running (persistence) — the SEMANTIC OPPOSITE of a block. Honored\r\n // (the reply carried the intent), but the reason must not say \"blocks\".\r\n if (reply.decision === \"block\") {\r\n return {\r\n honored: true,\r\n reason: `${host} continues the session/subagent on ${event} (persistence) — NOT a block`,\r\n };\r\n }\r\n // SubagentStart / PostToolUseFailure DEGRADE a deny to a context note — the\r\n // spawn/failure is not actually blocked.\r\n if (hso?.additionalContext != null) {\r\n return {\r\n honored: false,\r\n reason: `${host} degrades deny to a context note on ${event}; spawn/failure not blocked`,\r\n };\r\n }\r\n return {\r\n honored: false,\r\n reason: `${host} drops deny on ${event} (deny not honored; fails open to allow)`,\r\n };\r\n }\r\n\r\n if (decision === \"ask\") {\r\n if (hso?.permissionDecision === \"ask\") {\r\n return { honored: true, reason: `${host} asks on ${event}` };\r\n }\r\n // On PermissionRequest, ask/void falls through to the host's NATIVE dialog\r\n // (the dialog IS the ask), which the adapter expresses as a bare exit-0.\r\n if (event === \"PermissionRequest\" && !reply) {\r\n return {\r\n honored: true,\r\n reason: `${host} shows the native confirmation dialog on ${event} (the ask)`,\r\n };\r\n }\r\n return {\r\n honored: false,\r\n reason: `${host} drops ask on ${event} (no ask path)`,\r\n };\r\n }\r\n\r\n // decision === \"modify\": honored only when the decoded updatedInput deep-equals\r\n // what the handler asked to write (hookSpecificOutput.updatedInput, or the\r\n // PermissionRequest nested decision.updatedInput).\r\n const wrote = hso?.updatedInput ?? hso?.decision?.updatedInput;\r\n if (reply && wrote !== undefined && deepEqual(wrote, response.updatedInput)) {\r\n return { honored: true, reason: `${host} rewrites tool input on ${event}` };\r\n }\r\n return {\r\n honored: false,\r\n reason: `${host} drops modify on ${event} (modify not honored)`,\r\n };\r\n}\r\n\r\n/**\r\n * Run the connector against ONE host-shaped payload through the REAL adapter\r\n * path and report whether the host honors the response. Self-contained (does not\r\n * touch the registered-connector loader or telemetry) and fail-safe: any throw\r\n * in the chain degrades to `honored:false` with the error message, never rejects.\r\n */\r\nexport async function simulate(\r\n connector: ResolvedConnector,\r\n opts: SimulateOptions,\r\n): Promise<SimulateResult> {\r\n try {\r\n const adapter = await loadAdapter(opts.host);\r\n if (!adapter) {\r\n return { honored: false, reason: `unknown host \"${opts.host}\"` };\r\n }\r\n\r\n if (opts.surface === \"statusline\") {\r\n if (\r\n !(adapter.capabilities.supportsStatusline ?? false) ||\r\n !adapter.parseStatusInput ||\r\n !adapter.formatStatusOutput\r\n ) {\r\n return { honored: false, reason: `${opts.host} has no statusline surface` };\r\n }\r\n // Guarding on the TOP-LEVEL render is sufficient: a `hosts`-only statusline\r\n // (a hosts: map with no top-level render) is rejected at defineConnector, so\r\n // a connector that targets `opts.host` only via hosts[] still has a top-level\r\n // render here. (If that author-time invariant ever relaxes, revisit this.)\r\n if (!connector.statusline || typeof connector.statusline.render !== \"function\") {\r\n return { honored: false, reason: \"connector declares no statusline\" };\r\n }\r\n const ctx = adapter.parseStatusInput(coerceInput(opts.input));\r\n ctx.connectorId = connector.id;\r\n // Defensive: parseStatusInput now sets ctx.capabilities (it is required on\r\n // StatuslineContext), but a future adapter could omit it — backfill from\r\n // the adapter so render() can always read ctx.capabilities.\r\n if (ctx.capabilities == null) ctx.capabilities = adapter.capabilities;\r\n // Per-host render override, mirroring the runtime: hosts[host].render wins\r\n // over the top-level render; an unlisted host falls back to top-level.\r\n const perHost = connector.statusline.hosts?.[opts.host as PlatformId]?.render;\r\n const render =\r\n typeof perHost === \"function\" ? perHost : connector.statusline.render;\r\n const rendered = await render(ctx);\r\n const line = rendered == null ? \"\" : String(rendered);\r\n const reply = adapter.formatStatusOutput(line);\r\n return {\r\n honored: true,\r\n ...(reply.stdout !== undefined ? { hostReply: reply.stdout } : {}),\r\n reason: \"rendered\",\r\n };\r\n }\r\n\r\n // surface === \"hooks\"\r\n const event = opts.event;\r\n if (!event) {\r\n return { honored: false, reason: \"event is required when surface === \\\"hooks\\\"\" };\r\n }\r\n if (!adapter.parseEvent || !adapter.formatReply) {\r\n return { honored: false, reason: `${opts.host} has no hook runtime (mcp-only)` };\r\n }\r\n\r\n const evt = adapter.parseEvent(event, coerceInput(opts.input));\r\n evt.connectorId = connector.id;\r\n // Mirror the runtime: runHook backfills evt.capabilities from the adapter so\r\n // a handler reading evt.capabilities behaves the same here. (scope/telemetry\r\n // are runtime-only — recovered from the registered meta / store, which the\r\n // offline harness deliberately never touches — so they stay undefined.)\r\n evt.capabilities = adapter.capabilities;\r\n\r\n // Guarding on the TOP-LEVEL handler is sufficient: a `hosts`-only hook (a\r\n // hosts: map with no top-level handler) is rejected at defineConnector, so a\r\n // host targeted only via hosts[] still has a top-level handler here. (If that\r\n // author-time invariant ever relaxes, revisit this.)\r\n const definition = connector.hooks[event];\r\n if (!definition || typeof definition.handler !== \"function\") {\r\n return { honored: false, reason: `connector declares no ${event} handler` };\r\n }\r\n\r\n // Matcher filtering, in lockstep with runHook: when the connector's matcher\r\n // is set and the event's subject (tool name, or agent type for Subagent*\r\n // events) does not match, the host never runs the handler — so the harness\r\n // must NOT run it either, and reports the (honored) exclusion. Reuses the\r\n // runtime helpers directly so the two stay byte-for-byte aligned.\r\n const subject = eventMatcherSubject(evt as NormalizedEvent);\r\n if (subject !== undefined) {\r\n const re = compileMatcher(definition.matcher);\r\n if (re && !re.test(subject)) {\r\n return {\r\n honored: true,\r\n reason: `matcher excludes ${subject} (handler not run)`,\r\n };\r\n }\r\n }\r\n\r\n // Per-host handler override, mirroring runHook: hosts[host].handler wins\r\n // over the top-level handler; an unlisted host falls back to top-level.\r\n const perHost = definition.hosts?.[opts.host as PlatformId]?.handler;\r\n const handler = typeof perHost === \"function\" ? perHost : definition.handler;\r\n const response = normalizeResponse(\r\n await handler(evt as never),\r\n );\r\n const reply = adapter.formatReply(event, response);\r\n const verdict = judgeHookHonor(String(opts.host), event, response, reply.stdout);\r\n return {\r\n honored: verdict.honored,\r\n ...(reply.stdout !== undefined ? { hostReply: reply.stdout } : {}),\r\n reason: verdict.reason,\r\n };\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n const verb = opts.surface === \"statusline\" ? \"render\" : \"handler\";\r\n return { honored: false, reason: `${verb} threw: ${message}` };\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// explainHooks — the per-(host, event) honor verdict (powers doctor --explain)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * One per-(host, event) cell of {@link explainHooks}.\r\n *\r\n * - \"honored\" — the host fires the event AND carries the connector's response\r\n * intent (simulate's runtime verdict, e.g. a real deny/block).\r\n * - \"degraded\" — the host fires the event but silently DROPS the response's\r\n * decision (e.g. codex fires UserPromptSubmit but has no stdout\r\n * path for a context injection, so the injection vanishes).\r\n * - \"dropped\" — the host cannot fire the event at all (no native equivalent /\r\n * mcp-only host); install skip-warns it and it never runs.\r\n */\r\nexport interface HookEventVerdict {\r\n host: PlatformId;\r\n event: HookEventName;\r\n verdict: \"honored\" | \"degraded\" | \"dropped\";\r\n /** The simulate()-grade reason string (names the documented degradation). */\r\n reason: string;\r\n}\r\n\r\n/**\r\n * A subject (tool name / agent type) that SATISFIES the connector's matcher for\r\n * this event, so a matcher-gated hook actually runs and the verdict reflects the\r\n * real honor — not a spurious \"matcher excludes my arbitrary synthetic tool\".\r\n * A plain-literal matcher (no regex metacharacters — the common case, e.g.\r\n * `acme_query`) IS the subject. A missing/empty matcher matches everything, and\r\n * a complex regex falls back to the generic subject (then an honest \"matcher\r\n * excludes\" verdict surfaces — the dev's matcher genuinely would not fire here).\r\n */\r\nfunction matchingSubject(matcher: string | undefined, fallback: string): string {\r\n if (!matcher || matcher === \"\") return fallback;\r\n return /^[\\w.-]+$/.test(matcher) ? matcher : fallback;\r\n}\r\n\r\n/**\r\n * A minimal host-shaped synthetic payload so the real parse→handler→format\r\n * chain has something to run on. The harness coerces a `{}` to a valid event,\r\n * but tool/subagent events read a subject (tool name / agent type) the connector\r\n * matcher may filter on — so we plant a subject that SATISFIES the connector's\r\n * declared matcher ({@link matchingSubject}) to exercise the honor path.\r\n */\r\nfunction syntheticPayloadFor(\r\n event: HookEventName,\r\n matcher: string | undefined,\r\n): Record<string, unknown> {\r\n switch (event) {\r\n case \"PreToolUse\":\r\n case \"PostToolUse\":\r\n case \"PostToolUseFailure\":\r\n case \"PermissionRequest\": {\r\n const tool = matchingSubject(matcher, \"Bash\");\r\n return { tool_name: tool, tool_input: { command: \"echo hi\" } };\r\n }\r\n case \"SubagentStart\":\r\n case \"SubagentStop\": {\r\n const agent = matchingSubject(matcher, \"general\");\r\n return { agent_type: agent, subagent_type: agent };\r\n }\r\n case \"UserPromptSubmit\":\r\n return { prompt: \"hello\" };\r\n default:\r\n return {};\r\n }\r\n}\r\n\r\n/**\r\n * The per-(host, event) hook honor matrix for the connector's DECLARED events\r\n * across `hosts` — the trustworthy detail behind `doctor --explain`.\r\n *\r\n * For each (host, declared event): when the host cannot fire the event\r\n * ({@link hostCanFireEvent} is false, OR the host has no hook runtime at all)\r\n * the cell is `dropped`; otherwise it RUNS the real chain via {@link simulate}\r\n * and reports `honored` (simulate honored the response) or `degraded` (the host\r\n * fired but dropped the decision). Reuses the per-event signal + simulate's\r\n * verdict — no per-event honor logic is re-derived here.\r\n */\r\nexport async function explainHooks(\r\n connector: ResolvedConnector,\r\n hosts: (PlatformId | string)[],\r\n): Promise<HookEventVerdict[]> {\r\n const events = connector.hookEvents;\r\n const rows: HookEventVerdict[] = [];\r\n\r\n for (const host of hosts) {\r\n const adapter = await loadAdapter(host);\r\n if (!adapter) {\r\n for (const event of events) {\r\n rows.push({\r\n host: host as PlatformId,\r\n event,\r\n verdict: \"dropped\",\r\n reason: `unknown host \"${host}\"`,\r\n });\r\n }\r\n continue;\r\n }\r\n\r\n // An mcp-only host (no hook runtime) drops every declared event.\r\n const hasHookRuntime = !!adapter.parseEvent && !!adapter.formatReply;\r\n\r\n for (const event of events) {\r\n if (!hasHookRuntime) {\r\n rows.push({\r\n host: adapter.id,\r\n event,\r\n verdict: \"dropped\",\r\n reason: `${adapter.id} has no hook runtime (mcp-only) — ${event} never fires`,\r\n });\r\n continue;\r\n }\r\n if (!hostCanFireEvent(adapter.capabilities, event)) {\r\n rows.push({\r\n host: adapter.id,\r\n event,\r\n verdict: \"dropped\",\r\n reason: `${adapter.id} has no ${event} equivalent — install skip-warns it; never fires`,\r\n });\r\n continue;\r\n }\r\n const sim = await simulate(connector, {\r\n surface: \"hooks\",\r\n host: adapter.id,\r\n event,\r\n input: syntheticPayloadFor(event, connector.hooks[event]?.matcher),\r\n });\r\n rows.push({\r\n host: adapter.id,\r\n event,\r\n verdict: sim.honored ? \"honored\" : \"degraded\",\r\n reason: sim.reason,\r\n });\r\n }\r\n }\r\n\r\n rows.sort((a, b) =>\r\n a.host === b.host ? a.event.localeCompare(b.event) : a.host.localeCompare(b.host),\r\n );\r\n return rows;\r\n}\r\n"],"mappings":"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","names":["x","y","perHost","reply"]}
@@ -2,14 +2,14 @@
2
2
  import {
3
3
  codexConfigHome,
4
4
  xdgConfigHome
5
- } from "./chunk-UM5S7DKK.js";
5
+ } from "./chunk-C747DYNT.js";
6
6
  import {
7
7
  assertNoSymlinkInPath,
8
8
  connectorDir,
9
9
  connectorsDir,
10
10
  dataRoot,
11
11
  ensureDir
12
- } from "./chunk-GZGLCCEE.js";
12
+ } from "./chunk-QYOUQCC6.js";
13
13
 
14
14
  // src/core/marketplace-state.ts
15
15
  import { createHash } from "crypto";
@@ -561,4 +561,4 @@ export {
561
561
  marketplaceEvidence,
562
562
  hashDirectory
563
563
  };
564
- //# sourceMappingURL=chunk-2Y2MZRLP.js.map
564
+ //# sourceMappingURL=chunk-7C2IPIIB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/marketplace-state.ts"],"sourcesContent":["/**\r\n * core/marketplace-state — pure read/write helpers for MARKETPLACE-method\r\n * install state. A LEAF module (fs + paths only, no adapters, no spawning) so\r\n * the installer, the marketplace orchestration, doctor, and status can all\r\n * import it without cycles.\r\n *\r\n * Two kinds of state live here:\r\n *\r\n * 1. The per-connector state record `connectorDir(id)/marketplace-installs.json`\r\n * — one entry per platform a connector was marketplace-installed on\r\n * ({ format, bundleDir, marketplace, scope, version, contentHash,\r\n * installedAt }). It drives `uninstall --method auto`, upgrade staleness\r\n * warnings, doctor, and the double-install guard.\r\n *\r\n * 2. Read-only fs EVIDENCE probes of the hosts' own plugin state (e.g.\r\n * Claude Code's `$CLAUDE_CONFIG_DIR/plugins/installed_plugins.json`).\r\n * These catch installs the user performed manually from `package`'s\r\n * printed instructions, which the state record alone would miss. Probes\r\n * read undocumented host internals, so every reader degrades to null/false\r\n * on absence or parse failure — the guard convention is \"refuse only on\r\n * positive evidence\" (docs: design risk 4).\r\n */\r\n\r\nimport { createHash } from \"node:crypto\";\r\nimport {\r\n existsSync,\r\n readdirSync,\r\n readFileSync,\r\n realpathSync,\r\n rmSync,\r\n statSync,\r\n writeFileSync,\r\n} from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { dirname, join, relative, resolve } from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\n\r\nimport TOML from \"@iarna/toml\";\r\n\r\nimport type { InstallScope, PlatformId } from \"./types.js\";\r\nimport type { PackageFormat } from \"./package.js\";\r\nimport {\r\n assertNoSymlinkInPath,\r\n connectorDir,\r\n connectorsDir,\r\n dataRoot,\r\n ensureDir,\r\n} from \"./paths.js\";\r\nimport { codexConfigHome, xdgConfigHome } from \"./host-paths.js\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// State record: connectorDir(id)/marketplace-installs.json\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** The catalog name every staged local marketplace is registered under. */\r\nexport const MARKETPLACE_NAME = \"agent-connector\";\r\n\r\n/** One marketplace install of one connector on one platform. */\r\nexport interface MarketplaceInstallRecord {\r\n /** Bundle format that was staged + installed (e.g. \"claude-plugin\"). */\r\n format: PackageFormat;\r\n /** Absolute path of the staged plugin dir the host references/copied. */\r\n bundleDir: string;\r\n /** Marketplace/catalog name the install is keyed under ({@link MARKETPLACE_NAME}). */\r\n marketplace: string;\r\n /** Install scope (v1: always \"user\"). */\r\n scope: InstallScope;\r\n /** connector.version at install time (staleness warnings key off this). */\r\n version: string;\r\n /** sha256 over the staged plugin dir at install time (drift detection). */\r\n contentHash: string;\r\n /** ISO timestamp of the recorded install. */\r\n installedAt: string;\r\n}\r\n\r\n/** platform → record map persisted per connector. */\r\nexport type MarketplaceInstalls = Partial<\r\n Record<PlatformId, MarketplaceInstallRecord>\r\n>;\r\n\r\n/** Path of the per-connector marketplace state record. */\r\nexport function marketplaceInstallsPath(connectorId: string): string {\r\n return join(connectorDir(connectorId), \"marketplace-installs.json\");\r\n}\r\n\r\n/** Read the state record ({} on absence/parse failure — never throws). */\r\nexport function readMarketplaceInstalls(connectorId: string): MarketplaceInstalls {\r\n try {\r\n const raw = readFileSync(marketplaceInstallsPath(connectorId), \"utf8\");\r\n const parsed = JSON.parse(raw) as unknown;\r\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\r\n return parsed as MarketplaceInstalls;\r\n }\r\n } catch {\r\n /* absent / unreadable → empty */\r\n }\r\n return {};\r\n}\r\n\r\n/** Write the state record; an empty map DELETES the file (uninstall leaves no husk). */\r\nexport function writeMarketplaceInstalls(\r\n connectorId: string,\r\n installs: MarketplaceInstalls,\r\n): void {\r\n const path = marketplaceInstallsPath(connectorId);\r\n assertNoSymlinkInPath(path);\r\n if (Object.keys(installs).length === 0) {\r\n rmSync(path, { force: true });\r\n return;\r\n }\r\n ensureDir(dirname(path));\r\n writeFileSync(path, `${JSON.stringify(installs, null, 2)}\\n`, \"utf8\");\r\n}\r\n\r\n/** True when THIS connector has any recorded marketplace install. */\r\nexport function connectorHasMarketplaceRecords(connectorId: string): boolean {\r\n return Object.keys(readMarketplaceInstalls(connectorId)).length > 0;\r\n}\r\n\r\n/**\r\n * True when ANY registered connector (optionally excluding one) still records a\r\n * marketplace install. Guards home-bin removal on purge: an installed plugin's\r\n * hooks exec the home-bin OUTSIDE any adapter-managed config, so removing the\r\n * launcher while a marketplace install survives would silently kill its hooks.\r\n */\r\nexport function anyMarketplaceRecordsRemain(excludeConnectorId?: string): boolean {\r\n const dir = connectorsDir();\r\n if (!existsSync(dir)) return false;\r\n let entries: string[];\r\n try {\r\n entries = readdirSync(dir);\r\n } catch {\r\n return false;\r\n }\r\n return entries.some(\r\n (id) => id !== excludeConnectorId && connectorHasMarketplaceRecords(id),\r\n );\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Staging roots — bundles staged by-reference must survive cwd changes and\r\n// `rm -rf dist-plugin`, so they live under the data-root, one shared root per\r\n// host family, holding every marketplace-installed connector's bundle plus ONE\r\n// regenerated catalog named \"agent-connector\" listing them all.\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Root for every marketplace staging tree: <dataRoot>/marketplace. */\r\nexport function marketplaceRoot(): string {\r\n return join(dataRoot(), \"marketplace\");\r\n}\r\n\r\n/** Shared Claude-family staging root: <dataRoot>/marketplace/claude. */\r\nexport function claudeStagingRoot(): string {\r\n return join(marketplaceRoot(), \"claude\");\r\n}\r\n\r\n/** Shared Codex-family staging root: <dataRoot>/marketplace/codex. */\r\nexport function codexStagingRoot(): string {\r\n return join(marketplaceRoot(), \"codex\");\r\n}\r\n\r\n/** Shared GitHub Copilot CLI staging root: <dataRoot>/marketplace/copilot. */\r\nexport function copilotStagingRoot(): string {\r\n return join(marketplaceRoot(), \"copilot\");\r\n}\r\n\r\n/** Shared agy (Antigravity) staging root: <dataRoot>/marketplace/agy. */\r\nexport function agyStagingRoot(): string {\r\n return join(marketplaceRoot(), \"agy\");\r\n}\r\n\r\n/** Shared Gemini-CLI staging root: <dataRoot>/marketplace/gemini. */\r\nexport function geminiStagingRoot(): string {\r\n return join(marketplaceRoot(), \"gemini\");\r\n}\r\n\r\n/** Shared Qwen-Code staging root: <dataRoot>/marketplace/qwen. */\r\nexport function qwenStagingRoot(): string {\r\n return join(marketplaceRoot(), \"qwen\");\r\n}\r\n\r\n/** Shared droid (Factory) staging root: <dataRoot>/marketplace/droid. */\r\nexport function droidStagingRoot(): string {\r\n return join(marketplaceRoot(), \"droid\");\r\n}\r\n\r\n/**\r\n * Shared npm-local staging root: <dataRoot>/marketplace/npm. Every npm-plugin\r\n * host (opencode / kilo / kilo-cli) stages here; the host config's `plugin`\r\n * array references the bundle by `file://<this>/<id>`, so it MUST live under the\r\n * stable data-root (it survives cwd changes and `rm -rf dist-plugin`).\r\n */\r\nexport function npmStagingRoot(): string {\r\n return join(marketplaceRoot(), \"npm\");\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Claude Code host-state readers ($CLAUDE_CONFIG_DIR || ~/.claude)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Claude Code's config dir, honoring the CLAUDE_CONFIG_DIR override. */\r\nexport function claudeConfigDir(): string {\r\n const override = process.env.CLAUDE_CONFIG_DIR;\r\n if (override && override.trim() !== \"\") return resolve(override);\r\n return join(homedir(), \".claude\");\r\n}\r\n\r\n/** The installed_plugins.json key our marketplace install creates for `id`. */\r\nexport function claudePluginKey(connectorId: string): string {\r\n return `${connectorId}@${MARKETPLACE_NAME}`;\r\n}\r\n\r\nfunction readJsonFile(path: string): Record<string, unknown> | null {\r\n try {\r\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\r\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\r\n return parsed as Record<string, unknown>;\r\n }\r\n } catch {\r\n /* absent / unreadable → null */\r\n }\r\n return null;\r\n}\r\n\r\n/** The `plugins` map from Claude's installed_plugins.json (null when unreadable). */\r\nexport function readClaudeInstalledPlugins(): Record<string, unknown> | null {\r\n const file = join(claudeConfigDir(), \"plugins\", \"installed_plugins.json\");\r\n const parsed = readJsonFile(file);\r\n const plugins = parsed?.plugins;\r\n if (plugins && typeof plugins === \"object\" && !Array.isArray(plugins)) {\r\n return plugins as Record<string, unknown>;\r\n }\r\n return null;\r\n}\r\n\r\n/** True when Claude's own state lists `<id>@agent-connector` as installed. */\r\nexport function claudePluginInstalled(connectorId: string): boolean {\r\n const plugins = readClaudeInstalledPlugins();\r\n if (!plugins) return false;\r\n const entry = plugins[claudePluginKey(connectorId)];\r\n return Array.isArray(entry) ? entry.length > 0 : entry != null;\r\n}\r\n\r\n/** True when ANY `*@agent-connector` plugin remains in Claude's state. */\r\nexport function anyClaudeAgentConnectorPlugins(): boolean {\r\n const plugins = readClaudeInstalledPlugins();\r\n if (!plugins) return false;\r\n return Object.entries(plugins).some(\r\n ([key, entry]) =>\r\n key.endsWith(`@${MARKETPLACE_NAME}`) &&\r\n (Array.isArray(entry) ? entry.length > 0 : entry != null),\r\n );\r\n}\r\n\r\n/**\r\n * The directory Claude's known_marketplaces.json records for `name`, or null\r\n * when not registered / unreadable. Used both as a presence probe and as the\r\n * NAME-COLLISION check: a registration pointing somewhere other than our\r\n * staging root belongs to the user and must never be touched.\r\n */\r\nexport function claudeKnownMarketplacePath(name: string): string | null {\r\n const file = join(claudeConfigDir(), \"plugins\", \"known_marketplaces.json\");\r\n const parsed = readJsonFile(file);\r\n const entry = parsed?.[name];\r\n if (!entry || typeof entry !== \"object\") return null;\r\n const e = entry as { installLocation?: unknown; source?: { path?: unknown } };\r\n if (typeof e.installLocation === \"string\") return e.installLocation;\r\n const sourcePath = e.source?.path;\r\n return typeof sourcePath === \"string\" ? sourcePath : null;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Codex host-state readers ($CODEX_HOME || ~/.codex → config.toml)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** The config.toml key our marketplace install creates for `id` (== claude's). */\r\nexport function codexPluginKey(connectorId: string): string {\r\n return `${connectorId}@${MARKETPLACE_NAME}`;\r\n}\r\n\r\n/** Parse <CODEX_HOME>/config.toml ({} on absence/parse failure — never throws). */\r\nfunction readCodexConfig(): Record<string, unknown> {\r\n const file = join(codexConfigHome(), \"config.toml\");\r\n try {\r\n return TOML.parse(readFileSync(file, \"utf8\")) as Record<string, unknown>;\r\n } catch {\r\n return {};\r\n }\r\n}\r\n\r\n/**\r\n * True when Codex's config.toml carries the `[plugins.\"<id>@agent-connector\"]`\r\n * table. The DEFINITIVE install probe — empty `plugins/cache/...` dirs linger\r\n * after uninstall, so the cache dir must NOT be used (docs: codex quirk).\r\n */\r\nexport function codexPluginInstalled(connectorId: string): boolean {\r\n const cfg = readCodexConfig();\r\n const plugins = cfg[\"plugins\"];\r\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\r\n return codexPluginKey(connectorId) in (plugins as Record<string, unknown>);\r\n}\r\n\r\n/** True when ANY `*@agent-connector` plugin remains in Codex's config.toml. */\r\nexport function anyCodexAgentConnectorPlugins(): boolean {\r\n const cfg = readCodexConfig();\r\n const plugins = cfg[\"plugins\"];\r\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\r\n return Object.keys(plugins as Record<string, unknown>).some((k) =>\r\n k.endsWith(`@${MARKETPLACE_NAME}`),\r\n );\r\n}\r\n\r\n/**\r\n * The directory config.toml records as `[marketplaces.agent-connector].source`,\r\n * or null when not registered / unreadable. Both a presence probe and the\r\n * NAME-COLLISION check: a registration pointing somewhere other than our\r\n * staging root belongs to the user and must never be touched.\r\n */\r\nexport function codexMarketplaceSource(name: string): string | null {\r\n const cfg = readCodexConfig();\r\n const marketplaces = cfg[\"marketplaces\"];\r\n if (!marketplaces || typeof marketplaces !== \"object\" || Array.isArray(marketplaces)) {\r\n return null;\r\n }\r\n const entry = (marketplaces as Record<string, unknown>)[name];\r\n if (!entry || typeof entry !== \"object\") return null;\r\n const source = (entry as { source?: unknown }).source;\r\n return typeof source === \"string\" ? source : null;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// GitHub Copilot CLI host-state readers (~/.copilot, NO env — HOME isolation)\r\n//\r\n// LIVE-VERIFIED against GitHub Copilot CLI 1.0.63:\r\n// • ~/.copilot/config.json — JSONC (leading `// …` comment header, \"managed\r\n// automatically\") carrying `installedPlugins: [{ name, marketplace,\r\n// version, installed_at, enabled, cache_path }]`. The DEFINITIVE install\r\n// probe (an uninstall empties this array). Parsed JSONC-tolerantly so the\r\n// comment header does not break JSON.parse.\r\n// • ~/.copilot/settings.json — `extraKnownMarketplaces[\"<name>\"].source.path`\r\n// records the registered local marketplace dir (a `{source:\"directory\",\r\n// path}` object); `enabledPlugins[\"<id>@agent-connector\"]: true`. The\r\n// `.source.path` is both the presence probe and the NAME-COLLISION check.\r\n// Both readers degrade to false/null on absence/parse failure (refuse only on\r\n// positive evidence).\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Copilot CLI's config dir: ~/.copilot (NO dedicated env — HOME isolation). */\r\nexport function copilotConfigDir(): string {\r\n return join(homedir(), \".copilot\");\r\n}\r\n\r\n/** The state key our marketplace install creates for `id` (== claude/codex). */\r\nexport function copilotPluginKey(connectorId: string): string {\r\n return `${connectorId}@${MARKETPLACE_NAME}`;\r\n}\r\n\r\n/** The `installedPlugins` array from Copilot's config.json (JSONC; [] when unreadable). */\r\nfunction readCopilotInstalledPlugins(): Array<Record<string, unknown>> {\r\n let raw: string;\r\n try {\r\n raw = readFileSync(join(copilotConfigDir(), \"config.json\"), \"utf8\");\r\n } catch {\r\n return [];\r\n }\r\n const parsed = parseJsonc(raw);\r\n const plugins = parsed?.[\"installedPlugins\"];\r\n if (!Array.isArray(plugins)) return [];\r\n return plugins.filter(\r\n (e): e is Record<string, unknown> =>\r\n e != null && typeof e === \"object\" && !Array.isArray(e),\r\n );\r\n}\r\n\r\n/**\r\n * True when Copilot's config.json lists `id` installed from our marketplace\r\n * (`installedPlugins[]` carries an entry with `name === id`, `marketplace ===\r\n * agent-connector`, and not `enabled: false`). The DEFINITIVE probe — an\r\n * uninstall empties this array (live-verified on CLI 1.0.63).\r\n */\r\nexport function copilotPluginInstalled(connectorId: string): boolean {\r\n return readCopilotInstalledPlugins().some(\r\n (e) =>\r\n e[\"name\"] === connectorId &&\r\n e[\"marketplace\"] === MARKETPLACE_NAME &&\r\n e[\"enabled\"] !== false,\r\n );\r\n}\r\n\r\n/** True when ANY plugin from our marketplace remains in Copilot's config.json. */\r\nexport function anyCopilotAgentConnectorPlugins(): boolean {\r\n return readCopilotInstalledPlugins().some(\r\n (e) => e[\"marketplace\"] === MARKETPLACE_NAME && e[\"enabled\"] !== false,\r\n );\r\n}\r\n\r\n/**\r\n * The directory settings.json records as `extraKnownMarketplaces[\"<name>\"]\r\n * .source.path`, or null when not registered / unreadable. Both a presence probe\r\n * and the NAME-COLLISION check (a registration pointing somewhere other than our\r\n * staging root belongs to the user and must never be touched). `source` is a\r\n * `{ source: \"directory\", path }` object on CLI 1.0.63; a bare string source is\r\n * accepted defensively.\r\n */\r\nexport function copilotMarketplaceSource(name: string): string | null {\r\n const parsed = readJsonFile(join(copilotConfigDir(), \"settings.json\"));\r\n const known = parsed?.[\"extraKnownMarketplaces\"];\r\n if (!known || typeof known !== \"object\" || Array.isArray(known)) return null;\r\n const entry = (known as Record<string, unknown>)[name];\r\n if (entry == null) return null;\r\n const source = (entry as { source?: unknown }).source;\r\n if (typeof source === \"string\") return source;\r\n if (source && typeof source === \"object\" && !Array.isArray(source)) {\r\n const path = (source as { path?: unknown }).path;\r\n if (typeof path === \"string\") return path;\r\n }\r\n return null;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// droid (Factory) host-state readers (~/.factory/settings.json, JSON, NO env)\r\n//\r\n// DOCS-ONLY: no droid binary is present on this box. The settings.json key\r\n// shapes below (`enabledPlugins[\"<id>@agent-connector\"] === true` +\r\n// `extraKnownMarketplaces[\"agent-connector\"].source`) follow the documented\r\n// factory plugin model — confirm the exact JSON shapes live when a droid binary\r\n// is available. Both readers read defensively (refuse only on positive evidence).\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Factory's settings file: ~/.factory/settings.json (NO dedicated env). */\r\nexport function factorySettingsPath(): string {\r\n return join(homedir(), \".factory\", \"settings.json\");\r\n}\r\n\r\n/** The settings.json key our marketplace install creates for `id` (== codex's). */\r\nexport function droidPluginKey(connectorId: string): string {\r\n return `${connectorId}@${MARKETPLACE_NAME}`;\r\n}\r\n\r\n/** The `enabledPlugins` map from droid's settings.json (null when unreadable). */\r\nfunction readDroidEnabledPlugins(): Record<string, unknown> | null {\r\n const parsed = readJsonFile(factorySettingsPath());\r\n const plugins = parsed?.enabledPlugins;\r\n if (plugins && typeof plugins === \"object\" && !Array.isArray(plugins)) {\r\n return plugins as Record<string, unknown>;\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * True when droid's settings.json carries\r\n * `enabledPlugins[\"<id>@agent-connector\"] === true`. DOCS-only: the strict\r\n * `=== true` value shape is unverified live (a future droid may store an object).\r\n */\r\nexport function droidPluginInstalled(connectorId: string): boolean {\r\n const plugins = readDroidEnabledPlugins();\r\n if (!plugins) return false;\r\n return plugins[droidPluginKey(connectorId)] === true;\r\n}\r\n\r\n/** True when ANY `*@agent-connector` plugin remains enabled in droid's settings. */\r\nexport function anyDroidAgentConnectorPlugins(): boolean {\r\n const plugins = readDroidEnabledPlugins();\r\n if (!plugins) return false;\r\n return Object.entries(plugins).some(\r\n ([key, value]) => key.endsWith(`@${MARKETPLACE_NAME}`) && value === true,\r\n );\r\n}\r\n\r\n/**\r\n * The directory droid's settings.json records as\r\n * `extraKnownMarketplaces[\"<name>\"].source`, or null when not registered /\r\n * unreadable. Both a presence probe and the NAME-COLLISION check (a registration\r\n * pointing somewhere other than our staging root belongs to the user). DOCS-only:\r\n * the local-path `source` field shape is undocumented, so this reads defensively\r\n * — accepts either a bare string source or an object carrying a `path`/`source`\r\n * string, and NORMALIZES to a single string for the samePath compare.\r\n */\r\nexport function droidMarketplaceSource(name: string): string | null {\r\n const parsed = readJsonFile(factorySettingsPath());\r\n const known = parsed?.extraKnownMarketplaces;\r\n if (!known || typeof known !== \"object\" || Array.isArray(known)) return null;\r\n const entry = (known as Record<string, unknown>)[name];\r\n if (entry == null) return null;\r\n if (typeof entry === \"string\") return entry;\r\n if (typeof entry === \"object\" && !Array.isArray(entry)) {\r\n const e = entry as { source?: unknown; path?: unknown };\r\n if (typeof e.source === \"string\") return e.source;\r\n if (typeof e.path === \"string\") return e.path;\r\n }\r\n return null;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// agy (Antigravity) host-state readers (~/.gemini/config/plugins, NO env)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** agy's config dir: ~/.gemini/config (NO dedicated env — HOME isolation). */\r\nexport function agyConfigDir(): string {\r\n return join(homedir(), \".gemini\", \"config\");\r\n}\r\n\r\n/** agy's plugins dir: ~/.gemini/config/plugins. */\r\nexport function agyPluginsDir(): string {\r\n return join(agyConfigDir(), \"plugins\");\r\n}\r\n\r\n/**\r\n * Candidate `import_manifest.json` locations. agy 1.0.7 records the manifest at\r\n * `<config>/plugins/import_manifest.json` on POSIX but at\r\n * `<config>/import_manifest.json` on Windows (live-confirmed on the my-window\r\n * box) — both are probed, then the copied plugin dir is the final fallback.\r\n */\r\nexport function agyImportManifestPaths(): string[] {\r\n return [\r\n join(agyConfigDir(), \"import_manifest.json\"), // win32 location\r\n join(agyPluginsDir(), \"import_manifest.json\"), // posix location\r\n ];\r\n}\r\n\r\n/** @deprecated single-location accessor — prefer {@link agyImportManifestPaths}. */\r\nexport function agyImportManifestPath(): string {\r\n return join(agyPluginsDir(), \"import_manifest.json\");\r\n}\r\n\r\n/**\r\n * True when `id` is installed per agy's import manifest (`imports[].name`),\r\n * checking BOTH the win32 and posix manifest locations, with a fallback probe of\r\n * the copied plugin dir's plugin.json. agy's uninstall removes the plugin dir\r\n * and clears the manifest entry (live-verified on Windows: install → dir +\r\n * manifest present, uninstall → both gone, re-install idempotent).\r\n */\r\nexport function agyPluginInstalled(connectorId: string): boolean {\r\n for (const manifest of agyImportManifestPaths()) {\r\n try {\r\n const parsed = JSON.parse(readFileSync(manifest, \"utf8\")) as {\r\n imports?: unknown;\r\n };\r\n const imports = parsed.imports;\r\n if (\r\n Array.isArray(imports) &&\r\n imports.some(\r\n (e) =>\r\n e != null &&\r\n typeof e === \"object\" &&\r\n (e as { name?: unknown }).name === connectorId,\r\n )\r\n ) {\r\n return true;\r\n }\r\n } catch {\r\n /* absent / unreadable manifest → try the next candidate */\r\n }\r\n }\r\n return existsSync(join(agyPluginsDir(), connectorId, \"plugin.json\"));\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Gemini CLI host-state readers (~/.gemini/extensions, NO env — HOME isolation)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Gemini CLI's config dir: ~/.gemini (NO dedicated config-dir env — isolation is\r\n * via HOME, live-confirmed on gemini 0.36.0). DISTINCT from agy's ~/.gemini/config\r\n * tree: gemini extensions land under ~/.gemini/extensions/<id>/.\r\n */\r\nexport function geminiConfigDir(): string {\r\n return join(homedir(), \".gemini\");\r\n}\r\n\r\n/**\r\n * True when `id` is installed per Gemini's own extension store: the marker file\r\n * ~/.gemini/extensions/<id>/gemini-extension.json exists. Gemini exits 0 even on\r\n * a logical failure, so this fs probe — not the exit code — is the source of\r\n * truth (the driver re-probes after every spawn).\r\n */\r\nexport function geminiExtensionInstalled(connectorId: string): boolean {\r\n return existsSync(\r\n join(geminiConfigDir(), \"extensions\", connectorId, \"gemini-extension.json\"),\r\n );\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Qwen Code host-state readers (~/.qwen/extensions, NO env — HOME isolation)\r\n//\r\n// DOCS-ONLY: no qwen binary is present on this box. qwen-code is a gemini-cli\r\n// fork, so the extension store SHAPE (~/.qwen/extensions/<id>/qwen-extension.json)\r\n// mirrors gemini's — confirm the marker filename live when a qwen binary lands.\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Qwen Code's config dir: ~/.qwen (NO dedicated config-dir env — isolation is\r\n * via HOME, like its gemini-cli parent). DOCS-only: confirm live when a qwen\r\n * binary is available. DISTINCT from ~/.gemini (the gemini-cli store).\r\n */\r\nexport function qwenConfigDir(): string {\r\n return join(homedir(), \".qwen\");\r\n}\r\n\r\n/**\r\n * True when `id` is installed per Qwen's own extension store: the marker file\r\n * ~/.qwen/extensions/<id>/qwen-extension.json exists. Mirrors gemini's fs probe\r\n * (qwen is a gemini fork); the driver re-probes after every spawn. DOCS-only:\r\n * the marker filename (`qwen-extension.json`) is unverified live.\r\n */\r\nexport function qwenExtensionInstalled(connectorId: string): boolean {\r\n return existsSync(\r\n join(qwenConfigDir(), \"extensions\", connectorId, \"qwen-extension.json\"),\r\n );\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// npm-local host-state readers (opencode / kilo / kilo-cli — XDG_CONFIG_HOME)\r\n//\r\n// These hosts have NO uninstall verb; installs are recorded as `file://<dir>`\r\n// entries in a `plugin` array inside an opencode-style config file, which the\r\n// driver EDITS to remove. This module is a spawn/shared-free LEAF, so a tiny\r\n// posix-resolve path comparison is inlined here rather than importing\r\n// shared.ts's samePath (and samePath does not strip the `file://` scheme).\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** opencode's config dir: $XDG_CONFIG_HOME/opencode (or ~/.config/opencode). */\r\nexport function opencodeConfigDir(): string {\r\n return join(xdgConfigHome(), \"opencode\");\r\n}\r\n\r\n/** kilo / kilo-cli's config dir: $XDG_CONFIG_HOME/kilo (or ~/.config/kilo). */\r\nexport function kiloConfigDir(): string {\r\n return join(xdgConfigHome(), \"kilo\");\r\n}\r\n\r\n/**\r\n * Candidate config filenames per npm-local host, most-preferred first. opencode\r\n * accepts opencode.jsonc / opencode.json / config.json (JSONC — comments\r\n * tolerated); kilo / kilo-cli use opencode.json (plain JSON).\r\n */\r\nfunction npmConfigCandidates(platform: PlatformId): string[] {\r\n return platform === \"opencode\"\r\n ? [\"opencode.jsonc\", \"opencode.json\", \"config.json\"]\r\n : [\"opencode.json\"];\r\n}\r\n\r\n/** The config DIR for an npm-local host (opencode vs kilo/kilo-cli). */\r\nfunction npmConfigDir(platform: PlatformId): string {\r\n return platform === \"opencode\" ? opencodeConfigDir() : kiloConfigDir();\r\n}\r\n\r\n/**\r\n * The config FILE path for `platform`: the first existing candidate, else the\r\n * most-preferred candidate (the path a fresh install would write). Never throws.\r\n */\r\nexport function npmConfigFilePath(platform: PlatformId): string {\r\n const dir = npmConfigDir(platform);\r\n const candidates = npmConfigCandidates(platform);\r\n for (const name of candidates) {\r\n if (existsSync(join(dir, name))) return join(dir, name);\r\n }\r\n return join(dir, candidates[0]!);\r\n}\r\n\r\n/** Strip a single leading `file://` (and `file://localhost/`) scheme prefix. */\r\nexport function stripFileScheme(entry: string): string {\r\n let s = entry;\r\n if (s.startsWith(\"file://localhost/\")) s = s.slice(\"file://localhost\".length);\r\n else if (s.startsWith(\"file://\")) s = s.slice(\"file://\".length);\r\n return s;\r\n}\r\n\r\n/**\r\n * Strip `//` line and `/* *​/` block comments from JSONC, STRING-AWARE: a `//`\r\n * or `/*` inside a JSON string literal (e.g. the `file://` in a plugin entry) is\r\n * left intact. Scans char-by-char tracking string + escape state — a naive\r\n * regex would corrupt `\"file:///dir\"` by eating from the embedded `//`.\r\n */\r\nfunction stripJsoncComments(text: string): string {\r\n let out = \"\";\r\n let inString = false;\r\n let inLine = false;\r\n let inBlock = false;\r\n for (let i = 0; i < text.length; i++) {\r\n const c = text[i]!;\r\n const next = i + 1 < text.length ? text[i + 1]! : \"\";\r\n if (inLine) {\r\n if (c === \"\\n\") {\r\n inLine = false;\r\n out += c;\r\n }\r\n continue;\r\n }\r\n if (inBlock) {\r\n if (c === \"*\" && next === \"/\") {\r\n inBlock = false;\r\n i++;\r\n }\r\n continue;\r\n }\r\n if (inString) {\r\n out += c;\r\n if (c === \"\\\\\") {\r\n // Copy the escaped char verbatim (covers \\\" and \\\\).\r\n if (next !== \"\") {\r\n out += next;\r\n i++;\r\n }\r\n } else if (c === '\"') {\r\n inString = false;\r\n }\r\n continue;\r\n }\r\n if (c === '\"') {\r\n inString = true;\r\n out += c;\r\n continue;\r\n }\r\n if (c === \"/\" && next === \"/\") {\r\n inLine = true;\r\n i++;\r\n continue;\r\n }\r\n if (c === \"/\" && next === \"*\") {\r\n inBlock = true;\r\n i++;\r\n continue;\r\n }\r\n out += c;\r\n }\r\n return out;\r\n}\r\n\r\n/** Tolerant JSONC → object: strip // line and block comments, then JSON.parse. */\r\nfunction parseJsonc(text: string): Record<string, unknown> | null {\r\n // A parse failure degrades to null (the \"refuse only on positive evidence\"\r\n // convention) rather than throwing.\r\n try {\r\n const parsed = JSON.parse(stripJsoncComments(text)) as unknown;\r\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\r\n return parsed as Record<string, unknown>;\r\n }\r\n } catch {\r\n /* unreadable / not JSONC → null */\r\n }\r\n return null;\r\n}\r\n\r\n/** Read + parse an npm-local host's config file ({}-shaped or null). Never throws. */\r\nexport function readNpmConfig(platform: PlatformId): Record<string, unknown> | null {\r\n const file = npmConfigFilePath(platform);\r\n try {\r\n return parseJsonc(readFileSync(file, \"utf8\"));\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Convert an npm `plugin`-array entry to a native absolute path. opencode/kilo\r\n * record a proper `file://` URL — on Windows that is `file:///C:/Users/…` (drive\r\n * letter + forward slashes), which a textual `file://`-strip leaves as the broken\r\n * `/C:/Users/…`. `fileURLToPath` does the correct cross-platform decode\r\n * (`file:///C:/x`→`C:\\x`, `file:///tmp/x`→`/tmp/x`). Bare-path entries (also\r\n * accepted by the hosts) and any malformed URL fall back to a textual strip.\r\n */\r\nfunction npmEntryToPath(entry: string): string {\r\n if (/^file:\\/\\//i.test(entry)) {\r\n try {\r\n return fileURLToPath(entry);\r\n } catch {\r\n /* malformed file URL (e.g. backslashes) → manual strip below */\r\n }\r\n }\r\n return stripFileScheme(entry);\r\n}\r\n\r\n/**\r\n * Path equality for the npm probe. Resolve symlinks with `realpathSync.native`\r\n * when the path exists (catches macOS `/var/folders`→`/private/var/folders` and\r\n * win32 8.3 short paths — the host records the realpath, we hold the symlink),\r\n * else lexical `resolve()`; case-fold on win32. Inlined here (leaf module)\r\n * rather than importing shared.ts's samePath, which pulls in the spawn layer.\r\n */\r\nfunction npmPathEquals(a: string, b: string): boolean {\r\n const norm = (p: string): string => {\r\n let s: string;\r\n try {\r\n s = realpathSync.native(p);\r\n } catch {\r\n s = resolve(p);\r\n }\r\n return process.platform === \"win32\" ? s.toLowerCase() : s;\r\n };\r\n return norm(a) === norm(b);\r\n}\r\n\r\n/**\r\n * The `plugin`-array ENTRY (the raw string, including any `file://`) whose value\r\n * — after stripping a leading `file://` — path-equals `id`'s staged bundle dir,\r\n * or null when no entry references it. Used both as the install probe and to\r\n * locate the exact entry the uninstall edit must drop.\r\n */\r\nexport function npmPluginArrayEntry(\r\n platform: PlatformId,\r\n connectorId: string,\r\n): string | null {\r\n const cfg = readNpmConfig(platform);\r\n if (!cfg) return null;\r\n const arr = cfg[\"plugin\"];\r\n if (!Array.isArray(arr)) return null;\r\n const target = join(npmStagingRoot(), connectorId);\r\n for (const raw of arr) {\r\n if (typeof raw !== \"string\") continue;\r\n if (npmPathEquals(npmEntryToPath(raw), target)) return raw;\r\n }\r\n return null;\r\n}\r\n\r\n/** True when `platform`'s config `plugin` array references `id`'s staged bundle. */\r\nexport function npmPluginInstalled(platform: PlatformId, connectorId: string): boolean {\r\n return npmPluginArrayEntry(platform, connectorId) != null;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Cross-host marketplace-install EVIDENCE (the double-install guard's probe)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Read-only evidence that `connectorId` is marketplace/plugin-installed on\r\n * `platform`: the state record first, then a cheap fs probe of the host's own\r\n * plugin state (catches manual installs from `package`'s printed instructions).\r\n * Returns a human-readable description of the evidence, or null when none.\r\n */\r\nexport function marketplaceEvidence(\r\n connectorId: string,\r\n platform: PlatformId,\r\n): string | null {\r\n if (readMarketplaceInstalls(connectorId)[platform]) {\r\n return \"recorded in marketplace-installs.json\";\r\n }\r\n switch (platform) {\r\n case \"claude-code\":\r\n if (claudePluginInstalled(connectorId)) {\r\n return `${claudePluginKey(connectorId)} listed in Claude's installed_plugins.json`;\r\n }\r\n return null;\r\n case \"gemini-cli\":\r\n return geminiExtensionInstalled(connectorId)\r\n ? `extension ${connectorId} listed in Gemini's extensions store`\r\n : null;\r\n case \"qwen-code\":\r\n return qwenExtensionInstalled(connectorId)\r\n ? `extension ${connectorId} listed in Qwen's extensions store`\r\n : null;\r\n case \"droid\":\r\n return droidPluginInstalled(connectorId)\r\n ? `${droidPluginKey(connectorId)} enabled in droid's settings.json`\r\n : null;\r\n case \"antigravity\":\r\n case \"antigravity-cli\": {\r\n const dir = join(homedir(), \".gemini\", \"config\", \"plugins\", connectorId);\r\n return existsSync(dir) ? `plugin dir ${dir} exists` : null;\r\n }\r\n case \"opencode\":\r\n case \"kilo\":\r\n case \"kilo-cli\":\r\n return npmPluginInstalled(platform, connectorId)\r\n ? `${connectorId} listed in ${platform}'s config plugin array`\r\n : null;\r\n case \"codex\": {\r\n const dir = join(\r\n codexConfigHome(),\r\n \"plugins\",\r\n \"cache\",\r\n MARKETPLACE_NAME,\r\n connectorId,\r\n );\r\n return existsSync(dir) ? `codex plugin cache ${dir} exists` : null;\r\n }\r\n case \"copilot-cli\":\r\n return copilotPluginInstalled(connectorId)\r\n ? `${copilotPluginKey(connectorId)} listed in Copilot's config.json`\r\n : null;\r\n default:\r\n return null;\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Content hashing — staged-bundle drift detection\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Deterministic sha256 over every file under `dir` (sorted relative paths +\r\n * bytes). Empty string when the dir is missing/unreadable. Used to record the\r\n * staged bundle's content at install time and detect drift later (claude caches\r\n * a versioned COPY, so an edited connector goes silently stale without this).\r\n */\r\nexport function hashDirectory(dir: string): string {\r\n if (!existsSync(dir)) return \"\";\r\n const files: string[] = [];\r\n const walk = (d: string): void => {\r\n let entries: string[];\r\n try {\r\n entries = readdirSync(d);\r\n } catch {\r\n return;\r\n }\r\n for (const name of entries.sort()) {\r\n const full = join(d, name);\r\n let st;\r\n try {\r\n st = statSync(full);\r\n } catch {\r\n continue;\r\n }\r\n if (st.isDirectory()) walk(full);\r\n else if (st.isFile()) files.push(full);\r\n }\r\n };\r\n walk(dir);\r\n const hash = createHash(\"sha256\");\r\n for (const file of files.sort()) {\r\n hash.update(relative(dir, file).split(\"\\\\\").join(\"/\"));\r\n hash.update(\"\\0\");\r\n try {\r\n hash.update(readFileSync(file));\r\n } catch {\r\n /* unreadable file contributes only its path */\r\n }\r\n hash.update(\"\\0\");\r\n }\r\n return hash.digest(\"hex\");\r\n}\r\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  aggregateBy,
4
4
  dedupe
5
- } from "./chunk-XD7MY6HB.js";
5
+ } from "./chunk-7PSNXUFK.js";
6
6
 
7
7
  // src/usage/registry.ts
8
8
  var USAGE_READER_REGISTRY = [
@@ -11,142 +11,142 @@ var USAGE_READER_REGISTRY = [
11
11
  platformId: "claude-code",
12
12
  format: "jsonl",
13
13
  kind: "local",
14
- load: () => import("./claude-code-NMRKTAP7.js").then((m) => m.default)
14
+ load: () => import("./claude-code-FS42PXZO.js").then((m) => m.default)
15
15
  },
16
16
  {
17
17
  platformId: "codex",
18
18
  format: "jsonl",
19
19
  kind: "local",
20
- load: () => import("./codex-YAORPCOD.js").then((m) => m.default)
20
+ load: () => import("./codex-DKBAAUCO.js").then((m) => m.default)
21
21
  },
22
22
  {
23
23
  platformId: "gemini-cli",
24
24
  format: "jsonl",
25
25
  kind: "local",
26
- load: () => import("./gemini-cli-NMVVBO3K.js").then((m) => m.default)
26
+ load: () => import("./gemini-cli-Y6RJMU24.js").then((m) => m.default)
27
27
  },
28
28
  {
29
29
  platformId: "qwen-code",
30
30
  format: "jsonl",
31
31
  kind: "local",
32
- load: () => import("./qwen-code-GQILBJ5B.js").then((m) => m.default)
32
+ load: () => import("./qwen-code-26CSFDX6.js").then((m) => m.default)
33
33
  },
34
34
  {
35
35
  platformId: "copilot-cli",
36
36
  format: "jsonl",
37
37
  kind: "local",
38
- load: () => import("./copilot-cli-MZTAWMPW.js").then((m) => m.default)
38
+ load: () => import("./copilot-cli-CLBJACQ5.js").then((m) => m.default)
39
39
  },
40
40
  {
41
41
  platformId: "pi",
42
42
  format: "jsonl",
43
43
  kind: "local",
44
- load: () => import("./pi-GGDQREVG.js").then((m) => m.default)
44
+ load: () => import("./pi-DLKCPOJE.js").then((m) => m.default)
45
45
  },
46
46
  {
47
47
  platformId: "kimi",
48
48
  format: "jsonl",
49
49
  kind: "local",
50
- load: () => import("./kimi-5FHEXGZD.js").then((m) => m.default)
50
+ load: () => import("./kimi-MSIDEMCI.js").then((m) => m.default)
51
51
  },
52
52
  {
53
53
  platformId: "openclaw",
54
54
  format: "jsonl",
55
55
  kind: "local",
56
- load: () => import("./openclaw-5EQFGOWL.js").then((m) => m.default)
56
+ load: () => import("./openclaw-KAFTNLR2.js").then((m) => m.default)
57
57
  },
58
58
  // U2 — JSON readers
59
59
  {
60
60
  platformId: "amp",
61
61
  format: "json",
62
62
  kind: "local",
63
- load: () => import("./amp-BQZOL5GR.js").then((m) => m.default)
63
+ load: () => import("./amp-CEWOSWQO.js").then((m) => m.default)
64
64
  },
65
65
  {
66
66
  platformId: "droid",
67
67
  format: "json",
68
68
  kind: "local",
69
- load: () => import("./droid-U3ZBLQFO.js").then((m) => m.default)
69
+ load: () => import("./droid-PFQ2PZSF.js").then((m) => m.default)
70
70
  },
71
71
  {
72
72
  platformId: "codebuff",
73
73
  format: "json",
74
74
  kind: "local",
75
- load: () => import("./codebuff-ETYT5UBL.js").then((m) => m.default)
75
+ load: () => import("./codebuff-6KWBW6CT.js").then((m) => m.default)
76
76
  },
77
77
  {
78
78
  platformId: "mux",
79
79
  format: "json",
80
80
  kind: "local",
81
- load: () => import("./mux-XM6AP35L.js").then((m) => m.default)
81
+ load: () => import("./mux-7PIJ7HUQ.js").then((m) => m.default)
82
82
  },
83
83
  {
84
84
  platformId: "roo-code",
85
85
  format: "json",
86
86
  kind: "local",
87
- load: () => import("./roo-code-SUWI4BBN.js").then((m) => m.default)
87
+ load: () => import("./roo-code-HSAE2CJU.js").then((m) => m.default)
88
88
  },
89
89
  {
90
90
  platformId: "kilo",
91
91
  format: "json",
92
92
  kind: "local",
93
- load: () => import("./kilo-SW7BH6EG.js").then((m) => m.default)
93
+ load: () => import("./kilo-SMHPOHJ5.js").then((m) => m.default)
94
94
  },
95
95
  {
96
96
  platformId: "kiro",
97
97
  format: "json",
98
98
  kind: "local",
99
- load: () => import("./kiro-WGOI5CGT.js").then((m) => m.default)
99
+ load: () => import("./kiro-NYRRBIKW.js").then((m) => m.default)
100
100
  },
101
101
  // U3 — SQLite readers (via sql.js wasm)
102
102
  {
103
103
  platformId: "opencode",
104
104
  format: "sqlite",
105
105
  kind: "local",
106
- load: () => import("./opencode-QGP4ALQY.js").then((m) => m.default)
106
+ load: () => import("./opencode-Z65JYILL.js").then((m) => m.default)
107
107
  },
108
108
  {
109
109
  platformId: "goose",
110
110
  format: "sqlite",
111
111
  kind: "local",
112
- load: () => import("./goose-64V46Y2W.js").then((m) => m.default)
112
+ load: () => import("./goose-K2HKUTBK.js").then((m) => m.default)
113
113
  },
114
114
  {
115
115
  platformId: "hermes",
116
116
  format: "sqlite",
117
117
  kind: "local",
118
- load: () => import("./hermes-F3EGTK6N.js").then((m) => m.default)
118
+ load: () => import("./hermes-MF3NX4ZY.js").then((m) => m.default)
119
119
  },
120
120
  {
121
121
  platformId: "kilo-cli",
122
122
  format: "sqlite",
123
123
  kind: "local",
124
- load: () => import("./kilo-cli-ZF272ZHN.js").then((m) => m.default)
124
+ load: () => import("./kilo-cli-T2SRRRJS.js").then((m) => m.default)
125
125
  },
126
126
  {
127
127
  platformId: "synthetic",
128
128
  format: "sqlite",
129
129
  kind: "local",
130
- load: () => import("./synthetic-6FRA7P5Q.js").then((m) => m.default)
130
+ load: () => import("./synthetic-LUT6V6SD.js").then((m) => m.default)
131
131
  },
132
132
  {
133
133
  platformId: "crush",
134
134
  format: "sqlite",
135
135
  kind: "local",
136
- load: () => import("./crush-QBXLNN7T.js").then((m) => m.default)
136
+ load: () => import("./crush-A5C5USRL.js").then((m) => m.default)
137
137
  },
138
138
  {
139
139
  platformId: "zed",
140
140
  format: "sqlite",
141
141
  kind: "local",
142
- load: () => import("./zed-XJFOOKCO.js").then((m) => m.default)
142
+ load: () => import("./zed-XXCNYOIN.js").then((m) => m.default)
143
143
  },
144
144
  // U4 — synced/cloud readers (read tokscale local cache if present, else skip)
145
145
  {
146
146
  platformId: "cursor",
147
147
  format: "synced-cache",
148
148
  kind: "synced",
149
- load: () => import("./cursor-DIW43TRZ.js").then((m) => m.default)
149
+ load: () => import("./cursor-D5SWDVSW.js").then((m) => m.default)
150
150
  },
151
151
  {
152
152
  // Antigravity IDE: the native `~/.gemini/antigravity/conversations/<uuid>.pb`
@@ -156,7 +156,7 @@ var USAGE_READER_REGISTRY = [
156
156
  platformId: "antigravity",
157
157
  format: "synced-cache",
158
158
  kind: "synced",
159
- load: () => import("./antigravity-EA4L225L.js").then((m) => m.default)
159
+ load: () => import("./antigravity-LHFEFNMC.js").then((m) => m.default)
160
160
  },
161
161
  {
162
162
  // Antigravity CLI (`agy`): SHARES the IDE's protobuf (.pb) store (no separate
@@ -165,19 +165,19 @@ var USAGE_READER_REGISTRY = [
165
165
  platformId: "antigravity-cli",
166
166
  format: "synced-cache",
167
167
  kind: "synced",
168
- load: () => import("./antigravity-cli-B2HXZFYQ.js").then((m) => m.default)
168
+ load: () => import("./antigravity-cli-MOKFSWOU.js").then((m) => m.default)
169
169
  },
170
170
  {
171
171
  platformId: "trae",
172
172
  format: "synced-cache",
173
173
  kind: "synced",
174
- load: () => import("./trae-CPPYASR2.js").then((m) => m.default)
174
+ load: () => import("./trae-SIQI3C66.js").then((m) => m.default)
175
175
  },
176
176
  {
177
177
  platformId: "warp",
178
178
  format: "synced-cache",
179
179
  kind: "synced",
180
- load: () => import("./warp-NRBBJQR2.js").then((m) => m.default)
180
+ load: () => import("./warp-5YBSP7FP.js").then((m) => m.default)
181
181
  }
182
182
  ];
183
183
  var REGISTRY_BY_ID = new Map(
@@ -354,4 +354,4 @@ export {
354
354
  hostLeaderboard,
355
355
  formatHostLeaderboard
356
356
  };
357
- //# sourceMappingURL=chunk-NLE2J5P4.js.map
357
+ //# sourceMappingURL=chunk-7HHW23AC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/usage/registry.ts","../src/usage/scan.ts","../src/usage/leaderboard.ts"],"sourcesContent":["/**\r\n * usage/registry — the single source of truth for HOST usage readers.\r\n *\r\n * Mirrors adapters/registry.ts: each platform is ONE entry with a lazy `load()`\r\n * thunk resolving the reader module's default export. Adding a platform is a\r\n * single new entry here plus one `readers/<id>.ts` file — `getUsageReader` /\r\n * `allUsageReaders` and the scan orchestrator all derive from this list.\r\n *\r\n * `format` and `kind` are declared on the factory so the scan layer can describe\r\n * a reader (e.g. note a \"synced\" reader as skipped) without importing it.\r\n */\r\n\r\nimport type { PlatformId } from \"../core/types.js\";\r\nimport type { UsageReader, UsageReaderFactory } from \"./types.js\";\r\n\r\n/**\r\n * Authoritative list of usage readers. To add a platform: drop\r\n * `src/usage/readers/<id>.ts` (default-exporting a {@link UsageReader}) and add\r\n * one entry below.\r\n */\r\nexport const USAGE_READER_REGISTRY: readonly UsageReaderFactory[] = [\r\n // U1 — JSONL readers\r\n {\r\n platformId: \"claude-code\",\r\n format: \"jsonl\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/claude-code.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"codex\",\r\n format: \"jsonl\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/codex.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"gemini-cli\",\r\n format: \"jsonl\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/gemini-cli.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"qwen-code\",\r\n format: \"jsonl\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/qwen-code.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"copilot-cli\",\r\n format: \"jsonl\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/copilot-cli.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"pi\",\r\n format: \"jsonl\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/pi.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"kimi\",\r\n format: \"jsonl\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/kimi.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"openclaw\",\r\n format: \"jsonl\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/openclaw.js\").then((m) => m.default),\r\n },\r\n // U2 — JSON readers\r\n {\r\n platformId: \"amp\",\r\n format: \"json\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/amp.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"droid\",\r\n format: \"json\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/droid.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"codebuff\",\r\n format: \"json\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/codebuff.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"mux\",\r\n format: \"json\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/mux.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"roo-code\",\r\n format: \"json\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/roo-code.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"kilo\",\r\n format: \"json\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/kilo.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"kiro\",\r\n format: \"json\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/kiro.js\").then((m) => m.default),\r\n },\r\n // U3 — SQLite readers (via sql.js wasm)\r\n {\r\n platformId: \"opencode\",\r\n format: \"sqlite\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/opencode.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"goose\",\r\n format: \"sqlite\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/goose.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"hermes\",\r\n format: \"sqlite\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/hermes.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"kilo-cli\",\r\n format: \"sqlite\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/kilo-cli.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"synthetic\",\r\n format: \"sqlite\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/synthetic.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"crush\",\r\n format: \"sqlite\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/crush.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"zed\",\r\n format: \"sqlite\",\r\n kind: \"local\",\r\n load: () => import(\"./readers/zed.js\").then((m) => m.default),\r\n },\r\n // U4 — synced/cloud readers (read tokscale local cache if present, else skip)\r\n {\r\n platformId: \"cursor\",\r\n format: \"synced-cache\",\r\n kind: \"synced\",\r\n load: () => import(\"./readers/cursor.js\").then((m) => m.default),\r\n },\r\n {\r\n // Antigravity IDE: the native `~/.gemini/antigravity/conversations/<uuid>.pb`\r\n // store is PROTOBUF with no public schema (CONFIRMED by install) → not\r\n // parseable. So this is a SYNCED reader: it reads the tokscale synced-cache\r\n // if present, else [] (\"requires sync\"). The native dir is detection-only.\r\n platformId: \"antigravity\",\r\n format: \"synced-cache\",\r\n kind: \"synced\",\r\n load: () => import(\"./readers/antigravity.js\").then((m) => m.default),\r\n },\r\n {\r\n // Antigravity CLI (`agy`): SHARES the IDE's protobuf (.pb) store (no separate\r\n // dir, no public schema) → not parseable. SYNCED reader: tokscale cache if\r\n // present, else [] (\"requires sync\").\r\n platformId: \"antigravity-cli\",\r\n format: \"synced-cache\",\r\n kind: \"synced\",\r\n load: () => import(\"./readers/antigravity-cli.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"trae\",\r\n format: \"synced-cache\",\r\n kind: \"synced\",\r\n load: () => import(\"./readers/trae.js\").then((m) => m.default),\r\n },\r\n {\r\n platformId: \"warp\",\r\n format: \"synced-cache\",\r\n kind: \"synced\",\r\n load: () => import(\"./readers/warp.js\").then((m) => m.default),\r\n },\r\n];\r\n\r\n/** O(1) lookup index, built once at module-load time. */\r\nconst REGISTRY_BY_ID: ReadonlyMap<PlatformId, UsageReaderFactory> = new Map(\r\n USAGE_READER_REGISTRY.map((factory) => [factory.platformId, factory] as const),\r\n);\r\n\r\n/** Look up the lazy factory for a platform id, or undefined when unregistered. */\r\nexport function getUsageReaderFactory(id: PlatformId | string): UsageReaderFactory | undefined {\r\n return REGISTRY_BY_ID.get(id as PlatformId);\r\n}\r\n\r\n/** Resolve a platform id to its reader singleton, or undefined when unregistered. */\r\nexport async function getUsageReader(id: PlatformId | string): Promise<UsageReader | undefined> {\r\n const factory = getUsageReaderFactory(id);\r\n return factory ? factory.load() : undefined;\r\n}\r\n\r\n/** Load every registered reader (imports run in parallel; preserves order). */\r\nexport async function allUsageReaders(): Promise<UsageReader[]> {\r\n return Promise.all(USAGE_READER_REGISTRY.map((factory) => factory.load()));\r\n}\r\n","/**\r\n * usage/scan — orchestrate every registered reader into one deduped record set.\r\n *\r\n * Iterates USAGE_READER_REGISTRY (optionally filtered by platform), loads and\r\n * runs each reader, and folds the results together. Per-reader robustness:\r\n * • a reader that throws (it shouldn't — readers are fail-open) is CAUGHT and\r\n * recorded as skipped, never aborting the whole scan;\r\n * • a \"synced\" reader that returns no rows is noted as skipped(\"requires sync\")\r\n * so the report can honestly explain why a cloud platform shows nothing;\r\n * • a reader that simply has no local data returns [] silently (no root).\r\n *\r\n * After collection: global cross-source dedupe, then a final sinceMs filter\r\n * (readers also honor sinceMs, but the scan re-filters as a backstop).\r\n */\r\n\r\nimport type { PlatformId } from \"../core/types.js\";\r\nimport type { UsageReader, UsageReaderFactory, UsageRecord } from \"./types.js\";\r\nimport { dedupe } from \"./aggregate.js\";\r\nimport { USAGE_READER_REGISTRY } from \"./registry.js\";\r\n\r\n/** A platform the scan could not (fully) read, with a human reason. */\r\nexport interface SkippedPlatform {\r\n platformId: PlatformId;\r\n reason: string;\r\n}\r\n\r\n/** Options controlling a scan. */\r\nexport interface ScanOptions {\r\n /** Lower-bound epoch ms; records older than this are dropped. */\r\n sinceMs?: number;\r\n /** Restrict to these platforms; omit/empty to scan every registered reader. */\r\n platforms?: PlatformId[];\r\n}\r\n\r\n/** The result of a scan: deduped records + notes on anything skipped. */\r\nexport interface ScanResult {\r\n records: UsageRecord[];\r\n skipped: SkippedPlatform[];\r\n}\r\n\r\n/**\r\n * Scan host usage across the selected readers. Returns deduped, since-filtered\r\n * records plus a `skipped` list (load/read failures and synced-but-uncached\r\n * platforms). Never throws — a single bad reader is isolated.\r\n */\r\nexport async function scanUsage(opts: ScanOptions = {}): Promise<ScanResult> {\r\n const { sinceMs, platforms } = opts;\r\n const wanted = platforms && platforms.length > 0 ? new Set(platforms) : undefined;\r\n\r\n const factories = USAGE_READER_REGISTRY.filter(\r\n (f) => wanted === undefined || wanted.has(f.platformId),\r\n );\r\n\r\n const collected: UsageRecord[] = [];\r\n const skipped: SkippedPlatform[] = [];\r\n\r\n // Run readers concurrently; each result is independent.\r\n const results = await Promise.all(\r\n factories.map(async (factory) => readOne(factory, sinceMs)),\r\n );\r\n\r\n for (const r of results) {\r\n if (r.skipped) skipped.push(r.skipped);\r\n // Loop-push, never push(...spread): a reader over a large host history can\r\n // return hundreds of thousands of records, and spreading them as call\r\n // arguments overflows the stack (RangeError) — hit on a real machine the\r\n // first time the claude-code reader saw a multi-week log directory.\r\n for (const rec of r.records) collected.push(rec);\r\n }\r\n\r\n const deduped = dedupe(collected);\r\n const filtered =\r\n sinceMs === undefined ? deduped : deduped.filter((rec) => rec.ts >= sinceMs);\r\n\r\n return { records: filtered, skipped };\r\n}\r\n\r\n/** Load + run a single reader, isolating any failure into a skip note. */\r\nasync function readOne(\r\n factory: UsageReaderFactory,\r\n sinceMs: number | undefined,\r\n): Promise<{ records: UsageRecord[]; skipped?: SkippedPlatform }> {\r\n let reader: UsageReader;\r\n try {\r\n reader = await factory.load();\r\n } catch (err) {\r\n return {\r\n records: [],\r\n skipped: { platformId: factory.platformId, reason: `failed to load reader: ${errMsg(err)}` },\r\n };\r\n }\r\n\r\n let records: UsageRecord[];\r\n try {\r\n records = await reader.read(sinceMs === undefined ? {} : { sinceMs });\r\n } catch (err) {\r\n return {\r\n records: [],\r\n skipped: { platformId: factory.platformId, reason: `read error: ${errMsg(err)}` },\r\n };\r\n }\r\n\r\n if (records.length === 0 && factory.kind === \"synced\") {\r\n return {\r\n records: [],\r\n skipped: {\r\n platformId: factory.platformId,\r\n reason: \"requires sync (no local cache found)\",\r\n },\r\n };\r\n }\r\n\r\n return { records };\r\n}\r\n\r\nfunction errMsg(err: unknown): string {\r\n return err instanceof Error ? err.message : String(err);\r\n}\r\n","/**\r\n * usage/leaderboard — the USER / HOST leaderboard, derived from the host usage\r\n * readers (src/usage/*).\r\n *\r\n * Where {@link import(\"../telemetry/leaderboard.js\")} answers \"which MCP server\r\n * costs the most\" from the bytes WE wrap, this answers \"which CLI / host spent\r\n * the most\" from each agent's OWN native session logs. Origin is always\r\n * `host-native`; it is NEVER summed with the serve-proxy MCP leaderboard (they\r\n * measure different things: server bytes vs whole-conversation usage).\r\n *\r\n * Thin wrapper over {@link scanUsage} + {@link aggregateBy}: the ranking is just\r\n * the platform (or model) group-by, which `aggregateBy` already sorts by total\r\n * tokens desc. A dedicated formatter notes synced / host-estimated platforms.\r\n */\r\n\r\nimport type { SkippedPlatform } from \"./scan.js\";\r\nimport type { PlatformId } from \"../core/types.js\";\r\nimport type { UsageRecord, UsageSummary } from \"./types.js\";\r\nimport { aggregateBy } from \"./aggregate.js\";\r\nimport { scanUsage } from \"./scan.js\";\r\n\r\n/** Options for the host leaderboard queries. */\r\nexport interface HostLeaderboardOptions {\r\n /** Lower-bound epoch ms (inclusive); forwarded to {@link scanUsage}. */\r\n sinceMs?: number;\r\n /** Restrict to these platforms; omit/empty to scan every registered reader. */\r\n platforms?: PlatformId[];\r\n /** Rank dimension. \"platform\" (default) is the headline host leaderboard. */\r\n by?: \"platform\" | \"model\";\r\n}\r\n\r\n/** The host leaderboard result: ranked rows plus any honest skip notes. */\r\nexport interface HostLeaderboardResult {\r\n /** The ranking dimension actually used. */\r\n by: \"platform\" | \"model\";\r\n /** Ranked summary rows (sorted by total tokens desc by aggregateBy). */\r\n rows: UsageSummary[];\r\n /** Platforms the scan could not (fully) read (synced-but-uncached, errors). */\r\n skipped: SkippedPlatform[];\r\n}\r\n\r\n/**\r\n * The USER / HOST leaderboard: scan every host reader, then rank by platform\r\n * (the default — \"which CLI/host spent the most\") or by model. Returns the\r\n * ranked rows and the scan's skip notes so the formatter can stay honest about\r\n * cloud platforms that require a sync we do not perform.\r\n */\r\nexport async function hostLeaderboard(\r\n opts: HostLeaderboardOptions = {},\r\n): Promise<HostLeaderboardResult> {\r\n const by = opts.by ?? \"platform\";\r\n const scanOpts: { sinceMs?: number; platforms?: PlatformId[] } = {};\r\n if (opts.sinceMs !== undefined) scanOpts.sinceMs = opts.sinceMs;\r\n if (opts.platforms !== undefined && opts.platforms.length > 0) {\r\n scanOpts.platforms = opts.platforms;\r\n }\r\n const { records, skipped } = await scanUsage(scanOpts);\r\n const rows = aggregateBy(records, by);\r\n return { by, rows, skipped };\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Formatting (mirrors usage/report.ts style: aligned table + honesty legend)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Compact integer formatting with thousands separators. */\r\nfunction fmtInt(n: number): string {\r\n return n.toLocaleString(\"en-US\");\r\n}\r\n\r\n/** Right-pad (left-aligned columns). */\r\nfunction padRight(s: string, width: number): string {\r\n return s.length >= width ? s : s + \" \".repeat(width - s.length);\r\n}\r\n\r\n/** Left-pad (right-aligned numeric columns). */\r\nfunction padLeft(s: string, width: number): string {\r\n return s.length >= width ? s : \" \".repeat(width - s.length) + s;\r\n}\r\n\r\n/**\r\n * Format the host leaderboard as an aligned, ranked table:\r\n * RANK | PLATFORM|MODEL | IN | OUT | CACHE_R | CACHE_W | REASON | TOTAL | SESS | CONF\r\n * with a TOTAL footer (worst confidence across rows), an estimate legend, and\r\n * skip notes for synced-but-uncached platforms.\r\n */\r\nexport function formatHostLeaderboard(\r\n result: HostLeaderboardResult,\r\n): string {\r\n const keyHeader = result.by === \"model\" ? \"MODEL\" : \"PLATFORM\";\r\n const headers = [\r\n \"RANK\",\r\n keyHeader,\r\n \"IN\",\r\n \"OUT\",\r\n \"CACHE_R\",\r\n \"CACHE_W\",\r\n \"REASON\",\r\n \"TOTAL\",\r\n \"SESS\",\r\n \"CONF\",\r\n ];\r\n\r\n // aggregateBy already sorts by total desc; re-sort defensively for stability.\r\n const sorted = [...result.rows].sort(\r\n (a, b) => b.total - a.total || b.lastTs - a.lastTs,\r\n );\r\n\r\n const dataRows = sorted.map((r, i) => [\r\n `${i + 1}`,\r\n r.key,\r\n fmtInt(r.tokens.input),\r\n fmtInt(r.tokens.output),\r\n fmtInt(r.tokens.cacheRead),\r\n fmtInt(r.tokens.cacheWrite),\r\n fmtInt(r.tokens.reasoning),\r\n fmtInt(r.total),\r\n fmtInt(r.sessions),\r\n r.confidence,\r\n ]);\r\n\r\n const totals = sorted.reduce(\r\n (acc, r) => {\r\n acc.input += r.tokens.input;\r\n acc.output += r.tokens.output;\r\n acc.cacheRead += r.tokens.cacheRead;\r\n acc.cacheWrite += r.tokens.cacheWrite;\r\n acc.reasoning += r.tokens.reasoning;\r\n acc.total += r.total;\r\n acc.sessions += r.sessions;\r\n return acc;\r\n },\r\n { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0, total: 0, sessions: 0 },\r\n );\r\n const totalRow = [\r\n \"\",\r\n \"TOTAL\",\r\n fmtInt(totals.input),\r\n fmtInt(totals.output),\r\n fmtInt(totals.cacheRead),\r\n fmtInt(totals.cacheWrite),\r\n fmtInt(totals.reasoning),\r\n fmtInt(totals.total),\r\n fmtInt(totals.sessions),\r\n \"\",\r\n ];\r\n\r\n const widths = headers.map((h, col) => {\r\n let w = h.length;\r\n for (const row of dataRows) w = Math.max(w, (row[col] ?? \"\").length);\r\n w = Math.max(w, (totalRow[col] ?? \"\").length);\r\n return w;\r\n });\r\n\r\n // Left-align RANK (0), the KEY column (1), and CONF (9); numbers right.\r\n const leftCols = new Set<number>([0, 1, 9]);\r\n const renderRow = (cells: string[]): string =>\r\n cells\r\n .map((c, col) =>\r\n leftCols.has(col) ? padRight(c, widths[col] ?? 0) : padLeft(c, widths[col] ?? 0),\r\n )\r\n .join(\" \")\r\n .trimEnd();\r\n\r\n const ruleWidth = widths.reduce((a, b) => a + b, 0) + 2 * (widths.length - 1);\r\n const rule = \"-\".repeat(ruleWidth);\r\n\r\n const lines: string[] = [];\r\n lines.push(renderRow(headers));\r\n lines.push(rule);\r\n if (dataRows.length === 0) {\r\n lines.push(\"(no host usage found)\");\r\n } else {\r\n for (const row of dataRows) lines.push(renderRow(row));\r\n }\r\n lines.push(rule);\r\n lines.push(renderRow(totalRow));\r\n\r\n // Honesty legend — estimated rows and skipped synced platforms.\r\n if (sorted.some((r) => r.confidence === \"host-estimated\")) {\r\n lines.push(\"\");\r\n lines.push(\r\n \"note: host-estimated rows are derived (e.g. char/4 or cost-only), \" +\r\n \"not exact host-reported token counts.\",\r\n );\r\n }\r\n if (result.skipped.length > 0) {\r\n lines.push(\"\");\r\n lines.push(\"skipped (synced/host-estimated platforms):\");\r\n for (const s of result.skipped) lines.push(` - ${s.platformId}: ${s.reason}`);\r\n }\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n"],"mappings":"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","names":[]}
@@ -340,4 +340,4 @@ export {
340
340
  dedupe,
341
341
  aggregateBy
342
342
  };
343
- //# sourceMappingURL=chunk-XD7MY6HB.js.map
343
+ //# sourceMappingURL=chunk-7PSNXUFK.js.map