@dylanpiercey/work 0.2.17 → 0.2.18

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 (517) hide show
  1. package/README.md +13 -0
  2. package/bin/cli/README.md +34 -0
  3. package/bin/cli/_progress.mjs +68 -0
  4. package/bin/cli/_repo.mjs +32 -0
  5. package/bin/cli/_target.mjs +49 -0
  6. package/bin/cli/agent.mjs +153 -0
  7. package/bin/cli/branches.mjs +79 -0
  8. package/bin/cli/config.mjs +175 -0
  9. package/bin/cli/host.mjs +142 -0
  10. package/bin/cli/lsp.mjs +117 -0
  11. package/bin/cli/notify.mjs +53 -0
  12. package/bin/cli/open.mjs +37 -0
  13. package/bin/cli/repo.mjs +98 -0
  14. package/bin/cli/session.mjs +167 -0
  15. package/bin/cli/skill.mjs +99 -0
  16. package/bin/cli/stack.mjs +51 -0
  17. package/bin/cli/status.mjs +86 -0
  18. package/bin/cli/topic.mjs +585 -0
  19. package/bin/cli/workspace.mjs +184 -0
  20. package/bin/work.mjs +402 -80
  21. package/dist/_BAJGOEa1.js +168 -0
  22. package/dist/{_Bj-WwfnW.js → _BlLEM8du.js} +76 -46
  23. package/dist/_t9VsALwo.js +2 -0
  24. package/dist/index.mjs +3550 -2616
  25. package/dist/public/assets/{_CXVkXsw1.js → _10dZ1R0m.js} +1 -1
  26. package/dist/public/assets/_10dZ1R0m.js.br +0 -0
  27. package/dist/public/assets/_1EPD0XJG.js +1 -0
  28. package/dist/public/assets/_1jA3fFvQ.js +1 -0
  29. package/dist/public/assets/{_DZanHI_X.js → _4Sj6a8Ya.js} +1 -1
  30. package/dist/public/assets/_4Sj6a8Ya.js.br +0 -0
  31. package/dist/public/assets/{_Quh6tl1u.js → _5tN48iuJ.js} +1 -1
  32. package/dist/public/assets/_5tN48iuJ.js.br +0 -0
  33. package/dist/public/assets/{_CYQ4srZb.js → _8_DzkMgB.js} +1 -1
  34. package/dist/public/assets/_8_DzkMgB.js.br +0 -0
  35. package/dist/public/assets/{_CxgPm_xy.js → _9JG-F3GR.js} +1 -1
  36. package/dist/public/assets/_9JG-F3GR.js.br +0 -0
  37. package/dist/public/assets/_9KUM3Nch.js +2 -0
  38. package/dist/public/assets/_9KUM3Nch.js.br +0 -0
  39. package/dist/public/assets/{_BoEcMQmX.js → _B-H6ABVZ.js} +1 -1
  40. package/dist/public/assets/_B-H6ABVZ.js.br +0 -0
  41. package/dist/public/assets/{_D-0pUYXq.js → _B-iOtMg6.js} +1 -1
  42. package/dist/public/assets/_B-iOtMg6.js.br +0 -0
  43. package/dist/public/assets/{_CJgqLdsx.js → _B0FhzjB2.js} +1 -1
  44. package/dist/public/assets/_B0FhzjB2.js.br +0 -0
  45. package/dist/public/assets/{_C1-Nli75.js → _B26gPL08.js} +1 -1
  46. package/dist/public/assets/_B26gPL08.js.br +0 -0
  47. package/dist/public/assets/{_BA-MNdjL.js → _B2DHvTZR.js} +1 -1
  48. package/dist/public/assets/{_vX9d6g5Y.js → _B3Z1kRpD.js} +1 -1
  49. package/dist/public/assets/_B3Z1kRpD.js.br +0 -0
  50. package/dist/public/assets/{_BfQeZPXW.js → _B3sH8Usv.js} +1 -1
  51. package/dist/public/assets/_B3sH8Usv.js.br +0 -0
  52. package/dist/public/assets/_B42hLH2K.js +1 -0
  53. package/dist/public/assets/{_CY_Z-kV5.js → _B4hn3NNW.js} +1 -1
  54. package/dist/public/assets/_B4hn3NNW.js.br +0 -0
  55. package/dist/public/assets/{_DK-MOyi_.js → _B5NleRC6.js} +1 -1
  56. package/dist/public/assets/_B5NleRC6.js.br +0 -0
  57. package/dist/public/assets/_B5fRGxPQ.js +1 -0
  58. package/dist/public/assets/_B5fRGxPQ.js.br +0 -0
  59. package/dist/public/assets/_B65yR5Ej.js +1 -0
  60. package/dist/public/assets/_B6Sh9WEH.js +1 -0
  61. package/dist/public/assets/{_C7bHTlG1.js → _B6VtVybz.js} +1 -1
  62. package/dist/public/assets/_B6VtVybz.js.br +0 -0
  63. package/dist/public/assets/_B7rJ4n9b.js +1 -0
  64. package/dist/public/assets/_B8M-AD29.js +1 -0
  65. package/dist/public/assets/{_BQEpu56f.js → _B8joFpf1.js} +1 -1
  66. package/dist/public/assets/_B8joFpf1.js.br +0 -0
  67. package/dist/public/assets/_B9nA-Uh1.js +1 -0
  68. package/dist/public/assets/_B9nA-Uh1.js.br +0 -0
  69. package/dist/public/assets/_BA6X_Aj6.js +1 -0
  70. package/dist/public/assets/_BBFJV2Bx.js +1 -0
  71. package/dist/public/assets/_BCW5GFMW.js +1 -0
  72. package/dist/public/assets/{_TynjiShF.js → _BD1SQU0V.js} +1 -1
  73. package/dist/public/assets/_BD1SQU0V.js.br +0 -0
  74. package/dist/public/assets/_BEpd9Y1R.js +1 -0
  75. package/dist/public/assets/_BEy7R4W_.js +1 -0
  76. package/dist/public/assets/_BFGJOHQb.js +1 -0
  77. package/dist/public/assets/{_BiY8chB-.js → _BI45WgW9.js} +1 -1
  78. package/dist/public/assets/_BI45WgW9.js.br +0 -0
  79. package/dist/public/assets/_BIKvQKWe.js +1 -0
  80. package/dist/public/assets/_BIPnL2aI.js +1 -0
  81. package/dist/public/assets/{_Dj5RDA5P.js → _BIUaA4Nl.js} +1 -1
  82. package/dist/public/assets/_BIUaA4Nl.js.br +0 -0
  83. package/dist/public/assets/{_Ch-UiovX.js → _BJpBnuvv.js} +1 -1
  84. package/dist/public/assets/_BJpBnuvv.js.br +0 -0
  85. package/dist/public/assets/{_0KruTMn9.js → _BLeLpnYL.js} +1 -1
  86. package/dist/public/assets/_BLeLpnYL.js.br +0 -0
  87. package/dist/public/assets/_BMb5ANTv.js +1 -0
  88. package/dist/public/assets/{_ChPy2IX4.js → _BSRsPQTe.js} +1 -1
  89. package/dist/public/assets/_BSRsPQTe.js.br +0 -0
  90. package/dist/public/assets/_BVuerXin.js +6 -0
  91. package/dist/public/assets/_BVuerXin.js.br +0 -0
  92. package/dist/public/assets/_BWV8aSlZ.js +1 -0
  93. package/dist/public/assets/{_Q5s6o5H4.js → _BXHggM8M.js} +1 -1
  94. package/dist/public/assets/_BXHggM8M.js.br +0 -0
  95. package/dist/public/assets/{_D8uBvHXU.js → _BXPUZfKX.js} +1 -1
  96. package/dist/public/assets/_BXPUZfKX.js.br +0 -0
  97. package/dist/public/assets/_BYebhygn.js +1 -0
  98. package/dist/public/assets/_BZgCxd-U.js +1 -0
  99. package/dist/public/assets/_BZgCxd-U.js.br +0 -0
  100. package/dist/public/assets/_B_W51WxU.js +1 -0
  101. package/dist/public/assets/_B_jFFNyT.js +1 -0
  102. package/dist/public/assets/_Ba9OQzPV.js +1 -0
  103. package/dist/public/assets/_Ba9OQzPV.js.br +0 -0
  104. package/dist/public/assets/_BayiY7GU.js +1 -0
  105. package/dist/public/assets/_BcDMeS82.js +1 -0
  106. package/dist/public/assets/_BdD1sZp_.js +1 -0
  107. package/dist/public/assets/_Bg_WwlL2.js +1 -0
  108. package/dist/public/assets/_BhHzv4g0.js +1 -0
  109. package/dist/public/assets/_BjEinMEe.js +1 -0
  110. package/dist/public/assets/_BjEinMEe.js.br +0 -0
  111. package/dist/public/assets/{_DPiemRX1.js → _BkGAUdMd.js} +1 -1
  112. package/dist/public/assets/_BkGAUdMd.js.br +0 -0
  113. package/dist/public/assets/{_FwFz43m8.js → _BkHLXMjq.js} +1 -1
  114. package/dist/public/assets/_BkHLXMjq.js.br +0 -0
  115. package/dist/public/assets/_BmSWsZvV.js +1 -0
  116. package/dist/public/assets/{_D_GeNCls.js → _Bn6y2_ex.js} +1 -1
  117. package/dist/public/assets/_Bn6y2_ex.js.br +0 -0
  118. package/dist/public/assets/_Bp2gqUaf.js +1 -0
  119. package/dist/public/assets/_Bs6oYKkD.js +1 -0
  120. package/dist/public/assets/{_CYhJ6ae-.js → _Bs_MPTBZ.js} +1 -1
  121. package/dist/public/assets/_Bs_MPTBZ.js.br +0 -0
  122. package/dist/public/assets/_Bt13j1ev.js +1 -0
  123. package/dist/public/assets/{_D-pUpBL1.js → _Bt4cxzaP.js} +1 -1
  124. package/dist/public/assets/_BwlnYt-t.js +1 -0
  125. package/dist/public/assets/_Bx9aUXTG.js +1 -0
  126. package/dist/public/assets/{_DUdEYxV0.js → _BxKDE6Ct.js} +1 -1
  127. package/dist/public/assets/_BxKDE6Ct.js.br +0 -0
  128. package/dist/public/assets/_BymFDqKj.js +1 -0
  129. package/dist/public/assets/{_i4iteLF8.js → _C-z8Ntse.js} +1 -1
  130. package/dist/public/assets/_C-z8Ntse.js.br +0 -0
  131. package/dist/public/assets/_C0XUbqWe.js +1 -0
  132. package/dist/public/assets/_C1upDwBv.js +1 -0
  133. package/dist/public/assets/_C3f4huqn.js +1 -0
  134. package/dist/public/assets/{_BHyWcuOR.js → _C46o3wQ8.js} +1 -1
  135. package/dist/public/assets/_C46o3wQ8.js.br +0 -0
  136. package/dist/public/assets/_C4_3qKLX.js +1 -0
  137. package/dist/public/assets/_C7tG4xwv.js +1 -0
  138. package/dist/public/assets/_C8LciIOa.js +1 -0
  139. package/dist/public/assets/{_zcAaK79d.js → _C9k00otG.js} +1 -1
  140. package/dist/public/assets/_C9k00otG.js.br +0 -0
  141. package/dist/public/assets/_CBG92pZO.js +1 -0
  142. package/dist/public/assets/_CBfI2lSr.js +1 -0
  143. package/dist/public/assets/{_BPNSknLT.js → _CFKwHn62.js} +1 -1
  144. package/dist/public/assets/_CFKwHn62.js.br +0 -0
  145. package/dist/public/assets/_CHDvRNgn.js +1 -0
  146. package/dist/public/assets/{_DVLNzEkW.js → _CIDjLk1A.js} +1 -1
  147. package/dist/public/assets/_CIDjLk1A.js.br +0 -0
  148. package/dist/public/assets/{_3fvr5bU1.js → _CLV83hY7.js} +1 -1
  149. package/dist/public/assets/_CLV83hY7.js.br +0 -0
  150. package/dist/public/assets/_CO8ZosDU.js +1 -0
  151. package/dist/public/assets/{_D4CkNo0r.js → _CPqT91JX.js} +1 -1
  152. package/dist/public/assets/_CPqT91JX.js.br +0 -0
  153. package/dist/public/assets/_CRZ03hrO.js +1 -0
  154. package/dist/public/assets/_CUKvJRmi.js +1 -0
  155. package/dist/public/assets/_CU_FKEsy.js +1 -0
  156. package/dist/public/assets/{_Do6b-nfX.js → _CUxBk_RE.js} +1 -1
  157. package/dist/public/assets/_CUxBk_RE.js.br +0 -0
  158. package/dist/public/assets/_CW_vyZfo.js +1 -0
  159. package/dist/public/assets/{_BOBYB-cj.js → _CX5VOdNU.js} +1 -1
  160. package/dist/public/assets/_CX5VOdNU.js.br +0 -0
  161. package/dist/public/assets/{_C5c3o9N3.js → _CYcgwayF.js} +1 -1
  162. package/dist/public/assets/_CYcgwayF.js.br +0 -0
  163. package/dist/public/assets/{_BXcMBVCY.js → _CZsqsL_m.js} +1 -1
  164. package/dist/public/assets/_CZsqsL_m.js.br +0 -0
  165. package/dist/public/assets/_C_a8QggH.js +1 -0
  166. package/dist/public/assets/_C_ymFHNL.js +1 -0
  167. package/dist/public/assets/_CaU4irFc.js +1 -0
  168. package/dist/public/assets/_Cb3fSi2R.js +1 -0
  169. package/dist/public/assets/{_DOKecL0e.js → _CcINcTUt.js} +1 -1
  170. package/dist/public/assets/_CcINcTUt.js.br +0 -0
  171. package/dist/public/assets/{_BTh4v5Tr.js → _CcflXOTj.js} +1 -1
  172. package/dist/public/assets/_CcflXOTj.js.br +0 -0
  173. package/dist/public/assets/{_DbLQjG4R.js → _Cfts4mFJ.js} +1 -1
  174. package/dist/public/assets/_Cfts4mFJ.js.br +0 -0
  175. package/dist/public/assets/{_v3HPnNBs.js → _ClHeQc6g.js} +1 -1
  176. package/dist/public/assets/_ClHeQc6g.js.br +0 -0
  177. package/dist/public/assets/_CnSlVcEh.js +1 -0
  178. package/dist/public/assets/_Co71-HWR.js +1 -0
  179. package/dist/public/assets/_CpEeHKyF.js +1 -0
  180. package/dist/public/assets/{_CNUs2Gqk.js → _CpVeIwQ4.js} +1 -1
  181. package/dist/public/assets/_CpVeIwQ4.js.br +0 -0
  182. package/dist/public/assets/{_Cc_GUPR4.js → _Cq4DpL3R.js} +1 -1
  183. package/dist/public/assets/_Cq4DpL3R.js.br +0 -0
  184. package/dist/public/assets/_CvxsvMFa.js +1 -0
  185. package/dist/public/assets/{_DorQTaAa.js → _Cwdn40Q2.js} +1 -1
  186. package/dist/public/assets/_Cwdn40Q2.js.br +0 -0
  187. package/dist/public/assets/_CzN0wC_5.js +16 -0
  188. package/dist/public/assets/_CzN0wC_5.js.br +0 -0
  189. package/dist/public/assets/_D3ieGLoq.js +1 -0
  190. package/dist/public/assets/_D4wF26fk.js +1 -0
  191. package/dist/public/assets/_D9a9nY1Q.js +1 -0
  192. package/dist/public/assets/{_CIWiehYG.js → _DB7_Yora.js} +1 -1
  193. package/dist/public/assets/_DB7_Yora.js.br +0 -0
  194. package/dist/public/assets/{_6klEiBhY.js → _DDWIDZyP.js} +1 -1
  195. package/dist/public/assets/_DDWIDZyP.js.br +0 -0
  196. package/dist/public/assets/{_Cwtn28hL.js → _DG3L8-RO.js} +1 -1
  197. package/dist/public/assets/_DG3L8-RO.js.br +0 -0
  198. package/dist/public/assets/{_BxS3xvmE.js → _DGRgLaG-.js} +1 -1
  199. package/dist/public/assets/_DGRgLaG-.js.br +0 -0
  200. package/dist/public/assets/{_pEUCjhqm.js → _DG_QOtsx.js} +1 -1
  201. package/dist/public/assets/_DG_QOtsx.js.br +0 -0
  202. package/dist/public/assets/{_BwSXxzqy.js → _DHYIZWev.js} +11 -11
  203. package/dist/public/assets/_DHYIZWev.js.br +0 -0
  204. package/dist/public/assets/{_BoTzWlXR.js → _DJwY_zBh.js} +1 -1
  205. package/dist/public/assets/_DJwY_zBh.js.br +0 -0
  206. package/dist/public/assets/{_C9jR1P3s.js → _DKlB-yMq.js} +1 -1
  207. package/dist/public/assets/_DKlB-yMq.js.br +0 -0
  208. package/dist/public/assets/{_DYqx8g78.js → _DMCVOg7x.js} +1 -1
  209. package/dist/public/assets/_DMCVOg7x.js.br +0 -0
  210. package/dist/public/assets/_DMXnrdYy.js +1 -0
  211. package/dist/public/assets/_DMXnrdYy.js.br +0 -0
  212. package/dist/public/assets/{_Di78jTI-.js → _DNrRZhO7.js} +1 -1
  213. package/dist/public/assets/_DNrRZhO7.js.br +0 -0
  214. package/dist/public/assets/_DPdb5l-Z.js +1 -0
  215. package/dist/public/assets/{_Depr5Jps.js → _DRpsUHP5.js} +1 -1
  216. package/dist/public/assets/_DRpsUHP5.js.br +0 -0
  217. package/dist/public/assets/{_BHgwN0LC.js → _DUH5o_iN.js} +1 -1
  218. package/dist/public/assets/_DUH5o_iN.js.br +0 -0
  219. package/dist/public/assets/{_-ycNE92Z.js → _DUmYv8Kf.js} +1 -1
  220. package/dist/public/assets/_DUmYv8Kf.js.br +0 -0
  221. package/dist/public/assets/{_CPbClwhX.js → _DXvsvUmJ.js} +1 -1
  222. package/dist/public/assets/_DXvsvUmJ.js.br +0 -0
  223. package/dist/public/assets/{_DqGOYRpi.js → _DYxQeog7.js} +1 -1
  224. package/dist/public/assets/_DYxQeog7.js.br +0 -0
  225. package/dist/public/assets/_DZIDdpeW.js +1 -0
  226. package/dist/public/assets/{_CCONYsJT.js → _DZqoQxTm.js} +1 -1
  227. package/dist/public/assets/_DZqoQxTm.js.br +0 -0
  228. package/dist/public/assets/_D_4RJnk0.js +2 -0
  229. package/dist/public/assets/_D_4RJnk0.js.br +0 -0
  230. package/dist/public/assets/_DclJXqwW.js +1 -0
  231. package/dist/public/assets/_Debmjz1c.js +1 -0
  232. package/dist/public/assets/_DeiCZCO_.js +1 -0
  233. package/dist/public/assets/{_yp48NYOW.js → _Df-N1G_n.js} +1 -1
  234. package/dist/public/assets/_Df-N1G_n.js.br +0 -0
  235. package/dist/public/assets/_Df_3rEok.js +86 -0
  236. package/dist/public/assets/_Df_3rEok.js.br +0 -0
  237. package/dist/public/assets/{_DO_4_u2Q.js → _Dh9434oc.js} +1 -1
  238. package/dist/public/assets/{_Dhn3A3KJ.js → _DjYscMjF.js} +1 -1
  239. package/dist/public/assets/_DjYscMjF.js.br +0 -0
  240. package/dist/public/assets/{_cyorvuL1.js → _Dku55Ypt.js} +1 -1
  241. package/dist/public/assets/_Dku55Ypt.js.br +0 -0
  242. package/dist/public/assets/_Dm8TVkBX.js +1 -0
  243. package/dist/public/assets/_DnUBW0Qq.js +1 -0
  244. package/dist/public/assets/_DoOPrRIy.js +1 -0
  245. package/dist/public/assets/_DqJvxlYV.js +1 -0
  246. package/dist/public/assets/_Drle66GA.js +1 -0
  247. package/dist/public/assets/_DtrvrAfl.js +1 -0
  248. package/dist/public/assets/{_B5JxE4Sx.js → _DwTaEsY5.js} +1 -1
  249. package/dist/public/assets/_DwTaEsY5.js.br +0 -0
  250. package/dist/public/assets/_Dypj9l-z.js +1 -0
  251. package/dist/public/assets/_DyvwMfCp.js +1 -0
  252. package/dist/public/assets/{_OpbuiQ7h.js → _GK8r-gEu.js} +1 -1
  253. package/dist/public/assets/_GK8r-gEu.js.br +0 -0
  254. package/dist/public/assets/{_BqWlTTxo.js → _HpxvfbM2.js} +1 -1
  255. package/dist/public/assets/_HpxvfbM2.js.br +0 -0
  256. package/dist/public/assets/{_CIrZGxEf.js → _RudCGuBM.js} +1 -1
  257. package/dist/public/assets/_RudCGuBM.js.br +0 -0
  258. package/dist/public/assets/_UbfI3k4y.js +1 -0
  259. package/dist/public/assets/{_A-jpjc8j.js → _WOU8TCkO.js} +1 -1
  260. package/dist/public/assets/_WOU8TCkO.js.br +0 -0
  261. package/dist/public/assets/{_C-lB_p3U.js → _XWw1j8vS.js} +1 -1
  262. package/dist/public/assets/_XWw1j8vS.js.br +0 -0
  263. package/dist/public/assets/{_BCB8LQS3.js → _YDyNvOET.js} +1 -1
  264. package/dist/public/assets/_YDyNvOET.js.br +0 -0
  265. package/dist/public/assets/{_DzlXwaiG.js → _f8D86HXZ.js} +1 -1
  266. package/dist/public/assets/_f8D86HXZ.js.br +0 -0
  267. package/dist/public/assets/{_BUW_V5kN.js → _hbXd8Ogo.js} +1 -1
  268. package/dist/public/assets/_hbXd8Ogo.js.br +0 -0
  269. package/dist/public/assets/{_CrlNjDih.js → _i6YkSowH.js} +1 -1
  270. package/dist/public/assets/_i6YkSowH.js.br +0 -0
  271. package/dist/public/assets/_k1ciacDf.js +1 -0
  272. package/dist/public/assets/_k6nXIP-i.js +1 -0
  273. package/dist/public/assets/_keX09TNx.js +1 -0
  274. package/dist/public/assets/{_Dwe1TUjY.js → _l9xpLTP_.js} +1 -1
  275. package/dist/public/assets/_l9xpLTP_.js.br +0 -0
  276. package/dist/public/assets/_n9Rb26YX.js +1 -0
  277. package/dist/public/assets/{_4_sKOYsy.js → _o8mJsMHw.js} +1 -1
  278. package/dist/public/assets/_o8mJsMHw.js.br +0 -0
  279. package/dist/public/assets/{_Ca2DTddL.js → _oVsjszGq.js} +1 -1
  280. package/dist/public/assets/_oVsjszGq.js.br +0 -0
  281. package/dist/public/assets/_pM8W420Q.js +1 -0
  282. package/dist/public/assets/{_C449plW0.js → _rv9KtZHz.js} +1 -1
  283. package/dist/public/assets/_rv9KtZHz.js.br +0 -0
  284. package/dist/public/assets/{_B19mS3yM.js → _sSKEak2x.js} +1 -1
  285. package/dist/public/assets/_sSKEak2x.js.br +0 -0
  286. package/dist/public/assets/_tJgkkxOD.js +1 -0
  287. package/dist/public/assets/_tJgkkxOD.js.br +0 -0
  288. package/dist/public/assets/_uP3LGOtV.js +1 -0
  289. package/dist/public/assets/{_BT7DMail.js → _vG7bXn-C.js} +1 -1
  290. package/dist/public/assets/_vG7bXn-C.js.br +0 -0
  291. package/dist/public/assets/{_BTWfPOi-.js → _vHqiFYsh.js} +1 -1
  292. package/dist/public/assets/_vHqiFYsh.js.br +0 -0
  293. package/dist/public/assets/_vXJ7nP0d.js +1 -0
  294. package/dist/public/assets/{_Dks8YY2K.js → _w-MAJku6.js} +1 -1
  295. package/dist/public/assets/_wvhU__5f.js +1 -0
  296. package/dist/public/assets/_xFMpRp_e.js +1 -0
  297. package/dist/public/assets/_yXimF5vw.js +1 -0
  298. package/dist/public/assets/files-view-BennlUmP.js +2 -0
  299. package/dist/public/assets/manage-workspace-CaESLJdc.js +2 -0
  300. package/dist/public/assets/new-BNH4m99d.js +1 -0
  301. package/dist/public/assets/new-workspace-CwiZjHNn.js +2 -0
  302. package/dist/public/assets/review-view-BdbMyVt7.js +2 -0
  303. package/dist/public/assets/settings-BNH4m99d.js +1 -0
  304. package/dist/public/assets/settings-page-CU3aC70Q.js +2 -0
  305. package/dist/public/assets/styles-ctr-Sxsj.css +1 -0
  306. package/dist/public/assets/styles-ctr-Sxsj.css.br +0 -0
  307. package/dist/public/assets/w-BNH4m99d.js +1 -0
  308. package/dist/public/assets/w._-BNH4m99d.js +1 -0
  309. package/dist/public/assets/w._-CLYxMMR4.js +1 -0
  310. package/dist/public/assets/w._-_w8eUYMy.js +1 -0
  311. package/dist/public/sw.js +11 -4
  312. package/dist/public/sw.js.br +0 -0
  313. package/lib/ports/url.d.mts +20 -0
  314. package/lib/ports/url.mjs +80 -0
  315. package/package.json +1 -1
  316. package/dist/_CLoLdV87.js +0 -2
  317. package/dist/_DPpY3UqO.js +0 -167
  318. package/dist/public/assets/_-ycNE92Z.js.br +0 -0
  319. package/dist/public/assets/_0KruTMn9.js.br +0 -0
  320. package/dist/public/assets/_0NH5yJMx.js +0 -1
  321. package/dist/public/assets/_1CAp-NbR.js +0 -1
  322. package/dist/public/assets/_3fvr5bU1.js.br +0 -0
  323. package/dist/public/assets/_4Dkmi0BI.js +0 -1
  324. package/dist/public/assets/_4_sKOYsy.js.br +0 -0
  325. package/dist/public/assets/_6klEiBhY.js.br +0 -0
  326. package/dist/public/assets/_7_64OpLh.js +0 -1
  327. package/dist/public/assets/_7p-zxUs0.js +0 -1
  328. package/dist/public/assets/_8RAv3gFP.js +0 -1
  329. package/dist/public/assets/_9PDxaMA9.js +0 -1
  330. package/dist/public/assets/_9PDxaMA9.js.br +0 -0
  331. package/dist/public/assets/_9mbDrbe7.js +0 -1
  332. package/dist/public/assets/_A-jpjc8j.js.br +0 -0
  333. package/dist/public/assets/_B0JwTeu6.js +0 -1
  334. package/dist/public/assets/_B19mS3yM.js.br +0 -0
  335. package/dist/public/assets/_B1Sfrnda.js +0 -1
  336. package/dist/public/assets/_B3i0wS_I.js +0 -1
  337. package/dist/public/assets/_B5JxE4Sx.js.br +0 -0
  338. package/dist/public/assets/_B9RLcC8I.js +0 -1
  339. package/dist/public/assets/_BCB8LQS3.js.br +0 -0
  340. package/dist/public/assets/_BE7UYyYb.js +0 -1
  341. package/dist/public/assets/_BHgwN0LC.js.br +0 -0
  342. package/dist/public/assets/_BHyWcuOR.js.br +0 -0
  343. package/dist/public/assets/_BIpUDgQH.js +0 -16
  344. package/dist/public/assets/_BIpUDgQH.js.br +0 -0
  345. package/dist/public/assets/_BMlgaRlM.js +0 -1
  346. package/dist/public/assets/_BOBYB-cj.js.br +0 -0
  347. package/dist/public/assets/_BPNSknLT.js.br +0 -0
  348. package/dist/public/assets/_BQEpu56f.js.br +0 -0
  349. package/dist/public/assets/_BT7DMail.js.br +0 -0
  350. package/dist/public/assets/_BTJHQNIz.js +0 -1
  351. package/dist/public/assets/_BTWfPOi-.js.br +0 -0
  352. package/dist/public/assets/_BTh4v5Tr.js.br +0 -0
  353. package/dist/public/assets/_BUW_V5kN.js.br +0 -0
  354. package/dist/public/assets/_BXcMBVCY.js.br +0 -0
  355. package/dist/public/assets/_B_MtJKnj.js +0 -1
  356. package/dist/public/assets/_Bay3D1io.js +0 -1
  357. package/dist/public/assets/_BfQeZPXW.js.br +0 -0
  358. package/dist/public/assets/_BgKjJWz_.js +0 -1
  359. package/dist/public/assets/_BiY8chB-.js.br +0 -0
  360. package/dist/public/assets/_Bk5THQZb.js +0 -1
  361. package/dist/public/assets/_BmeQRnXS.js +0 -1
  362. package/dist/public/assets/_BoEcMQmX.js.br +0 -0
  363. package/dist/public/assets/_BoTzWlXR.js.br +0 -0
  364. package/dist/public/assets/_BqWlTTxo.js.br +0 -0
  365. package/dist/public/assets/_BuMTHEPk.js +0 -1
  366. package/dist/public/assets/_BuMTHEPk.js.br +0 -0
  367. package/dist/public/assets/_BubhcEjq.js +0 -1
  368. package/dist/public/assets/_BwSXxzqy.js.br +0 -0
  369. package/dist/public/assets/_BxS3xvmE.js.br +0 -0
  370. package/dist/public/assets/_ByvA6zJz.js +0 -1
  371. package/dist/public/assets/_Bz2Sejcy.js +0 -1
  372. package/dist/public/assets/_C-lB_p3U.js.br +0 -0
  373. package/dist/public/assets/_C1-Nli75.js.br +0 -0
  374. package/dist/public/assets/_C3mw4T_r.js +0 -1
  375. package/dist/public/assets/_C449plW0.js.br +0 -0
  376. package/dist/public/assets/_C5c3o9N3.js.br +0 -0
  377. package/dist/public/assets/_C7bHTlG1.js.br +0 -0
  378. package/dist/public/assets/_C8mt6nRC.js +0 -1
  379. package/dist/public/assets/_C9jR1P3s.js.br +0 -0
  380. package/dist/public/assets/_CCONYsJT.js.br +0 -0
  381. package/dist/public/assets/_CCZAROAW.js +0 -1
  382. package/dist/public/assets/_CCZAROAW.js.br +0 -0
  383. package/dist/public/assets/_CG3s9pE5.js +0 -1
  384. package/dist/public/assets/_CIWiehYG.js.br +0 -0
  385. package/dist/public/assets/_CIrZGxEf.js.br +0 -0
  386. package/dist/public/assets/_CIurl9jC.js +0 -1
  387. package/dist/public/assets/_CJgqLdsx.js.br +0 -0
  388. package/dist/public/assets/_CMbVHIgP.js +0 -1
  389. package/dist/public/assets/_CNUs2Gqk.js.br +0 -0
  390. package/dist/public/assets/_CPbClwhX.js.br +0 -0
  391. package/dist/public/assets/_CQUlxerJ.js +0 -1
  392. package/dist/public/assets/_CTDYlYHi.js +0 -1
  393. package/dist/public/assets/_CXVkXsw1.js.br +0 -0
  394. package/dist/public/assets/_CY5EBxZ6.js +0 -1
  395. package/dist/public/assets/_CYQ4srZb.js.br +0 -0
  396. package/dist/public/assets/_CY_Z-kV5.js.br +0 -0
  397. package/dist/public/assets/_CYhJ6ae-.js.br +0 -0
  398. package/dist/public/assets/_CZbVK94d.js +0 -1
  399. package/dist/public/assets/_C_A97h_2.js +0 -1
  400. package/dist/public/assets/_C_A97h_2.js.br +0 -0
  401. package/dist/public/assets/_C_Y5Dwzy.js +0 -1
  402. package/dist/public/assets/_Ca2DTddL.js.br +0 -0
  403. package/dist/public/assets/_Cc2nu3_5.js +0 -1
  404. package/dist/public/assets/_Cc_GUPR4.js.br +0 -0
  405. package/dist/public/assets/_CgFUs0bE.js +0 -1
  406. package/dist/public/assets/_Ch-UiovX.js.br +0 -0
  407. package/dist/public/assets/_ChDp0-FJ.js +0 -1
  408. package/dist/public/assets/_ChPy2IX4.js.br +0 -0
  409. package/dist/public/assets/_Chsgo2e1.js +0 -1
  410. package/dist/public/assets/_CjECwrWB.js +0 -1
  411. package/dist/public/assets/_CmW74dH7.js +0 -1
  412. package/dist/public/assets/_CmhAUlqu.js +0 -1
  413. package/dist/public/assets/_CrlNjDih.js.br +0 -0
  414. package/dist/public/assets/_CvzTEaQq.js +0 -1
  415. package/dist/public/assets/_Cw-dmwPJ.js +0 -86
  416. package/dist/public/assets/_Cw-dmwPJ.js.br +0 -0
  417. package/dist/public/assets/_Cwtn28hL.js.br +0 -0
  418. package/dist/public/assets/_CxHuLHdz.js +0 -1
  419. package/dist/public/assets/_CxgPm_xy.js.br +0 -0
  420. package/dist/public/assets/_D-0pUYXq.js.br +0 -0
  421. package/dist/public/assets/_D-rV-z_T.js +0 -1
  422. package/dist/public/assets/_D4CkNo0r.js.br +0 -0
  423. package/dist/public/assets/_D4fP_viq.js +0 -1
  424. package/dist/public/assets/_D570Sd6p.js +0 -1
  425. package/dist/public/assets/_D6Gudpw8.js +0 -1
  426. package/dist/public/assets/_D8ZTP1ck.js +0 -1
  427. package/dist/public/assets/_D8uBvHXU.js.br +0 -0
  428. package/dist/public/assets/_D8xGEGlm.js +0 -1
  429. package/dist/public/assets/_D9GZHKow.js +0 -1
  430. package/dist/public/assets/_DE0eZjad.js +0 -1
  431. package/dist/public/assets/_DElIEG9n.js +0 -1
  432. package/dist/public/assets/_DF0n9xzn.js +0 -2
  433. package/dist/public/assets/_DF0n9xzn.js.br +0 -0
  434. package/dist/public/assets/_DFX_tXcw.js +0 -1
  435. package/dist/public/assets/_DJ2gfhU_.js +0 -1
  436. package/dist/public/assets/_DK-MOyi_.js.br +0 -0
  437. package/dist/public/assets/_DOKecL0e.js.br +0 -0
  438. package/dist/public/assets/_DPXXIOLX.js +0 -1
  439. package/dist/public/assets/_DPb3t58B.js +0 -1
  440. package/dist/public/assets/_DPiemRX1.js.br +0 -0
  441. package/dist/public/assets/_DPpbgqBC.js +0 -1
  442. package/dist/public/assets/_DRVSLjxA.js +0 -1
  443. package/dist/public/assets/_DTHWnu6M.js +0 -1
  444. package/dist/public/assets/_DUdEYxV0.js.br +0 -0
  445. package/dist/public/assets/_DVLNzEkW.js.br +0 -0
  446. package/dist/public/assets/_DWpx2Uof.js +0 -1
  447. package/dist/public/assets/_DXYEUAmC.js +0 -1
  448. package/dist/public/assets/_DYTVambU.js +0 -1
  449. package/dist/public/assets/_DYqx8g78.js.br +0 -0
  450. package/dist/public/assets/_DZanHI_X.js.br +0 -0
  451. package/dist/public/assets/_D_GeNCls.js.br +0 -0
  452. package/dist/public/assets/_DbLQjG4R.js.br +0 -0
  453. package/dist/public/assets/_DbmusecK.js +0 -1
  454. package/dist/public/assets/_DbmusecK.js.br +0 -0
  455. package/dist/public/assets/_DemN-D5U.js +0 -1
  456. package/dist/public/assets/_Depr5Jps.js.br +0 -0
  457. package/dist/public/assets/_DhJfHI4f.js +0 -1
  458. package/dist/public/assets/_Dhn3A3KJ.js.br +0 -0
  459. package/dist/public/assets/_Di78jTI-.js.br +0 -0
  460. package/dist/public/assets/_DiBTtPlu.js +0 -1
  461. package/dist/public/assets/_Dj5RDA5P.js.br +0 -0
  462. package/dist/public/assets/_DkjKARSS.js +0 -1
  463. package/dist/public/assets/_Do6b-nfX.js.br +0 -0
  464. package/dist/public/assets/_DorQTaAa.js.br +0 -0
  465. package/dist/public/assets/_DpxVmwni.js +0 -1
  466. package/dist/public/assets/_DqGOYRpi.js.br +0 -0
  467. package/dist/public/assets/_DsbxQ1i8.js +0 -1
  468. package/dist/public/assets/_Dt_P7K1V.js +0 -6
  469. package/dist/public/assets/_Dt_P7K1V.js.br +0 -0
  470. package/dist/public/assets/_Dwe1TUjY.js.br +0 -0
  471. package/dist/public/assets/_DyROQKS8.js +0 -1
  472. package/dist/public/assets/_DzEUPsq0.js +0 -1
  473. package/dist/public/assets/_DzlXwaiG.js.br +0 -0
  474. package/dist/public/assets/_FwFz43m8.js.br +0 -0
  475. package/dist/public/assets/_OpbuiQ7h.js.br +0 -0
  476. package/dist/public/assets/_Q5s6o5H4.js.br +0 -0
  477. package/dist/public/assets/_Quh6tl1u.js.br +0 -0
  478. package/dist/public/assets/_Rh99sjDe.js +0 -1
  479. package/dist/public/assets/_SEE1XtID.js +0 -1
  480. package/dist/public/assets/_SZjr1kid.js +0 -1
  481. package/dist/public/assets/_T7rMqM5P.js +0 -1
  482. package/dist/public/assets/_TynjiShF.js.br +0 -0
  483. package/dist/public/assets/_UENAEFcY.js +0 -1
  484. package/dist/public/assets/_UwTOb1gP.js +0 -1
  485. package/dist/public/assets/_WrxtT1Ok.js +0 -1
  486. package/dist/public/assets/_WrxtT1Ok.js.br +0 -0
  487. package/dist/public/assets/_bcvRRCkB.js +0 -1
  488. package/dist/public/assets/_cyorvuL1.js.br +0 -0
  489. package/dist/public/assets/_gi--JOq8.js +0 -1
  490. package/dist/public/assets/_i4iteLF8.js.br +0 -0
  491. package/dist/public/assets/_i7vMugBN.js +0 -1
  492. package/dist/public/assets/_j7jPRUEQ.js +0 -2
  493. package/dist/public/assets/_j7jPRUEQ.js.br +0 -0
  494. package/dist/public/assets/_pEUCjhqm.js.br +0 -0
  495. package/dist/public/assets/_qkJuNTl-.js +0 -1
  496. package/dist/public/assets/_uuZJDLIV.js +0 -1
  497. package/dist/public/assets/_v3HPnNBs.js.br +0 -0
  498. package/dist/public/assets/_vX9d6g5Y.js.br +0 -0
  499. package/dist/public/assets/_vhysM7gk.js +0 -1
  500. package/dist/public/assets/_vjwPQHk_.js +0 -1
  501. package/dist/public/assets/_yp48NYOW.js.br +0 -0
  502. package/dist/public/assets/_zcAaK79d.js.br +0 -0
  503. package/dist/public/assets/_zwPrEKxN.js +0 -1
  504. package/dist/public/assets/_zwPrEKxN.js.br +0 -0
  505. package/dist/public/assets/files-view-CzRAql9-.js +0 -2
  506. package/dist/public/assets/manage-workspace-C-c1R4pP.js +0 -2
  507. package/dist/public/assets/new-DWfNwssk.js +0 -1
  508. package/dist/public/assets/new-workspace-B8XAfkIH.js +0 -2
  509. package/dist/public/assets/review-view-DivHZNBf.js +0 -2
  510. package/dist/public/assets/settings-DWfNwssk.js +0 -1
  511. package/dist/public/assets/settings-page-BN9Hj1ui.js +0 -2
  512. package/dist/public/assets/styles-NmwxQh5L.css +0 -1
  513. package/dist/public/assets/styles-NmwxQh5L.css.br +0 -0
  514. package/dist/public/assets/w-DWfNwssk.js +0 -1
  515. package/dist/public/assets/w._-C9dj1aZ0.js +0 -1
  516. package/dist/public/assets/w._-D7_D4HlM.js +0 -1
  517. package/dist/public/assets/w._-DWfNwssk.js +0 -1
