@vibeframe/cli 0.113.5 → 0.113.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +26 -16
- package/dist/tools/manifest/index.js +35 -25
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -41111,8 +41111,8 @@ async function executeJumpCut(options) {
|
|
|
41111
41111
|
};
|
|
41112
41112
|
} finally {
|
|
41113
41113
|
try {
|
|
41114
|
-
const { rm:
|
|
41115
|
-
await
|
|
41114
|
+
const { rm: rm6 } = await import("node:fs/promises");
|
|
41115
|
+
await rm6(tmpDir, { recursive: true, force: true });
|
|
41116
41116
|
} catch {
|
|
41117
41117
|
}
|
|
41118
41118
|
}
|
|
@@ -41457,8 +41457,8 @@ SRT saved to: ${outputSrtPath2}`
|
|
|
41457
41457
|
};
|
|
41458
41458
|
} finally {
|
|
41459
41459
|
try {
|
|
41460
|
-
const { rm:
|
|
41461
|
-
await
|
|
41460
|
+
const { rm: rm6 } = await import("node:fs/promises");
|
|
41461
|
+
await rm6(tmpDir, { recursive: true, force: true });
|
|
41462
41462
|
} catch {
|
|
41463
41463
|
}
|
|
41464
41464
|
}
|
|
@@ -62082,7 +62082,7 @@ __export(scene_build_exports, {
|
|
|
62082
62082
|
resolveSceneBuildMode: () => resolveSceneBuildMode
|
|
62083
62083
|
});
|
|
62084
62084
|
import { existsSync as existsSync49, readFileSync as readFileSync3 } from "node:fs";
|
|
62085
|
-
import { copyFile as copyFile5, mkdir as mkdir26, readFile as readFile46, writeFile as writeFile50 } from "node:fs/promises";
|
|
62085
|
+
import { copyFile as copyFile5, mkdir as mkdir26, readFile as readFile46, rm as rm4, writeFile as writeFile50 } from "node:fs/promises";
|
|
62086
62086
|
import { dirname as dirname24, join as join25, resolve as resolve65 } from "node:path";
|
|
62087
62087
|
async function executeSceneBuild(opts) {
|
|
62088
62088
|
const startedAt = Date.now();
|
|
@@ -62090,9 +62090,9 @@ async function executeSceneBuild(opts) {
|
|
|
62090
62090
|
const onProgress = opts.onProgress ?? (() => {
|
|
62091
62091
|
});
|
|
62092
62092
|
const mode = resolveSceneBuildMode(opts);
|
|
62093
|
-
|
|
62093
|
+
const selectedStage = opts.stage ?? (opts.skipRender ? "sync" : "all");
|
|
62094
62094
|
const skipVideoStopsRender = (opts.skipVideo ?? false) && (selectedStage === "all" || selectedStage === "render");
|
|
62095
|
-
|
|
62095
|
+
const effectiveSkipRender = (opts.skipRender ?? false) || skipVideoStopsRender;
|
|
62096
62096
|
const stageReports = createEmptyStageReports();
|
|
62097
62097
|
const warnings = [];
|
|
62098
62098
|
const retryWith = [];
|
|
@@ -62588,7 +62588,7 @@ async function executeSceneBuild(opts) {
|
|
|
62588
62588
|
});
|
|
62589
62589
|
}
|
|
62590
62590
|
}
|
|
62591
|
-
if (selectedStage === "sync" ||
|
|
62591
|
+
if (selectedStage === "sync" || effectiveSkipRender) {
|
|
62592
62592
|
return finishBuildResult({
|
|
62593
62593
|
success: true,
|
|
62594
62594
|
phase: "sync-only",
|
|
@@ -62880,6 +62880,10 @@ async function dispatchNarration(beat, ctx) {
|
|
|
62880
62880
|
}
|
|
62881
62881
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
62882
62882
|
await writeFile50(abs, result.audioBuffer);
|
|
62883
|
+
for (const otherExt of ["mp3", "wav"]) {
|
|
62884
|
+
const sibling = join25(ctx.projectDir, `assets/narration-${beat.id}.${otherExt}`);
|
|
62885
|
+
if (sibling !== abs && existsSync49(sibling)) await rm4(sibling, { force: true });
|
|
62886
|
+
}
|
|
62883
62887
|
await mkdir26(dirname24(cacheAbs), { recursive: true });
|
|
62884
62888
|
await writeFile50(cacheAbs, result.audioBuffer);
|
|
62885
62889
|
await writeAssetMetadata({
|
|
@@ -62912,11 +62916,12 @@ async function dispatchNarration(beat, ctx) {
|
|
|
62912
62916
|
}
|
|
62913
62917
|
function characterSheetPrompt(name, description) {
|
|
62914
62918
|
return [
|
|
62915
|
-
`Character
|
|
62916
|
-
"
|
|
62917
|
-
"
|
|
62919
|
+
`Character reference sheet of an original fictional character named ${name}: ${description}.`,
|
|
62920
|
+
"Top row: ONE large, sharp, evenly-lit frontal close-up of the FACE (head and shoulders) as the primary identity anchor \u2014 clear eyes, nose, jawline, and skin detail.",
|
|
62921
|
+
"Bottom row: three full-body views side by side \u2014 front, side profile, and back.",
|
|
62922
|
+
"The face, facial features, hairstyle, hair color, outfit, and body proportions must be IDENTICAL across the close-up and all three views.",
|
|
62918
62923
|
"Plain light-grey studio background, even neutral lighting, no props.",
|
|
62919
|
-
"Photorealistic
|
|
62924
|
+
"Photorealistic, professional concept-art character sheet layout."
|
|
62920
62925
|
].join(" ");
|
|
62921
62926
|
}
|
|
62922
62927
|
async function buildCharacters(characters, ctx) {
|
|
@@ -63255,7 +63260,8 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
63255
63260
|
imageSize: size,
|
|
63256
63261
|
imageQuality: ctx.imageQuality
|
|
63257
63262
|
};
|
|
63258
|
-
const
|
|
63263
|
+
const identityLock = "Keep the EXACT same person and face as the reference image(s): identical facial features (eyes, nose shape, jawline), skin tone and complexion, hairstyle, hair color, and wardrobe. Do not restyle or change the face. Change only the scene, pose, framing, and lighting. ";
|
|
63264
|
+
const generated = sheetBuffers.length > 0 ? await editKeyframeImage(`${identityLock}${keyframeCue}`, sheetBuffers, imgCtx, ratio) : await generateBackdropImage(keyframeCue, imgCtx, ratio);
|
|
63259
63265
|
if (!generated.success) return { status: "failed", error: generated.error };
|
|
63260
63266
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
63261
63267
|
await writeFile50(abs, generated.buffer);
|
|
@@ -63397,6 +63403,10 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
63397
63403
|
// reference-to-video.
|
|
63398
63404
|
image: keyframeImageAbs,
|
|
63399
63405
|
refImages: keyframeImageAbs ? void 0 : characterRefsAbs.length > 0 ? characterRefsAbs : void 0,
|
|
63406
|
+
// Build clips are always muted in the composition (a separate narration/music
|
|
63407
|
+
// track carries audio), so the model's generated audio is never used. Disable
|
|
63408
|
+
// it: it's wasted, and Seedance's audio moderation can spuriously fail a beat.
|
|
63409
|
+
generateAudio: false,
|
|
63400
63410
|
apiKey: await apiKeyForVideoProvider(ctx.videoProvider, ctx.projectDir)
|
|
63401
63411
|
});
|
|
63402
63412
|
if (!result.success || !result.taskId) {
|
|
@@ -74851,7 +74861,7 @@ init_source();
|
|
|
74851
74861
|
// package.json
|
|
74852
74862
|
var package_default = {
|
|
74853
74863
|
name: "@vibeframe/cli",
|
|
74854
|
-
version: "0.113.
|
|
74864
|
+
version: "0.113.7",
|
|
74855
74865
|
description: "VibeFrame CLI - agentic video workflow runtime",
|
|
74856
74866
|
private: false,
|
|
74857
74867
|
license: "MIT",
|
|
@@ -86704,7 +86714,7 @@ init_ai_edit();
|
|
|
86704
86714
|
init_output();
|
|
86705
86715
|
import { resolve as resolve71 } from "node:path";
|
|
86706
86716
|
import { existsSync as existsSync52 } from "node:fs";
|
|
86707
|
-
import { mkdir as mkdir27, rm as
|
|
86717
|
+
import { mkdir as mkdir27, rm as rm5 } from "node:fs/promises";
|
|
86708
86718
|
var DEMO_DIR = resolve71(process.cwd(), ".vibeframe-demo");
|
|
86709
86719
|
async function generateTestVideo(outputPath) {
|
|
86710
86720
|
const filter = [
|
|
@@ -86894,7 +86904,7 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
|
|
|
86894
86904
|
}
|
|
86895
86905
|
if (!options.keep) {
|
|
86896
86906
|
try {
|
|
86897
|
-
await
|
|
86907
|
+
await rm5(DEMO_DIR, { recursive: true, force: true });
|
|
86898
86908
|
} catch {
|
|
86899
86909
|
}
|
|
86900
86910
|
}
|
|
@@ -34576,7 +34576,7 @@ __export(scene_build_exports, {
|
|
|
34576
34576
|
resolveSceneBuildMode: () => resolveSceneBuildMode
|
|
34577
34577
|
});
|
|
34578
34578
|
import { existsSync as existsSync19, readFileSync as readFileSync3 } from "node:fs";
|
|
34579
|
-
import { copyFile as copyFile3, mkdir as mkdir10, readFile as readFile18, writeFile as writeFile18 } from "node:fs/promises";
|
|
34579
|
+
import { copyFile as copyFile3, mkdir as mkdir10, readFile as readFile18, rm, writeFile as writeFile18 } from "node:fs/promises";
|
|
34580
34580
|
import { dirname as dirname10, join as join16, resolve as resolve25 } from "node:path";
|
|
34581
34581
|
async function executeSceneBuild(opts) {
|
|
34582
34582
|
const startedAt = Date.now();
|
|
@@ -34584,9 +34584,9 @@ async function executeSceneBuild(opts) {
|
|
|
34584
34584
|
const onProgress = opts.onProgress ?? (() => {
|
|
34585
34585
|
});
|
|
34586
34586
|
const mode = resolveSceneBuildMode(opts);
|
|
34587
|
-
|
|
34587
|
+
const selectedStage = opts.stage ?? (opts.skipRender ? "sync" : "all");
|
|
34588
34588
|
const skipVideoStopsRender = (opts.skipVideo ?? false) && (selectedStage === "all" || selectedStage === "render");
|
|
34589
|
-
|
|
34589
|
+
const effectiveSkipRender = (opts.skipRender ?? false) || skipVideoStopsRender;
|
|
34590
34590
|
const stageReports = createEmptyStageReports();
|
|
34591
34591
|
const warnings = [];
|
|
34592
34592
|
const retryWith = [];
|
|
@@ -35082,7 +35082,7 @@ async function executeSceneBuild(opts) {
|
|
|
35082
35082
|
});
|
|
35083
35083
|
}
|
|
35084
35084
|
}
|
|
35085
|
-
if (selectedStage === "sync" ||
|
|
35085
|
+
if (selectedStage === "sync" || effectiveSkipRender) {
|
|
35086
35086
|
return finishBuildResult({
|
|
35087
35087
|
success: true,
|
|
35088
35088
|
phase: "sync-only",
|
|
@@ -35374,6 +35374,10 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35374
35374
|
}
|
|
35375
35375
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35376
35376
|
await writeFile18(abs, result.audioBuffer);
|
|
35377
|
+
for (const otherExt of ["mp3", "wav"]) {
|
|
35378
|
+
const sibling = join16(ctx.projectDir, `assets/narration-${beat.id}.${otherExt}`);
|
|
35379
|
+
if (sibling !== abs && existsSync19(sibling)) await rm(sibling, { force: true });
|
|
35380
|
+
}
|
|
35377
35381
|
await mkdir10(dirname10(cacheAbs), { recursive: true });
|
|
35378
35382
|
await writeFile18(cacheAbs, result.audioBuffer);
|
|
35379
35383
|
await writeAssetMetadata({
|
|
@@ -35406,11 +35410,12 @@ async function dispatchNarration(beat, ctx) {
|
|
|
35406
35410
|
}
|
|
35407
35411
|
function characterSheetPrompt(name, description) {
|
|
35408
35412
|
return [
|
|
35409
|
-
`Character
|
|
35410
|
-
"
|
|
35411
|
-
"
|
|
35413
|
+
`Character reference sheet of an original fictional character named ${name}: ${description}.`,
|
|
35414
|
+
"Top row: ONE large, sharp, evenly-lit frontal close-up of the FACE (head and shoulders) as the primary identity anchor \u2014 clear eyes, nose, jawline, and skin detail.",
|
|
35415
|
+
"Bottom row: three full-body views side by side \u2014 front, side profile, and back.",
|
|
35416
|
+
"The face, facial features, hairstyle, hair color, outfit, and body proportions must be IDENTICAL across the close-up and all three views.",
|
|
35412
35417
|
"Plain light-grey studio background, even neutral lighting, no props.",
|
|
35413
|
-
"Photorealistic
|
|
35418
|
+
"Photorealistic, professional concept-art character sheet layout."
|
|
35414
35419
|
].join(" ");
|
|
35415
35420
|
}
|
|
35416
35421
|
async function buildCharacters(characters, ctx) {
|
|
@@ -35749,7 +35754,8 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
35749
35754
|
imageSize: size,
|
|
35750
35755
|
imageQuality: ctx.imageQuality
|
|
35751
35756
|
};
|
|
35752
|
-
const
|
|
35757
|
+
const identityLock = "Keep the EXACT same person and face as the reference image(s): identical facial features (eyes, nose shape, jawline), skin tone and complexion, hairstyle, hair color, and wardrobe. Do not restyle or change the face. Change only the scene, pose, framing, and lighting. ";
|
|
35758
|
+
const generated = sheetBuffers.length > 0 ? await editKeyframeImage(`${identityLock}${keyframeCue}`, sheetBuffers, imgCtx, ratio) : await generateBackdropImage(keyframeCue, imgCtx, ratio);
|
|
35753
35759
|
if (!generated.success) return { status: "failed", error: generated.error };
|
|
35754
35760
|
await mkdir10(dirname10(abs), { recursive: true });
|
|
35755
35761
|
await writeFile18(abs, generated.buffer);
|
|
@@ -35891,6 +35897,10 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
35891
35897
|
// reference-to-video.
|
|
35892
35898
|
image: keyframeImageAbs,
|
|
35893
35899
|
refImages: keyframeImageAbs ? void 0 : characterRefsAbs.length > 0 ? characterRefsAbs : void 0,
|
|
35900
|
+
// Build clips are always muted in the composition (a separate narration/music
|
|
35901
|
+
// track carries audio), so the model's generated audio is never used. Disable
|
|
35902
|
+
// it: it's wasted, and Seedance's audio moderation can spuriously fail a beat.
|
|
35903
|
+
generateAudio: false,
|
|
35894
35904
|
apiKey: await apiKeyForVideoProvider(ctx.videoProvider, ctx.projectDir)
|
|
35895
35905
|
});
|
|
35896
35906
|
if (!result.success || !result.taskId) {
|
|
@@ -37419,8 +37429,8 @@ async function executeJumpCut(options) {
|
|
|
37419
37429
|
};
|
|
37420
37430
|
} finally {
|
|
37421
37431
|
try {
|
|
37422
|
-
const { rm:
|
|
37423
|
-
await
|
|
37432
|
+
const { rm: rm5 } = await import("node:fs/promises");
|
|
37433
|
+
await rm5(tmpDir, { recursive: true, force: true });
|
|
37424
37434
|
} catch {
|
|
37425
37435
|
}
|
|
37426
37436
|
}
|
|
@@ -37497,7 +37507,7 @@ __export(remotion_exports, {
|
|
|
37497
37507
|
wrapComponentWithImage: () => wrapComponentWithImage,
|
|
37498
37508
|
wrapComponentWithVideo: () => wrapComponentWithVideo
|
|
37499
37509
|
});
|
|
37500
|
-
import { writeFile as writeFile21, mkdir as mkdir12, rm, copyFile as copyFile4 } from "node:fs/promises";
|
|
37510
|
+
import { writeFile as writeFile21, mkdir as mkdir12, rm as rm2, copyFile as copyFile4 } from "node:fs/promises";
|
|
37501
37511
|
import { existsSync as existsSync23 } from "node:fs";
|
|
37502
37512
|
import { join as join18 } from "node:path";
|
|
37503
37513
|
import { tmpdir } from "node:os";
|
|
@@ -37691,7 +37701,7 @@ async function renderMotion(options) {
|
|
|
37691
37701
|
stderr: ${stderr}` : ""}`);
|
|
37692
37702
|
return { success: false, error: `Remotion render failed: ${msg}` };
|
|
37693
37703
|
} finally {
|
|
37694
|
-
await
|
|
37704
|
+
await rm2(dir, { recursive: true, force: true }).catch(() => {
|
|
37695
37705
|
});
|
|
37696
37706
|
}
|
|
37697
37707
|
}
|
|
@@ -37783,7 +37793,7 @@ async function renderWithEmbeddedImage(options) {
|
|
|
37783
37793
|
const msg = error instanceof Error ? error.message : String(error);
|
|
37784
37794
|
return { success: false, error: `Remotion image embed render failed: ${msg}` };
|
|
37785
37795
|
} finally {
|
|
37786
|
-
await
|
|
37796
|
+
await rm2(dir, { recursive: true, force: true }).catch(() => {
|
|
37787
37797
|
});
|
|
37788
37798
|
}
|
|
37789
37799
|
}
|
|
@@ -37860,14 +37870,14 @@ async function renderWithEmbeddedVideo(options) {
|
|
|
37860
37870
|
"-shortest",
|
|
37861
37871
|
mp4Out
|
|
37862
37872
|
], { timeout: 12e4 });
|
|
37863
|
-
await
|
|
37873
|
+
await rm2(mp4VideoOnly, { force: true }).catch(() => {
|
|
37864
37874
|
});
|
|
37865
37875
|
return { success: true, outputPath: mp4Out };
|
|
37866
37876
|
} catch (error) {
|
|
37867
37877
|
const msg = error instanceof Error ? error.message : String(error);
|
|
37868
37878
|
return { success: false, error: `Remotion video embed render failed: ${msg}` };
|
|
37869
37879
|
} finally {
|
|
37870
|
-
await
|
|
37880
|
+
await rm2(dir, { recursive: true, force: true }).catch(() => {
|
|
37871
37881
|
});
|
|
37872
37882
|
}
|
|
37873
37883
|
}
|
|
@@ -38431,8 +38441,8 @@ SRT saved to: ${outputSrtPath2}`
|
|
|
38431
38441
|
};
|
|
38432
38442
|
} finally {
|
|
38433
38443
|
try {
|
|
38434
|
-
const { rm:
|
|
38435
|
-
await
|
|
38444
|
+
const { rm: rm5 } = await import("node:fs/promises");
|
|
38445
|
+
await rm5(tmpDir, { recursive: true, force: true });
|
|
38436
38446
|
} catch {
|
|
38437
38447
|
}
|
|
38438
38448
|
}
|
|
@@ -41051,7 +41061,7 @@ __export(project_builder_exports, {
|
|
|
41051
41061
|
buildTempProject: () => buildTempProject,
|
|
41052
41062
|
resolveSourceUrl: () => resolveSourceUrl
|
|
41053
41063
|
});
|
|
41054
|
-
import { mkdtemp, mkdir as mkdir15, copyFile as copyFile5, writeFile as writeFile26, rm as
|
|
41064
|
+
import { mkdtemp, mkdir as mkdir15, copyFile as copyFile5, writeFile as writeFile26, rm as rm3, readdir as readdir3 } from "node:fs/promises";
|
|
41055
41065
|
import { existsSync as existsSync32 } from "node:fs";
|
|
41056
41066
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
41057
41067
|
import { createRequire } from "node:module";
|
|
@@ -41078,7 +41088,7 @@ async function buildTempProject(state, projectFileDir) {
|
|
|
41078
41088
|
await writeFile26(path.join(dir, "index.html"), html, "utf-8");
|
|
41079
41089
|
return {
|
|
41080
41090
|
dir,
|
|
41081
|
-
cleanup: () =>
|
|
41091
|
+
cleanup: () => rm3(dir, { recursive: true, force: true })
|
|
41082
41092
|
};
|
|
41083
41093
|
}
|
|
41084
41094
|
async function copyLottieRuntime(tempDir) {
|
|
@@ -55382,7 +55392,7 @@ init_api_key();
|
|
|
55382
55392
|
init_exec_safe();
|
|
55383
55393
|
init_remotion();
|
|
55384
55394
|
import { resolve as resolve36, dirname as dirname17, basename as basename10 } from "node:path";
|
|
55385
|
-
import { writeFile as writeFile28, mkdir as mkdir18, rm as
|
|
55395
|
+
import { writeFile as writeFile28, mkdir as mkdir18, rm as rm4 } from "node:fs/promises";
|
|
55386
55396
|
import { existsSync as existsSync34 } from "node:fs";
|
|
55387
55397
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
55388
55398
|
var ASS_STYLES = ["karaoke-sweep", "typewriter"];
|
|
@@ -55551,13 +55561,13 @@ async function executeAnimatedCaption(options) {
|
|
|
55551
55561
|
], { timeout: 12e4 });
|
|
55552
55562
|
const apiKey = await getApiKey("OPENAI_API_KEY", "OpenAI");
|
|
55553
55563
|
if (!apiKey) {
|
|
55554
|
-
await
|
|
55564
|
+
await rm4(tmpAudioDir, { recursive: true, force: true }).catch(() => {
|
|
55555
55565
|
});
|
|
55556
55566
|
return { success: false, error: "OPENAI_API_KEY required for Whisper transcription. Run 'vibe setup' or set OPENAI_API_KEY in .env" };
|
|
55557
55567
|
}
|
|
55558
55568
|
const transcript = await transcribeWithWords(audioPath, apiKey, language);
|
|
55559
55569
|
if (!transcript.words || transcript.words.length === 0) {
|
|
55560
|
-
await
|
|
55570
|
+
await rm4(tmpAudioDir, { recursive: true, force: true }).catch(() => {
|
|
55561
55571
|
});
|
|
55562
55572
|
return { success: false, error: "No words detected in transcription" };
|
|
55563
55573
|
}
|
|
@@ -55611,12 +55621,12 @@ async function executeAnimatedCaption(options) {
|
|
|
55611
55621
|
outputPath: absOutputPath
|
|
55612
55622
|
});
|
|
55613
55623
|
if (!renderResult.success) {
|
|
55614
|
-
await
|
|
55624
|
+
await rm4(tmpAudioDir, { recursive: true, force: true }).catch(() => {
|
|
55615
55625
|
});
|
|
55616
55626
|
return { success: false, error: renderResult.error };
|
|
55617
55627
|
}
|
|
55618
55628
|
}
|
|
55619
|
-
await
|
|
55629
|
+
await rm4(tmpAudioDir, { recursive: true, force: true }).catch(() => {
|
|
55620
55630
|
});
|
|
55621
55631
|
return {
|
|
55622
55632
|
success: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeframe/cli",
|
|
3
|
-
"version": "0.113.
|
|
3
|
+
"version": "0.113.7",
|
|
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/ai-providers": "0.113.
|
|
87
|
-
"@vibeframe/core": "0.113.
|
|
86
|
+
"@vibeframe/ai-providers": "0.113.7",
|
|
87
|
+
"@vibeframe/core": "0.113.7"
|
|
88
88
|
},
|
|
89
89
|
"engines": {
|
|
90
90
|
"node": ">=20"
|