@dylanpiercey/work 0.2.17 → 0.2.19

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 +16 -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 +296 -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 +411 -80
  21. package/dist/{_Bj-WwfnW.js → _CsokArOR.js} +183 -59
  22. package/dist/_L2HNAA_8.js +2 -0
  23. package/dist/__TCdgPY5.js +173 -0
  24. package/dist/index.mjs +3917 -2606
  25. package/dist/public/assets/{_C9jR1P3s.js → _-7Amnss4.js} +1 -1
  26. package/dist/public/assets/_-7Amnss4.js.br +0 -0
  27. package/dist/public/assets/_0XXNUttq.js +1 -0
  28. package/dist/public/assets/{_CIrZGxEf.js → _0sTkxfPx.js} +1 -1
  29. package/dist/public/assets/_0sTkxfPx.js.br +0 -0
  30. package/dist/public/assets/_0zzYbK0A.js +1 -0
  31. package/dist/public/assets/{_CJgqLdsx.js → _1M3oZPSf.js} +1 -1
  32. package/dist/public/assets/_1M3oZPSf.js.br +0 -0
  33. package/dist/public/assets/_2O5HnHht.js +1 -0
  34. package/dist/public/assets/_7WlUaJPH.js +1 -0
  35. package/dist/public/assets/_8Tr7ByPg.js +1 -0
  36. package/dist/public/assets/_9AXMmQSO.js +1 -0
  37. package/dist/public/assets/_B1VqbV-X.js +1 -0
  38. package/dist/public/assets/{_BxS3xvmE.js → _B9I2AEux.js} +1 -1
  39. package/dist/public/assets/_B9I2AEux.js.br +0 -0
  40. package/dist/public/assets/{_DO_4_u2Q.js → _B9qGqvit.js} +1 -1
  41. package/dist/public/assets/{_CIWiehYG.js → _B9xuaMHt.js} +1 -1
  42. package/dist/public/assets/_B9xuaMHt.js.br +0 -0
  43. package/dist/public/assets/{_BqWlTTxo.js → _BAWjkhA8.js} +1 -1
  44. package/dist/public/assets/_BAWjkhA8.js.br +0 -0
  45. package/dist/public/assets/{_CNUs2Gqk.js → _BBCzcNZY.js} +1 -1
  46. package/dist/public/assets/_BBCzcNZY.js.br +0 -0
  47. package/dist/public/assets/_BBW2szHh.js +1 -0
  48. package/dist/public/assets/_BCP2Njnu.js +1 -0
  49. package/dist/public/assets/{_A-jpjc8j.js → _BClUYs-k.js} +1 -1
  50. package/dist/public/assets/_BClUYs-k.js.br +0 -0
  51. package/dist/public/assets/{_B5JxE4Sx.js → _BDdZkZRc.js} +1 -1
  52. package/dist/public/assets/_BDdZkZRc.js.br +0 -0
  53. package/dist/public/assets/_BEDmGSgS.js +1 -0
  54. package/dist/public/assets/_BEDmGSgS.js.br +0 -0
  55. package/dist/public/assets/_BH9BIky6.js +1 -0
  56. package/dist/public/assets/_BH9BIky6.js.br +0 -0
  57. package/dist/public/assets/_BLdi8Ee5.js +1 -0
  58. package/dist/public/assets/{_C449plW0.js → _BPDSQUbl.js} +1 -1
  59. package/dist/public/assets/_BPDSQUbl.js.br +0 -0
  60. package/dist/public/assets/{_B19mS3yM.js → _BQW1kIop.js} +1 -1
  61. package/dist/public/assets/_BQW1kIop.js.br +0 -0
  62. package/dist/public/assets/_BQeoUfFw.js +1 -0
  63. package/dist/public/assets/_BRzroLbh.js +1 -0
  64. package/dist/public/assets/_BSXsTYSe.js +1 -0
  65. package/dist/public/assets/_BTXDT3Na.js +1 -0
  66. package/dist/public/assets/_BXm4xeFI.js +1 -0
  67. package/dist/public/assets/_BY8UeogK.js +1 -0
  68. package/dist/public/assets/_BYpyhp64.js +1 -0
  69. package/dist/public/assets/_BYpyhp64.js.br +0 -0
  70. package/dist/public/assets/_B_Kz7LnY.js +1 -0
  71. package/dist/public/assets/{_DqGOYRpi.js → _Bcc0Xs64.js} +1 -1
  72. package/dist/public/assets/_Bcc0Xs64.js.br +0 -0
  73. package/dist/public/assets/{_Do6b-nfX.js → _BdSsUhCi.js} +1 -1
  74. package/dist/public/assets/_BdSsUhCi.js.br +0 -0
  75. package/dist/public/assets/_BdaWssEH.js +1 -0
  76. package/dist/public/assets/{_BHyWcuOR.js → _BhecGD7y.js} +1 -1
  77. package/dist/public/assets/_BhecGD7y.js.br +0 -0
  78. package/dist/public/assets/_Bj9dpPhN.js +1 -0
  79. package/dist/public/assets/{_DorQTaAa.js → _BjZqS7za.js} +1 -1
  80. package/dist/public/assets/_BjZqS7za.js.br +0 -0
  81. package/dist/public/assets/_BkSUiyHK.js +1 -0
  82. package/dist/public/assets/_BlshXH2Y.js +16 -0
  83. package/dist/public/assets/_BlshXH2Y.js.br +0 -0
  84. package/dist/public/assets/_BmX-VP7w.js +1 -0
  85. package/dist/public/assets/{_v3HPnNBs.js → _BmpOmFbo.js} +1 -1
  86. package/dist/public/assets/_BmpOmFbo.js.br +0 -0
  87. package/dist/public/assets/_Bo6YntUl.js +1 -0
  88. package/dist/public/assets/_BprZx4vg.js +2 -0
  89. package/dist/public/assets/_BprZx4vg.js.br +0 -0
  90. package/dist/public/assets/_Bqm7OnIV.js +1 -0
  91. package/dist/public/assets/_BquBGUNK.js +1 -0
  92. package/dist/public/assets/_BquBGUNK.js.br +0 -0
  93. package/dist/public/assets/_BrB66aRZ.js +1 -0
  94. package/dist/public/assets/{_BA-MNdjL.js → _Bt03HU8S.js} +1 -1
  95. package/dist/public/assets/{_BTWfPOi-.js → _BtedGrGs.js} +1 -1
  96. package/dist/public/assets/_BtedGrGs.js.br +0 -0
  97. package/dist/public/assets/{_DUdEYxV0.js → _C-BYkx5h.js} +1 -1
  98. package/dist/public/assets/_C-BYkx5h.js.br +0 -0
  99. package/dist/public/assets/{_BUW_V5kN.js → _C-DhXiEm.js} +1 -1
  100. package/dist/public/assets/_C-DhXiEm.js.br +0 -0
  101. package/dist/public/assets/_C-NBFnST.js +1 -0
  102. package/dist/public/assets/{_BoTzWlXR.js → _C1RigMYD.js} +1 -1
  103. package/dist/public/assets/_C1RigMYD.js.br +0 -0
  104. package/dist/public/assets/_C2vRS-ES.js +1 -0
  105. package/dist/public/assets/{_CrlNjDih.js → _C3fslTAD.js} +1 -1
  106. package/dist/public/assets/_C3fslTAD.js.br +0 -0
  107. package/dist/public/assets/{_BuMTHEPk.js → _C4EYjWGn.js} +1 -1
  108. package/dist/public/assets/_C4EYjWGn.js.br +0 -0
  109. package/dist/public/assets/{_yp48NYOW.js → _C6Yt6h_R.js} +1 -1
  110. package/dist/public/assets/_C6Yt6h_R.js.br +0 -0
  111. package/dist/public/assets/{_cyorvuL1.js → _C7Y4xApK.js} +1 -1
  112. package/dist/public/assets/_C7Y4xApK.js.br +0 -0
  113. package/dist/public/assets/{_D4CkNo0r.js → _CB52LkYt.js} +1 -1
  114. package/dist/public/assets/_CB52LkYt.js.br +0 -0
  115. package/dist/public/assets/{_CxgPm_xy.js → _CBEKHwUV.js} +1 -1
  116. package/dist/public/assets/_CBEKHwUV.js.br +0 -0
  117. package/dist/public/assets/_CBSLBYQF.js +1 -0
  118. package/dist/public/assets/_CBgzBB_k.js +1 -0
  119. package/dist/public/assets/_CBgzBB_k.js.br +0 -0
  120. package/dist/public/assets/{_CCONYsJT.js → _CCoTlnvb.js} +1 -1
  121. package/dist/public/assets/_CCoTlnvb.js.br +0 -0
  122. package/dist/public/assets/_CD26To-f.js +1 -0
  123. package/dist/public/assets/_CDyWgQ0v.js +6 -0
  124. package/dist/public/assets/_CDyWgQ0v.js.br +0 -0
  125. package/dist/public/assets/{_vX9d6g5Y.js → _CEC5O89D.js} +1 -1
  126. package/dist/public/assets/_CEC5O89D.js.br +0 -0
  127. package/dist/public/assets/_CF2kEAU7.js +1 -0
  128. package/dist/public/assets/_CFURqbmO.js +1 -0
  129. package/dist/public/assets/{_Cwtn28hL.js → _CG383oZY.js} +1 -1
  130. package/dist/public/assets/_CG383oZY.js.br +0 -0
  131. package/dist/public/assets/_CHNJosg3.js +1 -0
  132. package/dist/public/assets/{_Cc_GUPR4.js → _CHmEM8Sm.js} +1 -1
  133. package/dist/public/assets/_CHmEM8Sm.js.br +0 -0
  134. package/dist/public/assets/_CIfCdAzS.js +1 -0
  135. package/dist/public/assets/_CKyzH0fl.js +1 -0
  136. package/dist/public/assets/{_BCB8LQS3.js → _CL8DzBla.js} +1 -1
  137. package/dist/public/assets/_CL8DzBla.js.br +0 -0
  138. package/dist/public/assets/_CNYS94C5.js +1 -0
  139. package/dist/public/assets/_CNhypHJN.js +1 -0
  140. package/dist/public/assets/_CO-FkxlA.js +1 -0
  141. package/dist/public/assets/_COiGBSpO.js +1 -0
  142. package/dist/public/assets/_COvolvRQ.js +1 -0
  143. package/dist/public/assets/{_Di78jTI-.js → _CPaMvBMG.js} +1 -1
  144. package/dist/public/assets/_CPaMvBMG.js.br +0 -0
  145. package/dist/public/assets/{_C7bHTlG1.js → _CPj_VL66.js} +1 -1
  146. package/dist/public/assets/_CPj_VL66.js.br +0 -0
  147. package/dist/public/assets/_CQJC8Vx4.js +1 -0
  148. package/dist/public/assets/_CRHNlzBC.js +1 -0
  149. package/dist/public/assets/{_C1-Nli75.js → _CToqok6Q.js} +1 -1
  150. package/dist/public/assets/_CToqok6Q.js.br +0 -0
  151. package/dist/public/assets/_CUFKkl0B.js +1 -0
  152. package/dist/public/assets/_CZsOUa-q.js +1 -0
  153. package/dist/public/assets/{_D8uBvHXU.js → _C_D5TPb7.js} +1 -1
  154. package/dist/public/assets/_C_D5TPb7.js.br +0 -0
  155. package/dist/public/assets/_CdHI48v6.js +1 -0
  156. package/dist/public/assets/{_BfQeZPXW.js → _CdpmkkzR.js} +1 -1
  157. package/dist/public/assets/_CdpmkkzR.js.br +0 -0
  158. package/dist/public/assets/{_6klEiBhY.js → _CdtjIoUd.js} +1 -1
  159. package/dist/public/assets/_CdtjIoUd.js.br +0 -0
  160. package/dist/public/assets/_Ce9Tv01m.js +1 -0
  161. package/dist/public/assets/_CeC7Lv10.js +1 -0
  162. package/dist/public/assets/{_CYhJ6ae-.js → _CfP0f0PU.js} +1 -1
  163. package/dist/public/assets/_CfP0f0PU.js.br +0 -0
  164. package/dist/public/assets/_Cg7DH3xk.js +1 -0
  165. package/dist/public/assets/_Cg7DH3xk.js.br +0 -0
  166. package/dist/public/assets/_CikNq0z8.js +1 -0
  167. package/dist/public/assets/{_DK-MOyi_.js → _Cl7LTITm.js} +1 -1
  168. package/dist/public/assets/_Cl7LTITm.js.br +0 -0
  169. package/dist/public/assets/{_BT7DMail.js → _Cmtl59QM.js} +1 -1
  170. package/dist/public/assets/_Cmtl59QM.js.br +0 -0
  171. package/dist/public/assets/_CshYq_Dp.js +1 -0
  172. package/dist/public/assets/{_Dj5RDA5P.js → _CspPbDoP.js} +1 -1
  173. package/dist/public/assets/_CspPbDoP.js.br +0 -0
  174. package/dist/public/assets/{_Ca2DTddL.js → _Ctjz3BD9.js} +1 -1
  175. package/dist/public/assets/_Ctjz3BD9.js.br +0 -0
  176. package/dist/public/assets/{_C5c3o9N3.js → _CuglAToi.js} +1 -1
  177. package/dist/public/assets/_CuglAToi.js.br +0 -0
  178. package/dist/public/assets/_CwSoAVRN.js +1 -0
  179. package/dist/public/assets/_D0XQV-Hm.js +1 -0
  180. package/dist/public/assets/{_D_GeNCls.js → _D36PnqCD.js} +1 -1
  181. package/dist/public/assets/_D36PnqCD.js.br +0 -0
  182. package/dist/public/assets/{_DYqx8g78.js → _D3IviZbJ.js} +1 -1
  183. package/dist/public/assets/_D3IviZbJ.js.br +0 -0
  184. package/dist/public/assets/_D6rgUB9O.js +1 -0
  185. package/dist/public/assets/{_Dwe1TUjY.js → _D7qWWxdI.js} +1 -1
  186. package/dist/public/assets/_D7qWWxdI.js.br +0 -0
  187. package/dist/public/assets/{_CPbClwhX.js → _D8ac72_O.js} +1 -1
  188. package/dist/public/assets/_D8ac72_O.js.br +0 -0
  189. package/dist/public/assets/_D9WWxrQR.js +1 -0
  190. package/dist/public/assets/_D9hI5dSA.js +1 -0
  191. package/dist/public/assets/{_DzlXwaiG.js → _DCrPsldZ.js} +1 -1
  192. package/dist/public/assets/_DCrPsldZ.js.br +0 -0
  193. package/dist/public/assets/{_ChPy2IX4.js → _DDcVFyaQ.js} +1 -1
  194. package/dist/public/assets/_DDcVFyaQ.js.br +0 -0
  195. package/dist/public/assets/_DDhkKo8Q.js +1 -0
  196. package/dist/public/assets/{_CY_Z-kV5.js → _DJBR8Qwq.js} +1 -1
  197. package/dist/public/assets/_DJBR8Qwq.js.br +0 -0
  198. package/dist/public/assets/{_CxHuLHdz.js → _DLpa6VYT.js} +1 -1
  199. package/dist/public/assets/_DMqe6rR-.js +1 -0
  200. package/dist/public/assets/{_DbLQjG4R.js → _DN7pTn2g.js} +1 -1
  201. package/dist/public/assets/_DN7pTn2g.js.br +0 -0
  202. package/dist/public/assets/_DP8UuFuJ.js +1 -0
  203. package/dist/public/assets/_DQedUaqM.js +1 -0
  204. package/dist/public/assets/{_DPiemRX1.js → _DSCqWPO0.js} +1 -1
  205. package/dist/public/assets/_DSCqWPO0.js.br +0 -0
  206. package/dist/public/assets/{_BoEcMQmX.js → _DSeQngGR.js} +1 -1
  207. package/dist/public/assets/_DSeQngGR.js.br +0 -0
  208. package/dist/public/assets/{_D-0pUYXq.js → _DTNiWZFr.js} +1 -1
  209. package/dist/public/assets/_DTNiWZFr.js.br +0 -0
  210. package/dist/public/assets/_DThlusXY.js +1 -0
  211. package/dist/public/assets/_DUK16H19.js +1 -0
  212. package/dist/public/assets/_DUZ13t3U.js +1 -0
  213. package/dist/public/assets/{_BHgwN0LC.js → _DWb03VKf.js} +1 -1
  214. package/dist/public/assets/_DWb03VKf.js.br +0 -0
  215. package/dist/public/assets/_DWsyhfgn.js +1 -0
  216. package/dist/public/assets/_DXuzYhW6.js +1 -0
  217. package/dist/public/assets/_DXyOzA2b.js +1 -0
  218. package/dist/public/assets/{_BXcMBVCY.js → _DYUFCi44.js} +1 -1
  219. package/dist/public/assets/_DYUFCi44.js.br +0 -0
  220. package/dist/public/assets/{_BwSXxzqy.js → _DYhs8iJg.js} +12 -12
  221. package/dist/public/assets/_DYhs8iJg.js.br +0 -0
  222. package/dist/public/assets/{_Dhn3A3KJ.js → _D__ojGam.js} +1 -1
  223. package/dist/public/assets/_D__ojGam.js.br +0 -0
  224. package/dist/public/assets/_DaFI_W5D.js +1 -0
  225. package/dist/public/assets/{_BiY8chB-.js → _DbT6uo7W.js} +1 -1
  226. package/dist/public/assets/_DbT6uo7W.js.br +0 -0
  227. package/dist/public/assets/_DhnT0cCE.js +1 -0
  228. package/dist/public/assets/_Dm9OGn2q.js +1 -0
  229. package/dist/public/assets/_DmkeGFoM.js +1 -0
  230. package/dist/public/assets/{_DVLNzEkW.js → _DmoI4cIl.js} +1 -1
  231. package/dist/public/assets/_DmoI4cIl.js.br +0 -0
  232. package/dist/public/assets/{_zcAaK79d.js → _DpbHp8yR.js} +1 -1
  233. package/dist/public/assets/_DpbHp8yR.js.br +0 -0
  234. package/dist/public/assets/_Dqz_HBE1.js +1 -0
  235. package/dist/public/assets/{_DOKecL0e.js → _DsehEuMD.js} +1 -1
  236. package/dist/public/assets/_DsehEuMD.js.br +0 -0
  237. package/dist/public/assets/{_D-pUpBL1.js → _Dtx52FYB.js} +1 -1
  238. package/dist/public/assets/_DuEyirL6.js +1 -0
  239. package/dist/public/assets/_DuuloSIi.js +1 -0
  240. package/dist/public/assets/{_Ch-UiovX.js → _DuyCZnNC.js} +1 -1
  241. package/dist/public/assets/_DuyCZnNC.js.br +0 -0
  242. package/dist/public/assets/{_BQEpu56f.js → _Dw1P6xZJ.js} +1 -1
  243. package/dist/public/assets/_Dw1P6xZJ.js.br +0 -0
  244. package/dist/public/assets/{_C-lB_p3U.js → _DzNFlI0M.js} +1 -1
  245. package/dist/public/assets/_DzNFlI0M.js.br +0 -0
  246. package/dist/public/assets/{_CYQ4srZb.js → _EcMqP1xI.js} +1 -1
  247. package/dist/public/assets/_EcMqP1xI.js.br +0 -0
  248. package/dist/public/assets/{_0KruTMn9.js → _G1BLeMns.js} +1 -1
  249. package/dist/public/assets/_G1BLeMns.js.br +0 -0
  250. package/dist/public/assets/{_FwFz43m8.js → _G8KNSVdM.js} +1 -1
  251. package/dist/public/assets/_G8KNSVdM.js.br +0 -0
  252. package/dist/public/assets/{_3fvr5bU1.js → _J1v2clxU.js} +1 -1
  253. package/dist/public/assets/_J1v2clxU.js.br +0 -0
  254. package/dist/public/assets/{_Depr5Jps.js → _J3wdy6bJ.js} +1 -1
  255. package/dist/public/assets/_J3wdy6bJ.js.br +0 -0
  256. package/dist/public/assets/{_-ycNE92Z.js → _MHZgY1AQ.js} +1 -1
  257. package/dist/public/assets/_MHZgY1AQ.js.br +0 -0
  258. package/dist/public/assets/_NHTZitq1.js +2 -0
  259. package/dist/public/assets/_NHTZitq1.js.br +0 -0
  260. package/dist/public/assets/{_Cw-dmwPJ.js → _PsuNqRL9.js} +20 -20
  261. package/dist/public/assets/_PsuNqRL9.js.br +0 -0
  262. package/dist/public/assets/{_CXVkXsw1.js → _T5HFHZgl.js} +1 -1
  263. package/dist/public/assets/_T5HFHZgl.js.br +0 -0
  264. package/dist/public/assets/{_4_sKOYsy.js → _UZAFx6D8.js} +1 -1
  265. package/dist/public/assets/_UZAFx6D8.js.br +0 -0
  266. package/dist/public/assets/_XA8_RQXF.js +1 -0
  267. package/dist/public/assets/{_BTh4v5Tr.js → _XsB9KmcW.js} +1 -1
  268. package/dist/public/assets/_XsB9KmcW.js.br +0 -0
  269. package/dist/public/assets/{_i4iteLF8.js → _YoXk_8Bl.js} +1 -1
  270. package/dist/public/assets/_YoXk_8Bl.js.br +0 -0
  271. package/dist/public/assets/_YubCdy3Z.js +1 -0
  272. package/dist/public/assets/{_Q5s6o5H4.js → _bDI-pCZR.js} +1 -1
  273. package/dist/public/assets/_bDI-pCZR.js.br +0 -0
  274. package/dist/public/assets/{_TynjiShF.js → _bHL-NcWS.js} +1 -1
  275. package/dist/public/assets/_bHL-NcWS.js.br +0 -0
  276. package/dist/public/assets/_bqRzDYc8.js +1 -0
  277. package/dist/public/assets/{_OpbuiQ7h.js → _briyaWgK.js} +1 -1
  278. package/dist/public/assets/_briyaWgK.js.br +0 -0
  279. package/dist/public/assets/{_pEUCjhqm.js → _dVHSDgPC.js} +1 -1
  280. package/dist/public/assets/_dVHSDgPC.js.br +0 -0
  281. package/dist/public/assets/_gkZjZ9I4.js +1 -0
  282. package/dist/public/assets/_plyCYAnN.js +1 -0
  283. package/dist/public/assets/_q0Z44mWJ.js +1 -0
  284. package/dist/public/assets/_q0Z44mWJ.js.br +0 -0
  285. package/dist/public/assets/{_BPNSknLT.js → _qAsGEOt5.js} +1 -1
  286. package/dist/public/assets/_qAsGEOt5.js.br +0 -0
  287. package/dist/public/assets/{_Dks8YY2K.js → _qxffRpjc.js} +1 -1
  288. package/dist/public/assets/{_BOBYB-cj.js → _uCKs5N6Q.js} +1 -1
  289. package/dist/public/assets/_uCKs5N6Q.js.br +0 -0
  290. package/dist/public/assets/_utWnC80Q.js +1 -0
  291. package/dist/public/assets/_utvzcBHT.js +1 -0
  292. package/dist/public/assets/_v8WbCPI8.js +1 -0
  293. package/dist/public/assets/_yK4b_Bl5.js +1 -0
  294. package/dist/public/assets/_yoQW4BY5.js +1 -0
  295. package/dist/public/assets/_zB6USXHX.js +1 -0
  296. package/dist/public/assets/{_Quh6tl1u.js → _zN8SypHW.js} +1 -1
  297. package/dist/public/assets/_zN8SypHW.js.br +0 -0
  298. package/dist/public/assets/files-view-Fl0rU3Ns.js +2 -0
  299. package/dist/public/assets/manage-workspace-DggRcsXf.js +2 -0
  300. package/dist/public/assets/new-C7NxTJAT.js +1 -0
  301. package/dist/public/assets/new-workspace-C0pw5Iz1.js +2 -0
  302. package/dist/public/assets/review-view-C4YQ0J-X.js +2 -0
  303. package/dist/public/assets/settings-C7NxTJAT.js +1 -0
  304. package/dist/public/assets/settings-page-Cl_1tWCv.js +2 -0
  305. package/dist/public/assets/styles-Vcsj4CA7.css +1 -0
  306. package/dist/public/assets/styles-Vcsj4CA7.css.br +0 -0
  307. package/dist/public/assets/w-C7NxTJAT.js +1 -0
  308. package/dist/public/assets/w._-BbFH2lR-.js +1 -0
  309. package/dist/public/assets/w._-C7NxTJAT.js +1 -0
  310. package/dist/public/assets/w._-YIzLOiyZ.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/lib/preview/media.d.mts +1 -1
  316. package/lib/preview/media.mjs +15 -2
  317. package/package.json +1 -1
  318. package/dist/_CLoLdV87.js +0 -2
  319. package/dist/_DPpY3UqO.js +0 -167
  320. package/dist/public/assets/_-ycNE92Z.js.br +0 -0
  321. package/dist/public/assets/_0KruTMn9.js.br +0 -0
  322. package/dist/public/assets/_0NH5yJMx.js +0 -1
  323. package/dist/public/assets/_1CAp-NbR.js +0 -1
  324. package/dist/public/assets/_3fvr5bU1.js.br +0 -0
  325. package/dist/public/assets/_4Dkmi0BI.js +0 -1
  326. package/dist/public/assets/_4_sKOYsy.js.br +0 -0
  327. package/dist/public/assets/_6klEiBhY.js.br +0 -0
  328. package/dist/public/assets/_7_64OpLh.js +0 -1
  329. package/dist/public/assets/_7p-zxUs0.js +0 -1
  330. package/dist/public/assets/_8RAv3gFP.js +0 -1
  331. package/dist/public/assets/_9PDxaMA9.js +0 -1
  332. package/dist/public/assets/_9PDxaMA9.js.br +0 -0
  333. package/dist/public/assets/_9mbDrbe7.js +0 -1
  334. package/dist/public/assets/_A-jpjc8j.js.br +0 -0
  335. package/dist/public/assets/_B0JwTeu6.js +0 -1
  336. package/dist/public/assets/_B19mS3yM.js.br +0 -0
  337. package/dist/public/assets/_B1Sfrnda.js +0 -1
  338. package/dist/public/assets/_B3i0wS_I.js +0 -1
  339. package/dist/public/assets/_B5JxE4Sx.js.br +0 -0
  340. package/dist/public/assets/_B9RLcC8I.js +0 -1
  341. package/dist/public/assets/_BCB8LQS3.js.br +0 -0
  342. package/dist/public/assets/_BE7UYyYb.js +0 -1
  343. package/dist/public/assets/_BHgwN0LC.js.br +0 -0
  344. package/dist/public/assets/_BHyWcuOR.js.br +0 -0
  345. package/dist/public/assets/_BIpUDgQH.js +0 -16
  346. package/dist/public/assets/_BIpUDgQH.js.br +0 -0
  347. package/dist/public/assets/_BMlgaRlM.js +0 -1
  348. package/dist/public/assets/_BOBYB-cj.js.br +0 -0
  349. package/dist/public/assets/_BPNSknLT.js.br +0 -0
  350. package/dist/public/assets/_BQEpu56f.js.br +0 -0
  351. package/dist/public/assets/_BT7DMail.js.br +0 -0
  352. package/dist/public/assets/_BTJHQNIz.js +0 -1
  353. package/dist/public/assets/_BTWfPOi-.js.br +0 -0
  354. package/dist/public/assets/_BTh4v5Tr.js.br +0 -0
  355. package/dist/public/assets/_BUW_V5kN.js.br +0 -0
  356. package/dist/public/assets/_BXcMBVCY.js.br +0 -0
  357. package/dist/public/assets/_B_MtJKnj.js +0 -1
  358. package/dist/public/assets/_Bay3D1io.js +0 -1
  359. package/dist/public/assets/_BfQeZPXW.js.br +0 -0
  360. package/dist/public/assets/_BgKjJWz_.js +0 -1
  361. package/dist/public/assets/_BiY8chB-.js.br +0 -0
  362. package/dist/public/assets/_Bk5THQZb.js +0 -1
  363. package/dist/public/assets/_BmeQRnXS.js +0 -1
  364. package/dist/public/assets/_BoEcMQmX.js.br +0 -0
  365. package/dist/public/assets/_BoTzWlXR.js.br +0 -0
  366. package/dist/public/assets/_BqWlTTxo.js.br +0 -0
  367. package/dist/public/assets/_BuMTHEPk.js.br +0 -0
  368. package/dist/public/assets/_BubhcEjq.js +0 -1
  369. package/dist/public/assets/_BwSXxzqy.js.br +0 -0
  370. package/dist/public/assets/_BxS3xvmE.js.br +0 -0
  371. package/dist/public/assets/_ByvA6zJz.js +0 -1
  372. package/dist/public/assets/_Bz2Sejcy.js +0 -1
  373. package/dist/public/assets/_C-lB_p3U.js.br +0 -0
  374. package/dist/public/assets/_C1-Nli75.js.br +0 -0
  375. package/dist/public/assets/_C3mw4T_r.js +0 -1
  376. package/dist/public/assets/_C449plW0.js.br +0 -0
  377. package/dist/public/assets/_C5c3o9N3.js.br +0 -0
  378. package/dist/public/assets/_C7bHTlG1.js.br +0 -0
  379. package/dist/public/assets/_C8mt6nRC.js +0 -1
  380. package/dist/public/assets/_C9jR1P3s.js.br +0 -0
  381. package/dist/public/assets/_CCONYsJT.js.br +0 -0
  382. package/dist/public/assets/_CCZAROAW.js +0 -1
  383. package/dist/public/assets/_CCZAROAW.js.br +0 -0
  384. package/dist/public/assets/_CG3s9pE5.js +0 -1
  385. package/dist/public/assets/_CIWiehYG.js.br +0 -0
  386. package/dist/public/assets/_CIrZGxEf.js.br +0 -0
  387. package/dist/public/assets/_CIurl9jC.js +0 -1
  388. package/dist/public/assets/_CJgqLdsx.js.br +0 -0
  389. package/dist/public/assets/_CMbVHIgP.js +0 -1
  390. package/dist/public/assets/_CNUs2Gqk.js.br +0 -0
  391. package/dist/public/assets/_CPbClwhX.js.br +0 -0
  392. package/dist/public/assets/_CQUlxerJ.js +0 -1
  393. package/dist/public/assets/_CTDYlYHi.js +0 -1
  394. package/dist/public/assets/_CXVkXsw1.js.br +0 -0
  395. package/dist/public/assets/_CY5EBxZ6.js +0 -1
  396. package/dist/public/assets/_CYQ4srZb.js.br +0 -0
  397. package/dist/public/assets/_CY_Z-kV5.js.br +0 -0
  398. package/dist/public/assets/_CYhJ6ae-.js.br +0 -0
  399. package/dist/public/assets/_CZbVK94d.js +0 -1
  400. package/dist/public/assets/_C_A97h_2.js +0 -1
  401. package/dist/public/assets/_C_A97h_2.js.br +0 -0
  402. package/dist/public/assets/_C_Y5Dwzy.js +0 -1
  403. package/dist/public/assets/_Ca2DTddL.js.br +0 -0
  404. package/dist/public/assets/_Cc2nu3_5.js +0 -1
  405. package/dist/public/assets/_Cc_GUPR4.js.br +0 -0
  406. package/dist/public/assets/_CgFUs0bE.js +0 -1
  407. package/dist/public/assets/_Ch-UiovX.js.br +0 -0
  408. package/dist/public/assets/_ChDp0-FJ.js +0 -1
  409. package/dist/public/assets/_ChPy2IX4.js.br +0 -0
  410. package/dist/public/assets/_Chsgo2e1.js +0 -1
  411. package/dist/public/assets/_CjECwrWB.js +0 -1
  412. package/dist/public/assets/_CmW74dH7.js +0 -1
  413. package/dist/public/assets/_CmhAUlqu.js +0 -1
  414. package/dist/public/assets/_CrlNjDih.js.br +0 -0
  415. package/dist/public/assets/_CvzTEaQq.js +0 -1
  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/_CxgPm_xy.js.br +0 -0
  419. package/dist/public/assets/_D-0pUYXq.js.br +0 -0
  420. package/dist/public/assets/_D-rV-z_T.js +0 -1
  421. package/dist/public/assets/_D4CkNo0r.js.br +0 -0
  422. package/dist/public/assets/_D4fP_viq.js +0 -1
  423. package/dist/public/assets/_D570Sd6p.js +0 -1
  424. package/dist/public/assets/_D6Gudpw8.js +0 -1
  425. package/dist/public/assets/_D8ZTP1ck.js +0 -1
  426. package/dist/public/assets/_D8uBvHXU.js.br +0 -0
  427. package/dist/public/assets/_D8xGEGlm.js +0 -1
  428. package/dist/public/assets/_D9GZHKow.js +0 -1
  429. package/dist/public/assets/_DE0eZjad.js +0 -1
  430. package/dist/public/assets/_DElIEG9n.js +0 -1
  431. package/dist/public/assets/_DF0n9xzn.js +0 -2
  432. package/dist/public/assets/_DF0n9xzn.js.br +0 -0
  433. package/dist/public/assets/_DFX_tXcw.js +0 -1
  434. package/dist/public/assets/_DJ2gfhU_.js +0 -1
  435. package/dist/public/assets/_DK-MOyi_.js.br +0 -0
  436. package/dist/public/assets/_DOKecL0e.js.br +0 -0
  437. package/dist/public/assets/_DPXXIOLX.js +0 -1
  438. package/dist/public/assets/_DPb3t58B.js +0 -1
  439. package/dist/public/assets/_DPiemRX1.js.br +0 -0
  440. package/dist/public/assets/_DPpbgqBC.js +0 -1
  441. package/dist/public/assets/_DRVSLjxA.js +0 -1
  442. package/dist/public/assets/_DTHWnu6M.js +0 -1
  443. package/dist/public/assets/_DUdEYxV0.js.br +0 -0
  444. package/dist/public/assets/_DVLNzEkW.js.br +0 -0
  445. package/dist/public/assets/_DWpx2Uof.js +0 -1
  446. package/dist/public/assets/_DXYEUAmC.js +0 -1
  447. package/dist/public/assets/_DYTVambU.js +0 -1
  448. package/dist/public/assets/_DYqx8g78.js.br +0 -0
  449. package/dist/public/assets/_DZanHI_X.js +0 -1
  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,22 @@ 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
