@vibeframe/mcp-server 0.106.7 → 0.106.8
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 +174 -27
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -112209,11 +112209,11 @@ var require_extract_zip = __commonJS({
|
|
|
112209
112209
|
var { createWriteStream: createWriteStream3, promises: fs9 } = __require("fs");
|
|
112210
112210
|
var getStream = require_get_stream();
|
|
112211
112211
|
var path14 = __require("path");
|
|
112212
|
-
var { promisify:
|
|
112212
|
+
var { promisify: promisify3 } = __require("util");
|
|
112213
112213
|
var stream = __require("stream");
|
|
112214
112214
|
var yauzl = require_yauzl();
|
|
112215
|
-
var openZip =
|
|
112216
|
-
var pipeline =
|
|
112215
|
+
var openZip = promisify3(yauzl.open);
|
|
112216
|
+
var pipeline = promisify3(stream.pipeline);
|
|
112217
112217
|
var Extractor = class {
|
|
112218
112218
|
constructor(zipPath, opts) {
|
|
112219
112219
|
this.zipPath = zipPath;
|
|
@@ -112295,7 +112295,7 @@ var require_extract_zip = __commonJS({
|
|
|
112295
112295
|
await fs9.mkdir(destDir, mkdirOptions);
|
|
112296
112296
|
if (isDir) return;
|
|
112297
112297
|
debug9("opening read stream", dest);
|
|
112298
|
-
const readStream = await
|
|
112298
|
+
const readStream = await promisify3(this.zipfile.openReadStream.bind(this.zipfile))(entry);
|
|
112299
112299
|
if (symlink) {
|
|
112300
112300
|
const link = await getStream(readStream);
|
|
112301
112301
|
debug9("creating symlink", link, dest);
|
|
@@ -426816,11 +426816,11 @@ If you think this is a bug, please report it on the Puppeteer issue tracker.`;
|
|
|
426816
426816
|
var { createWriteStream: createWriteStream3, promises: fs82 } = __require2("fs");
|
|
426817
426817
|
var getStream = require_get_stream2();
|
|
426818
426818
|
var path122 = __require2("path");
|
|
426819
|
-
var { promisify:
|
|
426819
|
+
var { promisify: promisify3 } = __require2("util");
|
|
426820
426820
|
var stream2 = __require2("stream");
|
|
426821
426821
|
var yauzl = require_yauzl2();
|
|
426822
|
-
var openZip =
|
|
426823
|
-
var pipeline =
|
|
426822
|
+
var openZip = promisify3(yauzl.open);
|
|
426823
|
+
var pipeline = promisify3(stream2.pipeline);
|
|
426824
426824
|
var Extractor = class {
|
|
426825
426825
|
constructor(zipPath, opts) {
|
|
426826
426826
|
this.zipPath = zipPath;
|
|
@@ -426902,7 +426902,7 @@ If you think this is a bug, please report it on the Puppeteer issue tracker.`;
|
|
|
426902
426902
|
await fs82.mkdir(destDir, mkdirOptions);
|
|
426903
426903
|
if (isDir) return;
|
|
426904
426904
|
debug62("opening read stream", dest);
|
|
426905
|
-
const readStream = await
|
|
426905
|
+
const readStream = await promisify3(this.zipfile.openReadStream.bind(this.zipfile))(entry);
|
|
426906
426906
|
if (symlink) {
|
|
426907
426907
|
const link = await getStream(readStream);
|
|
426908
426908
|
debug62("creating symlink", link, dest);
|
|
@@ -468755,15 +468755,18 @@ var init_scene_audio_mux = __esm({
|
|
|
468755
468755
|
// ../cli/src/commands/_shared/scene-render.ts
|
|
468756
468756
|
var scene_render_exports = {};
|
|
468757
468757
|
__export(scene_render_exports, {
|
|
468758
|
+
buildMediaOpenCommand: () => buildMediaOpenCommand,
|
|
468758
468759
|
buildRenderConfig: () => buildRenderConfig,
|
|
468759
468760
|
defaultOutputPath: () => defaultOutputPath,
|
|
468760
468761
|
executeSceneRender: () => executeSceneRender,
|
|
468761
468762
|
prepareBeatRenderRoot: () => prepareBeatRenderRoot,
|
|
468762
468763
|
qualityToCrf: () => qualityToCrf
|
|
468763
468764
|
});
|
|
468765
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
468764
468766
|
import { existsSync as existsSync28 } from "node:fs";
|
|
468765
468767
|
import { mkdir as mkdir9, readFile as readFile12, stat as stat2, writeFile as writeFile13 } from "node:fs/promises";
|
|
468766
468768
|
import { resolve as resolve24, relative as relative8, dirname as dirname16, basename as basename6, join as join28, isAbsolute as isAbsolute3 } from "node:path";
|
|
468769
|
+
import { promisify as promisify2 } from "node:util";
|
|
468767
468770
|
function qualityToCrf(quality = "standard") {
|
|
468768
468771
|
return quality === "draft" ? 28 : quality === "high" ? 18 : 23;
|
|
468769
468772
|
}
|
|
@@ -468786,6 +468789,38 @@ function buildRenderConfig(opts) {
|
|
|
468786
468789
|
workers: opts.workers ?? 1
|
|
468787
468790
|
};
|
|
468788
468791
|
}
|
|
468792
|
+
function buildMediaOpenCommand(action, filePath, platform = process.platform) {
|
|
468793
|
+
if (platform === "darwin") {
|
|
468794
|
+
const args = action === "reveal" ? ["-R", filePath] : [filePath];
|
|
468795
|
+
return {
|
|
468796
|
+
command: "open",
|
|
468797
|
+
args,
|
|
468798
|
+
display: ["open", ...args].map(quoteCliArg).join(" ")
|
|
468799
|
+
};
|
|
468800
|
+
}
|
|
468801
|
+
if (platform === "win32") {
|
|
468802
|
+
if (action === "reveal") {
|
|
468803
|
+
const args2 = [`/select,${filePath}`];
|
|
468804
|
+
return {
|
|
468805
|
+
command: "explorer",
|
|
468806
|
+
args: args2,
|
|
468807
|
+
display: ["explorer", ...args2].map(quoteCliArg).join(" ")
|
|
468808
|
+
};
|
|
468809
|
+
}
|
|
468810
|
+
const args = ["/c", "start", "", filePath];
|
|
468811
|
+
return {
|
|
468812
|
+
command: "cmd",
|
|
468813
|
+
args,
|
|
468814
|
+
display: ["cmd", ...args].map(quoteCliArg).join(" ")
|
|
468815
|
+
};
|
|
468816
|
+
}
|
|
468817
|
+
const target = action === "reveal" ? dirname16(filePath) : filePath;
|
|
468818
|
+
return {
|
|
468819
|
+
command: "xdg-open",
|
|
468820
|
+
args: [target],
|
|
468821
|
+
display: ["xdg-open", target].map(quoteCliArg).join(" ")
|
|
468822
|
+
};
|
|
468823
|
+
}
|
|
468789
468824
|
async function executeSceneRender(opts = {}) {
|
|
468790
468825
|
const projectDir = resolve24(opts.projectDir ?? ".");
|
|
468791
468826
|
const projectConfig = await readProjectConfig(projectDir);
|
|
@@ -468909,6 +468944,9 @@ async function executeSceneRender(opts = {}) {
|
|
|
468909
468944
|
beat: opts.beatId ?? null,
|
|
468910
468945
|
root: root2,
|
|
468911
468946
|
outputPath: relative8(process.cwd(), outputPath) || outputPath,
|
|
468947
|
+
absoluteOutputPath: outputPath,
|
|
468948
|
+
openCommand: buildMediaOpenCommand("open", outputPath).display,
|
|
468949
|
+
revealCommand: buildMediaOpenCommand("reveal", outputPath).display,
|
|
468912
468950
|
durationMs: Date.now() - start,
|
|
468913
468951
|
framesRendered: job.framesRendered,
|
|
468914
468952
|
totalFrames: job.totalFrames,
|
|
@@ -468919,12 +468957,38 @@ async function executeSceneRender(opts = {}) {
|
|
|
468919
468957
|
audioMuxApplied,
|
|
468920
468958
|
audioMuxWarning
|
|
468921
468959
|
};
|
|
468960
|
+
if (opts.revealInFinder) {
|
|
468961
|
+
try {
|
|
468962
|
+
await runMediaOpenAction("reveal", outputPath);
|
|
468963
|
+
result.revealed = true;
|
|
468964
|
+
} catch (err) {
|
|
468965
|
+
result.revealed = false;
|
|
468966
|
+
result.revealError = err instanceof Error ? err.message : String(err);
|
|
468967
|
+
}
|
|
468968
|
+
}
|
|
468969
|
+
if (opts.openAfterRender) {
|
|
468970
|
+
try {
|
|
468971
|
+
await runMediaOpenAction("open", outputPath);
|
|
468972
|
+
result.opened = true;
|
|
468973
|
+
} catch (err) {
|
|
468974
|
+
result.opened = false;
|
|
468975
|
+
result.openError = err instanceof Error ? err.message : String(err);
|
|
468976
|
+
}
|
|
468977
|
+
}
|
|
468922
468978
|
result.reportPath = await writeRenderReport(projectDir, {
|
|
468923
468979
|
...result,
|
|
468924
468980
|
outputPath
|
|
468925
468981
|
});
|
|
468926
468982
|
return result;
|
|
468927
468983
|
}
|
|
468984
|
+
async function runMediaOpenAction(action, filePath) {
|
|
468985
|
+
const cmd = buildMediaOpenCommand(action, filePath);
|
|
468986
|
+
await execFileAsync2(cmd.command, cmd.args);
|
|
468987
|
+
}
|
|
468988
|
+
function quoteCliArg(value) {
|
|
468989
|
+
if (/^[A-Za-z0-9_/@%+=:,.-]+$/.test(value)) return value;
|
|
468990
|
+
return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
468991
|
+
}
|
|
468928
468992
|
async function safeStat(p2) {
|
|
468929
468993
|
try {
|
|
468930
468994
|
return await stat2(p2);
|
|
@@ -469095,6 +469159,13 @@ async function writeRenderReport(projectDir, result) {
|
|
|
469095
469159
|
beat: result.beat ?? null,
|
|
469096
469160
|
root: result.root,
|
|
469097
469161
|
outputPath: result.outputPath,
|
|
469162
|
+
absoluteOutputPath: result.absoluteOutputPath,
|
|
469163
|
+
openCommand: result.openCommand,
|
|
469164
|
+
revealCommand: result.revealCommand,
|
|
469165
|
+
opened: result.opened,
|
|
469166
|
+
revealed: result.revealed,
|
|
469167
|
+
openError: result.openError,
|
|
469168
|
+
revealError: result.revealError,
|
|
469098
469169
|
fps: result.fps,
|
|
469099
469170
|
quality: result.quality,
|
|
469100
469171
|
format: result.format,
|
|
@@ -469139,6 +469210,7 @@ function escapeAttr(value) {
|
|
|
469139
469210
|
function unique(items) {
|
|
469140
469211
|
return [...new Set(items.filter((item) => Boolean(item)))];
|
|
469141
469212
|
}
|
|
469213
|
+
var execFileAsync2;
|
|
469142
469214
|
var init_scene_render = __esm({
|
|
469143
469215
|
"../cli/src/commands/_shared/scene-render.ts"() {
|
|
469144
469216
|
"use strict";
|
|
@@ -469149,6 +469221,7 @@ var init_scene_render = __esm({
|
|
|
469149
469221
|
init_scene_audio_mux();
|
|
469150
469222
|
init_project_config();
|
|
469151
469223
|
init_scene_project();
|
|
469224
|
+
execFileAsync2 = promisify2(execFile2);
|
|
469152
469225
|
}
|
|
469153
469226
|
});
|
|
469154
469227
|
|
|
@@ -475058,11 +475131,11 @@ registerRoot(Root);
|
|
|
475058
475131
|
return dir;
|
|
475059
475132
|
}
|
|
475060
475133
|
async function this_npmInstall(dir) {
|
|
475061
|
-
const { execFile:
|
|
475062
|
-
const { promisify:
|
|
475063
|
-
const
|
|
475134
|
+
const { execFile: execFile3 } = await import("node:child_process");
|
|
475135
|
+
const { promisify: promisify3 } = await import("node:util");
|
|
475136
|
+
const execFileAsync3 = promisify3(execFile3);
|
|
475064
475137
|
try {
|
|
475065
|
-
await
|
|
475138
|
+
await execFileAsync3("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund"], {
|
|
475066
475139
|
cwd: dir,
|
|
475067
475140
|
timeout: 18e4
|
|
475068
475141
|
});
|
|
@@ -475086,13 +475159,13 @@ async function renderMotion(options) {
|
|
|
475086
475159
|
);
|
|
475087
475160
|
try {
|
|
475088
475161
|
const entryPoint = join35(dir, "Root.tsx");
|
|
475089
|
-
const { execFile:
|
|
475090
|
-
const { promisify:
|
|
475091
|
-
const
|
|
475162
|
+
const { execFile: execFile3 } = await import("node:child_process");
|
|
475163
|
+
const { promisify: promisify3 } = await import("node:util");
|
|
475164
|
+
const execFileAsync3 = promisify3(execFile3);
|
|
475092
475165
|
if (transparent) {
|
|
475093
475166
|
const webmOut = options.outputPath.replace(/\.\w+$/, ".webm");
|
|
475094
475167
|
try {
|
|
475095
|
-
await
|
|
475168
|
+
await execFileAsync3("npx", [
|
|
475096
475169
|
"remotion",
|
|
475097
475170
|
"render",
|
|
475098
475171
|
entryPoint,
|
|
@@ -475109,7 +475182,7 @@ async function renderMotion(options) {
|
|
|
475109
475182
|
} catch {
|
|
475110
475183
|
}
|
|
475111
475184
|
try {
|
|
475112
|
-
await
|
|
475185
|
+
await execFileAsync3("npx", [
|
|
475113
475186
|
"remotion",
|
|
475114
475187
|
"render",
|
|
475115
475188
|
entryPoint,
|
|
@@ -475129,7 +475202,7 @@ async function renderMotion(options) {
|
|
|
475129
475202
|
}
|
|
475130
475203
|
}
|
|
475131
475204
|
const mp4Out = options.outputPath.replace(/\.\w+$/, ".mp4");
|
|
475132
|
-
await
|
|
475205
|
+
await execFileAsync3("npx", [
|
|
475133
475206
|
"remotion",
|
|
475134
475207
|
"render",
|
|
475135
475208
|
entryPoint,
|
|
@@ -487769,7 +487842,9 @@ var sceneRenderSchema = z2.object({
|
|
|
487769
487842
|
fps: z2.number().optional().describe("Frames per second. Must be 24, 30, or 60. Default 30."),
|
|
487770
487843
|
quality: z2.enum(["draft", "standard", "high"]).optional().describe("Quality preset. Default 'standard'."),
|
|
487771
487844
|
format: z2.enum(["mp4", "webm", "mov"]).optional().describe("Container format. Default 'mp4'."),
|
|
487772
|
-
workers: z2.number().optional().describe("Capture worker count (1-16). Default 1.")
|
|
487845
|
+
workers: z2.number().optional().describe("Capture worker count (1-16). Default 1."),
|
|
487846
|
+
openAfterRender: z2.boolean().optional().describe("Open the rendered video in the OS default app after render. Default false."),
|
|
487847
|
+
revealInFinder: z2.boolean().optional().describe("Reveal the rendered video in Finder/file manager after render. Default false.")
|
|
487773
487848
|
});
|
|
487774
487849
|
var sceneRenderTool = defineTool({
|
|
487775
487850
|
name: "render",
|
|
@@ -487787,7 +487862,9 @@ var sceneRenderTool = defineTool({
|
|
|
487787
487862
|
fps: args.fps,
|
|
487788
487863
|
quality: args.quality,
|
|
487789
487864
|
format: args.format,
|
|
487790
|
-
workers: args.workers
|
|
487865
|
+
workers: args.workers,
|
|
487866
|
+
openAfterRender: args.openAfterRender,
|
|
487867
|
+
revealInFinder: args.revealInFinder
|
|
487791
487868
|
});
|
|
487792
487869
|
if (!result.success) {
|
|
487793
487870
|
return { success: false, error: result.error ?? "render failed" };
|
|
@@ -487796,6 +487873,13 @@ var sceneRenderTool = defineTool({
|
|
|
487796
487873
|
success: true,
|
|
487797
487874
|
data: {
|
|
487798
487875
|
outputPath: result.outputPath,
|
|
487876
|
+
absoluteOutputPath: result.absoluteOutputPath,
|
|
487877
|
+
openCommand: result.openCommand,
|
|
487878
|
+
revealCommand: result.revealCommand,
|
|
487879
|
+
opened: result.opened,
|
|
487880
|
+
revealed: result.revealed,
|
|
487881
|
+
openError: result.openError,
|
|
487882
|
+
revealError: result.revealError,
|
|
487799
487883
|
beat: result.beat,
|
|
487800
487884
|
root: result.root,
|
|
487801
487885
|
reportPath: result.reportPath,
|
|
@@ -487804,13 +487888,24 @@ var sceneRenderTool = defineTool({
|
|
|
487804
487888
|
totalFrames: result.totalFrames,
|
|
487805
487889
|
fps: result.fps,
|
|
487806
487890
|
quality: result.quality,
|
|
487807
|
-
format: result.format
|
|
487891
|
+
format: result.format,
|
|
487892
|
+
audioCount: result.audioCount,
|
|
487893
|
+
audioMuxApplied: result.audioMuxApplied,
|
|
487894
|
+
audioMuxWarning: result.audioMuxWarning
|
|
487808
487895
|
},
|
|
487809
487896
|
humanLines: [
|
|
487810
|
-
`\u2705 Render complete: ${result.outputPath}`,
|
|
487897
|
+
`\u2705 Render complete: ${result.absoluteOutputPath ?? result.outputPath}`,
|
|
487811
487898
|
` duration: ${((result.durationMs ?? 0) / 1e3).toFixed(1)}s`,
|
|
487812
487899
|
` frames: ${result.framesRendered ?? "?"}${result.totalFrames ? ` / ${result.totalFrames}` : ""}`,
|
|
487813
|
-
` config: ${result.fps}fps \xB7 ${result.quality} \xB7 ${result.format}
|
|
487900
|
+
` config: ${result.fps}fps \xB7 ${result.quality} \xB7 ${result.format}`,
|
|
487901
|
+
` audio: ${result.audioCount && result.audioCount > 0 ? `${result.audioCount} track${result.audioCount === 1 ? "" : "s"} muxed` : "silent"}`,
|
|
487902
|
+
...result.openCommand ? [` open: ${result.openCommand}`] : [],
|
|
487903
|
+
...result.revealCommand ? [` reveal: ${result.revealCommand}`] : [],
|
|
487904
|
+
` inspect: vibe inspect render ${projectDir} --cheap --json`,
|
|
487905
|
+
...result.opened ? [` opened: yes`] : [],
|
|
487906
|
+
...result.revealed ? [` revealed: yes`] : [],
|
|
487907
|
+
...result.openError ? [` open warning: ${result.openError}`] : [],
|
|
487908
|
+
...result.revealError ? [` reveal warning: ${result.revealError}`] : []
|
|
487814
487909
|
]
|
|
487815
487910
|
};
|
|
487816
487911
|
}
|
|
@@ -496245,6 +496340,56 @@ function zodToJsonSchema(schema) {
|
|
|
496245
496340
|
}
|
|
496246
496341
|
|
|
496247
496342
|
// ../cli/src/tools/adapters/mcp.ts
|
|
496343
|
+
function formatConsolePart(value) {
|
|
496344
|
+
if (typeof value === "string") return value;
|
|
496345
|
+
if (value instanceof Error) return value.stack ?? value.message;
|
|
496346
|
+
try {
|
|
496347
|
+
return JSON.stringify(value);
|
|
496348
|
+
} catch {
|
|
496349
|
+
return String(value);
|
|
496350
|
+
}
|
|
496351
|
+
}
|
|
496352
|
+
function invokeWriteCallback(encodingOrCallback, callback) {
|
|
496353
|
+
const cb = typeof encodingOrCallback === "function" ? encodingOrCallback : typeof callback === "function" ? callback : void 0;
|
|
496354
|
+
if (cb) queueMicrotask(() => cb());
|
|
496355
|
+
}
|
|
496356
|
+
function formatStdoutChunk(chunk, encodingOrCallback) {
|
|
496357
|
+
if (Buffer.isBuffer(chunk)) {
|
|
496358
|
+
const encoding = typeof encodingOrCallback === "string" ? encodingOrCallback : "utf8";
|
|
496359
|
+
return chunk.toString(encoding);
|
|
496360
|
+
}
|
|
496361
|
+
return String(chunk);
|
|
496362
|
+
}
|
|
496363
|
+
async function withCapturedStdout(fn) {
|
|
496364
|
+
const originalWrite = process.stdout.write;
|
|
496365
|
+
const originalConsoleLog = console.log;
|
|
496366
|
+
const originalConsoleInfo = console.info;
|
|
496367
|
+
const originalConsoleDebug = console.debug;
|
|
496368
|
+
let captured = "";
|
|
496369
|
+
const captureConsole = (...values) => {
|
|
496370
|
+
captured += `${values.map(formatConsolePart).join(" ")}
|
|
496371
|
+
`;
|
|
496372
|
+
};
|
|
496373
|
+
process.stdout.write = ((chunk, encodingOrCallback, callback) => {
|
|
496374
|
+
captured += formatStdoutChunk(chunk, encodingOrCallback);
|
|
496375
|
+
invokeWriteCallback(encodingOrCallback, callback);
|
|
496376
|
+
return true;
|
|
496377
|
+
});
|
|
496378
|
+
console.log = captureConsole;
|
|
496379
|
+
console.info = captureConsole;
|
|
496380
|
+
console.debug = captureConsole;
|
|
496381
|
+
try {
|
|
496382
|
+
return await fn();
|
|
496383
|
+
} finally {
|
|
496384
|
+
process.stdout.write = originalWrite;
|
|
496385
|
+
console.log = originalConsoleLog;
|
|
496386
|
+
console.info = originalConsoleInfo;
|
|
496387
|
+
console.debug = originalConsoleDebug;
|
|
496388
|
+
if (captured.trim() && process.env.VIBE_MCP_DEBUG_STDIO === "1") {
|
|
496389
|
+
process.stderr.write(captured);
|
|
496390
|
+
}
|
|
496391
|
+
}
|
|
496392
|
+
}
|
|
496248
496393
|
function formatZodError(err) {
|
|
496249
496394
|
const missing = err.issues.filter((i2) => {
|
|
496250
496395
|
if (i2.code !== "invalid_type") return false;
|
|
@@ -496291,10 +496436,12 @@ function buildMcpDispatcher(manifest2) {
|
|
|
496291
496436
|
};
|
|
496292
496437
|
}
|
|
496293
496438
|
try {
|
|
496294
|
-
const result = await
|
|
496295
|
-
|
|
496296
|
-
|
|
496297
|
-
|
|
496439
|
+
const result = await withCapturedStdout(
|
|
496440
|
+
() => tool.execute(parsed.data, {
|
|
496441
|
+
workingDirectory: process.cwd(),
|
|
496442
|
+
surface: "mcp"
|
|
496443
|
+
})
|
|
496444
|
+
);
|
|
496298
496445
|
const text = result.success ? JSON.stringify({ success: true, ...result.data }) : result.data ? JSON.stringify({
|
|
496299
496446
|
success: false,
|
|
496300
496447
|
error: result.error ?? "unknown error",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeframe/mcp-server",
|
|
3
|
-
"version": "0.106.
|
|
3
|
+
"version": "0.106.8",
|
|
4
4
|
"description": "VibeFrame MCP Server - AI-native video editing via Model Context Protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"tsx": "^4.21.0",
|
|
58
58
|
"typescript": "^5.3.3",
|
|
59
59
|
"vitest": "^1.2.2",
|
|
60
|
-
"@vibeframe/
|
|
61
|
-
"@vibeframe/
|
|
60
|
+
"@vibeframe/cli": "0.106.8",
|
|
61
|
+
"@vibeframe/core": "0.106.8"
|
|
62
62
|
},
|
|
63
63
|
"engines": {
|
|
64
64
|
"node": ">=20"
|