@vibeframe/cli 0.113.24 → 0.113.26

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.
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @module _shared/frontmatter
3
+ *
4
+ * One canonical "YAML frontmatter + markdown body" splitter for all VibeFrame
5
+ * `.md` files. Previously each consumer (storyboard, design, install-skill)
6
+ * declared its own `/^---\n…\n---/` regex + `yaml.parse` — this consolidates
7
+ * them.
8
+ *
9
+ * The shape matches Google's Open Knowledge Format (OKF) and Mintlify: a leading
10
+ * `---` YAML block, then a free-form markdown body. VibeFrame project files
11
+ * (STORYBOARD.md, DESIGN.md, CHARACTERS.md, …) are OKF concept documents; docs/
12
+ * pages carry OKF/Mintlify frontmatter (`type`, `title`, `description`, `tags`).
13
+ */
14
+ /** Leading `---\n…\n---` block. Anchored to the start of the (normalized) text. */
15
+ export declare const FRONTMATTER_RE: RegExp;
16
+ export interface ExtractedFrontmatter {
17
+ /** Parsed frontmatter map, or `null` when absent / invalid / not a map. */
18
+ data: Record<string, unknown> | null;
19
+ /** Markdown body after the frontmatter block (or the whole doc when absent). */
20
+ body: string;
21
+ /** The full document, CRLF-normalized to LF. */
22
+ raw: string;
23
+ }
24
+ /**
25
+ * Split a leading YAML frontmatter block from the markdown body. CRLF is
26
+ * normalized to LF. Returns `data: null` (freeform) when there is no leading
27
+ * block, the YAML is invalid, or it is not a plain object — callers decide
28
+ * whether that means `undefined` or `{}`.
29
+ */
30
+ export declare function extractFrontmatter(md: string): ExtractedFrontmatter;
@@ -14,7 +14,7 @@
14
14
  */
