@eigenpal/cli 0.10.14 → 0.10.16

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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # @eigenpal/cli
2
2
 
3
+ ## 0.10.16
4
+
5
+ ### Patch Changes
6
+
7
+ - b1f0bca: `eigenpal run <workflow> --example <name>` now actually grades the example
8
+ instead of printing `PASS` whenever the run merely finished. It runs the
9
+ workflow's current published version on the server's stored dataset example, so
10
+ configured evaluators run automatically and the CLI surfaces the real weighted
11
+ score and per-evaluator pass/fail. Workflows with no evaluators fall back to a
12
+ structural diff of the output against the example's stored expected output. The
13
+ summary reports two separate signals: execution health (`ok`/`errored`) and
14
+ accuracy. Add `--fail-on-mismatch` to exit non-zero when a graded example fails.
15
+
16
+ Because the run uses the server-side example, push your latest dataset and
17
+ evaluators (`eigenpal workflow dataset push`, `eigenpal workflow evaluators
18
+ push`) before running `--example`.
19
+
3
20
  ## 0.10.9
4
21
 
5
22
  ### Patch Changes
package/dist/cli.js CHANGED
@@ -66143,7 +66143,7 @@ function partialMatch(expected, actual, path, mismatches) {
66143
66143
  }
66144
66144
  let allMatch = true;
66145
66145
  for (let i = 0;i < expected.length; i++) {
66146
- const childPath = path ? `${path}.${i}` : String(i);
66146
+ const childPath = path ? `${path}[${i}]` : `[${i}]`;
66147
66147
  if (!partialMatch(expected[i], actual[i], childPath, mismatches)) {
66148
66148
  allMatch = false;
66149
66149
  }
@@ -66314,7 +66314,7 @@ function formatZodError(source) {
66314
66314
  severity: "error"
66315
66315
  }));
66316
66316
  }
