@dylanpiercey/work 0.0.223 → 0.1.1

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 (521) hide show
  1. package/README.md +19 -32
  2. package/bin/work.mjs +259 -114
  3. package/dist/_D1C-XIqT.js +168 -0
  4. package/dist/{_CpANOzpd.js → _Dzz52ZtI.js} +488 -144
  5. package/dist/_HbAW5Cbt.js +2 -0
  6. package/dist/index.mjs +2634 -2786
  7. package/dist/public/assets/{_unfL8mz1.js → _-5UNb9-C.js} +1 -1
  8. package/dist/public/assets/_-5UNb9-C.js.br +0 -0
  9. package/dist/public/assets/_06rtP_SB.js +1 -0
  10. package/dist/public/assets/_14yhXek7.js +1 -0
  11. package/dist/public/assets/_14yhXek7.js.br +0 -0
  12. package/dist/public/assets/_1JXC9nFp.js +1 -0
  13. package/dist/public/assets/_1JXC9nFp.js.br +0 -0
  14. package/dist/public/assets/_1Yit_Y5R.js +1 -0
  15. package/dist/public/assets/_1ZPfR2B8.js +6 -0
  16. package/dist/public/assets/_1ZPfR2B8.js.br +0 -0
  17. package/dist/public/assets/{_BBpX-M4b.js → _1xOByopP.js} +1 -1
  18. package/dist/public/assets/_1xOByopP.js.br +0 -0
  19. package/dist/public/assets/{_BLTfQLdY.js → _3aBl7SEn.js} +1 -1
  20. package/dist/public/assets/_3aBl7SEn.js.br +0 -0
  21. package/dist/public/assets/_5aMLV2Vy.js +1 -0
  22. package/dist/public/assets/{_CX1wufuh.js → _7YrArFMB.js} +1 -1
  23. package/dist/public/assets/_7YrArFMB.js.br +0 -0
  24. package/dist/public/assets/{_mnBdD4YN.js → _B1-W0Sn2.js} +1 -1
  25. package/dist/public/assets/_B1-W0Sn2.js.br +0 -0
  26. package/dist/public/assets/{_BL9QMk_P.js → _B1D0PIz3.js} +1 -1
  27. package/dist/public/assets/_B1D0PIz3.js.br +0 -0
  28. package/dist/public/assets/_B1agoKUa.js +1 -0
  29. package/dist/public/assets/_B3NfNm8M.js +1591 -0
  30. package/dist/public/assets/_B3NfNm8M.js.br +0 -0
  31. package/dist/public/assets/{_DeCJ44Me.js → _B7VYILLZ.js} +1 -1
  32. package/dist/public/assets/_B7VYILLZ.js.br +0 -0
  33. package/dist/public/assets/_B7sPBy0J.js +1 -0
  34. package/dist/public/assets/_B81p6DSw.js +1 -0
  35. package/dist/public/assets/_BAkSKCkz.js +1 -0
  36. package/dist/public/assets/{_BUH04L5e.js → _BDUjU8Ev.js} +1 -1
  37. package/dist/public/assets/{_C6i9NP5A.js → _BHU5aikZ.js} +1 -1
  38. package/dist/public/assets/_BHZPTv1U.js +1 -0
  39. package/dist/public/assets/{_CttJbiei.js → _BKHtiW96.js} +1 -1
  40. package/dist/public/assets/_BKHtiW96.js.br +0 -0
  41. package/dist/public/assets/{_IWMmnVCy.js → _BKIx7IZP.js} +1 -1
  42. package/dist/public/assets/_BKIx7IZP.js.br +0 -0
  43. package/dist/public/assets/{_BCZpExov.js → _BLfzQY-3.js} +1 -1
  44. package/dist/public/assets/_BLfzQY-3.js.br +0 -0
  45. package/dist/public/assets/{_DhNuRs-V.js → _BPSTqkJn.js} +1 -1
  46. package/dist/public/assets/_BPSTqkJn.js.br +0 -0
  47. package/dist/public/assets/_BQZOimBB.js +1 -0
  48. package/dist/public/assets/{_vKQopDwG.js → _BRQ6iQ5p.js} +1 -1
  49. package/dist/public/assets/_BRQ6iQ5p.js.br +0 -0
  50. package/dist/public/assets/_BRVURc4u.js +1 -0
  51. package/dist/public/assets/{_eQK3EZCc.js → _BSFJEuJX.js} +1 -1
  52. package/dist/public/assets/_BSFJEuJX.js.br +0 -0
  53. package/dist/public/assets/{_B1RldJ0S.js → _BSWN-v3P.js} +1 -1
  54. package/dist/public/assets/_BSWN-v3P.js.br +0 -0
  55. package/dist/public/assets/_BTVAQr2c.js +1 -0
  56. package/dist/public/assets/_BUD1pBC0.js +1 -0
  57. package/dist/public/assets/_BVGzCczb.js +1 -0
  58. package/dist/public/assets/{_C1PmcEws.js → _BVQosHjY.js} +1 -1
  59. package/dist/public/assets/_BVQosHjY.js.br +0 -0
  60. package/dist/public/assets/{_DlpTErRA.js → _B_0C2_RO.js} +1 -1
  61. package/dist/public/assets/_B_0C2_RO.js.br +0 -0
  62. package/dist/public/assets/_BaA31ms5.js +1 -0
  63. package/dist/public/assets/_BgCHJGlO.js +1 -0
  64. package/dist/public/assets/_BhpRLDbL.js +1 -0
  65. package/dist/public/assets/_BhpRLDbL.js.br +0 -0
  66. package/dist/public/assets/_BkKcjMAC.js +1 -0
  67. package/dist/public/assets/{_BsimXDnm.js → _BkcPY2j0.js} +1 -1
  68. package/dist/public/assets/_BkcPY2j0.js.br +0 -0
  69. package/dist/public/assets/_BlTVd_3d.js +1 -0
  70. package/dist/public/assets/_BlqlLmEY.js +1 -0
  71. package/dist/public/assets/_BoNfxc9P.js +1 -0
  72. package/dist/public/assets/_BqswW9B9.js +1 -0
  73. package/dist/public/assets/_BricqzsO.js +1 -0
  74. package/dist/public/assets/_BrreckIa.js +1 -0
  75. package/dist/public/assets/{_CGLpCj_I.js → _BsJZPHmz.js} +1 -1
  76. package/dist/public/assets/_BsJZPHmz.js.br +0 -0
  77. package/dist/public/assets/_BtRYwaBb.js +1 -0
  78. package/dist/public/assets/{_CT3clsOQ.js → _BtZ2AatF.js} +1 -1
  79. package/dist/public/assets/_BvPBKjRP.js +1 -0
  80. package/dist/public/assets/_BvPBKjRP.js.br +0 -0
  81. package/dist/public/assets/_ByrrjMU9.js +1 -0
  82. package/dist/public/assets/{_s5Jez2mu.js → _BzGHediB.js} +1 -1
  83. package/dist/public/assets/_BzGHediB.js.br +0 -0
  84. package/dist/public/assets/_BzvTKm92.js +1 -0
  85. package/dist/public/assets/{_CnKYioYw.js → _C-EkQeP_.js} +1 -1
  86. package/dist/public/assets/_C-EkQeP_.js.br +0 -0
  87. package/dist/public/assets/{_BAHrOydH.js → _C0Ib7MZF.js} +1 -1
  88. package/dist/public/assets/_C0Ib7MZF.js.br +0 -0
  89. package/dist/public/assets/_C2p1Z5Lf.js +86 -0
  90. package/dist/public/assets/_C2p1Z5Lf.js.br +0 -0
  91. package/dist/public/assets/_C3iiOlxd.js +1 -0
  92. package/dist/public/assets/{_BdvO5Lqa.js → _C81nSl09.js} +1 -1
  93. package/dist/public/assets/_C81nSl09.js.br +0 -0
  94. package/dist/public/assets/{_BXT2bXMi.js → _C8bqYk4J.js} +1 -1
  95. package/dist/public/assets/_C8bqYk4J.js.br +0 -0
  96. package/dist/public/assets/_C98JZr09.js +1 -0
  97. package/dist/public/assets/_C98JZr09.js.br +0 -0
  98. package/dist/public/assets/_CB4VkmGZ.js +1 -0
  99. package/dist/public/assets/{_pUYwxsdF.js → _CCVkYcN5.js} +1 -1
  100. package/dist/public/assets/_CCVkYcN5.js.br +0 -0
  101. package/dist/public/assets/{_D-hK5uFn.js → _CDY0RpYX.js} +1 -1
  102. package/dist/public/assets/_CDY0RpYX.js.br +0 -0
  103. package/dist/public/assets/_CE2GEqMx.js +1 -0
  104. package/dist/public/assets/_CE2GEqMx.js.br +0 -0
  105. package/dist/public/assets/{_BeE9qDu0.js → _CFUREq1M.js} +1 -1
  106. package/dist/public/assets/_CFUREq1M.js.br +0 -0
  107. package/dist/public/assets/_CHdh6bII.js +1 -0
  108. package/dist/public/assets/{_CB31bp-W.js → _CHdpLB95.js} +1 -1
  109. package/dist/public/assets/_CHdpLB95.js.br +0 -0
  110. package/dist/public/assets/{_Fzbz4zvE.js → _CKLGAGKJ.js} +1 -1
  111. package/dist/public/assets/_CKLGAGKJ.js.br +0 -0
  112. package/dist/public/assets/_CMRt2lyQ.js +1 -0
  113. package/dist/public/assets/_CN1zW1AT.js +1 -0
  114. package/dist/public/assets/_CN1zW1AT.js.br +0 -0
  115. package/dist/public/assets/_CUILmj9R.js +1 -0
  116. package/dist/public/assets/_CUhP8KQ8.js +1 -0
  117. package/dist/public/assets/{_zKlxcAnQ.js → _CUsrMpdL.js} +1 -1
  118. package/dist/public/assets/_CUsrMpdL.js.br +0 -0
  119. package/dist/public/assets/_CVG0_w9T.js +1 -0
  120. package/dist/public/assets/{_CCCQMLw5.js → _CWG813ur.js} +1 -1
  121. package/dist/public/assets/_CWG813ur.js.br +0 -0
  122. package/dist/public/assets/{_C9OdJcx4.js → _CY87Y7QL.js} +1 -1
  123. package/dist/public/assets/_CY87Y7QL.js.br +0 -0
  124. package/dist/public/assets/_CYAsKva_.js +1 -0
  125. package/dist/public/assets/_CajKYNSY.js +1 -0
  126. package/dist/public/assets/{_gPFPzzPF.js → _Cb_k_JA_.js} +1 -1
  127. package/dist/public/assets/_Cb_k_JA_.js.br +0 -0
  128. package/dist/public/assets/_Ce8PlfMD.js +1 -0
  129. package/dist/public/assets/{_O70J1Mfv.js → _CfGEGt_k.js} +1 -1
  130. package/dist/public/assets/_CfGEGt_k.js.br +0 -0
  131. package/dist/public/assets/{_BLRpf6Uw.js → _CfdK8EeQ.js} +1 -1
  132. package/dist/public/assets/_CfdK8EeQ.js.br +0 -0
  133. package/dist/public/assets/_CgOKmAwr.js +1 -0
  134. package/dist/public/assets/{_C2ZpyiHV.js → _CiYQhf81.js} +1 -1
  135. package/dist/public/assets/_CiYQhf81.js.br +0 -0
  136. package/dist/public/assets/_Cj9DHPRL.js +1 -0
  137. package/dist/public/assets/{_BS4crAAm.js → _ClHPjF-1.js} +1 -1
  138. package/dist/public/assets/_ClHPjF-1.js.br +0 -0
  139. package/dist/public/assets/{_DmDSLxrw.js → _Cmmdd2e3.js} +1 -1
  140. package/dist/public/assets/_Cmmdd2e3.js.br +0 -0
  141. package/dist/public/assets/{_BsupnkWM.js → _Cn9Ia4SS.js} +1 -1
  142. package/dist/public/assets/_Cn9Ia4SS.js.br +0 -0
  143. package/dist/public/assets/_CnTzFbuG.js +1 -0
  144. package/dist/public/assets/{_DJ3UAR_s.js → _CqCenHOp.js} +1 -1
  145. package/dist/public/assets/_CqCenHOp.js.br +0 -0
  146. package/dist/public/assets/{_BRG_DBcC.js → _CtqGSS84.js} +1 -1
  147. package/dist/public/assets/_CtqGSS84.js.br +0 -0
  148. package/dist/public/assets/_CttfwM-c.js +1 -0
  149. package/dist/public/assets/{_DuXOUegE.js → _CxA3Yab0.js} +1 -1
  150. package/dist/public/assets/_CxA3Yab0.js.br +0 -0
  151. package/dist/public/assets/{_ifUb4WX7.js → _Cx_aJfDp.js} +1 -1
  152. package/dist/public/assets/_Cx_aJfDp.js.br +0 -0
  153. package/dist/public/assets/_D2L_ILOW.js +1 -0
  154. package/dist/public/assets/_D47lCu0c.js +1 -0
  155. package/dist/public/assets/_D5W-uGIK.js +1 -0
  156. package/dist/public/assets/_D6BbZLrc.js +1 -0
  157. package/dist/public/assets/_D6RVeCAn.js +1 -0
  158. package/dist/public/assets/_D75V4jV-.js +1 -0
  159. package/dist/public/assets/{_CwmzEsB4.js → _D9r_Fn0I.js} +1 -1
  160. package/dist/public/assets/_D9r_Fn0I.js.br +0 -0
  161. package/dist/public/assets/{_DoCtdMd2.js → _DBD57l1T.js} +1 -1
  162. package/dist/public/assets/_DBD57l1T.js.br +0 -0
  163. package/dist/public/assets/_DCWILZgL.js +1 -0
  164. package/dist/public/assets/{_DI0QHEXB.js → _DEOryr7w.js} +1 -1
  165. package/dist/public/assets/_DEOryr7w.js.br +0 -0
  166. package/dist/public/assets/_DF54Iz7o.js +1 -0
  167. package/dist/public/assets/_DFamU2Uj.js +2 -0
  168. package/dist/public/assets/_DFamU2Uj.js.br +0 -0
  169. package/dist/public/assets/_DHmBuyCO.js +1 -0
  170. package/dist/public/assets/_DHmBuyCO.js.br +0 -0
  171. package/dist/public/assets/{_BPcswKMK.js → _DJqEBkNd.js} +1 -1
  172. package/dist/public/assets/_DJqEBkNd.js.br +0 -0
  173. package/dist/public/assets/{_B_zyAtSJ.js → _DL3w8c_p.js} +1 -1
  174. package/dist/public/assets/_DL3w8c_p.js.br +0 -0
  175. package/dist/public/assets/_DLVL-4MP.js +1 -0
  176. package/dist/public/assets/{_BewXp5Y_.js → _DM60dKRs.js} +1 -1
  177. package/dist/public/assets/_DM60dKRs.js.br +0 -0
  178. package/dist/public/assets/{_BVp6r0fg.js → _DMJHTH74.js} +1 -1
  179. package/dist/public/assets/_DMJHTH74.js.br +0 -0
  180. package/dist/public/assets/_DMLv6KA6.js +1 -0
  181. package/dist/public/assets/_DQYpgyzr.js +1 -0
  182. package/dist/public/assets/_DQYpgyzr.js.br +0 -0
  183. package/dist/public/assets/{_BbiACo0Y.js → _DQyjuXTi.js} +1 -1
  184. package/dist/public/assets/_DQyjuXTi.js.br +0 -0
  185. package/dist/public/assets/{_C87NBm9i.js → _DRHCy0UF.js} +1 -1
  186. package/dist/public/assets/_DRHCy0UF.js.br +0 -0
  187. package/dist/public/assets/{_pvS44-AC.js → _DSUBv-Bl.js} +1 -1
  188. package/dist/public/assets/_DSUBv-Bl.js.br +0 -0
  189. package/dist/public/assets/{_BA8HFnO3.js → _DUFM9qLV.js} +1 -1
  190. package/dist/public/assets/_DUFM9qLV.js.br +0 -0
  191. package/dist/public/assets/{_D8u1YU9b.js → _DUs3ZGbj.js} +1 -1
  192. package/dist/public/assets/_DUs3ZGbj.js.br +0 -0
  193. package/dist/public/assets/{_CdgAsi3s.js → _DX5u6Wvm.js} +1 -1
  194. package/dist/public/assets/_DX5u6Wvm.js.br +0 -0
  195. package/dist/public/assets/{_BnyLUTdO.js → _DZvkgS3V.js} +1 -1
  196. package/dist/public/assets/_DZvkgS3V.js.br +0 -0
  197. package/dist/public/assets/{_CE5MhJWL.js → _Dbm6zAQS.js} +1 -1
  198. package/dist/public/assets/_Dbm6zAQS.js.br +0 -0
  199. package/dist/public/assets/{_DzrtBP-L.js → _DcOkTFth.js} +1 -1
  200. package/dist/public/assets/_Ddf1gumD.js +1 -0
  201. package/dist/public/assets/_De6E9M7R.js +1 -0
  202. package/dist/public/assets/{_C-6dImS5.js → _DgWKSW-Y.js} +1 -1
  203. package/dist/public/assets/_DgWKSW-Y.js.br +0 -0
  204. package/dist/public/assets/_DhEN6T4v.js +1 -0
  205. package/dist/public/assets/{_BxAl0m64.js → _Di1ccq_2.js} +1 -1
  206. package/dist/public/assets/_Di1ccq_2.js.br +0 -0
  207. package/dist/public/assets/{_CLerZOx-.js → _DifjKObE.js} +1 -1
  208. package/dist/public/assets/_DifjKObE.js.br +0 -0
  209. package/dist/public/assets/_Dn10ABa-.js +1 -0
  210. package/dist/public/assets/{_DuGmU4h0.js → _Do1ltY2a.js} +1 -1
  211. package/dist/public/assets/_Do1ltY2a.js.br +0 -0
  212. package/dist/public/assets/{_DeN4EY8L.js → _DomK7Scb.js} +1 -1
  213. package/dist/public/assets/_DomK7Scb.js.br +0 -0
  214. package/dist/public/assets/_DtWggCxt.js +1 -0
  215. package/dist/public/assets/_DtqqybyQ.js +1 -0
  216. package/dist/public/assets/{_CJ9j8N5Z.js → _DvKnakqk.js} +1 -1
  217. package/dist/public/assets/_DvKnakqk.js.br +0 -0
  218. package/dist/public/assets/_DxD_AC3a.js +1 -0
  219. package/dist/public/assets/_DxD_AC3a.js.br +0 -0
  220. package/dist/public/assets/_Dxo7m9cG.js +1 -0
  221. package/dist/public/assets/{_DhEdzMUw.js → _DzKW89Sx.js} +1 -1
  222. package/dist/public/assets/_DzKW89Sx.js.br +0 -0
  223. package/dist/public/assets/_DzTHCioH.js +1 -0
  224. package/dist/public/assets/{_CtyJUmXC.js → _EefWj858.js} +1 -1
  225. package/dist/public/assets/_EefWj858.js.br +0 -0
  226. package/dist/public/assets/{_Aq9oGnRu.js → _FlITqTCo.js} +1 -1
  227. package/dist/public/assets/_FlITqTCo.js.br +0 -0
  228. package/dist/public/assets/_GkEw2S0T.js +1 -0
  229. package/dist/public/assets/{__Oa255vo.js → _HM_QqtJi.js} +1 -1
  230. package/dist/public/assets/_HM_QqtJi.js.br +0 -0
  231. package/dist/public/assets/{_DuarImb7.js → _HmkI4Rfn.js} +1 -1
  232. package/dist/public/assets/_HmkI4Rfn.js.br +0 -0
  233. package/dist/public/assets/{_DaqT6m41.js → _JEvx8ub4.js} +1 -1
  234. package/dist/public/assets/_JEvx8ub4.js.br +0 -0
  235. package/dist/public/assets/_JOb8B3tz.js +1 -0
  236. package/dist/public/assets/_LEIhjFrO.js +1 -0
  237. package/dist/public/assets/_STJhwy7U.js +1 -0
  238. package/dist/public/assets/_TrcWRD8T.js +15 -0
  239. package/dist/public/assets/_TrcWRD8T.js.br +0 -0
  240. package/dist/public/assets/{_BO0OB-rz.js → _UaKPY71M.js} +1 -1
  241. package/dist/public/assets/_UaKPY71M.js.br +0 -0
  242. package/dist/public/assets/_UhK3lkm0.js +1 -0
  243. package/dist/public/assets/_VPLoEBTS.js +1 -0
  244. package/dist/public/assets/_Zeftz3t1.js +1 -0
  245. package/dist/public/assets/{_pP1W6w5v.js → __cyRVmTO.js} +1 -1
  246. package/dist/public/assets/__cyRVmTO.js.br +0 -0
  247. package/dist/public/assets/_awfYTHit.js +1 -0
  248. package/dist/public/assets/{_DiqV5bDH.js → _dsj7vMTW.js} +1 -1
  249. package/dist/public/assets/_dsj7vMTW.js.br +0 -0
  250. package/dist/public/assets/_eqpiOhSM.js +1 -0
  251. package/dist/public/assets/{_dr95P55a.js → _iutZ4EDe.js} +1 -1
  252. package/dist/public/assets/_iutZ4EDe.js.br +0 -0
  253. package/dist/public/assets/{_CZGS4T08.js → _kWQ0dtYZ.js} +1 -1
  254. package/dist/public/assets/_kWQ0dtYZ.js.br +0 -0
  255. package/dist/public/assets/_knvMqXQh.js +1 -0
  256. package/dist/public/assets/_lwC7sIgC.js +1 -0
  257. package/dist/public/assets/_m2tRa0jk.js +1 -0
  258. package/dist/public/assets/_m2tRa0jk.js.br +0 -0
  259. package/dist/public/assets/{_DwcoW-IU.js → _pXG8fRON.js} +1 -1
  260. package/dist/public/assets/_pXG8fRON.js.br +0 -0
  261. package/dist/public/assets/_r541ENtr.js +1 -0
  262. package/dist/public/assets/_rrUx1wn3.js +1 -0
  263. package/dist/public/assets/_tJvcu7sD.js +1 -0
  264. package/dist/public/assets/{_Cw_8WbwF.js → _vcLBrU2m.js} +1 -1
  265. package/dist/public/assets/_vcLBrU2m.js.br +0 -0
  266. package/dist/public/assets/_xZC4Vm70.js +1 -0
  267. package/dist/public/assets/_yAr5r-uL.js +1 -0
  268. package/dist/public/assets/{_DGTQBWy0.js → _yUmRJIJ6.js} +1 -1
  269. package/dist/public/assets/_yUmRJIJ6.js.br +0 -0
  270. package/dist/public/assets/app-shell-CVYlktwa.css +1 -0
  271. package/dist/public/assets/app-shell-CVYlktwa.css.br +0 -0
  272. package/dist/public/assets/files-view-CTadakLK.js +2 -0
  273. package/dist/public/assets/manage-workspace-HUAS6qjy.js +2 -0
  274. package/dist/public/assets/new-CirJkJFl.js +1 -0
  275. package/dist/public/assets/new-workspace-C3t1nA6z.js +2 -0
  276. package/dist/public/assets/review-view-DKPNtgmc.js +2 -0
  277. package/dist/public/assets/settings-CirJkJFl.js +1 -0
  278. package/dist/public/assets/settings-page-DH-9cN-8.js +2 -0
  279. package/dist/public/assets/styles-CIeNhVxC.css +1 -0
  280. package/dist/public/assets/styles-CIeNhVxC.css.br +0 -0
  281. package/dist/public/assets/w-CirJkJFl.js +1 -0
  282. package/dist/public/assets/w._-BnShhzdq.js +1 -0
  283. package/dist/public/assets/w._-CirJkJFl.js +1 -0
  284. package/dist/public/assets/w._-aCTYfruH.js +1 -0
  285. package/lib/review/markdown.mjs +5 -5
  286. package/lib/setup/bridge.d.mts +10 -0
  287. package/lib/setup/bridge.mjs +84 -0
  288. package/lib/setup/doctor.d.mts +17 -1
  289. package/lib/setup/doctor.mjs +97 -8
  290. package/lib/setup/install.d.mts +1 -0
  291. package/lib/setup/install.mjs +21 -1
  292. package/lib/setup/listen.mjs +2 -2
  293. package/lib/setup/tailscale.d.mts +53 -0
  294. package/lib/setup/tailscale.mjs +179 -0
  295. package/lib/share/manifest.mjs +2 -1
  296. package/package.json +2 -3
  297. package/dist/_LF2Fo5WE.js +0 -2
  298. package/dist/public/assets/_--tE4Gxz.js +0 -2
  299. package/dist/public/assets/_--tE4Gxz.js.br +0 -0
  300. package/dist/public/assets/_1OL9vXM-.js +0 -1
  301. package/dist/public/assets/_2mnAZf6B.js +0 -1
  302. package/dist/public/assets/_2mnAZf6B.js.br +0 -0
  303. package/dist/public/assets/_5f9fMvWJ.js +0 -1
  304. package/dist/public/assets/_5y69OR6M.js +0 -1
  305. package/dist/public/assets/_Aq9oGnRu.js.br +0 -0
  306. package/dist/public/assets/_B0uJCjmk.js +0 -1
  307. package/dist/public/assets/_B1RldJ0S.js.br +0 -0
  308. package/dist/public/assets/_B5TpOQCZ.js +0 -1
  309. package/dist/public/assets/_B6UdulKa.js +0 -1
  310. package/dist/public/assets/_BA8HFnO3.js.br +0 -0
  311. package/dist/public/assets/_BAHrOydH.js.br +0 -0
  312. package/dist/public/assets/_BBpX-M4b.js.br +0 -0
  313. package/dist/public/assets/_BCZpExov.js.br +0 -0
  314. package/dist/public/assets/_BFZHNauV.js +0 -1
  315. package/dist/public/assets/_BJRSndB4.js +0 -6
  316. package/dist/public/assets/_BJRSndB4.js.br +0 -0
  317. package/dist/public/assets/_BL9QMk_P.js.br +0 -0
  318. package/dist/public/assets/_BLNEzyb5.js +0 -1
  319. package/dist/public/assets/_BLRpf6Uw.js.br +0 -0
  320. package/dist/public/assets/_BLTfQLdY.js.br +0 -0
  321. package/dist/public/assets/_BO0OB-rz.js.br +0 -0
  322. package/dist/public/assets/_BPcswKMK.js.br +0 -0
  323. package/dist/public/assets/_BPhXWliU.js +0 -1
  324. package/dist/public/assets/_BQXTv9Cr.js +0 -1
  325. package/dist/public/assets/_BRG_DBcC.js.br +0 -0
  326. package/dist/public/assets/_BS4crAAm.js.br +0 -0
  327. package/dist/public/assets/_BVp6r0fg.js.br +0 -0
  328. package/dist/public/assets/_BXT2bXMi.js.br +0 -0
  329. package/dist/public/assets/_B_zyAtSJ.js.br +0 -0
  330. package/dist/public/assets/_BaXKdIeb.js +0 -1
  331. package/dist/public/assets/_BbiACo0Y.js.br +0 -0
  332. package/dist/public/assets/_BdvO5Lqa.js.br +0 -0
  333. package/dist/public/assets/_BeE9qDu0.js.br +0 -0
  334. package/dist/public/assets/_BewXp5Y_.js.br +0 -0
  335. package/dist/public/assets/_BfOOLKo5.js +0 -1
  336. package/dist/public/assets/_BhS8bTnr.js +0 -1
  337. package/dist/public/assets/_BhZ3ZoG0.js +0 -1
  338. package/dist/public/assets/_Bi3_ZsOQ.js +0 -1
  339. package/dist/public/assets/_Bj_Lfa8B.js +0 -1
  340. package/dist/public/assets/_BlN_x04q.js +0 -1
  341. package/dist/public/assets/_Bn5nsnie.js +0 -1
  342. package/dist/public/assets/_BnOOD6nr.js +0 -15
  343. package/dist/public/assets/_BnOOD6nr.js.br +0 -0
  344. package/dist/public/assets/_BnyLUTdO.js.br +0 -0
  345. package/dist/public/assets/_Bp_WT09q.js +0 -1
  346. package/dist/public/assets/_Bp_WT09q.js.br +0 -0
  347. package/dist/public/assets/_BsimXDnm.js.br +0 -0
  348. package/dist/public/assets/_BsupnkWM.js.br +0 -0
  349. package/dist/public/assets/_BxAl0m64.js.br +0 -0
  350. package/dist/public/assets/_C-6dImS5.js.br +0 -0
  351. package/dist/public/assets/_C18Torhh.js +0 -1
  352. package/dist/public/assets/_C1PmcEws.js.br +0 -0
  353. package/dist/public/assets/_C2ZpyiHV.js.br +0 -0
  354. package/dist/public/assets/_C3P1VHUI.js +0 -1
  355. package/dist/public/assets/_C6xwTKke.js +0 -1
  356. package/dist/public/assets/_C6yqlRfl.js +0 -1
  357. package/dist/public/assets/_C75D8bH2.js +0 -1
  358. package/dist/public/assets/_C87NBm9i.js.br +0 -0
  359. package/dist/public/assets/_C8Vc_vBH.js +0 -1
  360. package/dist/public/assets/_C9OdJcx4.js.br +0 -0
  361. package/dist/public/assets/_C9eoo0sE.js +0 -1
  362. package/dist/public/assets/_C9qO_dny.js +0 -1
  363. package/dist/public/assets/_C9qO_dny.js.br +0 -0
  364. package/dist/public/assets/_CB31bp-W.js.br +0 -0
  365. package/dist/public/assets/_CCCQMLw5.js.br +0 -0
  366. package/dist/public/assets/_CCie36pi.js +0 -1
  367. package/dist/public/assets/_CDD9N34p.js +0 -1
  368. package/dist/public/assets/_CE5MhJWL.js.br +0 -0
  369. package/dist/public/assets/_CGLpCj_I.js.br +0 -0
  370. package/dist/public/assets/_CJ9j8N5Z.js.br +0 -0
  371. package/dist/public/assets/_CLDmR3jR.js +0 -1
  372. package/dist/public/assets/_CLDmR3jR.js.br +0 -0
  373. package/dist/public/assets/_CLerZOx-.js.br +0 -0
  374. package/dist/public/assets/_CM5GtJb2.js +0 -1
  375. package/dist/public/assets/_COECwOVb.js +0 -1
  376. package/dist/public/assets/_CSQE9nMN.js +0 -1
  377. package/dist/public/assets/_CSXMgHLQ.js +0 -1
  378. package/dist/public/assets/_CVRiByCT.js +0 -1
  379. package/dist/public/assets/_CVSrm4Pl.js +0 -1
  380. package/dist/public/assets/_CX1wufuh.js.br +0 -0
  381. package/dist/public/assets/_CYNAhnRH.js +0 -1591
  382. package/dist/public/assets/_CYNAhnRH.js.br +0 -0
  383. package/dist/public/assets/_CZGS4T08.js.br +0 -0
  384. package/dist/public/assets/_C_L7l9Mg.js +0 -1
  385. package/dist/public/assets/_CaMrYy1W.js +0 -1
  386. package/dist/public/assets/_CdgAsi3s.js.br +0 -0
  387. package/dist/public/assets/_Ce-E7sJE.js +0 -1
  388. package/dist/public/assets/_Cg3S760D.js +0 -1
  389. package/dist/public/assets/_ChpYg41R.js +0 -1
  390. package/dist/public/assets/_CicyY7KP.js +0 -1
  391. package/dist/public/assets/_Ck0YOw_7.js +0 -1
  392. package/dist/public/assets/_CmHlTZSs.js +0 -1
  393. package/dist/public/assets/_CnECYh66.js +0 -1
  394. package/dist/public/assets/_CnKYioYw.js.br +0 -0
  395. package/dist/public/assets/_CnUVvVPd.js +0 -1
  396. package/dist/public/assets/_CqSgGCGC.js +0 -1
  397. package/dist/public/assets/_CttJbiei.js.br +0 -0
  398. package/dist/public/assets/_CtyJUmXC.js.br +0 -0
  399. package/dist/public/assets/_Cw_8WbwF.js.br +0 -0
  400. package/dist/public/assets/_CwmzEsB4.js.br +0 -0
  401. package/dist/public/assets/_CzETFsJs.js +0 -1
  402. package/dist/public/assets/_D-S9H-tX.js +0 -1
  403. package/dist/public/assets/_D-hK5uFn.js.br +0 -0
  404. package/dist/public/assets/_D5fTwHXK.js +0 -1
  405. package/dist/public/assets/_D8u1YU9b.js.br +0 -0
  406. package/dist/public/assets/_DAH8evhA.js +0 -1
  407. package/dist/public/assets/_DAemmoHI.js +0 -1
  408. package/dist/public/assets/_DEMYxWZf.js +0 -1
  409. package/dist/public/assets/_DGTQBWy0.js.br +0 -0
  410. package/dist/public/assets/_DHhv0pCA.js +0 -1
  411. package/dist/public/assets/_DI0QHEXB.js.br +0 -0
  412. package/dist/public/assets/_DJ3UAR_s.js.br +0 -0
  413. package/dist/public/assets/_DKu6UxI7.js +0 -1
  414. package/dist/public/assets/_DNC-9RBN.js +0 -1
  415. package/dist/public/assets/_DP-Bu7ET.js +0 -1
  416. package/dist/public/assets/_DYmmPBcq.js +0 -1
  417. package/dist/public/assets/_Da2Dmt_d.js +0 -1
  418. package/dist/public/assets/_Da2Dmt_d.js.br +0 -0
  419. package/dist/public/assets/_DaqT6m41.js.br +0 -0
  420. package/dist/public/assets/_DbVyutEZ.js +0 -1
  421. package/dist/public/assets/_DcBvO2Si.js +0 -1
  422. package/dist/public/assets/_DeCJ44Me.js.br +0 -0
  423. package/dist/public/assets/_DeN4EY8L.js.br +0 -0
  424. package/dist/public/assets/_DhEdzMUw.js.br +0 -0
  425. package/dist/public/assets/_DhNuRs-V.js.br +0 -0
  426. package/dist/public/assets/_DiqV5bDH.js.br +0 -0
  427. package/dist/public/assets/_Diqt_yCf.js +0 -1
  428. package/dist/public/assets/_DlpTErRA.js.br +0 -0
  429. package/dist/public/assets/_DmDSLxrw.js.br +0 -0
  430. package/dist/public/assets/_DoC0qbSD.js +0 -1
  431. package/dist/public/assets/_DoCtdMd2.js.br +0 -0
  432. package/dist/public/assets/_DpqWFkMg.js +0 -1
  433. package/dist/public/assets/_Dr0Bkgso.js +0 -1
  434. package/dist/public/assets/_Dt-n9FaQ.js +0 -1
  435. package/dist/public/assets/_DtOzdwHm.js +0 -1
  436. package/dist/public/assets/_DtOzdwHm.js.br +0 -0
  437. package/dist/public/assets/_DuGmU4h0.js.br +0 -0
  438. package/dist/public/assets/_DuXOUegE.js.br +0 -0
  439. package/dist/public/assets/_DuaocJd1.js +0 -1
  440. package/dist/public/assets/_DuarImb7.js.br +0 -0
  441. package/dist/public/assets/_DwcoW-IU.js.br +0 -0
  442. package/dist/public/assets/_DxVt7Zuk.js +0 -86
  443. package/dist/public/assets/_DxVt7Zuk.js.br +0 -0
  444. package/dist/public/assets/_Fzbz4zvE.js.br +0 -0
  445. package/dist/public/assets/_I5oX-lGP.js +0 -1
  446. package/dist/public/assets/_IWMmnVCy.js.br +0 -0
  447. package/dist/public/assets/_O70J1Mfv.js.br +0 -0
  448. package/dist/public/assets/_SwbsOoi9.js +0 -1
  449. package/dist/public/assets/_SwbsOoi9.js.br +0 -0
  450. package/dist/public/assets/_Yy39CykK.js +0 -1
  451. package/dist/public/assets/__Oa255vo.js.br +0 -0
  452. package/dist/public/assets/_bPze7k1x.js +0 -1
  453. package/dist/public/assets/_bU1KcT1I.js +0 -1
  454. package/dist/public/assets/_dr95P55a.js.br +0 -0
  455. package/dist/public/assets/_eQK3EZCc.js.br +0 -0
  456. package/dist/public/assets/_gPFPzzPF.js.br +0 -0
  457. package/dist/public/assets/_hzpOPZUB.js +0 -1
  458. package/dist/public/assets/_ifUb4WX7.js.br +0 -0
  459. package/dist/public/assets/_kQ7LiSim.js +0 -1
  460. package/dist/public/assets/_lOAW1VLM.js +0 -1
  461. package/dist/public/assets/_mnBdD4YN.js.br +0 -0
  462. package/dist/public/assets/_odBcV1kc.js +0 -1
  463. package/dist/public/assets/_pP1W6w5v.js.br +0 -0
  464. package/dist/public/assets/_pUYwxsdF.js.br +0 -0
  465. package/dist/public/assets/_pvS44-AC.js.br +0 -0
  466. package/dist/public/assets/_qJTL2ooe.js +0 -1
  467. package/dist/public/assets/_qJTL2ooe.js.br +0 -0
  468. package/dist/public/assets/_s5Jez2mu.js.br +0 -0
  469. package/dist/public/assets/_unfL8mz1.js.br +0 -0
  470. package/dist/public/assets/_unwtcXFQ.js +0 -1
  471. package/dist/public/assets/_unwtcXFQ.js.br +0 -0
  472. package/dist/public/assets/_vKQopDwG.js.br +0 -0
  473. package/dist/public/assets/_w1aegGdT.js +0 -1
  474. package/dist/public/assets/_xdrkfok5.js +0 -1
  475. package/dist/public/assets/_xdrkfok5.js.br +0 -0
  476. package/dist/public/assets/_y-lm36fI.js +0 -1
  477. package/dist/public/assets/_zKlxcAnQ.js.br +0 -0
  478. package/dist/public/assets/app-shell-CpPCwWPW.css +0 -1
  479. package/dist/public/assets/app-shell-CpPCwWPW.css.br +0 -0
  480. package/dist/public/assets/files-view-BfCdcfXX.js +0 -2
  481. package/dist/public/assets/files-view-DtI7JE-f.css +0 -1
  482. package/dist/public/assets/files-view-DtI7JE-f.css.br +0 -0
  483. package/dist/public/assets/group.css.ts-SeaNz7WZ.css +0 -1
  484. package/dist/public/assets/group.css.ts-SeaNz7WZ.css.br +0 -0
  485. package/dist/public/assets/icon-folder-DHVc-REl.css +0 -1
  486. package/dist/public/assets/icon-folder-DHVc-REl.css.br +0 -0
  487. package/dist/public/assets/icon-search-DAtjHWFL.css +0 -1
  488. package/dist/public/assets/icon-search-DAtjHWFL.css.br +0 -0
  489. package/dist/public/assets/manage-workspace-6i6Zeai7.css +0 -1
  490. package/dist/public/assets/manage-workspace-CixeMkdt.js +0 -2
  491. package/dist/public/assets/modal-view-D0qnmuJE.css +0 -1
  492. package/dist/public/assets/modal-view-D0qnmuJE.css.br +0 -0
  493. package/dist/public/assets/new-DxhaKmbF.js +0 -1
  494. package/dist/public/assets/new-workspace-BXjDRbqF.css +0 -1
  495. package/dist/public/assets/new-workspace-BXjDRbqF.css.br +0 -0
  496. package/dist/public/assets/new-workspace-DG693v2S.js +0 -2
  497. package/dist/public/assets/port-chip-Lz3mVJWb.css +0 -1
  498. package/dist/public/assets/port-chip-Lz3mVJWb.css.br +0 -0
  499. package/dist/public/assets/prompt-dialog-vhnae1-x.css +0 -1
  500. package/dist/public/assets/prompt-dialog-vhnae1-x.css.br +0 -0
  501. package/dist/public/assets/quick-open-DVqzk3-g.css +0 -1
  502. package/dist/public/assets/quick-open-DVqzk3-g.css.br +0 -0
  503. package/dist/public/assets/repo-picker-K5RYsEge.css +0 -1
  504. package/dist/public/assets/repo-picker-K5RYsEge.css.br +0 -0
  505. package/dist/public/assets/review-view-DqMrB4Ny.css +0 -1
  506. package/dist/public/assets/review-view-DqMrB4Ny.css.br +0 -0
  507. package/dist/public/assets/review-view-TdLbCgEg.js +0 -2
  508. package/dist/public/assets/section-2MyJSq5D.css +0 -1
  509. package/dist/public/assets/section-2MyJSq5D.css.br +0 -0
  510. package/dist/public/assets/settings-DxhaKmbF.js +0 -1
  511. package/dist/public/assets/settings-page-BBAG94Vh.js +0 -2
  512. package/dist/public/assets/settings-page-DUefc32x.css +0 -1
  513. package/dist/public/assets/settings-page-DUefc32x.css.br +0 -0
  514. package/dist/public/assets/skill-row-BeOk8NK8.css +0 -1
  515. package/dist/public/assets/sso-auth-CU3fGJ4U.css +0 -1
  516. package/dist/public/assets/sso-auth-CU3fGJ4U.css.br +0 -0
  517. package/dist/public/assets/w-DxhaKmbF.js +0 -1
  518. package/dist/public/assets/w._-C6JVdFgi.js +0 -1
  519. package/dist/public/assets/w._-DxhaKmbF.js +0 -1
  520. package/dist/public/assets/w._-rfvcDGPk.js +0 -1
  521. package/scripts/migrate.mjs +0 -682
