@vibeframe/cli 0.113.3 → 0.113.4
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.
|
@@ -15,6 +15,7 @@ export interface BuildPlanBeat {
|
|
|
15
15
|
assets: {
|
|
16
16
|
narration: AssetPlan | null;
|
|
17
17
|
backdrop: AssetPlan | null;
|
|
18
|
+
keyframe: AssetPlan | null;
|
|
18
19
|
video: AssetPlan | null;
|
|
19
20
|
music: AssetPlan | null;
|
|
20
21
|
};
|
|
@@ -93,6 +94,7 @@ export interface CreateBuildPlanOptions {
|
|
|
93
94
|
skipNarration?: boolean;
|
|
94
95
|
skipBackdrop?: boolean;
|
|
95
96
|
skipVideo?: boolean;
|
|
97
|
+
skipKeyframe?: boolean;
|
|
96
98
|
skipMusic?: boolean;
|
|
97
99
|
ttsProvider?: string;
|
|
98
100
|
voice?: string;
|
|
@@ -81,6 +81,23 @@ export type SceneBuildProgressEvent = {
|
|
|
81
81
|
type: "backdrop-skipped";
|
|
82
82
|
beatId: string;
|
|
83
83
|
reason: string;
|
|
84
|
+
} | {
|
|
85
|
+
type: "keyframe-cached";
|
|
86
|
+
beatId: string;
|
|
87
|
+
path: string;
|
|
88
|
+
} | {
|
|
89
|
+
type: "keyframe-generated";
|
|
90
|
+
beatId: string;
|
|
91
|
+
path: string;
|
|
92
|
+
provider: string;
|
|
93
|
+
} | {
|
|
94
|
+
type: "keyframe-failed";
|
|
95
|
+
beatId: string;
|
|
96
|
+
error: string;
|
|
97
|
+
} | {
|
|
98
|
+
type: "keyframe-skipped";
|
|
99
|
+
beatId: string;
|
|
100
|
+
reason: string;
|
|
84
101
|
} | {
|
|
85
102
|
type: "video-cached";
|
|
86
103
|
beatId: string;
|
|
@@ -233,6 +250,8 @@ export interface SceneBuildOptions {
|
|
|
233
250
|
musicProvider?: BuildMusicProvider;
|
|
234
251
|
/** Skip AI video generation even when beats declare video cues. */
|
|
235
252
|
skipVideo?: boolean;
|
|
253
|
+
/** Skip keyframe-still generation even when beats declare keyframe cues. */
|
|
254
|
+
skipKeyframe?: boolean;
|
|
236
255
|
/** Skip music generation even when beats declare music cues. */
|
|
237
256
|
skipMusic?: boolean;
|
|
238
257
|
/** OpenAI image quality — see `vibe generate image --quality`. */
|
package/dist/index.js
CHANGED
|
@@ -60690,6 +60690,14 @@ async function createBuildPlan(opts) {
|
|
|
60690
60690
|
duration: beat.duration,
|
|
60691
60691
|
keyframe: keyframePrompt
|
|
60692
60692
|
}) : null;
|
|
60693
|
+
const keyframeCache = keyframePrompt ? keyframeCacheDescriptor({
|
|
60694
|
+
beatId: beat.id,
|
|
60695
|
+
cue: keyframePrompt,
|
|
60696
|
+
provider: resolved.image.resolved,
|
|
60697
|
+
quality: imageQuality,
|
|
60698
|
+
size: imageSize2,
|
|
60699
|
+
ratio: imageRatio
|
|
60700
|
+
}) : null;
|
|
60693
60701
|
const musicCache = musicPrompt && !musicReference ? musicCacheDescriptor({
|
|
60694
60702
|
beatId: beat.id,
|
|
60695
60703
|
cue: musicPrompt,
|
|
@@ -60725,6 +60733,19 @@ async function createBuildPlan(opts) {
|
|
|
60725
60733
|
cost: backdropCostUsd(imageQuality),
|
|
60726
60734
|
active: includeAssets
|
|
60727
60735
|
}) : null;
|
|
60736
|
+
const keyframe = keyframePrompt && !opts.skipKeyframe ? assetPlan({
|
|
60737
|
+
kind: "keyframe",
|
|
60738
|
+
beatId: beat.id,
|
|
60739
|
+
cue: keyframePrompt,
|
|
60740
|
+
provider: resolved.image.resolved,
|
|
60741
|
+
path: `assets/keyframe-${beat.id}.png`,
|
|
60742
|
+
cache: keyframeCache,
|
|
60743
|
+
reference: null,
|
|
60744
|
+
projectDir,
|
|
60745
|
+
force: opts.force,
|
|
60746
|
+
cost: backdropCostUsd(imageQuality),
|
|
60747
|
+
active: includeAssets
|
|
60748
|
+
}) : null;
|
|
60728
60749
|
const video = videoCue && !opts.skipVideo ? assetPlan({
|
|
60729
60750
|
kind: "video",
|
|
60730
60751
|
beatId: beat.id,
|
|
@@ -60757,7 +60778,7 @@ async function createBuildPlan(opts) {
|
|
|
60757
60778
|
}) : null;
|
|
60758
60779
|
const compositionPath = `compositions/scene-${beat.id}.html`;
|
|
60759
60780
|
const compositionExists = existsSync47(join23(projectDir, compositionPath));
|
|
60760
|
-
for (const asset of [narration, backdrop, video, music]) {
|
|
60781
|
+
for (const asset of [narration, backdrop, keyframe, video, music]) {
|
|
60761
60782
|
if (!asset) continue;
|
|
60762
60783
|
if (asset.referenceError) {
|
|
60763
60784
|
missing.add("assets");
|
|
@@ -60782,13 +60803,9 @@ async function createBuildPlan(opts) {
|
|
|
60782
60803
|
if (narration?.willGenerate) noteProviderNeed(resolved.narration, "Narration");
|
|
60783
60804
|
if (backdrop?.willGenerate && isSupportedBuildImageProvider(resolved.image.resolved))
|
|
60784
60805
|
noteProviderNeed(resolved.image, "Backdrop generation");
|
|
60806
|
+
if (keyframe?.willGenerate && isSupportedBuildImageProvider(resolved.image.resolved))
|
|
60807
|
+
noteProviderNeed(resolved.image, "Keyframe generation");
|
|
60785
60808
|
if (video?.willGenerate) noteProviderNeed(resolved.video, "Video generation");
|
|
60786
|
-
if (keyframePrompt && video?.willGenerate) {
|
|
60787
|
-
estimatedCostUsd += backdropCostUsd(imageQuality);
|
|
60788
|
-
providers.add(resolved.image.resolved);
|
|
60789
|
-
if (isSupportedBuildImageProvider(resolved.image.resolved))
|
|
60790
|
-
noteProviderNeed(resolved.image, "Keyframe generation");
|
|
60791
|
-
}
|
|
60792
60809
|
if (music?.willGenerate) noteProviderNeed(resolved.music, "Music generation");
|
|
60793
60810
|
if (!compositionExists) missing.add("compositions");
|
|
60794
60811
|
if (includeCompose && !compositionExists && mode !== "agent") {
|
|
@@ -60801,7 +60818,7 @@ async function createBuildPlan(opts) {
|
|
|
60801
60818
|
heading: beat.heading,
|
|
60802
60819
|
durationSec: beat.duration ?? null,
|
|
60803
60820
|
cues: cue,
|
|
60804
|
-
assets: { narration, backdrop, video, music },
|
|
60821
|
+
assets: { narration, backdrop, keyframe, video, music },
|
|
60805
60822
|
composition: {
|
|
60806
60823
|
path: compositionPath,
|
|
60807
60824
|
exists: compositionExists
|
|
@@ -61017,6 +61034,7 @@ function providerResolutionsForPlan(resolved, beats, opts, includeAssets) {
|
|
|
61017
61034
|
return [
|
|
61018
61035
|
includeAssets && !opts.skipNarration && needsProvider("narration") ? resolved.narration : null,
|
|
61019
61036
|
includeAssets && !opts.skipBackdrop && needsProvider("backdrop") ? resolved.image : null,
|
|
61037
|
+
includeAssets && !opts.skipKeyframe && needsProvider("keyframe") ? resolved.image : null,
|
|
61020
61038
|
includeAssets && !opts.skipVideo && needsProvider("video") ? resolved.video : null,
|
|
61021
61039
|
includeAssets && !opts.skipMusic && needsProvider("music") ? resolved.music : null,
|
|
61022
61040
|
resolved.composer ?? null
|
|
@@ -62086,6 +62104,7 @@ async function executeSceneBuild(opts) {
|
|
|
62086
62104
|
skipNarration: opts.skipNarration,
|
|
62087
62105
|
skipBackdrop: opts.skipBackdrop,
|
|
62088
62106
|
skipVideo: opts.skipVideo,
|
|
62107
|
+
skipKeyframe: opts.skipKeyframe,
|
|
62089
62108
|
skipMusic: opts.skipMusic,
|
|
62090
62109
|
ttsProvider: opts.ttsProvider,
|
|
62091
62110
|
voice: opts.voice,
|
|
@@ -62271,6 +62290,7 @@ async function executeSceneBuild(opts) {
|
|
|
62271
62290
|
skipNarration: opts.skipNarration ?? false,
|
|
62272
62291
|
skipBackdrop: opts.skipBackdrop ?? false,
|
|
62273
62292
|
skipVideo: opts.skipVideo ?? false,
|
|
62293
|
+
skipKeyframe: opts.skipKeyframe ?? false,
|
|
62274
62294
|
skipMusic: opts.skipMusic ?? false,
|
|
62275
62295
|
force: opts.force ?? false,
|
|
62276
62296
|
onProgress,
|
|
@@ -62647,10 +62667,11 @@ async function executeSceneBuild(opts) {
|
|
|
62647
62667
|
});
|
|
62648
62668
|
}
|
|
62649
62669
|
async function buildBeatPrimitives(beat, ctx) {
|
|
62670
|
+
const keyframe = ctx.skipKeyframe ? await skipped("keyframe", beat.id, "--skip-keyframe", ctx) : await dispatchKeyframe(beat, ctx);
|
|
62650
62671
|
const [narration, backdrop, video, music] = await Promise.all([
|
|
62651
62672
|
ctx.skipNarration ? skipped("narration", beat.id, "--skip-narration", ctx) : dispatchNarration(beat, ctx),
|
|
62652
62673
|
ctx.skipBackdrop ? skipped("backdrop", beat.id, "--skip-backdrop", ctx) : dispatchBackdrop(beat, ctx),
|
|
62653
|
-
ctx.skipVideo ? skipped("video", beat.id, "--skip-video", ctx) : dispatchVideo(beat, ctx),
|
|
62674
|
+
ctx.skipVideo ? skipped("video", beat.id, "--skip-video", ctx) : dispatchVideo(beat, ctx, keyframe),
|
|
62654
62675
|
ctx.skipMusic ? skipped("music", beat.id, "--skip-music", ctx) : dispatchMusic(beat, ctx)
|
|
62655
62676
|
]);
|
|
62656
62677
|
return {
|
|
@@ -62694,8 +62715,8 @@ async function buildBeatPrimitives(beat, ctx) {
|
|
|
62694
62715
|
videoMetadataPath: video.metadataPath,
|
|
62695
62716
|
videoFreshness: video.freshness,
|
|
62696
62717
|
videoSourcePath: video.sourcePath,
|
|
62697
|
-
keyframeStatus:
|
|
62698
|
-
keyframePath:
|
|
62718
|
+
keyframeStatus: keyframe.status,
|
|
62719
|
+
keyframePath: keyframe.path,
|
|
62699
62720
|
musicStatus: music.status,
|
|
62700
62721
|
musicPath: music.path,
|
|
62701
62722
|
musicJobId: music.job?.id,
|
|
@@ -63266,7 +63287,21 @@ function imageProviderKeyInfo(provider) {
|
|
|
63266
63287
|
if (provider === "grok") return { configKey: "xai", envVar: "XAI_API_KEY" };
|
|
63267
63288
|
return { configKey: "openai", envVar: "OPENAI_API_KEY" };
|
|
63268
63289
|
}
|
|
63269
|
-
async function
|
|
63290
|
+
async function dispatchKeyframe(beat, ctx) {
|
|
63291
|
+
const keyframeCue = stringOrUndefined4(beat.cues?.keyframe);
|
|
63292
|
+
if (!keyframeCue) return { status: "no-cue" };
|
|
63293
|
+
const characterRefsRel = beatCharacterNames(beat.cues).map((name) => ctx.characterPaths?.get(name)).filter((p) => typeof p === "string");
|
|
63294
|
+
const kf = await ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel);
|
|
63295
|
+
if (kf.status === "failed") {
|
|
63296
|
+
ctx.onProgress({ type: "keyframe-failed", beatId: beat.id, error: kf.error });
|
|
63297
|
+
return { status: "failed", error: kf.error };
|
|
63298
|
+
}
|
|
63299
|
+
ctx.onProgress(
|
|
63300
|
+
kf.status === "cached" ? { type: "keyframe-cached", beatId: beat.id, path: kf.rel } : { type: "keyframe-generated", beatId: beat.id, path: kf.rel, provider: ctx.imageProvider }
|
|
63301
|
+
);
|
|
63302
|
+
return { status: kf.status, path: kf.rel, provider: ctx.imageProvider };
|
|
63303
|
+
}
|
|
63304
|
+
async function dispatchVideo(beat, ctx, keyframe) {
|
|
63270
63305
|
const reference = assetReferenceForBeat(ctx.projectDir, "video", beat);
|
|
63271
63306
|
if (reference) return referencePrimitiveOutcome("video", beat, ctx, reference);
|
|
63272
63307
|
const keyframeCue = stringOrUndefined4(beat.cues?.keyframe);
|
|
@@ -63336,20 +63371,14 @@ async function dispatchVideo(beat, ctx) {
|
|
|
63336
63371
|
freshness: "fresh"
|
|
63337
63372
|
};
|
|
63338
63373
|
}
|
|
63339
|
-
|
|
63340
|
-
|
|
63341
|
-
|
|
63342
|
-
|
|
63343
|
-
|
|
63344
|
-
const kf = await ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel);
|
|
63345
|
-
if (kf.status === "failed") {
|
|
63346
|
-
ctx.onProgress({ type: "video-failed", beatId: beat.id, error: kf.error });
|
|
63347
|
-
return { status: "failed", error: kf.error };
|
|
63348
|
-
}
|
|
63349
|
-
keyframeStatus = kf.status;
|
|
63350
|
-
keyframeRel = kf.rel;
|
|
63351
|
-
keyframeImageAbs = join25(ctx.projectDir, kf.rel);
|
|
63374
|
+
const keyframeImageRel = keyframeCue && (keyframe.status === "generated" || keyframe.status === "cached") ? keyframe.path : void 0;
|
|
63375
|
+
if (keyframeCue && !keyframeImageRel) {
|
|
63376
|
+
const reason = keyframe.status === "failed" ? `keyframe unavailable: ${keyframe.error ?? "generation failed"}` : "keyframe not generated (build without --skip-keyframe first)";
|
|
63377
|
+
ctx.onProgress({ type: "video-skipped", beatId: beat.id, reason });
|
|
63378
|
+
return { status: "skipped" };
|
|
63352
63379
|
}
|
|
63380
|
+
const keyframeImageAbs = keyframeImageRel ? join25(ctx.projectDir, keyframeImageRel) : void 0;
|
|
63381
|
+
loadSceneBuildEnv(ctx.projectDir);
|
|
63353
63382
|
const result = await executeVideoGenerate({
|
|
63354
63383
|
prompt: prompt3,
|
|
63355
63384
|
provider: ctx.videoProvider,
|
|
@@ -63395,9 +63424,7 @@ async function dispatchVideo(beat, ctx) {
|
|
|
63395
63424
|
cachePath: cache.path,
|
|
63396
63425
|
cacheKey: cache.key,
|
|
63397
63426
|
metadataPath,
|
|
63398
|
-
freshness: "fresh"
|
|
63399
|
-
keyframeStatus,
|
|
63400
|
-
keyframePath: keyframeRel
|
|
63427
|
+
freshness: "fresh"
|
|
63401
63428
|
};
|
|
63402
63429
|
}
|
|
63403
63430
|
const job = await createAndWriteJobRecord({
|
|
@@ -63429,9 +63456,7 @@ async function dispatchVideo(beat, ctx) {
|
|
|
63429
63456
|
provider: job.provider,
|
|
63430
63457
|
cachePath: cache.path,
|
|
63431
63458
|
cacheKey: cache.key,
|
|
63432
|
-
metadataPath
|
|
63433
|
-
keyframeStatus,
|
|
63434
|
-
keyframePath: keyframeRel
|
|
63459
|
+
metadataPath
|
|
63435
63460
|
};
|
|
63436
63461
|
}
|
|
63437
63462
|
async function dispatchMusic(beat, ctx) {
|
|
@@ -74819,7 +74844,7 @@ init_source();
|
|
|
74819
74844
|
// package.json
|
|
74820
74845
|
var package_default = {
|
|
74821
74846
|
name: "@vibeframe/cli",
|
|
74822
|
-
version: "0.113.
|
|
74847
|
+
version: "0.113.4",
|
|
74823
74848
|
description: "VibeFrame CLI - agentic video workflow runtime",
|
|
74824
74849
|
private: false,
|
|
74825
74850
|
license: "MIT",
|
|
@@ -85105,7 +85130,10 @@ var VALID_STAGES2 = ["assets", "compose", "sync", "render", "all"];
|
|
|
85105
85130
|
var VALID_IMAGE_PROVIDERS2 = ["openai", "gemini", "grok"];
|
|
85106
85131
|
var VALID_VIDEO_PROVIDERS2 = ["seedance", "grok", "kling", "runway", "veo"];
|
|
85107
85132
|
var VALID_MUSIC_PROVIDERS2 = ["elevenlabs", "replicate"];
|
|
85108
|
-
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(
|
|
85133
|
+
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(
|
|
85134
|
+
"--skip-keyframe",
|
|
85135
|
+
"Don't generate keyframe stills (review keyframes first with --skip-video, then build)"
|
|
85136
|
+
).option("--skip-music", "Don't dispatch music generation even when beats declare music cues").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(
|
|
85109
85137
|
"after",
|
|
85110
85138
|
`
|
|
85111
85139
|
Examples:
|
|
@@ -85164,6 +85192,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
85164
85192
|
skipNarration: options.skipNarration ?? false,
|
|
85165
85193
|
skipBackdrop: options.skipBackdrop ?? false,
|
|
85166
85194
|
skipVideo: options.skipVideo ?? false,
|
|
85195
|
+
skipKeyframe: options.skipKeyframe ?? false,
|
|
85167
85196
|
skipMusic: options.skipMusic ?? false,
|
|
85168
85197
|
skipRender: options.skipRender ?? false,
|
|
85169
85198
|
ttsProvider: options.tts,
|
|
@@ -85184,6 +85213,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
85184
85213
|
skipNarration: options.skipNarration,
|
|
85185
85214
|
skipBackdrop: options.skipBackdrop,
|
|
85186
85215
|
skipVideo: options.skipVideo,
|
|
85216
|
+
skipKeyframe: options.skipKeyframe,
|
|
85187
85217
|
skipMusic: options.skipMusic,
|
|
85188
85218
|
ttsProvider: options.tts,
|
|
85189
85219
|
voice: options.voice,
|
|
@@ -85274,6 +85304,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
85274
85304
|
skipNarration: options.skipNarration,
|
|
85275
85305
|
skipBackdrop: options.skipBackdrop,
|
|
85276
85306
|
skipVideo: options.skipVideo,
|
|
85307
|
+
skipKeyframe: options.skipKeyframe,
|
|
85277
85308
|
skipMusic: options.skipMusic,
|
|
85278
85309
|
skipRender: options.skipRender,
|
|
85279
85310
|
ttsProvider: options.tts,
|
|
@@ -33679,6 +33679,14 @@ async function createBuildPlan(opts) {
|
|
|
33679
33679
|
duration: beat.duration,
|
|
33680
33680
|
keyframe: keyframePrompt
|
|
33681
33681
|
}) : null;
|
|
33682
|
+
const keyframeCache = keyframePrompt ? keyframeCacheDescriptor({
|
|
33683
|
+
beatId: beat.id,
|
|
33684
|
+
cue: keyframePrompt,
|
|
33685
|
+
provider: resolved.image.resolved,
|
|
33686
|
+
quality: imageQuality,
|
|
33687
|
+
size: imageSize2,
|
|
33688
|
+
ratio: imageRatio
|
|
33689
|
+
}) : null;
|
|
33682
33690
|
const musicCache = musicPrompt && !musicReference ? musicCacheDescriptor({
|
|
33683
33691
|
beatId: beat.id,
|
|
33684
33692
|
cue: musicPrompt,
|
|
@@ -33714,6 +33722,19 @@ async function createBuildPlan(opts) {
|
|
|
33714
33722
|
cost: backdropCostUsd(imageQuality),
|
|
33715
33723
|
active: includeAssets
|
|
33716
33724
|
}) : null;
|
|
33725
|
+
const keyframe = keyframePrompt && !opts.skipKeyframe ? assetPlan({
|
|
33726
|
+
kind: "keyframe",
|
|
33727
|
+
beatId: beat.id,
|
|
33728
|
+
cue: keyframePrompt,
|
|
33729
|
+
provider: resolved.image.resolved,
|
|
33730
|
+
path: `assets/keyframe-${beat.id}.png`,
|
|
33731
|
+
cache: keyframeCache,
|
|
33732
|
+
reference: null,
|
|
33733
|
+
projectDir,
|
|
33734
|
+
force: opts.force,
|
|
33735
|
+
cost: backdropCostUsd(imageQuality),
|
|
33736
|
+
active: includeAssets
|
|
33737
|
+
}) : null;
|
|
33717
33738
|
const video = videoCue && !opts.skipVideo ? assetPlan({
|
|
33718
33739
|
kind: "video",
|
|
33719
33740
|
beatId: beat.id,
|
|
@@ -33746,7 +33767,7 @@ async function createBuildPlan(opts) {
|
|
|
33746
33767
|
}) : null;
|
|
33747
33768
|
const compositionPath = `compositions/scene-${beat.id}.html`;
|
|
33748
33769
|
const compositionExists = existsSync17(join15(projectDir, compositionPath));
|
|
33749
|
-
for (const asset of [narration, backdrop, video, music]) {
|
|
33770
|
+
for (const asset of [narration, backdrop, keyframe, video, music]) {
|
|
33750
33771
|
if (!asset) continue;
|
|
33751
33772
|
if (asset.referenceError) {
|
|
33752
33773
|
missing.add("assets");
|
|
@@ -33771,13 +33792,9 @@ async function createBuildPlan(opts) {
|
|
|
33771
33792
|
if (narration?.willGenerate) noteProviderNeed(resolved.narration, "Narration");
|
|
33772
33793
|
if (backdrop?.willGenerate && isSupportedBuildImageProvider(resolved.image.resolved))
|
|
33773
33794
|
noteProviderNeed(resolved.image, "Backdrop generation");
|
|
33795
|
+
if (keyframe?.willGenerate && isSupportedBuildImageProvider(resolved.image.resolved))
|
|
33796
|
+
noteProviderNeed(resolved.image, "Keyframe generation");
|
|
33774
33797
|
if (video?.willGenerate) noteProviderNeed(resolved.video, "Video generation");
|
|
33775
|
-
if (keyframePrompt && video?.willGenerate) {
|
|
33776
|
-
estimatedCostUsd += backdropCostUsd(imageQuality);
|
|
33777
|
-
providers.add(resolved.image.resolved);
|
|
33778
|
-
if (isSupportedBuildImageProvider(resolved.image.resolved))
|
|
33779
|
-
noteProviderNeed(resolved.image, "Keyframe generation");
|
|
33780
|
-
}
|
|
33781
33798
|
if (music?.willGenerate) noteProviderNeed(resolved.music, "Music generation");
|
|
33782
33799
|
if (!compositionExists) missing.add("compositions");
|
|
33783
33800
|
if (includeCompose && !compositionExists && mode !== "agent") {
|
|
@@ -33790,7 +33807,7 @@ async function createBuildPlan(opts) {
|
|
|
33790
33807
|
heading: beat.heading,
|
|
33791
33808
|
durationSec: beat.duration ?? null,
|
|
33792
33809
|
cues: cue,
|
|
33793
|
-
assets: { narration, backdrop, video, music },
|
|
33810
|
+
assets: { narration, backdrop, keyframe, video, music },
|
|
33794
33811
|
composition: {
|
|
33795
33812
|
path: compositionPath,
|
|
33796
33813
|
exists: compositionExists
|
|
@@ -34006,6 +34023,7 @@ function providerResolutionsForPlan(resolved, beats, opts, includeAssets) {
|
|
|
34006
34023
|
return [
|
|
34007
34024
|
includeAssets && !opts.skipNarration && needsProvider("narration") ? resolved.narration : null,
|
|
34008
34025
|
includeAssets && !opts.skipBackdrop && needsProvider("backdrop") ? resolved.image : null,
|
|
34026
|
+
includeAssets && !opts.skipKeyframe && needsProvider("keyframe") ? resolved.image : null,
|
|
34009
34027
|
includeAssets && !opts.skipVideo && needsProvider("video") ? resolved.video : null,
|
|
34010
34028
|
includeAssets && !opts.skipMusic && needsProvider("music") ? resolved.music : null,
|
|
34011
34029
|
resolved.composer ?? null
|
|
@@ -34580,6 +34598,7 @@ async function executeSceneBuild(opts) {
|
|
|
34580
34598
|
skipNarration: opts.skipNarration,
|
|
34581
34599
|
skipBackdrop: opts.skipBackdrop,
|
|
34582
34600
|
skipVideo: opts.skipVideo,
|
|
34601
|
+
skipKeyframe: opts.skipKeyframe,
|
|
34583
34602
|
skipMusic: opts.skipMusic,
|
|
34584
34603
|
ttsProvider: opts.ttsProvider,
|
|
34585
34604
|
voice: opts.voice,
|
|
@@ -34765,6 +34784,7 @@ async function executeSceneBuild(opts) {
|
|
|
34765
34784
|
skipNarration: opts.skipNarration ?? false,
|
|
34766
34785
|
skipBackdrop: opts.skipBackdrop ?? false,
|
|
34767
34786
|
skipVideo: opts.skipVideo ?? false,
|
|
34787
|
+
skipKeyframe: opts.skipKeyframe ?? false,
|
|
34768
34788
|
skipMusic: opts.skipMusic ?? false,
|
|
34769
34789
|
force: opts.force ?? false,
|
|
34770
34790
|
onProgress,
|
|
@@ -35141,10 +35161,11 @@ async function executeSceneBuild(opts) {
|
|
|
35141
35161
|
});
|
|
35142
35162
|
}
|
|
35143
35163
|
async function buildBeatPrimitives(beat, ctx) {
|
|
35164
|
+
const keyframe = ctx.skipKeyframe ? await skipped("keyframe", beat.id, "--skip-keyframe", ctx) : await dispatchKeyframe(beat, ctx);
|
|
35144
35165
|
const [narration, backdrop, video, music] = await Promise.all([
|
|
35145
35166
|
ctx.skipNarration ? skipped("narration", beat.id, "--skip-narration", ctx) : dispatchNarration(beat, ctx),
|
|
35146
35167
|
ctx.skipBackdrop ? skipped("backdrop", beat.id, "--skip-backdrop", ctx) : dispatchBackdrop(beat, ctx),
|
|
35147
|
-
ctx.skipVideo ? skipped("video", beat.id, "--skip-video", ctx) : dispatchVideo(beat, ctx),
|
|
35168
|
+
ctx.skipVideo ? skipped("video", beat.id, "--skip-video", ctx) : dispatchVideo(beat, ctx, keyframe),
|
|
35148
35169
|
ctx.skipMusic ? skipped("music", beat.id, "--skip-music", ctx) : dispatchMusic(beat, ctx)
|
|
35149
35170
|
]);
|
|
35150
35171
|
return {
|
|
@@ -35188,8 +35209,8 @@ async function buildBeatPrimitives(beat, ctx) {
|
|
|
35188
35209
|
videoMetadataPath: video.metadataPath,
|
|
35189
35210
|
videoFreshness: video.freshness,
|
|
35190
35211
|
videoSourcePath: video.sourcePath,
|
|
35191
|
-
keyframeStatus:
|
|
35192
|
-
keyframePath:
|
|
35212
|
+
keyframeStatus: keyframe.status,
|
|
35213
|
+
keyframePath: keyframe.path,
|
|
35193
35214
|
musicStatus: music.status,
|
|
35194
35215
|
musicPath: music.path,
|
|
35195
35216
|
musicJobId: music.job?.id,
|
|
@@ -35760,7 +35781,21 @@ function imageProviderKeyInfo(provider) {
|
|
|
35760
35781
|
if (provider === "grok") return { configKey: "xai", envVar: "XAI_API_KEY" };
|
|
35761
35782
|
return { configKey: "openai", envVar: "OPENAI_API_KEY" };
|
|
35762
35783
|
}
|
|
35763
|
-
async function
|
|
35784
|
+
async function dispatchKeyframe(beat, ctx) {
|
|
35785
|
+
const keyframeCue = stringOrUndefined4(beat.cues?.keyframe);
|
|
35786
|
+
if (!keyframeCue) return { status: "no-cue" };
|
|
35787
|
+
const characterRefsRel = beatCharacterNames(beat.cues).map((name) => ctx.characterPaths?.get(name)).filter((p) => typeof p === "string");
|
|
35788
|
+
const kf = await ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel);
|
|
35789
|
+
if (kf.status === "failed") {
|
|
35790
|
+
ctx.onProgress({ type: "keyframe-failed", beatId: beat.id, error: kf.error });
|
|
35791
|
+
return { status: "failed", error: kf.error };
|
|
35792
|
+
}
|
|
35793
|
+
ctx.onProgress(
|
|
35794
|
+
kf.status === "cached" ? { type: "keyframe-cached", beatId: beat.id, path: kf.rel } : { type: "keyframe-generated", beatId: beat.id, path: kf.rel, provider: ctx.imageProvider }
|
|
35795
|
+
);
|
|
35796
|
+
return { status: kf.status, path: kf.rel, provider: ctx.imageProvider };
|
|
35797
|
+
}
|
|
35798
|
+
async function dispatchVideo(beat, ctx, keyframe) {
|
|
35764
35799
|
const reference = assetReferenceForBeat(ctx.projectDir, "video", beat);
|
|
35765
35800
|
if (reference) return referencePrimitiveOutcome("video", beat, ctx, reference);
|
|
35766
35801
|
const keyframeCue = stringOrUndefined4(beat.cues?.keyframe);
|
|
@@ -35830,20 +35865,14 @@ async function dispatchVideo(beat, ctx) {
|
|
|
35830
35865
|
freshness: "fresh"
|
|
35831
35866
|
};
|
|
35832
35867
|
}
|
|
35833
|
-
|
|
35834
|
-
|
|
35835
|
-
|
|
35836
|
-
|
|
35837
|
-
|
|
35838
|
-
const kf = await ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel);
|
|
35839
|
-
if (kf.status === "failed") {
|
|
35840
|
-
ctx.onProgress({ type: "video-failed", beatId: beat.id, error: kf.error });
|
|
35841
|
-
return { status: "failed", error: kf.error };
|
|
35842
|
-
}
|
|
35843
|
-
keyframeStatus = kf.status;
|
|
35844
|
-
keyframeRel = kf.rel;
|
|
35845
|
-
keyframeImageAbs = join16(ctx.projectDir, kf.rel);
|
|
35868
|
+
const keyframeImageRel = keyframeCue && (keyframe.status === "generated" || keyframe.status === "cached") ? keyframe.path : void 0;
|
|
35869
|
+
if (keyframeCue && !keyframeImageRel) {
|
|
35870
|
+
const reason = keyframe.status === "failed" ? `keyframe unavailable: ${keyframe.error ?? "generation failed"}` : "keyframe not generated (build without --skip-keyframe first)";
|
|
35871
|
+
ctx.onProgress({ type: "video-skipped", beatId: beat.id, reason });
|
|
35872
|
+
return { status: "skipped" };
|
|
35846
35873
|
}
|
|
35874
|
+
const keyframeImageAbs = keyframeImageRel ? join16(ctx.projectDir, keyframeImageRel) : void 0;
|
|
35875
|
+
loadSceneBuildEnv(ctx.projectDir);
|
|
35847
35876
|
const result = await executeVideoGenerate({
|
|
35848
35877
|
prompt: prompt3,
|
|
35849
35878
|
provider: ctx.videoProvider,
|
|
@@ -35889,9 +35918,7 @@ async function dispatchVideo(beat, ctx) {
|
|
|
35889
35918
|
cachePath: cache.path,
|
|
35890
35919
|
cacheKey: cache.key,
|
|
35891
35920
|
metadataPath,
|
|
35892
|
-
freshness: "fresh"
|
|
35893
|
-
keyframeStatus,
|
|
35894
|
-
keyframePath: keyframeRel
|
|
35921
|
+
freshness: "fresh"
|
|
35895
35922
|
};
|
|
35896
35923
|
}
|
|
35897
35924
|
const job = await createAndWriteJobRecord({
|
|
@@ -35923,9 +35950,7 @@ async function dispatchVideo(beat, ctx) {
|
|
|
35923
35950
|
provider: job.provider,
|
|
35924
35951
|
cachePath: cache.path,
|
|
35925
35952
|
cacheKey: cache.key,
|
|
35926
|
-
metadataPath
|
|
35927
|
-
keyframeStatus,
|
|
35928
|
-
keyframePath: keyframeRel
|
|
35953
|
+
metadataPath
|
|
35929
35954
|
};
|
|
35930
35955
|
}
|
|
35931
35956
|
async function dispatchMusic(beat, ctx) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeframe/cli",
|
|
3
|
-
"version": "0.113.
|
|
3
|
+
"version": "0.113.4",
|
|
4
4
|
"description": "VibeFrame CLI - agentic video workflow runtime",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -83,8 +83,8 @@
|
|
|
83
83
|
"tsx": "^4.21.0",
|
|
84
84
|
"typescript": "^5.3.3",
|
|
85
85
|
"vitest": "^1.2.2",
|
|
86
|
-
"@vibeframe/
|
|
87
|
-
"@vibeframe/
|
|
86
|
+
"@vibeframe/ai-providers": "0.113.4",
|
|
87
|
+
"@vibeframe/core": "0.113.4"
|
|
88
88
|
},
|
|
89
89
|
"engines": {
|
|
90
90
|
"node": ">=20"
|