@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.
- package/README.md +1 -1
- package/dist/index.js +1272 -772
- 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: "
|
|
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
|
|
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:
|
|
14173
|
-
|
|
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
|
|
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
|
|
25570
|
+
if (envVar) return getEnvValue(envVar);
|
|
25493
25571
|
return void 0;
|
|
25494
25572
|
}
|
|
25495
|
-
|
|
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
|
|
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 =
|
|
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(
|
|
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 !!
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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
|
|
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 (
|
|
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(
|
|
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`
|
|
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(
|
|
457791
|
-
|
|
457792
|
-
|
|
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
|
-
|
|
457860
|
-
|
|
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/
|
|
459320
|
-
|
|
459321
|
-
|
|
459322
|
-
|
|
459323
|
-
|
|
459324
|
-
|
|
459325
|
-
|
|
459326
|
-
|
|
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
|
-
|
|
459357
|
-
|
|
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
|
-
|
|
459397
|
-
|
|
459398
|
-
|
|
459399
|
-
|
|
459400
|
-
|
|
459401
|
-
|
|
459402
|
-
|
|
459403
|
-
|
|
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
|
-
|
|
459430
|
-
|
|
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
|
-
|
|
459465
|
-
"
|
|
459466
|
-
|
|
459467
|
-
|
|
459468
|
-
|
|
459469
|
-
|
|
459470
|
-
}
|
|
459471
|
-
|
|
459472
|
-
|
|
459473
|
-
|
|
459474
|
-
|
|
459475
|
-
|
|
459476
|
-
|
|
459477
|
-
const
|
|
459478
|
-
|
|
459479
|
-
|
|
459480
|
-
|
|
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
|
-
|
|
459483
|
-
|
|
459484
|
-
|
|
459485
|
-
|
|
459486
|
-
|
|
459487
|
-
|
|
459488
|
-
|
|
459489
|
-
|
|
459490
|
-
|
|
459491
|
-
|
|
459492
|
-
|
|
459493
|
-
|
|
459494
|
-
|
|
459495
|
-
|
|
459496
|
-
|
|
459497
|
-
|
|
459498
|
-
|
|
459499
|
-
|
|
459500
|
-
|
|
459501
|
-
|
|
459502
|
-
|
|
459503
|
-
|
|
459504
|
-
|
|
459505
|
-
|
|
459506
|
-
|
|
459507
|
-
|
|
459508
|
-
|
|
459509
|
-
|
|
459510
|
-
|
|
459511
|
-
|
|
459512
|
-
|
|
459513
|
-
|
|
459514
|
-
|
|
459515
|
-
|
|
459516
|
-
|
|
459517
|
-
|
|
459518
|
-
|
|
459519
|
-
|
|
459520
|
-
|
|
459521
|
-
|
|
459522
|
-
|
|
459523
|
-
|
|
459524
|
-
|
|
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
|
-
|
|
459539
|
-
|
|
459540
|
-
|
|
459541
|
-
|
|
459542
|
-
|
|
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
|
-
|
|
459546
|
-
|
|
459547
|
-
|
|
459548
|
-
|
|
459549
|
-
|
|
459550
|
-
|
|
459551
|
-
|
|
459552
|
-
|
|
459553
|
-
|
|
459554
|
-
|
|
459555
|
-
|
|
459556
|
-
|
|
459557
|
-
|
|
459558
|
-
|
|
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
|
-
|
|
459561
|
-
|
|
459562
|
-
|
|
459563
|
-
|
|
459564
|
-
|
|
459565
|
-
|
|
459566
|
-
}
|
|
459567
|
-
|
|
459568
|
-
|
|
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
|
-
|
|
459597
|
-
|
|
459598
|
-
|
|
459599
|
-
|
|
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
|
-
|
|
459677
|
-
|
|
459678
|
-
|
|
459679
|
-
|
|
459680
|
-
|
|
459681
|
-
|
|
459682
|
-
|
|
459683
|
-
|
|
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
|
-
|
|
459871
|
-
|
|
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
|
-
|
|
459877
|
-
await writeFile32(storyboardPath, serialized, "utf-8");
|
|
459612
|
+
return { provider: "imgbb", url: result.url };
|
|
459878
459613
|
}
|
|
459879
|
-
|
|
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
|
|
459889
|
-
|
|
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
|
|
459905
|
-
import { readFile as
|
|
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(
|
|
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(
|
|
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
|
|
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`
|
|
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(
|
|
459944
|
-
|
|
459945
|
-
|
|
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: "
|
|
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 =
|
|
459991
|
-
const imageBuffer = await
|
|
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: "
|
|
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
|
-
|
|
460126
|
-
|
|
460127
|
-
|
|
460128
|
-
|
|
460129
|
-
|
|
460130
|
-
|
|
460131
|
-
|
|
460132
|
-
|
|
460133
|
-
|
|
460134
|
-
|
|
460135
|
-
|
|
460136
|
-
|
|
460137
|
-
|
|
460138
|
-
|
|
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 =
|
|
460194
|
-
const lastFrameBuffer = await
|
|
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 =
|
|
460204
|
-
const refBuffer = await
|
|
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
|
-
|
|
460286
|
-
|
|
460287
|
-
|
|
460288
|
-
|
|
460289
|
-
|
|
460290
|
-
|
|
460291
|
-
|
|
460292
|
-
|
|
460293
|
-
|
|
460294
|
-
|
|
460295
|
-
|
|
460296
|
-
|
|
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 =
|
|
460323
|
-
await
|
|
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 =
|
|
460351
|
-
await
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
460492
|
-
import { resolve as
|
|
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: "
|
|
460517
|
-
fal: "
|
|
460265
|
+
seedance: "FAL_API_KEY",
|
|
460266
|
+
fal: "FAL_API_KEY"
|
|
460518
460267
|
};
|
|
460519
|
-
const
|
|
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 =
|
|
460524
|
-
const imageBuffer = await
|
|
460275
|
+
const imagePath = resolve50(process.cwd(), image);
|
|
460276
|
+
const imageBuffer = await readFile25(imagePath);
|
|
460525
460277
|
const ext = image.toLowerCase().split(".").pop();
|
|
460526
|
-
const mimeTypes = {
|
|
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
|
|
460536
|
-
|
|
460537
|
-
|
|
460538
|
-
|
|
460539
|
-
|
|
460540
|
-
falImage =
|
|
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")
|
|
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 =
|
|
460556
|
-
await
|
|
460316
|
+
outputPath = resolve50(process.cwd(), output3);
|
|
460317
|
+
await writeFile33(outputPath, buffer);
|
|
460557
460318
|
}
|
|
460558
|
-
return {
|
|
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")
|
|
460570
|
-
|
|
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")
|
|
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 =
|
|
460578
|
-
await
|
|
460348
|
+
outputPath = resolve50(process.cwd(), output3);
|
|
460349
|
+
await writeFile33(outputPath, buffer);
|
|
460579
460350
|
}
|
|
460580
|
-
return {
|
|
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
|
|
460588
|
-
|
|
460589
|
-
|
|
460590
|
-
|
|
460591
|
-
|
|
460592
|
-
klingImage =
|
|
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")
|
|
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)
|
|
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")
|
|
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 =
|
|
460612
|
-
await
|
|
460393
|
+
outputPath = resolve50(process.cwd(), output3);
|
|
460394
|
+
await writeFile33(outputPath, buffer);
|
|
460613
460395
|
}
|
|
460614
|
-
return {
|
|
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 = {
|
|
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")
|
|
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")
|
|
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 =
|
|
460639
|
-
await
|
|
460434
|
+
outputPath = resolve50(process.cwd(), output3);
|
|
460435
|
+
await writeFile33(outputPath, buffer);
|
|
460640
460436
|
}
|
|
460641
|
-
return {
|
|
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")
|
|
460652
|
-
|
|
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")
|
|
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 =
|
|
460660
|
-
await
|
|
460465
|
+
outputPath = resolve50(process.cwd(), output3);
|
|
460466
|
+
await writeFile33(outputPath, buffer);
|
|
460661
460467
|
}
|
|
460662
|
-
return {
|
|
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 {
|
|
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 {
|
|
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 = {
|
|
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 =
|
|
460687
|
-
await
|
|
460513
|
+
outputPath = resolve50(process.cwd(), output3);
|
|
460514
|
+
await writeFile33(outputPath, buffer);
|
|
460688
460515
|
}
|
|
460689
|
-
return {
|
|
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 =
|
|
460702
|
-
await
|
|
460535
|
+
outputPath = resolve50(process.cwd(), output3);
|
|
460536
|
+
await writeFile33(outputPath, buffer);
|
|
460703
460537
|
}
|
|
460704
|
-
return {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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")
|
|
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")
|
|
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 =
|
|
460747
|
-
await
|
|
460605
|
+
outputPath = resolve50(process.cwd(), output3);
|
|
460606
|
+
await writeFile33(outputPath, buffer);
|
|
460748
460607
|
}
|
|
460749
|
-
return {
|
|
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 = {
|
|
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")
|
|
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")
|
|
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 =
|
|
460770
|
-
await
|
|
460641
|
+
outputPath = resolve50(process.cwd(), output3);
|
|
460642
|
+
await writeFile33(outputPath, buffer);
|
|
460771
460643
|
}
|
|
460772
|
-
return {
|
|
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 {
|
|
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
|
-
|
|
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)
|
|
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: {
|
|
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: [
|
|
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: {
|
|
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(
|
|
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)
|
|
464367
|
+
if (!result.success)
|
|
464368
|
+
return { success: false, error: result.error ?? "Image generation failed" };
|
|
464471
464369
|
return {
|
|
464472
464370
|
success: true,
|
|
464473
|
-
data: {
|
|
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: [
|
|
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: {
|
|
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: [
|
|
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: {
|
|
464594
|
-
|
|
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: {
|
|
464637
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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,
|
|
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
|
|
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 =
|
|
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(
|
|
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 =
|
|
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
|
}
|