@repo-os/repoos 0.5.66 → 0.5.67
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/dist/.build-info.json +2 -2
- package/dist/.build-stamp.json +1 -1
- package/dist/cli/api-route-parity.d.ts +13 -0
- package/dist/cli/api-route-parity.js +128 -0
- package/dist/cli/cli-session.d.ts +6 -0
- package/dist/cli/cli-session.js +56 -0
- package/dist/cli/effective-from-api.d.ts +11 -0
- package/dist/cli/effective-from-api.js +12 -0
- package/dist/cli/help.js +67 -1
- package/dist/cli/index.js +67 -0
- package/dist/cli/repoos-api.d.ts +52 -0
- package/dist/cli/repoos-api.js +164 -0
- package/dist/commands/check.js +15 -4
- package/dist/commands/control-api.d.ts +13 -0
- package/dist/commands/control-api.js +527 -0
- package/dist/commands/init.js +5 -0
- package/dist/commands/tasks.js +6 -3
- package/dist/core/agent-run-health.d.ts +59 -0
- package/dist/core/agent-run-health.js +173 -0
- package/dist/core/approval-policy.d.ts +19 -2
- package/dist/core/approval-policy.js +55 -0
- package/dist/core/attention.d.ts +2 -2
- package/dist/core/attention.js +6 -1
- package/dist/core/check-buildable-project.d.ts +16 -0
- package/dist/core/check-buildable-project.js +34 -0
- package/dist/core/check-failure-summary.d.ts +1 -1
- package/dist/core/check-failure-summary.js +1 -1
- package/dist/core/check-store.d.ts +19 -2
- package/dist/core/check-store.js +57 -4
- package/dist/core/close-out-timing.d.ts +28 -0
- package/dist/core/close-out-timing.js +81 -0
- package/dist/core/config.js +105 -8
- package/dist/core/cto-actions.d.ts +56 -0
- package/dist/core/cto-actions.js +98 -0
- package/dist/core/dependency-install.d.ts +18 -0
- package/dist/core/dependency-install.js +81 -1
- package/dist/core/git.d.ts +48 -2
- package/dist/core/git.js +97 -13
- package/dist/core/lockfile-conflict.d.ts +12 -0
- package/dist/core/lockfile-conflict.js +83 -0
- package/dist/core/needs-input.d.ts +2 -0
- package/dist/core/needs-input.js +16 -2
- package/dist/core/remote-pool-queue.d.ts +13 -0
- package/dist/core/remote-pool-queue.js +20 -0
- package/dist/core/review-passes.d.ts +11 -0
- package/dist/core/review-passes.js +14 -0
- package/dist/core/task-proof-commands.d.ts +10 -0
- package/dist/core/task-proof-commands.js +59 -0
- package/dist/core/task-underspecified.d.ts +17 -0
- package/dist/core/task-underspecified.js +47 -0
- package/dist/core/types.d.ts +42 -1
- package/dist/core/types.js +1 -0
- package/dist/core/ui-index.d.ts +7 -0
- package/dist/core/ui-index.js +10 -0
- package/dist/server/agents.d.ts +43 -0
- package/dist/server/agents.js +264 -16
- package/dist/server/api-route-catalog.d.ts +12 -0
- package/dist/server/api-route-catalog.js +11 -0
- package/dist/server/approval-policy.d.ts +5 -1
- package/dist/server/approval-policy.js +25 -0
- package/dist/server/attention-events.js +1 -1
- package/dist/server/close-out-repair.d.ts +10 -0
- package/dist/server/close-out-repair.js +100 -0
- package/dist/server/close-out-requeue.d.ts +15 -0
- package/dist/server/close-out-requeue.js +35 -0
- package/dist/server/cto-action-rates.d.ts +6 -0
- package/dist/server/cto-action-rates.js +65 -0
- package/dist/server/cto-actions.d.ts +39 -0
- package/dist/server/cto-actions.js +253 -0
- package/dist/server/cto-monitor.d.ts +3 -0
- package/dist/server/cto-monitor.js +17 -2
- package/dist/server/cto.js +1 -1
- package/dist/server/debug-tldr.d.ts +1 -1
- package/dist/server/debug-tldr.js +2 -0
- package/dist/server/distribution.d.ts +13 -0
- package/dist/server/distribution.js +37 -0
- package/dist/server/done.d.ts +13 -0
- package/dist/server/done.js +2 -4
- package/dist/server/engineer-launch.d.ts +27 -0
- package/dist/server/engineer-launch.js +75 -0
- package/dist/server/engineer-remote-self-check.d.ts +1 -0
- package/dist/server/engineer-remote-self-check.js +3 -1
- package/dist/server/handoff-failure-loop.d.ts +57 -0
- package/dist/server/handoff-failure-loop.js +278 -0
- package/dist/server/handoff.js +13 -47
- package/dist/server/integration-job.d.ts +5 -0
- package/dist/server/integration-orchestrator.d.ts +11 -5
- package/dist/server/integration-orchestrator.js +88 -28
- package/dist/server/notifications/reasons.js +1 -0
- package/dist/server/pre-review-remote-gate.d.ts +9 -0
- package/dist/server/pre-review-remote-gate.js +33 -0
- package/dist/server/reload.d.ts +4 -0
- package/dist/server/reload.js +6 -0
- package/dist/server/remote-validation.d.ts +380 -3
- package/dist/server/remote-validation.js +1187 -131
- package/dist/server/request-close-out.d.ts +2 -0
- package/dist/server/request-close-out.js +28 -6
- package/dist/server/review.d.ts +8 -1
- package/dist/server/review.js +39 -3
- package/dist/server/routes/router.d.ts +5 -0
- package/dist/server/routes/router.js +7 -0
- package/dist/server/routes/tasks.d.ts +2 -0
- package/dist/server/routes/tasks.js +88 -51
- package/dist/server/routes/types.d.ts +4 -1
- package/dist/server/server.js +63 -8
- package/dist/server/task-underspecified-flag.d.ts +8 -2
- package/dist/server/task-underspecified-flag.js +53 -4
- package/dist/server/task-watchdog.d.ts +8 -0
- package/dist/server/task-watchdog.js +39 -3
- package/dist/server/write.js +14 -6
- package/dist/ui/assets/AgentModelModal-DhY5NOZ1.js +1 -0
- package/dist/ui/assets/{AgentsView-BUCXPt-0.css → AgentsView-B_yMHKiT.css} +1 -1
- package/dist/ui/assets/AgentsView-Dxv4s_ff.js +1 -0
- package/dist/ui/assets/ChecksView-BGUWmHHI.js +0 -0
- package/dist/ui/assets/{ChecksView-BmdCOghF.css → ChecksView-DAZIbg5B.css} +1 -1
- package/dist/ui/assets/{ContextView-C9qd90E1.js → ContextView-BxlQCnkt.js} +3 -3
- package/dist/ui/assets/DashboardView-2xJiQLBz.js +1 -0
- package/dist/ui/assets/{DiffView-BbfRdnyi.js → DiffView-qdIZFGbS.js} +1 -1
- package/dist/ui/assets/FailedTestsList-92u0pppH.js +1 -0
- package/dist/ui/assets/{InputsView-Pm38FTsm.js → InputsView-DtPR9kyl.js} +1 -1
- package/dist/ui/assets/{LoginView-UddKfAab.js → LoginView-DWEf0kRL.js} +1 -1
- package/dist/ui/assets/ReleasesView-JG7zXQPU.js +5 -0
- package/dist/ui/assets/ReleasesView-mGdLAyHK.css +1 -0
- package/dist/ui/assets/SettingsView-C8blwbyh.js +4 -0
- package/dist/ui/assets/StoriesView-1FeSeEMD.js +1 -0
- package/dist/ui/assets/{VoiceDictate-CLPxhrXb.js → VoiceDictate-sbY2kf3g.js} +1 -1
- package/dist/ui/assets/WorkView-Y9JCMf7I.js +2 -0
- package/dist/ui/assets/{abnfDiagram-VCTEODGH-C35Cb3us.js → abnfDiagram-VCTEODGH-DbjEiPQk.js} +1 -1
- package/dist/ui/assets/architecture-7GRP2DOG-D3Zr7GMJ.js +1 -0
- package/dist/ui/assets/{architectureDiagram-5GKGNRK7-nMYcnnYD.js → architectureDiagram-5GKGNRK7-iuYOljXl.js} +1 -1
- package/dist/ui/assets/{blockDiagram-I7D4REHJ-B7tWmthB.js → blockDiagram-I7D4REHJ-BMQZCcl4.js} +1 -1
- package/dist/ui/assets/{c4Diagram-7LVT6UL2-CnY_QO67.js → c4Diagram-7LVT6UL2-CYARlZF5.js} +1 -1
- package/dist/ui/assets/channel-CsOxGNSt.js +1 -0
- package/dist/ui/assets/{chunk-4HAMMTFA-C-gVk_3z.js → chunk-4HAMMTFA-C-0fE7Hb.js} +1 -1
- package/dist/ui/assets/{chunk-75Z2AOVW-D-vfikH_.js → chunk-75Z2AOVW-9h5YgMdt.js} +1 -1
- package/dist/ui/assets/{chunk-DU6HZSFF-BiXVQhaB.js → chunk-DU6HZSFF-9BISDKkf.js} +1 -1
- package/dist/ui/assets/{chunk-F27PBJKO-5-2deQrd.js → chunk-F27PBJKO-VJth4ETL.js} +1 -1
- package/dist/ui/assets/{chunk-GMAD6QVW-CLC7oF0o.js → chunk-GMAD6QVW-CMTnCPtC.js} +1 -1
- package/dist/ui/assets/{chunk-GVQU2GXP-Cr61GTQu.js → chunk-GVQU2GXP-bjPEFMMp.js} +1 -1
- package/dist/ui/assets/{chunk-IMKFNOWR-B1lalIwa.js → chunk-IMKFNOWR-C1BByNVf.js} +1 -1
- package/dist/ui/assets/{chunk-L3NEJ4N5-BnlSzplF.js → chunk-L3NEJ4N5-whMpUmjS.js} +1 -1
- package/dist/ui/assets/{chunk-OSK3NFVY-r9ZRDaqv.js → chunk-OSK3NFVY-B6Ch9_w3.js} +1 -1
- package/dist/ui/assets/{chunk-P2QGCYS3-C23cpE-P.js → chunk-P2QGCYS3-CWRV4Ah9.js} +1 -1
- package/dist/ui/assets/{chunk-POPQ4Y6H-DI_FkXN2.js → chunk-POPQ4Y6H-CcHY8rtu.js} +1 -1
- package/dist/ui/assets/{chunk-PWAF6VOD-CGfzxALl.js → chunk-PWAF6VOD-BC0VBnZU.js} +1 -1
- package/dist/ui/assets/{chunk-SHT3W25Y-DoJGrPBk.js → chunk-SHT3W25Y-DfFIsip4.js} +1 -1
- package/dist/ui/assets/{chunk-SVP7TREG-DyU7jawq.js → chunk-SVP7TREG-C3xL4bvJ.js} +1 -1
- package/dist/ui/assets/{chunk-TICWLB2K-D9vYWbet.js → chunk-TICWLB2K-Bt1FxNVL.js} +1 -1
- package/dist/ui/assets/classDiagram-ZZMXUADV-DXz5ZBJc.js +1 -0
- package/dist/ui/assets/classDiagram-v2-VYDZK3BY-DXz5ZBJc.js +1 -0
- package/dist/ui/assets/{cynefin-OW5HDTMX-DhmDBTIi.js → cynefin-OW5HDTMX-BPQzV4Oh.js} +1 -1
- package/dist/ui/assets/{cynefinDiagram-5FMLGOSQ-DgNCoeoy.js → cynefinDiagram-5FMLGOSQ-BTmi3fdq.js} +1 -1
- package/dist/ui/assets/{dagre-GXQ25YYZ-B7mYz05F.js → dagre-GXQ25YYZ-EBMMAtpz.js} +1 -1
- package/dist/ui/assets/{diagram-S7CK7UJ4-Ddyyvm7S.js → diagram-S7CK7UJ4-BaEfBV0o.js} +1 -1
- package/dist/ui/assets/{diagram-UQ7AKVKN-DUmEoGKb.js → diagram-UQ7AKVKN-BRL9YWBF.js} +1 -1
- package/dist/ui/assets/{diagram-VSXAHHWV-BPHi15wT.js → diagram-VSXAHHWV-CI_jmzDG.js} +1 -1
- package/dist/ui/assets/{diagram-VX7I27RA-KXaNOSYt.js → diagram-VX7I27RA-DoZgqatt.js} +1 -1
- package/dist/ui/assets/{diagram-Z3DM3KII-VY-1gBHG.js → diagram-Z3DM3KII-CEZOwotu.js} +1 -1
- package/dist/ui/assets/{ebnfDiagram-PWID7BFC-DS8pCZ-o.js → ebnfDiagram-PWID7BFC-DoHUC2pF.js} +1 -1
- package/dist/ui/assets/{erDiagram-RLTQ6QDP-CMWkdSPJ.js → erDiagram-RLTQ6QDP-Bpj96FeR.js} +1 -1
- package/dist/ui/assets/eventmodeling-NTZA5JFV-BP096yAZ.js +1 -0
- package/dist/ui/assets/flowDiagram-HODETNUW-CGQzpVgi.js +1 -0
- package/dist/ui/assets/{ganttDiagram-EL5Y4UJY-BpirkJ4v.js → ganttDiagram-EL5Y4UJY-BXWL7ow_.js} +1 -1
- package/dist/ui/assets/{gitGraph-4MIJSDKK-CXSKWh2Z.js → gitGraph-4MIJSDKK-CygEn6Pm.js} +1 -1
- package/dist/ui/assets/{gitGraphDiagram-WWUBYQGX-yjxrBf9P.js → gitGraphDiagram-WWUBYQGX-BEN8kG8Y.js} +1 -1
- package/dist/ui/assets/index-CNf-gQPU.js +54 -0
- package/dist/ui/assets/{index-Dz1V2IY7.css → index-Dp0zyDhg.css} +1 -1
- package/dist/ui/assets/{info-A6RAGUB7-CHSw1OnI.js → info-A6RAGUB7-CG18k5Tc.js} +1 -1
- package/dist/ui/assets/{infoDiagram-27XIBGKW-XIzCq4Td.js → infoDiagram-27XIBGKW-BVEnM-o1.js} +1 -1
- package/dist/ui/assets/{ishikawaDiagram-5VMMS53U-CwTM6Xqe.js → ishikawaDiagram-5VMMS53U-Bz24j_3Q.js} +1 -1
- package/dist/ui/assets/{journeyDiagram-3NMN7TZE-uqHyan0n.js → journeyDiagram-3NMN7TZE-Cw49nq1P.js} +1 -1
- package/dist/ui/assets/{kanban-definition-UXKFOSKX-WHidT1Vi.js → kanban-definition-UXKFOSKX-Ck2BMOZW.js} +1 -1
- package/dist/ui/assets/{line-VhTFB16m.js → line-3ywrXSvS.js} +1 -1
- package/dist/ui/assets/{mermaid-parser.core-CnnWRcLv.js → mermaid-parser.core-pTkacvis.js} +3 -3
- package/dist/ui/assets/{mermaid.core-CMAk9c7e.js → mermaid.core-KI_Ug6Sr.js} +4 -4
- package/dist/ui/assets/{mindmap-definition-YA3MSWOX-CgXnpPZI.js → mindmap-definition-YA3MSWOX-CK7Qs2O6.js} +1 -1
- package/dist/ui/assets/{packet-AYTQ26CC-D79SEu1K.js → packet-AYTQ26CC-CB1LAlf8.js} +1 -1
- package/dist/ui/assets/{pegDiagram-XKGWAZYB-D57a204j.js → pegDiagram-XKGWAZYB-C7wyvhpZ.js} +1 -1
- package/dist/ui/assets/{pie-WAS4IAKB-eJ29qTHN.js → pie-WAS4IAKB-CtkGbS6o.js} +1 -1
- package/dist/ui/assets/{pieDiagram-E7YTZNPT-Cmu79QgL.js → pieDiagram-E7YTZNPT-DqYUbDdy.js} +1 -1
- package/dist/ui/assets/{quadrantDiagram-AXDQQJYC-DkJ_lByB.js → quadrantDiagram-AXDQQJYC-BVzreR-s.js} +1 -1
- package/dist/ui/assets/{radar-RG4KPBEZ-BvmFiqNf.js → radar-RG4KPBEZ-ElYRqk-4.js} +1 -1
- package/dist/ui/assets/{railroad-74A4TZTK-D8y71XD7.js → railroad-74A4TZTK-XUiiGOGw.js} +1 -1
- package/dist/ui/assets/railroad-abnf-HS5TGJTU-ebjcwFgX.js +1 -0
- package/dist/ui/assets/railroad-ebnf-LZEXJU2U-Z5mPRjaM.js +1 -0
- package/dist/ui/assets/railroad-peg-WCYAUIDC-Ds7v_hnS.js +1 -0
- package/dist/ui/assets/{railroadDiagram-O6MQD6OU-DVICUmxm.js → railroadDiagram-O6MQD6OU-C-um7ttl.js} +1 -1
- package/dist/ui/assets/repo-XLbfa7u-.js +2 -0
- package/dist/ui/assets/{requirementDiagram-BXWQKSXE-C-H3_t75.js → requirementDiagram-BXWQKSXE-CB77SNNB.js} +1 -1
- package/dist/ui/assets/{sankeyDiagram-P5KCCOFB-BW8dwgDY.js → sankeyDiagram-P5KCCOFB-D_qnpDIk.js} +1 -1
- package/dist/ui/assets/{sequenceDiagram-WJ2MYXX4-DKdAg21Q.js → sequenceDiagram-WJ2MYXX4-DrR-O2gR.js} +1 -1
- package/dist/ui/assets/{stateDiagram-D77RDMKH-CNl6WXRR.js → stateDiagram-D77RDMKH-Mz-Cjby1.js} +1 -1
- package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-cIIjufMH.js +1 -0
- package/dist/ui/assets/{swimlanes-42K2YHIH-CLrwIems.js → swimlanes-42K2YHIH-BwFAfw2O.js} +1 -1
- package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-BT2MlBys.js +8 -0
- package/dist/ui/assets/{timeline-definition-24CTP7MA-DUaCoZTg.js → timeline-definition-24CTP7MA-DS_U9LBk.js} +1 -1
- package/dist/ui/assets/{treeView-Q6P3EWNA-Da6faho-.js → treeView-Q6P3EWNA-D0Ds2Pnh.js} +1 -1
- package/dist/ui/assets/{treemap-WGGIJYW6-BZ2UeOx0.js → treemap-WGGIJYW6-Dr6ADVsx.js} +1 -1
- package/dist/ui/assets/{vennDiagram-4TSXK5OY-bsrbi5iL.js → vennDiagram-4TSXK5OY-CcEibS-z.js} +1 -1
- package/dist/ui/assets/{wardley-WFR3VGLG-1ye_cK6w.js → wardley-WFR3VGLG-D19UeE7v.js} +1 -1
- package/dist/ui/assets/{wardleyDiagram-VM6X3IG4-BI_0k668.js → wardleyDiagram-VM6X3IG4-m2YJGsr8.js} +1 -1
- package/dist/ui/assets/{xychartDiagram-S5SC5T6Z-ClQRAIHf.js → xychartDiagram-S5SC5T6Z-D-ENA4CQ.js} +1 -1
- package/dist/ui/index.html +7 -8
- package/dist/ui/sw.js +2 -2
- package/dist/ui-app/src/lib/effective-agent.d.ts +47 -0
- package/dist/ui-app/src/lib/effective-agent.js +37 -0
- package/package.json +1 -1
- package/dist/ui/assets/AgentModelModal-IyhQlgi_.js +0 -1
- package/dist/ui/assets/AgentsView-D8wSbWRE.js +0 -1
- package/dist/ui/assets/ChecksView-C_94-h2D.js +0 -0
- package/dist/ui/assets/DashboardView-D0lWbddW.js +0 -1
- package/dist/ui/assets/FailedTestsList-CcmaaJc3.js +0 -1
- package/dist/ui/assets/ReleasesView-CDa4RD79.css +0 -1
- package/dist/ui/assets/ReleasesView-CgTRWJn7.js +0 -5
- package/dist/ui/assets/SettingsView-05j5GTG5.js +0 -4
- package/dist/ui/assets/StoriesView-Bhr88XmF.js +0 -1
- package/dist/ui/assets/WorkView-BBpCR84p.js +0 -2
- package/dist/ui/assets/architecture-7GRP2DOG-C8l6_Iv0.js +0 -1
- package/dist/ui/assets/channel-CseWx8gd.js +0 -1
- package/dist/ui/assets/classDiagram-ZZMXUADV-Bs8Gifp6.js +0 -1
- package/dist/ui/assets/classDiagram-v2-VYDZK3BY-Bs8Gifp6.js +0 -1
- package/dist/ui/assets/config-CSMVq0tG.js +0 -1
- package/dist/ui/assets/eventmodeling-NTZA5JFV-CnRce4Ft.js +0 -1
- package/dist/ui/assets/flowDiagram-HODETNUW-Bl6RGQnG.js +0 -1
- package/dist/ui/assets/index-elTUWd7x.js +0 -54
- package/dist/ui/assets/railroad-abnf-HS5TGJTU-Br6QWckP.js +0 -1
- package/dist/ui/assets/railroad-ebnf-LZEXJU2U-DZ3Kdv94.js +0 -1
- package/dist/ui/assets/railroad-peg-WCYAUIDC-DS-utPYa.js +0 -1
- package/dist/ui/assets/repo-DJOMdIgK.js +0 -2
- package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-BbU_pRfJ.js +0 -1
- package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-UcPzpukO.js +0 -8
package/dist/.build-info.json
CHANGED
package/dist/.build-stamp.json
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI/API parity map (#0723). Every registered `/api/*` route must match at
|
|
3
|
+
* least one rule here or the parity test fails.
|
|
4
|
+
*
|
|
5
|
+
* Allowlist entries use exact method + pattern strings from `Router.listRoutes()`
|
|
6
|
+
* — no broad substring matchers that could hide new routes.
|
|
7
|
+
*/
|
|
8
|
+
export interface ApiRouteRef {
|
|
9
|
+
method: string;
|
|
10
|
+
pattern: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function isApiRouteCovered(route: ApiRouteRef): boolean;
|
|
13
|
+
export declare function uncoveredApiRoutes(routes: readonly ApiRouteRef[]): ApiRouteRef[];
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI/API parity map (#0723). Every registered `/api/*` route must match at
|
|
3
|
+
* least one rule here or the parity test fails.
|
|
4
|
+
*
|
|
5
|
+
* Allowlist entries use exact method + pattern strings from `Router.listRoutes()`
|
|
6
|
+
* — no broad substring matchers that could hide new routes.
|
|
7
|
+
*/
|
|
8
|
+
const exact = (method, pattern) => (r) => r.method === method && r.pattern === pattern;
|
|
9
|
+
const re = (method, patternSource) => (r) => r.method === method && r.pattern === patternSource;
|
|
10
|
+
/** Routes implemented by the server-backed control CLI (#0723). */
|
|
11
|
+
const CONTROL_API_MATCHERS = [
|
|
12
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/(start|pause|message|done)$/.source),
|
|
13
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/preview$/.source),
|
|
14
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/preview\/stop$/.source),
|
|
15
|
+
exact("GET", "/api/config"),
|
|
16
|
+
exact("PATCH", "/api/config"),
|
|
17
|
+
exact("GET", "/api/remote-validation/status"),
|
|
18
|
+
exact("POST", "/api/remote-validation/test"),
|
|
19
|
+
exact("GET", "/api/agents/running"),
|
|
20
|
+
exact("GET", "/api/stats/board"),
|
|
21
|
+
// `repoos review` → PATCH status=review; `repoos override` → PATCH overrides
|
|
22
|
+
re("PATCH", /^\/api\/tasks\/([^/]+)$/.source),
|
|
23
|
+
];
|
|
24
|
+
/** Routes covered by existing file-level CLI commands (not HTTP). */
|
|
25
|
+
const FILE_CLI_MATCHERS = [
|
|
26
|
+
exact("GET", "/api/health"),
|
|
27
|
+
exact("GET", "/api/tasks"),
|
|
28
|
+
re("GET", /^\/api\/tasks\/([^/]+)$/.source),
|
|
29
|
+
exact("POST", "/api/tasks"),
|
|
30
|
+
re("DELETE", /^\/api\/tasks\/([^/]+)$/.source),
|
|
31
|
+
exact("GET", "/api/check-plan"),
|
|
32
|
+
];
|
|
33
|
+
/** UI-only, SSE/hub, auth, or internal routes — no CLI required. */
|
|
34
|
+
const UI_ONLY_MATCHERS = [
|
|
35
|
+
(r) => r.pattern.startsWith("/api/auth/"),
|
|
36
|
+
(r) => r.pattern.startsWith("/api/hub/"),
|
|
37
|
+
(r) => r.pattern.startsWith("/api/telegram/"),
|
|
38
|
+
exact("GET", "/api/index"),
|
|
39
|
+
exact("GET", "/api/board"),
|
|
40
|
+
exact("GET", "/api/counts"),
|
|
41
|
+
exact("GET", "/api/docs"),
|
|
42
|
+
(r) => r.pattern.startsWith("/api/repo/"),
|
|
43
|
+
(r) => r.pattern.startsWith("/api/inputs"),
|
|
44
|
+
(r) => r.pattern.startsWith("/api/skills"),
|
|
45
|
+
(r) => r.pattern.startsWith("/api/skill-registry"),
|
|
46
|
+
(r) => r.pattern.startsWith("/api/stories"),
|
|
47
|
+
exact("GET", "/api/system"),
|
|
48
|
+
exact("GET", "/api/system/logs"),
|
|
49
|
+
(r) => r.pattern.startsWith("/api/support/"),
|
|
50
|
+
(r) => r.pattern.startsWith("/api/tunnel/"),
|
|
51
|
+
(r) => r.pattern.startsWith("/api/release"),
|
|
52
|
+
(r) => r.pattern.startsWith("/api/deployments"),
|
|
53
|
+
(r) => r.pattern.startsWith("/api/chat"),
|
|
54
|
+
(r) => r.pattern.startsWith("/api/debugger"),
|
|
55
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/debugger$/.source),
|
|
56
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/debugger\/message$/.source),
|
|
57
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/debugger\/interrupt$/.source),
|
|
58
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/debugger\/send-to-engineer$/.source),
|
|
59
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/debugger\/send-to-pm$/.source),
|
|
60
|
+
re("POST", /^\/api\/agents\/built-in\/([^/]+)\/run$/.source),
|
|
61
|
+
(r) => r.pattern.startsWith("/api/cto"),
|
|
62
|
+
exact("GET", "/api/stats/by-type"),
|
|
63
|
+
exact("GET", "/api/stats/daily"),
|
|
64
|
+
exact("POST", "/api/system/kill-process"),
|
|
65
|
+
exact("GET", "/api/system/run-tests"),
|
|
66
|
+
exact("POST", "/api/system/run-tests"),
|
|
67
|
+
exact("GET", "/api/attention"),
|
|
68
|
+
exact("GET", "/api/check-runs"),
|
|
69
|
+
(r) => r.pattern.startsWith("/api/integration"),
|
|
70
|
+
exact("GET", "/api/close-out/outcomes"),
|
|
71
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/integration-job$/.source),
|
|
72
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/done\/cancel$/.source),
|
|
73
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/worktree-handoff\/discard$/.source),
|
|
74
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/review$/.source),
|
|
75
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/review\/again$/.source),
|
|
76
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/review\/message$/.source),
|
|
77
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/pm\/message$/.source),
|
|
78
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/pm\/interrupt$/.source),
|
|
79
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/attachments\/([^/]+)$/.source),
|
|
80
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/attachments$/.source),
|
|
81
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/shots$/.source),
|
|
82
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/shots$/.source),
|
|
83
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/shots\/([^/]+)$/.source),
|
|
84
|
+
re("DELETE", /^\/api\/tasks\/([^/]+)\/shots\/([^/]+)$/.source),
|
|
85
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/ui-verification$/.source),
|
|
86
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/needs-input\/dismiss$/.source),
|
|
87
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/clear-worktree$/.source),
|
|
88
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/output$/.source),
|
|
89
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/logs$/.source),
|
|
90
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/checks$/.source),
|
|
91
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/stats$/.source),
|
|
92
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/diff-stats$/.source),
|
|
93
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/diff$/.source),
|
|
94
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/merge-conflict$/.source),
|
|
95
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/worktree-dirty$/.source),
|
|
96
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/file$/.source),
|
|
97
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/remote-validation\/log$/.source),
|
|
98
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/remote-validation\/events$/.source),
|
|
99
|
+
exact("GET", "/api/config/raw"),
|
|
100
|
+
exact("PUT", "/api/config/raw"),
|
|
101
|
+
(r) => r.pattern.startsWith("/api/dev/"),
|
|
102
|
+
(r) => r.pattern.startsWith("/api/models"),
|
|
103
|
+
(r) => r.pattern.startsWith("/api/playground"),
|
|
104
|
+
(r) => r.pattern.startsWith("/api/model-providers"),
|
|
105
|
+
exact("GET", "/api/agents/queued"),
|
|
106
|
+
exact("GET", "/api/agents/detect"),
|
|
107
|
+
exact("GET", "/api/agents/detect/stream"),
|
|
108
|
+
exact("POST", "/api/agents/updates"),
|
|
109
|
+
re("GET", /^\/api\/agents\/([^/]+)\/logs$/.source),
|
|
110
|
+
(r) => r.pattern.startsWith("/api/ntfy/"),
|
|
111
|
+
exact("POST", "/api/transcribe"),
|
|
112
|
+
(r) => r.pattern.startsWith("/api/service/"),
|
|
113
|
+
exact("POST", "/api/server/restart"),
|
|
114
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/(sync|hotfix|abandon|reopen|archive|unarchive)$/.source),
|
|
115
|
+
exact("POST", "/api/tasks/freeform"),
|
|
116
|
+
exact("POST", "/api/docs/create"),
|
|
117
|
+
exact("POST", "/api/docs/freeform"),
|
|
118
|
+
re("GET", /^\/api\/freeform\/runs\/([^/]+)$/.source),
|
|
119
|
+
];
|
|
120
|
+
const ALL_MATCHERS = [...CONTROL_API_MATCHERS, ...FILE_CLI_MATCHERS, ...UI_ONLY_MATCHERS];
|
|
121
|
+
export function isApiRouteCovered(route) {
|
|
122
|
+
if (!route.pattern.startsWith("/api/"))
|
|
123
|
+
return true;
|
|
124
|
+
return ALL_MATCHERS.some((m) => m(route));
|
|
125
|
+
}
|
|
126
|
+
export function uncoveredApiRoutes(routes) {
|
|
127
|
+
return routes.filter((r) => r.pattern.startsWith("/api/") && !isApiRouteCovered(r));
|
|
128
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function cliSessionPath(root: string, cacheDir: string): string;
|
|
2
|
+
export declare function readCliSession(root: string, cacheDir: string): string | null;
|
|
3
|
+
export declare function writeCliSession(root: string, cacheDir: string, token: string): void;
|
|
4
|
+
export declare function clearCliSession(root: string, cacheDir: string): void;
|
|
5
|
+
/** Parse `Set-Cookie` for the RepoOS session token value. */
|
|
6
|
+
export declare function sessionTokenFromSetCookie(setCookie: string | string[] | undefined, cookieName: string): string | null;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persisted RepoOS web session for the CLI (#0723).
|
|
3
|
+
*
|
|
4
|
+
* Stores the HttpOnly session token the server issued at login so repeat CLI
|
|
5
|
+
* calls do not need OTP every time. Mode 0600, never logged. Loopback calls
|
|
6
|
+
* still prefer the server's local-cli-token when present (#0582).
|
|
7
|
+
*/
|
|
8
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
export function cliSessionPath(root, cacheDir) {
|
|
11
|
+
return join(root, cacheDir, "cli-session");
|
|
12
|
+
}
|
|
13
|
+
export function readCliSession(root, cacheDir) {
|
|
14
|
+
try {
|
|
15
|
+
const token = readFileSync(cliSessionPath(root, cacheDir), "utf8").trim();
|
|
16
|
+
return token || null;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function writeCliSession(root, cacheDir, token) {
|
|
23
|
+
const dir = join(root, cacheDir);
|
|
24
|
+
mkdirSync(dir, { recursive: true });
|
|
25
|
+
const path = cliSessionPath(root, cacheDir);
|
|
26
|
+
writeFileSync(path, `${token}\n`, { mode: 0o600 });
|
|
27
|
+
try {
|
|
28
|
+
chmodSync(path, 0o600);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
/* best-effort */
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function clearCliSession(root, cacheDir) {
|
|
35
|
+
try {
|
|
36
|
+
unlinkSync(cliSessionPath(root, cacheDir));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
/* absent */
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Parse `Set-Cookie` for the RepoOS session token value. */
|
|
43
|
+
export function sessionTokenFromSetCookie(setCookie, cookieName) {
|
|
44
|
+
if (!setCookie)
|
|
45
|
+
return null;
|
|
46
|
+
const parts = Array.isArray(setCookie) ? setCookie : [setCookie];
|
|
47
|
+
for (const header of parts) {
|
|
48
|
+
const segment = header.split(";")[0]?.trim();
|
|
49
|
+
if (!segment?.startsWith(`${cookieName}=`))
|
|
50
|
+
continue;
|
|
51
|
+
const value = segment.slice(cookieName.length + 1);
|
|
52
|
+
if (value)
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the engineer a task will run with, from server API payloads (#0723).
|
|
3
|
+
* Uses the same rules as the UI (`effective-agent.ts`), not a local disk reindex.
|
|
4
|
+
*/
|
|
5
|
+
import { type EffectiveAgent, type EffectiveAgentInput } from "../ui-app/src/lib/effective-agent.js";
|
|
6
|
+
export interface ApiTaskOverrides {
|
|
7
|
+
agentOverride?: string | null;
|
|
8
|
+
cliOverride?: string | null;
|
|
9
|
+
modelOverride?: string | null;
|
|
10
|
+
}
|
|
11
|
+
export declare function effectiveEngineerFromApi(agents: readonly EffectiveAgentInput[], task: ApiTaskOverrides): EffectiveAgent;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the engineer a task will run with, from server API payloads (#0723).
|
|
3
|
+
* Uses the same rules as the UI (`effective-agent.ts`), not a local disk reindex.
|
|
4
|
+
*/
|
|
5
|
+
import { resolveEffectiveAgent, } from "../ui-app/src/lib/effective-agent.js";
|
|
6
|
+
export function effectiveEngineerFromApi(agents, task) {
|
|
7
|
+
return resolveEffectiveAgent(agents, {
|
|
8
|
+
agentOverride: task.agentOverride ?? null,
|
|
9
|
+
cliOverride: task.cliOverride ?? null,
|
|
10
|
+
modelOverride: task.modelOverride ?? null,
|
|
11
|
+
}, "engineer");
|
|
12
|
+
}
|
package/dist/cli/help.js
CHANGED
|
@@ -71,9 +71,75 @@ const GROUPS = [
|
|
|
71
71
|
{
|
|
72
72
|
name: "mv",
|
|
73
73
|
usage: "mv <id> <status> [--note <text>] [--force-not-merged]",
|
|
74
|
-
desc: "Move a task to a new status —
|
|
74
|
+
desc: "Move a task to a new status (file write) — use review/done for handoff and close-out",
|
|
75
75
|
flags: '--note "..." · --force-not-merged',
|
|
76
76
|
},
|
|
77
|
+
{
|
|
78
|
+
name: "start",
|
|
79
|
+
usage: "start <id> [--fresh] [--json] [--port N]",
|
|
80
|
+
desc: "Start or resume the engineer on a task (POST /api/tasks/:id/start)",
|
|
81
|
+
flags: "--fresh · --json · --port",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "pause",
|
|
85
|
+
usage: "pause <id> [--json] [--port N]",
|
|
86
|
+
desc: "Pause the running agent (POST /api/tasks/:id/pause)",
|
|
87
|
+
flags: "--json · --port",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "message",
|
|
91
|
+
usage: 'message <id> "<text>" [--json] [--port N]',
|
|
92
|
+
desc: "Send a follow-up to the task agent (POST /api/tasks/:id/message)",
|
|
93
|
+
flags: "--json · --port",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "review",
|
|
97
|
+
usage: "review <id> [--wait] [--json] [--port N]",
|
|
98
|
+
desc: "Request review synchronously (PATCH status=review; waits for handoff)",
|
|
99
|
+
flags: "--wait · --json · --port",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "done",
|
|
103
|
+
usage: "done <id> [--commit-dirty] [--wait] [--json] [--port N]",
|
|
104
|
+
desc: "Move to done through the close-out pipeline (POST /api/tasks/:id/done)",
|
|
105
|
+
flags: "--commit-dirty · --wait · --json · --port",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "override",
|
|
109
|
+
usage: "override <id> [--cli <name>] [--model <model>] [--json] [--port N]",
|
|
110
|
+
desc: "Set per-task engineer overrides (PATCH /api/tasks/:id)",
|
|
111
|
+
flags: "--cli · --model · --json · --port",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "preview",
|
|
115
|
+
usage: "preview <id> [--stop] [--json] [--port N]",
|
|
116
|
+
desc: "Start or stop the task preview (POST /api/tasks/:id/preview)",
|
|
117
|
+
flags: "--stop · --json · --port",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "config",
|
|
121
|
+
usage: "config get|set <key> [value] [--json] [--port N]",
|
|
122
|
+
desc: "Read or write server config (GET/PATCH /api/config)",
|
|
123
|
+
flags: "--json · --port",
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "runners",
|
|
127
|
+
usage: "runners [--probe] [--json] [--port N]",
|
|
128
|
+
desc: "Remote validation runner status (and optional probe)",
|
|
129
|
+
flags: "--probe · --json · --port",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "agents",
|
|
133
|
+
usage: "agents [--json] [--port N]",
|
|
134
|
+
desc: "List running agents (GET /api/agents/running)",
|
|
135
|
+
flags: "--json · --port",
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "stats",
|
|
139
|
+
usage: "stats [--json] [--port N]",
|
|
140
|
+
desc: "Board spend summary (GET /api/stats/board)",
|
|
141
|
+
flags: "--json · --port",
|
|
142
|
+
},
|
|
77
143
|
{
|
|
78
144
|
name: "rm",
|
|
79
145
|
usage: "rm <id> --yes",
|
package/dist/cli/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import { cmdDoctor } from "../commands/doctor.js";
|
|
|
20
20
|
import { cmdCertify } from "../commands/certify.js";
|
|
21
21
|
import { cmdSupport } from "../commands/support.js";
|
|
22
22
|
import { cmdService } from "../commands/service.js";
|
|
23
|
+
import { cmdAgentsRunning, cmdConfig, cmdDone, cmdMessage, cmdOverride, cmdPause, cmdPreview, cmdReview, cmdRunners, cmdStart, cmdStats, } from "../commands/control-api.js";
|
|
23
24
|
import { checkBuild } from "../core/build.js";
|
|
24
25
|
import { loadConfig } from "../core/config.js";
|
|
25
26
|
import { reexecAfterStaleBuild, reexecUnderBunIfRequested } from "../core/runtime.js";
|
|
@@ -192,6 +193,72 @@ function main() {
|
|
|
192
193
|
case "check":
|
|
193
194
|
void cmdCheck(rest);
|
|
194
195
|
break;
|
|
196
|
+
case "start":
|
|
197
|
+
void cmdStart(rest).then((code) => {
|
|
198
|
+
if (code !== 0)
|
|
199
|
+
process.exitCode = code;
|
|
200
|
+
});
|
|
201
|
+
break;
|
|
202
|
+
case "pause":
|
|
203
|
+
void cmdPause(rest).then((code) => {
|
|
204
|
+
if (code !== 0)
|
|
205
|
+
process.exitCode = code;
|
|
206
|
+
});
|
|
207
|
+
break;
|
|
208
|
+
case "message":
|
|
209
|
+
void cmdMessage(rest).then((code) => {
|
|
210
|
+
if (code !== 0)
|
|
211
|
+
process.exitCode = code;
|
|
212
|
+
});
|
|
213
|
+
break;
|
|
214
|
+
case "review":
|
|
215
|
+
void cmdReview(rest).then((code) => {
|
|
216
|
+
if (code !== 0)
|
|
217
|
+
process.exitCode = code;
|
|
218
|
+
});
|
|
219
|
+
break;
|
|
220
|
+
case "done":
|
|
221
|
+
void cmdDone(rest).then((code) => {
|
|
222
|
+
if (code !== 0)
|
|
223
|
+
process.exitCode = code;
|
|
224
|
+
});
|
|
225
|
+
break;
|
|
226
|
+
case "override":
|
|
227
|
+
void cmdOverride(rest).then((code) => {
|
|
228
|
+
if (code !== 0)
|
|
229
|
+
process.exitCode = code;
|
|
230
|
+
});
|
|
231
|
+
break;
|
|
232
|
+
case "preview":
|
|
233
|
+
void cmdPreview(rest).then((code) => {
|
|
234
|
+
if (code !== 0)
|
|
235
|
+
process.exitCode = code;
|
|
236
|
+
});
|
|
237
|
+
break;
|
|
238
|
+
case "config":
|
|
239
|
+
void cmdConfig(rest).then((code) => {
|
|
240
|
+
if (code !== 0)
|
|
241
|
+
process.exitCode = code;
|
|
242
|
+
});
|
|
243
|
+
break;
|
|
244
|
+
case "runners":
|
|
245
|
+
void cmdRunners(rest).then((code) => {
|
|
246
|
+
if (code !== 0)
|
|
247
|
+
process.exitCode = code;
|
|
248
|
+
});
|
|
249
|
+
break;
|
|
250
|
+
case "agents":
|
|
251
|
+
void cmdAgentsRunning(rest).then((code) => {
|
|
252
|
+
if (code !== 0)
|
|
253
|
+
process.exitCode = code;
|
|
254
|
+
});
|
|
255
|
+
break;
|
|
256
|
+
case "stats":
|
|
257
|
+
void cmdStats(rest).then((code) => {
|
|
258
|
+
if (code !== 0)
|
|
259
|
+
process.exitCode = code;
|
|
260
|
+
});
|
|
261
|
+
break;
|
|
195
262
|
case "shot":
|
|
196
263
|
void cmdShot(rest)
|
|
197
264
|
.then((code) => {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for the running RepoOS control plane (#0723).
|
|
3
|
+
*
|
|
4
|
+
* Authenticates with the loopback local-cli token when available (#0582), else a
|
|
5
|
+
* stored session cookie, and transparently re-logs in via the dev backdoor on
|
|
6
|
+
* 401 after a server reload (never prints the backdoor code).
|
|
7
|
+
*/
|
|
8
|
+
import { loadConfig } from "../core/config.js";
|
|
9
|
+
export declare class RepoOsApiError extends Error {
|
|
10
|
+
readonly status: number;
|
|
11
|
+
readonly body?: unknown | undefined;
|
|
12
|
+
constructor(message: string, status: number, body?: unknown | undefined);
|
|
13
|
+
}
|
|
14
|
+
export interface RepoOsApiOptions {
|
|
15
|
+
root?: string;
|
|
16
|
+
port?: number;
|
|
17
|
+
json?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface ApiJson {
|
|
20
|
+
ok?: boolean;
|
|
21
|
+
error?: string;
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
export declare function resolveControlPlaneBase(opts?: RepoOsApiOptions): {
|
|
25
|
+
root: string;
|
|
26
|
+
baseUrl: string;
|
|
27
|
+
config: ReturnType<typeof loadConfig>;
|
|
28
|
+
};
|
|
29
|
+
export declare class RepoOsApi {
|
|
30
|
+
private readonly root;
|
|
31
|
+
private readonly baseUrl;
|
|
32
|
+
private readonly config;
|
|
33
|
+
private session;
|
|
34
|
+
constructor(opts?: RepoOsApiOptions);
|
|
35
|
+
private localToken;
|
|
36
|
+
get base(): string;
|
|
37
|
+
private buildHeaders;
|
|
38
|
+
private devBackdoorLogin;
|
|
39
|
+
health(): Promise<boolean>;
|
|
40
|
+
request(method: string, path: string, body?: unknown, opts?: {
|
|
41
|
+
retryAuth?: boolean;
|
|
42
|
+
}): Promise<{
|
|
43
|
+
status: number;
|
|
44
|
+
body: ApiJson;
|
|
45
|
+
}>;
|
|
46
|
+
requestOk(method: string, path: string, body?: unknown, allowedStatuses?: number[]): Promise<ApiJson>;
|
|
47
|
+
}
|
|
48
|
+
export declare function pollUntil<T>(fn: () => Promise<T | null>, opts: {
|
|
49
|
+
intervalMs?: number;
|
|
50
|
+
timeoutMs?: number;
|
|
51
|
+
label?: string;
|
|
52
|
+
}): Promise<T>;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for the running RepoOS control plane (#0723).
|
|
3
|
+
*
|
|
4
|
+
* Authenticates with the loopback local-cli token when available (#0582), else a
|
|
5
|
+
* stored session cookie, and transparently re-logs in via the dev backdoor on
|
|
6
|
+
* 401 after a server reload (never prints the backdoor code).
|
|
7
|
+
*/
|
|
8
|
+
import { boardRoot, loadConfig, resolveServePort } from "../core/config.js";
|
|
9
|
+
import { SESSION_COOKIE_NAME } from "../core/auth.js";
|
|
10
|
+
import { readLocalCliToken } from "../server/local-token.js";
|
|
11
|
+
import { clearCliSession, readCliSession, sessionTokenFromSetCookie, writeCliSession, } from "./cli-session.js";
|
|
12
|
+
import { readServeLocks } from "../commands/status.js";
|
|
13
|
+
export class RepoOsApiError extends Error {
|
|
14
|
+
status;
|
|
15
|
+
body;
|
|
16
|
+
constructor(message, status, body) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.status = status;
|
|
19
|
+
this.body = body;
|
|
20
|
+
this.name = "RepoOsApiError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function readApiError(body, status) {
|
|
24
|
+
if (body && typeof body === "object" && "error" in body) {
|
|
25
|
+
const err = body.error;
|
|
26
|
+
if (typeof err === "string" && err.trim())
|
|
27
|
+
return err.trim();
|
|
28
|
+
}
|
|
29
|
+
return `HTTP ${status}`;
|
|
30
|
+
}
|
|
31
|
+
export function resolveControlPlaneBase(opts = {}) {
|
|
32
|
+
const { root } = opts.root ? { root: opts.root } : boardRoot();
|
|
33
|
+
const config = loadConfig(root);
|
|
34
|
+
const configuredPort = resolveServePort(root, config);
|
|
35
|
+
const liveLock = readServeLocks(root, config.cacheDir).find((l) => l.alive && (l.port ?? configuredPort) > 0);
|
|
36
|
+
const port = opts.port ?? liveLock?.port ?? configuredPort;
|
|
37
|
+
return { root, baseUrl: `http://127.0.0.1:${port}`, config };
|
|
38
|
+
}
|
|
39
|
+
export class RepoOsApi {
|
|
40
|
+
root;
|
|
41
|
+
baseUrl;
|
|
42
|
+
config;
|
|
43
|
+
session;
|
|
44
|
+
constructor(opts = {}) {
|
|
45
|
+
const resolved = resolveControlPlaneBase(opts);
|
|
46
|
+
this.root = resolved.root;
|
|
47
|
+
this.baseUrl = resolved.baseUrl;
|
|
48
|
+
this.config = resolved.config;
|
|
49
|
+
this.session = readCliSession(this.root, this.config.cacheDir);
|
|
50
|
+
}
|
|
51
|
+
localToken() {
|
|
52
|
+
return readLocalCliToken(this.root, this.config.cacheDir);
|
|
53
|
+
}
|
|
54
|
+
get base() {
|
|
55
|
+
return this.baseUrl;
|
|
56
|
+
}
|
|
57
|
+
buildHeaders(jsonBody) {
|
|
58
|
+
const headers = {};
|
|
59
|
+
if (jsonBody)
|
|
60
|
+
headers["content-type"] = "application/json";
|
|
61
|
+
const token = this.localToken();
|
|
62
|
+
if (token)
|
|
63
|
+
headers["x-repoos-local-token"] = token;
|
|
64
|
+
if (this.session)
|
|
65
|
+
headers.cookie = `${SESSION_COOKIE_NAME}=${this.session}`;
|
|
66
|
+
return headers;
|
|
67
|
+
}
|
|
68
|
+
async devBackdoorLogin() {
|
|
69
|
+
if (this.config.auth?.enabled !== true)
|
|
70
|
+
return true;
|
|
71
|
+
const email = this.config.auth.bootstrapAdmin?.trim().toLowerCase();
|
|
72
|
+
const code = this.config.auth.devBackdoorCode?.trim();
|
|
73
|
+
if (!email || !code || process.env.NODE_ENV === "production")
|
|
74
|
+
return false;
|
|
75
|
+
let res;
|
|
76
|
+
try {
|
|
77
|
+
res = await fetch(`${this.baseUrl}/api/auth/verify-otp`, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: { "content-type": "application/json" },
|
|
80
|
+
body: JSON.stringify({ email, code }),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (!res.ok)
|
|
87
|
+
return false;
|
|
88
|
+
const setCookie = typeof res.headers.getSetCookie === "function"
|
|
89
|
+
? res.headers.getSetCookie()
|
|
90
|
+
: (res.headers.get("set-cookie") ?? undefined);
|
|
91
|
+
const token = sessionTokenFromSetCookie(setCookie, SESSION_COOKIE_NAME);
|
|
92
|
+
if (!token)
|
|
93
|
+
return false;
|
|
94
|
+
this.session = token;
|
|
95
|
+
writeCliSession(this.root, this.config.cacheDir, token);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
async health() {
|
|
99
|
+
try {
|
|
100
|
+
const res = await fetch(`${this.baseUrl}/api/health`);
|
|
101
|
+
return res.ok;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async request(method, path, body, opts = {}) {
|
|
108
|
+
const jsonBody = body !== undefined;
|
|
109
|
+
const doFetch = async () => {
|
|
110
|
+
try {
|
|
111
|
+
return await fetch(`${this.baseUrl}${path}`, {
|
|
112
|
+
method,
|
|
113
|
+
headers: this.buildHeaders(jsonBody),
|
|
114
|
+
body: jsonBody ? JSON.stringify(body) : undefined,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
throw new RepoOsApiError(`Can't reach the RepoOS server at ${this.baseUrl}. Is \`repoos serve\` running? ${err.message}`, 0);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
let res = await doFetch();
|
|
122
|
+
if (res.status === 401 && opts.retryAuth !== false) {
|
|
123
|
+
const relogged = await this.devBackdoorLogin();
|
|
124
|
+
if (relogged) {
|
|
125
|
+
res = await doFetch();
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
clearCliSession(this.root, this.config.cacheDir);
|
|
129
|
+
this.session = null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
let parsed = {};
|
|
133
|
+
const text = await res.text();
|
|
134
|
+
if (text) {
|
|
135
|
+
try {
|
|
136
|
+
parsed = JSON.parse(text);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
parsed = { error: text };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { status: res.status, body: parsed };
|
|
143
|
+
}
|
|
144
|
+
async requestOk(method, path, body, allowedStatuses) {
|
|
145
|
+
const { status, body: data } = await this.request(method, path, body);
|
|
146
|
+
const allowed = allowedStatuses ?? [200];
|
|
147
|
+
if (!allowed.includes(status)) {
|
|
148
|
+
throw new RepoOsApiError(readApiError(data, status), status, data);
|
|
149
|
+
}
|
|
150
|
+
return data;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export async function pollUntil(fn, opts) {
|
|
154
|
+
const intervalMs = opts.intervalMs ?? 250;
|
|
155
|
+
const timeoutMs = opts.timeoutMs ?? 120_000;
|
|
156
|
+
const deadline = Date.now() + timeoutMs;
|
|
157
|
+
while (Date.now() < deadline) {
|
|
158
|
+
const value = await fn();
|
|
159
|
+
if (value !== null)
|
|
160
|
+
return value;
|
|
161
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
162
|
+
}
|
|
163
|
+
throw new RepoOsApiError(opts.label ? `Timed out: ${opts.label}` : "Timed out waiting for operation", 408);
|
|
164
|
+
}
|