@vibeframe/mcp-server 0.104.1 → 0.104.3

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 +96 -42
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -7613,7 +7613,7 @@ YAML cues that drive narration, backdrop generation, and timing.
7613
7613
 
7614
7614
  \`\`\`yaml
7615
7615
  narration: "Introduce the promise in one crisp sentence."
7616
- backdrop: "Cinematic abstract technology backdrop, precise light, premium editorial feel"
7616
+ backdrop: "Topic-aligned editorial background plate, abstract visual system, no readable text, no logos, no consumer products, clean negative space for HTML overlays"
7617
7617
  duration: 4
7618
7618
  \`\`\`
7619
7619
 
@@ -7624,7 +7624,7 @@ screen and one spoken breath.
7624
7624
 
7625
7625
  \`\`\`yaml
7626
7626
  narration: "Show the mechanism or proof point that makes the promise believable."
7627
- backdrop: "Layered interface details, subtle motion trails, high-contrast product storytelling"
7627
+ backdrop: "Topic-aligned analytical background plate, abstract dashboard structure, no readable text, no product photos, no shoes, no unrelated objects"
7628
7628
  duration: 4
7629
7629
  \`\`\`
7630
7630
 
@@ -7635,7 +7635,7 @@ before/after.
7635
7635
 
7636
7636
  \`\`\`yaml
7637
7637
  narration: "Close with the action the viewer should remember."
7638
- backdrop: "Resolved hero frame, confident final composition, clean negative space"
7638
+ backdrop: "Resolved editorial background plate, confident final composition, clean negative space, no readable text, no logos, no unrelated products"
7639
7639
  duration: 4
7640
7640
  \`\`\`
7641
7641
 
@@ -7688,6 +7688,25 @@ consult this file \u2014 run the generate command directly.
7688
7688
  Browse named styles: \`vibe scene list-styles\`. Re-seed from one with
7689
7689
  \`vibe scene init . --visual-style "Swiss Pulse"\` (idempotent).
7690
7690
 
7691
+ ## Provider keys and project scope
7692
+
7693
+ Use VibeFrame CLI generation for project assets:
7694
+ \`vibe generate image|video|speech ...\`. This lets VibeFrame use keys
7695
+ from \`vibe setup --scope project\`.
7696
+
7697
+ Project-scope keys may live in a parent directory, for example
7698
+ \`../.vibeframe/config.yaml\` when this scene was created by
7699
+ \`vibe init launch\`. The \`vibe\` CLI searches upward automatically, so do
7700
+ not decide keys are missing just because \`.vibeframe/config.yaml\` is not
7701
+ inside this scene folder.
7702
+
7703
+ To verify scope without exposing secrets, run \`vibe doctor --json\` from
7704
+ this directory and inspect \`data.scope.activeScope\` plus
7705
+ \`data.scope.project.configPath\`. Never print config contents. Do not use
7706
+ a host agent's built-in image/audio generation tool for VibeFrame project
7707
+ assets unless the user explicitly requests an external, non-VibeFrame
7708
+ asset.
7709
+
7691
7710
  ## Skills \u2014 USE THESE FIRST
7692
7711
 
7693
7712
  @SKILL.md
@@ -7749,7 +7768,11 @@ npx hyperframes render
7749
7768
  \`\`\`
7750
7769
  4. Videos use \`muted\` with a separate \`<audio>\` element for the audio track.
7751
7770
  5. Sub-compositions use \`data-composition-src="compositions/file.html"\`.
7752
- 6. Only deterministic logic \u2014 no \`Date.now()\`, \`Math.random()\`, or network fetches.
7771
+ 6. For render-stable text, do not apply continuous \`scale\`, \`x\`, \`y\`, or
7772
+ \`filter\` tweens to \`.scene-content\` or any ancestor containing live text.
7773
+ Animate background/media layers instead; text/cards should enter briefly and
7774
+ then hold still at their final CSS positions.
7775
+ 7. Only deterministic logic \u2014 no \`Date.now()\`, \`Math.random()\`, or network fetches.
7753
7776
 
7754
7777
  ## Linting \u2014 run after changes
7755
7778
 
@@ -25916,21 +25939,8 @@ async function prompt(question, hidden = false) {
25916
25939
  });
25917
25940
  }
25918
25941
  async function getApiKey(envVar, providerName, optionValue) {
25919
- if (optionValue) {
25920
- return optionValue;
25921
- }
25922
- const providerKey = providerKeyForEnvVar(envVar);
25923
- if (providerKey) {
25924
- const configKey = await getApiKeyFromConfig(providerKey);
25925
- if (configKey) {
25926
- return configKey;
25927
- }
25928
- }
25929
- loadEnv();
25930
- const envValue = getEnvValue2(envVar);
25931
- if (envValue) {
25932
- return envValue;
25933
- }
25942
+ const configuredKey = await getConfiguredApiKey(envVar, optionValue);
25943
+ if (configuredKey) return configuredKey;
25934
25944
  if (!process.stdin.isTTY) {
25935
25945
  return null;
25936
25946
  }
@@ -25951,6 +25961,19 @@ async function getApiKey(envVar, providerName, optionValue) {
25951
25961
  }
25952
25962
  return apiKey.trim();
25953
25963
  }
25964
+ async function getConfiguredApiKey(envVar, optionValue, options = {}) {
25965
+ if (optionValue) return optionValue;
25966
+ const providerKey = providerKeyForEnvVar(envVar);
25967
+ if (providerKey) {
25968
+ const configKey = await getApiKeyFromConfig(providerKey, { cwd: options.cwd });
25969
+ if (configKey) return configKey;
25970
+ }
25971
+ loadEnv();
25972
+ return getEnvValue2(envVar);
25973
+ }
25974
+ async function hasConfiguredApiKey(envVar, optionValue, options = {}) {
25975
+ return Boolean(await getConfiguredApiKey(envVar, optionValue, options));
25976
+ }
25954
25977
  function hasApiKey(envVar) {
25955
25978
  loadEnv();
25956
25979
  return !!getEnvValue2(envVar);
@@ -26047,7 +26070,7 @@ var init_api_key = __esm({
26047
26070
 
26048
26071
  // ../cli/src/commands/_shared/tts-resolve.ts
26049
26072
  async function resolveTtsProvider(preferred = "auto") {
26050
- const choice = preferred === "auto" ? hasApiKey("ELEVENLABS_API_KEY") ? "elevenlabs" : "kokoro" : preferred;
26073
+ const choice = preferred === "auto" ? await getConfiguredApiKey("ELEVENLABS_API_KEY") ? "elevenlabs" : "kokoro" : preferred;
26051
26074
  if (choice === "elevenlabs") {
26052
26075
  return buildElevenLabs();
26053
26076
  }
@@ -26091,7 +26114,6 @@ var init_tts_resolve = __esm({
26091
26114
  "use strict";
26092
26115
  init_dist();
26093
26116
  init_api_key();
26094
- init_api_key();
26095
26117
  TtsKeyMissingError = class extends Error {
26096
26118
  constructor(provider) {
26097
26119
  super(
@@ -449161,13 +449183,18 @@ Requirements (non-negotiable):
449161
449183
  producer's seek lands past the timeline's natural end and visibility state
449162
449184
  goes stale \u2014 the hold phase renders BLACK. Anchor the timeline to the full
449163
449185
  beat duration via either:
449164
- 1. A subtle idle motion spanning 0\u2192duration on a parent element, e.g.
449165
- \`tl.fromTo(".scene-content", { scale: 1.0 }, { scale: 1.015, duration: <beat>, ease: "none" }, 0);\`
449186
+ 1. A subtle idle motion spanning 0\u2192duration on a background/media layer,
449187
+ e.g. \`tl.fromTo(".backdrop", { scale: 1.0 }, { scale: 1.015, duration: <beat>, ease: "none" }, 0);\`
449166
449188
  (Ken-Burns, breathing opacity, gradient drift \u2014 should be barely
449167
449189
  perceptible so it doesn't compete with entry/exit beats).
449168
449190
  2. OR an explicit \`tl.set(target, { ...natural state... }, <beat - 0.001>)\`
449169
449191
  anchor at the end.
449170
449192
  This is the #2 source of "text disappears mid-beat" bugs after \`.clip\` sizing.
449193
+ - Do not apply continuous \`scale\`, \`x\`, \`y\`, \`filter\`, or other transform
449194
+ tweens to \`.scene-content\` or any ancestor that contains live text/cards.
449195
+ Animate the backdrop/media plane instead; let text enter briefly, then hold
449196
+ still at its final CSS position. Continuous transforms on text ancestors can
449197
+ create subpixel shimmer in screenshot-captured renders.
449171
449198
  - Timed children inside the composition have \`class="clip"\` plus
449172
449199
  \`data-start\`, \`data-duration\`, \`data-track-index\`.
449173
449200
  - If \`assets/backdrop-${ctx.beat.id}.png\` exists, use that local file as the
@@ -449237,7 +449264,9 @@ Reference shape (verbatim \u2014 match this skeleton exactly, no DOCTYPE / html
449237
449264
  const tl = gsap.timeline({ paused: true });
449238
449265
  // Idle motion spanning full beat duration \u2014 required to keep timeline
449239
449266
  // length aligned with data-duration (otherwise hold phase goes black).
449240
- tl.fromTo(".scene-content", { scale: 1.0 }, { scale: 1.015, duration: <sec>, ease: "none" }, 0);
449267
+ // Keep continuous motion on the background/media layer so live text does
449268
+ // not shimmer from subpixel resampling.
449269
+ tl.fromTo(".backdrop", { scale: 1.0 }, { scale: 1.015, duration: <sec>, ease: "none" }, 0);
449241
449270
  // entry tweens
449242
449271
  window.__timelines["${compositionId}"] = tl;
449243
449272
  </script>
@@ -451179,6 +451208,32 @@ var init_build_asset_metadata = __esm({
451179
451208
  }
451180
451209
  });
451181
451210
 
451211
+ // ../cli/src/commands/_shared/build-backdrop-prompt.ts
451212
+ function augmentBackdropPrompt(cue) {
451213
+ const trimmed = cue.trim();
451214
+ const lower = trimmed.toLowerCase();
451215
+ const requestsTextOrMarks = /\b(text|typography|title|headline|label|caption|logo|logos|wordmark|brand mark|brand marks)\b/.test(lower);
451216
+ const forbidsTextOrMarks = /\b(no|without|avoid)\s+(readable\s+)?(text|typography|titles?|headlines?|labels?|captions?|brand\s+logos?|logos?|wordmarks?|brand\s+marks?)\b/.test(
451217
+ lower
451218
+ );
451219
+ const allowsTextOrMarks = requestsTextOrMarks && !forbidsTextOrMarks;
451220
+ const overlayContract = allowsTextOrMarks ? "The image is a video background or end-card plate; do not add any text, logos, charts, or UI beyond what the scene cue explicitly requests." : "The image is a background only; HTML overlays will provide all final text, charts, logos, and UI labels.";
451221
+ const textRule = allowsTextOrMarks ? "If text, logos, or brand marks are explicitly requested, keep them minimal, legible, and do not invent extras." : "No readable text, labels, UI copy, logos, brand marks, watermarks, or invented typography.";
451222
+ return [
451223
+ "Create a 16:9 video background plate for a HyperFrames scene.",
451224
+ overlayContract,
451225
+ `Scene cue: ${trimmed}`,
451226
+ textRule,
451227
+ "Avoid unrelated consumer product photography, shoes, packaging, food, people, celebrity faces, advertisements, and random objects unless explicitly requested by the scene cue.",
451228
+ "Leave generous negative space for overlay text and cards. Keep the result topic-aligned, editorial, cinematic, and non-distracting."
451229
+ ].join(" ");
451230
+ }
451231
+ var init_build_backdrop_prompt = __esm({
451232
+ "../cli/src/commands/_shared/build-backdrop-prompt.ts"() {
451233
+ "use strict";
451234
+ }
451235
+ });
451236
+
451182
451237
  // ../cli/src/commands/_shared/storyboard-edit.ts
451183
451238
  function validateStoryboardMarkdown(markdown) {
451184
451239
  const parsed = parseStoryboard(markdown);
@@ -451463,6 +451518,7 @@ async function createBuildPlan(opts) {
451463
451518
  const voice = stringOrUndefined3(cue.voice) ?? resolved.voice;
451464
451519
  const narrationText = stringOrUndefined3(cue.narration);
451465
451520
  const backdropPrompt = stringOrUndefined3(cue.backdrop);
451521
+ const augmentedBackdropPrompt = backdropPrompt ? augmentBackdropPrompt(backdropPrompt) : null;
451466
451522
  const videoPrompt = stringOrUndefined3(cue.video);
451467
451523
  const musicPrompt = stringOrUndefined3(cue.music);
451468
451524
  const genericReference = resolveGenericAssetReference(projectDir, cue.asset);
@@ -451482,9 +451538,9 @@ async function createBuildPlan(opts) {
451482
451538
  voice,
451483
451539
  ext: resolved.narration.resolved === "elevenlabs" ? "mp3" : "wav"
451484
451540
  }) : null;
451485
- const backdropCache = backdropPrompt && !backdropReference ? backdropCacheDescriptor({
451541
+ const backdropCache = augmentedBackdropPrompt && !backdropReference ? backdropCacheDescriptor({
451486
451542
  beatId: beat.id,
451487
- cue: backdropPrompt,
451543
+ cue: augmentedBackdropPrompt,
451488
451544
  provider: resolved.image.resolved,
451489
451545
  quality: imageQuality,
451490
451546
  size: imageSize2,
@@ -452014,6 +452070,7 @@ var init_build_plan = __esm({
452014
452070
  init_build_asset_reference();
452015
452071
  init_build_cache();
452016
452072
  init_build_asset_metadata();
452073
+ init_build_backdrop_prompt();
452017
452074
  init_composer_resolve();
452018
452075
  init_storyboard_parse();
452019
452076
  init_project_config();
@@ -452436,7 +452493,7 @@ async function executeVideoGenerate(options) {
452436
452493
  fal: "FAL_API_KEY"
452437
452494
  };
452438
452495
  const envKey = envKeyMap[provider] || "";
452439
- const key2 = apiKey || (hasApiKey(envKey) ? process.env[envKey] : void 0);
452496
+ const key2 = await getConfiguredApiKey(envKey, apiKey);
452440
452497
  if (!key2) return { success: false, error: `${envKeyMap[provider]} required for ${provider}` };
452441
452498
  let referenceImage;
452442
452499
  let referenceImageBuffer;
@@ -452868,7 +452925,7 @@ var init_validate = __esm({
452868
452925
  // ../cli/src/commands/generate/music-status.ts
452869
452926
  async function executeMusicStatus(options) {
452870
452927
  try {
452871
- const apiKey = options.apiKey ?? (hasApiKey("REPLICATE_API_TOKEN") ? await getApiKeyFromConfig("replicate") || process.env.REPLICATE_API_TOKEN : null);
452928
+ const apiKey = await getConfiguredApiKey("REPLICATE_API_TOKEN", options.apiKey);
452872
452929
  if (!apiKey)
452873
452930
  return { success: false, error: "REPLICATE_API_TOKEN required for music status" };
452874
452931
  const replicate = new ReplicateProvider();
@@ -452941,7 +452998,6 @@ var init_music_status = __esm({
452941
452998
  init_source();
452942
452999
  init_dist();
452943
453000
  init_api_key();
452944
- init_config();
452945
453001
  init_output();
452946
453002
  }
452947
453003
  });
@@ -453741,7 +453797,7 @@ async function executeMusic(options) {
453741
453797
  try {
453742
453798
  const provider = options.provider || "elevenlabs";
453743
453799
  if (provider === "elevenlabs") {
453744
- const apiKey2 = hasApiKey("ELEVENLABS_API_KEY") ? await getApiKeyFromConfig("elevenlabs") || process.env.ELEVENLABS_API_KEY : null;
453800
+ const apiKey2 = await getConfiguredApiKey("ELEVENLABS_API_KEY");
453745
453801
  if (!apiKey2)
453746
453802
  return {
453747
453803
  success: false,
@@ -453761,7 +453817,7 @@ async function executeMusic(options) {
453761
453817
  await writeFile18(outputPath2, result2.audioBuffer);
453762
453818
  return { success: true, outputPath: outputPath2, provider: "elevenlabs", duration: duration2 };
453763
453819
  }
453764
- const apiKey = hasApiKey("REPLICATE_API_TOKEN") ? await getApiKeyFromConfig("replicate") || process.env.REPLICATE_API_TOKEN : null;
453820
+ const apiKey = await getConfiguredApiKey("REPLICATE_API_TOKEN");
453765
453821
  if (!apiKey)
453766
453822
  return {
453767
453823
  success: false,
@@ -453996,7 +454052,6 @@ var init_music = __esm({
453996
454052
  init_ora();
453997
454053
  init_dist();
453998
454054
  init_api_key();
453999
- init_config();
454000
454055
  init_output();
454001
454056
  init_validate();
454002
454057
  init_status_jobs();
@@ -454770,8 +454825,9 @@ async function dispatchNarration(beat, ctx) {
454770
454825
  async function dispatchBackdrop(beat, ctx) {
454771
454826
  const reference = assetReferenceForBeat(ctx.projectDir, "backdrop", beat);
454772
454827
  if (reference) return referencePrimitiveOutcome("backdrop", beat, ctx, reference);
454773
- const prompt3 = stringOrUndefined4(beat.cues?.backdrop);
454774
- if (!prompt3) return { status: "no-cue" };
454828
+ const cue = stringOrUndefined4(beat.cues?.backdrop);
454829
+ if (!cue) return { status: "no-cue" };
454830
+ const prompt3 = augmentBackdropPrompt(cue);
454775
454831
  const rel = `assets/backdrop-${beat.id}.png`;
454776
454832
  const abs = join33(ctx.projectDir, rel);
454777
454833
  const size = ctx.imageSize ?? "1536x1024";
@@ -455749,6 +455805,7 @@ var init_scene_build = __esm({
455749
455805
  init_root_sync();
455750
455806
  init_build_cache();
455751
455807
  init_build_asset_metadata();
455808
+ init_build_backdrop_prompt();
455752
455809
  init_ai_video();
455753
455810
  init_music();
455754
455811
  init_status_jobs();
@@ -462409,7 +462466,7 @@ import { resolve as resolve54 } from "node:path";
462409
462466
  import { writeFile as writeFile32 } from "node:fs/promises";
462410
462467
  async function executeSoundEffect(options) {
462411
462468
  try {
462412
- const apiKey = hasApiKey("ELEVENLABS_API_KEY") ? await getApiKeyFromConfig("elevenlabs") || process.env.ELEVENLABS_API_KEY : null;
462469
+ const apiKey = await getConfiguredApiKey("ELEVENLABS_API_KEY");
462413
462470
  if (!apiKey)
462414
462471
  return {
462415
462472
  success: false,
@@ -462508,7 +462565,6 @@ var init_sound_effect = __esm({
462508
462565
  init_ora();
462509
462566
  init_dist();
462510
462567
  init_api_key();
462511
- init_config();
462512
462568
  init_output();
462513
462569
  init_validate();
462514
462570
  }
@@ -462590,7 +462646,7 @@ import { resolve as resolve55, dirname as dirname30 } from "node:path";
462590
462646
  import { writeFile as writeFile33, mkdir as mkdir23 } from "node:fs/promises";
462591
462647
  async function executeBackground(options) {
462592
462648
  try {
462593
- const apiKey = options.apiKey ?? (hasApiKey("OPENAI_API_KEY") ? await getApiKeyFromConfig("openai") || process.env.OPENAI_API_KEY : null);
462649
+ const apiKey = await getConfiguredApiKey("OPENAI_API_KEY", options.apiKey);
462594
462650
  if (!apiKey)
462595
462651
  return { success: false, error: "OPENAI_API_KEY required for background generation" };
462596
462652
  const openaiImage = new OpenAIImageProvider();
@@ -462721,7 +462777,6 @@ var init_background = __esm({
462721
462777
  init_ora();
462722
462778
  init_dist();
462723
462779
  init_api_key();
462724
- init_config();
462725
462780
  init_output();
462726
462781
  init_validate();
462727
462782
  }
@@ -462962,7 +463017,7 @@ import { resolve as resolve57 } from "node:path";
462962
463017
  import { writeFile as writeFile35 } from "node:fs/promises";
462963
463018
  async function executeSpeech(options) {
462964
463019
  try {
462965
- const apiKey = hasApiKey("ELEVENLABS_API_KEY") ? await getApiKeyFromConfig("elevenlabs") || process.env.ELEVENLABS_API_KEY : null;
463020
+ const apiKey = await getConfiguredApiKey("ELEVENLABS_API_KEY");
462966
463021
  if (!apiKey)
462967
463022
  return {
462968
463023
  success: false,
@@ -463177,7 +463232,6 @@ var init_speech = __esm({
463177
463232
  init_dist();
463178
463233
  init_api_key();
463179
463234
  init_tty();
463180
- init_config();
463181
463235
  init_output();
463182
463236
  init_validate();
463183
463237
  }
@@ -463978,7 +464032,7 @@ Examples:
463978
464032
  )
463979
464033
  );
463980
464034
  }
463981
- if (providerEnvMap[provider] && !hasApiKey(providerEnvMap[provider]) && !options.apiKey) {
464035
+ if (providerEnvMap[provider] && !await hasConfiguredApiKey(providerEnvMap[provider], options.apiKey)) {
463982
464036
  const resolved = resolveProvider("image");
463983
464037
  if (resolved) {
463984
464038
  log(source_default.dim(` ${provider} key not found. Using ${resolved.label} instead.`));
@@ -465418,7 +465472,7 @@ Examples:
465418
465472
  );
465419
465473
  provider = "seedance";
465420
465474
  }
465421
- if (videoEnvMap[provider] && !hasApiKey(videoEnvMap[provider]) && !options.apiKey) {
465475
+ if (videoEnvMap[provider] && !await hasConfiguredApiKey(videoEnvMap[provider], options.apiKey)) {
465422
465476
  const resolved = resolveProvider("video");
465423
465477
  if (resolved) {
465424
465478
  log(source_default.dim(` ${provider} key not found. Using ${resolved.label} instead.`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/mcp-server",
3
- "version": "0.104.1",
3
+ "version": "0.104.3",
4
4
  "description": "VibeFrame MCP Server - AI-native video editing via Model Context Protocol",
5
5
  "type": "module",
6
6
  "bin": {
@@ -57,8 +57,8 @@
57
57
  "tsx": "^4.21.0",
58
58
  "typescript": "^5.3.3",
59
59
  "vitest": "^1.2.2",
60
- "@vibeframe/cli": "0.104.1",
61
- "@vibeframe/core": "0.104.1"
60
+ "@vibeframe/cli": "0.104.3",
61
+ "@vibeframe/core": "0.104.3"
62
62
  },
63
63
  "engines": {
64
64
  "node": ">=20"