package/bin/work.mjs CHANGED
@@ -6,17 +6,26 @@
6
6
  // it otherwise offers when Tailscale is logged in; --no-tailscale skips that;
7
7
  // --firewall adds a `work` rule for direct connections' UDP port to a host
8
8
  // firewall that may drop it, or on macOS lets the service's node in,
9
- // which it otherwise offers; --no-firewall skips that)
9
+ // which it otherwise offers; --no-firewall skips that;
10
+ // --allow-host <name> / --disallow-host <name>, repeated, put one name
11
+ // in or out of WORK_ALLOWED_HOSTS and keep the rest; --json prints
12
+ // what it did as JSON on stdout, its progress on stderr)
10
13
  // work doctor report machine readiness (--json, --fix to install what's missing)
11
14
  // work start run the server in the foreground
12
- // work update upgrade the npm package and refresh the service
15
+ // work update upgrade the npm package and refresh the service (--yes, --json as setup's)
13
16
  // work usage print agent plan/quota usage
14
17
  // work workspaces list workspaces (name, branch, sessions, ports)
15
- // work workspace [id|name] one workspace: repos, PRs, sessions, procs (default: the current one)
18
+ // work workspace [id|name] one workspace: repos, PRs, sessions, procs (default: the current one;
19
+ // --fresh asks GitHub again rather than the PR cache, ~2 min old)
16
20
  // work repos [id|name] a workspace's repos with base branch and open PRs
