@vibeframe/cli 0.78.0 → 0.79.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 +168 -143
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -29771,7 +29771,7 @@ __export(remotion_exports, {
|
|
|
29771
29771
|
wrapComponentWithVideo: () => wrapComponentWithVideo
|
|
29772
29772
|
});
|
|
29773
29773
|
import { writeFile as writeFile10, mkdir as mkdir6, rm, copyFile as copyFile2 } from "node:fs/promises";
|
|
29774
|
-
import { existsSync as
|
|
29774
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
29775
29775
|
import { join as join7 } from "node:path";
|
|
29776
29776
|
import { tmpdir } from "node:os";
|
|
29777
29777
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -29846,7 +29846,7 @@ registerRoot(Root);
|
|
|
29846
29846
|
const depsHash = createHash3("md5").update(packageJsonStr).digest("hex").slice(0, 12);
|
|
29847
29847
|
const cacheMarker = join7(REMOTION_CACHE_DIR, `.deps-${depsHash}`);
|
|
29848
29848
|
const cachedModules = join7(REMOTION_CACHE_DIR, "node_modules");
|
|
29849
|
-
if (
|
|
29849
|
+
if (existsSync9(cacheMarker) && existsSync9(cachedModules)) {
|
|
29850
29850
|
const { symlink } = await import("node:fs/promises");
|
|
29851
29851
|
try {
|
|
29852
29852
|
await symlink(cachedModules, join7(dir, "node_modules"), "dir");
|
|
@@ -29858,7 +29858,7 @@ registerRoot(Root);
|
|
|
29858
29858
|
try {
|
|
29859
29859
|
await mkdir6(REMOTION_CACHE_DIR, { recursive: true });
|
|
29860
29860
|
await writeFile10(join7(REMOTION_CACHE_DIR, "package.json"), packageJsonStr);
|
|
29861
|
-
if (
|
|
29861
|
+
if (existsSync9(join7(dir, "node_modules"))) {
|
|
29862
29862
|
const { rename: rename5, symlink } = await import("node:fs/promises");
|
|
29863
29863
|
await rename5(join7(dir, "node_modules"), cachedModules).catch(() => {
|
|
29864
29864
|
});
|
|
@@ -30526,7 +30526,7 @@ __export(ai_motion_exports, {
|
|
|
30526
30526
|
registerMotionCommand: () => registerMotionCommand
|
|
30527
30527
|
});
|
|
30528
30528
|
import { resolve as resolve12 } from "node:path";
|
|
30529
|
-
import { existsSync as
|
|
30529
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
30530
30530
|
import { readFile as readFile10, writeFile as writeFile11 } from "node:fs/promises";
|
|
30531
30531
|
async function executeMotion(options) {
|
|
30532
30532
|
const modelAlias = options.model || "sonnet";
|
|
@@ -30574,7 +30574,7 @@ Use this image analysis to inform the color palette, typography placement, and o
|
|
|
30574
30574
|
let result;
|
|
30575
30575
|
if (options.fromTsx) {
|
|
30576
30576
|
const tsxPath = resolve12(process.cwd(), options.fromTsx);
|
|
30577
|
-
if (!
|
|
30577
|
+
if (!existsSync10(tsxPath)) {
|
|
30578
30578
|
return { success: false, error: `TSX file not found: ${tsxPath}` };
|
|
30579
30579
|
}
|
|
30580
30580
|
const existingCode = await readFile10(tsxPath, "utf-8");
|
|
@@ -31778,7 +31778,7 @@ var init_speech = __esm({
|
|
|
31778
31778
|
// src/commands/generate/music.ts
|
|
31779
31779
|
import { resolve as resolve17 } from "node:path";
|
|
31780
31780
|
import { writeFile as writeFile16 } from "node:fs/promises";
|
|
31781
|
-
import { existsSync as
|
|
31781
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
31782
31782
|
async function executeMusic(options) {
|
|
31783
31783
|
try {
|
|
31784
31784
|
const provider = options.provider || "elevenlabs";
|
|
@@ -31835,7 +31835,7 @@ async function executeMusic(options) {
|
|
|
31835
31835
|
}
|
|
31836
31836
|
}
|
|
31837
31837
|
function registerMusicCommand(parent) {
|
|
31838
|
-
parent.command("music").description("Generate background music from a text prompt (ElevenLabs or Replicate MusicGen)").argument("<prompt>", "Description of the music to generate").option("-p, --provider <provider>", "Provider: elevenlabs (default, up to 10min), replicate (MusicGen, max 30s)", "elevenlabs").option("-k, --api-key <key>", "API key (or set ELEVENLABS_API_KEY / REPLICATE_API_TOKEN env)").option("-d, --duration <seconds>", "Duration in seconds (elevenlabs: 3-600, replicate: 1-30)", "8").option("--instrumental", "Force instrumental music, no vocals (ElevenLabs only)").option("--melody <file>", "Reference melody audio file for conditioning (Replicate only)").option("--model <model>", "Model variant (Replicate only): large, stereo-large, melody-large, stereo-melody-large", "stereo-large").option("-o, --output <path>", "Output audio file path", "music.mp3").option("--no-wait", "Don't wait for generation to complete (Replicate async mode)").option("--dry-run", "Preview parameters without executing").action(async (prompt3, options) => {
|
|
31838
|
+
parent.command("music").description("Generate background music from a text prompt (ElevenLabs or Replicate MusicGen)").argument("<prompt>", "Description of the music to generate").option("-p, --provider <provider>", "Provider: elevenlabs (default, up to 10min), replicate (MusicGen, max 30s)", "elevenlabs").option("-k, --api-key <key>", "API key (or set ELEVENLABS_API_KEY / REPLICATE_API_TOKEN env)").option("-d, --duration <seconds>", "Duration in seconds (elevenlabs: 3-600, replicate: 1-30)", "8").option("--instrumental", "Force instrumental music, no vocals (ElevenLabs only)").option("--melody <file>", "Reference melody audio file for conditioning (Replicate only)").option("-m, --model <model>", "Model variant (Replicate only): large, stereo-large, melody-large, stereo-melody-large", "stereo-large").option("-o, --output <path>", "Output audio file path", "music.mp3").option("--no-wait", "Don't wait for generation to complete (Replicate async mode)").option("--dry-run", "Preview parameters without executing").action(async (prompt3, options) => {
|
|
31839
31839
|
const startedAt = Date.now();
|
|
31840
31840
|
try {
|
|
31841
31841
|
rejectControlChars(prompt3);
|
|
@@ -31913,7 +31913,7 @@ function registerMusicCommand(parent) {
|
|
|
31913
31913
|
if (options.melody) {
|
|
31914
31914
|
spinner2.text = "Uploading melody reference...";
|
|
31915
31915
|
const absPath = resolve17(process.cwd(), options.melody);
|
|
31916
|
-
if (!
|
|
31916
|
+
if (!existsSync11(absPath)) {
|
|
31917
31917
|
spinner2.fail(`Melody file not found: ${options.melody}`);
|
|
31918
31918
|
exitWithError(notFoundError(options.melody));
|
|
31919
31919
|
}
|
|
@@ -32004,7 +32004,7 @@ __export(ai_image_exports, {
|
|
|
32004
32004
|
});
|
|
32005
32005
|
import { resolve as resolve18, dirname as dirname5 } from "node:path";
|
|
32006
32006
|
import { readFile as readFile12, writeFile as writeFile17, mkdir as mkdir8 } from "node:fs/promises";
|
|
32007
|
-
import { existsSync as
|
|
32007
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
32008
32008
|
async function executeImageGenerate(options) {
|
|
32009
32009
|
const {
|
|
32010
32010
|
prompt: prompt3,
|
|
@@ -32148,7 +32148,7 @@ async function executeGeminiEdit(options) {
|
|
|
32148
32148
|
const imageBuffers = [];
|
|
32149
32149
|
for (const imagePath of imagePaths) {
|
|
32150
32150
|
const absPath = resolve18(process.cwd(), imagePath);
|
|
32151
|
-
if (!
|
|
32151
|
+
if (!existsSync12(absPath)) {
|
|
32152
32152
|
return { success: false, error: `Image not found: ${absPath}` };
|
|
32153
32153
|
}
|
|
32154
32154
|
const buffer = await readFile12(absPath);
|
|
@@ -32196,7 +32196,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
32196
32196
|
model = "flash",
|
|
32197
32197
|
apiKey
|
|
32198
32198
|
} = options;
|
|
32199
|
-
if (!
|
|
32199
|
+
if (!existsSync12(videoPath)) {
|
|
32200
32200
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
32201
32201
|
}
|
|
32202
32202
|
if (!commandExists("ffmpeg")) {
|
|
@@ -32236,7 +32236,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
32236
32236
|
reason = reasonMatch[1];
|
|
32237
32237
|
}
|
|
32238
32238
|
await execSafe("ffmpeg", ["-ss", String(timestamp), "-i", videoPath, "-frames:v", "1", "-q:v", "2", outputPath, "-y"], { timeout: 6e4, maxBuffer: 50 * 1024 * 1024 });
|
|
32239
|
-
if (!
|
|
32239
|
+
if (!existsSync12(outputPath)) {
|
|
32240
32240
|
return { success: false, error: "FFmpeg failed to extract frame" };
|
|
32241
32241
|
}
|
|
32242
32242
|
return {
|
|
@@ -32262,7 +32262,7 @@ var init_ai_image = __esm({
|
|
|
32262
32262
|
|
|
32263
32263
|
// src/commands/generate/thumbnail.ts
|
|
32264
32264
|
import { resolve as resolve19, dirname as dirname6, basename as basename3, extname as extname2 } from "node:path";
|
|
32265
|
-
import { existsSync as
|
|
32265
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
32266
32266
|
import { writeFile as writeFile18, mkdir as mkdir9 } from "node:fs/promises";
|
|
32267
32267
|
function registerThumbnailCommand(parent) {
|
|
32268
32268
|
parent.command("thumbnail").description("Generate video thumbnail (DALL-E) or extract best frame from video (Gemini)").argument("[description]", "Thumbnail description (for DALL-E generation)").option("-k, --api-key <key>", "API key (OpenAI for generation, Google for best-frame)").option("-o, --output <path>", "Output file path").option("--style <style>", "Platform style: youtube, instagram, tiktok, twitter").option("--best-frame <video>", "Extract best thumbnail frame from video using Gemini AI").option("--prompt <prompt>", "Custom prompt for best-frame analysis").option("--model <model>", "Gemini model: flash, latest, pro (default: flash)", "flash").action(async (description, options) => {
|
|
@@ -32274,7 +32274,7 @@ function registerThumbnailCommand(parent) {
|
|
|
32274
32274
|
}
|
|
32275
32275
|
if (options.bestFrame) {
|
|
32276
32276
|
const absVideoPath = resolve19(process.cwd(), options.bestFrame);
|
|
32277
|
-
if (!
|
|
32277
|
+
if (!existsSync13(absVideoPath)) {
|
|
32278
32278
|
exitWithError(notFoundError(absVideoPath));
|
|
32279
32279
|
}
|
|
32280
32280
|
if (!commandExists("ffmpeg")) {
|
|
@@ -32975,6 +32975,15 @@ Examples:
|
|
|
32975
32975
|
if (options.output) {
|
|
32976
32976
|
validateOutputPath(options.output);
|
|
32977
32977
|
}
|
|
32978
|
+
if (options.count !== void 0) {
|
|
32979
|
+
const n = parseInt(options.count, 10);
|
|
32980
|
+
if (!Number.isFinite(n) || n < 1 || n > 10) {
|
|
32981
|
+
exitWithError(usageError(
|
|
32982
|
+
`Invalid --count: ${options.count}`,
|
|
32983
|
+
"Must be an integer between 1 and 10."
|
|
32984
|
+
));
|
|
32985
|
+
}
|
|
32986
|
+
}
|
|
32978
32987
|
const imageRegistry = getProvidersFor("image");
|
|
32979
32988
|
const validProviders = [...imageRegistry.map((p) => p.name), "runway"];
|
|
32980
32989
|
const providerEnvMap = Object.fromEntries(
|
|
@@ -34654,7 +34663,7 @@ var init_video_providers = __esm({
|
|
|
34654
34663
|
// src/commands/ai-script-pipeline.ts
|
|
34655
34664
|
import { readFile as readFile13, writeFile as writeFile23, unlink as unlink2, rename as rename2 } from "node:fs/promises";
|
|
34656
34665
|
import { resolve as resolve24, extname as extname3 } from "node:path";
|
|
34657
|
-
import { existsSync as
|
|
34666
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
34658
34667
|
async function executeRegenerateScene(options) {
|
|
34659
34668
|
const result = {
|
|
34660
34669
|
success: false,
|
|
@@ -34663,12 +34672,12 @@ async function executeRegenerateScene(options) {
|
|
|
34663
34672
|
};
|
|
34664
34673
|
try {
|
|
34665
34674
|
const outputDir = resolve24(process.cwd(), options.projectDir);
|
|
34666
|
-
if (!
|
|
34675
|
+
if (!existsSync14(outputDir)) {
|
|
34667
34676
|
return { ...result, error: `Project directory not found: ${outputDir}` };
|
|
34668
34677
|
}
|
|
34669
34678
|
const yamlPath = resolve24(outputDir, "storyboard.yaml");
|
|
34670
34679
|
const jsonPath = resolve24(outputDir, "storyboard.json");
|
|
34671
|
-
const storyboardPath =
|
|
34680
|
+
const storyboardPath = existsSync14(yamlPath) ? yamlPath : existsSync14(jsonPath) ? jsonPath : null;
|
|
34672
34681
|
if (!storyboardPath) {
|
|
34673
34682
|
return { ...result, error: `Storyboard not found in: ${outputDir} (expected storyboard.yaml or storyboard.json)` };
|
|
34674
34683
|
}
|
|
@@ -34761,14 +34770,14 @@ IMPORTANT - Character appearance must match exactly: ${characterDesc}`;
|
|
|
34761
34770
|
const refSceneNum = options.referenceScene;
|
|
34762
34771
|
if (refSceneNum && refSceneNum >= 1 && refSceneNum <= segments.length && refSceneNum !== sceneNum) {
|
|
34763
34772
|
const refImagePath = resolve24(outputDir, `scene-${refSceneNum}.png`);
|
|
34764
|
-
if (
|
|
34773
|
+
if (existsSync14(refImagePath)) {
|
|
34765
34774
|
referenceImageBuffer = await readFile13(refImagePath);
|
|
34766
34775
|
}
|
|
34767
34776
|
} else if (!refSceneNum) {
|
|
34768
34777
|
for (let i = 1; i <= segments.length; i++) {
|
|
34769
34778
|
if (i !== sceneNum) {
|
|
34770
34779
|
const otherImagePath = resolve24(outputDir, `scene-${i}.png`);
|
|
34771
|
-
if (
|
|
34780
|
+
if (existsSync14(otherImagePath)) {
|
|
34772
34781
|
referenceImageBuffer = await readFile13(otherImagePath);
|
|
34773
34782
|
break;
|
|
34774
34783
|
}
|
|
@@ -34857,7 +34866,7 @@ Generate the single-person scene image now.`;
|
|
|
34857
34866
|
}
|
|
34858
34867
|
if (regenerateVideo && videoApiKey) {
|
|
34859
34868
|
options.onProgress?.(`Scene ${sceneNum}: regenerating video (${options.generator || "grok"})...`);
|
|
34860
|
-
if (!
|
|
34869
|
+
if (!existsSync14(imagePath)) {
|
|
34861
34870
|
result.failedScenes.push(sceneNum);
|
|
34862
34871
|
continue;
|
|
34863
34872
|
}
|
|
@@ -35119,6 +35128,15 @@ Examples:
|
|
|
35119
35128
|
if (options.output) {
|
|
35120
35129
|
validateOutputPath(options.output);
|
|
35121
35130
|
}
|
|
35131
|
+
if (options.duration !== void 0) {
|
|
35132
|
+
const d = parseFloat(options.duration);
|
|
35133
|
+
if (!Number.isFinite(d) || d <= 0 || d > 60) {
|
|
35134
|
+
exitWithError(usageError(
|
|
35135
|
+
`Invalid --duration: ${options.duration}`,
|
|
35136
|
+
"Must be a positive number \u2264 60 seconds."
|
|
35137
|
+
));
|
|
35138
|
+
}
|
|
35139
|
+
}
|
|
35122
35140
|
const validProviders = ["runway", "kling", "veo", "grok", "seedance", "fal"];
|
|
35123
35141
|
const videoEnvMap = {
|
|
35124
35142
|
grok: "XAI_API_KEY",
|
|
@@ -35631,7 +35649,7 @@ Run 'vibe schema generate.<command>' for structured parameter info.
|
|
|
35631
35649
|
});
|
|
35632
35650
|
|
|
35633
35651
|
// src/commands/_shared/edit/silence-cut.ts
|
|
35634
|
-
import { existsSync as
|
|
35652
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
35635
35653
|
import { readFile as readFile15 } from "node:fs/promises";
|
|
35636
35654
|
async function detectSilencePeriods(videoPath, noiseThreshold, minDuration) {
|
|
35637
35655
|
const totalDuration = await getVideoDuration(videoPath);
|
|
@@ -35763,7 +35781,7 @@ async function executeSilenceCut(options) {
|
|
|
35763
35781
|
analyzeOnly = false,
|
|
35764
35782
|
useGemini = false
|
|
35765
35783
|
} = options;
|
|
35766
|
-
if (!
|
|
35784
|
+
if (!existsSync15(videoPath)) {
|
|
35767
35785
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
35768
35786
|
}
|
|
35769
35787
|
if (!commandExists("ffmpeg")) {
|
|
@@ -35871,7 +35889,7 @@ var init_silence_cut = __esm({
|
|
|
35871
35889
|
});
|
|
35872
35890
|
|
|
35873
35891
|
// src/commands/_shared/edit/jump-cut.ts
|
|
35874
|
-
import { existsSync as
|
|
35892
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
35875
35893
|
import { readFile as readFile16, writeFile as writeFile25, mkdir as mkdir11 } from "node:fs/promises";
|
|
35876
35894
|
import { join as join8 } from "node:path";
|
|
35877
35895
|
async function transcribeWithWords(audioPath, apiKey, language) {
|
|
@@ -35934,7 +35952,7 @@ async function executeJumpCut(options) {
|
|
|
35934
35952
|
analyzeOnly = false,
|
|
35935
35953
|
apiKey
|
|
35936
35954
|
} = options;
|
|
35937
|
-
if (!
|
|
35955
|
+
if (!existsSync16(videoPath)) {
|
|
35938
35956
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
35939
35957
|
}
|
|
35940
35958
|
if (!commandExists("ffmpeg")) {
|
|
@@ -36220,7 +36238,7 @@ var init_helpers = __esm({
|
|
|
36220
36238
|
});
|
|
36221
36239
|
|
|
36222
36240
|
// src/commands/_shared/edit/caption.ts
|
|
36223
|
-
import { existsSync as
|
|
36241
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
36224
36242
|
import { readFile as readFile17, writeFile as writeFile26, mkdir as mkdir12 } from "node:fs/promises";
|
|
36225
36243
|
import { dirname as dirname8, basename as basename5, extname as extname4, join as join9 } from "node:path";
|
|
36226
36244
|
function getCaptionForceStyle(style, fontSize, fontColor, position) {
|
|
@@ -36250,7 +36268,7 @@ async function executeCaption(options) {
|
|
|
36250
36268
|
position = "bottom",
|
|
36251
36269
|
apiKey
|
|
36252
36270
|
} = options;
|
|
36253
|
-
if (!
|
|
36271
|
+
if (!existsSync17(videoPath)) {
|
|
36254
36272
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
36255
36273
|
}
|
|
36256
36274
|
if (!commandExists("ffmpeg")) {
|
|
@@ -36428,10 +36446,10 @@ var init_caption = __esm({
|
|
|
36428
36446
|
});
|
|
36429
36447
|
|
|
36430
36448
|
// src/commands/_shared/edit/noise-reduce.ts
|
|
36431
|
-
import { existsSync as
|
|
36449
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
36432
36450
|
async function executeNoiseReduce(options) {
|
|
36433
36451
|
const { inputPath, outputPath, strength = "medium", noiseFloor } = options;
|
|
36434
|
-
if (!
|
|
36452
|
+
if (!existsSync18(inputPath)) {
|
|
36435
36453
|
return { success: false, error: `File not found: ${inputPath}` };
|
|
36436
36454
|
}
|
|
36437
36455
|
if (!commandExists("ffmpeg")) {
|
|
@@ -36488,7 +36506,7 @@ var init_noise_reduce = __esm({
|
|
|
36488
36506
|
});
|
|
36489
36507
|
|
|
36490
36508
|
// src/commands/_shared/edit/fade.ts
|
|
36491
|
-
import { existsSync as
|
|
36509
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
36492
36510
|
async function executeFade(options) {
|
|
36493
36511
|
const {
|
|
36494
36512
|
videoPath,
|
|
@@ -36498,7 +36516,7 @@ async function executeFade(options) {
|
|
|
36498
36516
|
audioOnly = false,
|
|
36499
36517
|
videoOnly = false
|
|
36500
36518
|
} = options;
|
|
36501
|
-
if (!
|
|
36519
|
+
if (!existsSync19(videoPath)) {
|
|
36502
36520
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
36503
36521
|
}
|
|
36504
36522
|
if (!commandExists("ffmpeg")) {
|
|
@@ -36565,7 +36583,7 @@ var init_fade = __esm({
|
|
|
36565
36583
|
});
|
|
36566
36584
|
|
|
36567
36585
|
// src/commands/_shared/edit/translate-srt.ts
|
|
36568
|
-
import { existsSync as
|
|
36586
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
36569
36587
|
import { readFile as readFile18, writeFile as writeFile27 } from "node:fs/promises";
|
|
36570
36588
|
async function executeTranslateSrt(options) {
|
|
36571
36589
|
const {
|
|
@@ -36576,7 +36594,7 @@ async function executeTranslateSrt(options) {
|
|
|
36576
36594
|
sourceLanguage,
|
|
36577
36595
|
apiKey
|
|
36578
36596
|
} = options;
|
|
36579
|
-
if (!
|
|
36597
|
+
if (!existsSync20(srtPath)) {
|
|
36580
36598
|
return { success: false, error: `SRT file not found: ${srtPath}` };
|
|
36581
36599
|
}
|
|
36582
36600
|
try {
|
|
@@ -36693,7 +36711,7 @@ var init_translate_srt = __esm({
|
|
|
36693
36711
|
});
|
|
36694
36712
|
|
|
36695
36713
|
// src/commands/_shared/edit/text-overlay.ts
|
|
36696
|
-
import { existsSync as
|
|
36714
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
36697
36715
|
import { resolve as resolve26 } from "node:path";
|
|
36698
36716
|
function detectSystemFont() {
|
|
36699
36717
|
const platform = process.platform;
|
|
@@ -36704,7 +36722,7 @@ function detectSystemFont() {
|
|
|
36704
36722
|
"/Library/Fonts/Arial.ttf"
|
|
36705
36723
|
];
|
|
36706
36724
|
for (const f of candidates) {
|
|
36707
|
-
if (
|
|
36725
|
+
if (existsSync21(f)) return f;
|
|
36708
36726
|
}
|
|
36709
36727
|
} else if (platform === "linux") {
|
|
36710
36728
|
const candidates = [
|
|
@@ -36713,7 +36731,7 @@ function detectSystemFont() {
|
|
|
36713
36731
|
"/usr/share/fonts/TTF/DejaVuSans-Bold.ttf"
|
|
36714
36732
|
];
|
|
36715
36733
|
for (const f of candidates) {
|
|
36716
|
-
if (
|
|
36734
|
+
if (existsSync21(f)) return f;
|
|
36717
36735
|
}
|
|
36718
36736
|
} else if (platform === "win32") {
|
|
36719
36737
|
const candidates = [
|
|
@@ -36721,7 +36739,7 @@ function detectSystemFont() {
|
|
|
36721
36739
|
"C:\\Windows\\Fonts\\segoeui.ttf"
|
|
36722
36740
|
];
|
|
36723
36741
|
for (const f of candidates) {
|
|
36724
|
-
if (
|
|
36742
|
+
if (existsSync21(f)) return f;
|
|
36725
36743
|
}
|
|
36726
36744
|
}
|
|
36727
36745
|
return null;
|
|
@@ -36742,7 +36760,7 @@ async function applyTextOverlays(options) {
|
|
|
36742
36760
|
}
|
|
36743
36761
|
const absVideoPath = resolve26(process.cwd(), videoPath);
|
|
36744
36762
|
const absOutputPath = resolve26(process.cwd(), outputPath);
|
|
36745
|
-
if (!
|
|
36763
|
+
if (!existsSync21(absVideoPath)) {
|
|
36746
36764
|
return { success: false, error: `Video not found: ${absVideoPath}` };
|
|
36747
36765
|
}
|
|
36748
36766
|
if (!commandExists("ffmpeg")) {
|
|
@@ -36923,12 +36941,12 @@ var init_ai_edit = __esm({
|
|
|
36923
36941
|
|
|
36924
36942
|
// src/commands/ai-edit-cli.ts
|
|
36925
36943
|
import { resolve as resolve27, extname as extname5, basename as basename6 } from "node:path";
|
|
36926
|
-
import { existsSync as
|
|
36944
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
36927
36945
|
function registerEditCommands(aiCommand) {
|
|
36928
36946
|
aiCommand.command("silence-cut").alias("sc").description("Remove silent segments from video (FFmpeg default, or Gemini for smart detection)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path (default: <name>-cut.<ext>)").option("--noise <dB>", "Silence threshold in dB (default: -30)", "-30").option("-d, --min-duration <seconds>", "Minimum silence duration to cut (default: 0.5)", "0.5").option("--padding <seconds>", "Padding around non-silent segments (default: 0.1)", "0.1").option("--analyze-only", "Only detect silence, don't cut").option("--use-gemini", "Use Gemini Video Understanding for context-aware silence detection").option("-m, --model <model>", "Gemini model (default: flash)").option("--low-res", "Low resolution mode for longer videos (Gemini only)").option("-k, --api-key <key>", "Google API key override (or set GOOGLE_API_KEY env)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
|
|
36929
36947
|
Examples:
|
|
36930
36948
|
$ vibe edit silence-cut interview.mp4 -o clean.mp4
|
|
36931
|
-
$ vibe ed sc podcast.mp4 -o trimmed.mp4
|
|
36949
|
+
$ vibe ed sc podcast.mp4 -o trimmed.mp4 --noise -25 --min-duration 1.0
|
|
36932
36950
|
$ vibe ed sc video.mp4 --use-gemini -o smart-cut.mp4 # AI-powered detection
|
|
36933
36951
|
$ vibe ed sc video.mp4 --analyze-only # Detect only, no cut
|
|
36934
36952
|
$ vibe ed sc video.mp4 --dry-run --json
|
|
@@ -36940,7 +36958,7 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
|
|
|
36940
36958
|
validateOutputPath(options.output);
|
|
36941
36959
|
}
|
|
36942
36960
|
const absVideoPath = resolve27(process.cwd(), videoPath);
|
|
36943
|
-
if (!
|
|
36961
|
+
if (!existsSync22(absVideoPath)) {
|
|
36944
36962
|
exitWithError(notFoundError(absVideoPath));
|
|
36945
36963
|
}
|
|
36946
36964
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37029,9 +37047,9 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
|
|
|
37029
37047
|
aiCommand.command("caption").alias("cap").description("Transcribe and burn styled captions onto video (Whisper + FFmpeg)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path (default: <name>-captioned.<ext>)").option("--style <style>", "Caption style: minimal, bold, outline, karaoke (default: bold)", "bold").option("--font-size <pixels>", "Override auto-calculated font size").option("--color <color>", "Font color (default: white)", "white").option("-l, --language <lang>", "Language code for transcription (e.g., en, ko)").option("--position <pos>", "Caption position: top, center, bottom (default: bottom)", "bottom").option("-k, --api-key <key>", "OpenAI API key (or set OPENAI_API_KEY env)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
|
|
37030
37048
|
Examples:
|
|
37031
37049
|
$ vibe edit caption video.mp4 -o captioned.mp4
|
|
37032
|
-
$ vibe ed cap video.mp4 -o out.mp4
|
|
37050
|
+
$ vibe ed cap video.mp4 -o out.mp4 --style bold --position top
|
|
37033
37051
|
$ vibe ed cap video.mp4 -o out.mp4 -l ko # Korean transcription
|
|
37034
|
-
$ vibe ed cap video.mp4 -o out.mp4
|
|
37052
|
+
$ vibe ed cap video.mp4 -o out.mp4 --style karaoke # Karaoke-style
|
|
37035
37053
|
$ vibe ed cap video.mp4 --dry-run --json
|
|
37036
37054
|
|
|
37037
37055
|
Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoPath, options) => {
|
|
@@ -37041,7 +37059,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37041
37059
|
validateOutputPath(options.output);
|
|
37042
37060
|
}
|
|
37043
37061
|
const absVideoPath = resolve27(process.cwd(), videoPath);
|
|
37044
|
-
if (!
|
|
37062
|
+
if (!existsSync22(absVideoPath)) {
|
|
37045
37063
|
exitWithError(notFoundError(absVideoPath));
|
|
37046
37064
|
}
|
|
37047
37065
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37122,7 +37140,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37122
37140
|
validateOutputPath(options.output);
|
|
37123
37141
|
}
|
|
37124
37142
|
const absInputPath = resolve27(process.cwd(), inputPath);
|
|
37125
|
-
if (!
|
|
37143
|
+
if (!existsSync22(absInputPath)) {
|
|
37126
37144
|
exitWithError(notFoundError(absInputPath));
|
|
37127
37145
|
}
|
|
37128
37146
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37188,7 +37206,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37188
37206
|
validateOutputPath(options.output);
|
|
37189
37207
|
}
|
|
37190
37208
|
const absVideoPath = resolve27(process.cwd(), videoPath);
|
|
37191
|
-
if (!
|
|
37209
|
+
if (!existsSync22(absVideoPath)) {
|
|
37192
37210
|
exitWithError(notFoundError(absVideoPath));
|
|
37193
37211
|
}
|
|
37194
37212
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37263,7 +37281,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37263
37281
|
exitWithError(usageError("Target language required. Use -t or --target"));
|
|
37264
37282
|
}
|
|
37265
37283
|
const absSrtPath = resolve27(process.cwd(), srtPath);
|
|
37266
|
-
if (!
|
|
37284
|
+
if (!existsSync22(absSrtPath)) {
|
|
37267
37285
|
exitWithError(notFoundError(absSrtPath));
|
|
37268
37286
|
}
|
|
37269
37287
|
if (options.dryRun) {
|
|
@@ -37339,7 +37357,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37339
37357
|
}
|
|
37340
37358
|
if (options.fillers) rejectControlChars(options.fillers);
|
|
37341
37359
|
const absVideoPath = resolve27(process.cwd(), videoPath);
|
|
37342
|
-
if (!
|
|
37360
|
+
if (!existsSync22(absVideoPath)) {
|
|
37343
37361
|
exitWithError(notFoundError(absVideoPath));
|
|
37344
37362
|
}
|
|
37345
37363
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37439,7 +37457,7 @@ var init_ai_edit_cli = __esm({
|
|
|
37439
37457
|
});
|
|
37440
37458
|
|
|
37441
37459
|
// src/commands/_shared/execute-fill-gaps.ts
|
|
37442
|
-
import { existsSync as
|
|
37460
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
37443
37461
|
import { readFile as readFile19, writeFile as writeFile28, mkdir as mkdir13, rename as renameFs } from "node:fs/promises";
|
|
37444
37462
|
import { resolve as resolve28, dirname as dirname9 } from "node:path";
|
|
37445
37463
|
function detectVideoGaps(videoClips, totalDuration) {
|
|
@@ -37522,7 +37540,7 @@ async function executeFillGaps(options) {
|
|
|
37522
37540
|
try {
|
|
37523
37541
|
onProgress("Loading project...");
|
|
37524
37542
|
const filePath = resolve28(process.cwd(), options.projectPath);
|
|
37525
|
-
if (!
|
|
37543
|
+
if (!existsSync23(filePath)) {
|
|
37526
37544
|
return {
|
|
37527
37545
|
success: false,
|
|
37528
37546
|
error: `Project file not found: ${filePath}`,
|
|
@@ -37657,7 +37675,7 @@ async function executeFillGaps(options) {
|
|
|
37657
37675
|
}
|
|
37658
37676
|
const projectDir = dirname9(filePath);
|
|
37659
37677
|
const footageDir = options.dir ? resolve28(process.cwd(), options.dir) : resolve28(projectDir, "footage");
|
|
37660
|
-
if (!
|
|
37678
|
+
if (!existsSync23(footageDir)) {
|
|
37661
37679
|
await mkdir13(footageDir, { recursive: true });
|
|
37662
37680
|
}
|
|
37663
37681
|
humanLines.push("Generating AI Videos");
|
|
@@ -38178,12 +38196,12 @@ var init_edit_cmd = __esm({
|
|
|
38178
38196
|
`
|
|
38179
38197
|
Examples:
|
|
38180
38198
|
$ vibe edit silence-cut interview.mp4 -o clean.mp4
|
|
38181
|
-
$ vibe edit caption video.mp4 -o captioned.mp4
|
|
38199
|
+
$ vibe edit caption video.mp4 -o captioned.mp4 --style bold
|
|
38182
38200
|
$ vibe edit grade video.mp4 -o graded.mp4 --preset cinematic-warm
|
|
38183
38201
|
$ vibe edit reframe landscape.mp4 -o vertical.mp4 -a 9:16
|
|
38184
38202
|
$ vibe edit image photo.png "add sunset background" -o edited.png
|
|
38185
|
-
$ vibe edit text-overlay video.mp4
|
|
38186
|
-
$ vibe edit noise-reduce noisy.mp4 -o clean.mp4
|
|
38203
|
+
$ vibe edit text-overlay video.mp4 --text "Title" --style center-bold -o out.mp4
|
|
38204
|
+
$ vibe edit noise-reduce noisy.mp4 -o clean.mp4 --strength high
|
|
38187
38205
|
$ vibe edit fade video.mp4 -o faded.mp4 --fade-in 1 --fade-out 1
|
|
38188
38206
|
|
|
38189
38207
|
API Keys (varies by subcommand):
|
|
@@ -38930,7 +38948,7 @@ __export(ai_analyze_exports, {
|
|
|
38930
38948
|
});
|
|
38931
38949
|
import { readFile as readFile21 } from "node:fs/promises";
|
|
38932
38950
|
import { extname as extname6, resolve as resolve30 } from "node:path";
|
|
38933
|
-
import { existsSync as
|
|
38951
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
38934
38952
|
async function executeGeminiVideo(options) {
|
|
38935
38953
|
try {
|
|
38936
38954
|
const apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
|
|
@@ -38949,7 +38967,7 @@ async function executeGeminiVideo(options) {
|
|
|
38949
38967
|
videoData = options.source;
|
|
38950
38968
|
} else {
|
|
38951
38969
|
const absPath = resolve30(process.cwd(), options.source);
|
|
38952
|
-
if (!
|
|
38970
|
+
if (!existsSync24(absPath)) {
|
|
38953
38971
|
return { success: false, error: `File not found: ${absPath}` };
|
|
38954
38972
|
}
|
|
38955
38973
|
videoData = await readFile21(absPath);
|
|
@@ -39022,7 +39040,7 @@ async function executeAnalyze(options) {
|
|
|
39022
39040
|
imageBuffer = Buffer.from(await response.arrayBuffer());
|
|
39023
39041
|
} else {
|
|
39024
39042
|
const absPath = resolve30(process.cwd(), source);
|
|
39025
|
-
if (!
|
|
39043
|
+
if (!existsSync24(absPath)) {
|
|
39026
39044
|
return { success: false, error: `File not found: ${absPath}` };
|
|
39027
39045
|
}
|
|
39028
39046
|
imageBuffer = await readFile21(absPath);
|
|
@@ -39057,7 +39075,7 @@ async function executeAnalyze(options) {
|
|
|
39057
39075
|
videoData = Buffer.from(await response.arrayBuffer());
|
|
39058
39076
|
} else {
|
|
39059
39077
|
const absPath = resolve30(process.cwd(), source);
|
|
39060
|
-
if (!
|
|
39078
|
+
if (!existsSync24(absPath)) {
|
|
39061
39079
|
return { success: false, error: `File not found: ${absPath}` };
|
|
39062
39080
|
}
|
|
39063
39081
|
videoData = await readFile21(absPath);
|
|
@@ -39196,7 +39214,7 @@ __export(ai_review_exports, {
|
|
|
39196
39214
|
});
|
|
39197
39215
|
import { readFile as readFile22, rename as rename3 } from "node:fs/promises";
|
|
39198
39216
|
import { resolve as resolve31 } from "node:path";
|
|
39199
|
-
import { existsSync as
|
|
39217
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
39200
39218
|
function parseReviewFeedback(response) {
|
|
39201
39219
|
let cleaned = response.trim();
|
|
39202
39220
|
if (cleaned.startsWith("```json")) {
|
|
@@ -39221,7 +39239,7 @@ function parseReviewFeedback(response) {
|
|
|
39221
39239
|
async function executeReview(options) {
|
|
39222
39240
|
const { videoPath, storyboardPath, autoApply = false, verify = false, model = "flash" } = options;
|
|
39223
39241
|
const absVideoPath = resolve31(process.cwd(), videoPath);
|
|
39224
|
-
if (!
|
|
39242
|
+
if (!existsSync25(absVideoPath)) {
|
|
39225
39243
|
return { success: false, error: `Video not found: ${absVideoPath}` };
|
|
39226
39244
|
}
|
|
39227
39245
|
const apiKey = process.env.GOOGLE_API_KEY || await getApiKey("GOOGLE_API_KEY", "Google");
|
|
@@ -39231,7 +39249,7 @@ async function executeReview(options) {
|
|
|
39231
39249
|
let storyboardContext = "";
|
|
39232
39250
|
if (storyboardPath) {
|
|
39233
39251
|
const absStoryboardPath = resolve31(process.cwd(), storyboardPath);
|
|
39234
|
-
if (
|
|
39252
|
+
if (existsSync25(absStoryboardPath)) {
|
|
39235
39253
|
const content = await readFile22(absStoryboardPath, "utf-8");
|
|
39236
39254
|
storyboardContext = `
|
|
39237
39255
|
|
|
@@ -39437,7 +39455,7 @@ var init_ai_review = __esm({
|
|
|
39437
39455
|
// src/commands/ai-highlights.ts
|
|
39438
39456
|
import { readFile as readFile26, writeFile as writeFile33, mkdir as mkdir15 } from "node:fs/promises";
|
|
39439
39457
|
import { resolve as resolve35, dirname as dirname12, basename as basename8, extname as extname8 } from "node:path";
|
|
39440
|
-
import { existsSync as
|
|
39458
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
39441
39459
|
function filterHighlights(highlights, options) {
|
|
39442
39460
|
let filtered = highlights.filter((h) => h.confidence >= options.threshold);
|
|
39443
39461
|
filtered.sort((a, b) => b.confidence - a.confidence);
|
|
@@ -39477,7 +39495,7 @@ function truncate(text, maxLength) {
|
|
|
39477
39495
|
async function executeHighlights(options) {
|
|
39478
39496
|
try {
|
|
39479
39497
|
const absPath = resolve35(process.cwd(), options.media);
|
|
39480
|
-
if (!
|
|
39498
|
+
if (!existsSync28(absPath)) {
|
|
39481
39499
|
return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: `File not found: ${absPath}` };
|
|
39482
39500
|
}
|
|
39483
39501
|
const ext = extname8(absPath).toLowerCase();
|
|
@@ -39593,7 +39611,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
39593
39611
|
const audioBuffer = await readFile26(audioPath);
|
|
39594
39612
|
const audioBlob = new Blob([audioBuffer]);
|
|
39595
39613
|
const transcriptResult = await whisper.transcribe(audioBlob, options.language);
|
|
39596
|
-
if (tempAudioPath &&
|
|
39614
|
+
if (tempAudioPath && existsSync28(tempAudioPath)) {
|
|
39597
39615
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
39598
39616
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
39599
39617
|
});
|
|
@@ -39680,7 +39698,7 @@ async function executeAutoShorts(options) {
|
|
|
39680
39698
|
return { success: false, shorts: [], error: "FFmpeg not found" };
|
|
39681
39699
|
}
|
|
39682
39700
|
const absPath = resolve35(process.cwd(), options.video);
|
|
39683
|
-
if (!
|
|
39701
|
+
if (!existsSync28(absPath)) {
|
|
39684
39702
|
return { success: false, shorts: [], error: `File not found: ${absPath}` };
|
|
39685
39703
|
}
|
|
39686
39704
|
const targetDuration = options.duration ?? 60;
|
|
@@ -39808,7 +39826,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
39808
39826
|
};
|
|
39809
39827
|
}
|
|
39810
39828
|
const outputDir = options.outputDir ? resolve35(process.cwd(), options.outputDir) : dirname12(absPath);
|
|
39811
|
-
if (options.outputDir && !
|
|
39829
|
+
if (options.outputDir && !existsSync28(outputDir)) {
|
|
39812
39830
|
await mkdir15(outputDir, { recursive: true });
|
|
39813
39831
|
}
|
|
39814
39832
|
const result = {
|
|
@@ -39891,7 +39909,7 @@ function registerHighlightsCommands(aiCommand) {
|
|
|
39891
39909
|
validateOutputPath(options.output);
|
|
39892
39910
|
}
|
|
39893
39911
|
const absPath = resolve35(process.cwd(), mediaPath);
|
|
39894
|
-
if (!
|
|
39912
|
+
if (!existsSync28(absPath)) {
|
|
39895
39913
|
exitWithError(notFoundError(absPath));
|
|
39896
39914
|
}
|
|
39897
39915
|
if (options.dryRun) {
|
|
@@ -40078,7 +40096,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
40078
40096
|
const transcriptResult = await whisper.transcribe(audioBlob, options.language);
|
|
40079
40097
|
if (transcriptResult.status === "failed" || !transcriptResult.segments) {
|
|
40080
40098
|
transcribeSpinner.fail("Transcription failed");
|
|
40081
|
-
if (tempAudioPath &&
|
|
40099
|
+
if (tempAudioPath && existsSync28(tempAudioPath)) {
|
|
40082
40100
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
40083
40101
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
40084
40102
|
});
|
|
@@ -40086,7 +40104,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
40086
40104
|
exitWithError(apiError(`Transcription failed: ${transcriptResult.error}`, true));
|
|
40087
40105
|
}
|
|
40088
40106
|
transcribeSpinner.succeed(source_default.green(`Transcribed ${transcriptResult.segments.length} segments`));
|
|
40089
|
-
if (tempAudioPath &&
|
|
40107
|
+
if (tempAudioPath && existsSync28(tempAudioPath)) {
|
|
40090
40108
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
40091
40109
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
40092
40110
|
});
|
|
@@ -40192,7 +40210,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
40192
40210
|
validateOutputPath(options.output);
|
|
40193
40211
|
}
|
|
40194
40212
|
const absPath = resolve35(process.cwd(), videoPath);
|
|
40195
|
-
if (!
|
|
40213
|
+
if (!existsSync28(absPath)) {
|
|
40196
40214
|
exitWithError(notFoundError(absPath));
|
|
40197
40215
|
}
|
|
40198
40216
|
if (options.dryRun) {
|
|
@@ -40385,7 +40403,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
40385
40403
|
return;
|
|
40386
40404
|
}
|
|
40387
40405
|
const outputDir = options.outputDir ? resolve35(process.cwd(), options.outputDir) : dirname12(absPath);
|
|
40388
|
-
if (options.outputDir && !
|
|
40406
|
+
if (options.outputDir && !existsSync28(outputDir)) {
|
|
40389
40407
|
await mkdir15(outputDir, { recursive: true });
|
|
40390
40408
|
}
|
|
40391
40409
|
for (let i = 0; i < selectedHighlights.length; i++) {
|
|
@@ -40402,7 +40420,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
40402
40420
|
outputPath = resolve35(outputDir, `${baseName}-short-${i + 1}.mp4`);
|
|
40403
40421
|
}
|
|
40404
40422
|
const parentDir = dirname12(outputPath);
|
|
40405
|
-
if (!
|
|
40423
|
+
if (!existsSync28(parentDir)) {
|
|
40406
40424
|
await mkdir15(parentDir, { recursive: true });
|
|
40407
40425
|
}
|
|
40408
40426
|
const { stdout: probeOut } = await execSafe("ffprobe", [
|
|
@@ -40477,7 +40495,7 @@ var init_ai_highlights = __esm({
|
|
|
40477
40495
|
// src/commands/ai-animated-caption.ts
|
|
40478
40496
|
import { resolve as resolve36, dirname as dirname13, basename as basename9 } from "node:path";
|
|
40479
40497
|
import { writeFile as writeFile34, mkdir as mkdir16, rm as rm2 } from "node:fs/promises";
|
|
40480
|
-
import { existsSync as
|
|
40498
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
40481
40499
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
40482
40500
|
function groupWords(words, options = {}) {
|
|
40483
40501
|
if (words.length === 0) return [];
|
|
@@ -40654,7 +40672,7 @@ async function executeAnimatedCaption(options) {
|
|
|
40654
40672
|
const groups = groupWords(transcript.words, { wordsPerGroup, maxChars });
|
|
40655
40673
|
const absOutputPath = resolve36(process.cwd(), outputPath);
|
|
40656
40674
|
const outDir = dirname13(absOutputPath);
|
|
40657
|
-
if (!
|
|
40675
|
+
if (!existsSync29(outDir)) {
|
|
40658
40676
|
await mkdir16(outDir, { recursive: true });
|
|
40659
40677
|
}
|
|
40660
40678
|
if (tier === "ass") {
|
|
@@ -50603,7 +50621,7 @@ var init_detect = __esm({
|
|
|
50603
50621
|
});
|
|
50604
50622
|
|
|
50605
50623
|
// src/pipeline/renderers/chrome.ts
|
|
50606
|
-
import { existsSync as
|
|
50624
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
50607
50625
|
import { homedir as homedir5 } from "node:os";
|
|
50608
50626
|
import { join as join11 } from "node:path";
|
|
50609
50627
|
function findChrome() {
|
|
@@ -50629,7 +50647,7 @@ function findChrome() {
|
|
|
50629
50647
|
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
|
|
50630
50648
|
];
|
|
50631
50649
|
for (const c of candidates) {
|
|
50632
|
-
if (c &&
|
|
50650
|
+
if (c && existsSync32(c)) return c;
|
|
50633
50651
|
}
|
|
50634
50652
|
return void 0;
|
|
50635
50653
|
}
|
|
@@ -50884,7 +50902,7 @@ __export(scene_render_exports, {
|
|
|
50884
50902
|
qualityToCrf: () => qualityToCrf
|
|
50885
50903
|
});
|
|
50886
50904
|
import { mkdir as mkdir18, readFile as readFile32, stat as stat4 } from "node:fs/promises";
|
|
50887
|
-
import { existsSync as
|
|
50905
|
+
import { existsSync as existsSync33 } from "node:fs";
|
|
50888
50906
|
import { resolve as resolve45, relative as relative6, dirname as dirname16, basename as basename16 } from "node:path";
|
|
50889
50907
|
import {
|
|
50890
50908
|
createRenderJob,
|
|
@@ -50902,7 +50920,7 @@ function defaultOutputPath(opts) {
|
|
|
50902
50920
|
}
|
|
50903
50921
|
async function readProjectName(projectDir) {
|
|
50904
50922
|
const cfgPath = resolve45(projectDir, "vibe.project.yaml");
|
|
50905
|
-
if (!
|
|
50923
|
+
if (!existsSync33(cfgPath)) return void 0;
|
|
50906
50924
|
try {
|
|
50907
50925
|
const raw = await (await import("node:fs/promises")).readFile(cfgPath, "utf-8");
|
|
50908
50926
|
const parsed = (0, import_yaml7.parse)(raw);
|
|
@@ -51032,7 +51050,7 @@ __export(scene_build_exports, {
|
|
|
51032
51050
|
executeSceneBuild: () => executeSceneBuild,
|
|
51033
51051
|
resolveSceneBuildMode: () => resolveSceneBuildMode
|
|
51034
51052
|
});
|
|
51035
|
-
import { existsSync as
|
|
51053
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
51036
51054
|
import { mkdir as mkdir19, readFile as readFile33, writeFile as writeFile38 } from "node:fs/promises";
|
|
51037
51055
|
import { dirname as dirname17, join as join12, resolve as resolve46 } from "node:path";
|
|
51038
51056
|
async function executeSceneBuild(opts) {
|
|
@@ -51042,7 +51060,7 @@ async function executeSceneBuild(opts) {
|
|
|
51042
51060
|
});
|
|
51043
51061
|
const mode = resolveSceneBuildMode(opts);
|
|
51044
51062
|
const storyboardPath = join12(projectDir, "STORYBOARD.md");
|
|
51045
|
-
if (!
|
|
51063
|
+
if (!existsSync34(storyboardPath)) {
|
|
51046
51064
|
return failBeforePrimitives(
|
|
51047
51065
|
`STORYBOARD.md not found at ${storyboardPath}. Run \`vibe scene init <dir>\` to create a starter, or add STORYBOARD.md with per-beat cues.`,
|
|
51048
51066
|
startedAt
|
|
@@ -51078,7 +51096,7 @@ async function executeSceneBuild(opts) {
|
|
|
51078
51096
|
if (mode === "agent") {
|
|
51079
51097
|
const compositionsDir = join12(projectDir, "compositions");
|
|
51080
51098
|
const missingBeats = parsed.beats.filter(
|
|
51081
|
-
(b) => !
|
|
51099
|
+
(b) => !existsSync34(join12(compositionsDir, `scene-${b.id}.html`))
|
|
51082
51100
|
);
|
|
51083
51101
|
if (missingBeats.length > 0) {
|
|
51084
51102
|
const plan = await getComposePrompts({ projectDir });
|
|
@@ -51126,7 +51144,7 @@ async function executeSceneBuild(opts) {
|
|
|
51126
51144
|
}
|
|
51127
51145
|
composeData = composeResult.data;
|
|
51128
51146
|
}
|
|
51129
|
-
if (!
|
|
51147
|
+
if (!existsSync34(join12(projectDir, "index.html"))) {
|
|
51130
51148
|
await scaffoldSceneProject({
|
|
51131
51149
|
dir: projectDir,
|
|
51132
51150
|
name: projectDir.split(/[\\/]/).filter(Boolean).pop(),
|
|
@@ -51186,7 +51204,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
51186
51204
|
if (!text) return { status: "no-cue" };
|
|
51187
51205
|
for (const ext of ["mp3", "wav"]) {
|
|
51188
51206
|
const rel2 = `assets/narration-${beat.id}.${ext}`;
|
|
51189
|
-
if (
|
|
51207
|
+
if (existsSync34(join12(ctx.projectDir, rel2)) && !ctx.force) {
|
|
51190
51208
|
ctx.onProgress({ type: "narration-cached", beatId: beat.id, path: rel2 });
|
|
51191
51209
|
return { status: "cached", path: rel2 };
|
|
51192
51210
|
}
|
|
@@ -51227,7 +51245,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
51227
51245
|
}
|
|
51228
51246
|
const rel = `assets/backdrop-${beat.id}.png`;
|
|
51229
51247
|
const abs = join12(ctx.projectDir, rel);
|
|
51230
|
-
if (
|
|
51248
|
+
if (existsSync34(abs) && !ctx.force) {
|
|
51231
51249
|
ctx.onProgress({ type: "backdrop-cached", beatId: beat.id, path: rel });
|
|
51232
51250
|
return { status: "cached", path: rel };
|
|
51233
51251
|
}
|
|
@@ -51265,7 +51283,7 @@ function loadSceneBuildEnv(projectDir) {
|
|
|
51265
51283
|
(0, import_dotenv3.config)({ path: resolve46(process.cwd(), ".env"), quiet: true });
|
|
51266
51284
|
let dir = process.cwd();
|
|
51267
51285
|
while (dir !== dirname17(dir)) {
|
|
51268
|
-
if (
|
|
51286
|
+
if (existsSync34(join12(dir, "pnpm-workspace.yaml"))) {
|
|
51269
51287
|
(0, import_dotenv3.config)({ path: join12(dir, ".env"), quiet: true });
|
|
51270
51288
|
return;
|
|
51271
51289
|
}
|
|
@@ -51296,7 +51314,7 @@ function resolveSceneBuildMode(opts) {
|
|
|
51296
51314
|
}
|
|
51297
51315
|
async function syncRootClipReferences(beats, projectDir, outcomes) {
|
|
51298
51316
|
const rootPath = join12(projectDir, "index.html");
|
|
51299
|
-
if (!
|
|
51317
|
+
if (!existsSync34(rootPath)) return;
|
|
51300
51318
|
const html = await readFile33(rootPath, "utf-8");
|
|
51301
51319
|
let cursor = 0;
|
|
51302
51320
|
const clipLines = [];
|
|
@@ -51747,14 +51765,14 @@ __export(ai_audio_exports, {
|
|
|
51747
51765
|
});
|
|
51748
51766
|
import { resolve as resolve53, dirname as dirname19, basename as basename17, extname as extname12 } from "node:path";
|
|
51749
51767
|
import { readFile as readFile36, writeFile as writeFile40 } from "node:fs/promises";
|
|
51750
|
-
import { existsSync as
|
|
51768
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
51751
51769
|
async function executeTranscribe(options) {
|
|
51752
51770
|
const { audioPath, language, output, format, apiKey } = options;
|
|
51753
51771
|
try {
|
|
51754
51772
|
const key = apiKey || process.env.OPENAI_API_KEY;
|
|
51755
51773
|
if (!key) return { success: false, error: "OPENAI_API_KEY required" };
|
|
51756
51774
|
const absPath = resolve53(process.cwd(), audioPath);
|
|
51757
|
-
if (!
|
|
51775
|
+
if (!existsSync37(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
51758
51776
|
const whisper = new WhisperProvider();
|
|
51759
51777
|
await whisper.initialize({ apiKey: key });
|
|
51760
51778
|
const audioBuffer = await readFile36(absPath);
|
|
@@ -51787,7 +51805,7 @@ async function executeIsolate(options) {
|
|
|
51787
51805
|
const key = apiKey || process.env.ELEVENLABS_API_KEY;
|
|
51788
51806
|
if (!key) return { success: false, error: "ELEVENLABS_API_KEY required" };
|
|
51789
51807
|
const absPath = resolve53(process.cwd(), audioPath);
|
|
51790
|
-
if (!
|
|
51808
|
+
if (!existsSync37(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
51791
51809
|
const audioBuffer = await readFile36(absPath);
|
|
51792
51810
|
const elevenlabs = new ElevenLabsProvider();
|
|
51793
51811
|
await elevenlabs.initialize({ apiKey: key });
|
|
@@ -51813,7 +51831,7 @@ async function executeVoiceClone(options) {
|
|
|
51813
51831
|
const audioBuffers = [];
|
|
51814
51832
|
for (const samplePath of samplePaths) {
|
|
51815
51833
|
const absPath = resolve53(process.cwd(), samplePath);
|
|
51816
|
-
if (!
|
|
51834
|
+
if (!existsSync37(absPath)) return { success: false, error: `File not found: ${samplePath}` };
|
|
51817
51835
|
const buffer = await readFile36(absPath);
|
|
51818
51836
|
audioBuffers.push(buffer);
|
|
51819
51837
|
}
|
|
@@ -51835,7 +51853,7 @@ async function executeDub(options) {
|
|
|
51835
51853
|
const { mediaPath, language, source, voice, analyzeOnly, output } = options;
|
|
51836
51854
|
try {
|
|
51837
51855
|
const absPath = resolve53(process.cwd(), mediaPath);
|
|
51838
|
-
if (!
|
|
51856
|
+
if (!existsSync37(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
51839
51857
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
51840
51858
|
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
51841
51859
|
const elevenlabsKey = process.env.ELEVENLABS_API_KEY;
|
|
@@ -51963,8 +51981,8 @@ async function executeDuck(options) {
|
|
|
51963
51981
|
if (!commandExists("ffmpeg")) return { success: false, error: "FFmpeg not found" };
|
|
51964
51982
|
const absMusicPath = resolve53(process.cwd(), musicPath);
|
|
51965
51983
|
const absVoicePath = resolve53(process.cwd(), voicePath);
|
|
51966
|
-
if (!
|
|
51967
|
-
if (!
|
|
51984
|
+
if (!existsSync37(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
|
|
51985
|
+
if (!existsSync37(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
|
|
51968
51986
|
const defaultOutput = resolve53(dirname19(absMusicPath), `${basename17(absMusicPath, extname12(absMusicPath))}-ducked${extname12(absMusicPath)}`);
|
|
51969
51987
|
const outputPath = resolve53(process.cwd(), output || defaultOutput);
|
|
51970
51988
|
const filter = `[1:a]asplit=2[sc][mix];[0:a][sc]sidechaincompress=threshold=${threshold}dB:ratio=${ratio}:attack=${attack}:release=${release}[ducked];[ducked][mix]amix=inputs=2:duration=longest`;
|
|
@@ -51978,7 +51996,7 @@ async function executeDuck(options) {
|
|
|
51978
51996
|
"-y",
|
|
51979
51997
|
outputPath
|
|
51980
51998
|
], { timeout: 12e4, maxBuffer: 50 * 1024 * 1024 });
|
|
51981
|
-
if (!
|
|
51999
|
+
if (!existsSync37(outputPath)) return { success: false, error: "FFmpeg failed to create output" };
|
|
51982
52000
|
return { success: true, outputPath };
|
|
51983
52001
|
} catch (error) {
|
|
51984
52002
|
return { success: false, error: `Audio ducking failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
@@ -51996,7 +52014,7 @@ var init_ai_audio = __esm({
|
|
|
51996
52014
|
// src/pipeline/executor.ts
|
|
51997
52015
|
import { resolve as resolve54 } from "node:path";
|
|
51998
52016
|
import { readFile as readFile37, writeFile as writeFile41, mkdir as mkdir21 } from "node:fs/promises";
|
|
51999
|
-
import { existsSync as
|
|
52017
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
52000
52018
|
function maxCostFor(action) {
|
|
52001
52019
|
const cmd = ACTION_METADATA[action]?.command;
|
|
52002
52020
|
if (!cmd) return 0;
|
|
@@ -52213,7 +52231,7 @@ async function ensureActionsRegistered() {
|
|
|
52213
52231
|
}
|
|
52214
52232
|
async function loadPipeline(filePath) {
|
|
52215
52233
|
const absPath = resolve54(process.cwd(), filePath);
|
|
52216
|
-
if (!
|
|
52234
|
+
if (!existsSync38(absPath)) {
|
|
52217
52235
|
throw new Error(`Pipeline file not found: ${absPath}`);
|
|
52218
52236
|
}
|
|
52219
52237
|
const content = await readFile37(absPath, "utf-8");
|
|
@@ -52238,7 +52256,7 @@ async function executePipeline(manifest2, options = {}) {
|
|
|
52238
52256
|
const startTime = Date.now();
|
|
52239
52257
|
if (options.resume) {
|
|
52240
52258
|
const checkpointPath = resolve54(outputDir, CHECKPOINT_FILE);
|
|
52241
|
-
if (
|
|
52259
|
+
if (existsSync38(checkpointPath)) {
|
|
52242
52260
|
const checkpointContent = await readFile37(checkpointPath, "utf-8");
|
|
52243
52261
|
const checkpoint = (0, import_yaml8.parse)(checkpointContent);
|
|
52244
52262
|
for (const cs of checkpoint.completedSteps) {
|
|
@@ -59644,7 +59662,7 @@ __export(project_builder_exports, {
|
|
|
59644
59662
|
resolveSourceUrl: () => resolveSourceUrl
|
|
59645
59663
|
});
|
|
59646
59664
|
import { mkdtemp, mkdir as mkdir22, copyFile as copyFile3, writeFile as writeFile46, rm as rm4, readdir as readdir3 } from "node:fs/promises";
|
|
59647
|
-
import { existsSync as
|
|
59665
|
+
import { existsSync as existsSync39 } from "node:fs";
|
|
59648
59666
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
59649
59667
|
import { createRequire } from "node:module";
|
|
59650
59668
|
import * as path from "node:path";
|
|
@@ -59656,7 +59674,7 @@ async function buildTempProject(state, projectFileDir) {
|
|
|
59656
59674
|
for (const source of state.sources) {
|
|
59657
59675
|
if (copied.has(source.id)) continue;
|
|
59658
59676
|
const resolved = resolveSourceUrl(source.url, projectFileDir);
|
|
59659
|
-
if (!
|
|
59677
|
+
if (!existsSync39(resolved)) {
|
|
59660
59678
|
throw new Error(`Source file not found: ${resolved} (from source id=${source.id})`);
|
|
59661
59679
|
}
|
|
59662
59680
|
const dest = path.join(assetsDir, path.basename(resolved));
|
|
@@ -60494,11 +60512,11 @@ var init_export = __esm({
|
|
|
60494
60512
|
).option("--overwrite", "Overwrite output file if exists", false).option("--gap-fill <strategy>", "Gap filling strategy (black, extend)", "extend").option("--backend <name>", "Render backend: ffmpeg (default) | hyperframes (experimental)", "ffmpeg").option("--bitrate <value>", "Video bitrate (e.g. 5000k, 8M) \u2014 overrides preset").option("--fps <number>", "Frames per second (e.g. 24, 30, 60) \u2014 overrides preset").option("--resolution <WxH>", "Output resolution (e.g. 1920x1080) \u2014 overrides preset").option("--codec <codec>", "Video codec: h264 (default) | h265 | vp9 \u2014 overrides preset").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
|
|
60495
60513
|
Examples:
|
|
60496
60514
|
$ vibe export project.vibe.json -o output.mp4
|
|
60497
|
-
$ vibe export project.vibe.json -o output.mp4
|
|
60498
|
-
$ vibe export project.vibe.json -o output.webm
|
|
60499
|
-
$ vibe export project.vibe.json -o output.gif
|
|
60515
|
+
$ vibe export project.vibe.json -o output.mp4 --preset high --overwrite
|
|
60516
|
+
$ vibe export project.vibe.json -o output.webm --format webm
|
|
60517
|
+
$ vibe export project.vibe.json -o output.gif --format gif
|
|
60500
60518
|
$ vibe export project.vibe.json -o out.mp4 --bitrate 5000k --fps 24 --codec h265
|
|
60501
|
-
$ vibe export project.vibe.json -o out.mp4
|
|
60519
|
+
$ vibe export project.vibe.json -o out.mp4 --preset high --fps 60
|
|
60502
60520
|
|
|
60503
60521
|
Cost: Free (no API keys needed). Requires FFmpeg.
|
|
60504
60522
|
GIF format: 15fps, no audio, looping. Good for previews and sharing.
|
|
@@ -61634,28 +61652,31 @@ narration / backdrop intent.
|
|
|
61634
61652
|
## Quick-draft path
|
|
61635
61653
|
|
|
61636
61654
|
\`\`\`bash
|
|
61637
|
-
vibe
|
|
61655
|
+
vibe init my-promo -r 16:9 -d 30
|
|
61638
61656
|
vibe scene add intro --style announcement \\
|
|
61639
61657
|
--headline "Ship videos, not clicks"
|
|
61640
61658
|
vibe scene lint
|
|
61641
61659
|
vibe render my-promo
|
|
61642
61660
|
\`\`\`
|
|
61643
61661
|
|
|
61644
|
-
\`vibe
|
|
61645
|
-
|
|
61646
|
-
|
|
61662
|
+
\`vibe init\` is **idempotent** \u2014 running it on an existing Hyperframes
|
|
61663
|
+
directory merges \`hyperframes.json\` instead of clobbering it. Safe to
|
|
61664
|
+
invoke on user-provided projects.
|
|
61647
61665
|
|
|
61648
61666
|
## Subcommands
|
|
61649
61667
|
|
|
61650
61668
|
\`\`\`bash
|
|
61651
|
-
|
|
61669
|
+
# Project flow (top-level \u2014 preferred entry points)
|
|
61670
|
+
vibe init <dir> [-r 16:9|9:16|1:1|4:5] [-d <sec>] [--visual-style "<name>"]
|
|
61671
|
+
vibe build [<dir>] [--mode agent|batch|auto] # H3 dispatch
|
|
61672
|
+
vibe render [<dir>] [--fps 30] [--quality standard] [--format mp4]
|
|
61673
|
+
|
|
61674
|
+
# Lower-level scene authoring
|
|
61652
61675
|
vibe scene list-styles [<name>] # list / show vendored visual identities
|
|
61653
61676
|
vibe scene install-skill [<dir>] [--host all] # retroactive composition-rules install
|
|
61654
61677
|
vibe scene add <name> --style <preset> [...]
|
|
61655
61678
|
vibe scene compose-prompts [<dir>] [--beat <id>] # H2: emit plan, no LLM call
|
|
61656
61679
|
vibe scene lint [<root>] [--json] [--fix]
|
|
61657
|
-
vibe scene render [<root>] [--fps 30] [--quality standard] [--format mp4]
|
|
61658
|
-
vibe build [<dir>] [--mode agent|batch|auto] # H3 dispatch
|
|
61659
61680
|
\`\`\`
|
|
61660
61681
|
|
|
61661
61682
|
## Style presets (for \`vibe scene add --style\`)
|
|
@@ -61709,7 +61730,7 @@ and surface the error to the user.
|
|
|
61709
61730
|
| Generate narration + image, then author scene | \`vibe scene add\` |
|
|
61710
61731
|
| Generate a full scenes project from a STORYBOARD | \`vibe build\` |
|
|
61711
61732
|
| Hand-tweak a single scene's animation | edit \`compositions/<file>.html\` directly |
|
|
61712
|
-
| Render the project | \`vibe render\`
|
|
61733
|
+
| Render the project | \`vibe render\` (one canonical entry point) |
|
|
61713
61734
|
| Lint | \`vibe scene lint\` *or* \`npx hyperframes lint\` (equivalent) |
|
|
61714
61735
|
|
|
61715
61736
|
The \`vibe\` CLI adds asset generation, AI orchestration, and pipeline
|
|
@@ -61837,12 +61858,12 @@ overlay, etc.) \u2014 useful at the tail of a pipeline.
|
|
|
61837
61858
|
],
|
|
61838
61859
|
relatedCommands: [
|
|
61839
61860
|
"vibe init",
|
|
61861
|
+
"vibe build",
|
|
61862
|
+
"vibe render",
|
|
61840
61863
|
"vibe scene list-styles",
|
|
61841
61864
|
"vibe scene install-skill",
|
|
61842
61865
|
"vibe scene compose-prompts",
|
|
61843
|
-
"vibe build",
|
|
61844
61866
|
"vibe scene lint",
|
|
61845
|
-
"vibe scene render",
|
|
61846
61867
|
"vibe scene add"
|
|
61847
61868
|
]
|
|
61848
61869
|
},
|
|
@@ -62166,7 +62187,7 @@ init_source();
|
|
|
62166
62187
|
// package.json
|
|
62167
62188
|
var package_default = {
|
|
62168
62189
|
name: "@vibeframe/cli",
|
|
62169
|
-
version: "0.
|
|
62190
|
+
version: "0.79.0",
|
|
62170
62191
|
description: "VibeFrame CLI - natural language video editing from the terminal",
|
|
62171
62192
|
private: false,
|
|
62172
62193
|
license: "MIT",
|
|
@@ -62578,6 +62599,7 @@ init_engine();
|
|
|
62578
62599
|
init_validate();
|
|
62579
62600
|
init_output();
|
|
62580
62601
|
import { readFile as readFile9, writeFile as writeFile9 } from "node:fs/promises";
|
|
62602
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
62581
62603
|
import { resolve as resolve11, basename as basename2, extname } from "node:path";
|
|
62582
62604
|
var timelineCommand = new Command("timeline").description("Timeline editing commands").addHelpText("after", `
|
|
62583
62605
|
Examples:
|
|
@@ -62928,6 +62950,9 @@ timelineCommand.command("list").description("List timeline contents").argument("
|
|
|
62928
62950
|
const startedAt = Date.now();
|
|
62929
62951
|
try {
|
|
62930
62952
|
const filePath = resolve11(process.cwd(), projectPath);
|
|
62953
|
+
if (!existsSync8(filePath)) {
|
|
62954
|
+
exitWithError(notFoundError(projectPath));
|
|
62955
|
+
}
|
|
62931
62956
|
const content = await readFile9(filePath, "utf-8");
|
|
62932
62957
|
const data = JSON.parse(content);
|
|
62933
62958
|
const project = Project.fromJSON(data);
|
|
@@ -63548,16 +63573,16 @@ init_output();
|
|
|
63548
63573
|
init_validate();
|
|
63549
63574
|
import { resolve as resolve33, dirname as dirname11, basename as basename7, extname as extname7 } from "node:path";
|
|
63550
63575
|
import { readFile as readFile24, writeFile as writeFile31 } from "node:fs/promises";
|
|
63551
|
-
import { existsSync as
|
|
63576
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
63552
63577
|
var audioCommand = new Command("audio").alias("au").description("Audio operations (transcribe, TTS, voice clone, ducking)").addHelpText(
|
|
63553
63578
|
"after",
|
|
63554
63579
|
`
|
|
63555
63580
|
Examples:
|
|
63556
|
-
$ vibe audio transcribe interview.mp3 -o transcript.srt
|
|
63581
|
+
$ vibe audio transcribe interview.mp3 -o transcript.srt --format srt
|
|
63557
63582
|
$ vibe audio transcribe video.mp4 -l ko # Specify language
|
|
63558
63583
|
$ vibe audio list-voices # List available voices
|
|
63559
63584
|
$ vibe audio isolate song.mp3 -o vocals.mp3
|
|
63560
|
-
$ vibe audio clone-voice sample.mp3
|
|
63585
|
+
$ vibe audio clone-voice sample.mp3 --name "my-voice"
|
|
63561
63586
|
$ vibe audio dub video.mp4 -l ko -o dubbed.mp4
|
|
63562
63587
|
$ vibe audio duck music.mp3 --voice narration.mp3 -o ducked.mp3
|
|
63563
63588
|
|
|
@@ -63752,7 +63777,7 @@ audioCommand.command("clone-voice").description("Clone a voice from audio sample
|
|
|
63752
63777
|
const audioBuffers = [];
|
|
63753
63778
|
for (const samplePath of samples) {
|
|
63754
63779
|
const absPath = resolve33(process.cwd(), samplePath);
|
|
63755
|
-
if (!
|
|
63780
|
+
if (!existsSync26(absPath)) {
|
|
63756
63781
|
spinner2.fail(`File not found: ${samplePath}`);
|
|
63757
63782
|
exitWithError(notFoundError(samplePath));
|
|
63758
63783
|
}
|
|
@@ -63813,7 +63838,7 @@ audioCommand.command("dub").description("Dub audio/video to another language (tr
|
|
|
63813
63838
|
exitWithError(usageError("Target language is required. Use -l or --language"));
|
|
63814
63839
|
}
|
|
63815
63840
|
const absPath = resolve33(process.cwd(), mediaPath);
|
|
63816
|
-
if (!
|
|
63841
|
+
if (!existsSync26(absPath)) {
|
|
63817
63842
|
exitWithError(notFoundError(mediaPath));
|
|
63818
63843
|
}
|
|
63819
63844
|
const openaiKey = await requireApiKey("OPENAI_API_KEY", "OpenAI");
|
|
@@ -64056,7 +64081,7 @@ init_esm();
|
|
|
64056
64081
|
init_source();
|
|
64057
64082
|
init_ora();
|
|
64058
64083
|
import { resolve as resolve37, basename as basename10 } from "node:path";
|
|
64059
|
-
import { existsSync as
|
|
64084
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
64060
64085
|
|
|
64061
64086
|
// src/commands/ai-script-pipeline-cli.ts
|
|
64062
64087
|
init_source();
|
|
@@ -64067,19 +64092,19 @@ init_ai_script_pipeline();
|
|
|
64067
64092
|
init_output();
|
|
64068
64093
|
import { readFile as readFile25, writeFile as writeFile32 } from "node:fs/promises";
|
|
64069
64094
|
import { resolve as resolve34 } from "node:path";
|
|
64070
|
-
import { existsSync as
|
|
64095
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
64071
64096
|
function registerScriptPipelineCommands(aiCommand) {
|
|
64072
64097
|
aiCommand.command("regenerate-scene").description("Regenerate a specific scene in a script-to-video output directory").argument("<project-dir>", "Path to the script-to-video output directory").requiredOption("--scene <numbers>", "Scene number(s) to regenerate (1-based), e.g., 3 or 3,4,5").option("--video-only", "Only regenerate video").option("--narration-only", "Only regenerate narration").option("--image-only", "Only regenerate image").option("--generator <engine>", "Video generator: grok | kling | runway | veo", "grok").option("-i, --image-provider <provider>", "Image provider: gemini | openai | grok", "gemini").option("--voice <id>", "ElevenLabs voice ID for narration").option("-a, --aspect-ratio <ratio>", "Aspect ratio: 16:9 | 9:16 | 1:1", "16:9").option("--retries <count>", "Number of retries for video generation failures", String(DEFAULT_VIDEO_RETRIES)).option("--reference-scene <num>", "Use another scene's image as reference for character consistency").option("--dry-run", "Preview parameters without executing").action(async (projectDir, options) => {
|
|
64073
64098
|
const startedAt = Date.now();
|
|
64074
64099
|
try {
|
|
64075
64100
|
const outputDir = resolve34(process.cwd(), projectDir);
|
|
64076
64101
|
const projectPath = resolve34(outputDir, "project.vibe.json");
|
|
64077
|
-
if (!
|
|
64102
|
+
if (!existsSync27(outputDir)) {
|
|
64078
64103
|
exitWithError(notFoundError(outputDir));
|
|
64079
64104
|
}
|
|
64080
64105
|
const yamlPath = resolve34(outputDir, "storyboard.yaml");
|
|
64081
64106
|
const jsonPath = resolve34(outputDir, "storyboard.json");
|
|
64082
|
-
const storyboardPath =
|
|
64107
|
+
const storyboardPath = existsSync27(yamlPath) ? yamlPath : existsSync27(jsonPath) ? jsonPath : null;
|
|
64083
64108
|
const storyboardIsYaml = storyboardPath === yamlPath;
|
|
64084
64109
|
if (!storyboardPath) {
|
|
64085
64110
|
exitWithError(notFoundError(`${outputDir}/storyboard.{yaml,json}`));
|
|
@@ -64177,7 +64202,7 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
64177
64202
|
exitWithError(apiError(result.error || "Scene regeneration failed", true));
|
|
64178
64203
|
}
|
|
64179
64204
|
spinner2.succeed(source_default.green(`Regenerated ${result.regeneratedScenes.length} scene(s)`));
|
|
64180
|
-
if (
|
|
64205
|
+
if (existsSync27(projectPath) && result.regeneratedScenes.length > 0) {
|
|
64181
64206
|
const updateSpinner = ora("\u{1F4E6} Updating project file...").start();
|
|
64182
64207
|
try {
|
|
64183
64208
|
const updatedContent = await readFile25(storyboardPath, "utf-8");
|
|
@@ -64191,7 +64216,7 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
64191
64216
|
const imagePath = resolve34(outputDir, `scene-${sceneNum}.png`);
|
|
64192
64217
|
const videoSource = projectData.state.sources.find((s) => s.name === `Scene ${sceneNum}`);
|
|
64193
64218
|
if (videoSource) {
|
|
64194
|
-
const hasVideo =
|
|
64219
|
+
const hasVideo = existsSync27(videoPath);
|
|
64195
64220
|
videoSource.url = hasVideo ? videoPath : imagePath;
|
|
64196
64221
|
videoSource.type = hasVideo ? "video" : "image";
|
|
64197
64222
|
videoSource.duration = segment.duration;
|
|
@@ -64255,9 +64280,9 @@ Two flows \u2014 pick by intent:
|
|
|
64255
64280
|
|
|
64256
64281
|
Examples (REMIX):
|
|
64257
64282
|
$ vibe remix highlights long-video.mp4 -o highlights.json -d 60
|
|
64258
|
-
$ vibe remix auto-shorts long-video.mp4 -o shorts/
|
|
64259
|
-
$ vibe remix animated-caption video.mp4 -o captioned.mp4
|
|
64260
|
-
$ vibe remix animated-caption video.mp4 -o out.mp4
|
|
64283
|
+
$ vibe remix auto-shorts long-video.mp4 -o shorts/ --count 3 --add-captions
|
|
64284
|
+
$ vibe remix animated-caption video.mp4 -o captioned.mp4 --style highlight
|
|
64285
|
+
$ vibe remix animated-caption video.mp4 -o out.mp4 --style karaoke-sweep --fast
|
|
64261
64286
|
|
|
64262
64287
|
Provider API Keys:
|
|
64263
64288
|
highlights: GOOGLE_API_KEY (Gemini analysis)
|
|
@@ -64282,8 +64307,8 @@ pipelineCommand.command("animated-caption").description("Add animated captions w
|
|
|
64282
64307
|
`
|
|
64283
64308
|
Examples:
|
|
64284
64309
|
$ vibe remix animated-caption video.mp4 -o captioned.mp4
|
|
64285
|
-
$ vibe remix animated-caption video.mp4 -o out.mp4
|
|
64286
|
-
$ vibe remix animated-caption video.mp4 -o out.mp4
|
|
64310
|
+
$ vibe remix animated-caption video.mp4 -o out.mp4 --style bounce
|
|
64311
|
+
$ vibe remix animated-caption video.mp4 -o out.mp4 --style karaoke-sweep --fast
|
|
64287
64312
|
|
|
64288
64313
|
Styles:
|
|
64289
64314
|
highlight (default) TikTok-style background highlight on active word (Remotion)
|
|
@@ -64299,7 +64324,7 @@ Required API Key: OPENAI_API_KEY (Whisper transcription)
|
|
|
64299
64324
|
const startedAt = Date.now();
|
|
64300
64325
|
try {
|
|
64301
64326
|
const absVideoPath = resolve37(process.cwd(), videoPath);
|
|
64302
|
-
if (!
|
|
64327
|
+
if (!existsSync30(absVideoPath)) {
|
|
64303
64328
|
exitWithError(notFoundError(absVideoPath));
|
|
64304
64329
|
}
|
|
64305
64330
|
if (!ANIMATED_CAPTION_STYLES.includes(options.style)) {
|
|
@@ -69400,7 +69425,7 @@ init_agent_host_detect();
|
|
|
69400
69425
|
init_scene_project();
|
|
69401
69426
|
init_visual_styles();
|
|
69402
69427
|
init_install_skill();
|
|
69403
|
-
import { existsSync as
|
|
69428
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
69404
69429
|
import { mkdir as mkdir17, readFile as readFile30, writeFile as writeFile37 } from "node:fs/promises";
|
|
69405
69430
|
import { basename as basename14, dirname as dirname14, resolve as resolve42 } from "node:path";
|
|
69406
69431
|
|
|
@@ -69691,7 +69716,7 @@ var initCommand = new Command("init").description("Scaffold a VibeFrame project
|
|
|
69691
69716
|
const wantsGemini = targetHosts.includes("gemini-cli");
|
|
69692
69717
|
const wantsCrossTool = targetHosts.some((h) => h !== "claude-code");
|
|
69693
69718
|
const actions = [];
|
|
69694
|
-
if (!
|
|
69719
|
+
if (!existsSync31(projectDir)) {
|
|
69695
69720
|
if (options.dryRun) {
|
|
69696
69721
|
actions.push({ path: projectDir, status: "would-write", reason: "(would create directory)" });
|
|
69697
69722
|
} else {
|
|
@@ -69937,7 +69962,7 @@ function resolveTargets(agent) {
|
|
|
69937
69962
|
return [agent];
|
|
69938
69963
|
}
|
|
69939
69964
|
async function writeIfMissing(absPath, content, force, dryRun) {
|
|
69940
|
-
const exists =
|
|
69965
|
+
const exists = existsSync31(absPath);
|
|
69941
69966
|
if (exists && !force) {
|
|
69942
69967
|
return { path: absPath, status: "skipped-exists", reason: "(use --force to overwrite)" };
|
|
69943
69968
|
}
|
|
@@ -69950,7 +69975,7 @@ async function writeIfMissing(absPath, content, force, dryRun) {
|
|
|
69950
69975
|
}
|
|
69951
69976
|
async function mergeGitignore(absPath, additions, dryRun) {
|
|
69952
69977
|
const marker = "# VibeFrame";
|
|
69953
|
-
if (!
|
|
69978
|
+
if (!existsSync31(absPath)) {
|
|
69954
69979
|
if (dryRun) return { path: absPath, status: "would-write" };
|
|
69955
69980
|
await writeFile37(absPath, additions.trimStart(), "utf-8");
|
|
69956
69981
|
return { path: absPath, status: "wrote" };
|
|
@@ -70293,7 +70318,7 @@ init_composer_resolve();
|
|
|
70293
70318
|
init_scene_build();
|
|
70294
70319
|
init_output();
|
|
70295
70320
|
import { access as access6 } from "node:fs/promises";
|
|
70296
|
-
import { existsSync as
|
|
70321
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
70297
70322
|
import { resolve as resolve49 } from "node:path";
|
|
70298
70323
|
var COMMAND_KEY_MAP = getCommandKeyMap();
|
|
70299
70324
|
var FREE_COMMANDS = [
|
|
@@ -70407,7 +70432,7 @@ async function runDiagnostics() {
|
|
|
70407
70432
|
installed = false;
|
|
70408
70433
|
}
|
|
70409
70434
|
} else if (toolName === "chrome") {
|
|
70410
|
-
const { existsSync:
|
|
70435
|
+
const { existsSync: existsSync40 } = await import("node:fs");
|
|
70411
70436
|
const { homedir: homedir6 } = await import("node:os");
|
|
70412
70437
|
const { join: join16 } = await import("node:path");
|
|
70413
70438
|
const chromePaths = [
|
|
@@ -70420,7 +70445,7 @@ async function runDiagnostics() {
|
|
|
70420
70445
|
"/usr/bin/chromium",
|
|
70421
70446
|
"/usr/bin/chromium-browser"
|
|
70422
70447
|
];
|
|
70423
|
-
installed = chromePaths.some((p) => p &&
|
|
70448
|
+
installed = chromePaths.some((p) => p && existsSync40(p));
|
|
70424
70449
|
} else {
|
|
70425
70450
|
installed = commandExists(toolName);
|
|
70426
70451
|
}
|
|
@@ -70451,7 +70476,7 @@ async function runDiagnostics() {
|
|
|
70451
70476
|
const cwd = process.cwd();
|
|
70452
70477
|
const projectFiles = ["AGENTS.md", "CLAUDE.md", "vibe.project.yaml"].map((rel) => ({
|
|
70453
70478
|
path: rel,
|
|
70454
|
-
exists:
|
|
70479
|
+
exists: existsSync35(resolve49(cwd, rel))
|
|
70455
70480
|
}));
|
|
70456
70481
|
const projectInitialized = projectFiles.some((f) => f.exists);
|
|
70457
70482
|
const hosts = detectAgentHosts();
|
|
@@ -70466,8 +70491,8 @@ async function runDiagnostics() {
|
|
|
70466
70491
|
} catch (err) {
|
|
70467
70492
|
if (!(err instanceof ComposerResolveError)) throw err;
|
|
70468
70493
|
}
|
|
70469
|
-
const sceneProjectInCwd =
|
|
70470
|
-
const skillInstalled =
|
|
70494
|
+
const sceneProjectInCwd = existsSync35(resolve49(cwd, "STORYBOARD.md"));
|
|
70495
|
+
const skillInstalled = existsSync35(resolve49(cwd, "SKILL.md"));
|
|
70471
70496
|
return {
|
|
70472
70497
|
system: {
|
|
70473
70498
|
node: { version: nodeVersion, ok: true },
|
|
@@ -70633,7 +70658,7 @@ init_detect();
|
|
|
70633
70658
|
init_ai_edit();
|
|
70634
70659
|
init_output();
|
|
70635
70660
|
import { resolve as resolve50 } from "node:path";
|
|
70636
|
-
import { existsSync as
|
|
70661
|
+
import { existsSync as existsSync36 } from "node:fs";
|
|
70637
70662
|
import { mkdir as mkdir20, rm as rm3 } from "node:fs/promises";
|
|
70638
70663
|
var DEMO_DIR = resolve50(process.cwd(), ".vibeframe-demo");
|
|
70639
70664
|
async function generateTestVideo(outputPath) {
|
|
@@ -70664,7 +70689,7 @@ async function generateTestVideo(outputPath) {
|
|
|
70664
70689
|
"-y",
|
|
70665
70690
|
outputPath
|
|
70666
70691
|
], { timeout: 3e4, maxBuffer: 50 * 1024 * 1024 });
|
|
70667
|
-
return
|
|
70692
|
+
return existsSync36(outputPath);
|
|
70668
70693
|
} catch {
|
|
70669
70694
|
try {
|
|
70670
70695
|
await execSafe("ffmpeg", [
|
|
@@ -70690,7 +70715,7 @@ async function generateTestVideo(outputPath) {
|
|
|
70690
70715
|
"-y",
|
|
70691
70716
|
outputPath
|
|
70692
70717
|
], { timeout: 3e4, maxBuffer: 50 * 1024 * 1024 });
|
|
70693
|
-
return
|
|
70718
|
+
return existsSync36(outputPath);
|
|
70694
70719
|
} catch {
|
|
70695
70720
|
return false;
|
|
70696
70721
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeframe/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.79.0",
|
|
4
4
|
"description": "VibeFrame CLI - natural language video editing from the terminal",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -182,8 +182,8 @@
|
|
|
182
182
|
"tsx": "^4.21.0",
|
|
183
183
|
"typescript": "^5.3.3",
|
|
184
184
|
"vitest": "^1.2.2",
|
|
185
|
-
"@vibeframe/
|
|
186
|
-
"@vibeframe/
|
|
185
|
+
"@vibeframe/ai-providers": "0.79.0",
|
|
186
|
+
"@vibeframe/core": "0.79.0"
|
|
187
187
|
},
|
|
188
188
|
"engines": {
|
|
189
189
|
"node": ">=20"
|