15
15
  export interface VideoGenerateOptions {
16
16
  prompt: string;
17
- provider?: "grok" | "runway" | "kling" | "veo" | "seedance" | "fal";
17
+ provider?: "grok" | "runway" | "kling" | "veo" | "seedance" | "fal" | "omni";
18
18
  image?: string;
19
19
  refImages?: string[];
20
20
  refVideos?: string[];
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * @module generate/video
3
3
  * @description `vibe generate video` (alias `vid`) — multi-provider video
4
- * generation. fal.ai (Seedance 2.0), Grok, Veo (Gemini), Kling, Runway.
4
+ * generation. fal.ai (Seedance 2.0), Grok, Veo (Gemini), Kling, Runway,
5
+ * plus experimental Gemini Omni (`-p omni`).
5
6
  * Split out of `generate.ts` in v0.69 (Plan G Phase 2).
6
7
  */
7
8
  import type { Command } from "commander";
package/dist/index.js CHANGED
@@ -8609,6 +8609,113 @@ Respond with ONLY the JSON array, no other text.`;
8609
8609
  }
8610
8610
  });
8611
8611
 
8612
+ // ../ai-providers/dist/gemini/gemini-omni.js
8613
+ function firstReferenceImage(options) {
8614
+ const refs = options.referenceImages;
8615
+ if (refs && refs.length > 0 && refs[0].base64)
8616
+ return refs[0];
8617
+ const ref = options.referenceImage;
8618
+ if (typeof ref === "string" && ref) {
8619
+ const m = ref.match(/^data:(.+?);base64,(.*)$/);
8620
+ if (m)
8621
+ return { mimeType: m[1], base64: m[2] };
8622
+ return { mimeType: "image/png", base64: ref };
8623
+ }
8624
+ return void 0;
8625
+ }
8626
+ function findVideoUrl(obj) {
8627
+ if (!obj || typeof obj !== "object")
8628
+ return void 0;
8629
+ for (const [k, v] of Object.entries(obj)) {
8630
+ if (typeof v === "string" && /^https?:\/\//.test(v) && /(uri|url|video)/i.test(k))
8631
+ return v;
8632
+ if (typeof v === "object") {
8633
+ const nested = findVideoUrl(v);
8634
+ if (nested)
8635
+ return nested;
8636
+ }
8637
+ }
8638
+ return void 0;
8639
+ }
8640
+ var OMNI_MODEL, INTERACTIONS_URL, OmniProvider, omniProvider;
8641
+ var init_gemini_omni = __esm({
8642
+ "../ai-providers/dist/gemini/gemini-omni.js"() {
8643
+ "use strict";
8644
+ OMNI_MODEL = "gemini-omni-flash-preview";
8645
+ INTERACTIONS_URL = "https://generativelanguage.googleapis.com/v1beta/interactions";
8646
+ OmniProvider = class {
8647
+ constructor() {
8648
+ this.id = "omni";
8649
+ this.label = "Gemini Omni (experimental)";
8650
+ }
8651
+ async initialize(config2) {
8652
+ this.apiKey = config2.apiKey;
8653
+ }
8654
+ isConfigured() {
8655
+ return !!this.apiKey;
8656
+ }
8657
+ async generateVideo(prompt3, options) {
8658
+ if (!this.apiKey) {
8659
+ return { id: "", status: "failed", error: "GOOGLE_API_KEY not configured for Gemini Omni" };
8660
+ }
8661
+ const opts = options ?? { prompt: prompt3 };
8662
+ const image = firstReferenceImage(opts);
8663
+ const task = image ? "image_to_video" : "text_to_video";
8664
+ const aspectRatio2 = opts.aspectRatio === "9:16" ? "9:16" : "16:9";
8665
+ const inputs = [{ text: prompt3 }];
8666
+ if (image) {
8667
+ inputs.push({ inline_data: { mime_type: image.mimeType, data: image.base64 } });
8668
+ }
8669
+ const body = {
8670
+ model: OMNI_MODEL,
8671
+ inputs,
8672
+ generation_config: {
8673
+ video_config: { task, aspect_ratio: aspectRatio2 }
8674
+ }
8675
+ };
8676
+ try {
8677
+ const res = await fetch(INTERACTIONS_URL, {
8678
+ method: "POST",
8679
+ headers: { "Content-Type": "application/json", "x-goog-api-key": this.apiKey },
8680
+ body: JSON.stringify(body)
8681
+ });
8682
+ const text = await res.text();
8683
+ if (!res.ok) {
8684
+ return {
8685
+ id: "",
8686
+ status: "failed",
8687
+ error: `Gemini Omni (experimental) request failed: HTTP ${res.status} \u2014 ${text.slice(0, 300)}`
8688
+ };
8689
+ }
8690
+ let parsed;
8691
+ try {
8692
+ parsed = JSON.parse(text);
8693
+ } catch {
8694
+ return { id: "", status: "failed", error: "Gemini Omni returned a non-JSON response" };
8695
+ }
8696
+ const videoUrl = findVideoUrl(parsed);
8697
+ const id = parsed?.interaction_id ?? parsed?.name ?? "";
8698
+ if (!videoUrl) {
8699
+ return {
8700
+ id,
8701
+ status: "failed",
8702
+ error: "Gemini Omni response contained no video URL (the preview interactions schema may have changed)."
8703
+ };
8704
+ }
8705
+ return { id, status: "completed", videoUrl };
8706
+ } catch (err) {
8707
+ return {
8708
+ id: "",
8709
+ status: "failed",
8710
+ error: `Gemini Omni (experimental) error: ${err instanceof Error ? err.message : String(err)}`
8711
+ };
8712
+ }
8713
+ }
8714
+ };
8715
+ omniProvider = new OmniProvider();
8716
+ }
8717
+ });
8718
+
8612
8719
  // ../ai-providers/dist/gemini/index.js
8613
8720
  var init_gemini = __esm({
8614
8721
  "../ai-providers/dist/gemini/index.js"() {
@@ -8616,6 +8723,7 @@ var init_gemini = __esm({
8616
8723
  init_GeminiProvider();
8617
8724
  init_gemini_motion();
8618
8725
  init_gemini_models();
8726
+ init_gemini_omni();
8619
8727
  init_define_provider();
8620
8728
  defineProvider({
8621
8729
  id: "gemini",
@@ -8639,6 +8747,13 @@ var init_gemini = __esm({
8639
8747
  resolverPriority: { video: 3 },
8640
8748
  commandsUnlocked: ["generate video -p veo"]
8641
8749
  });
8750
+ defineProvider({
8751
+ id: "omni",
8752
+ label: "Gemini Omni (experimental)",
8753
+ apiKey: "google",
8754
+ kinds: ["video"],
8755
+ commandsUnlocked: ["generate video -p omni"]
8756
+ });
8642
8757
  }
8643
8758
  });
8644
8759
 
@@ -18202,6 +18317,7 @@ __export(dist_exports2, {
18202
18317
  KokoroProvider: () => KokoroProvider,
18203
18318
  OPENAI_TTS_VOICES: () => OPENAI_TTS_VOICES,
18204
18319
  OllamaProvider: () => OllamaProvider,
18320
+ OmniProvider: () => OmniProvider,
18205
18321
  OpenAIImageProvider: () => OpenAIImageProvider,
18206
18322
  OpenAIProvider: () => OpenAIProvider,
18207
18323
  OpenAiTtsProvider: () => OpenAiTtsProvider,
@@ -18232,6 +18348,7 @@ __export(dist_exports2, {
18232
18348
  loadBundledKokoroRuntime: () => loadBundledKokoroRuntime,
18233
18349
  loadKokoroFromWorkspace: () => loadKokoroFromWorkspace,
18234
18350
  ollamaProvider: () => ollamaProvider,
18351
+ omniProvider: () => omniProvider,
18235
18352
  openaiImageProvider: () => openaiImageProvider,
18236
18353
  openaiProvider: () => openaiProvider,
18237
18354
  openaiTtsProvider: () => openaiTtsProvider,
@@ -26756,6 +26873,7 @@ function buildDesignMd(opts) {
26756
26873
  const { name, style } = opts;
26757
26874
  const roles = designColorRoles(style);
26758
26875
  const frontmatter = `---
26876
+ type: Design
26759
26877
  name: ${name}
26760
26878
  colors:
26761
26879
  primary: "${roles.primary}"
@@ -26880,6 +26998,7 @@ function buildCharactersMd(name) {
26880
26998
  }
26881
26999
  function buildStoryboardMd(name, duration = 12) {
26882
27000
  return `---
27001
+ type: Storyboard
26883
27002
  title: ${name}
26884
27003
  duration: ${duration}
26885
27004
  aspect: 16:9
@@ -29493,6 +29612,30 @@ var init_install_skill = __esm({
29493
29612
  }
29494
29613
  });
29495
29614
 
29615
+ // src/commands/_shared/frontmatter.ts
29616
+ function extractFrontmatter(md) {
29617
+ const raw = md.replace(/\r\n/g, "\n");
29618
+ const match = raw.match(FRONTMATTER_RE);
29619
+ if (match) {
29620
+ try {
29621
+ const parsed = (0, import_yaml3.parse)(match[1]);
29622
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
29623
+ return { data: parsed, body: raw.slice(match[0].length), raw };
29624
+ }
29625
+ } catch {
29626
+ }
29627
+ }
29628
+ return { data: null, body: raw, raw };
29629
+ }
29630
+ var import_yaml3, FRONTMATTER_RE;
29631
+ var init_frontmatter = __esm({
29632
+ "src/commands/_shared/frontmatter.ts"() {
29633
+ "use strict";
29634
+ import_yaml3 = __toESM(require_dist2(), 1);
29635
+ FRONTMATTER_RE = /^---\s*\n([\s\S]*?)\n---\s*(?:\n|$)/;
29636
+ }
29637
+ });
29638
+
29496
29639
  // src/commands/_shared/storyboard-parse.ts
29497
29640
  function resolveCharacters(frontmatter) {
29498
29641
  const pool = frontmatter?.characters;
@@ -29571,28 +29714,16 @@ function parseStoryboard(md) {
29571
29714
  };
29572
29715
  }
29573
29716
  function extractProjectFrontmatter(md) {
29574
- const match = md.match(FRONTMATTER_RE);
29575
- if (!match) return { frontmatter: void 0, remaining: md };
29576
- let parsed;
29577
- try {
29578
- parsed = (0, import_yaml3.parse)(match[1]);
29579
- } catch {
29580
- return { frontmatter: void 0, remaining: md };
29581
- }
29582
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
29583
- return { frontmatter: void 0, remaining: md };
29584
- }
29585
- return {
29586
- frontmatter: parsed,
29587
- remaining: md.slice(match[0].length)
29588
- };
29717
+ const { data, body } = extractFrontmatter(md);
29718
+ if (!data) return { frontmatter: void 0, remaining: md };
29719
+ return { frontmatter: data, remaining: body };
29589
29720
  }
29590
29721
  function extractBeatCues(body) {
29591
29722
  const match = body.match(BEAT_CUES_RE);
29592
29723
  if (!match) return { cues: void 0, body };
29593
29724
  let parsed;
29594
29725
  try {
29595
- parsed = (0, import_yaml3.parse)(match[1]);
29726
+ parsed = (0, import_yaml4.parse)(match[1]);
29596
29727
  } catch {
29597
29728
  return { cues: void 0, body };
29598
29729
  }
@@ -29622,16 +29753,16 @@ function parseBeatDuration(body) {
29622
29753
  const n = parseFloat(valueMatch[1]);
29623
29754
  return Number.isFinite(n) && n > 0 ? n : void 0;
29624
29755
  }
29625
- var import_yaml3, HEADING_RE, BEAT_PREFIX_RE, DURATION_SUBSECTION_RE, DURATION_VALUE_RE, FRONTMATTER_RE, BEAT_CUES_RE;
29756
+ var import_yaml4, HEADING_RE, BEAT_PREFIX_RE, DURATION_SUBSECTION_RE, DURATION_VALUE_RE, BEAT_CUES_RE;
29626
29757
  var init_storyboard_parse = __esm({
29627
29758
  "src/commands/_shared/storyboard-parse.ts"() {
29628
29759
  "use strict";
29629
- import_yaml3 = __toESM(require_dist2(), 1);
29760
+ import_yaml4 = __toESM(require_dist2(), 1);
29761
+ init_frontmatter();
29630
29762
  HEADING_RE = /^##\s+(.+?)\s*$/gm;
29631
29763
  BEAT_PREFIX_RE = /^Beat\s+(.+?)\s+(?:—|:|-)\s+(.+)$/i;
29632
29764
  DURATION_SUBSECTION_RE = /###\s+Beat\s+duration\s*\n([\s\S]*?)(?=\n###\s|\n##\s|$)/i;
29633
29765
  DURATION_VALUE_RE = /(-?\d+(?:\.\d+)?)\s*(?:s|sec|seconds?)?/i;
29634
- FRONTMATTER_RE = /^---\s*\n([\s\S]*?)\n---\s*(?:\n|$)/;
29635
29766
  BEAT_CUES_RE = /^\s*```ya?ml\s*\n([\s\S]*?)\n```\s*(?:\n|$)/;
29636
29767
  }
29637
29768
  });
@@ -32004,7 +32135,7 @@ function setStoryboardCue(markdown, opts) {
32004
32135
  cues[opts.key] = normalizeCueValue(opts.key, opts.value);
32005
32136
  }
32006
32137
  const remainingBody = cue ? section.body.slice(cue.full.length).replace(/^\s*\n/, "") : section.body.replace(/^\s*\n/, "");
32007
- const cueBlock = Object.keys(cues).length > 0 ? "```yaml\n" + (0, import_yaml4.stringify)(cues, { lineWidth: 0 }).trimEnd() + "\n```\n\n" : "";
32138
+ const cueBlock = Object.keys(cues).length > 0 ? "```yaml\n" + (0, import_yaml5.stringify)(cues, { lineWidth: 0 }).trimEnd() + "\n```\n\n" : "";
32008
32139
  const nextBody = cueBlock + remainingBody.trimStart();
32009
32140
  const nextSection = section.raw.slice(0, section.headingEnd - section.start) + "\n\n" + nextBody.trimEnd() + "\n";
32010
32141
  return markdown.slice(0, section.start) + nextSection + markdown.slice(section.end);
@@ -32038,7 +32169,7 @@ function storyboardCuesFromInput(opts) {
32038
32169
  }
32039
32170
  function buildStoryboardBeatSection(opts) {
32040
32171
  const title = normalizeTitle(opts.title ?? humanizeBeatId(opts.beatId));
32041
- const cueBlock = Object.keys(opts.cues).length > 0 ? "```yaml\n" + (0, import_yaml4.stringify)(opts.cues, { lineWidth: 0 }).trimEnd() + "\n```\n\n" : "";
32172
+ const cueBlock = Object.keys(opts.cues).length > 0 ? "```yaml\n" + (0, import_yaml5.stringify)(opts.cues, { lineWidth: 0 }).trimEnd() + "\n```\n\n" : "";
32042
32173
  const body = opts.body?.trim() || "Scene created with `vibe scene add`. Edit this body if the build flow should recompose it.";
32043
32174
  return `## Beat ${opts.beatId} - ${title}
32044
32175
 
@@ -32097,7 +32228,7 @@ function readLeadingCueBlock(body) {
32097
32228
  const match = body.match(LEADING_CUE_RE);
32098
32229
  if (!match) return null;
32099
32230
  try {
32100
- const parsed = (0, import_yaml4.parse)(match[2]);
32231
+ const parsed = (0, import_yaml5.parse)(match[2]);
32101
32232
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
32102
32233
  return { full: match[0], error: "cue block must parse to a YAML object" };
32103
32234
  }
@@ -32106,11 +32237,11 @@ function readLeadingCueBlock(body) {
32106
32237
  return { full: match[0], error: error instanceof Error ? error.message : String(error) };
32107
32238
  }
32108
32239
  }
32109
- var import_yaml4, STORYBOARD_CUE_KEYS, HEADING_RE2, LEADING_CUE_RE, ALLOWED_CUE_KEYS, STRING_CUE_KEYS, MAX_RECOMMENDED_BEAT_SEC;
32240
+ var import_yaml5, STORYBOARD_CUE_KEYS, HEADING_RE2, LEADING_CUE_RE, ALLOWED_CUE_KEYS, STRING_CUE_KEYS, MAX_RECOMMENDED_BEAT_SEC;
32110
32241
  var init_storyboard_edit = __esm({
32111
32242
  "src/commands/_shared/storyboard-edit.ts"() {
32112
32243
  "use strict";
32113
- import_yaml4 = __toESM(require_dist2(), 1);
32244
+ import_yaml5 = __toESM(require_dist2(), 1);
32114
32245
  init_storyboard_parse();
32115
32246
  STORYBOARD_CUE_KEYS = [
32116
32247
  "duration",
@@ -35976,6 +36107,7 @@ async function executeVideoGenerate(options) {
35976
36107
  runway: "RUNWAY_API_SECRET",
35977
36108
  kling: "KLING_API_KEY",
35978
36109
  veo: "GOOGLE_API_KEY",
36110
+ omni: "GOOGLE_API_KEY",
35979
36111
  seedance: "FAL_API_KEY",
35980
36112
  fal: "FAL_API_KEY"
35981
36113
  };
@@ -36196,6 +36328,31 @@ async function executeVideoGenerate(options) {
36196
36328
  outputPath,
36197
36329
  provider: "grok"
36198
36330
  };
36331
+ } else if (provider === "omni") {
36332
+ const omni = new OmniProvider();
36333
+ await omni.initialize({ apiKey: key });
36334
+ const result = await omni.generateVideo(prompt3, {
36335
+ prompt: prompt3,
36336
+ referenceImage,
36337
+ aspectRatio: ratio
36338
+ });
36339
+ if (result.status !== "completed" || !result.videoUrl) {
36340
+ return { success: false, error: result.error || "Gemini Omni generation failed" };
36341
+ }
36342
+ let outputPath;
36343
+ if (output) {
36344
+ const buffer = await downloadVideo(result.videoUrl, key);
36345
+ outputPath = resolve25(process.cwd(), output);
36346
+ await writeFile21(outputPath, buffer);
36347
+ }
36348
+ return {
36349
+ success: true,
36350
+ taskId: result.id,
36351
+ status: "completed",
36352
+ videoUrl: result.videoUrl,
36353
+ outputPath,
36354
+ provider: "omni"
36355
+ };
36199
36356
  }
36200
36357
  return { success: false, error: `Unsupported provider: ${provider}` };
36201
36358
  } catch (error) {
@@ -40156,9 +40313,9 @@ var init_dist3 = __esm({
40156
40313
  import { resolve as resolve33 } from "node:path";
40157
40314
  import { readFile as readFile21, writeFile as writeFile30 } from "node:fs/promises";
40158
40315
  function registerVideoCommand(parent) {
40159
- parent.command("video").alias("vid").description("Generate video using AI (Seedance, Grok, Kling, Runway, or Veo)").argument("[prompt]", "Text prompt describing the video (interactive if omitted)").option(
40316
+ parent.command("video").alias("vid").description("Generate video using AI (Seedance, Grok, Kling, Runway, Veo, or Gemini Omni)").argument("[prompt]", "Text prompt describing the video (interactive if omitted)").option(
40160
40317
  "-p, --provider <provider>",
40161
- "Provider: seedance (ByteDance Seedance 2.0 via fal.ai), grok, kling, runway, veo. `fal` is a deprecated v0.x alias for seedance and will be removed in 1.0."
40318
+ "Provider: seedance (ByteDance Seedance 2.0 via fal.ai), grok, kling, runway, veo, omni (Gemini Omni, experimental). `fal` is a deprecated v0.x alias for seedance and will be removed in 1.0."
40162
40319
  ).option(
40163
40320
  "-k, --api-key <key>",
40164
40321
  "API key (or set FAL_API_KEY / XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY / GOOGLE_API_KEY env)"
@@ -40221,10 +40378,11 @@ Examples:
40221
40378
  );
40222
40379
  }
40223
40380
  }
40224
- const validProviders = ["runway", "kling", "veo", "grok", "seedance", "fal"];
40381
+ const validProviders = ["runway", "kling", "veo", "grok", "seedance", "fal", "omni"];
40225
40382
  const videoEnvMap = {
40226
40383
  grok: "XAI_API_KEY",
40227
40384
  veo: "GOOGLE_API_KEY",
40385
+ omni: "GOOGLE_API_KEY",
40228
40386
  kling: "KLING_API_KEY",
40229
40387
  runway: "RUNWAY_API_SECRET",
40230
40388
  seedance: "FAL_API_KEY"
@@ -40236,7 +40394,7 @@ Examples:
40236
40394
  exitWithError(
40237
40395
  usageError(
40238
40396
  `Invalid provider: ${provider}`,
40239
- "Available providers: seedance, grok, kling, runway, veo. `fal` is a deprecated alias for seedance."
40397
+ "Available providers: seedance, grok, kling, runway, veo, omni (experimental). `fal` is a deprecated alias for seedance."
40240
40398
  )
40241
40399
  );
40242
40400
  }
@@ -40341,6 +40499,7 @@ Examples:
40341
40499
  runway: "RUNWAY_API_SECRET",
40342
40500
  kling: "KLING_API_KEY",
40343
40501
  veo: "GOOGLE_API_KEY",
40502
+ omni: "GOOGLE_API_KEY",
40344
40503
  grok: "XAI_API_KEY",
40345
40504
  seedance: "FAL_API_KEY"
40346
40505
  };
@@ -40348,6 +40507,7 @@ Examples:
40348
40507
  runway: "Runway",
40349
40508
  kling: "Kling",
40350
40509
  veo: "Veo",
40510
+ omni: "Gemini Omni (experimental)",
40351
40511
  grok: "Grok",
40352
40512
  seedance: "Seedance 2.0 via fal.ai"
40353
40513
  };
@@ -40682,6 +40842,16 @@ Examples:
40682
40842
  lastFrame: seedanceEndImage
40683
40843
  });
40684
40844
  finalResult = result;
40845
+ } else if (provider === "omni") {
40846
+ const omni = new OmniProvider();
40847
+ await omni.initialize({ apiKey });
40848
+ spinner2.text = "Generating video with Gemini Omni (experimental)...";
40849
+ result = await omni.generateVideo(prompt3, {
40850
+ prompt: prompt3,
40851
+ referenceImage,
40852
+ aspectRatio: options.ratio
40853
+ });
40854
+ finalResult = result;
40685
40855
  }
40686
40856
  if (!finalResult || finalResult.status !== "completed") {
40687
40857
  spinner2.fail(finalResult?.error || "Generation failed");
@@ -47153,7 +47323,7 @@ async function executeRegenerateScene(options) {
47153
47323
  return { ...result, error: `Storyboard not found in: ${outputDir} (expected storyboard.yaml or storyboard.json)` };
47154
47324
  }
47155
47325
  const storyboardContent = await readFile34(storyboardPath2, "utf-8");
47156
- const segments = storyboardPath2.endsWith(".yaml") ? (0, import_yaml5.parse)(storyboardContent).scenes : JSON.parse(storyboardContent);
47326
+ const segments = storyboardPath2.endsWith(".yaml") ? (0, import_yaml6.parse)(storyboardContent).scenes : JSON.parse(storyboardContent);
47157
47327
  for (const sceneNum of options.scenes) {
47158
47328
  if (sceneNum < 1 || sceneNum > segments.length) {
47159
47329
  return { ...result, error: `Scene ${sceneNum} does not exist. Storyboard has ${segments.length} scenes.` };
@@ -47535,7 +47705,7 @@ Generate the single-person scene image now.`;
47535
47705
  segment.startTime = currentTime;
47536
47706
  currentTime += segment.duration;
47537
47707
  }
47538
- const serialized = storyboardPath2.endsWith(".yaml") ? (0, import_yaml5.stringify)({ scenes: segments }, { indent: 2 }) : JSON.stringify(segments, null, 2);
47708
+ const serialized = storyboardPath2.endsWith(".yaml") ? (0, import_yaml6.stringify)({ scenes: segments }, { indent: 2 }) : JSON.stringify(segments, null, 2);
47539
47709
  await writeFile39(storyboardPath2, serialized, "utf-8");
47540
47710
  }
47541
47711
  result.success = result.failedScenes.length === 0;
@@ -47547,11 +47717,11 @@ Generate the single-person scene image now.`;
47547
47717
  };
47548
47718
  }
47549
47719
  }
47550
- var import_yaml5;
47720
+ var import_yaml6;
47551
47721
  var init_ai_script_pipeline = __esm({
47552
47722
  "src/commands/ai-script-pipeline.ts"() {
47553
47723
  "use strict";
47554
- import_yaml5 = __toESM(require_dist2(), 1);
47724
+ import_yaml6 = __toESM(require_dist2(), 1);
47555
47725
  init_dist2();
47556
47726
  init_api_key();
47557
47727
  init_config();
@@ -60714,20 +60884,8 @@ function colorTokens(value) {
60714
60884
  return out;
60715
60885
  }
60716
60886
  function parseDesign(md) {
60717
- const raw = md.replace(/\r\n/g, "\n");
60718
- let frontmatter = {};
60719
- let body = raw;
60720
- const fm = raw.match(DESIGN_FRONTMATTER_RE);
60721
- if (fm) {
60722
- try {
60723
- const parsed = (0, import_yaml7.parse)(fm[1]);
60724
- if (isPlainObject(parsed)) {
60725
- frontmatter = parsed;
60726
- body = raw.slice(fm[0].length);
60727
- }
60728
- } catch {
60729
- }
60730
- }
60887
+ const { data, body, raw } = extractFrontmatter(md);
60888
+ const frontmatter = data ?? {};
60731
60889
  const sections = {};
60732
60890
  const headings = [];
60733
60891
  SECTION_RE.lastIndex = 0;
@@ -60763,7 +60921,7 @@ function validateDesignMarkdown(md) {
60763
60921
  const fm = raw.match(DESIGN_FRONTMATTER_RE);
60764
60922
  if (fm) {
60765
60923
  try {
60766
- const parsed = (0, import_yaml7.parse)(fm[1]);
60924
+ const parsed = (0, import_yaml8.parse)(fm[1]);
60767
60925
  if (!isPlainObject(parsed)) {
60768
60926
  issues.push({
60769
60927
  severity: "warning",
@@ -60804,11 +60962,12 @@ function validateDesignMarkdown(md) {
60804
60962
  }
60805
60963
  return issues;
60806
60964
  }
60807
- var import_yaml7, DESIGN_FRONTMATTER_RE, SECTION_RE, HEX_RE;
60965
+ var import_yaml8, DESIGN_FRONTMATTER_RE, SECTION_RE, HEX_RE;
60808
60966
  var init_design_parse = __esm({
60809
60967
  "src/commands/_shared/design-parse.ts"() {
60810
60968
  "use strict";
60811
- import_yaml7 = __toESM(require_dist2(), 1);
60969
+ import_yaml8 = __toESM(require_dist2(), 1);
60970
+ init_frontmatter();
60812
60971
  DESIGN_FRONTMATTER_RE = /^---\s*\n([\s\S]*?)\n---\s*(?:\n|$)/;
60813
60972
  SECTION_RE = /^##\s+(.+?)\s*$/gm;
60814
60973
  HEX_RE = /#[0-9a-fA-F]{6}\b/g;
@@ -65773,7 +65932,7 @@ async function loadPipeline(filePath) {
65773
65932
  throw new Error(`Pipeline file not found: ${absPath}`);
65774
65933
  }
65775
65934
  const content = await readFile53(absPath, "utf-8");
65776
- const manifest2 = (0, import_yaml8.parse)(content);
65935
+ const manifest2 = (0, import_yaml9.parse)(content);
65777
65936
  if (!manifest2.name) throw new Error("Pipeline missing 'name' field");
65778
65937
  if (!manifest2.steps || !Array.isArray(manifest2.steps)) throw new Error("Pipeline missing 'steps' array");
65779
65938
  const ids = /* @__PURE__ */ new Set();
@@ -65796,7 +65955,7 @@ async function executePipeline(manifest2, options = {}) {
65796
65955
  const checkpointPath = resolve79(outputDir, CHECKPOINT_FILE);
65797
65956
  if (existsSync59(checkpointPath)) {
65798
65957
  const checkpointContent = await readFile53(checkpointPath, "utf-8");
65799
- const checkpoint = (0, import_yaml8.parse)(checkpointContent);
65958
+ const checkpoint = (0, import_yaml9.parse)(checkpointContent);
65800
65959
  for (const cs of checkpoint.completedSteps) {
65801
65960
  completedSteps.set(cs.id, {
65802
65961
  id: cs.id,
@@ -65915,7 +66074,7 @@ async function executePipeline(manifest2, options = {}) {
65915
66074
  };
65916
66075
  await writeFile53(
65917
66076
  resolve79(outputDir, CHECKPOINT_FILE),
65918
- (0, import_yaml8.stringify)(checkpoint, { indent: 2 }),
66077
+ (0, import_yaml9.stringify)(checkpoint, { indent: 2 }),
65919
66078
  "utf-8"
65920
66079
  );
65921
66080
  } else {
@@ -65961,11 +66120,11 @@ async function executePipeline(manifest2, options = {}) {
65961
66120
  budget: effectiveBudget ? budgetUsage : void 0
65962
66121
  };
65963
66122
  }
65964
- var import_yaml8, ACTION_METADATA, ACTION_HANDLERS, CHECKPOINT_FILE;
66123
+ var import_yaml9, ACTION_METADATA, ACTION_HANDLERS, CHECKPOINT_FILE;
65965
66124
  var init_executor = __esm({
65966
66125
  "src/pipeline/executor.ts"() {
65967
66126
  "use strict";
65968
- import_yaml8 = __toESM(require_dist2(), 1);
66127
+ import_yaml9 = __toESM(require_dist2(), 1);
65969
66128
  init_resolver();
65970
66129
  init_output();
65971
66130
  init_config();
@@ -72339,8 +72498,8 @@ var init_generate2 = __esm({
72339
72498
  description: "Generate video using AI. Supports Seedance 2.0 via fal.ai, Grok, Kling, Runway, and Veo. Requires FAL_API_KEY, XAI_API_KEY, KLING_API_KEY, RUNWAY_API_SECRET, or GOOGLE_API_KEY.",
72340
72499
  schema: external_exports.object({
72341
72500
  prompt: external_exports.string().describe("Text prompt describing the video"),
72342
- provider: external_exports.enum(["seedance", "grok", "kling", "runway", "veo"]).optional().describe(
72343
- "Video provider (default: seedance when FAL_API_KEY is configured, otherwise first configured provider)"
72501
+ provider: external_exports.enum(["seedance", "grok", "kling", "runway", "veo", "omni"]).optional().describe(
72502
+ "Video provider (default: seedance when FAL_API_KEY is configured, otherwise first configured provider). `omni` = Gemini Omni, experimental/opt-in, uses GOOGLE_API_KEY."
72344
72503
  ),
72345
72504
  image: external_exports.string().optional().describe("Reference image path for image-to-video"),
72346
72505
  endImage: external_exports.string().optional().describe("Ending frame image path for Seedance image-to-video"),
@@ -75683,7 +75842,7 @@ init_source();
75683
75842
  // package.json
75684
75843
  var package_default = {
75685
75844
  name: "@vibeframe/cli",
75686
- version: "0.113.24",
75845
+ version: "0.113.26",
75687
75846
  description: "VibeFrame CLI - agentic video workflow runtime",
75688
75847
  private: false,
75689
75848
  license: "MIT",
@@ -77769,7 +77928,7 @@ import { existsSync as existsSync42 } from "node:fs";
77769
77928
  // src/commands/ai-script-pipeline-cli.ts
77770
77929
  init_source();
77771
77930
  init_ora();
77772
- var import_yaml6 = __toESM(require_dist2(), 1);
77931
+ var import_yaml7 = __toESM(require_dist2(), 1);
77773
77932
  init_api_key();
77774
77933
  init_ai_script_pipeline();
77775
77934
  init_output();
@@ -77834,7 +77993,7 @@ function registerScriptPipelineCommands(aiCommand) {
77834
77993
  return;
77835
77994
  }
77836
77995
  const content = await readFile35(storyboardPath2, "utf-8");
77837
- const segments = storyboardIsYaml ? (0, import_yaml6.parse)(content).scenes : JSON.parse(content);
77996
+ const segments = storyboardIsYaml ? (0, import_yaml7.parse)(content).scenes : JSON.parse(content);
77838
77997
  for (const sceneNum of sceneNums) {
77839
77998
  if (sceneNum > segments.length) {
77840
77999
  exitWithError(
@@ -77920,7 +78079,7 @@ function registerScriptPipelineCommands(aiCommand) {
77920
78079
  const updateSpinner = ora("Updating timeline file...").start();
77921
78080
  try {
77922
78081
  const updatedContent = await readFile35(storyboardPath2, "utf-8");
77923
- const updatedSegments = storyboardIsYaml ? (0, import_yaml6.parse)(updatedContent).scenes : JSON.parse(updatedContent);
78082
+ const updatedSegments = storyboardIsYaml ? (0, import_yaml7.parse)(updatedContent).scenes : JSON.parse(updatedContent);
77924
78083
  const projectContent = await readFile35(projectPath, "utf-8");
77925
78084
  const projectData = JSON.parse(projectContent);
77926
78085
  for (const sceneNum of result.regeneratedScenes) {
@@ -84135,6 +84294,7 @@ function draftStoryboardFromBrief(opts) {
84135
84294
  const theme = describeTheme(brief, product);
84136
84295
  const beats = beatCount === 4 ? fourBeatDraft({ product, brief, theme, durations: beatDurations }) : threeBeatDraft({ product, brief, theme, durations: beatDurations });
84137
84296
  const storyboardMd = `---
84297
+ type: Storyboard
84138
84298
  title: ${JSON.stringify(product)}
84139
84299
  duration: ${total}
84140
84300
  aspect: ${opts.aspect ?? "16:9"}
@@ -197,7 +197,7 @@ export declare const generateThumbnailTool: import("../define-tool.js").ToolDefi
197
197
  }>>;
198
198
  export declare const generateVideoTool: import("../define-tool.js").ToolDefinition<z.ZodObject<{
199
199
  prompt: z.ZodString;
200
- provider: z.ZodOptional<z.ZodEnum<["seedance", "grok", "kling", "runway", "veo"]>>;
200
+ provider: z.ZodOptional<z.ZodEnum<["seedance", "grok", "kling", "runway", "veo", "omni"]>>;
201
201
  image: z.ZodOptional<z.ZodString>;
202
202
  endImage: z.ZodOptional<z.ZodString>;
203
203
  refImages: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
@@ -219,7 +219,7 @@ export declare const generateVideoTool: import("../define-tool.js").ToolDefiniti
219
219
  image?: string | undefined;
220
220
  duration?: number | undefined;
221
221
  output?: string | undefined;
222
- provider?: "grok" | "seedance" | "kling" | "runway" | "veo" | undefined;
222
+ provider?: "grok" | "seedance" | "kling" | "runway" | "veo" | "omni" | undefined;
223
223
  refImages?: string[] | undefined;
224
224
  refVideos?: string[] | undefined;
225
225
  refAudio?: string[] | undefined;
@@ -238,7 +238,7 @@ export declare const generateVideoTool: import("../define-tool.js").ToolDefiniti
238
238
  image?: string | undefined;
239
239
  duration?: number | undefined;
240
240
  output?: string | undefined;
241
- provider?: "grok" | "seedance" | "kling" | "runway" | "veo" | undefined;
241
+ provider?: "grok" | "seedance" | "kling" | "runway" | "veo" | "omni" | undefined;
242
242
  refImages?: string[] | undefined;
243
243
  refVideos?: string[] | undefined;
244
244
  refAudio?: string[] | undefined;
@@ -1842,6 +1842,113 @@ Respond with ONLY the JSON array, no other text.`;
1842
1842
  }
1843
1843
  });
1844
1844
 
1845
+ // ../ai-providers/dist/gemini/gemini-omni.js
1846
+ function firstReferenceImage(options) {
1847
+ const refs = options.referenceImages;
1848
+ if (refs && refs.length > 0 && refs[0].base64)
1849
+ return refs[0];
1850
+ const ref = options.referenceImage;
1851
+ if (typeof ref === "string" && ref) {
1852
+ const m = ref.match(/^data:(.+?);base64,(.*)$/);
1853
+ if (m)
1854
+ return { mimeType: m[1], base64: m[2] };
1855
+ return { mimeType: "image/png", base64: ref };
1856
+ }
1857
+ return void 0;
1858
+ }
1859
+ function findVideoUrl(obj) {
1860
+ if (!obj || typeof obj !== "object")
1861
+ return void 0;
1862
+ for (const [k, v] of Object.entries(obj)) {
1863
+ if (typeof v === "string" && /^https?:\/\//.test(v) && /(uri|url|video)/i.test(k))
1864
+ return v;
1865
+ if (typeof v === "object") {
1866
+ const nested = findVideoUrl(v);
1867
+ if (nested)
1868
+ return nested;
1869
+ }
1870
+ }
1871
+ return void 0;
1872
+ }
1873
+ var OMNI_MODEL, INTERACTIONS_URL, OmniProvider, omniProvider;
1874
+ var init_gemini_omni = __esm({
1875
+ "../ai-providers/dist/gemini/gemini-omni.js"() {
1876
+ "use strict";
1877
+ OMNI_MODEL = "gemini-omni-flash-preview";
1878
+ INTERACTIONS_URL = "https://generativelanguage.googleapis.com/v1beta/interactions";
1879
+ OmniProvider = class {
1880
+ constructor() {
1881
+ this.id = "omni";
1882
+ this.label = "Gemini Omni (experimental)";
1883
+ }
1884
+ async initialize(config2) {
1885
+ this.apiKey = config2.apiKey;
1886
+ }
1887
+ isConfigured() {
1888
+ return !!this.apiKey;
1889
+ }
1890
+ async generateVideo(prompt3, options) {
1891
+ if (!this.apiKey) {
1892
+ return { id: "", status: "failed", error: "GOOGLE_API_KEY not configured for Gemini Omni" };
1893
+ }
1894
+ const opts = options ?? { prompt: prompt3 };
1895
+ const image = firstReferenceImage(opts);
1896
+ const task = image ? "image_to_video" : "text_to_video";
1897
+ const aspectRatio2 = opts.aspectRatio === "9:16" ? "9:16" : "16:9";
1898
+ const inputs = [{ text: prompt3 }];
1899
+ if (image) {
1900
+ inputs.push({ inline_data: { mime_type: image.mimeType, data: image.base64 } });
1901
+ }
1902
+ const body = {
1903
+ model: OMNI_MODEL,
1904
+ inputs,
1905
+ generation_config: {
1906
+ video_config: { task, aspect_ratio: aspectRatio2 }
1907
+ }
1908
+ };
1909
+ try {
1910
+ const res = await fetch(INTERACTIONS_URL, {
1911
+ method: "POST",
1912
+ headers: { "Content-Type": "application/json", "x-goog-api-key": this.apiKey },
1913
+ body: JSON.stringify(body)
1914
+ });
1915
+ const text = await res.text();
1916
+ if (!res.ok) {
1917
+ return {
1918
+ id: "",
1919
+ status: "failed",
1920
+ error: `Gemini Omni (experimental) request failed: HTTP ${res.status} \u2014 ${text.slice(0, 300)}`
1921
+ };
1922
+ }
1923
+ let parsed;
1924
+ try {
1925
+ parsed = JSON.parse(text);
1926
+ } catch {
1927
+ return { id: "", status: "failed", error: "Gemini Omni returned a non-JSON response" };
1928
+ }
1929
+ const videoUrl = findVideoUrl(parsed);
1930
+ const id = parsed?.interaction_id ?? parsed?.name ?? "";
1931
+ if (!videoUrl) {
1932
+ return {
1933
+ id,
1934
+ status: "failed",
1935
+ error: "Gemini Omni response contained no video URL (the preview interactions schema may have changed)."
1936
+ };
1937
+ }
1938
+ return { id, status: "completed", videoUrl };
1939
+ } catch (err) {
1940
+ return {
1941
+ id: "",
1942
+ status: "failed",
1943
+ error: `Gemini Omni (experimental) error: ${err instanceof Error ? err.message : String(err)}`
1944
+ };
1945
+ }
1946
+ }
1947
+ };
1948
+ omniProvider = new OmniProvider();
1949
+ }
1950
+ });
1951
+
1845
1952
  // ../ai-providers/dist/gemini/index.js
1846
1953
  var init_gemini = __esm({
1847
1954
  "../ai-providers/dist/gemini/index.js"() {
@@ -1849,6 +1956,7 @@ var init_gemini = __esm({
1849
1956
  init_GeminiProvider();
1850
1957
  init_gemini_motion();
1851
1958
  init_gemini_models();
1959
+ init_gemini_omni();
1852
1960
  init_define_provider();
1853
1961
  defineProvider({
1854
1962
  id: "gemini",
@@ -1872,6 +1980,13 @@ var init_gemini = __esm({
1872
1980
  resolverPriority: { video: 3 },
1873
1981
  commandsUnlocked: ["generate video -p veo"]
1874
1982
  });
1983
+ defineProvider({
1984
+ id: "omni",
1985
+ label: "Gemini Omni (experimental)",
1986
+ apiKey: "google",
1987
+ kinds: ["video"],
1988
+ commandsUnlocked: ["generate video -p omni"]
1989
+ });
1875
1990
  }
1876
1991
  });
1877
1992
 
@@ -11435,6 +11550,7 @@ __export(dist_exports2, {
11435
11550
  KokoroProvider: () => KokoroProvider,
11436
11551
  OPENAI_TTS_VOICES: () => OPENAI_TTS_VOICES,
11437
11552
  OllamaProvider: () => OllamaProvider,
11553
+ OmniProvider: () => OmniProvider,
11438
11554
  OpenAIImageProvider: () => OpenAIImageProvider,
11439
11555
  OpenAIProvider: () => OpenAIProvider,
11440
11556
  OpenAiTtsProvider: () => OpenAiTtsProvider,
@@ -11465,6 +11581,7 @@ __export(dist_exports2, {
11465
11581
  loadBundledKokoroRuntime: () => loadBundledKokoroRuntime,
11466
11582
  loadKokoroFromWorkspace: () => loadKokoroFromWorkspace,
11467
11583
  ollamaProvider: () => ollamaProvider,
11584
+ omniProvider: () => omniProvider,
11468
11585
  openaiImageProvider: () => openaiImageProvider,
11469
11586
  openaiProvider: () => openaiProvider,
11470
11587
  openaiTtsProvider: () => openaiTtsProvider,
@@ -20712,6 +20829,7 @@ async function executeVideoGenerate(options) {
20712
20829
  runway: "RUNWAY_API_SECRET",
20713
20830
  kling: "KLING_API_KEY",
20714
20831
  veo: "GOOGLE_API_KEY",
20832
+ omni: "GOOGLE_API_KEY",
20715
20833
  seedance: "FAL_API_KEY",
20716
20834
  fal: "FAL_API_KEY"
20717
20835
  };
@@ -20932,6 +21050,31 @@ async function executeVideoGenerate(options) {
20932
21050
  outputPath,
20933
21051
  provider: "grok"
20934
21052
  };
21053
+ } else if (provider === "omni") {
21054
+ const omni = new OmniProvider();
21055
+ await omni.initialize({ apiKey: key });
21056
+ const result = await omni.generateVideo(prompt3, {
21057
+ prompt: prompt3,
21058
+ referenceImage,
21059
+ aspectRatio: ratio
21060
+ });
21061
+ if (result.status !== "completed" || !result.videoUrl) {
21062
+ return { success: false, error: result.error || "Gemini Omni generation failed" };
21063
+ }
21064
+ let outputPath;
21065
+ if (output) {
21066
+ const buffer = await downloadVideo(result.videoUrl, key);
21067
+ outputPath = resolve4(process.cwd(), output);
21068
+ await writeFile4(outputPath, buffer);
21069
+ }
21070
+ return {
21071
+ success: true,
21072
+ taskId: result.id,
21073
+ status: "completed",
21074
+ videoUrl: result.videoUrl,
21075
+ outputPath,
21076
+ provider: "omni"
21077
+ };
20935
21078
  }
20936
21079
  return { success: false, error: `Unsupported provider: ${provider}` };
20937
21080
  } catch (error) {
@@ -24635,6 +24778,30 @@ var init_review_report = __esm({
24635
24778
  }
24636
24779
  });
24637
24780
 
24781
+ // src/commands/_shared/frontmatter.ts
24782
+ function extractFrontmatter(md) {
24783
+ const raw = md.replace(/\r\n/g, "\n");
24784
+ const match = raw.match(FRONTMATTER_RE);
24785
+ if (match) {
24786
+ try {
24787
+ const parsed = (0, import_yaml2.parse)(match[1]);
24788
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
24789
+ return { data: parsed, body: raw.slice(match[0].length), raw };
24790
+ }
24791
+ } catch {
24792
+ }
24793
+ }
24794
+ return { data: null, body: raw, raw };
24795
+ }
24796
+ var import_yaml2, FRONTMATTER_RE;
24797
+ var init_frontmatter = __esm({
24798
+ "src/commands/_shared/frontmatter.ts"() {
24799
+ "use strict";
24800
+ import_yaml2 = __toESM(require_dist2(), 1);
24801
+ FRONTMATTER_RE = /^---\s*\n([\s\S]*?)\n---\s*(?:\n|$)/;
24802
+ }
24803
+ });
24804
+
24638
24805
  // src/commands/_shared/storyboard-parse.ts
24639
24806
  function resolveCharacters(frontmatter) {
24640
24807
  const pool = frontmatter?.characters;
@@ -24713,28 +24880,16 @@ function parseStoryboard(md) {
24713
24880
  };
24714
24881
  }
24715
24882
  function extractProjectFrontmatter(md) {
24716
- const match = md.match(FRONTMATTER_RE);
24717
- if (!match) return { frontmatter: void 0, remaining: md };
24718
- let parsed;
24719
- try {
24720
- parsed = (0, import_yaml2.parse)(match[1]);
24721
- } catch {
24722
- return { frontmatter: void 0, remaining: md };
24723
- }
24724
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
24725
- return { frontmatter: void 0, remaining: md };
24726
- }
24727
- return {
24728
- frontmatter: parsed,
24729
- remaining: md.slice(match[0].length)
24730
- };
24883
+ const { data, body } = extractFrontmatter(md);
24884
+ if (!data) return { frontmatter: void 0, remaining: md };
24885
+ return { frontmatter: data, remaining: body };
24731
24886
  }
24732
24887
  function extractBeatCues(body) {
24733
24888
  const match = body.match(BEAT_CUES_RE);
24734
24889
  if (!match) return { cues: void 0, body };
24735
24890
  let parsed;
24736
24891
  try {
24737
- parsed = (0, import_yaml2.parse)(match[1]);
24892
+ parsed = (0, import_yaml3.parse)(match[1]);
24738
24893
  } catch {
24739
24894
  return { cues: void 0, body };
24740
24895
  }
@@ -24764,16 +24919,16 @@ function parseBeatDuration(body) {
24764
24919
  const n = parseFloat(valueMatch[1]);
24765
24920
  return Number.isFinite(n) && n > 0 ? n : void 0;
24766
24921
  }
24767
- var import_yaml2, HEADING_RE, BEAT_PREFIX_RE, DURATION_SUBSECTION_RE, DURATION_VALUE_RE, FRONTMATTER_RE, BEAT_CUES_RE;
24922
+ var import_yaml3, HEADING_RE, BEAT_PREFIX_RE, DURATION_SUBSECTION_RE, DURATION_VALUE_RE, BEAT_CUES_RE;
24768
24923
  var init_storyboard_parse = __esm({
24769
24924
  "src/commands/_shared/storyboard-parse.ts"() {
24770
24925
  "use strict";
24771
- import_yaml2 = __toESM(require_dist2(), 1);
24926
+ import_yaml3 = __toESM(require_dist2(), 1);
24927
+ init_frontmatter();
24772
24928
  HEADING_RE = /^##\s+(.+?)\s*$/gm;
24773
24929
  BEAT_PREFIX_RE = /^Beat\s+(.+?)\s+(?:—|:|-)\s+(.+)$/i;
24774
24930
  DURATION_SUBSECTION_RE = /###\s+Beat\s+duration\s*\n([\s\S]*?)(?=\n###\s|\n##\s|$)/i;
24775
24931
  DURATION_VALUE_RE = /(-?\d+(?:\.\d+)?)\s*(?:s|sec|seconds?)?/i;
24776
- FRONTMATTER_RE = /^---\s*\n([\s\S]*?)\n---\s*(?:\n|$)/;
24777
24932
  BEAT_CUES_RE = /^\s*```ya?ml\s*\n([\s\S]*?)\n```\s*(?:\n|$)/;
24778
24933
  }
24779
24934
  });
@@ -25742,7 +25897,7 @@ async function readProjectConfig(projectDir) {
25742
25897
  if (existsSync3(legacyPath)) {
25743
25898
  try {
25744
25899
  const raw = await readFile5(legacyPath, "utf-8");
25745
- const legacy = (0, import_yaml3.parse)(raw);
25900
+ const legacy = (0, import_yaml4.parse)(raw);
25746
25901
  const name = legacy?.name ?? dir.split(/[\\/]/).filter(Boolean).pop() ?? "video";
25747
25902
  const config2 = defaultProjectConfig({
25748
25903
  name,
@@ -25774,11 +25929,11 @@ async function readProjectConfig(projectDir) {
25774
25929
  legacy: false
25775
25930
  };
25776
25931
  }
25777
- var import_yaml3, VIBE_CONFIG_FILENAME, LEGACY_VIBE_PROJECT_FILENAME;
25932
+ var import_yaml4, VIBE_CONFIG_FILENAME, LEGACY_VIBE_PROJECT_FILENAME;
25778
25933
  var init_project_config = __esm({
25779
25934
  "src/commands/_shared/project-config.ts"() {
25780
25935
  "use strict";
25781
- import_yaml3 = __toESM(require_dist2(), 1);
25936
+ import_yaml4 = __toESM(require_dist2(), 1);
25782
25937
  VIBE_CONFIG_FILENAME = "vibe.config.json";
25783
25938
  LEGACY_VIBE_PROJECT_FILENAME = "vibe.project.yaml";
25784
25939
  }
@@ -25900,6 +26055,7 @@ function buildDesignMd(opts) {
25900
26055
  const { name, style } = opts;
25901
26056
  const roles = designColorRoles(style);
25902
26057
  const frontmatter = `---
26058
+ type: Design
25903
26059
  name: ${name}
25904
26060
  colors:
25905
26061
  primary: "${roles.primary}"
@@ -26024,6 +26180,7 @@ function buildCharactersMd(name) {
26024
26180
  }
26025
26181
  function buildStoryboardMd(name, duration = 12) {
26026
26182
  return `---
26183
+ type: Storyboard
26027
26184
  title: ${name}
26028
26185
  duration: ${duration}
26029
26186
  aspect: 16:9
@@ -32589,7 +32746,7 @@ function setStoryboardCue(markdown, opts) {
32589
32746
  cues[opts.key] = normalizeCueValue(opts.key, opts.value);
32590
32747
  }
32591
32748
  const remainingBody = cue ? section.body.slice(cue.full.length).replace(/^\s*\n/, "") : section.body.replace(/^\s*\n/, "");
32592
- const cueBlock = Object.keys(cues).length > 0 ? "```yaml\n" + (0, import_yaml4.stringify)(cues, { lineWidth: 0 }).trimEnd() + "\n```\n\n" : "";
32749
+ const cueBlock = Object.keys(cues).length > 0 ? "```yaml\n" + (0, import_yaml5.stringify)(cues, { lineWidth: 0 }).trimEnd() + "\n```\n\n" : "";
32593
32750
  const nextBody = cueBlock + remainingBody.trimStart();
32594
32751
  const nextSection = section.raw.slice(0, section.headingEnd - section.start) + "\n\n" + nextBody.trimEnd() + "\n";
32595
32752
  return markdown.slice(0, section.start) + nextSection + markdown.slice(section.end);
@@ -32623,7 +32780,7 @@ function storyboardCuesFromInput(opts) {
32623
32780
  }
32624
32781
  function buildStoryboardBeatSection(opts) {
32625
32782
  const title = normalizeTitle(opts.title ?? humanizeBeatId(opts.beatId));
32626
- const cueBlock = Object.keys(opts.cues).length > 0 ? "```yaml\n" + (0, import_yaml4.stringify)(opts.cues, { lineWidth: 0 }).trimEnd() + "\n```\n\n" : "";
32783
+ const cueBlock = Object.keys(opts.cues).length > 0 ? "```yaml\n" + (0, import_yaml5.stringify)(opts.cues, { lineWidth: 0 }).trimEnd() + "\n```\n\n" : "";
32627
32784
  const body = opts.body?.trim() || "Scene created with `vibe scene add`. Edit this body if the build flow should recompose it.";
32628
32785
  return `## Beat ${opts.beatId} - ${title}
32629
32786
 
@@ -32682,7 +32839,7 @@ function readLeadingCueBlock(body) {
32682
32839
  const match = body.match(LEADING_CUE_RE);
32683
32840
  if (!match) return null;
32684
32841
  try {
32685
- const parsed = (0, import_yaml4.parse)(match[2]);
32842
+ const parsed = (0, import_yaml5.parse)(match[2]);
32686
32843
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
32687
32844
  return { full: match[0], error: "cue block must parse to a YAML object" };
32688
32845
  }
@@ -32691,11 +32848,11 @@ function readLeadingCueBlock(body) {
32691
32848
  return { full: match[0], error: error instanceof Error ? error.message : String(error) };
32692
32849
  }
32693
32850
  }
32694
- var import_yaml4, STORYBOARD_CUE_KEYS, HEADING_RE2, LEADING_CUE_RE, ALLOWED_CUE_KEYS, STRING_CUE_KEYS, MAX_RECOMMENDED_BEAT_SEC;
32851
+ var import_yaml5, STORYBOARD_CUE_KEYS, HEADING_RE2, LEADING_CUE_RE, ALLOWED_CUE_KEYS, STRING_CUE_KEYS, MAX_RECOMMENDED_BEAT_SEC;
32695
32852
  var init_storyboard_edit = __esm({
32696
32853
  "src/commands/_shared/storyboard-edit.ts"() {
32697
32854
  "use strict";
32698
- import_yaml4 = __toESM(require_dist2(), 1);
32855
+ import_yaml5 = __toESM(require_dist2(), 1);
32699
32856
  init_storyboard_parse();
32700
32857
  STORYBOARD_CUE_KEYS = [
32701
32858
  "duration",
@@ -33600,20 +33757,8 @@ function colorTokens(value) {
33600
33757
  return out;
33601
33758
  }
33602
33759
  function parseDesign(md) {
33603
- const raw = md.replace(/\r\n/g, "\n");
33604
- let frontmatter = {};
33605
- let body = raw;
33606
- const fm = raw.match(DESIGN_FRONTMATTER_RE);
33607
- if (fm) {
33608
- try {
33609
- const parsed = (0, import_yaml5.parse)(fm[1]);
33610
- if (isPlainObject(parsed)) {
33611
- frontmatter = parsed;
33612
- body = raw.slice(fm[0].length);
33613
- }
33614
- } catch {
33615
- }
33616
- }
33760
+ const { data, body, raw } = extractFrontmatter(md);
33761
+ const frontmatter = data ?? {};
33617
33762
  const sections = {};
33618
33763
  const headings = [];
33619
33764
  SECTION_RE.lastIndex = 0;
@@ -33649,7 +33794,7 @@ function validateDesignMarkdown(md) {
33649
33794
  const fm = raw.match(DESIGN_FRONTMATTER_RE);
33650
33795
  if (fm) {
33651
33796
  try {
33652
- const parsed = (0, import_yaml5.parse)(fm[1]);
33797
+ const parsed = (0, import_yaml6.parse)(fm[1]);
33653
33798
  if (!isPlainObject(parsed)) {
33654
33799
  issues.push({
33655
33800
  severity: "warning",
@@ -33690,11 +33835,12 @@ function validateDesignMarkdown(md) {
33690
33835
  }
33691
33836
  return issues;
33692
33837
  }
33693
- var import_yaml5, DESIGN_FRONTMATTER_RE, SECTION_RE, HEX_RE;
33838
+ var import_yaml6, DESIGN_FRONTMATTER_RE, SECTION_RE, HEX_RE;
33694
33839
  var init_design_parse = __esm({
33695
33840
  "src/commands/_shared/design-parse.ts"() {
33696
33841
  "use strict";
33697
- import_yaml5 = __toESM(require_dist2(), 1);
33842
+ import_yaml6 = __toESM(require_dist2(), 1);
33843
+ init_frontmatter();
33698
33844
  DESIGN_FRONTMATTER_RE = /^---\s*\n([\s\S]*?)\n---\s*(?:\n|$)/;
33699
33845
  SECTION_RE = /^##\s+(.+?)\s*$/gm;
33700
33846
  HEX_RE = /#[0-9a-fA-F]{6}\b/g;
@@ -47081,9 +47227,9 @@ var init_dist3 = __esm({
47081
47227
  import { resolve as resolve54 } from "node:path";
47082
47228
  import { readFile as readFile37, writeFile as writeFile40 } from "node:fs/promises";
47083
47229
  function registerVideoCommand(parent) {
47084
- parent.command("video").alias("vid").description("Generate video using AI (Seedance, Grok, Kling, Runway, or Veo)").argument("[prompt]", "Text prompt describing the video (interactive if omitted)").option(
47230
+ parent.command("video").alias("vid").description("Generate video using AI (Seedance, Grok, Kling, Runway, Veo, or Gemini Omni)").argument("[prompt]", "Text prompt describing the video (interactive if omitted)").option(
47085
47231
  "-p, --provider <provider>",
47086
- "Provider: seedance (ByteDance Seedance 2.0 via fal.ai), grok, kling, runway, veo. `fal` is a deprecated v0.x alias for seedance and will be removed in 1.0."
47232
+ "Provider: seedance (ByteDance Seedance 2.0 via fal.ai), grok, kling, runway, veo, omni (Gemini Omni, experimental). `fal` is a deprecated v0.x alias for seedance and will be removed in 1.0."
47087
47233
  ).option(
47088
47234
  "-k, --api-key <key>",
47089
47235
  "API key (or set FAL_API_KEY / XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY / GOOGLE_API_KEY env)"
@@ -47146,10 +47292,11 @@ Examples:
47146
47292
  );
47147
47293
  }
47148
47294
  }
47149
- const validProviders = ["runway", "kling", "veo", "grok", "seedance", "fal"];
47295
+ const validProviders = ["runway", "kling", "veo", "grok", "seedance", "fal", "omni"];
47150
47296
  const videoEnvMap = {
47151
47297
  grok: "XAI_API_KEY",
47152
47298
  veo: "GOOGLE_API_KEY",
47299
+ omni: "GOOGLE_API_KEY",
47153
47300
  kling: "KLING_API_KEY",
47154
47301
  runway: "RUNWAY_API_SECRET",
47155
47302
  seedance: "FAL_API_KEY"
@@ -47161,7 +47308,7 @@ Examples:
47161
47308
  exitWithError(
47162
47309
  usageError(
47163
47310
  `Invalid provider: ${provider}`,
47164
- "Available providers: seedance, grok, kling, runway, veo. `fal` is a deprecated alias for seedance."
47311
+ "Available providers: seedance, grok, kling, runway, veo, omni (experimental). `fal` is a deprecated alias for seedance."
47165
47312
  )
47166
47313
  );
47167
47314
  }
@@ -47266,6 +47413,7 @@ Examples:
47266
47413
  runway: "RUNWAY_API_SECRET",
47267
47414
  kling: "KLING_API_KEY",
47268
47415
  veo: "GOOGLE_API_KEY",
47416
+ omni: "GOOGLE_API_KEY",
47269
47417
  grok: "XAI_API_KEY",
47270
47418
  seedance: "FAL_API_KEY"
47271
47419
  };
@@ -47273,6 +47421,7 @@ Examples:
47273
47421
  runway: "Runway",
47274
47422
  kling: "Kling",
47275
47423
  veo: "Veo",
47424
+ omni: "Gemini Omni (experimental)",
47276
47425
  grok: "Grok",
47277
47426
  seedance: "Seedance 2.0 via fal.ai"
47278
47427
  };
@@ -47607,6 +47756,16 @@ Examples:
47607
47756
  lastFrame: seedanceEndImage
47608
47757
  });
47609
47758
  finalResult = result;
47759
+ } else if (provider === "omni") {
47760
+ const omni = new OmniProvider();
47761
+ await omni.initialize({ apiKey });
47762
+ spinner2.text = "Generating video with Gemini Omni (experimental)...";
47763
+ result = await omni.generateVideo(prompt3, {
47764
+ prompt: prompt3,
47765
+ referenceImage,
47766
+ aspectRatio: options.ratio
47767
+ });
47768
+ finalResult = result;
47610
47769
  }
47611
47770
  if (!finalResult || finalResult.status !== "completed") {
47612
47771
  spinner2.fail(finalResult?.error || "Generation failed");
@@ -59127,8 +59286,8 @@ var generateVideoTool = defineTool({
59127
59286
  description: "Generate video using AI. Supports Seedance 2.0 via fal.ai, Grok, Kling, Runway, and Veo. Requires FAL_API_KEY, XAI_API_KEY, KLING_API_KEY, RUNWAY_API_SECRET, or GOOGLE_API_KEY.",
59128
59287
  schema: external_exports.object({
59129
59288
  prompt: external_exports.string().describe("Text prompt describing the video"),
59130
- provider: external_exports.enum(["seedance", "grok", "kling", "runway", "veo"]).optional().describe(
59131
- "Video provider (default: seedance when FAL_API_KEY is configured, otherwise first configured provider)"
59289
+ provider: external_exports.enum(["seedance", "grok", "kling", "runway", "veo", "omni"]).optional().describe(
59290
+ "Video provider (default: seedance when FAL_API_KEY is configured, otherwise first configured provider). `omni` = Gemini Omni, experimental/opt-in, uses GOOGLE_API_KEY."
59132
59291
  ),
59133
59292
  image: external_exports.string().optional().describe("Reference image path for image-to-video"),
59134
59293
  endImage: external_exports.string().optional().describe("Ending frame image path for Seedance image-to-video"),
@@ -59942,7 +60101,7 @@ import { tmpdir as tmpdir4 } from "node:os";
59942
60101
  import { join as join27 } from "node:path";
59943
60102
 
59944
60103
  // src/commands/ai-script-pipeline.ts
59945
- var import_yaml6 = __toESM(require_dist2(), 1);
60104
+ var import_yaml7 = __toESM(require_dist2(), 1);
59946
60105
  init_dist2();
59947
60106
  init_api_key();
59948
60107
  init_config();
@@ -60120,7 +60279,7 @@ async function executeRegenerateScene(options) {
60120
60279
  return { ...result, error: `Storyboard not found in: ${outputDir} (expected storyboard.yaml or storyboard.json)` };
60121
60280
  }
60122
60281
  const storyboardContent = await readFile41(storyboardPath, "utf-8");
60123
- const segments = storyboardPath.endsWith(".yaml") ? (0, import_yaml6.parse)(storyboardContent).scenes : JSON.parse(storyboardContent);
60282
+ const segments = storyboardPath.endsWith(".yaml") ? (0, import_yaml7.parse)(storyboardContent).scenes : JSON.parse(storyboardContent);
60124
60283
  for (const sceneNum of options.scenes) {
60125
60284
  if (sceneNum < 1 || sceneNum > segments.length) {
60126
60285
  return { ...result, error: `Scene ${sceneNum} does not exist. Storyboard has ${segments.length} scenes.` };
@@ -60502,7 +60661,7 @@ Generate the single-person scene image now.`;
60502
60661
  segment.startTime = currentTime;
60503
60662
  currentTime += segment.duration;
60504
60663
  }
60505
- const serialized = storyboardPath.endsWith(".yaml") ? (0, import_yaml6.stringify)({ scenes: segments }, { indent: 2 }) : JSON.stringify(segments, null, 2);
60664
+ const serialized = storyboardPath.endsWith(".yaml") ? (0, import_yaml7.stringify)({ scenes: segments }, { indent: 2 }) : JSON.stringify(segments, null, 2);
60506
60665
  await writeFile43(storyboardPath, serialized, "utf-8");
60507
60666
  }
60508
60667
  result.success = result.failedScenes.length === 0;
@@ -60957,7 +61116,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
60957
61116
  }
60958
61117
 
60959
61118
  // src/pipeline/executor.ts
60960
- var import_yaml7 = __toESM(require_dist2(), 1);
61119
+ var import_yaml8 = __toESM(require_dist2(), 1);
60961
61120
  import { resolve as resolve61 } from "node:path";
60962
61121
  import { readFile as readFile44, writeFile as writeFile46, mkdir as mkdir25 } from "node:fs/promises";
60963
61122
  import { existsSync as existsSync49 } from "node:fs";
@@ -61377,7 +61536,7 @@ async function loadPipeline(filePath) {
61377
61536
  throw new Error(`Pipeline file not found: ${absPath}`);
61378
61537
  }
61379
61538
  const content = await readFile44(absPath, "utf-8");
61380
- const manifest2 = (0, import_yaml7.parse)(content);
61539
+ const manifest2 = (0, import_yaml8.parse)(content);
61381
61540
  if (!manifest2.name) throw new Error("Pipeline missing 'name' field");
61382
61541
  if (!manifest2.steps || !Array.isArray(manifest2.steps)) throw new Error("Pipeline missing 'steps' array");
61383
61542
  const ids = /* @__PURE__ */ new Set();
@@ -61401,7 +61560,7 @@ async function executePipeline(manifest2, options = {}) {
61401
61560
  const checkpointPath = resolve61(outputDir, CHECKPOINT_FILE);
61402
61561
  if (existsSync49(checkpointPath)) {
61403
61562
  const checkpointContent = await readFile44(checkpointPath, "utf-8");
61404
- const checkpoint = (0, import_yaml7.parse)(checkpointContent);
61563
+ const checkpoint = (0, import_yaml8.parse)(checkpointContent);
61405
61564
  for (const cs of checkpoint.completedSteps) {
61406
61565
  completedSteps.set(cs.id, {
61407
61566
  id: cs.id,
@@ -61520,7 +61679,7 @@ async function executePipeline(manifest2, options = {}) {
61520
61679
  };
61521
61680
  await writeFile46(
61522
61681
  resolve61(outputDir, CHECKPOINT_FILE),
61523
- (0, import_yaml7.stringify)(checkpoint, { indent: 2 }),
61682
+ (0, import_yaml8.stringify)(checkpoint, { indent: 2 }),
61524
61683
  "utf-8"
61525
61684
  );
61526
61685
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/cli",
3
- "version": "0.113.24",
3
+ "version": "0.113.26",
4
4
  "description": "VibeFrame CLI - agentic video workflow runtime",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -83,8 +83,8 @@
83
83
  "tsx": "^4.21.0",
84
84
  "typescript": "^5.3.3",
85
85
  "vitest": "^1.2.2",
86
- "@vibeframe/ai-providers": "0.113.24",
87
- "@vibeframe/core": "0.113.24"
86
+ "@vibeframe/ai-providers": "0.113.26",
87
+ "@vibeframe/core": "0.113.26"
88
88
  },
89
89
  "engines": {
90
90
  "node": ">=20"