@eigenpal/cli 0.6.9 → 0.6.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -19715,7 +19715,7 @@ function executionWallClockMs(startedAt, completedAt) {
19715
19715
  const ms = end - start;
19716
19716
  return ms >= 0 ? ms : null;
19717
19717
  }
19718
- var DURABLE_EXECUTION_TYPES, EXECUTION_STATUSES, EXECUTION_PHASES, EXECUTION_PHASE_ORDER, EXECUTION_PHASE_STATUSES, EXECUTION_FAILURE_CATEGORIES, TERMINAL_EXECUTION_STATUSES, TERMINAL_STATUS_SET, EXECUTION_NON_TERMINAL_STATUSES, NON_SUCCESS_TERMINAL_EXECUTION_STATUSES, NON_SUCCESS_TERMINAL_SET, EXECUTION_CONCURRENCY_ACTIVE_STATUSES, EXECUTION_SHARED_CANCEL_REQUESTABLE_STATUSES, EXECUTION_AGENT_CANCEL_REQUESTABLE_STATUSES, EXECUTION_AGENT_QUEUED_STATUSES, EXECUTION_AGENT_REAP_SCAN_STATUSES, EXECUTION_AGENT_METRICS_STATUSES, EXECUTION_EVAL_RUNNING_STATUSES, EXECUTION_FAILED_OR_CANCELLED_STATUSES;
19718
+ var DURABLE_EXECUTION_TYPES, RUNS_TRIGGERED_BY_SYSTEM = "__system__", EXECUTION_STATUSES, EXECUTION_PHASES, EXECUTION_PHASE_ORDER, EXECUTION_PHASE_STATUSES, EXECUTION_FAILURE_CATEGORIES, TERMINAL_EXECUTION_STATUSES, TERMINAL_STATUS_SET, EXECUTION_NON_TERMINAL_STATUSES, NON_SUCCESS_TERMINAL_EXECUTION_STATUSES, NON_SUCCESS_TERMINAL_SET, EXECUTION_CONCURRENCY_ACTIVE_STATUSES, EXECUTION_SHARED_CANCEL_REQUESTABLE_STATUSES, EXECUTION_AGENT_CANCEL_REQUESTABLE_STATUSES, EXECUTION_AGENT_QUEUED_STATUSES, EXECUTION_AGENT_REAP_SCAN_STATUSES, EXECUTION_AGENT_METRICS_STATUSES, EXECUTION_EVAL_RUNNING_STATUSES, EXECUTION_FAILED_OR_CANCELLED_STATUSES;
19719
19719
  var init_execution_row = __esm(() => {
19720
19720
  DURABLE_EXECUTION_TYPES = ["workflow", "agent"];
19721
19721
  EXECUTION_STATUSES = [
@@ -66958,6 +66958,7 @@ __export(exports_src, {
66958
66958
  S3_PATH_GRAMMAR: () => S3_PATH_GRAMMAR,
66959
66959
  RootSourceManifestSchema: () => RootSourceManifestSchema,
66960
66960
  ReleaseTagSchema: () => ReleaseTagSchema,
66961
+ RUNS_TRIGGERED_BY_SYSTEM: () => RUNS_TRIGGERED_BY_SYSTEM,
66961
66962
  ProcessorPortSchema: () => ProcessorPortSchema,
66962
66963
  ProcessorExecutionContextSchema: () => ProcessorExecutionContextSchema,
66963
66964
  ProcessorDefinitionSchema: () => ProcessorDefinitionSchema,
@@ -188771,7 +188772,7 @@ import { fileURLToPath as fileURLToPath6 } from "url";
188771
188772
  // package.json
188772
188773
  var package_default = {
188773
188774
  name: "@eigenpal/cli",
188774
- version: "0.6.9",
188775
+ version: "0.6.10",
188775
188776
  description: "Eigenpal CLI — author and run document workflows from your terminal. Agent-ready.",
188776
188777
  keywords: [
188777
188778
  "eigenpal",
@@ -191301,7 +191302,7 @@ function registerAgentSourceCommands(agent) {
191301
191302
  withBaseUrl(agent.command("clone")).description("Clone the organization source repository.").option("--out <dir>", "Output directory").option("--tenant-id <tenantId>", "Target tenant id for admin-token source clones").action(action(cloneSource));
191302
191303
  withBaseUrl(agent.command("install [packageRef]")).description("Materialize a source package and its workspace dependencies.").option("--out <dir>", "Output directory for an explicit package ref").option("--lockfile <path>", "Lockfile path").option("--frozen-lockfile", "Install exactly from the existing lockfile").option("--remote-url <url>", "Use an explicit organization Git remote URL").action(action(install));
191303
191304
  registerSourceInitCommand(agent);
191304
- withBaseUrl(agent.command("pull")).description("Pull organization source from origin/main with --ff-only. For datasets use agents dataset pull; for run artifacts use agents runs artifacts fetch.").option("--dir <dir>", "Repository directory").action(action(pullSource));
191305
+ withBaseUrl(agent.command("pull")).description("Pull organization source from origin/main with --ff-only. For datasets use agents dataset pull; for run artifacts use runs artifacts fetch.").option("--dir <dir>", "Repository directory").action(action(pullSource));
191305
191306
  withBaseUrl(agent.command("commit")).description("Validate changed source packages and commit them.").addOption(new Option("-m, --message <message>", "Commit message").makeOptionMandatory()).option("--dir <dir>", "Repository directory").action(action(commitSource));
191306
191307
  withBaseUrl(agent.command("save")).description("Validate, commit if dirty, and push the current source branch.").option("-m, --message <message>", "Commit message when source changes are dirty").option("--dir <dir>", "Repository directory").action(action(saveSource));
191307
191308
  withBaseUrl(agent.command("push")).description("Push the current organization source branch and tags.").option("--dir <dir>", "Repository directory").action(action(pushSource));
@@ -191322,12 +191323,240 @@ function registerGitCommands(program2) {
191322
191323
  credentialHelper.hidden = true;
191323
191324
  }
191324
191325
 
191325
- // src/commands/agents/dataset.ts
191326
+ // src/commands/runs.ts
191327
+ import { spawnSync as spawnSync4 } from "node:child_process";
191328
+ import { createHash as createHash2 } from "node:crypto";
191329
+ import { promises as fs3, readFileSync as readFileSync7 } from "node:fs";
191326
191330
  import path8 from "node:path";
191327
191331
 
191328
- // src/commands/agents/shared.ts
191329
- import { existsSync as existsSync6, promises as fs } from "node:fs";
191332
+ // src/lib/resolve-workflow.ts
191333
+ var WORKFLOW_ID_PREFIX = "wf_";
191334
+ function looksLikeWorkflowId(value) {
191335
+ return value.startsWith(WORKFLOW_ID_PREFIX);
191336
+ }
191337
+ function levenshtein(a, b2, maxDistance) {
191338
+ if (a === b2)
191339
+ return 0;
191340
+ if (Math.abs(a.length - b2.length) > maxDistance)
191341
+ return maxDistance + 1;
191342
+ if (a.length === 0 || b2.length === 0)
191343
+ return a.length || b2.length;
191344
+ const prev = new Array(b2.length + 1);
191345
+ const curr = new Array(b2.length + 1);
191346
+ for (let j3 = 0;j3 <= b2.length; j3++)
191347
+ prev[j3] = j3;
191348
+ for (let i = 1;i <= a.length; i++) {
191349
+ curr[0] = i;
191350
+ let minRow = curr[0];
191351
+ for (let j3 = 1;j3 <= b2.length; j3++) {
191352
+ const cost = a[i - 1] === b2[j3 - 1] ? 0 : 1;
191353
+ curr[j3] = Math.min(prev[j3] + 1, curr[j3 - 1] + 1, prev[j3 - 1] + cost);
191354
+ if (curr[j3] < minRow)
191355
+ minRow = curr[j3];
191356
+ }
191357
+ if (minRow > maxDistance)
191358
+ return maxDistance + 1;
191359
+ for (let j3 = 0;j3 <= b2.length; j3++)
191360
+ prev[j3] = curr[j3];
191361
+ }
191362
+ return prev[b2.length];
191363
+ }
191364
+ async function findSimilarSlug(client, slug) {
191365
+ try {
191366
+ const res = await client.get("/api/v1/workflows", {
191367
+ search: slug,
191368
+ limit: "20"
191369
+ });
191370
+ const candidates = (res.data ?? []).map((w3) => w3.name).filter((n) => typeof n === "string" && n.length > 0);
191371
+ let best = null;
191372
+ for (const candidate of candidates) {
191373
+ const distance = levenshtein(candidate, slug, 2);
191374
+ if (distance <= 2 && (best === null || distance < best.distance)) {
191375
+ best = { name: candidate, distance };
191376
+ }
191377
+ }
191378
+ return best?.name ?? null;
191379
+ } catch {
191380
+ return null;
191381
+ }
191382
+ }
191383
+ function notFound(idOrSlug, suggestion) {
191384
+ const base = `Workflow "${idOrSlug}" not found on the server.`;
191385
+ const action2 = "Push it first, or run `eigenpal workflow list` to see what's available.";
191386
+ return new Error(suggestion ? `${base} Did you mean "${suggestion}"? ${action2}` : `${base} ${action2}`);
191387
+ }
191388
+ async function resolveWorkflowId(client, idOrSlug) {
191389
+ if (looksLikeWorkflowId(idOrSlug)) {
191390
+ try {
191391
+ const wf = await client.get(`/api/v1/workflows/${idOrSlug}`);
191392
+ return wf.id;
191393
+ } catch {
191394
+ throw notFound(idOrSlug, null);
191395
+ }
191396
+ }
191397
+ const res = await client.get("/api/v1/workflows", { name: idOrSlug });
191398
+ const match = res.data?.[0];
191399
+ if (!match) {
191400
+ const suggestion = await findSimilarSlug(client, idOrSlug);
191401
+ throw notFound(idOrSlug, suggestion);
191402
+ }
191403
+ return match.id;
191404
+ }
191405
+
191406
+ // src/lib/watch.ts
191407
+ var TERMINAL_STATUSES = new Set(["completed", "failed", "cancelled", "rejected"]);
191408
+ var STATUS_BADGES = {
191409
+ pending: "[ ]",
191410
+ running: "[..]",
191411
+ completed: "[ok]",
191412
+ failed: "[x]",
191413
+ cancelled: "[-]",
191414
+ skipped: "[s]"
191415
+ };
191416
+ function statusBadge(status) {
191417
+ return STATUS_BADGES[status] ?? `[${status.slice(0, 3)}]`;
191418
+ }
191419
+ function durationSuffix(ms) {
191420
+ const formatted = formatDuration(ms);
191421
+ return formatted ? ` ${formatted}` : "";
191422
+ }
191423
+ function renderFrame(snapshot) {
191424
+ const lines = [
191425
+ `${statusBadge(snapshot.status)} ${snapshot.id}${durationSuffix(snapshot.durationMs)}`
191426
+ ];
191427
+ for (const step of snapshot.stepExecutions ?? []) {
191428
+ const errSuffix = step.error ? ` err: ${step.error.slice(0, 80)}` : "";
191429
+ lines.push(` ${statusBadge(step.status)} ${step.stepName}${durationSuffix(step.durationMs)}${errSuffix}`);
191430
+ if (step.outputPreview) {
191431
+ lines.push(` ↳ ${step.outputPreview}`);
191432
+ }
191433
+ }
191434
+ if (snapshot.error && (snapshot.stepExecutions ?? []).length === 0) {
191435
+ lines.push(` err: ${snapshot.error.slice(0, 200)}`);
191436
+ }
191437
+ return lines.join(`
191438
+ `);
191439
+ }
191440
+ var ANSI_CURSOR_UP = "\x1B[A";
191441
+ var ANSI_CLEAR_LINE = "\x1B[2K\r";
191442
+ function makeAnsiRenderer() {
191443
+ const isTty = Boolean(process.stderr.isTTY);
191444
+ let previousLineCount = 0;
191445
+ return (frame) => {
191446
+ let buf = "";
191447
+ if (isTty) {
191448
+ for (let i = 0;i < previousLineCount; i++) {
191449
+ buf += i === 0 ? ANSI_CLEAR_LINE : `${ANSI_CURSOR_UP}${ANSI_CLEAR_LINE}`;
191450
+ }
191451
+ }
191452
+ buf += frame;
191453
+ if (!frame.endsWith(`
191454
+ `))
191455
+ buf += `
191456
+ `;
191457
+ if (!isTty && previousLineCount > 0)
191458
+ buf += `
191459
+ `;
191460
+ process.stderr.write(buf);
191461
+ previousLineCount = frame.split(`
191462
+ `).length;
191463
+ };
191464
+ }
191465
+ function defaultSleep(ms) {
191466
+ return new Promise((resolve) => setTimeout(resolve, ms));
191467
+ }
191468
+ function snapshotsDiffer(a, b2) {
191469
+ if (a.status !== b2.status)
191470
+ return true;
191471
+ const aSteps = a.stepExecutions ?? [];
191472
+ const bSteps = b2.stepExecutions ?? [];
191473
+ if (aSteps.length !== bSteps.length)
191474
+ return true;
191475
+ for (let i = 0;i < aSteps.length; i++) {
191476
+ if (aSteps[i].stepName !== bSteps[i].stepName || aSteps[i].status !== bSteps[i].status) {
191477
+ return true;
191478
+ }
191479
+ }
191480
+ return false;
191481
+ }
191482
+ function defaultShouldAbort(err) {
191483
+ return err instanceof ApiError && err.status >= 400 && err.status < 500;
191484
+ }
191485
+ async function watchExecution(opts) {
191486
+ const sleep = opts.sleep ?? defaultSleep;
191487
+ const now = opts.now ?? (() => Date.now());
191488
+ const render = opts.render ?? makeAnsiRenderer();
191489
+ const maxMs = opts.maxMs ?? 30 * 60 * 1000;
191490
+ const transitionInterval = opts.transitionIntervalMs ?? 2000;
191491
+ const steadyInterval = opts.steadyIntervalMs ?? 5000;
191492
+ const shouldAbort = opts.shouldAbort ?? defaultShouldAbort;
191493
+ const start = now();
191494
+ let previous;
191495
+ let lastFetched;
191496
+ while (true) {
191497
+ let current;
191498
+ try {
191499
+ current = await opts.fetch();
191500
+ } catch (err) {
191501
+ if (shouldAbort(err))
191502
+ throw err;
191503
+ const message = err instanceof Error ? err.message : String(err);
191504
+ render(`(network) ${message.slice(0, 120)}`);
191505
+ if (lastFetched && now() - start >= maxMs) {
191506
+ return { final: lastFetched, detached: true };
191507
+ }
191508
+ await sleep(steadyInterval);
191509
+ continue;
191510
+ }
191511
+ lastFetched = current;
191512
+ render(renderFrame(current));
191513
+ if (TERMINAL_STATUSES.has(current.status)) {
191514
+ return { final: current, detached: false };
191515
+ }
191516
+ if (now() - start >= maxMs) {
191517
+ return { final: current, detached: true };
191518
+ }
191519
+ const transitioned = previous == null || snapshotsDiffer(previous, current);
191520
+ previous = current;
191521
+ await sleep(transitioned ? transitionInterval : steadyInterval);
191522
+ }
191523
+ }
191524
+
191525
+ // src/commands/agents/run-form-data.ts
191526
+ import { promises as fs } from "node:fs";
191330
191527
  import path6 from "node:path";
191528
+ async function buildAgentExecutionRunFormData(inputFile, inputJson) {
191529
+ const form = new FormData;
191530
+ for (const spec of parseAgentInputFileSpecs(inputFile)) {
191531
+ const data = await fs.readFile(spec.filePath);
191532
+ form.append(spec.fieldName, new Blob([data]), path6.basename(spec.filePath));
191533
+ }
191534
+ if (inputJson)
191535
+ form.append("_json", inputJson);
191536
+ return form;
191537
+ }
191538
+ function parseAgentInputFileSpecs(inputFile) {
191539
+ const values = Array.isArray(inputFile) ? inputFile : [inputFile];
191540
+ return values.map((value) => {
191541
+ const separatorIndex = value.indexOf("=");
191542
+ if (separatorIndex === -1) {
191543
+ return { fieldName: "file", filePath: value };
191544
+ }
191545
+ const fieldName = value.slice(0, separatorIndex).trim();
191546
+ const filePath = value.slice(separatorIndex + 1).trim();
191547
+ if (!fieldName || !filePath) {
191548
+ throw new Error("--input-file must be <field=path> or a bare path");
191549
+ }
191550
+ if (fieldName === "_json" || fieldName === "_metadata" || fieldName === "input") {
191551
+ throw new Error(`--input-file field "${fieldName}" is reserved`);
191552
+ }
191553
+ return { fieldName, filePath };
191554
+ });
191555
+ }
191556
+
191557
+ // src/commands/agents/shared.ts
191558
+ import { existsSync as existsSync6, promises as fs2 } from "node:fs";
191559
+ import path7 from "node:path";
191331
191560
  import { createInterface as createInterface2 } from "node:readline/promises";
191332
191561
  var PACKAGE_MANIFEST = "eigenpal.yaml";
191333
191562
  var DATASET_DIR = "dataset";
@@ -191351,17 +191580,17 @@ async function readFilesUnder(dir) {
191351
191580
  return [];
191352
191581
  const out = [];
191353
191582
  async function walk(current) {
191354
- for (const entry of await fs.readdir(current, { withFileTypes: true })) {
191583
+ for (const entry of await fs2.readdir(current, { withFileTypes: true })) {
191355
191584
  if (entry.name.startsWith(".") || entry.name === "node_modules")
191356
191585
  continue;
191357
- const absolute = path6.join(current, entry.name);
191586
+ const absolute = path7.join(current, entry.name);
191358
191587
  if (entry.isDirectory()) {
191359
191588
  await walk(absolute);
191360
191589
  } else if (entry.isFile()) {
191361
- const relative = path6.relative(dir, absolute).split(path6.sep).join("/");
191590
+ const relative = path7.relative(dir, absolute).split(path7.sep).join("/");
191362
191591
  out.push({
191363
191592
  path: relative,
191364
- contentBase64: (await fs.readFile(absolute)).toString("base64")
191593
+ contentBase64: (await fs2.readFile(absolute)).toString("base64")
191365
191594
  });
191366
191595
  }
191367
191596
  }
@@ -191370,13 +191599,13 @@ async function readFilesUnder(dir) {
191370
191599
  return out;
191371
191600
  }
191372
191601
  async function writeBase64File(file2, contentBase64) {
191373
- await fs.mkdir(path6.dirname(file2), { recursive: true });
191374
- await fs.writeFile(file2, Buffer.from(contentBase64, "base64"));
191602
+ await fs2.mkdir(path7.dirname(file2), { recursive: true });
191603
+ await fs2.writeFile(file2, Buffer.from(contentBase64, "base64"));
191375
191604
  }
191376
191605
  async function pollRun(client, executionId, interval, maxWait) {
191377
191606
  const started = Date.now();
191378
191607
  for (;; ) {
191379
- const payload = await client.get(`/api/v1/agents/runs/${encodeURIComponent(executionId)}`);
191608
+ const payload = await client.get(`/api/v1/runs/${encodeURIComponent(executionId)}?include=detail`);
191380
191609
  if (isTerminal(payload.run?.status))
191381
191610
  return payload;
191382
191611
  if (Date.now() - started > maxWait * 1000) {
@@ -191470,44 +191699,1037 @@ function toCsv(rows, columns) {
191470
191699
  `);
191471
191700
  }
191472
191701
 
191702
+ // src/commands/agents/target.ts
191703
+ init_src();
191704
+ function parseAgentTarget(target) {
191705
+ const [rawTarget, rawRef, extra] = target.split("@");
191706
+ if (extra !== undefined)
191707
+ throw new Error("Agent target must be <slug>[@ref].");
191708
+ const normalizedTarget = rawTarget.includes(".") ? target : `agents.${rawTarget}${rawRef !== undefined ? `@${rawRef}` : ""}`;
191709
+ const parsed = parseAutomationTarget(normalizedTarget);
191710
+ if (parsed.type !== "agents") {
191711
+ throw new Error("Only agent targets are supported in this release. Use agents.<slug>[@ref].");
191712
+ }
191713
+ return {
191714
+ packageName: parsed.packageName,
191715
+ slug: parsed.slug,
191716
+ sourceRef: rawRef !== undefined ? parsed.ref : undefined
191717
+ };
191718
+ }
191719
+
191720
+ // src/commands/runs.ts
191721
+ function registerRunsCommands(program2) {
191722
+ const runs = program2.command("runs").description("Inspect, watch, and manage workflow, agent, and eval runs.").action(() => {
191723
+ process.stderr.write("`eigenpal runs` requires a subcommand. Run `eigenpal runs --help`.\n");
191724
+ process.exit(2);
191725
+ });
191726
+ const listRunsCmd = addJsonFlag(withPagination(withBaseUrl(runs.command("list [source]")), 50)).alias("ls").description("List runs across workflows and agents, optionally scoped to one source.").option("--type <type>", "Filter by run type: workflow|agent").option("--status <status>", "Filter by run status").option("--source-ref <ref>", "Filter agent runs by source ref").option("--batch-id <id>", "Filter eval/experiment runs by batch id").option("--example-id <id>", "Filter eval runs by example id").option("--example-id-contains <text>", "Filter eval runs by example id substring").option("--from <date>", "Created at or after this ISO timestamp or relative date").option("--to <date>", "Created before this ISO timestamp or relative date").option("--created-after <date>", "Alias for --from").option("--created-before <date>", "Alias for --to").option("--completed-after <date>", "Completed at or after this ISO timestamp or relative date").option("--completed-before <date>", "Completed before this ISO timestamp or relative date").option("--compact", "Render compact run rows").option("--sort <field>", "Sort field; only createdAt is supported").option("--order <asc|desc>", "Sort order; only desc is supported");
191727
+ listRunsCmd.action(action(listRuns));
191728
+ addJsonFlag(withBaseUrl(runs.command("get <run-id>"))).description("Get one run.").option("--step <name>", "For workflow runs, show only this step (or comma-separated list)").option("--include <parts>", "Comma-separated extra parts or workflow step fields: feedback,expected,files,trace,issues,input,output,error,duration,inputRef", "feedback").action(action(getRun));
191729
+ addJsonFlag(withBaseUrl(runs.command("compare <reference-run-id> <run-id>"))).description("Compare one run against another run. PDF/DOCX text comparison uses pdftotext/python3 and reports byte fallbacks.").option("--baseline", "Compare actual outputs from both runs instead of expected artifacts").option("--step <name>", "For workflow runs, restrict comparison to one step").option("--out <dir>", "Write comparison artifacts to this directory").option("--normalize-dates", "Normalize YYYYMMDD and YYYY-MM-DD tokens in filenames/text").option("--fail-on-diff", "Exit 1 when comparison status is fail").action(action(compareRun));
191730
+ addJsonFlag(withBaseUrl(runs.command("rerun <run-id>"))).description("Create a new run from a previous run's stored input snapshot.").option("--source-ref <ref>", "Source ref for the new run: latest (default) or original. Workflow runs support only latest/original.").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 exiting 2", intArg, 1800).action(action(rerunRun));
191731
+ const artifacts = runs.command("artifacts").description("Inspect agent run artifacts.").action(() => {
191732
+ process.stderr.write("`eigenpal runs artifacts` requires a subcommand. Run `eigenpal runs artifacts --help`.\n");
191733
+ process.exit(2);
191734
+ });
191735
+ addJsonFlag(withBaseUrl(artifacts.command("list <run-id>"))).description("List available agent run artifacts without downloading them.").action(action(listRunArtifacts));
191736
+ withBaseUrl(artifacts.command("fetch <run-id>")).description("Download agent run artifacts by canonical artifact path.").option("--out <dir>", "Output directory").option("--include <parts>", "Comma-separated parts: output,input,metadata,issues,trace,lockfile,expected,all", "all").option("--path <path>", "Fetch one exact artifact path from `artifacts list`; repeatable", collectRepeated, []).option("--json", "Output a JSON summary of written artifacts").action(action(fetchRunArtifacts));
191737
+ withBaseUrl(runs.command("trace <run-id>")).description("Print raw trace.jsonl for a run, or write it with --out.").option("--out <file>", "Output file path").action(action(traceRun));
191738
+ registerRunFeedbackCommands(runs);
191739
+ registerRunExpectedCommands(runs);
191740
+ addJsonFlag(withBaseUrl(runs.command("watch <run-id>"))).description("Watch a run until it reaches a terminal status.").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).option("--max-wait <seconds>", "Maximum wait before exiting 2", intArg, 1800).action(action(watchRunCommand));
191741
+ addJsonFlag(withBaseUrl(runs.command("resume <run-id>"))).description("Resume a workflow run that is waiting for approval.").action(action(resumeRun));
191742
+ addJsonFlag(withBaseUrl(runs.command("cancel <run-id>"))).description("Cancel a run.").option("--yes", "Required in non-interactive environments").action(action(cancelRun));
191743
+ }
191744
+ function registerRunFeedbackCommands(runs) {
191745
+ const feedback = runs.command("feedback").description("Update or clear feedback attached to a run.").action(() => {
191746
+ process.stderr.write("`eigenpal runs feedback` requires a subcommand. Run `eigenpal runs feedback --help`.\n");
191747
+ process.exit(2);
191748
+ });
191749
+ addJsonFlag(withBaseUrl(feedback.command("update <run-id>"))).description("Edit feedback state, rating, message, or expected JSON for a run.").option("--status <open|resolved|ignored>", "Set feedback status").option("--rating <pass|fail|partial|none>", "Set feedback rating").option("--message <text>", "Set feedback message body").option("--message-file <path>", "Read feedback message body from a file").option("--expected-json <json>", "Set structured expected JSON").option("--expected-json-file <path>", "Read structured expected JSON from a file").option("--clear-message", "Clear the feedback message body").option("--clear-rating", "Clear feedback rating").option("--clear-expected-json", "Delete structured expected JSON").action(action(updateRunFeedback));
191750
+ addJsonFlag(withBaseUrl(feedback.command("resolve <run-id>"))).description("Mark run feedback as resolved.").option("--message <text>", "Set feedback message body").option("--message-file <path>", "Read feedback message body from a file").action(action((runId, opts) => updateRunFeedback(runId, { ...opts, status: "resolved" })));
191751
+ addJsonFlag(withBaseUrl(feedback.command("clear <run-id>"))).description("Delete feedback, expected.json, and expected files for a run.").option("--yes", "Required in non-interactive environments").action(action(clearRunFeedback));
191752
+ }
191753
+ function registerRunExpectedCommands(runs) {
191754
+ const expected = runs.command("expected").description("Manage expected artifacts attached to a run.").action(() => {
191755
+ process.stderr.write("`eigenpal runs expected` requires a subcommand. Run `eigenpal runs expected --help`.\n");
191756
+ process.exit(2);
191757
+ });
191758
+ addJsonFlag(withBaseUrl(expected.command("list <run-id>"))).description("List expected JSON and files attached to a run.").action(action(listRunExpected));
191759
+ withBaseUrl(expected.command("pull <run-id>")).description("Download expected JSON and files attached to a run.").option("--out <dir>", "Output directory").action(action(pullRunExpected));
191760
+ addJsonFlag(withBaseUrl(expected.command("upload <run-id> <file>"))).description("Upload a local file as an expected artifact.").option("--name <name>", "Expected artifact name").action(action(uploadRunExpected));
191761
+ addJsonFlag(withBaseUrl(expected.command("copy-output <run-id> <output-file>"))).description("Copy a generated output file into expected artifacts.").option("--name <name>", "Expected artifact name").action(action(copyOutputToExpected));
191762
+ addJsonFlag(withBaseUrl(expected.command("rename <run-id> <old-name> <new-name>"))).description("Rename an expected artifact.").action(action(renameRunExpected));
191763
+ addJsonFlag(withBaseUrl(expected.command("delete <run-id> <name>"))).description("Delete an expected artifact.").option("--yes", "Required in non-interactive environments").action(action(deleteRunExpected));
191764
+ }
191765
+ async function runExecution(agentId, opts) {
191766
+ const client = buildClient(opts);
191767
+ const runPath = `/api/v1/agents/${encodeURIComponent(agentId)}/run${opts.sourceRef ? `?sourceRef=${encodeURIComponent(opts.sourceRef)}` : ""}`;
191768
+ let payload;
191769
+ if (opts.inputFile && opts.inputFile.length > 0) {
191770
+ const form = await buildAgentExecutionRunFormData(opts.inputFile, opts.inputJson);
191771
+ payload = await client.postFormData(runPath, form);
191772
+ } else {
191773
+ payload = await client.post(runPath, {
191774
+ input: opts.inputJson ? JSON.parse(opts.inputJson) : {},
191775
+ ...opts.sourceRef ? { sourceRef: opts.sourceRef } : {}
191776
+ });
191777
+ }
191778
+ const runId = String(payload.runId ?? "");
191779
+ let waitedForTerminalRun = false;
191780
+ if (opts.wait && runId) {
191781
+ payload = await pollRun(client, runId, opts.interval ?? 2, opts.maxWait ?? 1800);
191782
+ waitedForTerminalRun = true;
191783
+ }
191784
+ renderRunPayload(payload, opts);
191785
+ if (waitedForTerminalRun)
191786
+ setExitCodeForFailedTerminalRun(payload);
191787
+ }
191788
+ async function runExample(agentId, opts) {
191789
+ const client = buildClient(opts);
191790
+ const started = await client.post(`/api/v1/agents/${encodeURIComponent(agentId)}/experiments`, {
191791
+ exampleId: opts.example,
191792
+ ...opts.sourceRef ? { sourceRef: opts.sourceRef } : {}
191793
+ });
191794
+ const runId = started.runs?.[0]?.runId;
191795
+ if (opts.wait && runId) {
191796
+ const payload2 = await pollRun(client, runId, opts.interval, opts.maxWait);
191797
+ renderRunPayload(payload2, opts);
191798
+ setExitCodeForFailedTerminalRun(payload2);
191799
+ return;
191800
+ }
191801
+ const payload = {
191802
+ runId,
191803
+ exampleId: opts.example,
191804
+ batchId: started.batchId,
191805
+ status: "pending"
191806
+ };
191807
+ if (opts.json)
191808
+ return printJson(payload);
191809
+ success2(`Run ${runId ?? ""} queued for example ${opts.example}`);
191810
+ }
191811
+ async function getRun(executionId, opts) {
191812
+ const client = buildClient(opts);
191813
+ const include = opts.include ? `detail,${opts.include}` : "detail";
191814
+ const payload = await client.get(`/api/v1/runs/${encodeURIComponent(executionId)}`, {
191815
+ include
191816
+ });
191817
+ const run = payload.run;
191818
+ if (!run)
191819
+ return renderRunPayload(payload, opts);
191820
+ if (opts.step && !isWorkflowRun(run)) {
191821
+ throw new Error("Agent runs do not have workflow steps. Use `eigenpal runs get <run-id> --json` to inspect the agent run payload.");
191822
+ }
191823
+ if (isWorkflowRun(run)) {
191824
+ const steps = filterWorkflowSteps(run.stepExecutions, opts.step);
191825
+ const projectionIncludes = workflowStepProjectionIncludes(opts.include);
191826
+ if (opts.step) {
191827
+ return printJson({
191828
+ ...payload,
191829
+ run: { ...run, stepExecutions: projectWorkflowSteps(steps, projectionIncludes) }
191830
+ });
191831
+ }
191832
+ if (opts.json) {
191833
+ return printJson(payload);
191834
+ }
191835
+ console.log(renderFrame(toExecutionSnapshot(run, executionId)));
191836
+ process.stderr.write(`${ui.dim(`use --json for the full payload, then pipe to jq for field projection`)}
191837
+ `);
191838
+ return;
191839
+ }
191840
+ renderRunPayload(payload, opts);
191841
+ }
191842
+ function isWorkflowRun(run) {
191843
+ return Array.isArray(run.stepExecutions);
191844
+ }
191845
+ function isAgentRun(run) {
191846
+ return run.type === "agent";
191847
+ }
191848
+ function ensureAgentRun(run, feature) {
191849
+ if (isAgentRun(run))
191850
+ return;
191851
+ throw new Error(`${feature} is only available for agent runs. Workflow runs do not have agent artifacts, feedback, or expected files.`);
191852
+ }
191853
+ async function getRunForCompatibilityCheck(client, executionId) {
191854
+ const payload = await client.get(`/api/v1/runs/${encodeURIComponent(executionId)}`, {
191855
+ include: "detail"
191856
+ });
191857
+ if (!payload.run)
191858
+ throw new Error(`Run ${executionId} not found`);
191859
+ return payload.run;
191860
+ }
191861
+ async function ensureAgentRunById(client, executionId, feature) {
191862
+ ensureAgentRun(await getRunForCompatibilityCheck(client, executionId), feature);
191863
+ }
191864
+ function workflowSteps(value) {
191865
+ return Array.isArray(value) ? value : [];
191866
+ }
191867
+ function filterWorkflowSteps(value, stepFilter) {
191868
+ const steps = workflowSteps(value);
191869
+ if (!stepFilter)
191870
+ return steps;
191871
+ const wanted = new Set(stepFilter.split(",").map((step) => step.trim()).filter(Boolean));
191872
+ return steps.filter((step) => typeof step.stepName === "string" && wanted.has(step.stepName));
191873
+ }
191874
+ function projectWorkflowSteps(steps, includes) {
191875
+ return steps.map((step) => projectWorkflowStep(step, includes));
191876
+ }
191877
+ function workflowStepProjectionIncludes(includes) {
191878
+ if (!includes)
191879
+ return;
191880
+ const parts = new Set(includes.split(",").map((part) => part.trim()));
191881
+ const workflowFields = [
191882
+ "input",
191883
+ "config",
191884
+ "resolvedConfig",
191885
+ "inputRef",
191886
+ "inputData",
191887
+ "output",
191888
+ "outputData",
191889
+ "error",
191890
+ "duration"
191891
+ ];
191892
+ return workflowFields.some((field) => parts.has(field)) ? includes : undefined;
191893
+ }
191894
+ function projectWorkflowStep(step, includes) {
191895
+ if (!includes)
191896
+ return step;
191897
+ const wanted = new Set(includes.split(",").map((part) => part.trim()));
191898
+ const out = { stepName: step.stepName, status: step.status };
191899
+ if (wanted.has("input") || wanted.has("config") || wanted.has("resolvedConfig")) {
191900
+ out.input = step.resolvedConfig;
191901
+ }
191902
+ if (wanted.has("inputRef") || wanted.has("inputData"))
191903
+ out.inputRef = step.inputData;
191904
+ if (wanted.has("output") || wanted.has("outputData"))
191905
+ out.output = step.outputData;
191906
+ if (wanted.has("error"))
191907
+ out.error = step.error;
191908
+ if (wanted.has("duration"))
191909
+ out.durationMs = step.durationMs;
191910
+ if ("overrideMode" in step)
191911
+ out.overrideMode = step.overrideMode;
191912
+ return out;
191913
+ }
191914
+ function toExecutionSnapshot(run, fallbackId) {
191915
+ return {
191916
+ id: String(run.id ?? run.executionId ?? fallbackId),
191917
+ status: String(run.status ?? "unknown"),
191918
+ startedAt: typeof run.startedAt === "string" ? run.startedAt : null,
191919
+ completedAt: typeof run.completedAt === "string" ? run.completedAt : null,
191920
+ durationMs: typeof run.durationMs === "number" ? run.durationMs : null,
191921
+ stepExecutions: workflowSteps(run.stepExecutions).map((step) => ({
191922
+ stepName: String(step.stepName ?? ""),
191923
+ status: String(step.status ?? "unknown"),
191924
+ durationMs: typeof step.durationMs === "number" ? step.durationMs : null,
191925
+ error: typeof step.error === "string" ? step.error : null,
191926
+ outputPreview: previewOutput(step.outputData)
191927
+ })),
191928
+ error: typeof run.error === "string" ? run.error : null
191929
+ };
191930
+ }
191931
+ var PREVIEW_MAX = 120;
191932
+ function previewOutput(value) {
191933
+ if (value == null)
191934
+ return null;
191935
+ if (typeof value === "string") {
191936
+ if (value.length === 0)
191937
+ return null;
191938
+ return value.length > PREVIEW_MAX ? `${value.slice(0, PREVIEW_MAX - 3)}...` : value;
191939
+ }
191940
+ let json2;
191941
+ try {
191942
+ json2 = JSON.stringify(value);
191943
+ } catch {
191944
+ return null;
191945
+ }
191946
+ if (!json2 || json2 === "{}" || json2 === "[]" || json2 === "null")
191947
+ return null;
191948
+ const flat = json2.replace(/\s+/g, " ");
191949
+ return flat.length > PREVIEW_MAX ? `${flat.slice(0, PREVIEW_MAX - 3)}...` : flat;
191950
+ }
191951
+ async function rerunRun(executionId, opts) {
191952
+ const client = buildClient(opts);
191953
+ const sourceRef = await resolveRerunSourceRef(client, executionId, opts.sourceRef);
191954
+ let payload = await client.post(`/api/v1/runs/${encodeURIComponent(executionId)}/rerun`, sourceRef ? { sourceRef } : {});
191955
+ const rerunId = String(payload.runId ?? payload.executionId ?? "");
191956
+ if (opts.wait && rerunId) {
191957
+ payload = await pollRun(client, rerunId, opts.interval, opts.maxWait);
191958
+ renderRunPayload(payload, opts);
191959
+ setExitCodeForFailedTerminalRun(payload);
191960
+ return;
191961
+ }
191962
+ if (opts.json)
191963
+ return printJson(payload);
191964
+ success2(`Started rerun ${ui.bold(rerunId)} from ${executionId}`);
191965
+ }
191966
+ async function resolveRerunSourceRef(client, executionId, requested) {
191967
+ if (!requested)
191968
+ return;
191969
+ if (requested !== "original")
191970
+ return requested;
191971
+ const payload = await client.get(`/api/v1/runs/${encodeURIComponent(executionId)}`, {
191972
+ include: "detail"
191973
+ });
191974
+ const run = payload.run;
191975
+ if (!run)
191976
+ throw new Error(`Run ${executionId} not found`);
191977
+ const resolved = stringOrNull(run.resolvedGitRef);
191978
+ const requestedSource = stringOrNull(run.requestedSourceRef);
191979
+ const original = resolved ?? requestedSource;
191980
+ if (!original) {
191981
+ throw new Error(`Run ${executionId} does not have an original source ref to reuse`);
191982
+ }
191983
+ return original;
191984
+ }
191985
+ async function fetchRunArtifacts(executionId, opts) {
191986
+ const client = buildClient(opts);
191987
+ const payload = await client.get(`/api/v1/runs/${encodeURIComponent(executionId)}`, {
191988
+ include: "detail,expected,files,input,output,issues,trace,lockfile,metadata"
191989
+ });
191990
+ const run = payload.run;
191991
+ if (!run)
191992
+ throw new Error(`Run ${executionId} not found`);
191993
+ ensureAgentRun(run, "Artifact download");
191994
+ const out = path8.resolve(opts.out ?? path8.join(".eigenpal", "artifacts", "runs", executionId));
191995
+ await fs3.mkdir(out, { recursive: true });
191996
+ const written = [];
191997
+ const skipped = [];
191998
+ const selectedPaths = opts.path && opts.path.length > 0 ? opts.path : runArtifactInventory(run).filter((artifact) => artifactIncluded(artifact, opts.include)).map((artifact) => artifact.path);
191999
+ for (const artifactPath of selectedPaths) {
192000
+ const normalized = normalizeArtifactPath(artifactPath);
192001
+ if (!normalized) {
192002
+ skipped.push(artifactPath);
192003
+ continue;
192004
+ }
192005
+ const result = await writeRunArtifact(client, executionId, out, normalized, run);
192006
+ if (result)
192007
+ written.push(result);
192008
+ else
192009
+ skipped.push(normalized);
192010
+ }
192011
+ const summary = {
192012
+ runId: executionId,
192013
+ out,
192014
+ written,
192015
+ skipped,
192016
+ counts: {
192017
+ written: written.length,
192018
+ skipped: skipped.length
192019
+ }
192020
+ };
192021
+ if (opts.json)
192022
+ return printJson(summary);
192023
+ success2(`Fetched run artifacts for ${executionId} to ${out}`);
192024
+ dim(`Wrote ${written.length} artifact${written.length === 1 ? "" : "s"}${skipped.length ? `; skipped missing ${skipped.join(", ")}` : ""}`);
192025
+ }
192026
+ async function listRunArtifacts(executionId, opts) {
192027
+ const client = buildClient(opts);
192028
+ const payload = await client.get(`/api/v1/runs/${encodeURIComponent(executionId)}`, {
192029
+ include: "detail,expected,files,input,output,issues,trace,lockfile,metadata"
192030
+ });
192031
+ const run = payload.run;
192032
+ if (!run)
192033
+ throw new Error(`Run ${executionId} not found`);
192034
+ ensureAgentRun(run, "Artifact inventory");
192035
+ const artifacts = runArtifactInventory(run);
192036
+ if (opts.json)
192037
+ return printJson({ runId: executionId, artifacts });
192038
+ console.log(table(artifacts, [
192039
+ { key: "kind", header: "KIND" },
192040
+ { key: "name", header: "NAME" },
192041
+ { key: "path", header: "PATH" },
192042
+ { key: "present", header: "PRESENT" }
192043
+ ]));
192044
+ }
192045
+ function artifactIncluded(artifact, include) {
192046
+ const parts = new Set(include.split(",").map((part) => part.trim()).filter(Boolean));
192047
+ if (parts.has("all"))
192048
+ return true;
192049
+ if (parts.has(artifact.kind))
192050
+ return true;
192051
+ if (parts.has("files") && (artifact.kind === "input" || artifact.kind === "output"))
192052
+ return true;
192053
+ if (parts.has("metadata") && artifact.path === "run.json")
192054
+ return true;
192055
+ return false;
192056
+ }
192057
+ function normalizeArtifactPath(artifactPath) {
192058
+ const parts = artifactPath.split("/");
192059
+ if (artifactPath.startsWith("/") || parts.some((part) => part === "" || part === "." || part === "..")) {
192060
+ return null;
192061
+ }
192062
+ return artifactPath;
192063
+ }
192064
+ async function writeJsonRunArtifact(out, artifactPath, value) {
192065
+ await fs3.mkdir(path8.dirname(path8.join(out, artifactPath)), { recursive: true });
192066
+ await fs3.writeFile(path8.join(out, artifactPath), JSON.stringify(value, null, 2));
192067
+ }
192068
+ async function writeDownloadedRunArtifact(client, executionId, out, artifactPath, routePrefix) {
192069
+ const routePath = routePrefix === "files" ? artifactPath : artifactPath.slice("expected/".length);
192070
+ const routeBase = routePrefix === "files" ? "artifact" : "expected";
192071
+ const response = await client.getStream(`/api/v1/runs/${encodeURIComponent(executionId)}/${routeBase}/${encodeRunArtifactPath(routePath)}`);
192072
+ const outPath = path8.join(out, artifactPath);
192073
+ await fs3.mkdir(path8.dirname(outPath), { recursive: true });
192074
+ await fs3.writeFile(outPath, Buffer.from(await response.arrayBuffer()));
192075
+ }
192076
+ async function writeRunArtifact(client, executionId, out, artifactPath, run) {
192077
+ if (artifactPath === "run.json") {
192078
+ await writeJsonRunArtifact(out, artifactPath, run);
192079
+ return artifactPath;
192080
+ }
192081
+ if (artifactPath === "input.json") {
192082
+ if (run.inputJson == null)
192083
+ return null;
192084
+ await writeJsonRunArtifact(out, artifactPath, run.inputJson);
192085
+ return artifactPath;
192086
+ }
192087
+ if (artifactPath === "metadata.json") {
192088
+ if (run.metadata == null)
192089
+ return null;
192090
+ await writeJsonRunArtifact(out, artifactPath, run.metadata);
192091
+ return artifactPath;
192092
+ }
192093
+ if (artifactPath === "expected.json") {
192094
+ if (run.expected == null)
192095
+ return null;
192096
+ await writeJsonRunArtifact(out, artifactPath, run.expected);
192097
+ return artifactPath;
192098
+ }
192099
+ if (artifactPath.startsWith("expected/")) {
192100
+ await writeDownloadedRunArtifact(client, executionId, out, artifactPath, "expected");
192101
+ return artifactPath;
192102
+ }
192103
+ await writeDownloadedRunArtifact(client, executionId, out, artifactPath, "files");
192104
+ return artifactPath;
192105
+ }
192106
+ async function traceRun(executionId, opts) {
192107
+ const client = buildClient(opts);
192108
+ await ensureAgentRunById(client, executionId, "Trace download");
192109
+ const text = await downloadTraceText(client, executionId);
192110
+ if (!opts.out) {
192111
+ process.stdout.write(text);
192112
+ return;
192113
+ }
192114
+ const out = path8.resolve(opts.out);
192115
+ await fs3.mkdir(path8.dirname(out), { recursive: true });
192116
+ await fs3.writeFile(out, text);
192117
+ success2(`Downloaded trace for ${executionId} to ${ui.bold(out)}`);
192118
+ }
192119
+ async function compareRun(referenceId, executionId, opts) {
192120
+ const client = buildClient(opts);
192121
+ const targetPayload = await client.get(`/api/v1/runs/${encodeURIComponent(executionId)}`, {
192122
+ include: "detail,files,output"
192123
+ });
192124
+ const target = targetPayload.run;
192125
+ if (!target)
192126
+ throw new Error(`Run ${executionId} not found`);
192127
+ const mode = opts.baseline ? "baseline" : "expected";
192128
+ const reference = (await client.get(`/api/v1/runs/${encodeURIComponent(referenceId)}`, {
192129
+ include: mode === "baseline" ? "detail,files,output" : "detail,expected"
192130
+ })).run;
192131
+ if (!reference)
192132
+ throw new Error(`Reference run ${referenceId} not found`);
192133
+ if (isWorkflowRun(reference) && isWorkflowRun(target)) {
192134
+ const report2 = compareWorkflowRuns(referenceId, reference, executionId, target, opts.step);
192135
+ if (opts.json) {
192136
+ printJson(report2);
192137
+ } else {
192138
+ renderWorkflowComparisonReport(report2);
192139
+ }
192140
+ if (opts.failOnDiff && report2.status === "fail")
192141
+ process.exit(1);
192142
+ return;
192143
+ }
192144
+ if (opts.step) {
192145
+ throw new Error("`--step` is only supported when both runs are workflow runs. Agent runs do not have workflow steps.");
192146
+ }
192147
+ if (isWorkflowRun(reference) || isWorkflowRun(target)) {
192148
+ throw new Error("Mixed workflow/agent comparisons are not supported. Compare two workflow runs or two agent runs.");
192149
+ }
192150
+ const out = path8.resolve(opts.out ?? path8.join(".eigenpal", "artifacts", "comparisons", `${referenceId}..${executionId}`));
192151
+ await fs3.mkdir(out, { recursive: true });
192152
+ const warnings = [];
192153
+ if (mode === "expected" && reference.expected == null && fileNames(reference.expectedFiles).length === 0) {
192154
+ warnings.push(`Reference run ${referenceId} has no expected JSON or expected files; comparison has no baseline artifacts. Use --baseline to compare actual outputs.`);
192155
+ }
192156
+ const jsonDiffs = diffJson(mode === "baseline" ? reference.output : reference.expected, target.output);
192157
+ const expectedFiles = mode === "baseline" ? fileNames(reference.resultFiles).filter((name) => name !== "issues.md" && name !== "trace.jsonl") : fileNames(reference.expectedFiles);
192158
+ const outputFiles = fileNames(target.resultFiles).filter((name) => name !== "issues.md" && name !== "trace.jsonl");
192159
+ const inventory = compareFileInventory(expectedFiles, outputFiles, Boolean(opts.normalizeDates));
192160
+ const textDifferences = await compareMatchedFileText(client, { runId: referenceId, kind: mode === "baseline" ? "output" : "expected" }, { runId: executionId, kind: "output" }, out, inventory.matched, Boolean(opts.normalizeDates));
192161
+ const report = {
192162
+ status: jsonDiffs.length === 0 && inventory.missing.length === 0 && inventory.extra.length === 0 && textDifferences.every((diff) => diff.status === "match" || diff.status === "binary-match") ? "pass" : "fail",
192163
+ runId: executionId,
192164
+ comparedWithRunId: referenceId,
192165
+ mode,
192166
+ warnings,
192167
+ jsonDifferences: jsonDiffs,
192168
+ matchedFiles: inventory.matched,
192169
+ missingFiles: inventory.missing,
192170
+ extraFiles: inventory.extra,
192171
+ textDifferences
192172
+ };
192173
+ await fs3.writeFile(path8.join(out, "comparison.json"), JSON.stringify(report, null, 2));
192174
+ if (opts.json) {
192175
+ printJson(report);
192176
+ } else {
192177
+ renderComparisonReport(report);
192178
+ }
192179
+ dim(`Comparison artifacts written to ${ui.bold(out)}`);
192180
+ if (opts.failOnDiff && report.status === "fail")
192181
+ process.exit(1);
192182
+ }
192183
+ function compareWorkflowRuns(referenceId, reference, targetId, target, stepFilter) {
192184
+ const referenceSteps = workflowSteps(reference.stepExecutions);
192185
+ const targetSteps = workflowSteps(target.stepExecutions);
192186
+ const stepNames = new Set;
192187
+ for (const step of referenceSteps)
192188
+ if (step.stepName)
192189
+ stepNames.add(step.stepName);
192190
+ for (const step of targetSteps)
192191
+ if (step.stepName)
192192
+ stepNames.add(step.stepName);
192193
+ const wanted = stepFilter ? new Set(stepFilter.split(",").map((step) => step.trim()).filter(Boolean)) : null;
192194
+ const steps = [...stepNames].sort().filter((stepName) => !wanted || wanted.has(stepName)).map((stepName) => {
192195
+ const referenceStep = referenceSteps.find((step) => step.stepName === stepName);
192196
+ const targetStep = targetSteps.find((step) => step.stepName === stepName);
192197
+ const referenceDuration = typeof referenceStep?.durationMs === "number" ? referenceStep.durationMs : null;
192198
+ const targetDuration = typeof targetStep?.durationMs === "number" ? targetStep.durationMs : null;
192199
+ return {
192200
+ stepName,
192201
+ referenceStatus: String(referenceStep?.status ?? "-"),
192202
+ targetStatus: String(targetStep?.status ?? "-"),
192203
+ durationDeltaMs: referenceDuration != null && targetDuration != null ? targetDuration - referenceDuration : null,
192204
+ outputState: compareOutputs(referenceStep?.outputData, targetStep?.outputData)
192205
+ };
192206
+ });
192207
+ const finalOutputState = compareOutputs(reference.output, target.output);
192208
+ const status = finalOutputState === "identical" && steps.every((step) => step.referenceStatus === step.targetStatus && (step.outputState === "identical" || step.outputState === "-")) ? "pass" : "fail";
192209
+ return {
192210
+ status,
192211
+ runId: targetId,
192212
+ comparedWithRunId: referenceId,
192213
+ steps,
192214
+ finalOutputState
192215
+ };
192216
+ }
192217
+ function renderWorkflowComparisonReport(report) {
192218
+ console.log(`${ui.dim("A =")} ${ui.bold(report.comparedWithRunId)} ${ui.dim("B =")} ${ui.bold(report.runId)}`);
192219
+ console.log("");
192220
+ console.log(ui.dim(`step A status B status Δ duration output`));
192221
+ console.log(ui.dim(`------------------------ --------- --------- ---------- ------`));
192222
+ for (const step of report.steps) {
192223
+ const delta = step.durationDeltaMs == null ? "-" : `${step.durationDeltaMs >= 0 ? "+" : ""}${step.durationDeltaMs}ms`;
192224
+ console.log(`${step.stepName.padEnd(28)} ${padColored(colorStatus(step.referenceStatus), step.referenceStatus, 11)} ${padColored(colorStatus(step.targetStatus), step.targetStatus, 11)} ${delta.padEnd(12)} ${colorOutputState(step.outputState)}`);
192225
+ }
192226
+ console.log("");
192227
+ console.log(`${ui.dim("final output:")} ${colorOutputState(report.finalOutputState)}`);
192228
+ }
192229
+ function padColored(colored, plain, width) {
192230
+ const pad = Math.max(0, width - plain.length);
192231
+ return colored + " ".repeat(pad);
192232
+ }
192233
+ function colorStatus(status) {
192234
+ if (!status)
192235
+ return "-";
192236
+ if (status === "completed")
192237
+ return ui.ok(status);
192238
+ if (status === "failed" || status === "cancelled")
192239
+ return ui.err(status);
192240
+ if (status === "running" || status === "pending")
192241
+ return ui.info(status);
192242
+ return status;
192243
+ }
192244
+ function colorOutputState(state) {
192245
+ if (state === "identical")
192246
+ return ui.ok(state);
192247
+ if (state === "-")
192248
+ return ui.dim(state);
192249
+ if (state.startsWith("differs"))
192250
+ return ui.warn(state);
192251
+ return state;
192252
+ }
192253
+ function compareOutputs(reference, target) {
192254
+ if (reference === undefined && target === undefined)
192255
+ return "-";
192256
+ if (reference === undefined)
192257
+ return "B only";
192258
+ if (target === undefined)
192259
+ return "A only";
192260
+ const referenceJson = JSON.stringify(reference);
192261
+ const targetJson = JSON.stringify(target);
192262
+ if (referenceJson === targetJson)
192263
+ return "identical";
192264
+ return `differs (A=${referenceJson.length}b, B=${targetJson.length}b)`;
192265
+ }
192266
+ async function listRuns(source, opts) {
192267
+ const client = buildClient(opts);
192268
+ const parsedAgentTarget = source && (opts.type === "agent" || source.startsWith("agents.") || source.includes("@")) ? parseAgentTarget(source) : null;
192269
+ const resolvedWorkflowSource = source && opts.type === "workflow" ? await resolveWorkflowId(client, source) : undefined;
192270
+ const params = {
192271
+ ...buildRunListParams(opts),
192272
+ ...parsedAgentTarget ? { type: opts.type ?? "agent", source: parsedAgentTarget.slug } : resolvedWorkflowSource ? { source: resolvedWorkflowSource } : source ? { source } : {},
192273
+ ...parsedAgentTarget?.sourceRef ? { sourceRef: parsedAgentTarget.sourceRef } : {}
192274
+ };
192275
+ const payload = await client.get("/api/v1/runs", params);
192276
+ const rows = opts.compact ? payload.runs.map(compactRunRow) : payload.runs;
192277
+ if (opts.json)
192278
+ return printJson({ ...payload, runs: rows });
192279
+ if (payload.scanLimited) {
192280
+ warn("Feedback/expected filters scanned only the first matching window. Increase --scan-limit or narrow DB filters for a fuller result.");
192281
+ }
192282
+ if (payload.noResolvedAnchor) {
192283
+ warn("No resolved feedback anchor was found in the scan window; --since-last-resolved returned no rows.");
192284
+ }
192285
+ console.log(table(rows, [
192286
+ { key: "id", header: "ID" },
192287
+ { key: "type", header: "TYPE" },
192288
+ { key: "status", header: "STATUS" },
192289
+ {
192290
+ key: "sourceName",
192291
+ header: "SOURCE",
192292
+ format: (value, row) => String(value ?? row.sourceId ?? "")
192293
+ },
192294
+ { key: "exampleId", header: "EXAMPLE" },
192295
+ {
192296
+ key: "feedback",
192297
+ header: "FEEDBACK",
192298
+ format: (value) => value && typeof value === "object" ? `${String(value.rating ?? "")}/${String(value.status ?? "")}` : ""
192299
+ },
192300
+ { key: "createdAt", header: "CREATED", format: formatTimestamp }
192301
+ ]));
192302
+ }
192303
+ function buildRunListParams(opts) {
192304
+ return compactParams({
192305
+ type: "type" in opts ? opts.type : undefined,
192306
+ sourceRef: "sourceRef" in opts ? opts.sourceRef : undefined,
192307
+ status: "status" in opts ? opts.status : undefined,
192308
+ batchId: "batchId" in opts ? opts.batchId : undefined,
192309
+ exampleId: "exampleId" in opts ? opts.exampleId : undefined,
192310
+ exampleIdContains: "exampleIdContains" in opts ? opts.exampleIdContains : undefined,
192311
+ from: "from" in opts ? opts.from : undefined,
192312
+ to: "to" in opts ? opts.to : undefined,
192313
+ createdAfter: "createdAfter" in opts ? opts.createdAfter : undefined,
192314
+ createdBefore: "createdBefore" in opts ? opts.createdBefore : undefined,
192315
+ completedAfter: "completedAfter" in opts ? opts.completedAfter : undefined,
192316
+ completedBefore: "completedBefore" in opts ? opts.completedBefore : undefined,
192317
+ limit: "limit" in opts ? opts.limit : undefined,
192318
+ offset: "offset" in opts ? opts.offset : undefined,
192319
+ sort: "sort" in opts ? opts.sort : undefined,
192320
+ order: "order" in opts ? opts.order : undefined
192321
+ });
192322
+ }
192323
+ async function updateRunFeedback(executionId, opts) {
192324
+ const client = buildClient(opts);
192325
+ await ensureAgentRunById(client, executionId, "Feedback");
192326
+ const body = {};
192327
+ if (opts.status)
192328
+ body.status = opts.status;
192329
+ if (opts.rating)
192330
+ body.rating = opts.rating === "none" ? null : opts.rating;
192331
+ if (opts.clearRating)
192332
+ body.rating = null;
192333
+ if (opts.messageFile)
192334
+ body.body = await fs3.readFile(opts.messageFile, "utf-8");
192335
+ if (opts.message !== undefined)
192336
+ body.body = opts.message;
192337
+ if (opts.clearMessage)
192338
+ body.body = "";
192339
+ if (opts.expectedJsonFile)
192340
+ body.expected = JSON.parse(await fs3.readFile(opts.expectedJsonFile, "utf-8"));
192341
+ if (opts.expectedJson !== undefined)
192342
+ body.expected = JSON.parse(opts.expectedJson);
192343
+ if (opts.clearExpectedJson)
192344
+ body.expected = null;
192345
+ const payload = await client.patch(`/api/v1/runs/${encodeURIComponent(executionId)}/feedback`, body);
192346
+ renderGeneric(payload, opts, `Updated feedback for ${executionId}`);
192347
+ }
192348
+ function compactRunRow(run) {
192349
+ const feedback = run.feedback && typeof run.feedback === "object" ? run.feedback : null;
192350
+ return {
192351
+ id: run.id,
192352
+ status: run.status,
192353
+ exampleId: run.exampleId,
192354
+ feedback: feedback ? {
192355
+ rating: feedback.rating ?? null,
192356
+ status: feedback.status ?? null,
192357
+ updatedAt: feedback.updatedAt ?? null
192358
+ } : null,
192359
+ hasExpectedJson: run.expected != null,
192360
+ expectedFileCount: Array.isArray(run.expectedFiles) ? run.expectedFiles.length : undefined,
192361
+ createdAt: run.createdAt,
192362
+ completedAt: run.completedAt
192363
+ };
192364
+ }
192365
+ async function clearRunFeedback(executionId, opts) {
192366
+ if (!(opts.yes || await confirmTyped(executionId, "clear feedback artifacts"))) {
192367
+ throw new Error("Clear cancelled");
192368
+ }
192369
+ const client = buildClient(opts);
192370
+ await ensureAgentRunById(client, executionId, "Feedback");
192371
+ const payload = await client.delete(`/api/v1/runs/${encodeURIComponent(executionId)}/feedback`);
192372
+ renderGeneric(payload, opts, `Cleared feedback for ${executionId}`);
192373
+ }
192374
+ async function listRunExpected(executionId, opts) {
192375
+ const client = buildClient(opts);
192376
+ await ensureAgentRunById(client, executionId, "Expected artifacts");
192377
+ const payload = await client.get(`/api/v1/runs/${encodeURIComponent(executionId)}/expected`);
192378
+ if (opts.json)
192379
+ return printJson(payload);
192380
+ console.log(table(payload.files ?? [], [{ key: "name", header: "NAME" }]));
192381
+ if (payload.expected != null)
192382
+ dim("expected.json present");
192383
+ }
192384
+ async function pullRunExpected(executionId, opts) {
192385
+ const client = buildClient(opts);
192386
+ await ensureAgentRunById(client, executionId, "Expected artifacts");
192387
+ const payload = await client.get(`/api/v1/runs/${encodeURIComponent(executionId)}/expected`);
192388
+ const out = path8.resolve(opts.out ?? path8.join(executionId, "expected"));
192389
+ await fs3.mkdir(out, { recursive: true });
192390
+ if (payload.expected != null) {
192391
+ await fs3.writeFile(path8.join(out, "expected.json"), JSON.stringify(payload.expected, null, 2));
192392
+ }
192393
+ await downloadExpectedFiles(client, executionId, out, payload.files);
192394
+ success2(`Pulled expected artifacts for ${executionId} to ${out}`);
192395
+ }
192396
+ async function downloadExpectedFiles(client, executionId, out, files) {
192397
+ const rows = Array.isArray(files) ? files : [];
192398
+ const written = await Promise.all(rows.map(async (file2) => {
192399
+ const name = String(file2.name ?? "");
192400
+ if (!name || name.includes("/") || name.includes(".."))
192401
+ return null;
192402
+ const response = await client.getStream(`/api/v1/runs/${encodeURIComponent(executionId)}/expected/${encodeURIComponent(name)}`);
192403
+ await fs3.mkdir(out, { recursive: true });
192404
+ await fs3.writeFile(path8.join(out, name), Buffer.from(await response.arrayBuffer()));
192405
+ return name;
192406
+ }));
192407
+ return written.filter((name) => Boolean(name));
192408
+ }
192409
+ async function uploadRunExpected(executionId, file2, opts) {
192410
+ const client = buildClient(opts);
192411
+ await ensureAgentRunById(client, executionId, "Expected artifacts");
192412
+ const form = new FormData;
192413
+ const data = await fs3.readFile(file2);
192414
+ form.append("file", new Blob([data]), path8.basename(file2));
192415
+ if (opts.name)
192416
+ form.append("name", opts.name);
192417
+ const payload = await client.postFormData(`/api/v1/runs/${encodeURIComponent(executionId)}/expected`, form);
192418
+ renderGeneric(payload, opts, `Uploaded expected file for ${executionId}`);
192419
+ }
192420
+ async function copyOutputToExpected(executionId, outputFile, opts) {
192421
+ const client = buildClient(opts);
192422
+ await ensureAgentRunById(client, executionId, "Expected artifacts");
192423
+ const payload = await client.post(`/api/v1/runs/${encodeURIComponent(executionId)}/expected`, {
192424
+ outputFileName: outputFile,
192425
+ ...opts.name ? { expectedName: opts.name } : {}
192426
+ });
192427
+ renderGeneric(payload, opts, `Copied output file to expected for ${executionId}`);
192428
+ }
192429
+ async function renameRunExpected(executionId, oldName, newName, opts) {
192430
+ const client = buildClient(opts);
192431
+ await ensureAgentRunById(client, executionId, "Expected artifacts");
192432
+ const payload = await client.patch(`/api/v1/runs/${encodeURIComponent(executionId)}/expected/${encodeURIComponent(oldName)}`, { name: newName });
192433
+ renderGeneric(payload, opts, `Renamed expected file for ${executionId}`);
192434
+ }
192435
+ async function deleteRunExpected(executionId, name, opts) {
192436
+ if (!(opts.yes || await confirmTyped(name, "delete expected file"))) {
192437
+ throw new Error("Delete cancelled");
192438
+ }
192439
+ const client = buildClient(opts);
192440
+ await ensureAgentRunById(client, executionId, "Expected artifacts");
192441
+ await client.delete(`/api/v1/runs/${encodeURIComponent(executionId)}/expected/${encodeURIComponent(name)}`);
192442
+ renderGeneric({ ok: true }, opts, `Deleted expected file ${name}`);
192443
+ }
192444
+ async function watchRunCommand(executionId, opts) {
192445
+ const client = buildClient(opts);
192446
+ if (opts.json) {
192447
+ const payload = await pollRun(client, executionId, opts.interval, opts.maxWait);
192448
+ renderRunPayload(payload, opts);
192449
+ setExitCodeForFailedTerminalRun(payload);
192450
+ return;
192451
+ }
192452
+ const result = await watchExecution({
192453
+ fetch: async () => {
192454
+ const payload = await client.get(`/api/v1/runs/${encodeURIComponent(executionId)}`, {
192455
+ include: "detail"
192456
+ });
192457
+ return toExecutionSnapshot(payload.run ?? {}, executionId);
192458
+ },
192459
+ maxMs: opts.maxWait * 1000,
192460
+ transitionIntervalMs: opts.interval * 1000,
192461
+ steadyIntervalMs: Math.max(opts.interval, 5) * 1000
192462
+ });
192463
+ if (result.detached) {
192464
+ process.stderr.write(`
192465
+ Detached after ${opts.maxWait}s without reaching terminal status. Re-run \`eigenpal runs watch ${executionId}\` to resume.
192466
+ `);
192467
+ process.exit(2);
192468
+ }
192469
+ if (result.final.status === "failed" || result.final.status === "cancelled") {
192470
+ process.exitCode = 1;
192471
+ }
192472
+ }
192473
+ async function cancelRun(executionId, opts) {
192474
+ if (!(opts.yes || process.stdin.isTTY))
192475
+ throw new Error("Pass --yes to cancel in non-interactive mode");
192476
+ const client = buildClient(opts);
192477
+ const payload = await client.post(`/api/v1/runs/${encodeURIComponent(executionId)}/cancel`, {});
192478
+ renderRunPayload(payload, opts);
192479
+ }
192480
+ async function resumeRun(executionId, opts) {
192481
+ const client = buildClient(opts);
192482
+ const payload = await client.post(`/api/v1/runs/${encodeURIComponent(executionId)}/resume`, {});
192483
+ renderRunPayload(payload, opts);
192484
+ }
192485
+ async function downloadTraceText(client, executionId) {
192486
+ const response = await client.getStream(`/api/v1/runs/${encodeURIComponent(executionId)}/artifact/trace.jsonl`);
192487
+ return Buffer.from(await response.arrayBuffer()).toString("utf-8");
192488
+ }
192489
+ function diffJson(expected, actual, basePath = "$") {
192490
+ if (expected === undefined || expected === null)
192491
+ return [];
192492
+ if (Object.is(expected, actual))
192493
+ return [];
192494
+ if (expected && actual && typeof expected === "object" && typeof actual === "object" && !Array.isArray(expected) && !Array.isArray(actual)) {
192495
+ const keys = new Set([...Object.keys(expected), ...Object.keys(actual)]);
192496
+ return [...keys].flatMap((key) => {
192497
+ const next = `${basePath}.${key}`;
192498
+ if (!(key in actual))
192499
+ return [{ path: next, type: "missing" }];
192500
+ if (!(key in expected))
192501
+ return [{ path: next, type: "extra" }];
192502
+ return diffJson(expected[key], actual[key], next);
192503
+ });
192504
+ }
192505
+ if (Array.isArray(expected) && Array.isArray(actual)) {
192506
+ const diffs = [];
192507
+ const max = Math.max(expected.length, actual.length);
192508
+ for (let index = 0;index < max; index += 1) {
192509
+ const next = `${basePath}[${index}]`;
192510
+ if (index >= actual.length)
192511
+ diffs.push({ path: next, type: "missing" });
192512
+ else if (index >= expected.length)
192513
+ diffs.push({ path: next, type: "extra" });
192514
+ else
192515
+ diffs.push(...diffJson(expected[index], actual[index], next));
192516
+ }
192517
+ return diffs;
192518
+ }
192519
+ return [
192520
+ {
192521
+ path: basePath,
192522
+ type: "changed",
192523
+ expected: JSON.stringify(expected),
192524
+ actual: JSON.stringify(actual)
192525
+ }
192526
+ ];
192527
+ }
192528
+ function fileNames(files) {
192529
+ return (Array.isArray(files) ? files : []).map((file2) => String(file2.name ?? "")).filter(Boolean).sort();
192530
+ }
192531
+ function stringOrNull(value) {
192532
+ return typeof value === "string" && value.length > 0 ? value : null;
192533
+ }
192534
+ function runArtifactInventory(run) {
192535
+ const rows = [
192536
+ { kind: "metadata", name: "run.json", path: "run.json", present: "yes" }
192537
+ ];
192538
+ if (run.inputJson != null) {
192539
+ rows.push({ kind: "input", name: "input.json", path: "input.json", present: "yes" });
192540
+ }
192541
+ if (run.metadata != null) {
192542
+ rows.push({ kind: "metadata", name: "metadata.json", path: "metadata.json", present: "yes" });
192543
+ }
192544
+ if (run.expected != null) {
192545
+ rows.push({ kind: "expected", name: "expected.json", path: "expected.json", present: "yes" });
192546
+ }
192547
+ for (const name of fileNames(run.inputFiles)) {
192548
+ rows.push({ kind: "input", name, path: `input/${name}`, present: "yes" });
192549
+ }
192550
+ for (const name of fileNames(run.resultFiles).filter((name2) => name2 !== "issues.md" && name2 !== "trace.jsonl")) {
192551
+ rows.push({ kind: "output", name, path: `output/${name}`, present: "yes" });
192552
+ }
192553
+ for (const name of fileNames(run.expectedFiles)) {
192554
+ rows.push({ kind: "expected", name, path: `expected/${name}`, present: "yes" });
192555
+ }
192556
+ for (const name of fileNames(run.issueFiles)) {
192557
+ rows.push({ kind: "issues", name, path: name, present: "yes" });
192558
+ }
192559
+ for (const name of fileNames(run.traceFiles)) {
192560
+ rows.push({ kind: "trace", name, path: name, present: "yes" });
192561
+ }
192562
+ for (const name of fileNames(run.lockfileFiles)) {
192563
+ rows.push({ kind: "lockfile", name, path: name, present: "yes" });
192564
+ }
192565
+ return rows.sort((a, b2) => a.path.localeCompare(b2.path));
192566
+ }
192567
+ function normalizeGeneratedTokens(value) {
192568
+ return value.replace(/\d{4}-\d{2}-\d{2}/g, "<date>").replace(/\d{8}/g, "<date>");
192569
+ }
192570
+ function compareFileInventory(expected, output, normalizeDates) {
192571
+ const normalize = (value) => normalizeDates ? normalizeGeneratedTokens(value) : value;
192572
+ const outputByNormalized = new Map(output.map((name) => [normalize(name), name]));
192573
+ const expectedByNormalized = new Map(expected.map((name) => [normalize(name), name]));
192574
+ const matched = expected.map((expectedName2) => {
192575
+ const outputName = outputByNormalized.get(normalize(expectedName2));
192576
+ return outputName ? { expected: expectedName2, output: outputName } : null;
192577
+ }).filter((match) => Boolean(match));
192578
+ return {
192579
+ matched,
192580
+ missing: expected.filter((name) => !outputByNormalized.has(normalize(name))),
192581
+ extra: output.filter((name) => !expectedByNormalized.has(normalize(name)))
192582
+ };
192583
+ }
192584
+ async function compareMatchedFileText(client, left, right, out, matched, normalizeDates) {
192585
+ const expectedDir = path8.join(out, "expected");
192586
+ const outputDir = path8.join(out, "output");
192587
+ await fs3.mkdir(expectedDir, { recursive: true });
192588
+ await fs3.mkdir(outputDir, { recursive: true });
192589
+ const diffs = [];
192590
+ for (const pair of matched) {
192591
+ const expectedPath = path8.join(expectedDir, pair.expected);
192592
+ const outputPath = path8.join(outputDir, pair.output);
192593
+ await downloadStreamToFile(client, runFileUrl(left, pair.expected), expectedPath);
192594
+ await downloadStreamToFile(client, runFileUrl(right, pair.output), outputPath);
192595
+ const expectedText = extractComparableText(expectedPath);
192596
+ const outputText = extractComparableText(outputPath);
192597
+ if (expectedText.text == null || outputText.text == null) {
192598
+ const [expectedBuffer, outputBuffer] = await Promise.all([
192599
+ fs3.readFile(expectedPath),
192600
+ fs3.readFile(outputPath)
192601
+ ]);
192602
+ const expectedHash = createHash2("sha256").update(expectedBuffer).digest("hex");
192603
+ const outputHash = createHash2("sha256").update(outputBuffer).digest("hex");
192604
+ diffs.push({
192605
+ ...pair,
192606
+ status: expectedBuffer.length === outputBuffer.length && expectedHash === outputHash ? "binary-match" : "binary-different",
192607
+ expectedBytes: expectedBuffer.length,
192608
+ outputBytes: outputBuffer.length,
192609
+ expectedSha256: expectedHash,
192610
+ outputSha256: outputHash,
192611
+ textExtraction: {
192612
+ expected: expectedText.reason ?? null,
192613
+ output: outputText.reason ?? null
192614
+ }
192615
+ });
192616
+ continue;
192617
+ }
192618
+ const normalize = (value) => normalizeDates ? normalizeGeneratedTokens(value).trim() : value.trim();
192619
+ diffs.push({
192620
+ ...pair,
192621
+ status: normalize(expectedText.text) === normalize(outputText.text) ? "match" : "text-different"
192622
+ });
192623
+ }
192624
+ return diffs;
192625
+ }
192626
+ function runFileUrl(side, name) {
192627
+ const runId = encodeURIComponent(side.runId);
192628
+ const filename = encodeURIComponent(name);
192629
+ return side.kind === "expected" ? `/api/v1/runs/${runId}/expected/${filename}` : `/api/v1/runs/${runId}/artifact/output/${filename}`;
192630
+ }
192631
+ function encodeRunArtifactPath(artifactPath) {
192632
+ return artifactPath.split("/").map(encodeURIComponent).join("/");
192633
+ }
192634
+ async function downloadStreamToFile(client, url2, target) {
192635
+ await fs3.mkdir(path8.dirname(target), { recursive: true });
192636
+ const response = await client.getStream(url2);
192637
+ await fs3.writeFile(target, Buffer.from(await response.arrayBuffer()));
192638
+ }
192639
+ function extractComparableText(file2) {
192640
+ const ext = path8.extname(file2).toLowerCase();
192641
+ if ([".txt", ".md", ".json", ".csv", ".xml"].includes(ext)) {
192642
+ return { text: readFileSync7(file2, "utf8") };
192643
+ }
192644
+ if (ext === ".pdf") {
192645
+ const result = spawnSync4("pdftotext", [file2, "-"], { encoding: "utf8" });
192646
+ return result.status === 0 ? { text: result.stdout } : { text: null, reason: textExtractionFallbackReason("pdftotext", result) };
192647
+ }
192648
+ if (ext === ".docx") {
192649
+ const script = "import re,sys,zipfile; z=zipfile.ZipFile(sys.argv[1]); x=z.read('word/document.xml').decode('utf-8','ignore'); print(re.sub('<[^>]+>',' ',x))";
192650
+ const result = spawnSync4("python3", ["-c", script, file2], { encoding: "utf8" });
192651
+ return result.status === 0 ? { text: result.stdout } : { text: null, reason: textExtractionFallbackReason("python3", result) };
192652
+ }
192653
+ return { text: null, reason: `No text extractor for ${ext || "extensionless"} files` };
192654
+ }
192655
+ function textExtractionFallbackReason(tool, result) {
192656
+ if (result.error)
192657
+ return `${tool} unavailable; compared bytes instead`;
192658
+ return `${tool} could not extract text; compared bytes instead`;
192659
+ }
192660
+ function renderComparisonReport(report) {
192661
+ const header = `Comparison ${report.status}`;
192662
+ if (report.status === "pass") {
192663
+ success2(header);
192664
+ } else {
192665
+ error51(header);
192666
+ }
192667
+ for (const warning of report.warnings ?? []) {
192668
+ dim(`warning: ${warning}`);
192669
+ }
192670
+ console.log(table([
192671
+ { item: "JSON differences", count: report.jsonDifferences.length },
192672
+ { item: "Matched files", count: report.matchedFiles.length },
192673
+ { item: "Missing files", count: report.missingFiles.length },
192674
+ { item: "Extra files", count: report.extraFiles.length },
192675
+ {
192676
+ item: "Text differences",
192677
+ count: report.textDifferences.filter((diff) => diff.status === "text-different").length
192678
+ }
192679
+ ], [
192680
+ { key: "item", header: "ITEM" },
192681
+ { key: "count", header: "COUNT" }
192682
+ ]));
192683
+ for (const diff of report.textDifferences) {
192684
+ const textExtraction = diff.textExtraction;
192685
+ const reasons = [textExtraction?.expected, textExtraction?.output].filter((value) => typeof value === "string" && value.length > 0).filter((value, index, values) => values.indexOf(value) === index);
192686
+ for (const reason of reasons) {
192687
+ dim(`warning: ${String(diff.expected)} vs ${String(diff.output)}: ${reason}`);
192688
+ }
192689
+ }
192690
+ }
192691
+
192692
+ // src/commands/agents/dataset.ts
192693
+ import path10 from "node:path";
192694
+
191473
192695
  // src/commands/agents/validation.ts
191474
192696
  init_src();
191475
192697
  var import_yaml3 = __toESM(require_dist4(), 1);
191476
- import { existsSync as existsSync7, promises as fs2 } from "node:fs";
191477
- import path7 from "node:path";
192698
+ import { existsSync as existsSync7, promises as fs4 } from "node:fs";
192699
+ import path9 from "node:path";
191478
192700
  async function validateAgentProject(root) {
191479
192701
  const errors3 = [];
191480
192702
  const warnings = [];
191481
192703
  for (const [oldName] of LEGACY_LAYOUTS) {
191482
- if (existsSync7(path7.join(root, oldName))) {
192704
+ if (existsSync7(path9.join(root, oldName))) {
191483
192705
  errors3.push(`Legacy layout ${oldName}/ is removed; use Git source under agents/<slug>/`);
191484
192706
  }
191485
192707
  }
191486
- if (existsSync7(path7.join(root, "agent.yaml"))) {
192708
+ if (existsSync7(path9.join(root, "agent.yaml"))) {
191487
192709
  errors3.push("Legacy agent.yaml layout is removed; use eigenpal.yaml in a Git-backed package");
191488
192710
  }
191489
- if (existsSync7(path7.join(root, "agent"))) {
192711
+ if (existsSync7(path9.join(root, "agent"))) {
191490
192712
  errors3.push("Legacy agent/ directory is removed; use Git source (eigenpal agents clone)");
191491
192713
  }
191492
- const manifestPath = path7.join(root, PACKAGE_MANIFEST);
192714
+ const manifestPath = path9.join(root, PACKAGE_MANIFEST);
191493
192715
  if (!existsSync7(manifestPath)) {
191494
192716
  errors3.push(`Missing ${PACKAGE_MANIFEST}`);
191495
192717
  } else {
191496
192718
  try {
191497
- SourcePackageManifestSchema.parse(import_yaml3.default.parse(await fs2.readFile(manifestPath, "utf8")));
192719
+ SourcePackageManifestSchema.parse(import_yaml3.default.parse(await fs4.readFile(manifestPath, "utf8")));
191498
192720
  } catch (err) {
191499
192721
  errors3.push(err instanceof Error ? err.message : String(err));
191500
192722
  }
191501
192723
  }
191502
- if (!existsSync7(path7.join(root, "AGENT.md")))
192724
+ if (!existsSync7(path9.join(root, "AGENT.md")))
191503
192725
  warnings.push("Missing AGENT.md");
191504
- if (!existsSync7(path7.join(root, DATASET_DIR)))
192726
+ if (!existsSync7(path9.join(root, DATASET_DIR)))
191505
192727
  warnings.push(`Missing ${DATASET_DIR}/`);
191506
192728
  for (const filename of SCHEMA_FILENAMES) {
191507
- const schemaPath = path7.join(root, filename);
192729
+ const schemaPath = path9.join(root, filename);
191508
192730
  if (!existsSync7(schemaPath))
191509
192731
  continue;
191510
- const validation2 = validateWorkspaceSchema(await fs2.readFile(schemaPath, "utf8"), filename);
192732
+ const validation2 = validateWorkspaceSchema(await fs4.readFile(schemaPath, "utf8"), filename);
191511
192733
  for (const issue2 of validation2.errors) {
191512
192734
  errors3.push(`${filename}: ${issue2}`);
191513
192735
  }
@@ -191519,7 +192741,7 @@ async function validateDatasetDir(dir, opts = {}) {
191519
192741
  if (!existsSync7(dir))
191520
192742
  errors3.push(`Missing dataset directory: ${dir}`);
191521
192743
  for (const [oldName, newName] of LEGACY_LAYOUTS) {
191522
- if (path7.basename(dir) === oldName) {
192744
+ if (path9.basename(dir) === oldName) {
191523
192745
  errors3.push(`Use ${newName}/ instead of legacy ${oldName}/`);
191524
192746
  }
191525
192747
  }
@@ -191530,28 +192752,28 @@ async function validateDatasetDir(dir, opts = {}) {
191530
192752
  const outputSchema = await loadOptionalWorkspaceSchema(agentDir, "output-schema.json", errors3);
191531
192753
  if (errors3.length > 0)
191532
192754
  return { valid: false, errors: errors3 };
191533
- const entries = await fs2.readdir(dir, { withFileTypes: true });
192755
+ const entries = await fs4.readdir(dir, { withFileTypes: true });
191534
192756
  const examples = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
191535
192757
  for (const example of examples) {
191536
- const exampleRoot = path7.join(dir, example);
192758
+ const exampleRoot = path9.join(dir, example);
191537
192759
  if (inputSchema) {
191538
192760
  errors3.push(...await validateAgentDatasetInputExample(example, exampleRoot, inputSchema));
191539
192761
  } else {
191540
- await validateJsonObjectIfPresent(path7.join(exampleRoot, AGENT_EXAMPLE_INPUT_JSON), `${example}/${AGENT_EXAMPLE_INPUT_JSON}`, errors3);
192762
+ await validateJsonObjectIfPresent(path9.join(exampleRoot, AGENT_EXAMPLE_INPUT_JSON), `${example}/${AGENT_EXAMPLE_INPUT_JSON}`, errors3);
191541
192763
  }
191542
192764
  if (outputSchema) {
191543
192765
  errors3.push(...await validateAgentDatasetExpectedExample(example, exampleRoot, outputSchema));
191544
192766
  } else {
191545
- await validateJsonObjectIfPresent(path7.join(exampleRoot, AGENT_EXAMPLE_EXPECTED_JSON), `${example}/${AGENT_EXAMPLE_EXPECTED_JSON}`, errors3);
192767
+ await validateJsonObjectIfPresent(path9.join(exampleRoot, AGENT_EXAMPLE_EXPECTED_JSON), `${example}/${AGENT_EXAMPLE_EXPECTED_JSON}`, errors3);
191546
192768
  }
191547
192769
  }
191548
192770
  return { valid: errors3.length === 0, errors: errors3 };
191549
192771
  }
191550
192772
  async function loadOptionalWorkspaceSchema(agentDir, filename, errors3) {
191551
- const schemaPath = path7.join(agentDir, filename);
192773
+ const schemaPath = path9.join(agentDir, filename);
191552
192774
  if (!existsSync7(schemaPath))
191553
192775
  return null;
191554
- const raw = await fs2.readFile(schemaPath, "utf8");
192776
+ const raw = await fs4.readFile(schemaPath, "utf8");
191555
192777
  const validation2 = validateWorkspaceSchema(raw, filename);
191556
192778
  for (const issue2 of validation2.errors)
191557
192779
  errors3.push(`${filename}: ${issue2}`);
@@ -191586,8 +192808,8 @@ async function validateAgentDatasetInputExample(example, exampleRoot, inputSchem
191586
192808
  const errors3 = [];
191587
192809
  const props = schemaProperties(inputSchema);
191588
192810
  const required2 = schemaRequired(inputSchema);
191589
- const inputDir = path7.join(exampleRoot, "input");
191590
- const inputFiles = existsSync7(inputDir) ? (await fs2.readdir(inputDir, { withFileTypes: true })).filter((entry) => entry.isFile()).map((entry) => entry.name) : [];
192811
+ const inputDir = path9.join(exampleRoot, "input");
192812
+ const inputFiles = existsSync7(inputDir) ? (await fs4.readdir(inputDir, { withFileTypes: true })).filter((entry) => entry.isFile()).map((entry) => entry.name) : [];
191591
192813
  const matchedFiles = new Set;
191592
192814
  for (const [fieldName, propSchema] of Object.entries(props)) {
191593
192815
  if (!isFileField(propSchema))
@@ -191600,7 +192822,7 @@ async function validateAgentDatasetInputExample(example, exampleRoot, inputSchem
191600
192822
  }
191601
192823
  }
191602
192824
  const dataFields = Object.entries(props).filter(([, propSchema]) => !isFileField(propSchema)).map(([fieldName]) => fieldName);
191603
- const inputJsonPath = path7.join(exampleRoot, AGENT_EXAMPLE_INPUT_JSON);
192825
+ const inputJsonPath = path9.join(exampleRoot, AGENT_EXAMPLE_INPUT_JSON);
191604
192826
  let inputData = null;
191605
192827
  if (existsSync7(inputJsonPath)) {
191606
192828
  inputData = await readJsonObject(inputJsonPath, `${example}/${AGENT_EXAMPLE_INPUT_JSON}`, errors3);
@@ -191640,9 +192862,9 @@ async function validateAgentDatasetInputExample(example, exampleRoot, inputSchem
191640
192862
  async function validateAgentDatasetExpectedExample(example, exampleRoot, outputSchema) {
191641
192863
  const errors3 = [];
191642
192864
  const props = schemaProperties(outputSchema);
191643
- const expectedPath = path7.join(exampleRoot, AGENT_EXAMPLE_EXPECTED_JSON);
191644
- const expectedDir = path7.join(exampleRoot, "expected");
191645
- const goldenFiles = existsSync7(expectedDir) ? (await fs2.readdir(expectedDir, { withFileTypes: true })).filter((entry) => entry.isFile() && !entry.name.startsWith(".")).map((entry) => entry.name) : [];
192865
+ const expectedPath = path9.join(exampleRoot, AGENT_EXAMPLE_EXPECTED_JSON);
192866
+ const expectedDir = path9.join(exampleRoot, "expected");
192867
+ const goldenFiles = existsSync7(expectedDir) ? (await fs4.readdir(expectedDir, { withFileTypes: true })).filter((entry) => entry.isFile() && !entry.name.startsWith(".")).map((entry) => entry.name) : [];
191646
192868
  let expected = null;
191647
192869
  if (existsSync7(expectedPath)) {
191648
192870
  expected = await readJsonObject(expectedPath, `${example}/${AGENT_EXAMPLE_EXPECTED_JSON}`, errors3);
@@ -191690,7 +192912,7 @@ async function validateJsonObjectIfPresent(filePath, label, errors3) {
191690
192912
  }
191691
192913
  async function readJsonObject(filePath, label, errors3) {
191692
192914
  try {
191693
- const parsed = JSON.parse(await fs2.readFile(filePath, "utf8"));
192915
+ const parsed = JSON.parse(await fs4.readFile(filePath, "utf8"));
191694
192916
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
191695
192917
  errors3.push(`${label}: must be a JSON object`);
191696
192918
  return null;
@@ -191754,7 +192976,7 @@ async function pushDataset(agentId, opts) {
191754
192976
  if (opts.mode === "replace" && !(opts.yes || await confirmTyped(agentId, "replace dataset"))) {
191755
192977
  throw new Error("Dataset replace aborted");
191756
192978
  }
191757
- const files = await readFilesUnder(path8.resolve(opts.file));
192979
+ const files = await readFilesUnder(path10.resolve(opts.file));
191758
192980
  const client = buildClient(opts);
191759
192981
  const payload = await client.post(`/api/v1/agents/${encodeURIComponent(agentId)}/dataset?mode=${opts.mode}`, { files });
191760
192982
  if (opts.json)
@@ -191766,15 +192988,15 @@ async function pullDataset(agentId, opts) {
191766
192988
  const payload = await client.get(`/api/v1/agents/${encodeURIComponent(agentId)}/dataset`, {
191767
192989
  include: "files"
191768
192990
  });
191769
- const out = path8.resolve(opts.out ?? DATASET_DIR);
192991
+ const out = path10.resolve(opts.out ?? DATASET_DIR);
191770
192992
  for (const file2 of payload.files) {
191771
- await writeBase64File(path8.join(out, file2.path), file2.contentBase64);
192993
+ await writeBase64File(path10.join(out, file2.path), file2.contentBase64);
191772
192994
  }
191773
192995
  success2(`Pulled dataset to ${ui.bold(out)}`);
191774
192996
  }
191775
192997
  async function validateDatasetCommand(dir = DATASET_DIR, opts) {
191776
- const result = await validateDatasetDir(path8.resolve(dir), {
191777
- agentDir: path8.resolve(opts.agentDir ?? ".")
192998
+ const result = await validateDatasetDir(path10.resolve(dir), {
192999
+ agentDir: path10.resolve(opts.agentDir ?? ".")
191778
193000
  });
191779
193001
  if (opts.json)
191780
193002
  return printJson(result);
@@ -191788,28 +193010,8 @@ async function validateDatasetCommand(dir = DATASET_DIR, opts) {
191788
193010
  // src/commands/agents/env.ts
191789
193011
  init_src();
191790
193012
  var import_yaml4 = __toESM(require_dist4(), 1);
191791
- import { existsSync as existsSync8, promises as fs3, readFileSync as readFileSync7 } from "node:fs";
191792
- import path9 from "node:path";
191793
-
191794
- // src/commands/agents/target.ts
191795
- init_src();
191796
- function parseAgentTarget(target) {
191797
- const [rawTarget, rawRef, extra] = target.split("@");
191798
- if (extra !== undefined)
191799
- throw new Error("Agent target must be <slug>[@ref].");
191800
- const normalizedTarget = rawTarget.includes(".") ? target : `agents.${rawTarget}${rawRef !== undefined ? `@${rawRef}` : ""}`;
191801
- const parsed = parseAutomationTarget(normalizedTarget);
191802
- if (parsed.type !== "agents") {
191803
- throw new Error("Only agent targets are supported in this release. Use agents.<slug>[@ref].");
191804
- }
191805
- return {
191806
- packageName: parsed.packageName,
191807
- slug: parsed.slug,
191808
- sourceRef: rawRef !== undefined ? parsed.ref : undefined
191809
- };
191810
- }
191811
-
191812
- // src/commands/agents/env.ts
193013
+ import { existsSync as existsSync8, promises as fs5, readFileSync as readFileSync8 } from "node:fs";
193014
+ import path11 from "node:path";
191813
193015
  function registerEnvCommands(agent) {
191814
193016
  const env2 = agent.command("env").description("Export encrypted source secrets for a local installed agent package.").action(() => {
191815
193017
  process.stderr.write("`eigenpal agents env` requires a subcommand. Run `eigenpal agents env --help`.\n");
@@ -191830,7 +193032,7 @@ async function pullAgentEnv(target, opts) {
191830
193032
  throw new Error("--format must be shell or dotenv");
191831
193033
  }
191832
193034
  const client = buildClient(opts);
191833
- const requests = target ? await collectEncryptedSecretsForTarget(client, target) : await collectEncryptedSecrets(path9.resolve(opts.dir ?? "."));
193035
+ const requests = target ? await collectEncryptedSecretsForTarget(client, target) : await collectEncryptedSecrets(path11.resolve(opts.dir ?? "."));
191834
193036
  if (requests.length === 0)
191835
193037
  return;
191836
193038
  const payload = await client.post("/api/v1/source/secrets/decrypt", {
@@ -191893,14 +193095,14 @@ async function readOptionalSourceFile(client, query) {
191893
193095
  }
191894
193096
  }
191895
193097
  async function collectEncryptedSecrets(root) {
191896
- const roots = [root, ...await listModulePackageRoots(path9.join(root, "eigenpal_modules"))];
193098
+ const roots = [root, ...await listModulePackageRoots(path11.join(root, "eigenpal_modules"))];
191897
193099
  const seen = new Set;
191898
193100
  const requests = [];
191899
193101
  for (const packageRoot of roots) {
191900
- const secretsPath = path9.join(packageRoot, SOURCE_SECRETS_FILENAME);
193102
+ const secretsPath = path11.join(packageRoot, SOURCE_SECRETS_FILENAME);
191901
193103
  if (!existsSync8(secretsPath))
191902
193104
  continue;
191903
- const parsed = SourceSecretsFileSchema.parse(import_yaml4.default.parse(await fs3.readFile(secretsPath, "utf8")));
193105
+ const parsed = SourceSecretsFileSchema.parse(import_yaml4.default.parse(await fs5.readFile(secretsPath, "utf8")));
191904
193106
  const sourcePath = sourcePathForInstalledPackage(root, packageRoot);
191905
193107
  for (const [secretName, entry] of Object.entries(parsed.secrets)) {
191906
193108
  if (!entry.encrypted)
@@ -191923,14 +193125,14 @@ async function listModulePackageRoots(modulesRoot) {
191923
193125
  if (!existsSync8(modulesRoot))
191924
193126
  return [];
191925
193127
  const result = [];
191926
- for (const type of await fs3.readdir(modulesRoot)) {
191927
- const typeRoot = path9.join(modulesRoot, type);
191928
- const typeStat = await fs3.stat(typeRoot).catch(() => null);
193128
+ for (const type of await fs5.readdir(modulesRoot)) {
193129
+ const typeRoot = path11.join(modulesRoot, type);
193130
+ const typeStat = await fs5.stat(typeRoot).catch(() => null);
191929
193131
  if (!typeStat?.isDirectory())
191930
193132
  continue;
191931
- for (const slug of await fs3.readdir(typeRoot)) {
191932
- const packageRoot = path9.join(typeRoot, slug);
191933
- const packageStat = await fs3.stat(packageRoot).catch(() => null);
193133
+ for (const slug of await fs5.readdir(typeRoot)) {
193134
+ const packageRoot = path11.join(typeRoot, slug);
193135
+ const packageStat = await fs5.stat(packageRoot).catch(() => null);
191934
193136
  if (packageStat?.isDirectory())
191935
193137
  result.push(packageRoot);
191936
193138
  }
@@ -191938,16 +193140,16 @@ async function listModulePackageRoots(modulesRoot) {
191938
193140
  return result;
191939
193141
  }
191940
193142
  function sourcePathForInstalledPackage(root, packageRoot) {
191941
- const sourceRelative = path9.relative(root, packageRoot).replace(/\\/g, "/");
193143
+ const sourceRelative = path11.relative(root, packageRoot).replace(/\\/g, "/");
191942
193144
  if (!sourceRelative.startsWith("..") && SourcePackagePathSchema.safeParse(sourceRelative).success) {
191943
193145
  return sourceRelative;
191944
193146
  }
191945
- const relative = path9.relative(path9.join(root, "eigenpal_modules"), packageRoot).replace(/\\/g, "/");
193147
+ const relative = path11.relative(path11.join(root, "eigenpal_modules"), packageRoot).replace(/\\/g, "/");
191946
193148
  if (!relative.startsWith(".."))
191947
193149
  return relative;
191948
- const lockfilePath = path9.join(root, ".eigenpal", "eigenpal.lock");
193150
+ const lockfilePath = path11.join(root, ".eigenpal", "eigenpal.lock");
191949
193151
  if (packageRoot === root && existsSync8(lockfilePath)) {
191950
- const lockfile = JSON.parse(readFileSync7(lockfilePath, "utf8"));
193152
+ const lockfile = JSON.parse(readFileSync8(lockfilePath, "utf8"));
191951
193153
  if (typeof lockfile.root?.packagePath === "string")
191952
193154
  return lockfile.root.packagePath;
191953
193155
  }
@@ -191961,7 +193163,7 @@ function dotenvQuote(value) {
191961
193163
  }
191962
193164
 
191963
193165
  // src/commands/agents/experiments.ts
191964
- import { promises as fs4 } from "node:fs";
193166
+ import { promises as fs6 } from "node:fs";
191965
193167
  function registerExperimentCommands(agent) {
191966
193168
  const experiment = agent.command("experiment").description("Run and inspect batches of agent executions.").action(() => {
191967
193169
  process.stderr.write("`eigenpal agents experiment` requires a subcommand. Run `eigenpal agents experiment --help`.\n");
@@ -192000,7 +193202,7 @@ async function experimentResults(agentId, batchId, opts) {
192000
193202
  const payload = await client.get(`/api/v1/agents/${encodeURIComponent(agentId)}/experiments/${encodeURIComponent(selected)}`);
192001
193203
  const content = opts.format === "json" ? JSON.stringify(payload, null, 2) : toCsv(payload.runs, ["id", "status", "exampleId"]);
192002
193204
  if (opts.out) {
192003
- await fs4.writeFile(opts.out, content);
193205
+ await fs6.writeFile(opts.out, content);
192004
193206
  success2(`Wrote ${opts.out}`);
192005
193207
  } else {
192006
193208
  process.stdout.write(`${content}
@@ -192037,9 +193239,9 @@ async function cancelExperiment(agentId, batchId, opts) {
192037
193239
  }
192038
193240
 
192039
193241
  // src/commands/agents/files.ts
192040
- import { createHash as createHash2 } from "node:crypto";
192041
- import { promises as fs5 } from "node:fs";
192042
- import path10 from "node:path";
193242
+ import { createHash as createHash3 } from "node:crypto";
193243
+ import { promises as fs7 } from "node:fs";
193244
+ import path12 from "node:path";
192043
193245
  function registerAgentFileCommands(agent) {
192044
193246
  const file2 = agent.command("file").description("List, download, and compare individual live agent files.").action(() => {
192045
193247
  process.stderr.write("`eigenpal agents file` requires a subcommand. Run `eigenpal agents file --help`.\n");
@@ -192065,19 +193267,19 @@ async function getAgentFile(agentId, remotePath, opts) {
192065
193267
  });
192066
193268
  if (opts.json)
192067
193269
  return printJson(payload);
192068
- const out = path10.resolve(opts.out ?? payload.path);
193270
+ const out = path12.resolve(opts.out ?? payload.path);
192069
193271
  await writeBase64File(out, payload.contentBase64);
192070
193272
  success2(`Downloaded ${payload.path} to ${ui.bold(out)}`);
192071
193273
  }
192072
193274
  async function diffAgentFile(agentId, remotePath, localPath, opts) {
192073
- const report = await buildAgentFileDiff(agentId, remotePath, path10.resolve(localPath), opts);
193275
+ const report = await buildAgentFileDiff(agentId, remotePath, path12.resolve(localPath), opts);
192074
193276
  if (opts.json)
192075
193277
  return printJson(report);
192076
193278
  renderAgentFileDiff(report);
192077
193279
  }
192078
193280
  async function buildAgentFileDiff(agentId, remotePath, localPath, opts) {
192079
193281
  const client = buildClient(opts);
192080
- const local = await fs5.readFile(localPath);
193282
+ const local = await fs7.readFile(localPath);
192081
193283
  let remote = null;
192082
193284
  let serverPath = remotePath;
192083
193285
  try {
@@ -192090,8 +193292,8 @@ async function buildAgentFileDiff(agentId, remotePath, localPath, opts) {
192090
193292
  if (!(err instanceof ApiError) || err.status !== 404)
192091
193293
  throw err;
192092
193294
  }
192093
- const localHash = createHash2("sha256").update(local).digest("hex");
192094
- const remoteHash = remote ? createHash2("sha256").update(remote).digest("hex") : null;
193295
+ const localHash = createHash3("sha256").update(local).digest("hex");
193296
+ const remoteHash = remote ? createHash3("sha256").update(remote).digest("hex") : null;
192095
193297
  const status = !remote ? "remote-missing" : localHash === remoteHash ? "match" : "different";
192096
193298
  return {
192097
193299
  agentId,
@@ -192145,713 +193347,6 @@ function buildTextDiffPreview(remoteText, localText) {
192145
193347
  return preview;
192146
193348
  }
192147
193349
 
192148
- // src/commands/agents/runs.ts
192149
- import { spawnSync as spawnSync4 } from "node:child_process";
192150
- import { createHash as createHash3 } from "node:crypto";
192151
- import { promises as fs7, readFileSync as readFileSync8 } from "node:fs";
192152
- import path12 from "node:path";
192153
-
192154
- // src/commands/agents/run-form-data.ts
192155
- import { promises as fs6 } from "node:fs";
192156
- import path11 from "node:path";
192157
- async function buildAgentExecutionRunFormData(inputFile, inputJson) {
192158
- const form = new FormData;
192159
- for (const spec of parseAgentInputFileSpecs(inputFile)) {
192160
- const data = await fs6.readFile(spec.filePath);
192161
- form.append(spec.fieldName, new Blob([data]), path11.basename(spec.filePath));
192162
- }
192163
- if (inputJson)
192164
- form.append("_json", inputJson);
192165
- return form;
192166
- }
192167
- function parseAgentInputFileSpecs(inputFile) {
192168
- const values = Array.isArray(inputFile) ? inputFile : [inputFile];
192169
- return values.map((value) => {
192170
- const separatorIndex = value.indexOf("=");
192171
- if (separatorIndex === -1) {
192172
- return { fieldName: "file", filePath: value };
192173
- }
192174
- const fieldName = value.slice(0, separatorIndex).trim();
192175
- const filePath = value.slice(separatorIndex + 1).trim();
192176
- if (!fieldName || !filePath) {
192177
- throw new Error("--input-file must be <field=path> or a bare path");
192178
- }
192179
- if (fieldName === "_json" || fieldName === "_metadata" || fieldName === "input") {
192180
- throw new Error(`--input-file field "${fieldName}" is reserved`);
192181
- }
192182
- return { fieldName, filePath };
192183
- });
192184
- }
192185
-
192186
- // src/commands/agents/runs.ts
192187
- function registerRunCommands(agent) {
192188
- const runs = agent.command("runs").description("Inspect, watch, and manage agent runs.").action(() => {
192189
- process.stderr.write("`eigenpal agents runs` requires a subcommand. Run `eigenpal agents runs --help`.\n");
192190
- process.exit(2);
192191
- });
192192
- const listRunsCmd = addJsonFlag(withPagination(withBaseUrl(runs.command("list <target>")), 50)).description("List runs for an agent target; unqualified targets include all source refs.").option("--status <status>", "Filter by run status").option("--include <items>", "Comma-separated include list").option("--compact", "Render compact run rows").option("--sort <field>", "Sort field").option("--order <asc|desc>", "Sort order");
192193
- listRunsCmd.action(action(listRunsTarget));
192194
- addJsonFlag(withBaseUrl(runs.command("get <run-id>"))).description("Get one agent run.").option("--include <parts>", "Comma-separated extra parts: feedback,expected,files,trace,issues", "feedback").action(action(getRun));
192195
- addJsonFlag(withBaseUrl(runs.command("compare <reference-run-id> <run-id>"))).description("Compare one run against another run. PDF/DOCX text comparison uses pdftotext/python3 and reports byte fallbacks.").option("--baseline", "Compare actual outputs from both runs instead of expected artifacts").option("--out <dir>", "Write comparison artifacts to this directory").option("--normalize-dates", "Normalize YYYYMMDD and YYYY-MM-DD tokens in filenames/text").option("--fail-on-diff", "Exit 1 when comparison status is fail").action(action(compareRun));
192196
- const artifacts = runs.command("artifacts").description("Inspect run artifact inventory.").action(() => {
192197
- process.stderr.write("`eigenpal agents runs artifacts` requires a subcommand. Run `eigenpal agents runs artifacts --help`.\n");
192198
- process.exit(2);
192199
- });
192200
- addJsonFlag(withBaseUrl(artifacts.command("list <run-id>"))).description("List available run artifacts without downloading them.").action(action(listRunArtifacts));
192201
- withBaseUrl(artifacts.command("fetch <run-id>")).description("Download run artifacts by canonical artifact path.").option("--out <dir>", "Output directory").option("--include <parts>", "Comma-separated parts: output,input,metadata,issues,trace,lockfile,expected,all", "all").option("--path <path>", "Fetch one exact artifact path from `artifacts list`; repeatable", collectRepeated, []).option("--json", "Output a JSON summary of written artifacts").action(action(fetchRunArtifacts));
192202
- withBaseUrl(runs.command("trace <run-id>")).description("Print raw trace.jsonl for a run, or write it with --out.").option("--out <file>", "Output file path").action(action(traceRun));
192203
- registerRunFeedbackCommands(runs);
192204
- registerRunExpectedCommands(runs);
192205
- addJsonFlag(withBaseUrl(runs.command("watch <run-id>"))).description("Watch a run until it reaches a terminal status.").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).option("--max-wait <seconds>", "Maximum wait before exiting 2", intArg, 1800).action(action(watchRunCommand));
192206
- addJsonFlag(withBaseUrl(runs.command("cancel <run-id>"))).description("Cancel an agent run.").option("--yes", "Required in non-interactive environments").action(action(cancelRun));
192207
- }
192208
- function registerRunFeedbackCommands(runs) {
192209
- const feedback = runs.command("feedback").description("Update or clear feedback attached to an agent run.").action(() => {
192210
- process.stderr.write("`eigenpal agents runs feedback` requires a subcommand. Run `eigenpal agents runs feedback --help`.\n");
192211
- process.exit(2);
192212
- });
192213
- addJsonFlag(withBaseUrl(feedback.command("update <run-id>"))).description("Edit feedback state, rating, message, or expected JSON for a run.").option("--status <open|resolved|ignored>", "Set feedback status").option("--rating <pass|fail|partial|none>", "Set feedback rating").option("--message <text>", "Set feedback message body").option("--message-file <path>", "Read feedback message body from a file").option("--expected-json <json>", "Set structured expected JSON").option("--expected-json-file <path>", "Read structured expected JSON from a file").option("--clear-message", "Clear the feedback message body").option("--clear-rating", "Clear feedback rating").option("--clear-expected-json", "Delete structured expected JSON").action(action(updateRunFeedback));
192214
- addJsonFlag(withBaseUrl(feedback.command("resolve <run-id>"))).description("Mark run feedback as resolved.").option("--message <text>", "Set feedback message body").option("--message-file <path>", "Read feedback message body from a file").action(action((runId, opts) => updateRunFeedback(runId, { ...opts, status: "resolved" })));
192215
- addJsonFlag(withBaseUrl(feedback.command("clear <run-id>"))).description("Delete feedback, expected.json, and expected files for a run.").option("--yes", "Required in non-interactive environments").action(action(clearRunFeedback));
192216
- }
192217
- function registerRunExpectedCommands(runs) {
192218
- const expected = runs.command("expected").description("Manage expected artifacts attached to an agent run.").action(() => {
192219
- process.stderr.write("`eigenpal agents runs expected` requires a subcommand. Run `eigenpal agents runs expected --help`.\n");
192220
- process.exit(2);
192221
- });
192222
- addJsonFlag(withBaseUrl(expected.command("list <run-id>"))).description("List expected JSON and files attached to a run.").action(action(listRunExpected));
192223
- withBaseUrl(expected.command("pull <run-id>")).description("Download expected JSON and files attached to a run.").option("--out <dir>", "Output directory").action(action(pullRunExpected));
192224
- addJsonFlag(withBaseUrl(expected.command("upload <run-id> <file>"))).description("Upload a local file as an expected artifact.").option("--name <name>", "Expected artifact name").action(action(uploadRunExpected));
192225
- addJsonFlag(withBaseUrl(expected.command("copy-output <run-id> <output-file>"))).description("Copy a generated output file into expected artifacts.").option("--name <name>", "Expected artifact name").action(action(copyOutputToExpected));
192226
- addJsonFlag(withBaseUrl(expected.command("rename <run-id> <old-name> <new-name>"))).description("Rename an expected artifact.").action(action(renameRunExpected));
192227
- addJsonFlag(withBaseUrl(expected.command("delete <run-id> <name>"))).description("Delete an expected artifact.").option("--yes", "Required in non-interactive environments").action(action(deleteRunExpected));
192228
- }
192229
- async function listRunsTarget(target, opts) {
192230
- const parsed = parseAgentTarget(target);
192231
- return listRuns(parsed.slug, { ...opts, sourceRef: parsed.sourceRef });
192232
- }
192233
- async function runExecution(agentId, opts) {
192234
- const client = buildClient(opts);
192235
- const runPath = `/api/v1/agents/${encodeURIComponent(agentId)}/run${opts.sourceRef ? `?sourceRef=${encodeURIComponent(opts.sourceRef)}` : ""}`;
192236
- let payload;
192237
- if (opts.inputFile && opts.inputFile.length > 0) {
192238
- const form = await buildAgentExecutionRunFormData(opts.inputFile, opts.inputJson);
192239
- payload = await client.postFormData(runPath, form);
192240
- } else {
192241
- payload = await client.post(runPath, {
192242
- input: opts.inputJson ? JSON.parse(opts.inputJson) : {},
192243
- ...opts.sourceRef ? { sourceRef: opts.sourceRef } : {}
192244
- });
192245
- }
192246
- const runId = String(payload.runId ?? "");
192247
- let waitedForTerminalRun = false;
192248
- if (opts.wait && runId) {
192249
- payload = await pollRun(client, runId, opts.interval ?? 2, opts.maxWait ?? 1800);
192250
- waitedForTerminalRun = true;
192251
- }
192252
- renderRunPayload(payload, opts);
192253
- if (waitedForTerminalRun)
192254
- setExitCodeForFailedTerminalRun(payload);
192255
- }
192256
- async function runExample(agentId, opts) {
192257
- const client = buildClient(opts);
192258
- const started = await client.post(`/api/v1/agents/${encodeURIComponent(agentId)}/experiments`, {
192259
- exampleId: opts.example,
192260
- ...opts.sourceRef ? { sourceRef: opts.sourceRef } : {}
192261
- });
192262
- const runId = started.runs?.[0]?.runId;
192263
- if (opts.wait && runId) {
192264
- const payload2 = await pollRun(client, runId, opts.interval, opts.maxWait);
192265
- renderRunPayload(payload2, opts);
192266
- setExitCodeForFailedTerminalRun(payload2);
192267
- return;
192268
- }
192269
- const payload = {
192270
- runId,
192271
- exampleId: opts.example,
192272
- batchId: started.batchId,
192273
- status: "pending"
192274
- };
192275
- if (opts.json)
192276
- return printJson(payload);
192277
- success2(`Run ${runId ?? ""} queued for example ${opts.example}`);
192278
- }
192279
- async function getRun(executionId, opts) {
192280
- const client = buildClient(opts);
192281
- const payload = await client.get(`/api/v1/agents/runs/${encodeURIComponent(executionId)}`, {
192282
- ...opts.include ? { include: opts.include } : {}
192283
- });
192284
- renderRunPayload(payload, opts);
192285
- }
192286
- async function rerunRun(executionId, opts) {
192287
- const client = buildClient(opts);
192288
- const sourceRef = await resolveRerunSourceRef(client, executionId, opts.sourceRef);
192289
- let payload = await client.post(`/api/v1/agents/runs/${encodeURIComponent(executionId)}/rerun`, sourceRef ? { sourceRef } : {});
192290
- const rerunId = String(payload.runId ?? "");
192291
- if (opts.wait && rerunId) {
192292
- payload = await pollRun(client, rerunId, opts.interval, opts.maxWait);
192293
- renderRunPayload(payload, opts);
192294
- setExitCodeForFailedTerminalRun(payload);
192295
- return;
192296
- }
192297
- if (opts.json)
192298
- return printJson(payload);
192299
- success2(`Started rerun ${ui.bold(rerunId)} from ${executionId}`);
192300
- }
192301
- async function resolveRerunSourceRef(client, executionId, requested) {
192302
- if (!requested)
192303
- return;
192304
- if (requested !== "original")
192305
- return requested;
192306
- const payload = await client.get(`/api/v1/agents/runs/${encodeURIComponent(executionId)}`);
192307
- const run = payload.run;
192308
- if (!run)
192309
- throw new Error(`Run ${executionId} not found`);
192310
- const resolved = stringOrNull(run.resolvedGitRef);
192311
- const requestedSource = stringOrNull(run.requestedSourceRef);
192312
- const original = resolved ?? requestedSource;
192313
- if (!original) {
192314
- throw new Error(`Run ${executionId} does not have an original source ref to reuse`);
192315
- }
192316
- return original;
192317
- }
192318
- async function fetchRunArtifacts(executionId, opts) {
192319
- const client = buildClient(opts);
192320
- const payload = await client.get(`/api/v1/agents/runs/${encodeURIComponent(executionId)}`, {
192321
- include: "expected,files,input,output,issues,trace,lockfile,metadata"
192322
- });
192323
- const run = payload.run;
192324
- if (!run)
192325
- throw new Error(`Run ${executionId} not found`);
192326
- const out = path12.resolve(opts.out ?? path12.join(".eigenpal", "artifacts", "runs", executionId));
192327
- await fs7.mkdir(out, { recursive: true });
192328
- const written = [];
192329
- const skipped = [];
192330
- const selectedPaths = opts.path && opts.path.length > 0 ? opts.path : runArtifactInventory(run).filter((artifact) => artifactIncluded(artifact, opts.include)).map((artifact) => artifact.path);
192331
- for (const artifactPath of selectedPaths) {
192332
- const normalized = normalizeArtifactPath(artifactPath);
192333
- if (!normalized) {
192334
- skipped.push(artifactPath);
192335
- continue;
192336
- }
192337
- const result = await writeRunArtifact(client, executionId, out, normalized, run);
192338
- if (result)
192339
- written.push(result);
192340
- else
192341
- skipped.push(normalized);
192342
- }
192343
- const summary = {
192344
- runId: executionId,
192345
- out,
192346
- written,
192347
- skipped,
192348
- counts: {
192349
- written: written.length,
192350
- skipped: skipped.length
192351
- }
192352
- };
192353
- if (opts.json)
192354
- return printJson(summary);
192355
- success2(`Fetched run artifacts for ${executionId} to ${out}`);
192356
- dim(`Wrote ${written.length} artifact${written.length === 1 ? "" : "s"}${skipped.length ? `; skipped missing ${skipped.join(", ")}` : ""}`);
192357
- }
192358
- async function listRunArtifacts(executionId, opts) {
192359
- const client = buildClient(opts);
192360
- const payload = await client.get(`/api/v1/agents/runs/${encodeURIComponent(executionId)}`, {
192361
- include: "expected,files,input,output,issues,trace,lockfile,metadata"
192362
- });
192363
- const run = payload.run;
192364
- if (!run)
192365
- throw new Error(`Run ${executionId} not found`);
192366
- const artifacts = runArtifactInventory(run);
192367
- if (opts.json)
192368
- return printJson({ runId: executionId, artifacts });
192369
- console.log(table(artifacts, [
192370
- { key: "kind", header: "KIND" },
192371
- { key: "name", header: "NAME" },
192372
- { key: "path", header: "PATH" },
192373
- { key: "present", header: "PRESENT" }
192374
- ]));
192375
- }
192376
- function artifactIncluded(artifact, include) {
192377
- const parts = new Set(include.split(",").map((part) => part.trim()).filter(Boolean));
192378
- if (parts.has("all"))
192379
- return true;
192380
- if (parts.has(artifact.kind))
192381
- return true;
192382
- if (parts.has("files") && (artifact.kind === "input" || artifact.kind === "output"))
192383
- return true;
192384
- if (parts.has("metadata") && artifact.path === "run.json")
192385
- return true;
192386
- return false;
192387
- }
192388
- function normalizeArtifactPath(artifactPath) {
192389
- const parts = artifactPath.split("/");
192390
- if (artifactPath.startsWith("/") || parts.some((part) => part === "" || part === "." || part === "..")) {
192391
- return null;
192392
- }
192393
- return artifactPath;
192394
- }
192395
- async function writeJsonRunArtifact(out, artifactPath, value) {
192396
- await fs7.mkdir(path12.dirname(path12.join(out, artifactPath)), { recursive: true });
192397
- await fs7.writeFile(path12.join(out, artifactPath), JSON.stringify(value, null, 2));
192398
- }
192399
- async function writeDownloadedRunArtifact(client, executionId, out, artifactPath, routePrefix) {
192400
- const routePath = routePrefix === "files" ? artifactPath : artifactPath.slice("expected/".length);
192401
- const response = await client.getStream(`/api/v1/agents/runs/${encodeURIComponent(executionId)}/${routePrefix}/${encodeRunArtifactPath(routePath)}`);
192402
- const outPath = path12.join(out, artifactPath);
192403
- await fs7.mkdir(path12.dirname(outPath), { recursive: true });
192404
- await fs7.writeFile(outPath, Buffer.from(await response.arrayBuffer()));
192405
- }
192406
- async function writeRunArtifact(client, executionId, out, artifactPath, run) {
192407
- if (artifactPath === "run.json") {
192408
- await writeJsonRunArtifact(out, artifactPath, run);
192409
- return artifactPath;
192410
- }
192411
- if (artifactPath === "input.json") {
192412
- if (run.inputJson == null)
192413
- return null;
192414
- await writeJsonRunArtifact(out, artifactPath, run.inputJson);
192415
- return artifactPath;
192416
- }
192417
- if (artifactPath === "metadata.json") {
192418
- if (run.metadata == null)
192419
- return null;
192420
- await writeJsonRunArtifact(out, artifactPath, run.metadata);
192421
- return artifactPath;
192422
- }
192423
- if (artifactPath === "expected.json") {
192424
- if (run.expected == null)
192425
- return null;
192426
- await writeJsonRunArtifact(out, artifactPath, run.expected);
192427
- return artifactPath;
192428
- }
192429
- if (artifactPath.startsWith("expected/")) {
192430
- await writeDownloadedRunArtifact(client, executionId, out, artifactPath, "expected");
192431
- return artifactPath;
192432
- }
192433
- await writeDownloadedRunArtifact(client, executionId, out, artifactPath, "files");
192434
- return artifactPath;
192435
- }
192436
- async function traceRun(executionId, opts) {
192437
- const client = buildClient(opts);
192438
- const text = await downloadTraceText(client, executionId);
192439
- if (!opts.out) {
192440
- process.stdout.write(text);
192441
- return;
192442
- }
192443
- const out = path12.resolve(opts.out);
192444
- await fs7.mkdir(path12.dirname(out), { recursive: true });
192445
- await fs7.writeFile(out, text);
192446
- success2(`Downloaded trace for ${executionId} to ${ui.bold(out)}`);
192447
- }
192448
- async function compareRun(referenceId, executionId, opts) {
192449
- const client = buildClient(opts);
192450
- const targetPayload = await client.get(`/api/v1/agents/runs/${encodeURIComponent(executionId)}`, { include: "files,output" });
192451
- const target = targetPayload.run;
192452
- if (!target)
192453
- throw new Error(`Run ${executionId} not found`);
192454
- const mode = opts.baseline ? "baseline" : "expected";
192455
- const reference = (await client.get(`/api/v1/agents/runs/${encodeURIComponent(referenceId)}`, {
192456
- include: mode === "baseline" ? "files,output" : "expected"
192457
- })).run;
192458
- if (!reference)
192459
- throw new Error(`Reference run ${referenceId} not found`);
192460
- const out = path12.resolve(opts.out ?? path12.join(".eigenpal", "artifacts", "comparisons", `${referenceId}..${executionId}`));
192461
- await fs7.mkdir(out, { recursive: true });
192462
- const warnings = [];
192463
- if (mode === "expected" && reference.expected == null && fileNames(reference.expectedFiles).length === 0) {
192464
- warnings.push(`Reference run ${referenceId} has no expected JSON or expected files; comparison has no baseline artifacts. Use --baseline to compare actual outputs.`);
192465
- }
192466
- const jsonDiffs = diffJson(mode === "baseline" ? reference.output : reference.expected, target.output);
192467
- const expectedFiles = mode === "baseline" ? fileNames(reference.resultFiles).filter((name) => name !== "issues.md" && name !== "trace.jsonl") : fileNames(reference.expectedFiles);
192468
- const outputFiles = fileNames(target.resultFiles).filter((name) => name !== "issues.md" && name !== "trace.jsonl");
192469
- const inventory = compareFileInventory(expectedFiles, outputFiles, Boolean(opts.normalizeDates));
192470
- const textDifferences = await compareMatchedFileText(client, { runId: referenceId, kind: mode === "baseline" ? "output" : "expected" }, { runId: executionId, kind: "output" }, out, inventory.matched, Boolean(opts.normalizeDates));
192471
- const report = {
192472
- status: jsonDiffs.length === 0 && inventory.missing.length === 0 && inventory.extra.length === 0 && textDifferences.every((diff) => diff.status === "match" || diff.status === "binary-match") ? "pass" : "fail",
192473
- runId: executionId,
192474
- comparedWithRunId: referenceId,
192475
- mode,
192476
- warnings,
192477
- jsonDifferences: jsonDiffs,
192478
- matchedFiles: inventory.matched,
192479
- missingFiles: inventory.missing,
192480
- extraFiles: inventory.extra,
192481
- textDifferences
192482
- };
192483
- await fs7.writeFile(path12.join(out, "comparison.json"), JSON.stringify(report, null, 2));
192484
- if (opts.json) {
192485
- printJson(report);
192486
- } else {
192487
- renderComparisonReport(report);
192488
- }
192489
- dim(`Comparison artifacts written to ${ui.bold(out)}`);
192490
- if (opts.failOnDiff && report.status === "fail")
192491
- process.exit(1);
192492
- }
192493
- async function listRuns(agentId, opts) {
192494
- const client = buildClient(opts);
192495
- const params = buildRunListParams(opts);
192496
- const payload = await client.get(`/api/v1/agents/${encodeURIComponent(agentId)}/runs`, params);
192497
- const rows = opts.compact ? payload.runs.map(compactRunRow) : payload.runs;
192498
- if (opts.json)
192499
- return printJson({ ...payload, runs: rows });
192500
- if (payload.scanLimited) {
192501
- warn("Feedback/expected filters scanned only the first matching window. Increase --scan-limit or narrow DB filters for a fuller result.");
192502
- }
192503
- if (payload.noResolvedAnchor) {
192504
- warn("No resolved feedback anchor was found in the scan window; --since-last-resolved returned no rows.");
192505
- }
192506
- console.log(table(rows, [
192507
- { key: "id", header: "ID" },
192508
- { key: "status", header: "STATUS" },
192509
- { key: "exampleId", header: "EXAMPLE" },
192510
- {
192511
- key: "feedback",
192512
- header: "FEEDBACK",
192513
- format: (value) => value && typeof value === "object" ? `${String(value.rating ?? "")}/${String(value.status ?? "")}` : ""
192514
- },
192515
- { key: "createdAt", header: "CREATED", format: formatTimestamp }
192516
- ]));
192517
- }
192518
- function buildRunListParams(opts) {
192519
- return compactParams({
192520
- ...opts,
192521
- feedback: undefined,
192522
- noFeedback: opts.noFeedback ?? (opts.feedback === false ? true : undefined)
192523
- });
192524
- }
192525
- async function updateRunFeedback(executionId, opts) {
192526
- const client = buildClient(opts);
192527
- const body = {};
192528
- if (opts.status)
192529
- body.status = opts.status;
192530
- if (opts.rating)
192531
- body.rating = opts.rating === "none" ? null : opts.rating;
192532
- if (opts.clearRating)
192533
- body.rating = null;
192534
- if (opts.messageFile)
192535
- body.body = await fs7.readFile(opts.messageFile, "utf-8");
192536
- if (opts.message !== undefined)
192537
- body.body = opts.message;
192538
- if (opts.clearMessage)
192539
- body.body = "";
192540
- if (opts.expectedJsonFile)
192541
- body.expected = JSON.parse(await fs7.readFile(opts.expectedJsonFile, "utf-8"));
192542
- if (opts.expectedJson !== undefined)
192543
- body.expected = JSON.parse(opts.expectedJson);
192544
- if (opts.clearExpectedJson)
192545
- body.expected = null;
192546
- const payload = await client.patch(`/api/v1/agents/runs/${encodeURIComponent(executionId)}/feedback`, body);
192547
- renderGeneric(payload, opts, `Updated feedback for ${executionId}`);
192548
- }
192549
- function compactRunRow(run) {
192550
- const feedback = run.feedback && typeof run.feedback === "object" ? run.feedback : null;
192551
- return {
192552
- id: run.id,
192553
- status: run.status,
192554
- exampleId: run.exampleId,
192555
- feedback: feedback ? {
192556
- rating: feedback.rating ?? null,
192557
- status: feedback.status ?? null,
192558
- updatedAt: feedback.updatedAt ?? null
192559
- } : null,
192560
- hasExpectedJson: run.expected != null,
192561
- expectedFileCount: Array.isArray(run.expectedFiles) ? run.expectedFiles.length : undefined,
192562
- createdAt: run.createdAt,
192563
- completedAt: run.completedAt
192564
- };
192565
- }
192566
- async function clearRunFeedback(executionId, opts) {
192567
- if (!(opts.yes || await confirmTyped(executionId, "clear feedback artifacts"))) {
192568
- throw new Error("Clear cancelled");
192569
- }
192570
- const client = buildClient(opts);
192571
- const payload = await client.delete(`/api/v1/agents/runs/${encodeURIComponent(executionId)}/feedback`);
192572
- renderGeneric(payload, opts, `Cleared feedback for ${executionId}`);
192573
- }
192574
- async function listRunExpected(executionId, opts) {
192575
- const client = buildClient(opts);
192576
- const payload = await client.get(`/api/v1/agents/runs/${encodeURIComponent(executionId)}/expected`);
192577
- if (opts.json)
192578
- return printJson(payload);
192579
- console.log(table(payload.files ?? [], [{ key: "name", header: "NAME" }]));
192580
- if (payload.expected != null)
192581
- dim("expected.json present");
192582
- }
192583
- async function pullRunExpected(executionId, opts) {
192584
- const client = buildClient(opts);
192585
- const payload = await client.get(`/api/v1/agents/runs/${encodeURIComponent(executionId)}/expected`);
192586
- const out = path12.resolve(opts.out ?? path12.join(executionId, "expected"));
192587
- await fs7.mkdir(out, { recursive: true });
192588
- if (payload.expected != null) {
192589
- await fs7.writeFile(path12.join(out, "expected.json"), JSON.stringify(payload.expected, null, 2));
192590
- }
192591
- await downloadExpectedFiles(client, executionId, out, payload.files);
192592
- success2(`Pulled expected artifacts for ${executionId} to ${out}`);
192593
- }
192594
- async function downloadExpectedFiles(client, executionId, out, files) {
192595
- const rows = Array.isArray(files) ? files : [];
192596
- const written = await Promise.all(rows.map(async (file2) => {
192597
- const name = String(file2.name ?? "");
192598
- if (!name || name.includes("/") || name.includes(".."))
192599
- return null;
192600
- const response = await client.getStream(`/api/v1/agents/runs/${encodeURIComponent(executionId)}/expected/${encodeURIComponent(name)}`);
192601
- await fs7.mkdir(out, { recursive: true });
192602
- await fs7.writeFile(path12.join(out, name), Buffer.from(await response.arrayBuffer()));
192603
- return name;
192604
- }));
192605
- return written.filter((name) => Boolean(name));
192606
- }
192607
- async function uploadRunExpected(executionId, file2, opts) {
192608
- const client = buildClient(opts);
192609
- const form = new FormData;
192610
- const data = await fs7.readFile(file2);
192611
- form.append("file", new Blob([data]), path12.basename(file2));
192612
- if (opts.name)
192613
- form.append("name", opts.name);
192614
- const payload = await client.postFormData(`/api/v1/agents/runs/${encodeURIComponent(executionId)}/expected`, form);
192615
- renderGeneric(payload, opts, `Uploaded expected file for ${executionId}`);
192616
- }
192617
- async function copyOutputToExpected(executionId, outputFile, opts) {
192618
- const client = buildClient(opts);
192619
- const payload = await client.post(`/api/v1/agents/runs/${encodeURIComponent(executionId)}/expected`, { outputFileName: outputFile, ...opts.name ? { expectedName: opts.name } : {} });
192620
- renderGeneric(payload, opts, `Copied output file to expected for ${executionId}`);
192621
- }
192622
- async function renameRunExpected(executionId, oldName, newName, opts) {
192623
- const client = buildClient(opts);
192624
- const payload = await client.patch(`/api/v1/agents/runs/${encodeURIComponent(executionId)}/expected/${encodeURIComponent(oldName)}`, { name: newName });
192625
- renderGeneric(payload, opts, `Renamed expected file for ${executionId}`);
192626
- }
192627
- async function deleteRunExpected(executionId, name, opts) {
192628
- if (!(opts.yes || await confirmTyped(name, "delete expected file"))) {
192629
- throw new Error("Delete cancelled");
192630
- }
192631
- const client = buildClient(opts);
192632
- await client.delete(`/api/v1/agents/runs/${encodeURIComponent(executionId)}/expected/${encodeURIComponent(name)}`);
192633
- renderGeneric({ ok: true }, opts, `Deleted expected file ${name}`);
192634
- }
192635
- async function watchRunCommand(executionId, opts) {
192636
- const client = buildClient(opts);
192637
- const payload = await pollRun(client, executionId, opts.interval, opts.maxWait);
192638
- renderRunPayload(payload, opts);
192639
- setExitCodeForFailedTerminalRun(payload);
192640
- }
192641
- async function cancelRun(executionId, opts) {
192642
- if (!(opts.yes || process.stdin.isTTY))
192643
- throw new Error("Pass --yes to cancel in non-interactive mode");
192644
- const client = buildClient(opts);
192645
- const payload = await client.post(`/api/v1/agents/runs/${encodeURIComponent(executionId)}/cancel`, {});
192646
- renderRunPayload(payload, opts);
192647
- }
192648
- async function downloadTraceText(client, executionId) {
192649
- const response = await client.getStream(`/api/v1/agents/runs/${encodeURIComponent(executionId)}/files/trace.jsonl`);
192650
- return Buffer.from(await response.arrayBuffer()).toString("utf-8");
192651
- }
192652
- function diffJson(expected, actual, basePath = "$") {
192653
- if (expected === undefined || expected === null)
192654
- return [];
192655
- if (Object.is(expected, actual))
192656
- return [];
192657
- if (expected && actual && typeof expected === "object" && typeof actual === "object" && !Array.isArray(expected) && !Array.isArray(actual)) {
192658
- const keys = new Set([...Object.keys(expected), ...Object.keys(actual)]);
192659
- return [...keys].flatMap((key) => {
192660
- const next = `${basePath}.${key}`;
192661
- if (!(key in actual))
192662
- return [{ path: next, type: "missing" }];
192663
- if (!(key in expected))
192664
- return [{ path: next, type: "extra" }];
192665
- return diffJson(expected[key], actual[key], next);
192666
- });
192667
- }
192668
- if (Array.isArray(expected) && Array.isArray(actual)) {
192669
- const diffs = [];
192670
- const max = Math.max(expected.length, actual.length);
192671
- for (let index = 0;index < max; index += 1) {
192672
- const next = `${basePath}[${index}]`;
192673
- if (index >= actual.length)
192674
- diffs.push({ path: next, type: "missing" });
192675
- else if (index >= expected.length)
192676
- diffs.push({ path: next, type: "extra" });
192677
- else
192678
- diffs.push(...diffJson(expected[index], actual[index], next));
192679
- }
192680
- return diffs;
192681
- }
192682
- return [
192683
- {
192684
- path: basePath,
192685
- type: "changed",
192686
- expected: JSON.stringify(expected),
192687
- actual: JSON.stringify(actual)
192688
- }
192689
- ];
192690
- }
192691
- function fileNames(files) {
192692
- return (Array.isArray(files) ? files : []).map((file2) => String(file2.name ?? "")).filter(Boolean).sort();
192693
- }
192694
- function stringOrNull(value) {
192695
- return typeof value === "string" && value.length > 0 ? value : null;
192696
- }
192697
- function runArtifactInventory(run) {
192698
- const rows = [
192699
- { kind: "metadata", name: "run.json", path: "run.json", present: "yes" }
192700
- ];
192701
- if (run.inputJson != null) {
192702
- rows.push({ kind: "input", name: "input.json", path: "input.json", present: "yes" });
192703
- }
192704
- if (run.metadata != null) {
192705
- rows.push({ kind: "metadata", name: "metadata.json", path: "metadata.json", present: "yes" });
192706
- }
192707
- if (run.expected != null) {
192708
- rows.push({ kind: "expected", name: "expected.json", path: "expected.json", present: "yes" });
192709
- }
192710
- for (const name of fileNames(run.inputFiles)) {
192711
- rows.push({ kind: "input", name, path: `input/${name}`, present: "yes" });
192712
- }
192713
- for (const name of fileNames(run.resultFiles).filter((name2) => name2 !== "issues.md" && name2 !== "trace.jsonl")) {
192714
- rows.push({ kind: "output", name, path: `output/${name}`, present: "yes" });
192715
- }
192716
- for (const name of fileNames(run.expectedFiles)) {
192717
- rows.push({ kind: "expected", name, path: `expected/${name}`, present: "yes" });
192718
- }
192719
- for (const name of fileNames(run.issueFiles)) {
192720
- rows.push({ kind: "issues", name, path: name, present: "yes" });
192721
- }
192722
- for (const name of fileNames(run.traceFiles)) {
192723
- rows.push({ kind: "trace", name, path: name, present: "yes" });
192724
- }
192725
- for (const name of fileNames(run.lockfileFiles)) {
192726
- rows.push({ kind: "lockfile", name, path: name, present: "yes" });
192727
- }
192728
- return rows.sort((a, b2) => a.path.localeCompare(b2.path));
192729
- }
192730
- function normalizeGeneratedTokens(value) {
192731
- return value.replace(/\d{4}-\d{2}-\d{2}/g, "<date>").replace(/\d{8}/g, "<date>");
192732
- }
192733
- function compareFileInventory(expected, output, normalizeDates) {
192734
- const normalize = (value) => normalizeDates ? normalizeGeneratedTokens(value) : value;
192735
- const outputByNormalized = new Map(output.map((name) => [normalize(name), name]));
192736
- const expectedByNormalized = new Map(expected.map((name) => [normalize(name), name]));
192737
- const matched = expected.map((expectedName2) => {
192738
- const outputName = outputByNormalized.get(normalize(expectedName2));
192739
- return outputName ? { expected: expectedName2, output: outputName } : null;
192740
- }).filter((match) => Boolean(match));
192741
- return {
192742
- matched,
192743
- missing: expected.filter((name) => !outputByNormalized.has(normalize(name))),
192744
- extra: output.filter((name) => !expectedByNormalized.has(normalize(name)))
192745
- };
192746
- }
192747
- async function compareMatchedFileText(client, left, right, out, matched, normalizeDates) {
192748
- const expectedDir = path12.join(out, "expected");
192749
- const outputDir = path12.join(out, "output");
192750
- await fs7.mkdir(expectedDir, { recursive: true });
192751
- await fs7.mkdir(outputDir, { recursive: true });
192752
- const diffs = [];
192753
- for (const pair of matched) {
192754
- const expectedPath = path12.join(expectedDir, pair.expected);
192755
- const outputPath = path12.join(outputDir, pair.output);
192756
- await downloadStreamToFile(client, runFileUrl(left, pair.expected), expectedPath);
192757
- await downloadStreamToFile(client, runFileUrl(right, pair.output), outputPath);
192758
- const expectedText = extractComparableText(expectedPath);
192759
- const outputText = extractComparableText(outputPath);
192760
- if (expectedText.text == null || outputText.text == null) {
192761
- const [expectedBuffer, outputBuffer] = await Promise.all([
192762
- fs7.readFile(expectedPath),
192763
- fs7.readFile(outputPath)
192764
- ]);
192765
- const expectedHash = createHash3("sha256").update(expectedBuffer).digest("hex");
192766
- const outputHash = createHash3("sha256").update(outputBuffer).digest("hex");
192767
- diffs.push({
192768
- ...pair,
192769
- status: expectedBuffer.length === outputBuffer.length && expectedHash === outputHash ? "binary-match" : "binary-different",
192770
- expectedBytes: expectedBuffer.length,
192771
- outputBytes: outputBuffer.length,
192772
- expectedSha256: expectedHash,
192773
- outputSha256: outputHash,
192774
- textExtraction: {
192775
- expected: expectedText.reason ?? null,
192776
- output: outputText.reason ?? null
192777
- }
192778
- });
192779
- continue;
192780
- }
192781
- const normalize = (value) => normalizeDates ? normalizeGeneratedTokens(value).trim() : value.trim();
192782
- diffs.push({
192783
- ...pair,
192784
- status: normalize(expectedText.text) === normalize(outputText.text) ? "match" : "text-different"
192785
- });
192786
- }
192787
- return diffs;
192788
- }
192789
- function runFileUrl(side, name) {
192790
- const runId = encodeURIComponent(side.runId);
192791
- const filename = encodeURIComponent(name);
192792
- return side.kind === "expected" ? `/api/v1/agents/runs/${runId}/expected/${filename}` : `/api/v1/agents/runs/${runId}/files/output/${filename}`;
192793
- }
192794
- function encodeRunArtifactPath(artifactPath) {
192795
- return artifactPath.split("/").map(encodeURIComponent).join("/");
192796
- }
192797
- async function downloadStreamToFile(client, url2, target) {
192798
- await fs7.mkdir(path12.dirname(target), { recursive: true });
192799
- const response = await client.getStream(url2);
192800
- await fs7.writeFile(target, Buffer.from(await response.arrayBuffer()));
192801
- }
192802
- function extractComparableText(file2) {
192803
- const ext = path12.extname(file2).toLowerCase();
192804
- if ([".txt", ".md", ".json", ".csv", ".xml"].includes(ext)) {
192805
- return { text: readFileSync8(file2, "utf8") };
192806
- }
192807
- if (ext === ".pdf") {
192808
- const result = spawnSync4("pdftotext", [file2, "-"], { encoding: "utf8" });
192809
- return result.status === 0 ? { text: result.stdout } : { text: null, reason: textExtractionFallbackReason("pdftotext", result) };
192810
- }
192811
- if (ext === ".docx") {
192812
- const script = "import re,sys,zipfile; z=zipfile.ZipFile(sys.argv[1]); x=z.read('word/document.xml').decode('utf-8','ignore'); print(re.sub('<[^>]+>',' ',x))";
192813
- const result = spawnSync4("python3", ["-c", script, file2], { encoding: "utf8" });
192814
- return result.status === 0 ? { text: result.stdout } : { text: null, reason: textExtractionFallbackReason("python3", result) };
192815
- }
192816
- return { text: null, reason: `No text extractor for ${ext || "extensionless"} files` };
192817
- }
192818
- function textExtractionFallbackReason(tool, result) {
192819
- if (result.error)
192820
- return `${tool} unavailable; compared bytes instead`;
192821
- return `${tool} could not extract text; compared bytes instead`;
192822
- }
192823
- function renderComparisonReport(report) {
192824
- const header = `Comparison ${report.status}`;
192825
- if (report.status === "pass") {
192826
- success2(header);
192827
- } else {
192828
- error51(header);
192829
- }
192830
- for (const warning of report.warnings ?? []) {
192831
- dim(`warning: ${warning}`);
192832
- }
192833
- console.log(table([
192834
- { item: "JSON differences", count: report.jsonDifferences.length },
192835
- { item: "Matched files", count: report.matchedFiles.length },
192836
- { item: "Missing files", count: report.missingFiles.length },
192837
- { item: "Extra files", count: report.extraFiles.length },
192838
- {
192839
- item: "Text differences",
192840
- count: report.textDifferences.filter((diff) => diff.status === "text-different").length
192841
- }
192842
- ], [
192843
- { key: "item", header: "ITEM" },
192844
- { key: "count", header: "COUNT" }
192845
- ]));
192846
- for (const diff of report.textDifferences) {
192847
- const textExtraction = diff.textExtraction;
192848
- const reasons = [textExtraction?.expected, textExtraction?.output].filter((value) => typeof value === "string" && value.length > 0).filter((value, index, values) => values.indexOf(value) === index);
192849
- for (const reason of reasons) {
192850
- dim(`warning: ${String(diff.expected)} vs ${String(diff.output)}: ${reason}`);
192851
- }
192852
- }
192853
- }
192854
-
192855
193350
  // src/commands/agents/sessions.ts
192856
193351
  function registerSessionCommands(agent) {
192857
193352
  const session = agent.command("session").description("Manage agent builder sessions.").action(() => {
@@ -192900,18 +193395,16 @@ async function stopSession(sessionId, opts) {
192900
193395
 
192901
193396
  // src/commands/agents/index.ts
192902
193397
  function registerAgentCommands(program2) {
192903
- const agent = program2.command("agents").alias("agent").description("Manage Eigenpal agents: Git source, datasets, runs, experiments, sessions, and releases.").action(() => {
193398
+ const agent = program2.command("agents").description("Manage Eigenpal agents: Git source, datasets, runs, experiments, sessions, and releases.").action(() => {
192904
193399
  process.stderr.write("`eigenpal agents` requires a subcommand. Run `eigenpal agents --help`.\n");
192905
193400
  process.exit(2);
192906
193401
  });
192907
193402
  addJsonFlag(withBaseUrl(agent.command("run <target>"))).description("Run an agent target, e.g. agents.invoice-agent@latest.").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 a persisted dataset example by name").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 exiting 2", intArg, 1800).action(action(runTarget));
192908
- addJsonFlag(withBaseUrl(agent.command("rerun <run-id>"))).description("Create a new run from a previous run's stored input snapshot.").option("--source-ref <ref>", 'Source ref for the new run (default: latest). Use "original" to reproduce the previous resolved version.').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 exiting 2", intArg, 1800).action(action(rerunRun));
192909
193403
  addJsonFlag(withPagination(withBaseUrl(agent.command("list")), 50)).description("List agents.").option("--search <q>", "Search by slug, name, or description").action(action(listAgents));
192910
193404
  registerAgentFileCommands(agent);
192911
193405
  addJsonFlag(agent.command("validate [dir]")).description("Validate a local agent package (layout, manifest, schemas, and Git source rules).").action(action(validateAgentCommand));
192912
193406
  registerAgentSourceCommands(agent);
192913
193407
  registerDatasetCommands(agent);
192914
- registerRunCommands(agent);
192915
193408
  registerExperimentCommands(agent);
192916
193409
  registerSessionCommands(agent);
192917
193410
  registerEnvCommands(agent);
@@ -193487,7 +193980,7 @@ async function init(name, opts) {
193487
193980
  info("Next steps:");
193488
193981
  dim(` cd ${targetDisplay}`);
193489
193982
  dim(` eigenpal workflow validate # all three local checks`);
193490
- dim(` eigenpal workflow execution run ${name} sample`);
193983
+ dim(` eigenpal workflow run ${name} sample`);
193491
193984
  }
193492
193985
  function assertSafeTargetDir(target, dir) {
193493
193986
  if (dir !== undefined && !dir.startsWith("/") && dir.split(/[/\\]/).some((s) => s === "..")) {
@@ -194858,80 +195351,6 @@ function formatEigenpalDirIfAvailable(dir) {
194858
195351
  });
194859
195352
  }
194860
195353
 
194861
- // src/lib/resolve-workflow.ts
194862
- var WORKFLOW_ID_PREFIX = "wf_";
194863
- function looksLikeWorkflowId(value) {
194864
- return value.startsWith(WORKFLOW_ID_PREFIX);
194865
- }
194866
- function levenshtein(a, b2, maxDistance) {
194867
- if (a === b2)
194868
- return 0;
194869
- if (Math.abs(a.length - b2.length) > maxDistance)
194870
- return maxDistance + 1;
194871
- if (a.length === 0 || b2.length === 0)
194872
- return a.length || b2.length;
194873
- const prev = new Array(b2.length + 1);
194874
- const curr = new Array(b2.length + 1);
194875
- for (let j3 = 0;j3 <= b2.length; j3++)
194876
- prev[j3] = j3;
194877
- for (let i2 = 1;i2 <= a.length; i2++) {
194878
- curr[0] = i2;
194879
- let minRow = curr[0];
194880
- for (let j3 = 1;j3 <= b2.length; j3++) {
194881
- const cost = a[i2 - 1] === b2[j3 - 1] ? 0 : 1;
194882
- curr[j3] = Math.min(prev[j3] + 1, curr[j3 - 1] + 1, prev[j3 - 1] + cost);
194883
- if (curr[j3] < minRow)
194884
- minRow = curr[j3];
194885
- }
194886
- if (minRow > maxDistance)
194887
- return maxDistance + 1;
194888
- for (let j3 = 0;j3 <= b2.length; j3++)
194889
- prev[j3] = curr[j3];
194890
- }
194891
- return prev[b2.length];
194892
- }
194893
- async function findSimilarSlug(client, slug) {
194894
- try {
194895
- const res = await client.get("/api/v1/workflows", {
194896
- search: slug,
194897
- limit: "20"
194898
- });
194899
- const candidates = (res.data ?? []).map((w3) => w3.name).filter((n) => typeof n === "string" && n.length > 0);
194900
- let best = null;
194901
- for (const candidate of candidates) {
194902
- const distance = levenshtein(candidate, slug, 2);
194903
- if (distance <= 2 && (best === null || distance < best.distance)) {
194904
- best = { name: candidate, distance };
194905
- }
194906
- }
194907
- return best?.name ?? null;
194908
- } catch {
194909
- return null;
194910
- }
194911
- }
194912
- function notFound(idOrSlug, suggestion) {
194913
- const base = `Workflow "${idOrSlug}" not found on the server.`;
194914
- const action2 = "Push it first, or run `eigenpal workflow list` to see what's available.";
194915
- return new Error(suggestion ? `${base} Did you mean "${suggestion}"? ${action2}` : `${base} ${action2}`);
194916
- }
194917
- async function resolveWorkflowId(client, idOrSlug) {
194918
- if (looksLikeWorkflowId(idOrSlug)) {
194919
- try {
194920
- const wf = await client.get(`/api/v1/workflows/${idOrSlug}`);
194921
- return wf.id;
194922
- } catch {
194923
- throw notFound(idOrSlug, null);
194924
- }
194925
- }
194926
- const res = await client.get("/api/v1/workflows", { name: idOrSlug });
194927
- const match = res.data?.[0];
194928
- if (!match) {
194929
- const suggestion = await findSimilarSlug(client, idOrSlug);
194930
- throw notFound(idOrSlug, suggestion);
194931
- }
194932
- return match.id;
194933
- }
194934
-
194935
195354
  // src/commands/workflow/clear.ts
194936
195355
  import { existsSync as existsSync14, readdirSync as readdirSync5, rmSync as rmSync3, statSync as statSync4 } from "fs";
194937
195356
  import { basename, join as join7 } from "path";
@@ -195225,125 +195644,6 @@ function registerEvaluatorTypeCommands(parent) {
195225
195644
  });
195226
195645
  }
195227
195646
 
195228
- // src/lib/watch.ts
195229
- var TERMINAL_STATUSES = new Set(["completed", "failed", "cancelled", "rejected"]);
195230
- var STATUS_BADGES = {
195231
- pending: "[ ]",
195232
- running: "[..]",
195233
- completed: "[ok]",
195234
- failed: "[x]",
195235
- cancelled: "[-]",
195236
- skipped: "[s]"
195237
- };
195238
- function statusBadge(status2) {
195239
- return STATUS_BADGES[status2] ?? `[${status2.slice(0, 3)}]`;
195240
- }
195241
- function durationSuffix(ms) {
195242
- const formatted = formatDuration(ms);
195243
- return formatted ? ` ${formatted}` : "";
195244
- }
195245
- function renderFrame(snapshot) {
195246
- const lines = [
195247
- `${statusBadge(snapshot.status)} ${snapshot.id}${durationSuffix(snapshot.durationMs)}`
195248
- ];
195249
- for (const step of snapshot.stepExecutions ?? []) {
195250
- const errSuffix = step.error ? ` err: ${step.error.slice(0, 80)}` : "";
195251
- lines.push(` ${statusBadge(step.status)} ${step.stepName}${durationSuffix(step.durationMs)}${errSuffix}`);
195252
- if (step.outputPreview) {
195253
- lines.push(` ↳ ${step.outputPreview}`);
195254
- }
195255
- }
195256
- if (snapshot.error && (snapshot.stepExecutions ?? []).length === 0) {
195257
- lines.push(` err: ${snapshot.error.slice(0, 200)}`);
195258
- }
195259
- return lines.join(`
195260
- `);
195261
- }
195262
- var ANSI_CURSOR_UP = "\x1B[A";
195263
- var ANSI_CLEAR_LINE = "\x1B[2K\r";
195264
- function makeAnsiRenderer() {
195265
- const isTty = Boolean(process.stderr.isTTY);
195266
- let previousLineCount = 0;
195267
- return (frame) => {
195268
- let buf = "";
195269
- if (isTty) {
195270
- for (let i2 = 0;i2 < previousLineCount; i2++) {
195271
- buf += i2 === 0 ? ANSI_CLEAR_LINE : `${ANSI_CURSOR_UP}${ANSI_CLEAR_LINE}`;
195272
- }
195273
- }
195274
- buf += frame;
195275
- if (!frame.endsWith(`
195276
- `))
195277
- buf += `
195278
- `;
195279
- if (!isTty && previousLineCount > 0)
195280
- buf += `
195281
- `;
195282
- process.stderr.write(buf);
195283
- previousLineCount = frame.split(`
195284
- `).length;
195285
- };
195286
- }
195287
- function defaultSleep(ms) {
195288
- return new Promise((resolve4) => setTimeout(resolve4, ms));
195289
- }
195290
- function snapshotsDiffer(a, b2) {
195291
- if (a.status !== b2.status)
195292
- return true;
195293
- const aSteps = a.stepExecutions ?? [];
195294
- const bSteps = b2.stepExecutions ?? [];
195295
- if (aSteps.length !== bSteps.length)
195296
- return true;
195297
- for (let i2 = 0;i2 < aSteps.length; i2++) {
195298
- if (aSteps[i2].stepName !== bSteps[i2].stepName || aSteps[i2].status !== bSteps[i2].status) {
195299
- return true;
195300
- }
195301
- }
195302
- return false;
195303
- }
195304
- function defaultShouldAbort(err2) {
195305
- return err2 instanceof ApiError && err2.status >= 400 && err2.status < 500;
195306
- }
195307
- async function watchExecution(opts) {
195308
- const sleep = opts.sleep ?? defaultSleep;
195309
- const now = opts.now ?? (() => Date.now());
195310
- const render = opts.render ?? makeAnsiRenderer();
195311
- const maxMs = opts.maxMs ?? 30 * 60 * 1000;
195312
- const transitionInterval = opts.transitionIntervalMs ?? 2000;
195313
- const steadyInterval = opts.steadyIntervalMs ?? 5000;
195314
- const shouldAbort = opts.shouldAbort ?? defaultShouldAbort;
195315
- const start = now();
195316
- let previous;
195317
- let lastFetched;
195318
- while (true) {
195319
- let current;
195320
- try {
195321
- current = await opts.fetch();
195322
- } catch (err2) {
195323
- if (shouldAbort(err2))
195324
- throw err2;
195325
- const message = err2 instanceof Error ? err2.message : String(err2);
195326
- render(`(network) ${message.slice(0, 120)}`);
195327
- if (lastFetched && now() - start >= maxMs) {
195328
- return { final: lastFetched, detached: true };
195329
- }
195330
- await sleep(steadyInterval);
195331
- continue;
195332
- }
195333
- lastFetched = current;
195334
- render(renderFrame(current));
195335
- if (TERMINAL_STATUSES.has(current.status)) {
195336
- return { final: current, detached: false };
195337
- }
195338
- if (now() - start >= maxMs) {
195339
- return { final: current, detached: true };
195340
- }
195341
- const transitioned = previous == null || snapshotsDiffer(previous, current);
195342
- previous = current;
195343
- await sleep(transitioned ? transitionInterval : steadyInterval);
195344
- }
195345
- }
195346
-
195347
195647
  // src/commands/workflow/exec.ts
195348
195648
  import { join as join11 } from "path";
195349
195649
 
@@ -195532,7 +195832,8 @@ var DEFAULT_CONCURRENCY = 3;
195532
195832
  async function pollExecution(client, executionId) {
195533
195833
  const start = Date.now();
195534
195834
  while (Date.now() - start < MAX_POLL_MS) {
195535
- const res = await client.get(`/api/v1/workflows/executions/${executionId}`);
195835
+ const payload = await client.get(`/api/v1/runs/${executionId}?include=detail`);
195836
+ const res = "run" in payload && payload.run ? payload.run : payload;
195536
195837
  if (["completed", "failed", "cancelled", "rejected"].includes(res.status))
195537
195838
  return res;
195538
195839
  await new Promise((r5) => setTimeout(r5, POLL_INTERVAL_MS));
@@ -195610,7 +195911,8 @@ async function runExec(client, dir, workflowIdOrSlug, exampleNames, options8 = {
195610
195911
  else
195611
195912
  failed++;
195612
195913
  try {
195613
- const artifact = await client.get(`/api/v1/workflows/executions/${executionId}?includeSteps=true`);
195914
+ const artifactPayload = await client.get(`/api/v1/runs/${executionId}?include=detail`);
195915
+ const artifact = "run" in artifactPayload && artifactPayload.run ? artifactPayload.run : artifactPayload;
195614
195916
  const artifactDir = await writeExecutionArtifacts(client, exampleDir, artifact);
195615
195917
  if (!interactive)
195616
195918
  console.log(ui.dim(` Artifacts: ${artifactDir}`));
@@ -195637,14 +195939,13 @@ ${ui.bold("Results:")} ${ui.ok(String(passed))} passed, ${failed > 0 ? ui.err(St
195637
195939
  }
195638
195940
 
195639
195941
  // src/commands/workflow/execution.ts
195640
- function registerWorkflowExecutionCommands(parent) {
195641
- const execution = parent.command("execution").description("Run, inspect, and compare server-side executions.");
195642
- const runCmd = execution.command("run <workflow-id> [examples...]").description("Run a saved workflow against local dataset examples.").option("--dir <dir>", "Local eigenpal directory", undefined).option("--concurrency <n>", "Max examples to run in parallel (default: 3)", intArg).addHelpText("after", `
195942
+ function registerWorkflowRunCommand(parent) {
195943
+ const runCmd = parent.command("run <workflow-id> [examples...]").description("Run a saved workflow against local dataset examples.").option("--dir <dir>", "Local eigenpal directory", undefined).option("--concurrency <n>", "Max examples to run in parallel (default: 3)", intArg).addHelpText("after", `
195643
195944
  Examples:
195644
- $ eigenpal workflow execution run wf_abc123 # all examples
195645
- $ eigenpal workflow execution run wf_abc123 sample-1 sample-2
195646
- $ eigenpal workflow execution run wf_abc123 --concurrency 5
195647
- $ eigenpal workflow execution run wf_abc123 sample --json | jq '.passed'
195945
+ $ eigenpal workflow run wf_abc123 # all examples
195946
+ $ eigenpal workflow run wf_abc123 sample-1 sample-2
195947
+ $ eigenpal workflow run wf_abc123 --concurrency 5
195948
+ $ eigenpal workflow run wf_abc123 sample --json | jq '.passed'
195648
195949
 
195649
195950
  Reads examples from ./dataset/examples/<example>/ and writes per-run
195650
195951
  artifacts to ./dataset/examples/<example>/executions/<timestamp>/.
@@ -195671,272 +195972,6 @@ Exits 1 when any example fails.
195671
195972
  formatEigenpalDirIfAvailable(config2.dir);
195672
195973
  }
195673
195974
  });
195674
- const getCmd = execution.command("get <executionId>").description("Fetch a single execution payload. Optionally narrow to one step.").option("--step <name>", "Show only this step (or comma-separated list)").option("--include <kinds>", "Comma-separated subset of input,output,error,duration. `input` projects the resolved templated config (= what the processor actually received). `inputRef` returns the minimal predecessor-id reference instead.", "input,output,error,duration").addHelpText("after", "\nFor field projection, pipe `--json` through real jq:\n" + ` $ eigenpal workflow execution get exec_… --json | jq '.status'
195675
- ` + ` $ eigenpal workflow execution get exec_… --json | jq '.stepExecutions[] | select(.status=="failed")'
195676
- `);
195677
- addJsonFlag(withBaseUrl(getCmd)).action(getExecution);
195678
- const listCmd = execution.command("list <workflow-id>").description("List recent executions for a workflow.").option("--status <status>", "Filter by status: pending|running|completed|failed|cancelled");
195679
- addJsonFlag(withBaseUrl(withPagination(listCmd))).action(listExecutions);
195680
- const watchCmd = execution.command("watch <executionId>").description("Stream live execution status until terminal or 30-min detach.").option("--max-wait <seconds>", "Detach after N seconds (default 1800 = 30 min)", intArg, 30 * 60);
195681
- withBaseUrl(watchCmd).action(watchCommand);
195682
- const compareCmd = execution.command("compare <executionA> <executionB>").description("Diff two executions side-by-side, per step.").option("--step <name>", "Restrict comparison to one step");
195683
- withBaseUrl(compareCmd).action(compareExecutions);
195684
- const cancelCmd = execution.command("cancel <executionId>").description("Cancel an execution. Idempotent on already-terminal runs.").option("--yes", "Required for non-TTY shells (CI, pipes). Acts immediately, no prompt.").addHelpText("after", `
195685
- Examples:
195686
- $ eigenpal workflow execution cancel exec_123abc
195687
- $ eigenpal workflow execution cancel exec_123abc --yes # required in CI
195688
- $ eigenpal workflow execution cancel exec_123abc --json # raw server payload
195689
-
195690
- Behavior:
195691
- - created/pending: transitions straight to cancelled.
195692
- - running/waiting: stamps cancelRequestedAt; the worker observes it
195693
- between step transitions and exits cleanly.
195694
- - already terminal (completed/failed/cancelled): no-op, exits 0 with an
195695
- info line. Idempotent — safe to retry.
195696
- `);
195697
- addJsonFlag(withBaseUrl(cancelCmd)).action(async (executionId, opts) => {
195698
- const config2 = resolveConfig(opts);
195699
- requireApiKey(config2);
195700
- const client = new ApiClient(config2);
195701
- try {
195702
- await cancelExecution(client, executionId, opts);
195703
- } catch (err2) {
195704
- error51(err2 instanceof Error ? err2.message : String(err2));
195705
- process.exit(1);
195706
- }
195707
- });
195708
- }
195709
- async function cancelExecution(client, executionId, opts) {
195710
- if (!opts.yes && !isTTY()) {
195711
- throw new Error("cancel is destructive and requires --yes when run non-interactively");
195712
- }
195713
- const result = await client.post(`/api/v1/workflows/executions/${executionId}/cancel`);
195714
- if (opts.json) {
195715
- console.log(JSON.stringify(result, null, 2));
195716
- return;
195717
- }
195718
- const status2 = result.status;
195719
- if (status2 === "already-terminal") {
195720
- info(`already terminal: ${result.wasStatus ?? "unknown"}`);
195721
- return;
195722
- }
195723
- if (status2 === "cancelled" || status2 === "cancellation-requested") {
195724
- success2("cancellation requested");
195725
- return;
195726
- }
195727
- info(`server returned status: ${String(status2 ?? "unknown")}`);
195728
- }
195729
- async function getExecution(executionId, opts) {
195730
- const config2 = resolveConfig(opts);
195731
- requireApiKey(config2);
195732
- const client = new ApiClient(config2);
195733
- try {
195734
- const result = await client.get(`/api/v1/workflows/executions/${executionId}?includeSteps=true`);
195735
- if (opts.step) {
195736
- const wanted = new Set(opts.step.split(",").map((s5) => s5.trim()));
195737
- const filtered = (result.stepExecutions ?? []).filter((s5) => wanted.has(s5.stepName));
195738
- console.log(JSON.stringify({ stepExecutions: filtered.map((s5) => filterIncludes(s5, opts.include)) }, null, 2));
195739
- return;
195740
- }
195741
- if (opts.json) {
195742
- console.log(JSON.stringify({
195743
- ...result,
195744
- stepExecutions: (result.stepExecutions ?? []).map((s5) => filterIncludes(s5, opts.include))
195745
- }, null, 2));
195746
- return;
195747
- }
195748
- const snapshot = {
195749
- id: result.id ?? executionId,
195750
- status: result.status ?? "unknown",
195751
- startedAt: result.startedAt ?? null,
195752
- completedAt: result.completedAt ?? null,
195753
- durationMs: result.durationMs ?? null,
195754
- stepExecutions: (result.stepExecutions ?? []).map((s5) => ({
195755
- stepName: s5.stepName,
195756
- status: s5.status,
195757
- durationMs: s5.durationMs,
195758
- error: s5.error,
195759
- outputPreview: previewOutput(s5.outputData)
195760
- })),
195761
- error: result.error ?? null
195762
- };
195763
- console.log(renderFrame(snapshot));
195764
- process.stderr.write(`${ui.dim(`use --json for the full payload, then pipe to jq for field projection`)}
195765
- `);
195766
- } catch (err2) {
195767
- error51(err2 instanceof Error ? err2.message : String(err2));
195768
- process.exit(1);
195769
- }
195770
- }
195771
- async function listExecutions(workflow2, opts) {
195772
- const config2 = resolveConfig(opts);
195773
- requireApiKey(config2);
195774
- const client = new ApiClient(config2);
195775
- const workflowId = await resolveWorkflowId(client, workflow2);
195776
- const params = {
195777
- limit: String(opts.limit),
195778
- offset: String(opts.offset),
195779
- workflowId
195780
- };
195781
- if (opts.status)
195782
- params.status = opts.status;
195783
- try {
195784
- const raw = await client.get(`/api/v1/workflows/${workflowId}/executions`, params);
195785
- renderListResult(raw, [
195786
- { key: "id", header: "id" },
195787
- { key: "status", header: "status" },
195788
- { key: "startedAt", header: "startedAt", format: formatTimestamp },
195789
- {
195790
- key: "durationMs",
195791
- header: "duration",
195792
- align: "right",
195793
- format: (v11) => typeof v11 === "number" ? formatDuration(v11) : "-"
195794
- }
195795
- ], { json: opts.json, entityLabel: "execution" });
195796
- } catch (err2) {
195797
- error51(err2 instanceof Error ? err2.message : String(err2));
195798
- process.exit(1);
195799
- }
195800
- }
195801
- async function watchCommand(executionId, opts) {
195802
- const config2 = resolveConfig(opts);
195803
- requireApiKey(config2);
195804
- const client = new ApiClient(config2);
195805
- try {
195806
- const result = await watchExecution({
195807
- fetch: () => client.get(`/api/v1/workflows/executions/${executionId}?includeSteps=true`),
195808
- maxMs: opts.maxWait * 1000
195809
- });
195810
- if (result.detached) {
195811
- process.stderr.write(`
195812
- Detached after ${opts.maxWait}s without reaching terminal status. Re-run \`eigenpal workflow execution watch ${executionId}\` to resume.
195813
- `);
195814
- process.exit(2);
195815
- }
195816
- if (result.final.status === "failed" || result.final.status === "cancelled") {
195817
- process.exit(1);
195818
- }
195819
- } catch (err2) {
195820
- error51(err2 instanceof Error ? err2.message : String(err2));
195821
- process.exit(1);
195822
- }
195823
- }
195824
- async function compareExecutions(idA, idB, opts) {
195825
- const config2 = resolveConfig(opts);
195826
- requireApiKey(config2);
195827
- const client = new ApiClient(config2);
195828
- try {
195829
- const [a5, b8] = await Promise.all([
195830
- client.get(`/api/v1/workflows/executions/${idA}?includeSteps=true`),
195831
- client.get(`/api/v1/workflows/executions/${idB}?includeSteps=true`)
195832
- ]);
195833
- const stepNames = new Set;
195834
- for (const s5 of a5.stepExecutions ?? [])
195835
- stepNames.add(s5.stepName);
195836
- for (const s5 of b8.stepExecutions ?? [])
195837
- stepNames.add(s5.stepName);
195838
- const ordered = Array.from(stepNames).sort();
195839
- const targetSteps = opts.step ? ordered.filter((n5) => n5 === opts.step) : ordered;
195840
- console.log(`${ui.dim("A =")} ${ui.bold(idA)} ${ui.dim("status=")}${colorStatus(a5.status)} ${ui.dim("durationMs=")}${a5.durationMs ?? "-"}`);
195841
- console.log(`${ui.dim("B =")} ${ui.bold(idB)} ${ui.dim("status=")}${colorStatus(b8.status)} ${ui.dim("durationMs=")}${b8.durationMs ?? "-"}`);
195842
- console.log("");
195843
- console.log(ui.dim(`step A status B status Δ duration output`));
195844
- console.log(ui.dim(`──────────────────────── ───────── ───────── ────────── ──────`));
195845
- for (const stepName of targetSteps) {
195846
- const aStep = (a5.stepExecutions ?? []).find((s5) => s5.stepName === stepName);
195847
- const bStep = (b8.stepExecutions ?? []).find((s5) => s5.stepName === stepName);
195848
- const aStatus = aStep?.status ?? "—";
195849
- const bStatus = bStep?.status ?? "—";
195850
- const aDur = aStep?.durationMs ?? null;
195851
- const bDur = bStep?.durationMs ?? null;
195852
- const delta = aDur != null && bDur != null ? `${bDur - aDur >= 0 ? "+" : ""}${bDur - aDur}ms` : "—";
195853
- const outputState = compareOutputs(aStep?.output, bStep?.output);
195854
- console.log(`${stepName.padEnd(28)} ${padColored(colorStatus(aStatus), aStatus, 11)} ${padColored(colorStatus(bStatus), bStatus, 11)} ${delta.padEnd(12)} ${colorOutputState(outputState)}`);
195855
- }
195856
- const finalState = compareOutputs(a5.output, b8.output);
195857
- console.log("");
195858
- console.log(`${ui.dim("final output:")} ${colorOutputState(finalState)}`);
195859
- } catch (err2) {
195860
- error51(err2 instanceof Error ? err2.message : String(err2));
195861
- process.exit(1);
195862
- }
195863
- }
195864
- function padColored(colored, plain, width) {
195865
- const pad = Math.max(0, width - plain.length);
195866
- return colored + " ".repeat(pad);
195867
- }
195868
- function colorStatus(status2) {
195869
- if (!status2)
195870
- return "—";
195871
- if (status2 === "completed")
195872
- return ui.ok(status2);
195873
- if (status2 === "failed" || status2 === "cancelled")
195874
- return ui.err(status2);
195875
- if (status2 === "running" || status2 === "pending")
195876
- return ui.info(status2);
195877
- return status2;
195878
- }
195879
- function colorOutputState(state) {
195880
- if (state === "identical")
195881
- return ui.ok(state);
195882
- if (state === "—")
195883
- return ui.dim(state);
195884
- if (state.startsWith("differs"))
195885
- return ui.warn(state);
195886
- return state;
195887
- }
195888
- function compareOutputs(a5, b8) {
195889
- if (a5 === undefined && b8 === undefined)
195890
- return "—";
195891
- if (a5 === undefined)
195892
- return "B only";
195893
- if (b8 === undefined)
195894
- return "A only";
195895
- const aJson = JSON.stringify(a5);
195896
- const bJson = JSON.stringify(b8);
195897
- if (aJson === bJson)
195898
- return "identical";
195899
- return `differs (A=${aJson.length}b, B=${bJson.length}b)`;
195900
- }
195901
- var PREVIEW_MAX = 120;
195902
- function previewOutput(value) {
195903
- if (value == null)
195904
- return null;
195905
- if (typeof value === "string") {
195906
- if (value.length === 0)
195907
- return null;
195908
- return value.length > PREVIEW_MAX ? `${value.slice(0, PREVIEW_MAX - 1)}…` : value;
195909
- }
195910
- let json2;
195911
- try {
195912
- json2 = JSON.stringify(value);
195913
- } catch {
195914
- return null;
195915
- }
195916
- if (!json2 || json2 === "{}" || json2 === "[]" || json2 === "null")
195917
- return null;
195918
- const flat = json2.replace(/\s+/g, " ");
195919
- return flat.length > PREVIEW_MAX ? `${flat.slice(0, PREVIEW_MAX - 1)}…` : flat;
195920
- }
195921
- function filterIncludes(step, includes) {
195922
- if (!includes)
195923
- return step;
195924
- const wanted = new Set(includes.split(",").map((s5) => s5.trim()));
195925
- const out = { stepName: step.stepName, status: step.status };
195926
- if (wanted.has("input") || wanted.has("config") || wanted.has("resolvedConfig")) {
195927
- out.input = step.resolvedConfig;
195928
- }
195929
- if (wanted.has("inputRef") || wanted.has("inputData"))
195930
- out.inputRef = step.inputData;
195931
- if (wanted.has("output") || wanted.has("outputData"))
195932
- out.output = step.outputData;
195933
- if (wanted.has("error"))
195934
- out.error = step.error;
195935
- if (wanted.has("duration"))
195936
- out.durationMs = step.durationMs;
195937
- if ("overrideMode" in step)
195938
- out.overrideMode = step.overrideMode;
195939
- return out;
195940
195975
  }
195941
195976
 
195942
195977
  // src/commands/workflow/experiment-compare.ts
@@ -196226,13 +196261,13 @@ function exitUnsupported(type) {
196226
196261
  ` + ` redesign in EIG-104.
196227
196262
 
196228
196263
  ` + ` To run a single step today, push the workflow and run it via:
196229
- ` + ` $ eigenpal workflow execution run <workflow-id>
196264
+ ` + ` $ eigenpal workflow run <workflow-id>
196230
196265
  ` + ` $ eigenpal workflow experiment run <workflow-id> --example-id <name>`);
196231
196266
  process.exit(UNSUPPORTED_TYPE_EXIT);
196232
196267
  }
196233
196268
  function registerStepExecCommands(workflow2) {
196234
196269
  const step = workflow2.command("step").description("Run a single workflow step (placeholder; see EIG-104).");
196235
- step.command("exec <type>").description("DISABLED — local mimic runners removed pending server-side redesign (EIG-104). " + "Use `workflow execution run` or `workflow experiment run` instead.").option("--config-json <json>", "(unused; kept for back-compat parsing)").option("--config-file <path>", "(unused)").option("--inputs <pairs...>", "(unused)").option("--output-schema <path>", "(unused)").action(action(async (rawType) => {
196270
+ step.command("exec <type>").description("DISABLED — local mimic runners removed pending server-side redesign (EIG-104). " + "Use `workflow run` or `workflow experiment run` instead.").option("--config-json <json>", "(unused; kept for back-compat parsing)").option("--config-file <path>", "(unused)").option("--inputs <pairs...>", "(unused)").option("--output-schema <path>", "(unused)").action(action(async (rawType) => {
196236
196271
  const known = listStepTypes();
196237
196272
  if (!known.includes(rawType)) {
196238
196273
  error51(`Unknown step type: ${rawType}. Run \`eigenpal workflow step-type list\`.`);
@@ -197138,7 +197173,7 @@ YAML's \`name:\` field). Both:
197138
197173
  registerEvaluatorsCommands(workflow2);
197139
197174
  registerDatasetCommands2(workflow2);
197140
197175
  registerExperimentCommands2(workflow2);
197141
- registerWorkflowExecutionCommands(workflow2);
197176
+ registerWorkflowRunCommand(workflow2);
197142
197177
  registerVersionsCommands(workflow2);
197143
197178
  registerStepTypeCommands(workflow2);
197144
197179
  registerEvaluatorTypeCommands(workflow2);
@@ -197962,7 +197997,7 @@ ${ui.bold(`✗ ${failedExecs.length}/${totalCount} did not pass`)}
197962
197997
  const reason = (e8.error ?? "(no error message — fetch full execution)").replace(/\s+/g, " ").slice(0, 200);
197963
197998
  process.stderr.write(` ${ui.bold(label2)} ${ui.dim(`[${e8.status}]`)} ${reason}
197964
197999
  `);
197965
- process.stderr.write(` ${ui.dim(`eigenpal workflow execution get ${e8.id}`)}
198000
+ process.stderr.write(` ${ui.dim(`eigenpal runs get ${e8.id}`)}
197966
198001
  `);
197967
198002
  }
197968
198003
  process.stderr.write(`
@@ -198336,7 +198371,7 @@ var HELP_GROUPS = [
198336
198371
  },
198337
198372
  {
198338
198373
  title: "Workflows",
198339
- description: "Definition, evaluators, dataset, experiments, executions — all in one tree",
198374
+ description: "Definition, evaluators, dataset, experiments, and local runs",
198340
198375
  commands: ["workflow"]
198341
198376
  },
198342
198377
  {
@@ -198507,7 +198542,7 @@ program2.name("eigenpal").description("Eigenpal CLI — AI workflows, evals, and
198507
198542
  setQuiet(true);
198508
198543
  });
198509
198544
  program2.command("status").description("One-shot dashboard: server, active tenant, user, key id, workflow count. Pair with `--json` for scripting.").option("--base-url <url>", "Server base URL").option("--json", "Emit machine-readable JSON instead of human-readable text").action(action(async (opts) => status(opts)));
198510
- var initCmd = program2.command("init [name]").description("Scaffold a new workflow project. Without `[name]`, scaffolds into the current directory using the cwd basename as the workflow name. With `[name]`, creates `./<name>/` and uses that as the slug. The flat layout matches what `workflow execution run <slug>` already discovers — no manual file moves.").option("--template <name>", "Skip the picker; use this template").option("--dir <dir>", "Target directory (default: cwd if `[name]` omitted, else ./<name>)").option("--yes", "Non-interactive: pick the default template (blank)").action(async (name, opts) => {
198545
+ var initCmd = program2.command("init [name]").description("Scaffold a new workflow project. Without `[name]`, scaffolds into the current directory using the cwd basename as the workflow name. With `[name]`, creates `./<name>/` and uses that as the slug. The flat layout matches what `workflow run <slug>` already discovers — no manual file moves.").option("--template <name>", "Skip the picker; use this template").option("--dir <dir>", "Target directory (default: cwd if `[name]` omitted, else ./<name>)").option("--yes", "Non-interactive: pick the default template (blank)").action(async (name, opts) => {
198511
198546
  try {
198512
198547
  if (name) {
198513
198548
  await init(name, opts);
@@ -198554,6 +198589,7 @@ authCmd.command("use [profile]").description("Switch the active profile (persist
198554
198589
  });
198555
198590
  registerWorkflowCommands(program2);
198556
198591
  registerAgentCommands(program2);
198592
+ registerRunsCommands(program2);
198557
198593
  registerGitCommands(program2);
198558
198594
  program2.command("completion <shell>").description("Print a shell completion script to stdout (supported shells: bash, zsh, fish). Pipe into the appropriate completion file for your shell — see the script header for install instructions.").action(async (shell) => {
198559
198595
  try {