@vibeframe/cli 0.91.0 → 0.91.2

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.
Files changed (2) hide show
  1. package/dist/index.js +199 -45
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -31895,7 +31895,7 @@ async function promptConfirm(question, defaultYes = true) {
31895
31895
  if (answer === "") return defaultYes;
31896
31896
  return answer.toLowerCase().startsWith("y");
31897
31897
  }
31898
- async function promptMultiSelect(_question, options, defaultSelected = []) {
31898
+ async function promptMultiSelect(_question, options, defaultSelected = [], opts = {}) {
31899
31899
  const input = getTTYInputStream();
31900
31900
  const selected = options.map((_, i) => Boolean(defaultSelected[i]));
31901
31901
  if (typeof input.setRawMode === "function") {
@@ -31931,6 +31931,9 @@ async function promptMultiSelect(_question, options, defaultSelected = []) {
31931
31931
  };
31932
31932
  const onData = (char) => {
31933
31933
  if (char === "\r" || char === "\n") {
31934
+ if (opts.enterSelectsFocusedWhenEmpty && selected.every((v) => !v)) {
31935
+ selected[cursor] = true;
31936
+ }
31934
31937
  finish();
31935
31938
  } else if (char === "") {
31936
31939
  input.setRawMode(false);
@@ -51607,7 +51610,7 @@ var init_key_live_test = __esm({
51607
51610
 
51608
51611
  // src/pipeline/renderers/chrome.ts
51609
51612
  import { existsSync as existsSync32 } from "node:fs";
51610
- import { homedir as homedir5 } from "node:os";
51613
+ import { homedir as homedir6 } from "node:os";
51611
51614
  import { join as join11 } from "node:path";
51612
51615
  function findChrome() {
51613
51616
  const candidates = [
@@ -51615,7 +51618,7 @@ function findChrome() {
51615
51618
  process.env.CHROME_PATH,
51616
51619
  // puppeteer auto-downloaded headless shell
51617
51620
  join11(
51618
- homedir5(),
51621
+ homedir6(),
51619
51622
  ".cache",
51620
51623
  "puppeteer",
51621
51624
  "chrome-headless-shell",
@@ -62910,7 +62913,7 @@ init_source();
62910
62913
  // package.json
62911
62914
  var package_default = {
62912
62915
  name: "@vibeframe/cli",
62913
- version: "0.91.0",
62916
+ version: "0.91.2",
62914
62917
  description: "VibeFrame CLI - natural language video editing from the terminal",
62915
62918
  private: false,
62916
62919
  license: "MIT",
@@ -69837,6 +69840,7 @@ init_tty();
69837
69840
  init_api_key();
69838
69841
  import { resolve as resolve42 } from "node:path";
69839
69842
  import { access as access6, readFile as readFile29 } from "node:fs/promises";
69843
+ import { homedir as homedir5 } from "node:os";
69840
69844
 
69841
69845
  // src/utils/key-format.ts
69842
69846
  init_dist2();
@@ -69987,6 +69991,26 @@ var AI_FEATURES = [
69987
69991
  defaultProvider: "OpenAI gpt-image-2 (Artificial Analysis #1, since v0.56)",
69988
69992
  alsoAvailable: "Gemini Nano Banana, Grok Imagine",
69989
69993
  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)" }],
69994
+ providerChoices: [
69995
+ {
69996
+ label: "OpenAI gpt-image-2",
69997
+ desc: "recommended default, image generation + editing",
69998
+ 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)" },
69999
+ defaultFor: { kind: "image", value: "openai" }
70000
+ },
70001
+ {
70002
+ label: "Gemini",
70003
+ desc: "Google image generation + multimodal analysis",
70004
+ key: { configKey: "google", envVar: "GOOGLE_API_KEY", name: "Google", url: "https://aistudio.google.com/apikey", what: "Gemini image generation and multimodal analysis" },
70005
+ defaultFor: { kind: "image", value: "gemini" }
70006
+ },
70007
+ {
70008
+ label: "Grok",
70009
+ desc: "xAI image generation",
70010
+ key: { configKey: "xai", envVar: "XAI_API_KEY", name: "xAI", url: "https://console.x.ai/", what: "Grok image generation and Agent" },
70011
+ defaultFor: { kind: "image", value: "grok" }
70012
+ }
70013
+ ],
69990
70014
  tryCommand: 'vibe generate image "a sunset over mountains" -o test.png'
69991
70015
  },
69992
70016
  {
@@ -69995,6 +70019,38 @@ var AI_FEATURES = [
69995
70019
  defaultProvider: "Seedance 2.0 via fal.ai (Artificial Analysis #2 t2v + i2v, since v0.57)",
69996
70020
  alsoAvailable: "Grok Imagine, Kling, Runway Gen-4.5, Google Veo",
69997
70021
  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" }],
70022
+ providerChoices: [
70023
+ {
70024
+ label: "Seedance 2.0 via fal.ai",
70025
+ desc: "recommended default, text-to-video + image-to-video",
70026
+ 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" },
70027
+ defaultFor: { kind: "video", value: "seedance" }
70028
+ },
70029
+ {
70030
+ label: "Grok",
70031
+ desc: "xAI video generation with audio",
70032
+ key: { configKey: "xai", envVar: "XAI_API_KEY", name: "xAI", url: "https://console.x.ai/", what: "Grok video generation and Agent" },
70033
+ defaultFor: { kind: "video", value: "grok" }
70034
+ },
70035
+ {
70036
+ label: "Kling",
70037
+ desc: "Kling v2.5/v3 video generation",
70038
+ key: { configKey: "kling", envVar: "KLING_API_KEY", name: "Kling", url: "https://platform.klingai.com/", what: "Kling video generation" },
70039
+ defaultFor: { kind: "video", value: "kling" }
70040
+ },
70041
+ {
70042
+ label: "Runway",
70043
+ desc: "Runway Gen-4.5 video generation",
70044
+ key: { configKey: "runway", envVar: "RUNWAY_API_SECRET", name: "Runway", url: "https://app.runwayml.com/settings/api-keys", what: "Runway Gen-4.5 video generation" },
70045
+ defaultFor: { kind: "video", value: "runway" }
70046
+ },
70047
+ {
70048
+ label: "Veo",
70049
+ desc: "Google Veo video generation",
70050
+ key: { configKey: "google", envVar: "GOOGLE_API_KEY", name: "Google", url: "https://aistudio.google.com/apikey", what: "Google Veo video generation" },
70051
+ defaultFor: { kind: "video", value: "veo" }
70052
+ }
70053
+ ],
69998
70054
  tryCommand: 'vibe generate video "ocean waves" -o waves.mp4'
69999
70055
  },
70000
70056
  {
@@ -70003,6 +70059,23 @@ var AI_FEATURES = [
70003
70059
  defaultProvider: "ElevenLabs (paid, premium quality) \u2014 falls back to local Kokoro when no key (free, since v0.54)",
70004
70060
  alsoAvailable: "Replicate MusicGen (music only)",
70005
70061
  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)" }],
70062
+ providerChoices: [
70063
+ {
70064
+ label: "ElevenLabs",
70065
+ desc: "premium TTS, SFX, music, voice clone",
70066
+ 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" }
70067
+ },
70068
+ {
70069
+ label: "Kokoro local",
70070
+ desc: "free local TTS fallback, no API key",
70071
+ keyless: true
70072
+ },
70073
+ {
70074
+ label: "Replicate MusicGen",
70075
+ desc: "background music generation",
70076
+ key: { configKey: "replicate", envVar: "REPLICATE_API_TOKEN", name: "Replicate", url: "https://replicate.com/account/api-tokens", what: "MusicGen background music" }
70077
+ }
70078
+ ],
70006
70079
  tryCommand: 'vibe generate speech "Hello world" -o hello.mp3'
70007
70080
  },
70008
70081
  {
@@ -70017,6 +70090,19 @@ var AI_FEATURES = [
70017
70090
  tryCommand: "vibe edit caption video.mp4 -o captioned.mp4"
70018
70091
  }
70019
70092
  ];
70093
+ function addUniqueKey(keys, keyDef) {
70094
+ if (!keys.some((k) => k.configKey === keyDef.configKey)) {
70095
+ keys.push(keyDef);
70096
+ }
70097
+ }
70098
+ function applyProviderDefault(config2, choice) {
70099
+ if (!choice.defaultFor) return;
70100
+ if (choice.defaultFor.kind === "image") {
70101
+ config2.defaults.imageProvider = choice.defaultFor.value;
70102
+ } else {
70103
+ config2.defaults.videoProvider = choice.defaultFor.value;
70104
+ }
70105
+ }
70020
70106
  async function runNonInteractiveSetup(opts) {
70021
70107
  const scope = opts.scope ?? "user";
70022
70108
  let config2 = await loadConfig({ scope });
@@ -70103,8 +70189,19 @@ async function runNonInteractiveSetup(opts) {
70103
70189
  }
70104
70190
  async function runSetupWizard(fullSetup = false, scope = "user") {
70105
70191
  console.log();
70106
- const scopeLabel = scope === "project" ? source_default.dim(` \u2014 project scope (${getConfigPath("project")})`) : source_default.dim(" \u2014 user scope");
70107
- console.log(source_default.bold.magenta("VibeFrame Setup") + scopeLabel);
70192
+ const cfgPath = getConfigPath(scope).replace(homedir5(), "~");
70193
+ const scopeNote = scope === "project" ? "applies to this project only \xB7 add .vibeframe/ to .gitignore" : "applies to every project for this user";
70194
+ console.log(
70195
+ source_default.bold.magenta("VibeFrame Setup") + source_default.bold(` \u2014 ${scope} scope`)
70196
+ );
70197
+ console.log(source_default.dim(`${cfgPath} \xB7 ${scopeNote}`));
70198
+ if (scope === "user") {
70199
+ console.log(
70200
+ source_default.dim(
70201
+ "Tip: for per-project keys, cd into a project and run: vibe setup --scope project"
70202
+ )
70203
+ );
70204
+ }
70108
70205
  console.log(source_default.dim("\u2500".repeat(40)));
70109
70206
  console.log();
70110
70207
  const hosts = detectedAgentHosts();
@@ -70158,6 +70255,7 @@ async function runSetupWizard(fullSetup = false, scope = "user") {
70158
70255
  console.log(source_default.dim("2. Features"));
70159
70256
  console.log(source_default.bold("Which AI features do you need?"));
70160
70257
  console.log(source_default.dim(" \u2191\u2193 navigate \xB7 space to toggle \xB7 enter to confirm"));
70258
+ console.log(source_default.dim(" Tip: pressing enter on the highlighted item selects it if nothing is checked yet."));
70161
70259
  console.log();
70162
70260
  const featureLabels = AI_FEATURES.map((f) => {
70163
70261
  const keyCount = f.keys.length;
@@ -70166,7 +70264,9 @@ async function runSetupWizard(fullSetup = false, scope = "user") {
70166
70264
  });
70167
70265
  const picked = await promptMultiSelect(
70168
70266
  source_default.cyan(" Pick (e.g. 1,3 or 'all'): "),
70169
- featureLabels
70267
+ featureLabels,
70268
+ [],
70269
+ { enterSelectsFocusedWhenEmpty: true }
70170
70270
  );
70171
70271
  const selectedFeatures = picked.map((i) => AI_FEATURES[i]);
70172
70272
  console.log();
@@ -70177,53 +70277,107 @@ async function runSetupWizard(fullSetup = false, scope = "user") {
70177
70277
  await showComplete(config2, "vibe --help", [], scope);
70178
70278
  return;
70179
70279
  }
70180
- console.log(source_default.dim("3. API keys"));
70181
- console.log(source_default.bold("API Keys"));
70182
- console.log(source_default.dim(" Saved locally, never shared. Press Enter to skip."));
70280
+ const plannedKeys = /* @__PURE__ */ new Map();
70281
+ console.log(source_default.dim("3. Providers"));
70282
+ console.log(source_default.bold("Choose providers for each selected feature"));
70283
+ console.log(source_default.dim(" Recommended defaults are pre-selected. You can toggle more than one."));
70183
70284
  console.log();
70184
- const collectedKeys = /* @__PURE__ */ new Set();
70185
70285
  for (const feature of selectedFeatures) {
70186
- console.log(source_default.bold.cyan(` ${feature.label}`));
70187
- console.log(source_default.dim(` Default: ${feature.defaultProvider}`));
70188
- if (feature.alsoAvailable) {
70189
- console.log(source_default.dim(` Also available: ${feature.alsoAvailable}`));
70286
+ const keysForFeature = [];
70287
+ if (feature.providerChoices && feature.providerChoices.length > 0) {
70288
+ console.log(source_default.bold.cyan(` ${feature.label}`));
70289
+ console.log(source_default.dim(` Default path: ${feature.defaultProvider}`));
70290
+ if (feature.alsoAvailable) {
70291
+ console.log(source_default.dim(` Alternatives: ${feature.alsoAvailable}`));
70292
+ }
70293
+ console.log();
70294
+ const defaultSelected = feature.providerChoices.map((_, i) => i === 0);
70295
+ const providerLabels = feature.providerChoices.map((choice) => {
70296
+ const keyHint = choice.keyless ? source_default.dim("no key") : choice.key ? source_default.dim(choice.key.envVar) : source_default.dim("no key");
70297
+ return `${choice.label} ${source_default.dim(`- ${choice.desc}`)} ${keyHint}`;
70298
+ });
70299
+ const pickedProviders = await promptMultiSelect(
70300
+ source_default.cyan(" Providers (space toggles, enter confirms): "),
70301
+ providerLabels,
70302
+ defaultSelected
70303
+ );
70304
+ const selectedChoices = pickedProviders.map((i) => feature.providerChoices[i]);
70305
+ const defaultChoice = selectedChoices.find((choice) => choice.defaultFor);
70306
+ if (defaultChoice) {
70307
+ applyProviderDefault(config2, defaultChoice);
70308
+ }
70309
+ for (const choice of selectedChoices) {
70310
+ if (choice.key) addUniqueKey(keysForFeature, choice.key);
70311
+ }
70312
+ if (selectedChoices.some((choice) => choice.keyless)) {
70313
+ console.log(source_default.dim(" No API key needed for selected local provider(s)."));
70314
+ }
70315
+ console.log();
70316
+ } else {
70317
+ for (const keyDef of feature.keys) {
70318
+ addUniqueKey(keysForFeature, keyDef);
70319
+ }
70190
70320
  }
70321
+ plannedKeys.set(feature.label, keysForFeature);
70322
+ }
70323
+ const needsKeys = [...plannedKeys.values()].some((keys) => keys.length > 0);
70324
+ if (needsKeys) {
70325
+ console.log(source_default.dim("4. API keys"));
70326
+ console.log(source_default.bold("API Keys"));
70327
+ console.log(source_default.dim(" Saved locally, never shared. Press Enter to skip."));
70191
70328
  console.log();
70192
- for (const keyDef of feature.keys) {
70193
- if (collectedKeys.has(keyDef.configKey)) {
70194
- console.log(` ${source_default.green("\u2713")} ${keyDef.name.padEnd(14)} (already set above)`);
70329
+ const collectedKeys = /* @__PURE__ */ new Set();
70330
+ for (const feature of selectedFeatures) {
70331
+ const keysForFeature = plannedKeys.get(feature.label) ?? [];
70332
+ if (keysForFeature.length === 0) {
70195
70333
  continue;
70196
70334
  }
70197
- loadEnv();
70198
- const configValue = config2.providers[keyDef.configKey];
70199
- const envValue = process.env[keyDef.envVar];
70200
- if (configValue || envValue) {
70201
- const value = configValue || envValue;
70202
- const source = configValue ? "config" : ".env";
70203
- console.log(` ${source_default.green("\u2713")} ${keyDef.name.padEnd(14)} ${maskApiKey(value)} ${source_default.dim(`(${source})`)}`);
70204
- collectedKeys.add(keyDef.configKey);
70205
- continue;
70335
+ console.log(source_default.bold.cyan(` ${feature.label}`));
70336
+ console.log(source_default.dim(` Default: ${feature.defaultProvider}`));
70337
+ if (feature.alsoAvailable) {
70338
+ console.log(source_default.dim(` Also available: ${feature.alsoAvailable}`));
70206
70339
  }
70207
- console.log(source_default.dim(` ${keyDef.what}`));
70208
- console.log(source_default.dim(` Get key: ${keyDef.url} [o] open in browser`));
70209
- const newKey = await promptHidden(
70210
- source_default.cyan(` ${keyDef.name.padEnd(14)} ${source_default.dim(keyDef.envVar)}: `),
70211
- { openHotkeyUrl: keyDef.url }
70212
- );
70213
- if (newKey.trim()) {
70214
- const trimmed = newKey.trim();
70215
- config2.providers[keyDef.configKey] = trimmed;
70216
- const fmt = validateKeyFormat(keyDef.configKey, trimmed);
70217
- if (!fmt.ok && fmt.expected) {
70218
- console.log(` ${source_default.yellow("\u26A0")} Saved, but format looks unusual ${source_default.dim(`(expected ${fmt.expected})`)}`);
70340
+ console.log();
70341
+ for (const keyDef of keysForFeature) {
70342
+ if (collectedKeys.has(keyDef.configKey)) {
70343
+ console.log(` ${source_default.green("\u2713")} ${keyDef.name.padEnd(14)} (already set above)`);
70344
+ continue;
70345
+ }
70346
+ loadEnv();
70347
+ const configValue = config2.providers[keyDef.configKey];
70348
+ const envValue = process.env[keyDef.envVar];
70349
+ if (configValue || envValue) {
70350
+ const value = configValue || envValue;
70351
+ const source = configValue ? "config" : ".env";
70352
+ console.log(` ${source_default.green("\u2713")} ${keyDef.name.padEnd(14)} ${maskApiKey(value)} ${source_default.dim(`(${source})`)}`);
70353
+ collectedKeys.add(keyDef.configKey);
70354
+ continue;
70355
+ }
70356
+ console.log(source_default.dim(` ${keyDef.what}`));
70357
+ console.log(source_default.dim(` Get key: ${keyDef.url} [o] open in browser`));
70358
+ const newKey = await promptHidden(
70359
+ source_default.cyan(` ${keyDef.name.padEnd(14)} ${source_default.dim(keyDef.envVar)}: `),
70360
+ { openHotkeyUrl: keyDef.url }
70361
+ );
70362
+ if (newKey.trim()) {
70363
+ const trimmed = newKey.trim();
70364
+ config2.providers[keyDef.configKey] = trimmed;
70365
+ const fmt = validateKeyFormat(keyDef.configKey, trimmed);
70366
+ if (!fmt.ok && fmt.expected) {
70367
+ console.log(` ${source_default.yellow("\u26A0")} Saved, but format looks unusual ${source_default.dim(`(expected ${fmt.expected})`)}`);
70368
+ } else {
70369
+ console.log(` ${source_default.green("\u2713")} Saved`);
70370
+ }
70371
+ collectedKeys.add(keyDef.configKey);
70219
70372
  } else {
70220
- console.log(` ${source_default.green("\u2713")} Saved`);
70373
+ console.log(` ${source_default.yellow("\u26A0")} Skipped ${source_default.dim(`(set ${keyDef.envVar} in .env later)`)}`);
70221
70374
  }
70222
- collectedKeys.add(keyDef.configKey);
70223
- } else {
70224
- console.log(` ${source_default.yellow("\u26A0")} Skipped ${source_default.dim(`(set ${keyDef.envVar} in .env later)`)}`);
70225
70375
  }
70376
+ console.log();
70226
70377
  }
70378
+ } else {
70379
+ console.log(source_default.dim("4. API keys"));
70380
+ console.log(source_default.dim(" No API keys needed for the selected provider(s)."));
70227
70381
  console.log();
70228
70382
  }
70229
70383
  await saveConfig(config2, { scope });
@@ -71605,12 +71759,12 @@ async function runDiagnostics() {
71605
71759
  }
71606
71760
  } else if (toolName === "chrome") {
71607
71761
  const { existsSync: existsSync41 } = await import("node:fs");
71608
- const { homedir: homedir6 } = await import("node:os");
71762
+ const { homedir: homedir7 } = await import("node:os");
71609
71763
  const { join: join17 } = await import("node:path");
71610
71764
  const chromePaths = [
71611
71765
  process.env.HYPERFRAMES_CHROME_PATH,
71612
71766
  process.env.CHROME_PATH,
71613
- join17(homedir6(), ".cache", "puppeteer", "chrome-headless-shell", "mac_arm-147.0.7727.56", "chrome-headless-shell-mac_arm", "chrome-headless-shell"),
71767
+ join17(homedir7(), ".cache", "puppeteer", "chrome-headless-shell", "mac_arm-147.0.7727.56", "chrome-headless-shell-mac_arm", "chrome-headless-shell"),
71614
71768
  "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
71615
71769
  "/Applications/Chromium.app/Contents/MacOS/Chromium",
71616
71770
  "/usr/bin/google-chrome",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/cli",
3
- "version": "0.91.0",
3
+ "version": "0.91.2",
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.91.0",
187
- "@vibeframe/core": "0.91.0"
186
+ "@vibeframe/ai-providers": "0.91.2",
187
+ "@vibeframe/core": "0.91.2"
188
188
  },
189
189
  "engines": {
190
190
  "node": ">=20"