@chrisjowen/wheelhouse 0.1.0 → 0.2.0
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 +87 -56
- package/build/client/_app/immutable/assets/0.CKUND0qa.css +1 -0
- package/build/client/_app/immutable/assets/0.CKUND0qa.css.br +0 -0
- package/build/client/_app/immutable/assets/0.CKUND0qa.css.gz +0 -0
- package/build/client/_app/immutable/assets/DocSheet.CaO2v_qh.css +1 -0
- package/build/client/_app/immutable/assets/DocSheet.CaO2v_qh.css.br +0 -0
- package/build/client/_app/immutable/assets/DocSheet.CaO2v_qh.css.gz +0 -0
- package/build/client/_app/immutable/chunks/08Q01-Mc.js +1 -0
- package/build/client/_app/immutable/chunks/08Q01-Mc.js.br +0 -0
- package/build/client/_app/immutable/chunks/08Q01-Mc.js.gz +0 -0
- package/build/client/_app/immutable/chunks/0TvUxbRv.js +1 -0
- package/build/client/_app/immutable/chunks/0TvUxbRv.js.br +2 -0
- package/build/client/_app/immutable/chunks/0TvUxbRv.js.gz +0 -0
- package/build/client/_app/immutable/chunks/3Of2kn6M.js +5 -0
- package/build/client/_app/immutable/chunks/3Of2kn6M.js.br +0 -0
- package/build/client/_app/immutable/chunks/3Of2kn6M.js.gz +0 -0
- package/build/client/_app/immutable/chunks/5fVeMNeJ.js +1 -0
- package/build/client/_app/immutable/chunks/5fVeMNeJ.js.br +0 -0
- package/build/client/_app/immutable/chunks/5fVeMNeJ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/A8GZ5Uuc.js +1 -0
- package/build/client/_app/immutable/chunks/A8GZ5Uuc.js.br +0 -0
- package/build/client/_app/immutable/chunks/A8GZ5Uuc.js.gz +0 -0
- package/build/client/_app/immutable/chunks/AHy1Hzhf.js +1 -0
- package/build/client/_app/immutable/chunks/AHy1Hzhf.js.br +0 -0
- package/build/client/_app/immutable/chunks/AHy1Hzhf.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B3jXa2LJ.js +1 -0
- package/build/client/_app/immutable/chunks/B3jXa2LJ.js.br +0 -0
- package/build/client/_app/immutable/chunks/B3jXa2LJ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BEDHe9fo.js +1 -0
- package/build/client/_app/immutable/chunks/BEDHe9fo.js.br +0 -0
- package/build/client/_app/immutable/chunks/BEDHe9fo.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BJ_s8GdZ.js +2 -0
- package/build/client/_app/immutable/chunks/BJ_s8GdZ.js.br +0 -0
- package/build/client/_app/immutable/chunks/BJ_s8GdZ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BKd0HUNP.js +1 -0
- package/build/client/_app/immutable/chunks/BKd0HUNP.js.br +1 -0
- package/build/client/_app/immutable/chunks/BKd0HUNP.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BLL4xbEj.js +1 -0
- package/build/client/_app/immutable/chunks/BLL4xbEj.js.br +0 -0
- package/build/client/_app/immutable/chunks/BLL4xbEj.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BUUcNufp.js +1 -0
- package/build/client/_app/immutable/chunks/BUUcNufp.js.br +0 -0
- package/build/client/_app/immutable/chunks/BUUcNufp.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BVXUMEgB.js +1 -0
- package/build/client/_app/immutable/chunks/BVXUMEgB.js.br +0 -0
- package/build/client/_app/immutable/chunks/BVXUMEgB.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BY5XjwMm.js +1 -0
- package/build/client/_app/immutable/chunks/BY5XjwMm.js.br +0 -0
- package/build/client/_app/immutable/chunks/BY5XjwMm.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BdZ-2OIO.js +1 -0
- package/build/client/_app/immutable/chunks/BdZ-2OIO.js.br +0 -0
- package/build/client/_app/immutable/chunks/BdZ-2OIO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BdvB_H6l.js +1 -0
- package/build/client/_app/immutable/chunks/BdvB_H6l.js.br +0 -0
- package/build/client/_app/immutable/chunks/BdvB_H6l.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BkLV0FAz.js +1 -0
- package/build/client/_app/immutable/chunks/BkLV0FAz.js.br +0 -0
- package/build/client/_app/immutable/chunks/BkLV0FAz.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bm8huziG.js +1 -0
- package/build/client/_app/immutable/chunks/Bm8huziG.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bm8huziG.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BnMEvq-8.js +1 -0
- package/build/client/_app/immutable/chunks/BnMEvq-8.js.br +0 -0
- package/build/client/_app/immutable/chunks/BnMEvq-8.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BngQrigD.js +1 -0
- package/build/client/_app/immutable/chunks/BngQrigD.js.br +0 -0
- package/build/client/_app/immutable/chunks/BngQrigD.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BqQtpLRF.js +5 -0
- package/build/client/_app/immutable/chunks/BqQtpLRF.js.br +0 -0
- package/build/client/_app/immutable/chunks/BqQtpLRF.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bx2K4OII.js +1 -0
- package/build/client/_app/immutable/chunks/Bx2K4OII.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bx2K4OII.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bypc6r1_.js +1 -0
- package/build/client/_app/immutable/chunks/Bypc6r1_.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bypc6r1_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C16DfZ5a.js +1 -0
- package/build/client/_app/immutable/chunks/C16DfZ5a.js.br +1 -0
- package/build/client/_app/immutable/chunks/C16DfZ5a.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C3Yi7wCu.js +1 -0
- package/build/client/_app/immutable/chunks/C3Yi7wCu.js.br +0 -0
- package/build/client/_app/immutable/chunks/C3Yi7wCu.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C3e5IrpE.js +1 -0
- package/build/client/_app/immutable/chunks/C3e5IrpE.js.br +0 -0
- package/build/client/_app/immutable/chunks/C3e5IrpE.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C3o9xHsB.js +1 -0
- package/build/client/_app/immutable/chunks/C3o9xHsB.js.br +0 -0
- package/build/client/_app/immutable/chunks/C3o9xHsB.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C6q0X9xa.js +1 -0
- package/build/client/_app/immutable/chunks/C6q0X9xa.js.br +0 -0
- package/build/client/_app/immutable/chunks/C6q0X9xa.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C74jQQYs.js +1 -0
- package/build/client/_app/immutable/chunks/C74jQQYs.js.br +0 -0
- package/build/client/_app/immutable/chunks/C74jQQYs.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CBHYGBBU.js +4 -0
- package/build/client/_app/immutable/chunks/CBHYGBBU.js.br +0 -0
- package/build/client/_app/immutable/chunks/CBHYGBBU.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CD3hbDYc.js +1 -0
- package/build/client/_app/immutable/chunks/CD3hbDYc.js.br +0 -0
- package/build/client/_app/immutable/chunks/CD3hbDYc.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CE3Ue5FU.js +1 -0
- package/build/client/_app/immutable/chunks/CE3Ue5FU.js.br +0 -0
- package/build/client/_app/immutable/chunks/CE3Ue5FU.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CGGuNYjO.js +1 -0
- package/build/client/_app/immutable/chunks/CGGuNYjO.js.br +0 -0
- package/build/client/_app/immutable/chunks/CGGuNYjO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CH_9Gtej.js +1 -0
- package/build/client/_app/immutable/chunks/CH_9Gtej.js.br +0 -0
- package/build/client/_app/immutable/chunks/CH_9Gtej.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CIj4gWfs.js +1 -0
- package/build/client/_app/immutable/chunks/CIj4gWfs.js.br +0 -0
- package/build/client/_app/immutable/chunks/CIj4gWfs.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CJ3NIasv.js +1 -0
- package/build/client/_app/immutable/chunks/CJ3NIasv.js.br +0 -0
- package/build/client/_app/immutable/chunks/CJ3NIasv.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CKEJURpk.js +1 -0
- package/build/client/_app/immutable/chunks/CKEJURpk.js.br +0 -0
- package/build/client/_app/immutable/chunks/CKEJURpk.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CPmGhju_.js +1 -0
- package/build/client/_app/immutable/chunks/CPmGhju_.js.br +1 -0
- package/build/client/_app/immutable/chunks/CPmGhju_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CXcL7cOb.js +1 -0
- package/build/client/_app/immutable/chunks/CXcL7cOb.js.br +0 -0
- package/build/client/_app/immutable/chunks/CXcL7cOb.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CXgAyvD-.js +1 -0
- package/build/client/_app/immutable/chunks/CXgAyvD-.js.br +0 -0
- package/build/client/_app/immutable/chunks/CXgAyvD-.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CZBcpBnY.js +1 -0
- package/build/client/_app/immutable/chunks/CZBcpBnY.js.br +0 -0
- package/build/client/_app/immutable/chunks/CZBcpBnY.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CZPeboo-.js +1 -0
- package/build/client/_app/immutable/chunks/CZPeboo-.js.br +0 -0
- package/build/client/_app/immutable/chunks/CZPeboo-.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cb-gOV5k.js +1 -0
- package/build/client/_app/immutable/chunks/Cb-gOV5k.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cb-gOV5k.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cl72MTSX.js +1 -0
- package/build/client/_app/immutable/chunks/Cl72MTSX.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cl72MTSX.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cl7r7DFs.js +1 -0
- package/build/client/_app/immutable/chunks/Cl7r7DFs.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cl7r7DFs.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cm8CSK8p.js +1 -0
- package/build/client/_app/immutable/chunks/Cm8CSK8p.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cm8CSK8p.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cng04DK0.js +1 -0
- package/build/client/_app/immutable/chunks/Cng04DK0.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cng04DK0.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CqOnPLsz.js +1 -0
- package/build/client/_app/immutable/chunks/CqOnPLsz.js.br +0 -0
- package/build/client/_app/immutable/chunks/CqOnPLsz.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cqy1lT7e.js +1 -0
- package/build/client/_app/immutable/chunks/Cqy1lT7e.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cqy1lT7e.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CrjvgIhR.js +1 -0
- package/build/client/_app/immutable/chunks/CrjvgIhR.js.br +0 -0
- package/build/client/_app/immutable/chunks/CrjvgIhR.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CsRBDrRN.js +1 -0
- package/build/client/_app/immutable/chunks/CsRBDrRN.js.br +0 -0
- package/build/client/_app/immutable/chunks/CsRBDrRN.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CwwFbl61.js +62 -0
- package/build/client/_app/immutable/chunks/CwwFbl61.js.br +0 -0
- package/build/client/_app/immutable/chunks/CwwFbl61.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D0JSbOCt.js +1 -0
- package/build/client/_app/immutable/chunks/D0JSbOCt.js.br +0 -0
- package/build/client/_app/immutable/chunks/D0JSbOCt.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D25j-NnT.js +1 -0
- package/build/client/_app/immutable/chunks/D25j-NnT.js.br +0 -0
- package/build/client/_app/immutable/chunks/D25j-NnT.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D5hwKKRv.js +1 -0
- package/build/client/_app/immutable/chunks/D5hwKKRv.js.br +0 -0
- package/build/client/_app/immutable/chunks/D5hwKKRv.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DBxUIz0k.js +1 -0
- package/build/client/_app/immutable/chunks/DBxUIz0k.js.br +0 -0
- package/build/client/_app/immutable/chunks/DBxUIz0k.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DH7t4M9x.js +1 -0
- package/build/client/_app/immutable/chunks/DH7t4M9x.js.br +0 -0
- package/build/client/_app/immutable/chunks/DH7t4M9x.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DIsXG5DF.js +1 -0
- package/build/client/_app/immutable/chunks/DIsXG5DF.js.br +0 -0
- package/build/client/_app/immutable/chunks/DIsXG5DF.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DNgh8jh8.js +1 -0
- package/build/client/_app/immutable/chunks/DNgh8jh8.js.br +0 -0
- package/build/client/_app/immutable/chunks/DNgh8jh8.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DOWSzKHL.js +1 -0
- package/build/client/_app/immutable/chunks/DOWSzKHL.js.br +0 -0
- package/build/client/_app/immutable/chunks/DOWSzKHL.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DSbOoIPC.js +1 -0
- package/build/client/_app/immutable/chunks/DSbOoIPC.js.br +0 -0
- package/build/client/_app/immutable/chunks/DSbOoIPC.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DUYBhCSA.js +1 -0
- package/build/client/_app/immutable/chunks/DUYBhCSA.js.br +0 -0
- package/build/client/_app/immutable/chunks/DUYBhCSA.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DfDxKk91.js +2 -0
- package/build/client/_app/immutable/chunks/DfDxKk91.js.br +0 -0
- package/build/client/_app/immutable/chunks/DfDxKk91.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DhfQd5fc.js +1 -0
- package/build/client/_app/immutable/chunks/DhfQd5fc.js.br +0 -0
- package/build/client/_app/immutable/chunks/DhfQd5fc.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DmYFgZ4g.js +6 -0
- package/build/client/_app/immutable/chunks/DmYFgZ4g.js.br +0 -0
- package/build/client/_app/immutable/chunks/DmYFgZ4g.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dn-83VwV.js +1 -0
- package/build/client/_app/immutable/chunks/Dn-83VwV.js.br +1 -0
- package/build/client/_app/immutable/chunks/Dn-83VwV.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dnnuh6qK.js +1 -0
- package/build/client/_app/immutable/chunks/Dnnuh6qK.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dnnuh6qK.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DocaGLjv.js +1 -0
- package/build/client/_app/immutable/chunks/DocaGLjv.js.br +0 -0
- package/build/client/_app/immutable/chunks/DocaGLjv.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dqlbq2bD.js +2 -0
- package/build/client/_app/immutable/chunks/Dqlbq2bD.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dqlbq2bD.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DugIbHkD.js +1 -0
- package/build/client/_app/immutable/chunks/DugIbHkD.js.br +0 -0
- package/build/client/_app/immutable/chunks/DugIbHkD.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dy07EIjs.js +1 -0
- package/build/client/_app/immutable/chunks/Dy07EIjs.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dy07EIjs.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dy_X9Fgu.js +1 -0
- package/build/client/_app/immutable/chunks/Dy_X9Fgu.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dy_X9Fgu.js.gz +0 -0
- package/build/client/_app/immutable/chunks/I26VQaTs.js +2 -0
- package/build/client/_app/immutable/chunks/I26VQaTs.js.br +0 -0
- package/build/client/_app/immutable/chunks/I26VQaTs.js.gz +0 -0
- package/build/client/_app/immutable/chunks/IsTbwoyV.js +1 -0
- package/build/client/_app/immutable/chunks/IsTbwoyV.js.br +0 -0
- package/build/client/_app/immutable/chunks/IsTbwoyV.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Km1CymxJ.js +1 -0
- package/build/client/_app/immutable/chunks/Km1CymxJ.js.br +0 -0
- package/build/client/_app/immutable/chunks/Km1CymxJ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Me06-nC7.js +1 -0
- package/build/client/_app/immutable/chunks/Me06-nC7.js.br +0 -0
- package/build/client/_app/immutable/chunks/Me06-nC7.js.gz +0 -0
- package/build/client/_app/immutable/chunks/PQApLOOr.js +1 -0
- package/build/client/_app/immutable/chunks/PQApLOOr.js.br +0 -0
- package/build/client/_app/immutable/chunks/PQApLOOr.js.gz +0 -0
- package/build/client/_app/immutable/chunks/WcvPAxq7.js +1 -0
- package/build/client/_app/immutable/chunks/WcvPAxq7.js.br +0 -0
- package/build/client/_app/immutable/chunks/WcvPAxq7.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ZoG8qKMx.js +1 -0
- package/build/client/_app/immutable/chunks/ZoG8qKMx.js.br +0 -0
- package/build/client/_app/immutable/chunks/ZoG8qKMx.js.gz +0 -0
- package/build/client/_app/immutable/chunks/cZnJ0mAm.js +7 -0
- package/build/client/_app/immutable/chunks/cZnJ0mAm.js.br +0 -0
- package/build/client/_app/immutable/chunks/cZnJ0mAm.js.gz +0 -0
- package/build/client/_app/immutable/chunks/qb_JS6rP.js +1 -0
- package/build/client/_app/immutable/chunks/qb_JS6rP.js.br +0 -0
- package/build/client/_app/immutable/chunks/qb_JS6rP.js.gz +0 -0
- package/build/client/_app/immutable/chunks/riwaJFkb.js +3 -0
- package/build/client/_app/immutable/chunks/riwaJFkb.js.br +0 -0
- package/build/client/_app/immutable/chunks/riwaJFkb.js.gz +0 -0
- package/build/client/_app/immutable/chunks/wDLVOANX.js +2 -0
- package/build/client/_app/immutable/chunks/wDLVOANX.js.br +0 -0
- package/build/client/_app/immutable/chunks/wDLVOANX.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.CNnpPQtw.js +2 -0
- package/build/client/_app/immutable/entry/app.CNnpPQtw.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CNnpPQtw.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.BGpEqKH0.js +1 -0
- package/build/client/_app/immutable/entry/start.BGpEqKH0.js.br +0 -0
- package/build/client/_app/immutable/entry/start.BGpEqKH0.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.CGlGdRWB.js +8 -0
- package/build/client/_app/immutable/nodes/0.CGlGdRWB.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.CGlGdRWB.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.B4M0q1v4.js +1 -0
- package/build/client/_app/immutable/nodes/1.B4M0q1v4.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.B4M0q1v4.js.gz +0 -0
- package/build/client/_app/immutable/nodes/10.LJhhE6c8.js +3 -0
- package/build/client/_app/immutable/nodes/10.LJhhE6c8.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.LJhhE6c8.js.gz +0 -0
- package/build/client/_app/immutable/nodes/11.CM_xAozz.js +1 -0
- package/build/client/_app/immutable/nodes/11.CM_xAozz.js.br +0 -0
- package/build/client/_app/immutable/nodes/11.CM_xAozz.js.gz +0 -0
- package/build/client/_app/immutable/nodes/12.UENbuJYy.js +14 -0
- package/build/client/_app/immutable/nodes/12.UENbuJYy.js.br +0 -0
- package/build/client/_app/immutable/nodes/12.UENbuJYy.js.gz +0 -0
- package/build/client/_app/immutable/nodes/13.NbAfy9an.js +150 -0
- package/build/client/_app/immutable/nodes/13.NbAfy9an.js.br +0 -0
- package/build/client/_app/immutable/nodes/13.NbAfy9an.js.gz +0 -0
- package/build/client/_app/immutable/nodes/15.BgyPK-jf.js +11 -0
- package/build/client/_app/immutable/nodes/15.BgyPK-jf.js.br +0 -0
- package/build/client/_app/immutable/nodes/15.BgyPK-jf.js.gz +0 -0
- package/build/client/_app/immutable/nodes/16.DcgniGJK.js +9 -0
- package/build/client/_app/immutable/nodes/16.DcgniGJK.js.br +0 -0
- package/build/client/_app/immutable/nodes/16.DcgniGJK.js.gz +0 -0
- package/build/client/_app/immutable/nodes/17.CB9DXHxS.js +2 -0
- package/build/client/_app/immutable/nodes/17.CB9DXHxS.js.br +0 -0
- package/build/client/_app/immutable/nodes/17.CB9DXHxS.js.gz +0 -0
- package/build/client/_app/immutable/nodes/18.BHa-KoGE.js +6 -0
- package/build/client/_app/immutable/nodes/18.BHa-KoGE.js.br +0 -0
- package/build/client/_app/immutable/nodes/18.BHa-KoGE.js.gz +0 -0
- package/build/client/_app/immutable/nodes/19._VaGaWBD.js +1 -0
- package/build/client/_app/immutable/nodes/19._VaGaWBD.js.br +0 -0
- package/build/client/_app/immutable/nodes/19._VaGaWBD.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.DKxd0T79.js +14 -0
- package/build/client/_app/immutable/nodes/2.DKxd0T79.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DKxd0T79.js.gz +0 -0
- package/build/client/_app/immutable/nodes/20.C10FVhEw.js +10 -0
- package/build/client/_app/immutable/nodes/20.C10FVhEw.js.br +0 -0
- package/build/client/_app/immutable/nodes/20.C10FVhEw.js.gz +0 -0
- package/build/client/_app/immutable/nodes/21.BhyJIr3P.js +6 -0
- package/build/client/_app/immutable/nodes/21.BhyJIr3P.js.br +0 -0
- package/build/client/_app/immutable/nodes/21.BhyJIr3P.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.CcRoRYc-.js +5 -0
- package/build/client/_app/immutable/nodes/3.CcRoRYc-.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.CcRoRYc-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.BulJqGnt.js +1 -0
- package/build/client/_app/immutable/nodes/4.BulJqGnt.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.BulJqGnt.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.BT3hraTU.js +1 -0
- package/build/client/_app/immutable/nodes/5.BT3hraTU.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.BT3hraTU.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.DtNMha3Y.js +7 -0
- package/build/client/_app/immutable/nodes/6.DtNMha3Y.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.DtNMha3Y.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.Bv6Vn8eC.js +4 -0
- package/build/client/_app/immutable/nodes/7.Bv6Vn8eC.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.Bv6Vn8eC.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.DgQz7-Cb.js +1 -0
- package/build/client/_app/immutable/nodes/9.DgQz7-Cb.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.DgQz7-Cb.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/handler.js +7 -7
- package/build/index.js +7 -7
- package/build/server/chunks/chunks/AllowanceMeter.js-DBpmESX6.js +42 -0
- package/build/server/chunks/chunks/DecisionCard.js-B1YgF5EF.js +446 -0
- package/build/server/chunks/chunks/DictateTextarea.js-CbkGsd1h.js +415 -0
- package/build/server/chunks/chunks/DocSheet.js-BPPDMi3P.js +50 -0
- package/build/server/chunks/chunks/EtaLine.js-5SCP9O1f.js +79 -0
- package/build/server/chunks/chunks/EveryProjectSwitch.js-DzcwkHh4.js +200 -0
- package/build/server/chunks/chunks/Icon.js-CVZZ-yuB.js +129 -0
- package/build/server/chunks/chunks/InsightsNav.js-Cmn5y1to.js +18 -0
- package/build/server/chunks/chunks/InstallPlugin.js-B46b-IrK.js +45 -0
- package/build/server/chunks/chunks/LinkedText.js-COjHV297.js +43 -0
- package/build/server/chunks/chunks/ProjectBanner.js-ByPp7Y_e.js +111 -0
- package/build/server/chunks/chunks/ProjectStateMark.js-BkRD8cX4.js +61 -0
- package/build/server/chunks/chunks/QuestionList.js-CppuauNj.js +187 -0
- package/build/server/chunks/chunks/ResizableRail.js-BS8hRLV0.js +395 -0
- package/build/server/chunks/chunks/SettingsNav.js-BtJIv5cc.js +22 -0
- package/build/server/chunks/chunks/SlotContent.js-B4p1QLQN.js +748 -0
- package/build/server/chunks/chunks/StageTrack.js-DHvZX7Bl.js +111 -0
- package/build/server/chunks/chunks/TimeBar.js-CUFKEJW9.js +32 -0
- package/build/server/chunks/chunks/TimeLegend.js-B3Wl61jM.js +29 -0
- package/build/server/chunks/chunks/aliases.svelte.js-C_vCoKdk.js +138 -0
- package/build/server/chunks/chunks/arrow-down.js-Bt3fQooI.js +18 -0
- package/build/server/chunks/chunks/arrow-left.js-BKiSMzvI.js +18 -0
- package/build/server/chunks/chunks/arrow-right.js-CNYqcx0M.js +18 -0
- package/build/server/chunks/chunks/arrow-up.js-DHbXk79z.js +18 -0
- package/build/server/chunks/chunks/attachments.js-BAnnqpd-.js +48 -0
- package/build/server/chunks/chunks/bell.js-CXJp-3UZ.js +150 -0
- package/build/server/chunks/chunks/berths.js-CuAE9lHW.js +268 -0
- package/build/server/chunks/chunks/book-open.js-C2rohN3N.js +23 -0
- package/build/server/chunks/chunks/button.js-BF2PsvWw.js +7311 -0
- package/build/server/chunks/chunks/chat.svelte.js--pRzj_Xo.js +545 -0
- package/build/server/chunks/chunks/check.js-32sYHVxG.js +15 -0
- package/build/server/chunks/chunks/checks.js-DR3fVVpO.js +242 -0
- package/build/server/chunks/chunks/chevron-down.js-CH53s_me.js +15 -0
- package/build/server/chunks/chunks/chevron-left.js-D5RHY-kD.js +15 -0
- package/build/server/chunks/chunks/chevron-right.js-DCBAqAae.js +15 -0
- package/build/server/chunks/chunks/circle-alert.js-XTuAj7ZH.js +23 -0
- package/build/server/chunks/chunks/circle-dashed.js-fXv_bIo-.js +24 -0
- package/build/server/chunks/chunks/circle-dot.js-C950Kosw.js +18 -0
- package/build/server/chunks/chunks/client.js-B_5mNqUe.js +24 -0
- package/build/server/chunks/chunks/clock.js-CHhHTWK_.js +18 -0
- package/build/server/chunks/chunks/compass.js-k2sRSGWn.js +23 -0
- package/build/server/chunks/chunks/detail.js-DDoqeh1A.js +144 -0
- package/build/server/chunks/chunks/docpath.js-D27w_pf9.js +47 -0
- package/build/server/chunks/chunks/external-link.js-BQs1euhR.js +24 -0
- package/build/server/chunks/chunks/file-text.js-CnuVvHlB.js +26 -0
- package/build/server/chunks/chunks/gauge.js-Cbb1aqE7.js +35 -0
- package/build/server/chunks/chunks/git-pull-request.js-BMzktlcs.js +20 -0
- package/build/server/chunks/chunks/hand.js-BA9yKAan.js +25 -0
- package/build/server/chunks/chunks/harbour.js-PHLHOFea.js +117 -0
- package/build/server/chunks/chunks/html.js-FW6Ia4bL.js +8 -0
- package/build/server/chunks/chunks/http.js-CBM8xgUM.js +25 -0
- package/build/server/chunks/chunks/http2.js-fIdVBJaD.js +26 -0
- package/build/server/chunks/chunks/inbox.js-Dv8a1ecX.js +184 -0
- package/build/server/chunks/chunks/inbox.svelte.js-n7K3YnGB.js +171 -0
- package/build/server/chunks/chunks/index-server.js-DsObimPA.js +9 -0
- package/build/server/chunks/chunks/index-server2.js-Fb2GE8KQ.js +17 -0
- package/build/server/chunks/chunks/index.js-B0NFhRTN.js +1524 -0
- package/build/server/chunks/chunks/index2.js-Bxt1T6_K.js +46 -0
- package/build/server/chunks/chunks/internal.js-BlgsFxfY.js +146 -0
- package/build/server/chunks/chunks/live.svelte.js-By2dseJB.js +253 -0
- package/build/server/chunks/chunks/loader-circle.js-rQHvIWN7.js +16 -0
- package/build/server/chunks/chunks/markdown.js-CApgGpeX.js +2634 -0
- package/build/server/chunks/chunks/message-circle-question-mark.js-DbmaS9k_.js +25 -0
- package/build/server/chunks/chunks/message-square-text.js-CyRiNGB8.js +25 -0
- package/build/server/chunks/chunks/message-square.js-XK15WVZq.js +22 -0
- package/build/server/chunks/chunks/messages-square.js-BXH8BHfd.js +28 -0
- package/build/server/chunks/chunks/minus.js-DKvkj8VD.js +15 -0
- package/build/server/chunks/chunks/monitor-play.js-4ed3ghDf.js +28 -0
- package/build/server/chunks/chunks/nav.js-DwnXJgxd.js +29 -0
- package/build/server/chunks/chunks/plus.js-caVrjGZc.js +15 -0
- package/build/server/chunks/chunks/preview.js-hrimfbCG.js +69 -0
- package/build/server/chunks/chunks/prreview.js-Cg9LDxJp.js +92 -0
- package/build/server/chunks/chunks/repo.js-CuWUNKbP.js +33 -0
- package/build/server/chunks/chunks/requests.js-DiL4Poa9.js +42 -0
- package/build/server/chunks/chunks/restart.js-Dv1_ODgZ.js +12 -0
- package/build/server/chunks/chunks/root.js-60iebdW4.js +3105 -0
- package/build/server/chunks/chunks/rotate-cw.js-M4SHOpYI.js +21 -0
- package/build/server/chunks/chunks/runtime.js-BuGWYyZZ.js +16470 -0
- package/build/server/chunks/chunks/scale.js-S5g8YUqL.js +21 -0
- package/build/server/chunks/chunks/scroll-lock.js-Ci_ylINL.js +2811 -0
- package/build/server/chunks/chunks/service.js-DQK1kj-4.js +914 -0
- package/build/server/chunks/chunks/settings.js-C_6kvzhM.js +862 -0
- package/build/server/chunks/chunks/square-terminal.js-D9D9_9_V.js +30 -0
- package/build/server/chunks/chunks/store.js-XXOawxPN.js +317 -0
- package/build/server/chunks/chunks/tone.js-Dxsl-adt.js +31 -0
- package/build/server/chunks/chunks/triangle-alert.js-ClLszsZE.js +25 -0
- package/build/server/chunks/chunks/uploads.js-D45pIvPk.js +107 -0
- package/build/server/chunks/chunks/urls.js-BrlC6XZ8.js +8 -0
- package/build/server/chunks/chunks/utils.js-BJgm9WbO.js +2985 -0
- package/build/server/chunks/chunks/vault.js-CTaWe09z.js +162 -0
- package/build/server/chunks/chunks/view.js-CrOFjYk4.js +76 -0
- package/build/server/chunks/chunks/views.js-DyGxvskG.js +124 -0
- package/build/server/chunks/chunks/words.js-Cte4wOB4.js +48 -0
- package/build/server/chunks/chunks/x.js-YxaUOlq9.js +18 -0
- package/build/server/chunks/entries/endpoints/api/apps/_op_/_server.ts.js-DSkVUNdc.js +90 -0
- package/build/server/chunks/entries/endpoints/api/events/_server.ts.js-CYnLRjOH.js +106 -0
- package/build/server/chunks/entries/endpoints/api/files/_server.ts.js-CG8IGmSp.js +49 -0
- package/build/server/chunks/entries/endpoints/api/firstmate/chat/_server.ts.js-fmzKkrXb.js +83 -0
- package/build/server/chunks/entries/endpoints/api/firstmate/commands/_server.ts.js-BgIU3add.js +119 -0
- package/build/server/chunks/entries/endpoints/api/firstmate/dismiss/_server.ts.js-Ba9E_53E.js +45 -0
- package/build/server/chunks/entries/endpoints/api/firstmate/input/_server.ts.js-BQZBERsR.js +51 -0
- package/build/server/chunks/entries/endpoints/api/firstmate/interrupt/_server.ts.js-BnT3VHSx.js +44 -0
- package/build/server/chunks/entries/endpoints/api/firstmate/messages/_server.ts.js-DRYHwJ7R.js +66 -0
- package/build/server/chunks/entries/endpoints/api/firstmate/pick/_server.ts.js-BD2B1ByG.js +54 -0
- package/build/server/chunks/entries/endpoints/api/firstmate/start/_server.ts.js-DB-m2WWH.js +44 -0
- package/build/server/chunks/entries/endpoints/api/firstmate/uploads/_day_/_id_/_name_/_server.ts.js-CK3swjFX.js +32 -0
- package/build/server/chunks/entries/endpoints/api/firstmate/uploads/_server.ts.js-WCE63uer.js +40 -0
- package/build/server/chunks/entries/endpoints/api/fleet/_server.ts.js-D5-3acjK.js +36 -0
- package/build/server/chunks/entries/endpoints/api/gateway/_server.ts.js-B2I7jHdc.js +100 -0
- package/build/server/chunks/entries/endpoints/api/health/_server.ts.js-BFSqEG5B.js +36 -0
- package/build/server/chunks/entries/endpoints/api/inbox/_server.ts.js-BpUaHfsf.js +60 -0
- package/build/server/chunks/entries/endpoints/api/inbox/report/_server.ts.js-BNRD4HyF.js +51 -0
- package/build/server/chunks/entries/endpoints/api/insights/_server.ts.js-7808SNDM.js +36 -0
- package/build/server/chunks/entries/endpoints/api/plugins/_plugin_/config/_server.ts.js-B-cQTShG.js +71 -0
- package/build/server/chunks/entries/endpoints/api/plugins/_plugin_/load/_server.ts.js-CPAHSih2.js +23 -0
- package/build/server/chunks/entries/endpoints/api/plugins/_plugin_/ui/_...path_/_server.ts.js-DW3M1pzH.js +59 -0
- package/build/server/chunks/entries/endpoints/api/plugins/_plugin_/update/_server.ts.js-DDTSw0pX.js +35 -0
- package/build/server/chunks/entries/endpoints/api/plugins/_server.ts.js-BlD-1l6V.js +36 -0
- package/build/server/chunks/entries/endpoints/api/plugins/inspect/_server.ts.js-IT-jeApI.js +35 -0
- package/build/server/chunks/entries/endpoints/api/plugins/install/_server.ts.js-DQqKN2MF.js +38 -0
- package/build/server/chunks/entries/endpoints/api/plugins/updates/_server.ts.js-D7rx-bwI.js +23 -0
- package/build/server/chunks/entries/endpoints/api/projects/_id_/passage/_server.ts.js-BC0HjK9M.js +41 -0
- package/build/server/chunks/entries/endpoints/api/projects/_id_/passage/commands/_server.ts.js-aVmcjStV.js +50 -0
- package/build/server/chunks/entries/endpoints/api/projects/_id_/passage/consent/_server.ts.js-BuHN_WYU.js +62 -0
- package/build/server/chunks/entries/endpoints/api/projects/_id_/passage/draft/_server.ts.js-Dx0x7sws.js +49 -0
- package/build/server/chunks/entries/endpoints/api/projects/_id_/passage/file/_server.ts.js-CcCDMmmF.js +72 -0
- package/build/server/chunks/entries/endpoints/api/projects/_id_/passage/test/_server.ts.js-BsOOyTNM.js +55 -0
- package/build/server/chunks/entries/endpoints/api/services/_server.ts.js-DGyU8Zmu.js +55 -0
- package/build/server/chunks/entries/endpoints/api/slots/_server.ts.js-1B8fEiat.js +44 -0
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/_server.ts.js-D4CKazw-.js +41 -0
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/artifacts/_...name_/_server.ts.js-CUKo26Ia.js +60 -0
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/diff/_server.ts.js-BOa2lgvP.js +48 -0
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/output/_server.ts.js-BmI8Ofms.js +93 -0
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/preview/_server.ts.js-C2mJi22c.js +110 -0
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/review/_server.ts.js-8pN6fHl8.js +43 -0
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/review/ask/_server.ts.js-D6D9O8Su.js +49 -0
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/review/check/_server.ts.js-BAaCwzOf.js +48 -0
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/review/guide/_server.ts.js-DfP2iNta.js +48 -0
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/review/runs/_run_/stop/_server.ts.js-DZDaSSYT.js +41 -0
- package/build/server/chunks/entries/endpoints/api/vault/_server.ts.js-CJNUplXf.js +54 -0
- package/build/server/chunks/entries/endpoints/api/wheelhouse/update/_server.ts.js-CVqjWsw8.js +23 -0
- package/build/server/chunks/entries/endpoints/api/wheelhouse/upgrade/_server.ts.js-CE2zzWtC.js +23 -0
- package/build/server/chunks/entries/endpoints/projects/_server.ts.js-uMp6dcat.js +7 -0
- package/build/server/chunks/entries/fallbacks/error.svelte.js-DnpcUjRk.js +15 -0
- package/build/server/chunks/entries/hooks.server.js-FjSdw_QS.js +54 -0
- package/build/server/chunks/entries/pages/_layout.server.ts.js-BkWAC802.js +22 -0
- package/build/server/chunks/entries/pages/_layout.svelte.js-DstinFjN.js +8746 -0
- package/build/server/chunks/entries/pages/_page.server.ts.js-BXkuWJh7.js +16 -0
- package/build/server/chunks/entries/pages/_page.svelte.js-ehBbQlco.js +2194 -0
- package/build/server/chunks/entries/pages/decisions/_page.server.ts.js-B2YZfjBk.js +11 -0
- package/build/server/chunks/entries/pages/decisions/_page.svelte.js-C3NsHXP-.js +184 -0
- package/build/server/chunks/entries/pages/files/_page.server.ts.js-CT3pm5a6.js +17 -0
- package/build/server/chunks/entries/pages/files/_page.svelte.js-Bj-5ZaJn.js +61 -0
- package/build/server/chunks/entries/pages/harbour/_page.server.ts.js-CcLkegHq.js +46 -0
- package/build/server/chunks/entries/pages/harbour/_page.svelte.js-hBC6QUHZ.js +580 -0
- package/build/server/chunks/entries/pages/headroom/_page.server.ts.js-BazCFIuz.js +11 -0
- package/build/server/chunks/entries/pages/headroom/_page.svelte.js-BLFQjLz8.js +149 -0
- package/build/server/chunks/entries/pages/inbox/_page.svelte.js-rjm303kf.js +185 -0
- package/build/server/chunks/entries/pages/plugins/_page.server.ts.js-D_nxVVQ0.js +11 -0
- package/build/server/chunks/entries/pages/plugins/_plugin_/_page_/_page.server.ts.js-BRQpg3kQ.js +16 -0
- package/build/server/chunks/entries/pages/plugins/_plugin_/_page_/_page.svelte.js-DazeLLT8.js +52 -0
- package/build/server/chunks/entries/pages/projects/_id_/_page.server.ts.js-BIypd_XU.js +23 -0
- package/build/server/chunks/entries/pages/projects/_id_/_page.svelte.js-DJajtLup.js +945 -0
- package/build/server/chunks/entries/pages/projects/_id_/_plugin_/_tab_/_page.server.ts.js-BwAOIwUv.js +21 -0
- package/build/server/chunks/entries/pages/projects/_id_/_plugin_/_tab_/_page.svelte.js-Cm8mxt61.js +74 -0
- package/build/server/chunks/entries/pages/projects/_id_/passage/_page.server.ts.js-CnfqlJgl.js +27 -0
- package/build/server/chunks/entries/pages/projects/_id_/passage/_page.svelte.js-CuMA-rCM.js +433 -0
- package/build/server/chunks/entries/pages/projects/_id_/passage/edit/_page.server.ts.js-CG14M19O.js +15 -0
- package/build/server/chunks/entries/pages/projects/_id_/passage/edit/_page.svelte.js-mtGQO-dJ.js +1880 -0
- package/build/server/chunks/entries/pages/settings/_page.server.ts.js-DnBg1UuM.js +11 -0
- package/build/server/chunks/entries/pages/settings/gateway/_page.server.ts.js-Zgfe-LX9.js +11 -0
- package/build/server/chunks/entries/pages/settings/gateway/_page.svelte.js-BmbSSxk6.js +208 -0
- package/build/server/chunks/entries/pages/settings/plugins/_page.server.ts.js-DZ4oJmnj.js +19 -0
- package/build/server/chunks/entries/pages/settings/plugins/_page.svelte.js-gBZCjZlg.js +1847 -0
- package/build/server/chunks/entries/pages/settings/plugins/_plugin_/_page.server.ts.js-Dm-tgyir.js +17 -0
- package/build/server/chunks/entries/pages/settings/plugins/_plugin_/_page.svelte.js-BFGxdrrb.js +220 -0
- package/build/server/chunks/entries/pages/tasks/_id_/_page.server.ts.js-Llp3a0Fb.js +25 -0
- package/build/server/chunks/entries/pages/tasks/_id_/_page.svelte.js-LgJMZ2RE.js +2107 -0
- package/build/server/chunks/entries/pages/tasks/_id_/doc/_page.server.ts.js-DuWuEdZ2.js +24 -0
- package/build/server/chunks/entries/pages/tasks/_id_/doc/_page.svelte.js-BOMmvf6G.js +80 -0
- package/build/server/chunks/entries/pages/tasks/_id_/review/_page.server.ts.js-BMQF9I9c.js +15 -0
- package/build/server/chunks/entries/pages/tasks/_id_/review/_page.svelte.js-k659tqiw.js +657 -0
- package/build/server/chunks/entries/pages/time/_page.server.ts.js-DstGu3ji.js +11 -0
- package/build/server/chunks/entries/pages/time/_page.svelte.js-Ckn0YUW_.js +108 -0
- package/build/server/chunks/handler-BRidfBIY.js +1474 -0
- package/build/server/chunks/index.js-Dhoi5wa_.js +4296 -0
- package/build/server/chunks/manifest.js-CF4aVg77.js +537 -0
- package/build/server/chunks/nodes/0.js-B2yTHeOL.js +41 -0
- package/build/server/chunks/nodes/1.js-BoD2_1or.js +9 -0
- package/build/server/chunks/nodes/10.js-C2XmTW8j.js +43 -0
- package/build/server/chunks/nodes/11.js-CAJnQkWH.js +42 -0
- package/build/server/chunks/nodes/12.js-Cu57dOUj.js +43 -0
- package/build/server/chunks/nodes/13.js-1g1BRnk_.js +42 -0
- package/build/server/chunks/nodes/14.js-Cl3VZ6XR.js +12 -0
- package/build/server/chunks/nodes/15.js-BqL5jpLU.js +41 -0
- package/build/server/chunks/nodes/16.js-DibdTAKZ.js +41 -0
- package/build/server/chunks/nodes/17.js-BUFA5Avk.js +44 -0
- package/build/server/chunks/nodes/18.js-cxLrKmMx.js +42 -0
- package/build/server/chunks/nodes/19.js-DMbI3mDp.js +42 -0
- package/build/server/chunks/nodes/2.js-8QSIq2KZ.js +41 -0
- package/build/server/chunks/nodes/20.js-BNXpftag.js +42 -0
- package/build/server/chunks/nodes/21.js-DdJK6h7p.js +41 -0
- package/build/server/chunks/nodes/3.js-DHT7fiTy.js +41 -0
- package/build/server/chunks/nodes/4.js-DvjrLz8n.js +41 -0
- package/build/server/chunks/nodes/5.js-CvUGUQbi.js +45 -0
- package/build/server/chunks/nodes/6.js-ptKHsAn6.js +41 -0
- package/build/server/chunks/nodes/7.js-BigF9SYG.js +9 -0
- package/build/server/chunks/nodes/8.js-D32pPbKa.js +12 -0
- package/build/server/chunks/nodes/9.js-DkS4HHqV.js +41 -0
- package/dist/cli.js +2156 -62
- package/dist/wh-vault.js +1007 -0
- package/package.json +5 -3
- package/plugins/github-issues/.wheelhouse-plugin/plugin.json +44 -0
- package/plugins/github-issues/README.md +16 -0
- package/plugins/github-issues/wheelhouse/server/index.js +463 -0
- package/plugins/project-links/.wheelhouse-plugin/plugin.json +25 -0
- package/plugins/project-links/README.md +17 -0
- package/plugins/project-links/wheelhouse/server/index.js +278 -0
- package/plugins/project-links/wheelhouse/ui/index.js +87 -0
- package/plugins/vault/.wheelhouse-plugin/plugin.json +22 -0
- package/plugins/vault/README.md +18 -0
- package/plugins/vault/wheelhouse/server/index.js +297 -0
- package/build/client/_app/immutable/assets/0.DaQhCSel.css +0 -1
- package/build/client/_app/immutable/assets/0.DaQhCSel.css.br +0 -0
- package/build/client/_app/immutable/assets/0.DaQhCSel.css.gz +0 -0
- package/build/client/_app/immutable/assets/12.DT1C9nmd.css +0 -1
- package/build/client/_app/immutable/assets/12.DT1C9nmd.css.br +0 -0
- package/build/client/_app/immutable/assets/12.DT1C9nmd.css.gz +0 -0
- package/build/client/_app/immutable/assets/14.CjNfSPQ6.css +0 -1
- package/build/client/_app/immutable/assets/14.CjNfSPQ6.css.br +0 -0
- package/build/client/_app/immutable/assets/14.CjNfSPQ6.css.gz +0 -0
- package/build/client/_app/immutable/chunks/36FDwM1G.js +0 -1
- package/build/client/_app/immutable/chunks/36FDwM1G.js.br +0 -0
- package/build/client/_app/immutable/chunks/36FDwM1G.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B1XxiJam.js +0 -1
- package/build/client/_app/immutable/chunks/B1XxiJam.js.br +0 -0
- package/build/client/_app/immutable/chunks/B1XxiJam.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B2wEHue2.js +0 -1
- package/build/client/_app/immutable/chunks/B2wEHue2.js.br +0 -0
- package/build/client/_app/immutable/chunks/B2wEHue2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BAurN1n6.js +0 -2
- package/build/client/_app/immutable/chunks/BAurN1n6.js.br +0 -0
- package/build/client/_app/immutable/chunks/BAurN1n6.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BEAYWcbT.js +0 -1
- package/build/client/_app/immutable/chunks/BEAYWcbT.js.br +0 -0
- package/build/client/_app/immutable/chunks/BEAYWcbT.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BGINfGsV.js +0 -1
- package/build/client/_app/immutable/chunks/BGINfGsV.js.br +0 -4
- package/build/client/_app/immutable/chunks/BGINfGsV.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BHsdHLbF.js +0 -1
- package/build/client/_app/immutable/chunks/BHsdHLbF.js.br +0 -0
- package/build/client/_app/immutable/chunks/BHsdHLbF.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BR5v76-T.js +0 -1
- package/build/client/_app/immutable/chunks/BR5v76-T.js.br +0 -0
- package/build/client/_app/immutable/chunks/BR5v76-T.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BUo4hMcM.js +0 -1
- package/build/client/_app/immutable/chunks/BUo4hMcM.js.br +0 -0
- package/build/client/_app/immutable/chunks/BUo4hMcM.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BVVO3JPx.js +0 -1
- package/build/client/_app/immutable/chunks/BVVO3JPx.js.br +0 -0
- package/build/client/_app/immutable/chunks/BVVO3JPx.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BZm5e4fE.js +0 -1
- package/build/client/_app/immutable/chunks/BZm5e4fE.js.br +0 -0
- package/build/client/_app/immutable/chunks/BZm5e4fE.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bfi4vm-w.js +0 -1
- package/build/client/_app/immutable/chunks/Bfi4vm-w.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bfi4vm-w.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BiTivU7x.js +0 -1
- package/build/client/_app/immutable/chunks/BiTivU7x.js.br +0 -0
- package/build/client/_app/immutable/chunks/BiTivU7x.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BiuLIp4b.js +0 -1
- package/build/client/_app/immutable/chunks/BiuLIp4b.js.br +0 -0
- package/build/client/_app/immutable/chunks/BiuLIp4b.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bns_F-QV.js +0 -1
- package/build/client/_app/immutable/chunks/Bns_F-QV.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bns_F-QV.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Brlpgp90.js +0 -1
- package/build/client/_app/immutable/chunks/Brlpgp90.js.br +0 -0
- package/build/client/_app/immutable/chunks/Brlpgp90.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BunkbzFG.js +0 -1
- package/build/client/_app/immutable/chunks/BunkbzFG.js.br +0 -0
- package/build/client/_app/immutable/chunks/BunkbzFG.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BwOfqJw9.js +0 -1
- package/build/client/_app/immutable/chunks/BwOfqJw9.js.br +0 -0
- package/build/client/_app/immutable/chunks/BwOfqJw9.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C-xrRA1Y.js +0 -1
- package/build/client/_app/immutable/chunks/C-xrRA1Y.js.br +0 -0
- package/build/client/_app/immutable/chunks/C-xrRA1Y.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C2_XZrIO.js +0 -7
- package/build/client/_app/immutable/chunks/C2_XZrIO.js.br +0 -0
- package/build/client/_app/immutable/chunks/C2_XZrIO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C2d4bDYU.js +0 -1
- package/build/client/_app/immutable/chunks/C2d4bDYU.js.br +0 -0
- package/build/client/_app/immutable/chunks/C2d4bDYU.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C6BQPSOs.js +0 -62
- package/build/client/_app/immutable/chunks/C6BQPSOs.js.br +0 -0
- package/build/client/_app/immutable/chunks/C6BQPSOs.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CDphfOBv.js +0 -1
- package/build/client/_app/immutable/chunks/CDphfOBv.js.br +0 -0
- package/build/client/_app/immutable/chunks/CDphfOBv.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CHoQMvOZ.js +0 -1
- package/build/client/_app/immutable/chunks/CHoQMvOZ.js.br +0 -0
- package/build/client/_app/immutable/chunks/CHoQMvOZ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CIE0U5Ih.js +0 -1
- package/build/client/_app/immutable/chunks/CIE0U5Ih.js.br +0 -0
- package/build/client/_app/immutable/chunks/CIE0U5Ih.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CLs9Rkbg.js +0 -1
- package/build/client/_app/immutable/chunks/CLs9Rkbg.js.br +0 -0
- package/build/client/_app/immutable/chunks/CLs9Rkbg.js.gz +0 -0
- package/build/client/_app/immutable/chunks/COQ3aAYj.js +0 -1
- package/build/client/_app/immutable/chunks/COQ3aAYj.js.br +0 -0
- package/build/client/_app/immutable/chunks/COQ3aAYj.js.gz +0 -0
- package/build/client/_app/immutable/chunks/COiIt96y.js +0 -1
- package/build/client/_app/immutable/chunks/COiIt96y.js.br +0 -0
- package/build/client/_app/immutable/chunks/COiIt96y.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CQ7gIAH8.js +0 -1
- package/build/client/_app/immutable/chunks/CQ7gIAH8.js.br +0 -2
- package/build/client/_app/immutable/chunks/CQ7gIAH8.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CQlHU-Rx.js +0 -1
- package/build/client/_app/immutable/chunks/CQlHU-Rx.js.br +0 -0
- package/build/client/_app/immutable/chunks/CQlHU-Rx.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CXfdgbLv.js +0 -1
- package/build/client/_app/immutable/chunks/CXfdgbLv.js.br +0 -0
- package/build/client/_app/immutable/chunks/CXfdgbLv.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CeYIQAXD.js +0 -1
- package/build/client/_app/immutable/chunks/CeYIQAXD.js.br +0 -0
- package/build/client/_app/immutable/chunks/CeYIQAXD.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CewXGI4o.js +0 -14
- package/build/client/_app/immutable/chunks/CewXGI4o.js.br +0 -0
- package/build/client/_app/immutable/chunks/CewXGI4o.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CfdzAfq2.js +0 -1
- package/build/client/_app/immutable/chunks/CfdzAfq2.js.br +0 -0
- package/build/client/_app/immutable/chunks/CfdzAfq2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CkFDaaNz.js +0 -1
- package/build/client/_app/immutable/chunks/CkFDaaNz.js.br +0 -0
- package/build/client/_app/immutable/chunks/CkFDaaNz.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CkLriSVx.js +0 -1
- package/build/client/_app/immutable/chunks/CkLriSVx.js.br +0 -0
- package/build/client/_app/immutable/chunks/CkLriSVx.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Clzs_eXS.js +0 -5
- package/build/client/_app/immutable/chunks/Clzs_eXS.js.br +0 -0
- package/build/client/_app/immutable/chunks/Clzs_eXS.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CrCQ2NNA.js +0 -1
- package/build/client/_app/immutable/chunks/CrCQ2NNA.js.br +0 -0
- package/build/client/_app/immutable/chunks/CrCQ2NNA.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CrgqcyB_.js +0 -1
- package/build/client/_app/immutable/chunks/CrgqcyB_.js.br +0 -0
- package/build/client/_app/immutable/chunks/CrgqcyB_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CtlVBKf-.js +0 -1
- package/build/client/_app/immutable/chunks/CtlVBKf-.js.br +0 -0
- package/build/client/_app/immutable/chunks/CtlVBKf-.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D4aeBIhU.js +0 -1
- package/build/client/_app/immutable/chunks/D4aeBIhU.js.br +0 -0
- package/build/client/_app/immutable/chunks/D4aeBIhU.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D8Hx-_mj.js +0 -1
- package/build/client/_app/immutable/chunks/D8Hx-_mj.js.br +0 -3
- package/build/client/_app/immutable/chunks/D8Hx-_mj.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DC8T2WRZ.js +0 -1
- package/build/client/_app/immutable/chunks/DC8T2WRZ.js.br +0 -0
- package/build/client/_app/immutable/chunks/DC8T2WRZ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DGy76IbW.js +0 -2
- package/build/client/_app/immutable/chunks/DGy76IbW.js.br +0 -0
- package/build/client/_app/immutable/chunks/DGy76IbW.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DIABVMrA.js +0 -1
- package/build/client/_app/immutable/chunks/DIABVMrA.js.br +0 -0
- package/build/client/_app/immutable/chunks/DIABVMrA.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DLith9Yi.js +0 -1
- package/build/client/_app/immutable/chunks/DLith9Yi.js.br +0 -0
- package/build/client/_app/immutable/chunks/DLith9Yi.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DPTtvukI.js +0 -1
- package/build/client/_app/immutable/chunks/DPTtvukI.js.br +0 -0
- package/build/client/_app/immutable/chunks/DPTtvukI.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Da92_52d.js +0 -1
- package/build/client/_app/immutable/chunks/Da92_52d.js.br +0 -0
- package/build/client/_app/immutable/chunks/Da92_52d.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Db0WMrvR.js +0 -1
- package/build/client/_app/immutable/chunks/Db0WMrvR.js.br +0 -0
- package/build/client/_app/immutable/chunks/Db0WMrvR.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dlb_KqyS.js +0 -2
- package/build/client/_app/immutable/chunks/Dlb_KqyS.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dlb_KqyS.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DmcreR7o.js +0 -1
- package/build/client/_app/immutable/chunks/DmcreR7o.js.br +0 -0
- package/build/client/_app/immutable/chunks/DmcreR7o.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dq9kJk5R.js +0 -1
- package/build/client/_app/immutable/chunks/Dq9kJk5R.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dq9kJk5R.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DxGQT-Hz.js +0 -1
- package/build/client/_app/immutable/chunks/DxGQT-Hz.js.br +0 -0
- package/build/client/_app/immutable/chunks/DxGQT-Hz.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ET-O1REu.js +0 -1
- package/build/client/_app/immutable/chunks/ET-O1REu.js.br +0 -0
- package/build/client/_app/immutable/chunks/ET-O1REu.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Ewrw42Vq.js +0 -1
- package/build/client/_app/immutable/chunks/Ewrw42Vq.js.br +0 -0
- package/build/client/_app/immutable/chunks/Ewrw42Vq.js.gz +0 -0
- package/build/client/_app/immutable/chunks/FQ69txbU.js +0 -1
- package/build/client/_app/immutable/chunks/FQ69txbU.js.br +0 -0
- package/build/client/_app/immutable/chunks/FQ69txbU.js.gz +0 -0
- package/build/client/_app/immutable/chunks/L-6wuY5f.js +0 -2
- package/build/client/_app/immutable/chunks/L-6wuY5f.js.br +0 -0
- package/build/client/_app/immutable/chunks/L-6wuY5f.js.gz +0 -0
- package/build/client/_app/immutable/chunks/M201SoVK.js +0 -1
- package/build/client/_app/immutable/chunks/M201SoVK.js.br +0 -1
- package/build/client/_app/immutable/chunks/M201SoVK.js.gz +0 -0
- package/build/client/_app/immutable/chunks/OeqXO44y.js +0 -1
- package/build/client/_app/immutable/chunks/OeqXO44y.js.br +0 -0
- package/build/client/_app/immutable/chunks/OeqXO44y.js.gz +0 -0
- package/build/client/_app/immutable/chunks/PPVm8Dsz.js +0 -1
- package/build/client/_app/immutable/chunks/PPVm8Dsz.js.br +0 -0
- package/build/client/_app/immutable/chunks/PPVm8Dsz.js.gz +0 -0
- package/build/client/_app/immutable/chunks/QAFSHLn2.js +0 -1
- package/build/client/_app/immutable/chunks/QAFSHLn2.js.br +0 -0
- package/build/client/_app/immutable/chunks/QAFSHLn2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Tm9AblHs.js +0 -1
- package/build/client/_app/immutable/chunks/Tm9AblHs.js.br +0 -0
- package/build/client/_app/immutable/chunks/Tm9AblHs.js.gz +0 -0
- package/build/client/_app/immutable/chunks/bvY4yrzM.js +0 -4
- package/build/client/_app/immutable/chunks/bvY4yrzM.js.br +0 -0
- package/build/client/_app/immutable/chunks/bvY4yrzM.js.gz +0 -0
- package/build/client/_app/immutable/chunks/eMSiEzFi.js +0 -1
- package/build/client/_app/immutable/chunks/eMSiEzFi.js.br +0 -0
- package/build/client/_app/immutable/chunks/eMSiEzFi.js.gz +0 -0
- package/build/client/_app/immutable/chunks/iVpzjqp1.js +0 -1
- package/build/client/_app/immutable/chunks/iVpzjqp1.js.br +0 -0
- package/build/client/_app/immutable/chunks/iVpzjqp1.js.gz +0 -0
- package/build/client/_app/immutable/chunks/m3YfhLk1.js +0 -1
- package/build/client/_app/immutable/chunks/m3YfhLk1.js.br +0 -0
- package/build/client/_app/immutable/chunks/m3YfhLk1.js.gz +0 -0
- package/build/client/_app/immutable/chunks/p7x6PRF0.js +0 -1
- package/build/client/_app/immutable/chunks/p7x6PRF0.js.br +0 -0
- package/build/client/_app/immutable/chunks/p7x6PRF0.js.gz +0 -0
- package/build/client/_app/immutable/chunks/t4rxsdjl.js +0 -74
- package/build/client/_app/immutable/chunks/t4rxsdjl.js.br +0 -0
- package/build/client/_app/immutable/chunks/t4rxsdjl.js.gz +0 -0
- package/build/client/_app/immutable/chunks/uD7-7yV9.js +0 -1
- package/build/client/_app/immutable/chunks/uD7-7yV9.js.br +0 -4
- package/build/client/_app/immutable/chunks/uD7-7yV9.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.D22_AlD8.js +0 -2
- package/build/client/_app/immutable/entry/app.D22_AlD8.js.br +0 -0
- package/build/client/_app/immutable/entry/app.D22_AlD8.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.xwa7znED.js +0 -1
- package/build/client/_app/immutable/entry/start.xwa7znED.js.br +0 -0
- package/build/client/_app/immutable/entry/start.xwa7znED.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.DsfUbybd.js +0 -5
- package/build/client/_app/immutable/nodes/0.DsfUbybd.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.DsfUbybd.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.9tnApS8T.js +0 -1
- package/build/client/_app/immutable/nodes/1.9tnApS8T.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.9tnApS8T.js.gz +0 -0
- package/build/client/_app/immutable/nodes/10.CbyVViLq.js +0 -14
- package/build/client/_app/immutable/nodes/10.CbyVViLq.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.CbyVViLq.js.gz +0 -0
- package/build/client/_app/immutable/nodes/11.xg5UTCOx.js +0 -77
- package/build/client/_app/immutable/nodes/11.xg5UTCOx.js.br +0 -0
- package/build/client/_app/immutable/nodes/11.xg5UTCOx.js.gz +0 -0
- package/build/client/_app/immutable/nodes/12.C6hLGaov.js +0 -32
- package/build/client/_app/immutable/nodes/12.C6hLGaov.js.br +0 -0
- package/build/client/_app/immutable/nodes/12.C6hLGaov.js.gz +0 -0
- package/build/client/_app/immutable/nodes/13.DHbTeLkm.js +0 -5
- package/build/client/_app/immutable/nodes/13.DHbTeLkm.js.br +0 -0
- package/build/client/_app/immutable/nodes/13.DHbTeLkm.js.gz +0 -0
- package/build/client/_app/immutable/nodes/14.BMIOGjF-.js +0 -1
- package/build/client/_app/immutable/nodes/14.BMIOGjF-.js.br +0 -0
- package/build/client/_app/immutable/nodes/14.BMIOGjF-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/15.DYSMqa5A.js +0 -10
- package/build/client/_app/immutable/nodes/15.DYSMqa5A.js.br +0 -0
- package/build/client/_app/immutable/nodes/15.DYSMqa5A.js.gz +0 -0
- package/build/client/_app/immutable/nodes/16.B0mX0t_j.js +0 -6
- package/build/client/_app/immutable/nodes/16.B0mX0t_j.js.br +0 -0
- package/build/client/_app/immutable/nodes/16.B0mX0t_j.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.CSYV0jmk.js +0 -4
- package/build/client/_app/immutable/nodes/2.CSYV0jmk.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.CSYV0jmk.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.X5qqsIUa.js +0 -5
- package/build/client/_app/immutable/nodes/3.X5qqsIUa.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.X5qqsIUa.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.Di4k8SR2.js +0 -3
- package/build/client/_app/immutable/nodes/4.Di4k8SR2.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.Di4k8SR2.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.DMFYTxVa.js +0 -7
- package/build/client/_app/immutable/nodes/5.DMFYTxVa.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.DMFYTxVa.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.6yDAURLV.js +0 -4
- package/build/client/_app/immutable/nodes/6.6yDAURLV.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.6yDAURLV.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.xxvMU05c.js +0 -1
- package/build/client/_app/immutable/nodes/7.xxvMU05c.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.xxvMU05c.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.GSaWotW8.js +0 -1
- package/build/client/_app/immutable/nodes/8.GSaWotW8.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.GSaWotW8.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.CEiUwDDg.js +0 -1
- package/build/client/_app/immutable/nodes/9.CEiUwDDg.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.CEiUwDDg.js.gz +0 -0
- package/build/server/chunks/chunks/AllowanceMeter.js-C9jRsAGU.js +0 -42
- package/build/server/chunks/chunks/DecisionCard.js-u1VQ9s_v.js +0 -350
- package/build/server/chunks/chunks/DictateTextarea.js-BlEnOzcN.js +0 -85
- package/build/server/chunks/chunks/EtaLine.js-Pa4_VV6V.js +0 -79
- package/build/server/chunks/chunks/Icon.js-D7CY3joB.js +0 -129
- package/build/server/chunks/chunks/InsightsNav.js-BlxFBJfj.js +0 -22
- package/build/server/chunks/chunks/MicButton.js-BZSQAyuM.js +0 -336
- package/build/server/chunks/chunks/QuestionList.js-DglKezUe.js +0 -259
- package/build/server/chunks/chunks/SlotContent.js-YqZXXtRz.js +0 -1977
- package/build/server/chunks/chunks/StageTrack.js-BaorW-yk.js +0 -59
- package/build/server/chunks/chunks/TimeBar.js-k38rc7ZI.js +0 -32
- package/build/server/chunks/chunks/TimeLegend.js-BQVvgsvs.js +0 -29
- package/build/server/chunks/chunks/access.js-CS2pJasF.js +0 -816
- package/build/server/chunks/chunks/anchor.js-ByXDBfbi.js +0 -20
- package/build/server/chunks/chunks/arrow-down.js-DXDJc9EE.js +0 -18
- package/build/server/chunks/chunks/arrow-left.js-6Yey35CE.js +0 -18
- package/build/server/chunks/chunks/arrow-right.js-CfbPWRqu.js +0 -18
- package/build/server/chunks/chunks/arrow-up.js-AhghNbhW.js +0 -18
- package/build/server/chunks/chunks/bell.js-CAlv92RY.js +0 -143
- package/build/server/chunks/chunks/blocks.js-BL9jLCe-.js +0 -710
- package/build/server/chunks/chunks/bus.svelte.js-DRTptEhF.js +0 -9
- package/build/server/chunks/chunks/button.js-HeBqLheH.js +0 -7311
- package/build/server/chunks/chunks/chat.svelte.js-Cgdbtjbw.js +0 -537
- package/build/server/chunks/chunks/check.js-bmUhmMEz.js +0 -15
- package/build/server/chunks/chunks/checks.js-BnIxIaus.js +0 -175
- package/build/server/chunks/chunks/chevron-down.js-D75hVSIe.js +0 -15
- package/build/server/chunks/chunks/chevron-right.js-yhLg_GaH.js +0 -15
- package/build/server/chunks/chunks/circle-dashed.js-Bm3gZCXK.js +0 -24
- package/build/server/chunks/chunks/client.js-BUNengrw.js +0 -24
- package/build/server/chunks/chunks/clock.js-D7JTvRbC.js +0 -18
- package/build/server/chunks/chunks/compass.js-Tq6uiG8I.js +0 -23
- package/build/server/chunks/chunks/detail.js-CSjOmiVi.js +0 -133
- package/build/server/chunks/chunks/external-link.js-WxVF0xXO.js +0 -24
- package/build/server/chunks/chunks/file-text.js-CAo3vdqd.js +0 -26
- package/build/server/chunks/chunks/gauge.js-Dw9bgE72.js +0 -35
- package/build/server/chunks/chunks/git-pull-request.js-Cx1w8BQr.js +0 -20
- package/build/server/chunks/chunks/hammer.js-fp_Kidfj.js +0 -24
- package/build/server/chunks/chunks/hand.js-xkYPYJZj.js +0 -37
- package/build/server/chunks/chunks/harbour.js-xu7EfIHk.js +0 -113
- package/build/server/chunks/chunks/http.js-TzZuOtxn.js +0 -25
- package/build/server/chunks/chunks/http2.js-Cr9xuMRL.js +0 -26
- package/build/server/chunks/chunks/inbox.js-CW2sFmx9.js +0 -114
- package/build/server/chunks/chunks/inbox.svelte.js-OoKhjjWM.js +0 -134
- package/build/server/chunks/chunks/index-server.js-CtyUzIGY.js +0 -17
- package/build/server/chunks/chunks/index-server2.js-DMc-UvjC.js +0 -19
- package/build/server/chunks/chunks/index.js-Bn6j9diM.js +0 -1524
- package/build/server/chunks/chunks/index2.js-CXaRTuov.js +0 -46
- package/build/server/chunks/chunks/internal.js-Co-6PAQm.js +0 -146
- package/build/server/chunks/chunks/live.svelte.js-BRUTNc9D.js +0 -196
- package/build/server/chunks/chunks/loader-circle.js-IYRaUYWq.js +0 -16
- package/build/server/chunks/chunks/message-circle-question-mark.js-DwrMi0ty.js +0 -25
- package/build/server/chunks/chunks/message-square-text.js-N5uEmy8L.js +0 -25
- package/build/server/chunks/chunks/message-square.js-Hp-6BHgG.js +0 -22
- package/build/server/chunks/chunks/minus.js-bsfnwE-h.js +0 -15
- package/build/server/chunks/chunks/monitor-play.js-B7qbbPH6.js +0 -28
- package/build/server/chunks/chunks/preview.js-CVD-Kwks.js +0 -68
- package/build/server/chunks/chunks/prreview.js-C5XxDIu2.js +0 -85
- package/build/server/chunks/chunks/root.js-CQzYN4Pu.js +0 -3105
- package/build/server/chunks/chunks/runtime.js-C0n924wq.js +0 -13280
- package/build/server/chunks/chunks/scale.js-vP4G0lZM.js +0 -21
- package/build/server/chunks/chunks/shield-check.js-PJLnCKD9.js +0 -23
- package/build/server/chunks/chunks/spec.js-B0yI1XqG.js +0 -307
- package/build/server/chunks/chunks/split.js-BsCU24rQ.js +0 -20
- package/build/server/chunks/chunks/square-terminal.js-D5Z4Sl2-.js +0 -47
- package/build/server/chunks/chunks/tone.js-r2qFVrZC.js +0 -30
- package/build/server/chunks/chunks/trash.js-DYFbRevY.js +0 -46
- package/build/server/chunks/chunks/triangle-alert.js-DuE40HSU.js +0 -38
- package/build/server/chunks/chunks/utils.js-DTSGDHlH.js +0 -2956
- package/build/server/chunks/chunks/view.js-BtcLmKUY.js +0 -76
- package/build/server/chunks/chunks/view2.js-BdUXqNlY.js +0 -87
- package/build/server/chunks/chunks/views.js-B0wP-tuB.js +0 -137
- package/build/server/chunks/chunks/waypoints.js-D3HN7bje.js +0 -67
- package/build/server/chunks/chunks/x.js-tlds9Ooz.js +0 -18
- package/build/server/chunks/entries/endpoints/api/events/_server.ts.js-DNHG661Q.js +0 -93
- package/build/server/chunks/entries/endpoints/api/firstmate/chat/_server.ts.js-XBUQePMq.js +0 -74
- package/build/server/chunks/entries/endpoints/api/firstmate/commands/_server.ts.js-D1tzpRWy.js +0 -110
- package/build/server/chunks/entries/endpoints/api/firstmate/input/_server.ts.js-DqhQUZUA.js +0 -42
- package/build/server/chunks/entries/endpoints/api/firstmate/interrupt/_server.ts.js-LsZer0Bi.js +0 -35
- package/build/server/chunks/entries/endpoints/api/firstmate/messages/_server.ts.js-DQob0XkJ.js +0 -57
- package/build/server/chunks/entries/endpoints/api/firstmate/pick/_server.ts.js-DgFjtYYL.js +0 -45
- package/build/server/chunks/entries/endpoints/api/firstmate/start/_server.ts.js-DOoA4rRW.js +0 -35
- package/build/server/chunks/entries/endpoints/api/fleet/_server.ts.js-DZQsFY87.js +0 -27
- package/build/server/chunks/entries/endpoints/api/health/_server.ts.js-DuSzTlq5.js +0 -27
- package/build/server/chunks/entries/endpoints/api/inbox/_server.ts.js-C15XwPXH.js +0 -51
- package/build/server/chunks/entries/endpoints/api/insights/_server.ts.js--gavJO4f.js +0 -27
- package/build/server/chunks/entries/endpoints/api/plugins/_server.ts.js-CsTy_jT6.js +0 -27
- package/build/server/chunks/entries/endpoints/api/projects/_id_/passage/_server.ts.js-BWOcGtOU.js +0 -32
- package/build/server/chunks/entries/endpoints/api/projects/_id_/passage/commands/_server.ts.js-Kjwbc2wm.js +0 -41
- package/build/server/chunks/entries/endpoints/api/projects/_id_/passage/consent/_server.ts.js-BTIb8CUb.js +0 -53
- package/build/server/chunks/entries/endpoints/api/projects/_id_/passage/draft/_server.ts.js-Vh52yDJ9.js +0 -40
- package/build/server/chunks/entries/endpoints/api/projects/_id_/passage/file/_server.ts.js-LJDJLrpB.js +0 -63
- package/build/server/chunks/entries/endpoints/api/projects/_id_/passage/test/_server.ts.js-CdDBYH6K.js +0 -46
- package/build/server/chunks/entries/endpoints/api/services/_server.ts.js-DoElFmWK.js +0 -46
- package/build/server/chunks/entries/endpoints/api/slots/_server.ts.js-KiiPnPLF.js +0 -35
- package/build/server/chunks/entries/endpoints/api/specs/_id_/_server.ts.js-LMXTPr04.js +0 -59
- package/build/server/chunks/entries/endpoints/api/specs/_id_/build-request/_server.ts.js-bqjJGIjt.js +0 -47
- package/build/server/chunks/entries/endpoints/api/specs/_id_/comments/_comment_/_server.ts.js-DD1qQE9w.js +0 -36
- package/build/server/chunks/entries/endpoints/api/specs/_id_/comments/_server.ts.js-Bby1xkpH.js +0 -57
- package/build/server/chunks/entries/endpoints/api/specs/_id_/images/_name_/_server.ts.js-CPX8OLQa.js +0 -49
- package/build/server/chunks/entries/endpoints/api/specs/_id_/images/_server.ts.js-BjBV4SVl.js +0 -55
- package/build/server/chunks/entries/endpoints/api/specs/_id_/sent/_server.ts.js-BAqm0IW0.js +0 -36
- package/build/server/chunks/entries/endpoints/api/specs/_id_/turn/_server.ts.js-CNFIHA70.js +0 -82
- package/build/server/chunks/entries/endpoints/api/specs/_server.ts.js-Bix9PKKr.js +0 -40
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/_server.ts.js-CT7cLDQU.js +0 -32
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/artifacts/_...name_/_server.ts.js-zCsm8UJT.js +0 -51
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/diff/_server.ts.js-ccyEPbua.js +0 -39
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/output/_server.ts.js-eZPQtK6T.js +0 -84
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/preview/_server.ts.js-CW76hSLW.js +0 -102
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/review/_server.ts.js-Cjuig1dr.js +0 -34
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/review/ask/_server.ts.js-C04r1OFS.js +0 -40
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/review/check/_server.ts.js-Ca5VNJtJ.js +0 -39
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/review/guide/_server.ts.js-BKLIKSUM.js +0 -39
- package/build/server/chunks/entries/endpoints/api/tasks/_id_/review/runs/_run_/stop/_server.ts.js-C-D9zXgK.js +0 -32
- package/build/server/chunks/entries/fallbacks/error.svelte.js-DlN_tCrh.js +0 -15
- package/build/server/chunks/entries/hooks.server.js-Cgq1_RNe.js +0 -40
- package/build/server/chunks/entries/pages/_layout.server.ts.js-B7EWMhm1.js +0 -20
- package/build/server/chunks/entries/pages/_layout.svelte.js-BJuZaosz.js +0 -10454
- package/build/server/chunks/entries/pages/_page.server.ts.js-CrjekE3C.js +0 -16
- package/build/server/chunks/entries/pages/_page.svelte.js-BI4c5H0n.js +0 -3699
- package/build/server/chunks/entries/pages/decisions/_page.server.ts.js-Olm_zS-k.js +0 -11
- package/build/server/chunks/entries/pages/decisions/_page.svelte.js-ClTz3O_K.js +0 -167
- package/build/server/chunks/entries/pages/harbour/_page.server.ts.js-CVhhNUGy.js +0 -20
- package/build/server/chunks/entries/pages/harbour/_page.svelte.js-43FRNSVK.js +0 -1346
- package/build/server/chunks/entries/pages/headroom/_page.server.ts.js-Bv8GukD1.js +0 -11
- package/build/server/chunks/entries/pages/headroom/_page.svelte.js-zmKQdAwy.js +0 -135
- package/build/server/chunks/entries/pages/inbox/_page.svelte.js-wTvAFPNM.js +0 -175
- package/build/server/chunks/entries/pages/plugins/_plugin_/_page_/_page.server.ts.js-BJRmRxez.js +0 -15
- package/build/server/chunks/entries/pages/plugins/_plugin_/_page_/_page.svelte.js-DfAqJJ3i.js +0 -55
- package/build/server/chunks/entries/pages/projects/_id_/_plugin_/_tab_/_page.server.ts.js-DawJ61h-.js +0 -15
- package/build/server/chunks/entries/pages/projects/_id_/_plugin_/_tab_/_page.svelte.js-B472Pugu.js +0 -66
- package/build/server/chunks/entries/pages/projects/_id_/passage/_page.server.ts.js-Cm-4qkeb.js +0 -14
- package/build/server/chunks/entries/pages/projects/_id_/passage/_page.svelte.js-CWjEArXY.js +0 -394
- package/build/server/chunks/entries/pages/projects/_id_/passage/edit/_page.server.ts.js-DWNW8L5o.js +0 -15
- package/build/server/chunks/entries/pages/projects/_id_/passage/edit/_page.svelte.js-iHacn81K.js +0 -1859
- package/build/server/chunks/entries/pages/projects/_page.server.ts.js-BVDM5Frg.js +0 -37
- package/build/server/chunks/entries/pages/projects/_page.svelte.js-B_NQ-Wg3.js +0 -270
- package/build/server/chunks/entries/pages/spec/_page.server.ts.js-B3atEUDm.js +0 -31
- package/build/server/chunks/entries/pages/spec/_page.svelte.js-BJjweG9u.js +0 -1561
- package/build/server/chunks/entries/pages/tasks/_id_/_page.server.ts.js-BDQIO7Ft.js +0 -25
- package/build/server/chunks/entries/pages/tasks/_id_/_page.svelte.js-Dr2wwDJG.js +0 -2063
- package/build/server/chunks/entries/pages/tasks/_id_/doc/_page.server.ts.js-Bw-3fcQ6.js +0 -24
- package/build/server/chunks/entries/pages/tasks/_id_/doc/_page.svelte.js-DOyZ1QzI.js +0 -145
- package/build/server/chunks/entries/pages/tasks/_id_/review/_page.server.ts.js-CJek9uLa.js +0 -15
- package/build/server/chunks/entries/pages/tasks/_id_/review/_page.svelte.js-BDaRELgE.js +0 -692
- package/build/server/chunks/entries/pages/time/_page.server.ts.js-An6F-Ydg.js +0 -11
- package/build/server/chunks/entries/pages/time/_page.svelte.js-CSXGxvVs.js +0 -107
- package/build/server/chunks/handler-BnRz5A8m.js +0 -1474
- package/build/server/chunks/index.js-C8djlQGs.js +0 -4296
- package/build/server/chunks/manifest.js-YAglwkb0.js +0 -434
- package/build/server/chunks/nodes/0.js-lkzICUiD.js +0 -32
- package/build/server/chunks/nodes/1.js-BJHI215Q.js +0 -9
- package/build/server/chunks/nodes/10.js-BWB-fD67.js +0 -33
- package/build/server/chunks/nodes/11.js-DWkXu85W.js +0 -33
- package/build/server/chunks/nodes/12.js-CKTx5eRx.js +0 -37
- package/build/server/chunks/nodes/13.js-C9YUj8Wf.js +0 -33
- package/build/server/chunks/nodes/14.js-CvfRHrCy.js +0 -33
- package/build/server/chunks/nodes/15.js-D_eEoMQb.js +0 -33
- package/build/server/chunks/nodes/16.js-BQhgpvij.js +0 -32
- package/build/server/chunks/nodes/2.js-C-3t-e-a.js +0 -32
- package/build/server/chunks/nodes/3.js-DgwKYi6C.js +0 -32
- package/build/server/chunks/nodes/4.js-DRDQ1cZ6.js +0 -34
- package/build/server/chunks/nodes/5.js-Bn5aoN6C.js +0 -32
- package/build/server/chunks/nodes/6.js-lazTWxVv.js +0 -9
- package/build/server/chunks/nodes/7.js-PUjlFfxr.js +0 -32
- package/build/server/chunks/nodes/8.js-DYPhaWV-.js +0 -34
- package/build/server/chunks/nodes/9.js-C2q_Jaiu.js +0 -32
- /package/build/client/_app/immutable/assets/{15.KLSHp5Yu.css → 20.KLSHp5Yu.css} +0 -0
- /package/build/client/_app/immutable/assets/{15.KLSHp5Yu.css.br → 20.KLSHp5Yu.css.br} +0 -0
- /package/build/client/_app/immutable/assets/{15.KLSHp5Yu.css.gz → 20.KLSHp5Yu.css.gz} +0 -0
- /package/build/client/_app/immutable/assets/{4.5dJKCkOT.css → 5.5dJKCkOT.css} +0 -0
- /package/build/client/_app/immutable/assets/{4.5dJKCkOT.css.br → 5.5dJKCkOT.css.br} +0 -0
- /package/build/client/_app/immutable/assets/{4.5dJKCkOT.css.gz → 5.5dJKCkOT.css.gz} +0 -0
- /package/build/client/_app/immutable/assets/{MicButton.Dga9zAug.css → DictateTextarea.Dga9zAug.css} +0 -0
- /package/build/client/_app/immutable/assets/{MicButton.Dga9zAug.css.br → DictateTextarea.Dga9zAug.css.br} +0 -0
- /package/build/client/_app/immutable/assets/{MicButton.Dga9zAug.css.gz → DictateTextarea.Dga9zAug.css.gz} +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// cli/index.ts
|
|
4
|
-
import { spawn as
|
|
4
|
+
import { fork, spawn as spawn4 } from "node:child_process";
|
|
5
5
|
import { existsSync as existsSync2 } from "node:fs";
|
|
6
6
|
import { createServer } from "node:http";
|
|
7
|
-
import
|
|
8
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
|
-
import { Command, InvalidArgumentError, Option } from "commander";
|
|
7
|
+
import path13 from "node:path";
|
|
8
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
|
|
9
|
+
import { Command as Command4, InvalidArgumentError as InvalidArgumentError2, Option } from "commander";
|
|
10
10
|
|
|
11
11
|
// package.json
|
|
12
|
-
var version = "0.
|
|
12
|
+
var version = "0.2.0";
|
|
13
|
+
|
|
14
|
+
// src/lib/chat/attachments.ts
|
|
15
|
+
var UPLOAD_MAX_BYTES = 25 * 1024 * 1024;
|
|
16
|
+
var UNIT = { B: 1, KB: 1024, MB: 1024 * 1024 };
|
|
13
17
|
|
|
14
18
|
// src/lib/server/firstmate/install.ts
|
|
15
19
|
import { access, constants, open, stat } from "node:fs/promises";
|
|
@@ -291,13 +295,13 @@ async function checkScript(firstmateDir, s) {
|
|
|
291
295
|
} catch {
|
|
292
296
|
return { ...base, ok: false, problem: `${s.name} is not executable` };
|
|
293
297
|
}
|
|
294
|
-
let
|
|
298
|
+
let text2;
|
|
295
299
|
try {
|
|
296
|
-
|
|
300
|
+
text2 = await readHead(file);
|
|
297
301
|
} catch (e) {
|
|
298
302
|
return { ...base, ok: false, problem: `${s.name} could not be read: ${e.message}` };
|
|
299
303
|
}
|
|
300
|
-
const missing = s.markers.filter((m) => !
|
|
304
|
+
const missing = s.markers.filter((m) => !text2.includes(m));
|
|
301
305
|
if (missing.length) {
|
|
302
306
|
return {
|
|
303
307
|
...base,
|
|
@@ -315,10 +319,10 @@ async function firstmateVersion(firstmateDir) {
|
|
|
315
319
|
);
|
|
316
320
|
return r.ok ? r.stdout.trim() || null : null;
|
|
317
321
|
}
|
|
318
|
-
async function checkCompatibility(
|
|
322
|
+
async function checkCompatibility(install2) {
|
|
319
323
|
const [scripts, version2] = await Promise.all([
|
|
320
|
-
Promise.all(SUPPORTED_SCRIPTS.map((s) => checkScript(
|
|
321
|
-
firstmateVersion(
|
|
324
|
+
Promise.all(SUPPORTED_SCRIPTS.map((s) => checkScript(install2.firstmateDir, s))),
|
|
325
|
+
firstmateVersion(install2.firstmateDir)
|
|
322
326
|
]);
|
|
323
327
|
const problems = scripts.flatMap((s) => s.problem ? [s.problem] : []);
|
|
324
328
|
return {
|
|
@@ -331,6 +335,7 @@ async function checkCompatibility(install) {
|
|
|
331
335
|
}
|
|
332
336
|
|
|
333
337
|
// src/lib/server/plugins/config.ts
|
|
338
|
+
import { mkdir, readFile, rename, stat as stat2, writeFile } from "node:fs/promises";
|
|
334
339
|
import { homedir } from "node:os";
|
|
335
340
|
import path2 from "node:path";
|
|
336
341
|
function defaultConfigDir(env) {
|
|
@@ -338,60 +343,2105 @@ function defaultConfigDir(env) {
|
|
|
338
343
|
if (env.XDG_CONFIG_HOME) return path2.join(env.XDG_CONFIG_HOME, "wheelhouse");
|
|
339
344
|
return path2.join(homedir(), ".config", "wheelhouse");
|
|
340
345
|
}
|
|
346
|
+
var PLUGINS_FILE = "plugins.json";
|
|
347
|
+
var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
348
|
+
function parsePluginConfig(text2) {
|
|
349
|
+
let raw;
|
|
350
|
+
try {
|
|
351
|
+
raw = JSON.parse(text2);
|
|
352
|
+
} catch (e) {
|
|
353
|
+
return { ok: false, error: `not valid JSON: ${e.message}` };
|
|
354
|
+
}
|
|
355
|
+
if (!isRecord(raw)) return { ok: false, error: "expected a JSON object" };
|
|
356
|
+
for (const k of Object.keys(raw)) {
|
|
357
|
+
if (k !== "plugins") return { ok: false, error: `unknown field "${k}"` };
|
|
358
|
+
}
|
|
359
|
+
const plugins = {};
|
|
360
|
+
if (raw.plugins !== void 0) {
|
|
361
|
+
if (!isRecord(raw.plugins)) return { ok: false, error: '"plugins" must be an object' };
|
|
362
|
+
for (const [id, s] of Object.entries(raw.plugins)) {
|
|
363
|
+
if (!isRecord(s)) return { ok: false, error: `plugins.${id} must be an object` };
|
|
364
|
+
const setting = {};
|
|
365
|
+
for (const [k, v] of Object.entries(s)) {
|
|
366
|
+
if (k === "enabled") {
|
|
367
|
+
if (typeof v !== "boolean") {
|
|
368
|
+
return { ok: false, error: `plugins.${id}.enabled must be true or false` };
|
|
369
|
+
}
|
|
370
|
+
setting.enabled = v;
|
|
371
|
+
} else if (k === "projects") {
|
|
372
|
+
if (!isRecord(v)) return { ok: false, error: `plugins.${id}.projects must be an object` };
|
|
373
|
+
for (const [p, on] of Object.entries(v)) {
|
|
374
|
+
if (typeof on !== "boolean") {
|
|
375
|
+
return { ok: false, error: `plugins.${id}.projects.${p} must be true or false` };
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
setting.projects = v;
|
|
379
|
+
} else if (k === "settings") {
|
|
380
|
+
if (!isRecord(v)) return { ok: false, error: `plugins.${id}.settings must be an object` };
|
|
381
|
+
setting.settings = v;
|
|
382
|
+
} else {
|
|
383
|
+
return { ok: false, error: `unknown field "plugins.${id}.${k}"` };
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
plugins[id] = setting;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return { ok: true, config: { plugins } };
|
|
390
|
+
}
|
|
391
|
+
async function readRaw(file) {
|
|
392
|
+
let text2;
|
|
393
|
+
try {
|
|
394
|
+
text2 = await readFile(file, "utf8");
|
|
395
|
+
} catch (e) {
|
|
396
|
+
if (e.code === "ENOENT") return { ok: true, raw: null };
|
|
397
|
+
return { ok: false, error: `could not read ${file}: ${e.message}` };
|
|
398
|
+
}
|
|
399
|
+
const parsed = parsePluginConfig(text2);
|
|
400
|
+
if (!parsed.ok) return { ok: false, error: `${file} is not valid: ${parsed.error}` };
|
|
401
|
+
return { ok: true, raw: JSON.parse(text2) };
|
|
402
|
+
}
|
|
403
|
+
async function readPluginSettings(dir, id) {
|
|
404
|
+
const r = await readRaw(path2.join(dir, PLUGINS_FILE));
|
|
405
|
+
if (!r.ok) return r;
|
|
406
|
+
const plugins = r.raw?.plugins ?? {};
|
|
407
|
+
return { ok: true, settings: { ...plugins[id]?.settings ?? {} } };
|
|
408
|
+
}
|
|
409
|
+
var editing = Promise.resolve();
|
|
410
|
+
function editPlugin(dir, id, change) {
|
|
411
|
+
const run = editing.then(async () => {
|
|
412
|
+
const file = path2.join(dir, PLUGINS_FILE);
|
|
413
|
+
const r = await readRaw(file);
|
|
414
|
+
if (!r.ok) return r;
|
|
415
|
+
const raw = r.raw ?? {};
|
|
416
|
+
const plugins = isRecord(raw.plugins) ? raw.plugins : {};
|
|
417
|
+
const entry2 = isRecord(plugins[id]) ? { ...plugins[id] } : {};
|
|
418
|
+
change(entry2);
|
|
419
|
+
if (Object.keys(entry2).length) plugins[id] = entry2;
|
|
420
|
+
else delete plugins[id];
|
|
421
|
+
raw.plugins = plugins;
|
|
422
|
+
try {
|
|
423
|
+
await mkdir(dir, { recursive: true });
|
|
424
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
425
|
+
await writeFile(tmp, `${JSON.stringify(raw, null, " ")}
|
|
426
|
+
`);
|
|
427
|
+
await rename(tmp, file);
|
|
428
|
+
} catch (e) {
|
|
429
|
+
return { ok: false, error: `could not write ${file}: ${e.message}` };
|
|
430
|
+
}
|
|
431
|
+
return { ok: true };
|
|
432
|
+
});
|
|
433
|
+
editing = run.catch(() => {
|
|
434
|
+
});
|
|
435
|
+
return run;
|
|
436
|
+
}
|
|
437
|
+
function writePluginSettings(dir, id, settings2) {
|
|
438
|
+
return editPlugin(dir, id, (entry2) => {
|
|
439
|
+
entry2.settings = settings2;
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// src/lib/server/plugins/installed.ts
|
|
444
|
+
import { mkdir as mkdir2, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "node:fs/promises";
|
|
445
|
+
import { homedir as homedir2 } from "node:os";
|
|
446
|
+
import path3 from "node:path";
|
|
447
|
+
var INSTALLED_FILE = "installed.json";
|
|
448
|
+
var SCHEMA = "wheelhouse-installed-plugins.v1";
|
|
449
|
+
var EMPTY_INSTALLED = () => ({ schema: SCHEMA, plugins: {} });
|
|
450
|
+
function defaultPluginsDir(env) {
|
|
451
|
+
if (env.WHEELHOUSE_PLUGINS_DIR) return path3.resolve(env.WHEELHOUSE_PLUGINS_DIR);
|
|
452
|
+
return path3.join(homedir2(), ".wheelhouse", "plugins");
|
|
453
|
+
}
|
|
454
|
+
var isRecord2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
455
|
+
function record(v) {
|
|
456
|
+
if (!isRecord2(v)) return null;
|
|
457
|
+
const { repository, ref, commit, version: version2, installedAt, enabled } = v;
|
|
458
|
+
if (typeof repository !== "string" || ref !== null && typeof ref !== "string" || typeof commit !== "string" || typeof version2 !== "string" || typeof installedAt !== "string" || typeof enabled !== "boolean") {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
return { repository, ref, commit, version: version2, installedAt, enabled };
|
|
462
|
+
}
|
|
463
|
+
async function readInstalled(dir) {
|
|
464
|
+
const file = path3.join(dir, INSTALLED_FILE);
|
|
465
|
+
let text2;
|
|
466
|
+
try {
|
|
467
|
+
text2 = await readFile2(file, "utf8");
|
|
468
|
+
} catch (e) {
|
|
469
|
+
if (e.code === "ENOENT")
|
|
470
|
+
return { ok: true, installed: EMPTY_INSTALLED() };
|
|
471
|
+
return { ok: false, error: `could not read ${file}: ${e.message}` };
|
|
472
|
+
}
|
|
473
|
+
let raw;
|
|
474
|
+
try {
|
|
475
|
+
raw = JSON.parse(text2);
|
|
476
|
+
} catch (e) {
|
|
477
|
+
return { ok: false, error: `${file} is not valid JSON: ${e.message}` };
|
|
478
|
+
}
|
|
479
|
+
if (!isRecord2(raw) || raw.schema !== SCHEMA || !isRecord2(raw.plugins)) {
|
|
480
|
+
return { ok: false, error: `${file} is not a ${SCHEMA} record` };
|
|
481
|
+
}
|
|
482
|
+
const installed = EMPTY_INSTALLED();
|
|
483
|
+
for (const [id, v] of Object.entries(raw.plugins)) {
|
|
484
|
+
const r = record(v);
|
|
485
|
+
if (!r) return { ok: false, error: `${file}: the entry for "${id}" is not a valid record` };
|
|
486
|
+
installed.plugins[id] = r;
|
|
487
|
+
}
|
|
488
|
+
return { ok: true, installed };
|
|
489
|
+
}
|
|
490
|
+
async function writeInstalled(dir, installed) {
|
|
491
|
+
await mkdir2(dir, { recursive: true });
|
|
492
|
+
const file = path3.join(dir, INSTALLED_FILE);
|
|
493
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
494
|
+
await writeFile2(tmp, `${JSON.stringify(installed, null, " ")}
|
|
495
|
+
`);
|
|
496
|
+
await rename2(tmp, file);
|
|
497
|
+
}
|
|
341
498
|
|
|
342
499
|
// src/lib/server/store/store.ts
|
|
343
500
|
import { realpathSync } from "node:fs";
|
|
344
|
-
import
|
|
501
|
+
import path4 from "node:path";
|
|
502
|
+
|
|
503
|
+
// src/lib/inbox.ts
|
|
504
|
+
var SNOOZE_MS = 4 * 3600 * 1e3;
|
|
505
|
+
|
|
506
|
+
// src/lib/server/store/store.ts
|
|
345
507
|
function defaultStateDir(env) {
|
|
346
|
-
if (env.WHEELHOUSE_STATE_DIR) return
|
|
347
|
-
if (env.XDG_STATE_HOME) return
|
|
348
|
-
return
|
|
508
|
+
if (env.WHEELHOUSE_STATE_DIR) return path4.resolve(env.WHEELHOUSE_STATE_DIR);
|
|
509
|
+
if (env.XDG_STATE_HOME) return path4.join(path4.resolve(env.XDG_STATE_HOME), "wheelhouse");
|
|
510
|
+
return path4.join(env.HOME ?? "", ".local", "state", "wheelhouse");
|
|
349
511
|
}
|
|
350
512
|
function real(p) {
|
|
351
|
-
const abs =
|
|
513
|
+
const abs = path4.resolve(p);
|
|
352
514
|
try {
|
|
353
515
|
return realpathSync(abs);
|
|
354
516
|
} catch {
|
|
355
|
-
const parent =
|
|
356
|
-
return parent === abs ? abs :
|
|
517
|
+
const parent = path4.dirname(abs);
|
|
518
|
+
return parent === abs ? abs : path4.join(real(parent), path4.basename(abs));
|
|
357
519
|
}
|
|
358
520
|
}
|
|
359
521
|
function isInside(p, dir) {
|
|
360
|
-
const rel =
|
|
361
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
522
|
+
const rel = path4.relative(real(dir), real(p));
|
|
523
|
+
return rel === "" || !rel.startsWith("..") && !path4.isAbsolute(rel);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// src/lib/server/restart.ts
|
|
527
|
+
var KEY = /* @__PURE__ */ Symbol.for("wheelhouse.restart");
|
|
528
|
+
var g = globalThis;
|
|
529
|
+
function setRestart(fn) {
|
|
530
|
+
g[KEY] = fn ?? void 0;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// cli/gateway.ts
|
|
534
|
+
import path6 from "node:path";
|
|
535
|
+
import { Argument, Command, InvalidArgumentError } from "commander";
|
|
536
|
+
|
|
537
|
+
// src/lib/gateway.ts
|
|
538
|
+
var GATEWAY_CHANNELS = [
|
|
539
|
+
{ channel: "discord", label: "Discord" }
|
|
540
|
+
];
|
|
541
|
+
var DEFAULT_GATEWAY_ALERTS = ["question", "pr-green", "stuck"];
|
|
542
|
+
var PAIRING_MS = 10 * 6e4;
|
|
543
|
+
|
|
544
|
+
// src/lib/server/gateway/channels/discord.ts
|
|
545
|
+
import WebSocket from "ws";
|
|
546
|
+
var DISCORD_API = "https://discord.com/api/v10";
|
|
547
|
+
var DISCORD_INTENTS = 1 << 12;
|
|
548
|
+
var REQUEST_TIMEOUT_MS = 15e3;
|
|
549
|
+
var wsTransport = {
|
|
550
|
+
fetch: (url, init) => fetch(url, init),
|
|
551
|
+
connect(url, on) {
|
|
552
|
+
const ws = new WebSocket(url);
|
|
553
|
+
ws.on("message", (data) => on.message(data.toString()));
|
|
554
|
+
ws.on("close", (code) => on.close(code));
|
|
555
|
+
ws.on("error", (e) => on.error(e));
|
|
556
|
+
return {
|
|
557
|
+
send: (data) => {
|
|
558
|
+
if (ws.readyState === WebSocket.OPEN) ws.send(data);
|
|
559
|
+
},
|
|
560
|
+
close: (code) => ws.close(code)
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
var DiscordError = class extends Error {
|
|
565
|
+
constructor(status, message) {
|
|
566
|
+
super(message);
|
|
567
|
+
this.status = status;
|
|
568
|
+
this.name = "DiscordError";
|
|
569
|
+
}
|
|
570
|
+
status;
|
|
571
|
+
};
|
|
572
|
+
var str = (v) => typeof v === "string" && v ? v : null;
|
|
573
|
+
function sleep(ms, signal) {
|
|
574
|
+
return new Promise((resolve) => {
|
|
575
|
+
const t = setTimeout(done, ms);
|
|
576
|
+
function done() {
|
|
577
|
+
clearTimeout(t);
|
|
578
|
+
signal?.removeEventListener("abort", done);
|
|
579
|
+
resolve();
|
|
580
|
+
}
|
|
581
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
async function call(transport, token, version2, method, route, what, body) {
|
|
585
|
+
const redact = (s) => s.split(token).join("[token]");
|
|
586
|
+
for (let attempt = 0; ; attempt++) {
|
|
587
|
+
let res;
|
|
588
|
+
try {
|
|
589
|
+
res = await transport.fetch(`${DISCORD_API}${route}`, {
|
|
590
|
+
method,
|
|
591
|
+
headers: {
|
|
592
|
+
authorization: `Bot ${token}`,
|
|
593
|
+
"content-type": "application/json",
|
|
594
|
+
"user-agent": `DiscordBot (https://github.com/chrisjowen/wheelhouse, ${version2})`
|
|
595
|
+
},
|
|
596
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
597
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
598
|
+
});
|
|
599
|
+
} catch (e) {
|
|
600
|
+
throw new DiscordError(
|
|
601
|
+
0,
|
|
602
|
+
redact(`Wheelhouse could not reach Discord to ${what}: ${e.message}`)
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
if (res.status === 429 && attempt < 2) {
|
|
606
|
+
const j = await res.json().catch(() => ({}));
|
|
607
|
+
await sleep(Math.min(10, Math.max(0.1, Number(j.retry_after) || 1)) * 1e3);
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
const text2 = await res.text().catch(() => "");
|
|
611
|
+
if (!res.ok) {
|
|
612
|
+
let said = "";
|
|
613
|
+
try {
|
|
614
|
+
said = str(JSON.parse(text2).message) ?? "";
|
|
615
|
+
} catch {
|
|
616
|
+
}
|
|
617
|
+
throw new DiscordError(
|
|
618
|
+
res.status,
|
|
619
|
+
redact(`Discord would not ${what} (${res.status}${said ? `: ${said}` : ""}).`)
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
return text2 ? JSON.parse(text2) : void 0;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
async function checkDiscordToken(token, opts = {}) {
|
|
626
|
+
const transport = opts.transport ?? wsTransport;
|
|
627
|
+
const version2 = opts.version ?? "0";
|
|
628
|
+
try {
|
|
629
|
+
const me = await call(
|
|
630
|
+
transport,
|
|
631
|
+
token,
|
|
632
|
+
version2,
|
|
633
|
+
"GET",
|
|
634
|
+
"/users/@me",
|
|
635
|
+
"say who the bot is"
|
|
636
|
+
);
|
|
637
|
+
const id = str(me?.id);
|
|
638
|
+
if (!id) return { ok: false, error: "Discord did not say who the bot is." };
|
|
639
|
+
const app = await call(
|
|
640
|
+
transport,
|
|
641
|
+
token,
|
|
642
|
+
version2,
|
|
643
|
+
"GET",
|
|
644
|
+
"/applications/@me",
|
|
645
|
+
"describe the application"
|
|
646
|
+
).catch(() => null);
|
|
647
|
+
return {
|
|
648
|
+
ok: true,
|
|
649
|
+
bot: { id, name: str(me.global_name) ?? str(me.username) ?? id },
|
|
650
|
+
appId: str(app?.id) ?? id,
|
|
651
|
+
publicBot: typeof app?.bot_public === "boolean" ? app.bot_public : null
|
|
652
|
+
};
|
|
653
|
+
} catch (e) {
|
|
654
|
+
const status = e instanceof DiscordError ? e.status : 0;
|
|
655
|
+
if (status === 401)
|
|
656
|
+
return {
|
|
657
|
+
ok: false,
|
|
658
|
+
error: "Discord refused this token: it is not a valid bot token, or it was reset."
|
|
659
|
+
};
|
|
660
|
+
return { ok: false, error: e.message };
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
var inviteUrl = (appId) => `https://discord.com/oauth2/authorize?client_id=${encodeURIComponent(appId)}&scope=bot&permissions=0`;
|
|
664
|
+
|
|
665
|
+
// src/lib/server/gateway/keychain.ts
|
|
666
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
667
|
+
var KEYCHAIN_SERVICE = "wheelhouse-gateway";
|
|
668
|
+
var SECURITY = "/usr/bin/security";
|
|
669
|
+
var NOT_FOUND = 44;
|
|
670
|
+
function security(args, input = "") {
|
|
671
|
+
return new Promise((resolve, reject) => {
|
|
672
|
+
const child = spawn2(SECURITY, args, { stdio: ["pipe", "pipe", "ignore"] });
|
|
673
|
+
let stdout = "";
|
|
674
|
+
child.stdout.setEncoding("utf8").on("data", (d) => stdout += d);
|
|
675
|
+
child.on("error", reject);
|
|
676
|
+
child.on("close", (code) => resolve({ code: code ?? -1, stdout }));
|
|
677
|
+
child.stdin.on("error", () => {
|
|
678
|
+
});
|
|
679
|
+
child.stdin.end(input);
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
function macKeychain(service = KEYCHAIN_SERVICE) {
|
|
683
|
+
const failed = (what, code) => new Error(`the macOS Keychain could not ${what} (security exited ${code})`);
|
|
684
|
+
return {
|
|
685
|
+
where: `the macOS Keychain (${service})`,
|
|
686
|
+
async get(account) {
|
|
687
|
+
const r = await security(["find-generic-password", "-s", service, "-a", account, "-w"]);
|
|
688
|
+
if (r.code === NOT_FOUND) return null;
|
|
689
|
+
if (r.code !== 0) throw failed("read it", r.code);
|
|
690
|
+
return r.stdout.trim() || null;
|
|
691
|
+
},
|
|
692
|
+
async set(account, secret) {
|
|
693
|
+
if (!/^[A-Za-z0-9._-]+$/.test(secret))
|
|
694
|
+
throw new Error("not a secret the Keychain line can hold");
|
|
695
|
+
const r = await security(
|
|
696
|
+
["-i"],
|
|
697
|
+
`add-generic-password -U -s ${service} -a ${account} -w ${secret}
|
|
698
|
+
`
|
|
699
|
+
);
|
|
700
|
+
if (r.code !== 0) throw failed("keep it", r.code);
|
|
701
|
+
},
|
|
702
|
+
async remove(account) {
|
|
703
|
+
const r = await security(["delete-generic-password", "-s", service, "-a", account]);
|
|
704
|
+
if (r.code === NOT_FOUND) return false;
|
|
705
|
+
if (r.code !== 0) throw failed("remove it", r.code);
|
|
706
|
+
return true;
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
function defaultKeychain() {
|
|
711
|
+
return process.platform === "darwin" ? macKeychain() : null;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// src/lib/server/gateway/settings.ts
|
|
715
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
716
|
+
import {
|
|
717
|
+
chmod,
|
|
718
|
+
lstat,
|
|
719
|
+
mkdir as mkdir3,
|
|
720
|
+
readFile as readFile3,
|
|
721
|
+
realpath,
|
|
722
|
+
rename as rename3,
|
|
723
|
+
rm,
|
|
724
|
+
stat as stat3,
|
|
725
|
+
writeFile as writeFile3
|
|
726
|
+
} from "node:fs/promises";
|
|
727
|
+
import path5 from "node:path";
|
|
728
|
+
|
|
729
|
+
// src/lib/bell.ts
|
|
730
|
+
var ALERT_KINDS = [
|
|
731
|
+
{
|
|
732
|
+
kind: "pr-green",
|
|
733
|
+
label: "PR green",
|
|
734
|
+
hint: "A pull request passed its checks and waits on you"
|
|
735
|
+
},
|
|
736
|
+
{ kind: "question", label: "Question waiting", hint: "Firstmate needs your decision" },
|
|
737
|
+
{ kind: "stuck", label: "Worker stuck", hint: "Blocked, failed, lost contact or gone quiet" },
|
|
738
|
+
{ kind: "landed", label: "Merge landed", hint: "A pull request was merged" },
|
|
739
|
+
{ kind: "issue", label: "New issue", hint: "A GitHub issue you watch, with the bell on" }
|
|
740
|
+
];
|
|
741
|
+
var QUIET_AFTER_MS = 20 * 6e4;
|
|
742
|
+
|
|
743
|
+
// src/lib/server/gateway/settings.ts
|
|
744
|
+
var GATEWAY_SETTINGS_SCHEMA = "wheelhouse-gateway-settings.v1";
|
|
745
|
+
var SETTINGS_FILE = "settings.json";
|
|
746
|
+
var gatewayDir = (configDir) => path5.join(configDir, "gateway");
|
|
747
|
+
function defaultSettings() {
|
|
748
|
+
return Object.fromEntries(
|
|
749
|
+
GATEWAY_CHANNELS.map((c) => [
|
|
750
|
+
c.channel,
|
|
751
|
+
{ enabled: true, paired: null, alerts: [...DEFAULT_GATEWAY_ALERTS], titlesOnly: false }
|
|
752
|
+
])
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
function cleanToken(raw) {
|
|
756
|
+
const t = raw.trim().replace(/^Bot\s+/i, "");
|
|
757
|
+
if (t.length < 50 || t.length > 200) return null;
|
|
758
|
+
return /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(t) ? t : null;
|
|
759
|
+
}
|
|
760
|
+
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
761
|
+
var str2 = (v) => typeof v === "string" && v ? v : null;
|
|
762
|
+
var KINDS = new Set(ALERT_KINDS.map((k) => k.kind));
|
|
763
|
+
function parsePeer(v) {
|
|
764
|
+
if (!isObj(v)) return null;
|
|
765
|
+
const id = str2(v.id);
|
|
766
|
+
if (!id || !/^\d{1,32}$/.test(id)) return null;
|
|
767
|
+
return { id, name: str2(v.name) ?? id, dm: str2(v.dm), at: str2(v.at) ?? (/* @__PURE__ */ new Date(0)).toISOString() };
|
|
768
|
+
}
|
|
769
|
+
function parseChannel(v, fallback) {
|
|
770
|
+
if (!isObj(v)) return fallback;
|
|
771
|
+
return {
|
|
772
|
+
enabled: typeof v.enabled === "boolean" ? v.enabled : fallback.enabled,
|
|
773
|
+
paired: parsePeer(v.paired),
|
|
774
|
+
alerts: Array.isArray(v.alerts) ? v.alerts.filter((k) => KINDS.has(k)) : fallback.alerts,
|
|
775
|
+
titlesOnly: typeof v.titlesOnly === "boolean" ? v.titlesOnly : fallback.titlesOnly
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
async function writeAtomic(file, text2) {
|
|
779
|
+
await mkdir3(path5.dirname(file), { recursive: true, mode: 448 });
|
|
780
|
+
const tmp = `${file}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
781
|
+
try {
|
|
782
|
+
await writeFile3(tmp, text2, { mode: 384 });
|
|
783
|
+
await rename3(tmp, file);
|
|
784
|
+
} catch (e) {
|
|
785
|
+
await rm(tmp, { force: true });
|
|
786
|
+
throw e;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
var GatewaySettings = class {
|
|
790
|
+
constructor(dir, keychain = null) {
|
|
791
|
+
this.dir = dir;
|
|
792
|
+
this.keychain = keychain;
|
|
793
|
+
}
|
|
794
|
+
dir;
|
|
795
|
+
keychain;
|
|
796
|
+
get file() {
|
|
797
|
+
return path5.join(this.dir, SETTINGS_FILE);
|
|
798
|
+
}
|
|
799
|
+
tokenFile(channel) {
|
|
800
|
+
return path5.join(this.dir, `${channel}.token`);
|
|
801
|
+
}
|
|
802
|
+
/** The settings, with defaults for anything missing; an unreadable file reads as the defaults, with why. */
|
|
803
|
+
async read() {
|
|
804
|
+
const settings2 = defaultSettings();
|
|
805
|
+
let text2;
|
|
806
|
+
try {
|
|
807
|
+
text2 = await readFile3(this.file, "utf8");
|
|
808
|
+
} catch (e) {
|
|
809
|
+
if (e.code === "ENOENT") return { settings: settings2, error: null };
|
|
810
|
+
return { settings: settings2, error: `${this.file} could not be read: ${e.message}` };
|
|
811
|
+
}
|
|
812
|
+
let parsed;
|
|
813
|
+
try {
|
|
814
|
+
parsed = JSON.parse(text2);
|
|
815
|
+
} catch {
|
|
816
|
+
return { settings: settings2, error: `${this.file} is not valid JSON.` };
|
|
817
|
+
}
|
|
818
|
+
if (!isObj(parsed) || parsed.schema !== GATEWAY_SETTINGS_SCHEMA) {
|
|
819
|
+
return { settings: settings2, error: `${this.file} is not a Wheelhouse gateway settings file.` };
|
|
820
|
+
}
|
|
821
|
+
for (const { channel } of GATEWAY_CHANNELS) {
|
|
822
|
+
settings2[channel] = parseChannel(parsed[channel], settings2[channel]);
|
|
823
|
+
}
|
|
824
|
+
return { settings: settings2, error: null };
|
|
825
|
+
}
|
|
826
|
+
/** Change the settings and write them; an unreadable file is replaced with the change applied to the defaults. */
|
|
827
|
+
async update(change) {
|
|
828
|
+
const { settings: settings2 } = await this.read();
|
|
829
|
+
change(settings2);
|
|
830
|
+
await writeAtomic(
|
|
831
|
+
this.file,
|
|
832
|
+
JSON.stringify({ schema: GATEWAY_SETTINGS_SCHEMA, ...settings2 }, null, " ") + "\n"
|
|
833
|
+
);
|
|
834
|
+
return settings2;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* The channel's bot token and where it is kept, or null when none is set:
|
|
838
|
+
* the Keychain first, then the file. A token file that is not a plain file
|
|
839
|
+
* is ignored.
|
|
840
|
+
*/
|
|
841
|
+
async find(channel) {
|
|
842
|
+
const kept = this.keychain ? await this.keychain.get(await this.keychainAccount(channel)).catch(() => null) : null;
|
|
843
|
+
const fromKeychain = kept ? cleanToken(kept) : null;
|
|
844
|
+
if (fromKeychain) return { token: fromKeychain, where: this.keychain.where };
|
|
845
|
+
const file = this.tokenFile(channel);
|
|
846
|
+
try {
|
|
847
|
+
const s = await lstat(file);
|
|
848
|
+
if (!s.isFile()) return null;
|
|
849
|
+
if ((s.mode & 63) !== 0) await chmod(file, 384);
|
|
850
|
+
const token = cleanToken(await readFile3(file, "utf8"));
|
|
851
|
+
return token ? { token, where: file } : null;
|
|
852
|
+
} catch {
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
async token(channel) {
|
|
857
|
+
return (await this.find(channel))?.token ?? null;
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* The Keychain account for the channel's token in this directory: the
|
|
861
|
+
* channel and a digest of the directory's real path, so each config
|
|
862
|
+
* directory has its own token.
|
|
863
|
+
*/
|
|
864
|
+
async keychainAccount(channel) {
|
|
865
|
+
const real2 = await realpath(this.dir).catch(() => path5.resolve(this.dir));
|
|
866
|
+
return `${channel}:${createHash("sha256").update(real2).digest("hex").slice(0, 16)}`;
|
|
867
|
+
}
|
|
868
|
+
/** Keep the token in the Keychain, else in its file, replacing the one before; returns where it went. */
|
|
869
|
+
async setToken(channel, token) {
|
|
870
|
+
await mkdir3(this.dir, { recursive: true, mode: 448 });
|
|
871
|
+
const keychain = this.keychain;
|
|
872
|
+
const account = await this.keychainAccount(channel);
|
|
873
|
+
const kept = keychain ? await keychain.set(account, token).then(
|
|
874
|
+
() => true,
|
|
875
|
+
() => false
|
|
876
|
+
) : false;
|
|
877
|
+
if (kept) {
|
|
878
|
+
await rm(this.tokenFile(channel), { force: true });
|
|
879
|
+
} else {
|
|
880
|
+
await keychain?.remove(account).catch(() => false);
|
|
881
|
+
await writeAtomic(this.tokenFile(channel), `${token}
|
|
882
|
+
`);
|
|
883
|
+
await chmod(this.dir, 448).catch(() => {
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
await this.update(() => {
|
|
887
|
+
});
|
|
888
|
+
return kept ? keychain.where : this.tokenFile(channel);
|
|
889
|
+
}
|
|
890
|
+
/** Changes whenever a file is written (setting a Keychain token rewrites the settings), so the server can pick up what the CLI changed. */
|
|
891
|
+
async stamp() {
|
|
892
|
+
const one = async (f) => {
|
|
893
|
+
try {
|
|
894
|
+
const s = await stat3(f);
|
|
895
|
+
return `${s.mtimeMs}:${s.size}:${s.ino}`;
|
|
896
|
+
} catch {
|
|
897
|
+
return "-";
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
const files = [this.file, ...GATEWAY_CHANNELS.map((c) => this.tokenFile(c.channel))];
|
|
901
|
+
return (await Promise.all(files.map(one))).join("|");
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
|
|
905
|
+
// cli/gateway.ts
|
|
906
|
+
var CHANNELS = GATEWAY_CHANNELS.map((c) => c.channel);
|
|
907
|
+
var labelOf = (c) => GATEWAY_CHANNELS.find((x) => x.channel === c).label;
|
|
908
|
+
function parseChannel2(v) {
|
|
909
|
+
if (!CHANNELS.includes(v)) {
|
|
910
|
+
throw new InvalidArgumentError(`Channels: ${CHANNELS.join(", ")}.`);
|
|
911
|
+
}
|
|
912
|
+
return v;
|
|
913
|
+
}
|
|
914
|
+
function parsePort(value) {
|
|
915
|
+
const n = Number(value);
|
|
916
|
+
if (!Number.isInteger(n) || n < 1 || n > 65535) {
|
|
917
|
+
throw new InvalidArgumentError("Port must be a whole number from 1 to 65535.");
|
|
918
|
+
}
|
|
919
|
+
return n;
|
|
920
|
+
}
|
|
921
|
+
async function readSecret(question2) {
|
|
922
|
+
const input = process.stdin;
|
|
923
|
+
if (!input.isTTY) {
|
|
924
|
+
const chunks = [];
|
|
925
|
+
for await (const c of input) chunks.push(Buffer.from(c));
|
|
926
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
927
|
+
}
|
|
928
|
+
process.stdout.write(question2);
|
|
929
|
+
input.setRawMode(true);
|
|
930
|
+
input.setEncoding("utf8");
|
|
931
|
+
input.resume();
|
|
932
|
+
return new Promise((resolve, reject) => {
|
|
933
|
+
let value = "";
|
|
934
|
+
const stop = () => {
|
|
935
|
+
input.off("data", onData);
|
|
936
|
+
input.setRawMode(false);
|
|
937
|
+
input.pause();
|
|
938
|
+
process.stdout.write("\n");
|
|
939
|
+
};
|
|
940
|
+
const onData = (s) => {
|
|
941
|
+
for (const ch of s) {
|
|
942
|
+
if (ch === "\r" || ch === "\n") {
|
|
943
|
+
stop();
|
|
944
|
+
return resolve(value);
|
|
945
|
+
}
|
|
946
|
+
if (ch === "" || ch === "") {
|
|
947
|
+
stop();
|
|
948
|
+
return reject(new Error("cancelled"));
|
|
949
|
+
}
|
|
950
|
+
if (ch === "\x7F" || ch === "\b") value = value.slice(0, -1);
|
|
951
|
+
else value += ch;
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
input.on("data", onData);
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
async function liveView(port) {
|
|
958
|
+
try {
|
|
959
|
+
const r = await fetch(`http://127.0.0.1:${port}/api/gateway`, {
|
|
960
|
+
signal: AbortSignal.timeout(1500)
|
|
961
|
+
});
|
|
962
|
+
return r.ok ? await r.json() : null;
|
|
963
|
+
} catch {
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
function gatewayCommand(over = {}) {
|
|
968
|
+
const deps = {
|
|
969
|
+
env: process.env,
|
|
970
|
+
readSecret,
|
|
971
|
+
check: (token) => checkDiscordToken(token, { version }),
|
|
972
|
+
keychain: defaultKeychain(),
|
|
973
|
+
live: liveView,
|
|
974
|
+
log: (line) => console.log(line),
|
|
975
|
+
error: (line) => console.error(line),
|
|
976
|
+
...over
|
|
977
|
+
};
|
|
978
|
+
const cmd = new Command("gateway").description("Reach Firstmate from your phone: set up the chat channels (Discord).").option(
|
|
979
|
+
"--config-dir <dir>",
|
|
980
|
+
"the Wheelhouse config directory (default: $WHEELHOUSE_CONFIG_DIR, else $XDG_CONFIG_HOME/wheelhouse, else ~/.config/wheelhouse)"
|
|
981
|
+
);
|
|
982
|
+
const settingsFor = (c) => {
|
|
983
|
+
const dir = c.optsWithGlobals().configDir;
|
|
984
|
+
const configDir = dir ? path6.resolve(dir) : defaultConfigDir(deps.env);
|
|
985
|
+
const found = resolveInstall({ cwd: process.cwd(), env: deps.env });
|
|
986
|
+
if (found.ok && isInside(configDir, found.install.homeDir)) {
|
|
987
|
+
deps.error(
|
|
988
|
+
`wheelhouse: the config directory ${configDir} is inside the Firstmate home, where the crew can read it. Pass --config-dir <dir> outside it.`
|
|
989
|
+
);
|
|
990
|
+
process.exitCode = 1;
|
|
991
|
+
return null;
|
|
992
|
+
}
|
|
993
|
+
return new GatewaySettings(gatewayDir(configDir), deps.keychain);
|
|
994
|
+
};
|
|
995
|
+
const fail = (line) => {
|
|
996
|
+
deps.error(`wheelhouse: ${line}`);
|
|
997
|
+
process.exitCode = 1;
|
|
998
|
+
};
|
|
999
|
+
cmd.command("connect <channel>").description(
|
|
1000
|
+
"Set the bot token for a channel (asked for here, never taken from the command line)."
|
|
1001
|
+
).action(async (channel, _o, c) => {
|
|
1002
|
+
const ch = parseChannel2(channel);
|
|
1003
|
+
const settings2 = settingsFor(c);
|
|
1004
|
+
if (!settings2) return;
|
|
1005
|
+
let raw;
|
|
1006
|
+
try {
|
|
1007
|
+
raw = await deps.readSecret(`${labelOf(ch)} bot token (input hidden): `);
|
|
1008
|
+
} catch {
|
|
1009
|
+
return fail("cancelled; nothing was changed.");
|
|
1010
|
+
}
|
|
1011
|
+
const token = cleanToken(raw);
|
|
1012
|
+
if (!token) {
|
|
1013
|
+
return fail(
|
|
1014
|
+
"that does not look like a bot token. Copy it from the Bot page of your application in the Discord Developer Portal (Reset Token)."
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
const checked = await deps.check(token);
|
|
1018
|
+
if (!checked.ok) return fail(`${checked.error} Nothing was changed.`);
|
|
1019
|
+
const where = await settings2.setToken(ch, token);
|
|
1020
|
+
deps.log(`Saved the ${labelOf(ch)} bot token in ${where} (readable by you only).`);
|
|
1021
|
+
deps.log(`Bot: ${checked.bot.name} (id ${checked.bot.id}).`);
|
|
1022
|
+
if (checked.publicBot) {
|
|
1023
|
+
deps.log(
|
|
1024
|
+
'Warning: "Public Bot" is on, so anyone with its link can add it to their server. Turn it off on the Bot page.'
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
deps.log(
|
|
1028
|
+
`Add it to a private server you are in (it needs no permissions): ${inviteUrl(checked.appId)}`
|
|
1029
|
+
);
|
|
1030
|
+
deps.log("Next: in Wheelhouse, open Settings \u2192 Gateway and pair your account.");
|
|
1031
|
+
});
|
|
1032
|
+
for (const [name, on] of [
|
|
1033
|
+
["on", true],
|
|
1034
|
+
["off", false]
|
|
1035
|
+
]) {
|
|
1036
|
+
cmd.command(`${name} [channel]`).description(
|
|
1037
|
+
on ? "Turn a channel on (default: discord)." : "Turn a channel off: it neither reads nor sends (default: discord)."
|
|
1038
|
+
).action(async (channel, _o, c) => {
|
|
1039
|
+
const ch = parseChannel2(channel ?? "discord");
|
|
1040
|
+
const settings2 = settingsFor(c);
|
|
1041
|
+
if (!settings2) return;
|
|
1042
|
+
await settings2.update((s) => s[ch].enabled = on);
|
|
1043
|
+
deps.log(`${labelOf(ch)} is turned ${name}.`);
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
cmd.command("unbind [channel]").description(
|
|
1047
|
+
"Forget the paired account: no one\u2019s messages reach Firstmate until you pair again (default: discord)."
|
|
1048
|
+
).action(async (channel, _o, c) => {
|
|
1049
|
+
const ch = parseChannel2(channel ?? "discord");
|
|
1050
|
+
const settings2 = settingsFor(c);
|
|
1051
|
+
if (!settings2) return;
|
|
1052
|
+
const { settings: before } = await settings2.read();
|
|
1053
|
+
await settings2.update((s) => s[ch].paired = null);
|
|
1054
|
+
deps.log(
|
|
1055
|
+
before[ch].paired ? `Unbound ${before[ch].paired.name} from ${labelOf(ch)}.` : `No account was bound to ${labelOf(ch)}.`
|
|
1056
|
+
);
|
|
1057
|
+
});
|
|
1058
|
+
cmd.command("titles-only").addArgument(new Argument("<state>", "on or off").choices(["on", "off"])).argument("[channel]").description(
|
|
1059
|
+
"Send alerts and decisions as their titles only, without their text, or in full again (default: discord)."
|
|
1060
|
+
).action(async (state, channel, _o, c) => {
|
|
1061
|
+
const ch = parseChannel2(channel ?? "discord");
|
|
1062
|
+
const settings2 = settingsFor(c);
|
|
1063
|
+
if (!settings2) return;
|
|
1064
|
+
await settings2.update((s) => s[ch].titlesOnly = state === "on");
|
|
1065
|
+
deps.log(
|
|
1066
|
+
state === "on" ? `${labelOf(ch)} sends alerts and decisions as titles only.` : `${labelOf(ch)} sends alerts and decisions in full.`
|
|
1067
|
+
);
|
|
1068
|
+
});
|
|
1069
|
+
cmd.command("status").description(
|
|
1070
|
+
"Show each channel: its token, whether it is on, who is paired, and the connection."
|
|
1071
|
+
).option("--port <n>", "the port the running Wheelhouse listens on", parsePort, 4310).action(async (o, c) => {
|
|
1072
|
+
const settings2 = settingsFor(c);
|
|
1073
|
+
if (!settings2) return;
|
|
1074
|
+
const { settings: s, error } = await settings2.read();
|
|
1075
|
+
if (error) deps.log(`Warning: ${error}`);
|
|
1076
|
+
const live = await deps.live(o.port);
|
|
1077
|
+
for (const { channel, label: label2 } of GATEWAY_CHANNELS) {
|
|
1078
|
+
const cs = s[channel];
|
|
1079
|
+
const token = await settings2.find(channel);
|
|
1080
|
+
const lv = live?.channels.find((x) => x.channel === channel);
|
|
1081
|
+
deps.log(label2);
|
|
1082
|
+
deps.log(
|
|
1083
|
+
` Bot token: ${token ? `set (${token.where})` : `not set (wheelhouse gateway connect ${channel})`}`
|
|
1084
|
+
);
|
|
1085
|
+
deps.log(` Turned: ${cs.enabled ? "on" : "off"}`);
|
|
1086
|
+
deps.log(
|
|
1087
|
+
` Paired: ${cs.paired ? `${cs.paired.name} (id ${cs.paired.id}), since ${cs.paired.at.slice(0, 10)}` : "no one (pair in Settings \u2192 Gateway)"}`
|
|
1088
|
+
);
|
|
1089
|
+
deps.log(` Alerts: ${cs.alerts.length ? cs.alerts.join(", ") : "none"}`);
|
|
1090
|
+
deps.log(` Titles only: ${cs.titlesOnly ? "on" : "off"}`);
|
|
1091
|
+
deps.log(
|
|
1092
|
+
` Connection: ${lv ? `${lv.state}${lv.bot && lv.state === "connected" ? ` as ${lv.bot.name}` : ""}${lv.reason ? ` (${lv.reason})` : ""}` : `no Wheelhouse answers on port ${o.port}; the gateway runs only while Wheelhouse does`}`
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
return cmd;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// cli/plugin.ts
|
|
1100
|
+
import { createInterface } from "node:readline/promises";
|
|
1101
|
+
import path10 from "node:path";
|
|
1102
|
+
import { Command as Command2 } from "commander";
|
|
1103
|
+
|
|
1104
|
+
// src/lib/server/plugins/install.ts
|
|
1105
|
+
import { execFile } from "node:child_process";
|
|
1106
|
+
import { mkdir as mkdir4, mkdtemp, readFile as readFile4, realpath as realpath2, rename as rename4, rm as rm2, stat as stat4 } from "node:fs/promises";
|
|
1107
|
+
import path8 from "node:path";
|
|
1108
|
+
|
|
1109
|
+
// src/lib/server/plugins/manifest.ts
|
|
1110
|
+
import path7 from "node:path";
|
|
1111
|
+
|
|
1112
|
+
// src/lib/plugins/addresses.ts
|
|
1113
|
+
var TEMPLATE = /^[a-z0-9-]*\{port\}[a-z0-9-]*(?:\.[a-z0-9-]+)*\.localhost$/;
|
|
1114
|
+
function validHostTemplate(v) {
|
|
1115
|
+
return typeof v === "string" && v.length <= 100 && TEMPLATE.test(v);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// src/lib/plugins/types.ts
|
|
1119
|
+
var SLOTS = [
|
|
1120
|
+
"project.tab",
|
|
1121
|
+
"harbour.card",
|
|
1122
|
+
"task.section",
|
|
1123
|
+
"step.evidence",
|
|
1124
|
+
"bridge.card",
|
|
1125
|
+
"home.card",
|
|
1126
|
+
"header.indicator",
|
|
1127
|
+
"nav.page"
|
|
1128
|
+
];
|
|
1129
|
+
|
|
1130
|
+
// src/lib/server/plugins/types.ts
|
|
1131
|
+
var PLUGIN_API_VERSION = 1;
|
|
1132
|
+
var SUPPORTED_API_VERSIONS = [PLUGIN_API_VERSION, PLUGIN_API_VERSION - 1].filter(
|
|
1133
|
+
(v) => v >= 1
|
|
1134
|
+
);
|
|
1135
|
+
|
|
1136
|
+
// src/lib/server/plugins/manifest.ts
|
|
1137
|
+
var MANIFEST_DIR = ".wheelhouse-plugin";
|
|
1138
|
+
var MANIFEST_FILE = "plugin.json";
|
|
1139
|
+
var manifestPath = (pluginDir) => path7.join(pluginDir, MANIFEST_DIR, MANIFEST_FILE);
|
|
1140
|
+
var MANIFEST_FIELDS = [
|
|
1141
|
+
"$schema",
|
|
1142
|
+
"name",
|
|
1143
|
+
"displayName",
|
|
1144
|
+
"version",
|
|
1145
|
+
"description",
|
|
1146
|
+
"author",
|
|
1147
|
+
"repository",
|
|
1148
|
+
"homepage",
|
|
1149
|
+
"license",
|
|
1150
|
+
"keywords",
|
|
1151
|
+
"wheelhouse"
|
|
1152
|
+
];
|
|
1153
|
+
var WHEELHOUSE_FIELDS = [
|
|
1154
|
+
"apiVersion",
|
|
1155
|
+
"server",
|
|
1156
|
+
"ui",
|
|
1157
|
+
"enabledByDefault",
|
|
1158
|
+
"enabledWhenFile",
|
|
1159
|
+
"enabledWhenHomeFile",
|
|
1160
|
+
"contributions",
|
|
1161
|
+
"services",
|
|
1162
|
+
"settings",
|
|
1163
|
+
"mcpServers",
|
|
1164
|
+
"addressRewrites",
|
|
1165
|
+
"hostRoutes"
|
|
1166
|
+
];
|
|
1167
|
+
var SETTING_FIELDS = [
|
|
1168
|
+
"title",
|
|
1169
|
+
"description",
|
|
1170
|
+
"type",
|
|
1171
|
+
"required",
|
|
1172
|
+
"default",
|
|
1173
|
+
"options"
|
|
1174
|
+
];
|
|
1175
|
+
var SETTING_TYPES = ["text", "number", "boolean", "list"];
|
|
1176
|
+
var ID = /^[a-z][a-z0-9-]{0,39}$/;
|
|
1177
|
+
var SETTING_KEY = /^[A-Za-z][A-Za-z0-9_]{0,39}$/;
|
|
1178
|
+
var MAX_SETTINGS = 20;
|
|
1179
|
+
var MAX_OPTIONS = 20;
|
|
1180
|
+
var MAX_LIST = 50;
|
|
1181
|
+
var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
1182
|
+
var MAX_TEXT = 400;
|
|
1183
|
+
var isRecord3 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1184
|
+
function insidePath(f) {
|
|
1185
|
+
return typeof f === "string" && f.length > 0 && !path7.isAbsolute(f) && !f.split(/[\\/]/).some((part) => part === ".." || part === "" || part === ".");
|
|
1186
|
+
}
|
|
1187
|
+
var Invalid = class extends Error {
|
|
1188
|
+
};
|
|
1189
|
+
function text(v, where, optional = false) {
|
|
1190
|
+
if (v === void 0 && optional) return null;
|
|
1191
|
+
if (typeof v !== "string" || !v.trim()) throw new Invalid(`${where} must be text`);
|
|
1192
|
+
if (v.length > MAX_TEXT) throw new Invalid(`${where} is longer than ${MAX_TEXT} characters`);
|
|
1193
|
+
return v;
|
|
1194
|
+
}
|
|
1195
|
+
function only(o, where, keys) {
|
|
1196
|
+
for (const k of Object.keys(o)) {
|
|
1197
|
+
if (!keys.includes(k)) throw new Invalid(`unknown field "${where ? `${where}.` : ""}${k}"`);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
function entry(v, where) {
|
|
1201
|
+
if (!insidePath(v)) throw new Invalid(`${where} must be a plain path inside the plugin`);
|
|
1202
|
+
if (!/\.m?js$/.test(v)) throw new Invalid(`${where} must be a JavaScript module (.js or .mjs)`);
|
|
1203
|
+
return v;
|
|
1204
|
+
}
|
|
1205
|
+
function author(v) {
|
|
1206
|
+
if (v === void 0) return null;
|
|
1207
|
+
if (typeof v === "string") return { name: text(v, "author") };
|
|
1208
|
+
if (!isRecord3(v)) throw new Invalid("author must be text or an object");
|
|
1209
|
+
only(v, "author", ["name", "email", "url"]);
|
|
1210
|
+
const a = { name: text(v.name, "author.name") };
|
|
1211
|
+
const email = text(v.email, "author.email", true);
|
|
1212
|
+
const url = text(v.url, "author.url", true);
|
|
1213
|
+
if (email) a.email = email;
|
|
1214
|
+
if (url) a.url = url;
|
|
1215
|
+
return a;
|
|
1216
|
+
}
|
|
1217
|
+
function wheelhouse(v) {
|
|
1218
|
+
if (!isRecord3(v)) throw new Invalid('"wheelhouse" must be an object');
|
|
1219
|
+
only(v, "wheelhouse", WHEELHOUSE_FIELDS);
|
|
1220
|
+
if (typeof v.apiVersion !== "number" || !Number.isInteger(v.apiVersion)) {
|
|
1221
|
+
throw new Invalid("wheelhouse.apiVersion must be a whole number");
|
|
1222
|
+
}
|
|
1223
|
+
if (!SUPPORTED_API_VERSIONS.includes(v.apiVersion)) {
|
|
1224
|
+
throw new Invalid(
|
|
1225
|
+
`it was written for plugin contract ${v.apiVersion}; this Wheelhouse supports ${SUPPORTED_API_VERSIONS.join(" and ")}`
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
const w = {
|
|
1229
|
+
apiVersion: v.apiVersion,
|
|
1230
|
+
server: entry(v.server, "wheelhouse.server"),
|
|
1231
|
+
ui: v.ui === void 0 ? null : entry(v.ui, "wheelhouse.ui"),
|
|
1232
|
+
contributions: [],
|
|
1233
|
+
services: [],
|
|
1234
|
+
settings: settings(v.settings),
|
|
1235
|
+
mcpServers: [],
|
|
1236
|
+
addressRewrites: hosts(v.addressRewrites, "addressRewrites", "to"),
|
|
1237
|
+
hostRoutes: hosts(v.hostRoutes, "hostRoutes", "host")
|
|
1238
|
+
};
|
|
1239
|
+
if (v.enabledByDefault !== void 0) {
|
|
1240
|
+
if (typeof v.enabledByDefault !== "boolean") {
|
|
1241
|
+
throw new Invalid("wheelhouse.enabledByDefault must be true or false");
|
|
1242
|
+
}
|
|
1243
|
+
w.enabledByDefault = v.enabledByDefault;
|
|
1244
|
+
}
|
|
1245
|
+
for (const k of ["enabledWhenFile", "enabledWhenHomeFile"]) {
|
|
1246
|
+
if (v[k] === void 0) continue;
|
|
1247
|
+
if (!insidePath(v[k])) throw new Invalid(`wheelhouse.${k} must be a plain relative path`);
|
|
1248
|
+
w[k] = v[k];
|
|
1249
|
+
}
|
|
1250
|
+
if (!Array.isArray(v.contributions)) {
|
|
1251
|
+
throw new Invalid("wheelhouse.contributions must be a list");
|
|
1252
|
+
}
|
|
1253
|
+
for (const [i, c] of v.contributions.entries()) {
|
|
1254
|
+
const where = `wheelhouse.contributions[${i}]`;
|
|
1255
|
+
if (!isRecord3(c)) throw new Invalid(`${where} must be an object`);
|
|
1256
|
+
only(c, where, ["id", "slot"]);
|
|
1257
|
+
if (typeof c.id !== "string" || !ID.test(c.id)) {
|
|
1258
|
+
throw new Invalid(`${where}.id must be lower-case letters, digits and dashes`);
|
|
1259
|
+
}
|
|
1260
|
+
if (!SLOTS.includes(c.slot)) {
|
|
1261
|
+
throw new Invalid(`${where}.slot must be one of ${SLOTS.join(", ")}`);
|
|
1262
|
+
}
|
|
1263
|
+
if (w.contributions.some((x) => x.id === c.id)) {
|
|
1264
|
+
throw new Invalid(`it declares contribution "${c.id}" twice`);
|
|
1265
|
+
}
|
|
1266
|
+
w.contributions.push({ id: c.id, slot: c.slot });
|
|
1267
|
+
}
|
|
1268
|
+
if (v.services !== void 0) {
|
|
1269
|
+
if (!Array.isArray(v.services)) throw new Invalid("wheelhouse.services must be a list");
|
|
1270
|
+
for (const [i, s] of v.services.entries()) {
|
|
1271
|
+
const where = `wheelhouse.services[${i}]`;
|
|
1272
|
+
if (!isRecord3(s)) throw new Invalid(`${where} must be an object`);
|
|
1273
|
+
only(s, where, ["id", "scope"]);
|
|
1274
|
+
if (typeof s.id !== "string" || !ID.test(s.id)) {
|
|
1275
|
+
throw new Invalid(`${where}.id must be lower-case letters, digits and dashes`);
|
|
1276
|
+
}
|
|
1277
|
+
if (s.scope !== "fleet" && s.scope !== "project") {
|
|
1278
|
+
throw new Invalid(`${where}.scope must be "fleet" or "project"`);
|
|
1279
|
+
}
|
|
1280
|
+
if (w.services.some((x) => x.id === s.id)) {
|
|
1281
|
+
throw new Invalid(`it declares service "${s.id}" twice`);
|
|
1282
|
+
}
|
|
1283
|
+
w.services.push({ id: s.id, scope: s.scope });
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
if (v.mcpServers !== void 0) {
|
|
1287
|
+
if (!Array.isArray(v.mcpServers)) throw new Invalid("wheelhouse.mcpServers must be a list");
|
|
1288
|
+
for (const [i, s] of v.mcpServers.entries()) {
|
|
1289
|
+
const where = `wheelhouse.mcpServers[${i}]`;
|
|
1290
|
+
if (!isRecord3(s)) throw new Invalid(`${where} must be an object`);
|
|
1291
|
+
only(s, where, ["id", "service"]);
|
|
1292
|
+
if (typeof s.id !== "string" || !ID.test(s.id)) {
|
|
1293
|
+
throw new Invalid(`${where}.id must be lower-case letters, digits and dashes`);
|
|
1294
|
+
}
|
|
1295
|
+
if (typeof s.service !== "string" || !w.services.some((x) => x.id === s.service)) {
|
|
1296
|
+
throw new Invalid(`${where}.service must be one of the services it declares`);
|
|
1297
|
+
}
|
|
1298
|
+
if (w.mcpServers.some((x) => x.id === s.id)) {
|
|
1299
|
+
throw new Invalid(`it declares MCP server "${s.id}" twice`);
|
|
1300
|
+
}
|
|
1301
|
+
w.mcpServers.push({ id: s.id, service: s.service });
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
return w;
|
|
1305
|
+
}
|
|
1306
|
+
function hosts(v, field, key) {
|
|
1307
|
+
if (v === void 0) return [];
|
|
1308
|
+
if (!Array.isArray(v)) throw new Invalid(`wheelhouse.${field} must be a list`);
|
|
1309
|
+
const out = [];
|
|
1310
|
+
for (const [i, h] of v.entries()) {
|
|
1311
|
+
const where = `wheelhouse.${field}[${i}]`;
|
|
1312
|
+
if (!isRecord3(h)) throw new Invalid(`${where} must be an object`);
|
|
1313
|
+
only(h, where, ["id", key]);
|
|
1314
|
+
if (typeof h.id !== "string" || !ID.test(h.id)) {
|
|
1315
|
+
throw new Invalid(`${where}.id must be lower-case letters, digits and dashes`);
|
|
1316
|
+
}
|
|
1317
|
+
if (!validHostTemplate(h[key])) {
|
|
1318
|
+
throw new Invalid(
|
|
1319
|
+
`${where}.${key} must be a host name ending in .localhost with {port} in its first label, such as {port}.localhost`
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
if (out.some((x) => x.id === h.id)) throw new Invalid(`it declares ${field} "${h.id}" twice`);
|
|
1323
|
+
out.push({ id: h.id, [key]: h[key] });
|
|
1324
|
+
}
|
|
1325
|
+
return out;
|
|
1326
|
+
}
|
|
1327
|
+
function settings(v) {
|
|
1328
|
+
if (v === void 0) return [];
|
|
1329
|
+
if (!isRecord3(v)) throw new Invalid("wheelhouse.settings must be an object of settings by key");
|
|
1330
|
+
const keys = Object.keys(v);
|
|
1331
|
+
if (keys.length > MAX_SETTINGS) {
|
|
1332
|
+
throw new Invalid(`wheelhouse.settings declares more than ${MAX_SETTINGS} settings`);
|
|
1333
|
+
}
|
|
1334
|
+
return keys.map((key) => {
|
|
1335
|
+
const where = `wheelhouse.settings.${key}`;
|
|
1336
|
+
if (!SETTING_KEY.test(key)) {
|
|
1337
|
+
throw new Invalid(
|
|
1338
|
+
`wheelhouse.settings key "${key}" must start with a letter and hold only letters, digits and underscores`
|
|
1339
|
+
);
|
|
1340
|
+
}
|
|
1341
|
+
const s = v[key];
|
|
1342
|
+
if (!isRecord3(s)) throw new Invalid(`${where} must be an object`);
|
|
1343
|
+
only(s, where, SETTING_FIELDS);
|
|
1344
|
+
const type = s.type === void 0 ? "text" : s.type;
|
|
1345
|
+
if (!SETTING_TYPES.includes(type)) {
|
|
1346
|
+
throw new Invalid(`${where}.type must be one of ${SETTING_TYPES.join(", ")}`);
|
|
1347
|
+
}
|
|
1348
|
+
if (s.required !== void 0 && typeof s.required !== "boolean") {
|
|
1349
|
+
throw new Invalid(`${where}.required must be true or false`);
|
|
1350
|
+
}
|
|
1351
|
+
const spec = {
|
|
1352
|
+
key,
|
|
1353
|
+
title: text(s.title, `${where}.title`, true) ?? key,
|
|
1354
|
+
description: text(s.description, `${where}.description`, true),
|
|
1355
|
+
type,
|
|
1356
|
+
required: s.required ?? false
|
|
1357
|
+
};
|
|
1358
|
+
if (s.options !== void 0) {
|
|
1359
|
+
if (type !== "text") throw new Invalid(`${where}.options are for a text setting only`);
|
|
1360
|
+
spec.options = options(s.options, `${where}.options`);
|
|
1361
|
+
}
|
|
1362
|
+
if (s.default !== void 0) {
|
|
1363
|
+
const ok = type === "number" ? typeof s.default === "number" && Number.isFinite(s.default) : type === "boolean" ? typeof s.default === "boolean" : type === "list" ? Array.isArray(s.default) && s.default.length <= MAX_LIST && s.default.every((x) => typeof x === "string" && x.trim() && x.length <= MAX_TEXT) : typeof s.default === "string" && s.default.length <= MAX_TEXT && (!spec.options || spec.options.some((o) => o.value === s.default));
|
|
1364
|
+
if (!ok) {
|
|
1365
|
+
const want = {
|
|
1366
|
+
text: spec.options ? "one of its options" : "text",
|
|
1367
|
+
number: "a number",
|
|
1368
|
+
boolean: "true or false",
|
|
1369
|
+
list: "a list of texts"
|
|
1370
|
+
}[type];
|
|
1371
|
+
throw new Invalid(`${where}.default must be ${want}`);
|
|
1372
|
+
}
|
|
1373
|
+
spec.default = s.default;
|
|
1374
|
+
}
|
|
1375
|
+
return spec;
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
function options(v, where) {
|
|
1379
|
+
if (!Array.isArray(v) || !v.length || v.length > MAX_OPTIONS) {
|
|
1380
|
+
throw new Invalid(`${where} must be a list of 1 to ${MAX_OPTIONS} options`);
|
|
1381
|
+
}
|
|
1382
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1383
|
+
return v.map((o, i) => {
|
|
1384
|
+
if (!isRecord3(o)) throw new Invalid(`${where}[${i}] must be an object`);
|
|
1385
|
+
only(o, `${where}[${i}]`, ["value", "label"]);
|
|
1386
|
+
const value = text(o.value, `${where}[${i}].value`);
|
|
1387
|
+
if (seen.has(value)) throw new Invalid(`${where} has "${value}" twice`);
|
|
1388
|
+
seen.add(value);
|
|
1389
|
+
return { value, label: text(o.label, `${where}[${i}].label`, true) ?? value };
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
function parseManifest(text_) {
|
|
1393
|
+
let raw;
|
|
1394
|
+
try {
|
|
1395
|
+
raw = JSON.parse(text_);
|
|
1396
|
+
} catch (e) {
|
|
1397
|
+
return { ok: false, error: `not valid JSON: ${e.message}` };
|
|
1398
|
+
}
|
|
1399
|
+
try {
|
|
1400
|
+
if (!isRecord3(raw)) throw new Invalid("expected a JSON object");
|
|
1401
|
+
only(raw, "", MANIFEST_FIELDS);
|
|
1402
|
+
if (typeof raw.name !== "string" || !ID.test(raw.name)) {
|
|
1403
|
+
throw new Invalid("name must be lower-case letters, digits and dashes (at most 40)");
|
|
1404
|
+
}
|
|
1405
|
+
if (typeof raw.version !== "string" || !SEMVER.test(raw.version)) {
|
|
1406
|
+
throw new Invalid("version must be a semantic version, such as 0.1.0");
|
|
1407
|
+
}
|
|
1408
|
+
if (raw.wheelhouse === void 0) {
|
|
1409
|
+
throw new Invalid('"wheelhouse" is missing, so this is not a Wheelhouse plugin');
|
|
1410
|
+
}
|
|
1411
|
+
const keywords = raw.keywords === void 0 ? [] : raw.keywords;
|
|
1412
|
+
if (!Array.isArray(keywords) || keywords.some((k) => typeof k !== "string")) {
|
|
1413
|
+
throw new Invalid("keywords must be a list of text");
|
|
1414
|
+
}
|
|
1415
|
+
return {
|
|
1416
|
+
ok: true,
|
|
1417
|
+
manifest: {
|
|
1418
|
+
name: raw.name,
|
|
1419
|
+
displayName: text(raw.displayName, "displayName", true) ?? raw.name,
|
|
1420
|
+
version: raw.version,
|
|
1421
|
+
description: text(raw.description, "description", true),
|
|
1422
|
+
author: author(raw.author),
|
|
1423
|
+
repository: text(raw.repository, "repository", true),
|
|
1424
|
+
homepage: text(raw.homepage, "homepage", true),
|
|
1425
|
+
license: text(raw.license, "license", true),
|
|
1426
|
+
keywords,
|
|
1427
|
+
wheelhouse: wheelhouse(raw.wheelhouse)
|
|
1428
|
+
}
|
|
1429
|
+
};
|
|
1430
|
+
} catch (e) {
|
|
1431
|
+
if (e instanceof Invalid) return { ok: false, error: e.message };
|
|
1432
|
+
throw e;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// src/lib/server/plugins/settings.ts
|
|
1437
|
+
var ATTEMPTS = 3;
|
|
1438
|
+
var has = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
1439
|
+
function parseGiven(pairs) {
|
|
1440
|
+
const given2 = {};
|
|
1441
|
+
for (const p of pairs) {
|
|
1442
|
+
const at = p.indexOf("=");
|
|
1443
|
+
if (at < 1) return { ok: false, error: `--setting "${p}" is not key=value` };
|
|
1444
|
+
given2[p.slice(0, at).trim()] = p.slice(at + 1);
|
|
1445
|
+
}
|
|
1446
|
+
return { ok: true, given: given2 };
|
|
1447
|
+
}
|
|
1448
|
+
function parseValue(spec, text2) {
|
|
1449
|
+
const t = text2.trim();
|
|
1450
|
+
if (spec.type === "list") {
|
|
1451
|
+
const items = [
|
|
1452
|
+
...new Set(
|
|
1453
|
+
t.split(",").map((x) => x.trim()).filter(Boolean)
|
|
1454
|
+
)
|
|
1455
|
+
];
|
|
1456
|
+
return items.length || !spec.required ? { ok: true, value: items } : { ok: false, error: `${spec.title} needs at least one` };
|
|
1457
|
+
}
|
|
1458
|
+
if (spec.options) {
|
|
1459
|
+
const o = spec.options.find((x) => x.value === t || x.label.toLowerCase() === t.toLowerCase());
|
|
1460
|
+
return o ? { ok: true, value: o.value } : {
|
|
1461
|
+
ok: false,
|
|
1462
|
+
error: `${spec.title} must be one of ${spec.options.map((x) => x.label).join(", ")}`
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
if (spec.type === "number") {
|
|
1466
|
+
const n = Number(t);
|
|
1467
|
+
return t !== "" && Number.isFinite(n) ? { ok: true, value: n } : { ok: false, error: `${spec.title} must be a number` };
|
|
1468
|
+
}
|
|
1469
|
+
if (spec.type === "boolean") {
|
|
1470
|
+
if (/^(y|yes|true|on)$/i.test(t)) return { ok: true, value: true };
|
|
1471
|
+
if (/^(n|no|false|off)$/i.test(t)) return { ok: true, value: false };
|
|
1472
|
+
return { ok: false, error: `${spec.title} must be yes or no` };
|
|
1473
|
+
}
|
|
1474
|
+
return t ? { ok: true, value: t } : { ok: false, error: `${spec.title} must not be empty` };
|
|
1475
|
+
}
|
|
1476
|
+
function showValue(spec, v) {
|
|
1477
|
+
if (v === true) return "yes";
|
|
1478
|
+
if (v === false) return "no";
|
|
1479
|
+
if (Array.isArray(v)) return v.join(", ");
|
|
1480
|
+
return spec.options?.find((o) => o.value === v)?.label ?? String(v);
|
|
1481
|
+
}
|
|
1482
|
+
function question(spec, offered) {
|
|
1483
|
+
const optional = spec.required ? "" : " (optional)";
|
|
1484
|
+
const kind = spec.type === "boolean" ? " (y/n)" : spec.type === "list" ? " (separate with commas)" : spec.options ? ` (${spec.options.map((o) => o.label).join(" / ")})` : "";
|
|
1485
|
+
const def = offered === void 0 ? "" : ` [${showValue(spec, offered)}]`;
|
|
1486
|
+
return `${spec.title}${optional}${kind}${def}: `;
|
|
1487
|
+
}
|
|
1488
|
+
async function gatherSettings(id, specs, o) {
|
|
1489
|
+
for (const k of Object.keys(o.given)) {
|
|
1490
|
+
if (!specs.some((s) => s.key === k)) {
|
|
1491
|
+
return { ok: false, error: `${id} has no setting "${k}"` };
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
const settings2 = { ...o.current };
|
|
1495
|
+
let introduced = false;
|
|
1496
|
+
for (const spec of specs) {
|
|
1497
|
+
if (has(o.given, spec.key)) {
|
|
1498
|
+
const v = parseValue(spec, o.given[spec.key]);
|
|
1499
|
+
if (!v.ok) return { ok: false, error: `--setting ${spec.key}: ${v.error}` };
|
|
1500
|
+
settings2[spec.key] = v.value;
|
|
1501
|
+
continue;
|
|
1502
|
+
}
|
|
1503
|
+
if (has(o.current, spec.key)) continue;
|
|
1504
|
+
const offered = spec.default;
|
|
1505
|
+
if (!o.ask) {
|
|
1506
|
+
if (offered !== void 0) settings2[spec.key] = offered;
|
|
1507
|
+
else if (spec.required) {
|
|
1508
|
+
return {
|
|
1509
|
+
ok: false,
|
|
1510
|
+
error: `${id} needs ${spec.title}; pass --setting ${spec.key}=<value>`
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1515
|
+
if (!introduced) {
|
|
1516
|
+
o.log(o.intro);
|
|
1517
|
+
introduced = true;
|
|
1518
|
+
}
|
|
1519
|
+
if (spec.description) o.log(` ${spec.description}`);
|
|
1520
|
+
let answered = false;
|
|
1521
|
+
for (let i = 0; i < ATTEMPTS && !answered; i++) {
|
|
1522
|
+
const a = await o.ask(question(spec, offered));
|
|
1523
|
+
if (a === null) return { ok: false, error: `stopped before ${spec.title} was answered` };
|
|
1524
|
+
if (!a.trim()) {
|
|
1525
|
+
if (offered !== void 0) {
|
|
1526
|
+
settings2[spec.key] = offered;
|
|
1527
|
+
answered = true;
|
|
1528
|
+
} else if (!spec.required) answered = true;
|
|
1529
|
+
else o.log(` ${spec.title} is needed.`);
|
|
1530
|
+
continue;
|
|
1531
|
+
}
|
|
1532
|
+
const v = parseValue(spec, a);
|
|
1533
|
+
if (v.ok) {
|
|
1534
|
+
settings2[spec.key] = v.value;
|
|
1535
|
+
answered = true;
|
|
1536
|
+
} else o.log(` ${v.error}.`);
|
|
1537
|
+
}
|
|
1538
|
+
if (!answered) return { ok: false, error: `${id} needs ${spec.title}` };
|
|
1539
|
+
}
|
|
1540
|
+
return { ok: true, settings: settings2 };
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
// src/lib/server/plugins/install.ts
|
|
1544
|
+
var ID2 = /^[a-z][a-z0-9-]{0,39}$/;
|
|
1545
|
+
var NAME = /^[A-Za-z0-9_.-]+$/;
|
|
1546
|
+
var SHA = /^[0-9a-f]{7,40}$/;
|
|
1547
|
+
function parseSource(spec) {
|
|
1548
|
+
const m = /^([^/@\s]+)\/([^/@\s]+?)(?:\.git)?(?:@(\S+))?$/.exec(spec.trim());
|
|
1549
|
+
if (!m || !NAME.test(m[1]) || !NAME.test(m[2]) || m[1].startsWith("-") || m[2].startsWith(".")) {
|
|
1550
|
+
return null;
|
|
1551
|
+
}
|
|
1552
|
+
const ref = m[3] ?? null;
|
|
1553
|
+
if (ref !== null && (ref.startsWith("-") || !/^[A-Za-z0-9_./-]+$/.test(ref))) return null;
|
|
1554
|
+
return { org: m[1], repo: m[2], ref };
|
|
1555
|
+
}
|
|
1556
|
+
var runGit = (args, cwd) => new Promise((resolve) => {
|
|
1557
|
+
execFile(
|
|
1558
|
+
"git",
|
|
1559
|
+
args,
|
|
1560
|
+
{
|
|
1561
|
+
cwd,
|
|
1562
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
|
|
1563
|
+
maxBuffer: 4 * 1024 * 1024
|
|
1564
|
+
},
|
|
1565
|
+
(err, stdout, stderr) => resolve({
|
|
1566
|
+
code: err ? typeof err.code === "number" ? err.code : 1 : 0,
|
|
1567
|
+
stdout: String(stdout),
|
|
1568
|
+
stderr: String(stderr)
|
|
1569
|
+
})
|
|
1570
|
+
);
|
|
1571
|
+
});
|
|
1572
|
+
var githubUrl = (s) => `https://github.com/${s.org}/${s.repo}.git`;
|
|
1573
|
+
var short = (commit) => commit.slice(0, 12);
|
|
1574
|
+
var label = (r) => `${r.repository}${r.ref ? `@${r.ref}` : ""}`;
|
|
1575
|
+
var Refused = class extends Error {
|
|
1576
|
+
};
|
|
1577
|
+
async function fetchSource(source, into, deps) {
|
|
1578
|
+
const git = deps.git ?? runGit;
|
|
1579
|
+
const url = (deps.gitUrl ?? githubUrl)(source);
|
|
1580
|
+
const step = async (args) => {
|
|
1581
|
+
const r = await git(args, into);
|
|
1582
|
+
if (r.code !== 0)
|
|
1583
|
+
throw new Refused(r.stderr.trim().split("\n").pop() || `git ${args[0]} failed`);
|
|
1584
|
+
return r.stdout.trim();
|
|
1585
|
+
};
|
|
1586
|
+
await step(["init", "-q"]);
|
|
1587
|
+
const want = source.ref ?? "HEAD";
|
|
1588
|
+
const shallow = await git(["fetch", "-q", "--depth", "1", url, want], into);
|
|
1589
|
+
if (shallow.code !== 0) {
|
|
1590
|
+
if (!source.ref || !SHA.test(source.ref)) {
|
|
1591
|
+
throw new Refused(
|
|
1592
|
+
`could not fetch ${source.org}/${source.repo}${source.ref ? ` at ${source.ref}` : ""}: ${shallow.stderr.trim().split("\n").pop() ?? ""}`
|
|
1593
|
+
);
|
|
1594
|
+
}
|
|
1595
|
+
await step(["fetch", "-q", url]);
|
|
1596
|
+
await step(["checkout", "-q", "--detach", source.ref]);
|
|
1597
|
+
} else {
|
|
1598
|
+
await step(["checkout", "-q", "--detach", "FETCH_HEAD"]);
|
|
1599
|
+
}
|
|
1600
|
+
const commit = await step(["rev-parse", "HEAD"]);
|
|
1601
|
+
await rm2(path8.join(into, ".git"), { recursive: true, force: true });
|
|
1602
|
+
return commit;
|
|
1603
|
+
}
|
|
1604
|
+
async function checkCopy(dir) {
|
|
1605
|
+
let text2;
|
|
1606
|
+
try {
|
|
1607
|
+
text2 = await readFile4(manifestPath(dir), "utf8");
|
|
1608
|
+
} catch {
|
|
1609
|
+
throw new Refused("it has no .wheelhouse-plugin/plugin.json, so it is not a Wheelhouse plugin");
|
|
1610
|
+
}
|
|
1611
|
+
const parsed = parseManifest(text2);
|
|
1612
|
+
if (!parsed.ok) throw new Refused(`its manifest is not valid: ${parsed.error}`);
|
|
1613
|
+
const m = parsed.manifest;
|
|
1614
|
+
const base = await realpath2(dir);
|
|
1615
|
+
for (const [what, rel] of [
|
|
1616
|
+
["server entry", m.wheelhouse.server],
|
|
1617
|
+
["ui bundle", m.wheelhouse.ui]
|
|
1618
|
+
]) {
|
|
1619
|
+
if (!rel) continue;
|
|
1620
|
+
let file;
|
|
1621
|
+
try {
|
|
1622
|
+
file = await realpath2(path8.join(base, rel));
|
|
1623
|
+
if (!(await stat4(file)).isFile()) throw new Error();
|
|
1624
|
+
} catch {
|
|
1625
|
+
throw new Refused(`its ${what} ${rel} is missing`);
|
|
1626
|
+
}
|
|
1627
|
+
if (!file.startsWith(base + path8.sep))
|
|
1628
|
+
throw new Refused(`its ${what} leads outside the plugin`);
|
|
1629
|
+
}
|
|
1630
|
+
return m;
|
|
1631
|
+
}
|
|
1632
|
+
function enabledText(m) {
|
|
1633
|
+
const w = m.wheelhouse;
|
|
1634
|
+
return w.enabledWhenHomeFile ? `while the Firstmate home has ${w.enabledWhenHomeFile}` : w.enabledWhenFile ? `for projects whose clone has ${w.enabledWhenFile}` : w.enabledByDefault ? "for every project" : "only where your plugins file turns it on";
|
|
1635
|
+
}
|
|
1636
|
+
function describeManifest(m, from, commit) {
|
|
1637
|
+
const w = m.wheelhouse;
|
|
1638
|
+
const enabled = enabledText(m);
|
|
1639
|
+
return [
|
|
1640
|
+
`${m.displayName} (${m.name}) ${m.version}`,
|
|
1641
|
+
...m.description ? [` ${m.description}`] : [],
|
|
1642
|
+
` From: ${from}`,
|
|
1643
|
+
` Commit: ${commit}`,
|
|
1644
|
+
...m.author ? [` Author: ${m.author.name}${m.author.url ? ` <${m.author.url}>` : ""}`] : [],
|
|
1645
|
+
...m.repository ? [` Homepage: ${m.repository}`] : [],
|
|
1646
|
+
` Contract: plugin API ${w.apiVersion}`,
|
|
1647
|
+
` Slots: ${w.contributions.map((c) => `${c.slot} (${c.id})`).join(", ") || "none"}`,
|
|
1648
|
+
` Services: ${w.services.map((s) => `${s.id} (${s.scope})`).join(", ") || "none"}`,
|
|
1649
|
+
` Settings: ${w.settings.map((s) => `${s.key}${s.required ? " (required)" : ""}`).join(", ") || "none"}`,
|
|
1650
|
+
...w.mcpServers.length ? [
|
|
1651
|
+
` MCP apps: ${w.mcpServers.map((s) => `${s.id} (on ${s.service})`).join(", ")}, drawn in a sandboxed frame`
|
|
1652
|
+
] : [],
|
|
1653
|
+
...w.hostRoutes.length ? [
|
|
1654
|
+
` Answers: ${w.hostRoutes.map((r) => r.host).join(", ")} on Wheelhouse\u2019s port, with its own code`
|
|
1655
|
+
] : [],
|
|
1656
|
+
...w.addressRewrites.length ? [
|
|
1657
|
+
` Addresses: opens addresses on this computer at ${w.addressRewrites.map((r) => r.to).join(", ")}`
|
|
1658
|
+
] : [],
|
|
1659
|
+
` UI bundle: ${w.ui ?? "none"}`,
|
|
1660
|
+
` On: ${enabled}`,
|
|
1661
|
+
"",
|
|
1662
|
+
"Its code runs with your permissions: its server entry inside Wheelhouse, and",
|
|
1663
|
+
"its UI bundle in Wheelhouse\u2019s pages. Wheelhouse gives it read-only helpers, but",
|
|
1664
|
+
"does not sandbox it. Install only plugins you trust."
|
|
1665
|
+
];
|
|
1666
|
+
}
|
|
1667
|
+
async function withStaging(dir, work) {
|
|
1668
|
+
await mkdir4(dir, { recursive: true });
|
|
1669
|
+
const staging = await mkdtemp(path8.join(dir, ".staging-"));
|
|
1670
|
+
try {
|
|
1671
|
+
return await work(staging);
|
|
1672
|
+
} finally {
|
|
1673
|
+
await rm2(staging, { recursive: true, force: true });
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
async function guarded(work) {
|
|
1677
|
+
try {
|
|
1678
|
+
return await work();
|
|
1679
|
+
} catch (e) {
|
|
1680
|
+
if (e instanceof Refused) return { ok: false, error: e.message };
|
|
1681
|
+
throw e;
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
function given(pairs) {
|
|
1685
|
+
const g2 = parseGiven(pairs ?? []);
|
|
1686
|
+
if (!g2.ok) throw new Refused(g2.error);
|
|
1687
|
+
return g2.given;
|
|
1688
|
+
}
|
|
1689
|
+
async function settle(m, deps, o) {
|
|
1690
|
+
if (!m.wheelhouse.settings.length && !Object.keys(o.given).length) return null;
|
|
1691
|
+
const current = await readPluginSettings(deps.configDir, m.name);
|
|
1692
|
+
if (!current.ok) throw new Refused(current.error);
|
|
1693
|
+
const r = await gatherSettings(m.name, m.wheelhouse.settings, {
|
|
1694
|
+
current: current.settings,
|
|
1695
|
+
given: o.given,
|
|
1696
|
+
ask: o.yes ? null : deps.ask ?? null,
|
|
1697
|
+
intro: `${m.displayName} needs some settings (Enter keeps the one in brackets):`,
|
|
1698
|
+
log: deps.log
|
|
1699
|
+
});
|
|
1700
|
+
if (!r.ok) throw new Refused(r.error);
|
|
1701
|
+
return r.settings;
|
|
1702
|
+
}
|
|
1703
|
+
async function keepSettings(id, settings2, deps) {
|
|
1704
|
+
const w = await writePluginSettings(deps.configDir, id, settings2);
|
|
1705
|
+
if (!w.ok) throw new Refused(`could not keep ${id}'s settings: ${w.error}`);
|
|
1706
|
+
deps.log(`Kept ${id}'s settings in ${path8.join(deps.configDir, PLUGINS_FILE)}.`);
|
|
1707
|
+
}
|
|
1708
|
+
async function reload(deps, what, yes) {
|
|
1709
|
+
const r = await deps.restart(async (url, busy) => {
|
|
1710
|
+
deps.log(`Restarting Wheelhouse at ${url} stops what it is running:`);
|
|
1711
|
+
for (const b of busy) deps.log(` - ${b}`);
|
|
1712
|
+
return yes || deps.confirm("Restart it now?");
|
|
1713
|
+
});
|
|
1714
|
+
for (const url of r.restarted) deps.log(`Restarting Wheelhouse at ${url} to ${what}.`);
|
|
1715
|
+
for (const url of r.kept) deps.log(`Left Wheelhouse at ${url} running; restart it to ${what}.`);
|
|
1716
|
+
for (const f of r.failed) {
|
|
1717
|
+
deps.log(`Could not restart Wheelhouse at ${f.url} (${f.reason}); restart it to ${what}.`);
|
|
1718
|
+
}
|
|
1719
|
+
if (!r.restarted.length && !r.kept.length && !r.failed.length) {
|
|
1720
|
+
deps.log(`Wheelhouse is not running; it will ${what} when it next starts.`);
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
async function record2(deps) {
|
|
1724
|
+
const r = await readInstalled(deps.dir);
|
|
1725
|
+
if (!r.ok) throw new Refused(r.error);
|
|
1726
|
+
return r.installed;
|
|
1727
|
+
}
|
|
1728
|
+
function expected(commit, expect, repository) {
|
|
1729
|
+
if (expect !== void 0 && commit !== expect) {
|
|
1730
|
+
throw new Refused(
|
|
1731
|
+
`${repository} is at ${short(commit)} now, not ${short(expect)} as when you looked; look it up again`
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
function install(spec, deps, o = {}) {
|
|
1736
|
+
return guarded(async () => {
|
|
1737
|
+
const source = parseSource(spec);
|
|
1738
|
+
if (!source) throw new Refused(`"${spec}" is not org/repo or org/repo@ref`);
|
|
1739
|
+
const answers = given(o.settings);
|
|
1740
|
+
const installed = await record2(deps);
|
|
1741
|
+
const repository = `${source.org}/${source.repo}`;
|
|
1742
|
+
return withStaging(deps.dir, async (staging) => {
|
|
1743
|
+
deps.log(`Fetching ${repository}${source.ref ? ` at ${source.ref}` : ""}\u2026`);
|
|
1744
|
+
const commit = await fetchSource(source, staging, deps);
|
|
1745
|
+
expected(commit, o.expect, repository);
|
|
1746
|
+
const m = await checkCopy(staging);
|
|
1747
|
+
const target = path8.join(deps.dir, m.name);
|
|
1748
|
+
if (installed.plugins[m.name]) {
|
|
1749
|
+
throw new Refused(
|
|
1750
|
+
`${m.name} is already installed from ${label(installed.plugins[m.name])}; use \`wheelhouse plugin update ${m.name}\``
|
|
1751
|
+
);
|
|
1752
|
+
}
|
|
1753
|
+
if (await exists(target)) {
|
|
1754
|
+
throw new Refused(`${target} already exists; remove it first`);
|
|
1755
|
+
}
|
|
1756
|
+
for (const line of describeManifest(
|
|
1757
|
+
m,
|
|
1758
|
+
`${repository}${source.ref ? ` (${source.ref})` : ""}`,
|
|
1759
|
+
commit
|
|
1760
|
+
))
|
|
1761
|
+
deps.log(line);
|
|
1762
|
+
if (!o.yes && !await deps.confirm(`Install ${m.name}?`)) {
|
|
1763
|
+
throw new Refused("not installed");
|
|
1764
|
+
}
|
|
1765
|
+
const settings2 = await settle(m, deps, { yes: o.yes, given: answers });
|
|
1766
|
+
if (settings2) await keepSettings(m.name, settings2, deps);
|
|
1767
|
+
await rename4(staging, target);
|
|
1768
|
+
installed.plugins[m.name] = {
|
|
1769
|
+
repository,
|
|
1770
|
+
ref: source.ref,
|
|
1771
|
+
commit,
|
|
1772
|
+
version: m.version,
|
|
1773
|
+
installedAt: (deps.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
1774
|
+
enabled: true
|
|
1775
|
+
};
|
|
1776
|
+
await writeInstalled(deps.dir, installed);
|
|
1777
|
+
deps.log(`Installed ${m.name} ${m.version} at ${short(commit)}.`);
|
|
1778
|
+
await reload(deps, `load ${m.name}`, o.yes);
|
|
1779
|
+
return { ok: true };
|
|
1780
|
+
});
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
function update(id, deps, o = {}) {
|
|
1784
|
+
return guarded(async () => {
|
|
1785
|
+
const answers = given(o.settings);
|
|
1786
|
+
const installed = await record2(deps);
|
|
1787
|
+
const was = installed.plugins[id];
|
|
1788
|
+
if (!was) throw new Refused(`${id} is not installed`);
|
|
1789
|
+
const [org, repo] = was.repository.split("/");
|
|
1790
|
+
const source = { org, repo, ref: o.ref ?? was.ref };
|
|
1791
|
+
if (!parseSource(`${org}/${repo}${source.ref ? `@${source.ref}` : ""}`)) {
|
|
1792
|
+
throw new Refused(`"${source.ref}" is not a ref`);
|
|
1793
|
+
}
|
|
1794
|
+
return withStaging(deps.dir, async (staging) => {
|
|
1795
|
+
deps.log(`Fetching ${was.repository}${source.ref ? ` at ${source.ref}` : ""}\u2026`);
|
|
1796
|
+
const commit = await fetchSource(source, staging, deps);
|
|
1797
|
+
expected(commit, o.expect, was.repository);
|
|
1798
|
+
if (commit === was.commit) {
|
|
1799
|
+
if (source.ref !== was.ref) {
|
|
1800
|
+
installed.plugins[id] = { ...was, ref: source.ref };
|
|
1801
|
+
await writeInstalled(deps.dir, installed);
|
|
1802
|
+
}
|
|
1803
|
+
deps.log(`${id} is already at ${short(commit)}.`);
|
|
1804
|
+
return { ok: true };
|
|
1805
|
+
}
|
|
1806
|
+
const m = await checkCopy(staging);
|
|
1807
|
+
if (m.name !== id) {
|
|
1808
|
+
throw new Refused(`${was.repository} now names its plugin "${m.name}", not "${id}"`);
|
|
1809
|
+
}
|
|
1810
|
+
deps.log(
|
|
1811
|
+
`Update ${id} from ${was.version} (${short(was.commit)}) to ${m.version} (${short(commit)}):`
|
|
1812
|
+
);
|
|
1813
|
+
for (const line of describeManifest(
|
|
1814
|
+
m,
|
|
1815
|
+
`${was.repository}${source.ref ? ` (${source.ref})` : ""}`,
|
|
1816
|
+
commit
|
|
1817
|
+
))
|
|
1818
|
+
deps.log(line);
|
|
1819
|
+
if (!o.yes && !await deps.confirm(`Update ${id}?`)) throw new Refused("not updated");
|
|
1820
|
+
const settings2 = await settle(m, deps, { yes: o.yes, given: answers });
|
|
1821
|
+
if (settings2) await keepSettings(id, settings2, deps);
|
|
1822
|
+
const target = path8.join(deps.dir, id);
|
|
1823
|
+
const old = await mkdtemp(path8.join(deps.dir, ".old-"));
|
|
1824
|
+
const parked = path8.join(old, id);
|
|
1825
|
+
if (await exists(target)) await rename4(target, parked);
|
|
1826
|
+
try {
|
|
1827
|
+
await rename4(staging, target);
|
|
1828
|
+
} catch (e) {
|
|
1829
|
+
if (await exists(parked)) await rename4(parked, target);
|
|
1830
|
+
throw e;
|
|
1831
|
+
} finally {
|
|
1832
|
+
await rm2(old, { recursive: true, force: true });
|
|
1833
|
+
}
|
|
1834
|
+
installed.plugins[id] = {
|
|
1835
|
+
...was,
|
|
1836
|
+
ref: source.ref,
|
|
1837
|
+
commit,
|
|
1838
|
+
version: m.version,
|
|
1839
|
+
installedAt: (deps.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
|
|
1840
|
+
};
|
|
1841
|
+
await writeInstalled(deps.dir, installed);
|
|
1842
|
+
deps.log(`Updated ${id} to ${m.version} at ${short(commit)}.`);
|
|
1843
|
+
await reload(deps, `load ${id} ${m.version}`, o.yes);
|
|
1844
|
+
return { ok: true };
|
|
1845
|
+
});
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
function remove(id, deps, o = {}) {
|
|
1849
|
+
return guarded(async () => {
|
|
1850
|
+
if (!ID2.test(id)) throw new Refused(`"${id}" is not a plugin id`);
|
|
1851
|
+
const installed = await record2(deps);
|
|
1852
|
+
if (!installed.plugins[id]) throw new Refused(`${id} is not installed`);
|
|
1853
|
+
await rm2(path8.join(deps.dir, id), { recursive: true, force: true });
|
|
1854
|
+
delete installed.plugins[id];
|
|
1855
|
+
await writeInstalled(deps.dir, installed);
|
|
1856
|
+
deps.log(`Removed ${id}.`);
|
|
1857
|
+
await reload(deps, `unload ${id}`, o.yes);
|
|
1858
|
+
return { ok: true };
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
function setEnabled(id, enabled, deps, o = {}) {
|
|
1862
|
+
return guarded(async () => {
|
|
1863
|
+
const installed = await record2(deps);
|
|
1864
|
+
const r = installed.plugins[id];
|
|
1865
|
+
if (!r) throw new Refused(`${id} is not installed`);
|
|
1866
|
+
installed.plugins[id] = { ...r, enabled };
|
|
1867
|
+
await writeInstalled(deps.dir, installed);
|
|
1868
|
+
deps.log(`${id} is ${enabled ? "on" : "off"}.`);
|
|
1869
|
+
await reload(deps, `${enabled ? "load" : "unload"} ${id}`, o.yes);
|
|
1870
|
+
return { ok: true };
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
function list(deps) {
|
|
1874
|
+
return guarded(async () => {
|
|
1875
|
+
const installed = await record2(deps);
|
|
1876
|
+
const rows = Object.entries(installed.plugins).sort(([a], [b]) => a.localeCompare(b));
|
|
1877
|
+
if (!rows.length) {
|
|
1878
|
+
deps.log(`No plugins installed in ${deps.dir}.`);
|
|
1879
|
+
return { ok: true };
|
|
1880
|
+
}
|
|
1881
|
+
const width = Math.max(...rows.map(([id]) => id.length));
|
|
1882
|
+
for (const [id, r] of rows) deps.log(row(id, r, width));
|
|
1883
|
+
return { ok: true };
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
function row(id, r, width) {
|
|
1887
|
+
return `${id.padEnd(width)} ${r.version.padEnd(8)} ${r.enabled ? "on " : "off"} ${short(r.commit)} ${label(r)}`;
|
|
1888
|
+
}
|
|
1889
|
+
async function exists(p) {
|
|
1890
|
+
try {
|
|
1891
|
+
await stat4(p);
|
|
1892
|
+
return true;
|
|
1893
|
+
} catch {
|
|
1894
|
+
return false;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
// cli/running.ts
|
|
1899
|
+
import { randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
1900
|
+
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
1901
|
+
import { readdir, readFile as readFile5, rm as rm3 } from "node:fs/promises";
|
|
1902
|
+
import path9 from "node:path";
|
|
1903
|
+
var RESTART_EXIT = 75;
|
|
1904
|
+
var RESTART_PATH = "/api/wheelhouse/restart";
|
|
1905
|
+
var RUNNING_DIR = ".running";
|
|
1906
|
+
var TIMEOUT_MS = 3e3;
|
|
1907
|
+
var newToken = () => randomBytes2(24).toString("hex");
|
|
1908
|
+
function writeRunning(pluginsDir, r) {
|
|
1909
|
+
const dir = path9.join(pluginsDir, RUNNING_DIR);
|
|
1910
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1911
|
+
const file = path9.join(dir, `${r.pid}.json`);
|
|
1912
|
+
writeFileSync(file, `${JSON.stringify(r)}
|
|
1913
|
+
`, { mode: 384 });
|
|
1914
|
+
return file;
|
|
1915
|
+
}
|
|
1916
|
+
function removeRunning(file) {
|
|
1917
|
+
rmSync(file, { force: true });
|
|
1918
|
+
}
|
|
1919
|
+
var sameToken = (got, want) => {
|
|
1920
|
+
const a = Buffer.from(got);
|
|
1921
|
+
const b = Buffer.from(want);
|
|
1922
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
1923
|
+
};
|
|
1924
|
+
function restartHandler(token, restart, busy) {
|
|
1925
|
+
return (req, res) => {
|
|
1926
|
+
if (req.url !== RESTART_PATH) return false;
|
|
1927
|
+
const auth = /^Bearer (\S+)$/.exec(req.headers.authorization ?? "")?.[1] ?? "";
|
|
1928
|
+
if (req.method !== "GET" && req.method !== "POST") {
|
|
1929
|
+
res.writeHead(405, { allow: "GET, POST" }).end();
|
|
1930
|
+
} else if (!sameToken(auth, token)) {
|
|
1931
|
+
res.writeHead(403).end();
|
|
1932
|
+
} else if (req.method === "GET") {
|
|
1933
|
+
busy().then(
|
|
1934
|
+
(work) => res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ busy: work })),
|
|
1935
|
+
() => res.writeHead(500).end()
|
|
1936
|
+
);
|
|
1937
|
+
} else {
|
|
1938
|
+
res.once("finish", restart);
|
|
1939
|
+
res.writeHead(202, { "content-type": "text/plain" }).end("restarting\n");
|
|
1940
|
+
}
|
|
1941
|
+
return true;
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
var alive = (pid) => {
|
|
1945
|
+
try {
|
|
1946
|
+
process.kill(pid, 0);
|
|
1947
|
+
return true;
|
|
1948
|
+
} catch (e) {
|
|
1949
|
+
return e.code === "EPERM";
|
|
1950
|
+
}
|
|
1951
|
+
};
|
|
1952
|
+
var refused = (e) => e.cause?.code === "ECONNREFUSED";
|
|
1953
|
+
async function requestRestart(pluginsDir, proceed) {
|
|
1954
|
+
const dir = path9.join(pluginsDir, RUNNING_DIR);
|
|
1955
|
+
const out = { restarted: [], failed: [], kept: [] };
|
|
1956
|
+
let names;
|
|
1957
|
+
try {
|
|
1958
|
+
names = (await readdir(dir)).filter((n) => /^\d+\.json$/.test(n)).sort();
|
|
1959
|
+
} catch {
|
|
1960
|
+
return out;
|
|
1961
|
+
}
|
|
1962
|
+
for (const name of names) {
|
|
1963
|
+
const file = path9.join(dir, name);
|
|
1964
|
+
let r;
|
|
1965
|
+
try {
|
|
1966
|
+
r = JSON.parse(await readFile5(file, "utf8"));
|
|
1967
|
+
if (typeof r.url !== "string" || typeof r.token !== "string") throw new Error();
|
|
1968
|
+
} catch {
|
|
1969
|
+
await rm3(file, { force: true });
|
|
1970
|
+
continue;
|
|
1971
|
+
}
|
|
1972
|
+
if (typeof r.pid === "number" && !alive(r.pid)) {
|
|
1973
|
+
await rm3(file, { force: true });
|
|
1974
|
+
continue;
|
|
1975
|
+
}
|
|
1976
|
+
const headers = { authorization: `Bearer ${r.token}` };
|
|
1977
|
+
try {
|
|
1978
|
+
const at = new URL(RESTART_PATH, r.url);
|
|
1979
|
+
const asked = await fetch(at, { headers, signal: AbortSignal.timeout(TIMEOUT_MS) });
|
|
1980
|
+
if (asked.status !== 200) {
|
|
1981
|
+
out.failed.push({ url: r.url, reason: `it answered ${asked.status}` });
|
|
1982
|
+
continue;
|
|
1983
|
+
}
|
|
1984
|
+
const { busy } = await asked.json();
|
|
1985
|
+
if (busy.length && !await proceed(r.url, busy)) {
|
|
1986
|
+
out.kept.push(r.url);
|
|
1987
|
+
continue;
|
|
1988
|
+
}
|
|
1989
|
+
const res = await fetch(at, {
|
|
1990
|
+
method: "POST",
|
|
1991
|
+
headers,
|
|
1992
|
+
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
1993
|
+
});
|
|
1994
|
+
if (res.status === 202) out.restarted.push(r.url);
|
|
1995
|
+
else out.failed.push({ url: r.url, reason: `it answered ${res.status}` });
|
|
1996
|
+
} catch (e) {
|
|
1997
|
+
if (refused(e)) await rm3(file, { force: true });
|
|
1998
|
+
else out.failed.push({ url: r.url, reason: e.message });
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
return out;
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
// cli/plugin.ts
|
|
2005
|
+
async function askTerminal(question2) {
|
|
2006
|
+
if (!process.stdin.isTTY) {
|
|
2007
|
+
console.error("wheelhouse: no terminal to ask on; pass --yes to agree without asking.");
|
|
2008
|
+
return false;
|
|
2009
|
+
}
|
|
2010
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
2011
|
+
try {
|
|
2012
|
+
return /^y(es)?$/i.test((await rl.question(`${question2} [y/N] `)).trim());
|
|
2013
|
+
} finally {
|
|
2014
|
+
rl.close();
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
async function askLine(question2) {
|
|
2018
|
+
if (!process.stdin.isTTY) return null;
|
|
2019
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
2020
|
+
try {
|
|
2021
|
+
return await new Promise((resolve) => {
|
|
2022
|
+
rl.once("close", () => resolve(null));
|
|
2023
|
+
rl.once("SIGINT", () => rl.close());
|
|
2024
|
+
rl.question(question2).then(resolve, () => resolve(null));
|
|
2025
|
+
});
|
|
2026
|
+
} finally {
|
|
2027
|
+
rl.close();
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
var collect = (v, all = []) => [...all, v];
|
|
2031
|
+
function pluginCommand(over = {}) {
|
|
2032
|
+
const env = over.env ?? process.env;
|
|
2033
|
+
const cmd = new Command2("plugin").description("Install and manage plugins from GitHub repositories.").option(
|
|
2034
|
+
"--plugins-dir <dir>",
|
|
2035
|
+
"where installed plugins live (default: $WHEELHOUSE_PLUGINS_DIR, else ~/.wheelhouse/plugins)"
|
|
2036
|
+
).option(
|
|
2037
|
+
"--config-dir <dir>",
|
|
2038
|
+
"the Wheelhouse config whose plugins.json keeps plugin settings (default: $WHEELHOUSE_CONFIG_DIR, else $XDG_CONFIG_HOME/wheelhouse, else ~/.config/wheelhouse)"
|
|
2039
|
+
);
|
|
2040
|
+
const deps = (c) => {
|
|
2041
|
+
const g2 = c.optsWithGlobals();
|
|
2042
|
+
const dir = g2.pluginsDir ? path10.resolve(g2.pluginsDir) : defaultPluginsDir(env);
|
|
2043
|
+
return {
|
|
2044
|
+
dir,
|
|
2045
|
+
configDir: g2.configDir ? path10.resolve(g2.configDir) : defaultConfigDir(env),
|
|
2046
|
+
confirm: askTerminal,
|
|
2047
|
+
ask: askLine,
|
|
2048
|
+
restart: (proceed) => requestRestart(dir, proceed),
|
|
2049
|
+
log: (line) => console.log(line),
|
|
2050
|
+
...over
|
|
2051
|
+
};
|
|
2052
|
+
};
|
|
2053
|
+
const run = async (c, work) => {
|
|
2054
|
+
const o = await work(deps(c));
|
|
2055
|
+
if (!o.ok) {
|
|
2056
|
+
(over.log ?? console.error)(`wheelhouse: ${o.error}.`);
|
|
2057
|
+
process.exitCode = 1;
|
|
2058
|
+
}
|
|
2059
|
+
};
|
|
2060
|
+
cmd.command("install <repo>").description("Install a plugin from org/repo, or org/repo@<tag, branch or commit>.").option("-y, --yes", "install without asking; settings take their defaults (for scripts)").option("-s, --setting <key=value>", "answer one of its settings (repeatable)", collect).action(
|
|
2061
|
+
(repo, o, c) => run(c, (d) => install(repo, d, { yes: o.yes, settings: o.setting }))
|
|
2062
|
+
);
|
|
2063
|
+
cmd.command("list").description("List installed plugins, their versions and pinned commits.").action((_o, c) => run(c, list));
|
|
2064
|
+
cmd.command("update <id>").description("Fetch the ref the plugin was installed from again (or --ref), and replace it.").option("--ref <ref>", "a tag, branch or commit to move to").option("-y, --yes", "update without asking; new settings take their defaults (for scripts)").option("-s, --setting <key=value>", "answer one of its settings (repeatable)", collect).action(
|
|
2065
|
+
(id, o, c) => run(c, (d) => update(id, d, { yes: o.yes, ref: o.ref, settings: o.setting }))
|
|
2066
|
+
);
|
|
2067
|
+
cmd.command("remove <id>").description("Delete an installed plugin.").option("-y, --yes", "restart a running Wheelhouse without asking, even mid-work").action(
|
|
2068
|
+
(id, o, c) => run(c, (d) => remove(id, d, { yes: o.yes }))
|
|
2069
|
+
);
|
|
2070
|
+
cmd.command("enable <id>").description("Turn an installed plugin on.").option("-y, --yes", "restart a running Wheelhouse without asking, even mid-work").action(
|
|
2071
|
+
(id, o, c) => run(c, (d) => setEnabled(id, true, d, { yes: o.yes }))
|
|
2072
|
+
);
|
|
2073
|
+
cmd.command("disable <id>").description("Turn an installed plugin off without removing it.").option("-y, --yes", "restart a running Wheelhouse without asking, even mid-work").action(
|
|
2074
|
+
(id, o, c) => run(c, (d) => setEnabled(id, false, d, { yes: o.yes }))
|
|
2075
|
+
);
|
|
2076
|
+
return cmd;
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
// cli/supervise.ts
|
|
2080
|
+
var MAX_RESTARTS = 5;
|
|
2081
|
+
var RESTART_WINDOW_MS = 6e4;
|
|
2082
|
+
var backoff = (n) => Math.min(500 * 2 ** (n - 1), 8e3);
|
|
2083
|
+
function isReady(msg) {
|
|
2084
|
+
return typeof msg === "object" && msg !== null && msg.type === "ready" && typeof msg.port === "number";
|
|
2085
|
+
}
|
|
2086
|
+
function supervise(opts) {
|
|
2087
|
+
const maxRestarts = opts.maxRestarts ?? MAX_RESTARTS;
|
|
2088
|
+
const windowMs = opts.windowMs ?? RESTART_WINDOW_MS;
|
|
2089
|
+
const wait = opts.backoffMs ?? backoff;
|
|
2090
|
+
let port = opts.port;
|
|
2091
|
+
let everReady = false;
|
|
2092
|
+
let stopping = false;
|
|
2093
|
+
let child = null;
|
|
2094
|
+
let pending = null;
|
|
2095
|
+
let killer = null;
|
|
2096
|
+
let restarts = [];
|
|
2097
|
+
let finish = () => {
|
|
2098
|
+
};
|
|
2099
|
+
const done = new Promise((resolve) => {
|
|
2100
|
+
finish = (code) => {
|
|
2101
|
+
if (pending) clearTimeout(pending);
|
|
2102
|
+
if (killer) clearTimeout(killer);
|
|
2103
|
+
pending = killer = null;
|
|
2104
|
+
resolve(code);
|
|
2105
|
+
};
|
|
2106
|
+
});
|
|
2107
|
+
const launch = (restart) => {
|
|
2108
|
+
pending = null;
|
|
2109
|
+
const c = opts.start(port, restart);
|
|
2110
|
+
child = c;
|
|
2111
|
+
c.on("message", (msg) => {
|
|
2112
|
+
if (!isReady(msg)) return;
|
|
2113
|
+
port = msg.port;
|
|
2114
|
+
if (!everReady) {
|
|
2115
|
+
everReady = true;
|
|
2116
|
+
opts.onReady(msg.port);
|
|
2117
|
+
} else {
|
|
2118
|
+
opts.log(`Wheelhouse's server is back on http://127.0.0.1:${msg.port}/.`);
|
|
2119
|
+
}
|
|
2120
|
+
});
|
|
2121
|
+
c.on("exit", (code, signal) => {
|
|
2122
|
+
if (child === c) child = null;
|
|
2123
|
+
if (stopping || code === 0) return finish(0);
|
|
2124
|
+
if (code === RESTART_EXIT && everReady) {
|
|
2125
|
+
opts.log("Restarting Wheelhouse\u2026");
|
|
2126
|
+
return launch(true);
|
|
2127
|
+
}
|
|
2128
|
+
if (!everReady) return finish(code ?? 1);
|
|
2129
|
+
const how = signal ? `was stopped by ${signal}` : `exited with code ${code}`;
|
|
2130
|
+
const now = Date.now();
|
|
2131
|
+
restarts = [...restarts.filter((t) => now - t < windowMs), now];
|
|
2132
|
+
if (restarts.length > maxRestarts) {
|
|
2133
|
+
opts.log(
|
|
2134
|
+
`Wheelhouse's server ${how} and has failed ${restarts.length} times within a minute, so Wheelhouse is closing. Firstmate and its crew are still running.`
|
|
2135
|
+
);
|
|
2136
|
+
return finish(1);
|
|
2137
|
+
}
|
|
2138
|
+
const ms = wait(restarts.length);
|
|
2139
|
+
opts.log(
|
|
2140
|
+
`Wheelhouse's server ${how}; restarting it in ${Math.round(ms / 100) / 10} s. Firstmate and its crew are untouched.`
|
|
2141
|
+
);
|
|
2142
|
+
pending = setTimeout(() => launch(true), ms);
|
|
2143
|
+
});
|
|
2144
|
+
};
|
|
2145
|
+
launch(false);
|
|
2146
|
+
return {
|
|
2147
|
+
done,
|
|
2148
|
+
stop: () => {
|
|
2149
|
+
if (stopping) return;
|
|
2150
|
+
stopping = true;
|
|
2151
|
+
if (!child) return finish(0);
|
|
2152
|
+
child.kill("SIGTERM");
|
|
2153
|
+
const c = child;
|
|
2154
|
+
killer = setTimeout(() => c.kill("SIGKILL"), opts.killAfterMs ?? 5e3);
|
|
2155
|
+
killer.unref?.();
|
|
2156
|
+
}
|
|
2157
|
+
};
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
// cli/upgrade.ts
|
|
2161
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
2162
|
+
import path12 from "node:path";
|
|
2163
|
+
import { fileURLToPath } from "node:url";
|
|
2164
|
+
import { Command as Command3 } from "commander";
|
|
2165
|
+
|
|
2166
|
+
// src/lib/server/upgrade/clone.ts
|
|
2167
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
2168
|
+
import { readFile as readFile6, realpath as realpath3 } from "node:fs/promises";
|
|
2169
|
+
import path11 from "node:path";
|
|
2170
|
+
var COMMITS_SHOWN = 20;
|
|
2171
|
+
var OUTPUT_KEPT = 6e4;
|
|
2172
|
+
var runCommand = (cmd, args, cwd) => new Promise((resolve) => {
|
|
2173
|
+
execFile2(cmd, args, { cwd, maxBuffer: 64 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
2174
|
+
const output = `${String(stdout)}${String(stderr)}`;
|
|
2175
|
+
resolve({
|
|
2176
|
+
code: err ? typeof err.code === "number" ? err.code : 1 : 0,
|
|
2177
|
+
output: output.slice(-OUTPUT_KEPT)
|
|
2178
|
+
});
|
|
2179
|
+
});
|
|
2180
|
+
});
|
|
2181
|
+
function githubRepository(url) {
|
|
2182
|
+
const m = /^(?:https?:\/\/(?:[^@/]+@)?github\.com\/|(?:ssh:\/\/)?git@github\.com[:/])([^/]+)\/([^/]+?)(?:\.git)?\/?$/.exec(
|
|
2183
|
+
url.trim()
|
|
2184
|
+
);
|
|
2185
|
+
return m ? `${m[1]}/${m[2]}` : null;
|
|
2186
|
+
}
|
|
2187
|
+
function packageVersion(text2) {
|
|
2188
|
+
if (text2 === null) return null;
|
|
2189
|
+
try {
|
|
2190
|
+
const v = JSON.parse(text2).version;
|
|
2191
|
+
return typeof v === "string" ? v : null;
|
|
2192
|
+
} catch {
|
|
2193
|
+
return null;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
var lastLine = (s) => s.trim().split("\n").pop() ?? "";
|
|
2197
|
+
async function checkClone(deps) {
|
|
2198
|
+
const git = deps.git ?? runGit;
|
|
2199
|
+
const g2 = async (...args) => git(args, deps.dir);
|
|
2200
|
+
const out = {
|
|
2201
|
+
schema: "wheelhouse-update.v1",
|
|
2202
|
+
checkedAt: (deps.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
2203
|
+
state: "unsupported",
|
|
2204
|
+
reason: null,
|
|
2205
|
+
dir: deps.dir,
|
|
2206
|
+
repository: null,
|
|
2207
|
+
branch: null,
|
|
2208
|
+
current: { commit: null, version: null },
|
|
2209
|
+
latest: null,
|
|
2210
|
+
commits: [],
|
|
2211
|
+
behind: 0,
|
|
2212
|
+
changes: []
|
|
2213
|
+
};
|
|
2214
|
+
out.current.version = packageVersion(
|
|
2215
|
+
await readFile6(path11.join(deps.dir, "package.json"), "utf8").catch(() => null)
|
|
2216
|
+
);
|
|
2217
|
+
const top = await g2("rev-parse", "--show-toplevel");
|
|
2218
|
+
const here = await realpath3(deps.dir).catch(() => deps.dir);
|
|
2219
|
+
if (top.code !== 0 || await realpath3(top.stdout.trim()).catch(() => "") !== here) {
|
|
2220
|
+
out.reason = `${deps.dir} is not a git clone, so Wheelhouse cannot upgrade it; update it the way it was installed (for npm, \`npm install -g @chrisjowen/wheelhouse\`)`;
|
|
2221
|
+
return out;
|
|
2222
|
+
}
|
|
2223
|
+
const head = await g2("rev-parse", "HEAD");
|
|
2224
|
+
out.current.commit = head.code === 0 ? head.stdout.trim() : null;
|
|
2225
|
+
const origin = await g2("remote", "get-url", "origin");
|
|
2226
|
+
if (origin.code !== 0) {
|
|
2227
|
+
out.reason = `${deps.dir} has no origin remote to upgrade from`;
|
|
2228
|
+
return out;
|
|
2229
|
+
}
|
|
2230
|
+
out.repository = githubRepository(origin.stdout);
|
|
2231
|
+
const symref = await g2("ls-remote", "--symref", "origin", "HEAD");
|
|
2232
|
+
const branch = /^ref: refs\/heads\/(\S+)\s+HEAD$/m.exec(symref.stdout)?.[1] ?? null;
|
|
2233
|
+
if (symref.code !== 0 || !branch) {
|
|
2234
|
+
out.state = "error";
|
|
2235
|
+
out.reason = `could not reach origin: ${lastLine(symref.stderr) || "it named no default branch"}`;
|
|
2236
|
+
return out;
|
|
2237
|
+
}
|
|
2238
|
+
out.branch = branch;
|
|
2239
|
+
const fetched = await g2("fetch", "--quiet", "--no-tags", "origin", `refs/heads/${branch}`);
|
|
2240
|
+
if (fetched.code !== 0) {
|
|
2241
|
+
out.state = "error";
|
|
2242
|
+
out.reason = `could not fetch ${branch}: ${lastLine(fetched.stderr)}`;
|
|
2243
|
+
return out;
|
|
2244
|
+
}
|
|
2245
|
+
const latest = (await g2("rev-parse", "FETCH_HEAD")).stdout.trim();
|
|
2246
|
+
const remotePkg = await g2("show", `${latest}:package.json`);
|
|
2247
|
+
out.latest = {
|
|
2248
|
+
commit: latest,
|
|
2249
|
+
version: packageVersion(remotePkg.code === 0 ? remotePkg.stdout : null)
|
|
2250
|
+
};
|
|
2251
|
+
const counts = (await g2("rev-list", "--left-right", "--count", `HEAD...${latest}`)).stdout.trim().split(/\s+/).map(Number);
|
|
2252
|
+
const ahead = counts[0] || 0;
|
|
2253
|
+
out.behind = counts[1] || 0;
|
|
2254
|
+
if (out.behind === 0) {
|
|
2255
|
+
out.state = "current";
|
|
2256
|
+
return out;
|
|
2257
|
+
}
|
|
2258
|
+
const log = await g2("log", "--format=%H%x1f%s%x1f%cI", `-n${COMMITS_SHOWN}`, `HEAD..${latest}`);
|
|
2259
|
+
out.commits = log.stdout.split("\n").filter(Boolean).map((l) => {
|
|
2260
|
+
const [sha, subject, date] = l.split("");
|
|
2261
|
+
return { sha, subject, date };
|
|
2262
|
+
});
|
|
2263
|
+
const on = await g2("symbolic-ref", "--quiet", "--short", "HEAD");
|
|
2264
|
+
const status = await g2("status", "--porcelain", "--untracked-files=no");
|
|
2265
|
+
const changes = status.stdout.split("\n").filter((l) => l.trim());
|
|
2266
|
+
out.state = "refused";
|
|
2267
|
+
if (on.code !== 0) {
|
|
2268
|
+
out.reason = `${deps.dir} is not on a branch (detached HEAD), so it cannot fast-forward to ${branch}`;
|
|
2269
|
+
} else if (on.stdout.trim() !== branch) {
|
|
2270
|
+
out.reason = `${deps.dir} is on ${on.stdout.trim()}, not ${branch}, so it cannot fast-forward`;
|
|
2271
|
+
} else if (changes.length) {
|
|
2272
|
+
out.reason = `${deps.dir} has local changes; commit or discard them, then check again`;
|
|
2273
|
+
out.changes = changes;
|
|
2274
|
+
} else if (ahead > 0) {
|
|
2275
|
+
out.reason = `${deps.dir} has ${ahead} commit${ahead === 1 ? "" : "s"} that ${branch} does not, so it cannot fast-forward`;
|
|
2276
|
+
} else {
|
|
2277
|
+
out.state = "available";
|
|
2278
|
+
}
|
|
2279
|
+
return out;
|
|
2280
|
+
}
|
|
2281
|
+
async function upgradeClone(deps, onStep = () => {
|
|
2282
|
+
}) {
|
|
2283
|
+
const git = deps.git ?? runGit;
|
|
2284
|
+
const run = deps.run ?? runCommand;
|
|
2285
|
+
onStep("fetch");
|
|
2286
|
+
const check = await checkClone(deps);
|
|
2287
|
+
if (check.state !== "available" || !check.latest) {
|
|
2288
|
+
const error = check.state === "current" ? `Wheelhouse is already up to date with ${check.branch}` : check.reason ?? "there is nothing to upgrade to";
|
|
2289
|
+
return { ok: false, step: "fetch", error, output: "", check };
|
|
2290
|
+
}
|
|
2291
|
+
onStep("fast-forward");
|
|
2292
|
+
const ff = await git(["merge", "--ff-only", "--quiet", check.latest.commit], deps.dir);
|
|
2293
|
+
if (ff.code !== 0) {
|
|
2294
|
+
return {
|
|
2295
|
+
ok: false,
|
|
2296
|
+
step: "fast-forward",
|
|
2297
|
+
error: `could not fast-forward: ${lastLine(ff.stderr)}`,
|
|
2298
|
+
output: ff.stderr,
|
|
2299
|
+
check
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
for (const [step, args] of [
|
|
2303
|
+
["install", ["ci"]],
|
|
2304
|
+
["build", ["run", "build"]]
|
|
2305
|
+
]) {
|
|
2306
|
+
onStep(step);
|
|
2307
|
+
const r = await run("npm", [...args], deps.dir);
|
|
2308
|
+
if (r.code !== 0) {
|
|
2309
|
+
return {
|
|
2310
|
+
ok: false,
|
|
2311
|
+
step,
|
|
2312
|
+
error: `npm ${args.join(" ")} failed (exit ${r.code}); the clone is at ${check.latest.commit.slice(0, 7)} but its build is incomplete, so rebuild it (npm ci && npm run build) before Wheelhouse restarts`,
|
|
2313
|
+
output: r.output,
|
|
2314
|
+
check
|
|
2315
|
+
};
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
return { ok: true, check, from: check.current.commit, to: check.latest.commit };
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
// src/lib/updates.ts
|
|
2322
|
+
var UPGRADE_STEPS = [
|
|
2323
|
+
{ step: "fetch", label: "Fetch from GitHub" },
|
|
2324
|
+
{ step: "fast-forward", label: "Fast-forward the clone" },
|
|
2325
|
+
{ step: "install", label: "Install packages (npm ci)" },
|
|
2326
|
+
{ step: "build", label: "Build" },
|
|
2327
|
+
{ step: "restart", label: "Restart Wheelhouse" }
|
|
2328
|
+
];
|
|
2329
|
+
|
|
2330
|
+
// cli/upgrade.ts
|
|
2331
|
+
function installDir() {
|
|
2332
|
+
return path12.resolve(path12.dirname(fileURLToPath(import.meta.url)), "..");
|
|
2333
|
+
}
|
|
2334
|
+
var runInherit = (cmd, args, cwd) => new Promise((resolve) => {
|
|
2335
|
+
const child = spawn3(cmd, args, { cwd, stdio: "inherit" });
|
|
2336
|
+
child.on("error", (e) => resolve({ code: 1, output: e.message }));
|
|
2337
|
+
child.on("exit", (code) => resolve({ code: code ?? 1, output: "" }));
|
|
2338
|
+
});
|
|
2339
|
+
var short2 = (c) => c ? c.slice(0, 7) : "unknown";
|
|
2340
|
+
var versionOf = (v) => v ? `${v.version ?? "?"} (${short2(v.commit)})` : "unknown";
|
|
2341
|
+
function describe(c, log) {
|
|
2342
|
+
const from = c.repository ?? "origin";
|
|
2343
|
+
if (c.state === "current") {
|
|
2344
|
+
log(`Wheelhouse ${versionOf(c.current)} is up to date with ${from} ${c.branch}.`);
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
if (c.state === "unsupported" || c.state === "error") return;
|
|
2348
|
+
log(
|
|
2349
|
+
`${from} ${c.branch} has ${c.behind} new commit${c.behind === 1 ? "" : "s"}: Wheelhouse ${versionOf(c.current)} \u2192 ${versionOf(c.latest)}`
|
|
2350
|
+
);
|
|
2351
|
+
for (const x of c.commits) log(` ${short2(x.sha)} ${x.subject}`);
|
|
2352
|
+
if (c.behind > c.commits.length) log(` \u2026 and ${c.behind - c.commits.length} more`);
|
|
2353
|
+
}
|
|
2354
|
+
async function upgrade(deps, o = {}) {
|
|
2355
|
+
const cloneDeps = { dir: deps.dir, git: deps.git, run: deps.run ?? runInherit };
|
|
2356
|
+
deps.log(`Checking ${deps.dir} for a newer Wheelhouse\u2026`);
|
|
2357
|
+
const c = await checkClone(cloneDeps);
|
|
2358
|
+
describe(c, deps.log);
|
|
2359
|
+
if (c.state === "current") return true;
|
|
2360
|
+
if (c.state !== "available") {
|
|
2361
|
+
deps.log(`wheelhouse: ${c.reason}.`);
|
|
2362
|
+
for (const ch of c.changes) deps.log(` ${ch}`);
|
|
2363
|
+
if (c.state === "refused") deps.log("Nothing was changed.");
|
|
2364
|
+
return false;
|
|
2365
|
+
}
|
|
2366
|
+
if (o.check) return true;
|
|
2367
|
+
if (!o.yes && !await deps.confirm("Upgrade Wheelhouse now?")) {
|
|
2368
|
+
deps.log("Not upgraded.");
|
|
2369
|
+
return false;
|
|
2370
|
+
}
|
|
2371
|
+
const label2 = Object.fromEntries(UPGRADE_STEPS.map((s) => [s.step, s.label]));
|
|
2372
|
+
const r = await upgradeClone(cloneDeps, (step) => deps.log(`${label2[step]}\u2026`));
|
|
2373
|
+
if (!r.ok) {
|
|
2374
|
+
deps.log(`wheelhouse: ${r.error}.`);
|
|
2375
|
+
return false;
|
|
2376
|
+
}
|
|
2377
|
+
deps.log(`Upgraded Wheelhouse to ${versionOf(r.check.latest)}.`);
|
|
2378
|
+
const restarted = await deps.restart(async (url, busy) => {
|
|
2379
|
+
deps.log(`Restarting Wheelhouse at ${url} stops what it is running:`);
|
|
2380
|
+
for (const b of busy) deps.log(` - ${b}`);
|
|
2381
|
+
return o.yes || deps.confirm("Restart it now?");
|
|
2382
|
+
});
|
|
2383
|
+
for (const url of restarted.restarted) deps.log(`Restarting Wheelhouse at ${url}.`);
|
|
2384
|
+
for (const url of restarted.kept) {
|
|
2385
|
+
deps.log(`Left Wheelhouse at ${url} running the old version; restart it to use the new one.`);
|
|
2386
|
+
}
|
|
2387
|
+
for (const f of restarted.failed) {
|
|
2388
|
+
deps.log(
|
|
2389
|
+
`Could not restart Wheelhouse at ${f.url} (${f.reason}); restart it to use the new one.`
|
|
2390
|
+
);
|
|
2391
|
+
}
|
|
2392
|
+
if (!restarted.restarted.length && !restarted.kept.length && !restarted.failed.length) {
|
|
2393
|
+
deps.log("Wheelhouse is not running; the new version starts with `wheelhouse open`.");
|
|
2394
|
+
}
|
|
2395
|
+
return true;
|
|
2396
|
+
}
|
|
2397
|
+
function upgradeCommand(over = {}) {
|
|
2398
|
+
const env = over.env ?? process.env;
|
|
2399
|
+
return new Command3("upgrade").description(
|
|
2400
|
+
"Fast-forward this Wheelhouse clone to the newest commit on GitHub, rebuild it and restart Wheelhouse."
|
|
2401
|
+
).option("--check", "only say whether there is a newer version").option("-y, --yes", "upgrade (and restart, even mid-work) without asking").option(
|
|
2402
|
+
"--plugins-dir <dir>",
|
|
2403
|
+
"the plugins directory the running Wheelhouse loads from, to find it (default: $WHEELHOUSE_PLUGINS_DIR, else ~/.wheelhouse/plugins)"
|
|
2404
|
+
).action(async (o) => {
|
|
2405
|
+
const pluginsDir = o.pluginsDir ? path12.resolve(o.pluginsDir) : defaultPluginsDir(env);
|
|
2406
|
+
const ok = await upgrade(
|
|
2407
|
+
{
|
|
2408
|
+
dir: installDir(),
|
|
2409
|
+
confirm: askTerminal,
|
|
2410
|
+
restart: (proceed) => requestRestart(pluginsDir, proceed),
|
|
2411
|
+
log: (line) => console.log(line),
|
|
2412
|
+
...over
|
|
2413
|
+
},
|
|
2414
|
+
o
|
|
2415
|
+
);
|
|
2416
|
+
if (!ok) process.exitCode = 1;
|
|
2417
|
+
});
|
|
362
2418
|
}
|
|
363
2419
|
|
|
364
2420
|
// cli/index.ts
|
|
365
2421
|
var DEFAULT_PORT = 4310;
|
|
366
2422
|
var HOST = "127.0.0.1";
|
|
367
2423
|
var runtimeHolder = () => globalThis[/* @__PURE__ */ Symbol.for("wheelhouse.runtime")];
|
|
368
|
-
function
|
|
2424
|
+
function parsePort2(value) {
|
|
369
2425
|
const n = Number(value);
|
|
370
2426
|
if (!Number.isInteger(n) || n < 0 || n > 65535) {
|
|
371
|
-
throw new
|
|
2427
|
+
throw new InvalidArgumentError2("Port must be a whole number from 0 to 65535.");
|
|
372
2428
|
}
|
|
373
2429
|
return n;
|
|
374
2430
|
}
|
|
375
2431
|
function handlerPath() {
|
|
376
|
-
const here =
|
|
377
|
-
return
|
|
2432
|
+
const here = path13.dirname(fileURLToPath2(import.meta.url));
|
|
2433
|
+
return path13.resolve(here, "..", "build", "handler.js");
|
|
378
2434
|
}
|
|
379
2435
|
function openBrowser(url) {
|
|
380
2436
|
const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", '""', url]] : ["xdg-open", [url]];
|
|
381
2437
|
try {
|
|
382
|
-
const child =
|
|
2438
|
+
const child = spawn4(cmd, args, { detached: true, stdio: "ignore" });
|
|
383
2439
|
child.on("error", () => console.log(`Open ${url} in your browser.`));
|
|
384
2440
|
child.unref();
|
|
385
2441
|
} catch {
|
|
386
2442
|
console.log(`Open ${url} in your browser.`);
|
|
387
2443
|
}
|
|
388
2444
|
}
|
|
389
|
-
function parseEffort(v) {
|
|
390
|
-
if (!["low", "medium", "high"].includes(v)) {
|
|
391
|
-
throw new InvalidArgumentError("Use low, medium or high.");
|
|
392
|
-
}
|
|
393
|
-
return v;
|
|
394
|
-
}
|
|
395
2445
|
async function open2(opts) {
|
|
396
2446
|
const resolved = resolveInstall({
|
|
397
2447
|
firstmate: opts.firstmate,
|
|
@@ -403,11 +2453,11 @@ async function open2(opts) {
|
|
|
403
2453
|
console.error(`wheelhouse: ${resolved.error}`);
|
|
404
2454
|
process.exit(1);
|
|
405
2455
|
}
|
|
406
|
-
const { install, notes } = resolved;
|
|
2456
|
+
const { install: install2, notes } = resolved;
|
|
407
2457
|
for (const n of notes) console.log(n);
|
|
408
|
-
console.log(`Firstmate: ${
|
|
409
|
-
console.log(`Home: ${
|
|
410
|
-
const compat = await checkCompatibility(
|
|
2458
|
+
console.log(`Firstmate: ${install2.firstmateDir}`);
|
|
2459
|
+
console.log(`Home: ${install2.homeDir}`);
|
|
2460
|
+
const compat = await checkCompatibility(install2);
|
|
411
2461
|
if (compat.compatible) {
|
|
412
2462
|
console.log(
|
|
413
2463
|
`Scripts: compatible${compat.firstmateVersion ? ` (Firstmate ${compat.firstmateVersion})` : ""}`
|
|
@@ -417,16 +2467,16 @@ async function open2(opts) {
|
|
|
417
2467
|
for (const p of compat.problems) console.warn(` - ${p}`);
|
|
418
2468
|
console.warn("Wheelhouse will start and show what it can; see /api/health.");
|
|
419
2469
|
}
|
|
420
|
-
const stateDir = opts.stateDir ?
|
|
421
|
-
if (isInside(stateDir,
|
|
2470
|
+
const stateDir = opts.stateDir ? path13.resolve(opts.stateDir) : defaultStateDir(process.env);
|
|
2471
|
+
if (isInside(stateDir, install2.homeDir)) {
|
|
422
2472
|
console.error(
|
|
423
2473
|
`wheelhouse: the state directory ${stateDir} is inside the Firstmate home. Wheelhouse never writes there; pass --state-dir <dir> outside it.`
|
|
424
2474
|
);
|
|
425
2475
|
process.exit(1);
|
|
426
2476
|
}
|
|
427
2477
|
console.log(`State: ${stateDir}`);
|
|
428
|
-
const configDir = opts.configDir ?
|
|
429
|
-
if (isInside(configDir,
|
|
2478
|
+
const configDir = opts.configDir ? path13.resolve(opts.configDir) : defaultConfigDir(process.env);
|
|
2479
|
+
if (isInside(configDir, install2.homeDir)) {
|
|
430
2480
|
console.error(
|
|
431
2481
|
`wheelhouse: the config directory ${configDir} is inside the Firstmate home. Wheelhouse never reads its own config from there; pass --config-dir <dir> outside it.`
|
|
432
2482
|
);
|
|
@@ -440,18 +2490,52 @@ async function open2(opts) {
|
|
|
440
2490
|
);
|
|
441
2491
|
process.exit(1);
|
|
442
2492
|
}
|
|
443
|
-
process.env.WHEELHOUSE_FIRSTMATE_DIR =
|
|
444
|
-
process.env.WHEELHOUSE_HOME =
|
|
2493
|
+
process.env.WHEELHOUSE_FIRSTMATE_DIR = install2.firstmateDir;
|
|
2494
|
+
process.env.WHEELHOUSE_HOME = install2.homeDir;
|
|
445
2495
|
process.env.WHEELHOUSE_STATE_DIR = stateDir;
|
|
446
2496
|
process.env.WHEELHOUSE_CONFIG_DIR = configDir;
|
|
2497
|
+
if (opts.pluginsDir) process.env.WHEELHOUSE_PLUGINS_DIR = path13.resolve(opts.pluginsDir);
|
|
2498
|
+
process.env.WHEELHOUSE_BUNDLED_PLUGINS_DIR = path13.resolve(path13.dirname(file), "..", "plugins");
|
|
2499
|
+
process.env.WHEELHOUSE_INSTALL_DIR = path13.resolve(path13.dirname(file), "..");
|
|
447
2500
|
if (opts.tmuxSocket) process.env.WHEELHOUSE_TMUX_SOCKET = opts.tmuxSocket;
|
|
448
2501
|
process.env.WHEELHOUSE_SKIP_PERMISSIONS = opts.skipPermissions ? "1" : "0";
|
|
449
|
-
if (opts.specModel) process.env.WHEELHOUSE_SPEC_MODEL = opts.specModel;
|
|
450
|
-
if (opts.specEffort) process.env.WHEELHOUSE_SPEC_EFFORT = opts.specEffort;
|
|
451
2502
|
if (opts.reviewModel) process.env.WHEELHOUSE_REVIEW_MODEL = opts.reviewModel;
|
|
452
2503
|
if (opts.reviewEffort) process.env.WHEELHOUSE_REVIEW_EFFORT = opts.reviewEffort;
|
|
453
|
-
const
|
|
454
|
-
const
|
|
2504
|
+
const cli = fileURLToPath2(import.meta.url);
|
|
2505
|
+
const watcher = supervise({
|
|
2506
|
+
port: opts.port,
|
|
2507
|
+
start: (port, restart) => fork(cli, ["__server", "--port", String(port)], {
|
|
2508
|
+
env: { ...process.env, WHEELHOUSE_RESTARTED: restart ? "1" : "0" },
|
|
2509
|
+
stdio: ["ignore", "inherit", "inherit", "ipc"]
|
|
2510
|
+
}),
|
|
2511
|
+
onReady: (port) => {
|
|
2512
|
+
const url = `http://${HOST}:${port}/`;
|
|
2513
|
+
console.log(`Wheelhouse is listening on ${url}`);
|
|
2514
|
+
console.log("Press Ctrl+C to close Wheelhouse. Firstmate and its crew keep running.");
|
|
2515
|
+
if (opts.browser) openBrowser(url);
|
|
2516
|
+
},
|
|
2517
|
+
log: (line) => console.warn(line)
|
|
2518
|
+
});
|
|
2519
|
+
process.on("SIGINT", watcher.stop);
|
|
2520
|
+
process.on("SIGTERM", watcher.stop);
|
|
2521
|
+
process.exit(await watcher.done);
|
|
2522
|
+
}
|
|
2523
|
+
async function serve(opts) {
|
|
2524
|
+
process.env.WHEELHOUSE_URL = `http://${HOST}:${opts.port}`;
|
|
2525
|
+
process.env.BODY_SIZE_LIMIT ??= String(UPLOAD_MAX_BYTES + 64 * 1024);
|
|
2526
|
+
const { handler } = await import(pathToFileURL(handlerPath()).href);
|
|
2527
|
+
const token = newToken();
|
|
2528
|
+
let close = (_code) => {
|
|
2529
|
+
};
|
|
2530
|
+
const restart = restartHandler(
|
|
2531
|
+
token,
|
|
2532
|
+
() => close(RESTART_EXIT),
|
|
2533
|
+
async () => await runtimeHolder()?.busy?.() ?? []
|
|
2534
|
+
);
|
|
2535
|
+
const server = createServer((req, res) => {
|
|
2536
|
+
if (!restart(req, res) && !runtimeHolder()?.request?.(req, res)) handler(req, res);
|
|
2537
|
+
});
|
|
2538
|
+
setRestart(() => close(RESTART_EXIT));
|
|
455
2539
|
server.on("upgrade", (req, socket, head) => {
|
|
456
2540
|
if (runtimeHolder()?.upgrade?.(req, socket, head)) return;
|
|
457
2541
|
socket.end("HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
|
|
@@ -472,32 +2556,42 @@ async function open2(opts) {
|
|
|
472
2556
|
const address = server.address();
|
|
473
2557
|
const port = typeof address === "object" && address ? address.port : opts.port;
|
|
474
2558
|
const url = `http://${HOST}:${port}/`;
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
2559
|
+
let running2 = null;
|
|
2560
|
+
try {
|
|
2561
|
+
running2 = writeRunning(defaultPluginsDir(process.env), { pid: process.pid, url, token });
|
|
2562
|
+
} catch (e) {
|
|
2563
|
+
console.warn(
|
|
2564
|
+
`Warning: plugin changes will not restart Wheelhouse by themselves: ${e.message}`
|
|
2565
|
+
);
|
|
2566
|
+
}
|
|
2567
|
+
process.send?.({ type: "ready", port });
|
|
478
2568
|
let closing = false;
|
|
479
|
-
|
|
2569
|
+
close = (code) => {
|
|
480
2570
|
if (closing) return;
|
|
481
2571
|
closing = true;
|
|
2572
|
+
if (running2) removeRunning(running2);
|
|
482
2573
|
runtimeHolder()?.shutdown?.();
|
|
483
2574
|
server.close(() => {
|
|
484
|
-
|
|
485
|
-
|
|
2575
|
+
if (code !== RESTART_EXIT) {
|
|
2576
|
+
console.log("Wheelhouse closed. Firstmate and its crew are still running.");
|
|
2577
|
+
}
|
|
2578
|
+
process.exit(code);
|
|
486
2579
|
});
|
|
487
2580
|
server.closeAllConnections();
|
|
488
|
-
setTimeout(() => process.exit(
|
|
2581
|
+
setTimeout(() => process.exit(code), 3e3).unref();
|
|
489
2582
|
};
|
|
490
|
-
process.on("SIGINT", close);
|
|
491
|
-
process.on("SIGTERM", close);
|
|
2583
|
+
process.on("SIGINT", () => close(0));
|
|
2584
|
+
process.on("SIGTERM", () => close(0));
|
|
2585
|
+
process.on("disconnect", () => close(0));
|
|
492
2586
|
}
|
|
493
2587
|
function program() {
|
|
494
|
-
const cli = new
|
|
2588
|
+
const cli = new Command4("wheelhouse").description("A local companion to Firstmate: watch the crew at work from a web UI.").version(version);
|
|
495
2589
|
cli.command("open", { isDefault: true }).description(
|
|
496
2590
|
"Start Wheelhouse, start (or show) the Firstmate session, and open it in the browser."
|
|
497
2591
|
).option("--firstmate <dir>", "Firstmate code directory (default: the checkout you are in)").option(
|
|
498
2592
|
"--home <dir>",
|
|
499
2593
|
"Firstmate operational home (default: $FM_HOME, else the code directory)"
|
|
500
|
-
).option("--port <n>", "port to listen on (127.0.0.1 only)",
|
|
2594
|
+
).option("--port <n>", "port to listen on (127.0.0.1 only)", parsePort2, DEFAULT_PORT).option(
|
|
501
2595
|
"--state-dir <dir>",
|
|
502
2596
|
"where Wheelhouse keeps its own records (default: $WHEELHOUSE_STATE_DIR, else $XDG_STATE_HOME/wheelhouse, else ~/.local/state/wheelhouse)"
|
|
503
2597
|
).option(
|
|
@@ -507,12 +2601,8 @@ function program() {
|
|
|
507
2601
|
"--config-dir <dir>",
|
|
508
2602
|
"the captain-owned Wheelhouse config, with per-project passage files in projects/<id>.yaml (default: $WHEELHOUSE_CONFIG_DIR, else $XDG_CONFIG_HOME/wheelhouse, else ~/.config/wheelhouse)"
|
|
509
2603
|
).option(
|
|
510
|
-
"--
|
|
511
|
-
"
|
|
512
|
-
).option(
|
|
513
|
-
"--spec-effort <level>",
|
|
514
|
-
"how hard the Spec tab's interviewer thinks: low, medium or high (default: $WHEELHOUSE_SPEC_EFFORT, else low)",
|
|
515
|
-
parseEffort
|
|
2604
|
+
"--plugins-dir <dir>",
|
|
2605
|
+
"where installed plugins live (default: $WHEELHOUSE_PLUGINS_DIR, else ~/.wheelhouse/plugins)"
|
|
516
2606
|
).option("--no-browser", "do not open the browser").option(
|
|
517
2607
|
"--no-skip-permissions",
|
|
518
2608
|
"start Firstmate without --dangerously-skip-permissions, so Claude Code asks before tools run (answer in the terminal drawer)"
|
|
@@ -525,6 +2615,10 @@ function program() {
|
|
|
525
2615
|
"how hard the PR review agent thinks (default: $WHEELHOUSE_REVIEW_EFFORT, else low)"
|
|
526
2616
|
).choices(["low", "medium", "high", "xhigh", "max"])
|
|
527
2617
|
).action((opts) => open2(opts));
|
|
2618
|
+
cli.command("__server", { hidden: true }).description("Wheelhouse's server, as `open` starts it under its watcher.").option("--port <n>", "port to listen on (127.0.0.1 only)", parsePort2, DEFAULT_PORT).action((opts) => serve(opts));
|
|
2619
|
+
cli.addCommand(pluginCommand());
|
|
2620
|
+
cli.addCommand(upgradeCommand());
|
|
2621
|
+
cli.addCommand(gatewayCommand());
|
|
528
2622
|
return cli;
|
|
529
2623
|
}
|
|
530
2624
|
await program().parseAsync(process.argv);
|