@repo-os/repoos 0.5.45
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/LICENSE.md +110 -0
- package/README.md +272 -0
- package/dist/.build-info.json +4 -0
- package/dist/.build-stamp.json +3 -0
- package/dist/cli/colors.d.ts +13 -0
- package/dist/cli/colors.js +37 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +221 -0
- package/dist/commands/check.d.ts +170 -0
- package/dist/commands/check.js +1227 -0
- package/dist/commands/docs.d.ts +2 -0
- package/dist/commands/docs.js +70 -0
- package/dist/commands/gc.d.ts +1 -0
- package/dist/commands/gc.js +57 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.js +699 -0
- package/dist/commands/screenshots.d.ts +7 -0
- package/dist/commands/screenshots.js +77 -0
- package/dist/commands/serve.d.ts +55 -0
- package/dist/commands/serve.js +204 -0
- package/dist/commands/status.d.ts +140 -0
- package/dist/commands/status.js +569 -0
- package/dist/commands/stop.d.ts +1 -0
- package/dist/commands/stop.js +59 -0
- package/dist/commands/tasks.d.ts +47 -0
- package/dist/commands/tasks.js +334 -0
- package/dist/commands/tunnel.d.ts +1 -0
- package/dist/commands/tunnel.js +1271 -0
- package/dist/commands/ui-harness.d.ts +53 -0
- package/dist/commands/ui-harness.js +62 -0
- package/dist/commands/ui-smoke.d.ts +6 -0
- package/dist/commands/ui-smoke.js +220 -0
- package/dist/commands/upgrade.d.ts +15 -0
- package/dist/commands/upgrade.js +188 -0
- package/dist/core/auth-store.d.ts +81 -0
- package/dist/core/auth-store.js +530 -0
- package/dist/core/auth.d.ts +75 -0
- package/dist/core/auth.js +124 -0
- package/dist/core/bootstrap.d.ts +41 -0
- package/dist/core/bootstrap.js +236 -0
- package/dist/core/build.d.ts +92 -0
- package/dist/core/build.js +249 -0
- package/dist/core/canary.d.ts +10 -0
- package/dist/core/canary.js +14 -0
- package/dist/core/check-failure-summary.d.ts +10 -0
- package/dist/core/check-failure-summary.js +34 -0
- package/dist/core/config.d.ts +160 -0
- package/dist/core/config.js +1288 -0
- package/dist/core/context-pack.d.ts +74 -0
- package/dist/core/context-pack.js +778 -0
- package/dist/core/db.d.ts +236 -0
- package/dist/core/db.js +747 -0
- package/dist/core/detect.d.ts +69 -0
- package/dist/core/detect.js +251 -0
- package/dist/core/docs.d.ts +76 -0
- package/dist/core/docs.js +225 -0
- package/dist/core/frontmatter.d.ts +29 -0
- package/dist/core/frontmatter.js +215 -0
- package/dist/core/git.d.ts +505 -0
- package/dist/core/git.js +1453 -0
- package/dist/core/indexer.d.ts +33 -0
- package/dist/core/indexer.js +282 -0
- package/dist/core/input.d.ts +66 -0
- package/dist/core/input.js +222 -0
- package/dist/core/logger.d.ts +70 -0
- package/dist/core/logger.js +138 -0
- package/dist/core/models.d.ts +45 -0
- package/dist/core/models.js +291 -0
- package/dist/core/net-probe.d.ts +4 -0
- package/dist/core/net-probe.js +20 -0
- package/dist/core/providers/deepinfra.d.ts +23 -0
- package/dist/core/providers/deepinfra.js +44 -0
- package/dist/core/providers/index.d.ts +24 -0
- package/dist/core/providers/index.js +59 -0
- package/dist/core/providers/openrouter.d.ts +28 -0
- package/dist/core/providers/openrouter.js +82 -0
- package/dist/core/providers/spend.d.ts +86 -0
- package/dist/core/providers/spend.js +286 -0
- package/dist/core/providers/types.d.ts +56 -0
- package/dist/core/providers/types.js +39 -0
- package/dist/core/repoos.d.ts +59 -0
- package/dist/core/repoos.js +221 -0
- package/dist/core/runtime.d.ts +35 -0
- package/dist/core/runtime.js +167 -0
- package/dist/core/skills-registry.d.ts +39 -0
- package/dist/core/skills-registry.js +148 -0
- package/dist/core/skills-registry.test.d.ts +1 -0
- package/dist/core/skills-registry.test.js +52 -0
- package/dist/core/sysmem.d.ts +7 -0
- package/dist/core/sysmem.js +55 -0
- package/dist/core/tailscale.d.ts +2 -0
- package/dist/core/tailscale.js +19 -0
- package/dist/core/task.d.ts +53 -0
- package/dist/core/task.js +310 -0
- package/dist/core/toml-line.d.ts +20 -0
- package/dist/core/toml-line.js +52 -0
- package/dist/core/toml-validate.d.ts +44 -0
- package/dist/core/toml-validate.js +335 -0
- package/dist/core/tunnel-assistant.d.ts +30 -0
- package/dist/core/tunnel-assistant.js +65 -0
- package/dist/core/tunnel-registry.d.ts +96 -0
- package/dist/core/tunnel-registry.js +267 -0
- package/dist/core/tunnel-registry.test.d.ts +1 -0
- package/dist/core/tunnel-registry.test.js +309 -0
- package/dist/core/tunnel.d.ts +103 -0
- package/dist/core/tunnel.js +320 -0
- package/dist/core/types.d.ts +811 -0
- package/dist/core/types.js +27 -0
- package/dist/core/worktree-gc.d.ts +57 -0
- package/dist/core/worktree-gc.js +194 -0
- package/dist/server/__tests__/integration-job.test.d.ts +1 -0
- package/dist/server/__tests__/integration-job.test.js +129 -0
- package/dist/server/agents.d.ts +1020 -0
- package/dist/server/agents.js +4070 -0
- package/dist/server/attachments.d.ts +51 -0
- package/dist/server/attachments.js +130 -0
- package/dist/server/auto-engineering.d.ts +45 -0
- package/dist/server/auto-engineering.js +275 -0
- package/dist/server/branchless-release.d.ts +22 -0
- package/dist/server/branchless-release.js +67 -0
- package/dist/server/built-in-agents.d.ts +305 -0
- package/dist/server/built-in-agents.js +1838 -0
- package/dist/server/cto-monitor.d.ts +35 -0
- package/dist/server/cto-monitor.js +281 -0
- package/dist/server/cto.d.ts +125 -0
- package/dist/server/cto.js +590 -0
- package/dist/server/deployments.d.ts +119 -0
- package/dist/server/deployments.js +325 -0
- package/dist/server/diff-snapshot.d.ts +20 -0
- package/dist/server/diff-snapshot.js +73 -0
- package/dist/server/done.d.ts +160 -0
- package/dist/server/done.js +488 -0
- package/dist/server/freeform.d.ts +55 -0
- package/dist/server/freeform.js +179 -0
- package/dist/server/handoff.d.ts +99 -0
- package/dist/server/handoff.js +575 -0
- package/dist/server/hetzner.d.ts +45 -0
- package/dist/server/hetzner.js +117 -0
- package/dist/server/icons.d.ts +8 -0
- package/dist/server/icons.js +195 -0
- package/dist/server/integration-job.d.ts +75 -0
- package/dist/server/integration-job.js +144 -0
- package/dist/server/integration-orchestrator.d.ts +211 -0
- package/dist/server/integration-orchestrator.js +1299 -0
- package/dist/server/integration-status.d.ts +48 -0
- package/dist/server/integration-status.js +71 -0
- package/dist/server/live-index.d.ts +295 -0
- package/dist/server/live-index.js +452 -0
- package/dist/server/model-test.d.ts +20 -0
- package/dist/server/model-test.js +100 -0
- package/dist/server/ntfy.d.ts +57 -0
- package/dist/server/ntfy.js +112 -0
- package/dist/server/pm-attachments.d.ts +35 -0
- package/dist/server/pm-attachments.js +181 -0
- package/dist/server/pm-runs.d.ts +58 -0
- package/dist/server/pm-runs.js +80 -0
- package/dist/server/preview.d.ts +174 -0
- package/dist/server/preview.js +641 -0
- package/dist/server/release.d.ts +88 -0
- package/dist/server/release.js +449 -0
- package/dist/server/reload.d.ts +194 -0
- package/dist/server/reload.js +586 -0
- package/dist/server/remote-validation.d.ts +132 -0
- package/dist/server/remote-validation.js +478 -0
- package/dist/server/repo-lock.d.ts +36 -0
- package/dist/server/repo-lock.js +141 -0
- package/dist/server/review-guard.d.ts +14 -0
- package/dist/server/review-guard.js +159 -0
- package/dist/server/review.d.ts +243 -0
- package/dist/server/review.js +1211 -0
- package/dist/server/routes/agents.d.ts +6 -0
- package/dist/server/routes/agents.js +28 -0
- package/dist/server/routes/auth.d.ts +60 -0
- package/dist/server/routes/auth.js +755 -0
- package/dist/server/routes/config.d.ts +20 -0
- package/dist/server/routes/config.js +347 -0
- package/dist/server/routes/debugger.d.ts +33 -0
- package/dist/server/routes/debugger.js +360 -0
- package/dist/server/routes/deployments.d.ts +3 -0
- package/dist/server/routes/deployments.js +16 -0
- package/dist/server/routes/docs.d.ts +8 -0
- package/dist/server/routes/docs.js +135 -0
- package/dist/server/routes/helpers.d.ts +21 -0
- package/dist/server/routes/helpers.js +171 -0
- package/dist/server/routes/index.d.ts +19 -0
- package/dist/server/routes/index.js +18 -0
- package/dist/server/routes/info.d.ts +17 -0
- package/dist/server/routes/info.js +177 -0
- package/dist/server/routes/inputs.d.ts +7 -0
- package/dist/server/routes/inputs.js +125 -0
- package/dist/server/routes/model-providers.d.ts +27 -0
- package/dist/server/routes/model-providers.js +94 -0
- package/dist/server/routes/models.d.ts +3 -0
- package/dist/server/routes/models.js +36 -0
- package/dist/server/routes/notify.d.ts +2 -0
- package/dist/server/routes/notify.js +17 -0
- package/dist/server/routes/playground.d.ts +29 -0
- package/dist/server/routes/playground.js +81 -0
- package/dist/server/routes/release.d.ts +20 -0
- package/dist/server/routes/release.js +86 -0
- package/dist/server/routes/router.d.ts +12 -0
- package/dist/server/routes/router.js +32 -0
- package/dist/server/routes/skill-registry.d.ts +5 -0
- package/dist/server/routes/skill-registry.js +51 -0
- package/dist/server/routes/tasks.d.ts +58 -0
- package/dist/server/routes/tasks.js +1442 -0
- package/dist/server/routes/transcribe.d.ts +2 -0
- package/dist/server/routes/transcribe.js +124 -0
- package/dist/server/routes/types.d.ts +68 -0
- package/dist/server/routes/types.js +1 -0
- package/dist/server/routes/ui.d.ts +5 -0
- package/dist/server/routes/ui.js +81 -0
- package/dist/server/routes/utils.d.ts +3 -0
- package/dist/server/routes/utils.js +23 -0
- package/dist/server/serve-reaper.d.ts +135 -0
- package/dist/server/serve-reaper.js +491 -0
- package/dist/server/server.d.ts +49 -0
- package/dist/server/server.js +2171 -0
- package/dist/server/supervisor.d.ts +89 -0
- package/dist/server/supervisor.js +231 -0
- package/dist/server/system.d.ts +198 -0
- package/dist/server/system.js +480 -0
- package/dist/server/task-check.d.ts +45 -0
- package/dist/server/task-check.js +71 -0
- package/dist/server/task-transitions.d.ts +51 -0
- package/dist/server/task-transitions.js +78 -0
- package/dist/server/task-watchdog.d.ts +158 -0
- package/dist/server/task-watchdog.js +681 -0
- package/dist/server/test-run.d.ts +28 -0
- package/dist/server/test-run.js +81 -0
- package/dist/server/watcher.d.ts +21 -0
- package/dist/server/watcher.js +194 -0
- package/dist/server/write.d.ts +94 -0
- package/dist/server/write.js +345 -0
- package/dist/ui/assets/AgentsView-CF4wRvHh.css +1 -0
- package/dist/ui/assets/AgentsView-D-myL-kA.js +1 -0
- package/dist/ui/assets/ContextView-D3TC-RSp.css +1 -0
- package/dist/ui/assets/ContextView-DsOqAdyE.js +4 -0
- package/dist/ui/assets/DashboardView-BOhsIJqO.js +1 -0
- package/dist/ui/assets/DashboardView-Ck9o0RP2.css +1 -0
- package/dist/ui/assets/DeploymentsView--iIUVfEH.css +1 -0
- package/dist/ui/assets/DeploymentsView-DXu_FYR2.js +1 -0
- package/dist/ui/assets/InputsView-24HVbVPk.js +1 -0
- package/dist/ui/assets/InputsView-BLb7GHtx.css +1 -0
- package/dist/ui/assets/LoginView-6QlGg6cx.css +1 -0
- package/dist/ui/assets/LoginView-BqupI0Lt.js +1 -0
- package/dist/ui/assets/ReleasesView-BEG1A0YH.css +1 -0
- package/dist/ui/assets/ReleasesView-CAgMuxpe.js +3 -0
- package/dist/ui/assets/SettingsView-CA-NYSMK.css +1 -0
- package/dist/ui/assets/SettingsView-O3iZSnjg.js +3 -0
- package/dist/ui/assets/WorkView-D8Tl6qwy.js +1 -0
- package/dist/ui/assets/WorkView-DMYL2s3W.css +1 -0
- package/dist/ui/assets/abnfDiagram-VCTEODGH-J5lkSnEE.js +1 -0
- package/dist/ui/assets/arc-BHZnmhhM.js +1 -0
- package/dist/ui/assets/architecture-7GRP2DOG-pLdFt-xS.js +1 -0
- package/dist/ui/assets/architectureDiagram-5GKGNRK7-C3k89uKX.js +36 -0
- package/dist/ui/assets/array-BifhSqXX.js +1 -0
- package/dist/ui/assets/blockDiagram-I7D4REHJ-AWNoBzop.js +129 -0
- package/dist/ui/assets/button-lbikqSph.js +1 -0
- package/dist/ui/assets/c4Diagram-7LVT6UL2-DKG1D5gd.js +38 -0
- package/dist/ui/assets/channel-gA4qBv3A.js +1 -0
- package/dist/ui/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/dist/ui/assets/chunk-4HAMMTFA-D1YR8lF_.js +62 -0
- package/dist/ui/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/dist/ui/assets/chunk-75Z2AOVW-5GuBxfsL.js +2 -0
- package/dist/ui/assets/chunk-DU6HZSFF-DfdNNqwa.js +127 -0
- package/dist/ui/assets/chunk-F27PBJKO-BPZzinQ4.js +1 -0
- package/dist/ui/assets/chunk-FOHPRMQF-l2z4K2jD.js +161 -0
- package/dist/ui/assets/chunk-GMAD6QVW-DGWvSKj5.js +72 -0
- package/dist/ui/assets/chunk-GVQU2GXP-D6Aw75wJ.js +1 -0
- package/dist/ui/assets/chunk-IMKFNOWR-B8mMBKCW.js +231 -0
- package/dist/ui/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/dist/ui/assets/chunk-L3NEJ4N5-D5OJuXYE.js +1 -0
- package/dist/ui/assets/chunk-OSK3NFVY-B7r9w2UW.js +10 -0
- package/dist/ui/assets/chunk-P2QGCYS3-C74X4AQX.js +1 -0
- package/dist/ui/assets/chunk-POPQ4Y6H-C5_u1gUS.js +1 -0
- package/dist/ui/assets/chunk-PWAF6VOD-DfoTLtxn.js +1 -0
- package/dist/ui/assets/chunk-SHT3W25Y-D40m2eqz.js +168 -0
- package/dist/ui/assets/chunk-SVP7TREG-euxi0Kap.js +88 -0
- package/dist/ui/assets/chunk-TICWLB2K-fkdxngzN.js +206 -0
- package/dist/ui/assets/chunk-XXDRQBXY-DFBUG-OT.js +1 -0
- package/dist/ui/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/dist/ui/assets/classDiagram-ZZMXUADV-BzW8bq2P.js +1 -0
- package/dist/ui/assets/classDiagram-v2-VYDZK3BY-BzW8bq2P.js +1 -0
- package/dist/ui/assets/cose-bilkent-JH36ORCC-BTcgMon1.js +1 -0
- package/dist/ui/assets/cynefin-OW5HDTMX-B5m_COsS.js +1 -0
- package/dist/ui/assets/cynefinDiagram-5FMLGOSQ-C-AG6CGe.js +62 -0
- package/dist/ui/assets/cytoscape.esm-FBntH-9t.js +321 -0
- package/dist/ui/assets/dagre-GXQ25YYZ-DDfDnp8L.js +4 -0
- package/dist/ui/assets/dagre-PrKaheQc.js +1 -0
- package/dist/ui/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/dist/ui/assets/diagram-S7CK7UJ4-DjQZAlEY.js +30 -0
- package/dist/ui/assets/diagram-UQ7AKVKN-DAZ9yp1B.js +41 -0
- package/dist/ui/assets/diagram-VSXAHHWV-BWepUKJD.js +3 -0
- package/dist/ui/assets/diagram-VX7I27RA-EcV1J81b.js +24 -0
- package/dist/ui/assets/diagram-Z3DM3KII-Dbz7XU_K.js +24 -0
- package/dist/ui/assets/dist-DTg6UBE_.js +1 -0
- package/dist/ui/assets/ebnfDiagram-PWID7BFC-BUyQw8Mm.js +1 -0
- package/dist/ui/assets/erDiagram-RLTQ6QDP-WVf8ukKt.js +99 -0
- package/dist/ui/assets/eventmodeling-NTZA5JFV-DHqyLOAn.js +1 -0
- package/dist/ui/assets/flowDiagram-HODETNUW-CYk_ze5K.js +1 -0
- package/dist/ui/assets/ganttDiagram-EL5Y4UJY-rB4MZuB-.js +292 -0
- package/dist/ui/assets/gitGraph-4MIJSDKK-BOTWM8dY.js +1 -0
- package/dist/ui/assets/gitGraphDiagram-WWUBYQGX-eYscuhtP.js +106 -0
- package/dist/ui/assets/graphlib-DS17s2tU.js +1 -0
- package/dist/ui/assets/index-BACBfnga.css +1 -0
- package/dist/ui/assets/index-BOQ-ust6.js +26 -0
- package/dist/ui/assets/info-A6RAGUB7-_67wOORe.js +1 -0
- package/dist/ui/assets/infoDiagram-27XIBGKW-XVXbddTA.js +2 -0
- package/dist/ui/assets/init-C-OQMol4.js +1 -0
- package/dist/ui/assets/ishikawaDiagram-5VMMS53U-ClLNiEru.js +70 -0
- package/dist/ui/assets/journeyDiagram-3NMN7TZE-NRawjR6i.js +139 -0
- package/dist/ui/assets/kanban-definition-UXKFOSKX-BgSzBfIx.js +89 -0
- package/dist/ui/assets/katex-DolUETbr.js +257 -0
- package/dist/ui/assets/line-KAyfuul4.js +1 -0
- package/dist/ui/assets/linear-cCCPOpu7.js +1 -0
- package/dist/ui/assets/mermaid-parser.core-CgXZk1iN.js +7 -0
- package/dist/ui/assets/mermaid.core-BUUkChHW.js +44 -0
- package/dist/ui/assets/mindmap-definition-YA3MSWOX-Cpn7vW4r.js +96 -0
- package/dist/ui/assets/ordinal-BDEzSJ7C.js +1 -0
- package/dist/ui/assets/packet-AYTQ26CC-0lZwM-d6.js +1 -0
- package/dist/ui/assets/path-COt_16Va.js +1 -0
- package/dist/ui/assets/pegDiagram-XKGWAZYB-impy0inn.js +1 -0
- package/dist/ui/assets/pie-WAS4IAKB-CqfdGHYg.js +1 -0
- package/dist/ui/assets/pieDiagram-E7YTZNPT-CxjL4I1h.js +39 -0
- package/dist/ui/assets/quadrantDiagram-AXDQQJYC-Cjd1QMIe.js +7 -0
- package/dist/ui/assets/radar-RG4KPBEZ-CRkQIwiv.js +1 -0
- package/dist/ui/assets/railroad-74A4TZTK-i67XSWTB.js +1 -0
- package/dist/ui/assets/railroad-abnf-HS5TGJTU-Cv9m5aS3.js +1 -0
- package/dist/ui/assets/railroad-ebnf-LZEXJU2U-C5ZIn-Jh.js +1 -0
- package/dist/ui/assets/railroad-peg-WCYAUIDC-CGra9Oq-.js +1 -0
- package/dist/ui/assets/railroadDiagram-O6MQD6OU-_pK0qyaY.js +1 -0
- package/dist/ui/assets/releases-B0WNV-SY.js +1 -0
- package/dist/ui/assets/repoos-cto-square.webp +0 -0
- package/dist/ui/assets/repoos-helpful-pm-square.webp +0 -0
- package/dist/ui/assets/repoos-orchestrator-square.webp +0 -0
- package/dist/ui/assets/repoos-performance-agent-square.webp +0 -0
- package/dist/ui/assets/repoos-ross-from-friends-square.webp +0 -0
- package/dist/ui/assets/repoos-tech-debt-agent-square.webp +0 -0
- package/dist/ui/assets/requirementDiagram-BXWQKSXE-CRrFX-yw.js +84 -0
- package/dist/ui/assets/rough.esm-By172zw-.js +1 -0
- package/dist/ui/assets/sankeyDiagram-P5KCCOFB-X_xFRj9E.js +40 -0
- package/dist/ui/assets/sequenceDiagram-WJ2MYXX4-Byb49lKf.js +162 -0
- package/dist/ui/assets/sizeCapture-INFHLROL-B0uUizjq.js +1 -0
- package/dist/ui/assets/src-BH-TyZbA.js +1 -0
- package/dist/ui/assets/stateDiagram-D77RDMKH-CzaPrqYF.js +1 -0
- package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-BCXckz7K.js +1 -0
- package/dist/ui/assets/swimlanes-42K2YHIH-Cx_1lnqr.js +1 -0
- package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-54qsKi6A.js +8 -0
- package/dist/ui/assets/timeline-definition-24CTP7MA-B1IbZPQ-.js +120 -0
- package/dist/ui/assets/treeView-Q6P3EWNA-DFWhVE3d.js +1 -0
- package/dist/ui/assets/treemap-WGGIJYW6-BmbYPThn.js +1 -0
- package/dist/ui/assets/vendor-ui-DPm7zLxi.js +3 -0
- package/dist/ui/assets/vendor-vue-CobgyEJp.js +1 -0
- package/dist/ui/assets/vennDiagram-4TSXK5OY-Ae2AtRpt.js +34 -0
- package/dist/ui/assets/wardley-WFR3VGLG-BtD4X4tL.js +1 -0
- package/dist/ui/assets/wardleyDiagram-VM6X3IG4-B3YN2fTd.js +78 -0
- package/dist/ui/assets/xychartDiagram-S5SC5T6Z-CIzJfMKA.js +7 -0
- package/dist/ui/favicon.svg +11 -0
- package/dist/ui/index.html +49 -0
- package/dist/ui/sw.js +48 -0
- package/package.json +91 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface PreviewServer {
|
|
2
|
+
close: () => void;
|
|
3
|
+
url: string;
|
|
4
|
+
}
|
|
5
|
+
/** Structural subset of the Playwright WebKit API both callers use. */
|
|
6
|
+
export interface SmokeConsoleMessage {
|
|
7
|
+
type(): string;
|
|
8
|
+
text(): string;
|
|
9
|
+
}
|
|
10
|
+
export interface SmokePage {
|
|
11
|
+
on(event: "console", handler: (msg: SmokeConsoleMessage) => void): void;
|
|
12
|
+
on(event: "pageerror", handler: (err: Error) => void): void;
|
|
13
|
+
goto(url: string, options: {
|
|
14
|
+
waitUntil: string;
|
|
15
|
+
timeout: number;
|
|
16
|
+
}): Promise<unknown>;
|
|
17
|
+
title(): Promise<string>;
|
|
18
|
+
evaluate<R>(fn: () => R): Promise<R>;
|
|
19
|
+
$(selector: string): Promise<unknown>;
|
|
20
|
+
waitForTimeout(ms: number): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export interface SmokeBrowser {
|
|
23
|
+
newPage(): Promise<SmokePage>;
|
|
24
|
+
newPage(options: {
|
|
25
|
+
viewport: {
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
};
|
|
29
|
+
}): Promise<SmokePage>;
|
|
30
|
+
close(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export interface SmokePlaywright {
|
|
33
|
+
webkit: {
|
|
34
|
+
launch(options: {
|
|
35
|
+
headless: boolean;
|
|
36
|
+
}): Promise<SmokeBrowser>;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Start the built UI's server on an ephemeral port so the gate works even when
|
|
41
|
+
* a `repoos serve` instance is already running on the default port. `root` is
|
|
42
|
+
* optional — both callers pass a throwaway fixture root for determinism: the
|
|
43
|
+
* smoke test (check.ts) boots against a bare empty work/ + minimal repoos.toml
|
|
44
|
+
* so it never touches the live board's jobs or preview auto-launch (#0260),
|
|
45
|
+
* and the screenshot script serves a fixture copy.
|
|
46
|
+
*/
|
|
47
|
+
export declare function startPreviewServer(root?: string): Promise<PreviewServer>;
|
|
48
|
+
/**
|
|
49
|
+
* Resolve `@playwright/test` and launch headless WebKit. Throws a descriptive
|
|
50
|
+
* error when the package isn't installed so callers can report it as a
|
|
51
|
+
* graceful skip rather than a gate failure.
|
|
52
|
+
*/
|
|
53
|
+
export declare function launchWebkit(): Promise<SmokeBrowser>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared headless-UI setup for the two places that start the built app's
|
|
3
|
+
* ephemeral HTTP server and a WebKit browser against it:
|
|
4
|
+
* - `runUISmokeTest` in src/commands/check.ts (`repoos check`'s UI gate)
|
|
5
|
+
* - `scripts/capture-screenshots.mjs` (`bun run screenshots`)
|
|
6
|
+
*
|
|
7
|
+
* Both previously hand-rolled the same "start server on an ephemeral port via
|
|
8
|
+
* createRequire + launch headless WebKit" boilerplate, each with its own copy
|
|
9
|
+
* of the `@playwright/test` / server wiring — so close-out ended up with two
|
|
10
|
+
* independent browser+server launch cycles to verify the same built UI.
|
|
11
|
+
*
|
|
12
|
+
* They run in separate OS processes that are never alive at the same time —
|
|
13
|
+
* `repoos check` is a subprocess of the close-out gate, while screenshots are
|
|
14
|
+
* an on-demand `bun run screenshots` run that, per #0140, is never part of a
|
|
15
|
+
* close-out — so each still performs its own launch at runtime. A literal
|
|
16
|
+
* single shared server + single browser *instance* across that boundary is
|
|
17
|
+
* therefore impossible without folding screenshots into `repoos check` (which
|
|
18
|
+
* would contradict #0140), so the shared logic itself is the intended AC3
|
|
19
|
+
* scope-down (task #0213): one launch implementation, zero drift between the
|
|
20
|
+
* two call sites.
|
|
21
|
+
*/
|
|
22
|
+
import { createRequire } from "node:module";
|
|
23
|
+
/**
|
|
24
|
+
* Start the built UI's server on an ephemeral port so the gate works even when
|
|
25
|
+
* a `repoos serve` instance is already running on the default port. `root` is
|
|
26
|
+
* optional — both callers pass a throwaway fixture root for determinism: the
|
|
27
|
+
* smoke test (check.ts) boots against a bare empty work/ + minimal repoos.toml
|
|
28
|
+
* so it never touches the live board's jobs or preview auto-launch (#0260),
|
|
29
|
+
* and the screenshot script serves a fixture copy.
|
|
30
|
+
*/
|
|
31
|
+
export async function startPreviewServer(root) {
|
|
32
|
+
const { startServer } = await import("../server/server.js");
|
|
33
|
+
// disableAuth: the smoke gate tests generic rendering (title, board,
|
|
34
|
+
// buttons), not auth flows — a project with [auth] enabled must not make
|
|
35
|
+
// `repoos check` itself unable to reach the dashboard.
|
|
36
|
+
const opts = { host: "127.0.0.1", port: 0, disableAuth: true };
|
|
37
|
+
if (root !== undefined)
|
|
38
|
+
opts.root = root;
|
|
39
|
+
return startServer(opts);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve `@playwright/test` and launch headless WebKit. Throws a descriptive
|
|
43
|
+
* error when the package isn't installed so callers can report it as a
|
|
44
|
+
* graceful skip rather than a gate failure.
|
|
45
|
+
*/
|
|
46
|
+
export async function launchWebkit() {
|
|
47
|
+
// @playwright/test is a CJS package. Bun's ESM `import()` of it resolves the
|
|
48
|
+
// named browser exports to `undefined` (root cause of #0200), so load it via
|
|
49
|
+
// createRequire, which handles the CJS interop under both Bun and Node.
|
|
50
|
+
// Types are structural (Smoke*) rather than `typeof import("@playwright/test")`
|
|
51
|
+
// so `tsc` compiles even when the package is absent — the build must not fail
|
|
52
|
+
// before the "not installed" skip can run.
|
|
53
|
+
const require = createRequire(import.meta.url);
|
|
54
|
+
let playwright;
|
|
55
|
+
try {
|
|
56
|
+
playwright = require("@playwright/test");
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw new Error("Cannot find module @playwright/test (not installed)");
|
|
60
|
+
}
|
|
61
|
+
return playwright.webkit.launch({ headless: true });
|
|
62
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run the smoke test with the same outcomes `repoos check` used to apply
|
|
3
|
+
* in-process: pass → 0, Playwright/WebKit unavailable → skip (0), anything
|
|
4
|
+
* else → 1. Returns the exit code so the caller decides how to exit.
|
|
5
|
+
*/
|
|
6
|
+
export declare function cmdUISmoke(): Promise<number>;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RepoOS's own headless UI smoke test (#0348).
|
|
3
|
+
*
|
|
4
|
+
* `repoos check`'s ui-smoke step is per-project and opt-in: it runs whatever a
|
|
5
|
+
* project declares (a `smoke` package.json script, or `[check] uiSmoke` in
|
|
6
|
+
* repoos.toml). RepoOS dogfoods that mechanism instead of keeping a
|
|
7
|
+
* RepoOS-only branch in check.ts: its own `smoke` script (scripts/ui-smoke.mjs)
|
|
8
|
+
* calls `cmdUISmoke()` below against the freshly built dist/.
|
|
9
|
+
*
|
|
10
|
+
* Server startup + webkit launch share the harness in ui-harness.ts with the
|
|
11
|
+
* screenshot script (#0213).
|
|
12
|
+
*/
|
|
13
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { c } from "../cli/colors.js";
|
|
17
|
+
import { startPreviewServer, launchWebkit } from "./ui-harness.js";
|
|
18
|
+
/**
|
|
19
|
+
* Build a throwaway, empty fixture repo to serve the built SPA against.
|
|
20
|
+
* The smoke test only cares that the built UI renders with zero console errors,
|
|
21
|
+
* so booting it against the live checkout's real board drags in job recovery
|
|
22
|
+
* and preview auto-launch reconciliation for every active/review task (0260).
|
|
23
|
+
* A bare `work/` + minimal `repoos.toml` avoids all of that: no tasks, no
|
|
24
|
+
* recovery, no auto-launch — constant cost regardless of board size.
|
|
25
|
+
*/
|
|
26
|
+
function makeSmokeFixture() {
|
|
27
|
+
const root = mkdtempSync(join(tmpdir(), "repoos-smoke-"));
|
|
28
|
+
mkdirSync(join(root, "work"), { recursive: true });
|
|
29
|
+
writeFileSync(join(root, "repoos.toml"), 'theme = "dark"\nuiTheme = "classic"\n\n');
|
|
30
|
+
return root;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Start the dev server, run Playwright WebKit smoke tests, then stop.
|
|
34
|
+
* Exports failures as thrown errors. Server startup + webkit launch share the
|
|
35
|
+
* harness in ui-harness.ts with the screenshot script (#0213).
|
|
36
|
+
*/
|
|
37
|
+
async function runUISmokeTest() {
|
|
38
|
+
const fixture = makeSmokeFixture();
|
|
39
|
+
let server;
|
|
40
|
+
try {
|
|
41
|
+
server = await startPreviewServer(fixture);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
rmSync(fixture, { recursive: true, force: true });
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
let browser;
|
|
48
|
+
try {
|
|
49
|
+
browser = await launchWebkit();
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
server.close();
|
|
53
|
+
rmSync(fixture, { recursive: true, force: true });
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const page = await browser.newPage();
|
|
58
|
+
const consoleErrs = [];
|
|
59
|
+
const pageErrors = [];
|
|
60
|
+
page.on("console", (msg) => {
|
|
61
|
+
if (msg.type() === "error")
|
|
62
|
+
consoleErrs.push(msg.text());
|
|
63
|
+
});
|
|
64
|
+
page.on("pageerror", (err) => {
|
|
65
|
+
pageErrors.push(err.message);
|
|
66
|
+
});
|
|
67
|
+
await page.goto(server.url, { waitUntil: "load", timeout: 20_000 });
|
|
68
|
+
// Check page title is correct
|
|
69
|
+
const title = await page.title();
|
|
70
|
+
if (title !== "RepoOS")
|
|
71
|
+
throw new Error(`Unexpected title: "${title}"`);
|
|
72
|
+
// Verify we are testing the built Vite SPA, which references hashed
|
|
73
|
+
// assets in /assets/.
|
|
74
|
+
const hashedAsset = await page.evaluate(() => {
|
|
75
|
+
const scripts = Array.from(document.querySelectorAll("script[src]"));
|
|
76
|
+
return scripts.some((s) => (s.getAttribute("src") ?? "").startsWith("/assets/"));
|
|
77
|
+
});
|
|
78
|
+
if (!hashedAsset) {
|
|
79
|
+
throw new Error("Served page is not the built Vite app (no /assets/ bundle)");
|
|
80
|
+
}
|
|
81
|
+
// Check that the app MOUNTED — no unrendered mustache in the DOM
|
|
82
|
+
const bodyText = await page.evaluate(() => document.body.innerText);
|
|
83
|
+
if (bodyText.includes("{{") || bodyText.includes("}}")) {
|
|
84
|
+
throw new Error("Unrendered mustache found in DOM — Vue did not mount");
|
|
85
|
+
}
|
|
86
|
+
// Check that a known root element rendered with real content
|
|
87
|
+
const appEl = await page.$("#app");
|
|
88
|
+
if (!appEl)
|
|
89
|
+
throw new Error("#app element not found");
|
|
90
|
+
const hasBrand = await page.evaluate(() => document.body.innerText.includes("RepoOS"));
|
|
91
|
+
if (!hasBrand)
|
|
92
|
+
throw new Error('Expected "RepoOS" in rendered content');
|
|
93
|
+
// Navigate to work page and click +New Task
|
|
94
|
+
await page.evaluate(() => {
|
|
95
|
+
const navItems = document.querySelectorAll(".nav-item");
|
|
96
|
+
for (const item of Array.from(navItems)) {
|
|
97
|
+
if (item.textContent?.includes("Work"))
|
|
98
|
+
item.click();
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
await page.waitForTimeout(500);
|
|
102
|
+
// Verify work page rendered
|
|
103
|
+
const workEl = await page.$(".board");
|
|
104
|
+
if (!workEl) {
|
|
105
|
+
consoleErrs.push("Work page board not rendered — check page navigation");
|
|
106
|
+
}
|
|
107
|
+
// Check that the +New Task button exists
|
|
108
|
+
const newBtn = await page.$(".new-btn");
|
|
109
|
+
if (!newBtn) {
|
|
110
|
+
consoleErrs.push("+New Task button not found in DOM");
|
|
111
|
+
}
|
|
112
|
+
// ── CSS regression guard: utility spacing must actually apply ─────
|
|
113
|
+
// Tailwind v4 emits all its CSS inside cascade layers. If an
|
|
114
|
+
// UNLAYERED reset such as `*{padding:0;margin:0}` is ever added to
|
|
115
|
+
// style.css, it silently beats every spacing utility (unlayered rules
|
|
116
|
+
// take precedence over @layer rules), collapsing padding on shadcn
|
|
117
|
+
// controls while console stays clean. Flag any non-explicit-zero
|
|
118
|
+
// spacing utility whose computed value is 0.
|
|
119
|
+
const assertUtilitySpacing = async (where) => {
|
|
120
|
+
const offenders = await page.evaluate(() => {
|
|
121
|
+
const AXIS = {
|
|
122
|
+
p: ["paddingTop", "paddingRight", "paddingBottom", "paddingLeft"],
|
|
123
|
+
px: ["paddingLeft", "paddingRight"],
|
|
124
|
+
py: ["paddingTop", "paddingBottom"],
|
|
125
|
+
pt: ["paddingTop"],
|
|
126
|
+
pr: ["paddingRight"],
|
|
127
|
+
pb: ["paddingBottom"],
|
|
128
|
+
pl: ["paddingLeft"],
|
|
129
|
+
m: ["marginTop", "marginRight", "marginBottom", "marginLeft"],
|
|
130
|
+
mx: ["marginLeft", "marginRight"],
|
|
131
|
+
my: ["marginTop", "marginBottom"],
|
|
132
|
+
mt: ["marginTop"],
|
|
133
|
+
mr: ["marginRight"],
|
|
134
|
+
mb: ["marginBottom"],
|
|
135
|
+
ml: ["marginLeft"],
|
|
136
|
+
};
|
|
137
|
+
const bad = [];
|
|
138
|
+
for (const el of Array.from(document.querySelectorAll("*"))) {
|
|
139
|
+
if (!(el instanceof HTMLElement))
|
|
140
|
+
continue;
|
|
141
|
+
const s = getComputedStyle(el);
|
|
142
|
+
for (const cls of el.classList) {
|
|
143
|
+
if (cls.startsWith("-"))
|
|
144
|
+
continue; // negative margins are intentional
|
|
145
|
+
const m = /^([pm])([trblxy]?)-(?:\[)?([1-9])/.exec(cls);
|
|
146
|
+
if (!m)
|
|
147
|
+
continue;
|
|
148
|
+
for (const prop of AXIS[m[1] + m[2]] ?? []) {
|
|
149
|
+
if (parseFloat(s[prop]) <= 0) {
|
|
150
|
+
bad.push(`${cls} → ${prop} = ${s[prop]} on <${el.tagName.toLowerCase()}>`);
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return [...new Set(bad)];
|
|
157
|
+
});
|
|
158
|
+
if (offenders.length > 0) {
|
|
159
|
+
throw new Error("Utility spacing collapsed on " + where + ": " + offenders.slice(0, 6).join("; "));
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
await assertUtilitySpacing("dashboard");
|
|
163
|
+
// Re-run the guard on the settings page (covers shadcn Button + Select)
|
|
164
|
+
await page.evaluate(() => {
|
|
165
|
+
const navItems = document.querySelectorAll(".nav-item");
|
|
166
|
+
for (const item of Array.from(navItems)) {
|
|
167
|
+
if (item.textContent?.includes("Settings"))
|
|
168
|
+
item.click();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
await page.waitForTimeout(500);
|
|
172
|
+
await assertUtilitySpacing("settings");
|
|
173
|
+
// Check for zero console errors
|
|
174
|
+
if (consoleErrs.length > 0) {
|
|
175
|
+
let msg = "Console errors (" + consoleErrs.length + "): " + consoleErrs.join("; ");
|
|
176
|
+
if (pageErrors.length > 0)
|
|
177
|
+
msg += " | Page errors: " + pageErrors.join("; ");
|
|
178
|
+
throw new Error(msg);
|
|
179
|
+
}
|
|
180
|
+
if (pageErrors.length > 0) {
|
|
181
|
+
throw new Error("Page errors (" + pageErrors.length + "): " + pageErrors.join("; "));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
if (browser)
|
|
186
|
+
await browser.close();
|
|
187
|
+
server.close();
|
|
188
|
+
rmSync(fixture, { recursive: true, force: true });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/** A missing @playwright/test install or WebKit binary — a skip, not a failure. */
|
|
192
|
+
function isPlaywrightUnavailable(msg) {
|
|
193
|
+
return (msg.includes("Cannot find module") ||
|
|
194
|
+
msg.includes("not installed") ||
|
|
195
|
+
msg.includes("Executable doesn't exist"));
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Run the smoke test with the same outcomes `repoos check` used to apply
|
|
199
|
+
* in-process: pass → 0, Playwright/WebKit unavailable → skip (0), anything
|
|
200
|
+
* else → 1. Returns the exit code so the caller decides how to exit.
|
|
201
|
+
*/
|
|
202
|
+
export async function cmdUISmoke() {
|
|
203
|
+
try {
|
|
204
|
+
await runUISmokeTest();
|
|
205
|
+
console.log(c.green(" ✔ UI smoke test passed"));
|
|
206
|
+
return 0;
|
|
207
|
+
}
|
|
208
|
+
catch (e) {
|
|
209
|
+
const msg = e.message;
|
|
210
|
+
if (isPlaywrightUnavailable(msg)) {
|
|
211
|
+
console.log(c.dim(" · Playwright not available — UI smoke test skipped"));
|
|
212
|
+
console.log(c.dim(" Install: bun add -d @playwright/test && bunx playwright install webkit"));
|
|
213
|
+
return 0;
|
|
214
|
+
}
|
|
215
|
+
console.log(c.red(" ✗ UI smoke test failed: " + msg.split("\n")[0]));
|
|
216
|
+
if (msg.includes("\n"))
|
|
217
|
+
console.log(c.dim(msg));
|
|
218
|
+
return 1;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The release notes to print after an upgrade, or null when there's nothing
|
|
3
|
+
* to show — GitHub returns `body` as `null`/absent for a release cut with an
|
|
4
|
+
* empty notes field (#0361's UI leaves the field optional), and this must
|
|
5
|
+
* stay silent then, not print an empty line or "no notes available" filler.
|
|
6
|
+
*/
|
|
7
|
+
export declare function releaseNotesToPrint(body: string | null | undefined): string | null;
|
|
8
|
+
export declare function cmdUpgrade(args: string[]): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* The `repoos` launcher script: Bun when it's installed, Node otherwise
|
|
11
|
+
* (`REPOOS_RUNTIME=node` pins Node). The runtime is picked each time `repoos`
|
|
12
|
+
* runs, so installing Bun later takes effect with no reinstall. Must match the
|
|
13
|
+
* heredoc in install.sh exactly; launcher.test.ts compares the two.
|
|
14
|
+
*/
|
|
15
|
+
export declare function launcherScript(entry: string): string;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `repoos upgrade` — self-update for standalone (curl-installed) builds.
|
|
3
|
+
* Downloads the latest GitHub release's dist tarball and swaps it into place.
|
|
4
|
+
*
|
|
5
|
+
* Package-manager installs (npm/bun, living under node_modules/) are not
|
|
6
|
+
* touched here — those update through the package manager itself.
|
|
7
|
+
*/
|
|
8
|
+
import { execFileSync } from "node:child_process";
|
|
9
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { c } from "../cli/colors.js";
|
|
14
|
+
const REPO = "repo-os/repoos";
|
|
15
|
+
/** The directory containing cli/, core/, server/, ui/, .build-info.json — dist/ in a source checkout, or the whole install root for a standalone install. */
|
|
16
|
+
function installRoot() {
|
|
17
|
+
return dirname(dirname(fileURLToPath(import.meta.url)));
|
|
18
|
+
}
|
|
19
|
+
function readVersion(root) {
|
|
20
|
+
try {
|
|
21
|
+
const info = JSON.parse(readFileSync(join(root, ".build-info.json"), "utf8"));
|
|
22
|
+
return info.version ?? null;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The release notes to print after an upgrade, or null when there's nothing
|
|
30
|
+
* to show — GitHub returns `body` as `null`/absent for a release cut with an
|
|
31
|
+
* empty notes field (#0361's UI leaves the field optional), and this must
|
|
32
|
+
* stay silent then, not print an empty line or "no notes available" filler.
|
|
33
|
+
*/
|
|
34
|
+
export function releaseNotesToPrint(body) {
|
|
35
|
+
const trimmed = body?.trim();
|
|
36
|
+
return trimmed ? trimmed : null;
|
|
37
|
+
}
|
|
38
|
+
/** `--channel beta|canary|rc` from argv, or null for the default stable channel. */
|
|
39
|
+
function parseChannel(args) {
|
|
40
|
+
const flag = args.indexOf("--channel");
|
|
41
|
+
const value = flag !== -1 ? args[flag + 1] : undefined;
|
|
42
|
+
return value && !value.startsWith("--") ? value : null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* `GET /releases/latest` always excludes prereleases (GitHub's own rule, not
|
|
46
|
+
* configurable) — release.yml marks any tag with a "-" (v0.6.0-beta.1,
|
|
47
|
+
* -canary.N, -rc.N) as a prerelease, exactly so stable users never get handed
|
|
48
|
+
* one from this endpoint. A channel opt-in has to use the list endpoint
|
|
49
|
+
* instead and find its own match.
|
|
50
|
+
*/
|
|
51
|
+
async function fetchLatestRelease(channel) {
|
|
52
|
+
if (!channel) {
|
|
53
|
+
const res = await fetch(`https://api.github.com/repos/${REPO}/releases/latest`);
|
|
54
|
+
if (!res.ok)
|
|
55
|
+
throw new Error(`GitHub API returned ${res.status}`);
|
|
56
|
+
return (await res.json());
|
|
57
|
+
}
|
|
58
|
+
// Newest first by default; the first tag matching "-<channel>." wins.
|
|
59
|
+
const res = await fetch(`https://api.github.com/repos/${REPO}/releases?per_page=30`);
|
|
60
|
+
if (!res.ok)
|
|
61
|
+
throw new Error(`GitHub API returned ${res.status}`);
|
|
62
|
+
const releases = (await res.json());
|
|
63
|
+
const match = releases.find((r) => r.tag_name.includes(`-${channel}.`));
|
|
64
|
+
if (!match)
|
|
65
|
+
throw new Error(`No "${channel}" release found in the last 30 releases.`);
|
|
66
|
+
return match;
|
|
67
|
+
}
|
|
68
|
+
export async function cmdUpgrade(args) {
|
|
69
|
+
const root = installRoot();
|
|
70
|
+
const channel = parseChannel(args);
|
|
71
|
+
if (root.includes("node_modules")) {
|
|
72
|
+
console.log(c.yellow(" repoos was installed via a package manager."));
|
|
73
|
+
console.log(c.dim(" Update it the same way:"));
|
|
74
|
+
console.log(c.dim(" bun update -g @repo-os/repoos") +
|
|
75
|
+
c.dim(" or ") +
|
|
76
|
+
c.dim("npm update -g @repo-os/repoos"));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const current = readVersion(root);
|
|
80
|
+
if (!current) {
|
|
81
|
+
console.log(c.yellow(" repoos appears to be running from a source checkout, not a standalone install."));
|
|
82
|
+
console.log(c.dim(" Update it with: git pull && bun run build"));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
console.log(c.dim(` Current version: v${current}`));
|
|
86
|
+
console.log(c.dim(channel ? ` Checking latest "${channel}" release…` : " Checking latest release…"));
|
|
87
|
+
let release;
|
|
88
|
+
try {
|
|
89
|
+
release = await fetchLatestRelease(channel);
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
console.error(c.red(" Failed to check for updates: " + e.message));
|
|
93
|
+
process.exitCode = 1;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const latest = release.tag_name.replace(/^v/, "");
|
|
97
|
+
if (latest === current) {
|
|
98
|
+
console.log(c.green(` Already up to date (v${current}).`));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const asset = release.assets.find((a) => a.name === "repoos-dist.tar.gz");
|
|
102
|
+
if (!asset) {
|
|
103
|
+
console.error(c.red(` No repoos-dist.tar.gz asset found on release v${latest}.`));
|
|
104
|
+
process.exitCode = 1;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
console.log(c.dim(` Upgrading v${current} → v${latest}…`));
|
|
108
|
+
// Do all the work in siblings of the install root, never in the system temp
|
|
109
|
+
// dir: `/tmp` is often a small tmpfs (or has a per-user quota — Omarchy sets
|
|
110
|
+
// one), and a multi-MB download there fails with `curl: (23)`. Siblings of
|
|
111
|
+
// `root` are on the same filesystem as the install, so there's space and the
|
|
112
|
+
// final swap is a plain rename.
|
|
113
|
+
const work = `${root}.upgrade-download`;
|
|
114
|
+
const staged = `${root}.upgrade-staged`;
|
|
115
|
+
const displaced = `${root}.upgrade-old`;
|
|
116
|
+
for (const dir of [work, staged, displaced]) {
|
|
117
|
+
if (existsSync(dir))
|
|
118
|
+
rmSync(dir, { recursive: true, force: true });
|
|
119
|
+
}
|
|
120
|
+
mkdirSync(work, { recursive: true });
|
|
121
|
+
const tmpTar = join(work, "repoos-dist.tar.gz");
|
|
122
|
+
try {
|
|
123
|
+
execFileSync("curl", ["-fsSL", asset.browser_download_url, "-o", tmpTar], { stdio: "inherit" });
|
|
124
|
+
mkdirSync(staged, { recursive: true });
|
|
125
|
+
execFileSync("tar", ["-xzf", tmpTar, "-C", staged]);
|
|
126
|
+
execFileSync("chmod", ["+x", join(staged, "cli", "index.js")]);
|
|
127
|
+
renameSync(root, displaced);
|
|
128
|
+
try {
|
|
129
|
+
renameSync(staged, root);
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
renameSync(displaced, root);
|
|
133
|
+
throw e;
|
|
134
|
+
}
|
|
135
|
+
rmSync(displaced, { recursive: true, force: true });
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
rmSync(work, { recursive: true, force: true });
|
|
139
|
+
if (existsSync(staged))
|
|
140
|
+
rmSync(staged, { recursive: true, force: true });
|
|
141
|
+
}
|
|
142
|
+
regenerateLauncher(root);
|
|
143
|
+
console.log(c.green(` Upgraded to v${latest}.`));
|
|
144
|
+
const notes = releaseNotesToPrint(release.body);
|
|
145
|
+
if (notes) {
|
|
146
|
+
console.log("");
|
|
147
|
+
console.log(notes);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The `repoos` launcher script: Bun when it's installed, Node otherwise
|
|
152
|
+
* (`REPOOS_RUNTIME=node` pins Node). The runtime is picked each time `repoos`
|
|
153
|
+
* runs, so installing Bun later takes effect with no reinstall. Must match the
|
|
154
|
+
* heredoc in install.sh exactly; launcher.test.ts compares the two.
|
|
155
|
+
*/
|
|
156
|
+
export function launcherScript(entry) {
|
|
157
|
+
return [
|
|
158
|
+
"#!/usr/bin/env bash",
|
|
159
|
+
"# repoos launcher: Bun when it's installed, Node otherwise (REPOOS_RUNTIME=node pins Node).",
|
|
160
|
+
`entry="${entry}"`,
|
|
161
|
+
'if [ "${REPOOS_RUNTIME:-}" != "node" ]; then',
|
|
162
|
+
' bun="${REPOOS_BUN_PATH:-$(command -v bun 2>/dev/null || true)}"',
|
|
163
|
+
' if [ -n "$bun" ] && [ -x "$bun" ]; then exec "$bun" "$entry" "$@"; fi',
|
|
164
|
+
"fi",
|
|
165
|
+
'exec node --no-warnings "$entry" "$@"',
|
|
166
|
+
"",
|
|
167
|
+
].join("\n");
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Rewrite the `repoos` launcher script install.sh created, so an existing
|
|
171
|
+
* curl install picks up launcher-level fixes (e.g. the Bun-first runtime
|
|
172
|
+
* choice) without needing to re-run install.sh. Silent
|
|
173
|
+
* no-op if the launcher isn't where install.sh puts it — e.g. a custom setup
|
|
174
|
+
* that symlinks straight to cli/index.js instead.
|
|
175
|
+
*/
|
|
176
|
+
function regenerateLauncher(root) {
|
|
177
|
+
const binDir = process.env.REPOOS_BIN_DIR || join(homedir(), ".local", "bin");
|
|
178
|
+
const launcherPath = join(binDir, "repoos");
|
|
179
|
+
if (!existsSync(launcherPath))
|
|
180
|
+
return;
|
|
181
|
+
try {
|
|
182
|
+
writeFileSync(launcherPath, launcherScript(join(root, "cli", "index.js")));
|
|
183
|
+
chmodSync(launcherPath, 0o755);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// Best-effort — an upgrade that fails to touch the launcher isn't worth failing over.
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth persistence layer. Wraps the SQLite database with typed operations
|
|
3
|
+
* for users, sessions, OTP challenges, and audit log entries.
|
|
4
|
+
*
|
|
5
|
+
* All OTP codes and session tokens are stored hashed — plaintext never
|
|
6
|
+
* touches disk.
|
|
7
|
+
*/
|
|
8
|
+
import type { AuthRole } from "./auth.js";
|
|
9
|
+
export interface AuthUser {
|
|
10
|
+
email: string;
|
|
11
|
+
role: AuthRole;
|
|
12
|
+
displayName: string | null;
|
|
13
|
+
authSource: string;
|
|
14
|
+
addedBy: string | null;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
updatedAt: string;
|
|
17
|
+
}
|
|
18
|
+
export interface AuthSessionRow {
|
|
19
|
+
sessionId: string;
|
|
20
|
+
tokenHash: string;
|
|
21
|
+
email: string;
|
|
22
|
+
role: AuthRole;
|
|
23
|
+
createdAt: string;
|
|
24
|
+
expiresAt: string;
|
|
25
|
+
revokedAt: string | null;
|
|
26
|
+
}
|
|
27
|
+
export interface OtpChallengeRow {
|
|
28
|
+
id: number;
|
|
29
|
+
email: string;
|
|
30
|
+
codeHash: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
expiresAt: string;
|
|
33
|
+
usedAt: string | null;
|
|
34
|
+
sourceIp: string | null;
|
|
35
|
+
}
|
|
36
|
+
export interface AuditLogEntry {
|
|
37
|
+
id: number;
|
|
38
|
+
action: string;
|
|
39
|
+
targetEmail: string | null;
|
|
40
|
+
actorEmail: string | null;
|
|
41
|
+
details: string | null;
|
|
42
|
+
createdAt: string;
|
|
43
|
+
}
|
|
44
|
+
export declare class AuthStore {
|
|
45
|
+
private db;
|
|
46
|
+
private available;
|
|
47
|
+
constructor(repoRoot: string);
|
|
48
|
+
isAvailable(): boolean;
|
|
49
|
+
getUser(email: string): AuthUser | null;
|
|
50
|
+
listUsers(): AuthUser[];
|
|
51
|
+
upsertUser(email: string, role: AuthRole, addedBy: string | null, displayName?: string): void;
|
|
52
|
+
deleteUser(email: string): boolean;
|
|
53
|
+
getAdminCount(): number;
|
|
54
|
+
createSession(email: string, role: AuthRole, maxAgeSeconds: number): string;
|
|
55
|
+
getSession(token: string): AuthSessionRow | null;
|
|
56
|
+
revokeSession(token: string): boolean;
|
|
57
|
+
revokeAllSessions(email: string): number;
|
|
58
|
+
/** Update the role column on all active sessions for a user. */
|
|
59
|
+
updateSessionRoles(email: string, newRole: AuthRole): number;
|
|
60
|
+
cleanupExpiredSessions(): number;
|
|
61
|
+
/** Store a hashed OTP challenge. Returns the challenge id. */
|
|
62
|
+
createOtpChallenge(email: string, codeHash: string, ttlSeconds: number, sourceIp: string | null): number;
|
|
63
|
+
/** Find a valid, unused OTP for this email. Returns null if none found or expired. */
|
|
64
|
+
findValidOtp(email: string, codeHash: string): OtpChallengeRow | null;
|
|
65
|
+
/** Mark an OTP as used. */
|
|
66
|
+
markOtpUsed(otpId: number): void;
|
|
67
|
+
/** Count OTP requests for this email in the given time window. */
|
|
68
|
+
countRecentOtpRequests(email: string, windowSeconds: number): number;
|
|
69
|
+
/** Count failed verify attempts for this email in the given time window. */
|
|
70
|
+
countFailedVerifyAttempts(email: string, windowSeconds: number): number;
|
|
71
|
+
cleanupExpiredOtps(): number;
|
|
72
|
+
logAudit(action: string, targetEmail: string | null, actorEmail: string | null, details?: string): void;
|
|
73
|
+
getAuditLog(limit?: number): AuditLogEntry[];
|
|
74
|
+
private toUser;
|
|
75
|
+
private toSession;
|
|
76
|
+
private toOtp;
|
|
77
|
+
private toAuditEntry;
|
|
78
|
+
close(): void;
|
|
79
|
+
}
|
|
80
|
+
export declare function getAuthStore(repoRoot: string): AuthStore | null;
|
|
81
|
+
export declare function resetAuthStoreInstance(): void;
|