svamp-cli 0.1.51 → 0.1.53

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 (319) hide show
  1. package/README.md +193 -0
  2. package/bin/svamp.mjs +16 -7
  3. package/dist/cli.mjs +19 -19
  4. package/dist/{commands-mw8HFkt6.mjs → commands-1tmye7o_.mjs} +3 -2
  5. package/dist/{commands-D1brd9fB.mjs → commands-BJdwoEe6.mjs} +7 -3
  6. package/dist/{commands-DHnFOhQC.mjs → commands-CI_BVphs.mjs} +7 -3
  7. package/dist/{commands-BOYo9cdy.mjs → commands-DcFOU9nW.mjs} +7 -3
  8. package/dist/commands-DwY2B7KW.mjs +562 -0
  9. package/dist/commands-VGt5ofDo.mjs +1755 -0
  10. package/dist/commands-ZuFXrcot.mjs +558 -0
  11. package/dist/index.mjs +1 -1
  12. package/dist/{package-BLDik3NY.mjs → package-D7EUXtnk.mjs} +1 -1
  13. package/dist/{package-BYUO-39f.mjs → package-DbeynOln.mjs} +1 -1
  14. package/dist/{run-6dwQnoBL.mjs → run-BYJX5syg.mjs} +1 -1
  15. package/dist/{run-C0ecvcRP.mjs → run-BnnUavlu.mjs} +131 -44
  16. package/dist/{run-3Et1ISd5.mjs → run-Bw6aGHLA.mjs} +1 -1
  17. package/dist/{run-CE4H8ZiN.mjs → run-CBhm4Jop.mjs} +219 -80
  18. package/dist/{run-BE_AIJ7z.mjs → run-DOwarObi.mjs} +1 -1
  19. package/dist/{run-Bc83CRUn.mjs → run-DxISzP_V.mjs} +38 -7
  20. package/dist/{run-4li60ojK.mjs → run-Y0b60UYS.mjs} +1 -1
  21. package/dist/{run-BdEHYoYE.mjs → run-eYBOEaWw.mjs} +20 -7
  22. package/dist/tunnel-C2_V6y3d.mjs +299 -0
  23. package/dist/tunnel-DhVAOdGd.mjs +299 -0
  24. package/package.json +1 -1
  25. package/dist/commands-4_MiOQVp.mjs +0 -1217
  26. package/dist/commands-8UAWGJrC.mjs +0 -1208
  27. package/dist/commands-8Wmq0uak.mjs +0 -1407
  28. package/dist/commands-8Xn02pQg.mjs +0 -1217
  29. package/dist/commands-9DPsh6ku.mjs +0 -1217
  30. package/dist/commands-9rMB13FP.mjs +0 -1214
  31. package/dist/commands-B-XaqFDB.mjs +0 -1407
  32. package/dist/commands-B53zuBHB.mjs +0 -1217
  33. package/dist/commands-B7yLt11i.mjs +0 -1217
  34. package/dist/commands-BDVAO_N2.mjs +0 -1217
  35. package/dist/commands-BD_NjWJL.mjs +0 -1217
  36. package/dist/commands-BEhSQqTp.mjs +0 -507
  37. package/dist/commands-BIFQZZGw.mjs +0 -1375
  38. package/dist/commands-BImRR1Wr.mjs +0 -1217
  39. package/dist/commands-BLmRIMdf.mjs +0 -1217
  40. package/dist/commands-BQ_347V_.mjs +0 -1374
  41. package/dist/commands-BTEmyf2m.mjs +0 -1407
  42. package/dist/commands-BVjcCbWS.mjs +0 -1375
  43. package/dist/commands-BVsLRttq.mjs +0 -1217
  44. package/dist/commands-BVuE0VQU.mjs +0 -507
  45. package/dist/commands-BY09VTpk.mjs +0 -1375
  46. package/dist/commands-Bcdp0X-o.mjs +0 -1217
  47. package/dist/commands-BdnG1cqQ.mjs +0 -1217
  48. package/dist/commands-BdvvRQIo.mjs +0 -1415
  49. package/dist/commands-Bgg_dvDw.mjs +0 -1683
  50. package/dist/commands-Bi0zYJvj.mjs +0 -1407
  51. package/dist/commands-BmirUCVt.mjs +0 -1208
  52. package/dist/commands-BpSUbvmr.mjs +0 -1217
  53. package/dist/commands-BuJ6xTfc.mjs +0 -1217
  54. package/dist/commands-BzbYPx0f.mjs +0 -1208
  55. package/dist/commands-C-RtFjJl.mjs +0 -1217
  56. package/dist/commands-C20_f6oo.mjs +0 -1217
  57. package/dist/commands-C6KDr9Yp.mjs +0 -1407
  58. package/dist/commands-C9TOoTCv.mjs +0 -1395
  59. package/dist/commands-C9TdN_El.mjs +0 -1683
  60. package/dist/commands-CFv6lO0D.mjs +0 -1217
  61. package/dist/commands-CJ2n5jS2.mjs +0 -1375
  62. package/dist/commands-CKEKQ_5B.mjs +0 -1217
  63. package/dist/commands-CQz67Rm1.mjs +0 -1395
  64. package/dist/commands-CRZbJjqN.mjs +0 -1375
  65. package/dist/commands-CToIvBFX.mjs +0 -1375
  66. package/dist/commands-CWsfciHn.mjs +0 -1217
  67. package/dist/commands-CYMSyqYC.mjs +0 -1395
  68. package/dist/commands-CZ7KPLLJ.mjs +0 -1217
  69. package/dist/commands-Cc73uUnP.mjs +0 -1375
  70. package/dist/commands-CdMsAD1-.mjs +0 -1217
  71. package/dist/commands-CdyCWC3y.mjs +0 -1395
  72. package/dist/commands-ClVCprrK.mjs +0 -1217
  73. package/dist/commands-Cnmf8znA.mjs +0 -1196
  74. package/dist/commands-CorUNLRF.mjs +0 -1375
  75. package/dist/commands-Cq0oj5_v.mjs +0 -1217
  76. package/dist/commands-CrdvbXPI.mjs +0 -1395
  77. package/dist/commands-Cw2Od6mc.mjs +0 -1683
  78. package/dist/commands-CwC2aVzu.mjs +0 -1217
  79. package/dist/commands-CxSCUJCB.mjs +0 -1217
  80. package/dist/commands-D-PTwdZz.mjs +0 -1217
  81. package/dist/commands-D-nIO_Sf.mjs +0 -1741
  82. package/dist/commands-D7-NHH5q.mjs +0 -1407
  83. package/dist/commands-D7kH-7Vn.mjs +0 -1217
  84. package/dist/commands-DBv6A3aJ.mjs +0 -507
  85. package/dist/commands-DD3HXakm.mjs +0 -1217
  86. package/dist/commands-DLoe6FyK.mjs +0 -1375
  87. package/dist/commands-DPbH8KF0.mjs +0 -1217
  88. package/dist/commands-DUAQ9MZM.mjs +0 -1217
  89. package/dist/commands-DVw-P6-0.mjs +0 -1407
  90. package/dist/commands-DVygnMsh.mjs +0 -1217
  91. package/dist/commands-DWira-Cz.mjs +0 -1741
  92. package/dist/commands-DYTdUlul.mjs +0 -1217
  93. package/dist/commands-DZfaDmsk.mjs +0 -1374
  94. package/dist/commands-Dd8cn8mW.mjs +0 -1217
  95. package/dist/commands-DsIoygTL.mjs +0 -1395
  96. package/dist/commands-Du-fdLLu.mjs +0 -969
  97. package/dist/commands-DuJGOq1y.mjs +0 -1217
  98. package/dist/commands-DwBr3sBn.mjs +0 -1217
  99. package/dist/commands-DwveR96q.mjs +0 -1683
  100. package/dist/commands-DypTF36z.mjs +0 -506
  101. package/dist/commands-EUMJqBCs.mjs +0 -1407
  102. package/dist/commands-HLu7P96l.mjs +0 -1214
  103. package/dist/commands-HrBaGV-C.mjs +0 -1683
  104. package/dist/commands-Jk5no-DX.mjs +0 -1407
  105. package/dist/commands-KH5dj9dv.mjs +0 -1214
  106. package/dist/commands-LaNHVHjc.mjs +0 -1217
  107. package/dist/commands-O1Q9g00y.mjs +0 -1395
  108. package/dist/commands-OwMfbBrU.mjs +0 -1395
  109. package/dist/commands-SQ0Wp_kD.mjs +0 -1217
  110. package/dist/commands-S_MFQ9n1.mjs +0 -354
  111. package/dist/commands-T3q8VKCY.mjs +0 -1407
  112. package/dist/commands-Ugz9TtRu.mjs +0 -1420
  113. package/dist/commands-Wng0OuNY.mjs +0 -1683
  114. package/dist/commands-YBW5jFpy.mjs +0 -1217
  115. package/dist/commands-Z-CbuF8E.mjs +0 -1217
  116. package/dist/commands-fSZOP80Z.mjs +0 -1217
  117. package/dist/commands-fqBuJe1b.mjs +0 -1217
  118. package/dist/commands-g-1n3_Rp.mjs +0 -1395
  119. package/dist/commands-mC0oe0lj.mjs +0 -1217
  120. package/dist/commands-od2hOku5.mjs +0 -1217
  121. package/dist/commands-otgzprjb.mjs +0 -1375
  122. package/dist/commands-rhHI6Wb2.mjs +0 -1420
  123. package/dist/commands-zGHnUXh5.mjs +0 -1217
  124. package/dist/package-BMCjXPI9.mjs +0 -58
  125. package/dist/package-BPMWPlS0.mjs +0 -57
  126. package/dist/package-BaGfG8vL.mjs +0 -58
  127. package/dist/package-BkBE6ZdN.mjs +0 -57
  128. package/dist/package-BufekbY1.mjs +0 -57
  129. package/dist/package-C1hpYMj4.mjs +0 -57
  130. package/dist/package-CmIBOZtY.mjs +0 -57
  131. package/dist/package-CmVt1kdw.mjs +0 -58
  132. package/dist/package-Cn6Ya4A0.mjs +0 -57
  133. package/dist/package-Csd530Ym.mjs +0 -57
  134. package/dist/package-D6mNQtUs.mjs +0 -57
  135. package/dist/package-DG0AkZdm.mjs +0 -58
  136. package/dist/package-DRO1LpXW.mjs +0 -58
  137. package/dist/package-Dav8qh6X.mjs +0 -57
  138. package/dist/package-DiA55dzE.mjs +0 -57
  139. package/dist/package-UwLIU765.mjs +0 -58
  140. package/dist/package-k3XsdP9k.mjs +0 -58
  141. package/dist/package-rasGC9_z.mjs +0 -58
  142. package/dist/run-4B1XZQB8.mjs +0 -5426
  143. package/dist/run-4eArMb_9.mjs +0 -1050
  144. package/dist/run-67wfoMuo.mjs +0 -5383
  145. package/dist/run-6N2IdEX7.mjs +0 -5410
  146. package/dist/run-7iQKryzo.mjs +0 -5383
  147. package/dist/run-7s8lOXqB.mjs +0 -1050
  148. package/dist/run-8kKykzTs.mjs +0 -5367
  149. package/dist/run-8mLZV2lg.mjs +0 -1050
  150. package/dist/run-9x7I9Ck-.mjs +0 -5264
  151. package/dist/run-B-PWtXF-.mjs +0 -5894
  152. package/dist/run-B1ivovUl.mjs +0 -5964
  153. package/dist/run-B1l9Ed8k.mjs +0 -5403
  154. package/dist/run-B2zRMxE0.mjs +0 -5508
  155. package/dist/run-B31biy0V.mjs +0 -1050
  156. package/dist/run-B5o5fMMd.mjs +0 -5369
  157. package/dist/run-B7V-xXM7.mjs +0 -5775
  158. package/dist/run-B9ND6srh.mjs +0 -6154
  159. package/dist/run-BG3279Kg.mjs +0 -1051
  160. package/dist/run-BHZNzX1F.mjs +0 -5235
  161. package/dist/run-BKdOv7gX.mjs +0 -1050
  162. package/dist/run-BLySdZ1K.mjs +0 -5251
  163. package/dist/run-BOVkQfM-.mjs +0 -1050
  164. package/dist/run-BQ0lIare.mjs +0 -1050
  165. package/dist/run-BREPr7Yc.mjs +0 -5508
  166. package/dist/run-BTwshVk1.mjs +0 -5728
  167. package/dist/run-BUL3eAqT.mjs +0 -1050
  168. package/dist/run-BVcPemGr.mjs +0 -5947
  169. package/dist/run-BWqEmIiz.mjs +0 -5964
  170. package/dist/run-BWsDPiNe.mjs +0 -1050
  171. package/dist/run-BX4iy6k8.mjs +0 -1050
  172. package/dist/run-BXYfq8mK.mjs +0 -5836
  173. package/dist/run-BY12Ataq.mjs +0 -5732
  174. package/dist/run-BYDOX4yk.mjs +0 -5402
  175. package/dist/run-Bd-t6s63.mjs +0 -5373
  176. package/dist/run-BenYqfwQ.mjs +0 -5273
  177. package/dist/run-BfF4bgA3.mjs +0 -5403
  178. package/dist/run-Bhh05yic.mjs +0 -5369
  179. package/dist/run-BicITYWX.mjs +0 -6138
  180. package/dist/run-BieEN0Pg.mjs +0 -5761
  181. package/dist/run-BjEQi6PN.mjs +0 -1050
  182. package/dist/run-BjZ6SyFy.mjs +0 -1051
  183. package/dist/run-Bl8OkKyC.mjs +0 -5969
  184. package/dist/run-BlEFlhfn.mjs +0 -5510
  185. package/dist/run-BmL1m0Bk.mjs +0 -1050
  186. package/dist/run-Bmx5wEBF.mjs +0 -1051
  187. package/dist/run-BnX5Rw8x.mjs +0 -5403
  188. package/dist/run-BpjmHeht.mjs +0 -1050
  189. package/dist/run-BxTdRjCG.mjs +0 -1051
  190. package/dist/run-ByOVDgvx.mjs +0 -6115
  191. package/dist/run-BzRP6Q5t.mjs +0 -1051
  192. package/dist/run-C0dyMP62.mjs +0 -1051
  193. package/dist/run-C1lS3SwN.mjs +0 -5733
  194. package/dist/run-C3PAp02X.mjs +0 -5252
  195. package/dist/run-C3eaYQub.mjs +0 -1050
  196. package/dist/run-C4pdX4sY.mjs +0 -1051
  197. package/dist/run-C676pHe-.mjs +0 -5423
  198. package/dist/run-C8GkzcfP.mjs +0 -1050
  199. package/dist/run-C9Hrqjy_.mjs +0 -1050
  200. package/dist/run-CC2C8P-U.mjs +0 -6031
  201. package/dist/run-CCcW4asS.mjs +0 -1050
  202. package/dist/run-CDBKhQ1Z.mjs +0 -1051
  203. package/dist/run-CEB6sYzn.mjs +0 -5962
  204. package/dist/run-CF6aXLmA.mjs +0 -5445
  205. package/dist/run-CHyN5U0t.mjs +0 -1050
  206. package/dist/run-CIFezmkC.mjs +0 -5949
  207. package/dist/run-CLA9zw7J.mjs +0 -5907
  208. package/dist/run-COWb9ovq.mjs +0 -1050
  209. package/dist/run-CSUAy5T5.mjs +0 -1051
  210. package/dist/run-CSk7i0Hq.mjs +0 -1050
  211. package/dist/run-CUtqSGWJ.mjs +0 -1050
  212. package/dist/run-CXrEt0TM.mjs +0 -5008
  213. package/dist/run-CY8Y0JPW.mjs +0 -5287
  214. package/dist/run-CZCKBcQ-.mjs +0 -5244
  215. package/dist/run-CZj0sRCs.mjs +0 -1050
  216. package/dist/run-C_1x2cNU.mjs +0 -5381
  217. package/dist/run-C_8iOjO1.mjs +0 -5892
  218. package/dist/run-C_KIew8H.mjs +0 -1051
  219. package/dist/run-CajRcN3C.mjs +0 -1050
  220. package/dist/run-CbzXO7fw.mjs +0 -1050
  221. package/dist/run-CcSr4x2f.mjs +0 -1050
  222. package/dist/run-CcYaXgCy.mjs +0 -6091
  223. package/dist/run-CdihMx0V.mjs +0 -1051
  224. package/dist/run-Cf2Dl_ck.mjs +0 -1051
  225. package/dist/run-CjH1H4vq.mjs +0 -1050
  226. package/dist/run-CkbDK6jA.mjs +0 -1051
  227. package/dist/run-Ckh6JE9F.mjs +0 -1050
  228. package/dist/run-Ckyg9-fm.mjs +0 -6079
  229. package/dist/run-CqL3ZWdr.mjs +0 -5381
  230. package/dist/run-Csj7sJAh.mjs +0 -1050
  231. package/dist/run-Ct--DWF1.mjs +0 -1051
  232. package/dist/run-CtJRxaFC.mjs +0 -1051
  233. package/dist/run-CuIMdkKF.mjs +0 -6099
  234. package/dist/run-CuckJGM-.mjs +0 -1050
  235. package/dist/run-CxGAa9MH.mjs +0 -1050
  236. package/dist/run-CyU4-O-e.mjs +0 -5411
  237. package/dist/run-CymDyu2b.mjs +0 -5389
  238. package/dist/run-CzIY4_RE.mjs +0 -6093
  239. package/dist/run-D0Ha4aWt.mjs +0 -1050
  240. package/dist/run-D0ow-xms.mjs +0 -5905
  241. package/dist/run-D1PFrNZB.mjs +0 -6273
  242. package/dist/run-D2X3jEqg.mjs +0 -1051
  243. package/dist/run-D39C7Ta3.mjs +0 -1050
  244. package/dist/run-D3Lqxasl.mjs +0 -1051
  245. package/dist/run-D3bhRCCb.mjs +0 -1051
  246. package/dist/run-D5N42sVA.mjs +0 -1050
  247. package/dist/run-D691XPXy.mjs +0 -6031
  248. package/dist/run-D7dLDpq3.mjs +0 -5403
  249. package/dist/run-D8mQ_fL5.mjs +0 -1050
  250. package/dist/run-DA-YBjNw.mjs +0 -6018
  251. package/dist/run-DByI8mI0.mjs +0 -1051
  252. package/dist/run-DCINWip4.mjs +0 -1050
  253. package/dist/run-DCrZ3vke.mjs +0 -5406
  254. package/dist/run-DDF-tRbn.mjs +0 -5954
  255. package/dist/run-DGSgljJE.mjs +0 -5421
  256. package/dist/run-DGsXW19O.mjs +0 -5541
  257. package/dist/run-DHrF2xpW.mjs +0 -5776
  258. package/dist/run-DIB0W42M.mjs +0 -1050
  259. package/dist/run-DJ4k0WzZ.mjs +0 -1051
  260. package/dist/run-DMI83W7i.mjs +0 -5434
  261. package/dist/run-DNX3djCI.mjs +0 -1050
  262. package/dist/run-DOPaGRT2.mjs +0 -6027
  263. package/dist/run-DP7KSZqR.mjs +0 -1051
  264. package/dist/run-DQ0yljWr.mjs +0 -1050
  265. package/dist/run-DTkldU6a.mjs +0 -1050
  266. package/dist/run-DU10B3gK.mjs +0 -5728
  267. package/dist/run-DV86VJNG.mjs +0 -5386
  268. package/dist/run-DVZGKdKO.mjs +0 -1050
  269. package/dist/run-DWdtp6VD.mjs +0 -6136
  270. package/dist/run-DWzA1gZ-.mjs +0 -1050
  271. package/dist/run-DXJ2M19k.mjs +0 -1050
  272. package/dist/run-DZOeccNu.mjs +0 -5484
  273. package/dist/run-D_W5YF0D.mjs +0 -6046
  274. package/dist/run-DaReJPf8.mjs +0 -1051
  275. package/dist/run-DaYrEeQ9.mjs +0 -5400
  276. package/dist/run-DbC9-WM4.mjs +0 -1050
  277. package/dist/run-Dd9XkswU.mjs +0 -1051
  278. package/dist/run-De-wkVl3.mjs +0 -5487
  279. package/dist/run-DfU2luyX.mjs +0 -1050
  280. package/dist/run-Dfl3Ze2L.mjs +0 -5541
  281. package/dist/run-DfuHUDIJ.mjs +0 -1051
  282. package/dist/run-DfwfyFqj.mjs +0 -5975
  283. package/dist/run-DgUDGHZy.mjs +0 -1051
  284. package/dist/run-Dge2K7h1.mjs +0 -1050
  285. package/dist/run-Di3I0USw.mjs +0 -1050
  286. package/dist/run-Dm3U4FB5.mjs +0 -6018
  287. package/dist/run-Du0YOs48.mjs +0 -5446
  288. package/dist/run-DuaIQAE4.mjs +0 -5392
  289. package/dist/run-Dwm19YhI.mjs +0 -1050
  290. package/dist/run-DxM7xaBa.mjs +0 -1050
  291. package/dist/run-DysN-cGm.mjs +0 -1050
  292. package/dist/run-DzXohf8-.mjs +0 -1051
  293. package/dist/run-E_MwVOtN.mjs +0 -5272
  294. package/dist/run-FPoL2-FD.mjs +0 -5381
  295. package/dist/run-HU4XjZfs.mjs +0 -6023
  296. package/dist/run-HhiYlJuS.mjs +0 -5414
  297. package/dist/run-JXLlRLFb.mjs +0 -1050
  298. package/dist/run-K-_jahIg.mjs +0 -1051
  299. package/dist/run-K_S7pfZ-.mjs +0 -1050
  300. package/dist/run-LDiT4WF-.mjs +0 -1050
  301. package/dist/run-NToLJWx-.mjs +0 -5442
  302. package/dist/run-RBufRqbs.mjs +0 -1050
  303. package/dist/run-YFYpyThQ.mjs +0 -1051
  304. package/dist/run-YG1Pb9dY.mjs +0 -5385
  305. package/dist/run-ZDa17iLg.mjs +0 -6060
  306. package/dist/run-ZN0qMdS_.mjs +0 -1051
  307. package/dist/run-azpFWM6w.mjs +0 -1050
  308. package/dist/run-coIDvBK_.mjs +0 -6127
  309. package/dist/run-jLp4pbTE.mjs +0 -1050
  310. package/dist/run-m3oAuSg0.mjs +0 -1050
  311. package/dist/run-r9CAcL_U.mjs +0 -5403
  312. package/dist/run-v32uF2bP.mjs +0 -5378
  313. package/dist/run-vTsskoZc.mjs +0 -5340
  314. package/dist/run-vt26p5D7.mjs +0 -1050
  315. package/dist/run-vvQiCHpi.mjs +0 -5427
  316. package/dist/run-w-HVv5py.mjs +0 -5410
  317. package/dist/run-wpUutZ9C.mjs +0 -1051
  318. package/dist/run-yTjJ7noq.mjs +0 -1051
  319. package/dist/run-zo5GSoVC.mjs +0 -1050
