@eigenpal/cli 0.10.4 → 0.10.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @eigenpal/cli
2
2
 
3
+ ## 0.10.5
4
+
5
+ ### Minor Changes
6
+
7
+ - d3a8b79: Workflow dataset commands now handle nested file references in example inputs, so local dataset archives can round-trip file-backed examples through the CLI without flattening the input shape.
8
+
9
+ ### Patch Changes
10
+
11
+ - 9343b8c: Add `eigenpal workflow schema <workflow>` to print a workflow's inferred output schema — the fields and files it returns, typed from its own steps — as JSON Schema, or as a TypeScript or Python type with `--format`.
12
+ - 9343b8c: `workflow validate` gains an `--online` flag that checks cross-workflow `action.invoke-workflow` references against the server (target existence, input type-match, missing/unknown input keys, undeclared output, and invoke cycles). Local validation can only check a workflow's own shape, so these reference errors previously surfaced only at push; run `eigenpal workflow validate --online` to catch them beforehand.
13
+
3
14
  ## 0.10.2
4
15
 
5
16
  ### Patch Changes
package/dist/cli.js CHANGED
@@ -17083,6 +17083,251 @@ var require_picocolors = __commonJS((exports, module) => {
17083
17083
  // ../types/src/core/index.ts
17084
17084
  var init_core3 = () => {};
17085
17085
 
17086
+ // ../types/src/files/legacy-boundary.ts
17087
+ var LEGACY_FILE_REF_COMPATIBILITY;
17088
+ var init_legacy_boundary = __esm(() => {
17089
+ LEGACY_FILE_REF_COMPATIBILITY = [
17090
+ {
17091
+ id: "legacy-workflow-execution-routes",
17092
+ description: "Old workflow execution list/detail routes may hydrate historical { fileId } trigger inputs for display only."
17093
+ },
17094
+ {
17095
+ id: "rerun-old-immutable-runs",
17096
+ description: "Rerun may remap historical immutable { fileId } trigger inputs while copying those bytes into the new run."
17097
+ },
17098
+ {
17099
+ id: "processor-adapter-runtime-handles",
17100
+ description: "Executor/headless/CLI adapters may normalize local, S3, inline, or fileId runtime handles into private ResolvedProcessorFile objects before processor invocation."
17101
+ }
17102
+ ];
17103
+ });
17104
+
17105
+ // ../types/src/files/runtime-file-ref.ts
17106
+ function isRuntimeFileRef(input) {
17107
+ return RuntimeFileRefSchema.safeParse(input).success;
17108
+ }
17109
+ function isResolvedProcessorFile(input) {
17110
+ return ResolvedProcessorFileSchema.safeParse(input).success;
17111
+ }
17112
+ function normalizeLegacyFileIdInput(input) {
17113
+ return {
17114
+ kind: "file",
17115
+ fileId: input.fileId,
17116
+ ...input.filename ? { filename: input.filename } : {},
17117
+ ...input.mimeType ? { mimeType: input.mimeType } : {}
17118
+ };
17119
+ }
17120
+ var DbFileRefSchema, LegacyFileIdInputSchema, LocalFileRefSchema, S3FileRefSchema, InlineFileRefSchema, RuntimeFileRefSchema, FilePathDescriptorSchema, ResolvedProcessorFileSchema, STRING_SCHEMA, WORKFLOW_FILE_REF_JSON_SCHEMA;
17121
+ var init_runtime_file_ref = __esm(() => {
17122
+ init_zod();
17123
+ DbFileRefSchema = exports_external.object({
17124
+ kind: exports_external.literal("file"),
17125
+ fileId: exports_external.string().describe("File ID from files table"),
17126
+ filename: exports_external.string().optional().describe("Original filename"),
17127
+ mimeType: exports_external.string().optional().describe("MIME type")
17128
+ });
17129
+ LegacyFileIdInputSchema = exports_external.object({
17130
+ kind: exports_external.never().optional(),
17131
+ fileId: exports_external.string().describe("File ID from files table"),
17132
+ filename: exports_external.string().optional().describe("Original filename"),
17133
+ mimeType: exports_external.string().optional().describe("MIME type")
17134
+ });
17135
+ LocalFileRefSchema = exports_external.object({
17136
+ kind: exports_external.literal("local"),
17137
+ path: exports_external.string().describe("Absolute path to a file on disk"),
17138
+ filename: exports_external.string().describe("Original filename"),
17139
+ mimeType: exports_external.string().describe("MIME type of the file")
17140
+ });
17141
+ S3FileRefSchema = exports_external.object({
17142
+ kind: exports_external.literal("s3"),
17143
+ ref: exports_external.string().describe("Storage key (suffix-only, prefix added by adapter)"),
17144
+ filename: exports_external.string().describe("Original filename"),
17145
+ mimeType: exports_external.string().describe("MIME type of the file")
17146
+ });
17147
+ InlineFileRefSchema = exports_external.object({
17148
+ kind: exports_external.literal("inline"),
17149
+ base64: exports_external.string().describe("Base64-encoded file bytes"),
17150
+ filename: exports_external.string().describe("Original filename"),
17151
+ mimeType: exports_external.string().describe("MIME type of the file")
17152
+ });
17153
+ RuntimeFileRefSchema = exports_external.discriminatedUnion("kind", [
17154
+ DbFileRefSchema,
17155
+ LocalFileRefSchema,
17156
+ S3FileRefSchema,
17157
+ InlineFileRefSchema
17158
+ ]);
17159
+ FilePathDescriptorSchema = exports_external.discriminatedUnion("kind", [
17160
+ LocalFileRefSchema,
17161
+ S3FileRefSchema,
17162
+ InlineFileRefSchema
17163
+ ]);
17164
+ ResolvedProcessorFileSchema = exports_external.object({
17165
+ __eigenpalFile: exports_external.literal(true),
17166
+ source: exports_external.enum(["db", "tenant-s3", "local", "inline"]),
17167
+ filename: exports_external.string().describe("Display filename for processor logs and parser routing"),
17168
+ mimeType: exports_external.string().describe("MIME type for processor routing"),
17169
+ fileId: exports_external.string().optional().describe("Legacy files-table id for db-backed handles"),
17170
+ ref: exports_external.string().optional().describe("Tenant-scoped storage key suffix for S3-backed handles"),
17171
+ path: exports_external.string().optional().describe("Local filesystem path for headless/CLI handles"),
17172
+ base64: exports_external.string().optional().describe("Base64 bytes for inline handles")
17173
+ }).strict();
17174
+ STRING_SCHEMA = { type: "string" };
17175
+ WORKFLOW_FILE_REF_JSON_SCHEMA = {
17176
+ "x-eigenpal-type": "file",
17177
+ type: "object",
17178
+ anyOf: [
17179
+ {
17180
+ type: "object",
17181
+ required: ["$file"],
17182
+ additionalProperties: false,
17183
+ properties: {
17184
+ $file: STRING_SCHEMA
17185
+ }
17186
+ },
17187
+ {
17188
+ type: "object",
17189
+ required: ["$fileId"],
17190
+ additionalProperties: false,
17191
+ properties: {
17192
+ $fileId: STRING_SCHEMA
17193
+ }
17194
+ },
17195
+ {
17196
+ type: "object",
17197
+ required: ["$inline"],
17198
+ additionalProperties: false,
17199
+ properties: {
17200
+ $inline: {
17201
+ type: "object",
17202
+ required: ["filename", "mimeType", "base64"],
17203
+ additionalProperties: false,
17204
+ properties: {
17205
+ filename: STRING_SCHEMA,
17206
+ mimeType: STRING_SCHEMA,
17207
+ base64: STRING_SCHEMA
17208
+ }
17209
+ }
17210
+ }
17211
+ }
17212
+ ]
17213
+ };
17214
+ });
17215
+
17216
+ // ../types/src/files/scoped-file-ref.ts
17217
+ function isScopedFileRef(value) {
17218
+ return ScopedFileRefSchema.safeParse(value).success;
17219
+ }
17220
+ function isFileIdIngressRef(value) {
17221
+ return FileIdIngressRefSchema.safeParse(value).success;
17222
+ }
17223
+ function isInlineFileIngressRef(value) {
17224
+ return InlineFileIngressRefSchema.safeParse(value).success;
17225
+ }
17226
+ function isFileIngressRef(value) {
17227
+ return FileIngressRefSchema.safeParse(value).success;
17228
+ }
17229
+ function isFileReferenceSentinel(value) {
17230
+ return FileReferenceSentinelSchema.safeParse(value).success;
17231
+ }
17232
+ function fileReferenceKind(ref) {
17233
+ if ("$file" in ref)
17234
+ return "file";
17235
+ if ("$fileId" in ref)
17236
+ return "fileId";
17237
+ return "inline";
17238
+ }
17239
+ function validateScopedArtifactPath(path, options = {}) {
17240
+ if (path.length === 0)
17241
+ return { ok: false, reason: "path is empty" };
17242
+ if (path.startsWith("/"))
17243
+ return { ok: false, reason: "leading slash not allowed" };
17244
+ if (path.includes("\\"))
17245
+ return { ok: false, reason: "backslashes not allowed" };
17246
+ if (path.includes("\x00"))
17247
+ return { ok: false, reason: "null bytes not allowed" };
17248
+ const segments = path.split("/");
17249
+ for (const segment of segments) {
17250
+ if (segment.length === 0)
17251
+ return { ok: false, reason: "empty path segment not allowed" };
17252
+ if (segment === ".")
17253
+ return { ok: false, reason: "current-directory segment not allowed" };
17254
+ if (segment === "..")
17255
+ return { ok: false, reason: "path traversal segment not allowed" };
17256
+ }
17257
+ if (segments[0] === "tenants" || segments[0] === "automations" || segments[0] === "workflows" || segments[0] === "agents") {
17258
+ return { ok: false, reason: "storage prefixes are not allowed in scoped file refs" };
17259
+ }
17260
+ const normalizedRoots = (options.allowedRoots ?? []).map((root) => root.replace(/^\/+|\/+$/g, ""));
17261
+ if (normalizedRoots.length > 0 && !normalizedRoots.some((root) => path === root || path.startsWith(`${root}/`))) {
17262
+ return { ok: false, reason: `path root must be one of: ${normalizedRoots.join(", ")}` };
17263
+ }
17264
+ return { ok: true, value: { path: segments.join("/"), segments, root: segments[0] } };
17265
+ }
17266
+ function collectFileReferenceSentinels(value) {
17267
+ const matches = [];
17268
+ walkFileReferenceSentinels(value, (match) => {
17269
+ matches.push(match);
17270
+ });
17271
+ return matches;
17272
+ }
17273
+ function walkFileReferenceSentinels(value, visit, path = []) {
17274
+ const parsed = FileReferenceSentinelSchema.safeParse(value);
17275
+ if (parsed.success) {
17276
+ visit({ path, ref: parsed.data, kind: fileReferenceKind(parsed.data) });
17277
+ return;
17278
+ }
17279
+ if (Array.isArray(value)) {
17280
+ value.forEach((item, index) => walkFileReferenceSentinels(item, visit, [...path, String(index)]));
17281
+ return;
17282
+ }
17283
+ if (value && typeof value === "object") {
17284
+ for (const [key, item] of Object.entries(value)) {
17285
+ walkFileReferenceSentinels(item, visit, [...path, key]);
17286
+ }
17287
+ }
17288
+ }
17289
+ function mapFileReferenceSentinels(value, mapper, path = []) {
17290
+ const parsed = FileReferenceSentinelSchema.safeParse(value);
17291
+ if (parsed.success) {
17292
+ return mapper({ path, ref: parsed.data, kind: fileReferenceKind(parsed.data) });
17293
+ }
17294
+ if (Array.isArray(value)) {
17295
+ return value.map((item, index) => mapFileReferenceSentinels(item, mapper, [...path, String(index)]));
17296
+ }
17297
+ if (value && typeof value === "object") {
17298
+ const out = {};
17299
+ for (const [key, item] of Object.entries(value)) {
17300
+ out[key] = mapFileReferenceSentinels(item, mapper, [...path, key]);
17301
+ }
17302
+ return out;
17303
+ }
17304
+ return value;
17305
+ }
17306
+ var ScopedFileRefSchema, FileIdIngressRefSchema, InlineFileIngressPayloadSchema, InlineFileIngressRefSchema, FileIngressRefSchema, FileReferenceSentinelSchema;
17307
+ var init_scoped_file_ref = __esm(() => {
17308
+ init_zod();
17309
+ ScopedFileRefSchema = exports_external.object({
17310
+ $file: exports_external.string().min(1).describe("Owner-relative artifact path")
17311
+ }).strict();
17312
+ FileIdIngressRefSchema = exports_external.object({
17313
+ $fileId: exports_external.string().min(1).describe("Reusable file-pool id to materialize")
17314
+ }).strict();
17315
+ InlineFileIngressPayloadSchema = exports_external.object({
17316
+ filename: exports_external.string().min(1).describe("Original filename for the materialized artifact"),
17317
+ mimeType: exports_external.string().min(1).describe("MIME type for the materialized artifact"),
17318
+ base64: exports_external.string().min(1).describe("Base64-encoded file bytes")
17319
+ }).strict();
17320
+ InlineFileIngressRefSchema = exports_external.object({
17321
+ $inline: InlineFileIngressPayloadSchema
17322
+ }).strict();
17323
+ FileIngressRefSchema = exports_external.union([FileIdIngressRefSchema, InlineFileIngressRefSchema]);
17324
+ FileReferenceSentinelSchema = exports_external.union([
17325
+ ScopedFileRefSchema,
17326
+ FileIdIngressRefSchema,
17327
+ InlineFileIngressRefSchema
17328
+ ]);
17329
+ });
17330
+
17086
17331
  // ../types/src/processor/index.ts
17087
17332
  var init_processor = () => {};
17088
17333
 
@@ -23915,7 +24160,7 @@ function workflowInputTypeToJsonSchema(def) {
23915
24160
  case "boolean":
23916
24161
  return { type: "boolean" };
23917
24162
  case "file":
23918
- return { ...FILE_REF_SCHEMA };
24163
+ return { ...WORKFLOW_FILE_REF_JSON_SCHEMA };
23919
24164
  case "array":
23920
24165
  return { type: "array", items: itemsToJsonSchema(def.items) };
23921
24166
  case "object":
@@ -23928,7 +24173,7 @@ function itemsToJsonSchema(items) {
23928
24173
  if (!items)
23929
24174
  return {};
23930
24175
  if (items.type === "file")
23931
- return { ...FILE_REF_SCHEMA };
24176
+ return { ...WORKFLOW_FILE_REF_JSON_SCHEMA };
23932
24177
  if (items.type === "enum" && items.values && items.values.length > 0) {
23933
24178
  return { type: "string", enum: items.values };
23934
24179
  }
@@ -24032,9 +24277,7 @@ function withPendingFileRefs(input, files) {
24032
24277
  const refsByField = {};
24033
24278
  for (const f of files) {
24034
24279
  (refsByField[f.fieldName] ??= []).push({
24035
- fileId: PENDING_FILE_REF,
24036
- filename: f.filename,
24037
- mimeType: f.mimeType
24280
+ $file: `input/${f.fieldName}/${PENDING_FILE_REF}-${f.filename}`
24038
24281
  });
24039
24282
  }
24040
24283
  const merged = { ...input };
@@ -24089,8 +24332,9 @@ function coerceScalar(value, type) {
24089
24332
  }
24090
24333
  return;
24091
24334
  }
24092
- var INPUT_VALIDATION_CODES, FILE_REF_SCHEMA, PENDING_FILE_REF = "__pending_upload__";
24335
+ var INPUT_VALIDATION_CODES, PENDING_FILE_REF = "__pending_upload__";
24093
24336
  var init_input = __esm(() => {
24337
+ init_runtime_file_ref();
24094
24338
  init_ajv();
24095
24339
  INPUT_VALIDATION_CODES = [
24096
24340
  "missing_required",
@@ -24100,16 +24344,6 @@ var init_input = __esm(() => {
24100
24344
  "array_length",
24101
24345
  "invalid_value"
24102
24346
  ];
24103
- FILE_REF_SCHEMA = {
24104
- type: "object",
24105
- required: ["fileId"],
24106
- "x-eigenpal-type": "file",
24107
- properties: {
24108
- fileId: { type: "string" },
24109
- filename: { type: "string" },
24110
- mimeType: { type: "string" }
24111
- }
24112
- };
24113
24347
  });
24114
24348
 
24115
24349
  // ../types/src/validation/output.ts
@@ -62096,7 +62330,7 @@ var init_step_configs = __esm(() => {
62096
62330
  text: exports_external.string().describe("Extracted text from the document")
62097
62331
  });
62098
62332
  TransformXlsxToJsonConfigSchema = exports_external.object({
62099
- input: exports_external.string().min(1, "Input is required").describe("File input - template expression e.g. {{input.document}} resolving to fileId or file path descriptor"),
62333
+ input: exports_external.string().min(1, "Input is required").describe("File input - template expression e.g. {{input.document}} resolving to a scoped $file artifact at runtime"),
62100
62334
  sheet: exports_external.union([exports_external.number().int().min(0), exports_external.string()]).optional().describe("Sheet to read: 0-based index or sheet name. Omit for first sheet."),
62101
62335
  outputCsv: exports_external.boolean().default(false).describe("If true, also write CSV to storage and include fileId in output"),
62102
62336
  outputFilename: exports_external.string().optional().describe("Output CSV filename when outputCsv is true - supports LiquidJS e.g. {{filename}}.csv")
@@ -62221,12 +62455,12 @@ var init_step_configs = __esm(() => {
62221
62455
  });
62222
62456
  ActionInvokeWorkflowConfigSchema = exports_external.object({
62223
62457
  workflowId: exports_external.string().min(1, "Workflow ID is required").describe("ID of workflow to invoke"),
62224
- input: exports_external.record(exports_external.string(), exports_external.unknown()).optional().describe("Input to pass to workflow"),
62225
- wait: exports_external.boolean().optional().describe("If true, wait for the invoked workflow to complete (default: false)"),
62226
- timeout: exports_external.number().optional().describe("Max wait time in ms when wait=true (default: 300000)"),
62227
- pollInterval: exports_external.number().optional().describe("How often to poll status in ms when wait=true (default: 1000)")
62458
+ input: exports_external.record(exports_external.string(), exports_external.unknown()).optional().describe("Input record keyed by the invoked workflow's declared inputs"),
62459
+ wait: exports_external.boolean().optional().describe("Wait for the invoked workflow to complete and return its output (default: true). Set false for fire-and-forget."),
62460
+ timeout: exports_external.number().optional().describe("Max wait time in ms when waiting (default: 300000)"),
62461
+ pollInterval: exports_external.number().optional().describe("How often to poll status in ms when waiting (default: 1000)")
62228
62462
  });
62229
- ActionInvokeWorkflowOutputSchema = exports_external.record(exports_external.string(), exports_external.unknown()).describe("Output from invoked workflow");
62463
+ ActionInvokeWorkflowOutputSchema = exports_external.record(exports_external.string(), exports_external.unknown()).describe("When wait is true, the invoked workflow's declared output fields are flattened to the top level alongside a `files` array — reference them as {{ steps.<invoke>.output.<field> }} (there is no `.data` or `.result` wrapper). When wait is false, execution metadata. Call get_workflow_output_schema to see the exact resolved fields.");
62230
62464
  ActionWebsiteReaderConfigSchema = exports_external.object({
62231
62465
  url: exports_external.string().min(1, "URL is required").describe("Website URL to fetch (supports template expressions)"),
62232
62466
  timeout: exports_external.number().positive().default(30000).optional().describe("Timeout in milliseconds"),
@@ -62719,7 +62953,7 @@ var init_workflow = __esm(() => {
62719
62953
  triggerMethods: exports_external.array(TriggerMethodSchema).default([{ type: "manual" }]),
62720
62954
  inputs: exports_external.array(WorkflowInputDefSchema).optional(),
62721
62955
  steps: exports_external.array(StepSchema),
62722
- output: exports_external.record(exports_external.string(), exports_external.string()).optional(),
62956
+ output: exports_external.union([exports_external.record(exports_external.string(), exports_external.string()), exports_external.string()]).optional(),
62723
62957
  settings: WorkflowSettingsSchema.optional(),
62724
62958
  defaultModel: exports_external.string().optional()
62725
62959
  });
@@ -63032,6 +63266,117 @@ var init_execution2 = __esm(() => {
63032
63266
  });
63033
63267
  });
63034
63268
 
63269
+ // ../types/src/workflow/invoke-workflow-contract.ts
63270
+ function inputToJsonSchema(input) {
63271
+ switch (input.type) {
63272
+ case "string":
63273
+ return { type: "string" };
63274
+ case "number":
63275
+ return { type: "number" };
63276
+ case "integer":
63277
+ return { type: "integer" };
63278
+ case "boolean":
63279
+ return { type: "boolean" };
63280
+ case "object":
63281
+ return { type: "object" };
63282
+ case "enum":
63283
+ return input.values && input.values.length > 0 ? { type: "string", enum: input.values } : { type: "string" };
63284
+ case "array": {
63285
+ const itemType = input.items?.type;
63286
+ if (itemType === "enum" && input.items?.values?.length) {
63287
+ return { type: "array", items: { type: "string", enum: input.items.values } };
63288
+ }
63289
+ return itemType ? { type: "array", items: { type: itemType } } : { type: "array" };
63290
+ }
63291
+ case "file":
63292
+ return {};
63293
+ default:
63294
+ return {};
63295
+ }
63296
+ }
63297
+ function buildWorkflowInputsJsonSchema(inputs) {
63298
+ const properties = {};
63299
+ const required2 = [];
63300
+ for (const input of inputs ?? []) {
63301
+ properties[input.name] = inputToJsonSchema(input);
63302
+ if (input.required !== false)
63303
+ required2.push(input.name);
63304
+ }
63305
+ return { type: "object", additionalProperties: false, required: required2, properties };
63306
+ }
63307
+ function workflowInputAcceptsType(inputType, resolvedType) {
63308
+ if (resolvedType === "unknown" || resolvedType === "null")
63309
+ return true;
63310
+ switch (inputType) {
63311
+ case "string":
63312
+ case "enum":
63313
+ return resolvedType === "string";
63314
+ case "number":
63315
+ case "integer":
63316
+ return resolvedType === "number" || resolvedType === "integer";
63317
+ case "boolean":
63318
+ return resolvedType === "boolean";
63319
+ case "array":
63320
+ return resolvedType === "array";
63321
+ case "file":
63322
+ return resolvedType === "file" || resolvedType === "object" || resolvedType === "array";
63323
+ case "object":
63324
+ default:
63325
+ return true;
63326
+ }
63327
+ }
63328
+ function buildWorkflowOutputContractSchema(output) {
63329
+ if (typeof output === "string")
63330
+ return;
63331
+ const keys = Object.keys(output ?? {});
63332
+ if (keys.length === 0)
63333
+ return;
63334
+ const properties = {};
63335
+ for (const key of keys)
63336
+ properties[key] = {};
63337
+ return { type: "object", additionalProperties: false, required: keys, properties };
63338
+ }
63339
+ function hasDeclaredOutput(output) {
63340
+ if (typeof output === "string")
63341
+ return output.trim().length > 0;
63342
+ return !!output && Object.keys(output).length > 0;
63343
+ }
63344
+ function* iterateSteps(steps) {
63345
+ if (!Array.isArray(steps))
63346
+ return;
63347
+ for (const raw of steps) {
63348
+ if (!raw || typeof raw !== "object")
63349
+ continue;
63350
+ const step = raw;
63351
+ yield step;
63352
+ for (const value of Object.values(step)) {
63353
+ if (!Array.isArray(value))
63354
+ continue;
63355
+ for (const item of value) {
63356
+ if (!item || typeof item !== "object")
63357
+ continue;
63358
+ const obj = item;
63359
+ if ("type" in obj) {
63360
+ yield* iterateSteps([obj]);
63361
+ } else if (Array.isArray(obj.steps)) {
63362
+ yield* iterateSteps(obj.steps);
63363
+ }
63364
+ }
63365
+ }
63366
+ }
63367
+ }
63368
+ function collectInvokeWorkflowTargetIds(definition) {
63369
+ const ids = new Set;
63370
+ for (const step of iterateSteps(definition?.steps)) {
63371
+ if (step.type === "action.invoke-workflow") {
63372
+ const id = step.with?.workflowId;
63373
+ if (typeof id === "string" && id.length > 0)
63374
+ ids.add(id);
63375
+ }
63376
+ }
63377
+ return [...ids];
63378
+ }
63379
+
63035
63380
  // ../types/src/workflow/index.ts
63036
63381
  var init_workflow2 = __esm(() => {
63037
63382
  init_step_configs();
@@ -65643,11 +65988,14 @@ __export(exports_src, {
65643
65988
  workspaceDependencyNameToPackagePath: () => workspaceDependencyNameToPackagePath,
65644
65989
  workflowTriggerConfigSchema: () => workflowTriggerConfigSchema,
65645
65990
  workflowInputsToJsonSchema: () => workflowInputsToJsonSchema,
65991
+ workflowInputAcceptsType: () => workflowInputAcceptsType,
65646
65992
  withPendingFileRefs: () => withPendingFileRefs,
65993
+ walkFileReferenceSentinels: () => walkFileReferenceSentinels,
65647
65994
  valuesMatch: () => valuesMatch,
65648
65995
  validateWorkspaceSchema: () => validateWorkspaceSchema,
65649
65996
  validateStepConfig: () => validateStepConfig,
65650
65997
  validateSourcePackageRequiredFiles: () => validateSourcePackageRequiredFiles,
65998
+ validateScopedArtifactPath: () => validateScopedArtifactPath,
65651
65999
  validateOutput: () => validateOutput,
65652
66000
  validateInput: () => validateInput,
65653
66001
  validateFileSourceConfig: () => validateFileSourceConfig,
@@ -65690,11 +66038,13 @@ __export(exports_src, {
65690
66038
  parseFailureEnvelope: () => parseFailureEnvelope,
65691
66039
  parseAutomationTarget: () => parseAutomationTarget,
65692
66040
  parseAgentRerunVersion: () => parseAgentRerunVersion,
66041
+ normalizeLegacyFileIdInput: () => normalizeLegacyFileIdInput,
65693
66042
  noopTracingContext: () => noopTracingContext,
65694
66043
  mimeTypeForExtension: () => mimeTypeForExtension,
65695
66044
  matchS3Path: () => matchS3Path,
65696
66045
  mapOCRAttributes: () => mapOCRAttributes,
65697
66046
  mapLLMAttributes: () => mapLLMAttributes,
66047
+ mapFileReferenceSentinels: () => mapFileReferenceSentinels,
65698
66048
  listStepTypesByCategory: () => listStepTypesByCategory,
65699
66049
  listStepTypes: () => listStepTypes,
65700
66050
  listProcessorIds: () => listProcessorIds,
@@ -65703,19 +66053,27 @@ __export(exports_src, {
65703
66053
  isWorkflowResult: () => isWorkflowResult,
65704
66054
  isTerminalExecutionStatus: () => isTerminalExecutionStatus,
65705
66055
  isStepCategory: () => isStepCategory,
66056
+ isScopedFileRef: () => isScopedFileRef,
65706
66057
  isSchemaFile: () => isSchemaFile,
66058
+ isRuntimeFileRef: () => isRuntimeFileRef,
65707
66059
  isRunInSampleRank: () => isRunInSampleRank,
65708
66060
  isRunExpandSection: () => isRunExpandSection,
66061
+ isResolvedProcessorFile: () => isResolvedProcessorFile,
65709
66062
  isOutputSchemaUnknown: () => isOutputSchemaUnknown,
65710
66063
  isNonSuccessTerminalExecutionStatus: () => isNonSuccessTerminalExecutionStatus,
65711
66064
  isManualTriggerEnabled: () => isManualTriggerEnabled,
65712
66065
  isLocalAgentResourcePath: () => isLocalAgentResourcePath,
66066
+ isInlineFileIngressRef: () => isInlineFileIngressRef,
66067
+ isFileReferenceSentinel: () => isFileReferenceSentinel,
65713
66068
  isFilePathDescriptor: () => isFilePathDescriptor,
65714
66069
  isFileOutput: () => isFileOutput,
66070
+ isFileIngressRef: () => isFileIngressRef,
66071
+ isFileIdIngressRef: () => isFileIdIngressRef,
65715
66072
  isAutomationTriggerEnabled: () => isAutomationTriggerEnabled,
65716
66073
  isAgentEmailExplicitlyDisabled: () => isAgentEmailExplicitlyDisabled,
65717
66074
  inputsWithSource: () => inputsWithSource,
65718
66075
  hasTriggerMethod: () => hasTriggerMethod,
66076
+ hasDeclaredOutput: () => hasDeclaredOutput,
65719
66077
  goldenFileSatisfiedInResult: () => goldenFileSatisfiedInResult,
65720
66078
  getTriggerMethods: () => getTriggerMethods,
65721
66079
  getSupportedFormatsLabel: () => getSupportedFormatsLabel,
@@ -65739,6 +66097,7 @@ __export(exports_src, {
65739
66097
  formatReleaseTag: () => formatReleaseTag,
65740
66098
  formatHostedSourceExportPath: () => formatHostedSourceExportPath,
65741
66099
  formatDiffValue: () => formatDiffValue,
66100
+ fileReferenceKind: () => fileReferenceKind,
65742
66101
  fileFieldNamesFromJsonSchema: () => fileFieldNamesFromJsonSchema,
65743
66102
  fieldStemFromGoldenName: () => fieldStemFromGoldenName,
65744
66103
  extractFieldMetadata: () => extractFieldMetadata,
@@ -65759,7 +66118,11 @@ __export(exports_src, {
65759
66118
  compareExpectedValues: () => compareExpectedValues,
65760
66119
  compareExpectedError: () => compareExpectedError,
65761
66120
  combineEvalScores: () => combineEvalScores,
66121
+ collectInvokeWorkflowTargetIds: () => collectInvokeWorkflowTargetIds,
66122
+ collectFileReferenceSentinels: () => collectFileReferenceSentinels,
65762
66123
  coerceInput: () => coerceInput,
66124
+ buildWorkflowOutputContractSchema: () => buildWorkflowOutputContractSchema,
66125
+ buildWorkflowInputsJsonSchema: () => buildWorkflowInputsJsonSchema,
65763
66126
  buildS3PathSuffix: () => buildS3PathSuffix,
65764
66127
  buildS3Path: () => buildS3Path,
65765
66128
  buildDiscreteJudgeSystemPrompt: () => buildDiscreteJudgeSystemPrompt,
@@ -65788,6 +66151,7 @@ __export(exports_src, {
65788
66151
  WaitStepSchema: () => WaitStepSchema,
65789
66152
  WORKFLOW_TOOL_METADATA: () => WORKFLOW_TOOL_METADATA,
65790
66153
  WORKFLOW_NAME_PATTERN: () => WORKFLOW_NAME_PATTERN,
66154
+ WORKFLOW_FILE_REF_JSON_SCHEMA: () => WORKFLOW_FILE_REF_JSON_SCHEMA,
65791
66155
  WORKFLOW_EXECUTION_STATUSES: () => WORKFLOW_EXECUTION_STATUSES,
65792
66156
  UNSUPPORTED_SOURCE_MANIFEST_FILENAME: () => UNSUPPORTED_SOURCE_MANIFEST_FILENAME,
65793
66157
  TriggerTypeValue: () => TriggerTypeValue,
@@ -65851,6 +66215,7 @@ __export(exports_src, {
65851
66215
  SharedEvaluatorReferenceSchema: () => SharedEvaluatorReferenceSchema,
65852
66216
  SharedEvaluatorCompositionItemSchema: () => SharedEvaluatorCompositionItemSchema,
65853
66217
  SecretNameSchema: () => SecretNameSchema,
66218
+ ScopedFileRefSchema: () => ScopedFileRefSchema,
65854
66219
  ScopeStackSchema: () => ScopeStackSchema,
65855
66220
  ScopeEntryTypeSchema: () => ScopeEntryTypeSchema,
65856
66221
  ScopeEntrySchema: () => ScopeEntrySchema,
@@ -65865,10 +66230,14 @@ __export(exports_src, {
65865
66230
  SOURCE_COMMIT_SHA_PATTERN: () => SOURCE_COMMIT_SHA_PATTERN,
65866
66231
  SCRIPT_FN_MAX_BYTES: () => SCRIPT_FN_MAX_BYTES,
65867
66232
  S3_PATH_GRAMMAR: () => S3_PATH_GRAMMAR,
66233
+ S3FileRefSchema: () => S3FileRefSchema,
66234
+ RuntimeFileRefSchema: () => RuntimeFileRefSchema,
66235
+ RuntimeFilePathDescriptorSchema: () => FilePathDescriptorSchema,
65868
66236
  RunTargetStringSchema: () => RunTargetStringSchema,
65869
66237
  RunTargetSchema: () => RunTargetSchema,
65870
66238
  RunTargetObjectSchema: () => RunTargetObjectSchema,
65871
66239
  RootSourceManifestSchema: () => RootSourceManifestSchema,
66240
+ ResolvedProcessorFileSchema: () => ResolvedProcessorFileSchema,
65872
66241
  ReleaseTagSchema: () => ReleaseTagSchema,
65873
66242
  RUN_TARGET_SEGMENT_PATTERN: () => RUN_TARGET_SEGMENT_PATTERN,
65874
66243
  RUN_EXPAND_SECTIONS: () => RUN_EXPAND_SECTIONS,
@@ -65907,16 +66276,22 @@ __export(exports_src, {
65907
66276
  MergeConfigSchema: () => MergeConfigSchema,
65908
66277
  ManualTriggerMethodSchema: () => ManualTriggerMethodSchema,
65909
66278
  MAX_PAGE_SIZE: () => MAX_PAGE_SIZE,
66279
+ LocalFileRefSchema: () => LocalFileRefSchema,
65910
66280
  LocalEvaluatorReferenceSchema: () => LocalEvaluatorReferenceSchema,
65911
66281
  LocalEvaluatorCompositionItemSchema: () => LocalEvaluatorCompositionItemSchema,
65912
66282
  LocalAgentResourceDirectorySchema: () => LocalAgentResourceDirectorySchema,
65913
66283
  LlmJudgeConfigSchema: () => LlmJudgeConfigSchema,
66284
+ LegacyFileIdInputSchema: () => LegacyFileIdInputSchema,
65914
66285
  LayoutElementTypeSchema: () => LayoutElementTypeSchema,
65915
66286
  LayoutElementSchema: () => LayoutElementSchema,
65916
66287
  LLM_JUDGE_PROMPT_MIN_LENGTH: () => LLM_JUDGE_PROMPT_MIN_LENGTH,
65917
66288
  LEGACY_RUN_EXPAND_MIGRATION: () => LEGACY_RUN_EXPAND_MIGRATION,
66289
+ LEGACY_FILE_REF_COMPATIBILITY: () => LEGACY_FILE_REF_COMPATIBILITY,
65918
66290
  JudgeModeSchema: () => JudgeModeSchema,
65919
66291
  JsonSchemaSchema: () => JsonSchemaSchema,
66292
+ InlineFileRefSchema: () => InlineFileRefSchema,
66293
+ InlineFileIngressRefSchema: () => InlineFileIngressRefSchema,
66294
+ InlineFileIngressPayloadSchema: () => InlineFileIngressPayloadSchema,
65920
66295
  IfStepSchema: () => IfStepSchema,
65921
66296
  INPUT_VALIDATION_CODES: () => INPUT_VALIDATION_CODES,
65922
66297
  INPUT_KINDS: () => INPUT_KINDS,
@@ -65926,7 +66301,10 @@ __export(exports_src, {
65926
66301
  GPFS_FILE_SOURCE_NAME: () => GPFS_FILE_SOURCE_NAME,
65927
66302
  GPFS_FILE_SOURCE_DESCRIPTOR: () => GPFS_FILE_SOURCE_DESCRIPTOR,
65928
66303
  ForeachStepSchema: () => ForeachStepSchema,
65929
- FilePathDescriptorSchema: () => FilePathDescriptorSchema,
66304
+ FileReferenceSentinelSchema: () => FileReferenceSentinelSchema,
66305
+ FilePathDescriptorSchema: () => FilePathDescriptorSchema2,
66306
+ FileIngressRefSchema: () => FileIngressRefSchema,
66307
+ FileIdIngressRefSchema: () => FileIdIngressRefSchema,
65930
66308
  FailStepSchema: () => FailStepSchema,
65931
66309
  ExtractedTableSchema: () => ExtractedTableSchema,
65932
66310
  ExtractOutputSchema: () => ExtractOutputSchema,
@@ -65983,6 +66361,7 @@ __export(exports_src, {
65983
66361
  DocumentParserInputSchema: () => DocumentParserInputSchema,
65984
66362
  DocumentParserConfigSchema: () => DocumentParserConfigSchema,
65985
66363
  DocumentMetadataSchema: () => DocumentMetadataSchema,
66364
+ DbFileRefSchema: () => DbFileRefSchema,
65986
66365
  DatasetNameSchema: () => DatasetNameSchema,
65987
66366
  DatasetMetaSchema: () => DatasetMetaSchema,
65988
66367
  DatasetImportRequestSchema: () => DatasetImportRequestSchema,
@@ -66041,6 +66420,9 @@ __export(exports_src, {
66041
66420
  });
66042
66421
  var init_src = __esm(() => {
66043
66422
  init_core3();
66423
+ init_legacy_boundary();
66424
+ init_runtime_file_ref();
66425
+ init_scoped_file_ref();
66044
66426
  init_processor();
66045
66427
  init_parser();
66046
66428
  init_otel();
@@ -187691,7 +188073,7 @@ import { fileURLToPath as fileURLToPath8 } from "url";
187691
188073
  // package.json
187692
188074
  var package_default = {
187693
188075
  name: "@eigenpal/cli",
187694
- version: "0.10.4",
188076
+ version: "0.10.6",
187695
188077
  description: "Eigenpal CLI — author and run document workflows from your terminal. Agent-ready.",
187696
188078
  keywords: [
187697
188079
  "eigenpal",
@@ -194388,6 +194770,7 @@ async function writeExecutionArtifacts(client, exampleDir, payload) {
194388
194770
  }
194389
194771
 
194390
194772
  // src/lib/payload.ts
194773
+ init_src();
194391
194774
  import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync13, statSync as statSync4 } from "fs";
194392
194775
  import { join as join8 } from "path";
194393
194776
  var DATASET_EXAMPLES_DIR = ["dataset", "examples"];
@@ -194423,16 +194806,17 @@ function readCanonicalInput(exampleDir, inputPath) {
194423
194806
  for (const [key2, value] of Object.entries(raw)) {
194424
194807
  const refs = topLevelFileRefs(value);
194425
194808
  if (refs.length === 0) {
194426
- scalars[key2] = value;
194809
+ scalars[key2] = materializeNestedFileRefs(exampleDir, inputPath, value);
194427
194810
  continue;
194428
194811
  }
194429
194812
  for (const ref of refs) {
194430
- const relative3 = parseDatasetInputFileRef(ref);
194431
- if (!relative3) {
194813
+ const validation2 = validateScopedArtifactPath(ref, { allowedRoots: ["input"] });
194814
+ if (!validation2.ok) {
194432
194815
  throw new Error(`${inputPath}: ${key2} must reference files under input/ without path traversal.`);
194433
194816
  }
194434
- const filePath = join8(exampleDir, "input", relative3);
194435
- const filename = relative3.split("/").at(-1) ?? relative3;
194817
+ const relative3 = validation2.value.segments.slice(1).join("/");
194818
+ const filePath = join8(exampleDir, validation2.value.path);
194819
+ const filename = validation2.value.segments.at(-1) ?? relative3;
194436
194820
  files.push({
194437
194821
  argument: key2,
194438
194822
  filename,
@@ -194443,29 +194827,55 @@ function readCanonicalInput(exampleDir, inputPath) {
194443
194827
  }
194444
194828
  return { scalars, files };
194445
194829
  }
194830
+ function materializeNestedFileRefs(exampleDir, inputPath, value) {
194831
+ if (isScopedFileRef2(value)) {
194832
+ const validation2 = validateScopedArtifactPath(value.$file, { allowedRoots: ["input"] });
194833
+ if (!validation2.ok) {
194834
+ throw new Error(`${inputPath}: $file refs must reference files under input/.`);
194835
+ }
194836
+ const filename = validation2.value.segments.at(-1) ?? "file";
194837
+ const filePath = join8(exampleDir, validation2.value.path);
194838
+ return {
194839
+ $inline: {
194840
+ filename,
194841
+ mimeType: guessMimeType(filename) ?? "application/octet-stream",
194842
+ base64: readFileSync13(filePath).toString("base64")
194843
+ }
194844
+ };
194845
+ }
194846
+ if (Array.isArray(value)) {
194847
+ return value.map((item) => materializeNestedFileRefs(exampleDir, inputPath, item));
194848
+ }
194849
+ if (value && typeof value === "object") {
194850
+ if (hasFileKey(value)) {
194851
+ throw new Error('file refs must be exact { "$file": "input/..." } objects.');
194852
+ }
194853
+ const out = {};
194854
+ for (const [key2, item] of Object.entries(value)) {
194855
+ out[key2] = materializeNestedFileRefs(exampleDir, inputPath, item);
194856
+ }
194857
+ return out;
194858
+ }
194859
+ return value;
194860
+ }
194446
194861
  function topLevelFileRefs(value) {
194447
- if (isFileRef(value))
194862
+ if (isScopedFileRef2(value))
194448
194863
  return [value.$file];
194449
- if (Array.isArray(value) && value.every(isFileRef))
194864
+ if (hasFileKey(value)) {
194865
+ throw new Error('file refs must be exact { "$file": "input/..." } objects.');
194866
+ }
194867
+ if (Array.isArray(value) && value.every(isScopedFileRef2))
194450
194868
  return value.map((item) => item.$file);
194451
- if (Array.isArray(value) && value.some(isFileRef)) {
194452
- throw new Error('file arrays must contain only { "$file": "input/..." } entries.');
194869
+ if (Array.isArray(value) && value.some(hasFileKey)) {
194870
+ throw new Error('file arrays must contain only exact { "$file": "input/..." } entries.');
194453
194871
  }
194454
194872
  return [];
194455
194873
  }
194456
- function isFileRef(value) {
194457
- return value !== null && typeof value === "object" && !Array.isArray(value) && typeof value.$file === "string";
194874
+ function isScopedFileRef2(value) {
194875
+ return value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 1 && typeof value.$file === "string";
194458
194876
  }
194459
- function parseDatasetInputFileRef(path15) {
194460
- if (!path15.startsWith("input/"))
194461
- return null;
194462
- if (path15.startsWith("/") || path15.includes("\\") || path15.includes("\x00"))
194463
- return null;
194464
- const relative3 = path15.slice("input/".length);
194465
- const parts = relative3.split("/");
194466
- if (parts.some((part) => !part || part === "." || part === ".."))
194467
- return null;
194468
- return parts.join("/");
194877
+ function hasFileKey(value) {
194878
+ return value !== null && typeof value === "object" && !Array.isArray(value) && "$file" in value;
194469
194879
  }
194470
194880
  function readOverrides(exampleDir) {
194471
194881
  const metaPath = join8(exampleDir, "meta.json");
@@ -196336,14 +196746,14 @@ function printIssues(label2, path15, issues) {
196336
196746
  console.error("");
196337
196747
  return false;
196338
196748
  }
196339
- function isFileRef2(value) {
196749
+ function isFileRef(value) {
196340
196750
  return value !== null && typeof value === "object" && !Array.isArray(value) && typeof value.$file === "string";
196341
196751
  }
196342
196752
  function isExpectedErrorRef(value) {
196343
196753
  return value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 1 && Object.prototype.hasOwnProperty.call(value, "$error");
196344
196754
  }
196345
196755
  function collectDatasetFileRefs(value, jsonPath = "<root>") {
196346
- if (isFileRef2(value))
196756
+ if (isFileRef(value))
196347
196757
  return [{ path: value.$file, jsonPath }];
196348
196758
  if (Array.isArray(value)) {
196349
196759
  return value.flatMap((item, index) => collectDatasetFileRefs(item, `${jsonPath}.${index}`));
@@ -196387,29 +196797,50 @@ function parseDatasetFileRef(path15, root2) {
196387
196797
  function rootDir(opts) {
196388
196798
  return resolve6(opts.dir ?? process.cwd());
196389
196799
  }
196800
+ async function onlineInvokeIssues(yamlText, opts) {
196801
+ const config2 = resolveConfig(opts);
196802
+ requireApiKey(config2);
196803
+ const client = new ApiClient(config2);
196804
+ const res = await client.post("/api/workflows/validate", { yaml: yamlText });
196805
+ return res.issues ?? [];
196806
+ }
196390
196807
  function registerAllValidateCommand(parent) {
196391
- parent.command("validate [path]").description("Local-only validation. Without [path]: runs the templated three-way check (./workflow.yaml + ./evaluators.yaml + ./dataset/). With [path] pointing at a YAML file: validates that workflow.yaml. For per-noun targeting use `evaluators validate` or `dataset validate`.").option("--dir <path>", "Project root (defaults to cwd; resolves the three default paths from here)").action((path15, opts) => {
196808
+ const cmd = parent.command("validate [path]").description("Local-only validation. Without [path]: runs the templated three-way check (./workflow.yaml + ./evaluators.yaml + ./dataset/). With [path] pointing at a YAML file: validates that workflow.yaml. For per-noun targeting use `evaluators validate` or `dataset validate`.").option("--dir <path>", "Project root (defaults to cwd; resolves the three default paths from here)").option("--online", "Also validate cross-workflow action.invoke-workflow references against the server (needs auth). Catches target-not-found, input type mismatches, missing/unknown input keys, undeclared output, and invoke cycles that local validation cannot see.").addHelpText("after", `
196809
+ Note
196810
+ ` + ` Local validation cannot resolve action.invoke-workflow targets (they live
196811
+ ` + ` in the server DB), so invoke reference/type/cycle errors only surface at
196812
+ ` + " push — or run `validate --online` to check them before pushing.\n");
196813
+ withBaseUrl(cmd).action(action(async (path15, opts) => {
196392
196814
  if (path15) {
196393
196815
  const target = resolve6(path15);
196394
196816
  const wfResult2 = validateWorkflowYaml(target);
196395
196817
  const wfOk2 = printIssues("workflow.yaml", target, wfResult2.issues);
196396
196818
  if (wfOk2)
196397
196819
  printSchemaQualityWarnings(target, wfResult2.warnings);
196398
- if (!wfOk2)
196820
+ let onlineOk2 = true;
196821
+ if (wfOk2 && opts.online) {
196822
+ onlineOk2 = printIssues("invoke refs (server)", target, await onlineInvokeIssues(readFileSync15(target, "utf-8"), opts));
196823
+ }
196824
+ if (!wfOk2 || !onlineOk2)
196399
196825
  process.exit(1);
196400
196826
  return;
196401
196827
  }
196402
196828
  const root2 = rootDir(opts);
196403
- const wfResult = validateWorkflowYaml(join13(root2, "workflow.yaml"));
196404
- const wfPath = relPath(root2, join13(root2, "workflow.yaml"));
196829
+ const wfFile = join13(root2, "workflow.yaml");
196830
+ const wfResult = validateWorkflowYaml(wfFile);
196831
+ const wfPath = relPath(root2, wfFile);
196405
196832
  const wfOk = printIssues("workflow.yaml", wfPath, wfResult.issues);
196406
196833
  if (wfOk)
196407
196834
  printSchemaQualityWarnings(wfPath, wfResult.warnings);
196408
196835
  const evOk = printIssues("evaluators.yaml", relPath(root2, join13(root2, "evaluators.yaml")), validateEvaluatorsYaml(join13(root2, "evaluators.yaml")));
196409
196836
  const dsOk = printIssues("dataset/", relPath(root2, join13(root2, "dataset")), validateDatasetFolder(join13(root2, "dataset")));
196410
- if (!wfOk || !evOk || !dsOk)
196837
+ let onlineOk = true;
196838
+ if (wfOk && opts.online && existsSync17(wfFile)) {
196839
+ onlineOk = printIssues("invoke refs (server)", wfPath, await onlineInvokeIssues(readFileSync15(wfFile, "utf-8"), opts));
196840
+ }
196841
+ if (!wfOk || !evOk || !dsOk || !onlineOk)
196411
196842
  process.exit(1);
196412
- });
196843
+ }));
196413
196844
  }
196414
196845
  function registerEvaluatorsValidateCommand(parent) {
196415
196846
  parent.command("validate [path]").description("Validate an evaluators YAML file against the EvalConfig schema. Defaults to ./evaluators.yaml.").action((path15) => {
@@ -196588,6 +197019,23 @@ function registerWorkflowCoreCommands(parent) {
196588
197019
  }
196589
197020
  await writeOrPrint(opts.out, result.yamlContent);
196590
197021
  }));
197022
+ const schemaCmd = parent.command("schema <workflow-id>").description("Show the inferred output schema for a workflow (what it returns).").option("--format <format>", "json | typescript | python", "json").option("--out <path>", "Write to file instead of stdout").addHelpText("after", `
197023
+ Examples
197024
+ ` + ` eigenpal workflow schema income-assessment
197025
+ ` + ` eigenpal workflow schema wf_123 --format typescript
197026
+ ` + ` eigenpal workflow schema wf_123 --format python --out Output.py
197027
+ `);
197028
+ withBaseUrl(schemaCmd).action(action(async (workflow2, opts) => {
197029
+ const { client, workflowId } = await buildClientForWorkflow(workflow2, opts);
197030
+ const format3 = opts.format ?? "json";
197031
+ const result = await client.get(`/api/workflows/${workflowId}/output-schema`, { format: format3 });
197032
+ if (format3 === "json") {
197033
+ await writeOrPrint(opts.out, JSON.stringify(result, null, 2));
197034
+ } else {
197035
+ const text = result instanceof Response ? await result.text() : String(result);
197036
+ await writeOrPrint(opts.out, text);
197037
+ }
197038
+ }));
196591
197039
  const pushCmd = parent.command("push").description(descriptionFor("workflow_set_definition")).option("--file <yaml>", "Path to YAML file").option("--workflow-id <id>", "Update existing workflow (default: create new)").option("--bump <level>", "Auto-bump from the server's current version: patch | minor | major. Mutually exclusive with `--version` and with a top-level `version:` in the YAML.").option("--set-version <semver>", "Push at this exact semver (e.g. 1.4.0). Mutually exclusive with `--bump` and with a top-level `version:` in the YAML. (Named `--set-version` to avoid the global `-v, --version` flag.)").addHelpText("after", `
196592
197040
  Examples:
196593
197041
  $ eigenpal workflow push --file workflow.yaml
@@ -196967,8 +197415,9 @@ Examples:
196967
197415
  $ cat expected.json | eigenpal workflow dataset example create wf_abc123 --name piped --input-json '{}' --expected-file -
196968
197416
 
196969
197417
  \`--input-json\` and \`--input-file\` (same for \`--expected-*\`) are mutually
196970
- exclusive. File uploads still require a full \`dataset push\`; CRUD only
196971
- handles JSON input + \`expected.json\`-style outputs.
197418
+ exclusive. To attach files without a dataset folder, pass ingress refs in JSON:
197419
+ \`{"document":{"$fileId":"file_..."}}\` or
197420
+ \`{"document":{"$inline":{"filename":"doc.pdf","mimeType":"application/pdf","base64":"..."}}}\`.
196972
197421
  `);
196973
197422
  addJsonFlag(withBaseUrl(createCmd)).action(action(async (workflow2, opts) => {
196974
197423
  const triggerInput = await readJsonInput(opts.inputJson, opts.inputFile, "input");
@@ -15,6 +15,7 @@ eigenpal
15
15
  ├── workflow
16
16
  │ ├── list|ls
17
17
  │ ├── pull <workflow-id>
18
+ │ ├── schema <workflow-id>
18
19
  │ ├── push
19
20
  │ ├── move <workflow-id>
20
21
  │ ├── evaluators
@@ -17,6 +17,7 @@ Manage workflows: push, pull, and evaluate.
17
17
  - [Details](#details)
18
18
  - [`eigenpal workflow list|ls [options]`](#eigenpal-workflow-listls-options)
19
19
  - [`eigenpal workflow pull [options] <workflow-id>`](#eigenpal-workflow-pull-options-workflow-id)
20
+ - [`eigenpal workflow schema [options] <workflow-id>`](#eigenpal-workflow-schema-options-workflow-id)
20
21
  - [`eigenpal workflow push [options]`](#eigenpal-workflow-push-options)
21
22
  - [`eigenpal workflow move [options] <workflow-id>`](#eigenpal-workflow-move-options-workflow-id)
22
23
  - [`eigenpal workflow validate [options] [path]`](#eigenpal-workflow-validate-options-path)
@@ -53,6 +54,7 @@ Manage workflows: push, pull, and evaluate.
53
54
  workflow
54
55
  ├── list|ls
55
56
  ├── pull <workflow-id>
57
+ ├── schema <workflow-id>
56
58
  ├── push
57
59
  ├── move <workflow-id>
58
60
  ├── evaluators
@@ -100,6 +102,7 @@ workflow
100
102
  | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
101
103
  | `eigenpal workflow list\|ls [options]` | List workflows the caller can read. |
102
104
  | `eigenpal workflow pull [options] <workflow-id>` | Download the YAML definition of the workflow at its current version. |
105
+ | `eigenpal workflow schema [options] <workflow-id>` | Show the inferred output schema for a workflow (what it returns). |
103
106
  | `eigenpal workflow push [options]` | Create or update a workflow from a YAML file. |
104
107
  | `eigenpal workflow move [options] <workflow-id>` | Move a workflow to a folder path, creating folders as needed |
105
108
  | `eigenpal workflow validate [options] [path]` | Local-only validation. Without [path]: runs the templated three-way check (./workflow.yaml + ./evaluators.yaml + ./dataset/). With [path] pointing at a YAML file: validates that workflow.yaml. For per-noun targeting use `evaluators validate` or `dataset validate`. |
@@ -198,6 +201,24 @@ Download the YAML definition of the workflow at its current version.
198
201
  | `--out <path>` | no | | Write YAML to file instead of stdout |
199
202
  | `--base-url <url>` | no | | Server base URL |
200
203
 
204
+ ### `eigenpal workflow schema [options] <workflow-id>`
205
+
206
+ Show the inferred output schema for a workflow (what it returns).
207
+
208
+ ### Arguments
209
+
210
+ | Name | Required | Variadic | Description |
211
+ | ------------- | -------- | -------- | ----------- |
212
+ | `workflow-id` | yes | no | |
213
+
214
+ ### Options
215
+
216
+ | Flag | Required | Default | Description |
217
+ | ------------------- | -------- | -------- | ------------------------------- |
218
+ | `--format <format>` | no | `"json"` | json \| typescript \| python |
219
+ | `--out <path>` | no | | Write to file instead of stdout |
220
+ | `--base-url <url>` | no | | Server base URL |
221
+
201
222
  ### `eigenpal workflow push [options]`
202
223
 
203
224
  Create or update a workflow from a YAML file.
@@ -243,9 +264,11 @@ Local-only validation. Without [path]: runs the templated three-way check (./wor
243
264
 
244
265
  ### Options
245
266
 
246
- | Flag | Required | Default | Description |
247
- | -------------- | -------- | ------- | -------------------------------------------------------------------------- |
248
- | `--dir <path>` | no | | Project root (defaults to cwd; resolves the three default paths from here) |
267
+ | Flag | Required | Default | Description |
268
+ | ------------------ | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
269
+ | `--dir <path>` | no | | Project root (defaults to cwd; resolves the three default paths from here) |
270
+ | `--online` | no | | Also validate cross-workflow action.invoke-workflow references against the server (needs auth). Catches target-not-found, input type mismatches, missing/unknown input keys, undeclared output, and invoke cycles that local validation cannot see. |
271
+ | `--base-url <url>` | no | | Server base URL |
249
272
 
250
273
  ### `eigenpal workflow clear-local [options] [examples...]`
251
274
 
@@ -574,7 +574,7 @@ Convert XLSX spreadsheet to JSON array of row objects for use in scripts or down
574
574
 
575
575
  | Field | Type | Required | Default | Description |
576
576
  | --- | --- | --- | --- | --- |
577
- | `input` | string | yes | | File input - template expression e.g. {{input.document}} resolving to fileId or file path descriptor |
577
+ | `input` | string | yes | | File input - template expression e.g. {{input.document}} resolving to a scoped $file artifact at runtime |
578
578
  | `sheet` | integer \| string | no | | Sheet to read: 0-based index or sheet name. Omit for first sheet. |
579
579
  | `outputCsv` | boolean | no | `false` | If true, also write CSV to storage and include fileId in output |
580
580
  | `outputFilename` | string | no | | Output CSV filename when outputCsv is true - supports LiquidJS e.g. {{filename}}.csv |
@@ -679,14 +679,14 @@ Execute another workflow and return its output
679
679
  | Field | Type | Required | Default | Description |
680
680
  | --- | --- | --- | --- | --- |
681
681
  | `workflowId` | string | yes | | ID of workflow to invoke |
682
- | `input` | record<string, unknown> | no | | Input to pass to workflow |
683
- | `wait` | boolean | no | | If true, wait for the invoked workflow to complete (default: false) |
684
- | `timeout` | number | no | | Max wait time in ms when wait=true (default: 300000) |
685
- | `pollInterval` | number | no | | How often to poll status in ms when wait=true (default: 1000) |
682
+ | `input` | record<string, unknown> | no | | Input record keyed by the invoked workflow's declared inputs |
683
+ | `wait` | boolean | no | | Wait for the invoked workflow to complete and return its output (default: true). Set false for fire-and-forget. |
684
+ | `timeout` | number | no | | Max wait time in ms when waiting (default: 300000) |
685
+ | `pollInterval` | number | no | | How often to poll status in ms when waiting (default: 1000) |
686
686
 
687
687
  **Output:** `record<string, unknown>`
688
688
 
689
- > Output from invoked workflow
689
+ > When wait is true, the invoked workflow's declared output fields are flattened to the top level alongside a `files` array — reference them as {{ steps.<invoke>.output.<field> }} (there is no `.data` or `.result` wrapper). When wait is false, execution metadata. Call get_workflow_output_schema to see the exact resolved fields.
690
690
 
691
691
  #### `action.website-reader` — Website Reader
692
692
 
@@ -79,10 +79,14 @@ The materialized shape on `triggerInput` is driven by the YAML, not by
79
79
  how many files the dataset uploaded. Get this wrong and the worker
80
80
  sees the opposite shape:
81
81
 
82
- | YAML | `triggerInput.<name>` | Template |
83
- | ------------------------------------------ | --------------------- | --------------------------------------- |
84
- | `type: file` | `{ fileId }` | `{{ input.<name> }}` |
85
- | `type: array`, `items: { type: file }` | `[{ fileId }, ...]` | `{{ input.<name>[0] }}` (or `forEach:`) |
82
+ | YAML | durable `triggerInput.<name>` | Template |
83
+ | ------------------------------------------ | ------------------------------ | --------------------------------------- |
84
+ | `type: file` | `{ "$file": "input/..." }` | `{{ input.<name> }}` |
85
+ | `type: array`, `items: { type: file }` | `[{ "$file": "input/..." }]` | `{{ input.<name>[0] }}` (or `forEach:`) |
86
+
87
+ API/SDK ingress can use `{ "$fileId": "file_..." }`, inline bytes via
88
+ `{ "$inline": { filename, mimeType, base64 } }`, or multipart file parts.
89
+ Eigenpal snapshots those into run-scoped artifacts before execution.
86
90
 
87
91
  If you see `Invalid input ... expected object, received array` from a
88
92
  step that takes a single file, the workflow input is `type: file` but
@@ -474,7 +478,7 @@ _Generated from `WorkflowDefinitionSchema` in `@eigenpal/types/src/workflow/work
474
478
  | `triggerMethods` | array<object> | no | `[{"type":"manual"}]` | |
475
479
  | `inputs` | array<object> | no | | |
476
480
  | `steps` | array<unknown> | yes | | |
477
- | `output` | record<string, string> | no | | |
481
+ | `output` | record<string, string> \| string | no | | |
478
482
  | `settings` | object | no | | |
479
483
  | `defaultModel` | string | no | | |
480
484
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eigenpal/cli",
3
- "version": "0.10.4",
3
+ "version": "0.10.6",
4
4
  "description": "Eigenpal CLI — author and run document workflows from your terminal. Agent-ready.",
5
5
  "keywords": [
6
6
  "eigenpal",