@dylanpiercey/work 0.0.47 → 0.0.48

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 (265) hide show
  1. package/bin/work.mjs +77 -47
  2. package/dist/{_DM_oFS_v.js → _CO83dVER.js} +446 -34
  3. package/dist/_DjrRGy4k.js +2 -0
  4. package/dist/{_o0dwm0Ww.js → _Hz_qlZE1.js} +59 -59
  5. package/dist/{_Dm4BVuln.js → _m3ryV1w6.js} +23 -22
  6. package/dist/index.mjs +2027 -662
  7. package/dist/public/assets/{_9CHuxVRu.js → _1cz-l3Ka.js} +1 -1
  8. package/dist/public/assets/_1cz-l3Ka.js.br +0 -0
  9. package/dist/public/assets/{_By0S6RFm2.js → _1oYv4fmY2.js} +1 -1
  10. package/dist/public/assets/_1oYv4fmY2.js.br +0 -0
  11. package/dist/public/assets/{_CfyASWBT2.js → _3i592Qvz2.js} +1 -1
  12. package/dist/public/assets/_3i592Qvz2.js.br +0 -0
  13. package/dist/public/assets/{_6fdNa05X2.js → _8nx9Raqx2.js} +1 -1
  14. package/dist/public/assets/_8nx9Raqx2.js.br +0 -0
  15. package/dist/public/assets/{_DJYs9cGt2.js → _9yP8iK4t2.js} +1 -1
  16. package/dist/public/assets/_9yP8iK4t2.js.br +0 -0
  17. package/dist/public/assets/{_zkaPdW0C2.js → _B-MMW3nb2.js} +1 -1
  18. package/dist/public/assets/_B-MMW3nb2.js.br +0 -0
  19. package/dist/public/assets/{_D1Ecsx8-2.js → _B2fn4I8S2.js} +1 -1
  20. package/dist/public/assets/_B2fn4I8S2.js.br +0 -0
  21. package/dist/public/assets/{_CrvHv7e4.js → _B49r6O_E.js} +1 -1
  22. package/dist/public/assets/_B49r6O_E.js.br +0 -0
  23. package/dist/public/assets/{_FZ5aDFyS.js → _BBW7mJhZ.js} +1 -1
  24. package/dist/public/assets/_BBW7mJhZ.js.br +0 -0
  25. package/dist/public/assets/{_fkktwOR8.js → _BC2FBUbj.js} +1 -1
  26. package/dist/public/assets/_BC2FBUbj.js.br +0 -0
  27. package/dist/public/assets/{_2_8X99jw.js → _BFt-jSq-.js} +1 -1
  28. package/dist/public/assets/_BFt-jSq-.js.br +0 -0
  29. package/dist/public/assets/{_BmH7JUGb2.js → _BGyMoPGj2.js} +1 -1
  30. package/dist/public/assets/_BGyMoPGj2.js.br +0 -0
  31. package/dist/public/assets/{_B8PQyubX.js → _BLjOZLxk.js} +1 -1
  32. package/dist/public/assets/_BLjOZLxk.js.br +0 -0
  33. package/dist/public/assets/{_BPq9uIVf.js → _BPFJiBiE.js} +1 -1
  34. package/dist/public/assets/_BPFJiBiE.js.br +0 -0
  35. package/dist/public/assets/{_DFC22iAc.js → _BPLnh5S4.js} +1 -1
  36. package/dist/public/assets/_BPLnh5S4.js.br +0 -0
  37. package/dist/public/assets/{_CMIAElmF.js → _BRzL7uNP.js} +1 -1
  38. package/dist/public/assets/_BRzL7uNP.js.br +0 -0
  39. package/dist/public/assets/{_DZExUZ7A.js → _BV-nVD9L2.js} +1 -1
  40. package/dist/public/assets/_BV-nVD9L2.js.br +0 -0
  41. package/dist/public/assets/{_DbkqHev42.js → _BbMkV19p2.js} +1 -1
  42. package/dist/public/assets/_BbMkV19p2.js.br +0 -0
  43. package/dist/public/assets/{_QZ7jIJEp.js → _BbW3-3ve.js} +1 -1
  44. package/dist/public/assets/_BbW3-3ve.js.br +0 -0
  45. package/dist/public/assets/{_BRHBgjBP2.js → _BdbnwCKs2.js} +1 -1
  46. package/dist/public/assets/_BdbnwCKs2.js.br +0 -0
  47. package/dist/public/assets/_BeSgyXoy2.js +1 -0
  48. package/dist/public/assets/_BeSgyXoy2.js.br +0 -0
  49. package/dist/public/assets/{_CRF3psRH.js → _Bg9mkw0u.js} +1 -1
  50. package/dist/public/assets/_Bg9mkw0u.js.br +0 -0
  51. package/dist/public/assets/{_BHjsQD4C.js → _BhUM9ZCP.js} +1 -1
  52. package/dist/public/assets/_BhUM9ZCP.js.br +0 -0
  53. package/dist/public/assets/{_C9YvMapn.js → _Bhnzfac_.js} +1 -1
  54. package/dist/public/assets/_Bhnzfac_.js.br +0 -0
  55. package/dist/public/assets/{_BPiDffSK.js → _BiEWaww7.js} +1 -1
  56. package/dist/public/assets/_BiEWaww7.js.br +0 -0
  57. package/dist/public/assets/{_B3rIZBik.js → _Bm-lmvlM.js} +1 -1
  58. package/dist/public/assets/_Bm-lmvlM.js.br +0 -0
  59. package/dist/public/assets/{_Cu3f85ax2.js → _Bpdzs0h4.js} +1 -1
  60. package/dist/public/assets/_Bpdzs0h4.js.br +0 -0
  61. package/dist/public/assets/{_BzuTzxB9.js → _BqeAAcor.js} +1 -1
  62. package/dist/public/assets/_BqeAAcor.js.br +0 -0
  63. package/dist/public/assets/{_5pEZdV75.js → _Bvwr3uff.js} +1 -1
  64. package/dist/public/assets/_Bvwr3uff.js.br +0 -0
  65. package/dist/public/assets/{_CRpvVy-g.js → _ByiODT9w.js} +1 -1
  66. package/dist/public/assets/_ByiODT9w.js.br +0 -0
  67. package/dist/public/assets/{_nqBpAHq9.js → _C4NK2Cq5.js} +1 -1
  68. package/dist/public/assets/_C4NK2Cq5.js.br +0 -0
  69. package/dist/public/assets/{_B2pxLNBO.js → _CASsRCtt.js} +1 -1
  70. package/dist/public/assets/_CASsRCtt.js.br +0 -0
  71. package/dist/public/assets/{_DPW4NADy2.js → _CH4P29Yf2.js} +1 -1
  72. package/dist/public/assets/_CH4P29Yf2.js.br +0 -0
  73. package/dist/public/assets/{_COmscE442.js → _CHqTBuzz2.js} +1 -1
  74. package/dist/public/assets/_CHqTBuzz2.js.br +0 -0
  75. package/dist/public/assets/{_DvqRc7vA2.js → _CIeZtXbg2.js} +1 -1
  76. package/dist/public/assets/_CIeZtXbg2.js.br +0 -0
  77. package/dist/public/assets/{_DU0F-fSc.js → _CObQoJCq.js} +1 -1
  78. package/dist/public/assets/_CObQoJCq.js.br +0 -0
  79. package/dist/public/assets/{_BxuGlOMJ.js → _CQDBqJkT.js} +1 -1
  80. package/dist/public/assets/_CQDBqJkT.js.br +0 -0
  81. package/dist/public/assets/{_DZyW50O5.js → _CUqlR7rP.js} +1 -1
  82. package/dist/public/assets/_CUqlR7rP.js.br +0 -0
  83. package/dist/public/assets/{_BDIKDRj8.js → _CUwAZxRw.js} +1 -1
  84. package/dist/public/assets/_CUwAZxRw.js.br +0 -0
  85. package/dist/public/assets/{_DeCqhciO.js → _CbMNS899.js} +1 -1
  86. package/dist/public/assets/_CbMNS899.js.br +0 -0
  87. package/dist/public/assets/{_BIP7B1g62.js → _Ced5468N2.js} +1 -1
  88. package/dist/public/assets/{_Cawlufow.js → _Cf0wSsqE.js} +1 -1
  89. package/dist/public/assets/_Cf0wSsqE.js.br +0 -0
  90. package/dist/public/assets/{_BMWhe7UA2.js → _ClEKr0Tm2.js} +1 -1
  91. package/dist/public/assets/_ClEKr0Tm2.js.br +0 -0
  92. package/dist/public/assets/{_ChIGZgCK2.js → _CsNJf4g52.js} +1 -1
  93. package/dist/public/assets/_CsNJf4g52.js.br +0 -0
  94. package/dist/public/assets/{_Dj0FqIsq2.js → _CtErcdf92.js} +1 -1
  95. package/dist/public/assets/_CtErcdf92.js.br +0 -0
  96. package/dist/public/assets/{_Btx74Q-k.js → _DAfu0wMa.js} +1 -1
  97. package/dist/public/assets/_DAfu0wMa.js.br +0 -0
  98. package/dist/public/assets/{_DgucIXC9.js → _DBN8F3s1.js} +1 -1
  99. package/dist/public/assets/_DBN8F3s1.js.br +0 -0
  100. package/dist/public/assets/{_DPc97sYv2.js → _DBn74psf2.js} +1 -1
  101. package/dist/public/assets/{_lTcPBjyz2.js → _DFOnuGNp.js} +1 -1
  102. package/dist/public/assets/_DFOnuGNp.js.br +0 -0
  103. package/dist/public/assets/{_CYb-TtX12.js → _DHvVwuPu2.js} +1 -1
  104. package/dist/public/assets/_DHvVwuPu2.js.br +0 -0
  105. package/dist/public/assets/{_AG00U05V.js → _DKnLFVdw.js} +1 -1
  106. package/dist/public/assets/_DKnLFVdw.js.br +0 -0
  107. package/dist/public/assets/{_D5DIKC5J2.js → _DLFnXIrG2.js} +1 -1
  108. package/dist/public/assets/_DLFnXIrG2.js.br +0 -0
  109. package/dist/public/assets/{_Dvsdxjs1.js → _DLK0WouR.js} +1 -1
  110. package/dist/public/assets/_DLK0WouR.js.br +0 -0
  111. package/dist/public/assets/_DLLEkkcM.js +1531 -0
  112. package/dist/public/assets/_DLLEkkcM.js.br +0 -0
  113. package/dist/public/assets/{_ClduJgR5.js → _DOpT45mO.js} +1 -1
  114. package/dist/public/assets/_DOpT45mO.js.br +0 -0
  115. package/dist/public/assets/{_ByQc1pkS.js → _DT9Qs1il.js} +1 -1
  116. package/dist/public/assets/_DT9Qs1il.js.br +0 -0
  117. package/dist/public/assets/{_C3QGlxhv2.js → _DVPPxo4e2.js} +1 -1
  118. package/dist/public/assets/_DVPPxo4e2.js.br +0 -0
  119. package/dist/public/assets/{_CpxRirGf.js → _De7fhQUT.js} +1 -1
  120. package/dist/public/assets/_De7fhQUT.js.br +0 -0
  121. package/dist/public/assets/{_BmtVFK052.js → _DgmBuYFG2.js} +1 -1
  122. package/dist/public/assets/_DgmBuYFG2.js.br +0 -0
  123. package/dist/public/assets/{_BLvHtG5b.js → _DjLQQUoq.js} +1 -1
  124. package/dist/public/assets/_DjLQQUoq.js.br +0 -0
  125. package/dist/public/assets/{_CBF2sJFl.js → _Dn4wMX5v.js} +1 -1
  126. package/dist/public/assets/_Dn4wMX5v.js.br +0 -0
  127. package/dist/public/assets/{_tKPNE6U52.js → _DsJkMBKU2.js} +1 -1
  128. package/dist/public/assets/_DsJkMBKU2.js.br +0 -0
  129. package/dist/public/assets/{_CYLr_2iE.js → _Ez06j6Qv.js} +1 -1
  130. package/dist/public/assets/_Ez06j6Qv.js.br +0 -0
  131. package/dist/public/assets/{_CyDJr5hJ.js → _FCV34uHN.js} +1 -1
  132. package/dist/public/assets/_FCV34uHN.js.br +0 -0
  133. package/dist/public/assets/{_BgDRYHYE.js → _I6H6F0ME.js} +1 -1
  134. package/dist/public/assets/_I6H6F0ME.js.br +0 -0
  135. package/dist/public/assets/{_ji5qc8J4.js → _K6PNL3j4.js} +1 -1
  136. package/dist/public/assets/_K6PNL3j4.js.br +0 -0
  137. package/dist/public/assets/{_CCXyJU0G.js → _KVpYyL9A.js} +1 -1
  138. package/dist/public/assets/_KVpYyL9A.js.br +0 -0
  139. package/dist/public/assets/{_BEEHMSv_.js → _KjztUkjx.js} +1 -1
  140. package/dist/public/assets/_KjztUkjx.js.br +0 -0
  141. package/dist/public/assets/{_BGFm3dJ4.js → _Q6y5EfU1.js} +1 -1
  142. package/dist/public/assets/_Q6y5EfU1.js.br +0 -0
  143. package/dist/public/assets/{_C7kYFLNM.js → _cRLLpjIg.js} +1 -1
  144. package/dist/public/assets/_cRLLpjIg.js.br +0 -0
  145. package/dist/public/assets/{_CXsVxa93.js → _h8qSl3Me.js} +1 -1
  146. package/dist/public/assets/{_BxlHpe2T.js → _kv39JOeM.js} +1 -1
  147. package/dist/public/assets/_kv39JOeM.js.br +0 -0
  148. package/dist/public/assets/{_DTXvrAdN.js → _o1of2_lP.js} +1 -1
  149. package/dist/public/assets/_o1of2_lP.js.br +0 -0
  150. package/dist/public/assets/{_T_hWiFRO.js → _qoSWRJiT.js} +1 -1
  151. package/dist/public/assets/_qoSWRJiT.js.br +0 -0
  152. package/dist/public/assets/{_BZe4BZ4G.js → _yQJK428k.js} +1 -1
  153. package/dist/public/assets/_yQJK428k.js.br +0 -0
  154. package/dist/public/assets/diff-view-CQX-Ovnt.css +1 -0
  155. package/dist/public/assets/diff-view-CQX-Ovnt.css.br +0 -0
  156. package/dist/public/assets/index-DR0Bvd6u.css +1 -0
  157. package/dist/public/assets/index-DR0Bvd6u.css.br +0 -0
  158. package/dist/public/assets/index-fCEONff1.js +85 -0
  159. package/dist/public/assets/index-fCEONff1.js.br +0 -0
  160. package/lib/setup/catalog.d.mts +22 -0
  161. package/lib/setup/catalog.mjs +72 -0
  162. package/lib/setup/doctor.d.mts +49 -0
  163. package/lib/setup/doctor.mjs +203 -0
  164. package/lib/setup/install.d.mts +14 -0
  165. package/lib/setup/install.mjs +135 -0
  166. package/package.json +2 -1
  167. package/dist/_CV5G1-Sr.js +0 -2
  168. package/dist/public/assets/_2_8X99jw.js.br +0 -0
  169. package/dist/public/assets/_5pEZdV75.js.br +0 -0
  170. package/dist/public/assets/_6fdNa05X2.js.br +0 -0
  171. package/dist/public/assets/_9CHuxVRu.js.br +0 -0
  172. package/dist/public/assets/_AG00U05V.js.br +0 -0
  173. package/dist/public/assets/_B2pxLNBO.js.br +0 -0
  174. package/dist/public/assets/_B3rIZBik.js.br +0 -0
  175. package/dist/public/assets/_B8PQyubX.js.br +0 -0
  176. package/dist/public/assets/_BDIKDRj8.js.br +0 -0
  177. package/dist/public/assets/_BEEHMSv_.js.br +0 -0
  178. package/dist/public/assets/_BGFm3dJ4.js.br +0 -0
  179. package/dist/public/assets/_BHjsQD4C.js.br +0 -0
  180. package/dist/public/assets/_BLibugcj.js +0 -1531
  181. package/dist/public/assets/_BLibugcj.js.br +0 -0
  182. package/dist/public/assets/_BLvHtG5b.js.br +0 -0
  183. package/dist/public/assets/_BMWhe7UA2.js.br +0 -0
  184. package/dist/public/assets/_BPiDffSK.js.br +0 -0
  185. package/dist/public/assets/_BPq9uIVf.js.br +0 -0
  186. package/dist/public/assets/_BRHBgjBP2.js.br +0 -0
  187. package/dist/public/assets/_BZe4BZ4G.js.br +0 -0
  188. package/dist/public/assets/_BgDRYHYE.js.br +0 -0
  189. package/dist/public/assets/_BmH7JUGb2.js.br +0 -0
  190. package/dist/public/assets/_BmtVFK052.js.br +0 -0
  191. package/dist/public/assets/_Btx74Q-k.js.br +0 -0
  192. package/dist/public/assets/_BxlHpe2T.js.br +0 -0
  193. package/dist/public/assets/_BxuGlOMJ.js.br +0 -0
  194. package/dist/public/assets/_By0S6RFm2.js.br +0 -0
  195. package/dist/public/assets/_ByQc1pkS.js.br +0 -0
  196. package/dist/public/assets/_BzuTzxB9.js.br +0 -0
  197. package/dist/public/assets/_C3QGlxhv2.js.br +0 -0
  198. package/dist/public/assets/_C7kYFLNM.js.br +0 -0
  199. package/dist/public/assets/_C9YvMapn.js.br +0 -0
  200. package/dist/public/assets/_CBF2sJFl.js.br +0 -0
  201. package/dist/public/assets/_CCXyJU0G.js.br +0 -0
  202. package/dist/public/assets/_CMIAElmF.js.br +0 -0
  203. package/dist/public/assets/_COmscE442.js.br +0 -0
  204. package/dist/public/assets/_CRF3psRH.js.br +0 -0
  205. package/dist/public/assets/_CRpvVy-g.js.br +0 -0
  206. package/dist/public/assets/_CYLr_2iE.js.br +0 -0
  207. package/dist/public/assets/_CYb-TtX12.js.br +0 -0
  208. package/dist/public/assets/_Cawlufow.js.br +0 -0
  209. package/dist/public/assets/_CfyASWBT2.js.br +0 -0
  210. package/dist/public/assets/_ChIGZgCK2.js.br +0 -0
  211. package/dist/public/assets/_ClduJgR5.js.br +0 -0
  212. package/dist/public/assets/_Cp2bkjzT2.js +0 -1
  213. package/dist/public/assets/_Cp2bkjzT2.js.br +0 -0
  214. package/dist/public/assets/_CpxRirGf.js.br +0 -0
  215. package/dist/public/assets/_CrvHv7e4.js.br +0 -0
  216. package/dist/public/assets/_Cu3f85ax2.js.br +0 -0
  217. package/dist/public/assets/_CyDJr5hJ.js.br +0 -0
  218. package/dist/public/assets/_D1Ecsx8-2.js.br +0 -0
  219. package/dist/public/assets/_D2tmOnaH.js +0 -1
  220. package/dist/public/assets/_D2tmOnaH.js.br +0 -0
  221. package/dist/public/assets/_D5DIKC5J2.js.br +0 -0
  222. package/dist/public/assets/_DFC22iAc.js.br +0 -0
  223. package/dist/public/assets/_DJYs9cGt2.js.br +0 -0
  224. package/dist/public/assets/_DPW4NADy2.js.br +0 -0
  225. package/dist/public/assets/_DTXvrAdN.js.br +0 -0
  226. package/dist/public/assets/_DU0F-fSc.js.br +0 -0
  227. package/dist/public/assets/_DZExUZ7A.js.br +0 -0
  228. package/dist/public/assets/_DZyW50O5.js.br +0 -0
  229. package/dist/public/assets/_DbkqHev42.js.br +0 -0
  230. package/dist/public/assets/_DeCqhciO.js.br +0 -0
  231. package/dist/public/assets/_DgucIXC9.js.br +0 -0
  232. package/dist/public/assets/_Dj0FqIsq2.js.br +0 -0
  233. package/dist/public/assets/_DvqRc7vA2.js.br +0 -0
  234. package/dist/public/assets/_Dvsdxjs1.js.br +0 -0
  235. package/dist/public/assets/_FZ5aDFyS.js.br +0 -0
  236. package/dist/public/assets/_QZ7jIJEp.js.br +0 -0
  237. package/dist/public/assets/_T_hWiFRO.js.br +0 -0
  238. package/dist/public/assets/_fkktwOR8.js.br +0 -0
  239. package/dist/public/assets/_ji5qc8J4.js.br +0 -0
  240. package/dist/public/assets/_lTcPBjyz2.js.br +0 -0
  241. package/dist/public/assets/_nqBpAHq9.js.br +0 -0
  242. package/dist/public/assets/_tKPNE6U52.js.br +0 -0
  243. package/dist/public/assets/_zkaPdW0C2.js.br +0 -0
  244. package/dist/public/assets/diff-view-AJeZtePd.css +0 -1
  245. package/dist/public/assets/diff-view-AJeZtePd.css.br +0 -0
  246. package/dist/public/assets/index-BG3uLJV6.js +0 -85
  247. package/dist/public/assets/index-BG3uLJV6.js.br +0 -0
  248. package/dist/public/assets/index-_5MkiydM.css +0 -1
  249. package/dist/public/assets/index-_5MkiydM.css.br +0 -0
  250. package/dist/public/assets/preload-helper-C1JvlFma.css +0 -1
  251. package/dist/public/assets/preload-helper-C1JvlFma.css.br +0 -0
  252. /package/dist/public/assets/{_B5W6OYN72.js → _B5W6OYN7.js} +0 -0
  253. /package/dist/public/assets/{_B5W6OYN72.js.br → _B5W6OYN7.js.br} +0 -0
  254. /package/dist/public/assets/{_DMoTqEGO2.js → _DMoTqEGO.js} +0 -0
  255. /package/dist/public/assets/{_DMoTqEGO2.js.br → _DMoTqEGO.js.br} +0 -0
  256. /package/dist/public/assets/{_DmIc4jLQ2.js → _DmIc4jLQ.js} +0 -0
  257. /package/dist/public/assets/{_DmIc4jLQ2.js.br → _DmIc4jLQ.js.br} +0 -0
  258. /package/dist/public/assets/{_DsUTQ-LC.js → _DsUTQ-LC2.js} +0 -0
  259. /package/dist/public/assets/{_DsUTQ-LC.js.br → _DsUTQ-LC2.js.br} +0 -0
  260. /package/dist/public/assets/{_VbXTXTou.js → _VbXTXTou2.js} +0 -0
  261. /package/dist/public/assets/{_VbXTXTou.js.br → _VbXTXTou2.js.br} +0 -0
  262. /package/dist/public/assets/{_iXnhIJG7.js → _iXnhIJG72.js} +0 -0
  263. /package/dist/public/assets/{_iXnhIJG7.js.br → _iXnhIJG72.js.br} +0 -0
  264. /package/dist/public/assets/{_vGVdxbeo.js → _vGVdxbeo2.js} +0 -0
  265. /package/dist/public/assets/{_vGVdxbeo.js.br → _vGVdxbeo2.js.br} +0 -0
