@vibeframe/mcp-server 0.91.8 → 0.92.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1222 -744
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -14169,8 +14169,8 @@ var init_api_keys = __esm({
14169
14169
  configKey: "imgbb",
14170
14170
  envVar: "IMGBB_API_KEY",
14171
14171
  label: "ImgBB",
14172
- showInSetup: false,
14173
- // not prompted in setup wizard — internal upload host
14172
+ showInSetup: true,
14173
+ setupDescription: "image hosting for Seedance/Kling image-to-video uploads",
14174
14174
  envExampleComment: "ImgBB API Key (image hosting \u2014 used by Kling and Seedance for image-to-video uploads)",
14175
14175
  envExampleUrl: "https://api.imgbb.com/",
14176
14176
  // ImgBB issues 32-character lowercase hex keys. Soft-warn if a pasted
@@ -14179,9 +14179,7 @@ var init_api_keys = __esm({
14179
14179
  keyFormat: { prefix: /^[a-f0-9]{32}$/, example: "32-char hex" },
14180
14180
  // ImgBB has no provider class (envvar-only); doctor still shows what it
14181
14181
  // unlocks at the apiKey level.
14182
- commandsUnlocked: [
14183
- "generate video -p kling/seedance (image-to-video upload host)"
14184
- ]
14182
+ commandsUnlocked: ["generate video -p kling/seedance (image-to-video upload host)"]
14185
14183
  });
14186
14184
  defineProvider({
14187
14185
  id: "openrouter",
@@ -25403,21 +25401,56 @@ function createDefaultConfig() {
25403
25401
  aspectRatio: "16:9",
25404
25402
  exportQuality: "standard"
25405
25403
  },
25404
+ upload: {
25405
+ provider: "imgbb",
25406
+ ttlSeconds: 3600,
25407
+ s3: {
25408
+ prefix: "vibeframe/tmp"
25409
+ }
25410
+ },
25406
25411
  repl: {
25407
25412
  autoSave: true
25408
25413
  }
25409
25414
  };
25410
25415
  }
25411
- var PROVIDER_ENV_VARS;
25416
+ var PROVIDER_NAMES, PROVIDER_ENV_VARS;
25412
25417
  var init_schema = __esm({
25413
25418
  "../cli/src/config/schema.ts"() {
25414
25419
  "use strict";
25415
25420
  init_dist();
25421
+ PROVIDER_NAMES = {
25422
+ claude: "Claude (Anthropic)",
25423
+ openai: "GPT-4 (OpenAI)",
25424
+ gemini: "Gemini (Google)",
25425
+ ollama: "Ollama (Local)",
25426
+ xai: "Grok (xAI)",
25427
+ openrouter: "OpenRouter"
25428
+ };
25416
25429
  PROVIDER_ENV_VARS = getProviderEnvVars();
25417
25430
  }
25418
25431
  });
25419
25432
 
25420
25433
  // ../cli/src/config/index.ts
25434
+ var config_exports = {};
25435
+ __export(config_exports, {
25436
+ CONFIG_DIR: () => CONFIG_DIR,
25437
+ CONFIG_PATH: () => CONFIG_PATH,
25438
+ PROVIDER_ENV_VARS: () => PROVIDER_ENV_VARS,
25439
+ PROVIDER_NAMES: () => PROVIDER_NAMES,
25440
+ USER_CONFIG_DIR: () => USER_CONFIG_DIR,
25441
+ USER_CONFIG_PATH: () => USER_CONFIG_PATH,
25442
+ createDefaultConfig: () => createDefaultConfig,
25443
+ getActiveScope: () => getActiveScope,
25444
+ getApiKeyFromConfig: () => getApiKeyFromConfig,
25445
+ getConfigDir: () => getConfigDir,
25446
+ getConfigPath: () => getConfigPath,
25447
+ getProjectConfigDir: () => getProjectConfigDir,
25448
+ getProjectConfigPath: () => getProjectConfigPath,
25449
+ isConfigured: () => isConfigured,
25450
+ loadConfig: () => loadConfig,
25451
+ saveConfig: () => saveConfig,
25452
+ updateProviderKey: () => updateProviderKey
25453
+ });
25421
25454
  import { resolve as resolve2 } from "node:path";
25422
25455
  import { homedir } from "node:os";
25423
25456
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, access as access2 } from "node:fs/promises";
@@ -25430,6 +25463,9 @@ function getProjectConfigPath(cwd = process.cwd()) {
25430
25463
  function getConfigPath(scope, cwd) {
25431
25464
  return scope === "project" ? getProjectConfigPath(cwd) : USER_CONFIG_PATH;
25432
25465
  }
25466
+ function getConfigDir(scope, cwd) {
25467
+ return scope === "project" ? getProjectConfigDir(cwd) : USER_CONFIG_DIR;
25468
+ }
25433
25469
  async function fileExists(path14) {
25434
25470
  try {
25435
25471
  await access2(path14);
@@ -25438,6 +25474,9 @@ async function fileExists(path14) {
25438
25474
  return false;
25439
25475
  }
25440
25476
  }
25477
+ async function getActiveScope(cwd) {
25478
+ return await fileExists(getProjectConfigPath(cwd)) ? "project" : "user";
25479
+ }
25441
25480
  function applyDefaults(parsed) {
25442
25481
  const defaults = createDefaultConfig();
25443
25482
  return {
@@ -25446,6 +25485,11 @@ function applyDefaults(parsed) {
25446
25485
  llm: { ...defaults.llm, ...parsed.llm },
25447
25486
  providers: { ...defaults.providers, ...parsed.providers },
25448
25487
  defaults: { ...defaults.defaults, ...parsed.defaults },
25488
+ upload: {
25489
+ ...defaults.upload,
25490
+ ...parsed.upload,
25491
+ s3: { ...defaults.upload.s3, ...parsed.upload?.s3 }
25492
+ },
25449
25493
  repl: { ...defaults.repl, ...parsed.repl }
25450
25494
  };
25451
25495
  }
@@ -25473,6 +25517,11 @@ async function loadConfig(options = {}) {
25473
25517
  llm: { ...user.llm, ...project2.llm },
25474
25518
  providers: { ...user.providers, ...project2.providers },
25475
25519
  defaults: { ...user.defaults, ...project2.defaults },
25520
+ upload: {
25521
+ ...user.upload,
25522
+ ...project2.upload,
25523
+ s3: { ...user.upload.s3, ...project2.upload.s3 }
25524
+ },
25476
25525
  repl: { ...user.repl, ...project2.repl }
25477
25526
  };
25478
25527
  }
@@ -25483,6 +25532,28 @@ async function loadConfig(options = {}) {
25483
25532
  if (project) return project;
25484
25533
  return readConfigFile(USER_CONFIG_PATH);
25485
25534
  }
25535
+ async function saveConfig(config4, options = {}) {
25536
+ const scope = options.scope ?? "user";
25537
+ const dir = getConfigDir(scope, options.cwd);
25538
+ const path14 = getConfigPath(scope, options.cwd);
25539
+ await mkdir2(dir, { recursive: true });
25540
+ const content = (0, import_yaml2.stringify)(config4, { indent: 2, lineWidth: 0 });
25541
+ await writeFile2(path14, content, "utf-8");
25542
+ }
25543
+ async function isConfigured() {
25544
+ const config4 = await loadConfig();
25545
+ if (!config4) return false;
25546
+ const provider = config4.llm.provider;
25547
+ const providerKey = provider === "gemini" ? "google" : provider === "claude" ? "anthropic" : provider;
25548
+ if (config4.providers[providerKey]) {
25549
+ return true;
25550
+ }
25551
+ const envVar = PROVIDER_ENV_VARS[providerKey];
25552
+ if (envVar && process.env[envVar]) {
25553
+ return true;
25554
+ }
25555
+ return false;
25556
+ }
25486
25557
  async function getApiKeyFromConfig(providerKey) {
25487
25558
  const config4 = await loadConfig();
25488
25559
  if (config4?.providers[providerKey]) {
@@ -25492,7 +25563,14 @@ async function getApiKeyFromConfig(providerKey) {
25492
25563
  if (envVar) return process.env[envVar];
25493
25564
  return void 0;
25494
25565
  }
25495
- var import_yaml2, USER_CONFIG_DIR, USER_CONFIG_PATH;
25566
+ async function updateProviderKey(providerKey, apiKey, options = {}) {
25567
+ const scope = options.scope ?? await getActiveScope(options.cwd);
25568
+ let config4 = await loadConfig({ scope, cwd: options.cwd });
25569
+ if (!config4) config4 = createDefaultConfig();
25570
+ config4.providers[providerKey] = apiKey;
25571
+ await saveConfig(config4, { scope, cwd: options.cwd });
25572
+ }
25573
+ var import_yaml2, USER_CONFIG_DIR, USER_CONFIG_PATH, CONFIG_DIR, CONFIG_PATH;
25496
25574
  var init_config = __esm({
25497
25575
  "../cli/src/config/index.ts"() {
25498
25576
  "use strict";
@@ -25501,6 +25579,8 @@ var init_config = __esm({
25501
25579
  init_schema();
25502
25580
  USER_CONFIG_DIR = resolve2(homedir(), ".vibeframe");
25503
25581
  USER_CONFIG_PATH = resolve2(USER_CONFIG_DIR, "config.yaml");
25582
+ CONFIG_DIR = USER_CONFIG_DIR;
25583
+ CONFIG_PATH = USER_CONFIG_PATH;
25504
25584
  }
25505
25585
  });
25506
25586
 
@@ -25599,7 +25679,9 @@ async function getApiKey(envVar, providerName, optionValue) {
25599
25679
  }
25600
25680
  console.log();
25601
25681
  console.log(source_default.yellow(`${providerName} API key not found.`));
25602
- console.log(source_default.dim(`Set ${envVar} in .env (current directory), run 'vibe setup', or enter below.`));
25682
+ console.log(
25683
+ source_default.dim(`Set ${envVar} in .env (current directory), run 'vibe setup', or enter below.`)
25684
+ );
25603
25685
  console.log();
25604
25686
  const apiKey = await prompt(source_default.cyan(`Enter ${providerName} API key: `), true);
25605
25687
  if (!apiKey || apiKey.trim() === "") {
@@ -25658,7 +25740,8 @@ var init_api_key = __esm({
25658
25740
  ELEVENLABS_API_KEY: "https://elevenlabs.io/app/settings/api-keys",
25659
25741
  RUNWAY_API_SECRET: "https://app.runwayml.com/settings/api-keys",
25660
25742
  KLING_API_KEY: "https://klingai.com/dev",
25661
- REPLICATE_API_TOKEN: "https://replicate.com/account/api-tokens"
25743
+ REPLICATE_API_TOKEN: "https://replicate.com/account/api-tokens",
25744
+ IMGBB_API_KEY: "https://api.imgbb.com/"
25662
25745
  };
25663
25746
  ApiKeyError = class extends Error {
25664
25747
  envVar;
@@ -77523,7 +77606,7 @@ var require_websocket = __commonJS({
77523
77606
  var http3 = __require("http");
77524
77607
  var net = __require("net");
77525
77608
  var tls = __require("tls");
77526
- var { randomBytes, createHash: createHash6 } = __require("crypto");
77609
+ var { randomBytes, createHash: createHash7 } = __require("crypto");
77527
77610
  var { Duplex, Readable: Readable3 } = __require("stream");
77528
77611
  var { URL: URL3 } = __require("url");
77529
77612
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -78183,7 +78266,7 @@ var require_websocket = __commonJS({
78183
78266
  abortHandshake(websocket, socket, "Invalid Upgrade header");
78184
78267
  return;
78185
78268
  }
78186
- const digest = createHash6("sha1").update(key2 + GUID).digest("base64");
78269
+ const digest = createHash7("sha1").update(key2 + GUID).digest("base64");
78187
78270
  if (res.headers["sec-websocket-accept"] !== digest) {
78188
78271
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
78189
78272
  return;
@@ -78550,7 +78633,7 @@ var require_websocket_server = __commonJS({
78550
78633
  var EventEmitter5 = __require("events");
78551
78634
  var http3 = __require("http");
78552
78635
  var { Duplex } = __require("stream");
78553
- var { createHash: createHash6 } = __require("crypto");
78636
+ var { createHash: createHash7 } = __require("crypto");
78554
78637
  var extension2 = require_extension();
78555
78638
  var PerMessageDeflate2 = require_permessage_deflate();
78556
78639
  var subprotocol2 = require_subprotocol();
@@ -78851,7 +78934,7 @@ var require_websocket_server = __commonJS({
78851
78934
  );
78852
78935
  }
78853
78936
  if (this._state > RUNNING) return abortHandshake(socket, 503);
78854
- const digest = createHash6("sha1").update(key2 + GUID).digest("base64");
78937
+ const digest = createHash7("sha1").update(key2 + GUID).digest("base64");
78855
78938
  const headers = [
78856
78939
  "HTTP/1.1 101 Switching Protocols",
78857
78940
  "Upgrade: websocket",
@@ -457697,29 +457780,61 @@ var init_video_extend = __esm({
457697
457780
  });
457698
457781
 
457699
457782
  // ../cli/src/utils/provider-resolver.ts
457783
+ function loadProviderDefaultsFromConfig(config4) {
457784
+ configDefaults = {};
457785
+ if (config4?.defaults.imageProvider) configDefaults.image = config4.defaults.imageProvider;
457786
+ if (config4?.defaults.videoProvider) configDefaults.video = config4.defaults.videoProvider;
457787
+ configProviderKeys = new Set(
457788
+ Object.entries(config4?.providers ?? {}).filter(([, value]) => Boolean(value)).map(([key2]) => key2)
457789
+ );
457790
+ }
457791
+ async function loadProviderDefaults() {
457792
+ try {
457793
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
457794
+ const config4 = await loadConfig2();
457795
+ loadProviderDefaultsFromConfig(config4);
457796
+ } catch {
457797
+ configDefaults = null;
457798
+ configProviderKeys = null;
457799
+ }
457800
+ }
457801
+ function providerKeyForEnvVar(envVar) {
457802
+ const envVars = getProviderEnvVars();
457803
+ for (const [providerKey, candidateEnvVar] of Object.entries(envVars)) {
457804
+ if (candidateEnvVar === envVar) return providerKey;
457805
+ }
457806
+ return null;
457807
+ }
457808
+ function hasCandidateKey(candidate) {
457809
+ if (candidate.envVar === null) return true;
457810
+ if (hasApiKey(candidate.envVar)) return true;
457811
+ const providerKey = providerKeyForEnvVar(candidate.envVar);
457812
+ return providerKey ? Boolean(configProviderKeys?.has(providerKey)) : false;
457813
+ }
457700
457814
  function resolveProvider(category) {
457701
457815
  const candidates = getProvidersFor(category);
457702
457816
  if (candidates.length === 0) return null;
457703
457817
  if (configDefaults?.[category]) {
457704
457818
  const preferred = candidates.find((c) => c.name === configDefaults[category]);
457705
- if (preferred && (preferred.envVar === null || hasApiKey(preferred.envVar))) {
457819
+ if (preferred && hasCandidateKey(preferred)) {
457706
457820
  return { name: preferred.name, label: preferred.label };
457707
457821
  }
457708
457822
  }
457709
457823
  for (const candidate of candidates) {
457710
- if (candidate.envVar === null || hasApiKey(candidate.envVar)) {
457824
+ if (hasCandidateKey(candidate)) {
457711
457825
  return { name: candidate.name, label: candidate.label };
457712
457826
  }
457713
457827
  }
457714
457828
  return null;
457715
457829
  }
457716
- var configDefaults;
457830
+ var configDefaults, configProviderKeys;
457717
457831
  var init_provider_resolver = __esm({
457718
457832
  "../cli/src/utils/provider-resolver.ts"() {
457719
457833
  "use strict";
457720
457834
  init_dist();
457721
457835
  init_api_key();
457722
457836
  configDefaults = null;
457837
+ configProviderKeys = null;
457723
457838
  }
457724
457839
  });
457725
457840
 
@@ -457756,13 +457871,26 @@ import { resolve as resolve47, dirname as dirname27 } from "node:path";
457756
457871
  import { fileURLToPath as fileURLToPath4 } from "node:url";
457757
457872
  import { writeFile as writeFile30, mkdir as mkdir20 } from "node:fs/promises";
457758
457873
  function registerImageCommand(parent) {
457759
- 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("-p, --provider <provider>", "Provider: openai (default when OPENAI_API_KEY set), gemini, grok, runway").option("-k, --api-key <key>", "API key (or set env: OPENAI_API_KEY, GOOGLE_API_KEY)").option("-o, --output <path>", "Output file path (downloads image)").option("--size <size>", "Image size (openai: 1024x1024, 1536x1024, 1024x1536)", "1024x1024").option("-r, --ratio <ratio>", "Aspect ratio (gemini: 1:1, 1:4, 1:8, 4:1, 8:1, 16:9, 9:16, 3:4, 4:3, etc.)", "1:1").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("-m, --model <model>", "Model. Gemini: flash, 3.1-flash, latest, pro. OpenAI: 1.5 (default), 2 (gpt-image-2)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
457874
+ 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(
457875
+ "-p, --provider <provider>",
457876
+ "Provider: openai (default when OPENAI_API_KEY set), gemini, grok, runway"
457877
+ ).option("-k, --api-key <key>", "API key (or set env: OPENAI_API_KEY, GOOGLE_API_KEY)").option("-o, --output <path>", "Output file path (downloads image)").option("--size <size>", "Image size (openai: 1024x1024, 1536x1024, 1024x1536)", "1024x1024").option(
457878
+ "-r, --ratio <ratio>",
457879
+ "Aspect ratio (gemini: 1:1, 1:4, 1:8, 4:1, 8:1, 16:9, 9:16, 3:4, 4:3, etc.)",
457880
+ "1:1"
457881
+ ).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(
457882
+ "-m, --model <model>",
457883
+ "Model. Gemini: flash, 3.1-flash, latest, pro. OpenAI: 1.5 (default), 2 (gpt-image-2)"
457884
+ ).option("--dry-run", "Preview parameters without executing").addHelpText(
457885
+ "after",
457886
+ `
457760
457887
  Examples:
457761
457888
  $ vibe generate image "a sunset over the ocean" -o sunset.png
457762
457889
  $ vibe gen img "logo design" -o logo.png -p openai
457763
457890
  $ vibe gen img "landscape photo" -o wide.png -r 16:9
457764
457891
  $ vibe gen img "portrait" -o portrait.png -p gemini -m pro
457765
- $ vibe gen img "product shot" --dry-run --json`).action(async (prompt3, options) => {
457892
+ $ vibe gen img "product shot" --dry-run --json`
457893
+ ).action(async (prompt3, options) => {
457766
457894
  const startedAt = Date.now();
457767
457895
  try {
457768
457896
  if (!prompt3) {
@@ -457773,10 +457901,7 @@ Examples:
457773
457901
  }
457774
457902
  } else {
457775
457903
  exitWithError(
457776
- usageError(
457777
- "Prompt argument is required.",
457778
- "Usage: vibe generate image <prompt>"
457779
- )
457904
+ usageError("Prompt argument is required.", "Usage: vibe generate image <prompt>")
457780
457905
  );
457781
457906
  }
457782
457907
  }
@@ -457784,13 +457909,16 @@ Examples:
457784
457909
  if (options.output) {
457785
457910
  validateOutputPath(options.output);
457786
457911
  }
457912
+ await loadProviderDefaults();
457787
457913
  if (options.count !== void 0) {
457788
457914
  const n = parseInt(options.count, 10);
457789
457915
  if (!Number.isFinite(n) || n < 1 || n > 10) {
457790
- exitWithError(usageError(
457791
- `Invalid --count: ${options.count}`,
457792
- "Must be an integer between 1 and 10."
457793
- ));
457916
+ exitWithError(
457917
+ usageError(
457918
+ `Invalid --count: ${options.count}`,
457919
+ "Must be an integer between 1 and 10."
457920
+ )
457921
+ );
457794
457922
  }
457795
457923
  }
457796
457924
  const imageRegistry = getProvidersFor("image");
@@ -457855,11 +457983,9 @@ Examples:
457855
457983
  const apiKey = await requireApiKey(envKey, providerName, options.apiKey);
457856
457984
  const spinner2 = ora(`Generating image with ${providerName}...`).start();
457857
457985
  if (provider === "openai") {
457858
- const { result, modelLabel } = await executeOpenAIImageGenerate(
457859
- prompt3,
457860
- options,
457861
- { apiKey }
457862
- );
457986
+ const { result, modelLabel } = await executeOpenAIImageGenerate(prompt3, options, {
457987
+ apiKey
457988
+ });
457863
457989
  if (!result.success || !result.images) {
457864
457990
  spinner2.fail(result.error || "Image generation failed");
457865
457991
  exitWithError(apiError(result.error || "Image generation failed", true));
@@ -458081,9 +458207,7 @@ Examples:
458081
458207
  spinner2.fail(result.error || "Image generation failed");
458082
458208
  exitWithError(apiError(result.error || "Image generation failed", true));
458083
458209
  }
458084
- spinner2.succeed(
458085
- source_default.green(`Generated ${result.images.length} image(s) with xAI Grok`)
458086
- );
458210
+ spinner2.succeed(source_default.green(`Generated ${result.images.length} image(s) with xAI Grok`));
458087
458211
  if (isJsonMode()) {
458088
458212
  const outputPath = options.output ? resolve47(process.cwd(), options.output) : void 0;
458089
458213
  if (outputPath && result.images.length > 0) {
@@ -459316,606 +459440,206 @@ var init_video_utils = __esm({
459316
459440
  }
459317
459441
  });
459318
459442
 
459319
- // ../cli/src/commands/_shared/video-providers.ts
459320
- async function generateVideoWithRetryGrok(grok, segment, options, maxRetries, onProgress) {
459321
- const prompt3 = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
459322
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
459323
- try {
459324
- const result = await grok.generateVideo(prompt3, {
459325
- prompt: prompt3,
459326
- duration: options.duration,
459327
- aspectRatio: options.aspectRatio,
459328
- referenceImage: options.referenceImage
459329
- });
459330
- if (result.status !== "failed" && result.id) {
459331
- return { requestId: result.id };
459332
- }
459333
- const providerErr = result.error || "Grok returned failed status";
459334
- if (attempt < maxRetries) {
459335
- onProgress?.(
459336
- `\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`
459337
- );
459338
- await sleep(RETRY_DELAY_MS);
459339
- } else {
459340
- console.error(source_default.dim(`
459341
- [Grok error: ${providerErr}]`));
459342
- }
459343
- } catch (err) {
459344
- const errMsg = err instanceof Error ? err.message : String(err);
459345
- if (attempt < maxRetries) {
459346
- onProgress?.(`\u26A0 Error: ${errMsg.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
459347
- await sleep(RETRY_DELAY_MS);
459348
- } else {
459349
- console.error(source_default.dim(`
459350
- [Grok error: ${errMsg}]`));
459351
- }
459352
- }
459353
- }
459354
- return null;
459443
+ // ../cli/src/utils/upload-host.ts
459444
+ import { createHash as createHash6, createHmac as createHmac2, randomUUID as randomUUID2 } from "node:crypto";
459445
+ import { extname as extname10 } from "node:path";
459446
+ function envNumber(name) {
459447
+ const value = process.env[name];
459448
+ if (!value) return void 0;
459449
+ const n = Number.parseInt(value, 10);
459450
+ return Number.isFinite(n) && n > 0 ? n : void 0;
459355
459451
  }
459356
- async function generateVideoWithRetryKling(kling, segment, options, maxRetries, onProgress) {
459357
- const prompt3 = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
459358
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
459359
- try {
459360
- const result = await kling.generateVideo(prompt3, {
459361
- prompt: prompt3,
459362
- // Pass reference image (base64 or URL) - KlingProvider handles v1.5 fallback for base64
459363
- referenceImage: options.referenceImage,
459364
- duration: options.duration,
459365
- aspectRatio: options.aspectRatio,
459366
- mode: "std"
459367
- // std mode for faster generation
459368
- });
459369
- if (result.status !== "failed" && result.id) {
459370
- return {
459371
- taskId: result.id,
459372
- type: options.referenceImage ? "image2video" : "text2video"
459373
- };
459374
- }
459375
- const providerErr = result.error || "Kling returned failed status";
459376
- if (attempt < maxRetries) {
459377
- onProgress?.(`\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
459378
- await sleep(RETRY_DELAY_MS);
459379
- } else {
459380
- console.error(source_default.dim(`
459381
- [Kling error: ${providerErr}]`));
459382
- }
459383
- } catch (err) {
459384
- const errMsg = err instanceof Error ? err.message : String(err);
459385
- if (attempt < maxRetries) {
459386
- onProgress?.(`\u26A0 Error: ${errMsg.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
459387
- await sleep(RETRY_DELAY_MS);
459388
- } else {
459389
- console.error(source_default.dim(`
459390
- [Kling error: ${errMsg}]`));
459391
- }
459392
- }
459393
- }
459394
- return null;
459452
+ function safePrefix(prefix) {
459453
+ return (prefix ?? "vibeframe/tmp").replace(/^\/+|\/+$/g, "");
459395
459454
  }
459396
- async function generateVideoWithRetryRunway(runway, segment, referenceImage, options, maxRetries, onProgress) {
459397
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
459398
- try {
459399
- const result = await runway.generateVideo(segment.visuals, {
459400
- prompt: segment.visuals,
459401
- referenceImage,
459402
- duration: options.duration,
459403
- aspectRatio: options.aspectRatio
459404
- });
459405
- if (result.status !== "failed" && result.id) {
459406
- return { taskId: result.id };
459407
- }
459408
- const providerErr = result.error || "Runway returned failed status";
459409
- if (attempt < maxRetries) {
459410
- onProgress?.(`\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
459411
- await sleep(RETRY_DELAY_MS);
459412
- } else {
459413
- console.error(source_default.dim(`
459414
- [Runway error: ${providerErr}]`));
459415
- }
459416
- } catch (err) {
459417
- const errMsg = err instanceof Error ? err.message : String(err);
459418
- if (attempt < maxRetries) {
459419
- onProgress?.(`\u26A0 Error: ${errMsg.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
459420
- await sleep(RETRY_DELAY_MS);
459421
- } else {
459422
- console.error(source_default.dim(`
459423
- [Runway error: ${errMsg}]`));
459424
- }
459425
- }
459426
- }
459427
- return null;
459455
+ function extensionFor(opts) {
459456
+ const fromName = opts?.filename ? extname10(opts.filename).replace(/^\./, "") : "";
459457
+ if (fromName) return fromName.toLowerCase();
459458
+ const mime = opts?.mimeType ?? "image/png";
459459
+ if (mime === "image/jpeg") return "jpg";
459460
+ if (mime === "image/webp") return "webp";
459461
+ if (mime === "image/gif") return "gif";
459462
+ return "png";
459428
459463
  }
459429
- async function generateVideoWithRetryVeo(gemini, segment, options, maxRetries, onProgress) {
459430
- const prompt3 = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
459431
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
459432
- try {
459433
- const result = await gemini.generateVideo(prompt3, {
459434
- prompt: prompt3,
459435
- referenceImage: options.referenceImage,
459436
- duration: options.duration,
459437
- aspectRatio: options.aspectRatio,
459438
- model: "veo-3.1-fast-generate-preview"
459439
- });
459440
- if (result.status !== "failed" && result.id) {
459441
- return { operationName: result.id };
459442
- }
459443
- const providerErr = result.error || "Veo returned failed status";
459444
- if (attempt < maxRetries) {
459445
- onProgress?.(`\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
459446
- await sleep(RETRY_DELAY_MS);
459447
- } else {
459448
- console.error(source_default.dim(`
459449
- [Veo error: ${providerErr}]`));
459450
- }
459451
- } catch (err) {
459452
- const errMsg = err instanceof Error ? err.message : String(err);
459453
- if (attempt < maxRetries) {
459454
- onProgress?.(`\u26A0 Error: ${errMsg.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
459455
- await sleep(RETRY_DELAY_MS);
459456
- } else {
459457
- console.error(source_default.dim(`
459458
- [Veo error: ${errMsg}]`));
459459
- }
459460
- }
459461
- }
459462
- return null;
459464
+ function hmac(key2, value) {
459465
+ return createHmac2("sha256", key2).update(value).digest();
459463
459466
  }
459464
- var init_video_providers = __esm({
459465
- "../cli/src/commands/_shared/video-providers.ts"() {
459466
- "use strict";
459467
- init_source();
459468
- init_video_utils();
459469
- }
459470
- });
459471
-
459472
- // ../cli/src/commands/ai-script-pipeline.ts
459473
- import { readFile as readFile24, writeFile as writeFile32, unlink as unlink6, rename as rename6 } from "node:fs/promises";
459474
- import { resolve as resolve49, extname as extname10 } from "node:path";
459475
- import { existsSync as existsSync48 } from "node:fs";
459476
- async function executeRegenerateScene(options) {
459477
- const result = {
459478
- success: false,
459479
- regeneratedScenes: [],
459480
- failedScenes: []
459467
+ function sha256Hex(value) {
459468
+ return createHash6("sha256").update(value).digest("hex");
459469
+ }
459470
+ function awsEncode(value) {
459471
+ return encodeURIComponent(value).replace(
459472
+ /[!'()*]/g,
459473
+ (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
459474
+ );
459475
+ }
459476
+ function presignS3PutUrl(params) {
459477
+ const now = /* @__PURE__ */ new Date();
459478
+ const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, "");
459479
+ const dateStamp = amzDate.slice(0, 8);
459480
+ const host = `${params.bucket}.s3.${params.region}.amazonaws.com`;
459481
+ const credentialScope = `${dateStamp}/${params.region}/s3/aws4_request`;
459482
+ const canonicalUri = `/${params.key.split("/").map(awsEncode).join("/")}`;
459483
+ const query2 = {
459484
+ "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
459485
+ "X-Amz-Credential": `${params.accessKeyId}/${credentialScope}`,
459486
+ "X-Amz-Date": amzDate,
459487
+ "X-Amz-Expires": String(params.ttlSeconds),
459488
+ "X-Amz-SignedHeaders": "host"
459481
459489
  };
459482
- try {
459483
- const outputDir = resolve49(process.cwd(), options.projectDir);
459484
- if (!existsSync48(outputDir)) {
459485
- return { ...result, error: `Project directory not found: ${outputDir}` };
459486
- }
459487
- const yamlPath = resolve49(outputDir, "storyboard.yaml");
459488
- const jsonPath = resolve49(outputDir, "storyboard.json");
459489
- const storyboardPath = existsSync48(yamlPath) ? yamlPath : existsSync48(jsonPath) ? jsonPath : null;
459490
- if (!storyboardPath) {
459491
- return { ...result, error: `Storyboard not found in: ${outputDir} (expected storyboard.yaml or storyboard.json)` };
459492
- }
459493
- const storyboardContent = await readFile24(storyboardPath, "utf-8");
459494
- const segments = storyboardPath.endsWith(".yaml") ? (0, import_yaml6.parse)(storyboardContent).scenes : JSON.parse(storyboardContent);
459495
- for (const sceneNum of options.scenes) {
459496
- if (sceneNum < 1 || sceneNum > segments.length) {
459497
- return { ...result, error: `Scene ${sceneNum} does not exist. Storyboard has ${segments.length} scenes.` };
459498
- }
459499
- }
459500
- const regenerateVideo = options.videoOnly || !options.narrationOnly && !options.imageOnly;
459501
- const regenerateNarration = options.narrationOnly || !options.videoOnly && !options.imageOnly;
459502
- const regenerateImage = options.imageOnly || !options.videoOnly && !options.narrationOnly;
459503
- let videoApiKey;
459504
- if (regenerateVideo) {
459505
- const generatorKeyMap = {
459506
- grok: { envVar: "XAI_API_KEY", name: "xAI (Grok)" },
459507
- kling: { envVar: "KLING_API_KEY", name: "Kling" },
459508
- runway: { envVar: "RUNWAY_API_SECRET", name: "Runway" },
459509
- veo: { envVar: "GOOGLE_API_KEY", name: "Google (Veo)" }
459510
- };
459511
- const generator = options.generator || "grok";
459512
- const genInfo = generatorKeyMap[generator];
459513
- if (!genInfo) {
459514
- return { ...result, error: `Invalid generator: ${generator}. Available: ${Object.keys(generatorKeyMap).join(", ")}` };
459515
- }
459516
- videoApiKey = await getApiKey(genInfo.envVar, genInfo.name) ?? void 0;
459517
- if (!videoApiKey) {
459518
- return { ...result, error: `${genInfo.name} API key required. Run 'vibe setup' or set ${genInfo.envVar} in .env` };
459519
- }
459520
- }
459521
- let imageApiKey;
459522
- if (regenerateImage) {
459523
- const imageProvider = options.imageProvider || "openai";
459524
- const imageKeyMap = {
459525
- openai: { envVar: "OPENAI_API_KEY", name: "OpenAI" },
459526
- gemini: { envVar: "GOOGLE_API_KEY", name: "Google" },
459527
- grok: { envVar: "XAI_API_KEY", name: "xAI" }
459528
- };
459529
- const info = imageKeyMap[imageProvider];
459530
- if (!info) {
459531
- return { ...result, error: `Invalid imageProvider: ${imageProvider}` };
459532
- }
459533
- imageApiKey = await getApiKey(info.envVar, info.name) ?? void 0;
459534
- if (!imageApiKey) {
459535
- return { ...result, error: `${info.name} API key required. Run 'vibe setup' or set ${info.envVar} in .env` };
459536
- }
459490
+ if (params.sessionToken) {
459491
+ query2["X-Amz-Security-Token"] = params.sessionToken;
459492
+ }
459493
+ const canonicalQuery = Object.entries(query2).sort(([a], [b]) => a.localeCompare(b)).map(([key2, value]) => `${awsEncode(key2)}=${awsEncode(value)}`).join("&");
459494
+ const canonicalRequest = [
459495
+ "PUT",
459496
+ canonicalUri,
459497
+ canonicalQuery,
459498
+ `host:${host}`,
459499
+ "",
459500
+ "host",
459501
+ "UNSIGNED-PAYLOAD"
459502
+ ].join("\n");
459503
+ const stringToSign = [
459504
+ "AWS4-HMAC-SHA256",
459505
+ amzDate,
459506
+ credentialScope,
459507
+ sha256Hex(canonicalRequest)
459508
+ ].join("\n");
459509
+ const dateKey = hmac(`AWS4${params.secretAccessKey}`, dateStamp);
459510
+ const regionKey = hmac(dateKey, params.region);
459511
+ const serviceKey = hmac(regionKey, "s3");
459512
+ const signingKey = hmac(serviceKey, "aws4_request");
459513
+ const signature = createHmac2("sha256", signingKey).update(stringToSign).digest("hex");
459514
+ return `https://${host}${canonicalUri}?${canonicalQuery}&X-Amz-Signature=${signature}`;
459515
+ }
459516
+ function publicS3Url(params) {
459517
+ if (params.publicBaseUrl) {
459518
+ return `${params.publicBaseUrl.replace(/\/+$/g, "")}/${params.key.split("/").map(awsEncode).join("/")}`;
459519
+ }
459520
+ return `https://${params.bucket}.s3.${params.region}.amazonaws.com/${params.key.split("/").map(awsEncode).join("/")}`;
459521
+ }
459522
+ async function resolveUploadSettings() {
459523
+ const config4 = await loadConfig();
459524
+ const provider = process.env.VIBE_UPLOAD_PROVIDER ?? config4?.upload.provider ?? "imgbb";
459525
+ return {
459526
+ provider,
459527
+ ttlSeconds: envNumber("VIBE_UPLOAD_TTL_SECONDS") ?? config4?.upload.ttlSeconds ?? 3600,
459528
+ s3: {
459529
+ bucket: process.env.VIBE_UPLOAD_S3_BUCKET ?? config4?.upload.s3?.bucket,
459530
+ region: process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? config4?.upload.s3?.region,
459531
+ prefix: process.env.VIBE_UPLOAD_S3_PREFIX ?? config4?.upload.s3?.prefix,
459532
+ publicBaseUrl: process.env.VIBE_UPLOAD_PUBLIC_BASE_URL ?? config4?.upload.s3?.publicBaseUrl
459537
459533
  }
459538
- let elevenlabsApiKey;
459539
- if (regenerateNarration) {
459540
- elevenlabsApiKey = await getApiKey("ELEVENLABS_API_KEY", "ElevenLabs") ?? void 0;
459541
- if (!elevenlabsApiKey) {
459542
- return { ...result, error: "ElevenLabs API key required. Run 'vibe setup' or set ELEVENLABS_API_KEY in .env" };
459543
- }
459534
+ };
459535
+ }
459536
+ async function resolveUploadHost() {
459537
+ const settings = await resolveUploadSettings();
459538
+ if (settings.provider === "s3") {
459539
+ const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
459540
+ const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
459541
+ const sessionToken = process.env.AWS_SESSION_TOKEN;
459542
+ const { bucket, region } = settings.s3;
459543
+ if (!accessKeyId || !secretAccessKey || !bucket || !region) {
459544
+ throw new Error(
459545
+ "S3 upload host requires AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, and VIBE_UPLOAD_S3_BUCKET."
459546
+ );
459544
459547
  }
459545
- let storyboardMutated = false;
459546
- for (const sceneNum of options.scenes) {
459547
- const segment = segments[sceneNum - 1];
459548
- const narrationPath = resolve49(outputDir, `narration-${sceneNum}.mp3`);
459549
- const imagePath = resolve49(outputDir, `scene-${sceneNum}.png`);
459550
- const videoPath = resolve49(outputDir, `scene-${sceneNum}.mp4`);
459551
- let sceneFailed = false;
459552
- if (regenerateNarration && elevenlabsApiKey) {
459553
- options.onProgress?.(`Scene ${sceneNum}: regenerating narration...`);
459554
- const elevenlabs = new ElevenLabsProvider();
459555
- await elevenlabs.initialize({ apiKey: elevenlabsApiKey });
459556
- const narrationText = segment.narration || segment.description;
459557
- const ttsResult = await elevenlabs.textToSpeech(narrationText, {
459558
- voiceId: options.voice
459548
+ return {
459549
+ provider: "s3",
459550
+ async uploadImage(imageBuffer, opts) {
459551
+ const ext = extensionFor(opts);
459552
+ const prefix = safePrefix(settings.s3.prefix);
459553
+ const key2 = `${prefix}/${Date.now()}-${randomUUID2()}.${ext}`;
459554
+ const presignedUrl = presignS3PutUrl({
459555
+ accessKeyId,
459556
+ secretAccessKey,
459557
+ sessionToken,
459558
+ region,
459559
+ bucket,
459560
+ key: key2,
459561
+ ttlSeconds: settings.ttlSeconds
459559
459562
  });
459560
- if (ttsResult.success && ttsResult.audioBuffer) {
459561
- await writeFile32(narrationPath, ttsResult.audioBuffer);
459562
- segment.duration = await getAudioDuration(narrationPath);
459563
- storyboardMutated = true;
459564
- } else {
459565
- sceneFailed = true;
459566
- }
459567
- }
459568
- if (!sceneFailed && regenerateImage && imageApiKey) {
459569
- options.onProgress?.(`Scene ${sceneNum}: regenerating image...`);
459570
- const imageProvider = options.imageProvider || "openai";
459571
- const characterDesc = segment.characterDescription || segments[0]?.characterDescription;
459572
- let imagePrompt = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
459573
- if (characterDesc) {
459574
- imagePrompt = `${imagePrompt}
459575
-
459576
- IMPORTANT - Character appearance must match exactly: ${characterDesc}`;
459577
- }
459578
- let referenceImageBuffer;
459579
- const refSceneNum = options.referenceScene;
459580
- if (refSceneNum && refSceneNum >= 1 && refSceneNum <= segments.length && refSceneNum !== sceneNum) {
459581
- const refImagePath = resolve49(outputDir, `scene-${refSceneNum}.png`);
459582
- if (existsSync48(refImagePath)) {
459583
- referenceImageBuffer = await readFile24(refImagePath);
459584
- }
459585
- } else if (!refSceneNum) {
459586
- for (let i = 1; i <= segments.length; i++) {
459587
- if (i !== sceneNum) {
459588
- const otherImagePath = resolve49(outputDir, `scene-${i}.png`);
459589
- if (existsSync48(otherImagePath)) {
459590
- referenceImageBuffer = await readFile24(otherImagePath);
459591
- break;
459592
- }
459593
- }
459594
- }
459563
+ const response = await fetch(presignedUrl, {
459564
+ method: "PUT",
459565
+ headers: {
459566
+ "content-type": opts?.mimeType ?? "application/octet-stream"
459567
+ },
459568
+ body: new Uint8Array(imageBuffer)
459569
+ });
459570
+ if (!response.ok) {
459571
+ throw new Error(`S3 upload failed (${response.status}): ${response.statusText}`);
459595
459572
  }
459596
- const dalleImageSizes = {
459597
- "16:9": "1536x1024",
459598
- "9:16": "1024x1536",
459599
- "1:1": "1024x1024"
459573
+ return {
459574
+ provider: "s3",
459575
+ url: publicS3Url({ region, bucket, key: key2, publicBaseUrl: settings.s3.publicBaseUrl }),
459576
+ expiresAt: new Date(Date.now() + settings.ttlSeconds * 1e3).toISOString()
459600
459577
  };
459601
- let imageBuffer;
459602
- let imageUrl;
459603
- if (imageProvider === "openai") {
459604
- const openaiImage = new OpenAIImageProvider();
459605
- await openaiImage.initialize({ apiKey: imageApiKey });
459606
- const imageResult = await openaiImage.generateImage(imagePrompt, {
459607
- size: dalleImageSizes[options.aspectRatio || "16:9"] || "1536x1024",
459608
- quality: "standard"
459609
- });
459610
- if (imageResult.success && imageResult.images?.[0]) {
459611
- const img = imageResult.images[0];
459612
- if (img.base64) imageBuffer = Buffer.from(img.base64, "base64");
459613
- else if (img.url) imageUrl = img.url;
459614
- }
459615
- } else if (imageProvider === "gemini") {
459616
- const gemini = new GeminiProvider();
459617
- await gemini.initialize({ apiKey: imageApiKey });
459618
- if (referenceImageBuffer) {
459619
- const simplifiedVisuals = segment.visuals.split(/[,.]/).find(
459620
- (part) => part.includes("standing") || part.includes("sitting") || part.includes("walking") || part.includes("lying") || part.includes("reaching") || part.includes("looking") || part.includes("working") || part.includes("coding") || part.includes("typing")
459621
- ) || segment.visuals.split(".")[0];
459622
- const editPrompt = `Generate a new image showing the SAME SINGLE person from the reference image in a new scene.
459623
-
459624
- REFERENCE: Look at the person in the reference image - their face, hair, build, and overall appearance.
459625
-
459626
- NEW SCENE: ${simplifiedVisuals}
459627
-
459628
- CRITICAL RULES:
459629
- 1. Show ONLY ONE person - the exact same individual from the reference image
459630
- 2. The person must have the IDENTICAL face, hair style, and body type
459631
- 3. Do NOT show multiple people or duplicate the character
459632
- 4. Create a single moment in time, one pose, one action
459633
- 5. Match the art style and quality of the reference image
459634
-
459635
- Generate the single-person scene image now.`;
459636
- const imageResult = await gemini.editImage([referenceImageBuffer], editPrompt, {
459637
- aspectRatio: options.aspectRatio || "16:9"
459638
- });
459639
- if (imageResult.success && imageResult.images?.[0]?.base64) {
459640
- imageBuffer = Buffer.from(imageResult.images[0].base64, "base64");
459641
- }
459642
- } else {
459643
- const imageResult = await gemini.generateImage(imagePrompt, {
459644
- aspectRatio: options.aspectRatio || "16:9"
459645
- });
459646
- if (imageResult.success && imageResult.images?.[0]?.base64) {
459647
- imageBuffer = Buffer.from(imageResult.images[0].base64, "base64");
459648
- }
459649
- }
459650
- } else if (imageProvider === "grok") {
459651
- const grok = new GrokProvider();
459652
- await grok.initialize({ apiKey: imageApiKey });
459653
- const imageResult = await grok.generateImage(imagePrompt, {
459654
- aspectRatio: options.aspectRatio || "16:9"
459655
- });
459656
- if (imageResult.success && imageResult.images?.[0]) {
459657
- const img = imageResult.images[0];
459658
- if (img.base64) imageBuffer = Buffer.from(img.base64, "base64");
459659
- else if (img.url) imageUrl = img.url;
459660
- }
459661
- }
459662
- if (imageBuffer) {
459663
- await writeFile32(imagePath, imageBuffer);
459664
- } else if (imageUrl) {
459665
- const response = await fetch(imageUrl);
459666
- const buffer = Buffer.from(await response.arrayBuffer());
459667
- await writeFile32(imagePath, buffer);
459668
- } else {
459669
- sceneFailed = true;
459670
- }
459671
- }
459672
- if (sceneFailed) {
459673
- result.failedScenes.push(sceneNum);
459674
- continue;
459675
459578
  }
459676
- if (regenerateVideo && videoApiKey) {
459677
- options.onProgress?.(`Scene ${sceneNum}: regenerating video (${options.generator || "grok"})...`);
459678
- if (!existsSync48(imagePath)) {
459679
- result.failedScenes.push(sceneNum);
459680
- continue;
459681
- }
459682
- const imageBuffer = await readFile24(imagePath);
459683
- const videoDuration = segment.duration > 5 ? 10 : 5;
459684
- const maxRetries = options.retries ?? DEFAULT_VIDEO_RETRIES;
459685
- if (options.generator === "grok") {
459686
- const grok = new GrokProvider();
459687
- await grok.initialize({ apiKey: videoApiKey });
459688
- const ext = extname10(imagePath).toLowerCase().slice(1);
459689
- const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
459690
- const referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
459691
- const grokDuration = Math.min(15, Math.max(1, segment.duration));
459692
- const taskResult = await generateVideoWithRetryGrok(
459693
- grok,
459694
- segment,
459695
- {
459696
- duration: grokDuration,
459697
- aspectRatio: options.aspectRatio || "16:9",
459698
- referenceImage
459699
- },
459700
- maxRetries
459701
- );
459702
- if (taskResult) {
459703
- try {
459704
- const waitResult = await grok.waitForCompletion(taskResult.requestId, void 0, 3e5);
459705
- if (waitResult.status === "completed" && waitResult.videoUrl) {
459706
- const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
459707
- await writeFile32(videoPath, buffer);
459708
- const targetDuration = segment.duration;
459709
- const actualVideoDuration = await getVideoDuration(videoPath);
459710
- if (actualVideoDuration < targetDuration - 0.1) {
459711
- const extendedPath = resolve49(outputDir, `scene-${sceneNum}-extended.mp4`);
459712
- await extendVideoNaturally(videoPath, targetDuration, extendedPath);
459713
- await unlink6(videoPath);
459714
- await rename6(extendedPath, videoPath);
459715
- }
459716
- result.regeneratedScenes.push(sceneNum);
459717
- } else {
459718
- logSceneFailure("Grok", `scene ${sceneNum}`, waitResult);
459719
- result.failedScenes.push(sceneNum);
459720
- }
459721
- } catch (err) {
459722
- logSceneFailure("Grok", `scene ${sceneNum}`, err);
459723
- result.failedScenes.push(sceneNum);
459724
- }
459725
- } else {
459726
- result.failedScenes.push(sceneNum);
459727
- }
459728
- } else if (options.generator === "veo") {
459729
- const veo = new GeminiProvider();
459730
- await veo.initialize({ apiKey: videoApiKey });
459731
- const ext = extname10(imagePath).toLowerCase().slice(1);
459732
- const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
459733
- const referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
459734
- const veoDuration = segment.duration > 6 ? 8 : segment.duration > 4 ? 6 : 4;
459735
- const taskResult = await generateVideoWithRetryVeo(
459736
- veo,
459737
- segment,
459738
- {
459739
- duration: veoDuration,
459740
- aspectRatio: options.aspectRatio || "16:9",
459741
- referenceImage
459742
- },
459743
- maxRetries
459744
- );
459745
- if (taskResult) {
459746
- try {
459747
- const waitResult = await veo.waitForVideoCompletion(taskResult.operationName, void 0, 3e5);
459748
- if (waitResult.status === "completed" && waitResult.videoUrl) {
459749
- const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
459750
- await writeFile32(videoPath, buffer);
459751
- const targetDuration = segment.duration;
459752
- const actualVideoDuration = await getVideoDuration(videoPath);
459753
- if (actualVideoDuration < targetDuration - 0.1) {
459754
- const extendedPath = resolve49(outputDir, `scene-${sceneNum}-extended.mp4`);
459755
- await extendVideoNaturally(videoPath, targetDuration, extendedPath);
459756
- await unlink6(videoPath);
459757
- await rename6(extendedPath, videoPath);
459758
- }
459759
- result.regeneratedScenes.push(sceneNum);
459760
- } else {
459761
- logSceneFailure("Veo", `scene ${sceneNum}`, waitResult);
459762
- result.failedScenes.push(sceneNum);
459763
- }
459764
- } catch (err) {
459765
- logSceneFailure("Veo", `scene ${sceneNum}`, err);
459766
- result.failedScenes.push(sceneNum);
459767
- }
459768
- } else {
459769
- result.failedScenes.push(sceneNum);
459770
- }
459771
- } else if (options.generator === "kling" || !options.generator) {
459772
- const kling = new KlingProvider();
459773
- await kling.initialize({ apiKey: videoApiKey });
459774
- if (!kling.isConfigured()) {
459775
- result.failedScenes.push(sceneNum);
459776
- continue;
459777
- }
459778
- const imgbbApiKey = await getApiKeyFromConfig("imgbb") || process.env.IMGBB_API_KEY;
459779
- let imageUrl;
459780
- if (imgbbApiKey) {
459781
- const uploadResult = await uploadToImgbb(imageBuffer, imgbbApiKey);
459782
- if (uploadResult.success && uploadResult.url) {
459783
- imageUrl = uploadResult.url;
459784
- }
459785
- }
459786
- const taskResult = await generateVideoWithRetryKling(
459787
- kling,
459788
- segment,
459789
- {
459790
- duration: videoDuration,
459791
- aspectRatio: options.aspectRatio || "16:9",
459792
- referenceImage: imageUrl
459793
- },
459794
- maxRetries
459795
- );
459796
- if (taskResult) {
459797
- try {
459798
- const waitResult = await kling.waitForCompletion(taskResult.taskId, taskResult.type, void 0, 6e5);
459799
- if (waitResult.status === "completed" && waitResult.videoUrl) {
459800
- const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
459801
- await writeFile32(videoPath, buffer);
459802
- await extendVideoToTarget(
459803
- videoPath,
459804
- segment.duration,
459805
- outputDir,
459806
- `Scene ${sceneNum}`,
459807
- {
459808
- kling,
459809
- videoId: waitResult.videoId,
459810
- onProgress: options.onProgress
459811
- }
459812
- );
459813
- result.regeneratedScenes.push(sceneNum);
459814
- } else {
459815
- logSceneFailure("Kling", `scene ${sceneNum}`, waitResult);
459816
- result.failedScenes.push(sceneNum);
459817
- }
459818
- } catch (err) {
459819
- logSceneFailure("Kling", `scene ${sceneNum}`, err);
459820
- result.failedScenes.push(sceneNum);
459821
- }
459822
- } else {
459823
- result.failedScenes.push(sceneNum);
459824
- }
459825
- } else {
459826
- const runway = new RunwayProvider();
459827
- await runway.initialize({ apiKey: videoApiKey });
459828
- const ext = extname10(imagePath).toLowerCase().slice(1);
459829
- const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
459830
- const referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
459831
- const aspectRatio = options.aspectRatio === "1:1" ? "16:9" : options.aspectRatio || "16:9";
459832
- const taskResult = await generateVideoWithRetryRunway(
459833
- runway,
459834
- segment,
459835
- referenceImage,
459836
- { duration: videoDuration, aspectRatio },
459837
- maxRetries
459838
- );
459839
- if (taskResult) {
459840
- try {
459841
- const waitResult = await runway.waitForCompletion(taskResult.taskId, void 0, 3e5);
459842
- if (waitResult.status === "completed" && waitResult.videoUrl) {
459843
- const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
459844
- await writeFile32(videoPath, buffer);
459845
- const targetDuration = segment.duration;
459846
- const actualVideoDuration = await getVideoDuration(videoPath);
459847
- if (actualVideoDuration < targetDuration - 0.1) {
459848
- const extendedPath = resolve49(outputDir, `scene-${sceneNum}-extended.mp4`);
459849
- await extendVideoNaturally(videoPath, targetDuration, extendedPath);
459850
- await unlink6(videoPath);
459851
- await rename6(extendedPath, videoPath);
459852
- }
459853
- result.regeneratedScenes.push(sceneNum);
459854
- } else {
459855
- logSceneFailure("Runway", `scene ${sceneNum}`, waitResult);
459856
- result.failedScenes.push(sceneNum);
459857
- }
459858
- } catch (err) {
459859
- logSceneFailure("Runway", `scene ${sceneNum}`, err);
459860
- result.failedScenes.push(sceneNum);
459861
- }
459862
- } else {
459863
- result.failedScenes.push(sceneNum);
459864
- }
459865
- }
459866
- } else if (!sceneFailed) {
459867
- result.regeneratedScenes.push(sceneNum);
459579
+ };
459580
+ }
459581
+ return {
459582
+ provider: "imgbb",
459583
+ async uploadImage(imageBuffer) {
459584
+ const imgbbKey = await getApiKeyFromConfig("imgbb") || process.env.IMGBB_API_KEY;
459585
+ if (!imgbbKey) {
459586
+ throw new Error("IMGBB_API_KEY required for image-to-video uploads.");
459868
459587
  }
459869
- }
459870
- if (storyboardMutated) {
459871
- let currentTime = 0;
459872
- for (const segment of segments) {
459873
- segment.startTime = currentTime;
459874
- currentTime += segment.duration;
459588
+ const result = await uploadToImgbb(imageBuffer, imgbbKey);
459589
+ if (!result.success || !result.url) {
459590
+ throw new Error(`ImgBB upload failed: ${result.error ?? "unknown error"}`);
459875
459591
  }
459876
- const serialized = storyboardPath.endsWith(".yaml") ? (0, import_yaml6.stringify)({ scenes: segments }, { indent: 2 }) : JSON.stringify(segments, null, 2);
459877
- await writeFile32(storyboardPath, serialized, "utf-8");
459592
+ return { provider: "imgbb", url: result.url };
459878
459593
  }
459879
- result.success = result.failedScenes.length === 0;
459880
- return result;
459881
- } catch (error) {
459882
- return {
459883
- ...result,
459884
- error: error instanceof Error ? error.message : String(error)
459885
- };
459886
- }
459594
+ };
459887
459595
  }
459888
- var import_yaml6;
459889
- var init_ai_script_pipeline = __esm({
459890
- "../cli/src/commands/ai-script-pipeline.ts"() {
459596
+ var init_upload_host = __esm({
459597
+ "../cli/src/utils/upload-host.ts"() {
459891
459598
  "use strict";
459892
- import_yaml6 = __toESM(require_dist(), 1);
459893
- init_dist();
459894
- init_api_key();
459895
459599
  init_config();
459896
- init_audio();
459897
- init_ai_helpers();
459898
459600
  init_video_utils();
459899
- init_video_providers();
459900
459601
  }
459901
459602
  });
459902
459603
 
459903
459604
  // ../cli/src/commands/generate/video.ts
459904
- import { resolve as resolve50 } from "node:path";
459905
- import { readFile as readFile25, writeFile as writeFile33 } from "node:fs/promises";
459605
+ import { resolve as resolve49 } from "node:path";
459606
+ import { readFile as readFile24, writeFile as writeFile32 } from "node:fs/promises";
459906
459607
  function registerVideoCommand(parent) {
459907
- parent.command("video").alias("vid").description("Generate video using AI (Seedance, Grok, Kling, Runway, or Veo)").argument("[prompt]", "Text prompt describing the video (interactive if omitted)").option("-p, --provider <provider>", "Provider: seedance (ByteDance Seedance 2.0 via fal.ai), grok, kling, runway, veo. `fal` is a deprecated v0.x alias for seedance and will be removed in 1.0.").option("-k, --api-key <key>", "API key (or set FAL_KEY / XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY / GOOGLE_API_KEY env)").option("-o, --output <path>", "Output file path (downloads video)").option("-i, --image <path>", "Reference image for image-to-video").option(
459608
+ parent.command("video").alias("vid").description("Generate video using AI (Seedance, Grok, Kling, Runway, or Veo)").argument("[prompt]", "Text prompt describing the video (interactive if omitted)").option(
459609
+ "-p, --provider <provider>",
459610
+ "Provider: seedance (ByteDance Seedance 2.0 via fal.ai), grok, kling, runway, veo. `fal` is a deprecated v0.x alias for seedance and will be removed in 1.0."
459611
+ ).option(
459612
+ "-k, --api-key <key>",
459613
+ "API key (or set FAL_KEY / XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY / GOOGLE_API_KEY env)"
459614
+ ).option("-o, --output <path>", "Output file path (downloads video)").option("-i, --image <path>", "Reference image for image-to-video").option(
459908
459615
  "-d, --duration <sec>",
459909
459616
  "Duration in seconds. Seedance accepts 4-15; Kling accepts 5 or 10; Veo maps to 6 or 8.",
459910
459617
  "5"
459911
- ).option("-r, --ratio <ratio>", "Aspect ratio: 16:9, 9:16, or 1:1 (auto-detected from image if omitted)").option("--seed <number>", "Random seed for reproducibility (Runway only)").option("--mode <mode>", "Generation mode: std or pro (Kling only)", "std").option("--seedance-model <model>", "Seedance variant: quality or fast (fal.ai only)", "quality").option("--negative <prompt>", "Negative prompt - what to avoid (Kling/Veo)").option("--resolution <res>", "Video resolution: 720p, 1080p, 4k (Veo only)").option("--last-frame <path>", "Last frame image for frame interpolation (Veo only)").option("--ref-images <paths...>", "Reference images for character consistency (Veo 3.1 only, max 3)").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("--runway-model <model>", "Runway model: gen4.5 (default, text+image-to-video), gen4_turbo (image-to-video only)", "gen4.5").option("--no-wait", "Start generation and return task ID without waiting").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
459618
+ ).option(
459619
+ "-r, --ratio <ratio>",
459620
+ "Aspect ratio: 16:9, 9:16, or 1:1 (auto-detected from image if omitted)"
459621
+ ).option("--seed <number>", "Random seed for reproducibility (Runway only)").option("--mode <mode>", "Generation mode: std or pro (Kling only)", "std").option(
459622
+ "--seedance-model <model>",
459623
+ "Seedance variant: quality or fast (fal.ai only)",
459624
+ "quality"
459625
+ ).option("--negative <prompt>", "Negative prompt - what to avoid (Kling/Veo)").option("--resolution <res>", "Video resolution: 720p, 1080p, 4k (Veo only)").option("--last-frame <path>", "Last frame image for frame interpolation (Veo only)").option(
459626
+ "--ref-images <paths...>",
459627
+ "Reference images for character consistency (Veo 3.1 only, max 3)"
459628
+ ).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(
459629
+ "--runway-model <model>",
459630
+ "Runway model: gen4.5 (default, text+image-to-video), gen4_turbo (image-to-video only)",
459631
+ "gen4.5"
459632
+ ).option("--no-wait", "Start generation and return task ID without waiting").option("--dry-run", "Preview parameters without executing").addHelpText(
459633
+ "after",
459634
+ `
459912
459635
  Examples:
459913
459636
  $ vibe generate video "dancing cat" -o cat.mp4 # Seedance when FAL_KEY is set
459914
459637
  $ vibe gen vid "cinematic city timelapse" -o city.mp4 -p seedance # Seedance via fal.ai
459915
459638
  $ vibe gen vid "city timelapse" -o city.mp4 -p kling # Kling
459916
459639
  $ vibe gen vid "epic scene" -i frame.png -o out.mp4 -p runway # Image-to-video
459917
459640
  $ vibe gen vid "ocean waves" -o waves.mp4 -p veo --resolution 1080p # Veo
459918
- $ vibe gen vid "sunset" -o sun.mp4 -d 10 --dry-run --json`).action(async (prompt3, options) => {
459641
+ $ vibe gen vid "sunset" -o sun.mp4 -d 10 --dry-run --json`
459642
+ ).action(async (prompt3, options) => {
459919
459643
  const startedAt = Date.now();
459920
459644
  try {
459921
459645
  if (!prompt3) {
@@ -459926,10 +459650,7 @@ Examples:
459926
459650
  }
459927
459651
  } else {
459928
459652
  exitWithError(
459929
- usageError(
459930
- "Prompt argument is required.",
459931
- "Usage: vibe generate video <prompt>"
459932
- )
459653
+ usageError("Prompt argument is required.", "Usage: vibe generate video <prompt>")
459933
459654
  );
459934
459655
  }
459935
459656
  }
@@ -459937,13 +459658,16 @@ Examples:
459937
459658
  if (options.output) {
459938
459659
  validateOutputPath(options.output);
459939
459660
  }
459661
+ await loadProviderDefaults();
459940
459662
  if (options.duration !== void 0) {
459941
459663
  const d = parseFloat(options.duration);
459942
459664
  if (!Number.isFinite(d) || d <= 0 || d > 60) {
459943
- exitWithError(usageError(
459944
- `Invalid --duration: ${options.duration}`,
459945
- "Must be a positive number \u2264 60 seconds."
459946
- ));
459665
+ exitWithError(
459666
+ usageError(
459667
+ `Invalid --duration: ${options.duration}`,
459668
+ "Must be a positive number \u2264 60 seconds."
459669
+ )
459670
+ );
459947
459671
  }
459948
459672
  }
459949
459673
  const validProviders = ["runway", "kling", "veo", "grok", "seedance", "fal"];
@@ -459985,10 +459709,12 @@ Examples:
459985
459709
  provider = resolved?.name ?? "grok";
459986
459710
  }
459987
459711
  let referenceImage;
459712
+ let referenceImageBuffer;
459713
+ let referenceImageMimeType;
459988
459714
  let isImageToVideo = false;
459989
459715
  if (options.image) {
459990
- const imagePath = resolve50(process.cwd(), options.image);
459991
- const imageBuffer = await readFile25(imagePath);
459716
+ const imagePath = resolve49(process.cwd(), options.image);
459717
+ const imageBuffer = await readFile24(imagePath);
459992
459718
  const ext = options.image.toLowerCase().split(".").pop();
459993
459719
  const mimeTypes = {
459994
459720
  jpg: "image/jpeg",
@@ -459998,6 +459724,8 @@ Examples:
459998
459724
  webp: "image/webp"
459999
459725
  };
460000
459726
  const mimeType = mimeTypes[ext || "png"] || "image/png";
459727
+ referenceImageBuffer = imageBuffer;
459728
+ referenceImageMimeType = mimeType;
460001
459729
  referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
460002
459730
  isImageToVideo = true;
460003
459731
  if (!options.ratio) {
@@ -460122,20 +459850,22 @@ Examples:
460122
459850
  }
460123
459851
  let klingImage = referenceImage;
460124
459852
  if (klingImage && klingImage.startsWith("data:")) {
460125
- spinner2.text = "Uploading image to ImgBB for Kling...";
460126
- const imgbbKey = await getApiKeyFromConfig("imgbb") || process.env.IMGBB_API_KEY;
460127
- if (!imgbbKey) {
460128
- spinner2.fail("ImgBB API key required");
460129
- exitWithError(authError("IMGBB_API_KEY", "ImgBB"));
460130
- }
460131
- const base64Data = klingImage.split(",")[1];
460132
- const imageBuffer = Buffer.from(base64Data, "base64");
460133
- const uploadResult = await uploadToImgbb(imageBuffer, imgbbKey);
460134
- if (!uploadResult.success || !uploadResult.url) {
460135
- spinner2.fail("ImgBB upload failed");
460136
- exitWithError(apiError(`ImgBB upload failed: ${uploadResult.error}`, true));
460137
- }
460138
- klingImage = uploadResult.url;
459853
+ try {
459854
+ const uploadHost = await resolveUploadHost();
459855
+ spinner2.text = `Uploading image via ${uploadHost.provider} for Kling...`;
459856
+ const upload = await uploadHost.uploadImage(referenceImageBuffer, {
459857
+ filename: options.image,
459858
+ mimeType: referenceImageMimeType
459859
+ });
459860
+ klingImage = upload.url;
459861
+ } catch (err) {
459862
+ spinner2.fail("Image upload failed");
459863
+ const message = err instanceof Error ? err.message : String(err);
459864
+ if (message.includes("IMGBB_API_KEY")) {
459865
+ exitWithError(authError("IMGBB_API_KEY", "ImgBB"));
459866
+ }
459867
+ exitWithError(apiError(message, true));
459868
+ }
460139
459869
  spinner2.text = "Starting video generation...";
460140
459870
  }
460141
459871
  result = await kling.generateVideo(prompt3, {
@@ -460190,8 +459920,8 @@ Examples:
460190
459920
  const veoDuration = parseInt(options.duration) <= 6 ? 6 : 8;
460191
459921
  let lastFrame;
460192
459922
  if (options.lastFrame) {
460193
- const lastFramePath = resolve50(process.cwd(), options.lastFrame);
460194
- const lastFrameBuffer = await readFile25(lastFramePath);
459923
+ const lastFramePath = resolve49(process.cwd(), options.lastFrame);
459924
+ const lastFrameBuffer = await readFile24(lastFramePath);
460195
459925
  const ext = options.lastFrame.toLowerCase().split(".").pop();
460196
459926
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
460197
459927
  lastFrame = `data:${mimeType};base64,${lastFrameBuffer.toString("base64")}`;
@@ -460200,8 +459930,8 @@ Examples:
460200
459930
  if (options.refImages && options.refImages.length > 0) {
460201
459931
  refImages = [];
460202
459932
  for (const refPath of options.refImages.slice(0, 3)) {
460203
- const absRefPath = resolve50(process.cwd(), refPath);
460204
- const refBuffer = await readFile25(absRefPath);
459933
+ const absRefPath = resolve49(process.cwd(), refPath);
459934
+ const refBuffer = await readFile24(absRefPath);
460205
459935
  const ext = refPath.toLowerCase().split(".").pop();
460206
459936
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
460207
459937
  refImages.push({ base64: refBuffer.toString("base64"), mimeType });
@@ -460282,20 +460012,22 @@ Examples:
460282
460012
  await fal.initialize({ apiKey });
460283
460013
  let falImage = referenceImage;
460284
460014
  if (falImage && falImage.startsWith("data:")) {
460285
- spinner2.text = "Uploading image to ImgBB for Seedance...";
460286
- const imgbbKey = await getApiKeyFromConfig("imgbb") || process.env.IMGBB_API_KEY;
460287
- if (!imgbbKey) {
460288
- spinner2.fail("ImgBB API key required for Seedance image-to-video");
460289
- exitWithError(authError("IMGBB_API_KEY", "ImgBB"));
460290
- }
460291
- const base64Data = falImage.split(",")[1];
460292
- const imageBuffer = Buffer.from(base64Data, "base64");
460293
- const uploadResult = await uploadToImgbb(imageBuffer, imgbbKey);
460294
- if (!uploadResult.success || !uploadResult.url) {
460295
- spinner2.fail("ImgBB upload failed");
460296
- exitWithError(apiError(`ImgBB upload failed: ${uploadResult.error}`, true));
460015
+ try {
460016
+ const uploadHost = await resolveUploadHost();
460017
+ spinner2.text = `Uploading image via ${uploadHost.provider} for Seedance...`;
460018
+ const upload = await uploadHost.uploadImage(referenceImageBuffer, {
460019
+ filename: options.image,
460020
+ mimeType: referenceImageMimeType
460021
+ });
460022
+ falImage = upload.url;
460023
+ } catch (err) {
460024
+ spinner2.fail("Image upload failed");
460025
+ const message = err instanceof Error ? err.message : String(err);
460026
+ if (message.includes("IMGBB_API_KEY")) {
460027
+ exitWithError(authError("IMGBB_API_KEY", "ImgBB"));
460028
+ }
460029
+ exitWithError(apiError(message, true));
460297
460030
  }
460298
- falImage = uploadResult.url;
460299
460031
  }
460300
460032
  spinner2.text = "Generating video with fal.ai Seedance 2.0 (this may take 1-3 minutes)...";
460301
460033
  const seedanceModel = String(options.seedanceModel ?? "quality").toLowerCase();
@@ -460319,8 +460051,8 @@ Examples:
460319
460051
  let outputPath;
460320
460052
  if (options.output && finalResult.videoUrl) {
460321
460053
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
460322
- outputPath = resolve50(process.cwd(), options.output);
460323
- await writeFile33(outputPath, buffer);
460054
+ outputPath = resolve49(process.cwd(), options.output);
460055
+ await writeFile32(outputPath, buffer);
460324
460056
  }
460325
460057
  outputSuccess({
460326
460058
  command: "generate video",
@@ -460347,14 +460079,12 @@ Examples:
460347
460079
  const downloadSpinner = ora("Downloading video...").start();
460348
460080
  try {
460349
460081
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
460350
- const outputPath = resolve50(process.cwd(), options.output);
460351
- await writeFile33(outputPath, buffer);
460082
+ const outputPath = resolve49(process.cwd(), options.output);
460083
+ await writeFile32(outputPath, buffer);
460352
460084
  downloadSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
460353
460085
  } catch (err) {
460354
460086
  downloadSpinner.fail(
460355
- source_default.red(
460356
- `Failed to download video: ${err instanceof Error ? err.message : err}`
460357
- )
460087
+ source_default.red(`Failed to download video: ${err instanceof Error ? err.message : err}`)
460358
460088
  );
460359
460089
  }
460360
460090
  }
@@ -460372,11 +460102,10 @@ var init_video = __esm({
460372
460102
  init_dist();
460373
460103
  init_api_key();
460374
460104
  init_tty();
460375
- init_config();
460376
460105
  init_output();
460377
460106
  init_validate();
460378
460107
  init_provider_resolver();
460379
- init_ai_script_pipeline();
460108
+ init_upload_host();
460380
460109
  init_ai_helpers();
460381
460110
  }
460382
460111
  });
@@ -460488,8 +460217,8 @@ __export(ai_video_exports, {
460488
460217
  executeVideoGenerate: () => executeVideoGenerate,
460489
460218
  executeVideoStatus: () => executeVideoStatus
460490
460219
  });
460491
- import { readFile as readFile26, writeFile as writeFile34 } from "node:fs/promises";
460492
- import { resolve as resolve51 } from "node:path";
460220
+ import { readFile as readFile25, writeFile as writeFile33 } from "node:fs/promises";
460221
+ import { resolve as resolve50 } from "node:path";
460493
460222
  async function executeVideoGenerate(options) {
460494
460223
  const {
460495
460224
  prompt: prompt3,
@@ -460519,12 +460248,22 @@ async function executeVideoGenerate(options) {
460519
460248
  const key2 = apiKey || process.env[envKeyMap[provider] || ""];
460520
460249
  if (!key2) return { success: false, error: `${envKeyMap[provider]} required for ${provider}` };
460521
460250
  let referenceImage;
460251
+ let referenceImageBuffer;
460252
+ let referenceImageMimeType;
460522
460253
  if (image) {
460523
- const imagePath = resolve51(process.cwd(), image);
460524
- const imageBuffer = await readFile26(imagePath);
460254
+ const imagePath = resolve50(process.cwd(), image);
460255
+ const imageBuffer = await readFile25(imagePath);
460525
460256
  const ext = image.toLowerCase().split(".").pop();
460526
- const mimeTypes = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", webp: "image/webp" };
460257
+ const mimeTypes = {
460258
+ jpg: "image/jpeg",
460259
+ jpeg: "image/jpeg",
460260
+ png: "image/png",
460261
+ gif: "image/gif",
460262
+ webp: "image/webp"
460263
+ };
460527
460264
  const mimeType = mimeTypes[ext || "png"] || "image/png";
460265
+ referenceImageBuffer = imageBuffer;
460266
+ referenceImageMimeType = mimeType;
460528
460267
  referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
460529
460268
  }
460530
460269
  if (provider === "seedance" || provider === "fal") {
@@ -460532,12 +460271,12 @@ async function executeVideoGenerate(options) {
460532
460271
  await fal.initialize({ apiKey: key2 });
460533
460272
  let falImage = referenceImage;
460534
460273
  if (falImage && falImage.startsWith("data:")) {
460535
- const imgbbKey = process.env.IMGBB_API_KEY;
460536
- if (!imgbbKey) return { success: false, error: "IMGBB_API_KEY required for Seedance image-to-video" };
460537
- const base64Data = falImage.split(",")[1];
460538
- const uploadResult = await uploadToImgbb(Buffer.from(base64Data, "base64"), imgbbKey);
460539
- if (!uploadResult.success || !uploadResult.url) return { success: false, error: `ImgBB upload failed: ${uploadResult.error}` };
460540
- falImage = uploadResult.url;
460274
+ const uploadHost = await resolveUploadHost();
460275
+ const upload = await uploadHost.uploadImage(referenceImageBuffer, {
460276
+ filename: image,
460277
+ mimeType: referenceImageMimeType
460278
+ });
460279
+ falImage = upload.url;
460541
460280
  }
460542
460281
  const model = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
460543
460282
  const result = await fal.generateVideo(prompt3, {
@@ -460548,14 +460287,22 @@ async function executeVideoGenerate(options) {
460548
460287
  negativePrompt: negative,
460549
460288
  model
460550
460289
  });
460551
- if (result.status === "failed") return { success: false, error: result.error || "Seedance generation failed" };
460290
+ if (result.status === "failed")
460291
+ return { success: false, error: result.error || "Seedance generation failed" };
460552
460292
  let outputPath;
460553
460293
  if (output3 && result.videoUrl) {
460554
460294
  const buffer = await downloadVideo(result.videoUrl, key2);
460555
- outputPath = resolve51(process.cwd(), output3);
460556
- await writeFile34(outputPath, buffer);
460295
+ outputPath = resolve50(process.cwd(), output3);
460296
+ await writeFile33(outputPath, buffer);
460557
460297
  }
460558
- return { success: true, taskId: result.id, status: "completed", videoUrl: result.videoUrl, outputPath, provider: "seedance" };
460298
+ return {
460299
+ success: true,
460300
+ taskId: result.id,
460301
+ status: "completed",
460302
+ videoUrl: result.videoUrl,
460303
+ outputPath,
460304
+ provider: "seedance"
460305
+ };
460559
460306
  } else if (provider === "runway") {
460560
460307
  const runway = new RunwayProvider();
460561
460308
  await runway.initialize({ apiKey: key2 });
@@ -460566,30 +460313,41 @@ async function executeVideoGenerate(options) {
460566
460313
  aspectRatio: ratio,
460567
460314
  seed
460568
460315
  });
460569
- if (result.status === "failed") return { success: false, error: result.error || "Runway generation failed" };
460570
- if (!wait) return { success: true, taskId: result.id, status: "processing", provider: "runway" };
460316
+ if (result.status === "failed")
460317
+ return { success: false, error: result.error || "Runway generation failed" };
460318
+ if (!wait)
460319
+ return { success: true, taskId: result.id, status: "processing", provider: "runway" };
460571
460320
  const finalResult = await runway.waitForCompletion(result.id, () => {
460572
460321
  }, 3e5);
460573
- if (finalResult.status !== "completed") return { success: false, error: finalResult.error || "Runway generation failed" };
460322
+ if (finalResult.status !== "completed")
460323
+ return { success: false, error: finalResult.error || "Runway generation failed" };
460574
460324
  let outputPath;
460575
460325
  if (output3 && finalResult.videoUrl) {
460576
460326
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
460577
- outputPath = resolve51(process.cwd(), output3);
460578
- await writeFile34(outputPath, buffer);
460327
+ outputPath = resolve50(process.cwd(), output3);
460328
+ await writeFile33(outputPath, buffer);
460579
460329
  }
460580
- return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, duration: finalResult.duration, outputPath, provider: "runway" };
460330
+ return {
460331
+ success: true,
460332
+ taskId: result.id,
460333
+ status: "completed",
460334
+ videoUrl: finalResult.videoUrl,
460335
+ duration: finalResult.duration,
460336
+ outputPath,
460337
+ provider: "runway"
460338
+ };
460581
460339
  } else if (provider === "kling") {
460582
460340
  const kling = new KlingProvider();
460583
460341
  await kling.initialize({ apiKey: key2 });
460584
460342
  if (!kling.isConfigured()) return { success: false, error: "Invalid Kling API key format" };
460585
460343
  let klingImage = referenceImage;
460586
460344
  if (klingImage && klingImage.startsWith("data:")) {
460587
- const imgbbKey = process.env.IMGBB_API_KEY;
460588
- if (!imgbbKey) return { success: false, error: "IMGBB_API_KEY required for Kling image-to-video" };
460589
- const base64Data = klingImage.split(",")[1];
460590
- const uploadResult = await uploadToImgbb(Buffer.from(base64Data, "base64"), imgbbKey);
460591
- if (!uploadResult.success || !uploadResult.url) return { success: false, error: `ImgBB upload failed: ${uploadResult.error}` };
460592
- klingImage = uploadResult.url;
460345
+ const uploadHost = await resolveUploadHost();
460346
+ const upload = await uploadHost.uploadImage(referenceImageBuffer, {
460347
+ filename: image,
460348
+ mimeType: referenceImageMimeType
460349
+ });
460350
+ klingImage = upload.url;
460593
460351
  }
460594
460352
  const result = await kling.generateVideo(prompt3, {
460595
460353
  prompt: prompt3,
@@ -460599,23 +460357,38 @@ async function executeVideoGenerate(options) {
460599
460357
  negativePrompt: negative,
460600
460358
  mode
460601
460359
  });
460602
- if (result.status === "failed") return { success: false, error: result.error || "Kling generation failed" };
460360
+ if (result.status === "failed")
460361
+ return { success: false, error: result.error || "Kling generation failed" };
460603
460362
  const taskType = referenceImage ? "image2video" : "text2video";
460604
- if (!wait) return { success: true, taskId: result.id, status: "processing", provider: "kling" };
460363
+ if (!wait)
460364
+ return { success: true, taskId: result.id, status: "processing", provider: "kling" };
460605
460365
  const finalResult = await kling.waitForCompletion(result.id, taskType, () => {
460606
460366
  }, 6e5);
460607
- if (finalResult.status !== "completed") return { success: false, error: finalResult.error || "Kling generation failed" };
460367
+ if (finalResult.status !== "completed")
460368
+ return { success: false, error: finalResult.error || "Kling generation failed" };
460608
460369
  let outputPath;
460609
460370
  if (output3 && finalResult.videoUrl) {
460610
460371
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
460611
- outputPath = resolve51(process.cwd(), output3);
460612
- await writeFile34(outputPath, buffer);
460372
+ outputPath = resolve50(process.cwd(), output3);
460373
+ await writeFile33(outputPath, buffer);
460613
460374
  }
460614
- return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, duration: finalResult.duration, outputPath, provider: "kling" };
460375
+ return {
460376
+ success: true,
460377
+ taskId: result.id,
460378
+ status: "completed",
460379
+ videoUrl: finalResult.videoUrl,
460380
+ duration: finalResult.duration,
460381
+ outputPath,
460382
+ provider: "kling"
460383
+ };
460615
460384
  } else if (provider === "veo") {
460616
460385
  const gemini = new GeminiProvider();
460617
460386
  await gemini.initialize({ apiKey: key2 });
460618
- const veoModelMap = { "3.0": "veo-3.0-generate-preview", "3.1": "veo-3.1-generate-preview", "3.1-fast": "veo-3.1-fast-generate-preview" };
460387
+ const veoModelMap = {
460388
+ "3.0": "veo-3.0-generate-preview",
460389
+ "3.1": "veo-3.1-generate-preview",
460390
+ "3.1-fast": "veo-3.1-fast-generate-preview"
460391
+ };
460619
460392
  const model = veoModelMap[veoModel] || "veo-3.1-fast-generate-preview";
460620
460393
  const veoDuration = duration <= 6 ? 6 : 8;
460621
460394
  const result = await gemini.generateVideo(prompt3, {
@@ -460627,18 +460400,27 @@ async function executeVideoGenerate(options) {
460627
460400
  negativePrompt: negative,
460628
460401
  resolution
460629
460402
  });
460630
- if (result.status === "failed") return { success: false, error: result.error || "Veo generation failed" };
460403
+ if (result.status === "failed")
460404
+ return { success: false, error: result.error || "Veo generation failed" };
460631
460405
  if (!wait) return { success: true, taskId: result.id, status: "processing", provider: "veo" };
460632
460406
  const finalResult = await gemini.waitForVideoCompletion(result.id, () => {
460633
460407
  }, 3e5);
460634
- if (finalResult.status !== "completed") return { success: false, error: finalResult.error || "Veo generation failed" };
460408
+ if (finalResult.status !== "completed")
460409
+ return { success: false, error: finalResult.error || "Veo generation failed" };
460635
460410
  let outputPath;
460636
460411
  if (output3 && finalResult.videoUrl) {
460637
460412
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
460638
- outputPath = resolve51(process.cwd(), output3);
460639
- await writeFile34(outputPath, buffer);
460413
+ outputPath = resolve50(process.cwd(), output3);
460414
+ await writeFile33(outputPath, buffer);
460640
460415
  }
460641
- return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, outputPath, provider: "veo" };
460416
+ return {
460417
+ success: true,
460418
+ taskId: result.id,
460419
+ status: "completed",
460420
+ videoUrl: finalResult.videoUrl,
460421
+ outputPath,
460422
+ provider: "veo"
460423
+ };
460642
460424
  } else if (provider === "grok") {
460643
460425
  const grok = new GrokProvider();
460644
460426
  await grok.initialize({ apiKey: key2 });
@@ -460648,28 +460430,52 @@ async function executeVideoGenerate(options) {
460648
460430
  duration,
460649
460431
  aspectRatio: ratio
460650
460432
  });
460651
- if (result.status === "failed") return { success: false, error: result.error || "Grok generation failed" };
460652
- if (!wait) return { success: true, taskId: result.id, status: "processing", provider: "grok" };
460433
+ if (result.status === "failed")
460434
+ return { success: false, error: result.error || "Grok generation failed" };
460435
+ if (!wait)
460436
+ return { success: true, taskId: result.id, status: "processing", provider: "grok" };
460653
460437
  const finalResult = await grok.waitForCompletion(result.id, () => {
460654
460438
  }, 3e5);
460655
- if (finalResult.status !== "completed") return { success: false, error: finalResult.error || "Grok generation failed" };
460439
+ if (finalResult.status !== "completed")
460440
+ return { success: false, error: finalResult.error || "Grok generation failed" };
460656
460441
  let outputPath;
460657
460442
  if (output3 && finalResult.videoUrl) {
460658
460443
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
460659
- outputPath = resolve51(process.cwd(), output3);
460660
- await writeFile34(outputPath, buffer);
460444
+ outputPath = resolve50(process.cwd(), output3);
460445
+ await writeFile33(outputPath, buffer);
460661
460446
  }
460662
- return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, duration: finalResult.duration, outputPath, provider: "grok" };
460447
+ return {
460448
+ success: true,
460449
+ taskId: result.id,
460450
+ status: "completed",
460451
+ videoUrl: finalResult.videoUrl,
460452
+ duration: finalResult.duration,
460453
+ outputPath,
460454
+ provider: "grok"
460455
+ };
460663
460456
  }
460664
460457
  return { success: false, error: `Unsupported provider: ${provider}` };
460665
460458
  } catch (error) {
460666
- return { success: false, error: `Video generation failed: ${error instanceof Error ? error.message : String(error)}` };
460459
+ return {
460460
+ success: false,
460461
+ error: `Video generation failed: ${error instanceof Error ? error.message : String(error)}`
460462
+ };
460667
460463
  }
460668
460464
  }
460669
460465
  async function executeVideoStatus(options) {
460670
- const { taskId, provider = "runway", taskType = "text2video", wait = false, output: output3, apiKey } = options;
460466
+ const {
460467
+ taskId,
460468
+ provider = "runway",
460469
+ taskType = "text2video",
460470
+ wait = false,
460471
+ output: output3,
460472
+ apiKey
460473
+ } = options;
460671
460474
  try {
460672
- const envKeyMap = { runway: "RUNWAY_API_SECRET", kling: "KLING_API_KEY" };
460475
+ const envKeyMap = {
460476
+ runway: "RUNWAY_API_SECRET",
460477
+ kling: "KLING_API_KEY"
460478
+ };
460673
460479
  const key2 = apiKey || process.env[envKeyMap[provider] || ""];
460674
460480
  if (!key2) return { success: false, error: `${envKeyMap[provider]} required` };
460675
460481
  if (provider === "runway") {
@@ -460683,10 +460489,17 @@ async function executeVideoStatus(options) {
460683
460489
  let outputPath;
460684
460490
  if (output3 && result.videoUrl) {
460685
460491
  const buffer = await downloadVideo(result.videoUrl, key2);
460686
- outputPath = resolve51(process.cwd(), output3);
460687
- await writeFile34(outputPath, buffer);
460492
+ outputPath = resolve50(process.cwd(), output3);
460493
+ await writeFile33(outputPath, buffer);
460688
460494
  }
460689
- return { success: true, taskId, status: result.status, progress: result.progress, videoUrl: result.videoUrl, outputPath };
460495
+ return {
460496
+ success: true,
460497
+ taskId,
460498
+ status: result.status,
460499
+ progress: result.progress,
460500
+ videoUrl: result.videoUrl,
460501
+ outputPath
460502
+ };
460690
460503
  } else if (provider === "kling") {
460691
460504
  const kling = new KlingProvider();
460692
460505
  await kling.initialize({ apiKey: key2 });
@@ -460698,14 +460511,24 @@ async function executeVideoStatus(options) {
460698
460511
  let outputPath;
460699
460512
  if (output3 && result.videoUrl) {
460700
460513
  const buffer = await downloadVideo(result.videoUrl, key2);
460701
- outputPath = resolve51(process.cwd(), output3);
460702
- await writeFile34(outputPath, buffer);
460514
+ outputPath = resolve50(process.cwd(), output3);
460515
+ await writeFile33(outputPath, buffer);
460703
460516
  }
460704
- return { success: true, taskId, status: result.status, videoUrl: result.videoUrl, duration: result.duration, outputPath };
460517
+ return {
460518
+ success: true,
460519
+ taskId,
460520
+ status: result.status,
460521
+ videoUrl: result.videoUrl,
460522
+ duration: result.duration,
460523
+ outputPath
460524
+ };
460705
460525
  }
460706
460526
  return { success: false, error: `Unsupported provider: ${provider}` };
460707
460527
  } catch (error) {
460708
- return { success: false, error: `Status check failed: ${error instanceof Error ? error.message : String(error)}` };
460528
+ return {
460529
+ success: false,
460530
+ error: `Status check failed: ${error instanceof Error ? error.message : String(error)}`
460531
+ };
460709
460532
  }
460710
460533
  }
460711
460534
  async function executeVideoCancel(options) {
@@ -460718,11 +460541,24 @@ async function executeVideoCancel(options) {
460718
460541
  const success = await runway.cancelGeneration(taskId);
460719
460542
  return { success };
460720
460543
  } catch (error) {
460721
- return { success: false, error: `Cancel failed: ${error instanceof Error ? error.message : String(error)}` };
460544
+ return {
460545
+ success: false,
460546
+ error: `Cancel failed: ${error instanceof Error ? error.message : String(error)}`
460547
+ };
460722
460548
  }
460723
460549
  }
460724
460550
  async function executeVideoExtend(options) {
460725
- const { videoId, provider = "kling", prompt: prompt3, duration = 5, negative, veoModel = "3.1", output: output3, wait = true, apiKey } = options;
460551
+ const {
460552
+ videoId,
460553
+ provider = "kling",
460554
+ prompt: prompt3,
460555
+ duration = 5,
460556
+ negative,
460557
+ veoModel = "3.1",
460558
+ output: output3,
460559
+ wait = true,
460560
+ apiKey
460561
+ } = options;
460726
460562
  try {
460727
460563
  if (provider === "kling") {
460728
460564
  const key2 = apiKey || process.env.KLING_API_KEY;
@@ -460735,52 +460571,76 @@ async function executeVideoExtend(options) {
460735
460571
  negativePrompt: negative,
460736
460572
  duration: String(duration)
460737
460573
  });
460738
- if (result.status === "failed") return { success: false, error: result.error || "Kling extension failed" };
460574
+ if (result.status === "failed")
460575
+ return { success: false, error: result.error || "Kling extension failed" };
460739
460576
  if (!wait) return { success: true, taskId: result.id, status: "processing" };
460740
460577
  const finalResult = await kling.waitForExtendCompletion(result.id, () => {
460741
460578
  }, 6e5);
460742
- if (finalResult.status !== "completed") return { success: false, error: finalResult.error || "Kling extension failed" };
460579
+ if (finalResult.status !== "completed")
460580
+ return { success: false, error: finalResult.error || "Kling extension failed" };
460743
460581
  let outputPath;
460744
460582
  if (output3 && finalResult.videoUrl) {
460745
460583
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
460746
- outputPath = resolve51(process.cwd(), output3);
460747
- await writeFile34(outputPath, buffer);
460584
+ outputPath = resolve50(process.cwd(), output3);
460585
+ await writeFile33(outputPath, buffer);
460748
460586
  }
460749
- return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, duration: finalResult.duration, outputPath };
460587
+ return {
460588
+ success: true,
460589
+ taskId: result.id,
460590
+ status: "completed",
460591
+ videoUrl: finalResult.videoUrl,
460592
+ duration: finalResult.duration,
460593
+ outputPath
460594
+ };
460750
460595
  } else if (provider === "veo") {
460751
460596
  const key2 = apiKey || process.env.GOOGLE_API_KEY;
460752
460597
  if (!key2) return { success: false, error: "GOOGLE_API_KEY required" };
460753
460598
  const gemini = new GeminiProvider();
460754
460599
  await gemini.initialize({ apiKey: key2 });
460755
- const veoModelMap = { "3.0": "veo-3.0-generate-preview", "3.1": "veo-3.1-generate-preview", "3.1-fast": "veo-3.1-fast-generate-preview" };
460600
+ const veoModelMap = {
460601
+ "3.0": "veo-3.0-generate-preview",
460602
+ "3.1": "veo-3.1-generate-preview",
460603
+ "3.1-fast": "veo-3.1-fast-generate-preview"
460604
+ };
460756
460605
  const model = veoModelMap[veoModel] || "veo-3.1-generate-preview";
460757
460606
  const result = await gemini.extendVideo(videoId, prompt3, {
460758
460607
  duration,
460759
460608
  model
460760
460609
  });
460761
- if (result.status === "failed") return { success: false, error: result.error || "Veo extension failed" };
460610
+ if (result.status === "failed")
460611
+ return { success: false, error: result.error || "Veo extension failed" };
460762
460612
  if (!wait) return { success: true, taskId: result.id, status: "processing" };
460763
460613
  const finalResult = await gemini.waitForVideoCompletion(result.id, () => {
460764
460614
  }, 3e5);
460765
- if (finalResult.status !== "completed") return { success: false, error: finalResult.error || "Veo extension failed" };
460615
+ if (finalResult.status !== "completed")
460616
+ return { success: false, error: finalResult.error || "Veo extension failed" };
460766
460617
  let outputPath;
460767
460618
  if (output3 && finalResult.videoUrl) {
460768
460619
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
460769
- outputPath = resolve51(process.cwd(), output3);
460770
- await writeFile34(outputPath, buffer);
460620
+ outputPath = resolve50(process.cwd(), output3);
460621
+ await writeFile33(outputPath, buffer);
460771
460622
  }
460772
- return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, outputPath };
460623
+ return {
460624
+ success: true,
460625
+ taskId: result.id,
460626
+ status: "completed",
460627
+ videoUrl: finalResult.videoUrl,
460628
+ outputPath
460629
+ };
460773
460630
  }
460774
460631
  return { success: false, error: `Unsupported provider: ${provider}` };
460775
460632
  } catch (error) {
460776
- return { success: false, error: `Video extension failed: ${error instanceof Error ? error.message : String(error)}` };
460633
+ return {
460634
+ success: false,
460635
+ error: `Video extension failed: ${error instanceof Error ? error.message : String(error)}`
460636
+ };
460777
460637
  }
460778
460638
  }
460779
460639
  var init_ai_video = __esm({
460780
460640
  "../cli/src/commands/ai-video.ts"() {
460781
460641
  "use strict";
460782
460642
  init_dist();
460783
- init_ai_script_pipeline();
460643
+ init_upload_host();
460784
460644
  init_ai_helpers();
460785
460645
  }
460786
460646
  });
@@ -464360,11 +464220,17 @@ var generateMotionTool = defineTool({
464360
464220
  }),
464361
464221
  async execute(args) {
464362
464222
  const result = await executeMotion(args);
464363
- if (!result.success) return { success: false, error: result.error ?? "Motion generation failed" };
464223
+ if (!result.success)
464224
+ return { success: false, error: result.error ?? "Motion generation failed" };
464364
464225
  const out = result.compositedPath ?? result.renderedPath ?? result.codePath;
464365
464226
  return {
464366
464227
  success: true,
464367
- data: { codePath: result.codePath, renderedPath: result.renderedPath, compositedPath: result.compositedPath, componentName: result.componentName },
464228
+ data: {
464229
+ codePath: result.codePath,
464230
+ renderedPath: result.renderedPath,
464231
+ compositedPath: result.compositedPath,
464232
+ componentName: result.componentName
464233
+ },
464368
464234
  humanLines: [`\u2705 Motion generated \u2192 ${out}`]
464369
464235
  };
464370
464236
  }
@@ -464428,7 +464294,9 @@ var generateMusicTool = defineTool({
464428
464294
  return {
464429
464295
  success: true,
464430
464296
  data: { outputPath: result.outputPath, provider: result.provider, duration: result.duration },
464431
- humanLines: [`\u2705 Music${result.provider ? ` (${result.provider})` : ""} \u2192 ${result.outputPath ?? "(async)"}`]
464297
+ humanLines: [
464298
+ `\u2705 Music${result.provider ? ` (${result.provider})` : ""} \u2192 ${result.outputPath ?? "(async)"}`
464299
+ ]
464432
464300
  };
464433
464301
  }
464434
464302
  });
@@ -464445,7 +464313,12 @@ var generateMusicStatusTool = defineTool({
464445
464313
  if (!result.success) return { success: false, error: result.error ?? "Music status failed" };
464446
464314
  return {
464447
464315
  success: true,
464448
- data: { taskId: result.taskId, status: result.status, audioUrl: result.audioUrl, error: result.error },
464316
+ data: {
464317
+ taskId: result.taskId,
464318
+ status: result.status,
464319
+ audioUrl: result.audioUrl,
464320
+ error: result.error
464321
+ },
464449
464322
  humanLines: [`Music task ${result.taskId}: ${result.status}`]
464450
464323
  };
464451
464324
  }
@@ -464457,7 +464330,9 @@ var generateImageTool = defineTool({
464457
464330
  description: "Generate an image using AI. Supports Gemini (free), OpenAI GPT Image, or Grok Imagine. Requires GOOGLE_API_KEY (Gemini), OPENAI_API_KEY (OpenAI), or XAI_API_KEY (Grok).",
464458
464331
  schema: z5.object({
464459
464332
  prompt: z5.string().describe("Image description prompt"),
464460
- provider: z5.enum(["gemini", "openai", "grok"]).optional().describe("Image provider (default: gemini)"),
464333
+ provider: z5.enum(["gemini", "openai", "grok"]).optional().describe(
464334
+ "Image provider (default: openai when OPENAI_API_KEY is configured, otherwise first configured provider)"
464335
+ ),
464461
464336
  output: z5.string().optional().describe("Output file path"),
464462
464337
  size: z5.string().optional().describe("Image size for OpenAI (1024x1024, 1536x1024, 1024x1536)"),
464463
464338
  ratio: z5.string().optional().describe("Aspect ratio for Gemini (1:1, 16:9, 9:16, 4:3, 3:4, etc.)"),
@@ -464467,10 +464342,16 @@ var generateImageTool = defineTool({
464467
464342
  }),
464468
464343
  async execute(args) {
464469
464344
  const result = await executeImageGenerate(args);
464470
- if (!result.success) return { success: false, error: result.error ?? "Image generation failed" };
464345
+ if (!result.success)
464346
+ return { success: false, error: result.error ?? "Image generation failed" };
464471
464347
  return {
464472
464348
  success: true,
464473
- data: { outputPath: result.outputPath, provider: result.provider, model: result.model, imageCount: result.images?.length },
464349
+ data: {
464350
+ outputPath: result.outputPath,
464351
+ provider: result.provider,
464352
+ model: result.model,
464353
+ imageCount: result.images?.length
464354
+ },
464474
464355
  humanLines: [`\u2705 Image (${result.provider}) \u2192 ${result.outputPath}`]
464475
464356
  };
464476
464357
  }
@@ -464492,7 +464373,9 @@ var generateStoryboardTool = defineTool({
464492
464373
  return {
464493
464374
  success: true,
464494
464375
  data: { segmentCount: result.segmentCount, outputPath: result.outputPath },
464495
- humanLines: [`\u2705 Storyboard: ${result.segmentCount} segments${result.outputPath ? ` \u2192 ${result.outputPath}` : ""}`]
464376
+ humanLines: [
464377
+ `\u2705 Storyboard: ${result.segmentCount} segments${result.outputPath ? ` \u2192 ${result.outputPath}` : ""}`
464378
+ ]
464496
464379
  };
464497
464380
  }
464498
464381
  });
@@ -464511,7 +464394,11 @@ var generateBackgroundTool = defineTool({
464511
464394
  if (!result.success) return { success: false, error: result.error ?? "Background failed" };
464512
464395
  return {
464513
464396
  success: true,
464514
- data: { imageUrl: result.imageUrl, outputPath: result.outputPath, revisedPrompt: result.revisedPrompt },
464397
+ data: {
464398
+ imageUrl: result.imageUrl,
464399
+ outputPath: result.outputPath,
464400
+ revisedPrompt: result.revisedPrompt
464401
+ },
464515
464402
  humanLines: [`\u2705 Background \u2192 ${result.outputPath ?? result.imageUrl}`]
464516
464403
  };
464517
464404
  }
@@ -464569,7 +464456,9 @@ var generateVideoTool = defineTool({
464569
464456
  outputPath: result.outputPath,
464570
464457
  provider: result.provider
464571
464458
  },
464572
- humanLines: [`\u2705 Video (${result.provider}, ${result.status})${result.outputPath ? ` \u2192 ${result.outputPath}` : ""}`]
464459
+ humanLines: [
464460
+ `\u2705 Video (${result.provider}, ${result.status})${result.outputPath ? ` \u2192 ${result.outputPath}` : ""}`
464461
+ ]
464573
464462
  };
464574
464463
  }
464575
464464
  });
@@ -464590,8 +464479,16 @@ var generateVideoStatusTool = defineTool({
464590
464479
  if (!result.success) return { success: false, error: result.error ?? "Status check failed" };
464591
464480
  return {
464592
464481
  success: true,
464593
- data: { taskId: result.taskId, status: result.status, progress: result.progress, videoUrl: result.videoUrl, outputPath: result.outputPath },
464594
- humanLines: [`Task ${result.taskId}: ${result.status}${result.progress !== void 0 ? ` (${result.progress}%)` : ""}`]
464482
+ data: {
464483
+ taskId: result.taskId,
464484
+ status: result.status,
464485
+ progress: result.progress,
464486
+ videoUrl: result.videoUrl,
464487
+ outputPath: result.outputPath
464488
+ },
464489
+ humanLines: [
464490
+ `Task ${result.taskId}: ${result.status}${result.progress !== void 0 ? ` (${result.progress}%)` : ""}`
464491
+ ]
464595
464492
  };
464596
464493
  }
464597
464494
  });
@@ -464633,8 +464530,16 @@ var generateVideoExtendTool = defineTool({
464633
464530
  if (!result.success) return { success: false, error: result.error ?? "Extend failed" };
464634
464531
  return {
464635
464532
  success: true,
464636
- data: { taskId: result.taskId, status: result.status, videoUrl: result.videoUrl, duration: result.duration, outputPath: result.outputPath },
464637
- humanLines: [`\u2705 Video extended (${result.status})${result.outputPath ? ` \u2192 ${result.outputPath}` : ""}`]
464533
+ data: {
464534
+ taskId: result.taskId,
464535
+ status: result.status,
464536
+ videoUrl: result.videoUrl,
464537
+ duration: result.duration,
464538
+ outputPath: result.outputPath
464539
+ },
464540
+ humanLines: [
464541
+ `\u2705 Video extended (${result.status})${result.outputPath ? ` \u2192 ${result.outputPath}` : ""}`
464542
+ ]
464638
464543
  };
464639
464544
  }
464640
464545
  });
@@ -464659,11 +464564,584 @@ import { writeFile as writeFile38 } from "node:fs/promises";
464659
464564
  import { tmpdir as tmpdir5 } from "node:os";
464660
464565
  import { join as join30 } from "node:path";
464661
464566
  import { z as z6 } from "zod";
464662
- init_ai_script_pipeline();
464567
+
464568
+ // ../cli/src/commands/ai-script-pipeline.ts
464569
+ var import_yaml6 = __toESM(require_dist(), 1);
464570
+ init_dist();
464571
+ init_api_key();
464572
+ init_config();
464573
+ init_audio();
464574
+ init_ai_helpers();
464575
+ init_video_utils();
464576
+ import { readFile as readFile26, writeFile as writeFile34, unlink as unlink6, rename as rename6 } from "node:fs/promises";
464577
+ import { resolve as resolve51, extname as extname11 } from "node:path";
464578
+ import { existsSync as existsSync48 } from "node:fs";
464579
+
464580
+ // ../cli/src/commands/_shared/video-providers.ts
464581
+ init_source();
464582
+ init_video_utils();
464583
+ async function generateVideoWithRetryGrok(grok, segment, options, maxRetries, onProgress) {
464584
+ const prompt3 = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
464585
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
464586
+ try {
464587
+ const result = await grok.generateVideo(prompt3, {
464588
+ prompt: prompt3,
464589
+ duration: options.duration,
464590
+ aspectRatio: options.aspectRatio,
464591
+ referenceImage: options.referenceImage
464592
+ });
464593
+ if (result.status !== "failed" && result.id) {
464594
+ return { requestId: result.id };
464595
+ }
464596
+ const providerErr = result.error || "Grok returned failed status";
464597
+ if (attempt < maxRetries) {
464598
+ onProgress?.(
464599
+ `\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`
464600
+ );
464601
+ await sleep(RETRY_DELAY_MS);
464602
+ } else {
464603
+ console.error(source_default.dim(`
464604
+ [Grok error: ${providerErr}]`));
464605
+ }
464606
+ } catch (err) {
464607
+ const errMsg = err instanceof Error ? err.message : String(err);
464608
+ if (attempt < maxRetries) {
464609
+ onProgress?.(`\u26A0 Error: ${errMsg.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464610
+ await sleep(RETRY_DELAY_MS);
464611
+ } else {
464612
+ console.error(source_default.dim(`
464613
+ [Grok error: ${errMsg}]`));
464614
+ }
464615
+ }
464616
+ }
464617
+ return null;
464618
+ }
464619
+ async function generateVideoWithRetryKling(kling, segment, options, maxRetries, onProgress) {
464620
+ const prompt3 = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
464621
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
464622
+ try {
464623
+ const result = await kling.generateVideo(prompt3, {
464624
+ prompt: prompt3,
464625
+ // Pass reference image (base64 or URL) - KlingProvider handles v1.5 fallback for base64
464626
+ referenceImage: options.referenceImage,
464627
+ duration: options.duration,
464628
+ aspectRatio: options.aspectRatio,
464629
+ mode: "std"
464630
+ // std mode for faster generation
464631
+ });
464632
+ if (result.status !== "failed" && result.id) {
464633
+ return {
464634
+ taskId: result.id,
464635
+ type: options.referenceImage ? "image2video" : "text2video"
464636
+ };
464637
+ }
464638
+ const providerErr = result.error || "Kling returned failed status";
464639
+ if (attempt < maxRetries) {
464640
+ onProgress?.(`\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464641
+ await sleep(RETRY_DELAY_MS);
464642
+ } else {
464643
+ console.error(source_default.dim(`
464644
+ [Kling error: ${providerErr}]`));
464645
+ }
464646
+ } catch (err) {
464647
+ const errMsg = err instanceof Error ? err.message : String(err);
464648
+ if (attempt < maxRetries) {
464649
+ onProgress?.(`\u26A0 Error: ${errMsg.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464650
+ await sleep(RETRY_DELAY_MS);
464651
+ } else {
464652
+ console.error(source_default.dim(`
464653
+ [Kling error: ${errMsg}]`));
464654
+ }
464655
+ }
464656
+ }
464657
+ return null;
464658
+ }
464659
+ async function generateVideoWithRetryRunway(runway, segment, referenceImage, options, maxRetries, onProgress) {
464660
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
464661
+ try {
464662
+ const result = await runway.generateVideo(segment.visuals, {
464663
+ prompt: segment.visuals,
464664
+ referenceImage,
464665
+ duration: options.duration,
464666
+ aspectRatio: options.aspectRatio
464667
+ });
464668
+ if (result.status !== "failed" && result.id) {
464669
+ return { taskId: result.id };
464670
+ }
464671
+ const providerErr = result.error || "Runway returned failed status";
464672
+ if (attempt < maxRetries) {
464673
+ onProgress?.(`\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464674
+ await sleep(RETRY_DELAY_MS);
464675
+ } else {
464676
+ console.error(source_default.dim(`
464677
+ [Runway error: ${providerErr}]`));
464678
+ }
464679
+ } catch (err) {
464680
+ const errMsg = err instanceof Error ? err.message : String(err);
464681
+ if (attempt < maxRetries) {
464682
+ onProgress?.(`\u26A0 Error: ${errMsg.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464683
+ await sleep(RETRY_DELAY_MS);
464684
+ } else {
464685
+ console.error(source_default.dim(`
464686
+ [Runway error: ${errMsg}]`));
464687
+ }
464688
+ }
464689
+ }
464690
+ return null;
464691
+ }
464692
+ async function generateVideoWithRetryVeo(gemini, segment, options, maxRetries, onProgress) {
464693
+ const prompt3 = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
464694
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
464695
+ try {
464696
+ const result = await gemini.generateVideo(prompt3, {
464697
+ prompt: prompt3,
464698
+ referenceImage: options.referenceImage,
464699
+ duration: options.duration,
464700
+ aspectRatio: options.aspectRatio,
464701
+ model: "veo-3.1-fast-generate-preview"
464702
+ });
464703
+ if (result.status !== "failed" && result.id) {
464704
+ return { operationName: result.id };
464705
+ }
464706
+ const providerErr = result.error || "Veo returned failed status";
464707
+ if (attempt < maxRetries) {
464708
+ onProgress?.(`\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464709
+ await sleep(RETRY_DELAY_MS);
464710
+ } else {
464711
+ console.error(source_default.dim(`
464712
+ [Veo error: ${providerErr}]`));
464713
+ }
464714
+ } catch (err) {
464715
+ const errMsg = err instanceof Error ? err.message : String(err);
464716
+ if (attempt < maxRetries) {
464717
+ onProgress?.(`\u26A0 Error: ${errMsg.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464718
+ await sleep(RETRY_DELAY_MS);
464719
+ } else {
464720
+ console.error(source_default.dim(`
464721
+ [Veo error: ${errMsg}]`));
464722
+ }
464723
+ }
464724
+ }
464725
+ return null;
464726
+ }
464727
+
464728
+ // ../cli/src/commands/ai-script-pipeline.ts
464729
+ async function executeRegenerateScene(options) {
464730
+ const result = {
464731
+ success: false,
464732
+ regeneratedScenes: [],
464733
+ failedScenes: []
464734
+ };
464735
+ try {
464736
+ const outputDir = resolve51(process.cwd(), options.projectDir);
464737
+ if (!existsSync48(outputDir)) {
464738
+ return { ...result, error: `Project directory not found: ${outputDir}` };
464739
+ }
464740
+ const yamlPath = resolve51(outputDir, "storyboard.yaml");
464741
+ const jsonPath = resolve51(outputDir, "storyboard.json");
464742
+ const storyboardPath = existsSync48(yamlPath) ? yamlPath : existsSync48(jsonPath) ? jsonPath : null;
464743
+ if (!storyboardPath) {
464744
+ return { ...result, error: `Storyboard not found in: ${outputDir} (expected storyboard.yaml or storyboard.json)` };
464745
+ }
464746
+ const storyboardContent = await readFile26(storyboardPath, "utf-8");
464747
+ const segments = storyboardPath.endsWith(".yaml") ? (0, import_yaml6.parse)(storyboardContent).scenes : JSON.parse(storyboardContent);
464748
+ for (const sceneNum of options.scenes) {
464749
+ if (sceneNum < 1 || sceneNum > segments.length) {
464750
+ return { ...result, error: `Scene ${sceneNum} does not exist. Storyboard has ${segments.length} scenes.` };
464751
+ }
464752
+ }
464753
+ const regenerateVideo = options.videoOnly || !options.narrationOnly && !options.imageOnly;
464754
+ const regenerateNarration = options.narrationOnly || !options.videoOnly && !options.imageOnly;
464755
+ const regenerateImage = options.imageOnly || !options.videoOnly && !options.narrationOnly;
464756
+ let videoApiKey;
464757
+ if (regenerateVideo) {
464758
+ const generatorKeyMap = {
464759
+ grok: { envVar: "XAI_API_KEY", name: "xAI (Grok)" },
464760
+ kling: { envVar: "KLING_API_KEY", name: "Kling" },
464761
+ runway: { envVar: "RUNWAY_API_SECRET", name: "Runway" },
464762
+ veo: { envVar: "GOOGLE_API_KEY", name: "Google (Veo)" }
464763
+ };
464764
+ const generator = options.generator || "grok";
464765
+ const genInfo = generatorKeyMap[generator];
464766
+ if (!genInfo) {
464767
+ return { ...result, error: `Invalid generator: ${generator}. Available: ${Object.keys(generatorKeyMap).join(", ")}` };
464768
+ }
464769
+ videoApiKey = await getApiKey(genInfo.envVar, genInfo.name) ?? void 0;
464770
+ if (!videoApiKey) {
464771
+ return { ...result, error: `${genInfo.name} API key required. Run 'vibe setup' or set ${genInfo.envVar} in .env` };
464772
+ }
464773
+ }
464774
+ let imageApiKey;
464775
+ if (regenerateImage) {
464776
+ const imageProvider = options.imageProvider || "openai";
464777
+ const imageKeyMap = {
464778
+ openai: { envVar: "OPENAI_API_KEY", name: "OpenAI" },
464779
+ gemini: { envVar: "GOOGLE_API_KEY", name: "Google" },
464780
+ grok: { envVar: "XAI_API_KEY", name: "xAI" }
464781
+ };
464782
+ const info = imageKeyMap[imageProvider];
464783
+ if (!info) {
464784
+ return { ...result, error: `Invalid imageProvider: ${imageProvider}` };
464785
+ }
464786
+ imageApiKey = await getApiKey(info.envVar, info.name) ?? void 0;
464787
+ if (!imageApiKey) {
464788
+ return { ...result, error: `${info.name} API key required. Run 'vibe setup' or set ${info.envVar} in .env` };
464789
+ }
464790
+ }
464791
+ let elevenlabsApiKey;
464792
+ if (regenerateNarration) {
464793
+ elevenlabsApiKey = await getApiKey("ELEVENLABS_API_KEY", "ElevenLabs") ?? void 0;
464794
+ if (!elevenlabsApiKey) {
464795
+ return { ...result, error: "ElevenLabs API key required. Run 'vibe setup' or set ELEVENLABS_API_KEY in .env" };
464796
+ }
464797
+ }
464798
+ let storyboardMutated = false;
464799
+ for (const sceneNum of options.scenes) {
464800
+ const segment = segments[sceneNum - 1];
464801
+ const narrationPath = resolve51(outputDir, `narration-${sceneNum}.mp3`);
464802
+ const imagePath = resolve51(outputDir, `scene-${sceneNum}.png`);
464803
+ const videoPath = resolve51(outputDir, `scene-${sceneNum}.mp4`);
464804
+ let sceneFailed = false;
464805
+ if (regenerateNarration && elevenlabsApiKey) {
464806
+ options.onProgress?.(`Scene ${sceneNum}: regenerating narration...`);
464807
+ const elevenlabs = new ElevenLabsProvider();
464808
+ await elevenlabs.initialize({ apiKey: elevenlabsApiKey });
464809
+ const narrationText = segment.narration || segment.description;
464810
+ const ttsResult = await elevenlabs.textToSpeech(narrationText, {
464811
+ voiceId: options.voice
464812
+ });
464813
+ if (ttsResult.success && ttsResult.audioBuffer) {
464814
+ await writeFile34(narrationPath, ttsResult.audioBuffer);
464815
+ segment.duration = await getAudioDuration(narrationPath);
464816
+ storyboardMutated = true;
464817
+ } else {
464818
+ sceneFailed = true;
464819
+ }
464820
+ }
464821
+ if (!sceneFailed && regenerateImage && imageApiKey) {
464822
+ options.onProgress?.(`Scene ${sceneNum}: regenerating image...`);
464823
+ const imageProvider = options.imageProvider || "openai";
464824
+ const characterDesc = segment.characterDescription || segments[0]?.characterDescription;
464825
+ let imagePrompt = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
464826
+ if (characterDesc) {
464827
+ imagePrompt = `${imagePrompt}
464828
+
464829
+ IMPORTANT - Character appearance must match exactly: ${characterDesc}`;
464830
+ }
464831
+ let referenceImageBuffer;
464832
+ const refSceneNum = options.referenceScene;
464833
+ if (refSceneNum && refSceneNum >= 1 && refSceneNum <= segments.length && refSceneNum !== sceneNum) {
464834
+ const refImagePath = resolve51(outputDir, `scene-${refSceneNum}.png`);
464835
+ if (existsSync48(refImagePath)) {
464836
+ referenceImageBuffer = await readFile26(refImagePath);
464837
+ }
464838
+ } else if (!refSceneNum) {
464839
+ for (let i = 1; i <= segments.length; i++) {
464840
+ if (i !== sceneNum) {
464841
+ const otherImagePath = resolve51(outputDir, `scene-${i}.png`);
464842
+ if (existsSync48(otherImagePath)) {
464843
+ referenceImageBuffer = await readFile26(otherImagePath);
464844
+ break;
464845
+ }
464846
+ }
464847
+ }
464848
+ }
464849
+ const dalleImageSizes = {
464850
+ "16:9": "1536x1024",
464851
+ "9:16": "1024x1536",
464852
+ "1:1": "1024x1024"
464853
+ };
464854
+ let imageBuffer;
464855
+ let imageUrl;
464856
+ if (imageProvider === "openai") {
464857
+ const openaiImage = new OpenAIImageProvider();
464858
+ await openaiImage.initialize({ apiKey: imageApiKey });
464859
+ const imageResult = await openaiImage.generateImage(imagePrompt, {
464860
+ size: dalleImageSizes[options.aspectRatio || "16:9"] || "1536x1024",
464861
+ quality: "standard"
464862
+ });
464863
+ if (imageResult.success && imageResult.images?.[0]) {
464864
+ const img = imageResult.images[0];
464865
+ if (img.base64) imageBuffer = Buffer.from(img.base64, "base64");
464866
+ else if (img.url) imageUrl = img.url;
464867
+ }
464868
+ } else if (imageProvider === "gemini") {
464869
+ const gemini = new GeminiProvider();
464870
+ await gemini.initialize({ apiKey: imageApiKey });
464871
+ if (referenceImageBuffer) {
464872
+ const simplifiedVisuals = segment.visuals.split(/[,.]/).find(
464873
+ (part) => part.includes("standing") || part.includes("sitting") || part.includes("walking") || part.includes("lying") || part.includes("reaching") || part.includes("looking") || part.includes("working") || part.includes("coding") || part.includes("typing")
464874
+ ) || segment.visuals.split(".")[0];
464875
+ const editPrompt = `Generate a new image showing the SAME SINGLE person from the reference image in a new scene.
464876
+
464877
+ REFERENCE: Look at the person in the reference image - their face, hair, build, and overall appearance.
464878
+
464879
+ NEW SCENE: ${simplifiedVisuals}
464880
+
464881
+ CRITICAL RULES:
464882
+ 1. Show ONLY ONE person - the exact same individual from the reference image
464883
+ 2. The person must have the IDENTICAL face, hair style, and body type
464884
+ 3. Do NOT show multiple people or duplicate the character
464885
+ 4. Create a single moment in time, one pose, one action
464886
+ 5. Match the art style and quality of the reference image
464887
+
464888
+ Generate the single-person scene image now.`;
464889
+ const imageResult = await gemini.editImage([referenceImageBuffer], editPrompt, {
464890
+ aspectRatio: options.aspectRatio || "16:9"
464891
+ });
464892
+ if (imageResult.success && imageResult.images?.[0]?.base64) {
464893
+ imageBuffer = Buffer.from(imageResult.images[0].base64, "base64");
464894
+ }
464895
+ } else {
464896
+ const imageResult = await gemini.generateImage(imagePrompt, {
464897
+ aspectRatio: options.aspectRatio || "16:9"
464898
+ });
464899
+ if (imageResult.success && imageResult.images?.[0]?.base64) {
464900
+ imageBuffer = Buffer.from(imageResult.images[0].base64, "base64");
464901
+ }
464902
+ }
464903
+ } else if (imageProvider === "grok") {
464904
+ const grok = new GrokProvider();
464905
+ await grok.initialize({ apiKey: imageApiKey });
464906
+ const imageResult = await grok.generateImage(imagePrompt, {
464907
+ aspectRatio: options.aspectRatio || "16:9"
464908
+ });
464909
+ if (imageResult.success && imageResult.images?.[0]) {
464910
+ const img = imageResult.images[0];
464911
+ if (img.base64) imageBuffer = Buffer.from(img.base64, "base64");
464912
+ else if (img.url) imageUrl = img.url;
464913
+ }
464914
+ }
464915
+ if (imageBuffer) {
464916
+ await writeFile34(imagePath, imageBuffer);
464917
+ } else if (imageUrl) {
464918
+ const response = await fetch(imageUrl);
464919
+ const buffer = Buffer.from(await response.arrayBuffer());
464920
+ await writeFile34(imagePath, buffer);
464921
+ } else {
464922
+ sceneFailed = true;
464923
+ }
464924
+ }
464925
+ if (sceneFailed) {
464926
+ result.failedScenes.push(sceneNum);
464927
+ continue;
464928
+ }
464929
+ if (regenerateVideo && videoApiKey) {
464930
+ options.onProgress?.(`Scene ${sceneNum}: regenerating video (${options.generator || "grok"})...`);
464931
+ if (!existsSync48(imagePath)) {
464932
+ result.failedScenes.push(sceneNum);
464933
+ continue;
464934
+ }
464935
+ const imageBuffer = await readFile26(imagePath);
464936
+ const videoDuration = segment.duration > 5 ? 10 : 5;
464937
+ const maxRetries = options.retries ?? DEFAULT_VIDEO_RETRIES;
464938
+ if (options.generator === "grok") {
464939
+ const grok = new GrokProvider();
464940
+ await grok.initialize({ apiKey: videoApiKey });
464941
+ const ext = extname11(imagePath).toLowerCase().slice(1);
464942
+ const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
464943
+ const referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
464944
+ const grokDuration = Math.min(15, Math.max(1, segment.duration));
464945
+ const taskResult = await generateVideoWithRetryGrok(
464946
+ grok,
464947
+ segment,
464948
+ {
464949
+ duration: grokDuration,
464950
+ aspectRatio: options.aspectRatio || "16:9",
464951
+ referenceImage
464952
+ },
464953
+ maxRetries
464954
+ );
464955
+ if (taskResult) {
464956
+ try {
464957
+ const waitResult = await grok.waitForCompletion(taskResult.requestId, void 0, 3e5);
464958
+ if (waitResult.status === "completed" && waitResult.videoUrl) {
464959
+ const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
464960
+ await writeFile34(videoPath, buffer);
464961
+ const targetDuration = segment.duration;
464962
+ const actualVideoDuration = await getVideoDuration(videoPath);
464963
+ if (actualVideoDuration < targetDuration - 0.1) {
464964
+ const extendedPath = resolve51(outputDir, `scene-${sceneNum}-extended.mp4`);
464965
+ await extendVideoNaturally(videoPath, targetDuration, extendedPath);
464966
+ await unlink6(videoPath);
464967
+ await rename6(extendedPath, videoPath);
464968
+ }
464969
+ result.regeneratedScenes.push(sceneNum);
464970
+ } else {
464971
+ logSceneFailure("Grok", `scene ${sceneNum}`, waitResult);
464972
+ result.failedScenes.push(sceneNum);
464973
+ }
464974
+ } catch (err) {
464975
+ logSceneFailure("Grok", `scene ${sceneNum}`, err);
464976
+ result.failedScenes.push(sceneNum);
464977
+ }
464978
+ } else {
464979
+ result.failedScenes.push(sceneNum);
464980
+ }
464981
+ } else if (options.generator === "veo") {
464982
+ const veo = new GeminiProvider();
464983
+ await veo.initialize({ apiKey: videoApiKey });
464984
+ const ext = extname11(imagePath).toLowerCase().slice(1);
464985
+ const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
464986
+ const referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
464987
+ const veoDuration = segment.duration > 6 ? 8 : segment.duration > 4 ? 6 : 4;
464988
+ const taskResult = await generateVideoWithRetryVeo(
464989
+ veo,
464990
+ segment,
464991
+ {
464992
+ duration: veoDuration,
464993
+ aspectRatio: options.aspectRatio || "16:9",
464994
+ referenceImage
464995
+ },
464996
+ maxRetries
464997
+ );
464998
+ if (taskResult) {
464999
+ try {
465000
+ const waitResult = await veo.waitForVideoCompletion(taskResult.operationName, void 0, 3e5);
465001
+ if (waitResult.status === "completed" && waitResult.videoUrl) {
465002
+ const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
465003
+ await writeFile34(videoPath, buffer);
465004
+ const targetDuration = segment.duration;
465005
+ const actualVideoDuration = await getVideoDuration(videoPath);
465006
+ if (actualVideoDuration < targetDuration - 0.1) {
465007
+ const extendedPath = resolve51(outputDir, `scene-${sceneNum}-extended.mp4`);
465008
+ await extendVideoNaturally(videoPath, targetDuration, extendedPath);
465009
+ await unlink6(videoPath);
465010
+ await rename6(extendedPath, videoPath);
465011
+ }
465012
+ result.regeneratedScenes.push(sceneNum);
465013
+ } else {
465014
+ logSceneFailure("Veo", `scene ${sceneNum}`, waitResult);
465015
+ result.failedScenes.push(sceneNum);
465016
+ }
465017
+ } catch (err) {
465018
+ logSceneFailure("Veo", `scene ${sceneNum}`, err);
465019
+ result.failedScenes.push(sceneNum);
465020
+ }
465021
+ } else {
465022
+ result.failedScenes.push(sceneNum);
465023
+ }
465024
+ } else if (options.generator === "kling" || !options.generator) {
465025
+ const kling = new KlingProvider();
465026
+ await kling.initialize({ apiKey: videoApiKey });
465027
+ if (!kling.isConfigured()) {
465028
+ result.failedScenes.push(sceneNum);
465029
+ continue;
465030
+ }
465031
+ const imgbbApiKey = await getApiKeyFromConfig("imgbb") || process.env.IMGBB_API_KEY;
465032
+ let imageUrl;
465033
+ if (imgbbApiKey) {
465034
+ const uploadResult = await uploadToImgbb(imageBuffer, imgbbApiKey);
465035
+ if (uploadResult.success && uploadResult.url) {
465036
+ imageUrl = uploadResult.url;
465037
+ }
465038
+ }
465039
+ const taskResult = await generateVideoWithRetryKling(
465040
+ kling,
465041
+ segment,
465042
+ {
465043
+ duration: videoDuration,
465044
+ aspectRatio: options.aspectRatio || "16:9",
465045
+ referenceImage: imageUrl
465046
+ },
465047
+ maxRetries
465048
+ );
465049
+ if (taskResult) {
465050
+ try {
465051
+ const waitResult = await kling.waitForCompletion(taskResult.taskId, taskResult.type, void 0, 6e5);
465052
+ if (waitResult.status === "completed" && waitResult.videoUrl) {
465053
+ const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
465054
+ await writeFile34(videoPath, buffer);
465055
+ await extendVideoToTarget(
465056
+ videoPath,
465057
+ segment.duration,
465058
+ outputDir,
465059
+ `Scene ${sceneNum}`,
465060
+ {
465061
+ kling,
465062
+ videoId: waitResult.videoId,
465063
+ onProgress: options.onProgress
465064
+ }
465065
+ );
465066
+ result.regeneratedScenes.push(sceneNum);
465067
+ } else {
465068
+ logSceneFailure("Kling", `scene ${sceneNum}`, waitResult);
465069
+ result.failedScenes.push(sceneNum);
465070
+ }
465071
+ } catch (err) {
465072
+ logSceneFailure("Kling", `scene ${sceneNum}`, err);
465073
+ result.failedScenes.push(sceneNum);
465074
+ }
465075
+ } else {
465076
+ result.failedScenes.push(sceneNum);
465077
+ }
465078
+ } else {
465079
+ const runway = new RunwayProvider();
465080
+ await runway.initialize({ apiKey: videoApiKey });
465081
+ const ext = extname11(imagePath).toLowerCase().slice(1);
465082
+ const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
465083
+ const referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
465084
+ const aspectRatio = options.aspectRatio === "1:1" ? "16:9" : options.aspectRatio || "16:9";
465085
+ const taskResult = await generateVideoWithRetryRunway(
465086
+ runway,
465087
+ segment,
465088
+ referenceImage,
465089
+ { duration: videoDuration, aspectRatio },
465090
+ maxRetries
465091
+ );
465092
+ if (taskResult) {
465093
+ try {
465094
+ const waitResult = await runway.waitForCompletion(taskResult.taskId, void 0, 3e5);
465095
+ if (waitResult.status === "completed" && waitResult.videoUrl) {
465096
+ const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
465097
+ await writeFile34(videoPath, buffer);
465098
+ const targetDuration = segment.duration;
465099
+ const actualVideoDuration = await getVideoDuration(videoPath);
465100
+ if (actualVideoDuration < targetDuration - 0.1) {
465101
+ const extendedPath = resolve51(outputDir, `scene-${sceneNum}-extended.mp4`);
465102
+ await extendVideoNaturally(videoPath, targetDuration, extendedPath);
465103
+ await unlink6(videoPath);
465104
+ await rename6(extendedPath, videoPath);
465105
+ }
465106
+ result.regeneratedScenes.push(sceneNum);
465107
+ } else {
465108
+ logSceneFailure("Runway", `scene ${sceneNum}`, waitResult);
465109
+ result.failedScenes.push(sceneNum);
465110
+ }
465111
+ } catch (err) {
465112
+ logSceneFailure("Runway", `scene ${sceneNum}`, err);
465113
+ result.failedScenes.push(sceneNum);
465114
+ }
465115
+ } else {
465116
+ result.failedScenes.push(sceneNum);
465117
+ }
465118
+ }
465119
+ } else if (!sceneFailed) {
465120
+ result.regeneratedScenes.push(sceneNum);
465121
+ }
465122
+ }
465123
+ if (storyboardMutated) {
465124
+ let currentTime = 0;
465125
+ for (const segment of segments) {
465126
+ segment.startTime = currentTime;
465127
+ currentTime += segment.duration;
465128
+ }
465129
+ const serialized = storyboardPath.endsWith(".yaml") ? (0, import_yaml6.stringify)({ scenes: segments }, { indent: 2 }) : JSON.stringify(segments, null, 2);
465130
+ await writeFile34(storyboardPath, serialized, "utf-8");
465131
+ }
465132
+ result.success = result.failedScenes.length === 0;
465133
+ return result;
465134
+ } catch (error) {
465135
+ return {
465136
+ ...result,
465137
+ error: error instanceof Error ? error.message : String(error)
465138
+ };
465139
+ }
465140
+ }
464663
465141
 
464664
465142
  // ../cli/src/commands/ai-highlights.ts
464665
465143
  import { readFile as readFile27, writeFile as writeFile35, mkdir as mkdir21 } from "node:fs/promises";
464666
- import { resolve as resolve53, dirname as dirname28, basename as basename12, extname as extname11 } from "node:path";
465144
+ import { resolve as resolve53, dirname as dirname28, basename as basename12, extname as extname12 } from "node:path";
464667
465145
  import { existsSync as existsSync49 } from "node:fs";
464668
465146
  init_dist();
464669
465147
  init_engine();
@@ -464698,7 +465176,7 @@ async function executeHighlights(options) {
464698
465176
  if (!existsSync49(absPath)) {
464699
465177
  return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: `File not found: ${absPath}` };
464700
465178
  }
464701
- const ext = extname11(absPath).toLowerCase();
465179
+ const ext = extname12(absPath).toLowerCase();
464702
465180
  const videoExtensions = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"];
464703
465181
  const isVideo = videoExtensions.includes(ext);
464704
465182
  const targetDuration = options.duration;
@@ -465035,7 +465513,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
465035
465513
  };
465036
465514
  for (let i = 0; i < selectedHighlights.length; i++) {
465037
465515
  const h = selectedHighlights[i];
465038
- const baseName = basename12(absPath, extname11(absPath));
465516
+ const baseName = basename12(absPath, extname12(absPath));
465039
465517
  const outputPath = resolve53(outputDir, `${baseName}-short-${i + 1}.mp4`);
465040
465518
  const { stdout: probeOut } = await execSafe("ffprobe", [
465041
465519
  "-v",
@@ -467149,7 +467627,7 @@ var exportTools = [exportVideoTool];
467149
467627
 
467150
467628
  // ../cli/src/tools/manifest/agent-only.ts
467151
467629
  import { readFile as readFile32, writeFile as writeFile43, readdir as readdir5, stat as stat4, access as access7, unlink as unlink7 } from "node:fs/promises";
467152
- import { resolve as resolve61, join as join33, basename as basename17, extname as extname12 } from "node:path";
467630
+ import { resolve as resolve61, join as join33, basename as basename17, extname as extname13 } from "node:path";
467153
467631
  import { z as z11 } from "zod";
467154
467632
  init_engine();
467155
467633
  init_exec_safe();
@@ -467172,7 +467650,7 @@ function formatSize(bytes) {
467172
467650
  return `${size.toFixed(1)}${units2[unit]}`;
467173
467651
  }
467174
467652
  function detectMediaType(filePath) {
467175
- const ext = extname12(filePath).toLowerCase();
467653
+ const ext = extname13(filePath).toLowerCase();
467176
467654
  if ([".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"].includes(ext)) return "video";
467177
467655
  if ([".mp3", ".wav", ".aac", ".flac", ".ogg", ".m4a"].includes(ext)) return "audio";
467178
467656
  if ([".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"].includes(ext)) return "image";
@@ -467199,7 +467677,7 @@ function isMediaFile(filePath) {
467199
467677
  ".webp",
467200
467678
  ".bmp"
467201
467679
  ];
467202
- return mediaExts.includes(extname12(filePath).toLowerCase());
467680
+ return mediaExts.includes(extname13(filePath).toLowerCase());
467203
467681
  }
467204
467682
  async function getMediaDuration2(filePath) {
467205
467683
  try {
@@ -467335,7 +467813,7 @@ var batchImportTool = defineTool({
467335
467813
  if (entry.isDirectory() && recursive) {
467336
467814
  await scanDir(entryPath);
467337
467815
  } else if (entry.isFile()) {
467338
- const ext = extname12(entry.name).toLowerCase();
467816
+ const ext = extname13(entry.name).toLowerCase();
467339
467817
  const matchesFilter = !filterExts || filterExts.includes(ext);
467340
467818
  if (matchesFilter && isMediaFile(entryPath)) mediaFiles.push(entryPath);
467341
467819
  }