@vibeframe/cli 0.113.10 → 0.113.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/_shared/build-plan.d.ts +7 -0
- package/dist/commands/_shared/compose-prompts.d.ts +7 -0
- package/dist/commands/_shared/compose-scenes-skills.d.ts +30 -1
- package/dist/commands/_shared/scene-build.d.ts +20 -0
- package/dist/commands/_shared/transcribe-narration.d.ts +23 -0
- package/dist/commands/_shared/transcribe-narration.test.d.ts +1 -0
- package/dist/index.js +759 -621
- package/dist/tools/manifest/index.js +603 -473
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -11631,15 +11631,15 @@ async function loadKokoroRuntime() {
|
|
|
11631
11631
|
}
|
|
11632
11632
|
async function loadKokoroFromWorkspace(baseDir = process.cwd()) {
|
|
11633
11633
|
try {
|
|
11634
|
-
const { readFile:
|
|
11635
|
-
const { join:
|
|
11634
|
+
const { readFile: readFile57 } = await import("node:fs/promises");
|
|
11635
|
+
const { join: join31, resolve: resolve86 } = await import("node:path");
|
|
11636
11636
|
const { pathToFileURL } = await import("node:url");
|
|
11637
11637
|
const pkgDir = resolve86(baseDir, "node_modules", "kokoro-js");
|
|
11638
|
-
const pkg = JSON.parse(await
|
|
11638
|
+
const pkg = JSON.parse(await readFile57(join31(pkgDir, "package.json"), "utf-8"));
|
|
11639
11639
|
const entry = pkg.exports?.node?.import ?? pkg.exports?.default ?? pkg.module ?? pkg.main;
|
|
11640
11640
|
if (!entry)
|
|
11641
11641
|
return null;
|
|
11642
|
-
const mod = await import(pathToFileURL(
|
|
11642
|
+
const mod = await import(pathToFileURL(join31(pkgDir, entry)).href);
|
|
11643
11643
|
return mod.KokoroTTS ?? null;
|
|
11644
11644
|
} catch {
|
|
11645
11645
|
return null;
|
|
@@ -11650,20 +11650,20 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
|
|
|
11650
11650
|
if (!base)
|
|
11651
11651
|
return null;
|
|
11652
11652
|
try {
|
|
11653
|
-
const { readFile:
|
|
11654
|
-
const { join:
|
|
11653
|
+
const { readFile: readFile57, writeFile: writeFile61, mkdir: mkdir31 } = await import("node:fs/promises");
|
|
11654
|
+
const { join: join31, resolve: resolve86 } = await import("node:path");
|
|
11655
11655
|
const { pathToFileURL } = await import("node:url");
|
|
11656
11656
|
const { homedir: homedir8 } = await import("node:os");
|
|
11657
11657
|
const transformersDir = resolve86(base, "node_modules", "@huggingface", "transformers");
|
|
11658
11658
|
const ortDistDir = resolve86(base, "node_modules", "onnxruntime-web", "dist");
|
|
11659
|
-
const transformers = await import(pathToFileURL(
|
|
11660
|
-
const cacheRoot =
|
|
11659
|
+
const transformers = await import(pathToFileURL(join31(transformersDir, "dist", "transformers.web.js")).href);
|
|
11660
|
+
const cacheRoot = join31(homedir8(), ".cache", "vibeframe", "models");
|
|
11661
11661
|
transformers.env.allowLocalModels = false;
|
|
11662
11662
|
transformers.env.useCustomCache = true;
|
|
11663
11663
|
transformers.env.customCache = {
|
|
11664
11664
|
async match(key) {
|
|
11665
11665
|
try {
|
|
11666
|
-
return new Response(await
|
|
11666
|
+
return new Response(await readFile57(join31(cacheRoot, encodeURIComponent(String(key)))));
|
|
11667
11667
|
} catch {
|
|
11668
11668
|
return void 0;
|
|
11669
11669
|
}
|
|
@@ -11672,7 +11672,7 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
|
|
|
11672
11672
|
try {
|
|
11673
11673
|
const buf = Buffer.from(await response.arrayBuffer());
|
|
11674
11674
|
await mkdir31(cacheRoot, { recursive: true });
|
|
11675
|
-
await writeFile61(
|
|
11675
|
+
await writeFile61(join31(cacheRoot, encodeURIComponent(String(key))), buf);
|
|
11676
11676
|
} catch {
|
|
11677
11677
|
}
|
|
11678
11678
|
}
|
|
@@ -29972,10 +29972,57 @@ var init_composer_resolve = __esm({
|
|
|
29972
29972
|
}
|
|
29973
29973
|
});
|
|
29974
29974
|
|
|
29975
|
+
// src/commands/_shared/transcribe-narration.ts
|
|
29976
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
29977
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
29978
|
+
import { join as join6 } from "node:path";
|
|
29979
|
+
async function transcribeNarrationWords(audioAbsPath, opts) {
|
|
29980
|
+
try {
|
|
29981
|
+
const whisper = new WhisperProvider();
|
|
29982
|
+
await whisper.initialize({ apiKey: opts.apiKey });
|
|
29983
|
+
const audioBytes = await readFile8(audioAbsPath);
|
|
29984
|
+
const audioBlob = new Blob([new Uint8Array(audioBytes)]);
|
|
29985
|
+
const transcript = await whisper.transcribe(audioBlob, void 0, {
|
|
29986
|
+
granularity: "word",
|
|
29987
|
+
language: opts.language
|
|
29988
|
+
});
|
|
29989
|
+
if (transcript.status === "completed" && transcript.words?.length) {
|
|
29990
|
+
return transcript.words.map((w) => ({ text: w.text, start: w.start, end: w.end }));
|
|
29991
|
+
}
|
|
29992
|
+
return [];
|
|
29993
|
+
} catch {
|
|
29994
|
+
return [];
|
|
29995
|
+
}
|
|
29996
|
+
}
|
|
29997
|
+
function beatTranscriptRelPath(beatId) {
|
|
29998
|
+
return `assets/transcript-${beatId}.json`;
|
|
29999
|
+
}
|
|
30000
|
+
async function readBeatTranscript(projectDir, beatId) {
|
|
30001
|
+
const abs = join6(projectDir, beatTranscriptRelPath(beatId));
|
|
30002
|
+
if (!existsSync7(abs)) return void 0;
|
|
30003
|
+
try {
|
|
30004
|
+
const parsed = JSON.parse(await readFile8(abs, "utf-8"));
|
|
30005
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
30006
|
+
const words = parsed.filter(
|
|
30007
|
+
(w) => typeof w === "object" && w !== null && typeof w.text === "string" && Number.isFinite(w.start) && Number.isFinite(w.end)
|
|
30008
|
+
).map((w) => ({ text: w.text, start: w.start, end: w.end }));
|
|
30009
|
+
return words.length > 0 ? words : void 0;
|
|
30010
|
+
} catch {
|
|
30011
|
+
return void 0;
|
|
30012
|
+
}
|
|
30013
|
+
}
|
|
30014
|
+
var init_transcribe_narration = __esm({
|
|
30015
|
+
"src/commands/_shared/transcribe-narration.ts"() {
|
|
30016
|
+
"use strict";
|
|
30017
|
+
init_dist2();
|
|
30018
|
+
}
|
|
30019
|
+
});
|
|
30020
|
+
|
|
29975
30021
|
// src/commands/_shared/compose-scenes-skills.ts
|
|
29976
30022
|
var compose_scenes_skills_exports = {};
|
|
29977
30023
|
__export(compose_scenes_skills_exports, {
|
|
29978
30024
|
ComposeBeatError: () => ComposeBeatError,
|
|
30025
|
+
TRANSCRIPT_PROMPT_MAX_WORDS: () => TRANSCRIPT_PROMPT_MAX_WORDS,
|
|
29979
30026
|
buildSystemPrompt: () => buildSystemPrompt,
|
|
29980
30027
|
buildUserPrompt: () => buildUserPrompt,
|
|
29981
30028
|
composeBeatHtml: () => composeBeatHtml,
|
|
@@ -29985,15 +30032,16 @@ __export(compose_scenes_skills_exports, {
|
|
|
29985
30032
|
executeComposeScenesWithSkills: () => executeComposeScenesWithSkills,
|
|
29986
30033
|
extractHtml: () => extractHtml,
|
|
29987
30034
|
formatLintFeedback: () => formatLintFeedback,
|
|
30035
|
+
formatTranscriptSection: () => formatTranscriptSection,
|
|
29988
30036
|
lintBeatHtml: () => lintBeatHtml
|
|
29989
30037
|
});
|
|
29990
30038
|
import Anthropic from "@anthropic-ai/sdk";
|
|
29991
30039
|
import OpenAI from "openai";
|
|
29992
30040
|
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
29993
30041
|
import { createHash as createHash2 } from "node:crypto";
|
|
29994
|
-
import { existsSync as
|
|
29995
|
-
import { mkdir as mkdir5, readFile as
|
|
29996
|
-
import { dirname as dirname3, join as
|
|
30042
|
+
import { existsSync as existsSync8, mkdirSync } from "node:fs";
|
|
30043
|
+
import { mkdir as mkdir5, readFile as readFile9, writeFile as writeFile8 } from "node:fs/promises";
|
|
30044
|
+
import { dirname as dirname3, join as join7, resolve as resolve11 } from "node:path";
|
|
29997
30045
|
import { runHyperframeLint as runHyperframeLint2 } from "@hyperframes/producer";
|
|
29998
30046
|
function buildSystemPrompt(ctx) {
|
|
29999
30047
|
return `You are a Hyperframes composition author. The skill content below
|
|
@@ -30027,6 +30075,46 @@ function formatBeatCues(cues) {
|
|
|
30027
30075
|
}
|
|
30028
30076
|
return lines.join("\n") + "\n";
|
|
30029
30077
|
}
|
|
30078
|
+
function formatTranscriptSection(transcript, maxWords = TRANSCRIPT_PROMPT_MAX_WORDS) {
|
|
30079
|
+
if (!transcript || transcript.length === 0) return "";
|
|
30080
|
+
const round = (n) => Number(Math.max(0, n).toFixed(2));
|
|
30081
|
+
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.";
|
|
30082
|
+
if (transcript.length <= maxWords) {
|
|
30083
|
+
const pairs = transcript.map((w) => `[${round(w.start)}, ${JSON.stringify(w.text)}]`).join(", ");
|
|
30084
|
+
return `
|
|
30085
|
+
|
|
30086
|
+
=== Narration word timings (seconds) ===
|
|
30087
|
+
|
|
30088
|
+
Whisper word-level start times for this beat's narration. If you author captions
|
|
30089
|
+
or kinetic typography, reveal each word at its start time with absolute-time
|
|
30090
|
+
GSAP tweens, e.g.
|
|
30091
|
+
\`tl.fromTo('.caption .word[data-i="0"]', { opacity: 0, y: 10 }, { opacity: 1, y: 0, duration: 0.18 }, ${round(
|
|
30092
|
+
transcript[0].start
|
|
30093
|
+
)});\`
|
|
30094
|
+
${noAudioRule}
|
|
30095
|
+
|
|
30096
|
+
word starts (word-level): ${pairs}`;
|
|
30097
|
+
}
|
|
30098
|
+
const phraseCount = Math.max(1, Math.floor(maxWords / 2));
|
|
30099
|
+
const chunkSize = Math.ceil(transcript.length / phraseCount);
|
|
30100
|
+
const anchors = [];
|
|
30101
|
+
for (let i = 0; i < transcript.length; i += chunkSize) {
|
|
30102
|
+
const chunk = transcript.slice(i, i + chunkSize);
|
|
30103
|
+
const text = chunk.map((w) => w.text).join(" ");
|
|
30104
|
+
anchors.push(`[${round(chunk[0].start)}, ${JSON.stringify(text)}]`);
|
|
30105
|
+
}
|
|
30106
|
+
return `
|
|
30107
|
+
|
|
30108
|
+
=== Narration timings (approximate, phrase-level) ===
|
|
30109
|
+
|
|
30110
|
+
This beat's narration is long (${transcript.length} words), so timings are
|
|
30111
|
+
grouped into phrase-level anchors (start time of each phrase). Use them to PACE
|
|
30112
|
+
caption / kinetic-type reveals across the beat \u2014 they are approximate, not
|
|
30113
|
+
per-word truth.
|
|
30114
|
+
${noAudioRule}
|
|
30115
|
+
|
|
30116
|
+
phrase starts (approximate): ${anchors.join(", ")}`;
|
|
30117
|
+
}
|
|
30030
30118
|
function buildUserPrompt(ctx) {
|
|
30031
30119
|
const compositionId = `scene-${ctx.beat.id}`;
|
|
30032
30120
|
const durLiteral = ctx.finalDurationSec !== void 0 ? String(ctx.finalDurationSec) : void 0;
|
|
@@ -30189,7 +30277,7 @@ ${ctx.storyboardGlobal || "(no global direction)"}
|
|
|
30189
30277
|
|
|
30190
30278
|
## ${ctx.beat.heading}
|
|
30191
30279
|
${formatBeatCues(ctx.beat.cues)}${cueDurationNote}
|
|
30192
|
-
${ctx.beat.body}
|
|
30280
|
+
${ctx.beat.body}${formatTranscriptSection(ctx.transcript)}
|
|
30193
30281
|
|
|
30194
30282
|
=== Output format ===
|
|
30195
30283
|
|
|
@@ -30209,7 +30297,7 @@ function computeCacheKey(parts) {
|
|
|
30209
30297
|
return createHash2("sha256").update(parts.provider).update("\u241E").update(parts.model).update("\u241E").update(parts.systemPrompt).update("\u241E").update(parts.userPrompt).digest("hex");
|
|
30210
30298
|
}
|
|
30211
30299
|
function defaultCacheDir() {
|
|
30212
|
-
return
|
|
30300
|
+
return join7(USER_CACHE_DIR, "compose-scenes");
|
|
30213
30301
|
}
|
|
30214
30302
|
function extractHtml(responseText) {
|
|
30215
30303
|
const fenced = responseText.match(/```html\s*\n([\s\S]*?)\n```/);
|
|
@@ -30233,9 +30321,9 @@ async function composeBeatHtml(ctx, overrides) {
|
|
|
30233
30321
|
model: settings.model
|
|
30234
30322
|
});
|
|
30235
30323
|
const cacheDir = ctx.cacheDir ?? defaultCacheDir();
|
|
30236
|
-
const cachePath =
|
|
30237
|
-
if (
|
|
30238
|
-
const html2 = await
|
|
30324
|
+
const cachePath = join7(cacheDir, `${cacheKey}.html`);
|
|
30325
|
+
if (existsSync8(cachePath)) {
|
|
30326
|
+
const html2 = await readFile9(cachePath, "utf-8");
|
|
30239
30327
|
return { html: html2, cached: true, cacheKey, model: settings.model, provider: ctx.provider };
|
|
30240
30328
|
}
|
|
30241
30329
|
if (!ctx.apiKey) {
|
|
@@ -30371,14 +30459,14 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
30371
30459
|
const projectRoot = params.project ? resolve11(outputDir, params.project) : resolve11(outputDir);
|
|
30372
30460
|
const designPath = resolve11(projectRoot, params.design ?? "DESIGN.md");
|
|
30373
30461
|
const storyboardPath2 = resolve11(projectRoot, params.storyboard ?? "STORYBOARD.md");
|
|
30374
|
-
if (!
|
|
30462
|
+
if (!existsSync8(designPath)) {
|
|
30375
30463
|
return { success: false, error: `DESIGN.md not found at ${designPath}` };
|
|
30376
30464
|
}
|
|
30377
|
-
if (!
|
|
30465
|
+
if (!existsSync8(storyboardPath2)) {
|
|
30378
30466
|
return { success: false, error: `STORYBOARD.md not found at ${storyboardPath2}` };
|
|
30379
30467
|
}
|
|
30380
|
-
const designMd = await
|
|
30381
|
-
const storyboardMd = await
|
|
30468
|
+
const designMd = await readFile9(designPath, "utf-8");
|
|
30469
|
+
const storyboardMd = await readFile9(storyboardPath2, "utf-8");
|
|
30382
30470
|
const { global: storyboardGlobal, beats } = parseStoryboard(storyboardMd);
|
|
30383
30471
|
if (beats.length === 0) {
|
|
30384
30472
|
return {
|
|
@@ -30398,7 +30486,7 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
30398
30486
|
provider = params.composer ?? "claude";
|
|
30399
30487
|
apiKey = "";
|
|
30400
30488
|
}
|
|
30401
|
-
const compositionsDir =
|
|
30489
|
+
const compositionsDir = join7(projectRoot, "compositions");
|
|
30402
30490
|
await mkdir5(compositionsDir, { recursive: true });
|
|
30403
30491
|
let finalDurations = /* @__PURE__ */ new Map();
|
|
30404
30492
|
try {
|
|
@@ -30412,6 +30500,7 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
30412
30500
|
async (beat, beatIndex) => {
|
|
30413
30501
|
onProgress({ type: "beat-start", beatId: beat.id, beatIndex, totalBeats });
|
|
30414
30502
|
try {
|
|
30503
|
+
const transcript = await readBeatTranscript(projectRoot, beat.id);
|
|
30415
30504
|
const result = await composeBeatWithRetry(
|
|
30416
30505
|
{
|
|
30417
30506
|
beat,
|
|
@@ -30422,11 +30511,12 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
30422
30511
|
apiKey,
|
|
30423
30512
|
effort: params.effort,
|
|
30424
30513
|
cacheDir: params.cacheDir,
|
|
30425
|
-
finalDurationSec: finalDurations.get(beat.id)
|
|
30514
|
+
finalDurationSec: finalDurations.get(beat.id),
|
|
30515
|
+
transcript
|
|
30426
30516
|
},
|
|
30427
30517
|
overrides
|
|
30428
30518
|
);
|
|
30429
|
-
const compositionPath =
|
|
30519
|
+
const compositionPath = join7(compositionsDir, `scene-${beat.id}.html`);
|
|
30430
30520
|
await mkdir5(dirname3(compositionPath), { recursive: true });
|
|
30431
30521
|
await writeFile8(compositionPath, result.html, "utf-8");
|
|
30432
30522
|
if (result.cached) {
|
|
@@ -30505,7 +30595,7 @@ ${failures.map((f) => ` - ${f.beatId}: ${f.error}`).join("\n")}`,
|
|
|
30505
30595
|
data: aggregateData
|
|
30506
30596
|
};
|
|
30507
30597
|
}
|
|
30508
|
-
var MODEL_SETTINGS_BY_PROVIDER, ComposeBeatError, defaultCallLLM, CONFIG_PROVIDER_BY_COMPOSER;
|
|
30598
|
+
var MODEL_SETTINGS_BY_PROVIDER, TRANSCRIPT_PROMPT_MAX_WORDS, ComposeBeatError, defaultCallLLM, CONFIG_PROVIDER_BY_COMPOSER;
|
|
30509
30599
|
var init_compose_scenes_skills = __esm({
|
|
30510
30600
|
"src/commands/_shared/compose-scenes-skills.ts"() {
|
|
30511
30601
|
"use strict";
|
|
@@ -30517,6 +30607,7 @@ var init_compose_scenes_skills = __esm({
|
|
|
30517
30607
|
init_root_sync();
|
|
30518
30608
|
init_composer_resolve();
|
|
30519
30609
|
init_config();
|
|
30610
|
+
init_transcribe_narration();
|
|
30520
30611
|
MODEL_SETTINGS_BY_PROVIDER = {
|
|
30521
30612
|
// Anthropic Claude Sonnet 4.6 — v0.69 baseline. ~9 s/beat, $3/$15 per MTok.
|
|
30522
30613
|
claude: {
|
|
@@ -30540,6 +30631,7 @@ var init_compose_scenes_skills = __esm({
|
|
|
30540
30631
|
high: { model: GEMINI_DEFAULT_TEXT_MODEL, maxTokens: 8e3, costPerMTokIn: 1.5, costPerMTokOut: 9 }
|
|
30541
30632
|
}
|
|
30542
30633
|
};
|
|
30634
|
+
TRANSCRIPT_PROMPT_MAX_WORDS = 120;
|
|
30543
30635
|
ComposeBeatError = class extends Error {
|
|
30544
30636
|
constructor(code, message, cause) {
|
|
30545
30637
|
super(message);
|
|
@@ -30625,22 +30717,22 @@ var init_compose_scenes_skills = __esm({
|
|
|
30625
30717
|
});
|
|
30626
30718
|
|
|
30627
30719
|
// src/commands/_shared/compose-prompts.ts
|
|
30628
|
-
import { existsSync as
|
|
30629
|
-
import { readFile as
|
|
30630
|
-
import { join as
|
|
30720
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
30721
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
30722
|
+
import { join as join8, relative as relative4, resolve as resolve12 } from "node:path";
|
|
30631
30723
|
async function getComposePrompts(opts) {
|
|
30632
30724
|
const projectDir = resolve12(opts.projectDir);
|
|
30633
|
-
const designPath =
|
|
30634
|
-
const storyboardPath2 =
|
|
30635
|
-
const skillPath =
|
|
30636
|
-
const compositionsDir =
|
|
30725
|
+
const designPath = join8(projectDir, "DESIGN.md");
|
|
30726
|
+
const storyboardPath2 = join8(projectDir, "STORYBOARD.md");
|
|
30727
|
+
const skillPath = join8(projectDir, "SKILL.md");
|
|
30728
|
+
const compositionsDir = join8(projectDir, "compositions");
|
|
30637
30729
|
const warnings = [];
|
|
30638
30730
|
const baseError = (msg) => ({
|
|
30639
30731
|
success: false,
|
|
30640
30732
|
projectDir,
|
|
30641
30733
|
designReference: "DESIGN.md",
|
|
30642
30734
|
storyboardReference: "STORYBOARD.md",
|
|
30643
|
-
skillReference:
|
|
30735
|
+
skillReference: existsSync9(skillPath) ? "SKILL.md" : null,
|
|
30644
30736
|
compositionsDir: "compositions",
|
|
30645
30737
|
beats: [],
|
|
30646
30738
|
instructions: [],
|
|
@@ -30648,18 +30740,18 @@ async function getComposePrompts(opts) {
|
|
|
30648
30740
|
warnings,
|
|
30649
30741
|
error: msg
|
|
30650
30742
|
});
|
|
30651
|
-
if (!
|
|
30743
|
+
if (!existsSync9(designPath)) {
|
|
30652
30744
|
return baseError(`DESIGN.md not found at ${designPath}. Run \`vibe scene init <dir>\` first.`);
|
|
30653
30745
|
}
|
|
30654
|
-
if (!
|
|
30746
|
+
if (!existsSync9(storyboardPath2)) {
|
|
30655
30747
|
return baseError(`STORYBOARD.md not found at ${storyboardPath2}. Run \`vibe scene init <dir>\` to create a starter, or add STORYBOARD.md with per-beat cues.`);
|
|
30656
30748
|
}
|
|
30657
|
-
if (!
|
|
30749
|
+
if (!existsSync9(skillPath)) {
|
|
30658
30750
|
warnings.push(
|
|
30659
30751
|
"SKILL.md not installed \u2014 host agent won't have Hyperframes rules in context. Run `vibe scene install-skill` to install it."
|
|
30660
30752
|
);
|
|
30661
30753
|
}
|
|
30662
|
-
const storyboardMd = await
|
|
30754
|
+
const storyboardMd = await readFile10(storyboardPath2, "utf-8");
|
|
30663
30755
|
const parsed = parseStoryboard(storyboardMd);
|
|
30664
30756
|
if (parsed.beats.length === 0) {
|
|
30665
30757
|
return baseError(`STORYBOARD.md has no \`## Beat \u2026\` headings.`);
|
|
@@ -30681,28 +30773,33 @@ async function getComposePrompts(opts) {
|
|
|
30681
30773
|
finalDurations = await resolveProjectBeatDurations(projectDir);
|
|
30682
30774
|
} catch {
|
|
30683
30775
|
}
|
|
30684
|
-
const result =
|
|
30685
|
-
|
|
30686
|
-
|
|
30687
|
-
|
|
30688
|
-
|
|
30689
|
-
beat
|
|
30690
|
-
|
|
30691
|
-
|
|
30692
|
-
|
|
30693
|
-
|
|
30694
|
-
|
|
30695
|
-
|
|
30696
|
-
|
|
30697
|
-
|
|
30698
|
-
|
|
30699
|
-
|
|
30700
|
-
|
|
30701
|
-
|
|
30702
|
-
|
|
30703
|
-
|
|
30704
|
-
|
|
30705
|
-
|
|
30776
|
+
const result = await Promise.all(
|
|
30777
|
+
beats.map(async (beat) => {
|
|
30778
|
+
const outputPathAbs = join8(compositionsDir, `scene-${beat.id}.html`);
|
|
30779
|
+
const outputPathRel = relative4(projectDir, outputPathAbs);
|
|
30780
|
+
const finalDurationSec = finalDurations.get(beat.id);
|
|
30781
|
+
const transcript = await readBeatTranscript(projectDir, beat.id);
|
|
30782
|
+
const userPrompt = buildUserPrompt({
|
|
30783
|
+
beat,
|
|
30784
|
+
storyboardGlobal: parsed.global,
|
|
30785
|
+
finalDurationSec,
|
|
30786
|
+
transcript
|
|
30787
|
+
});
|
|
30788
|
+
return {
|
|
30789
|
+
id: beat.id,
|
|
30790
|
+
heading: beat.heading,
|
|
30791
|
+
outputPath: outputPathRel,
|
|
30792
|
+
duration: beat.duration,
|
|
30793
|
+
finalDurationSec,
|
|
30794
|
+
cues: beat.cues,
|
|
30795
|
+
body: beat.body,
|
|
30796
|
+
userPrompt,
|
|
30797
|
+
transcriptPath: transcript ? beatTranscriptRelPath(beat.id) : void 0,
|
|
30798
|
+
exists: existsSync9(outputPathAbs)
|
|
30799
|
+
};
|
|
30800
|
+
})
|
|
30801
|
+
);
|
|
30802
|
+
const skillRef = existsSync9(skillPath) ? "SKILL.md" : null;
|
|
30706
30803
|
const instructions = buildInstructions({
|
|
30707
30804
|
skillRef,
|
|
30708
30805
|
beatCount: result.length,
|
|
@@ -30750,13 +30847,14 @@ var init_compose_prompts = __esm({
|
|
|
30750
30847
|
init_compose_scenes_skills();
|
|
30751
30848
|
init_root_sync();
|
|
30752
30849
|
init_bundle();
|
|
30850
|
+
init_transcribe_narration();
|
|
30753
30851
|
}
|
|
30754
30852
|
});
|
|
30755
30853
|
|
|
30756
30854
|
// src/commands/_shared/scene-submit.ts
|
|
30757
|
-
import { existsSync as
|
|
30758
|
-
import { mkdir as mkdir6, readFile as
|
|
30759
|
-
import { join as
|
|
30855
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
30856
|
+
import { mkdir as mkdir6, readFile as readFile11, writeFile as writeFile9 } from "node:fs/promises";
|
|
30857
|
+
import { join as join9, resolve as resolve13 } from "node:path";
|
|
30760
30858
|
async function executeSceneSubmit(opts) {
|
|
30761
30859
|
const projectDir = resolve13(opts.projectDir);
|
|
30762
30860
|
const beatId = opts.beatId.trim();
|
|
@@ -30770,15 +30868,15 @@ async function executeSceneSubmit(opts) {
|
|
|
30770
30868
|
warnings: [],
|
|
30771
30869
|
retryWith: []
|
|
30772
30870
|
};
|
|
30773
|
-
const storyboardPath2 =
|
|
30774
|
-
if (!
|
|
30871
|
+
const storyboardPath2 = join9(projectDir, "STORYBOARD.md");
|
|
30872
|
+
if (!existsSync10(storyboardPath2)) {
|
|
30775
30873
|
return {
|
|
30776
30874
|
...base,
|
|
30777
30875
|
success: false,
|
|
30778
30876
|
error: `STORYBOARD.md not found at ${storyboardPath2}. Run \`vibe init <dir>\` first.`
|
|
30779
30877
|
};
|
|
30780
30878
|
}
|
|
30781
|
-
const parsed = parseStoryboard(await
|
|
30879
|
+
const parsed = parseStoryboard(await readFile11(storyboardPath2, "utf-8"));
|
|
30782
30880
|
const beat = parsed.beats.find((b) => b.id === beatId);
|
|
30783
30881
|
if (!beat) {
|
|
30784
30882
|
const available = parsed.beats.map((b) => b.id).join(", ") || "(none)";
|
|
@@ -30848,8 +30946,8 @@ async function executeSceneSubmit(opts) {
|
|
|
30848
30946
|
if (opts.validateOnly) {
|
|
30849
30947
|
return { ...base, success: true };
|
|
30850
30948
|
}
|
|
30851
|
-
const absPath =
|
|
30852
|
-
await mkdir6(
|
|
30949
|
+
const absPath = join9(projectDir, scenePath);
|
|
30950
|
+
await mkdir6(join9(projectDir, "compositions"), { recursive: true });
|
|
30853
30951
|
await writeFile9(absPath, html.endsWith("\n") ? html : `${html}
|
|
30854
30952
|
`, "utf-8");
|
|
30855
30953
|
return {
|
|
@@ -30873,9 +30971,9 @@ var init_scene_submit = __esm({
|
|
|
30873
30971
|
});
|
|
30874
30972
|
|
|
30875
30973
|
// src/commands/_shared/sub-comp-duration-sync.ts
|
|
30876
|
-
import { existsSync as
|
|
30877
|
-
import { readFile as
|
|
30878
|
-
import { join as
|
|
30974
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
30975
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
30976
|
+
import { join as join10, resolve as resolve14 } from "node:path";
|
|
30879
30977
|
function syncSubCompositionDurationHtml(html, beatId, newDurationSec, fileRel) {
|
|
30880
30978
|
const compositionId = `scene-${beatId}`;
|
|
30881
30979
|
const file = fileRel ?? `compositions/${compositionId}.html`;
|
|
@@ -30967,15 +31065,15 @@ async function createSubCompDurationSyncPlans(opts) {
|
|
|
30967
31065
|
const results = [];
|
|
30968
31066
|
for (const beat of opts.beats) {
|
|
30969
31067
|
const fileRel = `compositions/scene-${beat.id}.html`;
|
|
30970
|
-
const filePath =
|
|
30971
|
-
if (!
|
|
31068
|
+
const filePath = join10(projectDir, fileRel);
|
|
31069
|
+
if (!existsSync11(filePath)) continue;
|
|
30972
31070
|
const newDurationSec = await resolveSyncedBeatDuration({
|
|
30973
31071
|
projectDir,
|
|
30974
31072
|
beatDuration: beat.duration,
|
|
30975
31073
|
narrationPath: beat.narrationPath,
|
|
30976
31074
|
sceneDurationSec: beat.sceneDurationSec
|
|
30977
31075
|
});
|
|
30978
|
-
const html = await
|
|
31076
|
+
const html = await readFile12(filePath, "utf-8");
|
|
30979
31077
|
const outcome = syncSubCompositionDurationHtml(html, beat.id, newDurationSec, fileRel);
|
|
30980
31078
|
results.push({ ...outcome, file: fileRel, beatId: beat.id });
|
|
30981
31079
|
}
|
|
@@ -31000,7 +31098,7 @@ var init_sub_comp_duration_sync = __esm({
|
|
|
31000
31098
|
|
|
31001
31099
|
// src/commands/_shared/review-report.ts
|
|
31002
31100
|
import { writeFile as writeFile10 } from "node:fs/promises";
|
|
31003
|
-
import { join as
|
|
31101
|
+
import { join as join11, resolve as resolve15 } from "node:path";
|
|
31004
31102
|
function statusFromIssues(issues) {
|
|
31005
31103
|
if (issues.some((issue) => issue.severity === "error")) return "fail";
|
|
31006
31104
|
if (issues.some((issue) => issue.severity === "warning")) return "warn";
|
|
@@ -31178,7 +31276,7 @@ function buildReviewReport(opts) {
|
|
|
31178
31276
|
});
|
|
31179
31277
|
}
|
|
31180
31278
|
function defaultReviewReportPath(projectDir) {
|
|
31181
|
-
return
|
|
31279
|
+
return join11(resolve15(projectDir), "review-report.json");
|
|
31182
31280
|
}
|
|
31183
31281
|
async function writeReviewReport(path2, report) {
|
|
31184
31282
|
await writeFile10(path2, JSON.stringify(report, null, 2) + "\n", "utf-8");
|
|
@@ -31493,7 +31591,7 @@ var init_review_report = __esm({
|
|
|
31493
31591
|
});
|
|
31494
31592
|
|
|
31495
31593
|
// src/commands/_shared/scene-repair.ts
|
|
31496
|
-
import { readFile as
|
|
31594
|
+
import { readFile as readFile13, writeFile as writeFile11 } from "node:fs/promises";
|
|
31497
31595
|
import { relative as relative5, resolve as resolve16 } from "node:path";
|
|
31498
31596
|
import { runHyperframeLint as runHyperframeLint3 } from "@hyperframes/producer";
|
|
31499
31597
|
async function executeSceneRepair(opts) {
|
|
@@ -31539,7 +31637,7 @@ async function executeSceneRepair(opts) {
|
|
|
31539
31637
|
}
|
|
31540
31638
|
for (const target of targets) {
|
|
31541
31639
|
const rel = relative5(projectDir, target.abs) || target.abs;
|
|
31542
|
-
const html = await
|
|
31640
|
+
const html = await readFile13(target.abs, "utf-8");
|
|
31543
31641
|
const findings = lintHtml(html, rel, target.isSub);
|
|
31544
31642
|
const { html: nextHtml, fixedCodes } = applyMechanicalFixes(html, findings);
|
|
31545
31643
|
if (fixedCodes.length > 0) {
|
|
@@ -31912,8 +32010,8 @@ var init_storyboard_edit = __esm({
|
|
|
31912
32010
|
|
|
31913
32011
|
// src/commands/scene.ts
|
|
31914
32012
|
import { resolve as resolve17, relative as relative6, dirname as dirname4 } from "node:path";
|
|
31915
|
-
import { mkdir as mkdir7, readFile as
|
|
31916
|
-
import { existsSync as
|
|
32013
|
+
import { mkdir as mkdir7, readFile as readFile14, writeFile as writeFile12, access as access5, copyFile } from "node:fs/promises";
|
|
32014
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
31917
32015
|
function validateDuration(value) {
|
|
31918
32016
|
const n = parseFloat(value);
|
|
31919
32017
|
if (!Number.isFinite(n) || n <= 0 || n > 3600) {
|
|
@@ -31977,8 +32075,8 @@ function readClipStart(rootHtml, beatId) {
|
|
|
31977
32075
|
async function resolveNarrationText(value) {
|
|
31978
32076
|
if (!value) return void 0;
|
|
31979
32077
|
const looksLikePath = /\.[a-z]{2,4}$/i.test(value) || value.includes("/") || value.includes("\\");
|
|
31980
|
-
if (looksLikePath &&
|
|
31981
|
-
return (await
|
|
32078
|
+
if (looksLikePath && existsSync12(value)) {
|
|
32079
|
+
return (await readFile14(value, "utf-8")).trim();
|
|
31982
32080
|
}
|
|
31983
32081
|
return value.trim();
|
|
31984
32082
|
}
|
|
@@ -32017,7 +32115,7 @@ async function executeSceneAdd(opts) {
|
|
|
32017
32115
|
if (!opts.force && await pathExists2(scenePath)) {
|
|
32018
32116
|
return errResult(`Scene already exists: ${sceneRel}. Re-run with --force to overwrite.`);
|
|
32019
32117
|
}
|
|
32020
|
-
const rootHtmlBefore = await
|
|
32118
|
+
const rootHtmlBefore = await readFile14(rootPath, "utf-8");
|
|
32021
32119
|
let dims = readRootDims(rootHtmlBefore);
|
|
32022
32120
|
if (!dims) {
|
|
32023
32121
|
const cfg2 = await loadVibeProjectConfig(projectDir);
|
|
@@ -32093,31 +32191,20 @@ async function executeSceneAdd(opts) {
|
|
|
32093
32191
|
);
|
|
32094
32192
|
} else {
|
|
32095
32193
|
opts.onProgress?.("Transcribing narration (Whisper word-level)...");
|
|
32096
|
-
|
|
32097
|
-
|
|
32098
|
-
|
|
32099
|
-
|
|
32100
|
-
|
|
32101
|
-
|
|
32102
|
-
|
|
32103
|
-
|
|
32104
|
-
|
|
32105
|
-
|
|
32106
|
-
|
|
32107
|
-
|
|
32108
|
-
|
|
32109
|
-
|
|
32110
|
-
transcriptWords = transcript.words.map((w) => ({
|
|
32111
|
-
text: w.text,
|
|
32112
|
-
start: w.start,
|
|
32113
|
-
end: w.end
|
|
32114
|
-
}));
|
|
32115
|
-
} else if (transcript.status === "failed") {
|
|
32116
|
-
opts.onProgress?.(`Transcribe failed: ${transcript.error ?? "unknown error"}`);
|
|
32117
|
-
}
|
|
32118
|
-
} catch (error) {
|
|
32119
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
32120
|
-
opts.onProgress?.(`Transcribe failed: ${msg}`);
|
|
32194
|
+
const words = await transcribeNarrationWords(audioAbsPath, {
|
|
32195
|
+
apiKey: whisperKey,
|
|
32196
|
+
language: opts.transcribeLanguage
|
|
32197
|
+
});
|
|
32198
|
+
if (words.length > 0) {
|
|
32199
|
+
transcriptRelPath = beatTranscriptRelPath(id);
|
|
32200
|
+
const transcriptAbs = resolve17(projectDir, transcriptRelPath);
|
|
32201
|
+
await writeFile12(transcriptAbs, JSON.stringify(words, null, 2), "utf-8");
|
|
32202
|
+
transcriptWordCount = words.length;
|
|
32203
|
+
transcriptWords = words;
|
|
32204
|
+
} else {
|
|
32205
|
+
opts.onProgress?.(
|
|
32206
|
+
"Transcribe produced no word timings \u2014 narration plays without word-sync"
|
|
32207
|
+
);
|
|
32121
32208
|
}
|
|
32122
32209
|
}
|
|
32123
32210
|
}
|
|
@@ -32226,7 +32313,7 @@ async function executeSceneAdd(opts) {
|
|
|
32226
32313
|
const shouldSyncStoryboard = opts.syncStoryboard !== false && await pathExists2(storyboardAbsPath);
|
|
32227
32314
|
if (shouldSyncStoryboard) {
|
|
32228
32315
|
opts.onProgress?.("Syncing STORYBOARD.md...");
|
|
32229
|
-
const before = await
|
|
32316
|
+
const before = await readFile14(storyboardAbsPath, "utf-8");
|
|
32230
32317
|
const synced = upsertStoryboardBeat(before, {
|
|
32231
32318
|
beatId: id,
|
|
32232
32319
|
title: opts.headline,
|
|
@@ -32268,7 +32355,7 @@ async function executeSceneAdd(opts) {
|
|
|
32268
32355
|
});
|
|
32269
32356
|
const blockingIssue = syncPlan.issues.find((issue) => issue.severity === "error");
|
|
32270
32357
|
if (blockingIssue) return errResult(blockingIssue.message);
|
|
32271
|
-
const rootHtmlAfterSync = await
|
|
32358
|
+
const rootHtmlAfterSync = await readFile14(rootPath, "utf-8");
|
|
32272
32359
|
start = readClipStart(rootHtmlAfterSync, id) ?? 0;
|
|
32273
32360
|
} else {
|
|
32274
32361
|
start = nextSceneStart(rootHtmlBefore, SCENE_OVERLAP_SECONDS);
|
|
@@ -32300,7 +32387,7 @@ async function resolveSceneRepairTarget(rootArg = "index.html", projectArg = "."
|
|
|
32300
32387
|
const root = rootArg || "index.html";
|
|
32301
32388
|
const project = projectArg || ".";
|
|
32302
32389
|
const defaultProject = project === ".";
|
|
32303
|
-
if (defaultProject && root !== "index.html" && !root.toLowerCase().endsWith(".html") &&
|
|
32390
|
+
if (defaultProject && root !== "index.html" && !root.toLowerCase().endsWith(".html") && existsSync12(resolve17(root, "index.html"))) {
|
|
32304
32391
|
return { projectDir: resolve17(root), rootRel: "index.html" };
|
|
32305
32392
|
}
|
|
32306
32393
|
return { projectDir: resolve17(project), rootRel: root };
|
|
@@ -32331,6 +32418,7 @@ var init_scene = __esm({
|
|
|
32331
32418
|
init_agent_host_detect();
|
|
32332
32419
|
init_install_skill();
|
|
32333
32420
|
init_compose_prompts();
|
|
32421
|
+
init_transcribe_narration();
|
|
32334
32422
|
init_scene_submit();
|
|
32335
32423
|
init_scene_repair();
|
|
32336
32424
|
init_root_sync();
|
|
@@ -32500,7 +32588,7 @@ Run 'vibe schema scene.<command>' for structured parameter info.`
|
|
|
32500
32588
|
}
|
|
32501
32589
|
html = Buffer.concat(chunks).toString("utf-8");
|
|
32502
32590
|
} else if (options.file) {
|
|
32503
|
-
html = await
|
|
32591
|
+
html = await readFile14(resolve17(String(options.file)), "utf-8");
|
|
32504
32592
|
} else {
|
|
32505
32593
|
exitWithError(
|
|
32506
32594
|
usageError(
|
|
@@ -32954,8 +33042,8 @@ __export(remotion_exports, {
|
|
|
32954
33042
|
wrapComponentWithVideo: () => wrapComponentWithVideo
|
|
32955
33043
|
});
|
|
32956
33044
|
import { writeFile as writeFile14, mkdir as mkdir8, rm, copyFile as copyFile2 } from "node:fs/promises";
|
|
32957
|
-
import { existsSync as
|
|
32958
|
-
import { join as
|
|
33045
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
33046
|
+
import { join as join12 } from "node:path";
|
|
32959
33047
|
import { tmpdir } from "node:os";
|
|
32960
33048
|
import { createHash as createHash3 } from "node:crypto";
|
|
32961
33049
|
async function ensureRemotionInstalled() {
|
|
@@ -32974,7 +33062,7 @@ async function ensureRemotionInstalled() {
|
|
|
32974
33062
|
}
|
|
32975
33063
|
}
|
|
32976
33064
|
async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
32977
|
-
const dir =
|
|
33065
|
+
const dir = join12(tmpdir(), `vibe_motion_${Date.now()}`);
|
|
32978
33066
|
await mkdir8(dir, { recursive: true });
|
|
32979
33067
|
const deps = {
|
|
32980
33068
|
remotion: REMOTION_VERSION,
|
|
@@ -32993,7 +33081,7 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
|
32993
33081
|
dependencies: deps
|
|
32994
33082
|
};
|
|
32995
33083
|
const packageJsonStr = JSON.stringify(packageJson, null, 2);
|
|
32996
|
-
await writeFile14(
|
|
33084
|
+
await writeFile14(join12(dir, "package.json"), packageJsonStr);
|
|
32997
33085
|
const tsconfig = {
|
|
32998
33086
|
compilerOptions: {
|
|
32999
33087
|
target: "ES2020",
|
|
@@ -33005,8 +33093,8 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
|
33005
33093
|
skipLibCheck: true
|
|
33006
33094
|
}
|
|
33007
33095
|
};
|
|
33008
|
-
await writeFile14(
|
|
33009
|
-
await writeFile14(
|
|
33096
|
+
await writeFile14(join12(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
|
|
33097
|
+
await writeFile14(join12(dir, "Component.tsx"), componentCode);
|
|
33010
33098
|
const rootCode = `import { registerRoot, Composition } from "remotion";
|
|
33011
33099
|
import { ${componentName} } from "./Component";
|
|
33012
33100
|
|
|
@@ -33025,14 +33113,14 @@ const Root = () => {
|
|
|
33025
33113
|
|
|
33026
33114
|
registerRoot(Root);
|
|
33027
33115
|
`;
|
|
33028
|
-
await writeFile14(
|
|
33116
|
+
await writeFile14(join12(dir, "Root.tsx"), rootCode);
|
|
33029
33117
|
const depsHash = createHash3("md5").update(packageJsonStr).digest("hex").slice(0, 12);
|
|
33030
|
-
const cacheMarker =
|
|
33031
|
-
const cachedModules =
|
|
33032
|
-
if (
|
|
33118
|
+
const cacheMarker = join12(REMOTION_CACHE_DIR, `.deps-${depsHash}`);
|
|
33119
|
+
const cachedModules = join12(REMOTION_CACHE_DIR, "node_modules");
|
|
33120
|
+
if (existsSync14(cacheMarker) && existsSync14(cachedModules)) {
|
|
33033
33121
|
const { symlink } = await import("node:fs/promises");
|
|
33034
33122
|
try {
|
|
33035
|
-
await symlink(cachedModules,
|
|
33123
|
+
await symlink(cachedModules, join12(dir, "node_modules"), "dir");
|
|
33036
33124
|
} catch {
|
|
33037
33125
|
await this_npmInstall(dir);
|
|
33038
33126
|
}
|
|
@@ -33040,12 +33128,12 @@ registerRoot(Root);
|
|
|
33040
33128
|
await this_npmInstall(dir);
|
|
33041
33129
|
try {
|
|
33042
33130
|
await mkdir8(REMOTION_CACHE_DIR, { recursive: true });
|
|
33043
|
-
await writeFile14(
|
|
33044
|
-
if (
|
|
33131
|
+
await writeFile14(join12(REMOTION_CACHE_DIR, "package.json"), packageJsonStr);
|
|
33132
|
+
if (existsSync14(join12(dir, "node_modules"))) {
|
|
33045
33133
|
const { rename: rename5, symlink } = await import("node:fs/promises");
|
|
33046
|
-
await rename5(
|
|
33134
|
+
await rename5(join12(dir, "node_modules"), cachedModules).catch(() => {
|
|
33047
33135
|
});
|
|
33048
|
-
await symlink(cachedModules,
|
|
33136
|
+
await symlink(cachedModules, join12(dir, "node_modules"), "dir").catch(() => {
|
|
33049
33137
|
});
|
|
33050
33138
|
await writeFile14(cacheMarker, depsHash);
|
|
33051
33139
|
}
|
|
@@ -33082,7 +33170,7 @@ async function renderMotion(options) {
|
|
|
33082
33170
|
}
|
|
33083
33171
|
);
|
|
33084
33172
|
try {
|
|
33085
|
-
const entryPoint =
|
|
33173
|
+
const entryPoint = join12(dir, "Root.tsx");
|
|
33086
33174
|
const { execFile: execFile3 } = await import("node:child_process");
|
|
33087
33175
|
const { promisify: promisify3 } = await import("node:util");
|
|
33088
33176
|
const execFileAsync3 = promisify3(execFile3);
|
|
@@ -33215,10 +33303,10 @@ async function renderWithEmbeddedImage(options) {
|
|
|
33215
33303
|
}
|
|
33216
33304
|
);
|
|
33217
33305
|
try {
|
|
33218
|
-
const publicDir =
|
|
33306
|
+
const publicDir = join12(dir, "public");
|
|
33219
33307
|
await mkdir8(publicDir, { recursive: true });
|
|
33220
|
-
await copyFile2(options.imagePath,
|
|
33221
|
-
const entryPoint =
|
|
33308
|
+
await copyFile2(options.imagePath, join12(publicDir, options.imageFileName));
|
|
33309
|
+
const entryPoint = join12(dir, "Root.tsx");
|
|
33222
33310
|
const mp4Out = options.outputPath.replace(/\.\w+$/, ".mp4");
|
|
33223
33311
|
const { execFile: execFileImg } = await import("node:child_process");
|
|
33224
33312
|
const { promisify: promisifyImg } = await import("node:util");
|
|
@@ -33279,10 +33367,10 @@ async function renderWithEmbeddedVideo(options) {
|
|
|
33279
33367
|
}
|
|
33280
33368
|
);
|
|
33281
33369
|
try {
|
|
33282
|
-
const publicDir =
|
|
33370
|
+
const publicDir = join12(dir, "public");
|
|
33283
33371
|
await mkdir8(publicDir, { recursive: true });
|
|
33284
|
-
await copyFile2(options.videoPath,
|
|
33285
|
-
const entryPoint =
|
|
33372
|
+
await copyFile2(options.videoPath, join12(publicDir, options.videoFileName));
|
|
33373
|
+
const entryPoint = join12(dir, "Root.tsx");
|
|
33286
33374
|
const mp4VideoOnly = options.outputPath.replace(/\.\w+$/, "_video_only.mp4");
|
|
33287
33375
|
const { execFile: execFileVid } = await import("node:child_process");
|
|
33288
33376
|
const { promisify: promisifyVid } = await import("node:util");
|
|
@@ -33698,7 +33786,7 @@ var init_remotion = __esm({
|
|
|
33698
33786
|
"use strict";
|
|
33699
33787
|
init_exec_safe();
|
|
33700
33788
|
REMOTION_VERSION = "4.0.447";
|
|
33701
|
-
REMOTION_CACHE_DIR =
|
|
33789
|
+
REMOTION_CACHE_DIR = join12(tmpdir(), "vibe_remotion_cache");
|
|
33702
33790
|
}
|
|
33703
33791
|
});
|
|
33704
33792
|
|
|
@@ -33709,8 +33797,8 @@ __export(ai_motion_exports, {
|
|
|
33709
33797
|
registerMotionCommand: () => registerMotionCommand
|
|
33710
33798
|
});
|
|
33711
33799
|
import { resolve as resolve19 } from "node:path";
|
|
33712
|
-
import { existsSync as
|
|
33713
|
-
import { readFile as
|
|
33800
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
33801
|
+
import { readFile as readFile16, writeFile as writeFile15 } from "node:fs/promises";
|
|
33714
33802
|
async function getOptionalGoogleApiKey() {
|
|
33715
33803
|
const configKey = await getApiKeyFromConfig("google");
|
|
33716
33804
|
if (configKey) return configKey;
|
|
@@ -33757,7 +33845,7 @@ async function executeMotion(options) {
|
|
|
33757
33845
|
};
|
|
33758
33846
|
}
|
|
33759
33847
|
const imagePath = resolve19(process.cwd(), options.image);
|
|
33760
|
-
const imageBuffer = await
|
|
33848
|
+
const imageBuffer = await readFile16(imagePath);
|
|
33761
33849
|
const gemini = new GeminiProvider();
|
|
33762
33850
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
33763
33851
|
const analysisResult = await gemini.analyzeImage(
|
|
@@ -33794,7 +33882,7 @@ Use this image analysis to inform the color palette, typography placement, and o
|
|
|
33794
33882
|
}
|
|
33795
33883
|
if (geminiApiKey) {
|
|
33796
33884
|
const videoPath = resolve19(process.cwd(), options.video);
|
|
33797
|
-
const videoBuffer = await
|
|
33885
|
+
const videoBuffer = await readFile16(videoPath);
|
|
33798
33886
|
const gemini = new GeminiProvider();
|
|
33799
33887
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
33800
33888
|
const analysisPrompt = options.understandingPrompt || `Analyze this video for motion graphics composition. Return concise production guidance:
|
|
@@ -33825,10 +33913,10 @@ Use this video understanding to place motion graphics in safe zones, match the c
|
|
|
33825
33913
|
let result;
|
|
33826
33914
|
if (options.fromTsx) {
|
|
33827
33915
|
const tsxPath = resolve19(process.cwd(), options.fromTsx);
|
|
33828
|
-
if (!
|
|
33916
|
+
if (!existsSync15(tsxPath)) {
|
|
33829
33917
|
return { success: false, error: `TSX file not found: ${tsxPath}` };
|
|
33830
33918
|
}
|
|
33831
|
-
const existingCode = await
|
|
33919
|
+
const existingCode = await readFile16(tsxPath, "utf-8");
|
|
33832
33920
|
if (useGemini) {
|
|
33833
33921
|
const gemini = new GeminiProvider();
|
|
33834
33922
|
await gemini.initialize({ apiKey });
|
|
@@ -34605,7 +34693,7 @@ var init_ai_helpers = __esm({
|
|
|
34605
34693
|
|
|
34606
34694
|
// src/commands/generate/storyboard.ts
|
|
34607
34695
|
import { resolve as resolve22 } from "node:path";
|
|
34608
|
-
import { readFile as
|
|
34696
|
+
import { readFile as readFile17, writeFile as writeFile18 } from "node:fs/promises";
|
|
34609
34697
|
async function executeStoryboard(options) {
|
|
34610
34698
|
const { content, duration, creativity = "low", output, apiKey } = options;
|
|
34611
34699
|
try {
|
|
@@ -34653,7 +34741,7 @@ function registerStoryboardCommand(parent) {
|
|
|
34653
34741
|
let textContent = content;
|
|
34654
34742
|
if (options.file) {
|
|
34655
34743
|
const filePath = resolve22(process.cwd(), content);
|
|
34656
|
-
textContent = await
|
|
34744
|
+
textContent = await readFile17(filePath, "utf-8");
|
|
34657
34745
|
}
|
|
34658
34746
|
if (options.dryRun) {
|
|
34659
34747
|
outputSuccess({
|
|
@@ -35725,7 +35813,7 @@ __export(ai_video_exports, {
|
|
|
35725
35813
|
executeVideoGenerate: () => executeVideoGenerate,
|
|
35726
35814
|
executeVideoStatus: () => executeVideoStatus
|
|
35727
35815
|
});
|
|
35728
|
-
import { readFile as
|
|
35816
|
+
import { readFile as readFile18, writeFile as writeFile21 } from "node:fs/promises";
|
|
35729
35817
|
import { resolve as resolve25 } from "node:path";
|
|
35730
35818
|
async function executeVideoGenerate(options) {
|
|
35731
35819
|
const {
|
|
@@ -35765,7 +35853,7 @@ async function executeVideoGenerate(options) {
|
|
|
35765
35853
|
let referenceImageMimeType;
|
|
35766
35854
|
if (image) {
|
|
35767
35855
|
const imagePath = resolve25(process.cwd(), image);
|
|
35768
|
-
const imageBuffer = await
|
|
35856
|
+
const imageBuffer = await readFile18(imagePath);
|
|
35769
35857
|
const ext = image.toLowerCase().split(".").pop();
|
|
35770
35858
|
const mimeTypes2 = {
|
|
35771
35859
|
jpg: "image/jpeg",
|
|
@@ -36013,7 +36101,7 @@ async function fileInputToUrlOrDataUri(input, fallbackMimeType) {
|
|
|
36013
36101
|
return input;
|
|
36014
36102
|
}
|
|
36015
36103
|
const absPath = resolve25(process.cwd(), input);
|
|
36016
|
-
const buffer = await
|
|
36104
|
+
const buffer = await readFile18(absPath);
|
|
36017
36105
|
return `data:${mimeTypeForPath(input, fallbackMimeType)};base64,${buffer.toString("base64")}`;
|
|
36018
36106
|
}
|
|
36019
36107
|
function mimeTypeForPath(path2, fallback2) {
|
|
@@ -36265,15 +36353,15 @@ var init_ai_video = __esm({
|
|
|
36265
36353
|
});
|
|
36266
36354
|
|
|
36267
36355
|
// src/commands/_shared/build-asset-metadata.ts
|
|
36268
|
-
import { existsSync as
|
|
36356
|
+
import { existsSync as existsSync16, readFileSync as readFileSync2 } from "node:fs";
|
|
36269
36357
|
import { mkdir as mkdir10, writeFile as writeFile22 } from "node:fs/promises";
|
|
36270
|
-
import { dirname as dirname6, join as
|
|
36358
|
+
import { dirname as dirname6, join as join13 } from "node:path";
|
|
36271
36359
|
function assetMetadataPath(kind, beatId) {
|
|
36272
36360
|
return `.vibeframe/assets/${kind}-${beatId}.json`;
|
|
36273
36361
|
}
|
|
36274
36362
|
function readAssetMetadata(projectDir, kind, beatId) {
|
|
36275
|
-
const path2 =
|
|
36276
|
-
if (!
|
|
36363
|
+
const path2 = join13(projectDir, assetMetadataPath(kind, beatId));
|
|
36364
|
+
if (!existsSync16(path2)) return null;
|
|
36277
36365
|
try {
|
|
36278
36366
|
const parsed = JSON.parse(readFileSync2(path2, "utf-8"));
|
|
36279
36367
|
if (parsed.schemaVersion !== "1" || parsed.kind !== kind || parsed.beatId !== beatId || typeof parsed.cacheKey !== "string") {
|
|
@@ -36302,7 +36390,7 @@ function isFreshCanonicalAsset(opts) {
|
|
|
36302
36390
|
}
|
|
36303
36391
|
async function writeAssetMetadata(opts) {
|
|
36304
36392
|
const rel = assetMetadataPath(opts.kind, opts.beatId);
|
|
36305
|
-
const abs =
|
|
36393
|
+
const abs = join13(opts.projectDir, rel);
|
|
36306
36394
|
const metadata = {
|
|
36307
36395
|
schemaVersion: "1",
|
|
36308
36396
|
kind: opts.kind,
|
|
@@ -36326,20 +36414,20 @@ var init_build_asset_metadata = __esm({
|
|
|
36326
36414
|
|
|
36327
36415
|
// src/commands/_shared/status-jobs.ts
|
|
36328
36416
|
import { createHash as createHash5, randomUUID as randomUUID2 } from "node:crypto";
|
|
36329
|
-
import { existsSync as
|
|
36330
|
-
import { copyFile as copyFile3, mkdir as mkdir11, readFile as
|
|
36331
|
-
import { dirname as dirname7, join as
|
|
36417
|
+
import { existsSync as existsSync17, statSync as statSync2 } from "node:fs";
|
|
36418
|
+
import { copyFile as copyFile3, mkdir as mkdir11, readFile as readFile19, readdir as readdir2, stat as stat4, writeFile as writeFile23 } from "node:fs/promises";
|
|
36419
|
+
import { dirname as dirname7, join as join14, parse as parse2, relative as relative7, resolve as resolve26 } from "node:path";
|
|
36332
36420
|
function isLocalJob(record) {
|
|
36333
36421
|
return record.jobType === "build" || record.jobType === "render";
|
|
36334
36422
|
}
|
|
36335
36423
|
function findProjectRoot(start = process.cwd()) {
|
|
36336
36424
|
let dir = resolve26(start);
|
|
36337
36425
|
try {
|
|
36338
|
-
if (
|
|
36426
|
+
if (existsSync17(dir) && statSync2(dir).isFile()) dir = dirname7(dir);
|
|
36339
36427
|
} catch {
|
|
36340
36428
|
}
|
|
36341
36429
|
for (; ; ) {
|
|
36342
|
-
if (
|
|
36430
|
+
if (existsSync17(join14(dir, "vibe.config.json")) || existsSync17(join14(dir, "STORYBOARD.md"))) {
|
|
36343
36431
|
return dir;
|
|
36344
36432
|
}
|
|
36345
36433
|
const parent = dirname7(dir);
|
|
@@ -36348,7 +36436,7 @@ function findProjectRoot(start = process.cwd()) {
|
|
|
36348
36436
|
}
|
|
36349
36437
|
}
|
|
36350
36438
|
function jobsDir(projectDir) {
|
|
36351
|
-
return
|
|
36439
|
+
return join14(resolve26(projectDir), ".vibeframe", "jobs");
|
|
36352
36440
|
}
|
|
36353
36441
|
function normalizeJobStatus(status) {
|
|
36354
36442
|
const value = String(status ?? "").toLowerCase();
|
|
@@ -36420,18 +36508,18 @@ async function readJobRecord(jobId, projectDir) {
|
|
|
36420
36508
|
const id = normalizeJobId(jobId);
|
|
36421
36509
|
const project = resolve26(projectDir ?? findProjectRoot());
|
|
36422
36510
|
const path2 = jobRecordPath(id, project);
|
|
36423
|
-
if (!
|
|
36424
|
-
return parseJobRecord(await
|
|
36511
|
+
if (!existsSync17(path2)) return null;
|
|
36512
|
+
return parseJobRecord(await readFile19(path2, "utf-8"));
|
|
36425
36513
|
}
|
|
36426
36514
|
async function listJobRecords(projectDir) {
|
|
36427
36515
|
const dir = jobsDir(projectDir);
|
|
36428
|
-
if (!
|
|
36516
|
+
if (!existsSync17(dir)) return [];
|
|
36429
36517
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
36430
36518
|
const records = [];
|
|
36431
36519
|
for (const entry of entries) {
|
|
36432
36520
|
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
36433
36521
|
try {
|
|
36434
|
-
const parsed = parseJobRecord(await
|
|
36522
|
+
const parsed = parseJobRecord(await readFile19(join14(dir, entry.name), "utf-8"));
|
|
36435
36523
|
if (parsed) records.push(parsed);
|
|
36436
36524
|
} catch {
|
|
36437
36525
|
}
|
|
@@ -36525,7 +36613,7 @@ async function refreshLocalJobRecord(record, opts) {
|
|
|
36525
36613
|
const heartbeatAge = record.heartbeatAt ? Date.now() - new Date(record.heartbeatAt).getTime() : Number.POSITIVE_INFINITY;
|
|
36526
36614
|
const orphaned = !isPidAlive(record.pid) || heartbeatAge > LOCAL_JOB_ORPHAN_AFTER_MS;
|
|
36527
36615
|
if (orphaned) {
|
|
36528
|
-
const hint = `The process that ran this ${record.jobType} job is gone. Check ${
|
|
36616
|
+
const hint = `The process that ran this ${record.jobType} job is gone. Check ${join14(record.projectDir, record.jobType === "render" ? "render-report.json" : "build-report.json")} \u2014 the work may have completed before the process exited.`;
|
|
36529
36617
|
warnings.push(hint);
|
|
36530
36618
|
updated = stripUndefined2({
|
|
36531
36619
|
...record,
|
|
@@ -36688,7 +36776,7 @@ function normalizeJobId(jobId) {
|
|
|
36688
36776
|
return id;
|
|
36689
36777
|
}
|
|
36690
36778
|
function jobRecordPath(jobId, projectDir) {
|
|
36691
|
-
return
|
|
36779
|
+
return join14(jobsDir(projectDir), `${normalizeJobId(jobId)}.json`);
|
|
36692
36780
|
}
|
|
36693
36781
|
function parseJobRecord(raw) {
|
|
36694
36782
|
const parsed = JSON.parse(raw);
|
|
@@ -36755,7 +36843,7 @@ async function maybeCacheOutput(record) {
|
|
|
36755
36843
|
}
|
|
36756
36844
|
async function maybeWriteCompletedAssetMetadata(record) {
|
|
36757
36845
|
const kind = assetKindForJob(record);
|
|
36758
|
-
if (record.status !== "completed" || !kind || !record.beatId || !record.assetCue || !record.cacheKey || !record.outputPath || !
|
|
36846
|
+
if (record.status !== "completed" || !kind || !record.beatId || !record.assetCue || !record.cacheKey || !record.outputPath || !existsSync17(record.outputPath)) {
|
|
36759
36847
|
return;
|
|
36760
36848
|
}
|
|
36761
36849
|
try {
|
|
@@ -36774,7 +36862,7 @@ async function maybeWriteCompletedAssetMetadata(record) {
|
|
|
36774
36862
|
}
|
|
36775
36863
|
}
|
|
36776
36864
|
async function refreshBuildReportFromJobs(projectDir, records) {
|
|
36777
|
-
const reportPath =
|
|
36865
|
+
const reportPath = join14(projectDir, "build-report.json");
|
|
36778
36866
|
const report = await readJson(reportPath);
|
|
36779
36867
|
if (!report || !Array.isArray(report.beats)) return;
|
|
36780
36868
|
let changed = false;
|
|
@@ -36827,7 +36915,7 @@ async function refreshBuildReportFromJobs(projectDir, records) {
|
|
|
36827
36915
|
}
|
|
36828
36916
|
}
|
|
36829
36917
|
async function readBuildSummary(projectDir) {
|
|
36830
|
-
const reportPath =
|
|
36918
|
+
const reportPath = join14(projectDir, "build-report.json");
|
|
36831
36919
|
const report = await readJson(reportPath);
|
|
36832
36920
|
if (!report) return null;
|
|
36833
36921
|
const info = await statOrNull(reportPath);
|
|
@@ -36850,7 +36938,7 @@ async function readBuildSummary(projectDir) {
|
|
|
36850
36938
|
});
|
|
36851
36939
|
}
|
|
36852
36940
|
async function readReviewSummary(projectDir) {
|
|
36853
|
-
const reportPath =
|
|
36941
|
+
const reportPath = join14(projectDir, "review-report.json");
|
|
36854
36942
|
const report = await readJson(reportPath);
|
|
36855
36943
|
if (!report) return null;
|
|
36856
36944
|
const info = await statOrNull(reportPath);
|
|
@@ -36939,16 +37027,16 @@ async function readProjectBeatReadiness(projectDir, build, records) {
|
|
|
36939
37027
|
if (fromBeats) return fromBeats;
|
|
36940
37028
|
if (build.beats) return augmentBeatReadinessFromJobs(build.beats, records, report?.jobs);
|
|
36941
37029
|
}
|
|
36942
|
-
const storyboardPath2 =
|
|
36943
|
-
if (!
|
|
37030
|
+
const storyboardPath2 = join14(projectDir, "STORYBOARD.md");
|
|
37031
|
+
if (!existsSync17(storyboardPath2)) {
|
|
36944
37032
|
return { total: 0, assetsReady: 0, compositionsReady: 0, needsAuthor: [] };
|
|
36945
37033
|
}
|
|
36946
37034
|
try {
|
|
36947
|
-
const parsed = parseStoryboard(await
|
|
37035
|
+
const parsed = parseStoryboard(await readFile19(storyboardPath2, "utf-8"));
|
|
36948
37036
|
const needsAuthor = [];
|
|
36949
37037
|
let compositionsReady = 0;
|
|
36950
37038
|
for (const beat of parsed.beats) {
|
|
36951
|
-
if (
|
|
37039
|
+
if (existsSync17(join14(projectDir, "compositions", `scene-${beat.id}.html`))) {
|
|
36952
37040
|
compositionsReady += 1;
|
|
36953
37041
|
} else {
|
|
36954
37042
|
needsAuthor.push(beat.id);
|
|
@@ -36966,7 +37054,7 @@ async function readProjectBeatReadiness(projectDir, build, records) {
|
|
|
36966
37054
|
}
|
|
36967
37055
|
function deriveProjectWorkflow(opts) {
|
|
36968
37056
|
const retryWith = [];
|
|
36969
|
-
const hasStoryboard =
|
|
37057
|
+
const hasStoryboard = existsSync17(join14(opts.project, "STORYBOARD.md"));
|
|
36970
37058
|
if (!hasStoryboard && !opts.build) {
|
|
36971
37059
|
retryWith.push(`vibe init ${opts.project} --from "<brief>" --json`);
|
|
36972
37060
|
return { status: "empty", currentStage: "init", retryWith };
|
|
@@ -37062,7 +37150,7 @@ function beatReadinessFromBuildBeats(projectDir, beats, records = [], reportJobs
|
|
|
37062
37150
|
if (buildBeatAssetsReady(record) || (id ? buildBeatJobsReady(id, records, reportJobs) : false))
|
|
37063
37151
|
assetsReady += 1;
|
|
37064
37152
|
const compositionPath = typeof record.compositionPath === "string" ? record.compositionPath : id ? `compositions/scene-${id}.html` : void 0;
|
|
37065
|
-
if (compositionPath &&
|
|
37153
|
+
if (compositionPath && existsSync17(resolve26(projectDir, compositionPath))) {
|
|
37066
37154
|
compositionsReady += 1;
|
|
37067
37155
|
} else if (id) {
|
|
37068
37156
|
needsAuthor.push(id);
|
|
@@ -37170,7 +37258,7 @@ function projectRelativePath(projectDir, path2) {
|
|
|
37170
37258
|
}
|
|
37171
37259
|
async function readJson(path2) {
|
|
37172
37260
|
try {
|
|
37173
|
-
return JSON.parse(await
|
|
37261
|
+
return JSON.parse(await readFile19(path2, "utf-8"));
|
|
37174
37262
|
} catch {
|
|
37175
37263
|
return null;
|
|
37176
37264
|
}
|
|
@@ -37209,7 +37297,7 @@ var init_status_jobs = __esm({
|
|
|
37209
37297
|
// src/commands/generate/music.ts
|
|
37210
37298
|
import { resolve as resolve27 } from "node:path";
|
|
37211
37299
|
import { writeFile as writeFile24 } from "node:fs/promises";
|
|
37212
|
-
import { existsSync as
|
|
37300
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
37213
37301
|
async function executeMusic(options) {
|
|
37214
37302
|
try {
|
|
37215
37303
|
const provider = options.provider || "elevenlabs";
|
|
@@ -37362,7 +37450,7 @@ function registerMusicCommand(parent) {
|
|
|
37362
37450
|
if (options.melody) {
|
|
37363
37451
|
spinner2.text = "Uploading melody reference...";
|
|
37364
37452
|
const absPath = resolve27(process.cwd(), options.melody);
|
|
37365
|
-
if (!
|
|
37453
|
+
if (!existsSync18(absPath)) {
|
|
37366
37454
|
spinner2.fail(`Melody file not found: ${options.melody}`);
|
|
37367
37455
|
exitWithError(notFoundError(options.melody));
|
|
37368
37456
|
}
|
|
@@ -37483,8 +37571,8 @@ __export(ai_image_exports, {
|
|
|
37483
37571
|
executeThumbnailBestFrame: () => executeThumbnailBestFrame
|
|
37484
37572
|
});
|
|
37485
37573
|
import { resolve as resolve28, dirname as dirname8 } from "node:path";
|
|
37486
|
-
import { readFile as
|
|
37487
|
-
import { existsSync as
|
|
37574
|
+
import { readFile as readFile20, writeFile as writeFile25, mkdir as mkdir12 } from "node:fs/promises";
|
|
37575
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
37488
37576
|
async function executeImageGenerate(options) {
|
|
37489
37577
|
const {
|
|
37490
37578
|
prompt: prompt3,
|
|
@@ -37628,10 +37716,10 @@ async function executeGeminiEdit(options) {
|
|
|
37628
37716
|
const imageBuffers = [];
|
|
37629
37717
|
for (const imagePath of imagePaths) {
|
|
37630
37718
|
const absPath = resolve28(process.cwd(), imagePath);
|
|
37631
|
-
if (!
|
|
37719
|
+
if (!existsSync19(absPath)) {
|
|
37632
37720
|
return { success: false, error: `Image not found: ${absPath}` };
|
|
37633
37721
|
}
|
|
37634
|
-
const buffer = await
|
|
37722
|
+
const buffer = await readFile20(absPath);
|
|
37635
37723
|
imageBuffers.push(buffer);
|
|
37636
37724
|
}
|
|
37637
37725
|
const gemini = new GeminiProvider();
|
|
@@ -37676,7 +37764,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
37676
37764
|
model = "flash",
|
|
37677
37765
|
apiKey
|
|
37678
37766
|
} = options;
|
|
37679
|
-
if (!
|
|
37767
|
+
if (!existsSync19(videoPath)) {
|
|
37680
37768
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
37681
37769
|
}
|
|
37682
37770
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37689,7 +37777,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
37689
37777
|
try {
|
|
37690
37778
|
const gemini = new GeminiProvider();
|
|
37691
37779
|
await gemini.initialize({ apiKey: googleKey });
|
|
37692
|
-
const videoData = await
|
|
37780
|
+
const videoData = await readFile20(videoPath);
|
|
37693
37781
|
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>"}';
|
|
37694
37782
|
const modelId = resolveGeminiTextModel(model);
|
|
37695
37783
|
const result = await gemini.analyzeVideo(videoData, analysisPrompt, {
|
|
@@ -37710,7 +37798,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
37710
37798
|
reason = reasonMatch[1];
|
|
37711
37799
|
}
|
|
37712
37800
|
await execSafe("ffmpeg", ["-ss", String(timestamp2), "-i", videoPath, "-frames:v", "1", "-q:v", "2", outputPath, "-y"], { timeout: 6e4, maxBuffer: 50 * 1024 * 1024 });
|
|
37713
|
-
if (!
|
|
37801
|
+
if (!existsSync19(outputPath)) {
|
|
37714
37802
|
return { success: false, error: "FFmpeg failed to extract frame" };
|
|
37715
37803
|
}
|
|
37716
37804
|
return {
|
|
@@ -37736,7 +37824,7 @@ var init_ai_image = __esm({
|
|
|
37736
37824
|
|
|
37737
37825
|
// src/commands/generate/thumbnail.ts
|
|
37738
37826
|
import { resolve as resolve29, dirname as dirname9, basename as basename5, extname as extname3 } from "node:path";
|
|
37739
|
-
import { existsSync as
|
|
37827
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
37740
37828
|
import { writeFile as writeFile26, mkdir as mkdir13 } from "node:fs/promises";
|
|
37741
37829
|
function registerThumbnailCommand(parent) {
|
|
37742
37830
|
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) => {
|
|
@@ -37748,7 +37836,7 @@ function registerThumbnailCommand(parent) {
|
|
|
37748
37836
|
}
|
|
37749
37837
|
if (options.bestFrame) {
|
|
37750
37838
|
const absVideoPath = resolve29(process.cwd(), options.bestFrame);
|
|
37751
|
-
if (!
|
|
37839
|
+
if (!existsSync20(absVideoPath)) {
|
|
37752
37840
|
exitWithError(notFoundError(absVideoPath));
|
|
37753
37841
|
}
|
|
37754
37842
|
if (!commandExists("ffmpeg")) {
|
|
@@ -39932,7 +40020,7 @@ var init_dist3 = __esm({
|
|
|
39932
40020
|
|
|
39933
40021
|
// src/commands/generate/video.ts
|
|
39934
40022
|
import { resolve as resolve33 } from "node:path";
|
|
39935
|
-
import { readFile as
|
|
40023
|
+
import { readFile as readFile21, writeFile as writeFile30 } from "node:fs/promises";
|
|
39936
40024
|
function registerVideoCommand(parent) {
|
|
39937
40025
|
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(
|
|
39938
40026
|
"-p, --provider <provider>",
|
|
@@ -40043,7 +40131,7 @@ Examples:
|
|
|
40043
40131
|
let isImageToVideo = false;
|
|
40044
40132
|
if (options.image) {
|
|
40045
40133
|
const imagePath = resolve33(process.cwd(), options.image);
|
|
40046
|
-
const imageBuffer = await
|
|
40134
|
+
const imageBuffer = await readFile21(imagePath);
|
|
40047
40135
|
const ext = options.image.toLowerCase().split(".").pop();
|
|
40048
40136
|
const mimeTypes2 = {
|
|
40049
40137
|
jpg: "image/jpeg",
|
|
@@ -40283,7 +40371,7 @@ Examples:
|
|
|
40283
40371
|
let lastFrame;
|
|
40284
40372
|
if (options.lastFrame) {
|
|
40285
40373
|
const lastFramePath = resolve33(process.cwd(), options.lastFrame);
|
|
40286
|
-
const lastFrameBuffer = await
|
|
40374
|
+
const lastFrameBuffer = await readFile21(lastFramePath);
|
|
40287
40375
|
const ext = options.lastFrame.toLowerCase().split(".").pop();
|
|
40288
40376
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
|
|
40289
40377
|
lastFrame = `data:${mimeType};base64,${lastFrameBuffer.toString("base64")}`;
|
|
@@ -40293,7 +40381,7 @@ Examples:
|
|
|
40293
40381
|
refImages = [];
|
|
40294
40382
|
for (const refPath of options.refImages.slice(0, 3)) {
|
|
40295
40383
|
const absRefPath = resolve33(process.cwd(), refPath);
|
|
40296
|
-
const refBuffer = await
|
|
40384
|
+
const refBuffer = await readFile21(absRefPath);
|
|
40297
40385
|
const ext = refPath.toLowerCase().split(".").pop();
|
|
40298
40386
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
|
|
40299
40387
|
refImages.push({ base64: refBuffer.toString("base64"), mimeType });
|
|
@@ -40425,7 +40513,7 @@ Examples:
|
|
|
40425
40513
|
if (seedanceEndImagePath && seedanceReferences.length === 0) {
|
|
40426
40514
|
try {
|
|
40427
40515
|
const absEndImagePath = resolve33(process.cwd(), seedanceEndImagePath);
|
|
40428
|
-
const endImageBuffer = await
|
|
40516
|
+
const endImageBuffer = await readFile21(absEndImagePath);
|
|
40429
40517
|
const ext = seedanceEndImagePath.toLowerCase().split(".").pop();
|
|
40430
40518
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
|
|
40431
40519
|
const uploadHost = await resolveUploadHost();
|
|
@@ -40542,7 +40630,7 @@ async function fileInputToUrlOrDataUri2(input, fallbackMimeType) {
|
|
|
40542
40630
|
return input;
|
|
40543
40631
|
}
|
|
40544
40632
|
const absPath = resolve33(process.cwd(), input);
|
|
40545
|
-
const buffer = await
|
|
40633
|
+
const buffer = await readFile21(absPath);
|
|
40546
40634
|
return `data:${mimeTypeForPath2(input, fallbackMimeType)};base64,${buffer.toString("base64")}`;
|
|
40547
40635
|
}
|
|
40548
40636
|
function mimeTypeForPath2(path2, fallback2) {
|
|
@@ -40715,8 +40803,8 @@ Run 'vibe schema generate.<command>' for structured parameter info.
|
|
|
40715
40803
|
});
|
|
40716
40804
|
|
|
40717
40805
|
// src/commands/_shared/edit/silence-cut.ts
|
|
40718
|
-
import { existsSync as
|
|
40719
|
-
import { readFile as
|
|
40806
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
40807
|
+
import { readFile as readFile22 } from "node:fs/promises";
|
|
40720
40808
|
async function detectSilencePeriods(videoPath, noiseThreshold, minDuration) {
|
|
40721
40809
|
const totalDuration = await getVideoDuration(videoPath);
|
|
40722
40810
|
const { stdout, stderr } = await execSafe(
|
|
@@ -40766,7 +40854,7 @@ async function detectSilencePeriodsWithGemini(videoPath, minDuration, options) {
|
|
|
40766
40854
|
}
|
|
40767
40855
|
const gemini = new GeminiProvider();
|
|
40768
40856
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
40769
|
-
const videoBuffer = await
|
|
40857
|
+
const videoBuffer = await readFile22(videoPath);
|
|
40770
40858
|
const modelId = options.model ? resolveGeminiTextModel(options.model) : void 0;
|
|
40771
40859
|
const prompt3 = `Analyze this video and identify all silent or dead segments where there is NO meaningful content.
|
|
40772
40860
|
|
|
@@ -40842,7 +40930,7 @@ async function executeSilenceCut(options) {
|
|
|
40842
40930
|
analyzeOnly = false,
|
|
40843
40931
|
useGemini = false
|
|
40844
40932
|
} = options;
|
|
40845
|
-
if (!
|
|
40933
|
+
if (!existsSync21(videoPath)) {
|
|
40846
40934
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
40847
40935
|
}
|
|
40848
40936
|
if (!commandExists("ffmpeg")) {
|
|
@@ -40950,11 +41038,11 @@ var init_silence_cut = __esm({
|
|
|
40950
41038
|
});
|
|
40951
41039
|
|
|
40952
41040
|
// src/commands/_shared/edit/jump-cut.ts
|
|
40953
|
-
import { existsSync as
|
|
40954
|
-
import { readFile as
|
|
40955
|
-
import { join as
|
|
41041
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
41042
|
+
import { readFile as readFile23, writeFile as writeFile31, mkdir as mkdir15 } from "node:fs/promises";
|
|
41043
|
+
import { join as join15 } from "node:path";
|
|
40956
41044
|
async function transcribeWithWords(audioPath, apiKey, language) {
|
|
40957
|
-
const audioBuffer = await
|
|
41045
|
+
const audioBuffer = await readFile23(audioPath);
|
|
40958
41046
|
const audioBlob = new Blob([audioBuffer]);
|
|
40959
41047
|
const formData = new FormData();
|
|
40960
41048
|
formData.append("file", audioBlob, "audio.wav");
|
|
@@ -41013,7 +41101,7 @@ async function executeJumpCut(options) {
|
|
|
41013
41101
|
analyzeOnly = false,
|
|
41014
41102
|
apiKey
|
|
41015
41103
|
} = options;
|
|
41016
|
-
if (!
|
|
41104
|
+
if (!existsSync22(videoPath)) {
|
|
41017
41105
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
41018
41106
|
}
|
|
41019
41107
|
if (!commandExists("ffmpeg")) {
|
|
@@ -41032,7 +41120,7 @@ async function executeJumpCut(options) {
|
|
|
41032
41120
|
try {
|
|
41033
41121
|
const tmpDir = `/tmp/vibe_jumpcut_${Date.now()}`;
|
|
41034
41122
|
await mkdir15(tmpDir, { recursive: true });
|
|
41035
|
-
const audioPath =
|
|
41123
|
+
const audioPath = join15(tmpDir, "audio.wav");
|
|
41036
41124
|
try {
|
|
41037
41125
|
await execSafe(
|
|
41038
41126
|
"ffmpeg",
|
|
@@ -41086,7 +41174,7 @@ async function executeJumpCut(options) {
|
|
|
41086
41174
|
const segmentPaths = [];
|
|
41087
41175
|
for (let i = 0; i < segments.length; i++) {
|
|
41088
41176
|
const seg = segments[i];
|
|
41089
|
-
const segPath =
|
|
41177
|
+
const segPath = join15(tmpDir, `seg-${i.toString().padStart(4, "0")}.ts`);
|
|
41090
41178
|
const duration = seg.end - seg.start;
|
|
41091
41179
|
await execSafe(
|
|
41092
41180
|
"ffmpeg",
|
|
@@ -41109,7 +41197,7 @@ async function executeJumpCut(options) {
|
|
|
41109
41197
|
segmentPaths.push(segPath);
|
|
41110
41198
|
}
|
|
41111
41199
|
const concatList = segmentPaths.map((p) => `file '${p}'`).join("\n");
|
|
41112
|
-
const listPath =
|
|
41200
|
+
const listPath = join15(tmpDir, "concat.txt");
|
|
41113
41201
|
await writeFile31(listPath, concatList);
|
|
41114
41202
|
await execSafe(
|
|
41115
41203
|
"ffmpeg",
|
|
@@ -41299,9 +41387,9 @@ var init_helpers = __esm({
|
|
|
41299
41387
|
});
|
|
41300
41388
|
|
|
41301
41389
|
// src/commands/_shared/edit/caption.ts
|
|
41302
|
-
import { existsSync as
|
|
41303
|
-
import { readFile as
|
|
41304
|
-
import { dirname as dirname11, basename as basename6, extname as extname4, join as
|
|
41390
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
41391
|
+
import { readFile as readFile24, writeFile as writeFile32, mkdir as mkdir16 } from "node:fs/promises";
|
|
41392
|
+
import { dirname as dirname11, basename as basename6, extname as extname4, join as join16 } from "node:path";
|
|
41305
41393
|
function getCaptionForceStyle(style, fontSize, fontColor, position) {
|
|
41306
41394
|
const alignment = position === "top" ? 8 : position === "center" ? 5 : 2;
|
|
41307
41395
|
const marginV = position === "center" ? 0 : 30;
|
|
@@ -41329,7 +41417,7 @@ async function executeCaption(options) {
|
|
|
41329
41417
|
position = "bottom",
|
|
41330
41418
|
apiKey
|
|
41331
41419
|
} = options;
|
|
41332
|
-
if (!
|
|
41420
|
+
if (!existsSync23(videoPath)) {
|
|
41333
41421
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
41334
41422
|
}
|
|
41335
41423
|
if (!commandExists("ffmpeg")) {
|
|
@@ -41348,8 +41436,8 @@ async function executeCaption(options) {
|
|
|
41348
41436
|
try {
|
|
41349
41437
|
const tmpDir = `/tmp/vibe_caption_${Date.now()}`;
|
|
41350
41438
|
await mkdir16(tmpDir, { recursive: true });
|
|
41351
|
-
const audioPath =
|
|
41352
|
-
const srtPath =
|
|
41439
|
+
const audioPath = join16(tmpDir, "audio.wav");
|
|
41440
|
+
const srtPath = join16(tmpDir, "captions.srt");
|
|
41353
41441
|
try {
|
|
41354
41442
|
await execSafe(
|
|
41355
41443
|
"ffmpeg",
|
|
@@ -41370,7 +41458,7 @@ async function executeCaption(options) {
|
|
|
41370
41458
|
);
|
|
41371
41459
|
const whisper = new WhisperProvider();
|
|
41372
41460
|
await whisper.initialize({ apiKey: openaiKey });
|
|
41373
|
-
const audioBuffer = await
|
|
41461
|
+
const audioBuffer = await readFile24(audioPath);
|
|
41374
41462
|
const audioBlob = new Blob([audioBuffer]);
|
|
41375
41463
|
const transcriptResult = await whisper.transcribe(audioBlob, language);
|
|
41376
41464
|
if (transcriptResult.status === "failed" || !transcriptResult.segments || transcriptResult.segments.length === 0) {
|
|
@@ -41418,7 +41506,7 @@ async function executeCaption(options) {
|
|
|
41418
41506
|
const remotionErr = await ensureRemotionInstalled2();
|
|
41419
41507
|
if (remotionErr) {
|
|
41420
41508
|
const outputDir2 = dirname11(outputPath);
|
|
41421
|
-
const outputSrtPath2 =
|
|
41509
|
+
const outputSrtPath2 = join16(
|
|
41422
41510
|
outputDir2,
|
|
41423
41511
|
basename6(outputPath, extname4(outputPath)) + ".srt"
|
|
41424
41512
|
);
|
|
@@ -41457,7 +41545,7 @@ SRT saved to: ${outputSrtPath2}` };
|
|
|
41457
41545
|
});
|
|
41458
41546
|
if (!renderResult.success) {
|
|
41459
41547
|
const outputDir2 = dirname11(outputPath);
|
|
41460
|
-
const outputSrtPath2 =
|
|
41548
|
+
const outputSrtPath2 = join16(
|
|
41461
41549
|
outputDir2,
|
|
41462
41550
|
basename6(outputPath, extname4(outputPath)) + ".srt"
|
|
41463
41551
|
);
|
|
@@ -41470,7 +41558,7 @@ SRT saved to: ${outputSrtPath2}`
|
|
|
41470
41558
|
}
|
|
41471
41559
|
}
|
|
41472
41560
|
const outputDir = dirname11(outputPath);
|
|
41473
|
-
const outputSrtPath =
|
|
41561
|
+
const outputSrtPath = join16(
|
|
41474
41562
|
outputDir,
|
|
41475
41563
|
basename6(outputPath, extname4(outputPath)) + ".srt"
|
|
41476
41564
|
);
|
|
@@ -41507,10 +41595,10 @@ var init_caption = __esm({
|
|
|
41507
41595
|
});
|
|
41508
41596
|
|
|
41509
41597
|
// src/commands/_shared/edit/noise-reduce.ts
|
|
41510
|
-
import { existsSync as
|
|
41598
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
41511
41599
|
async function executeNoiseReduce(options) {
|
|
41512
41600
|
const { inputPath, outputPath, strength = "medium", noiseFloor } = options;
|
|
41513
|
-
if (!
|
|
41601
|
+
if (!existsSync24(inputPath)) {
|
|
41514
41602
|
return { success: false, error: `File not found: ${inputPath}` };
|
|
41515
41603
|
}
|
|
41516
41604
|
if (!commandExists("ffmpeg")) {
|
|
@@ -41567,7 +41655,7 @@ var init_noise_reduce = __esm({
|
|
|
41567
41655
|
});
|
|
41568
41656
|
|
|
41569
41657
|
// src/commands/_shared/edit/fade.ts
|
|
41570
|
-
import { existsSync as
|
|
41658
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
41571
41659
|
async function executeFade(options) {
|
|
41572
41660
|
const {
|
|
41573
41661
|
videoPath,
|
|
@@ -41577,7 +41665,7 @@ async function executeFade(options) {
|
|
|
41577
41665
|
audioOnly = false,
|
|
41578
41666
|
videoOnly = false
|
|
41579
41667
|
} = options;
|
|
41580
|
-
if (!
|
|
41668
|
+
if (!existsSync25(videoPath)) {
|
|
41581
41669
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
41582
41670
|
}
|
|
41583
41671
|
if (!commandExists("ffmpeg")) {
|
|
@@ -41644,8 +41732,8 @@ var init_fade = __esm({
|
|
|
41644
41732
|
});
|
|
41645
41733
|
|
|
41646
41734
|
// src/commands/_shared/edit/translate-srt.ts
|
|
41647
|
-
import { existsSync as
|
|
41648
|
-
import { readFile as
|
|
41735
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
41736
|
+
import { readFile as readFile25, writeFile as writeFile33 } from "node:fs/promises";
|
|
41649
41737
|
async function executeTranslateSrt(options) {
|
|
41650
41738
|
const {
|
|
41651
41739
|
srtPath,
|
|
@@ -41655,11 +41743,11 @@ async function executeTranslateSrt(options) {
|
|
|
41655
41743
|
sourceLanguage,
|
|
41656
41744
|
apiKey
|
|
41657
41745
|
} = options;
|
|
41658
|
-
if (!
|
|
41746
|
+
if (!existsSync26(srtPath)) {
|
|
41659
41747
|
return { success: false, error: `SRT file not found: ${srtPath}` };
|
|
41660
41748
|
}
|
|
41661
41749
|
try {
|
|
41662
|
-
const srtContent = await
|
|
41750
|
+
const srtContent = await readFile25(srtPath, "utf-8");
|
|
41663
41751
|
const segments = parseSRT(srtContent);
|
|
41664
41752
|
if (segments.length === 0) {
|
|
41665
41753
|
return { success: false, error: "No subtitle segments found in SRT file" };
|
|
@@ -41772,7 +41860,7 @@ var init_translate_srt = __esm({
|
|
|
41772
41860
|
});
|
|
41773
41861
|
|
|
41774
41862
|
// src/commands/_shared/edit/text-overlay.ts
|
|
41775
|
-
import { existsSync as
|
|
41863
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
41776
41864
|
import { resolve as resolve34 } from "node:path";
|
|
41777
41865
|
function detectSystemFont() {
|
|
41778
41866
|
const platform3 = process.platform;
|
|
@@ -41783,7 +41871,7 @@ function detectSystemFont() {
|
|
|
41783
41871
|
"/Library/Fonts/Arial.ttf"
|
|
41784
41872
|
];
|
|
41785
41873
|
for (const f of candidates) {
|
|
41786
|
-
if (
|
|
41874
|
+
if (existsSync27(f)) return f;
|
|
41787
41875
|
}
|
|
41788
41876
|
} else if (platform3 === "linux") {
|
|
41789
41877
|
const candidates = [
|
|
@@ -41792,7 +41880,7 @@ function detectSystemFont() {
|
|
|
41792
41880
|
"/usr/share/fonts/TTF/DejaVuSans-Bold.ttf"
|
|
41793
41881
|
];
|
|
41794
41882
|
for (const f of candidates) {
|
|
41795
|
-
if (
|
|
41883
|
+
if (existsSync27(f)) return f;
|
|
41796
41884
|
}
|
|
41797
41885
|
} else if (platform3 === "win32") {
|
|
41798
41886
|
const candidates = [
|
|
@@ -41800,7 +41888,7 @@ function detectSystemFont() {
|
|
|
41800
41888
|
"C:\\Windows\\Fonts\\segoeui.ttf"
|
|
41801
41889
|
];
|
|
41802
41890
|
for (const f of candidates) {
|
|
41803
|
-
if (
|
|
41891
|
+
if (existsSync27(f)) return f;
|
|
41804
41892
|
}
|
|
41805
41893
|
}
|
|
41806
41894
|
return null;
|
|
@@ -41821,7 +41909,7 @@ async function applyTextOverlays(options) {
|
|
|
41821
41909
|
}
|
|
41822
41910
|
const absVideoPath = resolve34(process.cwd(), videoPath);
|
|
41823
41911
|
const absOutputPath = resolve34(process.cwd(), outputPath);
|
|
41824
|
-
if (!
|
|
41912
|
+
if (!existsSync27(absVideoPath)) {
|
|
41825
41913
|
return { success: false, error: `Video not found: ${absVideoPath}` };
|
|
41826
41914
|
}
|
|
41827
41915
|
if (!commandExists("ffmpeg")) {
|
|
@@ -42002,7 +42090,7 @@ var init_ai_edit = __esm({
|
|
|
42002
42090
|
|
|
42003
42091
|
// src/commands/ai-edit-cli.ts
|
|
42004
42092
|
import { resolve as resolve35, extname as extname5, basename as basename7 } from "node:path";
|
|
42005
|
-
import { existsSync as
|
|
42093
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
42006
42094
|
function registerEditCommands(aiCommand) {
|
|
42007
42095
|
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", `
|
|
42008
42096
|
Examples:
|
|
@@ -42019,7 +42107,7 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
|
|
|
42019
42107
|
validateOutputPath(options.output);
|
|
42020
42108
|
}
|
|
42021
42109
|
const absVideoPath = resolve35(process.cwd(), videoPath);
|
|
42022
|
-
if (!
|
|
42110
|
+
if (!existsSync28(absVideoPath)) {
|
|
42023
42111
|
exitWithError(notFoundError(absVideoPath));
|
|
42024
42112
|
}
|
|
42025
42113
|
if (!commandExists("ffmpeg")) {
|
|
@@ -42127,7 +42215,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
42127
42215
|
validateOutputPath(options.output);
|
|
42128
42216
|
}
|
|
42129
42217
|
const absVideoPath = resolve35(process.cwd(), videoPath);
|
|
42130
|
-
if (!
|
|
42218
|
+
if (!existsSync28(absVideoPath)) {
|
|
42131
42219
|
exitWithError(notFoundError(absVideoPath));
|
|
42132
42220
|
}
|
|
42133
42221
|
if (!commandExists("ffmpeg")) {
|
|
@@ -42208,7 +42296,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
42208
42296
|
validateOutputPath(options.output);
|
|
42209
42297
|
}
|
|
42210
42298
|
const absInputPath = resolve35(process.cwd(), inputPath);
|
|
42211
|
-
if (!
|
|
42299
|
+
if (!existsSync28(absInputPath)) {
|
|
42212
42300
|
exitWithError(notFoundError(absInputPath));
|
|
42213
42301
|
}
|
|
42214
42302
|
if (!commandExists("ffmpeg")) {
|
|
@@ -42274,7 +42362,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
42274
42362
|
validateOutputPath(options.output);
|
|
42275
42363
|
}
|
|
42276
42364
|
const absVideoPath = resolve35(process.cwd(), videoPath);
|
|
42277
|
-
if (!
|
|
42365
|
+
if (!existsSync28(absVideoPath)) {
|
|
42278
42366
|
exitWithError(notFoundError(absVideoPath));
|
|
42279
42367
|
}
|
|
42280
42368
|
if (!commandExists("ffmpeg")) {
|
|
@@ -42349,7 +42437,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
42349
42437
|
exitWithError(usageError("Target language required. Use -t or --target"));
|
|
42350
42438
|
}
|
|
42351
42439
|
const absSrtPath = resolve35(process.cwd(), srtPath);
|
|
42352
|
-
if (!
|
|
42440
|
+
if (!existsSync28(absSrtPath)) {
|
|
42353
42441
|
exitWithError(notFoundError(absSrtPath));
|
|
42354
42442
|
}
|
|
42355
42443
|
if (options.dryRun) {
|
|
@@ -42425,7 +42513,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
42425
42513
|
}
|
|
42426
42514
|
if (options.fillers) rejectControlChars(options.fillers);
|
|
42427
42515
|
const absVideoPath = resolve35(process.cwd(), videoPath);
|
|
42428
|
-
if (!
|
|
42516
|
+
if (!existsSync28(absVideoPath)) {
|
|
42429
42517
|
exitWithError(notFoundError(absVideoPath));
|
|
42430
42518
|
}
|
|
42431
42519
|
if (!commandExists("ffmpeg")) {
|
|
@@ -42525,8 +42613,8 @@ var init_ai_edit_cli = __esm({
|
|
|
42525
42613
|
});
|
|
42526
42614
|
|
|
42527
42615
|
// src/commands/_shared/execute-fill-gaps.ts
|
|
42528
|
-
import { existsSync as
|
|
42529
|
-
import { readFile as
|
|
42616
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
42617
|
+
import { readFile as readFile26, writeFile as writeFile34, mkdir as mkdir17, rename as renameFs } from "node:fs/promises";
|
|
42530
42618
|
import { resolve as resolve36, dirname as dirname12 } from "node:path";
|
|
42531
42619
|
function detectVideoGaps(videoClips, totalDuration) {
|
|
42532
42620
|
const gaps = [];
|
|
@@ -42583,7 +42671,7 @@ function analyzeGapFillability(gaps, videoClips, sources) {
|
|
|
42583
42671
|
}
|
|
42584
42672
|
async function uploadFrameToImgbb(framePath, imgbbApiKey) {
|
|
42585
42673
|
try {
|
|
42586
|
-
const frameBuffer = await
|
|
42674
|
+
const frameBuffer = await readFile26(framePath);
|
|
42587
42675
|
const frameBase64 = frameBuffer.toString("base64");
|
|
42588
42676
|
const formData = new FormData();
|
|
42589
42677
|
formData.append("key", imgbbApiKey);
|
|
@@ -42608,14 +42696,14 @@ async function executeFillGaps(options) {
|
|
|
42608
42696
|
try {
|
|
42609
42697
|
onProgress("Loading timeline...");
|
|
42610
42698
|
const filePath = await resolveTimelineFile(options.projectPath);
|
|
42611
|
-
if (!
|
|
42699
|
+
if (!existsSync29(filePath)) {
|
|
42612
42700
|
return {
|
|
42613
42701
|
success: false,
|
|
42614
42702
|
error: `Timeline file not found: ${filePath}`,
|
|
42615
42703
|
humanLines
|
|
42616
42704
|
};
|
|
42617
42705
|
}
|
|
42618
|
-
const content = await
|
|
42706
|
+
const content = await readFile26(filePath, "utf-8");
|
|
42619
42707
|
const data = JSON.parse(content);
|
|
42620
42708
|
const project = Project.fromJSON(data);
|
|
42621
42709
|
const clips = project.getClips().sort((a, b) => a.startTime - b.startTime);
|
|
@@ -42743,7 +42831,7 @@ async function executeFillGaps(options) {
|
|
|
42743
42831
|
}
|
|
42744
42832
|
const projectDir = dirname12(filePath);
|
|
42745
42833
|
const footageDir = options.dir ? resolve36(process.cwd(), options.dir) : resolve36(projectDir, "footage");
|
|
42746
|
-
if (!
|
|
42834
|
+
if (!existsSync29(footageDir)) {
|
|
42747
42835
|
await mkdir17(footageDir, { recursive: true });
|
|
42748
42836
|
}
|
|
42749
42837
|
humanLines.push("Generating AI Videos");
|
|
@@ -43057,15 +43145,15 @@ var init_ai_fill_gaps = __esm({
|
|
|
43057
43145
|
});
|
|
43058
43146
|
|
|
43059
43147
|
// src/pipeline/renderers/chrome.ts
|
|
43060
|
-
import { existsSync as
|
|
43148
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
43061
43149
|
import { homedir as homedir5 } from "node:os";
|
|
43062
|
-
import { join as
|
|
43150
|
+
import { join as join17 } from "node:path";
|
|
43063
43151
|
function findChrome() {
|
|
43064
43152
|
const candidates = [
|
|
43065
43153
|
process.env.HYPERFRAMES_CHROME_PATH,
|
|
43066
43154
|
process.env.CHROME_PATH,
|
|
43067
43155
|
// puppeteer auto-downloaded headless shell
|
|
43068
|
-
|
|
43156
|
+
join17(
|
|
43069
43157
|
homedir5(),
|
|
43070
43158
|
".cache",
|
|
43071
43159
|
"puppeteer",
|
|
@@ -43083,7 +43171,7 @@ function findChrome() {
|
|
|
43083
43171
|
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
|
|
43084
43172
|
];
|
|
43085
43173
|
for (const c of candidates) {
|
|
43086
|
-
if (c &&
|
|
43174
|
+
if (c && existsSync30(c)) return c;
|
|
43087
43175
|
}
|
|
43088
43176
|
return void 0;
|
|
43089
43177
|
}
|
|
@@ -43329,7 +43417,7 @@ __export(project_builder_exports, {
|
|
|
43329
43417
|
resolveSourceUrl: () => resolveSourceUrl
|
|
43330
43418
|
});
|
|
43331
43419
|
import { mkdtemp, mkdir as mkdir18, copyFile as copyFile4, writeFile as writeFile35, rm as rm2, readdir as readdir3 } from "node:fs/promises";
|
|
43332
|
-
import { existsSync as
|
|
43420
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
43333
43421
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
43334
43422
|
import { createRequire } from "node:module";
|
|
43335
43423
|
import * as path from "node:path";
|
|
@@ -43341,7 +43429,7 @@ async function buildTempProject(state, projectFileDir) {
|
|
|
43341
43429
|
for (const source of state.sources) {
|
|
43342
43430
|
if (copied.has(source.id)) continue;
|
|
43343
43431
|
const resolved = resolveSourceUrl(source.url, projectFileDir);
|
|
43344
|
-
if (!
|
|
43432
|
+
if (!existsSync31(resolved)) {
|
|
43345
43433
|
throw new Error(`Source file not found: ${resolved} (from source id=${source.id})`);
|
|
43346
43434
|
}
|
|
43347
43435
|
const dest = path.join(assetsDir, path.basename(resolved));
|
|
@@ -43478,7 +43566,7 @@ __export(motion_overlay_exports, {
|
|
|
43478
43566
|
registerMotionOverlayCommand: () => registerMotionOverlayCommand
|
|
43479
43567
|
});
|
|
43480
43568
|
import { mkdir as mkdir19 } from "node:fs/promises";
|
|
43481
|
-
import { existsSync as
|
|
43569
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
43482
43570
|
import { basename as basename9, dirname as dirname14, extname as extname6, resolve as resolve38 } from "node:path";
|
|
43483
43571
|
function normalizeUnderstand2(value) {
|
|
43484
43572
|
return value === "off" || value === "required" || value === "auto" ? value : "auto";
|
|
@@ -43609,11 +43697,11 @@ async function executeMotionOverlay(options) {
|
|
|
43609
43697
|
const validationError = assertValidRequest(options);
|
|
43610
43698
|
if (validationError) return { success: false, error: validationError };
|
|
43611
43699
|
const absVideo = resolve38(process.cwd(), options.videoPath);
|
|
43612
|
-
if (!
|
|
43700
|
+
if (!existsSync32(absVideo)) return { success: false, error: `Video file not found: ${absVideo}` };
|
|
43613
43701
|
const outputPath = options.output ? resolve38(process.cwd(), options.output) : defaultOverlayOutput(options.videoPath);
|
|
43614
43702
|
if (options.asset) {
|
|
43615
43703
|
const absAsset = resolve38(process.cwd(), options.asset);
|
|
43616
|
-
if (!
|
|
43704
|
+
if (!existsSync32(absAsset)) return { success: false, error: `Lottie asset not found: ${absAsset}` };
|
|
43617
43705
|
const [videoDuration, size] = await Promise.all([
|
|
43618
43706
|
ffprobeDuration(absVideo),
|
|
43619
43707
|
ffprobeVideoSize(absVideo)
|
|
@@ -43780,7 +43868,7 @@ __export(edit_cmd_exports, {
|
|
|
43780
43868
|
executeUpscale: () => executeUpscale
|
|
43781
43869
|
});
|
|
43782
43870
|
import { resolve as resolve39, dirname as dirname15 } from "node:path";
|
|
43783
|
-
import { readFile as
|
|
43871
|
+
import { readFile as readFile27, writeFile as writeFile36, mkdir as mkdir20 } from "node:fs/promises";
|
|
43784
43872
|
async function executeGrade(options) {
|
|
43785
43873
|
const { videoPath, style, preset, output, analyzeOnly, apiKey } = options;
|
|
43786
43874
|
try {
|
|
@@ -43821,7 +43909,7 @@ async function executeSpeedRamp(options) {
|
|
|
43821
43909
|
await execSafe("ffmpeg", ["-i", absPath, "-vn", "-acodec", "libmp3lame", "-q:a", "2", tempAudio, "-y"]);
|
|
43822
43910
|
const whisper = new WhisperProvider();
|
|
43823
43911
|
await whisper.initialize({ apiKey: openaiKey });
|
|
43824
|
-
const audioBuffer = await
|
|
43912
|
+
const audioBuffer = await readFile27(tempAudio);
|
|
43825
43913
|
const transcript = await whisper.transcribe(new Blob([audioBuffer]), language);
|
|
43826
43914
|
try {
|
|
43827
43915
|
const { unlink: unlink5 } = await import("node:fs/promises");
|
|
@@ -44215,7 +44303,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
44215
44303
|
spinner2.text = "Transcribing audio...";
|
|
44216
44304
|
const whisper = new WhisperProvider();
|
|
44217
44305
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
44218
|
-
const audioBuffer = await
|
|
44306
|
+
const audioBuffer = await readFile27(tempAudio);
|
|
44219
44307
|
const audioBlob = new Blob([audioBuffer]);
|
|
44220
44308
|
const transcript = await whisper.transcribe(audioBlob, options.language);
|
|
44221
44309
|
if (!transcript.segments || transcript.segments.length === 0) {
|
|
@@ -44348,7 +44436,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
44348
44436
|
for (let i = 1; i <= numKeyframes && i <= 30; i++) {
|
|
44349
44437
|
const framePath = `${tempDir}/frame-${i.toString().padStart(4, "0")}.jpg`;
|
|
44350
44438
|
try {
|
|
44351
|
-
const frameBuffer = await
|
|
44439
|
+
const frameBuffer = await readFile27(framePath);
|
|
44352
44440
|
const frameBase64 = frameBuffer.toString("base64");
|
|
44353
44441
|
const result = await claude.analyzeFrameForReframe(frameBase64, options.aspect, {
|
|
44354
44442
|
focusMode: options.focus,
|
|
@@ -44477,7 +44565,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
44477
44565
|
const imageBuffers = [];
|
|
44478
44566
|
for (const imagePath of imagePaths) {
|
|
44479
44567
|
const absPath = resolve39(process.cwd(), imagePath);
|
|
44480
|
-
const buffer = await
|
|
44568
|
+
const buffer = await readFile27(absPath);
|
|
44481
44569
|
imageBuffers.push(buffer);
|
|
44482
44570
|
}
|
|
44483
44571
|
let result;
|
|
@@ -44753,9 +44841,9 @@ __export(ai_analyze_exports, {
|
|
|
44753
44841
|
executeGeminiVideo: () => executeGeminiVideo,
|
|
44754
44842
|
registerAnalyzeCommands: () => registerAnalyzeCommands
|
|
44755
44843
|
});
|
|
44756
|
-
import { readFile as
|
|
44844
|
+
import { readFile as readFile28 } from "node:fs/promises";
|
|
44757
44845
|
import { extname as extname7, resolve as resolve40 } from "node:path";
|
|
44758
|
-
import { existsSync as
|
|
44846
|
+
import { existsSync as existsSync33 } from "node:fs";
|
|
44759
44847
|
async function executeGeminiVideo(options) {
|
|
44760
44848
|
try {
|
|
44761
44849
|
const apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
|
|
@@ -44769,10 +44857,10 @@ async function executeGeminiVideo(options) {
|
|
|
44769
44857
|
videoData = options.source;
|
|
44770
44858
|
} else {
|
|
44771
44859
|
const absPath = resolve40(process.cwd(), options.source);
|
|
44772
|
-
if (!
|
|
44860
|
+
if (!existsSync33(absPath)) {
|
|
44773
44861
|
return { success: false, error: `File not found: ${absPath}` };
|
|
44774
44862
|
}
|
|
44775
|
-
videoData = await
|
|
44863
|
+
videoData = await readFile28(absPath);
|
|
44776
44864
|
}
|
|
44777
44865
|
const gemini = new GeminiProvider();
|
|
44778
44866
|
await gemini.initialize({ apiKey });
|
|
@@ -44837,10 +44925,10 @@ async function executeAnalyze(options) {
|
|
|
44837
44925
|
imageBuffer = Buffer.from(await response.arrayBuffer());
|
|
44838
44926
|
} else {
|
|
44839
44927
|
const absPath = resolve40(process.cwd(), source);
|
|
44840
|
-
if (!
|
|
44928
|
+
if (!existsSync33(absPath)) {
|
|
44841
44929
|
return { success: false, error: `File not found: ${absPath}` };
|
|
44842
44930
|
}
|
|
44843
|
-
imageBuffer = await
|
|
44931
|
+
imageBuffer = await readFile28(absPath);
|
|
44844
44932
|
}
|
|
44845
44933
|
const result2 = await gemini.analyzeImage(imageBuffer, options.prompt, {
|
|
44846
44934
|
model: modelId,
|
|
@@ -44872,10 +44960,10 @@ async function executeAnalyze(options) {
|
|
|
44872
44960
|
videoData = Buffer.from(await response.arrayBuffer());
|
|
44873
44961
|
} else {
|
|
44874
44962
|
const absPath = resolve40(process.cwd(), source);
|
|
44875
|
-
if (!
|
|
44963
|
+
if (!existsSync33(absPath)) {
|
|
44876
44964
|
return { success: false, error: `File not found: ${absPath}` };
|
|
44877
44965
|
}
|
|
44878
|
-
videoData = await
|
|
44966
|
+
videoData = await readFile28(absPath);
|
|
44879
44967
|
}
|
|
44880
44968
|
const result = await gemini.analyzeVideo(videoData, options.prompt, {
|
|
44881
44969
|
model: modelId,
|
|
@@ -45009,9 +45097,9 @@ __export(ai_review_exports, {
|
|
|
45009
45097
|
executeReview: () => executeReview,
|
|
45010
45098
|
registerReviewCommand: () => registerReviewCommand
|
|
45011
45099
|
});
|
|
45012
|
-
import { readFile as
|
|
45100
|
+
import { readFile as readFile29, rename as rename2 } from "node:fs/promises";
|
|
45013
45101
|
import { resolve as resolve41 } from "node:path";
|
|
45014
|
-
import { existsSync as
|
|
45102
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
45015
45103
|
function parseReviewFeedback(response) {
|
|
45016
45104
|
let cleaned = response.trim();
|
|
45017
45105
|
if (cleaned.startsWith("```json")) {
|
|
@@ -45043,7 +45131,7 @@ async function executeReview(options) {
|
|
|
45043
45131
|
model = "flash"
|
|
45044
45132
|
} = options;
|
|
45045
45133
|
const absVideoPath = resolve41(process.cwd(), videoPath);
|
|
45046
|
-
if (!
|
|
45134
|
+
if (!existsSync34(absVideoPath)) {
|
|
45047
45135
|
return { success: false, error: `Video not found: ${absVideoPath}` };
|
|
45048
45136
|
}
|
|
45049
45137
|
const apiKey = process.env.GOOGLE_API_KEY || await getApiKey("GOOGLE_API_KEY", "Google");
|
|
@@ -45056,8 +45144,8 @@ async function executeReview(options) {
|
|
|
45056
45144
|
let storyboardContext = "";
|
|
45057
45145
|
if (storyboardPath2) {
|
|
45058
45146
|
const absStoryboardPath = resolve41(process.cwd(), storyboardPath2);
|
|
45059
|
-
if (
|
|
45060
|
-
const content = await
|
|
45147
|
+
if (existsSync34(absStoryboardPath)) {
|
|
45148
|
+
const content = await readFile29(absStoryboardPath, "utf-8");
|
|
45061
45149
|
storyboardContext = `
|
|
45062
45150
|
|
|
45063
45151
|
Original storyboard for reference:
|
|
@@ -45099,7 +45187,7 @@ ${projectContext}` : "";
|
|
|
45099
45187
|
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}`;
|
|
45100
45188
|
const gemini = new GeminiProvider();
|
|
45101
45189
|
await gemini.initialize({ apiKey });
|
|
45102
|
-
const videoData = await
|
|
45190
|
+
const videoData = await readFile29(absVideoPath);
|
|
45103
45191
|
const analysisResult = await gemini.analyzeVideo(videoData, reviewPrompt, {
|
|
45104
45192
|
model: modelId
|
|
45105
45193
|
});
|
|
@@ -45154,7 +45242,7 @@ Score each category 1-10. Prefer beatIssues when you can map a problem to a stor
|
|
|
45154
45242
|
}
|
|
45155
45243
|
}
|
|
45156
45244
|
if (verify && result.outputPath) {
|
|
45157
|
-
const verifyVideoData = await
|
|
45245
|
+
const verifyVideoData = await readFile29(result.outputPath);
|
|
45158
45246
|
const verifyResult = await gemini.analyzeVideo(
|
|
45159
45247
|
verifyVideoData,
|
|
45160
45248
|
'Rate this video overall quality on a scale of 1-10. Return ONLY a JSON object: {"score": <number>}',
|
|
@@ -45282,9 +45370,9 @@ var init_ai_review = __esm({
|
|
|
45282
45370
|
});
|
|
45283
45371
|
|
|
45284
45372
|
// src/commands/_shared/scene-inspect.ts
|
|
45285
|
-
import { existsSync as
|
|
45286
|
-
import { readdir as readdir4, readFile as
|
|
45287
|
-
import { basename as basename10, isAbsolute as isAbsolute3, join as
|
|
45373
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
45374
|
+
import { readdir as readdir4, readFile as readFile30, stat as stat5 } from "node:fs/promises";
|
|
45375
|
+
import { basename as basename10, isAbsolute as isAbsolute3, join as join19, relative as relative8, resolve as resolve42 } from "node:path";
|
|
45288
45376
|
async function inspectProject(opts) {
|
|
45289
45377
|
const projectDir = resolve42(opts.projectDir);
|
|
45290
45378
|
const issues = [];
|
|
@@ -45298,7 +45386,7 @@ async function inspectProject(opts) {
|
|
|
45298
45386
|
assets: { checked: 0, missing: [], stale: [], unknownFreshness: [] },
|
|
45299
45387
|
rootSync: { ok: null, issueCount: 0, issues: [] }
|
|
45300
45388
|
};
|
|
45301
|
-
if (!
|
|
45389
|
+
if (!existsSync35(projectDir) || !await isDirectory(projectDir)) {
|
|
45302
45390
|
issues.push({
|
|
45303
45391
|
severity: "error",
|
|
45304
45392
|
code: "PROJECT_NOT_FOUND",
|
|
@@ -45313,15 +45401,15 @@ async function inspectProject(opts) {
|
|
|
45313
45401
|
);
|
|
45314
45402
|
}
|
|
45315
45403
|
const coreFiles = {
|
|
45316
|
-
storyboard:
|
|
45317
|
-
design:
|
|
45318
|
-
config:
|
|
45319
|
-
legacyConfig:
|
|
45320
|
-
buildReport:
|
|
45321
|
-
root:
|
|
45404
|
+
storyboard: join19(projectDir, "STORYBOARD.md"),
|
|
45405
|
+
design: join19(projectDir, "DESIGN.md"),
|
|
45406
|
+
config: join19(projectDir, "vibe.config.json"),
|
|
45407
|
+
legacyConfig: join19(projectDir, "vibe.project.yaml"),
|
|
45408
|
+
buildReport: join19(projectDir, "build-report.json"),
|
|
45409
|
+
root: join19(projectDir, "index.html")
|
|
45322
45410
|
};
|
|
45323
45411
|
for (const [name, path2] of Object.entries(coreFiles)) {
|
|
45324
|
-
checks.files[name] =
|
|
45412
|
+
checks.files[name] = existsSync35(path2);
|
|
45325
45413
|
}
|
|
45326
45414
|
let beatIds = [];
|
|
45327
45415
|
let inspectedBeatIds = [];
|
|
@@ -45335,7 +45423,7 @@ async function inspectProject(opts) {
|
|
|
45335
45423
|
});
|
|
45336
45424
|
retryWith.push(`vibe init ${projectDir} --from "<brief>" --json`);
|
|
45337
45425
|
} else {
|
|
45338
|
-
const storyboard = await
|
|
45426
|
+
const storyboard = await readFile30(coreFiles.storyboard, "utf-8");
|
|
45339
45427
|
const validation = validateStoryboardMarkdown(storyboard);
|
|
45340
45428
|
storyboardBeats = validation.beats;
|
|
45341
45429
|
beatIds = validation.beats.map((beat) => beat.id);
|
|
@@ -45377,7 +45465,7 @@ async function inspectProject(opts) {
|
|
|
45377
45465
|
suggestedFix: "Create DESIGN.md or rerun `vibe init --from` in a new project."
|
|
45378
45466
|
});
|
|
45379
45467
|
} else {
|
|
45380
|
-
const design = await
|
|
45468
|
+
const design = await readFile30(coreFiles.design, "utf-8");
|
|
45381
45469
|
for (const issue of designPlaceholderIssues(design)) {
|
|
45382
45470
|
issues.push(issue);
|
|
45383
45471
|
}
|
|
@@ -45402,20 +45490,20 @@ async function inspectProject(opts) {
|
|
|
45402
45490
|
});
|
|
45403
45491
|
retryWith.push(`vibe build ${projectDir} --stage sync --json`);
|
|
45404
45492
|
}
|
|
45405
|
-
const compositionsDir =
|
|
45406
|
-
const existingComps =
|
|
45493
|
+
const compositionsDir = join19(projectDir, "compositions");
|
|
45494
|
+
const existingComps = existsSync35(compositionsDir) ? new Set(await listHtmlBasenames(compositionsDir)) : /* @__PURE__ */ new Set();
|
|
45407
45495
|
checks.compositions.expected = inspectedBeatIds.length;
|
|
45408
45496
|
for (const beatId of inspectedBeatIds) {
|
|
45409
45497
|
const file = `scene-${beatId}.html`;
|
|
45410
45498
|
if (existingComps.has(file)) {
|
|
45411
45499
|
checks.compositions.found++;
|
|
45412
45500
|
} else {
|
|
45413
|
-
checks.compositions.missing.push(
|
|
45501
|
+
checks.compositions.missing.push(join19("compositions", file));
|
|
45414
45502
|
issues.push({
|
|
45415
45503
|
severity: "error",
|
|
45416
45504
|
code: "MISSING_COMPOSITION",
|
|
45417
45505
|
message: `Composition for beat "${beatId}" is missing.`,
|
|
45418
|
-
file:
|
|
45506
|
+
file: join19("compositions", file),
|
|
45419
45507
|
scene: beatId,
|
|
45420
45508
|
beatId,
|
|
45421
45509
|
suggestedFix: "Run `vibe build --stage compose --json`."
|
|
@@ -45477,7 +45565,7 @@ async function inspectProject(opts) {
|
|
|
45477
45565
|
try {
|
|
45478
45566
|
const lint = await runProjectLint({ projectDir });
|
|
45479
45567
|
const lintFiles = opts.beatId ? lint.files.filter(
|
|
45480
|
-
(file) => file.file === "index.html" || file.file ===
|
|
45568
|
+
(file) => file.file === "index.html" || file.file === join19("compositions", `scene-${opts.beatId}.html`)
|
|
45481
45569
|
) : lint.files;
|
|
45482
45570
|
const lintCounts = countLintFindings(lintFiles);
|
|
45483
45571
|
checks.lint = {
|
|
@@ -45623,7 +45711,7 @@ function projectSourceReports(checks) {
|
|
|
45623
45711
|
async function inspectBuildReport(projectDir, reportPath, beatId, checks, issues, retryWith, storyboardBeats) {
|
|
45624
45712
|
checks.buildReport.exists = true;
|
|
45625
45713
|
try {
|
|
45626
|
-
const report = JSON.parse(await
|
|
45714
|
+
const report = JSON.parse(await readFile30(reportPath, "utf-8"));
|
|
45627
45715
|
if (typeof report.outputPath === "string") checks.buildReport.outputPath = report.outputPath;
|
|
45628
45716
|
const reportBeats = report.beats ?? [];
|
|
45629
45717
|
const selectedReportBeats = reportBeats.filter((item) => !beatId || item.id === beatId);
|
|
@@ -45760,7 +45848,7 @@ function inspectReportedAsset(opts) {
|
|
|
45760
45848
|
opts.checks.assets.checked++;
|
|
45761
45849
|
if (isExternalRef(opts.value)) return;
|
|
45762
45850
|
const abs = isAbsolute3(opts.value) ? opts.value : resolve42(opts.projectDir, opts.value);
|
|
45763
|
-
if (
|
|
45851
|
+
if (existsSync35(abs)) return;
|
|
45764
45852
|
opts.checks.assets.missing.push(opts.value);
|
|
45765
45853
|
opts.issues.push({
|
|
45766
45854
|
severity: "warning",
|
|
@@ -45859,9 +45947,9 @@ var init_scene_inspect = __esm({
|
|
|
45859
45947
|
});
|
|
45860
45948
|
|
|
45861
45949
|
// src/commands/_shared/render-inspect.ts
|
|
45862
|
-
import { existsSync as
|
|
45863
|
-
import { readdir as readdir5, readFile as
|
|
45864
|
-
import { basename as basename11, isAbsolute as isAbsolute4, join as
|
|
45950
|
+
import { existsSync as existsSync36 } from "node:fs";
|
|
45951
|
+
import { readdir as readdir5, readFile as readFile31, stat as stat6 } from "node:fs/promises";
|
|
45952
|
+
import { basename as basename11, isAbsolute as isAbsolute4, join as join20, relative as relative9, resolve as resolve43 } from "node:path";
|
|
45865
45953
|
function parseBlackdetectOutput(output) {
|
|
45866
45954
|
const ranges = [];
|
|
45867
45955
|
const regex2 = /black_start:(-?\d+(?:\.\d+)?)\s+black_end:(-?\d+(?:\.\d+)?)\s+black_duration:(-?\d+(?:\.\d+)?)/g;
|
|
@@ -46347,11 +46435,11 @@ async function inspectRender(opts) {
|
|
|
46347
46435
|
}
|
|
46348
46436
|
async function renderExpectsAudio(projectDir, beatId) {
|
|
46349
46437
|
const root = resolve43(projectDir);
|
|
46350
|
-
const rootHtml = await readTextIfExists(
|
|
46438
|
+
const rootHtml = await readTextIfExists(join20(root, "index.html"));
|
|
46351
46439
|
if (rootHtml && rootHtmlExpectsAudio(rootHtml, beatId)) return true;
|
|
46352
|
-
const buildReport = await readJsonIfExists(
|
|
46440
|
+
const buildReport = await readJsonIfExists(join20(root, "build-report.json"));
|
|
46353
46441
|
if (buildReportExpectsAudio(buildReport, beatId)) return true;
|
|
46354
|
-
const storyboard = await readTextIfExists(
|
|
46442
|
+
const storyboard = await readTextIfExists(join20(root, "STORYBOARD.md"));
|
|
46355
46443
|
if (!storyboard) return false;
|
|
46356
46444
|
try {
|
|
46357
46445
|
const parsed = parseStoryboard(storyboard);
|
|
@@ -46362,7 +46450,7 @@ async function renderExpectsAudio(projectDir, beatId) {
|
|
|
46362
46450
|
`assets/narration-${beat.id}.wav`,
|
|
46363
46451
|
`assets/music-${beat.id}.mp3`,
|
|
46364
46452
|
`assets/music-${beat.id}.wav`
|
|
46365
|
-
].some((asset) =>
|
|
46453
|
+
].some((asset) => existsSync36(join20(root, asset)))
|
|
46366
46454
|
);
|
|
46367
46455
|
} catch {
|
|
46368
46456
|
return false;
|
|
@@ -46470,8 +46558,8 @@ async function maybeWriteRenderReport(projectDir, opts, result) {
|
|
|
46470
46558
|
}
|
|
46471
46559
|
function renderSourceReports(projectDir, videoPath, checks) {
|
|
46472
46560
|
const reports = [];
|
|
46473
|
-
if (
|
|
46474
|
-
if (
|
|
46561
|
+
if (existsSync36(join20(projectDir, "build-report.json"))) reports.push("build-report.json");
|
|
46562
|
+
if (existsSync36(join20(projectDir, "render-report.json"))) reports.push("render-report.json");
|
|
46475
46563
|
if (videoPath) reports.push(displayPath(videoPath));
|
|
46476
46564
|
if (checks.durationSec !== void 0 || checks.width !== void 0 || checks.height !== void 0) {
|
|
46477
46565
|
reports.push("ffprobe");
|
|
@@ -46484,31 +46572,31 @@ function renderSourceReports(projectDir, videoPath, checks) {
|
|
|
46484
46572
|
async function resolveRenderVideoPath(projectDir, explicit, beatId) {
|
|
46485
46573
|
if (explicit) {
|
|
46486
46574
|
const candidate = resolve43(process.cwd(), explicit);
|
|
46487
|
-
return
|
|
46575
|
+
return existsSync36(candidate) ? candidate : null;
|
|
46488
46576
|
}
|
|
46489
|
-
const renderReportPath =
|
|
46490
|
-
if (
|
|
46577
|
+
const renderReportPath = join20(projectDir, "render-report.json");
|
|
46578
|
+
if (existsSync36(renderReportPath)) {
|
|
46491
46579
|
try {
|
|
46492
|
-
const report = JSON.parse(await
|
|
46580
|
+
const report = JSON.parse(await readFile31(renderReportPath, "utf-8"));
|
|
46493
46581
|
const reportBeat = typeof report.beat === "string" ? report.beat : null;
|
|
46494
46582
|
if (!beatId && reportBeat === null || beatId && reportBeat === beatId) {
|
|
46495
46583
|
const candidate = resolveReportedPath(projectDir, report.outputPath);
|
|
46496
|
-
if (candidate &&
|
|
46584
|
+
if (candidate && existsSync36(candidate)) return candidate;
|
|
46497
46585
|
}
|
|
46498
46586
|
} catch {
|
|
46499
46587
|
}
|
|
46500
46588
|
}
|
|
46501
|
-
const reportPath =
|
|
46502
|
-
if (!beatId &&
|
|
46589
|
+
const reportPath = join20(projectDir, "build-report.json");
|
|
46590
|
+
if (!beatId && existsSync36(reportPath)) {
|
|
46503
46591
|
try {
|
|
46504
|
-
const report = JSON.parse(await
|
|
46592
|
+
const report = JSON.parse(await readFile31(reportPath, "utf-8"));
|
|
46505
46593
|
const candidate = resolveReportedPath(projectDir, report.outputPath);
|
|
46506
|
-
if (candidate &&
|
|
46594
|
+
if (candidate && existsSync36(candidate)) return candidate;
|
|
46507
46595
|
} catch {
|
|
46508
46596
|
}
|
|
46509
46597
|
}
|
|
46510
|
-
const rendersDir =
|
|
46511
|
-
if (!
|
|
46598
|
+
const rendersDir = join20(projectDir, "renders");
|
|
46599
|
+
if (!existsSync36(rendersDir)) return null;
|
|
46512
46600
|
const entries = await readdir5(rendersDir, { withFileTypes: true });
|
|
46513
46601
|
const candidates = [];
|
|
46514
46602
|
for (const entry of entries) {
|
|
@@ -46516,7 +46604,7 @@ async function resolveRenderVideoPath(projectDir, explicit, beatId) {
|
|
|
46516
46604
|
const ext = entry.name.slice(entry.name.lastIndexOf(".")).toLowerCase();
|
|
46517
46605
|
if (!VIDEO_EXTENSIONS.has(ext)) continue;
|
|
46518
46606
|
if (beatId && !entry.name.includes(sanitizeFileSegment(beatId))) continue;
|
|
46519
|
-
const full =
|
|
46607
|
+
const full = join20(rendersDir, entry.name);
|
|
46520
46608
|
const info = await stat6(full);
|
|
46521
46609
|
candidates.push({ path: full, mtimeMs: info.mtimeMs });
|
|
46522
46610
|
}
|
|
@@ -46534,14 +46622,14 @@ async function runAiRenderReview(projectDir, videoPath, model) {
|
|
|
46534
46622
|
});
|
|
46535
46623
|
}
|
|
46536
46624
|
function resolveStoryboardPath(projectDir) {
|
|
46537
|
-
const storyboardPath2 =
|
|
46538
|
-
return
|
|
46625
|
+
const storyboardPath2 = join20(projectDir, "STORYBOARD.md");
|
|
46626
|
+
return existsSync36(storyboardPath2) ? storyboardPath2 : null;
|
|
46539
46627
|
}
|
|
46540
46628
|
async function expectedDurationFromBuildReport(projectDir, beatId) {
|
|
46541
|
-
const reportPath =
|
|
46542
|
-
if (!
|
|
46629
|
+
const reportPath = join20(projectDir, "build-report.json");
|
|
46630
|
+
if (!existsSync36(reportPath)) return void 0;
|
|
46543
46631
|
try {
|
|
46544
|
-
const report = JSON.parse(await
|
|
46632
|
+
const report = JSON.parse(await readFile31(reportPath, "utf-8"));
|
|
46545
46633
|
const beats = beatId ? (report.beats ?? []).filter((beat) => beat.id === beatId) : report.beats ?? [];
|
|
46546
46634
|
const durations = beats.map(
|
|
46547
46635
|
(beat) => parseOptionalNumber(beat.sceneDurationSec) ?? parseOptionalNumber(beat.narration?.sceneDurationSec)
|
|
@@ -46553,10 +46641,10 @@ async function expectedDurationFromBuildReport(projectDir, beatId) {
|
|
|
46553
46641
|
}
|
|
46554
46642
|
}
|
|
46555
46643
|
async function beatTimingsFromBuildReport(projectDir, beatId) {
|
|
46556
|
-
const reportPath =
|
|
46557
|
-
if (!
|
|
46644
|
+
const reportPath = join20(projectDir, "build-report.json");
|
|
46645
|
+
if (!existsSync36(reportPath)) return [];
|
|
46558
46646
|
try {
|
|
46559
|
-
const report = JSON.parse(await
|
|
46647
|
+
const report = JSON.parse(await readFile31(reportPath, "utf-8"));
|
|
46560
46648
|
const source = report.beats ?? [];
|
|
46561
46649
|
const selected = beatId ? source.filter((beat) => beat.id === beatId) : source;
|
|
46562
46650
|
let cursor = 0;
|
|
@@ -46582,17 +46670,17 @@ async function beatTimingsFromBuildReport(projectDir, beatId) {
|
|
|
46582
46670
|
}
|
|
46583
46671
|
async function buildRenderReviewProjectContext(projectDir) {
|
|
46584
46672
|
const parts = [];
|
|
46585
|
-
const storyboard = await readTextIfExists(
|
|
46673
|
+
const storyboard = await readTextIfExists(join20(projectDir, "STORYBOARD.md"));
|
|
46586
46674
|
if (storyboard) {
|
|
46587
46675
|
parts.push(`STORYBOARD.md:
|
|
46588
46676
|
${truncateText(storyboard, 4e3)}`);
|
|
46589
46677
|
parts.push(`Storyboard beat cue summary:
|
|
46590
46678
|
${storyboardBeatCueSummary(storyboard)}`);
|
|
46591
46679
|
}
|
|
46592
|
-
const design = await readTextIfExists(
|
|
46680
|
+
const design = await readTextIfExists(join20(projectDir, "DESIGN.md"));
|
|
46593
46681
|
if (design) parts.push(`DESIGN.md:
|
|
46594
46682
|
${truncateText(design, 4e3)}`);
|
|
46595
|
-
const buildReport = await readTextIfExists(
|
|
46683
|
+
const buildReport = await readTextIfExists(join20(projectDir, "build-report.json"));
|
|
46596
46684
|
if (buildReport) parts.push(`build-report.json:
|
|
46597
46685
|
${truncateText(buildReport, 6e3)}`);
|
|
46598
46686
|
const timings = await beatTimingsFromBuildReport(projectDir);
|
|
@@ -46620,9 +46708,9 @@ function storyboardBeatCueSummary(storyboard) {
|
|
|
46620
46708
|
}
|
|
46621
46709
|
}
|
|
46622
46710
|
async function readTextIfExists(path2) {
|
|
46623
|
-
if (!
|
|
46711
|
+
if (!existsSync36(path2)) return null;
|
|
46624
46712
|
try {
|
|
46625
|
-
return await
|
|
46713
|
+
return await readFile31(path2, "utf-8");
|
|
46626
46714
|
} catch {
|
|
46627
46715
|
return null;
|
|
46628
46716
|
}
|
|
@@ -46916,9 +47004,9 @@ var init_video_providers = __esm({
|
|
|
46916
47004
|
});
|
|
46917
47005
|
|
|
46918
47006
|
// src/commands/ai-script-pipeline.ts
|
|
46919
|
-
import { readFile as
|
|
47007
|
+
import { readFile as readFile34, writeFile as writeFile39, unlink as unlink2, rename as rename3 } from "node:fs/promises";
|
|
46920
47008
|
import { resolve as resolve46, extname as extname9 } from "node:path";
|
|
46921
|
-
import { existsSync as
|
|
47009
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
46922
47010
|
async function executeRegenerateScene(options) {
|
|
46923
47011
|
const result = {
|
|
46924
47012
|
success: false,
|
|
@@ -46927,16 +47015,16 @@ async function executeRegenerateScene(options) {
|
|
|
46927
47015
|
};
|
|
46928
47016
|
try {
|
|
46929
47017
|
const outputDir = resolve46(process.cwd(), options.projectDir);
|
|
46930
|
-
if (!
|
|
47018
|
+
if (!existsSync38(outputDir)) {
|
|
46931
47019
|
return { ...result, error: `Project directory not found: ${outputDir}` };
|
|
46932
47020
|
}
|
|
46933
47021
|
const yamlPath = resolve46(outputDir, "storyboard.yaml");
|
|
46934
47022
|
const jsonPath = resolve46(outputDir, "storyboard.json");
|
|
46935
|
-
const storyboardPath2 =
|
|
47023
|
+
const storyboardPath2 = existsSync38(yamlPath) ? yamlPath : existsSync38(jsonPath) ? jsonPath : null;
|
|
46936
47024
|
if (!storyboardPath2) {
|
|
46937
47025
|
return { ...result, error: `Storyboard not found in: ${outputDir} (expected storyboard.yaml or storyboard.json)` };
|
|
46938
47026
|
}
|
|
46939
|
-
const storyboardContent = await
|
|
47027
|
+
const storyboardContent = await readFile34(storyboardPath2, "utf-8");
|
|
46940
47028
|
const segments = storyboardPath2.endsWith(".yaml") ? (0, import_yaml6.parse)(storyboardContent).scenes : JSON.parse(storyboardContent);
|
|
46941
47029
|
for (const sceneNum of options.scenes) {
|
|
46942
47030
|
if (sceneNum < 1 || sceneNum > segments.length) {
|
|
@@ -47025,15 +47113,15 @@ IMPORTANT - Character appearance must match exactly: ${characterDesc}`;
|
|
|
47025
47113
|
const refSceneNum = options.referenceScene;
|
|
47026
47114
|
if (refSceneNum && refSceneNum >= 1 && refSceneNum <= segments.length && refSceneNum !== sceneNum) {
|
|
47027
47115
|
const refImagePath = resolve46(outputDir, `scene-${refSceneNum}.png`);
|
|
47028
|
-
if (
|
|
47029
|
-
referenceImageBuffer = await
|
|
47116
|
+
if (existsSync38(refImagePath)) {
|
|
47117
|
+
referenceImageBuffer = await readFile34(refImagePath);
|
|
47030
47118
|
}
|
|
47031
47119
|
} else if (!refSceneNum) {
|
|
47032
47120
|
for (let i = 1; i <= segments.length; i++) {
|
|
47033
47121
|
if (i !== sceneNum) {
|
|
47034
47122
|
const otherImagePath = resolve46(outputDir, `scene-${i}.png`);
|
|
47035
|
-
if (
|
|
47036
|
-
referenceImageBuffer = await
|
|
47123
|
+
if (existsSync38(otherImagePath)) {
|
|
47124
|
+
referenceImageBuffer = await readFile34(otherImagePath);
|
|
47037
47125
|
break;
|
|
47038
47126
|
}
|
|
47039
47127
|
}
|
|
@@ -47121,11 +47209,11 @@ Generate the single-person scene image now.`;
|
|
|
47121
47209
|
}
|
|
47122
47210
|
if (regenerateVideo && videoApiKey) {
|
|
47123
47211
|
options.onProgress?.(`Scene ${sceneNum}: regenerating video (${options.generator || "grok"})...`);
|
|
47124
|
-
if (!
|
|
47212
|
+
if (!existsSync38(imagePath)) {
|
|
47125
47213
|
result.failedScenes.push(sceneNum);
|
|
47126
47214
|
continue;
|
|
47127
47215
|
}
|
|
47128
|
-
const imageBuffer = await
|
|
47216
|
+
const imageBuffer = await readFile34(imagePath);
|
|
47129
47217
|
const videoDuration = segment.duration > 5 ? 10 : 5;
|
|
47130
47218
|
const maxRetries = options.retries ?? DEFAULT_VIDEO_RETRIES;
|
|
47131
47219
|
if (options.generator === "grok") {
|
|
@@ -47347,9 +47435,9 @@ var init_ai_script_pipeline = __esm({
|
|
|
47347
47435
|
});
|
|
47348
47436
|
|
|
47349
47437
|
// src/commands/ai-highlights.ts
|
|
47350
|
-
import { readFile as
|
|
47438
|
+
import { readFile as readFile36, writeFile as writeFile41, mkdir as mkdir21 } from "node:fs/promises";
|
|
47351
47439
|
import { resolve as resolve48, dirname as dirname17, basename as basename13, extname as extname10 } from "node:path";
|
|
47352
|
-
import { existsSync as
|
|
47440
|
+
import { existsSync as existsSync40 } from "node:fs";
|
|
47353
47441
|
function filterHighlights(highlights, options) {
|
|
47354
47442
|
let filtered = highlights.filter((h) => h.confidence >= options.threshold);
|
|
47355
47443
|
filtered.sort((a, b) => b.confidence - a.confidence);
|
|
@@ -47389,7 +47477,7 @@ function truncate(text, maxLength) {
|
|
|
47389
47477
|
async function executeHighlights(options) {
|
|
47390
47478
|
try {
|
|
47391
47479
|
const absPath = resolve48(process.cwd(), options.media);
|
|
47392
|
-
if (!
|
|
47480
|
+
if (!existsSync40(absPath)) {
|
|
47393
47481
|
return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: `File not found: ${absPath}` };
|
|
47394
47482
|
}
|
|
47395
47483
|
const ext = extname10(absPath).toLowerCase();
|
|
@@ -47408,7 +47496,7 @@ async function executeHighlights(options) {
|
|
|
47408
47496
|
sourceDuration = await ffprobeDuration(absPath);
|
|
47409
47497
|
const gemini = new GeminiProvider();
|
|
47410
47498
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
47411
|
-
const videoBuffer = await
|
|
47499
|
+
const videoBuffer = await readFile36(absPath);
|
|
47412
47500
|
const criteriaText = options.criteria === "all" || !options.criteria ? "emotional, informative, and funny moments" : `${options.criteria} moments`;
|
|
47413
47501
|
const durationText = targetDuration ? `Target a total highlight duration of ${targetDuration} seconds.` : "";
|
|
47414
47502
|
const countText = maxCount ? `Find up to ${maxCount} highlights.` : "";
|
|
@@ -47502,10 +47590,10 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
47502
47590
|
}
|
|
47503
47591
|
const whisper = new WhisperProvider();
|
|
47504
47592
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
47505
|
-
const audioBuffer = await
|
|
47593
|
+
const audioBuffer = await readFile36(audioPath);
|
|
47506
47594
|
const audioBlob = new Blob([audioBuffer]);
|
|
47507
47595
|
const transcriptResult = await whisper.transcribe(audioBlob, options.language);
|
|
47508
|
-
if (tempAudioPath &&
|
|
47596
|
+
if (tempAudioPath && existsSync40(tempAudioPath)) {
|
|
47509
47597
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
47510
47598
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
47511
47599
|
});
|
|
@@ -47592,7 +47680,7 @@ async function executeAutoShorts(options) {
|
|
|
47592
47680
|
return { success: false, shorts: [], error: "FFmpeg not found" };
|
|
47593
47681
|
}
|
|
47594
47682
|
const absPath = resolve48(process.cwd(), options.video);
|
|
47595
|
-
if (!
|
|
47683
|
+
if (!existsSync40(absPath)) {
|
|
47596
47684
|
return { success: false, shorts: [], error: `File not found: ${absPath}` };
|
|
47597
47685
|
}
|
|
47598
47686
|
const targetDuration = options.duration ?? 60;
|
|
@@ -47605,7 +47693,7 @@ async function executeAutoShorts(options) {
|
|
|
47605
47693
|
}
|
|
47606
47694
|
const gemini = new GeminiProvider();
|
|
47607
47695
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
47608
|
-
const videoBuffer = await
|
|
47696
|
+
const videoBuffer = await readFile36(absPath);
|
|
47609
47697
|
const geminiPrompt = `Analyze this video to find the BEST moments for short-form vertical video content (TikTok, YouTube Shorts, Instagram Reels).
|
|
47610
47698
|
|
|
47611
47699
|
Find ${shortCount * 3} potential clips that are ${targetDuration} seconds or shorter each.
|
|
@@ -47682,7 +47770,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
47682
47770
|
await execSafe("ffmpeg", ["-i", absPath, "-vn", "-acodec", "libmp3lame", "-q:a", "2", tempAudio, "-y"]);
|
|
47683
47771
|
const whisper = new WhisperProvider();
|
|
47684
47772
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
47685
|
-
const audioBuffer = await
|
|
47773
|
+
const audioBuffer = await readFile36(tempAudio);
|
|
47686
47774
|
const audioBlob = new Blob([audioBuffer]);
|
|
47687
47775
|
const transcript = await whisper.transcribe(audioBlob, options.language);
|
|
47688
47776
|
try {
|
|
@@ -47720,7 +47808,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
47720
47808
|
};
|
|
47721
47809
|
}
|
|
47722
47810
|
const outputDir = options.outputDir ? resolve48(process.cwd(), options.outputDir) : dirname17(absPath);
|
|
47723
|
-
if (options.outputDir && !
|
|
47811
|
+
if (options.outputDir && !existsSync40(outputDir)) {
|
|
47724
47812
|
await mkdir21(outputDir, { recursive: true });
|
|
47725
47813
|
}
|
|
47726
47814
|
const result = {
|
|
@@ -47803,7 +47891,7 @@ function registerHighlightsCommands(aiCommand) {
|
|
|
47803
47891
|
validateOutputPath(options.output);
|
|
47804
47892
|
}
|
|
47805
47893
|
const absPath = resolve48(process.cwd(), mediaPath);
|
|
47806
|
-
if (!
|
|
47894
|
+
if (!existsSync40(absPath)) {
|
|
47807
47895
|
exitWithError(notFoundError(absPath));
|
|
47808
47896
|
}
|
|
47809
47897
|
if (options.dryRun) {
|
|
@@ -47860,7 +47948,7 @@ function registerHighlightsCommands(aiCommand) {
|
|
|
47860
47948
|
const geminiSpinner = ora("\u{1F3AC} Analyzing video with Gemini (visual + audio)...").start();
|
|
47861
47949
|
const gemini = new GeminiProvider();
|
|
47862
47950
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
47863
|
-
const videoBuffer = await
|
|
47951
|
+
const videoBuffer = await readFile36(absPath);
|
|
47864
47952
|
const criteriaText = options.criteria === "all" ? "emotional, informative, and funny moments" : `${options.criteria} moments`;
|
|
47865
47953
|
const durationText = targetDuration ? `Target a total highlight duration of ${targetDuration} seconds.` : "";
|
|
47866
47954
|
const countText = maxCount ? `Find up to ${maxCount} highlights.` : "";
|
|
@@ -47985,12 +48073,12 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
47985
48073
|
const transcribeSpinner = ora("\u{1F4DD} Transcribing with Whisper...").start();
|
|
47986
48074
|
const whisper = new WhisperProvider();
|
|
47987
48075
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
47988
|
-
const audioBuffer = await
|
|
48076
|
+
const audioBuffer = await readFile36(audioPath);
|
|
47989
48077
|
const audioBlob = new Blob([audioBuffer]);
|
|
47990
48078
|
const transcriptResult = await whisper.transcribe(audioBlob, options.language);
|
|
47991
48079
|
if (transcriptResult.status === "failed" || !transcriptResult.segments) {
|
|
47992
48080
|
transcribeSpinner.fail("Transcription failed");
|
|
47993
|
-
if (tempAudioPath &&
|
|
48081
|
+
if (tempAudioPath && existsSync40(tempAudioPath)) {
|
|
47994
48082
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
47995
48083
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
47996
48084
|
});
|
|
@@ -47998,7 +48086,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
47998
48086
|
exitWithError(apiError(`Transcription failed: ${transcriptResult.error}`, true));
|
|
47999
48087
|
}
|
|
48000
48088
|
transcribeSpinner.succeed(source_default.green(`Transcribed ${transcriptResult.segments.length} segments`));
|
|
48001
|
-
if (tempAudioPath &&
|
|
48089
|
+
if (tempAudioPath && existsSync40(tempAudioPath)) {
|
|
48002
48090
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
48003
48091
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
48004
48092
|
});
|
|
@@ -48104,7 +48192,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
48104
48192
|
validateOutputPath(options.output);
|
|
48105
48193
|
}
|
|
48106
48194
|
const absPath = resolve48(process.cwd(), videoPath);
|
|
48107
|
-
if (!
|
|
48195
|
+
if (!existsSync40(absPath)) {
|
|
48108
48196
|
exitWithError(notFoundError(absPath));
|
|
48109
48197
|
}
|
|
48110
48198
|
if (options.dryRun) {
|
|
@@ -48154,7 +48242,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
48154
48242
|
const spinner2 = ora("\u{1F3AC} Analyzing video with Gemini (visual + audio)...").start();
|
|
48155
48243
|
const gemini = new GeminiProvider();
|
|
48156
48244
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
48157
|
-
const videoBuffer = await
|
|
48245
|
+
const videoBuffer = await readFile36(absPath);
|
|
48158
48246
|
const geminiPrompt = `Analyze this video to find the BEST moments for short-form vertical video content (TikTok, YouTube Shorts, Instagram Reels).
|
|
48159
48247
|
|
|
48160
48248
|
Find ${shortCount * 3} potential clips that are ${targetDuration} seconds or shorter each.
|
|
@@ -48251,7 +48339,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
48251
48339
|
spinner2.text = "Transcribing audio...";
|
|
48252
48340
|
const whisper = new WhisperProvider();
|
|
48253
48341
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
48254
|
-
const audioBuffer = await
|
|
48342
|
+
const audioBuffer = await readFile36(tempAudio);
|
|
48255
48343
|
const audioBlob = new Blob([audioBuffer]);
|
|
48256
48344
|
const transcript = await whisper.transcribe(audioBlob, options.language);
|
|
48257
48345
|
try {
|
|
@@ -48297,7 +48385,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
48297
48385
|
return;
|
|
48298
48386
|
}
|
|
48299
48387
|
const outputDir = options.outputDir ? resolve48(process.cwd(), options.outputDir) : dirname17(absPath);
|
|
48300
|
-
if (options.outputDir && !
|
|
48388
|
+
if (options.outputDir && !existsSync40(outputDir)) {
|
|
48301
48389
|
await mkdir21(outputDir, { recursive: true });
|
|
48302
48390
|
}
|
|
48303
48391
|
for (let i = 0; i < selectedHighlights.length; i++) {
|
|
@@ -48314,7 +48402,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
48314
48402
|
outputPath = resolve48(outputDir, `${baseName}-short-${i + 1}.mp4`);
|
|
48315
48403
|
}
|
|
48316
48404
|
const parentDir = dirname17(outputPath);
|
|
48317
|
-
if (!
|
|
48405
|
+
if (!existsSync40(parentDir)) {
|
|
48318
48406
|
await mkdir21(parentDir, { recursive: true });
|
|
48319
48407
|
}
|
|
48320
48408
|
const { stdout: probeOut } = await execSafe("ffprobe", [
|
|
@@ -48389,7 +48477,7 @@ var init_ai_highlights = __esm({
|
|
|
48389
48477
|
// src/commands/ai-animated-caption.ts
|
|
48390
48478
|
import { resolve as resolve49, dirname as dirname18, basename as basename14 } from "node:path";
|
|
48391
48479
|
import { writeFile as writeFile42, mkdir as mkdir22, rm as rm3 } from "node:fs/promises";
|
|
48392
|
-
import { existsSync as
|
|
48480
|
+
import { existsSync as existsSync41 } from "node:fs";
|
|
48393
48481
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
48394
48482
|
function groupWords(words, options = {}) {
|
|
48395
48483
|
if (words.length === 0) return [];
|
|
@@ -48566,7 +48654,7 @@ async function executeAnimatedCaption(options) {
|
|
|
48566
48654
|
const groups = groupWords(transcript.words, { wordsPerGroup, maxChars });
|
|
48567
48655
|
const absOutputPath = resolve49(process.cwd(), outputPath);
|
|
48568
48656
|
const outDir = dirname18(absOutputPath);
|
|
48569
|
-
if (!
|
|
48657
|
+
if (!existsSync41(outDir)) {
|
|
48570
48658
|
await mkdir22(outDir, { recursive: true });
|
|
48571
48659
|
}
|
|
48572
48660
|
if (tier === "ass") {
|
|
@@ -58249,7 +58337,7 @@ __export(detect_exports, {
|
|
|
58249
58337
|
executeDetectScenes: () => executeDetectScenes,
|
|
58250
58338
|
executeDetectSilence: () => executeDetectSilence
|
|
58251
58339
|
});
|
|
58252
|
-
import { readFile as
|
|
58340
|
+
import { readFile as readFile38, writeFile as writeFile44 } from "node:fs/promises";
|
|
58253
58341
|
import { resolve as resolve53, basename as basename18 } from "node:path";
|
|
58254
58342
|
async function executeDetectScenes(options) {
|
|
58255
58343
|
try {
|
|
@@ -58493,7 +58581,7 @@ var init_detect = __esm({
|
|
|
58493
58581
|
}
|
|
58494
58582
|
if (options.project) {
|
|
58495
58583
|
const projectPath = resolve53(process.cwd(), options.project);
|
|
58496
|
-
const content = await
|
|
58584
|
+
const content = await readFile38(projectPath, "utf-8");
|
|
58497
58585
|
const data = JSON.parse(content);
|
|
58498
58586
|
const project = Project.fromJSON(data);
|
|
58499
58587
|
let source = project.getSources().find((s) => s.url === absPath);
|
|
@@ -60184,14 +60272,14 @@ var init_adapters = __esm({
|
|
|
60184
60272
|
});
|
|
60185
60273
|
|
|
60186
60274
|
// src/commands/_shared/storyboard-revise.ts
|
|
60187
|
-
import { existsSync as
|
|
60188
|
-
import { readFile as
|
|
60189
|
-
import { join as
|
|
60275
|
+
import { existsSync as existsSync45 } from "node:fs";
|
|
60276
|
+
import { readFile as readFile42, writeFile as writeFile47 } from "node:fs/promises";
|
|
60277
|
+
import { join as join23, resolve as resolve57 } from "node:path";
|
|
60190
60278
|
async function executeStoryboardRevision(opts) {
|
|
60191
60279
|
const projectDir = resolve57(opts.projectDir);
|
|
60192
|
-
const storyboardPath2 =
|
|
60280
|
+
const storyboardPath2 = join23(projectDir, "STORYBOARD.md");
|
|
60193
60281
|
const retryWith = [`vibe storyboard revise ${projectDir} --from "<request>" --dry-run --json`];
|
|
60194
|
-
if (!
|
|
60282
|
+
if (!existsSync45(storyboardPath2)) {
|
|
60195
60283
|
return failureResult({
|
|
60196
60284
|
projectDir,
|
|
60197
60285
|
storyboardPath: storyboardPath2,
|
|
@@ -60201,7 +60289,7 @@ async function executeStoryboardRevision(opts) {
|
|
|
60201
60289
|
retryWith: [`vibe init ${projectDir} --from "<brief>" --json`]
|
|
60202
60290
|
});
|
|
60203
60291
|
}
|
|
60204
|
-
const currentStoryboard = await
|
|
60292
|
+
const currentStoryboard = await readFile42(storyboardPath2, "utf-8");
|
|
60205
60293
|
const projectConfig = await readProjectConfig(projectDir);
|
|
60206
60294
|
const explicitComposer = opts.composer ?? projectConfig.config.providers.composer ?? void 0;
|
|
60207
60295
|
let provider = explicitComposer ?? "claude";
|
|
@@ -60424,18 +60512,18 @@ function diffChangedBeats(before, after) {
|
|
|
60424
60512
|
return ids.filter((id) => JSON.stringify(beforeBeats.get(id) ?? null) !== JSON.stringify(afterBeats.get(id) ?? null));
|
|
60425
60513
|
}
|
|
60426
60514
|
async function readRevisionContext(projectDir) {
|
|
60427
|
-
const designMd = await readOptionalText(
|
|
60428
|
-
const buildReportJson = await readOptionalJsonText(
|
|
60515
|
+
const designMd = await readOptionalText(join23(projectDir, "DESIGN.md"));
|
|
60516
|
+
const buildReportJson = await readOptionalJsonText(join23(projectDir, "build-report.json"));
|
|
60429
60517
|
return { designMd, buildReportJson };
|
|
60430
60518
|
}
|
|
60431
60519
|
async function readOptionalText(path2) {
|
|
60432
|
-
if (!
|
|
60433
|
-
return
|
|
60520
|
+
if (!existsSync45(path2)) return null;
|
|
60521
|
+
return readFile42(path2, "utf-8");
|
|
60434
60522
|
}
|
|
60435
60523
|
async function readOptionalJsonText(path2) {
|
|
60436
|
-
if (!
|
|
60524
|
+
if (!existsSync45(path2)) return null;
|
|
60437
60525
|
try {
|
|
60438
|
-
const parsed = JSON.parse(await
|
|
60526
|
+
const parsed = JSON.parse(await readFile42(path2, "utf-8"));
|
|
60439
60527
|
return JSON.stringify(parsed, null, 2);
|
|
60440
60528
|
} catch {
|
|
60441
60529
|
return null;
|
|
@@ -60483,7 +60571,7 @@ var init_storyboard_revise = __esm({
|
|
|
60483
60571
|
});
|
|
60484
60572
|
|
|
60485
60573
|
// src/commands/_shared/build-asset-reference.ts
|
|
60486
|
-
import { existsSync as
|
|
60574
|
+
import { existsSync as existsSync47 } from "node:fs";
|
|
60487
60575
|
import { extname as extname13, isAbsolute as isAbsolute5, relative as relative10, resolve as resolve59, sep } from "node:path";
|
|
60488
60576
|
function resolveTypedAssetReference(projectDir, kind, value) {
|
|
60489
60577
|
const raw = stringOrUndefined2(value);
|
|
@@ -60514,7 +60602,7 @@ function resolveReference(projectDir, kind, raw, source, forcedError) {
|
|
|
60514
60602
|
const absPath = isAbsolute5(raw) ? resolve59(raw) : resolve59(projectDir, raw);
|
|
60515
60603
|
const rel = relative10(resolve59(projectDir), absPath);
|
|
60516
60604
|
const withinProject = rel.length > 0 && !rel.startsWith("..") && !isAbsolute5(rel);
|
|
60517
|
-
const exists = withinProject &&
|
|
60605
|
+
const exists = withinProject && existsSync47(absPath);
|
|
60518
60606
|
let error = forcedError;
|
|
60519
60607
|
if (!error && inferred && inferred !== kind && !isNarrationAudioReference(kind, ext)) {
|
|
60520
60608
|
error = `Asset reference "${raw}" is a ${inferred} asset, not ${kind}.`;
|
|
@@ -60701,9 +60789,9 @@ var init_build_backdrop_prompt = __esm({
|
|
|
60701
60789
|
});
|
|
60702
60790
|
|
|
60703
60791
|
// src/commands/_shared/build-plan.ts
|
|
60704
|
-
import { existsSync as
|
|
60705
|
-
import { readFile as
|
|
60706
|
-
import { dirname as dirname21, join as
|
|
60792
|
+
import { existsSync as existsSync48 } from "node:fs";
|
|
60793
|
+
import { readFile as readFile44 } from "node:fs/promises";
|
|
60794
|
+
import { dirname as dirname21, join as join24, resolve as resolve60 } from "node:path";
|
|
60707
60795
|
function backdropCostUsd(quality) {
|
|
60708
60796
|
return quality === "hd" ? 0.2 : 0.05;
|
|
60709
60797
|
}
|
|
@@ -60712,11 +60800,11 @@ async function createBuildPlan(opts) {
|
|
|
60712
60800
|
loadPlanEnv(projectDir);
|
|
60713
60801
|
const stage = opts.stage ?? "all";
|
|
60714
60802
|
const config2 = await readProjectConfig(projectDir);
|
|
60715
|
-
const storyboardPath2 =
|
|
60803
|
+
const storyboardPath2 = join24(projectDir, "STORYBOARD.md");
|
|
60716
60804
|
const warnings = [];
|
|
60717
60805
|
const retryWith = [];
|
|
60718
60806
|
const providerResolution = [];
|
|
60719
|
-
if (!
|
|
60807
|
+
if (!existsSync48(storyboardPath2)) {
|
|
60720
60808
|
const validation2 = {
|
|
60721
60809
|
ok: false,
|
|
60722
60810
|
issues: [
|
|
@@ -60745,7 +60833,7 @@ async function createBuildPlan(opts) {
|
|
|
60745
60833
|
validation: validation2
|
|
60746
60834
|
});
|
|
60747
60835
|
}
|
|
60748
|
-
const storyboardMd = await
|
|
60836
|
+
const storyboardMd = await readFile44(storyboardPath2, "utf-8");
|
|
60749
60837
|
const validation = validateStoryboardMarkdown(storyboardMd);
|
|
60750
60838
|
const validationIssues = [...validation.issues];
|
|
60751
60839
|
const parsed = parseStoryboard(storyboardMd);
|
|
@@ -60771,7 +60859,7 @@ async function createBuildPlan(opts) {
|
|
|
60771
60859
|
const imageQuality = opts.imageQuality ?? config2.config.build.imageQuality ?? "hd";
|
|
60772
60860
|
const imageSize2 = opts.imageSize ?? config2.config.build.imageSize ?? "1536x1024";
|
|
60773
60861
|
const imageRatio = imageRatioForSize(imageSize2);
|
|
60774
|
-
const needsComposer = includeCompose && mode !== "agent" && sourceBeats.some((beat) => !
|
|
60862
|
+
const needsComposer = includeCompose && mode !== "agent" && sourceBeats.some((beat) => !existsSync48(join24(projectDir, `compositions/scene-${beat.id}.html`)));
|
|
60775
60863
|
const resolved = await resolvePlanProviders({
|
|
60776
60864
|
projectDir,
|
|
60777
60865
|
config: config2,
|
|
@@ -60919,7 +61007,7 @@ async function createBuildPlan(opts) {
|
|
|
60919
61007
|
active: includeAssets
|
|
60920
61008
|
}) : null;
|
|
60921
61009
|
const compositionPath = `compositions/scene-${beat.id}.html`;
|
|
60922
|
-
const compositionExists =
|
|
61010
|
+
const compositionExists = existsSync48(join24(projectDir, compositionPath));
|
|
60923
61011
|
for (const asset of [narration, backdrop, keyframe, video, music]) {
|
|
60924
61012
|
if (!asset) continue;
|
|
60925
61013
|
if (asset.referenceError) {
|
|
@@ -60988,7 +61076,7 @@ async function createBuildPlan(opts) {
|
|
|
60988
61076
|
}
|
|
60989
61077
|
}
|
|
60990
61078
|
const validationOk = !validationIssues.some((issue) => issue.severity === "error");
|
|
60991
|
-
if (!
|
|
61079
|
+
if (!existsSync48(join24(projectDir, config2.config.composition.entry))) {
|
|
60992
61080
|
missing.add("root-composition");
|
|
60993
61081
|
if (validationOk) retryWith.push(`vibe build ${projectDir} --stage sync --json`);
|
|
60994
61082
|
}
|
|
@@ -61022,7 +61110,7 @@ async function createBuildPlan(opts) {
|
|
|
61022
61110
|
}
|
|
61023
61111
|
if (referenced.size > 0) {
|
|
61024
61112
|
const toGenerate = resolveCharacters(parsed.frontmatter).filter(
|
|
61025
|
-
(c) => referenced.has(c.name) && c.prompt && (opts.force || !
|
|
61113
|
+
(c) => referenced.has(c.name) && c.prompt && (opts.force || !existsSync48(join24(projectDir, `assets/character-${c.name}.png`)))
|
|
61026
61114
|
);
|
|
61027
61115
|
if (toGenerate.length > 0) {
|
|
61028
61116
|
estimatedCostUsd += toGenerate.length * backdropCostUsd(opts.imageQuality ?? "hd");
|
|
@@ -61135,8 +61223,8 @@ function assetPlan(opts) {
|
|
|
61135
61223
|
reason: ready ? "referenced-asset" : "invalid-reference"
|
|
61136
61224
|
};
|
|
61137
61225
|
}
|
|
61138
|
-
const canonicalExists =
|
|
61139
|
-
const cacheHit = opts.cache ?
|
|
61226
|
+
const canonicalExists = existsSync48(join24(opts.projectDir, opts.path));
|
|
61227
|
+
const cacheHit = opts.cache ? existsSync48(join24(opts.projectDir, opts.cache.path)) : false;
|
|
61140
61228
|
const freshness = assetFreshnessFromMetadata({
|
|
61141
61229
|
projectDir: opts.projectDir,
|
|
61142
61230
|
kind: opts.kind,
|
|
@@ -61334,7 +61422,7 @@ async function firstConfiguredComposer(projectDir) {
|
|
|
61334
61422
|
}
|
|
61335
61423
|
function firstExisting2(projectDir, paths) {
|
|
61336
61424
|
for (const path2 of paths) {
|
|
61337
|
-
if (
|
|
61425
|
+
if (existsSync48(join24(projectDir, path2))) return path2;
|
|
61338
61426
|
}
|
|
61339
61427
|
return null;
|
|
61340
61428
|
}
|
|
@@ -61375,12 +61463,12 @@ function unique3(items) {
|
|
|
61375
61463
|
return [...new Set(items.filter((item) => item.length > 0))];
|
|
61376
61464
|
}
|
|
61377
61465
|
function loadPlanEnv(projectDir) {
|
|
61378
|
-
(0, import_dotenv3.config)({ path:
|
|
61466
|
+
(0, import_dotenv3.config)({ path: join24(projectDir, ".env"), quiet: true });
|
|
61379
61467
|
(0, import_dotenv3.config)({ path: resolve60(process.cwd(), ".env"), quiet: true });
|
|
61380
61468
|
let dir = process.cwd();
|
|
61381
61469
|
while (dir !== dirname21(dir)) {
|
|
61382
|
-
if (
|
|
61383
|
-
(0, import_dotenv3.config)({ path:
|
|
61470
|
+
if (existsSync48(join24(dir, "pnpm-workspace.yaml"))) {
|
|
61471
|
+
(0, import_dotenv3.config)({ path: join24(dir, ".env"), quiet: true });
|
|
61384
61472
|
return;
|
|
61385
61473
|
}
|
|
61386
61474
|
dir = dirname21(dir);
|
|
@@ -61461,7 +61549,7 @@ var init_concurrency = __esm({
|
|
|
61461
61549
|
});
|
|
61462
61550
|
|
|
61463
61551
|
// src/commands/_shared/scene-audio-scan.ts
|
|
61464
|
-
import { readFile as
|
|
61552
|
+
import { readFile as readFile45 } from "node:fs/promises";
|
|
61465
61553
|
import { resolve as resolve62 } from "node:path";
|
|
61466
61554
|
function parseRootClips(rootHtml) {
|
|
61467
61555
|
const clips = [];
|
|
@@ -61502,7 +61590,7 @@ function makeFsCompositionReader(projectDir) {
|
|
|
61502
61590
|
return async (compositionSrcRel) => {
|
|
61503
61591
|
const abs = resolve62(projectDir, compositionSrcRel);
|
|
61504
61592
|
try {
|
|
61505
|
-
return await
|
|
61593
|
+
return await readFile45(abs, "utf-8");
|
|
61506
61594
|
} catch {
|
|
61507
61595
|
return null;
|
|
61508
61596
|
}
|
|
@@ -61702,9 +61790,9 @@ __export(scene_render_exports, {
|
|
|
61702
61790
|
qualityToCrf: () => qualityToCrf2
|
|
61703
61791
|
});
|
|
61704
61792
|
import { execFile as execFile2 } from "node:child_process";
|
|
61705
|
-
import { existsSync as
|
|
61706
|
-
import { mkdir as mkdir25, readFile as
|
|
61707
|
-
import { resolve as resolve64, relative as relative11, dirname as dirname23, basename as basename21, join as
|
|
61793
|
+
import { existsSync as existsSync49 } from "node:fs";
|
|
61794
|
+
import { mkdir as mkdir25, readFile as readFile46, stat as stat8, writeFile as writeFile49 } from "node:fs/promises";
|
|
61795
|
+
import { resolve as resolve64, relative as relative11, dirname as dirname23, basename as basename21, join as join25, isAbsolute as isAbsolute6 } from "node:path";
|
|
61708
61796
|
import { promisify as promisify2 } from "node:util";
|
|
61709
61797
|
import {
|
|
61710
61798
|
createRenderJob,
|
|
@@ -61796,7 +61884,7 @@ async function executeSceneRender(opts = {}) {
|
|
|
61796
61884
|
}
|
|
61797
61885
|
let autoSyncApplied = false;
|
|
61798
61886
|
let autoSyncWarning;
|
|
61799
|
-
if (!opts.beatId &&
|
|
61887
|
+
if (!opts.beatId && existsSync49(join25(projectDir, "STORYBOARD.md"))) {
|
|
61800
61888
|
try {
|
|
61801
61889
|
const rootPlan = await createProjectRootSyncPlan({ projectDir });
|
|
61802
61890
|
const subPlans = await createSubCompDurationSyncPlans({
|
|
@@ -61888,7 +61976,7 @@ async function executeSceneRender(opts = {}) {
|
|
|
61888
61976
|
let audioMuxWarning;
|
|
61889
61977
|
try {
|
|
61890
61978
|
opts.onProgress?.(0.95, "Mixing audio");
|
|
61891
|
-
const rootHtml = await
|
|
61979
|
+
const rootHtml = await readFile46(resolve64(projectDir, root), "utf-8");
|
|
61892
61980
|
const audios = await scanSceneAudio({ projectDir, rootHtml });
|
|
61893
61981
|
audioCount = audios.length;
|
|
61894
61982
|
if (audios.length > 0) {
|
|
@@ -61973,11 +62061,11 @@ async function safeStat(p) {
|
|
|
61973
62061
|
}
|
|
61974
62062
|
async function prepareBeatRenderRoot(opts) {
|
|
61975
62063
|
const root = `.vibeframe/tmp/render-beat-${sanitizeFileSegment2(opts.beatId)}.html`;
|
|
61976
|
-
const reportPath =
|
|
62064
|
+
const reportPath = join25(opts.projectDir, "build-report.json");
|
|
61977
62065
|
const retryWith = [
|
|
61978
62066
|
`vibe build ${opts.projectDir} --beat ${opts.beatId} --stage sync --json`
|
|
61979
62067
|
];
|
|
61980
|
-
if (!
|
|
62068
|
+
if (!existsSync49(reportPath)) {
|
|
61981
62069
|
return {
|
|
61982
62070
|
success: false,
|
|
61983
62071
|
root,
|
|
@@ -61988,7 +62076,7 @@ async function prepareBeatRenderRoot(opts) {
|
|
|
61988
62076
|
}
|
|
61989
62077
|
let report;
|
|
61990
62078
|
try {
|
|
61991
|
-
report = JSON.parse(await
|
|
62079
|
+
report = JSON.parse(await readFile46(reportPath, "utf-8"));
|
|
61992
62080
|
} catch (error) {
|
|
61993
62081
|
return {
|
|
61994
62082
|
success: false,
|
|
@@ -62057,8 +62145,8 @@ async function prepareBeatRenderRoot(opts) {
|
|
|
62057
62145
|
durationSec,
|
|
62058
62146
|
narrationPath
|
|
62059
62147
|
});
|
|
62060
|
-
await mkdir25(dirname23(
|
|
62061
|
-
await writeFile49(
|
|
62148
|
+
await mkdir25(dirname23(join25(opts.projectDir, root)), { recursive: true });
|
|
62149
|
+
await writeFile49(join25(opts.projectDir, root), html, "utf-8");
|
|
62062
62150
|
return {
|
|
62063
62151
|
success: true,
|
|
62064
62152
|
root,
|
|
@@ -62122,7 +62210,7 @@ function reportedBeatAssetPaths(beat) {
|
|
|
62122
62210
|
]);
|
|
62123
62211
|
}
|
|
62124
62212
|
async function writeRenderReport(projectDir, result) {
|
|
62125
|
-
const reportPath =
|
|
62213
|
+
const reportPath = join25(projectDir, "render-report.json");
|
|
62126
62214
|
try {
|
|
62127
62215
|
await writeFile49(
|
|
62128
62216
|
reportPath,
|
|
@@ -62164,7 +62252,7 @@ async function writeRenderReport(projectDir, result) {
|
|
|
62164
62252
|
}
|
|
62165
62253
|
function pathExists3(projectDir, value) {
|
|
62166
62254
|
const abs = isAbsolute6(value) ? value : resolve64(projectDir, value);
|
|
62167
|
-
return
|
|
62255
|
+
return existsSync49(abs);
|
|
62168
62256
|
}
|
|
62169
62257
|
function stringValue(value) {
|
|
62170
62258
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
@@ -62223,9 +62311,9 @@ __export(scene_build_exports, {
|
|
|
62223
62311
|
executeSceneBuild: () => executeSceneBuild,
|
|
62224
62312
|
resolveSceneBuildMode: () => resolveSceneBuildMode
|
|
62225
62313
|
});
|
|
62226
|
-
import { existsSync as
|
|
62227
|
-
import { copyFile as copyFile5, mkdir as mkdir26, readFile as
|
|
62228
|
-
import { dirname as dirname24, join as
|
|
62314
|
+
import { existsSync as existsSync50, readFileSync as readFileSync3 } from "node:fs";
|
|
62315
|
+
import { copyFile as copyFile5, mkdir as mkdir26, readFile as readFile47, rm as rm4, writeFile as writeFile50 } from "node:fs/promises";
|
|
62316
|
+
import { dirname as dirname24, join as join26, resolve as resolve65 } from "node:path";
|
|
62229
62317
|
async function executeSceneBuild(opts) {
|
|
62230
62318
|
const startedAt = Date.now();
|
|
62231
62319
|
const projectDir = resolve65(opts.projectDir);
|
|
@@ -62244,7 +62332,7 @@ async function executeSceneBuild(opts) {
|
|
|
62244
62332
|
);
|
|
62245
62333
|
}
|
|
62246
62334
|
let sceneRepair = skippedSceneRepairSummary();
|
|
62247
|
-
const storyboardPath2 =
|
|
62335
|
+
const storyboardPath2 = join26(projectDir, "STORYBOARD.md");
|
|
62248
62336
|
const buildPlan = await createBuildPlan({
|
|
62249
62337
|
projectDir,
|
|
62250
62338
|
stage: selectedStage,
|
|
@@ -62295,8 +62383,8 @@ async function executeSceneBuild(opts) {
|
|
|
62295
62383
|
}
|
|
62296
62384
|
if (!buildPlan.validation.ok) {
|
|
62297
62385
|
let invalidBeats = [];
|
|
62298
|
-
if (
|
|
62299
|
-
invalidBeats = parseStoryboard(await
|
|
62386
|
+
if (existsSync50(storyboardPath2)) {
|
|
62387
|
+
invalidBeats = parseStoryboard(await readFile47(storyboardPath2, "utf-8")).beats;
|
|
62300
62388
|
}
|
|
62301
62389
|
return finishBuildResult({
|
|
62302
62390
|
success: false,
|
|
@@ -62320,7 +62408,7 @@ async function executeSceneBuild(opts) {
|
|
|
62320
62408
|
totalLatencyMs: Date.now() - startedAt
|
|
62321
62409
|
});
|
|
62322
62410
|
}
|
|
62323
|
-
if (!
|
|
62411
|
+
if (!existsSync50(storyboardPath2)) {
|
|
62324
62412
|
return finishBuildResult({
|
|
62325
62413
|
success: false,
|
|
62326
62414
|
phase: "failed",
|
|
@@ -62334,7 +62422,7 @@ async function executeSceneBuild(opts) {
|
|
|
62334
62422
|
totalLatencyMs: Date.now() - startedAt
|
|
62335
62423
|
});
|
|
62336
62424
|
}
|
|
62337
|
-
const storyboardMd = await
|
|
62425
|
+
const storyboardMd = await readFile47(storyboardPath2, "utf-8");
|
|
62338
62426
|
const parsed = parseStoryboard(storyboardMd);
|
|
62339
62427
|
if (parsed.beats.length === 0) {
|
|
62340
62428
|
return finishBuildResult({
|
|
@@ -62441,6 +62529,7 @@ async function executeSceneBuild(opts) {
|
|
|
62441
62529
|
skipVideo: opts.skipVideo ?? false,
|
|
62442
62530
|
skipKeyframe: opts.skipKeyframe ?? false,
|
|
62443
62531
|
skipMusic: opts.skipMusic ?? false,
|
|
62532
|
+
skipTranscript: opts.skipTranscript ?? false,
|
|
62444
62533
|
force: opts.force ?? false,
|
|
62445
62534
|
onProgress,
|
|
62446
62535
|
characterPaths
|
|
@@ -62528,9 +62617,9 @@ async function executeSceneBuild(opts) {
|
|
|
62528
62617
|
let composeData;
|
|
62529
62618
|
if (shouldRunStage(selectedStage, "compose")) {
|
|
62530
62619
|
if (mode === "agent") {
|
|
62531
|
-
const compositionsDir =
|
|
62620
|
+
const compositionsDir = join26(projectDir, "compositions");
|
|
62532
62621
|
const missingBeats = activeBeats.filter(
|
|
62533
|
-
(b) => !
|
|
62622
|
+
(b) => !existsSync50(join26(compositionsDir, `scene-${b.id}.html`))
|
|
62534
62623
|
);
|
|
62535
62624
|
if (missingBeats.length > 0) {
|
|
62536
62625
|
const plan = await getComposePrompts({ projectDir, beatId: opts.beatId });
|
|
@@ -62619,8 +62708,8 @@ async function executeSceneBuild(opts) {
|
|
|
62619
62708
|
if (stageReports.compose.status === "done") {
|
|
62620
62709
|
for (const beat of activeBeats) {
|
|
62621
62710
|
const videoRel = `assets/video-${beat.id}.mp4`;
|
|
62622
|
-
const compositionAbs =
|
|
62623
|
-
if (!
|
|
62711
|
+
const compositionAbs = join26(projectDir, "compositions", `scene-${beat.id}.html`);
|
|
62712
|
+
if (!existsSync50(join26(projectDir, videoRel)) || !existsSync50(compositionAbs)) continue;
|
|
62624
62713
|
const html = readFileSync3(compositionAbs, "utf-8");
|
|
62625
62714
|
if (!html.includes(videoRel)) {
|
|
62626
62715
|
warnings.push(
|
|
@@ -62678,7 +62767,7 @@ async function executeSceneBuild(opts) {
|
|
|
62678
62767
|
});
|
|
62679
62768
|
}
|
|
62680
62769
|
if (shouldRunStage(selectedStage, "sync")) {
|
|
62681
|
-
if (!
|
|
62770
|
+
if (!existsSync50(join26(projectDir, "index.html"))) {
|
|
62682
62771
|
await scaffoldSceneProject({
|
|
62683
62772
|
dir: projectDir,
|
|
62684
62773
|
name: projectDir.split(/[\\/]/).filter(Boolean).pop(),
|
|
@@ -62823,6 +62912,9 @@ async function buildBeatPrimitives(beat, ctx) {
|
|
|
62823
62912
|
ctx.skipVideo ? skipped("video", beat.id, "--skip-video", ctx) : dispatchVideo(beat, ctx, keyframe),
|
|
62824
62913
|
ctx.skipMusic ? skipped("music", beat.id, "--skip-music", ctx) : dispatchMusic(beat, ctx)
|
|
62825
62914
|
]);
|
|
62915
|
+
if (!ctx.skipTranscript) {
|
|
62916
|
+
await dispatchTranscript(beat, narration, ctx);
|
|
62917
|
+
}
|
|
62826
62918
|
return {
|
|
62827
62919
|
outcome: {
|
|
62828
62920
|
beatId: beat.id,
|
|
@@ -62940,7 +63032,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
62940
63032
|
return {
|
|
62941
63033
|
status: "cached",
|
|
62942
63034
|
path: existingRel,
|
|
62943
|
-
durationSec: await safeAudioDuration(
|
|
63035
|
+
durationSec: await safeAudioDuration(join26(ctx.projectDir, existingRel)),
|
|
62944
63036
|
provider: metadata?.provider,
|
|
62945
63037
|
cachePath: metadata?.cachePath,
|
|
62946
63038
|
cacheKey: metadata?.cacheKey,
|
|
@@ -62964,9 +63056,9 @@ async function dispatchNarration(beat, ctx) {
|
|
|
62964
63056
|
voice,
|
|
62965
63057
|
ext: resolution.audioExtension
|
|
62966
63058
|
});
|
|
62967
|
-
const cacheAbs =
|
|
63059
|
+
const cacheAbs = join26(ctx.projectDir, cache.path);
|
|
62968
63060
|
const rel = `assets/narration-${beat.id}.${resolution.audioExtension}`;
|
|
62969
|
-
const abs =
|
|
63061
|
+
const abs = join26(ctx.projectDir, rel);
|
|
62970
63062
|
if (existingRel && !ctx.force && isFreshCanonicalAsset({
|
|
62971
63063
|
projectDir: ctx.projectDir,
|
|
62972
63064
|
kind: "narration",
|
|
@@ -62980,7 +63072,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
62980
63072
|
return {
|
|
62981
63073
|
status: "cached",
|
|
62982
63074
|
path: existingRel,
|
|
62983
|
-
durationSec: await safeAudioDuration(
|
|
63075
|
+
durationSec: await safeAudioDuration(join26(ctx.projectDir, existingRel)),
|
|
62984
63076
|
provider: resolution.provider,
|
|
62985
63077
|
cachePath: cache.path,
|
|
62986
63078
|
cacheKey: cache.key,
|
|
@@ -62988,7 +63080,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
62988
63080
|
freshness: "fresh"
|
|
62989
63081
|
};
|
|
62990
63082
|
}
|
|
62991
|
-
if (
|
|
63083
|
+
if (existsSync50(cacheAbs) && !ctx.force) {
|
|
62992
63084
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
62993
63085
|
await copyFile5(cacheAbs, abs);
|
|
62994
63086
|
await writeAssetMetadata({
|
|
@@ -63023,8 +63115,8 @@ async function dispatchNarration(beat, ctx) {
|
|
|
63023
63115
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
63024
63116
|
await writeFile50(abs, result.audioBuffer);
|
|
63025
63117
|
for (const otherExt of ["mp3", "wav"]) {
|
|
63026
|
-
const sibling =
|
|
63027
|
-
if (sibling !== abs &&
|
|
63118
|
+
const sibling = join26(ctx.projectDir, `assets/narration-${beat.id}.${otherExt}`);
|
|
63119
|
+
if (sibling !== abs && existsSync50(sibling)) await rm4(sibling, { force: true });
|
|
63028
63120
|
}
|
|
63029
63121
|
await mkdir26(dirname24(cacheAbs), { recursive: true });
|
|
63030
63122
|
await writeFile50(cacheAbs, result.audioBuffer);
|
|
@@ -63056,6 +63148,42 @@ async function dispatchNarration(beat, ctx) {
|
|
|
63056
63148
|
freshness: "fresh"
|
|
63057
63149
|
};
|
|
63058
63150
|
}
|
|
63151
|
+
async function dispatchTranscript(beat, narration, ctx) {
|
|
63152
|
+
if (!narration.path) return;
|
|
63153
|
+
const relPath = beatTranscriptRelPath(beat.id);
|
|
63154
|
+
const abs = join26(ctx.projectDir, relPath);
|
|
63155
|
+
const narrationFresh = narration.status === "generated";
|
|
63156
|
+
if (existsSync50(abs) && !ctx.force && !narrationFresh) {
|
|
63157
|
+
ctx.onProgress({ type: "transcript-cached", beatId: beat.id, path: relPath });
|
|
63158
|
+
return;
|
|
63159
|
+
}
|
|
63160
|
+
const apiKey = await getConfiguredApiKey("OPENAI_API_KEY", void 0, { cwd: ctx.projectDir });
|
|
63161
|
+
if (!apiKey) {
|
|
63162
|
+
ctx.onProgress({
|
|
63163
|
+
type: "transcript-skipped",
|
|
63164
|
+
beatId: beat.id,
|
|
63165
|
+
reason: "OPENAI_API_KEY not set \u2014 narration plays but word-sync timings unavailable"
|
|
63166
|
+
});
|
|
63167
|
+
return;
|
|
63168
|
+
}
|
|
63169
|
+
const words = await transcribeNarrationWords(join26(ctx.projectDir, narration.path), { apiKey });
|
|
63170
|
+
if (words.length === 0) {
|
|
63171
|
+
ctx.onProgress({
|
|
63172
|
+
type: "transcript-skipped",
|
|
63173
|
+
beatId: beat.id,
|
|
63174
|
+
reason: "transcription returned no word timings"
|
|
63175
|
+
});
|
|
63176
|
+
return;
|
|
63177
|
+
}
|
|
63178
|
+
await mkdir26(dirname24(abs), { recursive: true });
|
|
63179
|
+
await writeFile50(abs, JSON.stringify(words, null, 2), "utf-8");
|
|
63180
|
+
ctx.onProgress({
|
|
63181
|
+
type: "transcript-generated",
|
|
63182
|
+
beatId: beat.id,
|
|
63183
|
+
path: relPath,
|
|
63184
|
+
wordCount: words.length
|
|
63185
|
+
});
|
|
63186
|
+
}
|
|
63059
63187
|
function characterSheetPrompt(name, description) {
|
|
63060
63188
|
return [
|
|
63061
63189
|
`Character reference sheet of an original fictional character named ${name}: ${description}.`,
|
|
@@ -63073,7 +63201,7 @@ async function buildCharacters(characters, ctx) {
|
|
|
63073
63201
|
const size = ctx.imageSize ?? "1536x1024";
|
|
63074
63202
|
for (const character of characters) {
|
|
63075
63203
|
if (character.imagePath) {
|
|
63076
|
-
if (
|
|
63204
|
+
if (existsSync50(join26(ctx.projectDir, character.imagePath))) {
|
|
63077
63205
|
paths.set(character.name, character.imagePath);
|
|
63078
63206
|
ctx.onProgress({ type: "character-cached", name: character.name, path: character.imagePath });
|
|
63079
63207
|
} else {
|
|
@@ -63086,7 +63214,7 @@ async function buildCharacters(characters, ctx) {
|
|
|
63086
63214
|
if (!character.prompt) continue;
|
|
63087
63215
|
const prompt3 = characterSheetPrompt(character.name, character.prompt);
|
|
63088
63216
|
const rel = `assets/character-${character.name}.png`;
|
|
63089
|
-
const abs =
|
|
63217
|
+
const abs = join26(ctx.projectDir, rel);
|
|
63090
63218
|
const metadataOptions = { quality: ctx.imageQuality, size };
|
|
63091
63219
|
const cache = characterCacheDescriptor({
|
|
63092
63220
|
name: character.name,
|
|
@@ -63095,7 +63223,7 @@ async function buildCharacters(characters, ctx) {
|
|
|
63095
63223
|
quality: ctx.imageQuality,
|
|
63096
63224
|
size
|
|
63097
63225
|
});
|
|
63098
|
-
if (
|
|
63226
|
+
if (existsSync50(abs) && !ctx.force) {
|
|
63099
63227
|
const metadata = readAssetMetadata(ctx.projectDir, "character", character.name);
|
|
63100
63228
|
if (!metadata || isFreshCanonicalAsset({
|
|
63101
63229
|
projectDir: ctx.projectDir,
|
|
@@ -63111,8 +63239,8 @@ async function buildCharacters(characters, ctx) {
|
|
|
63111
63239
|
continue;
|
|
63112
63240
|
}
|
|
63113
63241
|
}
|
|
63114
|
-
const cacheAbs =
|
|
63115
|
-
if (
|
|
63242
|
+
const cacheAbs = join26(ctx.projectDir, cache.path);
|
|
63243
|
+
if (existsSync50(cacheAbs) && !ctx.force) {
|
|
63116
63244
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
63117
63245
|
await copyFile5(cacheAbs, abs);
|
|
63118
63246
|
await writeAssetMetadata({
|
|
@@ -63178,7 +63306,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
63178
63306
|
if (!cue) return { status: "no-cue" };
|
|
63179
63307
|
const prompt3 = augmentBackdropPrompt(cue);
|
|
63180
63308
|
const rel = `assets/backdrop-${beat.id}.png`;
|
|
63181
|
-
const abs =
|
|
63309
|
+
const abs = join26(ctx.projectDir, rel);
|
|
63182
63310
|
const size = ctx.imageSize ?? "1536x1024";
|
|
63183
63311
|
const ratio = imageRatioForSize(size);
|
|
63184
63312
|
const metadataOptions = { quality: ctx.imageQuality, size, ratio };
|
|
@@ -63191,7 +63319,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
63191
63319
|
ratio
|
|
63192
63320
|
});
|
|
63193
63321
|
const metadataPath = assetMetadataPath("backdrop", beat.id);
|
|
63194
|
-
if (
|
|
63322
|
+
if (existsSync50(abs) && !ctx.force) {
|
|
63195
63323
|
const metadata = readAssetMetadata(ctx.projectDir, "backdrop", beat.id);
|
|
63196
63324
|
if (!metadata || isFreshCanonicalAsset({
|
|
63197
63325
|
projectDir: ctx.projectDir,
|
|
@@ -63214,8 +63342,8 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
63214
63342
|
};
|
|
63215
63343
|
}
|
|
63216
63344
|
}
|
|
63217
|
-
const cacheAbs =
|
|
63218
|
-
if (
|
|
63345
|
+
const cacheAbs = join26(ctx.projectDir, cache.path);
|
|
63346
|
+
if (existsSync50(cacheAbs) && !ctx.force) {
|
|
63219
63347
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
63220
63348
|
await copyFile5(cacheAbs, abs);
|
|
63221
63349
|
await writeAssetMetadata({
|
|
@@ -63347,7 +63475,7 @@ async function editKeyframeImage(prompt3, sheetBuffers, ctx, ratio) {
|
|
|
63347
63475
|
}
|
|
63348
63476
|
async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
63349
63477
|
const rel = `assets/keyframe-${beat.id}.png`;
|
|
63350
|
-
const abs =
|
|
63478
|
+
const abs = join26(ctx.projectDir, rel);
|
|
63351
63479
|
const size = ctx.imageSize ?? "1536x1024";
|
|
63352
63480
|
const ratio = imageRatioForSize(size);
|
|
63353
63481
|
const metadataOptions = { quality: ctx.imageQuality, size, ratio };
|
|
@@ -63360,7 +63488,7 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
63360
63488
|
ratio,
|
|
63361
63489
|
characters: characterRefsRel
|
|
63362
63490
|
});
|
|
63363
|
-
if (
|
|
63491
|
+
if (existsSync50(abs) && !ctx.force) {
|
|
63364
63492
|
const metadata = readAssetMetadata(ctx.projectDir, "keyframe", beat.id);
|
|
63365
63493
|
if (!metadata || isFreshCanonicalAsset({
|
|
63366
63494
|
projectDir: ctx.projectDir,
|
|
@@ -63374,8 +63502,8 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
63374
63502
|
return { status: "cached", rel };
|
|
63375
63503
|
}
|
|
63376
63504
|
}
|
|
63377
|
-
const cacheAbs =
|
|
63378
|
-
if (
|
|
63505
|
+
const cacheAbs = join26(ctx.projectDir, cache.path);
|
|
63506
|
+
if (existsSync50(cacheAbs) && !ctx.force) {
|
|
63379
63507
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
63380
63508
|
await copyFile5(cacheAbs, abs);
|
|
63381
63509
|
await writeAssetMetadata({
|
|
@@ -63393,8 +63521,8 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
63393
63521
|
}
|
|
63394
63522
|
const sheetBuffers = [];
|
|
63395
63523
|
for (const relPath of characterRefsRel) {
|
|
63396
|
-
const sheetAbs =
|
|
63397
|
-
if (
|
|
63524
|
+
const sheetAbs = join26(ctx.projectDir, relPath);
|
|
63525
|
+
if (existsSync50(sheetAbs)) sheetBuffers.push(await readFile47(sheetAbs));
|
|
63398
63526
|
}
|
|
63399
63527
|
const imgCtx = {
|
|
63400
63528
|
projectDir: ctx.projectDir,
|
|
@@ -63463,9 +63591,9 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
63463
63591
|
const prompt3 = stringOrUndefined4(beat.cues?.video) ?? keyframeCue;
|
|
63464
63592
|
if (!prompt3) return { status: "no-cue" };
|
|
63465
63593
|
const characterRefsRel = beatCharacterNames(beat.cues).map((name) => ctx.characterPaths?.get(name)).filter((p) => typeof p === "string");
|
|
63466
|
-
const characterRefsAbs = characterRefsRel.map((p) =>
|
|
63594
|
+
const characterRefsAbs = characterRefsRel.map((p) => join26(ctx.projectDir, p));
|
|
63467
63595
|
const rel = `assets/video-${beat.id}.mp4`;
|
|
63468
|
-
const abs =
|
|
63596
|
+
const abs = join26(ctx.projectDir, rel);
|
|
63469
63597
|
const cache = videoCacheDescriptor({
|
|
63470
63598
|
beatId: beat.id,
|
|
63471
63599
|
cue: prompt3,
|
|
@@ -63477,7 +63605,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
63477
63605
|
keyframe: keyframeCue
|
|
63478
63606
|
});
|
|
63479
63607
|
const metadataPath = assetMetadataPath("video", beat.id);
|
|
63480
|
-
if (
|
|
63608
|
+
if (existsSync50(abs) && !ctx.force) {
|
|
63481
63609
|
const metadata = readAssetMetadata(ctx.projectDir, "video", beat.id);
|
|
63482
63610
|
if (!metadata || isFreshCanonicalAsset({
|
|
63483
63611
|
projectDir: ctx.projectDir,
|
|
@@ -63500,8 +63628,8 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
63500
63628
|
};
|
|
63501
63629
|
}
|
|
63502
63630
|
}
|
|
63503
|
-
const cacheAbs =
|
|
63504
|
-
if (
|
|
63631
|
+
const cacheAbs = join26(ctx.projectDir, cache.path);
|
|
63632
|
+
if (existsSync50(cacheAbs) && !ctx.force) {
|
|
63505
63633
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
63506
63634
|
await copyFile5(cacheAbs, abs);
|
|
63507
63635
|
await writeAssetMetadata({
|
|
@@ -63532,7 +63660,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
63532
63660
|
ctx.onProgress({ type: "video-skipped", beatId: beat.id, reason });
|
|
63533
63661
|
return { status: "skipped" };
|
|
63534
63662
|
}
|
|
63535
|
-
const keyframeImageAbs = keyframeImageRel ?
|
|
63663
|
+
const keyframeImageAbs = keyframeImageRel ? join26(ctx.projectDir, keyframeImageRel) : void 0;
|
|
63536
63664
|
loadSceneBuildEnv(ctx.projectDir);
|
|
63537
63665
|
const result = await executeVideoGenerate({
|
|
63538
63666
|
prompt: prompt3,
|
|
@@ -63556,7 +63684,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
63556
63684
|
ctx.onProgress({ type: "video-failed", beatId: beat.id, error });
|
|
63557
63685
|
return { status: "failed", error };
|
|
63558
63686
|
}
|
|
63559
|
-
if (result.status === "completed" &&
|
|
63687
|
+
if (result.status === "completed" && existsSync50(abs)) {
|
|
63560
63688
|
await mkdir26(dirname24(cacheAbs), { recursive: true });
|
|
63561
63689
|
await copyFile5(abs, cacheAbs);
|
|
63562
63690
|
await writeAssetMetadata({
|
|
@@ -63624,7 +63752,7 @@ async function dispatchMusic(beat, ctx) {
|
|
|
63624
63752
|
const prompt3 = stringOrUndefined4(beat.cues?.music);
|
|
63625
63753
|
if (!prompt3) return { status: "no-cue" };
|
|
63626
63754
|
const rel = `assets/music-${beat.id}.mp3`;
|
|
63627
|
-
const abs =
|
|
63755
|
+
const abs = join26(ctx.projectDir, rel);
|
|
63628
63756
|
const duration = normalizeMusicDuration(beat.duration);
|
|
63629
63757
|
const cache = musicCacheDescriptor({
|
|
63630
63758
|
beatId: beat.id,
|
|
@@ -63633,7 +63761,7 @@ async function dispatchMusic(beat, ctx) {
|
|
|
63633
63761
|
duration
|
|
63634
63762
|
});
|
|
63635
63763
|
const metadataPath = assetMetadataPath("music", beat.id);
|
|
63636
|
-
if (
|
|
63764
|
+
if (existsSync50(abs) && !ctx.force) {
|
|
63637
63765
|
const metadata = readAssetMetadata(ctx.projectDir, "music", beat.id);
|
|
63638
63766
|
if (!metadata || isFreshCanonicalAsset({
|
|
63639
63767
|
projectDir: ctx.projectDir,
|
|
@@ -63657,8 +63785,8 @@ async function dispatchMusic(beat, ctx) {
|
|
|
63657
63785
|
};
|
|
63658
63786
|
}
|
|
63659
63787
|
}
|
|
63660
|
-
const cacheAbs =
|
|
63661
|
-
if (
|
|
63788
|
+
const cacheAbs = join26(ctx.projectDir, cache.path);
|
|
63789
|
+
if (existsSync50(cacheAbs) && !ctx.force) {
|
|
63662
63790
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
63663
63791
|
await copyFile5(cacheAbs, abs);
|
|
63664
63792
|
await writeAssetMetadata({
|
|
@@ -63732,7 +63860,7 @@ async function dispatchMusic(beat, ctx) {
|
|
|
63732
63860
|
metadataPath
|
|
63733
63861
|
};
|
|
63734
63862
|
}
|
|
63735
|
-
if (!
|
|
63863
|
+
if (!existsSync50(abs)) {
|
|
63736
63864
|
const error = result.outputPath ? `music output was not written at ${abs}` : "music generation did not return an output file";
|
|
63737
63865
|
ctx.onProgress({ type: "music-failed", beatId: beat.id, error });
|
|
63738
63866
|
return { status: "failed", error };
|
|
@@ -63768,12 +63896,12 @@ async function dispatchMusic(beat, ctx) {
|
|
|
63768
63896
|
};
|
|
63769
63897
|
}
|
|
63770
63898
|
function loadSceneBuildEnv(projectDir) {
|
|
63771
|
-
(0, import_dotenv4.config)({ path:
|
|
63899
|
+
(0, import_dotenv4.config)({ path: join26(projectDir, ".env"), quiet: true });
|
|
63772
63900
|
(0, import_dotenv4.config)({ path: resolve65(process.cwd(), ".env"), quiet: true });
|
|
63773
63901
|
let dir = process.cwd();
|
|
63774
63902
|
while (dir !== dirname24(dir)) {
|
|
63775
|
-
if (
|
|
63776
|
-
(0, import_dotenv4.config)({ path:
|
|
63903
|
+
if (existsSync50(join26(dir, "pnpm-workspace.yaml"))) {
|
|
63904
|
+
(0, import_dotenv4.config)({ path: join26(dir, ".env"), quiet: true });
|
|
63777
63905
|
return;
|
|
63778
63906
|
}
|
|
63779
63907
|
dir = dirname24(dir);
|
|
@@ -63989,7 +64117,7 @@ function mergeBeatOutcomes(base, updates) {
|
|
|
63989
64117
|
}
|
|
63990
64118
|
function firstExisting3(projectDir, relPaths) {
|
|
63991
64119
|
for (const rel of relPaths) {
|
|
63992
|
-
if (
|
|
64120
|
+
if (existsSync50(join26(projectDir, rel))) return rel;
|
|
63993
64121
|
}
|
|
63994
64122
|
return null;
|
|
63995
64123
|
}
|
|
@@ -64020,7 +64148,7 @@ async function apiKeyForVideoProvider(provider, projectDir) {
|
|
|
64020
64148
|
return getApiKeyFromConfig(providerKey, { cwd: projectDir });
|
|
64021
64149
|
}
|
|
64022
64150
|
async function finalizeBuildResult(projectDir, startedAt, result) {
|
|
64023
|
-
const reportPath =
|
|
64151
|
+
const reportPath = join26(projectDir, "build-report.json");
|
|
64024
64152
|
if (result.stageReports) {
|
|
64025
64153
|
for (const report of Object.values(result.stageReports)) {
|
|
64026
64154
|
if (report.status === "pending") report.status = "skipped";
|
|
@@ -64179,7 +64307,7 @@ function normalizeReportDuration(value) {
|
|
|
64179
64307
|
}
|
|
64180
64308
|
function buildReportComposition(projectDir, result, beatId) {
|
|
64181
64309
|
const path2 = `compositions/scene-${beatId}.html`;
|
|
64182
|
-
const exists =
|
|
64310
|
+
const exists = existsSync50(join26(projectDir, path2));
|
|
64183
64311
|
const written = composeWrittenEntry(result, beatId);
|
|
64184
64312
|
if (written) {
|
|
64185
64313
|
return {
|
|
@@ -64236,8 +64364,8 @@ function summarizeBuildBeats(projectDir, beats) {
|
|
|
64236
64364
|
let compositionsReady = 0;
|
|
64237
64365
|
for (const beat of beats) {
|
|
64238
64366
|
if (beatAssetsReady(beat)) assetsReady += 1;
|
|
64239
|
-
const compositionPath =
|
|
64240
|
-
if (
|
|
64367
|
+
const compositionPath = join26(projectDir, "compositions", `scene-${beat.beatId}.html`);
|
|
64368
|
+
if (existsSync50(compositionPath)) {
|
|
64241
64369
|
compositionsReady += 1;
|
|
64242
64370
|
} else {
|
|
64243
64371
|
needsAuthor.push(beat.beatId);
|
|
@@ -64279,6 +64407,8 @@ var init_scene_build = __esm({
|
|
|
64279
64407
|
init_compose_scenes_skills();
|
|
64280
64408
|
init_compose_prompts();
|
|
64281
64409
|
init_config();
|
|
64410
|
+
init_api_key();
|
|
64411
|
+
init_transcribe_narration();
|
|
64282
64412
|
init_scene_render();
|
|
64283
64413
|
init_storyboard_parse();
|
|
64284
64414
|
init_scene_project();
|
|
@@ -64369,18 +64499,18 @@ __export(ai_audio_exports, {
|
|
|
64369
64499
|
executeVoiceClone: () => executeVoiceClone
|
|
64370
64500
|
});
|
|
64371
64501
|
import { resolve as resolve73, dirname as dirname26, basename as basename22, extname as extname15 } from "node:path";
|
|
64372
|
-
import { readFile as
|
|
64373
|
-
import { existsSync as
|
|
64502
|
+
import { readFile as readFile49, writeFile as writeFile51 } from "node:fs/promises";
|
|
64503
|
+
import { existsSync as existsSync55 } from "node:fs";
|
|
64374
64504
|
async function executeTranscribe(options) {
|
|
64375
64505
|
const { audioPath, language, output, format, apiKey } = options;
|
|
64376
64506
|
try {
|
|
64377
64507
|
const key = apiKey || process.env.OPENAI_API_KEY;
|
|
64378
64508
|
if (!key) return { success: false, error: "OPENAI_API_KEY required" };
|
|
64379
64509
|
const absPath = resolve73(process.cwd(), audioPath);
|
|
64380
|
-
if (!
|
|
64510
|
+
if (!existsSync55(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
64381
64511
|
const whisper = new WhisperProvider();
|
|
64382
64512
|
await whisper.initialize({ apiKey: key });
|
|
64383
|
-
const audioBuffer = await
|
|
64513
|
+
const audioBuffer = await readFile49(absPath);
|
|
64384
64514
|
const audioBlob = new Blob([audioBuffer]);
|
|
64385
64515
|
const result = await whisper.transcribe(audioBlob, language);
|
|
64386
64516
|
if (result.status === "failed") {
|
|
@@ -64410,8 +64540,8 @@ async function executeIsolate(options) {
|
|
|
64410
64540
|
const key = apiKey || process.env.ELEVENLABS_API_KEY;
|
|
64411
64541
|
if (!key) return { success: false, error: "ELEVENLABS_API_KEY required" };
|
|
64412
64542
|
const absPath = resolve73(process.cwd(), audioPath);
|
|
64413
|
-
if (!
|
|
64414
|
-
const audioBuffer = await
|
|
64543
|
+
if (!existsSync55(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
64544
|
+
const audioBuffer = await readFile49(absPath);
|
|
64415
64545
|
const elevenlabs = new ElevenLabsProvider();
|
|
64416
64546
|
await elevenlabs.initialize({ apiKey: key });
|
|
64417
64547
|
const result = await elevenlabs.isolateVocals(audioBuffer);
|
|
@@ -64436,8 +64566,8 @@ async function executeVoiceClone(options) {
|
|
|
64436
64566
|
const audioBuffers = [];
|
|
64437
64567
|
for (const samplePath of samplePaths) {
|
|
64438
64568
|
const absPath = resolve73(process.cwd(), samplePath);
|
|
64439
|
-
if (!
|
|
64440
|
-
const buffer = await
|
|
64569
|
+
if (!existsSync55(absPath)) return { success: false, error: `File not found: ${samplePath}` };
|
|
64570
|
+
const buffer = await readFile49(absPath);
|
|
64441
64571
|
audioBuffers.push(buffer);
|
|
64442
64572
|
}
|
|
64443
64573
|
const elevenlabs = new ElevenLabsProvider();
|
|
@@ -64458,7 +64588,7 @@ async function executeDub(options) {
|
|
|
64458
64588
|
const { mediaPath, language, source, voice, analyzeOnly, output } = options;
|
|
64459
64589
|
try {
|
|
64460
64590
|
const absPath = resolve73(process.cwd(), mediaPath);
|
|
64461
|
-
if (!
|
|
64591
|
+
if (!existsSync55(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
64462
64592
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
64463
64593
|
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
64464
64594
|
const elevenlabsKey = process.env.ELEVENLABS_API_KEY;
|
|
@@ -64475,7 +64605,7 @@ async function executeDub(options) {
|
|
|
64475
64605
|
}
|
|
64476
64606
|
const whisper = new WhisperProvider();
|
|
64477
64607
|
await whisper.initialize({ apiKey: openaiKey });
|
|
64478
|
-
const audioBuffer = await
|
|
64608
|
+
const audioBuffer = await readFile49(audioPath);
|
|
64479
64609
|
const audioBlob = new Blob([audioBuffer]);
|
|
64480
64610
|
const transcriptResult = await whisper.transcribe(audioBlob, source);
|
|
64481
64611
|
if (transcriptResult.status === "failed" || !transcriptResult.segments) {
|
|
@@ -64586,8 +64716,8 @@ async function executeDuck(options) {
|
|
|
64586
64716
|
if (!commandExists("ffmpeg")) return { success: false, error: "FFmpeg not found" };
|
|
64587
64717
|
const absMusicPath = resolve73(process.cwd(), musicPath);
|
|
64588
64718
|
const absVoicePath = resolve73(process.cwd(), voicePath);
|
|
64589
|
-
if (!
|
|
64590
|
-
if (!
|
|
64719
|
+
if (!existsSync55(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
|
|
64720
|
+
if (!existsSync55(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
|
|
64591
64721
|
const defaultOutput = resolve73(dirname26(absMusicPath), `${basename22(absMusicPath, extname15(absMusicPath))}-ducked${extname15(absMusicPath)}`);
|
|
64592
64722
|
const outputPath = resolve73(process.cwd(), output || defaultOutput);
|
|
64593
64723
|
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`;
|
|
@@ -64601,7 +64731,7 @@ async function executeDuck(options) {
|
|
|
64601
64731
|
"-y",
|
|
64602
64732
|
outputPath
|
|
64603
64733
|
], { timeout: 12e4, maxBuffer: 50 * 1024 * 1024 });
|
|
64604
|
-
if (!
|
|
64734
|
+
if (!existsSync55(outputPath)) return { success: false, error: "FFmpeg failed to create output" };
|
|
64605
64735
|
return { success: true, outputPath };
|
|
64606
64736
|
} catch (error) {
|
|
64607
64737
|
return { success: false, error: `Audio ducking failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
@@ -64618,8 +64748,8 @@ var init_ai_audio = __esm({
|
|
|
64618
64748
|
|
|
64619
64749
|
// src/pipeline/executor.ts
|
|
64620
64750
|
import { resolve as resolve74 } from "node:path";
|
|
64621
|
-
import { readFile as
|
|
64622
|
-
import { existsSync as
|
|
64751
|
+
import { readFile as readFile50, writeFile as writeFile52, mkdir as mkdir28 } from "node:fs/promises";
|
|
64752
|
+
import { existsSync as existsSync56 } from "node:fs";
|
|
64623
64753
|
function maxCostFor(action) {
|
|
64624
64754
|
const cmd = ACTION_METADATA[action]?.command;
|
|
64625
64755
|
if (!cmd) return 0;
|
|
@@ -64934,10 +65064,10 @@ async function ensureActionsRegistered() {
|
|
|
64934
65064
|
}
|
|
64935
65065
|
async function loadPipeline(filePath) {
|
|
64936
65066
|
const absPath = resolve74(process.cwd(), filePath);
|
|
64937
|
-
if (!
|
|
65067
|
+
if (!existsSync56(absPath)) {
|
|
64938
65068
|
throw new Error(`Pipeline file not found: ${absPath}`);
|
|
64939
65069
|
}
|
|
64940
|
-
const content = await
|
|
65070
|
+
const content = await readFile50(absPath, "utf-8");
|
|
64941
65071
|
const manifest2 = (0, import_yaml8.parse)(content);
|
|
64942
65072
|
if (!manifest2.name) throw new Error("Pipeline missing 'name' field");
|
|
64943
65073
|
if (!manifest2.steps || !Array.isArray(manifest2.steps)) throw new Error("Pipeline missing 'steps' array");
|
|
@@ -64959,8 +65089,8 @@ async function executePipeline(manifest2, options = {}) {
|
|
|
64959
65089
|
const startTime = Date.now();
|
|
64960
65090
|
if (options.resume) {
|
|
64961
65091
|
const checkpointPath = resolve74(outputDir, CHECKPOINT_FILE);
|
|
64962
|
-
if (
|
|
64963
|
-
const checkpointContent = await
|
|
65092
|
+
if (existsSync56(checkpointPath)) {
|
|
65093
|
+
const checkpointContent = await readFile50(checkpointPath, "utf-8");
|
|
64964
65094
|
const checkpoint = (0, import_yaml8.parse)(checkpointContent);
|
|
64965
65095
|
for (const cs of checkpoint.completedSteps) {
|
|
64966
65096
|
completedSteps.set(cs.id, {
|
|
@@ -70890,14 +71020,14 @@ var init_edit = __esm({
|
|
|
70890
71020
|
});
|
|
70891
71021
|
|
|
70892
71022
|
// src/commands/ai-suggest-edit.ts
|
|
70893
|
-
import { readFile as
|
|
71023
|
+
import { readFile as readFile51, writeFile as writeFile53 } from "node:fs/promises";
|
|
70894
71024
|
import { resolve as resolve76 } from "node:path";
|
|
70895
71025
|
async function executeSuggestEdit(options) {
|
|
70896
71026
|
try {
|
|
70897
71027
|
const apiKey = options.apiKey ?? process.env.GOOGLE_API_KEY;
|
|
70898
71028
|
if (!apiKey) return { success: false, error: "GOOGLE_API_KEY required for suggest" };
|
|
70899
71029
|
const filePath = resolve76(process.cwd(), options.projectPath);
|
|
70900
|
-
const content = await
|
|
71030
|
+
const content = await readFile51(filePath, "utf-8");
|
|
70901
71031
|
const data = JSON.parse(content);
|
|
70902
71032
|
const project = Project.fromJSON(data);
|
|
70903
71033
|
const gemini = new GeminiProvider();
|
|
@@ -71662,13 +71792,13 @@ var init_generate2 = __esm({
|
|
|
71662
71792
|
});
|
|
71663
71793
|
|
|
71664
71794
|
// src/tools/manifest/storyboard.ts
|
|
71665
|
-
import { existsSync as
|
|
71666
|
-
import { readFile as
|
|
71795
|
+
import { existsSync as existsSync57 } from "node:fs";
|
|
71796
|
+
import { readFile as readFile52, writeFile as writeFile54 } from "node:fs/promises";
|
|
71667
71797
|
import { resolve as resolve78 } from "node:path";
|
|
71668
71798
|
async function readStoryboard2(projectDir) {
|
|
71669
71799
|
const path2 = resolve78(projectDir, "STORYBOARD.md");
|
|
71670
|
-
if (!
|
|
71671
|
-
return { path: path2, markdown: await
|
|
71800
|
+
if (!existsSync57(path2)) return null;
|
|
71801
|
+
return { path: path2, markdown: await readFile52(path2, "utf-8") };
|
|
71672
71802
|
}
|
|
71673
71803
|
var PROJECT_DIR_DESCRIPTION3, projectDirSchema, storyboardListTool, storyboardValidateTool, storyboardGetTool, storyboardSetTool, storyboardMoveTool, storyboardReviseTool, planTool, storyboardTools;
|
|
71674
71804
|
var init_storyboard2 = __esm({
|
|
@@ -71943,7 +72073,7 @@ var init_storyboard2 = __esm({
|
|
|
71943
72073
|
// src/tools/manifest/remix.ts
|
|
71944
72074
|
import { writeFile as writeFile55 } from "node:fs/promises";
|
|
71945
72075
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
71946
|
-
import { join as
|
|
72076
|
+
import { join as join28 } from "node:path";
|
|
71947
72077
|
var pipelineHighlightsTool, pipelineAutoShortsTool, pipelineRunTool, pipelineRegenerateSceneTool, remixTools;
|
|
71948
72078
|
var init_remix = __esm({
|
|
71949
72079
|
"src/tools/manifest/remix.ts"() {
|
|
@@ -72057,7 +72187,7 @@ var init_remix = __esm({
|
|
|
72057
72187
|
let resolvedPath = args.pipelinePath;
|
|
72058
72188
|
let tempPath;
|
|
72059
72189
|
if (args.pipelineYaml) {
|
|
72060
|
-
tempPath =
|
|
72190
|
+
tempPath = join28(tmpdir4(), `vibe-mcp-pipeline-${Date.now()}.yaml`);
|
|
72061
72191
|
await writeFile55(tempPath, args.pipelineYaml, "utf-8");
|
|
72062
72192
|
resolvedPath = tempPath;
|
|
72063
72193
|
}
|
|
@@ -72233,10 +72363,10 @@ var init_detect2 = __esm({
|
|
|
72233
72363
|
});
|
|
72234
72364
|
|
|
72235
72365
|
// src/tools/manifest/_project-io.ts
|
|
72236
|
-
import { readFile as
|
|
72366
|
+
import { readFile as readFile53, writeFile as writeFile56 } from "node:fs/promises";
|
|
72237
72367
|
async function loadProject2(projectPath, cwd) {
|
|
72238
72368
|
const absPath = await resolveTimelineFile(projectPath, cwd);
|
|
72239
|
-
const content = await
|
|
72369
|
+
const content = await readFile53(absPath, "utf-8");
|
|
72240
72370
|
const data = JSON.parse(content);
|
|
72241
72371
|
return { project: Project.fromJSON(data), absPath };
|
|
72242
72372
|
}
|
|
@@ -72600,12 +72730,12 @@ var init_timeline = __esm({
|
|
|
72600
72730
|
});
|
|
72601
72731
|
|
|
72602
72732
|
// src/commands/_shared/project-list.ts
|
|
72603
|
-
import { readdir as readdir7, readFile as
|
|
72604
|
-
import { existsSync as
|
|
72605
|
-
import { join as
|
|
72733
|
+
import { readdir as readdir7, readFile as readFile54, stat as stat9 } from "node:fs/promises";
|
|
72734
|
+
import { existsSync as existsSync58 } from "node:fs";
|
|
72735
|
+
import { join as join29, resolve as resolve80 } from "node:path";
|
|
72606
72736
|
async function executeProjectList(opts = {}) {
|
|
72607
72737
|
const workspaceDir = resolve80(opts.workspaceDir ?? ".");
|
|
72608
|
-
if (!
|
|
72738
|
+
if (!existsSync58(workspaceDir)) {
|
|
72609
72739
|
return {
|
|
72610
72740
|
success: false,
|
|
72611
72741
|
workspaceDir,
|
|
@@ -72622,11 +72752,11 @@ async function executeProjectList(opts = {}) {
|
|
|
72622
72752
|
if (entry.name === "node_modules") continue;
|
|
72623
72753
|
if (entry.name.startsWith(".")) continue;
|
|
72624
72754
|
if (entry.name.startsWith("_")) {
|
|
72625
|
-
archivedCount += await countProjects(
|
|
72755
|
+
archivedCount += await countProjects(join29(workspaceDir, entry.name));
|
|
72626
72756
|
continue;
|
|
72627
72757
|
}
|
|
72628
|
-
const projectDir =
|
|
72629
|
-
if (!
|
|
72758
|
+
const projectDir = join29(workspaceDir, entry.name);
|
|
72759
|
+
if (!existsSync58(join29(projectDir, "STORYBOARD.md"))) continue;
|
|
72630
72760
|
projects.push(await describeProject(entry.name, projectDir));
|
|
72631
72761
|
}
|
|
72632
72762
|
projects.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
@@ -72636,7 +72766,7 @@ async function countProjects(dir) {
|
|
|
72636
72766
|
try {
|
|
72637
72767
|
const entries = await readdir7(dir, { withFileTypes: true });
|
|
72638
72768
|
return entries.filter(
|
|
72639
|
-
(e) => e.isDirectory() &&
|
|
72769
|
+
(e) => e.isDirectory() && existsSync58(join29(dir, e.name, "STORYBOARD.md"))
|
|
72640
72770
|
).length;
|
|
72641
72771
|
} catch {
|
|
72642
72772
|
return 0;
|
|
@@ -72647,8 +72777,8 @@ async function describeProject(name, projectDir) {
|
|
|
72647
72777
|
let storyboardDurationSec = 0;
|
|
72648
72778
|
const timestamps = [];
|
|
72649
72779
|
try {
|
|
72650
|
-
const storyboardPath2 =
|
|
72651
|
-
const parsed = parseStoryboard(await
|
|
72780
|
+
const storyboardPath2 = join29(projectDir, "STORYBOARD.md");
|
|
72781
|
+
const parsed = parseStoryboard(await readFile54(storyboardPath2, "utf-8"));
|
|
72652
72782
|
beats = parsed.beats.length;
|
|
72653
72783
|
storyboardDurationSec = Number(
|
|
72654
72784
|
parsed.beats.reduce((sum, beat) => sum + (beat.duration ?? 0), 0).toFixed(2)
|
|
@@ -72659,8 +72789,8 @@ async function describeProject(name, projectDir) {
|
|
|
72659
72789
|
let buildStatus;
|
|
72660
72790
|
let buildUpdatedAt;
|
|
72661
72791
|
try {
|
|
72662
|
-
const reportPath =
|
|
72663
|
-
const report = JSON.parse(await
|
|
72792
|
+
const reportPath = join29(projectDir, "build-report.json");
|
|
72793
|
+
const report = JSON.parse(await readFile54(reportPath, "utf-8"));
|
|
72664
72794
|
buildStatus = report.status ?? report.phase;
|
|
72665
72795
|
buildUpdatedAt = report.updatedAt;
|
|
72666
72796
|
timestamps.push((await stat9(reportPath)).mtimeMs);
|
|
@@ -72668,12 +72798,12 @@ async function describeProject(name, projectDir) {
|
|
|
72668
72798
|
}
|
|
72669
72799
|
let latestRender;
|
|
72670
72800
|
try {
|
|
72671
|
-
const rendersDir =
|
|
72801
|
+
const rendersDir = join29(projectDir, "renders");
|
|
72672
72802
|
const files = await readdir7(rendersDir);
|
|
72673
72803
|
let newest;
|
|
72674
72804
|
for (const file of files) {
|
|
72675
72805
|
if (!/\.(mp4|webm|mov)$/i.test(file)) continue;
|
|
72676
|
-
const { mtimeMs } = await stat9(
|
|
72806
|
+
const { mtimeMs } = await stat9(join29(rendersDir, file));
|
|
72677
72807
|
if (!newest || mtimeMs > newest.mtimeMs) newest = { file, mtimeMs };
|
|
72678
72808
|
}
|
|
72679
72809
|
if (newest) {
|
|
@@ -72815,7 +72945,7 @@ var init_project2 = __esm({
|
|
|
72815
72945
|
});
|
|
72816
72946
|
|
|
72817
72947
|
// src/commands/export.ts
|
|
72818
|
-
import { readFile as
|
|
72948
|
+
import { readFile as readFile55, access as access7 } from "node:fs/promises";
|
|
72819
72949
|
import { resolve as resolve82, basename as basename24 } from "node:path";
|
|
72820
72950
|
import { spawn as spawn3 } from "node:child_process";
|
|
72821
72951
|
function timelineBaseName(path2) {
|
|
@@ -72884,7 +73014,7 @@ async function runExport(projectPath, outputPath, options = {}) {
|
|
|
72884
73014
|
};
|
|
72885
73015
|
}
|
|
72886
73016
|
const filePath = await resolveTimelineFile(projectPath);
|
|
72887
|
-
const content = await
|
|
73017
|
+
const content = await readFile55(filePath, "utf-8");
|
|
72888
73018
|
const data = JSON.parse(content);
|
|
72889
73019
|
const project = Project.fromJSON(data);
|
|
72890
73020
|
const summary = project.getSummary();
|
|
@@ -73450,14 +73580,14 @@ function getPresetSettings(preset, aspectRatio2) {
|
|
|
73450
73580
|
}
|
|
73451
73581
|
async function runHyperframesExport(projectPath, options, spinner2, startedAt) {
|
|
73452
73582
|
spinner2.text = "Loading project...";
|
|
73453
|
-
const { readFile:
|
|
73583
|
+
const { readFile: readFile57 } = await import("node:fs/promises");
|
|
73454
73584
|
const { resolve: resolve86 } = await import("node:path");
|
|
73455
73585
|
const { Project: Project2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
73456
73586
|
const { createHyperframesBackend: createHyperframesBackend2 } = await Promise.resolve().then(() => (init_hyperframes(), hyperframes_exports));
|
|
73457
73587
|
const { exitWithError: exitWithError2, generalError: generalError2, outputSuccess: outputSuccess2 } = await Promise.resolve().then(() => (init_output(), output_exports));
|
|
73458
73588
|
const chalk2 = (await Promise.resolve().then(() => (init_source(), source_exports))).default;
|
|
73459
73589
|
const filePath = await resolveTimelineFile(projectPath);
|
|
73460
|
-
const content = await
|
|
73590
|
+
const content = await readFile57(filePath, "utf-8");
|
|
73461
73591
|
const project = Project2.fromJSON(JSON.parse(content));
|
|
73462
73592
|
const state = project.getState();
|
|
73463
73593
|
const outputPath = options.output ? resolve86(process.cwd(), options.output) : resolve86(process.cwd(), `${timelineBaseName(projectPath)}.${options.format ?? "mp4"}`);
|
|
@@ -73579,7 +73709,7 @@ Run 'vibe schema export' for structured parameter info.`).action(async (projectP
|
|
|
73579
73709
|
}
|
|
73580
73710
|
spinner2.text = "Loading project...";
|
|
73581
73711
|
const filePath = await resolveTimelineFile(projectPath);
|
|
73582
|
-
const content = await
|
|
73712
|
+
const content = await readFile55(filePath, "utf-8");
|
|
73583
73713
|
const data = JSON.parse(content);
|
|
73584
73714
|
const project = Project.fromJSON(data);
|
|
73585
73715
|
const summary = project.getSummary();
|
|
@@ -73714,8 +73844,8 @@ var init_export2 = __esm({
|
|
|
73714
73844
|
});
|
|
73715
73845
|
|
|
73716
73846
|
// src/tools/manifest/agent-only.ts
|
|
73717
|
-
import { readFile as
|
|
73718
|
-
import { resolve as resolve84, join as
|
|
73847
|
+
import { readFile as readFile56, writeFile as writeFile59, readdir as readdir8, stat as stat10, access as access8, unlink as unlink4 } from "node:fs/promises";
|
|
73848
|
+
import { resolve as resolve84, join as join30, basename as basename25, extname as extname16 } from "node:path";
|
|
73719
73849
|
function matchPattern(filename, pattern) {
|
|
73720
73850
|
const regex2 = new RegExp(
|
|
73721
73851
|
"^" + pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".") + "$",
|
|
@@ -73800,7 +73930,7 @@ var init_agent_only = __esm({
|
|
|
73800
73930
|
const results = [];
|
|
73801
73931
|
for (const entry of entries) {
|
|
73802
73932
|
if (args.pattern && !matchPattern(entry.name, args.pattern)) continue;
|
|
73803
|
-
const fullPath =
|
|
73933
|
+
const fullPath = join30(absPath, entry.name);
|
|
73804
73934
|
const stats = await stat10(fullPath);
|
|
73805
73935
|
if (entry.isDirectory()) {
|
|
73806
73936
|
results.push(`[DIR] ${entry.name}/`);
|
|
@@ -73829,7 +73959,7 @@ var init_agent_only = __esm({
|
|
|
73829
73959
|
async execute(args, ctx) {
|
|
73830
73960
|
try {
|
|
73831
73961
|
const absPath = resolve84(ctx.workingDirectory, args.path);
|
|
73832
|
-
const content = await
|
|
73962
|
+
const content = await readFile56(absPath, "utf-8");
|
|
73833
73963
|
const maxLength = 4e3;
|
|
73834
73964
|
const truncated = content.length > maxLength;
|
|
73835
73965
|
const output = truncated ? content.substring(0, maxLength) + "\n... (truncated)" : content;
|
|
@@ -73902,7 +74032,7 @@ var init_agent_only = __esm({
|
|
|
73902
74032
|
async execute(args, ctx) {
|
|
73903
74033
|
try {
|
|
73904
74034
|
const filePath = resolve84(ctx.workingDirectory, args.project);
|
|
73905
|
-
const content = await
|
|
74035
|
+
const content = await readFile56(filePath, "utf-8");
|
|
73906
74036
|
const data = JSON.parse(content);
|
|
73907
74037
|
const project = Project.fromJSON(data);
|
|
73908
74038
|
const dirPath = resolve84(ctx.workingDirectory, args.directory);
|
|
@@ -73913,7 +74043,7 @@ var init_agent_only = __esm({
|
|
|
73913
74043
|
const scanDir = async (dir) => {
|
|
73914
74044
|
const entries = await readdir8(dir, { withFileTypes: true });
|
|
73915
74045
|
for (const entry of entries) {
|
|
73916
|
-
const entryPath =
|
|
74046
|
+
const entryPath = join30(dir, entry.name);
|
|
73917
74047
|
if (entry.isDirectory() && recursive) {
|
|
73918
74048
|
await scanDir(entryPath);
|
|
73919
74049
|
} else if (entry.isFile()) {
|
|
@@ -73974,7 +74104,7 @@ var init_agent_only = __esm({
|
|
|
73974
74104
|
async execute(args, ctx) {
|
|
73975
74105
|
try {
|
|
73976
74106
|
const filePath = resolve84(ctx.workingDirectory, args.project);
|
|
73977
|
-
const content = await
|
|
74107
|
+
const content = await readFile56(filePath, "utf-8");
|
|
73978
74108
|
const data = JSON.parse(content);
|
|
73979
74109
|
const project = Project.fromJSON(data);
|
|
73980
74110
|
const sourceIds = args.sourceIds ?? [];
|
|
@@ -74045,7 +74175,7 @@ var init_agent_only = __esm({
|
|
|
74045
74175
|
async execute(args, ctx) {
|
|
74046
74176
|
try {
|
|
74047
74177
|
const filePath = resolve84(ctx.workingDirectory, args.project);
|
|
74048
|
-
const content = await
|
|
74178
|
+
const content = await readFile56(filePath, "utf-8");
|
|
74049
74179
|
const data = JSON.parse(content);
|
|
74050
74180
|
const project = Project.fromJSON(data);
|
|
74051
74181
|
const clipIds = args.clipIds ?? [];
|
|
@@ -74100,7 +74230,7 @@ var init_agent_only = __esm({
|
|
|
74100
74230
|
async execute(args, ctx) {
|
|
74101
74231
|
try {
|
|
74102
74232
|
const filePath = await resolveTimelineFile(args.project, ctx.workingDirectory);
|
|
74103
|
-
const content = await
|
|
74233
|
+
const content = await readFile56(filePath, "utf-8");
|
|
74104
74234
|
const data = JSON.parse(content);
|
|
74105
74235
|
const project = Project.fromJSON(data);
|
|
74106
74236
|
const what = args.what ?? "clips";
|
|
@@ -74177,7 +74307,7 @@ var init_agent_only = __esm({
|
|
|
74177
74307
|
async execute(args, ctx) {
|
|
74178
74308
|
try {
|
|
74179
74309
|
const filePath = await resolveTimelineFile(args.projectPath, ctx.workingDirectory);
|
|
74180
|
-
const content = await
|
|
74310
|
+
const content = await readFile56(filePath, "utf-8");
|
|
74181
74311
|
const data = JSON.parse(content);
|
|
74182
74312
|
const project = Project.fromJSON(data);
|
|
74183
74313
|
const updates = [];
|
|
@@ -74451,8 +74581,8 @@ Audio:`);
|
|
|
74451
74581
|
],
|
|
74452
74582
|
{ maxBuffer: 50 * 1024 * 1024 }
|
|
74453
74583
|
);
|
|
74454
|
-
const inputBuf = await
|
|
74455
|
-
const outputBuf = await
|
|
74584
|
+
const inputBuf = await readFile56(absInput);
|
|
74585
|
+
const outputBuf = await readFile56(outputPath);
|
|
74456
74586
|
const inputSize = inputBuf.length;
|
|
74457
74587
|
const outputSize = outputBuf.length;
|
|
74458
74588
|
const reduction = (inputSize - outputSize) / inputSize * 100;
|
|
@@ -75003,7 +75133,7 @@ init_source();
|
|
|
75003
75133
|
// package.json
|
|
75004
75134
|
var package_default = {
|
|
75005
75135
|
name: "@vibeframe/cli",
|
|
75006
|
-
version: "0.113.
|
|
75136
|
+
version: "0.113.11",
|
|
75007
75137
|
description: "VibeFrame CLI - agentic video workflow runtime",
|
|
75008
75138
|
private: false,
|
|
75009
75139
|
license: "MIT",
|
|
@@ -75347,8 +75477,8 @@ init_validate();
|
|
|
75347
75477
|
init_output();
|
|
75348
75478
|
init_cost_tier();
|
|
75349
75479
|
init_project_resolver();
|
|
75350
|
-
import { readFile as
|
|
75351
|
-
import { existsSync as
|
|
75480
|
+
import { readFile as readFile15, writeFile as writeFile13 } from "node:fs/promises";
|
|
75481
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
75352
75482
|
import { resolve as resolve18, basename as basename3, extname } from "node:path";
|
|
75353
75483
|
var timelineCommand = new Command("timeline").description("Low-level timeline JSON commands").addHelpText(
|
|
75354
75484
|
"after",
|
|
@@ -75397,7 +75527,7 @@ timelineCommand.command("add-source").description("Add a media source to the tim
|
|
|
75397
75527
|
return;
|
|
75398
75528
|
}
|
|
75399
75529
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75400
|
-
const content = await
|
|
75530
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75401
75531
|
const data = JSON.parse(content);
|
|
75402
75532
|
const project = Project.fromJSON(data);
|
|
75403
75533
|
const absMediaPath = resolve18(process.cwd(), mediaPath);
|
|
@@ -75462,7 +75592,7 @@ timelineCommand.command("add-clip").description("Add a clip to the timeline").ar
|
|
|
75462
75592
|
return;
|
|
75463
75593
|
}
|
|
75464
75594
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75465
|
-
const content = await
|
|
75595
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75466
75596
|
const data = JSON.parse(content);
|
|
75467
75597
|
const project = Project.fromJSON(data);
|
|
75468
75598
|
const source = project.getSource(sourceId);
|
|
@@ -75539,7 +75669,7 @@ timelineCommand.command("add-track").description("Add a new track").argument("<p
|
|
|
75539
75669
|
return;
|
|
75540
75670
|
}
|
|
75541
75671
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75542
|
-
const content = await
|
|
75672
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75543
75673
|
const data = JSON.parse(content);
|
|
75544
75674
|
const project = Project.fromJSON(data);
|
|
75545
75675
|
const existingTracks = project.getTracksByType(type);
|
|
@@ -75603,7 +75733,7 @@ timelineCommand.command("add-effect").description("Add an effect to a clip").arg
|
|
|
75603
75733
|
return;
|
|
75604
75734
|
}
|
|
75605
75735
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75606
|
-
const content = await
|
|
75736
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75607
75737
|
const data = JSON.parse(content);
|
|
75608
75738
|
const project = Project.fromJSON(data);
|
|
75609
75739
|
const clip = project.getClip(clipId);
|
|
@@ -75673,7 +75803,7 @@ timelineCommand.command("trim-clip").description("Trim a clip").argument("<proje
|
|
|
75673
75803
|
return;
|
|
75674
75804
|
}
|
|
75675
75805
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75676
|
-
const content = await
|
|
75806
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75677
75807
|
const data = JSON.parse(content);
|
|
75678
75808
|
const project = Project.fromJSON(data);
|
|
75679
75809
|
const clip = project.getClip(clipId);
|
|
@@ -75715,10 +75845,10 @@ timelineCommand.command("list").description("List timeline contents").argument("
|
|
|
75715
75845
|
const startedAt = Date.now();
|
|
75716
75846
|
try {
|
|
75717
75847
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75718
|
-
if (!
|
|
75848
|
+
if (!existsSync13(filePath)) {
|
|
75719
75849
|
exitWithError(notFoundError(projectPath));
|
|
75720
75850
|
}
|
|
75721
|
-
const content = await
|
|
75851
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75722
75852
|
const data = JSON.parse(content);
|
|
75723
75853
|
const project = Project.fromJSON(data);
|
|
75724
75854
|
const showAll = !options.sources && !options.tracks && !options.clips;
|
|
@@ -75839,7 +75969,7 @@ timelineCommand.command("split-clip").description("Split a clip at a specific ti
|
|
|
75839
75969
|
return;
|
|
75840
75970
|
}
|
|
75841
75971
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75842
|
-
const content = await
|
|
75972
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75843
75973
|
const data = JSON.parse(content);
|
|
75844
75974
|
const project = Project.fromJSON(data);
|
|
75845
75975
|
const clip = project.getClip(clipId);
|
|
@@ -75901,7 +76031,7 @@ timelineCommand.command("duplicate-clip").description("Duplicate a clip").argume
|
|
|
75901
76031
|
return;
|
|
75902
76032
|
}
|
|
75903
76033
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75904
|
-
const content = await
|
|
76034
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75905
76035
|
const data = JSON.parse(content);
|
|
75906
76036
|
const project = Project.fromJSON(data);
|
|
75907
76037
|
const clip = project.getClip(clipId);
|
|
@@ -75959,7 +76089,7 @@ timelineCommand.command("delete-clip").description("Delete a clip from the timel
|
|
|
75959
76089
|
return;
|
|
75960
76090
|
}
|
|
75961
76091
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75962
|
-
const content = await
|
|
76092
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75963
76093
|
const data = JSON.parse(content);
|
|
75964
76094
|
const project = Project.fromJSON(data);
|
|
75965
76095
|
const clip = project.getClip(clipId);
|
|
@@ -76014,7 +76144,7 @@ timelineCommand.command("move-clip").description("Move a clip to a new position"
|
|
|
76014
76144
|
return;
|
|
76015
76145
|
}
|
|
76016
76146
|
const filePath = await resolveTimelineFile(projectPath);
|
|
76017
|
-
const content = await
|
|
76147
|
+
const content = await readFile15(filePath, "utf-8");
|
|
76018
76148
|
const data = JSON.parse(content);
|
|
76019
76149
|
const project = Project.fromJSON(data);
|
|
76020
76150
|
const clip = project.getClip(clipId);
|
|
@@ -76100,7 +76230,7 @@ init_validate();
|
|
|
76100
76230
|
init_cost_tier();
|
|
76101
76231
|
init_scene_inspect();
|
|
76102
76232
|
init_render_inspect();
|
|
76103
|
-
import { readFile as
|
|
76233
|
+
import { readFile as readFile32, writeFile as writeFile37 } from "node:fs/promises";
|
|
76104
76234
|
import { resolve as resolve44 } from "node:path";
|
|
76105
76235
|
var VALID_RENDER_AI_MODELS = [
|
|
76106
76236
|
"flash",
|
|
@@ -76453,7 +76583,7 @@ inspectCommand.command("suggest").description("Get AI edit suggestions using Gem
|
|
|
76453
76583
|
const apiKey = await requireApiKey("GOOGLE_API_KEY", "Google", options.apiKey);
|
|
76454
76584
|
const spinner2 = ora("Initializing Gemini...").start();
|
|
76455
76585
|
const filePath = resolve44(process.cwd(), projectPath);
|
|
76456
|
-
const content = await
|
|
76586
|
+
const content = await readFile32(filePath, "utf-8");
|
|
76457
76587
|
const data = JSON.parse(content);
|
|
76458
76588
|
const project = Project.fromJSON(data);
|
|
76459
76589
|
const gemini = new GeminiProvider();
|
|
@@ -76522,8 +76652,8 @@ init_ai_helpers();
|
|
|
76522
76652
|
init_output();
|
|
76523
76653
|
init_validate();
|
|
76524
76654
|
import { resolve as resolve45, dirname as dirname16, basename as basename12, extname as extname8 } from "node:path";
|
|
76525
|
-
import { readFile as
|
|
76526
|
-
import { existsSync as
|
|
76655
|
+
import { readFile as readFile33, writeFile as writeFile38 } from "node:fs/promises";
|
|
76656
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
76527
76657
|
var audioCommand = new Command("audio").alias("au").description("Audio operations (transcribe, dub, duck, isolate, voice clone)").addHelpText(
|
|
76528
76658
|
"after",
|
|
76529
76659
|
`
|
|
@@ -76560,7 +76690,7 @@ audioCommand.command("transcribe").description("Transcribe audio using Whisper")
|
|
|
76560
76690
|
await whisper.initialize({ apiKey });
|
|
76561
76691
|
spinner2.text = "Reading audio file...";
|
|
76562
76692
|
const absPath = resolve45(process.cwd(), audioPath);
|
|
76563
|
-
const audioBuffer = await
|
|
76693
|
+
const audioBuffer = await readFile33(absPath);
|
|
76564
76694
|
const audioBlob = new Blob([audioBuffer]);
|
|
76565
76695
|
spinner2.text = "Transcribing...";
|
|
76566
76696
|
const result = await whisper.transcribe(audioBlob, options.language);
|
|
@@ -76664,7 +76794,7 @@ audioCommand.command("isolate").description("Isolate vocals from audio using Ele
|
|
|
76664
76794
|
const apiKey = await requireApiKey("ELEVENLABS_API_KEY", "ElevenLabs", options.apiKey);
|
|
76665
76795
|
const spinner2 = ora("Reading audio file...").start();
|
|
76666
76796
|
const absPath = resolve45(process.cwd(), audioPath);
|
|
76667
|
-
const audioBuffer = await
|
|
76797
|
+
const audioBuffer = await readFile33(absPath);
|
|
76668
76798
|
spinner2.text = "Isolating vocals...";
|
|
76669
76799
|
const elevenlabs = new ElevenLabsProvider();
|
|
76670
76800
|
await elevenlabs.initialize({ apiKey });
|
|
@@ -76735,11 +76865,11 @@ audioCommand.command("clone-voice").description("Clone a voice from audio sample
|
|
|
76735
76865
|
const audioBuffers = [];
|
|
76736
76866
|
for (const samplePath of samples) {
|
|
76737
76867
|
const absPath = resolve45(process.cwd(), samplePath);
|
|
76738
|
-
if (!
|
|
76868
|
+
if (!existsSync37(absPath)) {
|
|
76739
76869
|
spinner2.fail(`File not found: ${samplePath}`);
|
|
76740
76870
|
exitWithError(notFoundError(samplePath));
|
|
76741
76871
|
}
|
|
76742
|
-
const buffer = await
|
|
76872
|
+
const buffer = await readFile33(absPath);
|
|
76743
76873
|
audioBuffers.push(buffer);
|
|
76744
76874
|
}
|
|
76745
76875
|
spinner2.text = `Cloning voice from ${audioBuffers.length} sample(s)...`;
|
|
@@ -76803,7 +76933,7 @@ audioCommand.command("dub").description("Dub audio/video to another language (tr
|
|
|
76803
76933
|
exitWithError(usageError("Target language is required. Use -l or --language"));
|
|
76804
76934
|
}
|
|
76805
76935
|
const absPath = resolve45(process.cwd(), mediaPath);
|
|
76806
|
-
if (!
|
|
76936
|
+
if (!existsSync37(absPath)) {
|
|
76807
76937
|
exitWithError(notFoundError(mediaPath));
|
|
76808
76938
|
}
|
|
76809
76939
|
const openaiKey = await requireApiKey("OPENAI_API_KEY", "OpenAI");
|
|
@@ -76828,7 +76958,7 @@ audioCommand.command("dub").description("Dub audio/video to another language (tr
|
|
|
76828
76958
|
spinner2.text = "Transcribing audio...";
|
|
76829
76959
|
const whisper = new WhisperProvider();
|
|
76830
76960
|
await whisper.initialize({ apiKey: openaiKey });
|
|
76831
|
-
const audioBuffer = await
|
|
76961
|
+
const audioBuffer = await readFile33(audioPath);
|
|
76832
76962
|
const audioBlob = new Blob([audioBuffer]);
|
|
76833
76963
|
const transcriptResult = await whisper.transcribe(audioBlob, options.source);
|
|
76834
76964
|
if (transcriptResult.status === "failed" || !transcriptResult.segments) {
|
|
@@ -77084,7 +77214,7 @@ init_esm();
|
|
|
77084
77214
|
init_source();
|
|
77085
77215
|
init_ora();
|
|
77086
77216
|
import { resolve as resolve50, basename as basename15 } from "node:path";
|
|
77087
|
-
import { existsSync as
|
|
77217
|
+
import { existsSync as existsSync42 } from "node:fs";
|
|
77088
77218
|
|
|
77089
77219
|
// src/commands/ai-script-pipeline-cli.ts
|
|
77090
77220
|
init_source();
|
|
@@ -77093,9 +77223,9 @@ var import_yaml7 = __toESM(require_dist2(), 1);
|
|
|
77093
77223
|
init_api_key();
|
|
77094
77224
|
init_ai_script_pipeline();
|
|
77095
77225
|
init_output();
|
|
77096
|
-
import { readFile as
|
|
77226
|
+
import { readFile as readFile35, writeFile as writeFile40 } from "node:fs/promises";
|
|
77097
77227
|
import { resolve as resolve47 } from "node:path";
|
|
77098
|
-
import { existsSync as
|
|
77228
|
+
import { existsSync as existsSync39 } from "node:fs";
|
|
77099
77229
|
function registerScriptPipelineCommands(aiCommand) {
|
|
77100
77230
|
aiCommand.command("regenerate-scene", { hidden: true }).description("Regenerate a specific scene in a script-to-video output directory").argument("<project-dir>", "Path to the script-to-video output directory").requiredOption(
|
|
77101
77231
|
"--scene <numbers>",
|
|
@@ -77112,12 +77242,12 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
77112
77242
|
try {
|
|
77113
77243
|
const outputDir = resolve47(process.cwd(), projectDir);
|
|
77114
77244
|
const projectPath = resolve47(outputDir, "timeline.json");
|
|
77115
|
-
if (!
|
|
77245
|
+
if (!existsSync39(outputDir)) {
|
|
77116
77246
|
exitWithError(notFoundError(outputDir));
|
|
77117
77247
|
}
|
|
77118
77248
|
const yamlPath = resolve47(outputDir, "storyboard.yaml");
|
|
77119
77249
|
const jsonPath = resolve47(outputDir, "storyboard.json");
|
|
77120
|
-
const storyboardPath2 =
|
|
77250
|
+
const storyboardPath2 = existsSync39(yamlPath) ? yamlPath : existsSync39(jsonPath) ? jsonPath : null;
|
|
77121
77251
|
const storyboardIsYaml = storyboardPath2 === yamlPath;
|
|
77122
77252
|
if (!storyboardPath2) {
|
|
77123
77253
|
exitWithError(notFoundError(`${outputDir}/storyboard.{yaml,json}`));
|
|
@@ -77153,7 +77283,7 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
77153
77283
|
});
|
|
77154
77284
|
return;
|
|
77155
77285
|
}
|
|
77156
|
-
const content = await
|
|
77286
|
+
const content = await readFile35(storyboardPath2, "utf-8");
|
|
77157
77287
|
const segments = storyboardIsYaml ? (0, import_yaml7.parse)(content).scenes : JSON.parse(content);
|
|
77158
77288
|
for (const sceneNum of sceneNums) {
|
|
77159
77289
|
if (sceneNum > segments.length) {
|
|
@@ -77236,12 +77366,12 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
77236
77366
|
exitWithError(apiError(result.error || "Scene regeneration failed", true));
|
|
77237
77367
|
}
|
|
77238
77368
|
spinner2.succeed(source_default.green(`Regenerated ${result.regeneratedScenes.length} scene(s)`));
|
|
77239
|
-
if (
|
|
77369
|
+
if (existsSync39(projectPath) && result.regeneratedScenes.length > 0) {
|
|
77240
77370
|
const updateSpinner = ora("Updating timeline file...").start();
|
|
77241
77371
|
try {
|
|
77242
|
-
const updatedContent = await
|
|
77372
|
+
const updatedContent = await readFile35(storyboardPath2, "utf-8");
|
|
77243
77373
|
const updatedSegments = storyboardIsYaml ? (0, import_yaml7.parse)(updatedContent).scenes : JSON.parse(updatedContent);
|
|
77244
|
-
const projectContent = await
|
|
77374
|
+
const projectContent = await readFile35(projectPath, "utf-8");
|
|
77245
77375
|
const projectData = JSON.parse(projectContent);
|
|
77246
77376
|
for (const sceneNum of result.regeneratedScenes) {
|
|
77247
77377
|
const segment = updatedSegments[sceneNum - 1];
|
|
@@ -77252,7 +77382,7 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
77252
77382
|
(s) => s.name === `Scene ${sceneNum}`
|
|
77253
77383
|
);
|
|
77254
77384
|
if (videoSource) {
|
|
77255
|
-
const hasVideo =
|
|
77385
|
+
const hasVideo = existsSync39(videoPath);
|
|
77256
77386
|
videoSource.url = hasVideo ? videoPath : imagePath;
|
|
77257
77387
|
videoSource.type = hasVideo ? "video" : "image";
|
|
77258
77388
|
videoSource.duration = segment.duration;
|
|
@@ -77377,7 +77507,7 @@ Required API Key: OPENAI_API_KEY (Whisper transcription)
|
|
|
77377
77507
|
const startedAt = Date.now();
|
|
77378
77508
|
try {
|
|
77379
77509
|
const absVideoPath = resolve50(process.cwd(), videoPath);
|
|
77380
|
-
if (!
|
|
77510
|
+
if (!existsSync42(absVideoPath)) {
|
|
77381
77511
|
exitWithError(notFoundError(absVideoPath));
|
|
77382
77512
|
}
|
|
77383
77513
|
if (!ANIMATED_CAPTION_STYLES.includes(options.style)) {
|
|
@@ -81760,8 +81890,8 @@ init_ora();
|
|
|
81760
81890
|
init_engine();
|
|
81761
81891
|
init_output();
|
|
81762
81892
|
init_cost_tier();
|
|
81763
|
-
import { readFile as
|
|
81764
|
-
import { resolve as resolve52, basename as basename17, extname as extname12, join as
|
|
81893
|
+
import { readFile as readFile37, writeFile as writeFile43, readdir as readdir6 } from "node:fs/promises";
|
|
81894
|
+
import { resolve as resolve52, basename as basename17, extname as extname12, join as join21 } from "node:path";
|
|
81765
81895
|
var batchCommand = new Command("batch").description("Batch operations for processing multiple items");
|
|
81766
81896
|
function detectMediaType3(filePath) {
|
|
81767
81897
|
const ext = extname12(filePath).toLowerCase();
|
|
@@ -81819,7 +81949,7 @@ batchCommand.command("import").description("Import multiple media files from a d
|
|
|
81819
81949
|
return;
|
|
81820
81950
|
}
|
|
81821
81951
|
const filePath = resolve52(process.cwd(), projectPath);
|
|
81822
|
-
const content = await
|
|
81952
|
+
const content = await readFile37(filePath, "utf-8");
|
|
81823
81953
|
const data = JSON.parse(content);
|
|
81824
81954
|
const project = Project.fromJSON(data);
|
|
81825
81955
|
const dirPath = resolve52(process.cwd(), directory);
|
|
@@ -81828,7 +81958,7 @@ batchCommand.command("import").description("Import multiple media files from a d
|
|
|
81828
81958
|
const scanDir = async (dir) => {
|
|
81829
81959
|
const entries = await readdir6(dir, { withFileTypes: true });
|
|
81830
81960
|
for (const entry of entries) {
|
|
81831
|
-
const entryPath =
|
|
81961
|
+
const entryPath = join21(dir, entry.name);
|
|
81832
81962
|
if (entry.isDirectory() && options.recursive) {
|
|
81833
81963
|
await scanDir(entryPath);
|
|
81834
81964
|
} else if (entry.isFile()) {
|
|
@@ -81897,7 +82027,7 @@ batchCommand.command("concat").description("Concatenate multiple sources into se
|
|
|
81897
82027
|
return;
|
|
81898
82028
|
}
|
|
81899
82029
|
const filePath = resolve52(process.cwd(), projectPath);
|
|
81900
|
-
const content = await
|
|
82030
|
+
const content = await readFile37(filePath, "utf-8");
|
|
81901
82031
|
const data = JSON.parse(content);
|
|
81902
82032
|
const project = Project.fromJSON(data);
|
|
81903
82033
|
const sources = project.getSources();
|
|
@@ -81976,7 +82106,7 @@ batchCommand.command("apply-effect").description("Apply an effect to multiple cl
|
|
|
81976
82106
|
return;
|
|
81977
82107
|
}
|
|
81978
82108
|
const filePath = resolve52(process.cwd(), projectPath);
|
|
81979
|
-
const content = await
|
|
82109
|
+
const content = await readFile37(filePath, "utf-8");
|
|
81980
82110
|
const data = JSON.parse(content);
|
|
81981
82111
|
const project = Project.fromJSON(data);
|
|
81982
82112
|
const clips = project.getClips();
|
|
@@ -82044,7 +82174,7 @@ batchCommand.command("remove-clips").description("Remove multiple clips from the
|
|
|
82044
82174
|
return;
|
|
82045
82175
|
}
|
|
82046
82176
|
const filePath = resolve52(process.cwd(), projectPath);
|
|
82047
|
-
const content = await
|
|
82177
|
+
const content = await readFile37(filePath, "utf-8");
|
|
82048
82178
|
const data = JSON.parse(content);
|
|
82049
82179
|
const project = Project.fromJSON(data);
|
|
82050
82180
|
const clips = project.getClips();
|
|
@@ -82082,7 +82212,7 @@ batchCommand.command("info").description("Show batch processing statistics").arg
|
|
|
82082
82212
|
const spinner2 = ora("Loading project...").start();
|
|
82083
82213
|
try {
|
|
82084
82214
|
const filePath = resolve52(process.cwd(), projectPath);
|
|
82085
|
-
const content = await
|
|
82215
|
+
const content = await readFile37(filePath, "utf-8");
|
|
82086
82216
|
const data = JSON.parse(content);
|
|
82087
82217
|
const project = Project.fromJSON(data);
|
|
82088
82218
|
const sources = project.getSources();
|
|
@@ -82142,7 +82272,7 @@ init_output();
|
|
|
82142
82272
|
init_tty();
|
|
82143
82273
|
init_api_key();
|
|
82144
82274
|
import { resolve as resolve54 } from "node:path";
|
|
82145
|
-
import { access as access6, readFile as
|
|
82275
|
+
import { access as access6, readFile as readFile39 } from "node:fs/promises";
|
|
82146
82276
|
import { homedir as homedir6 } from "node:os";
|
|
82147
82277
|
|
|
82148
82278
|
// src/utils/key-format.ts
|
|
@@ -83343,7 +83473,7 @@ async function showConfig(opts = { verbose: false }) {
|
|
|
83343
83473
|
let dotenvKeys = {};
|
|
83344
83474
|
if (hasCwdEnv) {
|
|
83345
83475
|
try {
|
|
83346
|
-
const envContent = await
|
|
83476
|
+
const envContent = await readFile39(cwdEnvPath, "utf-8");
|
|
83347
83477
|
dotenvKeys = (0, import_dotenv2.parse)(Buffer.from(envContent));
|
|
83348
83478
|
} catch {
|
|
83349
83479
|
}
|
|
@@ -83440,8 +83570,8 @@ init_source();
|
|
|
83440
83570
|
init_agent_host_detect();
|
|
83441
83571
|
init_scene_project();
|
|
83442
83572
|
init_visual_styles();
|
|
83443
|
-
import { existsSync as
|
|
83444
|
-
import { mkdir as mkdir24, readFile as
|
|
83573
|
+
import { existsSync as existsSync44 } from "node:fs";
|
|
83574
|
+
import { mkdir as mkdir24, readFile as readFile41, writeFile as writeFile46 } from "node:fs/promises";
|
|
83445
83575
|
import { basename as basename19, dirname as dirname20, resolve as resolve56 } from "node:path";
|
|
83446
83576
|
|
|
83447
83577
|
// src/commands/_shared/storyboard-draft.ts
|
|
@@ -83604,10 +83734,10 @@ init_project_config();
|
|
|
83604
83734
|
init_install_skill();
|
|
83605
83735
|
|
|
83606
83736
|
// src/commands/_shared/host-integration.ts
|
|
83607
|
-
import { existsSync as
|
|
83608
|
-
import { mkdir as mkdir23, readFile as
|
|
83737
|
+
import { existsSync as existsSync43 } from "node:fs";
|
|
83738
|
+
import { mkdir as mkdir23, readFile as readFile40, writeFile as writeFile45 } from "node:fs/promises";
|
|
83609
83739
|
import { homedir as homedir7 } from "node:os";
|
|
83610
|
-
import { dirname as dirname19, join as
|
|
83740
|
+
import { dirname as dirname19, join as join22, resolve as resolve55 } from "node:path";
|
|
83611
83741
|
var HOST_SELECTIONS = [
|
|
83612
83742
|
"codex",
|
|
83613
83743
|
"claude",
|
|
@@ -83634,7 +83764,7 @@ function hostDefinitions(_projectDir = process.cwd()) {
|
|
|
83634
83764
|
label: "OpenAI Codex",
|
|
83635
83765
|
surfaces: ["shell", "project-guidance", "mcp"],
|
|
83636
83766
|
guidanceFiles: ["AGENTS.md"],
|
|
83637
|
-
configPath: (dir) =>
|
|
83767
|
+
configPath: (dir) => join22(resolve55(dir), ".codex", "config.toml"),
|
|
83638
83768
|
configKind: "codex-toml",
|
|
83639
83769
|
notes: [
|
|
83640
83770
|
"Codex reads AGENTS.md and can also load project-scoped .codex/config.toml after the project is trusted.",
|
|
@@ -83647,7 +83777,7 @@ function hostDefinitions(_projectDir = process.cwd()) {
|
|
|
83647
83777
|
label: "Claude Code",
|
|
83648
83778
|
surfaces: ["shell", "project-guidance", "mcp"],
|
|
83649
83779
|
guidanceFiles: ["AGENTS.md", "CLAUDE.md", ".claude/skills/hyperframes/"],
|
|
83650
|
-
configPath: (dir) =>
|
|
83780
|
+
configPath: (dir) => join22(resolve55(dir), ".mcp.json"),
|
|
83651
83781
|
configKind: "mcp-json",
|
|
83652
83782
|
notes: [
|
|
83653
83783
|
"Claude Code can drive vibe directly through shell plus AGENTS.md/CLAUDE.md.",
|
|
@@ -83672,7 +83802,7 @@ function hostDefinitions(_projectDir = process.cwd()) {
|
|
|
83672
83802
|
label: "Cursor",
|
|
83673
83803
|
surfaces: ["shell", "project-guidance", "mcp"],
|
|
83674
83804
|
guidanceFiles: ["AGENTS.md", ".cursor/rules/hyperframes.mdc"],
|
|
83675
|
-
configPath: (dir) =>
|
|
83805
|
+
configPath: (dir) => join22(resolve55(dir), ".cursor", "mcp.json"),
|
|
83676
83806
|
configKind: "mcp-json",
|
|
83677
83807
|
notes: [
|
|
83678
83808
|
"Cursor can use AGENTS.md/rules for shell-driven work and .cursor/mcp.json for typed MCP tools."
|
|
@@ -83713,11 +83843,11 @@ async function planHostSetup(host, projectDir, opts) {
|
|
|
83713
83843
|
const warnings = [];
|
|
83714
83844
|
const nextSteps = hostNextSteps(host);
|
|
83715
83845
|
for (const relPath of host.guidanceFiles.filter((p) => !p.endsWith("/"))) {
|
|
83716
|
-
const absPath =
|
|
83846
|
+
const absPath = join22(resolve55(projectDir), relPath);
|
|
83717
83847
|
files.push({
|
|
83718
83848
|
path: absPath,
|
|
83719
|
-
status:
|
|
83720
|
-
reason:
|
|
83849
|
+
status: existsSync43(absPath) ? "skipped-exists" : "missing",
|
|
83850
|
+
reason: existsSync43(absPath) ? "already present" : "run vibe init --agent all first"
|
|
83721
83851
|
});
|
|
83722
83852
|
}
|
|
83723
83853
|
if (!opts.write || opts.dryRun) {
|
|
@@ -83761,17 +83891,17 @@ async function inspectHost(host, projectDir) {
|
|
|
83761
83891
|
const warnings = [];
|
|
83762
83892
|
const nextSteps = hostNextSteps(host);
|
|
83763
83893
|
const guidanceFiles = host.guidanceFiles.map((relPath) => {
|
|
83764
|
-
const absPath = relPath.endsWith("/") ?
|
|
83894
|
+
const absPath = relPath.endsWith("/") ? join22(resolve55(projectDir), relPath) : join22(resolve55(projectDir), relPath);
|
|
83765
83895
|
return {
|
|
83766
83896
|
path: absPath,
|
|
83767
|
-
status:
|
|
83768
|
-
reason:
|
|
83897
|
+
status: existsSync43(absPath) ? "skipped-exists" : "missing",
|
|
83898
|
+
reason: existsSync43(absPath) ? "present" : "missing"
|
|
83769
83899
|
};
|
|
83770
83900
|
});
|
|
83771
83901
|
let configured = false;
|
|
83772
|
-
if (
|
|
83902
|
+
if (existsSync43(configPath)) {
|
|
83773
83903
|
try {
|
|
83774
|
-
const content = await
|
|
83904
|
+
const content = await readFile40(configPath, "utf-8");
|
|
83775
83905
|
if (host.configKind === "codex-toml") {
|
|
83776
83906
|
configured = /\[mcp_servers\.vibeframe\]/.test(content);
|
|
83777
83907
|
} else {
|
|
@@ -83792,9 +83922,9 @@ async function inspectHost(host, projectDir) {
|
|
|
83792
83922
|
}
|
|
83793
83923
|
async function mergeMcpJson(configPath, opts) {
|
|
83794
83924
|
let root = {};
|
|
83795
|
-
const exists =
|
|
83925
|
+
const exists = existsSync43(configPath);
|
|
83796
83926
|
if (exists) {
|
|
83797
|
-
const raw = await
|
|
83927
|
+
const raw = await readFile40(configPath, "utf-8");
|
|
83798
83928
|
try {
|
|
83799
83929
|
root = raw.trim() ? JSON.parse(raw) : {};
|
|
83800
83930
|
} catch (error) {
|
|
@@ -83815,7 +83945,7 @@ async function mergeMcpJson(configPath, opts) {
|
|
|
83815
83945
|
if (exists && opts.backup) {
|
|
83816
83946
|
await writeFile45(
|
|
83817
83947
|
`${configPath}.bak-${timestamp()}`,
|
|
83818
|
-
await
|
|
83948
|
+
await readFile40(configPath, "utf-8"),
|
|
83819
83949
|
"utf-8"
|
|
83820
83950
|
);
|
|
83821
83951
|
}
|
|
@@ -83837,7 +83967,7 @@ async function mergeMcpJson(configPath, opts) {
|
|
|
83837
83967
|
if (exists && opts.backup) {
|
|
83838
83968
|
await writeFile45(
|
|
83839
83969
|
`${configPath}.bak-${timestamp()}`,
|
|
83840
|
-
await
|
|
83970
|
+
await readFile40(configPath, "utf-8"),
|
|
83841
83971
|
"utf-8"
|
|
83842
83972
|
);
|
|
83843
83973
|
}
|
|
@@ -83845,7 +83975,7 @@ async function mergeMcpJson(configPath, opts) {
|
|
|
83845
83975
|
return { path: configPath, status: exists ? "merged" : "wrote" };
|
|
83846
83976
|
}
|
|
83847
83977
|
async function mergeCodexToml(configPath, opts) {
|
|
83848
|
-
const exists =
|
|
83978
|
+
const exists = existsSync43(configPath);
|
|
83849
83979
|
const block = `${CODEX_MANAGED_START}
|
|
83850
83980
|
${CODEX_MCP_BLOCK}${CODEX_MANAGED_END}`;
|
|
83851
83981
|
if (!exists) {
|
|
@@ -83860,7 +83990,7 @@ ${block}
|
|
|
83860
83990
|
);
|
|
83861
83991
|
return { path: configPath, status: "wrote" };
|
|
83862
83992
|
}
|
|
83863
|
-
const raw = await
|
|
83993
|
+
const raw = await readFile40(configPath, "utf-8");
|
|
83864
83994
|
if (raw.includes(CODEX_MANAGED_START)) {
|
|
83865
83995
|
const updated = raw.replace(
|
|
83866
83996
|
new RegExp(
|
|
@@ -83949,16 +84079,16 @@ function hostNextSteps(host) {
|
|
|
83949
84079
|
function claudeDesktopConfigPath() {
|
|
83950
84080
|
const home = homedir7();
|
|
83951
84081
|
if (process.platform === "darwin") {
|
|
83952
|
-
return
|
|
84082
|
+
return join22(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
83953
84083
|
}
|
|
83954
84084
|
if (process.platform === "win32") {
|
|
83955
|
-
return
|
|
83956
|
-
process.env.APPDATA ??
|
|
84085
|
+
return join22(
|
|
84086
|
+
process.env.APPDATA ?? join22(home, "AppData", "Roaming"),
|
|
83957
84087
|
"Claude",
|
|
83958
84088
|
"claude_desktop_config.json"
|
|
83959
84089
|
);
|
|
83960
84090
|
}
|
|
83961
|
-
return
|
|
84091
|
+
return join22(home, ".config", "Claude", "claude_desktop_config.json");
|
|
83962
84092
|
}
|
|
83963
84093
|
function timestamp() {
|
|
83964
84094
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -84411,7 +84541,7 @@ var initCommand = new Command("init").description("Scaffold a VibeFrame project
|
|
|
84411
84541
|
const wantsGemini = targetHosts.includes("gemini-cli");
|
|
84412
84542
|
const wantsCrossTool = targetHosts.some((h) => h !== "claude-code");
|
|
84413
84543
|
const actions = [];
|
|
84414
|
-
if (!
|
|
84544
|
+
if (!existsSync44(projectDir)) {
|
|
84415
84545
|
if (options.dryRun) {
|
|
84416
84546
|
actions.push({
|
|
84417
84547
|
path: projectDir,
|
|
@@ -84626,11 +84756,11 @@ async function runSceneInit(projectDirArg, options, startedAt) {
|
|
|
84626
84756
|
});
|
|
84627
84757
|
const storyboardPath2 = resolve56(projectDir, "STORYBOARD.md");
|
|
84628
84758
|
const designPath = resolve56(projectDir, "DESIGN.md");
|
|
84629
|
-
if (options.force || result.created.includes(storyboardPath2) || !
|
|
84759
|
+
if (options.force || result.created.includes(storyboardPath2) || !existsSync44(storyboardPath2)) {
|
|
84630
84760
|
await writeFile46(storyboardPath2, draft.storyboardMd, "utf-8");
|
|
84631
84761
|
if (!result.created.includes(storyboardPath2)) result.created.push(storyboardPath2);
|
|
84632
84762
|
}
|
|
84633
|
-
if (options.force || result.created.includes(designPath) || !
|
|
84763
|
+
if (options.force || result.created.includes(designPath) || !existsSync44(designPath)) {
|
|
84634
84764
|
await writeFile46(designPath, draft.designMd, "utf-8");
|
|
84635
84765
|
if (!result.created.includes(designPath)) result.created.push(designPath);
|
|
84636
84766
|
}
|
|
@@ -84754,8 +84884,8 @@ function printGroup(label, files, projectDir) {
|
|
|
84754
84884
|
}
|
|
84755
84885
|
async function readBriefOrLiteral(value, cwd) {
|
|
84756
84886
|
const candidate = resolve56(cwd, value);
|
|
84757
|
-
if (
|
|
84758
|
-
return
|
|
84887
|
+
if (existsSync44(candidate)) {
|
|
84888
|
+
return readFile41(candidate, "utf-8");
|
|
84759
84889
|
}
|
|
84760
84890
|
return value;
|
|
84761
84891
|
}
|
|
@@ -84808,7 +84938,7 @@ function normalizeHostAction(status) {
|
|
|
84808
84938
|
return "would-write";
|
|
84809
84939
|
}
|
|
84810
84940
|
async function writeIfMissing(absPath, content, force, dryRun) {
|
|
84811
|
-
const exists =
|
|
84941
|
+
const exists = existsSync44(absPath);
|
|
84812
84942
|
if (exists && !force) {
|
|
84813
84943
|
return { path: absPath, status: "skipped-exists", reason: "(use --force to overwrite)" };
|
|
84814
84944
|
}
|
|
@@ -84821,12 +84951,12 @@ async function writeIfMissing(absPath, content, force, dryRun) {
|
|
|
84821
84951
|
}
|
|
84822
84952
|
async function mergeGitignore(absPath, additions, dryRun) {
|
|
84823
84953
|
const marker = "# VibeFrame";
|
|
84824
|
-
if (!
|
|
84954
|
+
if (!existsSync44(absPath)) {
|
|
84825
84955
|
if (dryRun) return { path: absPath, status: "would-write" };
|
|
84826
84956
|
await writeFile46(absPath, additions.trimStart(), "utf-8");
|
|
84827
84957
|
return { path: absPath, status: "wrote" };
|
|
84828
84958
|
}
|
|
84829
|
-
const existing = await
|
|
84959
|
+
const existing = await readFile41(absPath, "utf-8");
|
|
84830
84960
|
if (existing.includes(marker)) {
|
|
84831
84961
|
return { path: absPath, status: "skipped-exists", reason: "(already has VibeFrame block)" };
|
|
84832
84962
|
}
|
|
@@ -84859,8 +84989,8 @@ init_storyboard_revise();
|
|
|
84859
84989
|
init_composer_resolve();
|
|
84860
84990
|
init_cost_tier();
|
|
84861
84991
|
init_output();
|
|
84862
|
-
import { readFile as
|
|
84863
|
-
import { existsSync as
|
|
84992
|
+
import { readFile as readFile43, writeFile as writeFile48 } from "node:fs/promises";
|
|
84993
|
+
import { existsSync as existsSync46 } from "node:fs";
|
|
84864
84994
|
import { resolve as resolve58 } from "node:path";
|
|
84865
84995
|
var storyboardCommand = new Command("storyboard").description("Read, validate, and safely mutate STORYBOARD.md cue blocks").addHelpText("after", `
|
|
84866
84996
|
Examples:
|
|
@@ -85043,10 +85173,10 @@ storyboardCommand.command("validate").description("Validate cue blocks and beat
|
|
|
85043
85173
|
});
|
|
85044
85174
|
async function readStoryboard(projectDir) {
|
|
85045
85175
|
const path2 = storyboardPath(projectDir);
|
|
85046
|
-
if (!
|
|
85176
|
+
if (!existsSync46(path2)) {
|
|
85047
85177
|
exitWithError(generalError(`STORYBOARD.md not found at ${path2}`, `Run 'vibe init ${projectDir} --from "<brief>"' first.`));
|
|
85048
85178
|
}
|
|
85049
|
-
return
|
|
85179
|
+
return readFile43(path2, "utf-8");
|
|
85050
85180
|
}
|
|
85051
85181
|
function storyboardPath(projectDir) {
|
|
85052
85182
|
return resolve58(projectDir, "STORYBOARD.md");
|
|
@@ -85058,8 +85188,8 @@ function parseComposer(value) {
|
|
|
85058
85188
|
}
|
|
85059
85189
|
async function readBrief(value, cwd) {
|
|
85060
85190
|
const candidate = resolve58(cwd, value);
|
|
85061
|
-
if (
|
|
85062
|
-
return
|
|
85191
|
+
if (existsSync46(candidate)) {
|
|
85192
|
+
return readFile43(candidate, "utf-8");
|
|
85063
85193
|
}
|
|
85064
85194
|
return value;
|
|
85065
85195
|
}
|
|
@@ -85285,8 +85415,8 @@ init_scene_build();
|
|
|
85285
85415
|
init_build_plan();
|
|
85286
85416
|
init_storyboard_parse();
|
|
85287
85417
|
init_output();
|
|
85288
|
-
import { resolve as resolve66, join as
|
|
85289
|
-
import { readFileSync as readFileSync4, existsSync as
|
|
85418
|
+
import { resolve as resolve66, join as join27 } from "node:path";
|
|
85419
|
+
import { readFileSync as readFileSync4, existsSync as existsSync51 } from "node:fs";
|
|
85290
85420
|
var VALID_MODES2 = ["agent", "batch", "auto"];
|
|
85291
85421
|
var VALID_STAGES2 = ["assets", "compose", "sync", "render", "all"];
|
|
85292
85422
|
var VALID_IMAGE_PROVIDERS2 = ["openai", "gemini", "grok"];
|
|
@@ -85295,7 +85425,10 @@ var VALID_MUSIC_PROVIDERS2 = ["elevenlabs", "replicate"];
|
|
|
85295
85425
|
var buildCommand = new Command("build").description("Build a VibeFrame video project from STORYBOARD.md").argument("[project-dir]", "Video project directory", ".").option("--stage <stage>", `Build stage: ${VALID_STAGES2.join("|")}`, "all").option("--beat <id>", "Restrict asset/compose work to one beat id").option("--mode <mode>", "Build mode: agent|batch|auto", "auto").option("--effort <level>", "Compose effort tier (batch mode only): low|medium|high", "medium").option("--composer <provider>", "Batch composer: claude|openai|gemini").option("--max-cost <usd>", "Fail before provider spend when estimated cost exceeds this USD cap").option("--skip-narration", "Don't dispatch TTS even when beats declare narration cues").option("--skip-backdrop", "Don't dispatch image-gen even when beats declare backdrop cues").option("--skip-video", "Don't dispatch video generation even when beats declare video cues").option(
|
|
85296
85426
|
"--skip-keyframe",
|
|
85297
85427
|
"Don't generate keyframe stills (review keyframes first with --skip-video, then build)"
|
|
85298
|
-
).option("--skip-music", "Don't dispatch music generation even when beats declare music cues").option(
|
|
85428
|
+
).option("--skip-music", "Don't dispatch music generation even when beats declare music cues").option(
|
|
85429
|
+
"--skip-transcript",
|
|
85430
|
+
"Don't transcribe narration for word-sync (default: transcribe when narration + OpenAI key exist)"
|
|
85431
|
+
).option("--skip-render", "Compose only \u2014 don't render to MP4").option("--tts <provider>", "TTS provider: auto|elevenlabs|openai|kokoro").option("--voice <id>", "Voice id").option("--image-provider <name>", `Image provider: ${VALID_IMAGE_PROVIDERS2.join("|")}`).option("--video-provider <name>", `Video provider: ${VALID_VIDEO_PROVIDERS2.join("|")}`).option("--music-provider <name>", `Music provider: ${VALID_MUSIC_PROVIDERS2.join("|")}`).option("--quality <q>", "Image quality: standard|hd", "hd").option("--image-size <s>", "Image size: 1024x1024|1536x1024|1024x1536", "1536x1024").option("--force", "Re-dispatch primitives even when assets already exist").option("--dry-run", "Preview parameters without dispatching").addHelpText(
|
|
85299
85432
|
"after",
|
|
85300
85433
|
`
|
|
85301
85434
|
Examples:
|
|
@@ -85356,6 +85489,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
85356
85489
|
skipVideo: options.skipVideo ?? false,
|
|
85357
85490
|
skipKeyframe: options.skipKeyframe ?? false,
|
|
85358
85491
|
skipMusic: options.skipMusic ?? false,
|
|
85492
|
+
skipTranscript: options.skipTranscript ?? false,
|
|
85359
85493
|
skipRender: options.skipRender ?? false,
|
|
85360
85494
|
ttsProvider: options.tts,
|
|
85361
85495
|
voice: options.voice,
|
|
@@ -85377,6 +85511,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
85377
85511
|
skipVideo: options.skipVideo,
|
|
85378
85512
|
skipKeyframe: options.skipKeyframe,
|
|
85379
85513
|
skipMusic: options.skipMusic,
|
|
85514
|
+
skipTranscript: options.skipTranscript,
|
|
85380
85515
|
ttsProvider: options.tts,
|
|
85381
85516
|
voice: options.voice,
|
|
85382
85517
|
imageProvider,
|
|
@@ -85468,6 +85603,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
85468
85603
|
skipVideo: options.skipVideo,
|
|
85469
85604
|
skipKeyframe: options.skipKeyframe,
|
|
85470
85605
|
skipMusic: options.skipMusic,
|
|
85606
|
+
skipTranscript: options.skipTranscript,
|
|
85471
85607
|
skipRender: options.skipRender,
|
|
85472
85608
|
ttsProvider: options.tts,
|
|
85473
85609
|
voice: options.voice,
|
|
@@ -85480,6 +85616,8 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
85480
85616
|
onProgress: (e) => {
|
|
85481
85617
|
if (!spinner2) return;
|
|
85482
85618
|
if (e.type === "phase-start") spinner2.text = `Phase: ${e.phase}...`;
|
|
85619
|
+
else if (e.type === "transcript-generated")
|
|
85620
|
+
spinner2.text = `Transcribed narration: ${e.beatId} (${e.wordCount} words)`;
|
|
85483
85621
|
else if (e.type === "render-done") spinner2.text = `Rendered: ${e.outputPath}`;
|
|
85484
85622
|
}
|
|
85485
85623
|
});
|
|
@@ -85655,8 +85793,8 @@ function buildPlanValidationFailureData(plan) {
|
|
|
85655
85793
|
};
|
|
85656
85794
|
}
|
|
85657
85795
|
function estimateBuildCost(params) {
|
|
85658
|
-
const storyboardPath2 =
|
|
85659
|
-
if (!
|
|
85796
|
+
const storyboardPath2 = join27(params.projectDir, "STORYBOARD.md");
|
|
85797
|
+
if (!existsSync51(storyboardPath2)) return null;
|
|
85660
85798
|
let beats = 0;
|
|
85661
85799
|
try {
|
|
85662
85800
|
const md = readFileSync4(storyboardPath2, "utf-8");
|
|
@@ -86101,7 +86239,7 @@ init_composer_resolve();
|
|
|
86101
86239
|
init_cost_tier();
|
|
86102
86240
|
init_scene_build_mode();
|
|
86103
86241
|
init_output();
|
|
86104
|
-
import { existsSync as
|
|
86242
|
+
import { existsSync as existsSync52 } from "node:fs";
|
|
86105
86243
|
import { resolve as resolve69 } from "node:path";
|
|
86106
86244
|
var COMMAND_KEY_MAP = getCommandKeyMap();
|
|
86107
86245
|
var FREE_COMMANDS = [
|
|
@@ -86229,13 +86367,13 @@ async function runDiagnostics() {
|
|
|
86229
86367
|
installed = false;
|
|
86230
86368
|
}
|
|
86231
86369
|
} else if (toolName === "chrome") {
|
|
86232
|
-
const { existsSync:
|
|
86370
|
+
const { existsSync: existsSync59 } = await import("node:fs");
|
|
86233
86371
|
const { homedir: homedir8 } = await import("node:os");
|
|
86234
|
-
const { join:
|
|
86372
|
+
const { join: join31 } = await import("node:path");
|
|
86235
86373
|
const chromePaths = [
|
|
86236
86374
|
process.env.HYPERFRAMES_CHROME_PATH,
|
|
86237
86375
|
process.env.CHROME_PATH,
|
|
86238
|
-
|
|
86376
|
+
join31(
|
|
86239
86377
|
homedir8(),
|
|
86240
86378
|
".cache",
|
|
86241
86379
|
"puppeteer",
|
|
@@ -86250,7 +86388,7 @@ async function runDiagnostics() {
|
|
|
86250
86388
|
"/usr/bin/chromium",
|
|
86251
86389
|
"/usr/bin/chromium-browser"
|
|
86252
86390
|
];
|
|
86253
|
-
installed = chromePaths.some((p) => p &&
|
|
86391
|
+
installed = chromePaths.some((p) => p && existsSync59(p));
|
|
86254
86392
|
} else {
|
|
86255
86393
|
installed = commandExists(toolName);
|
|
86256
86394
|
}
|
|
@@ -86290,7 +86428,7 @@ async function runDiagnostics() {
|
|
|
86290
86428
|
}
|
|
86291
86429
|
const projectFiles = ["AGENTS.md", "CLAUDE.md", "vibe.project.yaml"].map((rel) => ({
|
|
86292
86430
|
path: rel,
|
|
86293
|
-
exists:
|
|
86431
|
+
exists: existsSync52(resolve69(cwd, rel))
|
|
86294
86432
|
}));
|
|
86295
86433
|
const projectInitialized = projectFiles.some((f) => f.exists);
|
|
86296
86434
|
const nearestProjectConfigPath = await findProjectConfigPath(cwd);
|
|
@@ -86309,8 +86447,8 @@ async function runDiagnostics() {
|
|
|
86309
86447
|
} catch (err) {
|
|
86310
86448
|
if (!(err instanceof ComposerResolveError)) throw err;
|
|
86311
86449
|
}
|
|
86312
|
-
const sceneProjectInCwd =
|
|
86313
|
-
const skillInstalled =
|
|
86450
|
+
const sceneProjectInCwd = existsSync52(resolve69(cwd, "STORYBOARD.md"));
|
|
86451
|
+
const skillInstalled = existsSync52(resolve69(cwd, "SKILL.md"));
|
|
86314
86452
|
return {
|
|
86315
86453
|
system: {
|
|
86316
86454
|
node: { version: nodeVersion, ok: true },
|
|
@@ -86858,7 +86996,7 @@ init_detect();
|
|
|
86858
86996
|
init_ai_edit();
|
|
86859
86997
|
init_output();
|
|
86860
86998
|
import { resolve as resolve71 } from "node:path";
|
|
86861
|
-
import { existsSync as
|
|
86999
|
+
import { existsSync as existsSync53 } from "node:fs";
|
|
86862
87000
|
import { mkdir as mkdir27, rm as rm5 } from "node:fs/promises";
|
|
86863
87001
|
var DEMO_DIR = resolve71(process.cwd(), ".vibeframe-demo");
|
|
86864
87002
|
async function generateTestVideo(outputPath) {
|
|
@@ -86889,7 +87027,7 @@ async function generateTestVideo(outputPath) {
|
|
|
86889
87027
|
"-y",
|
|
86890
87028
|
outputPath
|
|
86891
87029
|
], { timeout: 3e4, maxBuffer: 50 * 1024 * 1024 });
|
|
86892
|
-
return
|
|
87030
|
+
return existsSync53(outputPath);
|
|
86893
87031
|
} catch {
|
|
86894
87032
|
try {
|
|
86895
87033
|
await execSafe("ffmpeg", [
|
|
@@ -86915,7 +87053,7 @@ async function generateTestVideo(outputPath) {
|
|
|
86915
87053
|
"-y",
|
|
86916
87054
|
outputPath
|
|
86917
87055
|
], { timeout: 3e4, maxBuffer: 50 * 1024 * 1024 });
|
|
86918
|
-
return
|
|
87056
|
+
return existsSync53(outputPath);
|
|
86919
87057
|
} catch {
|
|
86920
87058
|
return false;
|
|
86921
87059
|
}
|
|
@@ -87057,8 +87195,8 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
|
|
|
87057
87195
|
|
|
87058
87196
|
// src/commands/context.ts
|
|
87059
87197
|
init_esm();
|
|
87060
|
-
import { existsSync as
|
|
87061
|
-
import { readFile as
|
|
87198
|
+
import { existsSync as existsSync54 } from "node:fs";
|
|
87199
|
+
import { readFile as readFile48 } from "node:fs/promises";
|
|
87062
87200
|
import { resolve as resolve72, dirname as dirname25 } from "node:path";
|
|
87063
87201
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
87064
87202
|
var __filename = fileURLToPath2(import.meta.url);
|
|
@@ -87070,7 +87208,7 @@ function resolveContextPath(baseDir = __dirname) {
|
|
|
87070
87208
|
// Source/dev layout: packages/cli/src/commands/context.ts -> packages/cli/CONTEXT.md
|
|
87071
87209
|
resolve72(baseDir, "../../CONTEXT.md")
|
|
87072
87210
|
];
|
|
87073
|
-
return candidates.find((candidate) =>
|
|
87211
|
+
return candidates.find((candidate) => existsSync54(candidate)) ?? null;
|
|
87074
87212
|
}
|
|
87075
87213
|
var contextCommand = new Command("context").description("Print CLI context/guidelines for AI agent integration").option("--format <format>", "Output format: markdown | json", "markdown").action(async (_options, cmd) => {
|
|
87076
87214
|
const options = {
|
|
@@ -87176,7 +87314,7 @@ var contextCommand = new Command("context").description("Print CLI context/guide
|
|
|
87176
87314
|
try {
|
|
87177
87315
|
const contextPath = resolveContextPath();
|
|
87178
87316
|
if (!contextPath) throw new Error("CONTEXT.md not found");
|
|
87179
|
-
const content = await
|
|
87317
|
+
const content = await readFile48(contextPath, "utf-8");
|
|
87180
87318
|
console.log(
|
|
87181
87319
|
content.replace(
|
|
87182
87320
|
"## Mental model",
|