@@ -0,0 +1,22 @@
1
+ export interface ToolSpec {
2
+ id: string;
3
+ label: string;
4
+ bin: string;
5
+ mise?: string;
6
+ required?: boolean;
7
+ system?: boolean;
8
+ }
9
+ export interface AgentSpec {
10
+ id: string;
11
+ label: string;
12
+ bins: string[];
13
+ install: { url: string; shell: "bash" | "sh" };
14
+ }
15
+ export const TOOLS: ToolSpec[];
16
+ export const AGENTS: AgentSpec[];
17
+ export const DEFAULT_GITHUB_HOST: string;
18
+ export function toolById(id: string): ToolSpec | null;
19
+ export function agentById(id: string): AgentSpec | null;
20
+ export function parseAgentId(id: string): { family: AgentSpec; suffix: string } | null;
21
+ export function agentCommand(id: string): string | null;
22
+ export function agentLabel(id: string): string;
@@ -0,0 +1,72 @@
1
+ // The one list of what a `work` machine should have. Consumed by
2
+ // `work setup` / `work doctor` (bin/work.mjs, plain node) and by the server
3
+ // (src/server/setup.ts) so the CLI, the installer, and the Settings page can
4
+ // never disagree about what "ready" means. Keep it declarative: detection and
5
+ // installation live in doctor.mjs / install.mjs, this file only describes.
6
+
7
+ /** Machine tools. `required` ones gate readiness; the rest are the utilities
8
+ * agents reach for constantly (mirrors DylanPiercey/start). Static binaries via
9
+ * mise, except the system packages the installer provides (git, tmux). */
10
+ export const TOOLS = [
11
+ { id: "git", label: "git", bin: "git", required: true, system: true },
12
+ { id: "tmux", label: "tmux", bin: "tmux", required: true, system: true, mise: "tmux" },
13
+ // The server itself runs on it — install.sh puts it there via mise.
14
+ { id: "node", label: "Node.js", bin: "node", mise: "node", required: true },
15
+ { id: "gh", label: "GitHub CLI", bin: "gh", mise: "gh", required: true },
16
+ { id: "ripgrep", label: "ripgrep", bin: "rg", mise: "ripgrep" },
17
+ { id: "fd", label: "fd", bin: "fd", mise: "fd" },
18
+ { id: "jq", label: "jq", bin: "jq", mise: "jq" },
19
+ { id: "delta", label: "delta", bin: "delta", mise: "delta" },
20
+ // ast-grep's short alias `sg` collides with coreutils setgroups — detect
21
+ // the long binary name.
22
+ { id: "ast-grep", label: "ast-grep", bin: "ast-grep", mise: "ast-grep" },
23
+ { id: "hyperfine", label: "hyperfine", bin: "hyperfine", mise: "hyperfine" },
24
+ { id: "fzf", label: "fzf", bin: "fzf", mise: "fzf" },
25
+ { id: "bottom", label: "bottom", bin: "btm", mise: "bottom" },
26
+ { id: "pnpm", label: "pnpm", bin: "pnpm", mise: "pnpm" },
27
+ ];
28
+
29
+ /** Coding agent CLIs. Installed via each vendor's own installer (not npm) so
30
+ * self-update works. `bins` lists the executable names a family answers to;
31
+ * numeric suffixes (`claude1`) are discovered on top of these. */
32
+ export const AGENTS = [
33
+ { id: "claude", label: "Claude", bins: ["claude"], install: { url: "https://claude.ai/install.sh", shell: "bash" } },
34
+ { id: "codex", label: "Codex", bins: ["codex"], install: { url: "https://chatgpt.com/codex/install.sh", shell: "sh" } },
35
+ { id: "copilot", label: "Copilot", bins: ["copilot"], install: { url: "https://gh.io/copilot-install", shell: "bash" } },
36
+ { id: "cursor", label: "Cursor", bins: ["cursor-agent"], install: { url: "https://cursor.com/install", shell: "bash" } },
37
+ { id: "grok", label: "Grok", bins: ["grok"], install: { url: "https://x.ai/cli/install.sh", shell: "bash" } },
38
+ ];
39
+
40
+ export const DEFAULT_GITHUB_HOST = "github.com";
41
+
42
+ export function toolById(id) {
43
+ return TOOLS.find((t) => t.id === id) || null;
44
+ }
45
+
46
+ export function agentById(id) {
47
+ return AGENTS.find((a) => a.id === id) || null;
48
+ }
49
+
50
+ /**
51
+ * Agent *instances* are the family id plus an optional numeric suffix —
52
+ * `claude`, `claude1`, `claude2` — one per binary on PATH (`claude1`,
53
+ * `cursor-agent2`, …). This is the canonical id everywhere: sessions,
54
+ * settings, usage rows (`~/.claude-2` reports as `claude2`), icons.
55
+ */
56
+ export function parseAgentId(id) {
57
+ const m = /^([a-z]+?)(\d*)$/.exec(id || "");
58
+ const family = m && agentById(m[1]);
59
+ return family ? { family, suffix: m[2] } : null;
60
+ }
61
+
62
+ /** argv0 an instance id launches — the family's binary plus the suffix. */
63
+ export function agentCommand(id) {
64
+ const parsed = parseAgentId(id);
65
+ return parsed ? `${parsed.family.bins[0]}${parsed.suffix}` : null;
66
+ }
67
+
68
+ /** "Claude" / "Claude 2". */
69
+ export function agentLabel(id) {
70
+ const parsed = parseAgentId(id);
71
+ return parsed ? `${parsed.family.label}${parsed.suffix ? ` ${parsed.suffix}` : ""}` : id;
72
+ }
@@ -0,0 +1,49 @@
1
+ export interface DoctorTool {
2
+ id: string;
3
+ label: string;
4
+ required: boolean;
5
+ installed: boolean;
6
+ path?: string;
7
+ version?: string;
8
+ installable: boolean;
9
+ mise?: string;
10
+ }
11
+ export interface AgentInstance {
12
+ /** Canonical id: family + numeric suffix (`claude2`). */
13
+ id: string;
14
+ /** argv0 on PATH (`claude2`, `cursor-agent2`). */
15
+ command: string;
16
+ path: string;
17
+ suffix: string;
18
+ }
19
+ export interface DoctorAgent {
20
+ id: string;
21
+ label: string;
22
+ installed: boolean;
23
+ installable: boolean;
24
+ instances: AgentInstance[];
25
+ }
26
+ export interface GithubHostStatus {
27
+ host: string;
28
+ isEnterprise: boolean;
29
+ authed: boolean;
30
+ user?: string;
31
+ error?: string;
32
+ }
33
+ export interface Doctor {
34
+ ready: boolean;
35
+ missing: string[];
36
+ mise: { installed: boolean; path?: string };
37
+ tools: DoctorTool[];
38
+ agents: DoctorAgent[];
39
+ github: GithubHostStatus[];
40
+ }
41
+ export function setupPath(base?: string): string;
42
+ export function which(bin: string): string | null;
43
+ export function tilde(p: string): string;
44
+ export function miseVersions(): Promise<Record<string, string> | null>;
45
+ export function ghKnownHosts(): string[];
46
+ export function discoverAgents(): DoctorAgent[];
47
+ export function githubHostStatus(host: string): Promise<GithubHostStatus>;
48
+ export function doctor(opts?: { hosts?: string[] }): Promise<Doctor>;
49
+ export function formatDoctor(d: Doctor): string;
@@ -0,0 +1,203 @@
1
+ // Readiness probe for a `work` machine — the single truth behind `work
2
+ // doctor`, `work setup`, GET /api/setup, and the Settings page. Pure
3
+ // detection: nothing here installs or writes. Plain node, no dependencies.
4
+ import { execFile } from "node:child_process";
5
+ import fs from "node:fs";
6
+ import os from "node:os";
7
+ import path from "node:path";
8
+ import { AGENTS, DEFAULT_GITHUB_HOST, TOOLS } from "./catalog.mjs";
9
+
10
+ const home = os.homedir();
11
+
12
+ /** PATH the service runs with (no login shell) plus the current one, so a
13
+ * probe from the server sees the same binaries a probe from a terminal does. */
14
+ export function setupPath(base = process.env.PATH || "") {
15
+ const extra = [
16
+ `${home}/.local/share/mise/shims`,
17
+ `${home}/.local/bin`,
18
+ "/opt/homebrew/bin",
19
+ "/usr/local/bin",
20
+ "/usr/bin",
21
+ "/bin",
22
+ ];
23
+ const seen = new Set();
24
+ return [...base.split(path.delimiter), ...extra]
25
+ .filter((p) => p && !seen.has(p) && seen.add(p))
26
+ .join(path.delimiter);
27
+ }
28
+
29
+ /** `~/…` for anything under the home directory — every path here is for
30
+ * display (Settings rows, `work doctor`), never re-used to exec. */
31
+ export function tilde(p) {
32
+ if (!p) return p;
33
+ return p === home ? "~" : p.startsWith(home + path.sep) ? "~" + p.slice(home.length) : p;
34
+ }
35
+
36
+ /** Absolute path of `bin` on setupPath(), or null. */
37
+ export function which(bin) {
38
+ for (const dir of setupPath().split(path.delimiter)) {
39
+ const full = path.join(dir, bin);
40
+ try {
41
+ if (fs.statSync(full).isFile()) return full;
42
+ } catch {}
43
+ }
44
+ return null;
45
+ }
46
+
47
+ function run(cmd, args, { timeout = 15_000 } = {}) {
48
+ return new Promise((resolve) => {
49
+ execFile(
50
+ cmd,
51
+ args,
52
+ { env: { ...process.env, PATH: setupPath() }, timeout, maxBuffer: 8 << 20 },
53
+ (err, stdout, stderr) => resolve({ ok: !err, stdout: String(stdout || ""), stderr: String(stderr || "") }),
54
+ );
55
+ });
56
+ }
57
+
58
+ /** Versions of every mise-managed tool (global + any active config), keyed
59
+ * by short name. Local and fast; `mise outdated` (network) is a separate,
60
+ * on-demand call in the server. */
61
+ export async function miseVersions() {
62
+ if (!which("mise")) return null;
63
+ const { ok, stdout } = await run("mise", ["ls", "--json"]);
64
+ if (!ok) return {};
65
+ const out = {};
66
+ try {
67
+ for (const [name, list] of Object.entries(JSON.parse(stdout))) {
68
+ const active = list.find((v) => v.installed && v.active) || list.find((v) => v.installed);
69
+ if (active) out[name] = active.version;
70
+ }
71
+ } catch {}
72
+ return out;
73
+ }
74
+
75
+ /** Hosts `gh` knows about (~/.config/gh/hosts.yml top-level keys) — the
76
+ * source of truth for "which GitHub hosts has this machine signed in to",
77
+ * merged with whatever the caller has configured. */
78
+ export function ghKnownHosts() {
79
+ const file = path.join(process.env.GH_CONFIG_DIR || path.join(home, ".config", "gh"), "hosts.yml");
80
+ let text = "";
81
+ try {
82
+ text = fs.readFileSync(file, "utf8");
83
+ } catch {
84
+ return [];
85
+ }
86
+ return [...text.matchAll(/^([A-Za-z0-9.-]+):\s*$/gm)].map((m) => m[1]);
87
+ }
88
+
89
+ /**
90
+ * Every agent CLI instance on PATH, per family: the plain binary and any
91
+ * numeric-suffixed siblings (`claude1`, `cursor-agent2`). Sync — a PATH
92
+ * scan is cheap and the boot handler needs it without awaiting. First hit
93
+ * on PATH wins per name, like the shell.
94
+ */
95
+ export function discoverAgents() {
96
+ const dirs = setupPath().split(path.delimiter);
97
+ const seen = new Map();
98
+ for (const dir of dirs) {
99
+ let names;
100
+ try {
101
+ names = fs.readdirSync(dir);
102
+ } catch {
103
+ continue;
104
+ }
105
+ for (const name of names) {
106
+ const hit = AGENTS.find((a) => a.bins.some((b) => name === b || (name.startsWith(b) && /^\d+$/.test(name.slice(b.length)))));
107
+ if (!hit || seen.has(name)) continue;
108
+ const full = path.join(dir, name);
109
+ try {
110
+ if (!fs.statSync(full).isFile()) continue;
111
+ } catch {
112
+ continue;
113
+ }
114
+ const bin = hit.bins.find((b) => name.startsWith(b));
115
+ seen.set(name, { family: hit, id: `${hit.id}${name.slice(bin.length)}`, command: name, path: tilde(full) });
116
+ }
117
+ }
118
+ return AGENTS.map((a) => {
119
+ const instances = [...seen.values()]
120
+ .filter((i) => i.family === a)
121
+ .map(({ id, command, path: p }) => ({ id, command, path: p, suffix: id.slice(a.id.length) }))
122
+ .sort((x, y) => (x.suffix === "" ? -1 : y.suffix === "" ? 1 : Number(x.suffix) - Number(y.suffix)));
123
+ return { id: a.id, label: a.label, installed: instances.length > 0, installable: true, instances };
124
+ });
125
+ }
126
+
127
+ /** Auth status for one host via gh (which owns the tokens). */
128
+ export async function githubHostStatus(host) {
129
+ const isEnterprise = host !== DEFAULT_GITHUB_HOST;
130
+ if (!which("gh")) return { host, isEnterprise, authed: false, error: "gh not installed" };
131
+ const { ok, stdout, stderr } = await run("gh", ["auth", "status", "--hostname", host]);
132
+ const text = stdout + stderr;
133
+ const user = text.match(/account (\S+)/)?.[1] || text.match(/Logged in to \S+ as (\S+)/)?.[1];
134
+ return ok
135
+ ? { host, isEnterprise, authed: true, user }
136
+ : { host, isEnterprise, authed: false, error: /not logged in/i.test(text) ? undefined : text.trim().split("\n").pop() };
137
+ }
138
+
139
+ /**
140
+ * Full readiness snapshot.
141
+ *
142
+ * @param {{ hosts?: string[] }} [opts] configured GitHub hosts (settings);
143
+ * github.com and any host gh is already signed in to are always included.
144
+ */
145
+ export async function doctor(opts = {}) {
146
+ const hostSet = new Set([DEFAULT_GITHUB_HOST, ...(opts.hosts || []), ...ghKnownHosts()]);
147
+ const [versions, github] = await Promise.all([
148
+ miseVersions(),
149
+ Promise.all([...hostSet].map(githubHostStatus)),
150
+ ]);
151
+
152
+ const tools = TOOLS.map((t) => {
153
+ const found = which(t.bin);
154
+ return {
155
+ id: t.id,
156
+ label: t.label,
157
+ required: !!t.required,
158
+ installed: !!found,
159
+ path: tilde(found) || undefined,
160
+ version: (t.mise && versions && versions[t.mise]) || undefined,
161
+ // Installable when mise can provide it (system packages come from the
162
+ // installer / package manager, not from here).
163
+ installable: !!(t.mise && versions !== null),
164
+ mise: t.mise,
165
+ };
166
+ });
167
+
168
+ const agents = discoverAgents();
169
+
170
+ const missing = [];
171
+ for (const t of tools) if (t.required && !t.installed) missing.push(t.id);
172
+ if (!github.some((h) => h.authed)) missing.push("github-auth");
173
+ if (!agents.some((a) => a.installed)) missing.push("agent");
174
+
175
+ return {
176
+ ready: missing.length === 0,
177
+ missing,
178
+ mise: { installed: versions !== null, path: tilde(which("mise")) || undefined },
179
+ tools,
180
+ agents,
181
+ github,
182
+ };
183
+ }
184
+
185
+ /** Human summary for `work doctor` / logs. */
186
+ export function formatDoctor(d) {
187
+ const mark = (ok) => (ok ? "✓" : "✗");
188
+ const lines = [];
189
+ lines.push(`mise ${mark(d.mise.installed)} ${d.mise.path || "not installed"}`);
190
+ for (const t of d.tools) {
191
+ const tag = t.required ? "" : " (optional)";
192
+ lines.push(`${t.label.padEnd(11)}${mark(t.installed)} ${t.installed ? (t.version ? `v${t.version} ` : "") + (t.path || "") : "missing"}${tag}`);
193
+ }
194
+ for (const h of d.github) {
195
+ lines.push(`${h.host.padEnd(11)}${mark(h.authed)} ${h.authed ? `signed in${h.user ? ` as ${h.user}` : ""}` : h.error || "not signed in"}`);
196
+ }
197
+ for (const a of d.agents) {
198
+ lines.push(`${a.label.padEnd(11)}${mark(a.installed)} ${a.installed ? a.instances.map((i) => `${i.command} (${i.path})`).join(", ") : "not installed"}`);
199
+ }
200
+ lines.push("");
201
+ lines.push(d.ready ? "ready" : `not ready — missing: ${d.missing.join(", ")}`);
202
+ return lines.join("\n");
203
+ }
@@ -0,0 +1,14 @@
1
+ export interface RunOpts {
2
+ log?: (line: string) => void;
3
+ interactive?: boolean;
4
+ input?: string;
5
+ env?: Record<string, string>;
6
+ timeout?: number;
7
+ }
8
+ export function runStreaming(cmd: string, args: string[], opts?: RunOpts): Promise<string[]>;
9
+ export function installMise(opts?: RunOpts): Promise<void>;
10
+ export function installTool(idOrName: string, opts?: RunOpts & { version?: string }): Promise<void>;
11
+ export function updateTool(idOrName: string, opts?: RunOpts): Promise<void>;
12
+ export function removeTool(idOrName: string, opts?: RunOpts): Promise<void>;
13
+ export function installAgent(id: string, opts?: RunOpts): Promise<void>;
14
+ export function authGithub(host: string, opts?: RunOpts): Promise<string[]>;
@@ -0,0 +1,135 @@
1
+ // Installers behind `work setup`, `work doctor --fix`, and POST /api/setup.
2
+ // Wraps mise, gh, and the vendors' own agent installers — never reimplements
3
+ // them. Every command streams its output through `log` so a terminal and the
4
+ // Settings page see the same lines. Plain node, no dependencies.
5
+ import { spawn } from "node:child_process";
6
+ import fs from "node:fs";
7
+ import os from "node:os";
8
+ import path from "node:path";
9
+ import { agentById, toolById } from "./catalog.mjs";
10
+ import { setupPath, which } from "./doctor.mjs";
11
+
12
+ /**
13
+ * Run a command, streaming each output line to `log`.
14
+ * `interactive` hands the terminal over (CLI prompts such as gh's device
15
+ * flow); otherwise stdout/err are captured line by line and `input` (if
16
+ * any) is written to stdin before it closes.
17
+ */
18
+ export function runStreaming(cmd, args, { log = () => {}, interactive = false, input, env, timeout = 15 * 60_000 } = {}) {
19
+ return new Promise((resolve, reject) => {
20
+ const child = spawn(cmd, args, {
21
+ stdio: interactive ? "inherit" : ["pipe", "pipe", "pipe"],
22
+ env: { ...process.env, ...env, PATH: setupPath() },
23
+ });
24
+ const lines = [];
25
+ let buf = "";
26
+ const onData = (chunk) => {
27
+ buf += String(chunk);
28
+ let i;
29
+ while ((i = buf.search(/\r?\n/)) >= 0) {
30
+ const line = buf.slice(0, i).replace(/\x1b\[[0-9;]*[A-Za-z]/g, "").trimEnd();
31
+ buf = buf.slice(i + (buf[i] === "\r" ? 2 : 1));
32
+ if (line) {
33
+ lines.push(line);
34
+ log(line);
35
+ }
36
+ }
37
+ };
38
+ child.stdout?.on("data", onData);
39
+ child.stderr?.on("data", onData);
40
+ if (child.stdin) {
41
+ if (input) child.stdin.write(input);
42
+ child.stdin.end();
43
+ }
44
+ const timer = setTimeout(() => child.kill("SIGTERM"), timeout);
45
+ child.on("error", (err) => {
46
+ clearTimeout(timer);
47
+ reject(err);
48
+ });
49
+ child.on("close", (code) => {
50
+ clearTimeout(timer);
51
+ if (buf.trim()) onData("\n");
52
+ if (code === 0) resolve(lines);
53
+ else reject(new Error(`${cmd} ${args.join(" ")} exited ${code}${lines.length ? `: ${lines[lines.length - 1]}` : ""}`));
54
+ });
55
+ });
56
+ }
57
+
58
+ /** Fetch a vendor install script to a temp file and run it with `shell`.
59
+ * Same effect as the documented `curl … | bash`, without piping a network
60
+ * stream into a login shell: the URL is pinned in the catalog and the
61
+ * script runs in the setup PATH, not the user's rc. */
62
+ async function runRemoteScript(url, shell, opts) {
63
+ const res = await fetch(url, { redirect: "follow", signal: AbortSignal.timeout(60_000) });
64
+ if (!res.ok) throw new Error(`${url} → ${res.status}`);
65
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "work-install-"));
66
+ const file = path.join(dir, "install.sh");
67
+ fs.writeFileSync(file, await res.text(), { mode: 0o700 });
68
+ try {
69
+ return await runStreaming(shell, [file], opts);
70
+ } finally {
71
+ fs.rmSync(dir, { recursive: true, force: true });
72
+ }
73
+ }
74
+
75
+ export async function installMise(opts = {}) {
76
+ if (which("mise")) return;
77
+ opts.log?.("installing mise");
78
+ await runRemoteScript("https://mise.run", "sh", opts);
79
+ if (!which("mise")) throw new Error("mise install finished but binary not found");
80
+ }
81
+
82
+ function miseName(idOrName) {
83
+ const t = toolById(idOrName);
84
+ if (t && !t.mise) throw new Error(`${t.label} is a system package — install it with your package manager`);
85
+ return t ? t.mise : idOrName;
86
+ }
87
+
88
+ /** `mise use -g name@version` — installs (or re-pins) a global tool. Accepts
89
+ * catalog ids or any mise registry short name (Settings search). */
90
+ export async function installTool(idOrName, { version = "latest", ...opts } = {}) {
91
+ await installMise(opts);
92
+ const name = miseName(idOrName);
93
+ opts.log?.(`mise use -g ${name}@${version}`);
94
+ await runStreaming("mise", ["use", "-g", "-y", `${name}@${version}`], opts);
95
+ }
96
+
97
+ /** Move to the newest release and rewrite the global pin to match
98
+ * (`--bump`), so an exact pin such as `1.5.0` updates too. */
99
+ export async function updateTool(idOrName, opts = {}) {
100
+ const name = miseName(idOrName);
101
+ opts.log?.(`mise upgrade --bump ${name}`);
102
+ await runStreaming("mise", ["upgrade", "--bump", "-y", name], opts);
103
+ }
104
+
105
+ /** Drop the global pin and prune its installs. Required tools cannot go. */
106
+ export async function removeTool(idOrName, opts = {}) {
107
+ const t = toolById(idOrName);
108
+ if (t?.required) throw new Error(`${t.label} is required`);
109
+ const name = miseName(idOrName);
110
+ opts.log?.(`mise unuse -g ${name}`);
111
+ await runStreaming("mise", ["unuse", "-g", name], opts);
112
+ await runStreaming("mise", ["prune", "-y", name], opts).catch(() => {});
113
+ }
114
+
115
+ export async function installAgent(id, opts = {}) {
116
+ const a = agentById(id);
117
+ if (!a) throw new Error(`unknown agent: ${id}`);
118
+ opts.log?.(`installing ${a.label} (${a.install.url})`);
119
+ await runRemoteScript(a.install.url, a.install.shell, opts);
120
+ if (!a.bins.some((b) => which(b))) throw new Error(`${a.label} installer finished but ${a.bins[0]} is not on PATH`);
121
+ }
122
+
123
+ /**
124
+ * Sign in to a GitHub host with gh's browser device flow.
125
+ * Interactive (CLI): gh owns the terminal. Otherwise (server) we press Enter
126
+ * for it and stop it launching a browser, so the one-time code and URL flow
127
+ * out through `log` for the UI to show — gh keeps polling until the user
128
+ * finishes in their own browser.
129
+ */
130
+ export async function authGithub(host, { interactive = false, ...opts } = {}) {
131
+ if (!which("gh")) throw new Error("gh is not installed");
132
+ const args = ["auth", "login", "--hostname", host, "--web", "--git-protocol", "https"];
133
+ if (interactive) return runStreaming("gh", args, { ...opts, interactive: true });
134
+ return runStreaming("gh", args, { ...opts, input: "\n", env: { BROWSER: "true", GH_PROMPT_DISABLED: "1" } });
135
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dylanpiercey/work",
3
- "version": "0.0.47",
3
+ "version": "0.0.48",
4
4
  "type": "module",
5
5
  "description": "Web GUI for agentic dev workspaces",
6
6
  "license": "MIT",
@@ -14,6 +14,7 @@
14
14
  "files": [
15
15
  "dist",
16
16
  "bin",
17
+ "lib",
17
18
  "install.sh",
18
19
  "scripts/migrate.mjs"
19
20
  ],
package/dist/_CV5G1-Sr.js DELETED
@@ -1,2 +0,0 @@
1
- import { f as notifySessionsChanged } from "./_DM_oFS_v.js";
2
- export { notifySessionsChanged };
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file