@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.
- package/README.md +19 -32
- package/bin/work.mjs +259 -114
- package/dist/_D1C-XIqT.js +168 -0
- package/dist/{_CpANOzpd.js → _Dzz52ZtI.js} +488 -144
- package/dist/_HbAW5Cbt.js +2 -0
- package/dist/index.mjs +2634 -2786
- package/dist/public/assets/{_unfL8mz1.js → _-5UNb9-C.js} +1 -1
- package/dist/public/assets/_-5UNb9-C.js.br +0 -0
- package/dist/public/assets/_06rtP_SB.js +1 -0
- package/dist/public/assets/_14yhXek7.js +1 -0
- package/dist/public/assets/_14yhXek7.js.br +0 -0
- package/dist/public/assets/_1JXC9nFp.js +1 -0
- package/dist/public/assets/_1JXC9nFp.js.br +0 -0
- package/dist/public/assets/_1Yit_Y5R.js +1 -0
- package/dist/public/assets/_1ZPfR2B8.js +6 -0
- package/dist/public/assets/_1ZPfR2B8.js.br +0 -0
- package/dist/public/assets/{_BBpX-M4b.js → _1xOByopP.js} +1 -1
- package/dist/public/assets/_1xOByopP.js.br +0 -0
- package/dist/public/assets/{_BLTfQLdY.js → _3aBl7SEn.js} +1 -1
- package/dist/public/assets/_3aBl7SEn.js.br +0 -0
- package/dist/public/assets/_5aMLV2Vy.js +1 -0
- package/dist/public/assets/{_CX1wufuh.js → _7YrArFMB.js} +1 -1
- package/dist/public/assets/_7YrArFMB.js.br +0 -0
- package/dist/public/assets/{_mnBdD4YN.js → _B1-W0Sn2.js} +1 -1
- package/dist/public/assets/_B1-W0Sn2.js.br +0 -0
- package/dist/public/assets/{_BL9QMk_P.js → _B1D0PIz3.js} +1 -1
- package/dist/public/assets/_B1D0PIz3.js.br +0 -0
- package/dist/public/assets/_B1agoKUa.js +1 -0
- package/dist/public/assets/_B3NfNm8M.js +1591 -0
- package/dist/public/assets/_B3NfNm8M.js.br +0 -0
- package/dist/public/assets/{_DeCJ44Me.js → _B7VYILLZ.js} +1 -1
- package/dist/public/assets/_B7VYILLZ.js.br +0 -0
- package/dist/public/assets/_B7sPBy0J.js +1 -0
- package/dist/public/assets/_B81p6DSw.js +1 -0
- package/dist/public/assets/_BAkSKCkz.js +1 -0
- package/dist/public/assets/{_BUH04L5e.js → _BDUjU8Ev.js} +1 -1
- package/dist/public/assets/{_C6i9NP5A.js → _BHU5aikZ.js} +1 -1
- package/dist/public/assets/_BHZPTv1U.js +1 -0
- package/dist/public/assets/{_CttJbiei.js → _BKHtiW96.js} +1 -1
- package/dist/public/assets/_BKHtiW96.js.br +0 -0
- package/dist/public/assets/{_IWMmnVCy.js → _BKIx7IZP.js} +1 -1
- package/dist/public/assets/_BKIx7IZP.js.br +0 -0
- package/dist/public/assets/{_BCZpExov.js → _BLfzQY-3.js} +1 -1
- package/dist/public/assets/_BLfzQY-3.js.br +0 -0
- package/dist/public/assets/{_DhNuRs-V.js → _BPSTqkJn.js} +1 -1
- package/dist/public/assets/_BPSTqkJn.js.br +0 -0
- package/dist/public/assets/_BQZOimBB.js +1 -0
- package/dist/public/assets/{_vKQopDwG.js → _BRQ6iQ5p.js} +1 -1
- package/dist/public/assets/_BRQ6iQ5p.js.br +0 -0
- package/dist/public/assets/_BRVURc4u.js +1 -0
- package/dist/public/assets/{_eQK3EZCc.js → _BSFJEuJX.js} +1 -1
- package/dist/public/assets/_BSFJEuJX.js.br +0 -0
- package/dist/public/assets/{_B1RldJ0S.js → _BSWN-v3P.js} +1 -1
- package/dist/public/assets/_BSWN-v3P.js.br +0 -0
- package/dist/public/assets/_BTVAQr2c.js +1 -0
- package/dist/public/assets/_BUD1pBC0.js +1 -0
- package/dist/public/assets/_BVGzCczb.js +1 -0
- package/dist/public/assets/{_C1PmcEws.js → _BVQosHjY.js} +1 -1
- package/dist/public/assets/_BVQosHjY.js.br +0 -0
- package/dist/public/assets/{_DlpTErRA.js → _B_0C2_RO.js} +1 -1
- package/dist/public/assets/_B_0C2_RO.js.br +0 -0
- package/dist/public/assets/_BaA31ms5.js +1 -0
- package/dist/public/assets/_BgCHJGlO.js +1 -0
- package/dist/public/assets/_BhpRLDbL.js +1 -0
- package/dist/public/assets/_BhpRLDbL.js.br +0 -0
- package/dist/public/assets/_BkKcjMAC.js +1 -0
- package/dist/public/assets/{_BsimXDnm.js → _BkcPY2j0.js} +1 -1
- package/dist/public/assets/_BkcPY2j0.js.br +0 -0
- package/dist/public/assets/_BlTVd_3d.js +1 -0
- package/dist/public/assets/_BlqlLmEY.js +1 -0
- package/dist/public/assets/_BoNfxc9P.js +1 -0
- package/dist/public/assets/_BqswW9B9.js +1 -0
- package/dist/public/assets/_BricqzsO.js +1 -0
- package/dist/public/assets/_BrreckIa.js +1 -0
- package/dist/public/assets/{_CGLpCj_I.js → _BsJZPHmz.js} +1 -1
- package/dist/public/assets/_BsJZPHmz.js.br +0 -0
- package/dist/public/assets/_BtRYwaBb.js +1 -0
- package/dist/public/assets/{_CT3clsOQ.js → _BtZ2AatF.js} +1 -1
- package/dist/public/assets/_BvPBKjRP.js +1 -0
- package/dist/public/assets/_BvPBKjRP.js.br +0 -0
- package/dist/public/assets/_ByrrjMU9.js +1 -0
- package/dist/public/assets/{_s5Jez2mu.js → _BzGHediB.js} +1 -1
- package/dist/public/assets/_BzGHediB.js.br +0 -0
- package/dist/public/assets/_BzvTKm92.js +1 -0
- package/dist/public/assets/{_CnKYioYw.js → _C-EkQeP_.js} +1 -1
- package/dist/public/assets/_C-EkQeP_.js.br +0 -0
- package/dist/public/assets/{_BAHrOydH.js → _C0Ib7MZF.js} +1 -1
- package/dist/public/assets/_C0Ib7MZF.js.br +0 -0
- package/dist/public/assets/_C2p1Z5Lf.js +86 -0
- package/dist/public/assets/_C2p1Z5Lf.js.br +0 -0
- package/dist/public/assets/_C3iiOlxd.js +1 -0
- package/dist/public/assets/{_BdvO5Lqa.js → _C81nSl09.js} +1 -1
- package/dist/public/assets/_C81nSl09.js.br +0 -0
- package/dist/public/assets/{_BXT2bXMi.js → _C8bqYk4J.js} +1 -1
- package/dist/public/assets/_C8bqYk4J.js.br +0 -0
- package/dist/public/assets/_C98JZr09.js +1 -0
- package/dist/public/assets/_C98JZr09.js.br +0 -0
- package/dist/public/assets/_CB4VkmGZ.js +1 -0
- package/dist/public/assets/{_pUYwxsdF.js → _CCVkYcN5.js} +1 -1
- package/dist/public/assets/_CCVkYcN5.js.br +0 -0
- package/dist/public/assets/{_D-hK5uFn.js → _CDY0RpYX.js} +1 -1
- package/dist/public/assets/_CDY0RpYX.js.br +0 -0
- package/dist/public/assets/_CE2GEqMx.js +1 -0
- package/dist/public/assets/_CE2GEqMx.js.br +0 -0
- package/dist/public/assets/{_BeE9qDu0.js → _CFUREq1M.js} +1 -1
- package/dist/public/assets/_CFUREq1M.js.br +0 -0
- package/dist/public/assets/_CHdh6bII.js +1 -0
- package/dist/public/assets/{_CB31bp-W.js → _CHdpLB95.js} +1 -1
- package/dist/public/assets/_CHdpLB95.js.br +0 -0
- package/dist/public/assets/{_Fzbz4zvE.js → _CKLGAGKJ.js} +1 -1
- package/dist/public/assets/_CKLGAGKJ.js.br +0 -0
- package/dist/public/assets/_CMRt2lyQ.js +1 -0
- package/dist/public/assets/_CN1zW1AT.js +1 -0
- package/dist/public/assets/_CN1zW1AT.js.br +0 -0
- package/dist/public/assets/_CUILmj9R.js +1 -0
- package/dist/public/assets/_CUhP8KQ8.js +1 -0
- package/dist/public/assets/{_zKlxcAnQ.js → _CUsrMpdL.js} +1 -1
- package/dist/public/assets/_CUsrMpdL.js.br +0 -0
- package/dist/public/assets/_CVG0_w9T.js +1 -0
- package/dist/public/assets/{_CCCQMLw5.js → _CWG813ur.js} +1 -1
- package/dist/public/assets/_CWG813ur.js.br +0 -0
- package/dist/public/assets/{_C9OdJcx4.js → _CY87Y7QL.js} +1 -1
- package/dist/public/assets/_CY87Y7QL.js.br +0 -0
- package/dist/public/assets/_CYAsKva_.js +1 -0
- package/dist/public/assets/_CajKYNSY.js +1 -0
- package/dist/public/assets/{_gPFPzzPF.js → _Cb_k_JA_.js} +1 -1
- package/dist/public/assets/_Cb_k_JA_.js.br +0 -0
- package/dist/public/assets/_Ce8PlfMD.js +1 -0
- package/dist/public/assets/{_O70J1Mfv.js → _CfGEGt_k.js} +1 -1
- package/dist/public/assets/_CfGEGt_k.js.br +0 -0
- package/dist/public/assets/{_BLRpf6Uw.js → _CfdK8EeQ.js} +1 -1
- package/dist/public/assets/_CfdK8EeQ.js.br +0 -0
- package/dist/public/assets/_CgOKmAwr.js +1 -0
- package/dist/public/assets/{_C2ZpyiHV.js → _CiYQhf81.js} +1 -1
- package/dist/public/assets/_CiYQhf81.js.br +0 -0
- package/dist/public/assets/_Cj9DHPRL.js +1 -0
- package/dist/public/assets/{_BS4crAAm.js → _ClHPjF-1.js} +1 -1
- package/dist/public/assets/_ClHPjF-1.js.br +0 -0
- package/dist/public/assets/{_DmDSLxrw.js → _Cmmdd2e3.js} +1 -1
- package/dist/public/assets/_Cmmdd2e3.js.br +0 -0
- package/dist/public/assets/{_BsupnkWM.js → _Cn9Ia4SS.js} +1 -1
- package/dist/public/assets/_Cn9Ia4SS.js.br +0 -0
- package/dist/public/assets/_CnTzFbuG.js +1 -0
- package/dist/public/assets/{_DJ3UAR_s.js → _CqCenHOp.js} +1 -1
- package/dist/public/assets/_CqCenHOp.js.br +0 -0
- package/dist/public/assets/{_BRG_DBcC.js → _CtqGSS84.js} +1 -1
- package/dist/public/assets/_CtqGSS84.js.br +0 -0
- package/dist/public/assets/_CttfwM-c.js +1 -0
- package/dist/public/assets/{_DuXOUegE.js → _CxA3Yab0.js} +1 -1
- package/dist/public/assets/_CxA3Yab0.js.br +0 -0
- package/dist/public/assets/{_ifUb4WX7.js → _Cx_aJfDp.js} +1 -1
- package/dist/public/assets/_Cx_aJfDp.js.br +0 -0
- package/dist/public/assets/_D2L_ILOW.js +1 -0
- package/dist/public/assets/_D47lCu0c.js +1 -0
- package/dist/public/assets/_D5W-uGIK.js +1 -0
- package/dist/public/assets/_D6BbZLrc.js +1 -0
- package/dist/public/assets/_D6RVeCAn.js +1 -0
- package/dist/public/assets/_D75V4jV-.js +1 -0
- package/dist/public/assets/{_CwmzEsB4.js → _D9r_Fn0I.js} +1 -1
- package/dist/public/assets/_D9r_Fn0I.js.br +0 -0
- package/dist/public/assets/{_DoCtdMd2.js → _DBD57l1T.js} +1 -1
- package/dist/public/assets/_DBD57l1T.js.br +0 -0
- package/dist/public/assets/_DCWILZgL.js +1 -0
- package/dist/public/assets/{_DI0QHEXB.js → _DEOryr7w.js} +1 -1
- package/dist/public/assets/_DEOryr7w.js.br +0 -0
- package/dist/public/assets/_DF54Iz7o.js +1 -0
- package/dist/public/assets/_DFamU2Uj.js +2 -0
- package/dist/public/assets/_DFamU2Uj.js.br +0 -0
- package/dist/public/assets/_DHmBuyCO.js +1 -0
- package/dist/public/assets/_DHmBuyCO.js.br +0 -0
- package/dist/public/assets/{_BPcswKMK.js → _DJqEBkNd.js} +1 -1
- package/dist/public/assets/_DJqEBkNd.js.br +0 -0
- package/dist/public/assets/{_B_zyAtSJ.js → _DL3w8c_p.js} +1 -1
- package/dist/public/assets/_DL3w8c_p.js.br +0 -0
- package/dist/public/assets/_DLVL-4MP.js +1 -0
- package/dist/public/assets/{_BewXp5Y_.js → _DM60dKRs.js} +1 -1
- package/dist/public/assets/_DM60dKRs.js.br +0 -0
- package/dist/public/assets/{_BVp6r0fg.js → _DMJHTH74.js} +1 -1
- package/dist/public/assets/_DMJHTH74.js.br +0 -0
- package/dist/public/assets/_DMLv6KA6.js +1 -0
- package/dist/public/assets/_DQYpgyzr.js +1 -0
- package/dist/public/assets/_DQYpgyzr.js.br +0 -0
- package/dist/public/assets/{_BbiACo0Y.js → _DQyjuXTi.js} +1 -1
- package/dist/public/assets/_DQyjuXTi.js.br +0 -0
- package/dist/public/assets/{_C87NBm9i.js → _DRHCy0UF.js} +1 -1
- package/dist/public/assets/_DRHCy0UF.js.br +0 -0
- package/dist/public/assets/{_pvS44-AC.js → _DSUBv-Bl.js} +1 -1
- package/dist/public/assets/_DSUBv-Bl.js.br +0 -0
- package/dist/public/assets/{_BA8HFnO3.js → _DUFM9qLV.js} +1 -1
- package/dist/public/assets/_DUFM9qLV.js.br +0 -0
- package/dist/public/assets/{_D8u1YU9b.js → _DUs3ZGbj.js} +1 -1
- package/dist/public/assets/_DUs3ZGbj.js.br +0 -0
- package/dist/public/assets/{_CdgAsi3s.js → _DX5u6Wvm.js} +1 -1
- package/dist/public/assets/_DX5u6Wvm.js.br +0 -0
- package/dist/public/assets/{_BnyLUTdO.js → _DZvkgS3V.js} +1 -1
- package/dist/public/assets/_DZvkgS3V.js.br +0 -0
- package/dist/public/assets/{_CE5MhJWL.js → _Dbm6zAQS.js} +1 -1
- package/dist/public/assets/_Dbm6zAQS.js.br +0 -0
- package/dist/public/assets/{_DzrtBP-L.js → _DcOkTFth.js} +1 -1
- package/dist/public/assets/_Ddf1gumD.js +1 -0
- package/dist/public/assets/_De6E9M7R.js +1 -0
- package/dist/public/assets/{_C-6dImS5.js → _DgWKSW-Y.js} +1 -1
- package/dist/public/assets/_DgWKSW-Y.js.br +0 -0
- package/dist/public/assets/_DhEN6T4v.js +1 -0
- package/dist/public/assets/{_BxAl0m64.js → _Di1ccq_2.js} +1 -1
- package/dist/public/assets/_Di1ccq_2.js.br +0 -0
- package/dist/public/assets/{_CLerZOx-.js → _DifjKObE.js} +1 -1
- package/dist/public/assets/_DifjKObE.js.br +0 -0
- package/dist/public/assets/_Dn10ABa-.js +1 -0
- package/dist/public/assets/{_DuGmU4h0.js → _Do1ltY2a.js} +1 -1
- package/dist/public/assets/_Do1ltY2a.js.br +0 -0
- package/dist/public/assets/{_DeN4EY8L.js → _DomK7Scb.js} +1 -1
- package/dist/public/assets/_DomK7Scb.js.br +0 -0
- package/dist/public/assets/_DtWggCxt.js +1 -0
- package/dist/public/assets/_DtqqybyQ.js +1 -0
- package/dist/public/assets/{_CJ9j8N5Z.js → _DvKnakqk.js} +1 -1
- package/dist/public/assets/_DvKnakqk.js.br +0 -0
- package/dist/public/assets/_DxD_AC3a.js +1 -0
- package/dist/public/assets/_DxD_AC3a.js.br +0 -0
- package/dist/public/assets/_Dxo7m9cG.js +1 -0
- package/dist/public/assets/{_DhEdzMUw.js → _DzKW89Sx.js} +1 -1
- package/dist/public/assets/_DzKW89Sx.js.br +0 -0
- package/dist/public/assets/_DzTHCioH.js +1 -0
- package/dist/public/assets/{_CtyJUmXC.js → _EefWj858.js} +1 -1
- package/dist/public/assets/_EefWj858.js.br +0 -0
- package/dist/public/assets/{_Aq9oGnRu.js → _FlITqTCo.js} +1 -1
- package/dist/public/assets/_FlITqTCo.js.br +0 -0
- package/dist/public/assets/_GkEw2S0T.js +1 -0
- package/dist/public/assets/{__Oa255vo.js → _HM_QqtJi.js} +1 -1
- package/dist/public/assets/_HM_QqtJi.js.br +0 -0
- package/dist/public/assets/{_DuarImb7.js → _HmkI4Rfn.js} +1 -1
- package/dist/public/assets/_HmkI4Rfn.js.br +0 -0
- package/dist/public/assets/{_DaqT6m41.js → _JEvx8ub4.js} +1 -1
- package/dist/public/assets/_JEvx8ub4.js.br +0 -0
- package/dist/public/assets/_JOb8B3tz.js +1 -0
- package/dist/public/assets/_LEIhjFrO.js +1 -0
- package/dist/public/assets/_STJhwy7U.js +1 -0
- package/dist/public/assets/_TrcWRD8T.js +15 -0
- package/dist/public/assets/_TrcWRD8T.js.br +0 -0
- package/dist/public/assets/{_BO0OB-rz.js → _UaKPY71M.js} +1 -1
- package/dist/public/assets/_UaKPY71M.js.br +0 -0
- package/dist/public/assets/_UhK3lkm0.js +1 -0
- package/dist/public/assets/_VPLoEBTS.js +1 -0
- package/dist/public/assets/_Zeftz3t1.js +1 -0
- package/dist/public/assets/{_pP1W6w5v.js → __cyRVmTO.js} +1 -1
- package/dist/public/assets/__cyRVmTO.js.br +0 -0
- package/dist/public/assets/_awfYTHit.js +1 -0
- package/dist/public/assets/{_DiqV5bDH.js → _dsj7vMTW.js} +1 -1
- package/dist/public/assets/_dsj7vMTW.js.br +0 -0
- package/dist/public/assets/_eqpiOhSM.js +1 -0
- package/dist/public/assets/{_dr95P55a.js → _iutZ4EDe.js} +1 -1
- package/dist/public/assets/_iutZ4EDe.js.br +0 -0
- package/dist/public/assets/{_CZGS4T08.js → _kWQ0dtYZ.js} +1 -1
- package/dist/public/assets/_kWQ0dtYZ.js.br +0 -0
- package/dist/public/assets/_knvMqXQh.js +1 -0
- package/dist/public/assets/_lwC7sIgC.js +1 -0
- package/dist/public/assets/_m2tRa0jk.js +1 -0
- package/dist/public/assets/_m2tRa0jk.js.br +0 -0
- package/dist/public/assets/{_DwcoW-IU.js → _pXG8fRON.js} +1 -1
- package/dist/public/assets/_pXG8fRON.js.br +0 -0
- package/dist/public/assets/_r541ENtr.js +1 -0
- package/dist/public/assets/_rrUx1wn3.js +1 -0
- package/dist/public/assets/_tJvcu7sD.js +1 -0
- package/dist/public/assets/{_Cw_8WbwF.js → _vcLBrU2m.js} +1 -1
- package/dist/public/assets/_vcLBrU2m.js.br +0 -0
- package/dist/public/assets/_xZC4Vm70.js +1 -0
- package/dist/public/assets/_yAr5r-uL.js +1 -0
- package/dist/public/assets/{_DGTQBWy0.js → _yUmRJIJ6.js} +1 -1
- package/dist/public/assets/_yUmRJIJ6.js.br +0 -0
- package/dist/public/assets/app-shell-CVYlktwa.css +1 -0
- package/dist/public/assets/app-shell-CVYlktwa.css.br +0 -0
- package/dist/public/assets/files-view-CTadakLK.js +2 -0
- package/dist/public/assets/manage-workspace-HUAS6qjy.js +2 -0
- package/dist/public/assets/new-CirJkJFl.js +1 -0
- package/dist/public/assets/new-workspace-C3t1nA6z.js +2 -0
- package/dist/public/assets/review-view-DKPNtgmc.js +2 -0
- package/dist/public/assets/settings-CirJkJFl.js +1 -0
- package/dist/public/assets/settings-page-DH-9cN-8.js +2 -0
- package/dist/public/assets/styles-CIeNhVxC.css +1 -0
- package/dist/public/assets/styles-CIeNhVxC.css.br +0 -0
- package/dist/public/assets/w-CirJkJFl.js +1 -0
- package/dist/public/assets/w._-BnShhzdq.js +1 -0
- package/dist/public/assets/w._-CirJkJFl.js +1 -0
- package/dist/public/assets/w._-aCTYfruH.js +1 -0
- package/lib/review/markdown.mjs +5 -5
- package/lib/setup/bridge.d.mts +10 -0
- package/lib/setup/bridge.mjs +84 -0
- package/lib/setup/doctor.d.mts +17 -1
- package/lib/setup/doctor.mjs +97 -8
- package/lib/setup/install.d.mts +1 -0
- package/lib/setup/install.mjs +21 -1
- package/lib/setup/listen.mjs +2 -2
- package/lib/setup/tailscale.d.mts +53 -0
- package/lib/setup/tailscale.mjs +179 -0
- package/lib/share/manifest.mjs +2 -1
- package/package.json +2 -3
- package/dist/_LF2Fo5WE.js +0 -2
- package/dist/public/assets/_--tE4Gxz.js +0 -2
- package/dist/public/assets/_--tE4Gxz.js.br +0 -0
- package/dist/public/assets/_1OL9vXM-.js +0 -1
- package/dist/public/assets/_2mnAZf6B.js +0 -1
- package/dist/public/assets/_2mnAZf6B.js.br +0 -0
- package/dist/public/assets/_5f9fMvWJ.js +0 -1
- package/dist/public/assets/_5y69OR6M.js +0 -1
- package/dist/public/assets/_Aq9oGnRu.js.br +0 -0
- package/dist/public/assets/_B0uJCjmk.js +0 -1
- package/dist/public/assets/_B1RldJ0S.js.br +0 -0
- package/dist/public/assets/_B5TpOQCZ.js +0 -1
- package/dist/public/assets/_B6UdulKa.js +0 -1
- package/dist/public/assets/_BA8HFnO3.js.br +0 -0
- package/dist/public/assets/_BAHrOydH.js.br +0 -0
- package/dist/public/assets/_BBpX-M4b.js.br +0 -0
- package/dist/public/assets/_BCZpExov.js.br +0 -0
- package/dist/public/assets/_BFZHNauV.js +0 -1
- package/dist/public/assets/_BJRSndB4.js +0 -6
- package/dist/public/assets/_BJRSndB4.js.br +0 -0
- package/dist/public/assets/_BL9QMk_P.js.br +0 -0
- package/dist/public/assets/_BLNEzyb5.js +0 -1
- package/dist/public/assets/_BLRpf6Uw.js.br +0 -0
- package/dist/public/assets/_BLTfQLdY.js.br +0 -0
- package/dist/public/assets/_BO0OB-rz.js.br +0 -0
- package/dist/public/assets/_BPcswKMK.js.br +0 -0
- package/dist/public/assets/_BPhXWliU.js +0 -1
- package/dist/public/assets/_BQXTv9Cr.js +0 -1
- package/dist/public/assets/_BRG_DBcC.js.br +0 -0
- package/dist/public/assets/_BS4crAAm.js.br +0 -0
- package/dist/public/assets/_BVp6r0fg.js.br +0 -0
- package/dist/public/assets/_BXT2bXMi.js.br +0 -0
- package/dist/public/assets/_B_zyAtSJ.js.br +0 -0
- package/dist/public/assets/_BaXKdIeb.js +0 -1
- package/dist/public/assets/_BbiACo0Y.js.br +0 -0
- package/dist/public/assets/_BdvO5Lqa.js.br +0 -0
- package/dist/public/assets/_BeE9qDu0.js.br +0 -0
- package/dist/public/assets/_BewXp5Y_.js.br +0 -0
- package/dist/public/assets/_BfOOLKo5.js +0 -1
- package/dist/public/assets/_BhS8bTnr.js +0 -1
- package/dist/public/assets/_BhZ3ZoG0.js +0 -1
- package/dist/public/assets/_Bi3_ZsOQ.js +0 -1
- package/dist/public/assets/_Bj_Lfa8B.js +0 -1
- package/dist/public/assets/_BlN_x04q.js +0 -1
- package/dist/public/assets/_Bn5nsnie.js +0 -1
- package/dist/public/assets/_BnOOD6nr.js +0 -15
- package/dist/public/assets/_BnOOD6nr.js.br +0 -0
- package/dist/public/assets/_BnyLUTdO.js.br +0 -0
- package/dist/public/assets/_Bp_WT09q.js +0 -1
- package/dist/public/assets/_Bp_WT09q.js.br +0 -0
- package/dist/public/assets/_BsimXDnm.js.br +0 -0
- package/dist/public/assets/_BsupnkWM.js.br +0 -0
- package/dist/public/assets/_BxAl0m64.js.br +0 -0
- package/dist/public/assets/_C-6dImS5.js.br +0 -0
- package/dist/public/assets/_C18Torhh.js +0 -1
- package/dist/public/assets/_C1PmcEws.js.br +0 -0
- package/dist/public/assets/_C2ZpyiHV.js.br +0 -0
- package/dist/public/assets/_C3P1VHUI.js +0 -1
- package/dist/public/assets/_C6xwTKke.js +0 -1
- package/dist/public/assets/_C6yqlRfl.js +0 -1
- package/dist/public/assets/_C75D8bH2.js +0 -1
- package/dist/public/assets/_C87NBm9i.js.br +0 -0
- package/dist/public/assets/_C8Vc_vBH.js +0 -1
- package/dist/public/assets/_C9OdJcx4.js.br +0 -0
- package/dist/public/assets/_C9eoo0sE.js +0 -1
- package/dist/public/assets/_C9qO_dny.js +0 -1
- package/dist/public/assets/_C9qO_dny.js.br +0 -0
- package/dist/public/assets/_CB31bp-W.js.br +0 -0
- package/dist/public/assets/_CCCQMLw5.js.br +0 -0
- package/dist/public/assets/_CCie36pi.js +0 -1
- package/dist/public/assets/_CDD9N34p.js +0 -1
- package/dist/public/assets/_CE5MhJWL.js.br +0 -0
- package/dist/public/assets/_CGLpCj_I.js.br +0 -0
- package/dist/public/assets/_CJ9j8N5Z.js.br +0 -0
- package/dist/public/assets/_CLDmR3jR.js +0 -1
- package/dist/public/assets/_CLDmR3jR.js.br +0 -0
- package/dist/public/assets/_CLerZOx-.js.br +0 -0
- package/dist/public/assets/_CM5GtJb2.js +0 -1
- package/dist/public/assets/_COECwOVb.js +0 -1
- package/dist/public/assets/_CSQE9nMN.js +0 -1
- package/dist/public/assets/_CSXMgHLQ.js +0 -1
- package/dist/public/assets/_CVRiByCT.js +0 -1
- package/dist/public/assets/_CVSrm4Pl.js +0 -1
- package/dist/public/assets/_CX1wufuh.js.br +0 -0
- package/dist/public/assets/_CYNAhnRH.js +0 -1591
- package/dist/public/assets/_CYNAhnRH.js.br +0 -0
- package/dist/public/assets/_CZGS4T08.js.br +0 -0
- package/dist/public/assets/_C_L7l9Mg.js +0 -1
- package/dist/public/assets/_CaMrYy1W.js +0 -1
- package/dist/public/assets/_CdgAsi3s.js.br +0 -0
- package/dist/public/assets/_Ce-E7sJE.js +0 -1
- package/dist/public/assets/_Cg3S760D.js +0 -1
- package/dist/public/assets/_ChpYg41R.js +0 -1
- package/dist/public/assets/_CicyY7KP.js +0 -1
- package/dist/public/assets/_Ck0YOw_7.js +0 -1
- package/dist/public/assets/_CmHlTZSs.js +0 -1
- package/dist/public/assets/_CnECYh66.js +0 -1
- package/dist/public/assets/_CnKYioYw.js.br +0 -0
- package/dist/public/assets/_CnUVvVPd.js +0 -1
- package/dist/public/assets/_CqSgGCGC.js +0 -1
- package/dist/public/assets/_CttJbiei.js.br +0 -0
- package/dist/public/assets/_CtyJUmXC.js.br +0 -0
- package/dist/public/assets/_Cw_8WbwF.js.br +0 -0
- package/dist/public/assets/_CwmzEsB4.js.br +0 -0
- package/dist/public/assets/_CzETFsJs.js +0 -1
- package/dist/public/assets/_D-S9H-tX.js +0 -1
- package/dist/public/assets/_D-hK5uFn.js.br +0 -0
- package/dist/public/assets/_D5fTwHXK.js +0 -1
- package/dist/public/assets/_D8u1YU9b.js.br +0 -0
- package/dist/public/assets/_DAH8evhA.js +0 -1
- package/dist/public/assets/_DAemmoHI.js +0 -1
- package/dist/public/assets/_DEMYxWZf.js +0 -1
- package/dist/public/assets/_DGTQBWy0.js.br +0 -0
- package/dist/public/assets/_DHhv0pCA.js +0 -1
- package/dist/public/assets/_DI0QHEXB.js.br +0 -0
- package/dist/public/assets/_DJ3UAR_s.js.br +0 -0
- package/dist/public/assets/_DKu6UxI7.js +0 -1
- package/dist/public/assets/_DNC-9RBN.js +0 -1
- package/dist/public/assets/_DP-Bu7ET.js +0 -1
- package/dist/public/assets/_DYmmPBcq.js +0 -1
- package/dist/public/assets/_Da2Dmt_d.js +0 -1
- package/dist/public/assets/_Da2Dmt_d.js.br +0 -0
- package/dist/public/assets/_DaqT6m41.js.br +0 -0
- package/dist/public/assets/_DbVyutEZ.js +0 -1
- package/dist/public/assets/_DcBvO2Si.js +0 -1
- package/dist/public/assets/_DeCJ44Me.js.br +0 -0
- package/dist/public/assets/_DeN4EY8L.js.br +0 -0
- package/dist/public/assets/_DhEdzMUw.js.br +0 -0
- package/dist/public/assets/_DhNuRs-V.js.br +0 -0
- package/dist/public/assets/_DiqV5bDH.js.br +0 -0
- package/dist/public/assets/_Diqt_yCf.js +0 -1
- package/dist/public/assets/_DlpTErRA.js.br +0 -0
- package/dist/public/assets/_DmDSLxrw.js.br +0 -0
- package/dist/public/assets/_DoC0qbSD.js +0 -1
- package/dist/public/assets/_DoCtdMd2.js.br +0 -0
- package/dist/public/assets/_DpqWFkMg.js +0 -1
- package/dist/public/assets/_Dr0Bkgso.js +0 -1
- package/dist/public/assets/_Dt-n9FaQ.js +0 -1
- package/dist/public/assets/_DtOzdwHm.js +0 -1
- package/dist/public/assets/_DtOzdwHm.js.br +0 -0
- package/dist/public/assets/_DuGmU4h0.js.br +0 -0
- package/dist/public/assets/_DuXOUegE.js.br +0 -0
- package/dist/public/assets/_DuaocJd1.js +0 -1
- package/dist/public/assets/_DuarImb7.js.br +0 -0
- package/dist/public/assets/_DwcoW-IU.js.br +0 -0
- package/dist/public/assets/_DxVt7Zuk.js +0 -86
- package/dist/public/assets/_DxVt7Zuk.js.br +0 -0
- package/dist/public/assets/_Fzbz4zvE.js.br +0 -0
- package/dist/public/assets/_I5oX-lGP.js +0 -1
- package/dist/public/assets/_IWMmnVCy.js.br +0 -0
- package/dist/public/assets/_O70J1Mfv.js.br +0 -0
- package/dist/public/assets/_SwbsOoi9.js +0 -1
- package/dist/public/assets/_SwbsOoi9.js.br +0 -0
- package/dist/public/assets/_Yy39CykK.js +0 -1
- package/dist/public/assets/__Oa255vo.js.br +0 -0
- package/dist/public/assets/_bPze7k1x.js +0 -1
- package/dist/public/assets/_bU1KcT1I.js +0 -1
- package/dist/public/assets/_dr95P55a.js.br +0 -0
- package/dist/public/assets/_eQK3EZCc.js.br +0 -0
- package/dist/public/assets/_gPFPzzPF.js.br +0 -0
- package/dist/public/assets/_hzpOPZUB.js +0 -1
- package/dist/public/assets/_ifUb4WX7.js.br +0 -0
- package/dist/public/assets/_kQ7LiSim.js +0 -1
- package/dist/public/assets/_lOAW1VLM.js +0 -1
- package/dist/public/assets/_mnBdD4YN.js.br +0 -0
- package/dist/public/assets/_odBcV1kc.js +0 -1
- package/dist/public/assets/_pP1W6w5v.js.br +0 -0
- package/dist/public/assets/_pUYwxsdF.js.br +0 -0
- package/dist/public/assets/_pvS44-AC.js.br +0 -0
- package/dist/public/assets/_qJTL2ooe.js +0 -1
- package/dist/public/assets/_qJTL2ooe.js.br +0 -0
- package/dist/public/assets/_s5Jez2mu.js.br +0 -0
- package/dist/public/assets/_unfL8mz1.js.br +0 -0
- package/dist/public/assets/_unwtcXFQ.js +0 -1
- package/dist/public/assets/_unwtcXFQ.js.br +0 -0
- package/dist/public/assets/_vKQopDwG.js.br +0 -0
- package/dist/public/assets/_w1aegGdT.js +0 -1
- package/dist/public/assets/_xdrkfok5.js +0 -1
- package/dist/public/assets/_xdrkfok5.js.br +0 -0
- package/dist/public/assets/_y-lm36fI.js +0 -1
- package/dist/public/assets/_zKlxcAnQ.js.br +0 -0
- package/dist/public/assets/app-shell-CpPCwWPW.css +0 -1
- package/dist/public/assets/app-shell-CpPCwWPW.css.br +0 -0
- package/dist/public/assets/files-view-BfCdcfXX.js +0 -2
- package/dist/public/assets/files-view-DtI7JE-f.css +0 -1
- package/dist/public/assets/files-view-DtI7JE-f.css.br +0 -0
- package/dist/public/assets/group.css.ts-SeaNz7WZ.css +0 -1
- package/dist/public/assets/group.css.ts-SeaNz7WZ.css.br +0 -0
- package/dist/public/assets/icon-folder-DHVc-REl.css +0 -1
- package/dist/public/assets/icon-folder-DHVc-REl.css.br +0 -0
- package/dist/public/assets/icon-search-DAtjHWFL.css +0 -1
- package/dist/public/assets/icon-search-DAtjHWFL.css.br +0 -0
- package/dist/public/assets/manage-workspace-6i6Zeai7.css +0 -1
- package/dist/public/assets/manage-workspace-CixeMkdt.js +0 -2
- package/dist/public/assets/modal-view-D0qnmuJE.css +0 -1
- package/dist/public/assets/modal-view-D0qnmuJE.css.br +0 -0
- package/dist/public/assets/new-DxhaKmbF.js +0 -1
- package/dist/public/assets/new-workspace-BXjDRbqF.css +0 -1
- package/dist/public/assets/new-workspace-BXjDRbqF.css.br +0 -0
- package/dist/public/assets/new-workspace-DG693v2S.js +0 -2
- package/dist/public/assets/port-chip-Lz3mVJWb.css +0 -1
- package/dist/public/assets/port-chip-Lz3mVJWb.css.br +0 -0
- package/dist/public/assets/prompt-dialog-vhnae1-x.css +0 -1
- package/dist/public/assets/prompt-dialog-vhnae1-x.css.br +0 -0
- package/dist/public/assets/quick-open-DVqzk3-g.css +0 -1
- package/dist/public/assets/quick-open-DVqzk3-g.css.br +0 -0
- package/dist/public/assets/repo-picker-K5RYsEge.css +0 -1
- package/dist/public/assets/repo-picker-K5RYsEge.css.br +0 -0
- package/dist/public/assets/review-view-DqMrB4Ny.css +0 -1
- package/dist/public/assets/review-view-DqMrB4Ny.css.br +0 -0
- package/dist/public/assets/review-view-TdLbCgEg.js +0 -2
- package/dist/public/assets/section-2MyJSq5D.css +0 -1
- package/dist/public/assets/section-2MyJSq5D.css.br +0 -0
- package/dist/public/assets/settings-DxhaKmbF.js +0 -1
- package/dist/public/assets/settings-page-BBAG94Vh.js +0 -2
- package/dist/public/assets/settings-page-DUefc32x.css +0 -1
- package/dist/public/assets/settings-page-DUefc32x.css.br +0 -0
- package/dist/public/assets/skill-row-BeOk8NK8.css +0 -1
- package/dist/public/assets/sso-auth-CU3fGJ4U.css +0 -1
- package/dist/public/assets/sso-auth-CU3fGJ4U.css.br +0 -0
- package/dist/public/assets/w-DxhaKmbF.js +0 -1
- package/dist/public/assets/w._-C6JVdFgi.js +0 -1
- package/dist/public/assets/w._-DxhaKmbF.js +0 -1
- package/dist/public/assets/w._-rfvcDGPk.js +0 -1
- 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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
the
|
|
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
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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 {
|
|
31
|
-
|
|
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
|
|
91
|
-
// scripts/
|
|
92
|
-
// definition; WORK_SERVICE_EXEC points it at
|
|
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|
|
|
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
|
|
643
|
-
|
|
644
|
-
|
|
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
|
-
/**
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
*
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
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", [
|
|
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
|
|
905
|
-
// which
|
|
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/
|
|
920
|
-
* whatever the service sets. Never written into the service, which
|
|
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
|
-
//
|
|
976
|
-
|
|
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-
|
|
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
|
|
1133
|
-
*
|
|
1134
|
-
*
|
|
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
|
|
1205
|
-
//
|
|
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
|
-
//
|
|
1254
|
-
//
|
|
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.
|
|
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}
|
|
1372
|
-
|
|
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
|
}
|