@vosjs/cli 0.52.0 → 0.52.2

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.
@@ -727,6 +727,10 @@ function formatChanges(changes) {
727
727
  }
728
728
  return lines;
729
729
  }
730
+ function serverWarnings(body, log) {
731
+ const warnings = Array.isArray(body.warnings) ? body.warnings : [];
732
+ for (const w of warnings) if (typeof w === "string") log(`warning ${w}`);
733
+ }
730
734
 
731
735
  export {
732
736
  helpFlagsFor,
@@ -771,6 +775,7 @@ export {
771
775
  SYNC_STATE_NAME,
772
776
  readSyncState,
773
777
  writeSyncState,
774
- formatChanges
778
+ formatChanges,
779
+ serverWarnings
775
780
  };
776
- //# sourceMappingURL=chunk-KSEU3YK5.js.map
781
+ //# sourceMappingURL=chunk-PJX3XE7G.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/flagUse.ts","../src/args.ts","../src/loadConfig.ts","../src/browser.ts","../src/plugin/container.ts","../src/outputs.ts","../src/plugin/args.ts","../src/plugin/platform.ts","../src/plugin/hostAgent.ts"],"sourcesContent":["/**\n * Which of the flags a person gave did the verb actually READ?\n *\n * The parsers accept any `--name`, so a mistyped or misplaced flag used to be\n * swallowed: `vos frames <take> --at 3,5,8` wrote five evenly spread stills\n * and exited 0, because the flag is `--times` and nothing ever looked at\n * `at`. It looked like it worked.\n *\n * A declared list per verb would be the obvious guard and the wrong one here:\n * several verbs read their options through data (a name table, a helper three\n * calls down), so a list drifts, and the day it drifts it REFUSES a real flag\n * for every agent at once. Reading is the truth, so reading is what is\n * recorded: both parsers hand back their flags behind a proxy that notes each\n * name a verb asks for, and the CLI's one exit point compares that with what\n * was given. It cannot reject a flag a verb uses, by construction.\n *\n * The cost of being certain is that the verdict lands AFTER the verb ran, so\n * the message says so plainly: the command ran, without that flag.\n */\n\nconst given = new Set<string>()\nconst used = new Set<string>()\n\n/** A flag bag that remembers which names were asked for. */\nexport function trackFlags<T extends Record<string, unknown>>(bag: T): T {\n return new Proxy(bag, {\n get(target, key, receiver) {\n if (typeof key === 'string') used.add(key)\n return Reflect.get(target, key, receiver)\n },\n has(target, key) {\n if (typeof key === 'string') used.add(key)\n return Reflect.has(target, key)\n },\n // hasOwnProperty.call(flags, name) lands here.\n getOwnPropertyDescriptor(target, key) {\n if (typeof key === 'string') used.add(key)\n return Reflect.getOwnPropertyDescriptor(target, key)\n },\n // A spread, Object.keys or JSON.stringify hands every flag onward; what\n // happens to them after that cannot be seen, so they all count as read.\n ownKeys(target) {\n for (const key of Reflect.ownKeys(target))\n if (typeof key === 'string') used.add(key)\n return Reflect.ownKeys(target)\n },\n })\n}\n\nexport function noteGivenFlag(name: string): void {\n given.add(name)\n}\n\n/** Flags that were given and never read, in the order they were given. */\nexport function unusedFlags(): string[] {\n return [...given].filter((name) => !used.has(name) && name !== 'help')\n}\n\nexport function resetFlagUse(): void {\n given.clear()\n used.clear()\n}\n\n/**\n * The `--flags` a verb documents: its `vos <verb>` line in a help text plus\n * the indented lines that continue it.\n */\nexport function helpFlagsFor(help: string, verb: string): string[] {\n const out = new Set<string>()\n let inVerb = false\n for (const line of help.split('\\n')) {\n const head = /^\\s{2}vos\\s+(\\S+)/.exec(line)\n if (head) inVerb = head[1] === verb\n else if (!/^\\s{3,}\\S/.test(line)) inVerb = false\n if (!inVerb) continue\n for (const m of line.matchAll(/--([a-z][a-z0-9-]*)/g)) out.add(m[1])\n }\n return [...out]\n}\n\nfunction editDistance(a: string, b: string): number {\n const row = Array.from({ length: b.length + 1 }, (_, i) => i)\n for (let i = 1; i <= a.length; i++) {\n let prev = row[0]\n row[0] = i\n for (let j = 1; j <= b.length; j++) {\n const keep = row[j]\n row[j] = Math.min(\n row[j] + 1,\n row[j - 1] + 1,\n prev + (a[i - 1] === b[j - 1] ? 0 : 1),\n )\n prev = keep\n }\n }\n return row[b.length]\n}\n\n/** The documented flag a mistyped one most likely meant, or null. */\nexport function nearestFlag(name: string, candidates: string[]): string | null {\n let best: string | null = null\n let bestScore = Infinity\n for (const c of candidates) {\n if (c === name) continue\n // A candidate that contains the name (or the reverse) is as close as a\n // one-letter slip: `--zooms` for `--at-zooms`, `--body` for `--body-px`.\n const d = c.includes(name) || name.includes(c) ? 1 : editDistance(name, c)\n if (d < bestScore) {\n bestScore = d\n best = c\n }\n }\n return best !== null && bestScore <= Math.max(2, Math.floor(name.length / 3))\n ? best\n : null\n}\n\n/** The sentence for a run that ignored what it was given, or null when nothing was. */\nexport function unusedFlagsMessage(\n verb: string,\n documented: string[],\n): string | null {\n const unused = unusedFlags()\n if (!unused.length) return null\n const parts = unused.map((name) => {\n const near = nearestFlag(name, documented)\n return `--${name}${near ? ` (did you mean --${near}?)` : ''}`\n })\n const list = documented.length\n ? ` It reads: ${documented.map((f) => `--${f}`).join(' ')}.`\n : ''\n return `vos ${verb} RAN, and ignored ${parts.join(', ')}: ${unused.length > 1 ? 'they are' : 'it is'} not something this verb reads, so what it printed above was made without ${unused.length > 1 ? 'them' : 'it'}.${list}`\n}\n","/**\n * Minimal argv parser — positionals + `--flag value` / `--flag=value` /\n * boolean flags. Deliberately tiny: the CLI has a small, stable surface and\n * agents benefit from predictable, dependency-free parsing.\n */\nimport { noteGivenFlag, trackFlags } from './flagUse'\n\nexport interface ParsedArgs {\n positionals: string[]\n flags: Record<string, string | true>\n}\n\nexport class UsageError extends Error {}\n\nexport function parseArgs(\n argv: string[],\n booleanFlags: ReadonlySet<string>,\n): ParsedArgs {\n const positionals: string[] = []\n const flags: Record<string, string | true> = {}\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n if (arg === '--') {\n positionals.push(...argv.slice(i + 1))\n break\n }\n if (arg.startsWith('--')) {\n const eq = arg.indexOf('=')\n if (eq !== -1) {\n flags[arg.slice(2, eq)] = arg.slice(eq + 1)\n continue\n }\n const name = arg.slice(2)\n if (booleanFlags.has(name)) {\n flags[name] = true\n continue\n }\n const next = argv[i + 1]\n if (next === undefined || next.startsWith('--')) {\n throw new UsageError(`--${name} expects a value`)\n }\n flags[name] = next\n i++\n continue\n }\n positionals.push(arg)\n }\n for (const name of Object.keys(flags)) noteGivenFlag(name)\n return { positionals, flags: trackFlags(flags) }\n}\n\nexport function numFlag(\n flags: ParsedArgs['flags'],\n name: string,\n fallback: number,\n): number {\n const v = flags[name]\n if (v === undefined) return fallback\n const n = Number(v)\n if (!Number.isFinite(n))\n throw new UsageError(`--${name} expects a number, got \"${String(v)}\"`)\n return n\n}\n","import { readFile } from 'node:fs/promises'\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport {\n CURRENT_CONFIG_VERSION,\n migrateConfig,\n vosConfigJsonSchema,\n} from '@vosjs/core'\nimport { UsageError } from './args'\n\nexport interface LoadedConfig {\n config: Record<string, unknown>\n warnings: string[]\n}\n\n/**\n * What a directory holds, by one deterministic sniff and never a flag:\n * a TAKE carries a recording document (`doc.json` with `source`); a PROGRAM\n * directory carries `config.json`, optionally beside a program document\n * (`doc.json` without `source`: the shared layers, the tween overlay, the\n * program's own length, with `program.config` omitted on disk). An unparsable\n * doc.json counts as a take so the take path reports the real error.\n */\nexport type DirectoryKind = 'take' | 'program' | 'none'\n\nexport function directoryKind(target: string): DirectoryKind {\n try {\n if (!statSync(target).isDirectory()) return 'none'\n } catch {\n return 'none'\n }\n const docPath = join(target, 'doc.json')\n if (existsSync(docPath)) {\n try {\n const doc: unknown = JSON.parse(readFileSync(docPath, 'utf8'))\n if (typeof doc === 'object' && doc !== null && 'source' in doc)\n return 'take'\n } catch {\n return 'take'\n }\n }\n return existsSync(join(target, 'config.json')) ? 'program' : 'none'\n}\n\n/**\n * A program directory's config: `config.json`, composed with the program\n * document beside it when there is one (the layers ride as the studio stack\n * entry, the tween overlay is baked into `createTimeline`), so a render of\n * the directory is a render of what the studio and vos.so play.\n */\nexport async function loadProgramDirectory(\n dir: string,\n warnings: string[],\n): Promise<unknown> {\n const config: unknown = JSON.parse(\n await readFile(join(dir, 'config.json'), 'utf8'),\n )\n const docPath = join(dir, 'doc.json')\n if (!existsSync(docPath)) return config\n const doc: unknown = JSON.parse(await readFile(docPath, 'utf8'))\n if (typeof doc !== 'object' || doc === null || 'source' in doc) return config\n if (typeof config !== 'object' || config === null) {\n throw new UsageError(`${dir}/config.json does not contain a JSON object`)\n }\n const raw = doc as Record<string, unknown>\n const program =\n raw.program && typeof raw.program === 'object'\n ? (raw.program as Record<string, unknown>)\n : {}\n const { lowerProgramDoc } = await import('@vosjs/studio-core')\n const composed = lowerProgramDoc(\n { ...raw, program: { ...program, config } } as never,\n { bake: true },\n ).config\n warnings.push(\n 'composed config.json with doc.json (a program document: its layers and tween edits ride the render)',\n )\n return composed\n}\n\n/**\n * The text of a config source: an http(s) URL or a file. A directory that is\n * neither a take nor a program directory is refused in words, since the raw\n * read would report a bare EISDIR.\n */\nexport async function readSourceText(source: string): Promise<string> {\n if (/^https?:\\/\\//.test(source)) {\n const res = await fetch(source)\n if (!res.ok) throw new Error(`fetch ${source} → ${res.status}`)\n return await res.text()\n }\n let isDir = false\n try {\n isDir = statSync(source).isDirectory()\n } catch {\n /* a missing path reads below and reports itself */\n }\n if (isDir) {\n throw new UsageError(\n `${source} is a directory with no config.json and no doc.json — pass a config file, a URL, a program directory or a take`,\n )\n }\n return await readFile(source, 'utf8')\n}\n\n/**\n * Load a VosConfigJson from a file path, an http(s) URL, or a program\n * directory (config.json, composed with a program document when one sits\n * beside it); unwrap API `{ config }` envelopes, migrate old versions, and\n * validate against the schema. A take directory is refused here in words:\n * takes render through the take pipeline (`vos render <take>`).\n */\nexport async function loadVosConfig(source: string): Promise<LoadedConfig> {\n const warnings: string[] = []\n let parsed: unknown\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). Render it through the take pipeline: vos render ${source} [out]; vos frames ${source} for stills.`,\n )\n }\n if (kind === 'program') {\n parsed = await loadProgramDirectory(source, warnings)\n return finish(source, parsed, warnings)\n }\n const raw = await readSourceText(source)\n\n try {\n parsed = JSON.parse(raw)\n } catch (e) {\n throw new UsageError(`${source} is not valid JSON: ${(e as Error).message}`)\n }\n return finish(source, parsed, warnings)\n}\n\nfunction finish(\n source: string,\n parsed: unknown,\n warnings: string[],\n): LoadedConfig {\n if (typeof parsed !== 'object' || parsed === null) {\n throw new UsageError(`${source} does not contain a JSON object`)\n }\n\n // API endpoints wrap the config: { config: {...} }.\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 warnings.push('unwrapped { config } envelope')\n }\n\n const version = obj.version\n const migrated = migrateConfig(obj)\n if (version === undefined) {\n // Plays fine (the stamp happens above), but a storer refuses it.\n warnings.push(\n `config declares no \"version\". Add \"version\": ${CURRENT_CONFIG_VERSION} before pushing it.`,\n )\n } else if (version !== CURRENT_CONFIG_VERSION) {\n warnings.push(\n `migrated config v${String(version)} to v${CURRENT_CONFIG_VERSION}`,\n )\n }\n\n const check = vosConfigJsonSchema.safeParse(migrated)\n if (!check.success) {\n const issues = check.error.issues\n .slice(0, 5)\n .map((i) => ` ${i.path.join('.') || '(root)'}: ${i.message}`)\n .join('\\n')\n throw new UsageError(`invalid vos config:\\n${issues}`)\n }\n\n return { config: migrated, warnings }\n}\n\n/** Best-effort duration from the config (seconds). */\nexport function configDuration(\n config: Record<string, unknown>,\n): number | undefined {\n const d = config.duration\n return typeof d === 'number' && Number.isFinite(d) && d > 0 ? d : undefined\n}\n","import { chromium } from 'playwright'\nimport type { Browser } from 'playwright'\n\nexport class BrowserUnavailableError extends Error {\n constructor(cause: string) {\n super(\n `Could not launch a Chromium-family browser (${cause}).\\n` +\n `Fixes, in order of preference:\\n` +\n ` 1. Install Google Chrome (used automatically), or\\n` +\n ` 2. npx playwright install chromium, or\\n` +\n ` 3. Set VOS_BROWSER_PATH to a Chrome/Chromium executable.`,\n )\n }\n}\n\n/**\n * Launch headless Chromium: explicit VOS_BROWSER_PATH → system Chrome\n * (no download needed) → Playwright's bundled Chromium.\n *\n * `args` are extra Chromium switches (`--browser-arg` on the verbs that\n * record). Some product surfaces cannot be reached without one: a recorder\n * needs a fake capture device to get past a permission prompt, an extension\n * page needs the extension loaded. Passed through verbatim.\n */\nexport async function launchBrowser(args: string[] = []): Promise<Browser> {\n const opts = args.length ? { args } : {}\n const explicit = process.env.VOS_BROWSER_PATH\n if (explicit) {\n return chromium.launch({ ...opts, executablePath: explicit }).catch((e) => {\n throw new BrowserUnavailableError(\n `VOS_BROWSER_PATH failed: ${(e as Error).message}`,\n )\n })\n }\n try {\n return await chromium.launch({ ...opts, channel: 'chrome' })\n } catch {\n try {\n return await chromium.launch(opts)\n } catch (e) {\n throw new BrowserUnavailableError(\n (e as Error).message.split('\\n')[0] ?? 'unknown',\n )\n }\n }\n}\n","/**\n * What a media file IS, read from its first bytes.\n *\n * A file's NAME and its Content-Type are both CLAIMS, and both drift from the\n * bytes: the in-page recorder remuxes a take to mp4 while keeping the name\n * `recording.webm`, and a hosted asset keeps whatever name it was uploaded\n * under. Believing a claim is how an mp4 gets uploaded as `video/webm`, and\n * every render of that version then dies inside the video element with a\n * format error — one machine and one day away from the mislabelling that\n * caused it.\n *\n * So the rule is one line: THE BYTES DECIDE. A claim is the fallback only for\n * bytes that say nothing (an SVG, a recipe, an unknown blob), which keeps the\n * override safe — a name is corrected only when the file positively contradicts\n * it, never on a guess.\n */\n\nconst OCTET_STREAM = 'application/octet-stream'\n\n/** The one extension↔type table. */\nconst TYPES_BY_EXTENSION: Record<string, string> = {\n svg: 'image/svg+xml',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n webp: 'image/webp',\n gif: 'image/gif',\n avif: 'image/avif',\n webm: 'video/webm',\n mp4: 'video/mp4',\n mov: 'video/quicktime',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n m4a: 'audio/mp4',\n}\n\n/** The extension each type is written under, where the table is many-to-one. */\nconst EXTENSION_BY_TYPE: Record<string, string> = {\n 'image/jpeg': '.jpg',\n 'video/mp4': '.mp4',\n 'audio/mp4': '.m4a',\n}\n\n/**\n * Every container a take's own media (footage, mic, cam) can come home in —\n * the name `record` writes first, so a resolver is deterministic when a stale\n * sibling is somehow present.\n */\nexport const TAKE_MEDIA_EXTENSIONS = [\n '.webm',\n '.mp4',\n '.mov',\n '.m4a',\n '.mp3',\n '.ogg',\n '.wav',\n] as const\n\n/** Enough bytes for every signature below (the longest reads 12). */\nexport const MEDIA_HEAD_BYTES = 16\n\n/**\n * ISO base media brands that are NOT plain mp4. Everything else carrying an\n * `ftyp` box (isom, mp42, avc1, iso5, dash, M4V…) is video/mp4.\n */\nconst ISO_BRANDS: Record<string, string> = {\n 'qt ': 'video/quicktime',\n 'M4A ': 'audio/mp4',\n 'M4B ': 'audio/mp4',\n avif: 'image/avif',\n avis: 'image/avif',\n}\n\n/** RIFF forms, read from the four bytes after the size field. */\nconst RIFF_FORMS: Record<string, string> = {\n WEBP: 'image/webp',\n WAVE: 'audio/wav',\n 'AVI ': 'video/x-msvideo',\n}\n\n/** The media type a file's first bytes prove, or null when they prove nothing. */\nexport function sniffMediaType(head: Uint8Array): string | null {\n const ascii = (at: number, len: number): string =>\n head.length >= at + len\n ? String.fromCharCode(...head.subarray(at, at + len))\n : ''\n const startsWith = (...bytes: number[]): boolean =>\n bytes.every((b, i) => head[i] === b)\n\n // EBML: Matroska and its WebM profile. A .mkv sniffs as webm, which is what\n // every consumer here wants — the distinction is a codec constraint, not a\n // container one.\n if (startsWith(0x1a, 0x45, 0xdf, 0xa3)) return 'video/webm'\n if (ascii(4, 4) === 'ftyp') return ISO_BRANDS[ascii(8, 4)] ?? 'video/mp4'\n if (ascii(0, 4) === 'RIFF') return RIFF_FORMS[ascii(8, 4)] ?? null\n if (startsWith(0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a))\n return 'image/png'\n if (startsWith(0xff, 0xd8, 0xff)) return 'image/jpeg'\n if (ascii(0, 4) === 'GIF8') return 'image/gif'\n if (ascii(0, 4) === 'OggS') return 'audio/ogg'\n if (ascii(0, 3) === 'ID3') return 'audio/mpeg'\n return null\n}\n\n/** A Content-Type header reduced to its bare type, or '' when it says nothing. */\nfunction bareType(value: string | null | undefined): string {\n const type = (value ?? '').split(';')[0].trim().toLowerCase()\n // `binary/octet-stream` is a common spelling of the same non-answer.\n return !type || type === OCTET_STREAM || type === 'binary/octet-stream'\n ? ''\n : type\n}\n\n/** The content type a file's NAME claims, by extension. */\nexport function mediaContentType(file: string): string {\n const ext = (/\\.([a-z0-9]+)$/i.exec(file)?.[1] ?? '').toLowerCase()\n return TYPES_BY_EXTENSION[ext] ?? OCTET_STREAM\n}\n\n/** The extension a media type is written under ('' when it has none here). */\nexport function extensionFor(contentType: string): string {\n const type = (contentType ?? '').split(';')[0].trim().toLowerCase()\n if (EXTENSION_BY_TYPE[type]) return EXTENSION_BY_TYPE[type]\n const ext = Object.keys(TYPES_BY_EXTENSION).find(\n (e) => TYPES_BY_EXTENSION[e] === type,\n )\n return ext ? `.${ext}` : ''\n}\n\nexport interface MediaTypeClaims {\n /** The file's first bytes — `MEDIA_HEAD_BYTES` is enough. */\n head?: Uint8Array | null\n /** What a server said it was serving. */\n declared?: string | null\n /** What the name claims. */\n filename?: string | null\n}\n\nexport interface ResolvedMediaType {\n type: string\n /** Which claim won, so a caller can SAY when the bytes overruled a name. */\n from: 'bytes' | 'declared' | 'filename' | 'unknown'\n}\n\n/** The type to believe: the bytes, then the server, then the name. */\nexport function resolveMediaType(claims: MediaTypeClaims): ResolvedMediaType {\n const sniffed = claims.head?.length ? sniffMediaType(claims.head) : null\n if (sniffed) return { type: sniffed, from: 'bytes' }\n const declared = bareType(claims.declared)\n if (declared) return { type: declared, from: 'declared' }\n const named = mediaContentType(claims.filename ?? '')\n if (named !== OCTET_STREAM) return { type: named, from: 'filename' }\n return { type: OCTET_STREAM, from: 'unknown' }\n}\n\n/**\n * The same name wearing the extension its type earns. Unchanged when the two\n * already agree (`.jpeg` is not churned into `.jpg`) or when the type has no\n * extension here, so a correction is always a real one.\n */\nexport function nameForType(filename: string, type: string): string {\n const ext = extensionFor(type)\n if (!ext) return filename\n const current = /\\.[a-z0-9]+$/i.exec(filename)?.[0] ?? ''\n if (current && mediaContentType(`x${current}`) === type) return filename\n return `${filename.slice(0, filename.length - current.length)}${ext}`\n}\n\n/**\n * What to say about a file whose name and bytes disagree, or null when they\n * agree or the bytes prove nothing.\n *\n * Every reader in this CLI now believes the bytes (the take server sniffs, a\n * push declares from them), so a mismatch breaks nothing here — it is still a\n * file lying about itself to every player, editor and person who opens it, and\n * the rename is one move.\n */\nexport function containerMismatch(\n filename: string,\n head: Uint8Array,\n): string | null {\n const actual = sniffMediaType(head)\n if (!actual) return null\n const claimed = mediaContentType(filename)\n if (claimed === actual) return null\n return `${filename} is ${actual}, not the ${claimed} its name claims — rename it to ${nameForType(filename, actual)}`\n}\n","/**\n * What the output is, decided from what the person asked for.\n *\n * Pure, so the engine verbs (a program) and the take verbs agree: a file\n * named `out.mp4` IS an mp4, an explicit `--format` that contradicts the\n * name is refused instead of writing one container under the other's name,\n * and a still named `.png` is a PNG.\n */\nimport { UsageError } from './args'\n\nexport type VideoFormat = 'webm' | 'mp4'\nexport type StillFormat = 'webp' | 'png' | 'jpeg'\n\nfunction extOf(path: string | undefined): string {\n return /\\.([a-z0-9]+)$/i.exec(path ?? '')?.[1]?.toLowerCase() ?? ''\n}\n\n/** The video container: the flag, else the output's name, else WebM. */\nexport function videoFormat(\n flag: string | undefined,\n out: string | undefined,\n): VideoFormat {\n const named = extOf(out)\n const fromName: VideoFormat | null =\n named === 'mp4' ? 'mp4' : named === 'webm' ? 'webm' : null\n if (flag !== undefined) {\n if (flag !== 'webm' && flag !== 'mp4')\n throw new UsageError('--format must be webm or mp4')\n if (fromName && fromName !== flag)\n throw new UsageError(\n `--format ${flag} contradicts the output name ${out}: name it .${flag}, or drop --format and the name decides`,\n )\n return flag\n }\n return fromName ?? 'webm'\n}\n\n/** The still's encoding, from its name. The engine captures WebP. */\nexport function stillFormat(out: string): StillFormat {\n const ext = extOf(out)\n if (ext === 'png') return 'png'\n if (ext === 'jpg' || ext === 'jpeg') return 'jpeg'\n if (ext === 'webp' || ext === '') return 'webp'\n throw new UsageError(\n `vos still writes .webp, .png or .jpg, not .${ext}: rename ${out}`,\n )\n}\n\n/**\n * `--times 0,1.5,50%,100%`: seconds or percentages of the duration, in the\n * order given. 100% is clamped a hair inside the end, where the last frame is.\n */\nexport function parseTimes(raw: string, duration: number): number[] {\n const times = raw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n .map((s) => {\n const pct = /^(\\d+(?:\\.\\d+)?)%$/.exec(s)\n const t = pct ? (Number(pct[1]) / 100) * duration : Number(s)\n if (!Number.isFinite(t) || t < 0)\n throw new UsageError(\n `--times expects seconds or percentages (0,1.5,50%), got \"${s}\"`,\n )\n return Math.min(t, Math.max(0, duration - 1 / 60))\n })\n if (!times.length) throw new UsageError('--times needs at least one time')\n return times\n}\n\n/** Where the still for one time of several goes: `name-1.50s.ext`. */\nexport function stillOutFor(out: string, time: number, many: boolean): string {\n if (!many) return out\n const m = /^(.*?)(\\.[a-z0-9]+)?$/i.exec(out)\n return `${m?.[1] ?? out}-${time.toFixed(2)}s${m?.[2] ?? ''}`\n}\n\n/** Every `--set` value in argv, in order (the parser keeps only the last). */\nexport function setFlags(argv: string[]): string[] {\n const out: string[] = []\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]\n if (a === '--set' && argv[i + 1] !== undefined) out.push(argv[++i])\n else if (a.startsWith('--set=')) out.push(a.slice('--set='.length))\n }\n return out\n}\n\n/**\n * `--set data.<key>=<value>` on a PROGRAM: the knob-honesty check without a\n * second config. The value reads as JSON when it parses (numbers, booleans,\n * arrays) and as a string otherwise. Patched in memory, never written back.\n */\nexport function applyDataSets(\n config: Record<string, unknown>,\n sets: string[],\n): Record<string, unknown> {\n if (!sets.length) return config\n const data: Record<string, unknown> = {\n ...((config.data as Record<string, unknown> | undefined) ?? {}),\n }\n for (const set of sets) {\n const eq = set.indexOf('=')\n const path = eq === -1 ? set : set.slice(0, eq)\n const m = /^data\\.([A-Za-z0-9_$-]+)$/.exec(path)\n if (eq === -1 || !m)\n throw new UsageError(\n `--set on a program takes data.<key>=<value> (a knob's value), got \"${set}\"`,\n )\n const raw = set.slice(eq + 1)\n let value: unknown = raw\n try {\n value = JSON.parse(raw)\n } catch {\n // a bare word is a string: --set data.name=ROBIN\n }\n data[m[1]] = value\n }\n return { ...config, data }\n}\n","/** Minimal argv parser — mirrors @vosjs/cli's conventions. */\nimport { noteGivenFlag, trackFlags } from '../flagUse'\nimport { UsageError } from '../args'\n\nexport interface ParsedArgs {\n positionals: string[]\n flags: Record<string, string | true>\n /** Values of flags declared repeatable (multiFlags) — accumulated in order. */\n multi: Record<string, string[]>\n}\n\n// ONE usage error for the whole CLI: a verb that reaches a shared helper\n// (outputs.ts) must be reported as a usage error on either side.\nexport { UsageError }\n\nexport function parseArgs(\n argv: string[],\n booleanFlags: ReadonlySet<string>,\n multiFlags: ReadonlySet<string> = new Set(),\n): ParsedArgs {\n const positionals: string[] = []\n const flags: Record<string, string | true> = {}\n const multi: Record<string, string[]> = {}\n const put = (name: string, value: string): void => {\n if (multiFlags.has(name)) (multi[name] ??= []).push(value)\n else flags[name] = value\n }\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n if (arg === '--') {\n positionals.push(...argv.slice(i + 1))\n break\n }\n if (arg.startsWith('--')) {\n const eq = arg.indexOf('=')\n if (eq !== -1) {\n put(arg.slice(2, eq), arg.slice(eq + 1))\n continue\n }\n const name = arg.slice(2)\n if (booleanFlags.has(name)) {\n flags[name] = true\n continue\n }\n if (i + 1 >= argv.length || argv[i + 1].startsWith('--')) {\n throw new UsageError(`--${name} expects a value`)\n }\n put(name, argv[i + 1])\n i++\n continue\n }\n positionals.push(arg)\n }\n for (const name of Object.keys(flags)) noteGivenFlag(name)\n for (const name of Object.keys(multi)) noteGivenFlag(name)\n return { positionals, flags: trackFlags(flags), multi: trackFlags(multi) }\n}\n\nexport function numFlag(\n flags: ParsedArgs['flags'],\n name: string,\n fallback: number,\n): number {\n const v = flags[name]\n if (typeof v !== 'string') return fallback\n const n = Number(v)\n if (!Number.isFinite(n))\n throw new UsageError(`--${name} expects a number, got \"${v}\"`)\n return n\n}\n\n/** String flag value, or undefined when absent (or boolean). */\nexport function strFlag(\n flags: ParsedArgs['flags'],\n name: string,\n): string | undefined {\n const v = flags[name]\n return typeof v === 'string' ? v : undefined\n}\n\nexport function hasFlag(flags: ParsedArgs['flags'], name: string): boolean {\n return Object.prototype.hasOwnProperty.call(flags, name)\n}\n","/**\n * vos.so platform client — THE one HTTP surface of the vos CLI.\n * Every platform verb (fetch/push/pull for programs AND takes, login) goes\n * through here: one origin resolution, one credential ladder, one sync-state\n * file, one 409 conflict shape.\n *\n * Contract notes (mirrored at https://vos.so/llms.txt):\n * - public/unlisted programs read with no auth; writes ride a bearer key\n * - credentials resolve --key → VOS_API_KEY → ~/.config/vos/credentials\n * (first line; `vos login` writes it) and are NEVER printed — not in\n * logs, not in errors, not in NDJSON events\n * - pushed configs travel as the MIGRATED object with params/presets\n * untouched; the platform validates and compiles server-side\n */\nimport { mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { UsageError } from './args'\nimport { detectHostAgent } from './hostAgent'\nimport type { Env } from './hostAgent'\n\n/**\n * Origin resolution: --origin (or legacy --api) → VOS_ORIGIN → legacy\n * VOS_API_BASE (which historically carried a trailing /api) → https://vos.so.\n * Every shape converges on a bare origin; paths below carry the /api prefix.\n */\nexport function platformOrigin(\n flags: { origin?: string; api?: string } = {},\n): string {\n const legacyEnv = process.env.VOS_API_BASE?.trim()\n const raw =\n flags.origin ??\n flags.api ??\n process.env.VOS_ORIGIN?.trim() ??\n legacyEnv ??\n 'https://vos.so'\n if (!flags.origin && !flags.api && !process.env.VOS_ORIGIN && legacyEnv) {\n process.stderr.write(\n 'note: VOS_API_BASE is deprecated — set VOS_ORIGIN (an origin, no /api suffix)\\n',\n )\n }\n return raw.replace(/\\/+$/, '').replace(/\\/api$/, '')\n}\n\n/**\n * Accepts a bare vos id, a watch URL (vos.so/vos/{id}), an embed URL\n * (/embed/vos/{id}), or a studio/stage URL (?vos={id}).\n */\nexport function parseVosId(input: string): string {\n if (!/^https?:\\/\\//.test(input)) {\n if (/^[A-Za-z0-9_-]+$/.test(input)) return input\n throw new UsageError(`\"${input}\" is not a vos id or URL`)\n }\n let url: URL\n try {\n url = new URL(input)\n } catch {\n throw new UsageError(`\"${input}\" is not a valid URL`)\n }\n const query = url.searchParams.get('vos')\n if (query) return query\n const path = url.pathname.match(/\\/vos\\/([A-Za-z0-9_-]+)/)\n if (path) return path[1]\n throw new UsageError(\n `could not find a vos id in ${input} — expected /vos/{id} or ?vos={id}`,\n )\n}\n\nexport function deriveSlug(title: string): string {\n return (\n title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 50)\n .replace(/-+$/, '') || 'remix'\n )\n}\n\nexport const CREDENTIALS_PATH = join(homedir(), '.config', 'vos', 'credentials')\n\n/**\n * The full documented ladder: explicit --key → VOS_API_KEY → the first line\n * of ~/.config/vos/credentials. A vos_rg_ remix grant is just a key here.\n * Returns null when nothing resolves — callers decide whether auth is needed.\n */\nexport function resolveCredential(explicit?: string): string | null {\n const flag = explicit?.trim()\n if (flag) return flag\n const env = process.env.VOS_API_KEY?.trim()\n if (env) return env\n try {\n const first = readFileSync(CREDENTIALS_PATH, 'utf8').split('\\n')[0].trim()\n return first || null\n } catch {\n return null\n }\n}\n\nexport function requireCredential(explicit?: string): string {\n const key = resolveCredential(explicit)\n if (!key) {\n throw new Error(\n 'no credential found — pass --key, set VOS_API_KEY, or run `vos login` ' +\n '(mint a content key at https://vos.so/app/api; a vos_rg_ remix grant works too)',\n )\n }\n return key\n}\n\n/** `vos login` writes here: 0700 dir, 0600 file, first line is the key. */\nexport function writeCredential(key: string): string {\n mkdirSync(join(homedir(), '.config', 'vos'), { recursive: true, mode: 0o700 })\n writeFileSync(CREDENTIALS_PATH, `${key.trim()}\\n`, { mode: 0o600 })\n return CREDENTIALS_PATH\n}\n\n/**\n * The self-reported client string stamped onto pushed versions (User-Agent\n * grammar). Display-only on the platform — attribution trust\n * stays with the credential. The ladder: an explicit VOS_CLIENT (e.g.\n * VOS_CLIENT=claude-code/2.1), else the coding agent this CLI was spawned by\n * (`claude-code vos-cli`, read from the environment by `detectHostAgent`),\n * else the transport alone, so history rows name the tool that pushed.\n */\nexport function clientId(\n env: Env = process.env,\n exists?: (path: string) => boolean,\n): string {\n const explicit = env.VOS_CLIENT?.trim()\n if (explicit && explicit.length <= 60 && !/[\\r\\n]/.test(explicit))\n return explicit\n const host = detectHostAgent(env, exists)\n return host ? `${host} vos-cli` : 'vos-cli'\n}\n\nexport interface ApiResult {\n status: number\n body: Record<string, unknown>\n}\n\nexport async function apiJson(\n origin: string,\n path: string,\n init: {\n method?: string\n key?: string | null\n body?: unknown\n /** Raw request body (uploads) — used verbatim, with `headers`. */\n raw?: Uint8Array\n headers?: Record<string, string>\n } = {},\n): Promise<ApiResult> {\n const headers: Record<string, string> = {\n accept: 'application/json',\n ...init.headers,\n }\n if (init.key) headers.authorization = `Bearer ${init.key}`\n if (init.body !== undefined) headers['content-type'] = 'application/json'\n const reqBody: BodyInit | undefined =\n init.raw !== undefined\n ? (init.raw as unknown as BodyInit)\n : init.body === undefined\n ? undefined\n : JSON.stringify(init.body)\n const res = await fetch(`${origin}${path}`, {\n method: init.method ?? 'GET',\n headers,\n body: reqBody,\n })\n let body: Record<string, unknown> = {}\n try {\n body = (await res.json()) as Record<string, unknown>\n } catch {\n // non-JSON error bodies stay {}\n }\n return { status: res.status, body }\n}\n\n/** Human-readable error line for a failed platform response. Never echoes credentials. */\nexport function apiError(what: string, r: ApiResult): string {\n const detail =\n typeof r.body.error === 'string'\n ? r.body.error\n : r.body.details !== undefined\n ? JSON.stringify(r.body.details)\n : ''\n const hint =\n r.status === 401\n ? ' (run `vos login`, or set VOS_API_KEY — mint a key at https://vos.so/app/api)'\n : ''\n return `${what} → ${r.status}${detail ? `: ${detail}` : ''}${hint}`\n}\n\n// ---------------------------------------------------------------------------\n// Sync state — ONE file (vos.json) for both artifact kinds, legacy-read\n// compatible with the two files it replaces: push.json (take dirs) and\n// meta.json (fetched program dirs).\n// ---------------------------------------------------------------------------\n\nexport const SYNC_STATE_NAME = 'vos.json'\n\nexport interface SyncState {\n vosId: string\n /** The hosted version this directory is based on — the next push's base. */\n versionId: string | null\n pushedAt?: string\n title?: string\n slug?: string\n remixOfId?: string\n}\n\nfunction readJsonFile(file: string): Record<string, unknown> | null {\n try {\n return JSON.parse(readFileSync(file, 'utf8')) as Record<string, unknown>\n } catch {\n return null\n }\n}\n\n/**\n * vos.json → legacy push.json ({vosId, versionId}) → legacy meta.json\n * ({id, currentVersionId} — guarded on a string `id` so a take dir's\n * RecordingMeta meta.json is never misread as sync state).\n */\nexport function readSyncState(dir: string): SyncState | null {\n const own = readJsonFile(join(dir, SYNC_STATE_NAME))\n if (own && typeof own.vosId === 'string') {\n return {\n vosId: own.vosId,\n versionId: typeof own.versionId === 'string' ? own.versionId : null,\n ...(typeof own.pushedAt === 'string' ? { pushedAt: own.pushedAt } : {}),\n ...(typeof own.title === 'string' ? { title: own.title } : {}),\n ...(typeof own.slug === 'string' ? { slug: own.slug } : {}),\n ...(typeof own.remixOfId === 'string'\n ? { remixOfId: own.remixOfId }\n : {}),\n }\n }\n const push = readJsonFile(join(dir, 'push.json'))\n if (push && typeof push.vosId === 'string') {\n return {\n vosId: push.vosId,\n versionId: typeof push.versionId === 'string' ? push.versionId : null,\n ...(typeof push.pushedAt === 'string' ? { pushedAt: push.pushedAt } : {}),\n }\n }\n const meta = readJsonFile(join(dir, 'meta.json'))\n if (meta && typeof meta.id === 'string') {\n return {\n vosId: meta.id,\n versionId:\n typeof meta.currentVersionId === 'string'\n ? meta.currentVersionId\n : null,\n ...(typeof meta.title === 'string' ? { title: meta.title } : {}),\n ...(typeof meta.slug === 'string' ? { slug: meta.slug } : {}),\n ...(typeof meta.remixOfId === 'string'\n ? { remixOfId: meta.remixOfId }\n : {}),\n }\n }\n return null\n}\n\n/** Merge-writes vos.json; the legacy files are left alone (read-only compat). */\nexport function writeSyncState(\n dir: string,\n patch: Partial<SyncState> & { vosId: string },\n): SyncState {\n const merged = {\n ...(readSyncState(dir) ?? { versionId: null }),\n ...patch,\n } as SyncState\n writeFileSync(\n join(dir, SYNC_STATE_NAME),\n `${JSON.stringify(merged, null, 2)}\\n`,\n )\n return merged\n}\n\n// ---------------------------------------------------------------------------\n// The /changes payload (shape mirrored from the platform)\n// ---------------------------------------------------------------------------\n\nexport interface VersionChange {\n versionId?: string\n versionNumber?: number\n origin?: string\n label?: string | null\n note?: string | null\n summary?: string\n}\n\n/** Human lines for a changes walk — origin/label attributed, note indented. */\nexport function formatChanges(changes: readonly VersionChange[]): string[] {\n const lines: string[] = []\n for (const ch of changes) {\n const label = ch.label ? ` · ${ch.label}` : ''\n const who = ch.origin === 'studio' ? 'human' : (ch.origin ?? 'unknown')\n lines.push(\n `v${String(ch.versionNumber ?? '?')} (${who}${label}): ${ch.summary ?? ''}`,\n )\n if (ch.note) lines.push(` note: ${ch.note}`)\n }\n return lines\n}\n","import { existsSync } from 'node:fs'\n\n/**\n * Which coding agent is driving this CLI, read from the environment the agent\n * spawned it in. Used ONLY to name the tool on a pushed version (`clientId`),\n * which the platform treats as display-only: attribution trust stays with the\n * credential, so a wrong or spoofed answer changes a label and nothing else.\n *\n * The signals follow `@vercel/detect-agent` (Apache-2.0), kept as a small\n * synchronous table because every push reads it: the cross-tool `AI_AGENT`\n * convention first, then each host's own marker. Returns a lowercase slug,\n * or null when nothing says an agent is here.\n */\nexport type Env = Record<string, string | undefined>\n\nconst SLUG = /^[a-z0-9][a-z0-9.-]{0,39}$/\n\n/** The markers each host sets in the shells it runs, in precedence order. */\nconst MARKERS: Array<[slug: string, test: (env: Env) => boolean]> = [\n ['cursor', (e) => !!e.CURSOR_TRACE_ID],\n [\n 'cursor-cli',\n (e) => !!e.CURSOR_AGENT || e.CURSOR_EXTENSION_HOST_ROLE === 'agent-exec',\n ],\n ['gemini-cli', (e) => !!e.GEMINI_CLI],\n ['codex', (e) => !!(e.CODEX_SANDBOX || e.CODEX_CI || e.CODEX_THREAD_ID)],\n ['antigravity', (e) => !!e.ANTIGRAVITY_AGENT],\n ['augment-cli', (e) => !!e.AUGMENT_AGENT],\n ['opencode', (e) => !!e.OPENCODE_CLIENT],\n [\n 'claude-code',\n (e) => !!(e.CLAUDECODE || e.CLAUDE_CODE) && !e.CLAUDE_CODE_IS_COWORK,\n ],\n ['claude-cowork', (e) => !!(e.CLAUDECODE || e.CLAUDE_CODE)],\n ['replit', (e) => !!e.REPL_ID],\n [\n 'github-copilot',\n (e) => !!(e.COPILOT_MODEL || e.COPILOT_ALLOW_ALL || e.COPILOT_GITHUB_TOKEN),\n ],\n]\n\nconst DEVIN_LOCAL_PATH = '/opt/.devin'\n\n/**\n * `AI_AGENT` is free text by convention (`claude-code_2-1-284_agent`,\n * `github-copilot`, `v0`): the tool's name is its leading run of\n * `[a-z0-9.-]`, lowercased, so a version or a role suffix never reaches the\n * label and nothing but a short slug ever leaves the machine.\n */\nexport function slugFromAiAgent(value: string): string | null {\n const head = value.trim().toLowerCase().split('_')[0] ?? ''\n if (head === 'github-copilot-cli') return 'github-copilot'\n return SLUG.test(head) ? head : null\n}\n\nexport function detectHostAgent(\n env: Env = process.env,\n exists: (path: string) => boolean = existsSync,\n): string | null {\n if (env.AI_AGENT) {\n const slug = slugFromAiAgent(env.AI_AGENT)\n if (slug) return slug\n }\n for (const [slug, test] of MARKERS) if (test(env)) return slug\n try {\n if (exists(DEVIN_LOCAL_PATH)) return 'devin'\n } catch {\n // An unreadable path is not an agent.\n }\n return null\n}\n"],"mappings":"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","names":["parseArgs","numFlag","readFileSync","join","existsSync","join","readFileSync"]}
1
+ {"version":3,"sources":["../src/flagUse.ts","../src/args.ts","../src/loadConfig.ts","../src/browser.ts","../src/plugin/container.ts","../src/outputs.ts","../src/plugin/args.ts","../src/plugin/platform.ts","../src/plugin/hostAgent.ts"],"sourcesContent":["/**\n * Which of the flags a person gave did the verb actually READ?\n *\n * The parsers accept any `--name`, so a mistyped or misplaced flag used to be\n * swallowed: `vos frames <take> --at 3,5,8` wrote five evenly spread stills\n * and exited 0, because the flag is `--times` and nothing ever looked at\n * `at`. It looked like it worked.\n *\n * A declared list per verb would be the obvious guard and the wrong one here:\n * several verbs read their options through data (a name table, a helper three\n * calls down), so a list drifts, and the day it drifts it REFUSES a real flag\n * for every agent at once. Reading is the truth, so reading is what is\n * recorded: both parsers hand back their flags behind a proxy that notes each\n * name a verb asks for, and the CLI's one exit point compares that with what\n * was given. It cannot reject a flag a verb uses, by construction.\n *\n * The cost of being certain is that the verdict lands AFTER the verb ran, so\n * the message says so plainly: the command ran, without that flag.\n */\n\nconst given = new Set<string>()\nconst used = new Set<string>()\n\n/** A flag bag that remembers which names were asked for. */\nexport function trackFlags<T extends Record<string, unknown>>(bag: T): T {\n return new Proxy(bag, {\n get(target, key, receiver) {\n if (typeof key === 'string') used.add(key)\n return Reflect.get(target, key, receiver)\n },\n has(target, key) {\n if (typeof key === 'string') used.add(key)\n return Reflect.has(target, key)\n },\n // hasOwnProperty.call(flags, name) lands here.\n getOwnPropertyDescriptor(target, key) {\n if (typeof key === 'string') used.add(key)\n return Reflect.getOwnPropertyDescriptor(target, key)\n },\n // A spread, Object.keys or JSON.stringify hands every flag onward; what\n // happens to them after that cannot be seen, so they all count as read.\n ownKeys(target) {\n for (const key of Reflect.ownKeys(target))\n if (typeof key === 'string') used.add(key)\n return Reflect.ownKeys(target)\n },\n })\n}\n\nexport function noteGivenFlag(name: string): void {\n given.add(name)\n}\n\n/** Flags that were given and never read, in the order they were given. */\nexport function unusedFlags(): string[] {\n return [...given].filter((name) => !used.has(name) && name !== 'help')\n}\n\nexport function resetFlagUse(): void {\n given.clear()\n used.clear()\n}\n\n/**\n * The `--flags` a verb documents: its `vos <verb>` line in a help text plus\n * the indented lines that continue it.\n */\nexport function helpFlagsFor(help: string, verb: string): string[] {\n const out = new Set<string>()\n let inVerb = false\n for (const line of help.split('\\n')) {\n const head = /^\\s{2}vos\\s+(\\S+)/.exec(line)\n if (head) inVerb = head[1] === verb\n else if (!/^\\s{3,}\\S/.test(line)) inVerb = false\n if (!inVerb) continue\n for (const m of line.matchAll(/--([a-z][a-z0-9-]*)/g)) out.add(m[1])\n }\n return [...out]\n}\n\nfunction editDistance(a: string, b: string): number {\n const row = Array.from({ length: b.length + 1 }, (_, i) => i)\n for (let i = 1; i <= a.length; i++) {\n let prev = row[0]\n row[0] = i\n for (let j = 1; j <= b.length; j++) {\n const keep = row[j]\n row[j] = Math.min(\n row[j] + 1,\n row[j - 1] + 1,\n prev + (a[i - 1] === b[j - 1] ? 0 : 1),\n )\n prev = keep\n }\n }\n return row[b.length]\n}\n\n/** The documented flag a mistyped one most likely meant, or null. */\nexport function nearestFlag(name: string, candidates: string[]): string | null {\n let best: string | null = null\n let bestScore = Infinity\n for (const c of candidates) {\n if (c === name) continue\n // A candidate that contains the name (or the reverse) is as close as a\n // one-letter slip: `--zooms` for `--at-zooms`, `--body` for `--body-px`.\n const d = c.includes(name) || name.includes(c) ? 1 : editDistance(name, c)\n if (d < bestScore) {\n bestScore = d\n best = c\n }\n }\n return best !== null && bestScore <= Math.max(2, Math.floor(name.length / 3))\n ? best\n : null\n}\n\n/** The sentence for a run that ignored what it was given, or null when nothing was. */\nexport function unusedFlagsMessage(\n verb: string,\n documented: string[],\n): string | null {\n const unused = unusedFlags()\n if (!unused.length) return null\n const parts = unused.map((name) => {\n const near = nearestFlag(name, documented)\n return `--${name}${near ? ` (did you mean --${near}?)` : ''}`\n })\n const list = documented.length\n ? ` It reads: ${documented.map((f) => `--${f}`).join(' ')}.`\n : ''\n return `vos ${verb} RAN, and ignored ${parts.join(', ')}: ${unused.length > 1 ? 'they are' : 'it is'} not something this verb reads, so what it printed above was made without ${unused.length > 1 ? 'them' : 'it'}.${list}`\n}\n","/**\n * Minimal argv parser — positionals + `--flag value` / `--flag=value` /\n * boolean flags. Deliberately tiny: the CLI has a small, stable surface and\n * agents benefit from predictable, dependency-free parsing.\n */\nimport { noteGivenFlag, trackFlags } from './flagUse'\n\nexport interface ParsedArgs {\n positionals: string[]\n flags: Record<string, string | true>\n}\n\nexport class UsageError extends Error {}\n\nexport function parseArgs(\n argv: string[],\n booleanFlags: ReadonlySet<string>,\n): ParsedArgs {\n const positionals: string[] = []\n const flags: Record<string, string | true> = {}\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n if (arg === '--') {\n positionals.push(...argv.slice(i + 1))\n break\n }\n if (arg.startsWith('--')) {\n const eq = arg.indexOf('=')\n if (eq !== -1) {\n flags[arg.slice(2, eq)] = arg.slice(eq + 1)\n continue\n }\n const name = arg.slice(2)\n if (booleanFlags.has(name)) {\n flags[name] = true\n continue\n }\n const next = argv[i + 1]\n if (next === undefined || next.startsWith('--')) {\n throw new UsageError(`--${name} expects a value`)\n }\n flags[name] = next\n i++\n continue\n }\n positionals.push(arg)\n }\n for (const name of Object.keys(flags)) noteGivenFlag(name)\n return { positionals, flags: trackFlags(flags) }\n}\n\nexport function numFlag(\n flags: ParsedArgs['flags'],\n name: string,\n fallback: number,\n): number {\n const v = flags[name]\n if (v === undefined) return fallback\n const n = Number(v)\n if (!Number.isFinite(n))\n throw new UsageError(`--${name} expects a number, got \"${String(v)}\"`)\n return n\n}\n","import { readFile } from 'node:fs/promises'\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport {\n CURRENT_CONFIG_VERSION,\n migrateConfig,\n vosConfigJsonSchema,\n} from '@vosjs/core'\nimport { UsageError } from './args'\n\nexport interface LoadedConfig {\n config: Record<string, unknown>\n warnings: string[]\n}\n\n/**\n * What a directory holds, by one deterministic sniff and never a flag:\n * a TAKE carries a recording document (`doc.json` with `source`); a PROGRAM\n * directory carries `config.json`, optionally beside a program document\n * (`doc.json` without `source`: the shared layers, the tween overlay, the\n * program's own length, with `program.config` omitted on disk). An unparsable\n * doc.json counts as a take so the take path reports the real error.\n */\nexport type DirectoryKind = 'take' | 'program' | 'none'\n\nexport function directoryKind(target: string): DirectoryKind {\n try {\n if (!statSync(target).isDirectory()) return 'none'\n } catch {\n return 'none'\n }\n const docPath = join(target, 'doc.json')\n if (existsSync(docPath)) {\n try {\n const doc: unknown = JSON.parse(readFileSync(docPath, 'utf8'))\n if (typeof doc === 'object' && doc !== null && 'source' in doc)\n return 'take'\n } catch {\n return 'take'\n }\n }\n return existsSync(join(target, 'config.json')) ? 'program' : 'none'\n}\n\n/**\n * A program directory's config: `config.json`, composed with the program\n * document beside it when there is one (the layers ride as the studio stack\n * entry, the tween overlay is baked into `createTimeline`), so a render of\n * the directory is a render of what the studio and vos.so play.\n */\nexport async function loadProgramDirectory(\n dir: string,\n warnings: string[],\n): Promise<unknown> {\n const config: unknown = JSON.parse(\n await readFile(join(dir, 'config.json'), 'utf8'),\n )\n const docPath = join(dir, 'doc.json')\n if (!existsSync(docPath)) return config\n const doc: unknown = JSON.parse(await readFile(docPath, 'utf8'))\n if (typeof doc !== 'object' || doc === null || 'source' in doc) return config\n if (typeof config !== 'object' || config === null) {\n throw new UsageError(`${dir}/config.json does not contain a JSON object`)\n }\n const raw = doc as Record<string, unknown>\n const program =\n raw.program && typeof raw.program === 'object'\n ? (raw.program as Record<string, unknown>)\n : {}\n const { lowerProgramDoc } = await import('@vosjs/studio-core')\n const composed = lowerProgramDoc(\n { ...raw, program: { ...program, config } } as never,\n { bake: true },\n ).config\n warnings.push(\n 'composed config.json with doc.json (a program document: its layers and tween edits ride the render)',\n )\n return composed\n}\n\n/**\n * The text of a config source: an http(s) URL or a file. A directory that is\n * neither a take nor a program directory is refused in words, since the raw\n * read would report a bare EISDIR.\n */\nexport async function readSourceText(source: string): Promise<string> {\n if (/^https?:\\/\\//.test(source)) {\n const res = await fetch(source)\n if (!res.ok) throw new Error(`fetch ${source} → ${res.status}`)\n return await res.text()\n }\n let isDir = false\n try {\n isDir = statSync(source).isDirectory()\n } catch {\n /* a missing path reads below and reports itself */\n }\n if (isDir) {\n throw new UsageError(\n `${source} is a directory with no config.json and no doc.json — pass a config file, a URL, a program directory or a take`,\n )\n }\n return await readFile(source, 'utf8')\n}\n\n/**\n * Load a VosConfigJson from a file path, an http(s) URL, or a program\n * directory (config.json, composed with a program document when one sits\n * beside it); unwrap API `{ config }` envelopes, migrate old versions, and\n * validate against the schema. A take directory is refused here in words:\n * takes render through the take pipeline (`vos render <take>`).\n */\nexport async function loadVosConfig(source: string): Promise<LoadedConfig> {\n const warnings: string[] = []\n let parsed: unknown\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). Render it through the take pipeline: vos render ${source} [out]; vos frames ${source} for stills.`,\n )\n }\n if (kind === 'program') {\n parsed = await loadProgramDirectory(source, warnings)\n return finish(source, parsed, warnings)\n }\n const raw = await readSourceText(source)\n\n try {\n parsed = JSON.parse(raw)\n } catch (e) {\n throw new UsageError(`${source} is not valid JSON: ${(e as Error).message}`)\n }\n return finish(source, parsed, warnings)\n}\n\nfunction finish(\n source: string,\n parsed: unknown,\n warnings: string[],\n): LoadedConfig {\n if (typeof parsed !== 'object' || parsed === null) {\n throw new UsageError(`${source} does not contain a JSON object`)\n }\n\n // API endpoints wrap the config: { config: {...} }.\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 warnings.push('unwrapped { config } envelope')\n }\n\n const version = obj.version\n const migrated = migrateConfig(obj)\n if (version === undefined) {\n // Plays fine (the stamp happens above), but a storer refuses it.\n warnings.push(\n `config declares no \"version\". Add \"version\": ${CURRENT_CONFIG_VERSION} before pushing it.`,\n )\n } else if (version !== CURRENT_CONFIG_VERSION) {\n warnings.push(\n `migrated config v${String(version)} to v${CURRENT_CONFIG_VERSION}`,\n )\n }\n\n const check = vosConfigJsonSchema.safeParse(migrated)\n if (!check.success) {\n const issues = check.error.issues\n .slice(0, 5)\n .map((i) => ` ${i.path.join('.') || '(root)'}: ${i.message}`)\n .join('\\n')\n throw new UsageError(`invalid vos config:\\n${issues}`)\n }\n\n return { config: migrated, warnings }\n}\n\n/** Best-effort duration from the config (seconds). */\nexport function configDuration(\n config: Record<string, unknown>,\n): number | undefined {\n const d = config.duration\n return typeof d === 'number' && Number.isFinite(d) && d > 0 ? d : undefined\n}\n","import { chromium } from 'playwright'\nimport type { Browser } from 'playwright'\n\nexport class BrowserUnavailableError extends Error {\n constructor(cause: string) {\n super(\n `Could not launch a Chromium-family browser (${cause}).\\n` +\n `Fixes, in order of preference:\\n` +\n ` 1. Install Google Chrome (used automatically), or\\n` +\n ` 2. npx playwright install chromium, or\\n` +\n ` 3. Set VOS_BROWSER_PATH to a Chrome/Chromium executable.`,\n )\n }\n}\n\n/**\n * Launch headless Chromium: explicit VOS_BROWSER_PATH → system Chrome\n * (no download needed) → Playwright's bundled Chromium.\n *\n * `args` are extra Chromium switches (`--browser-arg` on the verbs that\n * record). Some product surfaces cannot be reached without one: a recorder\n * needs a fake capture device to get past a permission prompt, an extension\n * page needs the extension loaded. Passed through verbatim.\n */\nexport async function launchBrowser(args: string[] = []): Promise<Browser> {\n const opts = args.length ? { args } : {}\n const explicit = process.env.VOS_BROWSER_PATH\n if (explicit) {\n return chromium.launch({ ...opts, executablePath: explicit }).catch((e) => {\n throw new BrowserUnavailableError(\n `VOS_BROWSER_PATH failed: ${(e as Error).message}`,\n )\n })\n }\n try {\n return await chromium.launch({ ...opts, channel: 'chrome' })\n } catch {\n try {\n return await chromium.launch(opts)\n } catch (e) {\n throw new BrowserUnavailableError(\n (e as Error).message.split('\\n')[0] ?? 'unknown',\n )\n }\n }\n}\n","/**\n * What a media file IS, read from its first bytes.\n *\n * A file's NAME and its Content-Type are both CLAIMS, and both drift from the\n * bytes: the in-page recorder remuxes a take to mp4 while keeping the name\n * `recording.webm`, and a hosted asset keeps whatever name it was uploaded\n * under. Believing a claim is how an mp4 gets uploaded as `video/webm`, and\n * every render of that version then dies inside the video element with a\n * format error — one machine and one day away from the mislabelling that\n * caused it.\n *\n * So the rule is one line: THE BYTES DECIDE. A claim is the fallback only for\n * bytes that say nothing (an SVG, a recipe, an unknown blob), which keeps the\n * override safe — a name is corrected only when the file positively contradicts\n * it, never on a guess.\n */\n\nconst OCTET_STREAM = 'application/octet-stream'\n\n/** The one extension↔type table. */\nconst TYPES_BY_EXTENSION: Record<string, string> = {\n svg: 'image/svg+xml',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n webp: 'image/webp',\n gif: 'image/gif',\n avif: 'image/avif',\n webm: 'video/webm',\n mp4: 'video/mp4',\n mov: 'video/quicktime',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n m4a: 'audio/mp4',\n}\n\n/** The extension each type is written under, where the table is many-to-one. */\nconst EXTENSION_BY_TYPE: Record<string, string> = {\n 'image/jpeg': '.jpg',\n 'video/mp4': '.mp4',\n 'audio/mp4': '.m4a',\n}\n\n/**\n * Every container a take's own media (footage, mic, cam) can come home in —\n * the name `record` writes first, so a resolver is deterministic when a stale\n * sibling is somehow present.\n */\nexport const TAKE_MEDIA_EXTENSIONS = [\n '.webm',\n '.mp4',\n '.mov',\n '.m4a',\n '.mp3',\n '.ogg',\n '.wav',\n] as const\n\n/** Enough bytes for every signature below (the longest reads 12). */\nexport const MEDIA_HEAD_BYTES = 16\n\n/**\n * ISO base media brands that are NOT plain mp4. Everything else carrying an\n * `ftyp` box (isom, mp42, avc1, iso5, dash, M4V…) is video/mp4.\n */\nconst ISO_BRANDS: Record<string, string> = {\n 'qt ': 'video/quicktime',\n 'M4A ': 'audio/mp4',\n 'M4B ': 'audio/mp4',\n avif: 'image/avif',\n avis: 'image/avif',\n}\n\n/** RIFF forms, read from the four bytes after the size field. */\nconst RIFF_FORMS: Record<string, string> = {\n WEBP: 'image/webp',\n WAVE: 'audio/wav',\n 'AVI ': 'video/x-msvideo',\n}\n\n/** The media type a file's first bytes prove, or null when they prove nothing. */\nexport function sniffMediaType(head: Uint8Array): string | null {\n const ascii = (at: number, len: number): string =>\n head.length >= at + len\n ? String.fromCharCode(...head.subarray(at, at + len))\n : ''\n const startsWith = (...bytes: number[]): boolean =>\n bytes.every((b, i) => head[i] === b)\n\n // EBML: Matroska and its WebM profile. A .mkv sniffs as webm, which is what\n // every consumer here wants — the distinction is a codec constraint, not a\n // container one.\n if (startsWith(0x1a, 0x45, 0xdf, 0xa3)) return 'video/webm'\n if (ascii(4, 4) === 'ftyp') return ISO_BRANDS[ascii(8, 4)] ?? 'video/mp4'\n if (ascii(0, 4) === 'RIFF') return RIFF_FORMS[ascii(8, 4)] ?? null\n if (startsWith(0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a))\n return 'image/png'\n if (startsWith(0xff, 0xd8, 0xff)) return 'image/jpeg'\n if (ascii(0, 4) === 'GIF8') return 'image/gif'\n if (ascii(0, 4) === 'OggS') return 'audio/ogg'\n if (ascii(0, 3) === 'ID3') return 'audio/mpeg'\n return null\n}\n\n/** A Content-Type header reduced to its bare type, or '' when it says nothing. */\nfunction bareType(value: string | null | undefined): string {\n const type = (value ?? '').split(';')[0].trim().toLowerCase()\n // `binary/octet-stream` is a common spelling of the same non-answer.\n return !type || type === OCTET_STREAM || type === 'binary/octet-stream'\n ? ''\n : type\n}\n\n/** The content type a file's NAME claims, by extension. */\nexport function mediaContentType(file: string): string {\n const ext = (/\\.([a-z0-9]+)$/i.exec(file)?.[1] ?? '').toLowerCase()\n return TYPES_BY_EXTENSION[ext] ?? OCTET_STREAM\n}\n\n/** The extension a media type is written under ('' when it has none here). */\nexport function extensionFor(contentType: string): string {\n const type = (contentType ?? '').split(';')[0].trim().toLowerCase()\n if (EXTENSION_BY_TYPE[type]) return EXTENSION_BY_TYPE[type]\n const ext = Object.keys(TYPES_BY_EXTENSION).find(\n (e) => TYPES_BY_EXTENSION[e] === type,\n )\n return ext ? `.${ext}` : ''\n}\n\nexport interface MediaTypeClaims {\n /** The file's first bytes — `MEDIA_HEAD_BYTES` is enough. */\n head?: Uint8Array | null\n /** What a server said it was serving. */\n declared?: string | null\n /** What the name claims. */\n filename?: string | null\n}\n\nexport interface ResolvedMediaType {\n type: string\n /** Which claim won, so a caller can SAY when the bytes overruled a name. */\n from: 'bytes' | 'declared' | 'filename' | 'unknown'\n}\n\n/** The type to believe: the bytes, then the server, then the name. */\nexport function resolveMediaType(claims: MediaTypeClaims): ResolvedMediaType {\n const sniffed = claims.head?.length ? sniffMediaType(claims.head) : null\n if (sniffed) return { type: sniffed, from: 'bytes' }\n const declared = bareType(claims.declared)\n if (declared) return { type: declared, from: 'declared' }\n const named = mediaContentType(claims.filename ?? '')\n if (named !== OCTET_STREAM) return { type: named, from: 'filename' }\n return { type: OCTET_STREAM, from: 'unknown' }\n}\n\n/**\n * The same name wearing the extension its type earns. Unchanged when the two\n * already agree (`.jpeg` is not churned into `.jpg`) or when the type has no\n * extension here, so a correction is always a real one.\n */\nexport function nameForType(filename: string, type: string): string {\n const ext = extensionFor(type)\n if (!ext) return filename\n const current = /\\.[a-z0-9]+$/i.exec(filename)?.[0] ?? ''\n if (current && mediaContentType(`x${current}`) === type) return filename\n return `${filename.slice(0, filename.length - current.length)}${ext}`\n}\n\n/**\n * What to say about a file whose name and bytes disagree, or null when they\n * agree or the bytes prove nothing.\n *\n * Every reader in this CLI now believes the bytes (the take server sniffs, a\n * push declares from them), so a mismatch breaks nothing here — it is still a\n * file lying about itself to every player, editor and person who opens it, and\n * the rename is one move.\n */\nexport function containerMismatch(\n filename: string,\n head: Uint8Array,\n): string | null {\n const actual = sniffMediaType(head)\n if (!actual) return null\n const claimed = mediaContentType(filename)\n if (claimed === actual) return null\n return `${filename} is ${actual}, not the ${claimed} its name claims — rename it to ${nameForType(filename, actual)}`\n}\n","/**\n * What the output is, decided from what the person asked for.\n *\n * Pure, so the engine verbs (a program) and the take verbs agree: a file\n * named `out.mp4` IS an mp4, an explicit `--format` that contradicts the\n * name is refused instead of writing one container under the other's name,\n * and a still named `.png` is a PNG.\n */\nimport { UsageError } from './args'\n\nexport type VideoFormat = 'webm' | 'mp4'\nexport type StillFormat = 'webp' | 'png' | 'jpeg'\n\nfunction extOf(path: string | undefined): string {\n return /\\.([a-z0-9]+)$/i.exec(path ?? '')?.[1]?.toLowerCase() ?? ''\n}\n\n/** The video container: the flag, else the output's name, else WebM. */\nexport function videoFormat(\n flag: string | undefined,\n out: string | undefined,\n): VideoFormat {\n const named = extOf(out)\n const fromName: VideoFormat | null =\n named === 'mp4' ? 'mp4' : named === 'webm' ? 'webm' : null\n if (flag !== undefined) {\n if (flag !== 'webm' && flag !== 'mp4')\n throw new UsageError('--format must be webm or mp4')\n if (fromName && fromName !== flag)\n throw new UsageError(\n `--format ${flag} contradicts the output name ${out}: name it .${flag}, or drop --format and the name decides`,\n )\n return flag\n }\n return fromName ?? 'webm'\n}\n\n/** The still's encoding, from its name. The engine captures WebP. */\nexport function stillFormat(out: string): StillFormat {\n const ext = extOf(out)\n if (ext === 'png') return 'png'\n if (ext === 'jpg' || ext === 'jpeg') return 'jpeg'\n if (ext === 'webp' || ext === '') return 'webp'\n throw new UsageError(\n `vos still writes .webp, .png or .jpg, not .${ext}: rename ${out}`,\n )\n}\n\n/**\n * `--times 0,1.5,50%,100%`: seconds or percentages of the duration, in the\n * order given. 100% is clamped a hair inside the end, where the last frame is.\n */\nexport function parseTimes(raw: string, duration: number): number[] {\n const times = raw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n .map((s) => {\n const pct = /^(\\d+(?:\\.\\d+)?)%$/.exec(s)\n const t = pct ? (Number(pct[1]) / 100) * duration : Number(s)\n if (!Number.isFinite(t) || t < 0)\n throw new UsageError(\n `--times expects seconds or percentages (0,1.5,50%), got \"${s}\"`,\n )\n return Math.min(t, Math.max(0, duration - 1 / 60))\n })\n if (!times.length) throw new UsageError('--times needs at least one time')\n return times\n}\n\n/** Where the still for one time of several goes: `name-1.50s.ext`. */\nexport function stillOutFor(out: string, time: number, many: boolean): string {\n if (!many) return out\n const m = /^(.*?)(\\.[a-z0-9]+)?$/i.exec(out)\n return `${m?.[1] ?? out}-${time.toFixed(2)}s${m?.[2] ?? ''}`\n}\n\n/** Every `--set` value in argv, in order (the parser keeps only the last). */\nexport function setFlags(argv: string[]): string[] {\n const out: string[] = []\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]\n if (a === '--set' && argv[i + 1] !== undefined) out.push(argv[++i])\n else if (a.startsWith('--set=')) out.push(a.slice('--set='.length))\n }\n return out\n}\n\n/**\n * `--set data.<key>=<value>` on a PROGRAM: the knob-honesty check without a\n * second config. The value reads as JSON when it parses (numbers, booleans,\n * arrays) and as a string otherwise. Patched in memory, never written back.\n */\nexport function applyDataSets(\n config: Record<string, unknown>,\n sets: string[],\n): Record<string, unknown> {\n if (!sets.length) return config\n const data: Record<string, unknown> = {\n ...((config.data as Record<string, unknown> | undefined) ?? {}),\n }\n for (const set of sets) {\n const eq = set.indexOf('=')\n const path = eq === -1 ? set : set.slice(0, eq)\n const m = /^data\\.([A-Za-z0-9_$-]+)$/.exec(path)\n if (eq === -1 || !m)\n throw new UsageError(\n `--set on a program takes data.<key>=<value> (a knob's value), got \"${set}\"`,\n )\n const raw = set.slice(eq + 1)\n let value: unknown = raw\n try {\n value = JSON.parse(raw)\n } catch {\n // a bare word is a string: --set data.name=ROBIN\n }\n data[m[1]] = value\n }\n return { ...config, data }\n}\n","/** Minimal argv parser — mirrors @vosjs/cli's conventions. */\nimport { noteGivenFlag, trackFlags } from '../flagUse'\nimport { UsageError } from '../args'\n\nexport interface ParsedArgs {\n positionals: string[]\n flags: Record<string, string | true>\n /** Values of flags declared repeatable (multiFlags) — accumulated in order. */\n multi: Record<string, string[]>\n}\n\n// ONE usage error for the whole CLI: a verb that reaches a shared helper\n// (outputs.ts) must be reported as a usage error on either side.\nexport { UsageError }\n\nexport function parseArgs(\n argv: string[],\n booleanFlags: ReadonlySet<string>,\n multiFlags: ReadonlySet<string> = new Set(),\n): ParsedArgs {\n const positionals: string[] = []\n const flags: Record<string, string | true> = {}\n const multi: Record<string, string[]> = {}\n const put = (name: string, value: string): void => {\n if (multiFlags.has(name)) (multi[name] ??= []).push(value)\n else flags[name] = value\n }\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n if (arg === '--') {\n positionals.push(...argv.slice(i + 1))\n break\n }\n if (arg.startsWith('--')) {\n const eq = arg.indexOf('=')\n if (eq !== -1) {\n put(arg.slice(2, eq), arg.slice(eq + 1))\n continue\n }\n const name = arg.slice(2)\n if (booleanFlags.has(name)) {\n flags[name] = true\n continue\n }\n if (i + 1 >= argv.length || argv[i + 1].startsWith('--')) {\n throw new UsageError(`--${name} expects a value`)\n }\n put(name, argv[i + 1])\n i++\n continue\n }\n positionals.push(arg)\n }\n for (const name of Object.keys(flags)) noteGivenFlag(name)\n for (const name of Object.keys(multi)) noteGivenFlag(name)\n return { positionals, flags: trackFlags(flags), multi: trackFlags(multi) }\n}\n\nexport function numFlag(\n flags: ParsedArgs['flags'],\n name: string,\n fallback: number,\n): number {\n const v = flags[name]\n if (typeof v !== 'string') return fallback\n const n = Number(v)\n if (!Number.isFinite(n))\n throw new UsageError(`--${name} expects a number, got \"${v}\"`)\n return n\n}\n\n/** String flag value, or undefined when absent (or boolean). */\nexport function strFlag(\n flags: ParsedArgs['flags'],\n name: string,\n): string | undefined {\n const v = flags[name]\n return typeof v === 'string' ? v : undefined\n}\n\nexport function hasFlag(flags: ParsedArgs['flags'], name: string): boolean {\n return Object.prototype.hasOwnProperty.call(flags, name)\n}\n","/**\n * vos.so platform client — THE one HTTP surface of the vos CLI.\n * Every platform verb (fetch/push/pull for programs AND takes, login) goes\n * through here: one origin resolution, one credential ladder, one sync-state\n * file, one 409 conflict shape.\n *\n * Contract notes (mirrored at https://vos.so/llms.txt):\n * - public/unlisted programs read with no auth; writes ride a bearer key\n * - credentials resolve --key → VOS_API_KEY → ~/.config/vos/credentials\n * (first line; `vos login` writes it) and are NEVER printed — not in\n * logs, not in errors, not in NDJSON events\n * - pushed configs travel as the MIGRATED object with params/presets\n * untouched; the platform validates and compiles server-side\n */\nimport { mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { UsageError } from './args'\nimport { detectHostAgent } from './hostAgent'\nimport type { Env } from './hostAgent'\n\n/**\n * Origin resolution: --origin (or legacy --api) → VOS_ORIGIN → legacy\n * VOS_API_BASE (which historically carried a trailing /api) → https://vos.so.\n * Every shape converges on a bare origin; paths below carry the /api prefix.\n */\nexport function platformOrigin(\n flags: { origin?: string; api?: string } = {},\n): string {\n const legacyEnv = process.env.VOS_API_BASE?.trim()\n const raw =\n flags.origin ??\n flags.api ??\n process.env.VOS_ORIGIN?.trim() ??\n legacyEnv ??\n 'https://vos.so'\n if (!flags.origin && !flags.api && !process.env.VOS_ORIGIN && legacyEnv) {\n process.stderr.write(\n 'note: VOS_API_BASE is deprecated — set VOS_ORIGIN (an origin, no /api suffix)\\n',\n )\n }\n return raw.replace(/\\/+$/, '').replace(/\\/api$/, '')\n}\n\n/**\n * Accepts a bare vos id, a watch URL (vos.so/vos/{id}), an embed URL\n * (/embed/vos/{id}), or a studio/stage URL (?vos={id}).\n */\nexport function parseVosId(input: string): string {\n if (!/^https?:\\/\\//.test(input)) {\n if (/^[A-Za-z0-9_-]+$/.test(input)) return input\n throw new UsageError(`\"${input}\" is not a vos id or URL`)\n }\n let url: URL\n try {\n url = new URL(input)\n } catch {\n throw new UsageError(`\"${input}\" is not a valid URL`)\n }\n const query = url.searchParams.get('vos')\n if (query) return query\n const path = url.pathname.match(/\\/vos\\/([A-Za-z0-9_-]+)/)\n if (path) return path[1]\n throw new UsageError(\n `could not find a vos id in ${input} — expected /vos/{id} or ?vos={id}`,\n )\n}\n\nexport function deriveSlug(title: string): string {\n return (\n title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 50)\n .replace(/-+$/, '') || 'remix'\n )\n}\n\nexport const CREDENTIALS_PATH = join(homedir(), '.config', 'vos', 'credentials')\n\n/**\n * The full documented ladder: explicit --key → VOS_API_KEY → the first line\n * of ~/.config/vos/credentials. A vos_rg_ remix grant is just a key here.\n * Returns null when nothing resolves — callers decide whether auth is needed.\n */\nexport function resolveCredential(explicit?: string): string | null {\n const flag = explicit?.trim()\n if (flag) return flag\n const env = process.env.VOS_API_KEY?.trim()\n if (env) return env\n try {\n const first = readFileSync(CREDENTIALS_PATH, 'utf8').split('\\n')[0].trim()\n return first || null\n } catch {\n return null\n }\n}\n\nexport function requireCredential(explicit?: string): string {\n const key = resolveCredential(explicit)\n if (!key) {\n throw new Error(\n 'no credential found — pass --key, set VOS_API_KEY, or run `vos login` ' +\n '(mint a content key at https://vos.so/app/api; a vos_rg_ remix grant works too)',\n )\n }\n return key\n}\n\n/** `vos login` writes here: 0700 dir, 0600 file, first line is the key. */\nexport function writeCredential(key: string): string {\n mkdirSync(join(homedir(), '.config', 'vos'), { recursive: true, mode: 0o700 })\n writeFileSync(CREDENTIALS_PATH, `${key.trim()}\\n`, { mode: 0o600 })\n return CREDENTIALS_PATH\n}\n\n/**\n * The self-reported client string stamped onto pushed versions (User-Agent\n * grammar). Display-only on the platform — attribution trust\n * stays with the credential. The ladder: an explicit VOS_CLIENT (e.g.\n * VOS_CLIENT=claude-code/2.1), else the coding agent this CLI was spawned by\n * (`claude-code vos-cli`, read from the environment by `detectHostAgent`),\n * else the transport alone, so history rows name the tool that pushed.\n */\nexport function clientId(\n env: Env = process.env,\n exists?: (path: string) => boolean,\n): string {\n const explicit = env.VOS_CLIENT?.trim()\n if (explicit && explicit.length <= 60 && !/[\\r\\n]/.test(explicit))\n return explicit\n const host = detectHostAgent(env, exists)\n return host ? `${host} vos-cli` : 'vos-cli'\n}\n\nexport interface ApiResult {\n status: number\n body: Record<string, unknown>\n}\n\nexport async function apiJson(\n origin: string,\n path: string,\n init: {\n method?: string\n key?: string | null\n body?: unknown\n /** Raw request body (uploads) — used verbatim, with `headers`. */\n raw?: Uint8Array\n headers?: Record<string, string>\n } = {},\n): Promise<ApiResult> {\n const headers: Record<string, string> = {\n accept: 'application/json',\n ...init.headers,\n }\n if (init.key) headers.authorization = `Bearer ${init.key}`\n if (init.body !== undefined) headers['content-type'] = 'application/json'\n const reqBody: BodyInit | undefined =\n init.raw !== undefined\n ? (init.raw as unknown as BodyInit)\n : init.body === undefined\n ? undefined\n : JSON.stringify(init.body)\n const res = await fetch(`${origin}${path}`, {\n method: init.method ?? 'GET',\n headers,\n body: reqBody,\n })\n let body: Record<string, unknown> = {}\n try {\n body = (await res.json()) as Record<string, unknown>\n } catch {\n // non-JSON error bodies stay {}\n }\n return { status: res.status, body }\n}\n\n/** Human-readable error line for a failed platform response. Never echoes credentials. */\nexport function apiError(what: string, r: ApiResult): string {\n const detail =\n typeof r.body.error === 'string'\n ? r.body.error\n : r.body.details !== undefined\n ? JSON.stringify(r.body.details)\n : ''\n const hint =\n r.status === 401\n ? ' (run `vos login`, or set VOS_API_KEY — mint a key at https://vos.so/app/api)'\n : ''\n return `${what} → ${r.status}${detail ? `: ${detail}` : ''}${hint}`\n}\n\n// ---------------------------------------------------------------------------\n// Sync state — ONE file (vos.json) for both artifact kinds, legacy-read\n// compatible with the two files it replaces: push.json (take dirs) and\n// meta.json (fetched program dirs).\n// ---------------------------------------------------------------------------\n\nexport const SYNC_STATE_NAME = 'vos.json'\n\nexport interface SyncState {\n vosId: string\n /** The hosted version this directory is based on — the next push's base. */\n versionId: string | null\n pushedAt?: string\n title?: string\n slug?: string\n remixOfId?: string\n}\n\nfunction readJsonFile(file: string): Record<string, unknown> | null {\n try {\n return JSON.parse(readFileSync(file, 'utf8')) as Record<string, unknown>\n } catch {\n return null\n }\n}\n\n/**\n * vos.json → legacy push.json ({vosId, versionId}) → legacy meta.json\n * ({id, currentVersionId} — guarded on a string `id` so a take dir's\n * RecordingMeta meta.json is never misread as sync state).\n */\nexport function readSyncState(dir: string): SyncState | null {\n const own = readJsonFile(join(dir, SYNC_STATE_NAME))\n if (own && typeof own.vosId === 'string') {\n return {\n vosId: own.vosId,\n versionId: typeof own.versionId === 'string' ? own.versionId : null,\n ...(typeof own.pushedAt === 'string' ? { pushedAt: own.pushedAt } : {}),\n ...(typeof own.title === 'string' ? { title: own.title } : {}),\n ...(typeof own.slug === 'string' ? { slug: own.slug } : {}),\n ...(typeof own.remixOfId === 'string'\n ? { remixOfId: own.remixOfId }\n : {}),\n }\n }\n const push = readJsonFile(join(dir, 'push.json'))\n if (push && typeof push.vosId === 'string') {\n return {\n vosId: push.vosId,\n versionId: typeof push.versionId === 'string' ? push.versionId : null,\n ...(typeof push.pushedAt === 'string' ? { pushedAt: push.pushedAt } : {}),\n }\n }\n const meta = readJsonFile(join(dir, 'meta.json'))\n if (meta && typeof meta.id === 'string') {\n return {\n vosId: meta.id,\n versionId:\n typeof meta.currentVersionId === 'string'\n ? meta.currentVersionId\n : null,\n ...(typeof meta.title === 'string' ? { title: meta.title } : {}),\n ...(typeof meta.slug === 'string' ? { slug: meta.slug } : {}),\n ...(typeof meta.remixOfId === 'string'\n ? { remixOfId: meta.remixOfId }\n : {}),\n }\n }\n return null\n}\n\n/** Merge-writes vos.json; the legacy files are left alone (read-only compat). */\nexport function writeSyncState(\n dir: string,\n patch: Partial<SyncState> & { vosId: string },\n): SyncState {\n const merged = {\n ...(readSyncState(dir) ?? { versionId: null }),\n ...patch,\n } as SyncState\n writeFileSync(\n join(dir, SYNC_STATE_NAME),\n `${JSON.stringify(merged, null, 2)}\\n`,\n )\n return merged\n}\n\n// ---------------------------------------------------------------------------\n// The /changes payload (shape mirrored from the platform)\n// ---------------------------------------------------------------------------\n\nexport interface VersionChange {\n versionId?: string\n versionNumber?: number\n origin?: string\n label?: string | null\n note?: string | null\n summary?: string\n}\n\n/** Human lines for a changes walk — origin/label attributed, note indented. */\nexport function formatChanges(changes: readonly VersionChange[]): string[] {\n const lines: string[] = []\n for (const ch of changes) {\n const label = ch.label ? ` · ${ch.label}` : ''\n const who = ch.origin === 'studio' ? 'human' : (ch.origin ?? 'unknown')\n lines.push(\n `v${String(ch.versionNumber ?? '?')} (${who}${label}): ${ch.summary ?? ''}`,\n )\n if (ch.note) lines.push(` note: ${ch.note}`)\n }\n return lines\n}\n\n/**\n * The warnings a write came back with (a knob or Look the platform dropped),\n * one line each. A push that saved is not a push that kept everything.\n */\nexport function serverWarnings(\n body: Record<string, unknown>,\n log: (line: string) => void,\n): void {\n const warnings = Array.isArray(body.warnings) ? body.warnings : []\n for (const w of warnings) if (typeof w === 'string') log(`warning ${w}`)\n}\n","import { existsSync } from 'node:fs'\n\n/**\n * Which coding agent is driving this CLI, read from the environment the agent\n * spawned it in. Used ONLY to name the tool on a pushed version (`clientId`),\n * which the platform treats as display-only: attribution trust stays with the\n * credential, so a wrong or spoofed answer changes a label and nothing else.\n *\n * The signals follow `@vercel/detect-agent` (Apache-2.0), kept as a small\n * synchronous table because every push reads it: the cross-tool `AI_AGENT`\n * convention first, then each host's own marker. Returns a lowercase slug,\n * or null when nothing says an agent is here.\n */\nexport type Env = Record<string, string | undefined>\n\nconst SLUG = /^[a-z0-9][a-z0-9.-]{0,39}$/\n\n/** The markers each host sets in the shells it runs, in precedence order. */\nconst MARKERS: Array<[slug: string, test: (env: Env) => boolean]> = [\n ['cursor', (e) => !!e.CURSOR_TRACE_ID],\n [\n 'cursor-cli',\n (e) => !!e.CURSOR_AGENT || e.CURSOR_EXTENSION_HOST_ROLE === 'agent-exec',\n ],\n ['gemini-cli', (e) => !!e.GEMINI_CLI],\n ['codex', (e) => !!(e.CODEX_SANDBOX || e.CODEX_CI || e.CODEX_THREAD_ID)],\n ['antigravity', (e) => !!e.ANTIGRAVITY_AGENT],\n ['augment-cli', (e) => !!e.AUGMENT_AGENT],\n ['opencode', (e) => !!e.OPENCODE_CLIENT],\n [\n 'claude-code',\n (e) => !!(e.CLAUDECODE || e.CLAUDE_CODE) && !e.CLAUDE_CODE_IS_COWORK,\n ],\n ['claude-cowork', (e) => !!(e.CLAUDECODE || e.CLAUDE_CODE)],\n ['replit', (e) => !!e.REPL_ID],\n [\n 'github-copilot',\n (e) => !!(e.COPILOT_MODEL || e.COPILOT_ALLOW_ALL || e.COPILOT_GITHUB_TOKEN),\n ],\n]\n\nconst DEVIN_LOCAL_PATH = '/opt/.devin'\n\n/**\n * `AI_AGENT` is free text by convention (`claude-code_2-1-284_agent`,\n * `github-copilot`, `v0`): the tool's name is its leading run of\n * `[a-z0-9.-]`, lowercased, so a version or a role suffix never reaches the\n * label and nothing but a short slug ever leaves the machine.\n */\nexport function slugFromAiAgent(value: string): string | null {\n const head = value.trim().toLowerCase().split('_')[0] ?? ''\n if (head === 'github-copilot-cli') return 'github-copilot'\n return SLUG.test(head) ? head : null\n}\n\nexport function detectHostAgent(\n env: Env = process.env,\n exists: (path: string) => boolean = existsSync,\n): string | null {\n if (env.AI_AGENT) {\n const slug = slugFromAiAgent(env.AI_AGENT)\n if (slug) return slug\n }\n for (const [slug, test] of MARKERS) if (test(env)) return slug\n try {\n if (exists(DEVIN_LOCAL_PATH)) return 'devin'\n } catch {\n // An unreadable path is not an agent.\n }\n return null\n}\n"],"mappings":";;;AAoBA,IAAM,QAAQ,oBAAI,IAAY;AAC9B,IAAM,OAAO,oBAAI,IAAY;AAGtB,SAAS,WAA8C,KAAW;AACvE,SAAO,IAAI,MAAM,KAAK;AAAA,IACpB,IAAI,QAAQ,KAAK,UAAU;AACzB,UAAI,OAAO,QAAQ,SAAU,MAAK,IAAI,GAAG;AACzC,aAAO,QAAQ,IAAI,QAAQ,KAAK,QAAQ;AAAA,IAC1C;AAAA,IACA,IAAI,QAAQ,KAAK;AACf,UAAI,OAAO,QAAQ,SAAU,MAAK,IAAI,GAAG;AACzC,aAAO,QAAQ,IAAI,QAAQ,GAAG;AAAA,IAChC;AAAA;AAAA,IAEA,yBAAyB,QAAQ,KAAK;AACpC,UAAI,OAAO,QAAQ,SAAU,MAAK,IAAI,GAAG;AACzC,aAAO,QAAQ,yBAAyB,QAAQ,GAAG;AAAA,IACrD;AAAA;AAAA;AAAA,IAGA,QAAQ,QAAQ;AACd,iBAAW,OAAO,QAAQ,QAAQ,MAAM;AACtC,YAAI,OAAO,QAAQ,SAAU,MAAK,IAAI,GAAG;AAC3C,aAAO,QAAQ,QAAQ,MAAM;AAAA,IAC/B;AAAA,EACF,CAAC;AACH;AAEO,SAAS,cAAc,MAAoB;AAChD,QAAM,IAAI,IAAI;AAChB;AAGO,SAAS,cAAwB;AACtC,SAAO,CAAC,GAAG,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,KAAK,SAAS,MAAM;AACvE;AAWO,SAAS,aAAa,MAAc,MAAwB;AACjE,QAAM,MAAM,oBAAI,IAAY;AAC5B,MAAI,SAAS;AACb,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,UAAM,OAAO,oBAAoB,KAAK,IAAI;AAC1C,QAAI,KAAM,UAAS,KAAK,CAAC,MAAM;AAAA,aACtB,CAAC,YAAY,KAAK,IAAI,EAAG,UAAS;AAC3C,QAAI,CAAC,OAAQ;AACb,eAAW,KAAK,KAAK,SAAS,sBAAsB,EAAG,KAAI,IAAI,EAAE,CAAC,CAAC;AAAA,EACrE;AACA,SAAO,CAAC,GAAG,GAAG;AAChB;AAEA,SAAS,aAAa,GAAW,GAAmB;AAClD,QAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC;AAC5D,WAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK;AAClC,QAAI,OAAO,IAAI,CAAC;AAChB,QAAI,CAAC,IAAI;AACT,aAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK;AAClC,YAAM,OAAO,IAAI,CAAC;AAClB,UAAI,CAAC,IAAI,KAAK;AAAA,QACZ,IAAI,CAAC,IAAI;AAAA,QACT,IAAI,IAAI,CAAC,IAAI;AAAA,QACb,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,IAAI;AAAA,MACtC;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO,IAAI,EAAE,MAAM;AACrB;AAGO,SAAS,YAAY,MAAc,YAAqC;AAC7E,MAAI,OAAsB;AAC1B,MAAI,YAAY;AAChB,aAAW,KAAK,YAAY;AAC1B,QAAI,MAAM,KAAM;AAGhB,UAAM,IAAI,EAAE,SAAS,IAAI,KAAK,KAAK,SAAS,CAAC,IAAI,IAAI,aAAa,MAAM,CAAC;AACzE,QAAI,IAAI,WAAW;AACjB,kBAAY;AACZ,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO,SAAS,QAAQ,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,SAAS,CAAC,CAAC,IACxE,OACA;AACN;AAGO,SAAS,mBACd,MACA,YACe;AACf,QAAM,SAAS,YAAY;AAC3B,MAAI,CAAC,OAAO,OAAQ,QAAO;AAC3B,QAAM,QAAQ,OAAO,IAAI,CAAC,SAAS;AACjC,UAAM,OAAO,YAAY,MAAM,UAAU;AACzC,WAAO,KAAK,IAAI,GAAG,OAAO,oBAAoB,IAAI,OAAO,EAAE;AAAA,EAC7D,CAAC;AACD,QAAM,OAAO,WAAW,SACpB,cAAc,WAAW,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,EAAE,KAAK,GAAG,CAAC,MACvD;AACJ,SAAO,OAAO,IAAI,qBAAqB,MAAM,KAAK,IAAI,CAAC,KAAK,OAAO,SAAS,IAAI,aAAa,OAAO,6EAA6E,OAAO,SAAS,IAAI,SAAS,IAAI,IAAI,IAAI;AAC5N;;;ACxHO,IAAM,aAAN,cAAyB,MAAM;AAAC;AAEhC,SAAS,UACd,MACA,cACY;AACZ,QAAM,cAAwB,CAAC;AAC/B,QAAM,QAAuC,CAAC;AAC9C,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAClB,QAAI,QAAQ,MAAM;AAChB,kBAAY,KAAK,GAAG,KAAK,MAAM,IAAI,CAAC,CAAC;AACrC;AAAA,IACF;AACA,QAAI,IAAI,WAAW,IAAI,GAAG;AACxB,YAAM,KAAK,IAAI,QAAQ,GAAG;AAC1B,UAAI,OAAO,IAAI;AACb,cAAM,IAAI,MAAM,GAAG,EAAE,CAAC,IAAI,IAAI,MAAM,KAAK,CAAC;AAC1C;AAAA,MACF;AACA,YAAM,OAAO,IAAI,MAAM,CAAC;AACxB,UAAI,aAAa,IAAI,IAAI,GAAG;AAC1B,cAAM,IAAI,IAAI;AACd;AAAA,MACF;AACA,YAAM,OAAO,KAAK,IAAI,CAAC;AACvB,UAAI,SAAS,UAAa,KAAK,WAAW,IAAI,GAAG;AAC/C,cAAM,IAAI,WAAW,KAAK,IAAI,kBAAkB;AAAA,MAClD;AACA,YAAM,IAAI,IAAI;AACd;AACA;AAAA,IACF;AACA,gBAAY,KAAK,GAAG;AAAA,EACtB;AACA,aAAW,QAAQ,OAAO,KAAK,KAAK,EAAG,eAAc,IAAI;AACzD,SAAO,EAAE,aAAa,OAAO,WAAW,KAAK,EAAE;AACjD;AAEO,SAAS,QACd,OACA,MACA,UACQ;AACR,QAAM,IAAI,MAAM,IAAI;AACpB,MAAI,MAAM,OAAW,QAAO;AAC5B,QAAM,IAAI,OAAO,CAAC;AAClB,MAAI,CAAC,OAAO,SAAS,CAAC;AACpB,UAAM,IAAI,WAAW,KAAK,IAAI,2BAA2B,OAAO,CAAC,CAAC,GAAG;AACvE,SAAO;AACT;;;AC9DA,SAAS,gBAAgB;AACzB,SAAS,YAAY,cAAc,gBAAgB;AACnD,SAAS,YAAY;AACrB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAkBA,SAAS,cAAc,QAA+B;AAC3D,MAAI;AACF,QAAI,CAAC,SAAS,MAAM,EAAE,YAAY,EAAG,QAAO;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,UAAU,KAAK,QAAQ,UAAU;AACvC,MAAI,WAAW,OAAO,GAAG;AACvB,QAAI;AACF,YAAM,MAAe,KAAK,MAAM,aAAa,SAAS,MAAM,CAAC;AAC7D,UAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,YAAY;AACzD,eAAO;AAAA,IACX,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO,WAAW,KAAK,QAAQ,aAAa,CAAC,IAAI,YAAY;AAC/D;AAQA,eAAsB,qBACpB,KACA,UACkB;AAClB,QAAM,SAAkB,KAAK;AAAA,IAC3B,MAAM,SAAS,KAAK,KAAK,aAAa,GAAG,MAAM;AAAA,EACjD;AACA,QAAM,UAAU,KAAK,KAAK,UAAU;AACpC,MAAI,CAAC,WAAW,OAAO,EAAG,QAAO;AACjC,QAAM,MAAe,KAAK,MAAM,MAAM,SAAS,SAAS,MAAM,CAAC;AAC/D,MAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,YAAY,IAAK,QAAO;AACvE,MAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,UAAM,IAAI,WAAW,GAAG,GAAG,6CAA6C;AAAA,EAC1E;AACA,QAAM,MAAM;AACZ,QAAM,UACJ,IAAI,WAAW,OAAO,IAAI,YAAY,WACjC,IAAI,UACL,CAAC;AACP,QAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,oBAAoB;AAC7D,QAAM,WAAW;AAAA,IACf,EAAE,GAAG,KAAK,SAAS,EAAE,GAAG,SAAS,OAAO,EAAE;AAAA,IAC1C,EAAE,MAAM,KAAK;AAAA,EACf,EAAE;AACF,WAAS;AAAA,IACP;AAAA,EACF;AACA,SAAO;AACT;AAOA,eAAsB,eAAe,QAAiC;AACpE,MAAI,eAAe,KAAK,MAAM,GAAG;AAC/B,UAAM,MAAM,MAAM,MAAM,MAAM;AAC9B,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,SAAS,MAAM,WAAM,IAAI,MAAM,EAAE;AAC9D,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB;AACA,MAAI,QAAQ;AACZ,MAAI;AACF,YAAQ,SAAS,MAAM,EAAE,YAAY;AAAA,EACvC,QAAQ;AAAA,EAER;AACA,MAAI,OAAO;AACT,UAAM,IAAI;AAAA,MACR,GAAG,MAAM;AAAA,IACX;AAAA,EACF;AACA,SAAO,MAAM,SAAS,QAAQ,MAAM;AACtC;AASA,eAAsB,cAAc,QAAuC;AACzE,QAAM,WAAqB,CAAC;AAC5B,MAAI;AACJ,QAAM,OAAO,eAAe,KAAK,MAAM,IAAI,SAAS,cAAc,MAAM;AACxE,MAAI,SAAS,QAAQ;AACnB,UAAM,IAAI;AAAA,MACR,GAAG,MAAM,sGAAsG,MAAM,sBAAsB,MAAM;AAAA,IACnJ;AAAA,EACF;AACA,MAAI,SAAS,WAAW;AACtB,aAAS,MAAM,qBAAqB,QAAQ,QAAQ;AACpD,WAAO,OAAO,QAAQ,QAAQ,QAAQ;AAAA,EACxC;AACA,QAAM,MAAM,MAAM,eAAe,MAAM;AAEvC,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,SAAS,GAAG;AACV,UAAM,IAAI,WAAW,GAAG,MAAM,uBAAwB,EAAY,OAAO,EAAE;AAAA,EAC7E;AACA,SAAO,OAAO,QAAQ,QAAQ,QAAQ;AACxC;AAEA,SAAS,OACP,QACA,QACA,UACc;AACd,MAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,UAAM,IAAI,WAAW,GAAG,MAAM,iCAAiC;AAAA,EACjE;AAGA,MAAI,MAAM;AACV,MACE,OAAO,IAAI,WAAW,YACtB,IAAI,WAAW,QACf,EAAE,oBAAoB,MACtB;AACA,UAAM,IAAI;AACV,aAAS,KAAK,+BAA+B;AAAA,EAC/C;AAEA,QAAM,UAAU,IAAI;AACpB,QAAM,WAAW,cAAc,GAAG;AAClC,MAAI,YAAY,QAAW;AAEzB,aAAS;AAAA,MACP,gDAAgD,sBAAsB;AAAA,IACxE;AAAA,EACF,WAAW,YAAY,wBAAwB;AAC7C,aAAS;AAAA,MACP,oBAAoB,OAAO,OAAO,CAAC,QAAQ,sBAAsB;AAAA,IACnE;AAAA,EACF;AAEA,QAAM,QAAQ,oBAAoB,UAAU,QAAQ;AACpD,MAAI,CAAC,MAAM,SAAS;AAClB,UAAM,SAAS,MAAM,MAAM,OACxB,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,MAAM,KAAK,EAAE,KAAK,KAAK,GAAG,KAAK,QAAQ,KAAK,EAAE,OAAO,EAAE,EAC5D,KAAK,IAAI;AACZ,UAAM,IAAI,WAAW;AAAA,EAAwB,MAAM,EAAE;AAAA,EACvD;AAEA,SAAO,EAAE,QAAQ,UAAU,SAAS;AACtC;AAGO,SAAS,eACd,QACoB;AACpB,QAAM,IAAI,OAAO;AACjB,SAAO,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,KAAK,IAAI,IAAI,IAAI;AACpE;;;AC1LA,SAAS,gBAAgB;AAGlB,IAAM,0BAAN,cAAsC,MAAM;AAAA,EACjD,YAAY,OAAe;AACzB;AAAA,MACE,+CAA+C,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,IAKtD;AAAA,EACF;AACF;AAWA,eAAsB,cAAc,OAAiB,CAAC,GAAqB;AACzE,QAAM,OAAO,KAAK,SAAS,EAAE,KAAK,IAAI,CAAC;AACvC,QAAM,WAAW,QAAQ,IAAI;AAC7B,MAAI,UAAU;AACZ,WAAO,SAAS,OAAO,EAAE,GAAG,MAAM,gBAAgB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM;AACzE,YAAM,IAAI;AAAA,QACR,4BAA6B,EAAY,OAAO;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,EACH;AACA,MAAI;AACF,WAAO,MAAM,SAAS,OAAO,EAAE,GAAG,MAAM,SAAS,SAAS,CAAC;AAAA,EAC7D,QAAQ;AACN,QAAI;AACF,aAAO,MAAM,SAAS,OAAO,IAAI;AAAA,IACnC,SAAS,GAAG;AACV,YAAM,IAAI;AAAA,QACP,EAAY,QAAQ,MAAM,IAAI,EAAE,CAAC,KAAK;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AACF;;;AC5BA,IAAM,eAAe;AAGrB,IAAM,qBAA6C;AAAA,EACjD,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAGA,IAAM,oBAA4C;AAAA,EAChD,cAAc;AAAA,EACd,aAAa;AAAA,EACb,aAAa;AACf;AAOO,IAAM,wBAAwB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,IAAM,mBAAmB;AAMhC,IAAM,aAAqC;AAAA,EACzC,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AACR;AAGA,IAAM,aAAqC;AAAA,EACzC,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AACV;AAGO,SAAS,eAAe,MAAiC;AAC9D,QAAM,QAAQ,CAAC,IAAY,QACzB,KAAK,UAAU,KAAK,MAChB,OAAO,aAAa,GAAG,KAAK,SAAS,IAAI,KAAK,GAAG,CAAC,IAClD;AACN,QAAM,aAAa,IAAI,UACrB,MAAM,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC;AAKrC,MAAI,WAAW,IAAM,IAAM,KAAM,GAAI,EAAG,QAAO;AAC/C,MAAI,MAAM,GAAG,CAAC,MAAM,OAAQ,QAAO,WAAW,MAAM,GAAG,CAAC,CAAC,KAAK;AAC9D,MAAI,MAAM,GAAG,CAAC,MAAM,OAAQ,QAAO,WAAW,MAAM,GAAG,CAAC,CAAC,KAAK;AAC9D,MAAI,WAAW,KAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,EAAI;AAC3D,WAAO;AACT,MAAI,WAAW,KAAM,KAAM,GAAI,EAAG,QAAO;AACzC,MAAI,MAAM,GAAG,CAAC,MAAM,OAAQ,QAAO;AACnC,MAAI,MAAM,GAAG,CAAC,MAAM,OAAQ,QAAO;AACnC,MAAI,MAAM,GAAG,CAAC,MAAM,MAAO,QAAO;AAClC,SAAO;AACT;AAGA,SAAS,SAAS,OAA0C;AAC1D,QAAM,QAAQ,SAAS,IAAI,MAAM,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY;AAE5D,SAAO,CAAC,QAAQ,SAAS,gBAAgB,SAAS,wBAC9C,KACA;AACN;AAGO,SAAS,iBAAiB,MAAsB;AACrD,QAAM,OAAO,kBAAkB,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,YAAY;AAClE,SAAO,mBAAmB,GAAG,KAAK;AACpC;AAGO,SAAS,aAAa,aAA6B;AACxD,QAAM,QAAQ,eAAe,IAAI,MAAM,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY;AAClE,MAAI,kBAAkB,IAAI,EAAG,QAAO,kBAAkB,IAAI;AAC1D,QAAM,MAAM,OAAO,KAAK,kBAAkB,EAAE;AAAA,IAC1C,CAAC,MAAM,mBAAmB,CAAC,MAAM;AAAA,EACnC;AACA,SAAO,MAAM,IAAI,GAAG,KAAK;AAC3B;AAkBO,SAAS,iBAAiB,QAA4C;AAC3E,QAAM,UAAU,OAAO,MAAM,SAAS,eAAe,OAAO,IAAI,IAAI;AACpE,MAAI,QAAS,QAAO,EAAE,MAAM,SAAS,MAAM,QAAQ;AACnD,QAAM,WAAW,SAAS,OAAO,QAAQ;AACzC,MAAI,SAAU,QAAO,EAAE,MAAM,UAAU,MAAM,WAAW;AACxD,QAAM,QAAQ,iBAAiB,OAAO,YAAY,EAAE;AACpD,MAAI,UAAU,aAAc,QAAO,EAAE,MAAM,OAAO,MAAM,WAAW;AACnE,SAAO,EAAE,MAAM,cAAc,MAAM,UAAU;AAC/C;AAOO,SAAS,YAAY,UAAkB,MAAsB;AAClE,QAAM,MAAM,aAAa,IAAI;AAC7B,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,UAAU,gBAAgB,KAAK,QAAQ,IAAI,CAAC,KAAK;AACvD,MAAI,WAAW,iBAAiB,IAAI,OAAO,EAAE,MAAM,KAAM,QAAO;AAChE,SAAO,GAAG,SAAS,MAAM,GAAG,SAAS,SAAS,QAAQ,MAAM,CAAC,GAAG,GAAG;AACrE;AAWO,SAAS,kBACd,UACA,MACe;AACf,QAAM,SAAS,eAAe,IAAI;AAClC,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,iBAAiB,QAAQ;AACzC,MAAI,YAAY,OAAQ,QAAO;AAC/B,SAAO,GAAG,QAAQ,OAAO,MAAM,aAAa,OAAO,wCAAmC,YAAY,UAAU,MAAM,CAAC;AACrH;;;AC9KA,SAAS,MAAM,MAAkC;AAC/C,SAAO,kBAAkB,KAAK,QAAQ,EAAE,IAAI,CAAC,GAAG,YAAY,KAAK;AACnE;AAGO,SAAS,YACd,MACA,KACa;AACb,QAAM,QAAQ,MAAM,GAAG;AACvB,QAAM,WACJ,UAAU,QAAQ,QAAQ,UAAU,SAAS,SAAS;AACxD,MAAI,SAAS,QAAW;AACtB,QAAI,SAAS,UAAU,SAAS;AAC9B,YAAM,IAAI,WAAW,8BAA8B;AACrD,QAAI,YAAY,aAAa;AAC3B,YAAM,IAAI;AAAA,QACR,YAAY,IAAI,gCAAgC,GAAG,cAAc,IAAI;AAAA,MACvE;AACF,WAAO;AAAA,EACT;AACA,SAAO,YAAY;AACrB;AAGO,SAAS,YAAY,KAA0B;AACpD,QAAM,MAAM,MAAM,GAAG;AACrB,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,SAAS,QAAQ,OAAQ,QAAO;AAC5C,MAAI,QAAQ,UAAU,QAAQ,GAAI,QAAO;AACzC,QAAM,IAAI;AAAA,IACR,8CAA8C,GAAG,YAAY,GAAG;AAAA,EAClE;AACF;AAMO,SAAS,WAAW,KAAa,UAA4B;AAClE,QAAM,QAAQ,IACX,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO,EACd,IAAI,CAAC,MAAM;AACV,UAAM,MAAM,qBAAqB,KAAK,CAAC;AACvC,UAAM,IAAI,MAAO,OAAO,IAAI,CAAC,CAAC,IAAI,MAAO,WAAW,OAAO,CAAC;AAC5D,QAAI,CAAC,OAAO,SAAS,CAAC,KAAK,IAAI;AAC7B,YAAM,IAAI;AAAA,QACR,4DAA4D,CAAC;AAAA,MAC/D;AACF,WAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,WAAW,IAAI,EAAE,CAAC;AAAA,EACnD,CAAC;AACH,MAAI,CAAC,MAAM,OAAQ,OAAM,IAAI,WAAW,iCAAiC;AACzE,SAAO;AACT;AAGO,SAAS,YAAY,KAAa,MAAc,MAAuB;AAC5E,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,IAAI,yBAAyB,KAAK,GAAG;AAC3C,SAAO,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE;AAC5D;AAGO,SAAS,SAAS,MAA0B;AACjD,QAAM,MAAgB,CAAC;AACvB,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,IAAI,KAAK,CAAC;AAChB,QAAI,MAAM,WAAW,KAAK,IAAI,CAAC,MAAM,OAAW,KAAI,KAAK,KAAK,EAAE,CAAC,CAAC;AAAA,aACzD,EAAE,WAAW,QAAQ,EAAG,KAAI,KAAK,EAAE,MAAM,SAAS,MAAM,CAAC;AAAA,EACpE;AACA,SAAO;AACT;AAOO,SAAS,cACd,QACA,MACyB;AACzB,MAAI,CAAC,KAAK,OAAQ,QAAO;AACzB,QAAM,OAAgC;AAAA,IACpC,GAAK,OAAO,QAAgD,CAAC;AAAA,EAC/D;AACA,aAAW,OAAO,MAAM;AACtB,UAAM,KAAK,IAAI,QAAQ,GAAG;AAC1B,UAAM,OAAO,OAAO,KAAK,MAAM,IAAI,MAAM,GAAG,EAAE;AAC9C,UAAM,IAAI,4BAA4B,KAAK,IAAI;AAC/C,QAAI,OAAO,MAAM,CAAC;AAChB,YAAM,IAAI;AAAA,QACR,sEAAsE,GAAG;AAAA,MAC3E;AACF,UAAM,MAAM,IAAI,MAAM,KAAK,CAAC;AAC5B,QAAI,QAAiB;AACrB,QAAI;AACF,cAAQ,KAAK,MAAM,GAAG;AAAA,IACxB,QAAQ;AAAA,IAER;AACA,SAAK,EAAE,CAAC,CAAC,IAAI;AAAA,EACf;AACA,SAAO,EAAE,GAAG,QAAQ,KAAK;AAC3B;;;ACxGO,SAASA,WACd,MACA,cACA,aAAkC,oBAAI,IAAI,GAC9B;AACZ,QAAM,cAAwB,CAAC;AAC/B,QAAM,QAAuC,CAAC;AAC9C,QAAM,QAAkC,CAAC;AACzC,QAAM,MAAM,CAAC,MAAc,UAAwB;AACjD,QAAI,WAAW,IAAI,IAAI,EAAG,EAAC,MAAM,IAAI,MAAM,CAAC,GAAG,KAAK,KAAK;AAAA,QACpD,OAAM,IAAI,IAAI;AAAA,EACrB;AACA,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAClB,QAAI,QAAQ,MAAM;AAChB,kBAAY,KAAK,GAAG,KAAK,MAAM,IAAI,CAAC,CAAC;AACrC;AAAA,IACF;AACA,QAAI,IAAI,WAAW,IAAI,GAAG;AACxB,YAAM,KAAK,IAAI,QAAQ,GAAG;AAC1B,UAAI,OAAO,IAAI;AACb,YAAI,IAAI,MAAM,GAAG,EAAE,GAAG,IAAI,MAAM,KAAK,CAAC,CAAC;AACvC;AAAA,MACF;AACA,YAAM,OAAO,IAAI,MAAM,CAAC;AACxB,UAAI,aAAa,IAAI,IAAI,GAAG;AAC1B,cAAM,IAAI,IAAI;AACd;AAAA,MACF;AACA,UAAI,IAAI,KAAK,KAAK,UAAU,KAAK,IAAI,CAAC,EAAE,WAAW,IAAI,GAAG;AACxD,cAAM,IAAI,WAAW,KAAK,IAAI,kBAAkB;AAAA,MAClD;AACA,UAAI,MAAM,KAAK,IAAI,CAAC,CAAC;AACrB;AACA;AAAA,IACF;AACA,gBAAY,KAAK,GAAG;AAAA,EACtB;AACA,aAAW,QAAQ,OAAO,KAAK,KAAK,EAAG,eAAc,IAAI;AACzD,aAAW,QAAQ,OAAO,KAAK,KAAK,EAAG,eAAc,IAAI;AACzD,SAAO,EAAE,aAAa,OAAO,WAAW,KAAK,GAAG,OAAO,WAAW,KAAK,EAAE;AAC3E;AAEO,SAASC,SACd,OACA,MACA,UACQ;AACR,QAAM,IAAI,MAAM,IAAI;AACpB,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,QAAM,IAAI,OAAO,CAAC;AAClB,MAAI,CAAC,OAAO,SAAS,CAAC;AACpB,UAAM,IAAI,WAAW,KAAK,IAAI,2BAA2B,CAAC,GAAG;AAC/D,SAAO;AACT;AAGO,SAAS,QACd,OACA,MACoB;AACpB,QAAM,IAAI,MAAM,IAAI;AACpB,SAAO,OAAO,MAAM,WAAW,IAAI;AACrC;AAEO,SAAS,QAAQ,OAA4B,MAAuB;AACzE,SAAO,OAAO,UAAU,eAAe,KAAK,OAAO,IAAI;AACzD;;;ACpEA,SAAS,WAAW,gBAAAC,eAAc,qBAAqB;AACvD,SAAS,eAAe;AACxB,SAAS,QAAAC,aAAY;;;AChBrB,SAAS,cAAAC,mBAAkB;AAe3B,IAAM,OAAO;AAGb,IAAM,UAA8D;AAAA,EAClE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,eAAe;AAAA,EACrC;AAAA,IACE;AAAA,IACA,CAAC,MAAM,CAAC,CAAC,EAAE,gBAAgB,EAAE,+BAA+B;AAAA,EAC9D;AAAA,EACA,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU;AAAA,EACpC,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,iBAAiB,EAAE,YAAY,EAAE,gBAAgB;AAAA,EACvE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,iBAAiB;AAAA,EAC5C,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,aAAa;AAAA,EACxC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,eAAe;AAAA,EACvC;AAAA,IACE;AAAA,IACA,CAAC,MAAM,CAAC,EAAE,EAAE,cAAc,EAAE,gBAAgB,CAAC,EAAE;AAAA,EACjD;AAAA,EACA,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,EAAE,cAAc,EAAE,YAAY;AAAA,EAC1D,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO;AAAA,EAC7B;AAAA,IACE;AAAA,IACA,CAAC,MAAM,CAAC,EAAE,EAAE,iBAAiB,EAAE,qBAAqB,EAAE;AAAA,EACxD;AACF;AAEA,IAAM,mBAAmB;AAQlB,SAAS,gBAAgB,OAA8B;AAC5D,QAAM,OAAO,MAAM,KAAK,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,KAAK;AACzD,MAAI,SAAS,qBAAsB,QAAO;AAC1C,SAAO,KAAK,KAAK,IAAI,IAAI,OAAO;AAClC;AAEO,SAAS,gBACd,MAAW,QAAQ,KACnB,SAAoCA,aACrB;AACf,MAAI,IAAI,UAAU;AAChB,UAAM,OAAO,gBAAgB,IAAI,QAAQ;AACzC,QAAI,KAAM,QAAO;AAAA,EACnB;AACA,aAAW,CAAC,MAAM,IAAI,KAAK,QAAS,KAAI,KAAK,GAAG,EAAG,QAAO;AAC1D,MAAI;AACF,QAAI,OAAO,gBAAgB,EAAG,QAAO;AAAA,EACvC,QAAQ;AAAA,EAER;AACA,SAAO;AACT;;;AD5CO,SAAS,eACd,QAA2C,CAAC,GACpC;AACR,QAAM,YAAY,QAAQ,IAAI,cAAc,KAAK;AACjD,QAAM,MACJ,MAAM,UACN,MAAM,OACN,QAAQ,IAAI,YAAY,KAAK,KAC7B,aACA;AACF,MAAI,CAAC,MAAM,UAAU,CAAC,MAAM,OAAO,CAAC,QAAQ,IAAI,cAAc,WAAW;AACvE,YAAQ,OAAO;AAAA,MACb;AAAA,IACF;AAAA,EACF;AACA,SAAO,IAAI,QAAQ,QAAQ,EAAE,EAAE,QAAQ,UAAU,EAAE;AACrD;AAMO,SAAS,WAAW,OAAuB;AAChD,MAAI,CAAC,eAAe,KAAK,KAAK,GAAG;AAC/B,QAAI,mBAAmB,KAAK,KAAK,EAAG,QAAO;AAC3C,UAAM,IAAI,WAAW,IAAI,KAAK,0BAA0B;AAAA,EAC1D;AACA,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,KAAK;AAAA,EACrB,QAAQ;AACN,UAAM,IAAI,WAAW,IAAI,KAAK,sBAAsB;AAAA,EACtD;AACA,QAAM,QAAQ,IAAI,aAAa,IAAI,KAAK;AACxC,MAAI,MAAO,QAAO;AAClB,QAAM,OAAO,IAAI,SAAS,MAAM,yBAAyB;AACzD,MAAI,KAAM,QAAO,KAAK,CAAC;AACvB,QAAM,IAAI;AAAA,IACR,8BAA8B,KAAK;AAAA,EACrC;AACF;AAEO,SAAS,WAAW,OAAuB;AAChD,SACE,MACG,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE,EACtB,MAAM,GAAG,EAAE,EACX,QAAQ,OAAO,EAAE,KAAK;AAE7B;AAEO,IAAM,mBAAmBC,MAAK,QAAQ,GAAG,WAAW,OAAO,aAAa;AAOxE,SAAS,kBAAkB,UAAkC;AAClE,QAAM,OAAO,UAAU,KAAK;AAC5B,MAAI,KAAM,QAAO;AACjB,QAAM,MAAM,QAAQ,IAAI,aAAa,KAAK;AAC1C,MAAI,IAAK,QAAO;AAChB,MAAI;AACF,UAAM,QAAQC,cAAa,kBAAkB,MAAM,EAAE,MAAM,IAAI,EAAE,CAAC,EAAE,KAAK;AACzE,WAAO,SAAS;AAAA,EAClB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,kBAAkB,UAA2B;AAC3D,QAAM,MAAM,kBAAkB,QAAQ;AACtC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,gBAAgB,KAAqB;AACnD,YAAUD,MAAK,QAAQ,GAAG,WAAW,KAAK,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC7E,gBAAc,kBAAkB,GAAG,IAAI,KAAK,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAClE,SAAO;AACT;AAUO,SAAS,SACd,MAAW,QAAQ,KACnB,QACQ;AACR,QAAM,WAAW,IAAI,YAAY,KAAK;AACtC,MAAI,YAAY,SAAS,UAAU,MAAM,CAAC,SAAS,KAAK,QAAQ;AAC9D,WAAO;AACT,QAAM,OAAO,gBAAgB,KAAK,MAAM;AACxC,SAAO,OAAO,GAAG,IAAI,aAAa;AACpC;AAOA,eAAsB,QACpB,QACA,MACA,OAOI,CAAC,GACe;AACpB,QAAM,UAAkC;AAAA,IACtC,QAAQ;AAAA,IACR,GAAG,KAAK;AAAA,EACV;AACA,MAAI,KAAK,IAAK,SAAQ,gBAAgB,UAAU,KAAK,GAAG;AACxD,MAAI,KAAK,SAAS,OAAW,SAAQ,cAAc,IAAI;AACvD,QAAM,UACJ,KAAK,QAAQ,SACR,KAAK,MACN,KAAK,SAAS,SACZ,SACA,KAAK,UAAU,KAAK,IAAI;AAChC,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,IAAI;AAAA,IAC1C,QAAQ,KAAK,UAAU;AAAA,IACvB;AAAA,IACA,MAAM;AAAA,EACR,CAAC;AACD,MAAI,OAAgC,CAAC;AACrC,MAAI;AACF,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB,QAAQ;AAAA,EAER;AACA,SAAO,EAAE,QAAQ,IAAI,QAAQ,KAAK;AACpC;AAGO,SAAS,SAAS,MAAc,GAAsB;AAC3D,QAAM,SACJ,OAAO,EAAE,KAAK,UAAU,WACpB,EAAE,KAAK,QACP,EAAE,KAAK,YAAY,SACjB,KAAK,UAAU,EAAE,KAAK,OAAO,IAC7B;AACR,QAAM,OACJ,EAAE,WAAW,MACT,uFACA;AACN,SAAO,GAAG,IAAI,WAAM,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,KAAK,EAAE,GAAG,IAAI;AACnE;AAQO,IAAM,kBAAkB;AAY/B,SAAS,aAAa,MAA8C;AAClE,MAAI;AACF,WAAO,KAAK,MAAMC,cAAa,MAAM,MAAM,CAAC;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOO,SAAS,cAAc,KAA+B;AAC3D,QAAM,MAAM,aAAaD,MAAK,KAAK,eAAe,CAAC;AACnD,MAAI,OAAO,OAAO,IAAI,UAAU,UAAU;AACxC,WAAO;AAAA,MACL,OAAO,IAAI;AAAA,MACX,WAAW,OAAO,IAAI,cAAc,WAAW,IAAI,YAAY;AAAA,MAC/D,GAAI,OAAO,IAAI,aAAa,WAAW,EAAE,UAAU,IAAI,SAAS,IAAI,CAAC;AAAA,MACrE,GAAI,OAAO,IAAI,UAAU,WAAW,EAAE,OAAO,IAAI,MAAM,IAAI,CAAC;AAAA,MAC5D,GAAI,OAAO,IAAI,SAAS,WAAW,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;AAAA,MACzD,GAAI,OAAO,IAAI,cAAc,WACzB,EAAE,WAAW,IAAI,UAAU,IAC3B,CAAC;AAAA,IACP;AAAA,EACF;AACA,QAAM,OAAO,aAAaA,MAAK,KAAK,WAAW,CAAC;AAChD,MAAI,QAAQ,OAAO,KAAK,UAAU,UAAU;AAC1C,WAAO;AAAA,MACL,OAAO,KAAK;AAAA,MACZ,WAAW,OAAO,KAAK,cAAc,WAAW,KAAK,YAAY;AAAA,MACjE,GAAI,OAAO,KAAK,aAAa,WAAW,EAAE,UAAU,KAAK,SAAS,IAAI,CAAC;AAAA,IACzE;AAAA,EACF;AACA,QAAM,OAAO,aAAaA,MAAK,KAAK,WAAW,CAAC;AAChD,MAAI,QAAQ,OAAO,KAAK,OAAO,UAAU;AACvC,WAAO;AAAA,MACL,OAAO,KAAK;AAAA,MACZ,WACE,OAAO,KAAK,qBAAqB,WAC7B,KAAK,mBACL;AAAA,MACN,GAAI,OAAO,KAAK,UAAU,WAAW,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,MAC9D,GAAI,OAAO,KAAK,SAAS,WAAW,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,MAC3D,GAAI,OAAO,KAAK,cAAc,WAC1B,EAAE,WAAW,KAAK,UAAU,IAC5B,CAAC;AAAA,IACP;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,eACd,KACA,OACW;AACX,QAAM,SAAS;AAAA,IACb,GAAI,cAAc,GAAG,KAAK,EAAE,WAAW,KAAK;AAAA,IAC5C,GAAG;AAAA,EACL;AACA;AAAA,IACEA,MAAK,KAAK,eAAe;AAAA,IACzB,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA;AAAA,EACpC;AACA,SAAO;AACT;AAgBO,SAAS,cAAc,SAA6C;AACzE,QAAM,QAAkB,CAAC;AACzB,aAAW,MAAM,SAAS;AACxB,UAAM,QAAQ,GAAG,QAAQ,SAAM,GAAG,KAAK,KAAK;AAC5C,UAAM,MAAM,GAAG,WAAW,WAAW,UAAW,GAAG,UAAU;AAC7D,UAAM;AAAA,MACJ,IAAI,OAAO,GAAG,iBAAiB,GAAG,CAAC,KAAK,GAAG,GAAG,KAAK,MAAM,GAAG,WAAW,EAAE;AAAA,IAC3E;AACA,QAAI,GAAG,KAAM,OAAM,KAAK,aAAa,GAAG,IAAI,EAAE;AAAA,EAChD;AACA,SAAO;AACT;AAMO,SAAS,eACd,MACA,KACM;AACN,QAAM,WAAW,MAAM,QAAQ,KAAK,QAAQ,IAAI,KAAK,WAAW,CAAC;AACjE,aAAW,KAAK,SAAU,KAAI,OAAO,MAAM,SAAU,KAAI,WAAW,CAAC,EAAE;AACzE;","names":["parseArgs","numFlag","readFileSync","join","existsSync","join","readFileSync"]}
package/dist/cli.js CHANGED
@@ -28,7 +28,7 @@ import {
28
28
  stillOutFor,
29
29
  unusedFlagsMessage,
30
30
  videoFormat
31
- } from "./chunk-KSEU3YK5.js";
31
+ } from "./chunk-PJX3XE7G.js";
32
32
 
