@vosjs/cli 0.53.4 → 0.53.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/output.ts","../src/check.ts","../src/plugin/knobs.ts","../src/programAudio.ts"],"sourcesContent":["import { writeFile } from 'node:fs/promises'\nimport { existsSync, readFileSync, statSync } 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 { helpFlagsFor, unusedFlagsMessage } from './flagUse'\nimport {\n createReporter,\n EXIT_ERROR,\n EXIT_NO_BROWSER,\n EXIT_OK,\n EXIT_USAGE,\n} from './output'\nimport {\n loadVosConfig,\n configDuration,\n directoryKind,\n loadProgramDirectory,\n readSourceText,\n} from './loadConfig'\nimport { launchBrowser, BrowserUnavailableError } from './browser'\nimport { renderVideo, renderStill, reencodeStill, previewPages } from './render'\nimport { runCheck } from './check'\nimport { programAudio } from './programAudio'\nimport {\n applyDataSets,\n parseTimes,\n setFlags,\n stillFormat,\n stillOutFor,\n videoFormat,\n} from './outputs'\nimport { platformOrigin, resolveCredential } from './plugin/platform'\n\nconst BOOLEAN_FLAGS = new Set(['json', 'help', 'version'])\n\nconst HELP_ENGINE = `vos — command line for the vos programmatic video engine (https://vos.so/engine)\n\nEngine verbs (local, no account, no network beyond the render page's CDN deps)\n vos render <config.json|url|take> [out.mp4|out.webm] [--width 1920] [--height 1080] [--fps 30]\n [--duration <s>] [--format webm|mp4] [--set data.<key>=<value>]... [--json]\n the output's name picks the container; a program document's sound is mixed in\n vos still <config.json|url> [out.webp|out.png|out.jpg] [--time 0 | --times 0,1.5,50%]\n [--width] [--height] [--set data.<key>=<value>]... [--json]\n vos info <config.json|url> [--json]\n vos check <config.json|url> [--json]\n migrate → schema → syntax → compile → determinism/dialect lints, all local\n vos preview <config.json|url> [--port 0]\n vos versions [--json]\n`\n\nconst HELP_CONVENTIONS = `\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, 4 the recorder met a sign-in instead of the page\n it was asked for (nothing was recorded; it needs a session, not a new\n script). vos <verb> --help prints that verb's flags.\n`\n\n/** An `audio` element in the config: something a render never mixes. */\nfunction hasAudioElements(config: unknown): boolean {\n const elements = (config as { elements?: unknown }).elements\n return (\n Array.isArray(elements) &&\n elements.some(\n (e) =>\n !!e &&\n typeof e === 'object' &&\n (e as { type?: unknown }).type === 'audio',\n )\n )\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 (its doc.json is a recording\n // document) renders through the plugin; a program directory (config.json,\n // composed with a program document when one sits beside it) and every\n // other source are engine config renders. A deterministic sniff, never a\n // flag — and the sniff reads the document, because a program directory\n // carries a doc.json too.\n const first = argv.find((a) => !a.startsWith('-'))\n if (first && directoryKind(first) === 'take') {\n return delegate(['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 = videoFormat(\n flags.format === undefined ? undefined : String(flags.format),\n positionals[1],\n )\n\n const loaded = await loadVosConfig(source)\n for (const w of loaded.warnings) r.log(`note: ${w}`)\n void flags.set\n const config = applyDataSets(\n loaded.config as Record<string, unknown>,\n setFlags(argv),\n )\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 // A program document's sound rides its studio entry; mix it like a take's.\n const audio = await programAudio(config as Record<string, unknown>, {\n baseDir:\n existsSync(source) && statSync(source).isDirectory()\n ? source\n : dirname(source),\n duration,\n origin: platformOrigin({}),\n key: resolveCredential(),\n log: (line) => r.log(line),\n })\n if (audio) r.log(`mixing ${audio.clips} sound${audio.clips === 1 ? '' : 's'}`)\n if (hasAudioElements(config)) {\n r.log(\n 'note: an audio ELEMENT is not mixed by a render; put the sound on the program document instead (doc.json audio: [{ key, start, in, out, duration, gain }])',\n )\n }\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 audioProducerCode: audio?.producerCode,\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 {\n out,\n bytes: result.bytes.length,\n width,\n height,\n fps,\n duration,\n format,\n audio: audio?.clips ?? 0,\n },\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)\n throw new UsageError(\n 'vos still <config.json|url> [out.webp|out.png|out.jpg] [--time t | --times a,b,50%]',\n )\n const r = createReporter(flags.json === true)\n\n const loaded = await loadVosConfig(source)\n for (const w of loaded.warnings) r.log(`note: ${w}`)\n void flags.set\n const config = applyDataSets(\n loaded.config as Record<string, unknown>,\n setFlags(argv),\n )\n const width = numFlag(flags, 'width', 1280)\n const height = numFlag(flags, 'height', 720)\n const out = positionals[1] ?? outName(source, 'webp')\n const encoding = stillFormat(out)\n const timesRaw = flags.times === undefined ? undefined : String(flags.times)\n if (timesRaw !== undefined && flags.time !== undefined)\n throw new UsageError('--time and --times: pass one')\n const times =\n timesRaw !== undefined\n ? parseTimes(timesRaw, configDuration(config) ?? 5)\n : [numFlag(flags, 'time', 0)]\n const many = times.length > 1\n\n // One browser for every time: each still is its own capture page.\n const browser = await launchBrowser()\n try {\n const written: { out: string; bytes: number; time: number }[] = []\n for (const time of times) {\n const target = stillOutFor(out, time, many)\n const result = await renderStill(browser, {\n config,\n width,\n height,\n time,\n onPhase: (phase) => {\n r.event({ event: 'phase', phase, time })\n },\n })\n // The engine captures WebP; a .png or .jpg name is re-encoded in the\n // page, so the bytes always match the name.\n const bytes =\n encoding === 'webp'\n ? result.bytes\n : await reencodeStill(browser, result.bytes, encoding)\n await writeFile(target, bytes)\n written.push({ out: target, bytes: bytes.length, time })\n r.log(\n `Wrote ${target} (${(bytes.length / 1024).toFixed(0)} KB @ t=${Number(time.toFixed(2))}s)`,\n )\n }\n const first = written[0]\n r.done(\n many\n ? { stills: written, width, height, format: encoding }\n : { ...first, width, height, format: encoding },\n many\n ? `Wrote ${written.length} stills (${width}x${height})`\n : `Wrote ${first.out} (${(first.bytes / 1024).toFixed(0)} KB, ${width}x${height} @ t=${first.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 =\n typeof config.data === 'object' && config.data !== null\n ? Object.keys(config.data)\n : []\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:\n (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(\n readFileSync(join(dir, 'package.json'), 'utf8'),\n ) 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\nfunction ownVersion(): string {\n try {\n const own = JSON.parse(\n readFileSync(\n fileURLToPath(new URL('../package.json', import.meta.url)),\n 'utf8',\n ),\n ) as { version: string }\n return own.version\n } catch {\n return '(unknown)'\n }\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 versions['@vosjs/cli'] = ownVersion()\n for (const name of [\n '@vosjs/core',\n '@vosjs/elements',\n '@vosjs/tween',\n '@vosjs/editor',\n '@vosjs/timeline',\n '@vosjs/studio-core',\n '@vosjs/render-core',\n '@vosjs/shared',\n 'mediabunny',\n 'playwright',\n ]) {\n versions[name] = packageVersion(name) ?? '(not found)'\n }\n if (r.json) r.done({ versions }, '')\n else\n for (const [k, v] of Object.entries(versions))\n 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 cmdCheck(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos check <config.json>')\n const r = createReporter(flags.json === true)\n\n // A directory is a take (refused here: its document is `vos validate`'s\n // job) or a program directory (config.json, composed with the program\n // document beside it so the ladder reads the layers' strings too).\n const kind = /^https?:\\/\\//.test(source) ? 'none' : directoryKind(source)\n if (kind === 'take') {\n throw new UsageError(\n `${source} is a take (its doc.json is a recording document). vos validate ${source} lints it; vos render ${source} renders it.`,\n )\n }\n let raw: string\n let parsed: unknown\n try {\n if (kind === 'program') {\n const notes: string[] = []\n parsed = await loadProgramDirectory(source, notes)\n for (const n of notes) r.log(`note: ${n}`)\n raw = ''\n } else {\n raw = await readSourceText(source)\n parsed = JSON.parse(raw)\n }\n } catch (e) {\n if (!(e instanceof SyntaxError)) throw e\n parsed = undefined\n if (!r.json) process.stdout.write(`error [json] ${(e as Error).message}\\n`)\n r.done(\n {\n ok: false,\n errors: 1,\n warnings: 0,\n issues: [\n { level: 'error', source: 'json', message: (e as Error).message },\n ],\n },\n `${source}: 1 error`,\n )\n return EXIT_ERROR\n }\n\n const result = runCheck(parsed)\n if (r.json) {\n r.done(\n {\n ok: result.ok,\n errors: result.errors,\n warnings: result.warnings,\n issues: result.issues,\n },\n '',\n )\n } else {\n for (const i of result.issues) {\n process.stdout.write(`${i.level} [${i.source}] ${i.message}\\n`)\n }\n process.stdout.write(\n result.ok\n ? `${source}: ok (${result.warnings} warning${result.warnings === 1 ? '' : 's'})\\n`\n : `${source}: ${result.errors} error${result.errors === 1 ? '' : 's'}, ${result.warnings} warning${result.warnings === 1 ? '' : 's'}\\n`,\n )\n }\n return result.ok ? EXIT_OK : EXIT_ERROR\n}\n\n// ---------------------------------------------------------------------------\n// Every verb this file does not own is a take-pipeline or vos.so verb from\n// `./plugin` (what used to ship separately as @vosso/vos-plugin). It loads on\n// demand, so the engine verbs never pay for the recorder's imports, and the\n// old names (`@vosso/cli`, `@vosso/voila-cli`, the `vos voila` alias) keep\n// resolving here for scripts that still use them.\n// ---------------------------------------------------------------------------\n\nasync function delegate(argv: string[], viaAlias = false): Promise<number> {\n const { run } = await import('./plugin/run')\n if (viaAlias && argv[0]) {\n process.stderr.write(\n `note: \"vos voila ${argv[0]}\" is now \"vos ${argv[0]}\".\\n`,\n )\n }\n return await run(argv)\n}\n\nasync function printHelp(): Promise<void> {\n process.stdout.write(HELP_ENGINE)\n const { manifest } = await import('./plugin/manifest')\n process.stdout.write('\\nTake pipeline + vos.so verbs\\n')\n for (const v of manifest.verbs) {\n if (v.name === 'render') continue // polymorphic — already listed above\n process.stdout.write(` vos ${v.name.padEnd(8)} ${v.summary}\\n`)\n }\n process.stdout.write(HELP_CONVENTIONS)\n}\n\nconst ENGINE_VERBS = new Set([\n 'render',\n 'still',\n 'info',\n 'versions',\n 'preview',\n 'check',\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 await printHelp()\n return cmd ? EXIT_OK : EXIT_USAGE\n }\n if (cmd === '--version') return cmdVersions(['--json'])\n if (!ENGINE_VERBS.has(cmd) && cmd !== 'voila') return delegate([cmd, ...rest])\n // An engine verb asked for its flags: the engine's usage lines, plus the\n // take pipeline's where the verb is shared (render takes a config OR a take).\n if (rest.includes('--help') || rest.includes('-h')) {\n const engine = HELP_ENGINE.split('\\n').filter((l) =>\n l.startsWith(` vos ${cmd} `),\n )\n const { verbHelp } = await import('./plugin/run')\n const take = verbHelp(cmd)\n process.stdout.write(\n `${engine.join('\\n')}\\n${take.includes('no such verb') ? '' : take}`,\n )\n return EXIT_OK\n }\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 'check':\n return cmdCheck(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 delegate(rest, true)\n default:\n throw new UsageError(`unknown command \"${cmd}\" — run vos help`)\n }\n}\n\nmain()\n .then(async (code) => {\n // A run that succeeded while ignoring a flag it was given did not do\n // what was asked (flagUse.ts): say so, and do not exit 0.\n if (code === EXIT_OK) {\n const verb = process.argv[2] ?? ''\n const { HELP } = await import('./plugin/run')\n const documented = [\n ...helpFlagsFor(HELP_ENGINE, verb),\n ...helpFlagsFor(HELP, verb),\n ]\n const message = unusedFlagsMessage(verb, [...new Set(documented)])\n if (message) {\n process.stderr.write(`usage error: ${message}\\n`)\n process.exit(EXIT_USAGE)\n }\n }\n process.exit(code)\n })\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(\n `error: ${e instanceof Error ? e.message : String(e)}\\n`,\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 * 4 the recorder met a sign-in (the take pipeline's wall check)\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)\n process.stdout.write(`${JSON.stringify({ event: 'done', ...obj })}\\n`)\n else process.stdout.write(`${humanLine}\\n`)\n },\n }\n}\n","/**\n * `vos check` — the local validation pipeline, pure so it can be unit-tested:\n * envelope unwrap → migrate → schema → compile → determinism + dialect lints.\n * Everything runs locally (no network, no browser). The platform runs the\n * same compiler server-side, so a clean check is a push that will compile.\n */\nimport {\n CURRENT_CONFIG_VERSION,\n compileVosConfig,\n migrateConfig,\n vosConfigJsonSchema,\n} from '@vosjs/core'\nimport {\n lintVosConfig,\n lintVosDialect,\n lintVosFonts,\n lintVosPostprocessing,\n} from '@vosjs/core/lint'\nimport { knobWarnings } from './plugin/knobs'\n\nexport interface CheckIssue {\n level: 'error' | 'warn'\n source:\n | 'schema'\n | 'syntax'\n | 'compile'\n | 'determinism'\n | 'dialect'\n | 'shape'\n | 'fonts'\n | 'postprocessing'\n message: string\n}\n\nexport interface CheckResult {\n ok: boolean\n errors: number\n warnings: number\n issues: CheckIssue[]\n /** The migrated config (params/presets untouched) — what a push should send. */\n config: Record<string, unknown> | null\n}\n\n// Top-level keys the schema does not know but the platform preserves on push.\nconst PLATFORM_EXTRA_KEYS = new Set(['params', 'presets'])\n\nexport function runCheck(parsed: unknown): CheckResult {\n const issues: CheckIssue[] = []\n const finish = (config: Record<string, unknown> | null): CheckResult => {\n const errors = issues.filter((i) => i.level === 'error').length\n return {\n ok: errors === 0,\n errors,\n warnings: issues.length - errors,\n issues,\n config,\n }\n }\n\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n issues.push({\n level: 'error',\n source: 'shape',\n message: 'not a JSON object',\n })\n return finish(null)\n }\n\n // API endpoints wrap the config: { config: {...} } — accept both shapes.\n let obj = parsed as Record<string, unknown>\n if (\n typeof obj.config === 'object' &&\n obj.config !== null &&\n !('createTimeline' in obj)\n ) {\n obj = obj.config as Record<string, unknown>\n }\n\n const version = obj.version\n let migrated: Record<string, unknown>\n try {\n migrated = migrateConfig(obj) as Record<string, unknown>\n } catch (e) {\n issues.push({\n level: 'error',\n source: 'shape',\n message: `migration failed: ${e instanceof Error ? e.message : String(e)}`,\n })\n return finish(null)\n }\n if (version === undefined) {\n // It compiles (the engine reads an absent version as the current one),\n // but a push is refused: a stored config must say which schema era it was\n // written against, and nobody can tell that from the file later.\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `config declares no \"version\". It plays here, but a push is refused without one. Add \"version\": ${CURRENT_CONFIG_VERSION}.`,\n })\n } else if (version !== CURRENT_CONFIG_VERSION) {\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `config is v${String(version)}, migrated to v${CURRENT_CONFIG_VERSION}. Save the migrated form.`,\n })\n }\n\n const schema = vosConfigJsonSchema.safeParse(migrated)\n if (!schema.success) {\n for (const i of schema.error.issues.slice(0, 10)) {\n issues.push({\n level: 'error',\n source: 'schema',\n message: `${i.path.join('.') || '(root)'}: ${i.message}`,\n })\n }\n return finish(null)\n }\n\n // Keys the platform's schema will silently drop on push (params/presets\n // excepted — those are re-attached by contract).\n const known = new Set([\n ...Object.keys(vosConfigJsonSchema.shape),\n ...PLATFORM_EXTRA_KEYS,\n ])\n for (const key of Object.keys(migrated)) {\n if (!known.has(key)) {\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `unknown top-level key \"${key}\" — the platform drops it on push`,\n })\n }\n }\n\n // Knobs and Looks vos.so would drop (the push still saves, without them).\n for (const message of knobWarnings(migrated as Record<string, unknown>)) {\n issues.push({ level: 'warn', source: 'shape', message })\n }\n\n // Sound as an ELEMENT is not a track: no timeline row, no gain or fades,\n // nothing a render mixes, and a `data:` source inflates the config past\n // what a claimable push can carry. The document is where sound goes.\n const elements = Array.isArray(migrated.elements) ? migrated.elements : []\n for (const el of elements) {\n if (!el || typeof el !== 'object') continue\n const e = el as { type?: unknown; id?: unknown; src?: unknown }\n if (e.type !== 'audio') continue\n const inline =\n typeof e.src === 'string' && e.src.startsWith('data:')\n ? ` (embedded, ${Math.round(e.src.length / 1024)} KB of config)`\n : ''\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `audio element \"${String(e.id ?? '?')}\"${inline} is not a track: renders do not mix it and the studio cannot edit it. Put the file beside the config and name it in doc.json (audio: [{ key: \"score.ogg\", start, in, out, duration, gain }]); vos push uploads it`,\n })\n }\n\n // Function strings are pasted into the template as text, so the compiler\n // cannot see a syntax error — parse each one here (construction only,\n // nothing executes).\n let syntaxErrors = 0\n for (const key of [\n 'setup',\n 'createContent',\n 'createTimeline',\n 'onFrame',\n ] as const) {\n const src = migrated[key]\n if (typeof src !== 'string' || !src.length) continue\n try {\n new Function(`\"use strict\"; return (${src}\\n)`)\n } catch (e) {\n syntaxErrors++\n issues.push({\n level: 'error',\n source: 'syntax',\n message: `${key}: ${e instanceof Error ? e.message : String(e)}`,\n })\n }\n }\n if (syntaxErrors) return finish(migrated)\n\n try {\n // Same options as `vos render` — a clean check is a config the CLI's own\n // render path will accept.\n compileVosConfig(migrated as never, { tweenEngine: 'vos' })\n } catch (e) {\n issues.push({\n level: 'error',\n source: 'compile',\n message: e instanceof Error ? e.message : String(e),\n })\n return finish(migrated)\n }\n\n for (const i of lintVosConfig(migrated as never)) {\n issues.push({\n level: i.severity === 'error' ? 'error' : 'warn',\n source: 'determinism',\n message: `${i.fn}:${i.line} [${i.rule}] ${i.message}`,\n })\n }\n for (const i of lintVosDialect(migrated as never)) {\n issues.push({\n level: i.severity === 'error' ? 'error' : 'warn',\n source: 'dialect',\n message: `${i.fn}:${i.line} [${i.rule}] ${i.message}`,\n })\n }\n for (const i of lintVosFonts(migrated as never)) {\n issues.push({\n level: 'warn',\n source: 'fonts',\n message: `[${i.rule}] ${i.message}`,\n })\n }\n for (const i of lintVosPostprocessing(migrated as never)) {\n issues.push({\n level: 'warn',\n source: 'postprocessing',\n message: `[${i.rule}] ${i.message}`,\n })\n }\n\n return finish(migrated)\n}\n","/**\n * What vos.so keeps of a config's knobs (`params`) and Looks (`presets`).\n *\n * A vos.so fact, so it lives in the platform client: the platform keeps at\n * most 12 knobs and 8 Looks, each within its field limits, and drops the\n * rest ONE BY ONE with a warning (it used to drop them all, silently). This\n * mirrors that rule so `vos check` says the same thing before a push does.\n */\nexport const MAX_PARAMS = 12\nexport const MAX_LOOKS = 8\n\nconst KINDS = new Set(['number', 'color', 'select', 'toggle', 'text', 'font'])\n\nconst str = (v: unknown, max: number, min = 0) =>\n typeof v === 'string' && v.length >= min && v.length <= max\n\n/** Why one knob would be dropped, or null when vos.so keeps it. */\nexport function knobProblem(p: unknown): string | null {\n if (!p || typeof p !== 'object') return 'not an object'\n const k = p as Record<string, unknown>\n if (!str(k.key, 40, 1)) return 'key must be 1 to 40 characters'\n if (typeof k.kind !== 'string' || !KINDS.has(k.kind))\n return `kind must be one of ${[...KINDS].join(', ')}`\n if (k.label !== undefined && !str(k.label, 60))\n return 'label: at most 60 characters'\n if (k.hint !== undefined && !str(k.hint, 140))\n return 'hint: at most 140 characters'\n if (k.unit !== undefined && !str(k.unit, 8))\n return 'unit: at most 8 characters'\n if (k.group !== undefined && !str(k.group, 24))\n return 'group: at most 24 characters'\n if (k.options !== undefined) {\n if (!Array.isArray(k.options) || k.options.length > 12)\n return 'options: at most 12'\n if (k.options.some((o) => !str(o, 40, 1)))\n return 'options: each 1 to 40 characters'\n }\n const d = k.default\n if (\n !(\n typeof d === 'number' ||\n typeof d === 'boolean' ||\n (typeof d === 'string' && d.length <= 280)\n )\n )\n return 'default must be a number, a boolean or at most 280 characters'\n return null\n}\n\n/** Why one Look would be dropped, or null when vos.so keeps it. */\nexport function lookProblem(l: unknown): string | null {\n if (!l || typeof l !== 'object') return 'not an object'\n const look = l as Record<string, unknown>\n if (!str(look.name, 24, 1)) return 'name must be 1 to 24 characters'\n if (!look.values || typeof look.values !== 'object') return 'values missing'\n return null\n}\n\n/** Every knob and Look vos.so would drop, in words, in order. */\nexport function knobWarnings(config: Record<string, unknown>): string[] {\n const out: string[] = []\n const sort = (\n raw: unknown,\n noun: 'knob' | 'look',\n field: string,\n max: number,\n problem: (e: unknown) => string | null,\n ) => {\n if (raw === undefined) return\n if (!Array.isArray(raw)) {\n out.push(`${noun === 'knob' ? 'params' : 'presets'} is not a list`)\n return\n }\n let kept = 0\n raw.forEach((entry, i) => {\n const id =\n entry && typeof (entry as Record<string, unknown>)[field] === 'string'\n ? `${noun} \"${String((entry as Record<string, unknown>)[field])}\"`\n : `${noun} #${i + 1}`\n const why = problem(entry)\n if (why) out.push(`${id} would be dropped on vos.so (${why})`)\n else if (kept >= max)\n out.push(\n `${id} would be dropped on vos.so: a vos keeps at most ${max} ${noun}s${noun === 'knob' ? ' (the rest can stay bound data)' : ''}`,\n )\n else kept += 1\n })\n }\n sort(config.params, 'knob', 'key', MAX_PARAMS, knobProblem)\n sort(config.presets, 'look', 'name', MAX_LOOKS, lookProblem)\n return out\n}\n","/**\n * The sound a program document adds, for a LOCAL render of the program.\n *\n * A composed program carries its `doc.audio` clips on the studio stack entry,\n * exactly as a take does, and the capture page mixes them through the same\n * producer a take render uses. The one difference is WHERE the bytes are: a\n * capture page has no origin of its own, so a clip keyed by a file beside the\n * document (`score.ogg`) or by a hosted asset path (`/api/assets/…`) cannot be\n * fetched from inside it. Each is inlined as a `data:` URI here: a local file\n * read from disk, a hosted asset downloaded with the caller's credential.\n * A clip that cannot be reached is left out and said, never a silent track.\n */\nimport { existsSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { audioProducerCode, studioEntryData } from '@vosjs/render-core'\nimport { studioAudioPlan } from '@vosjs/studio-core'\nimport { MEDIA_HEAD_BYTES, resolveMediaType } from './plugin/container'\nimport type { EnvelopePoint, LoweredAudioClip } from '@vosjs/studio-core'\n\nexport interface ProgramAudioOptions {\n /** The directory the document's relative keys are read against. */\n baseDir: string\n /** The output length the mix is cut to, seconds. */\n duration: number\n /** For a clip keyed by a hosted asset: where it lives and who may read it. */\n origin?: string\n key?: string | null\n log: (line: string) => void\n}\n\nexport interface ProgramAudio {\n producerCode: string\n clips: number\n}\n\nfunction dataUri(bytes: Uint8Array, filename: string): string {\n const { type } = resolveMediaType({\n head: bytes.subarray(0, MEDIA_HEAD_BYTES),\n filename,\n })\n return `data:${type};base64,${Buffer.from(bytes).toString('base64')}`\n}\n\nasync function reach(\n clipKey: string,\n opts: ProgramAudioOptions,\n): Promise<string | null> {\n if (/^(data:|https?:)/.test(clipKey)) return clipKey\n if (clipKey.startsWith('/api/')) {\n if (!opts.origin || !opts.key) {\n opts.log(\n `note: sound ${clipKey} is a hosted asset and no credential is set, so the render leaves it out (vos login, or keep the file beside the document)`,\n )\n return null\n }\n const res = await fetch(`${opts.origin}${clipKey}`, {\n headers: { authorization: `Bearer ${opts.key}` },\n })\n if (!res.ok) {\n opts.log(\n `note: sound ${clipKey} could not be read (${res.status}), so the render leaves it out`,\n )\n return null\n }\n return dataUri(new Uint8Array(await res.arrayBuffer()), clipKey)\n }\n const file = join(opts.baseDir, clipKey.replace(/^\\/+/, ''))\n if (!existsSync(file)) {\n opts.log(\n `note: sound ${clipKey} is not beside the document, so the render leaves it out`,\n )\n return null\n }\n return dataUri(new Uint8Array(await readFile(file)), file)\n}\n\n/** The producer for a composed program's sound, or null when it adds none. */\nexport async function programAudio(\n config: Record<string, unknown>,\n opts: ProgramAudioOptions,\n): Promise<ProgramAudio | null> {\n const entry = studioEntryData(config.stack)\n const clips = Array.isArray(entry?.audio)\n ? (entry.audio as LoweredAudioClip[])\n : []\n if (!clips.length) return null\n const reached: LoweredAudioClip[] = []\n for (const clip of clips) {\n const key = await reach(clip.key, opts)\n if (key) reached.push({ ...clip, key })\n }\n if (!reached.length) return null\n const duckEnv = Array.isArray(entry?.duckEnv)\n ? (entry.duckEnv as EnvelopePoint[])\n : []\n const plan = studioAudioPlan(reached, duckEnv, opts.duration)\n return {\n producerCode: audioProducerCode({ plan }),\n clips: reached.length,\n }\n}\n"],"mappings":"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","names":["existsSync","join","existsSync","require","join"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/output.ts","../src/check.ts","../src/plugin/knobs.ts","../src/programAudio.ts"],"sourcesContent":["import { writeFile } from 'node:fs/promises'\nimport { existsSync, readFileSync, statSync } 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 { helpFlagsFor, unusedFlagsMessage } from './flagUse'\nimport {\n createReporter,\n EXIT_ERROR,\n EXIT_NO_BROWSER,\n EXIT_OK,\n EXIT_USAGE,\n} from './output'\nimport {\n loadVosConfig,\n configDuration,\n directoryKind,\n loadProgramDirectory,\n readSourceText,\n} from './loadConfig'\nimport { launchBrowser, BrowserUnavailableError } from './browser'\nimport {\n inspectStill,\n previewPages,\n reencodeStill,\n renderStill,\n renderVideo,\n stillWarnings,\n} from './render'\nimport { runCheck } from './check'\nimport { programAudio } from './programAudio'\nimport {\n applyDataSets,\n parseTimes,\n setFlags,\n stillFormat,\n stillOutFor,\n videoFormat,\n} from './outputs'\nimport { platformOrigin, resolveCredential } from './plugin/platform'\n\nconst BOOLEAN_FLAGS = new Set(['json', 'help', 'version'])\n\nconst HELP_ENGINE = `vos — command line for the vos programmatic video engine (https://vos.so/engine)\n\nEngine verbs (local, no account, no network beyond the render page's CDN deps)\n vos render <config.json|url|take> [out.mp4|out.webm] [--width 1920] [--height 1080] [--fps 30]\n [--duration <s>] [--format webm|mp4] [--set data.<key>=<value>]... [--json]\n the output's name picks the container; a program document's sound is mixed in\n vos still <config.json|url> [out.webp|out.png|out.jpg] [--time 0 | --times 0,1.5,50%]\n [--width] [--height] [--set data.<key>=<value>]... [--json]\n vos info <config.json|url> [--json]\n vos check <config.json|url> [--json]\n migrate → schema → syntax → compile → determinism/dialect lints, all local\n vos preview <config.json|url> [--port 0]\n vos versions [--json]\n`\n\nconst HELP_CONVENTIONS = `\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, 4 the recorder met a sign-in instead of the page\n it was asked for (nothing was recorded; it needs a session, not a new\n script). vos <verb> --help prints that verb's flags.\n`\n\n/** An `audio` element in the config: something a render never mixes. */\nfunction hasAudioElements(config: unknown): boolean {\n const elements = (config as { elements?: unknown }).elements\n return (\n Array.isArray(elements) &&\n elements.some(\n (e) =>\n !!e &&\n typeof e === 'object' &&\n (e as { type?: unknown }).type === 'audio',\n )\n )\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 (its doc.json is a recording\n // document) renders through the plugin; a program directory (config.json,\n // composed with a program document when one sits beside it) and every\n // other source are engine config renders. A deterministic sniff, never a\n // flag — and the sniff reads the document, because a program directory\n // carries a doc.json too.\n const first = argv.find((a) => !a.startsWith('-'))\n if (first && directoryKind(first) === 'take') {\n return delegate(['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 = videoFormat(\n flags.format === undefined ? undefined : String(flags.format),\n positionals[1],\n )\n\n const loaded = await loadVosConfig(source)\n for (const w of loaded.warnings) r.log(`note: ${w}`)\n void flags.set\n const config = applyDataSets(\n loaded.config as Record<string, unknown>,\n setFlags(argv),\n )\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 // A program document's sound rides its studio entry; mix it like a take's.\n const audio = await programAudio(config as Record<string, unknown>, {\n baseDir:\n existsSync(source) && statSync(source).isDirectory()\n ? source\n : dirname(source),\n duration,\n origin: platformOrigin({}),\n key: resolveCredential(),\n log: (line) => r.log(line),\n })\n if (audio) r.log(`mixing ${audio.clips} sound${audio.clips === 1 ? '' : 's'}`)\n if (hasAudioElements(config)) {\n r.log(\n 'note: an audio ELEMENT is not mixed by a render; put the sound on the program document instead (doc.json audio: [{ key, start, in, out, duration, gain }])',\n )\n }\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 audioProducerCode: audio?.producerCode,\n onPhase: (phase) => {\n r.log(`${phase}…`)\n r.event({ event: 'phase', phase })\n },\n })\n await writeFile(out, result.bytes)\n const warnings = stillWarnings(\n { transparent: false, flat: false },\n result.pageErrors,\n )\n for (const w of warnings) r.log(`warn: ${w}`)\n r.done(\n {\n out,\n bytes: result.bytes.length,\n width,\n height,\n fps,\n duration,\n format,\n audio: audio?.clips ?? 0,\n ...(warnings.length ? { warnings } : {}),\n },\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)\n throw new UsageError(\n 'vos still <config.json|url> [out.webp|out.png|out.jpg] [--time t | --times a,b,50%]',\n )\n const r = createReporter(flags.json === true)\n\n const loaded = await loadVosConfig(source)\n for (const w of loaded.warnings) r.log(`note: ${w}`)\n void flags.set\n const config = applyDataSets(\n loaded.config as Record<string, unknown>,\n setFlags(argv),\n )\n const width = numFlag(flags, 'width', 1280)\n const height = numFlag(flags, 'height', 720)\n const out = positionals[1] ?? outName(source, 'webp')\n const encoding = stillFormat(out)\n const timesRaw = flags.times === undefined ? undefined : String(flags.times)\n if (timesRaw !== undefined && flags.time !== undefined)\n throw new UsageError('--time and --times: pass one')\n const times =\n timesRaw !== undefined\n ? parseTimes(timesRaw, configDuration(config) ?? 5)\n : [numFlag(flags, 'time', 0)]\n const many = times.length > 1\n\n // One browser for every time: each still is its own capture page.\n const browser = await launchBrowser()\n try {\n const written: {\n out: string\n bytes: number\n time: number\n warnings?: string[]\n }[] = []\n for (const time of times) {\n const target = stillOutFor(out, time, many)\n const result = await renderStill(browser, {\n config,\n width,\n height,\n time,\n onPhase: (phase) => {\n r.event({ event: 'phase', phase, time })\n },\n })\n // The engine captures WebP; a .png or .jpg name is re-encoded in the\n // page, so the bytes always match the name.\n const bytes =\n encoding === 'webp'\n ? result.bytes\n : await reencodeStill(browser, result.bytes, encoding)\n await writeFile(target, bytes)\n // A still is checked before anyone looks at it: a fully transparent\n // or flat frame is written, and said, never passed off as a picture.\n const warnings = stillWarnings(\n await inspectStill(browser, result.bytes),\n result.pageErrors,\n )\n written.push({\n out: target,\n bytes: bytes.length,\n time,\n ...(warnings.length ? { warnings } : {}),\n })\n r.log(\n `Wrote ${target} (${(bytes.length / 1024).toFixed(0)} KB @ t=${Number(time.toFixed(2))}s)`,\n )\n for (const w of warnings)\n r.log(`warn: t=${Number(time.toFixed(2))}s ${w}`)\n }\n const first = written[0]\n r.done(\n many\n ? { stills: written, width, height, format: encoding }\n : { ...first, width, height, format: encoding },\n many\n ? `Wrote ${written.length} stills (${width}x${height})`\n : `Wrote ${first.out} (${(first.bytes / 1024).toFixed(0)} KB, ${width}x${height} @ t=${first.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 =\n typeof config.data === 'object' && config.data !== null\n ? Object.keys(config.data)\n : []\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:\n (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(\n readFileSync(join(dir, 'package.json'), 'utf8'),\n ) 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\nfunction ownVersion(): string {\n try {\n const own = JSON.parse(\n readFileSync(\n fileURLToPath(new URL('../package.json', import.meta.url)),\n 'utf8',\n ),\n ) as { version: string }\n return own.version\n } catch {\n return '(unknown)'\n }\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 versions['@vosjs/cli'] = ownVersion()\n for (const name of [\n '@vosjs/core',\n '@vosjs/elements',\n '@vosjs/tween',\n '@vosjs/editor',\n '@vosjs/timeline',\n '@vosjs/studio-core',\n '@vosjs/render-core',\n '@vosjs/shared',\n 'mediabunny',\n 'playwright',\n ]) {\n versions[name] = packageVersion(name) ?? '(not found)'\n }\n if (r.json) r.done({ versions }, '')\n else\n for (const [k, v] of Object.entries(versions))\n 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 cmdCheck(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos check <config.json>')\n const r = createReporter(flags.json === true)\n\n // A directory is a take (refused here: its document is `vos validate`'s\n // job) or a program directory (config.json, composed with the program\n // document beside it so the ladder reads the layers' strings too).\n const kind = /^https?:\\/\\//.test(source) ? 'none' : directoryKind(source)\n if (kind === 'take') {\n throw new UsageError(\n `${source} is a take (its doc.json is a recording document). vos validate ${source} lints it; vos render ${source} renders it.`,\n )\n }\n let raw: string\n let parsed: unknown\n try {\n if (kind === 'program') {\n const notes: string[] = []\n parsed = await loadProgramDirectory(source, notes)\n for (const n of notes) r.log(`note: ${n}`)\n raw = ''\n } else {\n raw = await readSourceText(source)\n parsed = JSON.parse(raw)\n }\n } catch (e) {\n if (!(e instanceof SyntaxError)) throw e\n parsed = undefined\n if (!r.json) process.stdout.write(`error [json] ${(e as Error).message}\\n`)\n r.done(\n {\n ok: false,\n errors: 1,\n warnings: 0,\n issues: [\n { level: 'error', source: 'json', message: (e as Error).message },\n ],\n },\n `${source}: 1 error`,\n )\n return EXIT_ERROR\n }\n\n const result = runCheck(parsed)\n if (r.json) {\n r.done(\n {\n ok: result.ok,\n errors: result.errors,\n warnings: result.warnings,\n issues: result.issues,\n },\n '',\n )\n } else {\n for (const i of result.issues) {\n process.stdout.write(`${i.level} [${i.source}] ${i.message}\\n`)\n }\n process.stdout.write(\n result.ok\n ? `${source}: ok (${result.warnings} warning${result.warnings === 1 ? '' : 's'})\\n`\n : `${source}: ${result.errors} error${result.errors === 1 ? '' : 's'}, ${result.warnings} warning${result.warnings === 1 ? '' : 's'}\\n`,\n )\n }\n return result.ok ? EXIT_OK : EXIT_ERROR\n}\n\n// ---------------------------------------------------------------------------\n// Every verb this file does not own is a take-pipeline or vos.so verb from\n// `./plugin` (what used to ship separately as @vosso/vos-plugin). It loads on\n// demand, so the engine verbs never pay for the recorder's imports, and the\n// old names (`@vosso/cli`, `@vosso/voila-cli`, the `vos voila` alias) keep\n// resolving here for scripts that still use them.\n// ---------------------------------------------------------------------------\n\nasync function delegate(argv: string[], viaAlias = false): Promise<number> {\n const { run } = await import('./plugin/run')\n if (viaAlias && argv[0]) {\n process.stderr.write(\n `note: \"vos voila ${argv[0]}\" is now \"vos ${argv[0]}\".\\n`,\n )\n }\n return await run(argv)\n}\n\nasync function printHelp(): Promise<void> {\n process.stdout.write(HELP_ENGINE)\n const { manifest } = await import('./plugin/manifest')\n process.stdout.write('\\nTake pipeline + vos.so verbs\\n')\n for (const v of manifest.verbs) {\n if (v.name === 'render') continue // polymorphic — already listed above\n process.stdout.write(` vos ${v.name.padEnd(8)} ${v.summary}\\n`)\n }\n process.stdout.write(HELP_CONVENTIONS)\n}\n\nconst ENGINE_VERBS = new Set([\n 'render',\n 'still',\n 'info',\n 'versions',\n 'preview',\n 'check',\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 await printHelp()\n return cmd ? EXIT_OK : EXIT_USAGE\n }\n if (cmd === '--version') return cmdVersions(['--json'])\n if (!ENGINE_VERBS.has(cmd) && cmd !== 'voila') return delegate([cmd, ...rest])\n // An engine verb asked for its flags: the engine's usage lines, plus the\n // take pipeline's where the verb is shared (render takes a config OR a take).\n if (rest.includes('--help') || rest.includes('-h')) {\n const engine = HELP_ENGINE.split('\\n').filter((l) =>\n l.startsWith(` vos ${cmd} `),\n )\n const { verbHelp } = await import('./plugin/run')\n const take = verbHelp(cmd)\n process.stdout.write(\n `${engine.join('\\n')}\\n${take.includes('no such verb') ? '' : take}`,\n )\n return EXIT_OK\n }\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 'check':\n return cmdCheck(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 delegate(rest, true)\n default:\n throw new UsageError(`unknown command \"${cmd}\" — run vos help`)\n }\n}\n\nmain()\n .then(async (code) => {\n // A run that succeeded while ignoring a flag it was given did not do\n // what was asked (flagUse.ts): say so, and do not exit 0.\n if (code === EXIT_OK) {\n const verb = process.argv[2] ?? ''\n const { HELP } = await import('./plugin/run')\n const documented = [\n ...helpFlagsFor(HELP_ENGINE, verb),\n ...helpFlagsFor(HELP, verb),\n ]\n const message = unusedFlagsMessage(verb, [...new Set(documented)])\n if (message) {\n process.stderr.write(`usage error: ${message}\\n`)\n process.exit(EXIT_USAGE)\n }\n }\n process.exit(code)\n })\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(\n `error: ${e instanceof Error ? e.message : String(e)}\\n`,\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 * 4 the recorder met a sign-in (the take pipeline's wall check)\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)\n process.stdout.write(`${JSON.stringify({ event: 'done', ...obj })}\\n`)\n else process.stdout.write(`${humanLine}\\n`)\n },\n }\n}\n","/**\n * `vos check` — the local validation pipeline, pure so it can be unit-tested:\n * envelope unwrap → migrate → schema → compile → determinism + dialect lints.\n * Everything runs locally (no network, no browser). The platform runs the\n * same compiler server-side, so a clean check is a push that will compile.\n */\nimport {\n CURRENT_CONFIG_VERSION,\n compileVosConfig,\n migrateConfig,\n vosConfigJsonSchema,\n} from '@vosjs/core'\nimport {\n lintVosConfig,\n lintVosDialect,\n lintVosFonts,\n lintVosPostprocessing,\n} from '@vosjs/core/lint'\nimport { knobWarnings } from './plugin/knobs'\n\nexport interface CheckIssue {\n level: 'error' | 'warn'\n source:\n | 'schema'\n | 'syntax'\n | 'compile'\n | 'determinism'\n | 'dialect'\n | 'shape'\n | 'fonts'\n | 'postprocessing'\n message: string\n}\n\nexport interface CheckResult {\n ok: boolean\n errors: number\n warnings: number\n issues: CheckIssue[]\n /** The migrated config (params/presets untouched) — what a push should send. */\n config: Record<string, unknown> | null\n}\n\n// Top-level keys the schema does not know but the platform preserves on push.\nconst PLATFORM_EXTRA_KEYS = new Set(['params', 'presets'])\n\nexport function runCheck(parsed: unknown): CheckResult {\n const issues: CheckIssue[] = []\n const finish = (config: Record<string, unknown> | null): CheckResult => {\n const errors = issues.filter((i) => i.level === 'error').length\n return {\n ok: errors === 0,\n errors,\n warnings: issues.length - errors,\n issues,\n config,\n }\n }\n\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n issues.push({\n level: 'error',\n source: 'shape',\n message: 'not a JSON object',\n })\n return finish(null)\n }\n\n // API endpoints wrap the config: { config: {...} } — accept both shapes.\n let obj = parsed as Record<string, unknown>\n if (\n typeof obj.config === 'object' &&\n obj.config !== null &&\n !('createTimeline' in obj)\n ) {\n obj = obj.config as Record<string, unknown>\n }\n\n const version = obj.version\n let migrated: Record<string, unknown>\n try {\n migrated = migrateConfig(obj) as Record<string, unknown>\n } catch (e) {\n issues.push({\n level: 'error',\n source: 'shape',\n message: `migration failed: ${e instanceof Error ? e.message : String(e)}`,\n })\n return finish(null)\n }\n if (version === undefined) {\n // It compiles (the engine reads an absent version as the current one),\n // but a push is refused: a stored config must say which schema era it was\n // written against, and nobody can tell that from the file later.\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `config declares no \"version\". It plays here, but a push is refused without one. Add \"version\": ${CURRENT_CONFIG_VERSION}.`,\n })\n } else if (version !== CURRENT_CONFIG_VERSION) {\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `config is v${String(version)}, migrated to v${CURRENT_CONFIG_VERSION}. Save the migrated form.`,\n })\n }\n\n const schema = vosConfigJsonSchema.safeParse(migrated)\n if (!schema.success) {\n for (const i of schema.error.issues.slice(0, 10)) {\n issues.push({\n level: 'error',\n source: 'schema',\n message: `${i.path.join('.') || '(root)'}: ${i.message}`,\n })\n }\n return finish(null)\n }\n\n // Keys the platform's schema will silently drop on push (params/presets\n // excepted — those are re-attached by contract).\n const known = new Set([\n ...Object.keys(vosConfigJsonSchema.shape),\n ...PLATFORM_EXTRA_KEYS,\n ])\n for (const key of Object.keys(migrated)) {\n if (!known.has(key)) {\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `unknown top-level key \"${key}\" — the platform drops it on push`,\n })\n }\n }\n\n // Knobs and Looks vos.so would drop (the push still saves, without them).\n for (const message of knobWarnings(migrated as Record<string, unknown>)) {\n issues.push({ level: 'warn', source: 'shape', message })\n }\n\n // Sound as an ELEMENT is not a track: no timeline row, no gain or fades,\n // nothing a render mixes, and a `data:` source inflates the config past\n // what a claimable push can carry. The document is where sound goes.\n const elements = Array.isArray(migrated.elements) ? migrated.elements : []\n for (const el of elements) {\n if (!el || typeof el !== 'object') continue\n const e = el as { type?: unknown; id?: unknown; src?: unknown }\n if (e.type !== 'audio') continue\n const inline =\n typeof e.src === 'string' && e.src.startsWith('data:')\n ? ` (embedded, ${Math.round(e.src.length / 1024)} KB of config)`\n : ''\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `audio element \"${String(e.id ?? '?')}\"${inline} is not a track: renders do not mix it and the studio cannot edit it. Put the file beside the config and name it in doc.json (audio: [{ key: \"score.ogg\", start, in, out, duration, gain }]); vos push uploads it`,\n })\n }\n\n // Function strings are pasted into the template as text, so the compiler\n // cannot see a syntax error — parse each one here (construction only,\n // nothing executes).\n let syntaxErrors = 0\n for (const key of [\n 'setup',\n 'createContent',\n 'createTimeline',\n 'onFrame',\n ] as const) {\n const src = migrated[key]\n if (typeof src !== 'string' || !src.length) continue\n try {\n new Function(`\"use strict\"; return (${src}\\n)`)\n } catch (e) {\n syntaxErrors++\n issues.push({\n level: 'error',\n source: 'syntax',\n message: `${key}: ${e instanceof Error ? e.message : String(e)}`,\n })\n }\n }\n if (syntaxErrors) return finish(migrated)\n\n try {\n // Same options as `vos render` — a clean check is a config the CLI's own\n // render path will accept.\n compileVosConfig(migrated as never, { tweenEngine: 'vos' })\n } catch (e) {\n issues.push({\n level: 'error',\n source: 'compile',\n message: e instanceof Error ? e.message : String(e),\n })\n return finish(migrated)\n }\n\n for (const i of lintVosConfig(migrated as never)) {\n issues.push({\n level: i.severity === 'error' ? 'error' : 'warn',\n source: 'determinism',\n message: `${i.fn}:${i.line} [${i.rule}] ${i.message}`,\n })\n }\n for (const i of lintVosDialect(migrated as never)) {\n issues.push({\n level: i.severity === 'error' ? 'error' : 'warn',\n source: 'dialect',\n message: `${i.fn}:${i.line} [${i.rule}] ${i.message}`,\n })\n }\n for (const i of lintVosFonts(migrated as never)) {\n issues.push({\n level: 'warn',\n source: 'fonts',\n message: `[${i.rule}] ${i.message}`,\n })\n }\n for (const i of lintVosPostprocessing(migrated as never)) {\n issues.push({\n level: 'warn',\n source: 'postprocessing',\n message: `[${i.rule}] ${i.message}`,\n })\n }\n\n return finish(migrated)\n}\n","/**\n * What vos.so keeps of a config's knobs (`params`) and Looks (`presets`).\n *\n * A vos.so fact, so it lives in the platform client: the platform keeps at\n * most 12 knobs and 8 Looks, each within its field limits, and drops the\n * rest ONE BY ONE with a warning (it used to drop them all, silently). This\n * mirrors that rule so `vos check` says the same thing before a push does.\n */\nexport const MAX_PARAMS = 12\nexport const MAX_LOOKS = 8\n\nconst KINDS = new Set(['number', 'color', 'select', 'toggle', 'text', 'font'])\n\nconst str = (v: unknown, max: number, min = 0) =>\n typeof v === 'string' && v.length >= min && v.length <= max\n\n/** Why one knob would be dropped, or null when vos.so keeps it. */\nexport function knobProblem(p: unknown): string | null {\n if (!p || typeof p !== 'object') return 'not an object'\n const k = p as Record<string, unknown>\n if (!str(k.key, 40, 1)) return 'key must be 1 to 40 characters'\n if (typeof k.kind !== 'string' || !KINDS.has(k.kind))\n return `kind must be one of ${[...KINDS].join(', ')}`\n if (k.label !== undefined && !str(k.label, 60))\n return 'label: at most 60 characters'\n if (k.hint !== undefined && !str(k.hint, 140))\n return 'hint: at most 140 characters'\n if (k.unit !== undefined && !str(k.unit, 8))\n return 'unit: at most 8 characters'\n if (k.group !== undefined && !str(k.group, 24))\n return 'group: at most 24 characters'\n if (k.options !== undefined) {\n if (!Array.isArray(k.options) || k.options.length > 12)\n return 'options: at most 12'\n if (k.options.some((o) => !str(o, 40, 1)))\n return 'options: each 1 to 40 characters'\n }\n const d = k.default\n if (\n !(\n typeof d === 'number' ||\n typeof d === 'boolean' ||\n (typeof d === 'string' && d.length <= 280)\n )\n )\n return 'default must be a number, a boolean or at most 280 characters'\n return null\n}\n\n/** Why one Look would be dropped, or null when vos.so keeps it. */\nexport function lookProblem(l: unknown): string | null {\n if (!l || typeof l !== 'object') return 'not an object'\n const look = l as Record<string, unknown>\n if (!str(look.name, 24, 1)) return 'name must be 1 to 24 characters'\n if (!look.values || typeof look.values !== 'object') return 'values missing'\n return null\n}\n\n/** Every knob and Look vos.so would drop, in words, in order. */\nexport function knobWarnings(config: Record<string, unknown>): string[] {\n const out: string[] = []\n const sort = (\n raw: unknown,\n noun: 'knob' | 'look',\n field: string,\n max: number,\n problem: (e: unknown) => string | null,\n ) => {\n if (raw === undefined) return\n if (!Array.isArray(raw)) {\n out.push(`${noun === 'knob' ? 'params' : 'presets'} is not a list`)\n return\n }\n let kept = 0\n raw.forEach((entry, i) => {\n const id =\n entry && typeof (entry as Record<string, unknown>)[field] === 'string'\n ? `${noun} \"${String((entry as Record<string, unknown>)[field])}\"`\n : `${noun} #${i + 1}`\n const why = problem(entry)\n if (why) out.push(`${id} would be dropped on vos.so (${why})`)\n else if (kept >= max)\n out.push(\n `${id} would be dropped on vos.so: a vos keeps at most ${max} ${noun}s${noun === 'knob' ? ' (the rest can stay bound data)' : ''}`,\n )\n else kept += 1\n })\n }\n sort(config.params, 'knob', 'key', MAX_PARAMS, knobProblem)\n sort(config.presets, 'look', 'name', MAX_LOOKS, lookProblem)\n return out\n}\n","/**\n * The sound a program document adds, for a LOCAL render of the program.\n *\n * A composed program carries its `doc.audio` clips on the studio stack entry,\n * exactly as a take does, and the capture page mixes them through the same\n * producer a take render uses. The one difference is WHERE the bytes are: a\n * capture page has no origin of its own, so a clip keyed by a file beside the\n * document (`score.ogg`) or by a hosted asset path (`/api/assets/…`) cannot be\n * fetched from inside it. Each is inlined as a `data:` URI here: a local file\n * read from disk, a hosted asset downloaded with the caller's credential.\n * A clip that cannot be reached is left out and said, never a silent track.\n */\nimport { existsSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { audioProducerCode, studioEntryData } from '@vosjs/render-core'\nimport { studioAudioPlan } from '@vosjs/studio-core'\nimport { MEDIA_HEAD_BYTES, resolveMediaType } from './plugin/container'\nimport type { EnvelopePoint, LoweredAudioClip } from '@vosjs/studio-core'\n\nexport interface ProgramAudioOptions {\n /** The directory the document's relative keys are read against. */\n baseDir: string\n /** The output length the mix is cut to, seconds. */\n duration: number\n /** For a clip keyed by a hosted asset: where it lives and who may read it. */\n origin?: string\n key?: string | null\n log: (line: string) => void\n}\n\nexport interface ProgramAudio {\n producerCode: string\n clips: number\n}\n\nfunction dataUri(bytes: Uint8Array, filename: string): string {\n const { type } = resolveMediaType({\n head: bytes.subarray(0, MEDIA_HEAD_BYTES),\n filename,\n })\n return `data:${type};base64,${Buffer.from(bytes).toString('base64')}`\n}\n\nasync function reach(\n clipKey: string,\n opts: ProgramAudioOptions,\n): Promise<string | null> {\n if (/^(data:|https?:)/.test(clipKey)) return clipKey\n if (clipKey.startsWith('/api/')) {\n if (!opts.origin || !opts.key) {\n opts.log(\n `note: sound ${clipKey} is a hosted asset and no credential is set, so the render leaves it out (vos login, or keep the file beside the document)`,\n )\n return null\n }\n const res = await fetch(`${opts.origin}${clipKey}`, {\n headers: { authorization: `Bearer ${opts.key}` },\n })\n if (!res.ok) {\n opts.log(\n `note: sound ${clipKey} could not be read (${res.status}), so the render leaves it out`,\n )\n return null\n }\n return dataUri(new Uint8Array(await res.arrayBuffer()), clipKey)\n }\n const file = join(opts.baseDir, clipKey.replace(/^\\/+/, ''))\n if (!existsSync(file)) {\n opts.log(\n `note: sound ${clipKey} is not beside the document, so the render leaves it out`,\n )\n return null\n }\n return dataUri(new Uint8Array(await readFile(file)), file)\n}\n\n/** The producer for a composed program's sound, or null when it adds none. */\nexport async function programAudio(\n config: Record<string, unknown>,\n opts: ProgramAudioOptions,\n): Promise<ProgramAudio | null> {\n const entry = studioEntryData(config.stack)\n const clips = Array.isArray(entry?.audio)\n ? (entry.audio as LoweredAudioClip[])\n : []\n if (!clips.length) return null\n const reached: LoweredAudioClip[] = []\n for (const clip of clips) {\n const key = await reach(clip.key, opts)\n if (key) reached.push({ ...clip, key })\n }\n if (!reached.length) return null\n const duckEnv = Array.isArray(entry?.duckEnv)\n ? (entry.duckEnv as EnvelopePoint[])\n : []\n const plan = studioAudioPlan(reached, duckEnv, opts.duration)\n return {\n producerCode: audioProducerCode({ plan }),\n clips: reached.length,\n }\n}\n"],"mappings":"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","names":["existsSync","join","existsSync","require","join"]}
package/dist/index.d.ts CHANGED
@@ -27,6 +27,9 @@ interface RenderStillOptions extends RenderCommonOptions {
27
27
  interface RenderResult {
28
28
  bytes: Uint8Array;
29
29
  mimeType: string;
30
+ /** Uncaught exceptions the page threw while it rendered (it may still have
31
+ * written a frame, so a caller says them instead of hiding them). */
32
+ pageErrors: string[];
30
33
  }
31
34
  /** Render a vos config to a video (WebM/MP4) in a headless browser. */
32
35
  declare function renderVideo(browser: Browser, opts: RenderVideoOptions): Promise<RenderResult>;
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  previewPages,
4
4
  renderStill,
5
5
  renderVideo
6
- } from "./chunk-3OIYR5VL.js";
6
+ } from "./chunk-ZCLGXETO.js";
7
7
  import {
8
8
  RECORDING_NAME,
9
9
  convertAgentBrowser,
@@ -23,7 +23,7 @@ import {
23
23
  takePaths,
24
24
  validateActions,
25
25
  waitForPageDone
26
- } from "./chunk-ZARSNMVY.js";
26
+ } from "./chunk-MJYXP4AA.js";
27
27
  import {
28
28
  BrowserUnavailableError,
29
29
  apiError,
@@ -36,7 +36,7 @@ import {
36
36
  readSyncState,
37
37
  resolveCredential,
38
38
  writeSyncState
39
- } from "./chunk-PJX3XE7G.js";
39
+ } from "./chunk-WLUWDQQO.js";
40
40
  import "./chunk-6XPFWQKP.js";
41
41
  import {
42
42
  manifest
@@ -6,8 +6,8 @@ import {
6
6
  takeBackdrop,
7
7
  takeBrowserArgs,
8
8
  verbHelp
9
- } from "./chunk-ZARSNMVY.js";
10
- import "./chunk-PJX3XE7G.js";
9
+ } from "./chunk-MJYXP4AA.js";
10
+ import "./chunk-WLUWDQQO.js";
11
11
  import "./chunk-6XPFWQKP.js";
12
12
  export {
13
13
  HELP,
@@ -17,4 +17,4 @@ export {
17
17
  takeBrowserArgs,
18
18
  verbHelp
19
19
  };
20
- //# sourceMappingURL=run-W5UTMIFU.js.map
20
+ //# sourceMappingURL=run-DYNCGXWO.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vosjs/cli",
3
- "version": "0.53.4",
3
+ "version": "0.53.6",
4
4
  "description": "The vos CLI: record the real product from a scripted browser flow, auto-zoom from the cursor track, cut as data in doc.json, render deterministic video and stills, deliver a release's media per destination spec, and sync with vos.so. One binary, every verb, MIT.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -49,14 +49,14 @@
49
49
  "dependencies": {
50
50
  "mediabunny": "^1.55.7",
51
51
  "playwright": "^1.49.0",
52
- "@vosjs/core": "^0.25.3",
53
52
  "@vosjs/editor": "^1.4.0",
54
53
  "@vosjs/elements": "^0.9.0",
54
+ "@vosjs/core": "^0.25.3",
55
55
  "@vosjs/render-core": "^0.3.9",
56
- "@vosjs/shared": "^0.5.0",
57
- "@vosjs/studio-core": "^0.33.1",
58
- "@vosjs/timeline": "^0.4.1",
59
- "@vosjs/tween": "^0.8.2"
56
+ "@vosjs/studio-core": "^0.33.2",
57
+ "@vosjs/shared": "^0.6.0",
58
+ "@vosjs/tween": "^0.8.2",
59
+ "@vosjs/timeline": "^0.4.1"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/node": "^22",
package/skills/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.1
1
+ 0.12.0
@@ -43,10 +43,15 @@ not in `node_modules`.
43
43
  `font.family` and `font.color` take `{ "$data": "<key>" }` (they
44
44
  re-raster live on a data edit). Logos and shapes are `svg` or `image`
45
45
  elements, footage a `video` element. Elements are what the studio selects
46
- and moves.
46
+ and moves. **Every visible word is a bound element**, whatever animates
47
+ it: a word whose letters rise one by one is ONE `split` element bound to
48
+ its key, never a pool of per-letter elements filled from `onFrame` (a
49
+ person can retype the first in the studio and not the second).
47
50
  4. **Motion is the timeline.** `createTimeline` holds the tweens, on
48
51
  `ctx.elements.get(id).props` (whole) or `.segments` (`split: { type:
49
- 'chars' | 'words' | 'lines' }` units), in the GSAP dialect. Keep curves
52
+ 'chars' | 'words' | 'lines' }` units), in the GSAP dialect. Read
53
+ `el.segments` inside `createTimeline` and nowhere else: a data edit to a
54
+ bound split word rebuilds its units and the timeline over them. Keep curves
50
55
  exact: Remotion's `Easing.bezier(a, b, c, d)` is `ease: 'css-bezier(a, b,
51
56
  c, d)'`; GSAP eases keep their names.
52
57
  5. **Scenes are labels.** One `tl.addLabel('<scene>', t)` per scene or
@@ -185,7 +190,7 @@ stated approximation:
185
190
 
186
191
  | Source | In vos today |
187
192
  | --- | --- |
188
- | `overflow: hidden` masks, `clip-path` reveals | no element masks: approximate with opacity, or paint the item |
193
+ | `overflow: hidden` masks, `clip-path` reveals | no element masks: approximate a masked WORD with opacity (it stays a bound element); paint only a shape |
189
194
  | a colour per split unit (`charStyle` making one letter red) | segments carry x/y/opacity/scale/rotation only: a separate element, or paint it |
190
195
  | `mixBlendMode` | no blend modes on elements: paint it |
191
196
  | a shape's colour as a knob | an svg's colours are compiled in (static `colors`): paint the shape if its colour must change live |
@@ -1,4 +1,4 @@
1
- <!-- Generated by scripts/build-element-reference.mjs from @vosjs/core 0.25.2, @vosjs/elements 0.8.3 and @vosjs/timeline 0.4.1. Do not edit by hand: re-run the script. -->
1
+ <!-- Generated by scripts/build-element-reference.mjs from @vosjs/core 0.25.3, @vosjs/elements 0.9.0, @vosjs/timeline 0.4.1 and @vosjs/shared 0.5.0. Do not edit by hand: re-run the script. -->
2
2
 
3
3
  # Elements, the context and eases: the declarations
4
4
 
@@ -117,12 +117,47 @@ interface VosConfigJson {
117
117
 
118
118
  `content`, `font.family` and `font.color` of a text element are the three
119
119
  fields that take a `{ "$data": "<key>" }` binding, and the only ones that
120
- re-resolve on a data edit; every other field is read once at build.
120
+ re-resolve on a data edit; every other field is read once at build. The key
121
+ names a TOP-LEVEL key of `config.data` (no dotted path, no array index), so
122
+ a list of words that each need binding is one key per word; `content`
123
+ takes the value as a string. A bound `split` element follows a data edit
124
+ too: its units are rebuilt from the new words and the timeline is rebuilt
125
+ over them (`@vosjs/elements` 0.9.0 with core 0.25.3; before that the
126
+ studio reloaded the program for such a knob and other hosts kept the old
127
+ word). So a per-letter word that is a knob is ONE bound `split` element,
128
+ never a pool of per-letter elements written from `onFrame`: a person can
129
+ retype a bound element in the studio, and cannot retype a pool.
121
130
  `font.size`, `letterSpacing`, `transform.translateX/Y`, `stroke.width`
122
131
  and `shadow.blur` are design pixels (a 1080-high frame). There is no
123
132
  `mask`, `clip`, `blend` or group field on any element: SKILL.md's
124
133
  "honest gaps" says what to do instead.
125
134
 
135
+ **Where an element sits.** `position` places the element's BOX:
136
+
137
+ - a preset (`'center'`, `'top-left'`, … `'bottom-right'`) sets the box
138
+ flush against the frame's edges or centre, with no margin;
139
+ - `{ x: '10%', y: '20%' }` (strings) puts the box's TOP-LEFT corner at
140
+ that fraction of the frame, the same at every output size;
141
+ - `{ x: 120, y: 80 }` (numbers) is RENDER pixels of that corner, never
142
+ scaled, so it moves when the output size changes: use a percentage or a
143
+ preset plus `transform` instead.
144
+
145
+ Then `transform.translateX` / `translateY` (design px, y down) move it.
146
+ `transform.scale`, `translateZ` and `rotation` / `rotateZ` (degrees,
147
+ about the box's centre) apply to a whole element, and a `split` element
148
+ takes only the translate. `anchor`, `transform.origin`, `scaleX`,
149
+ `scaleY`, `rotateX`, `rotateY` and `perspective` are not read: the
150
+ origin is always the box's centre. A config `rotation` is lost the first
151
+ time a tween writes `props.scale*` or `props.rotation*` (the props start at
152
+ rotation 0), so an element that is both tilted and animated takes its tilt
153
+ as `props.rotation` in the timeline (a `tl.set` at 0), never in the config.
154
+
155
+ A `stroke` is drawn UNDER the fill and centred on the outline, so around a
156
+ filled word only its outer half shows. CSS `-webkit-text-stroke` paints
157
+ over the fill, so a CSS stroke of N px is `stroke.width: 2N` around a
158
+ filled word and `stroke.width: N` for an outline-only word
159
+ (`font.color: 'transparent'`).
160
+
126
161
  ```ts
127
162
  /**
128
163
  * `{$data: key}` binding — the value resolves from the host's data object at
@@ -310,6 +345,14 @@ What `createTimeline` and `onFrame` animate: `el.props` (and
310
345
  from the frame centre with `y` down, and `rotation` is degrees
311
346
  counter-clockwise, unlike the config's design-pixel `transform`.
312
347
 
348
+ A `split` element lays its units out as one block at the element's
349
+ position: `el.segments[i]` starts at the CENTRE of unit i's ink, which
350
+ sits `lineStart + advanceBefore + width / 2` from the block's centre
351
+ (`advanceBefore` measured with `letterSpacing` and without kerning across
352
+ unit boundaries, the line aligned by `font.align`). So a per-letter tween
353
+ moves each letter FROM its laid-out place (`from`, or `s.y + dy`), never
354
+ to a position computed by hand.
355
+
313
356
  Writing any of `content`, `fontSize`, `fontFamily`, `fontWeight`, `fontStyle`, `letterSpacing`, `color`, `strokeColor`, `strokeWidth` on a text element's `props`
314
357
  re-rasters it (the runtime's own list), so a scramble or a counter writes
315
358
  `el.props.content` in `onFrame` and a colour change sets `el.props.color`.
@@ -318,9 +361,21 @@ The tween dialect animates numbers only: tween `fontSize` and
318
361
 
319
362
  Every such write queues a re-raster, even of an unchanged value, so guard
320
363
  a per-frame write: `if (el.props.content !== next) el.props.content = next`.
321
- A `split` text element does not re-raster at all: these writes are ignored
322
- on it, so text that changes and text that animates per letter are two
323
- elements.
364
+ These writes do nothing on a `split` element (its units are structure):
365
+ change a split element's words through its binding, never its props.
366
+
367
+ When `onFrame` writes a bound element's text, `onFrame` wins: the write
368
+ becomes the element's content, a data edit re-applies the bound value, and
369
+ the next frame's write replaces it before anything draws (the frame flushes
370
+ once, after `onFrame`). So derive the written string from `ctx.data`
371
+ (a scramble resolves to `ctx.data.subtitle`), and the binding stays the
372
+ words a person edits.
373
+
374
+ A text raster never fails for being long: its density drops until the
375
+ longest side fits the GPU's largest texture (4096 px when the GPU does
376
+ not say), so a line past about 4096 design px at 1080p, or half that at
377
+ 2160p, renders softer. Only then is a long marquee worth cutting into
378
+ elements, and each piece stays bound.
324
379
 
325
380
  ```ts
326
381
  /**
@@ -381,9 +436,14 @@ interface ElementInstance {
381
436
  props: ElementProps;
382
437
  /**
383
438
  * Split text segments (only available when split config is defined).
384
- * Each segment has its own ElementProps for individual animation.
439
+ * Each segment has its own ElementProps for individual animation. A data
440
+ * edit to a BOUND split text rebuilds them from the new words: the array
441
+ * and its objects are replaced (and the host rebuilds the timeline), so
442
+ * read `el.segments` inside createTimeline, never cache it elsewhere.
385
443
  */
386
444
  segments?: ElementProps[];
445
+ /** Set when a data edit rebuilt this element's units; the host clears it. */
446
+ structural?: boolean;
387
447
  /** Update element content (text or image src) */
388
448
  setContent: (content: string) => void;
389
449
  /**
@@ -409,6 +469,83 @@ interface ElementInstance {
409
469
  }
410
470
  ```
411
471
 
472
+ ## Knobs (`config.params` and `config.presets`)
473
+
474
+ A knob is a `ParamSpec` over one top-level `data` key, and a Look a named
475
+ set of values. vos.so keeps at most 12 knobs and 8 Looks, the first valid
476
+ ones, and `vos check` names every one it would drop before a push. Every
477
+ bound word stays a `data` key whether or not it has a knob: the studio's
478
+ element panel retypes a bound element directly, so the 12 knobs go to what
479
+ a person reaches for first (the palette, the headline), not to every word.
480
+ A `text` knob with `multiline: true` suits a list a painter draws line by
481
+ line; a list of elements is one key per element.
482
+
483
+ ```ts
484
+ /**
485
+ * Config params: `config.params`
486
+ * declares the `ctx.data` keys a program reads as its creative knobs — key,
487
+ * kind, range, default. THE one params module, shared by the web Remix
488
+ * panel, the API, and scripts (the knob honesty lint): edits commit by
489
+ * baking BOTH `params[i].default` and `data[key]` into the config, so
490
+ * saves/exports/server renders pick the values up through the existing
491
+ * `config.data` machinery with zero new plumbing.
492
+ *
493
+ * The engine schema does not know `params` yet (upstream addition pending) —
494
+ * `vosConfigJsonSchema` STRIPS unknown fields on parse, so the API re-attaches
495
+ * validated params at the storage boundary (the platform's server-side copy;
496
+ * change both together) and
497
+ * this module validates defensively.
498
+ */
499
+ type ParamValue = number | string | boolean;
500
+
501
+ interface ParamSpec {
502
+ /** The ctx.data key the program reads. */
503
+ key: string;
504
+ /** Human label; falls back to the key. */
505
+ label?: string;
506
+ /** One sentence on what the knob changes (U3b — knobs carry meaning). */
507
+ hint?: string;
508
+ /** Unit shown inside the number field: px, %, s, ×, °. */
509
+ unit?: string;
510
+ /** Optional card grouping; ungrouped knobs land on the Remix card. */
511
+ group?: string;
512
+ /** Sort order within a group (falls back to declaration order). */
513
+ order?: number;
514
+ kind: 'number' | 'color' | 'select' | 'toggle' | 'text' | 'font';
515
+ /** number kind */
516
+ min?: number;
517
+ max?: number;
518
+ step?: number;
519
+ /**
520
+ * select kind: the enumerated choices (REQUIRED, ≥2). font kind: an
521
+ * OPTIONAL curation — the families the knob offers; absent = the whole
522
+ * hosted catalog. Faces travel with the value: `applyParamValue` writes
523
+ * the chosen family's hosted faces into `data.fonts`, which the engine
524
+ * (core ≥0.17) registers at boot and on SET_DATA — no `config.fonts`
525
+ * declaration needed, no recompile.
526
+ */
527
+ options?: string[];
528
+ /** text kind: render a multiline editor (content knobs, e.g. a headline). */
529
+ multiline?: boolean;
530
+ default: ParamValue;
531
+ }
532
+
533
+ /**
534
+ * A Look (U3b): a named set of param values — the feel-the-range layer.
535
+ * Tap a look, then fine-tune; applying is ONE undoable multi-value commit.
536
+ */
537
+ interface LookPreset {
538
+ name: string;
539
+ values: Record<string, ParamValue>;
540
+ }
541
+
542
+ /**
543
+ * Text params carry URLs (modelUrl knobs) and bound content
544
+ * (headline knobs) — longer than other string kinds.
545
+ */
546
+ const TEXT_PARAM_MAX = 280;
547
+ ```
548
+
412
549
  ## The context (`ctx`)
413
550
 
414
551
  `setup(ctx)` gets a `SetupContext` and may return assets;
@@ -517,6 +654,18 @@ Author it as GSAP: `const tl = ctx.gsap.timeline({ paused: true })`, tweens
517
654
  on `el.props`, `tl.addLabel(name, t)` per scene, return `tl`. This is the
518
655
  whole surface the engine calls on it.
519
656
 
657
+ **Staggers.** An array target (`el.segments`, a list of props) takes a
658
+ `stagger` in two forms: a number, where target i starts `i × n` seconds
659
+ in; or `{ each | amount, from }`, where `from` is an index, `'start'`,
660
+ `'center'`, `'end'` or `'edges'`, distances are normalized so the
661
+ farthest target sits at n − 1 (as GSAP does), and `amount` spreads that
662
+ range over `amount` seconds. Outside the dialect, and not flagged by
663
+ `vos check`: `grid` and `axis` are ignored (a 2-D grid staggers as one
664
+ line in array order), `from: 'random'` and a stagger `ease` are not
665
+ supported, and a function stagger starts every target at once. For a grid,
666
+ compute each cell's delay in the build script (a row-major distance from the
667
+ source's `from` cell) and place one tween per cell at its time.
668
+
520
669
  ```ts
521
670
  /**
522
671
  * Structural master-clock interface the engine seeks each frame.
@@ -24,7 +24,7 @@ units (render pixels, y down, degrees counter-clockwise).
24
24
  | a CSS `cubic-bezier(a, b, c, d)` timing | `ease: 'css-bezier(a, b, c, d)'` (exact; spelled `cubic-bezier` it plays linear) |
25
25
  | CSS `--chrome` set by the timeline | a `data` value read in `onFrame`, or a tween on the element that shows it |
26
26
  | faces resolved by the runtime from CSS families | `fonts: [{ family, weight, url }]` from the catalog |
27
- | `mix-blend-mode`, `clip-path`, `overflow: hidden` reveals | GAPS: the painter, or an opacity approximation, said |
27
+ | `mix-blend-mode`, `clip-path`, `overflow: hidden` reveals | GAPS: a revealed word keeps its bound element with an opacity approximation; the painter only for a shape; said in the push note |
28
28
 
29
29
  Two traps specific to HyperFrames sources:
30
30