+ // `work session --help` is what an agent tries before `work help`.
137
+ if (args.includes("--help") || args.includes("-h")) await helpCmd(command);
138
+ else await cli.cmd.run(cliContext(cli.words));
139
+ break;
140
+ case "help":
141
+ case "--help":
142
+ case "-h":
143
+ await helpCmd(positional[0]);
144
+ break;
105
145
  case "start":
106
146
  await start();
107
147
  break;
@@ -169,9 +209,10 @@ switch (command) {
169
209
  console.log(pkg.version);
170
210
  break;
171
211
  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
- );
212
+ // A typo must fail: an agent reads the exit code, and usage on stdout
213
+ // with 0 read as the command having run.
214
+ console.error(`error: no command \`${command}\` (see: work help)`);
215
+ process.exitCode = 2;
175
216
  }
176
217
 
177
218
  async function start() {
@@ -232,18 +273,45 @@ function serverUrl() {
232
273
  return `http://${dialHost(env.WORK_LISTEN)}:${env.PORT}`;
233
274
  }
234
275
 
235
- async function apiGet(path) {
276
+ /** One request to the running server: its JSON, or the command dies with
277
+ * the server's own reason (`{error}`), never only its status. */
278
+ async function apiCall(method, path, body, timeoutMs = 30_000) {
236
279
  const port = serviceEnv().PORT;
280
+ let res;
237
281
  try {
238
- const res = await fetch(`${serverUrl()}${path}`, {
239
- signal: AbortSignal.timeout(30_000),
282
+ res = await fetch(`${serverUrl()}${path}`, {
283
+ method,
284
+ headers:
285
+ body === undefined ? undefined : { "content-type": "application/json" },
286
+ body: body === undefined ? undefined : JSON.stringify(body),
287
+ signal: AbortSignal.timeout(timeoutMs),
240
288
  });
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;
289
+ } catch {
290
+ // Codex runs commands in a sandbox with no network, a local socket
291
+ // included: the server is up, the command has to run outside it,
292
+ // which the agent asks the user for.
293
+ if (process.env.CODEX_SANDBOX_NETWORK_DISABLED)
294
+ die(
295
+ "work cannot reach its server from inside Codex's sandbox (no network there): run this command outside the sandbox, with escalated permissions",
296
+ );
245
297
  die(`no work server on port ${port}. Start one with: work start`);
246
298
  }
299
+ const text = await res.text();
300
+ let data = null;
301
+ try {
302
+ data = text ? JSON.parse(text) : null;
303
+ } catch {}
304
+ if (!res.ok || (data && typeof data === "object" && data.error)) {
305
+ die(
306
+ (data && data.error) ||
307
+ `server answered ${res.status} for ${method} ${path}`,
308
+ );
309
+ }
310
+ return data;
311
+ }
312
+
313
+ function apiGet(path) {
314
+ return apiCall("GET", path);
247
315
  }
248
316
 
249
317
  /**
@@ -255,7 +323,7 @@ async function apiGet(path) {
255
323
  async function resolveWorkspace(arg) {
256
324
  const [list, status] = await Promise.all([
257
325
  apiGet("/api/workspaces"),
258
- apiGet("/api/status"),
326
+ apiGet(statusPath()),
259
327
  ]);
260
328
  let ws;
261
329
  if (arg) {
@@ -294,10 +362,17 @@ function pad(s, n) {
294
362
  return String(s ?? "").padEnd(n);
295
363
  }
296
364
 
365
+ /** The status snapshot, or with --fresh one that asks GitHub again: a PR
366
+ * made a moment ago (`gh pr create`) is otherwise missing for as long as
367
+ * the PR cache keeps its last answer. */
368
+ function statusPath() {
369
+ return args.includes("--fresh") ? "/api/status?fresh" : "/api/status";
370
+ }
371
+
297
372
  async function workspacesCmd() {
298
373
  const [list, status] = await Promise.all([
299
374
  apiGet("/api/workspaces"),
300
- apiGet("/api/status"),
375
+ apiGet(statusPath()),
301
376
  ]);
302
377
  const rows = list.map((w) => ({ ...w, ...(status[w.id] || {}) }));
303
378
  if (jsonOut) return console.log(JSON.stringify(rows, null, 2));
@@ -323,21 +398,26 @@ function prLine(pr) {
323
398
  : pr.isDraft
324
399
  ? "draft"
325
400
  : "open";
326
- return `#${pr.number} ${state} ${pr.title} ${pr.url}`;
401
+ return `#${pr.number} ${state} ${pr.title}${pr.headRefName ? ` (${pr.headRefName})` : ""} ${pr.url}`;
327
402
  }
328
403
 
329
404
  async function workspaceCmd() {
330
405
  const { ws, status } = await resolveWorkspace(positional[0]);
331
406
  const procs = await apiGet(`/api/workspaces/${ws.id}/procs`).catch(() => []);
332
- const [sessions] = await Promise.all([
407
+ const [sessions, linkPort] = await Promise.all([
333
408
  apiGet(`/api/workspaces/${ws.id}/sessions`).catch(() => []),
409
+ portLinker(),
334
410
  ]);
411
+ const me = process.env.WORK_SESSION_ID;
335
412
  const out = {
336
413
  ...ws,
337
- ports: status.ports,
414
+ ports: (status.ports || []).map((p) => ({
415
+ ...p,
416
+ ...linkPort(p.port, p.local),
417
+ })),
338
418
  repos: mergeRepos(ws, status),
339
- sessions,
340
- procs,
419
+ sessions: sessions.map((s) => (s.id === me ? { ...s, self: true } : s)),
420
+ procs: procs.map((p) => withUrls(p, linkPort)),
341
421
  };
342
422
  if (jsonOut) return console.log(JSON.stringify(out, null, 2));
343
423
  console.log(`${ws.name} (${ws.status})`);
@@ -352,16 +432,58 @@ async function workspaceCmd() {
352
432
  console.log(` sessions:`);
353
433
  for (const s of sessions) {
354
434
  const phase = s.state?.phase || (s.state?.alive ? "idle" : "off");
355
- console.log(` ${pad(s.agent, 10)} ${pad(phase, 8)} ${s.title || ""}`);
435
+ console.log(
436
+ ` ${pad(s.id, 9)} ${pad(s.agent, 10)} ${pad(phase, 8)} ${s.title || ""}${s.id === me ? " (this session)" : ""}`,
437
+ );
356
438
  }
357
439
  }
358
440
  if (procs.length) {
359
441
  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
- }
442
+ for (const p of out.procs)
443
+ console.log(` ${pad(p.pid, 7)} ${p.cmd}${portsText(p)}`);
444
+ }
445
+ }
446
+
447
+ /** How a port opens for the user: through the address their browser last
448
+ * reached work by (`/api/boot`'s `origin`) as the page's port chip would
449
+ * (lib/ports/url), or this machine's own when no browser has yet. `blocked`
450
+ * says why that link cannot work (a port bound to loopback, a page on a
451
+ * direct address). */
452
+ async function portLinker() {
453
+ const boot = await apiGet("/api/boot");
454
+ const loc = boot.origin ? locOf(boot.origin) : null;
455
+ return (port, local) => {
456
+ if (!loc) return { url: `http://localhost:${port}` };
457
+ const blocked = portBlockedAt(port, local, loc);
458
+ return {
459
+ url: portUrlAt(port, loc, boot.portHosts),
460
+ ...(blocked && { blocked }),
461
+ };
462
+ };
463
+ }
464
+
465
+ /** A process with each of its ports' links (`urls`, by port). */
466
+ function withUrls(p, linkPort) {
467
+ if (!p.ports?.length) return p;
468
+ const local = new Set(p.local || []);
469
+ return {
470
+ ...p,
471
+ urls: Object.fromEntries(
472
+ p.ports.map((port) => [port, linkPort(port, local.has(port))]),
473
+ ),
474
+ };
475
+ }
476
+
477
+ /** A process's ports for a line of text, each with where it opens. */
478
+ function portsText(p) {
479
+ if (!p.ports?.length) return "";
480
+ return ` ${p.ports
481
+ .map((port) => {
482
+ const link = p.urls?.[port];
483
+ if (!link) return `:${port}`;
484
+ return `:${port} ${link.url}${link.blocked ? ` (${link.blocked})` : ""}`;
485
+ })
486
+ .join(" ")}`;
365
487
  }
366
488
 
367
489
  /** Workspace repos joined with the status snapshot's PRs per repo. */
@@ -387,13 +509,15 @@ async function reposCmd() {
387
509
 
388
510
  async function procsCmd() {
389
511
  const { ws } = await resolveWorkspace(positional[0]);
390
- const procs = await apiGet(`/api/workspaces/${ws.id}/procs`);
512
+ const [raw, linkPort] = await Promise.all([
513
+ apiGet(`/api/workspaces/${ws.id}/procs`),
514
+ portLinker(),
515
+ ]);
516
+ const procs = raw.map((p) => withUrls(p, linkPort));
391
517
  if (jsonOut) return console.log(JSON.stringify(procs, null, 2));
392
518
  if (!procs.length) return console.log("nothing running");
393
519
  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
- );
520
+ console.log(`${pad(p.pid, 7)} ${p.cmd}${portsText(p)}`);
397
521
  }
398
522
 
399
523
  /** Comments left on the review page but not yet sent: an agent can pull
@@ -402,9 +526,23 @@ async function procsCmd() {
402
526
  async function reviewCmd() {
403
527
  const { ws } = await resolveWorkspace(positional[0]);
404
528
  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)));
529
+ if (jsonOut) console.log(JSON.stringify(comments, null, 2));
530
+ else if (!comments.length) console.log("no pending review comments");
531
+ else
532
+ console.log(
533
+ commentMarkdown(comments, await reviewSections(ws.id, comments)),
534
+ );
535
+ // Taken, the comments leave the review as a Send takes them: exactly the
536
+ // ones printed, so one written meanwhile stays for the next look.
537
+ if (args.includes("--take") && comments.length) {
538
+ await apiPost(`/api/workspaces/${ws.id}/review`, {
539
+ action: "clear",
540
+ ids: comments.map((c) => c.id),
541
+ });
542
+ console.error(
543
+ `took ${comments.length} comment${comments.length === 1 ? "" : "s"} off the review`,
544
+ );
545
+ }
408
546
  }
409
547
 
410
548
  /** The diff's sections, which say which lane or PR a comment's lines are
@@ -413,41 +551,25 @@ async function reviewCmd() {
413
551
  * comments then read as the checkout's rather than failing the command. */
414
552
  async function reviewSections(wsId, comments) {
415
553
  if (!comments.some((c) => c.head)) return null;
416
- const port = serviceEnv().PORT;
417
554
  try {
418
- const res = await fetch(
419
- `http://127.0.0.1:${port}/api/workspaces/${wsId}/diff`,
420
- { signal: AbortSignal.timeout(60_000) },
421
- );
555
+ const res = await fetch(`${serverUrl()}/api/workspaces/${wsId}/diff`, {
556
+ signal: AbortSignal.timeout(60_000),
557
+ });
422
558
  return res.ok ? ((await res.json()).sections ?? null) : null;
423
559
  } catch {
424
560
  return null;
425
561
  }
426
562
  }
427
563
 
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;
564
+ /** A write: as long as a clone can take (a create, a restore). */
565
+ function apiPost(path, body) {
566
+ return apiCall("POST", path, body ?? {}, 15 * 60_000);
445
567
  }
446
568
 
447
569
  /** One line per workspace/repo of what the file will not carry. */
448
570
  function printPreflight(found) {
449
571
  for (const w of found.workspaces) {
450
- console.log(w.name);
572
+ say(w.name);
451
573
  for (const r of w.repos) {
452
574
  const parts = [];
453
575
  for (const b of r.unpushed)
@@ -456,10 +578,10 @@ function printPreflight(found) {
456
578
  if (r.untracked.length) parts.push(`${r.untracked.length} untracked`);
457
579
  if (r.ignored.length) parts.push(`${r.ignored.length} ignored`);
458
580
  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}`);
581
+ say(` ${r.worktree}/ ${parts.join(", ")}`);
582
+ for (const f of r.modified) say(` M ${f}`);
583
+ for (const f of r.untracked) say(` ? ${f}`);
584
+ for (const f of r.ignored) say(` ! ${f}`);
463
585
  }
464
586
  }
465
587
  }
@@ -477,7 +599,7 @@ async function exportCmd() {
477
599
  );
478
600
  }
479
601
  if (found.workspaces.length) {
480
- console.log("not in the backup:");
602
+ say("not in the backup:");
481
603
  printPreflight(found);
482
604
  if (!yes) {
483
605
  console.log("");
@@ -498,6 +620,18 @@ async function exportCmd() {
498
620
  "work-backup.json";
499
621
  fs.writeFileSync(name, await res.text());
500
622
  log(`wrote ${name}`);
623
+ if (jsonOut)
624
+ console.log(
625
+ JSON.stringify(
626
+ {
627
+ file: path.resolve(name),
628
+ contents: c ?? null,
629
+ left_out: found.workspaces,
630
+ },
631
+ null,
632
+ 2,
633
+ ),
634
+ );
501
635
  }
502
636
 
503
637
  function readManifest(file) {
@@ -658,6 +792,8 @@ function until(iso) {
658
792
  }
659
793
 
660
794
  async function setup() {
795
+ // First: a bad name fails before anything is touched.
796
+ editAllowedHosts();
661
797
  await bridge();
662
798
  const d = await doctor(doctorOpts());
663
799
  await fix(d);
@@ -670,13 +806,30 @@ async function setup() {
670
806
  const port = serviceEnv().PORT;
671
807
  if (!tailnet) maybeTunnel(port);
672
808
  if (running) {
673
- if (await waitForServer()) log(`done — http://localhost:${port}`);
674
- else
809
+ const answered = await waitForServer();
810
+ if (answered) log(`done — http://localhost:${port}`);
811
+ else {
675
812
  warn(
676
813
  `service started but nothing answered on port ${port} within 30s — check the logs`,
677
814
  );
815
+ // A setup that left no server is a failed one, to whatever ran it.
816
+ process.exitCode = 1;
817
+ }
678
818
  if (tailnet)
679
819
  log(`on your phone and other devices on the tailnet: ${tailnet}`);
820
+ if (jsonOut)
821
+ console.log(
822
+ JSON.stringify(
823
+ {
824
+ ok: answered,
825
+ version: pkg.version,
826
+ url: `http://localhost:${port}`,
827
+ tailnet: tailnet || null,
828
+ },
829
+ null,
830
+ 2,
831
+ ),
832
+ );
680
833
  } else {
681
834
  // A fresh install's shell may not have mise on PATH yet — name the shim.
682
835
  const shim = path.join(
@@ -694,6 +847,34 @@ async function setup() {
694
847
  }
695
848
  }
696
849
 
850
+ /** `--allow-host <name>` / `--disallow-host <name>`, each repeatable: one
851
+ * name in or out of the service's WORK_ALLOWED_HOSTS, the rest kept —
852
+ * `WORK_ALLOWED_HOSTS=… work setup` replaces the whole list, which an
853
+ * agent adding one proxy's name would otherwise have to read back first.
854
+ * Applied before anything writes the service, which carries what this
855
+ * leaves in the environment (`serviceEnv`). */
856
+ function editAllowedHosts() {
857
+ const allow = flagValues("--allow-host");
858
+ const disallow = flagValues("--disallow-host");
859
+ if (!allow.length && !disallow.length) return;
860
+ for (const name of [...allow, ...disallow]) {
861
+ if (!/^[A-Za-z0-9.-]+$/.test(name))
862
+ die(
863
+ `not a host name: ${name} (one name per flag, e.g. work.example.com)`,
864
+ );
865
+ }
866
+ const before = serviceEnv().WORK_ALLOWED_HOSTS || "";
867
+ let next = before;
868
+ for (const name of allow) next = allowHost(next, name, true);
869
+ for (const name of disallow) next = allowHost(next, name, false);
870
+ process.env.WORK_ALLOWED_HOSTS = next;
871
+ log(
872
+ next === before
873
+ ? `WORK_ALLOWED_HOSTS unchanged: ${next || "(none)"}`
874
+ : `WORK_ALLOWED_HOSTS=${next || "(none)"}`,
875
+ );
876
+ }
877
+
697
878
  /** Bring a database from before 0.1 to the baseline this release starts
698
879
  * from (lib/setup/bridge.mjs), before anything touches the service: the
699
880
  * server it would restart refuses such a database. Run with the settings
@@ -745,22 +926,35 @@ async function update() {
745
926
  // `--prefer-online`: npm serves `latest` from a cached packument for a
746
927
  // few minutes after a publish, so an update right behind a release
747
928
  // reinstalled the version already running.
748
- sh("npm", [
749
- "install",
750
- "-g",
751
- "--prefer-online",
752
- ...allowScriptsArgs(),
753
- "@dylanpiercey/work@latest",
754
- ]);
929
+ // Under --json, npm's own output goes to stderr: stdout is setup's JSON.
930
+ const stdio = jsonOut ? ["inherit", 2, 2] : "inherit";
931
+ sh(
932
+ "npm",
933
+ [
934
+ "install",
935
+ "-g",
936
+ "--prefer-online",
937
+ ...allowScriptsArgs(),
938
+ "@dylanpiercey/work@latest",
939
+ ],
940
+ { stdio },
941
+ );
755
942
  // Re-exec the (possibly new) CLI to finish with fresh code.
756
943
  sh("work", [
757
944
  "setup",
758
945
  ...(yes ? ["--yes"] : []),
759
946
  ...args.filter((a) =>
760
- ["--tailscale", "--no-tailscale", "--firewall", "--no-firewall"].includes(
761
- a,
762
- ),
947
+ [
948
+ "--tailscale",
949
+ "--no-tailscale",
950
+ "--firewall",
951
+ "--no-firewall",
952
+ "--json",
953
+ "--non-interactive",
954
+ ].includes(a),
763
955
  ),
956
+ ...flagValues("--allow-host").flatMap((h) => ["--allow-host", h]),
957
+ ...flagValues("--disallow-host").flatMap((h) => ["--disallow-host", h]),
764
958
  ]);
765
959
  }
766
960
 
@@ -976,7 +1170,7 @@ async function fix(d) {
976
1170
  }
977
1171
  log(`installing ${tool.label} via ${tool.mise ? "mise" : "gh"}`);
978
1172
  try {
979
- await installTool(tool.id, { log: (l) => console.log(` ${l}`) });
1173
+ await installTool(tool.id, { log: (l) => say(` ${l}`) });
980
1174
  } catch (err) {
981
1175
  if (tool.required) die(`${tool.label} install failed: ${err.message}`);
982
1176
  warn(`${tool.label} install failed (non-fatal)`);
@@ -1741,6 +1935,7 @@ function allowTailnet(serve) {
1741
1935
  * controlling terminal, since the curl installer pipes its script into the
1742
1936
  * shell this runs under. Null with no terminal at all. */
1743
1937
  function terminal() {
1938
+ if (!interactive) return null;
1744
1939
  if (process.stdin.isTTY) return process.stdin;
1745
1940
  try {
1746
1941
  return new tty.ReadStream(fs.openSync("/dev/tty", "r"));
@@ -1763,3 +1958,139 @@ async function confirm(input, question) {
1763
1958
  rl.close();
1764
1959
  }
1765
1960
  }
1961
+
1962
+ // -- commands of bin/cli/ and help ------------------------------------------
1963
+
1964
+ /** Every command a file of bin/cli/ exports (`_`-prefixed files are its
1965
+ * helpers), loaded once. (Kept on the function: the dispatch above runs
1966
+ * before a top-level `let` here would be initialised.) */
1967
+ async function cliCommands() {
1968
+ if (cliCommands.cache) return cliCommands.cache;
1969
+ const dir = path.join(pkgRoot, "bin", "cli");
1970
+ const files = fs.existsSync(dir)
1971
+ ? fs
1972
+ .readdirSync(dir)
1973
+ .filter((f) => f.endsWith(".mjs") && !f.startsWith("_"))
1974
+ .sort()
1975
+ : [];
1976
+ const all = [];
1977
+ for (const f of files) {
1978
+ const mod = await import(path.join(dir, f));
1979
+ for (const c of mod.commands || []) all.push(c);
1980
+ }
1981
+ cliCommands.cache = all;
1982
+ return all;
1983
+ }
1984
+
1985
+ /** A core command's other name, which a bin/cli/ command's first word may
1986
+ * be spelled with (`work ws create` is `workspace create`). */
1987
+ function canonical(word) {
1988
+ return word === "ws" ? "workspace" : word;
1989
+ }
1990
+
1991
+ /** The bin/cli/ command `argv` names: two words (`workspace create`)
1992
+ * before one, so a verb of its own wins over the word alone. */
1993
+ async function cliCommand(first, second) {
1994
+ const all = await cliCommands();
1995
+ const head = canonical(first);
1996
+ const names = (c) => [c.name, ...(c.aliases || [])];
1997
+ if (second) {
1998
+ const two = `${head} ${second}`;
1999
+ const cmd = all.find((c) => names(c).includes(two));
2000
+ if (cmd) return { cmd, words: 2 };
2001
+ }
2002
+ const cmd = all.find((c) => names(c).includes(head));
2003
+ return cmd ? { cmd, words: 1 } : null;
2004
+ }
2005
+
2006
+ /** What a bin/cli/ command is handed: its own argv, the CLI's flags, and
2007
+ * the helpers every command shares — the running server (`api`), which
2008
+ * workspace is meant, and how to say something or fail. */
2009
+ function cliContext(words) {
2010
+ const own = args.slice(0);
2011
+ // The words that named it are not its positionals.
2012
+ const pos = positional.slice(words - 1);
2013
+ return {
2014
+ pkg,
2015
+ args: own,
2016
+ positional: pos,
2017
+ json: jsonOut,
2018
+ yes,
2019
+ has: (flag) => args.includes(flag),
2020
+ flag: flagValue,
2021
+ flags: flagValues,
2022
+ /** Positionals with `valued` flags' values left out (`--agent x`). */
2023
+ parse(valued = []) {
2024
+ const set = new Set([...VALUED, ...valued]);
2025
+ return args
2026
+ .filter(
2027
+ (a, i) => a === "-" || (!a.startsWith("-") && !set.has(args[i - 1])),
2028
+ )
2029
+ .slice(words - 1);
2030
+ },
2031
+ api: {
2032
+ get: (p) => apiCall("GET", p),
2033
+ post: (p, body) => apiPost(p, body),
2034
+ put: (p, body) => apiCall("PUT", p, body ?? {}),
2035
+ patch: (p, body) => apiCall("PATCH", p, body ?? {}),
2036
+ del: (p, body) => apiCall("DELETE", p, body),
2037
+ },
2038
+ serverUrl,
2039
+ resolveWorkspace,
2040
+ /** `data` as JSON under --json, else what `text` prints. */
2041
+ out(data, text) {
2042
+ if (jsonOut) console.log(JSON.stringify(data, null, 2));
2043
+ else text?.(data);
2044
+ },
2045
+ pad,
2046
+ log,
2047
+ warn,
2048
+ die,
2049
+ };
2050
+ }
2051
+
2052
+ /** The header's own lines for the core commands: `work <name>` and what
2053
+ * continues it, as written at the top of this file. */
2054
+ function coreHelp() {
2055
+ const src = fs.readFileSync(fileURLToPath(import.meta.url), "utf8");
2056
+ const lines = [];
2057
+ for (const line of src.split("\n").slice(1)) {
2058
+ if (!line.startsWith("//")) break;
2059
+ const text = line.replace(/^\/\/ ?/, "");
2060
+ // The list starts at its first `work …` line and ends at a blank one.
2061
+ if (!lines.length && !/^ {2}work /.test(text)) continue;
2062
+ if (!text.trim()) break;
2063
+ lines.push(text.replace(/^ {2}/, ""));
2064
+ }
2065
+ return lines;
2066
+ }
2067
+
2068
+ /** `work help`: every command, the core's then bin/cli/'s; `work help
2069
+ * <cmd>` that one's whole text. */
2070
+ async function helpCmd(topic) {
2071
+ const all = await cliCommands();
2072
+ const core = coreHelp();
2073
+ if (topic) {
2074
+ const head = canonical(topic);
2075
+ const own = all.filter(
2076
+ (c) => c.name === head || c.name.startsWith(`${head} `),
2077
+ );
2078
+ const start = core.findIndex((l) => new RegExp(`^work ${head}\\b`).test(l));
2079
+ if (start < 0 && !own.length)
2080
+ die(`no command \`${topic}\` (see: work help)`);
2081
+ if (start >= 0) {
2082
+ let end = start + 1;
2083
+ while (end < core.length && !core[end].startsWith("work ")) end++;
2084
+ console.log(core.slice(start, end).join("\n"));
2085
+ }
2086
+ for (const c of own) console.log(`\n${c.help || c.summary}`);
2087
+ return;
2088
+ }
2089
+ console.log(`work ${pkg.version}\n`);
2090
+ console.log(core.join("\n"));
2091
+ for (const c of all) console.log(`work ${c.name.padEnd(22)} ${c.summary}`);
2092
+ console.log(
2093
+ "\nwork help <command> for one command's whole text. Most take --json;",
2094
+ );
2095
+ console.log("under it stdout is JSON alone and progress goes to stderr.");
2096
+ }