33
33
  // src/cli.ts
34
34
  import { writeFile } from "fs/promises";
@@ -77,6 +77,70 @@ import {
77
77
  lintVosFonts,
78
78
  lintVosPostprocessing
79
79
  } from "@vosjs/core/lint";
80
+
81
+ // src/plugin/knobs.ts
82
+ var MAX_PARAMS = 12;
83
+ var MAX_LOOKS = 8;
84
+ var KINDS = /* @__PURE__ */ new Set(["number", "color", "select", "toggle", "text", "font"]);
85
+ var str = (v, max, min = 0) => typeof v === "string" && v.length >= min && v.length <= max;
86
+ function knobProblem(p) {
87
+ if (!p || typeof p !== "object") return "not an object";
88
+ const k = p;
89
+ if (!str(k.key, 40, 1)) return "key must be 1 to 40 characters";
90
+ if (typeof k.kind !== "string" || !KINDS.has(k.kind))
91
+ return `kind must be one of ${[...KINDS].join(", ")}`;
92
+ if (k.label !== void 0 && !str(k.label, 60))
93
+ return "label: at most 60 characters";
94
+ if (k.hint !== void 0 && !str(k.hint, 140))
95
+ return "hint: at most 140 characters";
96
+ if (k.unit !== void 0 && !str(k.unit, 8))
97
+ return "unit: at most 8 characters";
98
+ if (k.group !== void 0 && !str(k.group, 24))
99
+ return "group: at most 24 characters";
100
+ if (k.options !== void 0) {
101
+ if (!Array.isArray(k.options) || k.options.length > 12)
102
+ return "options: at most 12";
103
+ if (k.options.some((o) => !str(o, 40, 1)))
104
+ return "options: each 1 to 40 characters";
105
+ }
106
+ const d = k.default;
107
+ if (!(typeof d === "number" || typeof d === "boolean" || typeof d === "string" && d.length <= 280))
108
+ return "default must be a number, a boolean or at most 280 characters";
109
+ return null;
110
+ }
111
+ function lookProblem(l) {
112
+ if (!l || typeof l !== "object") return "not an object";
113
+ const look = l;
114
+ if (!str(look.name, 24, 1)) return "name must be 1 to 24 characters";
115
+ if (!look.values || typeof look.values !== "object") return "values missing";
116
+ return null;
117
+ }
118
+ function knobWarnings(config) {
119
+ const out = [];
120
+ const sort = (raw, noun, field, max, problem) => {
121
+ if (raw === void 0) return;
122
+ if (!Array.isArray(raw)) {
123
+ out.push(`${noun === "knob" ? "params" : "presets"} is not a list`);
124
+ return;
125
+ }
126
+ let kept = 0;
127
+ raw.forEach((entry, i) => {
128
+ const id = entry && typeof entry[field] === "string" ? `${noun} "${String(entry[field])}"` : `${noun} #${i + 1}`;
129
+ const why = problem(entry);
130
+ if (why) out.push(`${id} would be dropped on vos.so (${why})`);
131
+ else if (kept >= max)
132
+ out.push(
133
+ `${id} would be dropped on vos.so: a vos keeps at most ${max} ${noun}s${noun === "knob" ? " (the rest can stay bound data)" : ""}`
134
+ );
135
+ else kept += 1;
136
+ });
137
+ };
138
+ sort(config.params, "knob", "key", MAX_PARAMS, knobProblem);
139
+ sort(config.presets, "look", "name", MAX_LOOKS, lookProblem);
140
+ return out;
141
+ }
142
+
143
+ // src/check.ts
80
144
  var PLATFORM_EXTRA_KEYS = /* @__PURE__ */ new Set(["params", "presets"]);
