@vibeframe/cli 0.106.7 → 0.106.9

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.
@@ -17,6 +17,7 @@ import { type SceneAspect } from "./scene-project.js";
17
17
  export type RenderFps = 24 | 30 | 60;
18
18
  export type RenderQuality = "draft" | "standard" | "high";
19
19
  export type RenderFormat = "mp4" | "webm" | "mov";
20
+ export type RenderOpenAction = "open" | "reveal";
20
21
  export interface SceneRenderOptions {
21
22
  /** Project directory (defaults to cwd). */
22
23
  projectDir?: string;
@@ -33,6 +34,10 @@ export interface SceneRenderOptions {
33
34
  /** Hyperframes capture worker count. Default 1 (the existing backend's
34
35
  * default — auto-worker mode times out on small comps). */
35
36
  workers?: number;
37
+ /** Open the rendered media in the OS default app after a successful render. */
38
+ openAfterRender?: boolean;
39
+ /** Reveal the rendered media in Finder/file manager after a successful render. */
40
+ revealInFinder?: boolean;
36
41
  signal?: AbortSignal;
37
42
  onProgress?: (pct: number, stage: string) => void;
38
43
  }
@@ -42,6 +47,13 @@ export interface SceneRenderResult {
42
47
  beat?: string | null;
43
48
  root?: string;
44
49
  outputPath?: string;
50
+ absoluteOutputPath?: string;
51
+ openCommand?: string;
52
+ revealCommand?: string;
53
+ opened?: boolean;
54
+ revealed?: boolean;
55
+ openError?: string;
56
+ revealError?: string;
45
57
  durationMs?: number;
46
58
  framesRendered?: number;
47
59
  totalFrames?: number;
@@ -59,6 +71,11 @@ export interface SceneRenderResult {
59
71
  error?: string;
60
72
  retryWith?: string[];
61
73
  }
74
+ export interface MediaOpenCommand {
75
+ command: string;
76
+ args: string[];
77
+ display: string;
78
+ }
62
79
  /** Map a quality preset to an x264 CRF (lower = higher quality). */
63
80
  export declare function qualityToCrf(quality?: RenderQuality): number;
64
81
  /**
@@ -85,6 +102,7 @@ export declare function buildRenderConfig(opts: {
85
102
  workers?: number;
86
103
  entryFile?: string;
87
104
  }): RenderConfig;
105
+ export declare function buildMediaOpenCommand(action: RenderOpenAction, filePath: string, platform?: NodeJS.Platform): MediaOpenCommand;
88
106
  /**
89
107
  * Render a scene project. Mirrors the contract of the existing Hyperframes
90
108
  * backend (preflight → execute → structured result), but skips the
package/dist/index.js CHANGED
@@ -31472,11 +31472,11 @@ registerRoot(Root);
31472
31472
  return dir;
31473
31473
  }
31474
31474
  async function this_npmInstall(dir) {
31475
- const { execFile: execFile2 } = await import("node:child_process");
31476
- const { promisify: promisify2 } = await import("node:util");
31477
- const execFileAsync2 = promisify2(execFile2);
31475
+ const { execFile: execFile3 } = await import("node:child_process");
31476
+ const { promisify: promisify3 } = await import("node:util");
31477
+ const execFileAsync3 = promisify3(execFile3);
31478
31478
  try {
31479
- await execFileAsync2("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund"], {
31479
+ await execFileAsync3("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund"], {
31480
31480
  cwd: dir,
31481
31481
  timeout: 18e4
31482
31482
  });
@@ -31500,13 +31500,13 @@ async function renderMotion(options) {
31500
31500
  );
31501
31501
  try {
31502
31502
  const entryPoint = join9(dir, "Root.tsx");
31503
- const { execFile: execFile2 } = await import("node:child_process");
31504
- const { promisify: promisify2 } = await import("node:util");
31505
- const execFileAsync2 = promisify2(execFile2);
31503
+ const { execFile: execFile3 } = await import("node:child_process");
31504
+ const { promisify: promisify3 } = await import("node:util");
31505
+ const execFileAsync3 = promisify3(execFile3);
31506
31506
  if (transparent) {
31507
31507
  const webmOut = options.outputPath.replace(/\.\w+$/, ".webm");
31508
31508
  try {
31509
- await execFileAsync2("npx", [
31509
+ await execFileAsync3("npx", [
31510
31510
  "remotion",
31511
31511
  "render",
31512
31512
  entryPoint,
@@ -31523,7 +31523,7 @@ async function renderMotion(options) {
31523
31523
  } catch {
31524
31524
  }
31525
31525
  try {
31526
- await execFileAsync2("npx", [
31526
+ await execFileAsync3("npx", [
31527
31527
  "remotion",
31528
31528
  "render",
31529
31529
  entryPoint,
@@ -31543,7 +31543,7 @@ async function renderMotion(options) {
31543
31543
  }
31544
31544
  }
31545
31545
  const mp4Out = options.outputPath.replace(/\.\w+$/, ".mp4");
31546
- await execFileAsync2("npx", [
31546
+ await execFileAsync3("npx", [
31547
31547
  "remotion",
31548
31548
  "render",
31549
31549
  entryPoint,
@@ -59857,15 +59857,18 @@ var init_scene_audio_mux = __esm({
59857
59857
  // src/commands/_shared/scene-render.ts
59858
59858
  var scene_render_exports = {};
59859
59859
  __export(scene_render_exports, {
59860
+ buildMediaOpenCommand: () => buildMediaOpenCommand,
59860
59861
  buildRenderConfig: () => buildRenderConfig,
59861
59862
  defaultOutputPath: () => defaultOutputPath,
59862
59863
  executeSceneRender: () => executeSceneRender,
59863
59864
  prepareBeatRenderRoot: () => prepareBeatRenderRoot,
59864
59865
  qualityToCrf: () => qualityToCrf2
59865
59866
  });
59867
+ import { execFile as execFile2 } from "node:child_process";
59866
59868
  import { existsSync as existsSync46 } from "node:fs";
59867
59869
  import { mkdir as mkdir24, readFile as readFile43, stat as stat8, writeFile as writeFile48 } from "node:fs/promises";
59868
59870
  import { resolve as resolve62, relative as relative11, dirname as dirname22, basename as basename21, join as join22, isAbsolute as isAbsolute6 } from "node:path";
59871
+ import { promisify as promisify2 } from "node:util";
59869
59872
  import {
59870
59873
  createRenderJob,
59871
59874
  executeRenderJob
@@ -59892,6 +59895,38 @@ function buildRenderConfig(opts) {
59892
59895
  workers: opts.workers ?? 1
59893
59896
  };
59894
59897
  }
59898
+ function buildMediaOpenCommand(action, filePath, platform3 = process.platform) {
59899
+ if (platform3 === "darwin") {
59900
+ const args = action === "reveal" ? ["-R", filePath] : [filePath];
59901
+ return {
59902
+ command: "open",
59903
+ args,
59904
+ display: ["open", ...args].map(quoteCliArg).join(" ")
59905
+ };
59906
+ }
59907
+ if (platform3 === "win32") {
59908
+ if (action === "reveal") {
59909
+ const args2 = [`/select,${filePath}`];
59910
+ return {
59911
+ command: "explorer",
59912
+ args: args2,
59913
+ display: ["explorer", ...args2].map(quoteCliArg).join(" ")
59914
+ };
59915
+ }
59916
+ const args = ["/c", "start", "", filePath];
59917
+ return {
59918
+ command: "cmd",
59919
+ args,
59920
+ display: ["cmd", ...args].map(quoteCliArg).join(" ")
59921
+ };
59922
+ }
59923
+ const target = action === "reveal" ? dirname22(filePath) : filePath;
59924
+ return {
59925
+ command: "xdg-open",
59926
+ args: [target],
59927
+ display: ["xdg-open", target].map(quoteCliArg).join(" ")
59928
+ };
59929
+ }
59895
59930
  async function executeSceneRender(opts = {}) {
59896
59931
  const projectDir = resolve62(opts.projectDir ?? ".");
59897
59932
  const projectConfig = await readProjectConfig(projectDir);
@@ -60015,6 +60050,9 @@ async function executeSceneRender(opts = {}) {
60015
60050
  beat: opts.beatId ?? null,
60016
60051
  root,
60017
60052
  outputPath: relative11(process.cwd(), outputPath) || outputPath,
60053
+ absoluteOutputPath: outputPath,
60054
+ openCommand: buildMediaOpenCommand("open", outputPath).display,
60055
+ revealCommand: buildMediaOpenCommand("reveal", outputPath).display,
60018
60056
  durationMs: Date.now() - start,
60019
60057
  framesRendered: job.framesRendered,
60020
60058
  totalFrames: job.totalFrames,
@@ -60025,12 +60063,38 @@ async function executeSceneRender(opts = {}) {
60025
60063
  audioMuxApplied,
60026
60064
  audioMuxWarning
60027
60065
  };
60066
+ if (opts.revealInFinder) {
60067
+ try {
60068
+ await runMediaOpenAction("reveal", outputPath);
60069
+ result.revealed = true;
60070
+ } catch (err) {
60071
+ result.revealed = false;
60072
+ result.revealError = err instanceof Error ? err.message : String(err);
60073
+ }
60074
+ }
60075
+ if (opts.openAfterRender) {
60076
+ try {
60077
+ await runMediaOpenAction("open", outputPath);
60078
+ result.opened = true;
60079
+ } catch (err) {
60080
+ result.opened = false;
60081
+ result.openError = err instanceof Error ? err.message : String(err);
60082
+ }
60083
+ }
60028
60084
  result.reportPath = await writeRenderReport(projectDir, {
60029
60085
  ...result,
60030
60086
  outputPath
60031
60087
  });
60032
60088
  return result;
60033
60089
  }
60090
+ async function runMediaOpenAction(action, filePath) {
60091
+ const cmd = buildMediaOpenCommand(action, filePath);
60092
+ await execFileAsync2(cmd.command, cmd.args);
60093
+ }
60094
+ function quoteCliArg(value) {
60095
+ if (/^[A-Za-z0-9_/@%+=:,.-]+$/.test(value)) return value;
60096
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
60097
+ }
60034
60098
  async function safeStat(p) {
60035
60099
  try {
60036
60100
  return await stat8(p);
@@ -60201,6 +60265,13 @@ async function writeRenderReport(projectDir, result) {
60201
60265
  beat: result.beat ?? null,
60202
60266
  root: result.root,
60203
60267
  outputPath: result.outputPath,
60268
+ absoluteOutputPath: result.absoluteOutputPath,
60269
+ openCommand: result.openCommand,
60270
+ revealCommand: result.revealCommand,
60271
+ opened: result.opened,
60272
+ revealed: result.revealed,
60273
+ openError: result.openError,
60274
+ revealError: result.revealError,
60204
60275
  fps: result.fps,
60205
60276
  quality: result.quality,
60206
60277
  format: result.format,
@@ -60245,6 +60316,7 @@ function escapeAttr(value) {
60245
60316
  function unique4(items) {
60246
60317
  return [...new Set(items.filter((item) => Boolean(item)))];
60247
60318
  }
60319
+ var execFileAsync2;
60248
60320
  var init_scene_render = __esm({
60249
60321
  "src/commands/_shared/scene-render.ts"() {
60250
60322
  "use strict";
@@ -60254,6 +60326,7 @@ var init_scene_render = __esm({
60254
60326
  init_scene_audio_mux();
60255
60327
  init_project_config();
60256
60328
  init_scene_project();
60329
+ execFileAsync2 = promisify2(execFile2);
60257
60330
  }
60258
60331
  });
60259
60332
 
@@ -67341,7 +67414,9 @@ var init_scene2 = __esm({
67341
67414
  fps: external_exports.number().optional().describe("Frames per second. Must be 24, 30, or 60. Default 30."),
67342
67415
  quality: external_exports.enum(["draft", "standard", "high"]).optional().describe("Quality preset. Default 'standard'."),
67343
67416
  format: external_exports.enum(["mp4", "webm", "mov"]).optional().describe("Container format. Default 'mp4'."),
67344
- workers: external_exports.number().optional().describe("Capture worker count (1-16). Default 1.")
67417
+ workers: external_exports.number().optional().describe("Capture worker count (1-16). Default 1."),
67418
+ openAfterRender: external_exports.boolean().optional().describe("Open the rendered video in the OS default app after render. Default false."),
67419
+ revealInFinder: external_exports.boolean().optional().describe("Reveal the rendered video in Finder/file manager after render. Default false.")
67345
67420
  });
67346
67421
  sceneRenderTool = defineTool({
67347
67422
  name: "render",
@@ -67359,7 +67434,9 @@ var init_scene2 = __esm({
67359
67434
  fps: args.fps,
67360
67435
  quality: args.quality,
67361
67436
  format: args.format,
67362
- workers: args.workers
67437
+ workers: args.workers,
67438
+ openAfterRender: args.openAfterRender,
67439
+ revealInFinder: args.revealInFinder
67363
67440
  });
67364
67441
  if (!result.success) {
67365
67442
  return { success: false, error: result.error ?? "render failed" };
@@ -67368,6 +67445,13 @@ var init_scene2 = __esm({
67368
67445
  success: true,
67369
67446
  data: {
67370
67447
  outputPath: result.outputPath,
67448
+ absoluteOutputPath: result.absoluteOutputPath,
67449
+ openCommand: result.openCommand,
67450
+ revealCommand: result.revealCommand,
67451
+ opened: result.opened,
67452
+ revealed: result.revealed,
67453
+ openError: result.openError,
67454
+ revealError: result.revealError,
67371
67455
  beat: result.beat,
67372
67456
  root: result.root,
67373
67457
  reportPath: result.reportPath,
@@ -67376,13 +67460,24 @@ var init_scene2 = __esm({
67376
67460
  totalFrames: result.totalFrames,
67377
67461
  fps: result.fps,
67378
67462
  quality: result.quality,
67379
- format: result.format
67463
+ format: result.format,
67464
+ audioCount: result.audioCount,
67465
+ audioMuxApplied: result.audioMuxApplied,
67466
+ audioMuxWarning: result.audioMuxWarning
67380
67467
  },
67381
67468
  humanLines: [
67382
- `\u2705 Render complete: ${result.outputPath}`,
67469
+ `\u2705 Render complete: ${result.absoluteOutputPath ?? result.outputPath}`,
67383
67470
  ` duration: ${((result.durationMs ?? 0) / 1e3).toFixed(1)}s`,
67384
67471
  ` frames: ${result.framesRendered ?? "?"}${result.totalFrames ? ` / ${result.totalFrames}` : ""}`,
67385
- ` config: ${result.fps}fps \xB7 ${result.quality} \xB7 ${result.format}`
67472
+ ` config: ${result.fps}fps \xB7 ${result.quality} \xB7 ${result.format}`,
67473
+ ` audio: ${result.audioCount && result.audioCount > 0 ? `${result.audioCount} track${result.audioCount === 1 ? "" : "s"} muxed` : "silent"}`,
67474
+ ...result.openCommand ? [` open: ${result.openCommand}`] : [],
67475
+ ...result.revealCommand ? [` reveal: ${result.revealCommand}`] : [],
67476
+ ` inspect: vibe inspect render ${projectDir} --cheap --json`,
67477
+ ...result.opened ? [` opened: yes`] : [],
67478
+ ...result.revealed ? [` revealed: yes`] : [],
67479
+ ...result.openError ? [` open warning: ${result.openError}`] : [],
67480
+ ...result.revealError ? [` reveal warning: ${result.revealError}`] : []
67386
67481
  ]
67387
67482
  };
67388
67483
  }
@@ -72028,7 +72123,7 @@ init_source();
72028
72123
  // package.json
72029
72124
  var package_default = {
72030
72125
  name: "@vibeframe/cli",
72031
- version: "0.106.7",
72126
+ version: "0.106.9",
72032
72127
  description: "VibeFrame CLI - agentic video workflow runtime",
72033
72128
  private: false,
72034
72129
  license: "MIT",
@@ -82731,7 +82826,7 @@ import { resolve as resolve65 } from "node:path";
82731
82826
  var VALID_FPS = [24, 30, 60];
82732
82827
  var VALID_QUALITIES = ["draft", "standard", "high"];
82733
82828
  var VALID_FORMATS = ["mp4", "webm", "mov"];
82734
- var renderCommand = new Command("render").description("Render a VibeFrame video project to MP4/WebM/MOV").argument("[project-dir]", "Video project directory", ".").option("-o, --out <path>", "Output file (default: renders/<name>-<timestamp>.<format>)").option("--root <file>", "Root composition file", "index.html").option("--beat <id>", "Render only one storyboard beat using a temporary root").option("--fps <n>", `Frames per second: ${VALID_FPS.join("|")}`, "30").option("--quality <q>", `Quality preset: ${VALID_QUALITIES.join("|")}`, "standard").option("--format <f>", `Output container: ${VALID_FORMATS.join("|")}`, "mp4").option("--workers <n>", "Capture workers (1-16, default 1)", "1").option("--dry-run", "Preview parameters without rendering").addHelpText("after", `
82829
+ var renderCommand = new Command("render").description("Render a VibeFrame video project to MP4/WebM/MOV").argument("[project-dir]", "Video project directory", ".").option("-o, --out <path>", "Output file (default: renders/<name>-<timestamp>.<format>)").option("--root <file>", "Root composition file", "index.html").option("--beat <id>", "Render only one storyboard beat using a temporary root").option("--fps <n>", `Frames per second: ${VALID_FPS.join("|")}`, "30").option("--quality <q>", `Quality preset: ${VALID_QUALITIES.join("|")}`, "standard").option("--format <f>", `Output container: ${VALID_FORMATS.join("|")}`, "mp4").option("--workers <n>", "Capture workers (1-16, default 1)", "1").option("--open", "Open the rendered video in the OS default app after render").option("--reveal", "Reveal the rendered video in Finder/file manager after render").option("--dry-run", "Preview parameters without rendering").addHelpText("after", `
82735
82830
  Examples:
82736
82831
  $ vibe render my-video
82737
82832
  $ vibe render my-video -o renders/final.mp4 --quality high
@@ -82751,7 +82846,9 @@ Alias note: this is the project-level entrypoint for \`vibe scene render\`.`).ac
82751
82846
  fps,
82752
82847
  quality,
82753
82848
  format,
82754
- workers
82849
+ workers,
82850
+ openAfterRender: Boolean(options.open),
82851
+ revealInFinder: Boolean(options.reveal)
82755
82852
  };
82756
82853
  if (options.dryRun) {
82757
82854
  if (!isJsonMode() && !isQuietMode()) {
@@ -82776,6 +82873,8 @@ Alias note: this is the project-level entrypoint for \`vibe scene render\`.`).ac
82776
82873
  quality,
82777
82874
  format,
82778
82875
  workers,
82876
+ openAfterRender: Boolean(options.open),
82877
+ revealInFinder: Boolean(options.reveal),
82779
82878
  onProgress: (pct, stage) => {
82780
82879
  if (spinner2) spinner2.text = `Rendering [${Math.round(pct * 100)}%] ${stage}`;
82781
82880
  }
@@ -82806,6 +82905,8 @@ function printRenderDryRun(projectDirArg, params) {
82806
82905
  console.log(` Format: ${source_default.bold(params.format)}`);
82807
82906
  console.log(` Quality/FPS: ${source_default.bold(`${params.quality} / ${params.fps}`)}`);
82808
82907
  console.log(` Workers: ${source_default.bold(String(params.workers))}`);
82908
+ if (params.openAfterRender) console.log(` Open: ${source_default.bold("yes")}`);
82909
+ if (params.revealInFinder) console.log(` Reveal: ${source_default.bold("yes")}`);
82809
82910
  console.log();
82810
82911
  console.log(source_default.dim("No browser capture, FFmpeg mux, or video files were created."));
82811
82912
  }
@@ -82814,7 +82915,7 @@ function printRenderResult(spinner2, result) {
82814
82915
  console.log();
82815
82916
  console.log(source_default.bold.cyan("Output"));
82816
82917
  console.log(source_default.dim("-".repeat(60)));
82817
- console.log(` File: ${source_default.bold(result.outputPath)}`);
82918
+ console.log(` File: ${source_default.bold(result.absoluteOutputPath ?? result.outputPath)}`);
82818
82919
  if (result.beat) console.log(` Beat: ${result.beat}`);
82819
82920
  if (result.root) console.log(` Root: ${result.root}`);
82820
82921
  console.log(` Format: ${result.format ?? "mp4"}`);
@@ -82827,6 +82928,12 @@ function printRenderResult(spinner2, result) {
82827
82928
  console.log(` Audio: ${audio}`);
82828
82929
  }
82829
82930
  if (result.audioMuxWarning) console.log(source_default.yellow(` Warning: ${result.audioMuxWarning}`));
82931
+ if (result.openCommand) console.log(` Open: ${source_default.dim(result.openCommand)}`);
82932
+ if (result.revealCommand) console.log(` Reveal: ${source_default.dim(result.revealCommand)}`);
82933
+ if (result.opened) console.log(source_default.green(" Opened: yes"));
82934
+ if (result.revealed) console.log(source_default.green(" Revealed: yes"));
82935
+ if (result.openError) console.log(source_default.yellow(` Open warn: ${result.openError}`));
82936
+ if (result.revealError) console.log(source_default.yellow(` Reveal warn: ${result.revealError}`));
82830
82937
  }
82831
82938
  function parseFps(value) {
82832
82939
  const n = Number.parseInt(value, 10);
@@ -109,6 +109,56 @@ function zodToJsonSchema(schema) {
109
109
  }
110
110
 
111
111
  // src/tools/adapters/mcp.ts
112
+ function formatConsolePart(value) {
113
+ if (typeof value === "string") return value;
114
+ if (value instanceof Error) return value.stack ?? value.message;
115
+ try {
116
+ return JSON.stringify(value);
117
+ } catch {
118
+ return String(value);
119
+ }
120
+ }
121
+ function invokeWriteCallback(encodingOrCallback, callback) {
122
+ const cb = typeof encodingOrCallback === "function" ? encodingOrCallback : typeof callback === "function" ? callback : void 0;
123
+ if (cb) queueMicrotask(() => cb());
124
+ }
125
+ function formatStdoutChunk(chunk, encodingOrCallback) {
126
+ if (Buffer.isBuffer(chunk)) {
127
+ const encoding = typeof encodingOrCallback === "string" ? encodingOrCallback : "utf8";
128
+ return chunk.toString(encoding);
129
+ }
130
+ return String(chunk);
131
+ }
132
+ async function withCapturedStdout(fn) {
133
+ const originalWrite = process.stdout.write;
134
+ const originalConsoleLog = console.log;
135
+ const originalConsoleInfo = console.info;
136
+ const originalConsoleDebug = console.debug;
137
+ let captured = "";
138
+ const captureConsole = (...values) => {
139
+ captured += `${values.map(formatConsolePart).join(" ")}
140
+ `;
141
+ };
142
+ process.stdout.write = (chunk, encodingOrCallback, callback) => {
143
+ captured += formatStdoutChunk(chunk, encodingOrCallback);
144
+ invokeWriteCallback(encodingOrCallback, callback);
145
+ return true;
146
+ };
147
+ console.log = captureConsole;
148
+ console.info = captureConsole;
149
+ console.debug = captureConsole;
150
+ try {
151
+ return await fn();
152
+ } finally {
153
+ process.stdout.write = originalWrite;
154
+ console.log = originalConsoleLog;
155
+ console.info = originalConsoleInfo;
156
+ console.debug = originalConsoleDebug;
157
+ if (captured.trim() && process.env.VIBE_MCP_DEBUG_STDIO === "1") {
158
+ process.stderr.write(captured);
159
+ }
160
+ }
161
+ }
112
162
  function formatZodError(err) {
113
163
  const missing = err.issues.filter((i) => {
114
164
  if (i.code !== "invalid_type") return false;
@@ -155,10 +205,12 @@ function buildMcpDispatcher(manifest) {
155
205
  };
156
206
  }
157
207
  try {
158
- const result = await tool.execute(parsed.data, {
159
- workingDirectory: process.cwd(),
160
- surface: "mcp"
161
- });
208
+ const result = await withCapturedStdout(
209
+ () => tool.execute(parsed.data, {
210
+ workingDirectory: process.cwd(),
211
+ surface: "mcp"
212
+ })
213
+ );
162
214
  const text = result.success ? JSON.stringify({ success: true, ...result.data }) : result.data ? JSON.stringify({
163
215
  success: false,
164
216
  error: result.error ?? "unknown error",
@@ -29537,15 +29537,18 @@ var init_scene_audio_mux = __esm({
29537
29537
  // src/commands/_shared/scene-render.ts
29538
29538
  var scene_render_exports = {};
29539
29539
  __export(scene_render_exports, {
29540
+ buildMediaOpenCommand: () => buildMediaOpenCommand,
29540
29541
  buildRenderConfig: () => buildRenderConfig,
29541
29542
  defaultOutputPath: () => defaultOutputPath,
29542
29543
  executeSceneRender: () => executeSceneRender,
29543
29544
  prepareBeatRenderRoot: () => prepareBeatRenderRoot,
29544
29545
  qualityToCrf: () => qualityToCrf
29545
29546
  });
29547
+ import { execFile as execFile2 } from "node:child_process";
29546
29548
  import { existsSync as existsSync11 } from "node:fs";
29547
29549
  import { mkdir as mkdir6, readFile as readFile12, stat as stat2, writeFile as writeFile11 } from "node:fs/promises";
29548
29550
  import { resolve as resolve15, relative as relative6, dirname as dirname5, basename as basename3, join as join10, isAbsolute } from "node:path";
29551
+ import { promisify as promisify2 } from "node:util";
29549
29552
  import {
29550
29553
  createRenderJob,
29551
29554
  executeRenderJob
@@ -29572,6 +29575,38 @@ function buildRenderConfig(opts) {
29572
29575
  workers: opts.workers ?? 1
29573
29576
  };
29574
29577
  }
29578
+ function buildMediaOpenCommand(action, filePath, platform = process.platform) {
29579
+ if (platform === "darwin") {
29580
+ const args = action === "reveal" ? ["-R", filePath] : [filePath];
29581
+ return {
29582
+ command: "open",
29583
+ args,
29584
+ display: ["open", ...args].map(quoteCliArg).join(" ")
29585
+ };
29586
+ }
29587
+ if (platform === "win32") {
29588
+ if (action === "reveal") {
29589
+ const args2 = [`/select,${filePath}`];
29590
+ return {
29591
+ command: "explorer",
29592
+ args: args2,
29593
+ display: ["explorer", ...args2].map(quoteCliArg).join(" ")
29594
+ };
29595
+ }
29596
+ const args = ["/c", "start", "", filePath];
29597
+ return {
29598
+ command: "cmd",
29599
+ args,
29600
+ display: ["cmd", ...args].map(quoteCliArg).join(" ")
29601
+ };
29602
+ }
29603
+ const target = action === "reveal" ? dirname5(filePath) : filePath;
29604
+ return {
29605
+ command: "xdg-open",
29606
+ args: [target],
29607
+ display: ["xdg-open", target].map(quoteCliArg).join(" ")
29608
+ };
29609
+ }
29575
29610
  async function executeSceneRender(opts = {}) {
29576
29611
  const projectDir = resolve15(opts.projectDir ?? ".");
29577
29612
  const projectConfig = await readProjectConfig(projectDir);
@@ -29695,6 +29730,9 @@ async function executeSceneRender(opts = {}) {
29695
29730
  beat: opts.beatId ?? null,
29696
29731
  root,
29697
29732
  outputPath: relative6(process.cwd(), outputPath) || outputPath,
29733
+ absoluteOutputPath: outputPath,
29734
+ openCommand: buildMediaOpenCommand("open", outputPath).display,
29735
+ revealCommand: buildMediaOpenCommand("reveal", outputPath).display,
29698
29736
  durationMs: Date.now() - start,
29699
29737
  framesRendered: job.framesRendered,
29700
29738
  totalFrames: job.totalFrames,
@@ -29705,12 +29743,38 @@ async function executeSceneRender(opts = {}) {
29705
29743
  audioMuxApplied,
29706
29744
  audioMuxWarning
29707
29745
  };
29746
+ if (opts.revealInFinder) {
29747
+ try {
29748
+ await runMediaOpenAction("reveal", outputPath);
29749
+ result.revealed = true;
29750
+ } catch (err) {
29751
+ result.revealed = false;
29752
+ result.revealError = err instanceof Error ? err.message : String(err);
29753
+ }
29754
+ }
29755
+ if (opts.openAfterRender) {
29756
+ try {
29757
+ await runMediaOpenAction("open", outputPath);
29758
+ result.opened = true;
29759
+ } catch (err) {
29760
+ result.opened = false;
29761
+ result.openError = err instanceof Error ? err.message : String(err);
29762
+ }
29763
+ }
29708
29764
  result.reportPath = await writeRenderReport(projectDir, {
29709
29765
  ...result,
29710
29766
  outputPath
29711
29767
  });
29712
29768
  return result;
29713
29769
  }
29770
+ async function runMediaOpenAction(action, filePath) {
29771
+ const cmd = buildMediaOpenCommand(action, filePath);
29772
+ await execFileAsync2(cmd.command, cmd.args);
29773
+ }
29774
+ function quoteCliArg(value) {
29775
+ if (/^[A-Za-z0-9_/@%+=:,.-]+$/.test(value)) return value;
29776
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
29777
+ }
29714
29778
  async function safeStat(p) {
29715
29779
  try {
29716
29780
  return await stat2(p);
@@ -29881,6 +29945,13 @@ async function writeRenderReport(projectDir, result) {
29881
29945
  beat: result.beat ?? null,
29882
29946
  root: result.root,
29883
29947
  outputPath: result.outputPath,
29948
+ absoluteOutputPath: result.absoluteOutputPath,
29949
+ openCommand: result.openCommand,
29950
+ revealCommand: result.revealCommand,
29951
+ opened: result.opened,
29952
+ revealed: result.revealed,
29953
+ openError: result.openError,
29954
+ revealError: result.revealError,
29884
29955
  fps: result.fps,
29885
29956
  quality: result.quality,
29886
29957
  format: result.format,
@@ -29925,6 +29996,7 @@ function escapeAttr(value) {
29925
29996
  function unique(items) {
29926
29997
  return [...new Set(items.filter((item) => Boolean(item)))];
29927
29998
  }
29999
+ var execFileAsync2;
29928
30000
  var init_scene_render = __esm({
29929
30001
  "src/commands/_shared/scene-render.ts"() {
29930
30002
  "use strict";
@@ -29934,6 +30006,7 @@ var init_scene_render = __esm({
29934
30006
  init_scene_audio_mux();
29935
30007
  init_project_config();
29936
30008
  init_scene_project();
30009
+ execFileAsync2 = promisify2(execFile2);
29937
30010
  }
29938
30011
  });
29939
30012
 
@@ -35843,11 +35916,11 @@ registerRoot(Root);
35843
35916
  return dir;
35844
35917
  }
35845
35918
  async function this_npmInstall(dir) {
35846
- const { execFile: execFile2 } = await import("node:child_process");
35847
- const { promisify: promisify2 } = await import("node:util");
35848
- const execFileAsync2 = promisify2(execFile2);
35919
+ const { execFile: execFile3 } = await import("node:child_process");
35920
+ const { promisify: promisify3 } = await import("node:util");
35921
+ const execFileAsync3 = promisify3(execFile3);
35849
35922
  try {
35850
- await execFileAsync2("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund"], {
35923
+ await execFileAsync3("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund"], {
35851
35924
  cwd: dir,
35852
35925
  timeout: 18e4
35853
35926
  });
@@ -35871,13 +35944,13 @@ async function renderMotion(options) {
35871
35944
  );
35872
35945
  try {
35873
35946
  const entryPoint = join16(dir, "Root.tsx");
35874
- const { execFile: execFile2 } = await import("node:child_process");
35875
- const { promisify: promisify2 } = await import("node:util");
35876
- const execFileAsync2 = promisify2(execFile2);
35947
+ const { execFile: execFile3 } = await import("node:child_process");
35948
+ const { promisify: promisify3 } = await import("node:util");
35949
+ const execFileAsync3 = promisify3(execFile3);
35877
35950
  if (transparent) {
35878
35951
  const webmOut = options.outputPath.replace(/\.\w+$/, ".webm");
35879
35952
  try {
35880
- await execFileAsync2("npx", [
35953
+ await execFileAsync3("npx", [
35881
35954
  "remotion",
35882
35955
  "render",
35883
35956
  entryPoint,
@@ -35894,7 +35967,7 @@ async function renderMotion(options) {
35894
35967
  } catch {
35895
35968
  }
35896
35969
  try {
35897
- await execFileAsync2("npx", [
35970
+ await execFileAsync3("npx", [
35898
35971
  "remotion",
35899
35972
  "render",
35900
35973
  entryPoint,
@@ -35914,7 +35987,7 @@ async function renderMotion(options) {
35914
35987
  }
35915
35988
  }
35916
35989
  const mp4Out = options.outputPath.replace(/\.\w+$/, ".mp4");
35917
- await execFileAsync2("npx", [
35990
+ await execFileAsync3("npx", [
35918
35991
  "remotion",
35919
35992
  "render",
35920
35993
  entryPoint,
@@ -52583,7 +52656,9 @@ var sceneRenderSchema = external_exports.object({
52583
52656
  fps: external_exports.number().optional().describe("Frames per second. Must be 24, 30, or 60. Default 30."),
52584
52657
  quality: external_exports.enum(["draft", "standard", "high"]).optional().describe("Quality preset. Default 'standard'."),
52585
52658
  format: external_exports.enum(["mp4", "webm", "mov"]).optional().describe("Container format. Default 'mp4'."),
52586
- workers: external_exports.number().optional().describe("Capture worker count (1-16). Default 1.")
52659
+ workers: external_exports.number().optional().describe("Capture worker count (1-16). Default 1."),
52660
+ openAfterRender: external_exports.boolean().optional().describe("Open the rendered video in the OS default app after render. Default false."),
52661
+ revealInFinder: external_exports.boolean().optional().describe("Reveal the rendered video in Finder/file manager after render. Default false.")
52587
52662
  });
52588
52663
  var sceneRenderTool = defineTool({
52589
52664
  name: "render",
@@ -52601,7 +52676,9 @@ var sceneRenderTool = defineTool({
52601
52676
  fps: args.fps,
52602
52677
  quality: args.quality,
52603
52678
  format: args.format,
52604
- workers: args.workers
52679
+ workers: args.workers,
52680
+ openAfterRender: args.openAfterRender,
52681
+ revealInFinder: args.revealInFinder
52605
52682
  });
52606
52683
  if (!result.success) {
52607
52684
  return { success: false, error: result.error ?? "render failed" };
@@ -52610,6 +52687,13 @@ var sceneRenderTool = defineTool({
52610
52687
  success: true,
52611
52688
  data: {
52612
52689
  outputPath: result.outputPath,
52690
+ absoluteOutputPath: result.absoluteOutputPath,
52691
+ openCommand: result.openCommand,
52692
+ revealCommand: result.revealCommand,
52693
+ opened: result.opened,
52694
+ revealed: result.revealed,
52695
+ openError: result.openError,
52696
+ revealError: result.revealError,
52613
52697
  beat: result.beat,
52614
52698
  root: result.root,
52615
52699
  reportPath: result.reportPath,
@@ -52618,13 +52702,24 @@ var sceneRenderTool = defineTool({
52618
52702
  totalFrames: result.totalFrames,
52619
52703
  fps: result.fps,
52620
52704
  quality: result.quality,
52621
- format: result.format
52705
+ format: result.format,
52706
+ audioCount: result.audioCount,
52707
+ audioMuxApplied: result.audioMuxApplied,
52708
+ audioMuxWarning: result.audioMuxWarning
52622
52709
  },
52623
52710
  humanLines: [
52624
- `\u2705 Render complete: ${result.outputPath}`,
52711
+ `\u2705 Render complete: ${result.absoluteOutputPath ?? result.outputPath}`,
52625
52712
  ` duration: ${((result.durationMs ?? 0) / 1e3).toFixed(1)}s`,
52626
52713
  ` frames: ${result.framesRendered ?? "?"}${result.totalFrames ? ` / ${result.totalFrames}` : ""}`,
52627
- ` config: ${result.fps}fps \xB7 ${result.quality} \xB7 ${result.format}`
52714
+ ` config: ${result.fps}fps \xB7 ${result.quality} \xB7 ${result.format}`,
52715
+ ` audio: ${result.audioCount && result.audioCount > 0 ? `${result.audioCount} track${result.audioCount === 1 ? "" : "s"} muxed` : "silent"}`,
52716
+ ...result.openCommand ? [` open: ${result.openCommand}`] : [],
52717
+ ...result.revealCommand ? [` reveal: ${result.revealCommand}`] : [],
52718
+ ` inspect: vibe inspect render ${projectDir} --cheap --json`,
52719
+ ...result.opened ? [` opened: yes`] : [],
52720
+ ...result.revealed ? [` revealed: yes`] : [],
52721
+ ...result.openError ? [` open warning: ${result.openError}`] : [],
52722
+ ...result.revealError ? [` reveal warning: ${result.revealError}`] : []
52628
52723
  ]
52629
52724
  };
52630
52725
  }
@@ -108,6 +108,8 @@ export declare const sceneRenderTool: import("../define-tool.js").ToolDefinition
108
108
  quality: z.ZodOptional<z.ZodEnum<["draft", "standard", "high"]>>;
109
109
  format: z.ZodOptional<z.ZodEnum<["mp4", "webm", "mov"]>>;
110
110
  workers: z.ZodOptional<z.ZodNumber>;
111
+ openAfterRender: z.ZodOptional<z.ZodBoolean>;
112
+ revealInFinder: z.ZodOptional<z.ZodBoolean>;
111
113
  }, "strip", z.ZodTypeAny, {
112
114
  output?: string | undefined;
113
115
  fps?: number | undefined;
@@ -117,6 +119,8 @@ export declare const sceneRenderTool: import("../define-tool.js").ToolDefinition
117
119
  root?: string | undefined;
118
120
  beat?: string | undefined;
119
121
  workers?: number | undefined;
122
+ openAfterRender?: boolean | undefined;
123
+ revealInFinder?: boolean | undefined;
120
124
  }, {
121
125
  output?: string | undefined;
122
126
  fps?: number | undefined;
@@ -126,6 +130,8 @@ export declare const sceneRenderTool: import("../define-tool.js").ToolDefinition
126
130
  root?: string | undefined;
127
131
  beat?: string | undefined;
128
132
  workers?: number | undefined;
133
+ openAfterRender?: boolean | undefined;
134
+ revealInFinder?: boolean | undefined;
129
135
  }>>;
130
136
  export declare const sceneBuildTool: import("../define-tool.js").ToolDefinition<z.ZodObject<{
131
137
  projectDir: z.ZodOptional<z.ZodString>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/cli",
3
- "version": "0.106.7",
3
+ "version": "0.106.9",
4
4
  "description": "VibeFrame CLI - agentic video workflow runtime",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -83,8 +83,8 @@
83
83
  "tsx": "^4.21.0",
84
84
  "typescript": "^5.3.3",
85
85
  "vitest": "^1.2.2",
86
- "@vibeframe/ai-providers": "0.106.7",
87
- "@vibeframe/core": "0.106.7"
86
+ "@vibeframe/ai-providers": "0.106.9",
87
+ "@vibeframe/core": "0.106.9"
88
88
  },
89
89
  "engines": {
90
90
  "node": ">=20"