66317
- var DATASET_NAME_PATTERN, DatasetNameSchema, DatasetMetaSchema, DatasetImportModeSchema, DatasetImportRequestSchema, EvaluatorsImportRequestSchema, EvalResultsExportFormatSchema, ExperimentExportQuerySchema, EvalResultsExportQuerySchema, EvalResultExportRowSchema, EvalResultsExportJsonSchema, ERROR_HINTS;
66317
+ var DATASET_NAME_PATTERN, DatasetNameSchema, DatasetMetaSchema, DatasetImportModeSchema, DatasetImportRequestSchema, EvaluatorsImportRequestSchema, EvalResultsExportFormatSchema, BooleanQueryFlagSchema, ExperimentExportBaseQuerySchema, ExperimentExportAllQuerySchema, ExperimentExportQuerySchema, EvalResultsExportQuerySchema, EvalResultExportRowSchema, EvalResultsExportJsonSchema, ERROR_HINTS;
66318
66318
  var init_dataset_archive = __esm(() => {
66319
66319
  init_zod();
66320
66320
  DATASET_NAME_PATTERN = /^[a-z0-9][a-z0-9-_]*$/;
@@ -66332,9 +66332,14 @@ var init_dataset_archive = __esm(() => {
66332
66332
  yaml: exports_external.string().min(1, "yaml content is required").max(1048576, "evaluator YAML exceeds 1 MB")
66333
66333
  });
66334
66334
  EvalResultsExportFormatSchema = exports_external.enum(["csv", "json"]);
66335
- ExperimentExportQuerySchema = exports_external.object({
66335
+ BooleanQueryFlagSchema = exports_external.enum(["true", "false", "1", "0"]).optional().transform((value) => value === "true" || value === "1");
66336
+ ExperimentExportBaseQuerySchema = exports_external.object({
66336
66337
  format: EvalResultsExportFormatSchema
66337
66338
  });
66339
+ ExperimentExportAllQuerySchema = ExperimentExportBaseQuerySchema;
66340
+ ExperimentExportQuerySchema = ExperimentExportBaseQuerySchema.extend({
66341
+ includeTrace: BooleanQueryFlagSchema
66342
+ });
66338
66343
  EvalResultsExportQuerySchema = ExperimentExportQuerySchema;
66339
66344
  EvalResultExportRowSchema = exports_external.object({
66340
66345
  executionId: exports_external.string().min(1),
@@ -68874,6 +68879,7 @@ __export(exports_src, {
68874
68879
  ExtractInputSchema: () => ExtractInputSchema,
68875
68880
  ExtractConfigSchema: () => ExtractConfigSchema,
68876
68881
  ExperimentExportQuerySchema: () => ExperimentExportQuerySchema,
68882
+ ExperimentExportAllQuerySchema: () => ExperimentExportAllQuerySchema,
68877
68883
  ExpectedOutputSchema: () => ExpectedOutputSchema,
68878
68884
  ExpectedOutputFilesSchema: () => ExpectedOutputFilesSchema,
68879
68885
  ExpectedErrorSchema: () => ExpectedErrorSchema,
@@ -183716,7 +183722,7 @@ import { fileURLToPath as fileURLToPath8 } from "url";
183716
183722
  // package.json
183717
183723
  var package_default = {
183718
183724
  name: "@eigenpal/cli",
183719
- version: "0.10.14",
183725
+ version: "0.10.16",
183720
183726
  description: "Eigenpal CLI — author and run document workflows from your terminal. Agent-ready.",
183721
183727
  keywords: [
183722
183728
  "eigenpal",
@@ -184199,9 +184205,6 @@ function resolveProfileKey(name, profiles) {
184199
184205
 
184200
184206
  // src/lib/ui.ts
184201
184207
  var import_picocolors = __toESM(require_picocolors(), 1);
184202
- function isTTY() {
184203
- return Boolean(process.stdout.isTTY);
184204
- }
184205
184208
  var ui = {
184206
184209
  ok: import_picocolors.default.green,
184207
184210
  err: import_picocolors.default.red,
@@ -190283,10 +190286,6 @@ function formatEigenpalDirIfAvailable(dir) {
190283
190286
  });
190284
190287
  }
190285
190288
 
190286
- // src/commands/workflow/exec.ts
190287
- init_src();
190288
- import { join as join9 } from "path";
190289
-
190290
190289
  // src/lib/execution-artifacts.ts
190291
190290
  import { existsSync as existsSync13, mkdirSync as mkdirSync5, readdirSync as readdirSync3, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
190292
190291
  import { join as join7 } from "path";
@@ -190412,363 +190411,318 @@ async function writeExecutionArtifacts(client, exampleDir, payload) {
190412
190411
  return executionsDir;
190413
190412
  }
190414
190413
 
190415
- // src/lib/payload.ts
190416
- init_src();
190417
- import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync13, statSync as statSync4 } from "fs";
190418
- import { join as join8 } from "path";
190419
- var DATASET_EXAMPLES_DIR = ["dataset", "examples"];
190420
- function resolveEvalBaseDir(dir) {
190421
- const candidates = [join8(dir, ...DATASET_EXAMPLES_DIR), join8(...DATASET_EXAMPLES_DIR)];
190422
- for (const candidate of candidates) {
190423
- if (existsSync14(candidate))
190424
- return candidate;
190414
+ // src/lib/grade-example.ts
190415
+ var MAX_VALUE_LEN = 60;
190416
+ function preview(value) {
190417
+ let text;
190418
+ try {
190419
+ text = typeof value === "string" ? value : JSON.stringify(value);
190420
+ } catch {
190421
+ text = String(value);
190425
190422
  }
190426
- return null;
190423
+ if (text === undefined)
190424
+ text = String(value);
190425
+ return text.length > MAX_VALUE_LEN ? `${text.slice(0, MAX_VALUE_LEN)}…` : text;
190427
190426
  }
190428
- function getExampleNames(dir, exampleNamesFilter) {
190429
- const baseDir = resolveEvalBaseDir(dir);
190430
- if (!baseDir) {
190431
- throw new Error(`No dataset/examples directory found at ${join8(dir, ...DATASET_EXAMPLES_DIR)}. Run \`eigenpal init workflow <name>\` to scaffold one.`);
190432
- }
190433
- const all = readdirSync4(baseDir).filter((name) => statSync4(join8(baseDir, name)).isDirectory() && existsSync14(join8(baseDir, name, "input.json"))).sort((a5, b8) => a5.localeCompare(b8, undefined, { numeric: true }));
190434
- if (!exampleNamesFilter?.length)
190435
- return all;
190436
- return all.filter((n5) => exampleNamesFilter.includes(n5));
190427
+ function isPlainObject3(value) {
190428
+ return value !== null && typeof value === "object" && !Array.isArray(value);
190437
190429
  }
190438
- function readInput(exampleDir) {
190439
- const canonicalPath = join8(exampleDir, "input.json");
190440
- return readCanonicalInput(exampleDir, canonicalPath);
190430
+ function joinPath(path15, key2) {
190431
+ return path15 ? `${path15}.${key2}` : key2;
190441
190432
  }
190442
- function readCanonicalInput(exampleDir, inputPath) {
190443
- const raw = JSON.parse(readFileSync13(inputPath, "utf-8"));
190444
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
190445
- throw new Error(`${inputPath} must contain a JSON object.`);
190446
- }
190447
- const scalars = {};
190448
- const files = [];
190449
- for (const [key2, value] of Object.entries(raw)) {
190450
- const refs = topLevelFileRefs(value);
190451
- if (refs.length === 0) {
190452
- scalars[key2] = materializeNestedFileRefs(exampleDir, inputPath, value);
190453
- continue;
190433
+ function walk2(expected, actual, path15, diffs) {
190434
+ if (Array.isArray(expected)) {
190435
+ if (!Array.isArray(actual)) {
190436
+ diffs.push(`${path15 || "(root)"}: expected array, got ${preview(actual)}`);
190437
+ return;
190454
190438
  }
190455
- for (const ref of refs) {
190456
- const validation2 = validateScopedArtifactPath(ref, { allowedRoots: ["input"] });
190457
- if (!validation2.ok) {
190458
- throw new Error(`${inputPath}: ${key2} must reference files under input/ without path traversal.`);
190459
- }
190460
- const relative3 = validation2.value.segments.slice(1).join("/");
190461
- const filePath = join8(exampleDir, validation2.value.path);
190462
- const filename = validation2.value.segments.at(-1) ?? relative3;
190463
- files.push({
190464
- argument: key2,
190465
- filename,
190466
- content: readFileSync13(filePath),
190467
- mimeType: guessMimeType(filename)
190468
- });
190439
+ if (expected.length !== actual.length) {
190440
+ diffs.push(`${path15 || "(root)"}: length ${expected.length} != ${actual.length}`);
190469
190441
  }
190470
- }
190471
- return { scalars, files };
190472
- }
190473
- function materializeNestedFileRefs(exampleDir, inputPath, value) {
190474
- if (isScopedFileRef2(value)) {
190475
- const validation2 = validateScopedArtifactPath(value.$file, { allowedRoots: ["input"] });
190476
- if (!validation2.ok) {
190477
- throw new Error(`${inputPath}: $file refs must reference files under input/.`);
190442
+ const overlap = Math.min(expected.length, actual.length);
190443
+ for (let i5 = 0;i5 < overlap; i5++) {
190444
+ walk2(expected[i5], actual[i5], `${path15}[${i5}]`, diffs);
190478
190445
  }
190479
- const filename = validation2.value.segments.at(-1) ?? "file";
190480
- const filePath = join8(exampleDir, validation2.value.path);
190481
- return {
190482
- $inline: {
190483
- filename,
190484
- mimeType: guessMimeType(filename) ?? "application/octet-stream",
190485
- base64: readFileSync13(filePath).toString("base64")
190486
- }
190487
- };
190488
- }
190489
- if (Array.isArray(value)) {
190490
- return value.map((item) => materializeNestedFileRefs(exampleDir, inputPath, item));
190446
+ return;
190491
190447
  }
190492
- if (value && typeof value === "object") {
190493
- if (hasFileKey(value)) {
190494
- throw new Error('file refs must be exact { "$file": "input/..." } objects.');
190448
+ if (isPlainObject3(expected)) {
190449
+ if (!isPlainObject3(actual)) {
190450
+ diffs.push(`${path15 || "(root)"}: expected object, got ${preview(actual)}`);
190451
+ return;
190495
190452
  }
190496
- const out = {};
190497
- for (const [key2, item] of Object.entries(value)) {
190498
- out[key2] = materializeNestedFileRefs(exampleDir, inputPath, item);
190453
+ for (const [key2, value] of Object.entries(expected)) {
190454
+ const childPath = joinPath(path15, key2);
190455
+ if (!(key2 in actual)) {
190456
+ diffs.push(`${childPath}: missing in output`);
190457
+ continue;
190458
+ }
190459
+ walk2(value, actual[key2], childPath, diffs);
190499
190460
  }
190500
- return out;
190501
- }
190502
- return value;
190503
- }
190504
- function topLevelFileRefs(value) {
190505
- if (isScopedFileRef2(value))
190506
- return [value.$file];
190507
- if (hasFileKey(value)) {
190508
- throw new Error('file refs must be exact { "$file": "input/..." } objects.');
190461
+ return;
190509
190462
  }
190510
- if (Array.isArray(value) && value.every(isScopedFileRef2))
190511
- return value.map((item) => item.$file);
190512
- if (Array.isArray(value) && value.some(hasFileKey)) {
190513
- throw new Error('file arrays must contain only exact { "$file": "input/..." } entries.');
190463
+ if (!Object.is(expected, actual)) {
190464
+ diffs.push(`${path15 || "(root)"}: ${preview(expected)} != ${preview(actual)}`);
190514
190465
  }
190515
- return [];
190516
- }
190517
- function isScopedFileRef2(value) {
190518
- return value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 1 && typeof value.$file === "string";
190519
190466
  }
190520
- function hasFileKey(value) {
190521
- return value !== null && typeof value === "object" && !Array.isArray(value) && "$file" in value;
190467
+ function gradeAgainstExpected(expected, actual) {
190468
+ const diffs = [];
190469
+ walk2(expected, actual, "", diffs);
190470
+ return { matched: diffs.length === 0, diffs };
190522
190471
  }
190523
- function readOverrides(exampleDir) {
190524
- const metaPath = join8(exampleDir, "meta.json");
190525
- if (!existsSync14(metaPath))
190526
- return null;
190527
- const raw = JSON.parse(readFileSync13(metaPath, "utf-8"));
190528
- const ov2 = raw.overrides;
190529
- if (ov2 && typeof ov2 === "object" && "steps" in ov2 && typeof ov2.steps === "object") {
190530
- return ov2;
190472
+
190473
+ // src/lib/payload.ts
190474
+ import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync13, statSync as statSync4 } from "fs";
190475
+ import { join as join8 } from "path";
190476
+ var DATASET_EXAMPLES_DIR = ["dataset", "examples"];
190477
+ function resolveEvalBaseDir(dir) {
190478
+ const candidates = [join8(dir, ...DATASET_EXAMPLES_DIR), join8(...DATASET_EXAMPLES_DIR)];
190479
+ for (const candidate of candidates) {
190480
+ if (existsSync14(candidate))
190481
+ return candidate;
190531
190482
  }
190532
190483
  return null;
190533
190484
  }
190534
- function buildExamplePayload(exampleDir) {
190535
- const { scalars, files } = readInput(exampleDir);
190536
- return {
190537
- scalars,
190538
- files,
190539
- overrides: readOverrides(exampleDir)
190540
- };
190541
- }
190542
190485
 
190543
- // src/lib/progress-lines.ts
190544
- var SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
190545
- var SPINNER_INTERVAL_MS = 120;
190546
- function writeLineInPlace(text) {
190547
- process.stdout.write(`\r\x1B[K${text}`);
190486
+ // src/commands/workflow/eval-example.ts
190487
+ var RUN_POLL_INTERVAL_MS = 2000;
190488
+ var RUN_MAX_WAIT_MS = 5 * 60 * 1000;
190489
+ var EVAL_GRACE_MS = 90 * 1000;
190490
+ var EVAL_POLL_INTERVAL_MS = 2000;
190491
+ var EXAMPLE_PAGE_SIZE = 100;
190492
+ var TERMINAL = new Set(["completed", "failed", "cancelled", "rejected", "timeout"]);
190493
+ function fmtScore(score) {
190494
+ return typeof score === "number" ? score.toFixed(2) : "n/a";
190548
190495
  }
190549
- function cursorUp(n5) {
190550
- if (n5 > 0)
190551
- process.stdout.write(`\x1B[${n5}A`);
190496
+ function toNumberOrNull(value) {
190497
+ if (typeof value === "number")
190498
+ return Number.isFinite(value) ? value : null;
190499
+ if (typeof value === "string") {
190500
+ const parsed = Number(value);
190501
+ return Number.isFinite(parsed) ? parsed : null;
190502
+ }
190503
+ return null;
190552
190504
  }
190553
- function cursorDown(n5) {
190554
- if (n5 > 0)
190555
- process.stdout.write(`\x1B[${n5}B`);
190505
+ function runOutput(run) {
190506
+ return run.output !== undefined ? run.output : run.result?.output;
190556
190507
  }
190557
- function updateLineAt(lineIndex, totalLines, text) {
190558
- const up2 = totalLines - lineIndex;
190559
- cursorUp(up2);
190560
- writeLineInPlace(text);
190561
- cursorDown(up2);
190508
+ async function fetchHasEvaluators(client, workflowId) {
190509
+ const cfg = await client.get(`/api/v1/automations/${workflowId}/evaluators`);
190510
+ return Array.isArray(cfg.config?.evaluators) && cfg.config.evaluators.length > 0;
190562
190511
  }
190563
- function createProgressLines(options8) {
190564
- const {
190565
- interactive,
190566
- lineLabels,
190567
- waitingLabel = "waiting",
190568
- runningLabel = "running",
190569
- dim: dim2 = (s5) => s5,
190570
- spinnerStyle = (s5) => s5
190571
- } = options8;
190572
- const totalLines = lineLabels.length;
190573
- const runningLabels = new Map;
190574
- let updateLock = Promise.resolve();
190575
- let spinnerInterval = null;
190576
- let spinnerFrame = 0;
190577
- const scheduleUpdate = (fn9) => {
190578
- const run = updateLock.then(() => fn9());
190579
- updateLock = run;
190580
- return run;
190581
- };
190582
- const start = () => {
190583
- if (!interactive || totalLines === 0)
190584
- return;
190585
- const wait = dim2(waitingLabel);
190586
- for (const label2 of lineLabels) {
190587
- process.stdout.write(`${label2} ${wait}
190588
- `);
190589
- }
190590
- };
190591
- const setRunning = (lineIndex, customLabel) => {
190592
- if (!interactive)
190593
- return;
190594
- runningLabels.set(lineIndex, customLabel ?? runningLabel);
190595
- if (spinnerInterval === null) {
190596
- spinnerInterval = setInterval(() => {
190597
- updateLock = updateLock.then(() => {
190598
- if (runningLabels.size === 0) {
190599
- if (spinnerInterval) {
190600
- clearInterval(spinnerInterval);
190601
- spinnerInterval = null;
190602
- }
190603
- return;
190604
- }
190605
- const frame = SPINNER_FRAMES[spinnerFrame++ % SPINNER_FRAMES.length];
190606
- for (const [i5, label2] of runningLabels) {
190607
- const lineLabel = lineLabels[i5] ?? "";
190608
- updateLineAt(i5, totalLines, `${lineLabel} ${label2} ${spinnerStyle(frame)}`);
190609
- }
190610
- });
190611
- }, SPINNER_INTERVAL_MS);
190512
+ async function resolveExample(client, workflowId, name) {
190513
+ let offset = 0;
190514
+ for (;; ) {
190515
+ const page = await client.get(`/api/v1/automations/${workflowId}/examples`, {
190516
+ limit: String(EXAMPLE_PAGE_SIZE),
190517
+ offset: String(offset)
190518
+ });
190519
+ const rows = page.data ?? [];
190520
+ const hit = rows.find((r5) => r5.name === name);
190521
+ if (hit)
190522
+ return { id: hit.id, expected: hit.expected };
190523
+ if (rows.length < EXAMPLE_PAGE_SIZE)
190524
+ return null;
190525
+ offset += rows.length;
190526
+ }
190527
+ }
190528
+ async function pollRunTerminal(client, runId) {
190529
+ const start = Date.now();
190530
+ const base = `/api/v1/runs/${encodeURIComponent(runId)}`;
190531
+ for (;; ) {
190532
+ const run = await client.get(base);
190533
+ const status = run.execution?.status ?? run.status;
190534
+ if (run.finished || status && TERMINAL.has(status)) {
190535
+ return await client.get(`${base}?expand=execution`);
190612
190536
  }
190613
- };
190614
- const setDone = async (lineIndex, text, extraLines = []) => {
190615
- if (interactive) {
190616
- runningLabels.delete(lineIndex);
190617
- await scheduleUpdate(() => updateLineAt(lineIndex, totalLines, text));
190618
- for (const line3 of extraLines)
190619
- console.log(line3);
190620
- } else {
190621
- console.log(text);
190622
- for (const line3 of extraLines)
190623
- console.log(line3);
190537
+ if (Date.now() - start > RUN_MAX_WAIT_MS) {
190538
+ return { ...run, status: "timeout", execution: { status: "timeout" } };
190624
190539
  }
190625
- };
190626
- return {
190627
- totalLines,
190628
- start,
190629
- setRunning,
190630
- setDone
190631
- };
190632
- }
190633
-
190634
- // src/commands/workflow/exec.ts
190635
- var POLL_INTERVAL_MS = 2000;
190636
- var MAX_POLL_MS = 5 * 60 * 1000;
190637
- var DEFAULT_CONCURRENCY = 3;
190638
- function runTerminalOutput(run) {
190639
- return run.output !== undefined ? run.output : run.result?.output;
190640
- }
190641
- function runTerminalError(run) {
190642
- const error52 = run.error !== undefined ? run.error : run.result?.error;
190643
- return error52 ?? undefined;
190540
+ await new Promise((r5) => setTimeout(r5, RUN_POLL_INTERVAL_MS));
190541
+ }
190644
190542
  }
190645
- async function pollExecution(client, executionId) {
190543
+ async function pollEvalRollup(client, runId) {
190646
190544
  const start = Date.now();
190647
- while (Date.now() - start < MAX_POLL_MS) {
190648
- const run = await client.get(`/api/v1/runs/${executionId}`);
190649
- const status = run.execution?.status ?? run.status;
190650
- if (run.finished || isTerminalExecutionStatus(status)) {
190651
- return {
190652
- executionId: run.id,
190653
- status: status ?? (run.error ? "failed" : "completed"),
190654
- output: runTerminalOutput(run),
190655
- error: runTerminalError(run)
190656
- };
190545
+ for (;; ) {
190546
+ const run = await client.get(`/api/v1/runs/${encodeURIComponent(runId)}`);
190547
+ if (run.eval && run.eval.passed != null) {
190548
+ return { score: run.eval.score ?? null, passed: run.eval.passed };
190657
190549
  }
190658
- await new Promise((r5) => setTimeout(r5, POLL_INTERVAL_MS));
190550
+ if (Date.now() - start > EVAL_GRACE_MS)
190551
+ return null;
190552
+ await new Promise((r5) => setTimeout(r5, EVAL_POLL_INTERVAL_MS));
190659
190553
  }
190660
- return { executionId, status: "timeout", error: "Execution timed out" };
190661
190554
  }
190662
- function toExecutionArtifactPayload(run) {
190663
- const status = run.execution?.status ?? run.status ?? "unknown";
190555
+ async function fetchEvalRows(client, workflowId, batchId, runId) {
190556
+ const detail = await client.get(`/api/v1/automations/${workflowId}/experiments/${batchId}`);
190557
+ return detail.resultsByRun?.[runId] ?? [];
190558
+ }
190559
+ function toArtifactPayload(run, runId) {
190664
190560
  return {
190665
- executionId: run.id,
190666
- status,
190561
+ executionId: runId,
190562
+ status: run.execution?.status ?? run.status ?? "unknown",
190667
190563
  createdAt: run.timing?.createdAt ?? new Date().toISOString(),
190668
190564
  completedAt: run.timing?.completedAt ?? null,
190669
190565
  error: run.error !== undefined ? run.error : run.result?.error ?? null,
190670
- output: runTerminalOutput(run),
190671
- stepExecutions: run.execution?.steps ?? []
190566
+ output: runOutput(run),
190567
+ stepExecutions: []
190672
190568
  };
190673
190569
  }
190674
- async function runExec(client, dir, workflowIdOrSlug, exampleNames, options8 = {}) {
190675
- const workflowId = await resolveWorkflowId(client, workflowIdOrSlug);
190676
- const names = getExampleNames(dir, exampleNames.length ? exampleNames : undefined);
190677
- if (names.length === 0)
190678
- throw new Error("No matching eval examples found.");
190679
- const requested = options8.concurrencyOverride ?? DEFAULT_CONCURRENCY;
190680
- const requestedValid = Number.isFinite(requested) && requested > 0 ? requested : DEFAULT_CONCURRENCY;
190681
- const concurrency = Math.max(1, Math.min(requestedValid, names.length));
190570
+ async function runWorkflowExamplesWithEval(client, dir, workflowIdOrSlug, workflowId, exampleNames) {
190571
+ const hasEvaluators = await fetchHasEvaluators(client, workflowId);
190572
+ const mode = hasEvaluators ? "evaluators" : "diff";
190573
+ info(`Running ${ui.bold(`"${workflowIdOrSlug}"`)} ${ui.dim(`(${workflowId}, ${exampleNames.length} example(s))`)}`);
190574
+ dim(hasEvaluators ? "Server example run; grading with configured evaluators." : "Server example run; no evaluators configured, grading by structural diff vs expected.");
190682
190575
  const evalBaseDir = resolveEvalBaseDir(dir);
190683
- if (!evalBaseDir)
190684
- throw new Error("No dataset/examples directory found.");
190685
- info(`Running workflow ${ui.bold(`"${workflowIdOrSlug}"`)} ${ui.dim(`(${workflowId}, ${names.length} example(s), concurrency ${concurrency})`)}`);
190686
- dim("Using unified run endpoint.");
190687
- const interactive = isTTY();
190688
- const progress = createProgressLines({
190689
- interactive,
190690
- lineLabels: names,
190691
- waitingLabel: "waiting",
190692
- runningLabel: "running",
190693
- dim: ui.dim,
190694
- spinnerStyle: ui.info
190695
- });
190696
- progress.start();
190697
- let passed = 0;
190698
- let failed = 0;
190699
- let nextIndex = 0;
190700
- const reportDone = (index, label2) => {
190701
- if (interactive)
190702
- progress.setDone(index, label2);
190703
- else
190704
- console.log(label2);
190705
- };
190706
- const runOne = async () => {
190707
- while (true) {
190708
- const index = nextIndex++;
190709
- if (index >= names.length)
190710
- return;
190711
- const name = names[index];
190712
- const exampleDir = join9(evalBaseDir, name);
190713
- try {
190714
- const example = buildExamplePayload(exampleDir);
190715
- if (interactive)
190716
- progress.setRunning(index);
190717
- else
190718
- console.log(`${ui.dim("→")} ${name}`);
190719
- const form = await buildRunFormData({
190720
- target: runStartMultipartTarget({
190721
- type: "workflow",
190722
- id: workflowId,
190723
- version: options8.version
190724
- }),
190725
- input: example.scalars,
190726
- overrides: example.overrides,
190727
- files: example.files.map((file2) => ({
190728
- fieldName: file2.argument,
190729
- content: file2.content,
190730
- filename: file2.filename,
190731
- mimeType: file2.mimeType
190732
- }))
190733
- });
190734
- const res = await client.postFormData(runTargetApiPath({ type: "workflow", id: workflowId, version: options8.version }), form);
190735
- if (typeof res?.id !== "string") {
190736
- throw new Error("Run API did not return a run id");
190737
- }
190738
- const executionId = res.id;
190739
- process.stderr.write(` ${ui.dim(`→ ${name}: ${executionId}`)}
190576
+ const results = [];
190577
+ for (const name of exampleNames) {
190578
+ process.stderr.write(`${ui.dim("→")} ${name}
190740
190579
  `);
190741
- const result = await pollExecution(client, executionId);
190742
- reportDone(index, result.status === "completed" ? `${name} ${ui.ok("PASS")}` : `${name} ${ui.err("FAIL")} ${result.error ?? result.status}`);
190743
- if (result.status === "completed")
190744
- passed++;
190745
- else
190746
- failed++;
190747
- try {
190748
- const run = await client.get(`/api/v1/runs/${executionId}?expand=execution`);
190749
- const artifactDir = await writeExecutionArtifacts(client, exampleDir, toExecutionArtifactPayload(run));
190750
- if (!interactive)
190751
- console.log(ui.dim(` Artifacts: ${artifactDir}`));
190752
- } catch (artErr) {
190753
- if (!interactive)
190754
- console.warn(ui.dim(` Warning: could not write artifacts: ${formatCliError(artErr)}`));
190580
+ try {
190581
+ const example = await resolveExample(client, workflowId, name);
190582
+ if (!example) {
190583
+ throw new Error(`example "${name}" not found in the server dataset for ${workflowId}. ` + `Push it first: eigenpal workflow dataset push ${workflowId}`);
190584
+ }
190585
+ const started = await client.post(`/api/v1/automations/${workflowId}/examples/${example.id}/run`, {});
190586
+ const runId = started.id;
190587
+ if (typeof runId !== "string")
190588
+ throw new Error("Example run did not return a run id");
190589
+ process.stderr.write(` ${ui.dim(`run: ${runId}`)}
190590
+ `);
190591
+ const run = await pollRunTerminal(client, runId);
190592
+ const status = run.execution?.status ?? run.status;
190593
+ const runOk = status === "completed";
190594
+ const result = {
190595
+ name,
190596
+ exampleId: example.id,
190597
+ executionId: runId,
190598
+ run: runOk ? "ok" : "error",
190599
+ runError: runOk ? undefined : run.error ?? status ?? "unknown",
190600
+ mode: "none"
190601
+ };
190602
+ if (runOk && hasEvaluators && started.batchId) {
190603
+ const rollup = await pollEvalRollup(client, runId);
190604
+ if (rollup) {
190605
+ const rows = await fetchEvalRows(client, workflowId, started.batchId, runId);
190606
+ result.mode = "evaluators";
190607
+ result.score = rollup.score;
190608
+ result.passed = rollup.passed;
190609
+ result.evaluators = rows.map((r5) => ({
190610
+ name: r5.evaluatorName ?? "evaluator",
190611
+ score: toNumberOrNull(r5.score),
190612
+ passed: r5.passed ?? null,
190613
+ weight: toNumberOrNull(r5.weight),
190614
+ error: r5.error ?? null
190615
+ }));
190616
+ } else {
190617
+ warn(` ${name}: no evaluator score after ${EVAL_GRACE_MS / 1000}s; grading by diff.`);
190618
+ gradeByDiff(result, example.expected, runOutput(run));
190755
190619
  }
190756
- } catch (err2) {
190757
- reportDone(index, `${name} ${ui.err("FAIL")} ${formatCliError(err2)}`);
190758
- failed++;
190620
+ } else if (runOk) {
190621
+ gradeByDiff(result, example.expected, runOutput(run));
190622
+ }
190623
+ results.push(result);
190624
+ reportLine(result);
190625
+ if (evalBaseDir) {
190626
+ try {
190627
+ await writeExecutionArtifacts(client, `${evalBaseDir}/${name}`, toArtifactPayload(run, runId));
190628
+ } catch {}
190759
190629
  }
190630
+ } catch (err2) {
190631
+ const result = {
190632
+ name,
190633
+ run: "error",
190634
+ runError: formatCliError(err2),
190635
+ mode: "none"
190636
+ };
190637
+ results.push(result);
190638
+ reportLine(result);
190760
190639
  }
190761
- };
190762
- const workers = Array.from({ length: concurrency }, () => runOne());
190763
- await Promise.all(workers);
190764
- console.log(`
190765
- ${ui.bold("Results:")} ${ui.ok(String(passed))} passed, ${failed > 0 ? ui.err(String(failed)) : ui.ok(String(failed))} failed`);
190640
+ }
190641
+ const summary = summarize(workflowIdOrSlug, mode, results);
190642
+ process.stderr.write(`
190643
+ ${formatEvalSummary(summary)}
190644
+ `);
190645
+ return summary;
190646
+ }
190647
+ function gradeByDiff(result, expected, output) {
190648
+ if (expected === undefined || expected === null) {
190649
+ result.mode = "none";
190650
+ return;
190651
+ }
190652
+ const graded = gradeAgainstExpected(expected, output);
190653
+ result.mode = "diff";
190654
+ result.matched = graded.matched;
190655
+ result.diffCount = graded.diffs.length;
190656
+ result.diffs = graded.diffs.length ? graded.diffs : undefined;
190657
+ }
190658
+ function formatExampleLines(result) {
190659
+ if (result.run === "error") {
190660
+ return `${result.name} ${ui.err(`err ${result.runError ?? ""}`.trimEnd())}`;
190661
+ }
190662
+ if (result.mode === "evaluators") {
190663
+ const verdict = result.passed === true ? ui.ok("PASS") : ui.err("FAIL");
190664
+ const head = `${result.name} ${ui.ok("ok")} score ${fmtScore(result.score)} ${verdict}`;
190665
+ const detail = (result.evaluators ?? []).map((e8) => {
190666
+ const mark = e8.error ? ui.warn("error") : e8.passed === true ? ui.ok("pass") : e8.passed === false ? ui.err("fail") : ui.dim("n/a");
190667
+ return ` ${e8.name} ${fmtScore(e8.score)} ${mark}`;
190668
+ });
190669
+ return [head, ...detail].join(`
190670
+ `);
190671
+ }
190672
+ if (result.mode === "diff") {
190673
+ const verdict = result.matched ? ui.ok("PASS") : ui.err(`FAIL (${result.diffCount} diff)`);
190674
+ return `${result.name} ${ui.ok("ok")} ${verdict}`;
190675
+ }
190676
+ return `${result.name} ${ui.ok("ok")} ${ui.dim("-")}`;
190677
+ }
190678
+ function reportLine(result) {
190679
+ process.stderr.write(`${formatExampleLines(result)}
190680
+ `);
190681
+ }
190682
+ function summarize(workflow2, mode, examples) {
190683
+ const ok2 = examples.filter((r5) => r5.run === "ok").length;
190684
+ const errored = examples.length - ok2;
190685
+ const gradedResults = examples.filter((r5) => r5.mode === "evaluators" || r5.mode === "diff");
190686
+ const passedCases = gradedResults.filter((r5) => r5.mode === "evaluators" ? r5.passed === true : r5.matched === true).length;
190687
+ const scores = gradedResults.filter((r5) => r5.mode === "evaluators" && typeof r5.score === "number").map((r5) => r5.score);
190766
190688
  return {
190767
- workflow: workflowIdOrSlug,
190768
- passed,
190769
- failed,
190770
- total: names.length
190771
- };
190689
+ workflow: workflow2,
190690
+ mode,
190691
+ total: examples.length,
190692
+ ok: ok2,
190693
+ errored,
190694
+ graded: gradedResults.length,
190695
+ passedCases,
190696
+ failedCases: gradedResults.length - passedCases,
190697
+ ungraded: ok2 - gradedResults.length,
190698
+ weightedAvg: scores.length ? scores.reduce((s5, n5) => s5 + n5, 0) / scores.length : null,
190699
+ examples,
190700
+ passed: ok2,
190701
+ failed: errored
190702
+ };
190703
+ }
190704
+ function formatEvalSummary(summary) {
190705
+ const lines = [];
190706
+ let line3 = `${ui.bold("Ran")} ${summary.total}: ${ui.ok(`${summary.ok} ok`)}`;
190707
+ if (summary.errored > 0)
190708
+ line3 += `, ${ui.err(`${summary.errored} errored`)}`;
190709
+ if (summary.graded > 0) {
190710
+ const allPass = summary.failedCases === 0;
190711
+ if (summary.mode === "evaluators") {
190712
+ const cases = `${summary.passedCases}/${summary.graded} cases pass`;
190713
+ const weighted = summary.weightedAvg != null ? `weighted ${summary.weightedAvg.toFixed(2)}, ` : "";
190714
+ line3 += ` ${ui.dim("/")} ${weighted}${allPass ? ui.ok(cases) : ui.err(cases)}`;
190715
+ } else {
190716
+ const acc = `${summary.passedCases}/${summary.graded} matched`;
190717
+ line3 += ` ${ui.dim("/")} accuracy ${allPass ? ui.ok(acc) : ui.err(acc)}`;
190718
+ }
190719
+ }
190720
+ lines.push(line3);
190721
+ if (summary.ungraded > 0) {
190722
+ lines.push(ui.dim(` ${summary.ungraded} ungraded (no stored expected output)`));
190723
+ }
190724
+ return lines.join(`
190725
+ `);
190772
190726
  }
190773
190727
 
190774
190728
  // src/commands/workflow/execution.ts
@@ -190777,13 +190731,16 @@ async function runSavedWorkflowExamples(workflow2, examples, opts) {
190777
190731
  try {
190778
190732
  requireApiKey(config2);
190779
190733
  const client = new ApiClient(config2);
190780
- const summary = await runExec(client, config2.dir, workflow2, examples, {
190781
- concurrencyOverride: opts.concurrency,
190782
- version: opts.version
190783
- });
190734
+ const workflowId = await resolveWorkflowId(client, workflow2);
190735
+ if (opts.version && !["latest", "current", "undefined"].includes(opts.version)) {
190736
+ warn(`--example runs the current published version; ignoring @${opts.version}.`);
190737
+ }
190738
+ const summary = await runWorkflowExamplesWithEval(client, config2.dir, workflow2, workflowId, examples);
190784
190739
  if (opts.json)
190785
190740
  console.log(JSON.stringify(summary, null, 2));
190786
- if (summary.failed > 0)
190741
+ if (summary.errored > 0)
190742
+ process.exit(1);
190743
+ if (opts.failOnMismatch && summary.failedCases > 0)
190787
190744
  process.exit(1);
190788
190745
  } catch (err2) {
190789
190746
  const msg = err2 instanceof Error ? err2.message : String(err2);
@@ -190796,7 +190753,23 @@ async function runSavedWorkflowExamples(workflow2, examples, opts) {
190796
190753
 
190797
190754
  // src/commands/run.ts
190798
190755
  function registerRunCommands(program2) {
190799
- addJsonFlag(withBaseUrl(program2.command("run <target>"))).description("Start a workflow or agent run, e.g. workflows.extract-invoice.").option("--input-json <json>", "JSON input object").option("--input-file <field=path>", 'Input file to upload as multipart form-data. Repeat for multiple files; bare paths use field "file".', collectRepeated, []).option("--example <name>", "Run one persisted example by name").option("--dir <dir>", "Local eigenpal directory for workflow examples", undefined).option("--wait", "Poll until the run reaches a terminal status").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).option("--max-wait <seconds>", "Maximum wait before exit code 2", intArg, 1800).action(action(runTarget));
190756
+ addJsonFlag(withBaseUrl(program2.command("run <target>"))).description("Start a workflow or agent run, e.g. workflows.extract-invoice.").option("--input-json <json>", "JSON input object").option("--input-file <field=path>", 'Input file to upload as multipart form-data. Repeat for multiple files; bare paths use field "file".', collectRepeated, []).option("--example <name>", "Run one persisted example by name").option("--dir <dir>", "Local eigenpal directory for workflow examples", undefined).option("--fail-on-mismatch", "For --example runs, exit non-zero when a graded example fails (evaluator fail, or output mismatch)").option("--wait", "Poll until the run reaches a terminal status").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).option("--max-wait <seconds>", "Maximum wait before exit code 2", intArg, 1800).action(action(runTarget)).addHelpText("after", `
190757
+ Grading --example runs
190758
+ --example runs the workflow's CURRENT published version on the SERVER's stored
190759
+ dataset example, so push your latest dataset/evaluators first
190760
+ (eigenpal workflow dataset push / evaluators push).
190761
+
190762
+ If the workflow has evaluators configured, they run automatically and the CLI
190763
+ shows the real weighted score and per-evaluator pass/fail. If it has no
190764
+ evaluators, the output is graded by a structural diff against the example's
190765
+ stored expected output (every expected field must be present and equal; extra
190766
+ output fields are ignored, arrays match by index). The summary reports two
190767
+ separate signals: execution health ("ok"/"errored") and accuracy.
190768
+
190769
+ Exit codes
190770
+ 1 a run errored (or, with --fail-on-mismatch, a graded example failed)
190771
+ By default a grading failure is informational and does not change the exit code.
190772
+ `);
190800
190773
  addJsonFlag(withBaseUrl(program2.command("rerun <run-id>"))).description("Create a new run from a previous run's stored input snapshot.").option("--version <version>", "Version/source ref for the new run: latest, original, or an explicit ref", "latest").option("--wait", "Poll until the rerun reaches a terminal status").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).option("--max-wait <seconds>", "Maximum wait before exit code 2", intArg, 1800).action(action(rerunTarget));
190801
190774
  }
190802
190775
  async function runTarget(target, opts) {
@@ -190814,7 +190787,8 @@ async function runTarget(target, opts) {
190814
190787
  }
190815
190788
  return runSavedWorkflowExamples(parsed.idOrSlug, [opts.example], {
190816
190789
  ...opts,
190817
- version: String(parsed.requestedVersion)
190790
+ version: String(parsed.requestedVersion),
190791
+ failOnMismatch: opts.failOnMismatch
190818
190792
  });
190819
190793
  }
190820
190794
  const client = buildClient(opts);
@@ -190859,73 +190833,73 @@ import {
190859
190833
  unlinkSync as unlinkSync2,
190860
190834
  writeFileSync as writeFileSync8
190861
190835
  } from "node:fs";
190862
- import { sep as PATH_SEP, dirname as dirname5, join as join11, relative as relative3, resolve as resolve5 } from "node:path";
190836
+ import { sep as PATH_SEP, dirname as dirname5, join as join10, relative as relative3, resolve as resolve5 } from "node:path";
190863
190837
  import { fileURLToPath as fileURLToPath6 } from "node:url";
190864
190838
 
190865
190839
  // src/commands/skill/tools.ts
190866
- import { join as join10 } from "node:path";
190840
+ import { join as join9 } from "node:path";
190867
190841
  var SKILL_NAME = "eigenpal";
190868
190842
  var TOOLS = [
190869
190843
  {
190870
190844
  id: "claude",
190871
190845
  label: "Claude Code",
190872
- relativePath: join10(".claude", "skills", SKILL_NAME),
190846
+ relativePath: join9(".claude", "skills", SKILL_NAME),
190873
190847
  detectPaths: [".claude", "CLAUDE.md"]
190874
190848
  },
190875
190849
  {
190876
190850
  id: "cursor",
190877
190851
  label: "Cursor",
190878
- relativePath: join10(".cursor", "skills", SKILL_NAME),
190852
+ relativePath: join9(".cursor", "skills", SKILL_NAME),
190879
190853
  detectPaths: [".cursor", ".cursorrules"]
190880
190854
  },
190881
190855
  {
190882
190856
  id: "codex",
190883
190857
  label: "Codex",
190884
- relativePath: join10(".codex", "skills", SKILL_NAME),
190858
+ relativePath: join9(".codex", "skills", SKILL_NAME),
190885
190859
  detectPaths: [".codex"]
190886
190860
  },
190887
190861
  {
190888
190862
  id: "gemini",
190889
190863
  label: "Gemini CLI",
190890
- relativePath: join10(".gemini", "skills", SKILL_NAME),
190864
+ relativePath: join9(".gemini", "skills", SKILL_NAME),
190891
190865
  detectPaths: [".gemini", "GEMINI.md"]
190892
190866
  },
190893
190867
  {
190894
190868
  id: "antigravity",
190895
190869
  label: "Antigravity",
190896
- relativePath: join10(".agent", "skills", SKILL_NAME),
190870
+ relativePath: join9(".agent", "skills", SKILL_NAME),
190897
190871
  detectPaths: [".agent"]
190898
190872
  },
190899
190873
  {
190900
190874
  id: "opencode",
190901
190875
  label: "OpenCode",
190902
- relativePath: join10(".opencode", "skills", SKILL_NAME),
190876
+ relativePath: join9(".opencode", "skills", SKILL_NAME),
190903
190877
  detectPaths: [".opencode"]
190904
190878
  },
190905
190879
  {
190906
190880
  id: "pi",
190907
190881
  label: "Pi",
190908
- relativePath: join10(".pi", "skills", SKILL_NAME),
190882
+ relativePath: join9(".pi", "skills", SKILL_NAME),
190909
190883
  detectPaths: [".pi"]
190910
190884
  },
190911
190885
  {
190912
190886
  id: "windsurf",
190913
190887
  label: "Windsurf",
190914
- relativePath: join10(".windsurf", "skills", SKILL_NAME),
190888
+ relativePath: join9(".windsurf", "skills", SKILL_NAME),
190915
190889
  detectPaths: [".windsurf", ".windsurfrules"]
190916
190890
  },
190917
190891
  {
190918
190892
  id: "github-copilot",
190919
190893
  label: "GitHub Copilot",
190920
- relativePath: join10(".github", "skills", SKILL_NAME),
190894
+ relativePath: join9(".github", "skills", SKILL_NAME),
190921
190895
  detectPaths: [
190922
- join10(".github", "copilot-instructions.md"),
190923
- join10(".github", "instructions"),
190924
- join10(".github", "prompts"),
190925
- join10(".github", "agents"),
190926
- join10(".github", "skills"),
190927
- join10(".github", "workflows", "copilot-setup-steps.yml"),
190928
- join10(".github", ".mcp.json")
190896
+ join9(".github", "copilot-instructions.md"),
190897
+ join9(".github", "instructions"),
190898
+ join9(".github", "prompts"),
190899
+ join9(".github", "agents"),
190900
+ join9(".github", "skills"),
190901
+ join9(".github", "workflows", "copilot-setup-steps.yml"),
190902
+ join9(".github", ".mcp.json")
190929
190903
  ]
190930
190904
  }
190931
190905
  ];
@@ -190945,7 +190919,7 @@ function resolveSkillSource() {
190945
190919
  ${distCandidate}`);
190946
190920
  }
190947
190921
  function defaultClaudeTarget() {
190948
- return join11(process.cwd(), TOOLS[0].relativePath);
190922
+ return join10(process.cwd(), TOOLS[0].relativePath);
190949
190923
  }
190950
190924
  function resolveSkillTarget(opts) {
190951
190925
  return opts.target ?? defaultClaudeTarget();
@@ -190973,8 +190947,8 @@ async function installSkill(opts) {
190973
190947
  let skipped = 0;
190974
190948
  let prompted = 0;
190975
190949
  for (const relativePath of sourceFiles) {
190976
- const sourcePath = join11(source2, relativePath);
190977
- const targetPath = join11(target, relativePath);
190950
+ const sourcePath = join10(source2, relativePath);
190951
+ const targetPath = join10(target, relativePath);
190978
190952
  const sourceBytes = templateSourceFile(relativePath, readFileSync14(sourcePath), cliVersion);
190979
190953
  const sourceHash = sha2562(sourceBytes);
190980
190954
  if (!existsSync15(targetPath)) {
@@ -191011,7 +190985,7 @@ async function installSkill(opts) {
191011
190985
  }
191012
190986
  const manifestEntries = [];
191013
190987
  for (const relativePath of sourceFiles) {
191014
- const targetPath = join11(target, relativePath);
190988
+ const targetPath = join10(target, relativePath);
191015
190989
  if (!existsSync15(targetPath))
191016
190990
  continue;
191017
190991
  manifestEntries.push({ path: relativePath, sha256: sha2562(readFileSync14(targetPath)) });
@@ -191021,7 +190995,7 @@ async function installSkill(opts) {
191021
190995
  installedAt: new Date().toISOString(),
191022
190996
  files: manifestEntries
191023
190997
  };
191024
- writeFileSync8(join11(target, MANIFEST_NAME), `${JSON.stringify(manifest, null, 2)}
190998
+ writeFileSync8(join10(target, MANIFEST_NAME), `${JSON.stringify(manifest, null, 2)}
191025
190999
  `);
191026
191000
  const displayTarget = relative3(process.cwd(), target) || target;
191027
191001
  success2(`Installed skill at ${ui.bold(displayTarget)} — ${written} written, ${skipped} unchanged${prompted > 0 ? `, ${prompted} prompted` : ""}.`);
@@ -191030,7 +191004,7 @@ async function uninstallSkill(opts) {
191030
191004
  const target = resolveSkillTarget(opts);
191031
191005
  const manifest = readManifest(target);
191032
191006
  if (!manifest) {
191033
- error51(`No manifest found at ${join11(target, MANIFEST_NAME)}; nothing to uninstall.`);
191007
+ error51(`No manifest found at ${join10(target, MANIFEST_NAME)}; nothing to uninstall.`);
191034
191008
  process.exit(1);
191035
191009
  }
191036
191010
  if (!opts.yes && process.stdin.isTTY) {
@@ -191063,7 +191037,7 @@ async function uninstallSkill(opts) {
191063
191037
  unlinkSync2(path15);
191064
191038
  removed += 1;
191065
191039
  }
191066
- rmSync2(join11(target, MANIFEST_NAME), { force: true });
191040
+ rmSync2(join10(target, MANIFEST_NAME), { force: true });
191067
191041
  pruneEmptyDirs(target);
191068
191042
  const displayTarget = relative3(process.cwd(), target) || target;
191069
191043
  success2(`Uninstalled skill from ${ui.bold(displayTarget)} — ${removed} removed${kept > 0 ? `, ${kept} user-edited kept` : ""}.`);
@@ -191124,14 +191098,14 @@ async function installSkillTools(opts) {
191124
191098
  for (const tool of toInstall) {
191125
191099
  await installSkill({
191126
191100
  source: opts.source,
191127
- target: join11(cwd, tool.relativePath),
191101
+ target: join10(cwd, tool.relativePath),
191128
191102
  force: opts.force,
191129
191103
  yes: opts.yes
191130
191104
  });
191131
191105
  }
191132
191106
  for (const tool of toUninstall) {
191133
191107
  await uninstallSkill({
191134
- target: join11(cwd, tool.relativePath),
191108
+ target: join10(cwd, tool.relativePath),
191135
191109
  yes: true
191136
191110
  });
191137
191111
  }
@@ -191194,7 +191168,7 @@ async function uninstallSkillTools(opts) {
191194
191168
  }
191195
191169
  for (const tool of targets) {
191196
191170
  await uninstallSkill({
191197
- target: join11(cwd, tool.relativePath),
191171
+ target: join10(cwd, tool.relativePath),
191198
191172
  yes: true
191199
191173
  });
191200
191174
  }
@@ -191202,7 +191176,7 @@ async function uninstallSkillTools(opts) {
191202
191176
  function listSkillTools(opts = {}) {
191203
191177
  const cwd = process.cwd();
191204
191178
  const rows = TOOLS.flatMap((tool) => {
191205
- const targetDir = join11(cwd, tool.relativePath);
191179
+ const targetDir = join10(cwd, tool.relativePath);
191206
191180
  const manifest = readManifest(targetDir);
191207
191181
  if (!manifest)
191208
191182
  return [];
@@ -191255,10 +191229,10 @@ function hintFor(tool, installed, detected) {
191255
191229
  return tool.relativePath;
191256
191230
  }
191257
191231
  function isInstalled(cwd, tool) {
191258
- return existsSync15(join11(cwd, tool.relativePath, MANIFEST_NAME));
191232
+ return existsSync15(join10(cwd, tool.relativePath, MANIFEST_NAME));
191259
191233
  }
191260
191234
  function isProjectUsing(cwd, tool) {
191261
- return tool.detectPaths.some((p4) => existsSync15(join11(cwd, p4)));
191235
+ return tool.detectPaths.some((p4) => existsSync15(join10(cwd, p4)));
191262
191236
  }
191263
191237
  function listFiles(root2) {
191264
191238
  const out = [];
@@ -191266,7 +191240,7 @@ function listFiles(root2) {
191266
191240
  while (stack2.length > 0) {
191267
191241
  const dir = stack2.pop();
191268
191242
  for (const entry of readdirSync5(dir)) {
191269
- const full = join11(dir, entry);
191243
+ const full = join10(dir, entry);
191270
191244
  const rel = relative3(root2, full);
191271
191245
  const stats = lstatSync(full);
191272
191246
  if (stats.isDirectory()) {
@@ -191281,7 +191255,7 @@ function listFiles(root2) {
191281
191255
  return out.sort();
191282
191256
  }
191283
191257
  function readManifest(target) {
191284
- const path15 = join11(target, MANIFEST_NAME);
191258
+ const path15 = join10(target, MANIFEST_NAME);
191285
191259
  if (!existsSync15(path15))
191286
191260
  return null;
191287
191261
  try {
@@ -191316,7 +191290,7 @@ function pruneEmptyDirs(root2) {
191316
191290
  const stack2 = [];
191317
191291
  const collect = (dir) => {
191318
191292
  for (const entry of readdirSync5(dir)) {
191319
- const full = join11(dir, entry);
191293
+ const full = join10(dir, entry);
191320
191294
  if (lstatSync(full).isDirectory())
191321
191295
  collect(full);
191322
191296
  }
@@ -191462,12 +191436,12 @@ import { createInterface as createInterface3 } from "node:readline/promises";
191462
191436
 
191463
191437
  // src/commands/workflow/clear.ts
191464
191438
  import { existsSync as existsSync16, readdirSync as readdirSync6, rmSync as rmSync3, statSync as statSync5 } from "fs";
191465
- import { basename, join as join12 } from "path";
191439
+ import { basename, join as join11 } from "path";
191466
191440
  var EMPTY_REPORT = { removed: 0, kept: 0, touched: 0 };
191467
191441
  function getDirectoryEntries(path15) {
191468
191442
  if (!existsSync16(path15))
191469
191443
  return [];
191470
- return readdirSync6(path15).filter((entry) => statSync5(join12(path15, entry)).isDirectory()).sort();
191444
+ return readdirSync6(path15).filter((entry) => statSync5(join11(path15, entry)).isDirectory()).sort();
191471
191445
  }
191472
191446
  function removeDirEntriesWithRetention(path15, removeAll) {
191473
191447
  const entries = getDirectoryEntries(path15);
@@ -191480,7 +191454,7 @@ function removeDirEntriesWithRetention(path15, removeAll) {
191480
191454
  for (const entry of entries) {
191481
191455
  if (keepSet.has(entry))
191482
191456
  continue;
191483
- rmSync3(join12(path15, entry), { recursive: true, force: true });
191457
+ rmSync3(join11(path15, entry), { recursive: true, force: true });
191484
191458
  removed++;
191485
191459
  }
191486
191460
  return { removed, kept: keepSet.size, touched: entries.length };
@@ -191507,7 +191481,7 @@ function removeJudgeSummaryFilesWithRetention(exampleDir, removeAll) {
191507
191481
  kept++;
191508
191482
  continue;
191509
191483
  }
191510
- rmSync3(join12(exampleDir, fileName), { force: true });
191484
+ rmSync3(join11(exampleDir, fileName), { force: true });
191511
191485
  removed++;
191512
191486
  }
191513
191487
  return { removed, kept, touched: stampByFile.size };
@@ -191515,17 +191489,17 @@ function removeJudgeSummaryFilesWithRetention(exampleDir, removeAll) {
191515
191489
  function getTargetExampleDirs(dir, exampleNames) {
191516
191490
  const baseDir = resolveEvalBaseDir(dir);
191517
191491
  if (!baseDir) {
191518
- error51(`No dataset/examples directory at ${join12(dir, "dataset", "examples")}`);
191492
+ error51(`No dataset/examples directory at ${join11(dir, "dataset", "examples")}`);
191519
191493
  process.exit(1);
191520
191494
  }
191521
- const all = readdirSync6(baseDir).filter((entry) => statSync5(join12(baseDir, entry)).isDirectory());
191495
+ const all = readdirSync6(baseDir).filter((entry) => statSync5(join11(baseDir, entry)).isDirectory());
191522
191496
  if (exampleNames.length === 0)
191523
- return all.map((name) => join12(baseDir, name));
191497
+ return all.map((name) => join11(baseDir, name));
191524
191498
  const matched = all.filter((name) => exampleNames.includes(name));
191525
191499
  if (matched.length === 0) {
191526
191500
  warn(`None of [${exampleNames.join(", ")}] match an example under ./dataset/examples/. ` + `Heads up: the \`clear-local\` positional changed — it used to take a workflow slug, ` + `now it takes example names (or omit to clean every example).`);
191527
191501
  }
191528
- return matched.map((name) => join12(baseDir, name));
191502
+ return matched.map((name) => join11(baseDir, name));
191529
191503
  }
191530
191504
  function runSection(args) {
191531
191505
  let removed = 0;
@@ -191557,8 +191531,8 @@ async function clearEvalOutputs(dir, exampleNames = [], options8 = {}) {
191557
191531
  const exampleDirs = getTargetExampleDirs(dir, exampleNames);
191558
191532
  runSection({
191559
191533
  exampleDirs,
191560
- clean: (exampleDir) => removeDirEntriesWithRetention(join12(exampleDir, "executions"), removeAll),
191561
- shouldCount: (exampleDir) => existsSync16(join12(exampleDir, "executions")),
191534
+ clean: (exampleDir) => removeDirEntriesWithRetention(join11(exampleDir, "executions"), removeAll),
191535
+ shouldCount: (exampleDir) => existsSync16(join11(exampleDir, "executions")),
191562
191536
  successLabel: "Execution cleanup complete",
191563
191537
  scannedLabel: "examples scanned:",
191564
191538
  seenLabel: "runs seen: ",
@@ -192133,7 +192107,7 @@ function looksLikeFreeform(prop) {
192133
192107
  // src/commands/workflow/validation.ts
192134
192108
  var import_yaml8 = __toESM(require_dist4(), 1);
192135
192109
  import { existsSync as existsSync17, readdirSync as readdirSync7, readFileSync as readFileSync15, statSync as statSync6 } from "fs";
192136
- import { join as join13, resolve as resolve6 } from "path";
192110
+ import { join as join12, resolve as resolve6 } from "path";
192137
192111
  var NAME_RE = /^[a-z0-9][a-z0-9-_]*$/;
192138
192112
  function isDirSafe(path15, issues, field) {
192139
192113
  try {
@@ -192253,19 +192227,19 @@ function validateDatasetFolder(root2) {
192253
192227
  if (!existsSync17(root2)) {
192254
192228
  return [{ field: root2, message: "Folder not found." }];
192255
192229
  }
192256
- if (existsSync17(join13(root2, "manifest.json"))) {
192230
+ if (existsSync17(join12(root2, "manifest.json"))) {
192257
192231
  issues.push({
192258
192232
  field: "manifest.json",
192259
192233
  message: "Top-level manifest.json is the legacy format. Re-export the dataset using the folder convention."
192260
192234
  });
192261
192235
  }
192262
- const examplesDir = join13(root2, "examples");
192236
+ const examplesDir = join12(root2, "examples");
192263
192237
  if (!existsSync17(examplesDir)) {
192264
192238
  issues.push({ field: "examples/", message: "No examples/ directory found at dataset root." });
192265
192239
  return issues;
192266
192240
  }
192267
192241
  for (const exampleName of readdirSync7(examplesDir)) {
192268
- const exampleFolder = join13(examplesDir, exampleName);
192242
+ const exampleFolder = join12(examplesDir, exampleName);
192269
192243
  if (!isDirSafe(exampleFolder, issues, `examples/${exampleName}`))
192270
192244
  continue;
192271
192245
  if (!NAME_RE.test(exampleName)) {
@@ -192275,7 +192249,7 @@ function validateDatasetFolder(root2) {
192275
192249
  });
192276
192250
  continue;
192277
192251
  }
192278
- const inputJsonPath = join13(exampleFolder, "input.json");
192252
+ const inputJsonPath = join12(exampleFolder, "input.json");
192279
192253
  const hasInputJson = existsSync17(inputJsonPath);
192280
192254
  if (!hasInputJson) {
192281
192255
  issues.push({
@@ -192302,8 +192276,8 @@ function validateDatasetFolder(root2) {
192302
192276
  });
192303
192277
  continue;
192304
192278
  }
192305
- const inputDir = join13(exampleFolder, "input");
192306
- if (existsSync17(join13(inputDir, "arguments.json"))) {
192279
+ const inputDir = join12(exampleFolder, "input");
192280
+ if (existsSync17(join12(inputDir, "arguments.json"))) {
192307
192281
  issues.push({
192308
192282
  field: `examples/${exampleName}/input/arguments.json`,
192309
192283
  message: "Legacy input/arguments.json is no longer accepted. Use input.json instead."
@@ -192318,8 +192292,8 @@ function validateDatasetFolder(root2) {
192318
192292
  })) {
192319
192293
  issues.push(issue2);
192320
192294
  }
192321
- const expectedDir = join13(exampleFolder, "expected");
192322
- const expectedJsonPath = join13(exampleFolder, "expected.json");
192295
+ const expectedDir = join12(exampleFolder, "expected");
192296
+ const expectedJsonPath = join12(exampleFolder, "expected.json");
192323
192297
  if (existsSync17(expectedDir)) {
192324
192298
  for (const entry of readdirSync7(expectedDir)) {
192325
192299
  if (entry === "output.json" || entry === "error.json") {
@@ -192329,7 +192303,7 @@ function validateDatasetFolder(root2) {
192329
192303
  });
192330
192304
  continue;
192331
192305
  }
192332
- const sub = join13(expectedDir, entry);
192306
+ const sub = join12(expectedDir, entry);
192333
192307
  const field = `examples/${exampleName}/expected/${entry}`;
192334
192308
  let isDir;
192335
192309
  try {
@@ -192385,7 +192359,7 @@ function validateDatasetFolder(root2) {
192385
192359
  } else {
192386
192360
  for (const issue2 of validateFileRefs({
192387
192361
  refs: collectDatasetFileRefs(parsed),
192388
- filesRoot: join13(exampleFolder, "expected"),
192362
+ filesRoot: join12(exampleFolder, "expected"),
192389
192363
  root: "expected",
192390
192364
  fieldPrefix: `examples/${exampleName}/expected.json`
192391
192365
  })) {
@@ -192438,7 +192412,7 @@ function validateFileRefs(args) {
192438
192412
  });
192439
192413
  continue;
192440
192414
  }
192441
- if (!existsSync17(join13(args.filesRoot, relative4))) {
192415
+ if (!existsSync17(join12(args.filesRoot, relative4))) {
192442
192416
  issues.push({
192443
192417
  field: `${args.fieldPrefix}:${ref.jsonPath}`,
192444
192418
  message: `Referenced file does not exist: ${ref.path}.`
@@ -192492,7 +192466,7 @@ Note
192492
192466
  return;
192493
192467
  }
192494
192468
  const root2 = rootDir(opts);
192495
- const wfFile = join13(root2, "workflow.yaml");
192469
+ const wfFile = join12(root2, "workflow.yaml");
192496
192470
  const wfResult = validateWorkflowYaml(wfFile);
192497
192471
  const wfPath = relPath(root2, wfFile);
192498
192472
  const wfOk = printIssues("workflow.yaml", wfPath, wfResult.issues);
@@ -192500,8 +192474,8 @@ Note
192500
192474
  printSchemaQualityWarnings(wfPath, wfResult.warnings);
192501
192475
  printInvokeOnlineHint(wfResult.invokeStepCount, opts.online);
192502
192476
  }
192503
- const evOk = printIssues("evaluators.yaml", relPath(root2, join13(root2, "evaluators.yaml")), validateEvaluatorsYaml(join13(root2, "evaluators.yaml")));
192504
- const dsOk = printIssues("dataset/", relPath(root2, join13(root2, "dataset")), validateDatasetFolder(join13(root2, "dataset")));
192477
+ const evOk = printIssues("evaluators.yaml", relPath(root2, join12(root2, "evaluators.yaml")), validateEvaluatorsYaml(join12(root2, "evaluators.yaml")));
192478
+ const dsOk = printIssues("dataset/", relPath(root2, join12(root2, "dataset")), validateDatasetFolder(join12(root2, "dataset")));
192505
192479
  let onlineOk = true;
192506
192480
  if (wfOk && opts.online && existsSync17(wfFile)) {
192507
192481
  onlineOk = printIssues("invoke refs (server)", wfPath, await onlineInvokeIssues(readFileSync15(wfFile, "utf-8"), opts));
@@ -192581,7 +192555,7 @@ async function writeOrPrint(out, content) {
192581
192555
  }
192582
192556
  async function readDirAsZip2(dir) {
192583
192557
  const tree = {};
192584
- async function walk2(current, relPrefix) {
192558
+ async function walk3(current, relPrefix) {
192585
192559
  const entries = await fs17.readdir(current, { withFileTypes: true });
192586
192560
  for (const e8 of entries) {
192587
192561
  if (e8.name.startsWith(".") || e8.name === "node_modules")
@@ -192589,12 +192563,12 @@ async function readDirAsZip2(dir) {
192589
192563
  const full = path15.join(current, e8.name);
192590
192564
  const rel = relPrefix ? `${relPrefix}/${e8.name}` : e8.name;
192591
192565
  if (e8.isDirectory())
192592
- await walk2(full, rel);
192566
+ await walk3(full, rel);
192593
192567
  else if (e8.isFile())
192594
192568
  tree[rel] = new Uint8Array(await fs17.readFile(full));
192595
192569
  }
192596
192570
  }
192597
- await walk2(dir, "");
192571
+ await walk3(dir, "");
192598
192572
  if (Object.keys(tree).length === 0) {
192599
192573
  throw new Error(`directory is empty: ${dir}`);
192600
192574
  }
@@ -18,6 +18,7 @@ Start a workflow or agent run, e.g. workflows.extract-invoice.
18
18
  | `--input-file <field=path>` | no | `[]` | Input file to upload as multipart form-data. Repeat for multiple files; bare paths use field "file". |
19
19
  | `--example <name>` | no | | Run one persisted example by name |
20
20
  | `--dir <dir>` | no | | Local eigenpal directory for workflow examples |
21
+ | `--fail-on-mismatch` | no | | For --example runs, exit non-zero when a graded example fails (evaluator fail, or output mismatch) |
21
22
  | `--wait` | no | | Poll until the run reaches a terminal status |
22
23
  | `--interval <seconds>` | no | `2` | Polling interval in seconds |
23
24
  | `--max-wait <seconds>` | no | `1800` | Maximum wait before exit code 2 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eigenpal/cli",
3
- "version": "0.10.14",
3
+ "version": "0.10.16",
4
4
  "description": "Eigenpal CLI — author and run document workflows from your terminal. Agent-ready.",
5
5
  "keywords": [
6
6
  "eigenpal",