@vibeframe/cli 0.31.0 → 0.33.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/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +8 -15
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/ai-analyze.d.ts.map +1 -1
- package/dist/commands/ai-analyze.js +9 -16
- package/dist/commands/ai-analyze.js.map +1 -1
- package/dist/commands/ai-audio.d.ts.map +1 -1
- package/dist/commands/ai-audio.js +129 -111
- package/dist/commands/ai-audio.js.map +1 -1
- package/dist/commands/ai-broll.d.ts.map +1 -1
- package/dist/commands/ai-broll.js +38 -23
- package/dist/commands/ai-broll.js.map +1 -1
- package/dist/commands/ai-edit-cli.d.ts.map +1 -1
- package/dist/commands/ai-edit-cli.js +53 -65
- package/dist/commands/ai-edit-cli.js.map +1 -1
- package/dist/commands/ai-fill-gaps.d.ts.map +1 -1
- package/dist/commands/ai-fill-gaps.js +13 -17
- package/dist/commands/ai-fill-gaps.js.map +1 -1
- package/dist/commands/ai-highlights.d.ts.map +1 -1
- package/dist/commands/ai-highlights.js +87 -60
- package/dist/commands/ai-highlights.js.map +1 -1
- package/dist/commands/ai-image.d.ts.map +1 -1
- package/dist/commands/ai-image.js +75 -60
- package/dist/commands/ai-image.js.map +1 -1
- package/dist/commands/ai-motion.d.ts.map +1 -1
- package/dist/commands/ai-motion.js +30 -5
- package/dist/commands/ai-motion.js.map +1 -1
- package/dist/commands/ai-narrate.d.ts.map +1 -1
- package/dist/commands/ai-narrate.js +19 -16
- package/dist/commands/ai-narrate.js.map +1 -1
- package/dist/commands/ai-review.d.ts.map +1 -1
- package/dist/commands/ai-review.js +24 -5
- package/dist/commands/ai-review.js.map +1 -1
- package/dist/commands/ai-script-pipeline-cli.d.ts.map +1 -1
- package/dist/commands/ai-script-pipeline-cli.js +114 -88
- package/dist/commands/ai-script-pipeline-cli.js.map +1 -1
- package/dist/commands/ai-script-pipeline.d.ts +12 -2
- package/dist/commands/ai-script-pipeline.d.ts.map +1 -1
- package/dist/commands/ai-script-pipeline.js +113 -27
- package/dist/commands/ai-script-pipeline.js.map +1 -1
- package/dist/commands/ai-suggest-edit.d.ts.map +1 -1
- package/dist/commands/ai-suggest-edit.js +16 -21
- package/dist/commands/ai-suggest-edit.js.map +1 -1
- package/dist/commands/ai-video-fx.d.ts.map +1 -1
- package/dist/commands/ai-video-fx.js +72 -71
- package/dist/commands/ai-video-fx.js.map +1 -1
- package/dist/commands/ai-video.d.ts.map +1 -1
- package/dist/commands/ai-video.js +99 -90
- package/dist/commands/ai-video.js.map +1 -1
- package/dist/commands/ai-viral.d.ts.map +1 -1
- package/dist/commands/ai-viral.js +12 -24
- package/dist/commands/ai-viral.js.map +1 -1
- package/dist/commands/ai-visual-fx.d.ts.map +1 -1
- package/dist/commands/ai-visual-fx.js +76 -60
- package/dist/commands/ai-visual-fx.js.map +1 -1
- package/dist/commands/analyze.js +4 -4
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/audio.js +44 -44
- package/dist/commands/audio.js.map +1 -1
- package/dist/commands/batch.d.ts.map +1 -1
- package/dist/commands/batch.js +92 -39
- package/dist/commands/batch.js.map +1 -1
- package/dist/commands/detect.d.ts.map +1 -1
- package/dist/commands/detect.js +62 -11
- package/dist/commands/detect.js.map +1 -1
- package/dist/commands/edit-cmd.js +60 -64
- package/dist/commands/edit-cmd.js.map +1 -1
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +137 -90
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/generate.js +125 -128
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/media.d.ts.map +1 -1
- package/dist/commands/media.js +7 -9
- package/dist/commands/media.js.map +1 -1
- package/dist/commands/output.js +2 -2
- package/dist/commands/output.js.map +1 -1
- package/dist/commands/pipeline.d.ts.map +1 -1
- package/dist/commands/pipeline.js +21 -27
- package/dist/commands/pipeline.js.map +1 -1
- package/dist/commands/project.d.ts.map +1 -1
- package/dist/commands/project.js +42 -9
- package/dist/commands/project.js.map +1 -1
- package/dist/commands/schema.d.ts.map +1 -1
- package/dist/commands/schema.js +10 -16
- package/dist/commands/schema.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +248 -234
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/timeline.d.ts.map +1 -1
- package/dist/commands/timeline.js +185 -59
- package/dist/commands/timeline.js.map +1 -1
- package/dist/commands/validate.d.ts +3 -1
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +9 -2
- package/dist/commands/validate.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -17
- package/dist/index.js.map +1 -1
- package/dist/utils/api-key.d.ts.map +1 -1
- package/dist/utils/api-key.js +4 -2
- package/dist/utils/api-key.js.map +1 -1
- package/dist/utils/first-run.d.ts.map +1 -1
- package/dist/utils/first-run.js +5 -6
- package/dist/utils/first-run.js.map +1 -1
- package/dist/utils/tty.d.ts +1 -1
- package/dist/utils/tty.d.ts.map +1 -1
- package/dist/utils/tty.js +62 -3
- package/dist/utils/tty.js.map +1 -1
- package/package.json +3 -3
|
@@ -18,6 +18,8 @@ import { applyTextOverlays } from "./ai-edit.js";
|
|
|
18
18
|
import { executeReview } from "./ai-review.js";
|
|
19
19
|
import { DEFAULT_VIDEO_RETRIES, RETRY_DELAY_MS, sleep, uploadToImgbb, extendVideoToTarget, generateVideoWithRetryKling, generateVideoWithRetryRunway, } from "./ai-script-pipeline.js";
|
|
20
20
|
import { downloadVideo } from "./ai-helpers.js";
|
|
21
|
+
import { exitWithError, outputResult, authError, notFoundError, usageError, apiError, generalError } from "./output.js";
|
|
22
|
+
import { validateOutputPath } from "./validate.js";
|
|
21
23
|
export function registerScriptPipelineCommands(aiCommand) {
|
|
22
24
|
// Script-to-Video command
|
|
23
25
|
aiCommand
|
|
@@ -29,7 +31,7 @@ export function registerScriptPipelineCommands(aiCommand) {
|
|
|
29
31
|
.option("-o, --output <path>", "Output project file path", "script-video.vibe.json")
|
|
30
32
|
.option("-d, --duration <seconds>", "Target total duration in seconds")
|
|
31
33
|
.option("-v, --voice <id>", "ElevenLabs voice ID for narration")
|
|
32
|
-
.option("-g, --generator <engine>", "Video generator: kling | runway | veo", "
|
|
34
|
+
.option("-g, --generator <engine>", "Video generator: grok | kling | runway | veo", "grok")
|
|
33
35
|
.option("-i, --image-provider <provider>", "Image provider: gemini | openai | grok", "gemini")
|
|
34
36
|
.option("-a, --aspect-ratio <ratio>", "Aspect ratio: 16:9 | 9:16 | 1:1", "16:9")
|
|
35
37
|
.option("--images-only", "Generate images only, skip video generation")
|
|
@@ -44,8 +46,36 @@ export function registerScriptPipelineCommands(aiCommand) {
|
|
|
44
46
|
.option("--text-style <style>", "Text overlay style: lower-third, center-bold, subtitle, minimal", "lower-third")
|
|
45
47
|
.option("--review", "Run AI review after assembly (requires GOOGLE_API_KEY)")
|
|
46
48
|
.option("--review-auto-apply", "Auto-apply fixable issues from AI review")
|
|
49
|
+
.option("--dry-run", "Preview parameters without executing")
|
|
47
50
|
.action(async (script, options) => {
|
|
48
51
|
try {
|
|
52
|
+
if (options.output) {
|
|
53
|
+
validateOutputPath(options.output);
|
|
54
|
+
}
|
|
55
|
+
if (options.dryRun) {
|
|
56
|
+
outputResult({
|
|
57
|
+
dryRun: true,
|
|
58
|
+
command: "pipeline script-to-video",
|
|
59
|
+
params: {
|
|
60
|
+
script: script.slice(0, 200),
|
|
61
|
+
file: options.file ?? false,
|
|
62
|
+
output: options.output,
|
|
63
|
+
duration: options.duration,
|
|
64
|
+
generator: options.generator,
|
|
65
|
+
imageProvider: options.imageProvider,
|
|
66
|
+
aspectRatio: options.aspectRatio,
|
|
67
|
+
imagesOnly: options.imagesOnly ?? false,
|
|
68
|
+
voiceover: options.voiceover,
|
|
69
|
+
outputDir: options.outputDir,
|
|
70
|
+
creativity: options.creativity,
|
|
71
|
+
storyboardProvider: options.storyboardProvider,
|
|
72
|
+
textOverlay: options.textOverlay,
|
|
73
|
+
textStyle: options.textStyle,
|
|
74
|
+
review: options.review ?? false,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
49
79
|
// Load environment variables from .env file
|
|
50
80
|
loadEnv();
|
|
51
81
|
// Get storyboard provider API key
|
|
@@ -54,27 +84,23 @@ export function registerScriptPipelineCommands(aiCommand) {
|
|
|
54
84
|
if (storyboardProvider === "openai") {
|
|
55
85
|
storyboardApiKey = (await getApiKey("OPENAI_API_KEY", "OpenAI")) ?? undefined;
|
|
56
86
|
if (!storyboardApiKey) {
|
|
57
|
-
|
|
58
|
-
process.exit(1);
|
|
87
|
+
exitWithError(authError("OPENAI_API_KEY", "OpenAI"));
|
|
59
88
|
}
|
|
60
89
|
}
|
|
61
90
|
else if (storyboardProvider === "gemini") {
|
|
62
91
|
storyboardApiKey = (await getApiKey("GOOGLE_API_KEY", "Google")) ?? undefined;
|
|
63
92
|
if (!storyboardApiKey) {
|
|
64
|
-
|
|
65
|
-
process.exit(1);
|
|
93
|
+
exitWithError(authError("GOOGLE_API_KEY", "Google"));
|
|
66
94
|
}
|
|
67
95
|
}
|
|
68
96
|
else if (storyboardProvider === "claude") {
|
|
69
97
|
storyboardApiKey = (await getApiKey("ANTHROPIC_API_KEY", "Anthropic")) ?? undefined;
|
|
70
98
|
if (!storyboardApiKey) {
|
|
71
|
-
|
|
72
|
-
process.exit(1);
|
|
99
|
+
exitWithError(authError("ANTHROPIC_API_KEY", "Anthropic"));
|
|
73
100
|
}
|
|
74
101
|
}
|
|
75
102
|
else {
|
|
76
|
-
|
|
77
|
-
process.exit(1);
|
|
103
|
+
exitWithError(usageError(`Unknown storyboard provider: ${storyboardProvider}`, "Use claude, openai, or gemini"));
|
|
78
104
|
}
|
|
79
105
|
// Get image provider API key
|
|
80
106
|
let imageApiKey;
|
|
@@ -82,55 +108,50 @@ export function registerScriptPipelineCommands(aiCommand) {
|
|
|
82
108
|
if (imageProvider === "openai" || imageProvider === "dalle") {
|
|
83
109
|
imageApiKey = (await getApiKey("OPENAI_API_KEY", "OpenAI")) ?? undefined;
|
|
84
110
|
if (!imageApiKey) {
|
|
85
|
-
|
|
86
|
-
process.exit(1);
|
|
111
|
+
exitWithError(authError("OPENAI_API_KEY", "OpenAI"));
|
|
87
112
|
}
|
|
88
113
|
}
|
|
89
114
|
else if (imageProvider === "gemini") {
|
|
90
115
|
imageApiKey = (await getApiKey("GOOGLE_API_KEY", "Google")) ?? undefined;
|
|
91
116
|
if (!imageApiKey) {
|
|
92
|
-
|
|
93
|
-
process.exit(1);
|
|
117
|
+
exitWithError(authError("GOOGLE_API_KEY", "Google"));
|
|
94
118
|
}
|
|
95
119
|
}
|
|
96
120
|
else if (imageProvider === "grok") {
|
|
97
121
|
imageApiKey = (await getApiKey("XAI_API_KEY", "xAI")) ?? undefined;
|
|
98
122
|
if (!imageApiKey) {
|
|
99
|
-
|
|
100
|
-
process.exit(1);
|
|
123
|
+
exitWithError(authError("XAI_API_KEY", "xAI"));
|
|
101
124
|
}
|
|
102
125
|
}
|
|
103
126
|
else {
|
|
104
|
-
|
|
105
|
-
process.exit(1);
|
|
127
|
+
exitWithError(usageError(`Unknown image provider: ${imageProvider}`, "Use openai, gemini, or grok"));
|
|
106
128
|
}
|
|
107
129
|
let elevenlabsApiKey;
|
|
108
130
|
if (options.voiceover !== false) {
|
|
109
131
|
const key = await getApiKey("ELEVENLABS_API_KEY", "ElevenLabs");
|
|
110
132
|
if (!key) {
|
|
111
|
-
|
|
112
|
-
process.exit(1);
|
|
133
|
+
exitWithError(authError("ELEVENLABS_API_KEY", "ElevenLabs"));
|
|
113
134
|
}
|
|
114
135
|
elevenlabsApiKey = key;
|
|
115
136
|
}
|
|
116
137
|
let videoApiKey;
|
|
117
138
|
if (!options.imagesOnly) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
139
|
+
const generatorKeyMap = {
|
|
140
|
+
grok: { envVar: "XAI_API_KEY", name: "xAI" },
|
|
141
|
+
kling: { envVar: "KLING_API_KEY", name: "Kling" },
|
|
142
|
+
runway: { envVar: "RUNWAY_API_SECRET", name: "Runway" },
|
|
143
|
+
veo: { envVar: "GOOGLE_API_KEY", name: "Google" },
|
|
144
|
+
};
|
|
145
|
+
const generator = options.generator || "grok";
|
|
146
|
+
const genInfo = generatorKeyMap[generator];
|
|
147
|
+
if (!genInfo) {
|
|
148
|
+
exitWithError(usageError(`Invalid generator: ${generator}`, `Available: ${Object.keys(generatorKeyMap).join(", ")}`));
|
|
125
149
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
console.error(chalk.red("Runway API key required (or use --images-only). Set RUNWAY_API_SECRET in .env or run: vibe setup"));
|
|
130
|
-
process.exit(1);
|
|
131
|
-
}
|
|
132
|
-
videoApiKey = key;
|
|
150
|
+
const key = await getApiKey(genInfo.envVar, genInfo.name);
|
|
151
|
+
if (!key) {
|
|
152
|
+
exitWithError(authError(genInfo.envVar, genInfo.name));
|
|
133
153
|
}
|
|
154
|
+
videoApiKey = key;
|
|
134
155
|
}
|
|
135
156
|
// Read script content
|
|
136
157
|
let scriptContent = script;
|
|
@@ -155,8 +176,7 @@ export function registerScriptPipelineCommands(aiCommand) {
|
|
|
155
176
|
// Validate creativity level
|
|
156
177
|
const creativity = options.creativity?.toLowerCase();
|
|
157
178
|
if (creativity && creativity !== "low" && creativity !== "high") {
|
|
158
|
-
|
|
159
|
-
process.exit(1);
|
|
179
|
+
exitWithError(usageError("Invalid creativity level.", "Use 'low' or 'high'."));
|
|
160
180
|
}
|
|
161
181
|
console.log();
|
|
162
182
|
console.log(chalk.bold.cyan("🎬 Script-to-Video Pipeline"));
|
|
@@ -190,8 +210,8 @@ export function registerScriptPipelineCommands(aiCommand) {
|
|
|
190
210
|
segments = await claude.analyzeContent(scriptContent, durationOpt, creativityOpts);
|
|
191
211
|
}
|
|
192
212
|
if (segments.length === 0) {
|
|
193
|
-
storyboardSpinner.fail(
|
|
194
|
-
|
|
213
|
+
storyboardSpinner.fail("Failed to generate storyboard");
|
|
214
|
+
exitWithError(apiError("Failed to generate storyboard (check API key and error above)", true));
|
|
195
215
|
}
|
|
196
216
|
let totalDuration = segments.reduce((sum, seg) => sum + seg.duration, 0);
|
|
197
217
|
storyboardSpinner.succeed(chalk.green(`Generated ${segments.length} scenes (total: ${totalDuration}s)`));
|
|
@@ -447,8 +467,8 @@ export function registerScriptPipelineCommands(aiCommand) {
|
|
|
447
467
|
const kling = new KlingProvider();
|
|
448
468
|
await kling.initialize({ apiKey: videoApiKey });
|
|
449
469
|
if (!kling.isConfigured()) {
|
|
450
|
-
videoSpinner.fail(
|
|
451
|
-
|
|
470
|
+
videoSpinner.fail("Invalid Kling API key format");
|
|
471
|
+
exitWithError(authError("KLING_API_KEY", "Kling"));
|
|
452
472
|
}
|
|
453
473
|
// Check for ImgBB API key for image-to-video support (from config or env)
|
|
454
474
|
const imgbbApiKey = await getApiKeyFromConfig("imgbb") || process.env.IMGBB_API_KEY;
|
|
@@ -1000,9 +1020,7 @@ export function registerScriptPipelineCommands(aiCommand) {
|
|
|
1000
1020
|
console.log();
|
|
1001
1021
|
}
|
|
1002
1022
|
catch (error) {
|
|
1003
|
-
|
|
1004
|
-
console.error(error);
|
|
1005
|
-
process.exit(1);
|
|
1023
|
+
exitWithError(generalError(error instanceof Error ? error.message : "Script-to-Video failed"));
|
|
1006
1024
|
}
|
|
1007
1025
|
});
|
|
1008
1026
|
// Regenerate Scene command
|
|
@@ -1014,12 +1032,13 @@ export function registerScriptPipelineCommands(aiCommand) {
|
|
|
1014
1032
|
.option("--video-only", "Only regenerate video")
|
|
1015
1033
|
.option("--narration-only", "Only regenerate narration")
|
|
1016
1034
|
.option("--image-only", "Only regenerate image")
|
|
1017
|
-
.option("-g, --generator <engine>", "Video generator: kling | runway | veo", "
|
|
1035
|
+
.option("-g, --generator <engine>", "Video generator: grok | kling | runway | veo", "grok")
|
|
1018
1036
|
.option("-i, --image-provider <provider>", "Image provider: gemini | openai | grok", "gemini")
|
|
1019
1037
|
.option("-v, --voice <id>", "ElevenLabs voice ID for narration")
|
|
1020
1038
|
.option("-a, --aspect-ratio <ratio>", "Aspect ratio: 16:9 | 9:16 | 1:1", "16:9")
|
|
1021
1039
|
.option("--retries <count>", "Number of retries for video generation failures", String(DEFAULT_VIDEO_RETRIES))
|
|
1022
1040
|
.option("--reference-scene <num>", "Use another scene's image as reference for character consistency")
|
|
1041
|
+
.option("--dry-run", "Preview parameters without executing")
|
|
1023
1042
|
.action(async (projectDir, options) => {
|
|
1024
1043
|
try {
|
|
1025
1044
|
const outputDir = resolve(process.cwd(), projectDir);
|
|
@@ -1027,19 +1046,34 @@ export function registerScriptPipelineCommands(aiCommand) {
|
|
|
1027
1046
|
const projectPath = resolve(outputDir, "project.vibe.json");
|
|
1028
1047
|
// Validate project directory
|
|
1029
1048
|
if (!existsSync(outputDir)) {
|
|
1030
|
-
|
|
1031
|
-
process.exit(1);
|
|
1049
|
+
exitWithError(notFoundError(outputDir));
|
|
1032
1050
|
}
|
|
1033
1051
|
if (!existsSync(storyboardPath)) {
|
|
1034
|
-
|
|
1035
|
-
console.error(chalk.dim("This command requires a storyboard.json file from script-to-video output"));
|
|
1036
|
-
process.exit(1);
|
|
1052
|
+
exitWithError(notFoundError(storyboardPath));
|
|
1037
1053
|
}
|
|
1038
1054
|
// Parse scene number(s) - supports "3" or "3,4,5"
|
|
1039
1055
|
const sceneNums = options.scene.split(",").map((s) => parseInt(s.trim())).filter((n) => !isNaN(n) && n >= 1);
|
|
1040
1056
|
if (sceneNums.length === 0) {
|
|
1041
|
-
|
|
1042
|
-
|
|
1057
|
+
exitWithError(usageError("Scene number must be a positive integer (1-based)", "e.g., --scene 3 or --scene 3,4,5"));
|
|
1058
|
+
}
|
|
1059
|
+
if (options.dryRun) {
|
|
1060
|
+
outputResult({
|
|
1061
|
+
dryRun: true,
|
|
1062
|
+
command: "pipeline regenerate-scene",
|
|
1063
|
+
params: {
|
|
1064
|
+
projectDir,
|
|
1065
|
+
scene: sceneNums,
|
|
1066
|
+
videoOnly: options.videoOnly ?? false,
|
|
1067
|
+
narrationOnly: options.narrationOnly ?? false,
|
|
1068
|
+
imageOnly: options.imageOnly ?? false,
|
|
1069
|
+
generator: options.generator,
|
|
1070
|
+
imageProvider: options.imageProvider,
|
|
1071
|
+
aspectRatio: options.aspectRatio,
|
|
1072
|
+
retries: options.retries,
|
|
1073
|
+
referenceScene: options.referenceScene,
|
|
1074
|
+
},
|
|
1075
|
+
});
|
|
1076
|
+
return;
|
|
1043
1077
|
}
|
|
1044
1078
|
// Load storyboard
|
|
1045
1079
|
const storyboardContent = await readFile(storyboardPath, "utf-8");
|
|
@@ -1047,8 +1081,7 @@ export function registerScriptPipelineCommands(aiCommand) {
|
|
|
1047
1081
|
// Validate all scene numbers
|
|
1048
1082
|
for (const sceneNum of sceneNums) {
|
|
1049
1083
|
if (sceneNum > segments.length) {
|
|
1050
|
-
|
|
1051
|
-
process.exit(1);
|
|
1084
|
+
exitWithError(usageError(`Scene ${sceneNum} does not exist. Storyboard has ${segments.length} scenes.`));
|
|
1052
1085
|
}
|
|
1053
1086
|
}
|
|
1054
1087
|
// Determine what to regenerate
|
|
@@ -1071,48 +1104,44 @@ export function registerScriptPipelineCommands(aiCommand) {
|
|
|
1071
1104
|
if (imageProvider === "openai" || imageProvider === "dalle") {
|
|
1072
1105
|
imageApiKey = (await getApiKey("OPENAI_API_KEY", "OpenAI")) ?? undefined;
|
|
1073
1106
|
if (!imageApiKey) {
|
|
1074
|
-
|
|
1075
|
-
process.exit(1);
|
|
1107
|
+
exitWithError(authError("OPENAI_API_KEY", "OpenAI"));
|
|
1076
1108
|
}
|
|
1077
1109
|
}
|
|
1078
1110
|
else if (imageProvider === "gemini") {
|
|
1079
1111
|
imageApiKey = (await getApiKey("GOOGLE_API_KEY", "Google")) ?? undefined;
|
|
1080
1112
|
if (!imageApiKey) {
|
|
1081
|
-
|
|
1082
|
-
process.exit(1);
|
|
1113
|
+
exitWithError(authError("GOOGLE_API_KEY", "Google"));
|
|
1083
1114
|
}
|
|
1084
1115
|
}
|
|
1085
1116
|
else if (imageProvider === "grok") {
|
|
1086
1117
|
imageApiKey = (await getApiKey("XAI_API_KEY", "xAI")) ?? undefined;
|
|
1087
1118
|
if (!imageApiKey) {
|
|
1088
|
-
|
|
1089
|
-
process.exit(1);
|
|
1119
|
+
exitWithError(authError("XAI_API_KEY", "xAI"));
|
|
1090
1120
|
}
|
|
1091
1121
|
}
|
|
1092
1122
|
}
|
|
1093
1123
|
if (regenerateVideo) {
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1124
|
+
const generatorKeyMap = {
|
|
1125
|
+
grok: { envVar: "XAI_API_KEY", name: "xAI" },
|
|
1126
|
+
kling: { envVar: "KLING_API_KEY", name: "Kling" },
|
|
1127
|
+
runway: { envVar: "RUNWAY_API_SECRET", name: "Runway" },
|
|
1128
|
+
veo: { envVar: "GOOGLE_API_KEY", name: "Google" },
|
|
1129
|
+
};
|
|
1130
|
+
const generator = options.generator || "grok";
|
|
1131
|
+
const genInfo = generatorKeyMap[generator];
|
|
1132
|
+
if (!genInfo) {
|
|
1133
|
+
exitWithError(usageError(`Invalid generator: ${generator}`, `Available: ${Object.keys(generatorKeyMap).join(", ")}`));
|
|
1101
1134
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
console.error(chalk.red("Runway API key required. Set RUNWAY_API_SECRET in .env or run: vibe setup"));
|
|
1106
|
-
process.exit(1);
|
|
1107
|
-
}
|
|
1108
|
-
videoApiKey = key;
|
|
1135
|
+
const key = await getApiKey(genInfo.envVar, genInfo.name);
|
|
1136
|
+
if (!key) {
|
|
1137
|
+
exitWithError(authError(genInfo.envVar, genInfo.name));
|
|
1109
1138
|
}
|
|
1139
|
+
videoApiKey = key;
|
|
1110
1140
|
}
|
|
1111
1141
|
if (regenerateNarration) {
|
|
1112
1142
|
const key = await getApiKey("ELEVENLABS_API_KEY", "ElevenLabs");
|
|
1113
1143
|
if (!key) {
|
|
1114
|
-
|
|
1115
|
-
process.exit(1);
|
|
1144
|
+
exitWithError(authError("ELEVENLABS_API_KEY", "ElevenLabs"));
|
|
1116
1145
|
}
|
|
1117
1146
|
elevenlabsApiKey = key;
|
|
1118
1147
|
}
|
|
@@ -1133,8 +1162,8 @@ export function registerScriptPipelineCommands(aiCommand) {
|
|
|
1133
1162
|
voiceId: options.voice,
|
|
1134
1163
|
});
|
|
1135
1164
|
if (!ttsResult.success || !ttsResult.audioBuffer) {
|
|
1136
|
-
ttsSpinner.fail(
|
|
1137
|
-
|
|
1165
|
+
ttsSpinner.fail(`Failed to generate narration: ${ttsResult.error || "Unknown error"}`);
|
|
1166
|
+
exitWithError(apiError(`Failed to generate narration: ${ttsResult.error || "Unknown error"}`, true));
|
|
1138
1167
|
}
|
|
1139
1168
|
await writeFile(narrationPath, ttsResult.audioBuffer);
|
|
1140
1169
|
narrationDuration = await getAudioDuration(narrationPath);
|
|
@@ -1286,8 +1315,8 @@ Generate the single-person scene image now.`;
|
|
|
1286
1315
|
}
|
|
1287
1316
|
else {
|
|
1288
1317
|
const errorMsg = imageError || "Unknown error";
|
|
1289
|
-
imageSpinner.fail(
|
|
1290
|
-
|
|
1318
|
+
imageSpinner.fail(`Failed to generate image: ${errorMsg}`);
|
|
1319
|
+
exitWithError(apiError(`Failed to generate image: ${errorMsg}`, true));
|
|
1291
1320
|
}
|
|
1292
1321
|
}
|
|
1293
1322
|
// Step 3: Regenerate video if needed
|
|
@@ -1296,9 +1325,8 @@ Generate the single-person scene image now.`;
|
|
|
1296
1325
|
const videoSpinner = ora(`🎬 Regenerating video for scene ${sceneNum}...`).start();
|
|
1297
1326
|
// Check if image exists
|
|
1298
1327
|
if (!existsSync(imagePath)) {
|
|
1299
|
-
videoSpinner.fail(
|
|
1300
|
-
|
|
1301
|
-
process.exit(1);
|
|
1328
|
+
videoSpinner.fail(`Reference image not found: ${imagePath}`);
|
|
1329
|
+
exitWithError(notFoundError(imagePath));
|
|
1302
1330
|
}
|
|
1303
1331
|
const imageBuffer = await readFile(imagePath);
|
|
1304
1332
|
const ext = extname(imagePath).toLowerCase().slice(1);
|
|
@@ -1311,8 +1339,8 @@ Generate the single-person scene image now.`;
|
|
|
1311
1339
|
const kling = new KlingProvider();
|
|
1312
1340
|
await kling.initialize({ apiKey: videoApiKey });
|
|
1313
1341
|
if (!kling.isConfigured()) {
|
|
1314
|
-
videoSpinner.fail(
|
|
1315
|
-
|
|
1342
|
+
videoSpinner.fail("Invalid Kling API key format");
|
|
1343
|
+
exitWithError(authError("KLING_API_KEY", "Kling"));
|
|
1316
1344
|
}
|
|
1317
1345
|
// Try to use image-to-video if ImgBB API key is available
|
|
1318
1346
|
const imgbbApiKey = await getApiKeyFromConfig("imgbb") || process.env.IMGBB_API_KEY;
|
|
@@ -1412,8 +1440,8 @@ Generate the single-person scene image now.`;
|
|
|
1412
1440
|
videoSpinner.succeed(chalk.green("Generated video"));
|
|
1413
1441
|
}
|
|
1414
1442
|
else {
|
|
1415
|
-
videoSpinner.fail(
|
|
1416
|
-
|
|
1443
|
+
videoSpinner.fail("Failed to generate video after all retries");
|
|
1444
|
+
exitWithError(apiError("Failed to generate video after all retries", true));
|
|
1417
1445
|
}
|
|
1418
1446
|
}
|
|
1419
1447
|
// Step 4: Recalculate startTime for ALL segments and re-save storyboard
|
|
@@ -1485,9 +1513,7 @@ Generate the single-person scene image now.`;
|
|
|
1485
1513
|
console.log();
|
|
1486
1514
|
}
|
|
1487
1515
|
catch (error) {
|
|
1488
|
-
|
|
1489
|
-
console.error(error);
|
|
1490
|
-
process.exit(1);
|
|
1516
|
+
exitWithError(generalError(error instanceof Error ? error.message : "Scene regeneration failed"));
|
|
1491
1517
|
}
|
|
1492
1518
|
});
|
|
1493
1519
|
}
|