17
- // work procs [id|name] a workspace's running processes and ports
18
- // work review [id|name] pending review comments as markdown (--json for rows)
19
- // work export [file] write a backup manifest (workspaces, settings, skills, tool pins)
21
+ // work procs [id|name] a workspace's running processes and ports, each port with
22
+ // the link that opens it from the user's browser (through the
23
+ // address it last reached work by)
24
+ // work review [id|name] pending review comments as markdown (--json for rows;
25
+ // --take also clears exactly those from the review, as its
26
+ // Send does, once acted on, so a later Send cannot repeat them)
27
+ // work export [file] write a backup manifest (workspaces, settings, skills, tool pins;
28
+ // --json says what it wrote)
20
29
  // work restore <file> bring a manifest's workspaces and machine setup back (--yes to run;
21
30
  // --allow <spec|host>, repeated, for each tool outside work's catalog
22
31
  // or new GitHub host it names — skipped otherwise)
@@ -26,7 +35,13 @@
26
35
  //
27
36
  // The read commands ask the running server (same data as the UI) and take
28
37
  // --json for machine-readable output — agents inside a workspace should
29
- // use them instead of poking at the filesystem or GitHub.
38
+ // use them instead of poking at the filesystem or GitHub. Nothing prompts
39
+ // under --json, --non-interactive or WORK_NONINTERACTIVE=1: a question is
40
+ // answered no, as --yes answers setup's optional ones.
41
+ //
42
+ // Commands beyond these live one file each under bin/cli/ (a file there
43
+ // exports `commands`; see bin/cli/README.md), found at run time, and
44
+ // `work help` lists them all: this header's own lines, then theirs.
30
45
  import { execFileSync, spawnSync } from "node:child_process";
