@eigenpal/cli 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -17389,7 +17389,7 @@ var init_run_expand = __esm(() => {
17389
17389
  observability: "use `expand=debug`",
17390
17390
  feedback: "use `expand=execution` — feedback lives at `execution.feedback` (agent runs)",
17391
17391
  steps: "use `expand=execution` — steps live at `execution.steps` (workflow runs)",
17392
- definition: "use `GET /api/v1/runs/{id}/definition`",
17392
+ definition: "use `expand=execution` — definitionSnapshot lives at `execution.definitionSnapshot` (workflow runs)",
17393
17393
  files: "downloadable output refs live at top-level `files` on completed detail responses; `expand=execution` adds `execution.files.output` metadata for agent runs",
17394
17394
  expected: "use `expand=execution` — expected output lives at `execution.expected`",
17395
17395
  trace: "trace id: `expand=debug` (`debug.traceId`); trace files: `GET /api/v1/runs/{id}/artifacts`",
@@ -62369,7 +62369,7 @@ var init_step_configs = __esm(() => {
62369
62369
  type: "transform.template",
62370
62370
  category: "transform",
62371
62371
  name: "Fill Template",
62372
- description: "Fill a DOCX template with data. Use list_templates tool to get template IDs and their placeholder schemas.",
62372
+ description: "Fill a DOCX template with data from previous steps. Select a template in the workflow builder or provide a template ID from your workspace.",
62373
62373
  configSchema: TransformTemplateConfigSchema,
62374
62374
  outputSchema: TransformTemplateOutputSchema,
62375
62375
  configInWith: true
@@ -62767,7 +62767,7 @@ var init_expected_error = __esm(() => {
62767
62767
  messageContains: exports_external.string().min(1).max(1000).optional(),
62768
62768
  step: exports_external.string().min(1).max(200).optional()
62769
62769
  }).refine((v) => v.code != null || v.messageContains != null || v.step != null, {
62770
- message: "expected/error.json must specify at least one of: code, messageContains, step (otherwise any failure would match)"
62770
+ message: "expected.json $error must specify at least one of: code, messageContains, step (otherwise any failure would match)"
62771
62771
  });
62772
62772
  });
62773
62773
 
@@ -63266,7 +63266,7 @@ function formatZodError(source) {
63266
63266
  severity: "error"
63267
63267
  }));
63268
63268
  }
