@vibeframe/mcp-server 0.115.2 → 0.115.4

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +1127 -752
  3. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -191,8 +191,8 @@ var init_api_keys = __esm({
191
191
  envVar: "OPENAI_API_KEY",
192
192
  label: "OpenAI",
193
193
  showInSetup: true,
194
- setupDescription: "gpt-image-2 image gen ($, default since v0.56), gpt-4o-mini-tts narration, Whisper transcribe, Agent",
195
- envExampleComment: "OpenAI API Key (Whisper transcription, gpt-4o-mini-tts narration, gpt-image-2 \u2014 default text-to-image since v0.56)",
194
+ setupDescription: "gpt-image-2.5 image gen ($), gpt-4o-mini-tts narration, Whisper transcribe, Agent",
195
+ envExampleComment: "OpenAI API Key (Whisper transcription, gpt-4o-mini-tts narration, gpt-image-2.5 text-to-image)",
196
196
  envExampleUrl: "https://platform.openai.com/api-keys",
197
197
  keyFormat: { prefix: /^sk-/, example: "sk-..." }
198
198
  });
@@ -267,8 +267,7 @@ var init_api_keys = __esm({
267
267
  setupDescription: "v2.5/v3 video \u2014 std ($$) and pro ($$$) modes",
268
268
  envExampleComment: "Kling API Key (Kling video generation)",
269
269
  envExampleUrl: "https://platform.klingai.com/",
270
- envExampleExtraLines: ["Format: ACCESS_KEY:SECRET_KEY"],
271
- keyFormat: { prefix: /:/, example: "ACCESS_KEY:SECRET_KEY" }
270
+ envExampleExtraLines: ["API key from the Kling console (legacy ACCESS_KEY:SECRET_KEY also works)"]
272
271
  });
273
272
  defineApiKey({
274
273
  configKey: "replicate",
@@ -440,6 +439,82 @@ var init_whisper = __esm({
440
439
  }
441
440
  });
442
441
 
442
+ // ../ai-providers/dist/catalog/catalog.js
443
+ function listModels(filter3 = {}) {
444
+ return MODEL_CATALOG.filter((m2) => (!filter3.provider || m2.provider === filter3.provider) && (!filter3.kind || m2.kind === filter3.kind));
445
+ }
446
+ function defaultModel(provider, kind3) {
447
+ const found = MODEL_CATALOG.find((m2) => m2.provider === provider && m2.kind === kind3 && m2.default);
448
+ if (!found)
449
+ throw new Error(`Model catalog has no default ${kind3} model for ${provider}`);
450
+ return found;
451
+ }
452
+ function findModel(provider, kind3, aliasOrId) {
453
+ const key2 = aliasOrId?.trim();
454
+ if (!key2)
455
+ return defaultModel(provider, kind3);
456
+ const lower = key2.toLowerCase();
457
+ return listModels({ provider, kind: kind3 }).find((m2) => m2.id === key2 || m2.aliases?.includes(lower));
458
+ }
459
+ function modelAliases(provider, kind3) {
460
+ return listModels({ provider, kind: kind3 }).flatMap((m2) => m2.aliases ?? []);
461
+ }
462
+ var MODEL_CATALOG;
463
+ var init_catalog = __esm({
464
+ "../ai-providers/dist/catalog/catalog.js"() {
465
+ "use strict";
466
+ MODEL_CATALOG = [
467
+ // ── LLMs ──────────────────────────────────────────────────────────────
468
+ { id: "claude-sonnet-5-5", provider: "claude", kind: "llm", label: "Claude Sonnet 5.5", aliases: ["sonnet"], default: true, status: "ga", price: { usd: 2, per: "1M input tokens" }, note: "Agent, storyboard, translate-srt, motion default" },
469
+ { id: "claude-opus-5-5", provider: "claude", kind: "llm", label: "Claude Opus 5.5", aliases: ["opus"], status: "ga", price: { usd: 4, per: "1M input tokens" } },
470
+ { id: "claude-opus-4-6", provider: "claude", kind: "llm", label: "Claude Opus 4.6", aliases: ["opus-4-6"], status: "legacy", price: { usd: 5, per: "1M input tokens" } },
471
+ { id: "gpt-5.4-mini", provider: "openai", kind: "llm", label: "GPT-5.4 mini", default: true, status: "ga", price: { usd: 0.75, per: "1M input tokens" }, note: "Agent and storyboard default (tools on Chat Completions)" },
472
+ { id: "gemini-3.8-flash", provider: "gemini", kind: "llm", label: "Gemini 3.8 Flash", aliases: ["flash", "latest", "flash-3.8", "gemini"], default: true, status: "ga", price: { usd: 0.75, per: "1M input tokens", basis: "until 2026-12-31, then $1.50" } },
473
+ { id: "gemini-3.5-flash", provider: "gemini", kind: "llm", label: "Gemini 3.5 Flash", aliases: ["flash-3.5"], status: "ga" },
474
+ { id: "gemini-3-flash-preview", provider: "gemini", kind: "llm", label: "Gemini 3 Flash (preview)", aliases: ["flash-3"], status: "preview" },
475
+ { id: "gemini-3.1-pro-preview", provider: "gemini", kind: "llm", label: "Gemini 3.1 Pro (preview)", aliases: ["pro", "pro-3.1", "3.1-pro", "gemini-3.1-pro"], status: "preview" },
476
+ { id: "gemini-2.5-flash", provider: "gemini", kind: "llm", label: "Gemini 2.5 Flash", aliases: ["flash-2.5"], status: "legacy", note: "New projects cannot use 2.5 models since 2026-09-18" },
477
+ { id: "gemini-2.5-pro", provider: "gemini", kind: "llm", label: "Gemini 2.5 Pro", aliases: ["pro-2.5", "2.5-pro"], status: "legacy" },
478
+ { id: "grok-4.3", provider: "grok", kind: "llm", label: "Grok 4.3", default: true, status: "ga", price: { usd: 1.25, per: "1M input tokens" } },
479
+ { id: "grok-4.7", provider: "grok", kind: "llm", label: "Grok 4.7", status: "ga", price: { usd: 2, per: "1M input tokens" } },
480
+ // ── Images ────────────────────────────────────────────────────────────
481
+ { id: "gpt-image-2.5-sunburst", provider: "openai", kind: "image", label: "GPT Image 2.5 Sunburst", aliases: ["2.5", "sunburst"], default: true, status: "ga", price: { usd: 0.211, per: "image", basis: "high, 1024x1024" } },
482
+ { id: "gpt-image-2.5-flare", provider: "openai", kind: "image", label: "GPT Image 2.5 Flare", aliases: ["flare", "2.5-flare"], status: "ga", price: { usd: 0.211, per: "image", basis: "high, 1024x1024" } },
483
+ { id: "gpt-image-2", provider: "openai", kind: "image", label: "GPT Image 2", aliases: ["2"], status: "ga", price: { usd: 0.211, per: "image", basis: "high, 1024x1024" } },
484
+ { id: "gpt-image-1.5", provider: "openai", kind: "image", label: "GPT Image 1.5", aliases: ["1.5"], status: "deprecated", shutdown: "2026-12-01", price: { usd: 0.133, per: "image", basis: "high, 1024x1024" } },
485
+ { id: "gemini-3.1-flash-image", provider: "gemini", kind: "image", label: "Nano Banana 2", aliases: ["flash", "3.1-flash", "latest"], default: true, status: "ga", price: { usd: 0.067, per: "image", basis: "1K" } },
486
+ { id: "gemini-3.1-flash-lite-image", provider: "gemini", kind: "image", label: "Nano Banana 2 Lite", aliases: ["lite"], status: "ga", price: { usd: 0.034, per: "image", basis: "1K" } },
487
+ { id: "gemini-3-pro-image", provider: "gemini", kind: "image", label: "Nano Banana Pro", aliases: ["pro"], status: "ga", price: { usd: 0.134, per: "image", basis: "1K-2K" } },
488
+ { id: "gemini-2.5-flash-image", provider: "gemini", kind: "image", label: "Nano Banana (2.5 Flash Image)", status: "deprecated", shutdown: "2026-10-02" },
489
+ { id: "gemini-3.1-flash-image-preview", provider: "gemini", kind: "image", label: "Nano Banana 2 (preview)", status: "deprecated", shutdown: "2026-06-25" },
490
+ { id: "gemini-3-pro-image-preview", provider: "gemini", kind: "image", label: "Nano Banana Pro (preview)", status: "deprecated", shutdown: "2026-06-25" },
491
+ { id: "grok-imagine-image", provider: "grok", kind: "image", label: "Grok Imagine Image", default: true, status: "ga", price: { usd: 0.02, per: "image" } },
492
+ { id: "grok-imagine-image-2.0", provider: "grok", kind: "image", label: "Grok Imagine Image 2.0", aliases: ["pro", "2.0", "quality"], status: "ga", price: { usd: 0.08, per: "image", basis: "medium, 2K" } },
493
+ // ── Video ─────────────────────────────────────────────────────────────
494
+ { id: "seedance-2.0", provider: "seedance", kind: "video", label: "Seedance 2.0", aliases: ["quality", "2.0"], default: true, status: "ga", price: { usd: 0.3024, per: "second", basis: "720p 16:9" }, note: "4-15 s, native audio" },
495
+ { id: "seedance-2.0-fast", provider: "seedance", kind: "video", label: "Seedance 2.0 Fast", aliases: ["fast"], status: "ga", price: { usd: 0.2419, per: "second", basis: "720p 16:9" }, note: "4-15 s, up to 720p, native audio" },
496
+ { id: "seedance-2.5", provider: "seedance", kind: "video", label: "Seedance 2.5", aliases: ["2.5"], status: "ga", price: { usd: 0.473, per: "second", basis: "720p 16:9" }, note: "Opt-in; 4-30 s, native audio" },
497
+ { id: "gemini-omni-1.1-flash", provider: "omni", kind: "video", label: "Gemini Omni 1.1 Flash", default: true, status: "ga", price: { usd: 0.1, per: "second", basis: "720p" }, note: "Google video default; the model picks 3-10 s; native audio" },
498
+ { id: "veo-3.1-generate-preview", provider: "veo", kind: "video", label: "Veo 3.1", aliases: ["3.1"], status: "deprecated", shutdown: "2026-10-22", price: { usd: 0.4, per: "second", basis: "720p/1080p" }, note: "4-8 s, native audio; explicit `-p veo` only" },
499
+ { id: "veo-3.1-fast-generate-preview", provider: "veo", kind: "video", label: "Veo 3.1 Fast", aliases: ["3.1-fast"], default: true, status: "deprecated", shutdown: "2026-10-22", price: { usd: 0.1, per: "second", basis: "720p" }, note: "4-8 s, native audio; explicit `-p veo` only" },
500
+ { id: "grok-imagine-video-1.5", provider: "grok", kind: "video", label: "Grok Imagine Video 1.5", aliases: ["1.5"], default: true, status: "ga", price: { usd: 0.14, per: "second", basis: "720p" }, note: "1-15 s, native audio; 720p unless `--resolution` says otherwise" },
501
+ { id: "grok-imagine-video-1.5-lite", provider: "grok", kind: "video", label: "Grok Imagine Video 1.5 Lite", aliases: ["lite", "1.5-lite"], status: "ga", price: { usd: 0.03, per: "second", basis: "720p" }, note: "1-15 s, native audio" },
502
+ { id: "grok-imagine-video", provider: "grok", kind: "video", label: "Grok Imagine Video", aliases: ["classic"], status: "legacy", price: { usd: 0.05, per: "second", basis: "480p" }, note: "Previous generation" },
503
+ { id: "kling-v3", provider: "kling", kind: "video", label: "Kling v3", aliases: ["v3"], default: true, status: "ga", price: { usd: 0.084, per: "second", basis: "std, silent" }, note: "3-15 s, multi-shot; `std` 720p, `pro` 1080p" },
504
+ { id: "kling-v2-6", provider: "kling", kind: "video", label: "Kling v2.6", aliases: ["v2.6"], status: "ga", price: { usd: 0.042, per: "second", basis: "std 720p" }, note: "5 or 10 s" },
505
+ { id: "kling-v2-5-turbo", provider: "kling", kind: "video", label: "Kling v2.5 Turbo", aliases: ["v2.5-turbo", "v2.5"], status: "ga", price: { usd: 0.042, per: "second", basis: "std" }, note: "5 or 10 s, no audio" },
506
+ { id: "gen4.5", provider: "runway", kind: "video", label: "Runway Gen-4.5", default: true, status: "ga", price: { usd: 0.12, per: "second" }, note: "2-10 s, no audio" },
507
+ { id: "gen4_turbo", provider: "runway", kind: "video", label: "Runway Gen-4 Turbo", status: "ga", price: { usd: 0.05, per: "second" }, note: "Image-to-video only, no audio" },
508
+ // ── Audio ─────────────────────────────────────────────────────────────
509
+ { id: "eleven_v3", provider: "elevenlabs", kind: "speech", label: "ElevenLabs v3", default: true, status: "ga", price: { usd: 0.08, per: "1K characters" } },
510
+ { id: "gpt-4o-mini-tts", provider: "openai", kind: "speech", label: "GPT-4o mini TTS", default: true, status: "deprecated", shutdown: "2027-01-06" },
511
+ { id: "music_v2_5", provider: "elevenlabs", kind: "music", label: "ElevenLabs Music v2.5", default: true, status: "ga", price: { usd: 0.15, per: "minute" } },
512
+ { id: "eleven_text_to_sound_v2", provider: "elevenlabs", kind: "sound-effect", label: "ElevenLabs Sound Effects v2", default: true, status: "ga", price: { usd: 0.12, per: "minute" }, note: "0.5-30 s" },
513
+ { id: "whisper-1", provider: "openai", kind: "transcription", label: "Whisper", default: true, status: "deprecated", shutdown: "2027-02-26", note: "Only OpenAI model with word timestamps" }
514
+ ];
515
+ }
516
+ });
517
+
443
518
  // ../ai-providers/dist/gemini/gemini-models.js
444
519
  function isGeminiTextModelAlias(model) {
445
520
  return Object.prototype.hasOwnProperty.call(GEMINI_TEXT_MODEL_ALIASES, model);
@@ -458,18 +533,11 @@ var GEMINI_DEFAULT_TEXT_MODEL, GEMINI_AGENT_DEFAULT_TEXT_MODEL, GEMINI_TEXT_MODE
458
533
  var init_gemini_models = __esm({
459
534
  "../ai-providers/dist/gemini/gemini-models.js"() {
460
535
  "use strict";
461
- GEMINI_DEFAULT_TEXT_MODEL = "gemini-3.5-flash";
462
- GEMINI_AGENT_DEFAULT_TEXT_MODEL = "gemini-2.5-flash";
463
- GEMINI_TEXT_MODEL_ALIASES = {
464
- flash: GEMINI_DEFAULT_TEXT_MODEL,
465
- latest: GEMINI_DEFAULT_TEXT_MODEL,
466
- "flash-3.5": GEMINI_DEFAULT_TEXT_MODEL,
467
- "flash-3": "gemini-3-flash-preview",
468
- "flash-2.5": "gemini-2.5-flash",
469
- pro: "gemini-2.5-pro",
470
- "pro-3.1": "gemini-3.1-pro-preview"
471
- };
472
- GEMINI_TEXT_MODEL_HELP = "flash/latest (Gemini 3.5 Flash), flash-3.5, flash-3, flash-2.5, pro, pro-3.1, or a full gemini-* model ID";
536
+ init_catalog();
537
+ GEMINI_DEFAULT_TEXT_MODEL = defaultModel("gemini", "llm").id;
538
+ GEMINI_AGENT_DEFAULT_TEXT_MODEL = GEMINI_DEFAULT_TEXT_MODEL;
539
+ GEMINI_TEXT_MODEL_ALIASES = Object.fromEntries(listModels({ provider: "gemini", kind: "llm" }).flatMap((m2) => (m2.aliases ?? []).map((a4) => [a4, m2.id])));
540
+ GEMINI_TEXT_MODEL_HELP = "flash/latest (Gemini 3.8 Flash), flash-3.8, flash-3.5, flash-3, flash-2.5, pro (Gemini 3.1 Pro), pro-3.1, pro-2.5, or a full gemini-* model ID";
473
541
  }
474
542
  });
475
543
 
@@ -656,7 +724,6 @@ UNIVERSAL RULES:
656
724
  system_instruction: { parts: [{ text: systemPrompt }] },
657
725
  contents: [{ role: "user", parts: [{ text: userPrompt }] }],
658
726
  generationConfig: {
659
- temperature: 0.8,
660
727
  maxOutputTokens: 16384
661
728
  }
662
729
  })
@@ -757,7 +824,7 @@ Return the complete modified component as JSON.`
757
824
  ]
758
825
  }
759
826
  ],
760
- generationConfig: { temperature: 0.5, maxOutputTokens: 16384 }
827
+ generationConfig: { maxOutputTokens: 16384 }
761
828
  })
762
829
  });
763
830
  if (!response.ok) {
@@ -803,18 +870,7 @@ var init_gemini_motion = __esm({
803
870
  "../ai-providers/dist/gemini/gemini-motion.js"() {
804
871
  "use strict";
805
872
  init_gemini_models();
806
- GEMINI_MOTION_MODELS = {
807
- gemini: GEMINI_DEFAULT_TEXT_MODEL,
808
- flash: GEMINI_DEFAULT_TEXT_MODEL,
809
- latest: GEMINI_DEFAULT_TEXT_MODEL,
810
- "flash-3.5": GEMINI_DEFAULT_TEXT_MODEL,
811
- "flash-3": "gemini-3-flash-preview",
812
- "flash-2.5": "gemini-2.5-flash",
813
- pro: "gemini-2.5-pro",
814
- "2.5-pro": "gemini-2.5-pro",
815
- "3.1-pro": "gemini-3.1-pro-preview",
816
- "pro-3.1": "gemini-3.1-pro-preview"
817
- };
873
+ GEMINI_MOTION_MODELS = GEMINI_TEXT_MODEL_ALIASES;
818
874
  STYLE_USER_INSTRUCTIONS = {
819
875
  minimal: "CENTER-ALIGNED pure typography. Thin lines + large title + subtitle. NO glass panels, NO bokeh, NO dark bars. Breathing pulse during hold.",
820
876
  corporate: "Data-rich info bar at bottom + top-right badge. Animated number counter. Brand color stripe. Progress bar.",
@@ -922,7 +978,6 @@ var init_storyboard_prompt = __esm({
922
978
  async function analyzeContent(api, content, targetDuration, options) {
923
979
  const creativity = options?.creativity || "low";
924
980
  const systemPrompt = buildStoryboardSystemPrompt(targetDuration, creativity);
925
- const temperature = creativity === "high" ? 1 : 0.7;
926
981
  try {
927
982
  const payload = {
928
983
  system_instruction: {
@@ -932,7 +987,6 @@ async function analyzeContent(api, content, targetDuration, options) {
932
987
  parts: [{ text: buildStoryboardUserMessage(content) }]
933
988
  }],
934
989
  generationConfig: {
935
- temperature,
936
990
  maxOutputTokens: 4096
937
991
  }
938
992
  };
@@ -1003,7 +1057,15 @@ var init_http = __esm({
1003
1057
  });
1004
1058
 
1005
1059
  // ../ai-providers/dist/gemini/GeminiProvider.js
1006
- var MODEL_MAP, GeminiProvider, geminiProvider;
1060
+ function resolveVeoModel(alias) {
1061
+ if (Object.hasOwn(VEO_MODEL_ALIASES, alias)) {
1062
+ return VEO_MODEL_ALIASES[alias];
1063
+ }
1064
+ const valid = Object.keys(VEO_MODEL_ALIASES).join(", ");
1065
+ const hint = alias === "3.0" ? " Veo 3.0 was shut down by Google on 2025-11-12." : "";
1066
+ throw new Error(`Unknown Veo model "${alias}". Valid: ${valid}.${hint}`);
1067
+ }
1068
+ var VEO_MODEL_ALIASES, GeminiProvider, geminiProvider;
1007
1069
  var init_GeminiProvider = __esm({
1008
1070
  "../ai-providers/dist/gemini/GeminiProvider.js"() {
1009
1071
  "use strict";
@@ -1011,15 +1073,8 @@ var init_GeminiProvider = __esm({
1011
1073
  init_gemini_models();
1012
1074
  init_gemini_storyboard();
1013
1075
  init_http();
1014
- MODEL_MAP = {
1015
- "flash": "gemini-2.5-flash-image",
1016
- "3.1-flash": "gemini-3.1-flash-image-preview",
1017
- "latest": "gemini-3.1-flash-image-preview",
1018
- "pro": "gemini-3-pro-image-preview",
1019
- "gemini-2.5-flash-image": "gemini-2.5-flash-image",
1020
- "gemini-3.1-flash-image-preview": "gemini-3.1-flash-image-preview",
1021
- "gemini-3-pro-image-preview": "gemini-3-pro-image-preview"
1022
- };
1076
+ init_catalog();
1077
+ VEO_MODEL_ALIASES = Object.fromEntries(listModels({ provider: "veo", kind: "video" }).flatMap((m2) => (m2.aliases ?? []).map((a4) => [a4, m2.id])));
1023
1078
  GeminiProvider = class {
1024
1079
  constructor() {
1025
1080
  this.id = "gemini";
@@ -1278,9 +1333,7 @@ var init_GeminiProvider = __esm({
1278
1333
  * Resolve model alias to full model ID
1279
1334
  */
1280
1335
  resolveModel(model) {
1281
- if (!model)
1282
- return MODEL_MAP["flash"];
1283
- return MODEL_MAP[model] || MODEL_MAP["flash"];
1336
+ return (findModel("gemini", "image", model) ?? defaultModel("gemini", "image")).id;
1284
1337
  }
1285
1338
  /**
1286
1339
  * Check if model is Pro
@@ -1324,7 +1377,7 @@ var init_GeminiProvider = __esm({
1324
1377
  contents: [{ parts: [{ text: prompt3 }] }],
1325
1378
  generationConfig
1326
1379
  };
1327
- const is31Flash = modelId === "gemini-3.1-flash-image-preview";
1380
+ const is31Flash = modelId.startsWith("gemini-3.1-flash-image");
1328
1381
  if (options.imageSearchGrounding && is31Flash) {
1329
1382
  payload.tools = [{ googleSearch: { searchTypes: { webSearch: {}, imageSearch: {} } } }];
1330
1383
  } else if (options.grounding && isPro) {
@@ -1552,7 +1605,6 @@ var init_GeminiProvider = __esm({
1552
1605
  videoPart.video_metadata = videoMetadata;
1553
1606
  }
1554
1607
  const generationConfig = {
1555
- temperature: 0.4,
1556
1608
  maxOutputTokens: 8192
1557
1609
  };
1558
1610
  if (options.lowResolution) {
@@ -1634,7 +1686,6 @@ var init_GeminiProvider = __esm({
1634
1686
  }
1635
1687
  }));
1636
1688
  const generationConfig = {
1637
- temperature: 0.4,
1638
1689
  maxOutputTokens: 8192
1639
1690
  };
1640
1691
  if (options.lowResolution) {
@@ -1734,7 +1785,6 @@ Respond with ONLY the JSON array, no other text.`;
1734
1785
  body: JSON.stringify({
1735
1786
  contents: [{ parts: [{ text: prompt3 }] }],
1736
1787
  generationConfig: {
1737
- temperature: 0.3,
1738
1788
  maxOutputTokens: 1024
1739
1789
  }
1740
1790
  })
@@ -1828,7 +1878,7 @@ Respond with ONLY the JSON array, no other text.`;
1828
1878
  return refineMotion({ apiKey: this.apiKey, baseUrl: this.baseUrl }, existingCode, instructions, options);
1829
1879
  }
1830
1880
  /**
1831
- * Generate a storyboard from script content using Gemini 2.5 Flash.
1881
+ * Generate a storyboard from script content using the default Gemini Flash model.
1832
1882
  * Alternative to ClaudeProvider.analyzeContent for when Claude is unavailable.
1833
1883
  */
1834
1884
  async analyzeContent(content, targetDuration, options) {
@@ -1843,43 +1893,48 @@ Respond with ONLY the JSON array, no other text.`;
1843
1893
  });
1844
1894
 
1845
1895
  // ../ai-providers/dist/gemini/gemini-omni.js
