@vibeframe/mcp-server 0.91.9 → 0.93.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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +1272 -772
  3. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -13996,6 +13996,9 @@ function getProvidersFor(kind) {
13996
13996
  function getProviderEnvVars() {
13997
13997
  return Object.fromEntries([...apiKeyRegistry.values()].map((k) => [k.configKey, k.envVar]));
13998
13998
  }
13999
+ function getProviderEnvAliases() {
14000
+ return Object.fromEntries([...apiKeyRegistry.values()].filter((k) => k.envAliases && k.envAliases.length > 0).map((k) => [k.envVar, k.envAliases ?? []]));
14001
+ }
13999
14002
  function getCommandKeyMap() {
14000
14003
  const map3 = {};
14001
14004
  for (const p of providerRegistry2.values()) {
@@ -14103,7 +14106,8 @@ var init_api_keys = __esm({
14103
14106
  });
14104
14107
  defineApiKey({
14105
14108
  configKey: "fal",
14106
- envVar: "FAL_KEY",
14109
+ envVar: "FAL_API_KEY",
14110
+ envAliases: ["FAL_KEY"],
14107
14111
  label: "fal.ai",
14108
14112
  showInSetup: true,
14109
14113
  setupDescription: "Seedance 2.0 video gen ($$, default since v0.57)",
@@ -14120,7 +14124,7 @@ var init_api_keys = __esm({
14120
14124
  label: "xAI",
14121
14125
  showInSetup: true,
14122
14126
  setupDescription: "Grok \u2014 video gen with audio ($$), image ($), Agent",
14123
- envExampleComment: "xAI API Key (Grok video generation \u2014 fallback when no FAL_KEY)",
14127
+ envExampleComment: "xAI API Key (Grok video generation \u2014 fallback when no FAL_API_KEY)",
14124
14128
  envExampleUrl: "https://console.x.ai/",
14125
14129
  keyFormat: { prefix: /^xai-/, example: "xai-..." }
14126
14130
  });
@@ -14169,8 +14173,8 @@ var init_api_keys = __esm({
14169
14173
  configKey: "imgbb",
14170
14174
  envVar: "IMGBB_API_KEY",
14171
14175
  label: "ImgBB",
14172
- showInSetup: false,
14173
- // not prompted in setup wizard — internal upload host
14176
+ showInSetup: true,
14177
+ setupDescription: "image hosting for Seedance/Kling image-to-video uploads",
14174
14178
  envExampleComment: "ImgBB API Key (image hosting \u2014 used by Kling and Seedance for image-to-video uploads)",
14175
14179
  envExampleUrl: "https://api.imgbb.com/",
14176
14180
  // ImgBB issues 32-character lowercase hex keys. Soft-warn if a pasted
@@ -14179,9 +14183,7 @@ var init_api_keys = __esm({
14179
14183
  keyFormat: { prefix: /^[a-f0-9]{32}$/, example: "32-char hex" },
14180
14184
  // ImgBB has no provider class (envvar-only); doctor still shows what it
14181
14185
  // unlocks at the apiKey level.
14182
- commandsUnlocked: [
14183
- "generate video -p kling/seedance (image-to-video upload host)"
14184
- ]
14186
+ commandsUnlocked: ["generate video -p kling/seedance (image-to-video upload host)"]
14185
14187
  });
14186
14188
  defineProvider({
14187
14189
  id: "openrouter",
@@ -24179,7 +24181,7 @@ var init_FalProvider = __esm({
24179
24181
  return {
24180
24182
  id: "",
24181
24183
  status: "failed",
24182
- error: "fal.ai API key not configured. Set FAL_KEY in .env."
24184
+ error: "fal.ai API key not configured. Set FAL_API_KEY in .env."
24183
24185
  };
24184
24186
  }
24185
24187
  const variant = options?.model ?? DEFAULT_VARIANT;
@@ -24956,6 +24958,7 @@ __export(dist_exports, {
24956
24958
  getCommandKeyMap: () => getCommandKeyMap,
24957
24959
  getDisplayLabelForApiKey: () => getDisplayLabelForApiKey,
24958
24960
  getKeyFormat: () => getKeyFormat,
24961
+ getProviderEnvAliases: () => getProviderEnvAliases,
24959
24962
  getProviderEnvVars: () => getProviderEnvVars,
24960
24963
  getProvidersFor: () => getProvidersFor,
24961
24964
  getSetupProviders: () => getSetupProviders,
@@ -25403,21 +25406,58 @@ function createDefaultConfig() {
25403
25406
  aspectRatio: "16:9",
25404
25407
  exportQuality: "standard"
25405
25408
  },
25409
+ upload: {
25410
+ provider: "imgbb",
25411
+ ttlSeconds: 3600,
25412
+ s3: {
25413
+ prefix: "vibeframe/tmp"
25414
+ }
25415
+ },
25406
25416
  repl: {
25407
25417
  autoSave: true
25408
25418
  }
25409
25419
  };
25410
25420
  }
25411
- var PROVIDER_ENV_VARS;
25421
+ var PROVIDER_NAMES, PROVIDER_ENV_VARS, PROVIDER_ENV_ALIASES;
25412
25422
  var init_schema = __esm({
25413
25423
  "../cli/src/config/schema.ts"() {
25414
25424
  "use strict";
25415
25425
  init_dist();
25426
+ PROVIDER_NAMES = {
25427
+ claude: "Claude (Anthropic)",
25428
+ openai: "GPT-4 (OpenAI)",
25429
+ gemini: "Gemini (Google)",
25430
+ ollama: "Ollama (Local)",
25431
+ xai: "Grok (xAI)",
25432
+ openrouter: "OpenRouter"
25433
+ };
25416
25434
  PROVIDER_ENV_VARS = getProviderEnvVars();
25435
+ PROVIDER_ENV_ALIASES = getProviderEnvAliases();
25417
25436
  }
25418
25437
  });
25419
25438
 
25420
25439
  // ../cli/src/config/index.ts
25440
+ var config_exports = {};
25441
+ __export(config_exports, {
25442
+ CONFIG_DIR: () => CONFIG_DIR,
25443
+ CONFIG_PATH: () => CONFIG_PATH,
25444
+ PROVIDER_ENV_ALIASES: () => PROVIDER_ENV_ALIASES,
25445
+ PROVIDER_ENV_VARS: () => PROVIDER_ENV_VARS,
25446
+ PROVIDER_NAMES: () => PROVIDER_NAMES,
25447
+ USER_CONFIG_DIR: () => USER_CONFIG_DIR,
25448
+ USER_CONFIG_PATH: () => USER_CONFIG_PATH,
25449
+ createDefaultConfig: () => createDefaultConfig,
25450
+ getActiveScope: () => getActiveScope,
25451
+ getApiKeyFromConfig: () => getApiKeyFromConfig,
25452
+ getConfigDir: () => getConfigDir,
25453
+ getConfigPath: () => getConfigPath,
25454
+ getProjectConfigDir: () => getProjectConfigDir,
25455
+ getProjectConfigPath: () => getProjectConfigPath,
25456
+ isConfigured: () => isConfigured,
25457
+ loadConfig: () => loadConfig,
25458
+ saveConfig: () => saveConfig,
25459
+ updateProviderKey: () => updateProviderKey
25460
+ });
25421
25461
  import { resolve as resolve2 } from "node:path";
25422
25462
  import { homedir } from "node:os";
25423
25463
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, access as access2 } from "node:fs/promises";
@@ -25430,6 +25470,9 @@ function getProjectConfigPath(cwd = process.cwd()) {
25430
25470
  function getConfigPath(scope, cwd) {
25431
25471
  return scope === "project" ? getProjectConfigPath(cwd) : USER_CONFIG_PATH;
25432
25472
  }
25473
+ function getConfigDir(scope, cwd) {
25474
+ return scope === "project" ? getProjectConfigDir(cwd) : USER_CONFIG_DIR;
25475
+ }
25433
25476
  async function fileExists(path14) {
25434
25477
  try {
25435
25478
  await access2(path14);
@@ -25438,6 +25481,9 @@ async function fileExists(path14) {
25438
25481
  return false;
25439
25482
  }
25440
25483
  }
25484
+ async function getActiveScope(cwd) {
25485
+ return await fileExists(getProjectConfigPath(cwd)) ? "project" : "user";
25486
+ }
25441
25487
  function applyDefaults(parsed) {
25442
25488
  const defaults = createDefaultConfig();
25443
25489
  return {
@@ -25446,6 +25492,11 @@ function applyDefaults(parsed) {
25446
25492
  llm: { ...defaults.llm, ...parsed.llm },
25447
25493
  providers: { ...defaults.providers, ...parsed.providers },
25448
25494
  defaults: { ...defaults.defaults, ...parsed.defaults },
25495
+ upload: {
25496
+ ...defaults.upload,
25497
+ ...parsed.upload,
25498
+ s3: { ...defaults.upload.s3, ...parsed.upload?.s3 }
25499
+ },
25449
25500
  repl: { ...defaults.repl, ...parsed.repl }
25450
25501
  };
25451
25502
  }
@@ -25473,6 +25524,11 @@ async function loadConfig(options = {}) {
25473
25524
  llm: { ...user.llm, ...project2.llm },
25474
25525
  providers: { ...user.providers, ...project2.providers },
25475
25526
  defaults: { ...user.defaults, ...project2.defaults },
25527
+ upload: {
25528
+ ...user.upload,
25529
+ ...project2.upload,
25530
+ s3: { ...user.upload.s3, ...project2.upload.s3 }
25531
+ },
25476
25532
  repl: { ...user.repl, ...project2.repl }
25477
25533
  };
25478
25534
  }
@@ -25483,16 +25539,48 @@ async function loadConfig(options = {}) {
25483
25539
  if (project) return project;
25484
25540
  return readConfigFile(USER_CONFIG_PATH);
25485
25541
  }
25542
+ async function saveConfig(config4, options = {}) {
25543
+ const scope = options.scope ?? "user";
25544
+ const dir = getConfigDir(scope, options.cwd);
25545
+ const path14 = getConfigPath(scope, options.cwd);
25546
+ await mkdir2(dir, { recursive: true });
25547
+ const content = (0, import_yaml2.stringify)(config4, { indent: 2, lineWidth: 0 });
25548
+ await writeFile2(path14, content, "utf-8");
25549
+ }
25550
+ async function isConfigured() {
25551
+ const config4 = await loadConfig();
25552
+ if (!config4) return false;
25553
+ const provider = config4.llm.provider;
25554
+ const providerKey = provider === "gemini" ? "google" : provider === "claude" ? "anthropic" : provider;
25555
+ if (config4.providers[providerKey]) {
25556
+ return true;
25557
+ }
25558
+ const envVar = PROVIDER_ENV_VARS[providerKey];
25559
+ if (envVar && getEnvValue(envVar)) {
25560
+ return true;
25561
+ }
25562
+ return false;
25563
+ }
25486
25564
  async function getApiKeyFromConfig(providerKey) {
25487
25565
  const config4 = await loadConfig();
25488
25566
  if (config4?.providers[providerKey]) {
25489
25567
  return config4.providers[providerKey];
25490
25568
  }
25491
25569
  const envVar = PROVIDER_ENV_VARS[providerKey];
25492
- if (envVar) return process.env[envVar];
25570
+ if (envVar) return getEnvValue(envVar);
25493
25571
  return void 0;
25494
25572
  }
25495
- var import_yaml2, USER_CONFIG_DIR, USER_CONFIG_PATH;
25573
+ function getEnvValue(envVar) {
25574
+ return process.env[envVar] || PROVIDER_ENV_ALIASES[envVar]?.map((alias) => process.env[alias]).find(Boolean);
25575
+ }
25576
+ async function updateProviderKey(providerKey, apiKey, options = {}) {
25577
+ const scope = options.scope ?? await getActiveScope(options.cwd);
25578
+ let config4 = await loadConfig({ scope, cwd: options.cwd });
25579
+ if (!config4) config4 = createDefaultConfig();
25580
+ config4.providers[providerKey] = apiKey;
25581
+ await saveConfig(config4, { scope, cwd: options.cwd });
25582
+ }
25583
+ var import_yaml2, USER_CONFIG_DIR, USER_CONFIG_PATH, CONFIG_DIR, CONFIG_PATH;
25496
25584
  var init_config = __esm({
25497
25585
  "../cli/src/config/index.ts"() {
25498
25586
  "use strict";
@@ -25501,6 +25589,8 @@ var init_config = __esm({
25501
25589
  init_schema();
25502
25590
  USER_CONFIG_DIR = resolve2(homedir(), ".vibeframe");
25503
25591
  USER_CONFIG_PATH = resolve2(USER_CONFIG_DIR, "config.yaml");
25592
+ CONFIG_DIR = USER_CONFIG_DIR;
25593
+ CONFIG_PATH = USER_CONFIG_PATH;
25504
25594
  }
25505
25595
  });
25506
25596
 
@@ -25508,6 +25598,12 @@ var init_config = __esm({
25508
25598
  import { createInterface } from "node:readline";
25509
25599
  import { readFile as readFile3, writeFile as writeFile3, access as access3 } from "node:fs/promises";
25510
25600
  import { resolve as resolve3 } from "node:path";
25601
+ function providerKeyForEnvVar(envVar) {
25602
+ return PROVIDER_KEY_BY_ENV_VAR[envVar];
25603
+ }
25604
+ function getEnvValue2(envVar) {
25605
+ return process.env[envVar] || PROVIDER_ENV_ALIASES[envVar]?.map((alias) => process.env[alias]).find(Boolean);
25606
+ }
25511
25607
  function loadEnv() {
25512
25608
  (0, import_dotenv.config)({ path: resolve3(process.cwd(), ".env"), debug: false, quiet: true });
25513
25609
  const monorepoRoot = findMonorepoRoot();
@@ -25570,19 +25666,7 @@ async function getApiKey(envVar, providerName, optionValue) {
25570
25666
  if (optionValue) {
25571
25667
  return optionValue;
25572
25668
  }
25573
- const providerKeyMap = {
25574
- ANTHROPIC_API_KEY: "anthropic",
25575
- OPENAI_API_KEY: "openai",
25576
- GOOGLE_API_KEY: "google",
25577
- XAI_API_KEY: "xai",
25578
- ELEVENLABS_API_KEY: "elevenlabs",
25579
- RUNWAY_API_SECRET: "runway",
25580
- KLING_API_KEY: "kling",
25581
- OPENROUTER_API_KEY: "openrouter",
25582
- IMGBB_API_KEY: "imgbb",
25583
- REPLICATE_API_TOKEN: "replicate"
25584
- };
25585
- const providerKey = providerKeyMap[envVar];
25669
+ const providerKey = providerKeyForEnvVar(envVar);
25586
25670
  if (providerKey) {
25587
25671
  const configKey = await getApiKeyFromConfig(providerKey);
25588
25672
  if (configKey) {
@@ -25590,7 +25674,7 @@ async function getApiKey(envVar, providerName, optionValue) {
25590
25674
  }
25591
25675
  }
25592
25676
  loadEnv();
25593
- const envValue = process.env[envVar];
25677
+ const envValue = getEnvValue2(envVar);
25594
25678
  if (envValue) {
25595
25679
  return envValue;
25596
25680
  }
@@ -25599,7 +25683,9 @@ async function getApiKey(envVar, providerName, optionValue) {
25599
25683
  }
25600
25684
  console.log();
25601
25685
  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.`));
25686
+ console.log(
25687
+ source_default.dim(`Set ${envVar} in .env (current directory), run 'vibe setup', or enter below.`)
25688
+ );
25603
25689
  console.log();
25604
25690
  const apiKey = await prompt(source_default.cyan(`Enter ${providerName} API key: `), true);
25605
25691
  if (!apiKey || apiKey.trim() === "") {
@@ -25614,7 +25700,7 @@ async function getApiKey(envVar, providerName, optionValue) {
25614
25700
  }
25615
25701
  function hasApiKey(envVar) {
25616
25702
  loadEnv();
25617
- return !!process.env[envVar];
25703
+ return !!getEnvValue2(envVar);
25618
25704
  }
25619
25705
  async function requireApiKey(envVar, providerName, cliOverride) {
25620
25706
  const key2 = await getApiKey(envVar, providerName, cliOverride);
@@ -25643,22 +25729,39 @@ async function saveApiKeyToEnv(envVar, apiKey) {
25643
25729
  }
25644
25730
  await writeFile3(envPath, content, "utf-8");
25645
25731
  }
25646
- var import_dotenv, API_KEY_URLS, ApiKeyError;
25732
+ var import_dotenv, PROVIDER_KEY_BY_ENV_VAR, API_KEY_URLS, ApiKeyError;
25647
25733
  var init_api_key = __esm({
25648
25734
  "../cli/src/utils/api-key.ts"() {
25649
25735
  "use strict";
25650
25736
  import_dotenv = __toESM(require_main(), 1);
25651
25737
  init_source();
25652
25738
  init_config();
25739
+ PROVIDER_KEY_BY_ENV_VAR = {
25740
+ ANTHROPIC_API_KEY: "anthropic",
25741
+ OPENAI_API_KEY: "openai",
25742
+ GOOGLE_API_KEY: "google",
25743
+ XAI_API_KEY: "xai",
25744
+ ELEVENLABS_API_KEY: "elevenlabs",
25745
+ FAL_API_KEY: "fal",
25746
+ FAL_KEY: "fal",
25747
+ RUNWAY_API_SECRET: "runway",
25748
+ KLING_API_KEY: "kling",
25749
+ OPENROUTER_API_KEY: "openrouter",
25750
+ IMGBB_API_KEY: "imgbb",
25751
+ REPLICATE_API_TOKEN: "replicate"
25752
+ };
25653
25753
  API_KEY_URLS = {
25654
25754
  GOOGLE_API_KEY: "https://aistudio.google.com/apikey",
25655
25755
  OPENAI_API_KEY: "https://platform.openai.com/api-keys",
25656
25756
  ANTHROPIC_API_KEY: "https://console.anthropic.com/settings/keys",
25657
25757
  XAI_API_KEY: "https://console.x.ai",
25658
25758
  ELEVENLABS_API_KEY: "https://elevenlabs.io/app/settings/api-keys",
25759
+ FAL_API_KEY: "https://fal.ai/dashboard/keys",
25760
+ FAL_KEY: "https://fal.ai/dashboard/keys",
25659
25761
  RUNWAY_API_SECRET: "https://app.runwayml.com/settings/api-keys",
25660
25762
  KLING_API_KEY: "https://klingai.com/dev",
25661
- REPLICATE_API_TOKEN: "https://replicate.com/account/api-tokens"
25763
+ REPLICATE_API_TOKEN: "https://replicate.com/account/api-tokens",
25764
+ IMGBB_API_KEY: "https://api.imgbb.com/"
25662
25765
  };
25663
25766
  ApiKeyError = class extends Error {
25664
25767
  envVar;
@@ -77523,7 +77626,7 @@ var require_websocket = __commonJS({
77523
77626
  var http3 = __require("http");
77524
77627
  var net = __require("net");
77525
77628
  var tls = __require("tls");
77526
- var { randomBytes, createHash: createHash6 } = __require("crypto");
77629
+ var { randomBytes, createHash: createHash7 } = __require("crypto");
77527
77630
  var { Duplex, Readable: Readable3 } = __require("stream");
77528
77631
  var { URL: URL3 } = __require("url");
77529
77632
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -78183,7 +78286,7 @@ var require_websocket = __commonJS({
78183
78286
  abortHandshake(websocket, socket, "Invalid Upgrade header");
78184
78287
  return;
78185
78288
  }
78186
- const digest = createHash6("sha1").update(key2 + GUID).digest("base64");
78289
+ const digest = createHash7("sha1").update(key2 + GUID).digest("base64");
78187
78290
  if (res.headers["sec-websocket-accept"] !== digest) {
78188
78291
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
78189
78292
  return;
@@ -78550,7 +78653,7 @@ var require_websocket_server = __commonJS({
78550
78653
  var EventEmitter5 = __require("events");
78551
78654
  var http3 = __require("http");
78552
78655
  var { Duplex } = __require("stream");
78553
- var { createHash: createHash6 } = __require("crypto");
78656
+ var { createHash: createHash7 } = __require("crypto");
78554
78657
  var extension2 = require_extension();
78555
78658
  var PerMessageDeflate2 = require_permessage_deflate();
78556
78659
  var subprotocol2 = require_subprotocol();
@@ -78851,7 +78954,7 @@ var require_websocket_server = __commonJS({
78851
78954
  );
78852
78955
  }
78853
78956
  if (this._state > RUNNING) return abortHandshake(socket, 503);
78854
- const digest = createHash6("sha1").update(key2 + GUID).digest("base64");
78957
+ const digest = createHash7("sha1").update(key2 + GUID).digest("base64");
78855
78958
  const headers = [
78856
78959
  "HTTP/1.1 101 Switching Protocols",
78857
78960
  "Upgrade: websocket",
@@ -457697,29 +457800,61 @@ var init_video_extend = __esm({
457697
457800
  });
457698
457801
 
457699
457802
  // ../cli/src/utils/provider-resolver.ts
457803
+ function loadProviderDefaultsFromConfig(config4) {
457804
+ configDefaults = {};
457805
+ if (config4?.defaults.imageProvider) configDefaults.image = config4.defaults.imageProvider;
457806
+ if (config4?.defaults.videoProvider) configDefaults.video = config4.defaults.videoProvider;
457807
+ configProviderKeys = new Set(
457808
+ Object.entries(config4?.providers ?? {}).filter(([, value]) => Boolean(value)).map(([key2]) => key2)
457809
+ );
457810
+ }
457811
+ async function loadProviderDefaults() {
457812
+ try {
457813
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
457814
+ const config4 = await loadConfig2();
457815
+ loadProviderDefaultsFromConfig(config4);
457816
+ } catch {
457817
+ configDefaults = null;
457818
+ configProviderKeys = null;
457819
+ }
457820
+ }
457821
+ function providerKeyForEnvVar2(envVar) {
457822
+ const envVars = getProviderEnvVars();
457823
+ for (const [providerKey, candidateEnvVar] of Object.entries(envVars)) {
457824
+ if (candidateEnvVar === envVar) return providerKey;
457825
+ }
457826
+ return null;
457827
+ }
457828
+ function hasCandidateKey(candidate) {
457829
+ if (candidate.envVar === null) return true;
457830
+ if (hasApiKey(candidate.envVar)) return true;
457831
+ const providerKey = providerKeyForEnvVar2(candidate.envVar);
457832
+ return providerKey ? Boolean(configProviderKeys?.has(providerKey)) : false;
457833
+ }
457700
457834
  function resolveProvider(category) {
457701
457835
  const candidates = getProvidersFor(category);
457702
457836
  if (candidates.length === 0) return null;
457703
457837
  if (configDefaults?.[category]) {
457704
457838
  const preferred = candidates.find((c) => c.name === configDefaults[category]);
457705
- if (preferred && (preferred.envVar === null || hasApiKey(preferred.envVar))) {
457839
+ if (preferred && hasCandidateKey(preferred)) {
457706
457840
  return { name: preferred.name, label: preferred.label };
457707
457841
  }
457708
457842
  }
457709
457843
  for (const candidate of candidates) {
457710
- if (candidate.envVar === null || hasApiKey(candidate.envVar)) {
457844
+ if (hasCandidateKey(candidate)) {
457711
457845
  return { name: candidate.name, label: candidate.label };
457712
457846
  }
457713
457847
  }
457714
457848
  return null;
457715
457849
  }
457716
- var configDefaults;
457850
+ var configDefaults, configProviderKeys;
457717
457851
  var init_provider_resolver = __esm({
457718
457852
  "../cli/src/utils/provider-resolver.ts"() {
457719
457853
  "use strict";
457720
457854
  init_dist();
457721
457855
  init_api_key();
457722
457856
  configDefaults = null;
457857
+ configProviderKeys = null;
457723
457858
  }
457724
457859
  });
457725
457860
 
@@ -457756,13 +457891,26 @@ import { resolve as resolve47, dirname as dirname27 } from "node:path";
457756
457891
  import { fileURLToPath as fileURLToPath4 } from "node:url";
457757
457892
  import { writeFile as writeFile30, mkdir as mkdir20 } from "node:fs/promises";
457758
457893
  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", `
457894
+ 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(
457895
+ "-p, --provider <provider>",
457896
+ "Provider: openai (default when OPENAI_API_KEY set), gemini, grok, runway"
457897
+ ).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(
457898
+ "-r, --ratio <ratio>",
457899
+ "Aspect ratio (gemini: 1:1, 1:4, 1:8, 4:1, 8:1, 16:9, 9:16, 3:4, 4:3, etc.)",
457900
+ "1:1"
457901
+ ).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(
457902
+ "-m, --model <model>",
457903
+ "Model. Gemini: flash, 3.1-flash, latest, pro. OpenAI: 1.5 (default), 2 (gpt-image-2)"
457904
+ ).option("--dry-run", "Preview parameters without executing").addHelpText(
457905
+ "after",
457906
+ `
457760
457907
  Examples:
457761
457908
  $ vibe generate image "a sunset over the ocean" -o sunset.png
457762
457909
  $ vibe gen img "logo design" -o logo.png -p openai
457763
457910
  $ vibe gen img "landscape photo" -o wide.png -r 16:9
457764
457911
  $ vibe gen img "portrait" -o portrait.png -p gemini -m pro
457765
- $ vibe gen img "product shot" --dry-run --json`).action(async (prompt3, options) => {
457912
+ $ vibe gen img "product shot" --dry-run --json`
457913
+ ).action(async (prompt3, options) => {
457766
457914
  const startedAt = Date.now();
457767
457915
  try {
457768
457916
  if (!prompt3) {
@@ -457773,10 +457921,7 @@ Examples:
457773
457921
  }
457774
457922
  } else {
457775
457923
  exitWithError(
457776
- usageError(
457777
- "Prompt argument is required.",
457778
- "Usage: vibe generate image <prompt>"
457779
- )
457924
+ usageError("Prompt argument is required.", "Usage: vibe generate image <prompt>")
457780
457925
  );
457781
457926
  }
457782
457927
  }
@@ -457784,13 +457929,16 @@ Examples:
457784
457929
  if (options.output) {
457785
457930
  validateOutputPath(options.output);
457786
457931
  }
457932
+ await loadProviderDefaults();
457787
457933
  if (options.count !== void 0) {
457788
457934
  const n = parseInt(options.count, 10);
457789
457935
  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
- ));
457936
+ exitWithError(
457937
+ usageError(
457938
+ `Invalid --count: ${options.count}`,
457939
+ "Must be an integer between 1 and 10."
457940
+ )
457941
+ );
457794
457942
  }
457795
457943
  }
457796
457944
  const imageRegistry = getProvidersFor("image");
@@ -457855,11 +458003,9 @@ Examples:
457855
458003
  const apiKey = await requireApiKey(envKey, providerName, options.apiKey);
457856
458004
  const spinner2 = ora(`Generating image with ${providerName}...`).start();
457857
458005
  if (provider === "openai") {
457858
- const { result, modelLabel } = await executeOpenAIImageGenerate(
457859
- prompt3,
457860
- options,
457861
- { apiKey }
457862
- );
458006
+ const { result, modelLabel } = await executeOpenAIImageGenerate(prompt3, options, {
458007
+ apiKey
458008
+ });
457863
458009
  if (!result.success || !result.images) {
457864
458010
  spinner2.fail(result.error || "Image generation failed");
457865
458011
  exitWithError(apiError(result.error || "Image generation failed", true));
@@ -458081,9 +458227,7 @@ Examples:
458081
458227
  spinner2.fail(result.error || "Image generation failed");
458082
458228
  exitWithError(apiError(result.error || "Image generation failed", true));
458083
458229
  }
458084
- spinner2.succeed(
458085
- source_default.green(`Generated ${result.images.length} image(s) with xAI Grok`)
458086
- );
458230
+ spinner2.succeed(source_default.green(`Generated ${result.images.length} image(s) with xAI Grok`));
458087
458231
  if (isJsonMode()) {
458088
458232
  const outputPath = options.output ? resolve47(process.cwd(), options.output) : void 0;
458089
458233
  if (outputPath && result.images.length > 0) {
@@ -459316,606 +459460,206 @@ var init_video_utils = __esm({
459316
459460
  }
459317
459461
  });
459318
459462
 
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;
459463
+ // ../cli/src/utils/upload-host.ts
459464
+ import { createHash as createHash6, createHmac as createHmac2, randomUUID as randomUUID2 } from "node:crypto";
459465
+ import { extname as extname10 } from "node:path";
459466
+ function envNumber(name) {
459467
+ const value = process.env[name];
459468
+ if (!value) return void 0;
459469
+ const n = Number.parseInt(value, 10);
459470
+ return Number.isFinite(n) && n > 0 ? n : void 0;
459355
459471
  }
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;
459472
+ function safePrefix(prefix) {
459473
+ return (prefix ?? "vibeframe/tmp").replace(/^\/+|\/+$/g, "");
459395
459474
  }
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;
459475
+ function extensionFor(opts) {
459476
+ const fromName = opts?.filename ? extname10(opts.filename).replace(/^\./, "") : "";
459477
+ if (fromName) return fromName.toLowerCase();
459478
+ const mime = opts?.mimeType ?? "image/png";
459479
+ if (mime === "image/jpeg") return "jpg";
459480
+ if (mime === "image/webp") return "webp";
459481
+ if (mime === "image/gif") return "gif";
459482
+ return "png";
459428
459483
  }
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;
459484
+ function hmac(key2, value) {
459485
+ return createHmac2("sha256", key2).update(value).digest();
459463
459486
  }
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: []
459487
+ function sha256Hex(value) {
459488
+ return createHash6("sha256").update(value).digest("hex");
459489
+ }
459490
+ function awsEncode(value) {
459491
+ return encodeURIComponent(value).replace(
459492
+ /[!'()*]/g,
459493
+ (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
459494
+ );
459495
+ }
459496
+ function presignS3PutUrl(params) {
459497
+ const now = /* @__PURE__ */ new Date();
459498
+ const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, "");
459499
+ const dateStamp = amzDate.slice(0, 8);
459500
+ const host = `${params.bucket}.s3.${params.region}.amazonaws.com`;
459501
+ const credentialScope = `${dateStamp}/${params.region}/s3/aws4_request`;
459502
+ const canonicalUri = `/${params.key.split("/").map(awsEncode).join("/")}`;
459503
+ const query2 = {
459504
+ "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
459505
+ "X-Amz-Credential": `${params.accessKeyId}/${credentialScope}`,
459506
+ "X-Amz-Date": amzDate,
459507
+ "X-Amz-Expires": String(params.ttlSeconds),
459508
+ "X-Amz-SignedHeaders": "host"
459481
459509
  };
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
- }
459510
+ if (params.sessionToken) {
459511
+ query2["X-Amz-Security-Token"] = params.sessionToken;
459512
+ }
459513
+ const canonicalQuery = Object.entries(query2).sort(([a], [b]) => a.localeCompare(b)).map(([key2, value]) => `${awsEncode(key2)}=${awsEncode(value)}`).join("&");
459514
+ const canonicalRequest = [
459515
+ "PUT",
459516
+ canonicalUri,
459517
+ canonicalQuery,
459518
+ `host:${host}`,
459519
+ "",
459520
+ "host",
459521
+ "UNSIGNED-PAYLOAD"
459522
+ ].join("\n");
459523
+ const stringToSign = [
459524
+ "AWS4-HMAC-SHA256",
459525
+ amzDate,
459526
+ credentialScope,
459527
+ sha256Hex(canonicalRequest)
459528
+ ].join("\n");
459529
+ const dateKey = hmac(`AWS4${params.secretAccessKey}`, dateStamp);
459530
+ const regionKey = hmac(dateKey, params.region);
459531
+ const serviceKey = hmac(regionKey, "s3");
459532
+ const signingKey = hmac(serviceKey, "aws4_request");
459533
+ const signature = createHmac2("sha256", signingKey).update(stringToSign).digest("hex");
459534
+ return `https://${host}${canonicalUri}?${canonicalQuery}&X-Amz-Signature=${signature}`;
459535
+ }
459536
+ function publicS3Url(params) {
459537
+ if (params.publicBaseUrl) {
459538
+ return `${params.publicBaseUrl.replace(/\/+$/g, "")}/${params.key.split("/").map(awsEncode).join("/")}`;
459539
+ }
459540
+ return `https://${params.bucket}.s3.${params.region}.amazonaws.com/${params.key.split("/").map(awsEncode).join("/")}`;
459541
+ }
459542
+ async function resolveUploadSettings() {
459543
+ const config4 = await loadConfig();
459544
+ const provider = process.env.VIBE_UPLOAD_PROVIDER ?? config4?.upload.provider ?? "imgbb";
459545
+ return {
459546
+ provider,
459547
+ ttlSeconds: envNumber("VIBE_UPLOAD_TTL_SECONDS") ?? config4?.upload.ttlSeconds ?? 3600,
459548
+ s3: {
459549
+ bucket: process.env.VIBE_UPLOAD_S3_BUCKET ?? config4?.upload.s3?.bucket,
459550
+ region: process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? config4?.upload.s3?.region,
459551
+ prefix: process.env.VIBE_UPLOAD_S3_PREFIX ?? config4?.upload.s3?.prefix,
459552
+ publicBaseUrl: process.env.VIBE_UPLOAD_PUBLIC_BASE_URL ?? config4?.upload.s3?.publicBaseUrl
459537
459553
  }
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
- }
459554
+ };
459555
+ }
459556
+ async function resolveUploadHost() {
459557
+ const settings = await resolveUploadSettings();
459558
+ if (settings.provider === "s3") {
459559
+ const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
459560
+ const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
459561
+ const sessionToken = process.env.AWS_SESSION_TOKEN;
459562
+ const { bucket, region } = settings.s3;
459563
+ if (!accessKeyId || !secretAccessKey || !bucket || !region) {
459564
+ throw new Error(
459565
+ "S3 upload host requires AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, and VIBE_UPLOAD_S3_BUCKET."
459566
+ );
459544
459567
  }
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
459568
+ return {
459569
+ provider: "s3",
459570
+ async uploadImage(imageBuffer, opts) {
459571
+ const ext = extensionFor(opts);
459572
+ const prefix = safePrefix(settings.s3.prefix);
459573
+ const key2 = `${prefix}/${Date.now()}-${randomUUID2()}.${ext}`;
459574
+ const presignedUrl = presignS3PutUrl({
459575
+ accessKeyId,
459576
+ secretAccessKey,
459577
+ sessionToken,
459578
+ region,
459579
+ bucket,
459580
+ key: key2,
459581
+ ttlSeconds: settings.ttlSeconds
459559
459582
  });
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
- }
459583
+ const response = await fetch(presignedUrl, {
459584
+ method: "PUT",
459585
+ headers: {
459586
+ "content-type": opts?.mimeType ?? "application/octet-stream"
459587
+ },
459588
+ body: new Uint8Array(imageBuffer)
459589
+ });
459590
+ if (!response.ok) {
459591
+ throw new Error(`S3 upload failed (${response.status}): ${response.statusText}`);
459595
459592
  }
459596
- const dalleImageSizes = {
459597
- "16:9": "1536x1024",
459598
- "9:16": "1024x1536",
459599
- "1:1": "1024x1024"
459593
+ return {
459594
+ provider: "s3",
459595
+ url: publicS3Url({ region, bucket, key: key2, publicBaseUrl: settings.s3.publicBaseUrl }),
459596
+ expiresAt: new Date(Date.now() + settings.ttlSeconds * 1e3).toISOString()
459600
459597
  };
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
459598
  }
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);
459599
+ };
459600
+ }
459601
+ return {
459602
+ provider: "imgbb",
459603
+ async uploadImage(imageBuffer) {
459604
+ const imgbbKey = await getApiKeyFromConfig("imgbb") || process.env.IMGBB_API_KEY;
459605
+ if (!imgbbKey) {
459606
+ throw new Error("IMGBB_API_KEY required for image-to-video uploads.");
459868
459607
  }
459869
- }
459870
- if (storyboardMutated) {
459871
- let currentTime = 0;
459872
- for (const segment of segments) {
459873
- segment.startTime = currentTime;
459874
- currentTime += segment.duration;
459608
+ const result = await uploadToImgbb(imageBuffer, imgbbKey);
459609
+ if (!result.success || !result.url) {
459610
+ throw new Error(`ImgBB upload failed: ${result.error ?? "unknown error"}`);
459875
459611
  }
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");
459612
+ return { provider: "imgbb", url: result.url };
459878
459613
  }
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
- }
459614
+ };
459887
459615
  }
459888
- var import_yaml6;
459889
- var init_ai_script_pipeline = __esm({
459890
- "../cli/src/commands/ai-script-pipeline.ts"() {
459616
+ var init_upload_host = __esm({
459617
+ "../cli/src/utils/upload-host.ts"() {
459891
459618
  "use strict";
459892
- import_yaml6 = __toESM(require_dist(), 1);
459893
- init_dist();
459894
- init_api_key();
459895
459619
  init_config();
459896
- init_audio();
459897
- init_ai_helpers();
459898
459620
  init_video_utils();
459899
- init_video_providers();
459900
459621
  }
459901
459622
  });
459902
459623
 
459903
459624
  // ../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";
459625
+ import { resolve as resolve49 } from "node:path";
459626
+ import { readFile as readFile24, writeFile as writeFile32 } from "node:fs/promises";
459906
459627
  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(
459628
+ 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(
459629
+ "-p, --provider <provider>",
459630
+ "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."
459631
+ ).option(
459632
+ "-k, --api-key <key>",
459633
+ "API key (or set FAL_API_KEY / XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY / GOOGLE_API_KEY env)"
459634
+ ).option("-o, --output <path>", "Output file path (downloads video)").option("-i, --image <path>", "Reference image for image-to-video").option(
459908
459635
  "-d, --duration <sec>",
459909
459636
  "Duration in seconds. Seedance accepts 4-15; Kling accepts 5 or 10; Veo maps to 6 or 8.",
459910
459637
  "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", `
459638
+ ).option(
459639
+ "-r, --ratio <ratio>",
459640
+ "Aspect ratio: 16:9, 9:16, or 1:1 (auto-detected from image if omitted)"
459641
+ ).option("--seed <number>", "Random seed for reproducibility (Runway only)").option("--mode <mode>", "Generation mode: std or pro (Kling only)", "std").option(
459642
+ "--seedance-model <model>",
459643
+ "Seedance variant: quality or fast (fal.ai only)",
459644
+ "quality"
459645
+ ).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(
459646
+ "--ref-images <paths...>",
459647
+ "Reference images for character consistency (Veo 3.1 only, max 3)"
459648
+ ).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(
459649
+ "--runway-model <model>",
459650
+ "Runway model: gen4.5 (default, text+image-to-video), gen4_turbo (image-to-video only)",
459651
+ "gen4.5"
459652
+ ).option("--no-wait", "Start generation and return task ID without waiting").option("--dry-run", "Preview parameters without executing").addHelpText(
459653
+ "after",
459654
+ `
459912
459655
  Examples:
459913
- $ vibe generate video "dancing cat" -o cat.mp4 # Seedance when FAL_KEY is set
459656
+ $ vibe generate video "dancing cat" -o cat.mp4 # Seedance when FAL_API_KEY is set
459914
459657
  $ vibe gen vid "cinematic city timelapse" -o city.mp4 -p seedance # Seedance via fal.ai
459915
459658
  $ vibe gen vid "city timelapse" -o city.mp4 -p kling # Kling
459916
459659
  $ vibe gen vid "epic scene" -i frame.png -o out.mp4 -p runway # Image-to-video
459917
459660
  $ 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) => {
459661
+ $ vibe gen vid "sunset" -o sun.mp4 -d 10 --dry-run --json`
459662
+ ).action(async (prompt3, options) => {
459919
459663
  const startedAt = Date.now();
459920
459664
  try {
459921
459665
  if (!prompt3) {
@@ -459926,10 +459670,7 @@ Examples:
459926
459670
  }
459927
459671
  } else {
459928
459672
  exitWithError(
459929
- usageError(
459930
- "Prompt argument is required.",
459931
- "Usage: vibe generate video <prompt>"
459932
- )
459673
+ usageError("Prompt argument is required.", "Usage: vibe generate video <prompt>")
459933
459674
  );
459934
459675
  }
459935
459676
  }
@@ -459937,13 +459678,16 @@ Examples:
459937
459678
  if (options.output) {
459938
459679
  validateOutputPath(options.output);
459939
459680
  }
459681
+ await loadProviderDefaults();
459940
459682
  if (options.duration !== void 0) {
459941
459683
  const d = parseFloat(options.duration);
459942
459684
  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
- ));
459685
+ exitWithError(
459686
+ usageError(
459687
+ `Invalid --duration: ${options.duration}`,
459688
+ "Must be a positive number \u2264 60 seconds."
459689
+ )
459690
+ );
459947
459691
  }
459948
459692
  }
459949
459693
  const validProviders = ["runway", "kling", "veo", "grok", "seedance", "fal"];
@@ -459952,7 +459696,7 @@ Examples:
459952
459696
  veo: "GOOGLE_API_KEY",
459953
459697
  kling: "KLING_API_KEY",
459954
459698
  runway: "RUNWAY_API_SECRET",
459955
- seedance: "FAL_KEY"
459699
+ seedance: "FAL_API_KEY"
459956
459700
  };
459957
459701
  let provider;
459958
459702
  if (options.provider) {
@@ -459985,10 +459729,12 @@ Examples:
459985
459729
  provider = resolved?.name ?? "grok";
459986
459730
  }
459987
459731
  let referenceImage;
459732
+ let referenceImageBuffer;
459733
+ let referenceImageMimeType;
459988
459734
  let isImageToVideo = false;
459989
459735
  if (options.image) {
459990
- const imagePath = resolve50(process.cwd(), options.image);
459991
- const imageBuffer = await readFile25(imagePath);
459736
+ const imagePath = resolve49(process.cwd(), options.image);
459737
+ const imageBuffer = await readFile24(imagePath);
459992
459738
  const ext = options.image.toLowerCase().split(".").pop();
459993
459739
  const mimeTypes = {
459994
459740
  jpg: "image/jpeg",
@@ -459998,6 +459744,8 @@ Examples:
459998
459744
  webp: "image/webp"
459999
459745
  };
460000
459746
  const mimeType = mimeTypes[ext || "png"] || "image/png";
459747
+ referenceImageBuffer = imageBuffer;
459748
+ referenceImageMimeType = mimeType;
460001
459749
  referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
460002
459750
  isImageToVideo = true;
460003
459751
  if (!options.ratio) {
@@ -460051,7 +459799,7 @@ Examples:
460051
459799
  kling: "KLING_API_KEY",
460052
459800
  veo: "GOOGLE_API_KEY",
460053
459801
  grok: "XAI_API_KEY",
460054
- seedance: "FAL_KEY"
459802
+ seedance: "FAL_API_KEY"
460055
459803
  };
460056
459804
  const providerNameMap = {
460057
459805
  runway: "Runway",
@@ -460122,20 +459870,22 @@ Examples:
460122
459870
  }
460123
459871
  let klingImage = referenceImage;
460124
459872
  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;
459873
+ try {
459874
+ const uploadHost = await resolveUploadHost();
459875
+ spinner2.text = `Uploading image via ${uploadHost.provider} for Kling...`;
459876
+ const upload = await uploadHost.uploadImage(referenceImageBuffer, {
459877
+ filename: options.image,
459878
+ mimeType: referenceImageMimeType
459879
+ });
459880
+ klingImage = upload.url;
459881
+ } catch (err) {
459882
+ spinner2.fail("Image upload failed");
459883
+ const message = err instanceof Error ? err.message : String(err);
459884
+ if (message.includes("IMGBB_API_KEY")) {
459885
+ exitWithError(authError("IMGBB_API_KEY", "ImgBB"));
459886
+ }
459887
+ exitWithError(apiError(message, true));
459888
+ }
460139
459889
  spinner2.text = "Starting video generation...";
460140
459890
  }
460141
459891
  result = await kling.generateVideo(prompt3, {
@@ -460190,8 +459940,8 @@ Examples:
460190
459940
  const veoDuration = parseInt(options.duration) <= 6 ? 6 : 8;
460191
459941
  let lastFrame;
460192
459942
  if (options.lastFrame) {
460193
- const lastFramePath = resolve50(process.cwd(), options.lastFrame);
460194
- const lastFrameBuffer = await readFile25(lastFramePath);
459943
+ const lastFramePath = resolve49(process.cwd(), options.lastFrame);
459944
+ const lastFrameBuffer = await readFile24(lastFramePath);
460195
459945
  const ext = options.lastFrame.toLowerCase().split(".").pop();
460196
459946
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
460197
459947
  lastFrame = `data:${mimeType};base64,${lastFrameBuffer.toString("base64")}`;
@@ -460200,8 +459950,8 @@ Examples:
460200
459950
  if (options.refImages && options.refImages.length > 0) {
460201
459951
  refImages = [];
460202
459952
  for (const refPath of options.refImages.slice(0, 3)) {
460203
- const absRefPath = resolve50(process.cwd(), refPath);
460204
- const refBuffer = await readFile25(absRefPath);
459953
+ const absRefPath = resolve49(process.cwd(), refPath);
459954
+ const refBuffer = await readFile24(absRefPath);
460205
459955
  const ext = refPath.toLowerCase().split(".").pop();
460206
459956
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
460207
459957
  refImages.push({ base64: refBuffer.toString("base64"), mimeType });
@@ -460282,20 +460032,22 @@ Examples:
460282
460032
  await fal.initialize({ apiKey });
460283
460033
  let falImage = referenceImage;
460284
460034
  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));
460035
+ try {
460036
+ const uploadHost = await resolveUploadHost();
460037
+ spinner2.text = `Uploading image via ${uploadHost.provider} for Seedance...`;
460038
+ const upload = await uploadHost.uploadImage(referenceImageBuffer, {
460039
+ filename: options.image,
460040
+ mimeType: referenceImageMimeType
460041
+ });
460042
+ falImage = upload.url;
460043
+ } catch (err) {
460044
+ spinner2.fail("Image upload failed");
460045
+ const message = err instanceof Error ? err.message : String(err);
460046
+ if (message.includes("IMGBB_API_KEY")) {
460047
+ exitWithError(authError("IMGBB_API_KEY", "ImgBB"));
460048
+ }
460049
+ exitWithError(apiError(message, true));
460297
460050
  }
460298
- falImage = uploadResult.url;
460299
460051
  }
460300
460052
  spinner2.text = "Generating video with fal.ai Seedance 2.0 (this may take 1-3 minutes)...";
460301
460053
  const seedanceModel = String(options.seedanceModel ?? "quality").toLowerCase();
@@ -460319,8 +460071,8 @@ Examples:
460319
460071
  let outputPath;
460320
460072
  if (options.output && finalResult.videoUrl) {
460321
460073
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
460322
- outputPath = resolve50(process.cwd(), options.output);
460323
- await writeFile33(outputPath, buffer);
460074
+ outputPath = resolve49(process.cwd(), options.output);
460075
+ await writeFile32(outputPath, buffer);
460324
460076
  }
460325
460077
  outputSuccess({
460326
460078
  command: "generate video",
@@ -460347,14 +460099,12 @@ Examples:
460347
460099
  const downloadSpinner = ora("Downloading video...").start();
460348
460100
  try {
460349
460101
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
460350
- const outputPath = resolve50(process.cwd(), options.output);
460351
- await writeFile33(outputPath, buffer);
460102
+ const outputPath = resolve49(process.cwd(), options.output);
460103
+ await writeFile32(outputPath, buffer);
460352
460104
  downloadSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
460353
460105
  } catch (err) {
460354
460106
  downloadSpinner.fail(
460355
- source_default.red(
460356
- `Failed to download video: ${err instanceof Error ? err.message : err}`
460357
- )
460107
+ source_default.red(`Failed to download video: ${err instanceof Error ? err.message : err}`)
460358
460108
  );
460359
460109
  }
460360
460110
  }
@@ -460372,11 +460122,10 @@ var init_video = __esm({
460372
460122
  init_dist();
460373
460123
  init_api_key();
460374
460124
  init_tty();
460375
- init_config();
460376
460125
  init_output();
460377
460126
  init_validate();
460378
460127
  init_provider_resolver();
460379
- init_ai_script_pipeline();
460128
+ init_upload_host();
460380
460129
  init_ai_helpers();
460381
460130
  }
460382
460131
  });
@@ -460429,7 +460178,7 @@ var init_generate = __esm({
460429
460178
  Examples:
460430
460179
  $ vibe generate image "a sunset over the ocean" -o sunset.png
460431
460180
  $ vibe generate image "logo design" -o logo.png -p openai
460432
- $ vibe generate video "dancing cat" -o cat.mp4 # Seedance when FAL_KEY is set
460181
+ $ vibe generate video "dancing cat" -o cat.mp4 # Seedance when FAL_API_KEY is set
460433
460182
  $ vibe generate video "city timelapse" -o city.mp4 -p seedance # Seedance via fal.ai
460434
460183
  $ vibe generate video "city timelapse" -o city.mp4 -p kling # Kling
460435
460184
  $ vibe generate video "epic scene" -i frame.png -o out.mp4 -p runway # Image-to-video
@@ -460440,7 +460189,7 @@ Examples:
460440
460189
  API Keys (per provider):
460441
460190
  GOOGLE_API_KEY Image (default), Veo video
460442
460191
  OPENAI_API_KEY Image (-p openai)
460443
- FAL_KEY Seedance video (-p seedance, default video)
460192
+ FAL_API_KEY Seedance video (-p seedance, default video)
460444
460193
  XAI_API_KEY Grok image/video
460445
460194
  KLING_API_KEY Kling video (-p kling)
460446
460195
  RUNWAY_API_SECRET Runway video (-p runway)
@@ -460488,8 +460237,8 @@ __export(ai_video_exports, {
460488
460237
  executeVideoGenerate: () => executeVideoGenerate,
460489
460238
  executeVideoStatus: () => executeVideoStatus
460490
460239
  });
460491
- import { readFile as readFile26, writeFile as writeFile34 } from "node:fs/promises";
460492
- import { resolve as resolve51 } from "node:path";
460240
+ import { readFile as readFile25, writeFile as writeFile33 } from "node:fs/promises";
460241
+ import { resolve as resolve50 } from "node:path";
460493
460242
  async function executeVideoGenerate(options) {
460494
460243
  const {
460495
460244
  prompt: prompt3,
@@ -460513,18 +460262,29 @@ async function executeVideoGenerate(options) {
460513
460262
  runway: "RUNWAY_API_SECRET",
460514
460263
  kling: "KLING_API_KEY",
460515
460264
  veo: "GOOGLE_API_KEY",
460516
- seedance: "FAL_KEY",
460517
- fal: "FAL_KEY"
460265
+ seedance: "FAL_API_KEY",
460266
+ fal: "FAL_API_KEY"
460518
460267
  };
460519
- const key2 = apiKey || process.env[envKeyMap[provider] || ""];
460268
+ const envKey = envKeyMap[provider] || "";
460269
+ const key2 = apiKey || (hasApiKey(envKey) ? process.env[envKey] || (envKey === "FAL_API_KEY" ? process.env.FAL_KEY : void 0) : void 0);
460520
460270
  if (!key2) return { success: false, error: `${envKeyMap[provider]} required for ${provider}` };
460521
460271
  let referenceImage;
460272
+ let referenceImageBuffer;
460273
+ let referenceImageMimeType;
460522
460274
  if (image) {
460523
- const imagePath = resolve51(process.cwd(), image);
460524
- const imageBuffer = await readFile26(imagePath);
460275
+ const imagePath = resolve50(process.cwd(), image);
460276
+ const imageBuffer = await readFile25(imagePath);
460525
460277
  const ext = image.toLowerCase().split(".").pop();
460526
- const mimeTypes = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", webp: "image/webp" };
460278
+ const mimeTypes = {
460279
+ jpg: "image/jpeg",
460280
+ jpeg: "image/jpeg",
460281
+ png: "image/png",
460282
+ gif: "image/gif",
460283
+ webp: "image/webp"
460284
+ };
460527
460285
  const mimeType = mimeTypes[ext || "png"] || "image/png";
460286
+ referenceImageBuffer = imageBuffer;
460287
+ referenceImageMimeType = mimeType;
460528
460288
  referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
460529
460289
  }
460530
460290
  if (provider === "seedance" || provider === "fal") {
@@ -460532,12 +460292,12 @@ async function executeVideoGenerate(options) {
460532
460292
  await fal.initialize({ apiKey: key2 });
460533
460293
  let falImage = referenceImage;
460534
460294
  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;
460295
+ const uploadHost = await resolveUploadHost();
460296
+ const upload = await uploadHost.uploadImage(referenceImageBuffer, {
460297
+ filename: image,
460298
+ mimeType: referenceImageMimeType
460299
+ });
460300
+ falImage = upload.url;
460541
460301
  }
460542
460302
  const model = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
460543
460303
  const result = await fal.generateVideo(prompt3, {
@@ -460548,14 +460308,22 @@ async function executeVideoGenerate(options) {
460548
460308
  negativePrompt: negative,
460549
460309
  model
460550
460310
  });
460551
- if (result.status === "failed") return { success: false, error: result.error || "Seedance generation failed" };
460311
+ if (result.status === "failed")
460312
+ return { success: false, error: result.error || "Seedance generation failed" };
460552
460313
  let outputPath;
460553
460314
  if (output3 && result.videoUrl) {
460554
460315
  const buffer = await downloadVideo(result.videoUrl, key2);
460555
- outputPath = resolve51(process.cwd(), output3);
460556
- await writeFile34(outputPath, buffer);
460316
+ outputPath = resolve50(process.cwd(), output3);
460317
+ await writeFile33(outputPath, buffer);
460557
460318
  }
460558
- return { success: true, taskId: result.id, status: "completed", videoUrl: result.videoUrl, outputPath, provider: "seedance" };
460319
+ return {
460320
+ success: true,
460321
+ taskId: result.id,
460322
+ status: "completed",
460323
+ videoUrl: result.videoUrl,
460324
+ outputPath,
460325
+ provider: "seedance"
460326
+ };
460559
460327
  } else if (provider === "runway") {
460560
460328
  const runway = new RunwayProvider();
460561
460329
  await runway.initialize({ apiKey: key2 });
@@ -460566,30 +460334,41 @@ async function executeVideoGenerate(options) {
460566
460334
  aspectRatio: ratio,
460567
460335
  seed
460568
460336
  });
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" };
460337
+ if (result.status === "failed")
460338
+ return { success: false, error: result.error || "Runway generation failed" };
460339
+ if (!wait)
460340
+ return { success: true, taskId: result.id, status: "processing", provider: "runway" };
460571
460341
  const finalResult = await runway.waitForCompletion(result.id, () => {
460572
460342
  }, 3e5);
460573
- if (finalResult.status !== "completed") return { success: false, error: finalResult.error || "Runway generation failed" };
460343
+ if (finalResult.status !== "completed")
460344
+ return { success: false, error: finalResult.error || "Runway generation failed" };
460574
460345
  let outputPath;
460575
460346
  if (output3 && finalResult.videoUrl) {
460576
460347
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
460577
- outputPath = resolve51(process.cwd(), output3);
460578
- await writeFile34(outputPath, buffer);
460348
+ outputPath = resolve50(process.cwd(), output3);
460349
+ await writeFile33(outputPath, buffer);
460579
460350
  }
460580
- return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, duration: finalResult.duration, outputPath, provider: "runway" };
460351
+ return {
460352
+ success: true,
460353
+ taskId: result.id,
460354
+ status: "completed",
460355
+ videoUrl: finalResult.videoUrl,
460356
+ duration: finalResult.duration,
460357
+ outputPath,
460358
+ provider: "runway"
460359
+ };
460581
460360
  } else if (provider === "kling") {
460582
460361
  const kling = new KlingProvider();
460583
460362
  await kling.initialize({ apiKey: key2 });
460584
460363
  if (!kling.isConfigured()) return { success: false, error: "Invalid Kling API key format" };
460585
460364
  let klingImage = referenceImage;
460586
460365
  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;
460366
+ const uploadHost = await resolveUploadHost();
460367
+ const upload = await uploadHost.uploadImage(referenceImageBuffer, {
460368
+ filename: image,
460369
+ mimeType: referenceImageMimeType
460370
+ });
460371
+ klingImage = upload.url;
460593
460372
  }
460594
460373
  const result = await kling.generateVideo(prompt3, {
460595
460374
  prompt: prompt3,
@@ -460599,23 +460378,38 @@ async function executeVideoGenerate(options) {
460599
460378
  negativePrompt: negative,
460600
460379
  mode
460601
460380
  });
460602
- if (result.status === "failed") return { success: false, error: result.error || "Kling generation failed" };
460381
+ if (result.status === "failed")
460382
+ return { success: false, error: result.error || "Kling generation failed" };
460603
460383
  const taskType = referenceImage ? "image2video" : "text2video";
460604
- if (!wait) return { success: true, taskId: result.id, status: "processing", provider: "kling" };
460384
+ if (!wait)
460385
+ return { success: true, taskId: result.id, status: "processing", provider: "kling" };
460605
460386
  const finalResult = await kling.waitForCompletion(result.id, taskType, () => {
460606
460387
  }, 6e5);
460607
- if (finalResult.status !== "completed") return { success: false, error: finalResult.error || "Kling generation failed" };
460388
+ if (finalResult.status !== "completed")
460389
+ return { success: false, error: finalResult.error || "Kling generation failed" };
460608
460390
  let outputPath;
460609
460391
  if (output3 && finalResult.videoUrl) {
460610
460392
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
460611
- outputPath = resolve51(process.cwd(), output3);
460612
- await writeFile34(outputPath, buffer);
460393
+ outputPath = resolve50(process.cwd(), output3);
460394
+ await writeFile33(outputPath, buffer);
460613
460395
  }
460614
- return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, duration: finalResult.duration, outputPath, provider: "kling" };
460396
+ return {
460397
+ success: true,
460398
+ taskId: result.id,
460399
+ status: "completed",
460400
+ videoUrl: finalResult.videoUrl,
460401
+ duration: finalResult.duration,
460402
+ outputPath,
460403
+ provider: "kling"
460404
+ };
460615
460405
  } else if (provider === "veo") {
460616
460406
  const gemini = new GeminiProvider();
460617
460407
  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" };
460408
+ const veoModelMap = {
460409
+ "3.0": "veo-3.0-generate-preview",
460410
+ "3.1": "veo-3.1-generate-preview",
460411
+ "3.1-fast": "veo-3.1-fast-generate-preview"
460412
+ };
460619
460413
  const model = veoModelMap[veoModel] || "veo-3.1-fast-generate-preview";
460620
460414
  const veoDuration = duration <= 6 ? 6 : 8;
460621
460415
  const result = await gemini.generateVideo(prompt3, {
@@ -460627,18 +460421,27 @@ async function executeVideoGenerate(options) {
460627
460421
  negativePrompt: negative,
460628
460422
  resolution
460629
460423
  });
460630
- if (result.status === "failed") return { success: false, error: result.error || "Veo generation failed" };
460424
+ if (result.status === "failed")
460425
+ return { success: false, error: result.error || "Veo generation failed" };
460631
460426
  if (!wait) return { success: true, taskId: result.id, status: "processing", provider: "veo" };
460632
460427
  const finalResult = await gemini.waitForVideoCompletion(result.id, () => {
460633
460428
  }, 3e5);
460634
- if (finalResult.status !== "completed") return { success: false, error: finalResult.error || "Veo generation failed" };
460429
+ if (finalResult.status !== "completed")
460430
+ return { success: false, error: finalResult.error || "Veo generation failed" };
460635
460431
  let outputPath;
460636
460432
  if (output3 && finalResult.videoUrl) {
460637
460433
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
460638
- outputPath = resolve51(process.cwd(), output3);
460639
- await writeFile34(outputPath, buffer);
460434
+ outputPath = resolve50(process.cwd(), output3);
460435
+ await writeFile33(outputPath, buffer);
460640
460436
  }
460641
- return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, outputPath, provider: "veo" };
460437
+ return {
460438
+ success: true,
460439
+ taskId: result.id,
460440
+ status: "completed",
460441
+ videoUrl: finalResult.videoUrl,
460442
+ outputPath,
460443
+ provider: "veo"
460444
+ };
460642
460445
  } else if (provider === "grok") {
460643
460446
  const grok = new GrokProvider();
460644
460447
  await grok.initialize({ apiKey: key2 });
@@ -460648,28 +460451,52 @@ async function executeVideoGenerate(options) {
460648
460451
  duration,
460649
460452
  aspectRatio: ratio
460650
460453
  });
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" };
460454
+ if (result.status === "failed")
460455
+ return { success: false, error: result.error || "Grok generation failed" };
460456
+ if (!wait)
460457
+ return { success: true, taskId: result.id, status: "processing", provider: "grok" };
460653
460458
  const finalResult = await grok.waitForCompletion(result.id, () => {
460654
460459
  }, 3e5);
460655
- if (finalResult.status !== "completed") return { success: false, error: finalResult.error || "Grok generation failed" };
460460
+ if (finalResult.status !== "completed")
460461
+ return { success: false, error: finalResult.error || "Grok generation failed" };
460656
460462
  let outputPath;
460657
460463
  if (output3 && finalResult.videoUrl) {
460658
460464
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
460659
- outputPath = resolve51(process.cwd(), output3);
460660
- await writeFile34(outputPath, buffer);
460465
+ outputPath = resolve50(process.cwd(), output3);
460466
+ await writeFile33(outputPath, buffer);
460661
460467
  }
460662
- return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, duration: finalResult.duration, outputPath, provider: "grok" };
460468
+ return {
460469
+ success: true,
460470
+ taskId: result.id,
460471
+ status: "completed",
460472
+ videoUrl: finalResult.videoUrl,
460473
+ duration: finalResult.duration,
460474
+ outputPath,
460475
+ provider: "grok"
460476
+ };
460663
460477
  }
460664
460478
  return { success: false, error: `Unsupported provider: ${provider}` };
460665
460479
  } catch (error) {
460666
- return { success: false, error: `Video generation failed: ${error instanceof Error ? error.message : String(error)}` };
460480
+ return {
460481
+ success: false,
460482
+ error: `Video generation failed: ${error instanceof Error ? error.message : String(error)}`
460483
+ };
460667
460484
  }
460668
460485
  }
460669
460486
  async function executeVideoStatus(options) {
460670
- const { taskId, provider = "runway", taskType = "text2video", wait = false, output: output3, apiKey } = options;
460487
+ const {
460488
+ taskId,
460489
+ provider = "runway",
460490
+ taskType = "text2video",
460491
+ wait = false,
460492
+ output: output3,
460493
+ apiKey
460494
+ } = options;
460671
460495
  try {
460672
- const envKeyMap = { runway: "RUNWAY_API_SECRET", kling: "KLING_API_KEY" };
460496
+ const envKeyMap = {
460497
+ runway: "RUNWAY_API_SECRET",
460498
+ kling: "KLING_API_KEY"
460499
+ };
460673
460500
  const key2 = apiKey || process.env[envKeyMap[provider] || ""];
460674
460501
  if (!key2) return { success: false, error: `${envKeyMap[provider]} required` };
460675
460502
  if (provider === "runway") {
@@ -460683,10 +460510,17 @@ async function executeVideoStatus(options) {
460683
460510
  let outputPath;
460684
460511
  if (output3 && result.videoUrl) {
460685
460512
  const buffer = await downloadVideo(result.videoUrl, key2);
460686
- outputPath = resolve51(process.cwd(), output3);
460687
- await writeFile34(outputPath, buffer);
460513
+ outputPath = resolve50(process.cwd(), output3);
460514
+ await writeFile33(outputPath, buffer);
460688
460515
  }
460689
- return { success: true, taskId, status: result.status, progress: result.progress, videoUrl: result.videoUrl, outputPath };
460516
+ return {
460517
+ success: true,
460518
+ taskId,
460519
+ status: result.status,
460520
+ progress: result.progress,
460521
+ videoUrl: result.videoUrl,
460522
+ outputPath
460523
+ };
460690
460524
  } else if (provider === "kling") {
460691
460525
  const kling = new KlingProvider();
460692
460526
  await kling.initialize({ apiKey: key2 });
@@ -460698,14 +460532,24 @@ async function executeVideoStatus(options) {
460698
460532
  let outputPath;
460699
460533
  if (output3 && result.videoUrl) {
460700
460534
  const buffer = await downloadVideo(result.videoUrl, key2);
460701
- outputPath = resolve51(process.cwd(), output3);
460702
- await writeFile34(outputPath, buffer);
460535
+ outputPath = resolve50(process.cwd(), output3);
460536
+ await writeFile33(outputPath, buffer);
460703
460537
  }
460704
- return { success: true, taskId, status: result.status, videoUrl: result.videoUrl, duration: result.duration, outputPath };
460538
+ return {
460539
+ success: true,
460540
+ taskId,
460541
+ status: result.status,
460542
+ videoUrl: result.videoUrl,
460543
+ duration: result.duration,
460544
+ outputPath
460545
+ };
460705
460546
  }
460706
460547
  return { success: false, error: `Unsupported provider: ${provider}` };
460707
460548
  } catch (error) {
460708
- return { success: false, error: `Status check failed: ${error instanceof Error ? error.message : String(error)}` };
460549
+ return {
460550
+ success: false,
460551
+ error: `Status check failed: ${error instanceof Error ? error.message : String(error)}`
460552
+ };
460709
460553
  }
460710
460554
  }
460711
460555
  async function executeVideoCancel(options) {
@@ -460718,11 +460562,24 @@ async function executeVideoCancel(options) {
460718
460562
  const success = await runway.cancelGeneration(taskId);
460719
460563
  return { success };
460720
460564
  } catch (error) {
460721
- return { success: false, error: `Cancel failed: ${error instanceof Error ? error.message : String(error)}` };
460565
+ return {
460566
+ success: false,
460567
+ error: `Cancel failed: ${error instanceof Error ? error.message : String(error)}`
460568
+ };
460722
460569
  }
460723
460570
  }
460724
460571
  async function executeVideoExtend(options) {
460725
- const { videoId, provider = "kling", prompt: prompt3, duration = 5, negative, veoModel = "3.1", output: output3, wait = true, apiKey } = options;
460572
+ const {
460573
+ videoId,
460574
+ provider = "kling",
460575
+ prompt: prompt3,
460576
+ duration = 5,
460577
+ negative,
460578
+ veoModel = "3.1",
460579
+ output: output3,
460580
+ wait = true,
460581
+ apiKey
460582
+ } = options;
460726
460583
  try {
460727
460584
  if (provider === "kling") {
460728
460585
  const key2 = apiKey || process.env.KLING_API_KEY;
@@ -460735,53 +460592,78 @@ async function executeVideoExtend(options) {
460735
460592
  negativePrompt: negative,
460736
460593
  duration: String(duration)
460737
460594
  });
460738
- if (result.status === "failed") return { success: false, error: result.error || "Kling extension failed" };
460595
+ if (result.status === "failed")
460596
+ return { success: false, error: result.error || "Kling extension failed" };
460739
460597
  if (!wait) return { success: true, taskId: result.id, status: "processing" };
460740
460598
  const finalResult = await kling.waitForExtendCompletion(result.id, () => {
460741
460599
  }, 6e5);
460742
- if (finalResult.status !== "completed") return { success: false, error: finalResult.error || "Kling extension failed" };
460600
+ if (finalResult.status !== "completed")
460601
+ return { success: false, error: finalResult.error || "Kling extension failed" };
460743
460602
  let outputPath;
460744
460603
  if (output3 && finalResult.videoUrl) {
460745
460604
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
460746
- outputPath = resolve51(process.cwd(), output3);
460747
- await writeFile34(outputPath, buffer);
460605
+ outputPath = resolve50(process.cwd(), output3);
460606
+ await writeFile33(outputPath, buffer);
460748
460607
  }
460749
- return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, duration: finalResult.duration, outputPath };
460608
+ return {
460609
+ success: true,
460610
+ taskId: result.id,
460611
+ status: "completed",
460612
+ videoUrl: finalResult.videoUrl,
460613
+ duration: finalResult.duration,
460614
+ outputPath
460615
+ };
460750
460616
  } else if (provider === "veo") {
460751
460617
  const key2 = apiKey || process.env.GOOGLE_API_KEY;
460752
460618
  if (!key2) return { success: false, error: "GOOGLE_API_KEY required" };
460753
460619
  const gemini = new GeminiProvider();
460754
460620
  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" };
460621
+ const veoModelMap = {
460622
+ "3.0": "veo-3.0-generate-preview",
460623
+ "3.1": "veo-3.1-generate-preview",
460624
+ "3.1-fast": "veo-3.1-fast-generate-preview"
460625
+ };
460756
460626
  const model = veoModelMap[veoModel] || "veo-3.1-generate-preview";
460757
460627
  const result = await gemini.extendVideo(videoId, prompt3, {
460758
460628
  duration,
460759
460629
  model
460760
460630
  });
460761
- if (result.status === "failed") return { success: false, error: result.error || "Veo extension failed" };
460631
+ if (result.status === "failed")
460632
+ return { success: false, error: result.error || "Veo extension failed" };
460762
460633
  if (!wait) return { success: true, taskId: result.id, status: "processing" };
460763
460634
  const finalResult = await gemini.waitForVideoCompletion(result.id, () => {
460764
460635
  }, 3e5);
460765
- if (finalResult.status !== "completed") return { success: false, error: finalResult.error || "Veo extension failed" };
460636
+ if (finalResult.status !== "completed")
460637
+ return { success: false, error: finalResult.error || "Veo extension failed" };
460766
460638
  let outputPath;
460767
460639
  if (output3 && finalResult.videoUrl) {
460768
460640
  const buffer = await downloadVideo(finalResult.videoUrl, key2);
460769
- outputPath = resolve51(process.cwd(), output3);
460770
- await writeFile34(outputPath, buffer);
460641
+ outputPath = resolve50(process.cwd(), output3);
460642
+ await writeFile33(outputPath, buffer);
460771
460643
  }
460772
- return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, outputPath };
460644
+ return {
460645
+ success: true,
460646
+ taskId: result.id,
460647
+ status: "completed",
460648
+ videoUrl: finalResult.videoUrl,
460649
+ outputPath
460650
+ };
460773
460651
  }
460774
460652
  return { success: false, error: `Unsupported provider: ${provider}` };
460775
460653
  } catch (error) {
460776
- return { success: false, error: `Video extension failed: ${error instanceof Error ? error.message : String(error)}` };
460654
+ return {
460655
+ success: false,
460656
+ error: `Video extension failed: ${error instanceof Error ? error.message : String(error)}`
460657
+ };
460777
460658
  }
460778
460659
  }
460779
460660
  var init_ai_video = __esm({
460780
460661
  "../cli/src/commands/ai-video.ts"() {
460781
460662
  "use strict";
460782
460663
  init_dist();
460783
- init_ai_script_pipeline();
460664
+ init_upload_host();
460784
460665
  init_ai_helpers();
460666
+ init_api_key();
460785
460667
  }
460786
460668
  });
460787
460669
 
@@ -464360,11 +464242,17 @@ var generateMotionTool = defineTool({
464360
464242
  }),
464361
464243
  async execute(args) {
464362
464244
  const result = await executeMotion(args);
464363
- if (!result.success) return { success: false, error: result.error ?? "Motion generation failed" };
464245
+ if (!result.success)
464246
+ return { success: false, error: result.error ?? "Motion generation failed" };
464364
464247
  const out = result.compositedPath ?? result.renderedPath ?? result.codePath;
464365
464248
  return {
464366
464249
  success: true,
464367
- data: { codePath: result.codePath, renderedPath: result.renderedPath, compositedPath: result.compositedPath, componentName: result.componentName },
464250
+ data: {
464251
+ codePath: result.codePath,
464252
+ renderedPath: result.renderedPath,
464253
+ compositedPath: result.compositedPath,
464254
+ componentName: result.componentName
464255
+ },
464368
464256
  humanLines: [`\u2705 Motion generated \u2192 ${out}`]
464369
464257
  };
464370
464258
  }
@@ -464428,7 +464316,9 @@ var generateMusicTool = defineTool({
464428
464316
  return {
464429
464317
  success: true,
464430
464318
  data: { outputPath: result.outputPath, provider: result.provider, duration: result.duration },
464431
- humanLines: [`\u2705 Music${result.provider ? ` (${result.provider})` : ""} \u2192 ${result.outputPath ?? "(async)"}`]
464319
+ humanLines: [
464320
+ `\u2705 Music${result.provider ? ` (${result.provider})` : ""} \u2192 ${result.outputPath ?? "(async)"}`
464321
+ ]
464432
464322
  };
464433
464323
  }
464434
464324
  });
@@ -464445,7 +464335,12 @@ var generateMusicStatusTool = defineTool({
464445
464335
  if (!result.success) return { success: false, error: result.error ?? "Music status failed" };
464446
464336
  return {
464447
464337
  success: true,
464448
- data: { taskId: result.taskId, status: result.status, audioUrl: result.audioUrl, error: result.error },
464338
+ data: {
464339
+ taskId: result.taskId,
464340
+ status: result.status,
464341
+ audioUrl: result.audioUrl,
464342
+ error: result.error
464343
+ },
464449
464344
  humanLines: [`Music task ${result.taskId}: ${result.status}`]
464450
464345
  };
464451
464346
  }
@@ -464457,7 +464352,9 @@ var generateImageTool = defineTool({
464457
464352
  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
464353
  schema: z5.object({
464459
464354
  prompt: z5.string().describe("Image description prompt"),
464460
- provider: z5.enum(["gemini", "openai", "grok"]).optional().describe("Image provider (default: gemini)"),
464355
+ provider: z5.enum(["gemini", "openai", "grok"]).optional().describe(
464356
+ "Image provider (default: openai when OPENAI_API_KEY is configured, otherwise first configured provider)"
464357
+ ),
464461
464358
  output: z5.string().optional().describe("Output file path"),
464462
464359
  size: z5.string().optional().describe("Image size for OpenAI (1024x1024, 1536x1024, 1024x1536)"),
464463
464360
  ratio: z5.string().optional().describe("Aspect ratio for Gemini (1:1, 16:9, 9:16, 4:3, 3:4, etc.)"),
@@ -464467,10 +464364,16 @@ var generateImageTool = defineTool({
464467
464364
  }),
464468
464365
  async execute(args) {
464469
464366
  const result = await executeImageGenerate(args);
464470
- if (!result.success) return { success: false, error: result.error ?? "Image generation failed" };
464367
+ if (!result.success)
464368
+ return { success: false, error: result.error ?? "Image generation failed" };
464471
464369
  return {
464472
464370
  success: true,
464473
- data: { outputPath: result.outputPath, provider: result.provider, model: result.model, imageCount: result.images?.length },
464371
+ data: {
464372
+ outputPath: result.outputPath,
464373
+ provider: result.provider,
464374
+ model: result.model,
464375
+ imageCount: result.images?.length
464376
+ },
464474
464377
  humanLines: [`\u2705 Image (${result.provider}) \u2192 ${result.outputPath}`]
464475
464378
  };
464476
464379
  }
@@ -464492,7 +464395,9 @@ var generateStoryboardTool = defineTool({
464492
464395
  return {
464493
464396
  success: true,
464494
464397
  data: { segmentCount: result.segmentCount, outputPath: result.outputPath },
464495
- humanLines: [`\u2705 Storyboard: ${result.segmentCount} segments${result.outputPath ? ` \u2192 ${result.outputPath}` : ""}`]
464398
+ humanLines: [
464399
+ `\u2705 Storyboard: ${result.segmentCount} segments${result.outputPath ? ` \u2192 ${result.outputPath}` : ""}`
464400
+ ]
464496
464401
  };
464497
464402
  }
464498
464403
  });
@@ -464511,7 +464416,11 @@ var generateBackgroundTool = defineTool({
464511
464416
  if (!result.success) return { success: false, error: result.error ?? "Background failed" };
464512
464417
  return {
464513
464418
  success: true,
464514
- data: { imageUrl: result.imageUrl, outputPath: result.outputPath, revisedPrompt: result.revisedPrompt },
464419
+ data: {
464420
+ imageUrl: result.imageUrl,
464421
+ outputPath: result.outputPath,
464422
+ revisedPrompt: result.revisedPrompt
464423
+ },
464515
464424
  humanLines: [`\u2705 Background \u2192 ${result.outputPath ?? result.imageUrl}`]
464516
464425
  };
464517
464426
  }
@@ -464569,7 +464478,9 @@ var generateVideoTool = defineTool({
464569
464478
  outputPath: result.outputPath,
464570
464479
  provider: result.provider
464571
464480
  },
464572
- humanLines: [`\u2705 Video (${result.provider}, ${result.status})${result.outputPath ? ` \u2192 ${result.outputPath}` : ""}`]
464481
+ humanLines: [
464482
+ `\u2705 Video (${result.provider}, ${result.status})${result.outputPath ? ` \u2192 ${result.outputPath}` : ""}`
464483
+ ]
464573
464484
  };
464574
464485
  }
464575
464486
  });
@@ -464590,8 +464501,16 @@ var generateVideoStatusTool = defineTool({
464590
464501
  if (!result.success) return { success: false, error: result.error ?? "Status check failed" };
464591
464502
  return {
464592
464503
  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}%)` : ""}`]
464504
+ data: {
464505
+ taskId: result.taskId,
464506
+ status: result.status,
464507
+ progress: result.progress,
464508
+ videoUrl: result.videoUrl,
464509
+ outputPath: result.outputPath
464510
+ },
464511
+ humanLines: [
464512
+ `Task ${result.taskId}: ${result.status}${result.progress !== void 0 ? ` (${result.progress}%)` : ""}`
464513
+ ]
464595
464514
  };
464596
464515
  }
464597
464516
  });
@@ -464633,8 +464552,16 @@ var generateVideoExtendTool = defineTool({
464633
464552
  if (!result.success) return { success: false, error: result.error ?? "Extend failed" };
464634
464553
  return {
464635
464554
  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}` : ""}`]
464555
+ data: {
464556
+ taskId: result.taskId,
464557
+ status: result.status,
464558
+ videoUrl: result.videoUrl,
464559
+ duration: result.duration,
464560
+ outputPath: result.outputPath
464561
+ },
464562
+ humanLines: [
464563
+ `\u2705 Video extended (${result.status})${result.outputPath ? ` \u2192 ${result.outputPath}` : ""}`
464564
+ ]
464638
464565
  };
464639
464566
  }
464640
464567
  });
@@ -464659,11 +464586,584 @@ import { writeFile as writeFile38 } from "node:fs/promises";
464659
464586
  import { tmpdir as tmpdir5 } from "node:os";
464660
464587
  import { join as join30 } from "node:path";
464661
464588
  import { z as z6 } from "zod";
464662
- init_ai_script_pipeline();
464589
+
464590
+ // ../cli/src/commands/ai-script-pipeline.ts
464591
+ var import_yaml6 = __toESM(require_dist(), 1);
464592
+ init_dist();
464593
+ init_api_key();
464594
+ init_config();
464595
+ init_audio();
464596
+ init_ai_helpers();
464597
+ init_video_utils();
464598
+ import { readFile as readFile26, writeFile as writeFile34, unlink as unlink6, rename as rename6 } from "node:fs/promises";
464599
+ import { resolve as resolve51, extname as extname11 } from "node:path";
464600
+ import { existsSync as existsSync48 } from "node:fs";
464601
+
464602
+ // ../cli/src/commands/_shared/video-providers.ts
464603
+ init_source();
464604
+ init_video_utils();
464605
+ async function generateVideoWithRetryGrok(grok, segment, options, maxRetries, onProgress) {
464606
+ const prompt3 = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
464607
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
464608
+ try {
464609
+ const result = await grok.generateVideo(prompt3, {
464610
+ prompt: prompt3,
464611
+ duration: options.duration,
464612
+ aspectRatio: options.aspectRatio,
464613
+ referenceImage: options.referenceImage
464614
+ });
464615
+ if (result.status !== "failed" && result.id) {
464616
+ return { requestId: result.id };
464617
+ }
464618
+ const providerErr = result.error || "Grok returned failed status";
464619
+ if (attempt < maxRetries) {
464620
+ onProgress?.(
464621
+ `\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`
464622
+ );
464623
+ await sleep(RETRY_DELAY_MS);
464624
+ } else {
464625
+ console.error(source_default.dim(`
464626
+ [Grok error: ${providerErr}]`));
464627
+ }
464628
+ } catch (err) {
464629
+ const errMsg = err instanceof Error ? err.message : String(err);
464630
+ if (attempt < maxRetries) {
464631
+ onProgress?.(`\u26A0 Error: ${errMsg.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464632
+ await sleep(RETRY_DELAY_MS);
464633
+ } else {
464634
+ console.error(source_default.dim(`
464635
+ [Grok error: ${errMsg}]`));
464636
+ }
464637
+ }
464638
+ }
464639
+ return null;
464640
+ }
464641
+ async function generateVideoWithRetryKling(kling, segment, options, maxRetries, onProgress) {
464642
+ const prompt3 = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
464643
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
464644
+ try {
464645
+ const result = await kling.generateVideo(prompt3, {
464646
+ prompt: prompt3,
464647
+ // Pass reference image (base64 or URL) - KlingProvider handles v1.5 fallback for base64
464648
+ referenceImage: options.referenceImage,
464649
+ duration: options.duration,
464650
+ aspectRatio: options.aspectRatio,
464651
+ mode: "std"
464652
+ // std mode for faster generation
464653
+ });
464654
+ if (result.status !== "failed" && result.id) {
464655
+ return {
464656
+ taskId: result.id,
464657
+ type: options.referenceImage ? "image2video" : "text2video"
464658
+ };
464659
+ }
464660
+ const providerErr = result.error || "Kling returned failed status";
464661
+ if (attempt < maxRetries) {
464662
+ onProgress?.(`\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464663
+ await sleep(RETRY_DELAY_MS);
464664
+ } else {
464665
+ console.error(source_default.dim(`
464666
+ [Kling error: ${providerErr}]`));
464667
+ }
464668
+ } catch (err) {
464669
+ const errMsg = err instanceof Error ? err.message : String(err);
464670
+ if (attempt < maxRetries) {
464671
+ onProgress?.(`\u26A0 Error: ${errMsg.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464672
+ await sleep(RETRY_DELAY_MS);
464673
+ } else {
464674
+ console.error(source_default.dim(`
464675
+ [Kling error: ${errMsg}]`));
464676
+ }
464677
+ }
464678
+ }
464679
+ return null;
464680
+ }
464681
+ async function generateVideoWithRetryRunway(runway, segment, referenceImage, options, maxRetries, onProgress) {
464682
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
464683
+ try {
464684
+ const result = await runway.generateVideo(segment.visuals, {
464685
+ prompt: segment.visuals,
464686
+ referenceImage,
464687
+ duration: options.duration,
464688
+ aspectRatio: options.aspectRatio
464689
+ });
464690
+ if (result.status !== "failed" && result.id) {
464691
+ return { taskId: result.id };
464692
+ }
464693
+ const providerErr = result.error || "Runway returned failed status";
464694
+ if (attempt < maxRetries) {
464695
+ onProgress?.(`\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464696
+ await sleep(RETRY_DELAY_MS);
464697
+ } else {
464698
+ console.error(source_default.dim(`
464699
+ [Runway error: ${providerErr}]`));
464700
+ }
464701
+ } catch (err) {
464702
+ const errMsg = err instanceof Error ? err.message : String(err);
464703
+ if (attempt < maxRetries) {
464704
+ onProgress?.(`\u26A0 Error: ${errMsg.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464705
+ await sleep(RETRY_DELAY_MS);
464706
+ } else {
464707
+ console.error(source_default.dim(`
464708
+ [Runway error: ${errMsg}]`));
464709
+ }
464710
+ }
464711
+ }
464712
+ return null;
464713
+ }
464714
+ async function generateVideoWithRetryVeo(gemini, segment, options, maxRetries, onProgress) {
464715
+ const prompt3 = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
464716
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
464717
+ try {
464718
+ const result = await gemini.generateVideo(prompt3, {
464719
+ prompt: prompt3,
464720
+ referenceImage: options.referenceImage,
464721
+ duration: options.duration,
464722
+ aspectRatio: options.aspectRatio,
464723
+ model: "veo-3.1-fast-generate-preview"
464724
+ });
464725
+ if (result.status !== "failed" && result.id) {
464726
+ return { operationName: result.id };
464727
+ }
464728
+ const providerErr = result.error || "Veo returned failed status";
464729
+ if (attempt < maxRetries) {
464730
+ onProgress?.(`\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464731
+ await sleep(RETRY_DELAY_MS);
464732
+ } else {
464733
+ console.error(source_default.dim(`
464734
+ [Veo error: ${providerErr}]`));
464735
+ }
464736
+ } catch (err) {
464737
+ const errMsg = err instanceof Error ? err.message : String(err);
464738
+ if (attempt < maxRetries) {
464739
+ onProgress?.(`\u26A0 Error: ${errMsg.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
464740
+ await sleep(RETRY_DELAY_MS);
464741
+ } else {
464742
+ console.error(source_default.dim(`
464743
+ [Veo error: ${errMsg}]`));
464744
+ }
464745
+ }
464746
+ }
464747
+ return null;
464748
+ }
464749
+
464750
+ // ../cli/src/commands/ai-script-pipeline.ts
464751
+ async function executeRegenerateScene(options) {
464752
+ const result = {
464753
+ success: false,
464754
+ regeneratedScenes: [],
464755
+ failedScenes: []
464756
+ };
464757
+ try {
464758
+ const outputDir = resolve51(process.cwd(), options.projectDir);
464759
+ if (!existsSync48(outputDir)) {
464760
+ return { ...result, error: `Project directory not found: ${outputDir}` };
464761
+ }
464762
+ const yamlPath = resolve51(outputDir, "storyboard.yaml");
464763
+ const jsonPath = resolve51(outputDir, "storyboard.json");
464764
+ const storyboardPath = existsSync48(yamlPath) ? yamlPath : existsSync48(jsonPath) ? jsonPath : null;
464765
+ if (!storyboardPath) {
464766
+ return { ...result, error: `Storyboard not found in: ${outputDir} (expected storyboard.yaml or storyboard.json)` };
464767
+ }
464768
+ const storyboardContent = await readFile26(storyboardPath, "utf-8");
464769
+ const segments = storyboardPath.endsWith(".yaml") ? (0, import_yaml6.parse)(storyboardContent).scenes : JSON.parse(storyboardContent);
464770
+ for (const sceneNum of options.scenes) {
464771
+ if (sceneNum < 1 || sceneNum > segments.length) {
464772
+ return { ...result, error: `Scene ${sceneNum} does not exist. Storyboard has ${segments.length} scenes.` };
464773
+ }
464774
+ }
464775
+ const regenerateVideo = options.videoOnly || !options.narrationOnly && !options.imageOnly;
464776
+ const regenerateNarration = options.narrationOnly || !options.videoOnly && !options.imageOnly;
464777
+ const regenerateImage = options.imageOnly || !options.videoOnly && !options.narrationOnly;
464778
+ let videoApiKey;
464779
+ if (regenerateVideo) {
464780
+ const generatorKeyMap = {
464781
+ grok: { envVar: "XAI_API_KEY", name: "xAI (Grok)" },
464782
+ kling: { envVar: "KLING_API_KEY", name: "Kling" },
464783
+ runway: { envVar: "RUNWAY_API_SECRET", name: "Runway" },
464784
+ veo: { envVar: "GOOGLE_API_KEY", name: "Google (Veo)" }
464785
+ };
464786
+ const generator = options.generator || "grok";
464787
+ const genInfo = generatorKeyMap[generator];
464788
+ if (!genInfo) {
464789
+ return { ...result, error: `Invalid generator: ${generator}. Available: ${Object.keys(generatorKeyMap).join(", ")}` };
464790
+ }
464791
+ videoApiKey = await getApiKey(genInfo.envVar, genInfo.name) ?? void 0;
464792
+ if (!videoApiKey) {
464793
+ return { ...result, error: `${genInfo.name} API key required. Run 'vibe setup' or set ${genInfo.envVar} in .env` };
464794
+ }
464795
+ }
464796
+ let imageApiKey;
464797
+ if (regenerateImage) {
464798
+ const imageProvider = options.imageProvider || "openai";
464799
+ const imageKeyMap = {
464800
+ openai: { envVar: "OPENAI_API_KEY", name: "OpenAI" },
464801
+ gemini: { envVar: "GOOGLE_API_KEY", name: "Google" },
464802
+ grok: { envVar: "XAI_API_KEY", name: "xAI" }
464803
+ };
464804
+ const info = imageKeyMap[imageProvider];
464805
+ if (!info) {
464806
+ return { ...result, error: `Invalid imageProvider: ${imageProvider}` };
464807
+ }
464808
+ imageApiKey = await getApiKey(info.envVar, info.name) ?? void 0;
464809
+ if (!imageApiKey) {
464810
+ return { ...result, error: `${info.name} API key required. Run 'vibe setup' or set ${info.envVar} in .env` };
464811
+ }
464812
+ }
464813
+ let elevenlabsApiKey;
464814
+ if (regenerateNarration) {
464815
+ elevenlabsApiKey = await getApiKey("ELEVENLABS_API_KEY", "ElevenLabs") ?? void 0;
464816
+ if (!elevenlabsApiKey) {
464817
+ return { ...result, error: "ElevenLabs API key required. Run 'vibe setup' or set ELEVENLABS_API_KEY in .env" };
464818
+ }
464819
+ }
464820
+ let storyboardMutated = false;
464821
+ for (const sceneNum of options.scenes) {
464822
+ const segment = segments[sceneNum - 1];
464823
+ const narrationPath = resolve51(outputDir, `narration-${sceneNum}.mp3`);
464824
+ const imagePath = resolve51(outputDir, `scene-${sceneNum}.png`);
464825
+ const videoPath = resolve51(outputDir, `scene-${sceneNum}.mp4`);
464826
+ let sceneFailed = false;
464827
+ if (regenerateNarration && elevenlabsApiKey) {
464828
+ options.onProgress?.(`Scene ${sceneNum}: regenerating narration...`);
464829
+ const elevenlabs = new ElevenLabsProvider();
464830
+ await elevenlabs.initialize({ apiKey: elevenlabsApiKey });
464831
+ const narrationText = segment.narration || segment.description;
464832
+ const ttsResult = await elevenlabs.textToSpeech(narrationText, {
464833
+ voiceId: options.voice
464834
+ });
464835
+ if (ttsResult.success && ttsResult.audioBuffer) {
464836
+ await writeFile34(narrationPath, ttsResult.audioBuffer);
464837
+ segment.duration = await getAudioDuration(narrationPath);
464838
+ storyboardMutated = true;
464839
+ } else {
464840
+ sceneFailed = true;
464841
+ }
464842
+ }
464843
+ if (!sceneFailed && regenerateImage && imageApiKey) {
464844
+ options.onProgress?.(`Scene ${sceneNum}: regenerating image...`);
464845
+ const imageProvider = options.imageProvider || "openai";
464846
+ const characterDesc = segment.characterDescription || segments[0]?.characterDescription;
464847
+ let imagePrompt = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
464848
+ if (characterDesc) {
464849
+ imagePrompt = `${imagePrompt}
464850
+
464851
+ IMPORTANT - Character appearance must match exactly: ${characterDesc}`;
464852
+ }
464853
+ let referenceImageBuffer;
464854
+ const refSceneNum = options.referenceScene;
464855
+ if (refSceneNum && refSceneNum >= 1 && refSceneNum <= segments.length && refSceneNum !== sceneNum) {
464856
+ const refImagePath = resolve51(outputDir, `scene-${refSceneNum}.png`);
464857
+ if (existsSync48(refImagePath)) {
464858
+ referenceImageBuffer = await readFile26(refImagePath);
464859
+ }
464860
+ } else if (!refSceneNum) {
464861
+ for (let i = 1; i <= segments.length; i++) {
464862
+ if (i !== sceneNum) {
464863
+ const otherImagePath = resolve51(outputDir, `scene-${i}.png`);
464864
+ if (existsSync48(otherImagePath)) {
464865
+ referenceImageBuffer = await readFile26(otherImagePath);
464866
+ break;
464867
+ }
464868
+ }
464869
+ }
464870
+ }
464871
+ const dalleImageSizes = {
464872
+ "16:9": "1536x1024",
464873
+ "9:16": "1024x1536",
464874
+ "1:1": "1024x1024"
464875
+ };
464876
+ let imageBuffer;
464877
+ let imageUrl;
464878
+ if (imageProvider === "openai") {
464879
+ const openaiImage = new OpenAIImageProvider();
464880
+ await openaiImage.initialize({ apiKey: imageApiKey });
464881
+ const imageResult = await openaiImage.generateImage(imagePrompt, {
464882
+ size: dalleImageSizes[options.aspectRatio || "16:9"] || "1536x1024",
464883
+ quality: "standard"
464884
+ });
464885
+ if (imageResult.success && imageResult.images?.[0]) {
464886
+ const img = imageResult.images[0];
464887
+ if (img.base64) imageBuffer = Buffer.from(img.base64, "base64");
464888
+ else if (img.url) imageUrl = img.url;
464889
+ }
464890
+ } else if (imageProvider === "gemini") {
464891
+ const gemini = new GeminiProvider();
464892
+ await gemini.initialize({ apiKey: imageApiKey });
464893
+ if (referenceImageBuffer) {
464894
+ const simplifiedVisuals = segment.visuals.split(/[,.]/).find(
464895
+ (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")
464896
+ ) || segment.visuals.split(".")[0];
464897
+ const editPrompt = `Generate a new image showing the SAME SINGLE person from the reference image in a new scene.
464898
+
464899
+ REFERENCE: Look at the person in the reference image - their face, hair, build, and overall appearance.
464900
+
464901
+ NEW SCENE: ${simplifiedVisuals}
464902
+
464903
+ CRITICAL RULES:
464904
+ 1. Show ONLY ONE person - the exact same individual from the reference image
464905
+ 2. The person must have the IDENTICAL face, hair style, and body type
464906
+ 3. Do NOT show multiple people or duplicate the character
464907
+ 4. Create a single moment in time, one pose, one action
464908
+ 5. Match the art style and quality of the reference image
464909
+
464910
+ Generate the single-person scene image now.`;
464911
+ const imageResult = await gemini.editImage([referenceImageBuffer], editPrompt, {
464912
+ aspectRatio: options.aspectRatio || "16:9"
464913
+ });
464914
+ if (imageResult.success && imageResult.images?.[0]?.base64) {
464915
+ imageBuffer = Buffer.from(imageResult.images[0].base64, "base64");
464916
+ }
464917
+ } else {
464918
+ const imageResult = await gemini.generateImage(imagePrompt, {
464919
+ aspectRatio: options.aspectRatio || "16:9"
464920
+ });
464921
+ if (imageResult.success && imageResult.images?.[0]?.base64) {
464922
+ imageBuffer = Buffer.from(imageResult.images[0].base64, "base64");
464923
+ }
464924
+ }
464925
+ } else if (imageProvider === "grok") {
464926
+ const grok = new GrokProvider();
464927
+ await grok.initialize({ apiKey: imageApiKey });
464928
+ const imageResult = await grok.generateImage(imagePrompt, {
464929
+ aspectRatio: options.aspectRatio || "16:9"
464930
+ });
464931
+ if (imageResult.success && imageResult.images?.[0]) {
464932
+ const img = imageResult.images[0];
464933
+ if (img.base64) imageBuffer = Buffer.from(img.base64, "base64");
464934
+ else if (img.url) imageUrl = img.url;
464935
+ }
464936
+ }
464937
+ if (imageBuffer) {
464938
+ await writeFile34(imagePath, imageBuffer);
464939
+ } else if (imageUrl) {
464940
+ const response = await fetch(imageUrl);
464941
+ const buffer = Buffer.from(await response.arrayBuffer());
464942
+ await writeFile34(imagePath, buffer);
464943
+ } else {
464944
+ sceneFailed = true;
464945
+ }
464946
+ }
464947
+ if (sceneFailed) {
464948
+ result.failedScenes.push(sceneNum);
464949
+ continue;
464950
+ }
464951
+ if (regenerateVideo && videoApiKey) {
464952
+ options.onProgress?.(`Scene ${sceneNum}: regenerating video (${options.generator || "grok"})...`);
464953
+ if (!existsSync48(imagePath)) {
464954
+ result.failedScenes.push(sceneNum);
464955
+ continue;
464956
+ }
464957
+ const imageBuffer = await readFile26(imagePath);
464958
+ const videoDuration = segment.duration > 5 ? 10 : 5;
464959
+ const maxRetries = options.retries ?? DEFAULT_VIDEO_RETRIES;
464960
+ if (options.generator === "grok") {
464961
+ const grok = new GrokProvider();
464962
+ await grok.initialize({ apiKey: videoApiKey });
464963
+ const ext = extname11(imagePath).toLowerCase().slice(1);
464964
+ const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
464965
+ const referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
464966
+ const grokDuration = Math.min(15, Math.max(1, segment.duration));
464967
+ const taskResult = await generateVideoWithRetryGrok(
464968
+ grok,
464969
+ segment,
464970
+ {
464971
+ duration: grokDuration,
464972
+ aspectRatio: options.aspectRatio || "16:9",
464973
+ referenceImage
464974
+ },
464975
+ maxRetries
464976
+ );
464977
+ if (taskResult) {
464978
+ try {
464979
+ const waitResult = await grok.waitForCompletion(taskResult.requestId, void 0, 3e5);
464980
+ if (waitResult.status === "completed" && waitResult.videoUrl) {
464981
+ const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
464982
+ await writeFile34(videoPath, buffer);
464983
+ const targetDuration = segment.duration;
464984
+ const actualVideoDuration = await getVideoDuration(videoPath);
464985
+ if (actualVideoDuration < targetDuration - 0.1) {
464986
+ const extendedPath = resolve51(outputDir, `scene-${sceneNum}-extended.mp4`);
464987
+ await extendVideoNaturally(videoPath, targetDuration, extendedPath);
464988
+ await unlink6(videoPath);
464989
+ await rename6(extendedPath, videoPath);
464990
+ }
464991
+ result.regeneratedScenes.push(sceneNum);
464992
+ } else {
464993
+ logSceneFailure("Grok", `scene ${sceneNum}`, waitResult);
464994
+ result.failedScenes.push(sceneNum);
464995
+ }
464996
+ } catch (err) {
464997
+ logSceneFailure("Grok", `scene ${sceneNum}`, err);
464998
+ result.failedScenes.push(sceneNum);
464999
+ }
465000
+ } else {
465001
+ result.failedScenes.push(sceneNum);
465002
+ }
465003
+ } else if (options.generator === "veo") {
465004
+ const veo = new GeminiProvider();
465005
+ await veo.initialize({ apiKey: videoApiKey });
465006
+ const ext = extname11(imagePath).toLowerCase().slice(1);
465007
+ const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
465008
+ const referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
465009
+ const veoDuration = segment.duration > 6 ? 8 : segment.duration > 4 ? 6 : 4;
465010
+ const taskResult = await generateVideoWithRetryVeo(
465011
+ veo,
465012
+ segment,
465013
+ {
465014
+ duration: veoDuration,
465015
+ aspectRatio: options.aspectRatio || "16:9",
465016
+ referenceImage
465017
+ },
465018
+ maxRetries
465019
+ );
465020
+ if (taskResult) {
465021
+ try {
465022
+ const waitResult = await veo.waitForVideoCompletion(taskResult.operationName, void 0, 3e5);
465023
+ if (waitResult.status === "completed" && waitResult.videoUrl) {
465024
+ const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
465025
+ await writeFile34(videoPath, buffer);
465026
+ const targetDuration = segment.duration;
465027
+ const actualVideoDuration = await getVideoDuration(videoPath);
465028
+ if (actualVideoDuration < targetDuration - 0.1) {
465029
+ const extendedPath = resolve51(outputDir, `scene-${sceneNum}-extended.mp4`);
465030
+ await extendVideoNaturally(videoPath, targetDuration, extendedPath);
465031
+ await unlink6(videoPath);
465032
+ await rename6(extendedPath, videoPath);
465033
+ }
465034
+ result.regeneratedScenes.push(sceneNum);
465035
+ } else {
465036
+ logSceneFailure("Veo", `scene ${sceneNum}`, waitResult);
465037
+ result.failedScenes.push(sceneNum);
465038
+ }
465039
+ } catch (err) {
465040
+ logSceneFailure("Veo", `scene ${sceneNum}`, err);
465041
+ result.failedScenes.push(sceneNum);
465042
+ }
465043
+ } else {
465044
+ result.failedScenes.push(sceneNum);
465045
+ }
465046
+ } else if (options.generator === "kling" || !options.generator) {
465047
+ const kling = new KlingProvider();
465048
+ await kling.initialize({ apiKey: videoApiKey });
465049
+ if (!kling.isConfigured()) {
465050
+ result.failedScenes.push(sceneNum);
465051
+ continue;
465052
+ }
465053
+ const imgbbApiKey = await getApiKeyFromConfig("imgbb") || process.env.IMGBB_API_KEY;
465054
+ let imageUrl;
465055
+ if (imgbbApiKey) {
465056
+ const uploadResult = await uploadToImgbb(imageBuffer, imgbbApiKey);
465057
+ if (uploadResult.success && uploadResult.url) {
465058
+ imageUrl = uploadResult.url;
465059
+ }
465060
+ }
465061
+ const taskResult = await generateVideoWithRetryKling(
465062
+ kling,
465063
+ segment,
465064
+ {
465065
+ duration: videoDuration,
465066
+ aspectRatio: options.aspectRatio || "16:9",
465067
+ referenceImage: imageUrl
465068
+ },
465069
+ maxRetries
465070
+ );
465071
+ if (taskResult) {
465072
+ try {
465073
+ const waitResult = await kling.waitForCompletion(taskResult.taskId, taskResult.type, void 0, 6e5);
465074
+ if (waitResult.status === "completed" && waitResult.videoUrl) {
465075
+ const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
465076
+ await writeFile34(videoPath, buffer);
465077
+ await extendVideoToTarget(
465078
+ videoPath,
465079
+ segment.duration,
465080
+ outputDir,
465081
+ `Scene ${sceneNum}`,
465082
+ {
465083
+ kling,
465084
+ videoId: waitResult.videoId,
465085
+ onProgress: options.onProgress
465086
+ }
465087
+ );
465088
+ result.regeneratedScenes.push(sceneNum);
465089
+ } else {
465090
+ logSceneFailure("Kling", `scene ${sceneNum}`, waitResult);
465091
+ result.failedScenes.push(sceneNum);
465092
+ }
465093
+ } catch (err) {
465094
+ logSceneFailure("Kling", `scene ${sceneNum}`, err);
465095
+ result.failedScenes.push(sceneNum);
465096
+ }
465097
+ } else {
465098
+ result.failedScenes.push(sceneNum);
465099
+ }
465100
+ } else {
465101
+ const runway = new RunwayProvider();
465102
+ await runway.initialize({ apiKey: videoApiKey });
465103
+ const ext = extname11(imagePath).toLowerCase().slice(1);
465104
+ const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
465105
+ const referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
465106
+ const aspectRatio = options.aspectRatio === "1:1" ? "16:9" : options.aspectRatio || "16:9";
465107
+ const taskResult = await generateVideoWithRetryRunway(
465108
+ runway,
465109
+ segment,
465110
+ referenceImage,
465111
+ { duration: videoDuration, aspectRatio },
465112
+ maxRetries
465113
+ );
465114
+ if (taskResult) {
465115
+ try {
465116
+ const waitResult = await runway.waitForCompletion(taskResult.taskId, void 0, 3e5);
465117
+ if (waitResult.status === "completed" && waitResult.videoUrl) {
465118
+ const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
465119
+ await writeFile34(videoPath, buffer);
465120
+ const targetDuration = segment.duration;
465121
+ const actualVideoDuration = await getVideoDuration(videoPath);
465122
+ if (actualVideoDuration < targetDuration - 0.1) {
465123
+ const extendedPath = resolve51(outputDir, `scene-${sceneNum}-extended.mp4`);
465124
+ await extendVideoNaturally(videoPath, targetDuration, extendedPath);
465125
+ await unlink6(videoPath);
465126
+ await rename6(extendedPath, videoPath);
465127
+ }
465128
+ result.regeneratedScenes.push(sceneNum);
465129
+ } else {
465130
+ logSceneFailure("Runway", `scene ${sceneNum}`, waitResult);
465131
+ result.failedScenes.push(sceneNum);
465132
+ }
465133
+ } catch (err) {
465134
+ logSceneFailure("Runway", `scene ${sceneNum}`, err);
465135
+ result.failedScenes.push(sceneNum);
465136
+ }
465137
+ } else {
465138
+ result.failedScenes.push(sceneNum);
465139
+ }
465140
+ }
465141
+ } else if (!sceneFailed) {
465142
+ result.regeneratedScenes.push(sceneNum);
465143
+ }
465144
+ }
465145
+ if (storyboardMutated) {
465146
+ let currentTime = 0;
465147
+ for (const segment of segments) {
465148
+ segment.startTime = currentTime;
465149
+ currentTime += segment.duration;
465150
+ }
465151
+ const serialized = storyboardPath.endsWith(".yaml") ? (0, import_yaml6.stringify)({ scenes: segments }, { indent: 2 }) : JSON.stringify(segments, null, 2);
465152
+ await writeFile34(storyboardPath, serialized, "utf-8");
465153
+ }
465154
+ result.success = result.failedScenes.length === 0;
465155
+ return result;
465156
+ } catch (error) {
465157
+ return {
465158
+ ...result,
465159
+ error: error instanceof Error ? error.message : String(error)
465160
+ };
465161
+ }
465162
+ }
464663
465163
 
464664
465164
  // ../cli/src/commands/ai-highlights.ts
464665
465165
  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";
465166
+ import { resolve as resolve53, dirname as dirname28, basename as basename12, extname as extname12 } from "node:path";
464667
465167
  import { existsSync as existsSync49 } from "node:fs";
464668
465168
  init_dist();
464669
465169
  init_engine();
@@ -464698,7 +465198,7 @@ async function executeHighlights(options) {
464698
465198
  if (!existsSync49(absPath)) {
464699
465199
  return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: `File not found: ${absPath}` };
464700
465200
  }
464701
- const ext = extname11(absPath).toLowerCase();
465201
+ const ext = extname12(absPath).toLowerCase();
464702
465202
  const videoExtensions = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"];
464703
465203
  const isVideo = videoExtensions.includes(ext);
464704
465204
  const targetDuration = options.duration;
@@ -465035,7 +465535,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
465035
465535
  };
465036
465536
  for (let i = 0; i < selectedHighlights.length; i++) {
465037
465537
  const h = selectedHighlights[i];
465038
- const baseName = basename12(absPath, extname11(absPath));
465538
+ const baseName = basename12(absPath, extname12(absPath));
465039
465539
  const outputPath = resolve53(outputDir, `${baseName}-short-${i + 1}.mp4`);
465040
465540
  const { stdout: probeOut } = await execSafe("ffprobe", [
465041
465541
  "-v",
@@ -467149,7 +467649,7 @@ var exportTools = [exportVideoTool];
467149
467649
 
467150
467650
  // ../cli/src/tools/manifest/agent-only.ts
467151
467651
  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";
467652
+ import { resolve as resolve61, join as join33, basename as basename17, extname as extname13 } from "node:path";
467153
467653
  import { z as z11 } from "zod";
467154
467654
  init_engine();
467155
467655
  init_exec_safe();
@@ -467172,7 +467672,7 @@ function formatSize(bytes) {
467172
467672
  return `${size.toFixed(1)}${units2[unit]}`;
467173
467673
  }
467174
467674
  function detectMediaType(filePath) {
467175
- const ext = extname12(filePath).toLowerCase();
467675
+ const ext = extname13(filePath).toLowerCase();
467176
467676
  if ([".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"].includes(ext)) return "video";
467177
467677
  if ([".mp3", ".wav", ".aac", ".flac", ".ogg", ".m4a"].includes(ext)) return "audio";
467178
467678
  if ([".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"].includes(ext)) return "image";
@@ -467199,7 +467699,7 @@ function isMediaFile(filePath) {
467199
467699
  ".webp",
467200
467700
  ".bmp"
467201
467701
  ];
467202
- return mediaExts.includes(extname12(filePath).toLowerCase());
467702
+ return mediaExts.includes(extname13(filePath).toLowerCase());
467203
467703
  }
467204
467704
  async function getMediaDuration2(filePath) {
467205
467705
  try {
@@ -467335,7 +467835,7 @@ var batchImportTool = defineTool({
467335
467835
  if (entry.isDirectory() && recursive) {
467336
467836
  await scanDir(entryPath);
467337
467837
  } else if (entry.isFile()) {
467338
- const ext = extname12(entry.name).toLowerCase();
467838
+ const ext = extname13(entry.name).toLowerCase();
467339
467839
  const matchesFilter = !filterExts || filterExts.includes(ext);
467340
467840
  if (matchesFilter && isMediaFile(entryPath)) mediaFiles.push(entryPath);
467341
467841
  }