@vibeframe/cli 0.115.3 → 0.115.5

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 CHANGED
@@ -6629,8 +6629,43 @@ function outputSuccess(opts) {
6629
6629
  const data = opts.data;
6630
6630
  const primary = data.outputPath ?? data.output ?? data.path ?? data.url ?? data.id;
6631
6631
  if (primary !== void 0) console.log(String(primary));
6632
+ return;
6633
+ }
6634
+ if (opts.dryRun && !opts.humanRendered) {
6635
+ printDryRunSummary(opts.command, costUsd, opts.data, opts.warnings ?? []);
6636
+ }
6637
+ }
6638
+ function printWarnings(warnings) {
6639
+ if (isJsonMode()) return;
6640
+ for (const warning of warnings) {
6641
+ process.stderr.write(`${source_default.yellow(`Warning: ${warning}`)}
6642
+ `);
6632
6643
  }
6633
6644
  }
6645
+ function formatDryRunValue(value) {
6646
+ const text = Array.isArray(value) ? value.map((v) => formatDryRunValue(v)).join(", ") : typeof value === "object" ? JSON.stringify(value) : String(value);
6647
+ return text.length > 80 ? `${text.slice(0, 77)}...` : text;
6648
+ }
6649
+ function printDryRunSummary(command, costUsd, data, warnings) {
6650
+ const params = data.params && typeof data.params === "object" ? data.params : data;
6651
+ const rows = [
6652
+ ["cost", costUsd > 0 ? `up to $${costUsd.toFixed(2)} (estimate)` : "free"]
6653
+ ];
6654
+ for (const [key, value] of Object.entries(params)) {
6655
+ if (value === void 0 || value === null || value === "") continue;
6656
+ if (Array.isArray(value) && value.length === 0) continue;
6657
+ rows.push([key, formatDryRunValue(value)]);
6658
+ }
6659
+ const width = Math.max(...rows.map(([key]) => key.length));
6660
+ console.log();
6661
+ console.log(`${source_default.bold.cyan(`Dry run: vibe ${command}`)} ${source_default.dim("(nothing was sent)")}`);
6662
+ for (const [key, value] of rows) {
6663
+ console.log(` ${source_default.dim(key.padEnd(width))} ${value}`);
6664
+ }
6665
+ printWarnings(warnings);
6666
+ console.log(source_default.dim(" Run again without --dry-run to execute."));
6667
+ console.log();
6668
+ }
6634
6669
  function log(...args) {
6635
6670
  if (!isJsonMode() && !isQuietMode()) {
6636
6671
  console.log(...args);
@@ -6984,8 +7019,8 @@ var init_api_keys = __esm({
6984
7019
  envVar: "GOOGLE_API_KEY",
6985
7020
  label: "Google",
6986
7021
  showInSetup: true,
6987
- setupDescription: "Gemini \u2014 image gen (free tier), video analysis ($), Veo ($$)",
6988
- envExampleComment: "Google API Key (Gemini auto-edit suggestions, image gen, Veo video)",
7022
+ setupDescription: "Gemini \u2014 image gen (free tier), video analysis ($), Omni video ($$)",
7023
+ envExampleComment: "Google API Key (Gemini auto-edit suggestions, image gen, Omni video)",
6989
7024
  envExampleUrl: "https://aistudio.google.com/apikey",
6990
7025
  keyFormat: { prefix: /^AIza/, example: "AIza..." }
6991
7026
  });
@@ -7222,6 +7257,114 @@ var init_whisper = __esm({
7222
7257
  }
7223
7258
  });
7224
7259
 
7260
+ // ../ai-providers/dist/catalog/catalog.js
7261
+ function listModels(filter = {}) {
7262
+ return MODEL_CATALOG.filter((m) => (!filter.provider || m.provider === filter.provider) && (!filter.kind || m.kind === filter.kind));
7263
+ }
7264
+ function defaultModel(provider, kind) {
7265
+ const found = MODEL_CATALOG.find((m) => m.provider === provider && m.kind === kind && m.default);
7266
+ if (!found)
7267
+ throw new Error(`Model catalog has no default ${kind} model for ${provider}`);
7268
+ return found;
7269
+ }
7270
+ function findModel(provider, kind, aliasOrId) {
7271
+ const key = aliasOrId?.trim();
7272
+ if (!key)
7273
+ return defaultModel(provider, kind);
7274
+ const lower = key.toLowerCase();
7275
+ return listModels({ provider, kind }).find((m) => m.id === key || m.aliases?.includes(lower));
7276
+ }
7277
+ function modelLifecycle(spec, now = /* @__PURE__ */ new Date()) {
7278
+ if (!spec.shutdown)
7279
+ return { state: "served" };
7280
+ const remainingMs = Date.parse(`${spec.shutdown}T00:00:00Z`) - now.getTime();
7281
+ const instead = spec.replacement ? ` Use ${spec.replacement} instead.` : "";
7282
+ if (remainingMs <= 0) {
7283
+ return {
7284
+ state: "retired",
7285
+ shutdown: spec.shutdown,
7286
+ message: `${spec.label} (${spec.id}) was shut down by the provider on ${spec.shutdown}.${instead}`
7287
+ };
7288
+ }
7289
+ const daysLeft = Math.ceil(remainingMs / 864e5);
7290
+ return {
7291
+ state: "retiring",
7292
+ shutdown: spec.shutdown,
7293
+ daysLeft,
7294
+ message: `${spec.label} (${spec.id}) shuts down on ${spec.shutdown} (${daysLeft} day${daysLeft === 1 ? "" : "s"} left).${instead}`
7295
+ };
7296
+ }
7297
+ function assertModelServed(spec, now = /* @__PURE__ */ new Date()) {
7298
+ const lifecycle = modelLifecycle(spec, now);
7299
+ if (lifecycle.state === "retired")
7300
+ throw new ModelRetiredError(lifecycle.message, spec);
7301
+ }
7302
+ function modelAliases(provider, kind) {
7303
+ return listModels({ provider, kind }).flatMap((m) => m.aliases ?? []);
7304
+ }
7305
+ var MODEL_CATALOG, ModelRetiredError;
7306
+ var init_catalog = __esm({
7307
+ "../ai-providers/dist/catalog/catalog.js"() {
7308
+ "use strict";
7309
+ MODEL_CATALOG = [
7310
+ // ── LLMs ──────────────────────────────────────────────────────────────
7311
+ { id: "claude-sonnet-5-5", provider: "claude", kind: "llm", label: "Claude Sonnet 5.5", aliases: ["sonnet"], default: true, status: "ga", price: { usd: 2, per: "1M input tokens" }, note: "Agent, storyboard, translate-srt, motion default" },
7312
+ { id: "claude-opus-5-5", provider: "claude", kind: "llm", label: "Claude Opus 5.5", aliases: ["opus"], status: "ga", price: { usd: 4, per: "1M input tokens" } },
7313
+ { id: "claude-opus-4-6", provider: "claude", kind: "llm", label: "Claude Opus 4.6", aliases: ["opus-4-6"], status: "legacy", price: { usd: 5, per: "1M input tokens" } },
7314
+ { id: "gpt-5.4-mini", provider: "openai", kind: "llm", label: "GPT-5.4 mini", default: true, status: "ga", price: { usd: 0.75, per: "1M input tokens" }, note: "Agent and storyboard default (tools on Chat Completions)" },
7315
+ { id: "gemini-3.8-flash", provider: "gemini", kind: "llm", label: "Gemini 3.8 Flash", aliases: ["flash", "latest", "flash-3.8", "gemini"], default: true, status: "ga", price: { usd: 0.75, per: "1M input tokens", basis: "until 2026-12-31, then $1.50" } },
7316
+ { id: "gemini-3.5-flash", provider: "gemini", kind: "llm", label: "Gemini 3.5 Flash", aliases: ["flash-3.5"], status: "ga" },
7317
+ { id: "gemini-3-flash-preview", provider: "gemini", kind: "llm", label: "Gemini 3 Flash (preview)", aliases: ["flash-3"], status: "preview" },
7318
+ { id: "gemini-3.1-pro-preview", provider: "gemini", kind: "llm", label: "Gemini 3.1 Pro (preview)", aliases: ["pro", "pro-3.1", "3.1-pro", "gemini-3.1-pro"], status: "preview" },
7319
+ { id: "gemini-2.5-flash", provider: "gemini", kind: "llm", label: "Gemini 2.5 Flash", aliases: ["flash-2.5"], status: "legacy", note: "New projects cannot use 2.5 models since 2026-09-18" },
7320
+ { id: "gemini-2.5-pro", provider: "gemini", kind: "llm", label: "Gemini 2.5 Pro", aliases: ["pro-2.5", "2.5-pro"], status: "legacy" },
7321
+ { id: "grok-4.3", provider: "grok", kind: "llm", label: "Grok 4.3", default: true, status: "ga", price: { usd: 1.25, per: "1M input tokens" } },
7322
+ { id: "grok-4.7", provider: "grok", kind: "llm", label: "Grok 4.7", status: "ga", price: { usd: 2, per: "1M input tokens" } },
7323
+ // ── Images ────────────────────────────────────────────────────────────
7324
+ { id: "gpt-image-2.5-sunburst", provider: "openai", kind: "image", label: "GPT Image 2.5 Sunburst", aliases: ["2.5", "sunburst"], default: true, status: "ga", price: { usd: 0.211, per: "image", basis: "high, 1024x1024" } },
7325
+ { id: "gpt-image-2.5-flare", provider: "openai", kind: "image", label: "GPT Image 2.5 Flare", aliases: ["flare", "2.5-flare"], status: "ga", price: { usd: 0.211, per: "image", basis: "high, 1024x1024" } },
7326
+ { id: "gpt-image-2", provider: "openai", kind: "image", label: "GPT Image 2", aliases: ["2"], status: "ga", price: { usd: 0.211, per: "image", basis: "high, 1024x1024" } },
7327
+ { id: "gpt-image-1.5", provider: "openai", kind: "image", label: "GPT Image 1.5", aliases: ["1.5"], status: "deprecated", shutdown: "2026-12-01", price: { usd: 0.133, per: "image", basis: "high, 1024x1024" } },
7328
+ { id: "gemini-3.1-flash-image", provider: "gemini", kind: "image", label: "Nano Banana 2", aliases: ["flash", "3.1-flash", "latest"], default: true, status: "ga", price: { usd: 0.067, per: "image", basis: "1K" } },
7329
+ { id: "gemini-3.1-flash-lite-image", provider: "gemini", kind: "image", label: "Nano Banana 2 Lite", aliases: ["lite"], status: "ga", price: { usd: 0.034, per: "image", basis: "1K" } },
7330
+ { id: "gemini-3-pro-image", provider: "gemini", kind: "image", label: "Nano Banana Pro", aliases: ["pro"], status: "ga", price: { usd: 0.134, per: "image", basis: "1K-2K" } },
7331
+ { id: "gemini-2.5-flash-image", provider: "gemini", kind: "image", label: "Nano Banana (2.5 Flash Image)", status: "deprecated", shutdown: "2026-10-02" },
7332
+ { id: "gemini-3.1-flash-image-preview", provider: "gemini", kind: "image", label: "Nano Banana 2 (preview)", status: "deprecated", shutdown: "2026-06-25" },
7333
+ { id: "gemini-3-pro-image-preview", provider: "gemini", kind: "image", label: "Nano Banana Pro (preview)", status: "deprecated", shutdown: "2026-06-25" },
7334
+ { id: "grok-imagine-image", provider: "grok", kind: "image", label: "Grok Imagine Image", default: true, status: "ga", price: { usd: 0.02, per: "image" } },
7335
+ { id: "grok-imagine-image-2.0", provider: "grok", kind: "image", label: "Grok Imagine Image 2.0", aliases: ["pro", "2.0", "quality"], status: "ga", price: { usd: 0.08, per: "image", basis: "medium, 2K" } },
7336
+ // ── Video ─────────────────────────────────────────────────────────────
7337
+ { id: "seedance-2.0", provider: "seedance", kind: "video", label: "Seedance 2.0", aliases: ["quality", "2.0"], default: true, status: "ga", price: { usd: 0.3024, per: "second", basis: "720p 16:9" }, note: "4-15 s, native audio" },
7338
+ { id: "seedance-2.0-fast", provider: "seedance", kind: "video", label: "Seedance 2.0 Fast", aliases: ["fast"], status: "ga", price: { usd: 0.2419, per: "second", basis: "720p 16:9" }, note: "4-15 s, up to 720p, native audio" },
7339
+ { id: "seedance-2.5", provider: "seedance", kind: "video", label: "Seedance 2.5", aliases: ["2.5"], status: "ga", price: { usd: 0.473, per: "second", basis: "720p 16:9" }, note: "Opt-in; 4-30 s, native audio" },
7340
+ { id: "gemini-omni-1.1-flash", provider: "omni", kind: "video", label: "Gemini Omni 1.1 Flash", default: true, status: "ga", price: { usd: 0.1, per: "second", basis: "720p" }, note: "Google video default; the model picks 3-10 s; native audio" },
7341
+ { id: "veo-3.1-generate-preview", provider: "veo", kind: "video", label: "Veo 3.1", aliases: ["3.1"], status: "deprecated", shutdown: "2026-10-22", replacement: "`-p omni` (Gemini Omni 1.1 Flash)", price: { usd: 0.4, per: "second", basis: "720p/1080p" }, note: "4-8 s, native audio; explicit `-p veo` only" },
7342
+ { id: "veo-3.1-fast-generate-preview", provider: "veo", kind: "video", label: "Veo 3.1 Fast", aliases: ["3.1-fast"], default: true, status: "deprecated", shutdown: "2026-10-22", replacement: "`-p omni` (Gemini Omni 1.1 Flash)", price: { usd: 0.1, per: "second", basis: "720p" }, note: "4-8 s, native audio; explicit `-p veo` only" },
7343
+ { id: "grok-imagine-video-1.5", provider: "grok", kind: "video", label: "Grok Imagine Video 1.5", aliases: ["1.5"], default: true, status: "ga", price: { usd: 0.14, per: "second", basis: "720p" }, note: "1-15 s, native audio; 720p unless `--resolution` says otherwise" },
7344
+ { id: "grok-imagine-video-1.5-lite", provider: "grok", kind: "video", label: "Grok Imagine Video 1.5 Lite", aliases: ["lite", "1.5-lite"], status: "ga", price: { usd: 0.03, per: "second", basis: "720p" }, note: "1-15 s, native audio" },
7345
+ { id: "grok-imagine-video", provider: "grok", kind: "video", label: "Grok Imagine Video", aliases: ["classic"], status: "legacy", price: { usd: 0.05, per: "second", basis: "480p" }, note: "Previous generation" },
7346
+ { id: "kling-v3", provider: "kling", kind: "video", label: "Kling v3", aliases: ["v3"], default: true, status: "ga", price: { usd: 0.084, per: "second", basis: "std, silent" }, note: "3-15 s, multi-shot; `std` 720p, `pro` 1080p" },
7347
+ { id: "kling-v2-6", provider: "kling", kind: "video", label: "Kling v2.6", aliases: ["v2.6"], status: "ga", price: { usd: 0.042, per: "second", basis: "std 720p" }, note: "5 or 10 s" },
7348
+ { id: "kling-v2-5-turbo", provider: "kling", kind: "video", label: "Kling v2.5 Turbo", aliases: ["v2.5-turbo", "v2.5"], status: "ga", price: { usd: 0.042, per: "second", basis: "std" }, note: "5 or 10 s, no audio" },
7349
+ { id: "gen4.5", provider: "runway", kind: "video", label: "Runway Gen-4.5", default: true, status: "ga", price: { usd: 0.12, per: "second" }, note: "2-10 s, no audio" },
7350
+ { id: "gen4_turbo", provider: "runway", kind: "video", label: "Runway Gen-4 Turbo", status: "ga", price: { usd: 0.05, per: "second" }, note: "Image-to-video only, no audio" },
7351
+ // ── Audio ─────────────────────────────────────────────────────────────
7352
+ { id: "eleven_v3", provider: "elevenlabs", kind: "speech", label: "ElevenLabs v3", default: true, status: "ga", price: { usd: 0.08, per: "1K characters" } },
7353
+ { id: "gpt-4o-mini-tts", provider: "openai", kind: "speech", label: "GPT-4o mini TTS", default: true, status: "deprecated", shutdown: "2027-01-06" },
7354
+ { id: "music_v2_5", provider: "elevenlabs", kind: "music", label: "ElevenLabs Music v2.5", default: true, status: "ga", price: { usd: 0.15, per: "minute" } },
7355
+ { id: "eleven_text_to_sound_v2", provider: "elevenlabs", kind: "sound-effect", label: "ElevenLabs Sound Effects v2", default: true, status: "ga", price: { usd: 0.12, per: "minute" }, note: "0.5-30 s" },
7356
+ { id: "whisper-1", provider: "openai", kind: "transcription", label: "Whisper", default: true, status: "deprecated", shutdown: "2027-02-26", note: "Only OpenAI model with word timestamps" }
7357
+ ];
7358
+ ModelRetiredError = class extends Error {
7359
+ constructor(message, model) {
7360
+ super(message);
7361
+ this.model = model;
7362
+ this.name = "ModelRetiredError";
7363
+ }
7364
+ };
7365
+ }
7366
+ });
7367
+
7225
7368
  // ../ai-providers/dist/gemini/gemini-models.js
7226
7369
  function isGeminiTextModelAlias(model) {
7227
7370
  return Object.prototype.hasOwnProperty.call(GEMINI_TEXT_MODEL_ALIASES, model);
@@ -7240,19 +7383,10 @@ var GEMINI_DEFAULT_TEXT_MODEL, GEMINI_AGENT_DEFAULT_TEXT_MODEL, GEMINI_TEXT_MODE
7240
7383
  var init_gemini_models = __esm({
7241
7384
  "../ai-providers/dist/gemini/gemini-models.js"() {
7242
7385
  "use strict";
7243
- GEMINI_DEFAULT_TEXT_MODEL = "gemini-3.8-flash";
7386
+ init_catalog();
7387
+ GEMINI_DEFAULT_TEXT_MODEL = defaultModel("gemini", "llm").id;
7244
7388
  GEMINI_AGENT_DEFAULT_TEXT_MODEL = GEMINI_DEFAULT_TEXT_MODEL;
7245
- GEMINI_TEXT_MODEL_ALIASES = {
7246
- flash: GEMINI_DEFAULT_TEXT_MODEL,
7247
- latest: GEMINI_DEFAULT_TEXT_MODEL,
7248
- "flash-3.8": GEMINI_DEFAULT_TEXT_MODEL,
7249
- "flash-3.5": "gemini-3.5-flash",
7250
- "flash-3": "gemini-3-flash-preview",
7251
- "flash-2.5": "gemini-2.5-flash",
7252
- pro: "gemini-3.1-pro-preview",
7253
- "pro-3.1": "gemini-3.1-pro-preview",
7254
- "pro-2.5": "gemini-2.5-pro"
7255
- };
7389
+ GEMINI_TEXT_MODEL_ALIASES = Object.fromEntries(listModels({ provider: "gemini", kind: "llm" }).flatMap((m) => (m.aliases ?? []).map((a) => [a, m.id])));
7256
7390
  GEMINI_TEXT_MODEL_HELP = "flash/latest (Gemini 3.8 Flash), flash-3.8, flash-3.5, flash-3, flash-2.5, pro (Gemini 3.1 Pro), pro-3.1, pro-2.5, or a full gemini-* model ID";
7257
7391
  }
7258
7392
  });
@@ -7586,19 +7720,7 @@ var init_gemini_motion = __esm({
7586
7720
  "../ai-providers/dist/gemini/gemini-motion.js"() {
7587
7721
  "use strict";
7588
7722
  init_gemini_models();
7589
- GEMINI_MOTION_MODELS = {
7590
- gemini: GEMINI_DEFAULT_TEXT_MODEL,
7591
- flash: GEMINI_DEFAULT_TEXT_MODEL,
7592
- latest: GEMINI_DEFAULT_TEXT_MODEL,
7593
- "flash-3.8": GEMINI_DEFAULT_TEXT_MODEL,
7594
- "flash-3.5": "gemini-3.5-flash",
7595
- "flash-3": "gemini-3-flash-preview",
7596
- "flash-2.5": "gemini-2.5-flash",
7597
- pro: "gemini-3.1-pro-preview",
7598
- "2.5-pro": "gemini-2.5-pro",
7599
- "3.1-pro": "gemini-3.1-pro-preview",
7600
- "pro-3.1": "gemini-3.1-pro-preview"
7601
- };
7723
+ GEMINI_MOTION_MODELS = GEMINI_TEXT_MODEL_ALIASES;
7602
7724
  STYLE_USER_INSTRUCTIONS = {
7603
7725
  minimal: "CENTER-ALIGNED pure typography. Thin lines + large title + subtitle. NO glass panels, NO bokeh, NO dark bars. Breathing pulse during hold.",
7604
7726
  corporate: "Data-rich info bar at bottom + top-right badge. Animated number counter. Brand color stripe. Progress bar.",
@@ -7787,13 +7909,19 @@ var init_http = __esm({
7787
7909
  // ../ai-providers/dist/gemini/GeminiProvider.js
7788
7910
  function resolveVeoModel(alias) {
7789
7911
  if (Object.hasOwn(VEO_MODEL_ALIASES, alias)) {
7790
- return VEO_MODEL_ALIASES[alias];
7912
+ return servedVeoModel(VEO_MODEL_ALIASES[alias]);
7791
7913
  }
7792
7914
  const valid = Object.keys(VEO_MODEL_ALIASES).join(", ");
7793
7915
  const hint = alias === "3.0" ? " Veo 3.0 was shut down by Google on 2025-11-12." : "";
7794
7916
  throw new Error(`Unknown Veo model "${alias}". Valid: ${valid}.${hint}`);
7795
7917
  }
7796
- var VEO_MODEL_ALIASES, MODEL_MAP, GeminiProvider, geminiProvider;
7918
+ function servedVeoModel(id) {
7919
+ const spec = id ? findModel("veo", "video", id) : defaultModel("veo", "video");
7920
+ if (spec)
7921
+ assertModelServed(spec);
7922
+ return spec?.id ?? id;
7923
+ }
7924
+ var VEO_MODEL_ALIASES, GeminiProvider, geminiProvider;
7797
7925
  var init_GeminiProvider = __esm({
7798
7926
  "../ai-providers/dist/gemini/GeminiProvider.js"() {
7799
7927
  "use strict";
@@ -7801,23 +7929,8 @@ var init_GeminiProvider = __esm({
7801
7929
  init_gemini_models();
7802
7930
  init_gemini_storyboard();
7803
7931
  init_http();
7804
- VEO_MODEL_ALIASES = {
7805
- "3.1": "veo-3.1-generate-preview",
7806
- "3.1-fast": "veo-3.1-fast-generate-preview"
7807
- };
7808
- MODEL_MAP = {
7809
- "flash": "gemini-3.1-flash-image",
7810
- "3.1-flash": "gemini-3.1-flash-image",
7811
- "latest": "gemini-3.1-flash-image",
7812
- "lite": "gemini-3.1-flash-lite-image",
7813
- "pro": "gemini-3-pro-image",
7814
- "gemini-3.1-flash-image": "gemini-3.1-flash-image",
7815
- "gemini-3.1-flash-lite-image": "gemini-3.1-flash-lite-image",
7816
- "gemini-3-pro-image": "gemini-3-pro-image",
7817
- "gemini-2.5-flash-image": "gemini-2.5-flash-image",
7818
- "gemini-3.1-flash-image-preview": "gemini-3.1-flash-image-preview",
7819
- "gemini-3-pro-image-preview": "gemini-3-pro-image-preview"
7820
- };
7932
+ init_catalog();
7933
+ VEO_MODEL_ALIASES = Object.fromEntries(listModels({ provider: "veo", kind: "video" }).flatMap((m) => (m.aliases ?? []).map((a) => [a, m.id])));
7821
7934
  GeminiProvider = class {
7822
7935
  constructor() {
7823
7936
  this.id = "gemini";
@@ -7851,7 +7964,7 @@ var init_GeminiProvider = __esm({
7851
7964
  }
7852
7965
  try {
7853
7966
  const veoOpts = options ?? {};
7854
- const model = veoOpts.model || "veo-3.1-fast-generate-preview";
7967
+ const model = servedVeoModel(veoOpts.model);
7855
7968
  const aspectRatioMap = {
7856
7969
  "16:9": "16:9",
7857
7970
  "9:16": "9:16",
@@ -8028,7 +8141,7 @@ var init_GeminiProvider = __esm({
8028
8141
  };
8029
8142
  }
8030
8143
  try {
8031
- const model = options?.model || "veo-3.1-generate-preview";
8144
+ const model = servedVeoModel(options?.model ?? findModel("veo", "video", "3.1")?.id);
8032
8145
  const instance = {
8033
8146
  video: { previousOperationName }
8034
8147
  };
@@ -8076,9 +8189,7 @@ var init_GeminiProvider = __esm({
8076
8189
  * Resolve model alias to full model ID
8077
8190
  */
8078
8191
  resolveModel(model) {
8079
- if (!model)
8080
- return MODEL_MAP["flash"];
8081
- return MODEL_MAP[model] || MODEL_MAP["flash"];
8192
+ return (findModel("gemini", "image", model) ?? defaultModel("gemini", "image")).id;
8082
8193
  }
8083
8194
  /**
8084
8195
  * Check if model is Pro
@@ -8671,7 +8782,8 @@ var init_gemini_omni = __esm({
8671
8782
  "../ai-providers/dist/gemini/gemini-omni.js"() {
8672
8783
  "use strict";
8673
8784
  init_http();
8674
- OMNI_MODEL = "gemini-omni-1.1-flash";
8785
+ init_catalog();
8786
+ OMNI_MODEL = defaultModel("omni", "video").id;
8675
8787
  API_ROOT = "https://generativelanguage.googleapis.com";
8676
8788
  FILE_POLL_INTERVAL_MS = 3e3;
8677
8789
  FILE_POLL_TIMEOUT_MS = 5 * 60 * 1e3;
@@ -8811,6 +8923,14 @@ var init_gemini = __esm({
8811
8923
  }
8812
8924
  });
8813
8925
 
8926
+ // ../ai-providers/dist/catalog/index.js
8927
+ var init_catalog2 = __esm({
8928
+ "../ai-providers/dist/catalog/index.js"() {
8929
+ "use strict";
8930
+ init_catalog();
8931
+ }
8932
+ });
8933
+
8814
8934
  // ../ai-providers/dist/openai/openai-storyboard.js
8815
8935
  async function analyzeContent2(apiKey, content, targetDuration, options) {
8816
8936
  const creativity = options?.creativity || "low";
@@ -8823,7 +8943,7 @@ async function analyzeContent2(apiKey, content, targetDuration, options) {
8823
8943
  Authorization: `Bearer ${apiKey}`
8824
8944
  },
8825
8945
  body: JSON.stringify({
8826
- model: "gpt-5.4-mini",
8946
+ model: defaultModel("openai", "llm").id,
8827
8947
  messages: [
8828
8948
  { role: "system", content: systemPrompt },
8829
8949
  { role: "user", content: buildStoryboardUserMessage(content) }
@@ -8868,6 +8988,7 @@ var init_openai_storyboard = __esm({
8868
8988
  "../ai-providers/dist/openai/openai-storyboard.js"() {
8869
8989
  "use strict";
8870
8990
  init_storyboard_prompt();
8991
+ init_catalog();
8871
8992
  }
8872
8993
  });
8873
8994
 
@@ -8877,6 +8998,7 @@ var init_OpenAIProvider = __esm({
8877
8998
  "../ai-providers/dist/openai/OpenAIProvider.js"() {
8878
8999
  "use strict";
8879
9000
  init_openai_storyboard();
9001
+ init_catalog();
8880
9002
  OpenAIProvider = class {
8881
9003
  constructor() {
8882
9004
  this.id = "openai-gpt";
@@ -8886,7 +9008,7 @@ var init_OpenAIProvider = __esm({
8886
9008
  this.iconUrl = "/icons/openai.svg";
8887
9009
  this.isAvailable = true;
8888
9010
  this.baseUrl = "https://api.openai.com/v1";
8889
- this.model = "gpt-5.4-mini";
9011
+ this.model = defaultModel("openai", "llm").id;
8890
9012
  }
8891
9013
  async initialize(config2) {
8892
9014
  this.apiKey = config2.apiKey;
@@ -9192,7 +9314,7 @@ Each segment should be 3-10 seconds long.`;
9192
9314
  Authorization: `Bearer ${this.apiKey}`
9193
9315
  },
9194
9316
  body: JSON.stringify({
9195
- model: "gpt-5.4-mini",
9317
+ model: defaultModel("openai", "llm").id,
9196
9318
  // Reasoning tokens count against this cap.
9197
9319
  max_completion_tokens: 16384,
9198
9320
  response_format: { type: "json_object" },
@@ -10593,6 +10715,7 @@ var init_ClaudeProvider = __esm({
10593
10715
  init_claude_analysis();
10594
10716
  init_claude_viral();
10595
10717
  init_claude_visual_fx();
10718
+ init_catalog();
10596
10719
  ClaudeProvider = class {
10597
10720
  constructor() {
10598
10721
  this.id = "claude";
@@ -10602,7 +10725,7 @@ var init_ClaudeProvider = __esm({
10602
10725
  this.iconUrl = "/icons/claude.svg";
10603
10726
  this.isAvailable = true;
10604
10727
  this.baseUrl = "https://api.anthropic.com/v1";
10605
- this.model = "claude-sonnet-5-5";
10728
+ this.model = defaultModel("claude", "llm").id;
10606
10729
  }
10607
10730
  async initialize(config2) {
10608
10731
  this.apiKey = config2.apiKey;
@@ -10904,11 +11027,7 @@ Instruction: ${instruction}`
10904
11027
  return generateNarrationScript(this.api, videoAnalysis, duration, style, language);
10905
11028
  }
10906
11029
  };
10907
- ClaudeProvider.MOTION_MODELS = {
10908
- sonnet: "claude-sonnet-5-5",
10909
- opus: "claude-opus-5-5",
10910
- "opus-4-6": "claude-opus-4-6"
10911
- };
11030
+ ClaudeProvider.MOTION_MODELS = Object.fromEntries(listModels({ provider: "claude", kind: "llm" }).flatMap((m) => (m.aliases ?? []).map((a) => [a, m.id])));
10912
11031
  claudeProvider = new ClaudeProvider();
10913
11032
  }
10914
11033
  });
@@ -11248,6 +11367,7 @@ var KNOWN_VOICES, TTS_429_MAX_RETRIES, TTS_429_RETRY_DELAY_MS, ELEVENLABS_MUSIC_
11248
11367
  var init_ElevenLabsProvider = __esm({
11249
11368
  "../ai-providers/dist/elevenlabs/ElevenLabsProvider.js"() {
11250
11369
  "use strict";
11370
+ init_catalog();
11251
11371
  KNOWN_VOICES = {
11252
11372
  // Default/popular voices
11253
11373
  rachel: "21m00Tcm4TlvDq8ikWAM",
@@ -11281,7 +11401,7 @@ var init_ElevenLabsProvider = __esm({
11281
11401
  };
11282
11402
  TTS_429_MAX_RETRIES = 1;
11283
11403
  TTS_429_RETRY_DELAY_MS = 2e3;
11284
- ELEVENLABS_MUSIC_MODEL = "music_v2_5";
11404
+ ELEVENLABS_MUSIC_MODEL = defaultModel("elevenlabs", "music").id;
11285
11405
  ElevenLabsProvider = class {
11286
11406
  constructor() {
11287
11407
  this.id = "elevenlabs";
@@ -11945,7 +12065,8 @@ var OPENAI_IMAGE_DEFAULT_MODEL, DEFAULT_MODEL, OpenAIImageProvider, openaiImageP
11945
12065
  var init_OpenAIImageProvider = __esm({
11946
12066
  "../ai-providers/dist/openai-image/OpenAIImageProvider.js"() {
11947
12067
  "use strict";
11948
- OPENAI_IMAGE_DEFAULT_MODEL = "gpt-image-2.5-sunburst";
12068
+ init_catalog();
12069
+ OPENAI_IMAGE_DEFAULT_MODEL = defaultModel("openai", "image").id;
11949
12070
  DEFAULT_MODEL = OPENAI_IMAGE_DEFAULT_MODEL;
11950
12071
  OpenAIImageProvider = class {
11951
12072
  constructor() {
@@ -12269,7 +12390,8 @@ var DEFAULT_MODEL3, RunwayProvider, runwayProvider;
12269
12390
  var init_RunwayProvider = __esm({
12270
12391
  "../ai-providers/dist/runway/RunwayProvider.js"() {
12271
12392
  "use strict";
12272
- DEFAULT_MODEL3 = "gen4.5";
12393
+ init_catalog();
12394
+ DEFAULT_MODEL3 = defaultModel("runway", "video").id;
12273
12395
  RunwayProvider = class _RunwayProvider {
12274
12396
  constructor() {
12275
12397
  this.id = "runway";
@@ -12322,6 +12444,13 @@ var init_RunwayProvider = __esm({
12322
12444
  }
12323
12445
  const hasImage = !!options?.referenceImage;
12324
12446
  const endpoint = hasImage ? "image_to_video" : "text_to_video";
12447
+ if (!hasImage && apiRatio === "960:960") {
12448
+ return {
12449
+ id: "",
12450
+ status: "failed",
12451
+ error: "Runway text-to-video supports 16:9 and 9:16 only. Use 16:9 or 9:16, or pass an image (-i) for square output."
12452
+ };
12453
+ }
12325
12454
  const body = {
12326
12455
  model,
12327
12456
  promptText: prompt3,
@@ -12422,7 +12551,9 @@ var init_RunwayProvider = __esm({
12422
12551
  SUCCEEDED: "completed",
12423
12552
  FAILED: "failed",
12424
12553
  CANCELLED: "cancelled",
12425
- THROTTLED: "failed"
12554
+ // THROTTLED means queued behind the account's concurrency limit; the
12555
+ // task starts on its own, so keep polling.
12556
+ THROTTLED: "pending"
12426
12557
  };
12427
12558
  const result = {
12428
12559
  id: data.id,
@@ -12432,8 +12563,9 @@ var init_RunwayProvider = __esm({
12432
12563
  if (data.status === "SUCCEEDED" && data.output && data.output.length > 0) {
12433
12564
  result.videoUrl = data.output[0];
12434
12565
  }
12435
- if (data.status === "FAILED" || data.status === "THROTTLED") {
12436
- result.error = data.failure || data.failureCode || "Generation failed";
12566
+ if (data.status === "FAILED") {
12567
+ const reason = data.failure || "Generation failed";
12568
+ result.error = data.failureCode ? `${reason} (${data.failureCode})` : reason;
12437
12569
  }
12438
12570
  return result;
12439
12571
  } catch (error) {
@@ -12445,27 +12577,14 @@ var init_RunwayProvider = __esm({
12445
12577
  }
12446
12578
  }
12447
12579
  /**
12448
- * Cancel ongoing generation
12580
+ * Cancel a pending or running task. Runway documents cancellation as
12581
+ * `DELETE /v1/tasks/{id}` (there is no `/cancel` route).
12449
12582
  */
12450
12583
  async cancelGeneration(id) {
12451
- if (!this.apiKey) {
12452
- return false;
12453
- }
12454
- try {
12455
- const response = await fetch(`${this.baseUrl}/tasks/${id}/cancel`, {
12456
- method: "POST",
12457
- headers: {
12458
- Authorization: `Bearer ${this.apiKey}`,
12459
- "X-Runway-Version": _RunwayProvider.API_VERSION
12460
- }
12461
- });
12462
- return response.ok;
12463
- } catch {
12464
- return false;
12465
- }
12584
+ return this.deleteTask(id);
12466
12585
  }
12467
12586
  /**
12468
- * Delete a completed task
12587
+ * Cancel a running task, or delete a finished one (`DELETE /v1/tasks/{id}`).
12469
12588
  */
12470
12589
  async deleteTask(id) {
12471
12590
  if (!this.apiKey) {
@@ -12575,19 +12694,10 @@ var init_runway = __esm({
12575
12694
  // ../ai-providers/dist/kling/KlingProvider.js
12576
12695
  import { createHmac } from "node:crypto";
12577
12696
  function resolveKlingModel(alias) {
12578
- const key = (alias ?? "v3").trim().toLowerCase();
12579
- const aliases = {
12580
- v3: "kling-v3",
12581
- "kling-v3": "kling-v3",
12582
- "v2.6": "kling-v2-6",
12583
- "kling-v2-6": "kling-v2-6",
12584
- "v2.5-turbo": "kling-v2-5-turbo",
12585
- "v2.5": "kling-v2-5-turbo",
12586
- "kling-v2-5-turbo": "kling-v2-5-turbo"
12587
- };
12588
- if (Object.hasOwn(aliases, key))
12589
- return aliases[key];
12590
- throw new Error(`Unknown Kling model "${alias}". Valid: v3, v2.6, v2.5-turbo.`);
12697
+ const model = findModel("kling", "video", alias);
12698
+ if (model)
12699
+ return model.id;
12700
+ throw new Error(`Unknown Kling model "${alias}". Valid: ${modelAliases("kling", "video").join(", ")}.`);
12591
12701
  }
12592
12702
  function klingDuration(model, seconds) {
12593
12703
  const requested = typeof seconds === "number" && Number.isFinite(seconds) ? Math.round(seconds) : 5;
@@ -12599,7 +12709,8 @@ var DEFAULT_MODEL4, STD_MODE_MODELS, KlingProvider, klingProvider;
12599
12709
  var init_KlingProvider = __esm({
12600
12710
  "../ai-providers/dist/kling/KlingProvider.js"() {
12601
12711
  "use strict";
12602
- DEFAULT_MODEL4 = "kling-v3";
12712
+ init_catalog();
12713
+ DEFAULT_MODEL4 = defaultModel("kling", "video").id;
12603
12714
  STD_MODE_MODELS = ["kling-v2-5-turbo", "kling-v2-6", "kling-v3"];
12604
12715
  KlingProvider = class {
12605
12716
  constructor() {
@@ -13038,31 +13149,21 @@ var init_kling = __esm({
13038
13149
 
13039
13150
  // ../ai-providers/dist/grok/GrokProvider.js
13040
13151
  function resolveGrokVideoModel(alias) {
13041
- const key = (alias ?? "1.5").trim().toLowerCase();
13042
- const aliases = {
13043
- "1.5": "grok-imagine-video-1.5",
13044
- "grok-imagine-video-1.5": "grok-imagine-video-1.5",
13045
- lite: "grok-imagine-video-1.5-lite",
13046
- "1.5-lite": "grok-imagine-video-1.5-lite",
13047
- "grok-imagine-video-1.5-lite": "grok-imagine-video-1.5-lite",
13048
- classic: "grok-imagine-video",
13049
- "grok-imagine-video": "grok-imagine-video"
13050
- };
13051
- if (Object.hasOwn(aliases, key))
13052
- return aliases[key];
13053
- throw new Error(`Unknown Grok video model "${alias}". Valid: 1.5, lite, classic.`);
13152
+ const model = findModel("grok", "video", alias);
13153
+ if (model)
13154
+ return model.id;
13155
+ throw new Error(`Unknown Grok video model "${alias}". Valid: ${modelAliases("grok", "video").join(", ")}.`);
13054
13156
  }
13055
13157
  function resolveGrokImageModel(alias) {
13056
- if (alias === "pro" || alias === "2.0" || alias === "quality" || alias === "grok-imagine-image-2.0") {
13057
- return { model: "grok-imagine-image-2.0", quality: "medium" };
13058
- }
13059
- return { model: "grok-imagine-image" };
13158
+ const model = (findModel("grok", "image", alias) ?? defaultModel("grok", "image")).id;
13159
+ return model === "grok-imagine-image-2.0" ? { model, quality: "medium" } : { model };
13060
13160
  }
13061
13161
  var DEFAULT_MODEL5, DEFAULT_VIDEO_RESOLUTION, GROK_VIDEO_RESOLUTIONS, GrokProvider, grokProvider;
13062
13162
  var init_GrokProvider = __esm({
13063
13163
  "../ai-providers/dist/grok/GrokProvider.js"() {
13064
13164
  "use strict";
13065
- DEFAULT_MODEL5 = "grok-imagine-video-1.5";
13165
+ init_catalog();
13166
+ DEFAULT_MODEL5 = defaultModel("grok", "video").id;
13066
13167
  DEFAULT_VIDEO_RESOLUTION = "720p";
13067
13168
  GROK_VIDEO_RESOLUTIONS = ["480p", "720p", "1080p"];
13068
13169
  GrokProvider = class {
@@ -17505,19 +17606,10 @@ var require_src = __commonJS({
17505
17606
 
17506
17607
  // ../ai-providers/dist/fal/FalProvider.js
17507
17608
  function resolveSeedanceVariant(alias) {
17508
- const key = (alias ?? "quality").trim().toLowerCase();
17509
- const aliases = {
17510
- quality: "seedance-2.0",
17511
- "2.0": "seedance-2.0",
17512
- "seedance-2.0": "seedance-2.0",
17513
- fast: "seedance-2.0-fast",
17514
- "seedance-2.0-fast": "seedance-2.0-fast",
17515
- "2.5": "seedance-2.5",
17516
- "seedance-2.5": "seedance-2.5"
17517
- };
17518
- if (Object.hasOwn(aliases, key))
17519
- return aliases[key];
17520
- throw new Error(`Unknown Seedance model "${alias}". Valid: quality, fast, 2.5.`);
17609
+ const model = findModel("seedance", "video", alias);
17610
+ if (model)
17611
+ return model.id;
17612
+ throw new Error(`Unknown Seedance model "${alias}". Valid: ${modelAliases("seedance", "video").join(", ")}.`);
17521
17613
  }
17522
17614
  function estimateSeedanceVideoCostUsd(opts) {
17523
17615
  const shortSide = SEEDANCE_SHORT_SIDE[opts.resolution ?? "720p"] ?? 720;
@@ -17645,6 +17737,7 @@ var init_FalProvider = __esm({
17645
17737
  "../ai-providers/dist/fal/FalProvider.js"() {
17646
17738
  "use strict";
17647
17739
  import_client = __toESM(require_src(), 1);
17740
+ init_catalog();
17648
17741
  ENDPOINT_TEXT_TO_VIDEO = {
17649
17742
  "seedance-2.0": "bytedance/seedance-2.0/text-to-video",
17650
17743
  "seedance-2.0-fast": "bytedance/seedance-2.0/fast/text-to-video",
@@ -18489,6 +18582,8 @@ __export(dist_exports2, {
18489
18582
  KOKORO_MODEL_ID: () => KOKORO_MODEL_ID,
18490
18583
  KlingProvider: () => KlingProvider,
18491
18584
  KokoroProvider: () => KokoroProvider,
18585
+ MODEL_CATALOG: () => MODEL_CATALOG,
18586
+ ModelRetiredError: () => ModelRetiredError,
18492
18587
  OPENAI_IMAGE_DEFAULT_MODEL: () => OPENAI_IMAGE_DEFAULT_MODEL,
18493
18588
  OPENAI_TTS_VOICES: () => OPENAI_TTS_VOICES,
18494
18589
  OllamaProvider: () => OllamaProvider,
@@ -18500,12 +18595,15 @@ __export(dist_exports2, {
18500
18595
  RunwayProvider: () => RunwayProvider,
18501
18596
  VEO_MODEL_ALIASES: () => VEO_MODEL_ALIASES,
18502
18597
  WhisperProvider: () => WhisperProvider,
18598
+ assertModelServed: () => assertModelServed,
18503
18599
  claudeProvider: () => claudeProvider,
18600
+ defaultModel: () => defaultModel,
18504
18601
  defineApiKey: () => defineApiKey,
18505
18602
  defineProvider: () => defineProvider,
18506
18603
  elevenLabsProvider: () => elevenLabsProvider,
18507
18604
  estimateSeedanceVideoCostUsd: () => estimateSeedanceVideoCostUsd,
18508
18605
  falProvider: () => falProvider,
18606
+ findModel: () => findModel,
18509
18607
  geminiProvider: () => geminiProvider,
18510
18608
  getAllApiKeys: () => getAllApiKeys,
18511
18609
  getAllProviders: () => getAllProviders,
@@ -18522,8 +18620,11 @@ __export(dist_exports2, {
18522
18620
  klingDuration: () => klingDuration,
18523
18621
  klingProvider: () => klingProvider,
18524
18622
  kokoroProvider: () => kokoroProvider,
18623
+ listModels: () => listModels,
18525
18624
  loadBundledKokoroRuntime: () => loadBundledKokoroRuntime,
18526
18625
  loadKokoroFromWorkspace: () => loadKokoroFromWorkspace,
18626
+ modelAliases: () => modelAliases,
18627
+ modelLifecycle: () => modelLifecycle,
18527
18628
  ollamaProvider: () => ollamaProvider,
18528
18629
  omniProvider: () => omniProvider,
18529
18630
  openaiImageProvider: () => openaiImageProvider,
@@ -18550,6 +18651,7 @@ var init_dist2 = __esm({
18550
18651
  init_define_provider();
18551
18652
  init_whisper();
18552
18653
  init_gemini();
18654
+ init_catalog2();
18553
18655
  init_openai();
18554
18656
  init_claude();
18555
18657
  init_ollama();
@@ -32160,16 +32262,14 @@ import { tmpdir } from "node:os";
32160
32262
  import { createHash } from "node:crypto";
32161
32263
  async function ensureRemotionInstalled() {
32162
32264
  try {
32163
- await execSafe("npx", ["--yes", "remotion", "--help"], { timeout: 6e4 });
32265
+ await execSafe("npm", ["--version"], { timeout: 3e4 });
32164
32266
  return null;
32165
32267
  } catch (error) {
32166
32268
  const detail = error instanceof Error ? error.message : String(error);
32167
- console.error(`[Remotion] ensureRemotionInstalled failed: ${detail.slice(0, 300)}`);
32168
32269
  return [
32169
- "Remotion CLI not found or failed to initialize.",
32270
+ "Remotion renders need npm to install the Remotion CLI into a temporary project.",
32170
32271
  ` Debug: ${detail.slice(0, 200)}`,
32171
- " Fix: npm install -g @remotion/cli",
32172
- " Or ensure npx is available and can download @remotion/cli on demand."
32272
+ " Fix: install Node.js 20+ (which includes npm) and make sure `npm` is on PATH."
32173
32273
  ].join("\n");
32174
32274
  }
32175
32275
  }
@@ -32911,6 +33011,13 @@ __export(ai_motion_exports, {
32911
33011
  import { resolve as resolve18 } from "node:path";
32912
33012
  import { existsSync as existsSync13 } from "node:fs";
32913
33013
  import { readFile as readFile14, writeFile as writeFile14 } from "node:fs/promises";
33014
+ function resolveMotionModel(alias) {
33015
+ const claude = findModel("claude", "llm", alias);
33016
+ if (claude) return { provider: "claude", modelId: claude.id };
33017
+ const gemini = findModel("gemini", "llm", alias);
33018
+ if (gemini) return { provider: "gemini", modelId: gemini.id };
33019
+ return { provider: "claude", modelId: defaultModel("claude", "llm").id };
33020
+ }
32914
33021
  async function getOptionalGoogleApiKey() {
32915
33022
  const configKey = await getApiKeyFromConfig("google");
32916
33023
  if (configKey) return configKey;
@@ -32925,7 +33032,7 @@ function normalizeUnderstand(value) {
32925
33032
  }
32926
33033
  async function executeMotion(options) {
32927
33034
  const modelAlias = options.model || "sonnet";
32928
- const modelConfig = MODEL_MAP2[modelAlias] ?? MODEL_MAP2["sonnet"];
33035
+ const modelConfig = resolveMotionModel(modelAlias);
32929
33036
  const useGemini = modelConfig.provider === "gemini";
32930
33037
  const width = options.width || 1920;
32931
33038
  const height = options.height || 1080;
@@ -33304,7 +33411,6 @@ function registerMotionCommand(aiCommand) {
33304
33411
  }
33305
33412
  });
33306
33413
  }
33307
- var MODEL_MAP2;
33308
33414
  var init_ai_motion = __esm({
33309
33415
  "src/commands/ai-motion.ts"() {
33310
33416
  "use strict";
@@ -33315,14 +33421,6 @@ var init_ai_motion = __esm({
33315
33421
  init_config();
33316
33422
  init_output();
33317
33423
  init_validate();
33318
- MODEL_MAP2 = {
33319
- sonnet: { provider: "claude", modelId: "claude-sonnet-5-5" },
33320
- opus: { provider: "claude", modelId: "claude-opus-5-5" },
33321
- "opus-4-6": { provider: "claude", modelId: "claude-opus-4-6" },
33322
- gemini: { provider: "gemini", modelId: GEMINI_DEFAULT_TEXT_MODEL },
33323
- "gemini-2.5-pro": { provider: "gemini", modelId: "gemini-2.5-pro" },
33324
- "gemini-3.1-pro": { provider: "gemini", modelId: "gemini-3.1-pro-preview" }
33325
- };
33326
33424
  }
33327
33425
  });
33328
33426
 
@@ -34088,12 +34186,15 @@ var init_ai_helpers = __esm({
34088
34186
  });
34089
34187
 
34090
34188
  // src/commands/_shared/video-utils.ts
34091
- async function uploadToImgbb(imageBuffer, apiKey) {
34189
+ async function uploadToImgbb(imageBuffer, apiKey, expirationSeconds) {
34092
34190
  try {
34093
34191
  const base64Image = imageBuffer.toString("base64");
34094
34192
  const formData = new URLSearchParams();
34095
34193
  formData.append("key", apiKey);
34096
34194
  formData.append("image", base64Image);
34195
+ if (expirationSeconds !== void 0) {
34196
+ formData.append("expiration", String(expirationSeconds));
34197
+ }
34097
34198
  const response = await fetch("https://api.imgbb.com/1/upload", {
34098
34199
  method: "POST",
34099
34200
  body: formData
@@ -34156,7 +34257,10 @@ function awsEncode(value) {
34156
34257
  (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
34157
34258
  );
34158
34259
  }
34159
- function presignS3PutUrl(params) {
34260
+ function clamp(value, min, max) {
34261
+ return Math.min(max, Math.max(min, Math.round(value)));
34262
+ }
34263
+ function presignS3Url(params) {
34160
34264
  const now = /* @__PURE__ */ new Date();
34161
34265
  const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, "");
34162
34266
  const dateStamp = amzDate.slice(0, 8);
@@ -34167,7 +34271,7 @@ function presignS3PutUrl(params) {
34167
34271
  "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
34168
34272
  "X-Amz-Credential": `${params.accessKeyId}/${credentialScope}`,
34169
34273
  "X-Amz-Date": amzDate,
34170
- "X-Amz-Expires": String(params.ttlSeconds),
34274
+ "X-Amz-Expires": String(clamp(params.ttlSeconds, 1, S3_MAX_PRESIGN_SECONDS)),
34171
34275
  "X-Amz-SignedHeaders": "host"
34172
34276
  };
34173
34277
  if (params.sessionToken) {
@@ -34175,7 +34279,7 @@ function presignS3PutUrl(params) {
34175
34279
  }
34176
34280
  const canonicalQuery = Object.entries(query).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `${awsEncode(key)}=${awsEncode(value)}`).join("&");
34177
34281
  const canonicalRequest = [
34178
- "PUT",
34282
+ params.method,
34179
34283
  canonicalUri,
34180
34284
  canonicalQuery,
34181
34285
  `host:${host}`,
@@ -34234,7 +34338,8 @@ async function resolveUploadHost() {
34234
34338
  const ext = extensionFor(opts);
34235
34339
  const prefix = safePrefix(settings.s3.prefix);
34236
34340
  const key = `${prefix}/${Date.now()}-${randomUUID()}.${ext}`;
34237
- const presignedUrl = presignS3PutUrl({
34341
+ const presignedUrl = presignS3Url({
34342
+ method: "PUT",
34238
34343
  accessKeyId,
34239
34344
  secretAccessKey,
34240
34345
  sessionToken,
@@ -34253,10 +34358,26 @@ async function resolveUploadHost() {
34253
34358
  if (!response.ok) {
34254
34359
  throw new Error(`S3 upload failed (${response.status}): ${response.statusText}`);
34255
34360
  }
34361
+ if (settings.s3.publicBaseUrl) {
34362
+ return {
34363
+ provider: "s3",
34364
+ url: publicS3Url({ region, bucket, key, publicBaseUrl: settings.s3.publicBaseUrl })
34365
+ };
34366
+ }
34367
+ const ttlSeconds = clamp(settings.ttlSeconds, 1, S3_MAX_PRESIGN_SECONDS);
34256
34368
  return {
34257
34369
  provider: "s3",
34258
- url: publicS3Url({ region, bucket, key, publicBaseUrl: settings.s3.publicBaseUrl }),
34259
- expiresAt: new Date(Date.now() + settings.ttlSeconds * 1e3).toISOString()
34370
+ url: presignS3Url({
34371
+ method: "GET",
34372
+ accessKeyId,
34373
+ secretAccessKey,
34374
+ sessionToken,
34375
+ region,
34376
+ bucket,
34377
+ key,
34378
+ ttlSeconds
34379
+ }),
34380
+ expiresAt: new Date(Date.now() + ttlSeconds * 1e3).toISOString()
34260
34381
  };
34261
34382
  }
34262
34383
  };
@@ -34268,19 +34389,28 @@ async function resolveUploadHost() {
34268
34389
  if (!imgbbKey) {
34269
34390
  throw new Error("IMGBB_API_KEY required for image-to-video uploads.");
34270
34391
  }
34271
- const result = await uploadToImgbb(imageBuffer, imgbbKey);
34392
+ const expiration = clamp(settings.ttlSeconds, IMGBB_MIN_EXPIRATION_SECONDS, IMGBB_MAX_EXPIRATION_SECONDS);
34393
+ const result = await uploadToImgbb(imageBuffer, imgbbKey, expiration);
34272
34394
  if (!result.success || !result.url) {
34273
34395
  throw new Error(`ImgBB upload failed: ${result.error ?? "unknown error"}`);
34274
34396
  }
34275
- return { provider: "imgbb", url: result.url };
34397
+ return {
34398
+ provider: "imgbb",
34399
+ url: result.url,
34400
+ expiresAt: new Date(Date.now() + expiration * 1e3).toISOString()
34401
+ };
34276
34402
  }
34277
34403
  };
34278
34404
  }
34405
+ var S3_MAX_PRESIGN_SECONDS, IMGBB_MIN_EXPIRATION_SECONDS, IMGBB_MAX_EXPIRATION_SECONDS;
34279
34406
  var init_upload_host = __esm({
34280
34407
  "src/utils/upload-host.ts"() {
34281
34408
  "use strict";
34282
34409
  init_config();
34283
34410
  init_video_utils();
34411
+ S3_MAX_PRESIGN_SECONDS = 7 * 24 * 60 * 60;
34412
+ IMGBB_MIN_EXPIRATION_SECONDS = 60;
34413
+ IMGBB_MAX_EXPIRATION_SECONDS = 15552e3;
34284
34414
  }
34285
34415
  });
34286
34416
 
@@ -34315,6 +34445,7 @@ async function executeVideoGenerate(options) {
34315
34445
  negative,
34316
34446
  resolution,
34317
34447
  veoModel = "3.1-fast",
34448
+ runwayModel,
34318
34449
  seedanceModel = "quality",
34319
34450
  grokModel,
34320
34451
  klingModel,
@@ -34404,6 +34535,7 @@ async function executeVideoGenerate(options) {
34404
34535
  await runway.initialize({ apiKey: key });
34405
34536
  const result = await runway.generateVideo(prompt3, {
34406
34537
  prompt: prompt3,
34538
+ model: runwayModel,
34407
34539
  referenceImage,
34408
34540
  duration,
34409
34541
  aspectRatio: ratio,
@@ -36546,6 +36678,34 @@ var init_thumbnail = __esm({
36546
36678
  }
36547
36679
  });
36548
36680
 
36681
+ // src/utils/model-lifecycle.ts
36682
+ function videoModelSpec(provider, flags) {
36683
+ const catalogProvider = provider === "fal" ? "seedance" : provider;
36684
+ const flag = VIDEO_MODEL_FLAG[catalogProvider];
36685
+ return findModel(catalogProvider, "video", flag ? flags[flag] : void 0);
36686
+ }
36687
+ function checkModelLifecycle(spec, now = /* @__PURE__ */ new Date()) {
36688
+ if (!spec) return [];
36689
+ const lifecycle = modelLifecycle(spec, now);
36690
+ if (lifecycle.state === "retired") exitWithError(usageError(lifecycle.message));
36691
+ return lifecycle.state === "retiring" ? [lifecycle.message] : [];
36692
+ }
36693
+ var VIDEO_MODEL_FLAG;
36694
+ var init_model_lifecycle = __esm({
36695
+ "src/utils/model-lifecycle.ts"() {
36696
+ "use strict";
36697
+ init_dist2();
36698
+ init_output();
36699
+ VIDEO_MODEL_FLAG = {
36700
+ seedance: "seedanceModel",
36701
+ grok: "grokModel",
36702
+ kling: "klingModel",
36703
+ veo: "veoModel",
36704
+ runway: "runwayModel"
36705
+ };
36706
+ }
36707
+ });
36708
+
36549
36709
  // src/commands/generate/video-extend.ts
36550
36710
  import { resolve as resolve26 } from "node:path";
36551
36711
  import { writeFile as writeFile23 } from "node:fs/promises";
@@ -36557,11 +36717,22 @@ function registerVideoExtendCommand(parent) {
36557
36717
  if (options.output) {
36558
36718
  validateOutputPath(options.output);
36559
36719
  }
36720
+ let lifecycleWarnings = [];
36721
+ if (provider === "veo") {
36722
+ try {
36723
+ resolveVeoModel(options.veoModel);
36724
+ } catch (err) {
36725
+ exitWithError(usageError(err instanceof Error ? err.message : String(err)));
36726
+ }
36727
+ lifecycleWarnings = checkModelLifecycle(findModel("veo", "video", options.veoModel));
36728
+ if (!options.dryRun) printWarnings(lifecycleWarnings);
36729
+ }
36560
36730
  if (options.dryRun) {
36561
36731
  outputSuccess({
36562
36732
  command: "generate video-extend",
36563
36733
  startedAt,
36564
36734
  dryRun: true,
36735
+ warnings: lifecycleWarnings,
36565
36736
  data: {
36566
36737
  params: {
36567
36738
  id,
@@ -36714,6 +36885,7 @@ function registerVideoExtendCommand(parent) {
36714
36885
  outputSuccess({
36715
36886
  command: "generate video-extend",
36716
36887
  startedAt,
36888
+ warnings: lifecycleWarnings,
36717
36889
  data: {
36718
36890
  provider: "veo",
36719
36891
  taskId: result.id,
@@ -36762,6 +36934,7 @@ var init_video_extend = __esm({
36762
36934
  init_ora();
36763
36935
  init_dist2();
36764
36936
  init_api_key();
36937
+ init_model_lifecycle();
36765
36938
  init_output();
36766
36939
  init_validate();
36767
36940
  init_ai_helpers();
@@ -36829,16 +37002,8 @@ var init_provider_resolver = __esm({
36829
37002
 
36830
37003
  // src/commands/_shared/openai-image.ts
36831
37004
  function resolveOpenAIImageModel(modelAlias) {
36832
- const alias = modelAlias?.trim() ?? "";
36833
- for (const [id, model] of Object.entries(OPENAI_IMAGE_MODELS)) {
36834
- if (alias === id || model.aliases.includes(alias)) {
36835
- return { openaiModel: id, modelLabel: model.label };
36836
- }
36837
- }
36838
- return {
36839
- openaiModel: OPENAI_IMAGE_DEFAULT_MODEL,
36840
- modelLabel: OPENAI_IMAGE_MODELS[OPENAI_IMAGE_DEFAULT_MODEL].label
36841
- };
37005
+ const model = findModel("openai", "image", modelAlias) ?? defaultModel("openai", "image");
37006
+ return { openaiModel: model.id, modelLabel: model.label };
36842
37007
  }
36843
37008
  async function executeOpenAIImageGenerate(prompt3, options, ctx) {
36844
37009
  const provider = new OpenAIImageProvider();
@@ -36853,18 +37018,45 @@ async function executeOpenAIImageGenerate(prompt3, options, ctx) {
36853
37018
  });
36854
37019
  return { result, openaiModel, modelLabel };
36855
37020
  }
36856
- var OPENAI_IMAGE_MODELS;
36857
37021
  var init_openai_image2 = __esm({
36858
37022
  "src/commands/_shared/openai-image.ts"() {
36859
37023
  "use strict";
36860
37024
  init_dist2();
36861
- OPENAI_IMAGE_MODELS = {
36862
- "gpt-image-2.5-sunburst": { label: "GPT Image 2.5 Sunburst", aliases: ["2.5", "sunburst"] },
36863
- "gpt-image-2.5-flare": { label: "GPT Image 2.5 Flare", aliases: ["flare", "2.5-flare"] },
36864
- "gpt-image-2": { label: "GPT Image 2", aliases: ["2"] },
36865
- "gpt-image-1.5": { label: "GPT Image 1.5", aliases: ["1.5"] }
37025
+ }
37026
+ });
37027
+
37028
+ // src/commands/_shared/image-cost.ts
37029
+ function catalogPrice(provider, model) {
37030
+ if (provider === "openai") return findModel("openai", "image", resolveOpenAIImageModel(model).openaiModel)?.price?.usd;
37031
+ if (provider === "gemini") return findModel("gemini", "image", model ?? "flash")?.price?.usd ?? findModel("gemini", "image")?.price?.usd;
37032
+ if (provider === "grok") return findModel("grok", "image", resolveGrokImageModel(model).model)?.price?.usd;
37033
+ return void 0;
37034
+ }
37035
+ function estimateImageCostUsd(provider, model, count) {
37036
+ const images = Math.max(1, count);
37037
+ const perImage = catalogPrice(provider, model);
37038
+ if (perImage === void 0) {
37039
+ return {
37040
+ costUsd: lookupCostEstimateUpperBound("generate image") ?? 5,
37041
+ warnings: [
37042
+ `costUsd is the tier upper bound for ${provider} - actual provider billing is typically lower and is not metered here.`
37043
+ ]
36866
37044
  };
36867
37045
  }
37046
+ return {
37047
+ costUsd: Number((perImage * images).toFixed(3)),
37048
+ warnings: [
37049
+ `costUsd is an upper-bound estimate from ${provider} list prices for ${images} image(s) - not metered billing.`
37050
+ ]
37051
+ };
37052
+ }
37053
+ var init_image_cost = __esm({
37054
+ "src/commands/_shared/image-cost.ts"() {
37055
+ "use strict";
37056
+ init_dist2();
37057
+ init_output();
37058
+ init_openai_image2();
37059
+ }
36868
37060
  });
36869
37061
 
36870
37062
  // src/commands/generate/image.ts
@@ -36964,6 +37156,7 @@ Examples:
36964
37156
  command: "generate image",
36965
37157
  startedAt,
36966
37158
  dryRun: true,
37159
+ ...estimateImageCostUsd(provider, options.model, parseInt(options.count) || 1),
36967
37160
  data: {
36968
37161
  params: {
36969
37162
  prompt: prompt3,
@@ -37013,6 +37206,7 @@ Examples:
37013
37206
  outputSuccess({
37014
37207
  command: "generate image",
37015
37208
  startedAt,
37209
+ ...estimateImageCostUsd("openai", options.model, result.images.length),
37016
37210
  data: {
37017
37211
  provider: "openai",
37018
37212
  model: modelLabel,
@@ -37128,7 +37322,7 @@ Examples:
37128
37322
  outputSuccess({
37129
37323
  command: "generate image",
37130
37324
  startedAt,
37131
- warnings: [],
37325
+ ...estimateImageCostUsd("gemini", options.model, result.images.length),
37132
37326
  data: {
37133
37327
  provider: "gemini",
37134
37328
  model: usedLabel,
@@ -37223,6 +37417,7 @@ Examples:
37223
37417
  outputSuccess({
37224
37418
  command: "generate image",
37225
37419
  startedAt,
37420
+ ...estimateImageCostUsd("grok", options.model, result.images.length),
37226
37421
  data: {
37227
37422
  provider: "grok",
37228
37423
  modelId: resolveGrokImageModel(options.model).model,
@@ -37342,6 +37537,7 @@ var init_image = __esm({
37342
37537
  init_validate();
37343
37538
  init_provider_resolver();
37344
37539
  init_openai_image2();
37540
+ init_image_cost();
37345
37541
  init_image_file();
37346
37542
  }
37347
37543
  });
@@ -38441,6 +38637,16 @@ Examples:
38441
38637
  log(`${provider} does not support 1:1 \u2014 falling back to 16:9`);
38442
38638
  options.ratio = "16:9";
38443
38639
  }
38640
+ try {
38641
+ if (provider === "seedance" || provider === "fal") resolveSeedanceVariant(options.seedanceModel);
38642
+ if (provider === "grok") resolveGrokVideoModel(options.grokModel);
38643
+ if (provider === "kling") resolveKlingModel(options.klingModel);
38644
+ if (provider === "veo") resolveVeoModel(options.veoModel);
38645
+ } catch (err) {
38646
+ exitWithError(usageError(err instanceof Error ? err.message : String(err)));
38647
+ }
38648
+ const lifecycleWarnings = checkModelLifecycle(videoModelSpec(provider, options));
38649
+ if (!options.dryRun) printWarnings(lifecycleWarnings);
38444
38650
  if (options.dryRun) {
38445
38651
  const costUsd = provider === "seedance" || provider === "fal" ? realRunCost(provider, options).costUsd : void 0;
38446
38652
  outputSuccess({
@@ -38448,6 +38654,7 @@ Examples:
38448
38654
  startedAt,
38449
38655
  dryRun: true,
38450
38656
  costUsd,
38657
+ warnings: lifecycleWarnings,
38451
38658
  data: {
38452
38659
  params: {
38453
38660
  prompt: prompt3,
@@ -38455,13 +38662,10 @@ Examples:
38455
38662
  duration: options.duration,
38456
38663
  ratio: options.ratio,
38457
38664
  image: options.image,
38458
- mode: options.mode,
38665
+ mode: provider === "kling" ? options.mode : void 0,
38459
38666
  negative: options.negative,
38460
38667
  resolution: options.resolution,
38461
- veoModel: options.veoModel,
38462
- seedanceModel: options.seedanceModel,
38463
- grokModel: options.grokModel,
38464
- klingModel: options.klingModel,
38668
+ model: videoModelSpec(provider, options)?.id,
38465
38669
  refImages: options.refImages,
38466
38670
  refVideos: options.refVideos,
38467
38671
  refAudio: options.refAudio,
@@ -38853,7 +39057,7 @@ Examples:
38853
39057
  command: "generate video",
38854
39058
  startedAt,
38855
39059
  costUsd: cost.costUsd,
38856
- warnings: provider === "veo" ? [VEO_SHUTDOWN_WARNING, ...cost.warnings] : cost.warnings,
39060
+ warnings: [...lifecycleWarnings, ...cost.warnings],
38857
39061
  data: {
38858
39062
  provider,
38859
39063
  taskId: result?.id,
@@ -38865,9 +39069,6 @@ Examples:
38865
39069
  return;
38866
39070
  }
38867
39071
  console.log();
38868
- if (provider === "veo") {
38869
- console.log(source_default.yellow(VEO_SHUTDOWN_WARNING));
38870
- }
38871
39072
  if (finalResult.videoUrl) {
38872
39073
  console.log(`Video URL: ${finalResult.videoUrl}`);
38873
39074
  }
@@ -38992,7 +39193,6 @@ function printVideoStarted(providerLabel, taskId, taskType) {
38992
39193
  console.log(`Task ID: ${source_default.bold(taskId)}`);
38993
39194
  if (taskType) console.log(`Type: ${taskType}`);
38994
39195
  }
38995
- var VEO_SHUTDOWN_WARNING;
38996
39196
  var init_video = __esm({
38997
39197
  "src/commands/generate/video.ts"() {
38998
39198
  "use strict";
@@ -39001,6 +39201,7 @@ var init_video = __esm({
39001
39201
  init_dist3();
39002
39202
  init_dist2();
39003
39203
  init_api_key();
39204
+ init_model_lifecycle();
39004
39205
  init_tty();
39005
39206
  init_output();
39006
39207
  init_validate();
@@ -39008,7 +39209,6 @@ var init_video = __esm({
39008
39209
  init_upload_host();
39009
39210
  init_ai_helpers();
39010
39211
  init_status_jobs();
39011
- VEO_SHUTDOWN_WARNING = "Veo 3.1 preview models shut down on 2026-10-22. Use `-p omni` (Gemini Omni 1.1 Flash) instead.";
39012
39212
  }
39013
39213
  });
39014
39214
 
@@ -39118,7 +39318,7 @@ Advanced:
39118
39318
  generate video-cancel Provider lifecycle control; poll with 'vibe status job <job-id> --json'
39119
39319
 
39120
39320
  API Keys (per provider):
39121
- GOOGLE_API_KEY Image (default), Veo video
39321
+ GOOGLE_API_KEY Image (default), Omni video
39122
39322
  OPENAI_API_KEY Image (-p openai)
39123
39323
  FAL_API_KEY Seedance video (-p seedance, default video)
39124
39324
  XAI_API_KEY Grok image/video
@@ -40127,7 +40327,7 @@ ${textsToTranslate}`;
40127
40327
  Authorization: `Bearer ${openaiKey}`
40128
40328
  },
40129
40329
  body: JSON.stringify({
40130
- model: "gpt-5.4-mini",
40330
+ model: defaultModel("openai", "llm").id,
40131
40331
  messages: [{ role: "user", content: translatePrompt }]
40132
40332
  })
40133
40333
  });
@@ -40155,7 +40355,7 @@ ${textsToTranslate}`;
40155
40355
  "anthropic-version": "2023-06-01"
40156
40356
  },
40157
40357
  body: JSON.stringify({
40158
- model: "claude-sonnet-5-5",
40358
+ model: defaultModel("claude", "llm").id,
40159
40359
  max_tokens: 16e3,
40160
40360
  messages: [{ role: "user", content: translatePrompt }]
40161
40361
  })
@@ -40206,6 +40406,7 @@ var init_translate_srt = __esm({
40206
40406
  "src/commands/_shared/edit/translate-srt.ts"() {
40207
40407
  "use strict";
40208
40408
  init_subtitle();
40409
+ init_dist2();
40209
40410
  }
40210
40411
  });
40211
40412
 
@@ -41019,22 +41220,13 @@ function analyzeGapFillability(gaps, videoClips, sources) {
41019
41220
  return { gap, canExtendBefore, canExtendAfter, remainingGap, gapStart };
41020
41221
  });
41021
41222
  }
41022
- async function uploadFrameToImgbb(framePath, imgbbApiKey) {
41223
+ async function uploadFrame(host, framePath) {
41023
41224
  try {
41024
- const frameBuffer = await readFile23(framePath);
41025
- const frameBase64 = frameBuffer.toString("base64");
41026
- const formData = new FormData();
41027
- formData.append("key", imgbbApiKey);
41028
- formData.append("image", frameBase64);
41029
- const response = await fetch("https://api.imgbb.com/1/upload", {
41030
- method: "POST",
41031
- body: formData
41225
+ const upload = await host.uploadImage(await readFile23(framePath), {
41226
+ filename: framePath,
41227
+ mimeType: "image/png"
41032
41228
  });
41033
- const data = await response.json();
41034
- if (!data.success || !data.data?.url) {
41035
- return { error: data.error?.message || "Upload failed" };
41036
- }
41037
- return { url: data.data.url };
41229
+ return { url: upload.url };
41038
41230
  } catch (err) {
41039
41231
  return { error: err instanceof Error ? err.message : String(err) };
41040
41232
  }
@@ -41185,11 +41377,13 @@ async function executeFillGaps(options) {
41185
41377
  await mkdir16(footageDir, { recursive: true });
41186
41378
  }
41187
41379
  humanLines.push("Generating AI Videos");
41188
- const imgbbApiKey = options.imgbbApiKey || await getApiKey("IMGBB_API_KEY", "imgbb", void 0);
41189
- if (!imgbbApiKey) {
41380
+ let uploadHost;
41381
+ try {
41382
+ uploadHost = await resolveUploadHost();
41383
+ } catch (err) {
41190
41384
  return {
41191
41385
  success: false,
41192
- error: "IMGBB_API_KEY required for image hosting. Get a free API key at https://api.imgbb.com/",
41386
+ error: err instanceof Error ? err.message : String(err),
41193
41387
  humanLines
41194
41388
  };
41195
41389
  }
@@ -41233,12 +41427,12 @@ async function executeFillGaps(options) {
41233
41427
  humanLines
41234
41428
  };
41235
41429
  }
41236
- onProgress("Uploading frame to imgbb...");
41237
- const upload = await uploadFrameToImgbb(framePath, imgbbApiKey);
41430
+ onProgress(`Uploading frame via ${uploadHost.provider}...`);
41431
+ const upload = await uploadFrame(uploadHost, framePath);
41238
41432
  if (!upload.url) {
41239
41433
  return {
41240
41434
  success: false,
41241
- error: `Failed to upload frame to imgbb: ${upload.error || "unknown"}`,
41435
+ error: `Failed to upload frame via ${uploadHost.provider}: ${upload.error || "unknown"}`,
41242
41436
  humanLines
41243
41437
  };
41244
41438
  }
@@ -41313,7 +41507,7 @@ async function executeFillGaps(options) {
41313
41507
  humanLines.push(" Failed to extract frame for continuation");
41314
41508
  break;
41315
41509
  }
41316
- const extUpload = await uploadFrameToImgbb(lastFramePath, imgbbApiKey);
41510
+ const extUpload = await uploadFrame(uploadHost, lastFramePath);
41317
41511
  if (!extUpload.url) {
41318
41512
  humanLines.push(" Failed to upload continuation frame");
41319
41513
  break;
@@ -41436,6 +41630,7 @@ var init_execute_fill_gaps = __esm({
41436
41630
  init_dist2();
41437
41631
  init_engine();
41438
41632
  init_api_key();
41633
+ init_upload_host();
41439
41634
  init_exec_safe();
41440
41635
  init_project_resolver();
41441
41636
  init_ai_helpers();
@@ -46474,8 +46669,9 @@ var init_product_surface = __esm({
46474
46669
  schema: { surface: "agent", note: "Machine-readable command discovery." },
46475
46670
  completion: { surface: "advanced", note: "Shell ergonomics helper." },
46476
46671
  agent: {
46477
- surface: "advanced",
46478
- note: "Optional fallback REPL; external coding agents are the primary workflow."
46672
+ surface: "legacy",
46673
+ replacement: "a host agent (Claude Code, Codex, Cursor) or the MCP server via `vibe host setup`",
46674
+ note: "Deprecated built-in REPL, removed before 1.0."
46479
46675
  },
46480
46676
  "design.validate": { surface: "public", note: "Validate the DESIGN.md visual contract." },
46481
46677
  "generate.image": { surface: "public" },
@@ -56144,6 +56340,7 @@ var init_detect = __esm({
56144
56340
  validateOutputPath(options.output);
56145
56341
  }
56146
56342
  if (options.dryRun) {
56343
+ spinner2.stop();
56147
56344
  outputSuccess({
56148
56345
  command: "detect scenes",
56149
56346
  startedAt,
@@ -56263,6 +56460,7 @@ var init_detect = __esm({
56263
56460
  validateOutputPath(options.output);
56264
56461
  }
56265
56462
  if (options.dryRun) {
56463
+ spinner2.stop();
56266
56464
  outputSuccess({
56267
56465
  command: "detect silence",
56268
56466
  startedAt,
@@ -56349,6 +56547,7 @@ var init_detect = __esm({
56349
56547
  validateOutputPath(options.output);
56350
56548
  }
56351
56549
  if (options.dryRun) {
56550
+ spinner2.stop();
56352
56551
  outputSuccess({
56353
56552
  command: "detect beats",
56354
56553
  startedAt,
@@ -56709,8 +56908,9 @@ they want for a given task. This guide is the operator-facing summary.
56709
56908
 
56710
56909
  If you already use Claude Code, Codex, Cursor, Aider, Gemini CLI, OpenCode,
56711
56910
  or another coding agent, let that host drive \`vibe\` directly through shell
56712
- commands plus \`AGENTS.md\` / \`CLAUDE.md\`. \`vibe agent\` is the optional
56713
- built-in fallback when you do not already have an agent host.
56911
+ commands plus \`AGENTS.md\` / \`CLAUDE.md\`. Without a coding agent, connect
56912
+ the MCP server to Claude Desktop or ChatGPT (\`vibe host setup\`).
56913
+ \`vibe agent\` is deprecated and will be removed before 1.0.
56714
56914
 
56715
56915
  ## TL;DR
56716
56916
 
@@ -56721,12 +56921,12 @@ built-in fallback when you do not already have an agent host.
56721
56921
  | **Reproducibility** | none | high (idempotent) | highest (checkpointed) |
56722
56922
  | **Budget caps** | per-session max-turns | none | \`--budget-usd\`, \`--budget-tokens\`, \`--max-errors\` |
56723
56923
  | **Resume after crash** | no | re-invoke | \`--resume\` |
56724
- | **Best for** | optional built-in exploration | finished script | repeatable workflows |
56924
+ | **Best for** | deprecated; use a host agent | finished script | repeatable workflows |
56725
56925
 
56726
56926
  ## Decision tree
56727
56927
 
56728
56928
  - "I am already in Claude Code/Codex/Cursor/etc." \u2192 regular \`vibe\` shell commands + \`vibe guide\` / \`vibe schema\`
56729
- - "I want to play and do not have an external coding agent" \u2192 \`vibe agent\`
56929
+ - "I do not use a coding agent" \u2192 the MCP server in Claude Desktop or ChatGPT (\`vibe host setup\`)
56730
56930
  - "I have a finished script + visual identity" \u2192 \`vibe build\`
56731
56931
  - "I want this to run again next month" \u2192 \`vibe run\`
56732
56932
 
@@ -56752,9 +56952,6 @@ agent calls them through a tool manifest; \`build\` calls a curated subset
56752
56952
  - \`vibe build\` is conceptually a 4-step pipeline; \`vibe run\` could express
56753
56953
  the same flow but build's STORYBOARD.md input + opinionated defaults are
56754
56954
  the value-add.
56755
- - \`vibe agent\`'s tool registry currently exposes primitives, not
56756
- orchestrators. Adding \`vibe.build\` / \`vibe.run\` as agent tools is on the
56757
- table for a future major.
56758
56955
  - \`vibe run --resume\` has no analog in build/agent; build's idempotent
56759
56956
  re-invoke covers the common case.
56760
56957
  `;
@@ -56861,17 +57058,17 @@ vibe generate motion "animated product logo reveal" --render -o logo-reveal.mp4
56861
57058
  },
56862
57059
  architecture: {
56863
57060
  title: "external agents / vibe agent / build / run",
56864
- summary: "Choose between host-agent shell use, optional built-in agent mode, storyboard builds, and YAML pipelines",
57061
+ summary: "Choose between host-agent shell use, MCP hosts, storyboard builds, and YAML pipelines",
56865
57062
  steps: [
56866
57063
  "If you are already in Claude Code/Codex/Cursor/etc., let that host drive normal `vibe` shell commands using `AGENTS.md`, `vibe guide`, and `vibe schema`.",
56867
- "Pick `vibe agent` only for optional built-in exploration when no external agent host is driving the CLI.",
57064
+ "Without a coding agent, connect the MCP server to Claude Desktop or ChatGPT with `vibe host setup`; `vibe agent` is deprecated.",
56868
57065
  "Pick build for STORYBOARD.md \u2192 MP4 with opinionated defaults.",
56869
57066
  "Pick run for repeatable, budget-capped, checkpointed YAML pipelines.",
56870
57067
  "Run `vibe doctor --test-keys` before any high-cost orchestrator to validate keys upfront.",
56871
57068
  "Use `vibe schema --list` (cost field, v0.84) to plan the budget per step before kicking off `vibe run --budget-usd`."
56872
57069
  ],
56873
57070
  relatedCommands: [
56874
- "vibe agent",
57071
+ "vibe host setup",
56875
57072
  "vibe build",
56876
57073
  "vibe run",
56877
57074
  "vibe doctor",
@@ -57046,10 +57243,11 @@ var OpenAIAdapter;
57046
57243
  var init_openai2 = __esm({
57047
57244
  "src/agent/adapters/openai.ts"() {
57048
57245
  "use strict";
57246
+ init_dist2();
57049
57247
  OpenAIAdapter = class {
57050
57248
  provider = "openai";
57051
57249
  client = null;
57052
- model = "gpt-5.4-mini";
57250
+ model = defaultModel("openai", "llm").id;
57053
57251
  async initialize(apiKey) {
57054
57252
  this.client = new OpenAI({ apiKey });
57055
57253
  }
@@ -57142,10 +57340,11 @@ var ClaudeAdapter;
57142
57340
  var init_claude2 = __esm({
57143
57341
  "src/agent/adapters/claude.ts"() {
57144
57342
  "use strict";
57343
+ init_dist2();
57145
57344
  ClaudeAdapter = class {
57146
57345
  provider = "claude";
57147
57346
  client = null;
57148
- model = "claude-sonnet-5-5";
57347
+ model = defaultModel("claude", "llm").id;
57149
57348
  async initialize(apiKey) {
57150
57349
  this.client = new Anthropic({ apiKey });
57151
57350
  }
@@ -57626,10 +57825,11 @@ var XAIAdapter;
57626
57825
  var init_xai = __esm({
57627
57826
  "src/agent/adapters/xai.ts"() {
57628
57827
  "use strict";
57828
+ init_dist2();
57629
57829
  XAIAdapter = class {
57630
57830
  provider = "xai";
57631
57831
  client = null;
57632
- model = "grok-4.3";
57832
+ model = defaultModel("grok", "llm").id;
57633
57833
  async initialize(apiKey) {
57634
57834
  this.client = new OpenAI2({
57635
57835
  apiKey,
@@ -70305,7 +70505,7 @@ var init_generate2 = __esm({
70305
70505
  cost: "high",
70306
70506
  title: "Generate Video",
70307
70507
  annotations: { readOnly: false, openWorld: true },
70308
- description: "Generate video using AI. Supports Seedance 2.0 via fal.ai, Grok, Kling, Runway, and Veo. Requires FAL_API_KEY, XAI_API_KEY, KLING_API_KEY, RUNWAY_API_SECRET, or GOOGLE_API_KEY.",
70508
+ description: "Generate video using AI. Supports Seedance 2.0 via fal.ai, Grok, Kling, Runway, Gemini Omni, and Veo (shuts down 2026-10-22). Requires FAL_API_KEY, XAI_API_KEY, KLING_API_KEY, RUNWAY_API_SECRET, or GOOGLE_API_KEY.",
70309
70509
  schema: external_exports.object({
70310
70510
  prompt: external_exports.string().describe("Text prompt describing the video"),
70311
70511
  provider: external_exports.enum(["seedance", "grok", "kling", "runway", "veo", "omni"]).optional().describe(
@@ -73588,7 +73788,7 @@ init_source();
73588
73788
  // package.json
73589
73789
  var package_default = {
73590
73790
  name: "@vibeframe/cli",
73591
- version: "0.115.3",
73791
+ version: "0.115.5",
73592
73792
  description: "Frontier AI video generation for coding agents - Seedance, Runway, Veo, and Kling on your own keys, behind a hard cost cap",
73593
73793
  private: false,
73594
73794
  license: "MIT",
@@ -73660,6 +73860,7 @@ var package_default = {
73660
73860
  start: "tsx src/index.ts",
73661
73861
  lint: "eslint --ext .ts,.tsx src/",
73662
73862
  test: "vitest run",
73863
+ "test:integration": "VIBE_TEST_INTEGRATION=1 vitest run",
73663
73864
  "test:watch": "vitest",
73664
73865
  pretest: "pnpm --dir ../core build && pnpm --dir ../ai-providers build && node build.js",
73665
73866
  prepublishOnly: "node build.js"
@@ -73973,6 +74174,7 @@ timelineCommand.command("add-source").description("Add a media source to the tim
73973
74174
  const spinner2 = ora("Adding source...").start();
73974
74175
  try {
73975
74176
  if (options.dryRun) {
74177
+ spinner2.stop();
73976
74178
  outputSuccess({
73977
74179
  command: "timeline add-source",
73978
74180
  startedAt,
@@ -74037,6 +74239,7 @@ timelineCommand.command("add-clip").description("Add a clip to the timeline").ar
74037
74239
  validateResourceId(sourceId);
74038
74240
  if (options.track) validateResourceId(options.track);
74039
74241
  if (options.dryRun) {
74242
+ spinner2.stop();
74040
74243
  outputSuccess({
74041
74244
  command: "timeline add-clip",
74042
74245
  startedAt,
@@ -74117,6 +74320,7 @@ timelineCommand.command("add-track").description("Add a new track").argument("<p
74117
74320
  const spinner2 = ora("Adding track...").start();
74118
74321
  try {
74119
74322
  if (options.dryRun) {
74323
+ spinner2.stop();
74120
74324
  outputSuccess({
74121
74325
  command: "timeline add-track",
74122
74326
  startedAt,
@@ -74178,6 +74382,7 @@ timelineCommand.command("add-effect").description("Add an effect to a clip").arg
74178
74382
  try {
74179
74383
  validateResourceId(clipId);
74180
74384
  if (options.dryRun) {
74385
+ spinner2.stop();
74181
74386
  outputSuccess({
74182
74387
  command: "timeline add-effect",
74183
74388
  startedAt,
@@ -74250,6 +74455,7 @@ timelineCommand.command("trim-clip").description("Trim a clip").argument("<proje
74250
74455
  try {
74251
74456
  validateResourceId(clipId);
74252
74457
  if (options.dryRun) {
74458
+ spinner2.stop();
74253
74459
  outputSuccess({
74254
74460
  command: "timeline trim-clip",
74255
74461
  startedAt,
@@ -74417,6 +74623,7 @@ timelineCommand.command("split-clip").description("Split a clip at a specific ti
74417
74623
  try {
74418
74624
  validateResourceId(clipId);
74419
74625
  if (options.dryRun) {
74626
+ spinner2.stop();
74420
74627
  outputSuccess({
74421
74628
  command: "timeline split-clip",
74422
74629
  startedAt,
@@ -74479,6 +74686,7 @@ timelineCommand.command("duplicate-clip").description("Duplicate a clip").argume
74479
74686
  try {
74480
74687
  validateResourceId(clipId);
74481
74688
  if (options.dryRun) {
74689
+ spinner2.stop();
74482
74690
  outputSuccess({
74483
74691
  command: "timeline duplicate-clip",
74484
74692
  startedAt,
@@ -74538,6 +74746,7 @@ timelineCommand.command("delete-clip").description("Delete a clip from the timel
74538
74746
  try {
74539
74747
  validateResourceId(clipId);
74540
74748
  if (options.dryRun) {
74749
+ spinner2.stop();
74541
74750
  outputSuccess({
74542
74751
  command: "timeline delete-clip",
74543
74752
  startedAt,
@@ -74591,6 +74800,7 @@ timelineCommand.command("move-clip").description("Move a clip to a new position"
74591
74800
  validateResourceId(clipId);
74592
74801
  if (options.track) validateResourceId(options.track);
74593
74802
  if (options.dryRun) {
74803
+ spinner2.stop();
74594
74804
  outputSuccess({
74595
74805
  command: "timeline move-clip",
74596
74806
  startedAt,
@@ -80086,6 +80296,7 @@ batchCommand.command("import").description("Import multiple media files from a d
80086
80296
  const spinner2 = ora("Scanning directory...").start();
80087
80297
  try {
80088
80298
  if (options.dryRun) {
80299
+ spinner2.stop();
80089
80300
  outputSuccess({
80090
80301
  command: "batch import",
80091
80302
  startedAt,
@@ -80163,6 +80374,7 @@ batchCommand.command("concat").description("Concatenate multiple sources into se
80163
80374
  const spinner2 = ora("Creating clips...").start();
80164
80375
  try {
80165
80376
  if (options.dryRun) {
80377
+ spinner2.stop();
80166
80378
  outputSuccess({
80167
80379
  command: "batch concat",
80168
80380
  startedAt,
@@ -80241,6 +80453,7 @@ batchCommand.command("apply-effect").description("Apply an effect to multiple cl
80241
80453
  const spinner2 = ora("Applying effects...").start();
80242
80454
  try {
80243
80455
  if (options.dryRun) {
80456
+ spinner2.stop();
80244
80457
  outputSuccess({
80245
80458
  command: "batch apply-effect",
80246
80459
  startedAt,
@@ -80312,6 +80525,7 @@ batchCommand.command("remove-clips").description("Remove multiple clips from the
80312
80525
  const spinner2 = ora("Removing clips...").start();
80313
80526
  try {
80314
80527
  if (options.dryRun) {
80528
+ spinner2.stop();
80315
80529
  outputSuccess({
80316
80530
  command: "batch remove-clips",
80317
80531
  startedAt,
@@ -80374,7 +80588,9 @@ batchCommand.command("info").description("Show batch processing statistics").arg
80374
80588
  const tracks = project.getTracks();
80375
80589
  const summary = project.getSummary();
80376
80590
  spinner2.stop();
80377
- console.log(source_default.bold("\nProject Statistics\n"));
80591
+ console.log(`
80592
+ ${source_default.bold("Project Statistics")}
80593
+ `);
80378
80594
  const videoSources = sources.filter((s) => s.type === "video").length;
80379
80595
  const audioSources = sources.filter((s) => s.type === "audio").length;
80380
80596
  const imageSources = sources.filter((s) => s.type === "image").length;
@@ -80768,7 +80984,7 @@ var AI_FEATURES = [
80768
80984
  label: "Videos",
80769
80985
  desc: "generate + extend",
80770
80986
  defaultProvider: "Seedance 2.0 via fal.ai (Artificial Analysis #2 t2v + i2v, since v0.57)",
80771
- alsoAvailable: "Grok Imagine, Kling, Runway Gen-4.5, Google Veo",
80987
+ alsoAvailable: "Grok Imagine, Kling, Runway Gen-4.5, Gemini Omni",
80772
80988
  keyHint: "1-2 keys",
80773
80989
  keys: [
80774
80990
  {
@@ -80847,16 +81063,16 @@ var AI_FEATURES = [
80847
81063
  defaultFor: { kind: "video", value: "runway" }
80848
81064
  },
80849
81065
  {
80850
- label: "Veo",
80851
- desc: "Google Veo video generation",
81066
+ label: "Gemini Omni",
81067
+ desc: "Google Gemini Omni video generation",
80852
81068
  key: {
80853
81069
  configKey: "google",
80854
81070
  envVar: "GOOGLE_API_KEY",
80855
81071
  name: "Google",
80856
81072
  url: "https://aistudio.google.com/apikey",
80857
- what: "Google Veo video generation"
81073
+ what: "Google Gemini Omni video generation"
80858
81074
  },
80859
- defaultFor: { kind: "video", value: "veo" }
81075
+ defaultFor: { kind: "video", value: "omni" }
80860
81076
  }
80861
81077
  ],
80862
81078
  tryCommand: 'vibe generate video "ocean waves" -o waves.mp4'
@@ -81537,7 +81753,7 @@ async function collectKeys(config2, keys) {
81537
81753
  }
81538
81754
  async function runCustomSetup(config2, scope = "user", opts = {}) {
81539
81755
  console.log(
81540
- source_default.bold("1. LLM Provider") + source_default.dim(" (optional built-in vibe agent / fallback)")
81756
+ source_default.bold("1. LLM Provider") + source_default.dim(" (only for the deprecated vibe agent)")
81541
81757
  );
81542
81758
  console.log(
81543
81759
  source_default.dim(
@@ -82390,7 +82606,7 @@ var AGENTS_MD = `# AGENTS.md
82390
82606
 
82391
82607
  This project uses [VibeFrame](https://github.com/vericontext/vibeframe) -
82392
82608
  a CLI for AI video generation on the user's own provider keys (Seedance,
82393
- Runway, Veo, Kling), behind dry runs and a hard \`--max-cost\` ceiling.
82609
+ Runway, Kling, Gemini Omni), behind dry runs and a hard \`--max-cost\` ceiling.
82394
82610
  The \`vibe\` command provides 80+ subcommands for video generation,
82395
82611
  editing, and analysis. Cost tiers below - **always confirm with the user
82396
82612
  before running High / Very High tier commands**.
@@ -82715,7 +82931,7 @@ function renderEnvExample(opts = {}) {
82715
82931
  ${fallback2}# \u2500\u2500 LLM provider / optional \`vibe agent\` fallback (pick one) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
82716
82932
  ANTHROPIC_API_KEY= # Claude - recommended default
82717
82933
  OPENAI_API_KEY= # GPT-5.4 mini \xB7 Whisper \xB7 gpt-image-2.5
82718
- GOOGLE_API_KEY= # Gemini \xB7 Veo
82934
+ GOOGLE_API_KEY= # Gemini \xB7 Omni video
82719
82935
  XAI_API_KEY= # Grok image+video
82720
82936
  OPENROUTER_API_KEY= # multiplexes any provider above
82721
82937
  # OLLAMA_HOST=http://localhost:11434 # offline, no key needed
@@ -86857,7 +87073,9 @@ async function promptConfirm2(toolName, args, cost) {
86857
87073
  const defaultYes = cost !== "very-high";
86858
87074
  return promptConfirm("", defaultYes);
86859
87075
  }
87076
+ var AGENT_DEPRECATION_NOTICE = "`vibe agent` is deprecated and will be removed before 1.0. Drive vibe from Claude Code, Codex, or Cursor, or connect the MCP server to Claude Desktop or ChatGPT (`vibe host setup`).";
86860
87077
  async function startAgent(options = {}) {
87078
+ console.error(source_default.yellow(`Deprecated: ${AGENT_DEPRECATION_NOTICE}`));
86861
87079
  const isNonInteractive = !!options.input;
86862
87080
  const confirmAlways = options.confirm || false;
86863
87081
  const noConfirm = options.noConfirm || false;
@@ -87068,7 +87286,7 @@ async function startAgent(options = {}) {
87068
87286
  rl.prompt();
87069
87287
  });
87070
87288
  }
87071
- var agentCommand = new Command("agent").description("Optional built-in natural-language agent (fallback when no external coding agent is driving vibe)").option("-p, --provider <provider>", "LLM provider (openai, claude, gemini, ollama, xai, openrouter, evolink)", "openai").option("-m, --model <model>", "Model to use (provider-specific)").option("--project <path>", "Timeline file or directory to load").option("-v, --verbose", "Show verbose output including tool calls").option("--max-turns <n>", "Maximum turns per request", "10").option("-i, --input <query>", "Run a single query and exit (non-interactive)").option("-c, --confirm", "Confirm before every tool - broadens the default cost gate (paid only) to all calls").option("--no-confirm", "Disable all confirm prompts including the high/very-high cost gate (CI / automation)").option("--budget-usd <usd>", "Reject tool calls past this cumulative USD ceiling using conservative tier estimates", parseFloat).action(async (options) => {
87289
+ var agentCommand = new Command("agent").description("[Deprecated, removed before 1.0] Built-in natural-language agent. Use a host agent or the MCP server instead").option("-p, --provider <provider>", "LLM provider (openai, claude, gemini, ollama, xai, openrouter, evolink)", "openai").option("-m, --model <model>", "Model to use (provider-specific)").option("--project <path>", "Timeline file or directory to load").option("-v, --verbose", "Show verbose output including tool calls").option("--max-turns <n>", "Maximum turns per request", "10").option("-i, --input <query>", "Run a single query and exit (non-interactive)").option("-c, --confirm", "Confirm before every tool - broadens the default cost gate (paid only) to all calls").option("--no-confirm", "Disable all confirm prompts including the high/very-high cost gate (CI / automation)").option("--budget-usd <usd>", "Reject tool calls past this cumulative USD ceiling using conservative tier estimates", parseFloat).action(async (options) => {
87072
87290
  await startAgent(options);
87073
87291
  });
87074
87292
  function getEnvVar(provider) {
@@ -87567,7 +87785,8 @@ program2.name("vibe").showSuggestionAfterError(true).description("VibeFrame CLI
87567
87785
  process.stderr.write(JSON.stringify(err, null, 2) + "\n");
87568
87786
  } else {
87569
87787
  write2(source_default.red(str.trim()) + "\n");
87570
- write2(source_default.dim("Run with --help for full options.\n"));
87788
+ write2(`${source_default.dim("Run with --help for full options.")}
87789
+ `);
87571
87790
  }
87572
87791
  }
87573
87792
  }).configureHelp({
@@ -87689,7 +87908,7 @@ program2.addCommand(doctorCommand);
87689
87908
  program2.addCommand(hostCommand);
87690
87909
  program2.addCommand(demoCommand, { hidden: true });
87691
87910
  program2.addCommand(runCommand);
87692
- program2.addCommand(agentCommand);
87911
+ program2.addCommand(agentCommand, { hidden: true });
87693
87912
  program2.addCommand(projectCommand, { hidden: true });
87694
87913
  program2.addCommand(sceneCommand, { hidden: true });
87695
87914
  program2.addCommand(timelineCommand, { hidden: true });
@@ -87720,7 +87939,8 @@ function propagateErrorHandling(cmd) {
87720
87939
  process.stderr.write(JSON.stringify(err, null, 2) + "\n");
87721
87940
  } else {
87722
87941
  write2(source_default.red(str.trim()) + "\n");
87723
- write2(source_default.dim("Run with --help for full options.\n"));
87942
+ write2(`${source_default.dim("Run with --help for full options.")}
87943
+ `);
87724
87944
  }
87725
87945
  }
87726
87946
  });