plum-e2e 2.10.0 → 2.12.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/.env +1 -1
- package/CLAUDE.md +1 -1
- package/README.md +7 -7
- package/backend/app.js +1 -1
- package/backend/lib/csv.js +21 -0
- package/backend/lib/exportResponse.js +37 -0
- package/backend/lib/serverBootstrap.js +2 -2
- package/backend/lib/serverConfig.js +5 -3
- package/backend/mcp/server.js +44 -0
- package/backend/middleware/runnerOrAdmin.js +1 -1
- package/backend/package.json +1 -1
- package/backend/routes/reports.routes.js +19 -0
- package/backend/routes/runners.routes.js +1 -1
- package/backend/routes/test-cases.routes.js +12 -0
- package/backend/routes/test-runs.routes.js +17 -0
- package/backend/routes/test-suites.routes.js +34 -0
- package/backend/scripts/{manage-runners.mjs → manage-nodes.mjs} +67 -52
- package/backend/services/exportService.js +339 -0
- package/backend/services/testImportService.js +87 -0
- package/bin/plum.js +174 -68
- package/docker-compose.yml +1 -1
- package/frontend/.svelte-kit/adapter-node/.vite/manifest.json +48 -40
- package/frontend/.svelte-kit/{output/server/_app/immutable/assets/Toast.DENwYn3g.css → adapter-node/_app/immutable/assets/Button.C1zsJAUG.css} +1 -1
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/ExportMenu.DsJQ1Qua.css +6 -0
- package/frontend/{build/client/_app/immutable/assets/_layout.HkPnpRl9.css → .svelte-kit/adapter-node/_app/immutable/assets/_layout.B4TT3qNx.css} +1 -1
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.4bPj83tb.css +6 -0
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.BwgtEi_a.css +6 -0
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.DOC8fPSI.css +6 -0
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.Dm1nSEgE.css +6 -0
- package/frontend/.svelte-kit/adapter-node/chunks/{Toast.js → Button.js} +2 -17
- package/frontend/.svelte-kit/adapter-node/chunks/ConfirmModal.js +1 -1
- package/frontend/.svelte-kit/adapter-node/chunks/EmptyState.js +1 -1
- package/frontend/.svelte-kit/adapter-node/chunks/common.js +11 -4
- package/frontend/.svelte-kit/adapter-node/chunks/constants.js +1 -1
- package/frontend/.svelte-kit/adapter-node/chunks/internal.js +1 -1
- package/frontend/.svelte-kit/adapter-node/chunks/repository.js +21 -17
- package/frontend/.svelte-kit/adapter-node/entries/pages/_layout.svelte.js +2 -0
- package/frontend/.svelte-kit/adapter-node/entries/pages/reports/_id_/_page.svelte.js +11 -2
- package/frontend/.svelte-kit/adapter-node/entries/pages/scheduled-tests/_page.svelte.js +2 -5
- package/frontend/.svelte-kit/adapter-node/entries/pages/settings/_page.svelte.js +300 -253
- package/frontend/.svelte-kit/adapter-node/entries/pages/test-repository/_page.svelte.js +55 -36
- package/frontend/.svelte-kit/adapter-node/entries/pages/test-repository/runs/_id_/_page.svelte.js +3 -6
- package/frontend/.svelte-kit/adapter-node/entries/pages/test-repository/suites/_id_/_page.svelte.js +15 -17
- package/frontend/.svelte-kit/adapter-node/manifest-full.js +1 -1
- package/frontend/.svelte-kit/adapter-node/manifest.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/0.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/1.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/10.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/11.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/12.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/2.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/3.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/4.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/5.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/6.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/7.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/8.js +2 -2
- package/frontend/.svelte-kit/adapter-node/nodes/9.js +1 -1
- package/frontend/.svelte-kit/generated/server/internal.js +1 -1
- package/frontend/.svelte-kit/output/client/.vite/manifest.json +459 -412
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/{0.CmJTG5mP.css → 0.BEqlRAtD.css} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/10.DC0wD6w_.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/12.Dm1nSEgE.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/6.DdQVxyZK.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/8.DOC8fPSI.css +6 -0
- package/frontend/.svelte-kit/{adapter-node/_app/immutable/assets/Toast.DENwYn3g.css → output/client/_app/immutable/assets/Button.C1zsJAUG.css} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/ExportMenu.DsJQ1Qua.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/{_layout.HkPnpRl9.css → _layout.B4TT3qNx.css} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.4bPj83tb.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.BwgtEi_a.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.DOC8fPSI.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.Dm1nSEgE.css +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{C2_rQ4aY.js → 3MBVX4Yb.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/CfkUW1vA.js → .svelte-kit/output/client/_app/immutable/chunks/6VDMVB5U.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/B0_HKFXV.js → .svelte-kit/output/client/_app/immutable/chunks/B2lVGNml.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/CboYYxrf.js → .svelte-kit/output/client/_app/immutable/chunks/BCh31ecr.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BDb8hZHu.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{CeH-RgMb.js → BIf7lHIm.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BIjh3jiU.js +8 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BTV7EXZb.js +11 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{DAmOMhaf.js → BWGoOYU0.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BdBJfKUi.js +6 -0
- package/frontend/{build/client/_app/immutable/chunks/BAvTKHxT.js → .svelte-kit/output/client/_app/immutable/chunks/BnXsGCy3.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BtTNggoY.js +8 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/C4Z3p6jE.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CNpD-yNH.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CUPWZLIN.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{Ck6C3Fl0.js → CXQhSK1T.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/C00jnSU2.js → .svelte-kit/output/client/_app/immutable/chunks/CtgCYzu5.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/D72giPTu.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{GDjpV3r7.js → DDx3no2z.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/D_thT6pZ.js +6 -0
- package/frontend/{build/client/_app/immutable/chunks/C8S7qQjQ.js → .svelte-kit/output/client/_app/immutable/chunks/DeiVAbyn.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DiexacSB.js +6 -0
- package/frontend/{build/client/_app/immutable/chunks/d2K3c8Kx.js → .svelte-kit/output/client/_app/immutable/chunks/Dlymnwnf.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/Cgw8TOOy.js → .svelte-kit/output/client/_app/immutable/chunks/gCgJ3Cwu.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/BJ_ypKl3.js → .svelte-kit/output/client/_app/immutable/chunks/htkwR9b-.js} +1 -1
- package/frontend/{build/client/_app/immutable/chunks/C7uEQ-LH.js → .svelte-kit/output/client/_app/immutable/chunks/iL4SCrvY.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{CoTEzvLa.js → kFFd6Oo5.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/pNZogwkM.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{DWzEJYIF.js → prk8PJfA.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/qvxlSGpD.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/r_d1Iu0W.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{B00vxw57.js → tsOANt55.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/zXSLr47H.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/entry/app.DgdSwPuN.js +7 -0
- package/frontend/{build/client/_app/immutable/entry/start.BXOLyf9R.js → .svelte-kit/output/client/_app/immutable/entry/start.D_lhI48n.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/0.D2F-i1I5.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{1.BI97il3r.js → 1.JIF2GfHq.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/10.A0AHLfOt.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/11.CVSPxlqY.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/12.CnAR0AQb.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{2.Dr6yugXz.js → 2.Bno00fr8.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/3.B6KwaCsM.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{4.DDADylfF.js → 4.BX70m3v6.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/5.UIFh06yr.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/6.DZ1OV24V.js +10 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/7.B_XntBZ1.js +6 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/8.CVHgJw-E.js +9 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{9.BOK0GNF_.js → 9.DDDQPmMj.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/version.json +1 -1
- package/frontend/.svelte-kit/output/server/.vite/manifest.json +48 -40
- package/frontend/{build/client/_app/immutable/assets/Toast.DENwYn3g.css → .svelte-kit/output/server/_app/immutable/assets/Button.C1zsJAUG.css} +1 -1
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/ExportMenu.DsJQ1Qua.css +6 -0
- package/frontend/.svelte-kit/{adapter-node/_app/immutable/assets/_layout.HkPnpRl9.css → output/server/_app/immutable/assets/_layout.B4TT3qNx.css} +1 -1
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.4bPj83tb.css +6 -0
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.BwgtEi_a.css +6 -0
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.DOC8fPSI.css +6 -0
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.Dm1nSEgE.css +6 -0
- package/frontend/.svelte-kit/output/server/chunks/{Toast.js → Button.js} +2 -17
- package/frontend/.svelte-kit/output/server/chunks/ConfirmModal.js +1 -1
- package/frontend/.svelte-kit/output/server/chunks/EmptyState.js +1 -1
- package/frontend/.svelte-kit/output/server/chunks/common.js +11 -4
- package/frontend/.svelte-kit/output/server/chunks/constants.js +1 -1
- package/frontend/.svelte-kit/output/server/chunks/internal.js +1 -1
- package/frontend/.svelte-kit/output/server/chunks/repository.js +21 -17
- package/frontend/.svelte-kit/output/server/entries/pages/_layout.svelte.js +2 -0
- package/frontend/.svelte-kit/output/server/entries/pages/reports/_id_/_page.svelte.js +11 -2
- package/frontend/.svelte-kit/output/server/entries/pages/scheduled-tests/_page.svelte.js +2 -5
- package/frontend/.svelte-kit/output/server/entries/pages/settings/_page.svelte.js +300 -253
- package/frontend/.svelte-kit/output/server/entries/pages/test-repository/_page.svelte.js +55 -36
- package/frontend/.svelte-kit/output/server/entries/pages/test-repository/runs/_id_/_page.svelte.js +3 -6
- package/frontend/.svelte-kit/output/server/entries/pages/test-repository/suites/_id_/_page.svelte.js +15 -17
- package/frontend/.svelte-kit/output/server/manifest-full.js +1 -1
- package/frontend/.svelte-kit/output/server/manifest.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/0.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/1.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/10.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/11.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/12.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/2.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/3.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/4.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/5.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/6.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/7.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/8.js +2 -2
- package/frontend/.svelte-kit/output/server/nodes/9.js +1 -1
- package/frontend/build/client/_app/immutable/assets/{0.CmJTG5mP.css → 0.BEqlRAtD.css} +1 -1
- package/frontend/build/client/_app/immutable/assets/0.BEqlRAtD.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/0.BEqlRAtD.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/10.DC0wD6w_.css +6 -0
- package/frontend/build/client/_app/immutable/assets/10.DC0wD6w_.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/10.DC0wD6w_.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/12.Dm1nSEgE.css +6 -0
- package/frontend/build/client/_app/immutable/assets/12.Dm1nSEgE.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/12.Dm1nSEgE.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/6.DdQVxyZK.css +6 -0
- package/frontend/build/client/_app/immutable/assets/6.DdQVxyZK.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/6.DdQVxyZK.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/8.DOC8fPSI.css +6 -0
- package/frontend/build/client/_app/immutable/assets/8.DOC8fPSI.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/8.DOC8fPSI.css.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/assets/Toast.DENwYn3g.css → build/client/_app/immutable/assets/Button.C1zsJAUG.css} +1 -1
- package/frontend/build/client/_app/immutable/assets/Button.C1zsJAUG.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/Button.C1zsJAUG.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/ExportMenu.DsJQ1Qua.css +6 -0
- package/frontend/build/client/_app/immutable/assets/ExportMenu.DsJQ1Qua.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/ExportMenu.DsJQ1Qua.css.gz +0 -0
- package/frontend/{.svelte-kit/output/server/_app/immutable/assets/_layout.HkPnpRl9.css → build/client/_app/immutable/assets/_layout.B4TT3qNx.css} +1 -1
- package/frontend/build/client/_app/immutable/assets/_layout.B4TT3qNx.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_layout.B4TT3qNx.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.4bPj83tb.css +6 -0
- package/frontend/build/client/_app/immutable/assets/_page.4bPj83tb.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.4bPj83tb.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.BwgtEi_a.css +6 -0
- package/frontend/build/client/_app/immutable/assets/_page.BwgtEi_a.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.BwgtEi_a.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.DOC8fPSI.css +6 -0
- package/frontend/build/client/_app/immutable/assets/_page.DOC8fPSI.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.DOC8fPSI.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.Dm1nSEgE.css +6 -0
- package/frontend/build/client/_app/immutable/assets/_page.Dm1nSEgE.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.Dm1nSEgE.css.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{C2_rQ4aY.js → 3MBVX4Yb.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/3MBVX4Yb.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/3MBVX4Yb.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/CfkUW1vA.js → build/client/_app/immutable/chunks/6VDMVB5U.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/6VDMVB5U.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/6VDMVB5U.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/B0_HKFXV.js → build/client/_app/immutable/chunks/B2lVGNml.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/B2lVGNml.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/B2lVGNml.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/CboYYxrf.js → build/client/_app/immutable/chunks/BCh31ecr.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/BCh31ecr.js.br +6 -0
- package/frontend/build/client/_app/immutable/chunks/BCh31ecr.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BDb8hZHu.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/BDb8hZHu.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BDb8hZHu.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{CeH-RgMb.js → BIf7lHIm.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/BIf7lHIm.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/{CeH-RgMb.js.gz → BIf7lHIm.js.gz} +0 -0
- package/frontend/build/client/_app/immutable/chunks/BIjh3jiU.js +8 -0
- package/frontend/build/client/_app/immutable/chunks/BIjh3jiU.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BIjh3jiU.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BTV7EXZb.js +11 -0
- package/frontend/build/client/_app/immutable/chunks/BTV7EXZb.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BTV7EXZb.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{DAmOMhaf.js → BWGoOYU0.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/BWGoOYU0.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BWGoOYU0.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BdBJfKUi.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/BdBJfKUi.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BdBJfKUi.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/BAvTKHxT.js → build/client/_app/immutable/chunks/BnXsGCy3.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/BnXsGCy3.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BnXsGCy3.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BtTNggoY.js +8 -0
- package/frontend/build/client/_app/immutable/chunks/BtTNggoY.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BtTNggoY.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/C4Z3p6jE.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/C4Z3p6jE.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/C4Z3p6jE.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CNpD-yNH.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/CNpD-yNH.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CNpD-yNH.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CUPWZLIN.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/CUPWZLIN.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CUPWZLIN.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{Ck6C3Fl0.js → CXQhSK1T.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/CXQhSK1T.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CXQhSK1T.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/C00jnSU2.js → build/client/_app/immutable/chunks/CtgCYzu5.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/CtgCYzu5.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CtgCYzu5.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/D72giPTu.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/D72giPTu.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/D72giPTu.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{GDjpV3r7.js → DDx3no2z.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/DDx3no2z.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DDx3no2z.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/D_thT6pZ.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/D_thT6pZ.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/D_thT6pZ.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/C8S7qQjQ.js → build/client/_app/immutable/chunks/DeiVAbyn.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/DeiVAbyn.js.br +6 -0
- package/frontend/build/client/_app/immutable/chunks/DeiVAbyn.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DiexacSB.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/DiexacSB.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DiexacSB.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/d2K3c8Kx.js → build/client/_app/immutable/chunks/Dlymnwnf.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/Dlymnwnf.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/Dlymnwnf.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/Cgw8TOOy.js → build/client/_app/immutable/chunks/gCgJ3Cwu.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/gCgJ3Cwu.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/gCgJ3Cwu.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/BJ_ypKl3.js → build/client/_app/immutable/chunks/htkwR9b-.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/htkwR9b-.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/htkwR9b-.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/chunks/C7uEQ-LH.js → build/client/_app/immutable/chunks/iL4SCrvY.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/iL4SCrvY.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/iL4SCrvY.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{CoTEzvLa.js → kFFd6Oo5.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/kFFd6Oo5.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/kFFd6Oo5.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/pNZogwkM.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/pNZogwkM.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/pNZogwkM.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{DWzEJYIF.js → prk8PJfA.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/prk8PJfA.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/prk8PJfA.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/qvxlSGpD.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/qvxlSGpD.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/qvxlSGpD.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/r_d1Iu0W.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/r_d1Iu0W.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/r_d1Iu0W.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{B00vxw57.js → tsOANt55.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/tsOANt55.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/tsOANt55.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/zXSLr47H.js +6 -0
- package/frontend/build/client/_app/immutable/chunks/zXSLr47H.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/zXSLr47H.js.gz +0 -0
- package/frontend/build/client/_app/immutable/entry/app.DgdSwPuN.js +7 -0
- package/frontend/build/client/_app/immutable/entry/app.DgdSwPuN.js.br +0 -0
- package/frontend/build/client/_app/immutable/entry/app.DgdSwPuN.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/entry/start.BXOLyf9R.js → build/client/_app/immutable/entry/start.D_lhI48n.js} +1 -1
- package/frontend/build/client/_app/immutable/entry/{start.BXOLyf9R.js.br → start.D_lhI48n.js.br} +1 -1
- package/frontend/build/client/_app/immutable/entry/{start.BXOLyf9R.js.gz → start.D_lhI48n.js.gz} +0 -0
- package/frontend/build/client/_app/immutable/nodes/0.D2F-i1I5.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/0.D2F-i1I5.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/0.D2F-i1I5.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/{1.BI97il3r.js → 1.JIF2GfHq.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/1.JIF2GfHq.js.br +5 -0
- package/frontend/build/client/_app/immutable/nodes/1.JIF2GfHq.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/10.A0AHLfOt.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/10.A0AHLfOt.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/10.A0AHLfOt.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/11.CVSPxlqY.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/11.CVSPxlqY.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/11.CVSPxlqY.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/12.CnAR0AQb.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/12.CnAR0AQb.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/12.CnAR0AQb.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/{2.Dr6yugXz.js → 2.Bno00fr8.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/2.Bno00fr8.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/2.Bno00fr8.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/3.B6KwaCsM.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/3.B6KwaCsM.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/3.B6KwaCsM.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/{4.DDADylfF.js → 4.BX70m3v6.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/4.BX70m3v6.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/4.BX70m3v6.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/5.UIFh06yr.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/5.UIFh06yr.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/5.UIFh06yr.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/6.DZ1OV24V.js +10 -0
- package/frontend/build/client/_app/immutable/nodes/6.DZ1OV24V.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/6.DZ1OV24V.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/7.B_XntBZ1.js +6 -0
- package/frontend/build/client/_app/immutable/nodes/7.B_XntBZ1.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/7.B_XntBZ1.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/8.CVHgJw-E.js +9 -0
- package/frontend/build/client/_app/immutable/nodes/8.CVHgJw-E.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/8.CVHgJw-E.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/{9.BOK0GNF_.js → 9.DDDQPmMj.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/9.DDDQPmMj.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/9.DDDQPmMj.js.gz +0 -0
- package/frontend/build/client/_app/version.json +1 -1
- package/frontend/build/client/_app/version.json.br +0 -0
- package/frontend/build/client/_app/version.json.gz +0 -0
- package/frontend/build/server/chunks/0-38cvTs44.js +22 -0
- package/frontend/build/server/chunks/0-38cvTs44.js.map +5 -0
- package/frontend/build/server/chunks/{1-B3HpigzL.js → 1-CcYsajvR.js} +2 -2
- package/frontend/build/server/chunks/{1-B3HpigzL.js.map → 1-CcYsajvR.js.map} +1 -1
- package/frontend/build/server/chunks/10-CFR2MUJt.js +14 -0
- package/frontend/build/server/chunks/10-CFR2MUJt.js.map +5 -0
- package/frontend/build/server/chunks/11-BmkiRQ9D.js +14 -0
- package/frontend/build/server/chunks/11-BmkiRQ9D.js.map +5 -0
- package/frontend/build/server/chunks/12-BphJAwnL.js +14 -0
- package/frontend/build/server/chunks/12-BphJAwnL.js.map +5 -0
- package/frontend/build/server/chunks/2-Dalgd2D4.js +14 -0
- package/frontend/build/server/chunks/{2-DioMbKbC.js.map → 2-Dalgd2D4.js.map} +1 -1
- package/frontend/build/server/chunks/3-BvsmdXCj.js +14 -0
- package/frontend/build/server/chunks/{3-CgiZg_9v.js.map → 3-BvsmdXCj.js.map} +1 -1
- package/frontend/build/server/chunks/4-CN18WCEn.js +14 -0
- package/frontend/build/server/chunks/{4-BM-49dbE.js.map → 4-CN18WCEn.js.map} +1 -1
- package/frontend/build/server/chunks/5-BwLYF3xL.js +14 -0
- package/frontend/build/server/chunks/{5-Brx_KOzy.js.map → 5-BwLYF3xL.js.map} +1 -1
- package/frontend/build/server/chunks/6-DTM2N_k3.js +14 -0
- package/frontend/build/server/chunks/6-DTM2N_k3.js.map +5 -0
- package/frontend/build/server/chunks/7-DTNbErSe.js +14 -0
- package/frontend/build/server/chunks/7-DTNbErSe.js.map +5 -0
- package/frontend/build/server/chunks/8-BfCbb7pl.js +14 -0
- package/frontend/build/server/chunks/8-BfCbb7pl.js.map +5 -0
- package/frontend/build/server/chunks/9-CQKepnMw.js +14 -0
- package/frontend/build/server/chunks/{9-BMAKiAem.js.map → 9-CQKepnMw.js.map} +1 -1
- package/frontend/build/server/chunks/{Toast-CHB87F8k.js → Button-B9h9viZX.js} +3 -17
- package/frontend/build/server/chunks/Button-B9h9viZX.js.map +5 -0
- package/frontend/build/server/chunks/{ConfirmModal-CtGWHl0v.js → ConfirmModal-C6Cz898n.js} +2 -2
- package/frontend/build/server/chunks/{ConfirmModal-CtGWHl0v.js.map → ConfirmModal-C6Cz898n.js.map} +1 -1
- package/frontend/build/server/chunks/{EmptyState-Zpm9tJe1.js → EmptyState-CRtsJIv6.js} +2 -2
- package/frontend/build/server/chunks/{EmptyState-Zpm9tJe1.js.map → EmptyState-CRtsJIv6.js.map} +1 -1
- package/frontend/build/server/chunks/{_layout.svelte-C_6nsIpT.js → _layout.svelte-B8vZGefg.js} +6 -7
- package/frontend/build/server/chunks/_layout.svelte-B8vZGefg.js.map +5 -0
- package/frontend/build/server/chunks/{_page.svelte-CTebwzwy.js → _page.svelte-BrnXHxJ4.js} +14 -7
- package/frontend/build/server/chunks/_page.svelte-BrnXHxJ4.js.map +5 -0
- package/frontend/build/server/chunks/_page.svelte-C2WqvInJ.js +898 -0
- package/frontend/build/server/chunks/_page.svelte-C2WqvInJ.js.map +5 -0
- package/frontend/build/server/chunks/{_page.svelte-Bamoq11m.js → _page.svelte-CP6jA1ex.js} +6 -6
- package/frontend/build/server/chunks/{_page.svelte-Bamoq11m.js.map → _page.svelte-CP6jA1ex.js.map} +1 -1
- package/frontend/build/server/chunks/{_page.svelte-CbTrCeeX.js → _page.svelte-D6b-klLK.js} +9 -9
- package/frontend/build/server/chunks/{_page.svelte-CbTrCeeX.js.map → _page.svelte-D6b-klLK.js.map} +1 -1
- package/frontend/build/server/chunks/{_page.svelte-CmJKt3-H.js → _page.svelte-DBjTpquQ.js} +18 -21
- package/frontend/build/server/chunks/_page.svelte-DBjTpquQ.js.map +5 -0
- package/frontend/build/server/chunks/{_page.svelte-DHZhNTPg.js → _page.svelte-DM1_y4Oq.js} +60 -42
- package/frontend/build/server/chunks/_page.svelte-DM1_y4Oq.js.map +5 -0
- package/frontend/build/server/chunks/{_page.svelte-DObTHQe2.js → _page.svelte-Mo-O3HJ5.js} +7 -7
- package/frontend/build/server/chunks/{_page.svelte-DObTHQe2.js.map → _page.svelte-Mo-O3HJ5.js.map} +1 -1
- package/frontend/build/server/chunks/{_page.svelte-CAlhaCzS.js → _page.svelte-mZF4NrfU.js} +9 -12
- package/frontend/build/server/chunks/_page.svelte-mZF4NrfU.js.map +5 -0
- package/frontend/build/server/chunks/{_page.svelte-BNwU9zza.js → _page.svelte-nrlgIFaU.js} +8 -11
- package/frontend/build/server/chunks/_page.svelte-nrlgIFaU.js.map +5 -0
- package/frontend/build/server/chunks/{common-DYYTVHPw.js → common-DS-Z0UFe.js} +7 -2
- package/frontend/build/server/chunks/common-DS-Z0UFe.js.map +5 -0
- package/frontend/build/server/chunks/{constants-BYVkTZ-O.js → constants-OmHysD51.js} +2 -2
- package/frontend/build/server/chunks/{constants-BYVkTZ-O.js.map → constants-OmHysD51.js.map} +1 -1
- package/frontend/build/server/chunks/{format-2pqy61MU.js → format-Bqb0as15.js} +2 -2
- package/frontend/build/server/chunks/{format-2pqy61MU.js.map → format-Bqb0as15.js.map} +1 -1
- package/frontend/build/server/chunks/{reports-WuXG6tj3.js → reports-chCHCDzB.js} +2 -2
- package/frontend/build/server/chunks/{reports-WuXG6tj3.js.map → reports-chCHCDzB.js.map} +1 -1
- package/frontend/build/server/chunks/{reports2-CCpI0xkQ.js → reports2-Dr3zu9CD.js} +2 -2
- package/frontend/build/server/chunks/{reports2-CCpI0xkQ.js.map → reports2-Dr3zu9CD.js.map} +1 -1
- package/frontend/build/server/chunks/{repository-TYWcFuWP.js → repository-DSsWVKEb.js} +5 -3
- package/frontend/build/server/chunks/repository-DSsWVKEb.js.map +5 -0
- package/frontend/build/server/chunks/{runner-D5yCWOsy.js → runner-aaUtOqck.js} +3 -3
- package/frontend/build/server/chunks/{runner-D5yCWOsy.js.map → runner-aaUtOqck.js.map} +1 -1
- package/frontend/build/server/index.js +1 -1
- package/frontend/build/server/index.js.map +1 -1
- package/frontend/build/server/manifest.js +14 -14
- package/frontend/build/server/manifest.js.map +1 -1
- package/frontend/package-lock.json +14 -0
- package/frontend/package.json +2 -0
- package/frontend/src/lib/api/reports.js +7 -0
- package/frontend/src/lib/api/repository.js +28 -0
- package/frontend/src/lib/components/layout/RunnerPanel.svelte +2 -1
- package/frontend/src/lib/components/reports/RecordingPlayer.svelte +172 -2
- package/frontend/src/lib/components/ui/ExportMenu.svelte +154 -0
- package/frontend/src/lib/components/ui/NotificationStack.svelte +177 -0
- package/frontend/src/lib/constants.js +1 -1
- package/frontend/src/lib/copy/common.js +9 -0
- package/frontend/src/lib/copy/reports.js +20 -0
- package/frontend/src/lib/copy/repository.js +4 -0
- package/frontend/src/lib/copy/settings.js +15 -0
- package/frontend/src/lib/stores/notifications.js +23 -0
- package/frontend/src/lib/stores/replayVideo.js +51 -0
- package/frontend/src/lib/utils/download.js +28 -0
- package/frontend/src/lib/utils/replayCapture.js +91 -0
- package/frontend/src/lib/utils/replayVideo.js +111 -0
- package/frontend/src/routes/+layout.svelte +2 -0
- package/frontend/src/routes/reports/[id]/+page.svelte +38 -3
- package/frontend/src/routes/scheduled-tests/+page.svelte +9 -17
- package/frontend/src/routes/settings/+page.svelte +140 -56
- package/frontend/src/routes/test-repository/+page.svelte +44 -16
- package/frontend/src/routes/test-repository/runs/[id]/+page.svelte +14 -23
- package/frontend/src/routes/test-repository/suites/[id]/+page.svelte +38 -22
- package/package.json +1 -1
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.5IJAEAjc.css +0 -6
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.B9H_IpSU.css +0 -6
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.COwSGmmy.css +0 -6
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.Dp6vaGRc.css +0 -6
- package/frontend/.svelte-kit/generated/client/nodes/13.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/10.CdaQUS6U.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/12.B9H_IpSU.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/6.BN5XbLUX.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/8.COwSGmmy.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/Button.FOWXxvc7.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.5IJAEAjc.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.B9H_IpSU.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.COwSGmmy.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.Dp6vaGRc.css +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/2-KCkWZO.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/BbYY_TzB.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/Beula-TH.js +0 -11
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CZWchtW8.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/CegSvdD-.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/Cfmt4_tJ.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DB3YXVFP.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DgQy0SB2.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DiVmSEJa.js +0 -8
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/Dl-F6VCo.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/DuAVKaL3.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/K1h4wprj.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/entry/app.DRjRA71t.js +0 -7
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/0.CRb8Nyce.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/10.kyazROhw.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/11.B3vMgT2v.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/12.DkPcIrgW.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/3.DUiL7fb5.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/5.N4CeDeoD.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/6.DWd93Bv5.js +0 -10
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/7.c8d1cQQM.js +0 -6
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/8.Cvd3MauU.js +0 -9
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.5IJAEAjc.css +0 -6
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.B9H_IpSU.css +0 -6
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.COwSGmmy.css +0 -6
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.Dp6vaGRc.css +0 -6
- package/frontend/build/client/_app/immutable/assets/0.CmJTG5mP.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/0.CmJTG5mP.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/10.CdaQUS6U.css +0 -6
- package/frontend/build/client/_app/immutable/assets/10.CdaQUS6U.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/10.CdaQUS6U.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/12.B9H_IpSU.css +0 -6
- package/frontend/build/client/_app/immutable/assets/12.B9H_IpSU.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/12.B9H_IpSU.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/6.BN5XbLUX.css +0 -6
- package/frontend/build/client/_app/immutable/assets/6.BN5XbLUX.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/6.BN5XbLUX.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/8.COwSGmmy.css +0 -6
- package/frontend/build/client/_app/immutable/assets/8.COwSGmmy.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/8.COwSGmmy.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/Button.FOWXxvc7.css +0 -6
- package/frontend/build/client/_app/immutable/assets/Button.FOWXxvc7.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/Button.FOWXxvc7.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/Toast.DENwYn3g.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/Toast.DENwYn3g.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_layout.HkPnpRl9.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_layout.HkPnpRl9.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.5IJAEAjc.css +0 -6
- package/frontend/build/client/_app/immutable/assets/_page.5IJAEAjc.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.5IJAEAjc.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.B9H_IpSU.css +0 -6
- package/frontend/build/client/_app/immutable/assets/_page.B9H_IpSU.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.B9H_IpSU.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.COwSGmmy.css +0 -6
- package/frontend/build/client/_app/immutable/assets/_page.COwSGmmy.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.COwSGmmy.css.gz +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.Dp6vaGRc.css +0 -6
- package/frontend/build/client/_app/immutable/assets/_page.Dp6vaGRc.css.br +0 -0
- package/frontend/build/client/_app/immutable/assets/_page.Dp6vaGRc.css.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/2-KCkWZO.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/2-KCkWZO.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/2-KCkWZO.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/B00vxw57.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/B00vxw57.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/B0_HKFXV.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/B0_HKFXV.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BAvTKHxT.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BAvTKHxT.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BJ_ypKl3.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BJ_ypKl3.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/BbYY_TzB.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/BbYY_TzB.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/BbYY_TzB.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/Beula-TH.js +0 -11
- package/frontend/build/client/_app/immutable/chunks/Beula-TH.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/Beula-TH.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/C00jnSU2.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/C00jnSU2.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/C2_rQ4aY.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/C2_rQ4aY.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/C7uEQ-LH.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/C7uEQ-LH.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/C8S7qQjQ.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/C8S7qQjQ.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CZWchtW8.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/CZWchtW8.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CZWchtW8.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CboYYxrf.js.br +0 -6
- package/frontend/build/client/_app/immutable/chunks/CboYYxrf.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CeH-RgMb.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CegSvdD-.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/CegSvdD-.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CegSvdD-.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CfkUW1vA.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CfkUW1vA.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/Cfmt4_tJ.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/Cfmt4_tJ.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/Cfmt4_tJ.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/Cgw8TOOy.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/Cgw8TOOy.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/Ck6C3Fl0.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/Ck6C3Fl0.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/CoTEzvLa.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/CoTEzvLa.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DAmOMhaf.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DAmOMhaf.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DB3YXVFP.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/DB3YXVFP.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DB3YXVFP.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DWzEJYIF.js.br +0 -7
- package/frontend/build/client/_app/immutable/chunks/DWzEJYIF.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DgQy0SB2.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/DgQy0SB2.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DgQy0SB2.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DiVmSEJa.js +0 -8
- package/frontend/build/client/_app/immutable/chunks/DiVmSEJa.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DiVmSEJa.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/Dl-F6VCo.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/Dl-F6VCo.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/Dl-F6VCo.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/DuAVKaL3.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/DuAVKaL3.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DuAVKaL3.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/GDjpV3r7.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/GDjpV3r7.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/K1h4wprj.js +0 -6
- package/frontend/build/client/_app/immutable/chunks/K1h4wprj.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/K1h4wprj.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/d2K3c8Kx.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/d2K3c8Kx.js.gz +0 -0
- package/frontend/build/client/_app/immutable/entry/app.DRjRA71t.js +0 -7
- package/frontend/build/client/_app/immutable/entry/app.DRjRA71t.js.br +0 -0
- package/frontend/build/client/_app/immutable/entry/app.DRjRA71t.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/0.CRb8Nyce.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/0.CRb8Nyce.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/0.CRb8Nyce.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/1.BI97il3r.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/1.BI97il3r.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/10.kyazROhw.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/10.kyazROhw.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/10.kyazROhw.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/11.B3vMgT2v.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/11.B3vMgT2v.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/11.B3vMgT2v.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/12.DkPcIrgW.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/12.DkPcIrgW.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/12.DkPcIrgW.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/2.Dr6yugXz.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/2.Dr6yugXz.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/3.DUiL7fb5.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/3.DUiL7fb5.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/3.DUiL7fb5.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/4.DDADylfF.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/4.DDADylfF.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/5.N4CeDeoD.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/5.N4CeDeoD.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/5.N4CeDeoD.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/6.DWd93Bv5.js +0 -10
- package/frontend/build/client/_app/immutable/nodes/6.DWd93Bv5.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/6.DWd93Bv5.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/7.c8d1cQQM.js +0 -6
- package/frontend/build/client/_app/immutable/nodes/7.c8d1cQQM.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/7.c8d1cQQM.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/8.Cvd3MauU.js +0 -9
- package/frontend/build/client/_app/immutable/nodes/8.Cvd3MauU.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/8.Cvd3MauU.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/9.BOK0GNF_.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/9.BOK0GNF_.js.gz +0 -0
- package/frontend/build/server/chunks/0-_rljpEay.js +0 -22
- package/frontend/build/server/chunks/0-_rljpEay.js.map +0 -5
- package/frontend/build/server/chunks/10-BQBBYpxV.js +0 -14
- package/frontend/build/server/chunks/10-BQBBYpxV.js.map +0 -5
- package/frontend/build/server/chunks/11-10xw0TIP.js +0 -14
- package/frontend/build/server/chunks/11-10xw0TIP.js.map +0 -5
- package/frontend/build/server/chunks/12-CZ3_Uw3c.js +0 -14
- package/frontend/build/server/chunks/12-CZ3_Uw3c.js.map +0 -5
- package/frontend/build/server/chunks/2-DioMbKbC.js +0 -14
- package/frontend/build/server/chunks/3-CgiZg_9v.js +0 -14
- package/frontend/build/server/chunks/4-BM-49dbE.js +0 -14
- package/frontend/build/server/chunks/5-Brx_KOzy.js +0 -14
- package/frontend/build/server/chunks/6-C5swINND.js +0 -14
- package/frontend/build/server/chunks/6-C5swINND.js.map +0 -5
- package/frontend/build/server/chunks/7-B8e4y3CB.js +0 -14
- package/frontend/build/server/chunks/7-B8e4y3CB.js.map +0 -5
- package/frontend/build/server/chunks/8-BZtkZMIZ.js +0 -14
- package/frontend/build/server/chunks/8-BZtkZMIZ.js.map +0 -5
- package/frontend/build/server/chunks/9-BMAKiAem.js +0 -14
- package/frontend/build/server/chunks/Toast-CHB87F8k.js.map +0 -5
- package/frontend/build/server/chunks/_layout.svelte-C_6nsIpT.js.map +0 -5
- package/frontend/build/server/chunks/_page.svelte-BNwU9zza.js.map +0 -5
- package/frontend/build/server/chunks/_page.svelte-CAlhaCzS.js.map +0 -5
- package/frontend/build/server/chunks/_page.svelte-CTebwzwy.js.map +0 -5
- package/frontend/build/server/chunks/_page.svelte-CmJKt3-H.js.map +0 -5
- package/frontend/build/server/chunks/_page.svelte-DHZhNTPg.js.map +0 -5
- package/frontend/build/server/chunks/_page.svelte-ywHy9KCh.js +0 -852
- package/frontend/build/server/chunks/_page.svelte-ywHy9KCh.js.map +0 -5
- package/frontend/build/server/chunks/common-DYYTVHPw.js.map +0 -5
- package/frontend/build/server/chunks/repository-TYWcFuWP.js.map +0 -5
- package/frontend/src/lib/components/ui/Toast.svelte +0 -24
package/.env
CHANGED
package/CLAUDE.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
- **Frontend**: SvelteKit 5, port 5173. ESM, no TypeScript.
|
|
6
6
|
- **Backend**: Express + Socket.io, port 3001. CommonJS (`require`/`module.exports`).
|
|
7
7
|
- **Database**: PostgreSQL via Prisma ORM (`backend/services/prisma.js`).
|
|
8
|
-
- **Infrastructure**: Docker Compose (`docker-compose.yml`) for the server stack.
|
|
8
|
+
- **Infrastructure**: Docker Compose (`docker-compose.yml`) for the server stack. Nodes run as a bare `PLUM_MODE=node` Node process (started by `plum node start` / the dev `manage-nodes` script), not via Docker.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
## Requirements
|
|
17
17
|
|
|
18
18
|
- [Node.js](https://nodejs.org) v18 or higher
|
|
19
|
-
- [Docker](https://www.docker.com) — required for `plum server start` (the web UI stack).
|
|
19
|
+
- [Docker](https://www.docker.com) — required for `plum server start` (the web UI stack). Nodes (`plum node start`) run as a plain Node process and **do not need Docker**.
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
@@ -42,7 +42,7 @@ plum run-test
|
|
|
42
42
|
plum server start
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
On first start, Plum asks whether you're setting up on a **local machine** or a **production / network server**, and whether to use the default ports (backend `3001`, frontend `3002`). Then open **http://localhost:3002** and sign in with the account you create.
|
|
46
46
|
|
|
47
47
|
### For contributors
|
|
48
48
|
|
|
@@ -53,7 +53,7 @@ npm run init # installs all monorepo dependencies
|
|
|
53
53
|
npm run docker:up # builds and starts the full stack
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
The UI is available at **http://localhost:
|
|
56
|
+
The UI is available at **http://localhost:3002**. For fast HMR while developing the frontend, run the Vite dev server outside Docker (`http://localhost:5173`) — see the **Development** section below.
|
|
57
57
|
|
|
58
58
|
---
|
|
59
59
|
|
|
@@ -71,7 +71,7 @@ Full documentation is available at:
|
|
|
71
71
|
| [Running Tests Locally](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/running-tests-locally-GGhFcqaAQ8) | `plum run-test` flags, parallel runs, debugging tips |
|
|
72
72
|
| [Retrying Flaky Tests](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/retrying-flaky-tests-NXwRF5SXru) | Auto-retry failed scenarios, global setting, report badges |
|
|
73
73
|
| [Setting Up the Server](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/setting-up-the-server-vj0Ab1kJVs) | Production server setup, reverse proxy (Nginx/Caddy), Docker |
|
|
74
|
-
| [Setting Up Nodes](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/setting-up-nodes-dtmekJGJia) |
|
|
74
|
+
| [Setting Up Nodes](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/setting-up-nodes-dtmekJGJia) | Nodes, systemd service, managing nodes |
|
|
75
75
|
| [Integrations](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/integrations-qfiqfmdP0j) | Discord & Slack webhook notifications, CI/external triggers |
|
|
76
76
|
| [Backup](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/backup-RNNObJfct9) | Backup strategy |
|
|
77
77
|
|
|
@@ -87,7 +87,7 @@ Full documentation is available at:
|
|
|
87
87
|
| `plum server stop` | Stop the server (data preserved) |
|
|
88
88
|
| `plum server reconfig` | Re-enter server settings without starting |
|
|
89
89
|
| `plum update` | Update Plum, then restart every registered server and node on this machine (asks before each) |
|
|
90
|
-
| `plum node start [name]` | Register a
|
|
90
|
+
| `plum node start [name]` | Register a node with the server and start it on this machine |
|
|
91
91
|
| `plum node list` | List this machine's nodes and their status |
|
|
92
92
|
| `plum node restart [name]` | Stop, refresh dependencies, and restart a node |
|
|
93
93
|
| `plum node stop [name]` | Stop a node |
|
|
@@ -100,7 +100,7 @@ Full documentation is available at:
|
|
|
100
100
|
| `plum run-test --help` | Show usage for `run-test` |
|
|
101
101
|
| `plum create-step` | Interactively scaffold a new step definition |
|
|
102
102
|
| `plum create-test` | Interactively scaffold a full feature (`.feature` + Page + Steps) |
|
|
103
|
-
| `plum manage-
|
|
103
|
+
| `plum manage-nodes` | Open the interactive node management menu |
|
|
104
104
|
|
|
105
105
|
---
|
|
106
106
|
|
|
@@ -133,7 +133,7 @@ npm test -- @TC-001 # run a specific scenario
|
|
|
133
133
|
npm test -- --parallel 4 # run in parallel
|
|
134
134
|
npm run create-step # scaffold a step definition
|
|
135
135
|
npm run create-test # scaffold a full test from template
|
|
136
|
-
npm run manage-
|
|
136
|
+
npm run manage-nodes # open the node management menu
|
|
137
137
|
```
|
|
138
138
|
|
|
139
139
|
### Test file locations
|
package/backend/app.js
CHANGED
|
@@ -8,7 +8,7 @@ const cors = require('cors');
|
|
|
8
8
|
const { isNodeMode } = require('./constants/env');
|
|
9
9
|
const app = express();
|
|
10
10
|
|
|
11
|
-
app.use(cors({ origin: '*' }));
|
|
11
|
+
app.use(cors({ origin: '*', exposedHeaders: ['Content-Disposition'] }));
|
|
12
12
|
// Dispatching a run to a node ships the whole tests/ tree (base64-encoded,
|
|
13
13
|
// fixtures included) as one JSON body — Express's 100kb default 413s well
|
|
14
14
|
// before a real test suite does.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// A leading =, +, or - makes a spreadsheet treat the cell as a formula. (@ is
|
|
7
|
+
// left alone — every Cucumber tag starts with one.)
|
|
8
|
+
const FORMULA_LEADS = /^[=+-]/;
|
|
9
|
+
|
|
10
|
+
function cell(value) {
|
|
11
|
+
let s = value === null || value === undefined ? '' : String(value);
|
|
12
|
+
if (FORMULA_LEADS.test(s)) s = `'${s}`;
|
|
13
|
+
return /[",\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// BOM-prefixed so Excel detects UTF-8. `rows[0]` is the header.
|
|
17
|
+
function toCsv(rows) {
|
|
18
|
+
return '' + rows.map((r) => r.map(cell).join(',')).join('\r\n') + '\r\n';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = { toCsv, cell };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const slug = (s) =>
|
|
7
|
+
String(s)
|
|
8
|
+
.replace(/[^a-z0-9]+/gi, '-')
|
|
9
|
+
.replace(/^-+|-+$/g, '')
|
|
10
|
+
.toLowerCase() || 'export';
|
|
11
|
+
|
|
12
|
+
const CONTENT_TYPE = {
|
|
13
|
+
json: 'application/json; charset=utf-8',
|
|
14
|
+
csv: 'text/csv; charset=utf-8'
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const exportFormat = (req) => (String(req.query.format).toLowerCase() === 'csv' ? 'csv' : 'json');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Renders and sends one of `renderers` (keyed by format) as a downloadable
|
|
21
|
+
* file. Each renderer is a function returning the body (or a promise of it);
|
|
22
|
+
* only the requested format's renderer runs.
|
|
23
|
+
*/
|
|
24
|
+
async function sendExport(res, { format, filenameBase, ...renderers }) {
|
|
25
|
+
if (typeof renderers[format] !== 'function') {
|
|
26
|
+
return res.status(400).json({ error: `Unsupported export format: ${format}` });
|
|
27
|
+
}
|
|
28
|
+
const name = `${slug(filenameBase)}-${new Date().toISOString().slice(0, 10)}.${format}`;
|
|
29
|
+
res.setHeader('Content-Disposition', `attachment; filename="${name}"`);
|
|
30
|
+
res.setHeader('Content-Type', CONTENT_TYPE[format]);
|
|
31
|
+
|
|
32
|
+
const body = await renderers[format]();
|
|
33
|
+
if (format === 'json') return res.send(JSON.stringify(body, null, 2));
|
|
34
|
+
res.send(body);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = { sendExport, exportFormat };
|
|
@@ -68,7 +68,7 @@ async function bootstrapMcpKey(isNodeMode) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
function logServerReady(port, isNodeMode) {
|
|
71
|
-
console.log(`Backend running on port ${port}${isNodeMode ? ' (node
|
|
71
|
+
console.log(`Backend running on port ${port}${isNodeMode ? ' (node mode)' : ''}`);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// Dispatches to the node-runner startup path or the full-server startup path
|
|
@@ -83,7 +83,7 @@ async function onServerListening({ port, io, testsDir, isNodeMode }) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
function handleNodeModeStartup(port) {
|
|
86
|
-
// Self-register PID so manage-
|
|
86
|
+
// Self-register PID so manage-nodes can track and stop this process.
|
|
87
87
|
const runnerId = process.env.RUNNER_ID;
|
|
88
88
|
if (!runnerId) return;
|
|
89
89
|
|
|
@@ -19,8 +19,10 @@ const CONFIG_FILENAME = '.plum-server.json';
|
|
|
19
19
|
function defaults() {
|
|
20
20
|
return {
|
|
21
21
|
headless: false,
|
|
22
|
+
// 'local' derives localhost URLs; 'production' takes operator-supplied ones.
|
|
23
|
+
mode: 'local',
|
|
22
24
|
backendPort: '3001',
|
|
23
|
-
frontendPort: '
|
|
25
|
+
frontendPort: '3002',
|
|
24
26
|
// Public URLs the browser actually uses. Left blank until the user sets
|
|
25
27
|
// them (e.g. behind a reverse proxy) — resolved to a localhost default
|
|
26
28
|
// at the call site otherwise.
|
|
@@ -56,10 +58,10 @@ function loadServerConfig(dir) {
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
function saveServerConfig(dir, cfg) {
|
|
59
|
-
const { headless, backendPort, frontendPort, apiUrl, uiUrl } = cfg;
|
|
61
|
+
const { headless, mode, backendPort, frontendPort, apiUrl, uiUrl } = cfg;
|
|
60
62
|
fs.writeFileSync(
|
|
61
63
|
configPath(dir),
|
|
62
|
-
JSON.stringify({ headless, backendPort, frontendPort, apiUrl, uiUrl }, null, 2) + '\n',
|
|
64
|
+
JSON.stringify({ headless, mode, backendPort, frontendPort, apiUrl, uiUrl }, null, 2) + '\n',
|
|
63
65
|
'utf8'
|
|
64
66
|
);
|
|
65
67
|
}
|
package/backend/mcp/server.js
CHANGED
|
@@ -32,6 +32,7 @@ const testSuiteService = require('../services/testSuiteService');
|
|
|
32
32
|
const testCaseService = require('../services/testCaseService');
|
|
33
33
|
const triggerService = require('../services/triggerService');
|
|
34
34
|
const reportService = require('../services/reportService');
|
|
35
|
+
const exportService = require('../services/exportService');
|
|
35
36
|
|
|
36
37
|
// ---------------------------------------------------------------------------
|
|
37
38
|
// Polling helper for test runs
|
|
@@ -506,6 +507,49 @@ function createMcpServer({ userId }) {
|
|
|
506
507
|
}
|
|
507
508
|
);
|
|
508
509
|
|
|
510
|
+
// -- Exports --------------------------------------------------------------
|
|
511
|
+
|
|
512
|
+
server.tool(
|
|
513
|
+
'export_test_cases',
|
|
514
|
+
'Export test cases as CSV (one row per step) or JSON. Scope: the whole repository, one suite, or one test run (a run also carries each case’s recorded result). The JSON form can be re-imported.',
|
|
515
|
+
{
|
|
516
|
+
format: z.enum(['csv', 'json']).describe('csv or json'),
|
|
517
|
+
scope: z.enum(['all', 'suite', 'run']).optional().describe('Default: all'),
|
|
518
|
+
id: z
|
|
519
|
+
.string()
|
|
520
|
+
.optional()
|
|
521
|
+
.describe('Suite or run id — required when scope is "suite" or "run"')
|
|
522
|
+
},
|
|
523
|
+
async ({ format, scope = 'all', id }) => {
|
|
524
|
+
if ((scope === 'suite' || scope === 'run') && !id) {
|
|
525
|
+
throw new Error(`scope "${scope}" needs an id`);
|
|
526
|
+
}
|
|
527
|
+
const data = await exportService.buildTestCaseExport(scope, {
|
|
528
|
+
suiteId: id,
|
|
529
|
+
runId: id
|
|
530
|
+
});
|
|
531
|
+
if (!data) throw new Error('Nothing found for that scope/id');
|
|
532
|
+
const text =
|
|
533
|
+
format === 'csv' ? exportService.testCaseCsv(data) : JSON.stringify(data, null, 2);
|
|
534
|
+
return { content: [{ type: 'text', text }] };
|
|
535
|
+
}
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
server.tool(
|
|
539
|
+
'export_report',
|
|
540
|
+
'Export a Plum test report as CSV (one row per step) or a minimal JSON summary suitable for sharing.',
|
|
541
|
+
{
|
|
542
|
+
reportId: z.number().int().describe('Numeric report ID'),
|
|
543
|
+
format: z.enum(['csv', 'json']).describe('csv or json')
|
|
544
|
+
},
|
|
545
|
+
async ({ reportId, format }) => {
|
|
546
|
+
const data = await exportService.buildReportExport(reportId);
|
|
547
|
+
if (!data) throw new Error('Report not found');
|
|
548
|
+
const text = format === 'csv' ? exportService.reportCsv(data) : JSON.stringify(data, null, 2);
|
|
549
|
+
return { content: [{ type: 'text', text }] };
|
|
550
|
+
}
|
|
551
|
+
);
|
|
552
|
+
|
|
509
553
|
return server;
|
|
510
554
|
}
|
|
511
555
|
|
|
@@ -10,7 +10,7 @@ const { AUTH_SCHEME } = require('../lib/authHeader');
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Accepts either a logged-in admin (web Settings UI) or any currently-registered
|
|
13
|
-
* runner's own token (manage-
|
|
13
|
+
* runner's own token (manage-nodes.mjs and node self-registration have no
|
|
14
14
|
* browser session/JWT to present, but every runner already has a token
|
|
15
15
|
* generated at registration — reuse it instead of a second credential).
|
|
16
16
|
*/
|
package/backend/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"create-step": "node config/scripts/create-step.mjs",
|
|
8
8
|
"create-env": "node services/envService.js",
|
|
9
9
|
"test": "node config/scripts/run-tests.js",
|
|
10
|
-
"manage-
|
|
10
|
+
"manage-nodes": "node scripts/manage-nodes.mjs",
|
|
11
11
|
"create-test": "node config/scripts/create-test.mjs"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [],
|
|
@@ -6,6 +6,25 @@
|
|
|
6
6
|
const express = require('express');
|
|
7
7
|
const router = express.Router();
|
|
8
8
|
const reportService = require('../services/reportService');
|
|
9
|
+
const exportService = require('../services/exportService');
|
|
10
|
+
const { sendExport, exportFormat } = require('../lib/exportResponse');
|
|
11
|
+
|
|
12
|
+
router.get('/:id/export', async (req, res, next) => {
|
|
13
|
+
const id = parseInt(req.params.id, 10);
|
|
14
|
+
if (isNaN(id)) return res.status(400).json({ error: 'Invalid report id' });
|
|
15
|
+
try {
|
|
16
|
+
const data = await exportService.buildReportExport(id);
|
|
17
|
+
if (!data) return res.status(404).json({ error: 'Report not found' });
|
|
18
|
+
await sendExport(res, {
|
|
19
|
+
format: exportFormat(req),
|
|
20
|
+
filenameBase: `report-${id}`,
|
|
21
|
+
json: () => data,
|
|
22
|
+
csv: () => exportService.reportCsv(data)
|
|
23
|
+
});
|
|
24
|
+
} catch (e) {
|
|
25
|
+
next(e);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
9
28
|
|
|
10
29
|
router.get('/', async (req, res) => {
|
|
11
30
|
try {
|
|
@@ -10,7 +10,7 @@ const { jwtAuth } = require('../middleware/jwtAuth');
|
|
|
10
10
|
const { requireAdmin } = require('../middleware/requireAdmin');
|
|
11
11
|
const { runnerOrAdmin } = require('../middleware/runnerOrAdmin');
|
|
12
12
|
|
|
13
|
-
// List/create are hit by headless CLI tools (manage-
|
|
13
|
+
// List/create are hit by headless CLI tools (manage-nodes.mjs, node
|
|
14
14
|
// self-registration) with no session to present — same as before the
|
|
15
15
|
// runner-management routes were locked down. The response never includes
|
|
16
16
|
// runner tokens (toPublicRunner strips them), so listing isn't a secret leak.
|
|
@@ -6,7 +6,19 @@
|
|
|
6
6
|
const express = require('express');
|
|
7
7
|
const router = express.Router();
|
|
8
8
|
const { jwtAuth } = require('../middleware/jwtAuth');
|
|
9
|
+
const { requireAdmin } = require('../middleware/requireAdmin');
|
|
9
10
|
const testCaseService = require('../services/testCaseService');
|
|
11
|
+
const testImportService = require('../services/testImportService');
|
|
12
|
+
|
|
13
|
+
router.post('/import', jwtAuth, requireAdmin, async (req, res, next) => {
|
|
14
|
+
try {
|
|
15
|
+
const result = await testImportService.importTestCases(req.body, req.user.userId);
|
|
16
|
+
res.json(result);
|
|
17
|
+
} catch (e) {
|
|
18
|
+
if (e.status === 400) return res.status(400).json({ error: e.message });
|
|
19
|
+
next(e);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
10
22
|
|
|
11
23
|
router.get('/:id', jwtAuth, async (req, res, next) => {
|
|
12
24
|
try {
|
|
@@ -7,6 +7,23 @@ const express = require('express');
|
|
|
7
7
|
const router = express.Router();
|
|
8
8
|
const { jwtAuth } = require('../middleware/jwtAuth');
|
|
9
9
|
const testRunService = require('../services/testRunService');
|
|
10
|
+
const exportService = require('../services/exportService');
|
|
11
|
+
const { sendExport, exportFormat } = require('../lib/exportResponse');
|
|
12
|
+
|
|
13
|
+
router.get('/:id/export', jwtAuth, async (req, res, next) => {
|
|
14
|
+
try {
|
|
15
|
+
const data = await exportService.buildTestCaseExport('run', { runId: req.params.id });
|
|
16
|
+
if (!data) return res.status(404).json({ error: 'Test run not found' });
|
|
17
|
+
await sendExport(res, {
|
|
18
|
+
format: exportFormat(req),
|
|
19
|
+
filenameBase: `run-${data.run?.title ?? req.params.id}`,
|
|
20
|
+
json: () => data,
|
|
21
|
+
csv: () => exportService.testCaseCsv(data)
|
|
22
|
+
});
|
|
23
|
+
} catch (e) {
|
|
24
|
+
next(e);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
10
27
|
|
|
11
28
|
router.get('/', jwtAuth, async (req, res, next) => {
|
|
12
29
|
try {
|
|
@@ -7,6 +7,40 @@ const express = require('express');
|
|
|
7
7
|
const router = express.Router();
|
|
8
8
|
const { jwtAuth } = require('../middleware/jwtAuth');
|
|
9
9
|
const testSuiteService = require('../services/testSuiteService');
|
|
10
|
+
const exportService = require('../services/exportService');
|
|
11
|
+
const { sendExport, exportFormat } = require('../lib/exportResponse');
|
|
12
|
+
|
|
13
|
+
const testCaseRenderers = (data) => ({
|
|
14
|
+
json: () => data,
|
|
15
|
+
csv: () => exportService.testCaseCsv(data)
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
router.get('/export', jwtAuth, async (req, res, next) => {
|
|
19
|
+
try {
|
|
20
|
+
const data = await exportService.buildTestCaseExport('all');
|
|
21
|
+
await sendExport(res, {
|
|
22
|
+
format: exportFormat(req),
|
|
23
|
+
filenameBase: 'test-cases',
|
|
24
|
+
...testCaseRenderers(data)
|
|
25
|
+
});
|
|
26
|
+
} catch (e) {
|
|
27
|
+
next(e);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
router.get('/:id/export', jwtAuth, async (req, res, next) => {
|
|
32
|
+
try {
|
|
33
|
+
const data = await exportService.buildTestCaseExport('suite', { suiteId: req.params.id });
|
|
34
|
+
if (!data) return res.status(404).json({ error: 'Suite not found' });
|
|
35
|
+
await sendExport(res, {
|
|
36
|
+
format: exportFormat(req),
|
|
37
|
+
filenameBase: `suite-${data.suites[0]?.displayId ?? req.params.id}`,
|
|
38
|
+
...testCaseRenderers(data)
|
|
39
|
+
});
|
|
40
|
+
} catch (e) {
|
|
41
|
+
next(e);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
10
44
|
|
|
11
45
|
router.get('/', jwtAuth, async (req, res, next) => {
|
|
12
46
|
try {
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Interactive manager for
|
|
7
|
+
* Interactive manager for Plum nodes.
|
|
8
8
|
*
|
|
9
|
-
* Registers new
|
|
10
|
-
* node processes
|
|
9
|
+
* Registers new nodes with the Plum primary and starts / stops / restarts the
|
|
10
|
+
* node processes, all from one menu.
|
|
11
11
|
*
|
|
12
|
-
* Usage: node scripts/manage-
|
|
13
|
-
* or: npm run manage-
|
|
12
|
+
* Usage: node scripts/manage-nodes.mjs
|
|
13
|
+
* or: npm run manage-nodes (from the backend directory)
|
|
14
14
|
*
|
|
15
15
|
* Env: PLUM_API_URL primary server API base (default http://localhost:3001)
|
|
16
16
|
*/
|
|
@@ -24,7 +24,7 @@ import runnerProcess from '../lib/runnerProcess.js';
|
|
|
24
24
|
import nodeRegister from '../lib/nodeRegister.js';
|
|
25
25
|
|
|
26
26
|
const { isLocalUrl, parsePort, pruneDead, statusOf, findPidOnPort } = runnerProcess;
|
|
27
|
-
const { generateToken,
|
|
27
|
+
const { generateToken, loadNodeByName } = nodeRegister;
|
|
28
28
|
|
|
29
29
|
const API_URL = process.env.PLUM_API_URL || 'http://localhost:3001';
|
|
30
30
|
|
|
@@ -55,21 +55,6 @@ function authHeaders() {
|
|
|
55
55
|
return RUNNER_TOKEN ? { Authorization: `Bearer ${RUNNER_TOKEN}` } : {};
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
/**
|
|
59
|
-
* When the primary runs in Docker it cannot reach `localhost` on the host —
|
|
60
|
-
* only substitute when the user explicitly enters localhost/127.0.0.1.
|
|
61
|
-
*/
|
|
62
|
-
function resolveNodeUrl(url) {
|
|
63
|
-
try {
|
|
64
|
-
const u = new URL(url);
|
|
65
|
-
if (u.hostname === 'localhost' || u.hostname === '127.0.0.1') {
|
|
66
|
-
u.hostname = 'host.docker.internal';
|
|
67
|
-
}
|
|
68
|
-
return u.toString().replace(/\/+$/, '');
|
|
69
|
-
} catch {}
|
|
70
|
-
return url.replace(/\/+$/, '');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
58
|
async function fetchRunners() {
|
|
74
59
|
const res = await fetch(`${API_URL}/runners`, { headers: authHeaders() });
|
|
75
60
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
@@ -250,29 +235,53 @@ async function runAction(r) {
|
|
|
250
235
|
}
|
|
251
236
|
}
|
|
252
237
|
|
|
253
|
-
async function
|
|
238
|
+
async function addNode() {
|
|
254
239
|
const suggested = `node-${generateToken().slice(0, 6)}`;
|
|
255
240
|
|
|
241
|
+
const mode = await clack.select({
|
|
242
|
+
message: 'Is this node for a production / network setup, or this local machine?',
|
|
243
|
+
options: [
|
|
244
|
+
{ value: 'production', label: 'Production / Network' },
|
|
245
|
+
{ value: 'local', label: 'Local machine' }
|
|
246
|
+
],
|
|
247
|
+
initialValue: 'local'
|
|
248
|
+
});
|
|
249
|
+
if (cancelled(mode)) return;
|
|
250
|
+
|
|
256
251
|
const name = await clack.text({
|
|
257
|
-
message: '
|
|
252
|
+
message: 'Node name or alias — call it whatever you like',
|
|
258
253
|
placeholder: suggested,
|
|
259
254
|
defaultValue: suggested
|
|
260
255
|
});
|
|
261
256
|
if (cancelled(name)) return;
|
|
262
257
|
|
|
263
|
-
let
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
message:
|
|
267
|
-
|
|
268
|
-
|
|
258
|
+
let primary;
|
|
259
|
+
if (mode === 'production') {
|
|
260
|
+
primary = await clack.text({
|
|
261
|
+
message:
|
|
262
|
+
'Plum server backend URL or IP address, including the port (with http:// or https://)',
|
|
263
|
+
placeholder: 'https://plum.example.com'
|
|
269
264
|
});
|
|
270
|
-
if (cancelled(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
265
|
+
if (cancelled(primary)) return;
|
|
266
|
+
primary = String(primary).trim();
|
|
267
|
+
} else {
|
|
268
|
+
const bp = await clack.text({
|
|
269
|
+
message: 'Port your Plum backend runs on (default 3001 — run `docker compose ps` if unsure)',
|
|
270
|
+
placeholder: '3001',
|
|
271
|
+
defaultValue: '3001'
|
|
272
|
+
});
|
|
273
|
+
if (cancelled(bp)) return;
|
|
274
|
+
primary = `http://localhost:${(bp || '3001').trim()}`;
|
|
274
275
|
}
|
|
275
276
|
|
|
277
|
+
const port = await clack.text({
|
|
278
|
+
message:
|
|
279
|
+
'Port this node will listen on — any process already using it will be stopped on start',
|
|
280
|
+
placeholder: '3002',
|
|
281
|
+
defaultValue: '3002'
|
|
282
|
+
});
|
|
283
|
+
if (cancelled(port)) return;
|
|
284
|
+
|
|
276
285
|
const defToken = process.env.NODE_TOKEN || generateToken();
|
|
277
286
|
const token = await clack.text({
|
|
278
287
|
message: 'Auth token',
|
|
@@ -281,15 +290,19 @@ async function addRunner() {
|
|
|
281
290
|
});
|
|
282
291
|
if (cancelled(token)) return;
|
|
283
292
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
+
let url;
|
|
294
|
+
if (mode === 'production') {
|
|
295
|
+
url = await clack.text({
|
|
296
|
+
message: 'URL your Plum server uses to reach this node (e.g. https://node-1.example.com)',
|
|
297
|
+
placeholder: `https://${name}.example.com`
|
|
298
|
+
});
|
|
299
|
+
if (cancelled(url)) return;
|
|
300
|
+
url = String(url).trim().replace(/\/+$/, '');
|
|
301
|
+
} else {
|
|
302
|
+
// Local primary runs in Docker — it reaches a host node via
|
|
303
|
+
// host.docker.internal, not localhost.
|
|
304
|
+
url = `http://host.docker.internal:${port}`;
|
|
305
|
+
}
|
|
293
306
|
|
|
294
307
|
// Exactly `plum node start` — register, start on this machine, verify,
|
|
295
308
|
// persist. One code path for both entry points.
|
|
@@ -297,8 +310,10 @@ async function addRunner() {
|
|
|
297
310
|
plumNode(
|
|
298
311
|
'start',
|
|
299
312
|
name,
|
|
313
|
+
'--mode',
|
|
314
|
+
mode,
|
|
300
315
|
'--primary',
|
|
301
|
-
|
|
316
|
+
primary,
|
|
302
317
|
'--url',
|
|
303
318
|
url,
|
|
304
319
|
'--port',
|
|
@@ -314,22 +329,22 @@ async function addRunner() {
|
|
|
314
329
|
}
|
|
315
330
|
|
|
316
331
|
async function main() {
|
|
317
|
-
clack.intro(pc.bgMagenta(pc.white(' 🟣 Plum — Manage
|
|
332
|
+
clack.intro(pc.bgMagenta(pc.white(' 🟣 Plum — Manage Nodes ')));
|
|
318
333
|
|
|
319
334
|
if (!RUNNER_TOKEN) {
|
|
320
335
|
clack.log.warn(
|
|
321
|
-
'No
|
|
322
|
-
'Run `plum manage-
|
|
336
|
+
'No node token available — stop / restart / delete will be rejected.\n' +
|
|
337
|
+
'Run `plum manage-nodes` on the primary host, or from a folder where you started a node.'
|
|
323
338
|
);
|
|
324
339
|
}
|
|
325
340
|
|
|
326
341
|
for (;;) {
|
|
327
342
|
const s = clack.spinner();
|
|
328
|
-
s.start(`Loading
|
|
343
|
+
s.start(`Loading nodes from ${API_URL}...`);
|
|
329
344
|
let runners;
|
|
330
345
|
try {
|
|
331
346
|
runners = await describeRunners();
|
|
332
|
-
s.stop(`
|
|
347
|
+
s.stop(`Nodes at ${API_URL}`);
|
|
333
348
|
} catch (e) {
|
|
334
349
|
s.stop(pc.red(`Could not reach Plum server at ${API_URL}`));
|
|
335
350
|
clack.log.error(e.message);
|
|
@@ -337,17 +352,17 @@ async function main() {
|
|
|
337
352
|
process.exit(1);
|
|
338
353
|
}
|
|
339
354
|
|
|
340
|
-
if (runners.length === 0) clack.log.info(pc.dim('No
|
|
355
|
+
if (runners.length === 0) clack.log.info(pc.dim('No nodes registered yet.'));
|
|
341
356
|
|
|
342
357
|
const choice = await clack.select({
|
|
343
|
-
message: runners.length ? 'Select a
|
|
358
|
+
message: runners.length ? 'Select a node' : 'No nodes yet',
|
|
344
359
|
options: [
|
|
345
360
|
...runners.map((r) => ({
|
|
346
361
|
value: r.id,
|
|
347
362
|
label: r.name,
|
|
348
363
|
hint: statusBadge(r)
|
|
349
364
|
})),
|
|
350
|
-
{ value: '__add__', label: pc.green('+ Add new
|
|
365
|
+
{ value: '__add__', label: pc.green('+ Add new node') },
|
|
351
366
|
{ value: '__refresh__', label: pc.cyan('↻ Refresh') },
|
|
352
367
|
{ value: '__quit__', label: pc.dim('Quit') }
|
|
353
368
|
]
|
|
@@ -356,7 +371,7 @@ async function main() {
|
|
|
356
371
|
if (cancelled(choice) || choice === '__quit__') break;
|
|
357
372
|
if (choice === '__refresh__') continue;
|
|
358
373
|
if (choice === '__add__') {
|
|
359
|
-
await
|
|
374
|
+
await addNode();
|
|
360
375
|
continue;
|
|
361
376
|
}
|
|
362
377
|
|