modelrig 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +94 -0
- package/LICENSE +201 -0
- package/README.md +145 -8
- package/data/PROVENANCE.md +19 -0
- package/data/pricing-manual.json +22 -0
- package/data/pricing-map.json +46362 -0
- package/data/pricing-overlay.json +419 -0
- package/dist/adapters/anthropic.d.ts +90 -0
- package/dist/adapters/anthropic.d.ts.map +1 -0
- package/dist/adapters/anthropic.js +296 -0
- package/dist/adapters/build-adapters.d.ts +11 -0
- package/dist/adapters/build-adapters.d.ts.map +1 -0
- package/dist/adapters/build-adapters.js +45 -0
- package/dist/adapters/deepseek.d.ts +47 -0
- package/dist/adapters/deepseek.d.ts.map +1 -0
- package/dist/adapters/deepseek.js +216 -0
- package/dist/adapters/gemini.d.ts +44 -0
- package/dist/adapters/gemini.d.ts.map +1 -0
- package/dist/adapters/gemini.js +262 -0
- package/dist/adapters/grok.d.ts +53 -0
- package/dist/adapters/grok.d.ts.map +1 -0
- package/dist/adapters/grok.js +232 -0
- package/dist/adapters/openai-compat.d.ts +80 -0
- package/dist/adapters/openai-compat.d.ts.map +1 -0
- package/dist/adapters/openai-compat.js +244 -0
- package/dist/adapters/openai-stream.d.ts +58 -0
- package/dist/adapters/openai-stream.d.ts.map +1 -0
- package/dist/adapters/openai-stream.js +120 -0
- package/dist/adapters/openai.d.ts +55 -0
- package/dist/adapters/openai.d.ts.map +1 -0
- package/dist/adapters/openai.js +281 -0
- package/dist/adapters/sampling.d.ts +28 -0
- package/dist/adapters/sampling.d.ts.map +1 -0
- package/dist/adapters/sampling.js +34 -0
- package/dist/adapters/tool-mapping.d.ts +92 -0
- package/dist/adapters/tool-mapping.d.ts.map +1 -0
- package/dist/adapters/tool-mapping.js +143 -0
- package/dist/artifacts/artifact.d.ts +137 -0
- package/dist/artifacts/artifact.d.ts.map +1 -0
- package/dist/artifacts/artifact.js +377 -0
- package/dist/artifacts/content-uploader.d.ts +87 -0
- package/dist/artifacts/content-uploader.d.ts.map +1 -0
- package/dist/artifacts/content-uploader.js +124 -0
- package/dist/artifacts/index.d.ts +63 -0
- package/dist/artifacts/index.d.ts.map +1 -0
- package/dist/artifacts/index.js +80 -0
- package/dist/artifacts/run-context.d.ts +129 -0
- package/dist/artifacts/run-context.d.ts.map +1 -0
- package/dist/artifacts/run-context.js +208 -0
- package/dist/artifacts/scrub.d.ts +60 -0
- package/dist/artifacts/scrub.d.ts.map +1 -0
- package/dist/artifacts/scrub.js +139 -0
- package/dist/artifacts/step.d.ts +26 -0
- package/dist/artifacts/step.d.ts.map +1 -0
- package/dist/artifacts/step.js +37 -0
- package/dist/artifacts/taint.d.ts +63 -0
- package/dist/artifacts/taint.d.ts.map +1 -0
- package/dist/artifacts/taint.js +76 -0
- package/dist/bakeoff/cache-sim.d.ts +99 -0
- package/dist/bakeoff/cache-sim.d.ts.map +1 -0
- package/dist/bakeoff/cache-sim.js +126 -0
- package/dist/bakeoff/engine.d.ts +52 -0
- package/dist/bakeoff/engine.d.ts.map +1 -0
- package/dist/bakeoff/engine.js +163 -0
- package/dist/bakeoff/eval-cases.d.ts +96 -0
- package/dist/bakeoff/eval-cases.d.ts.map +1 -0
- package/dist/bakeoff/eval-cases.js +105 -0
- package/dist/bakeoff/replay.d.ts +52 -0
- package/dist/bakeoff/replay.d.ts.map +1 -0
- package/dist/bakeoff/replay.js +94 -0
- package/dist/bakeoff/report.d.ts +41 -0
- package/dist/bakeoff/report.d.ts.map +1 -0
- package/dist/bakeoff/report.js +142 -0
- package/dist/bakeoff/samples.d.ts +77 -0
- package/dist/bakeoff/samples.d.ts.map +1 -0
- package/dist/bakeoff/samples.js +148 -0
- package/dist/bakeoff/stats.d.ts +13 -0
- package/dist/bakeoff/stats.d.ts.map +1 -0
- package/dist/bakeoff/stats.js +48 -0
- package/dist/bakeoff/types.d.ts +37 -0
- package/dist/bakeoff/types.d.ts.map +1 -0
- package/dist/bakeoff/types.js +10 -0
- package/dist/cli.d.ts +25 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +590 -0
- package/dist/config.d.ts +204 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +252 -0
- package/dist/deprecations.d.ts +32 -0
- package/dist/deprecations.d.ts.map +1 -0
- package/dist/deprecations.js +59 -0
- package/dist/feedback/coverage.d.ts +32 -0
- package/dist/feedback/coverage.d.ts.map +1 -0
- package/dist/feedback/coverage.js +95 -0
- package/dist/feedback/feedback.d.ts +35 -0
- package/dist/feedback/feedback.d.ts.map +1 -0
- package/dist/feedback/feedback.js +39 -0
- package/dist/gateway/http-protocol.d.ts +175 -0
- package/dist/gateway/http-protocol.d.ts.map +1 -0
- package/dist/gateway/http-protocol.js +105 -0
- package/dist/index.d.ts +242 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +576 -0
- package/dist/init/finder.d.ts +94 -0
- package/dist/init/finder.d.ts.map +1 -0
- package/dist/init/finder.js +343 -0
- package/dist/init/init-cli.d.ts +10 -0
- package/dist/init/init-cli.d.ts.map +1 -0
- package/dist/init/init-cli.js +86 -0
- package/dist/init/observe-cli.d.ts +19 -0
- package/dist/init/observe-cli.d.ts.map +1 -0
- package/dist/init/observe-cli.js +73 -0
- package/dist/init/observe.d.ts +81 -0
- package/dist/init/observe.d.ts.map +1 -0
- package/dist/init/observe.js +245 -0
- package/dist/init/run.d.ts +99 -0
- package/dist/init/run.d.ts.map +1 -0
- package/dist/init/run.js +281 -0
- package/dist/init/signatures.d.ts +39 -0
- package/dist/init/signatures.d.ts.map +1 -0
- package/dist/init/signatures.js +116 -0
- package/dist/matrix/aggregate.d.ts +89 -0
- package/dist/matrix/aggregate.d.ts.map +1 -0
- package/dist/matrix/aggregate.js +186 -0
- package/dist/query/grammar.d.ts +35 -0
- package/dist/query/grammar.d.ts.map +1 -0
- package/dist/query/grammar.js +279 -0
- package/dist/query/run.d.ts +8 -0
- package/dist/query/run.d.ts.map +1 -0
- package/dist/query/run.js +21 -0
- package/dist/registry/build.d.ts +219 -0
- package/dist/registry/build.d.ts.map +1 -0
- package/dist/registry/build.js +313 -0
- package/dist/registry/cache-policy.d.ts +94 -0
- package/dist/registry/cache-policy.d.ts.map +1 -0
- package/dist/registry/cache-policy.js +238 -0
- package/dist/registry/call-notes.d.ts +11 -0
- package/dist/registry/call-notes.d.ts.map +1 -0
- package/dist/registry/call-notes.js +110 -0
- package/dist/registry/claims.d.ts +63 -0
- package/dist/registry/claims.d.ts.map +1 -0
- package/dist/registry/claims.js +107 -0
- package/dist/registry/effective-rate.d.ts +151 -0
- package/dist/registry/effective-rate.d.ts.map +1 -0
- package/dist/registry/effective-rate.js +156 -0
- package/dist/registry/facts.d.ts +32 -0
- package/dist/registry/facts.d.ts.map +1 -0
- package/dist/registry/facts.js +136 -0
- package/dist/registry/fingerprint.d.ts +20 -0
- package/dist/registry/fingerprint.d.ts.map +1 -0
- package/dist/registry/fingerprint.js +150 -0
- package/dist/registry/parity.d.ts +52 -0
- package/dist/registry/parity.d.ts.map +1 -0
- package/dist/registry/parity.js +82 -0
- package/dist/registry/pricing-shape.d.ts +113 -0
- package/dist/registry/pricing-shape.d.ts.map +1 -0
- package/dist/registry/pricing-shape.js +123 -0
- package/dist/registry/pricing.d.ts +43 -0
- package/dist/registry/pricing.d.ts.map +1 -0
- package/dist/registry/pricing.js +127 -0
- package/dist/registry/rows.d.ts +16 -0
- package/dist/registry/rows.d.ts.map +1 -0
- package/dist/registry/rows.js +57 -0
- package/dist/registry/seed.d.ts +43 -0
- package/dist/registry/seed.d.ts.map +1 -0
- package/dist/registry/seed.js +97 -0
- package/dist/registry/select.d.ts +68 -0
- package/dist/registry/select.d.ts.map +1 -0
- package/dist/registry/select.js +141 -0
- package/dist/registry/staleness.d.ts +18 -0
- package/dist/registry/staleness.d.ts.map +1 -0
- package/dist/registry/staleness.js +39 -0
- package/dist/rigindex/bootstrap.d.ts +36 -0
- package/dist/rigindex/bootstrap.d.ts.map +1 -0
- package/dist/rigindex/bootstrap.js +97 -0
- package/dist/rigindex/fit.d.ts +24 -0
- package/dist/rigindex/fit.d.ts.map +1 -0
- package/dist/rigindex/fit.js +76 -0
- package/dist/rigindex/gates.d.ts +52 -0
- package/dist/rigindex/gates.d.ts.map +1 -0
- package/dist/rigindex/gates.js +109 -0
- package/dist/rigindex/graph.d.ts +31 -0
- package/dist/rigindex/graph.d.ts.map +1 -0
- package/dist/rigindex/graph.js +120 -0
- package/dist/rigindex/index.d.ts +23 -0
- package/dist/rigindex/index.d.ts.map +1 -0
- package/dist/rigindex/index.js +112 -0
- package/dist/rigindex/types.d.ts +76 -0
- package/dist/rigindex/types.d.ts.map +1 -0
- package/dist/rigindex/types.js +8 -0
- package/dist/routes/cache-lint.d.ts +41 -0
- package/dist/routes/cache-lint.d.ts.map +1 -0
- package/dist/routes/cache-lint.js +81 -0
- package/dist/routes/draft.d.ts +83 -0
- package/dist/routes/draft.d.ts.map +1 -0
- package/dist/routes/draft.js +135 -0
- package/dist/routes/loader.d.ts +63 -0
- package/dist/routes/loader.d.ts.map +1 -0
- package/dist/routes/loader.js +574 -0
- package/dist/routes/manifest.d.ts +24 -0
- package/dist/routes/manifest.d.ts.map +1 -0
- package/dist/routes/manifest.js +118 -0
- package/dist/routes/resolved-bundle.d.ts +79 -0
- package/dist/routes/resolved-bundle.d.ts.map +1 -0
- package/dist/routes/resolved-bundle.js +148 -0
- package/dist/routes/sampling-config.d.ts +32 -0
- package/dist/routes/sampling-config.d.ts.map +1 -0
- package/dist/routes/sampling-config.js +91 -0
- package/dist/routes/serveability.d.ts +52 -0
- package/dist/routes/serveability.d.ts.map +1 -0
- package/dist/routes/serveability.js +73 -0
- package/dist/routes/template.d.ts +18 -0
- package/dist/routes/template.d.ts.map +1 -0
- package/dist/routes/template.js +94 -0
- package/dist/run/ajv.d.ts +22 -0
- package/dist/run/ajv.d.ts.map +1 -0
- package/dist/run/ajv.js +32 -0
- package/dist/run/cache-handle.d.ts +58 -0
- package/dist/run/cache-handle.d.ts.map +1 -0
- package/dist/run/cache-handle.js +54 -0
- package/dist/run/cache-stamp.d.ts +65 -0
- package/dist/run/cache-stamp.d.ts.map +1 -0
- package/dist/run/cache-stamp.js +0 -0
- package/dist/run/capture-write.d.ts +33 -0
- package/dist/run/capture-write.d.ts.map +1 -0
- package/dist/run/capture-write.js +55 -0
- package/dist/run/cost.d.ts +26 -0
- package/dist/run/cost.d.ts.map +1 -0
- package/dist/run/cost.js +41 -0
- package/dist/run/dispatch.d.ts +99 -0
- package/dist/run/dispatch.d.ts.map +1 -0
- package/dist/run/dispatch.js +191 -0
- package/dist/run/envelope.d.ts +21 -0
- package/dist/run/envelope.d.ts.map +1 -0
- package/dist/run/envelope.js +45 -0
- package/dist/run/grounding.d.ts +56 -0
- package/dist/run/grounding.d.ts.map +1 -0
- package/dist/run/grounding.js +89 -0
- package/dist/run/raw.d.ts +88 -0
- package/dist/run/raw.d.ts.map +1 -0
- package/dist/run/raw.js +286 -0
- package/dist/run/record-attempt.d.ts +40 -0
- package/dist/run/record-attempt.d.ts.map +1 -0
- package/dist/run/record-attempt.js +99 -0
- package/dist/run/repair.d.ts +36 -0
- package/dist/run/repair.d.ts.map +1 -0
- package/dist/run/repair.js +61 -0
- package/dist/run/resolve.d.ts +99 -0
- package/dist/run/resolve.d.ts.map +1 -0
- package/dist/run/resolve.js +203 -0
- package/dist/run/retry.d.ts +19 -0
- package/dist/run/retry.d.ts.map +1 -0
- package/dist/run/retry.js +50 -0
- package/dist/run/run.d.ts +55 -0
- package/dist/run/run.d.ts.map +1 -0
- package/dist/run/run.js +540 -0
- package/dist/run/stream.d.ts +49 -0
- package/dist/run/stream.d.ts.map +1 -0
- package/dist/run/stream.js +87 -0
- package/dist/run/validate.d.ts +15 -0
- package/dist/run/validate.d.ts.map +1 -0
- package/dist/run/validate.js +55 -0
- package/dist/savings/cache-ledger.d.ts +108 -0
- package/dist/savings/cache-ledger.d.ts.map +1 -0
- package/dist/savings/cache-ledger.js +174 -0
- package/dist/savings/cache-reconcile.d.ts +64 -0
- package/dist/savings/cache-reconcile.d.ts.map +1 -0
- package/dist/savings/cache-reconcile.js +59 -0
- package/dist/savings/cache-stats.d.ts +63 -0
- package/dist/savings/cache-stats.d.ts.map +1 -0
- package/dist/savings/cache-stats.js +108 -0
- package/dist/savings/emit.d.ts +38 -0
- package/dist/savings/emit.d.ts.map +1 -0
- package/dist/savings/emit.js +100 -0
- package/dist/savings/odometer.d.ts +32 -0
- package/dist/savings/odometer.d.ts.map +1 -0
- package/dist/savings/odometer.js +70 -0
- package/dist/savings/swap-execute.d.ts +58 -0
- package/dist/savings/swap-execute.d.ts.map +1 -0
- package/dist/savings/swap-execute.js +130 -0
- package/dist/savings/swaps-client.d.ts +55 -0
- package/dist/savings/swaps-client.d.ts.map +1 -0
- package/dist/savings/swaps-client.js +95 -0
- package/dist/settings/api-fetcher.d.ts +39 -0
- package/dist/settings/api-fetcher.d.ts.map +1 -0
- package/dist/settings/api-fetcher.js +92 -0
- package/dist/settings/index.d.ts +10 -0
- package/dist/settings/index.d.ts.map +1 -0
- package/dist/settings/index.js +15 -0
- package/dist/settings/task-settings.d.ts +91 -0
- package/dist/settings/task-settings.d.ts.map +1 -0
- package/dist/settings/task-settings.js +116 -0
- package/dist/status.d.ts +77 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +113 -0
- package/dist/telemetry/artifact-store.d.ts +48 -0
- package/dist/telemetry/artifact-store.d.ts.map +1 -0
- package/dist/telemetry/artifact-store.js +189 -0
- package/dist/telemetry/cache-miss-streak.d.ts +79 -0
- package/dist/telemetry/cache-miss-streak.d.ts.map +1 -0
- package/dist/telemetry/cache-miss-streak.js +99 -0
- package/dist/telemetry/capture.d.ts +31 -0
- package/dist/telemetry/capture.d.ts.map +1 -0
- package/dist/telemetry/capture.js +81 -0
- package/dist/telemetry/exporter-setup.d.ts +28 -0
- package/dist/telemetry/exporter-setup.d.ts.map +1 -0
- package/dist/telemetry/exporter-setup.js +34 -0
- package/dist/telemetry/exporter.d.ts +137 -0
- package/dist/telemetry/exporter.d.ts.map +1 -0
- package/dist/telemetry/exporter.js +510 -0
- package/dist/telemetry/health.d.ts +76 -0
- package/dist/telemetry/health.d.ts.map +1 -0
- package/dist/telemetry/health.js +92 -0
- package/dist/telemetry/ingest-protocol.d.ts +310 -0
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -0
- package/dist/telemetry/ingest-protocol.js +363 -0
- package/dist/telemetry/load-error-report.d.ts +24 -0
- package/dist/telemetry/load-error-report.d.ts.map +1 -0
- package/dist/telemetry/load-error-report.js +50 -0
- package/dist/telemetry/otel-map.d.ts +121 -0
- package/dist/telemetry/otel-map.d.ts.map +1 -0
- package/dist/telemetry/otel-map.js +224 -0
- package/dist/telemetry/otel-normalize.d.ts +104 -0
- package/dist/telemetry/otel-normalize.d.ts.map +1 -0
- package/dist/telemetry/otel-normalize.js +530 -0
- package/dist/telemetry/otlp-emitter.d.ts +52 -0
- package/dist/telemetry/otlp-emitter.d.ts.map +1 -0
- package/dist/telemetry/otlp-emitter.js +144 -0
- package/dist/telemetry/otlp-proto.d.ts +29 -0
- package/dist/telemetry/otlp-proto.d.ts.map +1 -0
- package/dist/telemetry/otlp-proto.js +266 -0
- package/dist/telemetry/sinks.d.ts +90 -0
- package/dist/telemetry/sinks.d.ts.map +1 -0
- package/dist/telemetry/sinks.js +180 -0
- package/dist/telemetry/store.d.ts +65 -0
- package/dist/telemetry/store.d.ts.map +1 -0
- package/dist/telemetry/store.js +547 -0
- package/dist/tenancy/api-key-format.d.ts +77 -0
- package/dist/tenancy/api-key-format.d.ts.map +1 -0
- package/dist/tenancy/api-key-format.js +110 -0
- package/dist/tenancy/constants.d.ts +23 -0
- package/dist/tenancy/constants.d.ts.map +1 -0
- package/dist/tenancy/constants.js +25 -0
- package/dist/tenancy/scrub.d.ts +47 -0
- package/dist/tenancy/scrub.d.ts.map +1 -0
- package/dist/tenancy/scrub.js +85 -0
- package/dist/types/config-teaching.d.ts +62 -0
- package/dist/types/config-teaching.d.ts.map +1 -0
- package/dist/types/config-teaching.js +183 -0
- package/dist/types/failures-teaching.d.ts +56 -0
- package/dist/types/failures-teaching.d.ts.map +1 -0
- package/dist/types/failures-teaching.js +177 -0
- package/dist/types/failures.d.ts +27 -0
- package/dist/types/failures.d.ts.map +1 -0
- package/dist/types/failures.js +41 -0
- package/dist/types/provider.d.ts +200 -0
- package/dist/types/provider.d.ts.map +1 -0
- package/dist/types/provider.js +40 -0
- package/dist/types/route.d.ts +205 -0
- package/dist/types/route.d.ts.map +1 -0
- package/dist/types/route.js +6 -0
- package/dist/types/telemetry.d.ts +206 -0
- package/dist/types/telemetry.d.ts.map +1 -0
- package/dist/types/telemetry.js +6 -0
- package/dist/validate-cli.d.ts +12 -0
- package/dist/validate-cli.d.ts.map +1 -0
- package/dist/validate-cli.js +57 -0
- package/dist/verify/capture.d.ts +66 -0
- package/dist/verify/capture.d.ts.map +1 -0
- package/dist/verify/capture.js +180 -0
- package/dist/verify/cli-run.d.ts +23 -0
- package/dist/verify/cli-run.d.ts.map +1 -0
- package/dist/verify/cli-run.js +57 -0
- package/dist/verify/diff.d.ts +48 -0
- package/dist/verify/diff.d.ts.map +1 -0
- package/dist/verify/diff.js +157 -0
- package/dist/verify/index.d.ts +17 -0
- package/dist/verify/index.d.ts.map +1 -0
- package/dist/verify/index.js +24 -0
- package/dist/verify/report.d.ts +14 -0
- package/dist/verify/report.d.ts.map +1 -0
- package/dist/verify/report.js +88 -0
- package/dist/verify/types.d.ts +101 -0
- package/dist/verify/types.d.ts.map +1 -0
- package/dist/verify/types.js +17 -0
- package/dist/verify/verify.d.ts +29 -0
- package/dist/verify/verify.d.ts.map +1 -0
- package/dist/verify/verify.js +148 -0
- package/dist/watch/budget.d.ts +19 -0
- package/dist/watch/budget.d.ts.map +1 -0
- package/dist/watch/budget.js +35 -0
- package/dist/watch/evaluate.d.ts +225 -0
- package/dist/watch/evaluate.d.ts.map +1 -0
- package/dist/watch/evaluate.js +155 -0
- package/dist/watch/monitor.d.ts +116 -0
- package/dist/watch/monitor.d.ts.map +1 -0
- package/dist/watch/monitor.js +199 -0
- package/dist/watch/proposals.d.ts +89 -0
- package/dist/watch/proposals.d.ts.map +1 -0
- package/dist/watch/proposals.js +197 -0
- package/dist/watch/run-record.d.ts +64 -0
- package/dist/watch/run-record.d.ts.map +1 -0
- package/dist/watch/run-record.js +86 -0
- package/dist/watch/run-reported.d.ts +20 -0
- package/dist/watch/run-reported.d.ts.map +1 -0
- package/dist/watch/run-reported.js +53 -0
- package/dist/watch/run.d.ts +73 -0
- package/dist/watch/run.d.ts.map +1 -0
- package/dist/watch/run.js +83 -0
- package/dist/watch/state-store.d.ts +48 -0
- package/dist/watch/state-store.d.ts.map +1 -0
- package/dist/watch/state-store.js +86 -0
- package/dist/watch/state.d.ts +21 -0
- package/dist/watch/state.d.ts.map +1 -0
- package/dist/watch/state.js +93 -0
- package/package.json +56 -14
- package/registry/LICENSE +20 -0
- package/registry/leaderboard.json +3639 -0
- package/registry/registry.json +7263 -0
- package/index.js +0 -3
package/dist/cli.js
ADDED
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* modelrig CLI — Phase 3 plan §3 + Phase 4 plan §3:
|
|
5
|
+
* modelrig bakeoff --route X --variants a,b,c --replay-last N
|
|
6
|
+
* [--budget-usd usd] [--json] [--no-mirror] (--envelope is a kept alias)
|
|
7
|
+
* modelrig bakeoff --from-proposal <id> [--replay-last N] [--json]
|
|
8
|
+
* modelrig watch run [--events fixtures.json] [--json]
|
|
9
|
+
*
|
|
10
|
+
* Bake-offs replay locally captured traffic, write the artifact JSON next
|
|
11
|
+
* to the telemetry db (.modelrig/bakeoffs/<id>.json), upsert the `bakeoffs`
|
|
12
|
+
* mirror when the Supabase sink is configured, and print the matrix.
|
|
13
|
+
* Captures never travel — only aggregate metrics. Watch cycles spend $0;
|
|
14
|
+
* --from-proposal runs draw from the monthly watch envelope and attach
|
|
15
|
+
* their evidence to the proposal (suggested → evidenced). Nothing here
|
|
16
|
+
* executes a swap — that is `modelrig swap execute --approve`, a separate,
|
|
17
|
+
* human command.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.parseArgs = parseArgs;
|
|
21
|
+
exports.main = main;
|
|
22
|
+
const node_crypto_1 = require("node:crypto");
|
|
23
|
+
const node_fs_1 = require("node:fs");
|
|
24
|
+
const node_os_1 = require("node:os");
|
|
25
|
+
const node_path_1 = require("node:path");
|
|
26
|
+
const init_cli_1 = require("./init/init-cli");
|
|
27
|
+
const observe_cli_1 = require("./init/observe-cli");
|
|
28
|
+
const failures_1 = require("./types/failures");
|
|
29
|
+
const failures_teaching_1 = require("./types/failures-teaching");
|
|
30
|
+
const config_1 = require("./config");
|
|
31
|
+
const index_1 = require("./index");
|
|
32
|
+
const status_1 = require("./status");
|
|
33
|
+
const engine_1 = require("./bakeoff/engine");
|
|
34
|
+
const samples_1 = require("./bakeoff/samples");
|
|
35
|
+
const report_1 = require("./bakeoff/report");
|
|
36
|
+
const cli_run_1 = require("./verify/cli-run");
|
|
37
|
+
const loader_1 = require("./routes/loader");
|
|
38
|
+
const validate_cli_1 = require("./validate-cli");
|
|
39
|
+
const store_1 = require("./telemetry/store");
|
|
40
|
+
const budget_1 = require("./watch/budget");
|
|
41
|
+
const proposals_1 = require("./watch/proposals");
|
|
42
|
+
const run_reported_1 = require("./watch/run-reported");
|
|
43
|
+
const state_1 = require("./watch/state");
|
|
44
|
+
const state_store_1 = require("./watch/state-store");
|
|
45
|
+
const swaps_client_1 = require("./savings/swaps-client");
|
|
46
|
+
const swap_execute_1 = require("./savings/swap-execute");
|
|
47
|
+
const BARE_FLAGS = new Set([
|
|
48
|
+
"json",
|
|
49
|
+
"no-mirror",
|
|
50
|
+
"approve",
|
|
51
|
+
"keep-outputs",
|
|
52
|
+
"keep-outputs=all",
|
|
53
|
+
// modelrig init (Phase 5 WS2)
|
|
54
|
+
"dry-run",
|
|
55
|
+
"include-tests",
|
|
56
|
+
// modelrig observe / init --observe (T0/T1 scaffold)
|
|
57
|
+
"observe",
|
|
58
|
+
]);
|
|
59
|
+
function parseArgs(argv) {
|
|
60
|
+
const positional = [];
|
|
61
|
+
const named = new Map();
|
|
62
|
+
const bare = new Set();
|
|
63
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
64
|
+
const arg = argv[i];
|
|
65
|
+
if (arg.startsWith("--")) {
|
|
66
|
+
const name = arg.slice(2);
|
|
67
|
+
if (BARE_FLAGS.has(name)) {
|
|
68
|
+
bare.add(name);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const value = argv[i + 1];
|
|
72
|
+
if (value === undefined || value.startsWith("--")) {
|
|
73
|
+
throw new Error(`flag ${arg} needs a value`);
|
|
74
|
+
}
|
|
75
|
+
named.set(name, value);
|
|
76
|
+
i += 1;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
positional.push(arg);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return { positional, named, bare };
|
|
83
|
+
}
|
|
84
|
+
/** Control-plane client from config.export — the watch/proposal surfaces
|
|
85
|
+
* need it; a clear error beats a silent no-op. */
|
|
86
|
+
function proposalsClientFrom(config) {
|
|
87
|
+
const exp = (0, config_1.directExportOf)(config, "this command");
|
|
88
|
+
return (0, proposals_1.createProposalsClient)({
|
|
89
|
+
supabaseUrl: exp.supabaseUrl,
|
|
90
|
+
serviceRoleKey: exp.serviceRoleKey,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/** A3: --keep-outputs (winner) | --keep-outputs=all | off. */
|
|
94
|
+
function keepOutputsMode(flags) {
|
|
95
|
+
if (flags.bare.has("keep-outputs=all"))
|
|
96
|
+
return "all";
|
|
97
|
+
if (flags.bare.has("keep-outputs"))
|
|
98
|
+
return "winner";
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
async function runAndReportBakeoff(config, flags, route, variants, replayLastN, envelope) {
|
|
102
|
+
const keep = keepOutputsMode(flags);
|
|
103
|
+
if (keep !== undefined)
|
|
104
|
+
(0, config_1.directExportOf)(config, "--keep-outputs (bakeoff_samples lives there)");
|
|
105
|
+
const mirrorTarget = config.export?.mode === "direct" ? config.export : undefined;
|
|
106
|
+
const rig = (0, index_1.createRig)(config);
|
|
107
|
+
try {
|
|
108
|
+
const artifact = await rig.bakeoff(route, {
|
|
109
|
+
variants,
|
|
110
|
+
replayLastN,
|
|
111
|
+
...(envelope !== undefined ? { envelopeUsdPerVariant: envelope } : {}),
|
|
112
|
+
...(keep !== undefined ? { keepOutputs: keep } : {}),
|
|
113
|
+
});
|
|
114
|
+
const outDir = (0, node_path_1.join)((0, node_path_1.dirname)(config.telemetryDbPath), "bakeoffs");
|
|
115
|
+
(0, node_fs_1.mkdirSync)(outDir, { recursive: true });
|
|
116
|
+
const artifactPath = (0, node_path_1.join)(outDir, `${artifact.id}.json`);
|
|
117
|
+
(0, node_fs_1.writeFileSync)(artifactPath, `${JSON.stringify(artifact, null, 2)}\n`);
|
|
118
|
+
let mirrored = false;
|
|
119
|
+
if (mirrorTarget !== undefined && !flags.bare.has("no-mirror")) {
|
|
120
|
+
await (0, engine_1.mirrorBakeoff)(artifact, {
|
|
121
|
+
supabaseUrl: mirrorTarget.supabaseUrl,
|
|
122
|
+
serviceRoleKey: mirrorTarget.serviceRoleKey,
|
|
123
|
+
sourceId: mirrorTarget.sourceId ?? (0, node_os_1.hostname)(),
|
|
124
|
+
});
|
|
125
|
+
mirrored = true;
|
|
126
|
+
}
|
|
127
|
+
if (flags.bare.has("json")) {
|
|
128
|
+
console.log(JSON.stringify(artifact, null, 2));
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
(0, report_1.printMatrix)(artifact);
|
|
132
|
+
}
|
|
133
|
+
console.log(`artifact → ${artifactPath}${mirrored ? " · mirrored to bakeoffs" : ""}` +
|
|
134
|
+
(keep !== undefined
|
|
135
|
+
? ` · review samples kept (${keep}) → console /samples?bakeoff=${artifact.id}`
|
|
136
|
+
: ""));
|
|
137
|
+
return artifact;
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
// One-shot CLI: await the settled drain so the exporter's final batch is
|
|
141
|
+
// guaranteed to land before the process exits (H1).
|
|
142
|
+
await rig.close();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async function cmdBakeoff(flags) {
|
|
146
|
+
const config = (0, config_1.loadConfigFromEnv)();
|
|
147
|
+
const proposalId = flags.named.get("from-proposal");
|
|
148
|
+
if (proposalId !== undefined) {
|
|
149
|
+
// Evidence run for a watch proposal — budget-enveloped by the monthly
|
|
150
|
+
// watch cap, evidence attached on completion (suggested → evidenced).
|
|
151
|
+
const client = proposalsClientFrom(config);
|
|
152
|
+
const proposal = await client.get(proposalId);
|
|
153
|
+
if (proposal === null)
|
|
154
|
+
throw new Error(`no proposal "${proposalId}" on the control plane`);
|
|
155
|
+
if (proposal.status !== "suggested" && proposal.status !== "evidenced") {
|
|
156
|
+
throw new Error(`proposal ${proposalId} is ${proposal.status} — evidence runs apply to ` +
|
|
157
|
+
"suggested/evidenced proposals only");
|
|
158
|
+
}
|
|
159
|
+
const routes = (0, loader_1.loadRoutes)(config.routesDir);
|
|
160
|
+
const bundle = routes.get(proposal.route);
|
|
161
|
+
const variants = ["default", ...bundle.variants.map((v) => v.name)];
|
|
162
|
+
const lastRaw = flags.named.get("replay-last");
|
|
163
|
+
const replayLastN = lastRaw !== undefined ? Number(lastRaw) : 25;
|
|
164
|
+
if (!Number.isInteger(replayLastN) || replayLastN < 1) {
|
|
165
|
+
throw new Error("--replay-last must be a positive integer");
|
|
166
|
+
}
|
|
167
|
+
// Monthly watch budget: reserve headroom, split it across variants,
|
|
168
|
+
// accrue what the run actually spent afterwards.
|
|
169
|
+
const budgetStore = (0, store_1.openTelemetryStore)(config.telemetryDbPath);
|
|
170
|
+
try {
|
|
171
|
+
const monthlyUsd = config.watch?.monthlyUsd ?? budget_1.DEFAULT_WATCH_MONTHLY_USD;
|
|
172
|
+
const reservation = (0, budget_1.reserveWatchBudget)(budgetStore, monthlyUsd, new Date());
|
|
173
|
+
const perVariant = reservation.remainingUsd / variants.length;
|
|
174
|
+
console.log(`watch budget ${reservation.key}: $${reservation.remainingUsd.toFixed(2)} remaining → ` +
|
|
175
|
+
`$${perVariant.toFixed(2)}/variant cap`);
|
|
176
|
+
const artifact = await runAndReportBakeoff(config, flags, proposal.route, variants, replayLastN, perVariant);
|
|
177
|
+
const spentUsd = artifact.variants.reduce((a, m) => a + m.samples * m.meanCostUsd, 0);
|
|
178
|
+
budgetStore.accrue(reservation.key, spentUsd);
|
|
179
|
+
await client.attachEvidence(proposal.id, {
|
|
180
|
+
bakeoff_id: artifact.id,
|
|
181
|
+
replay_n: artifact.replayN,
|
|
182
|
+
ran_at: artifact.ranAt,
|
|
183
|
+
spent_usd: spentUsd,
|
|
184
|
+
proposal: artifact.proposal,
|
|
185
|
+
matrix: artifact.variants.map((m) => ({
|
|
186
|
+
variant: m.variant,
|
|
187
|
+
samples: m.samples,
|
|
188
|
+
conform_rate: m.conformRate,
|
|
189
|
+
conform_ci95: m.conformCi95,
|
|
190
|
+
effective_usd_per_1k_conformant: Number.isFinite(m.effectiveUsdPer1kConformant)
|
|
191
|
+
? m.effectiveUsdPer1kConformant
|
|
192
|
+
: null,
|
|
193
|
+
pricing_missing: m.pricingMissing,
|
|
194
|
+
})),
|
|
195
|
+
});
|
|
196
|
+
console.log(`evidence attached → proposal ${proposal.id} is now evidenced ` +
|
|
197
|
+
`($${spentUsd.toFixed(2)} accrued to ${reservation.key}). ` +
|
|
198
|
+
"Approval stays human: modelrig swap execute <proposalId> --approve");
|
|
199
|
+
return 0;
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
budgetStore.close();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Replay a task's promoted eval cases instead of the last-N captures (E4 chunk A).
|
|
206
|
+
const evalTask = flags.named.get("from-eval-cases");
|
|
207
|
+
if (evalTask !== undefined) {
|
|
208
|
+
const vRaw = flags.named.get("variants");
|
|
209
|
+
if (vRaw === undefined)
|
|
210
|
+
throw new Error("bakeoff --from-eval-cases <task> requires --variants a,b,c");
|
|
211
|
+
const vs = vRaw.split(",").map((v) => v.trim()).filter((v) => v !== "");
|
|
212
|
+
const envRaw = flags.named.get("budget-usd") ?? flags.named.get("envelope");
|
|
213
|
+
const env = envRaw !== undefined ? Number(envRaw) : undefined;
|
|
214
|
+
if (env !== undefined && (!Number.isFinite(env) || env <= 0))
|
|
215
|
+
throw new Error("--budget-usd must be positive");
|
|
216
|
+
return (0, report_1.runEvalCaseBakeoffCli)(config, evalTask, vs, env, keepOutputsMode(flags), flags.bare.has("json"));
|
|
217
|
+
}
|
|
218
|
+
const route = flags.named.get("route");
|
|
219
|
+
const variantsRaw = flags.named.get("variants");
|
|
220
|
+
const lastRaw = flags.named.get("replay-last");
|
|
221
|
+
if (route === undefined || variantsRaw === undefined || lastRaw === undefined) {
|
|
222
|
+
throw new Error("bakeoff requires --route, --variants a,b,c and --replay-last N (or --from-proposal <id>)");
|
|
223
|
+
}
|
|
224
|
+
const variants = variantsRaw.split(",").map((v) => v.trim()).filter((v) => v !== "");
|
|
225
|
+
const replayLastN = Number(lastRaw);
|
|
226
|
+
if (!Number.isInteger(replayLastN) || replayLastN < 1) {
|
|
227
|
+
throw new Error("--replay-last must be a positive integer");
|
|
228
|
+
}
|
|
229
|
+
// §4: --budget-usd is the named envelope knob; --envelope is kept as an alias.
|
|
230
|
+
const envelopeRaw = flags.named.get("budget-usd") ?? flags.named.get("envelope");
|
|
231
|
+
const envelope = envelopeRaw !== undefined ? Number(envelopeRaw) : undefined;
|
|
232
|
+
if (envelope !== undefined && (!Number.isFinite(envelope) || envelope <= 0)) {
|
|
233
|
+
throw new Error("--budget-usd must be a positive number of dollars (per replay variant)");
|
|
234
|
+
}
|
|
235
|
+
await runAndReportBakeoff(config, flags, route, variants, replayLastN, envelope);
|
|
236
|
+
return 0;
|
|
237
|
+
}
|
|
238
|
+
const DEFAULT_WATCH_THRESHOLD_PCT = 10;
|
|
239
|
+
async function cmdWatch(flags) {
|
|
240
|
+
const sub = flags.positional[0];
|
|
241
|
+
if (sub !== "run") {
|
|
242
|
+
throw new Error("usage: modelrig watch run [--events fixtures.json] [--json]");
|
|
243
|
+
}
|
|
244
|
+
const config = (0, config_1.loadConfigFromEnv)();
|
|
245
|
+
const client = proposalsClientFrom(config);
|
|
246
|
+
const routes = (0, loader_1.loadRoutes)(config.routesDir);
|
|
247
|
+
const routeEconomics = routes.list().map((b) => ({
|
|
248
|
+
route: b.route,
|
|
249
|
+
candidates: b.candidates.map((c) => ({ provider: c.provider, model: c.model })),
|
|
250
|
+
}));
|
|
251
|
+
const providers = new Set(routeEconomics.flatMap((r) => r.candidates.map((c) => c.provider)));
|
|
252
|
+
const eventsPath = flags.named.get("events");
|
|
253
|
+
const injectedEvents = eventsPath !== undefined
|
|
254
|
+
? JSON.parse((0, node_fs_1.readFileSync)(eventsPath, "utf8"))
|
|
255
|
+
: undefined;
|
|
256
|
+
// A4: on a cron container the pricing snapshot MUST outlive the process.
|
|
257
|
+
// A file-backed store there re-seeds every run and can never fire an event.
|
|
258
|
+
// Durable watch state needs the database itself, so api mode gets the local
|
|
259
|
+
// file (and the warning below) exactly as an unconfigured rig does.
|
|
260
|
+
const watchExport = config.export?.mode === "direct" ? config.export : undefined;
|
|
261
|
+
const store = (0, state_store_1.createWatchStateStore)({
|
|
262
|
+
filePath: (0, node_path_1.join)((0, node_path_1.dirname)(config.telemetryDbPath), "watch-state.json"),
|
|
263
|
+
export: watchExport,
|
|
264
|
+
});
|
|
265
|
+
if (watchExport === undefined) {
|
|
266
|
+
console.warn("[modelrig] watch state is local-file only — fine on your machine, but on an ephemeral " +
|
|
267
|
+
"container every cycle re-seeds and no price change can ever fire. Configure " +
|
|
268
|
+
"MODELRIG_SUPABASE_URL + MODELRIG_SUPABASE_SERVICE_ROLE_KEY for durable state.");
|
|
269
|
+
}
|
|
270
|
+
// The cycle reports its own outcome to the control plane. A4 gave this job
|
|
271
|
+
// durable state but no proof of life: `control_state` sat empty for a week
|
|
272
|
+
// and "never scheduled", "running and failing" and "running and finding
|
|
273
|
+
// nothing" were indistinguishable from outside, readable only through the
|
|
274
|
+
// hosting platform's log viewer. Now the job answers for itself.
|
|
275
|
+
const result = await (0, run_reported_1.runWatchCycleReported)({
|
|
276
|
+
routes: routeEconomics,
|
|
277
|
+
store,
|
|
278
|
+
universe: (0, state_1.buildPriceUniverse)(providers),
|
|
279
|
+
client,
|
|
280
|
+
thresholdPct: config.watch?.thresholdPct ?? DEFAULT_WATCH_THRESHOLD_PCT,
|
|
281
|
+
...(injectedEvents !== undefined ? { injectedEvents } : {}),
|
|
282
|
+
}, {
|
|
283
|
+
supabaseUrl: watchExport?.supabaseUrl ?? "",
|
|
284
|
+
serviceRoleKey: watchExport?.serviceRoleKey ?? "",
|
|
285
|
+
});
|
|
286
|
+
if (flags.bare.has("json")) {
|
|
287
|
+
console.log(JSON.stringify(result, null, 2));
|
|
288
|
+
}
|
|
289
|
+
else if (result.seeded) {
|
|
290
|
+
console.log("watch: seeded pricing state (first run watches silently — events fire from the next cycle)\n" +
|
|
291
|
+
` state → ${store.description}`);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
console.log(`watch: ${result.events} event(s) → ${result.drafts} draft(s) → ` +
|
|
295
|
+
`${result.inserted} new proposal(s) (duplicates deduped; run \`modelrig bakeoff ` +
|
|
296
|
+
"--from-proposal <id>` to attach evidence — nothing executes automatically)");
|
|
297
|
+
}
|
|
298
|
+
return 0;
|
|
299
|
+
}
|
|
300
|
+
function swapsClientFrom(config) {
|
|
301
|
+
const exp = (0, config_1.directExportOf)(config, "this command");
|
|
302
|
+
return (0, swaps_client_1.createSwapsClient)({
|
|
303
|
+
supabaseUrl: exp.supabaseUrl,
|
|
304
|
+
serviceRoleKey: exp.serviceRoleKey,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
async function cmdSwapExecute(flags) {
|
|
308
|
+
const proposalId = flags.positional[1];
|
|
309
|
+
if (proposalId === undefined) {
|
|
310
|
+
throw new Error("usage: modelrig swap execute <proposalId> --approve [--approved-by name]");
|
|
311
|
+
}
|
|
312
|
+
// THE gate (spec hard rule): no --approve, no execution — full stop,
|
|
313
|
+
// before any config or network access.
|
|
314
|
+
if (!flags.bare.has("approve")) {
|
|
315
|
+
throw new Error("swap execution requires explicit --approve — proposals never auto-execute. " +
|
|
316
|
+
`Re-run: modelrig swap execute ${proposalId} --approve`);
|
|
317
|
+
}
|
|
318
|
+
const config = (0, config_1.loadConfigFromEnv)();
|
|
319
|
+
const proposals = proposalsClientFrom(config);
|
|
320
|
+
const swaps = swapsClientFrom(config);
|
|
321
|
+
const routes = (0, loader_1.loadRoutes)(config.routesDir);
|
|
322
|
+
// The orchestration (guards, ordering, baseline snapshot) lives in the
|
|
323
|
+
// hermetically-tested executeSwap; the CLI only injects the real config
|
|
324
|
+
// deps and prints. The --approve gate is enforced here (fast-fail before
|
|
325
|
+
// config) AND inside executeSwap (defense in depth).
|
|
326
|
+
const result = await (0, swap_execute_1.executeSwap)({
|
|
327
|
+
proposalId,
|
|
328
|
+
approve: flags.bare.has("approve"),
|
|
329
|
+
approvedBy: flags.named.get("approved-by") ?? (0, node_os_1.userInfo)().username,
|
|
330
|
+
proposals,
|
|
331
|
+
swaps,
|
|
332
|
+
loadBundle: (route) => routes.get(route),
|
|
333
|
+
newId: () => (0, node_crypto_1.randomUUID)(),
|
|
334
|
+
now: () => new Date().toISOString(),
|
|
335
|
+
});
|
|
336
|
+
console.log(`swap ${result.swapId} recorded (approved by ${result.approvedBy}) — ` +
|
|
337
|
+
`${result.change.fromModel} → ${result.change.toModel}\n`);
|
|
338
|
+
console.log("APPLY THIS CHANGE (the executor never edits route files — git does):\n");
|
|
339
|
+
console.log(result.change.yaml);
|
|
340
|
+
console.log(`REVERT COMMAND (any time): modelrig swap revert ${result.swapId}\n` +
|
|
341
|
+
`The odometer starts measuring ${result.route} savings from the moment the change deploys.`);
|
|
342
|
+
return 0;
|
|
343
|
+
}
|
|
344
|
+
async function cmdSwapRevert(flags) {
|
|
345
|
+
const swapId = flags.positional[1];
|
|
346
|
+
if (swapId === undefined)
|
|
347
|
+
throw new Error("usage: modelrig swap revert <swapId>");
|
|
348
|
+
const config = (0, config_1.loadConfigFromEnv)();
|
|
349
|
+
const swaps = swapsClientFrom(config);
|
|
350
|
+
const swap = await swaps.get(swapId);
|
|
351
|
+
if (swap === null)
|
|
352
|
+
throw new Error(`no swap "${swapId}" on the control plane`);
|
|
353
|
+
if (swap.revertedAt !== null) {
|
|
354
|
+
console.log(`swap ${swapId} was already reverted at ${swap.revertedAt} — nothing to do`);
|
|
355
|
+
return 0;
|
|
356
|
+
}
|
|
357
|
+
const revertedAt = new Date().toISOString();
|
|
358
|
+
await swaps.markReverted(swapId, revertedAt);
|
|
359
|
+
const incumbents = swap.baseline.incumbent_candidates ?? [];
|
|
360
|
+
console.log(`swap ${swapId} marked reverted — the odometer stops accruing it at ${revertedAt}\n`);
|
|
361
|
+
console.log("APPLY THIS CHANGE (git is the actuator, as always):\n");
|
|
362
|
+
const routes = (0, loader_1.loadRoutes)(config.routesDir);
|
|
363
|
+
let version = null;
|
|
364
|
+
try {
|
|
365
|
+
version = routes.get(swap.route).version + 1;
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
// Route no longer loads locally — emit without the version line.
|
|
369
|
+
}
|
|
370
|
+
console.log([
|
|
371
|
+
`# revert swap ${swapId}: restore ${swap.route} to its pre-swap candidate order`,
|
|
372
|
+
`# apply to modelrig/routes/${swap.route}.yaml`,
|
|
373
|
+
...(version !== null ? [`version: ${version}`] : ["# bump the version field by 1"]),
|
|
374
|
+
"candidates:",
|
|
375
|
+
...incumbents.flatMap((c) => [` - provider: ${c.provider}`, ` model: ${c.model}`]),
|
|
376
|
+
"",
|
|
377
|
+
].join("\n"));
|
|
378
|
+
return 0;
|
|
379
|
+
}
|
|
380
|
+
async function cmdSwap(flags) {
|
|
381
|
+
const sub = flags.positional[0];
|
|
382
|
+
if (sub === "execute")
|
|
383
|
+
return cmdSwapExecute(flags);
|
|
384
|
+
if (sub === "revert")
|
|
385
|
+
return cmdSwapRevert(flags);
|
|
386
|
+
throw new Error("usage: modelrig swap execute <proposalId> --approve [--approved-by name] | modelrig swap revert <swapId>");
|
|
387
|
+
}
|
|
388
|
+
async function cmdSamples(flags) {
|
|
389
|
+
const sub = flags.positional[0];
|
|
390
|
+
if (sub !== "purge") {
|
|
391
|
+
throw new Error("usage: modelrig samples purge [--bakeoff <id>]");
|
|
392
|
+
}
|
|
393
|
+
const config = (0, config_1.loadConfigFromEnv)();
|
|
394
|
+
const samplesExport = (0, config_1.directExportOf)(config, "samples purge");
|
|
395
|
+
const client = (0, samples_1.createBakeoffSamplesClient)({
|
|
396
|
+
supabaseUrl: samplesExport.supabaseUrl,
|
|
397
|
+
serviceRoleKey: samplesExport.serviceRoleKey,
|
|
398
|
+
});
|
|
399
|
+
const bakeoffId = flags.named.get("bakeoff");
|
|
400
|
+
const deleted = await client.purge(bakeoffId !== undefined ? { bakeoffId } : { nowIso: new Date().toISOString() });
|
|
401
|
+
console.log(bakeoffId !== undefined
|
|
402
|
+
? `purged ${deleted} review sample(s) for bakeoff ${bakeoffId}`
|
|
403
|
+
: `purged ${deleted} expired review sample(s)`);
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* `modelrig verify-swap` — the autonomy-ladder Tier-2 verify step (Expansion
|
|
408
|
+
* §9). Replays a LOCAL capture fixture through the drafted SAME-MODEL route,
|
|
409
|
+
* diffs each output against the original, and emits the report the Tier-2 PR
|
|
410
|
+
* embeds. A plumbing proof; refuses uncapturable (streaming/dynamic) sites. */
|
|
411
|
+
async function cmdVerifySwap(flags) {
|
|
412
|
+
const route = flags.named.get("route");
|
|
413
|
+
const fixturePath = flags.named.get("captures");
|
|
414
|
+
if (route === undefined || fixturePath === undefined) {
|
|
415
|
+
throw new Error("verify-swap requires --route <route> and --captures <fixture.json> " +
|
|
416
|
+
"(the local capture of the original call's inputs/outputs)");
|
|
417
|
+
}
|
|
418
|
+
const envRaw = flags.named.get("budget-usd") ?? flags.named.get("envelope");
|
|
419
|
+
const envelopeUsd = envRaw !== undefined ? Number(envRaw) : undefined;
|
|
420
|
+
if (envelopeUsd !== undefined && (!Number.isFinite(envelopeUsd) || envelopeUsd <= 0)) {
|
|
421
|
+
throw new Error("--budget-usd must be a positive number of dollars (the replay-pass envelope)");
|
|
422
|
+
}
|
|
423
|
+
const config = (0, config_1.loadConfigFromEnv)();
|
|
424
|
+
return (0, cli_run_1.runVerifySwapCli)(config, {
|
|
425
|
+
route,
|
|
426
|
+
fixturePath,
|
|
427
|
+
...(envelopeUsd !== undefined ? { envelopeUsd } : {}),
|
|
428
|
+
...(flags.bare.has("json") ? { asJson: true } : {}),
|
|
429
|
+
...(flags.named.get("out") !== undefined ? { outPath: flags.named.get("out") } : {}),
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* `modelrig serve` — Phase 5 WS4's zero-config path. The HTTP surface lives
|
|
434
|
+
* in the separate `modelrig-server` package (it has its own auth and budget
|
|
435
|
+
* concerns); this is the one command name people will reach for, so it
|
|
436
|
+
* hands off rather than making them learn a second package name.
|
|
437
|
+
*
|
|
438
|
+
* The dependency stays one-way: the core package does not depend on the
|
|
439
|
+
* server, it just calls it when present and teaches when it isn't.
|
|
440
|
+
*/
|
|
441
|
+
async function cmdServe() {
|
|
442
|
+
let serverMain = null;
|
|
443
|
+
try {
|
|
444
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
445
|
+
const mod = require("modelrig-server");
|
|
446
|
+
serverMain = mod.main ?? null;
|
|
447
|
+
}
|
|
448
|
+
catch {
|
|
449
|
+
serverMain = null;
|
|
450
|
+
}
|
|
451
|
+
if (serverMain === null) {
|
|
452
|
+
console.error([
|
|
453
|
+
"`modelrig serve` needs the server package, which isn't installed here.",
|
|
454
|
+
"",
|
|
455
|
+
" pnpm add modelrig-server (or npm i / yarn add)",
|
|
456
|
+
"",
|
|
457
|
+
"It's a separate package because serving over HTTP brings its own concerns —",
|
|
458
|
+
"a bearer token and a daily spend cap — that a library shouldn't assume for you.",
|
|
459
|
+
"You do NOT need it to use ModelRig from your own code: `rig.run(...)` works",
|
|
460
|
+
"without any server at all. Docs: https://modelrig.dev/quickstart.html",
|
|
461
|
+
].join("\n"));
|
|
462
|
+
return 1;
|
|
463
|
+
}
|
|
464
|
+
serverMain();
|
|
465
|
+
// The server owns the process from here (it holds the listener open).
|
|
466
|
+
return 0;
|
|
467
|
+
}
|
|
468
|
+
/** `modelrig status` — print the runtime posture (§2): export mode, artifact
|
|
469
|
+
* namespace on/off + WHY, content custody, OTLP. Reads config only (no rig, no
|
|
470
|
+
* routes dir), so it works anywhere the env is set. */
|
|
471
|
+
function cmdStatus() {
|
|
472
|
+
console.log((0, status_1.formatStatusReport)((0, status_1.buildRigStatus)((0, config_1.loadConfigFromEnv)())));
|
|
473
|
+
return 0;
|
|
474
|
+
}
|
|
475
|
+
/** `modelrig judge <run>` — E5 V.4: explicitly ask the bounded model-judge to
|
|
476
|
+
* score a run. Goes through the API boundary (T12: the CLI never holds a DB
|
|
477
|
+
* client) — POST /v1/runs/:id/judge on the configured server, authed with a
|
|
478
|
+
* rig_sk key. The daily cap + metering are the SERVER's; this just triggers one. */
|
|
479
|
+
async function cmdJudge(flags) {
|
|
480
|
+
const runId = flags.positional[0] ?? "";
|
|
481
|
+
// Config is the only env reader (package-invariants rule). The judge endpoint
|
|
482
|
+
// lives on the same server as the api-mode ingest sink, so the base is that
|
|
483
|
+
// URL's origin and the credential is the api key.
|
|
484
|
+
const exp = (0, config_1.loadConfigFromEnv)().export;
|
|
485
|
+
const api = exp !== undefined && exp.mode === "api" ? exp : undefined;
|
|
486
|
+
const base = api !== undefined ? new URL(api.ingestUrl).origin : "";
|
|
487
|
+
const key = api?.apiKey ?? "";
|
|
488
|
+
if (runId === "" || base === "" || key === "") {
|
|
489
|
+
console.error([
|
|
490
|
+
"usage: modelrig judge <runId>",
|
|
491
|
+
"",
|
|
492
|
+
"Needs api-mode telemetry configured (the judge is on the same server):",
|
|
493
|
+
" MODELRIG_API_KEY a rig_sk_ key with the `oracle-act` scope",
|
|
494
|
+
" MODELRIG_INGEST_URL optional — the server base; defaults to the hosted API",
|
|
495
|
+
"",
|
|
496
|
+
"The judge is EXPLICIT-only and metered; scoring rides the gateway (a route),",
|
|
497
|
+
"so the org's daily cap and spend-stops apply. Nothing schedules it.",
|
|
498
|
+
].join("\n"));
|
|
499
|
+
return 1;
|
|
500
|
+
}
|
|
501
|
+
const res = await fetch(`${base}/v1/runs/${encodeURIComponent(runId)}/judge`, {
|
|
502
|
+
method: "POST",
|
|
503
|
+
headers: { authorization: `Bearer ${key}`, "content-type": "application/json" },
|
|
504
|
+
body: "{}",
|
|
505
|
+
});
|
|
506
|
+
const body = (await res.json().catch(() => ({})));
|
|
507
|
+
if (!res.ok) {
|
|
508
|
+
console.error(`judge ${res.status}: ${typeof body.error === "string" ? body.error : "failed"}`);
|
|
509
|
+
return 1;
|
|
510
|
+
}
|
|
511
|
+
console.log(JSON.stringify(body, null, 2));
|
|
512
|
+
return 0;
|
|
513
|
+
}
|
|
514
|
+
/** The one usage block, printed to stdout on `--help` (exit 0) and to stderr on
|
|
515
|
+
* an unknown command (exit 1). Kept as a const so the two paths can never
|
|
516
|
+
* drift. */
|
|
517
|
+
const USAGE = [
|
|
518
|
+
"usage:",
|
|
519
|
+
" modelrig init [path] [--scope glob,glob] [--out dir] [--dry-run] [--include-tests] [--json]",
|
|
520
|
+
" modelrig observe [path] [--scope glob,glob] [--out dir] [--dry-run] [--json] (T0/T1: see your runs first — no migration)",
|
|
521
|
+
" modelrig validate (structurally check rig.yaml + every route bundle — no provider key needed)",
|
|
522
|
+
" modelrig status (print export/artifact/otlp posture)",
|
|
523
|
+
" modelrig serve (needs the modelrig-server package)",
|
|
524
|
+
" modelrig bakeoff --route <route> --variants a,b,c --replay-last N [--budget-usd usd] [--keep-outputs[=all]] [--json] [--no-mirror]",
|
|
525
|
+
" modelrig bakeoff --from-proposal <id> [--replay-last N] [--keep-outputs[=all]] [--json]",
|
|
526
|
+
" modelrig watch run [--events fixtures.json] [--json]",
|
|
527
|
+
" modelrig verify-swap --route <route> --captures <fixture.json> [--budget-usd usd] [--out report.md] [--json]",
|
|
528
|
+
" modelrig swap execute <proposalId> --approve [--approved-by name]",
|
|
529
|
+
" modelrig swap revert <swapId>",
|
|
530
|
+
" modelrig judge <runId> (E5: score a run with the bounded model-judge; explicit + metered)",
|
|
531
|
+
" modelrig samples purge [--bakeoff <id>]",
|
|
532
|
+
].join("\n");
|
|
533
|
+
async function main(argv) {
|
|
534
|
+
const [command, ...rest] = argv;
|
|
535
|
+
// `--help`/`-h`/`help` is a success: print usage to stdout and exit 0. (An
|
|
536
|
+
// unknown command still prints usage to stderr and exits 1, below.) The
|
|
537
|
+
// clean-room tarball test asserts the shipped bin exits 0 on --help.
|
|
538
|
+
if (command === "--help" || command === "-h" || command === "help") {
|
|
539
|
+
console.log(USAGE);
|
|
540
|
+
return 0;
|
|
541
|
+
}
|
|
542
|
+
const flags = parseArgs(rest);
|
|
543
|
+
switch (command) {
|
|
544
|
+
case "status":
|
|
545
|
+
return cmdStatus();
|
|
546
|
+
case "validate":
|
|
547
|
+
return (0, validate_cli_1.runValidateCli)();
|
|
548
|
+
case "bakeoff":
|
|
549
|
+
return cmdBakeoff(flags);
|
|
550
|
+
case "watch":
|
|
551
|
+
return cmdWatch(flags);
|
|
552
|
+
case "swap":
|
|
553
|
+
return cmdSwap(flags);
|
|
554
|
+
case "samples":
|
|
555
|
+
return cmdSamples(flags);
|
|
556
|
+
case "init":
|
|
557
|
+
return (0, init_cli_1.cmdInit)(flags);
|
|
558
|
+
case "observe":
|
|
559
|
+
return (0, observe_cli_1.cmdObserve)(flags);
|
|
560
|
+
case "verify-swap":
|
|
561
|
+
return cmdVerifySwap(flags);
|
|
562
|
+
case "judge":
|
|
563
|
+
return cmdJudge(flags);
|
|
564
|
+
case "serve":
|
|
565
|
+
return cmdServe();
|
|
566
|
+
default:
|
|
567
|
+
console.error(USAGE);
|
|
568
|
+
return 1;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (require.main === module) {
|
|
572
|
+
main(process.argv.slice(2))
|
|
573
|
+
.then((code) => {
|
|
574
|
+
process.exitCode = code;
|
|
575
|
+
})
|
|
576
|
+
.catch((err) => {
|
|
577
|
+
// Errors that teach (WS3): a typed failure prints what happened, what
|
|
578
|
+
// to do, and where to read — from the one table, not from here.
|
|
579
|
+
if (err instanceof failures_1.RigFailureError) {
|
|
580
|
+
console.error((0, failures_teaching_1.renderFailureForTerminal)(err.failure));
|
|
581
|
+
}
|
|
582
|
+
else if ((0, failures_1.isRigFailure)(err)) {
|
|
583
|
+
console.error((0, failures_teaching_1.renderFailureForTerminal)(err));
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
587
|
+
}
|
|
588
|
+
process.exitCode = 1;
|
|
589
|
+
});
|
|
590
|
+
}
|