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