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
package/dist/init/finder.js
CHANGED
|
@@ -15,10 +15,53 @@
|
|
|
15
15
|
*/
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.DEFAULT_SKIP_DIRS = void 0;
|
|
18
|
+
exports.compileScope = compileScope;
|
|
19
|
+
exports.matchesScope = matchesScope;
|
|
18
20
|
exports.readObjectLiteral = readObjectLiteral;
|
|
19
21
|
exports.findCallSitesInText = findCallSitesInText;
|
|
20
22
|
exports.scanRepoForCallSites = scanRepoForCallSites;
|
|
21
23
|
const signatures_1 = require("./signatures");
|
|
24
|
+
/** Compile one scope pattern into a matcher over repo-relative POSIX paths.
|
|
25
|
+
* Bare paths (no glob metachar) match the file itself or anything beneath it,
|
|
26
|
+
* so `--scope src/pipeline` onboards one pipeline directory without ceremony. */
|
|
27
|
+
function compileScope(pattern) {
|
|
28
|
+
const trimmed = pattern.replace(/\/+$/, "");
|
|
29
|
+
if (!/[*?[\]]/.test(trimmed)) {
|
|
30
|
+
return (relPath) => relPath === trimmed || relPath.startsWith(`${trimmed}/`);
|
|
31
|
+
}
|
|
32
|
+
// Glob → RegExp. `**` spans path segments; a lone `*` stops at one. Every
|
|
33
|
+
// other regex metacharacter is escaped so a literal `.` stays literal.
|
|
34
|
+
let re = "";
|
|
35
|
+
for (let i = 0; i < trimmed.length; i += 1) {
|
|
36
|
+
const ch = trimmed[i];
|
|
37
|
+
if (ch === "*") {
|
|
38
|
+
if (trimmed[i + 1] === "*") {
|
|
39
|
+
re += ".*";
|
|
40
|
+
i += 1;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
re += "[^/]*";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (ch === "?") {
|
|
47
|
+
re += "[^/]";
|
|
48
|
+
}
|
|
49
|
+
else if ("\\^$.|+()[]{}".includes(ch)) {
|
|
50
|
+
re += `\\${ch}`;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
re += ch;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const compiled = new RegExp(`^${re}$`);
|
|
57
|
+
return (relPath) => compiled.test(relPath);
|
|
58
|
+
}
|
|
59
|
+
/** True when `relPath` matches ANY scope (or no scope is set). */
|
|
60
|
+
function matchesScope(relPath, scope) {
|
|
61
|
+
if (scope === undefined || scope.length === 0)
|
|
62
|
+
return true;
|
|
63
|
+
return scope.some((pattern) => compileScope(pattern)(relPath));
|
|
64
|
+
}
|
|
22
65
|
exports.DEFAULT_SKIP_DIRS = [
|
|
23
66
|
"node_modules",
|
|
24
67
|
"dist",
|
|
@@ -278,6 +321,9 @@ function scanRepoForCallSites(root, fs, path, options = {}) {
|
|
|
278
321
|
const isTest = /\.(test|spec)\.[jt]sx?$/.test(entry.name) || full.includes("__tests__") || full.includes("__mocks__");
|
|
279
322
|
if (isTest && options.includeTests !== true)
|
|
280
323
|
continue;
|
|
324
|
+
// F-4: a file outside every --scope is never read (not counted scanned).
|
|
325
|
+
if (!matchesScope(path.relative(root, full), options.scope))
|
|
326
|
+
continue;
|
|
281
327
|
try {
|
|
282
328
|
if (fs.statSync(full).size > maxBytes) {
|
|
283
329
|
skipped.push(path.relative(root, full));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `modelrig init` command runner — lives here (not cli.ts) for the LOC cap
|
|
3
|
+
* (P1), beside the scaffold it drives (same split as observe-cli.ts and
|
|
4
|
+
* verify/cli-run.ts). Scans a repo for provider call sites and drafts a route
|
|
5
|
+
* bundle per site plus a MIGRATION.md map. It writes ONLY under
|
|
6
|
+
* <routes-dir>/drafts/; the target's source is never edited.
|
|
7
|
+
*/
|
|
8
|
+
import type { ObserveFlags } from "./observe-cli";
|
|
9
|
+
export declare function cmdInit(flags: ObserveFlags): number;
|
|
10
|
+
//# sourceMappingURL=init-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-cli.d.ts","sourceRoot":"","sources":["../../src/init/init-cli.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGlD,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAwFnD"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `modelrig init` command runner — lives here (not cli.ts) for the LOC cap
|
|
4
|
+
* (P1), beside the scaffold it drives (same split as observe-cli.ts and
|
|
5
|
+
* verify/cli-run.ts). Scans a repo for provider call sites and drafts a route
|
|
6
|
+
* bundle per site plus a MIGRATION.md map. It writes ONLY under
|
|
7
|
+
* <routes-dir>/drafts/; the target's source is never edited.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.cmdInit = cmdInit;
|
|
11
|
+
const node_fs_1 = require("node:fs");
|
|
12
|
+
const node_path_1 = require("node:path");
|
|
13
|
+
const config_1 = require("../config");
|
|
14
|
+
const rows_1 = require("../registry/rows");
|
|
15
|
+
const observe_cli_1 = require("./observe-cli");
|
|
16
|
+
const run_1 = require("./run");
|
|
17
|
+
function cmdInit(flags) {
|
|
18
|
+
// `init --observe` is the observe-first (T0/T1) alias for `modelrig observe`.
|
|
19
|
+
if (flags.bare.has("observe"))
|
|
20
|
+
return (0, observe_cli_1.cmdObserve)(flags);
|
|
21
|
+
const root = (0, node_path_1.resolve)(flags.positional[0] ?? ".");
|
|
22
|
+
const config = (0, config_1.loadConfigFromEnv)();
|
|
23
|
+
const draftsDir = flags.named.get("out") ?? (0, node_path_1.join)(config.routesDir, "drafts");
|
|
24
|
+
const scope = (0, observe_cli_1.scopeFrom)(flags);
|
|
25
|
+
// Registry rows seed the challenger candidates. A missing/unbuilt registry
|
|
26
|
+
// is not fatal — drafts then carry the incumbent alone, and say so.
|
|
27
|
+
let registryRows = [];
|
|
28
|
+
try {
|
|
29
|
+
registryRows = (0, rows_1.loadRegistryRows)(config.registryPath);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
registryRows = [];
|
|
33
|
+
}
|
|
34
|
+
const result = (0, run_1.runInit)({
|
|
35
|
+
root,
|
|
36
|
+
draftsDir,
|
|
37
|
+
fs: { readdirSync: node_fs_1.readdirSync, readFileSync: node_fs_1.readFileSync, statSync: node_fs_1.statSync, existsSync: node_fs_1.existsSync, mkdirSync: node_fs_1.mkdirSync, writeFileSync: node_fs_1.writeFileSync },
|
|
38
|
+
path: { join: node_path_1.join, relative: node_path_1.relative, dirname: node_path_1.dirname, basename: node_path_1.basename },
|
|
39
|
+
}, {
|
|
40
|
+
registryRows,
|
|
41
|
+
...(flags.bare.has("dry-run") ? { dryRun: true } : {}),
|
|
42
|
+
...(flags.bare.has("include-tests") ? { includeTests: true } : {}),
|
|
43
|
+
...(scope !== undefined ? { scope } : {}),
|
|
44
|
+
});
|
|
45
|
+
if (flags.bare.has("json")) {
|
|
46
|
+
console.log(JSON.stringify({
|
|
47
|
+
sitesFound: result.sitesFound,
|
|
48
|
+
filesScanned: result.filesScanned,
|
|
49
|
+
filesSkipped: result.filesSkipped,
|
|
50
|
+
routes: result.drafts.map((d) => ({
|
|
51
|
+
route: d.route,
|
|
52
|
+
file: d.site.file,
|
|
53
|
+
line: d.site.line,
|
|
54
|
+
provider: d.site.provider,
|
|
55
|
+
model: d.site.model,
|
|
56
|
+
notes: d.site.notes,
|
|
57
|
+
})),
|
|
58
|
+
written: result.written,
|
|
59
|
+
skipped: result.skipped,
|
|
60
|
+
}, null, 2));
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
console.log(`modelrig init — scanned ${result.filesScanned} file(s) under ${root}` +
|
|
64
|
+
(scope !== undefined ? ` (scoped to ${scope.join(", ")})` : "") +
|
|
65
|
+
`\nfound ${result.sitesFound} provider call site(s)`);
|
|
66
|
+
if (result.sitesFound === 0) {
|
|
67
|
+
console.log("\nNothing to draft. If you know there are model calls here, they may sit behind a helper\n" +
|
|
68
|
+
"that hides the SDK — point init at the file that actually calls the provider.");
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
for (const draft of result.drafts) {
|
|
72
|
+
console.log(` ${draft.site.file}:${draft.site.line} (${draft.site.provider}` +
|
|
73
|
+
`${draft.site.model !== null ? ` ${draft.site.model}` : ""}) → ${draft.route}`);
|
|
74
|
+
}
|
|
75
|
+
if (result.filesSkipped.length > 0) {
|
|
76
|
+
console.log(`\n${result.filesSkipped.length} file(s) skipped (too large or unreadable).`);
|
|
77
|
+
}
|
|
78
|
+
if (flags.bare.has("dry-run")) {
|
|
79
|
+
console.log("\n--dry-run: nothing written.");
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
console.log(`\nwrote ${result.written.length} file(s) to ${draftsDir}` +
|
|
83
|
+
(result.skipped.length > 0 ? ` (${result.skipped.length} left alone — already there)` : "") +
|
|
84
|
+
`\nread ${(0, node_path_1.join)(draftsDir, "MIGRATION.md")} next. Your source was not touched.`);
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `modelrig observe` command runner — lives here (not cli.ts) for the LOC cap
|
|
3
|
+
* (P1), beside the scaffold it drives (same split as verify/cli-run.ts and
|
|
4
|
+
* bakeoff/report.ts). Closes onboarding-test run-1's headline gap (F-1: the
|
|
5
|
+
* only observe on-ramp was a hand-wired SDK seam with no CLI). Like `init`, it
|
|
6
|
+
* NEVER edits the target's source — it writes only under its out dir.
|
|
7
|
+
*/
|
|
8
|
+
/** Structural view of the CLI's parsed flags — declared here so this module
|
|
9
|
+
* never imports cli.ts (which imports us), keeping the dependency one-way. */
|
|
10
|
+
export interface ObserveFlags {
|
|
11
|
+
readonly positional: readonly string[];
|
|
12
|
+
readonly named: ReadonlyMap<string, string>;
|
|
13
|
+
readonly bare: ReadonlySet<string>;
|
|
14
|
+
}
|
|
15
|
+
/** `--scope a,b` → the finder's scope narrowing (F-4). Comma-separated globs
|
|
16
|
+
* or path prefixes; absent = scan everything. Shared with `modelrig init`. */
|
|
17
|
+
export declare function scopeFrom(flags: ObserveFlags): readonly string[] | undefined;
|
|
18
|
+
export declare function cmdObserve(flags: ObserveFlags): number;
|
|
19
|
+
//# sourceMappingURL=observe-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observe-cli.d.ts","sourceRoot":"","sources":["../../src/init/observe-cli.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH;8EAC8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,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;8EAC8E;AAC9E,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAK5E;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAgEtD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `modelrig observe` command runner — lives here (not cli.ts) for the LOC cap
|
|
4
|
+
* (P1), beside the scaffold it drives (same split as verify/cli-run.ts and
|
|
5
|
+
* bakeoff/report.ts). Closes onboarding-test run-1's headline gap (F-1: the
|
|
6
|
+
* only observe on-ramp was a hand-wired SDK seam with no CLI). Like `init`, it
|
|
7
|
+
* NEVER edits the target's source — it writes only under its out dir.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.scopeFrom = scopeFrom;
|
|
11
|
+
exports.cmdObserve = cmdObserve;
|
|
12
|
+
const node_fs_1 = require("node:fs");
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
const config_1 = require("../config");
|
|
15
|
+
const observe_1 = require("./observe");
|
|
16
|
+
/** `--scope a,b` → the finder's scope narrowing (F-4). Comma-separated globs
|
|
17
|
+
* or path prefixes; absent = scan everything. Shared with `modelrig init`. */
|
|
18
|
+
function scopeFrom(flags) {
|
|
19
|
+
const raw = flags.named.get("scope");
|
|
20
|
+
if (raw === undefined)
|
|
21
|
+
return undefined;
|
|
22
|
+
const scope = raw.split(",").map((s) => s.trim()).filter((s) => s !== "");
|
|
23
|
+
return scope.length > 0 ? scope : undefined;
|
|
24
|
+
}
|
|
25
|
+
function cmdObserve(flags) {
|
|
26
|
+
const root = (0, node_path_1.resolve)(flags.positional[0] ?? ".");
|
|
27
|
+
const config = (0, config_1.loadConfigFromEnv)();
|
|
28
|
+
// Sibling of the routes dir: <repo>/modelrig/observe by default.
|
|
29
|
+
const outDir = flags.named.get("out") ?? (0, node_path_1.join)((0, node_path_1.dirname)(config.routesDir), "observe");
|
|
30
|
+
const scope = scopeFrom(flags);
|
|
31
|
+
const result = (0, observe_1.runObserve)({
|
|
32
|
+
root,
|
|
33
|
+
outDir,
|
|
34
|
+
fs: { readdirSync: node_fs_1.readdirSync, readFileSync: node_fs_1.readFileSync, statSync: node_fs_1.statSync, existsSync: node_fs_1.existsSync, mkdirSync: node_fs_1.mkdirSync, writeFileSync: node_fs_1.writeFileSync },
|
|
35
|
+
path: { join: node_path_1.join, relative: node_path_1.relative, dirname: node_path_1.dirname, basename: node_path_1.basename },
|
|
36
|
+
}, {
|
|
37
|
+
...(flags.bare.has("dry-run") ? { dryRun: true } : {}),
|
|
38
|
+
...(flags.bare.has("include-tests") ? { includeTests: true } : {}),
|
|
39
|
+
...(scope !== undefined ? { scope } : {}),
|
|
40
|
+
});
|
|
41
|
+
if (flags.bare.has("json")) {
|
|
42
|
+
console.log(JSON.stringify({
|
|
43
|
+
sitesFound: result.sitesFound,
|
|
44
|
+
filesScanned: result.filesScanned,
|
|
45
|
+
filesSkipped: result.filesSkipped,
|
|
46
|
+
steps: result.plans.map((p) => ({
|
|
47
|
+
stepKey: p.stepKey,
|
|
48
|
+
file: p.site.file,
|
|
49
|
+
line: p.site.line,
|
|
50
|
+
provider: p.site.provider,
|
|
51
|
+
model: p.site.model,
|
|
52
|
+
})),
|
|
53
|
+
written: result.written,
|
|
54
|
+
skipped: result.skipped,
|
|
55
|
+
}, null, 2));
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
console.log(`modelrig observe — scanned ${result.filesScanned} file(s) under ${root}` +
|
|
59
|
+
(scope !== undefined ? ` (scoped to ${scope.join(", ")})` : "") +
|
|
60
|
+
`\nfound ${result.sitesFound} model call site(s) → ${result.sitesFound} proposed step(s)`);
|
|
61
|
+
for (const plan of result.plans) {
|
|
62
|
+
console.log(` ${plan.site.file}:${plan.site.line} → step "${plan.stepKey}"`);
|
|
63
|
+
}
|
|
64
|
+
if (flags.bare.has("dry-run")) {
|
|
65
|
+
console.log("\n--dry-run: nothing written.");
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
console.log(`\nwrote ${result.written.length} file(s) to ${outDir}` +
|
|
69
|
+
(result.skipped.length > 0 ? ` (${result.skipped.length} left alone — already there)` : "") +
|
|
70
|
+
`\nread ${(0, node_path_1.join)(outDir, "OBSERVE.md")} next — apply the wiring diffs one step at a time.` +
|
|
71
|
+
"\nObserve changes no behaviour; with MODELRIG_ARTIFACTS off your pipeline is byte-for-byte the same.");
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `modelrig observe` — the observe-first (T0/T1) scaffold.
|
|
3
|
+
*
|
|
4
|
+
* Onboarding-test run-1's headline finding (F-1): the ONLY documented observe
|
|
5
|
+
* on-ramp (`instrumentation.md`) is a hand-wired SDK seam with NO CLI. Every
|
|
6
|
+
* customer-facing surface points a migrating customer at `modelrig init`, which
|
|
7
|
+
* is route extraction (T2) — so "just show me my runs first" had no CLI
|
|
8
|
+
* affordance at all. This command closes that gap.
|
|
9
|
+
*
|
|
10
|
+
* It does for OBSERVE what `init` does for MIGRATE, and obeys the same hard
|
|
11
|
+
* rule: it NEVER edits the target's source. It writes exactly two things under
|
|
12
|
+
* `modelrig/observe/` — a self-contained `record-step` seam (generated fresh,
|
|
13
|
+
* per the doc's "write your own; do not resurrect a deleted one" rule) and an
|
|
14
|
+
* `OBSERVE.md` plan whose per-call-site wiring is emitted as a `diff` for the
|
|
15
|
+
* agent (or human) to apply with judgment, one step at a time.
|
|
16
|
+
*
|
|
17
|
+
* Observe is deliberately NOT migration: it drafts no routes, reroutes no
|
|
18
|
+
* model calls, and changes no behaviour. With `MODELRIG_ARTIFACTS` off the
|
|
19
|
+
* pipeline is byte-for-byte identical (instrumentation.md).
|
|
20
|
+
*/
|
|
21
|
+
import type { CallSite, FileSystemLike } from "./finder";
|
|
22
|
+
import type { FinderOptions } from "./finder";
|
|
23
|
+
/** One instrumented step the scaffold proposes: a detected model call site
|
|
24
|
+
* becomes a step boundary whose work products (prompt, raw, parsed) are saved. */
|
|
25
|
+
export interface ObservePlan {
|
|
26
|
+
readonly site: CallSite;
|
|
27
|
+
/** Stable, human-legible step key used in `recordStep(...)` and the console. */
|
|
28
|
+
readonly stepKey: string;
|
|
29
|
+
/** The `recordStep(...)` line proposed at this site. */
|
|
30
|
+
readonly snippet: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ObserveResult {
|
|
33
|
+
readonly plans: readonly ObservePlan[];
|
|
34
|
+
readonly sitesFound: number;
|
|
35
|
+
readonly filesScanned: number;
|
|
36
|
+
readonly filesSkipped: readonly string[];
|
|
37
|
+
/** Where the generated seam helper lives (repo-relative). */
|
|
38
|
+
readonly seamPath: string;
|
|
39
|
+
/** The generated, self-contained seam helper source. */
|
|
40
|
+
readonly seamContent: string;
|
|
41
|
+
/** The OBSERVE.md plan (wiring diffs + the human key-mint handoff). */
|
|
42
|
+
readonly scaffoldMarkdown: string;
|
|
43
|
+
readonly written: readonly string[];
|
|
44
|
+
readonly skipped: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
export interface ObserveOptions extends FinderOptions {
|
|
47
|
+
/** Plan without writing (`--dry-run`). */
|
|
48
|
+
readonly dryRun?: boolean;
|
|
49
|
+
}
|
|
50
|
+
export interface ObserveDeps {
|
|
51
|
+
readonly root: string;
|
|
52
|
+
/** Directory the scaffold writes into (repo-relative), e.g. modelrig/observe. */
|
|
53
|
+
readonly outDir: string;
|
|
54
|
+
readonly fs: FileSystemLike & {
|
|
55
|
+
existsSync: (p: string) => boolean;
|
|
56
|
+
mkdirSync: (p: string, o: {
|
|
57
|
+
recursive: boolean;
|
|
58
|
+
}) => void;
|
|
59
|
+
writeFileSync: (p: string, c: string, e: "utf8") => void;
|
|
60
|
+
};
|
|
61
|
+
readonly path: {
|
|
62
|
+
join: (...parts: string[]) => string;
|
|
63
|
+
relative: (from: string, to: string) => string;
|
|
64
|
+
dirname: (p: string) => string;
|
|
65
|
+
basename: (p: string, ext?: string) => string;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** A stable step key from a call site: `<dir>_<file>_<line>`, deduped so two
|
|
69
|
+
* sites never collide. Deterministic, so a second run proposes the same keys. */
|
|
70
|
+
export declare function stepKeyFor(site: CallSite, used: Set<string>): string;
|
|
71
|
+
/** The generated seam. Self-contained and fresh (never resurrected from git
|
|
72
|
+
* history — that is the point of onboarding-test run-2). Mirrors the reference
|
|
73
|
+
* `recordStep` in instrumentation.md, plus a `withRun` boundary wrapper. */
|
|
74
|
+
export declare function seamContent(): string;
|
|
75
|
+
export declare function buildObserveMarkdown(plans: readonly ObservePlan[], seamPath: string, result: {
|
|
76
|
+
sitesFound: number;
|
|
77
|
+
filesScanned: number;
|
|
78
|
+
filesSkipped: readonly string[];
|
|
79
|
+
}, path: ObserveDeps["path"]): string;
|
|
80
|
+
export declare function runObserve(deps: ObserveDeps, options?: ObserveOptions): ObserveResult;
|
|
81
|
+
//# sourceMappingURL=observe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../src/init/observe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C;kFACkF;AAClF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,wDAAwD;IACxD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,cAAc,GAAG;QAC5B,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;QACnC,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;YAAE,SAAS,EAAE,OAAO,CAAA;SAAE,KAAK,IAAI,CAAC;QAC1D,aAAa,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;KAC1D,CAAC;IACF,QAAQ,CAAC,IAAI,EAAE;QACb,IAAI,EAAE,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC;QACrC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;QAC/C,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;QAC/B,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;KAC/C,CAAC;CACH;AAKD;iFACiF;AACjF,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAQpE;AAED;;4EAE4E;AAC5E,wBAAgB,WAAW,IAAI,MAAM,CA6DpC;AAoCD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EACrF,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,GACxB,MAAM,CA+GR;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,GAAE,cAAmB,GAAG,aAAa,CA6DzF"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `modelrig observe` — the observe-first (T0/T1) scaffold.
|
|
4
|
+
*
|
|
5
|
+
* Onboarding-test run-1's headline finding (F-1): the ONLY documented observe
|
|
6
|
+
* on-ramp (`instrumentation.md`) is a hand-wired SDK seam with NO CLI. Every
|
|
7
|
+
* customer-facing surface points a migrating customer at `modelrig init`, which
|
|
8
|
+
* is route extraction (T2) — so "just show me my runs first" had no CLI
|
|
9
|
+
* affordance at all. This command closes that gap.
|
|
10
|
+
*
|
|
11
|
+
* It does for OBSERVE what `init` does for MIGRATE, and obeys the same hard
|
|
12
|
+
* rule: it NEVER edits the target's source. It writes exactly two things under
|
|
13
|
+
* `modelrig/observe/` — a self-contained `record-step` seam (generated fresh,
|
|
14
|
+
* per the doc's "write your own; do not resurrect a deleted one" rule) and an
|
|
15
|
+
* `OBSERVE.md` plan whose per-call-site wiring is emitted as a `diff` for the
|
|
16
|
+
* agent (or human) to apply with judgment, one step at a time.
|
|
17
|
+
*
|
|
18
|
+
* Observe is deliberately NOT migration: it drafts no routes, reroutes no
|
|
19
|
+
* model calls, and changes no behaviour. With `MODELRIG_ARTIFACTS` off the
|
|
20
|
+
* pipeline is byte-for-byte identical (instrumentation.md).
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.stepKeyFor = stepKeyFor;
|
|
24
|
+
exports.seamContent = seamContent;
|
|
25
|
+
exports.buildObserveMarkdown = buildObserveMarkdown;
|
|
26
|
+
exports.runObserve = runObserve;
|
|
27
|
+
const finder_1 = require("./finder");
|
|
28
|
+
const normalize = (s) => s.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_|_$/g, "").toLowerCase();
|
|
29
|
+
/** A stable step key from a call site: `<dir>_<file>_<line>`, deduped so two
|
|
30
|
+
* sites never collide. Deterministic, so a second run proposes the same keys. */
|
|
31
|
+
function stepKeyFor(site, used) {
|
|
32
|
+
const parts = site.file.split("/").filter((p) => p !== "" && p !== "." && p !== "src");
|
|
33
|
+
const fileStem = (parts[parts.length - 1] ?? "step").replace(/\.[jt]sx?$/, "");
|
|
34
|
+
const dir = parts.length > 1 ? parts[parts.length - 2] : "app";
|
|
35
|
+
let key = `${normalize(dir)}_${normalize(fileStem)}_${site.line}`;
|
|
36
|
+
while (used.has(key))
|
|
37
|
+
key = `${key}_x`;
|
|
38
|
+
used.add(key);
|
|
39
|
+
return key;
|
|
40
|
+
}
|
|
41
|
+
/** The generated seam. Self-contained and fresh (never resurrected from git
|
|
42
|
+
* history — that is the point of onboarding-test run-2). Mirrors the reference
|
|
43
|
+
* `recordStep` in instrumentation.md, plus a `withRun` boundary wrapper. */
|
|
44
|
+
function seamContent() {
|
|
45
|
+
return `/**
|
|
46
|
+
* ModelRig observe seam — generated by \`modelrig observe\`.
|
|
47
|
+
*
|
|
48
|
+
* This is YOURS to keep and edit; it is generated fresh (not copied from any
|
|
49
|
+
* deleted seam) so it always matches the current SDK. It records each pipeline
|
|
50
|
+
* run as a run -> step -> artifact chain you click through in the console.
|
|
51
|
+
*
|
|
52
|
+
* Observe-only: no routes move, no model calls are rerouted. With
|
|
53
|
+
* MODELRIG_ARTIFACTS off (or no control plane configured) every call here is
|
|
54
|
+
* inert and your pipeline behaves byte-for-byte as before. Nothing here ever
|
|
55
|
+
* throws into your pipeline — a telemetry write that fails is logged and
|
|
56
|
+
* dropped (instrumentation.md, "Never throws into your pipeline").
|
|
57
|
+
*
|
|
58
|
+
* Docs: https://modelrig.dev/instrumentation.html
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
import { createRig, loadConfigFromEnv, type Rig } from "modelrig";
|
|
62
|
+
|
|
63
|
+
const rig: Rig = createRig(loadConfigFromEnv());
|
|
64
|
+
|
|
65
|
+
/** Open a run context around a whole pipeline, close it on success/failure.
|
|
66
|
+
* \`episodeKey\` is a grouping key (a natural choice: your per-run attempt id),
|
|
67
|
+
* not a unique id. Pass \`environment\` (e.g. your NODE_ENV) to tag the run.
|
|
68
|
+
* Use \`rig.artifacts.run.scope(...)\` instead for concurrent runs in one
|
|
69
|
+
* process. */
|
|
70
|
+
export async function withRun<T>(
|
|
71
|
+
opts: { pipeline: string; episodeKey: string; environment?: string },
|
|
72
|
+
body: () => Promise<T>,
|
|
73
|
+
): Promise<T> {
|
|
74
|
+
const run = rig.artifacts.run.start({
|
|
75
|
+
pipeline: opts.pipeline,
|
|
76
|
+
episodeKey: opts.episodeKey,
|
|
77
|
+
...(opts.environment !== undefined ? { environment: opts.environment } : {}),
|
|
78
|
+
});
|
|
79
|
+
try {
|
|
80
|
+
const out = await body();
|
|
81
|
+
run.end("succeeded");
|
|
82
|
+
return out;
|
|
83
|
+
} catch (err) {
|
|
84
|
+
run.end("failed");
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Save prompt + raw + parsed for one step, link lineage, flag conformance.
|
|
90
|
+
* A stage whose model call does NOT go through the rig still produces
|
|
91
|
+
* artifacts (with \`stepKey\` set and no step row) — correct and honest; the
|
|
92
|
+
* console renders "cost n/a" rather than inventing a number. */
|
|
93
|
+
export function recordStep(
|
|
94
|
+
stepKey: string,
|
|
95
|
+
work: { prompt: string; rawText: string; json: unknown; conforms: boolean },
|
|
96
|
+
): void {
|
|
97
|
+
const p = rig.artifacts.artifact.save(work.prompt, { name: \`\${stepKey}.prompt\`, type: "prompt", stepKey });
|
|
98
|
+
const raw = rig.artifacts.artifact.save(work.rawText, { name: \`\${stepKey}.raw\`, type: "raw_response", stepKey });
|
|
99
|
+
const parsed = rig.artifacts.artifact.save(work.json, { name: \`\${stepKey}.output\`, type: "step_output", task: stepKey, stepKey });
|
|
100
|
+
if (p && parsed) rig.artifacts.artifact.link(parsed, p, "derived_from");
|
|
101
|
+
if (raw && parsed) rig.artifacts.artifact.link(parsed, raw, "derived_from");
|
|
102
|
+
if (parsed) rig.artifacts.artifact.evaluate(parsed, { evaluator: "schema-check", kind: "deterministic", passed: work.conforms });
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
/** The import specifier a source file uses to reach the seam. Best-effort
|
|
107
|
+
* relative path; a coding agent adjusts it to the project's alias if needed. */
|
|
108
|
+
function importSpecifier(fromFile, seamPath, path) {
|
|
109
|
+
const noExt = seamPath.replace(/\.[jt]sx?$/, "");
|
|
110
|
+
const rel = path.relative(path.dirname(fromFile), noExt);
|
|
111
|
+
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
112
|
+
}
|
|
113
|
+
function recordStepSnippet(stepKey) {
|
|
114
|
+
return (`recordStep(${JSON.stringify(stepKey)}, {\n` +
|
|
115
|
+
` prompt, // the assembled prompt string\n` +
|
|
116
|
+
` rawText, // the model's raw text, pre-parse\n` +
|
|
117
|
+
` json: parsed, // your parsed/validated output\n` +
|
|
118
|
+
` conforms, // did it satisfy your schema?\n` +
|
|
119
|
+
`});`);
|
|
120
|
+
}
|
|
121
|
+
/** The run boundary is most likely the shallowest detected file (the entry
|
|
122
|
+
* that drives the pipeline). A heuristic, flagged as such — never asserted. */
|
|
123
|
+
function runBoundaryFile(plans) {
|
|
124
|
+
let best = null;
|
|
125
|
+
let bestDepth = Number.POSITIVE_INFINITY;
|
|
126
|
+
for (const plan of plans) {
|
|
127
|
+
const depth = plan.site.file.split("/").length;
|
|
128
|
+
if (depth < bestDepth) {
|
|
129
|
+
bestDepth = depth;
|
|
130
|
+
best = plan.site.file;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return best;
|
|
134
|
+
}
|
|
135
|
+
function buildObserveMarkdown(plans, seamPath, result, path) {
|
|
136
|
+
const lines = [
|
|
137
|
+
"# ModelRig observe — wire up run/step/artifact telemetry",
|
|
138
|
+
"",
|
|
139
|
+
"**Nothing in your source has changed.** `modelrig observe` generated one seam helper",
|
|
140
|
+
`(\`${seamPath}\`) and this plan. Observe is **not migration**: no routes were drafted, no`,
|
|
141
|
+
"model calls rerouted. Applying the wiring below records each run as a run → step →",
|
|
142
|
+
"artifact chain you click through in the console — and with the flag off, your pipeline",
|
|
143
|
+
"behaves **byte-for-byte** as before.",
|
|
144
|
+
"",
|
|
145
|
+
`Detected **${result.sitesFound} model call site(s)** across ${result.filesScanned} scanned file(s);`,
|
|
146
|
+
"each is proposed as one instrumented **step**. Apply the diffs one step at a time — a step",
|
|
147
|
+
"you do not understand yet is a step you can leave for later.",
|
|
148
|
+
"",
|
|
149
|
+
"## 1. Turn the namespace on",
|
|
150
|
+
"",
|
|
151
|
+
"```bash",
|
|
152
|
+
"export MODELRIG_ARTIFACTS=1 # enable the artifact namespace",
|
|
153
|
+
"# export MODELRIG_ARTIFACT_STEPS=step_a,step_b # optional: instrument a subset",
|
|
154
|
+
"```",
|
|
155
|
+
"",
|
|
156
|
+
"> **Human handoff (one step, once).** Seeing these runs **in the console** needs a free",
|
|
157
|
+
"> account + an API key minted at <https://app.modelrig.ai> (`/keys`) — a console login a",
|
|
158
|
+
"> coding agent cannot perform. Set `MODELRIG_API_KEY=<key>` once a human provides it; the",
|
|
159
|
+
"> default ingest host (`https://api.modelrig.ai`) needs no other config. Everything else",
|
|
160
|
+
"> below an agent can do now — plan the handoff, do not stall on it.",
|
|
161
|
+
"",
|
|
162
|
+
];
|
|
163
|
+
if (plans.length === 0) {
|
|
164
|
+
lines.push("## No model call sites found", "", "Nothing matched in the scanned files. If you know there are model calls here, they may sit", "behind a helper that hides the SDK — point `modelrig observe` at the file that actually", "calls the provider, or wire `withRun` / `recordStep` by hand from the seam helper above.", "");
|
|
165
|
+
return `${lines.join("\n")}\n`;
|
|
166
|
+
}
|
|
167
|
+
const boundary = runBoundaryFile(plans);
|
|
168
|
+
const seamNoExt = seamPath.replace(/\.[jt]sx?$/, "");
|
|
169
|
+
const boundaryImport = boundary === null ? seamNoExt : importSpecifier(boundary, seamPath, path);
|
|
170
|
+
lines.push("## 2. Open a run context around the pipeline", "", boundary === null
|
|
171
|
+
? "Wrap your pipeline's entry point in `withRun(...)` from the seam helper:"
|
|
172
|
+
: `Your pipeline entry looks like \`${boundary}\` (the shallowest detected call site — a` +
|
|
173
|
+
" heuristic; move this if your real entry is elsewhere). Wrap it in `withRun(...)`:", "", "```diff", `+ import { withRun } from "${boundaryImport}";`, "", "- export async function runPipeline(input) {", "- /* ... your steps ... */", "- }", "+ export async function runPipeline(input) {", "+ return withRun(", '+ { pipeline: "my-pipeline", episodeKey: input.attemptId },', "+ async () => {", "+ /* ... your steps ... */", "+ },", "+ );", "+ }", "```", "", "## 3. Record each step's work products", "", "Each detected model call is one step. After the call parses its output, add a `recordStep`", "(the diffs below quote the call line so you can find it):", "");
|
|
174
|
+
// Group the per-site wiring diffs by file so each file gets one import line.
|
|
175
|
+
const byFile = new Map();
|
|
176
|
+
for (const plan of plans) {
|
|
177
|
+
const list = byFile.get(plan.site.file) ?? [];
|
|
178
|
+
list.push(plan);
|
|
179
|
+
byFile.set(plan.site.file, list);
|
|
180
|
+
}
|
|
181
|
+
for (const [file, filePlans] of byFile) {
|
|
182
|
+
lines.push(`### \`${file}\``, "", "```diff", `+ import { recordStep } from "${importSpecifier(file, seamPath, path)}";`, "");
|
|
183
|
+
for (const plan of filePlans) {
|
|
184
|
+
lines.push(` // line ${plan.site.line}: ${plan.site.provider}` +
|
|
185
|
+
`${plan.site.model !== null ? ` ${plan.site.model}` : ""} call — save its work products`, ...plan.snippet.split("\n").map((l) => `+ ${l}`), "");
|
|
186
|
+
}
|
|
187
|
+
lines.push("```", "");
|
|
188
|
+
}
|
|
189
|
+
lines.push("## 4. See it", "", "Run your pipeline once, then open the console: **Runs** lists the run, `/runs/[id]` renders", "the semantic chain (artifacts grouped by step, with hashes and evaluation badges), and", "`/artifacts/[id]` shows one artifact's lineage. Un-routed steps show **cost n/a** — correct", "for observe-only; nothing routes through the rig yet.", "", "## When you are ready to migrate (later, optional)", "", "Observe answers *what is my pipeline doing?* When you want to *change what serves a step*,", "that is route extraction (T2) — a separate, behaviour-affecting move behind its own review:", "`modelrig init --scope <this pipeline>`. Observe first is the safer order.", "");
|
|
190
|
+
return `${lines.join("\n")}\n`;
|
|
191
|
+
}
|
|
192
|
+
function runObserve(deps, options = {}) {
|
|
193
|
+
const scan = (0, finder_1.scanRepoForCallSites)(deps.root, deps.fs, deps.path, options);
|
|
194
|
+
const used = new Set();
|
|
195
|
+
const plans = scan.sites.map((site) => {
|
|
196
|
+
const stepKey = stepKeyFor(site, used);
|
|
197
|
+
return { site, stepKey, snippet: recordStepSnippet(stepKey) };
|
|
198
|
+
});
|
|
199
|
+
const seamPath = deps.path.join(deps.outDir, "record-step.ts");
|
|
200
|
+
// Import specifiers and prose are computed root-relative — call sites come
|
|
201
|
+
// back relative to `root`, so the seam must be expressed the same way for
|
|
202
|
+
// `path.relative` to yield a sane `../../modelrig/observe/...` specifier.
|
|
203
|
+
const seamRel = deps.path.relative(deps.root, seamPath);
|
|
204
|
+
const content = seamContent();
|
|
205
|
+
const scaffoldMarkdown = buildObserveMarkdown(plans, seamRel, { sitesFound: scan.sites.length, filesScanned: scan.filesScanned, filesSkipped: scan.filesSkipped }, deps.path);
|
|
206
|
+
if (options.dryRun === true) {
|
|
207
|
+
return {
|
|
208
|
+
plans,
|
|
209
|
+
sitesFound: scan.sites.length,
|
|
210
|
+
filesScanned: scan.filesScanned,
|
|
211
|
+
filesSkipped: scan.filesSkipped,
|
|
212
|
+
seamPath,
|
|
213
|
+
seamContent: content,
|
|
214
|
+
scaffoldMarkdown,
|
|
215
|
+
written: [],
|
|
216
|
+
skipped: [],
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
deps.fs.mkdirSync(deps.outDir, { recursive: true });
|
|
220
|
+
const written = [];
|
|
221
|
+
const skipped = [];
|
|
222
|
+
// The seam is the customer's to edit — never clobber an existing one.
|
|
223
|
+
if (deps.fs.existsSync(seamPath)) {
|
|
224
|
+
skipped.push(seamPath);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
deps.fs.writeFileSync(seamPath, content, "utf8");
|
|
228
|
+
written.push(seamPath);
|
|
229
|
+
}
|
|
230
|
+
// OBSERVE.md is a regenerated report, not a hand-edited draft — always rewrite.
|
|
231
|
+
const planPath = deps.path.join(deps.outDir, "OBSERVE.md");
|
|
232
|
+
deps.fs.writeFileSync(planPath, scaffoldMarkdown, "utf8");
|
|
233
|
+
written.push(planPath);
|
|
234
|
+
return {
|
|
235
|
+
plans,
|
|
236
|
+
sitesFound: scan.sites.length,
|
|
237
|
+
filesScanned: scan.filesScanned,
|
|
238
|
+
filesSkipped: scan.filesSkipped,
|
|
239
|
+
seamPath,
|
|
240
|
+
seamContent: content,
|
|
241
|
+
scaffoldMarkdown,
|
|
242
|
+
written,
|
|
243
|
+
skipped,
|
|
244
|
+
};
|
|
245
|
+
}
|