package/README.md CHANGED
@@ -44,12 +44,6 @@ offers the agent CLIs, and runs `pnpm install`:
44
44
 
45
45
  ```sh
46
46
  pnpm dev # http://localhost:7171
47
- ```
48
-
49
- For a production build plus a background service (systemd on Linux, launchd on macOS):
50
-
51
- ```sh
52
- ./scripts/setup-prod.sh # build + install + start the service
53
47
 
54
48
  # While hacking on work itself, point the background service at a checkout
55
49
  # (and back at the npm install when done):
@@ -58,8 +52,6 @@ work-source local # this tree (or: work-source local /path/to/work)
58
52
  work-source rebuild # rebuild + restart without rewriting the unit
59
53
  work-source prod # global @dylanpiercey/work again
60
54
  work-source # status
61
- ./scripts/run-prod.sh # or: build with --no-service and run manually
62
- ./scripts/update.sh # later: pull latest, rebuild, restart
63
55
  ```
64
56
 
65
57
  ### Access
@@ -85,18 +77,28 @@ Access, `tailscale serve` — and tell work about it:
85
77
  Set them when installing (`WORK_ALLOWED_HOSTS=… WORK_TRUSTED_PROXIES=…
86
78
  WORK_LISTEN=0.0.0.0 work setup`); `work setup` keeps them in the service
