@vibeframe/cli 0.90.0 → 0.91.1
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 +224 -58
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -14296,7 +14296,7 @@ function getCommandKeyMap() {
|
|
|
14296
14296
|
function getSetupProviders() {
|
|
14297
14297
|
return [...apiKeyRegistry.values()].filter((k) => k.showInSetup).map((k) => ({
|
|
14298
14298
|
key: k.configKey,
|
|
14299
|
-
name: k.
|
|
14299
|
+
name: getDisplayLabelForApiKey(k.configKey),
|
|
14300
14300
|
env: k.envVar,
|
|
14301
14301
|
desc: k.setupDescription ?? "",
|
|
14302
14302
|
url: k.envExampleUrl
|
|
@@ -14308,6 +14308,16 @@ function getAllApiKeys() {
|
|
|
14308
14308
|
function getKeyFormat(configKey) {
|
|
14309
14309
|
return apiKeyRegistry.get(configKey)?.keyFormat;
|
|
14310
14310
|
}
|
|
14311
|
+
function getDisplayLabelForApiKey(configKey) {
|
|
14312
|
+
const apiKey = apiKeyRegistry.get(configKey);
|
|
14313
|
+
if (!apiKey)
|
|
14314
|
+
return configKey;
|
|
14315
|
+
const provider = [...providerRegistry2.values()].find((p) => p.apiKey === configKey && p.displayName && p.gateway);
|
|
14316
|
+
if (provider?.displayName && provider.gateway) {
|
|
14317
|
+
return `${provider.displayName} (via ${provider.gateway})`;
|
|
14318
|
+
}
|
|
14319
|
+
return apiKey.label;
|
|
14320
|
+
}
|
|
14311
14321
|
function getAllProviders() {
|
|
14312
14322
|
return [...providerRegistry2.values()];
|
|
14313
14323
|
}
|
|
@@ -24412,7 +24422,7 @@ var init_FalProvider = __esm({
|
|
|
24412
24422
|
VALID_ASPECTS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "auto"];
|
|
24413
24423
|
FalProvider = class {
|
|
24414
24424
|
constructor() {
|
|
24415
|
-
this.id = "
|
|
24425
|
+
this.id = "seedance";
|
|
24416
24426
|
this.name = "fal.ai (Seedance 2.0)";
|
|
24417
24427
|
this.description = "fal.ai hosting ByteDance Seedance 2.0 \u2014 Artificial Analysis #2 on both text-to-video and image-to-video leaderboards";
|
|
24418
24428
|
this.capabilities = ["text-to-video", "image-to-video"];
|
|
@@ -24511,11 +24521,13 @@ var init_fal = __esm({
|
|
|
24511
24521
|
init_FalProvider();
|
|
24512
24522
|
init_define_provider();
|
|
24513
24523
|
defineProvider({
|
|
24514
|
-
id: "
|
|
24515
|
-
label: "
|
|
24524
|
+
id: "seedance",
|
|
24525
|
+
label: "Seedance 2.0",
|
|
24516
24526
|
displayName: "Seedance 2.0",
|
|
24517
24527
|
gateway: "fal.ai",
|
|
24518
|
-
|
|
24528
|
+
// `fal` is a deprecated v0.x alias kept so existing scripts keep working.
|
|
24529
|
+
// Will be removed at the 1.0 cut (see docs/1.0-readiness.md).
|
|
24530
|
+
aliases: ["fal"],
|
|
24519
24531
|
models: ["seedance-2.0", "seedance-2.0-fast"],
|
|
24520
24532
|
capabilities: ["text-to-video", "image-to-video", "native-audio"],
|
|
24521
24533
|
apiKey: "fal",
|
|
@@ -25216,6 +25228,7 @@ __export(dist_exports2, {
|
|
|
25216
25228
|
getAllProviders: () => getAllProviders,
|
|
25217
25229
|
getBestProviderForCapability: () => getBestProviderForCapability,
|
|
25218
25230
|
getCommandKeyMap: () => getCommandKeyMap,
|
|
25231
|
+
getDisplayLabelForApiKey: () => getDisplayLabelForApiKey,
|
|
25219
25232
|
getKeyFormat: () => getKeyFormat,
|
|
25220
25233
|
getProviderEnvVars: () => getProviderEnvVars,
|
|
25221
25234
|
getProvidersFor: () => getProvidersFor,
|
|
@@ -31882,7 +31895,7 @@ async function promptConfirm(question, defaultYes = true) {
|
|
|
31882
31895
|
if (answer === "") return defaultYes;
|
|
31883
31896
|
return answer.toLowerCase().startsWith("y");
|
|
31884
31897
|
}
|
|
31885
|
-
async function promptMultiSelect(_question, options, defaultSelected = []) {
|
|
31898
|
+
async function promptMultiSelect(_question, options, defaultSelected = [], opts = {}) {
|
|
31886
31899
|
const input = getTTYInputStream();
|
|
31887
31900
|
const selected = options.map((_, i) => Boolean(defaultSelected[i]));
|
|
31888
31901
|
if (typeof input.setRawMode === "function") {
|
|
@@ -31918,6 +31931,9 @@ async function promptMultiSelect(_question, options, defaultSelected = []) {
|
|
|
31918
31931
|
};
|
|
31919
31932
|
const onData = (char) => {
|
|
31920
31933
|
if (char === "\r" || char === "\n") {
|
|
31934
|
+
if (opts.enterSelectsFocusedWhenEmpty && selected.every((v) => !v)) {
|
|
31935
|
+
selected[cursor] = true;
|
|
31936
|
+
}
|
|
31921
31937
|
finish();
|
|
31922
31938
|
} else if (char === "") {
|
|
31923
31939
|
input.setRawMode(false);
|
|
@@ -35484,9 +35500,9 @@ var init_ai_script_pipeline = __esm({
|
|
|
35484
35500
|
import { resolve as resolve26 } from "node:path";
|
|
35485
35501
|
import { readFile as readFile14, writeFile as writeFile24 } from "node:fs/promises";
|
|
35486
35502
|
function registerVideoCommand(parent) {
|
|
35487
|
-
parent.command("video").alias("vid").description("Generate video using AI (Seedance, Grok, Kling, Runway, or Veo)").argument("[prompt]", "Text prompt describing the video (interactive if omitted)").option("-p, --provider <provider>", "Provider: seedance (ByteDance Seedance 2.0 via fal.ai), grok, kling, runway, veo. `fal` is a
|
|
35503
|
+
parent.command("video").alias("vid").description("Generate video using AI (Seedance, Grok, Kling, Runway, or Veo)").argument("[prompt]", "Text prompt describing the video (interactive if omitted)").option("-p, --provider <provider>", "Provider: seedance (ByteDance Seedance 2.0 via fal.ai), grok, kling, runway, veo. `fal` is a deprecated v0.x alias for seedance and will be removed in 1.0.").option("-k, --api-key <key>", "API key (or set FAL_KEY / XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY / GOOGLE_API_KEY env)").option("-o, --output <path>", "Output file path (downloads video)").option("-i, --image <path>", "Reference image for image-to-video").option(
|
|
35488
35504
|
"-d, --duration <sec>",
|
|
35489
|
-
"Duration in seconds. Seedance accepts 4-15
|
|
35505
|
+
"Duration in seconds. Seedance accepts 4-15; Kling accepts 5 or 10; Veo maps to 6 or 8.",
|
|
35490
35506
|
"5"
|
|
35491
35507
|
).option("-r, --ratio <ratio>", "Aspect ratio: 16:9, 9:16, or 1:1 (auto-detected from image if omitted)").option("--seed <number>", "Random seed for reproducibility (Runway only)").option("--mode <mode>", "Generation mode: std or pro (Kling only)", "std").option("--seedance-model <model>", "Seedance variant: quality or fast (fal.ai only)", "quality").option("--negative <prompt>", "Negative prompt - what to avoid (Kling/Veo)").option("--resolution <res>", "Video resolution: 720p, 1080p, 4k (Veo only)").option("--last-frame <path>", "Last frame image for frame interpolation (Veo only)").option("--ref-images <paths...>", "Reference images for character consistency (Veo 3.1 only, max 3)").option("--person <mode>", "Person generation: allow_all, allow_adult (Veo only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast (default: 3.1-fast)", "3.1-fast").option("--runway-model <model>", "Runway model: gen4.5 (default, text+image-to-video), gen4_turbo (image-to-video only)", "gen4.5").option("--no-wait", "Start generation and return task ID without waiting").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
|
|
35492
35508
|
Examples:
|
|
@@ -35532,8 +35548,7 @@ Examples:
|
|
|
35532
35548
|
veo: "GOOGLE_API_KEY",
|
|
35533
35549
|
kling: "KLING_API_KEY",
|
|
35534
35550
|
runway: "RUNWAY_API_SECRET",
|
|
35535
|
-
seedance: "FAL_KEY"
|
|
35536
|
-
fal: "FAL_KEY"
|
|
35551
|
+
seedance: "FAL_KEY"
|
|
35537
35552
|
};
|
|
35538
35553
|
let provider;
|
|
35539
35554
|
if (options.provider) {
|
|
@@ -35542,9 +35557,17 @@ Examples:
|
|
|
35542
35557
|
exitWithError(
|
|
35543
35558
|
usageError(
|
|
35544
35559
|
`Invalid provider: ${provider}`,
|
|
35545
|
-
"Available providers: seedance, grok, kling, runway, veo. `fal` is a
|
|
35560
|
+
"Available providers: seedance, grok, kling, runway, veo. `fal` is a deprecated alias for seedance."
|
|
35561
|
+
)
|
|
35562
|
+
);
|
|
35563
|
+
}
|
|
35564
|
+
if (provider === "fal") {
|
|
35565
|
+
process.stderr.write(
|
|
35566
|
+
source_default.yellow(
|
|
35567
|
+
"Note: `-p fal` is a deprecated alias for `-p seedance` and will be removed in 1.0.\n"
|
|
35546
35568
|
)
|
|
35547
35569
|
);
|
|
35570
|
+
provider = "seedance";
|
|
35548
35571
|
}
|
|
35549
35572
|
if (videoEnvMap[provider] && !hasApiKey(videoEnvMap[provider]) && !options.apiKey) {
|
|
35550
35573
|
const resolved = resolveProvider("video");
|
|
@@ -35605,7 +35628,7 @@ Examples:
|
|
|
35605
35628
|
data: {
|
|
35606
35629
|
params: {
|
|
35607
35630
|
prompt: prompt3,
|
|
35608
|
-
provider
|
|
35631
|
+
provider,
|
|
35609
35632
|
duration: options.duration,
|
|
35610
35633
|
ratio: options.ratio,
|
|
35611
35634
|
image: options.image,
|
|
@@ -35624,16 +35647,14 @@ Examples:
|
|
|
35624
35647
|
kling: "KLING_API_KEY",
|
|
35625
35648
|
veo: "GOOGLE_API_KEY",
|
|
35626
35649
|
grok: "XAI_API_KEY",
|
|
35627
|
-
seedance: "FAL_KEY"
|
|
35628
|
-
fal: "FAL_KEY"
|
|
35650
|
+
seedance: "FAL_KEY"
|
|
35629
35651
|
};
|
|
35630
35652
|
const providerNameMap = {
|
|
35631
35653
|
runway: "Runway",
|
|
35632
35654
|
kling: "Kling",
|
|
35633
35655
|
veo: "Veo",
|
|
35634
35656
|
grok: "Grok",
|
|
35635
|
-
seedance: "Seedance 2.0 via fal.ai"
|
|
35636
|
-
fal: "Seedance 2.0 via fal.ai"
|
|
35657
|
+
seedance: "Seedance 2.0 via fal.ai"
|
|
35637
35658
|
};
|
|
35638
35659
|
const envKey = envKeyMap[provider];
|
|
35639
35660
|
const providerName = providerNameMap[provider];
|
|
@@ -35852,7 +35873,7 @@ Examples:
|
|
|
35852
35873
|
},
|
|
35853
35874
|
3e5
|
|
35854
35875
|
);
|
|
35855
|
-
} else if (provider === "
|
|
35876
|
+
} else if (provider === "seedance") {
|
|
35856
35877
|
const fal = new FalProvider();
|
|
35857
35878
|
await fal.initialize({ apiKey });
|
|
35858
35879
|
let falImage = referenceImage;
|
|
@@ -62892,7 +62913,7 @@ init_source();
|
|
|
62892
62913
|
// package.json
|
|
62893
62914
|
var package_default = {
|
|
62894
62915
|
name: "@vibeframe/cli",
|
|
62895
|
-
version: "0.
|
|
62916
|
+
version: "0.91.1",
|
|
62896
62917
|
description: "VibeFrame CLI - natural language video editing from the terminal",
|
|
62897
62918
|
private: false,
|
|
62898
62919
|
license: "MIT",
|
|
@@ -69969,6 +69990,26 @@ var AI_FEATURES = [
|
|
|
69969
69990
|
defaultProvider: "OpenAI gpt-image-2 (Artificial Analysis #1, since v0.56)",
|
|
69970
69991
|
alsoAvailable: "Gemini Nano Banana, Grok Imagine",
|
|
69971
69992
|
keys: [{ configKey: "openai", envVar: "OPENAI_API_KEY", name: "OpenAI", url: "https://platform.openai.com/api-keys", what: "gpt-image-2 image generation + editing (also Whisper, Agent)" }],
|
|
69993
|
+
providerChoices: [
|
|
69994
|
+
{
|
|
69995
|
+
label: "OpenAI gpt-image-2",
|
|
69996
|
+
desc: "recommended default, image generation + editing",
|
|
69997
|
+
key: { configKey: "openai", envVar: "OPENAI_API_KEY", name: "OpenAI", url: "https://platform.openai.com/api-keys", what: "gpt-image-2 image generation + editing (also Whisper, Agent)" },
|
|
69998
|
+
defaultFor: { kind: "image", value: "openai" }
|
|
69999
|
+
},
|
|
70000
|
+
{
|
|
70001
|
+
label: "Gemini",
|
|
70002
|
+
desc: "Google image generation + multimodal analysis",
|
|
70003
|
+
key: { configKey: "google", envVar: "GOOGLE_API_KEY", name: "Google", url: "https://aistudio.google.com/apikey", what: "Gemini image generation and multimodal analysis" },
|
|
70004
|
+
defaultFor: { kind: "image", value: "gemini" }
|
|
70005
|
+
},
|
|
70006
|
+
{
|
|
70007
|
+
label: "Grok",
|
|
70008
|
+
desc: "xAI image generation",
|
|
70009
|
+
key: { configKey: "xai", envVar: "XAI_API_KEY", name: "xAI", url: "https://console.x.ai/", what: "Grok image generation and Agent" },
|
|
70010
|
+
defaultFor: { kind: "image", value: "grok" }
|
|
70011
|
+
}
|
|
70012
|
+
],
|
|
69972
70013
|
tryCommand: 'vibe generate image "a sunset over mountains" -o test.png'
|
|
69973
70014
|
},
|
|
69974
70015
|
{
|
|
@@ -69976,7 +70017,39 @@ var AI_FEATURES = [
|
|
|
69976
70017
|
desc: "generate + extend",
|
|
69977
70018
|
defaultProvider: "Seedance 2.0 via fal.ai (Artificial Analysis #2 t2v + i2v, since v0.57)",
|
|
69978
70019
|
alsoAvailable: "Grok Imagine, Kling, Runway Gen-4.5, Google Veo",
|
|
69979
|
-
keys: [{ configKey: "fal", envVar: "FAL_KEY", name: "fal.ai", url: "https://fal.ai/dashboard/keys", what: "ByteDance Seedance 2.0 text-to-video and image-to-video" }],
|
|
70020
|
+
keys: [{ configKey: "fal", envVar: "FAL_KEY", name: "Seedance 2.0 (via fal.ai)", url: "https://fal.ai/dashboard/keys", what: "ByteDance Seedance 2.0 text-to-video and image-to-video" }],
|
|
70021
|
+
providerChoices: [
|
|
70022
|
+
{
|
|
70023
|
+
label: "Seedance 2.0 via fal.ai",
|
|
70024
|
+
desc: "recommended default, text-to-video + image-to-video",
|
|
70025
|
+
key: { configKey: "fal", envVar: "FAL_KEY", name: "Seedance 2.0 (via fal.ai)", url: "https://fal.ai/dashboard/keys", what: "ByteDance Seedance 2.0 text-to-video and image-to-video" },
|
|
70026
|
+
defaultFor: { kind: "video", value: "seedance" }
|
|
70027
|
+
},
|
|
70028
|
+
{
|
|
70029
|
+
label: "Grok",
|
|
70030
|
+
desc: "xAI video generation with audio",
|
|
70031
|
+
key: { configKey: "xai", envVar: "XAI_API_KEY", name: "xAI", url: "https://console.x.ai/", what: "Grok video generation and Agent" },
|
|
70032
|
+
defaultFor: { kind: "video", value: "grok" }
|
|
70033
|
+
},
|
|
70034
|
+
{
|
|
70035
|
+
label: "Kling",
|
|
70036
|
+
desc: "Kling v2.5/v3 video generation",
|
|
70037
|
+
key: { configKey: "kling", envVar: "KLING_API_KEY", name: "Kling", url: "https://platform.klingai.com/", what: "Kling video generation" },
|
|
70038
|
+
defaultFor: { kind: "video", value: "kling" }
|
|
70039
|
+
},
|
|
70040
|
+
{
|
|
70041
|
+
label: "Runway",
|
|
70042
|
+
desc: "Runway Gen-4.5 video generation",
|
|
70043
|
+
key: { configKey: "runway", envVar: "RUNWAY_API_SECRET", name: "Runway", url: "https://app.runwayml.com/settings/api-keys", what: "Runway Gen-4.5 video generation" },
|
|
70044
|
+
defaultFor: { kind: "video", value: "runway" }
|
|
70045
|
+
},
|
|
70046
|
+
{
|
|
70047
|
+
label: "Veo",
|
|
70048
|
+
desc: "Google Veo video generation",
|
|
70049
|
+
key: { configKey: "google", envVar: "GOOGLE_API_KEY", name: "Google", url: "https://aistudio.google.com/apikey", what: "Google Veo video generation" },
|
|
70050
|
+
defaultFor: { kind: "video", value: "veo" }
|
|
70051
|
+
}
|
|
70052
|
+
],
|
|
69980
70053
|
tryCommand: 'vibe generate video "ocean waves" -o waves.mp4'
|
|
69981
70054
|
},
|
|
69982
70055
|
{
|
|
@@ -69985,6 +70058,23 @@ var AI_FEATURES = [
|
|
|
69985
70058
|
defaultProvider: "ElevenLabs (paid, premium quality) \u2014 falls back to local Kokoro when no key (free, since v0.54)",
|
|
69986
70059
|
alsoAvailable: "Replicate MusicGen (music only)",
|
|
69987
70060
|
keys: [{ configKey: "elevenlabs", envVar: "ELEVENLABS_API_KEY", name: "ElevenLabs", url: "https://elevenlabs.io/app/settings/api-keys", what: "Text-to-speech, sound effects, music, voice cloning (skip to use local Kokoro)" }],
|
|
70061
|
+
providerChoices: [
|
|
70062
|
+
{
|
|
70063
|
+
label: "ElevenLabs",
|
|
70064
|
+
desc: "premium TTS, SFX, music, voice clone",
|
|
70065
|
+
key: { configKey: "elevenlabs", envVar: "ELEVENLABS_API_KEY", name: "ElevenLabs", url: "https://elevenlabs.io/app/settings/api-keys", what: "Text-to-speech, sound effects, music, voice cloning" }
|
|
70066
|
+
},
|
|
70067
|
+
{
|
|
70068
|
+
label: "Kokoro local",
|
|
70069
|
+
desc: "free local TTS fallback, no API key",
|
|
70070
|
+
keyless: true
|
|
70071
|
+
},
|
|
70072
|
+
{
|
|
70073
|
+
label: "Replicate MusicGen",
|
|
70074
|
+
desc: "background music generation",
|
|
70075
|
+
key: { configKey: "replicate", envVar: "REPLICATE_API_TOKEN", name: "Replicate", url: "https://replicate.com/account/api-tokens", what: "MusicGen background music" }
|
|
70076
|
+
}
|
|
70077
|
+
],
|
|
69988
70078
|
tryCommand: 'vibe generate speech "Hello world" -o hello.mp3'
|
|
69989
70079
|
},
|
|
69990
70080
|
{
|
|
@@ -69999,6 +70089,19 @@ var AI_FEATURES = [
|
|
|
69999
70089
|
tryCommand: "vibe edit caption video.mp4 -o captioned.mp4"
|
|
70000
70090
|
}
|
|
70001
70091
|
];
|
|
70092
|
+
function addUniqueKey(keys, keyDef) {
|
|
70093
|
+
if (!keys.some((k) => k.configKey === keyDef.configKey)) {
|
|
70094
|
+
keys.push(keyDef);
|
|
70095
|
+
}
|
|
70096
|
+
}
|
|
70097
|
+
function applyProviderDefault(config2, choice) {
|
|
70098
|
+
if (!choice.defaultFor) return;
|
|
70099
|
+
if (choice.defaultFor.kind === "image") {
|
|
70100
|
+
config2.defaults.imageProvider = choice.defaultFor.value;
|
|
70101
|
+
} else {
|
|
70102
|
+
config2.defaults.videoProvider = choice.defaultFor.value;
|
|
70103
|
+
}
|
|
70104
|
+
}
|
|
70002
70105
|
async function runNonInteractiveSetup(opts) {
|
|
70003
70106
|
const scope = opts.scope ?? "user";
|
|
70004
70107
|
let config2 = await loadConfig({ scope });
|
|
@@ -70140,6 +70243,7 @@ async function runSetupWizard(fullSetup = false, scope = "user") {
|
|
|
70140
70243
|
console.log(source_default.dim("2. Features"));
|
|
70141
70244
|
console.log(source_default.bold("Which AI features do you need?"));
|
|
70142
70245
|
console.log(source_default.dim(" \u2191\u2193 navigate \xB7 space to toggle \xB7 enter to confirm"));
|
|
70246
|
+
console.log(source_default.dim(" Tip: pressing enter on the highlighted item selects it if nothing is checked yet."));
|
|
70143
70247
|
console.log();
|
|
70144
70248
|
const featureLabels = AI_FEATURES.map((f) => {
|
|
70145
70249
|
const keyCount = f.keys.length;
|
|
@@ -70148,7 +70252,9 @@ async function runSetupWizard(fullSetup = false, scope = "user") {
|
|
|
70148
70252
|
});
|
|
70149
70253
|
const picked = await promptMultiSelect(
|
|
70150
70254
|
source_default.cyan(" Pick (e.g. 1,3 or 'all'): "),
|
|
70151
|
-
featureLabels
|
|
70255
|
+
featureLabels,
|
|
70256
|
+
[],
|
|
70257
|
+
{ enterSelectsFocusedWhenEmpty: true }
|
|
70152
70258
|
);
|
|
70153
70259
|
const selectedFeatures = picked.map((i) => AI_FEATURES[i]);
|
|
70154
70260
|
console.log();
|
|
@@ -70159,53 +70265,107 @@ async function runSetupWizard(fullSetup = false, scope = "user") {
|
|
|
70159
70265
|
await showComplete(config2, "vibe --help", [], scope);
|
|
70160
70266
|
return;
|
|
70161
70267
|
}
|
|
70162
|
-
|
|
70163
|
-
console.log(source_default.
|
|
70164
|
-
console.log(source_default.
|
|
70268
|
+
const plannedKeys = /* @__PURE__ */ new Map();
|
|
70269
|
+
console.log(source_default.dim("3. Providers"));
|
|
70270
|
+
console.log(source_default.bold("Choose providers for each selected feature"));
|
|
70271
|
+
console.log(source_default.dim(" Recommended defaults are pre-selected. You can toggle more than one."));
|
|
70165
70272
|
console.log();
|
|
70166
|
-
const collectedKeys = /* @__PURE__ */ new Set();
|
|
70167
70273
|
for (const feature of selectedFeatures) {
|
|
70168
|
-
|
|
70169
|
-
|
|
70170
|
-
|
|
70171
|
-
console.log(source_default.dim(`
|
|
70274
|
+
const keysForFeature = [];
|
|
70275
|
+
if (feature.providerChoices && feature.providerChoices.length > 0) {
|
|
70276
|
+
console.log(source_default.bold.cyan(` ${feature.label}`));
|
|
70277
|
+
console.log(source_default.dim(` Default path: ${feature.defaultProvider}`));
|
|
70278
|
+
if (feature.alsoAvailable) {
|
|
70279
|
+
console.log(source_default.dim(` Alternatives: ${feature.alsoAvailable}`));
|
|
70280
|
+
}
|
|
70281
|
+
console.log();
|
|
70282
|
+
const defaultSelected = feature.providerChoices.map((_, i) => i === 0);
|
|
70283
|
+
const providerLabels = feature.providerChoices.map((choice) => {
|
|
70284
|
+
const keyHint = choice.keyless ? source_default.dim("no key") : choice.key ? source_default.dim(choice.key.envVar) : source_default.dim("no key");
|
|
70285
|
+
return `${choice.label} ${source_default.dim(`- ${choice.desc}`)} ${keyHint}`;
|
|
70286
|
+
});
|
|
70287
|
+
const pickedProviders = await promptMultiSelect(
|
|
70288
|
+
source_default.cyan(" Providers (space toggles, enter confirms): "),
|
|
70289
|
+
providerLabels,
|
|
70290
|
+
defaultSelected
|
|
70291
|
+
);
|
|
70292
|
+
const selectedChoices = pickedProviders.map((i) => feature.providerChoices[i]);
|
|
70293
|
+
const defaultChoice = selectedChoices.find((choice) => choice.defaultFor);
|
|
70294
|
+
if (defaultChoice) {
|
|
70295
|
+
applyProviderDefault(config2, defaultChoice);
|
|
70296
|
+
}
|
|
70297
|
+
for (const choice of selectedChoices) {
|
|
70298
|
+
if (choice.key) addUniqueKey(keysForFeature, choice.key);
|
|
70299
|
+
}
|
|
70300
|
+
if (selectedChoices.some((choice) => choice.keyless)) {
|
|
70301
|
+
console.log(source_default.dim(" No API key needed for selected local provider(s)."));
|
|
70302
|
+
}
|
|
70303
|
+
console.log();
|
|
70304
|
+
} else {
|
|
70305
|
+
for (const keyDef of feature.keys) {
|
|
70306
|
+
addUniqueKey(keysForFeature, keyDef);
|
|
70307
|
+
}
|
|
70172
70308
|
}
|
|
70309
|
+
plannedKeys.set(feature.label, keysForFeature);
|
|
70310
|
+
}
|
|
70311
|
+
const needsKeys = [...plannedKeys.values()].some((keys) => keys.length > 0);
|
|
70312
|
+
if (needsKeys) {
|
|
70313
|
+
console.log(source_default.dim("4. API keys"));
|
|
70314
|
+
console.log(source_default.bold("API Keys"));
|
|
70315
|
+
console.log(source_default.dim(" Saved locally, never shared. Press Enter to skip."));
|
|
70173
70316
|
console.log();
|
|
70174
|
-
|
|
70175
|
-
|
|
70176
|
-
|
|
70317
|
+
const collectedKeys = /* @__PURE__ */ new Set();
|
|
70318
|
+
for (const feature of selectedFeatures) {
|
|
70319
|
+
const keysForFeature = plannedKeys.get(feature.label) ?? [];
|
|
70320
|
+
if (keysForFeature.length === 0) {
|
|
70177
70321
|
continue;
|
|
70178
70322
|
}
|
|
70179
|
-
|
|
70180
|
-
|
|
70181
|
-
|
|
70182
|
-
|
|
70183
|
-
const value = configValue || envValue;
|
|
70184
|
-
const source = configValue ? "config" : ".env";
|
|
70185
|
-
console.log(` ${source_default.green("\u2713")} ${keyDef.name.padEnd(14)} ${maskApiKey(value)} ${source_default.dim(`(${source})`)}`);
|
|
70186
|
-
collectedKeys.add(keyDef.configKey);
|
|
70187
|
-
continue;
|
|
70323
|
+
console.log(source_default.bold.cyan(` ${feature.label}`));
|
|
70324
|
+
console.log(source_default.dim(` Default: ${feature.defaultProvider}`));
|
|
70325
|
+
if (feature.alsoAvailable) {
|
|
70326
|
+
console.log(source_default.dim(` Also available: ${feature.alsoAvailable}`));
|
|
70188
70327
|
}
|
|
70189
|
-
console.log(
|
|
70190
|
-
|
|
70191
|
-
|
|
70192
|
-
|
|
70193
|
-
|
|
70194
|
-
|
|
70195
|
-
|
|
70196
|
-
const
|
|
70197
|
-
|
|
70198
|
-
|
|
70199
|
-
|
|
70200
|
-
|
|
70328
|
+
console.log();
|
|
70329
|
+
for (const keyDef of keysForFeature) {
|
|
70330
|
+
if (collectedKeys.has(keyDef.configKey)) {
|
|
70331
|
+
console.log(` ${source_default.green("\u2713")} ${keyDef.name.padEnd(14)} (already set above)`);
|
|
70332
|
+
continue;
|
|
70333
|
+
}
|
|
70334
|
+
loadEnv();
|
|
70335
|
+
const configValue = config2.providers[keyDef.configKey];
|
|
70336
|
+
const envValue = process.env[keyDef.envVar];
|
|
70337
|
+
if (configValue || envValue) {
|
|
70338
|
+
const value = configValue || envValue;
|
|
70339
|
+
const source = configValue ? "config" : ".env";
|
|
70340
|
+
console.log(` ${source_default.green("\u2713")} ${keyDef.name.padEnd(14)} ${maskApiKey(value)} ${source_default.dim(`(${source})`)}`);
|
|
70341
|
+
collectedKeys.add(keyDef.configKey);
|
|
70342
|
+
continue;
|
|
70343
|
+
}
|
|
70344
|
+
console.log(source_default.dim(` ${keyDef.what}`));
|
|
70345
|
+
console.log(source_default.dim(` Get key: ${keyDef.url} [o] open in browser`));
|
|
70346
|
+
const newKey = await promptHidden(
|
|
70347
|
+
source_default.cyan(` ${keyDef.name.padEnd(14)} ${source_default.dim(keyDef.envVar)}: `),
|
|
70348
|
+
{ openHotkeyUrl: keyDef.url }
|
|
70349
|
+
);
|
|
70350
|
+
if (newKey.trim()) {
|
|
70351
|
+
const trimmed = newKey.trim();
|
|
70352
|
+
config2.providers[keyDef.configKey] = trimmed;
|
|
70353
|
+
const fmt = validateKeyFormat(keyDef.configKey, trimmed);
|
|
70354
|
+
if (!fmt.ok && fmt.expected) {
|
|
70355
|
+
console.log(` ${source_default.yellow("\u26A0")} Saved, but format looks unusual ${source_default.dim(`(expected ${fmt.expected})`)}`);
|
|
70356
|
+
} else {
|
|
70357
|
+
console.log(` ${source_default.green("\u2713")} Saved`);
|
|
70358
|
+
}
|
|
70359
|
+
collectedKeys.add(keyDef.configKey);
|
|
70201
70360
|
} else {
|
|
70202
|
-
console.log(` ${source_default.
|
|
70361
|
+
console.log(` ${source_default.yellow("\u26A0")} Skipped ${source_default.dim(`(set ${keyDef.envVar} in .env later)`)}`);
|
|
70203
70362
|
}
|
|
70204
|
-
collectedKeys.add(keyDef.configKey);
|
|
70205
|
-
} else {
|
|
70206
|
-
console.log(` ${source_default.yellow("\u26A0")} Skipped ${source_default.dim(`(set ${keyDef.envVar} in .env later)`)}`);
|
|
70207
70363
|
}
|
|
70364
|
+
console.log();
|
|
70208
70365
|
}
|
|
70366
|
+
} else {
|
|
70367
|
+
console.log(source_default.dim("4. API keys"));
|
|
70368
|
+
console.log(source_default.dim(" No API keys needed for the selected provider(s)."));
|
|
70209
70369
|
console.log();
|
|
70210
70370
|
}
|
|
70211
70371
|
await saveConfig(config2, { scope });
|
|
@@ -71795,11 +71955,16 @@ function printReport(results, opts = { verbose: false }) {
|
|
|
71795
71955
|
console.log(source_default.bold(" API Keys"));
|
|
71796
71956
|
const configured = [];
|
|
71797
71957
|
const missing = [];
|
|
71958
|
+
const labelFor = (configKey, fallbackEnv) => {
|
|
71959
|
+
const label = getDisplayLabelForApiKey(configKey);
|
|
71960
|
+
return label === configKey ? fallbackEnv : label;
|
|
71961
|
+
};
|
|
71798
71962
|
for (const [name, info] of Object.entries(results.providers)) {
|
|
71799
71963
|
if (info.configured) {
|
|
71800
71964
|
configured.push(name);
|
|
71965
|
+
const label = labelFor(name, info.envVar);
|
|
71801
71966
|
console.log(
|
|
71802
|
-
` ${source_default.green("OK")} ${info.envVar.padEnd(
|
|
71967
|
+
` ${source_default.green("OK")} ${label.padEnd(28)} ${source_default.dim(info.envVar.padEnd(20))} ${source_default.dim(info.commands.join(", "))}`
|
|
71803
71968
|
);
|
|
71804
71969
|
} else {
|
|
71805
71970
|
missing.push(name);
|
|
@@ -71809,8 +71974,9 @@ function printReport(results, opts = { verbose: false }) {
|
|
|
71809
71974
|
if (verbose) {
|
|
71810
71975
|
for (const name of missing) {
|
|
71811
71976
|
const info = results.providers[name];
|
|
71977
|
+
const label = labelFor(name, info.envVar);
|
|
71812
71978
|
console.log(
|
|
71813
|
-
` ${source_default.red("--")} ${info.envVar.padEnd(
|
|
71979
|
+
` ${source_default.red("--")} ${label.padEnd(28)} ${source_default.dim(info.envVar.padEnd(20))} ${source_default.dim(info.commands.join(", "))}`
|
|
71814
71980
|
);
|
|
71815
71981
|
}
|
|
71816
71982
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeframe/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.91.1",
|
|
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.91.1",
|
|
187
|
+
"@vibeframe/core": "0.91.1"
|
|
188
188
|
},
|
|
189
189
|
"engines": {
|
|
190
190
|
"node": ">=20"
|