modelrig 0.1.0 → 0.3.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 +158 -0
- package/README.md +53 -11
- 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 +50 -6
- package/dist/adapters/anthropic.d.ts.map +1 -1
- package/dist/adapters/anthropic.js +123 -29
- 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 +6 -0
- package/dist/adapters/deepseek.d.ts.map +1 -1
- package/dist/adapters/deepseek.js +54 -1
- package/dist/adapters/gemini.d.ts +10 -0
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +78 -7
- package/dist/adapters/grok.d.ts +9 -0
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +67 -3
- package/dist/adapters/openai-compat.d.ts +6 -0
- package/dist/adapters/openai-compat.d.ts.map +1 -1
- package/dist/adapters/openai-compat.js +58 -1
- 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 +6 -0
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +60 -1
- 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/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/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 +15 -0
- package/dist/bakeoff/samples.d.ts.map +1 -1
- package/dist/bakeoff/samples.js +20 -0
- package/dist/cli.d.ts +1 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +134 -116
- package/dist/config.d.ts +100 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +104 -0
- package/dist/deprecations.d.ts +32 -0
- package/dist/deprecations.d.ts.map +1 -0
- package/dist/deprecations.js +59 -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 +106 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +266 -138
- package/dist/init/finder.d.ts +15 -0
- package/dist/init/finder.d.ts.map +1 -1
- package/dist/init/finder.js +46 -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/registry/build.d.ts +94 -1
- package/dist/registry/build.d.ts.map +1 -1
- package/dist/registry/build.js +125 -4
- 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.map +1 -1
- package/dist/registry/call-notes.js +40 -2
- 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.map +1 -1
- package/dist/registry/facts.js +16 -0
- package/dist/registry/parity.d.ts +26 -0
- package/dist/registry/parity.d.ts.map +1 -1
- package/dist/registry/parity.js +43 -2
- 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 +25 -1
- package/dist/registry/pricing.d.ts.map +1 -1
- package/dist/registry/pricing.js +64 -6
- 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/loader.d.ts +48 -0
- package/dist/routes/loader.d.ts.map +1 -1
- package/dist/routes/loader.js +167 -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/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.map +1 -1
- package/dist/run/cost.js +1 -1
- 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/raw.d.ts +130 -0
- package/dist/run/raw.d.ts.map +1 -0
- package/dist/run/raw.js +363 -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/resolve.d.ts +75 -2
- package/dist/run/resolve.d.ts.map +1 -1
- package/dist/run/resolve.js +126 -4
- package/dist/run/run.d.ts +14 -0
- package/dist/run/run.d.ts.map +1 -1
- package/dist/run/run.js +0 -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.map +1 -1
- package/dist/run/validate.js +6 -7
- 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 +13 -0
- package/dist/savings/emit.d.ts.map +1 -1
- package/dist/savings/emit.js +25 -0
- package/dist/savings/swap-execute.d.ts +14 -0
- package/dist/savings/swap-execute.d.ts.map +1 -1
- package/dist/savings/swap-execute.js +44 -1
- package/dist/savings/swaps-client.d.ts +14 -0
- package/dist/savings/swaps-client.d.ts.map +1 -1
- package/dist/savings/swaps-client.js +16 -2
- 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 +3 -0
- package/dist/telemetry/capture.d.ts.map +1 -1
- package/dist/telemetry/capture.js +15 -8
- 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 +75 -0
- package/dist/telemetry/exporter.d.ts.map +1 -1
- package/dist/telemetry/exporter.js +324 -47
- 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 +152 -3
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
- package/dist/telemetry/ingest-protocol.js +204 -4
- 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 +37 -1
- package/dist/telemetry/sinks.d.ts.map +1 -1
- package/dist/telemetry/sinks.js +121 -30
- package/dist/telemetry/store.d.ts +28 -1
- package/dist/telemetry/store.d.ts.map +1 -1
- package/dist/telemetry/store.js +218 -3
- package/dist/tenancy/api-key-format.d.ts +15 -5
- package/dist/tenancy/api-key-format.d.ts.map +1 -1
- package/dist/tenancy/api-key-format.js +24 -5
- 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/provider.d.ts +147 -1
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/provider.js +21 -1
- package/dist/types/route.d.ts +79 -1
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/telemetry.d.ts +117 -0
- package/dist/types/telemetry.d.ts.map +1 -1
- 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/evaluate.d.ts +162 -3
- package/dist/watch/evaluate.d.ts.map +1 -1
- package/dist/watch/evaluate.js +61 -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 +20 -4
- package/dist/watch/proposals.d.ts.map +1 -1
- package/dist/watch/proposals.js +31 -3
- package/dist/watch/run.d.ts +33 -0
- package/dist/watch/run.d.ts.map +1 -1
- package/dist/watch/run.js +39 -0
- package/package.json +7 -1
- package/registry/LICENSE +20 -0
- package/registry/leaderboard.json +3639 -0
- package/registry/registry.json +7263 -0
- package/dist/telemetry/supabase-exporter.d.ts +0 -64
- package/dist/telemetry/supabase-exporter.d.ts.map +0 -1
- package/dist/telemetry/supabase-exporter.js +0 -211
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bake-off terminal reporting — the matrix printer (moved here from cli.ts for
|
|
3
|
+
* the LOC cap, P1) and the `--from-eval-cases` runner (E4 chunk A) with its
|
|
4
|
+
* HONEST-REFUSAL report: every eval case that could not be rehydrated is NAMED
|
|
5
|
+
* with a reason before any bake-off runs — never a silent subset (spec §3.3).
|
|
6
|
+
*/
|
|
7
|
+
import type { RigConfig } from "../config";
|
|
8
|
+
import type { CacheSimAssumptions } from "./cache-sim";
|
|
9
|
+
import type { KeepOutputsMode } from "./samples";
|
|
10
|
+
import type { BakeoffArtifact } from "./types";
|
|
11
|
+
/**
|
|
12
|
+
* One variant's measured-cold vs cache-adjusted input cost (cache-aware-routing
|
|
13
|
+
* spec §5.4). Built by the caller from `simulateCache` + the candidate's rates;
|
|
14
|
+
* this module only RENDERS it, so the report stays pure of pricing lookups.
|
|
15
|
+
*/
|
|
16
|
+
export interface CacheAdjustedRow {
|
|
17
|
+
readonly variant: string;
|
|
18
|
+
/** Input cost the way replay actually paid it — every token at list (§5.4). */
|
|
19
|
+
readonly measuredColdUsd: number;
|
|
20
|
+
/** Input cost at production cadence — reads/writes priced per policy (§5.4). */
|
|
21
|
+
readonly cacheAdjustedUsd: number;
|
|
22
|
+
readonly assumptions: CacheSimAssumptions;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Render the cache-adjusted section (spec §5.4) as lines — measured-cold AND
|
|
26
|
+
* cache-adjusted input cost side by side, with the simulator's assumptions
|
|
27
|
+
* PRINTED so the numbers are never mistaken for measured savings. Pure (returns
|
|
28
|
+
* strings; `printMatrix` does the console I/O) so it is unit-tested directly.
|
|
29
|
+
* Empty input ⇒ no lines (the section is omitted entirely — back-compat).
|
|
30
|
+
*/
|
|
31
|
+
export declare function formatCacheAdjustedSection(rows: readonly CacheAdjustedRow[]): string[];
|
|
32
|
+
export declare function printMatrix(artifact: BakeoffArtifact, cacheAdjusted?: readonly CacheAdjustedRow[]): void;
|
|
33
|
+
/**
|
|
34
|
+
* `modelrig bakeoff --from-eval-cases <task> --variants a,b,c` — replay a task's
|
|
35
|
+
* promoted eval cases through each variant. Prints the honest-refusal report,
|
|
36
|
+
* then (if anything rehydrated) runs the bake-off and persists/mirrors/prints it.
|
|
37
|
+
* Returns the process exit code: 0 on a run (or an empty suite), 1 when cases
|
|
38
|
+
* existed but NONE could be rehydrated (an actionable non-zero, not a crash).
|
|
39
|
+
*/
|
|
40
|
+
export declare function runEvalCaseBakeoffCli(config: RigConfig, task: string, variants: readonly string[], envelope: number | undefined, keep: KeepOutputsMode | undefined, asJson: boolean): Promise<number>;
|
|
41
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/bakeoff/report.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAM/D;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,gFAAgF;IAChF,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAC;CAC3C;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,EAAE,CAgCtF;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,eAAe,EAAE,aAAa,GAAE,SAAS,gBAAgB,EAAO,GAAG,IAAI,CAgC5G;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,eAAe,GAAG,SAAS,EACjC,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,MAAM,CAAC,CA8CjB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Bake-off terminal reporting — the matrix printer (moved here from cli.ts for
|
|
4
|
+
* the LOC cap, P1) and the `--from-eval-cases` runner (E4 chunk A) with its
|
|
5
|
+
* HONEST-REFUSAL report: every eval case that could not be rehydrated is NAMED
|
|
6
|
+
* with a reason before any bake-off runs — never a silent subset (spec §3.3).
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.formatCacheAdjustedSection = formatCacheAdjustedSection;
|
|
10
|
+
exports.printMatrix = printMatrix;
|
|
11
|
+
exports.runEvalCaseBakeoffCli = runEvalCaseBakeoffCli;
|
|
12
|
+
const node_fs_1 = require("node:fs");
|
|
13
|
+
const node_os_1 = require("node:os");
|
|
14
|
+
const node_path_1 = require("node:path");
|
|
15
|
+
const index_1 = require("../index");
|
|
16
|
+
const engine_1 = require("./engine");
|
|
17
|
+
function fmtEff(usd) {
|
|
18
|
+
return Number.isFinite(usd) ? `$${usd.toFixed(4)}` : "∞ (0 conformant)";
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Render the cache-adjusted section (spec §5.4) as lines — measured-cold AND
|
|
22
|
+
* cache-adjusted input cost side by side, with the simulator's assumptions
|
|
23
|
+
* PRINTED so the numbers are never mistaken for measured savings. Pure (returns
|
|
24
|
+
* strings; `printMatrix` does the console I/O) so it is unit-tested directly.
|
|
25
|
+
* Empty input ⇒ no lines (the section is omitted entirely — back-compat).
|
|
26
|
+
*/
|
|
27
|
+
function formatCacheAdjustedSection(rows) {
|
|
28
|
+
if (rows.length === 0)
|
|
29
|
+
return [];
|
|
30
|
+
const lines = [
|
|
31
|
+
"cache-adjusted input cost (SIMULATED — not measured savings; the odometer stays measured-only):",
|
|
32
|
+
];
|
|
33
|
+
const header = ["variant", "measured-cold", "cache-adjusted", "sim-savings"];
|
|
34
|
+
const body = rows.map((r) => {
|
|
35
|
+
const savings = r.measuredColdUsd > 0
|
|
36
|
+
? `${(((r.measuredColdUsd - r.cacheAdjustedUsd) / r.measuredColdUsd) * 100).toFixed(1)}%`
|
|
37
|
+
: "n/a";
|
|
38
|
+
return [r.variant, `$${r.measuredColdUsd.toFixed(4)}`, `$${r.cacheAdjustedUsd.toFixed(4)}`, savings];
|
|
39
|
+
});
|
|
40
|
+
const widths = header.map((h, c) => Math.max(h.length, ...body.map((row) => row[c].length)));
|
|
41
|
+
const line = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ");
|
|
42
|
+
lines.push(line(header));
|
|
43
|
+
for (const row of body)
|
|
44
|
+
lines.push(line(row));
|
|
45
|
+
// Assumptions basis — one line per distinct policy so a mixed-provider matrix
|
|
46
|
+
// is honest about which TTL/min-cacheable each arm assumed.
|
|
47
|
+
const seen = new Set();
|
|
48
|
+
for (const r of rows) {
|
|
49
|
+
const a = r.assumptions;
|
|
50
|
+
const key = `${a.provider}/${a.model}`;
|
|
51
|
+
if (seen.has(key))
|
|
52
|
+
continue;
|
|
53
|
+
seen.add(key);
|
|
54
|
+
lines.push(` assumptions ${key}: ${a.cacheSupported ? "" : "cache unsupported; "}ttl=${a.ttlSeconds}s` +
|
|
55
|
+
`${a.ttlSourced ? "" : " (fallback — undocumented)"}, min-cacheable=${a.minCacheableTokens}tok, ` +
|
|
56
|
+
`refresh-on-read=${a.refreshOnRead}`);
|
|
57
|
+
}
|
|
58
|
+
return lines;
|
|
59
|
+
}
|
|
60
|
+
function printMatrix(artifact, cacheAdjusted = []) {
|
|
61
|
+
console.log(`bakeoff ${artifact.id}\nroute ${artifact.route}@${artifact.routeVersion} · replayed ${artifact.replayN} captured inputs · ${artifact.ranAt}`);
|
|
62
|
+
const header = ["variant", "n", "conform", "ci95", "repair", "repair$", "lat(ms)", "eff-$/1K-conf"];
|
|
63
|
+
const rows = artifact.variants.map((m) => [
|
|
64
|
+
m.variant,
|
|
65
|
+
String(m.samples),
|
|
66
|
+
`${(m.conformRate * 100).toFixed(0)}%`,
|
|
67
|
+
`[${(m.conformCi95[0] * 100).toFixed(0)},${(m.conformCi95[1] * 100).toFixed(0)}]`,
|
|
68
|
+
`${(m.repairRate * 100).toFixed(0)}%`,
|
|
69
|
+
`$${m.meanRepairCostUsd.toFixed(4)}`,
|
|
70
|
+
m.meanLatencyMs.toFixed(0),
|
|
71
|
+
m.pricingMissing ? "UNPRICED (no ranking)" : fmtEff(m.effectiveUsdPer1kConformant),
|
|
72
|
+
]);
|
|
73
|
+
const widths = header.map((h, c) => Math.max(h.length, ...rows.map((r) => r[c].length)));
|
|
74
|
+
const line = (cells) => cells.map((cell, c) => cell.padEnd(widths[c])).join(" ");
|
|
75
|
+
console.log(line(header));
|
|
76
|
+
for (const row of rows)
|
|
77
|
+
console.log(line(row));
|
|
78
|
+
if (artifact.proposal !== null) {
|
|
79
|
+
console.log(`PROPOSAL: variant "${artifact.proposal.variant}" beats the incumbent by ` +
|
|
80
|
+
`${artifact.proposal.vsIncumbentSavingsPct.toFixed(1)}% effective cost — human decision required; ` +
|
|
81
|
+
"nothing is executed.");
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
console.log("proposal: none (no variant beats the incumbent's gates)");
|
|
85
|
+
}
|
|
86
|
+
// Cache-adjusted section (spec §5.4) — printed only when the caller supplied
|
|
87
|
+
// simulator rows; absent ⇒ the matrix output is byte-identical to pre-WS-B.
|
|
88
|
+
for (const l of formatCacheAdjustedSection(cacheAdjusted))
|
|
89
|
+
console.log(l);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* `modelrig bakeoff --from-eval-cases <task> --variants a,b,c` — replay a task's
|
|
93
|
+
* promoted eval cases through each variant. Prints the honest-refusal report,
|
|
94
|
+
* then (if anything rehydrated) runs the bake-off and persists/mirrors/prints it.
|
|
95
|
+
* Returns the process exit code: 0 on a run (or an empty suite), 1 when cases
|
|
96
|
+
* existed but NONE could be rehydrated (an actionable non-zero, not a crash).
|
|
97
|
+
*/
|
|
98
|
+
async function runEvalCaseBakeoffCli(config, task, variants, envelope, keep, asJson) {
|
|
99
|
+
const mirrorTarget = config.export?.mode === "direct" ? config.export : undefined;
|
|
100
|
+
const rig = (0, index_1.createRig)(config);
|
|
101
|
+
try {
|
|
102
|
+
const { selection, artifact } = await rig.bakeoffFromEvalCases(task, {
|
|
103
|
+
variants,
|
|
104
|
+
...(envelope !== undefined ? { envelopeUsdPerVariant: envelope } : {}),
|
|
105
|
+
...(keep !== undefined ? { keepOutputs: keep } : {}),
|
|
106
|
+
});
|
|
107
|
+
console.log(`eval cases for "${task}": ${selection.total} total · ` +
|
|
108
|
+
`${selection.rehydratable.length} rehydratable · ${selection.refusals.length} refused`);
|
|
109
|
+
// NEVER a silent subset: name every refused case with its reason.
|
|
110
|
+
for (const r of selection.refusals) {
|
|
111
|
+
console.log(` refused ${r.caseId} (source artifact ${r.sourceArtifactId}): ${r.reason}`);
|
|
112
|
+
}
|
|
113
|
+
if (artifact === null) {
|
|
114
|
+
console.log(selection.total === 0
|
|
115
|
+
? `no eval cases promoted for "${task}" yet — promote a failure first (artifact.promoteToEval).`
|
|
116
|
+
: "nothing rehydratable — no bake-off run (see the refusals above).");
|
|
117
|
+
return selection.total === 0 ? 0 : 1;
|
|
118
|
+
}
|
|
119
|
+
const outDir = (0, node_path_1.join)((0, node_path_1.dirname)(config.telemetryDbPath), "bakeoffs");
|
|
120
|
+
(0, node_fs_1.mkdirSync)(outDir, { recursive: true });
|
|
121
|
+
const artifactPath = (0, node_path_1.join)(outDir, `${artifact.id}.json`);
|
|
122
|
+
(0, node_fs_1.writeFileSync)(artifactPath, `${JSON.stringify(artifact, null, 2)}\n`);
|
|
123
|
+
let mirrored = false;
|
|
124
|
+
if (mirrorTarget !== undefined) {
|
|
125
|
+
await (0, engine_1.mirrorBakeoff)(artifact, {
|
|
126
|
+
supabaseUrl: mirrorTarget.supabaseUrl,
|
|
127
|
+
serviceRoleKey: mirrorTarget.serviceRoleKey,
|
|
128
|
+
sourceId: mirrorTarget.sourceId ?? (0, node_os_1.hostname)(),
|
|
129
|
+
});
|
|
130
|
+
mirrored = true;
|
|
131
|
+
}
|
|
132
|
+
if (asJson)
|
|
133
|
+
console.log(JSON.stringify(artifact, null, 2));
|
|
134
|
+
else
|
|
135
|
+
printMatrix(artifact);
|
|
136
|
+
console.log(`artifact → ${artifactPath}${mirrored ? " · mirrored to bakeoffs" : ""}`);
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
await rig.close();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Transport is raw PostgREST over fetch (same pattern as the exporter).
|
|
11
11
|
*/
|
|
12
|
+
import type { BakeoffSamplesSink } from "./engine";
|
|
12
13
|
/** 256KB per output, per the architect's caps; TOAST compresses at rest. */
|
|
13
14
|
export declare const OUTPUT_MAX_BYTES: number;
|
|
14
15
|
export type KeepOutputsMode = "winner" | "all";
|
|
@@ -59,4 +60,18 @@ export interface BakeoffSamplesClient {
|
|
|
59
60
|
}): Promise<number>;
|
|
60
61
|
}
|
|
61
62
|
export declare function createBakeoffSamplesClient(config: BakeoffSamplesClientConfig): BakeoffSamplesClient;
|
|
63
|
+
/**
|
|
64
|
+
* Build the direct-mode bake-off review-sample sink (extracted from index.ts for
|
|
65
|
+
* the LOC cap, P1). Persists retained outputs to the SEPARATE `bakeoff_samples`
|
|
66
|
+
* store (service role; deny-anon RLS); captures never touch it.
|
|
67
|
+
*
|
|
68
|
+
* DIRECT MODE ONLY, and that is a real limitation, not an oversight:
|
|
69
|
+
* `bakeoff_samples` has no ingest route (telemetry-ingestion-boundary.md §2
|
|
70
|
+
* covers telemetry, not review samples), so an api-mode rig keeps its retained
|
|
71
|
+
* outputs local. Callers pass `undefined` for a non-direct rig.
|
|
72
|
+
*/
|
|
73
|
+
export declare function buildDirectSamplesSink(cfg: {
|
|
74
|
+
readonly supabaseUrl: string;
|
|
75
|
+
readonly serviceRoleKey: string;
|
|
76
|
+
}): BakeoffSamplesSink;
|
|
62
77
|
//# sourceMappingURL=samples.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"samples.d.ts","sourceRoot":"","sources":["../../src/bakeoff/samples.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,QAAa,CAAC;AAE3C,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;QAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,CAAC,CAAC;CACJ;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,eAAe,EACrB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,MAAM,EAAE,CAKV;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAOjE;AAED;+DAC+D;AAC/D,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,MAAM,CAEhE;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,SAAS,EAAE,MAAM,GAChB,aAAa,EAAE,CAoBjB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,2EAA2E;IAC3E,KAAK,CAAC,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACxE;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,0BAA0B,GAAG,oBAAoB,CAoEnG"}
|
|
1
|
+
{"version":3,"file":"samples.d.ts","sourceRoot":"","sources":["../../src/bakeoff/samples.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,QAAa,CAAC;AAE3C,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;QAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,CAAC,CAAC;CACJ;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,eAAe,EACrB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,MAAM,EAAE,CAKV;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAOjE;AAED;+DAC+D;AAC/D,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,MAAM,CAEhE;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,SAAS,EAAE,MAAM,GAChB,aAAa,EAAE,CAoBjB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,2EAA2E;IAC3E,KAAK,CAAC,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACxE;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,0BAA0B,GAAG,oBAAoB,CAoEnG;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE;IAC1C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC,GAAG,kBAAkB,CAQrB"}
|
package/dist/bakeoff/samples.js
CHANGED
|
@@ -17,6 +17,7 @@ exports.truncateOutput = truncateOutput;
|
|
|
17
17
|
exports.retentionExpiry = retentionExpiry;
|
|
18
18
|
exports.buildBakeoffSamples = buildBakeoffSamples;
|
|
19
19
|
exports.createBakeoffSamplesClient = createBakeoffSamplesClient;
|
|
20
|
+
exports.buildDirectSamplesSink = buildDirectSamplesSink;
|
|
20
21
|
/** 256KB per output, per the architect's caps; TOAST compresses at rest. */
|
|
21
22
|
exports.OUTPUT_MAX_BYTES = 256 * 1024;
|
|
22
23
|
/** Default persists incumbent + winner only; "all" persists every arm. */
|
|
@@ -126,3 +127,22 @@ function createBakeoffSamplesClient(config) {
|
|
|
126
127
|
},
|
|
127
128
|
};
|
|
128
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Build the direct-mode bake-off review-sample sink (extracted from index.ts for
|
|
132
|
+
* the LOC cap, P1). Persists retained outputs to the SEPARATE `bakeoff_samples`
|
|
133
|
+
* store (service role; deny-anon RLS); captures never touch it.
|
|
134
|
+
*
|
|
135
|
+
* DIRECT MODE ONLY, and that is a real limitation, not an oversight:
|
|
136
|
+
* `bakeoff_samples` has no ingest route (telemetry-ingestion-boundary.md §2
|
|
137
|
+
* covers telemetry, not review samples), so an api-mode rig keeps its retained
|
|
138
|
+
* outputs local. Callers pass `undefined` for a non-direct rig.
|
|
139
|
+
*/
|
|
140
|
+
function buildDirectSamplesSink(cfg) {
|
|
141
|
+
return async ({ bakeoffId, route, arms }) => {
|
|
142
|
+
const rows = buildBakeoffSamples(bakeoffId, route, arms, retentionExpiry(Date.now()));
|
|
143
|
+
await createBakeoffSamplesClient({
|
|
144
|
+
supabaseUrl: cfg.supabaseUrl,
|
|
145
|
+
serviceRoleKey: cfg.serviceRoleKey,
|
|
146
|
+
}).insert(rows);
|
|
147
|
+
};
|
|
148
|
+
}
|
package/dist/cli.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* modelrig CLI — Phase 3 plan §3 + Phase 4 plan §3:
|
|
4
4
|
* modelrig bakeoff --route X --variants a,b,c --replay-last N
|
|
5
|
-
* [--
|
|
5
|
+
* [--budget-usd usd] [--json] [--no-mirror] (--envelope is a kept alias)
|
|
6
6
|
* modelrig bakeoff --from-proposal <id> [--replay-last N] [--json]
|
|
7
7
|
* modelrig watch run [--events fixtures.json] [--json]
|
|
8
8
|
*
|
|
@@ -15,13 +15,11 @@
|
|
|
15
15
|
* executes a swap — that is `modelrig swap execute --approve`, a separate,
|
|
16
16
|
* human command.
|
|
17
17
|
*/
|
|
18
|
-
import type { BakeoffArtifact } from "./bakeoff/types";
|
|
19
18
|
export interface Flags {
|
|
20
19
|
readonly positional: readonly string[];
|
|
21
20
|
readonly named: ReadonlyMap<string, string>;
|
|
22
21
|
readonly bare: ReadonlySet<string>;
|
|
23
22
|
}
|
|
24
23
|
export declare function parseArgs(argv: readonly string[]): Flags;
|
|
25
|
-
export declare function printMatrix(artifact: BakeoffArtifact): void;
|
|
26
24
|
export declare function main(argv: readonly string[]): Promise<number>;
|
|
27
25
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG;AAiCH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACpC;AAeD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAuBxD;AA0gBD,wBAAsB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqCnE"}
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* modelrig CLI — Phase 3 plan §3 + Phase 4 plan §3:
|
|
5
5
|
* modelrig bakeoff --route X --variants a,b,c --replay-last N
|
|
6
|
-
* [--
|
|
6
|
+
* [--budget-usd usd] [--json] [--no-mirror] (--envelope is a kept alias)
|
|
7
7
|
* modelrig bakeoff --from-proposal <id> [--replay-last N] [--json]
|
|
8
8
|
* modelrig watch run [--events fixtures.json] [--json]
|
|
9
9
|
*
|
|
@@ -18,21 +18,24 @@
|
|
|
18
18
|
*/
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
20
|
exports.parseArgs = parseArgs;
|
|
21
|
-
exports.printMatrix = printMatrix;
|
|
22
21
|
exports.main = main;
|
|
23
22
|
const node_crypto_1 = require("node:crypto");
|
|
24
23
|
const node_fs_1 = require("node:fs");
|
|
25
24
|
const node_os_1 = require("node:os");
|
|
26
25
|
const node_path_1 = require("node:path");
|
|
27
|
-
const
|
|
26
|
+
const init_cli_1 = require("./init/init-cli");
|
|
27
|
+
const observe_cli_1 = require("./init/observe-cli");
|
|
28
28
|
const failures_1 = require("./types/failures");
|
|
29
29
|
const failures_teaching_1 = require("./types/failures-teaching");
|
|
30
|
-
const rows_1 = require("./registry/rows");
|
|
31
30
|
const config_1 = require("./config");
|
|
32
31
|
const index_1 = require("./index");
|
|
32
|
+
const status_1 = require("./status");
|
|
33
33
|
const engine_1 = require("./bakeoff/engine");
|
|
34
34
|
const samples_1 = require("./bakeoff/samples");
|
|
35
|
+
const report_1 = require("./bakeoff/report");
|
|
36
|
+
const cli_run_1 = require("./verify/cli-run");
|
|
35
37
|
const loader_1 = require("./routes/loader");
|
|
38
|
+
const validate_cli_1 = require("./validate-cli");
|
|
36
39
|
const store_1 = require("./telemetry/store");
|
|
37
40
|
const budget_1 = require("./watch/budget");
|
|
38
41
|
const proposals_1 = require("./watch/proposals");
|
|
@@ -50,6 +53,8 @@ const BARE_FLAGS = new Set([
|
|
|
50
53
|
// modelrig init (Phase 5 WS2)
|
|
51
54
|
"dry-run",
|
|
52
55
|
"include-tests",
|
|
56
|
+
// modelrig observe / init --observe (T0/T1 scaffold)
|
|
57
|
+
"observe",
|
|
53
58
|
]);
|
|
54
59
|
function parseArgs(argv) {
|
|
55
60
|
const positional = [];
|
|
@@ -76,36 +81,6 @@ function parseArgs(argv) {
|
|
|
76
81
|
}
|
|
77
82
|
return { positional, named, bare };
|
|
78
83
|
}
|
|
79
|
-
function fmtEff(usd) {
|
|
80
|
-
return Number.isFinite(usd) ? `$${usd.toFixed(4)}` : "∞ (0 conformant)";
|
|
81
|
-
}
|
|
82
|
-
function printMatrix(artifact) {
|
|
83
|
-
console.log(`bakeoff ${artifact.id}\nroute ${artifact.route}@${artifact.routeVersion} · replayed ${artifact.replayN} captured inputs · ${artifact.ranAt}`);
|
|
84
|
-
const header = ["variant", "n", "conform", "ci95", "repair", "repair$", "lat(ms)", "eff-$/1K-conf"];
|
|
85
|
-
const rows = artifact.variants.map((m) => [
|
|
86
|
-
m.variant,
|
|
87
|
-
String(m.samples),
|
|
88
|
-
`${(m.conformRate * 100).toFixed(0)}%`,
|
|
89
|
-
`[${(m.conformCi95[0] * 100).toFixed(0)},${(m.conformCi95[1] * 100).toFixed(0)}]`,
|
|
90
|
-
`${(m.repairRate * 100).toFixed(0)}%`,
|
|
91
|
-
`$${m.meanRepairCostUsd.toFixed(4)}`,
|
|
92
|
-
m.meanLatencyMs.toFixed(0),
|
|
93
|
-
m.pricingMissing ? "UNPRICED (no ranking)" : fmtEff(m.effectiveUsdPer1kConformant),
|
|
94
|
-
]);
|
|
95
|
-
const widths = header.map((h, c) => Math.max(h.length, ...rows.map((r) => r[c].length)));
|
|
96
|
-
const line = (cells) => cells.map((cell, c) => cell.padEnd(widths[c])).join(" ");
|
|
97
|
-
console.log(line(header));
|
|
98
|
-
for (const row of rows)
|
|
99
|
-
console.log(line(row));
|
|
100
|
-
if (artifact.proposal !== null) {
|
|
101
|
-
console.log(`PROPOSAL: variant "${artifact.proposal.variant}" beats the incumbent by ` +
|
|
102
|
-
`${artifact.proposal.vsIncumbentSavingsPct.toFixed(1)}% effective cost — human decision required; ` +
|
|
103
|
-
"nothing is executed.");
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
console.log("proposal: none (no variant beats the incumbent's gates)");
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
84
|
/** Control-plane client from config.export — the watch/proposal surfaces
|
|
110
85
|
* need it; a clear error beats a silent no-op. */
|
|
111
86
|
function proposalsClientFrom(config) {
|
|
@@ -153,7 +128,7 @@ async function runAndReportBakeoff(config, flags, route, variants, replayLastN,
|
|
|
153
128
|
console.log(JSON.stringify(artifact, null, 2));
|
|
154
129
|
}
|
|
155
130
|
else {
|
|
156
|
-
printMatrix(artifact);
|
|
131
|
+
(0, report_1.printMatrix)(artifact);
|
|
157
132
|
}
|
|
158
133
|
console.log(`artifact → ${artifactPath}${mirrored ? " · mirrored to bakeoffs" : ""}` +
|
|
159
134
|
(keep !== undefined
|
|
@@ -162,7 +137,9 @@ async function runAndReportBakeoff(config, flags, route, variants, replayLastN,
|
|
|
162
137
|
return artifact;
|
|
163
138
|
}
|
|
164
139
|
finally {
|
|
165
|
-
|
|
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();
|
|
166
143
|
}
|
|
167
144
|
}
|
|
168
145
|
async function cmdBakeoff(flags) {
|
|
@@ -225,6 +202,19 @@ async function cmdBakeoff(flags) {
|
|
|
225
202
|
budgetStore.close();
|
|
226
203
|
}
|
|
227
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
|
+
}
|
|
228
218
|
const route = flags.named.get("route");
|
|
229
219
|
const variantsRaw = flags.named.get("variants");
|
|
230
220
|
const lastRaw = flags.named.get("replay-last");
|
|
@@ -236,10 +226,11 @@ async function cmdBakeoff(flags) {
|
|
|
236
226
|
if (!Number.isInteger(replayLastN) || replayLastN < 1) {
|
|
237
227
|
throw new Error("--replay-last must be a positive integer");
|
|
238
228
|
}
|
|
239
|
-
|
|
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");
|
|
240
231
|
const envelope = envelopeRaw !== undefined ? Number(envelopeRaw) : undefined;
|
|
241
232
|
if (envelope !== undefined && (!Number.isFinite(envelope) || envelope <= 0)) {
|
|
242
|
-
throw new Error("--
|
|
233
|
+
throw new Error("--budget-usd must be a positive number of dollars (per replay variant)");
|
|
243
234
|
}
|
|
244
235
|
await runAndReportBakeoff(config, flags, route, variants, replayLastN, envelope);
|
|
245
236
|
return 0;
|
|
@@ -413,75 +404,30 @@ async function cmdSamples(flags) {
|
|
|
413
404
|
return 0;
|
|
414
405
|
}
|
|
415
406
|
/**
|
|
416
|
-
* `modelrig
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
// Registry rows seed the challenger candidates. A missing/unbuilt registry
|
|
427
|
-
// is not fatal — drafts then carry the incumbent alone, and say so.
|
|
428
|
-
let registryRows = [];
|
|
429
|
-
try {
|
|
430
|
-
registryRows = (0, rows_1.loadRegistryRows)(config.registryPath);
|
|
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)");
|
|
431
417
|
}
|
|
432
|
-
|
|
433
|
-
|
|
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)");
|
|
434
422
|
}
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
...(flags.bare.has("dry-run") ? { dryRun: true } : {}),
|
|
443
|
-
...(flags.bare.has("include-tests") ? { includeTests: true } : {}),
|
|
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") } : {}),
|
|
444
430
|
});
|
|
445
|
-
if (flags.bare.has("json")) {
|
|
446
|
-
console.log(JSON.stringify({
|
|
447
|
-
sitesFound: result.sitesFound,
|
|
448
|
-
filesScanned: result.filesScanned,
|
|
449
|
-
filesSkipped: result.filesSkipped,
|
|
450
|
-
routes: result.drafts.map((d) => ({
|
|
451
|
-
route: d.route,
|
|
452
|
-
file: d.site.file,
|
|
453
|
-
line: d.site.line,
|
|
454
|
-
provider: d.site.provider,
|
|
455
|
-
model: d.site.model,
|
|
456
|
-
notes: d.site.notes,
|
|
457
|
-
})),
|
|
458
|
-
written: result.written,
|
|
459
|
-
skipped: result.skipped,
|
|
460
|
-
}, null, 2));
|
|
461
|
-
return 0;
|
|
462
|
-
}
|
|
463
|
-
console.log(`modelrig init — scanned ${result.filesScanned} file(s) under ${root}\n` +
|
|
464
|
-
`found ${result.sitesFound} provider call site(s)`);
|
|
465
|
-
if (result.sitesFound === 0) {
|
|
466
|
-
console.log("\nNothing to draft. If you know there are model calls here, they may sit behind a helper\n" +
|
|
467
|
-
"that hides the SDK — point init at the file that actually calls the provider.");
|
|
468
|
-
return 0;
|
|
469
|
-
}
|
|
470
|
-
for (const draft of result.drafts) {
|
|
471
|
-
console.log(` ${draft.site.file}:${draft.site.line} (${draft.site.provider}` +
|
|
472
|
-
`${draft.site.model !== null ? ` ${draft.site.model}` : ""}) → ${draft.route}`);
|
|
473
|
-
}
|
|
474
|
-
if (result.filesSkipped.length > 0) {
|
|
475
|
-
console.log(`\n${result.filesSkipped.length} file(s) skipped (too large or unreadable).`);
|
|
476
|
-
}
|
|
477
|
-
if (flags.bare.has("dry-run")) {
|
|
478
|
-
console.log("\n--dry-run: nothing written.");
|
|
479
|
-
return 0;
|
|
480
|
-
}
|
|
481
|
-
console.log(`\nwrote ${result.written.length} file(s) to ${draftsDir}` +
|
|
482
|
-
(result.skipped.length > 0 ? ` (${result.skipped.length} left alone — already there)` : "") +
|
|
483
|
-
`\nread ${(0, node_path_1.join)(draftsDir, "MIGRATION.md")} next. Your source was not touched.`);
|
|
484
|
-
return 0;
|
|
485
431
|
}
|
|
486
432
|
/**
|
|
487
433
|
* `modelrig serve` — Phase 5 WS4's zero-config path. The HTTP surface lives
|
|
@@ -519,10 +465,86 @@ async function cmdServe() {
|
|
|
519
465
|
// The server owns the process from here (it holds the listener open).
|
|
520
466
|
return 0;
|
|
521
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");
|
|
522
533
|
async function main(argv) {
|
|
523
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
|
+
}
|
|
524
542
|
const flags = parseArgs(rest);
|
|
525
543
|
switch (command) {
|
|
544
|
+
case "status":
|
|
545
|
+
return cmdStatus();
|
|
546
|
+
case "validate":
|
|
547
|
+
return (0, validate_cli_1.runValidateCli)();
|
|
526
548
|
case "bakeoff":
|
|
527
549
|
return cmdBakeoff(flags);
|
|
528
550
|
case "watch":
|
|
@@ -532,21 +554,17 @@ async function main(argv) {
|
|
|
532
554
|
case "samples":
|
|
533
555
|
return cmdSamples(flags);
|
|
534
556
|
case "init":
|
|
535
|
-
return cmdInit(flags);
|
|
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);
|
|
536
564
|
case "serve":
|
|
537
565
|
return cmdServe();
|
|
538
566
|
default:
|
|
539
|
-
console.error(
|
|
540
|
-
"usage:",
|
|
541
|
-
" modelrig init [path] [--out dir] [--dry-run] [--include-tests] [--json]",
|
|
542
|
-
" modelrig serve (needs the modelrig-server package)",
|
|
543
|
-
" modelrig bakeoff --route <route> --variants a,b,c --replay-last N [--envelope usd] [--keep-outputs[=all]] [--json] [--no-mirror]",
|
|
544
|
-
" modelrig bakeoff --from-proposal <id> [--replay-last N] [--keep-outputs[=all]] [--json]",
|
|
545
|
-
" modelrig watch run [--events fixtures.json] [--json]",
|
|
546
|
-
" modelrig swap execute <proposalId> --approve [--approved-by name]",
|
|
547
|
-
" modelrig swap revert <swapId>",
|
|
548
|
-
" modelrig samples purge [--bakeoff <id>]",
|
|
549
|
-
].join("\n"));
|
|
567
|
+
console.error(USAGE);
|
|
550
568
|
return 1;
|
|
551
569
|
}
|
|
552
570
|
}
|