@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,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory facts the OS won't give you straight. `os.freemem()` counts only
|
|
3
|
+
* fully-idle pages — on macOS that is near-zero at all times because the kernel
|
|
4
|
+
* fills unused RAM with reclaimable cache and inactive pages. This module
|
|
5
|
+
* reports the number that actually matters: how much the OS can hand back on
|
|
6
|
+
* demand without swapping.
|
|
7
|
+
*/
|
|
8
|
+
import { execFileSync } from "node:child_process";
|
|
9
|
+
import { readFileSync } from "node:fs";
|
|
10
|
+
import { freemem, platform } from "node:os";
|
|
11
|
+
function run(cmd, args) {
|
|
12
|
+
try {
|
|
13
|
+
return execFileSync(cmd, args, {
|
|
14
|
+
encoding: "utf8",
|
|
15
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
16
|
+
timeout: 4000,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Bytes the OS can reclaim on demand without swapping:
|
|
25
|
+
* free + inactive + speculative + purgeable (macOS, via `vm_stat`), or
|
|
26
|
+
* `MemAvailable` (Linux, via `/proc/meminfo`). Falls back to `os.freemem()`
|
|
27
|
+
* when the platform probe is unavailable or fails.
|
|
28
|
+
*/
|
|
29
|
+
export function availableMemBytes() {
|
|
30
|
+
const p = platform();
|
|
31
|
+
if (p === "darwin") {
|
|
32
|
+
const out = run("vm_stat", []);
|
|
33
|
+
if (out) {
|
|
34
|
+
const pageSize = Number(out.match(/page size of (\d+) bytes/)?.[1]) || 4096;
|
|
35
|
+
const pages = (label) => Number(out.match(new RegExp(`${label}:\\s+(\\d+)\\.`))?.[1]) || 0;
|
|
36
|
+
const reclaimable = pages("Pages free") +
|
|
37
|
+
pages("Pages inactive") +
|
|
38
|
+
pages("Pages speculative") +
|
|
39
|
+
pages("Pages purgeable");
|
|
40
|
+
if (reclaimable > 0)
|
|
41
|
+
return reclaimable * pageSize;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (p === "linux") {
|
|
45
|
+
try {
|
|
46
|
+
const avail = readFileSync("/proc/meminfo", "utf8").match(/^MemAvailable:\s+(\d+)\s+kB/m);
|
|
47
|
+
if (avail)
|
|
48
|
+
return Number(avail[1]) * 1024;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
/* fall through */
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return freemem();
|
|
55
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort Tailscale detection, used to pick a sane default bind host.
|
|
3
|
+
*/
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
/** This machine's Tailscale IPv4 address, or null if Tailscale isn't installed/running. */
|
|
6
|
+
export function detectTailscaleIPv4() {
|
|
7
|
+
try {
|
|
8
|
+
const out = execFileSync("tailscale", ["ip", "-4"], {
|
|
9
|
+
timeout: 1500,
|
|
10
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
11
|
+
})
|
|
12
|
+
.toString()
|
|
13
|
+
.trim();
|
|
14
|
+
return out || null;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type Task, type Status, type Assignee, type TaskGitInfo } from "./types.js";
|
|
2
|
+
export declare const ACTIVITY_HEADING = "## Activity";
|
|
3
|
+
/** User-attached screenshots section (0123). Rendered into the body, kept before `## Activity`. */
|
|
4
|
+
export declare const SCREENSHOTS_HEADING = "## Screenshots";
|
|
5
|
+
/** The user's verbatim freeform prompt, preserved so an agent rewrite can't lose it. */
|
|
6
|
+
export declare const ORIGINAL_PROMPT_HEADING = "## Original prompt";
|
|
7
|
+
/** ISO-8601 UTC timestamp to the second, e.g. 2026-06-01T09:14:02Z. */
|
|
8
|
+
export declare function utcTimestamp(): string;
|
|
9
|
+
/**
|
|
10
|
+
* Normalize a legacy date-only or partial timestamp to full ISO-8601 UTC.
|
|
11
|
+
* `2026-06-01` → `2026-06-01T00:00:00Z`. Null passthrough.
|
|
12
|
+
*/
|
|
13
|
+
export declare function normalizeTimestamp(value: string | null): string | null;
|
|
14
|
+
/**
|
|
15
|
+
* Append a single activity entry to the body's Activity section.
|
|
16
|
+
* Creates the section if it does not exist. The Activity section is always
|
|
17
|
+
* the last thing in the body — append-only invariant.
|
|
18
|
+
*/
|
|
19
|
+
export declare function appendActivityEntry(body: string, line: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Extract a top-level `## Heading` section from a body — the heading line
|
|
22
|
+
* through the last line before the next `## ` heading (or EOF), trimmed of
|
|
23
|
+
* surrounding blank lines. Returns null when the heading is absent.
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractSection(body: string, heading: string): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Remove the `## Heading` section (see {@link extractSection}) from a body,
|
|
28
|
+
* collapsing the blank lines it leaves behind. Unchanged when the heading is
|
|
29
|
+
* absent.
|
|
30
|
+
*/
|
|
31
|
+
export declare function removeSection(body: string, heading: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Single change-recording helper. Every mutation path MUST call this.
|
|
34
|
+
* Stamps `updated_at` and appends an activity log entry onto `task.body`.
|
|
35
|
+
*
|
|
36
|
+
* @param task The task to record a change on (mutated in-place).
|
|
37
|
+
* @param entry The activity line content after the timestamp,
|
|
38
|
+
* e.g. `status inbox→ready · nick`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function recordChange(task: Task, entry: string): void;
|
|
41
|
+
/** Latest successful-close-out timestamp recorded in the append-only activity log. */
|
|
42
|
+
export declare function releasedAtFromActivity(body: string): string | null;
|
|
43
|
+
export interface ParseTaskArgs {
|
|
44
|
+
content: string;
|
|
45
|
+
absPath: string;
|
|
46
|
+
root: string;
|
|
47
|
+
defaultStatus: Status;
|
|
48
|
+
defaultAssignee: Assignee;
|
|
49
|
+
git?: TaskGitInfo;
|
|
50
|
+
}
|
|
51
|
+
export declare function parseTask(args: ParseTaskArgs): Task;
|
|
52
|
+
/** Build the file content for a Task, preserving unknown frontmatter. */
|
|
53
|
+
export declare function serializeTask(task: Task): string;
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn a markdown file (content + path) into a normalized Task, and turn a
|
|
3
|
+
* Task back into file content for writing. Normalization is forgiving: missing
|
|
4
|
+
* fields get sensible derived defaults so even a bare markdown file becomes a
|
|
5
|
+
* usable task.
|
|
6
|
+
*/
|
|
7
|
+
import { basename, relative } from "node:path";
|
|
8
|
+
import { parseDocument, serializeDocument } from "./frontmatter.js";
|
|
9
|
+
import { STATUSES } from "./types.js";
|
|
10
|
+
import { emptyGitInfo } from "./git.js";
|
|
11
|
+
/** Canonical frontmatter key order, so writes produce tidy, stable diffs. */
|
|
12
|
+
const KEY_ORDER = [
|
|
13
|
+
"id",
|
|
14
|
+
"title",
|
|
15
|
+
"type",
|
|
16
|
+
"status",
|
|
17
|
+
"needs_input",
|
|
18
|
+
"needs_input_reason",
|
|
19
|
+
"needs_merge",
|
|
20
|
+
"no_source_change",
|
|
21
|
+
"priority",
|
|
22
|
+
"area",
|
|
23
|
+
"assigned_to",
|
|
24
|
+
"created_by",
|
|
25
|
+
"branch",
|
|
26
|
+
"tags",
|
|
27
|
+
"agent_override",
|
|
28
|
+
"cli_override",
|
|
29
|
+
"model_override",
|
|
30
|
+
"pm_agent_override",
|
|
31
|
+
"pm_cli_override",
|
|
32
|
+
"pm_model_override",
|
|
33
|
+
"review_agent_override",
|
|
34
|
+
"review_cli_override",
|
|
35
|
+
"review_model_override",
|
|
36
|
+
"hotfix",
|
|
37
|
+
"hotfix_target",
|
|
38
|
+
"created_at",
|
|
39
|
+
"updated_at",
|
|
40
|
+
];
|
|
41
|
+
export const ACTIVITY_HEADING = "## Activity";
|
|
42
|
+
/** User-attached screenshots section (0123). Rendered into the body, kept before `## Activity`. */
|
|
43
|
+
export const SCREENSHOTS_HEADING = "## Screenshots";
|
|
44
|
+
/** The user's verbatim freeform prompt, preserved so an agent rewrite can't lose it. */
|
|
45
|
+
export const ORIGINAL_PROMPT_HEADING = "## Original prompt";
|
|
46
|
+
/**
|
|
47
|
+
* A release is a successful close-out event, not any prose mentioning a
|
|
48
|
+
* release. `markTaskReleased` is the sole writer of this exact marker.
|
|
49
|
+
*/
|
|
50
|
+
const RELEASE_ACTIVITY = /^- (\d{4}-\d{2}-\d{2}T[^\s]+) · status [a-z]+→done, release:success\r?$/gmu;
|
|
51
|
+
/** ISO-8601 UTC timestamp to the second, e.g. 2026-06-01T09:14:02Z. */
|
|
52
|
+
export function utcTimestamp() {
|
|
53
|
+
return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Normalize a legacy date-only or partial timestamp to full ISO-8601 UTC.
|
|
57
|
+
* `2026-06-01` → `2026-06-01T00:00:00Z`. Null passthrough.
|
|
58
|
+
*/
|
|
59
|
+
export function normalizeTimestamp(value) {
|
|
60
|
+
if (!value)
|
|
61
|
+
return null;
|
|
62
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(value))
|
|
63
|
+
return `${value}T00:00:00Z`;
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Append a single activity entry to the body's Activity section.
|
|
68
|
+
* Creates the section if it does not exist. The Activity section is always
|
|
69
|
+
* the last thing in the body — append-only invariant.
|
|
70
|
+
*/
|
|
71
|
+
export function appendActivityEntry(body, line) {
|
|
72
|
+
const trimmed = body.replace(/\s+$/, "");
|
|
73
|
+
const headingIndex = trimmed.lastIndexOf(`\n${ACTIVITY_HEADING}\n`);
|
|
74
|
+
if (headingIndex === -1) {
|
|
75
|
+
return `${trimmed}\n\n${ACTIVITY_HEADING}\n\n${line}\n`;
|
|
76
|
+
}
|
|
77
|
+
return `${trimmed}\n${line}\n`;
|
|
78
|
+
}
|
|
79
|
+
/** The line index where a top-level `## Heading` section starts, or -1. */
|
|
80
|
+
function sectionStart(lines, heading) {
|
|
81
|
+
return lines.findIndex((l) => l.trim() === heading);
|
|
82
|
+
}
|
|
83
|
+
/** The line index of the next top-level `## ` heading after `from`, or `lines.length`. */
|
|
84
|
+
function nextSection(lines, from) {
|
|
85
|
+
for (let i = from; i < lines.length; i++) {
|
|
86
|
+
if (/^##\s/.test(lines[i].trim()))
|
|
87
|
+
return i;
|
|
88
|
+
}
|
|
89
|
+
return lines.length;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Extract a top-level `## Heading` section from a body — the heading line
|
|
93
|
+
* through the last line before the next `## ` heading (or EOF), trimmed of
|
|
94
|
+
* surrounding blank lines. Returns null when the heading is absent.
|
|
95
|
+
*/
|
|
96
|
+
export function extractSection(body, heading) {
|
|
97
|
+
const lines = body.split("\n");
|
|
98
|
+
const start = sectionStart(lines, heading);
|
|
99
|
+
if (start === -1)
|
|
100
|
+
return null;
|
|
101
|
+
return lines
|
|
102
|
+
.slice(start, nextSection(lines, start + 1))
|
|
103
|
+
.join("\n")
|
|
104
|
+
.trim();
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Remove the `## Heading` section (see {@link extractSection}) from a body,
|
|
108
|
+
* collapsing the blank lines it leaves behind. Unchanged when the heading is
|
|
109
|
+
* absent.
|
|
110
|
+
*/
|
|
111
|
+
export function removeSection(body, heading) {
|
|
112
|
+
const lines = body.split("\n");
|
|
113
|
+
const start = sectionStart(lines, heading);
|
|
114
|
+
if (start === -1)
|
|
115
|
+
return body;
|
|
116
|
+
const end = nextSection(lines, start + 1);
|
|
117
|
+
const before = lines.slice(0, start).join("\n").replace(/\s+$/, "");
|
|
118
|
+
const after = lines.slice(end).join("\n").replace(/^\s+/, "");
|
|
119
|
+
return [before, after].filter(Boolean).join("\n\n");
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Single change-recording helper. Every mutation path MUST call this.
|
|
123
|
+
* Stamps `updated_at` and appends an activity log entry onto `task.body`.
|
|
124
|
+
*
|
|
125
|
+
* @param task The task to record a change on (mutated in-place).
|
|
126
|
+
* @param entry The activity line content after the timestamp,
|
|
127
|
+
* e.g. `status inbox→ready · nick`.
|
|
128
|
+
*/
|
|
129
|
+
export function recordChange(task, entry) {
|
|
130
|
+
task.updated_at = utcTimestamp();
|
|
131
|
+
task.body = appendActivityEntry(task.body, `- ${task.updated_at} · ${entry}`);
|
|
132
|
+
}
|
|
133
|
+
/** Latest successful-close-out timestamp recorded in the append-only activity log. */
|
|
134
|
+
export function releasedAtFromActivity(body) {
|
|
135
|
+
let releasedAt = null;
|
|
136
|
+
for (const match of body.matchAll(RELEASE_ACTIVITY))
|
|
137
|
+
releasedAt = match[1];
|
|
138
|
+
return releasedAt;
|
|
139
|
+
}
|
|
140
|
+
function deriveIdFromFilename(file) {
|
|
141
|
+
const name = basename(file).replace(/\.[^.]+$/, "");
|
|
142
|
+
const m = name.match(/^(\d{2,})/);
|
|
143
|
+
if (m)
|
|
144
|
+
return m[1];
|
|
145
|
+
return name;
|
|
146
|
+
}
|
|
147
|
+
function deriveTitleFromBody(body, fallback) {
|
|
148
|
+
const h1 = body.match(/^\s*#\s+(.+)$/m);
|
|
149
|
+
if (h1)
|
|
150
|
+
return h1[1].trim();
|
|
151
|
+
return fallback;
|
|
152
|
+
}
|
|
153
|
+
function normalizeStatus(raw, fallback) {
|
|
154
|
+
const s = String(raw ?? "").toLowerCase();
|
|
155
|
+
return STATUSES.includes(s) ? s : fallback;
|
|
156
|
+
}
|
|
157
|
+
function resolveAssignee(raw) {
|
|
158
|
+
const v = raw.toLowerCase().trim();
|
|
159
|
+
if (v === "ai" || v === "agent")
|
|
160
|
+
return "ai";
|
|
161
|
+
if (v === "" || v === "unassigned" || v === "none")
|
|
162
|
+
return "unassigned";
|
|
163
|
+
return "human";
|
|
164
|
+
}
|
|
165
|
+
export function parseTask(args) {
|
|
166
|
+
const { content, absPath, root, defaultStatus, defaultAssignee } = args;
|
|
167
|
+
const { data, body } = parseDocument(content);
|
|
168
|
+
const relPath = relative(root, absPath).split("\\").join("/");
|
|
169
|
+
const id = String(data.id ?? deriveIdFromFilename(absPath));
|
|
170
|
+
const title = String(data.title ?? deriveTitleFromBody(body, id));
|
|
171
|
+
const assignedTo = String(data.assigned_to ?? "");
|
|
172
|
+
const assignee = resolveAssignee(assignedTo || (defaultAssignee === "unassigned" ? "" : defaultAssignee));
|
|
173
|
+
// collect unknown keys to preserve on write
|
|
174
|
+
const known = new Set(KEY_ORDER);
|
|
175
|
+
const extra = {};
|
|
176
|
+
for (const [k, v] of Object.entries(data)) {
|
|
177
|
+
if (!known.has(k))
|
|
178
|
+
extra[k] = v;
|
|
179
|
+
}
|
|
180
|
+
// Per-task agent override fields
|
|
181
|
+
const agentOverride = typeof data.agent_override === "string" && data.agent_override ? data.agent_override : null;
|
|
182
|
+
const cliOverride = typeof data.cli_override === "string" && data.cli_override ? data.cli_override : null;
|
|
183
|
+
const modelOverride = typeof data.model_override === "string" && data.model_override ? data.model_override : null;
|
|
184
|
+
// Per-task PM agent override fields
|
|
185
|
+
const pmAgentOverride = typeof data.pm_agent_override === "string" && data.pm_agent_override
|
|
186
|
+
? data.pm_agent_override
|
|
187
|
+
: null;
|
|
188
|
+
const pmCliOverride = typeof data.pm_cli_override === "string" && data.pm_cli_override ? data.pm_cli_override : null;
|
|
189
|
+
const pmModelOverride = typeof data.pm_model_override === "string" && data.pm_model_override
|
|
190
|
+
? data.pm_model_override
|
|
191
|
+
: null;
|
|
192
|
+
// Per-task reviewer agent override fields
|
|
193
|
+
const reviewAgentOverride = typeof data.review_agent_override === "string" && data.review_agent_override
|
|
194
|
+
? data.review_agent_override
|
|
195
|
+
: null;
|
|
196
|
+
const reviewCliOverride = typeof data.review_cli_override === "string" && data.review_cli_override
|
|
197
|
+
? data.review_cli_override
|
|
198
|
+
: null;
|
|
199
|
+
const reviewModelOverride = typeof data.review_model_override === "string" && data.review_model_override
|
|
200
|
+
? data.review_model_override
|
|
201
|
+
: null;
|
|
202
|
+
// read created_at with fallback to deprecated created
|
|
203
|
+
const created_at = data.created_at
|
|
204
|
+
? String(data.created_at)
|
|
205
|
+
: data.created
|
|
206
|
+
? String(data.created)
|
|
207
|
+
: null;
|
|
208
|
+
// read updated_at with fallback to deprecated updated
|
|
209
|
+
const updated_at = data.updated_at
|
|
210
|
+
? String(data.updated_at)
|
|
211
|
+
: data.updated
|
|
212
|
+
? String(data.updated)
|
|
213
|
+
: null;
|
|
214
|
+
return {
|
|
215
|
+
id,
|
|
216
|
+
title,
|
|
217
|
+
type: String(data.type ?? "feature"),
|
|
218
|
+
status: normalizeStatus(data.status, "inbox"),
|
|
219
|
+
needsInput: data.needs_input === true,
|
|
220
|
+
needsInputReason: typeof data.needs_input_reason === "string" ? data.needs_input_reason : undefined,
|
|
221
|
+
needsMerge: data.needs_merge === true,
|
|
222
|
+
noSourceChange: data.no_source_change === true,
|
|
223
|
+
priority: String(data.priority ?? "p2"),
|
|
224
|
+
area: String(data.area ?? "general"),
|
|
225
|
+
assignee,
|
|
226
|
+
assignedTo: assignedTo || (assignee === "unassigned" ? "" : assignee),
|
|
227
|
+
createdBy: String(data.created_by ?? ""),
|
|
228
|
+
branch: String(data.branch ?? ""),
|
|
229
|
+
tags: Array.isArray(data.tags) ? data.tags.map(String) : [],
|
|
230
|
+
created_at,
|
|
231
|
+
updated_at,
|
|
232
|
+
releasedAt: releasedAtFromActivity(body),
|
|
233
|
+
path: relPath,
|
|
234
|
+
absPath,
|
|
235
|
+
body,
|
|
236
|
+
extra,
|
|
237
|
+
agentOverride,
|
|
238
|
+
cliOverride,
|
|
239
|
+
modelOverride,
|
|
240
|
+
pmAgentOverride,
|
|
241
|
+
pmCliOverride,
|
|
242
|
+
pmModelOverride,
|
|
243
|
+
reviewAgentOverride,
|
|
244
|
+
reviewCliOverride,
|
|
245
|
+
reviewModelOverride,
|
|
246
|
+
hotfix: data.hotfix === true ? true : undefined,
|
|
247
|
+
hotfixTarget: data.hotfix === true ? (data.hotfix_target === "main" ? "main" : "branch") : undefined,
|
|
248
|
+
git: args.git ?? emptyGitInfo(),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
/** Build the file content for a Task, preserving unknown frontmatter. */
|
|
252
|
+
export function serializeTask(task) {
|
|
253
|
+
const data = {
|
|
254
|
+
id: task.id,
|
|
255
|
+
title: task.title,
|
|
256
|
+
type: task.type,
|
|
257
|
+
status: task.status,
|
|
258
|
+
priority: task.priority,
|
|
259
|
+
area: task.area,
|
|
260
|
+
assigned_to: task.assignedTo || (task.assignee === "ai" ? "ai" : ""),
|
|
261
|
+
created_by: task.createdBy,
|
|
262
|
+
branch: task.branch,
|
|
263
|
+
};
|
|
264
|
+
if (task.tags.length)
|
|
265
|
+
data.tags = task.tags;
|
|
266
|
+
// Only ever write `needs_input` / `needs_merge` when true — false is the
|
|
267
|
+
// default and is never persisted, so clearing the flag removes the key.
|
|
268
|
+
// The reason is only ever meaningful alongside the flag itself — clearing
|
|
269
|
+
// needsInput (or never setting a reason) must never leave a stale reason
|
|
270
|
+
// behind to be misread on a later re-escalation.
|
|
271
|
+
if (task.needsInput) {
|
|
272
|
+
data.needs_input = true;
|
|
273
|
+
if (task.needsInputReason)
|
|
274
|
+
data.needs_input_reason = task.needsInputReason;
|
|
275
|
+
}
|
|
276
|
+
if (task.needsMerge)
|
|
277
|
+
data.needs_merge = true;
|
|
278
|
+
if (task.noSourceChange)
|
|
279
|
+
data.no_source_change = true;
|
|
280
|
+
if (task.agentOverride)
|
|
281
|
+
data.agent_override = task.agentOverride;
|
|
282
|
+
if (task.cliOverride)
|
|
283
|
+
data.cli_override = task.cliOverride;
|
|
284
|
+
if (task.modelOverride)
|
|
285
|
+
data.model_override = task.modelOverride;
|
|
286
|
+
if (task.pmAgentOverride)
|
|
287
|
+
data.pm_agent_override = task.pmAgentOverride;
|
|
288
|
+
if (task.pmCliOverride)
|
|
289
|
+
data.pm_cli_override = task.pmCliOverride;
|
|
290
|
+
if (task.pmModelOverride)
|
|
291
|
+
data.pm_model_override = task.pmModelOverride;
|
|
292
|
+
if (task.reviewAgentOverride)
|
|
293
|
+
data.review_agent_override = task.reviewAgentOverride;
|
|
294
|
+
if (task.reviewCliOverride)
|
|
295
|
+
data.review_cli_override = task.reviewCliOverride;
|
|
296
|
+
if (task.reviewModelOverride)
|
|
297
|
+
data.review_model_override = task.reviewModelOverride;
|
|
298
|
+
if (task.hotfix) {
|
|
299
|
+
data.hotfix = true;
|
|
300
|
+
data.hotfix_target = task.hotfixTarget ?? "branch";
|
|
301
|
+
}
|
|
302
|
+
if (task.created_at)
|
|
303
|
+
data.created_at = normalizeTimestamp(task.created_at);
|
|
304
|
+
if (task.updated_at)
|
|
305
|
+
data.updated_at = normalizeTimestamp(task.updated_at);
|
|
306
|
+
// re-attach preserved unknown keys
|
|
307
|
+
for (const [k, v] of Object.entries(task.extra))
|
|
308
|
+
data[k] = v;
|
|
309
|
+
return serializeDocument(data, task.body, KEY_ORDER);
|
|
310
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Line-level helpers shared by RepoOS's small TOML readers and writers
|
|
3
|
+
* (config.ts, tunnel.ts).
|
|
4
|
+
*
|
|
5
|
+
* The readers used to strip comments with `line.replace(/#.*$/, "")`, which
|
|
6
|
+
* also cut quoted values at the first `#`. The Agents page then saved that
|
|
7
|
+
* cut value back, which is how the reviewer's instructions in repoos.toml lost
|
|
8
|
+
* everything after "with exactly `" (the next character was the `#` of
|
|
9
|
+
* `## Verdict`). Anything with a `#0348`-style task reference hit the same.
|
|
10
|
+
*/
|
|
11
|
+
/** Drop a trailing `# comment`, ignoring `#` inside "double" or 'single' quoted strings. */
|
|
12
|
+
export declare function stripTomlComment(line: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Turn a quoted TOML scalar back into its string. Double-quoted values are
|
|
15
|
+
* decoded with JSON rules, which is exactly how the writers encode them
|
|
16
|
+
* (`JSON.stringify`), so `\"` and `\\` round-trip instead of gaining a
|
|
17
|
+
* backslash on every save. A value that isn't valid JSON (for example a
|
|
18
|
+
* hand-written literal `\d`) falls back to stripping the outer quotes.
|
|
19
|
+
*/
|
|
20
|
+
export declare function unquoteTomlString(s: string): string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Line-level helpers shared by RepoOS's small TOML readers and writers
|
|
3
|
+
* (config.ts, tunnel.ts).
|
|
4
|
+
*
|
|
5
|
+
* The readers used to strip comments with `line.replace(/#.*$/, "")`, which
|
|
6
|
+
* also cut quoted values at the first `#`. The Agents page then saved that
|
|
7
|
+
* cut value back, which is how the reviewer's instructions in repoos.toml lost
|
|
8
|
+
* everything after "with exactly `" (the next character was the `#` of
|
|
9
|
+
* `## Verdict`). Anything with a `#0348`-style task reference hit the same.
|
|
10
|
+
*/
|
|
11
|
+
/** Drop a trailing `# comment`, ignoring `#` inside "double" or 'single' quoted strings. */
|
|
12
|
+
export function stripTomlComment(line) {
|
|
13
|
+
let quote = null;
|
|
14
|
+
for (let i = 0; i < line.length; i++) {
|
|
15
|
+
const ch = line[i];
|
|
16
|
+
if (quote) {
|
|
17
|
+
if (quote === '"' && ch === "\\") {
|
|
18
|
+
i++; // skip the escaped character, including an escaped quote
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (ch === quote)
|
|
22
|
+
quote = null;
|
|
23
|
+
}
|
|
24
|
+
else if (ch === '"' || ch === "'") {
|
|
25
|
+
quote = ch;
|
|
26
|
+
}
|
|
27
|
+
else if (ch === "#") {
|
|
28
|
+
return line.slice(0, i);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return line;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Turn a quoted TOML scalar back into its string. Double-quoted values are
|
|
35
|
+
* decoded with JSON rules, which is exactly how the writers encode them
|
|
36
|
+
* (`JSON.stringify`), so `\"` and `\\` round-trip instead of gaining a
|
|
37
|
+
* backslash on every save. A value that isn't valid JSON (for example a
|
|
38
|
+
* hand-written literal `\d`) falls back to stripping the outer quotes.
|
|
39
|
+
*/
|
|
40
|
+
export function unquoteTomlString(s) {
|
|
41
|
+
if (s.length >= 2 && s.startsWith('"') && s.endsWith('"')) {
|
|
42
|
+
try {
|
|
43
|
+
const decoded = JSON.parse(s);
|
|
44
|
+
if (typeof decoded === "string")
|
|
45
|
+
return decoded;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
/* not JSON-compatible: fall through */
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return s.replace(/^["']|["']$/g, "");
|
|
52
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency-free TOML syntax validator (#0375).
|
|
3
|
+
*
|
|
4
|
+
* The Settings page's raw editor writes `repoos.toml` verbatim, so a malformed
|
|
5
|
+
* save must be rejected *before* it reaches disk — `loadConfig`'s reader is
|
|
6
|
+
* deliberately forgiving (it skips lines it can't parse), which would turn a
|
|
7
|
+
* typo into a silently-ignored config instead of an error the user can see.
|
|
8
|
+
*
|
|
9
|
+
* This is a validator, not a parser: it walks the document and reports the
|
|
10
|
+
* first structural problem it finds (unterminated string/array, missing `=`,
|
|
11
|
+
* bad table header, invalid scalar) with a 1-based line number. It accepts the
|
|
12
|
+
* TOML shapes RepoOS itself emits plus the common hand-written ones (dotted
|
|
13
|
+
* and quoted keys, literal strings, single-line arrays, dates/times,
|
|
14
|
+
* hex/octal/binary numbers). It deliberately does not enforce key uniqueness
|
|
15
|
+
* or table-redefinition rules — those are valid-TOML concerns that never
|
|
16
|
+
* affect what `loadConfig` reads, and rejecting on them would make the escape
|
|
17
|
+
* hatch stricter than the loader it feeds.
|
|
18
|
+
*
|
|
19
|
+
* Crucially, it rejects the *valid TOML* shapes that `parseFlatToml`
|
|
20
|
+
* (`config.ts`) cannot read: multi-line strings, multi-line arrays, and inline
|
|
21
|
+
* tables. Those would otherwise sail through a save and then be silently
|
|
22
|
+
* misread (the first line of a multi-line array parses as the string `"["`,
|
|
23
|
+
* an inline table as a plain string), so a whole save could take no effect
|
|
24
|
+
* with no error — the exact silent-config failure this gate exists to stop.
|
|
25
|
+
*/
|
|
26
|
+
export interface TomlValidationResult {
|
|
27
|
+
ok: boolean;
|
|
28
|
+
/** Human-readable reason, without a line prefix. Present when `ok` is false. */
|
|
29
|
+
error?: string;
|
|
30
|
+
/** 1-based line the problem was found on. Present when `ok` is false. */
|
|
31
|
+
line?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Validate a TOML document's syntax. Returns `{ ok: true }` when the whole
|
|
35
|
+
* document is structurally well-formed, or `{ ok: false, error, line }` with
|
|
36
|
+
* the first problem found. Never throws on malformed input.
|
|
37
|
+
*/
|
|
38
|
+
export declare function validateToml(source: string): TomlValidationResult;
|
|
39
|
+
/**
|
|
40
|
+
* Format a validation failure for display, with the line folded into the
|
|
41
|
+
* message so callers that only carry a string (the HTTP JSON error, the store)
|
|
42
|
+
* still surface it.
|
|
43
|
+
*/
|
|
44
|
+
export declare function formatTomlError(result: TomlValidationResult): string;
|