@koda-sl/baker-cli 0.192.3 → 0.193.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7552,20 +7552,50 @@ var imageDescribeNode = delegated({
7552
7552
  cost: () => ({ credits: 2, seconds_estimate: 10 })
7553
7553
  });
7554
7554
 
7555
- // src/engine/nodes/remote/imageReferenceSheet.ts
7555
+ // src/engine/nodes/remote/imageLayerize.ts
7556
7556
  import { z as z21 } from "zod";
7557
+ var ImageLayerizeParams = z21.object({
7558
+ /**
7559
+ * What to separate. Read as extraction instructions, not as a generation
7560
+ * prompt — "keep the bottle and its shadow as one layer", not "make it blue".
7561
+ */
7562
+ instructions: z21.string().max(600).optional()
7563
+ }).strict();
7564
+ var imageLayerizeNode = delegated({
7565
+ id: "image_layerize",
7566
+ version: "1.0.0",
7567
+ category: "image",
7568
+ summary: "Split an image into editable layers: a transparent PNG per element, plus any copy baked into the picture recovered as real text with its typography. Powered by Seedream 5.0 Pro Layerize, with Ideogram lifting the text off first.",
7569
+ when_to_use: "Use when you need ONE element of a picture on its own (a product with no background, a logo lockup), or when a shot is right but its copy is wrong and regenerating would change the picture too. The manifest gives you each layer's box and, for text, its exact wording, font, size and colour \u2014 enough to re-set a headline in the original's type. For a plain subject cutout with no text involved, `image_background_remove` is cheaper and one call.",
7570
+ inputs: z21.object({
7571
+ image: ImageRef
7572
+ }).strict(),
7573
+ params: ImageLayerizeParams,
7574
+ outputs: z21.object({
7575
+ layers: z21.array(ImageRef),
7576
+ base: ImageRef,
7577
+ manifest: JsonRef
7578
+ }).strict(),
7579
+ outputKinds: { layers: "image", base: "image", manifest: "json" },
7580
+ // Billed per returned layer (2-17) plus an optional text pass; the backend
7581
+ // charges the real count on completion. This is the pre-run estimate.
7582
+ cost: () => ({ credits: 30, seconds_estimate: 100 })
7583
+ });
7584
+
7585
+ // src/engine/nodes/remote/imageReferenceSheet.ts
7586
+ import { z as z22 } from "zod";
7557
7587
  var REFERENCE_SHEET_MODELS = ["google/gemini-3-pro-image-preview", "google/gemini-3.1-flash-image-preview"];