31
46
  import fs from "node:fs";
32
47
  import os from "node:os";
@@ -55,6 +70,7 @@ import { dialHost, listenStream } from "../lib/setup/listen.mjs";
55
70
  import { commentMarkdown } from "../lib/review/markdown.mjs";
56
71
  import { formatInvite, parseInvite } from "../lib/share/manifest.mjs";
57
72
  import { duration, pctText } from "../lib/usage/format.mjs";
73
+ import { locOf, portBlockedAt, portUrlAt } from "../lib/ports/url.mjs";
58
74
 
59
75
  const pkgRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
60
76
  const pkg = JSON.parse(
@@ -63,7 +79,10 @@ const pkg = JSON.parse(
63
79
  const distEntry = path.join(pkgRoot, "dist", "index.mjs");
64
80
  const appPort = process.env.WORK_APP_PORT || "7171";
65
81
 
66
- const log = (msg) => console.log(`==> ${msg}`);
82
+ // Under --json, stdout is the JSON alone: what a command says along the
83
+ // way goes to stderr.
84
+ const log = (msg) => (jsonOut ? console.error : console.log)(`==> ${msg}`);
85
+ const say = (msg) => (jsonOut ? console.error : console.log)(msg);
67
86
  const warn = (msg) => console.warn(`warning: ${msg}`);
68
87
  const die = (msg) => {
69
88
  console.error(`error: ${msg}`);
@@ -79,6 +98,12 @@ const sh = (cmd, args, opts = {}) =>
79
98
  const [, , command = "help", ...args] = process.argv;
80
99
  const yes = args.includes("--yes") || args.includes("-y");
81
100
  const jsonOut = args.includes("--json");
101
+ /** Whether a question may be asked at all: an agent's shell often has a
102
+ * controlling terminal it cannot answer on, and a prompt there hangs it. */
103
+ const interactive =
104
+ !jsonOut &&
105
+ !args.includes("--non-interactive") &&
106
+ !process.env.WORK_NONINTERACTIVE;
82
107
  /** `--name <value>` style flags take the next arg; keep it out of the
83
108
  * positionals so `work join --name x` still reads stdin. */
84
109
  const VALUED = new Set(["--name", "--allow"]);
@@ -101,7 +126,20 @@ const flagValues = (flag) =>
101
126
  return [args[i + 1]];
102
127
  });
103
128
 
104
- switch (command) {
129
+ // A command of bin/cli/ (`workspace create`: a word of the core's, then
130
+ // its own) is looked for first, so one can extend a core command with
131
+ // verbs of its own without a case here.
132
+ const cli = await cliCommand(command, positional[0]);
133
+
134
+ switch (cli ? "" : command) {
135
+ case "":
136
+ await cli.cmd.run(cliContext(cli.words));
137
+ break;
138
+ case "help":
139
+ case "--help":
140
+ case "-h":
141
+ await helpCmd(positional[0]);
142
+ break;
105
143
  case "start":
106
144
  await start();
107
145
  break;
@@ -169,9 +207,10 @@ switch (command) {
169
207
  console.log(pkg.version);
170
208
  break;
171
209
  default:
172
- console.log(
173
- `work ${pkg.version}\n\nusage: work <setup|install-service|doctor|start|update|usage|workspaces|workspace|repos|procs|review|export|restore|share|join|version> [--json]`,
174
- );
210
+ // A typo must fail: an agent reads the exit code, and usage on stdout
211
+ // with 0 read as the command having run.
212
+ console.error(`error: no command \`${command}\` (see: work help)`);
213
+ process.exitCode = 2;
175
214
  }
176
215
 
177
216
  async function start() {
@@ -232,18 +271,38 @@ function serverUrl() {
232
271
  return `http://${dialHost(env.WORK_LISTEN)}:${env.PORT}`;
233
272
  }
234
273
 
235
- async function apiGet(path) {
274
+ /** One request to the running server: its JSON, or the command dies with
275
+ * the server's own reason (`{error}`), never only its status. */
276
+ async function apiCall(method, path, body, timeoutMs = 30_000) {
236
277
  const port = serviceEnv().PORT;
278
+ let res;
237
279
  try {
238
- const res = await fetch(`${serverUrl()}${path}`, {
239
- signal: AbortSignal.timeout(30_000),
280
+ res = await fetch(`${serverUrl()}${path}`, {
281
+ method,
282
+ headers:
283
+ body === undefined ? undefined : { "content-type": "application/json" },
284
+ body: body === undefined ? undefined : JSON.stringify(body),
285
+ signal: AbortSignal.timeout(timeoutMs),
240
286
  });
241
- if (!res.ok) die(`server answered ${res.status} for ${path}`);
242
- return await res.json();
243
- } catch (err) {
244
- if (err?.message?.startsWith("server answered")) throw err;
287
+ } catch {
245
288
  die(`no work server on port ${port}. Start one with: work start`);
246
289
  }
290
+ const text = await res.text();
291
+ let data = null;
292
+ try {
293
+ data = text ? JSON.parse(text) : null;
294
+ } catch {}
295
+ if (!res.ok || (data && typeof data === "object" && data.error)) {
296
+ die(
297
+ (data && data.error) ||
298
+ `server answered ${res.status} for ${method} ${path}`,
299
+ );
300
+ }
301
+ return data;
302
+ }
303
+
304
+ function apiGet(path) {
305
+ return apiCall("GET", path);
247
306
  }
248
307
 
249
308
  /**
@@ -255,7 +314,7 @@ async function apiGet(path) {
255
314
  async function resolveWorkspace(arg) {
256
315
  const [list, status] = await Promise.all([
257
316
  apiGet("/api/workspaces"),
258
- apiGet("/api/status"),
317
+ apiGet(statusPath()),
259
318
  ]);
260
319
  let ws;
261
320
  if (arg) {
@@ -294,10 +353,17 @@ function pad(s, n) {
294
353
  return String(s ?? "").padEnd(n);
295
354
  }
296
355
 
356
+ /** The status snapshot, or with --fresh one that asks GitHub again: a PR
357
+ * made a moment ago (`gh pr create`) is otherwise missing for as long as
358
+ * the PR cache keeps its last answer. */
359
+ function statusPath() {
360
+ return args.includes("--fresh") ? "/api/status?fresh" : "/api/status";
361
+ }
362
+
297
363
  async function workspacesCmd() {
298
364
  const [list, status] = await Promise.all([
299
365
  apiGet("/api/workspaces"),
300
- apiGet("/api/status"),
366
+ apiGet(statusPath()),
301
367
  ]);
302
368
  const rows = list.map((w) => ({ ...w, ...(status[w.id] || {}) }));
303
369
  if (jsonOut) return console.log(JSON.stringify(rows, null, 2));
@@ -323,21 +389,26 @@ function prLine(pr) {
323
389
  : pr.isDraft
324
390
  ? "draft"
325
391
  : "open";
326
- return `#${pr.number} ${state} ${pr.title} ${pr.url}`;
392
+ return `#${pr.number} ${state} ${pr.title}${pr.headRefName ? ` (${pr.headRefName})` : ""} ${pr.url}`;
327
393
  }
328
394
 
329
395
  async function workspaceCmd() {
330
396
  const { ws, status } = await resolveWorkspace(positional[0]);
331
397
  const procs = await apiGet(`/api/workspaces/${ws.id}/procs`).catch(() => []);
332
- const [sessions] = await Promise.all([
398
+ const [sessions, linkPort] = await Promise.all([
333
399
  apiGet(`/api/workspaces/${ws.id}/sessions`).catch(() => []),
400
+ portLinker(),
334
401
  ]);
402
+ const me = process.env.WORK_SESSION_ID;
335
403
  const out = {
336
404
  ...ws,
337
- ports: status.ports,
405
+ ports: (status.ports || []).map((p) => ({
406
+ ...p,
407
+ ...linkPort(p.port, p.local),
408
+ })),
338
409
  repos: mergeRepos(ws, status),
339
- sessions,
340
- procs,
410
+ sessions: sessions.map((s) => (s.id === me ? { ...s, self: true } : s)),
411
+ procs: procs.map((p) => withUrls(p, linkPort)),
341
412
  };
342
413
  if (jsonOut) return console.log(JSON.stringify(out, null, 2));
343
414
  console.log(`${ws.name} (${ws.status})`);
@@ -352,16 +423,58 @@ async function workspaceCmd() {
352
423
  console.log(` sessions:`);
353
424
  for (const s of sessions) {
354
425
  const phase = s.state?.phase || (s.state?.alive ? "idle" : "off");
355
- console.log(` ${pad(s.agent, 10)} ${pad(phase, 8)} ${s.title || ""}`);
426
+ console.log(
427
+ ` ${pad(s.id, 9)} ${pad(s.agent, 10)} ${pad(phase, 8)} ${s.title || ""}${s.id === me ? " (this session)" : ""}`,
428
+ );
356
429
  }
357
430
  }
358
431
  if (procs.length) {
359
432
  console.log(` running:`);
360
- for (const p of procs)
361
- console.log(
362
- ` ${pad(p.pid, 7)} ${p.cmd}${p.ports?.length ? " " + p.ports.map((x) => `:${x}`).join(" ") : ""}`,
363
- );
364
- }
433
+ for (const p of out.procs)
434
+ console.log(` ${pad(p.pid, 7)} ${p.cmd}${portsText(p)}`);
435
+ }
436
+ }
437
+
438
+ /** How a port opens for the user: through the address their browser last
439
+ * reached work by (`/api/boot`'s `origin`) as the page's port chip would
440
+ * (lib/ports/url), or this machine's own when no browser has yet. `blocked`
441
+ * says why that link cannot work (a port bound to loopback, a page on a
442
+ * direct address). */
443
+ async function portLinker() {
444
+ const boot = await apiGet("/api/boot");
445
+ const loc = boot.origin ? locOf(boot.origin) : null;
446
+ return (port, local) => {
447
+ if (!loc) return { url: `http://localhost:${port}` };
448
+ const blocked = portBlockedAt(port, local, loc);
449
+ return {
450
+ url: portUrlAt(port, loc, boot.portHosts),
451
+ ...(blocked && { blocked }),
452
+ };
453
+ };
454
+ }
455
+
456
+ /** A process with each of its ports' links (`urls`, by port). */
457
+ function withUrls(p, linkPort) {
458
+ if (!p.ports?.length) return p;
459
+ const local = new Set(p.local || []);
460
+ return {
461
+ ...p,
462
+ urls: Object.fromEntries(
463
+ p.ports.map((port) => [port, linkPort(port, local.has(port))]),
464
+ ),
465
+ };
466
+ }
467
+
468
+ /** A process's ports for a line of text, each with where it opens. */
469
+ function portsText(p) {
470
+ if (!p.ports?.length) return "";
471
+ return ` ${p.ports
472
+ .map((port) => {
473
+ const link = p.urls?.[port];
474
+ if (!link) return `:${port}`;
475
+ return `:${port} ${link.url}${link.blocked ? ` (${link.blocked})` : ""}`;
476
+ })
477
+ .join(" ")}`;
365
478
  }
366
479
 
367
480
  /** Workspace repos joined with the status snapshot's PRs per repo. */
@@ -387,13 +500,15 @@ async function reposCmd() {
387
500
 
388
501
  async function procsCmd() {
389
502
  const { ws } = await resolveWorkspace(positional[0]);
390
- const procs = await apiGet(`/api/workspaces/${ws.id}/procs`);
503
+ const [raw, linkPort] = await Promise.all([
504
+ apiGet(`/api/workspaces/${ws.id}/procs`),
505
+ portLinker(),
506
+ ]);
507
+ const procs = raw.map((p) => withUrls(p, linkPort));
391
508
  if (jsonOut) return console.log(JSON.stringify(procs, null, 2));
392
509
  if (!procs.length) return console.log("nothing running");
393
510
  for (const p of procs)
394
- console.log(
395
- `${pad(p.pid, 7)} ${p.cmd}${p.ports?.length ? " " + p.ports.map((x) => `:${x}`).join(" ") : ""}`,
396
- );
511
+ console.log(`${pad(p.pid, 7)} ${p.cmd}${portsText(p)}`);
397
512
  }
398
513
 
399
514
  /** Comments left on the review page but not yet sent: an agent can pull
@@ -402,9 +517,23 @@ async function procsCmd() {
402
517
  async function reviewCmd() {
403
518
  const { ws } = await resolveWorkspace(positional[0]);
404
519
  const { comments = [] } = await apiGet(`/api/workspaces/${ws.id}/review`);
405
- if (jsonOut) return console.log(JSON.stringify(comments, null, 2));
406
- if (!comments.length) return console.log("no pending review comments");
407
- console.log(commentMarkdown(comments, await reviewSections(ws.id, comments)));
520
+ if (jsonOut) console.log(JSON.stringify(comments, null, 2));
521
+ else if (!comments.length) console.log("no pending review comments");
522
+ else
523
+ console.log(
524
+ commentMarkdown(comments, await reviewSections(ws.id, comments)),
525
+ );
526
+ // Taken, the comments leave the review as a Send takes them: exactly the
527
+ // ones printed, so one written meanwhile stays for the next look.
528
+ if (args.includes("--take") && comments.length) {
529
+ await apiPost(`/api/workspaces/${ws.id}/review`, {
530
+ action: "clear",
531
+ ids: comments.map((c) => c.id),
532
+ });
533
+ console.error(
534
+ `took ${comments.length} comment${comments.length === 1 ? "" : "s"} off the review`,
535
+ );
536
+ }
408
537
  }
409
538
 
410
539
  /** The diff's sections, which say which lane or PR a comment's lines are
@@ -413,41 +542,25 @@ async function reviewCmd() {
413
542
  * comments then read as the checkout's rather than failing the command. */
414
543
  async function reviewSections(wsId, comments) {
415
544
  if (!comments.some((c) => c.head)) return null;
416
- const port = serviceEnv().PORT;
417
545
  try {
418
- const res = await fetch(
419
- `http://127.0.0.1:${port}/api/workspaces/${wsId}/diff`,
420
- { signal: AbortSignal.timeout(60_000) },
421
- );
546
+ const res = await fetch(`${serverUrl()}/api/workspaces/${wsId}/diff`, {
547
+ signal: AbortSignal.timeout(60_000),
548
+ });
422
549
  return res.ok ? ((await res.json()).sections ?? null) : null;
423
550
  } catch {
424
551
  return null;
425
552
  }
426
553
  }
427
554
 
428
- async function apiPost(path, body) {
429
- const port = serviceEnv().PORT;
430
- let res;
431
- try {
432
- res = await fetch(`${serverUrl()}${path}`, {
433
- method: "POST",
434
- headers: { "content-type": "application/json" },
435
- body: body === undefined ? undefined : JSON.stringify(body),
436
- signal: AbortSignal.timeout(15 * 60_000),
437
- });
438
- } catch {
439
- die(`no work server on port ${port}. Start one with: work start`);
440
- }
441
- const data = await res.json().catch(() => ({}));
442
- if (!res.ok || data.error)
443
- die(data.error || `server answered ${res.status} for ${path}`);
444
- return data;
555
+ /** A write: as long as a clone can take (a create, a restore). */
556
+ function apiPost(path, body) {
557
+ return apiCall("POST", path, body ?? {}, 15 * 60_000);
445
558
  }
446
559
 
447
560
  /** One line per workspace/repo of what the file will not carry. */
448
561
  function printPreflight(found) {
449
562
  for (const w of found.workspaces) {
450
- console.log(w.name);
563
+ say(w.name);
451
564
  for (const r of w.repos) {
452
565
  const parts = [];
453
566
  for (const b of r.unpushed)
@@ -456,10 +569,10 @@ function printPreflight(found) {
456
569
  if (r.untracked.length) parts.push(`${r.untracked.length} untracked`);
457
570
  if (r.ignored.length) parts.push(`${r.ignored.length} ignored`);
458
571
  if (r.more) parts.push(`+${r.more} more`);
459
- console.log(` ${r.worktree}/ ${parts.join(", ")}`);
460
- for (const f of r.modified) console.log(` M ${f}`);
461
- for (const f of r.untracked) console.log(` ? ${f}`);
462
- for (const f of r.ignored) console.log(` ! ${f}`);
572
+ say(` ${r.worktree}/ ${parts.join(", ")}`);
573
+ for (const f of r.modified) say(` M ${f}`);
574
+ for (const f of r.untracked) say(` ? ${f}`);
575
+ for (const f of r.ignored) say(` ! ${f}`);
463
576
  }
464
577
  }
465
578
  }
@@ -477,7 +590,7 @@ async function exportCmd() {
477
590
  );
478
591
  }
479
592
  if (found.workspaces.length) {
480
- console.log("not in the backup:");
593
+ say("not in the backup:");
481
594
  printPreflight(found);
482
595
  if (!yes) {
483
596
  console.log("");
@@ -498,6 +611,18 @@ async function exportCmd() {
498
611
  "work-backup.json";
499
612
  fs.writeFileSync(name, await res.text());
500
613
  log(`wrote ${name}`);
614
+ if (jsonOut)
615
+ console.log(
616
+ JSON.stringify(
617
+ {
618
+ file: path.resolve(name),
619
+ contents: c ?? null,
620
+ left_out: found.workspaces,
621
+ },
622
+ null,
623
+ 2,
624
+ ),
625
+ );
501
626
  }
502
627
 
503
628
  function readManifest(file) {
@@ -658,6 +783,8 @@ function until(iso) {
658
783
  }
659
784
 
660
785
  async function setup() {
786
+ // First: a bad name fails before anything is touched.
787
+ editAllowedHosts();
661
788
  await bridge();
662
789
  const d = await doctor(doctorOpts());
663
790
  await fix(d);
@@ -670,13 +797,30 @@ async function setup() {
670
797
  const port = serviceEnv().PORT;
671
798
  if (!tailnet) maybeTunnel(port);
672
799
  if (running) {
673
- if (await waitForServer()) log(`done — http://localhost:${port}`);
674
- else
800
+ const answered = await waitForServer();
801
+ if (answered) log(`done — http://localhost:${port}`);
802
+ else {
675
803
  warn(
676
804
  `service started but nothing answered on port ${port} within 30s — check the logs`,
677
805
  );
806
+ // A setup that left no server is a failed one, to whatever ran it.
807
+ process.exitCode = 1;
808
+ }
678
809
  if (tailnet)
679
810
  log(`on your phone and other devices on the tailnet: ${tailnet}`);
811
+ if (jsonOut)
812
+ console.log(
813
+ JSON.stringify(
814
+ {
815
+ ok: answered,
816
+ version: pkg.version,
817
+ url: `http://localhost:${port}`,
818
+ tailnet: tailnet || null,
819
+ },
820
+ null,
821
+ 2,
822
+ ),
823
+ );
680
824
  } else {
681
825
  // A fresh install's shell may not have mise on PATH yet — name the shim.
682
826
  const shim = path.join(
@@ -694,6 +838,34 @@ async function setup() {
694
838
  }
695
839
  }
696
840
 
841
+ /** `--allow-host <name>` / `--disallow-host <name>`, each repeatable: one
842
+ * name in or out of the service's WORK_ALLOWED_HOSTS, the rest kept —
843
+ * `WORK_ALLOWED_HOSTS=… work setup` replaces the whole list, which an
844
+ * agent adding one proxy's name would otherwise have to read back first.
845
+ * Applied before anything writes the service, which carries what this
846
+ * leaves in the environment (`serviceEnv`). */
847
+ function editAllowedHosts() {
848
+ const allow = flagValues("--allow-host");
849
+ const disallow = flagValues("--disallow-host");
850
+ if (!allow.length && !disallow.length) return;
851
+ for (const name of [...allow, ...disallow]) {
852
+ if (!/^[A-Za-z0-9.-]+$/.test(name))
853
+ die(
854
+ `not a host name: ${name} (one name per flag, e.g. work.example.com)`,
855
+ );
856
+ }
857
+ const before = serviceEnv().WORK_ALLOWED_HOSTS || "";
858
+ let next = before;
859
+ for (const name of allow) next = allowHost(next, name, true);
860
+ for (const name of disallow) next = allowHost(next, name, false);
861
+ process.env.WORK_ALLOWED_HOSTS = next;
862
+ log(
863
+ next === before
864
+ ? `WORK_ALLOWED_HOSTS unchanged: ${next || "(none)"}`
865
+ : `WORK_ALLOWED_HOSTS=${next || "(none)"}`,
866
+ );
867
+ }
868
+
697
869
  /** Bring a database from before 0.1 to the baseline this release starts
698
870
  * from (lib/setup/bridge.mjs), before anything touches the service: the
699
871
  * server it would restart refuses such a database. Run with the settings
@@ -745,22 +917,35 @@ async function update() {
745
917
  // `--prefer-online`: npm serves `latest` from a cached packument for a
746
918
  // few minutes after a publish, so an update right behind a release
747
919
  // reinstalled the version already running.
748
- sh("npm", [
749
- "install",
750
- "-g",
751
- "--prefer-online",
752
- ...allowScriptsArgs(),
753
- "@dylanpiercey/work@latest",
754
- ]);
920
+ // Under --json, npm's own output goes to stderr: stdout is setup's JSON.
921
+ const stdio = jsonOut ? ["inherit", 2, 2] : "inherit";
922
+ sh(
923
+ "npm",
924
+ [
925
+ "install",
926
+ "-g",
927
+ "--prefer-online",
928
+ ...allowScriptsArgs(),
929
+ "@dylanpiercey/work@latest",
930
+ ],
931
+ { stdio },
932
+ );
755
933
  // Re-exec the (possibly new) CLI to finish with fresh code.
756
934
  sh("work", [
757
935
  "setup",
758
936
  ...(yes ? ["--yes"] : []),
759
937
  ...args.filter((a) =>
760
- ["--tailscale", "--no-tailscale", "--firewall", "--no-firewall"].includes(
761
- a,
762
- ),
938
+ [
939
+ "--tailscale",
940
+ "--no-tailscale",
941
+ "--firewall",
942
+ "--no-firewall",
943
+ "--json",
944
+ "--non-interactive",
945
+ ].includes(a),
763
946
  ),
947
+ ...flagValues("--allow-host").flatMap((h) => ["--allow-host", h]),
948
+ ...flagValues("--disallow-host").flatMap((h) => ["--disallow-host", h]),
764
949
  ]);
765
950
  }
766
951
 
@@ -976,7 +1161,7 @@ async function fix(d) {
976
1161
  }
977
1162
  log(`installing ${tool.label} via ${tool.mise ? "mise" : "gh"}`);
978
1163
  try {
979
- await installTool(tool.id, { log: (l) => console.log(` ${l}`) });
1164
+ await installTool(tool.id, { log: (l) => say(` ${l}`) });
980
1165
  } catch (err) {
981
1166
  if (tool.required) die(`${tool.label} install failed: ${err.message}`);
982
1167
  warn(`${tool.label} install failed (non-fatal)`);
@@ -1741,6 +1926,7 @@ function allowTailnet(serve) {
1741
1926
  * controlling terminal, since the curl installer pipes its script into the
1742
1927
  * shell this runs under. Null with no terminal at all. */
1743
1928
  function terminal() {
1929
+ if (!interactive) return null;
1744
1930
  if (process.stdin.isTTY) return process.stdin;
1745
1931
  try {
1746
1932
  return new tty.ReadStream(fs.openSync("/dev/tty", "r"));
@@ -1763,3 +1949,139 @@ async function confirm(input, question) {
1763
1949
  rl.close();
1764
1950
  }
1765
1951
  }
1952
+
1953
+ // -- commands of bin/cli/ and help ------------------------------------------
1954
+
1955
+ /** Every command a file of bin/cli/ exports (`_`-prefixed files are its
1956
+ * helpers), loaded once. (Kept on the function: the dispatch above runs
1957
+ * before a top-level `let` here would be initialised.) */
1958
+ async function cliCommands() {
1959
+ if (cliCommands.cache) return cliCommands.cache;
1960
+ const dir = path.join(pkgRoot, "bin", "cli");
1961
+ const files = fs.existsSync(dir)
1962
+ ? fs
1963
+ .readdirSync(dir)
1964
+ .filter((f) => f.endsWith(".mjs") && !f.startsWith("_"))
1965
+ .sort()
1966
+ : [];
1967
+ const all = [];
1968
+ for (const f of files) {
1969
+ const mod = await import(path.join(dir, f));
1970
+ for (const c of mod.commands || []) all.push(c);
1971
+ }
1972
+ cliCommands.cache = all;
1973
+ return all;
1974
+ }
1975
+
1976
+ /** A core command's other name, which a bin/cli/ command's first word may
1977
+ * be spelled with (`work ws create` is `workspace create`). */
1978
+ function canonical(word) {
1979
+ return word === "ws" ? "workspace" : word;
1980
+ }
1981
+
1982
+ /** The bin/cli/ command `argv` names: two words (`workspace create`)
1983
+ * before one, so a verb of its own wins over the word alone. */
1984
+ async function cliCommand(first, second) {
1985
+ const all = await cliCommands();
1986
+ const head = canonical(first);
1987
+ const names = (c) => [c.name, ...(c.aliases || [])];
1988
+ if (second) {
1989
+ const two = `${head} ${second}`;
1990
+ const cmd = all.find((c) => names(c).includes(two));
1991
+ if (cmd) return { cmd, words: 2 };
1992
+ }
1993
+ const cmd = all.find((c) => names(c).includes(head));
1994
+ return cmd ? { cmd, words: 1 } : null;
1995
+ }
1996
+
1997
+ /** What a bin/cli/ command is handed: its own argv, the CLI's flags, and
1998
+ * the helpers every command shares — the running server (`api`), which
1999
+ * workspace is meant, and how to say something or fail. */
2000
+ function cliContext(words) {
2001
+ const own = args.slice(0);
2002
+ // The words that named it are not its positionals.
2003
+ const pos = positional.slice(words - 1);
2004
+ return {
2005
+ pkg,
2006
+ args: own,
2007
+ positional: pos,
2008
+ json: jsonOut,
2009
+ yes,
2010
+ has: (flag) => args.includes(flag),
2011
+ flag: flagValue,
2012
+ flags: flagValues,
2013
+ /** Positionals with `valued` flags' values left out (`--agent x`). */
2014
+ parse(valued = []) {
2015
+ const set = new Set([...VALUED, ...valued]);
2016
+ return args
2017
+ .filter(
2018
+ (a, i) => a === "-" || (!a.startsWith("-") && !set.has(args[i - 1])),
2019
+ )
2020
+ .slice(words - 1);
2021
+ },
2022
+ api: {
2023
+ get: (p) => apiCall("GET", p),
2024
+ post: (p, body) => apiPost(p, body),
2025
+ put: (p, body) => apiCall("PUT", p, body ?? {}),
2026
+ patch: (p, body) => apiCall("PATCH", p, body ?? {}),
2027
+ del: (p, body) => apiCall("DELETE", p, body),
2028
+ },
2029
+ serverUrl,
2030
+ resolveWorkspace,
2031
+ /** `data` as JSON under --json, else what `text` prints. */
2032
+ out(data, text) {
2033
+ if (jsonOut) console.log(JSON.stringify(data, null, 2));
2034
+ else text?.(data);
2035
+ },
2036
+ pad,
2037
+ log,
2038
+ warn,
2039
+ die,
2040
+ };
2041
+ }
2042
+
2043
+ /** The header's own lines for the core commands: `work <name>` and what
2044
+ * continues it, as written at the top of this file. */
2045
+ function coreHelp() {
2046
+ const src = fs.readFileSync(fileURLToPath(import.meta.url), "utf8");
2047
+ const lines = [];
2048
+ for (const line of src.split("\n").slice(1)) {
2049
+ if (!line.startsWith("//")) break;
2050
+ const text = line.replace(/^\/\/ ?/, "");
2051
+ // The list starts at its first `work …` line and ends at a blank one.
2052
+ if (!lines.length && !/^ {2}work /.test(text)) continue;
2053
+ if (!text.trim()) break;
2054
+ lines.push(text.replace(/^ {2}/, ""));
2055
+ }
2056
+ return lines;
2057
+ }
2058
+
2059
+ /** `work help`: every command, the core's then bin/cli/'s; `work help
2060
+ * <cmd>` that one's whole text. */
2061
+ async function helpCmd(topic) {
2062
+ const all = await cliCommands();
2063
+ const core = coreHelp();
2064
+ if (topic) {
2065
+ const head = canonical(topic);
2066
+ const own = all.filter(
2067
+ (c) => c.name === head || c.name.startsWith(`${head} `),
2068
+ );
2069
+ const start = core.findIndex((l) => new RegExp(`^work ${head}\\b`).test(l));
2070
+ if (start < 0 && !own.length)
2071
+ die(`no command \`${topic}\` (see: work help)`);
2072
+ if (start >= 0) {
2073
+ let end = start + 1;
2074
+ while (end < core.length && !core[end].startsWith("work ")) end++;
2075
+ console.log(core.slice(start, end).join("\n"));
2076
+ }
2077
+ for (const c of own) console.log(`\n${c.help || c.summary}`);
2078
+ return;
2079
+ }
2080
+ console.log(`work ${pkg.version}\n`);
2081
+ console.log(core.join("\n"));
2082
+ for (const c of all) console.log(`work ${c.name.padEnd(22)} ${c.summary}`);
2083
+ console.log(
2084
+ "\nwork help <command> for one command's whole text. Most take --json;",
2085
+ );
2086
+ console.log("under it stdout is JSON alone and progress goes to stderr.");
2087
+ }