@vibeframe/cli 0.113.9 → 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 +766 -621
- package/dist/tools/manifest/index.js +610 -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;
|
|
@@ -31341,6 +31429,13 @@ ${finalDurationBullet}
|
|
|
31341
31429
|
- If the beat cues include \`narration\`, do not embed an \`<audio>\` tag in the
|
|
31342
31430
|
scene fragment. The root timeline wires \`assets/narration-${ctx.beat.id}.wav\`
|
|
31343
31431
|
or \`assets/narration-${ctx.beat.id}.mp3\` separately.
|
|
31432
|
+
- Do NOT invent sound effects. This scene fragment is visual-only: never add
|
|
31433
|
+
an \`<audio>\` tag, and never imply ambient/foley SFX (keyboard clicks, whoosh,
|
|
31434
|
+
applause, room tone) that the beat did not explicitly request. Audio comes
|
|
31435
|
+
only from the beat's explicit \`narration\` and \`music\` cues, wired by the
|
|
31436
|
+
root timeline. If the beat declares an explicit \`sfx\` cue, treat it as intent
|
|
31437
|
+
for the storyboard author \u2014 still do not embed it here \u2014 and only when it is
|
|
31438
|
+
clearly tied to the beat's narration/motion; ignore orphaned or generic SFX.
|
|
31344
31439
|
- **\`.clip\` elements get visibility control from the framework but NO
|
|
31345
31440
|
sizing.** Always give \`.clip\` explicit fill via CSS:
|
|
31346
31441
|
\`{ position: absolute; inset: 0; }\` (or equivalent
|
|
@@ -31406,7 +31501,7 @@ ${ctx.storyboardGlobal || "(no global direction)"}
|
|
|
31406
31501
|
|
|
31407
31502
|
## ${ctx.beat.heading}
|
|
31408
31503
|
${formatBeatCues(ctx.beat.cues)}${cueDurationNote}
|
|
31409
|
-
${ctx.beat.body}
|
|
31504
|
+
${ctx.beat.body}${formatTranscriptSection(ctx.transcript)}
|
|
31410
31505
|
|
|
31411
31506
|
=== Output format ===
|
|
31412
31507
|
|
|
@@ -31426,7 +31521,7 @@ function computeCacheKey(parts) {
|
|
|
31426
31521
|
return createHash4("sha256").update(parts.provider).update("\u241E").update(parts.model).update("\u241E").update(parts.systemPrompt).update("\u241E").update(parts.userPrompt).digest("hex");
|
|
31427
31522
|
}
|
|
31428
31523
|
function defaultCacheDir() {
|
|
31429
|
-
return
|
|
31524
|
+
return join10(USER_CACHE_DIR, "compose-scenes");
|
|
31430
31525
|
}
|
|
31431
31526
|
function extractHtml(responseText) {
|
|
31432
31527
|
const fenced = responseText.match(/```html\s*\n([\s\S]*?)\n```/);
|
|
@@ -31450,9 +31545,9 @@ async function composeBeatHtml(ctx, overrides) {
|
|
|
31450
31545
|
model: settings.model
|
|
31451
31546
|
});
|
|
31452
31547
|
const cacheDir = ctx.cacheDir ?? defaultCacheDir();
|
|
31453
|
-
const cachePath =
|
|
31454
|
-
if (
|
|
31455
|
-
const html2 = await
|
|
31548
|
+
const cachePath = join10(cacheDir, `${cacheKey}.html`);
|
|
31549
|
+
if (existsSync10(cachePath)) {
|
|
31550
|
+
const html2 = await readFile10(cachePath, "utf-8");
|
|
31456
31551
|
return { html: html2, cached: true, cacheKey, model: settings.model, provider: ctx.provider };
|
|
31457
31552
|
}
|
|
31458
31553
|
if (!ctx.apiKey) {
|
|
@@ -31588,14 +31683,14 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
31588
31683
|
const projectRoot = params.project ? resolve12(outputDir, params.project) : resolve12(outputDir);
|
|
31589
31684
|
const designPath = resolve12(projectRoot, params.design ?? "DESIGN.md");
|
|
31590
31685
|
const storyboardPath = resolve12(projectRoot, params.storyboard ?? "STORYBOARD.md");
|
|
31591
|
-
if (!
|
|
31686
|
+
if (!existsSync10(designPath)) {
|
|
31592
31687
|
return { success: false, error: `DESIGN.md not found at ${designPath}` };
|
|
31593
31688
|
}
|
|
31594
|
-
if (!
|
|
31689
|
+
if (!existsSync10(storyboardPath)) {
|
|
31595
31690
|
return { success: false, error: `STORYBOARD.md not found at ${storyboardPath}` };
|
|
31596
31691
|
}
|
|
31597
|
-
const designMd = await
|
|
31598
|
-
const storyboardMd = await
|
|
31692
|
+
const designMd = await readFile10(designPath, "utf-8");
|
|
31693
|
+
const storyboardMd = await readFile10(storyboardPath, "utf-8");
|
|
31599
31694
|
const { global: storyboardGlobal, beats } = parseStoryboard(storyboardMd);
|
|
31600
31695
|
if (beats.length === 0) {
|
|
31601
31696
|
return {
|
|
@@ -31615,7 +31710,7 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
31615
31710
|
provider = params.composer ?? "claude";
|
|
31616
31711
|
apiKey = "";
|
|
31617
31712
|
}
|
|
31618
|
-
const compositionsDir =
|
|
31713
|
+
const compositionsDir = join10(projectRoot, "compositions");
|
|
31619
31714
|
await mkdir6(compositionsDir, { recursive: true });
|
|
31620
31715
|
let finalDurations = /* @__PURE__ */ new Map();
|
|
31621
31716
|
try {
|
|
@@ -31629,6 +31724,7 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
31629
31724
|
async (beat, beatIndex) => {
|
|
31630
31725
|
onProgress({ type: "beat-start", beatId: beat.id, beatIndex, totalBeats });
|
|
31631
31726
|
try {
|
|
31727
|
+
const transcript = await readBeatTranscript(projectRoot, beat.id);
|
|
31632
31728
|
const result = await composeBeatWithRetry(
|
|
31633
31729
|
{
|
|
31634
31730
|
beat,
|
|
@@ -31639,11 +31735,12 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
31639
31735
|
apiKey,
|
|
31640
31736
|
effort: params.effort,
|
|
31641
31737
|
cacheDir: params.cacheDir,
|
|
31642
|
-
finalDurationSec: finalDurations.get(beat.id)
|
|
31738
|
+
finalDurationSec: finalDurations.get(beat.id),
|
|
31739
|
+
transcript
|
|
31643
31740
|
},
|
|
31644
31741
|
overrides
|
|
31645
31742
|
);
|
|
31646
|
-
const compositionPath =
|
|
31743
|
+
const compositionPath = join10(compositionsDir, `scene-${beat.id}.html`);
|
|
31647
31744
|
await mkdir6(dirname5(compositionPath), { recursive: true });
|
|
31648
31745
|
await writeFile12(compositionPath, result.html, "utf-8");
|
|
31649
31746
|
if (result.cached) {
|
|
@@ -31722,7 +31819,7 @@ ${failures.map((f) => ` - ${f.beatId}: ${f.error}`).join("\n")}`,
|
|
|
31722
31819
|
data: aggregateData
|
|
31723
31820
|
};
|
|
31724
31821
|
}
|
|
31725
|
-
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;
|
|
31726
31823
|
var init_compose_scenes_skills = __esm({
|
|
31727
31824
|
"src/commands/_shared/compose-scenes-skills.ts"() {
|
|
31728
31825
|
"use strict";
|
|
@@ -31734,6 +31831,7 @@ var init_compose_scenes_skills = __esm({
|
|
|
31734
31831
|
init_root_sync();
|
|
31735
31832
|
init_composer_resolve();
|
|
31736
31833
|
init_config();
|
|
31834
|
+
init_transcribe_narration();
|
|
31737
31835
|
MODEL_SETTINGS_BY_PROVIDER = {
|
|
31738
31836
|
// Anthropic Claude Sonnet 4.6 — v0.69 baseline. ~9 s/beat, $3/$15 per MTok.
|
|
31739
31837
|
claude: {
|
|
@@ -31757,6 +31855,7 @@ var init_compose_scenes_skills = __esm({
|
|
|
31757
31855
|
high: { model: GEMINI_DEFAULT_TEXT_MODEL, maxTokens: 8e3, costPerMTokIn: 1.5, costPerMTokOut: 9 }
|
|
31758
31856
|
}
|
|
31759
31857
|
};
|
|
31858
|
+
TRANSCRIPT_PROMPT_MAX_WORDS = 120;
|
|
31760
31859
|
ComposeBeatError = class extends Error {
|
|
31761
31860
|
constructor(code, message, cause) {
|
|
31762
31861
|
super(message);
|
|
@@ -31842,22 +31941,22 @@ var init_compose_scenes_skills = __esm({
|
|
|
31842
31941
|
});
|
|
31843
31942
|
|
|
31844
31943
|
// src/commands/_shared/compose-prompts.ts
|
|
31845
|
-
import { existsSync as
|
|
31846
|
-
import { readFile as
|
|
31847
|
-
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";
|
|
31848
31947
|
async function getComposePrompts(opts) {
|
|
31849
31948
|
const projectDir = resolve13(opts.projectDir);
|
|
31850
|
-
const designPath =
|
|
31851
|
-
const storyboardPath =
|
|
31852
|
-
const skillPath =
|
|
31853
|
-
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");
|
|
31854
31953
|
const warnings = [];
|
|
31855
31954
|
const baseError = (msg) => ({
|
|
31856
31955
|
success: false,
|
|
31857
31956
|
projectDir,
|
|
31858
31957
|
designReference: "DESIGN.md",
|
|
31859
31958
|
storyboardReference: "STORYBOARD.md",
|
|
31860
|
-
skillReference:
|
|
31959
|
+
skillReference: existsSync11(skillPath) ? "SKILL.md" : null,
|
|
31861
31960
|
compositionsDir: "compositions",
|
|
31862
31961
|
beats: [],
|
|
31863
31962
|
instructions: [],
|
|
@@ -31865,18 +31964,18 @@ async function getComposePrompts(opts) {
|
|
|
31865
31964
|
warnings,
|
|
31866
31965
|
error: msg
|
|
31867
31966
|
});
|
|
31868
|
-
if (!
|
|
31967
|
+
if (!existsSync11(designPath)) {
|
|
31869
31968
|
return baseError(`DESIGN.md not found at ${designPath}. Run \`vibe scene init <dir>\` first.`);
|
|
31870
31969
|
}
|
|
31871
|
-
if (!
|
|
31970
|
+
if (!existsSync11(storyboardPath)) {
|
|
31872
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.`);
|
|
31873
31972
|
}
|
|
31874
|
-
if (!
|
|
31973
|
+
if (!existsSync11(skillPath)) {
|
|
31875
31974
|
warnings.push(
|
|
31876
31975
|
"SKILL.md not installed \u2014 host agent won't have Hyperframes rules in context. Run `vibe scene install-skill` to install it."
|
|
31877
31976
|
);
|
|
31878
31977
|
}
|
|
31879
|
-
const storyboardMd = await
|
|
31978
|
+
const storyboardMd = await readFile11(storyboardPath, "utf-8");
|
|
31880
31979
|
const parsed = parseStoryboard(storyboardMd);
|
|
31881
31980
|
if (parsed.beats.length === 0) {
|
|
31882
31981
|
return baseError(`STORYBOARD.md has no \`## Beat \u2026\` headings.`);
|
|
@@ -31898,28 +31997,33 @@ async function getComposePrompts(opts) {
|
|
|
31898
31997
|
finalDurations = await resolveProjectBeatDurations(projectDir);
|
|
31899
31998
|
} catch {
|
|
31900
31999
|
}
|
|
31901
|
-
const result =
|
|
31902
|
-
|
|
31903
|
-
|
|
31904
|
-
|
|
31905
|
-
|
|
31906
|
-
beat
|
|
31907
|
-
|
|
31908
|
-
|
|
31909
|
-
|
|
31910
|
-
|
|
31911
|
-
|
|
31912
|
-
|
|
31913
|
-
|
|
31914
|
-
|
|
31915
|
-
|
|
31916
|
-
|
|
31917
|
-
|
|
31918
|
-
|
|
31919
|
-
|
|
31920
|
-
|
|
31921
|
-
|
|
31922
|
-
|
|
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;
|
|
31923
32027
|
const instructions = buildInstructions({
|
|
31924
32028
|
skillRef,
|
|
31925
32029
|
beatCount: result.length,
|
|
@@ -31967,13 +32071,14 @@ var init_compose_prompts = __esm({
|
|
|
31967
32071
|
init_compose_scenes_skills();
|
|
31968
32072
|
init_root_sync();
|
|
31969
32073
|
init_bundle();
|
|
32074
|
+
init_transcribe_narration();
|
|
31970
32075
|
}
|
|
31971
32076
|
});
|
|
31972
32077
|
|
|
31973
32078
|
// src/commands/_shared/sub-comp-duration-sync.ts
|
|
31974
|
-
import { existsSync as
|
|
31975
|
-
import { readFile as
|
|
31976
|
-
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";
|
|
31977
32082
|
function syncSubCompositionDurationHtml(html, beatId, newDurationSec, fileRel) {
|
|
31978
32083
|
const compositionId = `scene-${beatId}`;
|
|
31979
32084
|
const file = fileRel ?? `compositions/${compositionId}.html`;
|
|
@@ -32065,15 +32170,15 @@ async function createSubCompDurationSyncPlans(opts) {
|
|
|
32065
32170
|
const results = [];
|
|
32066
32171
|
for (const beat of opts.beats) {
|
|
32067
32172
|
const fileRel = `compositions/scene-${beat.id}.html`;
|
|
32068
|
-
const filePath =
|
|
32069
|
-
if (!
|
|
32173
|
+
const filePath = join13(projectDir, fileRel);
|
|
32174
|
+
if (!existsSync13(filePath)) continue;
|
|
32070
32175
|
const newDurationSec = await resolveSyncedBeatDuration({
|
|
32071
32176
|
projectDir,
|
|
32072
32177
|
beatDuration: beat.duration,
|
|
32073
32178
|
narrationPath: beat.narrationPath,
|
|
32074
32179
|
sceneDurationSec: beat.sceneDurationSec
|
|
32075
32180
|
});
|
|
32076
|
-
const html = await
|
|
32181
|
+
const html = await readFile13(filePath, "utf-8");
|
|
32077
32182
|
const outcome = syncSubCompositionDurationHtml(html, beat.id, newDurationSec, fileRel);
|
|
32078
32183
|
results.push({ ...outcome, file: fileRel, beatId: beat.id });
|
|
32079
32184
|
}
|
|
@@ -32097,7 +32202,7 @@ var init_sub_comp_duration_sync = __esm({
|
|
|
32097
32202
|
});
|
|
32098
32203
|
|
|
32099
32204
|
// src/commands/_shared/scene-repair.ts
|
|
32100
|
-
import { readFile as
|
|
32205
|
+
import { readFile as readFile14, writeFile as writeFile14 } from "node:fs/promises";
|
|
32101
32206
|
import { relative as relative5, resolve as resolve16 } from "node:path";
|
|
32102
32207
|
import { runHyperframeLint as runHyperframeLint3 } from "@hyperframes/producer";
|
|
32103
32208
|
async function executeSceneRepair(opts) {
|
|
@@ -32143,7 +32248,7 @@ async function executeSceneRepair(opts) {
|
|
|
32143
32248
|
}
|
|
32144
32249
|
for (const target of targets) {
|
|
32145
32250
|
const rel = relative5(projectDir, target.abs) || target.abs;
|
|
32146
|
-
const html = await
|
|
32251
|
+
const html = await readFile14(target.abs, "utf-8");
|
|
32147
32252
|
const findings = lintHtml(html, rel, target.isSub);
|
|
32148
32253
|
const { html: nextHtml, fixedCodes } = applyMechanicalFixes(html, findings);
|
|
32149
32254
|
if (fixedCodes.length > 0) {
|
|
@@ -32515,15 +32620,15 @@ var init_storyboard_edit = __esm({
|
|
|
32515
32620
|
});
|
|
32516
32621
|
|
|
32517
32622
|
// src/pipeline/renderers/chrome.ts
|
|
32518
|
-
import { existsSync as
|
|
32623
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
32519
32624
|
import { homedir as homedir5 } from "node:os";
|
|
32520
|
-
import { join as
|
|
32625
|
+
import { join as join14 } from "node:path";
|
|
32521
32626
|
function findChrome() {
|
|
32522
32627
|
const candidates = [
|
|
32523
32628
|
process.env.HYPERFRAMES_CHROME_PATH,
|
|
32524
32629
|
process.env.CHROME_PATH,
|
|
32525
32630
|
// puppeteer auto-downloaded headless shell
|
|
32526
|
-
|
|
32631
|
+
join14(
|
|
32527
32632
|
homedir5(),
|
|
32528
32633
|
".cache",
|
|
32529
32634
|
"puppeteer",
|
|
@@ -32541,7 +32646,7 @@ function findChrome() {
|
|
|
32541
32646
|
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
|
|
32542
32647
|
];
|
|
32543
32648
|
for (const c of candidates) {
|
|
32544
|
-
if (c &&
|
|
32649
|
+
if (c && existsSync15(c)) return c;
|
|
32545
32650
|
}
|
|
32546
32651
|
return void 0;
|
|
32547
32652
|
}
|
|
@@ -32573,7 +32678,7 @@ var init_ffmpeg_gate = __esm({
|
|
|
32573
32678
|
});
|
|
32574
32679
|
|
|
32575
32680
|
// src/commands/_shared/scene-audio-scan.ts
|
|
32576
|
-
import { readFile as
|
|
32681
|
+
import { readFile as readFile16 } from "node:fs/promises";
|
|
32577
32682
|
import { resolve as resolve18 } from "node:path";
|
|
32578
32683
|
function parseRootClips(rootHtml) {
|
|
32579
32684
|
const clips = [];
|
|
@@ -32614,7 +32719,7 @@ function makeFsCompositionReader(projectDir) {
|
|
|
32614
32719
|
return async (compositionSrcRel) => {
|
|
32615
32720
|
const abs = resolve18(projectDir, compositionSrcRel);
|
|
32616
32721
|
try {
|
|
32617
|
-
return await
|
|
32722
|
+
return await readFile16(abs, "utf-8");
|
|
32618
32723
|
} catch {
|
|
32619
32724
|
return null;
|
|
32620
32725
|
}
|
|
@@ -32814,9 +32919,9 @@ __export(scene_render_exports, {
|
|
|
32814
32919
|
qualityToCrf: () => qualityToCrf
|
|
32815
32920
|
});
|
|
32816
32921
|
import { execFile as execFile2 } from "node:child_process";
|
|
32817
|
-
import { existsSync as
|
|
32818
|
-
import { mkdir as mkdir9, readFile as
|
|
32819
|
-
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";
|
|
32820
32925
|
import { promisify as promisify2 } from "node:util";
|
|
32821
32926
|
import {
|
|
32822
32927
|
createRenderJob,
|
|
@@ -32908,7 +33013,7 @@ async function executeSceneRender(opts = {}) {
|
|
|
32908
33013
|
}
|
|
32909
33014
|
let autoSyncApplied = false;
|
|
32910
33015
|
let autoSyncWarning;
|
|
32911
|
-
if (!opts.beatId &&
|
|
33016
|
+
if (!opts.beatId && existsSync16(join15(projectDir, "STORYBOARD.md"))) {
|
|
32912
33017
|
try {
|
|
32913
33018
|
const rootPlan = await createProjectRootSyncPlan({ projectDir });
|
|
32914
33019
|
const subPlans = await createSubCompDurationSyncPlans({
|
|
@@ -33000,7 +33105,7 @@ async function executeSceneRender(opts = {}) {
|
|
|
33000
33105
|
let audioMuxWarning;
|
|
33001
33106
|
try {
|
|
33002
33107
|
opts.onProgress?.(0.95, "Mixing audio");
|
|
33003
|
-
const rootHtml = await
|
|
33108
|
+
const rootHtml = await readFile17(resolve20(projectDir, root), "utf-8");
|
|
33004
33109
|
const audios = await scanSceneAudio({ projectDir, rootHtml });
|
|
33005
33110
|
audioCount = audios.length;
|
|
33006
33111
|
if (audios.length > 0) {
|
|
@@ -33085,11 +33190,11 @@ async function safeStat(p) {
|
|
|
33085
33190
|
}
|
|
33086
33191
|
async function prepareBeatRenderRoot(opts) {
|
|
33087
33192
|
const root = `.vibeframe/tmp/render-beat-${sanitizeFileSegment(opts.beatId)}.html`;
|
|
33088
|
-
const reportPath =
|
|
33193
|
+
const reportPath = join15(opts.projectDir, "build-report.json");
|
|
33089
33194
|
const retryWith = [
|
|
33090
33195
|
`vibe build ${opts.projectDir} --beat ${opts.beatId} --stage sync --json`
|
|
33091
33196
|
];
|
|
33092
|
-
if (!
|
|
33197
|
+
if (!existsSync16(reportPath)) {
|
|
33093
33198
|
return {
|
|
33094
33199
|
success: false,
|
|
33095
33200
|
root,
|
|
@@ -33100,7 +33205,7 @@ async function prepareBeatRenderRoot(opts) {
|
|
|
33100
33205
|
}
|
|
33101
33206
|
let report;
|
|
33102
33207
|
try {
|
|
33103
|
-
report = JSON.parse(await
|
|
33208
|
+
report = JSON.parse(await readFile17(reportPath, "utf-8"));
|
|
33104
33209
|
} catch (error) {
|
|
33105
33210
|
return {
|
|
33106
33211
|
success: false,
|
|
@@ -33169,8 +33274,8 @@ async function prepareBeatRenderRoot(opts) {
|
|
|
33169
33274
|
durationSec,
|
|
33170
33275
|
narrationPath
|
|
33171
33276
|
});
|
|
33172
|
-
await mkdir9(dirname8(
|
|
33173
|
-
await writeFile16(
|
|
33277
|
+
await mkdir9(dirname8(join15(opts.projectDir, root)), { recursive: true });
|
|
33278
|
+
await writeFile16(join15(opts.projectDir, root), html, "utf-8");
|
|
33174
33279
|
return {
|
|
33175
33280
|
success: true,
|
|
33176
33281
|
root,
|
|
@@ -33234,7 +33339,7 @@ function reportedBeatAssetPaths(beat) {
|
|
|
33234
33339
|
]);
|
|
33235
33340
|
}
|
|
33236
33341
|
async function writeRenderReport(projectDir, result) {
|
|
33237
|
-
const reportPath =
|
|
33342
|
+
const reportPath = join15(projectDir, "render-report.json");
|
|
33238
33343
|
try {
|
|
33239
33344
|
await writeFile16(
|
|
33240
33345
|
reportPath,
|
|
@@ -33276,7 +33381,7 @@ async function writeRenderReport(projectDir, result) {
|
|
|
33276
33381
|
}
|
|
33277
33382
|
function pathExists3(projectDir, value) {
|
|
33278
33383
|
const abs = isAbsolute(value) ? value : resolve20(projectDir, value);
|
|
33279
|
-
return
|
|
33384
|
+
return existsSync16(abs);
|
|
33280
33385
|
}
|
|
33281
33386
|
function stringValue(value) {
|
|
33282
33387
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
@@ -33349,7 +33454,7 @@ var init_concurrency = __esm({
|
|
|
33349
33454
|
});
|
|
33350
33455
|
|
|
33351
33456
|
// src/commands/_shared/build-asset-reference.ts
|
|
33352
|
-
import { existsSync as
|
|
33457
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
33353
33458
|
import { extname as extname3, isAbsolute as isAbsolute2, relative as relative8, resolve as resolve21, sep } from "node:path";
|
|
33354
33459
|
function resolveTypedAssetReference(projectDir, kind, value) {
|
|
33355
33460
|
const raw = stringOrUndefined2(value);
|
|
@@ -33380,7 +33485,7 @@ function resolveReference(projectDir, kind, raw, source, forcedError) {
|
|
|
33380
33485
|
const absPath = isAbsolute2(raw) ? resolve21(raw) : resolve21(projectDir, raw);
|
|
33381
33486
|
const rel = relative8(resolve21(projectDir), absPath);
|
|
33382
33487
|
const withinProject = rel.length > 0 && !rel.startsWith("..") && !isAbsolute2(rel);
|
|
33383
|
-
const exists = withinProject &&
|
|
33488
|
+
const exists = withinProject && existsSync17(absPath);
|
|
33384
33489
|
let error = forcedError;
|
|
33385
33490
|
if (!error && inferred && inferred !== kind && !isNarrationAudioReference(kind, ext)) {
|
|
33386
33491
|
error = `Asset reference "${raw}" is a ${inferred} asset, not ${kind}.`;
|
|
@@ -33567,9 +33672,9 @@ var init_build_backdrop_prompt = __esm({
|
|
|
33567
33672
|
});
|
|
33568
33673
|
|
|
33569
33674
|
// src/commands/_shared/build-plan.ts
|
|
33570
|
-
import { existsSync as
|
|
33571
|
-
import { readFile as
|
|
33572
|
-
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";
|
|
33573
33678
|
function backdropCostUsd(quality) {
|
|
33574
33679
|
return quality === "hd" ? 0.2 : 0.05;
|
|
33575
33680
|
}
|
|
@@ -33578,11 +33683,11 @@ async function createBuildPlan(opts) {
|
|
|
33578
33683
|
loadPlanEnv(projectDir);
|
|
33579
33684
|
const stage = opts.stage ?? "all";
|
|
33580
33685
|
const config2 = await readProjectConfig(projectDir);
|
|
33581
|
-
const storyboardPath =
|
|
33686
|
+
const storyboardPath = join16(projectDir, "STORYBOARD.md");
|
|
33582
33687
|
const warnings = [];
|
|
33583
33688
|
const retryWith = [];
|
|
33584
33689
|
const providerResolution = [];
|
|
33585
|
-
if (!
|
|
33690
|
+
if (!existsSync18(storyboardPath)) {
|
|
33586
33691
|
const validation2 = {
|
|
33587
33692
|
ok: false,
|
|
33588
33693
|
issues: [
|
|
@@ -33611,7 +33716,7 @@ async function createBuildPlan(opts) {
|
|
|
33611
33716
|
validation: validation2
|
|
33612
33717
|
});
|
|
33613
33718
|
}
|
|
33614
|
-
const storyboardMd = await
|
|
33719
|
+
const storyboardMd = await readFile18(storyboardPath, "utf-8");
|
|
33615
33720
|
const validation = validateStoryboardMarkdown(storyboardMd);
|
|
33616
33721
|
const validationIssues = [...validation.issues];
|
|
33617
33722
|
const parsed = parseStoryboard(storyboardMd);
|
|
@@ -33637,7 +33742,7 @@ async function createBuildPlan(opts) {
|
|
|
33637
33742
|
const imageQuality = opts.imageQuality ?? config2.config.build.imageQuality ?? "hd";
|
|
33638
33743
|
const imageSize2 = opts.imageSize ?? config2.config.build.imageSize ?? "1536x1024";
|
|
33639
33744
|
const imageRatio = imageRatioForSize(imageSize2);
|
|
33640
|
-
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`)));
|
|
33641
33746
|
const resolved = await resolvePlanProviders({
|
|
33642
33747
|
projectDir,
|
|
33643
33748
|
config: config2,
|
|
@@ -33785,7 +33890,7 @@ async function createBuildPlan(opts) {
|
|
|
33785
33890
|
active: includeAssets
|
|
33786
33891
|
}) : null;
|
|
33787
33892
|
const compositionPath = `compositions/scene-${beat.id}.html`;
|
|
33788
|
-
const compositionExists =
|
|
33893
|
+
const compositionExists = existsSync18(join16(projectDir, compositionPath));
|
|
33789
33894
|
for (const asset of [narration, backdrop, keyframe, video, music]) {
|
|
33790
33895
|
if (!asset) continue;
|
|
33791
33896
|
if (asset.referenceError) {
|
|
@@ -33854,7 +33959,7 @@ async function createBuildPlan(opts) {
|
|
|
33854
33959
|
}
|
|
33855
33960
|
}
|
|
33856
33961
|
const validationOk = !validationIssues.some((issue) => issue.severity === "error");
|
|
33857
|
-
if (!
|
|
33962
|
+
if (!existsSync18(join16(projectDir, config2.config.composition.entry))) {
|
|
33858
33963
|
missing.add("root-composition");
|
|
33859
33964
|
if (validationOk) retryWith.push(`vibe build ${projectDir} --stage sync --json`);
|
|
33860
33965
|
}
|
|
@@ -33888,7 +33993,7 @@ async function createBuildPlan(opts) {
|
|
|
33888
33993
|
}
|
|
33889
33994
|
if (referenced.size > 0) {
|
|
33890
33995
|
const toGenerate = resolveCharacters(parsed.frontmatter).filter(
|
|
33891
|
-
(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`)))
|
|
33892
33997
|
);
|
|
33893
33998
|
if (toGenerate.length > 0) {
|
|
33894
33999
|
estimatedCostUsd += toGenerate.length * backdropCostUsd(opts.imageQuality ?? "hd");
|
|
@@ -34001,8 +34106,8 @@ function assetPlan(opts) {
|
|
|
34001
34106
|
reason: ready ? "referenced-asset" : "invalid-reference"
|
|
34002
34107
|
};
|
|
34003
34108
|
}
|
|
34004
|
-
const canonicalExists =
|
|
34005
|
-
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;
|
|
34006
34111
|
const freshness = assetFreshnessFromMetadata({
|
|
34007
34112
|
projectDir: opts.projectDir,
|
|
34008
34113
|
kind: opts.kind,
|
|
@@ -34200,7 +34305,7 @@ async function firstConfiguredComposer(projectDir) {
|
|
|
34200
34305
|
}
|
|
34201
34306
|
function firstExisting2(projectDir, paths) {
|
|
34202
34307
|
for (const path2 of paths) {
|
|
34203
|
-
if (
|
|
34308
|
+
if (existsSync18(join16(projectDir, path2))) return path2;
|
|
34204
34309
|
}
|
|
34205
34310
|
return null;
|
|
34206
34311
|
}
|
|
@@ -34241,12 +34346,12 @@ function unique3(items) {
|
|
|
34241
34346
|
return [...new Set(items.filter((item) => item.length > 0))];
|
|
34242
34347
|
}
|
|
34243
34348
|
function loadPlanEnv(projectDir) {
|
|
34244
|
-
(0, import_dotenv2.config)({ path:
|
|
34349
|
+
(0, import_dotenv2.config)({ path: join16(projectDir, ".env"), quiet: true });
|
|
34245
34350
|
(0, import_dotenv2.config)({ path: resolve22(process.cwd(), ".env"), quiet: true });
|
|
34246
34351
|
let dir = process.cwd();
|
|
34247
34352
|
while (dir !== dirname9(dir)) {
|
|
34248
|
-
if (
|
|
34249
|
-
(0, import_dotenv2.config)({ path:
|
|
34353
|
+
if (existsSync18(join16(dir, "pnpm-workspace.yaml"))) {
|
|
34354
|
+
(0, import_dotenv2.config)({ path: join16(dir, ".env"), quiet: true });
|
|
34250
34355
|
return;
|
|
34251
34356
|
}
|
|
34252
34357
|
dir = dirname9(dir);
|
|
@@ -34308,7 +34413,7 @@ var init_validate = __esm({
|
|
|
34308
34413
|
// src/commands/generate/music.ts
|
|
34309
34414
|
import { resolve as resolve24 } from "node:path";
|
|
34310
34415
|
import { writeFile as writeFile17 } from "node:fs/promises";
|
|
34311
|
-
import { existsSync as
|
|
34416
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
34312
34417
|
async function executeMusic(options) {
|
|
34313
34418
|
try {
|
|
34314
34419
|
const provider = options.provider || "elevenlabs";
|
|
@@ -34461,7 +34566,7 @@ function registerMusicCommand(parent) {
|
|
|
34461
34566
|
if (options.melody) {
|
|
34462
34567
|
spinner2.text = "Uploading melody reference...";
|
|
34463
34568
|
const absPath = resolve24(process.cwd(), options.melody);
|
|
34464
|
-
if (!
|
|
34569
|
+
if (!existsSync19(absPath)) {
|
|
34465
34570
|
spinner2.fail(`Melody file not found: ${options.melody}`);
|
|
34466
34571
|
exitWithError(notFoundError(options.melody));
|
|
34467
34572
|
}
|
|
@@ -34594,9 +34699,9 @@ __export(scene_build_exports, {
|
|
|
34594
34699
|
executeSceneBuild: () => executeSceneBuild,
|
|
34595
34700
|
resolveSceneBuildMode: () => resolveSceneBuildMode
|
|
34596
34701
|
});
|
|
34597
|
-
import { existsSync as
|
|
34598
|
-
import { copyFile as copyFile3, mkdir as mkdir10, readFile as
|
|
34599
|
-
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";
|
|
34600
34705
|
async function executeSceneBuild(opts) {
|
|
34601
34706
|
const startedAt = Date.now();
|
|
34602
34707
|
const projectDir = resolve25(opts.projectDir);
|
|
@@ -34615,7 +34720,7 @@ async function executeSceneBuild(opts) {
|
|
|
34615
34720
|
);
|
|
34616
34721
|
}
|
|
34617
34722
|
let sceneRepair = skippedSceneRepairSummary();
|
|
34618
|
-
const storyboardPath =
|
|
34723
|
+
const storyboardPath = join17(projectDir, "STORYBOARD.md");
|
|
34619
34724
|
const buildPlan = await createBuildPlan({
|
|
34620
34725
|
projectDir,
|
|
34621
34726
|
stage: selectedStage,
|
|
@@ -34666,8 +34771,8 @@ async function executeSceneBuild(opts) {
|
|
|
34666
34771
|
}
|
|
34667
34772
|
if (!buildPlan.validation.ok) {
|
|
34668
34773
|
let invalidBeats = [];
|
|
34669
|
-
if (
|
|
34670
|
-
invalidBeats = parseStoryboard(await
|
|
34774
|
+
if (existsSync20(storyboardPath)) {
|
|
34775
|
+
invalidBeats = parseStoryboard(await readFile19(storyboardPath, "utf-8")).beats;
|
|
34671
34776
|
}
|
|
34672
34777
|
return finishBuildResult({
|
|
34673
34778
|
success: false,
|
|
@@ -34691,7 +34796,7 @@ async function executeSceneBuild(opts) {
|
|
|
34691
34796
|
totalLatencyMs: Date.now() - startedAt
|
|
34692
34797
|
});
|
|
34693
34798
|
}
|
|
34694
|
-
if (!
|
|
34799
|
+
if (!existsSync20(storyboardPath)) {
|
|
34695
34800
|
return finishBuildResult({
|
|
34696
34801
|
success: false,
|
|
34697
34802
|
phase: "failed",
|
|
@@ -34705,7 +34810,7 @@ async function executeSceneBuild(opts) {
|
|
|
34705
34810
|
totalLatencyMs: Date.now() - startedAt
|
|
34706
34811
|
});
|
|
34707
34812
|
}
|
|
34708
|
-
const storyboardMd = await
|
|
34813
|
+
const storyboardMd = await readFile19(storyboardPath, "utf-8");
|
|
34709
34814
|
const parsed = parseStoryboard(storyboardMd);
|
|
34710
34815
|
if (parsed.beats.length === 0) {
|
|
34711
34816
|
return finishBuildResult({
|
|
@@ -34812,6 +34917,7 @@ async function executeSceneBuild(opts) {
|
|
|
34812
34917
|
skipVideo: opts.skipVideo ?? false,
|
|
34813
34918
|
skipKeyframe: opts.skipKeyframe ?? false,
|
|
34814
34919
|
skipMusic: opts.skipMusic ?? false,
|
|
34920
|
+
skipTranscript: opts.skipTranscript ?? false,
|
|
34815
34921
|
force: opts.force ?? false,
|
|
34816
34922
|
onProgress,
|
|
34817
34923
|
characterPaths
|
|
@@ -34899,9 +35005,9 @@ async function executeSceneBuild(opts) {
|
|
|
34899
35005
|
let composeData;
|
|
34900
35006
|
if (shouldRunStage(selectedStage, "compose")) {
|
|
34901
35007
|
if (mode === "agent") {
|
|
34902
|
-
const compositionsDir =
|
|
35008
|
+
const compositionsDir = join17(projectDir, "compositions");
|
|
34903
35009
|
const missingBeats = activeBeats.filter(
|
|
34904
|
-
(b) => !
|
|
35010
|
+
(b) => !existsSync20(join17(compositionsDir, `scene-${b.id}.html`))
|
|
34905
35011
|
);
|
|
34906
35012
|
if (missingBeats.length > 0) {
|
|
34907
35013
|
const plan = await getComposePrompts({ projectDir, beatId: opts.beatId });
|
|
@@ -34990,8 +35096,8 @@ async function executeSceneBuild(opts) {
|
|
|
34990
35096
|
if (stageReports.compose.status === "done") {
|
|
34991
35097
|
for (const beat of activeBeats) {
|
|
34992
35098
|
const videoRel = `assets/video-${beat.id}.mp4`;
|
|
34993
|
-
const compositionAbs =
|
|
34994
|
-
if (!
|
|
35099
|
+
const compositionAbs = join17(projectDir, "compositions", `scene-${beat.id}.html`);
|
|
35100
|
+
if (!existsSync20(join17(projectDir, videoRel)) || !existsSync20(compositionAbs)) continue;
|
|
34995
35101
|
const html = readFileSync3(compositionAbs, "utf-8");
|
|
34996
35102
|
if (!html.includes(videoRel)) {
|
|
34997
35103
|
warnings.push(
|
|
@@ -35049,7 +35155,7 @@ async function executeSceneBuild(opts) {
|
|
|
35049
35155
|
});
|
|
35050
35156
|
}
|
|
35051
35157
|
if (shouldRunStage(selectedStage, "sync")) {
|
|
35052
|
-
if (!
|
|
35158
|
+
if (!existsSync20(join17(projectDir, "index.html"))) {
|
|
35053
35159
|
await scaffoldSceneProject({
|
|
35054
35160
|
dir: projectDir,
|
|
35055
35161
|
name: projectDir.split(/[\\/]/).filter(Boolean).pop(),
|
|
@@ -35194,6 +35300,9 @@ async function buildBeatPrimitives(beat, ctx) {
|
|
|
35194
35300
|
ctx.skipVideo ? skipped("video", beat.id, "--skip-video", ctx) : dispatchVideo(beat, ctx, keyframe),
|
|
35195
35301
|
ctx.skipMusic ? skipped("music", beat.id, "--skip-music", ctx) : dispatchMusic(beat, ctx)
|
|
35196
35302
|
]);
|
|
35303
|
+
if (!ctx.skipTranscript) {
|
|
35304
|
+
await dispatchTranscript(beat, narration, ctx);
|
|
35305
|
+
}
|
|
35197
35306
|
return {
|
|
35198
35307
|
outcome: {
|
|
35199
35308
|
beatId: beat.id,
|
|
@@ -35311,7 +35420,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35311
35420
|
return {
|
|
35312
35421
|
status: "cached",
|
|
35313
35422
|
path: existingRel,
|
|
35314
|
-
durationSec: await safeAudioDuration(
|
|
35423
|
+
durationSec: await safeAudioDuration(join17(ctx.projectDir, existingRel)),
|
|
35315
35424
|
provider: metadata?.provider,
|
|
35316
35425
|
cachePath: metadata?.cachePath,
|
|
35317
35426
|
cacheKey: metadata?.cacheKey,
|
|
@@ -35335,9 +35444,9 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35335
35444
|
voice,
|
|
35336
35445
|
ext: resolution.audioExtension
|
|
35337
35446
|
});
|
|
35338
|
-
const cacheAbs =
|
|
35447
|
+
const cacheAbs = join17(ctx.projectDir, cache.path);
|
|
35339
35448
|
const rel = `assets/narration-${beat.id}.${resolution.audioExtension}`;
|
|
35340
|
-
const abs =
|
|
35449
|
+
const abs = join17(ctx.projectDir, rel);
|
|
35341
35450
|
if (existingRel && !ctx.force && isFreshCanonicalAsset({
|
|
35342
35451
|
projectDir: ctx.projectDir,
|
|
35343
35452
|
kind: "narration",
|
|
@@ -35351,7 +35460,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35351
35460
|
return {
|
|
35352
35461
|
status: "cached",
|
|
35353
35462
|
path: existingRel,
|
|
35354
|
-
durationSec: await safeAudioDuration(
|
|
35463
|
+
durationSec: await safeAudioDuration(join17(ctx.projectDir, existingRel)),
|
|
35355
35464
|
provider: resolution.provider,
|
|
35356
35465
|
cachePath: cache.path,
|
|
35357
35466
|
cacheKey: cache.key,
|
|
@@ -35359,7 +35468,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35359
35468
|
freshness: "fresh"
|
|
35360
35469
|
};
|
|
35361
35470
|
}
|
|
35362
|
-
if (
|
|
35471
|
+
if (existsSync20(cacheAbs) && !ctx.force) {
|
|
35363
35472
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35364
35473
|
await copyFile3(cacheAbs, abs);
|
|
35365
35474
|
await writeAssetMetadata({
|
|
@@ -35394,8 +35503,8 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35394
35503
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35395
35504
|
await writeFile18(abs, result.audioBuffer);
|
|
35396
35505
|
for (const otherExt of ["mp3", "wav"]) {
|
|
35397
|
-
const sibling =
|
|
35398
|
-
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 });
|
|
35399
35508
|
}
|
|
35400
35509
|
await mkdir10(dirname10(cacheAbs), { recursive: true });
|
|
35401
35510
|
await writeFile18(cacheAbs, result.audioBuffer);
|
|
@@ -35427,6 +35536,42 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35427
35536
|
freshness: "fresh"
|
|
35428
35537
|
};
|
|
35429
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
|
+
}
|
|
35430
35575
|
function characterSheetPrompt(name, description) {
|
|
35431
35576
|
return [
|
|
35432
35577
|
`Character reference sheet of an original fictional character named ${name}: ${description}.`,
|
|
@@ -35444,7 +35589,7 @@ async function buildCharacters(characters, ctx) {
|
|
|
35444
35589
|
const size = ctx.imageSize ?? "1536x1024";
|
|
35445
35590
|
for (const character of characters) {
|
|
35446
35591
|
if (character.imagePath) {
|
|
35447
|
-
if (
|
|
35592
|
+
if (existsSync20(join17(ctx.projectDir, character.imagePath))) {
|
|
35448
35593
|
paths.set(character.name, character.imagePath);
|
|
35449
35594
|
ctx.onProgress({ type: "character-cached", name: character.name, path: character.imagePath });
|
|
35450
35595
|
} else {
|
|
@@ -35457,7 +35602,7 @@ async function buildCharacters(characters, ctx) {
|
|
|
35457
35602
|
if (!character.prompt) continue;
|
|
35458
35603
|
const prompt3 = characterSheetPrompt(character.name, character.prompt);
|
|
35459
35604
|
const rel = `assets/character-${character.name}.png`;
|
|
35460
|
-
const abs =
|
|
35605
|
+
const abs = join17(ctx.projectDir, rel);
|
|
35461
35606
|
const metadataOptions = { quality: ctx.imageQuality, size };
|
|
35462
35607
|
const cache = characterCacheDescriptor({
|
|
35463
35608
|
name: character.name,
|
|
@@ -35466,7 +35611,7 @@ async function buildCharacters(characters, ctx) {
|
|
|
35466
35611
|
quality: ctx.imageQuality,
|
|
35467
35612
|
size
|
|
35468
35613
|
});
|
|
35469
|
-
if (
|
|
35614
|
+
if (existsSync20(abs) && !ctx.force) {
|
|
35470
35615
|
const metadata = readAssetMetadata(ctx.projectDir, "character", character.name);
|
|
35471
35616
|
if (!metadata || isFreshCanonicalAsset({
|
|
35472
35617
|
projectDir: ctx.projectDir,
|
|
@@ -35482,8 +35627,8 @@ async function buildCharacters(characters, ctx) {
|
|
|
35482
35627
|
continue;
|
|
35483
35628
|
}
|
|
35484
35629
|
}
|
|
35485
|
-
const cacheAbs =
|
|
35486
|
-
if (
|
|
35630
|
+
const cacheAbs = join17(ctx.projectDir, cache.path);
|
|
35631
|
+
if (existsSync20(cacheAbs) && !ctx.force) {
|
|
35487
35632
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35488
35633
|
await copyFile3(cacheAbs, abs);
|
|
35489
35634
|
await writeAssetMetadata({
|
|
@@ -35549,7 +35694,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
35549
35694
|
if (!cue) return { status: "no-cue" };
|
|
35550
35695
|
const prompt3 = augmentBackdropPrompt(cue);
|
|
35551
35696
|
const rel = `assets/backdrop-${beat.id}.png`;
|
|
35552
|
-
const abs =
|
|
35697
|
+
const abs = join17(ctx.projectDir, rel);
|
|
35553
35698
|
const size = ctx.imageSize ?? "1536x1024";
|
|
35554
35699
|
const ratio = imageRatioForSize(size);
|
|
35555
35700
|
const metadataOptions = { quality: ctx.imageQuality, size, ratio };
|
|
@@ -35562,7 +35707,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
35562
35707
|
ratio
|
|
35563
35708
|
});
|
|
35564
35709
|
const metadataPath = assetMetadataPath("backdrop", beat.id);
|
|
35565
|
-
if (
|
|
35710
|
+
if (existsSync20(abs) && !ctx.force) {
|
|
35566
35711
|
const metadata = readAssetMetadata(ctx.projectDir, "backdrop", beat.id);
|
|
35567
35712
|
if (!metadata || isFreshCanonicalAsset({
|
|
35568
35713
|
projectDir: ctx.projectDir,
|
|
@@ -35585,8 +35730,8 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
35585
35730
|
};
|
|
35586
35731
|
}
|
|
35587
35732
|
}
|
|
35588
|
-
const cacheAbs =
|
|
35589
|
-
if (
|
|
35733
|
+
const cacheAbs = join17(ctx.projectDir, cache.path);
|
|
35734
|
+
if (existsSync20(cacheAbs) && !ctx.force) {
|
|
35590
35735
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35591
35736
|
await copyFile3(cacheAbs, abs);
|
|
35592
35737
|
await writeAssetMetadata({
|
|
@@ -35718,7 +35863,7 @@ async function editKeyframeImage(prompt3, sheetBuffers, ctx, ratio) {
|
|
|
35718
35863
|
}
|
|
35719
35864
|
async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
35720
35865
|
const rel = `assets/keyframe-${beat.id}.png`;
|
|
35721
|
-
const abs =
|
|
35866
|
+
const abs = join17(ctx.projectDir, rel);
|
|
35722
35867
|
const size = ctx.imageSize ?? "1536x1024";
|
|
35723
35868
|
const ratio = imageRatioForSize(size);
|
|
35724
35869
|
const metadataOptions = { quality: ctx.imageQuality, size, ratio };
|
|
@@ -35731,7 +35876,7 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
35731
35876
|
ratio,
|
|
35732
35877
|
characters: characterRefsRel
|
|
35733
35878
|
});
|
|
35734
|
-
if (
|
|
35879
|
+
if (existsSync20(abs) && !ctx.force) {
|
|
35735
35880
|
const metadata = readAssetMetadata(ctx.projectDir, "keyframe", beat.id);
|
|
35736
35881
|
if (!metadata || isFreshCanonicalAsset({
|
|
35737
35882
|
projectDir: ctx.projectDir,
|
|
@@ -35745,8 +35890,8 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
35745
35890
|
return { status: "cached", rel };
|
|
35746
35891
|
}
|
|
35747
35892
|
}
|
|
35748
|
-
const cacheAbs =
|
|
35749
|
-
if (
|
|
35893
|
+
const cacheAbs = join17(ctx.projectDir, cache.path);
|
|
35894
|
+
if (existsSync20(cacheAbs) && !ctx.force) {
|
|
35750
35895
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35751
35896
|
await copyFile3(cacheAbs, abs);
|
|
35752
35897
|
await writeAssetMetadata({
|
|
@@ -35764,8 +35909,8 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
35764
35909
|
}
|
|
35765
35910
|
const sheetBuffers = [];
|
|
35766
35911
|
for (const relPath of characterRefsRel) {
|
|
35767
|
-
const sheetAbs =
|
|
35768
|
-
if (
|
|
35912
|
+
const sheetAbs = join17(ctx.projectDir, relPath);
|
|
35913
|
+
if (existsSync20(sheetAbs)) sheetBuffers.push(await readFile19(sheetAbs));
|
|
35769
35914
|
}
|
|
35770
35915
|
const imgCtx = {
|
|
35771
35916
|
projectDir: ctx.projectDir,
|
|
@@ -35834,9 +35979,9 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
35834
35979
|
const prompt3 = stringOrUndefined4(beat.cues?.video) ?? keyframeCue;
|
|
35835
35980
|
if (!prompt3) return { status: "no-cue" };
|
|
35836
35981
|
const characterRefsRel = beatCharacterNames(beat.cues).map((name) => ctx.characterPaths?.get(name)).filter((p) => typeof p === "string");
|
|
35837
|
-
const characterRefsAbs = characterRefsRel.map((p) =>
|
|
35982
|
+
const characterRefsAbs = characterRefsRel.map((p) => join17(ctx.projectDir, p));
|
|
35838
35983
|
const rel = `assets/video-${beat.id}.mp4`;
|
|
35839
|
-
const abs =
|
|
35984
|
+
const abs = join17(ctx.projectDir, rel);
|
|
35840
35985
|
const cache = videoCacheDescriptor({
|
|
35841
35986
|
beatId: beat.id,
|
|
35842
35987
|
cue: prompt3,
|
|
@@ -35848,7 +35993,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
35848
35993
|
keyframe: keyframeCue
|
|
35849
35994
|
});
|
|
35850
35995
|
const metadataPath = assetMetadataPath("video", beat.id);
|
|
35851
|
-
if (
|
|
35996
|
+
if (existsSync20(abs) && !ctx.force) {
|
|
35852
35997
|
const metadata = readAssetMetadata(ctx.projectDir, "video", beat.id);
|
|
35853
35998
|
if (!metadata || isFreshCanonicalAsset({
|
|
35854
35999
|
projectDir: ctx.projectDir,
|
|
@@ -35871,8 +36016,8 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
35871
36016
|
};
|
|
35872
36017
|
}
|
|
35873
36018
|
}
|
|
35874
|
-
const cacheAbs =
|
|
35875
|
-
if (
|
|
36019
|
+
const cacheAbs = join17(ctx.projectDir, cache.path);
|
|
36020
|
+
if (existsSync20(cacheAbs) && !ctx.force) {
|
|
35876
36021
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35877
36022
|
await copyFile3(cacheAbs, abs);
|
|
35878
36023
|
await writeAssetMetadata({
|
|
@@ -35903,7 +36048,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
35903
36048
|
ctx.onProgress({ type: "video-skipped", beatId: beat.id, reason });
|
|
35904
36049
|
return { status: "skipped" };
|
|
35905
36050
|
}
|
|
35906
|
-
const keyframeImageAbs = keyframeImageRel ?
|
|
36051
|
+
const keyframeImageAbs = keyframeImageRel ? join17(ctx.projectDir, keyframeImageRel) : void 0;
|
|
35907
36052
|
loadSceneBuildEnv(ctx.projectDir);
|
|
35908
36053
|
const result = await executeVideoGenerate({
|
|
35909
36054
|
prompt: prompt3,
|
|
@@ -35927,7 +36072,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
35927
36072
|
ctx.onProgress({ type: "video-failed", beatId: beat.id, error });
|
|
35928
36073
|
return { status: "failed", error };
|
|
35929
36074
|
}
|
|
35930
|
-
if (result.status === "completed" &&
|
|
36075
|
+
if (result.status === "completed" && existsSync20(abs)) {
|
|
35931
36076
|
await mkdir10(dirname10(cacheAbs), { recursive: true });
|
|
35932
36077
|
await copyFile3(abs, cacheAbs);
|
|
35933
36078
|
await writeAssetMetadata({
|
|
@@ -35995,7 +36140,7 @@ async function dispatchMusic(beat, ctx) {
|
|
|
35995
36140
|
const prompt3 = stringOrUndefined4(beat.cues?.music);
|
|
35996
36141
|
if (!prompt3) return { status: "no-cue" };
|
|
35997
36142
|
const rel = `assets/music-${beat.id}.mp3`;
|
|
35998
|
-
const abs =
|
|
36143
|
+
const abs = join17(ctx.projectDir, rel);
|
|
35999
36144
|
const duration = normalizeMusicDuration(beat.duration);
|
|
36000
36145
|
const cache = musicCacheDescriptor({
|
|
36001
36146
|
beatId: beat.id,
|
|
@@ -36004,7 +36149,7 @@ async function dispatchMusic(beat, ctx) {
|
|
|
36004
36149
|
duration
|
|
36005
36150
|
});
|
|
36006
36151
|
const metadataPath = assetMetadataPath("music", beat.id);
|
|
36007
|
-
if (
|
|
36152
|
+
if (existsSync20(abs) && !ctx.force) {
|
|
36008
36153
|
const metadata = readAssetMetadata(ctx.projectDir, "music", beat.id);
|
|
36009
36154
|
if (!metadata || isFreshCanonicalAsset({
|
|
36010
36155
|
projectDir: ctx.projectDir,
|
|
@@ -36028,8 +36173,8 @@ async function dispatchMusic(beat, ctx) {
|
|
|
36028
36173
|
};
|
|
36029
36174
|
}
|
|
36030
36175
|
}
|
|
36031
|
-
const cacheAbs =
|
|
36032
|
-
if (
|
|
36176
|
+
const cacheAbs = join17(ctx.projectDir, cache.path);
|
|
36177
|
+
if (existsSync20(cacheAbs) && !ctx.force) {
|
|
36033
36178
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
36034
36179
|
await copyFile3(cacheAbs, abs);
|
|
36035
36180
|
await writeAssetMetadata({
|
|
@@ -36103,7 +36248,7 @@ async function dispatchMusic(beat, ctx) {
|
|
|
36103
36248
|
metadataPath
|
|
36104
36249
|
};
|
|
36105
36250
|
}
|
|
36106
|
-
if (!
|
|
36251
|
+
if (!existsSync20(abs)) {
|
|
36107
36252
|
const error = result.outputPath ? `music output was not written at ${abs}` : "music generation did not return an output file";
|
|
36108
36253
|
ctx.onProgress({ type: "music-failed", beatId: beat.id, error });
|
|
36109
36254
|
return { status: "failed", error };
|
|
@@ -36139,12 +36284,12 @@ async function dispatchMusic(beat, ctx) {
|
|
|
36139
36284
|
};
|
|
36140
36285
|
}
|
|
36141
36286
|
function loadSceneBuildEnv(projectDir) {
|
|
36142
|
-
(0, import_dotenv3.config)({ path:
|
|
36287
|
+
(0, import_dotenv3.config)({ path: join17(projectDir, ".env"), quiet: true });
|
|
36143
36288
|
(0, import_dotenv3.config)({ path: resolve25(process.cwd(), ".env"), quiet: true });
|
|
36144
36289
|
let dir = process.cwd();
|
|
36145
36290
|
while (dir !== dirname10(dir)) {
|
|
36146
|
-
if (
|
|
36147
|
-
(0, import_dotenv3.config)({ path:
|
|
36291
|
+
if (existsSync20(join17(dir, "pnpm-workspace.yaml"))) {
|
|
36292
|
+
(0, import_dotenv3.config)({ path: join17(dir, ".env"), quiet: true });
|
|
36148
36293
|
return;
|
|
36149
36294
|
}
|
|
36150
36295
|
dir = dirname10(dir);
|
|
@@ -36360,7 +36505,7 @@ function mergeBeatOutcomes(base, updates) {
|
|
|
36360
36505
|
}
|
|
36361
36506
|
function firstExisting3(projectDir, relPaths) {
|
|
36362
36507
|
for (const rel of relPaths) {
|
|
36363
|
-
if (
|
|
36508
|
+
if (existsSync20(join17(projectDir, rel))) return rel;
|
|
36364
36509
|
}
|
|
36365
36510
|
return null;
|
|
36366
36511
|
}
|
|
@@ -36391,7 +36536,7 @@ async function apiKeyForVideoProvider(provider, projectDir) {
|
|
|
36391
36536
|
return getApiKeyFromConfig(providerKey, { cwd: projectDir });
|
|
36392
36537
|
}
|
|
36393
36538
|
async function finalizeBuildResult(projectDir, startedAt, result) {
|
|
36394
|
-
const reportPath =
|
|
36539
|
+
const reportPath = join17(projectDir, "build-report.json");
|
|
36395
36540
|
if (result.stageReports) {
|
|
36396
36541
|
for (const report of Object.values(result.stageReports)) {
|
|
36397
36542
|
if (report.status === "pending") report.status = "skipped";
|
|
@@ -36550,7 +36695,7 @@ function normalizeReportDuration(value) {
|
|
|
36550
36695
|
}
|
|
36551
36696
|
function buildReportComposition(projectDir, result, beatId) {
|
|
36552
36697
|
const path2 = `compositions/scene-${beatId}.html`;
|
|
36553
|
-
const exists =
|
|
36698
|
+
const exists = existsSync20(join17(projectDir, path2));
|
|
36554
36699
|
const written = composeWrittenEntry(result, beatId);
|
|
36555
36700
|
if (written) {
|
|
36556
36701
|
return {
|
|
@@ -36607,8 +36752,8 @@ function summarizeBuildBeats(projectDir, beats) {
|
|
|
36607
36752
|
let compositionsReady = 0;
|
|
36608
36753
|
for (const beat of beats) {
|
|
36609
36754
|
if (beatAssetsReady(beat)) assetsReady += 1;
|
|
36610
|
-
const compositionPath =
|
|
36611
|
-
if (
|
|
36755
|
+
const compositionPath = join17(projectDir, "compositions", `scene-${beat.beatId}.html`);
|
|
36756
|
+
if (existsSync20(compositionPath)) {
|
|
36612
36757
|
compositionsReady += 1;
|
|
36613
36758
|
} else {
|
|
36614
36759
|
needsAuthor.push(beat.beatId);
|
|
@@ -36650,6 +36795,8 @@ var init_scene_build = __esm({
|
|
|
36650
36795
|
init_compose_scenes_skills();
|
|
36651
36796
|
init_compose_prompts();
|
|
36652
36797
|
init_config();
|
|
36798
|
+
init_api_key();
|
|
36799
|
+
init_transcribe_narration();
|
|
36653
36800
|
init_scene_render();
|
|
36654
36801
|
init_storyboard_parse();
|
|
36655
36802
|
init_scene_project();
|
|
@@ -36778,18 +36925,18 @@ __export(ai_audio_exports, {
|
|
|
36778
36925
|
executeVoiceClone: () => executeVoiceClone
|
|
36779
36926
|
});
|
|
36780
36927
|
import { resolve as resolve27, dirname as dirname11, basename as basename5, extname as extname4 } from "node:path";
|
|
36781
|
-
import { readFile as
|
|
36782
|
-
import { existsSync as
|
|
36928
|
+
import { readFile as readFile20, writeFile as writeFile19 } from "node:fs/promises";
|
|
36929
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
36783
36930
|
async function executeTranscribe(options) {
|
|
36784
36931
|
const { audioPath, language, output, format, apiKey } = options;
|
|
36785
36932
|
try {
|
|
36786
36933
|
const key = apiKey || process.env.OPENAI_API_KEY;
|
|
36787
36934
|
if (!key) return { success: false, error: "OPENAI_API_KEY required" };
|
|
36788
36935
|
const absPath = resolve27(process.cwd(), audioPath);
|
|
36789
|
-
if (!
|
|
36936
|
+
if (!existsSync21(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
36790
36937
|
const whisper = new WhisperProvider();
|
|
36791
36938
|
await whisper.initialize({ apiKey: key });
|
|
36792
|
-
const audioBuffer = await
|
|
36939
|
+
const audioBuffer = await readFile20(absPath);
|
|
36793
36940
|
const audioBlob = new Blob([audioBuffer]);
|
|
36794
36941
|
const result = await whisper.transcribe(audioBlob, language);
|
|
36795
36942
|
if (result.status === "failed") {
|
|
@@ -36819,8 +36966,8 @@ async function executeIsolate(options) {
|
|
|
36819
36966
|
const key = apiKey || process.env.ELEVENLABS_API_KEY;
|
|
36820
36967
|
if (!key) return { success: false, error: "ELEVENLABS_API_KEY required" };
|
|
36821
36968
|
const absPath = resolve27(process.cwd(), audioPath);
|
|
36822
|
-
if (!
|
|
36823
|
-
const audioBuffer = await
|
|
36969
|
+
if (!existsSync21(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
36970
|
+
const audioBuffer = await readFile20(absPath);
|
|
36824
36971
|
const elevenlabs = new ElevenLabsProvider();
|
|
36825
36972
|
await elevenlabs.initialize({ apiKey: key });
|
|
36826
36973
|
const result = await elevenlabs.isolateVocals(audioBuffer);
|
|
@@ -36845,8 +36992,8 @@ async function executeVoiceClone(options) {
|
|
|
36845
36992
|
const audioBuffers = [];
|
|
36846
36993
|
for (const samplePath of samplePaths) {
|
|
36847
36994
|
const absPath = resolve27(process.cwd(), samplePath);
|
|
36848
|
-
if (!
|
|
36849
|
-
const buffer = await
|
|
36995
|
+
if (!existsSync21(absPath)) return { success: false, error: `File not found: ${samplePath}` };
|
|
36996
|
+
const buffer = await readFile20(absPath);
|
|
36850
36997
|
audioBuffers.push(buffer);
|
|
36851
36998
|
}
|
|
36852
36999
|
const elevenlabs = new ElevenLabsProvider();
|
|
@@ -36867,7 +37014,7 @@ async function executeDub(options) {
|
|
|
36867
37014
|
const { mediaPath, language, source, voice, analyzeOnly, output } = options;
|
|
36868
37015
|
try {
|
|
36869
37016
|
const absPath = resolve27(process.cwd(), mediaPath);
|
|
36870
|
-
if (!
|
|
37017
|
+
if (!existsSync21(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
36871
37018
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
36872
37019
|
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
36873
37020
|
const elevenlabsKey = process.env.ELEVENLABS_API_KEY;
|
|
@@ -36884,7 +37031,7 @@ async function executeDub(options) {
|
|
|
36884
37031
|
}
|
|
36885
37032
|
const whisper = new WhisperProvider();
|
|
36886
37033
|
await whisper.initialize({ apiKey: openaiKey });
|
|
36887
|
-
const audioBuffer = await
|
|
37034
|
+
const audioBuffer = await readFile20(audioPath);
|
|
36888
37035
|
const audioBlob = new Blob([audioBuffer]);
|
|
36889
37036
|
const transcriptResult = await whisper.transcribe(audioBlob, source);
|
|
36890
37037
|
if (transcriptResult.status === "failed" || !transcriptResult.segments) {
|
|
@@ -36995,8 +37142,8 @@ async function executeDuck(options) {
|
|
|
36995
37142
|
if (!commandExists("ffmpeg")) return { success: false, error: "FFmpeg not found" };
|
|
36996
37143
|
const absMusicPath = resolve27(process.cwd(), musicPath);
|
|
36997
37144
|
const absVoicePath = resolve27(process.cwd(), voicePath);
|
|
36998
|
-
if (!
|
|
36999
|
-
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}` };
|
|
37000
37147
|
const defaultOutput = resolve27(dirname11(absMusicPath), `${basename5(absMusicPath, extname4(absMusicPath))}-ducked${extname4(absMusicPath)}`);
|
|
37001
37148
|
const outputPath = resolve27(process.cwd(), output || defaultOutput);
|
|
37002
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`;
|
|
@@ -37010,7 +37157,7 @@ async function executeDuck(options) {
|
|
|
37010
37157
|
"-y",
|
|
37011
37158
|
outputPath
|
|
37012
37159
|
], { timeout: 12e4, maxBuffer: 50 * 1024 * 1024 });
|
|
37013
|
-
if (!
|
|
37160
|
+
if (!existsSync21(outputPath)) return { success: false, error: "FFmpeg failed to create output" };
|
|
37014
37161
|
return { success: true, outputPath };
|
|
37015
37162
|
} catch (error) {
|
|
37016
37163
|
return { success: false, error: `Audio ducking failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
@@ -37026,8 +37173,8 @@ var init_ai_audio = __esm({
|
|
|
37026
37173
|
});
|
|
37027
37174
|
|
|
37028
37175
|
// src/commands/_shared/edit/silence-cut.ts
|
|
37029
|
-
import { existsSync as
|
|
37030
|
-
import { readFile as
|
|
37176
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
37177
|
+
import { readFile as readFile21 } from "node:fs/promises";
|
|
37031
37178
|
async function detectSilencePeriods(videoPath, noiseThreshold, minDuration) {
|
|
37032
37179
|
const totalDuration = await getVideoDuration(videoPath);
|
|
37033
37180
|
const { stdout, stderr } = await execSafe(
|
|
@@ -37077,7 +37224,7 @@ async function detectSilencePeriodsWithGemini(videoPath, minDuration, options) {
|
|
|
37077
37224
|
}
|
|
37078
37225
|
const gemini = new GeminiProvider();
|
|
37079
37226
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
37080
|
-
const videoBuffer = await
|
|
37227
|
+
const videoBuffer = await readFile21(videoPath);
|
|
37081
37228
|
const modelId = options.model ? resolveGeminiTextModel(options.model) : void 0;
|
|
37082
37229
|
const prompt3 = `Analyze this video and identify all silent or dead segments where there is NO meaningful content.
|
|
37083
37230
|
|
|
@@ -37153,7 +37300,7 @@ async function executeSilenceCut(options) {
|
|
|
37153
37300
|
analyzeOnly = false,
|
|
37154
37301
|
useGemini = false
|
|
37155
37302
|
} = options;
|
|
37156
|
-
if (!
|
|
37303
|
+
if (!existsSync22(videoPath)) {
|
|
37157
37304
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
37158
37305
|
}
|
|
37159
37306
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37261,11 +37408,11 @@ var init_silence_cut = __esm({
|
|
|
37261
37408
|
});
|
|
37262
37409
|
|
|
37263
37410
|
// src/commands/_shared/edit/jump-cut.ts
|
|
37264
|
-
import { existsSync as
|
|
37265
|
-
import { readFile as
|
|
37266
|
-
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";
|
|
37267
37414
|
async function transcribeWithWords(audioPath, apiKey, language) {
|
|
37268
|
-
const audioBuffer = await
|
|
37415
|
+
const audioBuffer = await readFile22(audioPath);
|
|
37269
37416
|
const audioBlob = new Blob([audioBuffer]);
|
|
37270
37417
|
const formData = new FormData();
|
|
37271
37418
|
formData.append("file", audioBlob, "audio.wav");
|
|
@@ -37324,7 +37471,7 @@ async function executeJumpCut(options) {
|
|
|
37324
37471
|
analyzeOnly = false,
|
|
37325
37472
|
apiKey
|
|
37326
37473
|
} = options;
|
|
37327
|
-
if (!
|
|
37474
|
+
if (!existsSync23(videoPath)) {
|
|
37328
37475
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
37329
37476
|
}
|
|
37330
37477
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37343,7 +37490,7 @@ async function executeJumpCut(options) {
|
|
|
37343
37490
|
try {
|
|
37344
37491
|
const tmpDir = `/tmp/vibe_jumpcut_${Date.now()}`;
|
|
37345
37492
|
await mkdir11(tmpDir, { recursive: true });
|
|
37346
|
-
const audioPath =
|
|
37493
|
+
const audioPath = join18(tmpDir, "audio.wav");
|
|
37347
37494
|
try {
|
|
37348
37495
|
await execSafe(
|
|
37349
37496
|
"ffmpeg",
|
|
@@ -37397,7 +37544,7 @@ async function executeJumpCut(options) {
|
|
|
37397
37544
|
const segmentPaths = [];
|
|
37398
37545
|
for (let i = 0; i < segments.length; i++) {
|
|
37399
37546
|
const seg = segments[i];
|
|
37400
|
-
const segPath =
|
|
37547
|
+
const segPath = join18(tmpDir, `seg-${i.toString().padStart(4, "0")}.ts`);
|
|
37401
37548
|
const duration = seg.end - seg.start;
|
|
37402
37549
|
await execSafe(
|
|
37403
37550
|
"ffmpeg",
|
|
@@ -37420,7 +37567,7 @@ async function executeJumpCut(options) {
|
|
|
37420
37567
|
segmentPaths.push(segPath);
|
|
37421
37568
|
}
|
|
37422
37569
|
const concatList = segmentPaths.map((p) => `file '${p}'`).join("\n");
|
|
37423
|
-
const listPath =
|
|
37570
|
+
const listPath = join18(tmpDir, "concat.txt");
|
|
37424
37571
|
await writeFile20(listPath, concatList);
|
|
37425
37572
|
await execSafe(
|
|
37426
37573
|
"ffmpeg",
|
|
@@ -37527,8 +37674,8 @@ __export(remotion_exports, {
|
|
|
37527
37674
|
wrapComponentWithVideo: () => wrapComponentWithVideo
|
|
37528
37675
|
});
|
|
37529
37676
|
import { writeFile as writeFile21, mkdir as mkdir12, rm as rm2, copyFile as copyFile4 } from "node:fs/promises";
|
|
37530
|
-
import { existsSync as
|
|
37531
|
-
import { join as
|
|
37677
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
37678
|
+
import { join as join19 } from "node:path";
|
|
37532
37679
|
import { tmpdir } from "node:os";
|
|
37533
37680
|
import { createHash as createHash6 } from "node:crypto";
|
|
37534
37681
|
async function ensureRemotionInstalled() {
|
|
@@ -37547,7 +37694,7 @@ async function ensureRemotionInstalled() {
|
|
|
37547
37694
|
}
|
|
37548
37695
|
}
|
|
37549
37696
|
async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
37550
|
-
const dir =
|
|
37697
|
+
const dir = join19(tmpdir(), `vibe_motion_${Date.now()}`);
|
|
37551
37698
|
await mkdir12(dir, { recursive: true });
|
|
37552
37699
|
const deps = {
|
|
37553
37700
|
remotion: REMOTION_VERSION,
|
|
@@ -37566,7 +37713,7 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
|
37566
37713
|
dependencies: deps
|
|
37567
37714
|
};
|
|
37568
37715
|
const packageJsonStr = JSON.stringify(packageJson, null, 2);
|
|
37569
|
-
await writeFile21(
|
|
37716
|
+
await writeFile21(join19(dir, "package.json"), packageJsonStr);
|
|
37570
37717
|
const tsconfig = {
|
|
37571
37718
|
compilerOptions: {
|
|
37572
37719
|
target: "ES2020",
|
|
@@ -37578,8 +37725,8 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
|
37578
37725
|
skipLibCheck: true
|
|
37579
37726
|
}
|
|
37580
37727
|
};
|
|
37581
|
-
await writeFile21(
|
|
37582
|
-
await writeFile21(
|
|
37728
|
+
await writeFile21(join19(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
|
|
37729
|
+
await writeFile21(join19(dir, "Component.tsx"), componentCode);
|
|
37583
37730
|
const rootCode = `import { registerRoot, Composition } from "remotion";
|
|
37584
37731
|
import { ${componentName} } from "./Component";
|
|
37585
37732
|
|
|
@@ -37598,14 +37745,14 @@ const Root = () => {
|
|
|
37598
37745
|
|
|
37599
37746
|
registerRoot(Root);
|
|
37600
37747
|
`;
|
|
37601
|
-
await writeFile21(
|
|
37748
|
+
await writeFile21(join19(dir, "Root.tsx"), rootCode);
|
|
37602
37749
|
const depsHash = createHash6("md5").update(packageJsonStr).digest("hex").slice(0, 12);
|
|
37603
|
-
const cacheMarker =
|
|
37604
|
-
const cachedModules =
|
|
37605
|
-
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)) {
|
|
37606
37753
|
const { symlink } = await import("node:fs/promises");
|
|
37607
37754
|
try {
|
|
37608
|
-
await symlink(cachedModules,
|
|
37755
|
+
await symlink(cachedModules, join19(dir, "node_modules"), "dir");
|
|
37609
37756
|
} catch {
|
|
37610
37757
|
await this_npmInstall(dir);
|
|
37611
37758
|
}
|
|
@@ -37613,12 +37760,12 @@ registerRoot(Root);
|
|
|
37613
37760
|
await this_npmInstall(dir);
|
|
37614
37761
|
try {
|
|
37615
37762
|
await mkdir12(REMOTION_CACHE_DIR, { recursive: true });
|
|
37616
|
-
await writeFile21(
|
|
37617
|
-
if (
|
|
37763
|
+
await writeFile21(join19(REMOTION_CACHE_DIR, "package.json"), packageJsonStr);
|
|
37764
|
+
if (existsSync24(join19(dir, "node_modules"))) {
|
|
37618
37765
|
const { rename: rename5, symlink } = await import("node:fs/promises");
|
|
37619
|
-
await rename5(
|
|
37766
|
+
await rename5(join19(dir, "node_modules"), cachedModules).catch(() => {
|
|
37620
37767
|
});
|
|
37621
|
-
await symlink(cachedModules,
|
|
37768
|
+
await symlink(cachedModules, join19(dir, "node_modules"), "dir").catch(() => {
|
|
37622
37769
|
});
|
|
37623
37770
|
await writeFile21(cacheMarker, depsHash);
|
|
37624
37771
|
}
|
|
@@ -37655,7 +37802,7 @@ async function renderMotion(options) {
|
|
|
37655
37802
|
}
|
|
37656
37803
|
);
|
|
37657
37804
|
try {
|
|
37658
|
-
const entryPoint =
|
|
37805
|
+
const entryPoint = join19(dir, "Root.tsx");
|
|
37659
37806
|
const { execFile: execFile3 } = await import("node:child_process");
|
|
37660
37807
|
const { promisify: promisify3 } = await import("node:util");
|
|
37661
37808
|
const execFileAsync3 = promisify3(execFile3);
|
|
@@ -37788,10 +37935,10 @@ async function renderWithEmbeddedImage(options) {
|
|
|
37788
37935
|
}
|
|
37789
37936
|
);
|
|
37790
37937
|
try {
|
|
37791
|
-
const publicDir =
|
|
37938
|
+
const publicDir = join19(dir, "public");
|
|
37792
37939
|
await mkdir12(publicDir, { recursive: true });
|
|
37793
|
-
await copyFile4(options.imagePath,
|
|
37794
|
-
const entryPoint =
|
|
37940
|
+
await copyFile4(options.imagePath, join19(publicDir, options.imageFileName));
|
|
37941
|
+
const entryPoint = join19(dir, "Root.tsx");
|
|
37795
37942
|
const mp4Out = options.outputPath.replace(/\.\w+$/, ".mp4");
|
|
37796
37943
|
const { execFile: execFileImg } = await import("node:child_process");
|
|
37797
37944
|
const { promisify: promisifyImg } = await import("node:util");
|
|
@@ -37852,10 +37999,10 @@ async function renderWithEmbeddedVideo(options) {
|
|
|
37852
37999
|
}
|
|
37853
38000
|
);
|
|
37854
38001
|
try {
|
|
37855
|
-
const publicDir =
|
|
38002
|
+
const publicDir = join19(dir, "public");
|
|
37856
38003
|
await mkdir12(publicDir, { recursive: true });
|
|
37857
|
-
await copyFile4(options.videoPath,
|
|
37858
|
-
const entryPoint =
|
|
38004
|
+
await copyFile4(options.videoPath, join19(publicDir, options.videoFileName));
|
|
38005
|
+
const entryPoint = join19(dir, "Root.tsx");
|
|
37859
38006
|
const mp4VideoOnly = options.outputPath.replace(/\.\w+$/, "_video_only.mp4");
|
|
37860
38007
|
const { execFile: execFileVid } = await import("node:child_process");
|
|
37861
38008
|
const { promisify: promisifyVid } = await import("node:util");
|
|
@@ -38271,14 +38418,14 @@ var init_remotion = __esm({
|
|
|
38271
38418
|
"use strict";
|
|
38272
38419
|
init_exec_safe();
|
|
38273
38420
|
REMOTION_VERSION = "4.0.447";
|
|
38274
|
-
REMOTION_CACHE_DIR =
|
|
38421
|
+
REMOTION_CACHE_DIR = join19(tmpdir(), "vibe_remotion_cache");
|
|
38275
38422
|
}
|
|
38276
38423
|
});
|
|
38277
38424
|
|
|
38278
38425
|
// src/commands/_shared/edit/caption.ts
|
|
38279
|
-
import { existsSync as
|
|
38280
|
-
import { readFile as
|
|
38281
|
-
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";
|
|
38282
38429
|
function getCaptionForceStyle(style, fontSize, fontColor, position) {
|
|
38283
38430
|
const alignment = position === "top" ? 8 : position === "center" ? 5 : 2;
|
|
38284
38431
|
const marginV = position === "center" ? 0 : 30;
|
|
@@ -38306,7 +38453,7 @@ async function executeCaption(options) {
|
|
|
38306
38453
|
position = "bottom",
|
|
38307
38454
|
apiKey
|
|
38308
38455
|
} = options;
|
|
38309
|
-
if (!
|
|
38456
|
+
if (!existsSync25(videoPath)) {
|
|
38310
38457
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
38311
38458
|
}
|
|
38312
38459
|
if (!commandExists("ffmpeg")) {
|
|
@@ -38325,8 +38472,8 @@ async function executeCaption(options) {
|
|
|
38325
38472
|
try {
|
|
38326
38473
|
const tmpDir = `/tmp/vibe_caption_${Date.now()}`;
|
|
38327
38474
|
await mkdir13(tmpDir, { recursive: true });
|
|
38328
|
-
const audioPath =
|
|
38329
|
-
const srtPath =
|
|
38475
|
+
const audioPath = join20(tmpDir, "audio.wav");
|
|
38476
|
+
const srtPath = join20(tmpDir, "captions.srt");
|
|
38330
38477
|
try {
|
|
38331
38478
|
await execSafe(
|
|
38332
38479
|
"ffmpeg",
|
|
@@ -38347,7 +38494,7 @@ async function executeCaption(options) {
|
|
|
38347
38494
|
);
|
|
38348
38495
|
const whisper = new WhisperProvider();
|
|
38349
38496
|
await whisper.initialize({ apiKey: openaiKey });
|
|
38350
|
-
const audioBuffer = await
|
|
38497
|
+
const audioBuffer = await readFile23(audioPath);
|
|
38351
38498
|
const audioBlob = new Blob([audioBuffer]);
|
|
38352
38499
|
const transcriptResult = await whisper.transcribe(audioBlob, language);
|
|
38353
38500
|
if (transcriptResult.status === "failed" || !transcriptResult.segments || transcriptResult.segments.length === 0) {
|
|
@@ -38395,7 +38542,7 @@ async function executeCaption(options) {
|
|
|
38395
38542
|
const remotionErr = await ensureRemotionInstalled2();
|
|
38396
38543
|
if (remotionErr) {
|
|
38397
38544
|
const outputDir2 = dirname12(outputPath);
|
|
38398
|
-
const outputSrtPath2 =
|
|
38545
|
+
const outputSrtPath2 = join20(
|
|
38399
38546
|
outputDir2,
|
|
38400
38547
|
basename6(outputPath, extname5(outputPath)) + ".srt"
|
|
38401
38548
|
);
|
|
@@ -38434,7 +38581,7 @@ SRT saved to: ${outputSrtPath2}` };
|
|
|
38434
38581
|
});
|
|
38435
38582
|
if (!renderResult.success) {
|
|
38436
38583
|
const outputDir2 = dirname12(outputPath);
|
|
38437
|
-
const outputSrtPath2 =
|
|
38584
|
+
const outputSrtPath2 = join20(
|
|
38438
38585
|
outputDir2,
|
|
38439
38586
|
basename6(outputPath, extname5(outputPath)) + ".srt"
|
|
38440
38587
|
);
|
|
@@ -38447,7 +38594,7 @@ SRT saved to: ${outputSrtPath2}`
|
|
|
38447
38594
|
}
|
|
38448
38595
|
}
|
|
38449
38596
|
const outputDir = dirname12(outputPath);
|
|
38450
|
-
const outputSrtPath =
|
|
38597
|
+
const outputSrtPath = join20(
|
|
38451
38598
|
outputDir,
|
|
38452
38599
|
basename6(outputPath, extname5(outputPath)) + ".srt"
|
|
38453
38600
|
);
|
|
@@ -38484,10 +38631,10 @@ var init_caption = __esm({
|
|
|
38484
38631
|
});
|
|
38485
38632
|
|
|
38486
38633
|
// src/commands/_shared/edit/noise-reduce.ts
|
|
38487
|
-
import { existsSync as
|
|
38634
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
38488
38635
|
async function executeNoiseReduce(options) {
|
|
38489
38636
|
const { inputPath, outputPath, strength = "medium", noiseFloor } = options;
|
|
38490
|
-
if (!
|
|
38637
|
+
if (!existsSync26(inputPath)) {
|
|
38491
38638
|
return { success: false, error: `File not found: ${inputPath}` };
|
|
38492
38639
|
}
|
|
38493
38640
|
if (!commandExists("ffmpeg")) {
|
|
@@ -38544,7 +38691,7 @@ var init_noise_reduce = __esm({
|
|
|
38544
38691
|
});
|
|
38545
38692
|
|
|
38546
38693
|
// src/commands/_shared/edit/fade.ts
|
|
38547
|
-
import { existsSync as
|
|
38694
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
38548
38695
|
async function executeFade(options) {
|
|
38549
38696
|
const {
|
|
38550
38697
|
videoPath,
|
|
@@ -38554,7 +38701,7 @@ async function executeFade(options) {
|
|
|
38554
38701
|
audioOnly = false,
|
|
38555
38702
|
videoOnly = false
|
|
38556
38703
|
} = options;
|
|
38557
|
-
if (!
|
|
38704
|
+
if (!existsSync27(videoPath)) {
|
|
38558
38705
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
38559
38706
|
}
|
|
38560
38707
|
if (!commandExists("ffmpeg")) {
|
|
@@ -38621,8 +38768,8 @@ var init_fade = __esm({
|
|
|
38621
38768
|
});
|
|
38622
38769
|
|
|
38623
38770
|
// src/commands/_shared/edit/translate-srt.ts
|
|
38624
|
-
import { existsSync as
|
|
38625
|
-
import { readFile as
|
|
38771
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
38772
|
+
import { readFile as readFile24, writeFile as writeFile23 } from "node:fs/promises";
|
|
38626
38773
|
async function executeTranslateSrt(options) {
|
|
38627
38774
|
const {
|
|
38628
38775
|
srtPath,
|
|
@@ -38632,11 +38779,11 @@ async function executeTranslateSrt(options) {
|
|
|
38632
38779
|
sourceLanguage,
|
|
38633
38780
|
apiKey
|
|
38634
38781
|
} = options;
|
|
38635
|
-
if (!
|
|
38782
|
+
if (!existsSync28(srtPath)) {
|
|
38636
38783
|
return { success: false, error: `SRT file not found: ${srtPath}` };
|
|
38637
38784
|
}
|
|
38638
38785
|
try {
|
|
38639
|
-
const srtContent = await
|
|
38786
|
+
const srtContent = await readFile24(srtPath, "utf-8");
|
|
38640
38787
|
const segments = parseSRT(srtContent);
|
|
38641
38788
|
if (segments.length === 0) {
|
|
38642
38789
|
return { success: false, error: "No subtitle segments found in SRT file" };
|
|
@@ -38749,7 +38896,7 @@ var init_translate_srt = __esm({
|
|
|
38749
38896
|
});
|
|
38750
38897
|
|
|
38751
38898
|
// src/commands/_shared/edit/text-overlay.ts
|
|
38752
|
-
import { existsSync as
|
|
38899
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
38753
38900
|
import { resolve as resolve28 } from "node:path";
|
|
38754
38901
|
function detectSystemFont() {
|
|
38755
38902
|
const platform = process.platform;
|
|
@@ -38760,7 +38907,7 @@ function detectSystemFont() {
|
|
|
38760
38907
|
"/Library/Fonts/Arial.ttf"
|
|
38761
38908
|
];
|
|
38762
38909
|
for (const f of candidates) {
|
|
38763
|
-
if (
|
|
38910
|
+
if (existsSync29(f)) return f;
|
|
38764
38911
|
}
|
|
38765
38912
|
} else if (platform === "linux") {
|
|
38766
38913
|
const candidates = [
|
|
@@ -38769,7 +38916,7 @@ function detectSystemFont() {
|
|
|
38769
38916
|
"/usr/share/fonts/TTF/DejaVuSans-Bold.ttf"
|
|
38770
38917
|
];
|
|
38771
38918
|
for (const f of candidates) {
|
|
38772
|
-
if (
|
|
38919
|
+
if (existsSync29(f)) return f;
|
|
38773
38920
|
}
|
|
38774
38921
|
} else if (platform === "win32") {
|
|
38775
38922
|
const candidates = [
|
|
@@ -38777,7 +38924,7 @@ function detectSystemFont() {
|
|
|
38777
38924
|
"C:\\Windows\\Fonts\\segoeui.ttf"
|
|
38778
38925
|
];
|
|
38779
38926
|
for (const f of candidates) {
|
|
38780
|
-
if (
|
|
38927
|
+
if (existsSync29(f)) return f;
|
|
38781
38928
|
}
|
|
38782
38929
|
}
|
|
38783
38930
|
return null;
|
|
@@ -38798,7 +38945,7 @@ async function applyTextOverlays(options) {
|
|
|
38798
38945
|
}
|
|
38799
38946
|
const absVideoPath = resolve28(process.cwd(), videoPath);
|
|
38800
38947
|
const absOutputPath = resolve28(process.cwd(), outputPath);
|
|
38801
|
-
if (!
|
|
38948
|
+
if (!existsSync29(absVideoPath)) {
|
|
38802
38949
|
return { success: false, error: `Video not found: ${absVideoPath}` };
|
|
38803
38950
|
}
|
|
38804
38951
|
if (!commandExists("ffmpeg")) {
|
|
@@ -38979,7 +39126,7 @@ var init_ai_edit = __esm({
|
|
|
38979
39126
|
|
|
38980
39127
|
// src/commands/ai-edit-cli.ts
|
|
38981
39128
|
import { resolve as resolve29, extname as extname6, basename as basename7 } from "node:path";
|
|
38982
|
-
import { existsSync as
|
|
39129
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
38983
39130
|
function registerEditCommands(aiCommand) {
|
|
38984
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", `
|
|
38985
39132
|
Examples:
|
|
@@ -38996,7 +39143,7 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
|
|
|
38996
39143
|
validateOutputPath(options.output);
|
|
38997
39144
|
}
|
|
38998
39145
|
const absVideoPath = resolve29(process.cwd(), videoPath);
|
|
38999
|
-
if (!
|
|
39146
|
+
if (!existsSync30(absVideoPath)) {
|
|
39000
39147
|
exitWithError(notFoundError(absVideoPath));
|
|
39001
39148
|
}
|
|
39002
39149
|
if (!commandExists("ffmpeg")) {
|
|
@@ -39104,7 +39251,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
39104
39251
|
validateOutputPath(options.output);
|
|
39105
39252
|
}
|
|
39106
39253
|
const absVideoPath = resolve29(process.cwd(), videoPath);
|
|
39107
|
-
if (!
|
|
39254
|
+
if (!existsSync30(absVideoPath)) {
|
|
39108
39255
|
exitWithError(notFoundError(absVideoPath));
|
|
39109
39256
|
}
|
|
39110
39257
|
if (!commandExists("ffmpeg")) {
|
|
@@ -39185,7 +39332,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
39185
39332
|
validateOutputPath(options.output);
|
|
39186
39333
|
}
|
|
39187
39334
|
const absInputPath = resolve29(process.cwd(), inputPath);
|
|
39188
|
-
if (!
|
|
39335
|
+
if (!existsSync30(absInputPath)) {
|
|
39189
39336
|
exitWithError(notFoundError(absInputPath));
|
|
39190
39337
|
}
|
|
39191
39338
|
if (!commandExists("ffmpeg")) {
|
|
@@ -39251,7 +39398,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
39251
39398
|
validateOutputPath(options.output);
|
|
39252
39399
|
}
|
|
39253
39400
|
const absVideoPath = resolve29(process.cwd(), videoPath);
|
|
39254
|
-
if (!
|
|
39401
|
+
if (!existsSync30(absVideoPath)) {
|
|
39255
39402
|
exitWithError(notFoundError(absVideoPath));
|
|
39256
39403
|
}
|
|
39257
39404
|
if (!commandExists("ffmpeg")) {
|
|
@@ -39326,7 +39473,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
39326
39473
|
exitWithError(usageError("Target language required. Use -t or --target"));
|
|
39327
39474
|
}
|
|
39328
39475
|
const absSrtPath = resolve29(process.cwd(), srtPath);
|
|
39329
|
-
if (!
|
|
39476
|
+
if (!existsSync30(absSrtPath)) {
|
|
39330
39477
|
exitWithError(notFoundError(absSrtPath));
|
|
39331
39478
|
}
|
|
39332
39479
|
if (options.dryRun) {
|
|
@@ -39402,7 +39549,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
39402
39549
|
}
|
|
39403
39550
|
if (options.fillers) rejectControlChars(options.fillers);
|
|
39404
39551
|
const absVideoPath = resolve29(process.cwd(), videoPath);
|
|
39405
|
-
if (!
|
|
39552
|
+
if (!existsSync30(absVideoPath)) {
|
|
39406
39553
|
exitWithError(notFoundError(absVideoPath));
|
|
39407
39554
|
}
|
|
39408
39555
|
if (!commandExists("ffmpeg")) {
|
|
@@ -39895,8 +40042,8 @@ var init_project_resolver = __esm({
|
|
|
39895
40042
|
});
|
|
39896
40043
|
|
|
39897
40044
|
// src/commands/_shared/execute-fill-gaps.ts
|
|
39898
|
-
import { existsSync as
|
|
39899
|
-
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";
|
|
39900
40047
|
import { resolve as resolve31, dirname as dirname13 } from "node:path";
|
|
39901
40048
|
function detectVideoGaps(videoClips, totalDuration) {
|
|
39902
40049
|
const gaps = [];
|
|
@@ -39953,7 +40100,7 @@ function analyzeGapFillability(gaps, videoClips, sources) {
|
|
|
39953
40100
|
}
|
|
39954
40101
|
async function uploadFrameToImgbb(framePath, imgbbApiKey) {
|
|
39955
40102
|
try {
|
|
39956
|
-
const frameBuffer = await
|
|
40103
|
+
const frameBuffer = await readFile25(framePath);
|
|
39957
40104
|
const frameBase64 = frameBuffer.toString("base64");
|
|
39958
40105
|
const formData = new FormData();
|
|
39959
40106
|
formData.append("key", imgbbApiKey);
|
|
@@ -39978,14 +40125,14 @@ async function executeFillGaps(options) {
|
|
|
39978
40125
|
try {
|
|
39979
40126
|
onProgress("Loading timeline...");
|
|
39980
40127
|
const filePath = await resolveTimelineFile(options.projectPath);
|
|
39981
|
-
if (!
|
|
40128
|
+
if (!existsSync31(filePath)) {
|
|
39982
40129
|
return {
|
|
39983
40130
|
success: false,
|
|
39984
40131
|
error: `Timeline file not found: ${filePath}`,
|
|
39985
40132
|
humanLines
|
|
39986
40133
|
};
|
|
39987
40134
|
}
|
|
39988
|
-
const content = await
|
|
40135
|
+
const content = await readFile25(filePath, "utf-8");
|
|
39989
40136
|
const data = JSON.parse(content);
|
|
39990
40137
|
const project = Project.fromJSON(data);
|
|
39991
40138
|
const clips = project.getClips().sort((a, b) => a.startTime - b.startTime);
|
|
@@ -40113,7 +40260,7 @@ async function executeFillGaps(options) {
|
|
|
40113
40260
|
}
|
|
40114
40261
|
const projectDir = dirname13(filePath);
|
|
40115
40262
|
const footageDir = options.dir ? resolve31(process.cwd(), options.dir) : resolve31(projectDir, "footage");
|
|
40116
|
-
if (!
|
|
40263
|
+
if (!existsSync31(footageDir)) {
|
|
40117
40264
|
await mkdir14(footageDir, { recursive: true });
|
|
40118
40265
|
}
|
|
40119
40266
|
humanLines.push("Generating AI Videos");
|
|
@@ -40433,8 +40580,8 @@ __export(ai_motion_exports, {
|
|
|
40433
40580
|
registerMotionCommand: () => registerMotionCommand
|
|
40434
40581
|
});
|
|
40435
40582
|
import { resolve as resolve32 } from "node:path";
|
|
40436
|
-
import { existsSync as
|
|
40437
|
-
import { readFile as
|
|
40583
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
40584
|
+
import { readFile as readFile26, writeFile as writeFile25 } from "node:fs/promises";
|
|
40438
40585
|
async function getOptionalGoogleApiKey() {
|
|
40439
40586
|
const configKey = await getApiKeyFromConfig("google");
|
|
40440
40587
|
if (configKey) return configKey;
|
|
@@ -40481,7 +40628,7 @@ async function executeMotion(options) {
|
|
|
40481
40628
|
};
|
|
40482
40629
|
}
|
|
40483
40630
|
const imagePath = resolve32(process.cwd(), options.image);
|
|
40484
|
-
const imageBuffer = await
|
|
40631
|
+
const imageBuffer = await readFile26(imagePath);
|
|
40485
40632
|
const gemini = new GeminiProvider();
|
|
40486
40633
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
40487
40634
|
const analysisResult = await gemini.analyzeImage(
|
|
@@ -40518,7 +40665,7 @@ Use this image analysis to inform the color palette, typography placement, and o
|
|
|
40518
40665
|
}
|
|
40519
40666
|
if (geminiApiKey) {
|
|
40520
40667
|
const videoPath = resolve32(process.cwd(), options.video);
|
|
40521
|
-
const videoBuffer = await
|
|
40668
|
+
const videoBuffer = await readFile26(videoPath);
|
|
40522
40669
|
const gemini = new GeminiProvider();
|
|
40523
40670
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
40524
40671
|
const analysisPrompt = options.understandingPrompt || `Analyze this video for motion graphics composition. Return concise production guidance:
|
|
@@ -40549,10 +40696,10 @@ Use this video understanding to place motion graphics in safe zones, match the c
|
|
|
40549
40696
|
let result;
|
|
40550
40697
|
if (options.fromTsx) {
|
|
40551
40698
|
const tsxPath = resolve32(process.cwd(), options.fromTsx);
|
|
40552
|
-
if (!
|
|
40699
|
+
if (!existsSync32(tsxPath)) {
|
|
40553
40700
|
return { success: false, error: `TSX file not found: ${tsxPath}` };
|
|
40554
40701
|
}
|
|
40555
|
-
const existingCode = await
|
|
40702
|
+
const existingCode = await readFile26(tsxPath, "utf-8");
|
|
40556
40703
|
if (useGemini) {
|
|
40557
40704
|
const gemini = new GeminiProvider();
|
|
40558
40705
|
await gemini.initialize({ apiKey });
|
|
@@ -41081,7 +41228,7 @@ __export(project_builder_exports, {
|
|
|
41081
41228
|
resolveSourceUrl: () => resolveSourceUrl
|
|
41082
41229
|
});
|
|
41083
41230
|
import { mkdtemp, mkdir as mkdir15, copyFile as copyFile5, writeFile as writeFile26, rm as rm3, readdir as readdir3 } from "node:fs/promises";
|
|
41084
|
-
import { existsSync as
|
|
41231
|
+
import { existsSync as existsSync33 } from "node:fs";
|
|
41085
41232
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
41086
41233
|
import { createRequire } from "node:module";
|
|
41087
41234
|
import * as path from "node:path";
|
|
@@ -41093,7 +41240,7 @@ async function buildTempProject(state, projectFileDir) {
|
|
|
41093
41240
|
for (const source of state.sources) {
|
|
41094
41241
|
if (copied.has(source.id)) continue;
|
|
41095
41242
|
const resolved = resolveSourceUrl(source.url, projectFileDir);
|
|
41096
|
-
if (!
|
|
41243
|
+
if (!existsSync33(resolved)) {
|
|
41097
41244
|
throw new Error(`Source file not found: ${resolved} (from source id=${source.id})`);
|
|
41098
41245
|
}
|
|
41099
41246
|
const dest = path.join(assetsDir, path.basename(resolved));
|
|
@@ -41230,7 +41377,7 @@ __export(motion_overlay_exports, {
|
|
|
41230
41377
|
registerMotionOverlayCommand: () => registerMotionOverlayCommand
|
|
41231
41378
|
});
|
|
41232
41379
|
import { mkdir as mkdir16 } from "node:fs/promises";
|
|
41233
|
-
import { existsSync as
|
|
41380
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
41234
41381
|
import { basename as basename9, dirname as dirname15, extname as extname7, resolve as resolve34 } from "node:path";
|
|
41235
41382
|
function normalizeUnderstand2(value) {
|
|
41236
41383
|
return value === "off" || value === "required" || value === "auto" ? value : "auto";
|
|
@@ -41361,11 +41508,11 @@ async function executeMotionOverlay(options) {
|
|
|
41361
41508
|
const validationError = assertValidRequest(options);
|
|
41362
41509
|
if (validationError) return { success: false, error: validationError };
|
|
41363
41510
|
const absVideo = resolve34(process.cwd(), options.videoPath);
|
|
41364
|
-
if (!
|
|
41511
|
+
if (!existsSync34(absVideo)) return { success: false, error: `Video file not found: ${absVideo}` };
|
|
41365
41512
|
const outputPath = options.output ? resolve34(process.cwd(), options.output) : defaultOverlayOutput(options.videoPath);
|
|
41366
41513
|
if (options.asset) {
|
|
41367
41514
|
const absAsset = resolve34(process.cwd(), options.asset);
|
|
41368
|
-
if (!
|
|
41515
|
+
if (!existsSync34(absAsset)) return { success: false, error: `Lottie asset not found: ${absAsset}` };
|
|
41369
41516
|
const [videoDuration, size] = await Promise.all([
|
|
41370
41517
|
ffprobeDuration(absVideo),
|
|
41371
41518
|
ffprobeVideoSize(absVideo)
|
|
@@ -41532,7 +41679,7 @@ __export(edit_cmd_exports, {
|
|
|
41532
41679
|
executeUpscale: () => executeUpscale
|
|
41533
41680
|
});
|
|
41534
41681
|
import { resolve as resolve35, dirname as dirname16 } from "node:path";
|
|
41535
|
-
import { readFile as
|
|
41682
|
+
import { readFile as readFile27, writeFile as writeFile27, mkdir as mkdir17 } from "node:fs/promises";
|
|
41536
41683
|
async function executeGrade(options) {
|
|
41537
41684
|
const { videoPath, style, preset, output, analyzeOnly, apiKey } = options;
|
|
41538
41685
|
try {
|
|
@@ -41573,7 +41720,7 @@ async function executeSpeedRamp(options) {
|
|
|
41573
41720
|
await execSafe("ffmpeg", ["-i", absPath, "-vn", "-acodec", "libmp3lame", "-q:a", "2", tempAudio, "-y"]);
|
|
41574
41721
|
const whisper = new WhisperProvider();
|
|
41575
41722
|
await whisper.initialize({ apiKey: openaiKey });
|
|
41576
|
-
const audioBuffer = await
|
|
41723
|
+
const audioBuffer = await readFile27(tempAudio);
|
|
41577
41724
|
const transcript = await whisper.transcribe(new Blob([audioBuffer]), language);
|
|
41578
41725
|
try {
|
|
41579
41726
|
const { unlink: unlink5 } = await import("node:fs/promises");
|
|
@@ -41967,7 +42114,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
41967
42114
|
spinner2.text = "Transcribing audio...";
|
|
41968
42115
|
const whisper = new WhisperProvider();
|
|
41969
42116
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
41970
|
-
const audioBuffer = await
|
|
42117
|
+
const audioBuffer = await readFile27(tempAudio);
|
|
41971
42118
|
const audioBlob = new Blob([audioBuffer]);
|
|
41972
42119
|
const transcript = await whisper.transcribe(audioBlob, options.language);
|
|
41973
42120
|
if (!transcript.segments || transcript.segments.length === 0) {
|
|
@@ -42100,7 +42247,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
42100
42247
|
for (let i = 1; i <= numKeyframes && i <= 30; i++) {
|
|
42101
42248
|
const framePath = `${tempDir}/frame-${i.toString().padStart(4, "0")}.jpg`;
|
|
42102
42249
|
try {
|
|
42103
|
-
const frameBuffer = await
|
|
42250
|
+
const frameBuffer = await readFile27(framePath);
|
|
42104
42251
|
const frameBase64 = frameBuffer.toString("base64");
|
|
42105
42252
|
const result = await claude.analyzeFrameForReframe(frameBase64, options.aspect, {
|
|
42106
42253
|
focusMode: options.focus,
|
|
@@ -42229,7 +42376,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
42229
42376
|
const imageBuffers = [];
|
|
42230
42377
|
for (const imagePath of imagePaths) {
|
|
42231
42378
|
const absPath = resolve35(process.cwd(), imagePath);
|
|
42232
|
-
const buffer = await
|
|
42379
|
+
const buffer = await readFile27(absPath);
|
|
42233
42380
|
imageBuffers.push(buffer);
|
|
42234
42381
|
}
|
|
42235
42382
|
let result;
|
|
@@ -42506,8 +42653,8 @@ __export(ai_image_exports, {
|
|
|
42506
42653
|
executeThumbnailBestFrame: () => executeThumbnailBestFrame
|
|
42507
42654
|
});
|
|
42508
42655
|
import { resolve as resolve37, dirname as dirname18 } from "node:path";
|
|
42509
|
-
import { readFile as
|
|
42510
|
-
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";
|
|
42511
42658
|
async function executeImageGenerate(options) {
|
|
42512
42659
|
const {
|
|
42513
42660
|
prompt: prompt3,
|
|
@@ -42651,10 +42798,10 @@ async function executeGeminiEdit(options) {
|
|
|
42651
42798
|
const imageBuffers = [];
|
|
42652
42799
|
for (const imagePath of imagePaths) {
|
|
42653
42800
|
const absPath = resolve37(process.cwd(), imagePath);
|
|
42654
|
-
if (!
|
|
42801
|
+
if (!existsSync36(absPath)) {
|
|
42655
42802
|
return { success: false, error: `Image not found: ${absPath}` };
|
|
42656
42803
|
}
|
|
42657
|
-
const buffer = await
|
|
42804
|
+
const buffer = await readFile28(absPath);
|
|
42658
42805
|
imageBuffers.push(buffer);
|
|
42659
42806
|
}
|
|
42660
42807
|
const gemini = new GeminiProvider();
|
|
@@ -42699,7 +42846,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
42699
42846
|
model = "flash",
|
|
42700
42847
|
apiKey
|
|
42701
42848
|
} = options;
|
|
42702
|
-
if (!
|
|
42849
|
+
if (!existsSync36(videoPath)) {
|
|
42703
42850
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
42704
42851
|
}
|
|
42705
42852
|
if (!commandExists("ffmpeg")) {
|
|
@@ -42712,7 +42859,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
42712
42859
|
try {
|
|
42713
42860
|
const gemini = new GeminiProvider();
|
|
42714
42861
|
await gemini.initialize({ apiKey: googleKey });
|
|
42715
|
-
const videoData = await
|
|
42862
|
+
const videoData = await readFile28(videoPath);
|
|
42716
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>"}';
|
|
42717
42864
|
const modelId = resolveGeminiTextModel(model);
|
|
42718
42865
|
const result = await gemini.analyzeVideo(videoData, analysisPrompt, {
|
|
@@ -42733,7 +42880,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
42733
42880
|
reason = reasonMatch[1];
|
|
42734
42881
|
}
|
|
42735
42882
|
await execSafe("ffmpeg", ["-ss", String(timestamp), "-i", videoPath, "-frames:v", "1", "-q:v", "2", outputPath, "-y"], { timeout: 6e4, maxBuffer: 50 * 1024 * 1024 });
|
|
42736
|
-
if (!
|
|
42883
|
+
if (!existsSync36(outputPath)) {
|
|
42737
42884
|
return { success: false, error: "FFmpeg failed to extract frame" };
|
|
42738
42885
|
}
|
|
42739
42886
|
return {
|
|
@@ -42764,9 +42911,9 @@ __export(ai_analyze_exports, {
|
|
|
42764
42911
|
executeGeminiVideo: () => executeGeminiVideo,
|
|
42765
42912
|
registerAnalyzeCommands: () => registerAnalyzeCommands
|
|
42766
42913
|
});
|
|
42767
|
-
import { readFile as
|
|
42914
|
+
import { readFile as readFile29 } from "node:fs/promises";
|
|
42768
42915
|
import { extname as extname8, resolve as resolve38 } from "node:path";
|
|
42769
|
-
import { existsSync as
|
|
42916
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
42770
42917
|
async function executeGeminiVideo(options) {
|
|
42771
42918
|
try {
|
|
42772
42919
|
const apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
|
|
@@ -42780,10 +42927,10 @@ async function executeGeminiVideo(options) {
|
|
|
42780
42927
|
videoData = options.source;
|
|
42781
42928
|
} else {
|
|
42782
42929
|
const absPath = resolve38(process.cwd(), options.source);
|
|
42783
|
-
if (!
|
|
42930
|
+
if (!existsSync37(absPath)) {
|
|
42784
42931
|
return { success: false, error: `File not found: ${absPath}` };
|
|
42785
42932
|
}
|
|
42786
|
-
videoData = await
|
|
42933
|
+
videoData = await readFile29(absPath);
|
|
42787
42934
|
}
|
|
42788
42935
|
const gemini = new GeminiProvider();
|
|
42789
42936
|
await gemini.initialize({ apiKey });
|
|
@@ -42848,10 +42995,10 @@ async function executeAnalyze(options) {
|
|
|
42848
42995
|
imageBuffer = Buffer.from(await response.arrayBuffer());
|
|
42849
42996
|
} else {
|
|
42850
42997
|
const absPath = resolve38(process.cwd(), source);
|
|
42851
|
-
if (!
|
|
42998
|
+
if (!existsSync37(absPath)) {
|
|
42852
42999
|
return { success: false, error: `File not found: ${absPath}` };
|
|
42853
43000
|
}
|
|
42854
|
-
imageBuffer = await
|
|
43001
|
+
imageBuffer = await readFile29(absPath);
|
|
42855
43002
|
}
|
|
42856
43003
|
const result2 = await gemini.analyzeImage(imageBuffer, options.prompt, {
|
|
42857
43004
|
model: modelId,
|
|
@@ -42883,10 +43030,10 @@ async function executeAnalyze(options) {
|
|
|
42883
43030
|
videoData = Buffer.from(await response.arrayBuffer());
|
|
42884
43031
|
} else {
|
|
42885
43032
|
const absPath = resolve38(process.cwd(), source);
|
|
42886
|
-
if (!
|
|
43033
|
+
if (!existsSync37(absPath)) {
|
|
42887
43034
|
return { success: false, error: `File not found: ${absPath}` };
|
|
42888
43035
|
}
|
|
42889
|
-
videoData = await
|
|
43036
|
+
videoData = await readFile29(absPath);
|
|
42890
43037
|
}
|
|
42891
43038
|
const result = await gemini.analyzeVideo(videoData, options.prompt, {
|
|
42892
43039
|
model: modelId,
|
|
@@ -43020,9 +43167,9 @@ __export(ai_review_exports, {
|
|
|
43020
43167
|
executeReview: () => executeReview,
|
|
43021
43168
|
registerReviewCommand: () => registerReviewCommand
|
|
43022
43169
|
});
|
|
43023
|
-
import { readFile as
|
|
43170
|
+
import { readFile as readFile30, rename as rename3 } from "node:fs/promises";
|
|
43024
43171
|
import { resolve as resolve39 } from "node:path";
|
|
43025
|
-
import { existsSync as
|
|
43172
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
43026
43173
|
function parseReviewFeedback(response) {
|
|
43027
43174
|
let cleaned = response.trim();
|
|
43028
43175
|
if (cleaned.startsWith("```json")) {
|
|
@@ -43054,7 +43201,7 @@ async function executeReview(options) {
|
|
|
43054
43201
|
model = "flash"
|
|
43055
43202
|
} = options;
|
|
43056
43203
|
const absVideoPath = resolve39(process.cwd(), videoPath);
|
|
43057
|
-
if (!
|
|
43204
|
+
if (!existsSync38(absVideoPath)) {
|
|
43058
43205
|
return { success: false, error: `Video not found: ${absVideoPath}` };
|
|
43059
43206
|
}
|
|
43060
43207
|
const apiKey = process.env.GOOGLE_API_KEY || await getApiKey("GOOGLE_API_KEY", "Google");
|
|
@@ -43067,8 +43214,8 @@ async function executeReview(options) {
|
|
|
43067
43214
|
let storyboardContext = "";
|
|
43068
43215
|
if (storyboardPath) {
|
|
43069
43216
|
const absStoryboardPath = resolve39(process.cwd(), storyboardPath);
|
|
43070
|
-
if (
|
|
43071
|
-
const content = await
|
|
43217
|
+
if (existsSync38(absStoryboardPath)) {
|
|
43218
|
+
const content = await readFile30(absStoryboardPath, "utf-8");
|
|
43072
43219
|
storyboardContext = `
|
|
43073
43220
|
|
|
43074
43221
|
Original storyboard for reference:
|
|
@@ -43110,7 +43257,7 @@ ${projectContext}` : "";
|
|
|
43110
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}`;
|
|
43111
43258
|
const gemini = new GeminiProvider();
|
|
43112
43259
|
await gemini.initialize({ apiKey });
|
|
43113
|
-
const videoData = await
|
|
43260
|
+
const videoData = await readFile30(absVideoPath);
|
|
43114
43261
|
const analysisResult = await gemini.analyzeVideo(videoData, reviewPrompt, {
|
|
43115
43262
|
model: modelId
|
|
43116
43263
|
});
|
|
@@ -43165,7 +43312,7 @@ Score each category 1-10. Prefer beatIssues when you can map a problem to a stor
|
|
|
43165
43312
|
}
|
|
43166
43313
|
}
|
|
43167
43314
|
if (verify && result.outputPath) {
|
|
43168
|
-
const verifyVideoData = await
|
|
43315
|
+
const verifyVideoData = await readFile30(result.outputPath);
|
|
43169
43316
|
const verifyResult = await gemini.analyzeVideo(
|
|
43170
43317
|
verifyVideoData,
|
|
43171
43318
|
'Rate this video overall quality on a scale of 1-10. Return ONLY a JSON object: {"score": <number>}',
|
|
@@ -43629,7 +43776,7 @@ var init_sanitize = __esm({
|
|
|
43629
43776
|
|
|
43630
43777
|
// src/commands/generate/storyboard.ts
|
|
43631
43778
|
import { resolve as resolve46 } from "node:path";
|
|
43632
|
-
import { readFile as
|
|
43779
|
+
import { readFile as readFile34, writeFile as writeFile33 } from "node:fs/promises";
|
|
43633
43780
|
async function executeStoryboard(options) {
|
|
43634
43781
|
const { content, duration, creativity = "low", output, apiKey } = options;
|
|
43635
43782
|
try {
|
|
@@ -43677,7 +43824,7 @@ function registerStoryboardCommand(parent) {
|
|
|
43677
43824
|
let textContent = content;
|
|
43678
43825
|
if (options.file) {
|
|
43679
43826
|
const filePath = resolve46(process.cwd(), content);
|
|
43680
|
-
textContent = await
|
|
43827
|
+
textContent = await readFile34(filePath, "utf-8");
|
|
43681
43828
|
}
|
|
43682
43829
|
if (options.dryRun) {
|
|
43683
43830
|
outputSuccess({
|
|
@@ -44070,7 +44217,7 @@ var init_speech = __esm({
|
|
|
44070
44217
|
|
|
44071
44218
|
// src/commands/generate/thumbnail.ts
|
|
44072
44219
|
import { resolve as resolve48, dirname as dirname20, basename as basename13, extname as extname9 } from "node:path";
|
|
44073
|
-
import { existsSync as
|
|
44220
|
+
import { existsSync as existsSync41 } from "node:fs";
|
|
44074
44221
|
import { writeFile as writeFile35, mkdir as mkdir21 } from "node:fs/promises";
|
|
44075
44222
|
function registerThumbnailCommand(parent) {
|
|
44076
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) => {
|
|
@@ -44082,7 +44229,7 @@ function registerThumbnailCommand(parent) {
|
|
|
44082
44229
|
}
|
|
44083
44230
|
if (options.bestFrame) {
|
|
44084
44231
|
const absVideoPath = resolve48(process.cwd(), options.bestFrame);
|
|
44085
|
-
if (!
|
|
44232
|
+
if (!existsSync41(absVideoPath)) {
|
|
44086
44233
|
exitWithError(notFoundError(absVideoPath));
|
|
44087
44234
|
}
|
|
44088
44235
|
if (!commandExists("ffmpeg")) {
|
|
@@ -46266,7 +46413,7 @@ var init_dist3 = __esm({
|
|
|
46266
46413
|
|
|
46267
46414
|
// src/commands/generate/video.ts
|
|
46268
46415
|
import { resolve as resolve52 } from "node:path";
|
|
46269
|
-
import { readFile as
|
|
46416
|
+
import { readFile as readFile35, writeFile as writeFile39 } from "node:fs/promises";
|
|
46270
46417
|
function registerVideoCommand(parent) {
|
|
46271
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(
|
|
46272
46419
|
"-p, --provider <provider>",
|
|
@@ -46377,7 +46524,7 @@ Examples:
|
|
|
46377
46524
|
let isImageToVideo = false;
|
|
46378
46525
|
if (options.image) {
|
|
46379
46526
|
const imagePath = resolve52(process.cwd(), options.image);
|
|
46380
|
-
const imageBuffer = await
|
|
46527
|
+
const imageBuffer = await readFile35(imagePath);
|
|
46381
46528
|
const ext = options.image.toLowerCase().split(".").pop();
|
|
46382
46529
|
const mimeTypes = {
|
|
46383
46530
|
jpg: "image/jpeg",
|
|
@@ -46617,7 +46764,7 @@ Examples:
|
|
|
46617
46764
|
let lastFrame;
|
|
46618
46765
|
if (options.lastFrame) {
|
|
46619
46766
|
const lastFramePath = resolve52(process.cwd(), options.lastFrame);
|
|
46620
|
-
const lastFrameBuffer = await
|
|
46767
|
+
const lastFrameBuffer = await readFile35(lastFramePath);
|
|
46621
46768
|
const ext = options.lastFrame.toLowerCase().split(".").pop();
|
|
46622
46769
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
|
|
46623
46770
|
lastFrame = `data:${mimeType};base64,${lastFrameBuffer.toString("base64")}`;
|
|
@@ -46627,7 +46774,7 @@ Examples:
|
|
|
46627
46774
|
refImages = [];
|
|
46628
46775
|
for (const refPath of options.refImages.slice(0, 3)) {
|
|
46629
46776
|
const absRefPath = resolve52(process.cwd(), refPath);
|
|
46630
|
-
const refBuffer = await
|
|
46777
|
+
const refBuffer = await readFile35(absRefPath);
|
|
46631
46778
|
const ext = refPath.toLowerCase().split(".").pop();
|
|
46632
46779
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
|
|
46633
46780
|
refImages.push({ base64: refBuffer.toString("base64"), mimeType });
|
|
@@ -46759,7 +46906,7 @@ Examples:
|
|
|
46759
46906
|
if (seedanceEndImagePath && seedanceReferences.length === 0) {
|
|
46760
46907
|
try {
|
|
46761
46908
|
const absEndImagePath = resolve52(process.cwd(), seedanceEndImagePath);
|
|
46762
|
-
const endImageBuffer = await
|
|
46909
|
+
const endImageBuffer = await readFile35(absEndImagePath);
|
|
46763
46910
|
const ext = seedanceEndImagePath.toLowerCase().split(".").pop();
|
|
46764
46911
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
|
|
46765
46912
|
const uploadHost = await resolveUploadHost();
|
|
@@ -46876,7 +47023,7 @@ async function fileInputToUrlOrDataUri2(input, fallbackMimeType) {
|
|
|
46876
47023
|
return input;
|
|
46877
47024
|
}
|
|
46878
47025
|
const absPath = resolve52(process.cwd(), input);
|
|
46879
|
-
const buffer = await
|
|
47026
|
+
const buffer = await readFile35(absPath);
|
|
46880
47027
|
return `data:${mimeTypeForPath2(input, fallbackMimeType)};base64,${buffer.toString("base64")}`;
|
|
46881
47028
|
}
|
|
46882
47029
|
function mimeTypeForPath2(path2, fallback2) {
|
|
@@ -47884,7 +48031,7 @@ __export(detect_exports, {
|
|
|
47884
48031
|
executeDetectScenes: () => executeDetectScenes,
|
|
47885
48032
|
executeDetectSilence: () => executeDetectSilence
|
|
47886
48033
|
});
|
|
47887
|
-
import { readFile as
|
|
48034
|
+
import { readFile as readFile40, writeFile as writeFile44 } from "node:fs/promises";
|
|
47888
48035
|
import { resolve as resolve57, basename as basename15 } from "node:path";
|
|
47889
48036
|
async function executeDetectScenes(options) {
|
|
47890
48037
|
try {
|
|
@@ -48128,7 +48275,7 @@ var init_detect = __esm({
|
|
|
48128
48275
|
}
|
|
48129
48276
|
if (options.project) {
|
|
48130
48277
|
const projectPath = resolve57(process.cwd(), options.project);
|
|
48131
|
-
const content = await
|
|
48278
|
+
const content = await readFile40(projectPath, "utf-8");
|
|
48132
48279
|
const data = JSON.parse(content);
|
|
48133
48280
|
const project = Project.fromJSON(data);
|
|
48134
48281
|
let source = project.getSources().find((s) => s.url === absPath);
|
|
@@ -52979,8 +53126,8 @@ init_scene_project();
|
|
|
52979
53126
|
init_project_config();
|
|
52980
53127
|
init_cost_tier();
|
|
52981
53128
|
import { resolve as resolve17, relative as relative6, dirname as dirname6 } from "node:path";
|
|
52982
|
-
import { mkdir as mkdir8, readFile as
|
|
52983
|
-
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";
|
|
52984
53131
|
|
|
52985
53132
|
// src/commands/_shared/scene-html-emit.ts
|
|
52986
53133
|
var SCENE_PRESETS = [
|
|
@@ -53549,15 +53696,16 @@ function deriveInstallHosts(detected) {
|
|
|
53549
53696
|
|
|
53550
53697
|
// src/commands/scene.ts
|
|
53551
53698
|
init_compose_prompts();
|
|
53699
|
+
init_transcribe_narration();
|
|
53552
53700
|
|
|
53553
53701
|
// src/commands/_shared/scene-submit.ts
|
|
53554
53702
|
init_compose_scenes_skills();
|
|
53555
53703
|
init_scene_lint();
|
|
53556
53704
|
init_storyboard_parse();
|
|
53557
53705
|
init_root_sync();
|
|
53558
|
-
import { existsSync as
|
|
53559
|
-
import { mkdir as mkdir7, readFile as
|
|
53560
|
-
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";
|
|
53561
53709
|
async function executeSceneSubmit(opts) {
|
|
53562
53710
|
const projectDir = resolve14(opts.projectDir);
|
|
53563
53711
|
const beatId = opts.beatId.trim();
|
|
@@ -53571,15 +53719,15 @@ async function executeSceneSubmit(opts) {
|
|
|
53571
53719
|
warnings: [],
|
|
53572
53720
|
retryWith: []
|
|
53573
53721
|
};
|
|
53574
|
-
const storyboardPath =
|
|
53575
|
-
if (!
|
|
53722
|
+
const storyboardPath = join12(projectDir, "STORYBOARD.md");
|
|
53723
|
+
if (!existsSync12(storyboardPath)) {
|
|
53576
53724
|
return {
|
|
53577
53725
|
...base,
|
|
53578
53726
|
success: false,
|
|
53579
53727
|
error: `STORYBOARD.md not found at ${storyboardPath}. Run \`vibe init <dir>\` first.`
|
|
53580
53728
|
};
|
|
53581
53729
|
}
|
|
53582
|
-
const parsed = parseStoryboard(await
|
|
53730
|
+
const parsed = parseStoryboard(await readFile12(storyboardPath, "utf-8"));
|
|
53583
53731
|
const beat = parsed.beats.find((b) => b.id === beatId);
|
|
53584
53732
|
if (!beat) {
|
|
53585
53733
|
const available = parsed.beats.map((b) => b.id).join(", ") || "(none)";
|
|
@@ -53649,8 +53797,8 @@ async function executeSceneSubmit(opts) {
|
|
|
53649
53797
|
if (opts.validateOnly) {
|
|
53650
53798
|
return { ...base, success: true };
|
|
53651
53799
|
}
|
|
53652
|
-
const absPath =
|
|
53653
|
-
await mkdir7(
|
|
53800
|
+
const absPath = join12(projectDir, scenePath);
|
|
53801
|
+
await mkdir7(join12(projectDir, "compositions"), { recursive: true });
|
|
53654
53802
|
await writeFile13(absPath, html.endsWith("\n") ? html : `${html}
|
|
53655
53803
|
`, "utf-8");
|
|
53656
53804
|
return {
|
|
@@ -53851,7 +53999,7 @@ sceneCommand.command("submit").description(
|
|
|
53851
53999
|
}
|
|
53852
54000
|
html = Buffer.concat(chunks).toString("utf-8");
|
|
53853
54001
|
} else if (options.file) {
|
|
53854
|
-
html = await
|
|
54002
|
+
html = await readFile15(resolve17(String(options.file)), "utf-8");
|
|
53855
54003
|
} else {
|
|
53856
54004
|
exitWithError(
|
|
53857
54005
|
usageError(
|
|
@@ -54181,8 +54329,8 @@ function readClipStart(rootHtml, beatId) {
|
|
|
54181
54329
|
async function resolveNarrationText(value) {
|
|
54182
54330
|
if (!value) return void 0;
|
|
54183
54331
|
const looksLikePath = /\.[a-z]{2,4}$/i.test(value) || value.includes("/") || value.includes("\\");
|
|
54184
|
-
if (looksLikePath &&
|
|
54185
|
-
return (await
|
|
54332
|
+
if (looksLikePath && existsSync14(value)) {
|
|
54333
|
+
return (await readFile15(value, "utf-8")).trim();
|
|
54186
54334
|
}
|
|
54187
54335
|
return value.trim();
|
|
54188
54336
|
}
|
|
@@ -54221,7 +54369,7 @@ async function executeSceneAdd(opts) {
|
|
|
54221
54369
|
if (!opts.force && await pathExists2(scenePath)) {
|
|
54222
54370
|
return errResult(`Scene already exists: ${sceneRel}. Re-run with --force to overwrite.`);
|
|
54223
54371
|
}
|
|
54224
|
-
const rootHtmlBefore = await
|
|
54372
|
+
const rootHtmlBefore = await readFile15(rootPath, "utf-8");
|
|
54225
54373
|
let dims = readRootDims(rootHtmlBefore);
|
|
54226
54374
|
if (!dims) {
|
|
54227
54375
|
const cfg2 = await loadVibeProjectConfig(projectDir);
|
|
@@ -54297,31 +54445,20 @@ async function executeSceneAdd(opts) {
|
|
|
54297
54445
|
);
|
|
54298
54446
|
} else {
|
|
54299
54447
|
opts.onProgress?.("Transcribing narration (Whisper word-level)...");
|
|
54300
|
-
|
|
54301
|
-
|
|
54302
|
-
|
|
54303
|
-
|
|
54304
|
-
|
|
54305
|
-
|
|
54306
|
-
|
|
54307
|
-
|
|
54308
|
-
|
|
54309
|
-
|
|
54310
|
-
|
|
54311
|
-
|
|
54312
|
-
|
|
54313
|
-
|
|
54314
|
-
transcriptWords = transcript.words.map((w) => ({
|
|
54315
|
-
text: w.text,
|
|
54316
|
-
start: w.start,
|
|
54317
|
-
end: w.end
|
|
54318
|
-
}));
|
|
54319
|
-
} else if (transcript.status === "failed") {
|
|
54320
|
-
opts.onProgress?.(`Transcribe failed: ${transcript.error ?? "unknown error"}`);
|
|
54321
|
-
}
|
|
54322
|
-
} catch (error) {
|
|
54323
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
54324
|
-
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
|
+
);
|
|
54325
54462
|
}
|
|
54326
54463
|
}
|
|
54327
54464
|
}
|
|
@@ -54430,7 +54567,7 @@ async function executeSceneAdd(opts) {
|
|
|
54430
54567
|
const shouldSyncStoryboard = opts.syncStoryboard !== false && await pathExists2(storyboardAbsPath);
|
|
54431
54568
|
if (shouldSyncStoryboard) {
|
|
54432
54569
|
opts.onProgress?.("Syncing STORYBOARD.md...");
|
|
54433
|
-
const before = await
|
|
54570
|
+
const before = await readFile15(storyboardAbsPath, "utf-8");
|
|
54434
54571
|
const synced = upsertStoryboardBeat(before, {
|
|
54435
54572
|
beatId: id,
|
|
54436
54573
|
title: opts.headline,
|
|
@@ -54472,7 +54609,7 @@ async function executeSceneAdd(opts) {
|
|
|
54472
54609
|
});
|
|
54473
54610
|
const blockingIssue = syncPlan.issues.find((issue) => issue.severity === "error");
|
|
54474
54611
|
if (blockingIssue) return errResult(blockingIssue.message);
|
|
54475
|
-
const rootHtmlAfterSync = await
|
|
54612
|
+
const rootHtmlAfterSync = await readFile15(rootPath, "utf-8");
|
|
54476
54613
|
start = readClipStart(rootHtmlAfterSync, id) ?? 0;
|
|
54477
54614
|
} else {
|
|
54478
54615
|
start = nextSceneStart(rootHtmlBefore, SCENE_OVERLAP_SECONDS);
|
|
@@ -54646,7 +54783,7 @@ async function resolveSceneRepairTarget(rootArg = "index.html", projectArg = "."
|
|
|
54646
54783
|
const root = rootArg || "index.html";
|
|
54647
54784
|
const project = projectArg || ".";
|
|
54648
54785
|
const defaultProject = project === ".";
|
|
54649
|
-
if (defaultProject && root !== "index.html" && !root.toLowerCase().endsWith(".html") &&
|
|
54786
|
+
if (defaultProject && root !== "index.html" && !root.toLowerCase().endsWith(".html") && existsSync14(resolve17(root, "index.html"))) {
|
|
54650
54787
|
return { projectDir: resolve17(root), rootRel: "index.html" };
|
|
54651
54788
|
}
|
|
54652
54789
|
return { projectDir: resolve17(project), rootRel: root };
|
|
@@ -55515,7 +55652,7 @@ init_exec_safe();
|
|
|
55515
55652
|
init_remotion();
|
|
55516
55653
|
import { resolve as resolve36, dirname as dirname17, basename as basename10 } from "node:path";
|
|
55517
55654
|
import { writeFile as writeFile28, mkdir as mkdir18, rm as rm4 } from "node:fs/promises";
|
|
55518
|
-
import { existsSync as
|
|
55655
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
55519
55656
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
55520
55657
|
var ASS_STYLES = ["karaoke-sweep", "typewriter"];
|
|
55521
55658
|
var SENTENCE_BREAKS = /[.!?]/;
|
|
@@ -55696,7 +55833,7 @@ async function executeAnimatedCaption(options) {
|
|
|
55696
55833
|
const groups = groupWords(transcript.words, { wordsPerGroup, maxChars });
|
|
55697
55834
|
const absOutputPath = resolve36(process.cwd(), outputPath);
|
|
55698
55835
|
const outDir = dirname17(absOutputPath);
|
|
55699
|
-
if (!
|
|
55836
|
+
if (!existsSync35(outDir)) {
|
|
55700
55837
|
await mkdir18(outDir, { recursive: true });
|
|
55701
55838
|
}
|
|
55702
55839
|
if (tier === "ass") {
|
|
@@ -56243,14 +56380,14 @@ init_ai_review();
|
|
|
56243
56380
|
init_dist2();
|
|
56244
56381
|
init_engine();
|
|
56245
56382
|
init_ai_helpers();
|
|
56246
|
-
import { readFile as
|
|
56383
|
+
import { readFile as readFile31, writeFile as writeFile30 } from "node:fs/promises";
|
|
56247
56384
|
import { resolve as resolve40 } from "node:path";
|
|
56248
56385
|
async function executeSuggestEdit(options) {
|
|
56249
56386
|
try {
|
|
56250
56387
|
const apiKey = options.apiKey ?? process.env.GOOGLE_API_KEY;
|
|
56251
56388
|
if (!apiKey) return { success: false, error: "GOOGLE_API_KEY required for suggest" };
|
|
56252
56389
|
const filePath = resolve40(process.cwd(), options.projectPath);
|
|
56253
|
-
const content = await
|
|
56390
|
+
const content = await readFile31(filePath, "utf-8");
|
|
56254
56391
|
const data = JSON.parse(content);
|
|
56255
56392
|
const project = Project.fromJSON(data);
|
|
56256
56393
|
const gemini = new GeminiProvider();
|
|
@@ -56287,9 +56424,9 @@ init_project_config();
|
|
|
56287
56424
|
init_scene_lint();
|
|
56288
56425
|
init_root_sync();
|
|
56289
56426
|
init_review_report();
|
|
56290
|
-
import { existsSync as
|
|
56291
|
-
import { readdir as readdir4, readFile as
|
|
56292
|
-
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";
|
|
56293
56430
|
async function inspectProject(opts) {
|
|
56294
56431
|
const projectDir = resolve41(opts.projectDir);
|
|
56295
56432
|
const issues = [];
|
|
@@ -56303,7 +56440,7 @@ async function inspectProject(opts) {
|
|
|
56303
56440
|
assets: { checked: 0, missing: [], stale: [], unknownFreshness: [] },
|
|
56304
56441
|
rootSync: { ok: null, issueCount: 0, issues: [] }
|
|
56305
56442
|
};
|
|
56306
|
-
if (!
|
|
56443
|
+
if (!existsSync39(projectDir) || !await isDirectory(projectDir)) {
|
|
56307
56444
|
issues.push({
|
|
56308
56445
|
severity: "error",
|
|
56309
56446
|
code: "PROJECT_NOT_FOUND",
|
|
@@ -56318,15 +56455,15 @@ async function inspectProject(opts) {
|
|
|
56318
56455
|
);
|
|
56319
56456
|
}
|
|
56320
56457
|
const coreFiles = {
|
|
56321
|
-
storyboard:
|
|
56322
|
-
design:
|
|
56323
|
-
config:
|
|
56324
|
-
legacyConfig:
|
|
56325
|
-
buildReport:
|
|
56326
|
-
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")
|
|
56327
56464
|
};
|
|
56328
56465
|
for (const [name, path2] of Object.entries(coreFiles)) {
|
|
56329
|
-
checks.files[name] =
|
|
56466
|
+
checks.files[name] = existsSync39(path2);
|
|
56330
56467
|
}
|
|
56331
56468
|
let beatIds = [];
|
|
56332
56469
|
let inspectedBeatIds = [];
|
|
@@ -56340,7 +56477,7 @@ async function inspectProject(opts) {
|
|
|
56340
56477
|
});
|
|
56341
56478
|
retryWith.push(`vibe init ${projectDir} --from "<brief>" --json`);
|
|
56342
56479
|
} else {
|
|
56343
|
-
const storyboard = await
|
|
56480
|
+
const storyboard = await readFile32(coreFiles.storyboard, "utf-8");
|
|
56344
56481
|
const validation = validateStoryboardMarkdown(storyboard);
|
|
56345
56482
|
storyboardBeats = validation.beats;
|
|
56346
56483
|
beatIds = validation.beats.map((beat) => beat.id);
|
|
@@ -56382,7 +56519,7 @@ async function inspectProject(opts) {
|
|
|
56382
56519
|
suggestedFix: "Create DESIGN.md or rerun `vibe init --from` in a new project."
|
|
56383
56520
|
});
|
|
56384
56521
|
} else {
|
|
56385
|
-
const design = await
|
|
56522
|
+
const design = await readFile32(coreFiles.design, "utf-8");
|
|
56386
56523
|
for (const issue of designPlaceholderIssues(design)) {
|
|
56387
56524
|
issues.push(issue);
|
|
56388
56525
|
}
|
|
@@ -56407,20 +56544,20 @@ async function inspectProject(opts) {
|
|
|
56407
56544
|
});
|
|
56408
56545
|
retryWith.push(`vibe build ${projectDir} --stage sync --json`);
|
|
56409
56546
|
}
|
|
56410
|
-
const compositionsDir =
|
|
56411
|
-
const existingComps =
|
|
56547
|
+
const compositionsDir = join22(projectDir, "compositions");
|
|
56548
|
+
const existingComps = existsSync39(compositionsDir) ? new Set(await listHtmlBasenames(compositionsDir)) : /* @__PURE__ */ new Set();
|
|
56412
56549
|
checks.compositions.expected = inspectedBeatIds.length;
|
|
56413
56550
|
for (const beatId of inspectedBeatIds) {
|
|
56414
56551
|
const file = `scene-${beatId}.html`;
|
|
56415
56552
|
if (existingComps.has(file)) {
|
|
56416
56553
|
checks.compositions.found++;
|
|
56417
56554
|
} else {
|
|
56418
|
-
checks.compositions.missing.push(
|
|
56555
|
+
checks.compositions.missing.push(join22("compositions", file));
|
|
56419
56556
|
issues.push({
|
|
56420
56557
|
severity: "error",
|
|
56421
56558
|
code: "MISSING_COMPOSITION",
|
|
56422
56559
|
message: `Composition for beat "${beatId}" is missing.`,
|
|
56423
|
-
file:
|
|
56560
|
+
file: join22("compositions", file),
|
|
56424
56561
|
scene: beatId,
|
|
56425
56562
|
beatId,
|
|
56426
56563
|
suggestedFix: "Run `vibe build --stage compose --json`."
|
|
@@ -56482,7 +56619,7 @@ async function inspectProject(opts) {
|
|
|
56482
56619
|
try {
|
|
56483
56620
|
const lint = await runProjectLint({ projectDir });
|
|
56484
56621
|
const lintFiles = opts.beatId ? lint.files.filter(
|
|
56485
|
-
(file) => file.file === "index.html" || file.file ===
|
|
56622
|
+
(file) => file.file === "index.html" || file.file === join22("compositions", `scene-${opts.beatId}.html`)
|
|
56486
56623
|
) : lint.files;
|
|
56487
56624
|
const lintCounts = countLintFindings(lintFiles);
|
|
56488
56625
|
checks.lint = {
|
|
@@ -56628,7 +56765,7 @@ function projectSourceReports(checks) {
|
|
|
56628
56765
|
async function inspectBuildReport(projectDir, reportPath, beatId, checks, issues, retryWith, storyboardBeats) {
|
|
56629
56766
|
checks.buildReport.exists = true;
|
|
56630
56767
|
try {
|
|
56631
|
-
const report = JSON.parse(await
|
|
56768
|
+
const report = JSON.parse(await readFile32(reportPath, "utf-8"));
|
|
56632
56769
|
if (typeof report.outputPath === "string") checks.buildReport.outputPath = report.outputPath;
|
|
56633
56770
|
const reportBeats = report.beats ?? [];
|
|
56634
56771
|
const selectedReportBeats = reportBeats.filter((item) => !beatId || item.id === beatId);
|
|
@@ -56765,7 +56902,7 @@ function inspectReportedAsset(opts) {
|
|
|
56765
56902
|
opts.checks.assets.checked++;
|
|
56766
56903
|
if (isExternalRef2(opts.value)) return;
|
|
56767
56904
|
const abs = isAbsolute5(opts.value) ? opts.value : resolve41(opts.projectDir, opts.value);
|
|
56768
|
-
if (
|
|
56905
|
+
if (existsSync39(abs)) return;
|
|
56769
56906
|
opts.checks.assets.missing.push(opts.value);
|
|
56770
56907
|
opts.issues.push({
|
|
56771
56908
|
severity: "warning",
|
|
@@ -56859,9 +56996,9 @@ init_exec_safe();
|
|
|
56859
56996
|
init_project_config();
|
|
56860
56997
|
init_storyboard_parse();
|
|
56861
56998
|
init_review_report();
|
|
56862
|
-
import { existsSync as
|
|
56863
|
-
import { readdir as readdir5, readFile as
|
|
56864
|
-
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";
|
|
56865
57002
|
var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([".mp4", ".mov", ".webm", ".m4v"]);
|
|
56866
57003
|
var DEFAULT_AI_MODEL = "flash";
|
|
56867
57004
|
var BLACK_FRAME_MIN_DURATION_SEC = 1;
|
|
@@ -57362,11 +57499,11 @@ async function inspectRender(opts) {
|
|
|
57362
57499
|
}
|
|
57363
57500
|
async function renderExpectsAudio(projectDir, beatId) {
|
|
57364
57501
|
const root = resolve42(projectDir);
|
|
57365
|
-
const rootHtml = await readTextIfExists(
|
|
57502
|
+
const rootHtml = await readTextIfExists(join23(root, "index.html"));
|
|
57366
57503
|
if (rootHtml && rootHtmlExpectsAudio(rootHtml, beatId)) return true;
|
|
57367
|
-
const buildReport = await readJsonIfExists(
|
|
57504
|
+
const buildReport = await readJsonIfExists(join23(root, "build-report.json"));
|
|
57368
57505
|
if (buildReportExpectsAudio(buildReport, beatId)) return true;
|
|
57369
|
-
const storyboard = await readTextIfExists(
|
|
57506
|
+
const storyboard = await readTextIfExists(join23(root, "STORYBOARD.md"));
|
|
57370
57507
|
if (!storyboard) return false;
|
|
57371
57508
|
try {
|
|
57372
57509
|
const parsed = parseStoryboard(storyboard);
|
|
@@ -57377,7 +57514,7 @@ async function renderExpectsAudio(projectDir, beatId) {
|
|
|
57377
57514
|
`assets/narration-${beat.id}.wav`,
|
|
57378
57515
|
`assets/music-${beat.id}.mp3`,
|
|
57379
57516
|
`assets/music-${beat.id}.wav`
|
|
57380
|
-
].some((asset) =>
|
|
57517
|
+
].some((asset) => existsSync40(join23(root, asset)))
|
|
57381
57518
|
);
|
|
57382
57519
|
} catch {
|
|
57383
57520
|
return false;
|
|
@@ -57485,8 +57622,8 @@ async function maybeWriteRenderReport(projectDir, opts, result) {
|
|
|
57485
57622
|
}
|
|
57486
57623
|
function renderSourceReports(projectDir, videoPath, checks) {
|
|
57487
57624
|
const reports = [];
|
|
57488
|
-
if (
|
|
57489
|
-
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");
|
|
57490
57627
|
if (videoPath) reports.push(displayPath(videoPath));
|
|
57491
57628
|
if (checks.durationSec !== void 0 || checks.width !== void 0 || checks.height !== void 0) {
|
|
57492
57629
|
reports.push("ffprobe");
|
|
@@ -57499,31 +57636,31 @@ function renderSourceReports(projectDir, videoPath, checks) {
|
|
|
57499
57636
|
async function resolveRenderVideoPath(projectDir, explicit, beatId) {
|
|
57500
57637
|
if (explicit) {
|
|
57501
57638
|
const candidate = resolve42(process.cwd(), explicit);
|
|
57502
|
-
return
|
|
57639
|
+
return existsSync40(candidate) ? candidate : null;
|
|
57503
57640
|
}
|
|
57504
|
-
const renderReportPath =
|
|
57505
|
-
if (
|
|
57641
|
+
const renderReportPath = join23(projectDir, "render-report.json");
|
|
57642
|
+
if (existsSync40(renderReportPath)) {
|
|
57506
57643
|
try {
|
|
57507
|
-
const report = JSON.parse(await
|
|
57644
|
+
const report = JSON.parse(await readFile33(renderReportPath, "utf-8"));
|
|
57508
57645
|
const reportBeat = typeof report.beat === "string" ? report.beat : null;
|
|
57509
57646
|
if (!beatId && reportBeat === null || beatId && reportBeat === beatId) {
|
|
57510
57647
|
const candidate = resolveReportedPath(projectDir, report.outputPath);
|
|
57511
|
-
if (candidate &&
|
|
57648
|
+
if (candidate && existsSync40(candidate)) return candidate;
|
|
57512
57649
|
}
|
|
57513
57650
|
} catch {
|
|
57514
57651
|
}
|
|
57515
57652
|
}
|
|
57516
|
-
const reportPath =
|
|
57517
|
-
if (!beatId &&
|
|
57653
|
+
const reportPath = join23(projectDir, "build-report.json");
|
|
57654
|
+
if (!beatId && existsSync40(reportPath)) {
|
|
57518
57655
|
try {
|
|
57519
|
-
const report = JSON.parse(await
|
|
57656
|
+
const report = JSON.parse(await readFile33(reportPath, "utf-8"));
|
|
57520
57657
|
const candidate = resolveReportedPath(projectDir, report.outputPath);
|
|
57521
|
-
if (candidate &&
|
|
57658
|
+
if (candidate && existsSync40(candidate)) return candidate;
|
|
57522
57659
|
} catch {
|
|
57523
57660
|
}
|
|
57524
57661
|
}
|
|
57525
|
-
const rendersDir =
|
|
57526
|
-
if (!
|
|
57662
|
+
const rendersDir = join23(projectDir, "renders");
|
|
57663
|
+
if (!existsSync40(rendersDir)) return null;
|
|
57527
57664
|
const entries = await readdir5(rendersDir, { withFileTypes: true });
|
|
57528
57665
|
const candidates = [];
|
|
57529
57666
|
for (const entry of entries) {
|
|
@@ -57531,7 +57668,7 @@ async function resolveRenderVideoPath(projectDir, explicit, beatId) {
|
|
|
57531
57668
|
const ext = entry.name.slice(entry.name.lastIndexOf(".")).toLowerCase();
|
|
57532
57669
|
if (!VIDEO_EXTENSIONS.has(ext)) continue;
|
|
57533
57670
|
if (beatId && !entry.name.includes(sanitizeFileSegment2(beatId))) continue;
|
|
57534
|
-
const full =
|
|
57671
|
+
const full = join23(rendersDir, entry.name);
|
|
57535
57672
|
const info = await stat6(full);
|
|
57536
57673
|
candidates.push({ path: full, mtimeMs: info.mtimeMs });
|
|
57537
57674
|
}
|
|
@@ -57549,14 +57686,14 @@ async function runAiRenderReview(projectDir, videoPath, model) {
|
|
|
57549
57686
|
});
|
|
57550
57687
|
}
|
|
57551
57688
|
function resolveStoryboardPath(projectDir) {
|
|
57552
|
-
const storyboardPath =
|
|
57553
|
-
return
|
|
57689
|
+
const storyboardPath = join23(projectDir, "STORYBOARD.md");
|
|
57690
|
+
return existsSync40(storyboardPath) ? storyboardPath : null;
|
|
57554
57691
|
}
|
|
57555
57692
|
async function expectedDurationFromBuildReport(projectDir, beatId) {
|
|
57556
|
-
const reportPath =
|
|
57557
|
-
if (!
|
|
57693
|
+
const reportPath = join23(projectDir, "build-report.json");
|
|
57694
|
+
if (!existsSync40(reportPath)) return void 0;
|
|
57558
57695
|
try {
|
|
57559
|
-
const report = JSON.parse(await
|
|
57696
|
+
const report = JSON.parse(await readFile33(reportPath, "utf-8"));
|
|
57560
57697
|
const beats = beatId ? (report.beats ?? []).filter((beat) => beat.id === beatId) : report.beats ?? [];
|
|
57561
57698
|
const durations = beats.map(
|
|
57562
57699
|
(beat) => parseOptionalNumber2(beat.sceneDurationSec) ?? parseOptionalNumber2(beat.narration?.sceneDurationSec)
|
|
@@ -57568,10 +57705,10 @@ async function expectedDurationFromBuildReport(projectDir, beatId) {
|
|
|
57568
57705
|
}
|
|
57569
57706
|
}
|
|
57570
57707
|
async function beatTimingsFromBuildReport(projectDir, beatId) {
|
|
57571
|
-
const reportPath =
|
|
57572
|
-
if (!
|
|
57708
|
+
const reportPath = join23(projectDir, "build-report.json");
|
|
57709
|
+
if (!existsSync40(reportPath)) return [];
|
|
57573
57710
|
try {
|
|
57574
|
-
const report = JSON.parse(await
|
|
57711
|
+
const report = JSON.parse(await readFile33(reportPath, "utf-8"));
|
|
57575
57712
|
const source = report.beats ?? [];
|
|
57576
57713
|
const selected = beatId ? source.filter((beat) => beat.id === beatId) : source;
|
|
57577
57714
|
let cursor = 0;
|
|
@@ -57597,17 +57734,17 @@ async function beatTimingsFromBuildReport(projectDir, beatId) {
|
|
|
57597
57734
|
}
|
|
57598
57735
|
async function buildRenderReviewProjectContext(projectDir) {
|
|
57599
57736
|
const parts = [];
|
|
57600
|
-
const storyboard = await readTextIfExists(
|
|
57737
|
+
const storyboard = await readTextIfExists(join23(projectDir, "STORYBOARD.md"));
|
|
57601
57738
|
if (storyboard) {
|
|
57602
57739
|
parts.push(`STORYBOARD.md:
|
|
57603
57740
|
${truncateText(storyboard, 4e3)}`);
|
|
57604
57741
|
parts.push(`Storyboard beat cue summary:
|
|
57605
57742
|
${storyboardBeatCueSummary(storyboard)}`);
|
|
57606
57743
|
}
|
|
57607
|
-
const design = await readTextIfExists(
|
|
57744
|
+
const design = await readTextIfExists(join23(projectDir, "DESIGN.md"));
|
|
57608
57745
|
if (design) parts.push(`DESIGN.md:
|
|
57609
57746
|
${truncateText(design, 4e3)}`);
|
|
57610
|
-
const buildReport = await readTextIfExists(
|
|
57747
|
+
const buildReport = await readTextIfExists(join23(projectDir, "build-report.json"));
|
|
57611
57748
|
if (buildReport) parts.push(`build-report.json:
|
|
57612
57749
|
${truncateText(buildReport, 6e3)}`);
|
|
57613
57750
|
const timings = await beatTimingsFromBuildReport(projectDir);
|
|
@@ -57635,9 +57772,9 @@ function storyboardBeatCueSummary(storyboard) {
|
|
|
57635
57772
|
}
|
|
57636
57773
|
}
|
|
57637
57774
|
async function readTextIfExists(path2) {
|
|
57638
|
-
if (!
|
|
57775
|
+
if (!existsSync40(path2)) return null;
|
|
57639
57776
|
try {
|
|
57640
|
-
return await
|
|
57777
|
+
return await readFile33(path2, "utf-8");
|
|
57641
57778
|
} catch {
|
|
57642
57779
|
return null;
|
|
57643
57780
|
}
|
|
@@ -58455,16 +58592,16 @@ var generateTools = [
|
|
|
58455
58592
|
];
|
|
58456
58593
|
|
|
58457
58594
|
// src/tools/manifest/storyboard.ts
|
|
58458
|
-
import { existsSync as
|
|
58459
|
-
import { readFile as
|
|
58595
|
+
import { existsSync as existsSync43 } from "node:fs";
|
|
58596
|
+
import { readFile as readFile37, writeFile as writeFile41 } from "node:fs/promises";
|
|
58460
58597
|
import { resolve as resolve54 } from "node:path";
|
|
58461
58598
|
init_storyboard_parse();
|
|
58462
58599
|
init_storyboard_edit();
|
|
58463
58600
|
|
|
58464
58601
|
// src/commands/_shared/storyboard-revise.ts
|
|
58465
|
-
import { existsSync as
|
|
58466
|
-
import { readFile as
|
|
58467
|
-
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";
|
|
58468
58605
|
|
|
58469
58606
|
// src/agent/adapters/index.ts
|
|
58470
58607
|
init_openai2();
|
|
@@ -58516,9 +58653,9 @@ init_storyboard_parse();
|
|
|
58516
58653
|
init_storyboard_edit();
|
|
58517
58654
|
async function executeStoryboardRevision(opts) {
|
|
58518
58655
|
const projectDir = resolve53(opts.projectDir);
|
|
58519
|
-
const storyboardPath =
|
|
58656
|
+
const storyboardPath = join24(projectDir, "STORYBOARD.md");
|
|
58520
58657
|
const retryWith = [`vibe storyboard revise ${projectDir} --from "<request>" --dry-run --json`];
|
|
58521
|
-
if (!
|
|
58658
|
+
if (!existsSync42(storyboardPath)) {
|
|
58522
58659
|
return failureResult({
|
|
58523
58660
|
projectDir,
|
|
58524
58661
|
storyboardPath,
|
|
@@ -58528,7 +58665,7 @@ async function executeStoryboardRevision(opts) {
|
|
|
58528
58665
|
retryWith: [`vibe init ${projectDir} --from "<brief>" --json`]
|
|
58529
58666
|
});
|
|
58530
58667
|
}
|
|
58531
|
-
const currentStoryboard = await
|
|
58668
|
+
const currentStoryboard = await readFile36(storyboardPath, "utf-8");
|
|
58532
58669
|
const projectConfig = await readProjectConfig(projectDir);
|
|
58533
58670
|
const explicitComposer = opts.composer ?? projectConfig.config.providers.composer ?? void 0;
|
|
58534
58671
|
let provider = explicitComposer ?? "claude";
|
|
@@ -58751,18 +58888,18 @@ function diffChangedBeats(before, after) {
|
|
|
58751
58888
|
return ids.filter((id) => JSON.stringify(beforeBeats.get(id) ?? null) !== JSON.stringify(afterBeats.get(id) ?? null));
|
|
58752
58889
|
}
|
|
58753
58890
|
async function readRevisionContext(projectDir) {
|
|
58754
|
-
const designMd = await readOptionalText(
|
|
58755
|
-
const buildReportJson = await readOptionalJsonText(
|
|
58891
|
+
const designMd = await readOptionalText(join24(projectDir, "DESIGN.md"));
|
|
58892
|
+
const buildReportJson = await readOptionalJsonText(join24(projectDir, "build-report.json"));
|
|
58756
58893
|
return { designMd, buildReportJson };
|
|
58757
58894
|
}
|
|
58758
58895
|
async function readOptionalText(path2) {
|
|
58759
|
-
if (!
|
|
58760
|
-
return
|
|
58896
|
+
if (!existsSync42(path2)) return null;
|
|
58897
|
+
return readFile36(path2, "utf-8");
|
|
58761
58898
|
}
|
|
58762
58899
|
async function readOptionalJsonText(path2) {
|
|
58763
|
-
if (!
|
|
58900
|
+
if (!existsSync42(path2)) return null;
|
|
58764
58901
|
try {
|
|
58765
|
-
const parsed = JSON.parse(await
|
|
58902
|
+
const parsed = JSON.parse(await readFile36(path2, "utf-8"));
|
|
58766
58903
|
return JSON.stringify(parsed, null, 2);
|
|
58767
58904
|
} catch {
|
|
58768
58905
|
return null;
|
|
@@ -58807,8 +58944,8 @@ var projectDirSchema = external_exports.object({
|
|
|
58807
58944
|
});
|
|
58808
58945
|
async function readStoryboard(projectDir) {
|
|
58809
58946
|
const path2 = resolve54(projectDir, "STORYBOARD.md");
|
|
58810
|
-
if (!
|
|
58811
|
-
return { path: path2, markdown: await
|
|
58947
|
+
if (!existsSync43(path2)) return null;
|
|
58948
|
+
return { path: path2, markdown: await readFile37(path2, "utf-8") };
|
|
58812
58949
|
}
|
|
58813
58950
|
var storyboardListTool = defineTool({
|
|
58814
58951
|
name: "storyboard_list",
|
|
@@ -59067,7 +59204,7 @@ var storyboardTools = [
|
|
|
59067
59204
|
// src/tools/manifest/remix.ts
|
|
59068
59205
|
import { writeFile as writeFile46 } from "node:fs/promises";
|
|
59069
59206
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
59070
|
-
import { join as
|
|
59207
|
+
import { join as join25 } from "node:path";
|
|
59071
59208
|
|
|
59072
59209
|
// src/commands/ai-script-pipeline.ts
|
|
59073
59210
|
var import_yaml6 = __toESM(require_dist2(), 1);
|
|
@@ -59077,9 +59214,9 @@ init_config();
|
|
|
59077
59214
|
init_audio();
|
|
59078
59215
|
init_ai_helpers();
|
|
59079
59216
|
init_video_utils();
|
|
59080
|
-
import { readFile as
|
|
59217
|
+
import { readFile as readFile38, writeFile as writeFile42, unlink as unlink3, rename as rename4 } from "node:fs/promises";
|
|
59081
59218
|
import { resolve as resolve55, extname as extname10 } from "node:path";
|
|
59082
|
-
import { existsSync as
|
|
59219
|
+
import { existsSync as existsSync44 } from "node:fs";
|
|
59083
59220
|
|
|
59084
59221
|
// src/commands/_shared/video-providers.ts
|
|
59085
59222
|
init_source();
|
|
@@ -59238,16 +59375,16 @@ async function executeRegenerateScene(options) {
|
|
|
59238
59375
|
};
|
|
59239
59376
|
try {
|
|
59240
59377
|
const outputDir = resolve55(process.cwd(), options.projectDir);
|
|
59241
|
-
if (!
|
|
59378
|
+
if (!existsSync44(outputDir)) {
|
|
59242
59379
|
return { ...result, error: `Project directory not found: ${outputDir}` };
|
|
59243
59380
|
}
|
|
59244
59381
|
const yamlPath = resolve55(outputDir, "storyboard.yaml");
|
|
59245
59382
|
const jsonPath = resolve55(outputDir, "storyboard.json");
|
|
59246
|
-
const storyboardPath =
|
|
59383
|
+
const storyboardPath = existsSync44(yamlPath) ? yamlPath : existsSync44(jsonPath) ? jsonPath : null;
|
|
59247
59384
|
if (!storyboardPath) {
|
|
59248
59385
|
return { ...result, error: `Storyboard not found in: ${outputDir} (expected storyboard.yaml or storyboard.json)` };
|
|
59249
59386
|
}
|
|
59250
|
-
const storyboardContent = await
|
|
59387
|
+
const storyboardContent = await readFile38(storyboardPath, "utf-8");
|
|
59251
59388
|
const segments = storyboardPath.endsWith(".yaml") ? (0, import_yaml6.parse)(storyboardContent).scenes : JSON.parse(storyboardContent);
|
|
59252
59389
|
for (const sceneNum of options.scenes) {
|
|
59253
59390
|
if (sceneNum < 1 || sceneNum > segments.length) {
|
|
@@ -59336,15 +59473,15 @@ IMPORTANT - Character appearance must match exactly: ${characterDesc}`;
|
|
|
59336
59473
|
const refSceneNum = options.referenceScene;
|
|
59337
59474
|
if (refSceneNum && refSceneNum >= 1 && refSceneNum <= segments.length && refSceneNum !== sceneNum) {
|
|
59338
59475
|
const refImagePath = resolve55(outputDir, `scene-${refSceneNum}.png`);
|
|
59339
|
-
if (
|
|
59340
|
-
referenceImageBuffer = await
|
|
59476
|
+
if (existsSync44(refImagePath)) {
|
|
59477
|
+
referenceImageBuffer = await readFile38(refImagePath);
|
|
59341
59478
|
}
|
|
59342
59479
|
} else if (!refSceneNum) {
|
|
59343
59480
|
for (let i = 1; i <= segments.length; i++) {
|
|
59344
59481
|
if (i !== sceneNum) {
|
|
59345
59482
|
const otherImagePath = resolve55(outputDir, `scene-${i}.png`);
|
|
59346
|
-
if (
|
|
59347
|
-
referenceImageBuffer = await
|
|
59483
|
+
if (existsSync44(otherImagePath)) {
|
|
59484
|
+
referenceImageBuffer = await readFile38(otherImagePath);
|
|
59348
59485
|
break;
|
|
59349
59486
|
}
|
|
59350
59487
|
}
|
|
@@ -59432,11 +59569,11 @@ Generate the single-person scene image now.`;
|
|
|
59432
59569
|
}
|
|
59433
59570
|
if (regenerateVideo && videoApiKey) {
|
|
59434
59571
|
options.onProgress?.(`Scene ${sceneNum}: regenerating video (${options.generator || "grok"})...`);
|
|
59435
|
-
if (!
|
|
59572
|
+
if (!existsSync44(imagePath)) {
|
|
59436
59573
|
result.failedScenes.push(sceneNum);
|
|
59437
59574
|
continue;
|
|
59438
59575
|
}
|
|
59439
|
-
const imageBuffer = await
|
|
59576
|
+
const imageBuffer = await readFile38(imagePath);
|
|
59440
59577
|
const videoDuration = segment.duration > 5 ? 10 : 5;
|
|
59441
59578
|
const maxRetries = options.retries ?? DEFAULT_VIDEO_RETRIES;
|
|
59442
59579
|
if (options.generator === "grok") {
|
|
@@ -59644,9 +59781,9 @@ Generate the single-person scene image now.`;
|
|
|
59644
59781
|
}
|
|
59645
59782
|
|
|
59646
59783
|
// src/commands/ai-highlights.ts
|
|
59647
|
-
import { readFile as
|
|
59784
|
+
import { readFile as readFile39, writeFile as writeFile43, mkdir as mkdir23 } from "node:fs/promises";
|
|
59648
59785
|
import { resolve as resolve56, dirname as dirname22, basename as basename14, extname as extname11 } from "node:path";
|
|
59649
|
-
import { existsSync as
|
|
59786
|
+
import { existsSync as existsSync45 } from "node:fs";
|
|
59650
59787
|
init_dist2();
|
|
59651
59788
|
init_engine();
|
|
59652
59789
|
init_api_key();
|
|
@@ -59677,7 +59814,7 @@ function filterHighlights(highlights, options) {
|
|
|
59677
59814
|
async function executeHighlights(options) {
|
|
59678
59815
|
try {
|
|
59679
59816
|
const absPath = resolve56(process.cwd(), options.media);
|
|
59680
|
-
if (!
|
|
59817
|
+
if (!existsSync45(absPath)) {
|
|
59681
59818
|
return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: `File not found: ${absPath}` };
|
|
59682
59819
|
}
|
|
59683
59820
|
const ext = extname11(absPath).toLowerCase();
|
|
@@ -59696,7 +59833,7 @@ async function executeHighlights(options) {
|
|
|
59696
59833
|
sourceDuration = await ffprobeDuration(absPath);
|
|
59697
59834
|
const gemini = new GeminiProvider();
|
|
59698
59835
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
59699
|
-
const videoBuffer = await
|
|
59836
|
+
const videoBuffer = await readFile39(absPath);
|
|
59700
59837
|
const criteriaText = options.criteria === "all" || !options.criteria ? "emotional, informative, and funny moments" : `${options.criteria} moments`;
|
|
59701
59838
|
const durationText = targetDuration ? `Target a total highlight duration of ${targetDuration} seconds.` : "";
|
|
59702
59839
|
const countText = maxCount ? `Find up to ${maxCount} highlights.` : "";
|
|
@@ -59790,10 +59927,10 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
59790
59927
|
}
|
|
59791
59928
|
const whisper = new WhisperProvider();
|
|
59792
59929
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
59793
|
-
const audioBuffer = await
|
|
59930
|
+
const audioBuffer = await readFile39(audioPath);
|
|
59794
59931
|
const audioBlob = new Blob([audioBuffer]);
|
|
59795
59932
|
const transcriptResult = await whisper.transcribe(audioBlob, options.language);
|
|
59796
|
-
if (tempAudioPath &&
|
|
59933
|
+
if (tempAudioPath && existsSync45(tempAudioPath)) {
|
|
59797
59934
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
59798
59935
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
59799
59936
|
});
|
|
@@ -59880,7 +60017,7 @@ async function executeAutoShorts(options) {
|
|
|
59880
60017
|
return { success: false, shorts: [], error: "FFmpeg not found" };
|
|
59881
60018
|
}
|
|
59882
60019
|
const absPath = resolve56(process.cwd(), options.video);
|
|
59883
|
-
if (!
|
|
60020
|
+
if (!existsSync45(absPath)) {
|
|
59884
60021
|
return { success: false, shorts: [], error: `File not found: ${absPath}` };
|
|
59885
60022
|
}
|
|
59886
60023
|
const targetDuration = options.duration ?? 60;
|
|
@@ -59893,7 +60030,7 @@ async function executeAutoShorts(options) {
|
|
|
59893
60030
|
}
|
|
59894
60031
|
const gemini = new GeminiProvider();
|
|
59895
60032
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
59896
|
-
const videoBuffer = await
|
|
60033
|
+
const videoBuffer = await readFile39(absPath);
|
|
59897
60034
|
const geminiPrompt = `Analyze this video to find the BEST moments for short-form vertical video content (TikTok, YouTube Shorts, Instagram Reels).
|
|
59898
60035
|
|
|
59899
60036
|
Find ${shortCount * 3} potential clips that are ${targetDuration} seconds or shorter each.
|
|
@@ -59970,7 +60107,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
59970
60107
|
await execSafe("ffmpeg", ["-i", absPath, "-vn", "-acodec", "libmp3lame", "-q:a", "2", tempAudio, "-y"]);
|
|
59971
60108
|
const whisper = new WhisperProvider();
|
|
59972
60109
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
59973
|
-
const audioBuffer = await
|
|
60110
|
+
const audioBuffer = await readFile39(tempAudio);
|
|
59974
60111
|
const audioBlob = new Blob([audioBuffer]);
|
|
59975
60112
|
const transcript = await whisper.transcribe(audioBlob, options.language);
|
|
59976
60113
|
try {
|
|
@@ -60008,7 +60145,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
60008
60145
|
};
|
|
60009
60146
|
}
|
|
60010
60147
|
const outputDir = options.outputDir ? resolve56(process.cwd(), options.outputDir) : dirname22(absPath);
|
|
60011
|
-
if (options.outputDir && !
|
|
60148
|
+
if (options.outputDir && !existsSync45(outputDir)) {
|
|
60012
60149
|
await mkdir23(outputDir, { recursive: true });
|
|
60013
60150
|
}
|
|
60014
60151
|
const result = {
|
|
@@ -60087,8 +60224,8 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
60087
60224
|
// src/pipeline/executor.ts
|
|
60088
60225
|
var import_yaml7 = __toESM(require_dist2(), 1);
|
|
60089
60226
|
import { resolve as resolve58 } from "node:path";
|
|
60090
|
-
import { readFile as
|
|
60091
|
-
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";
|
|
60092
60229
|
|
|
60093
60230
|
// src/pipeline/resolver.ts
|
|
60094
60231
|
function resolveStepParams(params, completedSteps) {
|
|
@@ -60501,10 +60638,10 @@ async function ensureActionsRegistered() {
|
|
|
60501
60638
|
}
|
|
60502
60639
|
async function loadPipeline(filePath) {
|
|
60503
60640
|
const absPath = resolve58(process.cwd(), filePath);
|
|
60504
|
-
if (!
|
|
60641
|
+
if (!existsSync46(absPath)) {
|
|
60505
60642
|
throw new Error(`Pipeline file not found: ${absPath}`);
|
|
60506
60643
|
}
|
|
60507
|
-
const content = await
|
|
60644
|
+
const content = await readFile41(absPath, "utf-8");
|
|
60508
60645
|
const manifest2 = (0, import_yaml7.parse)(content);
|
|
60509
60646
|
if (!manifest2.name) throw new Error("Pipeline missing 'name' field");
|
|
60510
60647
|
if (!manifest2.steps || !Array.isArray(manifest2.steps)) throw new Error("Pipeline missing 'steps' array");
|
|
@@ -60527,8 +60664,8 @@ async function executePipeline(manifest2, options = {}) {
|
|
|
60527
60664
|
const startTime = Date.now();
|
|
60528
60665
|
if (options.resume) {
|
|
60529
60666
|
const checkpointPath = resolve58(outputDir, CHECKPOINT_FILE);
|
|
60530
|
-
if (
|
|
60531
|
-
const checkpointContent = await
|
|
60667
|
+
if (existsSync46(checkpointPath)) {
|
|
60668
|
+
const checkpointContent = await readFile41(checkpointPath, "utf-8");
|
|
60532
60669
|
const checkpoint = (0, import_yaml7.parse)(checkpointContent);
|
|
60533
60670
|
for (const cs of checkpoint.completedSteps) {
|
|
60534
60671
|
completedSteps.set(cs.id, {
|
|
@@ -60800,7 +60937,7 @@ var pipelineRunTool = defineTool({
|
|
|
60800
60937
|
let resolvedPath = args.pipelinePath;
|
|
60801
60938
|
let tempPath;
|
|
60802
60939
|
if (args.pipelineYaml) {
|
|
60803
|
-
tempPath =
|
|
60940
|
+
tempPath = join25(tmpdir4(), `vibe-mcp-pipeline-${Date.now()}.yaml`);
|
|
60804
60941
|
await writeFile46(tempPath, args.pipelineYaml, "utf-8");
|
|
60805
60942
|
resolvedPath = tempPath;
|
|
60806
60943
|
}
|
|
@@ -60974,10 +61111,10 @@ init_project_resolver();
|
|
|
60974
61111
|
// src/tools/manifest/_project-io.ts
|
|
60975
61112
|
init_engine();
|
|
60976
61113
|
init_project_resolver();
|
|
60977
|
-
import { readFile as
|
|
61114
|
+
import { readFile as readFile42, writeFile as writeFile47 } from "node:fs/promises";
|
|
60978
61115
|
async function loadProject(projectPath, cwd) {
|
|
60979
61116
|
const absPath = await resolveTimelineFile(projectPath, cwd);
|
|
60980
|
-
const content = await
|
|
61117
|
+
const content = await readFile42(absPath, "utf-8");
|
|
60981
61118
|
const data = JSON.parse(content);
|
|
60982
61119
|
return { project: Project.fromJSON(data), absPath };
|
|
60983
61120
|
}
|
|
@@ -61327,12 +61464,12 @@ init_engine();
|
|
|
61327
61464
|
|
|
61328
61465
|
// src/commands/_shared/project-list.ts
|
|
61329
61466
|
init_storyboard_parse();
|
|
61330
|
-
import { readdir as readdir6, readFile as
|
|
61331
|
-
import { existsSync as
|
|
61332
|
-
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";
|
|
61333
61470
|
async function executeProjectList(opts = {}) {
|
|
61334
61471
|
const workspaceDir = resolve60(opts.workspaceDir ?? ".");
|
|
61335
|
-
if (!
|
|
61472
|
+
if (!existsSync47(workspaceDir)) {
|
|
61336
61473
|
return {
|
|
61337
61474
|
success: false,
|
|
61338
61475
|
workspaceDir,
|
|
@@ -61349,11 +61486,11 @@ async function executeProjectList(opts = {}) {
|
|
|
61349
61486
|
if (entry.name === "node_modules") continue;
|
|
61350
61487
|
if (entry.name.startsWith(".")) continue;
|
|
61351
61488
|
if (entry.name.startsWith("_")) {
|
|
61352
|
-
archivedCount += await countProjects(
|
|
61489
|
+
archivedCount += await countProjects(join26(workspaceDir, entry.name));
|
|
61353
61490
|
continue;
|
|
61354
61491
|
}
|
|
61355
|
-
const projectDir =
|
|
61356
|
-
if (!
|
|
61492
|
+
const projectDir = join26(workspaceDir, entry.name);
|
|
61493
|
+
if (!existsSync47(join26(projectDir, "STORYBOARD.md"))) continue;
|
|
61357
61494
|
projects.push(await describeProject(entry.name, projectDir));
|
|
61358
61495
|
}
|
|
61359
61496
|
projects.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
@@ -61363,7 +61500,7 @@ async function countProjects(dir) {
|
|
|
61363
61500
|
try {
|
|
61364
61501
|
const entries = await readdir6(dir, { withFileTypes: true });
|
|
61365
61502
|
return entries.filter(
|
|
61366
|
-
(e) => e.isDirectory() &&
|
|
61503
|
+
(e) => e.isDirectory() && existsSync47(join26(dir, e.name, "STORYBOARD.md"))
|
|
61367
61504
|
).length;
|
|
61368
61505
|
} catch {
|
|
61369
61506
|
return 0;
|
|
@@ -61374,8 +61511,8 @@ async function describeProject(name, projectDir) {
|
|
|
61374
61511
|
let storyboardDurationSec = 0;
|
|
61375
61512
|
const timestamps = [];
|
|
61376
61513
|
try {
|
|
61377
|
-
const storyboardPath =
|
|
61378
|
-
const parsed = parseStoryboard(await
|
|
61514
|
+
const storyboardPath = join26(projectDir, "STORYBOARD.md");
|
|
61515
|
+
const parsed = parseStoryboard(await readFile43(storyboardPath, "utf-8"));
|
|
61379
61516
|
beats = parsed.beats.length;
|
|
61380
61517
|
storyboardDurationSec = Number(
|
|
61381
61518
|
parsed.beats.reduce((sum, beat) => sum + (beat.duration ?? 0), 0).toFixed(2)
|
|
@@ -61386,8 +61523,8 @@ async function describeProject(name, projectDir) {
|
|
|
61386
61523
|
let buildStatus;
|
|
61387
61524
|
let buildUpdatedAt;
|
|
61388
61525
|
try {
|
|
61389
|
-
const reportPath =
|
|
61390
|
-
const report = JSON.parse(await
|
|
61526
|
+
const reportPath = join26(projectDir, "build-report.json");
|
|
61527
|
+
const report = JSON.parse(await readFile43(reportPath, "utf-8"));
|
|
61391
61528
|
buildStatus = report.status ?? report.phase;
|
|
61392
61529
|
buildUpdatedAt = report.updatedAt;
|
|
61393
61530
|
timestamps.push((await stat7(reportPath)).mtimeMs);
|
|
@@ -61395,12 +61532,12 @@ async function describeProject(name, projectDir) {
|
|
|
61395
61532
|
}
|
|
61396
61533
|
let latestRender;
|
|
61397
61534
|
try {
|
|
61398
|
-
const rendersDir =
|
|
61535
|
+
const rendersDir = join26(projectDir, "renders");
|
|
61399
61536
|
const files = await readdir6(rendersDir);
|
|
61400
61537
|
let newest;
|
|
61401
61538
|
for (const file of files) {
|
|
61402
61539
|
if (!/\.(mp4|webm|mov)$/i.test(file)) continue;
|
|
61403
|
-
const { mtimeMs } = await stat7(
|
|
61540
|
+
const { mtimeMs } = await stat7(join26(rendersDir, file));
|
|
61404
61541
|
if (!newest || mtimeMs > newest.mtimeMs) newest = { file, mtimeMs };
|
|
61405
61542
|
}
|
|
61406
61543
|
if (newest) {
|
|
@@ -61534,7 +61671,7 @@ init_exec_safe();
|
|
|
61534
61671
|
init_output();
|
|
61535
61672
|
init_validate();
|
|
61536
61673
|
init_project_resolver();
|
|
61537
|
-
import { readFile as
|
|
61674
|
+
import { readFile as readFile44, access as access6 } from "node:fs/promises";
|
|
61538
61675
|
import { resolve as resolve62, basename as basename17 } from "node:path";
|
|
61539
61676
|
import { spawn } from "node:child_process";
|
|
61540
61677
|
function timelineBaseName(path2) {
|
|
@@ -61611,7 +61748,7 @@ async function runExport(projectPath, outputPath, options = {}) {
|
|
|
61611
61748
|
};
|
|
61612
61749
|
}
|
|
61613
61750
|
const filePath = await resolveTimelineFile(projectPath);
|
|
61614
|
-
const content = await
|
|
61751
|
+
const content = await readFile44(filePath, "utf-8");
|
|
61615
61752
|
const data = JSON.parse(content);
|
|
61616
61753
|
const project = Project.fromJSON(data);
|
|
61617
61754
|
const summary = project.getSummary();
|
|
@@ -61735,7 +61872,7 @@ Run 'vibe schema export' for structured parameter info.`).action(async (projectP
|
|
|
61735
61872
|
}
|
|
61736
61873
|
spinner2.text = "Loading project...";
|
|
61737
61874
|
const filePath = await resolveTimelineFile(projectPath);
|
|
61738
|
-
const content = await
|
|
61875
|
+
const content = await readFile44(filePath, "utf-8");
|
|
61739
61876
|
const data = JSON.parse(content);
|
|
61740
61877
|
const project = Project.fromJSON(data);
|
|
61741
61878
|
const summary = project.getSummary();
|
|
@@ -62333,14 +62470,14 @@ function getPresetSettings(preset, aspectRatio2) {
|
|
|
62333
62470
|
}
|
|
62334
62471
|
async function runHyperframesExport(projectPath, options, spinner2, startedAt) {
|
|
62335
62472
|
spinner2.text = "Loading project...";
|
|
62336
|
-
const { readFile:
|
|
62473
|
+
const { readFile: readFile46 } = await import("node:fs/promises");
|
|
62337
62474
|
const { resolve: resolve66 } = await import("node:path");
|
|
62338
62475
|
const { Project: Project2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
62339
62476
|
const { createHyperframesBackend: createHyperframesBackend2 } = await Promise.resolve().then(() => (init_hyperframes(), hyperframes_exports));
|
|
62340
62477
|
const { exitWithError: exitWithError2, generalError: generalError2, outputSuccess: outputSuccess2 } = await Promise.resolve().then(() => (init_output(), output_exports));
|
|
62341
62478
|
const chalk2 = (await Promise.resolve().then(() => (init_source(), source_exports))).default;
|
|
62342
62479
|
const filePath = await resolveTimelineFile(projectPath);
|
|
62343
|
-
const content = await
|
|
62480
|
+
const content = await readFile46(filePath, "utf-8");
|
|
62344
62481
|
const project = Project2.fromJSON(JSON.parse(content));
|
|
62345
62482
|
const state = project.getState();
|
|
62346
62483
|
const outputPath = options.output ? resolve66(process.cwd(), options.output) : resolve66(process.cwd(), `${timelineBaseName(projectPath)}.${options.format ?? "mp4"}`);
|
|
@@ -62409,8 +62546,8 @@ var exportVideoTool = defineTool({
|
|
|
62409
62546
|
var exportTools = [exportVideoTool];
|
|
62410
62547
|
|
|
62411
62548
|
// src/tools/manifest/agent-only.ts
|
|
62412
|
-
import { readFile as
|
|
62413
|
-
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";
|
|
62414
62551
|
init_engine();
|
|
62415
62552
|
init_exec_safe();
|
|
62416
62553
|
init_project_resolver();
|
|
@@ -62488,7 +62625,7 @@ var fsListTool = defineTool({
|
|
|
62488
62625
|
const results = [];
|
|
62489
62626
|
for (const entry of entries) {
|
|
62490
62627
|
if (args.pattern && !matchPattern(entry.name, args.pattern)) continue;
|
|
62491
|
-
const fullPath =
|
|
62628
|
+
const fullPath = join27(absPath, entry.name);
|
|
62492
62629
|
const stats = await stat8(fullPath);
|
|
62493
62630
|
if (entry.isDirectory()) {
|
|
62494
62631
|
results.push(`[DIR] ${entry.name}/`);
|
|
@@ -62517,7 +62654,7 @@ var fsReadTool = defineTool({
|
|
|
62517
62654
|
async execute(args, ctx) {
|
|
62518
62655
|
try {
|
|
62519
62656
|
const absPath = resolve64(ctx.workingDirectory, args.path);
|
|
62520
|
-
const content = await
|
|
62657
|
+
const content = await readFile45(absPath, "utf-8");
|
|
62521
62658
|
const maxLength = 4e3;
|
|
62522
62659
|
const truncated = content.length > maxLength;
|
|
62523
62660
|
const output = truncated ? content.substring(0, maxLength) + "\n... (truncated)" : content;
|
|
@@ -62590,7 +62727,7 @@ var batchImportTool = defineTool({
|
|
|
62590
62727
|
async execute(args, ctx) {
|
|
62591
62728
|
try {
|
|
62592
62729
|
const filePath = resolve64(ctx.workingDirectory, args.project);
|
|
62593
|
-
const content = await
|
|
62730
|
+
const content = await readFile45(filePath, "utf-8");
|
|
62594
62731
|
const data = JSON.parse(content);
|
|
62595
62732
|
const project = Project.fromJSON(data);
|
|
62596
62733
|
const dirPath = resolve64(ctx.workingDirectory, args.directory);
|
|
@@ -62601,7 +62738,7 @@ var batchImportTool = defineTool({
|
|
|
62601
62738
|
const scanDir = async (dir) => {
|
|
62602
62739
|
const entries = await readdir7(dir, { withFileTypes: true });
|
|
62603
62740
|
for (const entry of entries) {
|
|
62604
|
-
const entryPath =
|
|
62741
|
+
const entryPath = join27(dir, entry.name);
|
|
62605
62742
|
if (entry.isDirectory() && recursive) {
|
|
62606
62743
|
await scanDir(entryPath);
|
|
62607
62744
|
} else if (entry.isFile()) {
|
|
@@ -62662,7 +62799,7 @@ var batchConcatTool = defineTool({
|
|
|
62662
62799
|
async execute(args, ctx) {
|
|
62663
62800
|
try {
|
|
62664
62801
|
const filePath = resolve64(ctx.workingDirectory, args.project);
|
|
62665
|
-
const content = await
|
|
62802
|
+
const content = await readFile45(filePath, "utf-8");
|
|
62666
62803
|
const data = JSON.parse(content);
|
|
62667
62804
|
const project = Project.fromJSON(data);
|
|
62668
62805
|
const sourceIds = args.sourceIds ?? [];
|
|
@@ -62733,7 +62870,7 @@ var batchApplyEffectTool = defineTool({
|
|
|
62733
62870
|
async execute(args, ctx) {
|
|
62734
62871
|
try {
|
|
62735
62872
|
const filePath = resolve64(ctx.workingDirectory, args.project);
|
|
62736
|
-
const content = await
|
|
62873
|
+
const content = await readFile45(filePath, "utf-8");
|
|
62737
62874
|
const data = JSON.parse(content);
|
|
62738
62875
|
const project = Project.fromJSON(data);
|
|
62739
62876
|
const clipIds = args.clipIds ?? [];
|
|
@@ -62788,7 +62925,7 @@ var timelineClearTool = defineTool({
|
|
|
62788
62925
|
async execute(args, ctx) {
|
|
62789
62926
|
try {
|
|
62790
62927
|
const filePath = await resolveTimelineFile(args.project, ctx.workingDirectory);
|
|
62791
|
-
const content = await
|
|
62928
|
+
const content = await readFile45(filePath, "utf-8");
|
|
62792
62929
|
const data = JSON.parse(content);
|
|
62793
62930
|
const project = Project.fromJSON(data);
|
|
62794
62931
|
const what = args.what ?? "clips";
|
|
@@ -62865,7 +63002,7 @@ var projectSetTool = defineTool({
|
|
|
62865
63002
|
async execute(args, ctx) {
|
|
62866
63003
|
try {
|
|
62867
63004
|
const filePath = await resolveTimelineFile(args.projectPath, ctx.workingDirectory);
|
|
62868
|
-
const content = await
|
|
63005
|
+
const content = await readFile45(filePath, "utf-8");
|
|
62869
63006
|
const data = JSON.parse(content);
|
|
62870
63007
|
const project = Project.fromJSON(data);
|
|
62871
63008
|
const updates = [];
|
|
@@ -63139,8 +63276,8 @@ var mediaCompressTool = defineTool({
|
|
|
63139
63276
|
],
|
|
63140
63277
|
{ maxBuffer: 50 * 1024 * 1024 }
|
|
63141
63278
|
);
|
|
63142
|
-
const inputBuf = await
|
|
63143
|
-
const outputBuf = await
|
|
63279
|
+
const inputBuf = await readFile45(absInput);
|
|
63280
|
+
const outputBuf = await readFile45(outputPath);
|
|
63144
63281
|
const inputSize = inputBuf.length;
|
|
63145
63282
|
const outputSize = outputBuf.length;
|
|
63146
63283
|
const reduction = (inputSize - outputSize) / inputSize * 100;
|