@eigenpal/cli 0.10.15 → 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 +17 -0
- package/dist/cli.js +361 -393
- package/dist/skill/reference/cli/run.md +1 -0
- package/package.json +1 -1
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
|
@@ -183722,7 +183722,7 @@ import { fileURLToPath as fileURLToPath8 } from "url";
|
|
|
183722
183722
|
// package.json
|
|
183723
183723
|
var package_default = {
|
|
183724
183724
|
name: "@eigenpal/cli",
|
|
183725
|
-
version: "0.10.
|
|
183725
|
+
version: "0.10.16",
|
|
183726
183726
|
description: "Eigenpal CLI — author and run document workflows from your terminal. Agent-ready.",
|
|
183727
183727
|
keywords: [
|
|
183728
183728
|
"eigenpal",
|
|
@@ -184205,9 +184205,6 @@ function resolveProfileKey(name, profiles) {
|
|
|
184205
184205
|
|
|
184206
184206
|
// src/lib/ui.ts
|
|
184207
184207
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
184208
|
-
function isTTY() {
|
|
184209
|
-
return Boolean(process.stdout.isTTY);
|
|
184210
|
-
}
|
|
184211
184208
|
var ui = {
|
|
184212
184209
|
ok: import_picocolors.default.green,
|
|
184213
184210
|
err: import_picocolors.default.red,
|
|
@@ -190289,10 +190286,6 @@ function formatEigenpalDirIfAvailable(dir) {
|
|
|
190289
190286
|
});
|
|
190290
190287
|
}
|
|
190291
190288
|
|
|
190292
|
-
// src/commands/workflow/exec.ts
|
|
190293
|
-
init_src();
|
|
190294
|
-
import { join as join9 } from "path";
|
|
190295
|
-
|
|
190296
190289
|
// src/lib/execution-artifacts.ts
|
|
190297
190290
|
import { existsSync as existsSync13, mkdirSync as mkdirSync5, readdirSync as readdirSync3, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
|
|
190298
190291
|
import { join as join7 } from "path";
|
|
@@ -190418,363 +190411,318 @@ async function writeExecutionArtifacts(client, exampleDir, payload) {
|
|
|
190418
190411
|
return executionsDir;
|
|
190419
190412
|
}
|
|
190420
190413
|
|
|
190421
|
-
// src/lib/
|
|
190422
|
-
|
|
190423
|
-
|
|
190424
|
-
|
|
190425
|
-
|
|
190426
|
-
|
|
190427
|
-
|
|
190428
|
-
|
|
190429
|
-
if (existsSync14(candidate))
|
|
190430
|
-
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);
|
|
190431
190422
|
}
|
|
190432
|
-
|
|
190423
|
+
if (text === undefined)
|
|
190424
|
+
text = String(value);
|
|
190425
|
+
return text.length > MAX_VALUE_LEN ? `${text.slice(0, MAX_VALUE_LEN)}…` : text;
|
|
190433
190426
|
}
|
|
190434
|
-
function
|
|
190435
|
-
|
|
190436
|
-
if (!baseDir) {
|
|
190437
|
-
throw new Error(`No dataset/examples directory found at ${join8(dir, ...DATASET_EXAMPLES_DIR)}. Run \`eigenpal init workflow <name>\` to scaffold one.`);
|
|
190438
|
-
}
|
|
190439
|
-
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 }));
|
|
190440
|
-
if (!exampleNamesFilter?.length)
|
|
190441
|
-
return all;
|
|
190442
|
-
return all.filter((n5) => exampleNamesFilter.includes(n5));
|
|
190427
|
+
function isPlainObject3(value) {
|
|
190428
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
190443
190429
|
}
|
|
190444
|
-
function
|
|
190445
|
-
|
|
190446
|
-
return readCanonicalInput(exampleDir, canonicalPath);
|
|
190430
|
+
function joinPath(path15, key2) {
|
|
190431
|
+
return path15 ? `${path15}.${key2}` : key2;
|
|
190447
190432
|
}
|
|
190448
|
-
function
|
|
190449
|
-
|
|
190450
|
-
|
|
190451
|
-
|
|
190452
|
-
|
|
190453
|
-
const scalars = {};
|
|
190454
|
-
const files = [];
|
|
190455
|
-
for (const [key2, value] of Object.entries(raw)) {
|
|
190456
|
-
const refs = topLevelFileRefs(value);
|
|
190457
|
-
if (refs.length === 0) {
|
|
190458
|
-
scalars[key2] = materializeNestedFileRefs(exampleDir, inputPath, value);
|
|
190459
|
-
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;
|
|
190460
190438
|
}
|
|
190461
|
-
|
|
190462
|
-
|
|
190463
|
-
if (!validation2.ok) {
|
|
190464
|
-
throw new Error(`${inputPath}: ${key2} must reference files under input/ without path traversal.`);
|
|
190465
|
-
}
|
|
190466
|
-
const relative3 = validation2.value.segments.slice(1).join("/");
|
|
190467
|
-
const filePath = join8(exampleDir, validation2.value.path);
|
|
190468
|
-
const filename = validation2.value.segments.at(-1) ?? relative3;
|
|
190469
|
-
files.push({
|
|
190470
|
-
argument: key2,
|
|
190471
|
-
filename,
|
|
190472
|
-
content: readFileSync13(filePath),
|
|
190473
|
-
mimeType: guessMimeType(filename)
|
|
190474
|
-
});
|
|
190439
|
+
if (expected.length !== actual.length) {
|
|
190440
|
+
diffs.push(`${path15 || "(root)"}: length ${expected.length} != ${actual.length}`);
|
|
190475
190441
|
}
|
|
190476
|
-
|
|
190477
|
-
|
|
190478
|
-
}
|
|
190479
|
-
function materializeNestedFileRefs(exampleDir, inputPath, value) {
|
|
190480
|
-
if (isScopedFileRef2(value)) {
|
|
190481
|
-
const validation2 = validateScopedArtifactPath(value.$file, { allowedRoots: ["input"] });
|
|
190482
|
-
if (!validation2.ok) {
|
|
190483
|
-
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);
|
|
190484
190445
|
}
|
|
190485
|
-
|
|
190486
|
-
const filePath = join8(exampleDir, validation2.value.path);
|
|
190487
|
-
return {
|
|
190488
|
-
$inline: {
|
|
190489
|
-
filename,
|
|
190490
|
-
mimeType: guessMimeType(filename) ?? "application/octet-stream",
|
|
190491
|
-
base64: readFileSync13(filePath).toString("base64")
|
|
190492
|
-
}
|
|
190493
|
-
};
|
|
190494
|
-
}
|
|
190495
|
-
if (Array.isArray(value)) {
|
|
190496
|
-
return value.map((item) => materializeNestedFileRefs(exampleDir, inputPath, item));
|
|
190446
|
+
return;
|
|
190497
190447
|
}
|
|
190498
|
-
if (
|
|
190499
|
-
if (
|
|
190500
|
-
|
|
190448
|
+
if (isPlainObject3(expected)) {
|
|
190449
|
+
if (!isPlainObject3(actual)) {
|
|
190450
|
+
diffs.push(`${path15 || "(root)"}: expected object, got ${preview(actual)}`);
|
|
190451
|
+
return;
|
|
190501
190452
|
}
|
|
190502
|
-
const
|
|
190503
|
-
|
|
190504
|
-
|
|
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);
|
|
190505
190460
|
}
|
|
190506
|
-
return
|
|
190507
|
-
}
|
|
190508
|
-
return value;
|
|
190509
|
-
}
|
|
190510
|
-
function topLevelFileRefs(value) {
|
|
190511
|
-
if (isScopedFileRef2(value))
|
|
190512
|
-
return [value.$file];
|
|
190513
|
-
if (hasFileKey(value)) {
|
|
190514
|
-
throw new Error('file refs must be exact { "$file": "input/..." } objects.');
|
|
190461
|
+
return;
|
|
190515
190462
|
}
|
|
190516
|
-
if (
|
|
190517
|
-
|
|
190518
|
-
if (Array.isArray(value) && value.some(hasFileKey)) {
|
|
190519
|
-
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)}`);
|
|
190520
190465
|
}
|
|
190521
|
-
return [];
|
|
190522
190466
|
}
|
|
190523
|
-
function
|
|
190524
|
-
|
|
190525
|
-
|
|
190526
|
-
|
|
190527
|
-
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 };
|
|
190528
190471
|
}
|
|
190529
|
-
|
|
190530
|
-
|
|
190531
|
-
|
|
190532
|
-
|
|
190533
|
-
|
|
190534
|
-
|
|
190535
|
-
|
|
190536
|
-
|
|
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;
|
|
190537
190482
|
}
|
|
190538
190483
|
return null;
|
|
190539
190484
|
}
|
|
190540
|
-
function buildExamplePayload(exampleDir) {
|
|
190541
|
-
const { scalars, files } = readInput(exampleDir);
|
|
190542
|
-
return {
|
|
190543
|
-
scalars,
|
|
190544
|
-
files,
|
|
190545
|
-
overrides: readOverrides(exampleDir)
|
|
190546
|
-
};
|
|
190547
|
-
}
|
|
190548
190485
|
|
|
190549
|
-
// src/
|
|
190550
|
-
var
|
|
190551
|
-
var
|
|
190552
|
-
|
|
190553
|
-
|
|
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";
|
|
190554
190495
|
}
|
|
190555
|
-
function
|
|
190556
|
-
if (
|
|
190557
|
-
|
|
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;
|
|
190558
190504
|
}
|
|
190559
|
-
function
|
|
190560
|
-
|
|
190561
|
-
process.stdout.write(`\x1B[${n5}B`);
|
|
190505
|
+
function runOutput(run) {
|
|
190506
|
+
return run.output !== undefined ? run.output : run.result?.output;
|
|
190562
190507
|
}
|
|
190563
|
-
function
|
|
190564
|
-
const
|
|
190565
|
-
|
|
190566
|
-
writeLineInPlace(text);
|
|
190567
|
-
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;
|
|
190568
190511
|
}
|
|
190569
|
-
function
|
|
190570
|
-
|
|
190571
|
-
|
|
190572
|
-
|
|
190573
|
-
|
|
190574
|
-
|
|
190575
|
-
|
|
190576
|
-
|
|
190577
|
-
|
|
190578
|
-
|
|
190579
|
-
|
|
190580
|
-
|
|
190581
|
-
|
|
190582
|
-
|
|
190583
|
-
|
|
190584
|
-
|
|
190585
|
-
|
|
190586
|
-
|
|
190587
|
-
}
|
|
190588
|
-
|
|
190589
|
-
|
|
190590
|
-
|
|
190591
|
-
|
|
190592
|
-
|
|
190593
|
-
process.stdout.write(`${label2} ${wait}
|
|
190594
|
-
`);
|
|
190595
|
-
}
|
|
190596
|
-
};
|
|
190597
|
-
const setRunning = (lineIndex, customLabel) => {
|
|
190598
|
-
if (!interactive)
|
|
190599
|
-
return;
|
|
190600
|
-
runningLabels.set(lineIndex, customLabel ?? runningLabel);
|
|
190601
|
-
if (spinnerInterval === null) {
|
|
190602
|
-
spinnerInterval = setInterval(() => {
|
|
190603
|
-
updateLock = updateLock.then(() => {
|
|
190604
|
-
if (runningLabels.size === 0) {
|
|
190605
|
-
if (spinnerInterval) {
|
|
190606
|
-
clearInterval(spinnerInterval);
|
|
190607
|
-
spinnerInterval = null;
|
|
190608
|
-
}
|
|
190609
|
-
return;
|
|
190610
|
-
}
|
|
190611
|
-
const frame = SPINNER_FRAMES[spinnerFrame++ % SPINNER_FRAMES.length];
|
|
190612
|
-
for (const [i5, label2] of runningLabels) {
|
|
190613
|
-
const lineLabel = lineLabels[i5] ?? "";
|
|
190614
|
-
updateLineAt(i5, totalLines, `${lineLabel} ${label2} ${spinnerStyle(frame)}`);
|
|
190615
|
-
}
|
|
190616
|
-
});
|
|
190617
|
-
}, 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`);
|
|
190618
190536
|
}
|
|
190619
|
-
|
|
190620
|
-
|
|
190621
|
-
if (interactive) {
|
|
190622
|
-
runningLabels.delete(lineIndex);
|
|
190623
|
-
await scheduleUpdate(() => updateLineAt(lineIndex, totalLines, text));
|
|
190624
|
-
for (const line3 of extraLines)
|
|
190625
|
-
console.log(line3);
|
|
190626
|
-
} else {
|
|
190627
|
-
console.log(text);
|
|
190628
|
-
for (const line3 of extraLines)
|
|
190629
|
-
console.log(line3);
|
|
190537
|
+
if (Date.now() - start > RUN_MAX_WAIT_MS) {
|
|
190538
|
+
return { ...run, status: "timeout", execution: { status: "timeout" } };
|
|
190630
190539
|
}
|
|
190631
|
-
|
|
190632
|
-
|
|
190633
|
-
totalLines,
|
|
190634
|
-
start,
|
|
190635
|
-
setRunning,
|
|
190636
|
-
setDone
|
|
190637
|
-
};
|
|
190638
|
-
}
|
|
190639
|
-
|
|
190640
|
-
// src/commands/workflow/exec.ts
|
|
190641
|
-
var POLL_INTERVAL_MS = 2000;
|
|
190642
|
-
var MAX_POLL_MS = 5 * 60 * 1000;
|
|
190643
|
-
var DEFAULT_CONCURRENCY = 3;
|
|
190644
|
-
function runTerminalOutput(run) {
|
|
190645
|
-
return run.output !== undefined ? run.output : run.result?.output;
|
|
190646
|
-
}
|
|
190647
|
-
function runTerminalError(run) {
|
|
190648
|
-
const error52 = run.error !== undefined ? run.error : run.result?.error;
|
|
190649
|
-
return error52 ?? undefined;
|
|
190540
|
+
await new Promise((r5) => setTimeout(r5, RUN_POLL_INTERVAL_MS));
|
|
190541
|
+
}
|
|
190650
190542
|
}
|
|
190651
|
-
async function
|
|
190543
|
+
async function pollEvalRollup(client, runId) {
|
|
190652
190544
|
const start = Date.now();
|
|
190653
|
-
|
|
190654
|
-
const run = await client.get(`/api/v1/runs/${
|
|
190655
|
-
|
|
190656
|
-
|
|
190657
|
-
return {
|
|
190658
|
-
executionId: run.id,
|
|
190659
|
-
status: status ?? (run.error ? "failed" : "completed"),
|
|
190660
|
-
output: runTerminalOutput(run),
|
|
190661
|
-
error: runTerminalError(run)
|
|
190662
|
-
};
|
|
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 };
|
|
190663
190549
|
}
|
|
190664
|
-
|
|
190550
|
+
if (Date.now() - start > EVAL_GRACE_MS)
|
|
190551
|
+
return null;
|
|
190552
|
+
await new Promise((r5) => setTimeout(r5, EVAL_POLL_INTERVAL_MS));
|
|
190665
190553
|
}
|
|
190666
|
-
return { executionId, status: "timeout", error: "Execution timed out" };
|
|
190667
190554
|
}
|
|
190668
|
-
function
|
|
190669
|
-
const
|
|
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) {
|
|
190670
190560
|
return {
|
|
190671
|
-
executionId:
|
|
190672
|
-
status,
|
|
190561
|
+
executionId: runId,
|
|
190562
|
+
status: run.execution?.status ?? run.status ?? "unknown",
|
|
190673
190563
|
createdAt: run.timing?.createdAt ?? new Date().toISOString(),
|
|
190674
190564
|
completedAt: run.timing?.completedAt ?? null,
|
|
190675
190565
|
error: run.error !== undefined ? run.error : run.result?.error ?? null,
|
|
190676
|
-
output:
|
|
190677
|
-
stepExecutions:
|
|
190566
|
+
output: runOutput(run),
|
|
190567
|
+
stepExecutions: []
|
|
190678
190568
|
};
|
|
190679
190569
|
}
|
|
190680
|
-
async function
|
|
190681
|
-
const
|
|
190682
|
-
const
|
|
190683
|
-
|
|
190684
|
-
|
|
190685
|
-
const requested = options8.concurrencyOverride ?? DEFAULT_CONCURRENCY;
|
|
190686
|
-
const requestedValid = Number.isFinite(requested) && requested > 0 ? requested : DEFAULT_CONCURRENCY;
|
|
190687
|
-
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.");
|
|
190688
190575
|
const evalBaseDir = resolveEvalBaseDir(dir);
|
|
190689
|
-
|
|
190690
|
-
|
|
190691
|
-
|
|
190692
|
-
dim("Using unified run endpoint.");
|
|
190693
|
-
const interactive = isTTY();
|
|
190694
|
-
const progress = createProgressLines({
|
|
190695
|
-
interactive,
|
|
190696
|
-
lineLabels: names,
|
|
190697
|
-
waitingLabel: "waiting",
|
|
190698
|
-
runningLabel: "running",
|
|
190699
|
-
dim: ui.dim,
|
|
190700
|
-
spinnerStyle: ui.info
|
|
190701
|
-
});
|
|
190702
|
-
progress.start();
|
|
190703
|
-
let passed = 0;
|
|
190704
|
-
let failed = 0;
|
|
190705
|
-
let nextIndex = 0;
|
|
190706
|
-
const reportDone = (index, label2) => {
|
|
190707
|
-
if (interactive)
|
|
190708
|
-
progress.setDone(index, label2);
|
|
190709
|
-
else
|
|
190710
|
-
console.log(label2);
|
|
190711
|
-
};
|
|
190712
|
-
const runOne = async () => {
|
|
190713
|
-
while (true) {
|
|
190714
|
-
const index = nextIndex++;
|
|
190715
|
-
if (index >= names.length)
|
|
190716
|
-
return;
|
|
190717
|
-
const name = names[index];
|
|
190718
|
-
const exampleDir = join9(evalBaseDir, name);
|
|
190719
|
-
try {
|
|
190720
|
-
const example = buildExamplePayload(exampleDir);
|
|
190721
|
-
if (interactive)
|
|
190722
|
-
progress.setRunning(index);
|
|
190723
|
-
else
|
|
190724
|
-
console.log(`${ui.dim("→")} ${name}`);
|
|
190725
|
-
const form = await buildRunFormData({
|
|
190726
|
-
target: runStartMultipartTarget({
|
|
190727
|
-
type: "workflow",
|
|
190728
|
-
id: workflowId,
|
|
190729
|
-
version: options8.version
|
|
190730
|
-
}),
|
|
190731
|
-
input: example.scalars,
|
|
190732
|
-
overrides: example.overrides,
|
|
190733
|
-
files: example.files.map((file2) => ({
|
|
190734
|
-
fieldName: file2.argument,
|
|
190735
|
-
content: file2.content,
|
|
190736
|
-
filename: file2.filename,
|
|
190737
|
-
mimeType: file2.mimeType
|
|
190738
|
-
}))
|
|
190739
|
-
});
|
|
190740
|
-
const res = await client.postFormData(runTargetApiPath({ type: "workflow", id: workflowId, version: options8.version }), form);
|
|
190741
|
-
if (typeof res?.id !== "string") {
|
|
190742
|
-
throw new Error("Run API did not return a run id");
|
|
190743
|
-
}
|
|
190744
|
-
const executionId = res.id;
|
|
190745
|
-
process.stderr.write(` ${ui.dim(`→ ${name}: ${executionId}`)}
|
|
190576
|
+
const results = [];
|
|
190577
|
+
for (const name of exampleNames) {
|
|
190578
|
+
process.stderr.write(`${ui.dim("→")} ${name}
|
|
190746
190579
|
`);
|
|
190747
|
-
|
|
190748
|
-
|
|
190749
|
-
|
|
190750
|
-
|
|
190751
|
-
|
|
190752
|
-
|
|
190753
|
-
|
|
190754
|
-
|
|
190755
|
-
|
|
190756
|
-
|
|
190757
|
-
|
|
190758
|
-
|
|
190759
|
-
|
|
190760
|
-
|
|
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));
|
|
190761
190619
|
}
|
|
190762
|
-
}
|
|
190763
|
-
|
|
190764
|
-
|
|
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 {}
|
|
190765
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);
|
|
190766
190639
|
}
|
|
190767
|
-
}
|
|
190768
|
-
const
|
|
190769
|
-
|
|
190770
|
-
|
|
190771
|
-
|
|
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);
|
|
190772
190688
|
return {
|
|
190773
|
-
workflow:
|
|
190774
|
-
|
|
190775
|
-
|
|
190776
|
-
|
|
190777
|
-
|
|
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
|
+
`);
|
|
190778
190726
|
}
|
|
190779
190727
|
|
|
190780
190728
|
// src/commands/workflow/execution.ts
|
|
@@ -190783,13 +190731,16 @@ async function runSavedWorkflowExamples(workflow2, examples, opts) {
|
|
|
190783
190731
|
try {
|
|
190784
190732
|
requireApiKey(config2);
|
|
190785
190733
|
const client = new ApiClient(config2);
|
|
190786
|
-
const
|
|
190787
|
-
|
|
190788
|
-
version
|
|
190789
|
-
}
|
|
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);
|
|
190790
190739
|
if (opts.json)
|
|
190791
190740
|
console.log(JSON.stringify(summary, null, 2));
|
|
190792
|
-
if (summary.
|
|
190741
|
+
if (summary.errored > 0)
|
|
190742
|
+
process.exit(1);
|
|
190743
|
+
if (opts.failOnMismatch && summary.failedCases > 0)
|
|
190793
190744
|
process.exit(1);
|
|
190794
190745
|
} catch (err2) {
|
|
190795
190746
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
@@ -190802,7 +190753,23 @@ async function runSavedWorkflowExamples(workflow2, examples, opts) {
|
|
|
190802
190753
|
|
|
190803
190754
|
// src/commands/run.ts
|
|
190804
190755
|
function registerRunCommands(program2) {
|
|
190805
|
-
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
|
+
`);
|
|
190806
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));
|
|
190807
190774
|
}
|
|
190808
190775
|
async function runTarget(target, opts) {
|
|
@@ -190820,7 +190787,8 @@ async function runTarget(target, opts) {
|
|
|
190820
190787
|
}
|
|
190821
190788
|
return runSavedWorkflowExamples(parsed.idOrSlug, [opts.example], {
|
|
190822
190789
|
...opts,
|
|
190823
|
-
version: String(parsed.requestedVersion)
|
|
190790
|
+
version: String(parsed.requestedVersion),
|
|
190791
|
+
failOnMismatch: opts.failOnMismatch
|
|
190824
190792
|
});
|
|
190825
190793
|
}
|
|
190826
190794
|
const client = buildClient(opts);
|
|
@@ -190865,73 +190833,73 @@ import {
|
|
|
190865
190833
|
unlinkSync as unlinkSync2,
|
|
190866
190834
|
writeFileSync as writeFileSync8
|
|
190867
190835
|
} from "node:fs";
|
|
190868
|
-
import { sep as PATH_SEP, dirname as dirname5, join as
|
|
190836
|
+
import { sep as PATH_SEP, dirname as dirname5, join as join10, relative as relative3, resolve as resolve5 } from "node:path";
|
|
190869
190837
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
190870
190838
|
|
|
190871
190839
|
// src/commands/skill/tools.ts
|
|
190872
|
-
import { join as
|
|
190840
|
+
import { join as join9 } from "node:path";
|
|
190873
190841
|
var SKILL_NAME = "eigenpal";
|
|
190874
190842
|
var TOOLS = [
|
|
190875
190843
|
{
|
|
190876
190844
|
id: "claude",
|
|
190877
190845
|
label: "Claude Code",
|
|
190878
|
-
relativePath:
|
|
190846
|
+
relativePath: join9(".claude", "skills", SKILL_NAME),
|
|
190879
190847
|
detectPaths: [".claude", "CLAUDE.md"]
|
|
190880
190848
|
},
|
|
190881
190849
|
{
|
|
190882
190850
|
id: "cursor",
|
|
190883
190851
|
label: "Cursor",
|
|
190884
|
-
relativePath:
|
|
190852
|
+
relativePath: join9(".cursor", "skills", SKILL_NAME),
|
|
190885
190853
|
detectPaths: [".cursor", ".cursorrules"]
|
|
190886
190854
|
},
|
|
190887
190855
|
{
|
|
190888
190856
|
id: "codex",
|
|
190889
190857
|
label: "Codex",
|
|
190890
|
-
relativePath:
|
|
190858
|
+
relativePath: join9(".codex", "skills", SKILL_NAME),
|
|
190891
190859
|
detectPaths: [".codex"]
|
|
190892
190860
|
},
|
|
190893
190861
|
{
|
|
190894
190862
|
id: "gemini",
|
|
190895
190863
|
label: "Gemini CLI",
|
|
190896
|
-
relativePath:
|
|
190864
|
+
relativePath: join9(".gemini", "skills", SKILL_NAME),
|
|
190897
190865
|
detectPaths: [".gemini", "GEMINI.md"]
|
|
190898
190866
|
},
|
|
190899
190867
|
{
|
|
190900
190868
|
id: "antigravity",
|
|
190901
190869
|
label: "Antigravity",
|
|
190902
|
-
relativePath:
|
|
190870
|
+
relativePath: join9(".agent", "skills", SKILL_NAME),
|
|
190903
190871
|
detectPaths: [".agent"]
|
|
190904
190872
|
},
|
|
190905
190873
|
{
|
|
190906
190874
|
id: "opencode",
|
|
190907
190875
|
label: "OpenCode",
|
|
190908
|
-
relativePath:
|
|
190876
|
+
relativePath: join9(".opencode", "skills", SKILL_NAME),
|
|
190909
190877
|
detectPaths: [".opencode"]
|
|
190910
190878
|
},
|
|
190911
190879
|
{
|
|
190912
190880
|
id: "pi",
|
|
190913
190881
|
label: "Pi",
|
|
190914
|
-
relativePath:
|
|
190882
|
+
relativePath: join9(".pi", "skills", SKILL_NAME),
|
|
190915
190883
|
detectPaths: [".pi"]
|
|
190916
190884
|
},
|
|
190917
190885
|
{
|
|
190918
190886
|
id: "windsurf",
|
|
190919
190887
|
label: "Windsurf",
|
|
190920
|
-
relativePath:
|
|
190888
|
+
relativePath: join9(".windsurf", "skills", SKILL_NAME),
|
|
190921
190889
|
detectPaths: [".windsurf", ".windsurfrules"]
|
|
190922
190890
|
},
|
|
190923
190891
|
{
|
|
190924
190892
|
id: "github-copilot",
|
|
190925
190893
|
label: "GitHub Copilot",
|
|
190926
|
-
relativePath:
|
|
190894
|
+
relativePath: join9(".github", "skills", SKILL_NAME),
|
|
190927
190895
|
detectPaths: [
|
|
190928
|
-
|
|
190929
|
-
|
|
190930
|
-
|
|
190931
|
-
|
|
190932
|
-
|
|
190933
|
-
|
|
190934
|
-
|
|
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")
|
|
190935
190903
|
]
|
|
190936
190904
|
}
|
|
190937
190905
|
];
|
|
@@ -190951,7 +190919,7 @@ function resolveSkillSource() {
|
|
|
190951
190919
|
${distCandidate}`);
|
|
190952
190920
|
}
|
|
190953
190921
|
function defaultClaudeTarget() {
|
|
190954
|
-
return
|
|
190922
|
+
return join10(process.cwd(), TOOLS[0].relativePath);
|
|
190955
190923
|
}
|
|
190956
190924
|
function resolveSkillTarget(opts) {
|
|
190957
190925
|
return opts.target ?? defaultClaudeTarget();
|
|
@@ -190979,8 +190947,8 @@ async function installSkill(opts) {
|
|
|
190979
190947
|
let skipped = 0;
|
|
190980
190948
|
let prompted = 0;
|
|
190981
190949
|
for (const relativePath of sourceFiles) {
|
|
190982
|
-
const sourcePath =
|
|
190983
|
-
const targetPath =
|
|
190950
|
+
const sourcePath = join10(source2, relativePath);
|
|
190951
|
+
const targetPath = join10(target, relativePath);
|
|
190984
190952
|
const sourceBytes = templateSourceFile(relativePath, readFileSync14(sourcePath), cliVersion);
|
|
190985
190953
|
const sourceHash = sha2562(sourceBytes);
|
|
190986
190954
|
if (!existsSync15(targetPath)) {
|
|
@@ -191017,7 +190985,7 @@ async function installSkill(opts) {
|
|
|
191017
190985
|
}
|
|
191018
190986
|
const manifestEntries = [];
|
|
191019
190987
|
for (const relativePath of sourceFiles) {
|
|
191020
|
-
const targetPath =
|
|
190988
|
+
const targetPath = join10(target, relativePath);
|
|
191021
190989
|
if (!existsSync15(targetPath))
|
|
191022
190990
|
continue;
|
|
191023
190991
|
manifestEntries.push({ path: relativePath, sha256: sha2562(readFileSync14(targetPath)) });
|
|
@@ -191027,7 +190995,7 @@ async function installSkill(opts) {
|
|
|
191027
190995
|
installedAt: new Date().toISOString(),
|
|
191028
190996
|
files: manifestEntries
|
|
191029
190997
|
};
|
|
191030
|
-
writeFileSync8(
|
|
190998
|
+
writeFileSync8(join10(target, MANIFEST_NAME), `${JSON.stringify(manifest, null, 2)}
|
|
191031
190999
|
`);
|
|
191032
191000
|
const displayTarget = relative3(process.cwd(), target) || target;
|
|
191033
191001
|
success2(`Installed skill at ${ui.bold(displayTarget)} — ${written} written, ${skipped} unchanged${prompted > 0 ? `, ${prompted} prompted` : ""}.`);
|
|
@@ -191036,7 +191004,7 @@ async function uninstallSkill(opts) {
|
|
|
191036
191004
|
const target = resolveSkillTarget(opts);
|
|
191037
191005
|
const manifest = readManifest(target);
|
|
191038
191006
|
if (!manifest) {
|
|
191039
|
-
error51(`No manifest found at ${
|
|
191007
|
+
error51(`No manifest found at ${join10(target, MANIFEST_NAME)}; nothing to uninstall.`);
|
|
191040
191008
|
process.exit(1);
|
|
191041
191009
|
}
|
|
191042
191010
|
if (!opts.yes && process.stdin.isTTY) {
|
|
@@ -191069,7 +191037,7 @@ async function uninstallSkill(opts) {
|
|
|
191069
191037
|
unlinkSync2(path15);
|
|
191070
191038
|
removed += 1;
|
|
191071
191039
|
}
|
|
191072
|
-
rmSync2(
|
|
191040
|
+
rmSync2(join10(target, MANIFEST_NAME), { force: true });
|
|
191073
191041
|
pruneEmptyDirs(target);
|
|
191074
191042
|
const displayTarget = relative3(process.cwd(), target) || target;
|
|
191075
191043
|
success2(`Uninstalled skill from ${ui.bold(displayTarget)} — ${removed} removed${kept > 0 ? `, ${kept} user-edited kept` : ""}.`);
|
|
@@ -191130,14 +191098,14 @@ async function installSkillTools(opts) {
|
|
|
191130
191098
|
for (const tool of toInstall) {
|
|
191131
191099
|
await installSkill({
|
|
191132
191100
|
source: opts.source,
|
|
191133
|
-
target:
|
|
191101
|
+
target: join10(cwd, tool.relativePath),
|
|
191134
191102
|
force: opts.force,
|
|
191135
191103
|
yes: opts.yes
|
|
191136
191104
|
});
|
|
191137
191105
|
}
|
|
191138
191106
|
for (const tool of toUninstall) {
|
|
191139
191107
|
await uninstallSkill({
|
|
191140
|
-
target:
|
|
191108
|
+
target: join10(cwd, tool.relativePath),
|
|
191141
191109
|
yes: true
|
|
191142
191110
|
});
|
|
191143
191111
|
}
|
|
@@ -191200,7 +191168,7 @@ async function uninstallSkillTools(opts) {
|
|
|
191200
191168
|
}
|
|
191201
191169
|
for (const tool of targets) {
|
|
191202
191170
|
await uninstallSkill({
|
|
191203
|
-
target:
|
|
191171
|
+
target: join10(cwd, tool.relativePath),
|
|
191204
191172
|
yes: true
|
|
191205
191173
|
});
|
|
191206
191174
|
}
|
|
@@ -191208,7 +191176,7 @@ async function uninstallSkillTools(opts) {
|
|
|
191208
191176
|
function listSkillTools(opts = {}) {
|
|
191209
191177
|
const cwd = process.cwd();
|
|
191210
191178
|
const rows = TOOLS.flatMap((tool) => {
|
|
191211
|
-
const targetDir =
|
|
191179
|
+
const targetDir = join10(cwd, tool.relativePath);
|
|
191212
191180
|
const manifest = readManifest(targetDir);
|
|
191213
191181
|
if (!manifest)
|
|
191214
191182
|
return [];
|
|
@@ -191261,10 +191229,10 @@ function hintFor(tool, installed, detected) {
|
|
|
191261
191229
|
return tool.relativePath;
|
|
191262
191230
|
}
|
|
191263
191231
|
function isInstalled(cwd, tool) {
|
|
191264
|
-
return existsSync15(
|
|
191232
|
+
return existsSync15(join10(cwd, tool.relativePath, MANIFEST_NAME));
|
|
191265
191233
|
}
|
|
191266
191234
|
function isProjectUsing(cwd, tool) {
|
|
191267
|
-
return tool.detectPaths.some((p4) => existsSync15(
|
|
191235
|
+
return tool.detectPaths.some((p4) => existsSync15(join10(cwd, p4)));
|
|
191268
191236
|
}
|
|
191269
191237
|
function listFiles(root2) {
|
|
191270
191238
|
const out = [];
|
|
@@ -191272,7 +191240,7 @@ function listFiles(root2) {
|
|
|
191272
191240
|
while (stack2.length > 0) {
|
|
191273
191241
|
const dir = stack2.pop();
|
|
191274
191242
|
for (const entry of readdirSync5(dir)) {
|
|
191275
|
-
const full =
|
|
191243
|
+
const full = join10(dir, entry);
|
|
191276
191244
|
const rel = relative3(root2, full);
|
|
191277
191245
|
const stats = lstatSync(full);
|
|
191278
191246
|
if (stats.isDirectory()) {
|
|
@@ -191287,7 +191255,7 @@ function listFiles(root2) {
|
|
|
191287
191255
|
return out.sort();
|
|
191288
191256
|
}
|
|
191289
191257
|
function readManifest(target) {
|
|
191290
|
-
const path15 =
|
|
191258
|
+
const path15 = join10(target, MANIFEST_NAME);
|
|
191291
191259
|
if (!existsSync15(path15))
|
|
191292
191260
|
return null;
|
|
191293
191261
|
try {
|
|
@@ -191322,7 +191290,7 @@ function pruneEmptyDirs(root2) {
|
|
|
191322
191290
|
const stack2 = [];
|
|
191323
191291
|
const collect = (dir) => {
|
|
191324
191292
|
for (const entry of readdirSync5(dir)) {
|
|
191325
|
-
const full =
|
|
191293
|
+
const full = join10(dir, entry);
|
|
191326
191294
|
if (lstatSync(full).isDirectory())
|
|
191327
191295
|
collect(full);
|
|
191328
191296
|
}
|
|
@@ -191468,12 +191436,12 @@ import { createInterface as createInterface3 } from "node:readline/promises";
|
|
|
191468
191436
|
|
|
191469
191437
|
// src/commands/workflow/clear.ts
|
|
191470
191438
|
import { existsSync as existsSync16, readdirSync as readdirSync6, rmSync as rmSync3, statSync as statSync5 } from "fs";
|
|
191471
|
-
import { basename, join as
|
|
191439
|
+
import { basename, join as join11 } from "path";
|
|
191472
191440
|
var EMPTY_REPORT = { removed: 0, kept: 0, touched: 0 };
|
|
191473
191441
|
function getDirectoryEntries(path15) {
|
|
191474
191442
|
if (!existsSync16(path15))
|
|
191475
191443
|
return [];
|
|
191476
|
-
return readdirSync6(path15).filter((entry) => statSync5(
|
|
191444
|
+
return readdirSync6(path15).filter((entry) => statSync5(join11(path15, entry)).isDirectory()).sort();
|
|
191477
191445
|
}
|
|
191478
191446
|
function removeDirEntriesWithRetention(path15, removeAll) {
|
|
191479
191447
|
const entries = getDirectoryEntries(path15);
|
|
@@ -191486,7 +191454,7 @@ function removeDirEntriesWithRetention(path15, removeAll) {
|
|
|
191486
191454
|
for (const entry of entries) {
|
|
191487
191455
|
if (keepSet.has(entry))
|
|
191488
191456
|
continue;
|
|
191489
|
-
rmSync3(
|
|
191457
|
+
rmSync3(join11(path15, entry), { recursive: true, force: true });
|
|
191490
191458
|
removed++;
|
|
191491
191459
|
}
|
|
191492
191460
|
return { removed, kept: keepSet.size, touched: entries.length };
|
|
@@ -191513,7 +191481,7 @@ function removeJudgeSummaryFilesWithRetention(exampleDir, removeAll) {
|
|
|
191513
191481
|
kept++;
|
|
191514
191482
|
continue;
|
|
191515
191483
|
}
|
|
191516
|
-
rmSync3(
|
|
191484
|
+
rmSync3(join11(exampleDir, fileName), { force: true });
|
|
191517
191485
|
removed++;
|
|
191518
191486
|
}
|
|
191519
191487
|
return { removed, kept, touched: stampByFile.size };
|
|
@@ -191521,17 +191489,17 @@ function removeJudgeSummaryFilesWithRetention(exampleDir, removeAll) {
|
|
|
191521
191489
|
function getTargetExampleDirs(dir, exampleNames) {
|
|
191522
191490
|
const baseDir = resolveEvalBaseDir(dir);
|
|
191523
191491
|
if (!baseDir) {
|
|
191524
|
-
error51(`No dataset/examples directory at ${
|
|
191492
|
+
error51(`No dataset/examples directory at ${join11(dir, "dataset", "examples")}`);
|
|
191525
191493
|
process.exit(1);
|
|
191526
191494
|
}
|
|
191527
|
-
const all = readdirSync6(baseDir).filter((entry) => statSync5(
|
|
191495
|
+
const all = readdirSync6(baseDir).filter((entry) => statSync5(join11(baseDir, entry)).isDirectory());
|
|
191528
191496
|
if (exampleNames.length === 0)
|
|
191529
|
-
return all.map((name) =>
|
|
191497
|
+
return all.map((name) => join11(baseDir, name));
|
|
191530
191498
|
const matched = all.filter((name) => exampleNames.includes(name));
|
|
191531
191499
|
if (matched.length === 0) {
|
|
191532
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).`);
|
|
191533
191501
|
}
|
|
191534
|
-
return matched.map((name) =>
|
|
191502
|
+
return matched.map((name) => join11(baseDir, name));
|
|
191535
191503
|
}
|
|
191536
191504
|
function runSection(args) {
|
|
191537
191505
|
let removed = 0;
|
|
@@ -191563,8 +191531,8 @@ async function clearEvalOutputs(dir, exampleNames = [], options8 = {}) {
|
|
|
191563
191531
|
const exampleDirs = getTargetExampleDirs(dir, exampleNames);
|
|
191564
191532
|
runSection({
|
|
191565
191533
|
exampleDirs,
|
|
191566
|
-
clean: (exampleDir) => removeDirEntriesWithRetention(
|
|
191567
|
-
shouldCount: (exampleDir) => existsSync16(
|
|
191534
|
+
clean: (exampleDir) => removeDirEntriesWithRetention(join11(exampleDir, "executions"), removeAll),
|
|
191535
|
+
shouldCount: (exampleDir) => existsSync16(join11(exampleDir, "executions")),
|
|
191568
191536
|
successLabel: "Execution cleanup complete",
|
|
191569
191537
|
scannedLabel: "examples scanned:",
|
|
191570
191538
|
seenLabel: "runs seen: ",
|
|
@@ -192139,7 +192107,7 @@ function looksLikeFreeform(prop) {
|
|
|
192139
192107
|
// src/commands/workflow/validation.ts
|
|
192140
192108
|
var import_yaml8 = __toESM(require_dist4(), 1);
|
|
192141
192109
|
import { existsSync as existsSync17, readdirSync as readdirSync7, readFileSync as readFileSync15, statSync as statSync6 } from "fs";
|
|
192142
|
-
import { join as
|
|
192110
|
+
import { join as join12, resolve as resolve6 } from "path";
|
|
192143
192111
|
var NAME_RE = /^[a-z0-9][a-z0-9-_]*$/;
|
|
192144
192112
|
function isDirSafe(path15, issues, field) {
|
|
192145
192113
|
try {
|
|
@@ -192259,19 +192227,19 @@ function validateDatasetFolder(root2) {
|
|
|
192259
192227
|
if (!existsSync17(root2)) {
|
|
192260
192228
|
return [{ field: root2, message: "Folder not found." }];
|
|
192261
192229
|
}
|
|
192262
|
-
if (existsSync17(
|
|
192230
|
+
if (existsSync17(join12(root2, "manifest.json"))) {
|
|
192263
192231
|
issues.push({
|
|
192264
192232
|
field: "manifest.json",
|
|
192265
192233
|
message: "Top-level manifest.json is the legacy format. Re-export the dataset using the folder convention."
|
|
192266
192234
|
});
|
|
192267
192235
|
}
|
|
192268
|
-
const examplesDir =
|
|
192236
|
+
const examplesDir = join12(root2, "examples");
|
|
192269
192237
|
if (!existsSync17(examplesDir)) {
|
|
192270
192238
|
issues.push({ field: "examples/", message: "No examples/ directory found at dataset root." });
|
|
192271
192239
|
return issues;
|
|
192272
192240
|
}
|
|
192273
192241
|
for (const exampleName of readdirSync7(examplesDir)) {
|
|
192274
|
-
const exampleFolder =
|
|
192242
|
+
const exampleFolder = join12(examplesDir, exampleName);
|
|
192275
192243
|
if (!isDirSafe(exampleFolder, issues, `examples/${exampleName}`))
|
|
192276
192244
|
continue;
|
|
192277
192245
|
if (!NAME_RE.test(exampleName)) {
|
|
@@ -192281,7 +192249,7 @@ function validateDatasetFolder(root2) {
|
|
|
192281
192249
|
});
|
|
192282
192250
|
continue;
|
|
192283
192251
|
}
|
|
192284
|
-
const inputJsonPath =
|
|
192252
|
+
const inputJsonPath = join12(exampleFolder, "input.json");
|
|
192285
192253
|
const hasInputJson = existsSync17(inputJsonPath);
|
|
192286
192254
|
if (!hasInputJson) {
|
|
192287
192255
|
issues.push({
|
|
@@ -192308,8 +192276,8 @@ function validateDatasetFolder(root2) {
|
|
|
192308
192276
|
});
|
|
192309
192277
|
continue;
|
|
192310
192278
|
}
|
|
192311
|
-
const inputDir =
|
|
192312
|
-
if (existsSync17(
|
|
192279
|
+
const inputDir = join12(exampleFolder, "input");
|
|
192280
|
+
if (existsSync17(join12(inputDir, "arguments.json"))) {
|
|
192313
192281
|
issues.push({
|
|
192314
192282
|
field: `examples/${exampleName}/input/arguments.json`,
|
|
192315
192283
|
message: "Legacy input/arguments.json is no longer accepted. Use input.json instead."
|
|
@@ -192324,8 +192292,8 @@ function validateDatasetFolder(root2) {
|
|
|
192324
192292
|
})) {
|
|
192325
192293
|
issues.push(issue2);
|
|
192326
192294
|
}
|
|
192327
|
-
const expectedDir =
|
|
192328
|
-
const expectedJsonPath =
|
|
192295
|
+
const expectedDir = join12(exampleFolder, "expected");
|
|
192296
|
+
const expectedJsonPath = join12(exampleFolder, "expected.json");
|
|
192329
192297
|
if (existsSync17(expectedDir)) {
|
|
192330
192298
|
for (const entry of readdirSync7(expectedDir)) {
|
|
192331
192299
|
if (entry === "output.json" || entry === "error.json") {
|
|
@@ -192335,7 +192303,7 @@ function validateDatasetFolder(root2) {
|
|
|
192335
192303
|
});
|
|
192336
192304
|
continue;
|
|
192337
192305
|
}
|
|
192338
|
-
const sub =
|
|
192306
|
+
const sub = join12(expectedDir, entry);
|
|
192339
192307
|
const field = `examples/${exampleName}/expected/${entry}`;
|
|
192340
192308
|
let isDir;
|
|
192341
192309
|
try {
|
|
@@ -192391,7 +192359,7 @@ function validateDatasetFolder(root2) {
|
|
|
192391
192359
|
} else {
|
|
192392
192360
|
for (const issue2 of validateFileRefs({
|
|
192393
192361
|
refs: collectDatasetFileRefs(parsed),
|
|
192394
|
-
filesRoot:
|
|
192362
|
+
filesRoot: join12(exampleFolder, "expected"),
|
|
192395
192363
|
root: "expected",
|
|
192396
192364
|
fieldPrefix: `examples/${exampleName}/expected.json`
|
|
192397
192365
|
})) {
|
|
@@ -192444,7 +192412,7 @@ function validateFileRefs(args) {
|
|
|
192444
192412
|
});
|
|
192445
192413
|
continue;
|
|
192446
192414
|
}
|
|
192447
|
-
if (!existsSync17(
|
|
192415
|
+
if (!existsSync17(join12(args.filesRoot, relative4))) {
|
|
192448
192416
|
issues.push({
|
|
192449
192417
|
field: `${args.fieldPrefix}:${ref.jsonPath}`,
|
|
192450
192418
|
message: `Referenced file does not exist: ${ref.path}.`
|
|
@@ -192498,7 +192466,7 @@ Note
|
|
|
192498
192466
|
return;
|
|
192499
192467
|
}
|
|
192500
192468
|
const root2 = rootDir(opts);
|
|
192501
|
-
const wfFile =
|
|
192469
|
+
const wfFile = join12(root2, "workflow.yaml");
|
|
192502
192470
|
const wfResult = validateWorkflowYaml(wfFile);
|
|
192503
192471
|
const wfPath = relPath(root2, wfFile);
|
|
192504
192472
|
const wfOk = printIssues("workflow.yaml", wfPath, wfResult.issues);
|
|
@@ -192506,8 +192474,8 @@ Note
|
|
|
192506
192474
|
printSchemaQualityWarnings(wfPath, wfResult.warnings);
|
|
192507
192475
|
printInvokeOnlineHint(wfResult.invokeStepCount, opts.online);
|
|
192508
192476
|
}
|
|
192509
|
-
const evOk = printIssues("evaluators.yaml", relPath(root2,
|
|
192510
|
-
const dsOk = printIssues("dataset/", relPath(root2,
|
|
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")));
|
|
192511
192479
|
let onlineOk = true;
|
|
192512
192480
|
if (wfOk && opts.online && existsSync17(wfFile)) {
|
|
192513
192481
|
onlineOk = printIssues("invoke refs (server)", wfPath, await onlineInvokeIssues(readFileSync15(wfFile, "utf-8"), opts));
|
|
@@ -192587,7 +192555,7 @@ async function writeOrPrint(out, content) {
|
|
|
192587
192555
|
}
|
|
192588
192556
|
async function readDirAsZip2(dir) {
|
|
192589
192557
|
const tree = {};
|
|
192590
|
-
async function
|
|
192558
|
+
async function walk3(current, relPrefix) {
|
|
192591
192559
|
const entries = await fs17.readdir(current, { withFileTypes: true });
|
|
192592
192560
|
for (const e8 of entries) {
|
|
192593
192561
|
if (e8.name.startsWith(".") || e8.name === "node_modules")
|
|
@@ -192595,12 +192563,12 @@ async function readDirAsZip2(dir) {
|
|
|
192595
192563
|
const full = path15.join(current, e8.name);
|
|
192596
192564
|
const rel = relPrefix ? `${relPrefix}/${e8.name}` : e8.name;
|
|
192597
192565
|
if (e8.isDirectory())
|
|
192598
|
-
await
|
|
192566
|
+
await walk3(full, rel);
|
|
192599
192567
|
else if (e8.isFile())
|
|
192600
192568
|
tree[rel] = new Uint8Array(await fs17.readFile(full));
|
|
192601
192569
|
}
|
|
192602
192570
|
}
|
|
192603
|
-
await
|
|
192571
|
+
await walk3(dir, "");
|
|
192604
192572
|
if (Object.keys(tree).length === 0) {
|
|
192605
192573
|
throw new Error(`directory is empty: ${dir}`);
|
|
192606
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 |
|