@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.
package/dist/index.js CHANGED
@@ -6974,8 +6974,8 @@ var init_api_keys = __esm({
6974
6974
  envVar: "OPENAI_API_KEY",
6975
6975
  label: "OpenAI",
6976
6976
  showInSetup: true,
6977
- setupDescription: "gpt-image-2 image gen ($, default since v0.56), gpt-4o-mini-tts narration, Whisper transcribe, Agent",
6978
- envExampleComment: "OpenAI API Key (Whisper transcription, gpt-4o-mini-tts narration, gpt-image-2 \u2014 default text-to-image since v0.56)",
6977
+ setupDescription: "gpt-image-2.5 image gen ($), gpt-4o-mini-tts narration, Whisper transcribe, Agent",
6978
+ envExampleComment: "OpenAI API Key (Whisper transcription, gpt-4o-mini-tts narration, gpt-image-2.5 text-to-image)",
6979
6979
  envExampleUrl: "https://platform.openai.com/api-keys",
6980
6980
  keyFormat: { prefix: /^sk-/, example: "sk-..." }
6981
6981
  });
@@ -7050,8 +7050,7 @@ var init_api_keys = __esm({
7050
7050
  setupDescription: "v2.5/v3 video \u2014 std ($$) and pro ($$$) modes",
7051
7051
  envExampleComment: "Kling API Key (Kling video generation)",
7052
7052
  envExampleUrl: "https://platform.klingai.com/",
7053
- envExampleExtraLines: ["Format: ACCESS_KEY:SECRET_KEY"],
7054
- keyFormat: { prefix: /:/, example: "ACCESS_KEY:SECRET_KEY" }
7053
+ envExampleExtraLines: ["API key from the Kling console (legacy ACCESS_KEY:SECRET_KEY also works)"]
7055
7054
  });
7056
7055
  defineApiKey({
7057
7056
  configKey: "replicate",
@@ -7241,18 +7240,20 @@ var GEMINI_DEFAULT_TEXT_MODEL, GEMINI_AGENT_DEFAULT_TEXT_MODEL, GEMINI_TEXT_MODE
7241
7240
  var init_gemini_models = __esm({
7242
7241
  "../ai-providers/dist/gemini/gemini-models.js"() {
7243
7242
  "use strict";
7244
- GEMINI_DEFAULT_TEXT_MODEL = "gemini-3.5-flash";
7245
- GEMINI_AGENT_DEFAULT_TEXT_MODEL = "gemini-2.5-flash";
7243
+ GEMINI_DEFAULT_TEXT_MODEL = "gemini-3.8-flash";
7244
+ GEMINI_AGENT_DEFAULT_TEXT_MODEL = GEMINI_DEFAULT_TEXT_MODEL;
7246
7245
  GEMINI_TEXT_MODEL_ALIASES = {
7247
7246
  flash: GEMINI_DEFAULT_TEXT_MODEL,
7248
7247
  latest: GEMINI_DEFAULT_TEXT_MODEL,
7249
- "flash-3.5": GEMINI_DEFAULT_TEXT_MODEL,
7248
+ "flash-3.8": GEMINI_DEFAULT_TEXT_MODEL,
7249
+ "flash-3.5": "gemini-3.5-flash",
7250
7250
  "flash-3": "gemini-3-flash-preview",
7251
7251
  "flash-2.5": "gemini-2.5-flash",
7252
- pro: "gemini-2.5-pro",
7253
- "pro-3.1": "gemini-3.1-pro-preview"
7252
+ pro: "gemini-3.1-pro-preview",
7253
+ "pro-3.1": "gemini-3.1-pro-preview",
7254
+ "pro-2.5": "gemini-2.5-pro"
7254
7255
  };
7255
- 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";
7256
+ 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";
7256
7257
  }
7257
7258
  });
7258
7259
 
@@ -7439,7 +7440,6 @@ UNIVERSAL RULES:
7439
7440
  system_instruction: { parts: [{ text: systemPrompt }] },
7440
7441
  contents: [{ role: "user", parts: [{ text: userPrompt }] }],
7441
7442
  generationConfig: {
7442
- temperature: 0.8,
7443
7443
  maxOutputTokens: 16384
7444
7444
  }
7445
7445
  })
@@ -7540,7 +7540,7 @@ Return the complete modified component as JSON.`
7540
7540
  ]
7541
7541
  }
7542
7542
  ],
7543
- generationConfig: { temperature: 0.5, maxOutputTokens: 16384 }
7543
+ generationConfig: { maxOutputTokens: 16384 }
7544
7544
  })
7545
7545
  });
7546
7546
  if (!response.ok) {
@@ -7590,10 +7590,11 @@ var init_gemini_motion = __esm({
7590
7590
  gemini: GEMINI_DEFAULT_TEXT_MODEL,
7591
7591
  flash: GEMINI_DEFAULT_TEXT_MODEL,
7592
7592
  latest: GEMINI_DEFAULT_TEXT_MODEL,
7593
- "flash-3.5": GEMINI_DEFAULT_TEXT_MODEL,
7593
+ "flash-3.8": GEMINI_DEFAULT_TEXT_MODEL,
7594
+ "flash-3.5": "gemini-3.5-flash",
7594
7595
  "flash-3": "gemini-3-flash-preview",
7595
7596
  "flash-2.5": "gemini-2.5-flash",
7596
- pro: "gemini-2.5-pro",
7597
+ pro: "gemini-3.1-pro-preview",
7597
7598
  "2.5-pro": "gemini-2.5-pro",
7598
7599
  "3.1-pro": "gemini-3.1-pro-preview",
7599
7600
  "pro-3.1": "gemini-3.1-pro-preview"
@@ -7705,7 +7706,6 @@ var init_storyboard_prompt = __esm({
7705
7706
  async function analyzeContent(api, content, targetDuration, options) {
7706
7707
  const creativity = options?.creativity || "low";
7707
7708
  const systemPrompt = buildStoryboardSystemPrompt(targetDuration, creativity);
7708
- const temperature = creativity === "high" ? 1 : 0.7;
7709
7709
  try {
7710
7710
  const payload = {
7711
7711
  system_instruction: {
@@ -7715,7 +7715,6 @@ async function analyzeContent(api, content, targetDuration, options) {
7715
7715
  parts: [{ text: buildStoryboardUserMessage(content) }]
7716
7716
  }],
7717
7717
  generationConfig: {
7718
- temperature,
7719
7718
  maxOutputTokens: 4096
7720
7719
  }
7721
7720
  };
@@ -7786,7 +7785,15 @@ var init_http = __esm({
7786
7785
  });
7787
7786
 
7788
7787
  // ../ai-providers/dist/gemini/GeminiProvider.js
7789
- var MODEL_MAP, GeminiProvider, geminiProvider;
7788
+ function resolveVeoModel(alias) {
7789
+ if (Object.hasOwn(VEO_MODEL_ALIASES, alias)) {
7790
+ return VEO_MODEL_ALIASES[alias];
7791
+ }
7792
+ const valid = Object.keys(VEO_MODEL_ALIASES).join(", ");
7793
+ const hint = alias === "3.0" ? " Veo 3.0 was shut down by Google on 2025-11-12." : "";
7794
+ throw new Error(`Unknown Veo model "${alias}". Valid: ${valid}.${hint}`);
7795
+ }
7796
+ var VEO_MODEL_ALIASES, MODEL_MAP, GeminiProvider, geminiProvider;
7790
7797
  var init_GeminiProvider = __esm({
7791
7798
  "../ai-providers/dist/gemini/GeminiProvider.js"() {
7792
7799
  "use strict";
@@ -7794,11 +7801,19 @@ var init_GeminiProvider = __esm({
7794
7801
  init_gemini_models();
7795
7802
  init_gemini_storyboard();
7796
7803
  init_http();
7804
+ VEO_MODEL_ALIASES = {
7805
+ "3.1": "veo-3.1-generate-preview",
7806
+ "3.1-fast": "veo-3.1-fast-generate-preview"
7807
+ };
7797
7808
  MODEL_MAP = {
7798
- "flash": "gemini-2.5-flash-image",
7799
- "3.1-flash": "gemini-3.1-flash-image-preview",
7800
- "latest": "gemini-3.1-flash-image-preview",
7801
- "pro": "gemini-3-pro-image-preview",
7809
+ "flash": "gemini-3.1-flash-image",
7810
+ "3.1-flash": "gemini-3.1-flash-image",
7811
+ "latest": "gemini-3.1-flash-image",
7812
+ "lite": "gemini-3.1-flash-lite-image",
7813
+ "pro": "gemini-3-pro-image",
7814
+ "gemini-3.1-flash-image": "gemini-3.1-flash-image",
7815
+ "gemini-3.1-flash-lite-image": "gemini-3.1-flash-lite-image",
7816
+ "gemini-3-pro-image": "gemini-3-pro-image",
7802
7817
  "gemini-2.5-flash-image": "gemini-2.5-flash-image",
7803
7818
  "gemini-3.1-flash-image-preview": "gemini-3.1-flash-image-preview",
7804
7819
  "gemini-3-pro-image-preview": "gemini-3-pro-image-preview"
@@ -8107,7 +8122,7 @@ var init_GeminiProvider = __esm({
8107
8122
  contents: [{ parts: [{ text: prompt3 }] }],
8108
8123
  generationConfig
8109
8124
  };
8110
- const is31Flash = modelId === "gemini-3.1-flash-image-preview";
8125
+ const is31Flash = modelId.startsWith("gemini-3.1-flash-image");
8111
8126
  if (options.imageSearchGrounding && is31Flash) {
8112
8127
  payload.tools = [{ googleSearch: { searchTypes: { webSearch: {}, imageSearch: {} } } }];
8113
8128
  } else if (options.grounding && isPro) {
@@ -8335,7 +8350,6 @@ var init_GeminiProvider = __esm({
8335
8350
  videoPart.video_metadata = videoMetadata;
8336
8351
  }
8337
8352
  const generationConfig = {
8338
- temperature: 0.4,
8339
8353
  maxOutputTokens: 8192
8340
8354
  };
8341
8355
  if (options.lowResolution) {
@@ -8417,7 +8431,6 @@ var init_GeminiProvider = __esm({
8417
8431
  }
8418
8432
  }));
8419
8433
  const generationConfig = {
8420
- temperature: 0.4,
8421
8434
  maxOutputTokens: 8192
8422
8435
  };
8423
8436
  if (options.lowResolution) {
@@ -8517,7 +8530,6 @@ Respond with ONLY the JSON array, no other text.`;
8517
8530
  body: JSON.stringify({
8518
8531
  contents: [{ parts: [{ text: prompt3 }] }],
8519
8532
  generationConfig: {
8520
- temperature: 0.3,
8521
8533
  maxOutputTokens: 1024
8522
8534
  }
8523
8535
  })
@@ -8611,7 +8623,7 @@ Respond with ONLY the JSON array, no other text.`;
8611
8623
  return refineMotion({ apiKey: this.apiKey, baseUrl: this.baseUrl }, existingCode, instructions, options);
8612
8624
  }
8613
8625
  /**
8614
- * Generate a storyboard from script content using Gemini 2.5 Flash.
8626
+ * Generate a storyboard from script content using the default Gemini Flash model.
8615
8627
  * Alternative to ClaudeProvider.analyzeContent for when Claude is unavailable.
8616
8628
  */
8617
8629
  async analyzeContent(content, targetDuration, options) {
@@ -8626,43 +8638,47 @@ Respond with ONLY the JSON array, no other text.`;
8626
8638
  });
8627
8639
 
8628
8640
  // ../ai-providers/dist/gemini/gemini-omni.js
