@vosjs/cli 0.2.1 → 0.3.0

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/README.md CHANGED
@@ -13,15 +13,22 @@ npx vos render animation.json out.webm
13
13
  ## Commands
14
14
 
15
15
  ```bash
16
- vos render <config.json|url> [out] # config → video (WebM/MP4)
16
+ vos render <config.json|url|take> [out] # config → video; a take directory renders via the take pipeline
17
17
  vos still <config.json|url> [out] # config → single frame (WebP)
18
18
  vos info <config.json|url> # inspect a config
19
19
  vos preview <config.json|url> # serve a local playback page
20
20
  vos versions # installed @vosjs/* versions
21
- vos voila <subcommand> … # product-video pipeline (npm i -D @vosso/voila-cli)
21
+
22
+ # Take pipeline — screen recordings in, product video out (npm i -D @vosso/cli)
23
+ vos create --actions actions.json [out.webm] [--strict] # record + plan + render, one shot
24
+ vos record --actions actions.json [--out take] [--strict]
25
+ vos plan <take> [--fresh]
26
+ vos frames <take> [--at-zooms | --frame <t> --size WxH]
27
+ vos open <take>
28
+ vos validate <actions.json|take>
22
29
  ```
23
30
 
24
- The `vos` namespace routes product verbs: `vos voila …` delegates to the separately installed product-video pipeline, and `vos riff` / `vos orbit` are reserved for the other vosso products today they print exactly what works instead (riff's remix contract lives at [vos.so/llms-remix.txt](https://vos.so/llms-remix.txt)) and exit non-zero, so scripts never mistake a stub for a run.
31
+ The take verbs delegate to the separately installed [`@vosso/cli`](https://www.npmjs.com/package/@vosso/cli) (its `run(argv)` export is the contract; `@vosso/voila-cli` remains an install fallback, and `vos voila <verb>` keeps working as a hidden alias for existing scripts). `vos render` is polymorphic by a deterministic sniff, never a flag: a take directory is recognized by its `doc.json`; anything else renders as an engine config.
25
32
 
26
33
  `vos render` accepts `--width` / `--height` / `--fps` / `--duration` / `--format webm|mp4`; `vos still` accepts `--time` / `--width` / `--height`. Configs can be local files or URLs, and API `{ "config": … }` envelopes are unwrapped automatically. Old config versions are migrated before rendering.
27
34
 
package/dist/cli.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
 
15
15
  // src/cli.ts
16
16
  import { writeFile } from "fs/promises";
17
- import { readFileSync } from "fs";
17
+ import { existsSync, readFileSync } from "fs";
18
18
  import { basename, dirname, join } from "path";
19
19
  import { createServer } from "http";
20
20
  import { createRequire } from "module";
@@ -50,15 +50,22 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["json", "help", "version"]);
50
50
  var HELP = `vos \u2014 command line for the vos programmatic video engine (https://vos.so)
51
51
 
52
52
  Usage
53
- vos render <config.json|url> [out] [--width 1920] [--height 1080] [--fps 30]
53
+ vos render <config.json|url|take> [out] [--width 1920] [--height 1080] [--fps 30]
54
54
  [--duration <s>] [--format webm|mp4] [--json]
55
55
  vos still <config.json|url> [out.webp] [--time 0] [--width] [--height] [--json]
56
56
  vos info <config.json|url> [--json]
57
57
  vos preview <config.json|url> [--port 0]
58
58
  vos versions [--json]
59
- vos voila <subcommand> \u2026 (product-video pipeline \u2014 separate install)
60
- vos riff \u2026 (remix vos programs \u2014 CLI verb planned; contract: vos.so/llms-remix.txt)
61
- vos orbit \u2026 (3D asset \u2192 product video \u2014 not released yet)
59
+
60
+ Take pipeline \u2014 screen recordings in, polished product video out
61
+ (ships separately: npm i -D @vosso/cli)
62
+ vos create --actions actions.json [out.webm] [--strict] (record + plan + render, one shot)
63
+ vos record --actions actions.json [--out take] [--strict]
64
+ vos plan <take> [--fresh]
65
+ vos frames <take> [--at-zooms | --frame <t> --size WxH]
66
+ vos open <take> (serve the take into the studio at vos.so)
67
+ vos validate <actions.json|take>
68
+ vos render <take> [out] (a take directory is detected by its doc.json)
62
69
 
63
70
  Conventions
64
71
  Results go to stdout; logs go to stderr. --json switches stdout to NDJSON
@@ -70,9 +77,13 @@ function outName(source, ext) {
70
77
  return `${base}.${ext}`;
71
78
  }
72
79
  async function cmdRender(argv) {
80
+ const first = argv.find((a) => !a.startsWith("-"));
81
+ if (first && existsSync(join(first, "doc.json"))) {
82
+ return delegateTake(["render", ...argv]);
83
+ }
73
84
  const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS);
74
85
  const source = positionals[0];
75
- if (!source) throw new UsageError("vos render <config.json|url> [out]");
86
+ if (!source) throw new UsageError("vos render <config.json|url|take> [out]");
76
87
  const r = createReporter(flags.json === true);
77
88
  const format = flags.format ?? "webm";
78
89
  if (format !== "webm" && format !== "mp4") throw new UsageError("--format must be webm or mp4");
@@ -248,28 +259,27 @@ async function cmdPreview(argv) {
248
259
  });
249
260
  return EXIT_OK;
250
261
  }
251
- async function cmdVoila(argv) {
252
- try {
253
- const mod = await import("@vosso/voila-cli");
254
- if (typeof mod.run === "function") return await mod.run(argv);
255
- process.stderr.write("@vosso/voila-cli is installed but exposes no run() \u2014 update it.\n");
256
- return EXIT_ERROR;
257
- } catch {
258
- process.stderr.write(
259
- "The Voila product-video pipeline ships separately.\n npm i -D @vosso/voila-cli\nthen re-run: vos voila \u2026\n"
260
- );
261
- return EXIT_ERROR;
262
+ var TAKE_VERBS = /* @__PURE__ */ new Set(["create", "record", "plan", "frames", "open", "validate"]);
263
+ async function delegateTake(argv, viaAlias = false) {
264
+ for (const name of ["@vosso/cli", "@vosso/voila-cli"]) {
265
+ let mod;
266
+ try {
267
+ mod = await import(name);
268
+ } catch {
269
+ continue;
270
+ }
271
+ if (typeof mod.run !== "function") continue;
272
+ if (viaAlias && argv[0]) {
273
+ process.stderr.write(`note: "vos voila ${argv[0]}" is now "vos ${argv[0]}".
274
+ `);
275
+ }
276
+ return await mod.run(argv);
262
277
  }
263
- }
264
- function cmdRiff() {
265
- process.stderr.write(
266
- "vos riff \u2014 remix vos programs (the animation product at https://vos.so/riff).\nThe CLI verb is planned and not yet shipped. What works today:\n - the agent remix contract: https://vos.so/llms-remix.txt\n (fetch a program config, edit it, push a private remix back over HTTP)\n - the browser editor: https://vos.so/stage\n - render any vos config locally: vos render <config.json>\n"
267
- );
268
- return EXIT_ERROR;
269
- }
270
- function cmdOrbit() {
271
278
  process.stderr.write(
272
- "vos orbit \u2014 3D assets to product videos (https://vos.so/orbit).\nThe dedicated orbit app is not released. What works today:\n - 3D showcase programs (turntable, dolly reveal) in the vos.so\n catalog: https://vos.so/gallery?tag=3d \u2014 each declares its knobs\n as params, and buildProduct() in the config is the documented\n swap point for your own model (GLTF via the addon loaders)\n - remix one over HTTP: https://vos.so/llms-remix.txt\n - render any 3D vos config locally: vos render <config.json>\n"
279
+ `The take pipeline (screen recordings in, product video out) ships separately.
280
+ npm i -D @vosso/cli
281
+ then re-run: vos ${argv.join(" ")}
282
+ `
273
283
  );
274
284
  return EXIT_ERROR;
275
285
  }
@@ -280,6 +290,7 @@ async function main() {
280
290
  return cmd ? EXIT_OK : EXIT_USAGE;
281
291
  }
282
292
  if (cmd === "--version") return cmdVersions(["--json"]);
293
+ if (TAKE_VERBS.has(cmd)) return delegateTake([cmd, ...rest]);
283
294
  switch (cmd) {
284
295
  case "render":
285
296
  return cmdRender(rest);
@@ -291,12 +302,10 @@ async function main() {
291
302
  return cmdVersions(rest);
292
303
  case "preview":
293
304
  return cmdPreview(rest);
305
+ // Hidden alias for existing scripts; not in HELP. Same code path as the
306
+ // promoted verbs, plus a one-line pointer at the new spelling.
294
307
  case "voila":
295
- return cmdVoila(rest);
296
- case "riff":
297
- return cmdRiff();
298
- case "orbit":
299
- return cmdOrbit();
308
+ return delegateTake(rest, true);
300
309
  default:
301
310
  throw new UsageError(`unknown command "${cmd}" \u2014 run vos help`);
302
311
  }
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/output.ts"],"sourcesContent":["import { writeFile } from 'node:fs/promises'\nimport { readFileSync } from 'node:fs'\nimport { basename, dirname, join } from 'node:path'\nimport { createServer } from 'node:http'\nimport { createRequire } from 'node:module'\nimport { fileURLToPath } from 'node:url'\nimport { parseArgs, numFlag, UsageError } from './args'\nimport {\n createReporter,\n EXIT_ERROR,\n EXIT_NO_BROWSER,\n EXIT_OK,\n EXIT_USAGE,\n} from './output'\nimport { loadVosConfig, configDuration } from './loadConfig'\nimport { launchBrowser, BrowserUnavailableError } from './browser'\nimport { renderVideo, renderStill, previewPages } from './render'\n\nconst BOOLEAN_FLAGS = new Set(['json', 'help', 'version'])\n\nconst HELP = `vos — command line for the vos programmatic video engine (https://vos.so)\n\nUsage\n vos render <config.json|url> [out] [--width 1920] [--height 1080] [--fps 30]\n [--duration <s>] [--format webm|mp4] [--json]\n vos still <config.json|url> [out.webp] [--time 0] [--width] [--height] [--json]\n vos info <config.json|url> [--json]\n vos preview <config.json|url> [--port 0]\n vos versions [--json]\n vos voila <subcommand> … (product-video pipeline — separate install)\n vos riff … (remix vos programs — CLI verb planned; contract: vos.so/llms-remix.txt)\n vos orbit … (3D asset → product video — not released yet)\n\nConventions\n Results go to stdout; logs go to stderr. --json switches stdout to NDJSON\n events ending with {\"event\":\"done\",…}. Exit codes: 0 ok, 1 error, 2 usage,\n 3 no browser available.\n`\n\nfunction outName(source: string, ext: string): string {\n const base = basename(source).replace(/\\.[a-z0-9]+$/i, '') || 'vos'\n return `${base}.${ext}`\n}\n\nasync function cmdRender(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos render <config.json|url> [out]')\n const r = createReporter(flags.json === true)\n const format = (flags.format as string) ?? 'webm'\n if (format !== 'webm' && format !== 'mp4') throw new UsageError('--format must be webm or mp4')\n\n const { config, warnings } = await loadVosConfig(source)\n for (const w of warnings) r.log(`note: ${w}`)\n const duration = numFlag(flags, 'duration', configDuration(config) ?? 5)\n const width = numFlag(flags, 'width', 1920)\n const height = numFlag(flags, 'height', 1080)\n const fps = numFlag(flags, 'fps', 30)\n const out = positionals[1] ?? outName(source, format)\n\n const browser = await launchBrowser()\n try {\n const result = await renderVideo(browser, {\n config,\n width,\n height,\n fps,\n duration,\n format,\n onPhase: (phase) => {\n r.log(`${phase}…`)\n r.event({ event: 'phase', phase })\n },\n })\n await writeFile(out, result.bytes)\n r.done(\n { out, bytes: result.bytes.length, width, height, fps, duration, format },\n `Wrote ${out} (${(result.bytes.length / 1024).toFixed(0)} KB, ${width}x${height}@${fps}, ${duration}s)`,\n )\n return EXIT_OK\n } finally {\n await browser.close()\n }\n}\n\nasync function cmdStill(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos still <config.json|url> [out.webp]')\n const r = createReporter(flags.json === true)\n\n const { config, warnings } = await loadVosConfig(source)\n for (const w of warnings) r.log(`note: ${w}`)\n const time = numFlag(flags, 'time', 0)\n const width = numFlag(flags, 'width', 1280)\n const height = numFlag(flags, 'height', 720)\n const out = positionals[1] ?? outName(source, 'webp')\n\n const browser = await launchBrowser()\n try {\n const result = await renderStill(browser, {\n config,\n width,\n height,\n time,\n onPhase: (phase) => {\n r.log(`${phase}…`)\n r.event({ event: 'phase', phase })\n },\n })\n await writeFile(out, result.bytes)\n r.done(\n { out, bytes: result.bytes.length, width, height, time, mimeType: result.mimeType },\n `Wrote ${out} (${(result.bytes.length / 1024).toFixed(0)} KB, ${width}x${height} @ t=${time}s)`,\n )\n return EXIT_OK\n } finally {\n await browser.close()\n }\n}\n\nasync function cmdInfo(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos info <config.json|url>')\n const r = createReporter(flags.json === true)\n\n const { config, warnings } = await loadVosConfig(source)\n const elements = Array.isArray(config.elements) ? config.elements.length : 0\n const data = typeof config.data === 'object' && config.data !== null ? Object.keys(config.data) : []\n const fns = ['setup', 'createContent', 'createTimeline', 'onFrame'].filter(\n (k) => typeof config[k] === 'string',\n )\n const info = {\n version: config.version,\n duration: configDuration(config) ?? null,\n camera: (config.camera as Record<string, unknown> | undefined)?.preset ?? null,\n elements,\n dataKeys: data,\n functions: fns,\n warnings,\n }\n if (r.json) r.done(info, '')\n else {\n for (const w of warnings) r.log(`note: ${w}`)\n process.stdout.write(\n `version: v${String(info.version)}\\n` +\n `duration: ${info.duration === null ? '(none)' : `${info.duration}s`}\\n` +\n `camera: ${String(info.camera ?? '(default)')}\\n` +\n `elements: ${elements}\\n` +\n `data keys: ${data.length ? data.join(', ') : '(none)'}\\n` +\n `functions: ${fns.join(', ')}\\n`,\n )\n }\n return EXIT_OK\n}\n\nfunction packageVersion(name: string): string | null {\n const require = createRequire(import.meta.url)\n // Fast path — packages that export ./package.json (e.g. playwright).\n try {\n return (require(`${name}/package.json`) as { version: string }).version\n } catch {\n // Strict `exports` maps (the @vosjs packages) hide package.json — resolve\n // the entry module instead and walk up to the owning package.json.\n }\n let entry: string | null = null\n try {\n entry = fileURLToPath(import.meta.resolve(name))\n } catch {\n try {\n entry = require.resolve(name)\n } catch {\n return null\n }\n }\n let dir = dirname(entry)\n for (let i = 0; i < 6; i++) {\n try {\n const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as {\n name?: string\n version?: string\n }\n if (pkg.name === name && pkg.version) return pkg.version\n } catch {\n // keep walking\n }\n const parent = dirname(dir)\n if (parent === dir) return null\n dir = parent\n }\n return null\n}\n\nasync function cmdVersions(argv: string[]): Promise<number> {\n const { flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const r = createReporter(flags.json === true)\n const versions: Record<string, string> = {}\n // Own version: read relative to dist (self-require is blocked by `exports`).\n try {\n const own = JSON.parse(\n readFileSync(fileURLToPath(new URL('../package.json', import.meta.url)), 'utf8'),\n ) as { version: string }\n versions['@vosjs/cli'] = own.version\n } catch {\n versions['@vosjs/cli'] = '(unknown)'\n }\n for (const name of ['@vosjs/core', '@vosjs/elements', '@vosjs/tween', 'playwright']) {\n versions[name] = packageVersion(name) ?? '(not found)'\n }\n if (r.json) r.done({ versions }, '')\n else for (const [k, v] of Object.entries(versions)) process.stdout.write(`${k} ${v}\\n`)\n return EXIT_OK\n}\n\nasync function cmdPreview(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos preview <config.json|url> [--port 0]')\n const r = createReporter(false)\n const { config, warnings } = await loadVosConfig(source)\n for (const w of warnings) r.log(`note: ${w}`)\n const { hostHtml, playerHtml } = previewPages(config)\n const server = createServer((req, res) => {\n const path = new URL(req.url ?? '/', 'http://x').pathname\n res.writeHead(200, { 'content-type': 'text/html' })\n res.end(path === '/player' ? playerHtml : hostHtml)\n })\n const port = numFlag(flags, 'port', 0)\n await new Promise<void>((resolve) => server.listen(port, resolve))\n const addr = server.address()\n const url = `http://localhost:${typeof addr === 'object' && addr ? addr.port : port}/`\n process.stdout.write(`${url}\\n`)\n r.log('Serving playback preview — Ctrl-C to stop.')\n await new Promise(() => {}) // keep alive until interrupted\n return EXIT_OK\n}\n\nasync function cmdVoila(argv: string[]): Promise<number> {\n try {\n const mod = (await import('@vosso/voila-cli' as string)) as {\n run?: (argv: string[]) => Promise<number>\n }\n if (typeof mod.run === 'function') return await mod.run(argv)\n process.stderr.write('@vosso/voila-cli is installed but exposes no run() — update it.\\n')\n return EXIT_ERROR\n } catch {\n process.stderr.write(\n 'The Voila product-video pipeline ships separately.\\n' +\n ' npm i -D @vosso/voila-cli\\n' +\n 'then re-run: vos voila …\\n',\n )\n return EXIT_ERROR\n }\n}\n\n// Product namespaces that are reserved but not yet shipped. Honest stubs:\n// they say exactly what works today and exit non-zero so scripts and agents\n// never mistake them for a successful run.\nfunction cmdRiff(): number {\n process.stderr.write(\n 'vos riff — remix vos programs (the animation product at https://vos.so/riff).\\n' +\n 'The CLI verb is planned and not yet shipped. What works today:\\n' +\n ' - the agent remix contract: https://vos.so/llms-remix.txt\\n' +\n ' (fetch a program config, edit it, push a private remix back over HTTP)\\n' +\n ' - the browser editor: https://vos.so/stage\\n' +\n ' - render any vos config locally: vos render <config.json>\\n',\n )\n return EXIT_ERROR\n}\n\nfunction cmdOrbit(): number {\n process.stderr.write(\n 'vos orbit — 3D assets to product videos (https://vos.so/orbit).\\n' +\n 'The dedicated orbit app is not released. What works today:\\n' +\n ' - 3D showcase programs (turntable, dolly reveal) in the vos.so\\n' +\n ' catalog: https://vos.so/gallery?tag=3d — each declares its knobs\\n' +\n ' as params, and buildProduct() in the config is the documented\\n' +\n ' swap point for your own model (GLTF via the addon loaders)\\n' +\n ' - remix one over HTTP: https://vos.so/llms-remix.txt\\n' +\n ' - render any 3D vos config locally: vos render <config.json>\\n',\n )\n return EXIT_ERROR\n}\n\nasync function main(): Promise<number> {\n const [cmd, ...rest] = process.argv.slice(2)\n if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {\n process.stdout.write(HELP)\n return cmd ? EXIT_OK : EXIT_USAGE\n }\n if (cmd === '--version') return cmdVersions(['--json'])\n switch (cmd) {\n case 'render':\n return cmdRender(rest)\n case 'still':\n return cmdStill(rest)\n case 'info':\n return cmdInfo(rest)\n case 'versions':\n return cmdVersions(rest)\n case 'preview':\n return cmdPreview(rest)\n case 'voila':\n return cmdVoila(rest)\n case 'riff':\n return cmdRiff()\n case 'orbit':\n return cmdOrbit()\n default:\n throw new UsageError(`unknown command \"${cmd}\" — run vos help`)\n }\n}\n\nmain()\n .then((code) => process.exit(code))\n .catch((e) => {\n if (e instanceof UsageError) {\n process.stderr.write(`usage error: ${e.message}\\n`)\n process.exit(EXIT_USAGE)\n }\n if (e instanceof BrowserUnavailableError) {\n process.stderr.write(`${e.message}\\n`)\n process.exit(EXIT_NO_BROWSER)\n }\n process.stderr.write(`error: ${e instanceof Error ? e.message : String(e)}\\n`)\n process.exit(EXIT_ERROR)\n })\n","/**\n * Output conventions (agent-safe by construction):\n * - logs/progress → stderr (human readable)\n * - results → stdout; with --json, NDJSON events (`{\"event\":\"phase\"...}` then\n * `{\"event\":\"done\"...}`) so scripts and agents never parse prose\n * - exit codes: 0 ok · 1 failure · 2 usage · 3 browser unavailable\n */\nexport const EXIT_OK = 0\nexport const EXIT_ERROR = 1\nexport const EXIT_USAGE = 2\nexport const EXIT_NO_BROWSER = 3\n\nexport interface Reporter {\n json: boolean\n /** Human log line (stderr; suppressed in --json mode). */\n log: (msg: string) => void\n /** Machine event (stdout NDJSON in --json mode; no-op otherwise). */\n event: (obj: Record<string, unknown>) => void\n /** Final result: NDJSON `done` event in --json mode, plain line otherwise. */\n done: (obj: Record<string, unknown>, humanLine: string) => void\n}\n\nexport function createReporter(json: boolean): Reporter {\n return {\n json,\n log: (msg) => {\n if (!json) process.stderr.write(`${msg}\\n`)\n },\n event: (obj) => {\n if (json) process.stdout.write(`${JSON.stringify(obj)}\\n`)\n },\n done: (obj, humanLine) => {\n if (json) process.stdout.write(`${JSON.stringify({ event: 'done', ...obj })}\\n`)\n else process.stdout.write(`${humanLine}\\n`)\n },\n }\n}\n"],"mappings":"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","names":["require"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/output.ts"],"sourcesContent":["import { writeFile } from 'node:fs/promises'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { basename, dirname, join } from 'node:path'\nimport { createServer } from 'node:http'\nimport { createRequire } from 'node:module'\nimport { fileURLToPath } from 'node:url'\nimport { parseArgs, numFlag, UsageError } from './args'\nimport {\n createReporter,\n EXIT_ERROR,\n EXIT_NO_BROWSER,\n EXIT_OK,\n EXIT_USAGE,\n} from './output'\nimport { loadVosConfig, configDuration } from './loadConfig'\nimport { launchBrowser, BrowserUnavailableError } from './browser'\nimport { renderVideo, renderStill, previewPages } from './render'\n\nconst BOOLEAN_FLAGS = new Set(['json', 'help', 'version'])\n\nconst HELP = `vos — command line for the vos programmatic video engine (https://vos.so)\n\nUsage\n vos render <config.json|url|take> [out] [--width 1920] [--height 1080] [--fps 30]\n [--duration <s>] [--format webm|mp4] [--json]\n vos still <config.json|url> [out.webp] [--time 0] [--width] [--height] [--json]\n vos info <config.json|url> [--json]\n vos preview <config.json|url> [--port 0]\n vos versions [--json]\n\nTake pipeline — screen recordings in, polished product video out\n(ships separately: npm i -D @vosso/cli)\n vos create --actions actions.json [out.webm] [--strict] (record + plan + render, one shot)\n vos record --actions actions.json [--out take] [--strict]\n vos plan <take> [--fresh]\n vos frames <take> [--at-zooms | --frame <t> --size WxH]\n vos open <take> (serve the take into the studio at vos.so)\n vos validate <actions.json|take>\n vos render <take> [out] (a take directory is detected by its doc.json)\n\nConventions\n Results go to stdout; logs go to stderr. --json switches stdout to NDJSON\n events ending with {\"event\":\"done\",…}. Exit codes: 0 ok, 1 error, 2 usage,\n 3 no browser available.\n`\n\nfunction outName(source: string, ext: string): string {\n const base = basename(source).replace(/\\.[a-z0-9]+$/i, '') || 'vos'\n return `${base}.${ext}`\n}\n\nasync function cmdRender(argv: string[]): Promise<number> {\n // Polymorphic render: a take DIRECTORY (detected by its doc.json — the take\n // pipeline's editable document) renders through the take pipeline; anything\n // else is an engine config render. A deterministic sniff, never a flag.\n const first = argv.find((a) => !a.startsWith('-'))\n if (first && existsSync(join(first, 'doc.json'))) {\n return delegateTake(['render', ...argv])\n }\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos render <config.json|url|take> [out]')\n const r = createReporter(flags.json === true)\n const format = (flags.format as string) ?? 'webm'\n if (format !== 'webm' && format !== 'mp4') throw new UsageError('--format must be webm or mp4')\n\n const { config, warnings } = await loadVosConfig(source)\n for (const w of warnings) r.log(`note: ${w}`)\n const duration = numFlag(flags, 'duration', configDuration(config) ?? 5)\n const width = numFlag(flags, 'width', 1920)\n const height = numFlag(flags, 'height', 1080)\n const fps = numFlag(flags, 'fps', 30)\n const out = positionals[1] ?? outName(source, format)\n\n const browser = await launchBrowser()\n try {\n const result = await renderVideo(browser, {\n config,\n width,\n height,\n fps,\n duration,\n format,\n onPhase: (phase) => {\n r.log(`${phase}…`)\n r.event({ event: 'phase', phase })\n },\n })\n await writeFile(out, result.bytes)\n r.done(\n { out, bytes: result.bytes.length, width, height, fps, duration, format },\n `Wrote ${out} (${(result.bytes.length / 1024).toFixed(0)} KB, ${width}x${height}@${fps}, ${duration}s)`,\n )\n return EXIT_OK\n } finally {\n await browser.close()\n }\n}\n\nasync function cmdStill(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos still <config.json|url> [out.webp]')\n const r = createReporter(flags.json === true)\n\n const { config, warnings } = await loadVosConfig(source)\n for (const w of warnings) r.log(`note: ${w}`)\n const time = numFlag(flags, 'time', 0)\n const width = numFlag(flags, 'width', 1280)\n const height = numFlag(flags, 'height', 720)\n const out = positionals[1] ?? outName(source, 'webp')\n\n const browser = await launchBrowser()\n try {\n const result = await renderStill(browser, {\n config,\n width,\n height,\n time,\n onPhase: (phase) => {\n r.log(`${phase}…`)\n r.event({ event: 'phase', phase })\n },\n })\n await writeFile(out, result.bytes)\n r.done(\n { out, bytes: result.bytes.length, width, height, time, mimeType: result.mimeType },\n `Wrote ${out} (${(result.bytes.length / 1024).toFixed(0)} KB, ${width}x${height} @ t=${time}s)`,\n )\n return EXIT_OK\n } finally {\n await browser.close()\n }\n}\n\nasync function cmdInfo(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos info <config.json|url>')\n const r = createReporter(flags.json === true)\n\n const { config, warnings } = await loadVosConfig(source)\n const elements = Array.isArray(config.elements) ? config.elements.length : 0\n const data = typeof config.data === 'object' && config.data !== null ? Object.keys(config.data) : []\n const fns = ['setup', 'createContent', 'createTimeline', 'onFrame'].filter(\n (k) => typeof config[k] === 'string',\n )\n const info = {\n version: config.version,\n duration: configDuration(config) ?? null,\n camera: (config.camera as Record<string, unknown> | undefined)?.preset ?? null,\n elements,\n dataKeys: data,\n functions: fns,\n warnings,\n }\n if (r.json) r.done(info, '')\n else {\n for (const w of warnings) r.log(`note: ${w}`)\n process.stdout.write(\n `version: v${String(info.version)}\\n` +\n `duration: ${info.duration === null ? '(none)' : `${info.duration}s`}\\n` +\n `camera: ${String(info.camera ?? '(default)')}\\n` +\n `elements: ${elements}\\n` +\n `data keys: ${data.length ? data.join(', ') : '(none)'}\\n` +\n `functions: ${fns.join(', ')}\\n`,\n )\n }\n return EXIT_OK\n}\n\nfunction packageVersion(name: string): string | null {\n const require = createRequire(import.meta.url)\n // Fast path — packages that export ./package.json (e.g. playwright).\n try {\n return (require(`${name}/package.json`) as { version: string }).version\n } catch {\n // Strict `exports` maps (the @vosjs packages) hide package.json — resolve\n // the entry module instead and walk up to the owning package.json.\n }\n let entry: string | null = null\n try {\n entry = fileURLToPath(import.meta.resolve(name))\n } catch {\n try {\n entry = require.resolve(name)\n } catch {\n return null\n }\n }\n let dir = dirname(entry)\n for (let i = 0; i < 6; i++) {\n try {\n const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as {\n name?: string\n version?: string\n }\n if (pkg.name === name && pkg.version) return pkg.version\n } catch {\n // keep walking\n }\n const parent = dirname(dir)\n if (parent === dir) return null\n dir = parent\n }\n return null\n}\n\nasync function cmdVersions(argv: string[]): Promise<number> {\n const { flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const r = createReporter(flags.json === true)\n const versions: Record<string, string> = {}\n // Own version: read relative to dist (self-require is blocked by `exports`).\n try {\n const own = JSON.parse(\n readFileSync(fileURLToPath(new URL('../package.json', import.meta.url)), 'utf8'),\n ) as { version: string }\n versions['@vosjs/cli'] = own.version\n } catch {\n versions['@vosjs/cli'] = '(unknown)'\n }\n for (const name of ['@vosjs/core', '@vosjs/elements', '@vosjs/tween', 'playwright']) {\n versions[name] = packageVersion(name) ?? '(not found)'\n }\n if (r.json) r.done({ versions }, '')\n else for (const [k, v] of Object.entries(versions)) process.stdout.write(`${k} ${v}\\n`)\n return EXIT_OK\n}\n\nasync function cmdPreview(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos preview <config.json|url> [--port 0]')\n const r = createReporter(false)\n const { config, warnings } = await loadVosConfig(source)\n for (const w of warnings) r.log(`note: ${w}`)\n const { hostHtml, playerHtml } = previewPages(config)\n const server = createServer((req, res) => {\n const path = new URL(req.url ?? '/', 'http://x').pathname\n res.writeHead(200, { 'content-type': 'text/html' })\n res.end(path === '/player' ? playerHtml : hostHtml)\n })\n const port = numFlag(flags, 'port', 0)\n await new Promise<void>((resolve) => server.listen(port, resolve))\n const addr = server.address()\n const url = `http://localhost:${typeof addr === 'object' && addr ? addr.port : port}/`\n process.stdout.write(`${url}\\n`)\n r.log('Serving playback preview — Ctrl-C to stop.')\n await new Promise(() => {}) // keep alive until interrupted\n return EXIT_OK\n}\n\n// The take pipeline's verbs live in @vosso/cli (published; previously\n// @vosso/voila-cli, kept as an install fallback during the transition).\n// The delegation contract is its `run(argv)` export.\nconst TAKE_VERBS = new Set(['create', 'record', 'plan', 'frames', 'open', 'validate'])\n\nasync function delegateTake(argv: string[], viaAlias = false): Promise<number> {\n for (const name of ['@vosso/cli', '@vosso/voila-cli']) {\n let mod: { run?: (argv: string[]) => Promise<number> }\n try {\n mod = (await import(name as string)) as typeof mod\n } catch {\n continue\n }\n if (typeof mod.run !== 'function') continue\n if (viaAlias && argv[0]) {\n process.stderr.write(`note: \"vos voila ${argv[0]}\" is now \"vos ${argv[0]}\".\\n`)\n }\n return await mod.run(argv)\n }\n process.stderr.write(\n 'The take pipeline (screen recordings in, product video out) ships separately.\\n' +\n ' npm i -D @vosso/cli\\n' +\n `then re-run: vos ${argv.join(' ')}\\n`,\n )\n return EXIT_ERROR\n}\n\nasync function main(): Promise<number> {\n const [cmd, ...rest] = process.argv.slice(2)\n if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {\n process.stdout.write(HELP)\n return cmd ? EXIT_OK : EXIT_USAGE\n }\n if (cmd === '--version') return cmdVersions(['--json'])\n if (TAKE_VERBS.has(cmd)) return delegateTake([cmd, ...rest])\n switch (cmd) {\n case 'render':\n return cmdRender(rest)\n case 'still':\n return cmdStill(rest)\n case 'info':\n return cmdInfo(rest)\n case 'versions':\n return cmdVersions(rest)\n case 'preview':\n return cmdPreview(rest)\n // Hidden alias for existing scripts; not in HELP. Same code path as the\n // promoted verbs, plus a one-line pointer at the new spelling.\n case 'voila':\n return delegateTake(rest, true)\n default:\n throw new UsageError(`unknown command \"${cmd}\" — run vos help`)\n }\n}\n\nmain()\n .then((code) => process.exit(code))\n .catch((e) => {\n if (e instanceof UsageError) {\n process.stderr.write(`usage error: ${e.message}\\n`)\n process.exit(EXIT_USAGE)\n }\n if (e instanceof BrowserUnavailableError) {\n process.stderr.write(`${e.message}\\n`)\n process.exit(EXIT_NO_BROWSER)\n }\n process.stderr.write(`error: ${e instanceof Error ? e.message : String(e)}\\n`)\n process.exit(EXIT_ERROR)\n })\n","/**\n * Output conventions (agent-safe by construction):\n * - logs/progress → stderr (human readable)\n * - results → stdout; with --json, NDJSON events (`{\"event\":\"phase\"...}` then\n * `{\"event\":\"done\"...}`) so scripts and agents never parse prose\n * - exit codes: 0 ok · 1 failure · 2 usage · 3 browser unavailable\n */\nexport const EXIT_OK = 0\nexport const EXIT_ERROR = 1\nexport const EXIT_USAGE = 2\nexport const EXIT_NO_BROWSER = 3\n\nexport interface Reporter {\n json: boolean\n /** Human log line (stderr; suppressed in --json mode). */\n log: (msg: string) => void\n /** Machine event (stdout NDJSON in --json mode; no-op otherwise). */\n event: (obj: Record<string, unknown>) => void\n /** Final result: NDJSON `done` event in --json mode, plain line otherwise. */\n done: (obj: Record<string, unknown>, humanLine: string) => void\n}\n\nexport function createReporter(json: boolean): Reporter {\n return {\n json,\n log: (msg) => {\n if (!json) process.stderr.write(`${msg}\\n`)\n },\n event: (obj) => {\n if (json) process.stdout.write(`${JSON.stringify(obj)}\\n`)\n },\n done: (obj, humanLine) => {\n if (json) process.stdout.write(`${JSON.stringify({ event: 'done', ...obj })}\\n`)\n else process.stdout.write(`${humanLine}\\n`)\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAAA,SAAS,iBAAiB;AAC1B,SAAS,YAAY,oBAAoB;AACzC,SAAS,UAAU,SAAS,YAAY;AACxC,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;;;ACEvB,IAAM,UAAU;AAChB,IAAM,aAAa;AACnB,IAAM,aAAa;AACnB,IAAM,kBAAkB;AAYxB,SAAS,eAAe,MAAyB;AACtD,SAAO;AAAA,IACL;AAAA,IACA,KAAK,CAAC,QAAQ;AACZ,UAAI,CAAC,KAAM,SAAQ,OAAO,MAAM,GAAG,GAAG;AAAA,CAAI;AAAA,IAC5C;AAAA,IACA,OAAO,CAAC,QAAQ;AACd,UAAI,KAAM,SAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,GAAG,CAAC;AAAA,CAAI;AAAA,IAC3D;AAAA,IACA,MAAM,CAAC,KAAK,cAAc;AACxB,UAAI,KAAM,SAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,EAAE,OAAO,QAAQ,GAAG,IAAI,CAAC,CAAC;AAAA,CAAI;AAAA,UAC1E,SAAQ,OAAO,MAAM,GAAG,SAAS;AAAA,CAAI;AAAA,IAC5C;AAAA,EACF;AACF;;;ADlBA,IAAM,gBAAgB,oBAAI,IAAI,CAAC,QAAQ,QAAQ,SAAS,CAAC;AAEzD,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0Bb,SAAS,QAAQ,QAAgB,KAAqB;AACpD,QAAM,OAAO,SAAS,MAAM,EAAE,QAAQ,iBAAiB,EAAE,KAAK;AAC9D,SAAO,GAAG,IAAI,IAAI,GAAG;AACvB;AAEA,eAAe,UAAU,MAAiC;AAIxD,QAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACjD,MAAI,SAAS,WAAW,KAAK,OAAO,UAAU,CAAC,GAAG;AAChD,WAAO,aAAa,CAAC,UAAU,GAAG,IAAI,CAAC;AAAA,EACzC;AACA,QAAM,EAAE,aAAa,MAAM,IAAI,UAAU,MAAM,aAAa;AAC5D,QAAM,SAAS,YAAY,CAAC;AAC5B,MAAI,CAAC,OAAQ,OAAM,IAAI,WAAW,yCAAyC;AAC3E,QAAM,IAAI,eAAe,MAAM,SAAS,IAAI;AAC5C,QAAM,SAAU,MAAM,UAAqB;AAC3C,MAAI,WAAW,UAAU,WAAW,MAAO,OAAM,IAAI,WAAW,8BAA8B;AAE9F,QAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,cAAc,MAAM;AACvD,aAAW,KAAK,SAAU,GAAE,IAAI,SAAS,CAAC,EAAE;AAC5C,QAAM,WAAW,QAAQ,OAAO,YAAY,eAAe,MAAM,KAAK,CAAC;AACvE,QAAM,QAAQ,QAAQ,OAAO,SAAS,IAAI;AAC1C,QAAM,SAAS,QAAQ,OAAO,UAAU,IAAI;AAC5C,QAAM,MAAM,QAAQ,OAAO,OAAO,EAAE;AACpC,QAAM,MAAM,YAAY,CAAC,KAAK,QAAQ,QAAQ,MAAM;AAEpD,QAAM,UAAU,MAAM,cAAc;AACpC,MAAI;AACF,UAAM,SAAS,MAAM,YAAY,SAAS;AAAA,MACxC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,CAAC,UAAU;AAClB,UAAE,IAAI,GAAG,KAAK,QAAG;AACjB,UAAE,MAAM,EAAE,OAAO,SAAS,MAAM,CAAC;AAAA,MACnC;AAAA,IACF,CAAC;AACD,UAAM,UAAU,KAAK,OAAO,KAAK;AACjC,MAAE;AAAA,MACA,EAAE,KAAK,OAAO,OAAO,MAAM,QAAQ,OAAO,QAAQ,KAAK,UAAU,OAAO;AAAA,MACxE,SAAS,GAAG,MAAM,OAAO,MAAM,SAAS,MAAM,QAAQ,CAAC,CAAC,QAAQ,KAAK,IAAI,MAAM,IAAI,GAAG,KAAK,QAAQ;AAAA,IACrG;AACA,WAAO;AAAA,EACT,UAAE;AACA,UAAM,QAAQ,MAAM;AAAA,EACtB;AACF;AAEA,eAAe,SAAS,MAAiC;AACvD,QAAM,EAAE,aAAa,MAAM,IAAI,UAAU,MAAM,aAAa;AAC5D,QAAM,SAAS,YAAY,CAAC;AAC5B,MAAI,CAAC,OAAQ,OAAM,IAAI,WAAW,wCAAwC;AAC1E,QAAM,IAAI,eAAe,MAAM,SAAS,IAAI;AAE5C,QAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,cAAc,MAAM;AACvD,aAAW,KAAK,SAAU,GAAE,IAAI,SAAS,CAAC,EAAE;AAC5C,QAAM,OAAO,QAAQ,OAAO,QAAQ,CAAC;AACrC,QAAM,QAAQ,QAAQ,OAAO,SAAS,IAAI;AAC1C,QAAM,SAAS,QAAQ,OAAO,UAAU,GAAG;AAC3C,QAAM,MAAM,YAAY,CAAC,KAAK,QAAQ,QAAQ,MAAM;AAEpD,QAAM,UAAU,MAAM,cAAc;AACpC,MAAI;AACF,UAAM,SAAS,MAAM,YAAY,SAAS;AAAA,MACxC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,CAAC,UAAU;AAClB,UAAE,IAAI,GAAG,KAAK,QAAG;AACjB,UAAE,MAAM,EAAE,OAAO,SAAS,MAAM,CAAC;AAAA,MACnC;AAAA,IACF,CAAC;AACD,UAAM,UAAU,KAAK,OAAO,KAAK;AACjC,MAAE;AAAA,MACA,EAAE,KAAK,OAAO,OAAO,MAAM,QAAQ,OAAO,QAAQ,MAAM,UAAU,OAAO,SAAS;AAAA,MAClF,SAAS,GAAG,MAAM,OAAO,MAAM,SAAS,MAAM,QAAQ,CAAC,CAAC,QAAQ,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC7F;AACA,WAAO;AAAA,EACT,UAAE;AACA,UAAM,QAAQ,MAAM;AAAA,EACtB;AACF;AAEA,eAAe,QAAQ,MAAiC;AACtD,QAAM,EAAE,aAAa,MAAM,IAAI,UAAU,MAAM,aAAa;AAC5D,QAAM,SAAS,YAAY,CAAC;AAC5B,MAAI,CAAC,OAAQ,OAAM,IAAI,WAAW,4BAA4B;AAC9D,QAAM,IAAI,eAAe,MAAM,SAAS,IAAI;AAE5C,QAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,cAAc,MAAM;AACvD,QAAM,WAAW,MAAM,QAAQ,OAAO,QAAQ,IAAI,OAAO,SAAS,SAAS;AAC3E,QAAM,OAAO,OAAO,OAAO,SAAS,YAAY,OAAO,SAAS,OAAO,OAAO,KAAK,OAAO,IAAI,IAAI,CAAC;AACnG,QAAM,MAAM,CAAC,SAAS,iBAAiB,kBAAkB,SAAS,EAAE;AAAA,IAClE,CAAC,MAAM,OAAO,OAAO,CAAC,MAAM;AAAA,EAC9B;AACA,QAAM,OAAO;AAAA,IACX,SAAS,OAAO;AAAA,IAChB,UAAU,eAAe,MAAM,KAAK;AAAA,IACpC,QAAS,OAAO,QAAgD,UAAU;AAAA,IAC1E;AAAA,IACA,UAAU;AAAA,IACV,WAAW;AAAA,IACX;AAAA,EACF;AACA,MAAI,EAAE,KAAM,GAAE,KAAK,MAAM,EAAE;AAAA,OACtB;AACH,eAAW,KAAK,SAAU,GAAE,IAAI,SAAS,CAAC,EAAE;AAC5C,YAAQ,OAAO;AAAA,MACb,eAAe,OAAO,KAAK,OAAO,CAAC;AAAA,aACnB,KAAK,aAAa,OAAO,WAAW,GAAG,KAAK,QAAQ,GAAG;AAAA,aACvD,OAAO,KAAK,UAAU,WAAW,CAAC;AAAA,aAClC,QAAQ;AAAA,aACR,KAAK,SAAS,KAAK,KAAK,IAAI,IAAI,QAAQ;AAAA,aACxC,IAAI,KAAK,IAAI,CAAC;AAAA;AAAA,IAChC;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,eAAe,MAA6B;AACnD,QAAMA,WAAU,cAAc,YAAY,GAAG;AAE7C,MAAI;AACF,WAAQA,SAAQ,GAAG,IAAI,eAAe,EAA0B;AAAA,EAClE,QAAQ;AAAA,EAGR;AACA,MAAI,QAAuB;AAC3B,MAAI;AACF,YAAQ,cAAc,YAAY,QAAQ,IAAI,CAAC;AAAA,EACjD,QAAQ;AACN,QAAI;AACF,cAAQA,SAAQ,QAAQ,IAAI;AAAA,IAC9B,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,MAAM,QAAQ,KAAK;AACvB,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAI;AACF,YAAM,MAAM,KAAK,MAAM,aAAa,KAAK,KAAK,cAAc,GAAG,MAAM,CAAC;AAItE,UAAI,IAAI,SAAS,QAAQ,IAAI,QAAS,QAAO,IAAI;AAAA,IACnD,QAAQ;AAAA,IAER;AACA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK,QAAO;AAC3B,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEA,eAAe,YAAY,MAAiC;AAC1D,QAAM,EAAE,MAAM,IAAI,UAAU,MAAM,aAAa;AAC/C,QAAM,IAAI,eAAe,MAAM,SAAS,IAAI;AAC5C,QAAM,WAAmC,CAAC;AAE1C,MAAI;AACF,UAAM,MAAM,KAAK;AAAA,MACf,aAAa,cAAc,IAAI,IAAI,mBAAmB,YAAY,GAAG,CAAC,GAAG,MAAM;AAAA,IACjF;AACA,aAAS,YAAY,IAAI,IAAI;AAAA,EAC/B,QAAQ;AACN,aAAS,YAAY,IAAI;AAAA,EAC3B;AACA,aAAW,QAAQ,CAAC,eAAe,mBAAmB,gBAAgB,YAAY,GAAG;AACnF,aAAS,IAAI,IAAI,eAAe,IAAI,KAAK;AAAA,EAC3C;AACA,MAAI,EAAE,KAAM,GAAE,KAAK,EAAE,SAAS,GAAG,EAAE;AAAA,MAC9B,YAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,QAAQ,EAAG,SAAQ,OAAO,MAAM,GAAG,CAAC,IAAI,CAAC;AAAA,CAAI;AACtF,SAAO;AACT;AAEA,eAAe,WAAW,MAAiC;AACzD,QAAM,EAAE,aAAa,MAAM,IAAI,UAAU,MAAM,aAAa;AAC5D,QAAM,SAAS,YAAY,CAAC;AAC5B,MAAI,CAAC,OAAQ,OAAM,IAAI,WAAW,0CAA0C;AAC5E,QAAM,IAAI,eAAe,KAAK;AAC9B,QAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,cAAc,MAAM;AACvD,aAAW,KAAK,SAAU,GAAE,IAAI,SAAS,CAAC,EAAE;AAC5C,QAAM,EAAE,UAAU,WAAW,IAAI,aAAa,MAAM;AACpD,QAAM,SAAS,aAAa,CAAC,KAAK,QAAQ;AACxC,UAAM,OAAO,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,EAAE;AACjD,QAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,QAAI,IAAI,SAAS,YAAY,aAAa,QAAQ;AAAA,EACpD,CAAC;AACD,QAAM,OAAO,QAAQ,OAAO,QAAQ,CAAC;AACrC,QAAM,IAAI,QAAc,CAAC,YAAY,OAAO,OAAO,MAAM,OAAO,CAAC;AACjE,QAAM,OAAO,OAAO,QAAQ;AAC5B,QAAM,MAAM,oBAAoB,OAAO,SAAS,YAAY,OAAO,KAAK,OAAO,IAAI;AACnF,UAAQ,OAAO,MAAM,GAAG,GAAG;AAAA,CAAI;AAC/B,IAAE,IAAI,iDAA4C;AAClD,QAAM,IAAI,QAAQ,MAAM;AAAA,EAAC,CAAC;AAC1B,SAAO;AACT;AAKA,IAAM,aAAa,oBAAI,IAAI,CAAC,UAAU,UAAU,QAAQ,UAAU,QAAQ,UAAU,CAAC;AAErF,eAAe,aAAa,MAAgB,WAAW,OAAwB;AAC7E,aAAW,QAAQ,CAAC,cAAc,kBAAkB,GAAG;AACrD,QAAI;AACJ,QAAI;AACF,YAAO,MAAM,OAAO;AAAA,IACtB,QAAQ;AACN;AAAA,IACF;AACA,QAAI,OAAO,IAAI,QAAQ,WAAY;AACnC,QAAI,YAAY,KAAK,CAAC,GAAG;AACvB,cAAQ,OAAO,MAAM,oBAAoB,KAAK,CAAC,CAAC,iBAAiB,KAAK,CAAC,CAAC;AAAA,CAAM;AAAA,IAChF;AACA,WAAO,MAAM,IAAI,IAAI,IAAI;AAAA,EAC3B;AACA,UAAQ,OAAO;AAAA,IACb;AAAA;AAAA,mBAEsB,KAAK,KAAK,GAAG,CAAC;AAAA;AAAA,EACtC;AACA,SAAO;AACT;AAEA,eAAe,OAAwB;AACrC,QAAM,CAAC,KAAK,GAAG,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC;AAC3C,MAAI,CAAC,OAAO,QAAQ,UAAU,QAAQ,YAAY,QAAQ,MAAM;AAC9D,YAAQ,OAAO,MAAM,IAAI;AACzB,WAAO,MAAM,UAAU;AAAA,EACzB;AACA,MAAI,QAAQ,YAAa,QAAO,YAAY,CAAC,QAAQ,CAAC;AACtD,MAAI,WAAW,IAAI,GAAG,EAAG,QAAO,aAAa,CAAC,KAAK,GAAG,IAAI,CAAC;AAC3D,UAAQ,KAAK;AAAA,IACX,KAAK;AACH,aAAO,UAAU,IAAI;AAAA,IACvB,KAAK;AACH,aAAO,SAAS,IAAI;AAAA,IACtB,KAAK;AACH,aAAO,QAAQ,IAAI;AAAA,IACrB,KAAK;AACH,aAAO,YAAY,IAAI;AAAA,IACzB,KAAK;AACH,aAAO,WAAW,IAAI;AAAA;AAAA;AAAA,IAGxB,KAAK;AACH,aAAO,aAAa,MAAM,IAAI;AAAA,IAChC;AACE,YAAM,IAAI,WAAW,oBAAoB,GAAG,uBAAkB;AAAA,EAClE;AACF;AAEA,KAAK,EACF,KAAK,CAAC,SAAS,QAAQ,KAAK,IAAI,CAAC,EACjC,MAAM,CAAC,MAAM;AACZ,MAAI,aAAa,YAAY;AAC3B,YAAQ,OAAO,MAAM,gBAAgB,EAAE,OAAO;AAAA,CAAI;AAClD,YAAQ,KAAK,UAAU;AAAA,EACzB;AACA,MAAI,aAAa,yBAAyB;AACxC,YAAQ,OAAO,MAAM,GAAG,EAAE,OAAO;AAAA,CAAI;AACrC,YAAQ,KAAK,eAAe;AAAA,EAC9B;AACA,UAAQ,OAAO,MAAM,UAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,CAAI;AAC7E,UAAQ,KAAK,UAAU;AACzB,CAAC;","names":["require"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vosjs/cli",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Command line for the vos programmatic video engine — render deterministic videos and stills from vos configs, headlessly, from your terminal or an AI agent.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,8 +43,8 @@
43
43
  "dependencies": {
44
44
  "playwright": "^1.49.0",
45
45
  "@vosjs/elements": "^0.3.1",
46
- "@vosjs/tween": "^0.7.2",
47
- "@vosjs/core": "^0.10.0"
46
+ "@vosjs/core": "^0.10.0",
47
+ "@vosjs/tween": "^0.7.2"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/node": "^22",