63269
- var DATASET_NAME_PATTERN, DatasetNameSchema, DatasetMetaSchema, DatasetImportModeSchema, DatasetImportRequestSchema, EvaluatorsImportRequestSchema, EvalResultsExportFormatSchema, EvalResultsExportQuerySchema, EvalResultsExportByBatchQuerySchema, EvalResultExportRowSchema, EvalResultsExportJsonSchema, ERROR_HINTS;
63269
+ var DATASET_NAME_PATTERN, DatasetNameSchema, DatasetMetaSchema, DatasetImportModeSchema, DatasetImportRequestSchema, EvaluatorsImportRequestSchema, EvalResultsExportFormatSchema, ExperimentExportQuerySchema, EvalResultsExportQuerySchema, EvalResultExportRowSchema, EvalResultsExportJsonSchema, ERROR_HINTS;
63270
63270
  var init_dataset_archive = __esm(() => {
63271
63271
  init_zod();
63272
63272
  DATASET_NAME_PATTERN = /^[a-z0-9][a-z0-9-_]*$/;
@@ -63284,14 +63284,10 @@ var init_dataset_archive = __esm(() => {
63284
63284
  yaml: exports_external.string().min(1, "yaml content is required").max(1048576, "evaluator YAML exceeds 1 MB")
63285
63285
  });
63286
63286
  EvalResultsExportFormatSchema = exports_external.enum(["csv", "json"]);
63287
- EvalResultsExportQuerySchema = exports_external.object({
63288
- format: EvalResultsExportFormatSchema,
63289
- batchId: exports_external.string().min(1).optional()
63290
- });
63291
- EvalResultsExportByBatchQuerySchema = exports_external.object({
63292
- format: EvalResultsExportFormatSchema,
63293
- batchId: exports_external.string().min(1)
63287
+ ExperimentExportQuerySchema = exports_external.object({
63288
+ format: EvalResultsExportFormatSchema
63294
63289
  });
63290
+ EvalResultsExportQuerySchema = ExperimentExportQuerySchema;
63295
63291
  EvalResultExportRowSchema = exports_external.object({
63296
63292
  executionId: exports_external.string().min(1),
63297
63293
  exampleId: exports_external.string().min(1).nullable(),
@@ -63315,7 +63311,7 @@ var init_dataset_archive = __esm(() => {
63315
63311
  "dataset-too-large": "Datasets are capped at 500 MB. Split into multiple Append imports.",
63316
63312
  "execution-in-flight": "Wait for the in-flight execution to complete, or cancel it from the Experiments tab.",
63317
63313
  "download-token-expired": "Re-request the resource to mint a new URL.",
63318
- "legacy-dataset-format": "Re-export the dataset using the new folder convention (examples/<name>/input/arguments.json + input/<arg>/<file> + expected/output.json)."
63314
+ "legacy-dataset-format": "Re-export the dataset using the canonical automation convention (examples/<name>/input.json + input/<file> refs + expected.json)."
63319
63315
  };
63320
63316
  });
63321
63317
 
@@ -65041,6 +65037,7 @@ __export(exports_src, {
65041
65037
  ExtractOutputSchema: () => ExtractOutputSchema,
65042
65038
  ExtractInputSchema: () => ExtractInputSchema,
65043
65039
  ExtractConfigSchema: () => ExtractConfigSchema,
65040
+ ExperimentExportQuerySchema: () => ExperimentExportQuerySchema,
65044
65041
  ExpectedOutputSchema: () => ExpectedOutputSchema,
65045
65042
  ExpectedOutputFilesSchema: () => ExpectedOutputFilesSchema,
65046
65043
  ExpectedErrorSchema: () => ExpectedErrorSchema,
@@ -65062,7 +65059,6 @@ __export(exports_src, {
65062
65059
  EvalResultsExportQuerySchema: () => EvalResultsExportQuerySchema,
65063
65060
  EvalResultsExportJsonSchema: () => EvalResultsExportJsonSchema,
65064
65061
  EvalResultsExportFormatSchema: () => EvalResultsExportFormatSchema,
65065
- EvalResultsExportByBatchQuerySchema: () => EvalResultsExportByBatchQuerySchema,
65066
65062
  EvalResultExportRowSchema: () => EvalResultExportRowSchema,
65067
65063
  EvalOverridesSchema: () => EvalOverridesSchema,
65068
65064
  EvalExampleMetaSchema: () => EvalExampleMetaSchema,
@@ -186797,7 +186793,7 @@ import { fileURLToPath as fileURLToPath8 } from "url";
186797
186793
  // package.json
186798
186794
  var package_default = {
186799
186795
  name: "@eigenpal/cli",
186800
- version: "0.8.0",
186796
+ version: "0.9.0",
186801
186797
  description: "Eigenpal CLI — author and run document workflows from your terminal. Agent-ready.",
186802
186798
  keywords: [
186803
186799
  "eigenpal",
@@ -188897,6 +188893,52 @@ function resolveAgentPackagePath(target) {
188897
188893
  return resolvePackagePath(target);
188898
188894
  return SourcePackagePathSchema.parse(`agents/${target}`);
188899
188895
  }
188896
+ function publicAgentAutomationTarget(target) {
188897
+ if (target.includes("/")) {
188898
+ return pathToDottedPackageName(SourcePackagePathSchema.parse(target));
188899
+ }
188900
+ if (target.startsWith("agents."))
188901
+ return target;
188902
+ if (/^[a-z]+_[a-zA-Z0-9]/.test(target) || target.includes(" ") || target.includes(".")) {
188903
+ return target;
188904
+ }
188905
+ return `agents.${target}`;
188906
+ }
188907
+ function formatTriggerState(triggers2) {
188908
+ if (!triggers2)
188909
+ return "";
188910
+ return ["api", "email", "manual", "cron"].filter((key) => triggers2[key]).join(",");
188911
+ }
188912
+ async function findAgentAutomationBySearch(client, target) {
188913
+ const payload = await client.get("/api/v1/automations", {
188914
+ type: "agent",
188915
+ search: target,
188916
+ limit: "100"
188917
+ });
188918
+ const candidates = payload.data ?? [];
188919
+ return candidates.find((automation) => automation.id === target || automation.slug === target || automation.name === target) ?? (candidates.length === 1 ? candidates[0] : null);
188920
+ }
188921
+ async function getPublicAgentAutomation(client, target) {
188922
+ const publicTarget = publicAgentAutomationTarget(target);
188923
+ try {
188924
+ const automation = await client.get(`/api/v1/automations/${encodeURIComponent(publicTarget)}`);
188925
+ if (automation.type !== "agent") {
188926
+ throw new Error(`Automation ${target} is not an agent.`);
188927
+ }
188928
+ return automation;
188929
+ } catch (err) {
188930
+ if (!(err instanceof ApiError) || err.status !== 404)
188931
+ throw err;
188932
+ }
188933
+ const resolved = await findAgentAutomationBySearch(client, target);
188934
+ if (!resolved) {
188935
+ throw new Error(`Agent automation not found: ${target}`);
188936
+ }
188937
+ if (resolved.type !== "agent") {
188938
+ throw new Error(`Automation ${target} is not an agent.`);
188939
+ }
188940
+ return resolved;
188941
+ }
188900
188942
  function isAutomationPackagePath(packagePath) {
188901
188943
  return packagePath.startsWith("agents/") || packagePath.startsWith("workflows/");
188902
188944
  }
@@ -188934,7 +188976,7 @@ function bumpReleaseVersion(releases, bump) {
188934
188976
  }
188935
188977
  async function syncLatestAutomation(client, packagePath) {
188936
188978
  const automation = pathToDottedPackageName(packagePath);
188937
- await client.post(`/api/automations/${encodeURIComponent(automation)}/sync`);
188979
+ await client.post(`/api/v1/automations/${encodeURIComponent(automation)}/sync`);
188938
188980
  success2(`Synced ${automation} to latest release.`);
188939
188981
  }
188940
188982
  async function cloneSource(opts) {
@@ -188954,52 +188996,38 @@ async function cloneSource(opts) {
188954
188996
  success2(`Cloned ${repo.remoteUrl}`);
188955
188997
  }
188956
188998
  async function show(target, opts) {
188957
- const packagePath = resolveAgentPackagePath(target);
188958
- const slug = packagePath.replace(/^agents\//, "");
188959
188999
  const client = new ApiClient(resolveConfig(opts));
188960
- const payload = await client.get(`/api/v1/agents/${encodeURIComponent(slug)}`, {
188961
- include: "files,dataset,runs"
188962
- });
189000
+ const automation = await getPublicAgentAutomation(client, target);
188963
189001
  if (opts.json)
188964
- console.log(JSON.stringify(payload, null, 2));
189002
+ console.log(JSON.stringify(automation, null, 2));
188965
189003
  else {
188966
- const agent = payload.agent;
189004
+ const slug = automation.slug ?? target;
188967
189005
  console.log(table([
188968
189006
  {
188969
189007
  type: "agent",
188970
- slug: agent.slug ?? slug,
188971
- path: packagePath,
188972
- name: agent.name ?? "",
188973
- status: agent.status ?? "",
188974
- source: agent.sourceIntegrity ?? "",
188975
- latest: agent.latestVersion ?? "",
188976
- commit: agent.latestCommit ?? ""
189008
+ id: automation.id ?? "",
189009
+ slug,
189010
+ path: `agents/${slug}`,
189011
+ name: automation.name ?? "",
189012
+ status: automation.status ?? "",
189013
+ version: automation.version ?? "",
189014
+ triggers: formatTriggerState(automation.triggers),
189015
+ implementation: automation.implementationAvailable === false ? "missing" : "available"
188977
189016
  }
188978
189017
  ], [
188979
189018
  { key: "type", header: "type" },
189019
+ { key: "id", header: "id" },
188980
189020
  { key: "slug", header: "slug" },
188981
189021
  { key: "path", header: "path" },
188982
189022
  { key: "name", header: "name" },
188983
189023
  { key: "status", header: "status" },
188984
- { key: "source", header: "source" },
188985
- { key: "latest", header: "latest" },
188986
- { key: "commit", header: "commit", format: (value) => String(value || "-").slice(0, 12) }
189024
+ { key: "version", header: "version" },
189025
+ { key: "triggers", header: "triggers" },
189026
+ { key: "implementation", header: "implementation" }
188987
189027
  ]));
188988
- if (agent.description)
189028
+ if (automation.description)
188989
189029
  console.log(`
188990
- description: ${agent.description}`);
188991
- const runs = agent.recentRuns ?? agent.runs ?? [];
188992
- console.log(`
188993
- recent runs`);
188994
- console.log(table(runs.slice(0, 5), [
188995
- { key: "id", header: "id" },
188996
- { key: "createdAt", header: "time", format: (value) => formatTimestamp(value) },
188997
- { key: "durationMs", header: "duration", format: (value) => formatDuration(Number(value)) },
188998
- { key: "triggeredBy", header: "triggered-by" },
188999
- { key: "requestedVersion", header: "requested" },
189000
- { key: "resolvedVersion", header: "resolved" },
189001
- { key: "status", header: "status" }
189002
- ]));
189030
+ description: ${automation.description}`);
189003
189031
  }
189004
189032
  }
189005
189033
  async function versions2(target, opts) {
@@ -190931,40 +190959,13 @@ function renderBatchDiffHuman(diff) {
190931
190959
  process.stderr.write(`${ui.dim("mean Δ:")} ${meanDeltaText} ` + `${ui.dim("examples shared:")} ${diff.summary.sharedExamples} ` + `${ui.dim("only in A:")} ${diff.summary.onlyInA.length} ` + `${ui.dim("only in B:")} ${diff.summary.onlyInB.length}
190932
190960
  `);
190933
190961
  }
190934
- async function fetchEvalResults(client, batchId) {
190935
- const runs = await listBatchRuns(client, batchId);
190936
- const results = [];
190937
- for (const run of runs) {
190938
- const payload = await client.get(`/api/v1/runs/${encodeURIComponent(run.id)}/eval-results`);
190939
- for (const result of payload.results ?? []) {
190940
- results.push({
190941
- exampleId: run.exampleId,
190942
- exampleName: run.exampleName,
190943
- evaluatorName: String(result.evaluatorName ?? ""),
190944
- score: typeof result.score === "number" ? result.score : null
190945
- });
190946
- }
190962
+ async function fetchEvalResults(client, experimentId) {
190963
+ const ref = await client.get(`/api/v1/experiments/${encodeURIComponent(experimentId)}`);
190964
+ const raw = await client.get(`/api/v1/automations/${encodeURIComponent(ref.automationId)}/experiments/${encodeURIComponent(experimentId)}/export`, { format: "json" });
190965
+ if (!raw || !Array.isArray(raw.results)) {
190966
+ throw new Error(`Unexpected experiment export response for ${experimentId}: missing 'results' array`);
190947
190967
  }
190948
- return { results };
190949
- }
190950
- async function listBatchRuns(client, batchId) {
190951
- const out = [];
190952
- let cursor;
190953
- do {
190954
- const query = { batchId, limit: "100" };
190955
- if (cursor)
190956
- query.cursor = cursor;
190957
- const page = await client.get("/api/v1/runs", query);
190958
- for (const run of page.runs ?? []) {
190959
- out.push({
190960
- id: run.id,
190961
- exampleId: run.eval?.exampleId ?? null,
190962
- exampleName: run.eval?.example ?? null
190963
- });
190964
- }
190965
- cursor = page.nextCursor ?? undefined;
190966
- } while (cursor);
190967
- return out;
190968
+ return raw;
190968
190969
  }
190969
190970
 
190970
190971
  // src/commands/agents/experiments.ts
@@ -190974,7 +190975,7 @@ function registerExperimentCommands(agent) {
190974
190975
  process.exit(2);
190975
190976
  });
190976
190977
  addJsonFlag(withBaseUrl(experiment.command("run <agent-id-or-slug>"))).description("Start an experiment over dataset examples.").option("--example-id <id>", "Run one dataset example").option("--wait", "Poll until the experiment reaches a terminal status").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).action(action(runExperiment));
190977
- addJsonFlag(withBaseUrl(experiment.command("status <agent-id-or-slug> <batch-id>"))).description("Get experiment status.").option("--watch", "Poll until complete").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).option("--max-wait <seconds>", "Maximum wait before exiting 2", intArg, 1800).option("--include <parts>", "Reserved for future detailed parts").action(action(experimentStatus));
190978
+ addJsonFlag(withBaseUrl(experiment.command("status <agent-id-or-slug> <batch-id>"))).description("Get experiment status.").option("--watch", "Poll until complete").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).option("--max-wait <seconds>", "Maximum wait before exit code 2", intArg, 1800).option("--include <parts>", "Reserved for future detailed parts").action(action(experimentStatus));
190978
190979
  withBaseUrl(experiment.command("results <agent-id-or-slug> [batch-id]")).description("Print experiment results as JSON or CSV.").requiredOption("--format <csv|json>", "Output format", parseResultsFormat).option("--out <path>", "Write output to file").action(action(experimentResults));
190979
190980
  addJsonFlag(withPagination(withBaseUrl(experiment.command("list <agent-id-or-slug>")), 50)).description("List experiments.").option("--batch-id <id>", "Filter to one batch id").action(action(listExperiments));
190980
190981
  addJsonFlag(withBaseUrl(experiment.command("compare <batch-id-a> <batch-id-b>"))).description("Diff eval scores between two experiment batches.").option("--sort <abs-delta-desc|delta-asc|delta-desc|name>", "Row sort order (default: biggest movers first)", "abs-delta-desc").option("--regression-threshold <n>", "Δ below this is flagged as a regression (default 0.05)", Number.parseFloat, 0.05).action(action(compareExperiments));
@@ -191078,8 +191079,10 @@ function registerAgentFileCommands(agent) {
191078
191079
  }
191079
191080
  async function listAgentFiles(agentId, opts) {
191080
191081
  const client = buildClient(opts);
191081
- const payload = await client.get(`/api/v1/agents/${encodeURIComponent(agentId)}/files`, {
191082
- ...opts.path ? { prefix: opts.path } : {}
191082
+ const packagePath = await resolveAgentPackagePath2(client, agentId);
191083
+ const payload = await client.get("/api/v1/source/tree", {
191084
+ packagePath,
191085
+ ...opts.path ? { prefix: normalizeAgentFilePath(opts.path, { allowEmpty: true }) } : {}
191083
191086
  });
191084
191087
  if (opts.json)
191085
191088
  return printJson(payload);
@@ -191087,9 +191090,7 @@ async function listAgentFiles(agentId, opts) {
191087
191090
  }
191088
191091
  async function getAgentFile(agentId, remotePath, opts) {
191089
191092
  const client = buildClient(opts);
191090
- const payload = await client.get(`/api/v1/agents/${encodeURIComponent(agentId)}/files`, {
191091
- path: remotePath
191092
- });
191093
+ const payload = await readAgentFile(client, agentId, remotePath);
191093
191094
  if (opts.json)
191094
191095
  return printJson(payload);
191095
191096
  const out = path10.resolve(opts.out ?? payload.path);
@@ -191106,11 +191107,9 @@ async function buildAgentFileDiff(agentId, remotePath, localPath, opts) {
191106
191107
  const client = buildClient(opts);
191107
191108
  const local = await fs6.readFile(localPath);
191108
191109
  let remote = null;
191109
- let serverPath = remotePath;
191110
+ let serverPath = normalizeAgentFilePath(remotePath);
191110
191111
  try {
191111
- const payload = await client.get(`/api/v1/agents/${encodeURIComponent(agentId)}/files`, {
191112
- path: remotePath
191113
- });
191112
+ const payload = await readAgentFile(client, agentId, remotePath);
191114
191113
  serverPath = String(payload.path ?? remotePath);
191115
191114
  remote = Buffer.from(String(payload.contentBase64 ?? ""), "base64");
191116
191115
  } catch (err2) {
@@ -191132,6 +191131,34 @@ async function buildAgentFileDiff(agentId, remotePath, localPath, opts) {
191132
191131
  textPreview: remote && isLikelyText(local) && isLikelyText(remote) ? buildTextDiffPreview(remote.toString("utf-8"), local.toString("utf-8")) : null
191133
191132
  };
191134
191133
  }
191134
+ async function resolveAgentPackagePath2(client, agentId) {
191135
+ const automation = await client.get(`/api/v1/automations/${encodeURIComponent(agentAutomationId(agentId))}`);
191136
+ if (automation.type !== "agent" || !automation.slug) {
191137
+ throw new Error(`Agent not found: ${agentId}`);
191138
+ }
191139
+ return `agents/${automation.slug}`;
191140
+ }
191141
+ async function readAgentFile(client, agentId, remotePath) {
191142
+ const packagePath = await resolveAgentPackagePath2(client, agentId);
191143
+ const relativePath = normalizeAgentFilePath(remotePath);
191144
+ const sourcePath = `${packagePath}/${relativePath}`;
191145
+ const payload = await client.get("/api/v1/source/raw", {
191146
+ path: sourcePath
191147
+ });
191148
+ return {
191149
+ path: relativePath,
191150
+ contentBase64: Buffer.from(payload.content).toString("base64"),
191151
+ contentType: payload.contentType
191152
+ };
191153
+ }
191154
+ function normalizeAgentFilePath(filePath, options = {}) {
191155
+ const raw = filePath.trim();
191156
+ const trimmed = raw.replace(/^agent\/+/, "").replace(/^\/+|\/+$/g, "");
191157
+ if (!trimmed && !options.allowEmpty || raw !== filePath || trimmed.includes("\x00") || trimmed.includes("\\") || trimmed.includes("..") || trimmed !== "" && trimmed.split("/").some((part) => part === "")) {
191158
+ throw new Error("Invalid agent file path");
191159
+ }
191160
+ return trimmed;
191161
+ }
191135
191162
  function renderAgentFileDiff(report) {
191136
191163
  const label = `File ${report.status}`;
191137
191164
  if (report.status === "match")
@@ -191259,7 +191286,7 @@ function levenshtein(a, b2, maxDistance) {
191259
191286
  }
191260
191287
  async function findSimilarSlug(client, slug) {
191261
191288
  try {
191262
- const res = await client.get("/api/v1/workflows", {
191289
+ const res = await client.get("/api/workflows", {
191263
191290
  search: slug,
191264
191291
  limit: "20"
191265
191292
  });
@@ -191284,13 +191311,13 @@ function notFound(idOrSlug, suggestion) {
191284
191311
  async function resolveWorkflowId(client, idOrSlug) {
191285
191312
  if (looksLikeWorkflowId(idOrSlug)) {
191286
191313
  try {
191287
- const wf = await client.get(`/api/v1/workflows/${idOrSlug}`);
191314
+ const wf = await client.get(`/api/workflows/${idOrSlug}`);
191288
191315
  return wf.id;
191289
191316
  } catch {
191290
191317
  throw notFound(idOrSlug, null);
191291
191318
  }
191292
191319
  }
191293
- const res = await client.get("/api/v1/workflows", { name: idOrSlug });
191320
+ const res = await client.get("/api/workflows", { name: idOrSlug });
191294
191321
  const match = res.data?.[0];
191295
191322
  if (!match) {
191296
191323
  const suggestion = await findSimilarSlug(client, idOrSlug);
@@ -191428,7 +191455,7 @@ function registerRunsCommands(program2) {
191428
191455
  listRunsCmd.action(action(listRuns));
191429
191456
  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("--expand <sections>", "Comma-separated server expand sections for GET /api/v1/runs/:id: input, usage, execution, debug. Use execution for status/schemaValid; completed runs include top-level output/files/error.").option("--include <parts>", "Comma-separated local workflow step projection fields (error, duration, inputRef, …). Legacy expand names are mapped when possible; use --expand for server sections.", "feedback").action(action(getRun));
191430
191457
  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));
191431
- addJsonFlag(withBaseUrl(runs.command("rerun <run-id>"))).description("Create a new run from a previous run's stored input snapshot.").option("--version <version>", "Version/source ref for the new run: latest (default), original, or an explicit ref (same grammar as eigenpal run)").option("--source-ref <ref>", "Alias for --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));
191458
+ addJsonFlag(withBaseUrl(runs.command("rerun <run-id>"))).description("Create a new run from a previous run's stored input snapshot.").option("--version <version>", "Version/source ref for the new run: latest (default), original, or an explicit ref (same grammar as eigenpal run)").option("--source-ref <ref>", "Alias for --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 exit code 2", intArg, 1800).action(action(rerunRun));
191432
191459
  addJsonFlag(withBaseUrl(runs.command("promote <run-id>"))).description("Promote a run into a dataset example on the run's automation.").option("--name <name>", "Name for the created example").addHelpText("after", `
191433
191460
  Examples
191434
191461
  $ eigenpal runs promote run_abc123 --name golden-invoice
@@ -191446,7 +191473,7 @@ Use \`eigenpal runs feedback update\` first when you need to correct expected ou
191446
191473
  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));
191447
191474
  registerRunFeedbackCommands(runs);
191448
191475
  registerRunExpectedCommands(runs);
191449
- 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));
191476
+ 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 exit code 2", intArg, 1800).action(action(watchRunCommand));
191450
191477
  addJsonFlag(withBaseUrl(runs.command("cancel <run-id>"))).description("Cancel a run.").option("--yes", "Required in non-interactive environments").action(action(cancelRun));
191451
191478
  }
191452
191479
  function registerRunFeedbackCommands(runs) {
@@ -192251,12 +192278,12 @@ async function uploadRunExpected(executionId, file2, opts) {
192251
192278
  form.append("file", new Blob([data]), path11.basename(file2));
192252
192279
  if (opts.name)
192253
192280
  form.append("name", opts.name);
192254
- const payload = await client.postFormData(`/api/v1/runs/${encodeURIComponent(executionId)}/expected`, form);
192281
+ const payload = await client.postFormData(`/api/v1/runs/${encodeURIComponent(executionId)}/feedback/expected`, form);
192255
192282
  renderGeneric(payload, opts, `Uploaded expected file for ${executionId}`);
192256
192283
  }
192257
192284
  async function copyOutputToExpected(executionId, outputFile, opts) {
192258
192285
  const client = buildClient(opts);
192259
- const payload = await client.post(`/api/v1/runs/${encodeURIComponent(executionId)}/expected`, {
192286
+ const payload = await client.post(`/api/v1/runs/${encodeURIComponent(executionId)}/feedback/expected`, {
192260
192287
  outputFileName: outputFile,
192261
192288
  ...opts.name ? { expectedName: opts.name } : {}
192262
192289
  });
@@ -192264,7 +192291,7 @@ async function copyOutputToExpected(executionId, outputFile, opts) {
192264
192291
  }
192265
192292
  async function renameRunExpected(executionId, oldName, newName, opts) {
192266
192293
  const client = buildClient(opts);
192267
- const payload = await client.patch(`/api/v1/runs/${encodeURIComponent(executionId)}/expected/${encodeURIComponent(oldName)}`, { name: newName });
192294
+ const payload = await client.patch(`/api/v1/runs/${encodeURIComponent(executionId)}/feedback/expected/${encodeURIComponent(oldName)}`, { name: newName });
192268
192295
  renderGeneric(payload, opts, `Renamed expected file for ${executionId}`);
192269
192296
  }
192270
192297
  async function deleteRunExpected(executionId, name, opts) {
@@ -192272,7 +192299,7 @@ async function deleteRunExpected(executionId, name, opts) {
192272
192299
  throw new Error("Delete cancelled");
192273
192300
  }
192274
192301
  const client = buildClient(opts);
192275
- await client.delete(`/api/v1/runs/${encodeURIComponent(executionId)}/expected/${encodeURIComponent(name)}`);
192302
+ await client.delete(`/api/v1/runs/${encodeURIComponent(executionId)}/feedback/expected/${encodeURIComponent(name)}`);
192276
192303
  renderGeneric({ ok: true }, opts, `Deleted expected file ${name}`);
192277
192304
  }
192278
192305
  async function watchRunCommand(executionId, opts) {
@@ -192557,7 +192584,10 @@ function registerAgentCommands(program2) {
192557
192584
  }
192558
192585
  async function listAgents(opts) {
192559
192586
  const client = buildClient(opts);
192560
- const payload = await client.get("/api/v1/agents", compactParams(opts));
192587
+ const payload = await client.get("/api/v1/automations", {
192588
+ ...compactParams(opts),
192589
+ type: "agent"
192590
+ });
192561
192591
  if (opts.json)
192562
192592
  return printJson(payload);
192563
192593
  console.log(table(payload.data, [
@@ -193470,32 +193500,69 @@ function getExampleNames(dir, exampleNamesFilter) {
193470
193500
  if (!baseDir) {
193471
193501
  throw new Error(`No dataset/examples directory found at ${join8(dir, ...DATASET_EXAMPLES_DIR)}. Run \`eigenpal init workflow <name>\` to scaffold one.`);
193472
193502
  }
193473
- const all = readdirSync4(baseDir).filter((name) => statSync4(join8(baseDir, name)).isDirectory() && existsSync14(join8(baseDir, name, "input", "arguments.json"))).sort((a5, b8) => a5.localeCompare(b8, undefined, { numeric: true }));
193503
+ const all = readdirSync4(baseDir).filter((name) => statSync4(join8(baseDir, name)).isDirectory() && existsSync14(join8(baseDir, name, "input.json"))).sort((a5, b8) => a5.localeCompare(b8, undefined, { numeric: true }));
193474
193504
  if (!exampleNamesFilter?.length)
193475
193505
  return all;
193476
193506
  return all.filter((n5) => exampleNamesFilter.includes(n5));
193477
193507
  }
193478
193508
  function readInput(exampleDir) {
193479
- const argsPath = join8(exampleDir, "input", "arguments.json");
193480
- const inputDir = join8(exampleDir, "input");
193481
- const scalars = JSON.parse(readFileSync13(argsPath, "utf-8"));
193509
+ const canonicalPath = join8(exampleDir, "input.json");
193510
+ return readCanonicalInput(exampleDir, canonicalPath);
193511
+ }
193512
+ function readCanonicalInput(exampleDir, inputPath) {
193513
+ const raw = JSON.parse(readFileSync13(inputPath, "utf-8"));
193514
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
193515
+ throw new Error(`${inputPath} must contain a JSON object.`);
193516
+ }
193517
+ const scalars = {};
193482
193518
  const files = [];
193483
- for (const entry of readdirSync4(inputDir)) {
193484
- const full = join8(inputDir, entry);
193485
- if (!statSync4(full).isDirectory())
193519
+ for (const [key2, value] of Object.entries(raw)) {
193520
+ const refs = topLevelFileRefs(value);
193521
+ if (refs.length === 0) {
193522
+ scalars[key2] = value;
193486
193523
  continue;
193487
- const filenames = readdirSync4(full).filter((f7) => statSync4(join8(full, f7)).isFile()).sort();
193488
- for (const filename of filenames) {
193524
+ }
193525
+ for (const ref of refs) {
193526
+ const relative3 = parseDatasetInputFileRef(ref);
193527
+ if (!relative3) {
193528
+ throw new Error(`${inputPath}: ${key2} must reference files under input/ without path traversal.`);
193529
+ }
193530
+ const filePath = join8(exampleDir, "input", relative3);
193531
+ const filename = relative3.split("/").at(-1) ?? relative3;
193489
193532
  files.push({
193490
- argument: entry,
193533
+ argument: key2,
193491
193534
  filename,
193492
- content: readFileSync13(join8(full, filename)),
193535
+ content: readFileSync13(filePath),
193493
193536
  mimeType: guessMimeType(filename)
193494
193537
  });
193495
193538
  }
193496
193539
  }
193497
193540
  return { scalars, files };
193498
193541
  }
193542
+ function topLevelFileRefs(value) {
193543
+ if (isFileRef(value))
193544
+ return [value.$file];
193545
+ if (Array.isArray(value) && value.every(isFileRef))
193546
+ return value.map((item) => item.$file);
193547
+ if (Array.isArray(value) && value.some(isFileRef)) {
193548
+ throw new Error('file arrays must contain only { "$file": "input/..." } entries.');
193549
+ }
193550
+ return [];
193551
+ }
193552
+ function isFileRef(value) {
193553
+ return value !== null && typeof value === "object" && !Array.isArray(value) && typeof value.$file === "string";
193554
+ }
193555
+ function parseDatasetInputFileRef(path15) {
193556
+ if (!path15.startsWith("input/"))
193557
+ return null;
193558
+ if (path15.startsWith("/") || path15.includes("\\") || path15.includes("\x00"))
193559
+ return null;
193560
+ const relative3 = path15.slice("input/".length);
193561
+ const parts = relative3.split("/");
193562
+ if (parts.some((part) => !part || part === "." || part === ".."))
193563
+ return null;
193564
+ return parts.join("/");
193565
+ }
193499
193566
  function readOverrides(exampleDir) {
193500
193567
  const metaPath = join8(exampleDir, "meta.json");
193501
193568
  if (!existsSync14(metaPath))
@@ -193772,8 +193839,8 @@ async function runSavedWorkflowExamples(workflow2, examples, opts) {
193772
193839
 
193773
193840
  // src/commands/run.ts
193774
193841
  function registerRunCommands(program2) {
193775
- addJsonFlag(withBaseUrl(program2.command("run <target>"))).description("Start a workflow or agent run, e.g. workflows.extract-invoice.").option("--input-json <json>", "JSON input object").option("--input-file <field=path>", 'Input file to upload as multipart form-data. Repeat for multiple files; bare paths use field "file".', collectRepeated, []).option("--example <name>", "Run one persisted example by name").option("--dir <dir>", "Local eigenpal directory for workflow examples", undefined).option("--wait", "Poll until the run reaches a terminal status").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).option("--max-wait <seconds>", "Maximum wait before exiting 2", intArg, 1800).action(action(runTarget));
193776
- addJsonFlag(withBaseUrl(program2.command("rerun <run-id>"))).description("Create a new run from a previous run's stored input snapshot.").option("--version <version>", "Version/source ref for the new run: latest, original, or an explicit ref", "latest").option("--wait", "Poll until the rerun reaches a terminal status").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).option("--max-wait <seconds>", "Maximum wait before exiting 2", intArg, 1800).action(action(rerunTarget));
193842
+ addJsonFlag(withBaseUrl(program2.command("run <target>"))).description("Start a workflow or agent run, e.g. workflows.extract-invoice.").option("--input-json <json>", "JSON input object").option("--input-file <field=path>", 'Input file to upload as multipart form-data. Repeat for multiple files; bare paths use field "file".', collectRepeated, []).option("--example <name>", "Run one persisted example by name").option("--dir <dir>", "Local eigenpal directory for workflow examples", undefined).option("--wait", "Poll until the run reaches a terminal status").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).option("--max-wait <seconds>", "Maximum wait before exit code 2", intArg, 1800).action(action(runTarget));
193843
+ addJsonFlag(withBaseUrl(program2.command("rerun <run-id>"))).description("Create a new run from a previous run's stored input snapshot.").option("--version <version>", "Version/source ref for the new run: latest, original, or an explicit ref", "latest").option("--wait", "Poll until the rerun reaches a terminal status").option("--interval <seconds>", "Polling interval in seconds", intArg, 2).option("--max-wait <seconds>", "Maximum wait before exit code 2", intArg, 1800).action(action(rerunTarget));
193777
193844
  }
193778
193845
  async function runTarget(target, opts) {
193779
193846
  const parsed = parseRunTarget(target);
@@ -194366,8 +194433,8 @@ async function status(opts) {
194366
194433
  }
194367
194434
  process.exit(1);
194368
194435
  }
194369
- const workflows = await fetchOrNull(client, "/api/v1/workflows?limit=1");
194370
- const workflowCount = typeof workflows?.total === "number" ? workflows.total : Array.isArray(workflows?.items) ? workflows.items.length : null;
194436
+ const workflows = await fetchOrNull(client, "/api/workflows?limit=1");
194437
+ const workflowCount = typeof workflows?.total === "number" ? workflows.total : Array.isArray(workflows?.data) ? workflows.data.length : Array.isArray(workflows?.items) ? workflows.items.length : null;
194371
194438
  if (opts.json) {
194372
194439
  console.log(JSON.stringify({
194373
194440
  authenticated: true,
@@ -195230,122 +195297,60 @@ function validateDatasetFolder(root2) {
195230
195297
  });
195231
195298
  continue;
195232
195299
  }
195233
- const argsPath = join13(exampleFolder, "input", "arguments.json");
195234
- if (!existsSync17(argsPath)) {
195300
+ const inputJsonPath = join13(exampleFolder, "input.json");
195301
+ const hasInputJson = existsSync17(inputJsonPath);
195302
+ if (!hasInputJson) {
195235
195303
  issues.push({
195236
- field: `examples/${exampleName}/input/arguments.json`,
195237
- message: "Missing required arguments.json."
195304
+ field: `examples/${exampleName}/input.json`,
195305
+ message: "Missing required input.json."
195238
195306
  });
195239
195307
  continue;
195240
195308
  }
195241
195309
  let args;
195242
195310
  try {
195243
- const parsed = JSON.parse(readFileSync15(argsPath, "utf-8"));
195311
+ const parsed = JSON.parse(readFileSync15(inputJsonPath, "utf-8"));
195244
195312
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
195245
195313
  issues.push({
195246
- field: `examples/${exampleName}/input/arguments.json`,
195247
- message: "arguments.json must be a JSON object."
195314
+ field: `examples/${exampleName}/input.json`,
195315
+ message: "input.json must be a JSON object."
195248
195316
  });
195249
195317
  continue;
195250
195318
  }
195251
195319
  args = parsed;
195252
195320
  } catch {
195253
195321
  issues.push({
195254
- field: `examples/${exampleName}/input/arguments.json`,
195255
- message: "arguments.json is not valid JSON."
195322
+ field: `examples/${exampleName}/input.json`,
195323
+ message: "input.json is not valid JSON."
195256
195324
  });
195257
195325
  continue;
195258
195326
  }
195259
195327
  const inputDir = join13(exampleFolder, "input");
195260
- for (const entry of readdirSync7(inputDir)) {
195261
- if (entry === "arguments.json")
195262
- continue;
195263
- const sub = join13(inputDir, entry);
195264
- const field = `examples/${exampleName}/input/${entry}`;
195265
- let isDir;
195266
- try {
195267
- isDir = statSync6(sub).isDirectory();
195268
- } catch (err2) {
195269
- issues.push({
195270
- field,
195271
- message: `unreadable filesystem entry: ${err2 instanceof Error ? err2.message : String(err2)}`
195272
- });
195273
- continue;
195274
- }
195275
- if (!isDir) {
195276
- issues.push({
195277
- field,
195278
- message: "Entries under input/ must live inside an argument folder."
195279
- });
195280
- continue;
195281
- }
195282
- if (!NAME_RE.test(entry)) {
195283
- issues.push({
195284
- field: `examples/${exampleName}/input/${entry}`,
195285
- message: "Argument folder name must be lowercase kebab/snake-case."
195286
- });
195287
- }
195288
- if (Object.prototype.hasOwnProperty.call(args, entry)) {
195289
- issues.push({
195290
- field: `examples/${exampleName}/input/${entry}`,
195291
- message: `Argument-name collision: "${entry}" appears in both arguments.json and as an input/ folder.`
195292
- });
195293
- }
195328
+ if (existsSync17(join13(inputDir, "arguments.json"))) {
195329
+ issues.push({
195330
+ field: `examples/${exampleName}/input/arguments.json`,
195331
+ message: "Legacy input/arguments.json is no longer accepted. Use input.json instead."
195332
+ });
195333
+ }
195334
+ const fileRefs = collectDatasetFileRefs(args);
195335
+ for (const issue2 of validateFileRefs({
195336
+ refs: fileRefs,
195337
+ filesRoot: inputDir,
195338
+ root: "input",
195339
+ fieldPrefix: `examples/${exampleName}/input.json`
195340
+ })) {
195341
+ issues.push(issue2);
195294
195342
  }
195295
195343
  const expectedDir = join13(exampleFolder, "expected");
195344
+ const expectedJsonPath = join13(exampleFolder, "expected.json");
195296
195345
  if (existsSync17(expectedDir)) {
195297
- const expectedPath = join13(expectedDir, "output.json");
195298
- const expectedErrorPath = join13(expectedDir, "error.json");
195299
- const hasExpectedOutput = existsSync17(expectedPath);
195300
- const hasExpectedError = existsSync17(expectedErrorPath);
195301
- if (hasExpectedOutput && hasExpectedError) {
195302
- issues.push({
195303
- field: `examples/${exampleName}/expected`,
195304
- message: "expected/output.json and expected/error.json are mutually exclusive (pick success-expected OR failure-expected)."
195305
- });
195306
- }
195307
- if (hasExpectedOutput) {
195308
- let parsed;
195309
- try {
195310
- parsed = JSON.parse(readFileSync15(expectedPath, "utf-8"));
195311
- } catch {
195312
- issues.push({
195313
- field: `examples/${exampleName}/expected/output.json`,
195314
- message: "expected/output.json is not valid JSON."
195315
- });
195316
- continue;
195317
- }
195318
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
195319
- issues.push({
195320
- field: `examples/${exampleName}/expected/output.json`,
195321
- message: "expected/output.json must be a JSON object (mirrors the workflow `output:` shape)."
195322
- });
195323
- }
195324
- }
195325
- if (hasExpectedError) {
195326
- let parsed;
195327
- try {
195328
- parsed = JSON.parse(readFileSync15(expectedErrorPath, "utf-8"));
195329
- } catch {
195346
+ for (const entry of readdirSync7(expectedDir)) {
195347
+ if (entry === "output.json" || entry === "error.json") {
195330
195348
  issues.push({
195331
- field: `examples/${exampleName}/expected/error.json`,
195332
- message: "expected/error.json is not valid JSON."
195349
+ field: `examples/${exampleName}/expected/${entry}`,
195350
+ message: "Legacy expected files are no longer accepted. Use expected.json instead."
195333
195351
  });
195334
195352
  continue;
195335
195353
  }
195336
- const result = ExpectedErrorSchema.safeParse(parsed);
195337
- if (!result.success) {
195338
- for (const err2 of result.error.issues) {
195339
- issues.push({
195340
- field: `examples/${exampleName}/expected/error.json${err2.path.length > 0 ? `:${err2.path.join(".")}` : ""}`,
195341
- message: err2.message
195342
- });
195343
- }
195344
- }
195345
- }
195346
- for (const entry of readdirSync7(expectedDir)) {
195347
- if (entry === "output.json" || entry === "error.json")
195348
- continue;
195349
195354
  const sub = join13(expectedDir, entry);
195350
195355
  const field = `examples/${exampleName}/expected/${entry}`;
195351
195356
  let isDir;
@@ -195361,7 +195366,7 @@ function validateDatasetFolder(root2) {
195361
195366
  if (!isDir) {
195362
195367
  issues.push({
195363
195368
  field,
195364
- message: "Entries under expected/ must be `output.json`, `error.json`, or a doc folder (`expected/<docKey>/<file>`)."
195369
+ message: 'Entries under expected/ must be file folders referenced from expected.json with { "$file": "expected/..." }.'
195365
195370
  });
195366
195371
  continue;
195367
195372
  }
@@ -195373,6 +195378,43 @@ function validateDatasetFolder(root2) {
195373
195378
  }
195374
195379
  }
195375
195380
  }
195381
+ if (existsSync17(expectedJsonPath)) {
195382
+ let parsed;
195383
+ try {
195384
+ parsed = JSON.parse(readFileSync15(expectedJsonPath, "utf-8"));
195385
+ } catch {
195386
+ issues.push({
195387
+ field: `examples/${exampleName}/expected.json`,
195388
+ message: "expected.json is not valid JSON."
195389
+ });
195390
+ continue;
195391
+ }
195392
+ if (isExpectedErrorRef(parsed)) {
195393
+ const result = ExpectedErrorSchema.safeParse(parsed.$error);
195394
+ if (!result.success) {
195395
+ for (const err2 of result.error.issues) {
195396
+ issues.push({
195397
+ field: `examples/${exampleName}/expected.json:$error${err2.path.length > 0 ? `.${err2.path.join(".")}` : ""}`,
195398
+ message: err2.message
195399
+ });
195400
+ }
195401
+ }
195402
+ } else if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
195403
+ issues.push({
195404
+ field: `examples/${exampleName}/expected.json`,
195405
+ message: "expected.json must be a JSON object."
195406
+ });
195407
+ } else {
195408
+ for (const issue2 of validateFileRefs({
195409
+ refs: collectDatasetFileRefs(parsed),
195410
+ filesRoot: join13(exampleFolder, "expected"),
195411
+ root: "expected",
195412
+ fieldPrefix: `examples/${exampleName}/expected.json`
195413
+ })) {
195414
+ issues.push(issue2);
195415
+ }
195416
+ }
195417
+ }
195376
195418
  }
195377
195419
  return issues;
195378
195420
  }
@@ -195390,6 +195432,54 @@ function printIssues(label2, path15, issues) {
195390
195432
  console.error("");
195391
195433
  return false;
195392
195434
  }
195435
+ function isFileRef2(value) {
195436
+ return value !== null && typeof value === "object" && !Array.isArray(value) && typeof value.$file === "string";
195437
+ }
195438
+ function isExpectedErrorRef(value) {
195439
+ return value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 1 && Object.prototype.hasOwnProperty.call(value, "$error");
195440
+ }
195441
+ function collectDatasetFileRefs(value, jsonPath = "<root>") {
195442
+ if (isFileRef2(value))
195443
+ return [{ path: value.$file, jsonPath }];
195444
+ if (Array.isArray(value)) {
195445
+ return value.flatMap((item, index) => collectDatasetFileRefs(item, `${jsonPath}.${index}`));
195446
+ }
195447
+ if (value && typeof value === "object") {
195448
+ return Object.entries(value).flatMap(([key2, item]) => collectDatasetFileRefs(item, jsonPath === "<root>" ? key2 : `${jsonPath}.${key2}`));
195449
+ }
195450
+ return [];
195451
+ }
195452
+ function validateFileRefs(args) {
195453
+ const issues = [];
195454
+ for (const ref of args.refs) {
195455
+ const relative4 = parseDatasetFileRef(ref.path, args.root);
195456
+ if (!relative4) {
195457
+ issues.push({
195458
+ field: `${args.fieldPrefix}:${ref.jsonPath}`,
195459
+ message: `File reference must point inside ${args.root}/ and must not contain path traversal.`
195460
+ });
195461
+ continue;
195462
+ }
195463
+ if (!existsSync17(join13(args.filesRoot, relative4))) {
195464
+ issues.push({
195465
+ field: `${args.fieldPrefix}:${ref.jsonPath}`,
195466
+ message: `Referenced file does not exist: ${ref.path}.`
195467
+ });
195468
+ }
195469
+ }
195470
+ return issues;
195471
+ }
195472
+ function parseDatasetFileRef(path15, root2) {
195473
+ if (!path15.startsWith(`${root2}/`))
195474
+ return null;
195475
+ if (path15.startsWith("/") || path15.includes("\\") || path15.includes("\x00"))
195476
+ return null;
195477
+ const relative4 = path15.slice(root2.length + 1);
195478
+ const parts = relative4.split("/");
195479
+ if (parts.some((part) => !part || part === "." || part === ".."))
195480
+ return null;
195481
+ return parts.join("/");
195482
+ }
195393
195483
  function rootDir(opts) {
195394
195484
  return resolve6(opts.dir ?? process.cwd());
195395
195485
  }
@@ -195577,7 +195667,7 @@ function registerWorkflowCoreCommands(parent) {
195577
195667
  };
195578
195668
  if (opts.search)
195579
195669
  params.search = opts.search;
195580
- const raw = await client.get("/api/v1/workflows", params);
195670
+ const raw = await client.get("/api/workflows", params);
195581
195671
  renderListResult(raw, [
195582
195672
  { key: "id", header: "id" },
195583
195673
  { key: "name", header: "name", format: (v11) => typeof v11 === "string" ? v11 : "-" },
@@ -195588,7 +195678,7 @@ function registerWorkflowCoreCommands(parent) {
195588
195678
  const pullCmd = parent.command("pull <workflow-id>").description(descriptionFor("workflow_get_definition")).option("--out <path>", "Write YAML to file instead of stdout");
195589
195679
  withBaseUrl(pullCmd).action(action(async (workflow2, opts) => {
195590
195680
  const { client, workflowId } = await buildClientForWorkflow(workflow2, opts);
195591
- const result = await client.get(`/api/v1/workflows/${workflowId}`);
195681
+ const result = await client.get(`/api/workflows/${workflowId}`);
195592
195682
  if (typeof result?.yamlContent !== "string" || result.yamlContent.length === 0) {
195593
195683
  throw new Error(`Workflow ${workflowId} has no published version yet, nothing to pull. Run \`eigenpal workflow push\` first.`);
195594
195684
  }
@@ -195659,7 +195749,7 @@ flag would otherwise shadow it. Validation failures come back as
195659
195749
  if (opts.setVersion) {
195660
195750
  nextVersion = opts.setVersion;
195661
195751
  } else if (opts.bump) {
195662
- const wf3 = await client.get(`/api/v1/workflows/${workflowId}`);
195752
+ const wf3 = await client.get(`/api/workflows/${workflowId}`);
195663
195753
  const current = wf3?.version;
195664
195754
  if (!current) {
195665
195755
  throw new Error(`Cannot --bump ${opts.bump} on ${workflowId}: server has no current version. Push an initial version (with \`--set-version 1.0.0\` or a YAML \`version:\` field) first.`);
@@ -195676,7 +195766,7 @@ flag would otherwise shadow it. Validation failures come back as
195676
195766
  process.exit(1);
195677
195767
  }
195678
195768
  const yamlToSend = cliWantsRewrite ? spliceWorkflowVersion2(yamlOnDisk, nextVersion) : yamlOnDisk;
195679
- const result = await client.post(`/api/v1/workflows/${workflowId}/versions`, {
195769
+ const result = await client.post(`/api/workflows/${workflowId}/versions`, {
195680
195770
  yaml: yamlToSend,
195681
195771
  version: nextVersion
195682
195772
  });
@@ -195692,7 +195782,7 @@ flag would otherwise shadow it. Validation failures come back as
195692
195782
  }
195693
195783
  const yamlToSend = opts.setVersion && !extractWorkflowVersion(yamlOnDisk) ? spliceWorkflowVersion2(yamlOnDisk, opts.setVersion) : yamlOnDisk;
195694
195784
  const createVersion = opts.setVersion ?? extractWorkflowVersion(yamlToSend) ?? undefined;
195695
- const result = await client.post("/api/v1/workflows", {
195785
+ const result = await client.post("/api/workflows", {
195696
195786
  yaml: yamlToSend,
195697
195787
  ...createVersion ? { version: createVersion } : {}
195698
195788
  });
@@ -195709,7 +195799,7 @@ flag would otherwise shadow it. Validation failures come back as
195709
195799
  const moveCmd = parent.command("move <workflow-id>").description("Move a workflow to a folder path, creating folders as needed").requiredOption("--folder <path>", "Target folder path (`/` for root)");
195710
195800
  addJsonFlag(withBaseUrl(moveCmd)).action(action(async (workflow2, opts) => {
195711
195801
  const { client, workflowId } = await buildClientForWorkflow(workflow2, opts);
195712
- const result = await client.patch(`/api/v1/workflows/${workflowId}`, {
195802
+ const result = await client.patch(`/api/workflows/${workflowId}`, {
195713
195803
  folderPath: opts.folder
195714
195804
  });
195715
195805
  if (opts.json) {
@@ -195898,19 +195988,12 @@ skip in CI). Folder layout reference: \`packages/cli/src/skill/reference/dataset
195898
195988
  process.stderr.write(` ${ui.dim(`size cap: ${errPayload.limitMb} MB, archive: ${errPayload.actualMb} MB`)}
195899
195989
  `);
195900
195990
  }
195901
- const codes2 = new Set(issues.map((i5) => i5.code).filter(Boolean));
195902
- if (codes2.has("invalid_expected_output")) {
195991
+ if (issues.some((i5) => i5.code === "invalid_expected_output")) {
195903
195992
  process.stderr.write(`
195904
- ${ui.dim("hint:")} ${ui.bold("expected/output.json")} must be a JSON object mirroring your workflow ${ui.bold("output:")} shape.
195905
- ` + ` For expected file outputs, drop binaries under ${ui.bold("expected/<docKey>/<file>")}.
195993
+ ${ui.dim("hint:")} ${ui.bold("expected.json")} must be a JSON object mirroring your automation output.
195994
+ ` + ` For expected files, reference binaries from JSON with ${ui.bold('{ "$file": "expected/<path>" }')}.
195906
195995
  ` + ` See ${ui.bold("reference/dataset-format.md")} or run ${ui.bold("eigenpal workflow dataset validate ./dataset")}.
195907
195996
 
195908
- `);
195909
- }
195910
- if (codes2.has("argument_name_collision")) {
195911
- process.stderr.write(`
195912
- ${ui.dim("hint:")} an arg cannot live in both ${ui.bold("arguments.json")} AND ${ui.bold("input/<arg>/")}; pick one.
195913
-
195914
195997
  `);
195915
195998
  }
195916
195999
  }
@@ -195926,7 +196009,7 @@ skip in CI). Folder layout reference: \`packages/cli/src/skill/reference/dataset
195926
196009
  const expectedSet = done.expectedSet ?? 0;
195927
196010
  success2(`Imported ${created} example${created === 1 ? "" : "s"}` + (done.mode === "replace" && done.deleted != null ? ` ${ui.dim(`(replaced ${done.deleted}, freed ${done.filesDeleted ?? 0} files)`)}` : ""));
195928
196011
  if (created > 0 && expectedSet < created) {
195929
- warn(`${expectedSet}/${created} examples have expected/output.json — the rest will run un-graded.`);
196012
+ warn(`${expectedSet}/${created} examples have expected.json — the rest will run un-graded.`);
195930
196013
  } else if (expectedSet > 0) {
195931
196014
  process.stderr.write(` ${ui.dim(`${expectedSet} with expected output`)}
195932
196015
  `);
@@ -195980,8 +196063,8 @@ Examples:
195980
196063
  $ cat expected.json | eigenpal workflow dataset example create wf_abc123 --name piped --input-json '{}' --expected-file -
195981
196064
 
195982
196065
  \`--input-json\` and \`--input-file\` (same for \`--expected-*\`) are mutually
195983
- exclusive. File-arg uploads still require a full \`dataset push\`; CRUD only
195984
- handles scalar args + \`expected/output.json\`-style outputs.
196066
+ exclusive. File uploads still require a full \`dataset push\`; CRUD only
196067
+ handles JSON input + \`expected.json\`-style outputs.
195985
196068
  `);
195986
196069
  addJsonFlag(withBaseUrl(createCmd)).action(action(async (workflow2, opts) => {
195987
196070
  const triggerInput = await readJsonInput(opts.inputJson, opts.inputFile, "input");
@@ -196298,9 +196381,8 @@ Behavior:
196298
196381
  withBaseUrl(resultsCmd).action(action(async (workflow2, batchId, opts) => {
196299
196382
  const { client, workflowId } = await buildClientForWorkflow(workflow2, opts);
196300
196383
  const params = new URLSearchParams({ format: opts.format });
196301
- if (batchId)
196302
- params.set("batchId", batchId);
196303
- await downloadStreamToFile2(client, `/api/v1/workflows/${workflowId}/eval-results/export?${params.toString()}`, opts.out);
196384
+ const exportPath = batchId ? `/api/v1/automations/${workflowId}/experiments/${batchId}/export?${params.toString()}` : `/api/v1/automations/${workflowId}/experiments/export?${params.toString()}`;
196385
+ await downloadStreamToFile2(client, exportPath, opts.out);
196304
196386
  }));
196305
196387
  const compareCmd = experiment.command("compare <batchIdA> <batchIdB>").description("Diff eval scores between two experiment batches.").option("--sort <abs-delta-desc|delta-asc|delta-desc|name>", "Row sort order (default: biggest movers first)", "abs-delta-desc").option("--regression-threshold <n>", "Δ below this is flagged as a regression (default 0.05)", Number.parseFloat, 0.05).addHelpText("after", `
196306
196388
  Examples:
@@ -196386,9 +196468,9 @@ Behavior:
196386
196468
  onTerminal: async () => {
196387
196469
  if (!dest)
196388
196470
  return;
196389
- const params = new URLSearchParams({ format: opts.format, batchId });
196471
+ const params = new URLSearchParams({ format: opts.format });
196390
196472
  try {
196391
- await downloadStreamToFile2(client, `/api/v1/workflows/${workflowId}/eval-results/export?${params.toString()}`, dest);
196473
+ await downloadStreamToFile2(client, `/api/v1/automations/${workflowId}/experiments/${batchId}/export?${params.toString()}`, dest);
196392
196474
  } catch (err2) {
196393
196475
  error51(`Watch finished but pulling results failed: ${err2 instanceof Error ? err2.message : String(err2)}. Re-run \`experiment results <wf> <batch>\` to retry.`);
196394
196476
  process.exit(1);
@@ -196515,7 +196597,7 @@ function registerVersionsCommands(parent) {
196515
196597
  const listCmd = versions3.command("list <workflow-id>").description(descriptionFor("workflow_list_versions"));
196516
196598
  addJsonFlag(withBaseUrl(withPagination(listCmd))).action(action(async (workflow2, opts) => {
196517
196599
  const { client, workflowId } = await buildClientForWorkflow(workflow2, opts);
196518
- const raw = await client.get(`/api/v1/workflows/${workflowId}/versions`, {
196600
+ const raw = await client.get(`/api/workflows/${workflowId}/versions`, {
196519
196601
  limit: String(opts.limit),
196520
196602
  offset: String(opts.offset)
196521
196603
  });
@@ -196529,7 +196611,7 @@ function registerVersionsCommands(parent) {
196529
196611
  const restoreCmd = versions3.command("restore <workflow-id> <versionId>").description(descriptionFor("workflow_restore_version"));
196530
196612
  addJsonFlag(withBaseUrl(restoreCmd)).action(action(async (workflow2, versionId, opts) => {
196531
196613
  const { client, workflowId } = await buildClientForWorkflow(workflow2, opts);
196532
- const result = await client.post(`/api/v1/workflows/${workflowId}/history/${versionId}/restore`, {});
196614
+ const result = await client.post(`/api/workflows/${workflowId}/history/${versionId}/restore`, {});
196533
196615
  if (opts.json) {
196534
196616
  printJson2(result);
196535
196617
  return;