8629
- function firstReferenceImage(options) {
8630
- const refs = options.referenceImages;
8631
- if (refs && refs.length > 0 && refs[0].base64)
8632
- return refs[0];
8633
- const ref = options.referenceImage;
8634
- if (typeof ref === "string" && ref) {
8641
+ function frameImages(options) {
8642
+ const frames = [];
8643
+ const toInline = (ref) => {
8635
8644
  const m = ref.match(/^data:(.+?);base64,(.*)$/);
8636
- if (m)
8637
- return { mimeType: m[1], base64: m[2] };
8638
- return { mimeType: "image/png", base64: ref };
8645
+ return m ? { mimeType: m[1], base64: m[2] } : { mimeType: "image/png", base64: ref };
8646
+ };
8647
+ if (options.referenceImages?.[0]?.base64)
8648
+ frames.push(options.referenceImages[0]);
8649
+ else if (typeof options.referenceImage === "string" && options.referenceImage) {
8650
+ frames.push(toInline(options.referenceImage));
8651
+ }
8652
+ if (frames.length > 0 && options.lastFrame)
8653
+ frames.push(toInline(options.lastFrame));
8654
+ return frames;
8655
+ }
8656
+ function findOmniVideo(response) {
8657
+ for (const step of response.steps ?? []) {
8658
+ if (step.type !== "model_output")
8659
+ continue;
8660
+ const video = step.content?.find((c) => c.type === "video");
8661
+ if (video)
8662
+ return video;
8639
8663
  }
8640
- return void 0;
8664
+ return response.output_video;
8641
8665
  }
8642
- function findVideoUrl(obj) {
8643
- if (!obj || typeof obj !== "object")
8644
- return void 0;
8645
- for (const [k, v] of Object.entries(obj)) {
8646
- if (typeof v === "string" && /^https?:\/\//.test(v) && /(uri|url|video)/i.test(k))
8647
- return v;
8648
- if (typeof v === "object") {
8649
- const nested = findVideoUrl(v);
8650
- if (nested)
8651
- return nested;
8652
- }
8653
- }
8654
- return void 0;
8666
+ function fileIdFromUri(uri) {
8667
+ return uri.match(/files\/([^/?:]+)/)?.[1];
8655
8668
  }
8656
- var OMNI_MODEL, INTERACTIONS_URL, OmniProvider, omniProvider;
8669
+ var OMNI_MODEL, API_ROOT, FILE_POLL_INTERVAL_MS, FILE_POLL_TIMEOUT_MS, OmniProvider, omniProvider;
8657
8670
  var init_gemini_omni = __esm({
8658
8671
  "../ai-providers/dist/gemini/gemini-omni.js"() {
8659
8672
  "use strict";
8660
- OMNI_MODEL = "gemini-omni-flash-preview";
8661
- INTERACTIONS_URL = "https://generativelanguage.googleapis.com/v1beta/interactions";
8673
+ init_http();
8674
+ OMNI_MODEL = "gemini-omni-1.1-flash";
8675
+ API_ROOT = "https://generativelanguage.googleapis.com";
8676
+ FILE_POLL_INTERVAL_MS = 3e3;
8677
+ FILE_POLL_TIMEOUT_MS = 5 * 60 * 1e3;
8662
8678
  OmniProvider = class {
8663
8679
  constructor() {
8664
8680
  this.id = "omni";
8665
- this.label = "Gemini Omni (experimental)";
8681
+ this.label = "Gemini Omni 1.1 Flash";
8666
8682
  }
8667
8683
  async initialize(config2) {
8668
8684
  this.apiKey = config2.apiKey;
@@ -8675,22 +8691,23 @@ var init_gemini_omni = __esm({
8675
8691
  return { id: "", status: "failed", error: "GOOGLE_API_KEY not configured for Gemini Omni" };
8676
8692
  }
8677
8693
  const opts = options ?? { prompt: prompt3 };
8678
- const image = firstReferenceImage(opts);
8679
- const task = image ? "image_to_video" : "text_to_video";
8680
- const aspectRatio2 = opts.aspectRatio === "9:16" ? "9:16" : "16:9";
8681
- const inputs = [{ text: prompt3 }];
8682
- if (image) {
8683
- inputs.push({ inline_data: { mime_type: image.mimeType, data: image.base64 } });
8684
- }
8694
+ const frames = frameImages(opts);
8695
+ const input = frames.length === 0 ? prompt3 : [
8696
+ ...frames.map((f) => ({ type: "image", data: f.base64, mime_type: f.mimeType })),
8697
+ { type: "text", text: prompt3 }
8698
+ ];
8685
8699
  const body = {
8686
8700
  model: OMNI_MODEL,
8687
- inputs,
8688
- generation_config: {
8689
- video_config: { task, aspect_ratio: aspectRatio2 }
8701
+ input,
8702
+ response_format: {
8703
+ type: "video",
8704
+ aspect_ratio: opts.aspectRatio === "9:16" ? "9:16" : "16:9",
8705
+ resolution: opts.resolution ?? "720p",
8706
+ delivery: "uri"
8690
8707
  }
8691
8708
  };
8692
8709
  try {
8693
- const res = await fetch(INTERACTIONS_URL, {
8710
+ const res = await fetch(`${API_ROOT}/v1beta/interactions`, {
8694
8711
  method: "POST",
8695
8712
  headers: { "Content-Type": "application/json", "x-goog-api-key": this.apiKey },
8696
8713
  body: JSON.stringify(body)
@@ -8700,33 +8717,52 @@ var init_gemini_omni = __esm({
8700
8717
  return {
8701
8718
  id: "",
8702
8719
  status: "failed",
8703
- error: `Gemini Omni (experimental) request failed: HTTP ${res.status} \u2014 ${text.slice(0, 300)}`
8720
+ error: `Gemini Omni request failed: HTTP ${res.status} - ${text.slice(0, 300)}`
8704
8721
  };
8705
8722
  }
8706
- let parsed;
8707
- try {
8708
- parsed = JSON.parse(text);
8709
- } catch {
8710
- return { id: "", status: "failed", error: "Gemini Omni returned a non-JSON response" };
8723
+ const parsed = JSON.parse(text);
8724
+ const id = parsed.id ?? "";
8725
+ const video = findOmniVideo(parsed);
8726
+ if (!video?.uri) {
8727
+ const reason = parsed.error?.message ?? `status ${parsed.status ?? "unknown"}`;
8728
+ return { id, status: "failed", error: `Gemini Omni returned no video (${reason})` };
8711
8729
  }
8712
- const videoUrl = findVideoUrl(parsed);
8713
- const id = parsed?.interaction_id ?? parsed?.name ?? "";
8714
- if (!videoUrl) {
8715
- return {
8716
- id,
8717
- status: "failed",
8718
- error: "Gemini Omni response contained no video URL (the preview interactions schema may have changed)."
8719
- };
8730
+ const fileId = fileIdFromUri(video.uri);
8731
+ if (!fileId) {
8732
+ return { id, status: "failed", error: `Gemini Omni returned an unexpected video URI: ${video.uri}` };
8720
8733
  }
8721
- return { id, status: "completed", videoUrl };
8734
+ const ready = await this.waitForFile(fileId);
8735
+ if (ready !== "ACTIVE") {
8736
+ return { id, status: "failed", error: `Gemini Omni video file ended in state ${ready}` };
8737
+ }
8738
+ return {
8739
+ id,
8740
+ status: "completed",
8741
+ videoUrl: `${API_ROOT}/download/v1beta/files/${fileId}:download?alt=media`
8742
+ };
8722
8743
  } catch (err) {
8723
8744
  return {
8724
8745
  id: "",
8725
8746
  status: "failed",
8726
- error: `Gemini Omni (experimental) error: ${err instanceof Error ? err.message : String(err)}`
8747
+ error: `Gemini Omni error: ${err instanceof Error ? err.message : String(err)}`
8727
8748
  };
8728
8749
  }
8729
8750
  }
8751
+ /** Poll the Files API until the generated video is downloadable. */
8752
+ async waitForFile(fileId) {
8753
+ const deadline = Date.now() + FILE_POLL_TIMEOUT_MS;
8754
+ for (; ; ) {
8755
+ const res = await fetch(`${API_ROOT}/v1beta/files/${fileId}`, {
8756
+ headers: { "x-goog-api-key": this.apiKey }
8757
+ });
8758
+ const state = res.ok ? (await res.json()).state : `HTTP ${res.status}`;
8759
+ if (state !== "PROCESSING")
8760
+ return state ?? "UNKNOWN";
8761
+ if (Date.now() > deadline)
8762
+ return "TIMEOUT";
8763
+ await sleep(FILE_POLL_INTERVAL_MS);
8764
+ }
8765
+ }
8730
8766
  };
8731
8767
  omniProvider = new OmniProvider();
8732
8768
  }
@@ -8760,14 +8796,16 @@ var init_gemini = __esm({
8760
8796
  label: "Veo",
8761
8797
  apiKey: "google",
8762
8798
  kinds: ["video"],
8763
- resolverPriority: { video: 3 },
8799
+ // No resolverPriority: the Veo 3.1 previews shut down on 2026-10-22, so
8800
+ // Veo is explicit-only (`-p veo`) and Omni is the Google default.
8764
8801
  commandsUnlocked: ["generate video -p veo"]
8765
8802
  });
8766
8803
  defineProvider({
8767
8804
  id: "omni",
8768
- label: "Gemini Omni (experimental)",
8805
+ label: "Gemini Omni",
8769
8806
  apiKey: "google",
8770
8807
  kinds: ["video"],
8808
+ resolverPriority: { video: 3 },
8771
8809
  commandsUnlocked: ["generate video -p omni"]
8772
8810
  });
8773
8811
  }
@@ -8776,8 +8814,7 @@ var init_gemini = __esm({
8776
8814
  // ../ai-providers/dist/openai/openai-storyboard.js
8777
8815
  async function analyzeContent2(apiKey, content, targetDuration, options) {
8778
8816
  const creativity = options?.creativity || "low";
8779
- const systemPrompt = buildStoryboardSystemPrompt(targetDuration, creativity);
8780
- const temperature = creativity === "high" ? 1 : 0.7;
8817
+ const systemPrompt = buildStoryboardSystemPrompt(targetDuration, creativity) + '\n\nWrap the array in a JSON object: {"segments": [ ...segment objects... ]}.';
8781
8818
  try {
8782
8819
  const response = await fetch("https://api.openai.com/v1/chat/completions", {
8783
8820
  method: "POST",
@@ -8786,13 +8823,13 @@ async function analyzeContent2(apiKey, content, targetDuration, options) {
8786
8823
  Authorization: `Bearer ${apiKey}`
8787
8824
  },
8788
8825
  body: JSON.stringify({
8789
- model: "gpt-5-mini",
8826
+ model: "gpt-5.4-mini",
8790
8827
  messages: [
8791
8828
  { role: "system", content: systemPrompt },
8792
8829
  { role: "user", content: buildStoryboardUserMessage(content) }
8793
8830
  ],
8794
- temperature,
8795
- max_tokens: 4096,
8831
+ // Reasoning tokens count against this cap.
8832
+ max_completion_tokens: 16384,
8796
8833
  response_format: { type: "json_object" }
8797
8834
  })
8798
8835
  });
@@ -8849,7 +8886,7 @@ var init_OpenAIProvider = __esm({
8849
8886
  this.iconUrl = "/icons/openai.svg";
8850
8887
  this.isAvailable = true;
8851
8888
  this.baseUrl = "https://api.openai.com/v1";
8852
- this.model = "gpt-5-mini";
8889
+ this.model = "gpt-5.4-mini";
8853
8890
  }
8854
8891
  async initialize(config2) {
8855
8892
  this.apiKey = config2.apiKey;
@@ -8945,7 +8982,6 @@ Or if clarification needed:
8945
8982
  { role: "system", content: systemPrompt },
8946
8983
  { role: "user", content: instruction }
8947
8984
  ],
8948
- temperature: 0.2,
8949
8985
  response_format: { type: "json_object" }
8950
8986
  })
8951
8987
  });
@@ -9156,8 +9192,9 @@ Each segment should be 3-10 seconds long.`;
9156
9192
  Authorization: `Bearer ${this.apiKey}`
9157
9193
  },
9158
9194
  body: JSON.stringify({
9159
- model: "gpt-5-mini",
9160
- max_tokens: 4096,
9195
+ model: "gpt-5.4-mini",
9196
+ // Reasoning tokens count against this cap.
9197
+ max_completion_tokens: 16384,
9161
9198
  response_format: { type: "json_object" },
9162
9199
  messages: [
9163
9200
  { role: "system", content: systemPrompt },
@@ -9190,7 +9227,7 @@ ${videoAnalysis}` }
9190
9227
  }
9191
9228
  }
9192
9229
  /**
9193
- * Generate a storyboard from script content using GPT-5-mini.
9230
+ * Generate a storyboard from script content using GPT-5.4 mini.
9194
9231
  * Alternative to ClaudeProvider.analyzeContent for when Claude is unavailable.
9195
9232
  */
9196
9233
  async analyzeContent(content, targetDuration, options) {
@@ -9238,10 +9275,12 @@ async function callClaude(params, opts) {
9238
9275
  },
9239
9276
  body: JSON.stringify({
9240
9277
  model: params.model,
9241
- max_tokens: opts.maxTokens,
9242
- ...opts.temperature !== void 0 ? { temperature: opts.temperature } : {},
9278
+ // Claude 5.x thinks by default and thinking tokens count against
9279
+ // max_tokens, so a small cap can end before any text is written.
9280
+ max_tokens: Math.max(opts.maxTokens, CLAUDE_MIN_MAX_TOKENS),
9243
9281
  messages: opts.messages,
9244
- system: opts.system
9282
+ system: opts.system,
9283
+ ...opts.jsonSchema ? { output_config: { format: { type: "json_schema", schema: opts.jsonSchema } } } : {}
9245
9284
  })
9246
9285
  });
9247
9286
  if (!response.ok) {
@@ -9263,9 +9302,11 @@ function extractJsonArray(text) {
9263
9302
  const match = text.match(/\[[\s\S]*\]/);
9264
9303
  return match ? match[0] : null;
9265
9304
  }
9305
+ var CLAUDE_MIN_MAX_TOKENS;
9266
9306
  var init_claude_api = __esm({
9267
9307
  "../ai-providers/dist/claude/claude-api.js"() {
9268
9308
  "use strict";
9309
+ CLAUDE_MIN_MAX_TOKENS = 16e3;
9269
9310
  }
9270
9311
  });
9271
9312
 
@@ -9301,7 +9342,8 @@ MANDATORY ADAPTATION RULES:
9301
9342
  const text = await callClaude(api, {
9302
9343
  system: systemPrompt,
9303
9344
  messages: [{ role: "user", content: userContent }],
9304
- maxTokens: 16e3
9345
+ maxTokens: 16e3,
9346
+ jsonSchema: MOTION_COMPONENT_SCHEMA
9305
9347
  });
9306
9348
  const jsonStr = extractJsonObject(text);
9307
9349
  if (!jsonStr) {
@@ -9376,7 +9418,8 @@ Modification instructions: ${instructions}
9376
9418
 
9377
9419
  Return the complete modified component as JSON.`
9378
9420
  }],
9379
- maxTokens: 16e3
9421
+ maxTokens: 16e3,
9422
+ jsonSchema: MOTION_COMPONENT_SCHEMA
9380
9423
  });
9381
9424
  const jsonStr = extractJsonObject(text);
9382
9425
  if (!jsonStr)
@@ -9405,15 +9448,13 @@ async function analyzeContent3(api, content, targetDuration, options) {
9405
9448
  const creativity = options?.creativity || "low";
9406
9449
  const systemPrompt = buildStoryboardSystemPrompt(targetDuration, creativity);
9407
9450
  try {
9408
- const temperature = creativity === "high" ? 1 : 0.7;
9409
9451
  const text = await callClaude(api, {
9410
9452
  system: systemPrompt,
9411
9453
  messages: [{
9412
9454
  role: "user",
9413
9455
  content: buildStoryboardUserMessage(content)
9414
9456
  }],
9415
- maxTokens: 4096,
9416
- temperature
9457
+ maxTokens: 4096
9417
9458
  });
9418
9459
  const jsonStr = extractJsonArray(text);
9419
9460
  if (!jsonStr) {
@@ -9770,11 +9811,22 @@ UNIVERSAL RULES:
9770
9811
  - Both entrance AND exit animations
9771
9812
  - whiteSpace: "nowrap" on all text`;
9772
9813
  }
9814
+ var MOTION_COMPONENT_SCHEMA;
9773
9815
  var init_claude_motion = __esm({
9774
9816
  "../ai-providers/dist/claude/claude-motion.js"() {
9775
9817
  "use strict";
9776
9818
  init_claude_api();
9777
9819
  init_storyboard_prompt();
9820
+ MOTION_COMPONENT_SCHEMA = {
9821
+ type: "object",
9822
+ properties: {
9823
+ name: { type: "string" },
9824
+ code: { type: "string" },
9825
+ description: { type: "string" }
9826
+ },
9827
+ required: ["name", "code", "description"],
9828
+ additionalProperties: false
9829
+ };
9778
9830
  }
9779
9831
  });
9780
9832
 
@@ -10536,6 +10588,7 @@ var ClaudeProvider, claudeProvider;
10536
10588
  var init_ClaudeProvider = __esm({
10537
10589
  "../ai-providers/dist/claude/ClaudeProvider.js"() {
10538
10590
  "use strict";
10591
+ init_claude_api();
10539
10592
  init_claude_motion();
10540
10593
  init_claude_analysis();
10541
10594
  init_claude_viral();
@@ -10549,7 +10602,7 @@ var init_ClaudeProvider = __esm({
10549
10602
  this.iconUrl = "/icons/claude.svg";
10550
10603
  this.isAvailable = true;
10551
10604
  this.baseUrl = "https://api.anthropic.com/v1";
10552
- this.model = "claude-sonnet-4-6";
10605
+ this.model = "claude-sonnet-5-5";
10553
10606
  }
10554
10607
  async initialize(config2) {
10555
10608
  this.apiKey = config2.apiKey;
@@ -10653,7 +10706,7 @@ Or if clarification needed:
10653
10706
  },
10654
10707
  body: JSON.stringify({
10655
10708
  model: this.model,
10656
- max_tokens: 2048,
10709
+ max_tokens: CLAUDE_MIN_MAX_TOKENS,
10657
10710
  messages: [{ role: "user", content: instruction }],
10658
10711
  system: systemPrompt
10659
10712
  })
@@ -10723,7 +10776,7 @@ Respond with JSON array:
10723
10776
  },
10724
10777
  body: JSON.stringify({
10725
10778
  model: this.model,
10726
- max_tokens: 2048,
10779
+ max_tokens: CLAUDE_MIN_MAX_TOKENS,
10727
10780
  messages: [{
10728
10781
  role: "user",
10729
10782
  content: `Clips: ${JSON.stringify(clipsInfo)}
@@ -10852,8 +10905,8 @@ Instruction: ${instruction}`
10852
10905
  }
10853
10906
  };
10854
10907
  ClaudeProvider.MOTION_MODELS = {
10855
- sonnet: "claude-sonnet-4-6",
10856
- opus: "claude-opus-4-7",
10908
+ sonnet: "claude-sonnet-5-5",
10909
+ opus: "claude-opus-5-5",
10857
10910
  "opus-4-6": "claude-opus-4-6"
10858
10911
  };
10859
10912
  claudeProvider = new ClaudeProvider();
@@ -11191,7 +11244,7 @@ function resolveVoiceId(input) {
11191
11244
  const availableVoices = Object.keys(KNOWN_VOICES).join(", ");
11192
11245
  throw new Error(`Unknown voice "${input}". Available voices: ${availableVoices}. You can also use a custom voice ID from your ElevenLabs account.`);
11193
11246
  }
11194
- var KNOWN_VOICES, TTS_429_MAX_RETRIES, TTS_429_RETRY_DELAY_MS, ElevenLabsProvider, elevenLabsProvider;
11247
+ var KNOWN_VOICES, TTS_429_MAX_RETRIES, TTS_429_RETRY_DELAY_MS, ELEVENLABS_MUSIC_MODEL, ElevenLabsProvider, elevenLabsProvider;
11195
11248
  var init_ElevenLabsProvider = __esm({
11196
11249
  "../ai-providers/dist/elevenlabs/ElevenLabsProvider.js"() {
11197
11250
  "use strict";
@@ -11228,6 +11281,7 @@ var init_ElevenLabsProvider = __esm({
11228
11281
  };
11229
11282
  TTS_429_MAX_RETRIES = 1;
11230
11283
  TTS_429_RETRY_DELAY_MS = 2e3;
11284
+ ELEVENLABS_MUSIC_MODEL = "music_v2_5";
11231
11285
  ElevenLabsProvider = class {
11232
11286
  constructor() {
11233
11287
  this.id = "elevenlabs";
@@ -11308,9 +11362,10 @@ var init_ElevenLabsProvider = __esm({
11308
11362
  stability: options.stability ?? 0.5,
11309
11363
  similarity_boost: options.similarityBoost ?? 0.75,
11310
11364
  style: options.style ?? 0,
11311
- use_speaker_boost: true
11312
- },
11313
- ...options.speed !== void 0 && { speed: options.speed }
11365
+ use_speaker_boost: true,
11366
+ // The API reads speed only inside voice_settings.
11367
+ ...options.speed !== void 0 && { speed: options.speed }
11368
+ }
11314
11369
  })
11315
11370
  });
11316
11371
  if (response.status === 429 && attempt < TTS_429_MAX_RETRIES) {
@@ -11380,7 +11435,7 @@ var init_ElevenLabsProvider = __esm({
11380
11435
  prompt_influence: options.promptInfluence ?? 0.3
11381
11436
  };
11382
11437
  if (options.duration !== void 0) {
11383
- const duration = Math.max(0.5, Math.min(22, options.duration));
11438
+ const duration = Math.max(0.5, Math.min(30, options.duration));
11384
11439
  body.duration_seconds = duration;
11385
11440
  }
11386
11441
  const response = await fetch(`${this.baseUrl}/sound-generation`, {
@@ -11425,7 +11480,7 @@ var init_ElevenLabsProvider = __esm({
11425
11480
  try {
11426
11481
  const body = {
11427
11482
  prompt: prompt3,
11428
- model_id: "music_v1"
11483
+ model_id: ELEVENLABS_MUSIC_MODEL
11429
11484
  };
11430
11485
  if (options.duration !== void 0) {
11431
11486
  const duration = Math.max(3, Math.min(600, options.duration));
@@ -11636,6 +11691,55 @@ var init_elevenlabs = __esm({
11636
11691
  });
11637
11692
 
11638
11693
  // ../ai-providers/dist/kokoro/KokoroProvider.js
11694
+ function splitForKokoro(text, maxChars = KOKORO_MAX_CHUNK_CHARS) {
11695
+ const pieces = (part, pattern) => part.length <= maxChars ? [part] : part.split(pattern).filter((p) => p.trim().length > 0);
11696
+ const units2 = text.trim().split(/(?<=[.!?…])\s+/).flatMap((sentence) => pieces(sentence, /(?<=[,;:])\s+/)).flatMap((clause) => {
11697
+ if (clause.length <= maxChars)
11698
+ return [clause];
11699
+ const words = [];
11700
+ let line = "";
11701
+ for (const word of clause.split(/\s+/)) {
11702
+ if (line && line.length + word.length + 1 > maxChars) {
11703
+ words.push(line);
11704
+ line = word;
11705
+ } else {
11706
+ line = line ? `${line} ${word}` : word;
11707
+ }
11708
+ }
11709
+ if (line)
11710
+ words.push(line);
11711
+ return words;
11712
+ });
11713
+ const chunks = [];
11714
+ for (const unit of units2) {
11715
+ const last = chunks.at(-1);
11716
+ if (last !== void 0 && last.length + unit.length + 1 <= maxChars) {
11717
+ chunks[chunks.length - 1] = `${last} ${unit}`;
11718
+ } else {
11719
+ chunks.push(unit);
11720
+ }
11721
+ }
11722
+ return chunks;
11723
+ }
11724
+ function encodeFloatWav(samples, sampleRate) {
11725
+ const dataBytes = samples.length * 4;
11726
+ const buffer = Buffer.alloc(44 + dataBytes);
11727
+ buffer.write("RIFF", 0, "ascii");
11728
+ buffer.writeUInt32LE(36 + dataBytes, 4);
11729
+ buffer.write("WAVE", 8, "ascii");
11730
+ buffer.write("fmt ", 12, "ascii");
11731
+ buffer.writeUInt32LE(16, 16);
11732
+ buffer.writeUInt16LE(3, 20);
11733
+ buffer.writeUInt16LE(1, 22);
11734
+ buffer.writeUInt32LE(sampleRate, 24);
11735
+ buffer.writeUInt32LE(sampleRate * 4, 28);
11736
+ buffer.writeUInt16LE(4, 32);
11737
+ buffer.writeUInt16LE(32, 34);
11738
+ buffer.write("data", 36, "ascii");
11739
+ buffer.writeUInt32LE(dataBytes, 40);
11740
+ Buffer.from(samples.buffer, samples.byteOffset, dataBytes).copy(buffer, 44);
11741
+ return buffer;
11742
+ }
11639
11743
  async function loadKokoroRuntime() {
11640
11744
  if (factoryOverride)
11641
11745
  return { factory: factoryOverride, device: "cpu" };
@@ -11673,7 +11777,7 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
11673
11777
  if (!base)
11674
11778
  return null;
11675
11779
  try {
11676
- const { readFile: readFile55, writeFile: writeFile57, mkdir: mkdir31 } = await import("node:fs/promises");
11780
+ const { readFile: readFile55, writeFile: writeFile56, mkdir: mkdir32 } = await import("node:fs/promises");
11677
11781
  const { join: join34, resolve: resolve87 } = await import("node:path");
11678
11782
  const { pathToFileURL } = await import("node:url");
11679
11783
  const { homedir: homedir8 } = await import("node:os");
@@ -11694,8 +11798,8 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
11694
11798
  async put(key, response) {
11695
11799
  try {
11696
11800
  const buf = Buffer.from(await response.arrayBuffer());
11697
- await mkdir31(cacheRoot, { recursive: true });
11698
- await writeFile57(join34(cacheRoot, encodeURIComponent(String(key))), buf);
11801
+ await mkdir32(cacheRoot, { recursive: true });
11802
+ await writeFile56(join34(cacheRoot, encodeURIComponent(String(key))), buf);
11699
11803
  } catch {
11700
11804
  }
11701
11805
  }
@@ -11744,12 +11848,13 @@ function normaliseEvent(raw) {
11744
11848
  total: typeof r.total === "number" ? r.total : void 0
11745
11849
  };
11746
11850
  }
11747
- var KOKORO_DEFAULT_VOICE, KOKORO_MODEL_ID, modelPromise, factoryOverride, KokoroProvider, kokoroProvider;
11851
+ var KOKORO_DEFAULT_VOICE, KOKORO_MODEL_ID, KOKORO_MAX_CHUNK_CHARS, modelPromise, factoryOverride, KokoroProvider, kokoroProvider;
11748
11852
  var init_KokoroProvider = __esm({
11749
11853
  "../ai-providers/dist/kokoro/KokoroProvider.js"() {
11750
11854
  "use strict";
11751
11855
  KOKORO_DEFAULT_VOICE = "af_heart";
11752
11856
  KOKORO_MODEL_ID = "onnx-community/Kokoro-82M-v1.0-ONNX";
11857
+ KOKORO_MAX_CHUNK_CHARS = 300;
11753
11858
  modelPromise = null;
11754
11859
  factoryOverride = null;
11755
11860
  KokoroProvider = class {
@@ -11776,11 +11881,32 @@ var init_KokoroProvider = __esm({
11776
11881
  }
11777
11882
  try {
11778
11883
  const model = await loadModel(options.onProgress);
11779
- const audio = await model.generate(text, {
11884
+ const voiceOptions = {
11780
11885
  voice: options.voice ?? KOKORO_DEFAULT_VOICE,
11781
11886
  speed: options.speed ?? 1
11782
- });
11783
- const buffer = Buffer.from(audio.toWav());
11887
+ };
11888
+ const chunks = splitForKokoro(text);
11889
+ let buffer;
11890
+ if (chunks.length === 1) {
11891
+ buffer = Buffer.from((await model.generate(chunks[0], voiceOptions)).toWav());
11892
+ } else {
11893
+ const parts = [];
11894
+ let sampleRate = 24e3;
11895
+ for (const chunk of chunks) {
11896
+ const audio = await model.generate(chunk, voiceOptions);
11897
+ if (!audio.audio)
11898
+ throw new Error("Kokoro returned no raw samples to join");
11899
+ parts.push(audio.audio);
11900
+ sampleRate = audio.sampling_rate ?? sampleRate;
11901
+ }
11902
+ const samples = new Float32Array(parts.reduce((n, part) => n + part.length, 0));
11903
+ let offset = 0;
11904
+ for (const part of parts) {
11905
+ samples.set(part, offset);
11906
+ offset += part.length;
11907
+ }
11908
+ buffer = encodeFloatWav(samples, sampleRate);
11909
+ }
11784
11910
  return {
11785
11911
  success: true,
11786
11912
  audioBuffer: buffer,
@@ -11815,11 +11941,12 @@ var init_kokoro = __esm({
11815
11941
  });
11816
11942
 
11817
11943
  // ../ai-providers/dist/openai-image/OpenAIImageProvider.js
11818
- var DEFAULT_MODEL, OpenAIImageProvider, openaiImageProvider;
11944
+ var OPENAI_IMAGE_DEFAULT_MODEL, DEFAULT_MODEL, OpenAIImageProvider, openaiImageProvider;
11819
11945
  var init_OpenAIImageProvider = __esm({
11820
11946
  "../ai-providers/dist/openai-image/OpenAIImageProvider.js"() {
11821
11947
  "use strict";
11822
- DEFAULT_MODEL = "gpt-image-2";
11948
+ OPENAI_IMAGE_DEFAULT_MODEL = "gpt-image-2.5-sunburst";
11949
+ DEFAULT_MODEL = OPENAI_IMAGE_DEFAULT_MODEL;
11823
11950
  OpenAIImageProvider = class {
11824
11951
  constructor() {
11825
11952
  this.id = "openai-image";
@@ -11841,7 +11968,7 @@ var init_OpenAIImageProvider = __esm({
11841
11968
  }
11842
11969
  /**
11843
11970
  * Generate images from text prompt
11844
- * Uses GPT Image 1.5 by default (fastest, best quality)
11971
+ * Uses GPT Image 2.5 Sunburst by default
11845
11972
  */
11846
11973
  async generateImage(prompt3, options = {}) {
11847
11974
  if (!this.apiKey) {
@@ -11851,28 +11978,20 @@ var init_OpenAIImageProvider = __esm({
11851
11978
  };
11852
11979
  }
11853
11980
  const model = options.model || DEFAULT_MODEL;
11854
- const isGPTImage = model === "gpt-image-1.5" || model === "gpt-image-2";
11855
11981
  try {
11856
11982
  const body = {
11857
11983
  model,
11858
11984
  prompt: prompt3,
11859
11985
  n: options.n || 1
11860
11986
  };
11861
- if (isGPTImage) {
11862
- const qualityMap = {
11863
- standard: "medium",
11864
- hd: "high"
11865
- };
11866
- const quality = options.quality || "medium";
11867
- body.quality = qualityMap[quality] || quality;
11868
- if (options.size && options.size !== "auto") {
11869
- body.size = options.size;
11870
- }
11871
- } else {
11872
- body.response_format = "url";
11873
- body.size = options.size || "1024x1024";
11874
- body.quality = options.quality === "high" ? "hd" : options.quality || "standard";
11875
- body.style = options.style || "vivid";
11987
+ const qualityMap = {
11988
+ standard: "medium",
11989
+ hd: "high"
11990
+ };
11991
+ const quality = options.quality || "medium";
11992
+ body.quality = qualityMap[quality] || quality;
11993
+ if (options.size && options.size !== "auto") {
11994
+ body.size = options.size;
11876
11995
  }
11877
11996
  const response = await fetch(`${this.baseUrl}/images/generations`, {
11878
11997
  method: "POST",
@@ -12025,49 +12144,6 @@ var init_OpenAIImageProvider = __esm({
12025
12144
  };
12026
12145
  }
12027
12146
  }
12028
- /**
12029
- * Create image variations (uses DALL-E 2)
12030
- */
12031
- async createVariation(imageBuffer, options = {}) {
12032
- if (!this.apiKey) {
12033
- return {
12034
- success: false,
12035
- error: "OpenAI API key not configured"
12036
- };
12037
- }
12038
- try {
12039
- const formData = new FormData();
12040
- const uint8Array = new Uint8Array(imageBuffer);
12041
- formData.append("image", new Blob([uint8Array], { type: "image/png" }), "image.png");
12042
- formData.append("model", "dall-e-2");
12043
- formData.append("n", String(options.n || 1));
12044
- formData.append("size", options.size || "1024x1024");
12045
- const response = await fetch(`${this.baseUrl}/images/variations`, {
12046
- method: "POST",
12047
- headers: {
12048
- Authorization: `Bearer ${this.apiKey}`
12049
- },
12050
- body: formData
12051
- });
12052
- if (!response.ok) {
12053
- const error = await response.text();
12054
- return {
12055
- success: false,
12056
- error: `API error: ${response.status} - ${error}`
12057
- };
12058
- }
12059
- const data = await response.json();
12060
- return {
12061
- success: true,
12062
- images: data.data.map((img) => ({ url: img.url }))
12063
- };
12064
- } catch (error) {
12065
- return {
12066
- success: false,
12067
- error: error instanceof Error ? error.message : "Unknown error"
12068
- };
12069
- }
12070
- }
12071
12147
  };
12072
12148
  openaiImageProvider = new OpenAIImageProvider();
12073
12149
  }
@@ -12498,12 +12574,33 @@ var init_runway = __esm({
12498
12574
 
12499
12575
  // ../ai-providers/dist/kling/KlingProvider.js
12500
12576
  import { createHmac } from "node:crypto";
12577
+ function resolveKlingModel(alias) {
12578
+ const key = (alias ?? "v3").trim().toLowerCase();
12579
+ const aliases = {
12580
+ v3: "kling-v3",
12581
+ "kling-v3": "kling-v3",
12582
+ "v2.6": "kling-v2-6",
12583
+ "kling-v2-6": "kling-v2-6",
12584
+ "v2.5-turbo": "kling-v2-5-turbo",
12585
+ "v2.5": "kling-v2-5-turbo",
12586
+ "kling-v2-5-turbo": "kling-v2-5-turbo"
12587
+ };
12588
+ if (Object.hasOwn(aliases, key))
12589
+ return aliases[key];
12590
+ throw new Error(`Unknown Kling model "${alias}". Valid: v3, v2.6, v2.5-turbo.`);
12591
+ }
12592
+ function klingDuration(model, seconds) {
12593
+ const requested = typeof seconds === "number" && Number.isFinite(seconds) ? Math.round(seconds) : 5;
12594
+ if (model === "kling-v3")
12595
+ return String(Math.max(3, Math.min(15, requested)));
12596
+ return requested >= 10 ? "10" : "5";
12597
+ }
12501
12598
  var DEFAULT_MODEL4, STD_MODE_MODELS, KlingProvider, klingProvider;
12502
12599
  var init_KlingProvider = __esm({
12503
12600
  "../ai-providers/dist/kling/KlingProvider.js"() {
12504
12601
  "use strict";
12505
- DEFAULT_MODEL4 = "kling-v2-5-turbo";
12506
- STD_MODE_MODELS = ["kling-v2-5-turbo", "kling-v2-6", "kling-v3", "kling-v3-omni"];
12602
+ DEFAULT_MODEL4 = "kling-v3";
12603
+ STD_MODE_MODELS = ["kling-v2-5-turbo", "kling-v2-6", "kling-v3"];
12507
12604
  KlingProvider = class {
12508
12605
  constructor() {
12509
12606
  this.id = "kling";
@@ -12518,11 +12615,11 @@ var init_KlingProvider = __esm({
12518
12615
  async initialize(config2) {
12519
12616
  if (config2.apiKey) {
12520
12617
  const parts = config2.apiKey.split(":");
12521
- if (parts.length === 2) {
12618
+ if (parts.length === 2 && parts[0] && parts[1]) {
12522
12619
  this.accessKey = parts[0];
12523
12620
  this.secretKey = parts[1];
12524
12621
  } else {
12525
- this.accessKey = config2.apiKey;
12622
+ this.apiKey = config2.apiKey;
12526
12623
  }
12527
12624
  }
12528
12625
  if (config2.baseUrl) {
@@ -12530,7 +12627,11 @@ var init_KlingProvider = __esm({
12530
12627
  }
12531
12628
  }
12532
12629
  isConfigured() {
12533
- return !!(this.accessKey && this.secretKey);
12630
+ return !!(this.apiKey || this.accessKey && this.secretKey);
12631
+ }
12632
+ /** Bearer value: the API key itself, or a JWT signed from the legacy pair. */
12633
+ bearerToken() {
12634
+ return this.apiKey ?? this.generateToken();
12534
12635
  }
12535
12636
  /**
12536
12637
  * Generate JWT token for Kling API authentication
@@ -12560,11 +12661,11 @@ var init_KlingProvider = __esm({
12560
12661
  return {
12561
12662
  id: "",
12562
12663
  status: "failed",
12563
- error: "Kling API credentials not configured. Use format: KLING_ACCESS_KEY:KLING_SECRET_KEY"
12664
+ error: "Kling API credentials not configured. Set KLING_API_KEY to your Kling API key (or the legacy ACCESS_KEY:SECRET_KEY pair)."
12564
12665
  };
12565
12666
  }
12566
12667
  try {
12567
- const token = this.generateToken();
12668
+ const token = this.bearerToken();
12568
12669
  const model = options?.model || DEFAULT_MODEL4;
12569
12670
  let mode = options?.mode || "std";
12570
12671
  if (!STD_MODE_MODELS.includes(model) && mode === "std") {
@@ -12582,7 +12683,7 @@ var init_KlingProvider = __esm({
12582
12683
  model_name: model,
12583
12684
  mode,
12584
12685
  aspect_ratio: aspectRatioMap[options?.aspectRatio || "16:9"] || "16:9",
12585
- duration: options?.duration === 10 ? "10" : "5"
12686
+ duration: klingDuration(model, options?.duration)
12586
12687
  };
12587
12688
  if (options?.negativePrompt) {
12588
12689
  body.negative_prompt = options.negativePrompt;
@@ -12694,7 +12795,7 @@ var init_KlingProvider = __esm({
12694
12795
  };
12695
12796
  }
12696
12797
  try {
12697
- const token = this.generateToken();
12798
+ const token = this.bearerToken();
12698
12799
  const response = await fetch(`${this.baseUrl}/videos/${type}/${id}`, {
12699
12800
  headers: {
12700
12801
  "Content-Type": "application/json",
@@ -12776,11 +12877,11 @@ var init_KlingProvider = __esm({
12776
12877
  return {
12777
12878
  id: "",
12778
12879
  status: "failed",
12779
- error: "Kling API credentials not configured. Use format: KLING_ACCESS_KEY:KLING_SECRET_KEY"
12880
+ error: "Kling API credentials not configured. Set KLING_API_KEY to your Kling API key (or the legacy ACCESS_KEY:SECRET_KEY pair)."
12780
12881
  };
12781
12882
  }
12782
12883
  try {
12783
- const token = this.generateToken();
12884
+ const token = this.bearerToken();
12784
12885
  const body = {
12785
12886
  video_id: videoId,
12786
12887
  duration: options?.duration || "5"
@@ -12820,7 +12921,7 @@ var init_KlingProvider = __esm({
12820
12921
  };
12821
12922
  }
12822
12923
  try {
12823
- const token = this.generateToken();
12924
+ const token = this.bearerToken();
12824
12925
  const response = await fetch(`${this.baseUrl}/videos/video-extend/${id}`, {
12825
12926
  headers: {
12826
12927
  "Content-Type": "application/json",
@@ -12936,11 +13037,34 @@ var init_kling = __esm({
12936
13037
  });
12937
13038
 
12938
13039
  // ../ai-providers/dist/grok/GrokProvider.js
12939
- var DEFAULT_MODEL5, GrokProvider, grokProvider;
13040
+ function resolveGrokVideoModel(alias) {
13041
+ const key = (alias ?? "1.5").trim().toLowerCase();
13042
+ const aliases = {
13043
+ "1.5": "grok-imagine-video-1.5",
13044
+ "grok-imagine-video-1.5": "grok-imagine-video-1.5",
13045
+ lite: "grok-imagine-video-1.5-lite",
13046
+ "1.5-lite": "grok-imagine-video-1.5-lite",
13047
+ "grok-imagine-video-1.5-lite": "grok-imagine-video-1.5-lite",
13048
+ classic: "grok-imagine-video",
13049
+ "grok-imagine-video": "grok-imagine-video"
13050
+ };
13051
+ if (Object.hasOwn(aliases, key))
13052
+ return aliases[key];
13053
+ throw new Error(`Unknown Grok video model "${alias}". Valid: 1.5, lite, classic.`);
13054
+ }
13055
+ function resolveGrokImageModel(alias) {
13056
+ if (alias === "pro" || alias === "2.0" || alias === "quality" || alias === "grok-imagine-image-2.0") {
13057
+ return { model: "grok-imagine-image-2.0", quality: "medium" };
13058
+ }
13059
+ return { model: "grok-imagine-image" };
13060
+ }
13061
+ var DEFAULT_MODEL5, DEFAULT_VIDEO_RESOLUTION, GROK_VIDEO_RESOLUTIONS, GrokProvider, grokProvider;
12940
13062
  var init_GrokProvider = __esm({
12941
13063
  "../ai-providers/dist/grok/GrokProvider.js"() {
12942
13064
  "use strict";
12943
- DEFAULT_MODEL5 = "grok-imagine-video";
13065
+ DEFAULT_MODEL5 = "grok-imagine-video-1.5";
13066
+ DEFAULT_VIDEO_RESOLUTION = "720p";
13067
+ GROK_VIDEO_RESOLUTIONS = ["480p", "720p", "1080p"];
12944
13068
  GrokProvider = class {
12945
13069
  constructor() {
12946
13070
  this.id = "grok";
@@ -12974,6 +13098,7 @@ var init_GrokProvider = __esm({
12974
13098
  try {
12975
13099
  const body = {
12976
13100
  model: options.model || "grok-imagine-image",
13101
+ ...options.quality ? { quality: options.quality } : {},
12977
13102
  prompt: prompt3,
12978
13103
  n: options.n || 1,
12979
13104
  response_format: options.responseFormat || "url"
@@ -13041,6 +13166,7 @@ var init_GrokProvider = __esm({
13041
13166
  const dataUri = `data:image/png;base64,${base64}`;
13042
13167
  const body = {
13043
13168
  model: options.model || "grok-imagine-image",
13169
+ ...options.quality ? { quality: options.quality } : {},
13044
13170
  prompt: prompt3,
13045
13171
  image: {
13046
13172
  url: dataUri,
@@ -13107,11 +13233,15 @@ var init_GrokProvider = __esm({
13107
13233
  try {
13108
13234
  const duration = Math.round(Math.min(15, Math.max(1, options?.duration || 5)));
13109
13235
  const body = {
13110
- model: DEFAULT_MODEL5,
13236
+ model: options?.model || DEFAULT_MODEL5,
13111
13237
  prompt: prompt3,
13112
13238
  duration,
13113
- aspect_ratio: options?.aspectRatio || "16:9"
13239
+ aspect_ratio: options?.aspectRatio || "16:9",
13240
+ resolution: GROK_VIDEO_RESOLUTIONS.includes(options?.resolution ?? "") ? options.resolution : DEFAULT_VIDEO_RESOLUTION
13114
13241
  };
13242
+ if (typeof options?.generateAudio === "boolean") {
13243
+ body.generate_audio = options.generateAudio;
13244
+ }
13115
13245
  if (options?.referenceImage) {
13116
13246
  body.image = { url: options.referenceImage };
13117
13247
  }
@@ -13173,13 +13303,14 @@ var init_GrokProvider = __esm({
13173
13303
  const statusMap = {
13174
13304
  pending: "pending",
13175
13305
  done: "completed",
13176
- expired: "failed"
13306
+ expired: "failed",
13307
+ failed: "failed"
13177
13308
  };
13178
13309
  return {
13179
13310
  id,
13180
13311
  status: statusMap[data.status] || "pending",
13181
13312
  videoUrl: data.video?.url,
13182
- error: data.status === "expired" ? "Generation expired" : void 0
13313
+ error: data.status === "expired" ? "Generation expired" : data.status === "failed" ? `Generation failed: ${data.error?.message ?? data.error?.code ?? "unknown error"}` : void 0
13183
13314
  };
13184
13315
  } catch (error) {
13185
13316
  return {
@@ -17373,14 +17504,30 @@ var require_src = __commonJS({
17373
17504
  });
17374
17505
 
17375
17506
  // ../ai-providers/dist/fal/FalProvider.js
17507
+ function resolveSeedanceVariant(alias) {
17508
+ const key = (alias ?? "quality").trim().toLowerCase();
17509
+ const aliases = {
17510
+ quality: "seedance-2.0",
17511
+ "2.0": "seedance-2.0",
17512
+ "seedance-2.0": "seedance-2.0",
17513
+ fast: "seedance-2.0-fast",
17514
+ "seedance-2.0-fast": "seedance-2.0-fast",
17515
+ "2.5": "seedance-2.5",
17516
+ "seedance-2.5": "seedance-2.5"
17517
+ };
17518
+ if (Object.hasOwn(aliases, key))
17519
+ return aliases[key];
17520
+ throw new Error(`Unknown Seedance model "${alias}". Valid: quality, fast, 2.5.`);
17521
+ }
17376
17522
  function estimateSeedanceVideoCostUsd(opts) {
17377
17523
  const shortSide = SEEDANCE_SHORT_SIDE[opts.resolution ?? "720p"] ?? 720;
17378
17524
  const [a, b] = (opts.aspectRatio ?? "16:9").split(":").map(Number);
17379
17525
  const ratioFactor = a > 0 && b > 0 ? Math.max(a, b) / Math.min(a, b) : 16 / 9;
17380
17526
  const longSide = Math.round(shortSide * ratioFactor);
17381
17527
  const tokensPerSecond = shortSide * longSide * 24 / 1024;
17382
- let usdPerSecond = tokensPerSecond / 1e3 * SEEDANCE_USD_PER_1K_TOKENS;
17383
- if (opts.fast && shortSide <= 720)
17528
+ const rate = opts.v25 ? shortSide >= 1080 ? SEEDANCE_25_1080P_USD_PER_1K_TOKENS : SEEDANCE_25_USD_PER_1K_TOKENS : SEEDANCE_USD_PER_1K_TOKENS;
17529
+ let usdPerSecond = tokensPerSecond / 1e3 * rate;
17530
+ if (opts.fast && !opts.v25 && shortSide <= 720)
17384
17531
  usdPerSecond *= SEEDANCE_FAST_FACTOR;
17385
17532
  if (opts.hasVideoReference)
17386
17533
  usdPerSecond *= SEEDANCE_VIDEO_REF_FACTOR;
@@ -17488,34 +17635,42 @@ function normaliseResolution(value) {
17488
17635
  return "1080p";
17489
17636
  return "720p";
17490
17637
  }
17491
- function normaliseDuration(value) {
17492
- if (typeof value !== "number")
17493
- return "auto";
17494
- if (!Number.isFinite(value))
17638
+ function normaliseDuration(value, maxSec) {
17639
+ if (typeof value !== "number" || !Number.isFinite(value))
17495
17640
  return "auto";
17496
- return Math.max(4, Math.min(15, Math.round(value)));
17641
+ return String(Math.max(4, Math.min(maxSec, Math.round(value))));
17497
17642
  }
17498
- 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;
17643
+ 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;
17499
17644
  var init_FalProvider = __esm({
17500
17645
  "../ai-providers/dist/fal/FalProvider.js"() {
17501
17646
  "use strict";
17502
17647
  import_client = __toESM(require_src(), 1);
17503
17648
  ENDPOINT_TEXT_TO_VIDEO = {
17504
17649
  "seedance-2.0": "bytedance/seedance-2.0/text-to-video",
17505
- "seedance-2.0-fast": "bytedance/seedance-2.0/fast/text-to-video"
17650
+ "seedance-2.0-fast": "bytedance/seedance-2.0/fast/text-to-video",
17651
+ "seedance-2.5": "bytedance/seedance-2.5/text-to-video"
17506
17652
  };
17507
17653
  ENDPOINT_IMAGE_TO_VIDEO = {
17508
17654
  "seedance-2.0": "bytedance/seedance-2.0/image-to-video",
17509
- "seedance-2.0-fast": "bytedance/seedance-2.0/fast/image-to-video"
17655
+ "seedance-2.0-fast": "bytedance/seedance-2.0/fast/image-to-video",
17656
+ "seedance-2.5": "bytedance/seedance-2.5/image-to-video"
17510
17657
  };
17511
17658
  ENDPOINT_REFERENCE_TO_VIDEO = {
17512
17659
  "seedance-2.0": "bytedance/seedance-2.0/reference-to-video",
17513
- "seedance-2.0-fast": "bytedance/seedance-2.0/fast/reference-to-video"
17660
+ "seedance-2.0-fast": "bytedance/seedance-2.0/fast/reference-to-video",
17661
+ "seedance-2.5": "bytedance/seedance-2.5/reference-to-video"
17514
17662
  };
17515
17663
  DEFAULT_VARIANT = "seedance-2.0";
17664
+ MAX_DURATION_SEC = {
17665
+ "seedance-2.0": 15,
17666
+ "seedance-2.0-fast": 15,
17667
+ "seedance-2.5": 30
17668
+ };
17516
17669
  VALID_RESOLUTIONS = ["480p", "720p", "1080p"];
17517
17670
  VALID_ASPECTS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "auto"];
17518
17671
  SEEDANCE_USD_PER_1K_TOKENS = 0.014;
17672
+ SEEDANCE_25_USD_PER_1K_TOKENS = 0.0214;
17673
+ SEEDANCE_25_1080P_USD_PER_1K_TOKENS = 0.0234;
17519
17674
  SEEDANCE_FAST_FACTOR = 0.8;
17520
17675
  SEEDANCE_VIDEO_REF_FACTOR = 0.6;
17521
17676
  SEEDANCE_SHORT_SIDE = { "480p": 480, "720p": 720, "1080p": 1080 };
@@ -17570,7 +17725,7 @@ var init_FalProvider = __esm({
17570
17725
  const endpointId = hasReferences ? ENDPOINT_REFERENCE_TO_VIDEO[variant] : isImageToVideo ? ENDPOINT_IMAGE_TO_VIDEO[variant] : ENDPOINT_TEXT_TO_VIDEO[variant];
17571
17726
  const aspect = normaliseAspect(options?.aspectRatio);
17572
17727
  const resolution = normaliseResolution(options?.resolution);
17573
- const duration = normaliseDuration(options?.duration);
17728
+ const duration = normaliseDuration(options?.duration, MAX_DURATION_SEC[variant]);
17574
17729
  const input = {
17575
17730
  prompt: prompt3,
17576
17731
  aspect_ratio: aspect,
@@ -17588,10 +17743,12 @@ var init_FalProvider = __esm({
17588
17743
  } else if (referenceImage) {
17589
17744
  input.image_url = referenceImage;
17590
17745
  }
17591
- if (options?.negativePrompt)
17592
- input.negative_prompt = options.negativePrompt;
17593
- if (typeof options?.seed === "number")
17594
- input.seed = options.seed;
17746
+ if (variant !== "seedance-2.5") {
17747
+ if (options?.negativePrompt)
17748
+ input.negative_prompt = options.negativePrompt;
17749
+ if (typeof options?.seed === "number")
17750
+ input.seed = options.seed;
17751
+ }
17595
17752
  if (typeof options?.generateAudio === "boolean")
17596
17753
  input.generate_audio = options.generateAudio;
17597
17754
  if (options?.endUserId)
@@ -18318,6 +18475,7 @@ var init_replicate = __esm({
18318
18475
  var dist_exports2 = {};
18319
18476
  __export(dist_exports2, {
18320
18477
  ClaudeProvider: () => ClaudeProvider,
18478
+ ELEVENLABS_MUSIC_MODEL: () => ELEVENLABS_MUSIC_MODEL,
18321
18479
  ElevenLabsProvider: () => ElevenLabsProvider,
18322
18480
  FalProvider: () => FalProvider,
18323
18481
  GEMINI_AGENT_DEFAULT_TEXT_MODEL: () => GEMINI_AGENT_DEFAULT_TEXT_MODEL,
@@ -18331,6 +18489,7 @@ __export(dist_exports2, {
18331
18489
  KOKORO_MODEL_ID: () => KOKORO_MODEL_ID,
18332
18490
  KlingProvider: () => KlingProvider,
18333
18491
  KokoroProvider: () => KokoroProvider,
18492
+ OPENAI_IMAGE_DEFAULT_MODEL: () => OPENAI_IMAGE_DEFAULT_MODEL,
18334
18493
  OPENAI_TTS_VOICES: () => OPENAI_TTS_VOICES,
18335
18494
  OllamaProvider: () => OllamaProvider,
18336
18495
  OmniProvider: () => OmniProvider,
@@ -18339,6 +18498,7 @@ __export(dist_exports2, {
18339
18498
  OpenAiTtsProvider: () => OpenAiTtsProvider,
18340
18499
  ReplicateProvider: () => ReplicateProvider,
18341
18500
  RunwayProvider: () => RunwayProvider,
18501
+ VEO_MODEL_ALIASES: () => VEO_MODEL_ALIASES,
18342
18502
  WhisperProvider: () => WhisperProvider,
18343
18503
  claudeProvider: () => claudeProvider,
18344
18504
  defineApiKey: () => defineApiKey,
@@ -18359,6 +18519,7 @@ __export(dist_exports2, {
18359
18519
  getSetupProviders: () => getSetupProviders,
18360
18520
  grokProvider: () => grokProvider,
18361
18521
  isGeminiTextModelAlias: () => isGeminiTextModelAlias,
18522
+ klingDuration: () => klingDuration,
18362
18523
  klingProvider: () => klingProvider,
18363
18524
  kokoroProvider: () => kokoroProvider,
18364
18525
  loadBundledKokoroRuntime: () => loadBundledKokoroRuntime,
@@ -18371,6 +18532,11 @@ __export(dist_exports2, {
18371
18532
  providerRegistry: () => providerRegistry,
18372
18533
  replicateProvider: () => replicateProvider,
18373
18534
  resolveGeminiTextModel: () => resolveGeminiTextModel,
18535
+ resolveGrokImageModel: () => resolveGrokImageModel,
18536
+ resolveGrokVideoModel: () => resolveGrokVideoModel,
18537
+ resolveKlingModel: () => resolveKlingModel,
18538
+ resolveSeedanceVariant: () => resolveSeedanceVariant,
18539
+ resolveVeoModel: () => resolveVeoModel,
18374
18540
  resolveVoiceId: () => resolveVoiceId,
18375
18541
  runwayProvider: () => runwayProvider,
18376
18542
  whisperProvider: () => whisperProvider
@@ -33150,8 +33316,8 @@ var init_ai_motion = __esm({
33150
33316
  init_output();
33151
33317
  init_validate();
33152
33318
  MODEL_MAP2 = {
33153
- sonnet: { provider: "claude", modelId: "claude-sonnet-4-6" },
33154
- opus: { provider: "claude", modelId: "claude-opus-4-7" },
33319
+ sonnet: { provider: "claude", modelId: "claude-sonnet-5-5" },
33320
+ opus: { provider: "claude", modelId: "claude-opus-5-5" },
33155
33321
  "opus-4-6": { provider: "claude", modelId: "claude-opus-4-6" },
33156
33322
  gemini: { provider: "gemini", modelId: GEMINI_DEFAULT_TEXT_MODEL },
33157
33323
  "gemini-2.5-pro": { provider: "gemini", modelId: "gemini-2.5-pro" },
@@ -33197,7 +33363,7 @@ async function executeSoundEffect(options) {
33197
33363
  }
33198
33364
  }
33199
33365
  function registerSoundEffectCommand(parent) {
33200
- 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) => {
33366
+ 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) => {
33201
33367
  const startedAt = Date.now();
33202
33368
  try {
33203
33369
  rejectControlChars(prompt3);
@@ -34126,8 +34292,14 @@ __export(ai_video_exports, {
34126
34292
  executeVideoGenerate: () => executeVideoGenerate,
34127
34293
  executeVideoStatus: () => executeVideoStatus
34128
34294
  });
34129
- import { readFile as readFile15, writeFile as writeFile17 } from "node:fs/promises";
34130
- import { resolve as resolve21 } from "node:path";
34295
+ import { mkdir as mkdir8, readFile as readFile15, writeFile as writeFile17 } from "node:fs/promises";
34296
+ import { dirname as dirname3, resolve as resolve21 } from "node:path";
34297
+ async function saveOutput(output, buffer) {
34298
+ const outputPath = resolve21(process.cwd(), output);
34299
+ await mkdir8(dirname3(outputPath), { recursive: true });
34300
+ await writeFile17(outputPath, buffer);
34301
+ return outputPath;
34302
+ }
34131
34303
  async function executeVideoGenerate(options) {
34132
34304
  const {
34133
34305
  prompt: prompt3,
@@ -34144,6 +34316,8 @@ async function executeVideoGenerate(options) {
34144
34316
  resolution,
34145
34317
  veoModel = "3.1-fast",
34146
34318
  seedanceModel = "quality",
34319
+ grokModel,
34320
+ klingModel,
34147
34321
  generateAudio,
34148
34322
  output,
34149
34323
  wait = true,
@@ -34198,7 +34372,7 @@ async function executeVideoGenerate(options) {
34198
34372
  });
34199
34373
  falImage = upload.url;
34200
34374
  }
34201
- const model = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
34375
+ const model = resolveSeedanceVariant(seedanceModel);
34202
34376
  const result = await fal.generateVideo(prompt3, {
34203
34377
  prompt: prompt3,
34204
34378
  referenceImage: references.length > 0 ? void 0 : falImage,
@@ -34215,8 +34389,7 @@ async function executeVideoGenerate(options) {
34215
34389
  let outputPath;
34216
34390
  if (output && result.videoUrl) {
34217
34391
  const buffer = await downloadVideo(result.videoUrl, key);
34218
- outputPath = resolve21(process.cwd(), output);
34219
- await writeFile17(outputPath, buffer);
34392
+ outputPath = await saveOutput(output, buffer);
34220
34393
  }
34221
34394
  return {
34222
34395
  success: true,
@@ -34247,8 +34420,7 @@ async function executeVideoGenerate(options) {
34247
34420
  let outputPath;
34248
34421
  if (output && finalResult.videoUrl) {
34249
34422
  const buffer = await downloadVideo(finalResult.videoUrl, key);
34250
- outputPath = resolve21(process.cwd(), output);
34251
- await writeFile17(outputPath, buffer);
34423
+ outputPath = await saveOutput(output, buffer);
34252
34424
  }
34253
34425
  return {
34254
34426
  success: true,
@@ -34274,6 +34446,7 @@ async function executeVideoGenerate(options) {
34274
34446
  }
34275
34447
  const result = await kling.generateVideo(prompt3, {
34276
34448
  prompt: prompt3,
34449
+ model: resolveKlingModel(klingModel),
34277
34450
  referenceImage: klingImage,
34278
34451
  duration,
34279
34452
  aspectRatio: ratio,
@@ -34292,8 +34465,7 @@ async function executeVideoGenerate(options) {
34292
34465
  let outputPath;
34293
34466
  if (output && finalResult.videoUrl) {
34294
34467
  const buffer = await downloadVideo(finalResult.videoUrl, key);
34295
- outputPath = resolve21(process.cwd(), output);
34296
- await writeFile17(outputPath, buffer);
34468
+ outputPath = await saveOutput(output, buffer);
34297
34469
  }
34298
34470
  return {
34299
34471
  success: true,
@@ -34307,12 +34479,7 @@ async function executeVideoGenerate(options) {
34307
34479
  } else if (provider === "veo") {
34308
34480
  const gemini = new GeminiProvider();
34309
34481
  await gemini.initialize({ apiKey: key });
34310
- const veoModelMap = {
34311
- "3.0": "veo-3.0-generate-preview",
34312
- "3.1": "veo-3.1-generate-preview",
34313
- "3.1-fast": "veo-3.1-fast-generate-preview"
34314
- };
34315
- const model = veoModelMap[veoModel] || "veo-3.1-fast-generate-preview";
34482
+ const model = resolveVeoModel(veoModel);
34316
34483
  const veoDuration = duration <= 6 ? 6 : 8;
34317
34484
  const result = await gemini.generateVideo(prompt3, {
34318
34485
  prompt: prompt3,
@@ -34333,8 +34500,7 @@ async function executeVideoGenerate(options) {
34333
34500
  let outputPath;
34334
34501
  if (output && finalResult.videoUrl) {
34335
34502
  const buffer = await downloadVideo(finalResult.videoUrl, key);
34336
- outputPath = resolve21(process.cwd(), output);
34337
- await writeFile17(outputPath, buffer);
34503
+ outputPath = await saveOutput(output, buffer);
34338
34504
  }
34339
34505
  return {
34340
34506
  success: true,
@@ -34349,9 +34515,12 @@ async function executeVideoGenerate(options) {
34349
34515
  await grok.initialize({ apiKey: key });
34350
34516
  const result = await grok.generateVideo(prompt3, {
34351
34517
  prompt: prompt3,
34518
+ model: resolveGrokVideoModel(grokModel),
34352
34519
  referenceImage,
34353
34520
  duration,
34354
- aspectRatio: ratio
34521
+ aspectRatio: ratio,
34522
+ resolution,
34523
+ generateAudio
34355
34524
  });
34356
34525
  if (result.status === "failed")
34357
34526
  return { success: false, error: result.error || "Grok generation failed" };
@@ -34364,8 +34533,7 @@ async function executeVideoGenerate(options) {
34364
34533
  let outputPath;
34365
34534
  if (output && finalResult.videoUrl) {
34366
34535
  const buffer = await downloadVideo(finalResult.videoUrl, key);
34367
- outputPath = resolve21(process.cwd(), output);
34368
- await writeFile17(outputPath, buffer);
34536
+ outputPath = await saveOutput(output, buffer);
34369
34537
  }
34370
34538
  return {
34371
34539
  success: true,
@@ -34382,7 +34550,8 @@ async function executeVideoGenerate(options) {
34382
34550
  const result = await omni.generateVideo(prompt3, {
34383
34551
  prompt: prompt3,
34384
34552
  referenceImage,
34385
- aspectRatio: ratio
34553
+ aspectRatio: ratio,
34554
+ resolution
34386
34555
  });
34387
34556
  if (result.status !== "completed" || !result.videoUrl) {
34388
34557
  return { success: false, error: result.error || "Gemini Omni generation failed" };
@@ -34390,8 +34559,7 @@ async function executeVideoGenerate(options) {
34390
34559
  let outputPath;
34391
34560
  if (output) {
34392
34561
  const buffer = await downloadVideo(result.videoUrl, key);
34393
- outputPath = resolve21(process.cwd(), output);
34394
- await writeFile17(outputPath, buffer);
34562
+ outputPath = await saveOutput(output, buffer);
34395
34563
  }
34396
34564
  return {
34397
34565
  success: true,
@@ -34487,8 +34655,7 @@ async function executeVideoStatus(options) {
34487
34655
  let outputPath;
34488
34656
  if (output && result.videoUrl) {
34489
34657
  const buffer = await downloadVideo(result.videoUrl);
34490
- outputPath = resolve21(process.cwd(), output);
34491
- await writeFile17(outputPath, buffer);
34658
+ outputPath = await saveOutput(output, buffer);
34492
34659
  }
34493
34660
  return {
34494
34661
  success: true,
@@ -34510,8 +34677,7 @@ async function executeVideoStatus(options) {
34510
34677
  let outputPath;
34511
34678
  if (output && result.videoUrl) {
34512
34679
  const buffer = await downloadVideo(result.videoUrl, key);
34513
- outputPath = resolve21(process.cwd(), output);
34514
- await writeFile17(outputPath, buffer);
34680
+ outputPath = await saveOutput(output, buffer);
34515
34681
  }
34516
34682
  return {
34517
34683
  success: true,
@@ -34532,8 +34698,7 @@ async function executeVideoStatus(options) {
34532
34698
  let outputPath;
34533
34699
  if (output && result.videoUrl) {
34534
34700
  const buffer = await downloadVideo(result.videoUrl, key);
34535
- outputPath = resolve21(process.cwd(), output);
34536
- await writeFile17(outputPath, buffer);
34701
+ outputPath = await saveOutput(output, buffer);
34537
34702
  }
34538
34703
  return {
34539
34704
  success: true,
@@ -34555,8 +34720,7 @@ async function executeVideoStatus(options) {
34555
34720
  let outputPath;
34556
34721
  if (output && result.videoUrl) {
34557
34722
  const buffer = await downloadVideo(result.videoUrl, key);
34558
- outputPath = resolve21(process.cwd(), output);
34559
- await writeFile17(outputPath, buffer);
34723
+ outputPath = await saveOutput(output, buffer);
34560
34724
  }
34561
34725
  return {
34562
34726
  success: true,
@@ -34626,8 +34790,7 @@ async function executeVideoExtend(options) {
34626
34790
  let outputPath;
34627
34791
  if (output && finalResult.videoUrl) {
34628
34792
  const buffer = await downloadVideo(finalResult.videoUrl, key);
34629
- outputPath = resolve21(process.cwd(), output);
34630
- await writeFile17(outputPath, buffer);
34793
+ outputPath = await saveOutput(output, buffer);
34631
34794
  }
34632
34795
  return {
34633
34796
  success: true,
@@ -34642,12 +34805,7 @@ async function executeVideoExtend(options) {
34642
34805
  if (!key) return { success: false, error: "GOOGLE_API_KEY required" };
34643
34806
  const gemini = new GeminiProvider();
34644
34807
  await gemini.initialize({ apiKey: key });
34645
- const veoModelMap = {
34646
- "3.0": "veo-3.0-generate-preview",
34647
- "3.1": "veo-3.1-generate-preview",
34648
- "3.1-fast": "veo-3.1-fast-generate-preview"
34649
- };
34650
- const model = veoModelMap[veoModel] || "veo-3.1-generate-preview";
34808
+ const model = resolveVeoModel(veoModel);
34651
34809
  const result = await gemini.extendVideo(videoId, prompt3, {
34652
34810
  duration,
34653
34811
  model
@@ -34662,8 +34820,7 @@ async function executeVideoExtend(options) {
34662
34820
  let outputPath;
34663
34821
  if (output && finalResult.videoUrl) {
34664
34822
  const buffer = await downloadVideo(finalResult.videoUrl, key);
34665
- outputPath = resolve21(process.cwd(), output);
34666
- await writeFile17(outputPath, buffer);
34823
+ outputPath = await saveOutput(output, buffer);
34667
34824
  }
34668
34825
  return {
34669
34826
  success: true,
@@ -34725,8 +34882,8 @@ var init_music_status = __esm({
34725
34882
 
34726
34883
  // src/commands/_shared/build-asset-metadata.ts
34727
34884
  import { existsSync as existsSync14, readFileSync } from "node:fs";
34728
- import { mkdir as mkdir8, writeFile as writeFile18 } from "node:fs/promises";
34729
- import { dirname as dirname3, join as join11 } from "node:path";
34885
+ import { mkdir as mkdir9, writeFile as writeFile18 } from "node:fs/promises";
34886
+ import { dirname as dirname4, join as join11 } from "node:path";
34730
34887
  function assetMetadataPath(kind, beatId) {
34731
34888
  return `.vibeframe/assets/${kind}-${beatId}.json`;
34732
34889
  }
@@ -34774,7 +34931,7 @@ async function writeAssetMetadata(opts) {
34774
34931
  cachePath: opts.cachePath,
34775
34932
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
34776
34933
  };
34777
- await mkdir8(dirname3(abs), { recursive: true });
34934
+ await mkdir9(dirname4(abs), { recursive: true });
34778
34935
  await writeFile18(abs, JSON.stringify(metadata, null, 2) + "\n", "utf-8");
34779
34936
  }
34780
34937
  var init_build_asset_metadata = __esm({
@@ -34786,22 +34943,22 @@ var init_build_asset_metadata = __esm({
34786
34943
  // src/commands/_shared/status-jobs.ts
34787
34944
  import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
34788
34945
  import { existsSync as existsSync15, statSync } from "node:fs";
34789
- import { copyFile as copyFile3, mkdir as mkdir9, readFile as readFile16, readdir as readdir3, rename as rename2, stat as stat4, writeFile as writeFile19 } from "node:fs/promises";
34790
- import { dirname as dirname4, join as join12, parse as parse2, relative as relative6, resolve as resolve22 } from "node:path";
34946
+ import { copyFile as copyFile3, mkdir as mkdir10, readFile as readFile16, readdir as readdir3, rename as rename2, stat as stat4, writeFile as writeFile19 } from "node:fs/promises";
34947
+ import { dirname as dirname5, join as join12, parse as parse2, relative as relative6, resolve as resolve22 } from "node:path";
34791
34948
  function isLocalJob(record) {
34792
34949
  return record.jobType === "build" || record.jobType === "render";
34793
34950
  }
34794
34951
  function findProjectRoot(start = process.cwd()) {
34795
34952
  let dir = resolve22(start);
34796
34953
  try {
34797
- if (existsSync15(dir) && statSync(dir).isFile()) dir = dirname4(dir);
34954
+ if (existsSync15(dir) && statSync(dir).isFile()) dir = dirname5(dir);
34798
34955
  } catch {
34799
34956
  }
34800
34957
  for (; ; ) {
34801
34958
  if (existsSync15(join12(dir, "vibe.config.json")) || existsSync15(join12(dir, "STORYBOARD.md"))) {
34802
34959
  return dir;
34803
34960
  }
34804
- const parent = dirname4(dir);
34961
+ const parent = dirname5(dir);
34805
34962
  if (parent === dir) return resolve22(start);
34806
34963
  dir = parent;
34807
34964
  }
@@ -34868,7 +35025,7 @@ async function createAndWriteJobRecord(opts) {
34868
35025
  }
34869
35026
  async function writeJobRecord(record) {
34870
35027
  const dir = jobsDir(record.projectDir);
34871
- await mkdir9(dir, { recursive: true });
35028
+ await mkdir10(dir, { recursive: true });
34872
35029
  const path2 = jobRecordPath(record.id, record.projectDir);
34873
35030
  const tmp = `${path2}.${randomUUID2()}.tmp`;
34874
35031
  await writeFile19(tmp, JSON.stringify(stripUndefined2(record), null, 2) + "\n", "utf-8");
@@ -34916,7 +35073,7 @@ async function refreshJobRecord(record, opts = {}) {
34916
35073
  try {
34917
35074
  if (record.jobType === "generate-video") {
34918
35075
  const output = resolveRefreshOutput(record, opts);
34919
- if (output) await mkdir9(dirname4(output), { recursive: true });
35076
+ if (output) await mkdir10(dirname5(output), { recursive: true });
34920
35077
  const result = await executeVideoStatus({
34921
35078
  taskId: record.providerTaskId,
34922
35079
  provider: record.provider,
@@ -35199,14 +35356,14 @@ async function downloadUrl(url, outputPath) {
35199
35356
  const response = await fetch(url);
35200
35357
  if (!response.ok) throw new Error(`Download failed: HTTP ${response.status}`);
35201
35358
  const buffer = Buffer.from(await response.arrayBuffer());
35202
- await mkdir9(dirname4(outputPath), { recursive: true });
35359
+ await mkdir10(dirname5(outputPath), { recursive: true });
35203
35360
  await writeFile19(outputPath, buffer);
35204
35361
  return outputPath;
35205
35362
  }
35206
35363
  async function maybeCacheOutput(record) {
35207
35364
  if (record.status !== "completed" || !record.outputPath || !record.cachePath) return;
35208
35365
  try {
35209
- await mkdir9(dirname4(record.cachePath), { recursive: true });
35366
+ await mkdir10(dirname5(record.cachePath), { recursive: true });
35210
35367
  await copyFile3(record.outputPath, record.cachePath);
35211
35368
  } catch {
35212
35369
  }
@@ -35805,7 +35962,7 @@ function registerMusicCommand(parent) {
35805
35962
  console.log();
35806
35963
  console.log(`Saved to: ${source_default.bold(outputPath)}`);
35807
35964
  console.log(`Duration: ${duration}s`);
35808
- console.log(`Provider: ElevenLabs (music_v1)`);
35965
+ console.log(`Provider: ElevenLabs (${ELEVENLABS_MUSIC_MODEL})`);
35809
35966
  if (options.instrumental) console.log(`Mode: Instrumental`);
35810
35967
  console.log();
35811
35968
  } else {
@@ -35934,6 +36091,48 @@ var init_music = __esm({
35934
36091
  }
35935
36092
  });
35936
36093
 
36094
+ // src/utils/image-file.ts
36095
+ import { rm as rm3, writeFile as writeFile21 } from "node:fs/promises";
36096
+ import { extname as extname3 } from "node:path";
36097
+ function detectImageFormat(buffer) {
36098
+ if (buffer.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
36099
+ return "png";
36100
+ }
36101
+ if (buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255) return "jpeg";
36102
+ if (buffer.toString("ascii", 0, 4) === "RIFF" && buffer.toString("ascii", 8, 12) === "WEBP") {
36103
+ return "webp";
36104
+ }
36105
+ return void 0;
36106
+ }
36107
+ async function writeImageFile(path2, buffer) {
36108
+ const wanted = EXTENSION_FORMATS[extname3(path2).toLowerCase()];
36109
+ const actual = detectImageFormat(buffer);
36110
+ if (!wanted || !actual || wanted === actual) {
36111
+ await writeFile21(path2, buffer);
36112
+ return;
36113
+ }
36114
+ const source = `${path2}.source.${actual === "jpeg" ? "jpg" : actual}`;
36115
+ await writeFile21(source, buffer);
36116
+ try {
36117
+ await execSafe("ffmpeg", ["-y", "-loglevel", "error", "-i", source, "-frames:v", "1", path2]);
36118
+ } finally {
36119
+ await rm3(source, { force: true });
36120
+ }
36121
+ }
36122
+ var EXTENSION_FORMATS;
36123
+ var init_image_file = __esm({
36124
+ "src/utils/image-file.ts"() {
36125
+ "use strict";
36126
+ init_exec_safe();
36127
+ EXTENSION_FORMATS = {
36128
+ ".png": "png",
36129
+ ".jpg": "jpeg",
36130
+ ".jpeg": "jpeg",
36131
+ ".webp": "webp"
36132
+ };
36133
+ }
36134
+ });
36135
+
35937
36136
  // src/commands/ai-image.ts
35938
36137
  var ai_image_exports = {};
35939
36138
  __export(ai_image_exports, {
@@ -35941,8 +36140,8 @@ __export(ai_image_exports, {
35941
36140
  executeImageGenerate: () => executeImageGenerate,
35942
36141
  executeThumbnailBestFrame: () => executeThumbnailBestFrame
35943
36142
  });
35944
- import { resolve as resolve24, dirname as dirname5 } from "node:path";
35945
- import { readFile as readFile17, writeFile as writeFile21, mkdir as mkdir10 } from "node:fs/promises";
36143
+ import { resolve as resolve24, dirname as dirname6 } from "node:path";
36144
+ import { readFile as readFile17, mkdir as mkdir11 } from "node:fs/promises";
35946
36145
  import { existsSync as existsSync17 } from "node:fs";
35947
36146
  async function executeImageGenerate(options) {
35948
36147
  const {
@@ -35985,8 +36184,8 @@ async function executeImageGenerate(options) {
35985
36184
  return { success: false, error: "No image data available" };
35986
36185
  }
35987
36186
  outputPath = resolve24(process.cwd(), output);
35988
- await mkdir10(dirname5(outputPath), { recursive: true });
35989
- await writeFile21(outputPath, buffer);
36187
+ await mkdir11(dirname6(outputPath), { recursive: true });
36188
+ await writeImageFile(outputPath, buffer);
35990
36189
  }
35991
36190
  return {
35992
36191
  success: true,
@@ -36020,8 +36219,8 @@ async function executeImageGenerate(options) {
36020
36219
  const img = result.images[0];
36021
36220
  if (img.base64) {
36022
36221
  outputPath = resolve24(process.cwd(), output);
36023
- await mkdir10(dirname5(outputPath), { recursive: true });
36024
- await writeFile21(outputPath, Buffer.from(img.base64, "base64"));
36222
+ await mkdir11(dirname6(outputPath), { recursive: true });
36223
+ await writeImageFile(outputPath, Buffer.from(img.base64, "base64"));
36025
36224
  }
36026
36225
  }
36027
36226
  return {
@@ -36034,10 +36233,11 @@ async function executeImageGenerate(options) {
36034
36233
  } else if (provider === "grok") {
36035
36234
  const key = apiKey || process.env.XAI_API_KEY;
36036
36235
  if (!key) return { success: false, error: "XAI_API_KEY required" };
36037
- const openaiImage = new OpenAIImageProvider();
36038
- await openaiImage.initialize({ apiKey: key, baseUrl: "https://api.x.ai/v1" });
36039
- const result = await openaiImage.generateImage(prompt3, {
36040
- size,
36236
+ const grok = new GrokProvider();
36237
+ await grok.initialize({ apiKey: key });
36238
+ const result = await grok.generateImage(prompt3, {
36239
+ ...resolveGrokImageModel(model),
36240
+ aspectRatio: ratio,
36041
36241
  n: count
36042
36242
  });
36043
36243
  if (!result.success || !result.images) {
@@ -36056,8 +36256,8 @@ async function executeImageGenerate(options) {
36056
36256
  return { success: false, error: "No image data available" };
36057
36257
  }
36058
36258
  outputPath = resolve24(process.cwd(), output);
36059
- await mkdir10(dirname5(outputPath), { recursive: true });
36060
- await writeFile21(outputPath, buffer);
36259
+ await mkdir11(dirname6(outputPath), { recursive: true });
36260
+ await writeImageFile(outputPath, buffer);
36061
36261
  }
36062
36262
  return {
36063
36263
  success: true,
@@ -36115,8 +36315,8 @@ async function executeGeminiEdit(options) {
36115
36315
  let outputPath;
36116
36316
  if (img.base64) {
36117
36317
  outputPath = resolve24(process.cwd(), output);
36118
- await mkdir10(dirname5(outputPath), { recursive: true });
36119
- await writeFile21(outputPath, Buffer.from(img.base64, "base64"));
36318
+ await mkdir11(dirname6(outputPath), { recursive: true });
36319
+ await writeImageFile(outputPath, Buffer.from(img.base64, "base64"));
36120
36320
  }
36121
36321
  return {
36122
36322
  success: true,
@@ -36190,13 +36390,14 @@ var init_ai_image = __esm({
36190
36390
  "use strict";
36191
36391
  init_dist2();
36192
36392
  init_exec_safe();
36393
+ init_image_file();
36193
36394
  }
36194
36395
  });
36195
36396
 
36196
36397
  // src/commands/generate/thumbnail.ts
36197
- import { resolve as resolve25, dirname as dirname6, basename as basename5, extname as extname3 } from "node:path";
36398
+ import { resolve as resolve25, dirname as dirname7, basename as basename5, extname as extname4 } from "node:path";
36198
36399
  import { existsSync as existsSync18 } from "node:fs";
36199
- import { writeFile as writeFile22, mkdir as mkdir11 } from "node:fs/promises";
36400
+ import { writeFile as writeFile22, mkdir as mkdir12 } from "node:fs/promises";
36200
36401
  function registerThumbnailCommand(parent) {
36201
36402
  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) => {
36202
36403
  const startedAt = Date.now();
@@ -36219,7 +36420,7 @@ function registerThumbnailCommand(parent) {
36219
36420
  );
36220
36421
  }
36221
36422
  const apiKey2 = await requireApiKey("GOOGLE_API_KEY", "Google", options.apiKey);
36222
- const name = basename5(options.bestFrame, extname3(options.bestFrame));
36423
+ const name = basename5(options.bestFrame, extname4(options.bestFrame));
36223
36424
  const outputPath = options.output || `${name}-thumbnail.png`;
36224
36425
  const spinner3 = ora("Analyzing video for best frame...").start();
36225
36426
  const result2 = await executeThumbnailBestFrame({
@@ -36287,7 +36488,7 @@ function registerThumbnailCommand(parent) {
36287
36488
  throw new Error("No image data available");
36288
36489
  }
36289
36490
  outputPath = resolve25(process.cwd(), options.output);
36290
- await mkdir11(dirname6(outputPath), { recursive: true });
36491
+ await mkdir12(dirname7(outputPath), { recursive: true });
36291
36492
  await writeFile22(outputPath, buffer);
36292
36493
  }
36293
36494
  outputSuccess({
@@ -36318,7 +36519,7 @@ function registerThumbnailCommand(parent) {
36318
36519
  throw new Error("No image data available");
36319
36520
  }
36320
36521
  const outputPath = resolve25(process.cwd(), options.output);
36321
- await mkdir11(dirname6(outputPath), { recursive: true });
36522
+ await mkdir12(dirname7(outputPath), { recursive: true });
36322
36523
  await writeFile22(outputPath, buffer);
36323
36524
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
36324
36525
  } catch {
@@ -36349,7 +36550,7 @@ var init_thumbnail = __esm({
36349
36550
  import { resolve as resolve26 } from "node:path";
36350
36551
  import { writeFile as writeFile23 } from "node:fs/promises";
36351
36552
  function registerVideoExtendCommand(parent) {
36352
- 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) => {
36553
+ 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) => {
36353
36554
  const startedAt = Date.now();
36354
36555
  try {
36355
36556
  const provider = (options.provider || "kling").toLowerCase();
@@ -36467,12 +36668,7 @@ function registerVideoExtendCommand(parent) {
36467
36668
  const spinner2 = ora("Initializing Veo...").start();
36468
36669
  const gemini = new GeminiProvider();
36469
36670
  await gemini.initialize({ apiKey });
36470
- const veoModelMap = {
36471
- "3.0": "veo-3.0-generate-preview",
36472
- "3.1": "veo-3.1-generate-preview",
36473
- "3.1-fast": "veo-3.1-fast-generate-preview"
36474
- };
36475
- const veoModel = veoModelMap[options.veoModel] || "veo-3.1-generate-preview";
36671
+ const veoModel = resolveVeoModel(options.veoModel);
36476
36672
  spinner2.text = "Starting video extension...";
36477
36673
  const result = await gemini.extendVideo(id, options.prompt, {
36478
36674
  duration: parseInt(options.duration),
@@ -36633,10 +36829,15 @@ var init_provider_resolver = __esm({
36633
36829
 
36634
36830
  // src/commands/_shared/openai-image.ts
36635
36831
  function resolveOpenAIImageModel(modelAlias) {
36636
- const isGptImage15 = modelAlias === "1.5" || modelAlias === "gpt-image-1.5";
36832
+ const alias = modelAlias?.trim() ?? "";
36833
+ for (const [id, model] of Object.entries(OPENAI_IMAGE_MODELS)) {
36834
+ if (alias === id || model.aliases.includes(alias)) {
36835
+ return { openaiModel: id, modelLabel: model.label };
36836
+ }
36837
+ }
36637
36838
  return {
36638
- openaiModel: isGptImage15 ? "gpt-image-1.5" : "gpt-image-2",
36639
- modelLabel: isGptImage15 ? "GPT Image 1.5" : "GPT Image 2"
36839
+ openaiModel: OPENAI_IMAGE_DEFAULT_MODEL,
36840
+ modelLabel: OPENAI_IMAGE_MODELS[OPENAI_IMAGE_DEFAULT_MODEL].label
36640
36841
  };
36641
36842
  }
36642
36843
  async function executeOpenAIImageGenerate(prompt3, options, ctx) {
@@ -36652,17 +36853,24 @@ async function executeOpenAIImageGenerate(prompt3, options, ctx) {
36652
36853
  });
36653
36854
  return { result, openaiModel, modelLabel };
36654
36855
  }
36856
+ var OPENAI_IMAGE_MODELS;
36655
36857
  var init_openai_image2 = __esm({
36656
36858
  "src/commands/_shared/openai-image.ts"() {
36657
36859
  "use strict";
36658
36860
  init_dist2();
36861
+ OPENAI_IMAGE_MODELS = {
36862
+ "gpt-image-2.5-sunburst": { label: "GPT Image 2.5 Sunburst", aliases: ["2.5", "sunburst"] },
36863
+ "gpt-image-2.5-flare": { label: "GPT Image 2.5 Flare", aliases: ["flare", "2.5-flare"] },
36864
+ "gpt-image-2": { label: "GPT Image 2", aliases: ["2"] },
36865
+ "gpt-image-1.5": { label: "GPT Image 1.5", aliases: ["1.5"] }
36866
+ };
36659
36867
  }
36660
36868
  });
36661
36869
 
36662
36870
  // src/commands/generate/image.ts
36663
- import { resolve as resolve27, dirname as dirname7 } from "node:path";
36871
+ import { resolve as resolve27, dirname as dirname8 } from "node:path";
36664
36872
  import { fileURLToPath } from "node:url";
36665
- import { writeFile as writeFile24, mkdir as mkdir12 } from "node:fs/promises";
36873
+ import { mkdir as mkdir13 } from "node:fs/promises";
36666
36874
  function registerImageCommand(parent) {
36667
36875
  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(
36668
36876
  "-p, --provider <provider>",
@@ -36673,7 +36881,7 @@ function registerImageCommand(parent) {
36673
36881
  "1:1"
36674
36882
  ).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(
36675
36883
  "-m, --model <model>",
36676
- "Model. Gemini: flash, 3.1-flash, latest, pro. OpenAI: 2 (default), 1.5"
36884
+ "Model. Gemini: flash (default), lite, pro. OpenAI: 2.5 (default), flare, 2, 1.5. Grok: pro"
36677
36885
  ).option("--dry-run", "Preview parameters without executing").addHelpText(
36678
36886
  "after",
36679
36887
  `
@@ -36776,7 +36984,7 @@ Examples:
36776
36984
  const apiKey = await requireApiKey(envKey, providerName, options.apiKey);
36777
36985
  const spinner2 = ora(`Generating image with ${providerName}...`).start();
36778
36986
  if (provider === "openai") {
36779
- const { result, modelLabel } = await executeOpenAIImageGenerate(prompt3, options, {
36987
+ const { result, modelLabel, openaiModel } = await executeOpenAIImageGenerate(prompt3, options, {
36780
36988
  apiKey
36781
36989
  });
36782
36990
  if (!result.success || !result.images) {
@@ -36799,14 +37007,16 @@ Examples:
36799
37007
  } else {
36800
37008
  throw new Error("No image data available");
36801
37009
  }
36802
- await mkdir12(dirname7(outputPath), { recursive: true });
36803
- await writeFile24(outputPath, buffer);
37010
+ await mkdir13(dirname8(outputPath), { recursive: true });
37011
+ await writeImageFile(outputPath, buffer);
36804
37012
  }
36805
37013
  outputSuccess({
36806
37014
  command: "generate image",
36807
37015
  startedAt,
36808
37016
  data: {
36809
37017
  provider: "openai",
37018
+ model: modelLabel,
37019
+ modelId: openaiModel,
36810
37020
  images: result.images.map((img) => ({
36811
37021
  url: img.url,
36812
37022
  revisedPrompt: img.revisedPrompt
@@ -36846,8 +37056,8 @@ Examples:
36846
37056
  throw new Error("No image data available");
36847
37057
  }
36848
37058
  const outputPath = resolve27(process.cwd(), options.output);
36849
- await mkdir12(dirname7(outputPath), { recursive: true });
36850
- await writeFile24(outputPath, buffer);
37059
+ await mkdir13(dirname8(outputPath), { recursive: true });
37060
+ await writeImageFile(outputPath, buffer);
36851
37061
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
36852
37062
  } catch (err) {
36853
37063
  saveSpinner.fail(
@@ -36856,7 +37066,7 @@ Examples:
36856
37066
  }
36857
37067
  }
36858
37068
  } else if (provider === "gemini") {
36859
- const validGeminiModels = ["flash", "3.1-flash", "latest", "pro"];
37069
+ const validGeminiModels = ["flash", "3.1-flash", "latest", "lite", "pro"];
36860
37070
  if (options.model && !validGeminiModels.includes(options.model)) {
36861
37071
  console.warn(
36862
37072
  source_default.yellow(
@@ -36889,26 +37099,17 @@ Examples:
36889
37099
  const gemini = new GeminiProvider();
36890
37100
  await gemini.initialize({ apiKey });
36891
37101
  const geminiModelNames = {
36892
- flash: "Nano Banana",
37102
+ flash: "Nano Banana 2",
36893
37103
  "3.1-flash": "Nano Banana 2",
36894
37104
  latest: "Nano Banana 2",
37105
+ lite: "Nano Banana 2 Lite",
36895
37106
  pro: "Nano Banana Pro"
36896
37107
  };
36897
- const modelLabel = geminiModelNames[options.model] || "Nano Banana";
36898
- let result = await gemini.generateImage(prompt3, {
37108
+ const usedLabel = geminiModelNames[options.model] || "Nano Banana 2";
37109
+ const result = await gemini.generateImage(prompt3, {
36899
37110
  model: options.model,
36900
37111
  aspectRatio: options.ratio
36901
37112
  });
36902
- let usedLabel = modelLabel;
36903
- const fallbackModels = ["latest", "3.1-flash"];
36904
- if (!result.success && options.model && fallbackModels.includes(options.model)) {
36905
- spinner2.text = `${source_default.dim(result.error || "Failed")} \u2014 retrying with Nano Banana (flash)...`;
36906
- result = await gemini.generateImage(prompt3, {
36907
- model: "flash",
36908
- aspectRatio: options.ratio
36909
- });
36910
- usedLabel = "Nano Banana (fallback)";
36911
- }
36912
37113
  if (!result.success || !result.images) {
36913
37114
  spinner2.fail(result.error || "Image generation failed");
36914
37115
  exitWithError(apiError(result.error || "Image generation failed", true));
@@ -36921,16 +37122,17 @@ Examples:
36921
37122
  if (outputPath && result.images.length > 0) {
36922
37123
  const img = result.images[0];
36923
37124
  const buffer = Buffer.from(img.base64, "base64");
36924
- await mkdir12(dirname7(outputPath), { recursive: true });
36925
- await writeFile24(outputPath, buffer);
37125
+ await mkdir13(dirname8(outputPath), { recursive: true });
37126
+ await writeImageFile(outputPath, buffer);
36926
37127
  }
36927
37128
  outputSuccess({
36928
37129
  command: "generate image",
36929
37130
  startedAt,
36930
- warnings: usedLabel.includes("fallback") ? [`Model "${options.model}" failed; fell back to flash`] : [],
37131
+ warnings: [],
36931
37132
  data: {
36932
37133
  provider: "gemini",
36933
37134
  model: usedLabel,
37135
+ modelId: result.model,
36934
37136
  images: result.images.map((img) => ({
36935
37137
  mimeType: img.mimeType
36936
37138
  })),
@@ -36954,8 +37156,8 @@ Examples:
36954
37156
  const img = result.images[0];
36955
37157
  const buffer = Buffer.from(img.base64, "base64");
36956
37158
  const outputPath = resolve27(process.cwd(), options.output);
36957
- await mkdir12(dirname7(outputPath), { recursive: true });
36958
- await writeFile24(outputPath, buffer);
37159
+ await mkdir13(dirname8(outputPath), { recursive: true });
37160
+ await writeImageFile(outputPath, buffer);
36959
37161
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
36960
37162
  } catch (err) {
36961
37163
  saveSpinner.fail(
@@ -36993,6 +37195,7 @@ Examples:
36993
37195
  options.ratio = "1:1";
36994
37196
  }
36995
37197
  const result = await grok.generateImage(prompt3, {
37198
+ ...resolveGrokImageModel(options.model),
36996
37199
  aspectRatio: options.ratio || "1:1",
36997
37200
  n: parseInt(options.count)
36998
37201
  });
@@ -37014,14 +37217,15 @@ Examples:
37014
37217
  } else {
37015
37218
  throw new Error("No image data available");
37016
37219
  }
37017
- await mkdir12(dirname7(outputPath), { recursive: true });
37018
- await writeFile24(outputPath, buffer);
37220
+ await mkdir13(dirname8(outputPath), { recursive: true });
37221
+ await writeImageFile(outputPath, buffer);
37019
37222
  }
37020
37223
  outputSuccess({
37021
37224
  command: "generate image",
37022
37225
  startedAt,
37023
37226
  data: {
37024
37227
  provider: "grok",
37228
+ modelId: resolveGrokImageModel(options.model).model,
37025
37229
  images: result.images.map((img) => ({ url: img.url })),
37026
37230
  outputPath
37027
37231
  }
@@ -37055,8 +37259,8 @@ Examples:
37055
37259
  throw new Error("No image data available");
37056
37260
  }
37057
37261
  const outputPath = resolve27(process.cwd(), options.output);
37058
- await mkdir12(dirname7(outputPath), { recursive: true });
37059
- await writeFile24(outputPath, buffer);
37262
+ await mkdir13(dirname8(outputPath), { recursive: true });
37263
+ await writeImageFile(outputPath, buffer);
37060
37264
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
37061
37265
  } catch (err) {
37062
37266
  saveSpinner.fail(
@@ -37067,7 +37271,7 @@ Examples:
37067
37271
  } else if (provider === "runway") {
37068
37272
  const { spawn: spawn4 } = await import("child_process");
37069
37273
  const __filename2 = fileURLToPath(import.meta.url);
37070
- const __dirname2 = dirname7(__filename2);
37274
+ const __dirname2 = dirname8(__filename2);
37071
37275
  const scriptPath = resolve27(
37072
37276
  __dirname2,
37073
37277
  "../../../../.claude/skills/runway-video/scripts/image.py"
@@ -37138,6 +37342,7 @@ var init_image = __esm({
37138
37342
  init_validate();
37139
37343
  init_provider_resolver();
37140
37344
  init_openai_image2();
37345
+ init_image_file();
37141
37346
  }
37142
37347
  });
37143
37348
 
@@ -38086,11 +38291,11 @@ var init_dist3 = __esm({
38086
38291
 
38087
38292
  // src/commands/generate/video.ts
38088
38293
  import { resolve as resolve28 } from "node:path";
38089
- import { readFile as readFile18, writeFile as writeFile25 } from "node:fs/promises";
38294
+ import { readFile as readFile18, writeFile as writeFile24 } from "node:fs/promises";
38090
38295
  function registerVideoCommand(parent) {
38091
38296
  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(
38092
38297
  "-p, --provider <provider>",
38093
- "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."
38298
+ "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."
38094
38299
  ).option(
38095
38300
  "-k, --api-key <key>",
38096
38301
  "API key (or set FAL_API_KEY / XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY / GOOGLE_API_KEY env)"
@@ -38103,12 +38308,12 @@ function registerVideoCommand(parent) {
38103
38308
  "Aspect ratio: 16:9, 9:16, or 1:1 (auto-detected from image if omitted)"
38104
38309
  ).option("--seed <number>", "Random seed for reproducibility (Runway only)").option("--mode <mode>", "Generation mode: std or pro (Kling only)", "std").option(
38105
38310
  "--seedance-model <model>",
38106
- "Seedance variant: quality or fast (fal.ai only)",
38311
+ "Seedance variant: quality (2.0), fast, or 2.5 (fal.ai only)",
38107
38312
  "quality"
38108
- ).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(
38313
+ ).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(
38109
38314
  "--ref-images <paths...>",
38110
38315
  "Reference images for Seedance reference-to-video or Veo character consistency"
38111
- ).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(
38316
+ ).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(
38112
38317
  "--runway-model <model>",
38113
38318
  "Runway model: gen4.5 (default, text+image-to-video), gen4_turbo (image-to-video only)",
38114
38319
  "gen4.5"
@@ -38169,7 +38374,7 @@ Examples:
38169
38374
  exitWithError(
38170
38375
  usageError(
38171
38376
  `Invalid provider: ${provider}`,
38172
- "Available providers: seedance, grok, kling, runway, veo, omni (experimental). `fal` is a deprecated alias for seedance."
38377
+ "Available providers: seedance, grok, kling, runway, omni, veo. `fal` is a deprecated alias for seedance."
38173
38378
  )
38174
38379
  );
38175
38380
  }
@@ -38237,13 +38442,7 @@ Examples:
38237
38442
  options.ratio = "16:9";
38238
38443
  }
38239
38444
  if (options.dryRun) {
38240
- const costUsd = provider === "seedance" || provider === "fal" ? estimateSeedanceVideoCostUsd({
38241
- durationSec: Number(options.duration) || 5,
38242
- resolution: options.resolution,
38243
- aspectRatio: options.ratio,
38244
- fast: options.seedanceModel === "fast",
38245
- hasVideoReference: Boolean(options.refVideos)
38246
- }) : void 0;
38445
+ const costUsd = provider === "seedance" || provider === "fal" ? realRunCost(provider, options).costUsd : void 0;
38247
38446
  outputSuccess({
38248
38447
  command: "generate video",
38249
38448
  startedAt,
@@ -38261,6 +38460,8 @@ Examples:
38261
38460
  resolution: options.resolution,
38262
38461
  veoModel: options.veoModel,
38263
38462
  seedanceModel: options.seedanceModel,
38463
+ grokModel: options.grokModel,
38464
+ klingModel: options.klingModel,
38264
38465
  refImages: options.refImages,
38265
38466
  refVideos: options.refVideos,
38266
38467
  refAudio: options.refAudio,
@@ -38282,7 +38483,7 @@ Examples:
38282
38483
  runway: "Runway",
38283
38484
  kling: "Kling",
38284
38485
  veo: "Veo",
38285
- omni: "Gemini Omni (experimental)",
38486
+ omni: "Gemini Omni 1.1 Flash",
38286
38487
  grok: "Grok",
38287
38488
  seedance: "Seedance 2.0 via fal.ai"
38288
38489
  };
@@ -38381,6 +38582,7 @@ Examples:
38381
38582
  }
38382
38583
  result = await kling.generateVideo(prompt3, {
38383
38584
  prompt: prompt3,
38585
+ model: resolveKlingModel(options.klingModel),
38384
38586
  referenceImage: klingImage,
38385
38587
  duration: parseInt(options.duration),
38386
38588
  aspectRatio: options.ratio,
@@ -38430,12 +38632,7 @@ Examples:
38430
38632
  } else if (provider === "veo") {
38431
38633
  const gemini = new GeminiProvider();
38432
38634
  await gemini.initialize({ apiKey });
38433
- const veoModelMap = {
38434
- "3.0": "veo-3.0-generate-preview",
38435
- "3.1": "veo-3.1-generate-preview",
38436
- "3.1-fast": "veo-3.1-fast-generate-preview"
38437
- };
38438
- const veoModel = veoModelMap[options.veoModel] || "veo-3.1-fast-generate-preview";
38635
+ const veoModel = resolveVeoModel(options.veoModel);
38439
38636
  const veoDuration = parseInt(options.duration) <= 6 ? 6 : 8;
38440
38637
  let lastFrame;
38441
38638
  if (options.lastFrame) {
@@ -38509,9 +38706,12 @@ Examples:
38509
38706
  await grok.initialize({ apiKey });
38510
38707
  result = await grok.generateVideo(prompt3, {
38511
38708
  prompt: prompt3,
38709
+ model: resolveGrokVideoModel(options.grokModel),
38512
38710
  referenceImage,
38513
38711
  duration: parseInt(options.duration),
38514
- aspectRatio: options.ratio
38712
+ aspectRatio: options.ratio,
38713
+ resolution: options.resolution,
38714
+ generateAudio: options.generateAudio
38515
38715
  });
38516
38716
  if (result.status === "failed") {
38517
38717
  spinner2.fail(result.error || "Failed to start generation");
@@ -38601,9 +38801,8 @@ Examples:
38601
38801
  exitWithError(apiError(message, true));
38602
38802
  }
38603
38803
  }
38604
- spinner2.text = "Generating video with fal.ai Seedance 2.0 (this may take 1-3 minutes)...";
38605
- const seedanceModel = String(options.seedanceModel ?? "quality").toLowerCase();
38606
- const falModel = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
38804
+ const falModel = resolveSeedanceVariant(options.seedanceModel);
38805
+ spinner2.text = `Generating video with fal.ai ${falModel} (this may take 1-3 minutes)...`;
38607
38806
  result = await fal.generateVideo(prompt3, {
38608
38807
  prompt: prompt3,
38609
38808
  referenceImage: seedanceReferences.length > 0 ? void 0 : falImage,
@@ -38620,11 +38819,20 @@ Examples:
38620
38819
  } else if (provider === "omni") {
38621
38820
  const omni = new OmniProvider();
38622
38821
  await omni.initialize({ apiKey });
38623
- spinner2.text = "Generating video with Gemini Omni (experimental)...";
38822
+ spinner2.text = "Generating video with Gemini Omni 1.1 Flash...";
38823
+ let lastFrame;
38824
+ if (options.lastFrame) {
38825
+ const lastFramePath = resolve28(process.cwd(), options.lastFrame);
38826
+ const ext = options.lastFrame.toLowerCase().split(".").pop();
38827
+ const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
38828
+ lastFrame = `data:${mimeType};base64,${(await readFile18(lastFramePath)).toString("base64")}`;
38829
+ }
38624
38830
  result = await omni.generateVideo(prompt3, {
38625
38831
  prompt: prompt3,
38626
38832
  referenceImage,
38627
- aspectRatio: options.ratio
38833
+ lastFrame,
38834
+ aspectRatio: options.ratio,
38835
+ resolution: options.resolution
38628
38836
  });
38629
38837
  finalResult = result;
38630
38838
  }
@@ -38638,14 +38846,14 @@ Examples:
38638
38846
  if (options.output && finalResult.videoUrl) {
38639
38847
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
38640
38848
  outputPath = resolve28(process.cwd(), options.output);
38641
- await writeFile25(outputPath, buffer);
38849
+ await writeFile24(outputPath, buffer);
38642
38850
  }
38643
38851
  const cost = realRunCost(provider, options);
38644
38852
  outputSuccess({
38645
38853
  command: "generate video",
38646
38854
  startedAt,
38647
38855
  costUsd: cost.costUsd,
38648
- warnings: cost.warnings,
38856
+ warnings: provider === "veo" ? [VEO_SHUTDOWN_WARNING, ...cost.warnings] : cost.warnings,
38649
38857
  data: {
38650
38858
  provider,
38651
38859
  taskId: result?.id,
@@ -38657,6 +38865,9 @@ Examples:
38657
38865
  return;
38658
38866
  }
38659
38867
  console.log();
38868
+ if (provider === "veo") {
38869
+ console.log(source_default.yellow(VEO_SHUTDOWN_WARNING));
38870
+ }
38660
38871
  if (finalResult.videoUrl) {
38661
38872
  console.log(`Video URL: ${finalResult.videoUrl}`);
38662
38873
  }
@@ -38669,7 +38880,7 @@ Examples:
38669
38880
  try {
38670
38881
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
38671
38882
  const outputPath = resolve28(process.cwd(), options.output);
38672
- await writeFile25(outputPath, buffer);
38883
+ await writeFile24(outputPath, buffer);
38673
38884
  downloadSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
38674
38885
  } catch (err) {
38675
38886
  downloadSpinner.fail(
@@ -38684,12 +38895,14 @@ Examples:
38684
38895
  }
38685
38896
  function realRunCost(provider, options) {
38686
38897
  if (provider === "seedance" || provider === "fal") {
38898
+ const variant = resolveSeedanceVariant(options.seedanceModel);
38687
38899
  return {
38688
38900
  costUsd: estimateSeedanceVideoCostUsd({
38689
38901
  durationSec: Number(options.duration) || 5,
38690
38902
  resolution: options.resolution,
38691
38903
  aspectRatio: options.ratio,
38692
- fast: options.seedanceModel === "fast",
38904
+ fast: variant === "seedance-2.0-fast",
38905
+ v25: variant === "seedance-2.5",
38693
38906
  hasVideoReference: Boolean(options.refVideos)
38694
38907
  }),
38695
38908
  warnings: []
@@ -38779,6 +38992,7 @@ function printVideoStarted(providerLabel, taskId, taskType) {
38779
38992
  console.log(`Task ID: ${source_default.bold(taskId)}`);
38780
38993
  if (taskType) console.log(`Type: ${taskType}`);
38781
38994
  }
38995
+ var VEO_SHUTDOWN_WARNING;
38782
38996
  var init_video = __esm({
38783
38997
  "src/commands/generate/video.ts"() {
38784
38998
  "use strict";
@@ -38794,6 +39008,7 @@ var init_video = __esm({
38794
39008
  init_upload_host();
38795
39009
  init_ai_helpers();
38796
39010
  init_status_jobs();
39011
+ VEO_SHUTDOWN_WARNING = "Veo 3.1 preview models shut down on 2026-10-22. Use `-p omni` (Gemini Omni 1.1 Flash) instead.";
38797
39012
  }
38798
39013
  });
38799
39014
 
@@ -38813,7 +39028,7 @@ var init_sanitize = __esm({
38813
39028
 
38814
39029
  // src/commands/generate/storyboard.ts
38815
39030
  import { resolve as resolve29 } from "node:path";
38816
- import { writeFile as writeFile26 } from "node:fs/promises";
39031
+ import { writeFile as writeFile25 } from "node:fs/promises";
38817
39032
  async function executeStoryboard(options) {
38818
39033
  const { content, duration, creativity = "low", output, apiKey } = options;
38819
39034
  try {
@@ -38832,7 +39047,7 @@ async function executeStoryboard(options) {
38832
39047
  let outputPath;
38833
39048
  if (output) {
38834
39049
  outputPath = resolve29(process.cwd(), output);
38835
- await writeFile26(outputPath, JSON.stringify(segments, null, 2), "utf-8");
39050
+ await writeFile25(outputPath, JSON.stringify(segments, null, 2), "utf-8");
38836
39051
  }
38837
39052
  return { success: true, segments, segmentCount: segments.length, outputPath };
38838
39053
  } catch (error) {
@@ -39175,7 +39390,7 @@ var init_silence_cut = __esm({
39175
39390
 
39176
39391
  // src/commands/_shared/edit/jump-cut.ts
39177
39392
  import { existsSync as existsSync20 } from "node:fs";
39178
- import { readFile as readFile20, writeFile as writeFile27, mkdir as mkdir13 } from "node:fs/promises";
39393
+ import { readFile as readFile20, writeFile as writeFile26, mkdir as mkdir14 } from "node:fs/promises";
39179
39394
  import { join as join13 } from "node:path";
39180
39395
  async function transcribeWithWords(audioPath, apiKey, language) {
39181
39396
  const audioBuffer = await readFile20(audioPath);
@@ -39255,7 +39470,7 @@ async function executeJumpCut(options) {
39255
39470
  }
39256
39471
  try {
39257
39472
  const tmpDir = `/tmp/vibe_jumpcut_${Date.now()}`;
39258
- await mkdir13(tmpDir, { recursive: true });
39473
+ await mkdir14(tmpDir, { recursive: true });
39259
39474
  const audioPath = join13(tmpDir, "audio.wav");
39260
39475
  try {
39261
39476
  await execSafe(
@@ -39334,7 +39549,7 @@ async function executeJumpCut(options) {
39334
39549
  }
39335
39550
  const concatList = segmentPaths.map((p) => `file '${p}'`).join("\n");
39336
39551
  const listPath = join13(tmpDir, "concat.txt");
39337
- await writeFile27(listPath, concatList);
39552
+ await writeFile26(listPath, concatList);
39338
39553
  await execSafe(
39339
39554
  "ffmpeg",
39340
39555
  [
@@ -39361,8 +39576,8 @@ async function executeJumpCut(options) {
39361
39576
  };
39362
39577
  } finally {
39363
39578
  try {
39364
- const { rm: rm7 } = await import("node:fs/promises");
39365
- await rm7(tmpDir, { recursive: true, force: true });
39579
+ const { rm: rm8 } = await import("node:fs/promises");
39580
+ await rm8(tmpDir, { recursive: true, force: true });
39366
39581
  } catch {
39367
39582
  }
39368
39583
  }
@@ -39524,8 +39739,8 @@ var init_helpers = __esm({
39524
39739
 
39525
39740
  // src/commands/_shared/edit/caption.ts
39526
39741
  import { existsSync as existsSync21 } from "node:fs";
39527
- import { readFile as readFile21, writeFile as writeFile28, mkdir as mkdir14 } from "node:fs/promises";
39528
- import { dirname as dirname8, basename as basename6, extname as extname4, join as join14 } from "node:path";
39742
+ import { readFile as readFile21, writeFile as writeFile27, mkdir as mkdir15 } from "node:fs/promises";
39743
+ import { dirname as dirname9, basename as basename6, extname as extname5, join as join14 } from "node:path";
39529
39744
  function getCaptionForceStyle(style, fontSize, fontColor, position) {
39530
39745
  const alignment = position === "top" ? 8 : position === "center" ? 5 : 2;
39531
39746
  const marginV = position === "center" ? 0 : 30;
@@ -39571,7 +39786,7 @@ async function executeCaption(options) {
39571
39786
  }
39572
39787
  try {
39573
39788
  const tmpDir = `/tmp/vibe_caption_${Date.now()}`;
39574
- await mkdir14(tmpDir, { recursive: true });
39789
+ await mkdir15(tmpDir, { recursive: true });
39575
39790
  const audioPath = join14(tmpDir, "audio.wav");
39576
39791
  const srtPath = join14(tmpDir, "captions.srt");
39577
39792
  try {
@@ -39604,7 +39819,7 @@ async function executeCaption(options) {
39604
39819
  };
39605
39820
  }
39606
39821
  const srtContent = formatSRT(transcriptResult.segments);
39607
- await writeFile28(srtPath, srtContent);
39822
+ await writeFile27(srtPath, srtContent);
39608
39823
  const { width, height } = await getVideoResolution(videoPath);
39609
39824
  const fontSize = customFontSize || Math.round(height / 18);
39610
39825
  let hasSubtitles = false;
@@ -39641,12 +39856,12 @@ async function executeCaption(options) {
39641
39856
  } = await Promise.resolve().then(() => (init_remotion(), remotion_exports));
39642
39857
  const remotionErr = await ensureRemotionInstalled2();
39643
39858
  if (remotionErr) {
39644
- const outputDir2 = dirname8(outputPath);
39859
+ const outputDir2 = dirname9(outputPath);
39645
39860
  const outputSrtPath2 = join14(
39646
39861
  outputDir2,
39647
- basename6(outputPath, extname4(outputPath)) + ".srt"
39862
+ basename6(outputPath, extname5(outputPath)) + ".srt"
39648
39863
  );
39649
- await writeFile28(outputSrtPath2, srtContent);
39864
+ await writeFile27(outputSrtPath2, srtContent);
39650
39865
  return { success: false, error: `${remotionErr}
39651
39866
  SRT saved to: ${outputSrtPath2}` };
39652
39867
  }
@@ -39680,12 +39895,12 @@ SRT saved to: ${outputSrtPath2}` };
39680
39895
  outputPath
39681
39896
  });
39682
39897
  if (!renderResult.success) {
39683
- const outputDir2 = dirname8(outputPath);
39898
+ const outputDir2 = dirname9(outputPath);
39684
39899
  const outputSrtPath2 = join14(
39685
39900
  outputDir2,
39686
- basename6(outputPath, extname4(outputPath)) + ".srt"
39901
+ basename6(outputPath, extname5(outputPath)) + ".srt"
39687
39902
  );
39688
- await writeFile28(outputSrtPath2, srtContent);
39903
+ await writeFile27(outputSrtPath2, srtContent);
39689
39904
  return {
39690
39905
  success: false,
39691
39906
  error: `${renderResult.error}
@@ -39693,12 +39908,12 @@ SRT saved to: ${outputSrtPath2}`
39693
39908
  };
39694
39909
  }
39695
39910
  }
39696
- const outputDir = dirname8(outputPath);
39911
+ const outputDir = dirname9(outputPath);
39697
39912
  const outputSrtPath = join14(
39698
39913
  outputDir,
39699
- basename6(outputPath, extname4(outputPath)) + ".srt"
39914
+ basename6(outputPath, extname5(outputPath)) + ".srt"
39700
39915
  );
39701
- await writeFile28(outputSrtPath, srtContent);
39916
+ await writeFile27(outputSrtPath, srtContent);
39702
39917
  return {
39703
39918
  success: true,
39704
39919
  outputPath,
@@ -39707,8 +39922,8 @@ SRT saved to: ${outputSrtPath2}`
39707
39922
  };
39708
39923
  } finally {
39709
39924
  try {
39710
- const { rm: rm7 } = await import("node:fs/promises");
39711
- await rm7(tmpDir, { recursive: true, force: true });
39925
+ const { rm: rm8 } = await import("node:fs/promises");
39926
+ await rm8(tmpDir, { recursive: true, force: true });
39712
39927
  } catch {
39713
39928
  }
39714
39929
  }
@@ -39869,7 +40084,7 @@ var init_fade = __esm({
39869
40084
 
39870
40085
  // src/commands/_shared/edit/translate-srt.ts
39871
40086
  import { existsSync as existsSync24 } from "node:fs";
39872
- import { readFile as readFile22, writeFile as writeFile29 } from "node:fs/promises";
40087
+ import { readFile as readFile22, writeFile as writeFile28 } from "node:fs/promises";
39873
40088
  async function executeTranslateSrt(options) {
39874
40089
  const {
39875
40090
  srtPath,
@@ -39912,15 +40127,14 @@ ${textsToTranslate}`;
39912
40127
  Authorization: `Bearer ${openaiKey}`
39913
40128
  },
39914
40129
  body: JSON.stringify({
39915
- model: "gpt-5-mini",
39916
- messages: [{ role: "user", content: translatePrompt }],
39917
- temperature: 0.3
40130
+ model: "gpt-5.4-mini",
40131
+ messages: [{ role: "user", content: translatePrompt }]
39918
40132
  })
39919
40133
  });
39920
40134
  if (!response.ok) {
39921
40135
  return {
39922
40136
  success: false,
39923
- error: `OpenAI API error: ${response.status} ${response.statusText}`
40137
+ error: `OpenAI API error: ${response.status} ${response.statusText}: ${await response.text()}`
39924
40138
  };
39925
40139
  }
39926
40140
  const data = await response.json();
@@ -39941,15 +40155,15 @@ ${textsToTranslate}`;
39941
40155
  "anthropic-version": "2023-06-01"
39942
40156
  },
39943
40157
  body: JSON.stringify({
39944
- model: "claude-sonnet-4-6-20250514",
39945
- max_tokens: 4096,
40158
+ model: "claude-sonnet-5-5",
40159
+ max_tokens: 16e3,
39946
40160
  messages: [{ role: "user", content: translatePrompt }]
39947
40161
  })
39948
40162
  });
39949
40163
  if (!response.ok) {
39950
40164
  return {
39951
40165
  success: false,
39952
- error: `Claude API error: ${response.status} ${response.statusText}`
40166
+ error: `Claude API error: ${response.status} ${response.statusText}: ${await response.text()}`
39953
40167
  };
39954
40168
  }
39955
40169
  const data = await response.json();
@@ -39973,7 +40187,7 @@ ${textsToTranslate}`;
39973
40187
  }
39974
40188
  }
39975
40189
  const translatedSrt = formatSRT(translatedSegments);
39976
- await writeFile29(outputPath, translatedSrt);
40190
+ await writeFile28(outputPath, translatedSrt);
39977
40191
  return {
39978
40192
  success: true,
39979
40193
  outputPath,
@@ -40225,7 +40439,7 @@ var init_ai_edit = __esm({
40225
40439
  });
40226
40440
 
40227
40441
  // src/commands/ai-edit-cli.ts
40228
- import { resolve as resolve31, extname as extname5, basename as basename7 } from "node:path";
40442
+ import { resolve as resolve31, extname as extname6, basename as basename7 } from "node:path";
40229
40443
  import { existsSync as existsSync26 } from "node:fs";
40230
40444
  function registerEditCommands(aiCommand) {
40231
40445
  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", `
@@ -40249,7 +40463,7 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
40249
40463
  if (!commandExists("ffmpeg")) {
40250
40464
  exitWithError(generalError("FFmpeg not found. Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux). Run `vibe doctor` for details."));
40251
40465
  }
40252
- const ext = extname5(videoPath);
40466
+ const ext = extname6(videoPath);
40253
40467
  const name = basename7(videoPath, ext);
40254
40468
  const outputPath = options.output || `${name}-cut${ext}`;
40255
40469
  const useGemini = options.useGemini || false;
@@ -40379,7 +40593,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
40379
40593
  if (!apiKey) {
40380
40594
  exitWithError(authError("OPENAI_API_KEY", "OpenAI"));
40381
40595
  }
40382
- const ext = extname5(videoPath);
40596
+ const ext = extname6(videoPath);
40383
40597
  const name = basename7(videoPath, ext);
40384
40598
  const outputPath = options.output || `${name}-captioned${ext}`;
40385
40599
  const spinner2 = ora("Starting caption process...").start();
@@ -40453,7 +40667,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
40453
40667
  });
40454
40668
  return;
40455
40669
  }
40456
- const ext = extname5(inputPath);
40670
+ const ext = extname6(inputPath);
40457
40671
  const name = basename7(inputPath, ext);
40458
40672
  const outputPath = options.output || `${name}-denoised${ext}`;
40459
40673
  const spinner2 = ora("Applying noise reduction...").start();
@@ -40521,7 +40735,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
40521
40735
  });
40522
40736
  return;
40523
40737
  }
40524
- const ext = extname5(videoPath);
40738
+ const ext = extname6(videoPath);
40525
40739
  const name = basename7(videoPath, ext);
40526
40740
  const outputPath = options.output || `${name}-faded${ext}`;
40527
40741
  const spinner2 = ora("Applying fade effects...").start();
@@ -40599,7 +40813,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
40599
40813
  if (!apiKey) {
40600
40814
  exitWithError(authError(envKey, providerName));
40601
40815
  }
40602
- const ext = extname5(srtPath);
40816
+ const ext = extname6(srtPath);
40603
40817
  const name = basename7(srtPath, ext);
40604
40818
  const outputPath = options.output || `${name}-${options.target}${ext}`;
40605
40819
  const spinner2 = ora(`Translating to ${options.target}...`).start();
@@ -40677,7 +40891,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
40677
40891
  if (!apiKey) {
40678
40892
  exitWithError(authError("OPENAI_API_KEY", "OpenAI"));
40679
40893
  }
40680
- const ext = extname5(videoPath);
40894
+ const ext = extname6(videoPath);
40681
40895
  const name = basename7(videoPath, ext);
40682
40896
  const outputPath = options.output || `${name}-jumpcut${ext}`;
40683
40897
  const fillers = options.fillers ? options.fillers.split(",").map((f) => f.trim()) : void 0;
@@ -40750,8 +40964,8 @@ var init_ai_edit_cli = __esm({
40750
40964
 
40751
40965
  // src/commands/_shared/execute-fill-gaps.ts
40752
40966
  import { existsSync as existsSync27 } from "node:fs";
40753
- import { readFile as readFile23, writeFile as writeFile30, mkdir as mkdir15, rename as renameFs } from "node:fs/promises";
40754
- import { resolve as resolve32, dirname as dirname9 } from "node:path";
40967
+ import { readFile as readFile23, writeFile as writeFile29, mkdir as mkdir16, rename as renameFs } from "node:fs/promises";
40968
+ import { resolve as resolve32, dirname as dirname10 } from "node:path";
40755
40969
  function detectVideoGaps(videoClips, totalDuration) {
40756
40970
  const gaps = [];
40757
40971
  const sortedClips = [...videoClips].sort((a, b) => a.startTime - b.startTime);
@@ -40961,14 +41175,14 @@ async function executeFillGaps(options) {
40961
41175
  if (!kling.isConfigured()) {
40962
41176
  return {
40963
41177
  success: false,
40964
- error: "Invalid KLING_API_KEY (expected ACCESS_KEY:SECRET_KEY format)",
41178
+ error: "Invalid KLING_API_KEY (set your Kling API key, or the legacy ACCESS_KEY:SECRET_KEY pair)",
40965
41179
  humanLines
40966
41180
  };
40967
41181
  }
40968
- const projectDir = dirname9(filePath);
41182
+ const projectDir = dirname10(filePath);
40969
41183
  const footageDir = options.dir ? resolve32(process.cwd(), options.dir) : resolve32(projectDir, "footage");
40970
41184
  if (!existsSync27(footageDir)) {
40971
- await mkdir15(footageDir, { recursive: true });
41185
+ await mkdir16(footageDir, { recursive: true });
40972
41186
  }
40973
41187
  humanLines.push("Generating AI Videos");
40974
41188
  const imgbbApiKey = options.imgbbApiKey || await getApiKey("IMGBB_API_KEY", "imgbb", void 0);
@@ -41033,13 +41247,13 @@ async function executeFillGaps(options) {
41033
41247
  let generatedDuration = 0;
41034
41248
  const generatedVideos = [];
41035
41249
  const initialDuration = Math.min(10, targetDuration);
41036
- const klingDuration = initialDuration > 5 ? "10" : "5";
41037
- onProgress(`Generating ${klingDuration}s video with Kling...`);
41250
+ const klingDuration2 = initialDuration > 5 ? "10" : "5";
41251
+ onProgress(`Generating ${klingDuration2}s video with Kling...`);
41038
41252
  const prompt3 = options.prompt || "Continue the scene naturally with subtle motion";
41039
41253
  const result = await kling.generateVideo(prompt3, {
41040
41254
  prompt: prompt3,
41041
41255
  referenceImage: frameUrl,
41042
- duration: parseInt(klingDuration),
41256
+ duration: parseInt(klingDuration2),
41043
41257
  aspectRatio: options.ratio || "16:9",
41044
41258
  mode: options.mode || "std"
41045
41259
  });
@@ -41062,8 +41276,8 @@ async function executeFillGaps(options) {
41062
41276
  const videoPath = resolve32(footageDir, videoFileName);
41063
41277
  onProgress("Downloading generated video...");
41064
41278
  const videoBuffer = await downloadVideo(finalResult.videoUrl);
41065
- await writeFile30(videoPath, videoBuffer);
41066
- generatedDuration = finalResult.duration || parseInt(klingDuration);
41279
+ await writeFile29(videoPath, videoBuffer);
41280
+ generatedDuration = finalResult.duration || parseInt(klingDuration2);
41067
41281
  generatedVideos.push(videoPath);
41068
41282
  humanLines.push(` Generated: ${videoFileName} (${generatedDuration}s)`);
41069
41283
  let segmentIndex = 1;
@@ -41132,11 +41346,11 @@ async function executeFillGaps(options) {
41132
41346
  `gap-fill-${gap.start.toFixed(2)}-${gap.end.toFixed(2)}-seg${segmentIndex}.mp4`
41133
41347
  );
41134
41348
  const segVideoBuffer = await downloadVideo(segFinalResult.videoUrl);
41135
- await writeFile30(segVideoPath, segVideoBuffer);
41349
+ await writeFile29(segVideoPath, segVideoBuffer);
41136
41350
  const concatListPath = resolve32(footageDir, `concat-${gap.start.toFixed(2)}.txt`);
41137
41351
  const concatList = generatedVideos.map((v) => `file '${v}'`).join("\n") + `
41138
41352
  file '${segVideoPath}'`;
41139
- await writeFile30(concatListPath, concatList);
41353
+ await writeFile29(concatListPath, concatList);
41140
41354
  const concatOutputPath = resolve32(
41141
41355
  footageDir,
41142
41356
  `gap-fill-${gap.start.toFixed(2)}-${gap.end.toFixed(2)}-merged.mp4`
@@ -41194,7 +41408,7 @@ file '${segVideoPath}'`;
41194
41408
  let outputPath = filePath;
41195
41409
  if (generatedCount > 0) {
41196
41410
  outputPath = options.output ? resolve32(process.cwd(), options.output) : filePath;
41197
- await writeFile30(outputPath, JSON.stringify(project.toJSON(), null, 2));
41411
+ await writeFile29(outputPath, JSON.stringify(project.toJSON(), null, 2));
41198
41412
  humanLines.push(`\u2714 Filled ${generatedCount} gap(s) with AI-generated video`);
41199
41413
  humanLines.push(`Project saved: ${outputPath}`);
41200
41414
  } else {
@@ -41571,7 +41785,7 @@ __export(project_builder_exports, {
41571
41785
  buildTempProject: () => buildTempProject,
41572
41786
  resolveSourceUrl: () => resolveSourceUrl
41573
41787
  });
41574
- import { mkdtemp, mkdir as mkdir16, copyFile as copyFile4, writeFile as writeFile31, rm as rm3, readdir as readdir4 } from "node:fs/promises";
41788
+ import { mkdtemp, mkdir as mkdir17, copyFile as copyFile4, writeFile as writeFile30, rm as rm4, readdir as readdir4 } from "node:fs/promises";
41575
41789
  import { existsSync as existsSync29 } from "node:fs";
41576
41790
  import { tmpdir as tmpdir2 } from "node:os";
41577
41791
  import { createRequire } from "node:module";
@@ -41579,7 +41793,7 @@ import * as path from "node:path";
41579
41793
  async function buildTempProject(state, projectFileDir) {
41580
41794
  const dir = await mkdtemp(path.join(tmpdir2(), "vibeframe-hf-"));
41581
41795
  const assetsDir = path.join(dir, "assets");
41582
- await mkdir16(assetsDir, { recursive: true });
41796
+ await mkdir17(assetsDir, { recursive: true });
41583
41797
  const copied = /* @__PURE__ */ new Map();
41584
41798
  for (const source of state.sources) {
41585
41799
  if (copied.has(source.id)) continue;
@@ -41595,10 +41809,10 @@ async function buildTempProject(state, projectFileDir) {
41595
41809
  await copyLottieRuntime(dir);
41596
41810
  }
41597
41811
  const html = generateCompositionHtml(state);
41598
- await writeFile31(path.join(dir, "index.html"), html, "utf-8");
41812
+ await writeFile30(path.join(dir, "index.html"), html, "utf-8");
41599
41813
  return {
41600
41814
  dir,
41601
- cleanup: () => rm3(dir, { recursive: true, force: true })
41815
+ cleanup: () => rm4(dir, { recursive: true, force: true })
41602
41816
  };
41603
41817
  }
41604
41818
  async function copyLottieRuntime(tempDir) {
@@ -41609,7 +41823,7 @@ async function copyLottieRuntime(tempDir) {
41609
41823
  const wasmSrc = path.join(path.dirname(webEntry), "dotlottie-player.wasm");
41610
41824
  const vendorDir = path.join(tempDir, "vendor");
41611
41825
  const wcDest = path.join(vendorDir, "dotlottie-wc");
41612
- await mkdir16(wcDest, { recursive: true });
41826
+ await mkdir17(wcDest, { recursive: true });
41613
41827
  for (const file of await readdir4(wcDistDir)) {
41614
41828
  if (file.endsWith(".map") || file.endsWith(".d.ts")) continue;
41615
41829
  await copyFile4(path.join(wcDistDir, file), path.join(wcDest, file));
@@ -41717,9 +41931,9 @@ __export(motion_overlay_exports, {
41717
41931
  executeMotionOverlay: () => executeMotionOverlay,
41718
41932
  registerMotionOverlayCommand: () => registerMotionOverlayCommand
41719
41933
  });
41720
- import { mkdir as mkdir17 } from "node:fs/promises";
41934
+ import { mkdir as mkdir18 } from "node:fs/promises";
41721
41935
  import { existsSync as existsSync30 } from "node:fs";
41722
- import { basename as basename9, dirname as dirname11, extname as extname6, resolve as resolve34 } from "node:path";
41936
+ import { basename as basename9, dirname as dirname12, extname as extname7, resolve as resolve34 } from "node:path";
41723
41937
  function normalizeUnderstand2(value) {
41724
41938
  return value === "off" || value === "required" || value === "auto" ? value : "auto";
41725
41939
  }
@@ -41742,7 +41956,7 @@ function assertValidRequest(options) {
41742
41956
  return "Provide a motion description, or pass --asset <logo.json|logo.lottie>.";
41743
41957
  }
41744
41958
  if (hasAsset) {
41745
- const ext = extname6(options.asset).toLowerCase();
41959
+ const ext = extname7(options.asset).toLowerCase();
41746
41960
  if (ext !== ".json" && ext !== ".lottie") {
41747
41961
  return "Lottie overlay asset must be a .json or .lottie file.";
41748
41962
  }
@@ -41865,7 +42079,7 @@ async function executeMotionOverlay(options) {
41865
42079
  const scale = Math.max(0.01, Math.min(2, numberOption(options.scale, position === "full" ? 1 : 0.25)));
41866
42080
  const opacity = Math.max(0, Math.min(1, numberOption(options.opacity, 1)));
41867
42081
  const loop = options.loop ?? true;
41868
- await mkdir17(dirname11(outputPath), { recursive: true });
42082
+ await mkdir18(dirname12(outputPath), { recursive: true });
41869
42083
  const backend = createHyperframesBackend();
41870
42084
  const result2 = await backend.render({
41871
42085
  projectState: buildLottieTimelineState({
@@ -41891,7 +42105,7 @@ async function executeMotionOverlay(options) {
41891
42105
  return { success: true, outputPath, provider: "lottie" };
41892
42106
  }
41893
42107
  const duration = options.duration ?? await ffprobeDuration(absVideo).catch(() => void 0);
41894
- await mkdir17(dirname11(outputPath), { recursive: true });
42108
+ await mkdir18(dirname12(outputPath), { recursive: true });
41895
42109
  const result = await executeMotion({
41896
42110
  description: options.description.trim(),
41897
42111
  video: absVideo,
@@ -42019,8 +42233,8 @@ __export(edit_cmd_exports, {
42019
42233
  executeSpeedRamp: () => executeSpeedRamp,
42020
42234
  executeUpscale: () => executeUpscale
42021
42235
  });
42022
- import { resolve as resolve35, dirname as dirname12 } from "node:path";
42023
- import { readFile as readFile24, writeFile as writeFile32, mkdir as mkdir18 } from "node:fs/promises";
42236
+ import { resolve as resolve35, dirname as dirname13 } from "node:path";
42237
+ import { readFile as readFile24, writeFile as writeFile31, mkdir as mkdir19 } from "node:fs/promises";
42024
42238
  async function executeGrade(options) {
42025
42239
  const { videoPath, style, preset, output, analyzeOnly, apiKey } = options;
42026
42240
  try {
@@ -42213,6 +42427,7 @@ var init_edit_cmd = __esm({
42213
42427
  init_output();
42214
42428
  init_validate();
42215
42429
  init_cost_tier();
42430
+ init_image_file();
42216
42431
  editCommand = new Command("edit").alias("ed").description(
42217
42432
  "Edit and post-process media (silence-cut, caption, grade, reframe, upscale...)"
42218
42433
  ).addHelpText(
@@ -42648,7 +42863,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
42648
42863
  console.log();
42649
42864
  if (options.keyframes) {
42650
42865
  const keyframesPath = resolve35(process.cwd(), options.keyframes);
42651
- await writeFile32(keyframesPath, JSON.stringify(cropKeyframes, null, 2));
42866
+ await writeFile31(keyframesPath, JSON.stringify(cropKeyframes, null, 2));
42652
42867
  console.log(source_default.green(`Keyframes saved to: ${keyframesPath}`));
42653
42868
  }
42654
42869
  if (options.analyzeOnly) {
@@ -42672,7 +42887,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
42672
42887
  exitWithError(generalError(`Reframe failed: ${msg}`));
42673
42888
  }
42674
42889
  });
42675
- 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) => {
42890
+ 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) => {
42676
42891
  const startedAt = Date.now();
42677
42892
  try {
42678
42893
  if (args.length < 2) {
@@ -42731,14 +42946,16 @@ Run 'vibe schema edit.<command>' for structured parameter info.
42731
42946
  const grok = new GrokProvider();
42732
42947
  await grok.initialize({ apiKey });
42733
42948
  result = await grok.editImage(imageBuffers[0], prompt3, {
42949
+ ...resolveGrokImageModel(options.model),
42734
42950
  aspectRatio: options.ratio
42735
42951
  });
42736
42952
  } else {
42737
42953
  const editModelNames = {
42738
- flash: "gemini-2.5-flash-image",
42739
- "3.1-flash": "gemini-3.1-flash-image-preview",
42740
- latest: "gemini-3.1-flash-image-preview",
42741
- pro: "gemini-3-pro-image-preview"
42954
+ flash: "gemini-3.1-flash-image",
42955
+ "3.1-flash": "gemini-3.1-flash-image",
42956
+ latest: "gemini-3.1-flash-image",
42957
+ lite: "gemini-3.1-flash-lite-image",
42958
+ pro: "gemini-3-pro-image"
42742
42959
  };
42743
42960
  const editModelName = editModelNames[options.model] || editModelNames.flash;
42744
42961
  spinner2.text = `Editing with ${editModelName}...`;
@@ -42749,15 +42966,6 @@ Run 'vibe schema edit.<command>' for structured parameter info.
42749
42966
  aspectRatio: options.ratio,
42750
42967
  resolution: options.size
42751
42968
  });
42752
- const fallbackModels = ["latest", "3.1-flash"];
42753
- if (!result.success && fallbackModels.includes(options.model)) {
42754
- spinner2.text = `${source_default.dim(result.error || `${editModelName} failed`)} \u2014 retrying with flash...`;
42755
- result = await gemini.editImage(imageBuffers, prompt3, {
42756
- model: "flash",
42757
- aspectRatio: options.ratio,
42758
- resolution: options.size
42759
- });
42760
- }
42761
42969
  }
42762
42970
  if (!result.success || !result.images || result.images.length === 0) {
42763
42971
  spinner2.fail(result.error || "Image editing failed");
@@ -42767,14 +42975,14 @@ Run 'vibe schema edit.<command>' for structured parameter info.
42767
42975
  const img = result.images[0];
42768
42976
  const outputPath = resolve35(process.cwd(), options.output);
42769
42977
  const saveImage = async () => {
42770
- await mkdir18(dirname12(outputPath), { recursive: true });
42978
+ await mkdir19(dirname13(outputPath), { recursive: true });
42771
42979
  if (img.base64) {
42772
42980
  const buffer = Buffer.from(img.base64, "base64");
42773
- await writeFile32(outputPath, buffer);
42981
+ await writeImageFile(outputPath, buffer);
42774
42982
  } else if (img.url) {
42775
42983
  const resp = await fetch(img.url);
42776
42984
  const arrayBuf = await resp.arrayBuffer();
42777
- await writeFile32(outputPath, Buffer.from(arrayBuf));
42985
+ await writeImageFile(outputPath, Buffer.from(arrayBuf));
42778
42986
  }
42779
42987
  };
42780
42988
  const resultModel = result.model;
@@ -42994,7 +43202,7 @@ __export(ai_analyze_exports, {
42994
43202
  registerAnalyzeCommands: () => registerAnalyzeCommands
42995
43203
  });
42996
43204
  import { readFile as readFile25 } from "node:fs/promises";
42997
- import { extname as extname7, resolve as resolve36 } from "node:path";
43205
+ import { extname as extname8, resolve as resolve36 } from "node:path";
42998
43206
  import { existsSync as existsSync31 } from "node:fs";
42999
43207
  async function executeGeminiVideo(options) {
43000
43208
  try {
@@ -43051,7 +43259,7 @@ async function executeAnalyze(options) {
43051
43259
  const isYouTube = source.includes("youtube.com") || source.includes("youtu.be");
43052
43260
  const isImageUrl = /^https?:\/\/.+\.(png|jpg|jpeg|webp|gif)(\?.*)?$/i.test(source);
43053
43261
  const isVideoUrl = /^https?:\/\/.+\.(mp4|mov|webm)(\?.*)?$/i.test(source);
43054
- const ext = extname7(source).toLowerCase();
43262
+ const ext = extname8(source).toLowerCase();
43055
43263
  const imageExts = [".png", ".jpg", ".jpeg", ".webp", ".gif"];
43056
43264
  const videoExts = [".mp4", ".mov", ".webm", ".avi", ".mkv"];
43057
43265
  const isLocalImage = imageExts.includes(ext);
@@ -44904,8 +45112,8 @@ var init_render_inspect = __esm({
44904
45112
  });
44905
45113
 
44906
45114
  // src/commands/ai-highlights.ts
44907
- import { readFile as readFile31, writeFile as writeFile35, mkdir as mkdir19 } from "node:fs/promises";
44908
- import { resolve as resolve42, dirname as dirname14, basename as basename13, extname as extname9 } from "node:path";
45115
+ import { readFile as readFile31, writeFile as writeFile34, mkdir as mkdir20 } from "node:fs/promises";
45116
+ import { resolve as resolve42, dirname as dirname15, basename as basename13, extname as extname10 } from "node:path";
44909
45117
  import { existsSync as existsSync36 } from "node:fs";
44910
45118
  function filterHighlights(highlights, options) {
44911
45119
  let filtered = highlights.filter((h) => h.confidence >= options.threshold);
@@ -44949,7 +45157,7 @@ async function executeHighlights(options) {
44949
45157
  if (!existsSync36(absPath)) {
44950
45158
  return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: `File not found: ${absPath}` };
44951
45159
  }
44952
- const ext = extname9(absPath).toLowerCase();
45160
+ const ext = extname10(absPath).toLowerCase();
44953
45161
  const videoExtensions = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"];
44954
45162
  const isVideo = videoExtensions.includes(ext);
44955
45163
  const targetDuration = options.duration;
@@ -45094,7 +45302,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
45094
45302
  };
45095
45303
  if (options.output) {
45096
45304
  const outputPath = resolve42(process.cwd(), options.output);
45097
- await writeFile35(outputPath, JSON.stringify({
45305
+ await writeFile34(outputPath, JSON.stringify({
45098
45306
  sourceFile: absPath,
45099
45307
  totalDuration: sourceDuration,
45100
45308
  criteria: options.criteria || "all",
@@ -45129,7 +45337,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
45129
45337
  }
45130
45338
  }
45131
45339
  const projectPath = resolve42(process.cwd(), options.project);
45132
- await writeFile35(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
45340
+ await writeFile34(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
45133
45341
  extractResult.projectPath = projectPath;
45134
45342
  }
45135
45343
  return extractResult;
@@ -45276,9 +45484,9 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
45276
45484
  }))
45277
45485
  };
45278
45486
  }
45279
- const outputDir = options.outputDir ? resolve42(process.cwd(), options.outputDir) : dirname14(absPath);
45487
+ const outputDir = options.outputDir ? resolve42(process.cwd(), options.outputDir) : dirname15(absPath);
45280
45488
  if (options.outputDir && !existsSync36(outputDir)) {
45281
- await mkdir19(outputDir, { recursive: true });
45489
+ await mkdir20(outputDir, { recursive: true });
45282
45490
  }
45283
45491
  const result = {
45284
45492
  success: true,
@@ -45286,7 +45494,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
45286
45494
  };
45287
45495
  for (let i = 0; i < selectedHighlights.length; i++) {
45288
45496
  const h = selectedHighlights[i];
45289
- const baseName = basename13(absPath, extname9(absPath));
45497
+ const baseName = basename13(absPath, extname10(absPath));
45290
45498
  const outputPath = resolve42(outputDir, `${baseName}-short-${i + 1}.mp4`);
45291
45499
  const { stdout: probeOut } = await execSafe("ffprobe", [
45292
45500
  "-v",
@@ -45385,7 +45593,7 @@ function registerHighlightsCommands(aiCommand) {
45385
45593
  });
45386
45594
  return;
45387
45595
  }
45388
- const ext = extname9(absPath).toLowerCase();
45596
+ const ext = extname10(absPath).toLowerCase();
45389
45597
  const videoExtensions = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"];
45390
45598
  const isVideo = videoExtensions.includes(ext);
45391
45599
  console.log();
@@ -45614,7 +45822,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
45614
45822
  console.log();
45615
45823
  if (options.output) {
45616
45824
  const outputPath = resolve42(process.cwd(), options.output);
45617
- await writeFile35(outputPath, JSON.stringify(result, null, 2), "utf-8");
45825
+ await writeFile34(outputPath, JSON.stringify(result, null, 2), "utf-8");
45618
45826
  console.log(source_default.green(`\u{1F4BE} Saved highlights to: ${outputPath}`));
45619
45827
  }
45620
45828
  if (options.project) {
@@ -45644,7 +45852,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
45644
45852
  currentTime += highlight.duration;
45645
45853
  }
45646
45854
  const projectPath = resolve42(process.cwd(), options.project);
45647
- await writeFile35(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
45855
+ await writeFile34(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
45648
45856
  projectSpinner.succeed(source_default.green(`Created project: ${projectPath}`));
45649
45857
  }
45650
45858
  console.log();
@@ -45853,9 +46061,9 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
45853
46061
  console.log(source_default.dim("Use without --analyze-only to generate shorts."));
45854
46062
  return;
45855
46063
  }
45856
- const outputDir = options.outputDir ? resolve42(process.cwd(), options.outputDir) : dirname14(absPath);
46064
+ const outputDir = options.outputDir ? resolve42(process.cwd(), options.outputDir) : dirname15(absPath);
45857
46065
  if (options.outputDir && !existsSync36(outputDir)) {
45858
- await mkdir19(outputDir, { recursive: true });
46066
+ await mkdir20(outputDir, { recursive: true });
45859
46067
  }
45860
46068
  for (let i = 0; i < selectedHighlights.length; i++) {
45861
46069
  const h = selectedHighlights[i];
@@ -45863,16 +46071,16 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
45863
46071
  let outputPath;
45864
46072
  if (shortCount === 1 && options.output) {
45865
46073
  outputPath = resolve42(process.cwd(), options.output);
45866
- if (!extname9(outputPath)) {
46074
+ if (!extname10(outputPath)) {
45867
46075
  outputPath += ".mp4";
45868
46076
  }
45869
46077
  } else {
45870
- const baseName = basename13(absPath, extname9(absPath));
46078
+ const baseName = basename13(absPath, extname10(absPath));
45871
46079
  outputPath = resolve42(outputDir, `${baseName}-short-${i + 1}.mp4`);
45872
46080
  }
45873
- const parentDir = dirname14(outputPath);
46081
+ const parentDir = dirname15(outputPath);
45874
46082
  if (!existsSync36(parentDir)) {
45875
- await mkdir19(parentDir, { recursive: true });
46083
+ await mkdir20(parentDir, { recursive: true });
45876
46084
  }
45877
46085
  const { stdout: probeOut } = await execSafe("ffprobe", [
45878
46086
  "-v",
@@ -45944,8 +46152,8 @@ var init_ai_highlights = __esm({
45944
46152
  });
45945
46153
 
45946
46154
  // src/commands/ai-animated-caption.ts
45947
- import { resolve as resolve43, dirname as dirname15, basename as basename14 } from "node:path";
45948
- import { writeFile as writeFile36, mkdir as mkdir20, rm as rm4 } from "node:fs/promises";
46155
+ import { resolve as resolve43, dirname as dirname16, basename as basename14 } from "node:path";
46156
+ import { writeFile as writeFile35, mkdir as mkdir21, rm as rm5 } from "node:fs/promises";
45949
46157
  import { existsSync as existsSync37 } from "node:fs";
45950
46158
  import { tmpdir as tmpdir3 } from "node:os";
45951
46159
  function groupWords(words, options = {}) {
@@ -46093,7 +46301,7 @@ async function executeAnimatedCaption(options) {
46093
46301
  }
46094
46302
  const effectiveFontSize = fontSize ?? Math.round(height * 0.04);
46095
46303
  const tmpAudioDir = resolve43(tmpdir3(), `vf-ac-${Date.now()}`);
46096
- await mkdir20(tmpAudioDir, { recursive: true });
46304
+ await mkdir21(tmpAudioDir, { recursive: true });
46097
46305
  const audioPath = resolve43(tmpAudioDir, "audio.wav");
46098
46306
  await execSafe("ffmpeg", [
46099
46307
  "-y",
@@ -46110,21 +46318,21 @@ async function executeAnimatedCaption(options) {
46110
46318
  ], { timeout: 12e4 });
46111
46319
  const apiKey = await getApiKey("OPENAI_API_KEY", "OpenAI");
46112
46320
  if (!apiKey) {
46113
- await rm4(tmpAudioDir, { recursive: true, force: true }).catch(() => {
46321
+ await rm5(tmpAudioDir, { recursive: true, force: true }).catch(() => {
46114
46322
  });
46115
46323
  return { success: false, error: "OPENAI_API_KEY required for Whisper transcription. Run 'vibe setup' or set OPENAI_API_KEY in .env" };
46116
46324
  }
46117
46325
  const transcript = await transcribeWithWords(audioPath, apiKey, language);
46118
46326
  if (!transcript.words || transcript.words.length === 0) {
46119
- await rm4(tmpAudioDir, { recursive: true, force: true }).catch(() => {
46327
+ await rm5(tmpAudioDir, { recursive: true, force: true }).catch(() => {
46120
46328
  });
46121
46329
  return { success: false, error: "No words detected in transcription" };
46122
46330
  }
46123
46331
  const groups = groupWords(transcript.words, { wordsPerGroup, maxChars });
46124
46332
  const absOutputPath = resolve43(process.cwd(), outputPath);
46125
- const outDir = dirname15(absOutputPath);
46333
+ const outDir = dirname16(absOutputPath);
46126
46334
  if (!existsSync37(outDir)) {
46127
- await mkdir20(outDir, { recursive: true });
46335
+ await mkdir21(outDir, { recursive: true });
46128
46336
  }
46129
46337
  if (tier === "ass") {
46130
46338
  const assContent = generateASS(
@@ -46133,7 +46341,7 @@ async function executeAnimatedCaption(options) {
46133
46341
  { highlightColor, fontSize: effectiveFontSize, position, width, height }
46134
46342
  );
46135
46343
  const assPath = resolve43(tmpAudioDir, "captions.ass");
46136
- await writeFile36(assPath, assContent, "utf-8");
46344
+ await writeFile35(assPath, assContent, "utf-8");
46137
46345
  const escapedAssPath = assPath.replace(/\\/g, "\\\\").replace(/:/g, "\\:");
46138
46346
  await execSafe("ffmpeg", [
46139
46347
  "-y",
@@ -46170,12 +46378,12 @@ async function executeAnimatedCaption(options) {
46170
46378
  outputPath: absOutputPath
46171
46379
  });
46172
46380
  if (!renderResult.success) {
46173
- await rm4(tmpAudioDir, { recursive: true, force: true }).catch(() => {
46381
+ await rm5(tmpAudioDir, { recursive: true, force: true }).catch(() => {
46174
46382
  });
46175
46383
  return { success: false, error: renderResult.error };
46176
46384
  }
46177
46385
  }
46178
- await rm4(tmpAudioDir, { recursive: true, force: true }).catch(() => {
46386
+ await rm5(tmpAudioDir, { recursive: true, force: true }).catch(() => {
46179
46387
  });
46180
46388
  return {
46181
46389
  success: true,
@@ -55769,7 +55977,7 @@ __export(detect_exports, {
55769
55977
  executeDetectScenes: () => executeDetectScenes,
55770
55978
  executeDetectSilence: () => executeDetectSilence
55771
55979
  });
55772
- import { readFile as readFile33, writeFile as writeFile38 } from "node:fs/promises";
55980
+ import { readFile as readFile33, writeFile as writeFile37 } from "node:fs/promises";
55773
55981
  import { resolve as resolve47, basename as basename18 } from "node:path";
55774
55982
  async function executeDetectScenes(options) {
55775
55983
  try {
@@ -55808,7 +56016,7 @@ async function executeDetectScenes(options) {
55808
56016
  }));
55809
56017
  if (options.outputPath) {
55810
56018
  const outputPath = resolve47(process.cwd(), options.outputPath);
55811
- await writeFile38(outputPath, JSON.stringify({ source: absPath, totalDuration, threshold, scenes: result }, null, 2), "utf-8");
56019
+ await writeFile37(outputPath, JSON.stringify({ source: absPath, totalDuration, threshold, scenes: result }, null, 2), "utf-8");
55812
56020
  }
55813
56021
  return { success: true, scenes: result, totalDuration };
55814
56022
  } catch (error) {
@@ -55855,7 +56063,7 @@ async function executeDetectSilence(options) {
55855
56063
  }
55856
56064
  if (options.outputPath) {
55857
56065
  const outputPath = resolve47(process.cwd(), options.outputPath);
55858
- await writeFile38(outputPath, JSON.stringify({ source: absPath, silences }, null, 2), "utf-8");
56066
+ await writeFile37(outputPath, JSON.stringify({ source: absPath, silences }, null, 2), "utf-8");
55859
56067
  }
55860
56068
  return { success: true, silences };
55861
56069
  } catch (error) {
@@ -55904,7 +56112,7 @@ async function executeDetectBeats(options) {
55904
56112
  }
55905
56113
  if (options.outputPath) {
55906
56114
  const outputPath = resolve47(process.cwd(), options.outputPath);
55907
- await writeFile38(outputPath, JSON.stringify({ source: absPath, beatCount: beats.length, beats }, null, 2), "utf-8");
56115
+ await writeFile37(outputPath, JSON.stringify({ source: absPath, beatCount: beats.length, beats }, null, 2), "utf-8");
55908
56116
  }
55909
56117
  return { success: true, beats, beatCount: beats.length };
55910
56118
  } catch (error) {
@@ -56008,7 +56216,7 @@ var init_detect = __esm({
56008
56216
  duration: (i < scenes.length - 1 ? scenes[i + 1].timestamp : totalDuration) - s.timestamp
56009
56217
  }))
56010
56218
  };
56011
- await writeFile38(outputPath, JSON.stringify(result, null, 2), "utf-8");
56219
+ await writeFile37(outputPath, JSON.stringify(result, null, 2), "utf-8");
56012
56220
  console.log(source_default.green(`Saved to: ${outputPath}`));
56013
56221
  }
56014
56222
  if (options.project) {
@@ -56038,7 +56246,7 @@ var init_detect = __esm({
56038
56246
  sourceEndOffset: end
56039
56247
  });
56040
56248
  }
56041
- await writeFile38(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
56249
+ await writeFile37(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
56042
56250
  console.log(source_default.green(`Added ${scenes.length} clips to project`));
56043
56251
  }
56044
56252
  } catch (error) {
@@ -56120,7 +56328,7 @@ var init_detect = __esm({
56120
56328
  console.log();
56121
56329
  if (options.output) {
56122
56330
  const outputPath = resolve47(process.cwd(), options.output);
56123
- await writeFile38(
56331
+ await writeFile37(
56124
56332
  outputPath,
56125
56333
  JSON.stringify({ source: absPath, silences }, null, 2),
56126
56334
  "utf-8"
@@ -56205,7 +56413,7 @@ var init_detect = __esm({
56205
56413
  console.log();
56206
56414
  if (options.output) {
56207
56415
  const outputPath = resolve47(process.cwd(), options.output);
56208
- await writeFile38(
56416
+ await writeFile37(
56209
56417
  outputPath,
56210
56418
  JSON.stringify({ source: absPath, beatCount: beats.length, beats }, null, 2),
56211
56419
  "utf-8"
@@ -56841,7 +57049,7 @@ var init_openai2 = __esm({
56841
57049
  OpenAIAdapter = class {
56842
57050
  provider = "openai";
56843
57051
  client = null;
56844
- model = "gpt-5-mini";
57052
+ model = "gpt-5.4-mini";
56845
57053
  async initialize(apiKey) {
56846
57054
  this.client = new OpenAI({ apiKey });
56847
57055
  }
@@ -56937,7 +57145,7 @@ var init_claude2 = __esm({
56937
57145
  ClaudeAdapter = class {
56938
57146
  provider = "claude";
56939
57147
  client = null;
56940
- model = "claude-sonnet-4-6";
57148
+ model = "claude-sonnet-5-5";
56941
57149
  async initialize(apiKey) {
56942
57150
  this.client = new Anthropic({ apiKey });
56943
57151
  }
@@ -56962,6 +57170,13 @@ var init_claude2 = __esm({
56962
57170
  content: msg.content
56963
57171
  });
56964
57172
  } else if (msg.role === "assistant") {
57173
+ if (msg.providerContent) {
57174
+ claudeMessages.push({
57175
+ role: "assistant",
57176
+ content: msg.providerContent
57177
+ });
57178
+ continue;
57179
+ }
56965
57180
  const content = [];
56966
57181
  if (msg.content) {
56967
57182
  content.push({ type: "text", text: msg.content });
@@ -56983,16 +57198,17 @@ var init_claude2 = __esm({
56983
57198
  });
56984
57199
  }
56985
57200
  } else if (msg.role === "tool") {
56986
- claudeMessages.push({
56987
- role: "user",
56988
- content: [
56989
- {
56990
- type: "tool_result",
56991
- tool_use_id: msg.toolCallId,
56992
- content: msg.content
56993
- }
56994
- ]
56995
- });
57201
+ const result = {
57202
+ type: "tool_result",
57203
+ tool_use_id: msg.toolCallId,
57204
+ content: msg.content
57205
+ };
57206
+ const last = claudeMessages.at(-1);
57207
+ if (last?.role === "user" && Array.isArray(last.content)) {
57208
+ last.content.push(result);
57209
+ } else {
57210
+ claudeMessages.push({ role: "user", content: [result] });
57211
+ }
56996
57212
  }
56997
57213
  }
56998
57214
  const claudeTools = tools.map((tool) => ({
@@ -57006,7 +57222,8 @@ var init_claude2 = __esm({
57006
57222
  }));
57007
57223
  const response = await this.client.messages.create({
57008
57224
  model: this.model,
57009
- max_tokens: 4096,
57225
+ // Thinking tokens count against this cap on Claude 5.x.
57226
+ max_tokens: 16e3,
57010
57227
  system: systemPrompt,
57011
57228
  messages: claudeMessages,
57012
57229
  tools: claudeTools.length > 0 ? claudeTools : void 0
@@ -57033,7 +57250,8 @@ var init_claude2 = __esm({
57033
57250
  return {
57034
57251
  content: textContent,
57035
57252
  toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
57036
- finishReason
57253
+ finishReason,
57254
+ providerContent: response.content
57037
57255
  };
57038
57256
  }
57039
57257
  };
@@ -57043,17 +57261,51 @@ var init_claude2 = __esm({
57043
57261
  // src/agent/adapters/gemini.ts
57044
57262
  var gemini_exports = {};
57045
57263
  __export(gemini_exports, {
57046
- GeminiAdapter: () => GeminiAdapter
57264
+ GeminiAdapter: () => GeminiAdapter,
57265
+ toGeminiSchema: () => toGeminiSchema
57047
57266
  });
57048
57267
  import { GoogleGenerativeAI, SchemaType } from "@google/generative-ai";
57049
- var GeminiAdapter;
57268
+ function toGeminiSchema(schema) {
57269
+ if (Array.isArray(schema)) return schema.map(toGeminiSchema);
57270
+ if (!schema || typeof schema !== "object") return schema;
57271
+ const out = {};
57272
+ for (const [key, value] of Object.entries(schema)) {
57273
+ if (!GEMINI_SCHEMA_KEYS.has(key)) continue;
57274
+ if (key === "properties" && value && typeof value === "object") {
57275
+ out.properties = Object.fromEntries(
57276
+ Object.entries(value).map(([name, prop]) => [name, toGeminiSchema(prop)])
57277
+ );
57278
+ } else if (key === "items") {
57279
+ out.items = toGeminiSchema(value);
57280
+ } else {
57281
+ out[key] = value;
57282
+ }
57283
+ }
57284
+ return out;
57285
+ }
57286
+ var GEMINI_SCHEMA_KEYS, GeminiAdapter;
57050
57287
  var init_gemini2 = __esm({
57051
57288
  "src/agent/adapters/gemini.ts"() {
57052
57289
  "use strict";
57290
+ init_dist2();
57291
+ GEMINI_SCHEMA_KEYS = /* @__PURE__ */ new Set([
57292
+ "type",
57293
+ "format",
57294
+ "description",
57295
+ "nullable",
57296
+ "enum",
57297
+ "items",
57298
+ "properties",
57299
+ "required",
57300
+ "minItems",
57301
+ "maxItems",
57302
+ "minimum",
57303
+ "maximum"
57304
+ ]);
57053
57305
  GeminiAdapter = class {
57054
57306
  provider = "gemini";
57055
57307
  client = null;
57056
- model = "gemini-2.5-flash";
57308
+ model = GEMINI_AGENT_DEFAULT_TEXT_MODEL;
57057
57309
  async initialize(apiKey) {
57058
57310
  this.client = new GoogleGenerativeAI(apiKey);
57059
57311
  }
@@ -57075,7 +57327,12 @@ var init_gemini2 = __esm({
57075
57327
  description: tool.description,
57076
57328
  parameters: {
57077
57329
  type: SchemaType.OBJECT,
57078
- properties: tool.parameters.properties,
57330
+ properties: Object.fromEntries(
57331
+ Object.entries(tool.parameters.properties).map(([name, prop]) => [
57332
+ name,
57333
+ toGeminiSchema(prop)
57334
+ ])
57335
+ ),
57079
57336
  required: tool.parameters.required
57080
57337
  }
57081
57338
  }))
@@ -57094,6 +57351,10 @@ var init_gemini2 = __esm({
57094
57351
  parts: [{ text: msg.content }]
57095
57352
  });
57096
57353
  } else if (msg.role === "assistant") {
57354
+ if (msg.providerContent) {
57355
+ geminiContents.push({ role: "model", parts: msg.providerContent });
57356
+ continue;
57357
+ }
57097
57358
  const parts = [];
57098
57359
  if (msg.content) {
57099
57360
  parts.push({ text: msg.content });
@@ -57115,16 +57376,19 @@ var init_gemini2 = __esm({
57115
57376
  });
57116
57377
  }
57117
57378
  } else if (msg.role === "tool") {
57118
- geminiContents.push({
57119
- role: "user",
57120
- parts: [{
57121
- functionResponse: {
57122
- name: msg.toolCallId.split(":::")[0] || "unknown",
57123
- // Extract function name from id
57124
- response: { result: msg.content }
57125
- }
57126
- }]
57127
- });
57379
+ const part = {
57380
+ functionResponse: {
57381
+ name: msg.toolCallId.split(":::")[0] || "unknown",
57382
+ // Extract function name from id
57383
+ response: { result: msg.content }
57384
+ }
57385
+ };
57386
+ const last = geminiContents.at(-1);
57387
+ if (last?.role === "user" && last.parts.every((p) => "functionResponse" in p)) {
57388
+ last.parts.push(part);
57389
+ } else {
57390
+ geminiContents.push({ role: "user", parts: [part] });
57391
+ }
57128
57392
  }
57129
57393
  }
57130
57394
  const result = await model.generateContent({
@@ -57161,7 +57425,8 @@ var init_gemini2 = __esm({
57161
57425
  return {
57162
57426
  content: textContent,
57163
57427
  toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
57164
- finishReason
57428
+ finishReason,
57429
+ providerContent: candidate.content.parts
57165
57430
  };
57166
57431
  }
57167
57432
  };
@@ -57364,7 +57629,7 @@ var init_xai = __esm({
57364
57629
  XAIAdapter = class {
57365
57630
  provider = "xai";
57366
57631
  client = null;
57367
- model = "grok-4-1-fast-reasoning";
57632
+ model = "grok-4.3";
57368
57633
  async initialize(apiKey) {
57369
57634
  this.client = new OpenAI2({
57370
57635
  apiKey,
@@ -57768,7 +58033,7 @@ var init_composer_resolve = __esm({
57768
58033
 
57769
58034
  // src/commands/_shared/storyboard-revise.ts
57770
58035
  import { existsSync as existsSync42 } from "node:fs";
57771
- import { readFile as readFile37, writeFile as writeFile41 } from "node:fs/promises";
58036
+ import { readFile as readFile37, writeFile as writeFile40 } from "node:fs/promises";
57772
58037
  import { join as join22, resolve as resolve51 } from "node:path";
57773
58038
  async function executeStoryboardRevision(opts) {
57774
58039
  const projectDir = resolve51(opts.projectDir);
@@ -57887,7 +58152,7 @@ async function executeStoryboardRevision(opts) {
57887
58152
  if (loaded.layout === "bundle") {
57888
58153
  await rewriteBundleFromMarkdown(projectDir, storyboardMd);
57889
58154
  } else {
57890
- await writeFile41(storyboardPath2, storyboardMd, "utf-8");
58155
+ await writeFile40(storyboardPath2, storyboardMd, "utf-8");
57891
58156
  }
57892
58157
  }
57893
58158
  const changedBeats = unique2([...diffChangedBeats(currentStoryboard, storyboardMd), ...payload.changedBeats]);
@@ -58179,7 +58444,7 @@ var init_design_parse = __esm({
58179
58444
 
58180
58445
  // src/commands/_shared/build-asset-reference.ts
58181
58446
  import { existsSync as existsSync45 } from "node:fs";
58182
- import { extname as extname12, isAbsolute as isAbsolute5, relative as relative9, resolve as resolve54, sep as sep2 } from "node:path";
58447
+ import { extname as extname13, isAbsolute as isAbsolute5, relative as relative9, resolve as resolve54, sep as sep2 } from "node:path";
58183
58448
  function resolveTypedAssetReference(projectDir, kind, value) {
58184
58449
  const raw = stringOrUndefined2(value);
58185
58450
  if (!raw || !looksLikeAssetPath(raw)) return null;
@@ -58204,7 +58469,7 @@ function isReadyAssetReference(reference) {
58204
58469
  return Boolean(reference && !reference.error && reference.relPath && reference.absPath);
58205
58470
  }
58206
58471
  function resolveReference(projectDir, kind, raw, source, forcedError) {
58207
- const ext = extname12(raw).toLowerCase();
58472
+ const ext = extname13(raw).toLowerCase();
58208
58473
  const inferred = inferAssetKind(raw);
58209
58474
  const absPath = isAbsolute5(raw) ? resolve54(raw) : resolve54(projectDir, raw);
58210
58475
  const rel = relative9(resolve54(projectDir), absPath);
@@ -58236,7 +58501,7 @@ function resolveReference(projectDir, kind, raw, source, forcedError) {
58236
58501
  };
58237
58502
  }
58238
58503
  function inferAssetKind(value) {
58239
- const ext = extname12(value).toLowerCase();
58504
+ const ext = extname13(value).toLowerCase();
58240
58505
  if (EXTENSIONS.backdrop.includes(ext)) return "backdrop";
58241
58506
  if (EXTENSIONS.video.includes(ext)) return "video";
58242
58507
  if (EXTENSIONS.narration.includes(ext)) return "music";
@@ -58398,9 +58663,40 @@ var init_build_backdrop_prompt = __esm({
58398
58663
  }
58399
58664
  });
58400
58665
 
58666
+ // src/commands/_shared/build-video-providers.ts
58667
+ function isBuildVideoProvider(value) {
58668
+ return BUILD_VIDEO_PROVIDERS.includes(value);
58669
+ }
58670
+ function resolveBuildVideoProvider(value) {
58671
+ const provider = String(value ?? "seedance").toLowerCase();
58672
+ if (provider === "fal") return "seedance";
58673
+ return isBuildVideoProvider(provider) ? provider : "seedance";
58674
+ }
58675
+ function videoProviderConfigKey(provider) {
58676
+ switch (provider) {
58677
+ case "seedance":
58678
+ case "fal":
58679
+ return "fal";
58680
+ case "grok":
58681
+ return "xai";
58682
+ case "veo":
58683
+ case "omni":
58684
+ return "google";
58685
+ default:
58686
+ return provider;
58687
+ }
58688
+ }
58689
+ var BUILD_VIDEO_PROVIDERS;
58690
+ var init_build_video_providers = __esm({
58691
+ "src/commands/_shared/build-video-providers.ts"() {
58692
+ "use strict";
58693
+ BUILD_VIDEO_PROVIDERS = ["seedance", "grok", "kling", "runway", "omni", "veo"];
58694
+ }
58695
+ });
58696
+
58401
58697
  // src/commands/_shared/build-plan.ts
58402
58698
  import { existsSync as existsSync46 } from "node:fs";
58403
- import { dirname as dirname18, join as join24, resolve as resolve55 } from "node:path";
58699
+ import { dirname as dirname19, join as join24, resolve as resolve55 } from "node:path";
58404
58700
  function backdropCostUsd(quality) {
58405
58701
  return quality === "hd" ? 0.2 : 0.05;
58406
58702
  }
@@ -59072,12 +59368,7 @@ function normalizeImageProvider(value) {
59072
59368
  return provider || "openai";
59073
59369
  }
59074
59370
  function normalizeVideoProvider(value) {
59075
- const provider = String(value ?? "seedance").toLowerCase();
59076
- if (provider === "fal") return "seedance";
59077
- if (provider === "seedance" || provider === "grok" || provider === "kling" || provider === "runway" || provider === "veo") {
59078
- return provider;
59079
- }
59080
- return "seedance";
59371
+ return resolveBuildVideoProvider(value);
59081
59372
  }
59082
59373
  function normalizeMusicProvider(value) {
59083
59374
  const provider = String(value ?? "elevenlabs").toLowerCase();
@@ -59089,7 +59380,7 @@ function imageConfigKey(provider) {
59089
59380
  return "openai";
59090
59381
  }
59091
59382
  function videoConfigKey(provider) {
59092
- return provider === "seedance" ? "fal" : provider === "grok" ? "xai" : provider === "veo" ? "google" : provider;
59383
+ return videoProviderConfigKey(provider);
59093
59384
  }
59094
59385
  function composerConfigKey(provider) {
59095
59386
  return provider === "claude" ? "anthropic" : provider === "gemini" ? "google" : "openai";
@@ -59107,12 +59398,12 @@ function loadPlanEnv(projectDir) {
59107
59398
  (0, import_dotenv3.config)({ path: join24(projectDir, ".env"), quiet: true });
59108
59399
  (0, import_dotenv3.config)({ path: resolve55(process.cwd(), ".env"), quiet: true });
59109
59400
  let dir = process.cwd();
59110
- while (dir !== dirname18(dir)) {
59401
+ while (dir !== dirname19(dir)) {
59111
59402
  if (existsSync46(join24(dir, "pnpm-workspace.yaml"))) {
59112
59403
  (0, import_dotenv3.config)({ path: join24(dir, ".env"), quiet: true });
59113
59404
  return;
59114
59405
  }
59115
- dir = dirname18(dir);
59406
+ dir = dirname19(dir);
59116
59407
  }
59117
59408
  }
59118
59409
  var import_dotenv3, VIDEO_COST_USD, MUSIC_COST_USD, ELEVENLABS_NARRATION_COST_USD, OPENAI_NARRATION_COST_USD, TRANSCRIPT_COST_USD, COMPOSE_COST_USD, SUPPORTED_BUILD_IMAGE_PROVIDERS;
@@ -59133,6 +59424,7 @@ var init_build_plan = __esm({
59133
59424
  init_project_config();
59134
59425
  init_scene_project();
59135
59426
  init_storyboard_edit();
59427
+ init_build_video_providers();
59136
59428
  VIDEO_COST_USD = 5;
59137
59429
  MUSIC_COST_USD = 0.5;
59138
59430
  ELEVENLABS_NARRATION_COST_USD = 0.05;
@@ -59194,7 +59486,7 @@ var init_concurrency = __esm({
59194
59486
 
59195
59487
  // src/commands/_shared/compose-aivideo.ts
59196
59488
  import { existsSync as existsSync47 } from "node:fs";
59197
- import { mkdir as mkdir23, readFile as readFile40, writeFile as writeFile43 } from "node:fs/promises";
59489
+ import { mkdir as mkdir24, readFile as readFile40, writeFile as writeFile42 } from "node:fs/promises";
59198
59490
  import { join as join25, resolve as resolve57 } from "node:path";
59199
59491
  function headingDescriptor(heading) {
59200
59492
  const stripped = heading.replace(/^\s*beat\s+/i, "");
@@ -59357,7 +59649,7 @@ async function composeAiVideo(opts) {
59357
59649
  }
59358
59650
  clipRels.push(rel);
59359
59651
  }
59360
- await mkdir23(join25(projectDir, "assets"), { recursive: true });
59652
+ await mkdir24(join25(projectDir, "assets"), { recursive: true });
59361
59653
  const bgRel = "assets/bg-full.mp4";
59362
59654
  const concatArgs = buildBgConcatArgs(
59363
59655
  clipRels.map((r) => join25(projectDir, r)),
@@ -59365,7 +59657,7 @@ async function composeAiVideo(opts) {
59365
59657
  );
59366
59658
  await execSafe("ffmpeg", concatArgs, { timeout: 6e5 });
59367
59659
  const totalDurationSec = await probeDurationSec(join25(projectDir, bgRel));
59368
- await mkdir23(join25(projectDir, "compositions"), { recursive: true });
59660
+ await mkdir24(join25(projectDir, "compositions"), { recursive: true });
59369
59661
  const sceneFiles = [];
59370
59662
  for (let i = 0; i < beats.length; i++) {
59371
59663
  const beat = beats[i];
@@ -59382,12 +59674,12 @@ async function composeAiVideo(opts) {
59382
59674
  isLast: i === beats.length - 1
59383
59675
  });
59384
59676
  const rel = `compositions/scene-${beat.id}.html`;
59385
- await writeFile43(join25(projectDir, rel), html, "utf-8");
59677
+ await writeFile42(join25(projectDir, rel), html, "utf-8");
59386
59678
  sceneFiles.push(rel);
59387
59679
  }
59388
59680
  const indexPath = join25(projectDir, "index.html");
59389
59681
  const next = existsSync47(indexPath) ? injectBgVideo(await readFile40(indexPath, "utf-8"), bgRel, totalDurationSec) : buildRootShell(tokens, bgRel, totalDurationSec);
59390
- await writeFile43(indexPath, next, "utf-8");
59682
+ await writeFile42(indexPath, next, "utf-8");
59391
59683
  return { bgVideoRel: bgRel, sceneFiles, totalDurationSec, beats: beats.length };
59392
59684
  }
59393
59685
  function buildRootShell(tokens, bgRel, totalDuration) {
@@ -59514,8 +59806,8 @@ var init_compose_aivideo = __esm({
59514
59806
 
59515
59807
  // src/commands/_shared/footage-assemble.ts
59516
59808
  import { existsSync as existsSync48 } from "node:fs";
59517
- import { mkdir as mkdir24, writeFile as writeFile44 } from "node:fs/promises";
59518
- import { dirname as dirname19, isAbsolute as isAbsolute6, join as join26, resolve as resolve58 } from "node:path";
59809
+ import { mkdir as mkdir25, writeFile as writeFile43 } from "node:fs/promises";
59810
+ import { dirname as dirname20, isAbsolute as isAbsolute6, join as join26, resolve as resolve58 } from "node:path";
59519
59811
  function planFootageTimeline(inputs, opts = {}) {
59520
59812
  if (inputs.length === 0) {
59521
59813
  throw new Error("No clips to assemble.");
@@ -59844,7 +60136,7 @@ async function executeFootageAssemble(opts) {
59844
60136
  if (opts.dryRun) {
59845
60137
  return { success: true, dryRun: true, outputPath, report };
59846
60138
  }
59847
- await mkdir24(dirname19(outputPath), { recursive: true });
60139
+ await mkdir25(dirname20(outputPath), { recursive: true });
59848
60140
  const args = buildFootageAssembleArgs({
59849
60141
  sources: sourceRels.map((s) => ({ path: join26(projectDir, s.rel), kind: s.kind })),
59850
60142
  plan,
@@ -59869,7 +60161,7 @@ async function executeFootageAssemble(opts) {
59869
60161
  };
59870
60162
  }
59871
60163
  const reportPath = join26(projectDir, "assemble-report.json");
59872
- await writeFile44(reportPath, `${JSON.stringify(report, null, 2)}
60164
+ await writeFile43(reportPath, `${JSON.stringify(report, null, 2)}
59873
60165
  `, "utf-8");
59874
60166
  return { success: true, dryRun: false, outputPath, reportPath, report };
59875
60167
  }
@@ -60034,7 +60326,7 @@ var init_scene_audio_scan = __esm({
60034
60326
 
60035
60327
  // src/commands/_shared/scene-audio-mux.ts
60036
60328
  import { rename as rename4, unlink } from "node:fs/promises";
60037
- import { resolve as resolve60, dirname as dirname20, extname as extname13, basename as basename20 } from "node:path";
60329
+ import { resolve as resolve60, dirname as dirname21, extname as extname14, basename as basename20 } from "node:path";
60038
60330
  function buildAudioMuxFilter(audios) {
60039
60331
  if (audios.length === 0) return null;
60040
60332
  const labels = [];
@@ -60093,9 +60385,9 @@ async function muxAudioIntoVideo(opts) {
60093
60385
  if (!filter) {
60094
60386
  return { success: true, outputPath: opts.videoPath, audioCount: 0 };
60095
60387
  }
60096
- const ext = extname13(opts.videoPath) || `.${opts.format}`;
60388
+ const ext = extname14(opts.videoPath) || `.${opts.format}`;
60097
60389
  const tmpPath = resolve60(
60098
- dirname20(opts.videoPath),
60390
+ dirname21(opts.videoPath),
60099
60391
  `.${basename20(opts.videoPath, ext)}.muxing${ext}`
60100
60392
  );
60101
60393
  const args = ["-y", "-loglevel", "error", "-i", opts.videoPath];
@@ -60243,8 +60535,8 @@ __export(scene_render_exports, {
60243
60535
  });
60244
60536
  import { execFile as execFile2 } from "node:child_process";
60245
60537
  import { existsSync as existsSync50 } from "node:fs";
60246
- import { mkdir as mkdir25, readFile as readFile43, stat as stat8, writeFile as writeFile45 } from "node:fs/promises";
60247
- import { resolve as resolve62, relative as relative10, dirname as dirname21, basename as basename21, join as join27, isAbsolute as isAbsolute8 } from "node:path";
60538
+ import { mkdir as mkdir26, readFile as readFile43, stat as stat8, writeFile as writeFile44 } from "node:fs/promises";
60539
+ import { resolve as resolve62, relative as relative10, dirname as dirname22, basename as basename21, join as join27, isAbsolute as isAbsolute8 } from "node:path";
60248
60540
  import { promisify as promisify2 } from "node:util";
60249
60541
  import {
60250
60542
  createRenderJob,
@@ -60297,7 +60589,7 @@ function buildMediaOpenCommand(action, filePath, platform3 = process.platform) {
60297
60589
  display: ["cmd", ...args].map(quoteCliArg).join(" ")
60298
60590
  };
60299
60591
  }
60300
- const target = action === "reveal" ? dirname21(filePath) : filePath;
60592
+ const target = action === "reveal" ? dirname22(filePath) : filePath;
60301
60593
  return {
60302
60594
  command: "xdg-open",
60303
60595
  args: [target],
@@ -60393,7 +60685,7 @@ async function executeSceneRender(opts = {}) {
60393
60685
  }
60394
60686
  const projectName = projectConfig.config.name;
60395
60687
  const outputPath = opts.output ? resolve62(projectDir, opts.output) : defaultOutputPath({ projectDir, projectName, format: opts.format, beatId: opts.beatId });
60396
- await mkdir25(dirname21(outputPath), { recursive: true });
60688
+ await mkdir26(dirname22(outputPath), { recursive: true });
60397
60689
  const config2 = buildRenderConfig({
60398
60690
  fps: opts.fps,
60399
60691
  quality: opts.quality,
@@ -60589,8 +60881,8 @@ async function prepareBeatRenderRoot(opts) {
60589
60881
  durationSec,
60590
60882
  narrationPath
60591
60883
  });
60592
- await mkdir25(dirname21(join27(opts.projectDir, root)), { recursive: true });
60593
- await writeFile45(join27(opts.projectDir, root), html, "utf-8");
60884
+ await mkdir26(dirname22(join27(opts.projectDir, root)), { recursive: true });
60885
+ await writeFile44(join27(opts.projectDir, root), html, "utf-8");
60594
60886
  return {
60595
60887
  success: true,
60596
60888
  root,
@@ -60656,7 +60948,7 @@ function reportedBeatAssetPaths(beat) {
60656
60948
  async function writeRenderReport(projectDir, result) {
60657
60949
  const reportPath = join27(projectDir, "render-report.json");
60658
60950
  try {
60659
- await writeFile45(
60951
+ await writeFile44(
60660
60952
  reportPath,
60661
60953
  JSON.stringify(
60662
60954
  {
@@ -60753,8 +61045,8 @@ __export(scene_build_exports, {
60753
61045
  resolveSceneBuildMode: () => resolveSceneBuildMode
60754
61046
  });
60755
61047
  import { existsSync as existsSync51, readFileSync as readFileSync2 } from "node:fs";
60756
- import { copyFile as copyFile5, mkdir as mkdir26, readFile as readFile44, rm as rm5, writeFile as writeFile46 } from "node:fs/promises";
60757
- import { dirname as dirname22, join as join28, resolve as resolve63 } from "node:path";
61048
+ import { copyFile as copyFile5, mkdir as mkdir27, readFile as readFile44, rm as rm6, writeFile as writeFile45 } from "node:fs/promises";
61049
+ import { dirname as dirname23, join as join28, resolve as resolve63 } from "node:path";
60758
61050
  function readProjectKindSync(projectDir) {
60759
61051
  try {
60760
61052
  const raw = readFileSync2(join28(projectDir, "vibe.config.json"), "utf-8");
@@ -61607,7 +61899,7 @@ async function dispatchNarration(beat, ctx) {
61607
61899
  };
61608
61900
  }
61609
61901
  if (existsSync51(cacheAbs) && !ctx.force) {
61610
- await mkdir26(dirname22(abs), { recursive: true });
61902
+ await mkdir27(dirname23(abs), { recursive: true });
61611
61903
  await copyFile5(cacheAbs, abs);
61612
61904
  await writeAssetMetadata({
61613
61905
  projectDir: ctx.projectDir,
@@ -61638,14 +61930,14 @@ async function dispatchNarration(beat, ctx) {
61638
61930
  ctx.onProgress({ type: "narration-failed", beatId: beat.id, error });
61639
61931
  return { status: "failed", error };
61640
61932
  }
61641
- await mkdir26(dirname22(abs), { recursive: true });
61642
- await writeFile46(abs, result.audioBuffer);
61933
+ await mkdir27(dirname23(abs), { recursive: true });
61934
+ await writeFile45(abs, result.audioBuffer);
61643
61935
  for (const otherExt of ["mp3", "wav"]) {
61644
61936
  const sibling = join28(ctx.projectDir, `assets/narration-${beat.id}.${otherExt}`);
61645
- if (sibling !== abs && existsSync51(sibling)) await rm5(sibling, { force: true });
61937
+ if (sibling !== abs && existsSync51(sibling)) await rm6(sibling, { force: true });
61646
61938
  }
61647
- await mkdir26(dirname22(cacheAbs), { recursive: true });
61648
- await writeFile46(cacheAbs, result.audioBuffer);
61939
+ await mkdir27(dirname23(cacheAbs), { recursive: true });
61940
+ await writeFile45(cacheAbs, result.audioBuffer);
61649
61941
  await writeAssetMetadata({
61650
61942
  projectDir: ctx.projectDir,
61651
61943
  kind: "narration",
@@ -61701,8 +61993,8 @@ async function dispatchTranscript(beat, narrationPath, narrationFresh, ctx) {
61701
61993
  return "skipped";
61702
61994
  }
61703
61995
  try {
61704
- await mkdir26(dirname22(abs), { recursive: true });
61705
- await writeFile46(abs, JSON.stringify(words, null, 2), "utf-8");
61996
+ await mkdir27(dirname23(abs), { recursive: true });
61997
+ await writeFile45(abs, JSON.stringify(words, null, 2), "utf-8");
61706
61998
  } catch (err) {
61707
61999
  ctx.onProgress({
61708
62000
  type: "transcript-skipped",
@@ -61790,7 +62082,7 @@ async function buildCharacters(characters, ctx) {
61790
62082
  }
61791
62083
  const cacheAbs = join28(ctx.projectDir, cache.path);
61792
62084
  if (existsSync51(cacheAbs) && !ctx.force) {
61793
- await mkdir26(dirname22(abs), { recursive: true });
62085
+ await mkdir27(dirname23(abs), { recursive: true });
61794
62086
  await copyFile5(cacheAbs, abs);
61795
62087
  await writeAssetMetadata({
61796
62088
  projectDir: ctx.projectDir,
@@ -61823,10 +62115,10 @@ async function buildCharacters(characters, ctx) {
61823
62115
  ctx.onProgress({ type: "character-failed", name: character.name, error: generated.error });
61824
62116
  continue;
61825
62117
  }
61826
- await mkdir26(dirname22(abs), { recursive: true });
61827
- await writeFile46(abs, generated.buffer);
61828
- await mkdir26(dirname22(cacheAbs), { recursive: true });
61829
- await writeFile46(cacheAbs, generated.buffer);
62118
+ await mkdir27(dirname23(abs), { recursive: true });
62119
+ await writeImageFile(abs, generated.buffer);
62120
+ await mkdir27(dirname23(cacheAbs), { recursive: true });
62121
+ await writeImageFile(cacheAbs, generated.buffer);
61830
62122
  await writeAssetMetadata({
61831
62123
  projectDir: ctx.projectDir,
61832
62124
  kind: "character",
@@ -61895,7 +62187,7 @@ async function dispatchBackdrop(beat, ctx) {
61895
62187
  }
61896
62188
  const cacheAbs = join28(ctx.projectDir, cache.path);
61897
62189
  if (existsSync51(cacheAbs) && !ctx.force) {
61898
- await mkdir26(dirname22(abs), { recursive: true });
62190
+ await mkdir27(dirname23(abs), { recursive: true });
61899
62191
  await copyFile5(cacheAbs, abs);
61900
62192
  await writeAssetMetadata({
61901
62193
  projectDir: ctx.projectDir,
@@ -61925,11 +62217,11 @@ async function dispatchBackdrop(beat, ctx) {
61925
62217
  ctx.onProgress({ type: "backdrop-failed", beatId: beat.id, error });
61926
62218
  return { status: "failed", error };
61927
62219
  }
61928
- await mkdir26(dirname22(abs), { recursive: true });
62220
+ await mkdir27(dirname23(abs), { recursive: true });
61929
62221
  const buffer = generated.buffer;
61930
- await writeFile46(abs, buffer);
61931
- await mkdir26(dirname22(cacheAbs), { recursive: true });
61932
- await writeFile46(cacheAbs, buffer);
62222
+ await writeImageFile(abs, buffer);
62223
+ await mkdir27(dirname23(cacheAbs), { recursive: true });
62224
+ await writeImageFile(cacheAbs, buffer);
61933
62225
  await writeAssetMetadata({
61934
62226
  projectDir: ctx.projectDir,
61935
62227
  kind: "backdrop",
@@ -61973,7 +62265,7 @@ async function generateBackdropImage(prompt3, ctx, ratio) {
61973
62265
  const result2 = await provider2.generateImage(prompt3, {
61974
62266
  // CLI input is free-form; an alias the provider doesn't know fails the
61975
62267
  // API call and surfaces as a beat-scoped backdrop/keyframe error.
61976
- model: ctx.imageModel ?? "gpt-image-2",
62268
+ model: ctx.imageModel ?? OPENAI_IMAGE_DEFAULT_MODEL,
61977
62269
  size: ctx.imageSize,
61978
62270
  quality: ctx.imageQuality
61979
62271
  });
@@ -62058,7 +62350,7 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
62058
62350
  }
62059
62351
  const cacheAbs = join28(ctx.projectDir, cache.path);
62060
62352
  if (existsSync51(cacheAbs) && !ctx.force) {
62061
- await mkdir26(dirname22(abs), { recursive: true });
62353
+ await mkdir27(dirname23(abs), { recursive: true });
62062
62354
  await copyFile5(cacheAbs, abs);
62063
62355
  await writeAssetMetadata({
62064
62356
  projectDir: ctx.projectDir,
@@ -62088,10 +62380,10 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
62088
62380
  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. ";
62089
62381
  const generated = sheetBuffers.length > 0 ? await editKeyframeImage(`${identityLock}${keyframeCue}`, sheetBuffers, imgCtx, ratio) : await generateBackdropImage(keyframeCue, imgCtx, ratio);
62090
62382
  if (!generated.success) return { status: "failed", error: generated.error };
62091
- await mkdir26(dirname22(abs), { recursive: true });
62092
- await writeFile46(abs, generated.buffer);
62093
- await mkdir26(dirname22(cacheAbs), { recursive: true });
62094
- await writeFile46(cacheAbs, generated.buffer);
62383
+ await mkdir27(dirname23(abs), { recursive: true });
62384
+ await writeImageFile(abs, generated.buffer);
62385
+ await mkdir27(dirname23(cacheAbs), { recursive: true });
62386
+ await writeImageFile(cacheAbs, generated.buffer);
62095
62387
  await writeAssetMetadata({
62096
62388
  projectDir: ctx.projectDir,
62097
62389
  kind: "keyframe",
@@ -62187,7 +62479,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
62187
62479
  }
62188
62480
  const cacheAbs = join28(ctx.projectDir, cache.path);
62189
62481
  if (existsSync51(cacheAbs) && !ctx.force) {
62190
- await mkdir26(dirname22(abs), { recursive: true });
62482
+ await mkdir27(dirname23(abs), { recursive: true });
62191
62483
  await copyFile5(cacheAbs, abs);
62192
62484
  await writeAssetMetadata({
62193
62485
  projectDir: ctx.projectDir,
@@ -62264,7 +62556,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
62264
62556
  }
62265
62557
  }
62266
62558
  if (result.status === "completed" && existsSync51(abs)) {
62267
- await mkdir26(dirname22(cacheAbs), { recursive: true });
62559
+ await mkdir27(dirname23(cacheAbs), { recursive: true });
62268
62560
  await copyFile5(abs, cacheAbs);
62269
62561
  await writeAssetMetadata({
62270
62562
  projectDir: ctx.projectDir,
@@ -62366,7 +62658,7 @@ async function dispatchMusic(beat, ctx) {
62366
62658
  }
62367
62659
  const cacheAbs = join28(ctx.projectDir, cache.path);
62368
62660
  if (existsSync51(cacheAbs) && !ctx.force) {
62369
- await mkdir26(dirname22(abs), { recursive: true });
62661
+ await mkdir27(dirname23(abs), { recursive: true });
62370
62662
  await copyFile5(cacheAbs, abs);
62371
62663
  await writeAssetMetadata({
62372
62664
  projectDir: ctx.projectDir,
@@ -62444,7 +62736,7 @@ async function dispatchMusic(beat, ctx) {
62444
62736
  ctx.onProgress({ type: "music-failed", beatId: beat.id, error });
62445
62737
  return { status: "failed", error };
62446
62738
  }
62447
- await mkdir26(dirname22(cacheAbs), { recursive: true });
62739
+ await mkdir27(dirname23(cacheAbs), { recursive: true });
62448
62740
  await copyFile5(abs, cacheAbs);
62449
62741
  await writeAssetMetadata({
62450
62742
  projectDir: ctx.projectDir,
@@ -62478,12 +62770,12 @@ function loadSceneBuildEnv(projectDir) {
62478
62770
  (0, import_dotenv4.config)({ path: join28(projectDir, ".env"), quiet: true });
62479
62771
  (0, import_dotenv4.config)({ path: resolve63(process.cwd(), ".env"), quiet: true });
62480
62772
  let dir = process.cwd();
62481
- while (dir !== dirname22(dir)) {
62773
+ while (dir !== dirname23(dir)) {
62482
62774
  if (existsSync51(join28(dir, "pnpm-workspace.yaml"))) {
62483
62775
  (0, import_dotenv4.config)({ path: join28(dir, ".env"), quiet: true });
62484
62776
  return;
62485
62777
  }
62486
- dir = dirname22(dir);
62778
+ dir = dirname23(dir);
62487
62779
  }
62488
62780
  }
62489
62781
  async function skipped(kind, beatId, reason, ctx) {
@@ -62708,14 +63000,6 @@ async function safeAudioDuration(absPath) {
62708
63000
  return void 0;
62709
63001
  }
62710
63002
  }
62711
- function resolveBuildVideoProvider(value) {
62712
- const provider = String(value ?? "seedance").toLowerCase();
62713
- if (provider === "fal") return "seedance";
62714
- if (provider === "seedance" || provider === "grok" || provider === "kling" || provider === "runway" || provider === "veo") {
62715
- return provider;
62716
- }
62717
- return "seedance";
62718
- }
62719
63003
  function resolveBuildMusicProvider(value) {
62720
63004
  const provider = String(value ?? "elevenlabs").toLowerCase();
62721
63005
  return provider === "replicate" ? "replicate" : "elevenlabs";
@@ -62727,8 +63011,7 @@ function stringOrUndefined4(value) {
62727
63011
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
62728
63012
  }
62729
63013
  async function apiKeyForVideoProvider(provider, projectDir) {
62730
- const providerKey = provider === "seedance" ? "fal" : provider === "grok" ? "xai" : provider === "veo" ? "google" : provider;
62731
- return getApiKeyFromConfig(providerKey, { cwd: projectDir });
63014
+ return getApiKeyFromConfig(videoProviderConfigKey(provider), { cwd: projectDir });
62732
63015
  }
62733
63016
  async function finalizeBuildResult(projectDir, startedAt, result, reportResult = result) {
62734
63017
  const reportPath = join28(projectDir, "build-report.json");
@@ -62751,7 +63034,7 @@ async function finalizeBuildResult(projectDir, startedAt, result, reportResult =
62751
63034
  const withMeta = decorate(result);
62752
63035
  const reportMeta = reportResult === result ? withMeta : decorate(reportResult);
62753
63036
  try {
62754
- await writeFile46(
63037
+ await writeFile45(
62755
63038
  reportPath,
62756
63039
  JSON.stringify(toBuildReport(projectDir, reportMeta), null, 2) + "\n",
62757
63040
  "utf-8"
@@ -63004,6 +63287,8 @@ var init_scene_build = __esm({
63004
63287
  init_scene_repair();
63005
63288
  init_tts_resolve();
63006
63289
  init_scene_build_mode();
63290
+ init_image_file();
63291
+ init_build_video_providers();
63007
63292
  init_scene_build_mode();
63008
63293
  BEAT_PRIMITIVES_CONCURRENCY = 4;
63009
63294
  }
@@ -63078,8 +63363,8 @@ __export(ai_audio_exports, {
63078
63363
  executeTranscribe: () => executeTranscribe,
63079
63364
  executeVoiceClone: () => executeVoiceClone
63080
63365
  });
63081
- import { resolve as resolve73, dirname as dirname24, basename as basename22, extname as extname14 } from "node:path";
63082
- import { readFile as readFile46, writeFile as writeFile47 } from "node:fs/promises";
63366
+ import { resolve as resolve73, dirname as dirname25, basename as basename22, extname as extname15 } from "node:path";
63367
+ import { readFile as readFile46, writeFile as writeFile46 } from "node:fs/promises";
63083
63368
  import { existsSync as existsSync56 } from "node:fs";
63084
63369
  async function executeTranscribe(options) {
63085
63370
  const { audioPath, language, output, format, apiKey } = options;
@@ -63101,7 +63386,7 @@ async function executeTranscribe(options) {
63101
63386
  outputPath = resolve73(process.cwd(), output);
63102
63387
  const fmt = detectFormat(output, format);
63103
63388
  const content = formatTranscript(result, fmt);
63104
- await writeFile47(outputPath, content, "utf-8");
63389
+ await writeFile46(outputPath, content, "utf-8");
63105
63390
  }
63106
63391
  return {
63107
63392
  success: true,
@@ -63129,7 +63414,7 @@ async function executeIsolate(options) {
63129
63414
  return { success: false, error: result.error || "Audio isolation failed" };
63130
63415
  }
63131
63416
  const outputPath = resolve73(process.cwd(), output);
63132
- await writeFile47(outputPath, result.audioBuffer);
63417
+ await writeFile46(outputPath, result.audioBuffer);
63133
63418
  return { success: true, outputPath };
63134
63419
  } catch (error) {
63135
63420
  return { success: false, error: `Audio isolation failed: ${error instanceof Error ? error.message : String(error)}` };
@@ -63175,11 +63460,11 @@ async function executeDub(options) {
63175
63460
  if (!openaiKey) return { success: false, error: "OPENAI_API_KEY required for Whisper transcription" };
63176
63461
  if (!anthropicKey) return { success: false, error: "ANTHROPIC_API_KEY required for Claude translation" };
63177
63462
  if (!analyzeOnly && !elevenlabsKey) return { success: false, error: "ELEVENLABS_API_KEY required for TTS" };
63178
- const ext = extname14(absPath).toLowerCase();
63463
+ const ext = extname15(absPath).toLowerCase();
63179
63464
  const isVideo = [".mp4", ".mov", ".avi", ".mkv", ".webm"].includes(ext);
63180
63465
  let audioPath = absPath;
63181
63466
  if (isVideo) {
63182
- const tempAudioPath = resolve73(dirname24(absPath), `temp-audio-${Date.now()}.mp3`);
63467
+ const tempAudioPath = resolve73(dirname25(absPath), `temp-audio-${Date.now()}.mp3`);
63183
63468
  execSafeSync("ffmpeg", ["-i", absPath, "-vn", "-acodec", "mp3", "-y", tempAudioPath]);
63184
63469
  audioPath = tempAudioPath;
63185
63470
  }
@@ -63240,7 +63525,7 @@ ${segmentTexts}`,
63240
63525
  translated: translatedSegments[i]?.text || s.text
63241
63526
  }))
63242
63527
  };
63243
- await writeFile47(timingPath, JSON.stringify(timingData, null, 2));
63528
+ await writeFile46(timingPath, JSON.stringify(timingData, null, 2));
63244
63529
  }
63245
63530
  return {
63246
63531
  success: true,
@@ -63260,10 +63545,10 @@ ${segmentTexts}`,
63260
63545
  }
63261
63546
  }
63262
63547
  const combinedBuffer = Buffer.concat(dubbedBuffers);
63263
- const outputExt = isVideo ? ".mp3" : extname14(absPath);
63264
- const defaultOutputPath2 = resolve73(dirname24(absPath), `${basename22(absPath, extname14(absPath))}-${language}${outputExt}`);
63548
+ const outputExt = isVideo ? ".mp3" : extname15(absPath);
63549
+ const defaultOutputPath2 = resolve73(dirname25(absPath), `${basename22(absPath, extname15(absPath))}-${language}${outputExt}`);
63265
63550
  const finalOutputPath = resolve73(process.cwd(), output || defaultOutputPath2);
63266
- await writeFile47(finalOutputPath, combinedBuffer);
63551
+ await writeFile46(finalOutputPath, combinedBuffer);
63267
63552
  if (isVideo && audioPath !== absPath) {
63268
63553
  try {
63269
63554
  const { unlink: unlink3 } = await import("node:fs/promises");
@@ -63298,7 +63583,7 @@ async function executeDuck(options) {
63298
63583
  const absVoicePath = resolve73(process.cwd(), voicePath);
63299
63584
  if (!existsSync56(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
63300
63585
  if (!existsSync56(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
63301
- const defaultOutput = resolve73(dirname24(absMusicPath), `${basename22(absMusicPath, extname14(absMusicPath))}-ducked${extname14(absMusicPath)}`);
63586
+ const defaultOutput = resolve73(dirname25(absMusicPath), `${basename22(absMusicPath, extname15(absMusicPath))}-ducked${extname15(absMusicPath)}`);
63302
63587
  const outputPath = resolve73(process.cwd(), output || defaultOutput);
63303
63588
  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`;
63304
63589
  await execSafe("ffmpeg", [
@@ -63328,7 +63613,7 @@ var init_ai_audio = __esm({
63328
63613
 
63329
63614
  // src/pipeline/executor.ts
63330
63615
  import { resolve as resolve74 } from "node:path";
63331
- import { readFile as readFile47, writeFile as writeFile48, mkdir as mkdir28 } from "node:fs/promises";
63616
+ import { readFile as readFile47, writeFile as writeFile47, mkdir as mkdir29 } from "node:fs/promises";
63332
63617
  import { existsSync as existsSync57 } from "node:fs";
63333
63618
  function maxCostFor(action) {
63334
63619
  const cmd = ACTION_METADATA[action]?.command;
@@ -63366,7 +63651,7 @@ async function resolvePipelineVideoProvider(provider) {
63366
63651
  if (explicit === "fal") return "seedance";
63367
63652
  if (explicit) return explicit;
63368
63653
  await loadProviderDefaults();
63369
- return resolveProvider("video")?.name ?? "grok";
63654
+ return resolveBuildVideoProvider(resolveProvider("video")?.name ?? "grok");
63370
63655
  }
63371
63656
  function providerKeyForImage(provider) {
63372
63657
  switch (provider) {
@@ -63392,6 +63677,7 @@ function providerKeyForVideo(provider) {
63392
63677
  case "runway":
63393
63678
  return "runway";
63394
63679
  case "veo":
63680
+ case "omni":
63395
63681
  return "google";
63396
63682
  default:
63397
63683
  return void 0;
@@ -63642,7 +63928,7 @@ async function loadPipeline(filePath) {
63642
63928
  async function executePipeline(manifest2, options = {}) {
63643
63929
  await ensureActionsRegistered();
63644
63930
  const outputDir = resolve74(process.cwd(), options.outputDir || `${manifest2.name}-output`);
63645
- await mkdir28(outputDir, { recursive: true });
63931
+ await mkdir29(outputDir, { recursive: true });
63646
63932
  const completedSteps = /* @__PURE__ */ new Map();
63647
63933
  const results = [];
63648
63934
  const startTime = Date.now();
@@ -63767,7 +64053,7 @@ async function executePipeline(manifest2, options = {}) {
63767
64053
  data: s.data
63768
64054
  }))
63769
64055
  };
63770
- await writeFile48(
64056
+ await writeFile47(
63771
64057
  resolve74(outputDir, CHECKPOINT_FILE),
63772
64058
  (0, import_yaml8.stringify)(checkpoint, { indent: 2 }),
63773
64059
  "utf-8"
@@ -63824,6 +64110,7 @@ var init_executor = __esm({
63824
64110
  init_output();
63825
64111
  init_config();
63826
64112
  init_provider_resolver();
64113
+ init_build_video_providers();
63827
64114
  ACTION_METADATA = {
63828
64115
  "generate-image": { id: "generate-image", title: "Generate image", category: "generate", command: "generate image", outputs: ["image"] },
63829
64116
  "generate-video": { id: "generate-video", title: "Generate video", category: "generate", command: "generate video", outputs: ["video"], requiredKeys: ["provider-dependent"] },
@@ -68387,6 +68674,7 @@ var init_scene2 = __esm({
68387
68674
  init_compose_prompts();
68388
68675
  init_scene_repair();
68389
68676
  init_scene_submit();
68677
+ init_build_video_providers();
68390
68678
  SCENE_PRESETS2 = [
68391
68679
  "simple",
68392
68680
  "announcement",
@@ -68718,12 +69006,12 @@ var init_scene2 = __esm({
68718
69006
  "TTS provider override. Default 'auto' (elevenlabs key > openai key > local kokoro)."
68719
69007
  ),
68720
69008
  voice: external_exports.string().optional().describe("TTS voice id (provider-specific)."),
68721
- imageProvider: external_exports.enum(["openai"]).optional().describe("Image provider for backdrops. Default 'openai' (gpt-image-2)."),
68722
- videoProvider: external_exports.enum(["seedance", "grok", "kling", "runway", "veo"]).optional().describe("Video provider for per-beat video cues. Default seedance."),
69009
+ imageProvider: external_exports.enum(["openai"]).optional().describe("Image provider for backdrops. Default 'openai' (gpt-image-2.5-sunburst)."),
69010
+ videoProvider: external_exports.enum(BUILD_VIDEO_PROVIDERS).optional().describe("Video provider for per-beat video cues. Default seedance."),
68723
69011
  musicProvider: external_exports.enum(["elevenlabs", "replicate"]).optional().describe("Music provider for per-beat music cues. Default elevenlabs."),
68724
69012
  imageQuality: external_exports.enum(["standard", "hd"]).optional().describe("OpenAI image quality. Default 'standard'."),
68725
69013
  imageModel: external_exports.string().optional().describe(
68726
- "Image model for keyframes/backdrops/character sheets (gemini: flash|pro, openai: gpt-image-2). Provider default when omitted."
69014
+ "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."
68727
69015
  ),
68728
69016
  imageSize: external_exports.enum(["1024x1024", "1536x1024", "1024x1536"]).optional().describe("OpenAI image size. Default '1536x1024' (cinematic 16:9-ish)."),
68729
69017
  maxCostUsd: external_exports.number().optional().describe("Fail before provider spend when estimated cost exceeds this USD cap."),
@@ -69490,7 +69778,7 @@ var init_edit = __esm({
69490
69778
  imagePaths: external_exports.array(external_exports.string()).describe("Input image file path(s)"),
69491
69779
  prompt: external_exports.string().describe("Edit instruction"),
69492
69780
  output: external_exports.string().optional().describe("Output file path (default: edited.png)"),
69493
- model: external_exports.enum(["flash", "3.1-flash", "latest", "pro"]).optional().describe("Gemini model (default: flash)"),
69781
+ model: external_exports.enum(["flash", "3.1-flash", "latest", "lite", "pro"]).optional().describe("Gemini model (default: flash = Nano Banana 2)"),
69494
69782
  ratio: external_exports.string().optional().describe("Output aspect ratio"),
69495
69783
  resolution: external_exports.string().optional().describe("Resolution: 1K, 2K, 4K (Pro only)")
69496
69784
  }),
@@ -69570,7 +69858,7 @@ var init_edit = __esm({
69570
69858
  });
69571
69859
 
69572
69860
  // src/commands/ai-suggest-edit.ts
69573
- import { readFile as readFile48, writeFile as writeFile49 } from "node:fs/promises";
69861
+ import { readFile as readFile48, writeFile as writeFile48 } from "node:fs/promises";
69574
69862
  import { resolve as resolve76 } from "node:path";
69575
69863
  async function executeSuggestEdit(options) {
69576
69864
  try {
@@ -69588,7 +69876,7 @@ async function executeSuggestEdit(options) {
69588
69876
  const first = suggestions[0];
69589
69877
  const applied = applySuggestion(project, first);
69590
69878
  if (applied) {
69591
- await writeFile49(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
69879
+ await writeFile48(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
69592
69880
  return {
69593
69881
  success: true,
69594
69882
  suggestions,
@@ -69893,7 +70181,7 @@ var init_generate2 = __esm({
69893
70181
  schema: external_exports.object({
69894
70182
  prompt: external_exports.string().describe("Description of the sound effect"),
69895
70183
  output: external_exports.string().optional().describe("Output audio file path (default: sound-effect.mp3)"),
69896
- duration: external_exports.number().optional().describe("Duration in seconds (0.5-22, default: auto)"),
70184
+ duration: external_exports.number().optional().describe("Duration in seconds (0.5-30, default: auto)"),
69897
70185
  promptInfluence: external_exports.number().optional().describe("Prompt influence 0-1 (default: 0.3)")
69898
70186
  }),
69899
70187
  async execute(args) {
@@ -69970,7 +70258,7 @@ var init_generate2 = __esm({
69970
70258
  ratio: external_exports.string().optional().describe("Aspect ratio for Gemini (1:1, 16:9, 9:16, 4:3, 3:4, etc.)"),
69971
70259
  quality: external_exports.string().optional().describe("Quality for OpenAI: standard, hd"),
69972
70260
  count: external_exports.number().optional().describe("Number of images (default: 1)"),
69973
- model: external_exports.string().optional().describe("Gemini model: flash, 3.1-flash, latest, pro")
70261
+ model: external_exports.string().optional().describe("Image model. Gemini: flash (default), lite, pro. OpenAI: 2.5 (default), flare, 2, 1.5. Grok: pro")
69974
70262
  }),
69975
70263
  async execute(args) {
69976
70264
  const result = await executeImageGenerate(args);
@@ -70033,9 +70321,11 @@ var init_generate2 = __esm({
70033
70321
  mode: external_exports.string().optional().describe("Kling mode: std or pro"),
70034
70322
  negative: external_exports.string().optional().describe("Negative prompt (Seedance/Kling/Veo)"),
70035
70323
  resolution: external_exports.string().optional().describe("Resolution: 480p, 720p, 1080p, or 4k depending on provider"),
70036
- veoModel: external_exports.string().optional().describe("Veo model: 3.0, 3.1, 3.1-fast"),
70324
+ veoModel: external_exports.string().optional().describe("Veo model: 3.1, 3.1-fast"),
70037
70325
  runwayModel: external_exports.string().optional().describe("Runway model: gen4.5, gen4_turbo"),
70038
- seedanceModel: external_exports.string().optional().describe("Seedance variant: quality or fast (fal.ai only)"),
70326
+ seedanceModel: external_exports.string().optional().describe("Seedance variant: quality (2.0), fast, or 2.5 (fal.ai only)"),
70327
+ grokModel: external_exports.string().optional().describe("Grok video model: 1.5 (default), lite, classic"),
70328
+ klingModel: external_exports.string().optional().describe("Kling model: v3 (default), v2.6, v2.5-turbo"),
70039
70329
  generateAudio: external_exports.boolean().optional().describe("Generate native synchronized audio when supported"),
70040
70330
  output: external_exports.string().optional().describe("Output file path (downloads video)"),
70041
70331
  wait: external_exports.boolean().optional().describe("Wait for completion (default: true)")
@@ -70107,7 +70397,7 @@ var init_generate2 = __esm({
70107
70397
  prompt: external_exports.string().optional().describe("Continuation prompt"),
70108
70398
  duration: external_exports.number().optional().describe("Duration in seconds"),
70109
70399
  negative: external_exports.string().optional().describe("Negative prompt (Kling)"),
70110
- veoModel: external_exports.string().optional().describe("Veo model: 3.0, 3.1, 3.1-fast"),
70400
+ veoModel: external_exports.string().optional().describe("Veo model: 3.1, 3.1-fast"),
70111
70401
  output: external_exports.string().optional().describe("Output file path"),
70112
70402
  wait: external_exports.boolean().optional().describe("Wait for completion (default: true)")
70113
70403
  }),
@@ -70145,7 +70435,7 @@ var init_generate2 = __esm({
70145
70435
 
70146
70436
  // src/tools/manifest/storyboard.ts
70147
70437
  import { existsSync as existsSync58 } from "node:fs";
70148
- import { readFile as readFile49, writeFile as writeFile50 } from "node:fs/promises";
70438
+ import { readFile as readFile49, writeFile as writeFile49 } from "node:fs/promises";
70149
70439
  import { resolve as resolve78 } from "node:path";
70150
70440
  async function readStoryboard2(projectDir) {
70151
70441
  const path2 = resolve78(projectDir, "STORYBOARD.md");
@@ -70162,6 +70452,7 @@ var init_storyboard2 = __esm({
70162
70452
  init_storyboard_edit();
70163
70453
  init_storyboard_revise();
70164
70454
  init_build_plan();
70455
+ init_build_video_providers();
70165
70456
  PROJECT_DIR_DESCRIPTION3 = "Project directory. Defaults to the surface's cwd; in MCP hosts, relative paths resolve under the configured server workspace.";
70166
70457
  projectDirSchema = external_exports.object({
70167
70458
  projectDir: external_exports.string().optional().describe(PROJECT_DIR_DESCRIPTION3)
@@ -70277,7 +70568,7 @@ var init_storyboard2 = __esm({
70277
70568
  value,
70278
70569
  unset: args.unset
70279
70570
  });
70280
- await writeFile50(file.path, next, "utf-8");
70571
+ await writeFile49(file.path, next, "utf-8");
70281
70572
  return {
70282
70573
  success: true,
70283
70574
  data: { beat: args.beat, key: args.key },
@@ -70309,7 +70600,7 @@ var init_storyboard2 = __esm({
70309
70600
  beatId: args.beat,
70310
70601
  afterBeatId: args.after
70311
70602
  });
70312
- await writeFile50(file.path, next, "utf-8");
70603
+ await writeFile49(file.path, next, "utf-8");
70313
70604
  return {
70314
70605
  success: true,
70315
70606
  data: { beat: args.beat, after: args.after },
@@ -70374,7 +70665,7 @@ var init_storyboard2 = __esm({
70374
70665
  imageProvider: external_exports.string().optional().describe("Image provider override."),
70375
70666
  imageQuality: external_exports.enum(["standard", "hd"]).optional().describe("Image quality override."),
70376
70667
  imageSize: external_exports.string().optional().describe("Image size override."),
70377
- videoProvider: external_exports.enum(["seedance", "grok", "kling", "runway", "veo"]).optional().describe("Video provider override."),
70668
+ videoProvider: external_exports.enum(BUILD_VIDEO_PROVIDERS).optional().describe("Video provider override."),
70378
70669
  musicProvider: external_exports.enum(["elevenlabs", "replicate"]).optional().describe("Music provider override."),
70379
70670
  composer: external_exports.enum(["claude", "openai", "gemini"]).optional().describe("Composer provider override."),
70380
70671
  force: external_exports.boolean().optional().describe("Plan regeneration even when outputs exist.")
@@ -70472,7 +70763,7 @@ var init_design = __esm({
70472
70763
  });
70473
70764
 
70474
70765
  // src/tools/manifest/remix.ts
70475
- import { writeFile as writeFile51 } from "node:fs/promises";
70766
+ import { writeFile as writeFile50 } from "node:fs/promises";
70476
70767
  import { tmpdir as tmpdir4 } from "node:os";
70477
70768
  import { join as join31 } from "node:path";
70478
70769
  var pipelineHighlightsTool, pipelineAutoShortsTool, pipelineRunTool, remixTools;
@@ -70588,7 +70879,7 @@ var init_remix = __esm({
70588
70879
  let tempPath;
70589
70880
  if (args.pipelineYaml) {
70590
70881
  tempPath = join31(tmpdir4(), `vibe-mcp-pipeline-${Date.now()}.yaml`);
70591
- await writeFile51(tempPath, args.pipelineYaml, "utf-8");
70882
+ await writeFile50(tempPath, args.pipelineYaml, "utf-8");
70592
70883
  resolvedPath = tempPath;
70593
70884
  }
70594
70885
  try {
@@ -70733,7 +71024,7 @@ var init_detect2 = __esm({
70733
71024
  });
70734
71025
 
70735
71026
  // src/tools/manifest/_project-io.ts
70736
- import { readFile as readFile51, writeFile as writeFile52 } from "node:fs/promises";
71027
+ import { readFile as readFile51, writeFile as writeFile51 } from "node:fs/promises";
70737
71028
  async function loadProject2(projectPath, cwd) {
70738
71029
  const absPath = await resolveTimelineFile(projectPath, cwd);
70739
71030
  const content = await readFile51(absPath, "utf-8");
@@ -70741,7 +71032,7 @@ async function loadProject2(projectPath, cwd) {
70741
71032
  return { project: Project.fromJSON(data), absPath };
70742
71033
  }
70743
71034
  async function saveProject(absPath, project) {
70744
- await writeFile52(absPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
71035
+ await writeFile51(absPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
70745
71036
  }
70746
71037
  var init_project_io = __esm({
70747
71038
  "src/tools/manifest/_project-io.ts"() {
@@ -70752,7 +71043,7 @@ var init_project_io = __esm({
70752
71043
  });
70753
71044
 
70754
71045
  // src/tools/manifest/timeline.ts
70755
- import { mkdir as mkdir29, writeFile as writeFile53 } from "node:fs/promises";
71046
+ import { mkdir as mkdir30, writeFile as writeFile52 } from "node:fs/promises";
70756
71047
  import { basename as basename23, resolve as resolve80 } from "node:path";
70757
71048
  var MEDIA_TYPES, timelineCreateTool, timelineInfoTool, timelineAddSourceTool, timelineAddClipTool, timelineSplitClipTool, timelineTrimClipTool, timelineMoveClipTool, timelineDeleteClipTool, timelineDuplicateClipTool, timelineAddEffectTool, timelineAddTrackTool, timelineListTool, timelineTools;
70758
71049
  var init_timeline = __esm({
@@ -70799,10 +71090,10 @@ var init_timeline = __esm({
70799
71090
  outputPath = resolve80(ctx.workingDirectory, args.outputPath);
70800
71091
  } else {
70801
71092
  const dirPath = resolve80(ctx.workingDirectory, args.name);
70802
- await mkdir29(dirPath, { recursive: true });
71093
+ await mkdir30(dirPath, { recursive: true });
70803
71094
  outputPath = resolve80(dirPath, TIMELINE_FILENAME);
70804
71095
  }
70805
- await writeFile53(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
71096
+ await writeFile52(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
70806
71097
  return {
70807
71098
  success: true,
70808
71099
  data: { name: projectName, outputPath },
@@ -71206,7 +71497,7 @@ var init_project_list = __esm({
71206
71497
 
71207
71498
  // src/tools/manifest/project.ts
71208
71499
  import { resolve as resolve82 } from "node:path";
71209
- import { writeFile as writeFile54 } from "node:fs/promises";
71500
+ import { writeFile as writeFile53 } from "node:fs/promises";
71210
71501
  var projectListTool, projectCreateTool, projectInfoTool, projectTools;
71211
71502
  var init_project2 = __esm({
71212
71503
  "src/tools/manifest/project.ts"() {
@@ -71270,7 +71561,7 @@ var init_project2 = __esm({
71270
71561
  const outputPath = resolve82(ctx.workingDirectory, args.outputPath ?? `${args.name}.vibe.json`);
71271
71562
  const project = new Project(args.name);
71272
71563
  if (args.fps) project.setFrameRate(args.fps);
71273
- await writeFile54(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
71564
+ await writeFile53(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
71274
71565
  return {
71275
71566
  success: true,
71276
71567
  data: { name: args.name, outputPath },
@@ -72005,8 +72296,8 @@ var init_export2 = __esm({
72005
72296
  });
72006
72297
 
72007
72298
  // src/tools/manifest/agent-only.ts
72008
- import { readFile as readFile54, writeFile as writeFile55, readdir as readdir9, stat as stat10, access as access8, unlink as unlink2 } from "node:fs/promises";
72009
- import { resolve as resolve85, join as join33, basename as basename24, extname as extname15 } from "node:path";
72299
+ import { readFile as readFile54, writeFile as writeFile54, readdir as readdir9, stat as stat10, access as access8, unlink as unlink2 } from "node:fs/promises";
72300
+ import { resolve as resolve85, join as join33, basename as basename24, extname as extname16 } from "node:path";
72010
72301
  function matchPattern(filename, pattern) {
72011
72302
  const regex2 = new RegExp(
72012
72303
  "^" + pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".") + "$",
@@ -72025,7 +72316,7 @@ function formatSize(bytes) {
72025
72316
  return `${size.toFixed(1)}${units2[unit]}`;
72026
72317
  }
72027
72318
  function detectMediaType4(filePath) {
72028
- const ext = extname15(filePath).toLowerCase();
72319
+ const ext = extname16(filePath).toLowerCase();
72029
72320
  if ([".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"].includes(ext)) return "video";
72030
72321
  if ([".mp3", ".wav", ".aac", ".flac", ".ogg", ".m4a"].includes(ext)) return "audio";
72031
72322
  if ([".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"].includes(ext)) return "image";
@@ -72052,7 +72343,7 @@ function isMediaFile2(filePath) {
72052
72343
  ".webp",
72053
72344
  ".bmp"
72054
72345
  ];
72055
- return mediaExts.includes(extname15(filePath).toLowerCase());
72346
+ return mediaExts.includes(extname16(filePath).toLowerCase());
72056
72347
  }
72057
72348
  async function getMediaDuration2(filePath) {
72058
72349
  try {
@@ -72145,7 +72436,7 @@ var init_agent_only = __esm({
72145
72436
  async execute(args, ctx) {
72146
72437
  try {
72147
72438
  const absPath = resolve85(ctx.workingDirectory, args.path);
72148
- await writeFile55(absPath, args.content, "utf-8");
72439
+ await writeFile54(absPath, args.content, "utf-8");
72149
72440
  return { success: true, data: { bytes: args.content.length }, humanLines: [`File written: ${args.path} (${formatSize(args.content.length)})`] };
72150
72441
  } catch (error) {
72151
72442
  return { success: false, error: `Failed to write file: ${error instanceof Error ? error.message : String(error)}` };
@@ -72208,7 +72499,7 @@ var init_agent_only = __esm({
72208
72499
  if (entry.isDirectory() && recursive) {
72209
72500
  await scanDir(entryPath);
72210
72501
  } else if (entry.isFile()) {
72211
- const ext = extname15(entry.name).toLowerCase();
72502
+ const ext = extname16(entry.name).toLowerCase();
72212
72503
  const matchesFilter = !filterExts || filterExts.includes(ext);
72213
72504
  if (matchesFilter && isMediaFile2(entryPath)) mediaFiles.push(entryPath);
72214
72505
  }
@@ -72231,7 +72522,7 @@ var init_agent_only = __esm({
72231
72522
  const source = project.addSource({ name: mediaName, type: mediaType, url: mediaFile, duration });
72232
72523
  addedSources.push({ id: source.id, name: mediaName, type: mediaType });
72233
72524
  }
72234
- await writeFile55(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
72525
+ await writeFile54(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
72235
72526
  return {
72236
72527
  success: true,
72237
72528
  data: { count: addedSources.length, sources: addedSources },
@@ -72301,7 +72592,7 @@ var init_agent_only = __esm({
72301
72592
  createdClips.push({ id: clip.id, sourceName: source.name, startTime: currentTime, duration: source.duration });
72302
72593
  currentTime += source.duration + gap;
72303
72594
  }
72304
- await writeFile55(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
72595
+ await writeFile54(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
72305
72596
  const totalDuration = currentTime - gap - startTime;
72306
72597
  return {
72307
72598
  success: true,
@@ -72364,7 +72655,7 @@ var init_agent_only = __esm({
72364
72655
  });
72365
72656
  if (effect) appliedEffects.push({ clipId: clip.id, effectId: effect.id });
72366
72657
  }
72367
- await writeFile55(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
72658
+ await writeFile54(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
72368
72659
  return {
72369
72660
  success: true,
72370
72661
  data: { applied: appliedEffects.length, effectType: args.effectType },
@@ -72433,7 +72724,7 @@ var init_agent_only = __esm({
72433
72724
  removed.sources++;
72434
72725
  }
72435
72726
  }
72436
- await writeFile55(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
72727
+ await writeFile54(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
72437
72728
  const parts = [];
72438
72729
  if (removed.clips > 0) parts.push(`${removed.clips} clips`);
72439
72730
  if (removed.tracks > 0) parts.push(`${removed.tracks} tracks`);
@@ -72484,7 +72775,7 @@ var init_agent_only = __esm({
72484
72775
  project.setFrameRate(args.fps);
72485
72776
  updates.push(`Frame Rate: ${args.fps} fps`);
72486
72777
  }
72487
- await writeFile55(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
72778
+ await writeFile54(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
72488
72779
  return {
72489
72780
  success: true,
72490
72781
  data: { updates },
@@ -72854,7 +73145,7 @@ Audio:`);
72854
73145
  } else {
72855
73146
  const tempList = resolve85(ctx.workingDirectory, `concat-list-${Date.now()}.txt`);
72856
73147
  const listContent = args.inputs.map((i) => `file '${resolve85(ctx.workingDirectory, i)}'`).join("\n");
72857
- await writeFile55(tempList, listContent, "utf-8");
73148
+ await writeFile54(tempList, listContent, "utf-8");
72858
73149
  try {
72859
73150
  await execSafe(
72860
73151
  "ffmpeg",
@@ -73297,7 +73588,7 @@ init_source();
73297
73588
  // package.json
73298
73589
  var package_default = {
73299
73590
  name: "@vibeframe/cli",
73300
- version: "0.115.2",
73591
+ version: "0.115.3",
73301
73592
  description: "Frontier AI video generation for coding agents - Seedance, Runway, Veo, and Kling on your own keys, behind a hard cost cap",
73302
73593
  private: false,
73303
73594
  license: "MIT",
@@ -74401,7 +74692,7 @@ init_validate();
74401
74692
  init_cost_tier();
74402
74693
  init_scene_inspect();
74403
74694
  init_render_inspect();
74404
- import { readFile as readFile29, writeFile as writeFile33 } from "node:fs/promises";
74695
+ import { readFile as readFile29, writeFile as writeFile32 } from "node:fs/promises";
74405
74696
  import { resolve as resolve40 } from "node:path";
74406
74697
  var VALID_RENDER_AI_MODELS = [
74407
74698
  "flash",
@@ -74716,7 +75007,7 @@ inspectCommand.command("suggest").description("Get AI edit suggestions using Gem
74716
75007
  const sug = suggestions[0];
74717
75008
  const applied = applySuggestion(project, sug);
74718
75009
  if (applied) {
74719
- await writeFile33(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
75010
+ await writeFile32(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
74720
75011
  spinner2.succeed(source_default.green("Suggestion applied"));
74721
75012
  } else {
74722
75013
  spinner2.warn(source_default.yellow("Could not apply suggestion automatically"));
@@ -74741,8 +75032,8 @@ init_cost_tier();
74741
75032
  init_ai_helpers();
74742
75033
  init_output();
74743
75034
  init_validate();
74744
- import { resolve as resolve41, dirname as dirname13, basename as basename12, extname as extname8 } from "node:path";
74745
- import { readFile as readFile30, writeFile as writeFile34 } from "node:fs/promises";
75035
+ import { resolve as resolve41, dirname as dirname14, basename as basename12, extname as extname9 } from "node:path";
75036
+ import { readFile as readFile30, writeFile as writeFile33 } from "node:fs/promises";
74746
75037
  import { existsSync as existsSync35 } from "node:fs";
74747
75038
  var audioCommand = new Command("audio").alias("au").description("Audio operations (transcribe, dub, duck, isolate, voice clone)").addHelpText(
74748
75039
  "after",
@@ -74820,7 +75111,7 @@ audioCommand.command("transcribe").description("Transcribe audio using Whisper")
74820
75111
  const outputPath = resolve41(process.cwd(), options.output);
74821
75112
  const format = detectFormat(options.output, options.format);
74822
75113
  const content = formatTranscript(result, format);
74823
- await writeFile34(outputPath, content, "utf-8");
75114
+ await writeFile33(outputPath, content, "utf-8");
74824
75115
  console.log(source_default.green(`Saved ${format.toUpperCase()} to: ${outputPath}`));
74825
75116
  }
74826
75117
  } catch (error) {
@@ -74894,7 +75185,7 @@ audioCommand.command("isolate").description("Isolate vocals from audio using Ele
74894
75185
  exitWithError(apiError(result.error || "Audio isolation failed", true));
74895
75186
  }
74896
75187
  const outputPath = resolve41(process.cwd(), options.output);
74897
- await writeFile34(outputPath, result.audioBuffer);
75188
+ await writeFile33(outputPath, result.audioBuffer);
74898
75189
  spinner2.succeed(source_default.green("Vocals isolated"));
74899
75190
  if (isJsonMode()) {
74900
75191
  outputSuccess({
@@ -75030,11 +75321,11 @@ audioCommand.command("dub").description("Dub audio/video to another language (tr
75030
75321
  const anthropicKey = await requireApiKey("ANTHROPIC_API_KEY", "Anthropic");
75031
75322
  const elevenlabsKey = options.analyzeOnly ? await getApiKey("ELEVENLABS_API_KEY", "ElevenLabs", void 0) : await requireApiKey("ELEVENLABS_API_KEY", "ElevenLabs");
75032
75323
  const spinner2 = ora("Extracting audio...").start();
75033
- const ext = extname8(absPath).toLowerCase();
75324
+ const ext = extname9(absPath).toLowerCase();
75034
75325
  const isVideo = [".mp4", ".mov", ".avi", ".mkv", ".webm"].includes(ext);
75035
75326
  let audioPath = absPath;
75036
75327
  if (isVideo) {
75037
- const tempAudioPath = resolve41(dirname13(absPath), `temp-audio-${Date.now()}.mp3`);
75328
+ const tempAudioPath = resolve41(dirname14(absPath), `temp-audio-${Date.now()}.mp3`);
75038
75329
  try {
75039
75330
  execSafeSync("ffmpeg", ["-i", absPath, "-vn", "-acodec", "mp3", "-y", tempAudioPath]);
75040
75331
  audioPath = tempAudioPath;
@@ -75146,7 +75437,7 @@ ${segmentTexts}`,
75146
75437
  translated: translatedSegments[i]?.text || s.text
75147
75438
  }))
75148
75439
  };
75149
- await writeFile34(timingPath, JSON.stringify(timingData, null, 2));
75440
+ await writeFile33(timingPath, JSON.stringify(timingData, null, 2));
75150
75441
  console.log(`Timing saved to: ${source_default.bold(timingPath)}`);
75151
75442
  }
75152
75443
  return;
@@ -75170,13 +75461,13 @@ ${segmentTexts}`,
75170
75461
  }
75171
75462
  spinner2.text = "Combining audio...";
75172
75463
  const combinedBuffer = Buffer.concat(dubbedAudioBuffers.map((a) => a.buffer));
75173
- const outputExt = isVideo ? ".mp3" : extname8(absPath);
75464
+ const outputExt = isVideo ? ".mp3" : extname9(absPath);
75174
75465
  const defaultOutputPath2 = resolve41(
75175
- dirname13(absPath),
75176
- `${basename12(absPath, extname8(absPath))}-${options.language}${outputExt}`
75466
+ dirname14(absPath),
75467
+ `${basename12(absPath, extname9(absPath))}-${options.language}${outputExt}`
75177
75468
  );
75178
75469
  const finalOutputPath = resolve41(process.cwd(), options.output || defaultOutputPath2);
75179
- await writeFile34(finalOutputPath, combinedBuffer);
75470
+ await writeFile33(finalOutputPath, combinedBuffer);
75180
75471
  spinner2.succeed(source_default.green("Dubbing complete"));
75181
75472
  if (isJsonMode()) {
75182
75473
  outputSuccess({
@@ -75735,7 +76026,7 @@ init_esm();
75735
76026
  init_source();
75736
76027
  init_ora();
75737
76028
  import { stat as stat7 } from "node:fs/promises";
75738
- import { resolve as resolve45, extname as extname10, basename as basename16 } from "node:path";
76029
+ import { resolve as resolve45, extname as extname11, basename as basename16 } from "node:path";
75739
76030
 
75740
76031
  // ../../node_modules/.pnpm/music-metadata@11.11.1/node_modules/music-metadata/lib/index.js
75741
76032
  init_lib();
@@ -79612,7 +79903,7 @@ mediaCommand.command("info").description("Get media file information").argument(
79612
79903
  const spinner2 = ora("Analyzing media...").start();
79613
79904
  try {
79614
79905
  const filePath = resolve45(process.cwd(), file);
79615
- const ext = extname10(filePath).toLowerCase();
79906
+ const ext = extname11(filePath).toLowerCase();
79616
79907
  const fileName = basename16(filePath);
79617
79908
  const fileStat = await stat7(filePath);
79618
79909
  const mediaType = detectMediaType2(ext);
@@ -79753,11 +80044,11 @@ init_ora();
79753
80044
  init_engine();
79754
80045
  init_output();
79755
80046
  init_cost_tier();
79756
- import { readFile as readFile32, writeFile as writeFile37, readdir as readdir7 } from "node:fs/promises";
79757
- import { resolve as resolve46, basename as basename17, extname as extname11, join as join19 } from "node:path";
80047
+ import { readFile as readFile32, writeFile as writeFile36, readdir as readdir7 } from "node:fs/promises";
80048
+ import { resolve as resolve46, basename as basename17, extname as extname12, join as join19 } from "node:path";
79758
80049
  var batchCommand = new Command("batch").description("Batch operations for processing multiple items");
79759
80050
  function detectMediaType3(filePath) {
79760
- const ext = extname11(filePath).toLowerCase();
80051
+ const ext = extname12(filePath).toLowerCase();
79761
80052
  const videoExts = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"];
79762
80053
  const audioExts = [".mp3", ".wav", ".aac", ".flac", ".ogg", ".m4a"];
79763
80054
  const imageExts = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"];
@@ -79767,7 +80058,7 @@ function detectMediaType3(filePath) {
79767
80058
  return "video";
79768
80059
  }
79769
80060
  function isMediaFile(filePath) {
79770
- const ext = extname11(filePath).toLowerCase();
80061
+ const ext = extname12(filePath).toLowerCase();
79771
80062
  const mediaExts = [
79772
80063
  ".mp4",
79773
80064
  ".mov",
@@ -79825,7 +80116,7 @@ batchCommand.command("import").description("Import multiple media files from a d
79825
80116
  if (entry.isDirectory() && options.recursive) {
79826
80117
  await scanDir(entryPath);
79827
80118
  } else if (entry.isFile()) {
79828
- const ext = extname11(entry.name).toLowerCase();
80119
+ const ext = extname12(entry.name).toLowerCase();
79829
80120
  const matchesFilter = !filterExts || filterExts.includes(ext);
79830
80121
  if (matchesFilter && isMediaFile(entryPath)) {
79831
80122
  mediaFiles.push(entryPath);
@@ -79854,7 +80145,7 @@ batchCommand.command("import").description("Import multiple media files from a d
79854
80145
  });
79855
80146
  addedSources.push(source.id);
79856
80147
  }
79857
- await writeFile37(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
80148
+ await writeFile36(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
79858
80149
  spinner2.succeed(source_default.green(`Imported ${addedSources.length} media files`));
79859
80150
  console.log();
79860
80151
  for (const file of mediaFiles) {
@@ -79929,7 +80220,7 @@ batchCommand.command("concat").description("Concatenate multiple sources into se
79929
80220
  addedClips.push(clip.id);
79930
80221
  currentTime += source.duration + gap;
79931
80222
  }
79932
- await writeFile37(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
80223
+ await writeFile36(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
79933
80224
  spinner2.succeed(source_default.green(`Created ${addedClips.length} clips`));
79934
80225
  console.log();
79935
80226
  console.log(source_default.dim(" Total duration:"), `${currentTime - gap}s`);
@@ -80000,7 +80291,7 @@ batchCommand.command("apply-effect").description("Apply an effect to multiple cl
80000
80291
  appliedCount++;
80001
80292
  }
80002
80293
  }
80003
- await writeFile37(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
80294
+ await writeFile36(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
80004
80295
  spinner2.succeed(
80005
80296
  source_default.green(`Applied ${effectType} to ${appliedCount} clips`)
80006
80297
  );
@@ -80062,7 +80353,7 @@ batchCommand.command("remove-clips").description("Remove multiple clips from the
80062
80353
  removedCount++;
80063
80354
  }
80064
80355
  }
80065
- await writeFile37(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
80356
+ await writeFile36(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
80066
80357
  spinner2.succeed(source_default.green(`Removed ${removedCount} clips`));
80067
80358
  console.log();
80068
80359
  } catch (error) {
@@ -80421,7 +80712,7 @@ var AI_FEATURES = [
80421
80712
  {
80422
80713
  label: "Images",
80423
80714
  desc: "generate + edit",
80424
- defaultProvider: "OpenAI gpt-image-2 (Artificial Analysis #1, since v0.56)",
80715
+ defaultProvider: "OpenAI gpt-image-2.5-sunburst",
80425
80716
  alsoAvailable: "Gemini Nano Banana, Grok Imagine",
80426
80717
  keyHint: "1 key",
80427
80718
  keys: [
@@ -80430,19 +80721,19 @@ var AI_FEATURES = [
80430
80721
  envVar: "OPENAI_API_KEY",
80431
80722
  name: "OpenAI",
80432
80723
  url: "https://platform.openai.com/api-keys",
80433
- what: "gpt-image-2 image generation + editing (also Whisper, Agent)"
80724
+ what: "gpt-image-2.5 image generation + editing (also Whisper, Agent)"
80434
80725
  }
80435
80726
  ],
80436
80727
  providerChoices: [
80437
80728
  {
80438
- label: "OpenAI gpt-image-2",
80729
+ label: "OpenAI gpt-image-2.5",
80439
80730
  desc: "recommended default, image generation + editing",
80440
80731
  key: {
80441
80732
  configKey: "openai",
80442
80733
  envVar: "OPENAI_API_KEY",
80443
80734
  name: "OpenAI",
80444
80735
  url: "https://platform.openai.com/api-keys",
80445
- what: "gpt-image-2 image generation + editing (also Whisper, Agent)"
80736
+ what: "gpt-image-2.5 image generation + editing (also Whisper, Agent)"
80446
80737
  },
80447
80738
  defaultFor: { kind: "image", value: "openai" }
80448
80739
  },
@@ -80943,7 +81234,7 @@ async function runSetupWizard(fullSetup = false, scope = "user") {
80943
81234
  envVar: "OPENAI_API_KEY",
80944
81235
  name: "OpenAI",
80945
81236
  url: "https://platform.openai.com/api-keys",
80946
- what: "gpt-image-2 image generation (default since v0.56) + Whisper word-level transcribe"
81237
+ what: "gpt-image-2.5 image generation + Whisper word-level transcribe"
80947
81238
  },
80948
81239
  {
80949
81240
  configKey: "fal",
@@ -81257,9 +81548,9 @@ async function runCustomSetup(config2, scope = "user", opts = {}) {
81257
81548
  const providers = ["claude", "openai", "gemini", "xai", "openrouter", "evolink", "ollama"];
81258
81549
  const providerDescriptions = {
81259
81550
  claude: "Best reasoning, most capable for complex tasks",
81260
- openai: "GPT-5-mini, reliable and fast, good default",
81551
+ openai: "GPT-5.4 mini, reliable and fast, good default",
81261
81552
  gemini: "Google AI, strong multimodal understanding",
81262
- xai: "Grok 4.1 Fast, 2M context, great for tool calling",
81553
+ xai: "Grok 4.3, 1M context, inexpensive tool calling",
81263
81554
  openrouter: "300+ models via one API key (Claude, GPT, Gemini, Llama, etc.)",
81264
81555
  evolink: "GPT-5, Claude, Gemini, DeepSeek & more via one key",
81265
81556
  ollama: "Free, local, no API key - offline capable (default: llama3.2)"
@@ -81554,8 +81845,8 @@ function pickCompletionHint(shellPath) {
81554
81845
  init_esm();
81555
81846
  init_source();
81556
81847
  import { existsSync as existsSync41 } from "node:fs";
81557
- import { mkdir as mkdir22, readFile as readFile36, writeFile as writeFile40 } from "node:fs/promises";
81558
- import { basename as basename19, dirname as dirname17, resolve as resolve50, sep } from "node:path";
81848
+ import { mkdir as mkdir23, readFile as readFile36, writeFile as writeFile39 } from "node:fs/promises";
81849
+ import { basename as basename19, dirname as dirname18, resolve as resolve50, sep } from "node:path";
81559
81850
  init_scene_project();
81560
81851
  init_visual_styles();
81561
81852
 
@@ -81722,9 +82013,9 @@ init_install_skill();
81722
82013
 
81723
82014
  // src/commands/_shared/host-integration.ts
81724
82015
  import { existsSync as existsSync40 } from "node:fs";
81725
- import { mkdir as mkdir21, readFile as readFile35, writeFile as writeFile39 } from "node:fs/promises";
82016
+ import { mkdir as mkdir22, readFile as readFile35, writeFile as writeFile38 } from "node:fs/promises";
81726
82017
  import { homedir as homedir7 } from "node:os";
81727
- import { dirname as dirname16, join as join21, resolve as resolve49 } from "node:path";
82018
+ import { dirname as dirname17, join as join21, resolve as resolve49 } from "node:path";
81728
82019
  var HOST_SELECTIONS = [
81729
82020
  "codex",
81730
82021
  "claude",
@@ -81928,15 +82219,15 @@ async function mergeMcpJson(configPath, opts) {
81928
82219
  ...mcpServers,
81929
82220
  vibeframe: opts.server
81930
82221
  };
81931
- await mkdir21(dirname16(configPath), { recursive: true });
82222
+ await mkdir22(dirname17(configPath), { recursive: true });
81932
82223
  if (exists && opts.backup) {
81933
- await writeFile39(
82224
+ await writeFile38(
81934
82225
  `${configPath}.bak-${timestamp()}`,
81935
82226
  await readFile35(configPath, "utf-8"),
81936
82227
  "utf-8"
81937
82228
  );
81938
82229
  }
81939
- await writeFile39(configPath, JSON.stringify(root, null, 2) + "\n", "utf-8");
82230
+ await writeFile38(configPath, JSON.stringify(root, null, 2) + "\n", "utf-8");
81940
82231
  return {
81941
82232
  path: configPath,
81942
82233
  status: "merged",
@@ -81950,15 +82241,15 @@ async function mergeMcpJson(configPath, opts) {
81950
82241
  };
81951
82242
  }
81952
82243
  root.mcpServers = { ...mcpServers, vibeframe: opts.server };
81953
- await mkdir21(dirname16(configPath), { recursive: true });
82244
+ await mkdir22(dirname17(configPath), { recursive: true });
81954
82245
  if (exists && opts.backup) {
81955
- await writeFile39(
82246
+ await writeFile38(
81956
82247
  `${configPath}.bak-${timestamp()}`,
81957
82248
  await readFile35(configPath, "utf-8"),
81958
82249
  "utf-8"
81959
82250
  );
81960
82251
  }
81961
- await writeFile39(configPath, JSON.stringify(root, null, 2) + "\n", "utf-8");
82252
+ await writeFile38(configPath, JSON.stringify(root, null, 2) + "\n", "utf-8");
81962
82253
  return { path: configPath, status: exists ? "merged" : "wrote" };
81963
82254
  }
81964
82255
  async function mergeCodexToml(configPath, opts) {
@@ -81966,8 +82257,8 @@ async function mergeCodexToml(configPath, opts) {
81966
82257
  const block = `${CODEX_MANAGED_START}
81967
82258
  ${CODEX_MCP_BLOCK}${CODEX_MANAGED_END}`;
81968
82259
  if (!exists) {
81969
- await mkdir21(dirname16(configPath), { recursive: true });
81970
- await writeFile39(
82260
+ await mkdir22(dirname17(configPath), { recursive: true });
82261
+ await writeFile38(
81971
82262
  configPath,
81972
82263
  `#:schema https://developers.openai.com/codex/config-schema.json
81973
82264
 
@@ -81985,7 +82276,7 @@ ${block}
81985
82276
  ),
81986
82277
  block
81987
82278
  );
81988
- await writeFile39(configPath, ensureTrailingNewline(updated), "utf-8");
82279
+ await writeFile38(configPath, ensureTrailingNewline(updated), "utf-8");
81989
82280
  return { path: configPath, status: "merged", reason: "updated managed VibeFrame block" };
81990
82281
  }
81991
82282
  if (/\[mcp_servers\.vibeframe\]/.test(raw)) {
@@ -81999,10 +82290,10 @@ ${block}
81999
82290
  const updated = raw.replace(/\n?\[mcp_servers\.vibeframe\][\s\S]*?(?=\n\[|$)/, `
82000
82291
  ${block}
82001
82292
  `);
82002
- await writeFile39(configPath, ensureTrailingNewline(updated), "utf-8");
82293
+ await writeFile38(configPath, ensureTrailingNewline(updated), "utf-8");
82003
82294
  return { path: configPath, status: "merged", reason: "replaced existing VibeFrame MCP table" };
82004
82295
  }
82005
- await writeFile39(configPath, `${ensureTrailingNewline(raw)}
82296
+ await writeFile38(configPath, `${ensureTrailingNewline(raw)}
82006
82297
  ${block}
82007
82298
  `, "utf-8");
82008
82299
  return { path: configPath, status: "merged" };
@@ -82423,7 +82714,7 @@ function renderEnvExample(opts = {}) {
82423
82714
 
82424
82715
  ${fallback2}# \u2500\u2500 LLM provider / optional \`vibe agent\` fallback (pick one) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
82425
82716
  ANTHROPIC_API_KEY= # Claude - recommended default
82426
- OPENAI_API_KEY= # GPT-5-mini \xB7 Whisper \xB7 gpt-image-2
82717
+ OPENAI_API_KEY= # GPT-5.4 mini \xB7 Whisper \xB7 gpt-image-2.5
82427
82718
  GOOGLE_API_KEY= # Gemini \xB7 Veo
82428
82719
  XAI_API_KEY= # Grok image+video
82429
82720
  OPENROUTER_API_KEY= # multiplexes any provider above
@@ -82526,7 +82817,7 @@ var initCommand = new Command("init").description("Scaffold a VibeFrame project
82526
82817
  reason: "(would create directory)"
82527
82818
  });
82528
82819
  } else {
82529
- await mkdir22(projectDir, { recursive: true });
82820
+ await mkdir23(projectDir, { recursive: true });
82530
82821
  }
82531
82822
  }
82532
82823
  if (wantsCrossTool || !wantsClaude) {
@@ -82747,7 +83038,7 @@ async function runSceneInit(projectDirArg, options, startedAt) {
82747
83038
  }
82748
83039
  }
82749
83040
  if (options.force || result.created.includes(designPath) || !existsSync41(designPath)) {
82750
- await writeFile40(designPath, draft.designMd, "utf-8");
83041
+ await writeFile39(designPath, draft.designMd, "utf-8");
82751
83042
  if (!result.created.includes(designPath)) result.created.push(designPath);
82752
83043
  }
82753
83044
  draftWarnings.push(...draft.warnings);
@@ -82930,15 +83221,15 @@ async function writeIfMissing(absPath, content, force, dryRun) {
82930
83221
  if (dryRun) {
82931
83222
  return { path: absPath, status: "would-write" };
82932
83223
  }
82933
- await mkdir22(dirname17(absPath), { recursive: true });
82934
- await writeFile40(absPath, content, "utf-8");
83224
+ await mkdir23(dirname18(absPath), { recursive: true });
83225
+ await writeFile39(absPath, content, "utf-8");
82935
83226
  return { path: absPath, status: "wrote" };
82936
83227
  }
82937
83228
  async function mergeGitignore(absPath, additions, dryRun) {
82938
83229
  const marker = "# VibeFrame";
82939
83230
  if (!existsSync41(absPath)) {
82940
83231
  if (dryRun) return { path: absPath, status: "would-write" };
82941
- await writeFile40(absPath, additions.trimStart(), "utf-8");
83232
+ await writeFile39(absPath, additions.trimStart(), "utf-8");
82942
83233
  return { path: absPath, status: "wrote" };
82943
83234
  }
82944
83235
  const existing = await readFile36(absPath, "utf-8");
@@ -82947,7 +83238,7 @@ async function mergeGitignore(absPath, additions, dryRun) {
82947
83238
  }
82948
83239
  if (dryRun) return { path: absPath, status: "merged" };
82949
83240
  const trailing = existing.endsWith("\n") ? "" : "\n";
82950
- await writeFile40(absPath, existing + trailing + additions, "utf-8");
83241
+ await writeFile39(absPath, existing + trailing + additions, "utf-8");
82951
83242
  return { path: absPath, status: "merged" };
82952
83243
  }
82953
83244
  function formatStatusIcon(status) {
@@ -82975,7 +83266,7 @@ init_storyboard_revise();
82975
83266
  init_composer_resolve();
82976
83267
  init_cost_tier();
82977
83268
  init_output();
82978
- import { readFile as readFile38, writeFile as writeFile42 } from "node:fs/promises";
83269
+ import { readFile as readFile38, writeFile as writeFile41 } from "node:fs/promises";
82979
83270
  import { existsSync as existsSync43 } from "node:fs";
82980
83271
  import { resolve as resolve52 } from "node:path";
82981
83272
  var storyboardCommand = new Command("storyboard").description("Read, validate, and safely mutate STORYBOARD.md cue blocks").addHelpText("after", `
@@ -83053,7 +83344,7 @@ storyboardCommand.command("set").description("Update one cue in one beat without
83053
83344
  cues = (await setSceneCue(projectDir, { beatId, key, value, unset: options.unset })).cues;
83054
83345
  } else {
83055
83346
  const next = setStoryboardCue(md, { beatId, key, value, unset: options.unset });
83056
- await writeFile42(storyboardPath(projectDir), next, "utf-8");
83347
+ await writeFile41(storyboardPath(projectDir), next, "utf-8");
83057
83348
  cues = getStoryboardBeat(next, beatId)?.cues ?? {};
83058
83349
  }
83059
83350
  } catch (error) {
@@ -83081,7 +83372,7 @@ storyboardCommand.command("move").description("Reorder beats safely").argument("
83081
83372
  order = await moveScene(projectDir, { beatId, afterBeatId: options.after });
83082
83373
  } else {
83083
83374
  const next = moveStoryboardBeat(md, { beatId, afterBeatId: options.after });
83084
- await writeFile42(storyboardPath(projectDir), next, "utf-8");
83375
+ await writeFile41(storyboardPath(projectDir), next, "utf-8");
83085
83376
  order = parseStoryboard(next).beats.map((beat) => beat.id);
83086
83377
  }
83087
83378
  } catch (error) {
@@ -83339,12 +83630,13 @@ init_esm();
83339
83630
  init_source();
83340
83631
  init_build_plan();
83341
83632
  init_output();
83633
+ init_build_video_providers();
83342
83634
  import { resolve as resolve56 } from "node:path";
83343
83635
  var VALID_STAGES = ["assets", "transcript", "compose", "sync", "render", "all"];
83344
83636
  var VALID_MODES = ["agent", "batch", "auto"];
83345
83637
  var VALID_TTS_PROVIDERS = ["auto", "elevenlabs", "openai", "kokoro"];
83346
83638
  var VALID_IMAGE_PROVIDERS = ["openai", "gemini", "grok"];
83347
- var VALID_VIDEO_PROVIDERS = ["seedance", "grok", "kling", "runway", "veo"];
83639
+ var VALID_VIDEO_PROVIDERS = BUILD_VIDEO_PROVIDERS;
83348
83640
  var VALID_MUSIC_PROVIDERS = ["elevenlabs", "replicate"];
83349
83641
  var VALID_COMPOSERS = ["claude", "openai", "gemini"];
83350
83642
  var VALID_IMAGE_QUALITIES = ["standard", "hd"];
@@ -83514,12 +83806,13 @@ init_scene_build();
83514
83806
  init_build_plan();
83515
83807
  init_storyboard_parse();
83516
83808
  init_output();
83809
+ init_build_video_providers();
83517
83810
  import { resolve as resolve64, join as join29 } from "node:path";
83518
83811
  import { readFileSync as readFileSync3, existsSync as existsSync52 } from "node:fs";
83519
83812
  var VALID_MODES2 = ["agent", "batch", "auto"];
83520
83813
  var VALID_STAGES2 = ["assets", "transcript", "compose", "sync", "render", "all"];
83521
83814
  var VALID_IMAGE_PROVIDERS2 = ["openai", "gemini", "grok"];
83522
- var VALID_VIDEO_PROVIDERS2 = ["seedance", "grok", "kling", "runway", "veo"];
83815
+ var VALID_VIDEO_PROVIDERS2 = BUILD_VIDEO_PROVIDERS;
83523
83816
  var VALID_MUSIC_PROVIDERS2 = ["elevenlabs", "replicate"];
83524
83817
  var buildCommand = new Command("build").description("Build a VibeFrame video project from STORYBOARD.md").argument("[project-dir]", "Video project directory", ".").option("--stage <stage>", `Build stage: ${VALID_STAGES2.join("|")}`, "all").option("--beat <id>", "Restrict asset/compose work to one beat id").option("--mode <mode>", "Retained for compatibility; scene HTML is always authored by your host agent unless --composer template is set", "auto").option(
83525
83818
  "--composer <provider>",
@@ -83532,7 +83825,7 @@ var buildCommand = new Command("build").description("Build a VibeFrame video pro
83532
83825
  "Don't transcribe narration for word-sync (default: transcribe when narration + OpenAI key exist)"
83533
83826
  ).option("--skip-render", "Compose only - don't render to MP4").option("--tts <provider>", "TTS provider: auto|elevenlabs|openai|kokoro").option("--voice <id>", "Voice id").option("--image-provider <name>", `Image provider: ${VALID_IMAGE_PROVIDERS2.join("|")}`).option(
83534
83827
  "--image-model <model>",
83535
- "Image model for keyframes/backdrops/character sheets (gemini: flash|pro, openai: gpt-image-2). Provider default when omitted."
83828
+ "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."
83536
83829
  ).option("--video-provider <name>", `Video provider: ${VALID_VIDEO_PROVIDERS2.join("|")}`).option("--music-provider <name>", `Music provider: ${VALID_MUSIC_PROVIDERS2.join("|")}`).option("--quality <q>", "Image quality: standard|hd", "hd").option("--image-size <s>", "Image size: 1024x1024|1536x1024|1024x1536", "1536x1024").option("--force", "Re-dispatch primitives even when assets already exist").option("--dry-run", "Preview parameters without dispatching").addHelpText(
83537
83830
  "after",
83538
83831
  `
@@ -85396,7 +85689,7 @@ init_ai_edit();
85396
85689
  init_output();
85397
85690
  import { resolve as resolve71 } from "node:path";
85398
85691
  import { existsSync as existsSync54 } from "node:fs";
85399
- import { mkdir as mkdir27, rm as rm6 } from "node:fs/promises";
85692
+ import { mkdir as mkdir28, rm as rm7 } from "node:fs/promises";
85400
85693
  var DEMO_DIR = resolve71(process.cwd(), ".vibeframe-demo");
85401
85694
  async function generateTestVideo(outputPath) {
85402
85695
  const filter = [
@@ -85476,7 +85769,7 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
85476
85769
  console.log(source_default.dim(" " + "\u2500".repeat(50)));
85477
85770
  console.log();
85478
85771
  }
85479
- await mkdir27(DEMO_DIR, { recursive: true });
85772
+ await mkdir28(DEMO_DIR, { recursive: true });
85480
85773
  const testVideo = resolve71(DEMO_DIR, "test-input.mp4");
85481
85774
  const genSpinner = !isJson ? ora("Generating test video...").start() : null;
85482
85775
  const generated = await generateTestVideo(testVideo);
@@ -85586,7 +85879,7 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
85586
85879
  }
85587
85880
  if (!options.keep) {
85588
85881
  try {
85589
- await rm6(DEMO_DIR, { recursive: true, force: true });
85882
+ await rm7(DEMO_DIR, { recursive: true, force: true });
85590
85883
  } catch {
85591
85884
  }
85592
85885
  }
@@ -85596,10 +85889,10 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
85596
85889
  init_esm();
85597
85890
  import { existsSync as existsSync55 } from "node:fs";
85598
85891
  import { readFile as readFile45 } from "node:fs/promises";
85599
- import { resolve as resolve72, dirname as dirname23 } from "node:path";
85892
+ import { resolve as resolve72, dirname as dirname24 } from "node:path";
85600
85893
  import { fileURLToPath as fileURLToPath2 } from "node:url";
85601
85894
  var __filename = fileURLToPath2(import.meta.url);
85602
- var __dirname = dirname23(__filename);
85895
+ var __dirname = dirname24(__filename);
85603
85896
  function resolveContextPath(baseDir = __dirname) {
85604
85897
  const candidates = [
85605
85898
  // Bundled package layout: packages/cli/dist/index.js -> packages/cli/CONTEXT.md
@@ -86228,11 +86521,12 @@ var ConversationMemory = class {
86228
86521
  /**
86229
86522
  * Add an assistant message
86230
86523
  */
86231
- addAssistant(content, toolCalls) {
86524
+ addAssistant(content, toolCalls, providerContent) {
86232
86525
  this.messages.push({
86233
86526
  role: "assistant",
86234
86527
  content,
86235
- toolCalls
86528
+ toolCalls,
86529
+ providerContent
86236
86530
  });
86237
86531
  this.trim();
86238
86532
  }
@@ -86406,7 +86700,7 @@ var AgentExecutor = class {
86406
86700
  this.registry.getDefinitions()
86407
86701
  );
86408
86702
  if (response.finishReason === "tool_calls" && response.toolCalls) {
86409
- this.memory.addAssistant(response.content, response.toolCalls);
86703
+ this.memory.addAssistant(response.content, response.toolCalls, response.providerContent);
86410
86704
  for (const toolCall of response.toolCalls) {
86411
86705
  if (this.config.verbose) {
86412
86706
  console.log(`[Agent] Calling tool: ${toolCall.name}`);
@@ -87043,8 +87337,8 @@ init_source();
87043
87337
  init_config();
87044
87338
  init_schema();
87045
87339
  init_api_key();
87046
- import { access as access9, writeFile as writeFile56, mkdir as mkdir30 } from "node:fs/promises";
87047
- import { dirname as dirname25 } from "node:path";
87340
+ import { access as access9, writeFile as writeFile55, mkdir as mkdir31 } from "node:fs/promises";
87341
+ import { dirname as dirname26 } from "node:path";
87048
87342
  var BANNER_SHOWN_PATH = `${USER_CONFIG_DIR}/.banner-shown`;
87049
87343
  async function isFirstRun() {
87050
87344
  try {
@@ -87063,8 +87357,8 @@ async function isFirstRun() {
87063
87357
  }
87064
87358
  async function markBannerShown() {
87065
87359
  try {
87066
- await mkdir30(dirname25(BANNER_SHOWN_PATH), { recursive: true });
87067
- await writeFile56(BANNER_SHOWN_PATH, (/* @__PURE__ */ new Date()).toISOString());
87360
+ await mkdir31(dirname26(BANNER_SHOWN_PATH), { recursive: true });
87361
+ await writeFile55(BANNER_SHOWN_PATH, (/* @__PURE__ */ new Date()).toISOString());
87068
87362
  } catch {
87069
87363
  }
87070
87364
  }