@vibeframe/cli 0.31.1 → 0.33.1
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 +169 -97
- 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
|
@@ -33,11 +33,11 @@ import { requireApiKey, hasApiKey } from "../utils/api-key.js";
|
|
|
33
33
|
import { hasTTY, prompt as promptText } from "../utils/tty.js";
|
|
34
34
|
import { getApiKeyFromConfig } from "../config/index.js";
|
|
35
35
|
import { sanitizeLLMResponse } from "./sanitize.js";
|
|
36
|
-
import { isJsonMode, outputResult, log, exitWithError, usageError, apiError } from "./output.js";
|
|
36
|
+
import { isJsonMode, outputResult, log, exitWithError, usageError, apiError, generalError, authError, notFoundError } from "./output.js";
|
|
37
37
|
import { commandExists } from "../utils/exec-safe.js";
|
|
38
38
|
import { uploadToImgbb } from "./ai-script-pipeline.js";
|
|
39
39
|
import { downloadVideo, formatTime } from "./ai-helpers.js";
|
|
40
|
-
import { rejectControlChars } from "./validate.js";
|
|
40
|
+
import { rejectControlChars, validateOutputPath } from "./validate.js";
|
|
41
41
|
import { resolveProvider } from "../utils/provider-resolver.js";
|
|
42
42
|
import { executeThumbnailBestFrame } from "./ai-image.js";
|
|
43
43
|
import { registerMotionCommand } from "./ai-motion.js";
|
|
@@ -116,16 +116,17 @@ Examples:
|
|
|
116
116
|
if (hasTTY()) {
|
|
117
117
|
prompt = await promptText(chalk.cyan("What would you like to generate? "));
|
|
118
118
|
if (!prompt?.trim()) {
|
|
119
|
-
|
|
120
|
-
return;
|
|
119
|
+
exitWithError(usageError("Prompt is required."));
|
|
121
120
|
}
|
|
122
121
|
}
|
|
123
122
|
else {
|
|
124
|
-
|
|
125
|
-
return;
|
|
123
|
+
exitWithError(usageError("Prompt argument is required.", "Usage: vibe generate image <prompt>"));
|
|
126
124
|
}
|
|
127
125
|
}
|
|
128
126
|
rejectControlChars(prompt);
|
|
127
|
+
if (options.output) {
|
|
128
|
+
validateOutputPath(options.output);
|
|
129
|
+
}
|
|
129
130
|
// Auto-resolve provider if user didn't explicitly set one
|
|
130
131
|
let provider = options.provider.toLowerCase();
|
|
131
132
|
const validProviders = ["openai", "dalle", "gemini", "grok", "runway"];
|
|
@@ -181,8 +182,8 @@ Examples:
|
|
|
181
182
|
n: parseInt(options.count),
|
|
182
183
|
});
|
|
183
184
|
if (!result.success || !result.images) {
|
|
184
|
-
spinner.fail(
|
|
185
|
-
|
|
185
|
+
spinner.fail(result.error || "Image generation failed");
|
|
186
|
+
exitWithError(apiError(result.error || "Image generation failed", true));
|
|
186
187
|
}
|
|
187
188
|
spinner.succeed(chalk.green(`Generated ${result.images.length} image(s) with OpenAI GPT Image 1.5`));
|
|
188
189
|
if (isJsonMode()) {
|
|
@@ -260,8 +261,7 @@ Examples:
|
|
|
260
261
|
// Validate aspect ratio
|
|
261
262
|
const validRatios = ["1:1", "1:4", "1:8", "2:3", "3:2", "3:4", "4:1", "4:3", "4:5", "5:4", "8:1", "9:16", "16:9", "21:9"];
|
|
262
263
|
if (options.ratio && !validRatios.includes(options.ratio)) {
|
|
263
|
-
|
|
264
|
-
process.exit(1);
|
|
264
|
+
exitWithError(usageError(`Invalid ratio "${options.ratio}". Valid: ${validRatios.join(", ")}`));
|
|
265
265
|
}
|
|
266
266
|
const gemini = new GeminiProvider();
|
|
267
267
|
await gemini.initialize({ apiKey });
|
|
@@ -288,8 +288,8 @@ Examples:
|
|
|
288
288
|
usedLabel = "Nano Banana (fallback)";
|
|
289
289
|
}
|
|
290
290
|
if (!result.success || !result.images) {
|
|
291
|
-
spinner.fail(
|
|
292
|
-
|
|
291
|
+
spinner.fail(result.error || "Image generation failed");
|
|
292
|
+
exitWithError(apiError(result.error || "Image generation failed", true));
|
|
293
293
|
}
|
|
294
294
|
spinner.succeed(chalk.green(`Generated ${result.images.length} image(s) with Gemini (${usedLabel})`));
|
|
295
295
|
if (isJsonMode()) {
|
|
@@ -345,8 +345,8 @@ Examples:
|
|
|
345
345
|
n: parseInt(options.count),
|
|
346
346
|
});
|
|
347
347
|
if (!result.success || !result.images) {
|
|
348
|
-
spinner.fail(
|
|
349
|
-
|
|
348
|
+
spinner.fail(result.error || "Image generation failed");
|
|
349
|
+
exitWithError(apiError(result.error || "Image generation failed", true));
|
|
350
350
|
}
|
|
351
351
|
spinner.succeed(chalk.green(`Generated ${result.images.length} image(s) with xAI Grok`));
|
|
352
352
|
if (isJsonMode()) {
|
|
@@ -416,8 +416,8 @@ Examples:
|
|
|
416
416
|
const __dirname = dirname(__filename);
|
|
417
417
|
const scriptPath = resolve(__dirname, "../../../../.claude/skills/runway-video/scripts/image.py");
|
|
418
418
|
if (!options.output) {
|
|
419
|
-
spinner.fail(
|
|
420
|
-
|
|
419
|
+
spinner.fail("Output path required for Runway");
|
|
420
|
+
exitWithError(usageError("Output path required for Runway. Use -o option."));
|
|
421
421
|
}
|
|
422
422
|
const outputPath = resolve(process.cwd(), options.output);
|
|
423
423
|
const args = [scriptPath, prompt, "-o", outputPath, "-r", options.ratio || "16:9"];
|
|
@@ -502,16 +502,17 @@ Examples:
|
|
|
502
502
|
if (hasTTY()) {
|
|
503
503
|
prompt = await promptText(chalk.cyan("Describe your video: "));
|
|
504
504
|
if (!prompt?.trim()) {
|
|
505
|
-
|
|
506
|
-
return;
|
|
505
|
+
exitWithError(usageError("Prompt is required."));
|
|
507
506
|
}
|
|
508
507
|
}
|
|
509
508
|
else {
|
|
510
|
-
|
|
511
|
-
return;
|
|
509
|
+
exitWithError(usageError("Prompt argument is required.", "Usage: vibe generate video <prompt>"));
|
|
512
510
|
}
|
|
513
511
|
}
|
|
514
512
|
rejectControlChars(prompt);
|
|
513
|
+
if (options.output) {
|
|
514
|
+
validateOutputPath(options.output);
|
|
515
|
+
}
|
|
515
516
|
let provider = options.provider.toLowerCase();
|
|
516
517
|
const validProviders = ["runway", "kling", "veo", "grok"];
|
|
517
518
|
if (!validProviders.includes(provider)) {
|
|
@@ -594,9 +595,7 @@ Examples:
|
|
|
594
595
|
// Runway gen4_turbo requires an input image; gen4.5 supports text-to-video
|
|
595
596
|
const runwayModel = options.runwayModel || "gen4.5";
|
|
596
597
|
if (provider === "runway" && !options.image && runwayModel !== "gen4.5") {
|
|
597
|
-
|
|
598
|
-
console.error(chalk.dim("Example: vibe generate video \"prompt\" -p runway -i image.png -o out.mp4"));
|
|
599
|
-
process.exit(1);
|
|
598
|
+
exitWithError(usageError(`Runway ${runwayModel} requires an input image. Use -i <image> or use gen4.5 for text-to-video.`));
|
|
600
599
|
}
|
|
601
600
|
const spinner = ora(`Initializing ${providerName}...`).start();
|
|
602
601
|
spinner.text = "Starting video generation...";
|
|
@@ -614,8 +613,8 @@ Examples:
|
|
|
614
613
|
seed: options.seed ? parseInt(options.seed) : undefined,
|
|
615
614
|
});
|
|
616
615
|
if (result.status === "failed") {
|
|
617
|
-
spinner.fail(
|
|
618
|
-
|
|
616
|
+
spinner.fail(result.error || "Failed to start generation");
|
|
617
|
+
exitWithError(apiError(result.error || "Failed to start generation", true));
|
|
619
618
|
}
|
|
620
619
|
console.log();
|
|
621
620
|
console.log(chalk.bold.cyan("Video Generation Started"));
|
|
@@ -641,8 +640,8 @@ Examples:
|
|
|
641
640
|
const kling = new KlingProvider();
|
|
642
641
|
await kling.initialize({ apiKey });
|
|
643
642
|
if (!kling.isConfigured()) {
|
|
644
|
-
spinner.fail(
|
|
645
|
-
|
|
643
|
+
spinner.fail("Invalid API key format");
|
|
644
|
+
exitWithError(authError("KLING_API_KEY", "Kling"));
|
|
646
645
|
}
|
|
647
646
|
// Kling v2.x requires image URL, not base64 — auto-upload to ImgBB
|
|
648
647
|
let klingImage = referenceImage;
|
|
@@ -650,17 +649,16 @@ Examples:
|
|
|
650
649
|
spinner.text = "Uploading image to ImgBB for Kling...";
|
|
651
650
|
const imgbbKey = (await getApiKeyFromConfig("imgbb")) || process.env.IMGBB_API_KEY;
|
|
652
651
|
if (!imgbbKey) {
|
|
653
|
-
spinner.fail(
|
|
654
|
-
|
|
655
|
-
process.exit(1);
|
|
652
|
+
spinner.fail("ImgBB API key required");
|
|
653
|
+
exitWithError(authError("IMGBB_API_KEY", "ImgBB"));
|
|
656
654
|
}
|
|
657
655
|
// Extract raw base64 from data URI
|
|
658
656
|
const base64Data = klingImage.split(",")[1];
|
|
659
657
|
const imageBuffer = Buffer.from(base64Data, "base64");
|
|
660
658
|
const uploadResult = await uploadToImgbb(imageBuffer, imgbbKey);
|
|
661
659
|
if (!uploadResult.success || !uploadResult.url) {
|
|
662
|
-
spinner.fail(
|
|
663
|
-
|
|
660
|
+
spinner.fail("ImgBB upload failed");
|
|
661
|
+
exitWithError(apiError(`ImgBB upload failed: ${uploadResult.error}`, true));
|
|
664
662
|
}
|
|
665
663
|
klingImage = uploadResult.url;
|
|
666
664
|
spinner.text = "Starting video generation...";
|
|
@@ -674,8 +672,8 @@ Examples:
|
|
|
674
672
|
mode: options.mode,
|
|
675
673
|
});
|
|
676
674
|
if (result.status === "failed") {
|
|
677
|
-
spinner.fail(
|
|
678
|
-
|
|
675
|
+
spinner.fail(result.error || "Failed to start generation");
|
|
676
|
+
exitWithError(apiError(result.error || "Failed to start generation", true));
|
|
679
677
|
}
|
|
680
678
|
console.log();
|
|
681
679
|
console.log(chalk.bold.cyan("Video Generation Started"));
|
|
@@ -742,8 +740,8 @@ Examples:
|
|
|
742
740
|
personGeneration: options.person,
|
|
743
741
|
});
|
|
744
742
|
if (result.status === "failed") {
|
|
745
|
-
spinner.fail(
|
|
746
|
-
|
|
743
|
+
spinner.fail(result.error || "Failed to start generation");
|
|
744
|
+
exitWithError(apiError(result.error || "Failed to start generation", true));
|
|
747
745
|
}
|
|
748
746
|
console.log();
|
|
749
747
|
console.log(chalk.bold.cyan("Video Generation Started"));
|
|
@@ -772,8 +770,8 @@ Examples:
|
|
|
772
770
|
aspectRatio: options.ratio,
|
|
773
771
|
});
|
|
774
772
|
if (result.status === "failed") {
|
|
775
|
-
spinner.fail(
|
|
776
|
-
|
|
773
|
+
spinner.fail(result.error || "Failed to start generation");
|
|
774
|
+
exitWithError(apiError(result.error || "Failed to start generation", true));
|
|
777
775
|
}
|
|
778
776
|
console.log();
|
|
779
777
|
console.log(chalk.bold.cyan("Video Generation Started"));
|
|
@@ -794,8 +792,8 @@ Examples:
|
|
|
794
792
|
}, 300000);
|
|
795
793
|
}
|
|
796
794
|
if (!finalResult || finalResult.status !== "completed") {
|
|
797
|
-
spinner.fail(
|
|
798
|
-
|
|
795
|
+
spinner.fail(finalResult?.error || "Generation failed");
|
|
796
|
+
exitWithError(apiError(finalResult?.error || "Generation failed", true));
|
|
799
797
|
}
|
|
800
798
|
spinner.succeed(chalk.green("Video generated"));
|
|
801
799
|
if (isJsonMode()) {
|
|
@@ -854,16 +852,17 @@ generateCommand
|
|
|
854
852
|
if (hasTTY()) {
|
|
855
853
|
text = await promptText(chalk.cyan("What text to speak? "));
|
|
856
854
|
if (!text?.trim()) {
|
|
857
|
-
|
|
858
|
-
return;
|
|
855
|
+
exitWithError(usageError("Text is required."));
|
|
859
856
|
}
|
|
860
857
|
}
|
|
861
858
|
else {
|
|
862
|
-
|
|
863
|
-
return;
|
|
859
|
+
exitWithError(usageError("Text argument is required.", "Usage: vibe generate speech <text>"));
|
|
864
860
|
}
|
|
865
861
|
}
|
|
866
862
|
rejectControlChars(text);
|
|
863
|
+
if (options.output) {
|
|
864
|
+
validateOutputPath(options.output);
|
|
865
|
+
}
|
|
867
866
|
if (options.dryRun) {
|
|
868
867
|
outputResult({ dryRun: true, command: "generate speech", params: { text, voice: options.voice, output: options.output } });
|
|
869
868
|
return;
|
|
@@ -898,8 +897,8 @@ generateCommand
|
|
|
898
897
|
voiceId: options.voice,
|
|
899
898
|
});
|
|
900
899
|
if (!result.success || !result.audioBuffer) {
|
|
901
|
-
spinner.fail(
|
|
902
|
-
|
|
900
|
+
spinner.fail(result.error || "TTS generation failed");
|
|
901
|
+
exitWithError(apiError(result.error || "TTS generation failed", true));
|
|
903
902
|
}
|
|
904
903
|
const outputPath = resolve(process.cwd(), options.output);
|
|
905
904
|
await writeFile(outputPath, result.audioBuffer);
|
|
@@ -945,9 +944,8 @@ generateCommand
|
|
|
945
944
|
console.log();
|
|
946
945
|
}
|
|
947
946
|
catch (error) {
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
process.exit(1);
|
|
947
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
948
|
+
exitWithError(apiError(`TTS generation failed: ${msg}`, true));
|
|
951
949
|
}
|
|
952
950
|
});
|
|
953
951
|
// ============================================================================
|
|
@@ -966,6 +964,9 @@ generateCommand
|
|
|
966
964
|
.action(async (prompt, options) => {
|
|
967
965
|
try {
|
|
968
966
|
rejectControlChars(prompt);
|
|
967
|
+
if (options.output) {
|
|
968
|
+
validateOutputPath(options.output);
|
|
969
|
+
}
|
|
969
970
|
if (options.dryRun) {
|
|
970
971
|
outputResult({ dryRun: true, command: "generate sound-effect", params: { prompt, duration: options.duration, promptInfluence: options.promptInfluence, output: options.output } });
|
|
971
972
|
return;
|
|
@@ -979,8 +980,8 @@ generateCommand
|
|
|
979
980
|
promptInfluence: options.promptInfluence ? parseFloat(options.promptInfluence) : undefined,
|
|
980
981
|
});
|
|
981
982
|
if (!result.success || !result.audioBuffer) {
|
|
982
|
-
spinner.fail(
|
|
983
|
-
|
|
983
|
+
spinner.fail(result.error || "Sound effect generation failed");
|
|
984
|
+
exitWithError(apiError(result.error || "Sound effect generation failed", true));
|
|
984
985
|
}
|
|
985
986
|
const outputPath = resolve(process.cwd(), options.output);
|
|
986
987
|
await writeFile(outputPath, result.audioBuffer);
|
|
@@ -993,9 +994,8 @@ generateCommand
|
|
|
993
994
|
console.log();
|
|
994
995
|
}
|
|
995
996
|
catch (error) {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
process.exit(1);
|
|
997
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
998
|
+
exitWithError(apiError(`Sound effect generation failed: ${msg}`, true));
|
|
999
999
|
}
|
|
1000
1000
|
});
|
|
1001
1001
|
// ============================================================================
|
|
@@ -1017,6 +1017,9 @@ generateCommand
|
|
|
1017
1017
|
.action(async (prompt, options) => {
|
|
1018
1018
|
try {
|
|
1019
1019
|
rejectControlChars(prompt);
|
|
1020
|
+
if (options.output) {
|
|
1021
|
+
validateOutputPath(options.output);
|
|
1022
|
+
}
|
|
1020
1023
|
const provider = (options.provider || "elevenlabs").toLowerCase();
|
|
1021
1024
|
if (options.dryRun) {
|
|
1022
1025
|
outputResult({ dryRun: true, command: "generate music", params: { prompt, provider, duration: options.duration, model: options.model, output: options.output, instrumental: options.instrumental } });
|
|
@@ -1034,8 +1037,8 @@ generateCommand
|
|
|
1034
1037
|
forceInstrumental: options.instrumental || false,
|
|
1035
1038
|
});
|
|
1036
1039
|
if (!result.success || !result.audioBuffer) {
|
|
1037
|
-
spinner.fail(
|
|
1038
|
-
|
|
1040
|
+
spinner.fail(result.error || "Music generation failed");
|
|
1041
|
+
exitWithError(apiError(result.error || "Music generation failed", true));
|
|
1039
1042
|
}
|
|
1040
1043
|
const outputPath = resolve(process.cwd(), options.output);
|
|
1041
1044
|
await writeFile(outputPath, result.audioBuffer);
|
|
@@ -1064,20 +1067,18 @@ generateCommand
|
|
|
1064
1067
|
spinner.text = "Uploading melody reference...";
|
|
1065
1068
|
const absPath = resolve(process.cwd(), options.melody);
|
|
1066
1069
|
if (!existsSync(absPath)) {
|
|
1067
|
-
spinner.fail(
|
|
1068
|
-
|
|
1070
|
+
spinner.fail(`Melody file not found: ${options.melody}`);
|
|
1071
|
+
exitWithError(notFoundError(options.melody));
|
|
1069
1072
|
}
|
|
1070
|
-
|
|
1071
|
-
console.log(chalk.yellow("Please upload your melody file and provide the URL"));
|
|
1072
|
-
process.exit(1);
|
|
1073
|
+
exitWithError(usageError("Melody conditioning requires a publicly accessible URL", "Please upload your melody file and provide the URL."));
|
|
1073
1074
|
}
|
|
1074
1075
|
const result = await replicate.generateMusic(prompt, {
|
|
1075
1076
|
duration,
|
|
1076
1077
|
model: options.model,
|
|
1077
1078
|
});
|
|
1078
1079
|
if (!result.success || !result.taskId) {
|
|
1079
|
-
spinner.fail(
|
|
1080
|
-
|
|
1080
|
+
spinner.fail(result.error || "Music generation failed");
|
|
1081
|
+
exitWithError(apiError(result.error || "Music generation failed", true));
|
|
1081
1082
|
}
|
|
1082
1083
|
if (!options.wait) {
|
|
1083
1084
|
spinner.succeed(chalk.green("Music generation started"));
|
|
@@ -1089,14 +1090,14 @@ generateCommand
|
|
|
1089
1090
|
spinner.text = "Generating music (this may take a few minutes)...";
|
|
1090
1091
|
const finalResult = await replicate.waitForMusic(result.taskId);
|
|
1091
1092
|
if (!finalResult.success || !finalResult.audioUrl) {
|
|
1092
|
-
spinner.fail(
|
|
1093
|
-
|
|
1093
|
+
spinner.fail(finalResult.error || "Music generation failed");
|
|
1094
|
+
exitWithError(apiError(finalResult.error || "Music generation failed", true));
|
|
1094
1095
|
}
|
|
1095
1096
|
spinner.text = "Downloading generated audio...";
|
|
1096
1097
|
const response = await fetch(finalResult.audioUrl);
|
|
1097
1098
|
if (!response.ok) {
|
|
1098
|
-
spinner.fail(
|
|
1099
|
-
|
|
1099
|
+
spinner.fail("Failed to download generated audio");
|
|
1100
|
+
exitWithError(apiError("Failed to download generated audio", true));
|
|
1100
1101
|
}
|
|
1101
1102
|
const audioBuffer = Buffer.from(await response.arrayBuffer());
|
|
1102
1103
|
const outputPath = resolve(process.cwd(), options.output);
|
|
@@ -1114,9 +1115,8 @@ generateCommand
|
|
|
1114
1115
|
}
|
|
1115
1116
|
}
|
|
1116
1117
|
catch (error) {
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
process.exit(1);
|
|
1118
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1119
|
+
exitWithError(apiError(`Music generation failed: ${msg}`, true));
|
|
1120
1120
|
}
|
|
1121
1121
|
});
|
|
1122
1122
|
// ============================================================================
|
|
@@ -1156,9 +1156,8 @@ generateCommand
|
|
|
1156
1156
|
console.log();
|
|
1157
1157
|
}
|
|
1158
1158
|
catch (error) {
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
process.exit(1);
|
|
1159
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1160
|
+
exitWithError(apiError(`Failed to get music status: ${msg}`, true));
|
|
1162
1161
|
}
|
|
1163
1162
|
});
|
|
1164
1163
|
// ============================================================================
|
|
@@ -1177,11 +1176,13 @@ generateCommand
|
|
|
1177
1176
|
.action(async (content, options) => {
|
|
1178
1177
|
try {
|
|
1179
1178
|
rejectControlChars(content);
|
|
1179
|
+
if (options.output) {
|
|
1180
|
+
validateOutputPath(options.output);
|
|
1181
|
+
}
|
|
1180
1182
|
// Validate creativity level
|
|
1181
1183
|
const creativity = options.creativity?.toLowerCase();
|
|
1182
1184
|
if (creativity && creativity !== "low" && creativity !== "high") {
|
|
1183
|
-
|
|
1184
|
-
process.exit(1);
|
|
1185
|
+
exitWithError(usageError("Invalid creativity level. Use 'low' or 'high'."));
|
|
1185
1186
|
}
|
|
1186
1187
|
let textContent = content;
|
|
1187
1188
|
if (options.file) {
|
|
@@ -1201,8 +1202,8 @@ generateCommand
|
|
|
1201
1202
|
await claude.initialize({ apiKey });
|
|
1202
1203
|
const segments = await claude.analyzeContent(textContent, options.duration ? parseFloat(options.duration) : undefined, { creativity: creativity });
|
|
1203
1204
|
if (segments.length === 0) {
|
|
1204
|
-
spinner.fail(
|
|
1205
|
-
|
|
1205
|
+
spinner.fail("Could not generate storyboard");
|
|
1206
|
+
exitWithError(apiError("Could not generate storyboard", true));
|
|
1206
1207
|
}
|
|
1207
1208
|
spinner.succeed(chalk.green(`Generated ${segments.length} segments`));
|
|
1208
1209
|
for (const seg of segments) {
|
|
@@ -1243,9 +1244,8 @@ generateCommand
|
|
|
1243
1244
|
}
|
|
1244
1245
|
}
|
|
1245
1246
|
catch (error) {
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
process.exit(1);
|
|
1247
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1248
|
+
exitWithError(apiError(`Storyboard generation failed: ${msg}`, true));
|
|
1249
1249
|
}
|
|
1250
1250
|
});
|
|
1251
1251
|
// ============================================================================
|
|
@@ -1269,16 +1269,17 @@ generateCommand
|
|
|
1269
1269
|
try {
|
|
1270
1270
|
if (description)
|
|
1271
1271
|
rejectControlChars(description);
|
|
1272
|
+
if (options.output) {
|
|
1273
|
+
validateOutputPath(options.output);
|
|
1274
|
+
}
|
|
1272
1275
|
// Best-frame mode: analyze video with Gemini and extract frame
|
|
1273
1276
|
if (options.bestFrame) {
|
|
1274
1277
|
const absVideoPath = resolve(process.cwd(), options.bestFrame);
|
|
1275
1278
|
if (!existsSync(absVideoPath)) {
|
|
1276
|
-
|
|
1277
|
-
process.exit(1);
|
|
1279
|
+
exitWithError(notFoundError(absVideoPath));
|
|
1278
1280
|
}
|
|
1279
1281
|
if (!commandExists("ffmpeg")) {
|
|
1280
|
-
|
|
1281
|
-
process.exit(1);
|
|
1282
|
+
exitWithError(generalError("FFmpeg not found", "Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux)"));
|
|
1282
1283
|
}
|
|
1283
1284
|
const apiKey = await requireApiKey("GOOGLE_API_KEY", "Google", options.apiKey);
|
|
1284
1285
|
const name = basename(options.bestFrame, extname(options.bestFrame));
|
|
@@ -1292,8 +1293,8 @@ generateCommand
|
|
|
1292
1293
|
apiKey,
|
|
1293
1294
|
});
|
|
1294
1295
|
if (!result.success) {
|
|
1295
|
-
spinner.fail(
|
|
1296
|
-
|
|
1296
|
+
spinner.fail(result.error || "Best frame extraction failed");
|
|
1297
|
+
exitWithError(apiError(result.error || "Best frame extraction failed", true));
|
|
1297
1298
|
}
|
|
1298
1299
|
spinner.succeed(chalk.green("Best frame extracted"));
|
|
1299
1300
|
if (isJsonMode()) {
|
|
@@ -1312,9 +1313,7 @@ generateCommand
|
|
|
1312
1313
|
}
|
|
1313
1314
|
// Generation mode: create thumbnail with DALL-E
|
|
1314
1315
|
if (!description) {
|
|
1315
|
-
|
|
1316
|
-
console.error(chalk.dim("Usage: vibe generate thumbnail <description> or vibe generate thumbnail --best-frame <video>"));
|
|
1317
|
-
process.exit(1);
|
|
1316
|
+
exitWithError(usageError("Description required for thumbnail generation.", "Usage: vibe generate thumbnail <description> or vibe generate thumbnail --best-frame <video>"));
|
|
1318
1317
|
}
|
|
1319
1318
|
const apiKey = await requireApiKey("OPENAI_API_KEY", "OpenAI", options.apiKey);
|
|
1320
1319
|
const spinner = ora("Generating thumbnail...").start();
|
|
@@ -1322,8 +1321,8 @@ generateCommand
|
|
|
1322
1321
|
await openaiImage.initialize({ apiKey });
|
|
1323
1322
|
const result = await openaiImage.generateThumbnail(description, options.style);
|
|
1324
1323
|
if (!result.success || !result.images) {
|
|
1325
|
-
spinner.fail(
|
|
1326
|
-
|
|
1324
|
+
spinner.fail(result.error || "Thumbnail generation failed");
|
|
1325
|
+
exitWithError(apiError(result.error || "Thumbnail generation failed", true));
|
|
1327
1326
|
}
|
|
1328
1327
|
spinner.succeed(chalk.green("Thumbnail generated"));
|
|
1329
1328
|
const img = result.images[0];
|
|
@@ -1382,9 +1381,8 @@ generateCommand
|
|
|
1382
1381
|
}
|
|
1383
1382
|
}
|
|
1384
1383
|
catch (error) {
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
process.exit(1);
|
|
1384
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1385
|
+
exitWithError(apiError(`Thumbnail generation failed: ${msg}`, true));
|
|
1388
1386
|
}
|
|
1389
1387
|
});
|
|
1390
1388
|
// ============================================================================
|
|
@@ -1401,6 +1399,9 @@ generateCommand
|
|
|
1401
1399
|
.action(async (description, options) => {
|
|
1402
1400
|
try {
|
|
1403
1401
|
rejectControlChars(description);
|
|
1402
|
+
if (options.output) {
|
|
1403
|
+
validateOutputPath(options.output);
|
|
1404
|
+
}
|
|
1404
1405
|
if (options.dryRun) {
|
|
1405
1406
|
outputResult({ dryRun: true, command: "generate background", params: { description, aspect: options.aspect, output: options.output } });
|
|
1406
1407
|
return;
|
|
@@ -1411,8 +1412,8 @@ generateCommand
|
|
|
1411
1412
|
await openaiImage.initialize({ apiKey });
|
|
1412
1413
|
const result = await openaiImage.generateBackground(description, options.aspect);
|
|
1413
1414
|
if (!result.success || !result.images) {
|
|
1414
|
-
spinner.fail(
|
|
1415
|
-
|
|
1415
|
+
spinner.fail(result.error || "Background generation failed");
|
|
1416
|
+
exitWithError(apiError(result.error || "Background generation failed", true));
|
|
1416
1417
|
}
|
|
1417
1418
|
spinner.succeed(chalk.green("Background generated"));
|
|
1418
1419
|
const img = result.images[0];
|
|
@@ -1471,9 +1472,8 @@ generateCommand
|
|
|
1471
1472
|
}
|
|
1472
1473
|
}
|
|
1473
1474
|
catch (error) {
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
process.exit(1);
|
|
1475
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1476
|
+
exitWithError(apiError(`Background generation failed: ${msg}`, true));
|
|
1477
1477
|
}
|
|
1478
1478
|
});
|
|
1479
1479
|
// ============================================================================
|
|
@@ -1649,14 +1649,12 @@ generateCommand
|
|
|
1649
1649
|
}
|
|
1650
1650
|
}
|
|
1651
1651
|
else {
|
|
1652
|
-
|
|
1653
|
-
process.exit(1);
|
|
1652
|
+
exitWithError(usageError(`Invalid provider: ${provider}. Use grok, runway, or kling.`));
|
|
1654
1653
|
}
|
|
1655
1654
|
}
|
|
1656
1655
|
catch (error) {
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
process.exit(1);
|
|
1656
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1657
|
+
exitWithError(apiError(`Failed to get status: ${msg}`, true));
|
|
1660
1658
|
}
|
|
1661
1659
|
});
|
|
1662
1660
|
// ============================================================================
|
|
@@ -1686,8 +1684,8 @@ generateCommand
|
|
|
1686
1684
|
}
|
|
1687
1685
|
}
|
|
1688
1686
|
else {
|
|
1689
|
-
spinner.fail(
|
|
1690
|
-
|
|
1687
|
+
spinner.fail("Failed to cancel generation");
|
|
1688
|
+
exitWithError(apiError("Failed to cancel generation", true));
|
|
1691
1689
|
}
|
|
1692
1690
|
}
|
|
1693
1691
|
else if (provider === "runway") {
|
|
@@ -1704,19 +1702,17 @@ generateCommand
|
|
|
1704
1702
|
}
|
|
1705
1703
|
}
|
|
1706
1704
|
else {
|
|
1707
|
-
spinner.fail(
|
|
1708
|
-
|
|
1705
|
+
spinner.fail("Failed to cancel generation");
|
|
1706
|
+
exitWithError(apiError("Failed to cancel generation", true));
|
|
1709
1707
|
}
|
|
1710
1708
|
}
|
|
1711
1709
|
else {
|
|
1712
|
-
|
|
1713
|
-
process.exit(1);
|
|
1710
|
+
exitWithError(usageError(`Invalid provider: ${provider}. Use grok or runway.`));
|
|
1714
1711
|
}
|
|
1715
1712
|
}
|
|
1716
1713
|
catch (error) {
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
process.exit(1);
|
|
1714
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1715
|
+
exitWithError(apiError(`Failed to cancel: ${msg}`, true));
|
|
1720
1716
|
}
|
|
1721
1717
|
});
|
|
1722
1718
|
// ============================================================================
|
|
@@ -1739,6 +1735,9 @@ generateCommand
|
|
|
1739
1735
|
.action(async (id, options) => {
|
|
1740
1736
|
try {
|
|
1741
1737
|
const provider = (options.provider || "kling").toLowerCase();
|
|
1738
|
+
if (options.output) {
|
|
1739
|
+
validateOutputPath(options.output);
|
|
1740
|
+
}
|
|
1742
1741
|
if (options.dryRun) {
|
|
1743
1742
|
outputResult({ dryRun: true, command: "generate video-extend", params: { id, provider, prompt: options.prompt, duration: options.duration, negative: options.negative, veoModel: options.veoModel } });
|
|
1744
1743
|
return;
|
|
@@ -1749,8 +1748,8 @@ generateCommand
|
|
|
1749
1748
|
const kling = new KlingProvider();
|
|
1750
1749
|
await kling.initialize({ apiKey });
|
|
1751
1750
|
if (!kling.isConfigured()) {
|
|
1752
|
-
spinner.fail(
|
|
1753
|
-
|
|
1751
|
+
spinner.fail("Invalid API key format");
|
|
1752
|
+
exitWithError(authError("KLING_API_KEY", "Kling"));
|
|
1754
1753
|
}
|
|
1755
1754
|
spinner.text = "Starting video extension...";
|
|
1756
1755
|
const result = await kling.extendVideo(id, {
|
|
@@ -1759,8 +1758,8 @@ generateCommand
|
|
|
1759
1758
|
duration: options.duration,
|
|
1760
1759
|
});
|
|
1761
1760
|
if (result.status === "failed") {
|
|
1762
|
-
spinner.fail(
|
|
1763
|
-
|
|
1761
|
+
spinner.fail(result.error || "Failed to start extension");
|
|
1762
|
+
exitWithError(apiError(result.error || "Failed to start extension", true));
|
|
1764
1763
|
}
|
|
1765
1764
|
console.log();
|
|
1766
1765
|
console.log(chalk.bold.cyan("Video Extension Started"));
|
|
@@ -1780,8 +1779,8 @@ generateCommand
|
|
|
1780
1779
|
spinner.text = `Extending video... ${status.status}`;
|
|
1781
1780
|
}, 600000);
|
|
1782
1781
|
if (finalResult.status !== "completed") {
|
|
1783
|
-
spinner.fail(
|
|
1784
|
-
|
|
1782
|
+
spinner.fail(finalResult.error || "Extension failed");
|
|
1783
|
+
exitWithError(apiError(finalResult.error || "Extension failed", true));
|
|
1785
1784
|
}
|
|
1786
1785
|
spinner.succeed(chalk.green("Video extended"));
|
|
1787
1786
|
if (isJsonMode()) {
|
|
@@ -1832,8 +1831,8 @@ generateCommand
|
|
|
1832
1831
|
model: veoModel,
|
|
1833
1832
|
});
|
|
1834
1833
|
if (result.status === "failed") {
|
|
1835
|
-
spinner.fail(
|
|
1836
|
-
|
|
1834
|
+
spinner.fail(result.error || "Failed to start extension");
|
|
1835
|
+
exitWithError(apiError(result.error || "Failed to start extension", true));
|
|
1837
1836
|
}
|
|
1838
1837
|
console.log();
|
|
1839
1838
|
console.log(chalk.bold.cyan("Veo Video Extension Started"));
|
|
@@ -1853,8 +1852,8 @@ generateCommand
|
|
|
1853
1852
|
spinner.text = `Extending video... ${status.status}`;
|
|
1854
1853
|
}, 300000);
|
|
1855
1854
|
if (finalResult.status !== "completed") {
|
|
1856
|
-
spinner.fail(
|
|
1857
|
-
|
|
1855
|
+
spinner.fail(finalResult.error || "Extension failed");
|
|
1856
|
+
exitWithError(apiError(finalResult.error || "Extension failed", true));
|
|
1858
1857
|
}
|
|
1859
1858
|
spinner.succeed(chalk.green("Video extended"));
|
|
1860
1859
|
if (isJsonMode()) {
|
|
@@ -1886,14 +1885,12 @@ generateCommand
|
|
|
1886
1885
|
}
|
|
1887
1886
|
}
|
|
1888
1887
|
else {
|
|
1889
|
-
|
|
1890
|
-
process.exit(1);
|
|
1888
|
+
exitWithError(usageError(`Invalid provider: ${provider}. Video extend supports: kling, veo`));
|
|
1891
1889
|
}
|
|
1892
1890
|
}
|
|
1893
1891
|
catch (error) {
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
process.exit(1);
|
|
1892
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1893
|
+
exitWithError(apiError(`Video extension failed: ${msg}`, true));
|
|
1897
1894
|
}
|
|
1898
1895
|
});
|
|
1899
1896
|
//# sourceMappingURL=generate.js.map
|