@vibeframe/cli 0.113.10 → 0.113.11
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/_shared/build-plan.d.ts +7 -0
- package/dist/commands/_shared/compose-prompts.d.ts +7 -0
- package/dist/commands/_shared/compose-scenes-skills.d.ts +30 -1
- package/dist/commands/_shared/scene-build.d.ts +20 -0
- package/dist/commands/_shared/transcribe-narration.d.ts +23 -0
- package/dist/commands/_shared/transcribe-narration.test.d.ts +1 -0
- package/dist/index.js +759 -621
- package/dist/tools/manifest/index.js +603 -473
- package/package.json +3 -3
|
@@ -4756,15 +4756,15 @@ async function loadKokoroRuntime() {
|
|
|
4756
4756
|
}
|
|
4757
4757
|
async function loadKokoroFromWorkspace(baseDir = process.cwd()) {
|
|
4758
4758
|
try {
|
|
4759
|
-
const { readFile:
|
|
4760
|
-
const { join:
|
|
4759
|
+
const { readFile: readFile46 } = await import("node:fs/promises");
|
|
4760
|
+
const { join: join28, resolve: resolve66 } = await import("node:path");
|
|
4761
4761
|
const { pathToFileURL } = await import("node:url");
|
|
4762
4762
|
const pkgDir = resolve66(baseDir, "node_modules", "kokoro-js");
|
|
4763
|
-
const pkg = JSON.parse(await
|
|
4763
|
+
const pkg = JSON.parse(await readFile46(join28(pkgDir, "package.json"), "utf-8"));
|
|
4764
4764
|
const entry = pkg.exports?.node?.import ?? pkg.exports?.default ?? pkg.module ?? pkg.main;
|
|
4765
4765
|
if (!entry)
|
|
4766
4766
|
return null;
|
|
4767
|
-
const mod = await import(pathToFileURL(
|
|
4767
|
+
const mod = await import(pathToFileURL(join28(pkgDir, entry)).href);
|
|
4768
4768
|
return mod.KokoroTTS ?? null;
|
|
4769
4769
|
} catch {
|
|
4770
4770
|
return null;
|
|
@@ -4775,20 +4775,20 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
|
|
|
4775
4775
|
if (!base)
|
|
4776
4776
|
return null;
|
|
4777
4777
|
try {
|
|
4778
|
-
const { readFile:
|
|
4779
|
-
const { join:
|
|
4778
|
+
const { readFile: readFile46, writeFile: writeFile51, mkdir: mkdir26 } = await import("node:fs/promises");
|
|
4779
|
+
const { join: join28, resolve: resolve66 } = await import("node:path");
|
|
4780
4780
|
const { pathToFileURL } = await import("node:url");
|
|
4781
4781
|
const { homedir: homedir6 } = await import("node:os");
|
|
4782
4782
|
const transformersDir = resolve66(base, "node_modules", "@huggingface", "transformers");
|
|
4783
4783
|
const ortDistDir = resolve66(base, "node_modules", "onnxruntime-web", "dist");
|
|
4784
|
-
const transformers = await import(pathToFileURL(
|
|
4785
|
-
const cacheRoot =
|
|
4784
|
+
const transformers = await import(pathToFileURL(join28(transformersDir, "dist", "transformers.web.js")).href);
|
|
4785
|
+
const cacheRoot = join28(homedir6(), ".cache", "vibeframe", "models");
|
|
4786
4786
|
transformers.env.allowLocalModels = false;
|
|
4787
4787
|
transformers.env.useCustomCache = true;
|
|
4788
4788
|
transformers.env.customCache = {
|
|
4789
4789
|
async match(key) {
|
|
4790
4790
|
try {
|
|
4791
|
-
return new Response(await
|
|
4791
|
+
return new Response(await readFile46(join28(cacheRoot, encodeURIComponent(String(key)))));
|
|
4792
4792
|
} catch {
|
|
4793
4793
|
return void 0;
|
|
4794
4794
|
}
|
|
@@ -4797,7 +4797,7 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
|
|
|
4797
4797
|
try {
|
|
4798
4798
|
const buf = Buffer.from(await response.arrayBuffer());
|
|
4799
4799
|
await mkdir26(cacheRoot, { recursive: true });
|
|
4800
|
-
await writeFile51(
|
|
4800
|
+
await writeFile51(join28(cacheRoot, encodeURIComponent(String(key))), buf);
|
|
4801
4801
|
} catch {
|
|
4802
4802
|
}
|
|
4803
4803
|
}
|
|
@@ -31196,10 +31196,57 @@ var init_composer_resolve = __esm({
|
|
|
31196
31196
|
}
|
|
31197
31197
|
});
|
|
31198
31198
|
|
|
31199
|
+
// src/commands/_shared/transcribe-narration.ts
|
|
31200
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
31201
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
31202
|
+
import { join as join9 } from "node:path";
|
|
31203
|
+
async function transcribeNarrationWords(audioAbsPath, opts) {
|
|
31204
|
+
try {
|
|
31205
|
+
const whisper = new WhisperProvider();
|
|
31206
|
+
await whisper.initialize({ apiKey: opts.apiKey });
|
|
31207
|
+
const audioBytes = await readFile9(audioAbsPath);
|
|
31208
|
+
const audioBlob = new Blob([new Uint8Array(audioBytes)]);
|
|
31209
|
+
const transcript = await whisper.transcribe(audioBlob, void 0, {
|
|
31210
|
+
granularity: "word",
|
|
31211
|
+
language: opts.language
|
|
31212
|
+
});
|
|
31213
|
+
if (transcript.status === "completed" && transcript.words?.length) {
|
|
31214
|
+
return transcript.words.map((w) => ({ text: w.text, start: w.start, end: w.end }));
|
|
31215
|
+
}
|
|
31216
|
+
return [];
|
|
31217
|
+
} catch {
|
|
31218
|
+
return [];
|
|
31219
|
+
}
|
|
31220
|
+
}
|
|
31221
|
+
function beatTranscriptRelPath(beatId) {
|
|
31222
|
+
return `assets/transcript-${beatId}.json`;
|
|
31223
|
+
}
|
|
31224
|
+
async function readBeatTranscript(projectDir, beatId) {
|
|
31225
|
+
const abs = join9(projectDir, beatTranscriptRelPath(beatId));
|
|
31226
|
+
if (!existsSync9(abs)) return void 0;
|
|
31227
|
+
try {
|
|
31228
|
+
const parsed = JSON.parse(await readFile9(abs, "utf-8"));
|
|
31229
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
31230
|
+
const words = parsed.filter(
|
|
31231
|
+
(w) => typeof w === "object" && w !== null && typeof w.text === "string" && Number.isFinite(w.start) && Number.isFinite(w.end)
|
|
31232
|
+
).map((w) => ({ text: w.text, start: w.start, end: w.end }));
|
|
31233
|
+
return words.length > 0 ? words : void 0;
|
|
31234
|
+
} catch {
|
|
31235
|
+
return void 0;
|
|
31236
|
+
}
|
|
31237
|
+
}
|
|
31238
|
+
var init_transcribe_narration = __esm({
|
|
31239
|
+
"src/commands/_shared/transcribe-narration.ts"() {
|
|
31240
|
+
"use strict";
|
|
31241
|
+
init_dist2();
|
|
31242
|
+
}
|
|
31243
|
+
});
|
|
31244
|
+
|
|
31199
31245
|
// src/commands/_shared/compose-scenes-skills.ts
|
|
31200
31246
|
var compose_scenes_skills_exports = {};
|
|
31201
31247
|
__export(compose_scenes_skills_exports, {
|
|
31202
31248
|
ComposeBeatError: () => ComposeBeatError,
|
|
31249
|
+
TRANSCRIPT_PROMPT_MAX_WORDS: () => TRANSCRIPT_PROMPT_MAX_WORDS,
|
|
31203
31250
|
buildSystemPrompt: () => buildSystemPrompt,
|
|
31204
31251
|
buildUserPrompt: () => buildUserPrompt,
|
|
31205
31252
|
composeBeatHtml: () => composeBeatHtml,
|
|
@@ -31209,15 +31256,16 @@ __export(compose_scenes_skills_exports, {
|
|
|
31209
31256
|
executeComposeScenesWithSkills: () => executeComposeScenesWithSkills,
|
|
31210
31257
|
extractHtml: () => extractHtml,
|
|
31211
31258
|
formatLintFeedback: () => formatLintFeedback,
|
|
31259
|
+
formatTranscriptSection: () => formatTranscriptSection,
|
|
31212
31260
|
lintBeatHtml: () => lintBeatHtml
|
|
31213
31261
|
});
|
|
31214
31262
|
import Anthropic from "@anthropic-ai/sdk";
|
|
31215
31263
|
import OpenAI from "openai";
|
|
31216
31264
|
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
31217
31265
|
import { createHash as createHash4 } from "node:crypto";
|
|
31218
|
-
import { existsSync as
|
|
31219
|
-
import { mkdir as mkdir6, readFile as
|
|
31220
|
-
import { dirname as dirname5, join as
|
|
31266
|
+
import { existsSync as existsSync10, mkdirSync } from "node:fs";
|
|
31267
|
+
import { mkdir as mkdir6, readFile as readFile10, writeFile as writeFile12 } from "node:fs/promises";
|
|
31268
|
+
import { dirname as dirname5, join as join10, resolve as resolve12 } from "node:path";
|
|
31221
31269
|
import { runHyperframeLint as runHyperframeLint2 } from "@hyperframes/producer";
|
|
31222
31270
|
function buildSystemPrompt(ctx) {
|
|
31223
31271
|
return `You are a Hyperframes composition author. The skill content below
|
|
@@ -31251,6 +31299,46 @@ function formatBeatCues(cues) {
|
|
|
31251
31299
|
}
|
|
31252
31300
|
return lines.join("\n") + "\n";
|
|
31253
31301
|
}
|
|
31302
|
+
function formatTranscriptSection(transcript, maxWords = TRANSCRIPT_PROMPT_MAX_WORDS) {
|
|
31303
|
+
if (!transcript || transcript.length === 0) return "";
|
|
31304
|
+
const round = (n) => Number(Math.max(0, n).toFixed(2));
|
|
31305
|
+
const noAudioRule = "This is for VISUAL sync only \u2014 do NOT add an `<audio>` tag or invent SFX; the narration audio is wired separately by the root timeline. If this beat is visual-only (no on-screen text), you may ignore these timings.";
|
|
31306
|
+
if (transcript.length <= maxWords) {
|
|
31307
|
+
const pairs = transcript.map((w) => `[${round(w.start)}, ${JSON.stringify(w.text)}]`).join(", ");
|
|
31308
|
+
return `
|
|
31309
|
+
|
|
31310
|
+
=== Narration word timings (seconds) ===
|
|
31311
|
+
|
|
31312
|
+
Whisper word-level start times for this beat's narration. If you author captions
|
|
31313
|
+
or kinetic typography, reveal each word at its start time with absolute-time
|
|
31314
|
+
GSAP tweens, e.g.
|
|
31315
|
+
\`tl.fromTo('.caption .word[data-i="0"]', { opacity: 0, y: 10 }, { opacity: 1, y: 0, duration: 0.18 }, ${round(
|
|
31316
|
+
transcript[0].start
|
|
31317
|
+
)});\`
|
|
31318
|
+
${noAudioRule}
|
|
31319
|
+
|
|
31320
|
+
word starts (word-level): ${pairs}`;
|
|
31321
|
+
}
|
|
31322
|
+
const phraseCount = Math.max(1, Math.floor(maxWords / 2));
|
|
31323
|
+
const chunkSize = Math.ceil(transcript.length / phraseCount);
|
|
31324
|
+
const anchors = [];
|
|
31325
|
+
for (let i = 0; i < transcript.length; i += chunkSize) {
|
|
31326
|
+
const chunk = transcript.slice(i, i + chunkSize);
|
|
31327
|
+
const text = chunk.map((w) => w.text).join(" ");
|
|
31328
|
+
anchors.push(`[${round(chunk[0].start)}, ${JSON.stringify(text)}]`);
|
|
31329
|
+
}
|
|
31330
|
+
return `
|
|
31331
|
+
|
|
31332
|
+
=== Narration timings (approximate, phrase-level) ===
|
|
31333
|
+
|
|
31334
|
+
This beat's narration is long (${transcript.length} words), so timings are
|
|
31335
|
+
grouped into phrase-level anchors (start time of each phrase). Use them to PACE
|
|
31336
|
+
caption / kinetic-type reveals across the beat \u2014 they are approximate, not
|
|
31337
|
+
per-word truth.
|
|
31338
|
+
${noAudioRule}
|
|
31339
|
+
|
|
31340
|
+
phrase starts (approximate): ${anchors.join(", ")}`;
|
|
31341
|
+
}
|
|
31254
31342
|
function buildUserPrompt(ctx) {
|
|
31255
31343
|
const compositionId = `scene-${ctx.beat.id}`;
|
|
31256
31344
|
const durLiteral = ctx.finalDurationSec !== void 0 ? String(ctx.finalDurationSec) : void 0;
|
|
@@ -31413,7 +31501,7 @@ ${ctx.storyboardGlobal || "(no global direction)"}
|
|
|
31413
31501
|
|
|
31414
31502
|
## ${ctx.beat.heading}
|
|
31415
31503
|
${formatBeatCues(ctx.beat.cues)}${cueDurationNote}
|
|
31416
|
-
${ctx.beat.body}
|
|
31504
|
+
${ctx.beat.body}${formatTranscriptSection(ctx.transcript)}
|
|
31417
31505
|
|
|
31418
31506
|
=== Output format ===
|
|
31419
31507
|
|
|
@@ -31433,7 +31521,7 @@ function computeCacheKey(parts) {
|
|
|
31433
31521
|
return createHash4("sha256").update(parts.provider).update("\u241E").update(parts.model).update("\u241E").update(parts.systemPrompt).update("\u241E").update(parts.userPrompt).digest("hex");
|
|
31434
31522
|
}
|
|
31435
31523
|
function defaultCacheDir() {
|
|
31436
|
-
return
|
|
31524
|
+
return join10(USER_CACHE_DIR, "compose-scenes");
|
|
31437
31525
|
}
|
|
31438
31526
|
function extractHtml(responseText) {
|
|
31439
31527
|
const fenced = responseText.match(/```html\s*\n([\s\S]*?)\n```/);
|
|
@@ -31457,9 +31545,9 @@ async function composeBeatHtml(ctx, overrides) {
|
|
|
31457
31545
|
model: settings.model
|
|
31458
31546
|
});
|
|
31459
31547
|
const cacheDir = ctx.cacheDir ?? defaultCacheDir();
|
|
31460
|
-
const cachePath =
|
|
31461
|
-
if (
|
|
31462
|
-
const html2 = await
|
|
31548
|
+
const cachePath = join10(cacheDir, `${cacheKey}.html`);
|
|
31549
|
+
if (existsSync10(cachePath)) {
|
|
31550
|
+
const html2 = await readFile10(cachePath, "utf-8");
|
|
31463
31551
|
return { html: html2, cached: true, cacheKey, model: settings.model, provider: ctx.provider };
|
|
31464
31552
|
}
|
|
31465
31553
|
if (!ctx.apiKey) {
|
|
@@ -31595,14 +31683,14 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
31595
31683
|
const projectRoot = params.project ? resolve12(outputDir, params.project) : resolve12(outputDir);
|
|
31596
31684
|
const designPath = resolve12(projectRoot, params.design ?? "DESIGN.md");
|
|
31597
31685
|
const storyboardPath = resolve12(projectRoot, params.storyboard ?? "STORYBOARD.md");
|
|
31598
|
-
if (!
|
|
31686
|
+
if (!existsSync10(designPath)) {
|
|
31599
31687
|
return { success: false, error: `DESIGN.md not found at ${designPath}` };
|
|
31600
31688
|
}
|
|
31601
|
-
if (!
|
|
31689
|
+
if (!existsSync10(storyboardPath)) {
|
|
31602
31690
|
return { success: false, error: `STORYBOARD.md not found at ${storyboardPath}` };
|
|
31603
31691
|
}
|
|
31604
|
-
const designMd = await
|
|
31605
|
-
const storyboardMd = await
|
|
31692
|
+
const designMd = await readFile10(designPath, "utf-8");
|
|
31693
|
+
const storyboardMd = await readFile10(storyboardPath, "utf-8");
|
|
31606
31694
|
const { global: storyboardGlobal, beats } = parseStoryboard(storyboardMd);
|
|
31607
31695
|
if (beats.length === 0) {
|
|
31608
31696
|
return {
|
|
@@ -31622,7 +31710,7 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
31622
31710
|
provider = params.composer ?? "claude";
|
|
31623
31711
|
apiKey = "";
|
|
31624
31712
|
}
|
|
31625
|
-
const compositionsDir =
|
|
31713
|
+
const compositionsDir = join10(projectRoot, "compositions");
|
|
31626
31714
|
await mkdir6(compositionsDir, { recursive: true });
|
|
31627
31715
|
let finalDurations = /* @__PURE__ */ new Map();
|
|
31628
31716
|
try {
|
|
@@ -31636,6 +31724,7 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
31636
31724
|
async (beat, beatIndex) => {
|
|
31637
31725
|
onProgress({ type: "beat-start", beatId: beat.id, beatIndex, totalBeats });
|
|
31638
31726
|
try {
|
|
31727
|
+
const transcript = await readBeatTranscript(projectRoot, beat.id);
|
|
31639
31728
|
const result = await composeBeatWithRetry(
|
|
31640
31729
|
{
|
|
31641
31730
|
beat,
|
|
@@ -31646,11 +31735,12 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
31646
31735
|
apiKey,
|
|
31647
31736
|
effort: params.effort,
|
|
31648
31737
|
cacheDir: params.cacheDir,
|
|
31649
|
-
finalDurationSec: finalDurations.get(beat.id)
|
|
31738
|
+
finalDurationSec: finalDurations.get(beat.id),
|
|
31739
|
+
transcript
|
|
31650
31740
|
},
|
|
31651
31741
|
overrides
|
|
31652
31742
|
);
|
|
31653
|
-
const compositionPath =
|
|
31743
|
+
const compositionPath = join10(compositionsDir, `scene-${beat.id}.html`);
|
|
31654
31744
|
await mkdir6(dirname5(compositionPath), { recursive: true });
|
|
31655
31745
|
await writeFile12(compositionPath, result.html, "utf-8");
|
|
31656
31746
|
if (result.cached) {
|
|
@@ -31729,7 +31819,7 @@ ${failures.map((f) => ` - ${f.beatId}: ${f.error}`).join("\n")}`,
|
|
|
31729
31819
|
data: aggregateData
|
|
31730
31820
|
};
|
|
31731
31821
|
}
|
|
31732
|
-
var MODEL_SETTINGS_BY_PROVIDER, ComposeBeatError, defaultCallLLM, CONFIG_PROVIDER_BY_COMPOSER;
|
|
31822
|
+
var MODEL_SETTINGS_BY_PROVIDER, TRANSCRIPT_PROMPT_MAX_WORDS, ComposeBeatError, defaultCallLLM, CONFIG_PROVIDER_BY_COMPOSER;
|
|
31733
31823
|
var init_compose_scenes_skills = __esm({
|
|
31734
31824
|
"src/commands/_shared/compose-scenes-skills.ts"() {
|
|
31735
31825
|
"use strict";
|
|
@@ -31741,6 +31831,7 @@ var init_compose_scenes_skills = __esm({
|
|
|
31741
31831
|
init_root_sync();
|
|
31742
31832
|
init_composer_resolve();
|
|
31743
31833
|
init_config();
|
|
31834
|
+
init_transcribe_narration();
|
|
31744
31835
|
MODEL_SETTINGS_BY_PROVIDER = {
|
|
31745
31836
|
// Anthropic Claude Sonnet 4.6 — v0.69 baseline. ~9 s/beat, $3/$15 per MTok.
|
|
31746
31837
|
claude: {
|
|
@@ -31764,6 +31855,7 @@ var init_compose_scenes_skills = __esm({
|
|
|
31764
31855
|
high: { model: GEMINI_DEFAULT_TEXT_MODEL, maxTokens: 8e3, costPerMTokIn: 1.5, costPerMTokOut: 9 }
|
|
31765
31856
|
}
|
|
31766
31857
|
};
|
|
31858
|
+
TRANSCRIPT_PROMPT_MAX_WORDS = 120;
|
|
31767
31859
|
ComposeBeatError = class extends Error {
|
|
31768
31860
|
constructor(code, message, cause) {
|
|
31769
31861
|
super(message);
|
|
@@ -31849,22 +31941,22 @@ var init_compose_scenes_skills = __esm({
|
|
|
31849
31941
|
});
|
|
31850
31942
|
|
|
31851
31943
|
// src/commands/_shared/compose-prompts.ts
|
|
31852
|
-
import { existsSync as
|
|
31853
|
-
import { readFile as
|
|
31854
|
-
import { join as
|
|
31944
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
31945
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
31946
|
+
import { join as join11, relative as relative4, resolve as resolve13 } from "node:path";
|
|
31855
31947
|
async function getComposePrompts(opts) {
|
|
31856
31948
|
const projectDir = resolve13(opts.projectDir);
|
|
31857
|
-
const designPath =
|
|
31858
|
-
const storyboardPath =
|
|
31859
|
-
const skillPath =
|
|
31860
|
-
const compositionsDir =
|
|
31949
|
+
const designPath = join11(projectDir, "DESIGN.md");
|
|
31950
|
+
const storyboardPath = join11(projectDir, "STORYBOARD.md");
|
|
31951
|
+
const skillPath = join11(projectDir, "SKILL.md");
|
|
31952
|
+
const compositionsDir = join11(projectDir, "compositions");
|
|
31861
31953
|
const warnings = [];
|
|
31862
31954
|
const baseError = (msg) => ({
|
|
31863
31955
|
success: false,
|
|
31864
31956
|
projectDir,
|
|
31865
31957
|
designReference: "DESIGN.md",
|
|
31866
31958
|
storyboardReference: "STORYBOARD.md",
|
|
31867
|
-
skillReference:
|
|
31959
|
+
skillReference: existsSync11(skillPath) ? "SKILL.md" : null,
|
|
31868
31960
|
compositionsDir: "compositions",
|
|
31869
31961
|
beats: [],
|
|
31870
31962
|
instructions: [],
|
|
@@ -31872,18 +31964,18 @@ async function getComposePrompts(opts) {
|
|
|
31872
31964
|
warnings,
|
|
31873
31965
|
error: msg
|
|
31874
31966
|
});
|
|
31875
|
-
if (!
|
|
31967
|
+
if (!existsSync11(designPath)) {
|
|
31876
31968
|
return baseError(`DESIGN.md not found at ${designPath}. Run \`vibe scene init <dir>\` first.`);
|
|
31877
31969
|
}
|
|
31878
|
-
if (!
|
|
31970
|
+
if (!existsSync11(storyboardPath)) {
|
|
31879
31971
|
return baseError(`STORYBOARD.md not found at ${storyboardPath}. Run \`vibe scene init <dir>\` to create a starter, or add STORYBOARD.md with per-beat cues.`);
|
|
31880
31972
|
}
|
|
31881
|
-
if (!
|
|
31973
|
+
if (!existsSync11(skillPath)) {
|
|
31882
31974
|
warnings.push(
|
|
31883
31975
|
"SKILL.md not installed \u2014 host agent won't have Hyperframes rules in context. Run `vibe scene install-skill` to install it."
|
|
31884
31976
|
);
|
|
31885
31977
|
}
|
|
31886
|
-
const storyboardMd = await
|
|
31978
|
+
const storyboardMd = await readFile11(storyboardPath, "utf-8");
|
|
31887
31979
|
const parsed = parseStoryboard(storyboardMd);
|
|
31888
31980
|
if (parsed.beats.length === 0) {
|
|
31889
31981
|
return baseError(`STORYBOARD.md has no \`## Beat \u2026\` headings.`);
|
|
@@ -31905,28 +31997,33 @@ async function getComposePrompts(opts) {
|
|
|
31905
31997
|
finalDurations = await resolveProjectBeatDurations(projectDir);
|
|
31906
31998
|
} catch {
|
|
31907
31999
|
}
|
|
31908
|
-
const result =
|
|
31909
|
-
|
|
31910
|
-
|
|
31911
|
-
|
|
31912
|
-
|
|
31913
|
-
beat
|
|
31914
|
-
|
|
31915
|
-
|
|
31916
|
-
|
|
31917
|
-
|
|
31918
|
-
|
|
31919
|
-
|
|
31920
|
-
|
|
31921
|
-
|
|
31922
|
-
|
|
31923
|
-
|
|
31924
|
-
|
|
31925
|
-
|
|
31926
|
-
|
|
31927
|
-
|
|
31928
|
-
|
|
31929
|
-
|
|
32000
|
+
const result = await Promise.all(
|
|
32001
|
+
beats.map(async (beat) => {
|
|
32002
|
+
const outputPathAbs = join11(compositionsDir, `scene-${beat.id}.html`);
|
|
32003
|
+
const outputPathRel = relative4(projectDir, outputPathAbs);
|
|
32004
|
+
const finalDurationSec = finalDurations.get(beat.id);
|
|
32005
|
+
const transcript = await readBeatTranscript(projectDir, beat.id);
|
|
32006
|
+
const userPrompt = buildUserPrompt({
|
|
32007
|
+
beat,
|
|
32008
|
+
storyboardGlobal: parsed.global,
|
|
32009
|
+
finalDurationSec,
|
|
32010
|
+
transcript
|
|
32011
|
+
});
|
|
32012
|
+
return {
|
|
32013
|
+
id: beat.id,
|
|
32014
|
+
heading: beat.heading,
|
|
32015
|
+
outputPath: outputPathRel,
|
|
32016
|
+
duration: beat.duration,
|
|
32017
|
+
finalDurationSec,
|
|
32018
|
+
cues: beat.cues,
|
|
32019
|
+
body: beat.body,
|
|
32020
|
+
userPrompt,
|
|
32021
|
+
transcriptPath: transcript ? beatTranscriptRelPath(beat.id) : void 0,
|
|
32022
|
+
exists: existsSync11(outputPathAbs)
|
|
32023
|
+
};
|
|
32024
|
+
})
|
|
32025
|
+
);
|
|
32026
|
+
const skillRef = existsSync11(skillPath) ? "SKILL.md" : null;
|
|
31930
32027
|
const instructions = buildInstructions({
|
|
31931
32028
|
skillRef,
|
|
31932
32029
|
beatCount: result.length,
|
|
@@ -31974,13 +32071,14 @@ var init_compose_prompts = __esm({
|
|
|
31974
32071
|
init_compose_scenes_skills();
|
|
31975
32072
|
init_root_sync();
|
|
31976
32073
|
init_bundle();
|
|
32074
|
+
init_transcribe_narration();
|
|
31977
32075
|
}
|
|
31978
32076
|
});
|
|
31979
32077
|
|
|
31980
32078
|
// src/commands/_shared/sub-comp-duration-sync.ts
|
|
31981
|
-
import { existsSync as
|
|
31982
|
-
import { readFile as
|
|
31983
|
-
import { join as
|
|
32079
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
32080
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
32081
|
+
import { join as join13, resolve as resolve15 } from "node:path";
|
|
31984
32082
|
function syncSubCompositionDurationHtml(html, beatId, newDurationSec, fileRel) {
|
|
31985
32083
|
const compositionId = `scene-${beatId}`;
|
|
31986
32084
|
const file = fileRel ?? `compositions/${compositionId}.html`;
|
|
@@ -32072,15 +32170,15 @@ async function createSubCompDurationSyncPlans(opts) {
|
|
|
32072
32170
|
const results = [];
|
|
32073
32171
|
for (const beat of opts.beats) {
|
|
32074
32172
|
const fileRel = `compositions/scene-${beat.id}.html`;
|
|
32075
|
-
const filePath =
|
|
32076
|
-
if (!
|
|
32173
|
+
const filePath = join13(projectDir, fileRel);
|
|
32174
|
+
if (!existsSync13(filePath)) continue;
|
|
32077
32175
|
const newDurationSec = await resolveSyncedBeatDuration({
|
|
32078
32176
|
projectDir,
|
|
32079
32177
|
beatDuration: beat.duration,
|
|
32080
32178
|
narrationPath: beat.narrationPath,
|
|
32081
32179
|
sceneDurationSec: beat.sceneDurationSec
|
|
32082
32180
|
});
|
|
32083
|
-
const html = await
|
|
32181
|
+
const html = await readFile13(filePath, "utf-8");
|
|
32084
32182
|
const outcome = syncSubCompositionDurationHtml(html, beat.id, newDurationSec, fileRel);
|
|
32085
32183
|
results.push({ ...outcome, file: fileRel, beatId: beat.id });
|
|
32086
32184
|
}
|
|
@@ -32104,7 +32202,7 @@ var init_sub_comp_duration_sync = __esm({
|
|
|
32104
32202
|
});
|
|
32105
32203
|
|
|
32106
32204
|
// src/commands/_shared/scene-repair.ts
|
|
32107
|
-
import { readFile as
|
|
32205
|
+
import { readFile as readFile14, writeFile as writeFile14 } from "node:fs/promises";
|
|
32108
32206
|
import { relative as relative5, resolve as resolve16 } from "node:path";
|
|
32109
32207
|
import { runHyperframeLint as runHyperframeLint3 } from "@hyperframes/producer";
|
|
32110
32208
|
async function executeSceneRepair(opts) {
|
|
@@ -32150,7 +32248,7 @@ async function executeSceneRepair(opts) {
|
|
|
32150
32248
|
}
|
|
32151
32249
|
for (const target of targets) {
|
|
32152
32250
|
const rel = relative5(projectDir, target.abs) || target.abs;
|
|
32153
|
-
const html = await
|
|
32251
|
+
const html = await readFile14(target.abs, "utf-8");
|
|
32154
32252
|
const findings = lintHtml(html, rel, target.isSub);
|
|
32155
32253
|
const { html: nextHtml, fixedCodes } = applyMechanicalFixes(html, findings);
|
|
32156
32254
|
if (fixedCodes.length > 0) {
|
|
@@ -32522,15 +32620,15 @@ var init_storyboard_edit = __esm({
|
|
|
32522
32620
|
});
|
|
32523
32621
|
|
|
32524
32622
|
// src/pipeline/renderers/chrome.ts
|
|
32525
|
-
import { existsSync as
|
|
32623
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
32526
32624
|
import { homedir as homedir5 } from "node:os";
|
|
32527
|
-
import { join as
|
|
32625
|
+
import { join as join14 } from "node:path";
|
|
32528
32626
|
function findChrome() {
|
|
32529
32627
|
const candidates = [
|
|
32530
32628
|
process.env.HYPERFRAMES_CHROME_PATH,
|
|
32531
32629
|
process.env.CHROME_PATH,
|
|
32532
32630
|
// puppeteer auto-downloaded headless shell
|
|
32533
|
-
|
|
32631
|
+
join14(
|
|
32534
32632
|
homedir5(),
|
|
32535
32633
|
".cache",
|
|
32536
32634
|
"puppeteer",
|
|
@@ -32548,7 +32646,7 @@ function findChrome() {
|
|
|
32548
32646
|
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
|
|
32549
32647
|
];
|
|
32550
32648
|
for (const c of candidates) {
|
|
32551
|
-
if (c &&
|
|
32649
|
+
if (c && existsSync15(c)) return c;
|
|
32552
32650
|
}
|
|
32553
32651
|
return void 0;
|
|
32554
32652
|
}
|
|
@@ -32580,7 +32678,7 @@ var init_ffmpeg_gate = __esm({
|
|
|
32580
32678
|
});
|
|
32581
32679
|
|
|
32582
32680
|
// src/commands/_shared/scene-audio-scan.ts
|
|
32583
|
-
import { readFile as
|
|
32681
|
+
import { readFile as readFile16 } from "node:fs/promises";
|
|
32584
32682
|
import { resolve as resolve18 } from "node:path";
|
|
32585
32683
|
function parseRootClips(rootHtml) {
|
|
32586
32684
|
const clips = [];
|
|
@@ -32621,7 +32719,7 @@ function makeFsCompositionReader(projectDir) {
|
|
|
32621
32719
|
return async (compositionSrcRel) => {
|
|
32622
32720
|
const abs = resolve18(projectDir, compositionSrcRel);
|
|
32623
32721
|
try {
|
|
32624
|
-
return await
|
|
32722
|
+
return await readFile16(abs, "utf-8");
|
|
32625
32723
|
} catch {
|
|
32626
32724
|
return null;
|
|
32627
32725
|
}
|
|
@@ -32821,9 +32919,9 @@ __export(scene_render_exports, {
|
|
|
32821
32919
|
qualityToCrf: () => qualityToCrf
|
|
32822
32920
|
});
|
|
32823
32921
|
import { execFile as execFile2 } from "node:child_process";
|
|
32824
|
-
import { existsSync as
|
|
32825
|
-
import { mkdir as mkdir9, readFile as
|
|
32826
|
-
import { resolve as resolve20, relative as relative7, dirname as dirname8, basename as basename4, join as
|
|
32922
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
32923
|
+
import { mkdir as mkdir9, readFile as readFile17, stat as stat3, writeFile as writeFile16 } from "node:fs/promises";
|
|
32924
|
+
import { resolve as resolve20, relative as relative7, dirname as dirname8, basename as basename4, join as join15, isAbsolute } from "node:path";
|
|
32827
32925
|
import { promisify as promisify2 } from "node:util";
|
|
32828
32926
|
import {
|
|
32829
32927
|
createRenderJob,
|
|
@@ -32915,7 +33013,7 @@ async function executeSceneRender(opts = {}) {
|
|
|
32915
33013
|
}
|
|
32916
33014
|
let autoSyncApplied = false;
|
|
32917
33015
|
let autoSyncWarning;
|
|
32918
|
-
if (!opts.beatId &&
|
|
33016
|
+
if (!opts.beatId && existsSync16(join15(projectDir, "STORYBOARD.md"))) {
|
|
32919
33017
|
try {
|
|
32920
33018
|
const rootPlan = await createProjectRootSyncPlan({ projectDir });
|
|
32921
33019
|
const subPlans = await createSubCompDurationSyncPlans({
|
|
@@ -33007,7 +33105,7 @@ async function executeSceneRender(opts = {}) {
|
|
|
33007
33105
|
let audioMuxWarning;
|
|
33008
33106
|
try {
|
|
33009
33107
|
opts.onProgress?.(0.95, "Mixing audio");
|
|
33010
|
-
const rootHtml = await
|
|
33108
|
+
const rootHtml = await readFile17(resolve20(projectDir, root), "utf-8");
|
|
33011
33109
|
const audios = await scanSceneAudio({ projectDir, rootHtml });
|
|
33012
33110
|
audioCount = audios.length;
|
|
33013
33111
|
if (audios.length > 0) {
|
|
@@ -33092,11 +33190,11 @@ async function safeStat(p) {
|
|
|
33092
33190
|
}
|
|
33093
33191
|
async function prepareBeatRenderRoot(opts) {
|
|
33094
33192
|
const root = `.vibeframe/tmp/render-beat-${sanitizeFileSegment(opts.beatId)}.html`;
|
|
33095
|
-
const reportPath =
|
|
33193
|
+
const reportPath = join15(opts.projectDir, "build-report.json");
|
|
33096
33194
|
const retryWith = [
|
|
33097
33195
|
`vibe build ${opts.projectDir} --beat ${opts.beatId} --stage sync --json`
|
|
33098
33196
|
];
|
|
33099
|
-
if (!
|
|
33197
|
+
if (!existsSync16(reportPath)) {
|
|
33100
33198
|
return {
|
|
33101
33199
|
success: false,
|
|
33102
33200
|
root,
|
|
@@ -33107,7 +33205,7 @@ async function prepareBeatRenderRoot(opts) {
|
|
|
33107
33205
|
}
|
|
33108
33206
|
let report;
|
|
33109
33207
|
try {
|
|
33110
|
-
report = JSON.parse(await
|
|
33208
|
+
report = JSON.parse(await readFile17(reportPath, "utf-8"));
|
|
33111
33209
|
} catch (error) {
|
|
33112
33210
|
return {
|
|
33113
33211
|
success: false,
|
|
@@ -33176,8 +33274,8 @@ async function prepareBeatRenderRoot(opts) {
|
|
|
33176
33274
|
durationSec,
|
|
33177
33275
|
narrationPath
|
|
33178
33276
|
});
|
|
33179
|
-
await mkdir9(dirname8(
|
|
33180
|
-
await writeFile16(
|
|
33277
|
+
await mkdir9(dirname8(join15(opts.projectDir, root)), { recursive: true });
|
|
33278
|
+
await writeFile16(join15(opts.projectDir, root), html, "utf-8");
|
|
33181
33279
|
return {
|
|
33182
33280
|
success: true,
|
|
33183
33281
|
root,
|
|
@@ -33241,7 +33339,7 @@ function reportedBeatAssetPaths(beat) {
|
|
|
33241
33339
|
]);
|
|
33242
33340
|
}
|
|
33243
33341
|
async function writeRenderReport(projectDir, result) {
|
|
33244
|
-
const reportPath =
|
|
33342
|
+
const reportPath = join15(projectDir, "render-report.json");
|
|
33245
33343
|
try {
|
|
33246
33344
|
await writeFile16(
|
|
33247
33345
|
reportPath,
|
|
@@ -33283,7 +33381,7 @@ async function writeRenderReport(projectDir, result) {
|
|
|
33283
33381
|
}
|
|
33284
33382
|
function pathExists3(projectDir, value) {
|
|
33285
33383
|
const abs = isAbsolute(value) ? value : resolve20(projectDir, value);
|
|
33286
|
-
return
|
|
33384
|
+
return existsSync16(abs);
|
|
33287
33385
|
}
|
|
33288
33386
|
function stringValue(value) {
|
|
33289
33387
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
@@ -33356,7 +33454,7 @@ var init_concurrency = __esm({
|
|
|
33356
33454
|
});
|
|
33357
33455
|
|
|
33358
33456
|
// src/commands/_shared/build-asset-reference.ts
|
|
33359
|
-
import { existsSync as
|
|
33457
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
33360
33458
|
import { extname as extname3, isAbsolute as isAbsolute2, relative as relative8, resolve as resolve21, sep } from "node:path";
|
|
33361
33459
|
function resolveTypedAssetReference(projectDir, kind, value) {
|
|
33362
33460
|
const raw = stringOrUndefined2(value);
|
|
@@ -33387,7 +33485,7 @@ function resolveReference(projectDir, kind, raw, source, forcedError) {
|
|
|
33387
33485
|
const absPath = isAbsolute2(raw) ? resolve21(raw) : resolve21(projectDir, raw);
|
|
33388
33486
|
const rel = relative8(resolve21(projectDir), absPath);
|
|
33389
33487
|
const withinProject = rel.length > 0 && !rel.startsWith("..") && !isAbsolute2(rel);
|
|
33390
|
-
const exists = withinProject &&
|
|
33488
|
+
const exists = withinProject && existsSync17(absPath);
|
|
33391
33489
|
let error = forcedError;
|
|
33392
33490
|
if (!error && inferred && inferred !== kind && !isNarrationAudioReference(kind, ext)) {
|
|
33393
33491
|
error = `Asset reference "${raw}" is a ${inferred} asset, not ${kind}.`;
|
|
@@ -33574,9 +33672,9 @@ var init_build_backdrop_prompt = __esm({
|
|
|
33574
33672
|
});
|
|
33575
33673
|
|
|
33576
33674
|
// src/commands/_shared/build-plan.ts
|
|
33577
|
-
import { existsSync as
|
|
33578
|
-
import { readFile as
|
|
33579
|
-
import { dirname as dirname9, join as
|
|
33675
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
33676
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
33677
|
+
import { dirname as dirname9, join as join16, resolve as resolve22 } from "node:path";
|
|
33580
33678
|
function backdropCostUsd(quality) {
|
|
33581
33679
|
return quality === "hd" ? 0.2 : 0.05;
|
|
33582
33680
|
}
|
|
@@ -33585,11 +33683,11 @@ async function createBuildPlan(opts) {
|
|
|
33585
33683
|
loadPlanEnv(projectDir);
|
|
33586
33684
|
const stage = opts.stage ?? "all";
|
|
33587
33685
|
const config2 = await readProjectConfig(projectDir);
|
|
33588
|
-
const storyboardPath =
|
|
33686
|
+
const storyboardPath = join16(projectDir, "STORYBOARD.md");
|
|
33589
33687
|
const warnings = [];
|
|
33590
33688
|
const retryWith = [];
|
|
33591
33689
|
const providerResolution = [];
|
|
33592
|
-
if (!
|
|
33690
|
+
if (!existsSync18(storyboardPath)) {
|
|
33593
33691
|
const validation2 = {
|
|
33594
33692
|
ok: false,
|
|
33595
33693
|
issues: [
|
|
@@ -33618,7 +33716,7 @@ async function createBuildPlan(opts) {
|
|
|
33618
33716
|
validation: validation2
|
|
33619
33717
|
});
|
|
33620
33718
|
}
|
|
33621
|
-
const storyboardMd = await
|
|
33719
|
+
const storyboardMd = await readFile18(storyboardPath, "utf-8");
|
|
33622
33720
|
const validation = validateStoryboardMarkdown(storyboardMd);
|
|
33623
33721
|
const validationIssues = [...validation.issues];
|
|
33624
33722
|
const parsed = parseStoryboard(storyboardMd);
|
|
@@ -33644,7 +33742,7 @@ async function createBuildPlan(opts) {
|
|
|
33644
33742
|
const imageQuality = opts.imageQuality ?? config2.config.build.imageQuality ?? "hd";
|
|
33645
33743
|
const imageSize2 = opts.imageSize ?? config2.config.build.imageSize ?? "1536x1024";
|
|
33646
33744
|
const imageRatio = imageRatioForSize(imageSize2);
|
|
33647
|
-
const needsComposer = includeCompose && mode !== "agent" && sourceBeats.some((beat) => !
|
|
33745
|
+
const needsComposer = includeCompose && mode !== "agent" && sourceBeats.some((beat) => !existsSync18(join16(projectDir, `compositions/scene-${beat.id}.html`)));
|
|
33648
33746
|
const resolved = await resolvePlanProviders({
|
|
33649
33747
|
projectDir,
|
|
33650
33748
|
config: config2,
|
|
@@ -33792,7 +33890,7 @@ async function createBuildPlan(opts) {
|
|
|
33792
33890
|
active: includeAssets
|
|
33793
33891
|
}) : null;
|
|
33794
33892
|
const compositionPath = `compositions/scene-${beat.id}.html`;
|
|
33795
|
-
const compositionExists =
|
|
33893
|
+
const compositionExists = existsSync18(join16(projectDir, compositionPath));
|
|
33796
33894
|
for (const asset of [narration, backdrop, keyframe, video, music]) {
|
|
33797
33895
|
if (!asset) continue;
|
|
33798
33896
|
if (asset.referenceError) {
|
|
@@ -33861,7 +33959,7 @@ async function createBuildPlan(opts) {
|
|
|
33861
33959
|
}
|
|
33862
33960
|
}
|
|
33863
33961
|
const validationOk = !validationIssues.some((issue) => issue.severity === "error");
|
|
33864
|
-
if (!
|
|
33962
|
+
if (!existsSync18(join16(projectDir, config2.config.composition.entry))) {
|
|
33865
33963
|
missing.add("root-composition");
|
|
33866
33964
|
if (validationOk) retryWith.push(`vibe build ${projectDir} --stage sync --json`);
|
|
33867
33965
|
}
|
|
@@ -33895,7 +33993,7 @@ async function createBuildPlan(opts) {
|
|
|
33895
33993
|
}
|
|
33896
33994
|
if (referenced.size > 0) {
|
|
33897
33995
|
const toGenerate = resolveCharacters(parsed.frontmatter).filter(
|
|
33898
|
-
(c) => referenced.has(c.name) && c.prompt && (opts.force || !
|
|
33996
|
+
(c) => referenced.has(c.name) && c.prompt && (opts.force || !existsSync18(join16(projectDir, `assets/character-${c.name}.png`)))
|
|
33899
33997
|
);
|
|
33900
33998
|
if (toGenerate.length > 0) {
|
|
33901
33999
|
estimatedCostUsd += toGenerate.length * backdropCostUsd(opts.imageQuality ?? "hd");
|
|
@@ -34008,8 +34106,8 @@ function assetPlan(opts) {
|
|
|
34008
34106
|
reason: ready ? "referenced-asset" : "invalid-reference"
|
|
34009
34107
|
};
|
|
34010
34108
|
}
|
|
34011
|
-
const canonicalExists =
|
|
34012
|
-
const cacheHit = opts.cache ?
|
|
34109
|
+
const canonicalExists = existsSync18(join16(opts.projectDir, opts.path));
|
|
34110
|
+
const cacheHit = opts.cache ? existsSync18(join16(opts.projectDir, opts.cache.path)) : false;
|
|
34013
34111
|
const freshness = assetFreshnessFromMetadata({
|
|
34014
34112
|
projectDir: opts.projectDir,
|
|
34015
34113
|
kind: opts.kind,
|
|
@@ -34207,7 +34305,7 @@ async function firstConfiguredComposer(projectDir) {
|
|
|
34207
34305
|
}
|
|
34208
34306
|
function firstExisting2(projectDir, paths) {
|
|
34209
34307
|
for (const path2 of paths) {
|
|
34210
|
-
if (
|
|
34308
|
+
if (existsSync18(join16(projectDir, path2))) return path2;
|
|
34211
34309
|
}
|
|
34212
34310
|
return null;
|
|
34213
34311
|
}
|
|
@@ -34248,12 +34346,12 @@ function unique3(items) {
|
|
|
34248
34346
|
return [...new Set(items.filter((item) => item.length > 0))];
|
|
34249
34347
|
}
|
|
34250
34348
|
function loadPlanEnv(projectDir) {
|
|
34251
|
-
(0, import_dotenv2.config)({ path:
|
|
34349
|
+
(0, import_dotenv2.config)({ path: join16(projectDir, ".env"), quiet: true });
|
|
34252
34350
|
(0, import_dotenv2.config)({ path: resolve22(process.cwd(), ".env"), quiet: true });
|
|
34253
34351
|
let dir = process.cwd();
|
|
34254
34352
|
while (dir !== dirname9(dir)) {
|
|
34255
|
-
if (
|
|
34256
|
-
(0, import_dotenv2.config)({ path:
|
|
34353
|
+
if (existsSync18(join16(dir, "pnpm-workspace.yaml"))) {
|
|
34354
|
+
(0, import_dotenv2.config)({ path: join16(dir, ".env"), quiet: true });
|
|
34257
34355
|
return;
|
|
34258
34356
|
}
|
|
34259
34357
|
dir = dirname9(dir);
|
|
@@ -34315,7 +34413,7 @@ var init_validate = __esm({
|
|
|
34315
34413
|
// src/commands/generate/music.ts
|
|
34316
34414
|
import { resolve as resolve24 } from "node:path";
|
|
34317
34415
|
import { writeFile as writeFile17 } from "node:fs/promises";
|
|
34318
|
-
import { existsSync as
|
|
34416
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
34319
34417
|
async function executeMusic(options) {
|
|
34320
34418
|
try {
|
|
34321
34419
|
const provider = options.provider || "elevenlabs";
|
|
@@ -34468,7 +34566,7 @@ function registerMusicCommand(parent) {
|
|
|
34468
34566
|
if (options.melody) {
|
|
34469
34567
|
spinner2.text = "Uploading melody reference...";
|
|
34470
34568
|
const absPath = resolve24(process.cwd(), options.melody);
|
|
34471
|
-
if (!
|
|
34569
|
+
if (!existsSync19(absPath)) {
|
|
34472
34570
|
spinner2.fail(`Melody file not found: ${options.melody}`);
|
|
34473
34571
|
exitWithError(notFoundError(options.melody));
|
|
34474
34572
|
}
|
|
@@ -34601,9 +34699,9 @@ __export(scene_build_exports, {
|
|
|
34601
34699
|
executeSceneBuild: () => executeSceneBuild,
|
|
34602
34700
|
resolveSceneBuildMode: () => resolveSceneBuildMode
|
|
34603
34701
|
});
|
|
34604
|
-
import { existsSync as
|
|
34605
|
-
import { copyFile as copyFile3, mkdir as mkdir10, readFile as
|
|
34606
|
-
import { dirname as dirname10, join as
|
|
34702
|
+
import { existsSync as existsSync20, readFileSync as readFileSync3 } from "node:fs";
|
|
34703
|
+
import { copyFile as copyFile3, mkdir as mkdir10, readFile as readFile19, rm, writeFile as writeFile18 } from "node:fs/promises";
|
|
34704
|
+
import { dirname as dirname10, join as join17, resolve as resolve25 } from "node:path";
|
|
34607
34705
|
async function executeSceneBuild(opts) {
|
|
34608
34706
|
const startedAt = Date.now();
|
|
34609
34707
|
const projectDir = resolve25(opts.projectDir);
|
|
@@ -34622,7 +34720,7 @@ async function executeSceneBuild(opts) {
|
|
|
34622
34720
|
);
|
|
34623
34721
|
}
|
|
34624
34722
|
let sceneRepair = skippedSceneRepairSummary();
|
|
34625
|
-
const storyboardPath =
|
|
34723
|
+
const storyboardPath = join17(projectDir, "STORYBOARD.md");
|
|
34626
34724
|
const buildPlan = await createBuildPlan({
|
|
34627
34725
|
projectDir,
|
|
34628
34726
|
stage: selectedStage,
|
|
@@ -34673,8 +34771,8 @@ async function executeSceneBuild(opts) {
|
|
|
34673
34771
|
}
|
|
34674
34772
|
if (!buildPlan.validation.ok) {
|
|
34675
34773
|
let invalidBeats = [];
|
|
34676
|
-
if (
|
|
34677
|
-
invalidBeats = parseStoryboard(await
|
|
34774
|
+
if (existsSync20(storyboardPath)) {
|
|
34775
|
+
invalidBeats = parseStoryboard(await readFile19(storyboardPath, "utf-8")).beats;
|
|
34678
34776
|
}
|
|
34679
34777
|
return finishBuildResult({
|
|
34680
34778
|
success: false,
|
|
@@ -34698,7 +34796,7 @@ async function executeSceneBuild(opts) {
|
|
|
34698
34796
|
totalLatencyMs: Date.now() - startedAt
|
|
34699
34797
|
});
|
|
34700
34798
|
}
|
|
34701
|
-
if (!
|
|
34799
|
+
if (!existsSync20(storyboardPath)) {
|
|
34702
34800
|
return finishBuildResult({
|
|
34703
34801
|
success: false,
|
|
34704
34802
|
phase: "failed",
|
|
@@ -34712,7 +34810,7 @@ async function executeSceneBuild(opts) {
|
|
|
34712
34810
|
totalLatencyMs: Date.now() - startedAt
|
|
34713
34811
|
});
|
|
34714
34812
|
}
|
|
34715
|
-
const storyboardMd = await
|
|
34813
|
+
const storyboardMd = await readFile19(storyboardPath, "utf-8");
|
|
34716
34814
|
const parsed = parseStoryboard(storyboardMd);
|
|
34717
34815
|
if (parsed.beats.length === 0) {
|
|
34718
34816
|
return finishBuildResult({
|
|
@@ -34819,6 +34917,7 @@ async function executeSceneBuild(opts) {
|
|
|
34819
34917
|
skipVideo: opts.skipVideo ?? false,
|
|
34820
34918
|
skipKeyframe: opts.skipKeyframe ?? false,
|
|
34821
34919
|
skipMusic: opts.skipMusic ?? false,
|
|
34920
|
+
skipTranscript: opts.skipTranscript ?? false,
|
|
34822
34921
|
force: opts.force ?? false,
|
|
34823
34922
|
onProgress,
|
|
34824
34923
|
characterPaths
|
|
@@ -34906,9 +35005,9 @@ async function executeSceneBuild(opts) {
|
|
|
34906
35005
|
let composeData;
|
|
34907
35006
|
if (shouldRunStage(selectedStage, "compose")) {
|
|
34908
35007
|
if (mode === "agent") {
|
|
34909
|
-
const compositionsDir =
|
|
35008
|
+
const compositionsDir = join17(projectDir, "compositions");
|
|
34910
35009
|
const missingBeats = activeBeats.filter(
|
|
34911
|
-
(b) => !
|
|
35010
|
+
(b) => !existsSync20(join17(compositionsDir, `scene-${b.id}.html`))
|
|
34912
35011
|
);
|
|
34913
35012
|
if (missingBeats.length > 0) {
|
|
34914
35013
|
const plan = await getComposePrompts({ projectDir, beatId: opts.beatId });
|
|
@@ -34997,8 +35096,8 @@ async function executeSceneBuild(opts) {
|
|
|
34997
35096
|
if (stageReports.compose.status === "done") {
|
|
34998
35097
|
for (const beat of activeBeats) {
|
|
34999
35098
|
const videoRel = `assets/video-${beat.id}.mp4`;
|
|
35000
|
-
const compositionAbs =
|
|
35001
|
-
if (!
|
|
35099
|
+
const compositionAbs = join17(projectDir, "compositions", `scene-${beat.id}.html`);
|
|
35100
|
+
if (!existsSync20(join17(projectDir, videoRel)) || !existsSync20(compositionAbs)) continue;
|
|
35002
35101
|
const html = readFileSync3(compositionAbs, "utf-8");
|
|
35003
35102
|
if (!html.includes(videoRel)) {
|
|
35004
35103
|
warnings.push(
|
|
@@ -35056,7 +35155,7 @@ async function executeSceneBuild(opts) {
|
|
|
35056
35155
|
});
|
|
35057
35156
|
}
|
|
35058
35157
|
if (shouldRunStage(selectedStage, "sync")) {
|
|
35059
|
-
if (!
|
|
35158
|
+
if (!existsSync20(join17(projectDir, "index.html"))) {
|
|
35060
35159
|
await scaffoldSceneProject({
|
|
35061
35160
|
dir: projectDir,
|
|
35062
35161
|
name: projectDir.split(/[\\/]/).filter(Boolean).pop(),
|
|
@@ -35201,6 +35300,9 @@ async function buildBeatPrimitives(beat, ctx) {
|
|
|
35201
35300
|
ctx.skipVideo ? skipped("video", beat.id, "--skip-video", ctx) : dispatchVideo(beat, ctx, keyframe),
|
|
35202
35301
|
ctx.skipMusic ? skipped("music", beat.id, "--skip-music", ctx) : dispatchMusic(beat, ctx)
|
|
35203
35302
|
]);
|
|
35303
|
+
if (!ctx.skipTranscript) {
|
|
35304
|
+
await dispatchTranscript(beat, narration, ctx);
|
|
35305
|
+
}
|
|
35204
35306
|
return {
|
|
35205
35307
|
outcome: {
|
|
35206
35308
|
beatId: beat.id,
|
|
@@ -35318,7 +35420,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35318
35420
|
return {
|
|
35319
35421
|
status: "cached",
|
|
35320
35422
|
path: existingRel,
|
|
35321
|
-
durationSec: await safeAudioDuration(
|
|
35423
|
+
durationSec: await safeAudioDuration(join17(ctx.projectDir, existingRel)),
|
|
35322
35424
|
provider: metadata?.provider,
|
|
35323
35425
|
cachePath: metadata?.cachePath,
|
|
35324
35426
|
cacheKey: metadata?.cacheKey,
|
|
@@ -35342,9 +35444,9 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35342
35444
|
voice,
|
|
35343
35445
|
ext: resolution.audioExtension
|
|
35344
35446
|
});
|
|
35345
|
-
const cacheAbs =
|
|
35447
|
+
const cacheAbs = join17(ctx.projectDir, cache.path);
|
|
35346
35448
|
const rel = `assets/narration-${beat.id}.${resolution.audioExtension}`;
|
|
35347
|
-
const abs =
|
|
35449
|
+
const abs = join17(ctx.projectDir, rel);
|
|
35348
35450
|
if (existingRel && !ctx.force && isFreshCanonicalAsset({
|
|
35349
35451
|
projectDir: ctx.projectDir,
|
|
35350
35452
|
kind: "narration",
|
|
@@ -35358,7 +35460,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35358
35460
|
return {
|
|
35359
35461
|
status: "cached",
|
|
35360
35462
|
path: existingRel,
|
|
35361
|
-
durationSec: await safeAudioDuration(
|
|
35463
|
+
durationSec: await safeAudioDuration(join17(ctx.projectDir, existingRel)),
|
|
35362
35464
|
provider: resolution.provider,
|
|
35363
35465
|
cachePath: cache.path,
|
|
35364
35466
|
cacheKey: cache.key,
|
|
@@ -35366,7 +35468,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35366
35468
|
freshness: "fresh"
|
|
35367
35469
|
};
|
|
35368
35470
|
}
|
|
35369
|
-
if (
|
|
35471
|
+
if (existsSync20(cacheAbs) && !ctx.force) {
|
|
35370
35472
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35371
35473
|
await copyFile3(cacheAbs, abs);
|
|
35372
35474
|
await writeAssetMetadata({
|
|
@@ -35401,8 +35503,8 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35401
35503
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35402
35504
|
await writeFile18(abs, result.audioBuffer);
|
|
35403
35505
|
for (const otherExt of ["mp3", "wav"]) {
|
|
35404
|
-
const sibling =
|
|
35405
|
-
if (sibling !== abs &&
|
|
35506
|
+
const sibling = join17(ctx.projectDir, `assets/narration-${beat.id}.${otherExt}`);
|
|
35507
|
+
if (sibling !== abs && existsSync20(sibling)) await rm(sibling, { force: true });
|
|
35406
35508
|
}
|
|
35407
35509
|
await mkdir10(dirname10(cacheAbs), { recursive: true });
|
|
35408
35510
|
await writeFile18(cacheAbs, result.audioBuffer);
|
|
@@ -35434,6 +35536,42 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35434
35536
|
freshness: "fresh"
|
|
35435
35537
|
};
|
|
35436
35538
|
}
|
|
35539
|
+
async function dispatchTranscript(beat, narration, ctx) {
|
|
35540
|
+
if (!narration.path) return;
|
|
35541
|
+
const relPath = beatTranscriptRelPath(beat.id);
|
|
35542
|
+
const abs = join17(ctx.projectDir, relPath);
|
|
35543
|
+
const narrationFresh = narration.status === "generated";
|
|
35544
|
+
if (existsSync20(abs) && !ctx.force && !narrationFresh) {
|
|
35545
|
+
ctx.onProgress({ type: "transcript-cached", beatId: beat.id, path: relPath });
|
|
35546
|
+
return;
|
|
35547
|
+
}
|
|
35548
|
+
const apiKey = await getConfiguredApiKey("OPENAI_API_KEY", void 0, { cwd: ctx.projectDir });
|
|
35549
|
+
if (!apiKey) {
|
|
35550
|
+
ctx.onProgress({
|
|
35551
|
+
type: "transcript-skipped",
|
|
35552
|
+
beatId: beat.id,
|
|
35553
|
+
reason: "OPENAI_API_KEY not set \u2014 narration plays but word-sync timings unavailable"
|
|
35554
|
+
});
|
|
35555
|
+
return;
|
|
35556
|
+
}
|
|
35557
|
+
const words = await transcribeNarrationWords(join17(ctx.projectDir, narration.path), { apiKey });
|
|
35558
|
+
if (words.length === 0) {
|
|
35559
|
+
ctx.onProgress({
|
|
35560
|
+
type: "transcript-skipped",
|
|
35561
|
+
beatId: beat.id,
|
|
35562
|
+
reason: "transcription returned no word timings"
|
|
35563
|
+
});
|
|
35564
|
+
return;
|
|
35565
|
+
}
|
|
35566
|
+
await mkdir10(dirname10(abs), { recursive: true });
|
|
35567
|
+
await writeFile18(abs, JSON.stringify(words, null, 2), "utf-8");
|
|
35568
|
+
ctx.onProgress({
|
|
35569
|
+
type: "transcript-generated",
|
|
35570
|
+
beatId: beat.id,
|
|
35571
|
+
path: relPath,
|
|
35572
|
+
wordCount: words.length
|
|
35573
|
+
});
|
|
35574
|
+
}
|
|
35437
35575
|
function characterSheetPrompt(name, description) {
|
|
35438
35576
|
return [
|
|
35439
35577
|
`Character reference sheet of an original fictional character named ${name}: ${description}.`,
|
|
@@ -35451,7 +35589,7 @@ async function buildCharacters(characters, ctx) {
|
|
|
35451
35589
|
const size = ctx.imageSize ?? "1536x1024";
|
|
35452
35590
|
for (const character of characters) {
|
|
35453
35591
|
if (character.imagePath) {
|
|
35454
|
-
if (
|
|
35592
|
+
if (existsSync20(join17(ctx.projectDir, character.imagePath))) {
|
|
35455
35593
|
paths.set(character.name, character.imagePath);
|
|
35456
35594
|
ctx.onProgress({ type: "character-cached", name: character.name, path: character.imagePath });
|
|
35457
35595
|
} else {
|
|
@@ -35464,7 +35602,7 @@ async function buildCharacters(characters, ctx) {
|
|
|
35464
35602
|
if (!character.prompt) continue;
|
|
35465
35603
|
const prompt3 = characterSheetPrompt(character.name, character.prompt);
|
|
35466
35604
|
const rel = `assets/character-${character.name}.png`;
|
|
35467
|
-
const abs =
|
|
35605
|
+
const abs = join17(ctx.projectDir, rel);
|
|
35468
35606
|
const metadataOptions = { quality: ctx.imageQuality, size };
|
|
35469
35607
|
const cache = characterCacheDescriptor({
|
|
35470
35608
|
name: character.name,
|
|
@@ -35473,7 +35611,7 @@ async function buildCharacters(characters, ctx) {
|
|
|
35473
35611
|
quality: ctx.imageQuality,
|
|
35474
35612
|
size
|
|
35475
35613
|
});
|
|
35476
|
-
if (
|
|
35614
|
+
if (existsSync20(abs) && !ctx.force) {
|
|
35477
35615
|
const metadata = readAssetMetadata(ctx.projectDir, "character", character.name);
|
|
35478
35616
|
if (!metadata || isFreshCanonicalAsset({
|
|
35479
35617
|
projectDir: ctx.projectDir,
|
|
@@ -35489,8 +35627,8 @@ async function buildCharacters(characters, ctx) {
|
|
|
35489
35627
|
continue;
|
|
35490
35628
|
}
|
|
35491
35629
|
}
|
|
35492
|
-
const cacheAbs =
|
|
35493
|
-
if (
|
|
35630
|
+
const cacheAbs = join17(ctx.projectDir, cache.path);
|
|
35631
|
+
if (existsSync20(cacheAbs) && !ctx.force) {
|
|
35494
35632
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35495
35633
|
await copyFile3(cacheAbs, abs);
|
|
35496
35634
|
await writeAssetMetadata({
|
|
@@ -35556,7 +35694,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
35556
35694
|
if (!cue) return { status: "no-cue" };
|
|
35557
35695
|
const prompt3 = augmentBackdropPrompt(cue);
|
|
35558
35696
|
const rel = `assets/backdrop-${beat.id}.png`;
|
|
35559
|
-
const abs =
|
|
35697
|
+
const abs = join17(ctx.projectDir, rel);
|
|
35560
35698
|
const size = ctx.imageSize ?? "1536x1024";
|
|
35561
35699
|
const ratio = imageRatioForSize(size);
|
|
35562
35700
|
const metadataOptions = { quality: ctx.imageQuality, size, ratio };
|
|
@@ -35569,7 +35707,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
35569
35707
|
ratio
|
|
35570
35708
|
});
|
|
35571
35709
|
const metadataPath = assetMetadataPath("backdrop", beat.id);
|
|
35572
|
-
if (
|
|
35710
|
+
if (existsSync20(abs) && !ctx.force) {
|
|
35573
35711
|
const metadata = readAssetMetadata(ctx.projectDir, "backdrop", beat.id);
|
|
35574
35712
|
if (!metadata || isFreshCanonicalAsset({
|
|
35575
35713
|
projectDir: ctx.projectDir,
|
|
@@ -35592,8 +35730,8 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
35592
35730
|
};
|
|
35593
35731
|
}
|
|
35594
35732
|
}
|
|
35595
|
-
const cacheAbs =
|
|
35596
|
-
if (
|
|
35733
|
+
const cacheAbs = join17(ctx.projectDir, cache.path);
|
|
35734
|
+
if (existsSync20(cacheAbs) && !ctx.force) {
|
|
35597
35735
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35598
35736
|
await copyFile3(cacheAbs, abs);
|
|
35599
35737
|
await writeAssetMetadata({
|
|
@@ -35725,7 +35863,7 @@ async function editKeyframeImage(prompt3, sheetBuffers, ctx, ratio) {
|
|
|
35725
35863
|
}
|
|
35726
35864
|
async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
35727
35865
|
const rel = `assets/keyframe-${beat.id}.png`;
|
|
35728
|
-
const abs =
|
|
35866
|
+
const abs = join17(ctx.projectDir, rel);
|
|
35729
35867
|
const size = ctx.imageSize ?? "1536x1024";
|
|
35730
35868
|
const ratio = imageRatioForSize(size);
|
|
35731
35869
|
const metadataOptions = { quality: ctx.imageQuality, size, ratio };
|
|
@@ -35738,7 +35876,7 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
35738
35876
|
ratio,
|
|
35739
35877
|
characters: characterRefsRel
|
|
35740
35878
|
});
|
|
35741
|
-
if (
|
|
35879
|
+
if (existsSync20(abs) && !ctx.force) {
|
|
35742
35880
|
const metadata = readAssetMetadata(ctx.projectDir, "keyframe", beat.id);
|
|
35743
35881
|
if (!metadata || isFreshCanonicalAsset({
|
|
35744
35882
|
projectDir: ctx.projectDir,
|
|
@@ -35752,8 +35890,8 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
35752
35890
|
return { status: "cached", rel };
|
|
35753
35891
|
}
|
|
35754
35892
|
}
|
|
35755
|
-
const cacheAbs =
|
|
35756
|
-
if (
|
|
35893
|
+
const cacheAbs = join17(ctx.projectDir, cache.path);
|
|
35894
|
+
if (existsSync20(cacheAbs) && !ctx.force) {
|
|
35757
35895
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35758
35896
|
await copyFile3(cacheAbs, abs);
|
|
35759
35897
|
await writeAssetMetadata({
|
|
@@ -35771,8 +35909,8 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
35771
35909
|
}
|
|
35772
35910
|
const sheetBuffers = [];
|
|
35773
35911
|
for (const relPath of characterRefsRel) {
|
|
35774
|
-
const sheetAbs =
|
|
35775
|
-
if (
|
|
35912
|
+
const sheetAbs = join17(ctx.projectDir, relPath);
|
|
35913
|
+
if (existsSync20(sheetAbs)) sheetBuffers.push(await readFile19(sheetAbs));
|
|
35776
35914
|
}
|
|
35777
35915
|
const imgCtx = {
|
|
35778
35916
|
projectDir: ctx.projectDir,
|
|
@@ -35841,9 +35979,9 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
35841
35979
|
const prompt3 = stringOrUndefined4(beat.cues?.video) ?? keyframeCue;
|
|
35842
35980
|
if (!prompt3) return { status: "no-cue" };
|
|
35843
35981
|
const characterRefsRel = beatCharacterNames(beat.cues).map((name) => ctx.characterPaths?.get(name)).filter((p) => typeof p === "string");
|
|
35844
|
-
const characterRefsAbs = characterRefsRel.map((p) =>
|
|
35982
|
+
const characterRefsAbs = characterRefsRel.map((p) => join17(ctx.projectDir, p));
|
|
35845
35983
|
const rel = `assets/video-${beat.id}.mp4`;
|
|
35846
|
-
const abs =
|
|
35984
|
+
const abs = join17(ctx.projectDir, rel);
|
|
35847
35985
|
const cache = videoCacheDescriptor({
|
|
35848
35986
|
beatId: beat.id,
|
|
35849
35987
|
cue: prompt3,
|
|
@@ -35855,7 +35993,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
35855
35993
|
keyframe: keyframeCue
|
|
35856
35994
|
});
|
|
35857
35995
|
const metadataPath = assetMetadataPath("video", beat.id);
|
|
35858
|
-
if (
|
|
35996
|
+
if (existsSync20(abs) && !ctx.force) {
|
|
35859
35997
|
const metadata = readAssetMetadata(ctx.projectDir, "video", beat.id);
|
|
35860
35998
|
if (!metadata || isFreshCanonicalAsset({
|
|
35861
35999
|
projectDir: ctx.projectDir,
|
|
@@ -35878,8 +36016,8 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
35878
36016
|
};
|
|
35879
36017
|
}
|
|
35880
36018
|
}
|
|
35881
|
-
const cacheAbs =
|
|
35882
|
-
if (
|
|
36019
|
+
const cacheAbs = join17(ctx.projectDir, cache.path);
|
|
36020
|
+
if (existsSync20(cacheAbs) && !ctx.force) {
|
|
35883
36021
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35884
36022
|
await copyFile3(cacheAbs, abs);
|
|
35885
36023
|
await writeAssetMetadata({
|
|
@@ -35910,7 +36048,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
35910
36048
|
ctx.onProgress({ type: "video-skipped", beatId: beat.id, reason });
|
|
35911
36049
|
return { status: "skipped" };
|
|
35912
36050
|
}
|
|
35913
|
-
const keyframeImageAbs = keyframeImageRel ?
|
|
36051
|
+
const keyframeImageAbs = keyframeImageRel ? join17(ctx.projectDir, keyframeImageRel) : void 0;
|
|
35914
36052
|
loadSceneBuildEnv(ctx.projectDir);
|
|
35915
36053
|
const result = await executeVideoGenerate({
|
|
35916
36054
|
prompt: prompt3,
|
|
@@ -35934,7 +36072,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
35934
36072
|
ctx.onProgress({ type: "video-failed", beatId: beat.id, error });
|
|
35935
36073
|
return { status: "failed", error };
|
|
35936
36074
|
}
|
|
35937
|
-
if (result.status === "completed" &&
|
|
36075
|
+
if (result.status === "completed" && existsSync20(abs)) {
|
|
35938
36076
|
await mkdir10(dirname10(cacheAbs), { recursive: true });
|
|
35939
36077
|
await copyFile3(abs, cacheAbs);
|
|
35940
36078
|
await writeAssetMetadata({
|
|
@@ -36002,7 +36140,7 @@ async function dispatchMusic(beat, ctx) {
|
|
|
36002
36140
|
const prompt3 = stringOrUndefined4(beat.cues?.music);
|
|
36003
36141
|
if (!prompt3) return { status: "no-cue" };
|
|
36004
36142
|
const rel = `assets/music-${beat.id}.mp3`;
|
|
36005
|
-
const abs =
|
|
36143
|
+
const abs = join17(ctx.projectDir, rel);
|
|
36006
36144
|
const duration = normalizeMusicDuration(beat.duration);
|
|
36007
36145
|
const cache = musicCacheDescriptor({
|
|
36008
36146
|
beatId: beat.id,
|
|
@@ -36011,7 +36149,7 @@ async function dispatchMusic(beat, ctx) {
|
|
|
36011
36149
|
duration
|
|
36012
36150
|
});
|
|
36013
36151
|
const metadataPath = assetMetadataPath("music", beat.id);
|
|
36014
|
-
if (
|
|
36152
|
+
if (existsSync20(abs) && !ctx.force) {
|
|
36015
36153
|
const metadata = readAssetMetadata(ctx.projectDir, "music", beat.id);
|
|
36016
36154
|
if (!metadata || isFreshCanonicalAsset({
|
|
36017
36155
|
projectDir: ctx.projectDir,
|
|
@@ -36035,8 +36173,8 @@ async function dispatchMusic(beat, ctx) {
|
|
|
36035
36173
|
};
|
|
36036
36174
|
}
|
|
36037
36175
|
}
|
|
36038
|
-
const cacheAbs =
|
|
36039
|
-
if (
|
|
36176
|
+
const cacheAbs = join17(ctx.projectDir, cache.path);
|
|
36177
|
+
if (existsSync20(cacheAbs) && !ctx.force) {
|
|
36040
36178
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
36041
36179
|
await copyFile3(cacheAbs, abs);
|
|
36042
36180
|
await writeAssetMetadata({
|
|
@@ -36110,7 +36248,7 @@ async function dispatchMusic(beat, ctx) {
|
|
|
36110
36248
|
metadataPath
|
|
36111
36249
|
};
|
|
36112
36250
|
}
|
|
36113
|
-
if (!
|
|
36251
|
+
if (!existsSync20(abs)) {
|
|
36114
36252
|
const error = result.outputPath ? `music output was not written at ${abs}` : "music generation did not return an output file";
|
|
36115
36253
|
ctx.onProgress({ type: "music-failed", beatId: beat.id, error });
|
|
36116
36254
|
return { status: "failed", error };
|
|
@@ -36146,12 +36284,12 @@ async function dispatchMusic(beat, ctx) {
|
|
|
36146
36284
|
};
|
|
36147
36285
|
}
|
|
36148
36286
|
function loadSceneBuildEnv(projectDir) {
|
|
36149
|
-
(0, import_dotenv3.config)({ path:
|
|
36287
|
+
(0, import_dotenv3.config)({ path: join17(projectDir, ".env"), quiet: true });
|
|
36150
36288
|
(0, import_dotenv3.config)({ path: resolve25(process.cwd(), ".env"), quiet: true });
|
|
36151
36289
|
let dir = process.cwd();
|
|
36152
36290
|
while (dir !== dirname10(dir)) {
|
|
36153
|
-
if (
|
|
36154
|
-
(0, import_dotenv3.config)({ path:
|
|
36291
|
+
if (existsSync20(join17(dir, "pnpm-workspace.yaml"))) {
|
|
36292
|
+
(0, import_dotenv3.config)({ path: join17(dir, ".env"), quiet: true });
|
|
36155
36293
|
return;
|
|
36156
36294
|
}
|
|
36157
36295
|
dir = dirname10(dir);
|
|
@@ -36367,7 +36505,7 @@ function mergeBeatOutcomes(base, updates) {
|
|
|
36367
36505
|
}
|
|
36368
36506
|
function firstExisting3(projectDir, relPaths) {
|
|
36369
36507
|
for (const rel of relPaths) {
|
|
36370
|
-
if (
|
|
36508
|
+
if (existsSync20(join17(projectDir, rel))) return rel;
|
|
36371
36509
|
}
|
|
36372
36510
|
return null;
|
|
36373
36511
|
}
|
|
@@ -36398,7 +36536,7 @@ async function apiKeyForVideoProvider(provider, projectDir) {
|
|
|
36398
36536
|
return getApiKeyFromConfig(providerKey, { cwd: projectDir });
|
|
36399
36537
|
}
|
|
36400
36538
|
async function finalizeBuildResult(projectDir, startedAt, result) {
|
|
36401
|
-
const reportPath =
|
|
36539
|
+
const reportPath = join17(projectDir, "build-report.json");
|
|
36402
36540
|
if (result.stageReports) {
|
|
36403
36541
|
for (const report of Object.values(result.stageReports)) {
|
|
36404
36542
|
if (report.status === "pending") report.status = "skipped";
|
|
@@ -36557,7 +36695,7 @@ function normalizeReportDuration(value) {
|
|
|
36557
36695
|
}
|
|
36558
36696
|
function buildReportComposition(projectDir, result, beatId) {
|
|
36559
36697
|
const path2 = `compositions/scene-${beatId}.html`;
|
|
36560
|
-
const exists =
|
|
36698
|
+
const exists = existsSync20(join17(projectDir, path2));
|
|
36561
36699
|
const written = composeWrittenEntry(result, beatId);
|
|
36562
36700
|
if (written) {
|
|
36563
36701
|
return {
|
|
@@ -36614,8 +36752,8 @@ function summarizeBuildBeats(projectDir, beats) {
|
|
|
36614
36752
|
let compositionsReady = 0;
|
|
36615
36753
|
for (const beat of beats) {
|
|
36616
36754
|
if (beatAssetsReady(beat)) assetsReady += 1;
|
|
36617
|
-
const compositionPath =
|
|
36618
|
-
if (
|
|
36755
|
+
const compositionPath = join17(projectDir, "compositions", `scene-${beat.beatId}.html`);
|
|
36756
|
+
if (existsSync20(compositionPath)) {
|
|
36619
36757
|
compositionsReady += 1;
|
|
36620
36758
|
} else {
|
|
36621
36759
|
needsAuthor.push(beat.beatId);
|
|
@@ -36657,6 +36795,8 @@ var init_scene_build = __esm({
|
|
|
36657
36795
|
init_compose_scenes_skills();
|
|
36658
36796
|
init_compose_prompts();
|
|
36659
36797
|
init_config();
|
|
36798
|
+
init_api_key();
|
|
36799
|
+
init_transcribe_narration();
|
|
36660
36800
|
init_scene_render();
|
|
36661
36801
|
init_storyboard_parse();
|
|
36662
36802
|
init_scene_project();
|
|
@@ -36785,18 +36925,18 @@ __export(ai_audio_exports, {
|
|
|
36785
36925
|
executeVoiceClone: () => executeVoiceClone
|
|
36786
36926
|
});
|
|
36787
36927
|
import { resolve as resolve27, dirname as dirname11, basename as basename5, extname as extname4 } from "node:path";
|
|
36788
|
-
import { readFile as
|
|
36789
|
-
import { existsSync as
|
|
36928
|
+
import { readFile as readFile20, writeFile as writeFile19 } from "node:fs/promises";
|
|
36929
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
36790
36930
|
async function executeTranscribe(options) {
|
|
36791
36931
|
const { audioPath, language, output, format, apiKey } = options;
|
|
36792
36932
|
try {
|
|
36793
36933
|
const key = apiKey || process.env.OPENAI_API_KEY;
|
|
36794
36934
|
if (!key) return { success: false, error: "OPENAI_API_KEY required" };
|
|
36795
36935
|
const absPath = resolve27(process.cwd(), audioPath);
|
|
36796
|
-
if (!
|
|
36936
|
+
if (!existsSync21(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
36797
36937
|
const whisper = new WhisperProvider();
|
|
36798
36938
|
await whisper.initialize({ apiKey: key });
|
|
36799
|
-
const audioBuffer = await
|
|
36939
|
+
const audioBuffer = await readFile20(absPath);
|
|
36800
36940
|
const audioBlob = new Blob([audioBuffer]);
|
|
36801
36941
|
const result = await whisper.transcribe(audioBlob, language);
|
|
36802
36942
|
if (result.status === "failed") {
|
|
@@ -36826,8 +36966,8 @@ async function executeIsolate(options) {
|
|
|
36826
36966
|
const key = apiKey || process.env.ELEVENLABS_API_KEY;
|
|
36827
36967
|
if (!key) return { success: false, error: "ELEVENLABS_API_KEY required" };
|
|
36828
36968
|
const absPath = resolve27(process.cwd(), audioPath);
|
|
36829
|
-
if (!
|
|
36830
|
-
const audioBuffer = await
|
|
36969
|
+
if (!existsSync21(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
36970
|
+
const audioBuffer = await readFile20(absPath);
|
|
36831
36971
|
const elevenlabs = new ElevenLabsProvider();
|
|
36832
36972
|
await elevenlabs.initialize({ apiKey: key });
|
|
36833
36973
|
const result = await elevenlabs.isolateVocals(audioBuffer);
|
|
@@ -36852,8 +36992,8 @@ async function executeVoiceClone(options) {
|
|
|
36852
36992
|
const audioBuffers = [];
|
|
36853
36993
|
for (const samplePath of samplePaths) {
|
|
36854
36994
|
const absPath = resolve27(process.cwd(), samplePath);
|
|
36855
|
-
if (!
|
|
36856
|
-
const buffer = await
|
|
36995
|
+
if (!existsSync21(absPath)) return { success: false, error: `File not found: ${samplePath}` };
|
|
36996
|
+
const buffer = await readFile20(absPath);
|
|
36857
36997
|
audioBuffers.push(buffer);
|
|
36858
36998
|
}
|
|
36859
36999
|
const elevenlabs = new ElevenLabsProvider();
|
|
@@ -36874,7 +37014,7 @@ async function executeDub(options) {
|
|
|
36874
37014
|
const { mediaPath, language, source, voice, analyzeOnly, output } = options;
|
|
36875
37015
|
try {
|
|
36876
37016
|
const absPath = resolve27(process.cwd(), mediaPath);
|
|
36877
|
-
if (!
|
|
37017
|
+
if (!existsSync21(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
36878
37018
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
36879
37019
|
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
36880
37020
|
const elevenlabsKey = process.env.ELEVENLABS_API_KEY;
|
|
@@ -36891,7 +37031,7 @@ async function executeDub(options) {
|
|
|
36891
37031
|
}
|
|
36892
37032
|
const whisper = new WhisperProvider();
|
|
36893
37033
|
await whisper.initialize({ apiKey: openaiKey });
|
|
36894
|
-
const audioBuffer = await
|
|
37034
|
+
const audioBuffer = await readFile20(audioPath);
|
|
36895
37035
|
const audioBlob = new Blob([audioBuffer]);
|
|
36896
37036
|
const transcriptResult = await whisper.transcribe(audioBlob, source);
|
|
36897
37037
|
if (transcriptResult.status === "failed" || !transcriptResult.segments) {
|
|
@@ -37002,8 +37142,8 @@ async function executeDuck(options) {
|
|
|
37002
37142
|
if (!commandExists("ffmpeg")) return { success: false, error: "FFmpeg not found" };
|
|
37003
37143
|
const absMusicPath = resolve27(process.cwd(), musicPath);
|
|
37004
37144
|
const absVoicePath = resolve27(process.cwd(), voicePath);
|
|
37005
|
-
if (!
|
|
37006
|
-
if (!
|
|
37145
|
+
if (!existsSync21(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
|
|
37146
|
+
if (!existsSync21(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
|
|
37007
37147
|
const defaultOutput = resolve27(dirname11(absMusicPath), `${basename5(absMusicPath, extname4(absMusicPath))}-ducked${extname4(absMusicPath)}`);
|
|
37008
37148
|
const outputPath = resolve27(process.cwd(), output || defaultOutput);
|
|
37009
37149
|
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`;
|
|
@@ -37017,7 +37157,7 @@ async function executeDuck(options) {
|
|
|
37017
37157
|
"-y",
|
|
37018
37158
|
outputPath
|
|
37019
37159
|
], { timeout: 12e4, maxBuffer: 50 * 1024 * 1024 });
|
|
37020
|
-
if (!
|
|
37160
|
+
if (!existsSync21(outputPath)) return { success: false, error: "FFmpeg failed to create output" };
|
|
37021
37161
|
return { success: true, outputPath };
|
|
37022
37162
|
} catch (error) {
|
|
37023
37163
|
return { success: false, error: `Audio ducking failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
@@ -37033,8 +37173,8 @@ var init_ai_audio = __esm({
|
|
|
37033
37173
|
});
|
|
37034
37174
|
|
|
37035
37175
|
// src/commands/_shared/edit/silence-cut.ts
|
|
37036
|
-
import { existsSync as
|
|
37037
|
-
import { readFile as
|
|
37176
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
37177
|
+
import { readFile as readFile21 } from "node:fs/promises";
|
|
37038
37178
|
async function detectSilencePeriods(videoPath, noiseThreshold, minDuration) {
|
|
37039
37179
|
const totalDuration = await getVideoDuration(videoPath);
|
|
37040
37180
|
const { stdout, stderr } = await execSafe(
|
|
@@ -37084,7 +37224,7 @@ async function detectSilencePeriodsWithGemini(videoPath, minDuration, options) {
|
|
|
37084
37224
|
}
|
|
37085
37225
|
const gemini = new GeminiProvider();
|
|
37086
37226
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
37087
|
-
const videoBuffer = await
|
|
37227
|
+
const videoBuffer = await readFile21(videoPath);
|
|
37088
37228
|
const modelId = options.model ? resolveGeminiTextModel(options.model) : void 0;
|
|
37089
37229
|
const prompt3 = `Analyze this video and identify all silent or dead segments where there is NO meaningful content.
|
|
37090
37230
|
|
|
@@ -37160,7 +37300,7 @@ async function executeSilenceCut(options) {
|
|
|
37160
37300
|
analyzeOnly = false,
|
|
37161
37301
|
useGemini = false
|
|
37162
37302
|
} = options;
|
|
37163
|
-
if (!
|
|
37303
|
+
if (!existsSync22(videoPath)) {
|
|
37164
37304
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
37165
37305
|
}
|
|
37166
37306
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37268,11 +37408,11 @@ var init_silence_cut = __esm({
|
|
|
37268
37408
|
});
|
|
37269
37409
|
|
|
37270
37410
|
// src/commands/_shared/edit/jump-cut.ts
|
|
37271
|
-
import { existsSync as
|
|
37272
|
-
import { readFile as
|
|
37273
|
-
import { join as
|
|
37411
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
37412
|
+
import { readFile as readFile22, writeFile as writeFile20, mkdir as mkdir11 } from "node:fs/promises";
|
|
37413
|
+
import { join as join18 } from "node:path";
|
|
37274
37414
|
async function transcribeWithWords(audioPath, apiKey, language) {
|
|
37275
|
-
const audioBuffer = await
|
|
37415
|
+
const audioBuffer = await readFile22(audioPath);
|
|
37276
37416
|
const audioBlob = new Blob([audioBuffer]);
|
|
37277
37417
|
const formData = new FormData();
|
|
37278
37418
|
formData.append("file", audioBlob, "audio.wav");
|
|
@@ -37331,7 +37471,7 @@ async function executeJumpCut(options) {
|
|
|
37331
37471
|
analyzeOnly = false,
|
|
37332
37472
|
apiKey
|
|
37333
37473
|
} = options;
|
|
37334
|
-
if (!
|
|
37474
|
+
if (!existsSync23(videoPath)) {
|
|
37335
37475
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
37336
37476
|
}
|
|
37337
37477
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37350,7 +37490,7 @@ async function executeJumpCut(options) {
|
|
|
37350
37490
|
try {
|
|
37351
37491
|
const tmpDir = `/tmp/vibe_jumpcut_${Date.now()}`;
|
|
37352
37492
|
await mkdir11(tmpDir, { recursive: true });
|
|
37353
|
-
const audioPath =
|
|
37493
|
+
const audioPath = join18(tmpDir, "audio.wav");
|
|
37354
37494
|
try {
|
|
37355
37495
|
await execSafe(
|
|
37356
37496
|
"ffmpeg",
|
|
@@ -37404,7 +37544,7 @@ async function executeJumpCut(options) {
|
|
|
37404
37544
|
const segmentPaths = [];
|
|
37405
37545
|
for (let i = 0; i < segments.length; i++) {
|
|
37406
37546
|
const seg = segments[i];
|
|
37407
|
-
const segPath =
|
|
37547
|
+
const segPath = join18(tmpDir, `seg-${i.toString().padStart(4, "0")}.ts`);
|
|
37408
37548
|
const duration = seg.end - seg.start;
|
|
37409
37549
|
await execSafe(
|
|
37410
37550
|
"ffmpeg",
|
|
@@ -37427,7 +37567,7 @@ async function executeJumpCut(options) {
|
|
|
37427
37567
|
segmentPaths.push(segPath);
|
|
37428
37568
|
}
|
|
37429
37569
|
const concatList = segmentPaths.map((p) => `file '${p}'`).join("\n");
|
|
37430
|
-
const listPath =
|
|
37570
|
+
const listPath = join18(tmpDir, "concat.txt");
|
|
37431
37571
|
await writeFile20(listPath, concatList);
|
|
37432
37572
|
await execSafe(
|
|
37433
37573
|
"ffmpeg",
|
|
@@ -37534,8 +37674,8 @@ __export(remotion_exports, {
|
|
|
37534
37674
|
wrapComponentWithVideo: () => wrapComponentWithVideo
|
|
37535
37675
|
});
|
|
37536
37676
|
import { writeFile as writeFile21, mkdir as mkdir12, rm as rm2, copyFile as copyFile4 } from "node:fs/promises";
|
|
37537
|
-
import { existsSync as
|
|
37538
|
-
import { join as
|
|
37677
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
37678
|
+
import { join as join19 } from "node:path";
|
|
37539
37679
|
import { tmpdir } from "node:os";
|
|
37540
37680
|
import { createHash as createHash6 } from "node:crypto";
|
|
37541
37681
|
async function ensureRemotionInstalled() {
|
|
@@ -37554,7 +37694,7 @@ async function ensureRemotionInstalled() {
|
|
|
37554
37694
|
}
|
|
37555
37695
|
}
|
|
37556
37696
|
async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
37557
|
-
const dir =
|
|
37697
|
+
const dir = join19(tmpdir(), `vibe_motion_${Date.now()}`);
|
|
37558
37698
|
await mkdir12(dir, { recursive: true });
|
|
37559
37699
|
const deps = {
|
|
37560
37700
|
remotion: REMOTION_VERSION,
|
|
@@ -37573,7 +37713,7 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
|
37573
37713
|
dependencies: deps
|
|
37574
37714
|
};
|
|
37575
37715
|
const packageJsonStr = JSON.stringify(packageJson, null, 2);
|
|
37576
|
-
await writeFile21(
|
|
37716
|
+
await writeFile21(join19(dir, "package.json"), packageJsonStr);
|
|
37577
37717
|
const tsconfig = {
|
|
37578
37718
|
compilerOptions: {
|
|
37579
37719
|
target: "ES2020",
|
|
@@ -37585,8 +37725,8 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
|
37585
37725
|
skipLibCheck: true
|
|
37586
37726
|
}
|
|
37587
37727
|
};
|
|
37588
|
-
await writeFile21(
|
|
37589
|
-
await writeFile21(
|
|
37728
|
+
await writeFile21(join19(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
|
|
37729
|
+
await writeFile21(join19(dir, "Component.tsx"), componentCode);
|
|
37590
37730
|
const rootCode = `import { registerRoot, Composition } from "remotion";
|
|
37591
37731
|
import { ${componentName} } from "./Component";
|
|
37592
37732
|
|
|
@@ -37605,14 +37745,14 @@ const Root = () => {
|
|
|
37605
37745
|
|
|
37606
37746
|
registerRoot(Root);
|
|
37607
37747
|
`;
|
|
37608
|
-
await writeFile21(
|
|
37748
|
+
await writeFile21(join19(dir, "Root.tsx"), rootCode);
|
|
37609
37749
|
const depsHash = createHash6("md5").update(packageJsonStr).digest("hex").slice(0, 12);
|
|
37610
|
-
const cacheMarker =
|
|
37611
|
-
const cachedModules =
|
|
37612
|
-
if (
|
|
37750
|
+
const cacheMarker = join19(REMOTION_CACHE_DIR, `.deps-${depsHash}`);
|
|
37751
|
+
const cachedModules = join19(REMOTION_CACHE_DIR, "node_modules");
|
|
37752
|
+
if (existsSync24(cacheMarker) && existsSync24(cachedModules)) {
|
|
37613
37753
|
const { symlink } = await import("node:fs/promises");
|
|
37614
37754
|
try {
|
|
37615
|
-
await symlink(cachedModules,
|
|
37755
|
+
await symlink(cachedModules, join19(dir, "node_modules"), "dir");
|
|
37616
37756
|
} catch {
|
|
37617
37757
|
await this_npmInstall(dir);
|
|
37618
37758
|
}
|
|
@@ -37620,12 +37760,12 @@ registerRoot(Root);
|
|
|
37620
37760
|
await this_npmInstall(dir);
|
|
37621
37761
|
try {
|
|
37622
37762
|
await mkdir12(REMOTION_CACHE_DIR, { recursive: true });
|
|
37623
|
-
await writeFile21(
|
|
37624
|
-
if (
|
|
37763
|
+
await writeFile21(join19(REMOTION_CACHE_DIR, "package.json"), packageJsonStr);
|
|
37764
|
+
if (existsSync24(join19(dir, "node_modules"))) {
|
|
37625
37765
|
const { rename: rename5, symlink } = await import("node:fs/promises");
|
|
37626
|
-
await rename5(
|
|
37766
|
+
await rename5(join19(dir, "node_modules"), cachedModules).catch(() => {
|
|
37627
37767
|
});
|
|
37628
|
-
await symlink(cachedModules,
|
|
37768
|
+
await symlink(cachedModules, join19(dir, "node_modules"), "dir").catch(() => {
|
|
37629
37769
|
});
|
|
37630
37770
|
await writeFile21(cacheMarker, depsHash);
|
|
37631
37771
|
}
|
|
@@ -37662,7 +37802,7 @@ async function renderMotion(options) {
|
|
|
37662
37802
|
}
|
|
37663
37803
|
);
|
|
37664
37804
|
try {
|
|
37665
|
-
const entryPoint =
|
|
37805
|
+
const entryPoint = join19(dir, "Root.tsx");
|
|
37666
37806
|
const { execFile: execFile3 } = await import("node:child_process");
|
|
37667
37807
|
const { promisify: promisify3 } = await import("node:util");
|
|
37668
37808
|
const execFileAsync3 = promisify3(execFile3);
|
|
@@ -37795,10 +37935,10 @@ async function renderWithEmbeddedImage(options) {
|
|
|
37795
37935
|
}
|
|
37796
37936
|
);
|
|
37797
37937
|
try {
|
|
37798
|
-
const publicDir =
|
|
37938
|
+
const publicDir = join19(dir, "public");
|
|
37799
37939
|
await mkdir12(publicDir, { recursive: true });
|
|
37800
|
-
await copyFile4(options.imagePath,
|
|
37801
|
-
const entryPoint =
|
|
37940
|
+
await copyFile4(options.imagePath, join19(publicDir, options.imageFileName));
|
|
37941
|
+
const entryPoint = join19(dir, "Root.tsx");
|
|
37802
37942
|
const mp4Out = options.outputPath.replace(/\.\w+$/, ".mp4");
|
|
37803
37943
|
const { execFile: execFileImg } = await import("node:child_process");
|
|
37804
37944
|
const { promisify: promisifyImg } = await import("node:util");
|
|
@@ -37859,10 +37999,10 @@ async function renderWithEmbeddedVideo(options) {
|
|
|
37859
37999
|
}
|
|
37860
38000
|
);
|
|
37861
38001
|
try {
|
|
37862
|
-
const publicDir =
|
|
38002
|
+
const publicDir = join19(dir, "public");
|
|
37863
38003
|
await mkdir12(publicDir, { recursive: true });
|
|
37864
|
-
await copyFile4(options.videoPath,
|
|
37865
|
-
const entryPoint =
|
|
38004
|
+
await copyFile4(options.videoPath, join19(publicDir, options.videoFileName));
|
|
38005
|
+
const entryPoint = join19(dir, "Root.tsx");
|
|
37866
38006
|
const mp4VideoOnly = options.outputPath.replace(/\.\w+$/, "_video_only.mp4");
|
|
37867
38007
|
const { execFile: execFileVid } = await import("node:child_process");
|
|
37868
38008
|
const { promisify: promisifyVid } = await import("node:util");
|
|
@@ -38278,14 +38418,14 @@ var init_remotion = __esm({
|
|
|
38278
38418
|
"use strict";
|
|
38279
38419
|
init_exec_safe();
|
|
38280
38420
|
REMOTION_VERSION = "4.0.447";
|
|
38281
|
-
REMOTION_CACHE_DIR =
|
|
38421
|
+
REMOTION_CACHE_DIR = join19(tmpdir(), "vibe_remotion_cache");
|
|
38282
38422
|
}
|
|
38283
38423
|
});
|
|
38284
38424
|
|
|
38285
38425
|
// src/commands/_shared/edit/caption.ts
|
|
38286
|
-
import { existsSync as
|
|
38287
|
-
import { readFile as
|
|
38288
|
-
import { dirname as dirname12, basename as basename6, extname as extname5, join as
|
|
38426
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
38427
|
+
import { readFile as readFile23, writeFile as writeFile22, mkdir as mkdir13 } from "node:fs/promises";
|
|
38428
|
+
import { dirname as dirname12, basename as basename6, extname as extname5, join as join20 } from "node:path";
|
|
38289
38429
|
function getCaptionForceStyle(style, fontSize, fontColor, position) {
|
|
38290
38430
|
const alignment = position === "top" ? 8 : position === "center" ? 5 : 2;
|
|
38291
38431
|
const marginV = position === "center" ? 0 : 30;
|
|
@@ -38313,7 +38453,7 @@ async function executeCaption(options) {
|
|
|
38313
38453
|
position = "bottom",
|
|
38314
38454
|
apiKey
|
|
38315
38455
|
} = options;
|
|
38316
|
-
if (!
|
|
38456
|
+
if (!existsSync25(videoPath)) {
|
|
38317
38457
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
38318
38458
|
}
|
|
38319
38459
|
if (!commandExists("ffmpeg")) {
|
|
@@ -38332,8 +38472,8 @@ async function executeCaption(options) {
|
|
|
38332
38472
|
try {
|
|
38333
38473
|
const tmpDir = `/tmp/vibe_caption_${Date.now()}`;
|
|
38334
38474
|
await mkdir13(tmpDir, { recursive: true });
|
|
38335
|
-
const audioPath =
|
|
38336
|
-
const srtPath =
|
|
38475
|
+
const audioPath = join20(tmpDir, "audio.wav");
|
|
38476
|
+
const srtPath = join20(tmpDir, "captions.srt");
|
|
38337
38477
|
try {
|
|
38338
38478
|
await execSafe(
|
|
38339
38479
|
"ffmpeg",
|
|
@@ -38354,7 +38494,7 @@ async function executeCaption(options) {
|
|
|
38354
38494
|
);
|
|
38355
38495
|
const whisper = new WhisperProvider();
|
|
38356
38496
|
await whisper.initialize({ apiKey: openaiKey });
|
|
38357
|
-
const audioBuffer = await
|
|
38497
|
+
const audioBuffer = await readFile23(audioPath);
|
|
38358
38498
|
const audioBlob = new Blob([audioBuffer]);
|
|
38359
38499
|
const transcriptResult = await whisper.transcribe(audioBlob, language);
|
|
38360
38500
|
if (transcriptResult.status === "failed" || !transcriptResult.segments || transcriptResult.segments.length === 0) {
|
|
@@ -38402,7 +38542,7 @@ async function executeCaption(options) {
|
|
|
38402
38542
|
const remotionErr = await ensureRemotionInstalled2();
|
|
38403
38543
|
if (remotionErr) {
|
|
38404
38544
|
const outputDir2 = dirname12(outputPath);
|
|
38405
|
-
const outputSrtPath2 =
|
|
38545
|
+
const outputSrtPath2 = join20(
|
|
38406
38546
|
outputDir2,
|
|
38407
38547
|
basename6(outputPath, extname5(outputPath)) + ".srt"
|
|
38408
38548
|
);
|
|
@@ -38441,7 +38581,7 @@ SRT saved to: ${outputSrtPath2}` };
|
|
|
38441
38581
|
});
|
|
38442
38582
|
if (!renderResult.success) {
|
|
38443
38583
|
const outputDir2 = dirname12(outputPath);
|
|
38444
|
-
const outputSrtPath2 =
|
|
38584
|
+
const outputSrtPath2 = join20(
|
|
38445
38585
|
outputDir2,
|
|
38446
38586
|
basename6(outputPath, extname5(outputPath)) + ".srt"
|
|
38447
38587
|
);
|
|
@@ -38454,7 +38594,7 @@ SRT saved to: ${outputSrtPath2}`
|
|
|
38454
38594
|
}
|
|
38455
38595
|
}
|
|
38456
38596
|
const outputDir = dirname12(outputPath);
|
|
38457
|
-
const outputSrtPath =
|
|
38597
|
+
const outputSrtPath = join20(
|
|
38458
38598
|
outputDir,
|
|
38459
38599
|
basename6(outputPath, extname5(outputPath)) + ".srt"
|
|
38460
38600
|
);
|
|
@@ -38491,10 +38631,10 @@ var init_caption = __esm({
|
|
|
38491
38631
|
});
|
|
38492
38632
|
|
|
38493
38633
|
// src/commands/_shared/edit/noise-reduce.ts
|
|
38494
|
-
import { existsSync as
|
|
38634
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
38495
38635
|
async function executeNoiseReduce(options) {
|
|
38496
38636
|
const { inputPath, outputPath, strength = "medium", noiseFloor } = options;
|
|
38497
|
-
if (!
|
|
38637
|
+
if (!existsSync26(inputPath)) {
|
|
38498
38638
|
return { success: false, error: `File not found: ${inputPath}` };
|
|
38499
38639
|
}
|
|
38500
38640
|
if (!commandExists("ffmpeg")) {
|
|
@@ -38551,7 +38691,7 @@ var init_noise_reduce = __esm({
|
|
|
38551
38691
|
});
|
|
38552
38692
|
|
|
38553
38693
|
// src/commands/_shared/edit/fade.ts
|
|
38554
|
-
import { existsSync as
|
|
38694
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
38555
38695
|
async function executeFade(options) {
|
|
38556
38696
|
const {
|
|
38557
38697
|
videoPath,
|
|
@@ -38561,7 +38701,7 @@ async function executeFade(options) {
|
|
|
38561
38701
|
audioOnly = false,
|
|
38562
38702
|
videoOnly = false
|
|
38563
38703
|
} = options;
|
|
38564
|
-
if (!
|
|
38704
|
+
if (!existsSync27(videoPath)) {
|
|
38565
38705
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
38566
38706
|
}
|
|
38567
38707
|
if (!commandExists("ffmpeg")) {
|
|
@@ -38628,8 +38768,8 @@ var init_fade = __esm({
|
|
|
38628
38768
|
});
|
|
38629
38769
|
|
|
38630
38770
|
// src/commands/_shared/edit/translate-srt.ts
|
|
38631
|
-
import { existsSync as
|
|
38632
|
-
import { readFile as
|
|
38771
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
38772
|
+
import { readFile as readFile24, writeFile as writeFile23 } from "node:fs/promises";
|
|
38633
38773
|
async function executeTranslateSrt(options) {
|
|
38634
38774
|
const {
|
|
38635
38775
|
srtPath,
|
|
@@ -38639,11 +38779,11 @@ async function executeTranslateSrt(options) {
|
|
|
38639
38779
|
sourceLanguage,
|
|
38640
38780
|
apiKey
|
|
38641
38781
|
} = options;
|
|
38642
|
-
if (!
|
|
38782
|
+
if (!existsSync28(srtPath)) {
|
|
38643
38783
|
return { success: false, error: `SRT file not found: ${srtPath}` };
|
|
38644
38784
|
}
|
|
38645
38785
|
try {
|
|
38646
|
-
const srtContent = await
|
|
38786
|
+
const srtContent = await readFile24(srtPath, "utf-8");
|
|
38647
38787
|
const segments = parseSRT(srtContent);
|
|
38648
38788
|
if (segments.length === 0) {
|
|
38649
38789
|
return { success: false, error: "No subtitle segments found in SRT file" };
|
|
@@ -38756,7 +38896,7 @@ var init_translate_srt = __esm({
|
|
|
38756
38896
|
});
|
|
38757
38897
|
|
|
38758
38898
|
// src/commands/_shared/edit/text-overlay.ts
|
|
38759
|
-
import { existsSync as
|
|
38899
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
38760
38900
|
import { resolve as resolve28 } from "node:path";
|
|
38761
38901
|
function detectSystemFont() {
|
|
38762
38902
|
const platform = process.platform;
|
|
@@ -38767,7 +38907,7 @@ function detectSystemFont() {
|
|
|
38767
38907
|
"/Library/Fonts/Arial.ttf"
|
|
38768
38908
|
];
|
|
38769
38909
|
for (const f of candidates) {
|
|
38770
|
-
if (
|
|
38910
|
+
if (existsSync29(f)) return f;
|
|
38771
38911
|
}
|
|
38772
38912
|
} else if (platform === "linux") {
|
|
38773
38913
|
const candidates = [
|
|
@@ -38776,7 +38916,7 @@ function detectSystemFont() {
|
|
|
38776
38916
|
"/usr/share/fonts/TTF/DejaVuSans-Bold.ttf"
|
|
38777
38917
|
];
|
|
38778
38918
|
for (const f of candidates) {
|
|
38779
|
-
if (
|
|
38919
|
+
if (existsSync29(f)) return f;
|
|
38780
38920
|
}
|
|
38781
38921
|
} else if (platform === "win32") {
|
|
38782
38922
|
const candidates = [
|
|
@@ -38784,7 +38924,7 @@ function detectSystemFont() {
|
|
|
38784
38924
|
"C:\\Windows\\Fonts\\segoeui.ttf"
|
|
38785
38925
|
];
|
|
38786
38926
|
for (const f of candidates) {
|
|
38787
|
-
if (
|
|
38927
|
+
if (existsSync29(f)) return f;
|
|
38788
38928
|
}
|
|
38789
38929
|
}
|
|
38790
38930
|
return null;
|
|
@@ -38805,7 +38945,7 @@ async function applyTextOverlays(options) {
|
|
|
38805
38945
|
}
|
|
38806
38946
|
const absVideoPath = resolve28(process.cwd(), videoPath);
|
|
38807
38947
|
const absOutputPath = resolve28(process.cwd(), outputPath);
|
|
38808
|
-
if (!
|
|
38948
|
+
if (!existsSync29(absVideoPath)) {
|
|
38809
38949
|
return { success: false, error: `Video not found: ${absVideoPath}` };
|
|
38810
38950
|
}
|
|
38811
38951
|
if (!commandExists("ffmpeg")) {
|
|
@@ -38986,7 +39126,7 @@ var init_ai_edit = __esm({
|
|
|
38986
39126
|
|
|
38987
39127
|
// src/commands/ai-edit-cli.ts
|
|
38988
39128
|
import { resolve as resolve29, extname as extname6, basename as basename7 } from "node:path";
|
|
38989
|
-
import { existsSync as
|
|
39129
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
38990
39130
|
function registerEditCommands(aiCommand) {
|
|
38991
39131
|
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", "(deprecated \u2014 use `vibe detect silence`) 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", `
|
|
38992
39132
|
Examples:
|
|
@@ -39003,7 +39143,7 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
|
|
|
39003
39143
|
validateOutputPath(options.output);
|
|
39004
39144
|
}
|
|
39005
39145
|
const absVideoPath = resolve29(process.cwd(), videoPath);
|
|
39006
|
-
if (!
|
|
39146
|
+
if (!existsSync30(absVideoPath)) {
|
|
39007
39147
|
exitWithError(notFoundError(absVideoPath));
|
|
39008
39148
|
}
|
|
39009
39149
|
if (!commandExists("ffmpeg")) {
|
|
@@ -39111,7 +39251,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
39111
39251
|
validateOutputPath(options.output);
|
|
39112
39252
|
}
|
|
39113
39253
|
const absVideoPath = resolve29(process.cwd(), videoPath);
|
|
39114
|
-
if (!
|
|
39254
|
+
if (!existsSync30(absVideoPath)) {
|
|
39115
39255
|
exitWithError(notFoundError(absVideoPath));
|
|
39116
39256
|
}
|
|
39117
39257
|
if (!commandExists("ffmpeg")) {
|
|
@@ -39192,7 +39332,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
39192
39332
|
validateOutputPath(options.output);
|
|
39193
39333
|
}
|
|
39194
39334
|
const absInputPath = resolve29(process.cwd(), inputPath);
|
|
39195
|
-
if (!
|
|
39335
|
+
if (!existsSync30(absInputPath)) {
|
|
39196
39336
|
exitWithError(notFoundError(absInputPath));
|
|
39197
39337
|
}
|
|
39198
39338
|
if (!commandExists("ffmpeg")) {
|
|
@@ -39258,7 +39398,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
39258
39398
|
validateOutputPath(options.output);
|
|
39259
39399
|
}
|
|
39260
39400
|
const absVideoPath = resolve29(process.cwd(), videoPath);
|
|
39261
|
-
if (!
|
|
39401
|
+
if (!existsSync30(absVideoPath)) {
|
|
39262
39402
|
exitWithError(notFoundError(absVideoPath));
|
|
39263
39403
|
}
|
|
39264
39404
|
if (!commandExists("ffmpeg")) {
|
|
@@ -39333,7 +39473,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
39333
39473
|
exitWithError(usageError("Target language required. Use -t or --target"));
|
|
39334
39474
|
}
|
|
39335
39475
|
const absSrtPath = resolve29(process.cwd(), srtPath);
|
|
39336
|
-
if (!
|
|
39476
|
+
if (!existsSync30(absSrtPath)) {
|
|
39337
39477
|
exitWithError(notFoundError(absSrtPath));
|
|
39338
39478
|
}
|
|
39339
39479
|
if (options.dryRun) {
|
|
@@ -39409,7 +39549,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
39409
39549
|
}
|
|
39410
39550
|
if (options.fillers) rejectControlChars(options.fillers);
|
|
39411
39551
|
const absVideoPath = resolve29(process.cwd(), videoPath);
|
|
39412
|
-
if (!
|
|
39552
|
+
if (!existsSync30(absVideoPath)) {
|
|
39413
39553
|
exitWithError(notFoundError(absVideoPath));
|
|
39414
39554
|
}
|
|
39415
39555
|
if (!commandExists("ffmpeg")) {
|
|
@@ -39902,8 +40042,8 @@ var init_project_resolver = __esm({
|
|
|
39902
40042
|
});
|
|
39903
40043
|
|
|
39904
40044
|
// src/commands/_shared/execute-fill-gaps.ts
|
|
39905
|
-
import { existsSync as
|
|
39906
|
-
import { readFile as
|
|
40045
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
40046
|
+
import { readFile as readFile25, writeFile as writeFile24, mkdir as mkdir14, rename as renameFs } from "node:fs/promises";
|
|
39907
40047
|
import { resolve as resolve31, dirname as dirname13 } from "node:path";
|
|
39908
40048
|
function detectVideoGaps(videoClips, totalDuration) {
|
|
39909
40049
|
const gaps = [];
|
|
@@ -39960,7 +40100,7 @@ function analyzeGapFillability(gaps, videoClips, sources) {
|
|
|
39960
40100
|
}
|
|
39961
40101
|
async function uploadFrameToImgbb(framePath, imgbbApiKey) {
|
|
39962
40102
|
try {
|
|
39963
|
-
const frameBuffer = await
|
|
40103
|
+
const frameBuffer = await readFile25(framePath);
|
|
39964
40104
|
const frameBase64 = frameBuffer.toString("base64");
|
|
39965
40105
|
const formData = new FormData();
|
|
39966
40106
|
formData.append("key", imgbbApiKey);
|
|
@@ -39985,14 +40125,14 @@ async function executeFillGaps(options) {
|
|
|
39985
40125
|
try {
|
|
39986
40126
|
onProgress("Loading timeline...");
|
|
39987
40127
|
const filePath = await resolveTimelineFile(options.projectPath);
|
|
39988
|
-
if (!
|
|
40128
|
+
if (!existsSync31(filePath)) {
|
|
39989
40129
|
return {
|
|
39990
40130
|
success: false,
|
|
39991
40131
|
error: `Timeline file not found: ${filePath}`,
|
|
39992
40132
|
humanLines
|
|
39993
40133
|
};
|
|
39994
40134
|
}
|
|
39995
|
-
const content = await
|
|
40135
|
+
const content = await readFile25(filePath, "utf-8");
|
|
39996
40136
|
const data = JSON.parse(content);
|
|
39997
40137
|
const project = Project.fromJSON(data);
|
|
39998
40138
|
const clips = project.getClips().sort((a, b) => a.startTime - b.startTime);
|
|
@@ -40120,7 +40260,7 @@ async function executeFillGaps(options) {
|
|
|
40120
40260
|
}
|
|
40121
40261
|
const projectDir = dirname13(filePath);
|
|
40122
40262
|
const footageDir = options.dir ? resolve31(process.cwd(), options.dir) : resolve31(projectDir, "footage");
|
|
40123
|
-
if (!
|
|
40263
|
+
if (!existsSync31(footageDir)) {
|
|
40124
40264
|
await mkdir14(footageDir, { recursive: true });
|
|
40125
40265
|
}
|
|
40126
40266
|
humanLines.push("Generating AI Videos");
|
|
@@ -40440,8 +40580,8 @@ __export(ai_motion_exports, {
|
|
|
40440
40580
|
registerMotionCommand: () => registerMotionCommand
|
|
40441
40581
|
});
|
|
40442
40582
|
import { resolve as resolve32 } from "node:path";
|
|
40443
|
-
import { existsSync as
|
|
40444
|
-
import { readFile as
|
|
40583
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
40584
|
+
import { readFile as readFile26, writeFile as writeFile25 } from "node:fs/promises";
|
|
40445
40585
|
async function getOptionalGoogleApiKey() {
|
|
40446
40586
|
const configKey = await getApiKeyFromConfig("google");
|
|
40447
40587
|
if (configKey) return configKey;
|
|
@@ -40488,7 +40628,7 @@ async function executeMotion(options) {
|
|
|
40488
40628
|
};
|
|
40489
40629
|
}
|
|
40490
40630
|
const imagePath = resolve32(process.cwd(), options.image);
|
|
40491
|
-
const imageBuffer = await
|
|
40631
|
+
const imageBuffer = await readFile26(imagePath);
|
|
40492
40632
|
const gemini = new GeminiProvider();
|
|
40493
40633
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
40494
40634
|
const analysisResult = await gemini.analyzeImage(
|
|
@@ -40525,7 +40665,7 @@ Use this image analysis to inform the color palette, typography placement, and o
|
|
|
40525
40665
|
}
|
|
40526
40666
|
if (geminiApiKey) {
|
|
40527
40667
|
const videoPath = resolve32(process.cwd(), options.video);
|
|
40528
|
-
const videoBuffer = await
|
|
40668
|
+
const videoBuffer = await readFile26(videoPath);
|
|
40529
40669
|
const gemini = new GeminiProvider();
|
|
40530
40670
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
40531
40671
|
const analysisPrompt = options.understandingPrompt || `Analyze this video for motion graphics composition. Return concise production guidance:
|
|
@@ -40556,10 +40696,10 @@ Use this video understanding to place motion graphics in safe zones, match the c
|
|
|
40556
40696
|
let result;
|
|
40557
40697
|
if (options.fromTsx) {
|
|
40558
40698
|
const tsxPath = resolve32(process.cwd(), options.fromTsx);
|
|
40559
|
-
if (!
|
|
40699
|
+
if (!existsSync32(tsxPath)) {
|
|
40560
40700
|
return { success: false, error: `TSX file not found: ${tsxPath}` };
|
|
40561
40701
|
}
|
|
40562
|
-
const existingCode = await
|
|
40702
|
+
const existingCode = await readFile26(tsxPath, "utf-8");
|
|
40563
40703
|
if (useGemini) {
|
|
40564
40704
|
const gemini = new GeminiProvider();
|
|
40565
40705
|
await gemini.initialize({ apiKey });
|
|
@@ -41088,7 +41228,7 @@ __export(project_builder_exports, {
|
|
|
41088
41228
|
resolveSourceUrl: () => resolveSourceUrl
|
|
41089
41229
|
});
|
|
41090
41230
|
import { mkdtemp, mkdir as mkdir15, copyFile as copyFile5, writeFile as writeFile26, rm as rm3, readdir as readdir3 } from "node:fs/promises";
|
|
41091
|
-
import { existsSync as
|
|
41231
|
+
import { existsSync as existsSync33 } from "node:fs";
|
|
41092
41232
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
41093
41233
|
import { createRequire } from "node:module";
|
|
41094
41234
|
import * as path from "node:path";
|
|
@@ -41100,7 +41240,7 @@ async function buildTempProject(state, projectFileDir) {
|
|
|
41100
41240
|
for (const source of state.sources) {
|
|
41101
41241
|
if (copied.has(source.id)) continue;
|
|
41102
41242
|
const resolved = resolveSourceUrl(source.url, projectFileDir);
|
|
41103
|
-
if (!
|
|
41243
|
+
if (!existsSync33(resolved)) {
|
|
41104
41244
|
throw new Error(`Source file not found: ${resolved} (from source id=${source.id})`);
|
|
41105
41245
|
}
|
|
41106
41246
|
const dest = path.join(assetsDir, path.basename(resolved));
|
|
@@ -41237,7 +41377,7 @@ __export(motion_overlay_exports, {
|
|
|
41237
41377
|
registerMotionOverlayCommand: () => registerMotionOverlayCommand
|
|
41238
41378
|
});
|
|
41239
41379
|
import { mkdir as mkdir16 } from "node:fs/promises";
|
|
41240
|
-
import { existsSync as
|
|
41380
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
41241
41381
|
import { basename as basename9, dirname as dirname15, extname as extname7, resolve as resolve34 } from "node:path";
|
|
41242
41382
|
function normalizeUnderstand2(value) {
|
|
41243
41383
|
return value === "off" || value === "required" || value === "auto" ? value : "auto";
|
|
@@ -41368,11 +41508,11 @@ async function executeMotionOverlay(options) {
|
|
|
41368
41508
|
const validationError = assertValidRequest(options);
|
|
41369
41509
|
if (validationError) return { success: false, error: validationError };
|
|
41370
41510
|
const absVideo = resolve34(process.cwd(), options.videoPath);
|
|
41371
|
-
if (!
|
|
41511
|
+
if (!existsSync34(absVideo)) return { success: false, error: `Video file not found: ${absVideo}` };
|
|
41372
41512
|
const outputPath = options.output ? resolve34(process.cwd(), options.output) : defaultOverlayOutput(options.videoPath);
|
|
41373
41513
|
if (options.asset) {
|
|
41374
41514
|
const absAsset = resolve34(process.cwd(), options.asset);
|
|
41375
|
-
if (!
|
|
41515
|
+
if (!existsSync34(absAsset)) return { success: false, error: `Lottie asset not found: ${absAsset}` };
|
|
41376
41516
|
const [videoDuration, size] = await Promise.all([
|
|
41377
41517
|
ffprobeDuration(absVideo),
|
|
41378
41518
|
ffprobeVideoSize(absVideo)
|
|
@@ -41539,7 +41679,7 @@ __export(edit_cmd_exports, {
|
|
|
41539
41679
|
executeUpscale: () => executeUpscale
|
|
41540
41680
|
});
|
|
41541
41681
|
import { resolve as resolve35, dirname as dirname16 } from "node:path";
|
|
41542
|
-
import { readFile as
|
|
41682
|
+
import { readFile as readFile27, writeFile as writeFile27, mkdir as mkdir17 } from "node:fs/promises";
|
|
41543
41683
|
async function executeGrade(options) {
|
|
41544
41684
|
const { videoPath, style, preset, output, analyzeOnly, apiKey } = options;
|
|
41545
41685
|
try {
|
|
@@ -41580,7 +41720,7 @@ async function executeSpeedRamp(options) {
|
|
|
41580
41720
|
await execSafe("ffmpeg", ["-i", absPath, "-vn", "-acodec", "libmp3lame", "-q:a", "2", tempAudio, "-y"]);
|
|
41581
41721
|
const whisper = new WhisperProvider();
|
|
41582
41722
|
await whisper.initialize({ apiKey: openaiKey });
|
|
41583
|
-
const audioBuffer = await
|
|
41723
|
+
const audioBuffer = await readFile27(tempAudio);
|
|
41584
41724
|
const transcript = await whisper.transcribe(new Blob([audioBuffer]), language);
|
|
41585
41725
|
try {
|
|
41586
41726
|
const { unlink: unlink5 } = await import("node:fs/promises");
|
|
@@ -41974,7 +42114,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
41974
42114
|
spinner2.text = "Transcribing audio...";
|
|
41975
42115
|
const whisper = new WhisperProvider();
|
|
41976
42116
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
41977
|
-
const audioBuffer = await
|
|
42117
|
+
const audioBuffer = await readFile27(tempAudio);
|
|
41978
42118
|
const audioBlob = new Blob([audioBuffer]);
|
|
41979
42119
|
const transcript = await whisper.transcribe(audioBlob, options.language);
|
|
41980
42120
|
if (!transcript.segments || transcript.segments.length === 0) {
|
|
@@ -42107,7 +42247,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
42107
42247
|
for (let i = 1; i <= numKeyframes && i <= 30; i++) {
|
|
42108
42248
|
const framePath = `${tempDir}/frame-${i.toString().padStart(4, "0")}.jpg`;
|
|
42109
42249
|
try {
|
|
42110
|
-
const frameBuffer = await
|
|
42250
|
+
const frameBuffer = await readFile27(framePath);
|
|
42111
42251
|
const frameBase64 = frameBuffer.toString("base64");
|
|
42112
42252
|
const result = await claude.analyzeFrameForReframe(frameBase64, options.aspect, {
|
|
42113
42253
|
focusMode: options.focus,
|
|
@@ -42236,7 +42376,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
42236
42376
|
const imageBuffers = [];
|
|
42237
42377
|
for (const imagePath of imagePaths) {
|
|
42238
42378
|
const absPath = resolve35(process.cwd(), imagePath);
|
|
42239
|
-
const buffer = await
|
|
42379
|
+
const buffer = await readFile27(absPath);
|
|
42240
42380
|
imageBuffers.push(buffer);
|
|
42241
42381
|
}
|
|
42242
42382
|
let result;
|
|
@@ -42513,8 +42653,8 @@ __export(ai_image_exports, {
|
|
|
42513
42653
|
executeThumbnailBestFrame: () => executeThumbnailBestFrame
|
|
42514
42654
|
});
|
|
42515
42655
|
import { resolve as resolve37, dirname as dirname18 } from "node:path";
|
|
42516
|
-
import { readFile as
|
|
42517
|
-
import { existsSync as
|
|
42656
|
+
import { readFile as readFile28, writeFile as writeFile29, mkdir as mkdir19 } from "node:fs/promises";
|
|
42657
|
+
import { existsSync as existsSync36 } from "node:fs";
|
|
42518
42658
|
async function executeImageGenerate(options) {
|
|
42519
42659
|
const {
|
|
42520
42660
|
prompt: prompt3,
|
|
@@ -42658,10 +42798,10 @@ async function executeGeminiEdit(options) {
|
|
|
42658
42798
|
const imageBuffers = [];
|
|
42659
42799
|
for (const imagePath of imagePaths) {
|
|
42660
42800
|
const absPath = resolve37(process.cwd(), imagePath);
|
|
42661
|
-
if (!
|
|
42801
|
+
if (!existsSync36(absPath)) {
|
|
42662
42802
|
return { success: false, error: `Image not found: ${absPath}` };
|
|
42663
42803
|
}
|
|
42664
|
-
const buffer = await
|
|
42804
|
+
const buffer = await readFile28(absPath);
|
|
42665
42805
|
imageBuffers.push(buffer);
|
|
42666
42806
|
}
|
|
42667
42807
|
const gemini = new GeminiProvider();
|
|
@@ -42706,7 +42846,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
42706
42846
|
model = "flash",
|
|
42707
42847
|
apiKey
|
|
42708
42848
|
} = options;
|
|
42709
|
-
if (!
|
|
42849
|
+
if (!existsSync36(videoPath)) {
|
|
42710
42850
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
42711
42851
|
}
|
|
42712
42852
|
if (!commandExists("ffmpeg")) {
|
|
@@ -42719,7 +42859,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
42719
42859
|
try {
|
|
42720
42860
|
const gemini = new GeminiProvider();
|
|
42721
42861
|
await gemini.initialize({ apiKey: googleKey });
|
|
42722
|
-
const videoData = await
|
|
42862
|
+
const videoData = await readFile28(videoPath);
|
|
42723
42863
|
const analysisPrompt = prompt3 || 'Analyze this video and find the single best frame for a thumbnail. Look for frames that are visually striking, well-composed, emotionally engaging, and representative of the video content. Avoid blurry frames, transitions, or dark scenes. Return ONLY a JSON object: {"timestamp": <seconds as number>, "reason": "<brief explanation>"}';
|
|
42724
42864
|
const modelId = resolveGeminiTextModel(model);
|
|
42725
42865
|
const result = await gemini.analyzeVideo(videoData, analysisPrompt, {
|
|
@@ -42740,7 +42880,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
42740
42880
|
reason = reasonMatch[1];
|
|
42741
42881
|
}
|
|
42742
42882
|
await execSafe("ffmpeg", ["-ss", String(timestamp), "-i", videoPath, "-frames:v", "1", "-q:v", "2", outputPath, "-y"], { timeout: 6e4, maxBuffer: 50 * 1024 * 1024 });
|
|
42743
|
-
if (!
|
|
42883
|
+
if (!existsSync36(outputPath)) {
|
|
42744
42884
|
return { success: false, error: "FFmpeg failed to extract frame" };
|
|
42745
42885
|
}
|
|
42746
42886
|
return {
|
|
@@ -42771,9 +42911,9 @@ __export(ai_analyze_exports, {
|
|
|
42771
42911
|
executeGeminiVideo: () => executeGeminiVideo,
|
|
42772
42912
|
registerAnalyzeCommands: () => registerAnalyzeCommands
|
|
42773
42913
|
});
|
|
42774
|
-
import { readFile as
|
|
42914
|
+
import { readFile as readFile29 } from "node:fs/promises";
|
|
42775
42915
|
import { extname as extname8, resolve as resolve38 } from "node:path";
|
|
42776
|
-
import { existsSync as
|
|
42916
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
42777
42917
|
async function executeGeminiVideo(options) {
|
|
42778
42918
|
try {
|
|
42779
42919
|
const apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
|
|
@@ -42787,10 +42927,10 @@ async function executeGeminiVideo(options) {
|
|
|
42787
42927
|
videoData = options.source;
|
|
42788
42928
|
} else {
|
|
42789
42929
|
const absPath = resolve38(process.cwd(), options.source);
|
|
42790
|
-
if (!
|
|
42930
|
+
if (!existsSync37(absPath)) {
|
|
42791
42931
|
return { success: false, error: `File not found: ${absPath}` };
|
|
42792
42932
|
}
|
|
42793
|
-
videoData = await
|
|
42933
|
+
videoData = await readFile29(absPath);
|
|
42794
42934
|
}
|
|
42795
42935
|
const gemini = new GeminiProvider();
|
|
42796
42936
|
await gemini.initialize({ apiKey });
|
|
@@ -42855,10 +42995,10 @@ async function executeAnalyze(options) {
|
|
|
42855
42995
|
imageBuffer = Buffer.from(await response.arrayBuffer());
|
|
42856
42996
|
} else {
|
|
42857
42997
|
const absPath = resolve38(process.cwd(), source);
|
|
42858
|
-
if (!
|
|
42998
|
+
if (!existsSync37(absPath)) {
|
|
42859
42999
|
return { success: false, error: `File not found: ${absPath}` };
|
|
42860
43000
|
}
|
|
42861
|
-
imageBuffer = await
|
|
43001
|
+
imageBuffer = await readFile29(absPath);
|
|
42862
43002
|
}
|
|
42863
43003
|
const result2 = await gemini.analyzeImage(imageBuffer, options.prompt, {
|
|
42864
43004
|
model: modelId,
|
|
@@ -42890,10 +43030,10 @@ async function executeAnalyze(options) {
|
|
|
42890
43030
|
videoData = Buffer.from(await response.arrayBuffer());
|
|
42891
43031
|
} else {
|
|
42892
43032
|
const absPath = resolve38(process.cwd(), source);
|
|
42893
|
-
if (!
|
|
43033
|
+
if (!existsSync37(absPath)) {
|
|
42894
43034
|
return { success: false, error: `File not found: ${absPath}` };
|
|
42895
43035
|
}
|
|
42896
|
-
videoData = await
|
|
43036
|
+
videoData = await readFile29(absPath);
|
|
42897
43037
|
}
|
|
42898
43038
|
const result = await gemini.analyzeVideo(videoData, options.prompt, {
|
|
42899
43039
|
model: modelId,
|
|
@@ -43027,9 +43167,9 @@ __export(ai_review_exports, {
|
|
|
43027
43167
|
executeReview: () => executeReview,
|
|
43028
43168
|
registerReviewCommand: () => registerReviewCommand
|
|
43029
43169
|
});
|
|
43030
|
-
import { readFile as
|
|
43170
|
+
import { readFile as readFile30, rename as rename3 } from "node:fs/promises";
|
|
43031
43171
|
import { resolve as resolve39 } from "node:path";
|
|
43032
|
-
import { existsSync as
|
|
43172
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
43033
43173
|
function parseReviewFeedback(response) {
|
|
43034
43174
|
let cleaned = response.trim();
|
|
43035
43175
|
if (cleaned.startsWith("```json")) {
|
|
@@ -43061,7 +43201,7 @@ async function executeReview(options) {
|
|
|
43061
43201
|
model = "flash"
|
|
43062
43202
|
} = options;
|
|
43063
43203
|
const absVideoPath = resolve39(process.cwd(), videoPath);
|
|
43064
|
-
if (!
|
|
43204
|
+
if (!existsSync38(absVideoPath)) {
|
|
43065
43205
|
return { success: false, error: `Video not found: ${absVideoPath}` };
|
|
43066
43206
|
}
|
|
43067
43207
|
const apiKey = process.env.GOOGLE_API_KEY || await getApiKey("GOOGLE_API_KEY", "Google");
|
|
@@ -43074,8 +43214,8 @@ async function executeReview(options) {
|
|
|
43074
43214
|
let storyboardContext = "";
|
|
43075
43215
|
if (storyboardPath) {
|
|
43076
43216
|
const absStoryboardPath = resolve39(process.cwd(), storyboardPath);
|
|
43077
|
-
if (
|
|
43078
|
-
const content = await
|
|
43217
|
+
if (existsSync38(absStoryboardPath)) {
|
|
43218
|
+
const content = await readFile30(absStoryboardPath, "utf-8");
|
|
43079
43219
|
storyboardContext = `
|
|
43080
43220
|
|
|
43081
43221
|
Original storyboard for reference:
|
|
@@ -43117,7 +43257,7 @@ ${projectContext}` : "";
|
|
|
43117
43257
|
Score each category 1-10. Prefer beatIssues when you can map a problem to a storyboard beat or timestamp. Use the exact beatId values from the storyboard or beat timing summary, and include timeRange in seconds for every localized finding. In particular, flag narration/visual mismatches, overly static holds, and audio-visual sync problems as beatIssues. Use category issue arrays only for problems that cannot be localized to a beat. For fixable issues, provide an FFmpeg filter in autoFixable. Be specific and practical.${storyboardContext}${extraProjectContext}`;
|
|
43118
43258
|
const gemini = new GeminiProvider();
|
|
43119
43259
|
await gemini.initialize({ apiKey });
|
|
43120
|
-
const videoData = await
|
|
43260
|
+
const videoData = await readFile30(absVideoPath);
|
|
43121
43261
|
const analysisResult = await gemini.analyzeVideo(videoData, reviewPrompt, {
|
|
43122
43262
|
model: modelId
|
|
43123
43263
|
});
|
|
@@ -43172,7 +43312,7 @@ Score each category 1-10. Prefer beatIssues when you can map a problem to a stor
|
|
|
43172
43312
|
}
|
|
43173
43313
|
}
|
|
43174
43314
|
if (verify && result.outputPath) {
|
|
43175
|
-
const verifyVideoData = await
|
|
43315
|
+
const verifyVideoData = await readFile30(result.outputPath);
|
|
43176
43316
|
const verifyResult = await gemini.analyzeVideo(
|
|
43177
43317
|
verifyVideoData,
|
|
43178
43318
|
'Rate this video overall quality on a scale of 1-10. Return ONLY a JSON object: {"score": <number>}',
|
|
@@ -43636,7 +43776,7 @@ var init_sanitize = __esm({
|
|
|
43636
43776
|
|
|
43637
43777
|
// src/commands/generate/storyboard.ts
|
|
43638
43778
|
import { resolve as resolve46 } from "node:path";
|
|
43639
|
-
import { readFile as
|
|
43779
|
+
import { readFile as readFile34, writeFile as writeFile33 } from "node:fs/promises";
|
|
43640
43780
|
async function executeStoryboard(options) {
|
|
43641
43781
|
const { content, duration, creativity = "low", output, apiKey } = options;
|
|
43642
43782
|
try {
|
|
@@ -43684,7 +43824,7 @@ function registerStoryboardCommand(parent) {
|
|
|
43684
43824
|
let textContent = content;
|
|
43685
43825
|
if (options.file) {
|
|
43686
43826
|
const filePath = resolve46(process.cwd(), content);
|
|
43687
|
-
textContent = await
|
|
43827
|
+
textContent = await readFile34(filePath, "utf-8");
|
|
43688
43828
|
}
|
|
43689
43829
|
if (options.dryRun) {
|
|
43690
43830
|
outputSuccess({
|
|
@@ -44077,7 +44217,7 @@ var init_speech = __esm({
|
|
|
44077
44217
|
|
|
44078
44218
|
// src/commands/generate/thumbnail.ts
|
|
44079
44219
|
import { resolve as resolve48, dirname as dirname20, basename as basename13, extname as extname9 } from "node:path";
|
|
44080
|
-
import { existsSync as
|
|
44220
|
+
import { existsSync as existsSync41 } from "node:fs";
|
|
44081
44221
|
import { writeFile as writeFile35, mkdir as mkdir21 } from "node:fs/promises";
|
|
44082
44222
|
function registerThumbnailCommand(parent) {
|
|
44083
44223
|
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: ${GEMINI_TEXT_MODEL_HELP}`, "flash").action(async (description, options) => {
|
|
@@ -44089,7 +44229,7 @@ function registerThumbnailCommand(parent) {
|
|
|
44089
44229
|
}
|
|
44090
44230
|
if (options.bestFrame) {
|
|
44091
44231
|
const absVideoPath = resolve48(process.cwd(), options.bestFrame);
|
|
44092
|
-
if (!
|
|
44232
|
+
if (!existsSync41(absVideoPath)) {
|
|
44093
44233
|
exitWithError(notFoundError(absVideoPath));
|
|
44094
44234
|
}
|
|
44095
44235
|
if (!commandExists("ffmpeg")) {
|
|
@@ -46273,7 +46413,7 @@ var init_dist3 = __esm({
|
|
|
46273
46413
|
|
|
46274
46414
|
// src/commands/generate/video.ts
|
|
46275
46415
|
import { resolve as resolve52 } from "node:path";
|
|
46276
|
-
import { readFile as
|
|
46416
|
+
import { readFile as readFile35, writeFile as writeFile39 } from "node:fs/promises";
|
|
46277
46417
|
function registerVideoCommand(parent) {
|
|
46278
46418
|
parent.command("video").alias("vid").description("Generate video using AI (Seedance, Grok, Kling, Runway, or Veo)").argument("[prompt]", "Text prompt describing the video (interactive if omitted)").option(
|
|
46279
46419
|
"-p, --provider <provider>",
|
|
@@ -46384,7 +46524,7 @@ Examples:
|
|
|
46384
46524
|
let isImageToVideo = false;
|
|
46385
46525
|
if (options.image) {
|
|
46386
46526
|
const imagePath = resolve52(process.cwd(), options.image);
|
|
46387
|
-
const imageBuffer = await
|
|
46527
|
+
const imageBuffer = await readFile35(imagePath);
|
|
46388
46528
|
const ext = options.image.toLowerCase().split(".").pop();
|
|
46389
46529
|
const mimeTypes = {
|
|
46390
46530
|
jpg: "image/jpeg",
|
|
@@ -46624,7 +46764,7 @@ Examples:
|
|
|
46624
46764
|
let lastFrame;
|
|
46625
46765
|
if (options.lastFrame) {
|
|
46626
46766
|
const lastFramePath = resolve52(process.cwd(), options.lastFrame);
|
|
46627
|
-
const lastFrameBuffer = await
|
|
46767
|
+
const lastFrameBuffer = await readFile35(lastFramePath);
|
|
46628
46768
|
const ext = options.lastFrame.toLowerCase().split(".").pop();
|
|
46629
46769
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
|
|
46630
46770
|
lastFrame = `data:${mimeType};base64,${lastFrameBuffer.toString("base64")}`;
|
|
@@ -46634,7 +46774,7 @@ Examples:
|
|
|
46634
46774
|
refImages = [];
|
|
46635
46775
|
for (const refPath of options.refImages.slice(0, 3)) {
|
|
46636
46776
|
const absRefPath = resolve52(process.cwd(), refPath);
|
|
46637
|
-
const refBuffer = await
|
|
46777
|
+
const refBuffer = await readFile35(absRefPath);
|
|
46638
46778
|
const ext = refPath.toLowerCase().split(".").pop();
|
|
46639
46779
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
|
|
46640
46780
|
refImages.push({ base64: refBuffer.toString("base64"), mimeType });
|
|
@@ -46766,7 +46906,7 @@ Examples:
|
|
|
46766
46906
|
if (seedanceEndImagePath && seedanceReferences.length === 0) {
|
|
46767
46907
|
try {
|
|
46768
46908
|
const absEndImagePath = resolve52(process.cwd(), seedanceEndImagePath);
|
|
46769
|
-
const endImageBuffer = await
|
|
46909
|
+
const endImageBuffer = await readFile35(absEndImagePath);
|
|
46770
46910
|
const ext = seedanceEndImagePath.toLowerCase().split(".").pop();
|
|
46771
46911
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
|
|
46772
46912
|
const uploadHost = await resolveUploadHost();
|
|
@@ -46883,7 +47023,7 @@ async function fileInputToUrlOrDataUri2(input, fallbackMimeType) {
|
|
|
46883
47023
|
return input;
|
|
46884
47024
|
}
|
|
46885
47025
|
const absPath = resolve52(process.cwd(), input);
|
|
46886
|
-
const buffer = await
|
|
47026
|
+
const buffer = await readFile35(absPath);
|
|
46887
47027
|
return `data:${mimeTypeForPath2(input, fallbackMimeType)};base64,${buffer.toString("base64")}`;
|
|
46888
47028
|
}
|
|
46889
47029
|
function mimeTypeForPath2(path2, fallback2) {
|
|
@@ -47891,7 +48031,7 @@ __export(detect_exports, {
|
|
|
47891
48031
|
executeDetectScenes: () => executeDetectScenes,
|
|
47892
48032
|
executeDetectSilence: () => executeDetectSilence
|
|
47893
48033
|
});
|
|
47894
|
-
import { readFile as
|
|
48034
|
+
import { readFile as readFile40, writeFile as writeFile44 } from "node:fs/promises";
|
|
47895
48035
|
import { resolve as resolve57, basename as basename15 } from "node:path";
|
|
47896
48036
|
async function executeDetectScenes(options) {
|
|
47897
48037
|
try {
|
|
@@ -48135,7 +48275,7 @@ var init_detect = __esm({
|
|
|
48135
48275
|
}
|
|
48136
48276
|
if (options.project) {
|
|
48137
48277
|
const projectPath = resolve57(process.cwd(), options.project);
|
|
48138
|
-
const content = await
|
|
48278
|
+
const content = await readFile40(projectPath, "utf-8");
|
|
48139
48279
|
const data = JSON.parse(content);
|
|
48140
48280
|
const project = Project.fromJSON(data);
|
|
48141
48281
|
let source = project.getSources().find((s) => s.url === absPath);
|
|
@@ -52986,8 +53126,8 @@ init_scene_project();
|
|
|
52986
53126
|
init_project_config();
|
|
52987
53127
|
init_cost_tier();
|
|
52988
53128
|
import { resolve as resolve17, relative as relative6, dirname as dirname6 } from "node:path";
|
|
52989
|
-
import { mkdir as mkdir8, readFile as
|
|
52990
|
-
import { existsSync as
|
|
53129
|
+
import { mkdir as mkdir8, readFile as readFile15, writeFile as writeFile15, access as access4, copyFile as copyFile2 } from "node:fs/promises";
|
|
53130
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
52991
53131
|
|
|
52992
53132
|
// src/commands/_shared/scene-html-emit.ts
|
|
52993
53133
|
var SCENE_PRESETS = [
|
|
@@ -53556,15 +53696,16 @@ function deriveInstallHosts(detected) {
|
|
|
53556
53696
|
|
|
53557
53697
|
// src/commands/scene.ts
|
|
53558
53698
|
init_compose_prompts();
|
|
53699
|
+
init_transcribe_narration();
|
|
53559
53700
|
|
|
53560
53701
|
// src/commands/_shared/scene-submit.ts
|
|
53561
53702
|
init_compose_scenes_skills();
|
|
53562
53703
|
init_scene_lint();
|
|
53563
53704
|
init_storyboard_parse();
|
|
53564
53705
|
init_root_sync();
|
|
53565
|
-
import { existsSync as
|
|
53566
|
-
import { mkdir as mkdir7, readFile as
|
|
53567
|
-
import { join as
|
|
53706
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
53707
|
+
import { mkdir as mkdir7, readFile as readFile12, writeFile as writeFile13 } from "node:fs/promises";
|
|
53708
|
+
import { join as join12, resolve as resolve14 } from "node:path";
|
|
53568
53709
|
async function executeSceneSubmit(opts) {
|
|
53569
53710
|
const projectDir = resolve14(opts.projectDir);
|
|
53570
53711
|
const beatId = opts.beatId.trim();
|
|
@@ -53578,15 +53719,15 @@ async function executeSceneSubmit(opts) {
|
|
|
53578
53719
|
warnings: [],
|
|
53579
53720
|
retryWith: []
|
|
53580
53721
|
};
|
|
53581
|
-
const storyboardPath =
|
|
53582
|
-
if (!
|
|
53722
|
+
const storyboardPath = join12(projectDir, "STORYBOARD.md");
|
|
53723
|
+
if (!existsSync12(storyboardPath)) {
|
|
53583
53724
|
return {
|
|
53584
53725
|
...base,
|
|
53585
53726
|
success: false,
|
|
53586
53727
|
error: `STORYBOARD.md not found at ${storyboardPath}. Run \`vibe init <dir>\` first.`
|
|
53587
53728
|
};
|
|
53588
53729
|
}
|
|
53589
|
-
const parsed = parseStoryboard(await
|
|
53730
|
+
const parsed = parseStoryboard(await readFile12(storyboardPath, "utf-8"));
|
|
53590
53731
|
const beat = parsed.beats.find((b) => b.id === beatId);
|
|
53591
53732
|
if (!beat) {
|
|
53592
53733
|
const available = parsed.beats.map((b) => b.id).join(", ") || "(none)";
|
|
@@ -53656,8 +53797,8 @@ async function executeSceneSubmit(opts) {
|
|
|
53656
53797
|
if (opts.validateOnly) {
|
|
53657
53798
|
return { ...base, success: true };
|
|
53658
53799
|
}
|
|
53659
|
-
const absPath =
|
|
53660
|
-
await mkdir7(
|
|
53800
|
+
const absPath = join12(projectDir, scenePath);
|
|
53801
|
+
await mkdir7(join12(projectDir, "compositions"), { recursive: true });
|
|
53661
53802
|
await writeFile13(absPath, html.endsWith("\n") ? html : `${html}
|
|
53662
53803
|
`, "utf-8");
|
|
53663
53804
|
return {
|
|
@@ -53858,7 +53999,7 @@ sceneCommand.command("submit").description(
|
|
|
53858
53999
|
}
|
|
53859
54000
|
html = Buffer.concat(chunks).toString("utf-8");
|
|
53860
54001
|
} else if (options.file) {
|
|
53861
|
-
html = await
|
|
54002
|
+
html = await readFile15(resolve17(String(options.file)), "utf-8");
|
|
53862
54003
|
} else {
|
|
53863
54004
|
exitWithError(
|
|
53864
54005
|
usageError(
|
|
@@ -54188,8 +54329,8 @@ function readClipStart(rootHtml, beatId) {
|
|
|
54188
54329
|
async function resolveNarrationText(value) {
|
|
54189
54330
|
if (!value) return void 0;
|
|
54190
54331
|
const looksLikePath = /\.[a-z]{2,4}$/i.test(value) || value.includes("/") || value.includes("\\");
|
|
54191
|
-
if (looksLikePath &&
|
|
54192
|
-
return (await
|
|
54332
|
+
if (looksLikePath && existsSync14(value)) {
|
|
54333
|
+
return (await readFile15(value, "utf-8")).trim();
|
|
54193
54334
|
}
|
|
54194
54335
|
return value.trim();
|
|
54195
54336
|
}
|
|
@@ -54228,7 +54369,7 @@ async function executeSceneAdd(opts) {
|
|
|
54228
54369
|
if (!opts.force && await pathExists2(scenePath)) {
|
|
54229
54370
|
return errResult(`Scene already exists: ${sceneRel}. Re-run with --force to overwrite.`);
|
|
54230
54371
|
}
|
|
54231
|
-
const rootHtmlBefore = await
|
|
54372
|
+
const rootHtmlBefore = await readFile15(rootPath, "utf-8");
|
|
54232
54373
|
let dims = readRootDims(rootHtmlBefore);
|
|
54233
54374
|
if (!dims) {
|
|
54234
54375
|
const cfg2 = await loadVibeProjectConfig(projectDir);
|
|
@@ -54304,31 +54445,20 @@ async function executeSceneAdd(opts) {
|
|
|
54304
54445
|
);
|
|
54305
54446
|
} else {
|
|
54306
54447
|
opts.onProgress?.("Transcribing narration (Whisper word-level)...");
|
|
54307
|
-
|
|
54308
|
-
|
|
54309
|
-
|
|
54310
|
-
|
|
54311
|
-
|
|
54312
|
-
|
|
54313
|
-
|
|
54314
|
-
|
|
54315
|
-
|
|
54316
|
-
|
|
54317
|
-
|
|
54318
|
-
|
|
54319
|
-
|
|
54320
|
-
|
|
54321
|
-
transcriptWords = transcript.words.map((w) => ({
|
|
54322
|
-
text: w.text,
|
|
54323
|
-
start: w.start,
|
|
54324
|
-
end: w.end
|
|
54325
|
-
}));
|
|
54326
|
-
} else if (transcript.status === "failed") {
|
|
54327
|
-
opts.onProgress?.(`Transcribe failed: ${transcript.error ?? "unknown error"}`);
|
|
54328
|
-
}
|
|
54329
|
-
} catch (error) {
|
|
54330
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
54331
|
-
opts.onProgress?.(`Transcribe failed: ${msg}`);
|
|
54448
|
+
const words = await transcribeNarrationWords(audioAbsPath, {
|
|
54449
|
+
apiKey: whisperKey,
|
|
54450
|
+
language: opts.transcribeLanguage
|
|
54451
|
+
});
|
|
54452
|
+
if (words.length > 0) {
|
|
54453
|
+
transcriptRelPath = beatTranscriptRelPath(id);
|
|
54454
|
+
const transcriptAbs = resolve17(projectDir, transcriptRelPath);
|
|
54455
|
+
await writeFile15(transcriptAbs, JSON.stringify(words, null, 2), "utf-8");
|
|
54456
|
+
transcriptWordCount = words.length;
|
|
54457
|
+
transcriptWords = words;
|
|
54458
|
+
} else {
|
|
54459
|
+
opts.onProgress?.(
|
|
54460
|
+
"Transcribe produced no word timings \u2014 narration plays without word-sync"
|
|
54461
|
+
);
|
|
54332
54462
|
}
|
|
54333
54463
|
}
|
|
54334
54464
|
}
|
|
@@ -54437,7 +54567,7 @@ async function executeSceneAdd(opts) {
|
|
|
54437
54567
|
const shouldSyncStoryboard = opts.syncStoryboard !== false && await pathExists2(storyboardAbsPath);
|
|
54438
54568
|
if (shouldSyncStoryboard) {
|
|
54439
54569
|
opts.onProgress?.("Syncing STORYBOARD.md...");
|
|
54440
|
-
const before = await
|
|
54570
|
+
const before = await readFile15(storyboardAbsPath, "utf-8");
|
|
54441
54571
|
const synced = upsertStoryboardBeat(before, {
|
|
54442
54572
|
beatId: id,
|
|
54443
54573
|
title: opts.headline,
|
|
@@ -54479,7 +54609,7 @@ async function executeSceneAdd(opts) {
|
|
|
54479
54609
|
});
|
|
54480
54610
|
const blockingIssue = syncPlan.issues.find((issue) => issue.severity === "error");
|
|
54481
54611
|
if (blockingIssue) return errResult(blockingIssue.message);
|
|
54482
|
-
const rootHtmlAfterSync = await
|
|
54612
|
+
const rootHtmlAfterSync = await readFile15(rootPath, "utf-8");
|
|
54483
54613
|
start = readClipStart(rootHtmlAfterSync, id) ?? 0;
|
|
54484
54614
|
} else {
|
|
54485
54615
|
start = nextSceneStart(rootHtmlBefore, SCENE_OVERLAP_SECONDS);
|
|
@@ -54653,7 +54783,7 @@ async function resolveSceneRepairTarget(rootArg = "index.html", projectArg = "."
|
|
|
54653
54783
|
const root = rootArg || "index.html";
|
|
54654
54784
|
const project = projectArg || ".";
|
|
54655
54785
|
const defaultProject = project === ".";
|
|
54656
|
-
if (defaultProject && root !== "index.html" && !root.toLowerCase().endsWith(".html") &&
|
|
54786
|
+
if (defaultProject && root !== "index.html" && !root.toLowerCase().endsWith(".html") && existsSync14(resolve17(root, "index.html"))) {
|
|
54657
54787
|
return { projectDir: resolve17(root), rootRel: "index.html" };
|
|
54658
54788
|
}
|
|
54659
54789
|
return { projectDir: resolve17(project), rootRel: root };
|
|
@@ -55522,7 +55652,7 @@ init_exec_safe();
|
|
|
55522
55652
|
init_remotion();
|
|
55523
55653
|
import { resolve as resolve36, dirname as dirname17, basename as basename10 } from "node:path";
|
|
55524
55654
|
import { writeFile as writeFile28, mkdir as mkdir18, rm as rm4 } from "node:fs/promises";
|
|
55525
|
-
import { existsSync as
|
|
55655
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
55526
55656
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
55527
55657
|
var ASS_STYLES = ["karaoke-sweep", "typewriter"];
|
|
55528
55658
|
var SENTENCE_BREAKS = /[.!?]/;
|
|
@@ -55703,7 +55833,7 @@ async function executeAnimatedCaption(options) {
|
|
|
55703
55833
|
const groups = groupWords(transcript.words, { wordsPerGroup, maxChars });
|
|
55704
55834
|
const absOutputPath = resolve36(process.cwd(), outputPath);
|
|
55705
55835
|
const outDir = dirname17(absOutputPath);
|
|
55706
|
-
if (!
|
|
55836
|
+
if (!existsSync35(outDir)) {
|
|
55707
55837
|
await mkdir18(outDir, { recursive: true });
|
|
55708
55838
|
}
|
|
55709
55839
|
if (tier === "ass") {
|
|
@@ -56250,14 +56380,14 @@ init_ai_review();
|
|
|
56250
56380
|
init_dist2();
|
|
56251
56381
|
init_engine();
|
|
56252
56382
|
init_ai_helpers();
|
|
56253
|
-
import { readFile as
|
|
56383
|
+
import { readFile as readFile31, writeFile as writeFile30 } from "node:fs/promises";
|
|
56254
56384
|
import { resolve as resolve40 } from "node:path";
|
|
56255
56385
|
async function executeSuggestEdit(options) {
|
|
56256
56386
|
try {
|
|
56257
56387
|
const apiKey = options.apiKey ?? process.env.GOOGLE_API_KEY;
|
|
56258
56388
|
if (!apiKey) return { success: false, error: "GOOGLE_API_KEY required for suggest" };
|
|
56259
56389
|
const filePath = resolve40(process.cwd(), options.projectPath);
|
|
56260
|
-
const content = await
|
|
56390
|
+
const content = await readFile31(filePath, "utf-8");
|
|
56261
56391
|
const data = JSON.parse(content);
|
|
56262
56392
|
const project = Project.fromJSON(data);
|
|
56263
56393
|
const gemini = new GeminiProvider();
|
|
@@ -56294,9 +56424,9 @@ init_project_config();
|
|
|
56294
56424
|
init_scene_lint();
|
|
56295
56425
|
init_root_sync();
|
|
56296
56426
|
init_review_report();
|
|
56297
|
-
import { existsSync as
|
|
56298
|
-
import { readdir as readdir4, readFile as
|
|
56299
|
-
import { basename as basename11, isAbsolute as isAbsolute5, join as
|
|
56427
|
+
import { existsSync as existsSync39 } from "node:fs";
|
|
56428
|
+
import { readdir as readdir4, readFile as readFile32, stat as stat5 } from "node:fs/promises";
|
|
56429
|
+
import { basename as basename11, isAbsolute as isAbsolute5, join as join22, relative as relative11, resolve as resolve41 } from "node:path";
|
|
56300
56430
|
async function inspectProject(opts) {
|
|
56301
56431
|
const projectDir = resolve41(opts.projectDir);
|
|
56302
56432
|
const issues = [];
|
|
@@ -56310,7 +56440,7 @@ async function inspectProject(opts) {
|
|
|
56310
56440
|
assets: { checked: 0, missing: [], stale: [], unknownFreshness: [] },
|
|
56311
56441
|
rootSync: { ok: null, issueCount: 0, issues: [] }
|
|
56312
56442
|
};
|
|
56313
|
-
if (!
|
|
56443
|
+
if (!existsSync39(projectDir) || !await isDirectory(projectDir)) {
|
|
56314
56444
|
issues.push({
|
|
56315
56445
|
severity: "error",
|
|
56316
56446
|
code: "PROJECT_NOT_FOUND",
|
|
@@ -56325,15 +56455,15 @@ async function inspectProject(opts) {
|
|
|
56325
56455
|
);
|
|
56326
56456
|
}
|
|
56327
56457
|
const coreFiles = {
|
|
56328
|
-
storyboard:
|
|
56329
|
-
design:
|
|
56330
|
-
config:
|
|
56331
|
-
legacyConfig:
|
|
56332
|
-
buildReport:
|
|
56333
|
-
root:
|
|
56458
|
+
storyboard: join22(projectDir, "STORYBOARD.md"),
|
|
56459
|
+
design: join22(projectDir, "DESIGN.md"),
|
|
56460
|
+
config: join22(projectDir, "vibe.config.json"),
|
|
56461
|
+
legacyConfig: join22(projectDir, "vibe.project.yaml"),
|
|
56462
|
+
buildReport: join22(projectDir, "build-report.json"),
|
|
56463
|
+
root: join22(projectDir, "index.html")
|
|
56334
56464
|
};
|
|
56335
56465
|
for (const [name, path2] of Object.entries(coreFiles)) {
|
|
56336
|
-
checks.files[name] =
|
|
56466
|
+
checks.files[name] = existsSync39(path2);
|
|
56337
56467
|
}
|
|
56338
56468
|
let beatIds = [];
|
|
56339
56469
|
let inspectedBeatIds = [];
|
|
@@ -56347,7 +56477,7 @@ async function inspectProject(opts) {
|
|
|
56347
56477
|
});
|
|
56348
56478
|
retryWith.push(`vibe init ${projectDir} --from "<brief>" --json`);
|
|
56349
56479
|
} else {
|
|
56350
|
-
const storyboard = await
|
|
56480
|
+
const storyboard = await readFile32(coreFiles.storyboard, "utf-8");
|
|
56351
56481
|
const validation = validateStoryboardMarkdown(storyboard);
|
|
56352
56482
|
storyboardBeats = validation.beats;
|
|
56353
56483
|
beatIds = validation.beats.map((beat) => beat.id);
|
|
@@ -56389,7 +56519,7 @@ async function inspectProject(opts) {
|
|
|
56389
56519
|
suggestedFix: "Create DESIGN.md or rerun `vibe init --from` in a new project."
|
|
56390
56520
|
});
|
|
56391
56521
|
} else {
|
|
56392
|
-
const design = await
|
|
56522
|
+
const design = await readFile32(coreFiles.design, "utf-8");
|
|
56393
56523
|
for (const issue of designPlaceholderIssues(design)) {
|
|
56394
56524
|
issues.push(issue);
|
|
56395
56525
|
}
|
|
@@ -56414,20 +56544,20 @@ async function inspectProject(opts) {
|
|
|
56414
56544
|
});
|
|
56415
56545
|
retryWith.push(`vibe build ${projectDir} --stage sync --json`);
|
|
56416
56546
|
}
|
|
56417
|
-
const compositionsDir =
|
|
56418
|
-
const existingComps =
|
|
56547
|
+
const compositionsDir = join22(projectDir, "compositions");
|
|
56548
|
+
const existingComps = existsSync39(compositionsDir) ? new Set(await listHtmlBasenames(compositionsDir)) : /* @__PURE__ */ new Set();
|
|
56419
56549
|
checks.compositions.expected = inspectedBeatIds.length;
|
|
56420
56550
|
for (const beatId of inspectedBeatIds) {
|
|
56421
56551
|
const file = `scene-${beatId}.html`;
|
|
56422
56552
|
if (existingComps.has(file)) {
|
|
56423
56553
|
checks.compositions.found++;
|
|
56424
56554
|
} else {
|
|
56425
|
-
checks.compositions.missing.push(
|
|
56555
|
+
checks.compositions.missing.push(join22("compositions", file));
|
|
56426
56556
|
issues.push({
|
|
56427
56557
|
severity: "error",
|
|
56428
56558
|
code: "MISSING_COMPOSITION",
|
|
56429
56559
|
message: `Composition for beat "${beatId}" is missing.`,
|
|
56430
|
-
file:
|
|
56560
|
+
file: join22("compositions", file),
|
|
56431
56561
|
scene: beatId,
|
|
56432
56562
|
beatId,
|
|
56433
56563
|
suggestedFix: "Run `vibe build --stage compose --json`."
|
|
@@ -56489,7 +56619,7 @@ async function inspectProject(opts) {
|
|
|
56489
56619
|
try {
|
|
56490
56620
|
const lint = await runProjectLint({ projectDir });
|
|
56491
56621
|
const lintFiles = opts.beatId ? lint.files.filter(
|
|
56492
|
-
(file) => file.file === "index.html" || file.file ===
|
|
56622
|
+
(file) => file.file === "index.html" || file.file === join22("compositions", `scene-${opts.beatId}.html`)
|
|
56493
56623
|
) : lint.files;
|
|
56494
56624
|
const lintCounts = countLintFindings(lintFiles);
|
|
56495
56625
|
checks.lint = {
|
|
@@ -56635,7 +56765,7 @@ function projectSourceReports(checks) {
|
|
|
56635
56765
|
async function inspectBuildReport(projectDir, reportPath, beatId, checks, issues, retryWith, storyboardBeats) {
|
|
56636
56766
|
checks.buildReport.exists = true;
|
|
56637
56767
|
try {
|
|
56638
|
-
const report = JSON.parse(await
|
|
56768
|
+
const report = JSON.parse(await readFile32(reportPath, "utf-8"));
|
|
56639
56769
|
if (typeof report.outputPath === "string") checks.buildReport.outputPath = report.outputPath;
|
|
56640
56770
|
const reportBeats = report.beats ?? [];
|
|
56641
56771
|
const selectedReportBeats = reportBeats.filter((item) => !beatId || item.id === beatId);
|
|
@@ -56772,7 +56902,7 @@ function inspectReportedAsset(opts) {
|
|
|
56772
56902
|
opts.checks.assets.checked++;
|
|
56773
56903
|
if (isExternalRef2(opts.value)) return;
|
|
56774
56904
|
const abs = isAbsolute5(opts.value) ? opts.value : resolve41(opts.projectDir, opts.value);
|
|
56775
|
-
if (
|
|
56905
|
+
if (existsSync39(abs)) return;
|
|
56776
56906
|
opts.checks.assets.missing.push(opts.value);
|
|
56777
56907
|
opts.issues.push({
|
|
56778
56908
|
severity: "warning",
|
|
@@ -56866,9 +56996,9 @@ init_exec_safe();
|
|
|
56866
56996
|
init_project_config();
|
|
56867
56997
|
init_storyboard_parse();
|
|
56868
56998
|
init_review_report();
|
|
56869
|
-
import { existsSync as
|
|
56870
|
-
import { readdir as readdir5, readFile as
|
|
56871
|
-
import { basename as basename12, isAbsolute as isAbsolute6, join as
|
|
56999
|
+
import { existsSync as existsSync40 } from "node:fs";
|
|
57000
|
+
import { readdir as readdir5, readFile as readFile33, stat as stat6 } from "node:fs/promises";
|
|
57001
|
+
import { basename as basename12, isAbsolute as isAbsolute6, join as join23, relative as relative12, resolve as resolve42 } from "node:path";
|
|
56872
57002
|
var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([".mp4", ".mov", ".webm", ".m4v"]);
|
|
56873
57003
|
var DEFAULT_AI_MODEL = "flash";
|
|
56874
57004
|
var BLACK_FRAME_MIN_DURATION_SEC = 1;
|
|
@@ -57369,11 +57499,11 @@ async function inspectRender(opts) {
|
|
|
57369
57499
|
}
|
|
57370
57500
|
async function renderExpectsAudio(projectDir, beatId) {
|
|
57371
57501
|
const root = resolve42(projectDir);
|
|
57372
|
-
const rootHtml = await readTextIfExists(
|
|
57502
|
+
const rootHtml = await readTextIfExists(join23(root, "index.html"));
|
|
57373
57503
|
if (rootHtml && rootHtmlExpectsAudio(rootHtml, beatId)) return true;
|
|
57374
|
-
const buildReport = await readJsonIfExists(
|
|
57504
|
+
const buildReport = await readJsonIfExists(join23(root, "build-report.json"));
|
|
57375
57505
|
if (buildReportExpectsAudio(buildReport, beatId)) return true;
|
|
57376
|
-
const storyboard = await readTextIfExists(
|
|
57506
|
+
const storyboard = await readTextIfExists(join23(root, "STORYBOARD.md"));
|
|
57377
57507
|
if (!storyboard) return false;
|
|
57378
57508
|
try {
|
|
57379
57509
|
const parsed = parseStoryboard(storyboard);
|
|
@@ -57384,7 +57514,7 @@ async function renderExpectsAudio(projectDir, beatId) {
|
|
|
57384
57514
|
`assets/narration-${beat.id}.wav`,
|
|
57385
57515
|
`assets/music-${beat.id}.mp3`,
|
|
57386
57516
|
`assets/music-${beat.id}.wav`
|
|
57387
|
-
].some((asset) =>
|
|
57517
|
+
].some((asset) => existsSync40(join23(root, asset)))
|
|
57388
57518
|
);
|
|
57389
57519
|
} catch {
|
|
57390
57520
|
return false;
|
|
@@ -57492,8 +57622,8 @@ async function maybeWriteRenderReport(projectDir, opts, result) {
|
|
|
57492
57622
|
}
|
|
57493
57623
|
function renderSourceReports(projectDir, videoPath, checks) {
|
|
57494
57624
|
const reports = [];
|
|
57495
|
-
if (
|
|
57496
|
-
if (
|
|
57625
|
+
if (existsSync40(join23(projectDir, "build-report.json"))) reports.push("build-report.json");
|
|
57626
|
+
if (existsSync40(join23(projectDir, "render-report.json"))) reports.push("render-report.json");
|
|
57497
57627
|
if (videoPath) reports.push(displayPath(videoPath));
|
|
57498
57628
|
if (checks.durationSec !== void 0 || checks.width !== void 0 || checks.height !== void 0) {
|
|
57499
57629
|
reports.push("ffprobe");
|
|
@@ -57506,31 +57636,31 @@ function renderSourceReports(projectDir, videoPath, checks) {
|
|
|
57506
57636
|
async function resolveRenderVideoPath(projectDir, explicit, beatId) {
|
|
57507
57637
|
if (explicit) {
|
|
57508
57638
|
const candidate = resolve42(process.cwd(), explicit);
|
|
57509
|
-
return
|
|
57639
|
+
return existsSync40(candidate) ? candidate : null;
|
|
57510
57640
|
}
|
|
57511
|
-
const renderReportPath =
|
|
57512
|
-
if (
|
|
57641
|
+
const renderReportPath = join23(projectDir, "render-report.json");
|
|
57642
|
+
if (existsSync40(renderReportPath)) {
|
|
57513
57643
|
try {
|
|
57514
|
-
const report = JSON.parse(await
|
|
57644
|
+
const report = JSON.parse(await readFile33(renderReportPath, "utf-8"));
|
|
57515
57645
|
const reportBeat = typeof report.beat === "string" ? report.beat : null;
|
|
57516
57646
|
if (!beatId && reportBeat === null || beatId && reportBeat === beatId) {
|
|
57517
57647
|
const candidate = resolveReportedPath(projectDir, report.outputPath);
|
|
57518
|
-
if (candidate &&
|
|
57648
|
+
if (candidate && existsSync40(candidate)) return candidate;
|
|
57519
57649
|
}
|
|
57520
57650
|
} catch {
|
|
57521
57651
|
}
|
|
57522
57652
|
}
|
|
57523
|
-
const reportPath =
|
|
57524
|
-
if (!beatId &&
|
|
57653
|
+
const reportPath = join23(projectDir, "build-report.json");
|
|
57654
|
+
if (!beatId && existsSync40(reportPath)) {
|
|
57525
57655
|
try {
|
|
57526
|
-
const report = JSON.parse(await
|
|
57656
|
+
const report = JSON.parse(await readFile33(reportPath, "utf-8"));
|
|
57527
57657
|
const candidate = resolveReportedPath(projectDir, report.outputPath);
|
|
57528
|
-
if (candidate &&
|
|
57658
|
+
if (candidate && existsSync40(candidate)) return candidate;
|
|
57529
57659
|
} catch {
|
|
57530
57660
|
}
|
|
57531
57661
|
}
|
|
57532
|
-
const rendersDir =
|
|
57533
|
-
if (!
|
|
57662
|
+
const rendersDir = join23(projectDir, "renders");
|
|
57663
|
+
if (!existsSync40(rendersDir)) return null;
|
|
57534
57664
|
const entries = await readdir5(rendersDir, { withFileTypes: true });
|
|
57535
57665
|
const candidates = [];
|
|
57536
57666
|
for (const entry of entries) {
|
|
@@ -57538,7 +57668,7 @@ async function resolveRenderVideoPath(projectDir, explicit, beatId) {
|
|
|
57538
57668
|
const ext = entry.name.slice(entry.name.lastIndexOf(".")).toLowerCase();
|
|
57539
57669
|
if (!VIDEO_EXTENSIONS.has(ext)) continue;
|
|
57540
57670
|
if (beatId && !entry.name.includes(sanitizeFileSegment2(beatId))) continue;
|
|
57541
|
-
const full =
|
|
57671
|
+
const full = join23(rendersDir, entry.name);
|
|
57542
57672
|
const info = await stat6(full);
|
|
57543
57673
|
candidates.push({ path: full, mtimeMs: info.mtimeMs });
|
|
57544
57674
|
}
|
|
@@ -57556,14 +57686,14 @@ async function runAiRenderReview(projectDir, videoPath, model) {
|
|
|
57556
57686
|
});
|
|
57557
57687
|
}
|
|
57558
57688
|
function resolveStoryboardPath(projectDir) {
|
|
57559
|
-
const storyboardPath =
|
|
57560
|
-
return
|
|
57689
|
+
const storyboardPath = join23(projectDir, "STORYBOARD.md");
|
|
57690
|
+
return existsSync40(storyboardPath) ? storyboardPath : null;
|
|
57561
57691
|
}
|
|
57562
57692
|
async function expectedDurationFromBuildReport(projectDir, beatId) {
|
|
57563
|
-
const reportPath =
|
|
57564
|
-
if (!
|
|
57693
|
+
const reportPath = join23(projectDir, "build-report.json");
|
|
57694
|
+
if (!existsSync40(reportPath)) return void 0;
|
|
57565
57695
|
try {
|
|
57566
|
-
const report = JSON.parse(await
|
|
57696
|
+
const report = JSON.parse(await readFile33(reportPath, "utf-8"));
|
|
57567
57697
|
const beats = beatId ? (report.beats ?? []).filter((beat) => beat.id === beatId) : report.beats ?? [];
|
|
57568
57698
|
const durations = beats.map(
|
|
57569
57699
|
(beat) => parseOptionalNumber2(beat.sceneDurationSec) ?? parseOptionalNumber2(beat.narration?.sceneDurationSec)
|
|
@@ -57575,10 +57705,10 @@ async function expectedDurationFromBuildReport(projectDir, beatId) {
|
|
|
57575
57705
|
}
|
|
57576
57706
|
}
|
|
57577
57707
|
async function beatTimingsFromBuildReport(projectDir, beatId) {
|
|
57578
|
-
const reportPath =
|
|
57579
|
-
if (!
|
|
57708
|
+
const reportPath = join23(projectDir, "build-report.json");
|
|
57709
|
+
if (!existsSync40(reportPath)) return [];
|
|
57580
57710
|
try {
|
|
57581
|
-
const report = JSON.parse(await
|
|
57711
|
+
const report = JSON.parse(await readFile33(reportPath, "utf-8"));
|
|
57582
57712
|
const source = report.beats ?? [];
|
|
57583
57713
|
const selected = beatId ? source.filter((beat) => beat.id === beatId) : source;
|
|
57584
57714
|
let cursor = 0;
|
|
@@ -57604,17 +57734,17 @@ async function beatTimingsFromBuildReport(projectDir, beatId) {
|
|
|
57604
57734
|
}
|
|
57605
57735
|
async function buildRenderReviewProjectContext(projectDir) {
|
|
57606
57736
|
const parts = [];
|
|
57607
|
-
const storyboard = await readTextIfExists(
|
|
57737
|
+
const storyboard = await readTextIfExists(join23(projectDir, "STORYBOARD.md"));
|
|
57608
57738
|
if (storyboard) {
|
|
57609
57739
|
parts.push(`STORYBOARD.md:
|
|
57610
57740
|
${truncateText(storyboard, 4e3)}`);
|
|
57611
57741
|
parts.push(`Storyboard beat cue summary:
|
|
57612
57742
|
${storyboardBeatCueSummary(storyboard)}`);
|
|
57613
57743
|
}
|
|
57614
|
-
const design = await readTextIfExists(
|
|
57744
|
+
const design = await readTextIfExists(join23(projectDir, "DESIGN.md"));
|
|
57615
57745
|
if (design) parts.push(`DESIGN.md:
|
|
57616
57746
|
${truncateText(design, 4e3)}`);
|
|
57617
|
-
const buildReport = await readTextIfExists(
|
|
57747
|
+
const buildReport = await readTextIfExists(join23(projectDir, "build-report.json"));
|
|
57618
57748
|
if (buildReport) parts.push(`build-report.json:
|
|
57619
57749
|
${truncateText(buildReport, 6e3)}`);
|
|
57620
57750
|
const timings = await beatTimingsFromBuildReport(projectDir);
|
|
@@ -57642,9 +57772,9 @@ function storyboardBeatCueSummary(storyboard) {
|
|
|
57642
57772
|
}
|
|
57643
57773
|
}
|
|
57644
57774
|
async function readTextIfExists(path2) {
|
|
57645
|
-
if (!
|
|
57775
|
+
if (!existsSync40(path2)) return null;
|
|
57646
57776
|
try {
|
|
57647
|
-
return await
|
|
57777
|
+
return await readFile33(path2, "utf-8");
|
|
57648
57778
|
} catch {
|
|
57649
57779
|
return null;
|
|
57650
57780
|
}
|
|
@@ -58462,16 +58592,16 @@ var generateTools = [
|
|
|
58462
58592
|
];
|
|
58463
58593
|
|
|
58464
58594
|
// src/tools/manifest/storyboard.ts
|
|
58465
|
-
import { existsSync as
|
|
58466
|
-
import { readFile as
|
|
58595
|
+
import { existsSync as existsSync43 } from "node:fs";
|
|
58596
|
+
import { readFile as readFile37, writeFile as writeFile41 } from "node:fs/promises";
|
|
58467
58597
|
import { resolve as resolve54 } from "node:path";
|
|
58468
58598
|
init_storyboard_parse();
|
|
58469
58599
|
init_storyboard_edit();
|
|
58470
58600
|
|
|
58471
58601
|
// src/commands/_shared/storyboard-revise.ts
|
|
58472
|
-
import { existsSync as
|
|
58473
|
-
import { readFile as
|
|
58474
|
-
import { join as
|
|
58602
|
+
import { existsSync as existsSync42 } from "node:fs";
|
|
58603
|
+
import { readFile as readFile36, writeFile as writeFile40 } from "node:fs/promises";
|
|
58604
|
+
import { join as join24, resolve as resolve53 } from "node:path";
|
|
58475
58605
|
|
|
58476
58606
|
// src/agent/adapters/index.ts
|
|
58477
58607
|
init_openai2();
|
|
@@ -58523,9 +58653,9 @@ init_storyboard_parse();
|
|
|
58523
58653
|
init_storyboard_edit();
|
|
58524
58654
|
async function executeStoryboardRevision(opts) {
|
|
58525
58655
|
const projectDir = resolve53(opts.projectDir);
|
|
58526
|
-
const storyboardPath =
|
|
58656
|
+
const storyboardPath = join24(projectDir, "STORYBOARD.md");
|
|
58527
58657
|
const retryWith = [`vibe storyboard revise ${projectDir} --from "<request>" --dry-run --json`];
|
|
58528
|
-
if (!
|
|
58658
|
+
if (!existsSync42(storyboardPath)) {
|
|
58529
58659
|
return failureResult({
|
|
58530
58660
|
projectDir,
|
|
58531
58661
|
storyboardPath,
|
|
@@ -58535,7 +58665,7 @@ async function executeStoryboardRevision(opts) {
|
|
|
58535
58665
|
retryWith: [`vibe init ${projectDir} --from "<brief>" --json`]
|
|
58536
58666
|
});
|
|
58537
58667
|
}
|
|
58538
|
-
const currentStoryboard = await
|
|
58668
|
+
const currentStoryboard = await readFile36(storyboardPath, "utf-8");
|
|
58539
58669
|
const projectConfig = await readProjectConfig(projectDir);
|
|
58540
58670
|
const explicitComposer = opts.composer ?? projectConfig.config.providers.composer ?? void 0;
|
|
58541
58671
|
let provider = explicitComposer ?? "claude";
|
|
@@ -58758,18 +58888,18 @@ function diffChangedBeats(before, after) {
|
|
|
58758
58888
|
return ids.filter((id) => JSON.stringify(beforeBeats.get(id) ?? null) !== JSON.stringify(afterBeats.get(id) ?? null));
|
|
58759
58889
|
}
|
|
58760
58890
|
async function readRevisionContext(projectDir) {
|
|
58761
|
-
const designMd = await readOptionalText(
|
|
58762
|
-
const buildReportJson = await readOptionalJsonText(
|
|
58891
|
+
const designMd = await readOptionalText(join24(projectDir, "DESIGN.md"));
|
|
58892
|
+
const buildReportJson = await readOptionalJsonText(join24(projectDir, "build-report.json"));
|
|
58763
58893
|
return { designMd, buildReportJson };
|
|
58764
58894
|
}
|
|
58765
58895
|
async function readOptionalText(path2) {
|
|
58766
|
-
if (!
|
|
58767
|
-
return
|
|
58896
|
+
if (!existsSync42(path2)) return null;
|
|
58897
|
+
return readFile36(path2, "utf-8");
|
|
58768
58898
|
}
|
|
58769
58899
|
async function readOptionalJsonText(path2) {
|
|
58770
|
-
if (!
|
|
58900
|
+
if (!existsSync42(path2)) return null;
|
|
58771
58901
|
try {
|
|
58772
|
-
const parsed = JSON.parse(await
|
|
58902
|
+
const parsed = JSON.parse(await readFile36(path2, "utf-8"));
|
|
58773
58903
|
return JSON.stringify(parsed, null, 2);
|
|
58774
58904
|
} catch {
|
|
58775
58905
|
return null;
|
|
@@ -58814,8 +58944,8 @@ var projectDirSchema = external_exports.object({
|
|
|
58814
58944
|
});
|
|
58815
58945
|
async function readStoryboard(projectDir) {
|
|
58816
58946
|
const path2 = resolve54(projectDir, "STORYBOARD.md");
|
|
58817
|
-
if (!
|
|
58818
|
-
return { path: path2, markdown: await
|
|
58947
|
+
if (!existsSync43(path2)) return null;
|
|
58948
|
+
return { path: path2, markdown: await readFile37(path2, "utf-8") };
|
|
58819
58949
|
}
|
|
58820
58950
|
var storyboardListTool = defineTool({
|
|
58821
58951
|
name: "storyboard_list",
|
|
@@ -59074,7 +59204,7 @@ var storyboardTools = [
|
|
|
59074
59204
|
// src/tools/manifest/remix.ts
|
|
59075
59205
|
import { writeFile as writeFile46 } from "node:fs/promises";
|
|
59076
59206
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
59077
|
-
import { join as
|
|
59207
|
+
import { join as join25 } from "node:path";
|
|
59078
59208
|
|
|
59079
59209
|
// src/commands/ai-script-pipeline.ts
|
|
59080
59210
|
var import_yaml6 = __toESM(require_dist2(), 1);
|
|
@@ -59084,9 +59214,9 @@ init_config();
|
|
|
59084
59214
|
init_audio();
|
|
59085
59215
|
init_ai_helpers();
|
|
59086
59216
|
init_video_utils();
|
|
59087
|
-
import { readFile as
|
|
59217
|
+
import { readFile as readFile38, writeFile as writeFile42, unlink as unlink3, rename as rename4 } from "node:fs/promises";
|
|
59088
59218
|
import { resolve as resolve55, extname as extname10 } from "node:path";
|
|
59089
|
-
import { existsSync as
|
|
59219
|
+
import { existsSync as existsSync44 } from "node:fs";
|
|
59090
59220
|
|
|
59091
59221
|
// src/commands/_shared/video-providers.ts
|
|
59092
59222
|
init_source();
|
|
@@ -59245,16 +59375,16 @@ async function executeRegenerateScene(options) {
|
|
|
59245
59375
|
};
|
|
59246
59376
|
try {
|
|
59247
59377
|
const outputDir = resolve55(process.cwd(), options.projectDir);
|
|
59248
|
-
if (!
|
|
59378
|
+
if (!existsSync44(outputDir)) {
|
|
59249
59379
|
return { ...result, error: `Project directory not found: ${outputDir}` };
|
|
59250
59380
|
}
|
|
59251
59381
|
const yamlPath = resolve55(outputDir, "storyboard.yaml");
|
|
59252
59382
|
const jsonPath = resolve55(outputDir, "storyboard.json");
|
|
59253
|
-
const storyboardPath =
|
|
59383
|
+
const storyboardPath = existsSync44(yamlPath) ? yamlPath : existsSync44(jsonPath) ? jsonPath : null;
|
|
59254
59384
|
if (!storyboardPath) {
|
|
59255
59385
|
return { ...result, error: `Storyboard not found in: ${outputDir} (expected storyboard.yaml or storyboard.json)` };
|
|
59256
59386
|
}
|
|
59257
|
-
const storyboardContent = await
|
|
59387
|
+
const storyboardContent = await readFile38(storyboardPath, "utf-8");
|
|
59258
59388
|
const segments = storyboardPath.endsWith(".yaml") ? (0, import_yaml6.parse)(storyboardContent).scenes : JSON.parse(storyboardContent);
|
|
59259
59389
|
for (const sceneNum of options.scenes) {
|
|
59260
59390
|
if (sceneNum < 1 || sceneNum > segments.length) {
|
|
@@ -59343,15 +59473,15 @@ IMPORTANT - Character appearance must match exactly: ${characterDesc}`;
|
|
|
59343
59473
|
const refSceneNum = options.referenceScene;
|
|
59344
59474
|
if (refSceneNum && refSceneNum >= 1 && refSceneNum <= segments.length && refSceneNum !== sceneNum) {
|
|
59345
59475
|
const refImagePath = resolve55(outputDir, `scene-${refSceneNum}.png`);
|
|
59346
|
-
if (
|
|
59347
|
-
referenceImageBuffer = await
|
|
59476
|
+
if (existsSync44(refImagePath)) {
|
|
59477
|
+
referenceImageBuffer = await readFile38(refImagePath);
|
|
59348
59478
|
}
|
|
59349
59479
|
} else if (!refSceneNum) {
|
|
59350
59480
|
for (let i = 1; i <= segments.length; i++) {
|
|
59351
59481
|
if (i !== sceneNum) {
|
|
59352
59482
|
const otherImagePath = resolve55(outputDir, `scene-${i}.png`);
|
|
59353
|
-
if (
|
|
59354
|
-
referenceImageBuffer = await
|
|
59483
|
+
if (existsSync44(otherImagePath)) {
|
|
59484
|
+
referenceImageBuffer = await readFile38(otherImagePath);
|
|
59355
59485
|
break;
|
|
59356
59486
|
}
|
|
59357
59487
|
}
|
|
@@ -59439,11 +59569,11 @@ Generate the single-person scene image now.`;
|
|
|
59439
59569
|
}
|
|
59440
59570
|
if (regenerateVideo && videoApiKey) {
|
|
59441
59571
|
options.onProgress?.(`Scene ${sceneNum}: regenerating video (${options.generator || "grok"})...`);
|
|
59442
|
-
if (!
|
|
59572
|
+
if (!existsSync44(imagePath)) {
|
|
59443
59573
|
result.failedScenes.push(sceneNum);
|
|
59444
59574
|
continue;
|
|
59445
59575
|
}
|
|
59446
|
-
const imageBuffer = await
|
|
59576
|
+
const imageBuffer = await readFile38(imagePath);
|
|
59447
59577
|
const videoDuration = segment.duration > 5 ? 10 : 5;
|
|
59448
59578
|
const maxRetries = options.retries ?? DEFAULT_VIDEO_RETRIES;
|
|
59449
59579
|
if (options.generator === "grok") {
|
|
@@ -59651,9 +59781,9 @@ Generate the single-person scene image now.`;
|
|
|
59651
59781
|
}
|
|
59652
59782
|
|
|
59653
59783
|
// src/commands/ai-highlights.ts
|
|
59654
|
-
import { readFile as
|
|
59784
|
+
import { readFile as readFile39, writeFile as writeFile43, mkdir as mkdir23 } from "node:fs/promises";
|
|
59655
59785
|
import { resolve as resolve56, dirname as dirname22, basename as basename14, extname as extname11 } from "node:path";
|
|
59656
|
-
import { existsSync as
|
|
59786
|
+
import { existsSync as existsSync45 } from "node:fs";
|
|
59657
59787
|
init_dist2();
|
|
59658
59788
|
init_engine();
|
|
59659
59789
|
init_api_key();
|
|
@@ -59684,7 +59814,7 @@ function filterHighlights(highlights, options) {
|
|
|
59684
59814
|
async function executeHighlights(options) {
|
|
59685
59815
|
try {
|
|
59686
59816
|
const absPath = resolve56(process.cwd(), options.media);
|
|
59687
|
-
if (!
|
|
59817
|
+
if (!existsSync45(absPath)) {
|
|
59688
59818
|
return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: `File not found: ${absPath}` };
|
|
59689
59819
|
}
|
|
59690
59820
|
const ext = extname11(absPath).toLowerCase();
|
|
@@ -59703,7 +59833,7 @@ async function executeHighlights(options) {
|
|
|
59703
59833
|
sourceDuration = await ffprobeDuration(absPath);
|
|
59704
59834
|
const gemini = new GeminiProvider();
|
|
59705
59835
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
59706
|
-
const videoBuffer = await
|
|
59836
|
+
const videoBuffer = await readFile39(absPath);
|
|
59707
59837
|
const criteriaText = options.criteria === "all" || !options.criteria ? "emotional, informative, and funny moments" : `${options.criteria} moments`;
|
|
59708
59838
|
const durationText = targetDuration ? `Target a total highlight duration of ${targetDuration} seconds.` : "";
|
|
59709
59839
|
const countText = maxCount ? `Find up to ${maxCount} highlights.` : "";
|
|
@@ -59797,10 +59927,10 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
59797
59927
|
}
|
|
59798
59928
|
const whisper = new WhisperProvider();
|
|
59799
59929
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
59800
|
-
const audioBuffer = await
|
|
59930
|
+
const audioBuffer = await readFile39(audioPath);
|
|
59801
59931
|
const audioBlob = new Blob([audioBuffer]);
|
|
59802
59932
|
const transcriptResult = await whisper.transcribe(audioBlob, options.language);
|
|
59803
|
-
if (tempAudioPath &&
|
|
59933
|
+
if (tempAudioPath && existsSync45(tempAudioPath)) {
|
|
59804
59934
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
59805
59935
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
59806
59936
|
});
|
|
@@ -59887,7 +60017,7 @@ async function executeAutoShorts(options) {
|
|
|
59887
60017
|
return { success: false, shorts: [], error: "FFmpeg not found" };
|
|
59888
60018
|
}
|
|
59889
60019
|
const absPath = resolve56(process.cwd(), options.video);
|
|
59890
|
-
if (!
|
|
60020
|
+
if (!existsSync45(absPath)) {
|
|
59891
60021
|
return { success: false, shorts: [], error: `File not found: ${absPath}` };
|
|
59892
60022
|
}
|
|
59893
60023
|
const targetDuration = options.duration ?? 60;
|
|
@@ -59900,7 +60030,7 @@ async function executeAutoShorts(options) {
|
|
|
59900
60030
|
}
|
|
59901
60031
|
const gemini = new GeminiProvider();
|
|
59902
60032
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
59903
|
-
const videoBuffer = await
|
|
60033
|
+
const videoBuffer = await readFile39(absPath);
|
|
59904
60034
|
const geminiPrompt = `Analyze this video to find the BEST moments for short-form vertical video content (TikTok, YouTube Shorts, Instagram Reels).
|
|
59905
60035
|
|
|
59906
60036
|
Find ${shortCount * 3} potential clips that are ${targetDuration} seconds or shorter each.
|
|
@@ -59977,7 +60107,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
59977
60107
|
await execSafe("ffmpeg", ["-i", absPath, "-vn", "-acodec", "libmp3lame", "-q:a", "2", tempAudio, "-y"]);
|
|
59978
60108
|
const whisper = new WhisperProvider();
|
|
59979
60109
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
59980
|
-
const audioBuffer = await
|
|
60110
|
+
const audioBuffer = await readFile39(tempAudio);
|
|
59981
60111
|
const audioBlob = new Blob([audioBuffer]);
|
|
59982
60112
|
const transcript = await whisper.transcribe(audioBlob, options.language);
|
|
59983
60113
|
try {
|
|
@@ -60015,7 +60145,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
60015
60145
|
};
|
|
60016
60146
|
}
|
|
60017
60147
|
const outputDir = options.outputDir ? resolve56(process.cwd(), options.outputDir) : dirname22(absPath);
|
|
60018
|
-
if (options.outputDir && !
|
|
60148
|
+
if (options.outputDir && !existsSync45(outputDir)) {
|
|
60019
60149
|
await mkdir23(outputDir, { recursive: true });
|
|
60020
60150
|
}
|
|
60021
60151
|
const result = {
|
|
@@ -60094,8 +60224,8 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
60094
60224
|
// src/pipeline/executor.ts
|
|
60095
60225
|
var import_yaml7 = __toESM(require_dist2(), 1);
|
|
60096
60226
|
import { resolve as resolve58 } from "node:path";
|
|
60097
|
-
import { readFile as
|
|
60098
|
-
import { existsSync as
|
|
60227
|
+
import { readFile as readFile41, writeFile as writeFile45, mkdir as mkdir24 } from "node:fs/promises";
|
|
60228
|
+
import { existsSync as existsSync46 } from "node:fs";
|
|
60099
60229
|
|
|
60100
60230
|
// src/pipeline/resolver.ts
|
|
60101
60231
|
function resolveStepParams(params, completedSteps) {
|
|
@@ -60508,10 +60638,10 @@ async function ensureActionsRegistered() {
|
|
|
60508
60638
|
}
|
|
60509
60639
|
async function loadPipeline(filePath) {
|
|
60510
60640
|
const absPath = resolve58(process.cwd(), filePath);
|
|
60511
|
-
if (!
|
|
60641
|
+
if (!existsSync46(absPath)) {
|
|
60512
60642
|
throw new Error(`Pipeline file not found: ${absPath}`);
|
|
60513
60643
|
}
|
|
60514
|
-
const content = await
|
|
60644
|
+
const content = await readFile41(absPath, "utf-8");
|
|
60515
60645
|
const manifest2 = (0, import_yaml7.parse)(content);
|
|
60516
60646
|
if (!manifest2.name) throw new Error("Pipeline missing 'name' field");
|
|
60517
60647
|
if (!manifest2.steps || !Array.isArray(manifest2.steps)) throw new Error("Pipeline missing 'steps' array");
|
|
@@ -60534,8 +60664,8 @@ async function executePipeline(manifest2, options = {}) {
|
|
|
60534
60664
|
const startTime = Date.now();
|
|
60535
60665
|
if (options.resume) {
|
|
60536
60666
|
const checkpointPath = resolve58(outputDir, CHECKPOINT_FILE);
|
|
60537
|
-
if (
|
|
60538
|
-
const checkpointContent = await
|
|
60667
|
+
if (existsSync46(checkpointPath)) {
|
|
60668
|
+
const checkpointContent = await readFile41(checkpointPath, "utf-8");
|
|
60539
60669
|
const checkpoint = (0, import_yaml7.parse)(checkpointContent);
|
|
60540
60670
|
for (const cs of checkpoint.completedSteps) {
|
|
60541
60671
|
completedSteps.set(cs.id, {
|
|
@@ -60807,7 +60937,7 @@ var pipelineRunTool = defineTool({
|
|
|
60807
60937
|
let resolvedPath = args.pipelinePath;
|
|
60808
60938
|
let tempPath;
|
|
60809
60939
|
if (args.pipelineYaml) {
|
|
60810
|
-
tempPath =
|
|
60940
|
+
tempPath = join25(tmpdir4(), `vibe-mcp-pipeline-${Date.now()}.yaml`);
|
|
60811
60941
|
await writeFile46(tempPath, args.pipelineYaml, "utf-8");
|
|
60812
60942
|
resolvedPath = tempPath;
|
|
60813
60943
|
}
|
|
@@ -60981,10 +61111,10 @@ init_project_resolver();
|
|
|
60981
61111
|
// src/tools/manifest/_project-io.ts
|
|
60982
61112
|
init_engine();
|
|
60983
61113
|
init_project_resolver();
|
|
60984
|
-
import { readFile as
|
|
61114
|
+
import { readFile as readFile42, writeFile as writeFile47 } from "node:fs/promises";
|
|
60985
61115
|
async function loadProject(projectPath, cwd) {
|
|
60986
61116
|
const absPath = await resolveTimelineFile(projectPath, cwd);
|
|
60987
|
-
const content = await
|
|
61117
|
+
const content = await readFile42(absPath, "utf-8");
|
|
60988
61118
|
const data = JSON.parse(content);
|
|
60989
61119
|
return { project: Project.fromJSON(data), absPath };
|
|
60990
61120
|
}
|
|
@@ -61334,12 +61464,12 @@ init_engine();
|
|
|
61334
61464
|
|
|
61335
61465
|
// src/commands/_shared/project-list.ts
|
|
61336
61466
|
init_storyboard_parse();
|
|
61337
|
-
import { readdir as readdir6, readFile as
|
|
61338
|
-
import { existsSync as
|
|
61339
|
-
import { join as
|
|
61467
|
+
import { readdir as readdir6, readFile as readFile43, stat as stat7 } from "node:fs/promises";
|
|
61468
|
+
import { existsSync as existsSync47 } from "node:fs";
|
|
61469
|
+
import { join as join26, resolve as resolve60 } from "node:path";
|
|
61340
61470
|
async function executeProjectList(opts = {}) {
|
|
61341
61471
|
const workspaceDir = resolve60(opts.workspaceDir ?? ".");
|
|
61342
|
-
if (!
|
|
61472
|
+
if (!existsSync47(workspaceDir)) {
|
|
61343
61473
|
return {
|
|
61344
61474
|
success: false,
|
|
61345
61475
|
workspaceDir,
|
|
@@ -61356,11 +61486,11 @@ async function executeProjectList(opts = {}) {
|
|
|
61356
61486
|
if (entry.name === "node_modules") continue;
|
|
61357
61487
|
if (entry.name.startsWith(".")) continue;
|
|
61358
61488
|
if (entry.name.startsWith("_")) {
|
|
61359
|
-
archivedCount += await countProjects(
|
|
61489
|
+
archivedCount += await countProjects(join26(workspaceDir, entry.name));
|
|
61360
61490
|
continue;
|
|
61361
61491
|
}
|
|
61362
|
-
const projectDir =
|
|
61363
|
-
if (!
|
|
61492
|
+
const projectDir = join26(workspaceDir, entry.name);
|
|
61493
|
+
if (!existsSync47(join26(projectDir, "STORYBOARD.md"))) continue;
|
|
61364
61494
|
projects.push(await describeProject(entry.name, projectDir));
|
|
61365
61495
|
}
|
|
61366
61496
|
projects.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
@@ -61370,7 +61500,7 @@ async function countProjects(dir) {
|
|
|
61370
61500
|
try {
|
|
61371
61501
|
const entries = await readdir6(dir, { withFileTypes: true });
|
|
61372
61502
|
return entries.filter(
|
|
61373
|
-
(e) => e.isDirectory() &&
|
|
61503
|
+
(e) => e.isDirectory() && existsSync47(join26(dir, e.name, "STORYBOARD.md"))
|
|
61374
61504
|
).length;
|
|
61375
61505
|
} catch {
|
|
61376
61506
|
return 0;
|
|
@@ -61381,8 +61511,8 @@ async function describeProject(name, projectDir) {
|
|
|
61381
61511
|
let storyboardDurationSec = 0;
|
|
61382
61512
|
const timestamps = [];
|
|
61383
61513
|
try {
|
|
61384
|
-
const storyboardPath =
|
|
61385
|
-
const parsed = parseStoryboard(await
|
|
61514
|
+
const storyboardPath = join26(projectDir, "STORYBOARD.md");
|
|
61515
|
+
const parsed = parseStoryboard(await readFile43(storyboardPath, "utf-8"));
|
|
61386
61516
|
beats = parsed.beats.length;
|
|
61387
61517
|
storyboardDurationSec = Number(
|
|
61388
61518
|
parsed.beats.reduce((sum, beat) => sum + (beat.duration ?? 0), 0).toFixed(2)
|
|
@@ -61393,8 +61523,8 @@ async function describeProject(name, projectDir) {
|
|
|
61393
61523
|
let buildStatus;
|
|
61394
61524
|
let buildUpdatedAt;
|
|
61395
61525
|
try {
|
|
61396
|
-
const reportPath =
|
|
61397
|
-
const report = JSON.parse(await
|
|
61526
|
+
const reportPath = join26(projectDir, "build-report.json");
|
|
61527
|
+
const report = JSON.parse(await readFile43(reportPath, "utf-8"));
|
|
61398
61528
|
buildStatus = report.status ?? report.phase;
|
|
61399
61529
|
buildUpdatedAt = report.updatedAt;
|
|
61400
61530
|
timestamps.push((await stat7(reportPath)).mtimeMs);
|
|
@@ -61402,12 +61532,12 @@ async function describeProject(name, projectDir) {
|
|
|
61402
61532
|
}
|
|
61403
61533
|
let latestRender;
|
|
61404
61534
|
try {
|
|
61405
|
-
const rendersDir =
|
|
61535
|
+
const rendersDir = join26(projectDir, "renders");
|
|
61406
61536
|
const files = await readdir6(rendersDir);
|
|
61407
61537
|
let newest;
|
|
61408
61538
|
for (const file of files) {
|
|
61409
61539
|
if (!/\.(mp4|webm|mov)$/i.test(file)) continue;
|
|
61410
|
-
const { mtimeMs } = await stat7(
|
|
61540
|
+
const { mtimeMs } = await stat7(join26(rendersDir, file));
|
|
61411
61541
|
if (!newest || mtimeMs > newest.mtimeMs) newest = { file, mtimeMs };
|
|
61412
61542
|
}
|
|
61413
61543
|
if (newest) {
|
|
@@ -61541,7 +61671,7 @@ init_exec_safe();
|
|
|
61541
61671
|
init_output();
|
|
61542
61672
|
init_validate();
|
|
61543
61673
|
init_project_resolver();
|
|
61544
|
-
import { readFile as
|
|
61674
|
+
import { readFile as readFile44, access as access6 } from "node:fs/promises";
|
|
61545
61675
|
import { resolve as resolve62, basename as basename17 } from "node:path";
|
|
61546
61676
|
import { spawn } from "node:child_process";
|
|
61547
61677
|
function timelineBaseName(path2) {
|
|
@@ -61618,7 +61748,7 @@ async function runExport(projectPath, outputPath, options = {}) {
|
|
|
61618
61748
|
};
|
|
61619
61749
|
}
|
|
61620
61750
|
const filePath = await resolveTimelineFile(projectPath);
|
|
61621
|
-
const content = await
|
|
61751
|
+
const content = await readFile44(filePath, "utf-8");
|
|
61622
61752
|
const data = JSON.parse(content);
|
|
61623
61753
|
const project = Project.fromJSON(data);
|
|
61624
61754
|
const summary = project.getSummary();
|
|
@@ -61742,7 +61872,7 @@ Run 'vibe schema export' for structured parameter info.`).action(async (projectP
|
|
|
61742
61872
|
}
|
|
61743
61873
|
spinner2.text = "Loading project...";
|
|
61744
61874
|
const filePath = await resolveTimelineFile(projectPath);
|
|
61745
|
-
const content = await
|
|
61875
|
+
const content = await readFile44(filePath, "utf-8");
|
|
61746
61876
|
const data = JSON.parse(content);
|
|
61747
61877
|
const project = Project.fromJSON(data);
|
|
61748
61878
|
const summary = project.getSummary();
|
|
@@ -62340,14 +62470,14 @@ function getPresetSettings(preset, aspectRatio2) {
|
|
|
62340
62470
|
}
|
|
62341
62471
|
async function runHyperframesExport(projectPath, options, spinner2, startedAt) {
|
|
62342
62472
|
spinner2.text = "Loading project...";
|
|
62343
|
-
const { readFile:
|
|
62473
|
+
const { readFile: readFile46 } = await import("node:fs/promises");
|
|
62344
62474
|
const { resolve: resolve66 } = await import("node:path");
|
|
62345
62475
|
const { Project: Project2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
62346
62476
|
const { createHyperframesBackend: createHyperframesBackend2 } = await Promise.resolve().then(() => (init_hyperframes(), hyperframes_exports));
|
|
62347
62477
|
const { exitWithError: exitWithError2, generalError: generalError2, outputSuccess: outputSuccess2 } = await Promise.resolve().then(() => (init_output(), output_exports));
|
|
62348
62478
|
const chalk2 = (await Promise.resolve().then(() => (init_source(), source_exports))).default;
|
|
62349
62479
|
const filePath = await resolveTimelineFile(projectPath);
|
|
62350
|
-
const content = await
|
|
62480
|
+
const content = await readFile46(filePath, "utf-8");
|
|
62351
62481
|
const project = Project2.fromJSON(JSON.parse(content));
|
|
62352
62482
|
const state = project.getState();
|
|
62353
62483
|
const outputPath = options.output ? resolve66(process.cwd(), options.output) : resolve66(process.cwd(), `${timelineBaseName(projectPath)}.${options.format ?? "mp4"}`);
|
|
@@ -62416,8 +62546,8 @@ var exportVideoTool = defineTool({
|
|
|
62416
62546
|
var exportTools = [exportVideoTool];
|
|
62417
62547
|
|
|
62418
62548
|
// src/tools/manifest/agent-only.ts
|
|
62419
|
-
import { readFile as
|
|
62420
|
-
import { resolve as resolve64, join as
|
|
62549
|
+
import { readFile as readFile45, writeFile as writeFile50, readdir as readdir7, stat as stat8, access as access7, unlink as unlink4 } from "node:fs/promises";
|
|
62550
|
+
import { resolve as resolve64, join as join27, basename as basename18, extname as extname12 } from "node:path";
|
|
62421
62551
|
init_engine();
|
|
62422
62552
|
init_exec_safe();
|
|
62423
62553
|
init_project_resolver();
|
|
@@ -62495,7 +62625,7 @@ var fsListTool = defineTool({
|
|
|
62495
62625
|
const results = [];
|
|
62496
62626
|
for (const entry of entries) {
|
|
62497
62627
|
if (args.pattern && !matchPattern(entry.name, args.pattern)) continue;
|
|
62498
|
-
const fullPath =
|
|
62628
|
+
const fullPath = join27(absPath, entry.name);
|
|
62499
62629
|
const stats = await stat8(fullPath);
|
|
62500
62630
|
if (entry.isDirectory()) {
|
|
62501
62631
|
results.push(`[DIR] ${entry.name}/`);
|
|
@@ -62524,7 +62654,7 @@ var fsReadTool = defineTool({
|
|
|
62524
62654
|
async execute(args, ctx) {
|
|
62525
62655
|
try {
|
|
62526
62656
|
const absPath = resolve64(ctx.workingDirectory, args.path);
|
|
62527
|
-
const content = await
|
|
62657
|
+
const content = await readFile45(absPath, "utf-8");
|
|
62528
62658
|
const maxLength = 4e3;
|
|
62529
62659
|
const truncated = content.length > maxLength;
|
|
62530
62660
|
const output = truncated ? content.substring(0, maxLength) + "\n... (truncated)" : content;
|
|
@@ -62597,7 +62727,7 @@ var batchImportTool = defineTool({
|
|
|
62597
62727
|
async execute(args, ctx) {
|
|
62598
62728
|
try {
|
|
62599
62729
|
const filePath = resolve64(ctx.workingDirectory, args.project);
|
|
62600
|
-
const content = await
|
|
62730
|
+
const content = await readFile45(filePath, "utf-8");
|
|
62601
62731
|
const data = JSON.parse(content);
|
|
62602
62732
|
const project = Project.fromJSON(data);
|
|
62603
62733
|
const dirPath = resolve64(ctx.workingDirectory, args.directory);
|
|
@@ -62608,7 +62738,7 @@ var batchImportTool = defineTool({
|
|
|
62608
62738
|
const scanDir = async (dir) => {
|
|
62609
62739
|
const entries = await readdir7(dir, { withFileTypes: true });
|
|
62610
62740
|
for (const entry of entries) {
|
|
62611
|
-
const entryPath =
|
|
62741
|
+
const entryPath = join27(dir, entry.name);
|
|
62612
62742
|
if (entry.isDirectory() && recursive) {
|
|
62613
62743
|
await scanDir(entryPath);
|
|
62614
62744
|
} else if (entry.isFile()) {
|
|
@@ -62669,7 +62799,7 @@ var batchConcatTool = defineTool({
|
|
|
62669
62799
|
async execute(args, ctx) {
|
|
62670
62800
|
try {
|
|
62671
62801
|
const filePath = resolve64(ctx.workingDirectory, args.project);
|
|
62672
|
-
const content = await
|
|
62802
|
+
const content = await readFile45(filePath, "utf-8");
|
|
62673
62803
|
const data = JSON.parse(content);
|
|
62674
62804
|
const project = Project.fromJSON(data);
|
|
62675
62805
|
const sourceIds = args.sourceIds ?? [];
|
|
@@ -62740,7 +62870,7 @@ var batchApplyEffectTool = defineTool({
|
|
|
62740
62870
|
async execute(args, ctx) {
|
|
62741
62871
|
try {
|
|
62742
62872
|
const filePath = resolve64(ctx.workingDirectory, args.project);
|
|
62743
|
-
const content = await
|
|
62873
|
+
const content = await readFile45(filePath, "utf-8");
|
|
62744
62874
|
const data = JSON.parse(content);
|
|
62745
62875
|
const project = Project.fromJSON(data);
|
|
62746
62876
|
const clipIds = args.clipIds ?? [];
|
|
@@ -62795,7 +62925,7 @@ var timelineClearTool = defineTool({
|
|
|
62795
62925
|
async execute(args, ctx) {
|
|
62796
62926
|
try {
|
|
62797
62927
|
const filePath = await resolveTimelineFile(args.project, ctx.workingDirectory);
|
|
62798
|
-
const content = await
|
|
62928
|
+
const content = await readFile45(filePath, "utf-8");
|
|
62799
62929
|
const data = JSON.parse(content);
|
|
62800
62930
|
const project = Project.fromJSON(data);
|
|
62801
62931
|
const what = args.what ?? "clips";
|
|
@@ -62872,7 +63002,7 @@ var projectSetTool = defineTool({
|
|
|
62872
63002
|
async execute(args, ctx) {
|
|
62873
63003
|
try {
|
|
62874
63004
|
const filePath = await resolveTimelineFile(args.projectPath, ctx.workingDirectory);
|
|
62875
|
-
const content = await
|
|
63005
|
+
const content = await readFile45(filePath, "utf-8");
|
|
62876
63006
|
const data = JSON.parse(content);
|
|
62877
63007
|
const project = Project.fromJSON(data);
|
|
62878
63008
|
const updates = [];
|
|
@@ -63146,8 +63276,8 @@ var mediaCompressTool = defineTool({
|
|
|
63146
63276
|
],
|
|
63147
63277
|
{ maxBuffer: 50 * 1024 * 1024 }
|
|
63148
63278
|
);
|
|
63149
|
-
const inputBuf = await
|
|
63150
|
-
const outputBuf = await
|
|
63279
|
+
const inputBuf = await readFile45(absInput);
|
|
63280
|
+
const outputBuf = await readFile45(outputPath);
|
|
63151
63281
|
const inputSize = inputBuf.length;
|
|
63152
63282
|
const outputSize = outputBuf.length;
|
|
63153
63283
|
const reduction = (inputSize - outputSize) / inputSize * 100;
|