1846
- function firstReferenceImage(options) {
1847
- const refs3 = options.referenceImages;
1848
- if (refs3 && refs3.length > 0 && refs3[0].base64)
1849
- return refs3[0];
1850
- const ref2 = options.referenceImage;
1851
- if (typeof ref2 === "string" && ref2) {
1896
+ function frameImages(options) {
1897
+ const frames = [];
1898
+ const toInline = (ref2) => {
1852
1899
  const m2 = ref2.match(/^data:(.+?);base64,(.*)$/);
1853
- if (m2)
1854
- return { mimeType: m2[1], base64: m2[2] };
1855
- return { mimeType: "image/png", base64: ref2 };
1900
+ return m2 ? { mimeType: m2[1], base64: m2[2] } : { mimeType: "image/png", base64: ref2 };
1901
+ };
1902
+ if (options.referenceImages?.[0]?.base64)
1903
+ frames.push(options.referenceImages[0]);
1904
+ else if (typeof options.referenceImage === "string" && options.referenceImage) {
1905
+ frames.push(toInline(options.referenceImage));
1856
1906
  }
1857
- return void 0;
1907
+ if (frames.length > 0 && options.lastFrame)
1908
+ frames.push(toInline(options.lastFrame));
1909
+ return frames;
1858
1910
  }
1859
- function findVideoUrl(obj) {
1860
- if (!obj || typeof obj !== "object")
1861
- return void 0;
1862
- for (const [k2, v4] of Object.entries(obj)) {
1863
- if (typeof v4 === "string" && /^https?:\/\//.test(v4) && /(uri|url|video)/i.test(k2))
1864
- return v4;
1865
- if (typeof v4 === "object") {
1866
- const nested = findVideoUrl(v4);
1867
- if (nested)
1868
- return nested;
1869
- }
1911
+ function findOmniVideo(response) {
1912
+ for (const step of response.steps ?? []) {
1913
+ if (step.type !== "model_output")
1914
+ continue;
1915
+ const video = step.content?.find((c2) => c2.type === "video");
1916
+ if (video)
1917
+ return video;
1870
1918
  }
1871
- return void 0;
1919
+ return response.output_video;
1920
+ }
1921
+ function fileIdFromUri(uri) {
1922
+ return uri.match(/files\/([^/?:]+)/)?.[1];
1872
1923
  }
1873
- var OMNI_MODEL, INTERACTIONS_URL, OmniProvider, omniProvider;
1924
+ var OMNI_MODEL, API_ROOT, FILE_POLL_INTERVAL_MS, FILE_POLL_TIMEOUT_MS, OmniProvider, omniProvider;
1874
1925
  var init_gemini_omni = __esm({
1875
1926
  "../ai-providers/dist/gemini/gemini-omni.js"() {
1876
1927
  "use strict";
1877
- OMNI_MODEL = "gemini-omni-flash-preview";
1878
- INTERACTIONS_URL = "https://generativelanguage.googleapis.com/v1beta/interactions";
1928
+ init_http();
1929
+ init_catalog();
1930
+ OMNI_MODEL = defaultModel("omni", "video").id;
1931
+ API_ROOT = "https://generativelanguage.googleapis.com";
1932
+ FILE_POLL_INTERVAL_MS = 3e3;
1933
+ FILE_POLL_TIMEOUT_MS = 5 * 60 * 1e3;
1879
1934
  OmniProvider = class {
1880
1935
  constructor() {
1881
1936
  this.id = "omni";
1882
- this.label = "Gemini Omni (experimental)";
1937
+ this.label = "Gemini Omni 1.1 Flash";
1883
1938
  }
1884
1939
  async initialize(config4) {
1885
1940
  this.apiKey = config4.apiKey;
@@ -1892,22 +1947,23 @@ var init_gemini_omni = __esm({
1892
1947
  return { id: "", status: "failed", error: "GOOGLE_API_KEY not configured for Gemini Omni" };
1893
1948
  }
1894
1949
  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
- }
1950
+ const frames = frameImages(opts);
1951
+ const input3 = frames.length === 0 ? prompt3 : [
1952
+ ...frames.map((f2) => ({ type: "image", data: f2.base64, mime_type: f2.mimeType })),
1953
+ { type: "text", text: prompt3 }
1954
+ ];
1902
1955
  const body = {
1903
1956
  model: OMNI_MODEL,
1904
- inputs,
1905
- generation_config: {
1906
- video_config: { task, aspect_ratio: aspectRatio2 }
1957
+ input: input3,
1958
+ response_format: {
1959
+ type: "video",
1960
+ aspect_ratio: opts.aspectRatio === "9:16" ? "9:16" : "16:9",
1961
+ resolution: opts.resolution ?? "720p",
1962
+ delivery: "uri"
1907
1963
  }
1908
1964
  };
1909
1965
  try {
1910
- const res = await fetch(INTERACTIONS_URL, {
1966
+ const res = await fetch(`${API_ROOT}/v1beta/interactions`, {
1911
1967
  method: "POST",
1912
1968
  headers: { "Content-Type": "application/json", "x-goog-api-key": this.apiKey },
1913
1969
  body: JSON.stringify(body)
@@ -1917,33 +1973,52 @@ var init_gemini_omni = __esm({
1917
1973
  return {
1918
1974
  id: "",
1919
1975
  status: "failed",
1920
- error: `Gemini Omni (experimental) request failed: HTTP ${res.status} \u2014 ${text.slice(0, 300)}`
1976
+ error: `Gemini Omni request failed: HTTP ${res.status} - ${text.slice(0, 300)}`
1921
1977
  };
1922
1978
  }
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" };
1979
+ const parsed = JSON.parse(text);
1980
+ const id = parsed.id ?? "";
1981
+ const video = findOmniVideo(parsed);
1982
+ if (!video?.uri) {
1983
+ const reason = parsed.error?.message ?? `status ${parsed.status ?? "unknown"}`;
1984
+ return { id, status: "failed", error: `Gemini Omni returned no video (${reason})` };
1928
1985
  }
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
- };
1986
+ const fileId = fileIdFromUri(video.uri);
1987
+ if (!fileId) {
1988
+ return { id, status: "failed", error: `Gemini Omni returned an unexpected video URI: ${video.uri}` };
1989
+ }
1990
+ const ready = await this.waitForFile(fileId);
1991
+ if (ready !== "ACTIVE") {
1992
+ return { id, status: "failed", error: `Gemini Omni video file ended in state ${ready}` };
1937
1993
  }
1938
- return { id, status: "completed", videoUrl };
1994
+ return {
1995
+ id,
1996
+ status: "completed",
1997
+ videoUrl: `${API_ROOT}/download/v1beta/files/${fileId}:download?alt=media`
1998
+ };
1939
1999
  } catch (err) {
1940
2000
  return {
1941
2001
  id: "",
1942
2002
  status: "failed",
1943
- error: `Gemini Omni (experimental) error: ${err instanceof Error ? err.message : String(err)}`
2003
+ error: `Gemini Omni error: ${err instanceof Error ? err.message : String(err)}`
1944
2004
  };
1945
2005
  }
1946
2006
  }
2007
+ /** Poll the Files API until the generated video is downloadable. */
2008
+ async waitForFile(fileId) {
2009
+ const deadline = Date.now() + FILE_POLL_TIMEOUT_MS;
2010
+ for (; ; ) {
2011
+ const res = await fetch(`${API_ROOT}/v1beta/files/${fileId}`, {
2012
+ headers: { "x-goog-api-key": this.apiKey }
2013
+ });
2014
+ const state = res.ok ? (await res.json()).state : `HTTP ${res.status}`;
2015
+ if (state !== "PROCESSING")
2016
+ return state ?? "UNKNOWN";
2017
+ if (Date.now() > deadline)
2018
+ return "TIMEOUT";
2019
+ await sleep(FILE_POLL_INTERVAL_MS);
2020
+ }
2021
+ }
1947
2022
  };
1948
2023
  omniProvider = new OmniProvider();
1949
2024
  }
@@ -1977,24 +2052,33 @@ var init_gemini = __esm({
1977
2052
  label: "Veo",
1978
2053
  apiKey: "google",
1979
2054
  kinds: ["video"],
1980
- resolverPriority: { video: 3 },
2055
+ // No resolverPriority: the Veo 3.1 previews shut down on 2026-10-22, so
2056
+ // Veo is explicit-only (`-p veo`) and Omni is the Google default.
1981
2057
  commandsUnlocked: ["generate video -p veo"]
1982
2058
  });
1983
2059
  defineProvider({
1984
2060
  id: "omni",
1985
- label: "Gemini Omni (experimental)",
2061
+ label: "Gemini Omni",
1986
2062
  apiKey: "google",
1987
2063
  kinds: ["video"],
2064
+ resolverPriority: { video: 3 },
1988
2065
  commandsUnlocked: ["generate video -p omni"]
1989
2066
  });
1990
2067
  }
1991
2068
  });
1992
2069
 
2070
+ // ../ai-providers/dist/catalog/index.js
2071
+ var init_catalog2 = __esm({
2072
+ "../ai-providers/dist/catalog/index.js"() {
2073
+ "use strict";
2074
+ init_catalog();
2075
+ }
2076
+ });
2077
+
1993
2078
  // ../ai-providers/dist/openai/openai-storyboard.js
1994
2079
  async function analyzeContent2(apiKey, content, targetDuration, options) {
1995
2080
  const creativity = options?.creativity || "low";
1996
- const systemPrompt = buildStoryboardSystemPrompt(targetDuration, creativity);
1997
- const temperature = creativity === "high" ? 1 : 0.7;
2081
+ const systemPrompt = buildStoryboardSystemPrompt(targetDuration, creativity) + '\n\nWrap the array in a JSON object: {"segments": [ ...segment objects... ]}.';
1998
2082
  try {
1999
2083
  const response = await fetch("https://api.openai.com/v1/chat/completions", {
2000
2084
  method: "POST",
@@ -2003,13 +2087,13 @@ async function analyzeContent2(apiKey, content, targetDuration, options) {
2003
2087
  Authorization: `Bearer ${apiKey}`
2004
2088
  },
2005
2089
  body: JSON.stringify({
2006
- model: "gpt-5-mini",
2090
+ model: defaultModel("openai", "llm").id,
2007
2091
  messages: [
2008
2092
  { role: "system", content: systemPrompt },
2009
2093
  { role: "user", content: buildStoryboardUserMessage(content) }
2010
2094
  ],
2011
- temperature,
2012
- max_tokens: 4096,
2095
+ // Reasoning tokens count against this cap.
2096
+ max_completion_tokens: 16384,
2013
2097
  response_format: { type: "json_object" }
2014
2098
  })
2015
2099
  });
@@ -2048,6 +2132,7 @@ var init_openai_storyboard = __esm({
2048
2132
  "../ai-providers/dist/openai/openai-storyboard.js"() {
2049
2133
  "use strict";
2050
2134
  init_storyboard_prompt();
2135
+ init_catalog();
2051
2136
  }
2052
2137
  });
2053
2138
 
@@ -2057,6 +2142,7 @@ var init_OpenAIProvider = __esm({
2057
2142
  "../ai-providers/dist/openai/OpenAIProvider.js"() {
2058
2143
  "use strict";
2059
2144
  init_openai_storyboard();
2145
+ init_catalog();
2060
2146
  OpenAIProvider = class {
2061
2147
  constructor() {
2062
2148
  this.id = "openai-gpt";
@@ -2066,7 +2152,7 @@ var init_OpenAIProvider = __esm({
2066
2152
  this.iconUrl = "/icons/openai.svg";
2067
2153
  this.isAvailable = true;
2068
2154
  this.baseUrl = "https://api.openai.com/v1";
2069
- this.model = "gpt-5-mini";
2155
+ this.model = defaultModel("openai", "llm").id;
2070
2156
  }
2071
2157
  async initialize(config4) {
2072
2158
  this.apiKey = config4.apiKey;
@@ -2162,7 +2248,6 @@ Or if clarification needed:
2162
2248
  { role: "system", content: systemPrompt },
2163
2249
  { role: "user", content: instruction }
2164
2250
  ],
2165
- temperature: 0.2,
2166
2251
  response_format: { type: "json_object" }
2167
2252
  })
2168
2253
  });
@@ -2373,8 +2458,9 @@ Each segment should be 3-10 seconds long.`;
2373
2458
  Authorization: `Bearer ${this.apiKey}`
2374
2459
  },
2375
2460
  body: JSON.stringify({
2376
- model: "gpt-5-mini",
2377
- max_tokens: 4096,
2461
+ model: defaultModel("openai", "llm").id,
2462
+ // Reasoning tokens count against this cap.
2463
+ max_completion_tokens: 16384,
2378
2464
  response_format: { type: "json_object" },
2379
2465
  messages: [
2380
2466
  { role: "system", content: systemPrompt },
@@ -2407,7 +2493,7 @@ ${videoAnalysis}` }
2407
2493
  }
2408
2494
  }
2409
2495
  /**
2410
- * Generate a storyboard from script content using GPT-5-mini.
2496
+ * Generate a storyboard from script content using GPT-5.4 mini.
2411
2497
  * Alternative to ClaudeProvider.analyzeContent for when Claude is unavailable.
2412
2498
  */
2413
2499
  async analyzeContent(content, targetDuration, options) {
@@ -2455,10 +2541,12 @@ async function callClaude(params, opts) {
2455
2541
  },
2456
2542
  body: JSON.stringify({
2457
2543
  model: params.model,
2458
- max_tokens: opts.maxTokens,
2459
- ...opts.temperature !== void 0 ? { temperature: opts.temperature } : {},
2544
+ // Claude 5.x thinks by default and thinking tokens count against
2545
+ // max_tokens, so a small cap can end before any text is written.
2546
+ max_tokens: Math.max(opts.maxTokens, CLAUDE_MIN_MAX_TOKENS),
2460
2547
  messages: opts.messages,
2461
- system: opts.system
2548
+ system: opts.system,
2549
+ ...opts.jsonSchema ? { output_config: { format: { type: "json_schema", schema: opts.jsonSchema } } } : {}
2462
2550
  })
2463
2551
  });
2464
2552
  if (!response.ok) {
@@ -2480,9 +2568,11 @@ function extractJsonArray(text) {
2480
2568
  const match2 = text.match(/\[[\s\S]*\]/);
2481
2569
  return match2 ? match2[0] : null;
2482
2570
  }
2571
+ var CLAUDE_MIN_MAX_TOKENS;
2483
2572
  var init_claude_api = __esm({
2484
2573
  "../ai-providers/dist/claude/claude-api.js"() {
2485
2574
  "use strict";
2575
+ CLAUDE_MIN_MAX_TOKENS = 16e3;
2486
2576
  }
2487
2577
  });
2488
2578
 
@@ -2518,7 +2608,8 @@ MANDATORY ADAPTATION RULES:
2518
2608
  const text = await callClaude(api, {
2519
2609
  system: systemPrompt,
2520
2610
  messages: [{ role: "user", content: userContent }],
2521
- maxTokens: 16e3
2611
+ maxTokens: 16e3,
2612
+ jsonSchema: MOTION_COMPONENT_SCHEMA
2522
2613
  });
2523
2614
  const jsonStr = extractJsonObject(text);
2524
2615
  if (!jsonStr) {
@@ -2593,7 +2684,8 @@ Modification instructions: ${instructions}
2593
2684
 
2594
2685
  Return the complete modified component as JSON.`
2595
2686
  }],
2596
- maxTokens: 16e3
2687
+ maxTokens: 16e3,
2688
+ jsonSchema: MOTION_COMPONENT_SCHEMA
2597
2689
  });
2598
2690
  const jsonStr = extractJsonObject(text);
2599
2691
  if (!jsonStr)
@@ -2622,15 +2714,13 @@ async function analyzeContent3(api, content, targetDuration, options) {
2622
2714
  const creativity = options?.creativity || "low";
2623
2715
  const systemPrompt = buildStoryboardSystemPrompt(targetDuration, creativity);
2624
2716
  try {
2625
- const temperature = creativity === "high" ? 1 : 0.7;
2626
2717
  const text = await callClaude(api, {
2627
2718
  system: systemPrompt,
2628
2719
  messages: [{
2629
2720
  role: "user",
2630
2721
  content: buildStoryboardUserMessage(content)
2631
2722
  }],
2632
- maxTokens: 4096,
2633
- temperature
2723
+ maxTokens: 4096
2634
2724
  });
2635
2725
  const jsonStr = extractJsonArray(text);
2636
2726
  if (!jsonStr) {
@@ -2987,11 +3077,22 @@ UNIVERSAL RULES:
2987
3077
  - Both entrance AND exit animations
2988
3078
  - whiteSpace: "nowrap" on all text`;
2989
3079
  }
3080
+ var MOTION_COMPONENT_SCHEMA;
2990
3081
  var init_claude_motion = __esm({
2991
3082
  "../ai-providers/dist/claude/claude-motion.js"() {
2992
3083
  "use strict";
2993
3084
  init_claude_api();
2994
3085
  init_storyboard_prompt();
3086
+ MOTION_COMPONENT_SCHEMA = {
3087
+ type: "object",
3088
+ properties: {
3089
+ name: { type: "string" },
3090
+ code: { type: "string" },
3091
+ description: { type: "string" }
3092
+ },
3093
+ required: ["name", "code", "description"],
3094
+ additionalProperties: false
3095
+ };
2995
3096
  }
2996
3097
  });
2997
3098
 
@@ -3753,10 +3854,12 @@ var ClaudeProvider, claudeProvider;
3753
3854
  var init_ClaudeProvider = __esm({
3754
3855
  "../ai-providers/dist/claude/ClaudeProvider.js"() {
3755
3856
  "use strict";
3857
+ init_claude_api();
3756
3858
  init_claude_motion();
3757
3859
  init_claude_analysis();
3758
3860
  init_claude_viral();
3759
3861
  init_claude_visual_fx();
3862
+ init_catalog();
3760
3863
  ClaudeProvider = class {
3761
3864
  constructor() {
3762
3865
  this.id = "claude";
@@ -3766,7 +3869,7 @@ var init_ClaudeProvider = __esm({
3766
3869
  this.iconUrl = "/icons/claude.svg";
3767
3870
  this.isAvailable = true;
3768
3871
  this.baseUrl = "https://api.anthropic.com/v1";
3769
- this.model = "claude-sonnet-4-6";
3872
+ this.model = defaultModel("claude", "llm").id;
3770
3873
  }
3771
3874
  async initialize(config4) {
3772
3875
  this.apiKey = config4.apiKey;
@@ -3870,7 +3973,7 @@ Or if clarification needed:
3870
3973
  },
3871
3974
  body: JSON.stringify({
3872
3975
  model: this.model,
3873
- max_tokens: 2048,
3976
+ max_tokens: CLAUDE_MIN_MAX_TOKENS,
3874
3977
  messages: [{ role: "user", content: instruction }],
3875
3978
  system: systemPrompt
3876
3979
  })
@@ -3940,7 +4043,7 @@ Respond with JSON array:
3940
4043
  },
3941
4044
  body: JSON.stringify({
3942
4045
  model: this.model,
3943
- max_tokens: 2048,
4046
+ max_tokens: CLAUDE_MIN_MAX_TOKENS,
3944
4047
  messages: [{
3945
4048
  role: "user",
3946
4049
  content: `Clips: ${JSON.stringify(clipsInfo)}
@@ -4068,11 +4171,7 @@ Instruction: ${instruction}`
4068
4171
  return generateNarrationScript(this.api, videoAnalysis, duration, style, language);
4069
4172
  }
4070
4173
  };
4071
- ClaudeProvider.MOTION_MODELS = {
4072
- sonnet: "claude-sonnet-4-6",
4073
- opus: "claude-opus-4-7",
4074
- "opus-4-6": "claude-opus-4-6"
4075
- };
4174
+ ClaudeProvider.MOTION_MODELS = Object.fromEntries(listModels({ provider: "claude", kind: "llm" }).flatMap((m2) => (m2.aliases ?? []).map((a4) => [a4, m2.id])));
4076
4175
  claudeProvider = new ClaudeProvider();
4077
4176
  }
4078
4177
  });
@@ -4408,10 +4507,11 @@ function resolveVoiceId(input3) {
4408
4507
  const availableVoices = Object.keys(KNOWN_VOICES).join(", ");
4409
4508
  throw new Error(`Unknown voice "${input3}". Available voices: ${availableVoices}. You can also use a custom voice ID from your ElevenLabs account.`);
4410
4509
  }
4411
- var KNOWN_VOICES, TTS_429_MAX_RETRIES, TTS_429_RETRY_DELAY_MS, ElevenLabsProvider, elevenLabsProvider;
4510
+ var KNOWN_VOICES, TTS_429_MAX_RETRIES, TTS_429_RETRY_DELAY_MS, ELEVENLABS_MUSIC_MODEL, ElevenLabsProvider, elevenLabsProvider;
4412
4511
  var init_ElevenLabsProvider = __esm({
4413
4512
  "../ai-providers/dist/elevenlabs/ElevenLabsProvider.js"() {
4414
4513
  "use strict";
4514
+ init_catalog();
4415
4515
  KNOWN_VOICES = {
4416
4516
  // Default/popular voices
4417
4517
  rachel: "21m00Tcm4TlvDq8ikWAM",
@@ -4445,6 +4545,7 @@ var init_ElevenLabsProvider = __esm({
4445
4545
  };
4446
4546
  TTS_429_MAX_RETRIES = 1;
4447
4547
  TTS_429_RETRY_DELAY_MS = 2e3;
4548
+ ELEVENLABS_MUSIC_MODEL = defaultModel("elevenlabs", "music").id;
4448
4549
  ElevenLabsProvider = class {
4449
4550
  constructor() {
4450
4551
  this.id = "elevenlabs";
@@ -4525,9 +4626,10 @@ var init_ElevenLabsProvider = __esm({
4525
4626
  stability: options.stability ?? 0.5,
4526
4627
  similarity_boost: options.similarityBoost ?? 0.75,
4527
4628
  style: options.style ?? 0,
4528
- use_speaker_boost: true
4529
- },
4530
- ...options.speed !== void 0 && { speed: options.speed }
4629
+ use_speaker_boost: true,
4630
+ // The API reads speed only inside voice_settings.
4631
+ ...options.speed !== void 0 && { speed: options.speed }
4632
+ }
4531
4633
  })
4532
4634
  });
4533
4635
  if (response.status === 429 && attempt < TTS_429_MAX_RETRIES) {
@@ -4597,7 +4699,7 @@ var init_ElevenLabsProvider = __esm({
4597
4699
  prompt_influence: options.promptInfluence ?? 0.3
4598
4700
  };
4599
4701
  if (options.duration !== void 0) {
4600
- const duration = Math.max(0.5, Math.min(22, options.duration));
4702
+ const duration = Math.max(0.5, Math.min(30, options.duration));
4601
4703
  body.duration_seconds = duration;
4602
4704
  }
4603
4705
  const response = await fetch(`${this.baseUrl}/sound-generation`, {
@@ -4642,7 +4744,7 @@ var init_ElevenLabsProvider = __esm({
4642
4744
  try {
4643
4745
  const body = {
4644
4746
  prompt: prompt3,
4645
- model_id: "music_v1"
4747
+ model_id: ELEVENLABS_MUSIC_MODEL
4646
4748
  };
4647
4749
  if (options.duration !== void 0) {
4648
4750
  const duration = Math.max(3, Math.min(600, options.duration));
@@ -4853,6 +4955,55 @@ var init_elevenlabs = __esm({
4853
4955
  });
4854
4956
 
4855
4957
  // ../ai-providers/dist/kokoro/KokoroProvider.js
4958
+ function splitForKokoro(text, maxChars = KOKORO_MAX_CHUNK_CHARS) {
4959
+ const pieces = (part, pattern) => part.length <= maxChars ? [part] : part.split(pattern).filter((p2) => p2.trim().length > 0);
4960
+ const units2 = text.trim().split(/(?<=[.!?…])\s+/).flatMap((sentence) => pieces(sentence, /(?<=[,;:])\s+/)).flatMap((clause) => {
4961
+ if (clause.length <= maxChars)
4962
+ return [clause];
4963
+ const words = [];
4964
+ let line = "";
4965
+ for (const word of clause.split(/\s+/)) {
4966
+ if (line && line.length + word.length + 1 > maxChars) {
4967
+ words.push(line);
4968
+ line = word;
4969
+ } else {
4970
+ line = line ? `${line} ${word}` : word;
4971
+ }
4972
+ }
4973
+ if (line)
4974
+ words.push(line);
4975
+ return words;
4976
+ });
4977
+ const chunks = [];
4978
+ for (const unit of units2) {
4979
+ const last3 = chunks.at(-1);
4980
+ if (last3 !== void 0 && last3.length + unit.length + 1 <= maxChars) {
4981
+ chunks[chunks.length - 1] = `${last3} ${unit}`;
4982
+ } else {
4983
+ chunks.push(unit);
4984
+ }
4985
+ }
4986
+ return chunks;
4987
+ }
4988
+ function encodeFloatWav(samples, sampleRate) {
4989
+ const dataBytes = samples.length * 4;
4990
+ const buffer = Buffer.alloc(44 + dataBytes);
4991
+ buffer.write("RIFF", 0, "ascii");
4992
+ buffer.writeUInt32LE(36 + dataBytes, 4);
4993
+ buffer.write("WAVE", 8, "ascii");
4994
+ buffer.write("fmt ", 12, "ascii");
4995
+ buffer.writeUInt32LE(16, 16);
4996
+ buffer.writeUInt16LE(3, 20);
4997
+ buffer.writeUInt16LE(1, 22);
4998
+ buffer.writeUInt32LE(sampleRate, 24);
4999
+ buffer.writeUInt32LE(sampleRate * 4, 28);
5000
+ buffer.writeUInt16LE(4, 32);
5001
+ buffer.writeUInt16LE(32, 34);
5002
+ buffer.write("data", 36, "ascii");
5003
+ buffer.writeUInt32LE(dataBytes, 40);
5004
+ Buffer.from(samples.buffer, samples.byteOffset, dataBytes).copy(buffer, 44);
5005
+ return buffer;
5006
+ }
4856
5007
  async function loadKokoroRuntime() {
4857
5008
  if (factoryOverride)
4858
5009
  return { factory: factoryOverride, device: "cpu" };
@@ -4890,7 +5041,7 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
4890
5041
  if (!base2)
4891
5042
  return null;
4892
5043
  try {
4893
- const { readFile: readFile48, writeFile: writeFile52, mkdir: mkdir31 } = await import("node:fs/promises");
5044
+ const { readFile: readFile48, writeFile: writeFile51, mkdir: mkdir33 } = await import("node:fs/promises");
4894
5045
  const { join: join79, resolve: resolve86 } = await import("node:path");
4895
5046
  const { pathToFileURL: pathToFileURL2 } = await import("node:url");
4896
5047
  const { homedir: homedir8 } = await import("node:os");
@@ -4911,8 +5062,8 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
4911
5062
  async put(key2, response) {
4912
5063
  try {
4913
5064
  const buf = Buffer.from(await response.arrayBuffer());
4914
- await mkdir31(cacheRoot, { recursive: true });
4915
- await writeFile52(join79(cacheRoot, encodeURIComponent(String(key2))), buf);
5065
+ await mkdir33(cacheRoot, { recursive: true });
5066
+ await writeFile51(join79(cacheRoot, encodeURIComponent(String(key2))), buf);
4916
5067
  } catch {
4917
5068
  }
4918
5069
  }
@@ -4961,12 +5112,13 @@ function normaliseEvent(raw2) {
4961
5112
  total: typeof r2.total === "number" ? r2.total : void 0
4962
5113
  };
4963
5114
  }
4964
- var KOKORO_DEFAULT_VOICE, KOKORO_MODEL_ID, modelPromise, factoryOverride, KokoroProvider, kokoroProvider;
5115
+ var KOKORO_DEFAULT_VOICE, KOKORO_MODEL_ID, KOKORO_MAX_CHUNK_CHARS, modelPromise, factoryOverride, KokoroProvider, kokoroProvider;
4965
5116
  var init_KokoroProvider = __esm({
4966
5117
  "../ai-providers/dist/kokoro/KokoroProvider.js"() {
4967
5118
  "use strict";
4968
5119
  KOKORO_DEFAULT_VOICE = "af_heart";
4969
5120
  KOKORO_MODEL_ID = "onnx-community/Kokoro-82M-v1.0-ONNX";
5121
+ KOKORO_MAX_CHUNK_CHARS = 300;
4970
5122
  modelPromise = null;
4971
5123
  factoryOverride = null;
4972
5124
  KokoroProvider = class {
@@ -4993,11 +5145,32 @@ var init_KokoroProvider = __esm({
4993
5145
  }
4994
5146
  try {
4995
5147
  const model = await loadModel(options.onProgress);
4996
- const audio = await model.generate(text, {
5148
+ const voiceOptions = {
4997
5149
  voice: options.voice ?? KOKORO_DEFAULT_VOICE,
4998
5150
  speed: options.speed ?? 1
4999
- });
5000
- const buffer = Buffer.from(audio.toWav());
5151
+ };
5152
+ const chunks = splitForKokoro(text);
5153
+ let buffer;
5154
+ if (chunks.length === 1) {
5155
+ buffer = Buffer.from((await model.generate(chunks[0], voiceOptions)).toWav());
5156
+ } else {
5157
+ const parts = [];
5158
+ let sampleRate = 24e3;
5159
+ for (const chunk of chunks) {
5160
+ const audio = await model.generate(chunk, voiceOptions);
5161
+ if (!audio.audio)
5162
+ throw new Error("Kokoro returned no raw samples to join");
5163
+ parts.push(audio.audio);
5164
+ sampleRate = audio.sampling_rate ?? sampleRate;
5165
+ }
5166
+ const samples = new Float32Array(parts.reduce((n2, part) => n2 + part.length, 0));
5167
+ let offset = 0;
5168
+ for (const part of parts) {
5169
+ samples.set(part, offset);
5170
+ offset += part.length;
5171
+ }
5172
+ buffer = encodeFloatWav(samples, sampleRate);
5173
+ }
5001
5174
  return {
5002
5175
  success: true,
5003
5176
  audioBuffer: buffer,
@@ -5032,11 +5205,13 @@ var init_kokoro = __esm({
5032
5205
  });
5033
5206
 
5034
5207
  // ../ai-providers/dist/openai-image/OpenAIImageProvider.js
5035
- var DEFAULT_MODEL, OpenAIImageProvider, openaiImageProvider;
5208
+ var OPENAI_IMAGE_DEFAULT_MODEL, DEFAULT_MODEL, OpenAIImageProvider, openaiImageProvider;
5036
5209
  var init_OpenAIImageProvider = __esm({
5037
5210
  "../ai-providers/dist/openai-image/OpenAIImageProvider.js"() {
5038
5211
  "use strict";
5039
- DEFAULT_MODEL = "gpt-image-2";
5212
+ init_catalog();
5213
+ OPENAI_IMAGE_DEFAULT_MODEL = defaultModel("openai", "image").id;
5214
+ DEFAULT_MODEL = OPENAI_IMAGE_DEFAULT_MODEL;
5040
5215
  OpenAIImageProvider = class {
5041
5216
  constructor() {
5042
5217
  this.id = "openai-image";
@@ -5058,7 +5233,7 @@ var init_OpenAIImageProvider = __esm({
5058
5233
  }
5059
5234
  /**
5060
5235
  * Generate images from text prompt
5061
- * Uses GPT Image 1.5 by default (fastest, best quality)
5236
+ * Uses GPT Image 2.5 Sunburst by default
5062
5237
  */
5063
5238
  async generateImage(prompt3, options = {}) {
5064
5239
  if (!this.apiKey) {
@@ -5068,28 +5243,20 @@ var init_OpenAIImageProvider = __esm({
5068
5243
  };
5069
5244
  }
5070
5245
  const model = options.model || DEFAULT_MODEL;
5071
- const isGPTImage = model === "gpt-image-1.5" || model === "gpt-image-2";
5072
5246
  try {
5073
5247
  const body = {
5074
5248
  model,
5075
5249
  prompt: prompt3,
5076
5250
  n: options.n || 1
5077
5251
  };
5078
- if (isGPTImage) {
5079
- const qualityMap = {
5080
- standard: "medium",
5081
- hd: "high"
5082
- };
5083
- const quality = options.quality || "medium";
5084
- body.quality = qualityMap[quality] || quality;
5085
- if (options.size && options.size !== "auto") {
5086
- body.size = options.size;
5087
- }
5088
- } else {
5089
- body.response_format = "url";
5090
- body.size = options.size || "1024x1024";
5091
- body.quality = options.quality === "high" ? "hd" : options.quality || "standard";
5092
- body.style = options.style || "vivid";
5252
+ const qualityMap = {
5253
+ standard: "medium",
5254
+ hd: "high"
5255
+ };
5256
+ const quality = options.quality || "medium";
5257
+ body.quality = qualityMap[quality] || quality;
5258
+ if (options.size && options.size !== "auto") {
5259
+ body.size = options.size;
5093
5260
  }
5094
5261
  const response = await fetch(`${this.baseUrl}/images/generations`, {
5095
5262
  method: "POST",
@@ -5242,49 +5409,6 @@ var init_OpenAIImageProvider = __esm({
5242
5409
  };
5243
5410
  }
5244
5411
  }
5245
- /**
5246
- * Create image variations (uses DALL-E 2)
5247
- */
5248
- async createVariation(imageBuffer, options = {}) {
5249
- if (!this.apiKey) {
5250
- return {
5251
- success: false,
5252
- error: "OpenAI API key not configured"
5253
- };
5254
- }
5255
- try {
5256
- const formData = new FormData();
5257
- const uint8Array = new Uint8Array(imageBuffer);
5258
- formData.append("image", new Blob([uint8Array], { type: "image/png" }), "image.png");
5259
- formData.append("model", "dall-e-2");
5260
- formData.append("n", String(options.n || 1));
5261
- formData.append("size", options.size || "1024x1024");
5262
- const response = await fetch(`${this.baseUrl}/images/variations`, {
5263
- method: "POST",
5264
- headers: {
5265
- Authorization: `Bearer ${this.apiKey}`
5266
- },
5267
- body: formData
5268
- });
5269
- if (!response.ok) {
5270
- const error = await response.text();
5271
- return {
5272
- success: false,
5273
- error: `API error: ${response.status} - ${error}`
5274
- };
5275
- }
5276
- const data2 = await response.json();
5277
- return {
5278
- success: true,
5279
- images: data2.data.map((img) => ({ url: img.url }))
5280
- };
5281
- } catch (error) {
5282
- return {
5283
- success: false,
5284
- error: error instanceof Error ? error.message : "Unknown error"
5285
- };
5286
- }
5287
- }
5288
5412
  };
5289
5413
  openaiImageProvider = new OpenAIImageProvider();
5290
5414
  }
@@ -5410,7 +5534,8 @@ var DEFAULT_MODEL3, RunwayProvider, runwayProvider;
5410
5534
  var init_RunwayProvider = __esm({
5411
5535
  "../ai-providers/dist/runway/RunwayProvider.js"() {
5412
5536
  "use strict";
5413
- DEFAULT_MODEL3 = "gen4.5";
5537
+ init_catalog();
5538
+ DEFAULT_MODEL3 = defaultModel("runway", "video").id;
5414
5539
  RunwayProvider = class _RunwayProvider {
5415
5540
  constructor() {
5416
5541
  this.id = "runway";
@@ -5463,6 +5588,13 @@ var init_RunwayProvider = __esm({
5463
5588
  }
5464
5589
  const hasImage = !!options?.referenceImage;
5465
5590
  const endpoint = hasImage ? "image_to_video" : "text_to_video";
5591
+ if (!hasImage && apiRatio === "960:960") {
5592
+ return {
5593
+ id: "",
5594
+ status: "failed",
5595
+ error: "Runway text-to-video supports 16:9 and 9:16 only. Use 16:9 or 9:16, or pass an image (-i) for square output."
5596
+ };
5597
+ }
5466
5598
  const body = {
5467
5599
  model,
5468
5600
  promptText: prompt3,
@@ -5563,7 +5695,9 @@ var init_RunwayProvider = __esm({
5563
5695
  SUCCEEDED: "completed",
5564
5696
  FAILED: "failed",
5565
5697
  CANCELLED: "cancelled",
5566
- THROTTLED: "failed"
5698
+ // THROTTLED means queued behind the account's concurrency limit; the
5699
+ // task starts on its own, so keep polling.
5700
+ THROTTLED: "pending"
5567
5701
  };
5568
5702
  const result = {
5569
5703
  id: data2.id,
@@ -5573,8 +5707,9 @@ var init_RunwayProvider = __esm({
5573
5707
  if (data2.status === "SUCCEEDED" && data2.output && data2.output.length > 0) {
5574
5708
  result.videoUrl = data2.output[0];
5575
5709
  }
5576
- if (data2.status === "FAILED" || data2.status === "THROTTLED") {
5577
- result.error = data2.failure || data2.failureCode || "Generation failed";
5710
+ if (data2.status === "FAILED") {
5711
+ const reason = data2.failure || "Generation failed";
5712
+ result.error = data2.failureCode ? `${reason} (${data2.failureCode})` : reason;
5578
5713
  }
5579
5714
  return result;
5580
5715
  } catch (error) {
@@ -5586,27 +5721,14 @@ var init_RunwayProvider = __esm({
5586
5721
  }
5587
5722
  }
5588
5723
  /**
5589
- * Cancel ongoing generation
5724
+ * Cancel a pending or running task. Runway documents cancellation as
5725
+ * `DELETE /v1/tasks/{id}` (there is no `/cancel` route).
5590
5726
  */
5591
5727
  async cancelGeneration(id) {
5592
- if (!this.apiKey) {
5593
- return false;
5594
- }
5595
- try {
5596
- const response = await fetch(`${this.baseUrl}/tasks/${id}/cancel`, {
5597
- method: "POST",
5598
- headers: {
5599
- Authorization: `Bearer ${this.apiKey}`,
5600
- "X-Runway-Version": _RunwayProvider.API_VERSION
5601
- }
5602
- });
5603
- return response.ok;
5604
- } catch {
5605
- return false;
5606
- }
5728
+ return this.deleteTask(id);
5607
5729
  }
5608
5730
  /**
5609
- * Delete a completed task
5731
+ * Cancel a running task, or delete a finished one (`DELETE /v1/tasks/{id}`).
5610
5732
  */
5611
5733
  async deleteTask(id) {
5612
5734
  if (!this.apiKey) {
@@ -5715,12 +5837,25 @@ var init_runway = __esm({
5715
5837
 
5716
5838
  // ../ai-providers/dist/kling/KlingProvider.js
5717
5839
  import { createHmac } from "node:crypto";
5840
+ function resolveKlingModel(alias) {
5841
+ const model = findModel("kling", "video", alias);
5842
+ if (model)
5843
+ return model.id;
5844
+ throw new Error(`Unknown Kling model "${alias}". Valid: ${modelAliases("kling", "video").join(", ")}.`);
5845
+ }
5846
+ function klingDuration(model, seconds) {
5847
+ const requested = typeof seconds === "number" && Number.isFinite(seconds) ? Math.round(seconds) : 5;
5848
+ if (model === "kling-v3")
5849
+ return String(Math.max(3, Math.min(15, requested)));
5850
+ return requested >= 10 ? "10" : "5";
5851
+ }
5718
5852
  var DEFAULT_MODEL4, STD_MODE_MODELS, KlingProvider, klingProvider;
5719
5853
  var init_KlingProvider = __esm({
5720
5854
  "../ai-providers/dist/kling/KlingProvider.js"() {
5721
5855
  "use strict";
5722
- DEFAULT_MODEL4 = "kling-v2-5-turbo";
5723
- STD_MODE_MODELS = ["kling-v2-5-turbo", "kling-v2-6", "kling-v3", "kling-v3-omni"];
5856
+ init_catalog();
5857
+ DEFAULT_MODEL4 = defaultModel("kling", "video").id;
5858
+ STD_MODE_MODELS = ["kling-v2-5-turbo", "kling-v2-6", "kling-v3"];
5724
5859
  KlingProvider = class {
5725
5860
  constructor() {
5726
5861
  this.id = "kling";
@@ -5735,11 +5870,11 @@ var init_KlingProvider = __esm({
5735
5870
  async initialize(config4) {
5736
5871
  if (config4.apiKey) {
5737
5872
  const parts = config4.apiKey.split(":");
5738
- if (parts.length === 2) {
5873
+ if (parts.length === 2 && parts[0] && parts[1]) {
5739
5874
  this.accessKey = parts[0];
5740
5875
  this.secretKey = parts[1];
5741
5876
  } else {
5742
- this.accessKey = config4.apiKey;
5877
+ this.apiKey = config4.apiKey;
5743
5878
  }
5744
5879
  }
5745
5880
  if (config4.baseUrl) {
@@ -5747,7 +5882,11 @@ var init_KlingProvider = __esm({
5747
5882
  }
5748
5883
  }
5749
5884
  isConfigured() {
5750
- return !!(this.accessKey && this.secretKey);
5885
+ return !!(this.apiKey || this.accessKey && this.secretKey);
5886
+ }
5887
+ /** Bearer value: the API key itself, or a JWT signed from the legacy pair. */
5888
+ bearerToken() {
5889
+ return this.apiKey ?? this.generateToken();
5751
5890
  }
5752
5891
  /**
5753
5892
  * Generate JWT token for Kling API authentication
@@ -5777,11 +5916,11 @@ var init_KlingProvider = __esm({
5777
5916
  return {
5778
5917
  id: "",
5779
5918
  status: "failed",
5780
- error: "Kling API credentials not configured. Use format: KLING_ACCESS_KEY:KLING_SECRET_KEY"
5919
+ error: "Kling API credentials not configured. Set KLING_API_KEY to your Kling API key (or the legacy ACCESS_KEY:SECRET_KEY pair)."
5781
5920
  };
5782
5921
  }
5783
5922
  try {
5784
- const token = this.generateToken();
5923
+ const token = this.bearerToken();
5785
5924
  const model = options?.model || DEFAULT_MODEL4;
5786
5925
  let mode = options?.mode || "std";
5787
5926
  if (!STD_MODE_MODELS.includes(model) && mode === "std") {
@@ -5799,7 +5938,7 @@ var init_KlingProvider = __esm({
5799
5938
  model_name: model,
5800
5939
  mode,
5801
5940
  aspect_ratio: aspectRatioMap[options?.aspectRatio || "16:9"] || "16:9",
5802
- duration: options?.duration === 10 ? "10" : "5"
5941
+ duration: klingDuration(model, options?.duration)
5803
5942
  };
5804
5943
  if (options?.negativePrompt) {
5805
5944
  body.negative_prompt = options.negativePrompt;
@@ -5911,7 +6050,7 @@ var init_KlingProvider = __esm({
5911
6050
  };
5912
6051
  }
5913
6052
  try {
5914
- const token = this.generateToken();
6053
+ const token = this.bearerToken();
5915
6054
  const response = await fetch(`${this.baseUrl}/videos/${type}/${id}`, {
5916
6055
  headers: {
5917
6056
  "Content-Type": "application/json",
@@ -5993,11 +6132,11 @@ var init_KlingProvider = __esm({
5993
6132
  return {
5994
6133
  id: "",
5995
6134
  status: "failed",
5996
- error: "Kling API credentials not configured. Use format: KLING_ACCESS_KEY:KLING_SECRET_KEY"
6135
+ error: "Kling API credentials not configured. Set KLING_API_KEY to your Kling API key (or the legacy ACCESS_KEY:SECRET_KEY pair)."
5997
6136
  };
5998
6137
  }
5999
6138
  try {
6000
- const token = this.generateToken();
6139
+ const token = this.bearerToken();
6001
6140
  const body = {
6002
6141
  video_id: videoId,
6003
6142
  duration: options?.duration || "5"
@@ -6037,7 +6176,7 @@ var init_KlingProvider = __esm({
6037
6176
  };
6038
6177
  }
6039
6178
  try {
6040
- const token = this.generateToken();
6179
+ const token = this.bearerToken();
6041
6180
  const response = await fetch(`${this.baseUrl}/videos/video-extend/${id}`, {
6042
6181
  headers: {
6043
6182
  "Content-Type": "application/json",
@@ -6153,11 +6292,24 @@ var init_kling = __esm({
6153
6292
  });
6154
6293
 
6155
6294
  // ../ai-providers/dist/grok/GrokProvider.js
6156
- var DEFAULT_MODEL5, GrokProvider, grokProvider;
6295
+ function resolveGrokVideoModel(alias) {
6296
+ const model = findModel("grok", "video", alias);
6297
+ if (model)
6298
+ return model.id;
6299
+ throw new Error(`Unknown Grok video model "${alias}". Valid: ${modelAliases("grok", "video").join(", ")}.`);
6300
+ }
6301
+ function resolveGrokImageModel(alias) {
6302
+ const model = (findModel("grok", "image", alias) ?? defaultModel("grok", "image")).id;
6303
+ return model === "grok-imagine-image-2.0" ? { model, quality: "medium" } : { model };
6304
+ }
6305
+ var DEFAULT_MODEL5, DEFAULT_VIDEO_RESOLUTION, GROK_VIDEO_RESOLUTIONS, GrokProvider, grokProvider;
6157
6306
  var init_GrokProvider = __esm({
6158
6307
  "../ai-providers/dist/grok/GrokProvider.js"() {
6159
6308
  "use strict";
6160
- DEFAULT_MODEL5 = "grok-imagine-video";
6309
+ init_catalog();
6310
+ DEFAULT_MODEL5 = defaultModel("grok", "video").id;
6311
+ DEFAULT_VIDEO_RESOLUTION = "720p";
6312
+ GROK_VIDEO_RESOLUTIONS = ["480p", "720p", "1080p"];
6161
6313
  GrokProvider = class {
6162
6314
  constructor() {
6163
6315
  this.id = "grok";
@@ -6191,6 +6343,7 @@ var init_GrokProvider = __esm({
6191
6343
  try {
6192
6344
  const body = {
6193
6345
  model: options.model || "grok-imagine-image",
6346
+ ...options.quality ? { quality: options.quality } : {},
6194
6347
  prompt: prompt3,
6195
6348
  n: options.n || 1,
6196
6349
  response_format: options.responseFormat || "url"
@@ -6258,6 +6411,7 @@ var init_GrokProvider = __esm({
6258
6411
  const dataUri = `data:image/png;base64,${base64}`;
6259
6412
  const body = {
6260
6413
  model: options.model || "grok-imagine-image",
6414
+ ...options.quality ? { quality: options.quality } : {},
6261
6415
  prompt: prompt3,
6262
6416
  image: {
6263
6417
  url: dataUri,
@@ -6324,11 +6478,15 @@ var init_GrokProvider = __esm({
6324
6478
  try {
6325
6479
  const duration = Math.round(Math.min(15, Math.max(1, options?.duration || 5)));
6326
6480
  const body = {
6327
- model: DEFAULT_MODEL5,
6481
+ model: options?.model || DEFAULT_MODEL5,
6328
6482
  prompt: prompt3,
6329
6483
  duration,
6330
- aspect_ratio: options?.aspectRatio || "16:9"
6484
+ aspect_ratio: options?.aspectRatio || "16:9",
6485
+ resolution: GROK_VIDEO_RESOLUTIONS.includes(options?.resolution ?? "") ? options.resolution : DEFAULT_VIDEO_RESOLUTION
6331
6486
  };
6487
+ if (typeof options?.generateAudio === "boolean") {
6488
+ body.generate_audio = options.generateAudio;
6489
+ }
6332
6490
  if (options?.referenceImage) {
6333
6491
  body.image = { url: options.referenceImage };
6334
6492
  }
@@ -6390,13 +6548,14 @@ var init_GrokProvider = __esm({
6390
6548
  const statusMap = {
6391
6549
  pending: "pending",
6392
6550
  done: "completed",
6393
- expired: "failed"
6551
+ expired: "failed",
6552
+ failed: "failed"
6394
6553
  };
6395
6554
  return {
6396
6555
  id,
6397
6556
  status: statusMap[data2.status] || "pending",
6398
6557
  videoUrl: data2.video?.url,
6399
- error: data2.status === "expired" ? "Generation expired" : void 0
6558
+ error: data2.status === "expired" ? "Generation expired" : data2.status === "failed" ? `Generation failed: ${data2.error?.message ?? data2.error?.code ?? "unknown error"}` : void 0
6400
6559
  };
6401
6560
  } catch (error) {
6402
6561
  return {
@@ -10590,14 +10749,21 @@ var require_src = __commonJS({
10590
10749
  });
10591
10750
 
10592
10751
  // ../ai-providers/dist/fal/FalProvider.js
10752
+ function resolveSeedanceVariant(alias) {
10753
+ const model = findModel("seedance", "video", alias);
10754
+ if (model)
10755
+ return model.id;
10756
+ throw new Error(`Unknown Seedance model "${alias}". Valid: ${modelAliases("seedance", "video").join(", ")}.`);
10757
+ }
10593
10758
  function estimateSeedanceVideoCostUsd(opts) {
10594
10759
  const shortSide = SEEDANCE_SHORT_SIDE[opts.resolution ?? "720p"] ?? 720;
10595
10760
  const [a4, b4] = (opts.aspectRatio ?? "16:9").split(":").map(Number);
10596
10761
  const ratioFactor = a4 > 0 && b4 > 0 ? Math.max(a4, b4) / Math.min(a4, b4) : 16 / 9;
10597
10762
  const longSide = Math.round(shortSide * ratioFactor);
10598
10763
  const tokensPerSecond = shortSide * longSide * 24 / 1024;
10599
- let usdPerSecond = tokensPerSecond / 1e3 * SEEDANCE_USD_PER_1K_TOKENS;
10600
- if (opts.fast && shortSide <= 720)
10764
+ const rate = opts.v25 ? shortSide >= 1080 ? SEEDANCE_25_1080P_USD_PER_1K_TOKENS : SEEDANCE_25_USD_PER_1K_TOKENS : SEEDANCE_USD_PER_1K_TOKENS;
10765
+ let usdPerSecond = tokensPerSecond / 1e3 * rate;
10766
+ if (opts.fast && !opts.v25 && shortSide <= 720)
10601
10767
  usdPerSecond *= SEEDANCE_FAST_FACTOR;
10602
10768
  if (opts.hasVideoReference)
10603
10769
  usdPerSecond *= SEEDANCE_VIDEO_REF_FACTOR;
@@ -10705,34 +10871,43 @@ function normaliseResolution(value) {
10705
10871
  return "1080p";
10706
10872
  return "720p";
10707
10873
  }
10708
- function normaliseDuration(value) {
10709
- if (typeof value !== "number")
10874
+ function normaliseDuration(value, maxSec) {
10875
+ if (typeof value !== "number" || !Number.isFinite(value))
10710
10876
  return "auto";
10711
- if (!Number.isFinite(value))
10712
- return "auto";
10713
- return Math.max(4, Math.min(15, Math.round(value)));
10877
+ return String(Math.max(4, Math.min(maxSec, Math.round(value))));
10714
10878
  }
10715
- var import_client, ENDPOINT_TEXT_TO_VIDEO, ENDPOINT_IMAGE_TO_VIDEO, ENDPOINT_REFERENCE_TO_VIDEO, DEFAULT_VARIANT, VALID_RESOLUTIONS, VALID_ASPECTS, SEEDANCE_USD_PER_1K_TOKENS, SEEDANCE_FAST_FACTOR, SEEDANCE_VIDEO_REF_FACTOR, SEEDANCE_SHORT_SIDE, FalProvider, falProvider;
10879
+ var import_client, ENDPOINT_TEXT_TO_VIDEO, ENDPOINT_IMAGE_TO_VIDEO, ENDPOINT_REFERENCE_TO_VIDEO, DEFAULT_VARIANT, MAX_DURATION_SEC, VALID_RESOLUTIONS, VALID_ASPECTS, SEEDANCE_USD_PER_1K_TOKENS, SEEDANCE_25_USD_PER_1K_TOKENS, SEEDANCE_25_1080P_USD_PER_1K_TOKENS, SEEDANCE_FAST_FACTOR, SEEDANCE_VIDEO_REF_FACTOR, SEEDANCE_SHORT_SIDE, FalProvider, falProvider;
10716
10880
  var init_FalProvider = __esm({
10717
10881
  "../ai-providers/dist/fal/FalProvider.js"() {
10718
10882
  "use strict";
10719
10883
  import_client = __toESM(require_src(), 1);
10884
+ init_catalog();
10720
10885
  ENDPOINT_TEXT_TO_VIDEO = {
10721
10886
  "seedance-2.0": "bytedance/seedance-2.0/text-to-video",
10722
- "seedance-2.0-fast": "bytedance/seedance-2.0/fast/text-to-video"
10887
+ "seedance-2.0-fast": "bytedance/seedance-2.0/fast/text-to-video",
10888
+ "seedance-2.5": "bytedance/seedance-2.5/text-to-video"
10723
10889
  };
10724
10890
  ENDPOINT_IMAGE_TO_VIDEO = {
10725
10891
  "seedance-2.0": "bytedance/seedance-2.0/image-to-video",
10726
- "seedance-2.0-fast": "bytedance/seedance-2.0/fast/image-to-video"
10892
+ "seedance-2.0-fast": "bytedance/seedance-2.0/fast/image-to-video",
10893
+ "seedance-2.5": "bytedance/seedance-2.5/image-to-video"
10727
10894
  };
10728
10895
  ENDPOINT_REFERENCE_TO_VIDEO = {
10729
10896
  "seedance-2.0": "bytedance/seedance-2.0/reference-to-video",
10730
- "seedance-2.0-fast": "bytedance/seedance-2.0/fast/reference-to-video"
10897
+ "seedance-2.0-fast": "bytedance/seedance-2.0/fast/reference-to-video",
10898
+ "seedance-2.5": "bytedance/seedance-2.5/reference-to-video"
10731
10899
  };
10732
10900
  DEFAULT_VARIANT = "seedance-2.0";
10901
+ MAX_DURATION_SEC = {
10902
+ "seedance-2.0": 15,
10903
+ "seedance-2.0-fast": 15,
10904
+ "seedance-2.5": 30
10905
+ };
10733
10906
  VALID_RESOLUTIONS = ["480p", "720p", "1080p"];
10734
10907
  VALID_ASPECTS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "auto"];
10735
10908
  SEEDANCE_USD_PER_1K_TOKENS = 0.014;
10909
+ SEEDANCE_25_USD_PER_1K_TOKENS = 0.0214;
10910
+ SEEDANCE_25_1080P_USD_PER_1K_TOKENS = 0.0234;
10736
10911
  SEEDANCE_FAST_FACTOR = 0.8;
10737
10912
  SEEDANCE_VIDEO_REF_FACTOR = 0.6;
10738
10913
  SEEDANCE_SHORT_SIDE = { "480p": 480, "720p": 720, "1080p": 1080 };
@@ -10787,7 +10962,7 @@ var init_FalProvider = __esm({
10787
10962
  const endpointId = hasReferences ? ENDPOINT_REFERENCE_TO_VIDEO[variant] : isImageToVideo ? ENDPOINT_IMAGE_TO_VIDEO[variant] : ENDPOINT_TEXT_TO_VIDEO[variant];
10788
10963
  const aspect = normaliseAspect(options?.aspectRatio);
10789
10964
  const resolution = normaliseResolution(options?.resolution);
10790
- const duration = normaliseDuration(options?.duration);
10965
+ const duration = normaliseDuration(options?.duration, MAX_DURATION_SEC[variant]);
10791
10966
  const input3 = {
10792
10967
  prompt: prompt3,
10793
10968
  aspect_ratio: aspect,
@@ -10805,10 +10980,12 @@ var init_FalProvider = __esm({
10805
10980
  } else if (referenceImage) {
10806
10981
  input3.image_url = referenceImage;
10807
10982
  }
10808
- if (options?.negativePrompt)
10809
- input3.negative_prompt = options.negativePrompt;
10810
- if (typeof options?.seed === "number")
10811
- input3.seed = options.seed;
10983
+ if (variant !== "seedance-2.5") {
10984
+ if (options?.negativePrompt)
10985
+ input3.negative_prompt = options.negativePrompt;
10986
+ if (typeof options?.seed === "number")
10987
+ input3.seed = options.seed;
10988
+ }
10812
10989
  if (typeof options?.generateAudio === "boolean")
10813
10990
  input3.generate_audio = options.generateAudio;
10814
10991
  if (options?.endUserId)
@@ -11535,6 +11712,7 @@ var init_replicate = __esm({
11535
11712
  var dist_exports = {};
11536
11713
  __export(dist_exports, {
11537
11714
  ClaudeProvider: () => ClaudeProvider,
11715
+ ELEVENLABS_MUSIC_MODEL: () => ELEVENLABS_MUSIC_MODEL,
11538
11716
  ElevenLabsProvider: () => ElevenLabsProvider,
11539
11717
  FalProvider: () => FalProvider,
11540
11718
  GEMINI_AGENT_DEFAULT_TEXT_MODEL: () => GEMINI_AGENT_DEFAULT_TEXT_MODEL,
@@ -11548,6 +11726,8 @@ __export(dist_exports, {
11548
11726
  KOKORO_MODEL_ID: () => KOKORO_MODEL_ID,
11549
11727
  KlingProvider: () => KlingProvider,
11550
11728
  KokoroProvider: () => KokoroProvider,
11729
+ MODEL_CATALOG: () => MODEL_CATALOG,
11730
+ OPENAI_IMAGE_DEFAULT_MODEL: () => OPENAI_IMAGE_DEFAULT_MODEL,
11551
11731
  OPENAI_TTS_VOICES: () => OPENAI_TTS_VOICES,
11552
11732
  OllamaProvider: () => OllamaProvider,
11553
11733
  OmniProvider: () => OmniProvider,
@@ -11556,13 +11736,16 @@ __export(dist_exports, {
11556
11736
  OpenAiTtsProvider: () => OpenAiTtsProvider,
11557
11737
  ReplicateProvider: () => ReplicateProvider,
11558
11738
  RunwayProvider: () => RunwayProvider,
11739
+ VEO_MODEL_ALIASES: () => VEO_MODEL_ALIASES,
11559
11740
  WhisperProvider: () => WhisperProvider,
11560
11741
  claudeProvider: () => claudeProvider,
11742
+ defaultModel: () => defaultModel,
11561
11743
  defineApiKey: () => defineApiKey,
11562
11744
  defineProvider: () => defineProvider,
11563
11745
  elevenLabsProvider: () => elevenLabsProvider,
11564
11746
  estimateSeedanceVideoCostUsd: () => estimateSeedanceVideoCostUsd,
11565
11747
  falProvider: () => falProvider,
11748
+ findModel: () => findModel,
11566
11749
  geminiProvider: () => geminiProvider,
11567
11750
  getAllApiKeys: () => getAllApiKeys,
11568
11751
  getAllProviders: () => getAllProviders,
@@ -11576,10 +11759,13 @@ __export(dist_exports, {
11576
11759
  getSetupProviders: () => getSetupProviders,
11577
11760
  grokProvider: () => grokProvider,
11578
11761
  isGeminiTextModelAlias: () => isGeminiTextModelAlias,
11762
+ klingDuration: () => klingDuration,
11579
11763
  klingProvider: () => klingProvider,
11580
11764
  kokoroProvider: () => kokoroProvider,
11765
+ listModels: () => listModels,
11581
11766
  loadBundledKokoroRuntime: () => loadBundledKokoroRuntime,
11582
11767
  loadKokoroFromWorkspace: () => loadKokoroFromWorkspace,
11768
+ modelAliases: () => modelAliases,
11583
11769
  ollamaProvider: () => ollamaProvider,
11584
11770
  omniProvider: () => omniProvider,
11585
11771
  openaiImageProvider: () => openaiImageProvider,
@@ -11588,6 +11774,11 @@ __export(dist_exports, {
11588
11774
  providerRegistry: () => providerRegistry,
11589
11775
  replicateProvider: () => replicateProvider,
11590
11776
  resolveGeminiTextModel: () => resolveGeminiTextModel,
11777
+ resolveGrokImageModel: () => resolveGrokImageModel,
11778
+ resolveGrokVideoModel: () => resolveGrokVideoModel,
11779
+ resolveKlingModel: () => resolveKlingModel,
11780
+ resolveSeedanceVariant: () => resolveSeedanceVariant,
11781
+ resolveVeoModel: () => resolveVeoModel,
11591
11782
  resolveVoiceId: () => resolveVoiceId,
11592
11783
  runwayProvider: () => runwayProvider,
11593
11784
  whisperProvider: () => whisperProvider
@@ -11601,6 +11792,7 @@ var init_dist = __esm({
11601
11792
  init_define_provider();
11602
11793
  init_whisper();
11603
11794
  init_gemini();
11795
+ init_catalog2();
11604
11796
  init_openai();
11605
11797
  init_claude();
11606
11798
  init_ollama();
@@ -19843,12 +20035,15 @@ var init_ai_helpers = __esm({
19843
20035
  });
19844
20036
 
19845
20037
  // ../cli/src/commands/_shared/video-utils.ts
19846
- async function uploadToImgbb(imageBuffer, apiKey) {
20038
+ async function uploadToImgbb(imageBuffer, apiKey, expirationSeconds) {
19847
20039
  try {
19848
20040
  const base64Image = imageBuffer.toString("base64");
19849
20041
  const formData = new URLSearchParams();
19850
20042
  formData.append("key", apiKey);
19851
20043
  formData.append("image", base64Image);
20044
+ if (expirationSeconds !== void 0) {
20045
+ formData.append("expiration", String(expirationSeconds));
20046
+ }
19852
20047
  const response = await fetch("https://api.imgbb.com/1/upload", {
19853
20048
  method: "POST",
19854
20049
  body: formData
@@ -19911,7 +20106,10 @@ function awsEncode(value) {
19911
20106
  (c2) => `%${c2.charCodeAt(0).toString(16).toUpperCase()}`
19912
20107
  );
19913
20108
  }
19914
- function presignS3PutUrl(params) {
20109
+ function clamp(value, min, max) {
20110
+ return Math.min(max, Math.max(min, Math.round(value)));
20111
+ }
20112
+ function presignS3Url(params) {
19915
20113
  const now = /* @__PURE__ */ new Date();
19916
20114
  const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, "");
19917
20115
  const dateStamp = amzDate.slice(0, 8);
@@ -19922,7 +20120,7 @@ function presignS3PutUrl(params) {
19922
20120
  "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
19923
20121
  "X-Amz-Credential": `${params.accessKeyId}/${credentialScope}`,
19924
20122
  "X-Amz-Date": amzDate,
19925
- "X-Amz-Expires": String(params.ttlSeconds),
20123
+ "X-Amz-Expires": String(clamp(params.ttlSeconds, 1, S3_MAX_PRESIGN_SECONDS)),
19926
20124
  "X-Amz-SignedHeaders": "host"
19927
20125
  };
19928
20126
  if (params.sessionToken) {
@@ -19930,7 +20128,7 @@ function presignS3PutUrl(params) {
19930
20128
  }
19931
20129
  const canonicalQuery = Object.entries(query2).sort(([a4], [b4]) => a4.localeCompare(b4)).map(([key2, value]) => `${awsEncode(key2)}=${awsEncode(value)}`).join("&");
19932
20130
  const canonicalRequest = [
19933
- "PUT",
20131
+ params.method,
19934
20132
  canonicalUri,
19935
20133
  canonicalQuery,
19936
20134
  `host:${host}`,
@@ -19989,7 +20187,8 @@ async function resolveUploadHost() {
19989
20187
  const ext = extensionFor(opts);
19990
20188
  const prefix = safePrefix(settings.s3.prefix);
19991
20189
  const key2 = `${prefix}/${Date.now()}-${randomUUID()}.${ext}`;
19992
- const presignedUrl = presignS3PutUrl({
20190
+ const presignedUrl = presignS3Url({
20191
+ method: "PUT",
19993
20192
  accessKeyId,
19994
20193
  secretAccessKey,
19995
20194
  sessionToken,
@@ -20008,10 +20207,26 @@ async function resolveUploadHost() {
20008
20207
  if (!response.ok) {
20009
20208
  throw new Error(`S3 upload failed (${response.status}): ${response.statusText}`);
20010
20209
  }
20210
+ if (settings.s3.publicBaseUrl) {
20211
+ return {
20212
+ provider: "s3",
20213
+ url: publicS3Url({ region, bucket, key: key2, publicBaseUrl: settings.s3.publicBaseUrl })
20214
+ };
20215
+ }
20216
+ const ttlSeconds = clamp(settings.ttlSeconds, 1, S3_MAX_PRESIGN_SECONDS);
20011
20217
  return {
20012
20218
  provider: "s3",
20013
- url: publicS3Url({ region, bucket, key: key2, publicBaseUrl: settings.s3.publicBaseUrl }),
20014
- expiresAt: new Date(Date.now() + settings.ttlSeconds * 1e3).toISOString()
20219
+ url: presignS3Url({
20220
+ method: "GET",
20221
+ accessKeyId,
20222
+ secretAccessKey,
20223
+ sessionToken,
20224
+ region,
20225
+ bucket,
20226
+ key: key2,
20227
+ ttlSeconds
20228
+ }),
20229
+ expiresAt: new Date(Date.now() + ttlSeconds * 1e3).toISOString()
20015
20230
  };
20016
20231
  }
20017
20232
  };
@@ -20023,19 +20238,28 @@ async function resolveUploadHost() {
20023
20238
  if (!imgbbKey) {
20024
20239
  throw new Error("IMGBB_API_KEY required for image-to-video uploads.");
20025
20240
  }
20026
- const result = await uploadToImgbb(imageBuffer, imgbbKey);
20241
+ const expiration = clamp(settings.ttlSeconds, IMGBB_MIN_EXPIRATION_SECONDS, IMGBB_MAX_EXPIRATION_SECONDS);
20242
+ const result = await uploadToImgbb(imageBuffer, imgbbKey, expiration);
20027
20243
  if (!result.success || !result.url) {
20028
20244
  throw new Error(`ImgBB upload failed: ${result.error ?? "unknown error"}`);
20029
20245
  }
20030
- return { provider: "imgbb", url: result.url };
20246
+ return {
20247
+ provider: "imgbb",
20248
+ url: result.url,
20249
+ expiresAt: new Date(Date.now() + expiration * 1e3).toISOString()
20250
+ };
20031
20251
  }
20032
20252
  };
20033
20253
  }
20254
+ var S3_MAX_PRESIGN_SECONDS, IMGBB_MIN_EXPIRATION_SECONDS, IMGBB_MAX_EXPIRATION_SECONDS;
20034
20255
  var init_upload_host = __esm({
20035
20256
  "../cli/src/utils/upload-host.ts"() {
20036
20257
  "use strict";
20037
20258
  init_config();
20038
20259
  init_video_utils();
20260
+ S3_MAX_PRESIGN_SECONDS = 7 * 24 * 60 * 60;
20261
+ IMGBB_MIN_EXPIRATION_SECONDS = 60;
20262
+ IMGBB_MAX_EXPIRATION_SECONDS = 15552e3;
20039
20263
  }
20040
20264
  });
20041
20265
 
@@ -20653,8 +20877,14 @@ __export(ai_video_exports, {
20653
20877
  executeVideoGenerate: () => executeVideoGenerate,
20654
20878
  executeVideoStatus: () => executeVideoStatus
20655
20879
  });
20656
- import { readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
20657
- import { resolve as resolve3 } from "node:path";
20880
+ import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
20881
+ import { dirname as dirname2, resolve as resolve3 } from "node:path";
20882
+ async function saveOutput(output3, buffer) {
20883
+ const outputPath = resolve3(process.cwd(), output3);
20884
+ await mkdir2(dirname2(outputPath), { recursive: true });
20885
+ await writeFile3(outputPath, buffer);
20886
+ return outputPath;
20887
+ }
20658
20888
  async function executeVideoGenerate(options) {
20659
20889
  const {
20660
20890
  prompt: prompt3,
@@ -20670,7 +20900,10 @@ async function executeVideoGenerate(options) {
20670
20900
  negative,
20671
20901
  resolution,
20672
20902
  veoModel = "3.1-fast",
20903
+ runwayModel,
20673
20904
  seedanceModel = "quality",
20905
+ grokModel,
20906
+ klingModel,
20674
20907
  generateAudio,
20675
20908
  output: output3,
20676
20909
  wait = true,
@@ -20725,7 +20958,7 @@ async function executeVideoGenerate(options) {
20725
20958
  });
20726
20959
  falImage = upload.url;
20727
20960
  }
20728
- const model = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
20961
+ const model = resolveSeedanceVariant(seedanceModel);
20729
20962
  const result = await fal.generateVideo(prompt3, {
20730
20963
  prompt: prompt3,
20731
20964
  referenceImage: references.length > 0 ? void 0 : falImage,
@@ -20742,8 +20975,7 @@ async function executeVideoGenerate(options) {
20742
20975
  let outputPath;
20743
20976
  if (output3 && result.videoUrl) {
20744
20977
  const buffer = await downloadVideo(result.videoUrl, key2);
20745
- outputPath = resolve3(process.cwd(), output3);
20746
- await writeFile3(outputPath, buffer);
20978
+ outputPath = await saveOutput(output3, buffer);
20747
20979
  }
20748
20980
  return {
20749
20981
  success: true,
@@ -20758,6 +20990,7 @@ async function executeVideoGenerate(options) {
20758
20990
  await runway.initialize({ apiKey: key2 });
20759
20991
  const result = await runway.generateVideo(prompt3, {
20760
20992
  prompt: prompt3,
20993
+ model: runwayModel,
20761
20994
  referenceImage,
20762
20995
  duration,
20763
20996
  aspectRatio: ratio,
@@ -20774,8 +21007,7 @@ async function executeVideoGenerate(options) {
20774
21007
  let outputPath;
20775
21008
  if (output3 && finalResult.videoUrl) {
20776
21009
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
20777
- outputPath = resolve3(process.cwd(), output3);
20778
- await writeFile3(outputPath, buffer);
21010
+ outputPath = await saveOutput(output3, buffer);
20779
21011
  }
20780
21012
  return {
20781
21013
  success: true,
@@ -20801,6 +21033,7 @@ async function executeVideoGenerate(options) {
20801
21033
  }
20802
21034
  const result = await kling.generateVideo(prompt3, {
20803
21035
  prompt: prompt3,
21036
+ model: resolveKlingModel(klingModel),
20804
21037
  referenceImage: klingImage,
20805
21038
  duration,
20806
21039
  aspectRatio: ratio,
@@ -20819,8 +21052,7 @@ async function executeVideoGenerate(options) {
20819
21052
  let outputPath;
20820
21053
  if (output3 && finalResult.videoUrl) {
20821
21054
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
20822
- outputPath = resolve3(process.cwd(), output3);
20823
- await writeFile3(outputPath, buffer);
21055
+ outputPath = await saveOutput(output3, buffer);
20824
21056
  }
20825
21057
  return {
20826
21058
  success: true,
@@ -20834,12 +21066,7 @@ async function executeVideoGenerate(options) {
20834
21066
  } else if (provider === "veo") {
20835
21067
  const gemini = new GeminiProvider();
20836
21068
  await gemini.initialize({ apiKey: key2 });
20837
- const veoModelMap = {
20838
- "3.0": "veo-3.0-generate-preview",
20839
- "3.1": "veo-3.1-generate-preview",
20840
- "3.1-fast": "veo-3.1-fast-generate-preview"
20841
- };
20842
- const model = veoModelMap[veoModel] || "veo-3.1-fast-generate-preview";
21069
+ const model = resolveVeoModel(veoModel);
20843
21070
  const veoDuration = duration <= 6 ? 6 : 8;
20844
21071
  const result = await gemini.generateVideo(prompt3, {
20845
21072
  prompt: prompt3,
@@ -20860,8 +21087,7 @@ async function executeVideoGenerate(options) {
20860
21087
  let outputPath;
20861
21088
  if (output3 && finalResult.videoUrl) {
20862
21089
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
20863
- outputPath = resolve3(process.cwd(), output3);
20864
- await writeFile3(outputPath, buffer);
21090
+ outputPath = await saveOutput(output3, buffer);
20865
21091
  }
20866
21092
  return {
20867
21093
  success: true,
@@ -20876,9 +21102,12 @@ async function executeVideoGenerate(options) {
20876
21102
  await grok.initialize({ apiKey: key2 });
20877
21103
  const result = await grok.generateVideo(prompt3, {
20878
21104
  prompt: prompt3,
21105
+ model: resolveGrokVideoModel(grokModel),
20879
21106
  referenceImage,
20880
21107
  duration,
20881
- aspectRatio: ratio
21108
+ aspectRatio: ratio,
21109
+ resolution,
21110
+ generateAudio
20882
21111
  });
20883
21112
  if (result.status === "failed")
20884
21113
  return { success: false, error: result.error || "Grok generation failed" };
@@ -20891,8 +21120,7 @@ async function executeVideoGenerate(options) {
20891
21120
  let outputPath;
20892
21121
  if (output3 && finalResult.videoUrl) {
20893
21122
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
20894
- outputPath = resolve3(process.cwd(), output3);
20895
- await writeFile3(outputPath, buffer);
21123
+ outputPath = await saveOutput(output3, buffer);
20896
21124
  }
20897
21125
  return {
20898
21126
  success: true,
@@ -20909,7 +21137,8 @@ async function executeVideoGenerate(options) {
20909
21137
  const result = await omni.generateVideo(prompt3, {
20910
21138
  prompt: prompt3,
20911
21139
  referenceImage,
20912
- aspectRatio: ratio
21140
+ aspectRatio: ratio,
21141
+ resolution
20913
21142
  });
20914
21143
  if (result.status !== "completed" || !result.videoUrl) {
20915
21144
  return { success: false, error: result.error || "Gemini Omni generation failed" };
@@ -20917,8 +21146,7 @@ async function executeVideoGenerate(options) {
20917
21146
  let outputPath;
20918
21147
  if (output3) {
20919
21148
  const buffer = await downloadVideo(result.videoUrl, key2);
20920
- outputPath = resolve3(process.cwd(), output3);
20921
- await writeFile3(outputPath, buffer);
21149
+ outputPath = await saveOutput(output3, buffer);
20922
21150
  }
20923
21151
  return {
20924
21152
  success: true,
@@ -21014,8 +21242,7 @@ async function executeVideoStatus(options) {
21014
21242
  let outputPath;
21015
21243
  if (output3 && result.videoUrl) {
21016
21244
  const buffer = await downloadVideo(result.videoUrl);
21017
- outputPath = resolve3(process.cwd(), output3);
21018
- await writeFile3(outputPath, buffer);
21245
+ outputPath = await saveOutput(output3, buffer);
21019
21246
  }
21020
21247
  return {
21021
21248
  success: true,
@@ -21037,8 +21264,7 @@ async function executeVideoStatus(options) {
21037
21264
  let outputPath;
21038
21265
  if (output3 && result.videoUrl) {
21039
21266
  const buffer = await downloadVideo(result.videoUrl, key2);
21040
- outputPath = resolve3(process.cwd(), output3);
21041
- await writeFile3(outputPath, buffer);
21267
+ outputPath = await saveOutput(output3, buffer);
21042
21268
  }
21043
21269
  return {
21044
21270
  success: true,
@@ -21059,8 +21285,7 @@ async function executeVideoStatus(options) {
21059
21285
  let outputPath;
21060
21286
  if (output3 && result.videoUrl) {
21061
21287
  const buffer = await downloadVideo(result.videoUrl, key2);
21062
- outputPath = resolve3(process.cwd(), output3);
21063
- await writeFile3(outputPath, buffer);
21288
+ outputPath = await saveOutput(output3, buffer);
21064
21289
  }
21065
21290
  return {
21066
21291
  success: true,
@@ -21082,8 +21307,7 @@ async function executeVideoStatus(options) {
21082
21307
  let outputPath;
21083
21308
  if (output3 && result.videoUrl) {
21084
21309
  const buffer = await downloadVideo(result.videoUrl, key2);
21085
- outputPath = resolve3(process.cwd(), output3);
21086
- await writeFile3(outputPath, buffer);
21310
+ outputPath = await saveOutput(output3, buffer);
21087
21311
  }
21088
21312
  return {
21089
21313
  success: true,
@@ -21153,8 +21377,7 @@ async function executeVideoExtend(options) {
21153
21377
  let outputPath;
21154
21378
  if (output3 && finalResult.videoUrl) {
21155
21379
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
21156
- outputPath = resolve3(process.cwd(), output3);
21157
- await writeFile3(outputPath, buffer);
21380
+ outputPath = await saveOutput(output3, buffer);
21158
21381
  }
21159
21382
  return {
21160
21383
  success: true,
@@ -21169,12 +21392,7 @@ async function executeVideoExtend(options) {
21169
21392
  if (!key2) return { success: false, error: "GOOGLE_API_KEY required" };
21170
21393
  const gemini = new GeminiProvider();
21171
21394
  await gemini.initialize({ apiKey: key2 });
21172
- const veoModelMap = {
21173
- "3.0": "veo-3.0-generate-preview",
21174
- "3.1": "veo-3.1-generate-preview",
21175
- "3.1-fast": "veo-3.1-fast-generate-preview"
21176
- };
21177
- const model = veoModelMap[veoModel] || "veo-3.1-generate-preview";
21395
+ const model = resolveVeoModel(veoModel);
21178
21396
  const result = await gemini.extendVideo(videoId, prompt3, {
21179
21397
  duration,
21180
21398
  model
@@ -21189,8 +21407,7 @@ async function executeVideoExtend(options) {
21189
21407
  let outputPath;
21190
21408
  if (output3 && finalResult.videoUrl) {
21191
21409
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
21192
- outputPath = resolve3(process.cwd(), output3);
21193
- await writeFile3(outputPath, buffer);
21410
+ outputPath = await saveOutput(output3, buffer);
21194
21411
  }
21195
21412
  return {
21196
21413
  success: true,
@@ -21252,8 +21469,8 @@ var init_music_status = __esm({
21252
21469
 
21253
21470
  // ../cli/src/commands/_shared/build-asset-metadata.ts
21254
21471
  import { existsSync, readFileSync } from "node:fs";
21255
- import { mkdir as mkdir2, writeFile as writeFile4 } from "node:fs/promises";
21256
- import { dirname as dirname2, join } from "node:path";
21472
+ import { mkdir as mkdir3, writeFile as writeFile4 } from "node:fs/promises";
21473
+ import { dirname as dirname3, join } from "node:path";
21257
21474
  function assetMetadataPath(kind3, beatId) {
21258
21475
  return `.vibeframe/assets/${kind3}-${beatId}.json`;
21259
21476
  }
@@ -21301,7 +21518,7 @@ async function writeAssetMetadata(opts) {
21301
21518
  cachePath: opts.cachePath,
21302
21519
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
21303
21520
  };
21304
- await mkdir2(dirname2(abs), { recursive: true });
21521
+ await mkdir3(dirname3(abs), { recursive: true });
21305
21522
  await writeFile4(abs, JSON.stringify(metadata, null, 2) + "\n", "utf-8");
21306
21523
  }
21307
21524
  var init_build_asset_metadata = __esm({
@@ -22260,7 +22477,7 @@ var init_storyboard_edit = __esm({
22260
22477
 
22261
22478
  // ../cli/src/commands/_shared/storyboard-source.ts
22262
22479
  import { existsSync as existsSync2 } from "node:fs";
22263
- import { mkdir as mkdir3, readdir, readFile as readFile4, rename, rm, writeFile as writeFile6 } from "node:fs/promises";
22480
+ import { mkdir as mkdir4, readdir, readFile as readFile4, rename, rm, writeFile as writeFile6 } from "node:fs/promises";
22264
22481
  import { basename, join as join3 } from "node:path";
22265
22482
  function sceneFileFrom(dir, filename) {
22266
22483
  const stem = filename.replace(/\.mdx?$/i, "");
@@ -22373,7 +22590,7 @@ ${(0, import_yaml5.stringify)(data2, { lineWidth: 0 }).trimEnd()}
22373
22590
  };
22374
22591
  }
22375
22592
  async function writeMigration(projectDir, plan2) {
22376
- await mkdir3(join3(projectDir, SCENES_DIRNAME), { recursive: true });
22593
+ await mkdir4(join3(projectDir, SCENES_DIRNAME), { recursive: true });
22377
22594
  for (const file of plan2.scenes) {
22378
22595
  await writeFile6(join3(projectDir, file.path), file.contents, "utf-8");
22379
22596
  }
@@ -22395,7 +22612,7 @@ function strayBeatIssues(strayBeatIds) {
22395
22612
  async function upsertScene(projectDir, opts) {
22396
22613
  const beatId = deriveBeatId(opts.beatId);
22397
22614
  const dir = join3(projectDir, SCENES_DIRNAME);
22398
- await mkdir3(dir, { recursive: true });
22615
+ await mkdir4(dir, { recursive: true });
22399
22616
  const cues = {};
22400
22617
  if (opts.duration !== void 0) cues.duration = opts.duration;
22401
22618
  if (opts.narration?.trim()) cues.narration = opts.narration.trim();
@@ -22467,22 +22684,22 @@ var init_storyboard_source = __esm({
22467
22684
  // ../cli/src/commands/_shared/status-jobs.ts
22468
22685
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
22469
22686
  import { existsSync as existsSync3, statSync } from "node:fs";
22470
- import { copyFile, mkdir as mkdir4, readFile as readFile5, readdir as readdir2, rename as rename2, stat, writeFile as writeFile7 } from "node:fs/promises";
22471
- import { dirname as dirname3, join as join4, parse as parse2, relative, resolve as resolve5 } from "node:path";
22687
+ import { copyFile, mkdir as mkdir5, readFile as readFile5, readdir as readdir2, rename as rename2, stat, writeFile as writeFile7 } from "node:fs/promises";
22688
+ import { dirname as dirname4, join as join4, parse as parse2, relative, resolve as resolve5 } from "node:path";
22472
22689
  function isLocalJob(record) {
22473
22690
  return record.jobType === "build" || record.jobType === "render";
22474
22691
  }
22475
22692
  function findProjectRoot(start = process.cwd()) {
22476
22693
  let dir = resolve5(start);
22477
22694
  try {
22478
- if (existsSync3(dir) && statSync(dir).isFile()) dir = dirname3(dir);
22695
+ if (existsSync3(dir) && statSync(dir).isFile()) dir = dirname4(dir);
22479
22696
  } catch {
22480
22697
  }
22481
22698
  for (; ; ) {
22482
22699
  if (existsSync3(join4(dir, "vibe.config.json")) || existsSync3(join4(dir, "STORYBOARD.md"))) {
22483
22700
  return dir;
22484
22701
  }
22485
- const parent = dirname3(dir);
22702
+ const parent = dirname4(dir);
22486
22703
  if (parent === dir) return resolve5(start);
22487
22704
  dir = parent;
22488
22705
  }
@@ -22549,7 +22766,7 @@ async function createAndWriteJobRecord(opts) {
22549
22766
  }
22550
22767
  async function writeJobRecord(record) {
22551
22768
  const dir = jobsDir(record.projectDir);
22552
- await mkdir4(dir, { recursive: true });
22769
+ await mkdir5(dir, { recursive: true });
22553
22770
  const path16 = jobRecordPath(record.id, record.projectDir);
22554
22771
  const tmp = `${path16}.${randomUUID2()}.tmp`;
22555
22772
  await writeFile7(tmp, JSON.stringify(stripUndefined2(record), null, 2) + "\n", "utf-8");
@@ -22597,7 +22814,7 @@ async function refreshJobRecord(record, opts = {}) {
22597
22814
  try {
22598
22815
  if (record.jobType === "generate-video") {
22599
22816
  const output3 = resolveRefreshOutput(record, opts);
22600
- if (output3) await mkdir4(dirname3(output3), { recursive: true });
22817
+ if (output3) await mkdir5(dirname4(output3), { recursive: true });
22601
22818
  const result = await executeVideoStatus({
22602
22819
  taskId: record.providerTaskId,
22603
22820
  provider: record.provider,
@@ -22880,14 +23097,14 @@ async function downloadUrl(url, outputPath) {
22880
23097
  const response = await fetch(url);
22881
23098
  if (!response.ok) throw new Error(`Download failed: HTTP ${response.status}`);
22882
23099
  const buffer = Buffer.from(await response.arrayBuffer());
22883
- await mkdir4(dirname3(outputPath), { recursive: true });
23100
+ await mkdir5(dirname4(outputPath), { recursive: true });
22884
23101
  await writeFile7(outputPath, buffer);
22885
23102
  return outputPath;
22886
23103
  }
22887
23104
  async function maybeCacheOutput(record) {
22888
23105
  if (record.status !== "completed" || !record.outputPath || !record.cachePath) return;
22889
23106
  try {
22890
- await mkdir4(dirname3(record.cachePath), { recursive: true });
23107
+ await mkdir5(dirname4(record.cachePath), { recursive: true });
22891
23108
  await copyFile(record.outputPath, record.cachePath);
22892
23109
  } catch {
22893
23110
  }
@@ -23471,7 +23688,7 @@ var init_project_config = __esm({
23471
23688
  });
23472
23689
 
23473
23690
  // ../cli/src/commands/_shared/scene-project.ts
23474
- import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile8, access as access3 } from "node:fs/promises";
23691
+ import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile8, access as access3 } from "node:fs/promises";
23475
23692
  import { resolve as resolve7, basename as basename2 } from "node:path";
23476
23693
  function isSceneKind(value) {
23477
23694
  return VALID_SCENE_KINDS.includes(value);
@@ -24083,11 +24300,11 @@ async function scaffoldSceneProject(opts) {
24083
24300
  const now = opts.now ?? /* @__PURE__ */ new Date();
24084
24301
  const profile = opts.profile ?? "full";
24085
24302
  const kind3 = opts.kind ?? DEFAULT_SCENE_KIND;
24086
- await mkdir5(dir, { recursive: true });
24303
+ await mkdir6(dir, { recursive: true });
24087
24304
  if (profile === "full") {
24088
- await mkdir5(resolve7(dir, "compositions"), { recursive: true });
24089
- await mkdir5(resolve7(dir, "assets"), { recursive: true });
24090
- await mkdir5(resolve7(dir, "renders"), { recursive: true });
24305
+ await mkdir6(resolve7(dir, "compositions"), { recursive: true });
24306
+ await mkdir6(resolve7(dir, "assets"), { recursive: true });
24307
+ await mkdir6(resolve7(dir, "renders"), { recursive: true });
24091
24308
  }
24092
24309
  const created = [];
24093
24310
  const skipped2 = [];
@@ -87788,7 +88005,7 @@ var require_yauzl = __commonJS({
87788
88005
  // ../../node_modules/.pnpm/@puppeteer+browsers@3.0.6_yauzl@2.10.0/node_modules/@puppeteer/browsers/lib/fileUtil.js
87789
88006
  import { spawnSync, spawn, execFile as execFile2 } from "node:child_process";
87790
88007
  import { constants as constants2, createReadStream, createWriteStream as createWriteStream2 } from "node:fs";
87791
- import { mkdir as mkdir7, readdir as readdir4, symlink as symlink2 } from "node:fs/promises";
88008
+ import { mkdir as mkdir8, readdir as readdir4, symlink as symlink2 } from "node:fs/promises";
87792
88009
  import * as path9 from "node:path";
87793
88010
  import { Stream, Writable as Writable2 } from "node:stream";
87794
88011
  import { pipeline } from "node:stream/promises";
@@ -87798,12 +88015,12 @@ async function unpackArchive(archivePath, folderPath) {
87798
88015
  folderPath = path9.resolve(process.cwd(), folderPath);
87799
88016
  }
87800
88017
  if (archivePath.endsWith(".zip")) {
87801
- await mkdir7(folderPath, { recursive: true });
88018
+ await mkdir8(folderPath, { recursive: true });
87802
88019
  await extractZip(archivePath, folderPath);
87803
88020
  } else if (archivePath.endsWith(".tar.bz2")) {
87804
88021
  await extractTar(archivePath, folderPath, "bzip2");
87805
88022
  } else if (archivePath.endsWith(".dmg")) {
87806
- await mkdir7(folderPath);
88023
+ await mkdir8(folderPath);
87807
88024
  await installDMG(archivePath, folderPath);
87808
88025
  } else if (archivePath.endsWith(".exe")) {
87809
88026
  const result = spawnSync(archivePath, [`/ExtractDir=${folderPath}`], {
@@ -87991,10 +88208,10 @@ async function extractZipEntry(zipFile, entry, folderPath) {
87991
88208
  const mode = unixMode === 0 ? isDirectory2 ? 493 : 420 : unixMode & 511;
87992
88209
  const destination = path9.join(folderPath, entry.fileName);
87993
88210
  if (isDirectory2) {
87994
- await mkdir7(destination, { recursive: true, mode });
88211
+ await mkdir8(destination, { recursive: true, mode });
87995
88212
  return;
87996
88213
  }
87997
- await mkdir7(path9.dirname(destination), { recursive: true });
88214
+ await mkdir8(path9.dirname(destination), { recursive: true });
87998
88215
  const readStream = await promisify2(zipFile.openReadStream.bind(zipFile))(entry);
87999
88216
  if (isSymlink) {
88000
88217
  const chunks = [];
@@ -88162,7 +88379,7 @@ var init_ProgressBar = __esm({
88162
88379
  import assert3 from "node:assert";
88163
88380
  import { spawnSync as spawnSync2 } from "node:child_process";
88164
88381
  import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
88165
- import { mkdir as mkdir8, unlink } from "node:fs/promises";
88382
+ import { mkdir as mkdir9, unlink } from "node:fs/promises";
88166
88383
  import os6 from "node:os";
88167
88384
  import path10 from "node:path";
88168
88385
  function debugTime(label) {
@@ -88215,7 +88432,7 @@ async function installWithProviders(options) {
88215
88432
  }
88216
88433
  debugInstall?.(`Successfully got URL from ${provider.getName()}: ${url}`);
88217
88434
  if (!existsSync5(browserRoot)) {
88218
- await mkdir8(browserRoot, { recursive: true });
88435
+ await mkdir9(browserRoot, { recursive: true });
88219
88436
  }
88220
88437
  return await installUrl(url, options, provider);
88221
88438
  } catch (err) {
@@ -88288,7 +88505,7 @@ async function installUrl(url, options, provider) {
88288
88505
  const browserRoot = cache.browserRoot(options.browser);
88289
88506
  const archivePath = path10.join(browserRoot, `${options.buildId}-${fileName}`);
88290
88507
  if (!existsSync5(browserRoot)) {
88291
- await mkdir8(browserRoot, { recursive: true });
88508
+ await mkdir9(browserRoot, { recursive: true });
88292
88509
  }
88293
88510
  if (options.expectedHash) {
88294
88511
  debugInstall?.(`Using provided checksum for ${fileName}: ${options.expectedHash}`);
@@ -89079,18 +89296,18 @@ var init_cliui = __esm({
89079
89296
  });
89080
89297
 
89081
89298
  // ../../node_modules/.pnpm/escalade@3.2.0/node_modules/escalade/sync/index.mjs
89082
- import { dirname as dirname6, resolve as resolve10 } from "path";
89299
+ import { dirname as dirname7, resolve as resolve10 } from "path";
89083
89300
  import { readdirSync, statSync as statSync2 } from "fs";
89084
89301
  function sync_default(start, callback) {
89085
89302
  let dir = resolve10(".", start);
89086
89303
  let tmp, stats = statSync2(dir);
89087
89304
  if (!stats.isDirectory()) {
89088
- dir = dirname6(dir);
89305
+ dir = dirname7(dir);
89089
89306
  }
89090
89307
  while (true) {
89091
89308
  tmp = callback(dir, readdirSync(dir));
89092
89309
  if (tmp) return resolve10(dir, tmp);
89093
- dir = dirname6(tmp = dir);
89310
+ dir = dirname7(tmp = dir);
89094
89311
  if (tmp === dir) break;
89095
89312
  }
89096
89313
  }
@@ -90359,7 +90576,7 @@ var require_get_caller_file = __commonJS({
90359
90576
  import { notStrictEqual, strictEqual } from "assert";
90360
90577
  import { inspect } from "util";
90361
90578
  import { fileURLToPath } from "url";
90362
- import { basename as basename4, dirname as dirname7, extname as extname2, relative as relative3, resolve as resolve13, join as join7 } from "path";
90579
+ import { basename as basename4, dirname as dirname8, extname as extname2, relative as relative3, resolve as resolve13, join as join7 } from "path";
90363
90580
  import { createRequire as createRequire2 } from "node:module";
90364
90581
  import { readFileSync as readFileSync5, readdirSync as readdirSync2 } from "node:fs";
90365
90582
  var import_get_caller_file, __dirname2, mainFilename, require3, esm_default;
@@ -90392,7 +90609,7 @@ var init_esm2 = __esm({
90392
90609
  Parser: lib_default,
90393
90610
  path: {
90394
90611
  basename: basename4,
90395
- dirname: dirname7,
90612
+ dirname: dirname8,
90396
90613
  extname: extname2,
90397
90614
  relative: relative3,
90398
90615
  resolve: resolve13,
@@ -95599,7 +95816,7 @@ var init_PuppeteerNode = __esm({
95599
95816
  import { spawn as spawn3, spawnSync as spawnSync3 } from "node:child_process";
95600
95817
  import fs8 from "node:fs";
95601
95818
  import os10 from "node:os";
95602
- import { dirname as dirname8 } from "node:path";
95819
+ import { dirname as dirname9 } from "node:path";
95603
95820
  import { PassThrough } from "node:stream";
95604
95821
  function countFrames(startTimestamp, previousTimestamp, timestamp, fps) {
95605
95822
  const end = Math.round((timestamp - startTimestamp) * fps);
@@ -95723,7 +95940,7 @@ var init_ScreenRecorder = __esm({
95723
95940
  filters2.push(formatArgs.splice(vf, 2).at(-1) ?? "");
95724
95941
  }
95725
95942
  if (path16) {
95726
- fs8.mkdirSync(dirname8(path16), { recursive: overwrite });
95943
+ fs8.mkdirSync(dirname9(path16), { recursive: overwrite });
95727
95944
  }
95728
95945
  this.#process = spawn3(
95729
95946
  ffmpegPath,
@@ -96910,7 +97127,7 @@ import {
96910
97127
  symlinkSync,
96911
97128
  writeFileSync as writeFileSync6
96912
97129
  } from "node:fs";
96913
- import { basename as basename42, dirname as dirname11, isAbsolute as isAbsolute42, join as join21, relative as relative22, resolve as resolve122 } from "node:path";
97130
+ import { basename as basename42, dirname as dirname112, isAbsolute as isAbsolute42, join as join21, relative as relative22, resolve as resolve122 } from "node:path";
96914
97131
  import {
96915
97132
  basename as basename32,
96916
97133
  join as join20,
@@ -124195,7 +124412,7 @@ function writeCompiledArtifacts(compiled, workDir, includeSummary) {
124195
124412
  writeFileSync6(join21(compileDir, "index.html"), compiled.html, "utf-8");
124196
124413
  for (const [srcPath, html] of compiled.subCompositions) {
124197
124414
  const outPath = join21(compileDir, srcPath);
124198
- mkdirSync9(dirname11(outPath), { recursive: true });
124415
+ mkdirSync9(dirname112(outPath), { recursive: true });
124199
124416
  writeFileSync6(outPath, html, "utf-8");
124200
124417
  }
124201
124418
  for (const [relativePath, absolutePath] of compiled.externalAssets) {
@@ -124204,7 +124421,7 @@ function writeCompiledArtifacts(compiled, workDir, includeSummary) {
124204
124421
  console.warn(`[Render] Skipping external asset with unsafe path: ${relativePath}`);
124205
124422
  continue;
124206
124423
  }
124207
- mkdirSync9(dirname11(outPath), { recursive: true });
124424
+ mkdirSync9(dirname112(outPath), { recursive: true });
124208
124425
  copyFileSync3(absolutePath, outPath);
124209
124426
  }
124210
124427
  if (includeSummary) {
@@ -267045,7 +267262,7 @@ ${allInlineStyles}`.replace(/\/\*[\s\S]*?\*\//g, "");
267045
267262
  materializePathModule = {
267046
267263
  resolve: resolve122,
267047
267264
  join: join21,
267048
- dirname: dirname11,
267265
+ dirname: dirname112,
267049
267266
  basename: basename42,
267050
267267
  relative: relative22,
267051
267268
  isAbsolute: isAbsolute42
@@ -269905,7 +270122,7 @@ __export(scene_render_exports, {
269905
270122
  });
269906
270123
  import { execFile as execFile4 } from "node:child_process";
269907
270124
  import { existsSync as existsSync49 } from "node:fs";
269908
- import { mkdir as mkdir12, readFile as readFile17, stat as stat5, writeFile as writeFile16 } from "node:fs/promises";
270125
+ import { mkdir as mkdir13, readFile as readFile17, stat as stat5, writeFile as writeFile16 } from "node:fs/promises";
269909
270126
  import { resolve as resolve33, relative as relative9, dirname as dirname26, basename as basename8, join as join61, isAbsolute as isAbsolute8 } from "node:path";
269910
270127
  import { promisify as promisify4 } from "node:util";
269911
270128
  function qualityToCrf(quality = "standard") {
@@ -270051,7 +270268,7 @@ async function executeSceneRender(opts = {}) {
270051
270268
  }
270052
270269
  const projectName = projectConfig.config.name;
270053
270270
  const outputPath = opts.output ? resolve33(projectDir, opts.output) : defaultOutputPath({ projectDir, projectName, format: opts.format, beatId: opts.beatId });
270054
- await mkdir12(dirname26(outputPath), { recursive: true });
270271
+ await mkdir13(dirname26(outputPath), { recursive: true });
270055
270272
  const config4 = buildRenderConfig({
270056
270273
  fps: opts.fps,
270057
270274
  quality: opts.quality,
@@ -270247,7 +270464,7 @@ async function prepareBeatRenderRoot(opts) {
270247
270464
  durationSec,
270248
270465
  narrationPath
270249
270466
  });
270250
- await mkdir12(dirname26(join61(opts.projectDir, root2)), { recursive: true });
270467
+ await mkdir13(dirname26(join61(opts.projectDir, root2)), { recursive: true });
270251
270468
  await writeFile16(join61(opts.projectDir, root2), html, "utf-8");
270252
270469
  return {
270253
270470
  success: true,
@@ -270532,7 +270749,7 @@ var init_design_parse = __esm({
270532
270749
 
270533
270750
  // ../cli/src/commands/_shared/compose-aivideo.ts
270534
270751
  import { existsSync as existsSync50 } from "node:fs";
270535
- import { mkdir as mkdir13, readFile as readFile18, writeFile as writeFile17 } from "node:fs/promises";
270752
+ import { mkdir as mkdir14, readFile as readFile18, writeFile as writeFile17 } from "node:fs/promises";
270536
270753
  import { join as join63, resolve as resolve34 } from "node:path";
270537
270754
  function headingDescriptor(heading) {
270538
270755
  const stripped = heading.replace(/^\s*beat\s+/i, "");
@@ -270695,7 +270912,7 @@ async function composeAiVideo(opts) {
270695
270912
  }
270696
270913
  clipRels.push(rel);
270697
270914
  }
270698
- await mkdir13(join63(projectDir, "assets"), { recursive: true });
270915
+ await mkdir14(join63(projectDir, "assets"), { recursive: true });
270699
270916
  const bgRel = "assets/bg-full.mp4";
270700
270917
  const concatArgs = buildBgConcatArgs(
270701
270918
  clipRels.map((r2) => join63(projectDir, r2)),
@@ -270703,7 +270920,7 @@ async function composeAiVideo(opts) {
270703
270920
  );
270704
270921
  await execSafe("ffmpeg", concatArgs, { timeout: 6e5 });
270705
270922
  const totalDurationSec = await probeDurationSec(join63(projectDir, bgRel));
270706
- await mkdir13(join63(projectDir, "compositions"), { recursive: true });
270923
+ await mkdir14(join63(projectDir, "compositions"), { recursive: true });
270707
270924
  const sceneFiles = [];
270708
270925
  for (let i3 = 0; i3 < beats.length; i3++) {
270709
270926
  const beat = beats[i3];
@@ -270852,7 +271069,7 @@ var init_compose_aivideo = __esm({
270852
271069
 
270853
271070
  // ../cli/src/commands/_shared/footage-assemble.ts
270854
271071
  import { existsSync as existsSync51 } from "node:fs";
270855
- import { mkdir as mkdir14, writeFile as writeFile18 } from "node:fs/promises";
271072
+ import { mkdir as mkdir15, writeFile as writeFile18 } from "node:fs/promises";
270856
271073
  import { dirname as dirname27, isAbsolute as isAbsolute9, join as join64, resolve as resolve35 } from "node:path";
270857
271074
  function planFootageTimeline(inputs, opts = {}) {
270858
271075
  if (inputs.length === 0) {
@@ -271182,7 +271399,7 @@ async function executeFootageAssemble(opts) {
271182
271399
  if (opts.dryRun) {
271183
271400
  return { success: true, dryRun: true, outputPath, report };
271184
271401
  }
271185
- await mkdir14(dirname27(outputPath), { recursive: true });
271402
+ await mkdir15(dirname27(outputPath), { recursive: true });
271186
271403
  const args = buildFootageAssembleArgs({
271187
271404
  sources: sourceRels.map((s2) => ({ path: join64(projectDir, s2.rel), kind: s2.kind })),
271188
271405
  plan: plan2,
@@ -271543,6 +271760,37 @@ var init_composer_resolve = __esm({
271543
271760
  }
271544
271761
  });
271545
271762
 
271763
+ // ../cli/src/commands/_shared/build-video-providers.ts
271764
+ function isBuildVideoProvider(value) {
271765
+ return BUILD_VIDEO_PROVIDERS.includes(value);
271766
+ }
271767
+ function resolveBuildVideoProvider(value) {
271768
+ const provider = String(value ?? "seedance").toLowerCase();
271769
+ if (provider === "fal") return "seedance";
271770
+ return isBuildVideoProvider(provider) ? provider : "seedance";
271771
+ }
271772
+ function videoProviderConfigKey(provider) {
271773
+ switch (provider) {
271774
+ case "seedance":
271775
+ case "fal":
271776
+ return "fal";
271777
+ case "grok":
271778
+ return "xai";
271779
+ case "veo":
271780
+ case "omni":
271781
+ return "google";
271782
+ default:
271783
+ return provider;
271784
+ }
271785
+ }
271786
+ var BUILD_VIDEO_PROVIDERS;
271787
+ var init_build_video_providers = __esm({
271788
+ "../cli/src/commands/_shared/build-video-providers.ts"() {
271789
+ "use strict";
271790
+ BUILD_VIDEO_PROVIDERS = ["seedance", "grok", "kling", "runway", "omni", "veo"];
271791
+ }
271792
+ });
271793
+
271546
271794
  // ../cli/src/commands/_shared/build-plan.ts
271547
271795
  import { existsSync as existsSync54 } from "node:fs";
271548
271796
  import { dirname as dirname28, join as join65, resolve as resolve37 } from "node:path";
@@ -272217,12 +272465,7 @@ function normalizeImageProvider(value) {
272217
272465
  return provider || "openai";
272218
272466
  }
272219
272467
  function normalizeVideoProvider(value) {
272220
- const provider = String(value ?? "seedance").toLowerCase();
272221
- if (provider === "fal") return "seedance";
272222
- if (provider === "seedance" || provider === "grok" || provider === "kling" || provider === "runway" || provider === "veo") {
272223
- return provider;
272224
- }
272225
- return "seedance";
272468
+ return resolveBuildVideoProvider(value);
272226
272469
  }
272227
272470
  function normalizeMusicProvider(value) {
272228
272471
  const provider = String(value ?? "elevenlabs").toLowerCase();
@@ -272234,7 +272477,7 @@ function imageConfigKey(provider) {
272234
272477
  return "openai";
272235
272478
  }
272236
272479
  function videoConfigKey(provider) {
272237
- return provider === "seedance" ? "fal" : provider === "grok" ? "xai" : provider === "veo" ? "google" : provider;
272480
+ return videoProviderConfigKey(provider);
272238
272481
  }
272239
272482
  function composerConfigKey(provider) {
272240
272483
  return provider === "claude" ? "anthropic" : provider === "gemini" ? "google" : "openai";
@@ -272278,6 +272521,7 @@ var init_build_plan = __esm({
272278
272521
  init_project_config();
272279
272522
  init_scene_project();
272280
272523
  init_storyboard_edit();
272524
+ init_build_video_providers();
272281
272525
  VIDEO_COST_USD = 5;
272282
272526
  MUSIC_COST_USD = 0.5;
272283
272527
  ELEVENLABS_NARRATION_COST_USD = 0.05;
@@ -272456,7 +272700,7 @@ function registerMusicCommand(parent) {
272456
272700
  console.log();
272457
272701
  console.log(`Saved to: ${source_default.bold(outputPath)}`);
272458
272702
  console.log(`Duration: ${duration}s`);
272459
- console.log(`Provider: ElevenLabs (music_v1)`);
272703
+ console.log(`Provider: ElevenLabs (${ELEVENLABS_MUSIC_MODEL})`);
272460
272704
  if (options.instrumental) console.log(`Mode: Instrumental`);
272461
272705
  console.log();
272462
272706
  } else {
@@ -272596,6 +272840,48 @@ var init_scene_build_mode = __esm({
272596
272840
  }
272597
272841
  });
272598
272842
 
272843
+ // ../cli/src/utils/image-file.ts
272844
+ import { rm as rm6, writeFile as writeFile20 } from "node:fs/promises";
272845
+ import { extname as extname12 } from "node:path";
272846
+ function detectImageFormat(buffer) {
272847
+ if (buffer.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
272848
+ return "png";
272849
+ }
272850
+ if (buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255) return "jpeg";
272851
+ if (buffer.toString("ascii", 0, 4) === "RIFF" && buffer.toString("ascii", 8, 12) === "WEBP") {
272852
+ return "webp";
272853
+ }
272854
+ return void 0;
272855
+ }
272856
+ async function writeImageFile(path16, buffer) {
272857
+ const wanted = EXTENSION_FORMATS[extname12(path16).toLowerCase()];
272858
+ const actual = detectImageFormat(buffer);
272859
+ if (!wanted || !actual || wanted === actual) {
272860
+ await writeFile20(path16, buffer);
272861
+ return;
272862
+ }
272863
+ const source3 = `${path16}.source.${actual === "jpeg" ? "jpg" : actual}`;
272864
+ await writeFile20(source3, buffer);
272865
+ try {
272866
+ await execSafe("ffmpeg", ["-y", "-loglevel", "error", "-i", source3, "-frames:v", "1", path16]);
272867
+ } finally {
272868
+ await rm6(source3, { force: true });
272869
+ }
272870
+ }
272871
+ var EXTENSION_FORMATS;
272872
+ var init_image_file = __esm({
272873
+ "../cli/src/utils/image-file.ts"() {
272874
+ "use strict";
272875
+ init_exec_safe();
272876
+ EXTENSION_FORMATS = {
272877
+ ".png": "png",
272878
+ ".jpg": "jpeg",
272879
+ ".jpeg": "jpeg",
272880
+ ".webp": "webp"
272881
+ };
272882
+ }
272883
+ });
272884
+
272599
272885
  // ../cli/src/commands/_shared/scene-build.ts
272600
272886
  var scene_build_exports = {};
272601
272887
  __export(scene_build_exports, {
@@ -272604,7 +272890,7 @@ __export(scene_build_exports, {
272604
272890
  resolveSceneBuildMode: () => resolveSceneBuildMode
272605
272891
  });
272606
272892
  import { existsSync as existsSync56, readFileSync as readFileSync21 } from "node:fs";
272607
- import { copyFile as copyFile4, mkdir as mkdir15, readFile as readFile19, rm as rm6, writeFile as writeFile20 } from "node:fs/promises";
272893
+ import { copyFile as copyFile4, mkdir as mkdir16, readFile as readFile19, rm as rm7, writeFile as writeFile21 } from "node:fs/promises";
272608
272894
  import { dirname as dirname29, join as join66, resolve as resolve40 } from "node:path";
272609
272895
  function readProjectKindSync(projectDir) {
272610
272896
  try {
@@ -273458,7 +273744,7 @@ async function dispatchNarration(beat, ctx) {
273458
273744
  };
273459
273745
  }
273460
273746
  if (existsSync56(cacheAbs) && !ctx.force) {
273461
- await mkdir15(dirname29(abs), { recursive: true });
273747
+ await mkdir16(dirname29(abs), { recursive: true });
273462
273748
  await copyFile4(cacheAbs, abs);
273463
273749
  await writeAssetMetadata({
273464
273750
  projectDir: ctx.projectDir,
@@ -273489,14 +273775,14 @@ async function dispatchNarration(beat, ctx) {
273489
273775
  ctx.onProgress({ type: "narration-failed", beatId: beat.id, error });
273490
273776
  return { status: "failed", error };
273491
273777
  }
273492
- await mkdir15(dirname29(abs), { recursive: true });
273493
- await writeFile20(abs, result.audioBuffer);
273778
+ await mkdir16(dirname29(abs), { recursive: true });
273779
+ await writeFile21(abs, result.audioBuffer);
273494
273780
  for (const otherExt of ["mp3", "wav"]) {
273495
273781
  const sibling = join66(ctx.projectDir, `assets/narration-${beat.id}.${otherExt}`);
273496
- if (sibling !== abs && existsSync56(sibling)) await rm6(sibling, { force: true });
273782
+ if (sibling !== abs && existsSync56(sibling)) await rm7(sibling, { force: true });
273497
273783
  }
273498
- await mkdir15(dirname29(cacheAbs), { recursive: true });
273499
- await writeFile20(cacheAbs, result.audioBuffer);
273784
+ await mkdir16(dirname29(cacheAbs), { recursive: true });
273785
+ await writeFile21(cacheAbs, result.audioBuffer);
273500
273786
  await writeAssetMetadata({
273501
273787
  projectDir: ctx.projectDir,
273502
273788
  kind: "narration",
@@ -273552,8 +273838,8 @@ async function dispatchTranscript(beat, narrationPath, narrationFresh, ctx) {
273552
273838
  return "skipped";
273553
273839
  }
273554
273840
  try {
273555
- await mkdir15(dirname29(abs), { recursive: true });
273556
- await writeFile20(abs, JSON.stringify(words, null, 2), "utf-8");
273841
+ await mkdir16(dirname29(abs), { recursive: true });
273842
+ await writeFile21(abs, JSON.stringify(words, null, 2), "utf-8");
273557
273843
  } catch (err) {
273558
273844
  ctx.onProgress({
273559
273845
  type: "transcript-skipped",
@@ -273641,7 +273927,7 @@ async function buildCharacters(characters, ctx) {
273641
273927
  }
273642
273928
  const cacheAbs = join66(ctx.projectDir, cache.path);
273643
273929
  if (existsSync56(cacheAbs) && !ctx.force) {
273644
- await mkdir15(dirname29(abs), { recursive: true });
273930
+ await mkdir16(dirname29(abs), { recursive: true });
273645
273931
  await copyFile4(cacheAbs, abs);
273646
273932
  await writeAssetMetadata({
273647
273933
  projectDir: ctx.projectDir,
@@ -273674,10 +273960,10 @@ async function buildCharacters(characters, ctx) {
273674
273960
  ctx.onProgress({ type: "character-failed", name: character.name, error: generated.error });
273675
273961
  continue;
273676
273962
  }
273677
- await mkdir15(dirname29(abs), { recursive: true });
273678
- await writeFile20(abs, generated.buffer);
273679
- await mkdir15(dirname29(cacheAbs), { recursive: true });
273680
- await writeFile20(cacheAbs, generated.buffer);
273963
+ await mkdir16(dirname29(abs), { recursive: true });
273964
+ await writeImageFile(abs, generated.buffer);
273965
+ await mkdir16(dirname29(cacheAbs), { recursive: true });
273966
+ await writeImageFile(cacheAbs, generated.buffer);
273681
273967
  await writeAssetMetadata({
273682
273968
  projectDir: ctx.projectDir,
273683
273969
  kind: "character",
@@ -273746,7 +274032,7 @@ async function dispatchBackdrop(beat, ctx) {
273746
274032
  }
273747
274033
  const cacheAbs = join66(ctx.projectDir, cache.path);
273748
274034
  if (existsSync56(cacheAbs) && !ctx.force) {
273749
- await mkdir15(dirname29(abs), { recursive: true });
274035
+ await mkdir16(dirname29(abs), { recursive: true });
273750
274036
  await copyFile4(cacheAbs, abs);
273751
274037
  await writeAssetMetadata({
273752
274038
  projectDir: ctx.projectDir,
@@ -273776,11 +274062,11 @@ async function dispatchBackdrop(beat, ctx) {
273776
274062
  ctx.onProgress({ type: "backdrop-failed", beatId: beat.id, error });
273777
274063
  return { status: "failed", error };
273778
274064
  }
273779
- await mkdir15(dirname29(abs), { recursive: true });
274065
+ await mkdir16(dirname29(abs), { recursive: true });
273780
274066
  const buffer = generated.buffer;
273781
- await writeFile20(abs, buffer);
273782
- await mkdir15(dirname29(cacheAbs), { recursive: true });
273783
- await writeFile20(cacheAbs, buffer);
274067
+ await writeImageFile(abs, buffer);
274068
+ await mkdir16(dirname29(cacheAbs), { recursive: true });
274069
+ await writeImageFile(cacheAbs, buffer);
273784
274070
  await writeAssetMetadata({
273785
274071
  projectDir: ctx.projectDir,
273786
274072
  kind: "backdrop",
@@ -273824,7 +274110,7 @@ async function generateBackdropImage(prompt3, ctx, ratio) {
273824
274110
  const result2 = await provider2.generateImage(prompt3, {
273825
274111
  // CLI input is free-form; an alias the provider doesn't know fails the
273826
274112
  // API call and surfaces as a beat-scoped backdrop/keyframe error.
273827
- model: ctx.imageModel ?? "gpt-image-2",
274113
+ model: ctx.imageModel ?? OPENAI_IMAGE_DEFAULT_MODEL,
273828
274114
  size: ctx.imageSize,
273829
274115
  quality: ctx.imageQuality
273830
274116
  });
@@ -273909,7 +274195,7 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
273909
274195
  }
273910
274196
  const cacheAbs = join66(ctx.projectDir, cache.path);
273911
274197
  if (existsSync56(cacheAbs) && !ctx.force) {
273912
- await mkdir15(dirname29(abs), { recursive: true });
274198
+ await mkdir16(dirname29(abs), { recursive: true });
273913
274199
  await copyFile4(cacheAbs, abs);
273914
274200
  await writeAssetMetadata({
273915
274201
  projectDir: ctx.projectDir,
@@ -273939,10 +274225,10 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
273939
274225
  const identityLock = "Keep the EXACT same person and face as the reference image(s): identical facial features (eyes, nose shape, jawline), skin tone and complexion, hairstyle, hair color, and wardrobe. Do not restyle or change the face. Change only the scene, pose, framing, and lighting. ";
273940
274226
  const generated = sheetBuffers.length > 0 ? await editKeyframeImage(`${identityLock}${keyframeCue}`, sheetBuffers, imgCtx, ratio) : await generateBackdropImage(keyframeCue, imgCtx, ratio);
273941
274227
  if (!generated.success) return { status: "failed", error: generated.error };
273942
- await mkdir15(dirname29(abs), { recursive: true });
273943
- await writeFile20(abs, generated.buffer);
273944
- await mkdir15(dirname29(cacheAbs), { recursive: true });
273945
- await writeFile20(cacheAbs, generated.buffer);
274228
+ await mkdir16(dirname29(abs), { recursive: true });
274229
+ await writeImageFile(abs, generated.buffer);
274230
+ await mkdir16(dirname29(cacheAbs), { recursive: true });
274231
+ await writeImageFile(cacheAbs, generated.buffer);
273946
274232
  await writeAssetMetadata({
273947
274233
  projectDir: ctx.projectDir,
273948
274234
  kind: "keyframe",
@@ -274038,7 +274324,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
274038
274324
  }
274039
274325
  const cacheAbs = join66(ctx.projectDir, cache.path);
274040
274326
  if (existsSync56(cacheAbs) && !ctx.force) {
274041
- await mkdir15(dirname29(abs), { recursive: true });
274327
+ await mkdir16(dirname29(abs), { recursive: true });
274042
274328
  await copyFile4(cacheAbs, abs);
274043
274329
  await writeAssetMetadata({
274044
274330
  projectDir: ctx.projectDir,
@@ -274115,7 +274401,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
274115
274401
  }
274116
274402
  }
274117
274403
  if (result.status === "completed" && existsSync56(abs)) {
274118
- await mkdir15(dirname29(cacheAbs), { recursive: true });
274404
+ await mkdir16(dirname29(cacheAbs), { recursive: true });
274119
274405
  await copyFile4(abs, cacheAbs);
274120
274406
  await writeAssetMetadata({
274121
274407
  projectDir: ctx.projectDir,
@@ -274217,7 +274503,7 @@ async function dispatchMusic(beat, ctx) {
274217
274503
  }
274218
274504
  const cacheAbs = join66(ctx.projectDir, cache.path);
274219
274505
  if (existsSync56(cacheAbs) && !ctx.force) {
274220
- await mkdir15(dirname29(abs), { recursive: true });
274506
+ await mkdir16(dirname29(abs), { recursive: true });
274221
274507
  await copyFile4(cacheAbs, abs);
274222
274508
  await writeAssetMetadata({
274223
274509
  projectDir: ctx.projectDir,
@@ -274295,7 +274581,7 @@ async function dispatchMusic(beat, ctx) {
274295
274581
  ctx.onProgress({ type: "music-failed", beatId: beat.id, error });
274296
274582
  return { status: "failed", error };
274297
274583
  }
274298
- await mkdir15(dirname29(cacheAbs), { recursive: true });
274584
+ await mkdir16(dirname29(cacheAbs), { recursive: true });
274299
274585
  await copyFile4(abs, cacheAbs);
274300
274586
  await writeAssetMetadata({
274301
274587
  projectDir: ctx.projectDir,
@@ -274559,14 +274845,6 @@ async function safeAudioDuration(absPath) {
274559
274845
  return void 0;
274560
274846
  }
274561
274847
  }
274562
- function resolveBuildVideoProvider(value) {
274563
- const provider = String(value ?? "seedance").toLowerCase();
274564
- if (provider === "fal") return "seedance";
274565
- if (provider === "seedance" || provider === "grok" || provider === "kling" || provider === "runway" || provider === "veo") {
274566
- return provider;
274567
- }
274568
- return "seedance";
274569
- }
274570
274848
  function resolveBuildMusicProvider(value) {
274571
274849
  const provider = String(value ?? "elevenlabs").toLowerCase();
274572
274850
  return provider === "replicate" ? "replicate" : "elevenlabs";
@@ -274578,8 +274856,7 @@ function stringOrUndefined4(value) {
274578
274856
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
274579
274857
  }
274580
274858
  async function apiKeyForVideoProvider(provider, projectDir) {
274581
- const providerKey = provider === "seedance" ? "fal" : provider === "grok" ? "xai" : provider === "veo" ? "google" : provider;
274582
- return getApiKeyFromConfig(providerKey, { cwd: projectDir });
274859
+ return getApiKeyFromConfig(videoProviderConfigKey(provider), { cwd: projectDir });
274583
274860
  }
274584
274861
  async function finalizeBuildResult(projectDir, startedAt, result, reportResult = result) {
274585
274862
  const reportPath = join66(projectDir, "build-report.json");
@@ -274602,7 +274879,7 @@ async function finalizeBuildResult(projectDir, startedAt, result, reportResult =
274602
274879
  const withMeta = decorate(result);
274603
274880
  const reportMeta = reportResult === result ? withMeta : decorate(reportResult);
274604
274881
  try {
274605
- await writeFile20(
274882
+ await writeFile21(
274606
274883
  reportPath,
274607
274884
  JSON.stringify(toBuildReport(projectDir, reportMeta), null, 2) + "\n",
274608
274885
  "utf-8"
@@ -274855,6 +275132,8 @@ var init_scene_build = __esm({
274855
275132
  init_scene_repair();
274856
275133
  init_tts_resolve();
274857
275134
  init_scene_build_mode();
275135
+ init_image_file();
275136
+ init_build_video_providers();
274858
275137
  init_scene_build_mode();
274859
275138
  BEAT_PRIMITIVES_CONCURRENCY = 4;
274860
275139
  }
@@ -274967,8 +275246,8 @@ __export(ai_audio_exports, {
274967
275246
  executeTranscribe: () => executeTranscribe,
274968
275247
  executeVoiceClone: () => executeVoiceClone
274969
275248
  });
274970
- import { resolve as resolve43, dirname as dirname30, basename as basename9, extname as extname12 } from "node:path";
274971
- import { readFile as readFile20, writeFile as writeFile21 } from "node:fs/promises";
275249
+ import { resolve as resolve43, dirname as dirname30, basename as basename9, extname as extname13 } from "node:path";
275250
+ import { readFile as readFile20, writeFile as writeFile23 } from "node:fs/promises";
274972
275251
  import { existsSync as existsSync57 } from "node:fs";
274973
275252
  async function executeTranscribe(options) {
274974
275253
  const { audioPath, language, output: output3, format: format4, apiKey } = options;
@@ -274990,7 +275269,7 @@ async function executeTranscribe(options) {
274990
275269
  outputPath = resolve43(process.cwd(), output3);
274991
275270
  const fmt = detectFormat(output3, format4);
274992
275271
  const content = formatTranscript(result, fmt);
274993
- await writeFile21(outputPath, content, "utf-8");
275272
+ await writeFile23(outputPath, content, "utf-8");
274994
275273
  }
274995
275274
  return {
274996
275275
  success: true,
@@ -275018,7 +275297,7 @@ async function executeIsolate(options) {
275018
275297
  return { success: false, error: result.error || "Audio isolation failed" };
275019
275298
  }
275020
275299
  const outputPath = resolve43(process.cwd(), output3);
275021
- await writeFile21(outputPath, result.audioBuffer);
275300
+ await writeFile23(outputPath, result.audioBuffer);
275022
275301
  return { success: true, outputPath };
275023
275302
  } catch (error) {
275024
275303
  return { success: false, error: `Audio isolation failed: ${error instanceof Error ? error.message : String(error)}` };
@@ -275064,7 +275343,7 @@ async function executeDub(options) {
275064
275343
  if (!openaiKey) return { success: false, error: "OPENAI_API_KEY required for Whisper transcription" };
275065
275344
  if (!anthropicKey) return { success: false, error: "ANTHROPIC_API_KEY required for Claude translation" };
275066
275345
  if (!analyzeOnly && !elevenlabsKey) return { success: false, error: "ELEVENLABS_API_KEY required for TTS" };
275067
- const ext = extname12(absPath).toLowerCase();
275346
+ const ext = extname13(absPath).toLowerCase();
275068
275347
  const isVideo = [".mp4", ".mov", ".avi", ".mkv", ".webm"].includes(ext);
275069
275348
  let audioPath = absPath;
275070
275349
  if (isVideo) {
@@ -275129,7 +275408,7 @@ ${segmentTexts}`,
275129
275408
  translated: translatedSegments[i3]?.text || s2.text
275130
275409
  }))
275131
275410
  };
275132
- await writeFile21(timingPath, JSON.stringify(timingData, null, 2));
275411
+ await writeFile23(timingPath, JSON.stringify(timingData, null, 2));
275133
275412
  }
275134
275413
  return {
275135
275414
  success: true,
@@ -275149,10 +275428,10 @@ ${segmentTexts}`,
275149
275428
  }
275150
275429
  }
275151
275430
  const combinedBuffer = Buffer.concat(dubbedBuffers);
275152
- const outputExt = isVideo ? ".mp3" : extname12(absPath);
275153
- const defaultOutputPath2 = resolve43(dirname30(absPath), `${basename9(absPath, extname12(absPath))}-${language}${outputExt}`);
275431
+ const outputExt = isVideo ? ".mp3" : extname13(absPath);
275432
+ const defaultOutputPath2 = resolve43(dirname30(absPath), `${basename9(absPath, extname13(absPath))}-${language}${outputExt}`);
275154
275433
  const finalOutputPath = resolve43(process.cwd(), output3 || defaultOutputPath2);
275155
- await writeFile21(finalOutputPath, combinedBuffer);
275434
+ await writeFile23(finalOutputPath, combinedBuffer);
275156
275435
  if (isVideo && audioPath !== absPath) {
275157
275436
  try {
275158
275437
  const { unlink: unlink6 } = await import("node:fs/promises");
@@ -275187,7 +275466,7 @@ async function executeDuck(options) {
275187
275466
  const absVoicePath = resolve43(process.cwd(), voicePath);
275188
275467
  if (!existsSync57(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
275189
275468
  if (!existsSync57(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
275190
- const defaultOutput = resolve43(dirname30(absMusicPath), `${basename9(absMusicPath, extname12(absMusicPath))}-ducked${extname12(absMusicPath)}`);
275469
+ const defaultOutput = resolve43(dirname30(absMusicPath), `${basename9(absMusicPath, extname13(absMusicPath))}-ducked${extname13(absMusicPath)}`);
275191
275470
  const outputPath = resolve43(process.cwd(), output3 || defaultOutput);
275192
275471
  const filter3 = `[1:a]asplit=2[sc][mix];[0:a][sc]sidechaincompress=threshold=${threshold}dB:ratio=${ratio}:attack=${attack}:release=${release}[ducked];[ducked][mix]amix=inputs=2:duration=longest`;
275193
275472
  await execSafe("ffmpeg", [
@@ -275452,7 +275731,7 @@ var init_silence_cut = __esm({
275452
275731
 
275453
275732
  // ../cli/src/commands/_shared/edit/jump-cut.ts
275454
275733
  import { existsSync as existsSync59 } from "node:fs";
275455
- import { readFile as readFile23, writeFile as writeFile23, mkdir as mkdir16 } from "node:fs/promises";
275734
+ import { readFile as readFile23, writeFile as writeFile24, mkdir as mkdir17 } from "node:fs/promises";
275456
275735
  import { join as join67 } from "node:path";
275457
275736
  async function transcribeWithWords(audioPath, apiKey, language) {
275458
275737
  const audioBuffer = await readFile23(audioPath);
@@ -275532,7 +275811,7 @@ async function executeJumpCut(options) {
275532
275811
  }
275533
275812
  try {
275534
275813
  const tmpDir = `/tmp/vibe_jumpcut_${Date.now()}`;
275535
- await mkdir16(tmpDir, { recursive: true });
275814
+ await mkdir17(tmpDir, { recursive: true });
275536
275815
  const audioPath = join67(tmpDir, "audio.wav");
275537
275816
  try {
275538
275817
  await execSafe(
@@ -275611,7 +275890,7 @@ async function executeJumpCut(options) {
275611
275890
  }
275612
275891
  const concatList = segmentPaths.map((p2) => `file '${p2}'`).join("\n");
275613
275892
  const listPath = join67(tmpDir, "concat.txt");
275614
- await writeFile23(listPath, concatList);
275893
+ await writeFile24(listPath, concatList);
275615
275894
  await execSafe(
275616
275895
  "ffmpeg",
275617
275896
  [
@@ -275638,8 +275917,8 @@ async function executeJumpCut(options) {
275638
275917
  };
275639
275918
  } finally {
275640
275919
  try {
275641
- const { rm: rm10 } = await import("node:fs/promises");
275642
- await rm10(tmpDir, { recursive: true, force: true });
275920
+ const { rm: rm11 } = await import("node:fs/promises");
275921
+ await rm11(tmpDir, { recursive: true, force: true });
275643
275922
  } catch {
275644
275923
  }
275645
275924
  }
@@ -275716,29 +275995,27 @@ __export(remotion_exports, {
275716
275995
  wrapComponentWithImage: () => wrapComponentWithImage,
275717
275996
  wrapComponentWithVideo: () => wrapComponentWithVideo
275718
275997
  });
275719
- import { writeFile as writeFile24, mkdir as mkdir17, rm as rm7, copyFile as copyFile5 } from "node:fs/promises";
275998
+ import { writeFile as writeFile25, mkdir as mkdir18, rm as rm8, copyFile as copyFile5 } from "node:fs/promises";
275720
275999
  import { existsSync as existsSync60 } from "node:fs";
275721
276000
  import { join as join68 } from "node:path";
275722
276001
  import { tmpdir as tmpdir9 } from "node:os";
275723
276002
  import { createHash as createHash11 } from "node:crypto";
275724
276003
  async function ensureRemotionInstalled() {
275725
276004
  try {
275726
- await execSafe("npx", ["--yes", "remotion", "--help"], { timeout: 6e4 });
276005
+ await execSafe("npm", ["--version"], { timeout: 3e4 });
275727
276006
  return null;
275728
276007
  } catch (error) {
275729
276008
  const detail = error instanceof Error ? error.message : String(error);
275730
- console.error(`[Remotion] ensureRemotionInstalled failed: ${detail.slice(0, 300)}`);
275731
276009
  return [
275732
- "Remotion CLI not found or failed to initialize.",
276010
+ "Remotion renders need npm to install the Remotion CLI into a temporary project.",
275733
276011
  ` Debug: ${detail.slice(0, 200)}`,
275734
- " Fix: npm install -g @remotion/cli",
275735
- " Or ensure npx is available and can download @remotion/cli on demand."
276012
+ " Fix: install Node.js 20+ (which includes npm) and make sure `npm` is on PATH."
275736
276013
  ].join("\n");
275737
276014
  }
275738
276015
  }
275739
276016
  async function scaffoldRemotionProject(componentCode, componentName, opts) {
275740
276017
  const dir = join68(tmpdir9(), `vibe_motion_${Date.now()}`);
275741
- await mkdir17(dir, { recursive: true });
276018
+ await mkdir18(dir, { recursive: true });
275742
276019
  const deps = {
275743
276020
  remotion: REMOTION_VERSION,
275744
276021
  "@remotion/cli": REMOTION_VERSION,
@@ -275756,7 +276033,7 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
275756
276033
  dependencies: deps
275757
276034
  };
275758
276035
  const packageJsonStr = JSON.stringify(packageJson, null, 2);
275759
- await writeFile24(join68(dir, "package.json"), packageJsonStr);
276036
+ await writeFile25(join68(dir, "package.json"), packageJsonStr);
275760
276037
  const tsconfig = {
275761
276038
  compilerOptions: {
275762
276039
  target: "ES2020",
@@ -275768,8 +276045,8 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
275768
276045
  skipLibCheck: true
275769
276046
  }
275770
276047
  };
275771
- await writeFile24(join68(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
275772
- await writeFile24(join68(dir, "Component.tsx"), componentCode);
276048
+ await writeFile25(join68(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
276049
+ await writeFile25(join68(dir, "Component.tsx"), componentCode);
275773
276050
  const rootCode = `import { registerRoot, Composition } from "remotion";
275774
276051
  import { ${componentName} } from "./Component";
275775
276052
 
@@ -275788,7 +276065,7 @@ const Root = () => {
275788
276065
 
275789
276066
  registerRoot(Root);
275790
276067
  `;
275791
- await writeFile24(join68(dir, "Root.tsx"), rootCode);
276068
+ await writeFile25(join68(dir, "Root.tsx"), rootCode);
275792
276069
  const depsHash = createHash11("md5").update(packageJsonStr).digest("hex").slice(0, 12);
275793
276070
  const cacheMarker = join68(REMOTION_CACHE_DIR, `.deps-${depsHash}`);
275794
276071
  const cachedModules = join68(REMOTION_CACHE_DIR, "node_modules");
@@ -275802,15 +276079,15 @@ registerRoot(Root);
275802
276079
  } else {
275803
276080
  await this_npmInstall(dir);
275804
276081
  try {
275805
- await mkdir17(REMOTION_CACHE_DIR, { recursive: true });
275806
- await writeFile24(join68(REMOTION_CACHE_DIR, "package.json"), packageJsonStr);
276082
+ await mkdir18(REMOTION_CACHE_DIR, { recursive: true });
276083
+ await writeFile25(join68(REMOTION_CACHE_DIR, "package.json"), packageJsonStr);
275807
276084
  if (existsSync60(join68(dir, "node_modules"))) {
275808
276085
  const { rename: rename7, symlink: symlink4 } = await import("node:fs/promises");
275809
276086
  await rename7(join68(dir, "node_modules"), cachedModules).catch(() => {
275810
276087
  });
275811
276088
  await symlink4(cachedModules, join68(dir, "node_modules"), "dir").catch(() => {
275812
276089
  });
275813
- await writeFile24(cacheMarker, depsHash);
276090
+ await writeFile25(cacheMarker, depsHash);
275814
276091
  }
275815
276092
  } catch {
275816
276093
  }
@@ -275910,7 +276187,7 @@ async function renderMotion(options) {
275910
276187
  stderr: ${stderr}` : ""}`);
275911
276188
  return { success: false, error: `Remotion render failed: ${msg}` };
275912
276189
  } finally {
275913
- await rm7(dir, { recursive: true, force: true }).catch(() => {
276190
+ await rm8(dir, { recursive: true, force: true }).catch(() => {
275914
276191
  });
275915
276192
  }
275916
276193
  }
@@ -275979,7 +276256,7 @@ async function renderWithEmbeddedImage(options) {
275979
276256
  );
275980
276257
  try {
275981
276258
  const publicDir = join68(dir, "public");
275982
- await mkdir17(publicDir, { recursive: true });
276259
+ await mkdir18(publicDir, { recursive: true });
275983
276260
  await copyFile5(options.imagePath, join68(publicDir, options.imageFileName));
275984
276261
  const entryPoint = join68(dir, "Root.tsx");
275985
276262
  const mp4Out = options.outputPath.replace(/\.\w+$/, ".mp4");
@@ -276002,7 +276279,7 @@ async function renderWithEmbeddedImage(options) {
276002
276279
  const msg = error instanceof Error ? error.message : String(error);
276003
276280
  return { success: false, error: `Remotion image embed render failed: ${msg}` };
276004
276281
  } finally {
276005
- await rm7(dir, { recursive: true, force: true }).catch(() => {
276282
+ await rm8(dir, { recursive: true, force: true }).catch(() => {
276006
276283
  });
276007
276284
  }
276008
276285
  }
@@ -276043,7 +276320,7 @@ async function renderWithEmbeddedVideo(options) {
276043
276320
  );
276044
276321
  try {
276045
276322
  const publicDir = join68(dir, "public");
276046
- await mkdir17(publicDir, { recursive: true });
276323
+ await mkdir18(publicDir, { recursive: true });
276047
276324
  await copyFile5(options.videoPath, join68(publicDir, options.videoFileName));
276048
276325
  const entryPoint = join68(dir, "Root.tsx");
276049
276326
  const mp4VideoOnly = options.outputPath.replace(/\.\w+$/, "_video_only.mp4");
@@ -276079,14 +276356,14 @@ async function renderWithEmbeddedVideo(options) {
276079
276356
  "-shortest",
276080
276357
  mp4Out
276081
276358
  ], { timeout: 12e4 });
276082
- await rm7(mp4VideoOnly, { force: true }).catch(() => {
276359
+ await rm8(mp4VideoOnly, { force: true }).catch(() => {
276083
276360
  });
276084
276361
  return { success: true, outputPath: mp4Out };
276085
276362
  } catch (error) {
276086
276363
  const msg = error instanceof Error ? error.message : String(error);
276087
276364
  return { success: false, error: `Remotion video embed render failed: ${msg}` };
276088
276365
  } finally {
276089
- await rm7(dir, { recursive: true, force: true }).catch(() => {
276366
+ await rm8(dir, { recursive: true, force: true }).catch(() => {
276090
276367
  });
276091
276368
  }
276092
276369
  }
@@ -276467,8 +276744,8 @@ var init_remotion = __esm({
276467
276744
 
276468
276745
  // ../cli/src/commands/_shared/edit/caption.ts
276469
276746
  import { existsSync as existsSync61 } from "node:fs";
276470
- import { readFile as readFile24, writeFile as writeFile25, mkdir as mkdir18 } from "node:fs/promises";
276471
- import { dirname as dirname31, basename as basename10, extname as extname13, join as join69 } from "node:path";
276747
+ import { readFile as readFile24, writeFile as writeFile26, mkdir as mkdir19 } from "node:fs/promises";
276748
+ import { dirname as dirname31, basename as basename10, extname as extname14, join as join69 } from "node:path";
276472
276749
  function getCaptionForceStyle(style, fontSize, fontColor, position) {
276473
276750
  const alignment = position === "top" ? 8 : position === "center" ? 5 : 2;
276474
276751
  const marginV = position === "center" ? 0 : 30;
@@ -276514,7 +276791,7 @@ async function executeCaption(options) {
276514
276791
  }
276515
276792
  try {
276516
276793
  const tmpDir = `/tmp/vibe_caption_${Date.now()}`;
276517
- await mkdir18(tmpDir, { recursive: true });
276794
+ await mkdir19(tmpDir, { recursive: true });
276518
276795
  const audioPath = join69(tmpDir, "audio.wav");
276519
276796
  const srtPath = join69(tmpDir, "captions.srt");
276520
276797
  try {
@@ -276547,7 +276824,7 @@ async function executeCaption(options) {
276547
276824
  };
276548
276825
  }
276549
276826
  const srtContent = formatSRT(transcriptResult.segments);
276550
- await writeFile25(srtPath, srtContent);
276827
+ await writeFile26(srtPath, srtContent);
276551
276828
  const { width, height } = await getVideoResolution(videoPath);
276552
276829
  const fontSize = customFontSize || Math.round(height / 18);
276553
276830
  let hasSubtitles = false;
@@ -276587,9 +276864,9 @@ async function executeCaption(options) {
276587
276864
  const outputDir2 = dirname31(outputPath);
276588
276865
  const outputSrtPath2 = join69(
276589
276866
  outputDir2,
276590
- basename10(outputPath, extname13(outputPath)) + ".srt"
276867
+ basename10(outputPath, extname14(outputPath)) + ".srt"
276591
276868
  );
276592
- await writeFile25(outputSrtPath2, srtContent);
276869
+ await writeFile26(outputSrtPath2, srtContent);
276593
276870
  return { success: false, error: `${remotionErr}
276594
276871
  SRT saved to: ${outputSrtPath2}` };
276595
276872
  }
@@ -276626,9 +276903,9 @@ SRT saved to: ${outputSrtPath2}` };
276626
276903
  const outputDir2 = dirname31(outputPath);
276627
276904
  const outputSrtPath2 = join69(
276628
276905
  outputDir2,
276629
- basename10(outputPath, extname13(outputPath)) + ".srt"
276906
+ basename10(outputPath, extname14(outputPath)) + ".srt"
276630
276907
  );
276631
- await writeFile25(outputSrtPath2, srtContent);
276908
+ await writeFile26(outputSrtPath2, srtContent);
276632
276909
  return {
276633
276910
  success: false,
276634
276911
  error: `${renderResult.error}
@@ -276639,9 +276916,9 @@ SRT saved to: ${outputSrtPath2}`
276639
276916
  const outputDir = dirname31(outputPath);
276640
276917
  const outputSrtPath = join69(
276641
276918
  outputDir,
276642
- basename10(outputPath, extname13(outputPath)) + ".srt"
276919
+ basename10(outputPath, extname14(outputPath)) + ".srt"
276643
276920
  );
276644
- await writeFile25(outputSrtPath, srtContent);
276921
+ await writeFile26(outputSrtPath, srtContent);
276645
276922
  return {
276646
276923
  success: true,
276647
276924
  outputPath,
@@ -276650,8 +276927,8 @@ SRT saved to: ${outputSrtPath2}`
276650
276927
  };
276651
276928
  } finally {
276652
276929
  try {
276653
- const { rm: rm10 } = await import("node:fs/promises");
276654
- await rm10(tmpDir, { recursive: true, force: true });
276930
+ const { rm: rm11 } = await import("node:fs/promises");
276931
+ await rm11(tmpDir, { recursive: true, force: true });
276655
276932
  } catch {
276656
276933
  }
276657
276934
  }
@@ -276812,7 +277089,7 @@ var init_fade = __esm({
276812
277089
 
276813
277090
  // ../cli/src/commands/_shared/edit/translate-srt.ts
276814
277091
  import { existsSync as existsSync65 } from "node:fs";
276815
- import { readFile as readFile25, writeFile as writeFile26 } from "node:fs/promises";
277092
+ import { readFile as readFile25, writeFile as writeFile27 } from "node:fs/promises";
276816
277093
  async function executeTranslateSrt(options) {
276817
277094
  const {
276818
277095
  srtPath,
@@ -276855,15 +277132,14 @@ ${textsToTranslate}`;
276855
277132
  Authorization: `Bearer ${openaiKey}`
276856
277133
  },
276857
277134
  body: JSON.stringify({
276858
- model: "gpt-5-mini",
276859
- messages: [{ role: "user", content: translatePrompt }],
276860
- temperature: 0.3
277135
+ model: defaultModel("openai", "llm").id,
277136
+ messages: [{ role: "user", content: translatePrompt }]
276861
277137
  })
276862
277138
  });
276863
277139
  if (!response.ok) {
276864
277140
  return {
276865
277141
  success: false,
276866
- error: `OpenAI API error: ${response.status} ${response.statusText}`
277142
+ error: `OpenAI API error: ${response.status} ${response.statusText}: ${await response.text()}`
276867
277143
  };
276868
277144
  }
276869
277145
  const data2 = await response.json();
@@ -276884,15 +277160,15 @@ ${textsToTranslate}`;
276884
277160
  "anthropic-version": "2023-06-01"
276885
277161
  },
276886
277162
  body: JSON.stringify({
276887
- model: "claude-sonnet-4-6-20250514",
276888
- max_tokens: 4096,
277163
+ model: defaultModel("claude", "llm").id,
277164
+ max_tokens: 16e3,
276889
277165
  messages: [{ role: "user", content: translatePrompt }]
276890
277166
  })
276891
277167
  });
276892
277168
  if (!response.ok) {
276893
277169
  return {
276894
277170
  success: false,
276895
- error: `Claude API error: ${response.status} ${response.statusText}`
277171
+ error: `Claude API error: ${response.status} ${response.statusText}: ${await response.text()}`
276896
277172
  };
276897
277173
  }
276898
277174
  const data2 = await response.json();
@@ -276916,7 +277192,7 @@ ${textsToTranslate}`;
276916
277192
  }
276917
277193
  }
276918
277194
  const translatedSrt = formatSRT(translatedSegments);
276919
- await writeFile26(outputPath, translatedSrt);
277195
+ await writeFile27(outputPath, translatedSrt);
276920
277196
  return {
276921
277197
  success: true,
276922
277198
  outputPath,
@@ -276935,6 +277211,7 @@ var init_translate_srt = __esm({
276935
277211
  "../cli/src/commands/_shared/edit/translate-srt.ts"() {
276936
277212
  "use strict";
276937
277213
  init_subtitle();
277214
+ init_dist();
276938
277215
  }
276939
277216
  });
276940
277217
 
@@ -277168,7 +277445,7 @@ var init_ai_edit = __esm({
277168
277445
  });
277169
277446
 
277170
277447
  // ../cli/src/commands/ai-edit-cli.ts
277171
- import { resolve as resolve45, extname as extname14, basename as basename11 } from "node:path";
277448
+ import { resolve as resolve45, extname as extname15, basename as basename11 } from "node:path";
277172
277449
  import { existsSync as existsSync67 } from "node:fs";
277173
277450
  function registerEditCommands(aiCommand) {
277174
277451
  aiCommand.command("silence-cut").alias("sc").description("Remove silent segments from video (FFmpeg default, or Gemini for smart detection)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path (default: <name>-cut.<ext>)").option("--noise <dB>", "Silence threshold in dB (default: -30)", "-30").option("-d, --min-duration <seconds>", "Minimum silence duration to cut (default: 0.5)", "0.5").option("--padding <seconds>", "Padding around non-silent segments (default: 0.1)", "0.1").option("--analyze-only", "(deprecated - use `vibe detect silence`) Only detect silence, don't cut").option("--use-gemini", "Use Gemini Video Understanding for context-aware silence detection").option("-m, --model <model>", "Gemini model (default: flash)").option("--low-res", "Low resolution mode for longer videos (Gemini only)").option("-k, --api-key <key>", "Google API key override (or set GOOGLE_API_KEY env)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
@@ -277192,7 +277469,7 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
277192
277469
  if (!commandExists("ffmpeg")) {
277193
277470
  exitWithError(generalError("FFmpeg not found. Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux). Run `vibe doctor` for details."));
277194
277471
  }
277195
- const ext = extname14(videoPath);
277472
+ const ext = extname15(videoPath);
277196
277473
  const name = basename11(videoPath, ext);
277197
277474
  const outputPath = options.output || `${name}-cut${ext}`;
277198
277475
  const useGemini = options.useGemini || false;
@@ -277322,7 +277599,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
277322
277599
  if (!apiKey) {
277323
277600
  exitWithError(authError("OPENAI_API_KEY", "OpenAI"));
277324
277601
  }
277325
- const ext = extname14(videoPath);
277602
+ const ext = extname15(videoPath);
277326
277603
  const name = basename11(videoPath, ext);
277327
277604
  const outputPath = options.output || `${name}-captioned${ext}`;
277328
277605
  const spinner2 = ora("Starting caption process...").start();
@@ -277396,7 +277673,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
277396
277673
  });
277397
277674
  return;
277398
277675
  }
277399
- const ext = extname14(inputPath);
277676
+ const ext = extname15(inputPath);
277400
277677
  const name = basename11(inputPath, ext);
277401
277678
  const outputPath = options.output || `${name}-denoised${ext}`;
277402
277679
  const spinner2 = ora("Applying noise reduction...").start();
@@ -277464,7 +277741,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
277464
277741
  });
277465
277742
  return;
277466
277743
  }
277467
- const ext = extname14(videoPath);
277744
+ const ext = extname15(videoPath);
277468
277745
  const name = basename11(videoPath, ext);
277469
277746
  const outputPath = options.output || `${name}-faded${ext}`;
277470
277747
  const spinner2 = ora("Applying fade effects...").start();
@@ -277542,7 +277819,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
277542
277819
  if (!apiKey) {
277543
277820
  exitWithError(authError(envKey, providerName));
277544
277821
  }
277545
- const ext = extname14(srtPath);
277822
+ const ext = extname15(srtPath);
277546
277823
  const name = basename11(srtPath, ext);
277547
277824
  const outputPath = options.output || `${name}-${options.target}${ext}`;
277548
277825
  const spinner2 = ora(`Translating to ${options.target}...`).start();
@@ -277620,7 +277897,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
277620
277897
  if (!apiKey) {
277621
277898
  exitWithError(authError("OPENAI_API_KEY", "OpenAI"));
277622
277899
  }
277623
- const ext = extname14(videoPath);
277900
+ const ext = extname15(videoPath);
277624
277901
  const name = basename11(videoPath, ext);
277625
277902
  const outputPath = options.output || `${name}-jumpcut${ext}`;
277626
277903
  const fillers = options.fillers ? options.fillers.split(",").map((f2) => f2.trim()) : void 0;
@@ -278080,7 +278357,7 @@ var init_project_resolver = __esm({
278080
278357
 
278081
278358
  // ../cli/src/commands/_shared/execute-fill-gaps.ts
278082
278359
  import { existsSync as existsSync68 } from "node:fs";
278083
- import { readFile as readFile26, writeFile as writeFile27, mkdir as mkdir19, rename as renameFs } from "node:fs/promises";
278360
+ import { readFile as readFile26, writeFile as writeFile28, mkdir as mkdir20, rename as renameFs } from "node:fs/promises";
278084
278361
  import { resolve as resolve47, dirname as dirname32 } from "node:path";
278085
278362
  function detectVideoGaps(videoClips, totalDuration) {
278086
278363
  const gaps = [];
@@ -278135,22 +278412,13 @@ function analyzeGapFillability(gaps, videoClips, sources) {
278135
278412
  return { gap, canExtendBefore, canExtendAfter, remainingGap, gapStart };
278136
278413
  });
278137
278414
  }
278138
- async function uploadFrameToImgbb(framePath, imgbbApiKey) {
278415
+ async function uploadFrame(host, framePath) {
278139
278416
  try {
278140
- const frameBuffer = await readFile26(framePath);
278141
- const frameBase64 = frameBuffer.toString("base64");
278142
- const formData = new FormData();
278143
- formData.append("key", imgbbApiKey);
278144
- formData.append("image", frameBase64);
278145
- const response = await fetch("https://api.imgbb.com/1/upload", {
278146
- method: "POST",
278147
- body: formData
278417
+ const upload = await host.uploadImage(await readFile26(framePath), {
278418
+ filename: framePath,
278419
+ mimeType: "image/png"
278148
278420
  });
278149
- const data2 = await response.json();
278150
- if (!data2.success || !data2.data?.url) {
278151
- return { error: data2.error?.message || "Upload failed" };
278152
- }
278153
- return { url: data2.data.url };
278421
+ return { url: upload.url };
278154
278422
  } catch (err) {
278155
278423
  return { error: err instanceof Error ? err.message : String(err) };
278156
278424
  }
@@ -278291,21 +278559,23 @@ async function executeFillGaps(options) {
278291
278559
  if (!kling.isConfigured()) {
278292
278560
  return {
278293
278561
  success: false,
278294
- error: "Invalid KLING_API_KEY (expected ACCESS_KEY:SECRET_KEY format)",
278562
+ error: "Invalid KLING_API_KEY (set your Kling API key, or the legacy ACCESS_KEY:SECRET_KEY pair)",
278295
278563
  humanLines
278296
278564
  };
278297
278565
  }
278298
278566
  const projectDir = dirname32(filePath);
278299
278567
  const footageDir = options.dir ? resolve47(process.cwd(), options.dir) : resolve47(projectDir, "footage");
278300
278568
  if (!existsSync68(footageDir)) {
278301
- await mkdir19(footageDir, { recursive: true });
278569
+ await mkdir20(footageDir, { recursive: true });
278302
278570
  }
278303
278571
  humanLines.push("Generating AI Videos");
278304
- const imgbbApiKey = options.imgbbApiKey || await getApiKey("IMGBB_API_KEY", "imgbb", void 0);
278305
- if (!imgbbApiKey) {
278572
+ let uploadHost;
278573
+ try {
278574
+ uploadHost = await resolveUploadHost();
278575
+ } catch (err) {
278306
278576
  return {
278307
278577
  success: false,
278308
- error: "IMGBB_API_KEY required for image hosting. Get a free API key at https://api.imgbb.com/",
278578
+ error: err instanceof Error ? err.message : String(err),
278309
278579
  humanLines
278310
278580
  };
278311
278581
  }
@@ -278349,12 +278619,12 @@ async function executeFillGaps(options) {
278349
278619
  humanLines
278350
278620
  };
278351
278621
  }
278352
- onProgress("Uploading frame to imgbb...");
278353
- const upload = await uploadFrameToImgbb(framePath, imgbbApiKey);
278622
+ onProgress(`Uploading frame via ${uploadHost.provider}...`);
278623
+ const upload = await uploadFrame(uploadHost, framePath);
278354
278624
  if (!upload.url) {
278355
278625
  return {
278356
278626
  success: false,
278357
- error: `Failed to upload frame to imgbb: ${upload.error || "unknown"}`,
278627
+ error: `Failed to upload frame via ${uploadHost.provider}: ${upload.error || "unknown"}`,
278358
278628
  humanLines
278359
278629
  };
278360
278630
  }
@@ -278363,13 +278633,13 @@ async function executeFillGaps(options) {
278363
278633
  let generatedDuration = 0;
278364
278634
  const generatedVideos = [];
278365
278635
  const initialDuration = Math.min(10, targetDuration);
278366
- const klingDuration = initialDuration > 5 ? "10" : "5";
278367
- onProgress(`Generating ${klingDuration}s video with Kling...`);
278636
+ const klingDuration2 = initialDuration > 5 ? "10" : "5";
278637
+ onProgress(`Generating ${klingDuration2}s video with Kling...`);
278368
278638
  const prompt3 = options.prompt || "Continue the scene naturally with subtle motion";
278369
278639
  const result = await kling.generateVideo(prompt3, {
278370
278640
  prompt: prompt3,
278371
278641
  referenceImage: frameUrl,
278372
- duration: parseInt(klingDuration),
278642
+ duration: parseInt(klingDuration2),
278373
278643
  aspectRatio: options.ratio || "16:9",
278374
278644
  mode: options.mode || "std"
278375
278645
  });
@@ -278392,8 +278662,8 @@ async function executeFillGaps(options) {
278392
278662
  const videoPath = resolve47(footageDir, videoFileName);
278393
278663
  onProgress("Downloading generated video...");
278394
278664
  const videoBuffer = await downloadVideo(finalResult.videoUrl);
278395
- await writeFile27(videoPath, videoBuffer);
278396
- generatedDuration = finalResult.duration || parseInt(klingDuration);
278665
+ await writeFile28(videoPath, videoBuffer);
278666
+ generatedDuration = finalResult.duration || parseInt(klingDuration2);
278397
278667
  generatedVideos.push(videoPath);
278398
278668
  humanLines.push(` Generated: ${videoFileName} (${generatedDuration}s)`);
278399
278669
  let segmentIndex = 1;
@@ -278429,7 +278699,7 @@ async function executeFillGaps(options) {
278429
278699
  humanLines.push(" Failed to extract frame for continuation");
278430
278700
  break;
278431
278701
  }
278432
- const extUpload = await uploadFrameToImgbb(lastFramePath, imgbbApiKey);
278702
+ const extUpload = await uploadFrame(uploadHost, lastFramePath);
278433
278703
  if (!extUpload.url) {
278434
278704
  humanLines.push(" Failed to upload continuation frame");
278435
278705
  break;
@@ -278462,11 +278732,11 @@ async function executeFillGaps(options) {
278462
278732
  `gap-fill-${gap.start.toFixed(2)}-${gap.end.toFixed(2)}-seg${segmentIndex}.mp4`
278463
278733
  );
278464
278734
  const segVideoBuffer = await downloadVideo(segFinalResult.videoUrl);
278465
- await writeFile27(segVideoPath, segVideoBuffer);
278735
+ await writeFile28(segVideoPath, segVideoBuffer);
278466
278736
  const concatListPath = resolve47(footageDir, `concat-${gap.start.toFixed(2)}.txt`);
278467
278737
  const concatList = generatedVideos.map((v4) => `file '${v4}'`).join("\n") + `
278468
278738
  file '${segVideoPath}'`;
278469
- await writeFile27(concatListPath, concatList);
278739
+ await writeFile28(concatListPath, concatList);
278470
278740
  const concatOutputPath = resolve47(
278471
278741
  footageDir,
278472
278742
  `gap-fill-${gap.start.toFixed(2)}-${gap.end.toFixed(2)}-merged.mp4`
@@ -278524,7 +278794,7 @@ file '${segVideoPath}'`;
278524
278794
  let outputPath = filePath;
278525
278795
  if (generatedCount > 0) {
278526
278796
  outputPath = options.output ? resolve47(process.cwd(), options.output) : filePath;
278527
- await writeFile27(outputPath, JSON.stringify(project.toJSON(), null, 2));
278797
+ await writeFile28(outputPath, JSON.stringify(project.toJSON(), null, 2));
278528
278798
  humanLines.push(`\u2714 Filled ${generatedCount} gap(s) with AI-generated video`);
278529
278799
  humanLines.push(`Project saved: ${outputPath}`);
278530
278800
  } else {
@@ -278552,6 +278822,7 @@ var init_execute_fill_gaps = __esm({
278552
278822
  init_dist();
278553
278823
  init_engine();
278554
278824
  init_api_key();
278825
+ init_upload_host();
278555
278826
  init_exec_safe();
278556
278827
  init_project_resolver();
278557
278828
  init_ai_helpers();
@@ -278618,7 +278889,14 @@ __export(ai_motion_exports, {
278618
278889
  });
278619
278890
  import { resolve as resolve48 } from "node:path";
278620
278891
  import { existsSync as existsSync69 } from "node:fs";
278621
- import { readFile as readFile27, writeFile as writeFile28 } from "node:fs/promises";
278892
+ import { readFile as readFile27, writeFile as writeFile29 } from "node:fs/promises";
278893
+ function resolveMotionModel(alias) {
278894
+ const claude = findModel("claude", "llm", alias);
278895
+ if (claude) return { provider: "claude", modelId: claude.id };
278896
+ const gemini = findModel("gemini", "llm", alias);
278897
+ if (gemini) return { provider: "gemini", modelId: gemini.id };
278898
+ return { provider: "claude", modelId: defaultModel("claude", "llm").id };
278899
+ }
278622
278900
  async function getOptionalGoogleApiKey() {
278623
278901
  const configKey = await getApiKeyFromConfig("google");
278624
278902
  if (configKey) return configKey;
@@ -278633,7 +278911,7 @@ function normalizeUnderstand(value) {
278633
278911
  }
278634
278912
  async function executeMotion(options) {
278635
278913
  const modelAlias = options.model || "sonnet";
278636
- const modelConfig = MODEL_MAP2[modelAlias] ?? MODEL_MAP2["sonnet"];
278914
+ const modelConfig = resolveMotionModel(modelAlias);
278637
278915
  const useGemini = modelConfig.provider === "gemini";
278638
278916
  const width = options.width || 1920;
278639
278917
  const height = options.height || 1080;
@@ -278788,7 +279066,7 @@ Use this video understanding to place motion graphics in safe zones, match the c
278788
279066
  const defaultOutput = options.video || options.image ? "motion-output.mp4" : options.render ? "motion.webm" : "motion.tsx";
278789
279067
  const outputPath = resolve48(process.cwd(), options.output || defaultOutput);
278790
279068
  const codePath = outputPath.replace(/\.\w+$/, ".tsx");
278791
- await writeFile28(codePath, component.code, "utf-8");
279069
+ await writeFile29(codePath, component.code, "utf-8");
278792
279070
  const shouldRender = options.render || !!options.video || !!options.image;
278793
279071
  if (!shouldRender) {
278794
279072
  return { success: true, codePath, componentName: component.name };
@@ -279012,7 +279290,6 @@ function registerMotionCommand(aiCommand) {
279012
279290
  }
279013
279291
  });
279014
279292
  }
279015
- var MODEL_MAP2;
279016
279293
  var init_ai_motion = __esm({
279017
279294
  "../cli/src/commands/ai-motion.ts"() {
279018
279295
  "use strict";
@@ -279023,14 +279300,6 @@ var init_ai_motion = __esm({
279023
279300
  init_config();
279024
279301
  init_output();
279025
279302
  init_validate();
279026
- MODEL_MAP2 = {
279027
- sonnet: { provider: "claude", modelId: "claude-sonnet-4-6" },
279028
- opus: { provider: "claude", modelId: "claude-opus-4-7" },
279029
- "opus-4-6": { provider: "claude", modelId: "claude-opus-4-6" },
279030
- gemini: { provider: "gemini", modelId: GEMINI_DEFAULT_TEXT_MODEL },
279031
- "gemini-2.5-pro": { provider: "gemini", modelId: "gemini-2.5-pro" },
279032
- "gemini-3.1-pro": { provider: "gemini", modelId: "gemini-3.1-pro-preview" }
279033
- };
279034
279303
  }
279035
279304
  });
279036
279305
 
@@ -279267,7 +279536,7 @@ __export(project_builder_exports, {
279267
279536
  buildTempProject: () => buildTempProject,
279268
279537
  resolveSourceUrl: () => resolveSourceUrl
279269
279538
  });
279270
- import { mkdtemp as mkdtemp5, mkdir as mkdir20, copyFile as copyFile6, writeFile as writeFile29, rm as rm8, readdir as readdir7 } from "node:fs/promises";
279539
+ import { mkdtemp as mkdtemp5, mkdir as mkdir21, copyFile as copyFile6, writeFile as writeFile30, rm as rm9, readdir as readdir7 } from "node:fs/promises";
279271
279540
  import { existsSync as existsSync70 } from "node:fs";
279272
279541
  import { tmpdir as tmpdir10 } from "node:os";
279273
279542
  import { createRequire as createRequire4 } from "node:module";
@@ -279275,7 +279544,7 @@ import * as path15 from "node:path";
279275
279544
  async function buildTempProject(state, projectFileDir) {
279276
279545
  const dir = await mkdtemp5(path15.join(tmpdir10(), "vibeframe-hf-"));
279277
279546
  const assetsDir = path15.join(dir, "assets");
279278
- await mkdir20(assetsDir, { recursive: true });
279547
+ await mkdir21(assetsDir, { recursive: true });
279279
279548
  const copied = /* @__PURE__ */ new Map();
279280
279549
  for (const source3 of state.sources) {
279281
279550
  if (copied.has(source3.id)) continue;
@@ -279291,10 +279560,10 @@ async function buildTempProject(state, projectFileDir) {
279291
279560
  await copyLottieRuntime(dir);
279292
279561
  }
279293
279562
  const html = generateCompositionHtml(state);
279294
- await writeFile29(path15.join(dir, "index.html"), html, "utf-8");
279563
+ await writeFile30(path15.join(dir, "index.html"), html, "utf-8");
279295
279564
  return {
279296
279565
  dir,
279297
- cleanup: () => rm8(dir, { recursive: true, force: true })
279566
+ cleanup: () => rm9(dir, { recursive: true, force: true })
279298
279567
  };
279299
279568
  }
279300
279569
  async function copyLottieRuntime(tempDir) {
@@ -279305,7 +279574,7 @@ async function copyLottieRuntime(tempDir) {
279305
279574
  const wasmSrc = path15.join(path15.dirname(webEntry), "dotlottie-player.wasm");
279306
279575
  const vendorDir = path15.join(tempDir, "vendor");
279307
279576
  const wcDest = path15.join(vendorDir, "dotlottie-wc");
279308
- await mkdir20(wcDest, { recursive: true });
279577
+ await mkdir21(wcDest, { recursive: true });
279309
279578
  for (const file of await readdir7(wcDistDir)) {
279310
279579
  if (file.endsWith(".map") || file.endsWith(".d.ts")) continue;
279311
279580
  await copyFile6(path15.join(wcDistDir, file), path15.join(wcDest, file));
@@ -279413,9 +279682,9 @@ __export(motion_overlay_exports, {
279413
279682
  executeMotionOverlay: () => executeMotionOverlay,
279414
279683
  registerMotionOverlayCommand: () => registerMotionOverlayCommand
279415
279684
  });
279416
- import { mkdir as mkdir21 } from "node:fs/promises";
279685
+ import { mkdir as mkdir23 } from "node:fs/promises";
279417
279686
  import { existsSync as existsSync71 } from "node:fs";
279418
- import { basename as basename13, dirname as dirname34, extname as extname15, resolve as resolve50 } from "node:path";
279687
+ import { basename as basename13, dirname as dirname34, extname as extname16, resolve as resolve50 } from "node:path";
279419
279688
  function normalizeUnderstand2(value) {
279420
279689
  return value === "off" || value === "required" || value === "auto" ? value : "auto";
279421
279690
  }
@@ -279438,7 +279707,7 @@ function assertValidRequest(options) {
279438
279707
  return "Provide a motion description, or pass --asset <logo.json|logo.lottie>.";
279439
279708
  }
279440
279709
  if (hasAsset) {
279441
- const ext = extname15(options.asset).toLowerCase();
279710
+ const ext = extname16(options.asset).toLowerCase();
279442
279711
  if (ext !== ".json" && ext !== ".lottie") {
279443
279712
  return "Lottie overlay asset must be a .json or .lottie file.";
279444
279713
  }
@@ -279561,7 +279830,7 @@ async function executeMotionOverlay(options) {
279561
279830
  const scale = Math.max(0.01, Math.min(2, numberOption(options.scale, position === "full" ? 1 : 0.25)));
279562
279831
  const opacity = Math.max(0, Math.min(1, numberOption(options.opacity, 1)));
279563
279832
  const loop = options.loop ?? true;
279564
- await mkdir21(dirname34(outputPath), { recursive: true });
279833
+ await mkdir23(dirname34(outputPath), { recursive: true });
279565
279834
  const backend = createHyperframesBackend();
279566
279835
  const result2 = await backend.render({
279567
279836
  projectState: buildLottieTimelineState({
@@ -279587,7 +279856,7 @@ async function executeMotionOverlay(options) {
279587
279856
  return { success: true, outputPath, provider: "lottie" };
279588
279857
  }
279589
279858
  const duration = options.duration ?? await ffprobeDuration(absVideo).catch(() => void 0);
279590
- await mkdir21(dirname34(outputPath), { recursive: true });
279859
+ await mkdir23(dirname34(outputPath), { recursive: true });
279591
279860
  const result = await executeMotion({
279592
279861
  description: options.description.trim(),
279593
279862
  video: absVideo,
@@ -279716,7 +279985,7 @@ __export(edit_cmd_exports, {
279716
279985
  executeUpscale: () => executeUpscale
279717
279986
  });
279718
279987
  import { resolve as resolve51, dirname as dirname35 } from "node:path";
279719
- import { readFile as readFile28, writeFile as writeFile30, mkdir as mkdir23 } from "node:fs/promises";
279988
+ import { readFile as readFile28, writeFile as writeFile31, mkdir as mkdir24 } from "node:fs/promises";
279720
279989
  async function executeGrade(options) {
279721
279990
  const { videoPath, style, preset: preset3, output: output3, analyzeOnly, apiKey } = options;
279722
279991
  try {
@@ -279909,6 +280178,7 @@ var init_edit_cmd = __esm({
279909
280178
  init_output();
279910
280179
  init_validate();
279911
280180
  init_cost_tier();
280181
+ init_image_file();
279912
280182
  editCommand = new Command("edit").alias("ed").description(
279913
280183
  "Edit and post-process media (silence-cut, caption, grade, reframe, upscale...)"
279914
280184
  ).addHelpText(
@@ -280344,7 +280614,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
280344
280614
  console.log();
280345
280615
  if (options.keyframes) {
280346
280616
  const keyframesPath = resolve51(process.cwd(), options.keyframes);
280347
- await writeFile30(keyframesPath, JSON.stringify(cropKeyframes, null, 2));
280617
+ await writeFile31(keyframesPath, JSON.stringify(cropKeyframes, null, 2));
280348
280618
  console.log(source_default.green(`Keyframes saved to: ${keyframesPath}`));
280349
280619
  }
280350
280620
  if (options.analyzeOnly) {
@@ -280368,7 +280638,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
280368
280638
  exitWithError(generalError(`Reframe failed: ${msg}`));
280369
280639
  }
280370
280640
  });
280371
- editCommand.command("image").description("Edit image(s) using AI (Gemini, OpenAI, or Grok)").argument("<images...>", "Input image file(s) followed by edit prompt").option("-p, --provider <provider>", "Provider: gemini (default), openai, grok", "gemini").option("-k, --api-key <key>", "API key (or set env variable)").option("-o, --output <path>", "Output file path", "edited.png").option("-m, --model <model>", "Model: flash/3.1-flash/latest/pro (Gemini only)", "flash").option("-r, --ratio <ratio>", "Output aspect ratio").option("--size <resolution>", "Resolution: 1K, 2K, 4K (Gemini Pro only)").option("--dry-run", "Preview parameters without executing").action(async (args, options) => {
280641
+ editCommand.command("image").description("Edit image(s) using AI (Gemini, OpenAI, or Grok)").argument("<images...>", "Input image file(s) followed by edit prompt").option("-p, --provider <provider>", "Provider: gemini (default), openai, grok", "gemini").option("-k, --api-key <key>", "API key (or set env variable)").option("-o, --output <path>", "Output file path", "edited.png").option("-m, --model <model>", "Model: flash, lite, pro (Gemini); pro (Grok)", "flash").option("-r, --ratio <ratio>", "Output aspect ratio").option("--size <resolution>", "Resolution: 1K, 2K, 4K (Gemini Pro only)").option("--dry-run", "Preview parameters without executing").action(async (args, options) => {
280372
280642
  const startedAt = Date.now();
280373
280643
  try {
280374
280644
  if (args.length < 2) {
@@ -280427,14 +280697,16 @@ Run 'vibe schema edit.<command>' for structured parameter info.
280427
280697
  const grok = new GrokProvider();
280428
280698
  await grok.initialize({ apiKey });
280429
280699
  result = await grok.editImage(imageBuffers[0], prompt3, {
280700
+ ...resolveGrokImageModel(options.model),
280430
280701
  aspectRatio: options.ratio
280431
280702
  });
280432
280703
  } else {
280433
280704
  const editModelNames = {
280434
- flash: "gemini-2.5-flash-image",
280435
- "3.1-flash": "gemini-3.1-flash-image-preview",
280436
- latest: "gemini-3.1-flash-image-preview",
280437
- pro: "gemini-3-pro-image-preview"
280705
+ flash: "gemini-3.1-flash-image",
280706
+ "3.1-flash": "gemini-3.1-flash-image",
280707
+ latest: "gemini-3.1-flash-image",
280708
+ lite: "gemini-3.1-flash-lite-image",
280709
+ pro: "gemini-3-pro-image"
280438
280710
  };
280439
280711
  const editModelName = editModelNames[options.model] || editModelNames.flash;
280440
280712
  spinner2.text = `Editing with ${editModelName}...`;
@@ -280445,15 +280717,6 @@ Run 'vibe schema edit.<command>' for structured parameter info.
280445
280717
  aspectRatio: options.ratio,
280446
280718
  resolution: options.size
280447
280719
  });
280448
- const fallbackModels = ["latest", "3.1-flash"];
280449
- if (!result.success && fallbackModels.includes(options.model)) {
280450
- spinner2.text = `${source_default.dim(result.error || `${editModelName} failed`)} \u2014 retrying with flash...`;
280451
- result = await gemini.editImage(imageBuffers, prompt3, {
280452
- model: "flash",
280453
- aspectRatio: options.ratio,
280454
- resolution: options.size
280455
- });
280456
- }
280457
280720
  }
280458
280721
  if (!result.success || !result.images || result.images.length === 0) {
280459
280722
  spinner2.fail(result.error || "Image editing failed");
@@ -280463,14 +280726,14 @@ Run 'vibe schema edit.<command>' for structured parameter info.
280463
280726
  const img = result.images[0];
280464
280727
  const outputPath = resolve51(process.cwd(), options.output);
280465
280728
  const saveImage = async () => {
280466
- await mkdir23(dirname35(outputPath), { recursive: true });
280729
+ await mkdir24(dirname35(outputPath), { recursive: true });
280467
280730
  if (img.base64) {
280468
280731
  const buffer = Buffer.from(img.base64, "base64");
280469
- await writeFile30(outputPath, buffer);
280732
+ await writeImageFile(outputPath, buffer);
280470
280733
  } else if (img.url) {
280471
280734
  const resp = await fetch(img.url);
280472
280735
  const arrayBuf = await resp.arrayBuffer();
280473
- await writeFile30(outputPath, Buffer.from(arrayBuf));
280736
+ await writeImageFile(outputPath, Buffer.from(arrayBuf));
280474
280737
  }
280475
280738
  };
280476
280739
  const resultModel = result.model;
@@ -280690,7 +280953,7 @@ __export(ai_image_exports, {
280690
280953
  executeThumbnailBestFrame: () => executeThumbnailBestFrame
280691
280954
  });
280692
280955
  import { resolve as resolve54, dirname as dirname37 } from "node:path";
280693
- import { readFile as readFile29, writeFile as writeFile33, mkdir as mkdir25 } from "node:fs/promises";
280956
+ import { readFile as readFile29, mkdir as mkdir26 } from "node:fs/promises";
280694
280957
  import { existsSync as existsSync74 } from "node:fs";
280695
280958
  async function executeImageGenerate(options) {
280696
280959
  const {
@@ -280733,8 +280996,8 @@ async function executeImageGenerate(options) {
280733
280996
  return { success: false, error: "No image data available" };
280734
280997
  }
280735
280998
  outputPath = resolve54(process.cwd(), output3);
280736
- await mkdir25(dirname37(outputPath), { recursive: true });
280737
- await writeFile33(outputPath, buffer);
280999
+ await mkdir26(dirname37(outputPath), { recursive: true });
281000
+ await writeImageFile(outputPath, buffer);
280738
281001
  }
280739
281002
  return {
280740
281003
  success: true,
@@ -280768,8 +281031,8 @@ async function executeImageGenerate(options) {
280768
281031
  const img = result.images[0];
280769
281032
  if (img.base64) {
280770
281033
  outputPath = resolve54(process.cwd(), output3);
280771
- await mkdir25(dirname37(outputPath), { recursive: true });
280772
- await writeFile33(outputPath, Buffer.from(img.base64, "base64"));
281034
+ await mkdir26(dirname37(outputPath), { recursive: true });
281035
+ await writeImageFile(outputPath, Buffer.from(img.base64, "base64"));
280773
281036
  }
280774
281037
  }
280775
281038
  return {
@@ -280782,10 +281045,11 @@ async function executeImageGenerate(options) {
280782
281045
  } else if (provider === "grok") {
280783
281046
  const key2 = apiKey || process.env.XAI_API_KEY;
280784
281047
  if (!key2) return { success: false, error: "XAI_API_KEY required" };
280785
- const openaiImage = new OpenAIImageProvider();
280786
- await openaiImage.initialize({ apiKey: key2, baseUrl: "https://api.x.ai/v1" });
280787
- const result = await openaiImage.generateImage(prompt3, {
280788
- size,
281048
+ const grok = new GrokProvider();
281049
+ await grok.initialize({ apiKey: key2 });
281050
+ const result = await grok.generateImage(prompt3, {
281051
+ ...resolveGrokImageModel(model),
281052
+ aspectRatio: ratio,
280789
281053
  n: count
280790
281054
  });
280791
281055
  if (!result.success || !result.images) {
@@ -280804,8 +281068,8 @@ async function executeImageGenerate(options) {
280804
281068
  return { success: false, error: "No image data available" };
280805
281069
  }
280806
281070
  outputPath = resolve54(process.cwd(), output3);
280807
- await mkdir25(dirname37(outputPath), { recursive: true });
280808
- await writeFile33(outputPath, buffer);
281071
+ await mkdir26(dirname37(outputPath), { recursive: true });
281072
+ await writeImageFile(outputPath, buffer);
280809
281073
  }
280810
281074
  return {
280811
281075
  success: true,
@@ -280863,8 +281127,8 @@ async function executeGeminiEdit(options) {
280863
281127
  let outputPath;
280864
281128
  if (img.base64) {
280865
281129
  outputPath = resolve54(process.cwd(), output3);
280866
- await mkdir25(dirname37(outputPath), { recursive: true });
280867
- await writeFile33(outputPath, Buffer.from(img.base64, "base64"));
281130
+ await mkdir26(dirname37(outputPath), { recursive: true });
281131
+ await writeImageFile(outputPath, Buffer.from(img.base64, "base64"));
280868
281132
  }
280869
281133
  return {
280870
281134
  success: true,
@@ -280938,6 +281202,7 @@ var init_ai_image = __esm({
280938
281202
  "use strict";
280939
281203
  init_dist();
280940
281204
  init_exec_safe();
281205
+ init_image_file();
280941
281206
  }
280942
281207
  });
280943
281208
 
@@ -280949,7 +281214,7 @@ __export(ai_analyze_exports, {
280949
281214
  registerAnalyzeCommands: () => registerAnalyzeCommands
280950
281215
  });
280951
281216
  import { readFile as readFile30 } from "node:fs/promises";
280952
- import { extname as extname16, resolve as resolve55 } from "node:path";
281217
+ import { extname as extname17, resolve as resolve55 } from "node:path";
280953
281218
  import { existsSync as existsSync75 } from "node:fs";
280954
281219
  async function executeGeminiVideo(options) {
280955
281220
  try {
@@ -281006,7 +281271,7 @@ async function executeAnalyze(options) {
281006
281271
  const isYouTube = source3.includes("youtube.com") || source3.includes("youtu.be");
281007
281272
  const isImageUrl = /^https?:\/\/.+\.(png|jpg|jpeg|webp|gif)(\?.*)?$/i.test(source3);
281008
281273
  const isVideoUrl = /^https?:\/\/.+\.(mp4|mov|webm)(\?.*)?$/i.test(source3);
281009
- const ext = extname16(source3).toLowerCase();
281274
+ const ext = extname17(source3).toLowerCase();
281010
281275
  const imageExts = [".png", ".jpg", ".jpeg", ".webp", ".gif"];
281011
281276
  const videoExts = [".mp4", ".mov", ".webm", ".avi", ".mkv"];
281012
281277
  const isLocalImage = imageExts.includes(ext);
@@ -281414,7 +281679,7 @@ async function executeSoundEffect(options) {
281414
281679
  }
281415
281680
  }
281416
281681
  function registerSoundEffectCommand(parent) {
281417
- parent.command("sound-effect").description("Generate sound effect using ElevenLabs").argument("<prompt>", "Description of the sound effect").option("-k, --api-key <key>", "ElevenLabs API key (or set ELEVENLABS_API_KEY env)").option("-o, --output <path>", "Output audio file path", "sound-effect.mp3").option("-d, --duration <seconds>", "Duration in seconds (0.5-22, default: auto)").option("--prompt-influence <value>", "Prompt influence (0-1, default: 0.3)").option("--dry-run", "Preview parameters without executing").action(async (prompt3, options) => {
281682
+ parent.command("sound-effect").description("Generate sound effect using ElevenLabs").argument("<prompt>", "Description of the sound effect").option("-k, --api-key <key>", "ElevenLabs API key (or set ELEVENLABS_API_KEY env)").option("-o, --output <path>", "Output audio file path", "sound-effect.mp3").option("-d, --duration <seconds>", "Duration in seconds (0.5-30, default: auto)").option("--prompt-influence <value>", "Prompt influence (0-1, default: 0.3)").option("--dry-run", "Preview parameters without executing").action(async (prompt3, options) => {
281418
281683
  const startedAt = Date.now();
281419
281684
  try {
281420
281685
  rejectControlChars(prompt3);
@@ -281714,9 +281979,9 @@ var init_speech = __esm({
281714
281979
  });
281715
281980
 
281716
281981
  // ../cli/src/commands/generate/thumbnail.ts
281717
- import { resolve as resolve64, dirname as dirname38, basename as basename17, extname as extname17 } from "node:path";
281982
+ import { resolve as resolve64, dirname as dirname38, basename as basename17, extname as extname18 } from "node:path";
281718
281983
  import { existsSync as existsSync79 } from "node:fs";
281719
- import { writeFile as writeFile37, mkdir as mkdir26 } from "node:fs/promises";
281984
+ import { writeFile as writeFile37, mkdir as mkdir27 } from "node:fs/promises";
281720
281985
  function registerThumbnailCommand(parent) {
281721
281986
  parent.command("thumbnail").description("Generate video thumbnail (DALL-E) or extract best frame from video (Gemini)").argument("[description]", "Thumbnail description (for DALL-E generation)").option("-k, --api-key <key>", "API key (OpenAI for generation, Google for best-frame)").option("-o, --output <path>", "Output file path").option("--style <style>", "Platform style: youtube, instagram, tiktok, twitter").option("--best-frame <video>", "Extract best thumbnail frame from video using Gemini AI").option("--prompt <prompt>", "Custom prompt for best-frame analysis").option("--model <model>", `Gemini model: ${GEMINI_TEXT_MODEL_HELP}`, "flash").action(async (description, options) => {
281722
281987
  const startedAt = Date.now();
@@ -281739,7 +282004,7 @@ function registerThumbnailCommand(parent) {
281739
282004
  );
281740
282005
  }
281741
282006
  const apiKey2 = await requireApiKey("GOOGLE_API_KEY", "Google", options.apiKey);
281742
- const name = basename17(options.bestFrame, extname17(options.bestFrame));
282007
+ const name = basename17(options.bestFrame, extname18(options.bestFrame));
281743
282008
  const outputPath = options.output || `${name}-thumbnail.png`;
281744
282009
  const spinner3 = ora("Analyzing video for best frame...").start();
281745
282010
  const result2 = await executeThumbnailBestFrame({
@@ -281807,7 +282072,7 @@ function registerThumbnailCommand(parent) {
281807
282072
  throw new Error("No image data available");
281808
282073
  }
281809
282074
  outputPath = resolve64(process.cwd(), options.output);
281810
- await mkdir26(dirname38(outputPath), { recursive: true });
282075
+ await mkdir27(dirname38(outputPath), { recursive: true });
281811
282076
  await writeFile37(outputPath, buffer);
281812
282077
  }
281813
282078
  outputSuccess({
@@ -281838,7 +282103,7 @@ function registerThumbnailCommand(parent) {
281838
282103
  throw new Error("No image data available");
281839
282104
  }
281840
282105
  const outputPath = resolve64(process.cwd(), options.output);
281841
- await mkdir26(dirname38(outputPath), { recursive: true });
282106
+ await mkdir27(dirname38(outputPath), { recursive: true });
281842
282107
  await writeFile37(outputPath, buffer);
281843
282108
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
281844
282109
  } catch {
@@ -281869,7 +282134,7 @@ var init_thumbnail = __esm({
281869
282134
  import { resolve as resolve65 } from "node:path";
281870
282135
  import { writeFile as writeFile38 } from "node:fs/promises";
281871
282136
  function registerVideoExtendCommand(parent) {
281872
- parent.command("video-extend", { hidden: true }).description("Extend video duration (Kling by video ID, Veo by operation name)").argument("<id>", "Kling video ID or Veo operation name").option("-p, --provider <provider>", "Provider: kling, veo", "kling").option("-k, --api-key <key>", "API key (KLING_API_KEY or GOOGLE_API_KEY)").option("-o, --output <path>", "Output file path").option("--prompt <text>", "Continuation prompt").option("-d, --duration <sec>", "Duration: 5 or 10 (Kling), 4/6/8 (Veo)", "5").option("--negative <prompt>", "Negative prompt (what to avoid, Kling only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast", "3.1").option("--no-wait", "Start extension and return task ID without waiting").option("--dry-run", "Preview parameters without executing").action(async (id, options) => {
282137
+ parent.command("video-extend", { hidden: true }).description("Extend video duration (Kling by video ID, Veo by operation name)").argument("<id>", "Kling video ID or Veo operation name").option("-p, --provider <provider>", "Provider: kling, veo", "kling").option("-k, --api-key <key>", "API key (KLING_API_KEY or GOOGLE_API_KEY)").option("-o, --output <path>", "Output file path").option("--prompt <text>", "Continuation prompt").option("-d, --duration <sec>", "Duration: 5 or 10 (Kling), 4/6/8 (Veo)", "5").option("--negative <prompt>", "Negative prompt (what to avoid, Kling only)").option("--veo-model <model>", "Veo model: 3.1, 3.1-fast", "3.1").option("--no-wait", "Start extension and return task ID without waiting").option("--dry-run", "Preview parameters without executing").action(async (id, options) => {
281873
282138
  const startedAt = Date.now();
281874
282139
  try {
281875
282140
  const provider = (options.provider || "kling").toLowerCase();
@@ -281987,12 +282252,7 @@ function registerVideoExtendCommand(parent) {
281987
282252
  const spinner2 = ora("Initializing Veo...").start();
281988
282253
  const gemini = new GeminiProvider();
281989
282254
  await gemini.initialize({ apiKey });
281990
- const veoModelMap = {
281991
- "3.0": "veo-3.0-generate-preview",
281992
- "3.1": "veo-3.1-generate-preview",
281993
- "3.1-fast": "veo-3.1-fast-generate-preview"
281994
- };
281995
- const veoModel = veoModelMap[options.veoModel] || "veo-3.1-generate-preview";
282255
+ const veoModel = resolveVeoModel(options.veoModel);
281996
282256
  spinner2.text = "Starting video extension...";
281997
282257
  const result = await gemini.extendVideo(id, options.prompt, {
281998
282258
  duration: parseInt(options.duration),
@@ -282153,11 +282413,8 @@ var init_provider_resolver = __esm({
282153
282413
 
282154
282414
  // ../cli/src/commands/_shared/openai-image.ts
282155
282415
  function resolveOpenAIImageModel(modelAlias) {
282156
- const isGptImage15 = modelAlias === "1.5" || modelAlias === "gpt-image-1.5";
282157
- return {
282158
- openaiModel: isGptImage15 ? "gpt-image-1.5" : "gpt-image-2",
282159
- modelLabel: isGptImage15 ? "GPT Image 1.5" : "GPT Image 2"
282160
- };
282416
+ const model = findModel("openai", "image", modelAlias) ?? defaultModel("openai", "image");
282417
+ return { openaiModel: model.id, modelLabel: model.label };
282161
282418
  }
282162
282419
  async function executeOpenAIImageGenerate(prompt3, options, ctx) {
282163
282420
  const provider = new OpenAIImageProvider();
@@ -282179,10 +282436,44 @@ var init_openai_image2 = __esm({
282179
282436
  }
282180
282437
  });
282181
282438
 
282439
+ // ../cli/src/commands/_shared/image-cost.ts
282440
+ function catalogPrice(provider, model) {
282441
+ if (provider === "openai") return findModel("openai", "image", resolveOpenAIImageModel(model).openaiModel)?.price?.usd;
282442
+ if (provider === "gemini") return findModel("gemini", "image", model ?? "flash")?.price?.usd ?? findModel("gemini", "image")?.price?.usd;
282443
+ if (provider === "grok") return findModel("grok", "image", resolveGrokImageModel(model).model)?.price?.usd;
282444
+ return void 0;
282445
+ }
282446
+ function estimateImageCostUsd(provider, model, count) {
282447
+ const images = Math.max(1, count);
282448
+ const perImage = catalogPrice(provider, model);
282449
+ if (perImage === void 0) {
282450
+ return {
282451
+ costUsd: lookupCostEstimateUpperBound("generate image") ?? 5,
282452
+ warnings: [
282453
+ `costUsd is the tier upper bound for ${provider} - actual provider billing is typically lower and is not metered here.`
282454
+ ]
282455
+ };
282456
+ }
282457
+ return {
282458
+ costUsd: Number((perImage * images).toFixed(3)),
282459
+ warnings: [
282460
+ `costUsd is an upper-bound estimate from ${provider} list prices for ${images} image(s) - not metered billing.`
282461
+ ]
282462
+ };
282463
+ }
282464
+ var init_image_cost = __esm({
282465
+ "../cli/src/commands/_shared/image-cost.ts"() {
282466
+ "use strict";
282467
+ init_dist();
282468
+ init_output();
282469
+ init_openai_image2();
282470
+ }
282471
+ });
282472
+
282182
282473
  // ../cli/src/commands/generate/image.ts
282183
282474
  import { resolve as resolve66, dirname as dirname39 } from "node:path";
282184
282475
  import { fileURLToPath as fileURLToPath8 } from "node:url";
282185
- import { writeFile as writeFile39, mkdir as mkdir27 } from "node:fs/promises";
282476
+ import { mkdir as mkdir28 } from "node:fs/promises";
282186
282477
  function registerImageCommand(parent) {
282187
282478
  parent.command("image").alias("img").description("Generate image using AI (Gemini, OpenAI gpt-image, Grok, or Runway)").argument("[prompt]", "Image description prompt (interactive if omitted)").option(
282188
282479
  "-p, --provider <provider>",
@@ -282193,7 +282484,7 @@ function registerImageCommand(parent) {
282193
282484
  "1:1"
282194
282485
  ).option("--quality <quality>", "Quality: standard, hd (openai only)", "standard").option("--style <style>", "Style: vivid, natural (openai only)", "vivid").option("--count <n>", "Number of images to generate", "1").option(
282195
282486
  "-m, --model <model>",
282196
- "Model. Gemini: flash, 3.1-flash, latest, pro. OpenAI: 2 (default), 1.5"
282487
+ "Model. Gemini: flash (default), lite, pro. OpenAI: 2.5 (default), flare, 2, 1.5. Grok: pro"
282197
282488
  ).option("--dry-run", "Preview parameters without executing").addHelpText(
282198
282489
  "after",
282199
282490
  `
@@ -282276,6 +282567,7 @@ Examples:
282276
282567
  command: "generate image",
282277
282568
  startedAt,
282278
282569
  dryRun: true,
282570
+ ...estimateImageCostUsd(provider, options.model, parseInt(options.count) || 1),
282279
282571
  data: {
282280
282572
  params: {
282281
282573
  prompt: prompt3,
@@ -282296,7 +282588,7 @@ Examples:
282296
282588
  const apiKey = await requireApiKey(envKey, providerName, options.apiKey);
282297
282589
  const spinner2 = ora(`Generating image with ${providerName}...`).start();
282298
282590
  if (provider === "openai") {
282299
- const { result, modelLabel } = await executeOpenAIImageGenerate(prompt3, options, {
282591
+ const { result, modelLabel, openaiModel } = await executeOpenAIImageGenerate(prompt3, options, {
282300
282592
  apiKey
282301
282593
  });
282302
282594
  if (!result.success || !result.images) {
@@ -282319,14 +282611,17 @@ Examples:
282319
282611
  } else {
282320
282612
  throw new Error("No image data available");
282321
282613
  }
282322
- await mkdir27(dirname39(outputPath), { recursive: true });
282323
- await writeFile39(outputPath, buffer);
282614
+ await mkdir28(dirname39(outputPath), { recursive: true });
282615
+ await writeImageFile(outputPath, buffer);
282324
282616
  }
282325
282617
  outputSuccess({
282326
282618
  command: "generate image",
282327
282619
  startedAt,
282620
+ ...estimateImageCostUsd("openai", options.model, result.images.length),
282328
282621
  data: {
282329
282622
  provider: "openai",
282623
+ model: modelLabel,
282624
+ modelId: openaiModel,
282330
282625
  images: result.images.map((img) => ({
282331
282626
  url: img.url,
282332
282627
  revisedPrompt: img.revisedPrompt
@@ -282366,8 +282661,8 @@ Examples:
282366
282661
  throw new Error("No image data available");
282367
282662
  }
282368
282663
  const outputPath = resolve66(process.cwd(), options.output);
282369
- await mkdir27(dirname39(outputPath), { recursive: true });
282370
- await writeFile39(outputPath, buffer);
282664
+ await mkdir28(dirname39(outputPath), { recursive: true });
282665
+ await writeImageFile(outputPath, buffer);
282371
282666
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
282372
282667
  } catch (err) {
282373
282668
  saveSpinner.fail(
@@ -282376,7 +282671,7 @@ Examples:
282376
282671
  }
282377
282672
  }
282378
282673
  } else if (provider === "gemini") {
282379
- const validGeminiModels = ["flash", "3.1-flash", "latest", "pro"];
282674
+ const validGeminiModels = ["flash", "3.1-flash", "latest", "lite", "pro"];
282380
282675
  if (options.model && !validGeminiModels.includes(options.model)) {
282381
282676
  console.warn(
282382
282677
  source_default.yellow(
@@ -282409,26 +282704,17 @@ Examples:
282409
282704
  const gemini = new GeminiProvider();
282410
282705
  await gemini.initialize({ apiKey });
282411
282706
  const geminiModelNames = {
282412
- flash: "Nano Banana",
282707
+ flash: "Nano Banana 2",
282413
282708
  "3.1-flash": "Nano Banana 2",
282414
282709
  latest: "Nano Banana 2",
282710
+ lite: "Nano Banana 2 Lite",
282415
282711
  pro: "Nano Banana Pro"
282416
282712
  };
282417
- const modelLabel = geminiModelNames[options.model] || "Nano Banana";
282418
- let result = await gemini.generateImage(prompt3, {
282713
+ const usedLabel = geminiModelNames[options.model] || "Nano Banana 2";
282714
+ const result = await gemini.generateImage(prompt3, {
282419
282715
  model: options.model,
282420
282716
  aspectRatio: options.ratio
282421
282717
  });
282422
- let usedLabel = modelLabel;
282423
- const fallbackModels = ["latest", "3.1-flash"];
282424
- if (!result.success && options.model && fallbackModels.includes(options.model)) {
282425
- spinner2.text = `${source_default.dim(result.error || "Failed")} \u2014 retrying with Nano Banana (flash)...`;
282426
- result = await gemini.generateImage(prompt3, {
282427
- model: "flash",
282428
- aspectRatio: options.ratio
282429
- });
282430
- usedLabel = "Nano Banana (fallback)";
282431
- }
282432
282718
  if (!result.success || !result.images) {
282433
282719
  spinner2.fail(result.error || "Image generation failed");
282434
282720
  exitWithError(apiError(result.error || "Image generation failed", true));
@@ -282441,16 +282727,17 @@ Examples:
282441
282727
  if (outputPath && result.images.length > 0) {
282442
282728
  const img = result.images[0];
282443
282729
  const buffer = Buffer.from(img.base64, "base64");
282444
- await mkdir27(dirname39(outputPath), { recursive: true });
282445
- await writeFile39(outputPath, buffer);
282730
+ await mkdir28(dirname39(outputPath), { recursive: true });
282731
+ await writeImageFile(outputPath, buffer);
282446
282732
  }
282447
282733
  outputSuccess({
282448
282734
  command: "generate image",
282449
282735
  startedAt,
282450
- warnings: usedLabel.includes("fallback") ? [`Model "${options.model}" failed; fell back to flash`] : [],
282736
+ ...estimateImageCostUsd("gemini", options.model, result.images.length),
282451
282737
  data: {
282452
282738
  provider: "gemini",
282453
282739
  model: usedLabel,
282740
+ modelId: result.model,
282454
282741
  images: result.images.map((img) => ({
282455
282742
  mimeType: img.mimeType
282456
282743
  })),
@@ -282474,8 +282761,8 @@ Examples:
282474
282761
  const img = result.images[0];
282475
282762
  const buffer = Buffer.from(img.base64, "base64");
282476
282763
  const outputPath = resolve66(process.cwd(), options.output);
282477
- await mkdir27(dirname39(outputPath), { recursive: true });
282478
- await writeFile39(outputPath, buffer);
282764
+ await mkdir28(dirname39(outputPath), { recursive: true });
282765
+ await writeImageFile(outputPath, buffer);
282479
282766
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
282480
282767
  } catch (err) {
282481
282768
  saveSpinner.fail(
@@ -282513,6 +282800,7 @@ Examples:
282513
282800
  options.ratio = "1:1";
282514
282801
  }
282515
282802
  const result = await grok.generateImage(prompt3, {
282803
+ ...resolveGrokImageModel(options.model),
282516
282804
  aspectRatio: options.ratio || "1:1",
282517
282805
  n: parseInt(options.count)
282518
282806
  });
@@ -282534,14 +282822,16 @@ Examples:
282534
282822
  } else {
282535
282823
  throw new Error("No image data available");
282536
282824
  }
282537
- await mkdir27(dirname39(outputPath), { recursive: true });
282538
- await writeFile39(outputPath, buffer);
282825
+ await mkdir28(dirname39(outputPath), { recursive: true });
282826
+ await writeImageFile(outputPath, buffer);
282539
282827
  }
282540
282828
  outputSuccess({
282541
282829
  command: "generate image",
282542
282830
  startedAt,
282831
+ ...estimateImageCostUsd("grok", options.model, result.images.length),
282543
282832
  data: {
282544
282833
  provider: "grok",
282834
+ modelId: resolveGrokImageModel(options.model).model,
282545
282835
  images: result.images.map((img) => ({ url: img.url })),
282546
282836
  outputPath
282547
282837
  }
@@ -282575,8 +282865,8 @@ Examples:
282575
282865
  throw new Error("No image data available");
282576
282866
  }
282577
282867
  const outputPath = resolve66(process.cwd(), options.output);
282578
- await mkdir27(dirname39(outputPath), { recursive: true });
282579
- await writeFile39(outputPath, buffer);
282868
+ await mkdir28(dirname39(outputPath), { recursive: true });
282869
+ await writeImageFile(outputPath, buffer);
282580
282870
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
282581
282871
  } catch (err) {
282582
282872
  saveSpinner.fail(
@@ -282658,6 +282948,8 @@ var init_image2 = __esm({
282658
282948
  init_validate();
282659
282949
  init_provider_resolver();
282660
282950
  init_openai_image2();
282951
+ init_image_cost();
282952
+ init_image_file();
282661
282953
  }
282662
282954
  });
282663
282955
 
@@ -283606,11 +283898,11 @@ var init_dist5 = __esm({
283606
283898
 
283607
283899
  // ../cli/src/commands/generate/video.ts
283608
283900
  import { resolve as resolve67 } from "node:path";
283609
- import { readFile as readFile36, writeFile as writeFile40 } from "node:fs/promises";
283901
+ import { readFile as readFile36, writeFile as writeFile39 } from "node:fs/promises";
283610
283902
  function registerVideoCommand(parent) {
283611
283903
  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(
283612
283904
  "-p, --provider <provider>",
283613
- "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."
283905
+ "Provider: seedance (ByteDance Seedance 2.0 via fal.ai), grok, kling, runway, omni (Gemini Omni 1.1 Flash; Google default), veo (Veo 3.1 preview, shuts down 2026-10-22). `fal` is a deprecated v0.x alias for seedance and will be removed in 1.0."
283614
283906
  ).option(
283615
283907
  "-k, --api-key <key>",
283616
283908
  "API key (or set FAL_API_KEY / XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY / GOOGLE_API_KEY env)"
@@ -283623,12 +283915,12 @@ function registerVideoCommand(parent) {
283623
283915
  "Aspect ratio: 16:9, 9:16, or 1:1 (auto-detected from image if omitted)"
283624
283916
  ).option("--seed <number>", "Random seed for reproducibility (Runway only)").option("--mode <mode>", "Generation mode: std or pro (Kling only)", "std").option(
283625
283917
  "--seedance-model <model>",
283626
- "Seedance variant: quality or fast (fal.ai only)",
283918
+ "Seedance variant: quality (2.0), fast, or 2.5 (fal.ai only)",
283627
283919
  "quality"
283628
- ).option("--negative <prompt>", "Negative prompt - what to avoid (Kling/Veo)").option("--resolution <res>", "Video resolution: 480p, 720p, 1080p, or 4k depending on provider").option("--last-frame <path>", "Last frame image for frame interpolation (Veo) or Seedance end frame").option("--end-image <path>", "Ending frame image for Seedance image-to-video").option(
283920
+ ).option("--grok-model <model>", "Grok video model: 1.5 (default), lite, classic", "1.5").option("--kling-model <model>", "Kling model: v3 (default), v2.6, v2.5-turbo", "v3").option("--negative <prompt>", "Negative prompt - what to avoid (Kling/Veo)").option("--resolution <res>", "Video resolution: 480p, 720p, 1080p, or 4k depending on provider").option("--last-frame <path>", "Last frame image for frame interpolation (Veo) or Seedance end frame").option("--end-image <path>", "Ending frame image for Seedance image-to-video").option(
283629
283921
  "--ref-images <paths...>",
283630
283922
  "Reference images for Seedance reference-to-video or Veo character consistency"
283631
- ).option("--ref-videos <paths...>", "Reference videos for Seedance reference-to-video").option("--ref-audio <paths...>", "Reference audio for Seedance reference-to-video").option("--no-generate-audio", "Disable native audio when the provider supports it").option("--person <mode>", "Person generation: allow_all, allow_adult (Veo only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast (default: 3.1-fast)", "3.1-fast").option(
283923
+ ).option("--ref-videos <paths...>", "Reference videos for Seedance reference-to-video").option("--ref-audio <paths...>", "Reference audio for Seedance reference-to-video").option("--no-generate-audio", "Disable native audio when the provider supports it").option("--person <mode>", "Person generation: allow_all, allow_adult (Veo only)").option("--veo-model <model>", "Veo model: 3.1, 3.1-fast (default: 3.1-fast)", "3.1-fast").option(
283632
283924
  "--runway-model <model>",
283633
283925
  "Runway model: gen4.5 (default, text+image-to-video), gen4_turbo (image-to-video only)",
283634
283926
  "gen4.5"
@@ -283689,7 +283981,7 @@ Examples:
283689
283981
  exitWithError(
283690
283982
  usageError(
283691
283983
  `Invalid provider: ${provider}`,
283692
- "Available providers: seedance, grok, kling, runway, veo, omni (experimental). `fal` is a deprecated alias for seedance."
283984
+ "Available providers: seedance, grok, kling, runway, omni, veo. `fal` is a deprecated alias for seedance."
283693
283985
  )
283694
283986
  );
283695
283987
  }
@@ -283756,14 +284048,16 @@ Examples:
283756
284048
  log(`${provider} does not support 1:1 \u2014 falling back to 16:9`);
283757
284049
  options.ratio = "16:9";
283758
284050
  }
284051
+ try {
284052
+ if (provider === "seedance" || provider === "fal") resolveSeedanceVariant(options.seedanceModel);
284053
+ if (provider === "grok") resolveGrokVideoModel(options.grokModel);
284054
+ if (provider === "kling") resolveKlingModel(options.klingModel);
284055
+ if (provider === "veo") resolveVeoModel(options.veoModel);
284056
+ } catch (err) {
284057
+ exitWithError(usageError(err instanceof Error ? err.message : String(err)));
284058
+ }
283759
284059
  if (options.dryRun) {
283760
- const costUsd = provider === "seedance" || provider === "fal" ? estimateSeedanceVideoCostUsd({
283761
- durationSec: Number(options.duration) || 5,
283762
- resolution: options.resolution,
283763
- aspectRatio: options.ratio,
283764
- fast: options.seedanceModel === "fast",
283765
- hasVideoReference: Boolean(options.refVideos)
283766
- }) : void 0;
284060
+ const costUsd = provider === "seedance" || provider === "fal" ? realRunCost(provider, options).costUsd : void 0;
283767
284061
  outputSuccess({
283768
284062
  command: "generate video",
283769
284063
  startedAt,
@@ -283781,6 +284075,8 @@ Examples:
283781
284075
  resolution: options.resolution,
283782
284076
  veoModel: options.veoModel,
283783
284077
  seedanceModel: options.seedanceModel,
284078
+ grokModel: options.grokModel,
284079
+ klingModel: options.klingModel,
283784
284080
  refImages: options.refImages,
283785
284081
  refVideos: options.refVideos,
283786
284082
  refAudio: options.refAudio,
@@ -283802,7 +284098,7 @@ Examples:
283802
284098
  runway: "Runway",
283803
284099
  kling: "Kling",
283804
284100
  veo: "Veo",
283805
- omni: "Gemini Omni (experimental)",
284101
+ omni: "Gemini Omni 1.1 Flash",
283806
284102
  grok: "Grok",
283807
284103
  seedance: "Seedance 2.0 via fal.ai"
283808
284104
  };
@@ -283901,6 +284197,7 @@ Examples:
283901
284197
  }
283902
284198
  result = await kling.generateVideo(prompt3, {
283903
284199
  prompt: prompt3,
284200
+ model: resolveKlingModel(options.klingModel),
283904
284201
  referenceImage: klingImage,
283905
284202
  duration: parseInt(options.duration),
283906
284203
  aspectRatio: options.ratio,
@@ -283950,12 +284247,7 @@ Examples:
283950
284247
  } else if (provider === "veo") {
283951
284248
  const gemini = new GeminiProvider();
283952
284249
  await gemini.initialize({ apiKey });
283953
- const veoModelMap = {
283954
- "3.0": "veo-3.0-generate-preview",
283955
- "3.1": "veo-3.1-generate-preview",
283956
- "3.1-fast": "veo-3.1-fast-generate-preview"
283957
- };
283958
- const veoModel = veoModelMap[options.veoModel] || "veo-3.1-fast-generate-preview";
284250
+ const veoModel = resolveVeoModel(options.veoModel);
283959
284251
  const veoDuration = parseInt(options.duration) <= 6 ? 6 : 8;
283960
284252
  let lastFrame;
283961
284253
  if (options.lastFrame) {
@@ -284029,9 +284321,12 @@ Examples:
284029
284321
  await grok.initialize({ apiKey });
284030
284322
  result = await grok.generateVideo(prompt3, {
284031
284323
  prompt: prompt3,
284324
+ model: resolveGrokVideoModel(options.grokModel),
284032
284325
  referenceImage,
284033
284326
  duration: parseInt(options.duration),
284034
- aspectRatio: options.ratio
284327
+ aspectRatio: options.ratio,
284328
+ resolution: options.resolution,
284329
+ generateAudio: options.generateAudio
284035
284330
  });
284036
284331
  if (result.status === "failed") {
284037
284332
  spinner2.fail(result.error || "Failed to start generation");
@@ -284121,9 +284416,8 @@ Examples:
284121
284416
  exitWithError(apiError(message, true));
284122
284417
  }
284123
284418
  }
284124
- spinner2.text = "Generating video with fal.ai Seedance 2.0 (this may take 1-3 minutes)...";
284125
- const seedanceModel = String(options.seedanceModel ?? "quality").toLowerCase();
284126
- const falModel = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
284419
+ const falModel = resolveSeedanceVariant(options.seedanceModel);
284420
+ spinner2.text = `Generating video with fal.ai ${falModel} (this may take 1-3 minutes)...`;
284127
284421
  result = await fal.generateVideo(prompt3, {
284128
284422
  prompt: prompt3,
284129
284423
  referenceImage: seedanceReferences.length > 0 ? void 0 : falImage,
@@ -284140,11 +284434,20 @@ Examples:
284140
284434
  } else if (provider === "omni") {
284141
284435
  const omni = new OmniProvider();
284142
284436
  await omni.initialize({ apiKey });
284143
- spinner2.text = "Generating video with Gemini Omni (experimental)...";
284437
+ spinner2.text = "Generating video with Gemini Omni 1.1 Flash...";
284438
+ let lastFrame;
284439
+ if (options.lastFrame) {
284440
+ const lastFramePath = resolve67(process.cwd(), options.lastFrame);
284441
+ const ext = options.lastFrame.toLowerCase().split(".").pop();
284442
+ const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
284443
+ lastFrame = `data:${mimeType};base64,${(await readFile36(lastFramePath)).toString("base64")}`;
284444
+ }
284144
284445
  result = await omni.generateVideo(prompt3, {
284145
284446
  prompt: prompt3,
284146
284447
  referenceImage,
284147
- aspectRatio: options.ratio
284448
+ lastFrame,
284449
+ aspectRatio: options.ratio,
284450
+ resolution: options.resolution
284148
284451
  });
284149
284452
  finalResult = result;
284150
284453
  }
@@ -284158,14 +284461,14 @@ Examples:
284158
284461
  if (options.output && finalResult.videoUrl) {
284159
284462
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
284160
284463
  outputPath = resolve67(process.cwd(), options.output);
284161
- await writeFile40(outputPath, buffer);
284464
+ await writeFile39(outputPath, buffer);
284162
284465
  }
284163
284466
  const cost = realRunCost(provider, options);
284164
284467
  outputSuccess({
284165
284468
  command: "generate video",
284166
284469
  startedAt,
284167
284470
  costUsd: cost.costUsd,
284168
- warnings: cost.warnings,
284471
+ warnings: provider === "veo" ? [VEO_SHUTDOWN_WARNING, ...cost.warnings] : cost.warnings,
284169
284472
  data: {
284170
284473
  provider,
284171
284474
  taskId: result?.id,
@@ -284177,6 +284480,9 @@ Examples:
284177
284480
  return;
284178
284481
  }
284179
284482
  console.log();
284483
+ if (provider === "veo") {
284484
+ console.log(source_default.yellow(VEO_SHUTDOWN_WARNING));
284485
+ }
284180
284486
  if (finalResult.videoUrl) {
284181
284487
  console.log(`Video URL: ${finalResult.videoUrl}`);
284182
284488
  }
@@ -284189,7 +284495,7 @@ Examples:
284189
284495
  try {
284190
284496
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
284191
284497
  const outputPath = resolve67(process.cwd(), options.output);
284192
- await writeFile40(outputPath, buffer);
284498
+ await writeFile39(outputPath, buffer);
284193
284499
  downloadSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
284194
284500
  } catch (err) {
284195
284501
  downloadSpinner.fail(
@@ -284204,12 +284510,14 @@ Examples:
284204
284510
  }
284205
284511
  function realRunCost(provider, options) {
284206
284512
  if (provider === "seedance" || provider === "fal") {
284513
+ const variant = resolveSeedanceVariant(options.seedanceModel);
284207
284514
  return {
284208
284515
  costUsd: estimateSeedanceVideoCostUsd({
284209
284516
  durationSec: Number(options.duration) || 5,
284210
284517
  resolution: options.resolution,
284211
284518
  aspectRatio: options.ratio,
284212
- fast: options.seedanceModel === "fast",
284519
+ fast: variant === "seedance-2.0-fast",
284520
+ v25: variant === "seedance-2.5",
284213
284521
  hasVideoReference: Boolean(options.refVideos)
284214
284522
  }),
284215
284523
  warnings: []
@@ -284299,6 +284607,7 @@ function printVideoStarted(providerLabel, taskId, taskType) {
284299
284607
  console.log(`Task ID: ${source_default.bold(taskId)}`);
284300
284608
  if (taskType) console.log(`Type: ${taskType}`);
284301
284609
  }
284610
+ var VEO_SHUTDOWN_WARNING;
284302
284611
  var init_video = __esm({
284303
284612
  "../cli/src/commands/generate/video.ts"() {
284304
284613
  "use strict";
@@ -284314,6 +284623,7 @@ var init_video = __esm({
284314
284623
  init_upload_host();
284315
284624
  init_ai_helpers();
284316
284625
  init_status_jobs();
284626
+ VEO_SHUTDOWN_WARNING = "Veo 3.1 preview models shut down on 2026-10-22. Use `-p omni` (Gemini Omni 1.1 Flash) instead.";
284317
284627
  }
284318
284628
  });
284319
284629
 
@@ -284333,7 +284643,7 @@ var init_sanitize = __esm({
284333
284643
 
284334
284644
  // ../cli/src/commands/generate/storyboard.ts
284335
284645
  import { resolve as resolve68 } from "node:path";
284336
- import { writeFile as writeFile41 } from "node:fs/promises";
284646
+ import { writeFile as writeFile40 } from "node:fs/promises";
284337
284647
  async function executeStoryboard(options) {
284338
284648
  const { content, duration, creativity = "low", output: output3, apiKey } = options;
284339
284649
  try {
@@ -284352,7 +284662,7 @@ async function executeStoryboard(options) {
284352
284662
  let outputPath;
284353
284663
  if (output3) {
284354
284664
  outputPath = resolve68(process.cwd(), output3);
284355
- await writeFile41(outputPath, JSON.stringify(segments, null, 2), "utf-8");
284665
+ await writeFile40(outputPath, JSON.stringify(segments, null, 2), "utf-8");
284356
284666
  }
284357
284667
  return { success: true, segments, segmentCount: segments.length, outputPath };
284358
284668
  } catch (error) {
@@ -298147,10 +298457,11 @@ var init_openai3 = __esm({
298147
298457
  "../cli/src/agent/adapters/openai.ts"() {
298148
298458
  "use strict";
298149
298459
  init_openai2();
298460
+ init_dist();
298150
298461
  OpenAIAdapter = class {
298151
298462
  provider = "openai";
298152
298463
  client = null;
298153
- model = "gpt-5-mini";
298464
+ model = defaultModel("openai", "llm").id;
298154
298465
  async initialize(apiKey) {
298155
298466
  this.client = new openai_default({ apiKey });
298156
298467
  }
@@ -301696,10 +302007,11 @@ var init_claude2 = __esm({
301696
302007
  "../cli/src/agent/adapters/claude.ts"() {
301697
302008
  "use strict";
301698
302009
  init_sdk();
302010
+ init_dist();
301699
302011
  ClaudeAdapter = class {
301700
302012
  provider = "claude";
301701
302013
  client = null;
301702
- model = "claude-sonnet-4-6";
302014
+ model = defaultModel("claude", "llm").id;
301703
302015
  async initialize(apiKey) {
301704
302016
  this.client = new sdk_default({ apiKey });
301705
302017
  }
@@ -301724,6 +302036,13 @@ var init_claude2 = __esm({
301724
302036
  content: msg.content
301725
302037
  });
301726
302038
  } else if (msg.role === "assistant") {
302039
+ if (msg.providerContent) {
302040
+ claudeMessages.push({
302041
+ role: "assistant",
302042
+ content: msg.providerContent
302043
+ });
302044
+ continue;
302045
+ }
301727
302046
  const content = [];
301728
302047
  if (msg.content) {
301729
302048
  content.push({ type: "text", text: msg.content });
@@ -301745,16 +302064,17 @@ var init_claude2 = __esm({
301745
302064
  });
301746
302065
  }
301747
302066
  } else if (msg.role === "tool") {
301748
- claudeMessages.push({
301749
- role: "user",
301750
- content: [
301751
- {
301752
- type: "tool_result",
301753
- tool_use_id: msg.toolCallId,
301754
- content: msg.content
301755
- }
301756
- ]
301757
- });
302067
+ const result = {
302068
+ type: "tool_result",
302069
+ tool_use_id: msg.toolCallId,
302070
+ content: msg.content
302071
+ };
302072
+ const last3 = claudeMessages.at(-1);
302073
+ if (last3?.role === "user" && Array.isArray(last3.content)) {
302074
+ last3.content.push(result);
302075
+ } else {
302076
+ claudeMessages.push({ role: "user", content: [result] });
302077
+ }
301758
302078
  }
301759
302079
  }
301760
302080
  const claudeTools = tools2.map((tool) => ({
@@ -301768,7 +302088,8 @@ var init_claude2 = __esm({
301768
302088
  }));
301769
302089
  const response = await this.client.messages.create({
301770
302090
  model: this.model,
301771
- max_tokens: 4096,
302091
+ // Thinking tokens count against this cap on Claude 5.x.
302092
+ max_tokens: 16e3,
301772
302093
  system: systemPrompt,
301773
302094
  messages: claudeMessages,
301774
302095
  tools: claudeTools.length > 0 ? claudeTools : void 0
@@ -301795,7 +302116,8 @@ var init_claude2 = __esm({
301795
302116
  return {
301796
302117
  content: textContent2,
301797
302118
  toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
301798
- finishReason
302119
+ finishReason,
302120
+ providerContent: response.content
301799
302121
  };
301800
302122
  }
301801
302123
  };
@@ -302760,17 +303082,51 @@ var init_dist6 = __esm({
302760
303082
  // ../cli/src/agent/adapters/gemini.ts
302761
303083
  var gemini_exports = {};
302762
303084
  __export(gemini_exports, {
302763
- GeminiAdapter: () => GeminiAdapter
303085
+ GeminiAdapter: () => GeminiAdapter,
303086
+ toGeminiSchema: () => toGeminiSchema
302764
303087
  });
302765
- var GeminiAdapter;
303088
+ function toGeminiSchema(schema) {
303089
+ if (Array.isArray(schema)) return schema.map(toGeminiSchema);
303090
+ if (!schema || typeof schema !== "object") return schema;
303091
+ const out = {};
303092
+ for (const [key2, value] of Object.entries(schema)) {
303093
+ if (!GEMINI_SCHEMA_KEYS.has(key2)) continue;
303094
+ if (key2 === "properties" && value && typeof value === "object") {
303095
+ out.properties = Object.fromEntries(
303096
+ Object.entries(value).map(([name, prop2]) => [name, toGeminiSchema(prop2)])
303097
+ );
303098
+ } else if (key2 === "items") {
303099
+ out.items = toGeminiSchema(value);
303100
+ } else {
303101
+ out[key2] = value;
303102
+ }
303103
+ }
303104
+ return out;
303105
+ }
303106
+ var GEMINI_SCHEMA_KEYS, GeminiAdapter;
302766
303107
  var init_gemini2 = __esm({
302767
303108
  "../cli/src/agent/adapters/gemini.ts"() {
302768
303109
  "use strict";
302769
303110
  init_dist6();
303111
+ init_dist();
303112
+ GEMINI_SCHEMA_KEYS = /* @__PURE__ */ new Set([
303113
+ "type",
303114
+ "format",
303115
+ "description",
303116
+ "nullable",
303117
+ "enum",
303118
+ "items",
303119
+ "properties",
303120
+ "required",
303121
+ "minItems",
303122
+ "maxItems",
303123
+ "minimum",
303124
+ "maximum"
303125
+ ]);
302770
303126
  GeminiAdapter = class {
302771
303127
  provider = "gemini";
302772
303128
  client = null;
302773
- model = "gemini-2.5-flash";
303129
+ model = GEMINI_AGENT_DEFAULT_TEXT_MODEL;
302774
303130
  async initialize(apiKey) {
302775
303131
  this.client = new GoogleGenerativeAI(apiKey);
302776
303132
  }
@@ -302792,7 +303148,12 @@ var init_gemini2 = __esm({
302792
303148
  description: tool.description,
302793
303149
  parameters: {
302794
303150
  type: SchemaType.OBJECT,
302795
- properties: tool.parameters.properties,
303151
+ properties: Object.fromEntries(
303152
+ Object.entries(tool.parameters.properties).map(([name, prop2]) => [
303153
+ name,
303154
+ toGeminiSchema(prop2)
303155
+ ])
303156
+ ),
302796
303157
  required: tool.parameters.required
302797
303158
  }
302798
303159
  }))
@@ -302811,6 +303172,10 @@ var init_gemini2 = __esm({
302811
303172
  parts: [{ text: msg.content }]
302812
303173
  });
302813
303174
  } else if (msg.role === "assistant") {
303175
+ if (msg.providerContent) {
303176
+ geminiContents.push({ role: "model", parts: msg.providerContent });
303177
+ continue;
303178
+ }
302814
303179
  const parts = [];
302815
303180
  if (msg.content) {
302816
303181
  parts.push({ text: msg.content });
@@ -302832,16 +303197,19 @@ var init_gemini2 = __esm({
302832
303197
  });
302833
303198
  }
302834
303199
  } else if (msg.role === "tool") {
302835
- geminiContents.push({
302836
- role: "user",
302837
- parts: [{
302838
- functionResponse: {
302839
- name: msg.toolCallId.split(":::")[0] || "unknown",
302840
- // Extract function name from id
302841
- response: { result: msg.content }
302842
- }
302843
- }]
302844
- });
303200
+ const part = {
303201
+ functionResponse: {
303202
+ name: msg.toolCallId.split(":::")[0] || "unknown",
303203
+ // Extract function name from id
303204
+ response: { result: msg.content }
303205
+ }
303206
+ };
303207
+ const last3 = geminiContents.at(-1);
303208
+ if (last3?.role === "user" && last3.parts.every((p2) => "functionResponse" in p2)) {
303209
+ last3.parts.push(part);
303210
+ } else {
303211
+ geminiContents.push({ role: "user", parts: [part] });
303212
+ }
302845
303213
  }
302846
303214
  }
302847
303215
  const result = await model.generateContent({
@@ -302878,7 +303246,8 @@ var init_gemini2 = __esm({
302878
303246
  return {
302879
303247
  content: textContent2,
302880
303248
  toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
302881
- finishReason
303249
+ finishReason,
303250
+ providerContent: candidate.content.parts
302882
303251
  };
302883
303252
  }
302884
303253
  };
@@ -303078,10 +303447,11 @@ var init_xai = __esm({
303078
303447
  "../cli/src/agent/adapters/xai.ts"() {
303079
303448
  "use strict";
303080
303449
  init_openai2();
303450
+ init_dist();
303081
303451
  XAIAdapter = class {
303082
303452
  provider = "xai";
303083
303453
  client = null;
303084
- model = "grok-4-1-fast-reasoning";
303454
+ model = defaultModel("grok", "llm").id;
303085
303455
  async initialize(apiKey) {
303086
303456
  this.client = new openai_default({
303087
303457
  apiKey,
@@ -303381,7 +303751,7 @@ __export(detect_exports, {
303381
303751
  executeDetectScenes: () => executeDetectScenes,
303382
303752
  executeDetectSilence: () => executeDetectSilence
303383
303753
  });
303384
- import { readFile as readFile41, writeFile as writeFile45 } from "node:fs/promises";
303754
+ import { readFile as readFile41, writeFile as writeFile44 } from "node:fs/promises";
303385
303755
  import { resolve as resolve74, basename as basename20 } from "node:path";
303386
303756
  async function executeDetectScenes(options) {
303387
303757
  try {
@@ -303420,7 +303790,7 @@ async function executeDetectScenes(options) {
303420
303790
  }));
303421
303791
  if (options.outputPath) {
303422
303792
  const outputPath = resolve74(process.cwd(), options.outputPath);
303423
- await writeFile45(outputPath, JSON.stringify({ source: absPath, totalDuration, threshold, scenes: result }, null, 2), "utf-8");
303793
+ await writeFile44(outputPath, JSON.stringify({ source: absPath, totalDuration, threshold, scenes: result }, null, 2), "utf-8");
303424
303794
  }
303425
303795
  return { success: true, scenes: result, totalDuration };
303426
303796
  } catch (error) {
@@ -303467,7 +303837,7 @@ async function executeDetectSilence(options) {
303467
303837
  }
303468
303838
  if (options.outputPath) {
303469
303839
  const outputPath = resolve74(process.cwd(), options.outputPath);
303470
- await writeFile45(outputPath, JSON.stringify({ source: absPath, silences }, null, 2), "utf-8");
303840
+ await writeFile44(outputPath, JSON.stringify({ source: absPath, silences }, null, 2), "utf-8");
303471
303841
  }
303472
303842
  return { success: true, silences };
303473
303843
  } catch (error) {
@@ -303516,7 +303886,7 @@ async function executeDetectBeats(options) {
303516
303886
  }
303517
303887
  if (options.outputPath) {
303518
303888
  const outputPath = resolve74(process.cwd(), options.outputPath);
303519
- await writeFile45(outputPath, JSON.stringify({ source: absPath, beatCount: beats.length, beats }, null, 2), "utf-8");
303889
+ await writeFile44(outputPath, JSON.stringify({ source: absPath, beatCount: beats.length, beats }, null, 2), "utf-8");
303520
303890
  }
303521
303891
  return { success: true, beats, beatCount: beats.length };
303522
303892
  } catch (error) {
@@ -303620,7 +303990,7 @@ var init_detect = __esm({
303620
303990
  duration: (i3 < scenes.length - 1 ? scenes[i3 + 1].timestamp : totalDuration) - s2.timestamp
303621
303991
  }))
303622
303992
  };
303623
- await writeFile45(outputPath, JSON.stringify(result, null, 2), "utf-8");
303993
+ await writeFile44(outputPath, JSON.stringify(result, null, 2), "utf-8");
303624
303994
  console.log(source_default.green(`Saved to: ${outputPath}`));
303625
303995
  }
303626
303996
  if (options.project) {
@@ -303650,7 +304020,7 @@ var init_detect = __esm({
303650
304020
  sourceEndOffset: end
303651
304021
  });
303652
304022
  }
303653
- await writeFile45(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
304023
+ await writeFile44(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
303654
304024
  console.log(source_default.green(`Added ${scenes.length} clips to project`));
303655
304025
  }
303656
304026
  } catch (error) {
@@ -303732,7 +304102,7 @@ var init_detect = __esm({
303732
304102
  console.log();
303733
304103
  if (options.output) {
303734
304104
  const outputPath = resolve74(process.cwd(), options.output);
303735
- await writeFile45(
304105
+ await writeFile44(
303736
304106
  outputPath,
303737
304107
  JSON.stringify({ source: absPath, silences }, null, 2),
303738
304108
  "utf-8"
@@ -303817,7 +304187,7 @@ var init_detect = __esm({
303817
304187
  console.log();
303818
304188
  if (options.output) {
303819
304189
  const outputPath = resolve74(process.cwd(), options.output);
303820
- await writeFile45(
304190
+ await writeFile44(
303821
304191
  outputPath,
303822
304192
  JSON.stringify({ source: absPath, beatCount: beats.length, beats }, null, 2),
303823
304193
  "utf-8"
@@ -303872,8 +304242,9 @@ var EXPLICIT_COMMAND_METADATA = {
303872
304242
  schema: { surface: "agent", note: "Machine-readable command discovery." },
303873
304243
  completion: { surface: "advanced", note: "Shell ergonomics helper." },
303874
304244
  agent: {
303875
- surface: "advanced",
303876
- note: "Optional fallback REPL; external coding agents are the primary workflow."
304245
+ surface: "legacy",
304246
+ replacement: "a host agent (Claude Code, Codex, Cursor) or the MCP server via `vibe host setup`",
304247
+ note: "Deprecated built-in REPL, removed before 1.0."
303877
304248
  },
303878
304249
  "design.validate": { surface: "public", note: "Validate the DESIGN.md visual contract." },
303879
304250
  "generate.image": { surface: "public" },
@@ -304411,7 +304782,7 @@ init_scene_project();
304411
304782
  init_project_config();
304412
304783
  init_cost_tier();
304413
304784
  import { resolve as resolve29, relative as relative8, dirname as dirname24 } from "node:path";
304414
- import { mkdir as mkdir11, readFile as readFile14, writeFile as writeFile15, access as access4, copyFile as copyFile3 } from "node:fs/promises";
304785
+ import { mkdir as mkdir12, readFile as readFile14, writeFile as writeFile15, access as access4, copyFile as copyFile3 } from "node:fs/promises";
304415
304786
  import { existsSync as existsSync46 } from "node:fs";
304416
304787
 
304417
304788
  // ../cli/src/commands/_shared/scene-html-emit.ts
@@ -304891,7 +305262,7 @@ init_storyboard_parse();
304891
305262
  init_storyboard_source();
304892
305263
  init_root_sync();
304893
305264
  import { existsSync as existsSync44 } from "node:fs";
304894
- import { mkdir as mkdir10, writeFile as writeFile13 } from "node:fs/promises";
305265
+ import { mkdir as mkdir11, writeFile as writeFile13 } from "node:fs/promises";
304895
305266
  import { join as join58, resolve as resolve26 } from "node:path";
304896
305267
  async function executeSceneSubmit(opts) {
304897
305268
  const projectDir = resolve26(opts.projectDir);
@@ -304985,7 +305356,7 @@ async function executeSceneSubmit(opts) {
304985
305356
  return { ...base2, success: true };
304986
305357
  }
304987
305358
  const absPath = join58(projectDir, scenePath);
304988
- await mkdir10(join58(projectDir, "compositions"), { recursive: true });
305359
+ await mkdir11(join58(projectDir, "compositions"), { recursive: true });
304989
305360
  await writeFile13(absPath, html.endsWith("\n") ? html : `${html}
304990
305361
  `, "utf-8");
304991
305362
  return {
@@ -305576,7 +305947,7 @@ async function executeSceneAdd(opts) {
305576
305947
  }
305577
305948
  audioRelPath = `assets/narration-${id}.${ext}`;
305578
305949
  audioAbsPath = resolve29(projectDir, audioRelPath);
305579
- await mkdir11(dirname24(audioAbsPath), { recursive: true });
305950
+ await mkdir12(dirname24(audioAbsPath), { recursive: true });
305580
305951
  await copyFile3(sourceAbs, audioAbsPath);
305581
305952
  try {
305582
305953
  narrationDuration = await getAudioDuration(audioAbsPath);
@@ -305609,7 +305980,7 @@ async function executeSceneAdd(opts) {
305609
305980
  }
305610
305981
  audioRelPath = `assets/narration-${id}.${resolution.audioExtension}`;
305611
305982
  audioAbsPath = resolve29(projectDir, audioRelPath);
305612
- await mkdir11(dirname24(audioAbsPath), { recursive: true });
305983
+ await mkdir12(dirname24(audioAbsPath), { recursive: true });
305613
305984
  await writeFile15(audioAbsPath, tts.audioBuffer);
305614
305985
  try {
305615
305986
  narrationDuration = await getAudioDuration(audioAbsPath);
@@ -305672,7 +306043,7 @@ async function executeSceneAdd(opts) {
305672
306043
  const img = imageResult.images[0];
305673
306044
  imageRelPath = `assets/scene-${id}.png`;
305674
306045
  imageAbsPath = resolve29(projectDir, imageRelPath);
305675
- await mkdir11(dirname24(imageAbsPath), { recursive: true });
306046
+ await mkdir12(dirname24(imageAbsPath), { recursive: true });
305676
306047
  let buffer;
305677
306048
  if (img.base64) {
305678
306049
  buffer = Buffer.from(img.base64, "base64");
@@ -305699,7 +306070,7 @@ async function executeSceneAdd(opts) {
305699
306070
  }
305700
306071
  imageRelPath = `assets/scene-${id}.png`;
305701
306072
  imageAbsPath = resolve29(projectDir, imageRelPath);
305702
- await mkdir11(dirname24(imageAbsPath), { recursive: true });
306073
+ await mkdir12(dirname24(imageAbsPath), { recursive: true });
305703
306074
  const buffer = Buffer.from(imageResult.images[0].base64, "base64");
305704
306075
  await writeFile15(imageAbsPath, buffer);
305705
306076
  }
@@ -305718,7 +306089,7 @@ async function executeSceneAdd(opts) {
305718
306089
  }
305719
306090
  lottieRelPath = `assets/lottie-${id}.${ext}`;
305720
306091
  lottieAbsPath = resolve29(projectDir, lottieRelPath);
305721
- await mkdir11(dirname24(lottieAbsPath), { recursive: true });
306092
+ await mkdir12(dirname24(lottieAbsPath), { recursive: true });
305722
306093
  await copyFile3(sourceAbs, lottieAbsPath);
305723
306094
  lottieInput = {
305724
306095
  src: lottieRelPath,
@@ -305788,7 +306159,7 @@ async function executeSceneAdd(opts) {
305788
306159
  transcript: transcriptWords,
305789
306160
  lottie: lottieInput
305790
306161
  });
305791
- await mkdir11(dirname24(scenePath), { recursive: true });
306162
+ await mkdir12(dirname24(scenePath), { recursive: true });
305792
306163
  await writeFile15(scenePath, sceneHtml, "utf-8");
305793
306164
  opts.onProgress?.("Updating root composition...");
305794
306165
  let start = 0;
@@ -306005,6 +306376,7 @@ init_scene_build();
306005
306376
  init_install_skill();
306006
306377
  init_compose_prompts();
306007
306378
  init_scene_repair();
306379
+ init_build_video_providers();
306008
306380
  var SCENE_PRESETS2 = [
306009
306381
  "simple",
306010
306382
  "announcement",
@@ -306404,12 +306776,12 @@ var sceneBuildSchema = z3.object({
306404
306776
  "TTS provider override. Default 'auto' (elevenlabs key > openai key > local kokoro)."
306405
306777
  ),
306406
306778
  voice: z3.string().optional().describe("TTS voice id (provider-specific)."),
306407
- imageProvider: z3.enum(["openai"]).optional().describe("Image provider for backdrops. Default 'openai' (gpt-image-2)."),
306408
- videoProvider: z3.enum(["seedance", "grok", "kling", "runway", "veo"]).optional().describe("Video provider for per-beat video cues. Default seedance."),
306779
+ imageProvider: z3.enum(["openai"]).optional().describe("Image provider for backdrops. Default 'openai' (gpt-image-2.5-sunburst)."),
306780
+ videoProvider: z3.enum(BUILD_VIDEO_PROVIDERS).optional().describe("Video provider for per-beat video cues. Default seedance."),
306409
306781
  musicProvider: z3.enum(["elevenlabs", "replicate"]).optional().describe("Music provider for per-beat music cues. Default elevenlabs."),
306410
306782
  imageQuality: z3.enum(["standard", "hd"]).optional().describe("OpenAI image quality. Default 'standard'."),
306411
306783
  imageModel: z3.string().optional().describe(
306412
- "Image model for keyframes/backdrops/character sheets (gemini: flash|pro, openai: gpt-image-2). Provider default when omitted."
306784
+ "Image model for keyframes/backdrops/character sheets (gemini: flash|lite|pro, openai: gpt-image-2.5-sunburst|gpt-image-2.5-flare|gpt-image-2). Provider default when omitted."
306413
306785
  ),
306414
306786
  imageSize: z3.enum(["1024x1024", "1536x1024", "1024x1536"]).optional().describe("OpenAI image size. Default '1536x1024' (cinematic 16:9-ish)."),
306415
306787
  maxCostUsd: z3.number().optional().describe("Fail before provider spend when estimated cost exceeds this USD cap."),
@@ -306842,7 +307214,7 @@ init_api_key();
306842
307214
  init_exec_safe();
306843
307215
  init_remotion();
306844
307216
  import { resolve as resolve53, dirname as dirname36, basename as basename14 } from "node:path";
306845
- import { writeFile as writeFile31, mkdir as mkdir24, rm as rm9 } from "node:fs/promises";
307217
+ import { writeFile as writeFile33, mkdir as mkdir25, rm as rm10 } from "node:fs/promises";
306846
307218
  import { existsSync as existsSync73 } from "node:fs";
306847
307219
  import { tmpdir as tmpdir11 } from "node:os";
306848
307220
  var ASS_STYLES = ["karaoke-sweep", "typewriter"];
@@ -306994,7 +307366,7 @@ async function executeAnimatedCaption(options) {
306994
307366
  }
306995
307367
  const effectiveFontSize = fontSize ?? Math.round(height * 0.04);
306996
307368
  const tmpAudioDir = resolve53(tmpdir11(), `vf-ac-${Date.now()}`);
306997
- await mkdir24(tmpAudioDir, { recursive: true });
307369
+ await mkdir25(tmpAudioDir, { recursive: true });
306998
307370
  const audioPath = resolve53(tmpAudioDir, "audio.wav");
306999
307371
  await execSafe("ffmpeg", [
307000
307372
  "-y",
@@ -307011,13 +307383,13 @@ async function executeAnimatedCaption(options) {
307011
307383
  ], { timeout: 12e4 });
307012
307384
  const apiKey = await getApiKey("OPENAI_API_KEY", "OpenAI");
307013
307385
  if (!apiKey) {
307014
- await rm9(tmpAudioDir, { recursive: true, force: true }).catch(() => {
307386
+ await rm10(tmpAudioDir, { recursive: true, force: true }).catch(() => {
307015
307387
  });
307016
307388
  return { success: false, error: "OPENAI_API_KEY required for Whisper transcription. Run 'vibe setup' or set OPENAI_API_KEY in .env" };
307017
307389
  }
307018
307390
  const transcript = await transcribeWithWords(audioPath, apiKey, language);
307019
307391
  if (!transcript.words || transcript.words.length === 0) {
307020
- await rm9(tmpAudioDir, { recursive: true, force: true }).catch(() => {
307392
+ await rm10(tmpAudioDir, { recursive: true, force: true }).catch(() => {
307021
307393
  });
307022
307394
  return { success: false, error: "No words detected in transcription" };
307023
307395
  }
@@ -307025,7 +307397,7 @@ async function executeAnimatedCaption(options) {
307025
307397
  const absOutputPath = resolve53(process.cwd(), outputPath);
307026
307398
  const outDir = dirname36(absOutputPath);
307027
307399
  if (!existsSync73(outDir)) {
307028
- await mkdir24(outDir, { recursive: true });
307400
+ await mkdir25(outDir, { recursive: true });
307029
307401
  }
307030
307402
  if (tier === "ass") {
307031
307403
  const assContent = generateASS(
@@ -307034,7 +307406,7 @@ async function executeAnimatedCaption(options) {
307034
307406
  { highlightColor, fontSize: effectiveFontSize, position, width, height }
307035
307407
  );
307036
307408
  const assPath = resolve53(tmpAudioDir, "captions.ass");
307037
- await writeFile31(assPath, assContent, "utf-8");
307409
+ await writeFile33(assPath, assContent, "utf-8");
307038
307410
  const escapedAssPath = assPath.replace(/\\/g, "\\\\").replace(/:/g, "\\:");
307039
307411
  await execSafe("ffmpeg", [
307040
307412
  "-y",
@@ -307071,12 +307443,12 @@ async function executeAnimatedCaption(options) {
307071
307443
  outputPath: absOutputPath
307072
307444
  });
307073
307445
  if (!renderResult.success) {
307074
- await rm9(tmpAudioDir, { recursive: true, force: true }).catch(() => {
307446
+ await rm10(tmpAudioDir, { recursive: true, force: true }).catch(() => {
307075
307447
  });
307076
307448
  return { success: false, error: renderResult.error };
307077
307449
  }
307078
307450
  }
307079
- await rm9(tmpAudioDir, { recursive: true, force: true }).catch(() => {
307451
+ await rm10(tmpAudioDir, { recursive: true, force: true }).catch(() => {
307080
307452
  });
307081
307453
  return {
307082
307454
  success: true,
@@ -307485,7 +307857,7 @@ var editImageTool = defineTool({
307485
307857
  imagePaths: z5.array(z5.string()).describe("Input image file path(s)"),
307486
307858
  prompt: z5.string().describe("Edit instruction"),
307487
307859
  output: z5.string().optional().describe("Output file path (default: edited.png)"),
307488
- model: z5.enum(["flash", "3.1-flash", "latest", "pro"]).optional().describe("Gemini model (default: flash)"),
307860
+ model: z5.enum(["flash", "3.1-flash", "latest", "lite", "pro"]).optional().describe("Gemini model (default: flash = Nano Banana 2)"),
307489
307861
  ratio: z5.string().optional().describe("Output aspect ratio"),
307490
307862
  resolution: z5.string().optional().describe("Resolution: 1K, 2K, 4K (Pro only)")
307491
307863
  }),
@@ -309338,7 +309710,7 @@ var generateSoundEffectTool = defineTool({
309338
309710
  schema: z7.object({
309339
309711
  prompt: z7.string().describe("Description of the sound effect"),
309340
309712
  output: z7.string().optional().describe("Output audio file path (default: sound-effect.mp3)"),
309341
- duration: z7.number().optional().describe("Duration in seconds (0.5-22, default: auto)"),
309713
+ duration: z7.number().optional().describe("Duration in seconds (0.5-30, default: auto)"),
309342
309714
  promptInfluence: z7.number().optional().describe("Prompt influence 0-1 (default: 0.3)")
309343
309715
  }),
309344
309716
  async execute(args) {
@@ -309415,7 +309787,7 @@ var generateImageTool = defineTool({
309415
309787
  ratio: z7.string().optional().describe("Aspect ratio for Gemini (1:1, 16:9, 9:16, 4:3, 3:4, etc.)"),
309416
309788
  quality: z7.string().optional().describe("Quality for OpenAI: standard, hd"),
309417
309789
  count: z7.number().optional().describe("Number of images (default: 1)"),
309418
- model: z7.string().optional().describe("Gemini model: flash, 3.1-flash, latest, pro")
309790
+ model: z7.string().optional().describe("Image model. Gemini: flash (default), lite, pro. OpenAI: 2.5 (default), flare, 2, 1.5. Grok: pro")
309419
309791
  }),
309420
309792
  async execute(args) {
309421
309793
  const result = await executeImageGenerate(args);
@@ -309478,9 +309850,11 @@ var generateVideoTool = defineTool({
309478
309850
  mode: z7.string().optional().describe("Kling mode: std or pro"),
309479
309851
  negative: z7.string().optional().describe("Negative prompt (Seedance/Kling/Veo)"),
309480
309852
  resolution: z7.string().optional().describe("Resolution: 480p, 720p, 1080p, or 4k depending on provider"),
309481
- veoModel: z7.string().optional().describe("Veo model: 3.0, 3.1, 3.1-fast"),
309853
+ veoModel: z7.string().optional().describe("Veo model: 3.1, 3.1-fast"),
309482
309854
  runwayModel: z7.string().optional().describe("Runway model: gen4.5, gen4_turbo"),
309483
- seedanceModel: z7.string().optional().describe("Seedance variant: quality or fast (fal.ai only)"),
309855
+ seedanceModel: z7.string().optional().describe("Seedance variant: quality (2.0), fast, or 2.5 (fal.ai only)"),
309856
+ grokModel: z7.string().optional().describe("Grok video model: 1.5 (default), lite, classic"),
309857
+ klingModel: z7.string().optional().describe("Kling model: v3 (default), v2.6, v2.5-turbo"),
309484
309858
  generateAudio: z7.boolean().optional().describe("Generate native synchronized audio when supported"),
309485
309859
  output: z7.string().optional().describe("Output file path (downloads video)"),
309486
309860
  wait: z7.boolean().optional().describe("Wait for completion (default: true)")
@@ -309552,7 +309926,7 @@ var generateVideoExtendTool = defineTool({
309552
309926
  prompt: z7.string().optional().describe("Continuation prompt"),
309553
309927
  duration: z7.number().optional().describe("Duration in seconds"),
309554
309928
  negative: z7.string().optional().describe("Negative prompt (Kling)"),
309555
- veoModel: z7.string().optional().describe("Veo model: 3.0, 3.1, 3.1-fast"),
309929
+ veoModel: z7.string().optional().describe("Veo model: 3.1, 3.1-fast"),
309556
309930
  output: z7.string().optional().describe("Output file path"),
309557
309931
  wait: z7.boolean().optional().describe("Wait for completion (default: true)")
309558
309932
  }),
@@ -309588,7 +309962,7 @@ var generateTools = [
309588
309962
 
309589
309963
  // ../cli/src/tools/manifest/storyboard.ts
309590
309964
  import { existsSync as existsSync81 } from "node:fs";
309591
- import { readFile as readFile38, writeFile as writeFile43 } from "node:fs/promises";
309965
+ import { readFile as readFile38, writeFile as writeFile42 } from "node:fs/promises";
309592
309966
  import { resolve as resolve70 } from "node:path";
309593
309967
  import { z as z9 } from "zod";
309594
309968
  init_storyboard_parse();
@@ -309596,7 +309970,7 @@ init_storyboard_edit();
309596
309970
 
309597
309971
  // ../cli/src/commands/_shared/storyboard-revise.ts
309598
309972
  import { existsSync as existsSync80 } from "node:fs";
309599
- import { readFile as readFile37, writeFile as writeFile42 } from "node:fs/promises";
309973
+ import { readFile as readFile37, writeFile as writeFile41 } from "node:fs/promises";
309600
309974
  import { join as join74, resolve as resolve69 } from "node:path";
309601
309975
 
309602
309976
  // ../cli/src/agent/adapters/index.ts
@@ -309765,7 +310139,7 @@ async function executeStoryboardRevision(opts) {
309765
310139
  if (loaded.layout === "bundle") {
309766
310140
  await rewriteBundleFromMarkdown(projectDir, storyboardMd);
309767
310141
  } else {
309768
- await writeFile42(storyboardPath, storyboardMd, "utf-8");
310142
+ await writeFile41(storyboardPath, storyboardMd, "utf-8");
309769
310143
  }
309770
310144
  }
309771
310145
  const changedBeats = unique5([...diffChangedBeats(currentStoryboard, storyboardMd), ...payload.changedBeats]);
@@ -309943,6 +310317,7 @@ function stripUndefined4(value) {
309943
310317
 
309944
310318
  // ../cli/src/tools/manifest/storyboard.ts
309945
310319
  init_build_plan();
310320
+ init_build_video_providers();
309946
310321
  var PROJECT_DIR_DESCRIPTION3 = "Project directory. Defaults to the surface's cwd; in MCP hosts, relative paths resolve under the configured server workspace.";
309947
310322
  var projectDirSchema = z9.object({
309948
310323
  projectDir: z9.string().optional().describe(PROJECT_DIR_DESCRIPTION3)
@@ -310063,7 +310438,7 @@ var storyboardSetTool = defineTool({
310063
310438
  value,
310064
310439
  unset: args.unset
310065
310440
  });
310066
- await writeFile43(file.path, next, "utf-8");
310441
+ await writeFile42(file.path, next, "utf-8");
310067
310442
  return {
310068
310443
  success: true,
310069
310444
  data: { beat: args.beat, key: args.key },
@@ -310095,7 +310470,7 @@ var storyboardMoveTool = defineTool({
310095
310470
  beatId: args.beat,
310096
310471
  afterBeatId: args.after
310097
310472
  });
310098
- await writeFile43(file.path, next, "utf-8");
310473
+ await writeFile42(file.path, next, "utf-8");
310099
310474
  return {
310100
310475
  success: true,
310101
310476
  data: { beat: args.beat, after: args.after },
@@ -310160,7 +310535,7 @@ var planTool = defineTool({
310160
310535
  imageProvider: z9.string().optional().describe("Image provider override."),
310161
310536
  imageQuality: z9.enum(["standard", "hd"]).optional().describe("Image quality override."),
310162
310537
  imageSize: z9.string().optional().describe("Image size override."),
310163
- videoProvider: z9.enum(["seedance", "grok", "kling", "runway", "veo"]).optional().describe("Video provider override."),
310538
+ videoProvider: z9.enum(BUILD_VIDEO_PROVIDERS).optional().describe("Video provider override."),
310164
310539
  musicProvider: z9.enum(["elevenlabs", "replicate"]).optional().describe("Music provider override."),
310165
310540
  composer: z9.enum(["claude", "openai", "gemini"]).optional().describe("Composer provider override."),
310166
310541
  force: z9.boolean().optional().describe("Plan regeneration even when outputs exist.")
@@ -310249,14 +310624,14 @@ var designValidateTool = defineTool({
310249
310624
  var designTools = [designValidateTool];
310250
310625
 
310251
310626
  // ../cli/src/tools/manifest/remix.ts
310252
- import { writeFile as writeFile47 } from "node:fs/promises";
310627
+ import { writeFile as writeFile46 } from "node:fs/promises";
310253
310628
  import { tmpdir as tmpdir12 } from "node:os";
310254
310629
  import { join as join76 } from "node:path";
310255
310630
  import { z as z11 } from "zod";
310256
310631
 
310257
310632
  // ../cli/src/commands/ai-highlights.ts
310258
- import { readFile as readFile40, writeFile as writeFile44, mkdir as mkdir28 } from "node:fs/promises";
310259
- import { resolve as resolve73, dirname as dirname40, basename as basename19, extname as extname18 } from "node:path";
310633
+ import { readFile as readFile40, writeFile as writeFile43, mkdir as mkdir29 } from "node:fs/promises";
310634
+ import { resolve as resolve73, dirname as dirname40, basename as basename19, extname as extname19 } from "node:path";
310260
310635
  import { existsSync as existsSync83 } from "node:fs";
310261
310636
  init_dist();
310262
310637
  init_engine();
@@ -310291,7 +310666,7 @@ async function executeHighlights(options) {
310291
310666
  if (!existsSync83(absPath)) {
310292
310667
  return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: `File not found: ${absPath}` };
310293
310668
  }
310294
- const ext = extname18(absPath).toLowerCase();
310669
+ const ext = extname19(absPath).toLowerCase();
310295
310670
  const videoExtensions = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"];
310296
310671
  const isVideo = videoExtensions.includes(ext);
310297
310672
  const targetDuration = options.duration;
@@ -310436,7 +310811,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
310436
310811
  };
310437
310812
  if (options.output) {
310438
310813
  const outputPath = resolve73(process.cwd(), options.output);
310439
- await writeFile44(outputPath, JSON.stringify({
310814
+ await writeFile43(outputPath, JSON.stringify({
310440
310815
  sourceFile: absPath,
310441
310816
  totalDuration: sourceDuration,
310442
310817
  criteria: options.criteria || "all",
@@ -310471,7 +310846,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
310471
310846
  }
310472
310847
  }
310473
310848
  const projectPath = resolve73(process.cwd(), options.project);
310474
- await writeFile44(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
310849
+ await writeFile43(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
310475
310850
  extractResult.projectPath = projectPath;
310476
310851
  }
310477
310852
  return extractResult;
@@ -310620,7 +310995,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
310620
310995
  }
310621
310996
  const outputDir = options.outputDir ? resolve73(process.cwd(), options.outputDir) : dirname40(absPath);
310622
310997
  if (options.outputDir && !existsSync83(outputDir)) {
310623
- await mkdir28(outputDir, { recursive: true });
310998
+ await mkdir29(outputDir, { recursive: true });
310624
310999
  }
310625
311000
  const result = {
310626
311001
  success: true,
@@ -310628,7 +311003,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
310628
311003
  };
310629
311004
  for (let i3 = 0; i3 < selectedHighlights.length; i3++) {
310630
311005
  const h2 = selectedHighlights[i3];
310631
- const baseName = basename19(absPath, extname18(absPath));
311006
+ const baseName = basename19(absPath, extname19(absPath));
310632
311007
  const outputPath = resolve73(outputDir, `${baseName}-short-${i3 + 1}.mp4`);
310633
311008
  const { stdout: probeOut } = await execSafe("ffprobe", [
310634
311009
  "-v",
@@ -310698,7 +311073,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
310698
311073
  // ../cli/src/pipeline/executor.ts
310699
311074
  var import_yaml8 = __toESM(require_dist3(), 1);
310700
311075
  import { resolve as resolve75 } from "node:path";
310701
- import { readFile as readFile42, writeFile as writeFile46, mkdir as mkdir29 } from "node:fs/promises";
311076
+ import { readFile as readFile42, writeFile as writeFile45, mkdir as mkdir30 } from "node:fs/promises";
310702
311077
  import { existsSync as existsSync84 } from "node:fs";
310703
311078
 
310704
311079
  // ../cli/src/pipeline/resolver.ts
@@ -310760,6 +311135,7 @@ function findUnresolvedRefs(params, availableStepIds) {
310760
311135
  init_output();
310761
311136
  init_config();
310762
311137
  init_provider_resolver();
311138
+ init_build_video_providers();
310763
311139
  var ACTION_METADATA = {
310764
311140
  "generate-image": { id: "generate-image", title: "Generate image", category: "generate", command: "generate image", outputs: ["image"] },
310765
311141
  "generate-video": { id: "generate-video", title: "Generate video", category: "generate", command: "generate video", outputs: ["video"], requiredKeys: ["provider-dependent"] },
@@ -310833,7 +311209,7 @@ async function resolvePipelineVideoProvider(provider) {
310833
311209
  if (explicit === "fal") return "seedance";
310834
311210
  if (explicit) return explicit;
310835
311211
  await loadProviderDefaults();
310836
- return resolveProvider("video")?.name ?? "grok";
311212
+ return resolveBuildVideoProvider(resolveProvider("video")?.name ?? "grok");
310837
311213
  }
310838
311214
  function providerKeyForImage(provider) {
310839
311215
  switch (provider) {
@@ -310859,6 +311235,7 @@ function providerKeyForVideo(provider) {
310859
311235
  case "runway":
310860
311236
  return "runway";
310861
311237
  case "veo":
311238
+ case "omni":
310862
311239
  return "google";
310863
311240
  default:
310864
311241
  return void 0;
@@ -311110,7 +311487,7 @@ var CHECKPOINT_FILE = ".pipeline-state.yaml";
311110
311487
  async function executePipeline(manifest2, options = {}) {
311111
311488
  await ensureActionsRegistered();
311112
311489
  const outputDir = resolve75(process.cwd(), options.outputDir || `${manifest2.name}-output`);
311113
- await mkdir29(outputDir, { recursive: true });
311490
+ await mkdir30(outputDir, { recursive: true });
311114
311491
  const completedSteps = /* @__PURE__ */ new Map();
311115
311492
  const results = [];
311116
311493
  const startTime = Date.now();
@@ -311235,7 +311612,7 @@ async function executePipeline(manifest2, options = {}) {
311235
311612
  data: s2.data
311236
311613
  }))
311237
311614
  };
311238
- await writeFile46(
311615
+ await writeFile45(
311239
311616
  resolve75(outputDir, CHECKPOINT_FILE),
311240
311617
  (0, import_yaml8.stringify)(checkpoint, { indent: 2 }),
311241
311618
  "utf-8"
@@ -311390,7 +311767,7 @@ var pipelineRunTool = defineTool({
311390
311767
  let tempPath;
311391
311768
  if (args.pipelineYaml) {
311392
311769
  tempPath = join76(tmpdir12(), `vibe-mcp-pipeline-${Date.now()}.yaml`);
311393
- await writeFile47(tempPath, args.pipelineYaml, "utf-8");
311770
+ await writeFile46(tempPath, args.pipelineYaml, "utf-8");
311394
311771
  resolvedPath = tempPath;
311395
311772
  }
311396
311773
  try {
@@ -311527,7 +311904,7 @@ var detectTools = [
311527
311904
 
311528
311905
  // ../cli/src/tools/manifest/timeline.ts
311529
311906
  import { z as z13 } from "zod";
311530
- import { mkdir as mkdir30, writeFile as writeFile49 } from "node:fs/promises";
311907
+ import { mkdir as mkdir31, writeFile as writeFile48 } from "node:fs/promises";
311531
311908
  import { basename as basename21, resolve as resolve76 } from "node:path";
311532
311909
  init_engine();
311533
311910
  init_project_resolver();
@@ -311535,7 +311912,7 @@ init_project_resolver();
311535
311912
  // ../cli/src/tools/manifest/_project-io.ts
311536
311913
  init_engine();
311537
311914
  init_project_resolver();
311538
- import { readFile as readFile43, writeFile as writeFile48 } from "node:fs/promises";
311915
+ import { readFile as readFile43, writeFile as writeFile47 } from "node:fs/promises";
311539
311916
  async function loadProject(projectPath, cwd) {
311540
311917
  const absPath = await resolveTimelineFile(projectPath, cwd);
311541
311918
  const content = await readFile43(absPath, "utf-8");
@@ -311543,7 +311920,7 @@ async function loadProject(projectPath, cwd) {
311543
311920
  return { project: Project.fromJSON(data2), absPath };
311544
311921
  }
311545
311922
  async function saveProject(absPath, project) {
311546
- await writeFile48(absPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
311923
+ await writeFile47(absPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
311547
311924
  }
311548
311925
 
311549
311926
  // ../cli/src/tools/manifest/timeline.ts
@@ -311583,10 +311960,10 @@ var timelineCreateTool = defineTool({
311583
311960
  outputPath = resolve76(ctx.workingDirectory, args.outputPath);
311584
311961
  } else {
311585
311962
  const dirPath = resolve76(ctx.workingDirectory, args.name);
311586
- await mkdir30(dirPath, { recursive: true });
311963
+ await mkdir31(dirPath, { recursive: true });
311587
311964
  outputPath = resolve76(dirPath, TIMELINE_FILENAME);
311588
311965
  }
311589
- await writeFile49(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
311966
+ await writeFile48(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
311590
311967
  return {
311591
311968
  success: true,
311592
311969
  data: { name: projectName, outputPath },
@@ -311884,7 +312261,7 @@ var timelineTools = [
311884
312261
  // ../cli/src/tools/manifest/project.ts
311885
312262
  import { z as z14 } from "zod";
311886
312263
  import { resolve as resolve78 } from "node:path";
311887
- import { writeFile as writeFile50 } from "node:fs/promises";
312264
+ import { writeFile as writeFile49 } from "node:fs/promises";
311888
312265
  init_engine();
311889
312266
 
311890
312267
  // ../cli/src/commands/_shared/project-list.ts
@@ -312042,7 +312419,7 @@ var projectCreateTool = defineTool({
312042
312419
  const outputPath = resolve78(ctx.workingDirectory, args.outputPath ?? `${args.name}.vibe.json`);
312043
312420
  const project = new Project(args.name);
312044
312421
  if (args.fps) project.setFrameRate(args.fps);
312045
- await writeFile50(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
312422
+ await writeFile49(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
312046
312423
  return {
312047
312424
  success: true,
312048
312425
  data: { name: args.name, outputPath },
@@ -312763,8 +313140,8 @@ var exportVideoTool = defineTool({
312763
313140
  var exportTools = [exportVideoTool];
312764
313141
 
312765
313142
  // ../cli/src/tools/manifest/agent-only.ts
312766
- import { readFile as readFile46, writeFile as writeFile51, readdir as readdir11, stat as stat10, access as access7, unlink as unlink5 } from "node:fs/promises";
312767
- import { resolve as resolve81, join as join78, basename as basename23, extname as extname19 } from "node:path";
313143
+ import { readFile as readFile46, writeFile as writeFile50, readdir as readdir11, stat as stat10, access as access7, unlink as unlink5 } from "node:fs/promises";
313144
+ import { resolve as resolve81, join as join78, basename as basename23, extname as extname20 } from "node:path";
312768
313145
  import { z as z16 } from "zod";
312769
313146
  init_engine();
312770
313147
  init_exec_safe();
@@ -312787,7 +313164,7 @@ function formatSize(bytes) {
312787
313164
  return `${size.toFixed(1)}${units2[unit]}`;
312788
313165
  }
312789
313166
  function detectMediaType(filePath) {
312790
- const ext = extname19(filePath).toLowerCase();
313167
+ const ext = extname20(filePath).toLowerCase();
312791
313168
  if ([".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"].includes(ext)) return "video";
312792
313169
  if ([".mp3", ".wav", ".aac", ".flac", ".ogg", ".m4a"].includes(ext)) return "audio";
312793
313170
  if ([".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"].includes(ext)) return "image";
@@ -312814,7 +313191,7 @@ function isMediaFile(filePath) {
312814
313191
  ".webp",
312815
313192
  ".bmp"
312816
313193
  ];
312817
- return mediaExts.includes(extname19(filePath).toLowerCase());
313194
+ return mediaExts.includes(extname20(filePath).toLowerCase());
312818
313195
  }
312819
313196
  async function getMediaDuration2(filePath) {
312820
313197
  try {
@@ -312897,7 +313274,7 @@ var fsWriteTool = defineTool({
312897
313274
  async execute(args, ctx) {
312898
313275
  try {
312899
313276
  const absPath = resolve81(ctx.workingDirectory, args.path);
312900
- await writeFile51(absPath, args.content, "utf-8");
313277
+ await writeFile50(absPath, args.content, "utf-8");
312901
313278
  return { success: true, data: { bytes: args.content.length }, humanLines: [`File written: ${args.path} (${formatSize(args.content.length)})`] };
312902
313279
  } catch (error) {
312903
313280
  return { success: false, error: `Failed to write file: ${error instanceof Error ? error.message : String(error)}` };
@@ -312960,7 +313337,7 @@ var batchImportTool = defineTool({
312960
313337
  if (entry.isDirectory() && recursive) {
312961
313338
  await scanDir(entryPath);
312962
313339
  } else if (entry.isFile()) {
312963
- const ext = extname19(entry.name).toLowerCase();
313340
+ const ext = extname20(entry.name).toLowerCase();
312964
313341
  const matchesFilter = !filterExts || filterExts.includes(ext);
312965
313342
  if (matchesFilter && isMediaFile(entryPath)) mediaFiles.push(entryPath);
312966
313343
  }
@@ -312983,7 +313360,7 @@ var batchImportTool = defineTool({
312983
313360
  const source3 = project.addSource({ name: mediaName, type: mediaType, url: mediaFile, duration });
312984
313361
  addedSources.push({ id: source3.id, name: mediaName, type: mediaType });
312985
313362
  }
312986
- await writeFile51(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
313363
+ await writeFile50(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
312987
313364
  return {
312988
313365
  success: true,
312989
313366
  data: { count: addedSources.length, sources: addedSources },
@@ -313053,7 +313430,7 @@ var batchConcatTool = defineTool({
313053
313430
  createdClips.push({ id: clip.id, sourceName: source3.name, startTime: currentTime, duration: source3.duration });
313054
313431
  currentTime += source3.duration + gap;
313055
313432
  }
313056
- await writeFile51(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
313433
+ await writeFile50(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
313057
313434
  const totalDuration = currentTime - gap - startTime;
313058
313435
  return {
313059
313436
  success: true,
@@ -313116,7 +313493,7 @@ var batchApplyEffectTool = defineTool({
313116
313493
  });
313117
313494
  if (effect) appliedEffects.push({ clipId: clip.id, effectId: effect.id });
313118
313495
  }
313119
- await writeFile51(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
313496
+ await writeFile50(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
313120
313497
  return {
313121
313498
  success: true,
313122
313499
  data: { applied: appliedEffects.length, effectType: args.effectType },
@@ -313185,7 +313562,7 @@ var timelineClearTool = defineTool({
313185
313562
  removed.sources++;
313186
313563
  }
313187
313564
  }
313188
- await writeFile51(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
313565
+ await writeFile50(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
313189
313566
  const parts = [];
313190
313567
  if (removed.clips > 0) parts.push(`${removed.clips} clips`);
313191
313568
  if (removed.tracks > 0) parts.push(`${removed.tracks} tracks`);
@@ -313236,7 +313613,7 @@ var projectSetTool = defineTool({
313236
313613
  project.setFrameRate(args.fps);
313237
313614
  updates.push(`Frame Rate: ${args.fps} fps`);
313238
313615
  }
313239
- await writeFile51(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
313616
+ await writeFile50(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
313240
313617
  return {
313241
313618
  success: true,
313242
313619
  data: { updates },
@@ -313606,7 +313983,7 @@ var mediaConcatTool = defineTool({
313606
313983
  } else {
313607
313984
  const tempList = resolve81(ctx.workingDirectory, `concat-list-${Date.now()}.txt`);
313608
313985
  const listContent = args.inputs.map((i3) => `file '${resolve81(ctx.workingDirectory, i3)}'`).join("\n");
313609
- await writeFile51(tempList, listContent, "utf-8");
313986
+ await writeFile50(tempList, listContent, "utf-8");
313610
313987
  try {
313611
313988
  await execSafe(
313612
313989
  "ffmpeg",
@@ -313900,8 +314277,9 @@ they want for a given task. This guide is the operator-facing summary.
313900
314277
 
313901
314278
  If you already use Claude Code, Codex, Cursor, Aider, Gemini CLI, OpenCode,
313902
314279
  or another coding agent, let that host drive \`vibe\` directly through shell
313903
- commands plus \`AGENTS.md\` / \`CLAUDE.md\`. \`vibe agent\` is the optional
313904
- built-in fallback when you do not already have an agent host.
314280
+ commands plus \`AGENTS.md\` / \`CLAUDE.md\`. Without a coding agent, connect
314281
+ the MCP server to Claude Desktop or ChatGPT (\`vibe host setup\`).
314282
+ \`vibe agent\` is deprecated and will be removed before 1.0.
313905
314283
 
313906
314284
  ## TL;DR
313907
314285
 
@@ -313912,12 +314290,12 @@ built-in fallback when you do not already have an agent host.
313912
314290
  | **Reproducibility** | none | high (idempotent) | highest (checkpointed) |
313913
314291
  | **Budget caps** | per-session max-turns | none | \`--budget-usd\`, \`--budget-tokens\`, \`--max-errors\` |
313914
314292
  | **Resume after crash** | no | re-invoke | \`--resume\` |
313915
- | **Best for** | optional built-in exploration | finished script | repeatable workflows |
314293
+ | **Best for** | deprecated; use a host agent | finished script | repeatable workflows |
313916
314294
 
313917
314295
  ## Decision tree
313918
314296
 
313919
314297
  - "I am already in Claude Code/Codex/Cursor/etc." \u2192 regular \`vibe\` shell commands + \`vibe guide\` / \`vibe schema\`
313920
- - "I want to play and do not have an external coding agent" \u2192 \`vibe agent\`
314298
+ - "I do not use a coding agent" \u2192 the MCP server in Claude Desktop or ChatGPT (\`vibe host setup\`)
313921
314299
  - "I have a finished script + visual identity" \u2192 \`vibe build\`
313922
314300
  - "I want this to run again next month" \u2192 \`vibe run\`
313923
314301
 
@@ -313943,9 +314321,6 @@ agent calls them through a tool manifest; \`build\` calls a curated subset
313943
314321
  - \`vibe build\` is conceptually a 4-step pipeline; \`vibe run\` could express
313944
314322
  the same flow but build's STORYBOARD.md input + opinionated defaults are
313945
314323
  the value-add.
313946
- - \`vibe agent\`'s tool registry currently exposes primitives, not
313947
- orchestrators. Adding \`vibe.build\` / \`vibe.run\` as agent tools is on the
313948
- table for a future major.
313949
314324
  - \`vibe run --resume\` has no analog in build/agent; build's idempotent
313950
314325
  re-invoke covers the common case.
313951
314326
  `;
@@ -314052,17 +314427,17 @@ var META = {
314052
314427
  },
314053
314428
  architecture: {
314054
314429
  title: "external agents / vibe agent / build / run",
314055
- summary: "Choose between host-agent shell use, optional built-in agent mode, storyboard builds, and YAML pipelines",
314430
+ summary: "Choose between host-agent shell use, MCP hosts, storyboard builds, and YAML pipelines",
314056
314431
  steps: [
314057
314432
  "If you are already in Claude Code/Codex/Cursor/etc., let that host drive normal `vibe` shell commands using `AGENTS.md`, `vibe guide`, and `vibe schema`.",
314058
- "Pick `vibe agent` only for optional built-in exploration when no external agent host is driving the CLI.",
314433
+ "Without a coding agent, connect the MCP server to Claude Desktop or ChatGPT with `vibe host setup`; `vibe agent` is deprecated.",
314059
314434
  "Pick build for STORYBOARD.md \u2192 MP4 with opinionated defaults.",
314060
314435
  "Pick run for repeatable, budget-capped, checkpointed YAML pipelines.",
314061
314436
  "Run `vibe doctor --test-keys` before any high-cost orchestrator to validate keys upfront.",
314062
314437
  "Use `vibe schema --list` (cost field, v0.84) to plan the budget per step before kicking off `vibe run --budget-usd`."
314063
314438
  ],
314064
314439
  relatedCommands: [
314065
- "vibe agent",
314440
+ "vibe host setup",
314066
314441
  "vibe build",
314067
314442
  "vibe run",
314068
314443
  "vibe doctor",
@@ -315058,7 +315433,7 @@ function handleCliFlags(argv, version2) {
315058
315433
  }
315059
315434
 
315060
315435
  // src/index.ts
315061
- if (handleCliFlags(process.argv.slice(2), "0.115.2")) {
315436
+ if (handleCliFlags(process.argv.slice(2), "0.115.4")) {
315062
315437
  process.exit(0);
315063
315438
  }
315064
315439
  console.log = (...args) => console.error(...args);
@@ -315071,7 +315446,7 @@ var server = new Server(
315071
315446
  {
315072
315447
  name: "vibeframe",
315073
315448
  // Stamped with the package version by build.js at bundle time.
315074
- version: "0.115.2"
315449
+ version: "0.115.4"
315075
315450
  },
315076
315451
  {
315077
315452
  capabilities: {