87
79
  from then on. A refused request gets a page saying which setting it needs.
88
- Installs from before this default keep listening on every interface, and
89
- other machines see that page until they are configured. Everything (pages,
90
- API, and the `/term` websockets) serves from the single app port, so route
91
- the whole hostname to it.
80
+ Everything (pages, API, and the `/term` websockets) serves from the single
81
+ app port, so route the whole hostname to it.
82
+
83
+ **Tailscale** is the zero-config way. On a machine logged in to Tailscale
84
+ (1.52 or newer), `work setup` offers to run `tailscale serve --bg <port>`,
85
+ which puts work at `https://<machine>.<tailnet>.ts.net/` for your devices on
86
+ the tailnet alone (never Funnel, which would publish it to the internet),
87
+ and adds that name to `WORK_ALLOWED_HOSTS`: open it on a phone signed in to
88
+ the same tailnet. `work setup --tailscale` does it without asking and
89
+ `--no-tailscale` skips the question; `--yes` alone leaves Tailscale's config
90
+ as it is. A serve config already pointing at work only has its name
91
+ allowed, and `work doctor` says which of these holds.
92
92
 
93
93
  **Opening workspace ports remotely.** The port chips in the sidebar link to
94
94
  `http://<host>:<port>`, which only works where the machine is reachable