7558
- var ImageReferenceSheetParams = z21.object({
7559
- model: z21.enum(REFERENCE_SHEET_MODELS),
7560
- subject_description: z21.string().min(1),
7588
+ var ImageReferenceSheetParams = z22.object({
7589
+ model: z22.enum(REFERENCE_SHEET_MODELS),
7590
+ subject_description: z22.string().min(1),
7561
7591
  // `location` = a set/room shown from several camera ANGLES (not a rotated subject),
7562
7592
  // so a multi-scene shoot keeps one consistent set.
7563
- subject_type: z21.enum(["character", "person", "product", "location"]),
7564
- views: z21.array(z21.string().min(1)).min(2).max(8).optional(),
7565
- style: z21.string().optional(),
7566
- prompt_override: z21.string().min(1).optional(),
7567
- 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(),
7568
- image_size: z21.enum(OPENROUTER_IMAGE_SIZES).optional()
7593
+ subject_type: z22.enum(["character", "person", "product", "location"]),
7594
+ views: z22.array(z22.string().min(1)).min(2).max(8).optional(),
7595
+ style: z22.string().optional(),
7596
+ prompt_override: z22.string().min(1).optional(),
7597
+ aspect_ratio: z22.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(),
7598
+ image_size: z22.enum(OPENROUTER_IMAGE_SIZES).optional()
7569
7599
  }).strict();
7570
7600
  var imageReferenceSheetNode = delegated({
7571
7601
  id: "image_reference_sheet",
@@ -7573,9 +7603,9 @@ var imageReferenceSheetNode = delegated({
7573
7603
  category: "image",
7574
7604
  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).",
7575
7605
  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.",
7576
- inputs: z21.object({ references: z21.array(ImageRef).min(1).max(6) }).loose(),
7606
+ inputs: z22.object({ references: z22.array(ImageRef).min(1).max(6) }).loose(),
7577
7607
  params: ImageReferenceSheetParams,
7578
- outputs: z21.object({ sheet: ImageRef }).strict(),
7608
+ outputs: z22.object({ sheet: ImageRef }).strict(),
7579
7609
  outputKinds: { sheet: "image" },
7580
7610
  cost: ({ params }) => ({
7581
7611
  credits: params?.model === "google/gemini-3-pro-image-preview" ? 20 : 5,
@@ -7584,10 +7614,10 @@ var imageReferenceSheetNode = delegated({
7584
7614
  });
7585
7615
 
7586
7616
  // src/engine/nodes/remote/imageSearch.ts
7587
- import { z as z22 } from "zod";
7588
- var ImageSearchParams = z22.object({
7589
- prompt: z22.string().min(1),
7590
- count: z22.number().int().min(1).max(20).default(5)
7617
+ import { z as z23 } from "zod";
7618
+ var ImageSearchParams = z23.object({
7619
+ prompt: z23.string().min(1),
7620
+ count: z23.number().int().min(1).max(20).default(5)
7591
7621
  }).strict();
7592
7622
  var imageSearchNode = delegated({
7593
7623
  id: "image_search",
@@ -7595,15 +7625,15 @@ var imageSearchNode = delegated({
7595
7625
  category: "image",
7596
7626
  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.",
7597
7627
  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.",
7598
- inputs: z22.object({}).loose(),
7628
+ inputs: z23.object({}).loose(),
7599
7629
  params: ImageSearchParams,
7600
- outputs: z22.object({ images: z22.array(ImageRef).min(1) }).strict(),
7630
+ outputs: z23.object({ images: z23.array(ImageRef).min(1) }).strict(),
7601
7631
  outputKinds: { images: "image" },
7602
7632
  cost: ({ params }) => ({ credits: Math.ceil(2 + params.count / 2), seconds_estimate: 30 })
7603
7633
  });
7604
7634
 
7605
7635
  // src/engine/nodes/remote/imageSelect.ts
7606
- import { z as z23 } from "zod";
7636
+ import { z as z24 } from "zod";
7607
7637
  var IMAGE_SELECT_MODELS = ["~google/gemini-flash-latest", "~google/gemini-pro-latest"];
7608
7638
  var imageSelectNode = delegated({
7609
7639
  id: "image_select",
@@ -7611,15 +7641,15 @@ var imageSelectNode = delegated({
7611
7641
  category: "vision",
7612
7642
  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.",
7613
7643
  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.",
7614
- inputs: z23.object({ images: z23.array(ImageRef).min(2) }).loose(),
7615
- params: z23.object({
7616
- model: z23.enum(IMAGE_SELECT_MODELS),
7617
- prompt: z23.string().min(1),
7618
- count: z23.number().int().min(1).default(1),
7619
- temperature: z23.number().min(0).max(2).optional(),
7620
- max_tokens: z23.number().int().positive().optional()
7644
+ inputs: z24.object({ images: z24.array(ImageRef).min(2) }).loose(),
7645
+ params: z24.object({
7646
+ model: z24.enum(IMAGE_SELECT_MODELS),
7647
+ prompt: z24.string().min(1),
7648
+ count: z24.number().int().min(1).default(1),
7649
+ temperature: z24.number().min(0).max(2).optional(),
7650
+ max_tokens: z24.number().int().positive().optional()
7621
7651
  }).strict(),
7622
- outputs: z23.object({ images: z23.array(ImageRef).min(1), reasoning: TextRef }).strict(),
7652
+ outputs: z24.object({ images: z24.array(ImageRef).min(1), reasoning: TextRef }).strict(),
7623
7653
  outputKinds: { images: "image", reasoning: "text" },
7624
7654
  cost: () => ({ credits: 1, seconds_estimate: 5 }),
7625
7655
  // Arity is only knowable at validate time when `images` is a literal array
@@ -7644,34 +7674,34 @@ var imageSelectNode = delegated({
7644
7674
  });
7645
7675
 
7646
7676
  // src/engine/nodes/remote/music.ts
7647
- import { z as z24 } from "zod";
7677
+ import { z as z25 } from "zod";
7648
7678
  var MUSIC_MODELS = ["elevenlabs/music-v1", "elevenlabs/video-background-music-v1"];
7649
- var MusicParams = z24.object({
7650
- model: z24.enum(MUSIC_MODELS),
7679
+ var MusicParams = z25.object({
7680
+ model: z25.enum(MUSIC_MODELS),
7651
7681
  /** Free-form prompt. Used by `elevenlabs/music-v1` (compose-detailed). */
7652
- prompt: z24.string().optional(),
7682
+ prompt: z25.string().optional(),
7653
7683
  /**
7654
7684
  * Structured composition plan (intro / hook / verse / outro sections with
7655
7685
  * per-section styles + durations). Mutually exclusive with `prompt`.
7656
7686
  */
7657
- composition_plan: z24.record(z24.string(), z24.unknown()).optional(),
7687
+ composition_plan: z25.record(z25.string(), z25.unknown()).optional(),
7658
7688
  /** Target length when using `prompt`. 3000–454545ms (capped by the $10 per-node cost limit). */
7659
- music_length_ms: z24.number().int().min(3e3).max(ELEVENLABS_MAX_MUSIC_LENGTH_MS).optional(),
7660
- seed: z24.number().int().optional(),
7689
+ music_length_ms: z25.number().int().min(3e3).max(ELEVENLABS_MAX_MUSIC_LENGTH_MS).optional(),
7690
+ seed: z25.number().int().optional(),
7661
7691
  /** Prompt mode only — forces an instrumental (no vocals) track. */
7662
- force_instrumental: z24.boolean().optional(),
7692
+ force_instrumental: z25.boolean().optional(),
7663
7693
  /** composition_plan only — honor exact section durations. */
7664
- respect_sections_durations: z24.boolean().optional(),
7694
+ respect_sections_durations: z25.boolean().optional(),
7665
7695
  /** Emit word-level timestamps alongside the audio. */
7666
- with_timestamps: z24.boolean().optional(),
7696
+ with_timestamps: z25.boolean().optional(),
7667
7697
  /**
7668
7698
  * video-to-music only — short description of the desired score
7669
7699
  * ("upbeat synth, fast cuts, 80s") used to bias the model.
7670
7700
  */
7671
- description: z24.string().max(1e3).optional(),
7701
+ description: z25.string().max(1e3).optional(),
7672
7702
  /** video-to-music only — up to 10 style tags. */
7673
- tags: z24.array(z24.string()).max(10).optional(),
7674
- output_format: z24.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
7703
+ tags: z25.array(z25.string()).max(10).optional(),
7704
+ output_format: z25.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
7675
7705
  }).strict();
7676
7706
  var musicNode = delegated({
7677
7707
  id: "music",
@@ -7679,9 +7709,9 @@ var musicNode = delegated({
7679
7709
  category: "audio",
7680
7710
  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`.",
7681
7711
  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.",
7682
- inputs: z24.object({ video: VideoRef.optional() }).loose(),
7712
+ inputs: z25.object({ video: VideoRef.optional() }).loose(),
7683
7713
  params: MusicParams,
7684
- outputs: z24.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
7714
+ outputs: z25.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
7685
7715
  outputKinds: { audio: "audio", timestamps: "json" },
7686
7716
  cost: ({ params }) => {
7687
7717
  const seconds = params.music_length_ms ? Math.ceil(params.music_length_ms / 1e3) : 30;
@@ -7712,25 +7742,25 @@ var musicNode = delegated({
7712
7742
  });
7713
7743
 
7714
7744
  // src/engine/nodes/remote/soundEffect.ts
7715
- import { z as z25 } from "zod";
7745
+ import { z as z26 } from "zod";
7716
7746
  var SOUND_EFFECT_MODELS = ["elevenlabs/eleven_text_to_sound_v2"];
7717
- var SoundEffectParams = z25.object({
7718
- model: z25.enum(SOUND_EFFECT_MODELS),
7747
+ var SoundEffectParams = z26.object({
7748
+ model: z26.enum(SOUND_EFFECT_MODELS),
7719
7749
  /** Prompt describing the SFX ("metal door slam", "soft UI tap", "ocean waves"). */
7720
- text: z25.string().min(1),
7750
+ text: z26.string().min(1),
7721
7751
  /**
7722
7752
  * Target length in seconds. 0.5–30. Leave unset to let the model pick the
7723
7753
  * natural length for the described effect.
7724
7754
  */
7725
- duration_seconds: z25.number().min(0.5).max(30).optional(),
7755
+ duration_seconds: z26.number().min(0.5).max(30).optional(),
7726
7756
  /**
7727
7757
  * 0–1. Higher = stick closer to the prompt at the cost of variety; lower
7728
7758
  * = let the model interpret more freely. Defaults to 0.3 on the provider.
7729
7759
  */
7730
- prompt_influence: z25.number().min(0).max(1).optional(),
7760
+ prompt_influence: z26.number().min(0).max(1).optional(),
7731
7761
  /** Only valid on `eleven_text_to_sound_v2` — produce a seamless loop. */
7732
- loop: z25.boolean().optional(),
7733
- output_format: z25.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
7762
+ loop: z26.boolean().optional(),
7763
+ output_format: z26.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
7734
7764
  }).strict();
7735
7765
  var soundEffectNode = delegated({
7736
7766
  id: "sound_effect",
@@ -7738,9 +7768,9 @@ var soundEffectNode = delegated({
7738
7768
  category: "audio",
7739
7769
  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.",
7740
7770
  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.",
7741
- inputs: z25.object({}).loose(),
7771
+ inputs: z26.object({}).loose(),
7742
7772
  params: SoundEffectParams,
7743
- outputs: z25.object({ audio: AudioRef }).strict(),
7773
+ outputs: z26.object({ audio: AudioRef }).strict(),
7744
7774
  outputKinds: { audio: "audio" },
7745
7775
  cost: ({ params }) => {
7746
7776
  const seconds = params.duration_seconds ?? 5;
@@ -7749,7 +7779,7 @@ var soundEffectNode = delegated({
7749
7779
  });
7750
7780
 
7751
7781
  // src/engine/nodes/remote/textGenerate.ts
7752
- import { z as z26 } from "zod";
7782
+ import { z as z27 } from "zod";
7753
7783
  var TEXT_GENERATE_MODELS = ["~google/gemini-flash-latest", "~google/gemini-pro-latest"];
7754
7784
  var textGenerateNode = delegated({
7755
7785
  id: "text_generate",
@@ -7757,58 +7787,58 @@ var textGenerateNode = delegated({
7757
7787
  category: "language",
7758
7788
  summary: "Single-turn LLM text generation via OpenRouter. Returns a text response.",
7759
7789
  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.',
7760
- inputs: z26.object({}).loose(),
7761
- params: z26.object({
7762
- model: z26.enum(TEXT_GENERATE_MODELS),
7763
- prompt: z26.string().min(1),
7764
- system: z26.string().optional(),
7765
- response_format: z26.enum(["text", "json_object"]).optional(),
7766
- web_search: z26.boolean().optional(),
7767
- temperature: z26.number().min(0).max(2).optional(),
7768
- max_tokens: z26.number().int().positive().optional()
7790
+ inputs: z27.object({}).loose(),
7791
+ params: z27.object({
7792
+ model: z27.enum(TEXT_GENERATE_MODELS),
7793
+ prompt: z27.string().min(1),
7794
+ system: z27.string().optional(),
7795
+ response_format: z27.enum(["text", "json_object"]).optional(),
7796
+ web_search: z27.boolean().optional(),
7797
+ temperature: z27.number().min(0).max(2).optional(),
7798
+ max_tokens: z27.number().int().positive().optional()
7769
7799
  }).strict(),
7770
- outputs: z26.object({ text: TextRef }).strict(),
7800
+ outputs: z27.object({ text: TextRef }).strict(),
7771
7801
  outputKinds: { text: "text" },
7772
7802
  cost: () => ({ credits: 1, seconds_estimate: 3 })
7773
7803
  });
7774
7804
 
7775
7805
  // src/engine/nodes/remote/tts.ts
7776
- import { z as z27 } from "zod";
7806
+ import { z as z28 } from "zod";
7777
7807
  var TTS_MODELS = ["elevenlabs/eleven_v3"];
7778
- var TtsVoiceSettings = z27.object({
7779
- stability: z27.number().min(0).max(1).optional(),
7780
- similarity_boost: z27.number().min(0).max(1).optional(),
7781
- style: z27.number().min(0).max(1).optional(),
7782
- use_speaker_boost: z27.boolean().optional(),
7783
- speed: z27.number().min(0.25).max(4).optional()
7808
+ var TtsVoiceSettings = z28.object({
7809
+ stability: z28.number().min(0).max(1).optional(),
7810
+ similarity_boost: z28.number().min(0).max(1).optional(),
7811
+ style: z28.number().min(0).max(1).optional(),
7812
+ use_speaker_boost: z28.boolean().optional(),
7813
+ speed: z28.number().min(0.25).max(4).optional()
7784
7814
  }).strict();
7785
- var TtsPronunciationLocator = z27.object({
7786
- pronunciation_dictionary_id: z27.string().min(1),
7787
- version_id: z27.string().nullable().optional()
7815
+ var TtsPronunciationLocator = z28.object({
7816
+ pronunciation_dictionary_id: z28.string().min(1),
7817
+ version_id: z28.string().nullable().optional()
7788
7818
  }).strict();
7789
- var TtsParams = z27.object({
7790
- model: z27.enum(TTS_MODELS),
7791
- text: z27.string().min(1).max(ELEVENLABS_MAX_TEXT_CHARS),
7792
- voice: z27.string().min(1),
7819
+ var TtsParams = z28.object({
7820
+ model: z28.enum(TTS_MODELS),
7821
+ text: z28.string().min(1).max(ELEVENLABS_MAX_TEXT_CHARS),
7822
+ voice: z28.string().min(1),
7793
7823
  /** Provider output_format (mp3 family only — assets are stored as audio/mpeg). */
7794
- output_format: z27.enum(ELEVENLABS_OUTPUT_FORMATS).optional(),
7795
- seed: z27.number().int().min(0).max(4294967295).optional(),
7824
+ output_format: z28.enum(ELEVENLABS_OUTPUT_FORMATS).optional(),
7825
+ seed: z28.number().int().min(0).max(4294967295).optional(),
7796
7826
  // Top-level shortcuts; structured form is `voice_settings`.
7797
- stability: z27.number().min(0).max(1).optional(),
7798
- similarity_boost: z27.number().min(0).max(1).optional(),
7827
+ stability: z28.number().min(0).max(1).optional(),
7828
+ similarity_boost: z28.number().min(0).max(1).optional(),
7799
7829
  voice_settings: TtsVoiceSettings.optional(),
7800
7830
  /** ISO 639-1 language code. eleven_v3 supports language hints. */
7801
- language_code: z27.string().optional(),
7802
- pronunciation_dictionary_locators: z27.array(TtsPronunciationLocator).max(3).optional(),
7803
- apply_text_normalization: z27.enum(["auto", "on", "off"]).optional(),
7831
+ language_code: z28.string().optional(),
7832
+ pronunciation_dictionary_locators: z28.array(TtsPronunciationLocator).max(3).optional(),
7833
+ apply_text_normalization: z28.enum(["auto", "on", "off"]).optional(),
7804
7834
  /** Currently Japanese-only. Adds latency. */
7805
- apply_language_text_normalization: z27.boolean().optional(),
7835
+ apply_language_text_normalization: z28.boolean().optional(),
7806
7836
  /**
7807
7837
  * When true, hits `/v1/text-to-speech/{voice_id}/with-timestamps` and
7808
7838
  * adds a `timestamps` output (character-level alignment) for caption
7809
7839
  * rendering, lipsync, and beat-matched cuts.
7810
7840
  */
7811
- with_timestamps: z27.boolean().optional()
7841
+ with_timestamps: z28.boolean().optional()
7812
7842
  }).strict();
7813
7843
  var ttsNode = delegated({
7814
7844
  id: "tts",
@@ -7816,9 +7846,9 @@ var ttsNode = delegated({
7816
7846
  category: "audio",
7817
7847
  summary: "Single-voice text-to-speech via ElevenLabs Eleven v3. Optional character-level timestamps for caption rendering and beat-matched cuts.",
7818
7848
  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).",
7819
- inputs: z27.object({}).loose(),
7849
+ inputs: z28.object({}).loose(),
7820
7850
  params: TtsParams,
7821
- outputs: z27.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
7851
+ outputs: z28.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
7822
7852
  outputKinds: { audio: "audio", timestamps: "json" },
7823
7853
  cost: ({ params }) => ({
7824
7854
  credits: Math.max(1, Math.ceil(params.text.length * 15e-4)),
@@ -7827,25 +7857,25 @@ var ttsNode = delegated({
7827
7857
  });
7828
7858
 
7829
7859
  // src/engine/nodes/remote/video.ts
7830
- import { z as z28 } from "zod";
7831
- var videoModelEnum = z28.enum(VIDEO_GENERATE_MODELS);
7832
- var VideoGenerateParams = z28.object({
7860
+ import { z as z29 } from "zod";
7861
+ var videoModelEnum = z29.enum(VIDEO_GENERATE_MODELS);
7862
+ var VideoGenerateParams = z29.object({
7833
7863
  model: videoModelEnum,
7834
- prompt: z28.string().min(1),
7835
- duration: z28.number().int().positive().optional(),
7836
- resolution: z28.string().optional(),
7864
+ prompt: z29.string().min(1),
7865
+ duration: z29.number().int().positive().optional(),
7866
+ resolution: z29.string().optional(),
7837
7867
  // Union of ratios accepted by at least one curated model (registry gates
7838
7868
  // per-model). 3:2/2:3 are deliberately absent: no registered model takes them.
7839
- aspect_ratio: z28.enum(["16:9", "9:16", "1:1", "4:3", "3:4", "21:9", "9:21"]).optional(),
7840
- generate_audio: z28.boolean().optional(),
7841
- seed: z28.number().int().nonnegative().optional(),
7869
+ aspect_ratio: z29.enum(["16:9", "9:16", "1:1", "4:3", "3:4", "21:9", "9:21"]).optional(),
7870
+ generate_audio: z29.boolean().optional(),
7871
+ seed: z29.number().int().nonnegative().optional(),
7842
7872
  // Veo-only passthroughs (routed via `provider.options.google-vertex.parameters`).
7843
- negative_prompt: z28.string().optional(),
7844
- person_generation: z28.string().optional(),
7845
- enhance_prompt: z28.boolean().optional(),
7846
- conditioning_scale: z28.number().optional(),
7873
+ negative_prompt: z29.string().optional(),
7874
+ person_generation: z29.string().optional(),
7875
+ enhance_prompt: z29.boolean().optional(),
7876
+ conditioning_scale: z29.number().optional(),
7847
7877
  // Kling-only passthrough (prompt-adherence dial, sent top-level).
7848
- cfg_scale: z28.number().optional()
7878
+ cfg_scale: z29.number().optional()
7849
7879
  }).strict();
7850
7880
  var videoGenerateNode = delegated({
7851
7881
  id: "video_generate",
@@ -7853,7 +7883,7 @@ var videoGenerateNode = delegated({
7853
7883
  category: "video",
7854
7884
  summary: "Generate video for ad creatives. Curated roster: `google/gemini-omni-flash` (DEFAULT \u2014 native audio + reference images in one call, 720p, 3-10s), `bytedance/seedance-2.0` (identity/product workhorse, NO photoreal human faces), `google/veo-3.1` (photoreal cine ceiling + the human-face model), `google/veo-3.1-fast` (cheap Veo iteration), `kwaivgi/kling-3.0` (motion-transfer/dynamic). Async with polling.",
7855
7885
  when_to_use: "`google/gemini-omni-flash` is the DEFAULT: it renders picture and synchronized audio in one call, and is the only model here that takes reference images alongside a prompt. SET `duration` (3-10s) to the length the beat actually needs \u2014 it bills ~$0.101 PER SECOND, so leaving it out renders the 10s default and charges ~$1.01 for a beat that may need 3s (~$0.30). Its resolution is fixed at 720p and it accepts only `16:9` or `9:16`; it takes NO `resolution`, `seed`, `negative_prompt` or `generate_audio` param \u2014 sending one fails validation. Keep the default for ordinary beats; move off it when a beat needs 1080p, a length outside 3-10s, or a 1:1 frame. Use `bytedance/seedance-2.0` for identity/product output WITHOUT a photoreal human face on camera. Any clip whose frame shows a photoreal human face \u2014 a UGC creator, presenter, or testimonial speaker, REAL OR AI-GENERATED \u2014 must use `google/veo-3.1`: ByteDance's filter rejects photoreal human faces on Seedance (content_policy_blocked/E005), and a synthetic AI face does NOT clear it. Use `google/veo-3.1-fast` while iterating to keep cost low; `kwaivgi/kling-3.0` for motion-transfer/hyper-dynamic beats. `baker canvas scaffold-video` auto-routes a photoreal cast to Veo; a Seedance clip blocked at runtime also auto-recovers on Veo. Each model gates its own durations/resolutions/aspect ratios in the registry \u2014 see the README per-model section.",
7856
- inputs: z28.object({
7886
+ inputs: z29.object({
7857
7887
  first_frame: ImageRef.optional(),
7858
7888
  last_frame: ImageRef.optional(),
7859
7889
  reference: ImageRef.optional(),
@@ -7863,10 +7893,10 @@ var videoGenerateNode = delegated({
7863
7893
  * exclusive with `first_frame` — OpenRouter treats a request carrying both
7864
7894
  * as image-to-video and ignores these, so wire one or the other.
7865
7895
  */
7866
- references: z28.array(ImageRef).optional()
7896
+ references: z29.array(ImageRef).optional()
7867
7897
  }).loose(),
7868
7898
  params: VideoGenerateParams,
7869
- outputs: z28.object({ video: VideoRef }).strict(),
7899
+ outputs: z29.object({ video: VideoRef }).strict(),
7870
7900
  outputKinds: { video: "video" },
7871
7901
  // Priced from the shared contract, so `validate`'s quote and the charge the
7872
7902
  // backend applies come from ONE table. A flat number here under-quoted a
@@ -7886,11 +7916,11 @@ var videoGenerateNode = delegated({
7886
7916
  });
7887
7917
 
7888
7918
  // src/engine/nodes/remote/videoBackgroundRemove.ts
7889
- import { z as z29 } from "zod";
7890
- var VideoBackgroundRemoveParams = z29.object({
7891
- model: z29.literal("fal/veed-video-background-removal").optional().default("fal/veed-video-background-removal"),
7892
- edge_refinement: z29.boolean().optional().default(true),
7893
- output_codec: z29.enum(["vp9", "h264"]).optional().default("vp9")
7919
+ import { z as z30 } from "zod";
7920
+ var VideoBackgroundRemoveParams = z30.object({
7921
+ model: z30.literal("fal/veed-video-background-removal").optional().default("fal/veed-video-background-removal"),
7922
+ edge_refinement: z30.boolean().optional().default(true),
7923
+ output_codec: z30.enum(["vp9", "h264"]).optional().default("vp9")
7894
7924
  }).strict();
7895
7925
  var videoBackgroundRemoveNode = delegated({
7896
7926
  id: "video_background_remove",
@@ -7898,18 +7928,18 @@ var videoBackgroundRemoveNode = delegated({
7898
7928
  category: "video",
7899
7929
  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`.",
7900
7930
  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.",
7901
- inputs: z29.object({
7931
+ inputs: z30.object({
7902
7932
  video: VideoRef
7903
7933
  }).strict(),
7904
7934
  params: VideoBackgroundRemoveParams,
7905
- outputs: z29.object({ video: VideoRef }).strict(),
7935
+ outputs: z30.object({ video: VideoRef }).strict(),
7906
7936
  outputKinds: { video: "video" },
7907
7937
  // $0.012 per 30 frames (edge refinement on) — assume ~30fps; refine via fal dashboard.
7908
7938
  cost: () => ({ credits: 50, seconds_estimate: 60 })
7909
7939
  });
7910
7940
 
7911
7941
  // src/engine/nodes/remote/videoDeconstruct.ts
7912
- import { z as z30 } from "zod";
7942
+ import { z as z31 } from "zod";
7913
7943
  var VIDEO_DECONSTRUCT_MODELS = ["~google/gemini-flash-latest", "~google/gemini-pro-latest"];
7914
7944
  var videoDeconstructNode = delegated({
7915
7945
  id: "video_deconstruct",
@@ -7917,34 +7947,34 @@ var videoDeconstructNode = delegated({
7917
7947
  category: "video",
7918
7948
  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).',
7919
7949
  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.',
7920
- inputs: z30.object({ video: VideoRef }).loose(),
7921
- params: z30.object({
7922
- model: z30.enum(VIDEO_DECONSTRUCT_MODELS),
7923
- mode: z30.enum(["full", "index"]).optional(),
7924
- language: z30.string().min(2).max(8).optional(),
7925
- max_scenes: z30.number().int().min(1).max(60).optional(),
7926
- focus: z30.string().optional(),
7927
- start_s: z30.number().min(0).optional(),
7928
- end_s: z30.number().positive().optional(),
7950
+ inputs: z31.object({ video: VideoRef }).loose(),
7951
+ params: z31.object({
7952
+ model: z31.enum(VIDEO_DECONSTRUCT_MODELS),
7953
+ mode: z31.enum(["full", "index"]).optional(),
7954
+ language: z31.string().min(2).max(8).optional(),
7955
+ max_scenes: z31.number().int().min(1).max(60).optional(),
7956
+ focus: z31.string().optional(),
7957
+ start_s: z31.number().min(0).optional(),
7958
+ end_s: z31.number().positive().optional(),
7929
7959
  // Real visual shot-cut timestamps (absolute seconds), detected locally with
7930
7960
  // ffmpeg before the deconstruct. The backend SNAPS its LLM scene boundaries
7931
7961
  // onto these and SPLITS any scene that spans one, so a scene's frames never
7932
7962
  // straddle a hard cut. `scaffold-video` populates this; omit for LLM-only cuts.
7933
- shot_cuts: z30.array(z30.number().min(0)).max(200).optional(),
7963
+ shot_cuts: z31.array(z31.number().min(0)).max(200).optional(),
7934
7964
  // The video model's per-clip ceiling (seconds). A shot longer than this is
7935
7965
  // split into seamless continuation sub-scenes (shared splice frame), so long
7936
7966
  // shots reproduce in full instead of being truncated. `scaffold-video` sets
7937
7967
  // the Seedance ceiling (15); omit to disable length splitting.
7938
- max_clip_s: z30.number().positive().max(60).optional(),
7968
+ max_clip_s: z31.number().positive().max(60).optional(),
7939
7969
  // Transcript provider for the blueprint's dialogue/transcript. Default
7940
7970
  // Groq Whisper; "deepgram" routes to Nova-3 so words carry punctuation.
7941
- transcriber: z30.enum(["groq", "deepgram"]).optional()
7971
+ transcriber: z31.enum(["groq", "deepgram"]).optional()
7942
7972
  }).strict(),
7943
- outputs: z30.object({
7973
+ outputs: z31.object({
7944
7974
  analysis: JsonRef,
7945
7975
  // Absent in mode:"index" (structure only, no Mux frame extraction).
7946
- start_frames: z30.array(ImageRef).min(1).optional(),
7947
- end_frames: z30.array(ImageRef).min(1).optional(),
7976
+ start_frames: z31.array(ImageRef).min(1).optional(),
7977
+ end_frames: z31.array(ImageRef).min(1).optional(),
7948
7978
  transcript: JsonRef
7949
7979
  }).strict(),
7950
7980
  outputKinds: { analysis: "json", start_frames: "image", end_frames: "image", transcript: "json" },
@@ -7952,22 +7982,22 @@ var videoDeconstructNode = delegated({
7952
7982
  });
7953
7983
 
7954
7984
  // src/engine/nodes/remote/videoLipsync.ts
7955
- import { z as z31 } from "zod";
7956
- var FalLipsyncParams = z31.object({
7957
- model: z31.literal("fal/veed-lipsync")
7985
+ import { z as z32 } from "zod";
7986
+ var FalLipsyncParams = z32.object({
7987
+ model: z32.literal("fal/veed-lipsync")
7958
7988
  }).strict();
7959
- var VideoLipsyncParams = z31.discriminatedUnion("model", [FalLipsyncParams]);
7989
+ var VideoLipsyncParams = z32.discriminatedUnion("model", [FalLipsyncParams]);
7960
7990
  var videoLipsyncNode = delegated({
7961
7991
  id: "video_lipsync",
7962
7992
  version: "1.0.0",
7963
7993
  category: "video",
7964
7994
  summary: "Lip-sync a video to an audio track. Currently backed by VEED via fal.ai (`fal/veed-lipsync`). $0.40/min of output.",
7965
- inputs: z31.object({
7995
+ inputs: z32.object({
7966
7996
  video: VideoRef,
7967
7997
  audio: AudioRef
7968
7998
  }).strict(),
7969
7999
  params: VideoLipsyncParams,
7970
- outputs: z31.object({ video: VideoRef }).strict(),
8000
+ outputs: z32.object({ video: VideoRef }).strict(),
7971
8001
  outputKinds: { video: "video" },
7972
8002
  cost: () => ({ credits: 20, seconds_estimate: 120 })
7973
8003
  });
@@ -7976,7 +8006,7 @@ var videoLipsyncNode = delegated({
7976
8006
  import { mkdtemp as mkdtemp6, readFile as readFile11, rm as rm6 } from "fs/promises";
7977
8007
  import { tmpdir as tmpdir6 } from "os";
7978
8008
  import path14 from "path";
7979
- import { z as z32 } from "zod";
8009
+ import { z as z33 } from "zod";
7980
8010
 
7981
8011
  // src/engine/nodes/local/lib/ffmpeg.ts
7982
8012
  import { execFile as execFile7 } from "child_process";
@@ -8055,24 +8085,24 @@ ${detail.slice(-4e3)}`);
8055
8085
  }
8056
8086
 
8057
8087
  // src/engine/nodes/remote/videoTranscribe.ts
8058
- var VideoTranscribeParams = z32.object({
8059
- language: z32.string().min(2).max(8).optional(),
8088
+ var VideoTranscribeParams = z33.object({
8089
+ language: z33.string().min(2).max(8).optional(),
8060
8090
  // Provider choice is explicit (no env-based silent branching). Default Groq
8061
8091
  // Whisper; "deepgram" routes to Deepgram Nova-3, which additionally emits a
8062
8092
  // `rich` JSON output with punctuated words + paragraph/sentence grouping.
8063
- transcriber: z32.enum(["groq", "deepgram"]).optional()
8093
+ transcriber: z33.enum(["groq", "deepgram"]).optional()
8064
8094
  }).strict();
8065
- var VideoTranscribeInputs = z32.object({
8095
+ var VideoTranscribeInputs = z33.object({
8066
8096
  // A video (audio auto-extracted locally) OR a bare audio track. The key stays
8067
8097
  // `video` for back-compat; the backend already accepts audio-kind refs on it —
8068
8098
  // the local extraction path has been shipping one for every video input.
8069
- video: z32.union([VideoRef, AudioRef])
8099
+ video: z33.union([VideoRef, AudioRef])
8070
8100
  }).strict();
8071
- var VideoTranscribeOutputs = z32.object({
8072
- transcript: z32.custom(),
8101
+ var VideoTranscribeOutputs = z33.object({
8102
+ transcript: z33.custom(),
8073
8103
  // Only emitted by the Deepgram path: full punctuated words + paragraph /
8074
8104
  // sentence grouping with speaker indices. Absent for the default Groq path.
8075
- rich: z32.custom().optional()
8105
+ rich: z33.custom().optional()
8076
8106
  }).strict();
8077
8107
  var AUDIO_EXTRACT_TIMEOUT_MS = 6e4;
8078
8108
  var videoTranscribeNode = defineNode({
@@ -8157,29 +8187,29 @@ async function tryExtractAudio(inputs, ctx) {
8157
8187
  }
8158
8188
 
8159
8189
  // src/engine/nodes/remote/voiceSelect.ts
8160
- import { z as z33 } from "zod";
8190
+ import { z as z34 } from "zod";
8161
8191
  var voiceSelectNode = delegated({
8162
8192
  id: "voice_select",
8163
8193
  version: "1.0.0",
8164
8194
  category: "audio",
8165
8195
  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.',
8166
8196
  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.',
8167
- inputs: z33.object({}).loose(),
8168
- params: z33.object({
8169
- description: z33.string().min(1),
8170
- gender: z33.string().optional(),
8171
- age: z33.string().optional(),
8172
- accent: z33.string().optional(),
8173
- language: z33.string().optional(),
8174
- limit: z33.number().int().min(1).max(20).optional()
8197
+ inputs: z34.object({}).loose(),
8198
+ params: z34.object({
8199
+ description: z34.string().min(1),
8200
+ gender: z34.string().optional(),
8201
+ age: z34.string().optional(),
8202
+ accent: z34.string().optional(),
8203
+ language: z34.string().optional(),
8204
+ limit: z34.number().int().min(1).max(20).optional()
8175
8205
  }).strict(),
8176
- outputs: z33.object({ voice_id: TextRef, candidates: JsonRef }).strict(),
8206
+ outputs: z34.object({ voice_id: TextRef, candidates: JsonRef }).strict(),
8177
8207
  outputKinds: { voice_id: "text", candidates: "json" },
8178
8208
  cost: () => ({ credits: 0, seconds_estimate: 5 })
8179
8209
  });
8180
8210
 
8181
8211
  // src/engine/schema/catalog.ts
8182
- import { z as z34 } from "zod";
8212
+ import { z as z35 } from "zod";
8183
8213
  function generateCatalog(registry, opts = {}) {
8184
8214
  const entries = registry.all().map((def) => {
8185
8215
  const cost = def.cost ? safeCost(def) : void 0;
@@ -8190,9 +8220,9 @@ function generateCatalog(registry, opts = {}) {
8190
8220
  summary: def.summary,
8191
8221
  when_to_use: def.when_to_use,
8192
8222
  location: def.location,
8193
- inputs: z34.toJSONSchema(def.inputs, { unrepresentable: "any" }),
8194
- params: z34.toJSONSchema(def.params, { unrepresentable: "any" }),
8195
- outputs: z34.toJSONSchema(def.outputs, { unrepresentable: "any" }),
8223
+ inputs: z35.toJSONSchema(def.inputs, { unrepresentable: "any" }),
8224
+ params: z35.toJSONSchema(def.params, { unrepresentable: "any" }),
8225
+ outputs: z35.toJSONSchema(def.outputs, { unrepresentable: "any" }),
8196
8226
  cost_estimate_credits: cost?.credits,
8197
8227
  runtime_estimate_seconds: cost?.seconds_estimate
8198
8228
  };
@@ -8290,7 +8320,8 @@ var REMOTE_NODES = [
8290
8320
  videoBackgroundRemoveNode,
8291
8321
  videoDeconstructNode,
8292
8322
  voiceSelectNode,
8293
- imageBackgroundRemoveNode
8323
+ imageBackgroundRemoveNode,
8324
+ imageLayerizeNode
8294
8325
  ];
8295
8326
  function defaultRegistry() {
8296
8327
  const r = new NodeRegistry();
@@ -8376,4 +8407,4 @@ export {
8376
8407
  defaultRegistry,
8377
8408
  createEngineFromEnv
8378
8409
  };
8379
- //# sourceMappingURL=chunk-ISZWNERZ.js.map
8410
+ //# sourceMappingURL=chunk-O5GXFPWC.js.map