@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,82 @@
|
|
|
1
|
+
import { oneLineReason, parseJsonResponse } from "./types.js";
|
|
2
|
+
const MODELS_URL = "https://openrouter.ai/api/v1/models";
|
|
3
|
+
const FETCH_TIMEOUT_MS = 8000;
|
|
4
|
+
const MAX_MODELS = 12;
|
|
5
|
+
const MIN_CONTEXT_TOKENS = 32_000;
|
|
6
|
+
/**
|
|
7
|
+
* Hygiene mirror of the server validator's runId model-segment charset
|
|
8
|
+
* (`RUN_ID_PATTERN` in src/server/routes/playground.ts): a catalog id using
|
|
9
|
+
* characters outside this set would be rejected at run time, so curate it
|
|
10
|
+
* out here rather than shipping a sidebar entry that 400s on click.
|
|
11
|
+
*/
|
|
12
|
+
const MODEL_ID_CHARSET = /^[\w.:/~-]+$/;
|
|
13
|
+
function isTextModel(m) {
|
|
14
|
+
const arch = m.architecture;
|
|
15
|
+
if (!arch)
|
|
16
|
+
return true;
|
|
17
|
+
const input = arch.input_modalities ?? [];
|
|
18
|
+
const output = arch.output_modalities ?? [];
|
|
19
|
+
return input.includes("text") && output.includes("text");
|
|
20
|
+
}
|
|
21
|
+
function namespaceOf(id) {
|
|
22
|
+
return id.split("/")[0] ?? id;
|
|
23
|
+
}
|
|
24
|
+
/** OpenRouter prices are USD-per-token strings; $/1M tokens = price * 1,000,000. */
|
|
25
|
+
function dollarsPerMillion(perTokenUsd) {
|
|
26
|
+
const n = Number(perTokenUsd);
|
|
27
|
+
return Number.isFinite(n) && n > 0 ? n * 1_000_000 : null;
|
|
28
|
+
}
|
|
29
|
+
/** A malformed/non-positive `context_length` isn't a usable context window. */
|
|
30
|
+
function positiveContextWindow(contextLength) {
|
|
31
|
+
return typeof contextLength === "number" && Number.isFinite(contextLength) && contextLength > 0
|
|
32
|
+
? contextLength
|
|
33
|
+
: null;
|
|
34
|
+
}
|
|
35
|
+
/** Curate + parse OpenRouter's models response into playground models. Exported for tests. */
|
|
36
|
+
export function curateOpenRouterModels(models) {
|
|
37
|
+
const candidates = models.filter((m) => {
|
|
38
|
+
if (!m.id || m.id.endsWith(":free") || !MODEL_ID_CHARSET.test(m.id))
|
|
39
|
+
return false;
|
|
40
|
+
if (!isTextModel(m))
|
|
41
|
+
return false;
|
|
42
|
+
const promptPrice = Number(m.pricing?.prompt ?? "0");
|
|
43
|
+
if (!(promptPrice > 0))
|
|
44
|
+
return false;
|
|
45
|
+
if ((m.context_length ?? 0) < MIN_CONTEXT_TOKENS)
|
|
46
|
+
return false;
|
|
47
|
+
return true;
|
|
48
|
+
});
|
|
49
|
+
const bestPerNamespace = new Map();
|
|
50
|
+
for (const m of candidates) {
|
|
51
|
+
const ns = namespaceOf(m.id);
|
|
52
|
+
const current = bestPerNamespace.get(ns);
|
|
53
|
+
if (!current || (m.context_length ?? 0) > (current.context_length ?? 0)) {
|
|
54
|
+
bestPerNamespace.set(ns, m);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return [...bestPerNamespace.values()]
|
|
58
|
+
.sort((a, b) => (b.context_length ?? 0) - (a.context_length ?? 0))
|
|
59
|
+
.slice(0, MAX_MODELS)
|
|
60
|
+
.map((m) => ({
|
|
61
|
+
id: m.id,
|
|
62
|
+
runId: `openrouter/${m.id}`,
|
|
63
|
+
name: m.name ?? m.id,
|
|
64
|
+
reason: oneLineReason(m.description, "Available through OpenRouter."),
|
|
65
|
+
inputPricePerM: dollarsPerMillion(m.pricing?.prompt),
|
|
66
|
+
outputPricePerM: dollarsPerMillion(m.pricing?.completion),
|
|
67
|
+
contextWindow: positiveContextWindow(m.context_length),
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
export const openrouterProvider = {
|
|
71
|
+
id: "openrouter",
|
|
72
|
+
label: "OpenRouter",
|
|
73
|
+
async fetchModels() {
|
|
74
|
+
const res = await fetch(MODELS_URL, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
|
|
75
|
+
if (!res.ok)
|
|
76
|
+
throw new Error(`OpenRouter API returned ${res.status}`);
|
|
77
|
+
const body = await parseJsonResponse(res, "OpenRouter");
|
|
78
|
+
if (!Array.isArray(body.data))
|
|
79
|
+
throw new Error("OpenRouter API returned an unexpected shape");
|
|
80
|
+
return curateOpenRouterModels(body.data);
|
|
81
|
+
},
|
|
82
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export type ModelProviderId = "openrouter" | "opencode-go" | "opencode-zen" | "deepinfra";
|
|
2
|
+
/**
|
|
3
|
+
* One row of the Model providers tab. `kind: "live"` rows have a real spend
|
|
4
|
+
* API behind a user-pasted key (`envVar` names the `.env` variable and
|
|
5
|
+
* `configKey` the loadConfig section field the key is read back through);
|
|
6
|
+
* `kind: "link"` rows are dashboard link-outs with no key collection at all.
|
|
7
|
+
*/
|
|
8
|
+
export interface ModelProviderRow {
|
|
9
|
+
id: ModelProviderId;
|
|
10
|
+
label: string;
|
|
11
|
+
kind: "live" | "link";
|
|
12
|
+
dashboardUrl: string;
|
|
13
|
+
/** One line under the label explaining what the row shows and why. */
|
|
14
|
+
note: string;
|
|
15
|
+
envVar: string | null;
|
|
16
|
+
configKey: "openrouterApiKey" | "opencodeGoApiKey" | null;
|
|
17
|
+
}
|
|
18
|
+
export declare const MODEL_PROVIDERS: ModelProviderRow[];
|
|
19
|
+
export declare function modelProviderById(id: string): ModelProviderRow | undefined;
|
|
20
|
+
export interface OpenRouterCredits {
|
|
21
|
+
totalCredits: number | null;
|
|
22
|
+
totalUsage: number | null;
|
|
23
|
+
remaining: number | null;
|
|
24
|
+
}
|
|
25
|
+
export interface OpenRouterKeyInfo {
|
|
26
|
+
label: string | null;
|
|
27
|
+
usageDaily: number | null;
|
|
28
|
+
usageWeekly: number | null;
|
|
29
|
+
usageMonthly: number | null;
|
|
30
|
+
/** null = this key has no spend limit set. */
|
|
31
|
+
limit: number | null;
|
|
32
|
+
limitRemaining: number | null;
|
|
33
|
+
rateLimit: {
|
|
34
|
+
requests: number | null;
|
|
35
|
+
interval: string | null;
|
|
36
|
+
} | null;
|
|
37
|
+
}
|
|
38
|
+
export declare function parseOpenRouterCredits(body: unknown): OpenRouterCredits;
|
|
39
|
+
export declare function parseOpenRouterKey(body: unknown): OpenRouterKeyInfo;
|
|
40
|
+
export interface OpenRouterSpend {
|
|
41
|
+
credits: OpenRouterCredits | null;
|
|
42
|
+
creditsError: string | null;
|
|
43
|
+
key: OpenRouterKeyInfo | null;
|
|
44
|
+
keyError: string | null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Fetch both OpenRouter endpoints in parallel. Per-endpoint failures are
|
|
48
|
+
* isolated: a key that can read `/key` but is refused by `/credits` (the
|
|
49
|
+
* credits endpoint requires an OpenRouter management key) still renders its
|
|
50
|
+
* key-side data with a clear note about the failed half, instead of losing
|
|
51
|
+
* everything to one upstream restriction.
|
|
52
|
+
*/
|
|
53
|
+
export declare function fetchOpenRouterSpend(apiKey: string): Promise<OpenRouterSpend>;
|
|
54
|
+
export interface OpenCodeGoWindow {
|
|
55
|
+
/** Stable window id, e.g. "five_hour" | "weekly" | "monthly". */
|
|
56
|
+
id: string;
|
|
57
|
+
label: string;
|
|
58
|
+
/** 0–100, percent of this rolling window consumed. */
|
|
59
|
+
usedPct: number | null;
|
|
60
|
+
/** Dollar spend inside the window, when the API reports one. */
|
|
61
|
+
usedUsd: number | null;
|
|
62
|
+
/** Window's dollar limit, when the API reports one. */
|
|
63
|
+
limitUsd: number | null;
|
|
64
|
+
/** ISO timestamp for when the window resets, when reported. */
|
|
65
|
+
resetsAt: string | null;
|
|
66
|
+
}
|
|
67
|
+
export interface OpenCodeGoUsage {
|
|
68
|
+
windows: OpenCodeGoWindow[];
|
|
69
|
+
/** True when the response parsed but no window shape was recognized. */
|
|
70
|
+
unrecognized: boolean;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Extract rolling usage windows from the Go usage response. The endpoint has
|
|
74
|
+
* no published response schema, so this accepts the plausible shapes —
|
|
75
|
+
* `{ windows: [...] }`, `{ data: { windows: [...] } }`, or a keyed object of
|
|
76
|
+
* window entries (`five_hour` / `weekly` / `monthly`, optionally nested under
|
|
77
|
+
* `usage`) — and reports `unrecognized` instead of guessing when none match.
|
|
78
|
+
*/
|
|
79
|
+
export declare function parseOpenCodeGoUsage(body: unknown): OpenCodeGoUsage;
|
|
80
|
+
/**
|
|
81
|
+
* Fetch opencode Go's rolling usage windows. Throws with a clean message on
|
|
82
|
+
* any failure — the route maps that to a 502 and the row shows a retryable
|
|
83
|
+
* error. Unlike OpenRouter there is exactly one endpoint, so there is no
|
|
84
|
+
* per-part error isolation to preserve.
|
|
85
|
+
*/
|
|
86
|
+
export declare function fetchOpenCodeGoUsage(apiKey: string): Promise<OpenCodeGoUsage>;
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live spend/usage data for the Agents page's "Model providers" tab (0327).
|
|
3
|
+
*
|
|
4
|
+
* Scoped to the four v1 providers: OpenRouter and opencode Go expose real
|
|
5
|
+
* spend APIs and render live (each needs an API key the user pastes once —
|
|
6
|
+
* stored via `setDotEnvSecret`, never logged, never echoed back); opencode
|
|
7
|
+
* Zen and DeepInfra have no public balance/usage API and render as dashboard
|
|
8
|
+
* link-outs. All upstream calls are plain `fetch` with a hard timeout — zero
|
|
9
|
+
* runtime dependencies, same rule as the playground adapters next door.
|
|
10
|
+
*
|
|
11
|
+
* The parsers (`parseOpenRouterCredits`, `parseOpenRouterKey`,
|
|
12
|
+
* `parseOpenCodeGoUsage`) are separated from the fetchers so tests can pin
|
|
13
|
+
* the response shapes without stubbing global fetch, and so an upstream
|
|
14
|
+
* response shape change fails as a typed null instead of a crash.
|
|
15
|
+
*/
|
|
16
|
+
import { parseJsonResponse } from "./types.js";
|
|
17
|
+
const OPENROUTER_BASE = "https://openrouter.ai/api/v1";
|
|
18
|
+
const OPENCODE_GO_USAGE_URL = "https://opencode.ai/zen/go/v1/usage";
|
|
19
|
+
const FETCH_TIMEOUT_MS = 8000;
|
|
20
|
+
export const MODEL_PROVIDERS = [
|
|
21
|
+
{
|
|
22
|
+
id: "openrouter",
|
|
23
|
+
label: "OpenRouter",
|
|
24
|
+
kind: "live",
|
|
25
|
+
dashboardUrl: "https://openrouter.ai/credits",
|
|
26
|
+
note: "Credit balance, daily/weekly/monthly spend and rate limits, live from the OpenRouter API.",
|
|
27
|
+
envVar: "REPOOS_OPENROUTER_API_KEY",
|
|
28
|
+
configKey: "openrouterApiKey",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "opencode-go",
|
|
32
|
+
label: "opencode Go",
|
|
33
|
+
kind: "live",
|
|
34
|
+
dashboardUrl: "https://opencode.ai/auth",
|
|
35
|
+
note: "Rolling usage windows (5-hour / weekly / monthly), live from the Go usage API. No dollar balance exists.",
|
|
36
|
+
envVar: "REPOOS_OPENCODE_GO_API_KEY",
|
|
37
|
+
configKey: "opencodeGoApiKey",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "opencode-zen",
|
|
41
|
+
label: "opencode Zen",
|
|
42
|
+
kind: "link",
|
|
43
|
+
dashboardUrl: "https://opencode.ai/auth",
|
|
44
|
+
note: "No public balance API yet — balance and usage live in the Zen console.",
|
|
45
|
+
envVar: null,
|
|
46
|
+
configKey: null,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "deepinfra",
|
|
50
|
+
label: "DeepInfra",
|
|
51
|
+
kind: "link",
|
|
52
|
+
dashboardUrl: "https://deepinfra.com/dash/billing",
|
|
53
|
+
note: "No public billing/usage API — credit balance lives in the DeepInfra dashboard.",
|
|
54
|
+
envVar: null,
|
|
55
|
+
configKey: null,
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
export function modelProviderById(id) {
|
|
59
|
+
return MODEL_PROVIDERS.find((p) => p.id === id);
|
|
60
|
+
}
|
|
61
|
+
/** Upstream error text, extracted from the common `{ error: { message } }` shapes. */
|
|
62
|
+
function upstreamError(body, fallback) {
|
|
63
|
+
if (typeof body === "object" && body !== null) {
|
|
64
|
+
const err = body.error;
|
|
65
|
+
if (typeof err === "string" && err.trim())
|
|
66
|
+
return err.trim();
|
|
67
|
+
if (typeof err === "object" && err !== null) {
|
|
68
|
+
const msg = err.message;
|
|
69
|
+
if (typeof msg === "string" && msg.trim())
|
|
70
|
+
return msg.trim();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return fallback;
|
|
74
|
+
}
|
|
75
|
+
/** A number field that is actually a finite number (null/undefined/"12" all fail). */
|
|
76
|
+
function num(v) {
|
|
77
|
+
return typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
78
|
+
}
|
|
79
|
+
export function parseOpenRouterCredits(body) {
|
|
80
|
+
const data = body?.data ?? {};
|
|
81
|
+
const totalCredits = num(data.total_credits);
|
|
82
|
+
const totalUsage = num(data.total_usage);
|
|
83
|
+
return {
|
|
84
|
+
totalCredits,
|
|
85
|
+
totalUsage,
|
|
86
|
+
remaining: totalCredits != null && totalUsage != null ? totalCredits - totalUsage : null,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export function parseOpenRouterKey(body) {
|
|
90
|
+
const data = body?.data ?? {};
|
|
91
|
+
const rateLimit = data.rate_limit;
|
|
92
|
+
return {
|
|
93
|
+
label: typeof data.label === "string" && data.label ? data.label : null,
|
|
94
|
+
usageDaily: num(data.usage_daily),
|
|
95
|
+
usageWeekly: num(data.usage_weekly),
|
|
96
|
+
usageMonthly: num(data.usage_monthly),
|
|
97
|
+
limit: num(data.limit),
|
|
98
|
+
limitRemaining: num(data.limit_remaining),
|
|
99
|
+
rateLimit: rateLimit && typeof rateLimit === "object"
|
|
100
|
+
? {
|
|
101
|
+
requests: num(rateLimit.requests),
|
|
102
|
+
interval: typeof rateLimit.interval === "string" ? rateLimit.interval : null,
|
|
103
|
+
}
|
|
104
|
+
: null,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
async function getOpenRouterJson(path, apiKey) {
|
|
108
|
+
const res = await fetch(`${OPENROUTER_BASE}${path}`, {
|
|
109
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
110
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
111
|
+
});
|
|
112
|
+
const body = await parseJsonResponse(res, "OpenRouter");
|
|
113
|
+
return { body, status: res.status };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Fetch both OpenRouter endpoints in parallel. Per-endpoint failures are
|
|
117
|
+
* isolated: a key that can read `/key` but is refused by `/credits` (the
|
|
118
|
+
* credits endpoint requires an OpenRouter management key) still renders its
|
|
119
|
+
* key-side data with a clear note about the failed half, instead of losing
|
|
120
|
+
* everything to one upstream restriction.
|
|
121
|
+
*/
|
|
122
|
+
export async function fetchOpenRouterSpend(apiKey) {
|
|
123
|
+
const [credits, key] = await Promise.allSettled([
|
|
124
|
+
getOpenRouterJson("/credits", apiKey),
|
|
125
|
+
getOpenRouterJson("/key", apiKey),
|
|
126
|
+
]);
|
|
127
|
+
const settle = (part) => {
|
|
128
|
+
if (part.status === "fulfilled") {
|
|
129
|
+
const { body, status } = part.value;
|
|
130
|
+
if (status >= 400) {
|
|
131
|
+
return { data: null, error: upstreamError(body, `OpenRouter API returned ${status}`) };
|
|
132
|
+
}
|
|
133
|
+
return { data: body, error: null };
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
data: null,
|
|
137
|
+
error: part.reason instanceof Error ? part.reason.message : "OpenRouter request failed",
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
const creditsPart = settle(credits);
|
|
141
|
+
const keyPart = settle(key);
|
|
142
|
+
return {
|
|
143
|
+
credits: creditsPart.data != null ? parseOpenRouterCredits(creditsPart.data) : null,
|
|
144
|
+
creditsError: creditsPart.error,
|
|
145
|
+
key: keyPart.data != null ? parseOpenRouterKey(keyPart.data) : null,
|
|
146
|
+
keyError: keyPart.error,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const GO_WINDOW_LABELS = {
|
|
150
|
+
five_hour: "5-hour window",
|
|
151
|
+
"5h": "5-hour window",
|
|
152
|
+
fivehour: "5-hour window",
|
|
153
|
+
daily: "Daily window",
|
|
154
|
+
weekly: "Weekly window",
|
|
155
|
+
monthly: "Monthly window",
|
|
156
|
+
};
|
|
157
|
+
function windowLabel(id) {
|
|
158
|
+
const known = GO_WINDOW_LABELS[id.toLowerCase().replace(/[-_\s]/g, "")] ?? null;
|
|
159
|
+
if (known)
|
|
160
|
+
return known;
|
|
161
|
+
const text = id.replace(/[_-]+/g, " ").trim();
|
|
162
|
+
return text ? text.charAt(0).toUpperCase() + text.slice(1) : "Usage window";
|
|
163
|
+
}
|
|
164
|
+
function clampPct(v) {
|
|
165
|
+
return Math.max(0, Math.min(100, v));
|
|
166
|
+
}
|
|
167
|
+
/** Percent out of a window object, tolerating 0–1 ratios and 0–100 percents. */
|
|
168
|
+
function windowPct(win) {
|
|
169
|
+
for (const k of ["utilization", "used_percent", "usedPercent", "percent_used", "percentUsed"]) {
|
|
170
|
+
const v = num(win[k]);
|
|
171
|
+
if (v != null)
|
|
172
|
+
return clampPct(v <= 1 ? v * 100 : v);
|
|
173
|
+
}
|
|
174
|
+
for (const k of ["percent", "percentage", "pct", "usedPct"]) {
|
|
175
|
+
const v = num(win[k]);
|
|
176
|
+
if (v != null)
|
|
177
|
+
return clampPct(v);
|
|
178
|
+
}
|
|
179
|
+
const used = num(win.used) ?? num(win.usage) ?? num(win.spent) ?? num(win.cost);
|
|
180
|
+
const limit = num(win.limit) ?? num(win.cap) ?? num(win.allowance) ?? num(win.max);
|
|
181
|
+
if (used != null && limit != null && limit > 0)
|
|
182
|
+
return clampPct((used / limit) * 100);
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
function windowResetsAt(win) {
|
|
186
|
+
for (const k of ["resets_at", "reset_at", "resetsAt", "resetAt"]) {
|
|
187
|
+
const v = win[k];
|
|
188
|
+
if (typeof v === "string" && v && !Number.isNaN(Date.parse(v)))
|
|
189
|
+
return new Date(v).toISOString();
|
|
190
|
+
}
|
|
191
|
+
const unix = num(win.resets_at_unix) ?? num(win.reset_at_unix) ?? num(win.resetsAtUnix);
|
|
192
|
+
if (unix != null && unix > 0) {
|
|
193
|
+
return new Date(unix < 1e12 ? unix * 1000 : unix).toISOString();
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
/** One recognized window entry from an id/name-ish key and a window object. */
|
|
198
|
+
function windowFromEntry(id, win) {
|
|
199
|
+
const used = num(win.used) ?? num(win.usage) ?? num(win.spent) ?? num(win.cost);
|
|
200
|
+
const limit = num(win.limit) ?? num(win.cap) ?? num(win.allowance) ?? num(win.max);
|
|
201
|
+
return {
|
|
202
|
+
id,
|
|
203
|
+
label: typeof win.label === "string" && win.label ? win.label : windowLabel(id),
|
|
204
|
+
usedPct: windowPct(win),
|
|
205
|
+
usedUsd: used != null && used <= 10_000 ? used : null,
|
|
206
|
+
limitUsd: limit != null && limit <= 10_000 ? limit : null,
|
|
207
|
+
resetsAt: windowResetsAt(win),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const WINDOW_ID_KEYS = ["id", "type", "window", "name", "key", "period", "granularity"];
|
|
211
|
+
/**
|
|
212
|
+
* Extract rolling usage windows from the Go usage response. The endpoint has
|
|
213
|
+
* no published response schema, so this accepts the plausible shapes —
|
|
214
|
+
* `{ windows: [...] }`, `{ data: { windows: [...] } }`, or a keyed object of
|
|
215
|
+
* window entries (`five_hour` / `weekly` / `monthly`, optionally nested under
|
|
216
|
+
* `usage`) — and reports `unrecognized` instead of guessing when none match.
|
|
217
|
+
*/
|
|
218
|
+
export function parseOpenCodeGoUsage(body) {
|
|
219
|
+
const windows = [];
|
|
220
|
+
const push = (id, win) => {
|
|
221
|
+
if (!id || typeof win !== "object" || win === null || Array.isArray(win))
|
|
222
|
+
return;
|
|
223
|
+
windows.push(windowFromEntry(id, win));
|
|
224
|
+
};
|
|
225
|
+
const scopes = [body];
|
|
226
|
+
if (typeof body === "object" && body !== null)
|
|
227
|
+
scopes.push(body.data);
|
|
228
|
+
for (const scope of scopes) {
|
|
229
|
+
if (typeof scope !== "object" || scope === null)
|
|
230
|
+
continue;
|
|
231
|
+
const obj = scope;
|
|
232
|
+
const list = obj.windows;
|
|
233
|
+
if (Array.isArray(list)) {
|
|
234
|
+
for (const entry of list) {
|
|
235
|
+
if (typeof entry !== "object" || entry === null)
|
|
236
|
+
continue;
|
|
237
|
+
const e = entry;
|
|
238
|
+
const id = WINDOW_ID_KEYS.map((k) => e[k]).find((v) => typeof v === "string" && !!v);
|
|
239
|
+
push(id ?? `window-${windows.length}`, e);
|
|
240
|
+
}
|
|
241
|
+
if (windows.length)
|
|
242
|
+
return { windows, unrecognized: false };
|
|
243
|
+
}
|
|
244
|
+
const usageScope = typeof obj.usage === "object" && obj.usage !== null && !Array.isArray(obj.usage)
|
|
245
|
+
? obj.usage
|
|
246
|
+
: obj;
|
|
247
|
+
for (const [key, value] of Object.entries(usageScope)) {
|
|
248
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
249
|
+
continue;
|
|
250
|
+
const win = value;
|
|
251
|
+
// Only trust an entry when the KEY names a known window or the object
|
|
252
|
+
// itself carries explicit window signals (a percent/utilization field
|
|
253
|
+
// or a reset timestamp). A bare `{ cost: 0.05 }` must not become a row.
|
|
254
|
+
const keyIsWindow = GO_WINDOW_LABELS[key.toLowerCase().replace(/[-_\s]/g, "")] != null;
|
|
255
|
+
if (keyIsWindow || windowPct(win) != null || windowResetsAt(win) != null)
|
|
256
|
+
push(key, win);
|
|
257
|
+
}
|
|
258
|
+
if (windows.length)
|
|
259
|
+
return { windows, unrecognized: false };
|
|
260
|
+
}
|
|
261
|
+
return { windows: [], unrecognized: true };
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Fetch opencode Go's rolling usage windows. Throws with a clean message on
|
|
265
|
+
* any failure — the route maps that to a 502 and the row shows a retryable
|
|
266
|
+
* error. Unlike OpenRouter there is exactly one endpoint, so there is no
|
|
267
|
+
* per-part error isolation to preserve.
|
|
268
|
+
*/
|
|
269
|
+
export async function fetchOpenCodeGoUsage(apiKey) {
|
|
270
|
+
let res;
|
|
271
|
+
try {
|
|
272
|
+
res = await fetch(OPENCODE_GO_USAGE_URL, {
|
|
273
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
274
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
throw new Error("Could not reach the opencode Go usage API.");
|
|
279
|
+
}
|
|
280
|
+
const body = await parseJsonResponse(res, "opencode Go");
|
|
281
|
+
if (res.status === 401)
|
|
282
|
+
throw new Error(upstreamError(body, "opencode rejected the API key."));
|
|
283
|
+
if (res.status >= 400)
|
|
284
|
+
throw new Error(upstreamError(body, `opencode Go API returned ${res.status}`));
|
|
285
|
+
return parseOpenCodeGoUsage(body);
|
|
286
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider abstraction for the Model Playground (0313). Each adapter knows
|
|
3
|
+
* how to turn one external catalog (DeepInfra, OpenRouter, …) into a small
|
|
4
|
+
* curated list of "worth trying" models with pricing and context window.
|
|
5
|
+
* Adding a provider is a one-file change: implement `fetchModels` and
|
|
6
|
+
* register it in `PLAYGROUND_PROVIDERS` (see ./index.ts).
|
|
7
|
+
*/
|
|
8
|
+
/** One model worth trying, enriched with pricing/context from its provider. */
|
|
9
|
+
export interface PlaygroundModel {
|
|
10
|
+
/** The id as understood by the provider's own API, e.g. "zai-org/GLM-5.3-Flash". */
|
|
11
|
+
id: string;
|
|
12
|
+
/** The opencode-runnable id: `${providerId}/${id}`, passed as `--model`. */
|
|
13
|
+
runId: string;
|
|
14
|
+
/** Display name. */
|
|
15
|
+
name: string;
|
|
16
|
+
/** One-line reason this model is worth trying. */
|
|
17
|
+
reason: string;
|
|
18
|
+
/** USD per 1M input tokens, or null when the provider doesn't report it. */
|
|
19
|
+
inputPricePerM: number | null;
|
|
20
|
+
/** USD per 1M output tokens, or null when the provider doesn't report it. */
|
|
21
|
+
outputPricePerM: number | null;
|
|
22
|
+
/** Context window in tokens, or null when unknown. */
|
|
23
|
+
contextWindow: number | null;
|
|
24
|
+
}
|
|
25
|
+
/** A per-provider catalog adapter. `fetchModels` should throw on failure — the registry handles caching/fallback. */
|
|
26
|
+
export interface PlaygroundProviderAdapter {
|
|
27
|
+
/** Stable id, matches the opencode provider prefix (e.g. "deepinfra"). */
|
|
28
|
+
id: string;
|
|
29
|
+
/** Display label, e.g. "DeepInfra". */
|
|
30
|
+
label: string;
|
|
31
|
+
fetchModels(): Promise<PlaygroundModel[]>;
|
|
32
|
+
}
|
|
33
|
+
/** Result shape served to the client for one provider. */
|
|
34
|
+
export interface PlaygroundProviderGroup {
|
|
35
|
+
id: string;
|
|
36
|
+
label: string;
|
|
37
|
+
models: PlaygroundModel[];
|
|
38
|
+
/** Set when the live fetch failed and no cached snapshot could cover for it. */
|
|
39
|
+
error?: string;
|
|
40
|
+
fetchedAt: string;
|
|
41
|
+
}
|
|
42
|
+
/** Trim a description down to its first sentence for a compact "reason" line. */
|
|
43
|
+
export declare function oneLineReason(description: string | undefined | null, fallback: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Parse a fetch `Response` as JSON on behalf of a provider adapter.
|
|
46
|
+
*
|
|
47
|
+
* A blocked/rate-limited request often comes back `ok: true` with an HTML
|
|
48
|
+
* block page instead of the expected JSON body. Calling `res.json()` directly
|
|
49
|
+
* on that throws a `SyntaxError` whose message embeds a raw snippet of the
|
|
50
|
+
* HTML (e.g. `Unexpected token '<', "<!DOCTYPE "... is not valid JSON`) — that
|
|
51
|
+
* snippet then propagates untouched into `PlaygroundProviderGroup.error` and
|
|
52
|
+
* renders in the sidebar in place of the model list. Checking content-type
|
|
53
|
+
* first, and falling back to a clean message if parsing still fails, keeps
|
|
54
|
+
* every error shown to the user readable text instead of a chunk of markup.
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseJsonResponse<T>(res: Response, providerLabel: string): Promise<T>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider abstraction for the Model Playground (0313). Each adapter knows
|
|
3
|
+
* how to turn one external catalog (DeepInfra, OpenRouter, …) into a small
|
|
4
|
+
* curated list of "worth trying" models with pricing and context window.
|
|
5
|
+
* Adding a provider is a one-file change: implement `fetchModels` and
|
|
6
|
+
* register it in `PLAYGROUND_PROVIDERS` (see ./index.ts).
|
|
7
|
+
*/
|
|
8
|
+
/** Trim a description down to its first sentence for a compact "reason" line. */
|
|
9
|
+
export function oneLineReason(description, fallback) {
|
|
10
|
+
const text = (description ?? "").trim();
|
|
11
|
+
if (!text)
|
|
12
|
+
return fallback;
|
|
13
|
+
const firstSentence = text.split(/(?<=[.!?])\s/)[0] ?? text;
|
|
14
|
+
return firstSentence.length > 140 ? `${firstSentence.slice(0, 137)}...` : firstSentence;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Parse a fetch `Response` as JSON on behalf of a provider adapter.
|
|
18
|
+
*
|
|
19
|
+
* A blocked/rate-limited request often comes back `ok: true` with an HTML
|
|
20
|
+
* block page instead of the expected JSON body. Calling `res.json()` directly
|
|
21
|
+
* on that throws a `SyntaxError` whose message embeds a raw snippet of the
|
|
22
|
+
* HTML (e.g. `Unexpected token '<', "<!DOCTYPE "... is not valid JSON`) — that
|
|
23
|
+
* snippet then propagates untouched into `PlaygroundProviderGroup.error` and
|
|
24
|
+
* renders in the sidebar in place of the model list. Checking content-type
|
|
25
|
+
* first, and falling back to a clean message if parsing still fails, keeps
|
|
26
|
+
* every error shown to the user readable text instead of a chunk of markup.
|
|
27
|
+
*/
|
|
28
|
+
export async function parseJsonResponse(res, providerLabel) {
|
|
29
|
+
const contentType = res.headers?.get("content-type") ?? "";
|
|
30
|
+
if (contentType && !contentType.includes("json")) {
|
|
31
|
+
throw new Error(`${providerLabel} returned a non-JSON response (${contentType || "unknown content type"})`);
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
return (await res.json());
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
throw new Error(`${providerLabel} returned a response that could not be parsed as JSON`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ORIGINAL_PROMPT_HEADING } from "./task.js";
|
|
2
|
+
import { type CommitNewFileResult } from "./git.js";
|
|
3
|
+
import { type RepoOSConfig, type RepoIndex, type Task, type Status } from "./types.js";
|
|
4
|
+
export interface CreateTaskInput {
|
|
5
|
+
title: string;
|
|
6
|
+
type?: string;
|
|
7
|
+
status?: Status;
|
|
8
|
+
priority?: string;
|
|
9
|
+
area?: string;
|
|
10
|
+
assignedTo?: string;
|
|
11
|
+
createdBy?: string;
|
|
12
|
+
branch?: string;
|
|
13
|
+
body?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The user's raw freeform prompt. When provided, it is preserved verbatim
|
|
16
|
+
* under a `## Original prompt` section in the initial body so the user's
|
|
17
|
+
* capture is never lost, even if a later agent pass rewrites the body.
|
|
18
|
+
*/
|
|
19
|
+
originalPrompt?: string;
|
|
20
|
+
}
|
|
21
|
+
export { ORIGINAL_PROMPT_HEADING };
|
|
22
|
+
/**
|
|
23
|
+
* Append the user's raw freeform prompt as a `## Original prompt` section to a
|
|
24
|
+
* body, unless the section is already present (idempotent). An empty prompt is
|
|
25
|
+
* left untouched. This keeps the raw capture in the file no matter how a later
|
|
26
|
+
* agent pass rewrites the rest of the body.
|
|
27
|
+
*/
|
|
28
|
+
export declare function withOriginalPromptSection(body: string, originalPrompt?: string): string;
|
|
29
|
+
export interface RepoOS {
|
|
30
|
+
config: RepoOSConfig;
|
|
31
|
+
/** Build a fresh index from the files on disk. */
|
|
32
|
+
reindex(): RepoIndex;
|
|
33
|
+
/** All tasks, optionally filtered by status. */
|
|
34
|
+
getTasks(status?: Status): Task[];
|
|
35
|
+
/** A single task by id, or null. */
|
|
36
|
+
getTask(id: string): Task | null;
|
|
37
|
+
/** Counts per status. */
|
|
38
|
+
counts(): Record<Status, number>;
|
|
39
|
+
/**
|
|
40
|
+
* Change a task's status (frontmatter edit; file stays put). An optional
|
|
41
|
+
* `note` is appended to the activity log in the same operation.
|
|
42
|
+
*/
|
|
43
|
+
updateStatus(id: string, status: Status, note?: string): Task;
|
|
44
|
+
/**
|
|
45
|
+
* Append a short, free-form note to a task's activity log (see addNote).
|
|
46
|
+
* The note is recorded as its own activity entry and never rewrites the
|
|
47
|
+
* task body.
|
|
48
|
+
*/
|
|
49
|
+
addNote(id: string, note: string): Task;
|
|
50
|
+
/** Update arbitrary known fields on a task. */
|
|
51
|
+
updateTask(id: string, patch: Partial<Omit<Task, "git" | "absPath" | "path">>): Task;
|
|
52
|
+
/** Create a new task file under workDir. Returns the created task. */
|
|
53
|
+
createTask(input: CreateTaskInput): Task;
|
|
54
|
+
/** Surgically commit a single new file (fail-soft; never touches other files). */
|
|
55
|
+
commitNewFile(absPath: string, message: string): CommitNewFileResult;
|
|
56
|
+
}
|
|
57
|
+
export declare function createRepoOS(root?: string): RepoOS;
|
|
58
|
+
export * from "./types.js";
|
|
59
|
+
export { loadConfig, findRepoRoot } from "./config.js";
|