@rulvar/cli 1.16.2 → 1.18.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/dist/cli.js +1 -1
- package/dist/index.d.ts +25 -1
- package/dist/index.js +1 -1
- package/dist/{io-lNmGU5Lv.js → io-74cSn0r8.js} +381 -111
- package/package.json +7 -7
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ interface CliIo {
|
|
|
16
16
|
declare function processIo(): CliIo;
|
|
17
17
|
//#endregion
|
|
18
18
|
//#region src/cli-main.d.ts
|
|
19
|
-
declare const HELP
|
|
19
|
+
declare const HELP: string;
|
|
20
20
|
declare function runCli(argv: string[], options: {
|
|
21
21
|
cwd: string;
|
|
22
22
|
io: CliIo;
|
|
@@ -76,6 +76,30 @@ interface KbSweepCliConfig {
|
|
|
76
76
|
model: `${string}:${string}`;
|
|
77
77
|
effort?: string;
|
|
78
78
|
}) => unknown;
|
|
79
|
+
/**
|
|
80
|
+
* Immutable per-run ceilings and the aggregate debit-only envelope
|
|
81
|
+
* (v1.16.2 review P1-2). A sweep multiplies paid runs: pool members
|
|
82
|
+
* times cases for targets, one judge run per judge-grader call, one
|
|
83
|
+
* canary run per probe per member, and the falsification union can
|
|
84
|
+
* grow the pool past the configured models. Per-run ceilings alone do
|
|
85
|
+
* not bound that product, so maxTotalUsd is the hard aggregate ceiling
|
|
86
|
+
* every target, judge, and canary run authorizes against BEFORE it
|
|
87
|
+
* starts. Required unless allowUnbounded is set: a sweep is never
|
|
88
|
+
* silently unbounded.
|
|
89
|
+
*/
|
|
90
|
+
budgets?: {
|
|
91
|
+
/** Immutable ceiling B0 of every eval target run. */targetUsd: number; /** Immutable ceiling of every judge run. */
|
|
92
|
+
judgeUsd: number; /** Immutable ceiling of every canary probe run. */
|
|
93
|
+
canaryUsd: number; /** The debit-only envelope over the WHOLE sweep (targets, judges, canary). */
|
|
94
|
+
maxTotalUsd: number;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Explicitly waive the ceilings and run every target, judge, and
|
|
98
|
+
* canary run unbounded (the pre-v1.16.2 behavior). A sweep with
|
|
99
|
+
* neither budgets nor this flag set fails loudly: an unbounded paid
|
|
100
|
+
* matrix is never the silent default.
|
|
101
|
+
*/
|
|
102
|
+
allowUnbounded?: boolean;
|
|
79
103
|
}
|
|
80
104
|
/** Loads `rulvar.config.mjs`/`.js` from cwd; absent config is fine. */
|
|
81
105
|
declare function loadCliConfig(cwd: string): Promise<CliConfig>;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as resumeCommand, c as driveRun, d as renderEventLine, f as DEFAULT_STORE_DIR, g as looksLikeFile, h as loadWorkflowModule, i as inspectCommand, l as reportOutcome, m as loadCliConfig, n as HELP, o as runCommand, p as assembleEngine, r as runCli, s as runsLsCommand, t as processIo, u as attachProgress } from "./io-
|
|
1
|
+
import { a as resumeCommand, c as driveRun, d as renderEventLine, f as DEFAULT_STORE_DIR, g as looksLikeFile, h as loadWorkflowModule, i as inspectCommand, l as reportOutcome, m as loadCliConfig, n as HELP, o as runCommand, p as assembleEngine, r as runCli, s as runsLsCommand, t as processIo, u as attachProgress } from "./io-74cSn0r8.js";
|
|
2
2
|
import { ConfigError, InvalidResolutionError, JournalCompatibilityError, LeaseHeldError, Replayer, RulvarError, buildDeriverRegistry, costReportFromJournal, maskSecrets, normalizeEntry, scanJournalCompatibility, validateSchemaSpec } from "@rulvar/core";
|
|
3
3
|
//#region src/server.ts
|
|
4
4
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ConfigError, FileModelKnowledgeStore, INBOX_PROPOSAL_TTL_DAYS, JsonlFileStore, claimExpired, claimExpiry, compilePermissionPreset, costReportFromJournal, createEngine, parseModelRef, priceUsdOf, proposalStatement, remeasureQueue, resolvePricing, runProfile } from "@rulvar/core";
|
|
2
|
-
import { parseArgs } from "node:util";
|
|
3
2
|
import { join, resolve } from "node:path";
|
|
4
3
|
import { existsSync, statSync } from "node:fs";
|
|
5
4
|
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { parseArgs } from "node:util";
|
|
6
6
|
import { createInterface } from "node:readline";
|
|
7
7
|
//#region src/config.ts
|
|
8
8
|
/**
|
|
@@ -302,6 +302,254 @@ function reportOutcome(outcome, io) {
|
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
304
|
//#endregion
|
|
305
|
+
//#region src/grammar.ts
|
|
306
|
+
/**
|
|
307
|
+
* The canonical CLI grammar as data (v1.16.2 review P3-1): one
|
|
308
|
+
* structure generates the help block, every per-command usage line,
|
|
309
|
+
* and the docs contract test, so the three surfaces can never drift
|
|
310
|
+
* apart again. Parsing goes through parseCommand, which converts
|
|
311
|
+
* node:util parseArgs failures into the fail-loud ConfigError grammar
|
|
312
|
+
* the CLI documents (v1.16.2 review P2-1): nothing is ignored; unknown
|
|
313
|
+
* flags, duplicate value flags, mutually exclusive pairs, and wrong
|
|
314
|
+
* positional arity all fail naming the canonical usage.
|
|
315
|
+
*/
|
|
316
|
+
const ARGS = {
|
|
317
|
+
name: "args",
|
|
318
|
+
placeholder: "JSON"
|
|
319
|
+
};
|
|
320
|
+
const STORE = {
|
|
321
|
+
name: "store",
|
|
322
|
+
placeholder: "PATH"
|
|
323
|
+
};
|
|
324
|
+
/**
|
|
325
|
+
* Every command of the canonical grammar (no aliases in v1). The help
|
|
326
|
+
* block, the per-command usage errors, and the docs grammar block in
|
|
327
|
+
* docs/guide/cli.md all derive from this table; the docs contract test
|
|
328
|
+
* compares them literally.
|
|
329
|
+
*/
|
|
330
|
+
const GRAMMAR = {
|
|
331
|
+
run: {
|
|
332
|
+
command: "run",
|
|
333
|
+
positionals: ["<file|name>"],
|
|
334
|
+
flags: [
|
|
335
|
+
ARGS,
|
|
336
|
+
STORE,
|
|
337
|
+
{
|
|
338
|
+
name: "budget-usd",
|
|
339
|
+
placeholder: "N"
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: "profile",
|
|
343
|
+
placeholder: "NAME"
|
|
344
|
+
}
|
|
345
|
+
]
|
|
346
|
+
},
|
|
347
|
+
resume: {
|
|
348
|
+
command: "resume",
|
|
349
|
+
positionals: ["<runId>"],
|
|
350
|
+
flags: [ARGS, STORE]
|
|
351
|
+
},
|
|
352
|
+
"runs ls": {
|
|
353
|
+
command: "runs ls",
|
|
354
|
+
positionals: [],
|
|
355
|
+
flags: [STORE],
|
|
356
|
+
note: "(no aliases in v1)"
|
|
357
|
+
},
|
|
358
|
+
inspect: {
|
|
359
|
+
command: "inspect",
|
|
360
|
+
positionals: ["<runId>"],
|
|
361
|
+
flags: [STORE]
|
|
362
|
+
},
|
|
363
|
+
plan: {
|
|
364
|
+
command: "plan",
|
|
365
|
+
positionals: ["\"<goal>\""],
|
|
366
|
+
flags: [
|
|
367
|
+
{
|
|
368
|
+
name: "planning-budget-usd",
|
|
369
|
+
placeholder: "N"
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: "budget-usd",
|
|
373
|
+
placeholder: "N"
|
|
374
|
+
},
|
|
375
|
+
{ name: "allow-unbounded" },
|
|
376
|
+
{ name: "dry-run" }
|
|
377
|
+
]
|
|
378
|
+
},
|
|
379
|
+
"kb list": {
|
|
380
|
+
command: "kb list",
|
|
381
|
+
positionals: [],
|
|
382
|
+
flags: []
|
|
383
|
+
},
|
|
384
|
+
"kb inbox": {
|
|
385
|
+
command: "kb inbox",
|
|
386
|
+
positionals: [],
|
|
387
|
+
flags: [STORE]
|
|
388
|
+
},
|
|
389
|
+
"kb gate": {
|
|
390
|
+
command: "kb gate",
|
|
391
|
+
positionals: ["<runId>", "<entryRef>"],
|
|
392
|
+
flags: [
|
|
393
|
+
{
|
|
394
|
+
name: "approver",
|
|
395
|
+
placeholder: "NAME",
|
|
396
|
+
required: true
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
name: "ruled-out",
|
|
400
|
+
placeholder: "a,b,c",
|
|
401
|
+
required: true
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
name: "contrast-run",
|
|
405
|
+
placeholder: "runId#seq",
|
|
406
|
+
exclusiveGroup: "contrast"
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
name: "contrast-eval",
|
|
410
|
+
placeholder: "reportId:caseId[,caseId...]",
|
|
411
|
+
exclusiveGroup: "contrast"
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
name: "confidence",
|
|
415
|
+
placeholder: "high|medium|low"
|
|
416
|
+
},
|
|
417
|
+
STORE
|
|
418
|
+
]
|
|
419
|
+
},
|
|
420
|
+
"kb sweep": {
|
|
421
|
+
command: "kb sweep",
|
|
422
|
+
positionals: [],
|
|
423
|
+
flags: [],
|
|
424
|
+
note: "(configuration lives in rulvar.config.mjs)"
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
/** The kb dispatch line: subcommands carry their own grammar entries. */
|
|
428
|
+
const KB_FAMILY_USAGE = "usage: rulvar kb <list | inbox | gate | sweep> (no aliases in v1)";
|
|
429
|
+
function renderFlags(flags) {
|
|
430
|
+
const tokens = [];
|
|
431
|
+
const renderedGroups = /* @__PURE__ */ new Set();
|
|
432
|
+
for (const flag of flags) {
|
|
433
|
+
if (flag.exclusiveGroup !== void 0) {
|
|
434
|
+
if (renderedGroups.has(flag.exclusiveGroup)) continue;
|
|
435
|
+
renderedGroups.add(flag.exclusiveGroup);
|
|
436
|
+
const members = flags.filter((candidate) => candidate.exclusiveGroup === flag.exclusiveGroup).map((member) => `--${member.name} ${member.placeholder ?? ""}`.trim());
|
|
437
|
+
tokens.push(`[${members.join(" | ")}]`);
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
const body = flag.placeholder === void 0 ? `--${flag.name}` : `--${flag.name} ${flag.placeholder}`;
|
|
441
|
+
tokens.push(flag.required === true ? body : `[${body}]`);
|
|
442
|
+
}
|
|
443
|
+
return tokens;
|
|
444
|
+
}
|
|
445
|
+
/** The full invocation shape, e.g. `rulvar run <file|name> [--args JSON] ...`. */
|
|
446
|
+
function invocationOf(grammar) {
|
|
447
|
+
const parts = [
|
|
448
|
+
`rulvar ${grammar.command}`,
|
|
449
|
+
...grammar.positionals,
|
|
450
|
+
...renderFlags(grammar.flags)
|
|
451
|
+
];
|
|
452
|
+
if (grammar.note !== void 0) parts.push(grammar.note);
|
|
453
|
+
return parts.join(" ");
|
|
454
|
+
}
|
|
455
|
+
/** The canonical usage error line for a command. */
|
|
456
|
+
function usageOf(grammar) {
|
|
457
|
+
return `usage: ${invocationOf(grammar)}`;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* The command lines of the help block: every top-level command plus the
|
|
461
|
+
* kb family line, aligned on the flag column. The docs grammar block is
|
|
462
|
+
* the same list verbatim (docs contract test).
|
|
463
|
+
*/
|
|
464
|
+
function helpCommandLines() {
|
|
465
|
+
const top = [
|
|
466
|
+
GRAMMAR.run,
|
|
467
|
+
GRAMMAR.resume,
|
|
468
|
+
GRAMMAR["runs ls"],
|
|
469
|
+
GRAMMAR.inspect,
|
|
470
|
+
GRAMMAR.plan
|
|
471
|
+
];
|
|
472
|
+
const heads = top.map((grammar) => [
|
|
473
|
+
"rulvar",
|
|
474
|
+
grammar.command,
|
|
475
|
+
...grammar.positionals
|
|
476
|
+
].join(" "));
|
|
477
|
+
const kbHead = "rulvar kb <list | inbox | gate | sweep>";
|
|
478
|
+
const width = Math.max(...heads.map((head) => head.length), 39);
|
|
479
|
+
const lines = top.map((grammar, index) => {
|
|
480
|
+
const tail = renderFlags(grammar.flags).join(" ");
|
|
481
|
+
return tail === "" ? heads[index] : `${heads[index].padEnd(width)} ${tail}`;
|
|
482
|
+
});
|
|
483
|
+
lines.push(kbHead);
|
|
484
|
+
return lines;
|
|
485
|
+
}
|
|
486
|
+
function isParseArgsError(error) {
|
|
487
|
+
if (!(error instanceof Error)) return false;
|
|
488
|
+
const code = error.code;
|
|
489
|
+
return typeof code === "string" && code.startsWith("ERR_PARSE_ARGS");
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Parses argv against one grammar entry. Everything the grammar does
|
|
493
|
+
* not name is an error: unknown options (raised by parseArgs), value
|
|
494
|
+
* flags given twice, both members of an exclusive group, and any
|
|
495
|
+
* positional beyond the exact arity. Every rejection names the
|
|
496
|
+
* canonical usage and happens before configs, stores, or adapters load.
|
|
497
|
+
*/
|
|
498
|
+
function parseCommand(grammar, argv) {
|
|
499
|
+
const options = {};
|
|
500
|
+
for (const flag of grammar.flags) options[flag.name] = {
|
|
501
|
+
type: flag.placeholder === void 0 ? "boolean" : "string",
|
|
502
|
+
multiple: true
|
|
503
|
+
};
|
|
504
|
+
let raw;
|
|
505
|
+
try {
|
|
506
|
+
raw = parseArgs({
|
|
507
|
+
args: argv,
|
|
508
|
+
allowPositionals: true,
|
|
509
|
+
options
|
|
510
|
+
});
|
|
511
|
+
} catch (error) {
|
|
512
|
+
if (isParseArgsError(error)) {
|
|
513
|
+
const summary = error.message.split(".")[0];
|
|
514
|
+
throw new ConfigError(`${summary}; ${usageOf(grammar)}`);
|
|
515
|
+
}
|
|
516
|
+
throw error;
|
|
517
|
+
}
|
|
518
|
+
if (raw.positionals.length < grammar.positionals.length) throw new ConfigError(usageOf(grammar));
|
|
519
|
+
if (raw.positionals.length > grammar.positionals.length) {
|
|
520
|
+
const extra = raw.positionals[grammar.positionals.length];
|
|
521
|
+
throw new ConfigError(`unexpected extra argument '${extra}'; ${usageOf(grammar)}`);
|
|
522
|
+
}
|
|
523
|
+
const values = {};
|
|
524
|
+
const groupMembers = /* @__PURE__ */ new Map();
|
|
525
|
+
for (const flag of grammar.flags) {
|
|
526
|
+
const entry = raw.values[flag.name];
|
|
527
|
+
if (entry === void 0) continue;
|
|
528
|
+
if (flag.placeholder !== void 0 && entry.length > 1) throw new ConfigError(`--${flag.name} may appear at most once; ${usageOf(grammar)}`);
|
|
529
|
+
values[flag.name] = flag.placeholder === void 0 ? true : entry[entry.length - 1];
|
|
530
|
+
if (flag.exclusiveGroup !== void 0) {
|
|
531
|
+
const members = groupMembers.get(flag.exclusiveGroup) ?? [];
|
|
532
|
+
members.push(`--${flag.name}`);
|
|
533
|
+
groupMembers.set(flag.exclusiveGroup, members);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
for (const members of groupMembers.values()) if (members.length > 1) throw new ConfigError(`${members.join(" and ")} are mutually exclusive; ${usageOf(grammar)}`);
|
|
537
|
+
return {
|
|
538
|
+
positionals: raw.positionals,
|
|
539
|
+
values
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Validates a `--...-usd` flag value: a finite dollar amount strictly
|
|
544
|
+
* above zero (0, NaN, Infinity, negatives, and non-numbers all fail),
|
|
545
|
+
* checked at parse time, before any provider work.
|
|
546
|
+
*/
|
|
547
|
+
function parseBudgetValue(flagName, value) {
|
|
548
|
+
const budget = Number(value);
|
|
549
|
+
if (!Number.isFinite(budget) || budget <= 0) throw new ConfigError(`--${flagName} must be a positive number, got '${value}'`);
|
|
550
|
+
return budget;
|
|
551
|
+
}
|
|
552
|
+
//#endregion
|
|
305
553
|
//#region src/commands.ts
|
|
306
554
|
/**
|
|
307
555
|
* The four M5 commands of the canonical CLI grammar (no aliases in v1):
|
|
@@ -340,61 +588,34 @@ async function loadCompanion(loading, specifier, command, missingMessage) {
|
|
|
340
588
|
throw new Error(`${command}: ${specifier} is installed but failed to load; the cause below is a defect in the installed package or its dependencies, not a missing install`, { cause: error });
|
|
341
589
|
}
|
|
342
590
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
profile: { type: "string" }
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
const parsed = { positionals };
|
|
355
|
-
if (values.store !== void 0) parsed.store = values.store;
|
|
356
|
-
if (values.profile !== void 0) parsed.profile = values.profile;
|
|
357
|
-
if (values.args !== void 0) parsed.args = values.args;
|
|
358
|
-
if (values["budget-usd"] !== void 0) {
|
|
359
|
-
const budget = Number(values["budget-usd"]);
|
|
360
|
-
if (!Number.isFinite(budget) || budget <= 0) throw new ConfigError(`--budget-usd must be a positive number, got '${values["budget-usd"]}'`);
|
|
361
|
-
parsed.budgetUsd = budget;
|
|
362
|
-
}
|
|
363
|
-
return parsed;
|
|
364
|
-
}
|
|
365
|
-
function parseCommonFlags(argv) {
|
|
366
|
-
const { values, positionals } = parseArgs({
|
|
367
|
-
args: argv,
|
|
368
|
-
allowPositionals: true,
|
|
369
|
-
options: { store: { type: "string" } }
|
|
370
|
-
});
|
|
371
|
-
return {
|
|
372
|
-
positionals,
|
|
373
|
-
...values.store === void 0 ? {} : { store: values.store }
|
|
374
|
-
};
|
|
591
|
+
/** Parses --args JSON into workflow arguments; undefined when absent. */
|
|
592
|
+
function parseArgsJson(raw) {
|
|
593
|
+
if (raw === void 0) return;
|
|
594
|
+
try {
|
|
595
|
+
return JSON.parse(raw);
|
|
596
|
+
} catch {
|
|
597
|
+
throw new ConfigError(`--args is not valid JSON: ${raw}`);
|
|
598
|
+
}
|
|
375
599
|
}
|
|
376
600
|
async function runCommand(argv, context) {
|
|
377
|
-
const
|
|
378
|
-
const target =
|
|
379
|
-
|
|
601
|
+
const parsed = parseCommand(GRAMMAR.run, argv);
|
|
602
|
+
const target = parsed.positionals[0];
|
|
603
|
+
const store = parsed.values.store;
|
|
604
|
+
const profile = parsed.values.profile;
|
|
605
|
+
const budgetUsd = parsed.values["budget-usd"] === void 0 ? void 0 : parseBudgetValue("budget-usd", parsed.values["budget-usd"]);
|
|
606
|
+
const args = parseArgsJson(parsed.values.args);
|
|
380
607
|
const config = await loadCliConfig(context.cwd);
|
|
381
608
|
const module = looksLikeFile(target) ? await loadWorkflowModule(target, context.cwd) : void 0;
|
|
382
609
|
const assembled = assembleEngine({
|
|
383
610
|
config,
|
|
384
611
|
...module === void 0 ? {} : { module },
|
|
385
|
-
...
|
|
386
|
-
...
|
|
612
|
+
...store === void 0 ? {} : { storePath: store },
|
|
613
|
+
...profile === void 0 ? {} : { profile },
|
|
387
614
|
cwd: context.cwd
|
|
388
615
|
});
|
|
389
616
|
const workflow = module?.workflow ?? assembled.workflows[target];
|
|
390
617
|
if (workflow === void 0) throw new ConfigError(looksLikeFile(target) ? `${target} exports no workflow (default export or named 'workflow')` : `no workflow named '${target}' in the registry; register it in rulvar.config.mjs`);
|
|
391
|
-
|
|
392
|
-
if (flags.args !== void 0) try {
|
|
393
|
-
args = JSON.parse(flags.args);
|
|
394
|
-
} catch {
|
|
395
|
-
throw new ConfigError(`--args is not valid JSON: ${flags.args}`);
|
|
396
|
-
}
|
|
397
|
-
const runOptions = { ...flags.budgetUsd === void 0 ? {} : { budgetUsd: flags.budgetUsd } };
|
|
618
|
+
const runOptions = { ...budgetUsd === void 0 ? {} : { budgetUsd } };
|
|
398
619
|
const first = assembled.engine.run(workflow, args, runOptions);
|
|
399
620
|
context.io.err(`runId: ${first.runId}`);
|
|
400
621
|
return reportOutcome(await driveRun({
|
|
@@ -406,18 +627,13 @@ async function runCommand(argv, context) {
|
|
|
406
627
|
}), context.io);
|
|
407
628
|
}
|
|
408
629
|
async function resumeCommand(argv, context) {
|
|
409
|
-
const
|
|
410
|
-
const runId =
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if (flags.args !== void 0) try {
|
|
414
|
-
args = JSON.parse(flags.args);
|
|
415
|
-
} catch {
|
|
416
|
-
throw new ConfigError(`--args is not valid JSON: ${flags.args}`);
|
|
417
|
-
}
|
|
630
|
+
const parsed = parseCommand(GRAMMAR.resume, argv);
|
|
631
|
+
const runId = parsed.positionals[0];
|
|
632
|
+
const args = parseArgsJson(parsed.values.args);
|
|
633
|
+
const store = parsed.values.store;
|
|
418
634
|
const assembled = assembleEngine({
|
|
419
635
|
config: await loadCliConfig(context.cwd),
|
|
420
|
-
...
|
|
636
|
+
...store === void 0 ? {} : { storePath: store },
|
|
421
637
|
cwd: context.cwd
|
|
422
638
|
});
|
|
423
639
|
const meta = (await assembled.store.listRuns()).find((m) => m.runId === runId);
|
|
@@ -435,10 +651,10 @@ async function resumeCommand(argv, context) {
|
|
|
435
651
|
}), context.io);
|
|
436
652
|
}
|
|
437
653
|
async function runsLsCommand(argv, context) {
|
|
438
|
-
const
|
|
654
|
+
const store = parseCommand(GRAMMAR["runs ls"], argv).values.store;
|
|
439
655
|
const metas = await assembleEngine({
|
|
440
656
|
config: await loadCliConfig(context.cwd),
|
|
441
|
-
...
|
|
657
|
+
...store === void 0 ? {} : { storePath: store },
|
|
442
658
|
cwd: context.cwd
|
|
443
659
|
}).store.listRuns();
|
|
444
660
|
if (metas.length === 0) {
|
|
@@ -453,12 +669,12 @@ async function runsLsCommand(argv, context) {
|
|
|
453
669
|
return 0;
|
|
454
670
|
}
|
|
455
671
|
async function inspectCommand(argv, context) {
|
|
456
|
-
const
|
|
457
|
-
const runId =
|
|
458
|
-
|
|
672
|
+
const parsed = parseCommand(GRAMMAR.inspect, argv);
|
|
673
|
+
const runId = parsed.positionals[0];
|
|
674
|
+
const store = parsed.values.store;
|
|
459
675
|
const assembled = assembleEngine({
|
|
460
676
|
config: await loadCliConfig(context.cwd),
|
|
461
|
-
...
|
|
677
|
+
...store === void 0 ? {} : { storePath: store },
|
|
462
678
|
cwd: context.cwd
|
|
463
679
|
});
|
|
464
680
|
const meta = (await assembled.store.listRuns()).find((m) => m.runId === runId);
|
|
@@ -491,34 +707,44 @@ async function inspectCommand(argv, context) {
|
|
|
491
707
|
return 0;
|
|
492
708
|
}
|
|
493
709
|
/**
|
|
494
|
-
* rulvar plan
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
*
|
|
710
|
+
* rulvar plan (M6-T11; grammar in grammar.ts): plans a workflow script
|
|
711
|
+
* through @rulvar/planner (loaded dynamically: the CLI's static
|
|
712
|
+
* dependency stays @rulvar/core only), prints the accepted script and
|
|
713
|
+
* its advisories, and runs it in the worker sandbox unless --dry-run.
|
|
714
|
+
*
|
|
715
|
+
* Both stages are paid runs with their OWN immutable ceilings (the
|
|
716
|
+
* v1.16.2 review P1-1): --planning-budget-usd caps the planning run
|
|
717
|
+
* (PlanOptions.run.budgetUsd, frozen at the planning journal's
|
|
718
|
+
* genesis), --budget-usd caps the execution run (RunOptions.budgetUsd,
|
|
719
|
+
* consistent with rulvar run). A machine-written workflow never runs
|
|
720
|
+
* unbounded silently: missing ceilings fail loudly unless
|
|
721
|
+
* --allow-unbounded waives them explicitly, and an execution ceiling
|
|
722
|
+
* beside --dry-run is a contradiction, not an ignorable leftover.
|
|
499
723
|
*/
|
|
500
724
|
async function planCommand(argv, context) {
|
|
501
|
-
const parsed =
|
|
502
|
-
args: argv,
|
|
503
|
-
allowPositionals: true,
|
|
504
|
-
options: { "dry-run": { type: "boolean" } }
|
|
505
|
-
});
|
|
725
|
+
const parsed = parseCommand(GRAMMAR.plan, argv);
|
|
506
726
|
const goal = parsed.positionals[0];
|
|
507
|
-
|
|
727
|
+
const dryRun = parsed.values["dry-run"] === true;
|
|
728
|
+
const allowUnbounded = parsed.values["allow-unbounded"] === true;
|
|
729
|
+
const planningBudgetUsd = parsed.values["planning-budget-usd"] === void 0 ? void 0 : parseBudgetValue("planning-budget-usd", parsed.values["planning-budget-usd"]);
|
|
730
|
+
const executionBudgetUsd = parsed.values["budget-usd"] === void 0 ? void 0 : parseBudgetValue("budget-usd", parsed.values["budget-usd"]);
|
|
731
|
+
if (dryRun && executionBudgetUsd !== void 0) throw new ConfigError("--dry-run never executes the planned workflow, so --budget-usd (the execution ceiling) has nothing to bound; drop one of the two");
|
|
732
|
+
if (!allowUnbounded && planningBudgetUsd === void 0) throw new ConfigError("rulvar plan runs the planner model as a paid run; set --planning-budget-usd N (its immutable ceiling) or waive it explicitly with --allow-unbounded");
|
|
733
|
+
if (!allowUnbounded && !dryRun && executionBudgetUsd === void 0) throw new ConfigError("executing the planned workflow is a second paid run; set --budget-usd N (its immutable ceiling) or waive it explicitly with --allow-unbounded");
|
|
508
734
|
const plannerModule = await loadCompanion(import("@rulvar/planner"), "@rulvar/planner", "rulvar plan", "rulvar plan requires @rulvar/planner (the plan agent, compileScript, and the worker sandbox live there); install it next to the CLI");
|
|
509
735
|
const assembled = assembleEngine({
|
|
510
736
|
config: await loadCliConfig(context.cwd),
|
|
511
737
|
cwd: context.cwd
|
|
512
738
|
});
|
|
513
|
-
const planned = await plannerModule.plan(assembled.engine, goal);
|
|
739
|
+
const planned = await plannerModule.plan(assembled.engine, goal, planningBudgetUsd === void 0 ? void 0 : { run: { budgetUsd: planningBudgetUsd } });
|
|
514
740
|
context.io.err(`plan: accepted with ${String(planned.lint.length)} advisory diagnostic(s)`);
|
|
515
741
|
for (const diagnostic of planned.lint) context.io.err(` ${diagnostic.ruleId}: ${diagnostic.message}`);
|
|
516
|
-
if (
|
|
742
|
+
if (dryRun) {
|
|
517
743
|
context.io.out(planned.source);
|
|
518
744
|
return 0;
|
|
519
745
|
}
|
|
520
746
|
const workflow = planned.workflow;
|
|
521
|
-
const first = assembled.engine.run(workflow, null);
|
|
747
|
+
const first = assembled.engine.run(workflow, null, executionBudgetUsd === void 0 ? {} : { budgetUsd: executionBudgetUsd });
|
|
522
748
|
context.io.err(`runId: ${first.runId}`);
|
|
523
749
|
return reportOutcome(await driveRun({
|
|
524
750
|
engine: assembled.engine,
|
|
@@ -540,7 +766,8 @@ async function kbCommand(argv, context) {
|
|
|
540
766
|
if (sub === "inbox") return await kbInboxCommand(rest, context);
|
|
541
767
|
if (sub === "gate") return await kbGateCommand(rest, context);
|
|
542
768
|
if (sub === "sweep") return await kbSweepCommand(rest, context);
|
|
543
|
-
if (sub !== "list"
|
|
769
|
+
if (sub !== "list") throw new ConfigError(KB_FAMILY_USAGE);
|
|
770
|
+
parseCommand(GRAMMAR["kb list"], rest);
|
|
544
771
|
const snapshot = await new FileModelKnowledgeStore({ path: join(context.cwd, "rulvar.models.json") }).current();
|
|
545
772
|
context.io.out(`knowledge store: rulvar.models.json (version ${String(snapshot.version)}, ${String(snapshot.claims.length)} claim${snapshot.claims.length === 1 ? "" : "s"})`);
|
|
546
773
|
renderKbList(snapshot, context);
|
|
@@ -574,8 +801,7 @@ function renderKbList(snapshot, context) {
|
|
|
574
801
|
* concrete model names render here VERBATIM, exactly like kb list.
|
|
575
802
|
*/
|
|
576
803
|
async function kbInboxCommand(argv, context) {
|
|
577
|
-
const flags =
|
|
578
|
-
if (flags.positionals.length > 0) throw new ConfigError("usage: rulvar kb inbox [--store PATH]");
|
|
804
|
+
const flags = { store: parseCommand(GRAMMAR["kb inbox"], argv).values.store };
|
|
579
805
|
const plan = await loadCompanion(import("@rulvar/plan"), "@rulvar/plan", "rulvar kb inbox", "rulvar kb inbox requires @rulvar/plan (the RunLedger fold behind the LedgerExport seam)");
|
|
580
806
|
const assembled = assembleEngine({
|
|
581
807
|
config: await loadCliConfig(context.cwd),
|
|
@@ -662,22 +888,11 @@ const RULED_OUT_VOCABULARY = [
|
|
|
662
888
|
* per-project file store, whose git review is the authenticating gate.
|
|
663
889
|
*/
|
|
664
890
|
async function kbGateCommand(argv, context) {
|
|
665
|
-
const
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
approver: { type: "string" },
|
|
671
|
-
"ruled-out": { type: "string" },
|
|
672
|
-
"contrast-run": { type: "string" },
|
|
673
|
-
"contrast-eval": { type: "string" },
|
|
674
|
-
confidence: { type: "string" }
|
|
675
|
-
}
|
|
676
|
-
});
|
|
677
|
-
const usage = "usage: rulvar kb gate <runId> <entryRef> --approver NAME --ruled-out a,b,c [--contrast-run runId#seq | --contrast-eval reportId:caseId[,caseId...]] [--confidence high|medium|low] [--store PATH]";
|
|
678
|
-
const runId = positionals[0];
|
|
679
|
-
const entryRefRaw = positionals[1];
|
|
680
|
-
if (runId === void 0 || entryRefRaw === void 0 || positionals.length > 2) throw new ConfigError(usage);
|
|
891
|
+
const parsed = parseCommand(GRAMMAR["kb gate"], argv);
|
|
892
|
+
const values = parsed.values;
|
|
893
|
+
const usage = usageOf(GRAMMAR["kb gate"]);
|
|
894
|
+
const runId = parsed.positionals[0];
|
|
895
|
+
const entryRefRaw = parsed.positionals[1];
|
|
681
896
|
const entryRef = Number(entryRefRaw);
|
|
682
897
|
if (!Number.isInteger(entryRef) || entryRef < 1) throw new ConfigError(`entryRef must be a positive integer entry seq, got '${entryRefRaw}'`);
|
|
683
898
|
const approver = values.approver;
|
|
@@ -686,7 +901,6 @@ async function kbGateCommand(argv, context) {
|
|
|
686
901
|
if (ruledOutRaw === void 0 || ruledOutRaw === "") throw new ConfigError(`--ruled-out is required: the attribution attestation lists the alternative causes you ruled out (${RULED_OUT_VOCABULARY.join(", ")}). ${usage}`);
|
|
687
902
|
const ruledOut = ruledOutRaw.split(",").map((entry) => entry.trim());
|
|
688
903
|
for (const entry of ruledOut) if (!RULED_OUT_VOCABULARY.includes(entry)) throw new ConfigError(`--ruled-out '${entry}' is not in the attestation vocabulary (${RULED_OUT_VOCABULARY.join(", ")})`);
|
|
689
|
-
if (values["contrast-run"] !== void 0 && values["contrast-eval"] !== void 0) throw new ConfigError("--contrast-run and --contrast-eval are mutually exclusive");
|
|
690
904
|
let contrastEvidence;
|
|
691
905
|
if (values["contrast-run"] !== void 0) {
|
|
692
906
|
const [contrastRun, seqRaw, ...tail] = values["contrast-run"].split("#");
|
|
@@ -804,10 +1018,21 @@ async function kbGateCommand(argv, context) {
|
|
|
804
1018
|
* sweep re-measures.
|
|
805
1019
|
*/
|
|
806
1020
|
async function kbSweepCommand(argv, context) {
|
|
807
|
-
|
|
1021
|
+
parseCommand(GRAMMAR["kb sweep"], argv);
|
|
808
1022
|
const config = await loadCliConfig(context.cwd);
|
|
809
1023
|
const sweep = config.kbSweep;
|
|
810
1024
|
if (sweep === void 0) throw new ConfigError("rulvar kb sweep requires a kbSweep section in rulvar.config.mjs ({ committerId, models, cases })");
|
|
1025
|
+
const budgets = sweep.budgets;
|
|
1026
|
+
if (budgets === void 0 && sweep.allowUnbounded !== true) throw new ConfigError("rulvar kb sweep runs paid target, judge, and canary runs; set kbSweep.budgets ({ targetUsd, judgeUsd, canaryUsd, maxTotalUsd }) so every run carries an immutable ceiling and the whole sweep stays under maxTotalUsd, or waive the ceilings explicitly with kbSweep.allowUnbounded: true");
|
|
1027
|
+
if (budgets !== void 0) for (const field of [
|
|
1028
|
+
"targetUsd",
|
|
1029
|
+
"judgeUsd",
|
|
1030
|
+
"canaryUsd",
|
|
1031
|
+
"maxTotalUsd"
|
|
1032
|
+
]) {
|
|
1033
|
+
const value = budgets[field];
|
|
1034
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw new ConfigError(`kbSweep.budgets.${field} must be a positive finite number, got ${String(value)}`);
|
|
1035
|
+
}
|
|
811
1036
|
const evals = await loadCompanion(import("@rulvar/evals"), "@rulvar/evals", "rulvar kb sweep", "rulvar kb sweep requires @rulvar/evals (matrix sweeps, the eval-committer identity, and the canary live there); install it next to the CLI");
|
|
812
1037
|
const store = new FileModelKnowledgeStore({ path: join(context.cwd, "rulvar.models.json") });
|
|
813
1038
|
const snapshot = await store.current();
|
|
@@ -849,15 +1074,40 @@ async function kbSweepCommand(argv, context) {
|
|
|
849
1074
|
}
|
|
850
1075
|
}
|
|
851
1076
|
}));
|
|
1077
|
+
const usd = (amount) => `$${String(Math.round(amount * 1e6) / 1e6)}`;
|
|
1078
|
+
let envelope;
|
|
1079
|
+
if (budgets !== void 0) {
|
|
1080
|
+
envelope = new evals.SpendEnvelope(budgets.maxTotalUsd);
|
|
1081
|
+
const canaryRuns = (sweep.canary?.prompts.length ?? 0) * pool.size;
|
|
1082
|
+
const targetRuns = sweep.cases.length * pool.size;
|
|
1083
|
+
context.io.out(`sweep budget: ${usd(budgets.maxTotalUsd)} maxTotalUsd hard ceiling; authorizes up to ${usd(canaryRuns * budgets.canaryUsd + targetRuns * budgets.targetUsd)} for ${String(canaryRuns)} canary + ${String(targetRuns)} target run(s) before judges (each judge run up to ${usd(budgets.judgeUsd)} draws from the same envelope at grade time)`);
|
|
1084
|
+
} else context.io.err("kb sweep: running UNBOUNDED (kbSweep.allowUnbounded); no target, judge, or canary run carries a ceiling");
|
|
852
1085
|
for (const { member, origin } of pool.values()) {
|
|
853
1086
|
const effort = member.effort === void 0 ? "" : ` effort=${member.effort}`;
|
|
854
1087
|
context.io.out(`pool: ${member.model}${effort} [${origin}]`);
|
|
855
1088
|
}
|
|
856
1089
|
if (sweep.canary !== void 0) for (const { member } of pool.values()) {
|
|
857
1090
|
const engine = await engineFor(member);
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
1091
|
+
let canary;
|
|
1092
|
+
try {
|
|
1093
|
+
canary = await evals.runCanary(engine, sweep.canary, { ...budgets === void 0 ? {} : {
|
|
1094
|
+
budgetUsd: budgets.canaryUsd,
|
|
1095
|
+
envelope
|
|
1096
|
+
} });
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
if (error instanceof Error && error.name === "SweepBudgetError") {
|
|
1099
|
+
context.io.out(`canary ${member.model}: envelope exhausted, skipped`);
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
throw error;
|
|
1103
|
+
}
|
|
1104
|
+
if (!canary.allOk) {
|
|
1105
|
+
const refused = canary.probes.filter((probe) => probe.status === "refused").length;
|
|
1106
|
+
context.io.out(`canary ${member.model}: ${canary.fingerprint.slice(0, 12)}... incomplete ` + (refused > 0 ? `(${String(refused)} probe(s) refused by the envelope); NOT flipping claims` : "(a probe did not settle ok); NOT flipping claims"));
|
|
1107
|
+
continue;
|
|
1108
|
+
}
|
|
1109
|
+
const drift = await evals.flipStaleOnCanaryDrift(store, member.model, canary.fingerprint);
|
|
1110
|
+
context.io.out(`canary ${member.model}: ${canary.fingerprint.slice(0, 12)}...` + (drift.flipped.length === 0 ? " no drift" : ` DRIFT, ${String(drift.flipped.length)} claim(s) flipped stale`));
|
|
861
1111
|
}
|
|
862
1112
|
const report = await evals.runSweepMatrix({
|
|
863
1113
|
models: [...pool.values()].map((entry) => entry.member),
|
|
@@ -868,11 +1118,31 @@ async function kbSweepCommand(argv, context) {
|
|
|
868
1118
|
observedAt,
|
|
869
1119
|
engineFor,
|
|
870
1120
|
store,
|
|
871
|
-
...sweep.thresholds === void 0 ? {} : { thresholds: sweep.thresholds }
|
|
1121
|
+
...sweep.thresholds === void 0 ? {} : { thresholds: sweep.thresholds },
|
|
1122
|
+
...budgets === void 0 ? {} : {
|
|
1123
|
+
suite: {
|
|
1124
|
+
budgetUsd: budgets.targetUsd,
|
|
1125
|
+
judgeBudgetUsd: budgets.judgeUsd
|
|
1126
|
+
},
|
|
1127
|
+
envelope
|
|
1128
|
+
}
|
|
872
1129
|
});
|
|
873
|
-
for (const cell of report.cells)
|
|
1130
|
+
for (const cell of report.cells) {
|
|
1131
|
+
if (cell.envelopeExhausted === true && cell.n === 0) {
|
|
1132
|
+
context.io.out(`cell ${cell.model} :: ${cell.taskClass}: envelope exhausted, not measured (no claim)`);
|
|
1133
|
+
continue;
|
|
1134
|
+
}
|
|
1135
|
+
const notes = [];
|
|
1136
|
+
if (cell.envelopeExhausted === true) notes.push(`INCOMPLETE: envelope refused ${cell.refusedRunLabel ?? "a run"} after ${String(cell.n)} of ${String(cell.plannedN)} case(s)`);
|
|
1137
|
+
else if (cell.n < cell.plannedN) notes.push(`INCOMPLETE: ${String(cell.n)} of ${String(cell.plannedN)} case(s) measured`);
|
|
1138
|
+
if (cell.exhaustedRuns !== void 0) notes.push(`${String(cell.exhaustedRuns)} run(s) hit their own ceiling`);
|
|
1139
|
+
if (cell.judgeIncompleteRuns !== void 0) notes.push(`${String(cell.judgeIncompleteRuns)} case(s) kept as evidence with an unfinished judge (${cell.incompleteReason ?? "judge budget"})`);
|
|
1140
|
+
const suffix = notes.length === 0 ? "" : ` (${notes.join("; ")}; no claim)`;
|
|
1141
|
+
context.io.out(`cell ${cell.model} :: ${cell.taskClass}: passRate ${cell.passRate.toFixed(2)} over ${String(cell.n)} case${cell.n === 1 ? "" : "s"}${suffix}`);
|
|
1142
|
+
}
|
|
874
1143
|
for (const claim of report.claims) context.io.out(`claim ${claim.id}: ${claim.taskClass} ${claim.polarity}`);
|
|
875
1144
|
context.io.out(report.committedVersion === void 0 ? "no claims crossed a threshold; nothing committed" : `committed ${String(report.claims.length)} claim(s) as store version ${String(report.committedVersion)} (report ${report.reportId})`);
|
|
1145
|
+
if (envelope !== void 0) context.io.out(`sweep budget: authorized ${usd(envelope.authorizedUsd)} of ${usd(envelope.maxTotalUsd)}`);
|
|
876
1146
|
return 0;
|
|
877
1147
|
}
|
|
878
1148
|
//#endregion
|
|
@@ -883,12 +1153,7 @@ async function kbSweepCommand(argv, context) {
|
|
|
883
1153
|
*/
|
|
884
1154
|
const HELP = `rulvar: durable multi-agent workflows (https://docs.rulvar.com)
|
|
885
1155
|
|
|
886
|
-
|
|
887
|
-
rulvar resume <runId> [--store PATH]
|
|
888
|
-
rulvar runs ls [--store PATH]
|
|
889
|
-
rulvar inspect <runId> [--store PATH]
|
|
890
|
-
rulvar plan "<goal>" [--dry-run]
|
|
891
|
-
rulvar kb <list | inbox | gate | sweep>
|
|
1156
|
+
${helpCommandLines().map((line) => ` ${line}`).join("\n")}
|
|
892
1157
|
|
|
893
1158
|
Engine assembly: adapters, defaults, and the workflow registry come from
|
|
894
1159
|
rulvar.config.mjs in the working directory (default export
|
|
@@ -896,13 +1161,18 @@ rulvar.config.mjs in the working directory (default export
|
|
|
896
1161
|
exports. --store selects the JsonlFileStore directory (default .rulvar).
|
|
897
1162
|
plan asks the planner model (role plan) to write a workflow script,
|
|
898
1163
|
lints and self-repairs it, then runs it in the worker sandbox; --dry-run
|
|
899
|
-
prints the accepted script without running.
|
|
900
|
-
|
|
1164
|
+
prints the accepted script without running. Both stages are paid runs
|
|
1165
|
+
with their own immutable ceilings: --planning-budget-usd caps the
|
|
1166
|
+
planning run, --budget-usd caps the execution run, and a missing
|
|
1167
|
+
ceiling fails loudly unless --allow-unbounded waives it explicitly.
|
|
1168
|
+
Requires @rulvar/planner installed. kb list shows the per-project claim store
|
|
901
1169
|
(./rulvar.models.json) with full provenance. kb sweep runs the
|
|
902
1170
|
falsification matrix from the kbSweep section of rulvar.config.mjs
|
|
903
1171
|
(fixed pool UNIONED with every model carrying an active negative claim
|
|
904
1172
|
plus the re-measure queue; optional canary probes flip drifted claims
|
|
905
|
-
stale first;
|
|
1173
|
+
stale first; kbSweep.budgets sets per-run ceilings plus the maxTotalUsd
|
|
1174
|
+
envelope, required unless allowUnbounded waives it; requires
|
|
1175
|
+
@rulvar/evals installed). kb inbox aggregates
|
|
906
1176
|
kb_propose proposals from finished runs (14 day TTL); kb gate turns one
|
|
907
1177
|
inbox proposal into a committed claim behind a human attestation
|
|
908
1178
|
(--approver and --ruled-out are mandatory). inbox and gate require
|
|
@@ -919,7 +1189,7 @@ async function runCli(argv, options) {
|
|
|
919
1189
|
case "resume": return await resumeCommand(rest, context);
|
|
920
1190
|
case "runs": {
|
|
921
1191
|
const [sub, ...subRest] = rest;
|
|
922
|
-
if (sub !== "ls") throw new ConfigError("
|
|
1192
|
+
if (sub !== "ls") throw new ConfigError(usageOf(GRAMMAR["runs ls"]));
|
|
923
1193
|
return await runsLsCommand(subRest, context);
|
|
924
1194
|
}
|
|
925
1195
|
case "inspect": return await inspectCommand(rest, context);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "Rulvar shell: run/resume/runs/inspect/plan/kb commands, TUI progress, createServer, createWorker, OTel exporter.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -22,17 +22,17 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@rulvar/core": "1.
|
|
25
|
+
"@rulvar/core": "1.18.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.20.0",
|
|
29
29
|
"tsdown": "^0.22.3",
|
|
30
30
|
"typescript": "~6.0.3",
|
|
31
|
-
"@rulvar/
|
|
32
|
-
"@rulvar/
|
|
33
|
-
"@rulvar/store-sqlite": "1.
|
|
34
|
-
"@rulvar/
|
|
35
|
-
"@rulvar/
|
|
31
|
+
"@rulvar/planner": "1.18.0",
|
|
32
|
+
"@rulvar/plan": "1.18.0",
|
|
33
|
+
"@rulvar/store-sqlite": "1.18.0",
|
|
34
|
+
"@rulvar/evals": "1.18.0",
|
|
35
|
+
"@rulvar/testing": "1.18.0"
|
|
36
36
|
},
|
|
37
37
|
"bin": {
|
|
38
38
|
"rulvar": "./dist/cli.js"
|