95
- directly. Through a tunnel, route a wildcard hostname to the app port as
96
- well and work proxies it — HTTP and websockets, so HMR works — to
97
- `localhost:<port>`. `WORK_PORT_HOSTS` picks the hostname shape the chips
98
- link to; `sub` is always accepted, `dash` only when picked, and only for a
99
- name of three labels or more:
95
+ directly — under a tailnet name too, since `tailscale serve` has no
96
+ wildcard names: there a chip reaches a dev server that listens beyond
97
+ loopback (`--host`), and one on loopback says so. Through a tunnel, route
98
+ a wildcard hostname to the app port as well and work proxies it — HTTP and
99
+ websockets, so HMR works — to `localhost:<port>`. `WORK_PORT_HOSTS` picks
100
+ the hostname shape the chips link to; `sub` is always accepted, `dash` only
101
+ when picked, and only for a name of three labels or more:
100
102
 
101
103
  - `sub` (default): `https://3000.work.example.com`. Needs a wildcard
102
104
  `*.work.example.com` route and a certificate that covers it (your own
@@ -114,11 +116,6 @@ auth as the app. API writes and the terminal websockets refuse a foreign
114
116
  `Origin`, so a page a workspace serves cannot drive work with the shared
115
117
  login cookie.
116
118
 
117
- > **Upgrading from the two-port layout:** older setups routed `/term/*` to a
118
- > separate terminal port (7172). That port no longer exists — remove the
119
- > `/term` rule from your tunnel/proxy config and send all traffic to the app
120
- > port, or terminals will fail to connect.
121
-
122
119
  ## Backup, restore, join
123
120
 
124
121
  A `work` machine holds almost nothing that lives only there: branches are
@@ -163,16 +160,6 @@ targeting that branch.
163
160
  | `WORK_USER` / `WORK_HOST` | current user/host | Branch-name prefix parts |
164
161
  | `WORK_BRANCH_PREFIX` | `<user>-<host>` | Whole branch-name prefix, overriding the parts |
165
162
 
166
- Ports and paths can be exported before `scripts/setup-prod.sh` to bake them into
167
- the service. For settings that should outlive an update, put them in an untracked
168
- `.env.local` at the repo root instead — `scripts/run-prod.sh` reads it on every
169
- start (the dev server does not), only `WORK_*` names apply, and the
170
- surrounding environment still takes precedence:
171
-
172
- ```sh
173
- WORK_BRANCH_PREFIX=rturnq
174
- ```
175
-
176
163
  The usage panel (and `work usage`) reads plan/quota windows straight from each
177
164
  agent CLI's own credentials on disk — Claude, Codex, Cursor, Grok, and Copilot
178
165
  (via `gh`'s token). Providers you are not signed into simply say so.
package/bin/work.mjs CHANGED
@@ -2,6 +2,8 @@
2
2
  // work CLI — ships with the npm package (no dependencies; plain node).
3
3
  //
4
4
  // work setup install missing tools, sign in, install/refresh the background service
5
+ // (--tailscale serves work on the tailnet with `tailscale serve`, which
6
+ // it otherwise offers when Tailscale is logged in; --no-tailscale skips that)
5
7
  // work doctor report machine readiness (--json, --fix to install what's missing)
6
8
  // work start run the server in the foreground
7
9
  // work update upgrade the npm package and refresh the service
@@ -26,9 +28,19 @@ import { execFileSync, spawnSync } from "node:child_process";
26
28
  import fs from "node:fs";
27
29
  import os from "node:os";
28
30
  import path from "node:path";
31
+ import readline from "node:readline/promises";
32
+ import tty from "node:tty";
29
33
  import { fileURLToPath } from "node:url";
30
- import { doctor, formatDoctor, setupPath } from "../lib/setup/doctor.mjs";
31
- import { installTool } from "../lib/setup/install.mjs";
34
+ import {
35
+ doctor,
36
+ formatDoctor,
37
+ setupPath,
38
+ tailscaleProbe,
39
+ which,
40
+ } from "../lib/setup/doctor.mjs";
41
+ import { allowHost, stateText } from "../lib/setup/tailscale.mjs";
42
+ import { installTool, repairGhHelpers } from "../lib/setup/install.mjs";
43
+ import { bridgeCommands, bridgeIfNeeded } from "../lib/setup/bridge.mjs";
32
44
  import { dialHost, listenStream } from "../lib/setup/listen.mjs";
33
45
  import { commentMarkdown } from "../lib/review/markdown.mjs";
34
46
  import { formatInvite, parseInvite } from "../lib/share/manifest.mjs";
@@ -87,9 +99,12 @@ switch (command) {
87
99
  await setup();
88
100
  break;
89
101
  case "install-service":
90
- // Service installation only — no doctor/fix, no migrate. Used by
91
- // scripts/setup-prod.sh so npm and source installs share one
92
- // definition; WORK_SERVICE_EXEC points it at a checkout's run-prod.sh.
102
+ // Service installation only — no doctor/fix. Used by
103
+ // scripts/work-source.sh so a checkout and the npm package share one
104
+ // definition; WORK_SERVICE_EXEC points it at the checkout's
105
+ // run-local.sh. The database is brought to the baseline first all the
106
+ // same: a server this checkout builds cannot open one from before it.
107
+ await bridge();
93
108
  if (!installService()) {
94
109
  process.exitCode = 1;
95
110
  } else if (await waitForServer()) {
@@ -101,12 +116,6 @@ switch (command) {
101
116
  process.exitCode = 1;
102
117
  }
103
118
  break;
104
- case "migrate":
105
- // The one-shot cleanups alone, against the service's settings (and the
106
- // checkout's .env.local when WORK_SERVICE_EXEC names one). Used by
107
- // scripts/update.sh ahead of setup-prod.sh.
108
- process.exitCode = migrate();
109
- break;
110
119
  case "doctor":
111
120
  await doctorCmd();
112
121
  break;
@@ -151,7 +160,7 @@ switch (command) {
151
160
  break;
152
161
  default:
153
162
  console.log(
154
- `work ${pkg.version}\n\nusage: work <setup|install-service|migrate|doctor|start|update|usage|workspaces|workspace|repos|procs|review|export|restore|share|join|version> [--json]`,
163
+ `work ${pkg.version}\n\nusage: work <setup|install-service|doctor|start|update|usage|workspaces|workspace|repos|procs|review|export|restore|share|join|version> [--json]`,
155
164
  );
156
165
  }
157
166
 
@@ -639,19 +648,24 @@ function until(iso) {
639
648
  }
640
649
 
641
650
  async function setup() {
642
- await fix(await doctor());
643
-
644
- migrate();
651
+ await bridge();
652
+ const d = await doctor(doctorOpts());
653
+ await fix(d);
654
+ // Before the service is written: a tailnet name to answer goes into the
655
+ // WORK_ALLOWED_HOSTS it carries.
656
+ const tailnet = await tailscale(d.tailscale);
645
657
 
646
658
  const running = installService();
647
659
  const port = serviceEnv().PORT;
648
- maybeTunnel(port);
660
+ if (!tailnet) maybeTunnel(port);
649
661
  if (running) {
650
662
  if (await waitForServer()) log(`done — http://localhost:${port}`);
651
663
  else
652
664
  warn(
653
665
  `service started but nothing answered on port ${port} within 30s — check the logs`,
654
666
  );
667
+ if (tailnet)
668
+ log(`on your phone and other devices on the tailnet: ${tailnet}`);
655
669
  } else {
656
670
  // A fresh install's shell may not have mise on PATH yet — name the shim.
657
671
  const shim = path.join(
@@ -669,19 +683,24 @@ async function setup() {
669
683
  }
670
684
  }
671
685
 
672
- /** One-shot cleanups between versions (scripts/migrate.mjs), run against
673
- * the settings the service actually uses: migrate.mjs exits quietly when it
674
- * finds no db, so an ambient environment missing WORK_DATA_DIR skips every
675
- * sweep. A checkout's .env.local sits under both, as it does when its run
676
- * script starts the server. Returns the exit status. */
677
- function migrate() {
678
- const script = path.join(pkgRoot, "scripts", "migrate.mjs");
679
- if (!fs.existsSync(script)) return 0;
680
- return (
681
- spawnSync(process.execPath, [script], {
682
- stdio: "inherit",
683
- env: { ...checkoutEnvLocal(), ...process.env, ...serviceEnv() },
684
- }).status ?? 1
686
+ /** Bring a database from before 0.1 to the baseline this release starts
687
+ * from (lib/setup/bridge.mjs), before anything touches the service: the
688
+ * server it would restart refuses such a database. Run with the settings
689
+ * the service uses (a checkout's .env.local under them, as its run script
690
+ * has it), so it finds the data dir the server opens rather than whatever
691
+ * this shell has. On failure the service is left as it was, and what to
692
+ * run by hand ends the output. */
693
+ async function bridge() {
694
+ const env = { ...checkoutEnvLocal(), ...process.env, ...serviceEnv() };
695
+ const dataDir =
696
+ env.WORK_DATA_DIR ||
697
+ path.join(
698
+ env.XDG_DATA_HOME || path.join(os.homedir(), ".local", "share"),
699
+ "work",
700
+ );
701
+ if (bridgeIfNeeded(dataDir, env, log)) return;
702
+ die(
703
+ `${path.join(dataDir, "work.db")} is still from before 0.1, so the service was left as it was. Run:\n ${bridgeCommands(dataDir).join("\n ")}`,
685
704
  );
686
705
  }
687
706
 
@@ -723,15 +742,19 @@ async function update() {
723
742
  "@dylanpiercey/work@latest",
724
743
  ]);
725
744
  // Re-exec the (possibly new) CLI to finish with fresh code.
726
- sh("work", ["setup", ...(yes ? ["--yes"] : [])]);
745
+ sh("work", [
746
+ "setup",
747
+ ...(yes ? ["--yes"] : []),
748
+ ...args.filter((a) => a === "--tailscale" || a === "--no-tailscale"),
749
+ ]);
727
750
  }
728
751
 
729
752
  /** Machine readiness from lib/setup — the same probe the Settings page runs. */
730
753
  async function doctorCmd() {
731
- let d = await doctor();
754
+ let d = await doctor(doctorOpts());
732
755
  if (args.includes("--fix")) {
733
756
  await fix(d);
734
- d = await doctor();
757
+ d = await doctor(doctorOpts());
735
758
  }
736
759
  if (args.includes("--json")) {
737
760
  console.log(JSON.stringify(d, null, 2));
@@ -745,6 +768,13 @@ async function doctorCmd() {
745
768
  if (!d.ready) process.exitCode = 1;
746
769
  }
747
770
 
771
+ /** What the doctor's Tailscale probe checks against: the port and allowed
772
+ * names the service runs with (or will, after `work setup`). */
773
+ function doctorOpts() {
774
+ const env = serviceEnv();
775
+ return { port: env.PORT, allowedHosts: env.WORK_ALLOWED_HOSTS || "" };
776
+ }
777
+
748
778
  /** Said when the service listens beyond this machine with nothing declared
749
779
  * to answer there: every other machine is refused (server/access.ts), which
750
780
  * is safe, but likely not what the listening was for. */
@@ -789,6 +819,13 @@ async function fix(d) {
789
819
  "GitHub is not signed in. Open the app and use Settings → GitHub → Sign in",
790
820
  );
791
821
  }
822
+ if (d.staleGhHelpers.length) {
823
+ try {
824
+ repairGhHelpers(d.staleGhHelpers, { log });
825
+ } catch (err) {
826
+ warn(`could not repair git's gh credential helper: ${err.message}`);
827
+ }
828
+ }
792
829
  // Agent CLIs are installed from the app (Settings → Agents), where each
793
830
  // one is a click and the picker updates immediately — no prompt here.
794
831
  if (!d.agents.some((a) => a.installed)) {
@@ -833,25 +870,6 @@ function serviceFile() {
833
870
  );
834
871
  }
835
872
 
836
- /** Whether an installed service with no WORK_LISTEN line was installed
837
- * before the this-machine-only default. On Linux its shape tells: under the
838
- * default, work.socket binds an address (`127.0.0.1:7171`), where an older
839
- * service had no socket or bound a bare port. A launchd agent has no such
840
- * tell. */
841
- function predatesListenDefault() {
842
- if (!fs.existsSync(serviceFile())) return false;
843
- if (process.platform === "darwin") return true;
844
- try {
845
- const socket = fs.readFileSync(
846
- path.join(path.dirname(serviceFile()), "work.socket"),
847
- "utf8",
848
- );
849
- return /^ListenStream=\d+\s*$/m.test(socket);
850
- } catch {
851
- return true;
852
- }
853
- }
854
-
855
873
  /** One Environment= line systemd reads back as exactly this value. A value
856
874
  * holding whitespace, a quote, `\` or `%` is quoted, with `\` and `"`
857
875
  * escaped and `%` doubled, since systemd expands specifiers there: bare, a
@@ -901,8 +919,8 @@ function installedServiceEnv() {
901
919
  }
902
920
  return installed;
903
921
  }
904
- // unitEnvLine's quoted form, or an older service's bare `KEY=value`,
905
- // which was never escaped either.
922
+ // unitEnvLine's quoted form, or the bare `KEY=value` it writes for a
923
+ // plain value, which is not escaped either.
906
924
  const re =
907
925
  /^Environment=(?:"([A-Za-z_][A-Za-z0-9_]*)=((?:[^"\\]|\\.)*)"|([A-Za-z_][A-Za-z0-9_]*)=(.*))$/gm;
908
926
  for (const [, qKey, qValue, key, value] of text.matchAll(re)) {
@@ -916,9 +934,9 @@ function installedServiceEnv() {
916
934
  }
917
935
 
918
936
  /** WORK_* from the .env.local of the checkout WORK_SERVICE_EXEC runs, read
919
- * as scripts/env-local.sh reads it: that script exports them at start, under
920
- * whatever the service sets. Never written into the service, which would
921
- * shadow later edits of the file. */
937
+ * as scripts/run-local.sh reads it: that script exports them at start,
938
+ * under whatever the service sets. Never written into the service, which
939
+ * would shadow later edits of the file. */
922
940
  function checkoutEnvLocal() {
923
941
  const exec = process.env.WORK_SERVICE_EXEC;
924
942
  const out = {};
@@ -972,18 +990,8 @@ function serviceEnv() {
972
990
  const value = key in process.env ? process.env[key] : installed[key];
973
991
  if (value) env[key] = value;
974
992
  }
975
- // A new install listens on this machine only, and its service says so, so
976
- // that a missing line only ever means a service from before that default.
977
- // Those listened on every interface, and other machines (a phone, the
978
- // proxy in front of it) may be reaching it that way: keep it listening,
979
- // so they get work's page naming the setting to add (server/access.ts)
980
- // rather than a connection that is simply refused.
981
- if (!env.WORK_LISTEN) {
982
- env.WORK_LISTEN =
983
- !("WORK_LISTEN" in process.env) && predatesListenDefault()
984
- ? "0.0.0.0"
985
- : "127.0.0.1";
986
- }
993
+ // This machine only unless set, and the service says so.
994
+ env.WORK_LISTEN ||= "127.0.0.1";
987
995
  // entry.ts prefers PORT, so carrying WORK_APP_PORT forward without this
988
996
  // would leave the service on the default port anyway. A checkout's run
989
997
  // script serves the port its .env.local names, so the socket binds that
@@ -1022,9 +1030,7 @@ function isWorkServerCmd(cmd) {
1022
1030
  if (/\bwork(\.mjs)?\s+(setup|update|version)\b/.test(cmd)) return false;
1023
1031
  if (
1024
1032
  /\bwork\.mjs\s+start(?:\s|$)/.test(cmd) ||
1025
- /(?:^|[\s/])work\s+start(?:\s|$)/.test(cmd) ||
1026
- /--why=work server is running/.test(cmd) ||
1027
- /--why="work server is running"/.test(cmd)
1033
+ /(?:^|[\s/])work\s+start(?:\s|$)/.test(cmd)
1028
1034
  )
1029
1035
  return true;
1030
1036
  // A built server or run script, by the absolute path an interpreter was
@@ -1035,9 +1041,7 @@ function isWorkServerCmd(cmd) {
1035
1041
  const argv = cmd.split(/\s+/);
1036
1042
  return argv.some((arg, i) => {
1037
1043
  if (
1038
- !/^\/.*\/(?:(?:dist|\.run)\/index\.mjs|scripts\/run-(?:prod|local)\.sh)$/.test(
1039
- arg,
1040
- )
1044
+ !/^\/.*\/(?:(?:dist|\.run)\/index\.mjs|scripts\/run-local\.sh)$/.test(arg)
1041
1045
  )
1042
1046
  return false;
1043
1047
  const runner = argv
@@ -1129,10 +1133,9 @@ function killPids(pids, signal) {
1129
1133
  }
1130
1134
  }
1131
1135
 
1132
- /** Stop the managed service *and* any leftover checkout / `work start`
1133
- * process still bound to the port. A pre-npm install often wasn't the
1134
- * current unit (or was a bare node), so restarting work.service left it
1135
- * sitting in front of the new one. */
1136
+ /** Stop the managed service *and* any other work server still bound to
1137
+ * the port — a `work start` left in a terminal, a checkout's — which would
1138
+ * otherwise sit in front of the new one. */
1136
1139
  function stopOldCopies(port) {
1137
1140
  log("stopping any previous work server");
1138
1141
  if (process.platform === "darwin") {
@@ -1140,28 +1143,6 @@ function stopOldCopies(port) {
1140
1143
  spawnSync("launchctl", ["bootout", `gui/${uid}/dev.work.app`], {
1141
1144
  stdio: "ignore",
1142
1145
  });
1143
- const agents = path.join(os.homedir(), "Library/LaunchAgents");
1144
- try {
1145
- for (const name of fs.readdirSync(agents)) {
1146
- if (!name.endsWith(".plist")) continue;
1147
- const file = path.join(agents, name);
1148
- const text = fs.readFileSync(file, "utf8");
1149
- if (
1150
- !/dev\.work\.app|work\.mjs|run-prod\.sh|run-local\.sh|@dylanpiercey\/work/.test(
1151
- text,
1152
- )
1153
- )
1154
- continue;
1155
- const label = text.match(
1156
- /<key>Label<\/key>\s*<string>([^<]+)<\/string>/,
1157
- )?.[1];
1158
- if (label) {
1159
- spawnSync("launchctl", ["bootout", `gui/${uid}/${label}`], {
1160
- stdio: "ignore",
1161
- });
1162
- }
1163
- }
1164
- } catch {}
1165
1146
  } else if (have("systemctl")) {
1166
1147
  spawnSync("systemctl", ["--user", "stop", "work.service"], {
1167
1148
  stdio: "ignore",
@@ -1201,8 +1182,8 @@ function installService() {
1201
1182
  const port = serviceEnv().PORT;
1202
1183
  stopOldCopies(port);
1203
1184
  const cliPath = fileURLToPath(import.meta.url);
1204
- // One service definition serves both installers. A source install
1205
- // (scripts/setup-prod.sh) points the unit at this checkout's run-prod.sh
1185
+ // One service definition serves the npm package and a checkout:
1186
+ // scripts/work-source.sh points the unit at the checkout's run-local.sh
1206
1187
  // instead of the CLI — that script carries its own sleep inhibitor and
1207
1188
  // PORT handling, so no awake prefix is layered on top.
1208
1189
  const execOverride = process.env.WORK_SERVICE_EXEC;
@@ -1250,15 +1231,8 @@ ${envXml}
1250
1231
  stdio: "ignore",
1251
1232
  }).status === 0;
1252
1233
  if (!started) {
1253
- // A launchd that only speaks the legacy verbs: `load -w` honours
1254
- // RunAtLoad the same way.
1255
- started =
1256
- spawnSync("launchctl", ["load", "-w", plist], { stdio: "ignore" })
1257
- .status === 0;
1258
- }
1259
- if (!started) {
1260
- // Both refused because the job is loaded already (a bootout that did
1261
- // not take). Nothing spawned it with the new definition, and it is old
1234
+ // Refused because the job is loaded already (a bootout that did not
1235
+ // take). Nothing spawned it with the new definition, and it is old
1262
1236
  // enough not to be throttled, so restart it.
1263
1237
  started =
1264
1238
  spawnSync("launchctl", ["kickstart", "-k", label], { stdio: "ignore" })
@@ -1366,8 +1340,179 @@ function maybeTunnel(port = appPort) {
1366
1340
  if (yes || have("cloudflared")) return;
1367
1341
  console.log(`
1368
1342
  To reach work from your phone or other devices, put it behind something
1369
- that terminates TLS and authenticates, e.g. a Cloudflare Tunnel with
1343
+ that terminates TLS and authenticates, e.g. Tailscale (\`work setup
1344
+ --tailscale\` runs \`tailscale serve\` for you) or a Cloudflare Tunnel with
1370
1345
  Access (https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/)
1371
- pointing at http://localhost:${port}, or Tailscale. The server itself has
1372
- no auth of its own.`);
1346
+ pointing at http://localhost:${port}. The server itself has no auth of its
1347
+ own.`);
1348
+ }
1349
+
1350
+ /**
1351
+ * `tailscale serve` in front of work: https://<machine>.<tailnet>.ts.net/,
1352
+ * TLS and the tailnet's own sign-in in the local daemon, proxied to work's
1353
+ * port, so requests arrive from this machine under that name — which goes
1354
+ * into WORK_ALLOWED_HOSTS. Tailnet only, never Funnel: Funnel publishes to
1355
+ * the internet, and work has no login of its own. Tailscale's config is
1356
+ * changed only on an explicit yes, the prompt's or `--tailscale`; `--yes`
1357
+ * is how the installer, `work update` and the app's own update run setup
1358
+ * unattended, and none of them asked for it. A serve config already
1359
+ * pointing at work only has its name allowed. Returns the URL work is
1360
+ * served at, or null.
1361
+ */
1362
+ async function tailscale(ts) {
1363
+ if (args.includes("--no-tailscale")) return null;
1364
+ const asked = args.includes("--tailscale");
1365
+ const port = serviceEnv().PORT;
1366
+ if (!ts?.installed) {
1367
+ if (asked)
1368
+ warn(
1369
+ "Tailscale is not installed: https://tailscale.com/download, then work setup --tailscale",
1370
+ );
1371
+ return null;
1372
+ }
1373
+ if (ts.serve) {
1374
+ if (!allowTailnet(ts.serve)) return null;
1375
+ if (ts.running) return servedAt(ts.serve.url);
1376
+ // A serve config outlives a logout, and serves again once it is up.
1377
+ log(
1378
+ `Tailscale is ${stateText(ts)}: work is at ${ts.serve.url} once it is up`,
1379
+ );
1380
+ return null;
1381
+ }
1382
+ if (!ts.running) {
1383
+ if (asked)
1384
+ warn(
1385
+ `Tailscale is ${stateText(ts)}: ${ts.state === null ? "start tailscaled" : "run `tailscale up`"}, then work setup --tailscale`,
1386
+ );
1387
+ return null;
1388
+ }
1389
+ const input = yes ? null : terminal();
1390
+ try {
1391
+ const refusal = serveRefusal(ts, port);
1392
+ if (refusal) {
1393
+ if (asked || input) warn(refusal);
1394
+ return null;
1395
+ }
1396
+ const target = serveTarget(port);
1397
+ const command = `tailscale serve --bg ${target}`;
1398
+ if (!asked) {
1399
+ if (!input) {
1400
+ log(
1401
+ `Tailscale is logged in: \`work setup --tailscale\` serves work at https://${ts.name}/ on your tailnet`,
1402
+ );
1403
+ return null;
1404
+ }
1405
+ console.log(`
1406
+ Tailscale is logged in here as ${ts.name}.
1407
+ \`${command}\` puts work at https://${ts.name}/
1408
+ for your devices on the tailnet (only them: never Funnel), proxied by the
1409
+ local Tailscale daemon to port ${port}.${ts.https === false ? "\nHTTPS certificates are off on your tailnet: tailscale prints a link that\nturns them on." : ""}`);
1410
+ if (!(await confirm(input, `Run \`${command}\`? [y/N] `))) {
1411
+ log(
1412
+ "left Tailscale as it was (work setup --tailscale serves work later; --no-tailscale skips this question)",
1413
+ );
1414
+ return null;
1415
+ }
1416
+ } else if (ts.https === false && !input) {
1417
+ // tailscale serve would wait, unattended, for someone to follow the
1418
+ // link it prints.
1419
+ warn(
1420
+ "HTTPS certificates are off on your tailnet: turn them on (https://login.tailscale.com/admin/dns), then run work setup --tailscale",
1421
+ );
1422
+ return null;
1423
+ }
1424
+ log(command);
1425
+ spawnSync(which("tailscale"), ["serve", "--bg", target], {
1426
+ stdio: "inherit",
1427
+ });
1428
+ } finally {
1429
+ if (input && input !== process.stdin) input.destroy();
1430
+ }
1431
+ // Its exit status is no proof: it exits 0 having only printed how to
1432
+ // turn HTTPS on, when it does not wait for that.
1433
+ const after = await tailscaleProbe({ port, allowedHosts: "" });
1434
+ if (!after.serve) {
1435
+ warn(
1436
+ "tailscale serve did not serve work (see above); once it does, run work setup again",
1437
+ );
1438
+ return null;
1439
+ }
1440
+ return allowTailnet(after.serve) ? servedAt(after.serve.url) : null;
1441
+ }
1442
+
1443
+ function servedAt(url) {
1444
+ log(`Tailscale serves work at ${url} (tailnet only)`);
1445
+ return url;
1446
+ }
1447
+
1448
+ /** Why `tailscale serve --bg <port>` is not run here, or null. */
1449
+ function serveRefusal(ts, port) {
1450
+ if (!ts.serveCli)
1451
+ return `tailscale ${ts.version || "(unknown version)"} predates \`tailscale serve --bg <port>\` (1.52): update Tailscale, then run work setup --tailscale`;
1452
+ // It would replace whatever serves the name's root now.
1453
+ if (ts.rootTaken)
1454
+ return `https://${ts.name}/ already serves ${ts.rootTaken}, which work leaves alone. To serve work beside it: tailscale serve --bg --https=8443 ${port}, then work setup`;
1455
+ if (!serveTarget(port))
1456
+ return `work listens on ${serviceEnv().WORK_LISTEN} alone, and tailscale serve proxies to loopback: WORK_LISTEN= work setup --tailscale`;
1457
+ return null;
1458
+ }
1459
+
1460
+ /** What `tailscale serve` proxies to: work's port on loopback, where
1461
+ * requests are this machine's (server/access.ts). Null when work listens
1462
+ * on one other address only, which loopback does not reach. */
1463
+ function serveTarget(port) {
1464
+ const listen = serviceEnv().WORK_LISTEN;
1465
+ if (listen === "::1") return `http://[::1]:${port}`;
1466
+ return ["127.0.0.1", "0.0.0.0", "::", "*"].includes(listen)
1467
+ ? String(port)
1468
+ : null;
1469
+ }
1470
+
1471
+ /** Put the name tailscale serve serves work under in WORK_ALLOWED_HOSTS,
1472
+ * for installService() to write — or, when Funnel publishes that name to
1473
+ * the internet, take it out, so work refuses the internet there. False
1474
+ * then. */
1475
+ function allowTailnet(serve) {
1476
+ const current = serviceEnv().WORK_ALLOWED_HOSTS || "";
1477
+ const next = allowHost(current, serve.host, !serve.funnel);
1478
+ if (next !== current) {
1479
+ process.env.WORK_ALLOWED_HOSTS = next;
1480
+ log(
1481
+ `${serve.funnel ? "refusing" : "allowing"} ${serve.host} (WORK_ALLOWED_HOSTS=${next})`,
1482
+ );
1483
+ }
1484
+ if (serve.funnel) {
1485
+ warn(
1486
+ `Tailscale Funnel publishes ${serve.url} to the internet, and work has no login, so work refuses that name. Turn Funnel off (tailscale funnel --https=${new URL(serve.url).port || 443} off), then run work setup again`,
1487
+ );
1488
+ return false;
1489
+ }
1490
+ return true;
1491
+ }
1492
+
1493
+ /** Where to ask the user something: stdin when it is a terminal, else the
1494
+ * controlling terminal, since the curl installer pipes its script into the
1495
+ * shell this runs under. Null with no terminal at all. */
1496
+ function terminal() {
1497
+ if (process.stdin.isTTY) return process.stdin;
1498
+ try {
1499
+ return new tty.ReadStream(fs.openSync("/dev/tty", "r"));
1500
+ } catch {
1501
+ return null;
1502
+ }
1503
+ }
1504
+
1505
+ /** A yes/no question, no unless answered yes. Line-buffered by the
1506
+ * terminal itself, so Ctrl-C stops setup as it would anywhere else. */
1507
+ async function confirm(input, question) {
1508
+ const rl = readline.createInterface({
1509
+ input,
1510
+ output: process.stdout,
1511
+ terminal: false,
1512
+ });
1513
+ try {
1514
+ return /^y(es)?$/i.test((await rl.question(question)).trim());
1515
+ } finally {
1516
+ rl.close();
1517
+ }
1373
1518
  }