@vibeframe/cli 0.81.0 → 0.83.0
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 +284 -129
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -35862,6 +35862,37 @@ var init_video = __esm({
|
|
|
35862
35862
|
}
|
|
35863
35863
|
});
|
|
35864
35864
|
|
|
35865
|
+
// src/commands/_shared/cost-tier.ts
|
|
35866
|
+
function applyTier(cmd, tier) {
|
|
35867
|
+
cmd[COST_TIER_KEY] = tier;
|
|
35868
|
+
cmd.addHelpText("after", `
|
|
35869
|
+
Cost: ${tier} (${TIER_DESCRIPTION[tier]})
|
|
35870
|
+
`);
|
|
35871
|
+
return cmd;
|
|
35872
|
+
}
|
|
35873
|
+
function getCostTier(cmd) {
|
|
35874
|
+
return cmd[COST_TIER_KEY];
|
|
35875
|
+
}
|
|
35876
|
+
function applyTiers(parent, tiers) {
|
|
35877
|
+
for (const cmd of parent.commands) {
|
|
35878
|
+
const tier = tiers[cmd.name()];
|
|
35879
|
+
if (tier) applyTier(cmd, tier);
|
|
35880
|
+
}
|
|
35881
|
+
}
|
|
35882
|
+
var TIER_DESCRIPTION, COST_TIER_KEY;
|
|
35883
|
+
var init_cost_tier = __esm({
|
|
35884
|
+
"src/commands/_shared/cost-tier.ts"() {
|
|
35885
|
+
"use strict";
|
|
35886
|
+
TIER_DESCRIPTION = {
|
|
35887
|
+
"free": "FFmpeg only, no API call",
|
|
35888
|
+
"low": "$0.01\u2013$0.10 per call",
|
|
35889
|
+
"high": "$1\u2013$5 per call",
|
|
35890
|
+
"very-high": "$5\u2013$50+ per call"
|
|
35891
|
+
};
|
|
35892
|
+
COST_TIER_KEY = Symbol.for("@vibeframe/cli.costTier");
|
|
35893
|
+
}
|
|
35894
|
+
});
|
|
35895
|
+
|
|
35865
35896
|
// src/commands/generate.ts
|
|
35866
35897
|
var generate_exports = {};
|
|
35867
35898
|
__export(generate_exports, {
|
|
@@ -35873,6 +35904,10 @@ __export(generate_exports, {
|
|
|
35873
35904
|
executeStoryboard: () => executeStoryboard,
|
|
35874
35905
|
generateCommand: () => generateCommand
|
|
35875
35906
|
});
|
|
35907
|
+
function tierLast(parent, tier) {
|
|
35908
|
+
const newest = parent.commands[parent.commands.length - 1];
|
|
35909
|
+
if (newest) applyTier(newest, tier);
|
|
35910
|
+
}
|
|
35876
35911
|
var generateCommand;
|
|
35877
35912
|
var init_generate = __esm({
|
|
35878
35913
|
"src/commands/generate.ts"() {
|
|
@@ -35891,6 +35926,7 @@ var init_generate = __esm({
|
|
|
35891
35926
|
init_video_extend();
|
|
35892
35927
|
init_image();
|
|
35893
35928
|
init_video();
|
|
35929
|
+
init_cost_tier();
|
|
35894
35930
|
init_sound_effect();
|
|
35895
35931
|
init_music_status();
|
|
35896
35932
|
init_background();
|
|
@@ -35928,18 +35964,31 @@ Run 'vibe schema generate.<command>' for structured parameter info.
|
|
|
35928
35964
|
`
|
|
35929
35965
|
);
|
|
35930
35966
|
registerImageCommand(generateCommand);
|
|
35967
|
+
tierLast(generateCommand, "high");
|
|
35931
35968
|
registerVideoCommand(generateCommand);
|
|
35969
|
+
tierLast(generateCommand, "very-high");
|
|
35932
35970
|
registerSpeechCommand(generateCommand);
|
|
35971
|
+
tierLast(generateCommand, "low");
|
|
35933
35972
|
registerSoundEffectCommand(generateCommand);
|
|
35973
|
+
tierLast(generateCommand, "low");
|
|
35934
35974
|
registerMusicCommand(generateCommand);
|
|
35975
|
+
tierLast(generateCommand, "low");
|
|
35935
35976
|
registerMusicStatusCommand(generateCommand);
|
|
35977
|
+
tierLast(generateCommand, "free");
|
|
35936
35978
|
registerStoryboardCommand(generateCommand);
|
|
35979
|
+
tierLast(generateCommand, "high");
|
|
35937
35980
|
registerMotionCommand(generateCommand);
|
|
35981
|
+
tierLast(generateCommand, "high");
|
|
35938
35982
|
registerThumbnailCommand(generateCommand);
|
|
35983
|
+
tierLast(generateCommand, "free");
|
|
35939
35984
|
registerBackgroundCommand(generateCommand);
|
|
35985
|
+
tierLast(generateCommand, "high");
|
|
35940
35986
|
registerVideoStatusCommand(generateCommand);
|
|
35987
|
+
tierLast(generateCommand, "free");
|
|
35941
35988
|
registerVideoCancelCommand(generateCommand);
|
|
35989
|
+
tierLast(generateCommand, "free");
|
|
35942
35990
|
registerVideoExtendCommand(generateCommand);
|
|
35991
|
+
tierLast(generateCommand, "very-high");
|
|
35943
35992
|
}
|
|
35944
35993
|
});
|
|
35945
35994
|
|
|
@@ -37238,7 +37287,7 @@ var init_ai_edit = __esm({
|
|
|
37238
37287
|
import { resolve as resolve28, extname as extname5, basename as basename7 } from "node:path";
|
|
37239
37288
|
import { existsSync as existsSync22 } from "node:fs";
|
|
37240
37289
|
function registerEditCommands(aiCommand) {
|
|
37241
|
-
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", "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", `
|
|
37290
|
+
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", `
|
|
37242
37291
|
Examples:
|
|
37243
37292
|
$ vibe edit silence-cut interview.mp4 -o clean.mp4
|
|
37244
37293
|
$ vibe ed sc podcast.mp4 -o trimmed.mp4 --noise -25 --min-duration 1.0
|
|
@@ -37263,6 +37312,13 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
|
|
|
37263
37312
|
const name = basename7(videoPath, ext);
|
|
37264
37313
|
const outputPath = options.output || `${name}-cut${ext}`;
|
|
37265
37314
|
const useGemini = options.useGemini || false;
|
|
37315
|
+
if (options.analyzeOnly) {
|
|
37316
|
+
console.error(
|
|
37317
|
+
source_default.yellow(
|
|
37318
|
+
"\u26A0 --analyze-only is deprecated. Use `vibe detect silence` for read-only detection. Will be removed in v1.0."
|
|
37319
|
+
)
|
|
37320
|
+
);
|
|
37321
|
+
}
|
|
37266
37322
|
if (options.dryRun) {
|
|
37267
37323
|
outputSuccess({
|
|
37268
37324
|
command: "edit silence-cut",
|
|
@@ -38485,6 +38541,7 @@ var init_edit_cmd = __esm({
|
|
|
38485
38541
|
init_ai_fill_gaps();
|
|
38486
38542
|
init_output();
|
|
38487
38543
|
init_validate();
|
|
38544
|
+
init_cost_tier();
|
|
38488
38545
|
editCommand = new Command("edit").alias("ed").description(
|
|
38489
38546
|
"Edit and post-process media (silence-cut, caption, grade, reframe, upscale...)"
|
|
38490
38547
|
).addHelpText(
|
|
@@ -39232,6 +39289,26 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39232
39289
|
exitWithError(generalError(`Video upscaling failed: ${msg}`));
|
|
39233
39290
|
}
|
|
39234
39291
|
});
|
|
39292
|
+
applyTiers(editCommand, {
|
|
39293
|
+
// free — FFmpeg only
|
|
39294
|
+
"noise-reduce": "free",
|
|
39295
|
+
"fade": "free",
|
|
39296
|
+
"text-overlay": "free",
|
|
39297
|
+
"interpolate": "free",
|
|
39298
|
+
// low — Whisper / single LLM call
|
|
39299
|
+
"silence-cut": "low",
|
|
39300
|
+
"caption": "low",
|
|
39301
|
+
"translate-srt": "low",
|
|
39302
|
+
"jump-cut": "low",
|
|
39303
|
+
"grade": "low",
|
|
39304
|
+
"speed-ramp": "low",
|
|
39305
|
+
// high — vision LLM or image gen
|
|
39306
|
+
"reframe": "high",
|
|
39307
|
+
"image": "high",
|
|
39308
|
+
"upscale": "high",
|
|
39309
|
+
// very-high — video gen per gap
|
|
39310
|
+
"fill-gaps": "very-high"
|
|
39311
|
+
});
|
|
39235
39312
|
}
|
|
39236
39313
|
});
|
|
39237
39314
|
|
|
@@ -50622,6 +50699,7 @@ var init_detect = __esm({
|
|
|
50622
50699
|
init_exec_safe();
|
|
50623
50700
|
init_output();
|
|
50624
50701
|
init_validate();
|
|
50702
|
+
init_cost_tier();
|
|
50625
50703
|
detectCommand = new Command("detect").description("Auto-detect scenes, beats, and silences in media");
|
|
50626
50704
|
detectCommand.command("scenes").description("Detect scene changes in video").argument("<video>", "Video file path").option("--threshold <value>", "Scene change threshold (0-1)", "0.3").option("-o, --output <path>", "Output JSON file with timestamps").option("--project <path>", "Add scenes as clips to project").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
|
|
50627
50705
|
const startedAt = Date.now();
|
|
@@ -50913,6 +50991,11 @@ var init_detect = __esm({
|
|
|
50913
50991
|
exitWithError(generalError(`Beat detection failed: ${msg}`));
|
|
50914
50992
|
}
|
|
50915
50993
|
});
|
|
50994
|
+
applyTiers(detectCommand, {
|
|
50995
|
+
"scenes": "free",
|
|
50996
|
+
"silence": "free",
|
|
50997
|
+
"beats": "free"
|
|
50998
|
+
});
|
|
50916
50999
|
}
|
|
50917
51000
|
});
|
|
50918
51001
|
|
|
@@ -51230,6 +51313,137 @@ overlay, etc.) \u2014 useful at the tail of a pipeline.
|
|
|
51230
51313
|
}
|
|
51231
51314
|
});
|
|
51232
51315
|
|
|
51316
|
+
// src/utils/key-live-test.ts
|
|
51317
|
+
var key_live_test_exports = {};
|
|
51318
|
+
__export(key_live_test_exports, {
|
|
51319
|
+
isTestableProvider: () => isTestableProvider,
|
|
51320
|
+
testKey: () => testKey
|
|
51321
|
+
});
|
|
51322
|
+
async function getOk(url, headers, signal) {
|
|
51323
|
+
try {
|
|
51324
|
+
const res = await fetch(url, { method: "GET", headers, signal });
|
|
51325
|
+
if (res.ok) return { ok: true, status: res.status };
|
|
51326
|
+
return {
|
|
51327
|
+
ok: false,
|
|
51328
|
+
status: res.status,
|
|
51329
|
+
message: `${res.status} ${res.statusText || ""}`.trim()
|
|
51330
|
+
};
|
|
51331
|
+
} catch (err) {
|
|
51332
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
51333
|
+
return { ok: false, message: msg };
|
|
51334
|
+
}
|
|
51335
|
+
}
|
|
51336
|
+
async function testKey(configKey, key, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
51337
|
+
const tester = TESTERS[configKey];
|
|
51338
|
+
if (!tester) {
|
|
51339
|
+
return { ok: false, skipped: true, message: "no tester registered" };
|
|
51340
|
+
}
|
|
51341
|
+
const ctrl = new AbortController();
|
|
51342
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
51343
|
+
try {
|
|
51344
|
+
return await tester.test(key, ctrl.signal);
|
|
51345
|
+
} finally {
|
|
51346
|
+
clearTimeout(timer);
|
|
51347
|
+
}
|
|
51348
|
+
}
|
|
51349
|
+
function isTestableProvider(configKey) {
|
|
51350
|
+
const t = TESTERS[configKey];
|
|
51351
|
+
if (!t) return false;
|
|
51352
|
+
return true;
|
|
51353
|
+
}
|
|
51354
|
+
var DEFAULT_TIMEOUT_MS, TESTERS;
|
|
51355
|
+
var init_key_live_test = __esm({
|
|
51356
|
+
"src/utils/key-live-test.ts"() {
|
|
51357
|
+
"use strict";
|
|
51358
|
+
DEFAULT_TIMEOUT_MS = 5e3;
|
|
51359
|
+
TESTERS = {
|
|
51360
|
+
anthropic: {
|
|
51361
|
+
name: "Anthropic",
|
|
51362
|
+
test: (key, signal) => getOk(
|
|
51363
|
+
"https://api.anthropic.com/v1/models",
|
|
51364
|
+
{ "x-api-key": key, "anthropic-version": "2023-06-01" },
|
|
51365
|
+
signal
|
|
51366
|
+
)
|
|
51367
|
+
},
|
|
51368
|
+
openai: {
|
|
51369
|
+
name: "OpenAI",
|
|
51370
|
+
test: (key, signal) => getOk(
|
|
51371
|
+
"https://api.openai.com/v1/models",
|
|
51372
|
+
{ Authorization: `Bearer ${key}` },
|
|
51373
|
+
signal
|
|
51374
|
+
)
|
|
51375
|
+
},
|
|
51376
|
+
google: {
|
|
51377
|
+
name: "Google",
|
|
51378
|
+
test: (key, signal) => getOk(
|
|
51379
|
+
`https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(key)}`,
|
|
51380
|
+
{},
|
|
51381
|
+
signal
|
|
51382
|
+
)
|
|
51383
|
+
},
|
|
51384
|
+
xai: {
|
|
51385
|
+
name: "xAI",
|
|
51386
|
+
test: (key, signal) => getOk(
|
|
51387
|
+
"https://api.x.ai/v1/models",
|
|
51388
|
+
{ Authorization: `Bearer ${key}` },
|
|
51389
|
+
signal
|
|
51390
|
+
)
|
|
51391
|
+
},
|
|
51392
|
+
elevenlabs: {
|
|
51393
|
+
name: "ElevenLabs",
|
|
51394
|
+
test: (key, signal) => getOk("https://api.elevenlabs.io/v1/user", { "xi-api-key": key }, signal)
|
|
51395
|
+
},
|
|
51396
|
+
replicate: {
|
|
51397
|
+
name: "Replicate",
|
|
51398
|
+
test: (key, signal) => getOk(
|
|
51399
|
+
"https://api.replicate.com/v1/account",
|
|
51400
|
+
{ Authorization: `Bearer ${key}` },
|
|
51401
|
+
signal
|
|
51402
|
+
)
|
|
51403
|
+
},
|
|
51404
|
+
openrouter: {
|
|
51405
|
+
name: "OpenRouter",
|
|
51406
|
+
test: (key, signal) => getOk(
|
|
51407
|
+
"https://openrouter.ai/api/v1/models",
|
|
51408
|
+
{ Authorization: `Bearer ${key}` },
|
|
51409
|
+
signal
|
|
51410
|
+
)
|
|
51411
|
+
},
|
|
51412
|
+
// Runway: /v1/organization is a read-only GET that returns tier info
|
|
51413
|
+
// (free, doesn't consume credits). Requires the X-Runway-Version
|
|
51414
|
+
// header alongside the Bearer token — without it the request 401s.
|
|
51415
|
+
runway: {
|
|
51416
|
+
name: "Runway",
|
|
51417
|
+
test: (key, signal) => getOk(
|
|
51418
|
+
"https://api.dev.runwayml.com/v1/organization",
|
|
51419
|
+
{
|
|
51420
|
+
Authorization: `Bearer ${key}`,
|
|
51421
|
+
"X-Runway-Version": "2024-11-06"
|
|
51422
|
+
},
|
|
51423
|
+
signal
|
|
51424
|
+
)
|
|
51425
|
+
},
|
|
51426
|
+
// The three below still have no cheap authenticated GET as of v0.83:
|
|
51427
|
+
// fal lacks a documented validation endpoint, Kling auth is per-
|
|
51428
|
+
// request HMAC-signed, ImgBB's only authenticated route is image
|
|
51429
|
+
// upload. Surface them as `skipped` so the user knows we didn't
|
|
51430
|
+
// blank-check.
|
|
51431
|
+
fal: {
|
|
51432
|
+
name: "fal.ai",
|
|
51433
|
+
test: async () => ({ ok: false, skipped: true, message: "no auth-only endpoint published" })
|
|
51434
|
+
},
|
|
51435
|
+
kling: {
|
|
51436
|
+
name: "Kling",
|
|
51437
|
+
test: async () => ({ ok: false, skipped: true, message: "per-request HMAC; can't test cheaply" })
|
|
51438
|
+
},
|
|
51439
|
+
imgbb: {
|
|
51440
|
+
name: "ImgBB",
|
|
51441
|
+
test: async () => ({ ok: false, skipped: true, message: "only auth route is image upload" })
|
|
51442
|
+
}
|
|
51443
|
+
};
|
|
51444
|
+
}
|
|
51445
|
+
});
|
|
51446
|
+
|
|
51233
51447
|
// src/pipeline/renderers/chrome.ts
|
|
51234
51448
|
import { existsSync as existsSync32 } from "node:fs";
|
|
51235
51449
|
import { homedir as homedir5 } from "node:os";
|
|
@@ -51999,126 +52213,6 @@ var init_scene_build = __esm({
|
|
|
51999
52213
|
}
|
|
52000
52214
|
});
|
|
52001
52215
|
|
|
52002
|
-
// src/utils/key-live-test.ts
|
|
52003
|
-
var key_live_test_exports = {};
|
|
52004
|
-
__export(key_live_test_exports, {
|
|
52005
|
-
isTestableProvider: () => isTestableProvider,
|
|
52006
|
-
testKey: () => testKey
|
|
52007
|
-
});
|
|
52008
|
-
async function getOk(url, headers, signal) {
|
|
52009
|
-
try {
|
|
52010
|
-
const res = await fetch(url, { method: "GET", headers, signal });
|
|
52011
|
-
if (res.ok) return { ok: true, status: res.status };
|
|
52012
|
-
return {
|
|
52013
|
-
ok: false,
|
|
52014
|
-
status: res.status,
|
|
52015
|
-
message: `${res.status} ${res.statusText || ""}`.trim()
|
|
52016
|
-
};
|
|
52017
|
-
} catch (err) {
|
|
52018
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
52019
|
-
return { ok: false, message: msg };
|
|
52020
|
-
}
|
|
52021
|
-
}
|
|
52022
|
-
async function testKey(configKey, key, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
52023
|
-
const tester = TESTERS[configKey];
|
|
52024
|
-
if (!tester) {
|
|
52025
|
-
return { ok: false, skipped: true, message: "no tester registered" };
|
|
52026
|
-
}
|
|
52027
|
-
const ctrl = new AbortController();
|
|
52028
|
-
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
52029
|
-
try {
|
|
52030
|
-
return await tester.test(key, ctrl.signal);
|
|
52031
|
-
} finally {
|
|
52032
|
-
clearTimeout(timer);
|
|
52033
|
-
}
|
|
52034
|
-
}
|
|
52035
|
-
function isTestableProvider(configKey) {
|
|
52036
|
-
const t = TESTERS[configKey];
|
|
52037
|
-
if (!t) return false;
|
|
52038
|
-
return true;
|
|
52039
|
-
}
|
|
52040
|
-
var DEFAULT_TIMEOUT_MS, TESTERS;
|
|
52041
|
-
var init_key_live_test = __esm({
|
|
52042
|
-
"src/utils/key-live-test.ts"() {
|
|
52043
|
-
"use strict";
|
|
52044
|
-
DEFAULT_TIMEOUT_MS = 5e3;
|
|
52045
|
-
TESTERS = {
|
|
52046
|
-
anthropic: {
|
|
52047
|
-
name: "Anthropic",
|
|
52048
|
-
test: (key, signal) => getOk(
|
|
52049
|
-
"https://api.anthropic.com/v1/models",
|
|
52050
|
-
{ "x-api-key": key, "anthropic-version": "2023-06-01" },
|
|
52051
|
-
signal
|
|
52052
|
-
)
|
|
52053
|
-
},
|
|
52054
|
-
openai: {
|
|
52055
|
-
name: "OpenAI",
|
|
52056
|
-
test: (key, signal) => getOk(
|
|
52057
|
-
"https://api.openai.com/v1/models",
|
|
52058
|
-
{ Authorization: `Bearer ${key}` },
|
|
52059
|
-
signal
|
|
52060
|
-
)
|
|
52061
|
-
},
|
|
52062
|
-
google: {
|
|
52063
|
-
name: "Google",
|
|
52064
|
-
test: (key, signal) => getOk(
|
|
52065
|
-
`https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(key)}`,
|
|
52066
|
-
{},
|
|
52067
|
-
signal
|
|
52068
|
-
)
|
|
52069
|
-
},
|
|
52070
|
-
xai: {
|
|
52071
|
-
name: "xAI",
|
|
52072
|
-
test: (key, signal) => getOk(
|
|
52073
|
-
"https://api.x.ai/v1/models",
|
|
52074
|
-
{ Authorization: `Bearer ${key}` },
|
|
52075
|
-
signal
|
|
52076
|
-
)
|
|
52077
|
-
},
|
|
52078
|
-
elevenlabs: {
|
|
52079
|
-
name: "ElevenLabs",
|
|
52080
|
-
test: (key, signal) => getOk("https://api.elevenlabs.io/v1/user", { "xi-api-key": key }, signal)
|
|
52081
|
-
},
|
|
52082
|
-
replicate: {
|
|
52083
|
-
name: "Replicate",
|
|
52084
|
-
test: (key, signal) => getOk(
|
|
52085
|
-
"https://api.replicate.com/v1/account",
|
|
52086
|
-
{ Authorization: `Bearer ${key}` },
|
|
52087
|
-
signal
|
|
52088
|
-
)
|
|
52089
|
-
},
|
|
52090
|
-
openrouter: {
|
|
52091
|
-
name: "OpenRouter",
|
|
52092
|
-
test: (key, signal) => getOk(
|
|
52093
|
-
"https://openrouter.ai/api/v1/models",
|
|
52094
|
-
{ Authorization: `Bearer ${key}` },
|
|
52095
|
-
signal
|
|
52096
|
-
)
|
|
52097
|
-
},
|
|
52098
|
-
// The four below have no cheap authenticated GET — providing a tester
|
|
52099
|
-
// would either consume a paid API call (fal, Runway), require HMAC
|
|
52100
|
-
// signing per request (Kling), or burn an image upload (ImgBB). We
|
|
52101
|
-
// surface them as `skipped` so the user knows we didn't blank-check.
|
|
52102
|
-
fal: {
|
|
52103
|
-
name: "fal.ai",
|
|
52104
|
-
test: async () => ({ ok: false, skipped: true, message: "no list endpoint" })
|
|
52105
|
-
},
|
|
52106
|
-
kling: {
|
|
52107
|
-
name: "Kling",
|
|
52108
|
-
test: async () => ({ ok: false, skipped: true, message: "HMAC-signed; can't test cheaply" })
|
|
52109
|
-
},
|
|
52110
|
-
runway: {
|
|
52111
|
-
name: "Runway",
|
|
52112
|
-
test: async () => ({ ok: false, skipped: true, message: "POST-only API" })
|
|
52113
|
-
},
|
|
52114
|
-
imgbb: {
|
|
52115
|
-
name: "ImgBB",
|
|
52116
|
-
test: async () => ({ ok: false, skipped: true, message: "would consume an upload" })
|
|
52117
|
-
}
|
|
52118
|
-
};
|
|
52119
|
-
}
|
|
52120
|
-
});
|
|
52121
|
-
|
|
52122
52216
|
// src/pipeline/resolver.ts
|
|
52123
52217
|
function resolveStepParams(params, completedSteps) {
|
|
52124
52218
|
const resolved = {};
|
|
@@ -62651,7 +62745,7 @@ init_source();
|
|
|
62651
62745
|
// package.json
|
|
62652
62746
|
var package_default = {
|
|
62653
62747
|
name: "@vibeframe/cli",
|
|
62654
|
-
version: "0.
|
|
62748
|
+
version: "0.83.0",
|
|
62655
62749
|
description: "VibeFrame CLI - natural language video editing from the terminal",
|
|
62656
62750
|
private: false,
|
|
62657
62751
|
license: "MIT",
|
|
@@ -63819,6 +63913,7 @@ init_ai_review();
|
|
|
63819
63913
|
init_output();
|
|
63820
63914
|
init_sanitize();
|
|
63821
63915
|
init_validate();
|
|
63916
|
+
init_cost_tier();
|
|
63822
63917
|
import { readFile as readFile23, writeFile as writeFile30 } from "node:fs/promises";
|
|
63823
63918
|
import { resolve as resolve33 } from "node:path";
|
|
63824
63919
|
var inspectCommand = new Command("inspect").description("Inspect media using AI (images, videos, YouTube URLs)").addHelpText(
|
|
@@ -63916,6 +64011,7 @@ inspectCommand.command("media").description("Analyze any media: images, videos,
|
|
|
63916
64011
|
exitWithError(apiError(`Analysis failed: ${error.message}`));
|
|
63917
64012
|
}
|
|
63918
64013
|
});
|
|
64014
|
+
applyTier(inspectCommand.commands[inspectCommand.commands.length - 1], "low");
|
|
63919
64015
|
inspectCommand.command("video").description("Analyze video using Gemini (summarize, Q&A, extract info)").argument("<source>", "Video file path or YouTube URL").argument("<prompt>", "Analysis prompt (e.g., 'Summarize this video')").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("-m, --model <model>", "Model: flash (default), flash-2.5, pro", "flash").option("--fps <number>", "Frames per second (default: 1, higher for action)").option("--start <seconds>", "Start offset in seconds (for clipping)").option("--end <seconds>", "End offset in seconds (for clipping)").option("--low-res", "Use low resolution mode (fewer tokens, longer videos)").option("--verbose", "Show token usage").option("--fields <fields>", "Comma-separated fields to include in output (e.g., response,model)").option("--dry-run", "Preview parameters without executing").action(async (source, prompt3, options) => {
|
|
63920
64016
|
const startedAt = Date.now();
|
|
63921
64017
|
try {
|
|
@@ -63992,7 +64088,9 @@ inspectCommand.command("video").description("Analyze video using Gemini (summari
|
|
|
63992
64088
|
exitWithError(apiError(`Video analysis failed: ${error.message}`));
|
|
63993
64089
|
}
|
|
63994
64090
|
});
|
|
64091
|
+
applyTier(inspectCommand.commands[inspectCommand.commands.length - 1], "low");
|
|
63995
64092
|
registerReviewCommand(inspectCommand);
|
|
64093
|
+
applyTier(inspectCommand.commands[inspectCommand.commands.length - 1], "low");
|
|
63996
64094
|
inspectCommand.command("suggest").description("Get AI edit suggestions using Gemini").argument("<project>", "Timeline file or directory").argument("<instruction>", "Natural language instruction").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("--apply", "Apply the first suggestion automatically").option("--dry-run", "Preview parameters without executing").action(async (projectPath, instruction, options) => {
|
|
63997
64095
|
const startedAt = Date.now();
|
|
63998
64096
|
try {
|
|
@@ -64063,6 +64161,7 @@ inspectCommand.command("suggest").description("Get AI edit suggestions using Gem
|
|
|
64063
64161
|
exitWithError(apiError(`AI suggestion failed: ${error.message}`));
|
|
64064
64162
|
}
|
|
64065
64163
|
});
|
|
64164
|
+
applyTier(inspectCommand.commands[inspectCommand.commands.length - 1], "low");
|
|
64066
64165
|
|
|
64067
64166
|
// src/commands/audio.ts
|
|
64068
64167
|
init_esm();
|
|
@@ -64072,6 +64171,7 @@ init_dist2();
|
|
|
64072
64171
|
init_api_key();
|
|
64073
64172
|
init_exec_safe();
|
|
64074
64173
|
init_subtitle();
|
|
64174
|
+
init_cost_tier();
|
|
64075
64175
|
init_ai_helpers();
|
|
64076
64176
|
init_output();
|
|
64077
64177
|
init_validate();
|
|
@@ -64579,6 +64679,14 @@ audioCommand.command("duck").description("Auto-duck background music when voice
|
|
|
64579
64679
|
exitWithError(generalError(`Audio ducking failed: ${msg}`));
|
|
64580
64680
|
}
|
|
64581
64681
|
});
|
|
64682
|
+
applyTiers(audioCommand, {
|
|
64683
|
+
"transcribe": "low",
|
|
64684
|
+
"list-voices": "low",
|
|
64685
|
+
"isolate": "low",
|
|
64686
|
+
"clone-voice": "low",
|
|
64687
|
+
"dub": "high",
|
|
64688
|
+
"duck": "free"
|
|
64689
|
+
});
|
|
64582
64690
|
|
|
64583
64691
|
// src/commands/remix.ts
|
|
64584
64692
|
init_esm();
|
|
@@ -64769,6 +64877,7 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
64769
64877
|
init_ai_highlights();
|
|
64770
64878
|
init_ai_animated_caption();
|
|
64771
64879
|
init_output();
|
|
64880
|
+
init_cost_tier();
|
|
64772
64881
|
var remixCommand = new Command("remix").description(
|
|
64773
64882
|
"Transform existing media (highlights, auto-shorts, animated captions, regenerate-scene)"
|
|
64774
64883
|
).addHelpText(
|
|
@@ -64910,10 +65019,17 @@ Required API Key: OPENAI_API_KEY (Whisper transcription)
|
|
|
64910
65019
|
exitWithError(generalError(`Animated caption failed: ${msg}`));
|
|
64911
65020
|
}
|
|
64912
65021
|
});
|
|
65022
|
+
applyTiers(remixCommand, {
|
|
65023
|
+
"regenerate-scene": "very-high",
|
|
65024
|
+
"highlights": "high",
|
|
65025
|
+
"auto-shorts": "high",
|
|
65026
|
+
"animated-caption": "low"
|
|
65027
|
+
});
|
|
64913
65028
|
|
|
64914
65029
|
// src/commands/schema.ts
|
|
64915
65030
|
init_esm();
|
|
64916
65031
|
init_output();
|
|
65032
|
+
init_cost_tier();
|
|
64917
65033
|
var schemaCommand = new Command("schema").description("Show JSON schema for a CLI command").argument("[command]", "Command path (e.g., generate.image, edit.silence-cut)").option("--list", "List all available command paths").action((commandPath, options) => {
|
|
64918
65034
|
const program3 = schemaCommand.parent;
|
|
64919
65035
|
if (!program3) {
|
|
@@ -65106,9 +65222,11 @@ function buildSchema(cmd, toolName) {
|
|
|
65106
65222
|
}
|
|
65107
65223
|
properties[name] = prop;
|
|
65108
65224
|
}
|
|
65225
|
+
const cost = getCostTier(cmd);
|
|
65109
65226
|
return {
|
|
65110
65227
|
name: toolName,
|
|
65111
65228
|
description: cmd.description(),
|
|
65229
|
+
...cost ? { cost } : {},
|
|
65112
65230
|
parameters: {
|
|
65113
65231
|
type: "object",
|
|
65114
65232
|
properties,
|
|
@@ -69581,13 +69699,17 @@ var VALID_LLM_PROVIDERS = [
|
|
|
69581
69699
|
"xai",
|
|
69582
69700
|
"openrouter"
|
|
69583
69701
|
];
|
|
69584
|
-
var setupCommand = new Command("setup").description("Configure VibeFrame (LLM provider, API keys)").option("--reset", "Reset configuration to defaults").option("--full", "Run full setup with all optional providers").option("--show", "Show current configuration (for debugging)").option("-v, --verbose", "With --show: include unset providers + Resolution order + Defaults block").option("--claude-code", "Show Claude Code integration guide").option("-y, --yes", "Non-interactive: write config without prompting (CI / devcontainer)").option("--provider <id>", "Set the Agent LLM provider (claude | openai | gemini | xai | openrouter | ollama)").option("--import-env", "Promote API keys from .env / shell env into config.yaml").addHelpText(
|
|
69702
|
+
var setupCommand = new Command("setup").description("Configure VibeFrame (LLM provider, API keys)").option("--reset", "Reset configuration to defaults").option("--full", "Run full setup with all optional providers").option("--show", "Show current configuration (for debugging)").option("-v, --verbose", "With --show: include unset providers + Resolution order + Defaults block").option("--claude-code", "Show Claude Code integration guide").option("-y, --yes", "Non-interactive: write config without prompting (CI / devcontainer)").option("--provider <id>", "Set the Agent LLM provider (claude | openai | gemini | xai | openrouter | ollama)").option("--import-env", "Promote API keys from .env / shell env into config.yaml").option("--test", "After save, live-test each configured key (exits 7 if any FAIL)").addHelpText(
|
|
69585
69703
|
"after",
|
|
69586
69704
|
`
|
|
69587
69705
|
Non-interactive examples (no TTY required):
|
|
69588
|
-
vibe setup --yes --provider claude --import-env
|
|
69589
|
-
vibe setup --yes --import-env
|
|
69590
|
-
vibe setup --yes --provider openai
|
|
69706
|
+
vibe setup --yes --provider claude --import-env Bootstrap CI / devcontainer
|
|
69707
|
+
vibe setup --yes --import-env --test Persist .env keys + verify each one
|
|
69708
|
+
vibe setup --yes --provider openai Just switch the agent provider
|
|
69709
|
+
|
|
69710
|
+
Exit codes (non-interactive):
|
|
69711
|
+
0 success 7 --test verification failed (one or more keys returned non-2xx)
|
|
69712
|
+
2 usage error (other codes match the rest of the CLI: 4/5/6 = auth/api/network)
|
|
69591
69713
|
`
|
|
69592
69714
|
).action(async (options) => {
|
|
69593
69715
|
if (options.claudeCode) {
|
|
@@ -69609,7 +69731,8 @@ Non-interactive examples (no TTY required):
|
|
|
69609
69731
|
if (wantsNonInteractive) {
|
|
69610
69732
|
await runNonInteractiveSetup({
|
|
69611
69733
|
provider: options.provider,
|
|
69612
|
-
importEnv: Boolean(options.importEnv)
|
|
69734
|
+
importEnv: Boolean(options.importEnv),
|
|
69735
|
+
test: Boolean(options.test)
|
|
69613
69736
|
});
|
|
69614
69737
|
return;
|
|
69615
69738
|
}
|
|
@@ -69716,6 +69839,37 @@ async function runNonInteractiveSetup(opts) {
|
|
|
69716
69839
|
for (const w of warnings) {
|
|
69717
69840
|
console.error(source_default.yellow(`\u26A0 ${w}`));
|
|
69718
69841
|
}
|
|
69842
|
+
if (opts.test) {
|
|
69843
|
+
const { testKey: testKey2 } = await Promise.resolve().then(() => (init_key_live_test(), key_live_test_exports));
|
|
69844
|
+
console.log();
|
|
69845
|
+
console.log(source_default.bold("Live key tests"));
|
|
69846
|
+
let failures = 0;
|
|
69847
|
+
let tested = 0;
|
|
69848
|
+
for (const meta of getAllApiKeys()) {
|
|
69849
|
+
const value = config2.providers[meta.configKey];
|
|
69850
|
+
if (!value) continue;
|
|
69851
|
+
tested++;
|
|
69852
|
+
const result = await testKey2(meta.configKey, value);
|
|
69853
|
+
const label = meta.label.padEnd(12);
|
|
69854
|
+
if (result.skipped) {
|
|
69855
|
+
console.log(source_default.dim(` SKIP ${label} ${result.message ?? "no test available"}`));
|
|
69856
|
+
} else if (result.ok) {
|
|
69857
|
+
console.log(source_default.green(` \u2713 ${label}`) + source_default.dim(` ${result.status}`));
|
|
69858
|
+
} else {
|
|
69859
|
+
failures++;
|
|
69860
|
+
const detail = result.message ?? `status ${result.status ?? "?"}`;
|
|
69861
|
+
console.log(source_default.red(` \u2717 ${label}`) + source_default.dim(` ${detail}`));
|
|
69862
|
+
}
|
|
69863
|
+
}
|
|
69864
|
+
if (tested === 0) {
|
|
69865
|
+
console.log(source_default.dim(" (no keys configured to test)"));
|
|
69866
|
+
}
|
|
69867
|
+
if (failures > 0) {
|
|
69868
|
+
console.error();
|
|
69869
|
+
console.error(source_default.red(`Verification failed: ${failures} key${failures === 1 ? "" : "s"} returned non-2xx.`));
|
|
69870
|
+
process.exit(7);
|
|
69871
|
+
}
|
|
69872
|
+
}
|
|
69719
69873
|
}
|
|
69720
69874
|
async function runSetupWizard(fullSetup = false) {
|
|
69721
69875
|
console.log();
|
|
@@ -69981,6 +70135,7 @@ async function showComplete(config2, defaultTryCommand, features = []) {
|
|
|
69981
70135
|
const tag = hostDetected && w.topic === "scene" ? source_default.cyan(" (recommended)") : "";
|
|
69982
70136
|
console.log(source_default.dim(` ${cmd}${w.summary}${tag}`));
|
|
69983
70137
|
}
|
|
70138
|
+
console.log(source_default.dim(" vibe doctor --test-keys Live-check each provider key against its API"));
|
|
69984
70139
|
console.log(source_default.dim(" vibe doctor Check system health + available commands"));
|
|
69985
70140
|
console.log(source_default.dim(" vibe schema --list Discover every command"));
|
|
69986
70141
|
console.log(source_default.dim(" vibe setup Re-run user-scope setup anytime"));
|
|
@@ -72709,10 +72864,10 @@ function showFirstRunBanner() {
|
|
|
72709
72864
|
console.log(` ${source_default.white("2.")} ${source_default.green("vibe doctor")} Check system health`);
|
|
72710
72865
|
console.log(` ${source_default.white("3.")} ${source_default.green("vibe --help")} See all commands`);
|
|
72711
72866
|
console.log();
|
|
72712
|
-
console.log(source_default.dim("
|
|
72867
|
+
console.log(source_default.dim(" Try without keys:"));
|
|
72868
|
+
console.log(` ${source_default.green("vibe demo")} Run sample edits on a test video`);
|
|
72713
72869
|
console.log(source_default.dim(" vibe edit silence-cut video.mp4 -o clean.mp4"));
|
|
72714
72870
|
console.log(source_default.dim(" vibe detect scenes video.mp4"));
|
|
72715
|
-
console.log(source_default.dim(" vibe edit fade video.mp4 -o faded.mp4 --fade-in 1 --fade-out 1"));
|
|
72716
72871
|
console.log();
|
|
72717
72872
|
}
|
|
72718
72873
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeframe/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.83.0",
|
|
4
4
|
"description": "VibeFrame CLI - natural language video editing from the terminal",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -183,8 +183,8 @@
|
|
|
183
183
|
"tsx": "^4.21.0",
|
|
184
184
|
"typescript": "^5.3.3",
|
|
185
185
|
"vitest": "^1.2.2",
|
|
186
|
-
"@vibeframe/ai-providers": "0.
|
|
187
|
-
"@vibeframe/core": "0.
|
|
186
|
+
"@vibeframe/ai-providers": "0.83.0",
|
|
187
|
+
"@vibeframe/core": "0.83.0"
|
|
188
188
|
},
|
|
189
189
|
"engines": {
|
|
190
190
|
"node": ">=20"
|