@koda-sl/baker-cli 0.123.0-dev.31b784126 → 0.123.0-dev.4a85b9f30

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.
@@ -3214,7 +3214,7 @@ var Engine = class {
3214
3214
  log: this.log,
3215
3215
  signal: opts.signal
3216
3216
  };
3217
- const preparedForExec = needsLocalMaterialization(def) ? { ...prepared, resolvedInputs: await this.materializeLocalInputs(prepared.resolvedInputs) } : prepared;
3217
+ const preparedForExec = def.location === "local" ? { ...prepared, resolvedInputs: await this.materializeLocalInputs(prepared.resolvedInputs) } : prepared;
3218
3218
  const { parsedInputs, parsedParams } = parseNodeArgs(def, preparedForExec, node.id, node.type);
3219
3219
  const result = await invokeExecute(def, parsedInputs, parsedParams, ctx, node.id, node.type);
3220
3220
  const elapsed = Date.now() - t0;
@@ -3336,9 +3336,6 @@ async function invokeExecute(def, parsedInputs, parsedParams, ctx, nodeId, nodeT
3336
3336
  throw new NodeExecutionError(nodeId, nodeType, { kind: "local", cause: e });
3337
3337
  }
3338
3338
  }
3339
- function needsLocalMaterialization(def) {
3340
- return def.location === "local" && !def.passthroughRefs;
3341
- }
3342
3339
  function pickFinalOutput(canvas, outputs) {
3343
3340
  if (canvas.output) {
3344
3341
  const node = outputs[canvas.output.node];
@@ -3354,7 +3351,7 @@ function computeNeedsLocalBytes(canvas, graph, registry) {
3354
3351
  const needs = /* @__PURE__ */ new Set();
3355
3352
  for (const [consumerId, deps] of graph) {
3356
3353
  const def = registry.get(typeById.get(consumerId) ?? "");
3357
- if (def?.location !== "local" || def.passthroughRefs) continue;
3354
+ if (def?.location !== "local") continue;
3358
3355
  for (const dep of deps) needs.add(dep);
3359
3356
  }
3360
3357
  return needs;
@@ -4685,56 +4682,19 @@ var audioTimelineNode = defineNode({
4685
4682
  }
4686
4683
  });
4687
4684
 
4688
- // src/engine/nodes/local/collect.ts
4689
- import { z as z7 } from "zod";
4690
- var collectNode = defineNode({
4691
- id: "collect",
4692
- version: "1.0.0",
4693
- category: "data",
4694
- location: "local",
4695
- passthroughRefs: true,
4696
- summary: "Gather images from multiple upstream nodes into one ordered array \u2014 the standard terminal for multi-variant canvases whose final output is several images.",
4697
- when_to_use: "Point the canvas `output` at this node when several independent branches (e.g. one image_generate per scene/variant) must ALL be finals. Wire `inputs.images` as an array of refs like `$ref:gen_billboard_03.images#0` \u2014 each final inherits its producer node id as its label (or set `params.labels` to override), so variants stay identifiable in the dashboard and selection.",
4698
- inputs: z7.object({ images: z7.array(ImageRef).min(1) }).strict(),
4699
- params: z7.object({ labels: z7.array(z7.string().min(1)).min(1).optional() }).strict(),
4700
- outputs: z7.object({ images: z7.array(ImageRef).min(1) }).strict(),
4701
- outputKinds: { images: "image" },
4702
- cost: () => ({ credits: 0, seconds_estimate: 0 }),
4703
- // Arity is only knowable at validate time when `images` is a literal array;
4704
- // a single `$ref:` string to an upstream array output defers to runtime.
4705
- validateExtra: ({ rawParams, rawInputs }) => {
4706
- const labels = rawParams?.labels;
4707
- if (!Array.isArray(labels)) return [];
4708
- if (new Set(labels).size !== labels.length) {
4709
- return [{ path: "params.labels", message: "labels must be unique \u2014 each names one output variant" }];
4710
- }
4711
- const images = rawInputs?.images;
4712
- if (Array.isArray(images) && labels.length !== images.length) {
4713
- return [
4714
- {
4715
- path: "params.labels",
4716
- message: `labels has ${labels.length} entries but ${images.length} images are wired \u2014 provide one label per image`
4717
- }
4718
- ];
4719
- }
4720
- return [];
4721
- },
4722
- execute: ({ inputs }) => Promise.resolve({ images: inputs.images })
4723
- });
4724
-
4725
4685
  // src/engine/nodes/local/ffmpeg.ts
4726
- import { z as z8 } from "zod";
4686
+ import { z as z7 } from "zod";
4727
4687
  var FFMPEG_BIN2 = "ffmpeg";
4728
- var OutputDecl = z8.object({
4729
- kind: z8.enum(["image", "video", "audio"]),
4730
- ext: z8.string().min(1).max(8)
4688
+ var OutputDecl = z7.object({
4689
+ kind: z7.enum(["image", "video", "audio"]),
4690
+ ext: z7.string().min(1).max(8)
4731
4691
  }).strict();
4732
- var FfmpegParams = z8.object({
4733
- args: z8.array(z8.string()).min(1),
4734
- outputs: z8.record(z8.string(), OutputDecl).default({})
4692
+ var FfmpegParams = z7.object({
4693
+ args: z7.array(z7.string()).min(1),
4694
+ outputs: z7.record(z7.string(), OutputDecl).default({})
4735
4695
  }).strict();
4736
- var FfmpegInputs = z8.record(z8.string(), z8.unknown());
4737
- var FfmpegOutputs = z8.record(z8.string(), z8.custom());
4696
+ var FfmpegInputs = z7.record(z7.string(), z7.unknown());
4697
+ var FfmpegOutputs = z7.record(z7.string(), z7.custom());
4738
4698
  var ffmpegNode = defineNode({
4739
4699
  id: "ffmpeg",
4740
4700
  version: "2.0.0",
@@ -4765,7 +4725,7 @@ import { mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile3 } from "fs/prom
4765
4725
  import { createRequire } from "module";
4766
4726
  import { tmpdir as tmpdir3 } from "os";
4767
4727
  import path6 from "path";
4768
- import { z as z9 } from "zod";
4728
+ import { z as z8 } from "zod";
4769
4729
 
4770
4730
  // src/engine/nodes/local/lib/assets.ts
4771
4731
  import { copyFile as copyFile3, readFile as readFile4 } from "fs/promises";
@@ -4801,15 +4761,15 @@ var DEFAULT_SPECIMEN = [
4801
4761
  "abcdefghijklmnopqrstuvwxyz",
4802
4762
  `0123456789 !?&@#$%().,:;'"-`
4803
4763
  ].join("\n");
4804
- var FontSpecimenParams = z9.object({
4805
- text: z9.string().min(1).max(2e3).optional().default(DEFAULT_SPECIMEN),
4806
- font_size: z9.number().int().min(8).max(512).optional().default(72),
4807
- padding: z9.number().int().min(0).max(512).optional().default(64),
4808
- line_height: z9.number().min(0.8).max(3).optional().default(1.35),
4809
- max_width: z9.number().int().min(256).max(4096).optional()
4764
+ var FontSpecimenParams = z8.object({
4765
+ text: z8.string().min(1).max(2e3).optional().default(DEFAULT_SPECIMEN),
4766
+ font_size: z8.number().int().min(8).max(512).optional().default(72),
4767
+ padding: z8.number().int().min(0).max(512).optional().default(64),
4768
+ line_height: z8.number().min(0.8).max(3).optional().default(1.35),
4769
+ max_width: z8.number().int().min(256).max(4096).optional()
4810
4770
  }).strict();
4811
- var FontSpecimenInputs = z9.object({ font: FontRef }).loose();
4812
- var FontSpecimenOutputs = z9.object({ image: ImageRef }).strict();
4771
+ var FontSpecimenInputs = z8.object({ font: FontRef }).loose();
4772
+ var FontSpecimenOutputs = z8.object({ image: ImageRef }).strict();
4813
4773
  var DEVICE_SCALE_FACTOR = 2;
4814
4774
  var PAGE_TIMEOUT_MS = 3e4;
4815
4775
  function escapeHtml(text) {
@@ -4950,7 +4910,7 @@ import { createRequire as createRequire2 } from "module";
4950
4910
  import { cpus, tmpdir as tmpdir4 } from "os";
4951
4911
  import path11 from "path";
4952
4912
  import { promisify as promisify4 } from "util";
4953
- import { z as z11 } from "zod";
4913
+ import { z as z10 } from "zod";
4954
4914
 
4955
4915
  // src/engine/engine/composition-hash.ts
4956
4916
  import { readdir as readdir2, readFile as readFile5, stat as stat4 } from "fs/promises";
@@ -4988,62 +4948,62 @@ async function collectFiles(root, current) {
4988
4948
  // src/engine/engine/composition-meta.ts
4989
4949
  import { readFile as readFile6 } from "fs/promises";
4990
4950
  import path8 from "path";
4991
- import { z as z10 } from "zod";
4992
- var InputKind = z10.enum(["video", "image", "audio", "json"]);
4993
- var InputSpec = z10.object({
4951
+ import { z as z9 } from "zod";
4952
+ var InputKind = z9.enum(["video", "image", "audio", "json"]);
4953
+ var InputSpec = z9.object({
4994
4954
  kind: InputKind,
4995
- required: z10.boolean().optional().default(false),
4955
+ required: z9.boolean().optional().default(false),
4996
4956
  // Filename the composition's HTML references (e.g. `input.mp4`, `logo.png`).
4997
4957
  // Defaults to `<key><ext>` derived from the kind.
4998
- staged_as: z10.string().min(1).optional(),
4999
- description: z10.string().optional()
4958
+ staged_as: z9.string().min(1).optional(),
4959
+ description: z9.string().optional()
5000
4960
  }).strict();
5001
4961
  var ParamSpecBase = {
5002
- required: z10.boolean().optional().default(false),
5003
- description: z10.string().optional()
4962
+ required: z9.boolean().optional().default(false),
4963
+ description: z9.string().optional()
5004
4964
  };
5005
- var StringParam = z10.object({
4965
+ var StringParam = z9.object({
5006
4966
  ...ParamSpecBase,
5007
- kind: z10.literal("string"),
5008
- default: z10.string().optional(),
5009
- enum: z10.array(z10.string()).optional()
4967
+ kind: z9.literal("string"),
4968
+ default: z9.string().optional(),
4969
+ enum: z9.array(z9.string()).optional()
5010
4970
  }).strict();
5011
- var IntegerParam = z10.object({
4971
+ var IntegerParam = z9.object({
5012
4972
  ...ParamSpecBase,
5013
- kind: z10.literal("integer"),
5014
- default: z10.number().int().optional(),
5015
- min: z10.number().int().optional(),
5016
- max: z10.number().int().optional()
4973
+ kind: z9.literal("integer"),
4974
+ default: z9.number().int().optional(),
4975
+ min: z9.number().int().optional(),
4976
+ max: z9.number().int().optional()
5017
4977
  }).strict();
5018
- var NumberParam = z10.object({
4978
+ var NumberParam = z9.object({
5019
4979
  ...ParamSpecBase,
5020
- kind: z10.literal("number"),
5021
- default: z10.number().optional(),
5022
- min: z10.number().optional(),
5023
- max: z10.number().optional()
4980
+ kind: z9.literal("number"),
4981
+ default: z9.number().optional(),
4982
+ min: z9.number().optional(),
4983
+ max: z9.number().optional()
5024
4984
  }).strict();
5025
- var BooleanParam = z10.object({
4985
+ var BooleanParam = z9.object({
5026
4986
  ...ParamSpecBase,
5027
- kind: z10.literal("boolean"),
5028
- default: z10.boolean().optional()
4987
+ kind: z9.literal("boolean"),
4988
+ default: z9.boolean().optional()
5029
4989
  }).strict();
5030
- var ColorParam = z10.object({
4990
+ var ColorParam = z9.object({
5031
4991
  ...ParamSpecBase,
5032
- kind: z10.literal("color"),
5033
- default: z10.string().optional()
4992
+ kind: z9.literal("color"),
4993
+ default: z9.string().optional()
5034
4994
  }).strict();
5035
- var ImageParam = z10.object({
4995
+ var ImageParam = z9.object({
5036
4996
  ...ParamSpecBase,
5037
- kind: z10.literal("image"),
5038
- default: z10.string().optional()
4997
+ kind: z9.literal("image"),
4998
+ default: z9.string().optional()
5039
4999
  }).strict();
5040
- var JsonParam = z10.object({
5000
+ var JsonParam = z9.object({
5041
5001
  ...ParamSpecBase,
5042
- kind: z10.literal("json"),
5043
- schema: z10.unknown().optional(),
5044
- default: z10.unknown().optional()
5002
+ kind: z9.literal("json"),
5003
+ schema: z9.unknown().optional(),
5004
+ default: z9.unknown().optional()
5045
5005
  }).strict();
5046
- var ParamSpec = z10.discriminatedUnion("kind", [
5006
+ var ParamSpec = z9.discriminatedUnion("kind", [
5047
5007
  StringParam,
5048
5008
  IntegerParam,
5049
5009
  NumberParam,
@@ -5052,16 +5012,16 @@ var ParamSpec = z10.discriminatedUnion("kind", [
5052
5012
  ImageParam,
5053
5013
  JsonParam
5054
5014
  ]);
5055
- var CompositionMetaSchema = z10.object({
5056
- id: z10.string().min(1),
5057
- title: z10.string().min(1),
5058
- description: z10.string().optional(),
5059
- width: z10.number().int().positive(),
5060
- height: z10.number().int().positive(),
5061
- fps: z10.number().int().positive().default(30),
5062
- default_duration: z10.number().positive().default(10),
5063
- inputs: z10.record(z10.string(), InputSpec).default({}),
5064
- params: z10.record(z10.string(), ParamSpec).default({})
5015
+ var CompositionMetaSchema = z9.object({
5016
+ id: z9.string().min(1),
5017
+ title: z9.string().min(1),
5018
+ description: z9.string().optional(),
5019
+ width: z9.number().int().positive(),
5020
+ height: z9.number().int().positive(),
5021
+ fps: z9.number().int().positive().default(30),
5022
+ default_duration: z9.number().positive().default(10),
5023
+ inputs: z9.record(z9.string(), InputSpec).default({}),
5024
+ params: z9.record(z9.string(), ParamSpec).default({})
5065
5025
  }).strict();
5066
5026
  async function loadCompositionMeta(compositionDir) {
5067
5027
  const metaPath = path8.join(compositionDir, "meta.json");
@@ -5089,39 +5049,39 @@ function buildParamsSchema(meta) {
5089
5049
  for (const [name, spec] of Object.entries(meta.params)) {
5090
5050
  shape[name] = buildParamFieldSchema(name, spec);
5091
5051
  }
5092
- return z10.object(shape).strict();
5052
+ return z9.object(shape).strict();
5093
5053
  }
5094
5054
  function buildParamFieldSchema(name, spec) {
5095
5055
  switch (spec.kind) {
5096
5056
  case "string": {
5097
- const s = spec.enum && spec.enum.length > 0 ? z10.enum(spec.enum) : z10.string();
5057
+ const s = spec.enum && spec.enum.length > 0 ? z9.enum(spec.enum) : z9.string();
5098
5058
  return finalize(s, spec.default, spec.required);
5099
5059
  }
5100
5060
  case "integer": {
5101
- let s = z10.number().int();
5061
+ let s = z9.number().int();
5102
5062
  if (spec.min !== void 0) s = s.min(spec.min);
5103
5063
  if (spec.max !== void 0) s = s.max(spec.max);
5104
5064
  return finalize(s, spec.default, spec.required);
5105
5065
  }
5106
5066
  case "number": {
5107
- let s = z10.number();
5067
+ let s = z9.number();
5108
5068
  if (spec.min !== void 0) s = s.min(spec.min);
5109
5069
  if (spec.max !== void 0) s = s.max(spec.max);
5110
5070
  return finalize(s, spec.default, spec.required);
5111
5071
  }
5112
5072
  case "boolean":
5113
- return finalize(z10.boolean(), spec.default, spec.required);
5073
+ return finalize(z9.boolean(), spec.default, spec.required);
5114
5074
  case "color": {
5115
- const s = z10.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {
5075
+ const s = z9.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {
5116
5076
  message: `param "${name}": must be a 3/6/8-digit hex color (e.g. "#ff0066")`
5117
5077
  });
5118
5078
  return finalize(s, spec.default, spec.required);
5119
5079
  }
5120
5080
  case "image":
5121
- return finalize(z10.union([z10.string().min(1), z10.record(z10.string(), z10.unknown())]), spec.default, spec.required);
5081
+ return finalize(z9.union([z9.string().min(1), z9.record(z9.string(), z9.unknown())]), spec.default, spec.required);
5122
5082
  case "json":
5123
5083
  return finalize(
5124
- z10.unknown().refine((v) => v !== void 0, { message: `param "${name}" is required` }),
5084
+ z9.unknown().refine((v) => v !== void 0, { message: `param "${name}" is required` }),
5125
5085
  spec.default,
5126
5086
  spec.required
5127
5087
  );
@@ -5421,17 +5381,17 @@ function literalize(value) {
5421
5381
  // src/engine/nodes/local/hyperframe.ts
5422
5382
  var execFileAsync2 = promisify4(execFile4);
5423
5383
  var require_2 = createRequire2(import.meta.url);
5424
- var HyperframeParams = z11.object({
5425
- composition: z11.string().min(1),
5384
+ var HyperframeParams = z10.object({
5385
+ composition: z10.string().min(1),
5426
5386
  // Output container. mp4 (default) for delivery; webm/mov render WITH
5427
5387
  // transparency (alpha) when the composition background is transparent —
5428
5388
  // use for motion-graphic overlays dropped into Premiere/AE/Nuke.
5429
- format: z11.enum(["mp4", "webm", "mov"]).optional().default("mp4"),
5430
- timeout_ms: z11.number().int().positive().optional().default(10 * 60 * 1e3)
5431
- }).catchall(z11.unknown());
5432
- var HyperframeInputs = z11.record(z11.string(), z11.custom()).optional().default({});
5433
- var HyperframeOutputs = z11.object({
5434
- video: z11.custom()
5389
+ format: z10.enum(["mp4", "webm", "mov"]).optional().default("mp4"),
5390
+ timeout_ms: z10.number().int().positive().optional().default(10 * 60 * 1e3)
5391
+ }).catchall(z10.unknown());
5392
+ var HyperframeInputs = z10.record(z10.string(), z10.custom()).optional().default({});
5393
+ var HyperframeOutputs = z10.object({
5394
+ video: z10.custom()
5435
5395
  }).strict();
5436
5396
  var NODE_OWNED_PARAM_KEYS = /* @__PURE__ */ new Set(["composition", "format", "timeout_ms"]);
5437
5397
  var MIME_BY_FORMAT = {
@@ -5720,23 +5680,23 @@ import { createRequire as createRequire3 } from "module";
5720
5680
  import { tmpdir as tmpdir5 } from "os";
5721
5681
  import path12 from "path";
5722
5682
  import { promisify as promisify5 } from "util";
5723
- import { z as z12 } from "zod";
5683
+ import { z as z11 } from "zod";
5724
5684
  var _execFileAsync = promisify5(execFile5);
5725
5685
  var require_3 = createRequire3(import.meta.url);
5726
- var WaitForSpec = z12.discriminatedUnion("kind", [
5727
- z12.object({ kind: z12.literal("auto") }),
5728
- z12.object({ kind: z12.literal("selector"), value: z12.string().min(1) }),
5729
- z12.object({ kind: z12.literal("function"), value: z12.string().min(1) }),
5730
- z12.object({ kind: z12.literal("timeout"), ms: z12.number().int().min(0).max(6e4) })
5686
+ var WaitForSpec = z11.discriminatedUnion("kind", [
5687
+ z11.object({ kind: z11.literal("auto") }),
5688
+ z11.object({ kind: z11.literal("selector"), value: z11.string().min(1) }),
5689
+ z11.object({ kind: z11.literal("function"), value: z11.string().min(1) }),
5690
+ z11.object({ kind: z11.literal("timeout"), ms: z11.number().int().min(0).max(6e4) })
5731
5691
  ]);
5732
- var HyperframeSnapshotParams = z12.object({
5733
- composition: z12.string().min(1),
5692
+ var HyperframeSnapshotParams = z11.object({
5693
+ composition: z11.string().min(1),
5734
5694
  wait_for: WaitForSpec.optional().default({ kind: "auto" }),
5735
- timeout_ms: z12.number().int().positive().optional().default(6e4)
5736
- }).catchall(z12.unknown());
5737
- var HyperframeSnapshotInputs = z12.record(z12.string(), z12.custom()).optional().default({});
5738
- var HyperframeSnapshotOutputs = z12.object({
5739
- image: z12.custom()
5695
+ timeout_ms: z11.number().int().positive().optional().default(6e4)
5696
+ }).catchall(z11.unknown());
5697
+ var HyperframeSnapshotInputs = z11.record(z11.string(), z11.custom()).optional().default({});
5698
+ var HyperframeSnapshotOutputs = z11.object({
5699
+ image: z11.custom()
5740
5700
  }).strict();
5741
5701
  var NODE_OWNED_PARAM_KEYS2 = /* @__PURE__ */ new Set(["composition", "wait_for", "timeout_ms"]);
5742
5702
  var DEVICE_SCALE_FACTOR2 = 2;
@@ -5928,18 +5888,18 @@ async function waitForReady(page, waitFor, timeoutMs) {
5928
5888
  // src/engine/nodes/local/imagemagick.ts
5929
5889
  import { execFile as execFile6 } from "child_process";
5930
5890
  import { promisify as promisify6 } from "util";
5931
- import { z as z13 } from "zod";
5891
+ import { z as z12 } from "zod";
5932
5892
  var execFileAsync3 = promisify6(execFile6);
5933
- var OutputDecl2 = z13.object({
5934
- kind: z13.enum(["image", "video", "audio"]),
5935
- ext: z13.string().min(1).max(8)
5893
+ var OutputDecl2 = z12.object({
5894
+ kind: z12.enum(["image", "video", "audio"]),
5895
+ ext: z12.string().min(1).max(8)
5936
5896
  }).strict();
5937
- var ImageMagickParams = z13.object({
5938
- args: z13.array(z13.string()).min(1),
5939
- outputs: z13.record(z13.string(), OutputDecl2).default({})
5897
+ var ImageMagickParams = z12.object({
5898
+ args: z12.array(z12.string()).min(1),
5899
+ outputs: z12.record(z12.string(), OutputDecl2).default({})
5940
5900
  }).strict();
5941
- var ImageMagickInputs = z13.record(z13.string(), z13.unknown());
5942
- var ImageMagickOutputs = z13.record(z13.string(), z13.custom());
5901
+ var ImageMagickInputs = z12.record(z12.string(), z12.unknown());
5902
+ var ImageMagickOutputs = z12.record(z12.string(), z12.custom());
5943
5903
  var resolvedBin;
5944
5904
  async function resolveBin() {
5945
5905
  if (resolvedBin) return resolvedBin;
@@ -5981,29 +5941,29 @@ var imagemagickNode = defineNode({
5981
5941
  });
5982
5942
 
5983
5943
  // src/engine/nodes/local/text.ts
5984
- import { z as z14 } from "zod";
5944
+ import { z as z13 } from "zod";
5985
5945
  var textNode = defineNode({
5986
5946
  id: "text",
5987
5947
  version: "1.0.0",
5988
5948
  category: "data",
5989
5949
  location: "local",
5990
5950
  summary: "A literal text value. Use for prompts, descriptions, copy.",
5991
- inputs: z14.object({}).strict(),
5992
- params: z14.object({ value: z14.string() }).strict(),
5993
- outputs: z14.object({ text: z14.string() }).strict(),
5951
+ inputs: z13.object({}).strict(),
5952
+ params: z13.object({ value: z13.string() }).strict(),
5953
+ outputs: z13.object({ text: z13.string() }).strict(),
5994
5954
  cost: () => ({ credits: 0, seconds_estimate: 0 }),
5995
5955
  execute: ({ params }) => Promise.resolve({ text: params.value })
5996
5956
  });
5997
5957
 
5998
5958
  // src/engine/nodes/remote/audioVoiceConvert.ts
5999
- import { z as z15 } from "zod";
6000
- var AudioVoiceConvertParams = z15.object({
6001
- model: z15.literal("elevenlabs/eleven_multilingual_sts_v2"),
5959
+ import { z as z14 } from "zod";
5960
+ var AudioVoiceConvertParams = z14.object({
5961
+ model: z14.literal("elevenlabs/eleven_multilingual_sts_v2"),
6002
5962
  /** Target voice id. Splice an upstream `voice_select` via `"{{voice_ref}}"`. */
6003
- voice: z15.string().min(1),
6004
- output_format: z15.string().optional(),
5963
+ voice: z14.string().min(1),
5964
+ output_format: z14.string().optional(),
6005
5965
  /** Strip the source clip's background noise before re-voicing. */
6006
- remove_background_noise: z15.boolean().optional()
5966
+ remove_background_noise: z14.boolean().optional()
6007
5967
  }).strict();
6008
5968
  var audioVoiceConvertNode = delegated({
6009
5969
  id: "audio_voice_convert",
@@ -6011,44 +5971,44 @@ var audioVoiceConvertNode = delegated({
6011
5971
  category: "audio",
6012
5972
  summary: "Voice Changer / speech-to-speech via ElevenLabs (eleven_multilingual_sts_v2). Re-voices an existing audio clip in a TARGET voice while preserving timing/prosody.",
6013
5973
  when_to_use: 'Use to normalize a generator-chosen voice (e.g. a Seedance talking-head clip\'s native audio) into ONE consistent brand voice across every scene \u2014 the cadence is preserved so any lip-sync stays valid. Wire `inputs.voice_ref: $ref:<voice_select>.voice_id` and set `params.voice: "{{voice_ref}}"`.',
6014
- inputs: z15.object({
5974
+ inputs: z14.object({
6015
5975
  audio: AudioRef,
6016
5976
  voice_ref: TextRef.optional()
6017
5977
  }).strict(),
6018
5978
  params: AudioVoiceConvertParams,
6019
- outputs: z15.object({ audio: AudioRef }).strict(),
5979
+ outputs: z14.object({ audio: AudioRef }).strict(),
6020
5980
  outputKinds: { audio: "audio" },
6021
5981
  cost: () => ({ credits: 1, seconds_estimate: 20 })
6022
5982
  });
6023
5983
 
6024
5984
  // src/engine/nodes/remote/dialogue.ts
6025
- import { z as z16 } from "zod";
6026
- var DialogueInput = z16.object({
6027
- text: z16.string().min(1),
6028
- voice_id: z16.string().min(1)
5985
+ import { z as z15 } from "zod";
5986
+ var DialogueInput = z15.object({
5987
+ text: z15.string().min(1),
5988
+ voice_id: z15.string().min(1)
6029
5989
  });
6030
5990
  var DIALOGUE_MODELS = ["elevenlabs/eleven_v3"];
6031
- var DialogueParams = z16.object({
6032
- model: z16.enum(DIALOGUE_MODELS),
5991
+ var DialogueParams = z15.object({
5992
+ model: z15.enum(DIALOGUE_MODELS),
6033
5993
  /**
6034
5994
  * Ordered list of lines, each tagged with the voice that should speak it.
6035
5995
  * Up to 10 unique voice_ids; total text across all lines should stay under
6036
5996
  * ~2000 characters for best quality (ElevenLabs guidance).
6037
5997
  */
6038
- inputs: z16.array(DialogueInput).min(1).max(50),
6039
- language_code: z16.string().optional(),
5998
+ inputs: z15.array(DialogueInput).min(1).max(50),
5999
+ language_code: z15.string().optional(),
6040
6000
  /** ElevenLabs voice/model settings passthrough (e.g. `{ stability: 0.5 }`). */
6041
- settings: z16.record(z16.string(), z16.unknown()).optional(),
6042
- seed: z16.number().int().min(0).max(4294967295).optional(),
6043
- apply_text_normalization: z16.enum(["auto", "on", "off"]).optional(),
6001
+ settings: z15.record(z15.string(), z15.unknown()).optional(),
6002
+ seed: z15.number().int().min(0).max(4294967295).optional(),
6003
+ apply_text_normalization: z15.enum(["auto", "on", "off"]).optional(),
6044
6004
  /**
6045
6005
  * When true, hits `/v1/text-to-dialogue/with-timestamps` and emits a
6046
6006
  * separate `timestamps` output — character-level alignment plus
6047
6007
  * per-voice segment markers usable for captions, lipsync, or
6048
6008
  * beat-matched cuts in ad creatives.
6049
6009
  */
6050
- with_timestamps: z16.boolean().optional(),
6051
- output_format: z16.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
6010
+ with_timestamps: z15.boolean().optional(),
6011
+ output_format: z15.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
6052
6012
  }).strict().refine((p) => p.inputs.reduce((sum, line) => sum + line.text.length, 0) <= ELEVENLABS_MAX_TEXT_CHARS, {
6053
6013
  message: `total dialogue text exceeds ${ELEVENLABS_MAX_TEXT_CHARS} characters`,
6054
6014
  path: ["inputs"]
@@ -6059,9 +6019,9 @@ var dialogueNode = delegated({
6059
6019
  category: "audio",
6060
6020
  summary: "Multi-voice dialogue / VO with ElevenLabs Eleven v3. Each line is tagged with a `voice_id`, so you can render two-character scripts (e.g. ad VO + customer testimonial reaction) in a single call. Setting `with_timestamps: true` adds character-level alignment for caption rendering and lipsync-friendly cuts.",
6061
6021
  when_to_use: "Use for any ad creative or website video VO that needs more than narration \u2014 interviews, two-actor scripts, character ads, testimonial reads. For single-voice flat reads the existing `tts` node is cheaper and simpler; reach for `dialogue` when you need multiple speakers in one stitched track or word-level timing for downstream lipsync / captions.",
6062
- inputs: z16.object({}).loose(),
6022
+ inputs: z15.object({}).loose(),
6063
6023
  params: DialogueParams,
6064
- outputs: z16.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
6024
+ outputs: z15.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
6065
6025
  outputKinds: { audio: "audio", timestamps: "json" },
6066
6026
  cost: ({ params }) => {
6067
6027
  const chars = params.inputs.reduce((sum, line) => sum + line.text.length, 0);
@@ -6070,7 +6030,7 @@ var dialogueNode = delegated({
6070
6030
  });
6071
6031
 
6072
6032
  // src/engine/nodes/remote/image.ts
6073
- import { z as z17 } from "zod";
6033
+ import { z as z16 } from "zod";
6074
6034
  var IMAGE_GENERATE_MODELS2 = [
6075
6035
  "openai/gpt-5.4-image-2",
6076
6036
  "google/gemini-3.5-flash",
@@ -6078,16 +6038,16 @@ var IMAGE_GENERATE_MODELS2 = [
6078
6038
  "google/gemini-3-pro-image-preview",
6079
6039
  "recraft/recraft-v4.1-pro-vector"
6080
6040
  ];
6081
- var ImageGenerateParams = z17.object({
6082
- model: z17.enum(IMAGE_GENERATE_MODELS2),
6083
- prompt: z17.string().min(1),
6084
- aspect_ratio: z17.enum(["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "4:5", "5:4", "21:9", "1:4", "4:1", "1:8", "8:1"]).optional(),
6085
- image_size: z17.enum(["0.5K", "1K", "2K", "4K"]).optional(),
6041
+ var ImageGenerateParams = z16.object({
6042
+ model: z16.enum(IMAGE_GENERATE_MODELS2),
6043
+ prompt: z16.string().min(1),
6044
+ aspect_ratio: z16.enum(["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "4:5", "5:4", "21:9", "1:4", "4:1", "1:8", "8:1"]).optional(),
6045
+ image_size: z16.enum(["0.5K", "1K", "2K", "4K"]).optional(),
6086
6046
  // Recraft v4 vector controls — forwarded into `image_config`. Registry
6087
6047
  // rejects them on non-Recraft models.
6088
- strength: z17.number().min(0).max(1).optional(),
6089
- rgb_colors: z17.array(z17.array(z17.number().int().min(0).max(255))).optional(),
6090
- background_rgb_color: z17.array(z17.number().int().min(0).max(255)).optional()
6048
+ strength: z16.number().min(0).max(1).optional(),
6049
+ rgb_colors: z16.array(z16.array(z16.number().int().min(0).max(255))).optional(),
6050
+ background_rgb_color: z16.array(z16.number().int().min(0).max(255)).optional()
6091
6051
  }).strict();
6092
6052
  var imageGenerateNode = delegated({
6093
6053
  id: "image_generate",
@@ -6097,22 +6057,22 @@ var imageGenerateNode = delegated({
6097
6057
  when_to_use: "Use for hero shots, product photography, illustrations, and vector logos. `recraft/recraft-v4.1-pro-vector` for crisp vector / logo work; `openai/gpt-5.4-image-2` for photorealistic; Gemini variants for fast iteration and editing via the `reference` input. `reference` accepts ONE image or an ARRAY of images \u2014 wire several to combine references in a single generation (e.g. a subject sheet + a font specimen + the original ad). Every reference is forwarded to the model in array order.",
6098
6058
  // `reference` is one image or an ordered array of images. The backend forwards
6099
6059
  // each as a separate `image_url` to the provider (OpenRouter accepts many).
6100
- inputs: z17.object({ reference: z17.union([ImageRef, z17.array(ImageRef).min(1)]).optional() }).loose(),
6060
+ inputs: z16.object({ reference: z16.union([ImageRef, z16.array(ImageRef).min(1)]).optional() }).loose(),
6101
6061
  params: ImageGenerateParams,
6102
- outputs: z17.object({ images: z17.array(ImageRef).min(1) }).strict(),
6062
+ outputs: z16.object({ images: z16.array(ImageRef).min(1) }).strict(),
6103
6063
  outputKinds: { images: "image" },
6104
6064
  cost: () => ({ credits: 5, seconds_estimate: 10 })
6105
6065
  });
6106
6066
 
6107
6067
  // src/engine/nodes/remote/imageAspectAdapt.ts
6108
- import { z as z18 } from "zod";
6068
+ import { z as z17 } from "zod";
6109
6069
  var ASPECT_ADAPT_MODELS = ["google/gemini-3-pro-image-preview", "google/gemini-3.1-flash-image-preview"];
6110
6070
  var ASPECT_ADAPT_FORMATS = ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"];
6111
- var ImageAspectAdaptParams = z18.object({
6112
- model: z18.enum(ASPECT_ADAPT_MODELS),
6113
- formats: z18.array(z18.enum(ASPECT_ADAPT_FORMATS)).min(1).max(6).refine((formats) => new Set(formats).size === formats.length, { message: "formats must be unique" }),
6114
- guidance: z18.string().min(1).optional(),
6115
- image_size: z18.enum(["0.5K", "1K", "2K", "4K"]).optional()
6071
+ var ImageAspectAdaptParams = z17.object({
6072
+ model: z17.enum(ASPECT_ADAPT_MODELS),
6073
+ formats: z17.array(z17.enum(ASPECT_ADAPT_FORMATS)).min(1).max(6).refine((formats) => new Set(formats).size === formats.length, { message: "formats must be unique" }),
6074
+ guidance: z17.string().min(1).optional(),
6075
+ image_size: z17.enum(["0.5K", "1K", "2K", "4K"]).optional()
6116
6076
  }).strict();
6117
6077
  var imageAspectAdaptNode = delegated({
6118
6078
  id: "image_aspect_adapt",
@@ -6120,9 +6080,9 @@ var imageAspectAdaptNode = delegated({
6120
6080
  category: "image",
6121
6081
  summary: "Adapt ONE creative into multiple aspect ratios (Meta: 9:16 stories, 1:1 feed, 4:5, 16:9\u2026) in a single step. AI recomposes the layout per format \u2014 identical subject, text, logos, colors, and style; the scene is extended/restructured, never stretched or cropped. Formats that already match the source ratio pass through unchanged at zero cost. Outputs are ordered exactly as `formats`.",
6122
6082
  when_to_use: "Use after a hero creative exists (image_generate, ingest, image_search) to fan it out to every placement format \u2014 wire the creative into `source` and list the target ratios in `formats`. Cost is estimated per format; formats matching the source ratio are free pass-throughs. Pick `google/gemini-3.1-flash-image-preview` (Nano Banana flash) while iterating, `google/gemini-3-pro-image-preview` (Nano Banana Pro) for final-quality adaptation.",
6123
- inputs: z18.object({ source: ImageRef }).loose(),
6083
+ inputs: z17.object({ source: ImageRef }).loose(),
6124
6084
  params: ImageAspectAdaptParams,
6125
- outputs: z18.object({ images: z18.array(ImageRef).min(1) }).strict(),
6085
+ outputs: z17.object({ images: z17.array(ImageRef).min(1) }).strict(),
6126
6086
  outputKinds: { images: "image" },
6127
6087
  cost: ({ params }) => {
6128
6088
  const p = params;
@@ -6135,12 +6095,12 @@ var imageAspectAdaptNode = delegated({
6135
6095
  });
6136
6096
 
6137
6097
  // src/engine/nodes/remote/imageBackgroundRemove.ts
6138
- import { z as z19 } from "zod";
6139
- var ImageBackgroundRemoveParams = z19.object({
6140
- model: z19.literal("fal/birefnet-v2").optional().default("fal/birefnet-v2"),
6141
- model_variant: z19.enum(["General Use (Light)", "General Use (Heavy)", "Matting", "Portrait", "DIS", "HRSOD", "COD"]).optional().default("General Use (Light)"),
6142
- operating_resolution: z19.enum(["1024x1024", "2048x2048", "2304x2304"]).optional(),
6143
- mask_only: z19.boolean().optional().default(false)
6098
+ import { z as z18 } from "zod";
6099
+ var ImageBackgroundRemoveParams = z18.object({
6100
+ model: z18.literal("fal/birefnet-v2").optional().default("fal/birefnet-v2"),
6101
+ model_variant: z18.enum(["General Use (Light)", "General Use (Heavy)", "Matting", "Portrait", "DIS", "HRSOD", "COD"]).optional().default("General Use (Light)"),
6102
+ operating_resolution: z18.enum(["1024x1024", "2048x2048", "2304x2304"]).optional(),
6103
+ mask_only: z18.boolean().optional().default(false)
6144
6104
  }).strict();
6145
6105
  var imageBackgroundRemoveNode = delegated({
6146
6106
  id: "image_background_remove",
@@ -6148,11 +6108,11 @@ var imageBackgroundRemoveNode = delegated({
6148
6108
  category: "image",
6149
6109
  summary: "Remove the background from an image and return a transparent PNG (or the segmentation mask). Powered by fal.ai `fal-ai/birefnet/v2`.",
6150
6110
  when_to_use: "Use to extract subjects from photos for use as overlays in hyperframe compositions, product shots, or compositing pipelines. Set `mask_only:true` to return the binary mask instead of the alpha-cut image.",
6151
- inputs: z19.object({
6111
+ inputs: z18.object({
6152
6112
  image: ImageRef
6153
6113
  }).strict(),
6154
6114
  params: ImageBackgroundRemoveParams,
6155
- outputs: z19.object({
6115
+ outputs: z18.object({
6156
6116
  image: ImageRef,
6157
6117
  mask: ImageRef.optional()
6158
6118
  }).strict(),
@@ -6161,7 +6121,7 @@ var imageBackgroundRemoveNode = delegated({
6161
6121
  });
6162
6122
 
6163
6123
  // src/engine/nodes/remote/imageDescribe.ts
6164
- import { z as z20 } from "zod";
6124
+ import { z as z19 } from "zod";
6165
6125
  var IMAGE_DESCRIBE_MODELS = ["~google/gemini-pro-latest", "~google/gemini-flash-latest"];
6166
6126
  var imageDescribeNode = delegated({
6167
6127
  id: "image_describe",
@@ -6169,33 +6129,33 @@ var imageDescribeNode = delegated({
6169
6129
  category: "vision",
6170
6130
  summary: "Reverse-engineer an image into an exhaustive, replication-grade JSON description: who the advertiser is and what they sell (source_context), composition, non-person subjects with expression/treatment, deeply detailed people, brand-identified logos (named by brand, not appearance), camera optics, lighting, color palette WITH per-color brand-ownership (brand vs borrowed-functional) and purpose, materials, visible text, ad signals (proof badges/CTA/price), the persuasion engine (ad_intent), style, post-processing.",
6171
6131
  when_to_use: 'Use to turn a reference image into a structured blueprint you can inject into downstream prompts via `{{slot}}` \u2014 e.g. restyle a competitor ad onto your own product, lock a look across a series, or feed exact palette/lighting into image_generate. Purpose-built for market adaptation: logos are identified by brand ("Trustpilot", never "green star"), people and animals carry expression/emotion/intent detail, and each color is tagged brand vs borrowed-functional so a recolor can keep the reds/yellows that do a job. The extraction prompt is baked in; use `focus` to emphasise aspects and `context` to pass known provenance (advertiser, category, market) so source_context and color ownership are grounded. Pick `~google/gemini-pro-latest` for the densest extraction (recommended for ad / market-adaptation passes), `~google/gemini-flash-latest` for cheap/fast passes. The output is rich \u2014 raise `max_tokens` (e.g. 8000+) for dense ads so the JSON isn\'t truncated.',
6172
- inputs: z20.object({ image: ImageRef }).loose(),
6173
- params: z20.object({
6174
- model: z20.enum(IMAGE_DESCRIBE_MODELS),
6175
- focus: z20.string().optional(),
6176
- context: z20.string().optional(),
6177
- temperature: z20.number().min(0).max(2).optional(),
6178
- max_tokens: z20.number().int().positive().optional()
6132
+ inputs: z19.object({ image: ImageRef }).loose(),
6133
+ params: z19.object({
6134
+ model: z19.enum(IMAGE_DESCRIBE_MODELS),
6135
+ focus: z19.string().optional(),
6136
+ context: z19.string().optional(),
6137
+ temperature: z19.number().min(0).max(2).optional(),
6138
+ max_tokens: z19.number().int().positive().optional()
6179
6139
  }).strict(),
6180
- outputs: z20.object({ description: JsonRef }).strict(),
6140
+ outputs: z19.object({ description: JsonRef }).strict(),
6181
6141
  outputKinds: { description: "json" },
6182
6142
  cost: () => ({ credits: 2, seconds_estimate: 10 })
6183
6143
  });
6184
6144
 
6185
6145
  // src/engine/nodes/remote/imageReferenceSheet.ts
6186
- import { z as z21 } from "zod";
6146
+ import { z as z20 } from "zod";
6187
6147
  var REFERENCE_SHEET_MODELS = ["google/gemini-3-pro-image-preview", "google/gemini-3.1-flash-image-preview"];
6188
- var ImageReferenceSheetParams = z21.object({
6189
- model: z21.enum(REFERENCE_SHEET_MODELS),
6190
- subject_description: z21.string().min(1),
6148
+ var ImageReferenceSheetParams = z20.object({
6149
+ model: z20.enum(REFERENCE_SHEET_MODELS),
6150
+ subject_description: z20.string().min(1),
6191
6151
  // `location` = a set/room shown from several camera ANGLES (not a rotated subject),
6192
6152
  // so a multi-scene shoot keeps one consistent set.
6193
- subject_type: z21.enum(["character", "person", "product", "location"]),
6194
- views: z21.array(z21.string().min(1)).min(2).max(8).optional(),
6195
- style: z21.string().optional(),
6196
- prompt_override: z21.string().min(1).optional(),
6197
- aspect_ratio: z21.enum(["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "4:5", "5:4", "21:9", "1:4", "4:1", "1:8", "8:1"]).optional(),
6198
- image_size: z21.enum(["0.5K", "1K", "2K", "4K"]).optional()
6153
+ subject_type: z20.enum(["character", "person", "product", "location"]),
6154
+ views: z20.array(z20.string().min(1)).min(2).max(8).optional(),
6155
+ style: z20.string().optional(),
6156
+ prompt_override: z20.string().min(1).optional(),
6157
+ aspect_ratio: z20.enum(["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "4:5", "5:4", "21:9", "1:4", "4:1", "1:8", "8:1"]).optional(),
6158
+ image_size: z20.enum(["0.5K", "1K", "2K", "4K"]).optional()
6199
6159
  }).strict();
6200
6160
  var imageReferenceSheetNode = delegated({
6201
6161
  id: "image_reference_sheet",
@@ -6203,9 +6163,9 @@ var imageReferenceSheetNode = delegated({
6203
6163
  category: "image",
6204
6164
  summary: "Fuse 1\u20136 images of a single subject (person, character, product, or location/set) into ONE multi-view reference sheet \u2014 a labeled grid in consistent style and lighting: a turnaround (FRONT / SIDE / BACK\u2026) for a person/character/product, or several camera angles of the same room (WIDE / REVERSE / DETAIL\u2026) for a location. Curated models: Gemini 3 Pro Image (best fusion + labels), Gemini 3.1 Flash Image (cheap iteration).",
6205
6165
  when_to_use: "Use before image_generate / video_generate when a subject must stay consistent across many creatives \u2014 wire the `sheet` output into their `reference` input instead of re-describing the subject per prompt. `subject_description` should be the exact wording you reuse downstream. Pick `google/gemini-3-pro-image-preview` for final 6-view sheets at 2K+, `google/gemini-3.1-flash-image-preview` while iterating.",
6206
- inputs: z21.object({ references: z21.array(ImageRef).min(1).max(6) }).loose(),
6166
+ inputs: z20.object({ references: z20.array(ImageRef).min(1).max(6) }).loose(),
6207
6167
  params: ImageReferenceSheetParams,
6208
- outputs: z21.object({ sheet: ImageRef }).strict(),
6168
+ outputs: z20.object({ sheet: ImageRef }).strict(),
6209
6169
  outputKinds: { sheet: "image" },
6210
6170
  cost: ({ params }) => ({
6211
6171
  credits: params?.model === "google/gemini-3-pro-image-preview" ? 20 : 5,
@@ -6214,10 +6174,10 @@ var imageReferenceSheetNode = delegated({
6214
6174
  });
6215
6175
 
6216
6176
  // src/engine/nodes/remote/imageSearch.ts
6217
- import { z as z22 } from "zod";
6218
- var ImageSearchParams = z22.object({
6219
- prompt: z22.string().min(1),
6220
- count: z22.number().int().min(1).max(20).default(5)
6177
+ import { z as z21 } from "zod";
6178
+ var ImageSearchParams = z21.object({
6179
+ prompt: z21.string().min(1),
6180
+ count: z21.number().int().min(1).max(20).default(5)
6221
6181
  }).strict();
6222
6182
  var imageSearchNode = delegated({
6223
6183
  id: "image_search",
@@ -6225,15 +6185,15 @@ var imageSearchNode = delegated({
6225
6185
  category: "image",
6226
6186
  summary: "Agentic image search across Google Images, stock photography (Freepik), and Pinterest. An LLM agent picks the search tools and queries, selects the best matches, and the results are downloaded into canvas assets.",
6227
6187
  when_to_use: "Use to gather real-world reference or inspiration images for a prompt (e.g. several photos of an australian shepherd) so a later step or the user can pick the best one. Not for creating new imagery \u2014 use image_generate for that.",
6228
- inputs: z22.object({}).loose(),
6188
+ inputs: z21.object({}).loose(),
6229
6189
  params: ImageSearchParams,
6230
- outputs: z22.object({ images: z22.array(ImageRef).min(1) }).strict(),
6190
+ outputs: z21.object({ images: z21.array(ImageRef).min(1) }).strict(),
6231
6191
  outputKinds: { images: "image" },
6232
6192
  cost: ({ params }) => ({ credits: Math.ceil(2 + params.count / 2), seconds_estimate: 30 })
6233
6193
  });
6234
6194
 
6235
6195
  // src/engine/nodes/remote/imageSelect.ts
6236
- import { z as z23 } from "zod";
6196
+ import { z as z22 } from "zod";
6237
6197
  var IMAGE_SELECT_MODELS = ["~google/gemini-flash-latest", "~google/gemini-pro-latest"];
6238
6198
  var imageSelectNode = delegated({
6239
6199
  id: "image_select",
@@ -6241,15 +6201,15 @@ var imageSelectNode = delegated({
6241
6201
  category: "vision",
6242
6202
  summary: "Pick the best `count` images out of 2+ candidates with a vision LLM, judged against a prompt. Outputs a passthrough subset of the input refs (no new pixels) plus the model's comparative reasoning.",
6243
6203
  when_to_use: "Use after fanning out several image_generate variants (or any pool of 2+ images) to keep only the strongest before expensive downstream steps \u2014 video generation, reference sheets, final delivery. `count` fixes the output size, so `images#0`\u2026`images#count-1` are always safe to wire. Pick `~google/gemini-flash-latest` for cheap/fast picks and `~google/gemini-pro-latest` for harder aesthetic judgement.",
6244
- inputs: z23.object({ images: z23.array(ImageRef).min(2) }).loose(),
6245
- params: z23.object({
6246
- model: z23.enum(IMAGE_SELECT_MODELS),
6247
- prompt: z23.string().min(1),
6248
- count: z23.number().int().min(1).default(1),
6249
- temperature: z23.number().min(0).max(2).optional(),
6250
- max_tokens: z23.number().int().positive().optional()
6204
+ inputs: z22.object({ images: z22.array(ImageRef).min(2) }).loose(),
6205
+ params: z22.object({
6206
+ model: z22.enum(IMAGE_SELECT_MODELS),
6207
+ prompt: z22.string().min(1),
6208
+ count: z22.number().int().min(1).default(1),
6209
+ temperature: z22.number().min(0).max(2).optional(),
6210
+ max_tokens: z22.number().int().positive().optional()
6251
6211
  }).strict(),
6252
- outputs: z23.object({ images: z23.array(ImageRef).min(1), reasoning: TextRef }).strict(),
6212
+ outputs: z22.object({ images: z22.array(ImageRef).min(1), reasoning: TextRef }).strict(),
6253
6213
  outputKinds: { images: "image", reasoning: "text" },
6254
6214
  cost: () => ({ credits: 1, seconds_estimate: 5 }),
6255
6215
  // Arity is only knowable at validate time when `images` is a literal array
@@ -6274,34 +6234,34 @@ var imageSelectNode = delegated({
6274
6234
  });
6275
6235
 
6276
6236
  // src/engine/nodes/remote/music.ts
6277
- import { z as z24 } from "zod";
6237
+ import { z as z23 } from "zod";
6278
6238
  var MUSIC_MODELS = ["elevenlabs/music-v1", "elevenlabs/video-background-music-v1"];
6279
- var MusicParams = z24.object({
6280
- model: z24.enum(MUSIC_MODELS),
6239
+ var MusicParams = z23.object({
6240
+ model: z23.enum(MUSIC_MODELS),
6281
6241
  /** Free-form prompt. Used by `elevenlabs/music-v1` (compose-detailed). */
6282
- prompt: z24.string().optional(),
6242
+ prompt: z23.string().optional(),
6283
6243
  /**
6284
6244
  * Structured composition plan (intro / hook / verse / outro sections with
6285
6245
  * per-section styles + durations). Mutually exclusive with `prompt`.
6286
6246
  */
6287
- composition_plan: z24.record(z24.string(), z24.unknown()).optional(),
6247
+ composition_plan: z23.record(z23.string(), z23.unknown()).optional(),
6288
6248
  /** Target length when using `prompt`. 3000–454545ms (capped by the $10 per-node cost limit). */
6289
- music_length_ms: z24.number().int().min(3e3).max(ELEVENLABS_MAX_MUSIC_LENGTH_MS).optional(),
6290
- seed: z24.number().int().optional(),
6249
+ music_length_ms: z23.number().int().min(3e3).max(ELEVENLABS_MAX_MUSIC_LENGTH_MS).optional(),
6250
+ seed: z23.number().int().optional(),
6291
6251
  /** Prompt mode only — forces an instrumental (no vocals) track. */
6292
- force_instrumental: z24.boolean().optional(),
6252
+ force_instrumental: z23.boolean().optional(),
6293
6253
  /** composition_plan only — honor exact section durations. */
6294
- respect_sections_durations: z24.boolean().optional(),
6254
+ respect_sections_durations: z23.boolean().optional(),
6295
6255
  /** Emit word-level timestamps alongside the audio. */
6296
- with_timestamps: z24.boolean().optional(),
6256
+ with_timestamps: z23.boolean().optional(),
6297
6257
  /**
6298
6258
  * video-to-music only — short description of the desired score
6299
6259
  * ("upbeat synth, fast cuts, 80s") used to bias the model.
6300
6260
  */
6301
- description: z24.string().max(1e3).optional(),
6261
+ description: z23.string().max(1e3).optional(),
6302
6262
  /** video-to-music only — up to 10 style tags. */
6303
- tags: z24.array(z24.string()).max(10).optional(),
6304
- output_format: z24.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
6263
+ tags: z23.array(z23.string()).max(10).optional(),
6264
+ output_format: z23.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
6305
6265
  }).strict();
6306
6266
  var musicNode = delegated({
6307
6267
  id: "music",
@@ -6309,9 +6269,9 @@ var musicNode = delegated({
6309
6269
  category: "audio",
6310
6270
  summary: "Generate music for ad creatives and website video content. `elevenlabs/music-v1` composes from a text prompt or structured composition plan; `elevenlabs/video-background-music-v1` scores an existing video clip provided via `inputs.video`.",
6311
6271
  when_to_use: "Use to produce background music or a full score for video ads, hero-section reels, or any motion content. Prefer the video-to-music model when you already have a cut and want music timed to it; use compose-detailed when you have only a brief or want section-level control (intro / hook / outro). Pair the resulting audio with `video_generate` or `video_lipsync` at compose time.",
6312
- inputs: z24.object({ video: VideoRef.optional() }).loose(),
6272
+ inputs: z23.object({ video: VideoRef.optional() }).loose(),
6313
6273
  params: MusicParams,
6314
- outputs: z24.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
6274
+ outputs: z23.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
6315
6275
  outputKinds: { audio: "audio", timestamps: "json" },
6316
6276
  cost: ({ params }) => {
6317
6277
  const seconds = params.music_length_ms ? Math.ceil(params.music_length_ms / 1e3) : 30;
@@ -6342,25 +6302,25 @@ var musicNode = delegated({
6342
6302
  });
6343
6303
 
6344
6304
  // src/engine/nodes/remote/soundEffect.ts
6345
- import { z as z25 } from "zod";
6305
+ import { z as z24 } from "zod";
6346
6306
  var SOUND_EFFECT_MODELS = ["elevenlabs/eleven_text_to_sound_v2"];
6347
- var SoundEffectParams = z25.object({
6348
- model: z25.enum(SOUND_EFFECT_MODELS),
6307
+ var SoundEffectParams = z24.object({
6308
+ model: z24.enum(SOUND_EFFECT_MODELS),
6349
6309
  /** Prompt describing the SFX ("metal door slam", "soft UI tap", "ocean waves"). */
6350
- text: z25.string().min(1),
6310
+ text: z24.string().min(1),
6351
6311
  /**
6352
6312
  * Target length in seconds. 0.5–30. Leave unset to let the model pick the
6353
6313
  * natural length for the described effect.
6354
6314
  */
6355
- duration_seconds: z25.number().min(0.5).max(30).optional(),
6315
+ duration_seconds: z24.number().min(0.5).max(30).optional(),
6356
6316
  /**
6357
6317
  * 0–1. Higher = stick closer to the prompt at the cost of variety; lower
6358
6318
  * = let the model interpret more freely. Defaults to 0.3 on the provider.
6359
6319
  */
6360
- prompt_influence: z25.number().min(0).max(1).optional(),
6320
+ prompt_influence: z24.number().min(0).max(1).optional(),
6361
6321
  /** Only valid on `eleven_text_to_sound_v2` — produce a seamless loop. */
6362
- loop: z25.boolean().optional(),
6363
- output_format: z25.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
6322
+ loop: z24.boolean().optional(),
6323
+ output_format: z24.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
6364
6324
  }).strict();
6365
6325
  var soundEffectNode = delegated({
6366
6326
  id: "sound_effect",
@@ -6368,9 +6328,9 @@ var soundEffectNode = delegated({
6368
6328
  category: "audio",
6369
6329
  summary: "Generate short sound effects from a text prompt via ElevenLabs Text-to-Sound. Use for whooshes, impacts, UI clicks, ambient beds, or signature stingers in ad creatives and product videos.",
6370
6330
  when_to_use: "Reach for this when you need a punch-in SFX layered against `video_generate` or `hyperframe_render` output \u2014 e.g. a logo whoosh on a hero shot, a click on a CTA cut, a swelling ambient bed under VO. Set `loop: true` for atmospheric beds that need to tile under longer footage; leave `duration_seconds` unset and the model picks a natural length.",
6371
- inputs: z25.object({}).loose(),
6331
+ inputs: z24.object({}).loose(),
6372
6332
  params: SoundEffectParams,
6373
- outputs: z25.object({ audio: AudioRef }).strict(),
6333
+ outputs: z24.object({ audio: AudioRef }).strict(),
6374
6334
  outputKinds: { audio: "audio" },
6375
6335
  cost: ({ params }) => {
6376
6336
  const seconds = params.duration_seconds ?? 5;
@@ -6379,7 +6339,7 @@ var soundEffectNode = delegated({
6379
6339
  });
6380
6340
 
6381
6341
  // src/engine/nodes/remote/textGenerate.ts
6382
- import { z as z26 } from "zod";
6342
+ import { z as z25 } from "zod";
6383
6343
  var TEXT_GENERATE_MODELS = ["~google/gemini-flash-latest", "~google/gemini-pro-latest"];
6384
6344
  var textGenerateNode = delegated({
6385
6345
  id: "text_generate",
@@ -6387,58 +6347,58 @@ var textGenerateNode = delegated({
6387
6347
  category: "language",
6388
6348
  summary: "Single-turn LLM text generation via OpenRouter. Returns a text response.",
6389
6349
  when_to_use: 'Use for any short text generation step in a canvas \u2014 ad copy, hooks, headlines, JSON outputs for downstream nodes. Pick `~google/gemini-flash-latest` for cheap/fast work and `~google/gemini-pro-latest` for harder reasoning. When the output must be JSON for a downstream `{{slot}}` (e.g. the ad-blueprint transform), set `response_format: "json_object"` so the model returns clean JSON with no markdown fences or prose. Set `web_search: true` to let the model search the live web first (OpenRouter `:online`) \u2014 useful when the transform must adapt copy to the target brand\'s real facts (current pricing, the trust signals it actually has) rather than guess.',
6390
- inputs: z26.object({}).loose(),
6391
- params: z26.object({
6392
- model: z26.enum(TEXT_GENERATE_MODELS),
6393
- prompt: z26.string().min(1),
6394
- system: z26.string().optional(),
6395
- response_format: z26.enum(["text", "json_object"]).optional(),
6396
- web_search: z26.boolean().optional(),
6397
- temperature: z26.number().min(0).max(2).optional(),
6398
- max_tokens: z26.number().int().positive().optional()
6350
+ inputs: z25.object({}).loose(),
6351
+ params: z25.object({
6352
+ model: z25.enum(TEXT_GENERATE_MODELS),
6353
+ prompt: z25.string().min(1),
6354
+ system: z25.string().optional(),
6355
+ response_format: z25.enum(["text", "json_object"]).optional(),
6356
+ web_search: z25.boolean().optional(),
6357
+ temperature: z25.number().min(0).max(2).optional(),
6358
+ max_tokens: z25.number().int().positive().optional()
6399
6359
  }).strict(),
6400
- outputs: z26.object({ text: TextRef }).strict(),
6360
+ outputs: z25.object({ text: TextRef }).strict(),
6401
6361
  outputKinds: { text: "text" },
6402
6362
  cost: () => ({ credits: 1, seconds_estimate: 3 })
6403
6363
  });
6404
6364
 
6405
6365
  // src/engine/nodes/remote/tts.ts
6406
- import { z as z27 } from "zod";
6366
+ import { z as z26 } from "zod";
6407
6367
  var TTS_MODELS = ["elevenlabs/eleven_v3"];
6408
- var TtsVoiceSettings = z27.object({
6409
- stability: z27.number().min(0).max(1).optional(),
6410
- similarity_boost: z27.number().min(0).max(1).optional(),
6411
- style: z27.number().min(0).max(1).optional(),
6412
- use_speaker_boost: z27.boolean().optional(),
6413
- speed: z27.number().min(0.25).max(4).optional()
6368
+ var TtsVoiceSettings = z26.object({
6369
+ stability: z26.number().min(0).max(1).optional(),
6370
+ similarity_boost: z26.number().min(0).max(1).optional(),
6371
+ style: z26.number().min(0).max(1).optional(),
6372
+ use_speaker_boost: z26.boolean().optional(),
6373
+ speed: z26.number().min(0.25).max(4).optional()
6414
6374
  }).strict();
6415
- var TtsPronunciationLocator = z27.object({
6416
- pronunciation_dictionary_id: z27.string().min(1),
6417
- version_id: z27.string().nullable().optional()
6375
+ var TtsPronunciationLocator = z26.object({
6376
+ pronunciation_dictionary_id: z26.string().min(1),
6377
+ version_id: z26.string().nullable().optional()
6418
6378
  }).strict();
6419
- var TtsParams = z27.object({
6420
- model: z27.enum(TTS_MODELS),
6421
- text: z27.string().min(1).max(ELEVENLABS_MAX_TEXT_CHARS),
6422
- voice: z27.string().min(1),
6379
+ var TtsParams = z26.object({
6380
+ model: z26.enum(TTS_MODELS),
6381
+ text: z26.string().min(1).max(ELEVENLABS_MAX_TEXT_CHARS),
6382
+ voice: z26.string().min(1),
6423
6383
  /** Provider output_format (mp3 family only — assets are stored as audio/mpeg). */
6424
- output_format: z27.enum(ELEVENLABS_OUTPUT_FORMATS).optional(),
6425
- seed: z27.number().int().min(0).max(4294967295).optional(),
6384
+ output_format: z26.enum(ELEVENLABS_OUTPUT_FORMATS).optional(),
6385
+ seed: z26.number().int().min(0).max(4294967295).optional(),
6426
6386
  // Top-level shortcuts; structured form is `voice_settings`.
6427
- stability: z27.number().min(0).max(1).optional(),
6428
- similarity_boost: z27.number().min(0).max(1).optional(),
6387
+ stability: z26.number().min(0).max(1).optional(),
6388
+ similarity_boost: z26.number().min(0).max(1).optional(),
6429
6389
  voice_settings: TtsVoiceSettings.optional(),
6430
6390
  /** ISO 639-1 language code. eleven_v3 supports language hints. */
6431
- language_code: z27.string().optional(),
6432
- pronunciation_dictionary_locators: z27.array(TtsPronunciationLocator).max(3).optional(),
6433
- apply_text_normalization: z27.enum(["auto", "on", "off"]).optional(),
6391
+ language_code: z26.string().optional(),
6392
+ pronunciation_dictionary_locators: z26.array(TtsPronunciationLocator).max(3).optional(),
6393
+ apply_text_normalization: z26.enum(["auto", "on", "off"]).optional(),
6434
6394
  /** Currently Japanese-only. Adds latency. */
6435
- apply_language_text_normalization: z27.boolean().optional(),
6395
+ apply_language_text_normalization: z26.boolean().optional(),
6436
6396
  /**
6437
6397
  * When true, hits `/v1/text-to-speech/{voice_id}/with-timestamps` and
6438
6398
  * adds a `timestamps` output (character-level alignment) for caption
6439
6399
  * rendering, lipsync, and beat-matched cuts.
6440
6400
  */
6441
- with_timestamps: z27.boolean().optional()
6401
+ with_timestamps: z26.boolean().optional()
6442
6402
  }).strict();
6443
6403
  var ttsNode = delegated({
6444
6404
  id: "tts",
@@ -6446,9 +6406,9 @@ var ttsNode = delegated({
6446
6406
  category: "audio",
6447
6407
  summary: "Single-voice text-to-speech via ElevenLabs Eleven v3. Optional character-level timestamps for caption rendering and beat-matched cuts.",
6448
6408
  when_to_use: "Use for single-speaker VO \u2014 ad reads, hero-section narration, product walkthroughs. Reach for `dialogue` when you need multiple voices in one stitched track. Set `with_timestamps: true` when downstream needs character-level alignment (captions, lipsync).",
6449
- inputs: z27.object({}).loose(),
6409
+ inputs: z26.object({}).loose(),
6450
6410
  params: TtsParams,
6451
- outputs: z27.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
6411
+ outputs: z26.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
6452
6412
  outputKinds: { audio: "audio", timestamps: "json" },
6453
6413
  cost: ({ params }) => ({
6454
6414
  credits: Math.max(1, Math.ceil(params.text.length * 15e-4)),
@@ -6457,23 +6417,23 @@ var ttsNode = delegated({
6457
6417
  });
6458
6418
 
6459
6419
  // src/engine/nodes/remote/video.ts
6460
- import { z as z28 } from "zod";
6420
+ import { z as z27 } from "zod";
6461
6421
  var VIDEO_GENERATE_MODELS = ["bytedance/seedance-2.0", "google/veo-3.1-fast"];
6462
- var VideoGenerateParams = z28.object({
6463
- model: z28.enum(VIDEO_GENERATE_MODELS),
6464
- prompt: z28.string().min(1),
6465
- duration: z28.number().int().positive().optional(),
6466
- resolution: z28.string().optional(),
6422
+ var VideoGenerateParams = z27.object({
6423
+ model: z27.enum(VIDEO_GENERATE_MODELS),
6424
+ prompt: z27.string().min(1),
6425
+ duration: z27.number().int().positive().optional(),
6426
+ resolution: z27.string().optional(),
6467
6427
  // Union of ratios accepted by at least one curated model (registry gates
6468
6428
  // per-model). 3:2/2:3 are deliberately absent: no registered model takes them.
6469
- aspect_ratio: z28.enum(["16:9", "9:16", "1:1", "4:3", "3:4", "21:9", "9:21"]).optional(),
6470
- generate_audio: z28.boolean().optional(),
6471
- seed: z28.number().int().nonnegative().optional(),
6429
+ aspect_ratio: z27.enum(["16:9", "9:16", "1:1", "4:3", "3:4", "21:9", "9:21"]).optional(),
6430
+ generate_audio: z27.boolean().optional(),
6431
+ seed: z27.number().int().nonnegative().optional(),
6472
6432
  // Veo-only passthroughs (routed via `provider.options.google-vertex.parameters`).
6473
- negative_prompt: z28.string().optional(),
6474
- person_generation: z28.string().optional(),
6475
- enhance_prompt: z28.boolean().optional(),
6476
- conditioning_scale: z28.number().optional()
6433
+ negative_prompt: z27.string().optional(),
6434
+ person_generation: z27.string().optional(),
6435
+ enhance_prompt: z27.boolean().optional(),
6436
+ conditioning_scale: z27.number().optional()
6477
6437
  }).strict();
6478
6438
  var videoGenerateNode = delegated({
6479
6439
  id: "video_generate",
@@ -6481,23 +6441,23 @@ var videoGenerateNode = delegated({
6481
6441
  category: "video",
6482
6442
  summary: "Generate video for ad creatives. Two curated models: `bytedance/seedance-2.0` (production quality, photorealistic humans via fal.ai) and `google/veo-3.1-fast` (cheap/fast for iteration and tests). Async with polling.",
6483
6443
  when_to_use: "Use `bytedance/seedance-2.0` for final ad output (photoreal subjects, image-to-video with first/last frames). Use `google/veo-3.1-fast` while iterating to keep cost low. Each model has different supported durations, resolutions, and aspect ratios \u2014 see the README per-model section.",
6484
- inputs: z28.object({
6444
+ inputs: z27.object({
6485
6445
  first_frame: ImageRef.optional(),
6486
6446
  last_frame: ImageRef.optional(),
6487
6447
  reference: ImageRef.optional()
6488
6448
  }).loose(),
6489
6449
  params: VideoGenerateParams,
6490
- outputs: z28.object({ video: VideoRef }).strict(),
6450
+ outputs: z27.object({ video: VideoRef }).strict(),
6491
6451
  outputKinds: { video: "video" },
6492
6452
  cost: () => ({ credits: 50, seconds_estimate: 120 })
6493
6453
  });
6494
6454
 
6495
6455
  // src/engine/nodes/remote/videoBackgroundRemove.ts
6496
- import { z as z29 } from "zod";
6497
- var VideoBackgroundRemoveParams = z29.object({
6498
- model: z29.literal("fal/veed-video-background-removal").optional().default("fal/veed-video-background-removal"),
6499
- edge_refinement: z29.boolean().optional().default(true),
6500
- output_codec: z29.enum(["vp9", "h264"]).optional().default("vp9")
6456
+ import { z as z28 } from "zod";
6457
+ var VideoBackgroundRemoveParams = z28.object({
6458
+ model: z28.literal("fal/veed-video-background-removal").optional().default("fal/veed-video-background-removal"),
6459
+ edge_refinement: z28.boolean().optional().default(true),
6460
+ output_codec: z28.enum(["vp9", "h264"]).optional().default("vp9")
6501
6461
  }).strict();
6502
6462
  var videoBackgroundRemoveNode = delegated({
6503
6463
  id: "video_background_remove",
@@ -6505,18 +6465,18 @@ var videoBackgroundRemoveNode = delegated({
6505
6465
  category: "video",
6506
6466
  summary: "Remove the background from a video and return a transparent VP9-with-alpha WebM (or H264 RGB+alpha pair). Drops directly into a hyperframe composition as `<video src='...'>` for chroma-keyed picture-in-picture overlays. Powered by fal.ai `veed/video-background-removal/fast`.",
6507
6467
  when_to_use: "Use when you need a talking-head or subject to float over a custom background in a hyperframe composition. Pair with hyperframe_render(composition: screencast-with-talker) for screencast-with-narrator videos. Output is `video/webm` with alpha \u2014 feed straight into `<video src>` in a composition.",
6508
- inputs: z29.object({
6468
+ inputs: z28.object({
6509
6469
  video: VideoRef
6510
6470
  }).strict(),
6511
6471
  params: VideoBackgroundRemoveParams,
6512
- outputs: z29.object({ video: VideoRef }).strict(),
6472
+ outputs: z28.object({ video: VideoRef }).strict(),
6513
6473
  outputKinds: { video: "video" },
6514
6474
  // $0.012 per 30 frames (edge refinement on) — assume ~30fps; refine via fal dashboard.
6515
6475
  cost: () => ({ credits: 50, seconds_estimate: 60 })
6516
6476
  });
6517
6477
 
6518
6478
  // src/engine/nodes/remote/videoDeconstruct.ts
6519
- import { z as z30 } from "zod";
6479
+ import { z as z29 } from "zod";
6520
6480
  var VIDEO_DECONSTRUCT_MODELS = ["~google/gemini-flash-latest", "~google/gemini-pro-latest"];
6521
6481
  var videoDeconstructNode = delegated({
6522
6482
  id: "video_deconstruct",
@@ -6524,34 +6484,34 @@ var videoDeconstructNode = delegated({
6524
6484
  category: "video",
6525
6485
  summary: 'Deconstruct a video into a replication-grade blueprint: scene boundaries, the real start/end frame of every scene (extracted from the video as images), and an exhaustive JSON analysis \u2014 per-scene action detail, camera motion, generation-ready frame/motion prompts, overlay text with full typographic style, floating elements, deeply detailed cast (perceived demographics, ethnicity/skin-tone, styling, market-recasting notes), brand-identified logos (named by brand and what they signal, not by appearance, with on-screen timestamps), dialogue with voice descriptions, music spec, SFX list, plus a word-level transcript. `mode:"index"` is the cheap structure-first pass: scene boundaries + global blueprint only (one LLM call, no frames).',
6526
6486
  when_to_use: 'Use to reverse-engineer a reference video (e.g. a competitor ad) so a new canvas can reproduce or remix it scene by scene. Agent loop: (1) optionally run `mode:"index"` to see the structure cheaply (scene count, boundaries, transcript) before planning; (2) run the full deconstruct; (3) read `analysis` and author the reproduction canvas. The blueprint maps 1:1 onto generation nodes: `analysis.scenes[i]` aligns positionally with `start_frames#i`/`end_frames#i`; per scene, `start_frame_prompt`/`end_frame_prompt` feed image_generate (overlay text is excluded from them by contract \u2014 recomposite it from `overlays`), `motion_prompt` + the two frames feed video_generate (first_frame/last_frame), `dialogue[].voice_description` casts tts/dialogue voices, `global.music.music_prompt` feeds music, `sfx[].sound_effect_prompt` feeds sound_effect, and `overlays`/`floating_elements` drive an ffmpeg/hyperframe overlay pass. Long videos (over ~8 min single-shot): run `mode:"index"` first, then several full nodes IN PARALLEL each with a `start_s`/`end_s` window (\u2264480s, snap edges to index scene boundaries), and merge by concatenating `analysis.scenes`; over-length errors include suggested windows. Inject fields into downstream prompts via `{{slot}}`. Pick `~google/gemini-pro-latest` for the densest extraction, `~google/gemini-flash-latest` for cheap/fast passes.',
6527
- inputs: z30.object({ video: VideoRef }).loose(),
6528
- params: z30.object({
6529
- model: z30.enum(VIDEO_DECONSTRUCT_MODELS),
6530
- mode: z30.enum(["full", "index"]).optional(),
6531
- language: z30.string().min(2).max(8).optional(),
6532
- max_scenes: z30.number().int().min(1).max(60).optional(),
6533
- focus: z30.string().optional(),
6534
- start_s: z30.number().min(0).optional(),
6535
- end_s: z30.number().positive().optional(),
6487
+ inputs: z29.object({ video: VideoRef }).loose(),
6488
+ params: z29.object({
6489
+ model: z29.enum(VIDEO_DECONSTRUCT_MODELS),
6490
+ mode: z29.enum(["full", "index"]).optional(),
6491
+ language: z29.string().min(2).max(8).optional(),
6492
+ max_scenes: z29.number().int().min(1).max(60).optional(),
6493
+ focus: z29.string().optional(),
6494
+ start_s: z29.number().min(0).optional(),
6495
+ end_s: z29.number().positive().optional(),
6536
6496
  // Real visual shot-cut timestamps (absolute seconds), detected locally with
6537
6497
  // ffmpeg before the deconstruct. The backend SNAPS its LLM scene boundaries
6538
6498
  // onto these and SPLITS any scene that spans one, so a scene's frames never
6539
6499
  // straddle a hard cut. `scaffold-video` populates this; omit for LLM-only cuts.
6540
- shot_cuts: z30.array(z30.number().min(0)).max(200).optional(),
6500
+ shot_cuts: z29.array(z29.number().min(0)).max(200).optional(),
6541
6501
  // The video model's per-clip ceiling (seconds). A shot longer than this is
6542
6502
  // split into seamless continuation sub-scenes (shared splice frame), so long
6543
6503
  // shots reproduce in full instead of being truncated. `scaffold-video` sets
6544
6504
  // the Seedance ceiling (15); omit to disable length splitting.
6545
- max_clip_s: z30.number().positive().max(60).optional(),
6505
+ max_clip_s: z29.number().positive().max(60).optional(),
6546
6506
  // Transcript provider for the blueprint's dialogue/transcript. Default
6547
6507
  // Groq Whisper; "deepgram" routes to Nova-3 so words carry punctuation.
6548
- transcriber: z30.enum(["groq", "deepgram"]).optional()
6508
+ transcriber: z29.enum(["groq", "deepgram"]).optional()
6549
6509
  }).strict(),
6550
- outputs: z30.object({
6510
+ outputs: z29.object({
6551
6511
  analysis: JsonRef,
6552
6512
  // Absent in mode:"index" (structure only, no Mux frame extraction).
6553
- start_frames: z30.array(ImageRef).min(1).optional(),
6554
- end_frames: z30.array(ImageRef).min(1).optional(),
6513
+ start_frames: z29.array(ImageRef).min(1).optional(),
6514
+ end_frames: z29.array(ImageRef).min(1).optional(),
6555
6515
  transcript: JsonRef
6556
6516
  }).strict(),
6557
6517
  outputKinds: { analysis: "json", start_frames: "image", end_frames: "image", transcript: "json" },
@@ -6559,22 +6519,22 @@ var videoDeconstructNode = delegated({
6559
6519
  });
6560
6520
 
6561
6521
  // src/engine/nodes/remote/videoLipsync.ts
6562
- import { z as z31 } from "zod";
6563
- var FalLipsyncParams = z31.object({
6564
- model: z31.literal("fal/veed-lipsync")
6522
+ import { z as z30 } from "zod";
6523
+ var FalLipsyncParams = z30.object({
6524
+ model: z30.literal("fal/veed-lipsync")
6565
6525
  }).strict();
6566
- var VideoLipsyncParams = z31.discriminatedUnion("model", [FalLipsyncParams]);
6526
+ var VideoLipsyncParams = z30.discriminatedUnion("model", [FalLipsyncParams]);
6567
6527
  var videoLipsyncNode = delegated({
6568
6528
  id: "video_lipsync",
6569
6529
  version: "1.0.0",
6570
6530
  category: "video",
6571
6531
  summary: "Lip-sync a video to an audio track. Currently backed by VEED via fal.ai (`fal/veed-lipsync`). $0.40/min of output.",
6572
- inputs: z31.object({
6532
+ inputs: z30.object({
6573
6533
  video: VideoRef,
6574
6534
  audio: AudioRef
6575
6535
  }).strict(),
6576
6536
  params: VideoLipsyncParams,
6577
- outputs: z31.object({ video: VideoRef }).strict(),
6537
+ outputs: z30.object({ video: VideoRef }).strict(),
6578
6538
  outputKinds: { video: "video" },
6579
6539
  cost: () => ({ credits: 20, seconds_estimate: 120 })
6580
6540
  });
@@ -6583,7 +6543,7 @@ var videoLipsyncNode = delegated({
6583
6543
  import { mkdtemp as mkdtemp6, readFile as readFile10, rm as rm6 } from "fs/promises";
6584
6544
  import { tmpdir as tmpdir6 } from "os";
6585
6545
  import path13 from "path";
6586
- import { z as z32 } from "zod";
6546
+ import { z as z31 } from "zod";
6587
6547
 
6588
6548
  // src/engine/nodes/local/lib/ffmpeg.ts
6589
6549
  import { execFile as execFile7 } from "child_process";
@@ -6662,21 +6622,21 @@ ${detail.slice(-4e3)}`);
6662
6622
  }
6663
6623
 
6664
6624
  // src/engine/nodes/remote/videoTranscribe.ts
6665
- var VideoTranscribeParams = z32.object({
6666
- language: z32.string().min(2).max(8).optional(),
6625
+ var VideoTranscribeParams = z31.object({
6626
+ language: z31.string().min(2).max(8).optional(),
6667
6627
  // Provider choice is explicit (no env-based silent branching). Default Groq
6668
6628
  // Whisper; "deepgram" routes to Deepgram Nova-3, which additionally emits a
6669
6629
  // `rich` JSON output with punctuated words + paragraph/sentence grouping.
6670
- transcriber: z32.enum(["groq", "deepgram"]).optional()
6630
+ transcriber: z31.enum(["groq", "deepgram"]).optional()
6671
6631
  }).strict();
6672
- var VideoTranscribeInputs = z32.object({
6632
+ var VideoTranscribeInputs = z31.object({
6673
6633
  video: VideoRef
6674
6634
  }).strict();
6675
- var VideoTranscribeOutputs = z32.object({
6676
- transcript: z32.custom(),
6635
+ var VideoTranscribeOutputs = z31.object({
6636
+ transcript: z31.custom(),
6677
6637
  // Only emitted by the Deepgram path: full punctuated words + paragraph /
6678
6638
  // sentence grouping with speaker indices. Absent for the default Groq path.
6679
- rich: z32.custom().optional()
6639
+ rich: z31.custom().optional()
6680
6640
  }).strict();
6681
6641
  var AUDIO_EXTRACT_TIMEOUT_MS = 6e4;
6682
6642
  var videoTranscribeNode = defineNode({
@@ -6761,29 +6721,29 @@ async function tryExtractAudio(inputs, ctx) {
6761
6721
  }
6762
6722
 
6763
6723
  // src/engine/nodes/remote/voiceSelect.ts
6764
- import { z as z33 } from "zod";
6724
+ import { z as z32 } from "zod";
6765
6725
  var voiceSelectNode = delegated({
6766
6726
  id: "voice_select",
6767
6727
  version: "1.0.0",
6768
6728
  category: "audio",
6769
6729
  summary: 'Cast an ElevenLabs voice from a natural-language description (e.g. "warm, authoritative female narrator, American accent"). Lists the account\'s voices and ranks them against the brief, emitting the best `voice_id` as a bare-string text asset plus a ranked `candidates` JSON.',
6770
6730
  when_to_use: 'Use to turn a voice description (e.g. from a `video_deconstruct` blueprint\'s `voice_description`) into a usable ElevenLabs voice id, then feed it into a `tts` node by wiring `inputs.voice_ref: $ref:<this>.voice_id` and setting `params.voice: "{{voice_ref}}"` \u2014 the engine splices the id in at run time. Review `candidates` (json) to pick a different voice. Optional `gender`/`age`/`accent`/`language` hints sharpen the ranking.',
6771
- inputs: z33.object({}).loose(),
6772
- params: z33.object({
6773
- description: z33.string().min(1),
6774
- gender: z33.string().optional(),
6775
- age: z33.string().optional(),
6776
- accent: z33.string().optional(),
6777
- language: z33.string().optional(),
6778
- limit: z33.number().int().min(1).max(20).optional()
6731
+ inputs: z32.object({}).loose(),
6732
+ params: z32.object({
6733
+ description: z32.string().min(1),
6734
+ gender: z32.string().optional(),
6735
+ age: z32.string().optional(),
6736
+ accent: z32.string().optional(),
6737
+ language: z32.string().optional(),
6738
+ limit: z32.number().int().min(1).max(20).optional()
6779
6739
  }).strict(),
6780
- outputs: z33.object({ voice_id: TextRef, candidates: JsonRef }).strict(),
6740
+ outputs: z32.object({ voice_id: TextRef, candidates: JsonRef }).strict(),
6781
6741
  outputKinds: { voice_id: "text", candidates: "json" },
6782
6742
  cost: () => ({ credits: 0, seconds_estimate: 5 })
6783
6743
  });
6784
6744
 
6785
6745
  // src/engine/schema/catalog.ts
6786
- import { z as z34 } from "zod";
6746
+ import { z as z33 } from "zod";
6787
6747
  function generateCatalog(registry, opts = {}) {
6788
6748
  const entries = registry.all().map((def) => {
6789
6749
  const cost = def.cost ? safeCost(def) : void 0;
@@ -6794,9 +6754,9 @@ function generateCatalog(registry, opts = {}) {
6794
6754
  summary: def.summary,
6795
6755
  when_to_use: def.when_to_use,
6796
6756
  location: def.location,
6797
- inputs: z34.toJSONSchema(def.inputs, { unrepresentable: "any" }),
6798
- params: z34.toJSONSchema(def.params, { unrepresentable: "any" }),
6799
- outputs: z34.toJSONSchema(def.outputs, { unrepresentable: "any" }),
6757
+ inputs: z33.toJSONSchema(def.inputs, { unrepresentable: "any" }),
6758
+ params: z33.toJSONSchema(def.params, { unrepresentable: "any" }),
6759
+ outputs: z33.toJSONSchema(def.outputs, { unrepresentable: "any" }),
6800
6760
  cost_estimate_credits: cost?.credits,
6801
6761
  runtime_estimate_seconds: cost?.seconds_estimate
6802
6762
  };
@@ -6873,8 +6833,7 @@ var LOCAL_NODES = [
6873
6833
  imagemagickNode,
6874
6834
  videoTranscribeNode,
6875
6835
  fontSpecimenNode,
6876
- audioTimelineNode,
6877
- collectNode
6836
+ audioTimelineNode
6878
6837
  ];
6879
6838
  var REMOTE_NODES = [
6880
6839
  textGenerateNode,
@@ -6955,4 +6914,4 @@ export {
6955
6914
  defaultRegistry,
6956
6915
  createEngineFromEnv
6957
6916
  };
6958
- //# sourceMappingURL=chunk-Q3K5TXC6.js.map
6917
+ //# sourceMappingURL=chunk-43KBQLP5.js.map