@@ -1,1217 +0,0 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
- import { resolve, join } from 'node:path';
3
- import os from 'node:os';
4
- import { l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha } from './run-vvQiCHpi.mjs';
5
- import 'os';
6
- import 'fs/promises';
7
- import 'fs';
8
- import 'path';
9
- import 'url';
10
- import 'child_process';
11
- import 'crypto';
12
- import 'node:crypto';
13
- import 'node:child_process';
14
- import '@agentclientprotocol/sdk';
15
- import '@modelcontextprotocol/sdk/client/index.js';
16
- import '@modelcontextprotocol/sdk/client/stdio.js';
17
- import '@modelcontextprotocol/sdk/types.js';
18
- import 'zod';
19
- import 'node:fs/promises';
20
- import 'node:util';
21
-
22
- function toMarkdownInline(value) {
23
- const escaped = value.replace(/`/g, "\\`");
24
- return `\`${escaped}\``;
25
- }
26
- function formatSessionStatus(data) {
27
- const lines = [
28
- "## Session Status",
29
- "",
30
- `- Session ID: ${toMarkdownInline(data.sessionId)}`,
31
- `- Agent: ${data.flavor}`
32
- ];
33
- if (data.name) lines.push(`- Name: ${data.name}`);
34
- if (data.summary) lines.push(`- Summary: ${data.summary}`);
35
- if (data.path) lines.push(`- Path: ${data.path}`);
36
- if (data.host) lines.push(`- Host: ${data.host}`);
37
- if (data.lifecycleState) lines.push(`- Lifecycle: ${data.lifecycleState}`);
38
- lines.push(`- Active: ${data.active ? "yes" : "no"}`);
39
- lines.push(`- Thinking: ${data.thinking ? "yes" : "no"}`);
40
- lines.push(`- Agent Status: ${data.active ? "busy" : "idle"}`);
41
- if (data.startedBy) lines.push(`- Started By: ${data.startedBy}`);
42
- if (data.claudeSessionId) lines.push(`- Claude Session: ${data.claudeSessionId}`);
43
- if (data.sessionLink) lines.push(`- Link: ${data.sessionLink}`);
44
- return lines.join("\n");
45
- }
46
- function formatJson(data) {
47
- return JSON.stringify(data, null, 2);
48
- }
49
-
50
- const SVAMP_HOME = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
51
- const DAEMON_STATE_FILE = join(SVAMP_HOME, "daemon.state.json");
52
- const ENV_FILE = join(SVAMP_HOME, ".env");
53
- function loadDotEnv() {
54
- if (!existsSync(ENV_FILE)) return;
55
- const lines = readFileSync(ENV_FILE, "utf-8").split("\n");
56
- for (const line of lines) {
57
- const trimmed = line.trim();
58
- if (!trimmed || trimmed.startsWith("#")) continue;
59
- const eqIdx = trimmed.indexOf("=");
60
- if (eqIdx === -1) continue;
61
- const key = trimmed.slice(0, eqIdx).trim();
62
- const value = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
63
- if (!process.env[key]) {
64
- process.env[key] = value;
65
- }
66
- }
67
- }
68
- function readDaemonState() {
69
- if (!existsSync(DAEMON_STATE_FILE)) return null;
70
- try {
71
- return JSON.parse(readFileSync(DAEMON_STATE_FILE, "utf-8"));
72
- } catch {
73
- return null;
74
- }
75
- }
76
- function isDaemonAlive(state) {
77
- try {
78
- process.kill(state.pid, 0);
79
- return true;
80
- } catch {
81
- return false;
82
- }
83
- }
84
- async function connectAndGetMachine(machineId) {
85
- loadDotEnv();
86
- const state = readDaemonState();
87
- if (!state || !isDaemonAlive(state)) {
88
- console.error('Daemon is not running. Start it with "svamp daemon start".');
89
- process.exit(1);
90
- }
91
- const serverUrl = process.env.HYPHA_SERVER_URL || state.hyphaServerUrl;
92
- const token = process.env.HYPHA_TOKEN;
93
- if (!serverUrl) {
94
- console.error('No Hypha server URL. Run "svamp login <url>" first.');
95
- process.exit(1);
96
- }
97
- const origLog = console.log;
98
- const origWarn = console.warn;
99
- const origInfo = console.info;
100
- const origError = console.error;
101
- const stdoutWrite = process.stdout.write.bind(process.stdout);
102
- const stderrWrite = process.stderr.write.bind(process.stderr);
103
- const isHyphaLog = (chunk) => typeof chunk === "string" && (chunk.includes("WebSocket connection") || chunk.includes("Connection established") || chunk.includes("registering service built-in") || chunk.includes("registered service") || chunk.includes("registered all") || chunk.includes("Subscribing to client_") || chunk.includes("subscribed to client_") || chunk.includes("subscribe to client_") || chunk.includes("Cleaning up all sessions") || chunk.includes("WebSocket connection disconnected") || chunk.includes("local RPC disconnection") || chunk.includes("Timeout registering service") || chunk.includes("Failed to subscribe to client_disconnected") || chunk.includes("Timeout subscribing to client_disconnected"));
104
- console.log = () => {
105
- };
106
- console.warn = () => {
107
- };
108
- console.info = () => {
109
- };
110
- console.error = (...args) => {
111
- if (args.some((a) => isHyphaLog(a))) return;
112
- origError(...args);
113
- };
114
- process.stdout.write = (chunk, ...args) => {
115
- if (isHyphaLog(chunk)) return true;
116
- return stdoutWrite(chunk, ...args);
117
- };
118
- process.stderr.write = (chunk, ...args) => {
119
- if (isHyphaLog(chunk)) return true;
120
- return stderrWrite(chunk, ...args);
121
- };
122
- const restoreConsole = () => {
123
- console.log = origLog;
124
- console.warn = origWarn;
125
- console.info = origInfo;
126
- console.error = origError;
127
- };
128
- let server;
129
- try {
130
- server = await connectToHypha({
131
- serverUrl,
132
- token,
133
- name: "svamp-session-cli"
134
- });
135
- } catch (err) {
136
- restoreConsole();
137
- console.error(`Failed to connect to Hypha: ${err.message}`);
138
- process.exit(1);
139
- }
140
- let machine;
141
- try {
142
- const services = await server.listServices({ type: "svamp-machine" });
143
- if (services.length === 0) {
144
- restoreConsole();
145
- console.error("No machine service found. Is the daemon registered on Hypha?");
146
- await server.disconnect();
147
- process.exit(1);
148
- }
149
- let selectedService;
150
- if (machineId) {
151
- const exact = services.find((s) => (s.id || s.name) === machineId);
152
- if (exact) {
153
- selectedService = exact;
154
- } else {
155
- const prefixMatches = services.filter((s) => {
156
- const id = s.id || s.name;
157
- return id.startsWith(machineId);
158
- });
159
- if (prefixMatches.length === 1) {
160
- selectedService = prefixMatches[0];
161
- } else if (prefixMatches.length === 0) {
162
- const substringMatches = services.filter((s) => {
163
- const id = s.id || s.name || "";
164
- return id.includes(machineId);
165
- });
166
- if (substringMatches.length === 1) {
167
- selectedService = substringMatches[0];
168
- } else {
169
- restoreConsole();
170
- console.error(`No machine found matching: ${machineId}`);
171
- console.error("Available machines:");
172
- for (const s of services) {
173
- console.error(` ${s.id || s.name}`);
174
- }
175
- await server.disconnect();
176
- process.exit(1);
177
- }
178
- } else {
179
- restoreConsole();
180
- console.error(`Ambiguous machine ID "${machineId}". Matches:`);
181
- for (const s of prefixMatches) {
182
- console.error(` ${s.id || s.name}`);
183
- }
184
- await server.disconnect();
185
- process.exit(1);
186
- }
187
- }
188
- } else {
189
- if (state.hyphaClientId) {
190
- const localMatch = services.find((s) => {
191
- const id = s.id || s.name || "";
192
- return id.includes(state.hyphaClientId);
193
- });
194
- selectedService = localMatch || services[0];
195
- } else if (state.machineId) {
196
- const localMatch = services.find((s) => {
197
- const id = s.id || s.name || "";
198
- return id.includes(state.machineId);
199
- });
200
- selectedService = localMatch || services[0];
201
- } else {
202
- selectedService = services[0];
203
- }
204
- }
205
- const svcId = selectedService.id || selectedService.name;
206
- machine = await server.getService(svcId);
207
- } catch (err) {
208
- restoreConsole();
209
- console.error(`Failed to discover machine service: ${err.message}`);
210
- await server.disconnect();
211
- process.exit(1);
212
- }
213
- restoreConsole();
214
- return { server, machine };
215
- }
216
- async function connectAndGetAllMachines() {
217
- loadDotEnv();
218
- const state = readDaemonState();
219
- if (!state || !isDaemonAlive(state)) {
220
- console.error('Daemon is not running. Start it with "svamp daemon start".');
221
- process.exit(1);
222
- }
223
- const serverUrl = process.env.HYPHA_SERVER_URL || state.hyphaServerUrl;
224
- const token = process.env.HYPHA_TOKEN;
225
- if (!serverUrl) {
226
- console.error('No Hypha server URL. Run "svamp login <url>" first.');
227
- process.exit(1);
228
- }
229
- const origLog = console.log;
230
- const origWarn = console.warn;
231
- const origInfo = console.info;
232
- const origError = console.error;
233
- const stdoutWrite = process.stdout.write.bind(process.stdout);
234
- const stderrWrite = process.stderr.write.bind(process.stderr);
235
- const isHyphaLog = (chunk) => typeof chunk === "string" && (chunk.includes("WebSocket connection") || chunk.includes("Connection established") || chunk.includes("registering service built-in") || chunk.includes("registered service") || chunk.includes("registered all") || chunk.includes("Subscribing to client_") || chunk.includes("subscribed to client_") || chunk.includes("subscribe to client_") || chunk.includes("Cleaning up all sessions") || chunk.includes("WebSocket connection disconnected") || chunk.includes("local RPC disconnection") || chunk.includes("Timeout registering service") || chunk.includes("Failed to subscribe to client_disconnected") || chunk.includes("Timeout subscribing to client_disconnected"));
236
- console.log = () => {
237
- };
238
- console.warn = () => {
239
- };
240
- console.info = () => {
241
- };
242
- console.error = (...args) => {
243
- if (!args.some((a) => isHyphaLog(a))) origError(...args);
244
- };
245
- process.stdout.write = (chunk, ...args) => {
246
- if (isHyphaLog(chunk)) return true;
247
- return stdoutWrite(chunk, ...args);
248
- };
249
- process.stderr.write = (chunk, ...args) => {
250
- if (isHyphaLog(chunk)) return true;
251
- return stderrWrite(chunk, ...args);
252
- };
253
- const restoreConsole = () => {
254
- console.log = origLog;
255
- console.warn = origWarn;
256
- console.info = origInfo;
257
- console.error = origError;
258
- };
259
- let server;
260
- try {
261
- server = await connectToHypha({ serverUrl, token, name: "svamp-session-cli" });
262
- } catch (err) {
263
- restoreConsole();
264
- console.error(`Failed to connect to Hypha: ${err.message}`);
265
- process.exit(1);
266
- }
267
- const machines = [];
268
- try {
269
- const services = await server.listServices({ type: "svamp-machine" });
270
- for (const svc of services) {
271
- try {
272
- const svcId = svc.id || svc.name;
273
- machines.push(await server.getService(svcId));
274
- } catch {
275
- }
276
- }
277
- } catch (err) {
278
- restoreConsole();
279
- console.error(`Failed to discover machine services: ${err.message}`);
280
- await server.disconnect();
281
- process.exit(1);
282
- }
283
- restoreConsole();
284
- if (machines.length === 0) {
285
- console.error("No machine service found. Is the daemon registered on Hypha?");
286
- await server.disconnect();
287
- process.exit(1);
288
- }
289
- return { server, machines };
290
- }
291
- async function sessionMachines() {
292
- loadDotEnv();
293
- const state = readDaemonState();
294
- if (!state || !isDaemonAlive(state)) {
295
- console.error('Daemon is not running. Start it with "svamp daemon start".');
296
- process.exit(1);
297
- }
298
- const serverUrl = process.env.HYPHA_SERVER_URL || state.hyphaServerUrl;
299
- const token = process.env.HYPHA_TOKEN;
300
- if (!serverUrl) {
301
- console.error('No Hypha server URL. Run "svamp login <url>" first.');
302
- process.exit(1);
303
- }
304
- const origLog = console.log;
305
- const origWarn = console.warn;
306
- const origInfo = console.info;
307
- const origError = console.error;
308
- const stdoutWrite = process.stdout.write.bind(process.stdout);
309
- const stderrWrite = process.stderr.write.bind(process.stderr);
310
- const isHyphaLog = (chunk) => typeof chunk === "string" && (chunk.includes("WebSocket connection") || chunk.includes("Connection established") || chunk.includes("registering service built-in") || chunk.includes("registered service") || chunk.includes("registered all") || chunk.includes("Subscribing to client_") || chunk.includes("subscribed to client_") || chunk.includes("subscribe to client_") || chunk.includes("Cleaning up all sessions") || chunk.includes("WebSocket connection disconnected") || chunk.includes("local RPC disconnection") || chunk.includes("Timeout registering service") || chunk.includes("Failed to subscribe to client_disconnected") || chunk.includes("Timeout subscribing to client_disconnected"));
311
- console.log = () => {
312
- };
313
- console.warn = () => {
314
- };
315
- console.info = () => {
316
- };
317
- console.error = (...args) => {
318
- if (args.some((a) => isHyphaLog(a))) return;
319
- origError(...args);
320
- };
321
- process.stdout.write = (chunk, ...args) => {
322
- if (isHyphaLog(chunk)) return true;
323
- return stdoutWrite(chunk, ...args);
324
- };
325
- process.stderr.write = (chunk, ...args) => {
326
- if (isHyphaLog(chunk)) return true;
327
- return stderrWrite(chunk, ...args);
328
- };
329
- const restoreConsole = () => {
330
- console.log = origLog;
331
- console.warn = origWarn;
332
- console.info = origInfo;
333
- console.error = origError;
334
- };
335
- let server;
336
- try {
337
- server = await connectToHypha({
338
- serverUrl,
339
- token,
340
- name: "svamp-session-cli"
341
- });
342
- } catch (err) {
343
- restoreConsole();
344
- console.error(`Failed to connect to Hypha: ${err.message}`);
345
- process.exit(1);
346
- }
347
- try {
348
- const services = await server.listServices({ type: "svamp-machine" });
349
- restoreConsole();
350
- if (services.length === 0) {
351
- console.log("No machines found.");
352
- return;
353
- }
354
- const machines = [];
355
- for (const svc of services) {
356
- const svcId = svc.id || svc.name;
357
- try {
358
- const machineSvc = await server.getService(svcId);
359
- const info = await machineSvc.getMachineInfo();
360
- const sessions = await machineSvc.listSessions();
361
- machines.push({
362
- serviceId: svcId,
363
- machineId: info.machineId || svcId,
364
- displayName: info.metadata?.displayName || info.metadata?.host || "-",
365
- platform: info.metadata?.platform || "-",
366
- host: info.metadata?.host || "-",
367
- sessions: sessions.length,
368
- status: info.daemonState?.status || "unknown"
369
- });
370
- } catch {
371
- machines.push({
372
- serviceId: svcId,
373
- machineId: svcId,
374
- displayName: "-",
375
- platform: "-",
376
- host: "-",
377
- sessions: -1,
378
- status: "unreachable"
379
- });
380
- }
381
- }
382
- const header = `${"MACHINE ID".padEnd(20)} ${"NAME".padEnd(20)} ${"PLATFORM".padEnd(12)} ${"HOST".padEnd(25)} ${"SESSIONS".padEnd(10)} ${"STATUS"}`;
383
- console.log(header);
384
- console.log("-".repeat(header.length));
385
- for (const m of machines) {
386
- const id = truncate(m.machineId, 18).padEnd(20);
387
- const name = truncate(m.displayName, 18).padEnd(20);
388
- const platform = m.platform.padEnd(12);
389
- const host = truncate(m.host, 23).padEnd(25);
390
- const sessions = m.sessions >= 0 ? String(m.sessions).padEnd(10) : "-".padEnd(10);
391
- const status = m.status === "running" ? `\x1B[32m${m.status}\x1B[0m` : m.status === "unreachable" ? `\x1B[31m${m.status}\x1B[0m` : m.status;
392
- console.log(`${id} ${name} ${platform} ${host} ${sessions} ${status}`);
393
- }
394
- console.log(`
395
- ${machines.length} machine(s) found.`);
396
- console.log("Use --machine <id> to target a specific machine.");
397
- } finally {
398
- await server.disconnect();
399
- }
400
- }
401
- function resolveSessionId(sessions, partial) {
402
- const exact = sessions.find((s) => s.sessionId === partial);
403
- if (exact) return exact;
404
- const matches = sessions.filter((s) => s.sessionId.startsWith(partial));
405
- if (matches.length === 1) return matches[0];
406
- if (matches.length === 0) {
407
- console.error(`No session found matching: ${partial}`);
408
- console.error('Run "svamp session list" to see active sessions.');
409
- process.exit(1);
410
- }
411
- console.error(`Ambiguous session ID "${partial}". Matches:`);
412
- for (const s of matches) {
413
- console.error(` ${s.sessionId}`);
414
- }
415
- process.exit(1);
416
- }
417
- function truncate(str, max) {
418
- if (str.length <= max) return str;
419
- return "..." + str.slice(str.length - max + 3);
420
- }
421
- function renderMessage(msg) {
422
- const content = msg.content;
423
- if (!content) return;
424
- const role = content.role;
425
- if (role === "user") {
426
- const data = content.content;
427
- let text;
428
- if (typeof data === "string") {
429
- try {
430
- const parsed = JSON.parse(data);
431
- text = parsed?.text || parsed?.content?.text || data;
432
- } catch {
433
- text = data;
434
- }
435
- } else if (data?.text) {
436
- text = data.text;
437
- } else if (data?.type === "text") {
438
- text = data.text || "";
439
- } else {
440
- text = typeof data === "object" ? JSON.stringify(data) : String(data || "");
441
- }
442
- console.log(`\x1B[36m[user]\x1B[0m ${text}`);
443
- } else if (role === "agent" || role === "assistant") {
444
- const data = content.content?.data || content.content;
445
- if (!data) return;
446
- if (data.type === "assistant" && Array.isArray(data.content)) {
447
- for (const block of data.content) {
448
- if (block.type === "text" && block.text) {
449
- process.stdout.write(block.text);
450
- if (!block.text.endsWith("\n")) process.stdout.write("\n");
451
- } else if (block.type === "tool_use") {
452
- const argsStr = JSON.stringify(block.input || {}).slice(0, 120);
453
- console.log(`\x1B[33m[tool]\x1B[0m ${block.name}(${argsStr})`);
454
- } else if (block.type === "tool_result") {
455
- const resultStr = typeof block.content === "string" ? block.content : JSON.stringify(block.content || "");
456
- console.log(`\x1B[90m[result]\x1B[0m ${resultStr.slice(0, 200)}${resultStr.length > 200 ? "..." : ""}`);
457
- } else if (block.type === "thinking") {
458
- const text = block.thinking || block.text || "";
459
- if (text) console.log(`\x1B[90m[thinking] ${text.slice(0, 200)}\x1B[0m`);
460
- }
461
- }
462
- } else if (data.type === "result") {
463
- if (data.result) console.log(`\x1B[32m[done]\x1B[0m ${data.result}`);
464
- } else if (data.type === "output") {
465
- const inner = data.data;
466
- if (inner?.type === "assistant" && Array.isArray(inner.content)) {
467
- for (const block of inner.content) {
468
- if (block.type === "text" && block.text) {
469
- process.stdout.write(block.text);
470
- if (!block.text.endsWith("\n")) process.stdout.write("\n");
471
- } else if (block.type === "tool_use") {
472
- const argsStr = JSON.stringify(block.input || {}).slice(0, 120);
473
- console.log(`\x1B[33m[tool]\x1B[0m ${block.name}(${argsStr})`);
474
- } else if (block.type === "tool_result") {
475
- const resultStr = typeof block.content === "string" ? block.content : JSON.stringify(block.content || "");
476
- console.log(`\x1B[90m[result]\x1B[0m ${resultStr.slice(0, 200)}${resultStr.length > 200 ? "..." : ""}`);
477
- }
478
- }
479
- } else if (inner?.type === "result") {
480
- if (inner.result) console.log(`\x1B[32m[done]\x1B[0m ${inner.result}`);
481
- }
482
- }
483
- } else if (role === "session") {
484
- const data = content.content?.data;
485
- if (data?.type === "system" && data?.subtype === "init") {
486
- console.log(`\x1B[90m[session init]\x1B[0m`);
487
- }
488
- }
489
- }
490
- function extractMessageText(msg) {
491
- const content = msg.content;
492
- if (!content) return null;
493
- const role = content.role || "unknown";
494
- let text = "";
495
- if (role === "user") {
496
- const data = content.content;
497
- if (typeof data === "string") {
498
- try {
499
- const parsed = JSON.parse(data);
500
- text = parsed?.text || parsed?.content?.text || data;
501
- } catch {
502
- text = data;
503
- }
504
- } else if (data?.text) {
505
- text = data.text;
506
- } else if (data?.type === "text") {
507
- text = data.text || "";
508
- } else {
509
- text = typeof data === "object" ? JSON.stringify(data) : String(data || "");
510
- }
511
- } else if (role === "agent" || role === "assistant") {
512
- const data = content.content?.data || content.content;
513
- if (!data) return null;
514
- if (data.type === "assistant" && Array.isArray(data.content)) {
515
- const parts = [];
516
- for (const block of data.content) {
517
- if (block.type === "text" && block.text) {
518
- parts.push(block.text);
519
- } else if (block.type === "tool_use") {
520
- parts.push(`[tool: ${block.name}]`);
521
- }
522
- }
523
- text = parts.join("\n");
524
- } else if (data.type === "result") {
525
- text = data.result || "";
526
- } else if (data.type === "output") {
527
- const inner = data.data;
528
- if (inner?.type === "assistant" && Array.isArray(inner.content)) {
529
- const parts = [];
530
- for (const block of inner.content) {
531
- if (block.type === "text" && block.text) {
532
- parts.push(block.text);
533
- } else if (block.type === "tool_use") {
534
- parts.push(`[tool: ${block.name}]`);
535
- }
536
- }
537
- text = parts.join("\n");
538
- } else if (inner?.type === "result") {
539
- text = inner.result || "";
540
- }
541
- }
542
- } else if (role === "session") {
543
- text = "[session event]";
544
- }
545
- return {
546
- id: msg.id || "",
547
- seq: msg.seq || 0,
548
- role,
549
- text,
550
- createdAt: msg.createdAt || 0
551
- };
552
- }
553
- async function waitForIdle(server, sessionId, timeoutMs) {
554
- const svc = await server.getService(`svamp-session-${sessionId}`);
555
- const pollInterval = 2e3;
556
- const deadline = Date.now() + timeoutMs;
557
- while (Date.now() < deadline) {
558
- const activity = await svc.getActivityState();
559
- if (activity && !activity.thinking) {
560
- return;
561
- }
562
- await new Promise((r) => setTimeout(r, pollInterval));
563
- }
564
- throw new Error("Timeout waiting for agent to become idle");
565
- }
566
- async function waitForBusyThenIdle(server, sessionId, timeoutMs = 3e5, busyTimeoutMs = 1e4) {
567
- const svc = await server.getService(`svamp-session-${sessionId}`);
568
- const pollInterval = 2e3;
569
- const deadline = Date.now() + timeoutMs;
570
- const busyDeadline = Date.now() + busyTimeoutMs;
571
- let sawBusy = false;
572
- while (Date.now() < deadline) {
573
- const activity = await svc.getActivityState();
574
- const isBusy = activity?.thinking === true;
575
- if (isBusy) {
576
- sawBusy = true;
577
- }
578
- if (activity && !activity.active) {
579
- return;
580
- }
581
- if (sawBusy && !isBusy) {
582
- return;
583
- }
584
- if (!sawBusy && Date.now() > busyDeadline) {
585
- return;
586
- }
587
- await new Promise((r) => setTimeout(r, pollInterval));
588
- }
589
- throw new Error("Timeout waiting for agent to become idle");
590
- }
591
- async function sessionList(machineId, opts) {
592
- if (machineId) {
593
- const { server, machine } = await connectAndGetMachine(machineId);
594
- try {
595
- await listSessionsFromMachines(server, [machine], opts);
596
- } finally {
597
- await server.disconnect();
598
- }
599
- } else {
600
- const { server, machines } = await connectAndGetAllMachines();
601
- try {
602
- await listSessionsFromMachines(server, machines, opts);
603
- } finally {
604
- await server.disconnect();
605
- }
606
- }
607
- }
608
- async function listSessionsFromMachines(server, machines, opts) {
609
- const allSessions = [];
610
- for (const machine of machines) {
611
- try {
612
- const info = await machine.getMachineInfo();
613
- const sessions = await machine.listSessions();
614
- for (const s of sessions) {
615
- s.machineHost = info.metadata?.displayName || info.metadata?.host || info.machineId;
616
- }
617
- allSessions.push(...sessions);
618
- } catch {
619
- }
620
- }
621
- const filtered = opts?.active ? allSessions.filter((s) => s.active) : allSessions;
622
- if (filtered.length === 0) {
623
- if (opts?.json) {
624
- console.log(formatJson([]));
625
- } else {
626
- console.log("No active sessions.");
627
- }
628
- return;
629
- }
630
- const enriched = [];
631
- for (const s of filtered) {
632
- let flavor = "claude";
633
- let name = "";
634
- let path = s.directory || "";
635
- let host = s.machineHost || "";
636
- if (s.metadata) {
637
- flavor = s.metadata.flavor || "claude";
638
- name = s.metadata.name || "";
639
- }
640
- if (s.active) {
641
- try {
642
- const svc = await server.getService(`svamp-session-${s.sessionId}`);
643
- const { metadata } = await svc.getMetadata();
644
- flavor = metadata?.flavor || flavor;
645
- name = metadata?.name || name;
646
- path = metadata?.path || path;
647
- host = metadata?.host || host;
648
- } catch {
649
- }
650
- }
651
- enriched.push({ ...s, flavor, name, path, host });
652
- }
653
- if (opts?.json) {
654
- console.log(formatJson(enriched.map((s) => ({
655
- sessionId: s.sessionId,
656
- agent: s.flavor,
657
- name: s.name,
658
- path: s.path,
659
- host: s.host,
660
- active: s.active,
661
- directory: s.directory
662
- }))));
663
- } else {
664
- const header = `${"ID".padEnd(10)} ${"AGENT".padEnd(10)} ${"STATUS".padEnd(9)} ${"NAME".padEnd(25)} ${"MACHINE".padEnd(18)} ${"DIRECTORY".padEnd(35)}`;
665
- console.log(header);
666
- console.log("-".repeat(header.length));
667
- for (const s of enriched) {
668
- const id = s.sessionId.slice(0, 8);
669
- const agent = (s.flavor || "claude").padEnd(10);
670
- const status = s.active ? "\x1B[32mactive\x1B[0m " : "\x1B[90minactive\x1B[0m";
671
- const name = truncate(s.name || "-", 25).padEnd(25);
672
- const machine = truncate(s.host || "-", 16).padEnd(18);
673
- const dir = truncate(s.directory || "-", 33).padEnd(35);
674
- console.log(`${id.padEnd(10)} ${agent} ${status} ${name} ${machine} ${dir}`);
675
- }
676
- }
677
- }
678
- function parseShareArg(arg) {
679
- const parts = arg.split(":");
680
- const email = parts[0];
681
- const role = parts[1] || "interact";
682
- if (!["view", "interact", "admin"].includes(role)) {
683
- throw new Error(`Invalid role "${role}" in --share ${arg}. Must be view, interact, or admin.`);
684
- }
685
- return { email, role };
686
- }
687
- async function sessionSpawn(agent, directory, machineId, opts) {
688
- const { server, machine } = await connectAndGetMachine(machineId);
689
- try {
690
- let sharing;
691
- if (opts?.share?.length) {
692
- sharing = {
693
- enabled: true,
694
- owner: "",
695
- // will be auto-set by machine service from Hypha context
696
- allowedUsers: opts.share.map((s) => ({
697
- email: s.email,
698
- role: s.role || "interact",
699
- addedAt: Date.now(),
700
- addedBy: "cli"
701
- }))
702
- };
703
- }
704
- let securityContext;
705
- if (opts?.securityContextPath) {
706
- const configPath = resolve(opts.securityContextPath);
707
- const config = loadSecurityContextConfig(configPath);
708
- securityContext = resolveSecurityContext(config, void 0);
709
- }
710
- if (opts?.denyRead?.length || opts?.allowWrite?.length || opts?.denyNetwork || opts?.allowDomain?.length) {
711
- const flagCtx = buildSecurityContextFromFlags({
712
- denyRead: opts.denyRead,
713
- allowWrite: opts.allowWrite,
714
- denyNetwork: opts.denyNetwork,
715
- allowDomain: opts.allowDomain
716
- });
717
- securityContext = mergeSecurityContexts(securityContext, flagCtx);
718
- }
719
- const forceIsolation = opts?.isolate || !!securityContext;
720
- if (securityContext?.filesystem?.allowWrite?.length) {
721
- const absDir = resolve(directory);
722
- const writePaths = securityContext.filesystem.allowWrite;
723
- const dirCovered = writePaths.some((p) => absDir.startsWith(resolve(p)) || resolve(p).startsWith(absDir));
724
- if (!dirCovered) {
725
- console.warn(`Warning: Working directory ${absDir} is not covered by allowWrite paths: ${writePaths.join(", ")}`);
726
- console.warn(` The agent may not be able to write files in the working directory.`);
727
- }
728
- }
729
- console.log(`Spawning ${agent} session in ${directory}...`);
730
- if (forceIsolation) {
731
- console.log(`Isolation: enabled (workspace: ${resolve(directory)})`);
732
- }
733
- if (securityContext) {
734
- console.log(`Security context: ${JSON.stringify(securityContext, null, 2)}`);
735
- }
736
- if (sharing) {
737
- console.log(`Sharing with: ${sharing.allowedUsers.map((u) => `${u.email} (${u.role})`).join(", ")}`);
738
- }
739
- const result = await machine.spawnSession({
740
- directory,
741
- agent,
742
- sharing,
743
- securityContext,
744
- forceIsolation
745
- });
746
- if (result.type === "success") {
747
- console.log(`Session started: ${result.sessionId}`);
748
- if (result.message) console.log(` ${result.message}`);
749
- if (opts?.message && result.sessionId) {
750
- const svc = await server.getService(`svamp-session-${result.sessionId}`);
751
- const sendResult = await svc.sendMessage(
752
- JSON.stringify({
753
- role: "user",
754
- content: { type: "text", text: opts.message },
755
- meta: { sentFrom: "svamp-cli" }
756
- })
757
- );
758
- console.log(`Message sent (seq: ${sendResult.seq})`);
759
- if (opts.wait) {
760
- console.log("Waiting for agent to become idle...");
761
- await waitForBusyThenIdle(server, result.sessionId);
762
- console.log("Agent is idle.");
763
- }
764
- }
765
- } else if (result.type === "requestToApproveDirectoryCreation") {
766
- console.error(`Directory ${result.directory} does not exist. Create it first or use an existing directory.`);
767
- process.exit(1);
768
- } else {
769
- console.error(`Failed: ${result.errorMessage || "Unknown error"}`);
770
- process.exit(1);
771
- }
772
- } finally {
773
- await server.disconnect();
774
- }
775
- }
776
- async function sessionStop(sessionId, machineId) {
777
- const { server, machine } = await connectAndGetMachine(machineId);
778
- try {
779
- const sessions = await machine.listSessions();
780
- const match = resolveSessionId(sessions, sessionId);
781
- const success = await machine.stopSession(match.sessionId);
782
- if (success) {
783
- console.log(`Session ${match.sessionId.slice(0, 8)} stopped.`);
784
- } else {
785
- console.error("Failed to stop session (not found on daemon).");
786
- process.exit(1);
787
- }
788
- } finally {
789
- await server.disconnect();
790
- }
791
- }
792
- async function sessionInfo(sessionId, machineId, opts) {
793
- const { server, machine } = await connectAndGetMachine(machineId);
794
- try {
795
- const sessions = await machine.listSessions();
796
- const match = resolveSessionId(sessions, sessionId);
797
- const fullId = match.sessionId;
798
- let metadata = {};
799
- let activity = {};
800
- try {
801
- const svc = await server.getService(`svamp-session-${fullId}`);
802
- const metaResult = await svc.getMetadata();
803
- metadata = metaResult.metadata || {};
804
- activity = await svc.getActivityState();
805
- } catch {
806
- }
807
- const statusData = {
808
- sessionId: fullId,
809
- flavor: metadata.flavor || "claude",
810
- name: metadata.name || "",
811
- path: metadata.path || match.directory || "",
812
- host: metadata.host || "",
813
- lifecycleState: metadata.lifecycleState || "unknown",
814
- active: activity.active ?? false,
815
- thinking: activity.thinking ?? false,
816
- startedBy: metadata.startedBy || match.startedBy || "",
817
- summary: metadata.summary?.text || void 0,
818
- claudeSessionId: metadata.claudeSessionId || void 0,
819
- sessionLink: metadata.sessionLink?.url || void 0
820
- };
821
- if (opts?.json) {
822
- console.log(formatJson(statusData));
823
- } else {
824
- console.log(formatSessionStatus(statusData));
825
- }
826
- } finally {
827
- await server.disconnect();
828
- }
829
- }
830
- async function sessionMessages(sessionId, machineId, opts) {
831
- const { server, machine } = await connectAndGetMachine(machineId);
832
- try {
833
- const sessions = await machine.listSessions();
834
- const match = resolveSessionId(sessions, sessionId);
835
- const fullId = match.sessionId;
836
- const svc = await server.getService(`svamp-session-${fullId}`);
837
- const afterSeq = opts?.after ?? 0;
838
- const apiLimit = opts?.limit ?? 1e3;
839
- const { messages } = await svc.getMessages(afterSeq, apiLimit);
840
- const toShow = opts?.last ? messages.slice(-opts.last) : messages;
841
- if (toShow.length === 0) {
842
- if (opts?.json) {
843
- console.log(formatJson([]));
844
- } else {
845
- console.log("No messages yet.");
846
- }
847
- return;
848
- }
849
- if (opts?.json) {
850
- const formatted = [];
851
- for (const msg of toShow) {
852
- const extracted = extractMessageText(msg);
853
- if (extracted) {
854
- formatted.push(extracted);
855
- }
856
- }
857
- formatted.sort((a, b) => a.createdAt - b.createdAt);
858
- console.log(formatJson(formatted));
859
- } else {
860
- for (const msg of toShow) {
861
- renderMessage(msg);
862
- }
863
- }
864
- } finally {
865
- await server.disconnect();
866
- }
867
- }
868
- async function sessionAttach(sessionId, machineId) {
869
- const { server, machine } = await connectAndGetMachine(machineId);
870
- const sessions = await machine.listSessions();
871
- const match = resolveSessionId(sessions, sessionId);
872
- const fullId = match.sessionId;
873
- let svc;
874
- try {
875
- svc = await server.getService(`svamp-session-${fullId}`);
876
- } catch (err) {
877
- console.error(`Could not find session service: ${err.message}`);
878
- await server.disconnect();
879
- process.exit(1);
880
- }
881
- const { metadata } = await svc.getMetadata();
882
- const flavor = metadata?.flavor || "claude";
883
- const name = metadata?.name || fullId.slice(0, 8);
884
- console.log(`Attached to ${flavor} session "${name}". Commands: /quit /abort /kill
885
- `);
886
- const seenMessageIds = /* @__PURE__ */ new Set();
887
- let replayDone = false;
888
- await svc.registerListener({
889
- onUpdate: (update) => {
890
- if (update.type === "new-message") {
891
- const msg = update.message;
892
- if (!msg?.id) return;
893
- if (seenMessageIds.has(msg.id)) return;
894
- seenMessageIds.add(msg.id);
895
- if (!replayDone) return;
896
- renderMessage(msg);
897
- } else if (update.type === "activity") {
898
- if (!replayDone) return;
899
- if (update.thinking) {
900
- process.stdout.write("\x1B[90m[thinking...]\x1B[0m\r");
901
- } else if (!update.thinking) {
902
- process.stdout.write("\n> ");
903
- }
904
- } else if (update.type === "update-session") ;
905
- }
906
- });
907
- await new Promise((r) => setTimeout(r, 500));
908
- replayDone = true;
909
- console.log(`\x1B[90m(${seenMessageIds.size} messages in history)\x1B[0m`);
910
- process.stdout.write("> ");
911
- const readline = await import('readline');
912
- const rl = readline.createInterface({
913
- input: process.stdin,
914
- output: process.stdout,
915
- terminal: true
916
- });
917
- rl.on("line", async (line) => {
918
- const trimmed = line.trim();
919
- if (!trimmed) {
920
- process.stdout.write("> ");
921
- return;
922
- }
923
- if (trimmed === "/quit" || trimmed === "/detach") {
924
- console.log("Detaching (session continues running)...");
925
- rl.close();
926
- await server.disconnect();
927
- process.exit(0);
928
- }
929
- if (trimmed === "/abort" || trimmed === "/cancel") {
930
- try {
931
- await svc.abort();
932
- console.log("Abort sent.");
933
- } catch (err) {
934
- console.error(`Abort failed: ${err.message}`);
935
- }
936
- process.stdout.write("> ");
937
- return;
938
- }
939
- if (trimmed === "/kill") {
940
- try {
941
- await svc.killSession();
942
- console.log("Session killed.");
943
- } catch (err) {
944
- console.error(`Kill failed: ${err.message}`);
945
- }
946
- rl.close();
947
- await server.disconnect();
948
- process.exit(0);
949
- }
950
- if (trimmed === "/info") {
951
- try {
952
- const { metadata: m } = await svc.getMetadata();
953
- const act = await svc.getActivityState();
954
- console.log(` Agent: ${m?.flavor || "claude"}, State: ${m?.lifecycleState || "?"}, Active: ${act?.active}, Thinking: ${act?.thinking}`);
955
- } catch (err) {
956
- console.error(`Info failed: ${err.message}`);
957
- }
958
- process.stdout.write("> ");
959
- return;
960
- }
961
- try {
962
- await svc.sendMessage(
963
- JSON.stringify({
964
- role: "user",
965
- content: { type: "text", text: trimmed }
966
- })
967
- );
968
- } catch (err) {
969
- console.error(`Send failed: ${err.message}`);
970
- process.stdout.write("> ");
971
- }
972
- });
973
- rl.on("close", async () => {
974
- await server.disconnect();
975
- process.exit(0);
976
- });
977
- process.on("SIGINT", async () => {
978
- console.log("\nDetaching (session continues running)...");
979
- rl.close();
980
- await server.disconnect();
981
- process.exit(0);
982
- });
983
- }
984
- async function sessionSend(sessionId, message, machineId, opts) {
985
- const { server, machine } = await connectAndGetMachine(machineId);
986
- try {
987
- const sessions = await machine.listSessions();
988
- const match = resolveSessionId(sessions, sessionId);
989
- const fullId = match.sessionId;
990
- const svc = await server.getService(`svamp-session-${fullId}`);
991
- const result = await svc.sendMessage(
992
- JSON.stringify({
993
- role: "user",
994
- content: { type: "text", text: message },
995
- meta: { sentFrom: "svamp-cli" }
996
- })
997
- );
998
- if (opts?.wait) {
999
- const timeoutMs = (opts.timeout || 300) * 1e3;
1000
- await waitForBusyThenIdle(server, fullId, timeoutMs);
1001
- }
1002
- if (opts?.json) {
1003
- console.log(formatJson({
1004
- sessionId: fullId,
1005
- message,
1006
- sent: true,
1007
- seq: result.seq,
1008
- waited: !!opts.wait
1009
- }));
1010
- } else {
1011
- console.log(`Message sent to session ${fullId.slice(0, 8)} (seq: ${result.seq})`);
1012
- if (opts?.wait) {
1013
- console.log("Agent is idle.");
1014
- }
1015
- }
1016
- } finally {
1017
- await server.disconnect();
1018
- }
1019
- }
1020
- async function sessionWait(sessionId, machineId, opts) {
1021
- const { server, machine } = await connectAndGetMachine(machineId);
1022
- try {
1023
- const sessions = await machine.listSessions();
1024
- const match = resolveSessionId(sessions, sessionId);
1025
- const fullId = match.sessionId;
1026
- const timeoutMs = (opts?.timeout || 300) * 1e3;
1027
- await waitForIdle(server, fullId, timeoutMs);
1028
- console.log(`Session ${fullId.slice(0, 8)} is idle.`);
1029
- } catch (err) {
1030
- const msg = err instanceof Error ? err.message : String(err);
1031
- console.error(msg);
1032
- process.exitCode = 1;
1033
- } finally {
1034
- await server.disconnect();
1035
- }
1036
- }
1037
- async function sessionShare(sessionIdPartial, machineId, opts) {
1038
- const { server, machine } = await connectAndGetMachine(machineId);
1039
- try {
1040
- const sessions = await machine.listSessions();
1041
- const match = resolveSessionId(sessions, sessionIdPartial);
1042
- const fullId = match.sessionId;
1043
- const svc = await server.getService(`svamp-session-${fullId}`);
1044
- if (opts.list) {
1045
- const metaResult = await svc.getMetadata();
1046
- const sharing = metaResult.metadata?.sharing;
1047
- if (!sharing || !sharing.enabled) {
1048
- console.log("Sharing is not enabled for this session.");
1049
- return;
1050
- }
1051
- console.log(`Owner: ${sharing.owner}`);
1052
- if (sharing.allowedUsers.length === 0) {
1053
- console.log("No shared users.");
1054
- } else {
1055
- console.log("Shared users:");
1056
- for (const u of sharing.allowedUsers) {
1057
- console.log(` ${u.email.padEnd(30)} ${u.role.padEnd(10)} (added ${new Date(u.addedAt).toISOString().slice(0, 10)})`);
1058
- }
1059
- }
1060
- const secCtx = metaResult.metadata?.securityContext;
1061
- if (secCtx) {
1062
- console.log(`
1063
- Security context:`);
1064
- console.log(JSON.stringify(secCtx, null, 2));
1065
- }
1066
- return;
1067
- }
1068
- if (opts.add) {
1069
- const { email, role } = parseShareArg(opts.add);
1070
- const metaResult = await svc.getMetadata();
1071
- let sharing = metaResult.metadata?.sharing || {
1072
- enabled: true,
1073
- owner: "",
1074
- allowedUsers: []
1075
- };
1076
- sharing.enabled = true;
1077
- sharing.allowedUsers = sharing.allowedUsers.filter(
1078
- (u) => u.email.toLowerCase() !== email.toLowerCase()
1079
- );
1080
- sharing.allowedUsers.push({
1081
- email,
1082
- role,
1083
- addedAt: Date.now(),
1084
- addedBy: "cli"
1085
- });
1086
- await svc.updateSharing(sharing);
1087
- console.log(`Shared session ${fullId.slice(0, 8)} with ${email} (${role}).`);
1088
- return;
1089
- }
1090
- if (opts.remove) {
1091
- const email = opts.remove;
1092
- const metaResult = await svc.getMetadata();
1093
- const sharing = metaResult.metadata?.sharing;
1094
- if (!sharing) {
1095
- console.error("Sharing is not enabled for this session.");
1096
- process.exit(1);
1097
- }
1098
- const before = sharing.allowedUsers.length;
1099
- sharing.allowedUsers = sharing.allowedUsers.filter(
1100
- (u) => u.email.toLowerCase() !== email.toLowerCase()
1101
- );
1102
- if (sharing.allowedUsers.length === before) {
1103
- console.error(`User ${email} is not in the shared users list.`);
1104
- process.exit(1);
1105
- }
1106
- await svc.updateSharing(sharing);
1107
- console.log(`Removed ${email} from session ${fullId.slice(0, 8)}.`);
1108
- return;
1109
- }
1110
- console.error("Usage: svamp session share <id> --add <email>[:<role>] | --remove <email> | --list");
1111
- process.exit(1);
1112
- } finally {
1113
- await server.disconnect();
1114
- }
1115
- }
1116
- async function machineShare(machineId, opts) {
1117
- const { server, machine } = await connectAndGetMachine(machineId);
1118
- try {
1119
- if (opts.list) {
1120
- const info = await machine.getMachineInfo();
1121
- const sharing = info.metadata?.sharing;
1122
- if (!sharing || !sharing.enabled) {
1123
- console.log("Sharing is not enabled for this machine.");
1124
- } else {
1125
- console.log(`Owner: ${sharing.owner}`);
1126
- if (sharing.allowedUsers.length === 0) {
1127
- console.log("No shared users.");
1128
- } else {
1129
- console.log("Shared users:");
1130
- for (const u of sharing.allowedUsers) {
1131
- console.log(` ${u.email.padEnd(30)} ${u.role.padEnd(10)} (added ${new Date(u.addedAt).toISOString().slice(0, 10)})`);
1132
- }
1133
- }
1134
- }
1135
- const iso = info.metadata?.isolationCapabilities;
1136
- if (iso) {
1137
- console.log(`
1138
- Isolation: ${iso.available.length > 0 ? iso.available.join(", ") : "none available"} (preferred: ${iso.preferred || "none"})`);
1139
- }
1140
- return;
1141
- }
1142
- if (opts.showConfig) {
1143
- const result = await machine.getSecurityContextConfig();
1144
- const config = result?.securityContextConfig;
1145
- if (!config) {
1146
- console.log("No security context config set for this machine.");
1147
- } else {
1148
- console.log(JSON.stringify(config, null, 2));
1149
- }
1150
- return;
1151
- }
1152
- if (opts.configPath) {
1153
- const configPath = resolve(opts.configPath);
1154
- const config = loadSecurityContextConfig(configPath);
1155
- await machine.updateSecurityContextConfig(config);
1156
- const userCount = config.users ? Object.keys(config.users).length : 0;
1157
- console.log(`Security context config applied to machine.`);
1158
- console.log(` Default context: ${config.default ? "yes" : "none"}`);
1159
- console.log(` User-specific entries: ${userCount}`);
1160
- if (config.users) {
1161
- for (const email of Object.keys(config.users)) {
1162
- const ctx = config.users[email];
1163
- console.log(` ${email}: role=${ctx.role || "default"}`);
1164
- }
1165
- }
1166
- return;
1167
- }
1168
- if (opts.add) {
1169
- const { email, role } = parseShareArg(opts.add);
1170
- const info = await machine.getMachineInfo();
1171
- let sharing = info.metadata?.sharing || {
1172
- enabled: true,
1173
- owner: "",
1174
- allowedUsers: []
1175
- };
1176
- sharing.enabled = true;
1177
- sharing.allowedUsers = sharing.allowedUsers.filter(
1178
- (u) => u.email.toLowerCase() !== email.toLowerCase()
1179
- );
1180
- sharing.allowedUsers.push({
1181
- email,
1182
- role,
1183
- addedAt: Date.now(),
1184
- addedBy: "cli"
1185
- });
1186
- await machine.updateSharing(sharing);
1187
- console.log(`Shared machine with ${email} (${role}).`);
1188
- return;
1189
- }
1190
- if (opts.remove) {
1191
- const email = opts.remove;
1192
- const info = await machine.getMachineInfo();
1193
- const sharing = info.metadata?.sharing;
1194
- if (!sharing) {
1195
- console.error("Sharing is not enabled for this machine.");
1196
- process.exit(1);
1197
- }
1198
- const before = sharing.allowedUsers.length;
1199
- sharing.allowedUsers = sharing.allowedUsers.filter(
1200
- (u) => u.email.toLowerCase() !== email.toLowerCase()
1201
- );
1202
- if (sharing.allowedUsers.length === before) {
1203
- console.error(`User ${email} is not in the shared users list.`);
1204
- process.exit(1);
1205
- }
1206
- await machine.updateSharing(sharing);
1207
- console.log(`Removed ${email} from machine sharing.`);
1208
- return;
1209
- }
1210
- console.error("Usage: svamp machine share --add <email>[:<role>] | --remove <email> | --list | --config <path> | --show-config");
1211
- process.exit(1);
1212
- } finally {
1213
- await server.disconnect();
1214
- }
1215
- }
1216
-
1217
- export { connectAndGetMachine, machineShare, parseShareArg, renderMessage, resolveSessionId, sessionAttach, sessionInfo, sessionList, sessionMachines, sessionMessages, sessionSend, sessionShare, sessionSpawn, sessionStop, sessionWait };