@vibeframe/cli 0.115.2 → 0.115.3

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.
@@ -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",
@@ -458,18 +457,20 @@ var GEMINI_DEFAULT_TEXT_MODEL, GEMINI_AGENT_DEFAULT_TEXT_MODEL, GEMINI_TEXT_MODE
458
457
  var init_gemini_models = __esm({
459
458
  "../ai-providers/dist/gemini/gemini-models.js"() {
460
459
  "use strict";
461
- GEMINI_DEFAULT_TEXT_MODEL = "gemini-3.5-flash";
462
- GEMINI_AGENT_DEFAULT_TEXT_MODEL = "gemini-2.5-flash";
460
+ GEMINI_DEFAULT_TEXT_MODEL = "gemini-3.8-flash";
461
+ GEMINI_AGENT_DEFAULT_TEXT_MODEL = GEMINI_DEFAULT_TEXT_MODEL;
463
462
  GEMINI_TEXT_MODEL_ALIASES = {
464
463
  flash: GEMINI_DEFAULT_TEXT_MODEL,
465
464
  latest: GEMINI_DEFAULT_TEXT_MODEL,
466
- "flash-3.5": GEMINI_DEFAULT_TEXT_MODEL,
465
+ "flash-3.8": GEMINI_DEFAULT_TEXT_MODEL,
466
+ "flash-3.5": "gemini-3.5-flash",
467
467
  "flash-3": "gemini-3-flash-preview",
468
468
  "flash-2.5": "gemini-2.5-flash",
469
- pro: "gemini-2.5-pro",
470
- "pro-3.1": "gemini-3.1-pro-preview"
469
+ pro: "gemini-3.1-pro-preview",
470
+ "pro-3.1": "gemini-3.1-pro-preview",
471
+ "pro-2.5": "gemini-2.5-pro"
471
472
  };
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";
473
+ 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
474
  }
474
475
  });
475
476
 
@@ -656,7 +657,6 @@ UNIVERSAL RULES:
656
657
  system_instruction: { parts: [{ text: systemPrompt }] },
657
658
  contents: [{ role: "user", parts: [{ text: userPrompt }] }],
658
659
  generationConfig: {
659
- temperature: 0.8,
660
660
  maxOutputTokens: 16384
661
661
  }
662
662
  })
@@ -757,7 +757,7 @@ Return the complete modified component as JSON.`
757
757
  ]
758
758
  }
759
759
  ],
760
- generationConfig: { temperature: 0.5, maxOutputTokens: 16384 }
760
+ generationConfig: { maxOutputTokens: 16384 }
761
761
  })
762
762
  });
763
763
  if (!response.ok) {
@@ -807,10 +807,11 @@ var init_gemini_motion = __esm({
807
807
  gemini: GEMINI_DEFAULT_TEXT_MODEL,
808
808
  flash: GEMINI_DEFAULT_TEXT_MODEL,
809
809
  latest: GEMINI_DEFAULT_TEXT_MODEL,
810
- "flash-3.5": GEMINI_DEFAULT_TEXT_MODEL,
810
+ "flash-3.8": GEMINI_DEFAULT_TEXT_MODEL,
811
+ "flash-3.5": "gemini-3.5-flash",
811
812
  "flash-3": "gemini-3-flash-preview",
812
813
  "flash-2.5": "gemini-2.5-flash",
813
- pro: "gemini-2.5-pro",
814
+ pro: "gemini-3.1-pro-preview",
814
815
  "2.5-pro": "gemini-2.5-pro",
815
816
  "3.1-pro": "gemini-3.1-pro-preview",
816
817
  "pro-3.1": "gemini-3.1-pro-preview"
@@ -922,7 +923,6 @@ var init_storyboard_prompt = __esm({
922
923
  async function analyzeContent(api, content, targetDuration, options) {
923
924
  const creativity = options?.creativity || "low";
924
925
  const systemPrompt = buildStoryboardSystemPrompt(targetDuration, creativity);
925
- const temperature = creativity === "high" ? 1 : 0.7;
926
926
  try {
927
927
  const payload = {
928
928
  system_instruction: {
@@ -932,7 +932,6 @@ async function analyzeContent(api, content, targetDuration, options) {
932
932
  parts: [{ text: buildStoryboardUserMessage(content) }]
933
933
  }],
934
934
  generationConfig: {
935
- temperature,
936
935
  maxOutputTokens: 4096
937
936
  }
938
937
  };
@@ -1003,7 +1002,15 @@ var init_http = __esm({
1003
1002
  });
1004
1003
 
1005
1004
  // ../ai-providers/dist/gemini/GeminiProvider.js
1006
- var MODEL_MAP, GeminiProvider, geminiProvider;
1005
+ function resolveVeoModel(alias) {
1006
+ if (Object.hasOwn(VEO_MODEL_ALIASES, alias)) {
1007
+ return VEO_MODEL_ALIASES[alias];
1008
+ }
1009
+ const valid = Object.keys(VEO_MODEL_ALIASES).join(", ");
1010
+ const hint = alias === "3.0" ? " Veo 3.0 was shut down by Google on 2025-11-12." : "";
1011
+ throw new Error(`Unknown Veo model "${alias}". Valid: ${valid}.${hint}`);
1012
+ }
1013
+ var VEO_MODEL_ALIASES, MODEL_MAP, GeminiProvider, geminiProvider;
1007
1014
  var init_GeminiProvider = __esm({
1008
1015
  "../ai-providers/dist/gemini/GeminiProvider.js"() {
1009
1016
  "use strict";
@@ -1011,11 +1018,19 @@ var init_GeminiProvider = __esm({
1011
1018
  init_gemini_models();
1012
1019
  init_gemini_storyboard();
1013
1020
  init_http();
1021
+ VEO_MODEL_ALIASES = {
1022
+ "3.1": "veo-3.1-generate-preview",
1023
+ "3.1-fast": "veo-3.1-fast-generate-preview"
1024
+ };
1014
1025
  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",
1026
+ "flash": "gemini-3.1-flash-image",
1027
+ "3.1-flash": "gemini-3.1-flash-image",
1028
+ "latest": "gemini-3.1-flash-image",
1029
+ "lite": "gemini-3.1-flash-lite-image",
1030
+ "pro": "gemini-3-pro-image",
1031
+ "gemini-3.1-flash-image": "gemini-3.1-flash-image",
1032
+ "gemini-3.1-flash-lite-image": "gemini-3.1-flash-lite-image",
1033
+ "gemini-3-pro-image": "gemini-3-pro-image",
1019
1034
  "gemini-2.5-flash-image": "gemini-2.5-flash-image",
1020
1035
  "gemini-3.1-flash-image-preview": "gemini-3.1-flash-image-preview",
1021
1036
  "gemini-3-pro-image-preview": "gemini-3-pro-image-preview"
@@ -1324,7 +1339,7 @@ var init_GeminiProvider = __esm({
1324
1339
  contents: [{ parts: [{ text: prompt3 }] }],
1325
1340
  generationConfig
1326
1341
  };
1327
- const is31Flash = modelId === "gemini-3.1-flash-image-preview";
1342
+ const is31Flash = modelId.startsWith("gemini-3.1-flash-image");
1328
1343
  if (options.imageSearchGrounding && is31Flash) {
1329
1344
  payload.tools = [{ googleSearch: { searchTypes: { webSearch: {}, imageSearch: {} } } }];
1330
1345
  } else if (options.grounding && isPro) {
@@ -1552,7 +1567,6 @@ var init_GeminiProvider = __esm({
1552
1567
  videoPart.video_metadata = videoMetadata;
1553
1568
  }
1554
1569
  const generationConfig = {
1555
- temperature: 0.4,
1556
1570
  maxOutputTokens: 8192
1557
1571
  };
1558
1572
  if (options.lowResolution) {
@@ -1634,7 +1648,6 @@ var init_GeminiProvider = __esm({
1634
1648
  }
1635
1649
  }));
1636
1650
  const generationConfig = {
1637
- temperature: 0.4,
1638
1651
  maxOutputTokens: 8192
1639
1652
  };
1640
1653
  if (options.lowResolution) {
@@ -1734,7 +1747,6 @@ Respond with ONLY the JSON array, no other text.`;
1734
1747
  body: JSON.stringify({
1735
1748
  contents: [{ parts: [{ text: prompt3 }] }],
1736
1749
  generationConfig: {
1737
- temperature: 0.3,
1738
1750
  maxOutputTokens: 1024
1739
1751
  }
1740
1752
  })
@@ -1828,7 +1840,7 @@ Respond with ONLY the JSON array, no other text.`;
1828
1840
  return refineMotion({ apiKey: this.apiKey, baseUrl: this.baseUrl }, existingCode, instructions, options);
1829
1841
  }
1830
1842
  /**
1831
- * Generate a storyboard from script content using Gemini 2.5 Flash.
1843
+ * Generate a storyboard from script content using the default Gemini Flash model.
1832
1844
  * Alternative to ClaudeProvider.analyzeContent for when Claude is unavailable.
1833
1845
  */
1834
1846
  async analyzeContent(content, targetDuration, options) {
@@ -1843,43 +1855,47 @@ Respond with ONLY the JSON array, no other text.`;
1843
1855
  });
1844
1856
 
1845
1857
  // ../ai-providers/dist/gemini/gemini-omni.js
