@rulvar/cli 1.60.0 → 1.62.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 +40 -2
- package/dist/index.js +2 -2
- package/dist/{io-DzlCfHd9.js → io-DQqS6oAt.js} +186 -4
- package/package.json +7 -7
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CreateEngineOptions, Engine, JournalStore, KeyDeriver, LeasableStore, ModelRef, RunHandle, RunMeta, RunOutcome, Usage, Workflow, WorkflowEvent, WorkflowRegistry } from "@rulvar/core";
|
|
1
|
+
import { CreateEngineOptions, Engine, JournalStore, KeyDeriver, LeasableStore, ModelRef, PreflightInput, RunHandle, RunMeta, RunOutcome, Usage, Workflow, WorkflowEvent, WorkflowRegistry } from "@rulvar/core";
|
|
2
2
|
|
|
3
3
|
//#region src/io.d.ts
|
|
4
4
|
interface CliIo {
|
|
@@ -31,12 +31,49 @@ declare function runCommand(argv: string[], context: CommandContext): Promise<nu
|
|
|
31
31
|
declare function resumeCommand(argv: string[], context: CommandContext): Promise<number>;
|
|
32
32
|
declare function runsLsCommand(argv: string[], context: CommandContext): Promise<number>;
|
|
33
33
|
declare function inspectCommand(argv: string[], context: CommandContext): Promise<number>;
|
|
34
|
+
/**
|
|
35
|
+
* rulvar invoice (P1.3): the per-dispatch reconciliation export from
|
|
36
|
+
* the journal's providerCalls ledger, one row per billable provider
|
|
37
|
+
* call with the provider's response id when the adapter surfaced one,
|
|
38
|
+
* plus the gross/net ledger totals (`totalUsd` here is the GROSS
|
|
39
|
+
* figure: abandoned subtrees included, exactly what a provider invoice
|
|
40
|
+
* bills). --json prints the machine-readable InvoiceExport; the text
|
|
41
|
+
* form prints one line per row. Pricing folds at read time from the
|
|
42
|
+
* assembled price table, the same numbers rulvar inspect reports.
|
|
43
|
+
*/
|
|
44
|
+
declare function invoiceCommand(argv: string[], context: CommandContext): Promise<number>;
|
|
45
|
+
/**
|
|
46
|
+
* rulvar preflight (the experiment-review P2.2; grammar in grammar.ts):
|
|
47
|
+
* the effective-config linter and dry-run estimator. Loads the SAME
|
|
48
|
+
* config, module, and run-profile merge `rulvar run` would assemble,
|
|
49
|
+
* but constructs no engine, opens no store, and dispatches nothing:
|
|
50
|
+
* the report is computed by preflightEstimate over options alone, so
|
|
51
|
+
* the command cannot pay for a single provider token by construction.
|
|
52
|
+
* The declared spawn wave comes from the `preflight` export of the
|
|
53
|
+
* config or workflow module (module wins), and --spawns JSON overrides
|
|
54
|
+
* it from the command line. --json prints the machine-readable report.
|
|
55
|
+
* Exit 1 when any finding has severity 'error' (the linter contract:
|
|
56
|
+
* green preflight means the run can at least start), 0 otherwise.
|
|
57
|
+
*/
|
|
58
|
+
declare function preflightCommand(argv: string[], context: CommandContext): Promise<number>;
|
|
34
59
|
//#endregion
|
|
35
60
|
//#region src/config.d.ts
|
|
61
|
+
/**
|
|
62
|
+
* The preflight declaration a config or workflow module may export
|
|
63
|
+
* (the experiment-review P2.2): the declared spawn wave, the
|
|
64
|
+
* orchestrator spec, and the quota rule set behind the configured
|
|
65
|
+
* limiter, exactly the PreflightInput slices the estimator cannot
|
|
66
|
+
* derive from engineOptions alone. `rulvar preflight` merges the
|
|
67
|
+
* workflow module's declaration over the config file's, and --spawns
|
|
68
|
+
* overrides the spawn wave from the command line.
|
|
69
|
+
*/
|
|
70
|
+
type PreflightDeclaration = Pick<PreflightInput, "spawns" | "orchestrator" | "quotaRules">;
|
|
36
71
|
/** The shape both the config module and a workflow module may export. */
|
|
37
72
|
interface CliConfig {
|
|
38
73
|
engineOptions?: Partial<CreateEngineOptions>;
|
|
39
74
|
workflows?: WorkflowRegistry;
|
|
75
|
+
/** rulvar preflight declaration (P2.2). */
|
|
76
|
+
preflight?: PreflightDeclaration;
|
|
40
77
|
/** rulvar kb sweep configuration (M11-T05). */
|
|
41
78
|
kbSweep?: KbSweepCliConfig;
|
|
42
79
|
}
|
|
@@ -107,6 +144,7 @@ interface LoadedWorkflowModule {
|
|
|
107
144
|
workflow?: Workflow<never, unknown>;
|
|
108
145
|
engineOptions?: Partial<CreateEngineOptions>;
|
|
109
146
|
workflows?: WorkflowRegistry;
|
|
147
|
+
preflight?: PreflightDeclaration;
|
|
110
148
|
}
|
|
111
149
|
/** Imports a workflow module given on the command line. */
|
|
112
150
|
declare function loadWorkflowModule(file: string, cwd: string): Promise<LoadedWorkflowModule>;
|
|
@@ -375,4 +413,4 @@ declare function toOtel(run: {
|
|
|
375
413
|
result: Promise<RunOutcome<unknown>>;
|
|
376
414
|
}, tracer: TracerLike, options?: ToOtelOptions): Promise<number>;
|
|
377
415
|
//#endregion
|
|
378
|
-
export { type AssembledCli, type CliConfig, type CliIo, type CommandContext, type CreateServerOptions, type CreateWorkerOptions, DEFAULT_MAX_PENDING_EVENTS_PER_CLIENT, DEFAULT_STORE_DIR, DEFAULT_WORKER_TTL_MS, HELP, type KbSweepCliConfig, type LoadedWorkflowModule, type OtelContextApi, type RulvarServer, type SpanLike, type ToOtelOptions, type TracerLike, type Worker, assembleEngine, attachProgress, createServer, createWorker, driveRun, inspectCommand, loadCliConfig, loadWorkflowModule, looksLikeFile, processIo, renderEventLine, reportOutcome, resumeCommand, runCli, runCommand, runsLsCommand, strictExitCode, toOtel };
|
|
416
|
+
export { type AssembledCli, type CliConfig, type CliIo, type CommandContext, type CreateServerOptions, type CreateWorkerOptions, DEFAULT_MAX_PENDING_EVENTS_PER_CLIENT, DEFAULT_STORE_DIR, DEFAULT_WORKER_TTL_MS, HELP, type KbSweepCliConfig, type LoadedWorkflowModule, type OtelContextApi, type PreflightDeclaration, type RulvarServer, type SpanLike, type ToOtelOptions, type TracerLike, type Worker, assembleEngine, attachProgress, createServer, createWorker, driveRun, inspectCommand, invoiceCommand, loadCliConfig, loadWorkflowModule, looksLikeFile, preflightCommand, processIo, renderEventLine, reportOutcome, resumeCommand, runCli, runCommand, runsLsCommand, strictExitCode, toOtel };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as
|
|
1
|
+
import { _ as loadCliConfig, a as invoiceCommand, c as runCommand, d as reportOutcome, f as strictExitCode, g as assembleEngine, h as DEFAULT_STORE_DIR, i as inspectCommand, l as runsLsCommand, m as renderEventLine, n as HELP, o as preflightCommand, p as attachProgress, r as runCli, s as resumeCommand, t as processIo, u as driveRun, v as loadWorkflowModule, y as looksLikeFile } from "./io-DQqS6oAt.js";
|
|
2
2
|
import { ConfigError, InvalidResolutionError, JournalCompatibilityError, LeaseHeldError, Replayer, RulvarError, buildDeriverRegistry, compileSecretMasker, costReportFromJournal, maskSecrets, normalizeEntry, readRunMeta, scanJournalCompatibility, validateSchemaSpec } from "@rulvar/core";
|
|
3
3
|
//#region src/server.ts
|
|
4
4
|
/**
|
|
@@ -961,4 +961,4 @@ async function toOtel(run, tracer, options = {}) {
|
|
|
961
961
|
return created;
|
|
962
962
|
}
|
|
963
963
|
//#endregion
|
|
964
|
-
export { DEFAULT_MAX_PENDING_EVENTS_PER_CLIENT, DEFAULT_STORE_DIR, DEFAULT_WORKER_TTL_MS, HELP, assembleEngine, attachProgress, createServer, createWorker, driveRun, inspectCommand, loadCliConfig, loadWorkflowModule, looksLikeFile, processIo, renderEventLine, reportOutcome, resumeCommand, runCli, runCommand, runsLsCommand, strictExitCode, toOtel };
|
|
964
|
+
export { DEFAULT_MAX_PENDING_EVENTS_PER_CLIENT, DEFAULT_STORE_DIR, DEFAULT_WORKER_TTL_MS, HELP, assembleEngine, attachProgress, createServer, createWorker, driveRun, inspectCommand, invoiceCommand, loadCliConfig, loadWorkflowModule, looksLikeFile, preflightCommand, processIo, renderEventLine, reportOutcome, resumeCommand, runCli, runCommand, runsLsCommand, strictExitCode, toOtel };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConfigError, FileModelKnowledgeStore, INBOX_PROPOSAL_TTL_DAYS, JsonlFileStore, LeaseHeldError, auditRuns, claimExpired, claimExpiry, compilePermissionPreset, costReportFromJournal, createEngine, hashRunArgs, hashRunOutput, lastRunSettle, parseModelRef, priceUsdOf, proposalStatement, readRunMeta, reconcileRunMeta, remeasureQueue, resolvePricing, runProfile, sanitizeTerminalText } from "@rulvar/core";
|
|
1
|
+
import { ConfigError, FileModelKnowledgeStore, INBOX_PROPOSAL_TTL_DAYS, JsonlFileStore, LeaseHeldError, auditRuns, claimExpired, claimExpiry, compilePermissionPreset, costReportFromJournal, createEngine, hashRunArgs, hashRunOutput, invoiceFromJournal, lastRunSettle, parseModelRef, preflightEstimate, priceUsdOf, proposalStatement, readRunMeta, reconcileRunMeta, remeasureQueue, resolvePricing, runProfile, sanitizeTerminalText } from "@rulvar/core";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { existsSync, statSync } from "node:fs";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
@@ -43,7 +43,8 @@ async function loadCliConfig(cwd) {
|
|
|
43
43
|
return {
|
|
44
44
|
...config ?? {},
|
|
45
45
|
...mod.engineOptions === void 0 ? {} : { engineOptions: mod.engineOptions },
|
|
46
|
-
...mod.workflows === void 0 ? {} : { workflows: mod.workflows }
|
|
46
|
+
...mod.workflows === void 0 ? {} : { workflows: mod.workflows },
|
|
47
|
+
...mod.preflight === void 0 ? {} : { preflight: mod.preflight }
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
50
|
return {};
|
|
@@ -58,6 +59,7 @@ async function loadWorkflowModule(file, cwd) {
|
|
|
58
59
|
if (candidate !== void 0 && isWorkflowValue(candidate)) loaded.workflow = candidate;
|
|
59
60
|
if (mod.engineOptions !== void 0) loaded.engineOptions = mod.engineOptions;
|
|
60
61
|
if (mod.workflows !== void 0) loaded.workflows = mod.workflows;
|
|
62
|
+
if (mod.preflight !== void 0) loaded.preflight = mod.preflight;
|
|
61
63
|
return loaded;
|
|
62
64
|
}
|
|
63
65
|
/** True when the `run` target names a file rather than a registry entry. */
|
|
@@ -474,6 +476,11 @@ const GRAMMAR = {
|
|
|
474
476
|
positionals: ["<runId>"],
|
|
475
477
|
flags: [STORE]
|
|
476
478
|
},
|
|
479
|
+
invoice: {
|
|
480
|
+
command: "invoice",
|
|
481
|
+
positionals: ["<runId>"],
|
|
482
|
+
flags: [STORE, { name: "json" }]
|
|
483
|
+
},
|
|
477
484
|
plan: {
|
|
478
485
|
command: "plan",
|
|
479
486
|
positionals: ["\"<goal>\""],
|
|
@@ -490,6 +497,25 @@ const GRAMMAR = {
|
|
|
490
497
|
{ name: "dry-run" }
|
|
491
498
|
]
|
|
492
499
|
},
|
|
500
|
+
preflight: {
|
|
501
|
+
command: "preflight",
|
|
502
|
+
positionals: ["<file|name>"],
|
|
503
|
+
flags: [
|
|
504
|
+
{
|
|
505
|
+
name: "budget-usd",
|
|
506
|
+
placeholder: "N"
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
name: "profile",
|
|
510
|
+
placeholder: "NAME"
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
name: "spawns",
|
|
514
|
+
placeholder: "JSON"
|
|
515
|
+
},
|
|
516
|
+
{ name: "json" }
|
|
517
|
+
]
|
|
518
|
+
},
|
|
493
519
|
"kb list": {
|
|
494
520
|
command: "kb list",
|
|
495
521
|
positionals: [],
|
|
@@ -585,7 +611,9 @@ function helpCommandLines() {
|
|
|
585
611
|
GRAMMAR["runs ls"],
|
|
586
612
|
GRAMMAR["runs audit"],
|
|
587
613
|
GRAMMAR.inspect,
|
|
588
|
-
GRAMMAR.
|
|
614
|
+
GRAMMAR.invoice,
|
|
615
|
+
GRAMMAR.plan,
|
|
616
|
+
GRAMMAR.preflight
|
|
589
617
|
];
|
|
590
618
|
const heads = top.map((grammar) => [
|
|
591
619
|
"rulvar",
|
|
@@ -1074,6 +1102,7 @@ async function inspectCommand(argv, context) {
|
|
|
1074
1102
|
context.io.out(`open suspensions: ${openSuspensions}`);
|
|
1075
1103
|
const cost = costReportFromJournal(entries, assembled.priceUsd);
|
|
1076
1104
|
context.io.out(`cost: $${cost.totalUsd.toFixed(4)}`);
|
|
1105
|
+
if (cost.abandoned.usd > 0) context.io.out(`gross: $${cost.grossUsd.toFixed(4)} (abandoned: $${cost.abandoned.usd.toFixed(4)}; see rulvar invoice)`);
|
|
1077
1106
|
for (const [model, usd] of Object.entries(cost.byModel)) context.io.out(` ${model}: $${usd.toFixed(4)}`);
|
|
1078
1107
|
for (const item of cost.unpriced) context.io.out(` unpriced: ${item.model} (${item.usage.inputTokens + item.usage.outputTokens} tok)`);
|
|
1079
1108
|
for (const entry of entries) {
|
|
@@ -1084,6 +1113,142 @@ async function inspectCommand(argv, context) {
|
|
|
1084
1113
|
return 0;
|
|
1085
1114
|
}
|
|
1086
1115
|
/**
|
|
1116
|
+
* rulvar invoice (P1.3): the per-dispatch reconciliation export from
|
|
1117
|
+
* the journal's providerCalls ledger, one row per billable provider
|
|
1118
|
+
* call with the provider's response id when the adapter surfaced one,
|
|
1119
|
+
* plus the gross/net ledger totals (`totalUsd` here is the GROSS
|
|
1120
|
+
* figure: abandoned subtrees included, exactly what a provider invoice
|
|
1121
|
+
* bills). --json prints the machine-readable InvoiceExport; the text
|
|
1122
|
+
* form prints one line per row. Pricing folds at read time from the
|
|
1123
|
+
* assembled price table, the same numbers rulvar inspect reports.
|
|
1124
|
+
*/
|
|
1125
|
+
async function invoiceCommand(argv, context) {
|
|
1126
|
+
const parsed = parseCommand(GRAMMAR.invoice, argv);
|
|
1127
|
+
const runId = parsed.positionals[0];
|
|
1128
|
+
const store = parsed.values.store;
|
|
1129
|
+
const json = parsed.values.json === true;
|
|
1130
|
+
const assembled = assembleEngine({
|
|
1131
|
+
config: await loadCliConfig(context.cwd),
|
|
1132
|
+
...store === void 0 ? {} : { storePath: store },
|
|
1133
|
+
cwd: context.cwd
|
|
1134
|
+
});
|
|
1135
|
+
const meta = await readRunMeta(assembled.store, runId);
|
|
1136
|
+
if (meta === void 0) throw new ConfigError(`run '${runId}' not found in the store`);
|
|
1137
|
+
const invoice = invoiceFromJournal(await assembled.store.load(runId), assembled.priceUsd);
|
|
1138
|
+
if (json) {
|
|
1139
|
+
context.io.out(JSON.stringify(invoice, null, 2));
|
|
1140
|
+
return 0;
|
|
1141
|
+
}
|
|
1142
|
+
context.io.out(`run ${meta.runId}: invoice (${meta.status})`);
|
|
1143
|
+
context.io.out(`gross: $${invoice.totalUsd.toFixed(4)} | net: $${invoice.netUsd.toFixed(4)} | abandoned: $${invoice.abandonedUsd.toFixed(4)}${invoice.usageApprox === true ? " (approx)" : ""}`);
|
|
1144
|
+
context.io.out(`rows: ${invoice.rows.length} (reconciliation failures: ${invoice.reconciliationFailures})`);
|
|
1145
|
+
for (const row of invoice.rows) {
|
|
1146
|
+
const usd = row.usd === void 0 ? "unpriced" : `$${row.usd.toFixed(4)}`;
|
|
1147
|
+
const tokens = row.usage.inputTokens + row.usage.outputTokens;
|
|
1148
|
+
const id = row.responseId ?? "no-id";
|
|
1149
|
+
const flags = `${row.usageApprox === true ? " approx" : ""}${row.abandoned === true ? " abandoned" : ""}`;
|
|
1150
|
+
context.io.out(`#${row.entrySeq}.${row.ordinal} ${row.servedBy}${row.role === void 0 ? "" : ` ${row.role}`}${row.attempt === void 0 ? "" : ` attempt=${row.attempt}`} ${row.outcome} ${id} ${tokens} tok ${usd}${flags} [${row.reconciliation}]`);
|
|
1151
|
+
}
|
|
1152
|
+
for (const item of invoice.unpriced) context.io.out(`unpriced: ${item.model} (${item.usage.inputTokens + item.usage.outputTokens} tok)`);
|
|
1153
|
+
return 0;
|
|
1154
|
+
}
|
|
1155
|
+
/** Formats an optional USD number for the preflight text rows. */
|
|
1156
|
+
function usdOf(value) {
|
|
1157
|
+
return value === void 0 ? "n/a" : `$${value.toFixed(4)}`;
|
|
1158
|
+
}
|
|
1159
|
+
function renderPreflight(report, io) {
|
|
1160
|
+
io.out("preflight: effective limits and admission projection (zero provider dispatches)");
|
|
1161
|
+
const perProvider = report.concurrency.perProvider;
|
|
1162
|
+
io.out(`concurrency: perRun=${report.concurrency.perRun}` + (perProvider === void 0 ? "" : ` perProvider={${Object.entries(perProvider).map(([id, cap]) => `${id}:${cap}`).join(", ")}}`));
|
|
1163
|
+
const budget = report.budget;
|
|
1164
|
+
io.out(`budget: ceiling=${usdOf(budget.ceilingUsd)} flatReserve=${usdOf(budget.flatReserveUsd)} lifetimeSpawnCap=${budget.lifetimeSpawnCap} childFraction=${budget.childBudgetFraction} maxDepth=${budget.maxDepth}`);
|
|
1165
|
+
if (budget.orchestrator !== void 0) {
|
|
1166
|
+
const orch = budget.orchestrator;
|
|
1167
|
+
io.out(`orchestrator: effectiveCap=${usdOf(orch.effectiveCapUsd)} finalizeReserve=${usdOf(orch.finalizeReserveUsd)} over ${orch.finalizeTurns} turns (${orch.reserveCommitted ? "committed against the run root" : "not committed: no plan extension"})`);
|
|
1168
|
+
}
|
|
1169
|
+
io.out(`quota: ${report.quota.configured ? "configured" : "none"}` + (report.quota.tenant === void 0 ? "" : ` tenant=${report.quota.tenant}`) + (report.quota.rules === void 0 ? "" : ` rules=${report.quota.rules}`));
|
|
1170
|
+
io.out(`run limits: ${JSON.stringify(report.runLimits)}`);
|
|
1171
|
+
for (const spawn of report.spawns) {
|
|
1172
|
+
io.out(`spawn '${spawn.label}' role=${spawn.role} x${spawn.count} servedBy=${spawn.servedBy ?? "UNROUTED"}${spawn.unpriced === true ? " (unpriced)" : ""} reserve=${usdOf(spawn.admissionReserveUsd)} (${spawn.reserveSource})` + (spawn.maxOutputTokensPerTurn === void 0 ? "" : ` maxOutput=${spawn.maxOutputTokensPerTurn}`) + (spawn.turnFloorUsd === void 0 ? "" : ` turnFloor=${usdOf(spawn.turnFloorUsd)}`) + ` toolCallCeiling=${spawn.executedToolCallCeiling ?? "unlimited"}`);
|
|
1173
|
+
io.out(` limits: ${JSON.stringify(spawn.limits)}`);
|
|
1174
|
+
for (const row of spawn.toolCeilings) {
|
|
1175
|
+
if (row.tool === "(any)" && row.ceiling === null) continue;
|
|
1176
|
+
io.out(` tool ${row.tool}: ceiling=${row.ceiling ?? "unlimited"}` + (row.boundBy === void 0 ? "" : ` (${row.boundBy})`));
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
const admission = report.admission;
|
|
1180
|
+
if (admission.wave.length > 0) {
|
|
1181
|
+
io.out(`admission: ${admission.admitted} of ${admission.wave.length} admitted` + (admission.ceilingUsd === void 0 ? "" : ` under ceiling=${usdOf(admission.ceilingUsd)}`) + (admission.reservedForFinalizationUsd === 0 ? "" : ` (finalization holds ${usdOf(admission.reservedForFinalizationUsd)})`));
|
|
1182
|
+
for (const row of admission.wave) io.out(` ${row.admitted ? "admit" : "DENY "} ${row.label} reserve=${usdOf(row.reserveUsd)}` + (row.deniedBy === void 0 ? "" : ` [${row.deniedBy}]`));
|
|
1183
|
+
}
|
|
1184
|
+
io.out(`exposure: maxInFlight=${report.exposure.maxInFlight}` + (report.exposure.overshootOneTurnFloorUsd === void 0 ? "" : ` overshootOneTurnFloor=${usdOf(report.exposure.overshootOneTurnFloorUsd)}`));
|
|
1185
|
+
for (const [provider, row] of Object.entries(report.exposure.perProvider)) io.out(` provider ${provider}: inFlight=${row.inFlight} requestsPerWave=${row.requestsPerWave} tokensPerWaveFloor=${row.tokensPerWaveFloor}`);
|
|
1186
|
+
io.out(`findings: ${report.findings.length}`);
|
|
1187
|
+
for (const finding of report.findings) io.out(` ${finding.severity} ${finding.code}: ${finding.message}` + (finding.spawn === void 0 ? "" : ` [spawn ${finding.spawn}]`));
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* rulvar preflight (the experiment-review P2.2; grammar in grammar.ts):
|
|
1191
|
+
* the effective-config linter and dry-run estimator. Loads the SAME
|
|
1192
|
+
* config, module, and run-profile merge `rulvar run` would assemble,
|
|
1193
|
+
* but constructs no engine, opens no store, and dispatches nothing:
|
|
1194
|
+
* the report is computed by preflightEstimate over options alone, so
|
|
1195
|
+
* the command cannot pay for a single provider token by construction.
|
|
1196
|
+
* The declared spawn wave comes from the `preflight` export of the
|
|
1197
|
+
* config or workflow module (module wins), and --spawns JSON overrides
|
|
1198
|
+
* it from the command line. --json prints the machine-readable report.
|
|
1199
|
+
* Exit 1 when any finding has severity 'error' (the linter contract:
|
|
1200
|
+
* green preflight means the run can at least start), 0 otherwise.
|
|
1201
|
+
*/
|
|
1202
|
+
async function preflightCommand(argv, context) {
|
|
1203
|
+
const parsed = parseCommand(GRAMMAR.preflight, argv);
|
|
1204
|
+
const target = parsed.positionals[0];
|
|
1205
|
+
const profile = parsed.values.profile;
|
|
1206
|
+
const json = parsed.values.json === true;
|
|
1207
|
+
const budgetUsd = parsed.values["budget-usd"] === void 0 ? void 0 : parseBudgetValue("budget-usd", parsed.values["budget-usd"]);
|
|
1208
|
+
const config = await loadCliConfig(context.cwd);
|
|
1209
|
+
const module = looksLikeFile(target) ? await loadWorkflowModule(target, context.cwd) : void 0;
|
|
1210
|
+
const workflows = {
|
|
1211
|
+
...config.workflows,
|
|
1212
|
+
...module?.workflows
|
|
1213
|
+
};
|
|
1214
|
+
if ((module?.workflow ?? workflows[target]) === 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`);
|
|
1215
|
+
let engineOptions = {
|
|
1216
|
+
...config.engineOptions,
|
|
1217
|
+
...module?.engineOptions
|
|
1218
|
+
};
|
|
1219
|
+
if (profile !== void 0) {
|
|
1220
|
+
const preset = runProfile(profile);
|
|
1221
|
+
if (preset === void 0) throw new ConfigError(`unknown run profile '${profile}'; shipped: fast, standard, deep, ultra`);
|
|
1222
|
+
engineOptions = applyRunProfile(preset, engineOptions);
|
|
1223
|
+
}
|
|
1224
|
+
const declaration = {
|
|
1225
|
+
...config.preflight,
|
|
1226
|
+
...module?.preflight
|
|
1227
|
+
};
|
|
1228
|
+
let spawns = declaration.spawns;
|
|
1229
|
+
const spawnsJson = parsed.values.spawns;
|
|
1230
|
+
if (spawnsJson !== void 0) {
|
|
1231
|
+
let parsedSpawns;
|
|
1232
|
+
try {
|
|
1233
|
+
parsedSpawns = JSON.parse(spawnsJson);
|
|
1234
|
+
} catch (error) {
|
|
1235
|
+
throw new ConfigError(`--spawns is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
1236
|
+
}
|
|
1237
|
+
if (!Array.isArray(parsedSpawns)) throw new ConfigError("--spawns must be a JSON array of spawn specs");
|
|
1238
|
+
spawns = parsedSpawns;
|
|
1239
|
+
}
|
|
1240
|
+
const report = preflightEstimate({
|
|
1241
|
+
engine: engineOptions,
|
|
1242
|
+
run: { ...budgetUsd === void 0 ? {} : { budgetUsd } },
|
|
1243
|
+
...declaration.orchestrator === void 0 ? {} : { orchestrator: declaration.orchestrator },
|
|
1244
|
+
...spawns === void 0 ? {} : { spawns },
|
|
1245
|
+
...declaration.quotaRules === void 0 ? {} : { quotaRules: declaration.quotaRules }
|
|
1246
|
+
});
|
|
1247
|
+
if (json) context.io.out(JSON.stringify(report, null, 2));
|
|
1248
|
+
else renderPreflight(report, context.io);
|
|
1249
|
+
return report.findings.some((finding) => finding.severity === "error") ? 1 : 0;
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1087
1252
|
* rulvar plan (M6-T11; grammar in grammar.ts): plans a workflow script
|
|
1088
1253
|
* through @rulvar/planner (loaded dynamically: the CLI's static
|
|
1089
1254
|
* dependency stays @rulvar/core only), prints the accepted script and
|
|
@@ -1546,6 +1711,21 @@ accounting, localized determinism warnings, and the output digest;
|
|
|
1546
1711
|
--assert-no-live exits 1 unless the replay is pure, and
|
|
1547
1712
|
--compare-output-hash exits 1 unless the replayed result's digest
|
|
1548
1713
|
equals the journaled one.
|
|
1714
|
+
invoice prints the per-dispatch reconciliation export: one row per
|
|
1715
|
+
billable provider call (failed and retried attempts included) with the
|
|
1716
|
+
provider's response id when the adapter surfaced one, plus the
|
|
1717
|
+
gross/net ledger totals (gross includes abandoned subtrees, exactly
|
|
1718
|
+
what the provider bills); --json emits the machine-readable form.
|
|
1719
|
+
preflight is the effective-config linter and dry-run estimator: it
|
|
1720
|
+
assembles the SAME options rulvar run would (config, module exports,
|
|
1721
|
+
--profile, --budget-usd) but constructs no engine and dispatches
|
|
1722
|
+
nothing, then prints the effective merged limits per declared spawn,
|
|
1723
|
+
the layer-1 admission projection over the declared wave (which spawns
|
|
1724
|
+
admit, which are denied and by what), the per-tool and weighted-unit
|
|
1725
|
+
bottleneck ordering, the concurrency and quota exposure floors, and
|
|
1726
|
+
the linter findings. The declared wave comes from the preflight export
|
|
1727
|
+
of the config or workflow module, or --spawns JSON; --json emits the
|
|
1728
|
+
machine-readable report; exit 1 when any finding is severity error.
|
|
1549
1729
|
plan asks the planner model (role plan) to write a workflow script,
|
|
1550
1730
|
lints and self-repairs it, then runs it in the worker sandbox; --dry-run
|
|
1551
1731
|
prints the accepted script without running. Both stages are paid runs
|
|
@@ -1582,7 +1762,9 @@ async function runCli(argv, options) {
|
|
|
1582
1762
|
throw new ConfigError(RUNS_FAMILY_USAGE);
|
|
1583
1763
|
}
|
|
1584
1764
|
case "inspect": return await inspectCommand(rest, context);
|
|
1765
|
+
case "invoice": return await invoiceCommand(rest, context);
|
|
1585
1766
|
case "plan": return await planCommand(rest, context);
|
|
1767
|
+
case "preflight": return await preflightCommand(rest, context);
|
|
1586
1768
|
case "kb": return await kbCommand(rest, context);
|
|
1587
1769
|
case void 0:
|
|
1588
1770
|
case "help":
|
|
@@ -1632,4 +1814,4 @@ function processIo() {
|
|
|
1632
1814
|
};
|
|
1633
1815
|
}
|
|
1634
1816
|
//#endregion
|
|
1635
|
-
export {
|
|
1817
|
+
export { loadCliConfig as _, invoiceCommand as a, runCommand as c, reportOutcome as d, strictExitCode as f, assembleEngine as g, DEFAULT_STORE_DIR as h, inspectCommand as i, runsLsCommand as l, renderEventLine as m, HELP as n, preflightCommand as o, attachProgress as p, runCli as r, resumeCommand as s, processIo as t, driveRun as u, loadWorkflowModule as v, looksLikeFile as y };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.62.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.62.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/planner": "1.
|
|
32
|
-
"@rulvar/
|
|
33
|
-
"@rulvar/
|
|
34
|
-
"@rulvar/
|
|
35
|
-
"@rulvar/evals": "1.
|
|
31
|
+
"@rulvar/planner": "1.62.0",
|
|
32
|
+
"@rulvar/testing": "1.62.0",
|
|
33
|
+
"@rulvar/plan": "1.62.0",
|
|
34
|
+
"@rulvar/store-sqlite": "1.62.0",
|
|
35
|
+
"@rulvar/evals": "1.62.0"
|
|
36
36
|
},
|
|
37
37
|
"bin": {
|
|
38
38
|
"rulvar": "./dist/cli.js"
|