81
145
  function runCheck(parsed) {
82
146
  const issues = [];
@@ -151,6 +215,9 @@ function runCheck(parsed) {
151
215
  });
152
216
  }
153
217
  }
218
+ for (const message of knobWarnings(migrated)) {
219
+ issues.push({ level: "warn", source: "shape", message });
220
+ }
154
221
  const elements = Array.isArray(migrated.elements) ? migrated.elements : [];
155
222
  for (const el of elements) {
156
223
  if (!el || typeof el !== "object") continue;
@@ -642,7 +709,7 @@ async function cmdCheck(argv) {
642
709
  return result.ok ? EXIT_OK : EXIT_ERROR;
643
710
  }
644
711
  async function delegate(argv, viaAlias = false) {
645
- const { run } = await import("./run-ASYY2YTV.js");
712
+ const { run } = await import("./run-T5RKKGLE.js");
646
713
  if (viaAlias && argv[0]) {
647
714
  process.stderr.write(
648
715
  `note: "vos voila ${argv[0]}" is now "vos ${argv[0]}".
@@ -682,7 +749,7 @@ async function main() {
682
749
  const engine = HELP_ENGINE.split("\n").filter(
683
750
  (l) => l.startsWith(` vos ${cmd} `)
684
751
  );
685
- const { verbHelp } = await import("./run-ASYY2YTV.js");
752
+ const { verbHelp } = await import("./run-T5RKKGLE.js");
686
753
  const take = verbHelp(cmd);
687
754
  process.stdout.write(
688
755
  `${engine.join("\n")}
@@ -714,7 +781,7 @@ ${take.includes("no such verb") ? "" : take}`
714
781
  main().then(async (code) => {
715
782
  if (code === EXIT_OK) {
716
783
  const verb = process.argv[2] ?? "";
717
- const { HELP } = await import("./run-ASYY2YTV.js");
784
+ const { HELP } = await import("./run-T5RKKGLE.js");
718
785
  const documented = [
719
786
  ...helpFlagsFor(HELP_ENGINE, verb),
720
787
  ...helpFlagsFor(HELP, verb)