1846
- function firstReferenceImage(options) {
1847
- const refs = options.referenceImages;
1848
- if (refs && refs.length > 0 && refs[0].base64)
1849
- return refs[0];
1850
- const ref = options.referenceImage;
1851
- if (typeof ref === "string" && ref) {
1858
+ function frameImages(options) {
1859
+ const frames = [];
1860
+ const toInline = (ref) => {
1852
1861
  const m = ref.match(/^data:(.+?);base64,(.*)$/);
1853
- if (m)
1854
- return { mimeType: m[1], base64: m[2] };
1855
- return { mimeType: "image/png", base64: ref };
1862
+ return m ? { mimeType: m[1], base64: m[2] } : { mimeType: "image/png", base64: ref };
1863
+ };
1864
+ if (options.referenceImages?.[0]?.base64)
1865
+ frames.push(options.referenceImages[0]);
1866
+ else if (typeof options.referenceImage === "string" && options.referenceImage) {
1867
+ frames.push(toInline(options.referenceImage));
1868
+ }
1869
+ if (frames.length > 0 && options.lastFrame)
1870
+ frames.push(toInline(options.lastFrame));
1871
+ return frames;
1872
+ }
1873
+ function findOmniVideo(response) {
1874
+ for (const step of response.steps ?? []) {
1875
+ if (step.type !== "model_output")
1876
+ continue;
1877
+ const video = step.content?.find((c) => c.type === "video");
1878
+ if (video)
1879
+ return video;
1856
1880
  }
1857
- return void 0;
1881
+ return response.output_video;
1858
1882
  }
1859
- function findVideoUrl(obj) {
1860
- if (!obj || typeof obj !== "object")
1861
- return void 0;
1862
- for (const [k, v] of Object.entries(obj)) {
1863
- if (typeof v === "string" && /^https?:\/\//.test(v) && /(uri|url|video)/i.test(k))
1864
- return v;
1865
- if (typeof v === "object") {
1866
- const nested = findVideoUrl(v);
1867
- if (nested)
1868
- return nested;
1869
- }
1870
- }
1871
- return void 0;
1883
+ function fileIdFromUri(uri) {
1884
+ return uri.match(/files\/([^/?:]+)/)?.[1];
1872
1885
  }
1873
- var OMNI_MODEL, INTERACTIONS_URL, OmniProvider, omniProvider;
1886
+ var OMNI_MODEL, API_ROOT, FILE_POLL_INTERVAL_MS, FILE_POLL_TIMEOUT_MS, OmniProvider, omniProvider;
1874
1887
  var init_gemini_omni = __esm({
1875
1888
  "../ai-providers/dist/gemini/gemini-omni.js"() {
1876
1889
  "use strict";
1877
- OMNI_MODEL = "gemini-omni-flash-preview";
1878
- INTERACTIONS_URL = "https://generativelanguage.googleapis.com/v1beta/interactions";
1890
+ init_http();
1891
+ OMNI_MODEL = "gemini-omni-1.1-flash";
1892
+ API_ROOT = "https://generativelanguage.googleapis.com";
1893
+ FILE_POLL_INTERVAL_MS = 3e3;
1894
+ FILE_POLL_TIMEOUT_MS = 5 * 60 * 1e3;
1879
1895
  OmniProvider = class {
1880
1896
  constructor() {
1881
1897
  this.id = "omni";
1882
- this.label = "Gemini Omni (experimental)";
1898
+ this.label = "Gemini Omni 1.1 Flash";
1883
1899
  }
1884
1900
  async initialize(config2) {
1885
1901
  this.apiKey = config2.apiKey;
@@ -1892,22 +1908,23 @@ var init_gemini_omni = __esm({
1892
1908
  return { id: "", status: "failed", error: "GOOGLE_API_KEY not configured for Gemini Omni" };
1893
1909
  }
1894
1910
  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
- }
1911
+ const frames = frameImages(opts);
1912
+ const input = frames.length === 0 ? prompt3 : [
1913
+ ...frames.map((f) => ({ type: "image", data: f.base64, mime_type: f.mimeType })),
1914
+ { type: "text", text: prompt3 }
1915
+ ];
1902
1916
  const body = {
1903
1917
  model: OMNI_MODEL,
1904
- inputs,
1905
- generation_config: {
1906
- video_config: { task, aspect_ratio: aspectRatio2 }
1918
+ input,
1919
+ response_format: {
1920
+ type: "video",
1921
+ aspect_ratio: opts.aspectRatio === "9:16" ? "9:16" : "16:9",
1922
+ resolution: opts.resolution ?? "720p",
1923
+ delivery: "uri"
1907
1924
  }
1908
1925
  };
1909
1926
  try {
1910
- const res = await fetch(INTERACTIONS_URL, {
1927
+ const res = await fetch(`${API_ROOT}/v1beta/interactions`, {
1911
1928
  method: "POST",
1912
1929
  headers: { "Content-Type": "application/json", "x-goog-api-key": this.apiKey },
1913
1930
  body: JSON.stringify(body)
@@ -1917,33 +1934,52 @@ var init_gemini_omni = __esm({
1917
1934
  return {
1918
1935
  id: "",
1919
1936
  status: "failed",
1920
- error: `Gemini Omni (experimental) request failed: HTTP ${res.status} \u2014 ${text.slice(0, 300)}`
1937
+ error: `Gemini Omni request failed: HTTP ${res.status} - ${text.slice(0, 300)}`
1921
1938
  };
1922
1939
  }
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" };
1940
+ const parsed = JSON.parse(text);
1941
+ const id = parsed.id ?? "";
1942
+ const video = findOmniVideo(parsed);
1943
+ if (!video?.uri) {
1944
+ const reason = parsed.error?.message ?? `status ${parsed.status ?? "unknown"}`;
1945
+ return { id, status: "failed", error: `Gemini Omni returned no video (${reason})` };
1928
1946
  }
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
- };
1947
+ const fileId = fileIdFromUri(video.uri);
1948
+ if (!fileId) {
1949
+ return { id, status: "failed", error: `Gemini Omni returned an unexpected video URI: ${video.uri}` };
1937
1950
  }
1938
- return { id, status: "completed", videoUrl };
1951
+ const ready = await this.waitForFile(fileId);
1952
+ if (ready !== "ACTIVE") {
1953
+ return { id, status: "failed", error: `Gemini Omni video file ended in state ${ready}` };
1954
+ }
1955
+ return {
1956
+ id,
1957
+ status: "completed",
1958
+ videoUrl: `${API_ROOT}/download/v1beta/files/${fileId}:download?alt=media`
1959
+ };
1939
1960
  } catch (err) {
1940
1961
  return {
1941
1962
  id: "",
1942
1963
  status: "failed",
1943
- error: `Gemini Omni (experimental) error: ${err instanceof Error ? err.message : String(err)}`
1964
+ error: `Gemini Omni error: ${err instanceof Error ? err.message : String(err)}`
1944
1965
  };
1945
1966
  }
1946
1967
  }
1968
+ /** Poll the Files API until the generated video is downloadable. */
1969
+ async waitForFile(fileId) {
1970
+ const deadline = Date.now() + FILE_POLL_TIMEOUT_MS;
1971
+ for (; ; ) {
1972
+ const res = await fetch(`${API_ROOT}/v1beta/files/${fileId}`, {
1973
+ headers: { "x-goog-api-key": this.apiKey }
1974
+ });
1975
+ const state = res.ok ? (await res.json()).state : `HTTP ${res.status}`;
1976
+ if (state !== "PROCESSING")
1977
+ return state ?? "UNKNOWN";
1978
+ if (Date.now() > deadline)
1979
+ return "TIMEOUT";
1980
+ await sleep(FILE_POLL_INTERVAL_MS);
1981
+ }
1982
+ }
1947
1983
  };
1948
1984
  omniProvider = new OmniProvider();
1949
1985
  }
@@ -1977,14 +2013,16 @@ var init_gemini = __esm({
1977
2013
  label: "Veo",
1978
2014
  apiKey: "google",
1979
2015
  kinds: ["video"],
1980
- resolverPriority: { video: 3 },
2016
+ // No resolverPriority: the Veo 3.1 previews shut down on 2026-10-22, so
2017
+ // Veo is explicit-only (`-p veo`) and Omni is the Google default.
1981
2018
  commandsUnlocked: ["generate video -p veo"]
1982
2019
  });
1983
2020
  defineProvider({
1984
2021
  id: "omni",
1985
- label: "Gemini Omni (experimental)",
2022
+ label: "Gemini Omni",
1986
2023
  apiKey: "google",
1987
2024
  kinds: ["video"],
2025
+ resolverPriority: { video: 3 },
1988
2026
  commandsUnlocked: ["generate video -p omni"]
1989
2027
  });
1990
2028
  }
@@ -1993,8 +2031,7 @@ var init_gemini = __esm({
1993
2031
  // ../ai-providers/dist/openai/openai-storyboard.js
1994
2032
  async function analyzeContent2(apiKey, content, targetDuration, options) {
1995
2033
  const creativity = options?.creativity || "low";
1996
- const systemPrompt = buildStoryboardSystemPrompt(targetDuration, creativity);
1997
- const temperature = creativity === "high" ? 1 : 0.7;
2034
+ const systemPrompt = buildStoryboardSystemPrompt(targetDuration, creativity) + '\n\nWrap the array in a JSON object: {"segments": [ ...segment objects... ]}.';
1998
2035
  try {
1999
2036
  const response = await fetch("https://api.openai.com/v1/chat/completions", {
2000
2037
  method: "POST",
@@ -2003,13 +2040,13 @@ async function analyzeContent2(apiKey, content, targetDuration, options) {
2003
2040
  Authorization: `Bearer ${apiKey}`
2004
2041
  },
2005
2042
  body: JSON.stringify({
2006
- model: "gpt-5-mini",
2043
+ model: "gpt-5.4-mini",
2007
2044
  messages: [
2008
2045
  { role: "system", content: systemPrompt },
2009
2046
  { role: "user", content: buildStoryboardUserMessage(content) }
2010
2047
  ],
2011
- temperature,
2012
- max_tokens: 4096,
2048
+ // Reasoning tokens count against this cap.
2049
+ max_completion_tokens: 16384,
2013
2050
  response_format: { type: "json_object" }
2014
2051
  })
2015
2052
  });
@@ -2066,7 +2103,7 @@ var init_OpenAIProvider = __esm({
2066
2103
  this.iconUrl = "/icons/openai.svg";
2067
2104
  this.isAvailable = true;
2068
2105
  this.baseUrl = "https://api.openai.com/v1";
2069
- this.model = "gpt-5-mini";
2106
+ this.model = "gpt-5.4-mini";
2070
2107
  }
2071
2108
  async initialize(config2) {
2072
2109
  this.apiKey = config2.apiKey;
@@ -2162,7 +2199,6 @@ Or if clarification needed:
2162
2199
  { role: "system", content: systemPrompt },
2163
2200
  { role: "user", content: instruction }
2164
2201
  ],
2165
- temperature: 0.2,
2166
2202
  response_format: { type: "json_object" }
2167
2203
  })
2168
2204
  });
@@ -2373,8 +2409,9 @@ Each segment should be 3-10 seconds long.`;
2373
2409
  Authorization: `Bearer ${this.apiKey}`
2374
2410
  },
2375
2411
  body: JSON.stringify({
2376
- model: "gpt-5-mini",
2377
- max_tokens: 4096,
2412
+ model: "gpt-5.4-mini",
2413
+ // Reasoning tokens count against this cap.
2414
+ max_completion_tokens: 16384,
2378
2415
  response_format: { type: "json_object" },
2379
2416
  messages: [
2380
2417
  { role: "system", content: systemPrompt },
@@ -2407,7 +2444,7 @@ ${videoAnalysis}` }
2407
2444
  }
2408
2445
  }
2409
2446
  /**
2410
- * Generate a storyboard from script content using GPT-5-mini.
2447
+ * Generate a storyboard from script content using GPT-5.4 mini.
2411
2448
  * Alternative to ClaudeProvider.analyzeContent for when Claude is unavailable.
2412
2449
  */
2413
2450
  async analyzeContent(content, targetDuration, options) {
@@ -2455,10 +2492,12 @@ async function callClaude(params, opts) {
2455
2492
  },
2456
2493
  body: JSON.stringify({
2457
2494
  model: params.model,
2458
- max_tokens: opts.maxTokens,
2459
- ...opts.temperature !== void 0 ? { temperature: opts.temperature } : {},
2495
+ // Claude 5.x thinks by default and thinking tokens count against
2496
+ // max_tokens, so a small cap can end before any text is written.
2497
+ max_tokens: Math.max(opts.maxTokens, CLAUDE_MIN_MAX_TOKENS),
2460
2498
  messages: opts.messages,
2461
- system: opts.system
2499
+ system: opts.system,
2500
+ ...opts.jsonSchema ? { output_config: { format: { type: "json_schema", schema: opts.jsonSchema } } } : {}
2462
2501
  })
2463
2502
  });
2464
2503
  if (!response.ok) {
@@ -2480,9 +2519,11 @@ function extractJsonArray(text) {
2480
2519
  const match = text.match(/\[[\s\S]*\]/);
2481
2520
  return match ? match[0] : null;
2482
2521
  }
2522
+ var CLAUDE_MIN_MAX_TOKENS;
2483
2523
  var init_claude_api = __esm({
2484
2524
  "../ai-providers/dist/claude/claude-api.js"() {
2485
2525
  "use strict";
2526
+ CLAUDE_MIN_MAX_TOKENS = 16e3;
2486
2527
  }
2487
2528
  });
2488
2529
 
@@ -2518,7 +2559,8 @@ MANDATORY ADAPTATION RULES:
2518
2559
  const text = await callClaude(api, {
2519
2560
  system: systemPrompt,
2520
2561
  messages: [{ role: "user", content: userContent }],
2521
- maxTokens: 16e3
2562
+ maxTokens: 16e3,
2563
+ jsonSchema: MOTION_COMPONENT_SCHEMA
2522
2564
  });
2523
2565
  const jsonStr = extractJsonObject(text);
2524
2566
  if (!jsonStr) {
@@ -2593,7 +2635,8 @@ Modification instructions: ${instructions}
2593
2635
 
2594
2636
  Return the complete modified component as JSON.`
2595
2637
  }],
2596
- maxTokens: 16e3
2638
+ maxTokens: 16e3,
2639
+ jsonSchema: MOTION_COMPONENT_SCHEMA
2597
2640
  });
2598
2641
  const jsonStr = extractJsonObject(text);
2599
2642
  if (!jsonStr)
@@ -2622,15 +2665,13 @@ async function analyzeContent3(api, content, targetDuration, options) {
2622
2665
  const creativity = options?.creativity || "low";
2623
2666
  const systemPrompt = buildStoryboardSystemPrompt(targetDuration, creativity);
2624
2667
  try {
2625
- const temperature = creativity === "high" ? 1 : 0.7;
2626
2668
  const text = await callClaude(api, {
2627
2669
  system: systemPrompt,
2628
2670
  messages: [{
2629
2671
  role: "user",
2630
2672
  content: buildStoryboardUserMessage(content)
2631
2673
  }],
2632
- maxTokens: 4096,
2633
- temperature
2674
+ maxTokens: 4096
2634
2675
  });
2635
2676
  const jsonStr = extractJsonArray(text);
2636
2677
  if (!jsonStr) {
@@ -2987,11 +3028,22 @@ UNIVERSAL RULES:
2987
3028
  - Both entrance AND exit animations
2988
3029
  - whiteSpace: "nowrap" on all text`;
2989
3030
  }
3031
+ var MOTION_COMPONENT_SCHEMA;
2990
3032
  var init_claude_motion = __esm({
2991
3033
  "../ai-providers/dist/claude/claude-motion.js"() {
2992
3034
  "use strict";
2993
3035
  init_claude_api();
2994
3036
  init_storyboard_prompt();
3037
+ MOTION_COMPONENT_SCHEMA = {
3038
+ type: "object",
3039
+ properties: {
3040
+ name: { type: "string" },
3041
+ code: { type: "string" },
3042
+ description: { type: "string" }
3043
+ },
3044
+ required: ["name", "code", "description"],
3045
+ additionalProperties: false
3046
+ };
2995
3047
  }
2996
3048
  });
2997
3049
 
@@ -3753,6 +3805,7 @@ var ClaudeProvider, claudeProvider;
3753
3805
  var init_ClaudeProvider = __esm({
3754
3806
  "../ai-providers/dist/claude/ClaudeProvider.js"() {
3755
3807
  "use strict";
3808
+ init_claude_api();
3756
3809
  init_claude_motion();
3757
3810
  init_claude_analysis();
3758
3811
  init_claude_viral();
@@ -3766,7 +3819,7 @@ var init_ClaudeProvider = __esm({
3766
3819
  this.iconUrl = "/icons/claude.svg";
3767
3820
  this.isAvailable = true;
3768
3821
  this.baseUrl = "https://api.anthropic.com/v1";
3769
- this.model = "claude-sonnet-4-6";
3822
+ this.model = "claude-sonnet-5-5";
3770
3823
  }
3771
3824
  async initialize(config2) {
3772
3825
  this.apiKey = config2.apiKey;
@@ -3870,7 +3923,7 @@ Or if clarification needed:
3870
3923
  },
3871
3924
  body: JSON.stringify({
3872
3925
  model: this.model,
3873
- max_tokens: 2048,
3926
+ max_tokens: CLAUDE_MIN_MAX_TOKENS,
3874
3927
  messages: [{ role: "user", content: instruction }],
3875
3928
  system: systemPrompt
3876
3929
  })
@@ -3940,7 +3993,7 @@ Respond with JSON array:
3940
3993
  },
3941
3994
  body: JSON.stringify({
3942
3995
  model: this.model,
3943
- max_tokens: 2048,
3996
+ max_tokens: CLAUDE_MIN_MAX_TOKENS,
3944
3997
  messages: [{
3945
3998
  role: "user",
3946
3999
  content: `Clips: ${JSON.stringify(clipsInfo)}
@@ -4069,8 +4122,8 @@ Instruction: ${instruction}`
4069
4122
  }
4070
4123
  };
4071
4124
  ClaudeProvider.MOTION_MODELS = {
4072
- sonnet: "claude-sonnet-4-6",
4073
- opus: "claude-opus-4-7",
4125
+ sonnet: "claude-sonnet-5-5",
4126
+ opus: "claude-opus-5-5",
4074
4127
  "opus-4-6": "claude-opus-4-6"
4075
4128
  };
4076
4129
  claudeProvider = new ClaudeProvider();
@@ -4408,7 +4461,7 @@ function resolveVoiceId(input) {
4408
4461
  const availableVoices = Object.keys(KNOWN_VOICES).join(", ");
4409
4462
  throw new Error(`Unknown voice "${input}". Available voices: ${availableVoices}. You can also use a custom voice ID from your ElevenLabs account.`);
4410
4463
  }
4411
- var KNOWN_VOICES, TTS_429_MAX_RETRIES, TTS_429_RETRY_DELAY_MS, ElevenLabsProvider, elevenLabsProvider;
4464
+ var KNOWN_VOICES, TTS_429_MAX_RETRIES, TTS_429_RETRY_DELAY_MS, ELEVENLABS_MUSIC_MODEL, ElevenLabsProvider, elevenLabsProvider;
4412
4465
  var init_ElevenLabsProvider = __esm({
4413
4466
  "../ai-providers/dist/elevenlabs/ElevenLabsProvider.js"() {
4414
4467
  "use strict";
@@ -4445,6 +4498,7 @@ var init_ElevenLabsProvider = __esm({
4445
4498
  };
4446
4499
  TTS_429_MAX_RETRIES = 1;
4447
4500
  TTS_429_RETRY_DELAY_MS = 2e3;
4501
+ ELEVENLABS_MUSIC_MODEL = "music_v2_5";
4448
4502
  ElevenLabsProvider = class {
4449
4503
  constructor() {
4450
4504
  this.id = "elevenlabs";
@@ -4525,9 +4579,10 @@ var init_ElevenLabsProvider = __esm({
4525
4579
  stability: options.stability ?? 0.5,
4526
4580
  similarity_boost: options.similarityBoost ?? 0.75,
4527
4581
  style: options.style ?? 0,
4528
- use_speaker_boost: true
4529
- },
4530
- ...options.speed !== void 0 && { speed: options.speed }
4582
+ use_speaker_boost: true,
4583
+ // The API reads speed only inside voice_settings.
4584
+ ...options.speed !== void 0 && { speed: options.speed }
4585
+ }
4531
4586
  })
4532
4587
  });
4533
4588
  if (response.status === 429 && attempt < TTS_429_MAX_RETRIES) {
@@ -4597,7 +4652,7 @@ var init_ElevenLabsProvider = __esm({
4597
4652
  prompt_influence: options.promptInfluence ?? 0.3
4598
4653
  };
4599
4654
  if (options.duration !== void 0) {
4600
- const duration = Math.max(0.5, Math.min(22, options.duration));
4655
+ const duration = Math.max(0.5, Math.min(30, options.duration));
4601
4656
  body.duration_seconds = duration;
4602
4657
  }
4603
4658
  const response = await fetch(`${this.baseUrl}/sound-generation`, {
@@ -4642,7 +4697,7 @@ var init_ElevenLabsProvider = __esm({
4642
4697
  try {
4643
4698
  const body = {
4644
4699
  prompt: prompt3,
4645
- model_id: "music_v1"
4700
+ model_id: ELEVENLABS_MUSIC_MODEL
4646
4701
  };
4647
4702
  if (options.duration !== void 0) {
4648
4703
  const duration = Math.max(3, Math.min(600, options.duration));
@@ -4853,6 +4908,55 @@ var init_elevenlabs = __esm({
4853
4908
  });
4854
4909
 
4855
4910
  // ../ai-providers/dist/kokoro/KokoroProvider.js
4911
+ function splitForKokoro(text, maxChars = KOKORO_MAX_CHUNK_CHARS) {
4912
+ const pieces = (part, pattern) => part.length <= maxChars ? [part] : part.split(pattern).filter((p) => p.trim().length > 0);
4913
+ const units2 = text.trim().split(/(?<=[.!?…])\s+/).flatMap((sentence) => pieces(sentence, /(?<=[,;:])\s+/)).flatMap((clause) => {
4914
+ if (clause.length <= maxChars)
4915
+ return [clause];
4916
+ const words = [];
4917
+ let line = "";
4918
+ for (const word of clause.split(/\s+/)) {
4919
+ if (line && line.length + word.length + 1 > maxChars) {
4920
+ words.push(line);
4921
+ line = word;
4922
+ } else {
4923
+ line = line ? `${line} ${word}` : word;
4924
+ }
4925
+ }
4926
+ if (line)
4927
+ words.push(line);
4928
+ return words;
4929
+ });
4930
+ const chunks = [];
4931
+ for (const unit of units2) {
4932
+ const last = chunks.at(-1);
4933
+ if (last !== void 0 && last.length + unit.length + 1 <= maxChars) {
4934
+ chunks[chunks.length - 1] = `${last} ${unit}`;
4935
+ } else {
4936
+ chunks.push(unit);
4937
+ }
4938
+ }
4939
+ return chunks;
4940
+ }
4941
+ function encodeFloatWav(samples, sampleRate) {
4942
+ const dataBytes = samples.length * 4;
4943
+ const buffer = Buffer.alloc(44 + dataBytes);
4944
+ buffer.write("RIFF", 0, "ascii");
4945
+ buffer.writeUInt32LE(36 + dataBytes, 4);
4946
+ buffer.write("WAVE", 8, "ascii");
4947
+ buffer.write("fmt ", 12, "ascii");
4948
+ buffer.writeUInt32LE(16, 16);
4949
+ buffer.writeUInt16LE(3, 20);
4950
+ buffer.writeUInt16LE(1, 22);
4951
+ buffer.writeUInt32LE(sampleRate, 24);
4952
+ buffer.writeUInt32LE(sampleRate * 4, 28);
4953
+ buffer.writeUInt16LE(4, 32);
4954
+ buffer.writeUInt16LE(32, 34);
4955
+ buffer.write("data", 36, "ascii");
4956
+ buffer.writeUInt32LE(dataBytes, 40);
4957
+ Buffer.from(samples.buffer, samples.byteOffset, dataBytes).copy(buffer, 44);
4958
+ return buffer;
4959
+ }
4856
4960
  async function loadKokoroRuntime() {
4857
4961
  if (factoryOverride)
4858
4962
  return { factory: factoryOverride, device: "cpu" };
@@ -4890,7 +4994,7 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
4890
4994
  if (!base)
4891
4995
  return null;
4892
4996
  try {
4893
- const { readFile: readFile44, writeFile: writeFile48, mkdir: mkdir26 } = await import("node:fs/promises");
4997
+ const { readFile: readFile44, writeFile: writeFile47, mkdir: mkdir27 } = await import("node:fs/promises");
4894
4998
  const { join: join29, resolve: resolve65 } = await import("node:path");
4895
4999
  const { pathToFileURL } = await import("node:url");
4896
5000
  const { homedir: homedir5 } = await import("node:os");
@@ -4911,8 +5015,8 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
4911
5015
  async put(key, response) {
4912
5016
  try {
4913
5017
  const buf = Buffer.from(await response.arrayBuffer());
4914
- await mkdir26(cacheRoot, { recursive: true });
4915
- await writeFile48(join29(cacheRoot, encodeURIComponent(String(key))), buf);
5018
+ await mkdir27(cacheRoot, { recursive: true });
5019
+ await writeFile47(join29(cacheRoot, encodeURIComponent(String(key))), buf);
4916
5020
  } catch {
4917
5021
  }
4918
5022
  }
@@ -4961,12 +5065,13 @@ function normaliseEvent(raw) {
4961
5065
  total: typeof r.total === "number" ? r.total : void 0
4962
5066
  };
4963
5067
  }
4964
- var KOKORO_DEFAULT_VOICE, KOKORO_MODEL_ID, modelPromise, factoryOverride, KokoroProvider, kokoroProvider;
5068
+ var KOKORO_DEFAULT_VOICE, KOKORO_MODEL_ID, KOKORO_MAX_CHUNK_CHARS, modelPromise, factoryOverride, KokoroProvider, kokoroProvider;
4965
5069
  var init_KokoroProvider = __esm({
4966
5070
  "../ai-providers/dist/kokoro/KokoroProvider.js"() {
4967
5071
  "use strict";
4968
5072
  KOKORO_DEFAULT_VOICE = "af_heart";
4969
5073
  KOKORO_MODEL_ID = "onnx-community/Kokoro-82M-v1.0-ONNX";
5074
+ KOKORO_MAX_CHUNK_CHARS = 300;
4970
5075
  modelPromise = null;
4971
5076
  factoryOverride = null;
4972
5077
  KokoroProvider = class {
@@ -4993,11 +5098,32 @@ var init_KokoroProvider = __esm({
4993
5098
  }
4994
5099
  try {
4995
5100
  const model = await loadModel(options.onProgress);
4996
- const audio = await model.generate(text, {
5101
+ const voiceOptions = {
4997
5102
  voice: options.voice ?? KOKORO_DEFAULT_VOICE,
4998
5103
  speed: options.speed ?? 1
4999
- });
5000
- const buffer = Buffer.from(audio.toWav());
5104
+ };
5105
+ const chunks = splitForKokoro(text);
5106
+ let buffer;
5107
+ if (chunks.length === 1) {
5108
+ buffer = Buffer.from((await model.generate(chunks[0], voiceOptions)).toWav());
5109
+ } else {
5110
+ const parts = [];
5111
+ let sampleRate = 24e3;
5112
+ for (const chunk of chunks) {
5113
+ const audio = await model.generate(chunk, voiceOptions);
5114
+ if (!audio.audio)
5115
+ throw new Error("Kokoro returned no raw samples to join");
5116
+ parts.push(audio.audio);
5117
+ sampleRate = audio.sampling_rate ?? sampleRate;
5118
+ }
5119
+ const samples = new Float32Array(parts.reduce((n, part) => n + part.length, 0));
5120
+ let offset = 0;
5121
+ for (const part of parts) {
5122
+ samples.set(part, offset);
5123
+ offset += part.length;
5124
+ }
5125
+ buffer = encodeFloatWav(samples, sampleRate);
5126
+ }
5001
5127
  return {
5002
5128
  success: true,
5003
5129
  audioBuffer: buffer,
@@ -5032,11 +5158,12 @@ var init_kokoro = __esm({
5032
5158
  });
5033
5159
 
5034
5160
  // ../ai-providers/dist/openai-image/OpenAIImageProvider.js
5035
- var DEFAULT_MODEL, OpenAIImageProvider, openaiImageProvider;
5161
+ var OPENAI_IMAGE_DEFAULT_MODEL, DEFAULT_MODEL, OpenAIImageProvider, openaiImageProvider;
5036
5162
  var init_OpenAIImageProvider = __esm({
5037
5163
  "../ai-providers/dist/openai-image/OpenAIImageProvider.js"() {
5038
5164
  "use strict";
5039
- DEFAULT_MODEL = "gpt-image-2";
5165
+ OPENAI_IMAGE_DEFAULT_MODEL = "gpt-image-2.5-sunburst";
5166
+ DEFAULT_MODEL = OPENAI_IMAGE_DEFAULT_MODEL;
5040
5167
  OpenAIImageProvider = class {
5041
5168
  constructor() {
5042
5169
  this.id = "openai-image";
@@ -5058,7 +5185,7 @@ var init_OpenAIImageProvider = __esm({
5058
5185
  }
5059
5186
  /**
5060
5187
  * Generate images from text prompt
5061
- * Uses GPT Image 1.5 by default (fastest, best quality)
5188
+ * Uses GPT Image 2.5 Sunburst by default
5062
5189
  */
5063
5190
  async generateImage(prompt3, options = {}) {
5064
5191
  if (!this.apiKey) {
@@ -5068,28 +5195,20 @@ var init_OpenAIImageProvider = __esm({
5068
5195
  };
5069
5196
  }
5070
5197
  const model = options.model || DEFAULT_MODEL;
5071
- const isGPTImage = model === "gpt-image-1.5" || model === "gpt-image-2";
5072
5198
  try {
5073
5199
  const body = {
5074
5200
  model,
5075
5201
  prompt: prompt3,
5076
5202
  n: options.n || 1
5077
5203
  };
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";
5204
+ const qualityMap = {
5205
+ standard: "medium",
5206
+ hd: "high"
5207
+ };
5208
+ const quality = options.quality || "medium";
5209
+ body.quality = qualityMap[quality] || quality;
5210
+ if (options.size && options.size !== "auto") {
5211
+ body.size = options.size;
5093
5212
  }
5094
5213
  const response = await fetch(`${this.baseUrl}/images/generations`, {
5095
5214
  method: "POST",
@@ -5242,49 +5361,6 @@ var init_OpenAIImageProvider = __esm({
5242
5361
  };
5243
5362
  }
5244
5363
  }
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 data = await response.json();
5277
- return {
5278
- success: true,
5279
- images: data.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
5364
  };
5289
5365
  openaiImageProvider = new OpenAIImageProvider();
5290
5366
  }
@@ -5715,12 +5791,33 @@ var init_runway = __esm({
5715
5791
 
5716
5792
  // ../ai-providers/dist/kling/KlingProvider.js
5717
5793
  import { createHmac } from "node:crypto";
5794
+ function resolveKlingModel(alias) {
5795
+ const key = (alias ?? "v3").trim().toLowerCase();
5796
+ const aliases = {
5797
+ v3: "kling-v3",
5798
+ "kling-v3": "kling-v3",
5799
+ "v2.6": "kling-v2-6",
5800
+ "kling-v2-6": "kling-v2-6",
5801
+ "v2.5-turbo": "kling-v2-5-turbo",
5802
+ "v2.5": "kling-v2-5-turbo",
5803
+ "kling-v2-5-turbo": "kling-v2-5-turbo"
5804
+ };
5805
+ if (Object.hasOwn(aliases, key))
5806
+ return aliases[key];
5807
+ throw new Error(`Unknown Kling model "${alias}". Valid: v3, v2.6, v2.5-turbo.`);
5808
+ }
5809
+ function klingDuration(model, seconds) {
5810
+ const requested = typeof seconds === "number" && Number.isFinite(seconds) ? Math.round(seconds) : 5;
5811
+ if (model === "kling-v3")
5812
+ return String(Math.max(3, Math.min(15, requested)));
5813
+ return requested >= 10 ? "10" : "5";
5814
+ }
5718
5815
  var DEFAULT_MODEL4, STD_MODE_MODELS, KlingProvider, klingProvider;
5719
5816
  var init_KlingProvider = __esm({
5720
5817
  "../ai-providers/dist/kling/KlingProvider.js"() {
5721
5818
  "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"];
5819
+ DEFAULT_MODEL4 = "kling-v3";
5820
+ STD_MODE_MODELS = ["kling-v2-5-turbo", "kling-v2-6", "kling-v3"];
5724
5821
  KlingProvider = class {
5725
5822
  constructor() {
5726
5823
  this.id = "kling";
@@ -5735,11 +5832,11 @@ var init_KlingProvider = __esm({
5735
5832
  async initialize(config2) {
5736
5833
  if (config2.apiKey) {
5737
5834
  const parts = config2.apiKey.split(":");
5738
- if (parts.length === 2) {
5835
+ if (parts.length === 2 && parts[0] && parts[1]) {
5739
5836
  this.accessKey = parts[0];
5740
5837
  this.secretKey = parts[1];
5741
5838
  } else {
5742
- this.accessKey = config2.apiKey;
5839
+ this.apiKey = config2.apiKey;
5743
5840
  }
5744
5841
  }
5745
5842
  if (config2.baseUrl) {
@@ -5747,7 +5844,11 @@ var init_KlingProvider = __esm({
5747
5844
  }
5748
5845
  }
5749
5846
  isConfigured() {
5750
- return !!(this.accessKey && this.secretKey);
5847
+ return !!(this.apiKey || this.accessKey && this.secretKey);
5848
+ }
5849
+ /** Bearer value: the API key itself, or a JWT signed from the legacy pair. */
5850
+ bearerToken() {
5851
+ return this.apiKey ?? this.generateToken();
5751
5852
  }
5752
5853
  /**
5753
5854
  * Generate JWT token for Kling API authentication
@@ -5777,11 +5878,11 @@ var init_KlingProvider = __esm({
5777
5878
  return {
5778
5879
  id: "",
5779
5880
  status: "failed",
5780
- error: "Kling API credentials not configured. Use format: KLING_ACCESS_KEY:KLING_SECRET_KEY"
5881
+ error: "Kling API credentials not configured. Set KLING_API_KEY to your Kling API key (or the legacy ACCESS_KEY:SECRET_KEY pair)."
5781
5882
  };
5782
5883
  }
5783
5884
  try {
5784
- const token = this.generateToken();
5885
+ const token = this.bearerToken();
5785
5886
  const model = options?.model || DEFAULT_MODEL4;
5786
5887
  let mode = options?.mode || "std";
5787
5888
  if (!STD_MODE_MODELS.includes(model) && mode === "std") {
@@ -5799,7 +5900,7 @@ var init_KlingProvider = __esm({
5799
5900
  model_name: model,
5800
5901
  mode,
5801
5902
  aspect_ratio: aspectRatioMap[options?.aspectRatio || "16:9"] || "16:9",
5802
- duration: options?.duration === 10 ? "10" : "5"
5903
+ duration: klingDuration(model, options?.duration)
5803
5904
  };
5804
5905
  if (options?.negativePrompt) {
5805
5906
  body.negative_prompt = options.negativePrompt;
@@ -5911,7 +6012,7 @@ var init_KlingProvider = __esm({
5911
6012
  };
5912
6013
  }
5913
6014
  try {
5914
- const token = this.generateToken();
6015
+ const token = this.bearerToken();
5915
6016
  const response = await fetch(`${this.baseUrl}/videos/${type}/${id}`, {
5916
6017
  headers: {
5917
6018
  "Content-Type": "application/json",
@@ -5993,11 +6094,11 @@ var init_KlingProvider = __esm({
5993
6094
  return {
5994
6095
  id: "",
5995
6096
  status: "failed",
5996
- error: "Kling API credentials not configured. Use format: KLING_ACCESS_KEY:KLING_SECRET_KEY"
6097
+ error: "Kling API credentials not configured. Set KLING_API_KEY to your Kling API key (or the legacy ACCESS_KEY:SECRET_KEY pair)."
5997
6098
  };
5998
6099
  }
5999
6100
  try {
6000
- const token = this.generateToken();
6101
+ const token = this.bearerToken();
6001
6102
  const body = {
6002
6103
  video_id: videoId,
6003
6104
  duration: options?.duration || "5"
@@ -6037,7 +6138,7 @@ var init_KlingProvider = __esm({
6037
6138
  };
6038
6139
  }
6039
6140
  try {
6040
- const token = this.generateToken();
6141
+ const token = this.bearerToken();
6041
6142
  const response = await fetch(`${this.baseUrl}/videos/video-extend/${id}`, {
6042
6143
  headers: {
6043
6144
  "Content-Type": "application/json",
@@ -6153,11 +6254,34 @@ var init_kling = __esm({
6153
6254
  });
6154
6255
 
6155
6256
  // ../ai-providers/dist/grok/GrokProvider.js
6156
- var DEFAULT_MODEL5, GrokProvider, grokProvider;
6257
+ function resolveGrokVideoModel(alias) {
6258
+ const key = (alias ?? "1.5").trim().toLowerCase();
6259
+ const aliases = {
6260
+ "1.5": "grok-imagine-video-1.5",
6261
+ "grok-imagine-video-1.5": "grok-imagine-video-1.5",
6262
+ lite: "grok-imagine-video-1.5-lite",
6263
+ "1.5-lite": "grok-imagine-video-1.5-lite",
6264
+ "grok-imagine-video-1.5-lite": "grok-imagine-video-1.5-lite",
6265
+ classic: "grok-imagine-video",
6266
+ "grok-imagine-video": "grok-imagine-video"
6267
+ };
6268
+ if (Object.hasOwn(aliases, key))
6269
+ return aliases[key];
6270
+ throw new Error(`Unknown Grok video model "${alias}". Valid: 1.5, lite, classic.`);
6271
+ }
6272
+ function resolveGrokImageModel(alias) {
6273
+ if (alias === "pro" || alias === "2.0" || alias === "quality" || alias === "grok-imagine-image-2.0") {
6274
+ return { model: "grok-imagine-image-2.0", quality: "medium" };
6275
+ }
6276
+ return { model: "grok-imagine-image" };
6277
+ }
6278
+ var DEFAULT_MODEL5, DEFAULT_VIDEO_RESOLUTION, GROK_VIDEO_RESOLUTIONS, GrokProvider, grokProvider;
6157
6279
  var init_GrokProvider = __esm({
6158
6280
  "../ai-providers/dist/grok/GrokProvider.js"() {
6159
6281
  "use strict";
6160
- DEFAULT_MODEL5 = "grok-imagine-video";
6282
+ DEFAULT_MODEL5 = "grok-imagine-video-1.5";
6283
+ DEFAULT_VIDEO_RESOLUTION = "720p";
6284
+ GROK_VIDEO_RESOLUTIONS = ["480p", "720p", "1080p"];
6161
6285
  GrokProvider = class {
6162
6286
  constructor() {
6163
6287
  this.id = "grok";
@@ -6191,6 +6315,7 @@ var init_GrokProvider = __esm({
6191
6315
  try {
6192
6316
  const body = {
6193
6317
  model: options.model || "grok-imagine-image",
6318
+ ...options.quality ? { quality: options.quality } : {},
6194
6319
  prompt: prompt3,
6195
6320
  n: options.n || 1,
6196
6321
  response_format: options.responseFormat || "url"
@@ -6258,6 +6383,7 @@ var init_GrokProvider = __esm({
6258
6383
  const dataUri = `data:image/png;base64,${base64}`;
6259
6384
  const body = {
6260
6385
  model: options.model || "grok-imagine-image",
6386
+ ...options.quality ? { quality: options.quality } : {},
6261
6387
  prompt: prompt3,
6262
6388
  image: {
6263
6389
  url: dataUri,
@@ -6324,11 +6450,15 @@ var init_GrokProvider = __esm({
6324
6450
  try {
6325
6451
  const duration = Math.round(Math.min(15, Math.max(1, options?.duration || 5)));
6326
6452
  const body = {
6327
- model: DEFAULT_MODEL5,
6453
+ model: options?.model || DEFAULT_MODEL5,
6328
6454
  prompt: prompt3,
6329
6455
  duration,
6330
- aspect_ratio: options?.aspectRatio || "16:9"
6456
+ aspect_ratio: options?.aspectRatio || "16:9",
6457
+ resolution: GROK_VIDEO_RESOLUTIONS.includes(options?.resolution ?? "") ? options.resolution : DEFAULT_VIDEO_RESOLUTION
6331
6458
  };
6459
+ if (typeof options?.generateAudio === "boolean") {
6460
+ body.generate_audio = options.generateAudio;
6461
+ }
6332
6462
  if (options?.referenceImage) {
6333
6463
  body.image = { url: options.referenceImage };
6334
6464
  }
@@ -6390,13 +6520,14 @@ var init_GrokProvider = __esm({
6390
6520
  const statusMap = {
6391
6521
  pending: "pending",
6392
6522
  done: "completed",
6393
- expired: "failed"
6523
+ expired: "failed",
6524
+ failed: "failed"
6394
6525
  };
6395
6526
  return {
6396
6527
  id,
6397
6528
  status: statusMap[data.status] || "pending",
6398
6529
  videoUrl: data.video?.url,
6399
- error: data.status === "expired" ? "Generation expired" : void 0
6530
+ error: data.status === "expired" ? "Generation expired" : data.status === "failed" ? `Generation failed: ${data.error?.message ?? data.error?.code ?? "unknown error"}` : void 0
6400
6531
  };
6401
6532
  } catch (error) {
6402
6533
  return {
@@ -10590,14 +10721,30 @@ var require_src = __commonJS({
10590
10721
  });
10591
10722
 
10592
10723
  // ../ai-providers/dist/fal/FalProvider.js
10724
+ function resolveSeedanceVariant(alias) {
10725
+ const key = (alias ?? "quality").trim().toLowerCase();
10726
+ const aliases = {
10727
+ quality: "seedance-2.0",
10728
+ "2.0": "seedance-2.0",
10729
+ "seedance-2.0": "seedance-2.0",
10730
+ fast: "seedance-2.0-fast",
10731
+ "seedance-2.0-fast": "seedance-2.0-fast",
10732
+ "2.5": "seedance-2.5",
10733
+ "seedance-2.5": "seedance-2.5"
10734
+ };
10735
+ if (Object.hasOwn(aliases, key))
10736
+ return aliases[key];
10737
+ throw new Error(`Unknown Seedance model "${alias}". Valid: quality, fast, 2.5.`);
10738
+ }
10593
10739
  function estimateSeedanceVideoCostUsd(opts) {
10594
10740
  const shortSide = SEEDANCE_SHORT_SIDE[opts.resolution ?? "720p"] ?? 720;
10595
10741
  const [a, b] = (opts.aspectRatio ?? "16:9").split(":").map(Number);
10596
10742
  const ratioFactor = a > 0 && b > 0 ? Math.max(a, b) / Math.min(a, b) : 16 / 9;
10597
10743
  const longSide = Math.round(shortSide * ratioFactor);
10598
10744
  const tokensPerSecond = shortSide * longSide * 24 / 1024;
10599
- let usdPerSecond = tokensPerSecond / 1e3 * SEEDANCE_USD_PER_1K_TOKENS;
10600
- if (opts.fast && shortSide <= 720)
10745
+ const rate = opts.v25 ? shortSide >= 1080 ? SEEDANCE_25_1080P_USD_PER_1K_TOKENS : SEEDANCE_25_USD_PER_1K_TOKENS : SEEDANCE_USD_PER_1K_TOKENS;
10746
+ let usdPerSecond = tokensPerSecond / 1e3 * rate;
10747
+ if (opts.fast && !opts.v25 && shortSide <= 720)
10601
10748
  usdPerSecond *= SEEDANCE_FAST_FACTOR;
10602
10749
  if (opts.hasVideoReference)
10603
10750
  usdPerSecond *= SEEDANCE_VIDEO_REF_FACTOR;
@@ -10705,34 +10852,42 @@ function normaliseResolution(value) {
10705
10852
  return "1080p";
10706
10853
  return "720p";
10707
10854
  }
10708
- function normaliseDuration(value) {
10709
- if (typeof value !== "number")
10710
- return "auto";
10711
- if (!Number.isFinite(value))
10855
+ function normaliseDuration(value, maxSec) {
10856
+ if (typeof value !== "number" || !Number.isFinite(value))
10712
10857
  return "auto";
10713
- return Math.max(4, Math.min(15, Math.round(value)));
10858
+ return String(Math.max(4, Math.min(maxSec, Math.round(value))));
10714
10859
  }
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;
10860
+ 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
10861
  var init_FalProvider = __esm({
10717
10862
  "../ai-providers/dist/fal/FalProvider.js"() {
10718
10863
  "use strict";
10719
10864
  import_client = __toESM(require_src(), 1);
10720
10865
  ENDPOINT_TEXT_TO_VIDEO = {
10721
10866
  "seedance-2.0": "bytedance/seedance-2.0/text-to-video",
10722
- "seedance-2.0-fast": "bytedance/seedance-2.0/fast/text-to-video"
10867
+ "seedance-2.0-fast": "bytedance/seedance-2.0/fast/text-to-video",
10868
+ "seedance-2.5": "bytedance/seedance-2.5/text-to-video"
10723
10869
  };
10724
10870
  ENDPOINT_IMAGE_TO_VIDEO = {
10725
10871
  "seedance-2.0": "bytedance/seedance-2.0/image-to-video",
10726
- "seedance-2.0-fast": "bytedance/seedance-2.0/fast/image-to-video"
10872
+ "seedance-2.0-fast": "bytedance/seedance-2.0/fast/image-to-video",
10873
+ "seedance-2.5": "bytedance/seedance-2.5/image-to-video"
10727
10874
  };
10728
10875
  ENDPOINT_REFERENCE_TO_VIDEO = {
10729
10876
  "seedance-2.0": "bytedance/seedance-2.0/reference-to-video",
10730
- "seedance-2.0-fast": "bytedance/seedance-2.0/fast/reference-to-video"
10877
+ "seedance-2.0-fast": "bytedance/seedance-2.0/fast/reference-to-video",
10878
+ "seedance-2.5": "bytedance/seedance-2.5/reference-to-video"
10731
10879
  };
10732
10880
  DEFAULT_VARIANT = "seedance-2.0";
10881
+ MAX_DURATION_SEC = {
10882
+ "seedance-2.0": 15,
10883
+ "seedance-2.0-fast": 15,
10884
+ "seedance-2.5": 30
10885
+ };
10733
10886
  VALID_RESOLUTIONS = ["480p", "720p", "1080p"];
10734
10887
  VALID_ASPECTS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "auto"];
10735
10888
  SEEDANCE_USD_PER_1K_TOKENS = 0.014;
10889
+ SEEDANCE_25_USD_PER_1K_TOKENS = 0.0214;
10890
+ SEEDANCE_25_1080P_USD_PER_1K_TOKENS = 0.0234;
10736
10891
  SEEDANCE_FAST_FACTOR = 0.8;
10737
10892
  SEEDANCE_VIDEO_REF_FACTOR = 0.6;
10738
10893
  SEEDANCE_SHORT_SIDE = { "480p": 480, "720p": 720, "1080p": 1080 };
@@ -10787,7 +10942,7 @@ var init_FalProvider = __esm({
10787
10942
  const endpointId = hasReferences ? ENDPOINT_REFERENCE_TO_VIDEO[variant] : isImageToVideo ? ENDPOINT_IMAGE_TO_VIDEO[variant] : ENDPOINT_TEXT_TO_VIDEO[variant];
10788
10943
  const aspect = normaliseAspect(options?.aspectRatio);
10789
10944
  const resolution = normaliseResolution(options?.resolution);
10790
- const duration = normaliseDuration(options?.duration);
10945
+ const duration = normaliseDuration(options?.duration, MAX_DURATION_SEC[variant]);
10791
10946
  const input = {
10792
10947
  prompt: prompt3,
10793
10948
  aspect_ratio: aspect,
@@ -10805,10 +10960,12 @@ var init_FalProvider = __esm({
10805
10960
  } else if (referenceImage) {
10806
10961
  input.image_url = referenceImage;
10807
10962
  }
10808
- if (options?.negativePrompt)
10809
- input.negative_prompt = options.negativePrompt;
10810
- if (typeof options?.seed === "number")
10811
- input.seed = options.seed;
10963
+ if (variant !== "seedance-2.5") {
10964
+ if (options?.negativePrompt)
10965
+ input.negative_prompt = options.negativePrompt;
10966
+ if (typeof options?.seed === "number")
10967
+ input.seed = options.seed;
10968
+ }
10812
10969
  if (typeof options?.generateAudio === "boolean")
10813
10970
  input.generate_audio = options.generateAudio;
10814
10971
  if (options?.endUserId)
@@ -11535,6 +11692,7 @@ var init_replicate = __esm({
11535
11692
  var dist_exports2 = {};
11536
11693
  __export(dist_exports2, {
11537
11694
  ClaudeProvider: () => ClaudeProvider,
11695
+ ELEVENLABS_MUSIC_MODEL: () => ELEVENLABS_MUSIC_MODEL,
11538
11696
  ElevenLabsProvider: () => ElevenLabsProvider,
11539
11697
  FalProvider: () => FalProvider,
11540
11698
  GEMINI_AGENT_DEFAULT_TEXT_MODEL: () => GEMINI_AGENT_DEFAULT_TEXT_MODEL,
@@ -11548,6 +11706,7 @@ __export(dist_exports2, {
11548
11706
  KOKORO_MODEL_ID: () => KOKORO_MODEL_ID,
11549
11707
  KlingProvider: () => KlingProvider,
11550
11708
  KokoroProvider: () => KokoroProvider,
11709
+ OPENAI_IMAGE_DEFAULT_MODEL: () => OPENAI_IMAGE_DEFAULT_MODEL,
11551
11710
  OPENAI_TTS_VOICES: () => OPENAI_TTS_VOICES,
11552
11711
  OllamaProvider: () => OllamaProvider,
11553
11712
  OmniProvider: () => OmniProvider,
@@ -11556,6 +11715,7 @@ __export(dist_exports2, {
11556
11715
  OpenAiTtsProvider: () => OpenAiTtsProvider,
11557
11716
  ReplicateProvider: () => ReplicateProvider,
11558
11717
  RunwayProvider: () => RunwayProvider,
11718
+ VEO_MODEL_ALIASES: () => VEO_MODEL_ALIASES,
11559
11719
  WhisperProvider: () => WhisperProvider,
11560
11720
  claudeProvider: () => claudeProvider,
11561
11721
  defineApiKey: () => defineApiKey,
@@ -11576,6 +11736,7 @@ __export(dist_exports2, {
11576
11736
  getSetupProviders: () => getSetupProviders,
11577
11737
  grokProvider: () => grokProvider,
11578
11738
  isGeminiTextModelAlias: () => isGeminiTextModelAlias,
11739
+ klingDuration: () => klingDuration,
11579
11740
  klingProvider: () => klingProvider,
11580
11741
  kokoroProvider: () => kokoroProvider,
11581
11742
  loadBundledKokoroRuntime: () => loadBundledKokoroRuntime,
@@ -11588,6 +11749,11 @@ __export(dist_exports2, {
11588
11749
  providerRegistry: () => providerRegistry,
11589
11750
  replicateProvider: () => replicateProvider,
11590
11751
  resolveGeminiTextModel: () => resolveGeminiTextModel,
11752
+ resolveGrokImageModel: () => resolveGrokImageModel,
11753
+ resolveGrokVideoModel: () => resolveGrokVideoModel,
11754
+ resolveKlingModel: () => resolveKlingModel,
11755
+ resolveSeedanceVariant: () => resolveSeedanceVariant,
11756
+ resolveVeoModel: () => resolveVeoModel,
11591
11757
  resolveVoiceId: () => resolveVoiceId,
11592
11758
  runwayProvider: () => runwayProvider,
11593
11759
  whisperProvider: () => whisperProvider
@@ -20653,8 +20819,14 @@ __export(ai_video_exports, {
20653
20819
  executeVideoGenerate: () => executeVideoGenerate,
20654
20820
  executeVideoStatus: () => executeVideoStatus
20655
20821
  });
20656
- import { readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
20657
- import { resolve as resolve3 } from "node:path";
20822
+ import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
20823
+ import { dirname as dirname2, resolve as resolve3 } from "node:path";
20824
+ async function saveOutput(output, buffer) {
20825
+ const outputPath = resolve3(process.cwd(), output);
20826
+ await mkdir2(dirname2(outputPath), { recursive: true });
20827
+ await writeFile3(outputPath, buffer);
20828
+ return outputPath;
20829
+ }
20658
20830
  async function executeVideoGenerate(options) {
20659
20831
  const {
20660
20832
  prompt: prompt3,
@@ -20671,6 +20843,8 @@ async function executeVideoGenerate(options) {
20671
20843
  resolution,
20672
20844
  veoModel = "3.1-fast",
20673
20845
  seedanceModel = "quality",
20846
+ grokModel,
20847
+ klingModel,
20674
20848
  generateAudio,
20675
20849
  output,
20676
20850
  wait = true,
@@ -20725,7 +20899,7 @@ async function executeVideoGenerate(options) {
20725
20899
  });
20726
20900
  falImage = upload.url;
20727
20901
  }
20728
- const model = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
20902
+ const model = resolveSeedanceVariant(seedanceModel);
20729
20903
  const result = await fal.generateVideo(prompt3, {
20730
20904
  prompt: prompt3,
20731
20905
  referenceImage: references.length > 0 ? void 0 : falImage,
@@ -20742,8 +20916,7 @@ async function executeVideoGenerate(options) {
20742
20916
  let outputPath;
20743
20917
  if (output && result.videoUrl) {
20744
20918
  const buffer = await downloadVideo(result.videoUrl, key);
20745
- outputPath = resolve3(process.cwd(), output);
20746
- await writeFile3(outputPath, buffer);
20919
+ outputPath = await saveOutput(output, buffer);
20747
20920
  }
20748
20921
  return {
20749
20922
  success: true,
@@ -20774,8 +20947,7 @@ async function executeVideoGenerate(options) {
20774
20947
  let outputPath;
20775
20948
  if (output && finalResult.videoUrl) {
20776
20949
  const buffer = await downloadVideo(finalResult.videoUrl, key);
20777
- outputPath = resolve3(process.cwd(), output);
20778
- await writeFile3(outputPath, buffer);
20950
+ outputPath = await saveOutput(output, buffer);
20779
20951
  }
20780
20952
  return {
20781
20953
  success: true,
@@ -20801,6 +20973,7 @@ async function executeVideoGenerate(options) {
20801
20973
  }
20802
20974
  const result = await kling.generateVideo(prompt3, {
20803
20975
  prompt: prompt3,
20976
+ model: resolveKlingModel(klingModel),
20804
20977
  referenceImage: klingImage,
20805
20978
  duration,
20806
20979
  aspectRatio: ratio,
@@ -20819,8 +20992,7 @@ async function executeVideoGenerate(options) {
20819
20992
  let outputPath;
20820
20993
  if (output && finalResult.videoUrl) {
20821
20994
  const buffer = await downloadVideo(finalResult.videoUrl, key);
20822
- outputPath = resolve3(process.cwd(), output);
20823
- await writeFile3(outputPath, buffer);
20995
+ outputPath = await saveOutput(output, buffer);
20824
20996
  }
20825
20997
  return {
20826
20998
  success: true,
@@ -20834,12 +21006,7 @@ async function executeVideoGenerate(options) {
20834
21006
  } else if (provider === "veo") {
20835
21007
  const gemini = new GeminiProvider();
20836
21008
  await gemini.initialize({ apiKey: key });
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";
21009
+ const model = resolveVeoModel(veoModel);
20843
21010
  const veoDuration = duration <= 6 ? 6 : 8;
20844
21011
  const result = await gemini.generateVideo(prompt3, {
20845
21012
  prompt: prompt3,
@@ -20860,8 +21027,7 @@ async function executeVideoGenerate(options) {
20860
21027
  let outputPath;
20861
21028
  if (output && finalResult.videoUrl) {
20862
21029
  const buffer = await downloadVideo(finalResult.videoUrl, key);
20863
- outputPath = resolve3(process.cwd(), output);
20864
- await writeFile3(outputPath, buffer);
21030
+ outputPath = await saveOutput(output, buffer);
20865
21031
  }
20866
21032
  return {
20867
21033
  success: true,
@@ -20876,9 +21042,12 @@ async function executeVideoGenerate(options) {
20876
21042
  await grok.initialize({ apiKey: key });
20877
21043
  const result = await grok.generateVideo(prompt3, {
20878
21044
  prompt: prompt3,
21045
+ model: resolveGrokVideoModel(grokModel),
20879
21046
  referenceImage,
20880
21047
  duration,
20881
- aspectRatio: ratio
21048
+ aspectRatio: ratio,
21049
+ resolution,
21050
+ generateAudio
20882
21051
  });
20883
21052
  if (result.status === "failed")
20884
21053
  return { success: false, error: result.error || "Grok generation failed" };
@@ -20891,8 +21060,7 @@ async function executeVideoGenerate(options) {
20891
21060
  let outputPath;
20892
21061
  if (output && finalResult.videoUrl) {
20893
21062
  const buffer = await downloadVideo(finalResult.videoUrl, key);
20894
- outputPath = resolve3(process.cwd(), output);
20895
- await writeFile3(outputPath, buffer);
21063
+ outputPath = await saveOutput(output, buffer);
20896
21064
  }
20897
21065
  return {
20898
21066
  success: true,
@@ -20909,7 +21077,8 @@ async function executeVideoGenerate(options) {
20909
21077
  const result = await omni.generateVideo(prompt3, {
20910
21078
  prompt: prompt3,
20911
21079
  referenceImage,
20912
- aspectRatio: ratio
21080
+ aspectRatio: ratio,
21081
+ resolution
20913
21082
  });
20914
21083
  if (result.status !== "completed" || !result.videoUrl) {
20915
21084
  return { success: false, error: result.error || "Gemini Omni generation failed" };
@@ -20917,8 +21086,7 @@ async function executeVideoGenerate(options) {
20917
21086
  let outputPath;
20918
21087
  if (output) {
20919
21088
  const buffer = await downloadVideo(result.videoUrl, key);
20920
- outputPath = resolve3(process.cwd(), output);
20921
- await writeFile3(outputPath, buffer);
21089
+ outputPath = await saveOutput(output, buffer);
20922
21090
  }
20923
21091
  return {
20924
21092
  success: true,
@@ -21014,8 +21182,7 @@ async function executeVideoStatus(options) {
21014
21182
  let outputPath;
21015
21183
  if (output && result.videoUrl) {
21016
21184
  const buffer = await downloadVideo(result.videoUrl);
21017
- outputPath = resolve3(process.cwd(), output);
21018
- await writeFile3(outputPath, buffer);
21185
+ outputPath = await saveOutput(output, buffer);
21019
21186
  }
21020
21187
  return {
21021
21188
  success: true,
@@ -21037,8 +21204,7 @@ async function executeVideoStatus(options) {
21037
21204
  let outputPath;
21038
21205
  if (output && result.videoUrl) {
21039
21206
  const buffer = await downloadVideo(result.videoUrl, key);
21040
- outputPath = resolve3(process.cwd(), output);
21041
- await writeFile3(outputPath, buffer);
21207
+ outputPath = await saveOutput(output, buffer);
21042
21208
  }
21043
21209
  return {
21044
21210
  success: true,
@@ -21059,8 +21225,7 @@ async function executeVideoStatus(options) {
21059
21225
  let outputPath;
21060
21226
  if (output && result.videoUrl) {
21061
21227
  const buffer = await downloadVideo(result.videoUrl, key);
21062
- outputPath = resolve3(process.cwd(), output);
21063
- await writeFile3(outputPath, buffer);
21228
+ outputPath = await saveOutput(output, buffer);
21064
21229
  }
21065
21230
  return {
21066
21231
  success: true,
@@ -21082,8 +21247,7 @@ async function executeVideoStatus(options) {
21082
21247
  let outputPath;
21083
21248
  if (output && result.videoUrl) {
21084
21249
  const buffer = await downloadVideo(result.videoUrl, key);
21085
- outputPath = resolve3(process.cwd(), output);
21086
- await writeFile3(outputPath, buffer);
21250
+ outputPath = await saveOutput(output, buffer);
21087
21251
  }
21088
21252
  return {
21089
21253
  success: true,
@@ -21153,8 +21317,7 @@ async function executeVideoExtend(options) {
21153
21317
  let outputPath;
21154
21318
  if (output && finalResult.videoUrl) {
21155
21319
  const buffer = await downloadVideo(finalResult.videoUrl, key);
21156
- outputPath = resolve3(process.cwd(), output);
21157
- await writeFile3(outputPath, buffer);
21320
+ outputPath = await saveOutput(output, buffer);
21158
21321
  }
21159
21322
  return {
21160
21323
  success: true,
@@ -21169,12 +21332,7 @@ async function executeVideoExtend(options) {
21169
21332
  if (!key) return { success: false, error: "GOOGLE_API_KEY required" };
21170
21333
  const gemini = new GeminiProvider();
21171
21334
  await gemini.initialize({ apiKey: key });
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";
21335
+ const model = resolveVeoModel(veoModel);
21178
21336
  const result = await gemini.extendVideo(videoId, prompt3, {
21179
21337
  duration,
21180
21338
  model
@@ -21189,8 +21347,7 @@ async function executeVideoExtend(options) {
21189
21347
  let outputPath;
21190
21348
  if (output && finalResult.videoUrl) {
21191
21349
  const buffer = await downloadVideo(finalResult.videoUrl, key);
21192
- outputPath = resolve3(process.cwd(), output);
21193
- await writeFile3(outputPath, buffer);
21350
+ outputPath = await saveOutput(output, buffer);
21194
21351
  }
21195
21352
  return {
21196
21353
  success: true,
@@ -21252,8 +21409,8 @@ var init_music_status = __esm({
21252
21409
 
21253
21410
  // src/commands/_shared/build-asset-metadata.ts
21254
21411
  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";
21412
+ import { mkdir as mkdir3, writeFile as writeFile4 } from "node:fs/promises";
21413
+ import { dirname as dirname3, join } from "node:path";
21257
21414
  function assetMetadataPath(kind, beatId) {
21258
21415
  return `.vibeframe/assets/${kind}-${beatId}.json`;
21259
21416
  }
@@ -21301,7 +21458,7 @@ async function writeAssetMetadata(opts) {
21301
21458
  cachePath: opts.cachePath,
21302
21459
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
21303
21460
  };
21304
- await mkdir2(dirname2(abs), { recursive: true });
21461
+ await mkdir3(dirname3(abs), { recursive: true });
21305
21462
  await writeFile4(abs, JSON.stringify(metadata, null, 2) + "\n", "utf-8");
21306
21463
  }
21307
21464
  var init_build_asset_metadata = __esm({
@@ -22260,7 +22417,7 @@ var init_storyboard_edit = __esm({
22260
22417
 
22261
22418
  // src/commands/_shared/storyboard-source.ts
22262
22419
  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";
22420
+ import { mkdir as mkdir4, readdir, readFile as readFile4, rename, rm, writeFile as writeFile6 } from "node:fs/promises";
22264
22421
  import { basename, join as join3 } from "node:path";
22265
22422
  function sceneFileFrom(dir, filename) {
22266
22423
  const stem = filename.replace(/\.mdx?$/i, "");
@@ -22373,7 +22530,7 @@ ${(0, import_yaml5.stringify)(data, { lineWidth: 0 }).trimEnd()}
22373
22530
  };
22374
22531
  }
22375
22532
  async function writeMigration(projectDir, plan) {
22376
- await mkdir3(join3(projectDir, SCENES_DIRNAME), { recursive: true });
22533
+ await mkdir4(join3(projectDir, SCENES_DIRNAME), { recursive: true });
22377
22534
  for (const file of plan.scenes) {
22378
22535
  await writeFile6(join3(projectDir, file.path), file.contents, "utf-8");
22379
22536
  }
@@ -22395,7 +22552,7 @@ function strayBeatIssues(strayBeatIds) {
22395
22552
  async function upsertScene(projectDir, opts) {
22396
22553
  const beatId = deriveBeatId(opts.beatId);
22397
22554
  const dir = join3(projectDir, SCENES_DIRNAME);
22398
- await mkdir3(dir, { recursive: true });
22555
+ await mkdir4(dir, { recursive: true });
22399
22556
  const cues = {};
22400
22557
  if (opts.duration !== void 0) cues.duration = opts.duration;
22401
22558
  if (opts.narration?.trim()) cues.narration = opts.narration.trim();
@@ -22467,22 +22624,22 @@ var init_storyboard_source = __esm({
22467
22624
  // src/commands/_shared/status-jobs.ts
22468
22625
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
22469
22626
  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";
22627
+ import { copyFile, mkdir as mkdir5, readFile as readFile5, readdir as readdir2, rename as rename2, stat, writeFile as writeFile7 } from "node:fs/promises";
22628
+ import { dirname as dirname4, join as join4, parse as parse2, relative, resolve as resolve5 } from "node:path";
22472
22629
  function isLocalJob(record) {
22473
22630
  return record.jobType === "build" || record.jobType === "render";
22474
22631
  }
22475
22632
  function findProjectRoot(start = process.cwd()) {
22476
22633
  let dir = resolve5(start);
22477
22634
  try {
22478
- if (existsSync3(dir) && statSync(dir).isFile()) dir = dirname3(dir);
22635
+ if (existsSync3(dir) && statSync(dir).isFile()) dir = dirname4(dir);
22479
22636
  } catch {
22480
22637
  }
22481
22638
  for (; ; ) {
22482
22639
  if (existsSync3(join4(dir, "vibe.config.json")) || existsSync3(join4(dir, "STORYBOARD.md"))) {
22483
22640
  return dir;
22484
22641
  }
22485
- const parent = dirname3(dir);
22642
+ const parent = dirname4(dir);
22486
22643
  if (parent === dir) return resolve5(start);
22487
22644
  dir = parent;
22488
22645
  }
@@ -22549,7 +22706,7 @@ async function createAndWriteJobRecord(opts) {
22549
22706
  }
22550
22707
  async function writeJobRecord(record) {
22551
22708
  const dir = jobsDir(record.projectDir);
22552
- await mkdir4(dir, { recursive: true });
22709
+ await mkdir5(dir, { recursive: true });
22553
22710
  const path2 = jobRecordPath(record.id, record.projectDir);
22554
22711
  const tmp = `${path2}.${randomUUID2()}.tmp`;
22555
22712
  await writeFile7(tmp, JSON.stringify(stripUndefined2(record), null, 2) + "\n", "utf-8");
@@ -22597,7 +22754,7 @@ async function refreshJobRecord(record, opts = {}) {
22597
22754
  try {
22598
22755
  if (record.jobType === "generate-video") {
22599
22756
  const output = resolveRefreshOutput(record, opts);
22600
- if (output) await mkdir4(dirname3(output), { recursive: true });
22757
+ if (output) await mkdir5(dirname4(output), { recursive: true });
22601
22758
  const result = await executeVideoStatus({
22602
22759
  taskId: record.providerTaskId,
22603
22760
  provider: record.provider,
@@ -22880,14 +23037,14 @@ async function downloadUrl(url, outputPath) {
22880
23037
  const response = await fetch(url);
22881
23038
  if (!response.ok) throw new Error(`Download failed: HTTP ${response.status}`);
22882
23039
  const buffer = Buffer.from(await response.arrayBuffer());
22883
- await mkdir4(dirname3(outputPath), { recursive: true });
23040
+ await mkdir5(dirname4(outputPath), { recursive: true });
22884
23041
  await writeFile7(outputPath, buffer);
22885
23042
  return outputPath;
22886
23043
  }
22887
23044
  async function maybeCacheOutput(record) {
22888
23045
  if (record.status !== "completed" || !record.outputPath || !record.cachePath) return;
22889
23046
  try {
22890
- await mkdir4(dirname3(record.cachePath), { recursive: true });
23047
+ await mkdir5(dirname4(record.cachePath), { recursive: true });
22891
23048
  await copyFile(record.outputPath, record.cachePath);
22892
23049
  } catch {
22893
23050
  }
@@ -23471,7 +23628,7 @@ var init_project_config = __esm({
23471
23628
  });
23472
23629
 
23473
23630
  // src/commands/_shared/scene-project.ts
23474
- import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile8, access as access3 } from "node:fs/promises";
23631
+ import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile8, access as access3 } from "node:fs/promises";
23475
23632
  import { resolve as resolve7, basename as basename2 } from "node:path";
23476
23633
  function isSceneKind(value) {
23477
23634
  return VALID_SCENE_KINDS.includes(value);
@@ -24083,11 +24240,11 @@ async function scaffoldSceneProject(opts) {
24083
24240
  const now = opts.now ?? /* @__PURE__ */ new Date();
24084
24241
  const profile = opts.profile ?? "full";
24085
24242
  const kind = opts.kind ?? DEFAULT_SCENE_KIND;
24086
- await mkdir5(dir, { recursive: true });
24243
+ await mkdir6(dir, { recursive: true });
24087
24244
  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 });
24245
+ await mkdir6(resolve7(dir, "compositions"), { recursive: true });
24246
+ await mkdir6(resolve7(dir, "assets"), { recursive: true });
24247
+ await mkdir6(resolve7(dir, "renders"), { recursive: true });
24091
24248
  }
24092
24249
  const created = [];
24093
24250
  const skipped2 = [];
@@ -31642,7 +31799,7 @@ var init_scene_audio_scan = __esm({
31642
31799
 
31643
31800
  // src/commands/_shared/scene-audio-mux.ts
31644
31801
  import { rename as rename3, unlink } from "node:fs/promises";
31645
- import { resolve as resolve17, dirname as dirname5, extname as extname2, basename as basename3 } from "node:path";
31802
+ import { resolve as resolve17, dirname as dirname6, extname as extname2, basename as basename3 } from "node:path";
31646
31803
  function buildAudioMuxFilter(audios) {
31647
31804
  if (audios.length === 0) return null;
31648
31805
  const labels = [];
@@ -31703,7 +31860,7 @@ async function muxAudioIntoVideo(opts) {
31703
31860
  }
31704
31861
  const ext = extname2(opts.videoPath) || `.${opts.format}`;
31705
31862
  const tmpPath = resolve17(
31706
- dirname5(opts.videoPath),
31863
+ dirname6(opts.videoPath),
31707
31864
  `.${basename3(opts.videoPath, ext)}.muxing${ext}`
31708
31865
  );
31709
31866
  const args = ["-y", "-loglevel", "error", "-i", opts.videoPath];
@@ -31851,8 +32008,8 @@ __export(scene_render_exports, {
31851
32008
  });
31852
32009
  import { execFile as execFile2 } from "node:child_process";
31853
32010
  import { existsSync as existsSync15 } from "node:fs";
31854
- import { mkdir as mkdir8, readFile as readFile16, stat as stat3, writeFile as writeFile14 } from "node:fs/promises";
31855
- import { resolve as resolve19, relative as relative6, dirname as dirname6, basename as basename4, join as join13, isAbsolute as isAbsolute2 } from "node:path";
32011
+ import { mkdir as mkdir9, readFile as readFile16, stat as stat3, writeFile as writeFile14 } from "node:fs/promises";
32012
+ import { resolve as resolve19, relative as relative6, dirname as dirname7, basename as basename4, join as join13, isAbsolute as isAbsolute2 } from "node:path";
31856
32013
  import { promisify as promisify2 } from "node:util";
31857
32014
  import {
31858
32015
  createRenderJob,
@@ -31905,7 +32062,7 @@ function buildMediaOpenCommand(action, filePath, platform = process.platform) {
31905
32062
  display: ["cmd", ...args].map(quoteCliArg).join(" ")
31906
32063
  };
31907
32064
  }
31908
- const target = action === "reveal" ? dirname6(filePath) : filePath;
32065
+ const target = action === "reveal" ? dirname7(filePath) : filePath;
31909
32066
  return {
31910
32067
  command: "xdg-open",
31911
32068
  args: [target],
@@ -32001,7 +32158,7 @@ async function executeSceneRender(opts = {}) {
32001
32158
  }
32002
32159
  const projectName = projectConfig.config.name;
32003
32160
  const outputPath = opts.output ? resolve19(projectDir, opts.output) : defaultOutputPath({ projectDir, projectName, format: opts.format, beatId: opts.beatId });
32004
- await mkdir8(dirname6(outputPath), { recursive: true });
32161
+ await mkdir9(dirname7(outputPath), { recursive: true });
32005
32162
  const config2 = buildRenderConfig({
32006
32163
  fps: opts.fps,
32007
32164
  quality: opts.quality,
@@ -32197,7 +32354,7 @@ async function prepareBeatRenderRoot(opts) {
32197
32354
  durationSec,
32198
32355
  narrationPath
32199
32356
  });
32200
- await mkdir8(dirname6(join13(opts.projectDir, root)), { recursive: true });
32357
+ await mkdir9(dirname7(join13(opts.projectDir, root)), { recursive: true });
32201
32358
  await writeFile14(join13(opts.projectDir, root), html, "utf-8");
32202
32359
  return {
32203
32360
  success: true,
@@ -32481,7 +32638,7 @@ var init_design_parse = __esm({
32481
32638
 
32482
32639
  // src/commands/_shared/compose-aivideo.ts
32483
32640
  import { existsSync as existsSync16 } from "node:fs";
32484
- import { mkdir as mkdir9, readFile as readFile17, writeFile as writeFile15 } from "node:fs/promises";
32641
+ import { mkdir as mkdir10, readFile as readFile17, writeFile as writeFile15 } from "node:fs/promises";
32485
32642
  import { join as join14, resolve as resolve20 } from "node:path";
32486
32643
  function headingDescriptor(heading) {
32487
32644
  const stripped = heading.replace(/^\s*beat\s+/i, "");
@@ -32644,7 +32801,7 @@ async function composeAiVideo(opts) {
32644
32801
  }
32645
32802
  clipRels.push(rel);
32646
32803
  }
32647
- await mkdir9(join14(projectDir, "assets"), { recursive: true });
32804
+ await mkdir10(join14(projectDir, "assets"), { recursive: true });
32648
32805
  const bgRel = "assets/bg-full.mp4";
32649
32806
  const concatArgs = buildBgConcatArgs(
32650
32807
  clipRels.map((r) => join14(projectDir, r)),
@@ -32652,7 +32809,7 @@ async function composeAiVideo(opts) {
32652
32809
  );
32653
32810
  await execSafe("ffmpeg", concatArgs, { timeout: 6e5 });
32654
32811
  const totalDurationSec = await probeDurationSec(join14(projectDir, bgRel));
32655
- await mkdir9(join14(projectDir, "compositions"), { recursive: true });
32812
+ await mkdir10(join14(projectDir, "compositions"), { recursive: true });
32656
32813
  const sceneFiles = [];
32657
32814
  for (let i = 0; i < beats.length; i++) {
32658
32815
  const beat = beats[i];
@@ -32801,8 +32958,8 @@ var init_compose_aivideo = __esm({
32801
32958
 
32802
32959
  // src/commands/_shared/footage-assemble.ts
32803
32960
  import { existsSync as existsSync17 } from "node:fs";
32804
- import { mkdir as mkdir10, writeFile as writeFile16 } from "node:fs/promises";
32805
- import { dirname as dirname7, isAbsolute as isAbsolute3, join as join15, resolve as resolve21 } from "node:path";
32961
+ import { mkdir as mkdir11, writeFile as writeFile16 } from "node:fs/promises";
32962
+ import { dirname as dirname8, isAbsolute as isAbsolute3, join as join15, resolve as resolve21 } from "node:path";
32806
32963
  function planFootageTimeline(inputs, opts = {}) {
32807
32964
  if (inputs.length === 0) {
32808
32965
  throw new Error("No clips to assemble.");
@@ -33131,7 +33288,7 @@ async function executeFootageAssemble(opts) {
33131
33288
  if (opts.dryRun) {
33132
33289
  return { success: true, dryRun: true, outputPath, report };
33133
33290
  }
33134
- await mkdir10(dirname7(outputPath), { recursive: true });
33291
+ await mkdir11(dirname8(outputPath), { recursive: true });
33135
33292
  const args = buildFootageAssembleArgs({
33136
33293
  sources: sourceRels.map((s) => ({ path: join15(projectDir, s.rel), kind: s.kind })),
33137
33294
  plan,
@@ -33492,9 +33649,40 @@ var init_composer_resolve = __esm({
33492
33649
  }
33493
33650
  });
33494
33651
 
33652
+ // src/commands/_shared/build-video-providers.ts
33653
+ function isBuildVideoProvider(value) {
33654
+ return BUILD_VIDEO_PROVIDERS.includes(value);
33655
+ }
33656
+ function resolveBuildVideoProvider(value) {
33657
+ const provider = String(value ?? "seedance").toLowerCase();
33658
+ if (provider === "fal") return "seedance";
33659
+ return isBuildVideoProvider(provider) ? provider : "seedance";
33660
+ }
33661
+ function videoProviderConfigKey(provider) {
33662
+ switch (provider) {
33663
+ case "seedance":
33664
+ case "fal":
33665
+ return "fal";
33666
+ case "grok":
33667
+ return "xai";
33668
+ case "veo":
33669
+ case "omni":
33670
+ return "google";
33671
+ default:
33672
+ return provider;
33673
+ }
33674
+ }
33675
+ var BUILD_VIDEO_PROVIDERS;
33676
+ var init_build_video_providers = __esm({
33677
+ "src/commands/_shared/build-video-providers.ts"() {
33678
+ "use strict";
33679
+ BUILD_VIDEO_PROVIDERS = ["seedance", "grok", "kling", "runway", "omni", "veo"];
33680
+ }
33681
+ });
33682
+
33495
33683
  // src/commands/_shared/build-plan.ts
33496
33684
  import { existsSync as existsSync19 } from "node:fs";
33497
- import { dirname as dirname8, join as join16, resolve as resolve23 } from "node:path";
33685
+ import { dirname as dirname9, join as join16, resolve as resolve23 } from "node:path";
33498
33686
  function backdropCostUsd(quality) {
33499
33687
  return quality === "hd" ? 0.2 : 0.05;
33500
33688
  }
@@ -34166,12 +34354,7 @@ function normalizeImageProvider(value) {
34166
34354
  return provider || "openai";
34167
34355
  }
34168
34356
  function normalizeVideoProvider(value) {
34169
- const provider = String(value ?? "seedance").toLowerCase();
34170
- if (provider === "fal") return "seedance";
34171
- if (provider === "seedance" || provider === "grok" || provider === "kling" || provider === "runway" || provider === "veo") {
34172
- return provider;
34173
- }
34174
- return "seedance";
34357
+ return resolveBuildVideoProvider(value);
34175
34358
  }
34176
34359
  function normalizeMusicProvider(value) {
34177
34360
  const provider = String(value ?? "elevenlabs").toLowerCase();
@@ -34183,7 +34366,7 @@ function imageConfigKey(provider) {
34183
34366
  return "openai";
34184
34367
  }
34185
34368
  function videoConfigKey(provider) {
34186
- return provider === "seedance" ? "fal" : provider === "grok" ? "xai" : provider === "veo" ? "google" : provider;
34369
+ return videoProviderConfigKey(provider);
34187
34370
  }
34188
34371
  function composerConfigKey(provider) {
34189
34372
  return provider === "claude" ? "anthropic" : provider === "gemini" ? "google" : "openai";
@@ -34201,12 +34384,12 @@ function loadPlanEnv(projectDir) {
34201
34384
  (0, import_dotenv2.config)({ path: join16(projectDir, ".env"), quiet: true });
34202
34385
  (0, import_dotenv2.config)({ path: resolve23(process.cwd(), ".env"), quiet: true });
34203
34386
  let dir = process.cwd();
34204
- while (dir !== dirname8(dir)) {
34387
+ while (dir !== dirname9(dir)) {
34205
34388
  if (existsSync19(join16(dir, "pnpm-workspace.yaml"))) {
34206
34389
  (0, import_dotenv2.config)({ path: join16(dir, ".env"), quiet: true });
34207
34390
  return;
34208
34391
  }
34209
- dir = dirname8(dir);
34392
+ dir = dirname9(dir);
34210
34393
  }
34211
34394
  }
34212
34395
  var import_dotenv2, VIDEO_COST_USD, MUSIC_COST_USD, ELEVENLABS_NARRATION_COST_USD, OPENAI_NARRATION_COST_USD, TRANSCRIPT_COST_USD, COMPOSE_COST_USD, SUPPORTED_BUILD_IMAGE_PROVIDERS;
@@ -34227,6 +34410,7 @@ var init_build_plan = __esm({
34227
34410
  init_project_config();
34228
34411
  init_scene_project();
34229
34412
  init_storyboard_edit();
34413
+ init_build_video_providers();
34230
34414
  VIDEO_COST_USD = 5;
34231
34415
  MUSIC_COST_USD = 0.5;
34232
34416
  ELEVENLABS_NARRATION_COST_USD = 0.05;
@@ -34405,7 +34589,7 @@ function registerMusicCommand(parent) {
34405
34589
  console.log();
34406
34590
  console.log(`Saved to: ${source_default.bold(outputPath)}`);
34407
34591
  console.log(`Duration: ${duration}s`);
34408
- console.log(`Provider: ElevenLabs (music_v1)`);
34592
+ console.log(`Provider: ElevenLabs (${ELEVENLABS_MUSIC_MODEL})`);
34409
34593
  if (options.instrumental) console.log(`Mode: Instrumental`);
34410
34594
  console.log();
34411
34595
  } else {
@@ -34544,6 +34728,48 @@ var init_scene_build_mode = __esm({
34544
34728
  }
34545
34729
  });
34546
34730
 
34731
+ // src/utils/image-file.ts
34732
+ import { rm as rm2, writeFile as writeFile18 } from "node:fs/promises";
34733
+ import { extname as extname4 } from "node:path";
34734
+ function detectImageFormat(buffer) {
34735
+ if (buffer.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
34736
+ return "png";
34737
+ }
34738
+ if (buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255) return "jpeg";
34739
+ if (buffer.toString("ascii", 0, 4) === "RIFF" && buffer.toString("ascii", 8, 12) === "WEBP") {
34740
+ return "webp";
34741
+ }
34742
+ return void 0;
34743
+ }
34744
+ async function writeImageFile(path2, buffer) {
34745
+ const wanted = EXTENSION_FORMATS[extname4(path2).toLowerCase()];
34746
+ const actual = detectImageFormat(buffer);
34747
+ if (!wanted || !actual || wanted === actual) {
34748
+ await writeFile18(path2, buffer);
34749
+ return;
34750
+ }
34751
+ const source = `${path2}.source.${actual === "jpeg" ? "jpg" : actual}`;
34752
+ await writeFile18(source, buffer);
34753
+ try {
34754
+ await execSafe("ffmpeg", ["-y", "-loglevel", "error", "-i", source, "-frames:v", "1", path2]);
34755
+ } finally {
34756
+ await rm2(source, { force: true });
34757
+ }
34758
+ }
34759
+ var EXTENSION_FORMATS;
34760
+ var init_image_file = __esm({
34761
+ "src/utils/image-file.ts"() {
34762
+ "use strict";
34763
+ init_exec_safe();
34764
+ EXTENSION_FORMATS = {
34765
+ ".png": "png",
34766
+ ".jpg": "jpeg",
34767
+ ".jpeg": "jpeg",
34768
+ ".webp": "webp"
34769
+ };
34770
+ }
34771
+ });
34772
+
34547
34773
  // src/commands/_shared/scene-build.ts
34548
34774
  var scene_build_exports = {};
34549
34775
  __export(scene_build_exports, {
@@ -34552,8 +34778,8 @@ __export(scene_build_exports, {
34552
34778
  resolveSceneBuildMode: () => resolveSceneBuildMode
34553
34779
  });
34554
34780
  import { existsSync as existsSync21, readFileSync as readFileSync2 } from "node:fs";
34555
- import { copyFile as copyFile3, mkdir as mkdir11, readFile as readFile18, rm as rm2, writeFile as writeFile18 } from "node:fs/promises";
34556
- import { dirname as dirname9, join as join17, resolve as resolve26 } from "node:path";
34781
+ import { copyFile as copyFile3, mkdir as mkdir12, readFile as readFile18, rm as rm3, writeFile as writeFile19 } from "node:fs/promises";
34782
+ import { dirname as dirname10, join as join17, resolve as resolve26 } from "node:path";
34557
34783
  function readProjectKindSync(projectDir) {
34558
34784
  try {
34559
34785
  const raw = readFileSync2(join17(projectDir, "vibe.config.json"), "utf-8");
@@ -35406,7 +35632,7 @@ async function dispatchNarration(beat, ctx) {
35406
35632
  };
35407
35633
  }
35408
35634
  if (existsSync21(cacheAbs) && !ctx.force) {
35409
- await mkdir11(dirname9(abs), { recursive: true });
35635
+ await mkdir12(dirname10(abs), { recursive: true });
35410
35636
  await copyFile3(cacheAbs, abs);
35411
35637
  await writeAssetMetadata({
35412
35638
  projectDir: ctx.projectDir,
@@ -35437,14 +35663,14 @@ async function dispatchNarration(beat, ctx) {
35437
35663
  ctx.onProgress({ type: "narration-failed", beatId: beat.id, error });
35438
35664
  return { status: "failed", error };
35439
35665
  }
35440
- await mkdir11(dirname9(abs), { recursive: true });
35441
- await writeFile18(abs, result.audioBuffer);
35666
+ await mkdir12(dirname10(abs), { recursive: true });
35667
+ await writeFile19(abs, result.audioBuffer);
35442
35668
  for (const otherExt of ["mp3", "wav"]) {
35443
35669
  const sibling = join17(ctx.projectDir, `assets/narration-${beat.id}.${otherExt}`);
35444
- if (sibling !== abs && existsSync21(sibling)) await rm2(sibling, { force: true });
35670
+ if (sibling !== abs && existsSync21(sibling)) await rm3(sibling, { force: true });
35445
35671
  }
35446
- await mkdir11(dirname9(cacheAbs), { recursive: true });
35447
- await writeFile18(cacheAbs, result.audioBuffer);
35672
+ await mkdir12(dirname10(cacheAbs), { recursive: true });
35673
+ await writeFile19(cacheAbs, result.audioBuffer);
35448
35674
  await writeAssetMetadata({
35449
35675
  projectDir: ctx.projectDir,
35450
35676
  kind: "narration",
@@ -35500,8 +35726,8 @@ async function dispatchTranscript(beat, narrationPath, narrationFresh, ctx) {
35500
35726
  return "skipped";
35501
35727
  }
35502
35728
  try {
35503
- await mkdir11(dirname9(abs), { recursive: true });
35504
- await writeFile18(abs, JSON.stringify(words, null, 2), "utf-8");
35729
+ await mkdir12(dirname10(abs), { recursive: true });
35730
+ await writeFile19(abs, JSON.stringify(words, null, 2), "utf-8");
35505
35731
  } catch (err) {
35506
35732
  ctx.onProgress({
35507
35733
  type: "transcript-skipped",
@@ -35589,7 +35815,7 @@ async function buildCharacters(characters, ctx) {
35589
35815
  }
35590
35816
  const cacheAbs = join17(ctx.projectDir, cache.path);
35591
35817
  if (existsSync21(cacheAbs) && !ctx.force) {
35592
- await mkdir11(dirname9(abs), { recursive: true });
35818
+ await mkdir12(dirname10(abs), { recursive: true });
35593
35819
  await copyFile3(cacheAbs, abs);
35594
35820
  await writeAssetMetadata({
35595
35821
  projectDir: ctx.projectDir,
@@ -35622,10 +35848,10 @@ async function buildCharacters(characters, ctx) {
35622
35848
  ctx.onProgress({ type: "character-failed", name: character.name, error: generated.error });
35623
35849
  continue;
35624
35850
  }
35625
- await mkdir11(dirname9(abs), { recursive: true });
35626
- await writeFile18(abs, generated.buffer);
35627
- await mkdir11(dirname9(cacheAbs), { recursive: true });
35628
- await writeFile18(cacheAbs, generated.buffer);
35851
+ await mkdir12(dirname10(abs), { recursive: true });
35852
+ await writeImageFile(abs, generated.buffer);
35853
+ await mkdir12(dirname10(cacheAbs), { recursive: true });
35854
+ await writeImageFile(cacheAbs, generated.buffer);
35629
35855
  await writeAssetMetadata({
35630
35856
  projectDir: ctx.projectDir,
35631
35857
  kind: "character",
@@ -35694,7 +35920,7 @@ async function dispatchBackdrop(beat, ctx) {
35694
35920
  }
35695
35921
  const cacheAbs = join17(ctx.projectDir, cache.path);
35696
35922
  if (existsSync21(cacheAbs) && !ctx.force) {
35697
- await mkdir11(dirname9(abs), { recursive: true });
35923
+ await mkdir12(dirname10(abs), { recursive: true });
35698
35924
  await copyFile3(cacheAbs, abs);
35699
35925
  await writeAssetMetadata({
35700
35926
  projectDir: ctx.projectDir,
@@ -35724,11 +35950,11 @@ async function dispatchBackdrop(beat, ctx) {
35724
35950
  ctx.onProgress({ type: "backdrop-failed", beatId: beat.id, error });
35725
35951
  return { status: "failed", error };
35726
35952
  }
35727
- await mkdir11(dirname9(abs), { recursive: true });
35953
+ await mkdir12(dirname10(abs), { recursive: true });
35728
35954
  const buffer = generated.buffer;
35729
- await writeFile18(abs, buffer);
35730
- await mkdir11(dirname9(cacheAbs), { recursive: true });
35731
- await writeFile18(cacheAbs, buffer);
35955
+ await writeImageFile(abs, buffer);
35956
+ await mkdir12(dirname10(cacheAbs), { recursive: true });
35957
+ await writeImageFile(cacheAbs, buffer);
35732
35958
  await writeAssetMetadata({
35733
35959
  projectDir: ctx.projectDir,
35734
35960
  kind: "backdrop",
@@ -35772,7 +35998,7 @@ async function generateBackdropImage(prompt3, ctx, ratio) {
35772
35998
  const result2 = await provider2.generateImage(prompt3, {
35773
35999
  // CLI input is free-form; an alias the provider doesn't know fails the
35774
36000
  // API call and surfaces as a beat-scoped backdrop/keyframe error.
35775
- model: ctx.imageModel ?? "gpt-image-2",
36001
+ model: ctx.imageModel ?? OPENAI_IMAGE_DEFAULT_MODEL,
35776
36002
  size: ctx.imageSize,
35777
36003
  quality: ctx.imageQuality
35778
36004
  });
@@ -35857,7 +36083,7 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
35857
36083
  }
35858
36084
  const cacheAbs = join17(ctx.projectDir, cache.path);
35859
36085
  if (existsSync21(cacheAbs) && !ctx.force) {
35860
- await mkdir11(dirname9(abs), { recursive: true });
36086
+ await mkdir12(dirname10(abs), { recursive: true });
35861
36087
  await copyFile3(cacheAbs, abs);
35862
36088
  await writeAssetMetadata({
35863
36089
  projectDir: ctx.projectDir,
@@ -35887,10 +36113,10 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
35887
36113
  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. ";
35888
36114
  const generated = sheetBuffers.length > 0 ? await editKeyframeImage(`${identityLock}${keyframeCue}`, sheetBuffers, imgCtx, ratio) : await generateBackdropImage(keyframeCue, imgCtx, ratio);
35889
36115
  if (!generated.success) return { status: "failed", error: generated.error };
35890
- await mkdir11(dirname9(abs), { recursive: true });
35891
- await writeFile18(abs, generated.buffer);
35892
- await mkdir11(dirname9(cacheAbs), { recursive: true });
35893
- await writeFile18(cacheAbs, generated.buffer);
36116
+ await mkdir12(dirname10(abs), { recursive: true });
36117
+ await writeImageFile(abs, generated.buffer);
36118
+ await mkdir12(dirname10(cacheAbs), { recursive: true });
36119
+ await writeImageFile(cacheAbs, generated.buffer);
35894
36120
  await writeAssetMetadata({
35895
36121
  projectDir: ctx.projectDir,
35896
36122
  kind: "keyframe",
@@ -35986,7 +36212,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
35986
36212
  }
35987
36213
  const cacheAbs = join17(ctx.projectDir, cache.path);
35988
36214
  if (existsSync21(cacheAbs) && !ctx.force) {
35989
- await mkdir11(dirname9(abs), { recursive: true });
36215
+ await mkdir12(dirname10(abs), { recursive: true });
35990
36216
  await copyFile3(cacheAbs, abs);
35991
36217
  await writeAssetMetadata({
35992
36218
  projectDir: ctx.projectDir,
@@ -36063,7 +36289,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
36063
36289
  }
36064
36290
  }
36065
36291
  if (result.status === "completed" && existsSync21(abs)) {
36066
- await mkdir11(dirname9(cacheAbs), { recursive: true });
36292
+ await mkdir12(dirname10(cacheAbs), { recursive: true });
36067
36293
  await copyFile3(abs, cacheAbs);
36068
36294
  await writeAssetMetadata({
36069
36295
  projectDir: ctx.projectDir,
@@ -36165,7 +36391,7 @@ async function dispatchMusic(beat, ctx) {
36165
36391
  }
36166
36392
  const cacheAbs = join17(ctx.projectDir, cache.path);
36167
36393
  if (existsSync21(cacheAbs) && !ctx.force) {
36168
- await mkdir11(dirname9(abs), { recursive: true });
36394
+ await mkdir12(dirname10(abs), { recursive: true });
36169
36395
  await copyFile3(cacheAbs, abs);
36170
36396
  await writeAssetMetadata({
36171
36397
  projectDir: ctx.projectDir,
@@ -36243,7 +36469,7 @@ async function dispatchMusic(beat, ctx) {
36243
36469
  ctx.onProgress({ type: "music-failed", beatId: beat.id, error });
36244
36470
  return { status: "failed", error };
36245
36471
  }
36246
- await mkdir11(dirname9(cacheAbs), { recursive: true });
36472
+ await mkdir12(dirname10(cacheAbs), { recursive: true });
36247
36473
  await copyFile3(abs, cacheAbs);
36248
36474
  await writeAssetMetadata({
36249
36475
  projectDir: ctx.projectDir,
@@ -36277,12 +36503,12 @@ function loadSceneBuildEnv(projectDir) {
36277
36503
  (0, import_dotenv3.config)({ path: join17(projectDir, ".env"), quiet: true });
36278
36504
  (0, import_dotenv3.config)({ path: resolve26(process.cwd(), ".env"), quiet: true });
36279
36505
  let dir = process.cwd();
36280
- while (dir !== dirname9(dir)) {
36506
+ while (dir !== dirname10(dir)) {
36281
36507
  if (existsSync21(join17(dir, "pnpm-workspace.yaml"))) {
36282
36508
  (0, import_dotenv3.config)({ path: join17(dir, ".env"), quiet: true });
36283
36509
  return;
36284
36510
  }
36285
- dir = dirname9(dir);
36511
+ dir = dirname10(dir);
36286
36512
  }
36287
36513
  }
36288
36514
  async function skipped(kind, beatId, reason, ctx) {
@@ -36507,14 +36733,6 @@ async function safeAudioDuration(absPath) {
36507
36733
  return void 0;
36508
36734
  }
36509
36735
  }
36510
- function resolveBuildVideoProvider(value) {
36511
- const provider = String(value ?? "seedance").toLowerCase();
36512
- if (provider === "fal") return "seedance";
36513
- if (provider === "seedance" || provider === "grok" || provider === "kling" || provider === "runway" || provider === "veo") {
36514
- return provider;
36515
- }
36516
- return "seedance";
36517
- }
36518
36736
  function resolveBuildMusicProvider(value) {
36519
36737
  const provider = String(value ?? "elevenlabs").toLowerCase();
36520
36738
  return provider === "replicate" ? "replicate" : "elevenlabs";
@@ -36526,8 +36744,7 @@ function stringOrUndefined4(value) {
36526
36744
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
36527
36745
  }
36528
36746
  async function apiKeyForVideoProvider(provider, projectDir) {
36529
- const providerKey = provider === "seedance" ? "fal" : provider === "grok" ? "xai" : provider === "veo" ? "google" : provider;
36530
- return getApiKeyFromConfig(providerKey, { cwd: projectDir });
36747
+ return getApiKeyFromConfig(videoProviderConfigKey(provider), { cwd: projectDir });
36531
36748
  }
36532
36749
  async function finalizeBuildResult(projectDir, startedAt, result, reportResult = result) {
36533
36750
  const reportPath = join17(projectDir, "build-report.json");
@@ -36550,7 +36767,7 @@ async function finalizeBuildResult(projectDir, startedAt, result, reportResult =
36550
36767
  const withMeta = decorate(result);
36551
36768
  const reportMeta = reportResult === result ? withMeta : decorate(reportResult);
36552
36769
  try {
36553
- await writeFile18(
36770
+ await writeFile19(
36554
36771
  reportPath,
36555
36772
  JSON.stringify(toBuildReport(projectDir, reportMeta), null, 2) + "\n",
36556
36773
  "utf-8"
@@ -36803,6 +37020,8 @@ var init_scene_build = __esm({
36803
37020
  init_scene_repair();
36804
37021
  init_tts_resolve();
36805
37022
  init_scene_build_mode();
37023
+ init_image_file();
37024
+ init_build_video_providers();
36806
37025
  init_scene_build_mode();
36807
37026
  BEAT_PRIMITIVES_CONCURRENCY = 4;
36808
37027
  }
@@ -36915,8 +37134,8 @@ __export(ai_audio_exports, {
36915
37134
  executeTranscribe: () => executeTranscribe,
36916
37135
  executeVoiceClone: () => executeVoiceClone
36917
37136
  });
36918
- import { resolve as resolve28, dirname as dirname10, basename as basename5, extname as extname4 } from "node:path";
36919
- import { readFile as readFile19, writeFile as writeFile19 } from "node:fs/promises";
37137
+ import { resolve as resolve28, dirname as dirname11, basename as basename5, extname as extname5 } from "node:path";
37138
+ import { readFile as readFile19, writeFile as writeFile20 } from "node:fs/promises";
36920
37139
  import { existsSync as existsSync22 } from "node:fs";
36921
37140
  async function executeTranscribe(options) {
36922
37141
  const { audioPath, language, output, format, apiKey } = options;
@@ -36938,7 +37157,7 @@ async function executeTranscribe(options) {
36938
37157
  outputPath = resolve28(process.cwd(), output);
36939
37158
  const fmt = detectFormat(output, format);
36940
37159
  const content = formatTranscript(result, fmt);
36941
- await writeFile19(outputPath, content, "utf-8");
37160
+ await writeFile20(outputPath, content, "utf-8");
36942
37161
  }
36943
37162
  return {
36944
37163
  success: true,
@@ -36966,7 +37185,7 @@ async function executeIsolate(options) {
36966
37185
  return { success: false, error: result.error || "Audio isolation failed" };
36967
37186
  }
36968
37187
  const outputPath = resolve28(process.cwd(), output);
36969
- await writeFile19(outputPath, result.audioBuffer);
37188
+ await writeFile20(outputPath, result.audioBuffer);
36970
37189
  return { success: true, outputPath };
36971
37190
  } catch (error) {
36972
37191
  return { success: false, error: `Audio isolation failed: ${error instanceof Error ? error.message : String(error)}` };
@@ -37012,11 +37231,11 @@ async function executeDub(options) {
37012
37231
  if (!openaiKey) return { success: false, error: "OPENAI_API_KEY required for Whisper transcription" };
37013
37232
  if (!anthropicKey) return { success: false, error: "ANTHROPIC_API_KEY required for Claude translation" };
37014
37233
  if (!analyzeOnly && !elevenlabsKey) return { success: false, error: "ELEVENLABS_API_KEY required for TTS" };
37015
- const ext = extname4(absPath).toLowerCase();
37234
+ const ext = extname5(absPath).toLowerCase();
37016
37235
  const isVideo = [".mp4", ".mov", ".avi", ".mkv", ".webm"].includes(ext);
37017
37236
  let audioPath = absPath;
37018
37237
  if (isVideo) {
37019
- const tempAudioPath = resolve28(dirname10(absPath), `temp-audio-${Date.now()}.mp3`);
37238
+ const tempAudioPath = resolve28(dirname11(absPath), `temp-audio-${Date.now()}.mp3`);
37020
37239
  execSafeSync("ffmpeg", ["-i", absPath, "-vn", "-acodec", "mp3", "-y", tempAudioPath]);
37021
37240
  audioPath = tempAudioPath;
37022
37241
  }
@@ -37077,7 +37296,7 @@ ${segmentTexts}`,
37077
37296
  translated: translatedSegments[i]?.text || s.text
37078
37297
  }))
37079
37298
  };
37080
- await writeFile19(timingPath, JSON.stringify(timingData, null, 2));
37299
+ await writeFile20(timingPath, JSON.stringify(timingData, null, 2));
37081
37300
  }
37082
37301
  return {
37083
37302
  success: true,
@@ -37097,10 +37316,10 @@ ${segmentTexts}`,
37097
37316
  }
37098
37317
  }
37099
37318
  const combinedBuffer = Buffer.concat(dubbedBuffers);
37100
- const outputExt = isVideo ? ".mp3" : extname4(absPath);
37101
- const defaultOutputPath2 = resolve28(dirname10(absPath), `${basename5(absPath, extname4(absPath))}-${language}${outputExt}`);
37319
+ const outputExt = isVideo ? ".mp3" : extname5(absPath);
37320
+ const defaultOutputPath2 = resolve28(dirname11(absPath), `${basename5(absPath, extname5(absPath))}-${language}${outputExt}`);
37102
37321
  const finalOutputPath = resolve28(process.cwd(), output || defaultOutputPath2);
37103
- await writeFile19(finalOutputPath, combinedBuffer);
37322
+ await writeFile20(finalOutputPath, combinedBuffer);
37104
37323
  if (isVideo && audioPath !== absPath) {
37105
37324
  try {
37106
37325
  const { unlink: unlink3 } = await import("node:fs/promises");
@@ -37135,7 +37354,7 @@ async function executeDuck(options) {
37135
37354
  const absVoicePath = resolve28(process.cwd(), voicePath);
37136
37355
  if (!existsSync22(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
37137
37356
  if (!existsSync22(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
37138
- const defaultOutput = resolve28(dirname10(absMusicPath), `${basename5(absMusicPath, extname4(absMusicPath))}-ducked${extname4(absMusicPath)}`);
37357
+ const defaultOutput = resolve28(dirname11(absMusicPath), `${basename5(absMusicPath, extname5(absMusicPath))}-ducked${extname5(absMusicPath)}`);
37139
37358
  const outputPath = resolve28(process.cwd(), output || defaultOutput);
37140
37359
  const filter = `[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`;
37141
37360
  await execSafe("ffmpeg", [
@@ -37400,7 +37619,7 @@ var init_silence_cut = __esm({
37400
37619
 
37401
37620
  // src/commands/_shared/edit/jump-cut.ts
37402
37621
  import { existsSync as existsSync24 } from "node:fs";
37403
- import { readFile as readFile21, writeFile as writeFile20, mkdir as mkdir12 } from "node:fs/promises";
37622
+ import { readFile as readFile21, writeFile as writeFile21, mkdir as mkdir13 } from "node:fs/promises";
37404
37623
  import { join as join18 } from "node:path";
37405
37624
  async function transcribeWithWords(audioPath, apiKey, language) {
37406
37625
  const audioBuffer = await readFile21(audioPath);
@@ -37480,7 +37699,7 @@ async function executeJumpCut(options) {
37480
37699
  }
37481
37700
  try {
37482
37701
  const tmpDir = `/tmp/vibe_jumpcut_${Date.now()}`;
37483
- await mkdir12(tmpDir, { recursive: true });
37702
+ await mkdir13(tmpDir, { recursive: true });
37484
37703
  const audioPath = join18(tmpDir, "audio.wav");
37485
37704
  try {
37486
37705
  await execSafe(
@@ -37559,7 +37778,7 @@ async function executeJumpCut(options) {
37559
37778
  }
37560
37779
  const concatList = segmentPaths.map((p) => `file '${p}'`).join("\n");
37561
37780
  const listPath = join18(tmpDir, "concat.txt");
37562
- await writeFile20(listPath, concatList);
37781
+ await writeFile21(listPath, concatList);
37563
37782
  await execSafe(
37564
37783
  "ffmpeg",
37565
37784
  [
@@ -37586,8 +37805,8 @@ async function executeJumpCut(options) {
37586
37805
  };
37587
37806
  } finally {
37588
37807
  try {
37589
- const { rm: rm6 } = await import("node:fs/promises");
37590
- await rm6(tmpDir, { recursive: true, force: true });
37808
+ const { rm: rm7 } = await import("node:fs/promises");
37809
+ await rm7(tmpDir, { recursive: true, force: true });
37591
37810
  } catch {
37592
37811
  }
37593
37812
  }
@@ -37664,7 +37883,7 @@ __export(remotion_exports, {
37664
37883
  wrapComponentWithImage: () => wrapComponentWithImage,
37665
37884
  wrapComponentWithVideo: () => wrapComponentWithVideo
37666
37885
  });
37667
- import { writeFile as writeFile21, mkdir as mkdir13, rm as rm3, copyFile as copyFile4 } from "node:fs/promises";
37886
+ import { writeFile as writeFile22, mkdir as mkdir14, rm as rm4, copyFile as copyFile4 } from "node:fs/promises";
37668
37887
  import { existsSync as existsSync25 } from "node:fs";
37669
37888
  import { join as join19 } from "node:path";
37670
37889
  import { tmpdir } from "node:os";
@@ -37686,7 +37905,7 @@ async function ensureRemotionInstalled() {
37686
37905
  }
37687
37906
  async function scaffoldRemotionProject(componentCode, componentName, opts) {
37688
37907
  const dir = join19(tmpdir(), `vibe_motion_${Date.now()}`);
37689
- await mkdir13(dir, { recursive: true });
37908
+ await mkdir14(dir, { recursive: true });
37690
37909
  const deps = {
37691
37910
  remotion: REMOTION_VERSION,
37692
37911
  "@remotion/cli": REMOTION_VERSION,
@@ -37704,7 +37923,7 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
37704
37923
  dependencies: deps
37705
37924
  };
37706
37925
  const packageJsonStr = JSON.stringify(packageJson, null, 2);
37707
- await writeFile21(join19(dir, "package.json"), packageJsonStr);
37926
+ await writeFile22(join19(dir, "package.json"), packageJsonStr);
37708
37927
  const tsconfig = {
37709
37928
  compilerOptions: {
37710
37929
  target: "ES2020",
@@ -37716,8 +37935,8 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
37716
37935
  skipLibCheck: true
37717
37936
  }
37718
37937
  };
37719
- await writeFile21(join19(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
37720
- await writeFile21(join19(dir, "Component.tsx"), componentCode);
37938
+ await writeFile22(join19(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
37939
+ await writeFile22(join19(dir, "Component.tsx"), componentCode);
37721
37940
  const rootCode = `import { registerRoot, Composition } from "remotion";
37722
37941
  import { ${componentName} } from "./Component";
37723
37942
 
@@ -37736,7 +37955,7 @@ const Root = () => {
37736
37955
 
37737
37956
  registerRoot(Root);
37738
37957
  `;
37739
- await writeFile21(join19(dir, "Root.tsx"), rootCode);
37958
+ await writeFile22(join19(dir, "Root.tsx"), rootCode);
37740
37959
  const depsHash = createHash4("md5").update(packageJsonStr).digest("hex").slice(0, 12);
37741
37960
  const cacheMarker = join19(REMOTION_CACHE_DIR, `.deps-${depsHash}`);
37742
37961
  const cachedModules = join19(REMOTION_CACHE_DIR, "node_modules");
@@ -37750,15 +37969,15 @@ registerRoot(Root);
37750
37969
  } else {
37751
37970
  await this_npmInstall(dir);
37752
37971
  try {
37753
- await mkdir13(REMOTION_CACHE_DIR, { recursive: true });
37754
- await writeFile21(join19(REMOTION_CACHE_DIR, "package.json"), packageJsonStr);
37972
+ await mkdir14(REMOTION_CACHE_DIR, { recursive: true });
37973
+ await writeFile22(join19(REMOTION_CACHE_DIR, "package.json"), packageJsonStr);
37755
37974
  if (existsSync25(join19(dir, "node_modules"))) {
37756
37975
  const { rename: rename5, symlink } = await import("node:fs/promises");
37757
37976
  await rename5(join19(dir, "node_modules"), cachedModules).catch(() => {
37758
37977
  });
37759
37978
  await symlink(cachedModules, join19(dir, "node_modules"), "dir").catch(() => {
37760
37979
  });
37761
- await writeFile21(cacheMarker, depsHash);
37980
+ await writeFile22(cacheMarker, depsHash);
37762
37981
  }
37763
37982
  } catch {
37764
37983
  }
@@ -37858,7 +38077,7 @@ async function renderMotion(options) {
37858
38077
  stderr: ${stderr}` : ""}`);
37859
38078
  return { success: false, error: `Remotion render failed: ${msg}` };
37860
38079
  } finally {
37861
- await rm3(dir, { recursive: true, force: true }).catch(() => {
38080
+ await rm4(dir, { recursive: true, force: true }).catch(() => {
37862
38081
  });
37863
38082
  }
37864
38083
  }
@@ -37927,7 +38146,7 @@ async function renderWithEmbeddedImage(options) {
37927
38146
  );
37928
38147
  try {
37929
38148
  const publicDir = join19(dir, "public");
37930
- await mkdir13(publicDir, { recursive: true });
38149
+ await mkdir14(publicDir, { recursive: true });
37931
38150
  await copyFile4(options.imagePath, join19(publicDir, options.imageFileName));
37932
38151
  const entryPoint = join19(dir, "Root.tsx");
37933
38152
  const mp4Out = options.outputPath.replace(/\.\w+$/, ".mp4");
@@ -37950,7 +38169,7 @@ async function renderWithEmbeddedImage(options) {
37950
38169
  const msg = error instanceof Error ? error.message : String(error);
37951
38170
  return { success: false, error: `Remotion image embed render failed: ${msg}` };
37952
38171
  } finally {
37953
- await rm3(dir, { recursive: true, force: true }).catch(() => {
38172
+ await rm4(dir, { recursive: true, force: true }).catch(() => {
37954
38173
  });
37955
38174
  }
37956
38175
  }
@@ -37991,7 +38210,7 @@ async function renderWithEmbeddedVideo(options) {
37991
38210
  );
37992
38211
  try {
37993
38212
  const publicDir = join19(dir, "public");
37994
- await mkdir13(publicDir, { recursive: true });
38213
+ await mkdir14(publicDir, { recursive: true });
37995
38214
  await copyFile4(options.videoPath, join19(publicDir, options.videoFileName));
37996
38215
  const entryPoint = join19(dir, "Root.tsx");
37997
38216
  const mp4VideoOnly = options.outputPath.replace(/\.\w+$/, "_video_only.mp4");
@@ -38027,14 +38246,14 @@ async function renderWithEmbeddedVideo(options) {
38027
38246
  "-shortest",
38028
38247
  mp4Out
38029
38248
  ], { timeout: 12e4 });
38030
- await rm3(mp4VideoOnly, { force: true }).catch(() => {
38249
+ await rm4(mp4VideoOnly, { force: true }).catch(() => {
38031
38250
  });
38032
38251
  return { success: true, outputPath: mp4Out };
38033
38252
  } catch (error) {
38034
38253
  const msg = error instanceof Error ? error.message : String(error);
38035
38254
  return { success: false, error: `Remotion video embed render failed: ${msg}` };
38036
38255
  } finally {
38037
- await rm3(dir, { recursive: true, force: true }).catch(() => {
38256
+ await rm4(dir, { recursive: true, force: true }).catch(() => {
38038
38257
  });
38039
38258
  }
38040
38259
  }
@@ -38415,8 +38634,8 @@ var init_remotion = __esm({
38415
38634
 
38416
38635
  // src/commands/_shared/edit/caption.ts
38417
38636
  import { existsSync as existsSync26 } from "node:fs";
38418
- import { readFile as readFile22, writeFile as writeFile22, mkdir as mkdir14 } from "node:fs/promises";
38419
- import { dirname as dirname11, basename as basename6, extname as extname5, join as join20 } from "node:path";
38637
+ import { readFile as readFile22, writeFile as writeFile23, mkdir as mkdir15 } from "node:fs/promises";
38638
+ import { dirname as dirname12, basename as basename6, extname as extname6, join as join20 } from "node:path";
38420
38639
  function getCaptionForceStyle(style, fontSize, fontColor, position) {
38421
38640
  const alignment = position === "top" ? 8 : position === "center" ? 5 : 2;
38422
38641
  const marginV = position === "center" ? 0 : 30;
@@ -38462,7 +38681,7 @@ async function executeCaption(options) {
38462
38681
  }
38463
38682
  try {
38464
38683
  const tmpDir = `/tmp/vibe_caption_${Date.now()}`;
38465
- await mkdir14(tmpDir, { recursive: true });
38684
+ await mkdir15(tmpDir, { recursive: true });
38466
38685
  const audioPath = join20(tmpDir, "audio.wav");
38467
38686
  const srtPath = join20(tmpDir, "captions.srt");
38468
38687
  try {
@@ -38495,7 +38714,7 @@ async function executeCaption(options) {
38495
38714
  };
38496
38715
  }
38497
38716
  const srtContent = formatSRT(transcriptResult.segments);
38498
- await writeFile22(srtPath, srtContent);
38717
+ await writeFile23(srtPath, srtContent);
38499
38718
  const { width, height } = await getVideoResolution(videoPath);
38500
38719
  const fontSize = customFontSize || Math.round(height / 18);
38501
38720
  let hasSubtitles = false;
@@ -38532,12 +38751,12 @@ async function executeCaption(options) {
38532
38751
  } = await Promise.resolve().then(() => (init_remotion(), remotion_exports));
38533
38752
  const remotionErr = await ensureRemotionInstalled2();
38534
38753
  if (remotionErr) {
38535
- const outputDir2 = dirname11(outputPath);
38754
+ const outputDir2 = dirname12(outputPath);
38536
38755
  const outputSrtPath2 = join20(
38537
38756
  outputDir2,
38538
- basename6(outputPath, extname5(outputPath)) + ".srt"
38757
+ basename6(outputPath, extname6(outputPath)) + ".srt"
38539
38758
  );
38540
- await writeFile22(outputSrtPath2, srtContent);
38759
+ await writeFile23(outputSrtPath2, srtContent);
38541
38760
  return { success: false, error: `${remotionErr}
38542
38761
  SRT saved to: ${outputSrtPath2}` };
38543
38762
  }
@@ -38571,12 +38790,12 @@ SRT saved to: ${outputSrtPath2}` };
38571
38790
  outputPath
38572
38791
  });
38573
38792
  if (!renderResult.success) {
38574
- const outputDir2 = dirname11(outputPath);
38793
+ const outputDir2 = dirname12(outputPath);
38575
38794
  const outputSrtPath2 = join20(
38576
38795
  outputDir2,
38577
- basename6(outputPath, extname5(outputPath)) + ".srt"
38796
+ basename6(outputPath, extname6(outputPath)) + ".srt"
38578
38797
  );
38579
- await writeFile22(outputSrtPath2, srtContent);
38798
+ await writeFile23(outputSrtPath2, srtContent);
38580
38799
  return {
38581
38800
  success: false,
38582
38801
  error: `${renderResult.error}
@@ -38584,12 +38803,12 @@ SRT saved to: ${outputSrtPath2}`
38584
38803
  };
38585
38804
  }
38586
38805
  }
38587
- const outputDir = dirname11(outputPath);
38806
+ const outputDir = dirname12(outputPath);
38588
38807
  const outputSrtPath = join20(
38589
38808
  outputDir,
38590
- basename6(outputPath, extname5(outputPath)) + ".srt"
38809
+ basename6(outputPath, extname6(outputPath)) + ".srt"
38591
38810
  );
38592
- await writeFile22(outputSrtPath, srtContent);
38811
+ await writeFile23(outputSrtPath, srtContent);
38593
38812
  return {
38594
38813
  success: true,
38595
38814
  outputPath,
@@ -38598,8 +38817,8 @@ SRT saved to: ${outputSrtPath2}`
38598
38817
  };
38599
38818
  } finally {
38600
38819
  try {
38601
- const { rm: rm6 } = await import("node:fs/promises");
38602
- await rm6(tmpDir, { recursive: true, force: true });
38820
+ const { rm: rm7 } = await import("node:fs/promises");
38821
+ await rm7(tmpDir, { recursive: true, force: true });
38603
38822
  } catch {
38604
38823
  }
38605
38824
  }
@@ -38760,7 +38979,7 @@ var init_fade = __esm({
38760
38979
 
38761
38980
  // src/commands/_shared/edit/translate-srt.ts
38762
38981
  import { existsSync as existsSync29 } from "node:fs";
38763
- import { readFile as readFile23, writeFile as writeFile23 } from "node:fs/promises";
38982
+ import { readFile as readFile23, writeFile as writeFile24 } from "node:fs/promises";
38764
38983
  async function executeTranslateSrt(options) {
38765
38984
  const {
38766
38985
  srtPath,
@@ -38803,15 +39022,14 @@ ${textsToTranslate}`;
38803
39022
  Authorization: `Bearer ${openaiKey}`
38804
39023
  },
38805
39024
  body: JSON.stringify({
38806
- model: "gpt-5-mini",
38807
- messages: [{ role: "user", content: translatePrompt }],
38808
- temperature: 0.3
39025
+ model: "gpt-5.4-mini",
39026
+ messages: [{ role: "user", content: translatePrompt }]
38809
39027
  })
38810
39028
  });
38811
39029
  if (!response.ok) {
38812
39030
  return {
38813
39031
  success: false,
38814
- error: `OpenAI API error: ${response.status} ${response.statusText}`
39032
+ error: `OpenAI API error: ${response.status} ${response.statusText}: ${await response.text()}`
38815
39033
  };
38816
39034
  }
38817
39035
  const data = await response.json();
@@ -38832,15 +39050,15 @@ ${textsToTranslate}`;
38832
39050
  "anthropic-version": "2023-06-01"
38833
39051
  },
38834
39052
  body: JSON.stringify({
38835
- model: "claude-sonnet-4-6-20250514",
38836
- max_tokens: 4096,
39053
+ model: "claude-sonnet-5-5",
39054
+ max_tokens: 16e3,
38837
39055
  messages: [{ role: "user", content: translatePrompt }]
38838
39056
  })
38839
39057
  });
38840
39058
  if (!response.ok) {
38841
39059
  return {
38842
39060
  success: false,
38843
- error: `Claude API error: ${response.status} ${response.statusText}`
39061
+ error: `Claude API error: ${response.status} ${response.statusText}: ${await response.text()}`
38844
39062
  };
38845
39063
  }
38846
39064
  const data = await response.json();
@@ -38864,7 +39082,7 @@ ${textsToTranslate}`;
38864
39082
  }
38865
39083
  }
38866
39084
  const translatedSrt = formatSRT(translatedSegments);
38867
- await writeFile23(outputPath, translatedSrt);
39085
+ await writeFile24(outputPath, translatedSrt);
38868
39086
  return {
38869
39087
  success: true,
38870
39088
  outputPath,
@@ -39116,7 +39334,7 @@ var init_ai_edit = __esm({
39116
39334
  });
39117
39335
 
39118
39336
  // src/commands/ai-edit-cli.ts
39119
- import { resolve as resolve30, extname as extname6, basename as basename7 } from "node:path";
39337
+ import { resolve as resolve30, extname as extname7, basename as basename7 } from "node:path";
39120
39338
  import { existsSync as existsSync31 } from "node:fs";
39121
39339
  function registerEditCommands(aiCommand) {
39122
39340
  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", `
@@ -39140,7 +39358,7 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
39140
39358
  if (!commandExists("ffmpeg")) {
39141
39359
  exitWithError(generalError("FFmpeg not found. Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux). Run `vibe doctor` for details."));
39142
39360
  }
39143
- const ext = extname6(videoPath);
39361
+ const ext = extname7(videoPath);
39144
39362
  const name = basename7(videoPath, ext);
39145
39363
  const outputPath = options.output || `${name}-cut${ext}`;
39146
39364
  const useGemini = options.useGemini || false;
@@ -39270,7 +39488,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
39270
39488
  if (!apiKey) {
39271
39489
  exitWithError(authError("OPENAI_API_KEY", "OpenAI"));
39272
39490
  }
39273
- const ext = extname6(videoPath);
39491
+ const ext = extname7(videoPath);
39274
39492
  const name = basename7(videoPath, ext);
39275
39493
  const outputPath = options.output || `${name}-captioned${ext}`;
39276
39494
  const spinner2 = ora("Starting caption process...").start();
@@ -39344,7 +39562,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
39344
39562
  });
39345
39563
  return;
39346
39564
  }
39347
- const ext = extname6(inputPath);
39565
+ const ext = extname7(inputPath);
39348
39566
  const name = basename7(inputPath, ext);
39349
39567
  const outputPath = options.output || `${name}-denoised${ext}`;
39350
39568
  const spinner2 = ora("Applying noise reduction...").start();
@@ -39412,7 +39630,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
39412
39630
  });
39413
39631
  return;
39414
39632
  }
39415
- const ext = extname6(videoPath);
39633
+ const ext = extname7(videoPath);
39416
39634
  const name = basename7(videoPath, ext);
39417
39635
  const outputPath = options.output || `${name}-faded${ext}`;
39418
39636
  const spinner2 = ora("Applying fade effects...").start();
@@ -39490,7 +39708,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
39490
39708
  if (!apiKey) {
39491
39709
  exitWithError(authError(envKey, providerName));
39492
39710
  }
39493
- const ext = extname6(srtPath);
39711
+ const ext = extname7(srtPath);
39494
39712
  const name = basename7(srtPath, ext);
39495
39713
  const outputPath = options.output || `${name}-${options.target}${ext}`;
39496
39714
  const spinner2 = ora(`Translating to ${options.target}...`).start();
@@ -39568,7 +39786,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
39568
39786
  if (!apiKey) {
39569
39787
  exitWithError(authError("OPENAI_API_KEY", "OpenAI"));
39570
39788
  }
39571
- const ext = extname6(videoPath);
39789
+ const ext = extname7(videoPath);
39572
39790
  const name = basename7(videoPath, ext);
39573
39791
  const outputPath = options.output || `${name}-jumpcut${ext}`;
39574
39792
  const fillers = options.fillers ? options.fillers.split(",").map((f) => f.trim()) : void 0;
@@ -40029,8 +40247,8 @@ var init_project_resolver = __esm({
40029
40247
 
40030
40248
  // src/commands/_shared/execute-fill-gaps.ts
40031
40249
  import { existsSync as existsSync32 } from "node:fs";
40032
- import { readFile as readFile24, writeFile as writeFile24, mkdir as mkdir15, rename as renameFs } from "node:fs/promises";
40033
- import { resolve as resolve32, dirname as dirname12 } from "node:path";
40250
+ import { readFile as readFile24, writeFile as writeFile25, mkdir as mkdir16, rename as renameFs } from "node:fs/promises";
40251
+ import { resolve as resolve32, dirname as dirname13 } from "node:path";
40034
40252
  function detectVideoGaps(videoClips, totalDuration) {
40035
40253
  const gaps = [];
40036
40254
  const sortedClips = [...videoClips].sort((a, b) => a.startTime - b.startTime);
@@ -40240,14 +40458,14 @@ async function executeFillGaps(options) {
40240
40458
  if (!kling.isConfigured()) {
40241
40459
  return {
40242
40460
  success: false,
40243
- error: "Invalid KLING_API_KEY (expected ACCESS_KEY:SECRET_KEY format)",
40461
+ error: "Invalid KLING_API_KEY (set your Kling API key, or the legacy ACCESS_KEY:SECRET_KEY pair)",
40244
40462
  humanLines
40245
40463
  };
40246
40464
  }
40247
- const projectDir = dirname12(filePath);
40465
+ const projectDir = dirname13(filePath);
40248
40466
  const footageDir = options.dir ? resolve32(process.cwd(), options.dir) : resolve32(projectDir, "footage");
40249
40467
  if (!existsSync32(footageDir)) {
40250
- await mkdir15(footageDir, { recursive: true });
40468
+ await mkdir16(footageDir, { recursive: true });
40251
40469
  }
40252
40470
  humanLines.push("Generating AI Videos");
40253
40471
  const imgbbApiKey = options.imgbbApiKey || await getApiKey("IMGBB_API_KEY", "imgbb", void 0);
@@ -40312,13 +40530,13 @@ async function executeFillGaps(options) {
40312
40530
  let generatedDuration = 0;
40313
40531
  const generatedVideos = [];
40314
40532
  const initialDuration = Math.min(10, targetDuration);
40315
- const klingDuration = initialDuration > 5 ? "10" : "5";
40316
- onProgress(`Generating ${klingDuration}s video with Kling...`);
40533
+ const klingDuration2 = initialDuration > 5 ? "10" : "5";
40534
+ onProgress(`Generating ${klingDuration2}s video with Kling...`);
40317
40535
  const prompt3 = options.prompt || "Continue the scene naturally with subtle motion";
40318
40536
  const result = await kling.generateVideo(prompt3, {
40319
40537
  prompt: prompt3,
40320
40538
  referenceImage: frameUrl,
40321
- duration: parseInt(klingDuration),
40539
+ duration: parseInt(klingDuration2),
40322
40540
  aspectRatio: options.ratio || "16:9",
40323
40541
  mode: options.mode || "std"
40324
40542
  });
@@ -40341,8 +40559,8 @@ async function executeFillGaps(options) {
40341
40559
  const videoPath = resolve32(footageDir, videoFileName);
40342
40560
  onProgress("Downloading generated video...");
40343
40561
  const videoBuffer = await downloadVideo(finalResult.videoUrl);
40344
- await writeFile24(videoPath, videoBuffer);
40345
- generatedDuration = finalResult.duration || parseInt(klingDuration);
40562
+ await writeFile25(videoPath, videoBuffer);
40563
+ generatedDuration = finalResult.duration || parseInt(klingDuration2);
40346
40564
  generatedVideos.push(videoPath);
40347
40565
  humanLines.push(` Generated: ${videoFileName} (${generatedDuration}s)`);
40348
40566
  let segmentIndex = 1;
@@ -40411,11 +40629,11 @@ async function executeFillGaps(options) {
40411
40629
  `gap-fill-${gap.start.toFixed(2)}-${gap.end.toFixed(2)}-seg${segmentIndex}.mp4`
40412
40630
  );
40413
40631
  const segVideoBuffer = await downloadVideo(segFinalResult.videoUrl);
40414
- await writeFile24(segVideoPath, segVideoBuffer);
40632
+ await writeFile25(segVideoPath, segVideoBuffer);
40415
40633
  const concatListPath = resolve32(footageDir, `concat-${gap.start.toFixed(2)}.txt`);
40416
40634
  const concatList = generatedVideos.map((v) => `file '${v}'`).join("\n") + `
40417
40635
  file '${segVideoPath}'`;
40418
- await writeFile24(concatListPath, concatList);
40636
+ await writeFile25(concatListPath, concatList);
40419
40637
  const concatOutputPath = resolve32(
40420
40638
  footageDir,
40421
40639
  `gap-fill-${gap.start.toFixed(2)}-${gap.end.toFixed(2)}-merged.mp4`
@@ -40473,7 +40691,7 @@ file '${segVideoPath}'`;
40473
40691
  let outputPath = filePath;
40474
40692
  if (generatedCount > 0) {
40475
40693
  outputPath = options.output ? resolve32(process.cwd(), options.output) : filePath;
40476
- await writeFile24(outputPath, JSON.stringify(project.toJSON(), null, 2));
40694
+ await writeFile25(outputPath, JSON.stringify(project.toJSON(), null, 2));
40477
40695
  humanLines.push(`\u2714 Filled ${generatedCount} gap(s) with AI-generated video`);
40478
40696
  humanLines.push(`Project saved: ${outputPath}`);
40479
40697
  } else {
@@ -40567,7 +40785,7 @@ __export(ai_motion_exports, {
40567
40785
  });
40568
40786
  import { resolve as resolve33 } from "node:path";
40569
40787
  import { existsSync as existsSync33 } from "node:fs";
40570
- import { readFile as readFile25, writeFile as writeFile25 } from "node:fs/promises";
40788
+ import { readFile as readFile25, writeFile as writeFile26 } from "node:fs/promises";
40571
40789
  async function getOptionalGoogleApiKey() {
40572
40790
  const configKey = await getApiKeyFromConfig("google");
40573
40791
  if (configKey) return configKey;
@@ -40737,7 +40955,7 @@ Use this video understanding to place motion graphics in safe zones, match the c
40737
40955
  const defaultOutput = options.video || options.image ? "motion-output.mp4" : options.render ? "motion.webm" : "motion.tsx";
40738
40956
  const outputPath = resolve33(process.cwd(), options.output || defaultOutput);
40739
40957
  const codePath = outputPath.replace(/\.\w+$/, ".tsx");
40740
- await writeFile25(codePath, component.code, "utf-8");
40958
+ await writeFile26(codePath, component.code, "utf-8");
40741
40959
  const shouldRender = options.render || !!options.video || !!options.image;
40742
40960
  if (!shouldRender) {
40743
40961
  return { success: true, codePath, componentName: component.name };
@@ -40973,8 +41191,8 @@ var init_ai_motion = __esm({
40973
41191
  init_output();
40974
41192
  init_validate();
40975
41193
  MODEL_MAP2 = {
40976
- sonnet: { provider: "claude", modelId: "claude-sonnet-4-6" },
40977
- opus: { provider: "claude", modelId: "claude-opus-4-7" },
41194
+ sonnet: { provider: "claude", modelId: "claude-sonnet-5-5" },
41195
+ opus: { provider: "claude", modelId: "claude-opus-5-5" },
40978
41196
  "opus-4-6": { provider: "claude", modelId: "claude-opus-4-6" },
40979
41197
  gemini: { provider: "gemini", modelId: GEMINI_DEFAULT_TEXT_MODEL },
40980
41198
  "gemini-2.5-pro": { provider: "gemini", modelId: "gemini-2.5-pro" },
@@ -41216,7 +41434,7 @@ __export(project_builder_exports, {
41216
41434
  buildTempProject: () => buildTempProject,
41217
41435
  resolveSourceUrl: () => resolveSourceUrl
41218
41436
  });
41219
- import { mkdtemp, mkdir as mkdir16, copyFile as copyFile5, writeFile as writeFile26, rm as rm4, readdir as readdir4 } from "node:fs/promises";
41437
+ import { mkdtemp, mkdir as mkdir17, copyFile as copyFile5, writeFile as writeFile27, rm as rm5, readdir as readdir4 } from "node:fs/promises";
41220
41438
  import { existsSync as existsSync34 } from "node:fs";
41221
41439
  import { tmpdir as tmpdir2 } from "node:os";
41222
41440
  import { createRequire } from "node:module";
@@ -41224,7 +41442,7 @@ import * as path from "node:path";
41224
41442
  async function buildTempProject(state, projectFileDir) {
41225
41443
  const dir = await mkdtemp(path.join(tmpdir2(), "vibeframe-hf-"));
41226
41444
  const assetsDir = path.join(dir, "assets");
41227
- await mkdir16(assetsDir, { recursive: true });
41445
+ await mkdir17(assetsDir, { recursive: true });
41228
41446
  const copied = /* @__PURE__ */ new Map();
41229
41447
  for (const source of state.sources) {
41230
41448
  if (copied.has(source.id)) continue;
@@ -41240,10 +41458,10 @@ async function buildTempProject(state, projectFileDir) {
41240
41458
  await copyLottieRuntime(dir);
41241
41459
  }
41242
41460
  const html = generateCompositionHtml(state);
41243
- await writeFile26(path.join(dir, "index.html"), html, "utf-8");
41461
+ await writeFile27(path.join(dir, "index.html"), html, "utf-8");
41244
41462
  return {
41245
41463
  dir,
41246
- cleanup: () => rm4(dir, { recursive: true, force: true })
41464
+ cleanup: () => rm5(dir, { recursive: true, force: true })
41247
41465
  };
41248
41466
  }
41249
41467
  async function copyLottieRuntime(tempDir) {
@@ -41254,7 +41472,7 @@ async function copyLottieRuntime(tempDir) {
41254
41472
  const wasmSrc = path.join(path.dirname(webEntry), "dotlottie-player.wasm");
41255
41473
  const vendorDir = path.join(tempDir, "vendor");
41256
41474
  const wcDest = path.join(vendorDir, "dotlottie-wc");
41257
- await mkdir16(wcDest, { recursive: true });
41475
+ await mkdir17(wcDest, { recursive: true });
41258
41476
  for (const file of await readdir4(wcDistDir)) {
41259
41477
  if (file.endsWith(".map") || file.endsWith(".d.ts")) continue;
41260
41478
  await copyFile5(path.join(wcDistDir, file), path.join(wcDest, file));
@@ -41362,9 +41580,9 @@ __export(motion_overlay_exports, {
41362
41580
  executeMotionOverlay: () => executeMotionOverlay,
41363
41581
  registerMotionOverlayCommand: () => registerMotionOverlayCommand
41364
41582
  });
41365
- import { mkdir as mkdir17 } from "node:fs/promises";
41583
+ import { mkdir as mkdir18 } from "node:fs/promises";
41366
41584
  import { existsSync as existsSync35 } from "node:fs";
41367
- import { basename as basename9, dirname as dirname14, extname as extname7, resolve as resolve35 } from "node:path";
41585
+ import { basename as basename9, dirname as dirname15, extname as extname8, resolve as resolve35 } from "node:path";
41368
41586
  function normalizeUnderstand2(value) {
41369
41587
  return value === "off" || value === "required" || value === "auto" ? value : "auto";
41370
41588
  }
@@ -41387,7 +41605,7 @@ function assertValidRequest(options) {
41387
41605
  return "Provide a motion description, or pass --asset <logo.json|logo.lottie>.";
41388
41606
  }
41389
41607
  if (hasAsset) {
41390
- const ext = extname7(options.asset).toLowerCase();
41608
+ const ext = extname8(options.asset).toLowerCase();
41391
41609
  if (ext !== ".json" && ext !== ".lottie") {
41392
41610
  return "Lottie overlay asset must be a .json or .lottie file.";
41393
41611
  }
@@ -41510,7 +41728,7 @@ async function executeMotionOverlay(options) {
41510
41728
  const scale = Math.max(0.01, Math.min(2, numberOption(options.scale, position === "full" ? 1 : 0.25)));
41511
41729
  const opacity = Math.max(0, Math.min(1, numberOption(options.opacity, 1)));
41512
41730
  const loop = options.loop ?? true;
41513
- await mkdir17(dirname14(outputPath), { recursive: true });
41731
+ await mkdir18(dirname15(outputPath), { recursive: true });
41514
41732
  const backend = createHyperframesBackend();
41515
41733
  const result2 = await backend.render({
41516
41734
  projectState: buildLottieTimelineState({
@@ -41536,7 +41754,7 @@ async function executeMotionOverlay(options) {
41536
41754
  return { success: true, outputPath, provider: "lottie" };
41537
41755
  }
41538
41756
  const duration = options.duration ?? await ffprobeDuration(absVideo).catch(() => void 0);
41539
- await mkdir17(dirname14(outputPath), { recursive: true });
41757
+ await mkdir18(dirname15(outputPath), { recursive: true });
41540
41758
  const result = await executeMotion({
41541
41759
  description: options.description.trim(),
41542
41760
  video: absVideo,
@@ -41664,8 +41882,8 @@ __export(edit_cmd_exports, {
41664
41882
  executeSpeedRamp: () => executeSpeedRamp,
41665
41883
  executeUpscale: () => executeUpscale
41666
41884
  });
41667
- import { resolve as resolve36, dirname as dirname15 } from "node:path";
41668
- import { readFile as readFile26, writeFile as writeFile27, mkdir as mkdir18 } from "node:fs/promises";
41885
+ import { resolve as resolve36, dirname as dirname16 } from "node:path";
41886
+ import { readFile as readFile26, writeFile as writeFile28, mkdir as mkdir19 } from "node:fs/promises";
41669
41887
  async function executeGrade(options) {
41670
41888
  const { videoPath, style, preset, output, analyzeOnly, apiKey } = options;
41671
41889
  try {
@@ -41858,6 +42076,7 @@ var init_edit_cmd = __esm({
41858
42076
  init_output();
41859
42077
  init_validate();
41860
42078
  init_cost_tier();
42079
+ init_image_file();
41861
42080
  editCommand = new Command("edit").alias("ed").description(
41862
42081
  "Edit and post-process media (silence-cut, caption, grade, reframe, upscale...)"
41863
42082
  ).addHelpText(
@@ -42293,7 +42512,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
42293
42512
  console.log();
42294
42513
  if (options.keyframes) {
42295
42514
  const keyframesPath = resolve36(process.cwd(), options.keyframes);
42296
- await writeFile27(keyframesPath, JSON.stringify(cropKeyframes, null, 2));
42515
+ await writeFile28(keyframesPath, JSON.stringify(cropKeyframes, null, 2));
42297
42516
  console.log(source_default.green(`Keyframes saved to: ${keyframesPath}`));
42298
42517
  }
42299
42518
  if (options.analyzeOnly) {
@@ -42317,7 +42536,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
42317
42536
  exitWithError(generalError(`Reframe failed: ${msg}`));
42318
42537
  }
42319
42538
  });
42320
- 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) => {
42539
+ 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) => {
42321
42540
  const startedAt = Date.now();
42322
42541
  try {
42323
42542
  if (args.length < 2) {
@@ -42376,14 +42595,16 @@ Run 'vibe schema edit.<command>' for structured parameter info.
42376
42595
  const grok = new GrokProvider();
42377
42596
  await grok.initialize({ apiKey });
42378
42597
  result = await grok.editImage(imageBuffers[0], prompt3, {
42598
+ ...resolveGrokImageModel(options.model),
42379
42599
  aspectRatio: options.ratio
42380
42600
  });
42381
42601
  } else {
42382
42602
  const editModelNames = {
42383
- flash: "gemini-2.5-flash-image",
42384
- "3.1-flash": "gemini-3.1-flash-image-preview",
42385
- latest: "gemini-3.1-flash-image-preview",
42386
- pro: "gemini-3-pro-image-preview"
42603
+ flash: "gemini-3.1-flash-image",
42604
+ "3.1-flash": "gemini-3.1-flash-image",
42605
+ latest: "gemini-3.1-flash-image",
42606
+ lite: "gemini-3.1-flash-lite-image",
42607
+ pro: "gemini-3-pro-image"
42387
42608
  };
42388
42609
  const editModelName = editModelNames[options.model] || editModelNames.flash;
42389
42610
  spinner2.text = `Editing with ${editModelName}...`;
@@ -42394,15 +42615,6 @@ Run 'vibe schema edit.<command>' for structured parameter info.
42394
42615
  aspectRatio: options.ratio,
42395
42616
  resolution: options.size
42396
42617
  });
42397
- const fallbackModels = ["latest", "3.1-flash"];
42398
- if (!result.success && fallbackModels.includes(options.model)) {
42399
- spinner2.text = `${source_default.dim(result.error || `${editModelName} failed`)} \u2014 retrying with flash...`;
42400
- result = await gemini.editImage(imageBuffers, prompt3, {
42401
- model: "flash",
42402
- aspectRatio: options.ratio,
42403
- resolution: options.size
42404
- });
42405
- }
42406
42618
  }
42407
42619
  if (!result.success || !result.images || result.images.length === 0) {
42408
42620
  spinner2.fail(result.error || "Image editing failed");
@@ -42412,14 +42624,14 @@ Run 'vibe schema edit.<command>' for structured parameter info.
42412
42624
  const img = result.images[0];
42413
42625
  const outputPath = resolve36(process.cwd(), options.output);
42414
42626
  const saveImage = async () => {
42415
- await mkdir18(dirname15(outputPath), { recursive: true });
42627
+ await mkdir19(dirname16(outputPath), { recursive: true });
42416
42628
  if (img.base64) {
42417
42629
  const buffer = Buffer.from(img.base64, "base64");
42418
- await writeFile27(outputPath, buffer);
42630
+ await writeImageFile(outputPath, buffer);
42419
42631
  } else if (img.url) {
42420
42632
  const resp = await fetch(img.url);
42421
42633
  const arrayBuf = await resp.arrayBuffer();
42422
- await writeFile27(outputPath, Buffer.from(arrayBuf));
42634
+ await writeImageFile(outputPath, Buffer.from(arrayBuf));
42423
42635
  }
42424
42636
  };
42425
42637
  const resultModel = result.model;
@@ -42638,8 +42850,8 @@ __export(ai_image_exports, {
42638
42850
  executeImageGenerate: () => executeImageGenerate,
42639
42851
  executeThumbnailBestFrame: () => executeThumbnailBestFrame
42640
42852
  });
42641
- import { resolve as resolve38, dirname as dirname17 } from "node:path";
42642
- import { readFile as readFile27, writeFile as writeFile29, mkdir as mkdir20 } from "node:fs/promises";
42853
+ import { resolve as resolve38, dirname as dirname18 } from "node:path";
42854
+ import { readFile as readFile27, mkdir as mkdir21 } from "node:fs/promises";
42643
42855
  import { existsSync as existsSync37 } from "node:fs";
42644
42856
  async function executeImageGenerate(options) {
42645
42857
  const {
@@ -42682,8 +42894,8 @@ async function executeImageGenerate(options) {
42682
42894
  return { success: false, error: "No image data available" };
42683
42895
  }
42684
42896
  outputPath = resolve38(process.cwd(), output);
42685
- await mkdir20(dirname17(outputPath), { recursive: true });
42686
- await writeFile29(outputPath, buffer);
42897
+ await mkdir21(dirname18(outputPath), { recursive: true });
42898
+ await writeImageFile(outputPath, buffer);
42687
42899
  }
42688
42900
  return {
42689
42901
  success: true,
@@ -42717,8 +42929,8 @@ async function executeImageGenerate(options) {
42717
42929
  const img = result.images[0];
42718
42930
  if (img.base64) {
42719
42931
  outputPath = resolve38(process.cwd(), output);
42720
- await mkdir20(dirname17(outputPath), { recursive: true });
42721
- await writeFile29(outputPath, Buffer.from(img.base64, "base64"));
42932
+ await mkdir21(dirname18(outputPath), { recursive: true });
42933
+ await writeImageFile(outputPath, Buffer.from(img.base64, "base64"));
42722
42934
  }
42723
42935
  }
42724
42936
  return {
@@ -42731,10 +42943,11 @@ async function executeImageGenerate(options) {
42731
42943
  } else if (provider === "grok") {
42732
42944
  const key = apiKey || process.env.XAI_API_KEY;
42733
42945
  if (!key) return { success: false, error: "XAI_API_KEY required" };
42734
- const openaiImage = new OpenAIImageProvider();
42735
- await openaiImage.initialize({ apiKey: key, baseUrl: "https://api.x.ai/v1" });
42736
- const result = await openaiImage.generateImage(prompt3, {
42737
- size,
42946
+ const grok = new GrokProvider();
42947
+ await grok.initialize({ apiKey: key });
42948
+ const result = await grok.generateImage(prompt3, {
42949
+ ...resolveGrokImageModel(model),
42950
+ aspectRatio: ratio,
42738
42951
  n: count
42739
42952
  });
42740
42953
  if (!result.success || !result.images) {
@@ -42753,8 +42966,8 @@ async function executeImageGenerate(options) {
42753
42966
  return { success: false, error: "No image data available" };
42754
42967
  }
42755
42968
  outputPath = resolve38(process.cwd(), output);
42756
- await mkdir20(dirname17(outputPath), { recursive: true });
42757
- await writeFile29(outputPath, buffer);
42969
+ await mkdir21(dirname18(outputPath), { recursive: true });
42970
+ await writeImageFile(outputPath, buffer);
42758
42971
  }
42759
42972
  return {
42760
42973
  success: true,
@@ -42812,8 +43025,8 @@ async function executeGeminiEdit(options) {
42812
43025
  let outputPath;
42813
43026
  if (img.base64) {
42814
43027
  outputPath = resolve38(process.cwd(), output);
42815
- await mkdir20(dirname17(outputPath), { recursive: true });
42816
- await writeFile29(outputPath, Buffer.from(img.base64, "base64"));
43028
+ await mkdir21(dirname18(outputPath), { recursive: true });
43029
+ await writeImageFile(outputPath, Buffer.from(img.base64, "base64"));
42817
43030
  }
42818
43031
  return {
42819
43032
  success: true,
@@ -42887,6 +43100,7 @@ var init_ai_image = __esm({
42887
43100
  "use strict";
42888
43101
  init_dist2();
42889
43102
  init_exec_safe();
43103
+ init_image_file();
42890
43104
  }
42891
43105
  });
42892
43106
 
@@ -42898,7 +43112,7 @@ __export(ai_analyze_exports, {
42898
43112
  registerAnalyzeCommands: () => registerAnalyzeCommands
42899
43113
  });
42900
43114
  import { readFile as readFile28 } from "node:fs/promises";
42901
- import { extname as extname8, resolve as resolve39 } from "node:path";
43115
+ import { extname as extname9, resolve as resolve39 } from "node:path";
42902
43116
  import { existsSync as existsSync38 } from "node:fs";
42903
43117
  async function executeGeminiVideo(options) {
42904
43118
  try {
@@ -42955,7 +43169,7 @@ async function executeAnalyze(options) {
42955
43169
  const isYouTube = source.includes("youtube.com") || source.includes("youtu.be");
42956
43170
  const isImageUrl = /^https?:\/\/.+\.(png|jpg|jpeg|webp|gif)(\?.*)?$/i.test(source);
42957
43171
  const isVideoUrl = /^https?:\/\/.+\.(mp4|mov|webm)(\?.*)?$/i.test(source);
42958
- const ext = extname8(source).toLowerCase();
43172
+ const ext = extname9(source).toLowerCase();
42959
43173
  const imageExts = [".png", ".jpg", ".jpeg", ".webp", ".gif"];
42960
43174
  const videoExts = [".mp4", ".mov", ".webm", ".avi", ".mkv"];
42961
43175
  const isLocalImage = imageExts.includes(ext);
@@ -43363,7 +43577,7 @@ async function executeSoundEffect(options) {
43363
43577
  }
43364
43578
  }
43365
43579
  function registerSoundEffectCommand(parent) {
43366
- 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) => {
43580
+ 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) => {
43367
43581
  const startedAt = Date.now();
43368
43582
  try {
43369
43583
  rejectControlChars(prompt3);
@@ -43663,9 +43877,9 @@ var init_speech = __esm({
43663
43877
  });
43664
43878
 
43665
43879
  // src/commands/generate/thumbnail.ts
43666
- import { resolve as resolve47, dirname as dirname18, basename as basename13, extname as extname9 } from "node:path";
43880
+ import { resolve as resolve47, dirname as dirname19, basename as basename13, extname as extname10 } from "node:path";
43667
43881
  import { existsSync as existsSync42 } from "node:fs";
43668
- import { writeFile as writeFile33, mkdir as mkdir21 } from "node:fs/promises";
43882
+ import { writeFile as writeFile33, mkdir as mkdir22 } from "node:fs/promises";
43669
43883
  function registerThumbnailCommand(parent) {
43670
43884
  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) => {
43671
43885
  const startedAt = Date.now();
@@ -43688,7 +43902,7 @@ function registerThumbnailCommand(parent) {
43688
43902
  );
43689
43903
  }
43690
43904
  const apiKey2 = await requireApiKey("GOOGLE_API_KEY", "Google", options.apiKey);
43691
- const name = basename13(options.bestFrame, extname9(options.bestFrame));
43905
+ const name = basename13(options.bestFrame, extname10(options.bestFrame));
43692
43906
  const outputPath = options.output || `${name}-thumbnail.png`;
43693
43907
  const spinner3 = ora("Analyzing video for best frame...").start();
43694
43908
  const result2 = await executeThumbnailBestFrame({
@@ -43756,7 +43970,7 @@ function registerThumbnailCommand(parent) {
43756
43970
  throw new Error("No image data available");
43757
43971
  }
43758
43972
  outputPath = resolve47(process.cwd(), options.output);
43759
- await mkdir21(dirname18(outputPath), { recursive: true });
43973
+ await mkdir22(dirname19(outputPath), { recursive: true });
43760
43974
  await writeFile33(outputPath, buffer);
43761
43975
  }
43762
43976
  outputSuccess({
@@ -43787,7 +44001,7 @@ function registerThumbnailCommand(parent) {
43787
44001
  throw new Error("No image data available");
43788
44002
  }
43789
44003
  const outputPath = resolve47(process.cwd(), options.output);
43790
- await mkdir21(dirname18(outputPath), { recursive: true });
44004
+ await mkdir22(dirname19(outputPath), { recursive: true });
43791
44005
  await writeFile33(outputPath, buffer);
43792
44006
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
43793
44007
  } catch {
@@ -43818,7 +44032,7 @@ var init_thumbnail = __esm({
43818
44032
  import { resolve as resolve48 } from "node:path";
43819
44033
  import { writeFile as writeFile34 } from "node:fs/promises";
43820
44034
  function registerVideoExtendCommand(parent) {
43821
- 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) => {
44035
+ 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) => {
43822
44036
  const startedAt = Date.now();
43823
44037
  try {
43824
44038
  const provider = (options.provider || "kling").toLowerCase();
@@ -43936,12 +44150,7 @@ function registerVideoExtendCommand(parent) {
43936
44150
  const spinner2 = ora("Initializing Veo...").start();
43937
44151
  const gemini = new GeminiProvider();
43938
44152
  await gemini.initialize({ apiKey });
43939
- const veoModelMap = {
43940
- "3.0": "veo-3.0-generate-preview",
43941
- "3.1": "veo-3.1-generate-preview",
43942
- "3.1-fast": "veo-3.1-fast-generate-preview"
43943
- };
43944
- const veoModel = veoModelMap[options.veoModel] || "veo-3.1-generate-preview";
44153
+ const veoModel = resolveVeoModel(options.veoModel);
43945
44154
  spinner2.text = "Starting video extension...";
43946
44155
  const result = await gemini.extendVideo(id, options.prompt, {
43947
44156
  duration: parseInt(options.duration),
@@ -44102,10 +44311,15 @@ var init_provider_resolver = __esm({
44102
44311
 
44103
44312
  // src/commands/_shared/openai-image.ts
44104
44313
  function resolveOpenAIImageModel(modelAlias) {
44105
- const isGptImage15 = modelAlias === "1.5" || modelAlias === "gpt-image-1.5";
44314
+ const alias = modelAlias?.trim() ?? "";
44315
+ for (const [id, model] of Object.entries(OPENAI_IMAGE_MODELS)) {
44316
+ if (alias === id || model.aliases.includes(alias)) {
44317
+ return { openaiModel: id, modelLabel: model.label };
44318
+ }
44319
+ }
44106
44320
  return {
44107
- openaiModel: isGptImage15 ? "gpt-image-1.5" : "gpt-image-2",
44108
- modelLabel: isGptImage15 ? "GPT Image 1.5" : "GPT Image 2"
44321
+ openaiModel: OPENAI_IMAGE_DEFAULT_MODEL,
44322
+ modelLabel: OPENAI_IMAGE_MODELS[OPENAI_IMAGE_DEFAULT_MODEL].label
44109
44323
  };
44110
44324
  }
44111
44325
  async function executeOpenAIImageGenerate(prompt3, options, ctx) {
@@ -44121,17 +44335,24 @@ async function executeOpenAIImageGenerate(prompt3, options, ctx) {
44121
44335
  });
44122
44336
  return { result, openaiModel, modelLabel };
44123
44337
  }
44338
+ var OPENAI_IMAGE_MODELS;
44124
44339
  var init_openai_image2 = __esm({
44125
44340
  "src/commands/_shared/openai-image.ts"() {
44126
44341
  "use strict";
44127
44342
  init_dist2();
44343
+ OPENAI_IMAGE_MODELS = {
44344
+ "gpt-image-2.5-sunburst": { label: "GPT Image 2.5 Sunburst", aliases: ["2.5", "sunburst"] },
44345
+ "gpt-image-2.5-flare": { label: "GPT Image 2.5 Flare", aliases: ["flare", "2.5-flare"] },
44346
+ "gpt-image-2": { label: "GPT Image 2", aliases: ["2"] },
44347
+ "gpt-image-1.5": { label: "GPT Image 1.5", aliases: ["1.5"] }
44348
+ };
44128
44349
  }
44129
44350
  });
44130
44351
 
44131
44352
  // src/commands/generate/image.ts
44132
- import { resolve as resolve49, dirname as dirname19 } from "node:path";
44353
+ import { resolve as resolve49, dirname as dirname20 } from "node:path";
44133
44354
  import { fileURLToPath } from "node:url";
44134
- import { writeFile as writeFile35, mkdir as mkdir22 } from "node:fs/promises";
44355
+ import { mkdir as mkdir23 } from "node:fs/promises";
44135
44356
  function registerImageCommand(parent) {
44136
44357
  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(
44137
44358
  "-p, --provider <provider>",
@@ -44142,7 +44363,7 @@ function registerImageCommand(parent) {
44142
44363
  "1:1"
44143
44364
  ).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(
44144
44365
  "-m, --model <model>",
44145
- "Model. Gemini: flash, 3.1-flash, latest, pro. OpenAI: 2 (default), 1.5"
44366
+ "Model. Gemini: flash (default), lite, pro. OpenAI: 2.5 (default), flare, 2, 1.5. Grok: pro"
44146
44367
  ).option("--dry-run", "Preview parameters without executing").addHelpText(
44147
44368
  "after",
44148
44369
  `
@@ -44245,7 +44466,7 @@ Examples:
44245
44466
  const apiKey = await requireApiKey(envKey, providerName, options.apiKey);
44246
44467
  const spinner2 = ora(`Generating image with ${providerName}...`).start();
44247
44468
  if (provider === "openai") {
44248
- const { result, modelLabel } = await executeOpenAIImageGenerate(prompt3, options, {
44469
+ const { result, modelLabel, openaiModel } = await executeOpenAIImageGenerate(prompt3, options, {
44249
44470
  apiKey
44250
44471
  });
44251
44472
  if (!result.success || !result.images) {
@@ -44268,14 +44489,16 @@ Examples:
44268
44489
  } else {
44269
44490
  throw new Error("No image data available");
44270
44491
  }
44271
- await mkdir22(dirname19(outputPath), { recursive: true });
44272
- await writeFile35(outputPath, buffer);
44492
+ await mkdir23(dirname20(outputPath), { recursive: true });
44493
+ await writeImageFile(outputPath, buffer);
44273
44494
  }
44274
44495
  outputSuccess({
44275
44496
  command: "generate image",
44276
44497
  startedAt,
44277
44498
  data: {
44278
44499
  provider: "openai",
44500
+ model: modelLabel,
44501
+ modelId: openaiModel,
44279
44502
  images: result.images.map((img) => ({
44280
44503
  url: img.url,
44281
44504
  revisedPrompt: img.revisedPrompt
@@ -44315,8 +44538,8 @@ Examples:
44315
44538
  throw new Error("No image data available");
44316
44539
  }
44317
44540
  const outputPath = resolve49(process.cwd(), options.output);
44318
- await mkdir22(dirname19(outputPath), { recursive: true });
44319
- await writeFile35(outputPath, buffer);
44541
+ await mkdir23(dirname20(outputPath), { recursive: true });
44542
+ await writeImageFile(outputPath, buffer);
44320
44543
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
44321
44544
  } catch (err) {
44322
44545
  saveSpinner.fail(
@@ -44325,7 +44548,7 @@ Examples:
44325
44548
  }
44326
44549
  }
44327
44550
  } else if (provider === "gemini") {
44328
- const validGeminiModels = ["flash", "3.1-flash", "latest", "pro"];
44551
+ const validGeminiModels = ["flash", "3.1-flash", "latest", "lite", "pro"];
44329
44552
  if (options.model && !validGeminiModels.includes(options.model)) {
44330
44553
  console.warn(
44331
44554
  source_default.yellow(
@@ -44358,26 +44581,17 @@ Examples:
44358
44581
  const gemini = new GeminiProvider();
44359
44582
  await gemini.initialize({ apiKey });
44360
44583
  const geminiModelNames = {
44361
- flash: "Nano Banana",
44584
+ flash: "Nano Banana 2",
44362
44585
  "3.1-flash": "Nano Banana 2",
44363
44586
  latest: "Nano Banana 2",
44587
+ lite: "Nano Banana 2 Lite",
44364
44588
  pro: "Nano Banana Pro"
44365
44589
  };
44366
- const modelLabel = geminiModelNames[options.model] || "Nano Banana";
44367
- let result = await gemini.generateImage(prompt3, {
44590
+ const usedLabel = geminiModelNames[options.model] || "Nano Banana 2";
44591
+ const result = await gemini.generateImage(prompt3, {
44368
44592
  model: options.model,
44369
44593
  aspectRatio: options.ratio
44370
44594
  });
44371
- let usedLabel = modelLabel;
44372
- const fallbackModels = ["latest", "3.1-flash"];
44373
- if (!result.success && options.model && fallbackModels.includes(options.model)) {
44374
- spinner2.text = `${source_default.dim(result.error || "Failed")} \u2014 retrying with Nano Banana (flash)...`;
44375
- result = await gemini.generateImage(prompt3, {
44376
- model: "flash",
44377
- aspectRatio: options.ratio
44378
- });
44379
- usedLabel = "Nano Banana (fallback)";
44380
- }
44381
44595
  if (!result.success || !result.images) {
44382
44596
  spinner2.fail(result.error || "Image generation failed");
44383
44597
  exitWithError(apiError(result.error || "Image generation failed", true));
@@ -44390,16 +44604,17 @@ Examples:
44390
44604
  if (outputPath && result.images.length > 0) {
44391
44605
  const img = result.images[0];
44392
44606
  const buffer = Buffer.from(img.base64, "base64");
44393
- await mkdir22(dirname19(outputPath), { recursive: true });
44394
- await writeFile35(outputPath, buffer);
44607
+ await mkdir23(dirname20(outputPath), { recursive: true });
44608
+ await writeImageFile(outputPath, buffer);
44395
44609
  }
44396
44610
  outputSuccess({
44397
44611
  command: "generate image",
44398
44612
  startedAt,
44399
- warnings: usedLabel.includes("fallback") ? [`Model "${options.model}" failed; fell back to flash`] : [],
44613
+ warnings: [],
44400
44614
  data: {
44401
44615
  provider: "gemini",
44402
44616
  model: usedLabel,
44617
+ modelId: result.model,
44403
44618
  images: result.images.map((img) => ({
44404
44619
  mimeType: img.mimeType
44405
44620
  })),
@@ -44423,8 +44638,8 @@ Examples:
44423
44638
  const img = result.images[0];
44424
44639
  const buffer = Buffer.from(img.base64, "base64");
44425
44640
  const outputPath = resolve49(process.cwd(), options.output);
44426
- await mkdir22(dirname19(outputPath), { recursive: true });
44427
- await writeFile35(outputPath, buffer);
44641
+ await mkdir23(dirname20(outputPath), { recursive: true });
44642
+ await writeImageFile(outputPath, buffer);
44428
44643
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
44429
44644
  } catch (err) {
44430
44645
  saveSpinner.fail(
@@ -44462,6 +44677,7 @@ Examples:
44462
44677
  options.ratio = "1:1";
44463
44678
  }
44464
44679
  const result = await grok.generateImage(prompt3, {
44680
+ ...resolveGrokImageModel(options.model),
44465
44681
  aspectRatio: options.ratio || "1:1",
44466
44682
  n: parseInt(options.count)
44467
44683
  });
@@ -44483,14 +44699,15 @@ Examples:
44483
44699
  } else {
44484
44700
  throw new Error("No image data available");
44485
44701
  }
44486
- await mkdir22(dirname19(outputPath), { recursive: true });
44487
- await writeFile35(outputPath, buffer);
44702
+ await mkdir23(dirname20(outputPath), { recursive: true });
44703
+ await writeImageFile(outputPath, buffer);
44488
44704
  }
44489
44705
  outputSuccess({
44490
44706
  command: "generate image",
44491
44707
  startedAt,
44492
44708
  data: {
44493
44709
  provider: "grok",
44710
+ modelId: resolveGrokImageModel(options.model).model,
44494
44711
  images: result.images.map((img) => ({ url: img.url })),
44495
44712
  outputPath
44496
44713
  }
@@ -44524,8 +44741,8 @@ Examples:
44524
44741
  throw new Error("No image data available");
44525
44742
  }
44526
44743
  const outputPath = resolve49(process.cwd(), options.output);
44527
- await mkdir22(dirname19(outputPath), { recursive: true });
44528
- await writeFile35(outputPath, buffer);
44744
+ await mkdir23(dirname20(outputPath), { recursive: true });
44745
+ await writeImageFile(outputPath, buffer);
44529
44746
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
44530
44747
  } catch (err) {
44531
44748
  saveSpinner.fail(
@@ -44536,7 +44753,7 @@ Examples:
44536
44753
  } else if (provider === "runway") {
44537
44754
  const { spawn: spawn2 } = await import("child_process");
44538
44755
  const __filename = fileURLToPath(import.meta.url);
44539
- const __dirname = dirname19(__filename);
44756
+ const __dirname = dirname20(__filename);
44540
44757
  const scriptPath = resolve49(
44541
44758
  __dirname,
44542
44759
  "../../../../.claude/skills/runway-video/scripts/image.py"
@@ -44607,6 +44824,7 @@ var init_image = __esm({
44607
44824
  init_validate();
44608
44825
  init_provider_resolver();
44609
44826
  init_openai_image2();
44827
+ init_image_file();
44610
44828
  }
44611
44829
  });
44612
44830
 
@@ -45555,11 +45773,11 @@ var init_dist3 = __esm({
45555
45773
 
45556
45774
  // src/commands/generate/video.ts
45557
45775
  import { resolve as resolve50 } from "node:path";
45558
- import { readFile as readFile33, writeFile as writeFile36 } from "node:fs/promises";
45776
+ import { readFile as readFile33, writeFile as writeFile35 } from "node:fs/promises";
45559
45777
  function registerVideoCommand(parent) {
45560
45778
  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(
45561
45779
  "-p, --provider <provider>",
45562
- "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."
45780
+ "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."
45563
45781
  ).option(
45564
45782
  "-k, --api-key <key>",
45565
45783
  "API key (or set FAL_API_KEY / XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY / GOOGLE_API_KEY env)"
@@ -45572,12 +45790,12 @@ function registerVideoCommand(parent) {
45572
45790
  "Aspect ratio: 16:9, 9:16, or 1:1 (auto-detected from image if omitted)"
45573
45791
  ).option("--seed <number>", "Random seed for reproducibility (Runway only)").option("--mode <mode>", "Generation mode: std or pro (Kling only)", "std").option(
45574
45792
  "--seedance-model <model>",
45575
- "Seedance variant: quality or fast (fal.ai only)",
45793
+ "Seedance variant: quality (2.0), fast, or 2.5 (fal.ai only)",
45576
45794
  "quality"
45577
- ).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(
45795
+ ).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(
45578
45796
  "--ref-images <paths...>",
45579
45797
  "Reference images for Seedance reference-to-video or Veo character consistency"
45580
- ).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(
45798
+ ).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(
45581
45799
  "--runway-model <model>",
45582
45800
  "Runway model: gen4.5 (default, text+image-to-video), gen4_turbo (image-to-video only)",
45583
45801
  "gen4.5"
@@ -45638,7 +45856,7 @@ Examples:
45638
45856
  exitWithError(
45639
45857
  usageError(
45640
45858
  `Invalid provider: ${provider}`,
45641
- "Available providers: seedance, grok, kling, runway, veo, omni (experimental). `fal` is a deprecated alias for seedance."
45859
+ "Available providers: seedance, grok, kling, runway, omni, veo. `fal` is a deprecated alias for seedance."
45642
45860
  )
45643
45861
  );
45644
45862
  }
@@ -45706,13 +45924,7 @@ Examples:
45706
45924
  options.ratio = "16:9";
45707
45925
  }
45708
45926
  if (options.dryRun) {
45709
- const costUsd = provider === "seedance" || provider === "fal" ? estimateSeedanceVideoCostUsd({
45710
- durationSec: Number(options.duration) || 5,
45711
- resolution: options.resolution,
45712
- aspectRatio: options.ratio,
45713
- fast: options.seedanceModel === "fast",
45714
- hasVideoReference: Boolean(options.refVideos)
45715
- }) : void 0;
45927
+ const costUsd = provider === "seedance" || provider === "fal" ? realRunCost(provider, options).costUsd : void 0;
45716
45928
  outputSuccess({
45717
45929
  command: "generate video",
45718
45930
  startedAt,
@@ -45730,6 +45942,8 @@ Examples:
45730
45942
  resolution: options.resolution,
45731
45943
  veoModel: options.veoModel,
45732
45944
  seedanceModel: options.seedanceModel,
45945
+ grokModel: options.grokModel,
45946
+ klingModel: options.klingModel,
45733
45947
  refImages: options.refImages,
45734
45948
  refVideos: options.refVideos,
45735
45949
  refAudio: options.refAudio,
@@ -45751,7 +45965,7 @@ Examples:
45751
45965
  runway: "Runway",
45752
45966
  kling: "Kling",
45753
45967
  veo: "Veo",
45754
- omni: "Gemini Omni (experimental)",
45968
+ omni: "Gemini Omni 1.1 Flash",
45755
45969
  grok: "Grok",
45756
45970
  seedance: "Seedance 2.0 via fal.ai"
45757
45971
  };
@@ -45850,6 +46064,7 @@ Examples:
45850
46064
  }
45851
46065
  result = await kling.generateVideo(prompt3, {
45852
46066
  prompt: prompt3,
46067
+ model: resolveKlingModel(options.klingModel),
45853
46068
  referenceImage: klingImage,
45854
46069
  duration: parseInt(options.duration),
45855
46070
  aspectRatio: options.ratio,
@@ -45899,12 +46114,7 @@ Examples:
45899
46114
  } else if (provider === "veo") {
45900
46115
  const gemini = new GeminiProvider();
45901
46116
  await gemini.initialize({ apiKey });
45902
- const veoModelMap = {
45903
- "3.0": "veo-3.0-generate-preview",
45904
- "3.1": "veo-3.1-generate-preview",
45905
- "3.1-fast": "veo-3.1-fast-generate-preview"
45906
- };
45907
- const veoModel = veoModelMap[options.veoModel] || "veo-3.1-fast-generate-preview";
46117
+ const veoModel = resolveVeoModel(options.veoModel);
45908
46118
  const veoDuration = parseInt(options.duration) <= 6 ? 6 : 8;
45909
46119
  let lastFrame;
45910
46120
  if (options.lastFrame) {
@@ -45978,9 +46188,12 @@ Examples:
45978
46188
  await grok.initialize({ apiKey });
45979
46189
  result = await grok.generateVideo(prompt3, {
45980
46190
  prompt: prompt3,
46191
+ model: resolveGrokVideoModel(options.grokModel),
45981
46192
  referenceImage,
45982
46193
  duration: parseInt(options.duration),
45983
- aspectRatio: options.ratio
46194
+ aspectRatio: options.ratio,
46195
+ resolution: options.resolution,
46196
+ generateAudio: options.generateAudio
45984
46197
  });
45985
46198
  if (result.status === "failed") {
45986
46199
  spinner2.fail(result.error || "Failed to start generation");
@@ -46070,9 +46283,8 @@ Examples:
46070
46283
  exitWithError(apiError(message, true));
46071
46284
  }
46072
46285
  }
46073
- spinner2.text = "Generating video with fal.ai Seedance 2.0 (this may take 1-3 minutes)...";
46074
- const seedanceModel = String(options.seedanceModel ?? "quality").toLowerCase();
46075
- const falModel = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
46286
+ const falModel = resolveSeedanceVariant(options.seedanceModel);
46287
+ spinner2.text = `Generating video with fal.ai ${falModel} (this may take 1-3 minutes)...`;
46076
46288
  result = await fal.generateVideo(prompt3, {
46077
46289
  prompt: prompt3,
46078
46290
  referenceImage: seedanceReferences.length > 0 ? void 0 : falImage,
@@ -46089,11 +46301,20 @@ Examples:
46089
46301
  } else if (provider === "omni") {
46090
46302
  const omni = new OmniProvider();
46091
46303
  await omni.initialize({ apiKey });
46092
- spinner2.text = "Generating video with Gemini Omni (experimental)...";
46304
+ spinner2.text = "Generating video with Gemini Omni 1.1 Flash...";
46305
+ let lastFrame;
46306
+ if (options.lastFrame) {
46307
+ const lastFramePath = resolve50(process.cwd(), options.lastFrame);
46308
+ const ext = options.lastFrame.toLowerCase().split(".").pop();
46309
+ const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
46310
+ lastFrame = `data:${mimeType};base64,${(await readFile33(lastFramePath)).toString("base64")}`;
46311
+ }
46093
46312
  result = await omni.generateVideo(prompt3, {
46094
46313
  prompt: prompt3,
46095
46314
  referenceImage,
46096
- aspectRatio: options.ratio
46315
+ lastFrame,
46316
+ aspectRatio: options.ratio,
46317
+ resolution: options.resolution
46097
46318
  });
46098
46319
  finalResult = result;
46099
46320
  }
@@ -46107,14 +46328,14 @@ Examples:
46107
46328
  if (options.output && finalResult.videoUrl) {
46108
46329
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
46109
46330
  outputPath = resolve50(process.cwd(), options.output);
46110
- await writeFile36(outputPath, buffer);
46331
+ await writeFile35(outputPath, buffer);
46111
46332
  }
46112
46333
  const cost = realRunCost(provider, options);
46113
46334
  outputSuccess({
46114
46335
  command: "generate video",
46115
46336
  startedAt,
46116
46337
  costUsd: cost.costUsd,
46117
- warnings: cost.warnings,
46338
+ warnings: provider === "veo" ? [VEO_SHUTDOWN_WARNING, ...cost.warnings] : cost.warnings,
46118
46339
  data: {
46119
46340
  provider,
46120
46341
  taskId: result?.id,
@@ -46126,6 +46347,9 @@ Examples:
46126
46347
  return;
46127
46348
  }
46128
46349
  console.log();
46350
+ if (provider === "veo") {
46351
+ console.log(source_default.yellow(VEO_SHUTDOWN_WARNING));
46352
+ }
46129
46353
  if (finalResult.videoUrl) {
46130
46354
  console.log(`Video URL: ${finalResult.videoUrl}`);
46131
46355
  }
@@ -46138,7 +46362,7 @@ Examples:
46138
46362
  try {
46139
46363
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
46140
46364
  const outputPath = resolve50(process.cwd(), options.output);
46141
- await writeFile36(outputPath, buffer);
46365
+ await writeFile35(outputPath, buffer);
46142
46366
  downloadSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
46143
46367
  } catch (err) {
46144
46368
  downloadSpinner.fail(
@@ -46153,12 +46377,14 @@ Examples:
46153
46377
  }
46154
46378
  function realRunCost(provider, options) {
46155
46379
  if (provider === "seedance" || provider === "fal") {
46380
+ const variant = resolveSeedanceVariant(options.seedanceModel);
46156
46381
  return {
46157
46382
  costUsd: estimateSeedanceVideoCostUsd({
46158
46383
  durationSec: Number(options.duration) || 5,
46159
46384
  resolution: options.resolution,
46160
46385
  aspectRatio: options.ratio,
46161
- fast: options.seedanceModel === "fast",
46386
+ fast: variant === "seedance-2.0-fast",
46387
+ v25: variant === "seedance-2.5",
46162
46388
  hasVideoReference: Boolean(options.refVideos)
46163
46389
  }),
46164
46390
  warnings: []
@@ -46248,6 +46474,7 @@ function printVideoStarted(providerLabel, taskId, taskType) {
46248
46474
  console.log(`Task ID: ${source_default.bold(taskId)}`);
46249
46475
  if (taskType) console.log(`Type: ${taskType}`);
46250
46476
  }
46477
+ var VEO_SHUTDOWN_WARNING;
46251
46478
  var init_video = __esm({
46252
46479
  "src/commands/generate/video.ts"() {
46253
46480
  "use strict";
@@ -46263,6 +46490,7 @@ var init_video = __esm({
46263
46490
  init_upload_host();
46264
46491
  init_ai_helpers();
46265
46492
  init_status_jobs();
46493
+ VEO_SHUTDOWN_WARNING = "Veo 3.1 preview models shut down on 2026-10-22. Use `-p omni` (Gemini Omni 1.1 Flash) instead.";
46266
46494
  }
46267
46495
  });
46268
46496
 
@@ -46282,7 +46510,7 @@ var init_sanitize = __esm({
46282
46510
 
46283
46511
  // src/commands/generate/storyboard.ts
46284
46512
  import { resolve as resolve51 } from "node:path";
46285
- import { writeFile as writeFile37 } from "node:fs/promises";
46513
+ import { writeFile as writeFile36 } from "node:fs/promises";
46286
46514
  async function executeStoryboard(options) {
46287
46515
  const { content, duration, creativity = "low", output, apiKey } = options;
46288
46516
  try {
@@ -46301,7 +46529,7 @@ async function executeStoryboard(options) {
46301
46529
  let outputPath;
46302
46530
  if (output) {
46303
46531
  outputPath = resolve51(process.cwd(), output);
46304
- await writeFile37(outputPath, JSON.stringify(segments, null, 2), "utf-8");
46532
+ await writeFile36(outputPath, JSON.stringify(segments, null, 2), "utf-8");
46305
46533
  }
46306
46534
  return { success: true, segments, segmentCount: segments.length, outputPath };
46307
46535
  } catch (error) {
@@ -46420,7 +46648,7 @@ var init_openai2 = __esm({
46420
46648
  OpenAIAdapter = class {
46421
46649
  provider = "openai";
46422
46650
  client = null;
46423
- model = "gpt-5-mini";
46651
+ model = "gpt-5.4-mini";
46424
46652
  async initialize(apiKey) {
46425
46653
  this.client = new OpenAI({ apiKey });
46426
46654
  }
@@ -46516,7 +46744,7 @@ var init_claude2 = __esm({
46516
46744
  ClaudeAdapter = class {
46517
46745
  provider = "claude";
46518
46746
  client = null;
46519
- model = "claude-sonnet-4-6";
46747
+ model = "claude-sonnet-5-5";
46520
46748
  async initialize(apiKey) {
46521
46749
  this.client = new Anthropic({ apiKey });
46522
46750
  }
@@ -46541,6 +46769,13 @@ var init_claude2 = __esm({
46541
46769
  content: msg.content
46542
46770
  });
46543
46771
  } else if (msg.role === "assistant") {
46772
+ if (msg.providerContent) {
46773
+ claudeMessages.push({
46774
+ role: "assistant",
46775
+ content: msg.providerContent
46776
+ });
46777
+ continue;
46778
+ }
46544
46779
  const content = [];
46545
46780
  if (msg.content) {
46546
46781
  content.push({ type: "text", text: msg.content });
@@ -46562,16 +46797,17 @@ var init_claude2 = __esm({
46562
46797
  });
46563
46798
  }
46564
46799
  } else if (msg.role === "tool") {
46565
- claudeMessages.push({
46566
- role: "user",
46567
- content: [
46568
- {
46569
- type: "tool_result",
46570
- tool_use_id: msg.toolCallId,
46571
- content: msg.content
46572
- }
46573
- ]
46574
- });
46800
+ const result = {
46801
+ type: "tool_result",
46802
+ tool_use_id: msg.toolCallId,
46803
+ content: msg.content
46804
+ };
46805
+ const last = claudeMessages.at(-1);
46806
+ if (last?.role === "user" && Array.isArray(last.content)) {
46807
+ last.content.push(result);
46808
+ } else {
46809
+ claudeMessages.push({ role: "user", content: [result] });
46810
+ }
46575
46811
  }
46576
46812
  }
46577
46813
  const claudeTools = tools.map((tool) => ({
@@ -46585,7 +46821,8 @@ var init_claude2 = __esm({
46585
46821
  }));
46586
46822
  const response = await this.client.messages.create({
46587
46823
  model: this.model,
46588
- max_tokens: 4096,
46824
+ // Thinking tokens count against this cap on Claude 5.x.
46825
+ max_tokens: 16e3,
46589
46826
  system: systemPrompt,
46590
46827
  messages: claudeMessages,
46591
46828
  tools: claudeTools.length > 0 ? claudeTools : void 0
@@ -46612,7 +46849,8 @@ var init_claude2 = __esm({
46612
46849
  return {
46613
46850
  content: textContent,
46614
46851
  toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
46615
- finishReason
46852
+ finishReason,
46853
+ providerContent: response.content
46616
46854
  };
46617
46855
  }
46618
46856
  };
@@ -46622,17 +46860,51 @@ var init_claude2 = __esm({
46622
46860
  // src/agent/adapters/gemini.ts
46623
46861
  var gemini_exports = {};
46624
46862
  __export(gemini_exports, {
46625
- GeminiAdapter: () => GeminiAdapter
46863
+ GeminiAdapter: () => GeminiAdapter,
46864
+ toGeminiSchema: () => toGeminiSchema
46626
46865
  });
46627
46866
  import { GoogleGenerativeAI, SchemaType } from "@google/generative-ai";
46628
- var GeminiAdapter;
46867
+ function toGeminiSchema(schema) {
46868
+ if (Array.isArray(schema)) return schema.map(toGeminiSchema);
46869
+ if (!schema || typeof schema !== "object") return schema;
46870
+ const out = {};
46871
+ for (const [key, value] of Object.entries(schema)) {
46872
+ if (!GEMINI_SCHEMA_KEYS.has(key)) continue;
46873
+ if (key === "properties" && value && typeof value === "object") {
46874
+ out.properties = Object.fromEntries(
46875
+ Object.entries(value).map(([name, prop]) => [name, toGeminiSchema(prop)])
46876
+ );
46877
+ } else if (key === "items") {
46878
+ out.items = toGeminiSchema(value);
46879
+ } else {
46880
+ out[key] = value;
46881
+ }
46882
+ }
46883
+ return out;
46884
+ }
46885
+ var GEMINI_SCHEMA_KEYS, GeminiAdapter;
46629
46886
  var init_gemini2 = __esm({
46630
46887
  "src/agent/adapters/gemini.ts"() {
46631
46888
  "use strict";
46889
+ init_dist2();
46890
+ GEMINI_SCHEMA_KEYS = /* @__PURE__ */ new Set([
46891
+ "type",
46892
+ "format",
46893
+ "description",
46894
+ "nullable",
46895
+ "enum",
46896
+ "items",
46897
+ "properties",
46898
+ "required",
46899
+ "minItems",
46900
+ "maxItems",
46901
+ "minimum",
46902
+ "maximum"
46903
+ ]);
46632
46904
  GeminiAdapter = class {
46633
46905
  provider = "gemini";
46634
46906
  client = null;
46635
- model = "gemini-2.5-flash";
46907
+ model = GEMINI_AGENT_DEFAULT_TEXT_MODEL;
46636
46908
  async initialize(apiKey) {
46637
46909
  this.client = new GoogleGenerativeAI(apiKey);
46638
46910
  }
@@ -46654,7 +46926,12 @@ var init_gemini2 = __esm({
46654
46926
  description: tool.description,
46655
46927
  parameters: {
46656
46928
  type: SchemaType.OBJECT,
46657
- properties: tool.parameters.properties,
46929
+ properties: Object.fromEntries(
46930
+ Object.entries(tool.parameters.properties).map(([name, prop]) => [
46931
+ name,
46932
+ toGeminiSchema(prop)
46933
+ ])
46934
+ ),
46658
46935
  required: tool.parameters.required
46659
46936
  }
46660
46937
  }))
@@ -46673,6 +46950,10 @@ var init_gemini2 = __esm({
46673
46950
  parts: [{ text: msg.content }]
46674
46951
  });
46675
46952
  } else if (msg.role === "assistant") {
46953
+ if (msg.providerContent) {
46954
+ geminiContents.push({ role: "model", parts: msg.providerContent });
46955
+ continue;
46956
+ }
46676
46957
  const parts = [];
46677
46958
  if (msg.content) {
46678
46959
  parts.push({ text: msg.content });
@@ -46694,16 +46975,19 @@ var init_gemini2 = __esm({
46694
46975
  });
46695
46976
  }
46696
46977
  } else if (msg.role === "tool") {
46697
- geminiContents.push({
46698
- role: "user",
46699
- parts: [{
46700
- functionResponse: {
46701
- name: msg.toolCallId.split(":::")[0] || "unknown",
46702
- // Extract function name from id
46703
- response: { result: msg.content }
46704
- }
46705
- }]
46706
- });
46978
+ const part = {
46979
+ functionResponse: {
46980
+ name: msg.toolCallId.split(":::")[0] || "unknown",
46981
+ // Extract function name from id
46982
+ response: { result: msg.content }
46983
+ }
46984
+ };
46985
+ const last = geminiContents.at(-1);
46986
+ if (last?.role === "user" && last.parts.every((p) => "functionResponse" in p)) {
46987
+ last.parts.push(part);
46988
+ } else {
46989
+ geminiContents.push({ role: "user", parts: [part] });
46990
+ }
46707
46991
  }
46708
46992
  }
46709
46993
  const result = await model.generateContent({
@@ -46740,7 +47024,8 @@ var init_gemini2 = __esm({
46740
47024
  return {
46741
47025
  content: textContent,
46742
47026
  toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
46743
- finishReason
47027
+ finishReason,
47028
+ providerContent: candidate.content.parts
46744
47029
  };
46745
47030
  }
46746
47031
  };
@@ -46943,7 +47228,7 @@ var init_xai = __esm({
46943
47228
  XAIAdapter = class {
46944
47229
  provider = "xai";
46945
47230
  client = null;
46946
- model = "grok-4-1-fast-reasoning";
47231
+ model = "grok-4.3";
46947
47232
  async initialize(apiKey) {
46948
47233
  this.client = new OpenAI2({
46949
47234
  apiKey,
@@ -47243,7 +47528,7 @@ __export(detect_exports, {
47243
47528
  executeDetectScenes: () => executeDetectScenes,
47244
47529
  executeDetectSilence: () => executeDetectSilence
47245
47530
  });
47246
- import { readFile as readFile38, writeFile as writeFile41 } from "node:fs/promises";
47531
+ import { readFile as readFile38, writeFile as writeFile40 } from "node:fs/promises";
47247
47532
  import { resolve as resolve56, basename as basename15 } from "node:path";
47248
47533
  async function executeDetectScenes(options) {
47249
47534
  try {
@@ -47282,7 +47567,7 @@ async function executeDetectScenes(options) {
47282
47567
  }));
47283
47568
  if (options.outputPath) {
47284
47569
  const outputPath = resolve56(process.cwd(), options.outputPath);
47285
- await writeFile41(outputPath, JSON.stringify({ source: absPath, totalDuration, threshold, scenes: result }, null, 2), "utf-8");
47570
+ await writeFile40(outputPath, JSON.stringify({ source: absPath, totalDuration, threshold, scenes: result }, null, 2), "utf-8");
47286
47571
  }
47287
47572
  return { success: true, scenes: result, totalDuration };
47288
47573
  } catch (error) {
@@ -47329,7 +47614,7 @@ async function executeDetectSilence(options) {
47329
47614
  }
47330
47615
  if (options.outputPath) {
47331
47616
  const outputPath = resolve56(process.cwd(), options.outputPath);
47332
- await writeFile41(outputPath, JSON.stringify({ source: absPath, silences }, null, 2), "utf-8");
47617
+ await writeFile40(outputPath, JSON.stringify({ source: absPath, silences }, null, 2), "utf-8");
47333
47618
  }
47334
47619
  return { success: true, silences };
47335
47620
  } catch (error) {
@@ -47378,7 +47663,7 @@ async function executeDetectBeats(options) {
47378
47663
  }
47379
47664
  if (options.outputPath) {
47380
47665
  const outputPath = resolve56(process.cwd(), options.outputPath);
47381
- await writeFile41(outputPath, JSON.stringify({ source: absPath, beatCount: beats.length, beats }, null, 2), "utf-8");
47666
+ await writeFile40(outputPath, JSON.stringify({ source: absPath, beatCount: beats.length, beats }, null, 2), "utf-8");
47382
47667
  }
47383
47668
  return { success: true, beats, beatCount: beats.length };
47384
47669
  } catch (error) {
@@ -47482,7 +47767,7 @@ var init_detect = __esm({
47482
47767
  duration: (i < scenes.length - 1 ? scenes[i + 1].timestamp : totalDuration) - s.timestamp
47483
47768
  }))
47484
47769
  };
47485
- await writeFile41(outputPath, JSON.stringify(result, null, 2), "utf-8");
47770
+ await writeFile40(outputPath, JSON.stringify(result, null, 2), "utf-8");
47486
47771
  console.log(source_default.green(`Saved to: ${outputPath}`));
47487
47772
  }
47488
47773
  if (options.project) {
@@ -47512,7 +47797,7 @@ var init_detect = __esm({
47512
47797
  sourceEndOffset: end
47513
47798
  });
47514
47799
  }
47515
- await writeFile41(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
47800
+ await writeFile40(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
47516
47801
  console.log(source_default.green(`Added ${scenes.length} clips to project`));
47517
47802
  }
47518
47803
  } catch (error) {
@@ -47594,7 +47879,7 @@ var init_detect = __esm({
47594
47879
  console.log();
47595
47880
  if (options.output) {
47596
47881
  const outputPath = resolve56(process.cwd(), options.output);
47597
- await writeFile41(
47882
+ await writeFile40(
47598
47883
  outputPath,
47599
47884
  JSON.stringify({ source: absPath, silences }, null, 2),
47600
47885
  "utf-8"
@@ -47679,7 +47964,7 @@ var init_detect = __esm({
47679
47964
  console.log();
47680
47965
  if (options.output) {
47681
47966
  const outputPath = resolve56(process.cwd(), options.output);
47682
- await writeFile41(
47967
+ await writeFile40(
47683
47968
  outputPath,
47684
47969
  JSON.stringify({ source: absPath, beatCount: beats.length, beats }, null, 2),
47685
47970
  "utf-8"
@@ -52300,8 +52585,8 @@ init_tts_resolve();
52300
52585
  init_scene_project();
52301
52586
  init_project_config();
52302
52587
  init_cost_tier();
52303
- import { resolve as resolve15, relative as relative5, dirname as dirname4 } from "node:path";
52304
- import { mkdir as mkdir7, readFile as readFile13, writeFile as writeFile13, access as access4, copyFile as copyFile2 } from "node:fs/promises";
52588
+ import { resolve as resolve15, relative as relative5, dirname as dirname5 } from "node:path";
52589
+ import { mkdir as mkdir8, readFile as readFile13, writeFile as writeFile13, access as access4, copyFile as copyFile2 } from "node:fs/promises";
52305
52590
  import { existsSync as existsSync12 } from "node:fs";
52306
52591
 
52307
52592
  // src/commands/_shared/scene-html-emit.ts
@@ -52781,7 +53066,7 @@ init_storyboard_parse();
52781
53066
  init_storyboard_source();
52782
53067
  init_root_sync();
52783
53068
  import { existsSync as existsSync10 } from "node:fs";
52784
- import { mkdir as mkdir6, writeFile as writeFile11 } from "node:fs/promises";
53069
+ import { mkdir as mkdir7, writeFile as writeFile11 } from "node:fs/promises";
52785
53070
  import { join as join10, resolve as resolve12 } from "node:path";
52786
53071
  async function executeSceneSubmit(opts) {
52787
53072
  const projectDir = resolve12(opts.projectDir);
@@ -52875,7 +53160,7 @@ async function executeSceneSubmit(opts) {
52875
53160
  return { ...base, success: true };
52876
53161
  }
52877
53162
  const absPath = join10(projectDir, scenePath);
52878
- await mkdir6(join10(projectDir, "compositions"), { recursive: true });
53163
+ await mkdir7(join10(projectDir, "compositions"), { recursive: true });
52879
53164
  await writeFile11(absPath, html.endsWith("\n") ? html : `${html}
52880
53165
  `, "utf-8");
52881
53166
  return {
@@ -53466,7 +53751,7 @@ async function executeSceneAdd(opts) {
53466
53751
  }
53467
53752
  audioRelPath = `assets/narration-${id}.${ext}`;
53468
53753
  audioAbsPath = resolve15(projectDir, audioRelPath);
53469
- await mkdir7(dirname4(audioAbsPath), { recursive: true });
53754
+ await mkdir8(dirname5(audioAbsPath), { recursive: true });
53470
53755
  await copyFile2(sourceAbs, audioAbsPath);
53471
53756
  try {
53472
53757
  narrationDuration = await getAudioDuration(audioAbsPath);
@@ -53499,7 +53784,7 @@ async function executeSceneAdd(opts) {
53499
53784
  }
53500
53785
  audioRelPath = `assets/narration-${id}.${resolution.audioExtension}`;
53501
53786
  audioAbsPath = resolve15(projectDir, audioRelPath);
53502
- await mkdir7(dirname4(audioAbsPath), { recursive: true });
53787
+ await mkdir8(dirname5(audioAbsPath), { recursive: true });
53503
53788
  await writeFile13(audioAbsPath, tts.audioBuffer);
53504
53789
  try {
53505
53790
  narrationDuration = await getAudioDuration(audioAbsPath);
@@ -53562,7 +53847,7 @@ async function executeSceneAdd(opts) {
53562
53847
  const img = imageResult.images[0];
53563
53848
  imageRelPath = `assets/scene-${id}.png`;
53564
53849
  imageAbsPath = resolve15(projectDir, imageRelPath);
53565
- await mkdir7(dirname4(imageAbsPath), { recursive: true });
53850
+ await mkdir8(dirname5(imageAbsPath), { recursive: true });
53566
53851
  let buffer;
53567
53852
  if (img.base64) {
53568
53853
  buffer = Buffer.from(img.base64, "base64");
@@ -53589,7 +53874,7 @@ async function executeSceneAdd(opts) {
53589
53874
  }
53590
53875
  imageRelPath = `assets/scene-${id}.png`;
53591
53876
  imageAbsPath = resolve15(projectDir, imageRelPath);
53592
- await mkdir7(dirname4(imageAbsPath), { recursive: true });
53877
+ await mkdir8(dirname5(imageAbsPath), { recursive: true });
53593
53878
  const buffer = Buffer.from(imageResult.images[0].base64, "base64");
53594
53879
  await writeFile13(imageAbsPath, buffer);
53595
53880
  }
@@ -53608,7 +53893,7 @@ async function executeSceneAdd(opts) {
53608
53893
  }
53609
53894
  lottieRelPath = `assets/lottie-${id}.${ext}`;
53610
53895
  lottieAbsPath = resolve15(projectDir, lottieRelPath);
53611
- await mkdir7(dirname4(lottieAbsPath), { recursive: true });
53896
+ await mkdir8(dirname5(lottieAbsPath), { recursive: true });
53612
53897
  await copyFile2(sourceAbs, lottieAbsPath);
53613
53898
  lottieInput = {
53614
53899
  src: lottieRelPath,
@@ -53678,7 +53963,7 @@ async function executeSceneAdd(opts) {
53678
53963
  transcript: transcriptWords,
53679
53964
  lottie: lottieInput
53680
53965
  });
53681
- await mkdir7(dirname4(scenePath), { recursive: true });
53966
+ await mkdir8(dirname5(scenePath), { recursive: true });
53682
53967
  await writeFile13(scenePath, sceneHtml, "utf-8");
53683
53968
  opts.onProgress?.("Updating root composition...");
53684
53969
  let start = 0;
@@ -53895,6 +54180,7 @@ init_scene_build();
53895
54180
  init_install_skill();
53896
54181
  init_compose_prompts();
53897
54182
  init_scene_repair();
54183
+ init_build_video_providers();
53898
54184
  var SCENE_PRESETS2 = [
53899
54185
  "simple",
53900
54186
  "announcement",
@@ -54294,12 +54580,12 @@ var sceneBuildSchema = external_exports.object({
54294
54580
  "TTS provider override. Default 'auto' (elevenlabs key > openai key > local kokoro)."
54295
54581
  ),
54296
54582
  voice: external_exports.string().optional().describe("TTS voice id (provider-specific)."),
54297
- imageProvider: external_exports.enum(["openai"]).optional().describe("Image provider for backdrops. Default 'openai' (gpt-image-2)."),
54298
- videoProvider: external_exports.enum(["seedance", "grok", "kling", "runway", "veo"]).optional().describe("Video provider for per-beat video cues. Default seedance."),
54583
+ imageProvider: external_exports.enum(["openai"]).optional().describe("Image provider for backdrops. Default 'openai' (gpt-image-2.5-sunburst)."),
54584
+ videoProvider: external_exports.enum(BUILD_VIDEO_PROVIDERS).optional().describe("Video provider for per-beat video cues. Default seedance."),
54299
54585
  musicProvider: external_exports.enum(["elevenlabs", "replicate"]).optional().describe("Music provider for per-beat music cues. Default elevenlabs."),
54300
54586
  imageQuality: external_exports.enum(["standard", "hd"]).optional().describe("OpenAI image quality. Default 'standard'."),
54301
54587
  imageModel: external_exports.string().optional().describe(
54302
- "Image model for keyframes/backdrops/character sheets (gemini: flash|pro, openai: gpt-image-2). Provider default when omitted."
54588
+ "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."
54303
54589
  ),
54304
54590
  imageSize: external_exports.enum(["1024x1024", "1536x1024", "1024x1536"]).optional().describe("OpenAI image size. Default '1536x1024' (cinematic 16:9-ish)."),
54305
54591
  maxCostUsd: external_exports.number().optional().describe("Fail before provider spend when estimated cost exceeds this USD cap."),
@@ -54729,8 +55015,8 @@ init_ai_edit();
54729
55015
  init_api_key();
54730
55016
  init_exec_safe();
54731
55017
  init_remotion();
54732
- import { resolve as resolve37, dirname as dirname16, basename as basename10 } from "node:path";
54733
- import { writeFile as writeFile28, mkdir as mkdir19, rm as rm5 } from "node:fs/promises";
55018
+ import { resolve as resolve37, dirname as dirname17, basename as basename10 } from "node:path";
55019
+ import { writeFile as writeFile29, mkdir as mkdir20, rm as rm6 } from "node:fs/promises";
54734
55020
  import { existsSync as existsSync36 } from "node:fs";
54735
55021
  import { tmpdir as tmpdir3 } from "node:os";
54736
55022
  var ASS_STYLES = ["karaoke-sweep", "typewriter"];
@@ -54882,7 +55168,7 @@ async function executeAnimatedCaption(options) {
54882
55168
  }
54883
55169
  const effectiveFontSize = fontSize ?? Math.round(height * 0.04);
54884
55170
  const tmpAudioDir = resolve37(tmpdir3(), `vf-ac-${Date.now()}`);
54885
- await mkdir19(tmpAudioDir, { recursive: true });
55171
+ await mkdir20(tmpAudioDir, { recursive: true });
54886
55172
  const audioPath = resolve37(tmpAudioDir, "audio.wav");
54887
55173
  await execSafe("ffmpeg", [
54888
55174
  "-y",
@@ -54899,21 +55185,21 @@ async function executeAnimatedCaption(options) {
54899
55185
  ], { timeout: 12e4 });
54900
55186
  const apiKey = await getApiKey("OPENAI_API_KEY", "OpenAI");
54901
55187
  if (!apiKey) {
54902
- await rm5(tmpAudioDir, { recursive: true, force: true }).catch(() => {
55188
+ await rm6(tmpAudioDir, { recursive: true, force: true }).catch(() => {
54903
55189
  });
54904
55190
  return { success: false, error: "OPENAI_API_KEY required for Whisper transcription. Run 'vibe setup' or set OPENAI_API_KEY in .env" };
54905
55191
  }
54906
55192
  const transcript = await transcribeWithWords(audioPath, apiKey, language);
54907
55193
  if (!transcript.words || transcript.words.length === 0) {
54908
- await rm5(tmpAudioDir, { recursive: true, force: true }).catch(() => {
55194
+ await rm6(tmpAudioDir, { recursive: true, force: true }).catch(() => {
54909
55195
  });
54910
55196
  return { success: false, error: "No words detected in transcription" };
54911
55197
  }
54912
55198
  const groups = groupWords(transcript.words, { wordsPerGroup, maxChars });
54913
55199
  const absOutputPath = resolve37(process.cwd(), outputPath);
54914
- const outDir = dirname16(absOutputPath);
55200
+ const outDir = dirname17(absOutputPath);
54915
55201
  if (!existsSync36(outDir)) {
54916
- await mkdir19(outDir, { recursive: true });
55202
+ await mkdir20(outDir, { recursive: true });
54917
55203
  }
54918
55204
  if (tier === "ass") {
54919
55205
  const assContent = generateASS(
@@ -54922,7 +55208,7 @@ async function executeAnimatedCaption(options) {
54922
55208
  { highlightColor, fontSize: effectiveFontSize, position, width, height }
54923
55209
  );
54924
55210
  const assPath = resolve37(tmpAudioDir, "captions.ass");
54925
- await writeFile28(assPath, assContent, "utf-8");
55211
+ await writeFile29(assPath, assContent, "utf-8");
54926
55212
  const escapedAssPath = assPath.replace(/\\/g, "\\\\").replace(/:/g, "\\:");
54927
55213
  await execSafe("ffmpeg", [
54928
55214
  "-y",
@@ -54959,12 +55245,12 @@ async function executeAnimatedCaption(options) {
54959
55245
  outputPath: absOutputPath
54960
55246
  });
54961
55247
  if (!renderResult.success) {
54962
- await rm5(tmpAudioDir, { recursive: true, force: true }).catch(() => {
55248
+ await rm6(tmpAudioDir, { recursive: true, force: true }).catch(() => {
54963
55249
  });
54964
55250
  return { success: false, error: renderResult.error };
54965
55251
  }
54966
55252
  }
54967
- await rm5(tmpAudioDir, { recursive: true, force: true }).catch(() => {
55253
+ await rm6(tmpAudioDir, { recursive: true, force: true }).catch(() => {
54968
55254
  });
54969
55255
  return {
54970
55256
  success: true,
@@ -55373,7 +55659,7 @@ var editImageTool = defineTool({
55373
55659
  imagePaths: external_exports.array(external_exports.string()).describe("Input image file path(s)"),
55374
55660
  prompt: external_exports.string().describe("Edit instruction"),
55375
55661
  output: external_exports.string().optional().describe("Output file path (default: edited.png)"),
55376
- model: external_exports.enum(["flash", "3.1-flash", "latest", "pro"]).optional().describe("Gemini model (default: flash)"),
55662
+ model: external_exports.enum(["flash", "3.1-flash", "latest", "lite", "pro"]).optional().describe("Gemini model (default: flash = Nano Banana 2)"),
55377
55663
  ratio: external_exports.string().optional().describe("Output aspect ratio"),
55378
55664
  resolution: external_exports.string().optional().describe("Resolution: 1K, 2K, 4K (Pro only)")
55379
55665
  }),
@@ -57224,7 +57510,7 @@ var generateSoundEffectTool = defineTool({
57224
57510
  schema: external_exports.object({
57225
57511
  prompt: external_exports.string().describe("Description of the sound effect"),
57226
57512
  output: external_exports.string().optional().describe("Output audio file path (default: sound-effect.mp3)"),
57227
- duration: external_exports.number().optional().describe("Duration in seconds (0.5-22, default: auto)"),
57513
+ duration: external_exports.number().optional().describe("Duration in seconds (0.5-30, default: auto)"),
57228
57514
  promptInfluence: external_exports.number().optional().describe("Prompt influence 0-1 (default: 0.3)")
57229
57515
  }),
57230
57516
  async execute(args) {
@@ -57301,7 +57587,7 @@ var generateImageTool = defineTool({
57301
57587
  ratio: external_exports.string().optional().describe("Aspect ratio for Gemini (1:1, 16:9, 9:16, 4:3, 3:4, etc.)"),
57302
57588
  quality: external_exports.string().optional().describe("Quality for OpenAI: standard, hd"),
57303
57589
  count: external_exports.number().optional().describe("Number of images (default: 1)"),
57304
- model: external_exports.string().optional().describe("Gemini model: flash, 3.1-flash, latest, pro")
57590
+ model: external_exports.string().optional().describe("Image model. Gemini: flash (default), lite, pro. OpenAI: 2.5 (default), flare, 2, 1.5. Grok: pro")
57305
57591
  }),
57306
57592
  async execute(args) {
57307
57593
  const result = await executeImageGenerate(args);
@@ -57364,9 +57650,11 @@ var generateVideoTool = defineTool({
57364
57650
  mode: external_exports.string().optional().describe("Kling mode: std or pro"),
57365
57651
  negative: external_exports.string().optional().describe("Negative prompt (Seedance/Kling/Veo)"),
57366
57652
  resolution: external_exports.string().optional().describe("Resolution: 480p, 720p, 1080p, or 4k depending on provider"),
57367
- veoModel: external_exports.string().optional().describe("Veo model: 3.0, 3.1, 3.1-fast"),
57653
+ veoModel: external_exports.string().optional().describe("Veo model: 3.1, 3.1-fast"),
57368
57654
  runwayModel: external_exports.string().optional().describe("Runway model: gen4.5, gen4_turbo"),
57369
- seedanceModel: external_exports.string().optional().describe("Seedance variant: quality or fast (fal.ai only)"),
57655
+ seedanceModel: external_exports.string().optional().describe("Seedance variant: quality (2.0), fast, or 2.5 (fal.ai only)"),
57656
+ grokModel: external_exports.string().optional().describe("Grok video model: 1.5 (default), lite, classic"),
57657
+ klingModel: external_exports.string().optional().describe("Kling model: v3 (default), v2.6, v2.5-turbo"),
57370
57658
  generateAudio: external_exports.boolean().optional().describe("Generate native synchronized audio when supported"),
57371
57659
  output: external_exports.string().optional().describe("Output file path (downloads video)"),
57372
57660
  wait: external_exports.boolean().optional().describe("Wait for completion (default: true)")
@@ -57438,7 +57726,7 @@ var generateVideoExtendTool = defineTool({
57438
57726
  prompt: external_exports.string().optional().describe("Continuation prompt"),
57439
57727
  duration: external_exports.number().optional().describe("Duration in seconds"),
57440
57728
  negative: external_exports.string().optional().describe("Negative prompt (Kling)"),
57441
- veoModel: external_exports.string().optional().describe("Veo model: 3.0, 3.1, 3.1-fast"),
57729
+ veoModel: external_exports.string().optional().describe("Veo model: 3.1, 3.1-fast"),
57442
57730
  output: external_exports.string().optional().describe("Output file path"),
57443
57731
  wait: external_exports.boolean().optional().describe("Wait for completion (default: true)")
57444
57732
  }),
@@ -57474,14 +57762,14 @@ var generateTools = [
57474
57762
 
57475
57763
  // src/tools/manifest/storyboard.ts
57476
57764
  import { existsSync as existsSync44 } from "node:fs";
57477
- import { readFile as readFile35, writeFile as writeFile39 } from "node:fs/promises";
57765
+ import { readFile as readFile35, writeFile as writeFile38 } from "node:fs/promises";
57478
57766
  import { resolve as resolve53 } from "node:path";
57479
57767
  init_storyboard_parse();
57480
57768
  init_storyboard_edit();
57481
57769
 
57482
57770
  // src/commands/_shared/storyboard-revise.ts
57483
57771
  import { existsSync as existsSync43 } from "node:fs";
57484
- import { readFile as readFile34, writeFile as writeFile38 } from "node:fs/promises";
57772
+ import { readFile as readFile34, writeFile as writeFile37 } from "node:fs/promises";
57485
57773
  import { join as join24, resolve as resolve52 } from "node:path";
57486
57774
 
57487
57775
  // src/agent/adapters/index.ts
@@ -57650,7 +57938,7 @@ async function executeStoryboardRevision(opts) {
57650
57938
  if (loaded.layout === "bundle") {
57651
57939
  await rewriteBundleFromMarkdown(projectDir, storyboardMd);
57652
57940
  } else {
57653
- await writeFile38(storyboardPath, storyboardMd, "utf-8");
57941
+ await writeFile37(storyboardPath, storyboardMd, "utf-8");
57654
57942
  }
57655
57943
  }
57656
57944
  const changedBeats = unique5([...diffChangedBeats(currentStoryboard, storyboardMd), ...payload.changedBeats]);
@@ -57828,6 +58116,7 @@ function stripUndefined3(value) {
57828
58116
 
57829
58117
  // src/tools/manifest/storyboard.ts
57830
58118
  init_build_plan();
58119
+ init_build_video_providers();
57831
58120
  var PROJECT_DIR_DESCRIPTION3 = "Project directory. Defaults to the surface's cwd; in MCP hosts, relative paths resolve under the configured server workspace.";
57832
58121
  var projectDirSchema = external_exports.object({
57833
58122
  projectDir: external_exports.string().optional().describe(PROJECT_DIR_DESCRIPTION3)
@@ -57948,7 +58237,7 @@ var storyboardSetTool = defineTool({
57948
58237
  value,
57949
58238
  unset: args.unset
57950
58239
  });
57951
- await writeFile39(file.path, next, "utf-8");
58240
+ await writeFile38(file.path, next, "utf-8");
57952
58241
  return {
57953
58242
  success: true,
57954
58243
  data: { beat: args.beat, key: args.key },
@@ -57980,7 +58269,7 @@ var storyboardMoveTool = defineTool({
57980
58269
  beatId: args.beat,
57981
58270
  afterBeatId: args.after
57982
58271
  });
57983
- await writeFile39(file.path, next, "utf-8");
58272
+ await writeFile38(file.path, next, "utf-8");
57984
58273
  return {
57985
58274
  success: true,
57986
58275
  data: { beat: args.beat, after: args.after },
@@ -58045,7 +58334,7 @@ var planTool = defineTool({
58045
58334
  imageProvider: external_exports.string().optional().describe("Image provider override."),
58046
58335
  imageQuality: external_exports.enum(["standard", "hd"]).optional().describe("Image quality override."),
58047
58336
  imageSize: external_exports.string().optional().describe("Image size override."),
58048
- videoProvider: external_exports.enum(["seedance", "grok", "kling", "runway", "veo"]).optional().describe("Video provider override."),
58337
+ videoProvider: external_exports.enum(BUILD_VIDEO_PROVIDERS).optional().describe("Video provider override."),
58049
58338
  musicProvider: external_exports.enum(["elevenlabs", "replicate"]).optional().describe("Music provider override."),
58050
58339
  composer: external_exports.enum(["claude", "openai", "gemini"]).optional().describe("Composer provider override."),
58051
58340
  force: external_exports.boolean().optional().describe("Plan regeneration even when outputs exist.")
@@ -58133,13 +58422,13 @@ var designValidateTool = defineTool({
58133
58422
  var designTools = [designValidateTool];
58134
58423
 
58135
58424
  // src/tools/manifest/remix.ts
58136
- import { writeFile as writeFile43 } from "node:fs/promises";
58425
+ import { writeFile as writeFile42 } from "node:fs/promises";
58137
58426
  import { tmpdir as tmpdir4 } from "node:os";
58138
58427
  import { join as join26 } from "node:path";
58139
58428
 
58140
58429
  // src/commands/ai-highlights.ts
58141
- import { readFile as readFile37, writeFile as writeFile40, mkdir as mkdir23 } from "node:fs/promises";
58142
- import { resolve as resolve55, dirname as dirname20, basename as basename14, extname as extname10 } from "node:path";
58430
+ import { readFile as readFile37, writeFile as writeFile39, mkdir as mkdir24 } from "node:fs/promises";
58431
+ import { resolve as resolve55, dirname as dirname21, basename as basename14, extname as extname11 } from "node:path";
58143
58432
  import { existsSync as existsSync46 } from "node:fs";
58144
58433
  init_dist2();
58145
58434
  init_engine();
@@ -58174,7 +58463,7 @@ async function executeHighlights(options) {
58174
58463
  if (!existsSync46(absPath)) {
58175
58464
  return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: `File not found: ${absPath}` };
58176
58465
  }
58177
- const ext = extname10(absPath).toLowerCase();
58466
+ const ext = extname11(absPath).toLowerCase();
58178
58467
  const videoExtensions = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"];
58179
58468
  const isVideo = videoExtensions.includes(ext);
58180
58469
  const targetDuration = options.duration;
@@ -58319,7 +58608,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
58319
58608
  };
58320
58609
  if (options.output) {
58321
58610
  const outputPath = resolve55(process.cwd(), options.output);
58322
- await writeFile40(outputPath, JSON.stringify({
58611
+ await writeFile39(outputPath, JSON.stringify({
58323
58612
  sourceFile: absPath,
58324
58613
  totalDuration: sourceDuration,
58325
58614
  criteria: options.criteria || "all",
@@ -58354,7 +58643,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
58354
58643
  }
58355
58644
  }
58356
58645
  const projectPath = resolve55(process.cwd(), options.project);
58357
- await writeFile40(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
58646
+ await writeFile39(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
58358
58647
  extractResult.projectPath = projectPath;
58359
58648
  }
58360
58649
  return extractResult;
@@ -58501,9 +58790,9 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
58501
58790
  }))
58502
58791
  };
58503
58792
  }
58504
- const outputDir = options.outputDir ? resolve55(process.cwd(), options.outputDir) : dirname20(absPath);
58793
+ const outputDir = options.outputDir ? resolve55(process.cwd(), options.outputDir) : dirname21(absPath);
58505
58794
  if (options.outputDir && !existsSync46(outputDir)) {
58506
- await mkdir23(outputDir, { recursive: true });
58795
+ await mkdir24(outputDir, { recursive: true });
58507
58796
  }
58508
58797
  const result = {
58509
58798
  success: true,
@@ -58511,7 +58800,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
58511
58800
  };
58512
58801
  for (let i = 0; i < selectedHighlights.length; i++) {
58513
58802
  const h = selectedHighlights[i];
58514
- const baseName = basename14(absPath, extname10(absPath));
58803
+ const baseName = basename14(absPath, extname11(absPath));
58515
58804
  const outputPath = resolve55(outputDir, `${baseName}-short-${i + 1}.mp4`);
58516
58805
  const { stdout: probeOut } = await execSafe("ffprobe", [
58517
58806
  "-v",
@@ -58581,7 +58870,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
58581
58870
  // src/pipeline/executor.ts
58582
58871
  var import_yaml8 = __toESM(require_dist2(), 1);
58583
58872
  import { resolve as resolve57 } from "node:path";
58584
- import { readFile as readFile39, writeFile as writeFile42, mkdir as mkdir24 } from "node:fs/promises";
58873
+ import { readFile as readFile39, writeFile as writeFile41, mkdir as mkdir25 } from "node:fs/promises";
58585
58874
  import { existsSync as existsSync47 } from "node:fs";
58586
58875
 
58587
58876
  // src/pipeline/resolver.ts
@@ -58643,6 +58932,7 @@ function findUnresolvedRefs(params, availableStepIds) {
58643
58932
  init_output();
58644
58933
  init_config();
58645
58934
  init_provider_resolver();
58935
+ init_build_video_providers();
58646
58936
  var ACTION_METADATA = {
58647
58937
  "generate-image": { id: "generate-image", title: "Generate image", category: "generate", command: "generate image", outputs: ["image"] },
58648
58938
  "generate-video": { id: "generate-video", title: "Generate video", category: "generate", command: "generate video", outputs: ["video"], requiredKeys: ["provider-dependent"] },
@@ -58716,7 +59006,7 @@ async function resolvePipelineVideoProvider(provider) {
58716
59006
  if (explicit === "fal") return "seedance";
58717
59007
  if (explicit) return explicit;
58718
59008
  await loadProviderDefaults();
58719
- return resolveProvider("video")?.name ?? "grok";
59009
+ return resolveBuildVideoProvider(resolveProvider("video")?.name ?? "grok");
58720
59010
  }
58721
59011
  function providerKeyForImage(provider) {
58722
59012
  switch (provider) {
@@ -58742,6 +59032,7 @@ function providerKeyForVideo(provider) {
58742
59032
  case "runway":
58743
59033
  return "runway";
58744
59034
  case "veo":
59035
+ case "omni":
58745
59036
  return "google";
58746
59037
  default:
58747
59038
  return void 0;
@@ -58993,7 +59284,7 @@ var CHECKPOINT_FILE = ".pipeline-state.yaml";
58993
59284
  async function executePipeline(manifest2, options = {}) {
58994
59285
  await ensureActionsRegistered();
58995
59286
  const outputDir = resolve57(process.cwd(), options.outputDir || `${manifest2.name}-output`);
58996
- await mkdir24(outputDir, { recursive: true });
59287
+ await mkdir25(outputDir, { recursive: true });
58997
59288
  const completedSteps = /* @__PURE__ */ new Map();
58998
59289
  const results = [];
58999
59290
  const startTime = Date.now();
@@ -59118,7 +59409,7 @@ async function executePipeline(manifest2, options = {}) {
59118
59409
  data: s.data
59119
59410
  }))
59120
59411
  };
59121
- await writeFile42(
59412
+ await writeFile41(
59122
59413
  resolve57(outputDir, CHECKPOINT_FILE),
59123
59414
  (0, import_yaml8.stringify)(checkpoint, { indent: 2 }),
59124
59415
  "utf-8"
@@ -59273,7 +59564,7 @@ var pipelineRunTool = defineTool({
59273
59564
  let tempPath;
59274
59565
  if (args.pipelineYaml) {
59275
59566
  tempPath = join26(tmpdir4(), `vibe-mcp-pipeline-${Date.now()}.yaml`);
59276
- await writeFile43(tempPath, args.pipelineYaml, "utf-8");
59567
+ await writeFile42(tempPath, args.pipelineYaml, "utf-8");
59277
59568
  resolvedPath = tempPath;
59278
59569
  }
59279
59570
  try {
@@ -59408,7 +59699,7 @@ var detectTools = [
59408
59699
  ];
59409
59700
 
59410
59701
  // src/tools/manifest/timeline.ts
59411
- import { mkdir as mkdir25, writeFile as writeFile45 } from "node:fs/promises";
59702
+ import { mkdir as mkdir26, writeFile as writeFile44 } from "node:fs/promises";
59412
59703
  import { basename as basename16, resolve as resolve58 } from "node:path";
59413
59704
  init_engine();
59414
59705
  init_project_resolver();
@@ -59416,7 +59707,7 @@ init_project_resolver();
59416
59707
  // src/tools/manifest/_project-io.ts
59417
59708
  init_engine();
59418
59709
  init_project_resolver();
59419
- import { readFile as readFile40, writeFile as writeFile44 } from "node:fs/promises";
59710
+ import { readFile as readFile40, writeFile as writeFile43 } from "node:fs/promises";
59420
59711
  async function loadProject(projectPath, cwd) {
59421
59712
  const absPath = await resolveTimelineFile(projectPath, cwd);
59422
59713
  const content = await readFile40(absPath, "utf-8");
@@ -59424,7 +59715,7 @@ async function loadProject(projectPath, cwd) {
59424
59715
  return { project: Project.fromJSON(data), absPath };
59425
59716
  }
59426
59717
  async function saveProject(absPath, project) {
59427
- await writeFile44(absPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
59718
+ await writeFile43(absPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
59428
59719
  }
59429
59720
 
59430
59721
  // src/tools/manifest/timeline.ts
@@ -59464,10 +59755,10 @@ var timelineCreateTool = defineTool({
59464
59755
  outputPath = resolve58(ctx.workingDirectory, args.outputPath);
59465
59756
  } else {
59466
59757
  const dirPath = resolve58(ctx.workingDirectory, args.name);
59467
- await mkdir25(dirPath, { recursive: true });
59758
+ await mkdir26(dirPath, { recursive: true });
59468
59759
  outputPath = resolve58(dirPath, TIMELINE_FILENAME);
59469
59760
  }
59470
- await writeFile45(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
59761
+ await writeFile44(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
59471
59762
  return {
59472
59763
  success: true,
59473
59764
  data: { name: projectName, outputPath },
@@ -59764,7 +60055,7 @@ var timelineTools = [
59764
60055
 
59765
60056
  // src/tools/manifest/project.ts
59766
60057
  import { resolve as resolve60 } from "node:path";
59767
- import { writeFile as writeFile46 } from "node:fs/promises";
60058
+ import { writeFile as writeFile45 } from "node:fs/promises";
59768
60059
  init_engine();
59769
60060
 
59770
60061
  // src/commands/_shared/project-list.ts
@@ -59922,7 +60213,7 @@ var projectCreateTool = defineTool({
59922
60213
  const outputPath = resolve60(ctx.workingDirectory, args.outputPath ?? `${args.name}.vibe.json`);
59923
60214
  const project = new Project(args.name);
59924
60215
  if (args.fps) project.setFrameRate(args.fps);
59925
- await writeFile46(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
60216
+ await writeFile45(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
59926
60217
  return {
59927
60218
  success: true,
59928
60219
  data: { name: args.name, outputPath },
@@ -60642,8 +60933,8 @@ var exportVideoTool = defineTool({
60642
60933
  var exportTools = [exportVideoTool];
60643
60934
 
60644
60935
  // src/tools/manifest/agent-only.ts
60645
- import { readFile as readFile43, writeFile as writeFile47, readdir as readdir8, stat as stat8, access as access7, unlink as unlink2 } from "node:fs/promises";
60646
- import { resolve as resolve63, join as join28, basename as basename17, extname as extname11 } from "node:path";
60936
+ import { readFile as readFile43, writeFile as writeFile46, readdir as readdir8, stat as stat8, access as access7, unlink as unlink2 } from "node:fs/promises";
60937
+ import { resolve as resolve63, join as join28, basename as basename17, extname as extname12 } from "node:path";
60647
60938
  init_engine();
60648
60939
  init_exec_safe();
60649
60940
  init_project_resolver();
@@ -60665,7 +60956,7 @@ function formatSize(bytes) {
60665
60956
  return `${size.toFixed(1)}${units2[unit]}`;
60666
60957
  }
60667
60958
  function detectMediaType(filePath) {
60668
- const ext = extname11(filePath).toLowerCase();
60959
+ const ext = extname12(filePath).toLowerCase();
60669
60960
  if ([".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"].includes(ext)) return "video";
60670
60961
  if ([".mp3", ".wav", ".aac", ".flac", ".ogg", ".m4a"].includes(ext)) return "audio";
60671
60962
  if ([".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"].includes(ext)) return "image";
@@ -60692,7 +60983,7 @@ function isMediaFile(filePath) {
60692
60983
  ".webp",
60693
60984
  ".bmp"
60694
60985
  ];
60695
- return mediaExts.includes(extname11(filePath).toLowerCase());
60986
+ return mediaExts.includes(extname12(filePath).toLowerCase());
60696
60987
  }
60697
60988
  async function getMediaDuration2(filePath) {
60698
60989
  try {
@@ -60775,7 +61066,7 @@ var fsWriteTool = defineTool({
60775
61066
  async execute(args, ctx) {
60776
61067
  try {
60777
61068
  const absPath = resolve63(ctx.workingDirectory, args.path);
60778
- await writeFile47(absPath, args.content, "utf-8");
61069
+ await writeFile46(absPath, args.content, "utf-8");
60779
61070
  return { success: true, data: { bytes: args.content.length }, humanLines: [`File written: ${args.path} (${formatSize(args.content.length)})`] };
60780
61071
  } catch (error) {
60781
61072
  return { success: false, error: `Failed to write file: ${error instanceof Error ? error.message : String(error)}` };
@@ -60838,7 +61129,7 @@ var batchImportTool = defineTool({
60838
61129
  if (entry.isDirectory() && recursive) {
60839
61130
  await scanDir(entryPath);
60840
61131
  } else if (entry.isFile()) {
60841
- const ext = extname11(entry.name).toLowerCase();
61132
+ const ext = extname12(entry.name).toLowerCase();
60842
61133
  const matchesFilter = !filterExts || filterExts.includes(ext);
60843
61134
  if (matchesFilter && isMediaFile(entryPath)) mediaFiles.push(entryPath);
60844
61135
  }
@@ -60861,7 +61152,7 @@ var batchImportTool = defineTool({
60861
61152
  const source = project.addSource({ name: mediaName, type: mediaType, url: mediaFile, duration });
60862
61153
  addedSources.push({ id: source.id, name: mediaName, type: mediaType });
60863
61154
  }
60864
- await writeFile47(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
61155
+ await writeFile46(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
60865
61156
  return {
60866
61157
  success: true,
60867
61158
  data: { count: addedSources.length, sources: addedSources },
@@ -60931,7 +61222,7 @@ var batchConcatTool = defineTool({
60931
61222
  createdClips.push({ id: clip.id, sourceName: source.name, startTime: currentTime, duration: source.duration });
60932
61223
  currentTime += source.duration + gap;
60933
61224
  }
60934
- await writeFile47(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
61225
+ await writeFile46(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
60935
61226
  const totalDuration = currentTime - gap - startTime;
60936
61227
  return {
60937
61228
  success: true,
@@ -60994,7 +61285,7 @@ var batchApplyEffectTool = defineTool({
60994
61285
  });
60995
61286
  if (effect) appliedEffects.push({ clipId: clip.id, effectId: effect.id });
60996
61287
  }
60997
- await writeFile47(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
61288
+ await writeFile46(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
60998
61289
  return {
60999
61290
  success: true,
61000
61291
  data: { applied: appliedEffects.length, effectType: args.effectType },
@@ -61063,7 +61354,7 @@ var timelineClearTool = defineTool({
61063
61354
  removed.sources++;
61064
61355
  }
61065
61356
  }
61066
- await writeFile47(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
61357
+ await writeFile46(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
61067
61358
  const parts = [];
61068
61359
  if (removed.clips > 0) parts.push(`${removed.clips} clips`);
61069
61360
  if (removed.tracks > 0) parts.push(`${removed.tracks} tracks`);
@@ -61114,7 +61405,7 @@ var projectSetTool = defineTool({
61114
61405
  project.setFrameRate(args.fps);
61115
61406
  updates.push(`Frame Rate: ${args.fps} fps`);
61116
61407
  }
61117
- await writeFile47(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
61408
+ await writeFile46(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
61118
61409
  return {
61119
61410
  success: true,
61120
61411
  data: { updates },
@@ -61484,7 +61775,7 @@ var mediaConcatTool = defineTool({
61484
61775
  } else {
61485
61776
  const tempList = resolve63(ctx.workingDirectory, `concat-list-${Date.now()}.txt`);
61486
61777
  const listContent = args.inputs.map((i) => `file '${resolve63(ctx.workingDirectory, i)}'`).join("\n");
61487
- await writeFile47(tempList, listContent, "utf-8");
61778
+ await writeFile46(tempList, listContent, "utf-8");
61488
61779
  try {
61489
61780
  await execSafe(
61490
61781
  "ffmpeg",