@vosjs/cli 0.59.1 → 0.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,42 +1,70 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  BrowserUnavailableError,
4
+ configDuration,
5
+ directoryKind,
6
+ launchBrowser,
7
+ loadVosConfig,
8
+ parseTimes,
9
+ reencodeStill,
10
+ renderStill,
11
+ videoFormat
12
+ } from "./chunk-OMDRILG7.js";
13
+ import {
14
+ PREV_DOC_NAME,
15
+ RECORDING_NAME,
16
+ docAudioRefs,
17
+ docLayerRefs,
18
+ docMediaRefs,
19
+ dropScreencastFrames,
20
+ ensureTakeDir,
21
+ hostedLiteralWarnings,
22
+ isTakeRelativeKey,
23
+ loadTake,
24
+ localManifestRefs,
25
+ prepareReRecord,
26
+ pullManifest,
27
+ pullMedia,
28
+ readJson,
29
+ readMediaHead,
30
+ sourceSidecarRefs,
31
+ takeMediaFile,
32
+ takePaths,
33
+ uploadAsset,
34
+ uploadDocRefs,
35
+ uploadManifest,
36
+ writeJson
37
+ } from "./chunk-PUXY32AP.js";
38
+ import {
4
39
  CREDENTIALS_PATH,
5
40
  MEDIA_HEAD_BYTES,
6
41
  SYNC_STATE_NAME,
7
- TAKE_MEDIA_EXTENSIONS,
8
42
  UsageError,
9
43
  apiError,
10
44
  apiJson,
11
45
  clientId,
12
- configDuration,
13
46
  containerMismatch,
14
47
  deriveSlug,
15
- directoryKind,
16
- extensionFor,
17
48
  formatChanges,
18
49
  hasFlag,
19
- launchBrowser,
20
- loadVosConfig,
50
+ hostedAssetId,
51
+ manifestRefs,
21
52
  nameForType,
22
53
  numFlag2 as numFlag,
23
54
  parseArgs2 as parseArgs,
24
- parseTimes,
25
55
  parseVosId,
26
56
  platformOrigin,
57
+ programAssets,
27
58
  readSyncState,
28
- reencodeStill,
29
- renderStill,
30
59
  requireCredential,
31
60
  resolveCredential,
32
61
  resolveMediaType,
33
62
  serverWarnings,
34
63
  sniffMediaType,
35
64
  strFlag,
36
- videoFormat,
37
65
  writeCredential,
38
66
  writeSyncState
39
- } from "./chunk-YC3LY4LW.js";
67
+ } from "./chunk-36P5RREC.js";
40
68
  import {
41
69
  deadLine,
42
70
  deadWarns
@@ -98,9 +126,9 @@ function landedLines(l) {
98
126
  }
99
127
 
100
128
  // src/plugin/run.ts
101
- import { mkdir as mkdir11, readFile as readFile17, rm as rm5 } from "fs/promises";
102
- import { existsSync as existsSync24 } from "fs";
103
- import { join as join28, resolve as resolve15 } from "path";
129
+ import { mkdir as mkdir9, readFile as readFile15, rm as rm3 } from "fs/promises";
130
+ import { existsSync as existsSync23 } from "fs";
131
+ import { join as join26, resolve as resolve15 } from "path";
104
132
  import {
105
133
  END_CARD_FROM as END_CARD_FROM2,
106
134
  docOutputDuration as docOutputDuration3,
@@ -1813,535 +1841,6 @@ function framingWarnings(docIn, doc, zoom, overlays, warnings) {
1813
1841
  }
1814
1842
  }
1815
1843
 
1816
- // src/plugin/media.ts
1817
- import { isKeyedOverlay } from "@vosjs/studio-core";
1818
- import { createHash } from "crypto";
1819
- import { createWriteStream, existsSync as existsSync2 } from "fs";
1820
- import {
1821
- mkdir as mkdir2,
1822
- open,
1823
- readFile as readFile2,
1824
- readdir,
1825
- rename as rename2,
1826
- rm as rm2,
1827
- stat,
1828
- writeFile as writeFile2
1829
- } from "fs/promises";
1830
- import { basename as basename2, dirname, join as join2 } from "path";
1831
- import { Readable } from "stream";
1832
- import { pipeline } from "stream/promises";
1833
-
1834
- // src/plugin/take.ts
1835
- import { mkdir, readFile, rename, rm, writeFile } from "fs/promises";
1836
- import { existsSync, readdirSync, statSync } from "fs";
1837
- import { basename, join } from "path";
1838
- var RECORDING_NAME = "recording.webm";
1839
- function takeMediaFiles(dir, stem) {
1840
- return TAKE_MEDIA_EXTENSIONS.map((ext) => join(dir, `${stem}${ext}`)).filter(
1841
- (file) => existsSync(file)
1842
- );
1843
- }
1844
- function takeMediaFile(dir, stem, fallback) {
1845
- return takeMediaFiles(dir, stem)[0] ?? join(dir, fallback);
1846
- }
1847
- var PREV_DOC_NAME = "doc.prev.json";
1848
- function takePaths(dir) {
1849
- const recording = takeMediaFile(dir, "recording", RECORDING_NAME);
1850
- return {
1851
- dir,
1852
- recording,
1853
- recordingName: basename(recording),
1854
- framesDir: join(dir, "frames"),
1855
- framesIndex: join(dir, "frames.json"),
1856
- cursor: join(dir, "cursor.json"),
1857
- meta: join(dir, "meta.json"),
1858
- actions: join(dir, "actions.json"),
1859
- doc: join(dir, "doc.json")
1860
- };
1861
- }
1862
- async function ensureTakeDir(dir) {
1863
- const p = takePaths(dir);
1864
- await mkdir(p.framesDir, { recursive: true });
1865
- return p;
1866
- }
1867
- async function dropScreencastFrames(p) {
1868
- if (!existsSync(p.recording) || statSync(p.recording).size === 0) return 0;
1869
- if (!existsSync(p.framesDir)) return 0;
1870
- let freed = 0;
1871
- for (const name of readdirSync(p.framesDir)) {
1872
- try {
1873
- freed += statSync(join(p.framesDir, name)).size;
1874
- } catch {
1875
- }
1876
- }
1877
- await rm(p.framesDir, { recursive: true, force: true });
1878
- await rm(p.framesIndex, { force: true });
1879
- return freed;
1880
- }
1881
- async function prepareReRecord(dir) {
1882
- const p = takePaths(dir);
1883
- const kept = [];
1884
- let prevDoc = false;
1885
- if (existsSync(p.doc)) {
1886
- await rename(p.doc, join(dir, PREV_DOC_NAME));
1887
- prevDoc = true;
1888
- kept.push(PREV_DOC_NAME);
1889
- }
1890
- if (existsSync(p.actions)) kept.push("actions.json");
1891
- if (existsSync(join(dir, "vos.json"))) kept.push("vos.json");
1892
- for (const stale of [
1893
- // Every container, not just the one `record` writes: a pulled take's
1894
- // footage may be mp4, and a survivor would outrank the new recording.
1895
- ...takeMediaFiles(dir, "recording"),
1896
- ...takeMediaFiles(dir, "mic"),
1897
- ...takeMediaFiles(dir, "cam"),
1898
- p.cursor,
1899
- p.meta,
1900
- p.framesIndex,
1901
- p.framesDir,
1902
- join(dir, "stills"),
1903
- join(dir, "digest")
1904
- ]) {
1905
- await rm(stale, { recursive: true, force: true });
1906
- }
1907
- return { prevDoc, kept };
1908
- }
1909
- async function readJson(file) {
1910
- return JSON.parse(await readFile(file, "utf8"));
1911
- }
1912
- async function writeJson(file, value, pretty = false) {
1913
- await writeFile(
1914
- file,
1915
- pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value)
1916
- );
1917
- }
1918
- async function loadTake(dir) {
1919
- const paths = takePaths(dir);
1920
- if (!existsSync(paths.meta)) {
1921
- throw new Error(
1922
- `${dir} is not a take directory (missing meta.json) \u2014 run record first`
1923
- );
1924
- }
1925
- const meta = await readJson(paths.meta);
1926
- const cursor = existsSync(paths.cursor) ? await readJson(paths.cursor) : [];
1927
- const actions = existsSync(paths.actions) ? await readJson(paths.actions) : null;
1928
- const doc = existsSync(paths.doc) ? await readJson(paths.doc) : null;
1929
- return { paths, cursor, meta, actions, doc };
1930
- }
1931
-
1932
- // src/plugin/uploadAsset.ts
1933
- var SINGLE_SHOT_MAX_BYTES = 32 * 1024 * 1024;
1934
- var PART_ATTEMPTS = 3;
1935
- function planParts(totalBytes, partBytes) {
1936
- if (totalBytes <= 0 || partBytes <= 0) return [];
1937
- const parts = [];
1938
- for (let start = 0; start < totalBytes; start += partBytes) {
1939
- parts.push({
1940
- partNumber: parts.length + 1,
1941
- start,
1942
- end: Math.min(start + partBytes, totalBytes)
1943
- });
1944
- }
1945
- return parts;
1946
- }
1947
- function failed(what, r) {
1948
- const detail = typeof r.body.error === "string" ? r.body.error : "";
1949
- return new Error(`${what} (${r.status})${detail ? `: ${detail}` : ""}`);
1950
- }
1951
- function asAsset(body) {
1952
- return {
1953
- id: String(body.id),
1954
- url: String(body.url),
1955
- size: typeof body.size === "number" ? body.size : 0,
1956
- reused: body.reused === true
1957
- };
1958
- }
1959
- function transient(status) {
1960
- return status >= 500 || status === 429;
1961
- }
1962
- var wait = (ms) => new Promise((r) => setTimeout(r, ms));
1963
- async function uploadAsset(target, bytes, opts) {
1964
- if (bytes.length > SINGLE_SHOT_MAX_BYTES && opts.folderId) {
1965
- throw new Error(
1966
- `${opts.filename} is over ${SINGLE_SHOT_MAX_BYTES / 1024 / 1024} MB, and a file that large cannot be filed into a project on upload yet. Push it without --folder and reference it from a document, which keeps it`
1967
- );
1968
- }
1969
- return bytes.length > SINGLE_SHOT_MAX_BYTES ? uploadInParts(target, bytes, opts) : uploadWhole(target, bytes, opts);
1970
- }
1971
- async function uploadWhole(target, bytes, opts) {
1972
- const res = await apiJson(target.origin, "/api/assets/recording", {
1973
- method: "POST",
1974
- key: target.key,
1975
- headers: {
1976
- "Content-Type": opts.contentType,
1977
- "Content-Length": String(bytes.length),
1978
- "X-Filename": opts.filename,
1979
- "X-Content-Hash": opts.contentHash,
1980
- ...opts.durationSeconds && opts.durationSeconds > 0 ? { "X-Content-Duration": opts.durationSeconds.toFixed(3) } : {},
1981
- ...opts.folderId ? { "X-Folder-Id": opts.folderId } : {}
1982
- },
1983
- raw: bytes
1984
- });
1985
- if (res.status !== 201 && res.status !== 200)
1986
- throw failed("upload failed", res);
1987
- return asAsset(res.body);
1988
- }
1989
- async function uploadInParts(target, bytes, opts) {
1990
- const begun = await apiJson(
1991
- target.origin,
1992
- "/api/assets/recording/multipart",
1993
- {
1994
- method: "POST",
1995
- key: target.key,
1996
- body: {
1997
- filename: opts.filename,
1998
- contentType: opts.contentType,
1999
- size: bytes.length,
2000
- contentHash: opts.contentHash,
2001
- ...opts.durationSeconds && opts.durationSeconds > 0 ? { durationSeconds: opts.durationSeconds } : {}
2002
- }
2003
- }
2004
- );
2005
- if (begun.status !== 201 && begun.status !== 200) {
2006
- throw failed("upload failed", begun);
2007
- }
2008
- if (begun.body.reused === true || typeof begun.body.url === "string") {
2009
- return asAsset(begun.body);
2010
- }
2011
- const uploadId = String(begun.body.id);
2012
- const partBytes = Number(begun.body.partBytes);
2013
- if (!Number.isFinite(partBytes) || partBytes <= 0) {
2014
- throw new Error("upload failed: the platform returned no part size");
2015
- }
2016
- const parts = planParts(bytes.length, partBytes);
2017
- const etags = [];
2018
- try {
2019
- for (const part of parts) {
2020
- const chunk2 = bytes.subarray(part.start, part.end);
2021
- let sent = null;
2022
- for (let attempt = 1; attempt <= PART_ATTEMPTS; attempt++) {
2023
- try {
2024
- sent = await apiJson(
2025
- target.origin,
2026
- `/api/assets/recording/multipart/${uploadId}/parts/${part.partNumber}`,
2027
- {
2028
- method: "PUT",
2029
- key: target.key,
2030
- headers: {
2031
- "Content-Type": "application/octet-stream",
2032
- "Content-Length": String(chunk2.length)
2033
- },
2034
- raw: chunk2
2035
- }
2036
- );
2037
- } catch (networkError) {
2038
- if (attempt === PART_ATTEMPTS) throw networkError;
2039
- await wait(500 * 2 ** (attempt - 1));
2040
- continue;
2041
- }
2042
- if (sent.status === 200) break;
2043
- if (!transient(sent.status) || attempt === PART_ATTEMPTS) {
2044
- throw failed(`part ${part.partNumber} failed`, sent);
2045
- }
2046
- await wait(500 * 2 ** (attempt - 1));
2047
- }
2048
- if (!sent || sent.status !== 200) {
2049
- throw new Error(`part ${part.partNumber} failed`);
2050
- }
2051
- etags.push({
2052
- partNumber: Number(sent.body.partNumber),
2053
- etag: String(sent.body.etag)
2054
- });
2055
- opts.onPart?.(etags.length, parts.length);
2056
- }
2057
- const sealed = await apiJson(
2058
- target.origin,
2059
- `/api/assets/recording/multipart/${uploadId}/complete`,
2060
- { method: "POST", key: target.key, body: { parts: etags } }
2061
- );
2062
- if (sealed.status !== 201 && sealed.status !== 200) {
2063
- throw failed("upload failed", sealed);
2064
- }
2065
- return asAsset(sealed.body);
2066
- } catch (err) {
2067
- await apiJson(
2068
- target.origin,
2069
- `/api/assets/recording/multipart/${uploadId}`,
2070
- { method: "DELETE", key: target.key }
2071
- ).catch(() => void 0);
2072
- throw err;
2073
- }
2074
- }
2075
-
2076
- // src/plugin/media.ts
2077
- var SOURCE_MEDIA = [
2078
- { key: "videoKey", stem: "recording", fallbackExt: ".webm" },
2079
- { key: "micKey", stem: "mic", fallbackExt: ".webm" },
2080
- { key: "camKey", stem: "cam", fallbackExt: ".webm" }
2081
- ];
2082
- function isTakeRelativeKey(key) {
2083
- return !!key && !/^(https?:|blob:|data:|media:|\/\/)/.test(key) && !key.startsWith("/api/");
2084
- }
2085
- var takeRelativeFile = (key) => key.replace(/^\/+/, "");
2086
- function sourceSidecarRefs(doc, keep = isTakeRelativeKey) {
2087
- const out = [];
2088
- for (const [field, where] of [
2089
- ["micKey", "mic track"],
2090
- ["camKey", "cam track"]
2091
- ]) {
2092
- const key = doc.source[field];
2093
- if (!key || !keep(key)) continue;
2094
- out.push({
2095
- where,
2096
- key,
2097
- set: (next) => {
2098
- doc.source[field] = next;
2099
- }
2100
- });
2101
- }
2102
- return out;
2103
- }
2104
- function docObjectRefs(doc, keep = isTakeRelativeKey) {
2105
- const out = [];
2106
- for (const clip of doc.objects ?? []) {
2107
- const asset = clip.asset;
2108
- if (asset.kind !== "gltf" || !keep(asset.key)) continue;
2109
- out.push({
2110
- where: `object ${clip.id}`,
2111
- key: asset.key,
2112
- set: (next) => {
2113
- asset.key = next;
2114
- }
2115
- });
2116
- }
2117
- return out;
2118
- }
2119
- function docOverlayRefs(doc, keep = isTakeRelativeKey) {
2120
- const out = [];
2121
- for (const clip of doc.overlays ?? []) {
2122
- if (!isKeyedOverlay(clip) || !keep(clip.key)) continue;
2123
- out.push({
2124
- where: `overlay ${clip.id}`,
2125
- key: clip.key,
2126
- set: (next) => {
2127
- clip.key = next;
2128
- }
2129
- });
2130
- }
2131
- return out;
2132
- }
2133
- function docLayerRefs(doc, keep = isTakeRelativeKey) {
2134
- return [
2135
- ...docOverlayRefs(doc, keep),
2136
- ...docObjectRefs(doc, keep),
2137
- ...docAudioRefs(doc, keep)
2138
- ];
2139
- }
2140
- function docMediaRefs(doc, keep = isTakeRelativeKey) {
2141
- const out = [];
2142
- for (const m of doc.media ?? []) {
2143
- if (keep(m.videoKey))
2144
- out.push({
2145
- where: `media ${m.id}`,
2146
- key: m.videoKey,
2147
- set: (next) => {
2148
- m.videoKey = next;
2149
- }
2150
- });
2151
- const mic = m.micKey;
2152
- if (mic && keep(mic))
2153
- out.push({
2154
- where: `media ${m.id} mic`,
2155
- key: mic,
2156
- set: (next) => {
2157
- m.micKey = next;
2158
- }
2159
- });
2160
- const cam = m.camKey;
2161
- if (cam && keep(cam))
2162
- out.push({
2163
- where: `media ${m.id} cam`,
2164
- key: cam,
2165
- set: (next) => {
2166
- m.camKey = next;
2167
- }
2168
- });
2169
- }
2170
- out.push(...docOverlayRefs(doc, keep));
2171
- out.push(...docObjectRefs(doc, keep));
2172
- const mark = doc.endCard?.mark;
2173
- if (mark && keep(mark.key))
2174
- out.push({
2175
- where: "end card mark",
2176
- key: mark.key,
2177
- set: (next) => {
2178
- mark.key = next;
2179
- }
2180
- });
2181
- const bg = doc.frame.backgroundMedia;
2182
- if (bg && keep(bg.key))
2183
- out.push({
2184
- where: "background",
2185
- key: bg.key,
2186
- set: (next) => {
2187
- bg.key = next;
2188
- }
2189
- });
2190
- out.push(...docAudioRefs(doc, keep));
2191
- return out;
2192
- }
2193
- function docAudioRefs(doc, keep = isTakeRelativeKey) {
2194
- const out = [];
2195
- for (const clip of doc.audio ?? []) {
2196
- if (!keep(clip.key)) continue;
2197
- out.push({
2198
- where: `audio ${clip.id}`,
2199
- key: clip.key,
2200
- set: (next) => {
2201
- clip.key = next;
2202
- }
2203
- });
2204
- }
2205
- return out;
2206
- }
2207
- async function uploadDocRefs(refs, dir, target, log) {
2208
- for (const ref of refs) {
2209
- const file = join2(dir, takeRelativeFile(ref.key));
2210
- if (!existsSync2(file)) {
2211
- log(` ${ref.where}: ${ref.key} is not beside the document \u2014 left as is`);
2212
- continue;
2213
- }
2214
- const media = await readFile2(file);
2215
- const mediaHash = createHash("sha256").update(media).digest("hex");
2216
- const { type } = resolveMediaType({
2217
- head: media.subarray(0, MEDIA_HEAD_BYTES),
2218
- filename: file
2219
- });
2220
- const name = nameForType(basename2(file), type);
2221
- if (name !== basename2(file)) {
2222
- log(` ${ref.where}: ${basename2(file)} holds ${type} \u2014 sent as ${name}`);
2223
- }
2224
- let put;
2225
- try {
2226
- put = await uploadAsset(target, new Uint8Array(media), {
2227
- filename: name,
2228
- contentType: type,
2229
- contentHash: mediaHash
2230
- });
2231
- } catch (e) {
2232
- throw new Error(
2233
- `${ref.where} upload failed: ${e instanceof Error ? e.message : String(e)}`
2234
- );
2235
- }
2236
- ref.set(put.url);
2237
- log(
2238
- ` ${ref.where}: ${ref.key} \u2192 asset ${put.id}${put.reused ? " (reused)" : ""}`
2239
- );
2240
- }
2241
- }
2242
- function assetIdOf(url) {
2243
- if (!url) return null;
2244
- const m = /\/api\/assets\/([A-Za-z0-9_-]+)\/file/.exec(url);
2245
- return m ? m[1] : null;
2246
- }
2247
- async function readMediaHead(file) {
2248
- const handle = await open(file, "r");
2249
- try {
2250
- const buf = Buffer.alloc(MEDIA_HEAD_BYTES);
2251
- const { bytesRead } = await handle.read(buf, 0, MEDIA_HEAD_BYTES, 0);
2252
- return new Uint8Array(buf.subarray(0, bytesRead));
2253
- } finally {
2254
- await handle.close();
2255
- }
2256
- }
2257
- async function downloadMedia(ctx, url, target) {
2258
- const abs = /^https?:/.test(url) ? url : `${ctx.origin}${url}`;
2259
- const res = await fetch(abs, {
2260
- headers: ctx.key ? { authorization: `Bearer ${ctx.key}` } : {}
2261
- });
2262
- if (!res.ok || !res.body) {
2263
- throw new Error(
2264
- `download of ${target.describe} failed (${res.status})${res.status === 404 && target.notFoundHint ? ` \u2014 ${target.notFoundHint}` : ""}`
2265
- );
2266
- }
2267
- const into = target.subdir ? join2(target.dir, target.subdir) : target.dir;
2268
- const partial = join2(into, `${target.stem}.part`);
2269
- await mkdir2(dirname(partial), { recursive: true });
2270
- try {
2271
- await pipeline(
2272
- Readable.fromWeb(res.body),
2273
- createWriteStream(partial)
2274
- );
2275
- const { type } = resolveMediaType({
2276
- head: await readMediaHead(partial),
2277
- declared: res.headers.get("content-type"),
2278
- filename: `${target.stem}${target.fallbackExt}`
2279
- });
2280
- const name = `${target.stem}${extensionFor(type) || target.fallbackExt}`;
2281
- await rename2(partial, join2(into, name));
2282
- const file = target.subdir ? `${target.subdir}/${name}` : name;
2283
- return { file, bytes: (await stat(join2(target.dir, file))).size };
2284
- } finally {
2285
- await rm2(partial, { force: true });
2286
- }
2287
- }
2288
- async function pullMedia(ctx, dir, doc, log) {
2289
- const result = { downloaded: [], kept: [] };
2290
- for (const { key, stem, fallbackExt } of SOURCE_MEDIA) {
2291
- const url = doc.source[key];
2292
- const assetId = assetIdOf(url);
2293
- if (!assetId) continue;
2294
- const present = takeMediaFiles(dir, stem)[0];
2295
- if (present) {
2296
- result.kept.push(basename2(present));
2297
- doc.source[key] = basename2(present);
2298
- continue;
2299
- }
2300
- const got = await downloadMedia(ctx, url, {
2301
- dir,
2302
- stem,
2303
- fallbackExt,
2304
- describe: stem,
2305
- notFoundHint: "a private recording needs a content key of its owner"
2306
- });
2307
- result.downloaded.push({ file: got.file, assetId, bytes: got.bytes });
2308
- log(` ${got.file} \u2190 asset ${assetId} (${Math.round(got.bytes / 1024)} kB)`);
2309
- doc.source[key] = got.file;
2310
- }
2311
- for (const ref of docMediaRefs(doc, (k) => assetIdOf(k) !== null)) {
2312
- const assetId = assetIdOf(ref.key);
2313
- const existing = existsSync2(join2(dir, "media")) ? (await readdir(join2(dir, "media"))).find(
2314
- (f) => f === assetId || f.startsWith(`${assetId}.`)
2315
- ) : void 0;
2316
- if (existing) {
2317
- const file = `media/${existing}`;
2318
- result.kept.push(file);
2319
- ref.set(file);
2320
- continue;
2321
- }
2322
- const got = await downloadMedia(ctx, ref.key, {
2323
- dir,
2324
- subdir: "media",
2325
- stem: assetId,
2326
- // A type with no extension here (an svg served as such) keeps the bare
2327
- // asset id, exactly as before.
2328
- fallbackExt: "",
2329
- describe: `${ref.where} (${ref.key})`
2330
- });
2331
- result.downloaded.push({ file: got.file, assetId, bytes: got.bytes });
2332
- log(
2333
- ` ${got.file} \u2190 ${ref.where}, asset ${assetId} (${Math.round(got.bytes / 1024)} kB)`
2334
- );
2335
- ref.set(got.file);
2336
- }
2337
- const metaPath = join2(dir, "meta.json");
2338
- if (!existsSync2(metaPath)) await writeJson(metaPath, doc.source.meta, true);
2339
- const cursorPath = join2(dir, "cursor.json");
2340
- if (!existsSync2(cursorPath)) await writeJson(cursorPath, doc.source.cursor);
2341
- await writeFile2(join2(dir, "doc.json"), JSON.stringify(doc, null, 2));
2342
- return result;
2343
- }
2344
-
2345
1844
  // src/plugin/mediaProbe.ts
2346
1845
  var MEDIA_EXT = /\.(png|jpe?g|webp|gif|webm|mp4|mov|mp3|wav|m4a|ogg|glb|gltf|hdr|exr|woff2?)($|\?)/i;
2347
1846
  var isRemote = (v) => typeof v === "string" && /^https?:\/\//i.test(v);
@@ -2427,9 +1926,9 @@ function mediaProbeLints(results) {
2427
1926
  }
2428
1927
 
2429
1928
  // src/plugin/framesTake.ts
2430
- import { mkdir as mkdir4, readFile as readFile5, rename as rename4, writeFile as writeFile5 } from "fs/promises";
2431
- import { existsSync as existsSync5 } from "fs";
2432
- import { join as join5, resolve as resolve2 } from "path";
1929
+ import { mkdir as mkdir2, readFile as readFile3, rename as rename2, writeFile as writeFile3 } from "fs/promises";
1930
+ import { existsSync as existsSync3 } from "fs";
1931
+ import { join as join3, resolve as resolve2 } from "path";
2433
1932
  import { compileVosConfig } from "@vosjs/core";
2434
1933
  import {
2435
1934
  docStillTime,
@@ -2443,9 +1942,9 @@ import {
2443
1942
 
2444
1943
  // src/plugin/server.ts
2445
1944
  import { createServer } from "http";
2446
- import { existsSync as existsSync3, readFileSync, statSync as statSync2 } from "fs";
2447
- import { writeFile as writeFile3 } from "fs/promises";
2448
- import { extname, join as join3 } from "path";
1945
+ import { existsSync, readFileSync, statSync } from "fs";
1946
+ import { writeFile } from "fs/promises";
1947
+ import { extname, join } from "path";
2449
1948
  var MIME = {
2450
1949
  ".html": "text/html",
2451
1950
  ".js": "text/javascript",
@@ -2489,7 +1988,7 @@ function startTakeServer(rootDir, pages) {
2489
1988
  const chunks = [];
2490
1989
  req.on("data", (c) => chunks.push(c));
2491
1990
  req.on("end", () => {
2492
- void writeFile3(join3(rootDir, name), Buffer.concat(chunks)).then(
1991
+ void writeFile(join(rootDir, name), Buffer.concat(chunks)).then(
2493
1992
  () => res.writeHead(200).end("ok"),
2494
1993
  (e) => res.writeHead(500).end(String(e))
2495
1994
  );
@@ -2501,9 +2000,9 @@ function startTakeServer(rootDir, pages) {
2501
2000
  res.end(pages[url.pathname]);
2502
2001
  return;
2503
2002
  }
2504
- const file = join3(rootDir, decodeURIComponent(url.pathname));
2505
- if (existsSync3(file) && statSync2(file).isFile()) {
2506
- const size = statSync2(file).size;
2003
+ const file = join(rootDir, decodeURIComponent(url.pathname));
2004
+ if (existsSync(file) && statSync(file).isFile()) {
2005
+ const size = statSync(file).size;
2507
2006
  const buf = readFileSync(file);
2508
2007
  const type = sniffMediaType(buf.subarray(0, MEDIA_HEAD_BYTES)) ?? MIME[extname(file)] ?? "application/octet-stream";
2509
2008
  const range = req.headers.range;
@@ -2568,7 +2067,7 @@ async function waitForPageDone(page, label, onProgress, timeoutMs) {
2568
2067
  }
2569
2068
 
2570
2069
  // src/plugin/renderAnimation.ts
2571
- import { readFile as readFile3, rm as rm3 } from "fs/promises";
2070
+ import { readFile, rm } from "fs/promises";
2572
2071
  import { tweenRuntimeCode } from "@vosjs/tween/bundle";
2573
2072
  import { elementsBundleCode } from "@vosjs/elements/bundle";
2574
2073
  import { concatEncodedVideo, planChunks } from "@vosjs/render-core";
@@ -2863,10 +2362,10 @@ async function renderAnimation(browser, opts) {
2863
2362
  )
2864
2363
  );
2865
2364
  const files = await Promise.all(
2866
- outNames.map((name) => readFile3(`${opts.workDir}/${name}`))
2365
+ outNames.map((name) => readFile(`${opts.workDir}/${name}`))
2867
2366
  );
2868
2367
  await Promise.all(
2869
- outNames.map((name) => rm3(`${opts.workDir}/${name}`, { force: true }))
2368
+ outNames.map((name) => rm(`${opts.workDir}/${name}`, { force: true }))
2870
2369
  );
2871
2370
  if (chunks.length === 1) {
2872
2371
  return { bytes: new Uint8Array(files[0]), totalFrames, chunks: 1 };
@@ -3121,9 +2620,9 @@ function applyAndValidate(doc, o) {
3121
2620
  }
3122
2621
 
3123
2622
  // src/plugin/digestTake.ts
3124
- import { mkdir as mkdir3, readFile as readFile4, rename as rename3, writeFile as writeFile4 } from "fs/promises";
3125
- import { existsSync as existsSync4 } from "fs";
3126
- import { join as join4, resolve } from "path";
2623
+ import { mkdir, readFile as readFile2, rename, writeFile as writeFile2 } from "fs/promises";
2624
+ import { existsSync as existsSync2 } from "fs";
2625
+ import { join as join2, resolve } from "path";
3127
2626
  import {
3128
2627
  DIGEST_CROP_MAX,
3129
2628
  DIGEST_FULL_MAX,
@@ -3345,8 +2844,8 @@ async function digestTake(browser, dir, opts = {}) {
3345
2844
  const meta = doc.source.meta;
3346
2845
  let plan = planForDigest(doc);
3347
2846
  const transcript = opts.transcript ?? await discoverTranscript(dir);
3348
- const outDir = resolve(opts.outDir ?? join4(dir, DIGEST_DIR));
3349
- await mkdir3(outDir, { recursive: true });
2847
+ const outDir = resolve(opts.outDir ?? join2(dir, DIGEST_DIR));
2848
+ await mkdir(outDir, { recursive: true });
3350
2849
  const fullMax = opts.full ?? DIGEST_FULL_MAX;
3351
2850
  const cropMax = opts.crop ?? DIGEST_CROP_MAX;
3352
2851
  const wantFrames = opts.frames !== false && browser !== null;
@@ -3362,7 +2861,7 @@ async function digestTake(browser, dir, opts = {}) {
3362
2861
  kind: "video",
3363
2862
  url: `/${take.paths.recordingName}`
3364
2863
  };
3365
- if (existsSync4(take.paths.framesIndex) && existsSync4(take.paths.framesDir)) {
2864
+ if (existsSync2(take.paths.framesIndex) && existsSync2(take.paths.framesDir)) {
3366
2865
  const index = await readJson(
3367
2866
  take.paths.framesIndex
3368
2867
  );
@@ -3377,7 +2876,7 @@ async function digestTake(browser, dir, opts = {}) {
3377
2876
  }
3378
2877
  }
3379
2878
  if (wantFrames) {
3380
- if (source.kind === "video" && !existsSync4(take.paths.recording))
2879
+ if (source.kind === "video" && !existsSync2(take.paths.recording))
3381
2880
  throw new Error(
3382
2881
  `${dir} has no recording \u2014 pull it with \`vos pull --media\` first`
3383
2882
  );
@@ -3450,9 +2949,9 @@ async function digestTake(browser, dir, opts = {}) {
3450
2949
  );
3451
2950
  sizes = f.sizes;
3452
2951
  for (const name of Object.keys(sizes)) {
3453
- const from = join4(dir, name);
3454
- const to = join4(outDir, name);
3455
- if (from !== to && existsSync4(from)) await rename3(from, to);
2952
+ const from = join2(dir, name);
2953
+ const to = join2(outDir, name);
2954
+ if (from !== to && existsSync2(from)) await rename(from, to);
3456
2955
  }
3457
2956
  if (sizes["sheet.png"]) sheet = "sheet.png";
3458
2957
  } finally {
@@ -3478,12 +2977,12 @@ async function digestTake(browser, dir, opts = {}) {
3478
2977
  if (!p) continue;
3479
2978
  for (const f of [p.full, p.crop]) {
3480
2979
  if (!f) continue;
3481
- const size = await pngSize(join4(outDir, f));
2980
+ const size = await pngSize(join2(outDir, f));
3482
2981
  if (size) sizes[f] = size;
3483
2982
  }
3484
2983
  if (p.box) boxes.set(m.id, p.box);
3485
2984
  }
3486
- if (prior.images.sheet && existsSync4(join4(outDir, prior.images.sheet)))
2985
+ if (prior.images.sheet && existsSync2(join2(outDir, prior.images.sheet)))
3487
2986
  sheet = prior.images.sheet;
3488
2987
  }
3489
2988
  }
@@ -3512,16 +3011,16 @@ async function digestTake(browser, dir, opts = {}) {
3512
3011
  style: opts.style ?? null,
3513
3012
  transcript
3514
3013
  });
3515
- const file = join4(outDir, "digest.json");
3014
+ const file = join2(outDir, "digest.json");
3516
3015
  const body = JSON.stringify(digest, null, 2);
3517
- await writeFile4(file, body);
3016
+ await writeFile2(file, body);
3518
3017
  return { file, outDir, digest, bytes: Buffer.byteLength(body) };
3519
3018
  }
3520
3019
  async function discoverTranscript(dir) {
3521
- const file = join4(dir, "transcript.json");
3522
- if (!existsSync4(file)) return null;
3020
+ const file = join2(dir, "transcript.json");
3021
+ if (!existsSync2(file)) return null;
3523
3022
  try {
3524
- return parseTranscript(JSON.parse(await readFile4(file, "utf8")));
3023
+ return parseTranscript(JSON.parse(await readFile2(file, "utf8")));
3525
3024
  } catch {
3526
3025
  return null;
3527
3026
  }
@@ -3529,7 +3028,7 @@ async function discoverTranscript(dir) {
3529
3028
  async function readPriorDigest(outDir) {
3530
3029
  try {
3531
3030
  const raw = JSON.parse(
3532
- await readFile4(join4(outDir, "digest.json"), "utf8")
3031
+ await readFile2(join2(outDir, "digest.json"), "utf8")
3533
3032
  );
3534
3033
  return raw.digestVersion === DIGEST_VERSION && Array.isArray(raw.moments) ? raw : null;
3535
3034
  } catch {
@@ -3538,7 +3037,7 @@ async function readPriorDigest(outDir) {
3538
3037
  }
3539
3038
  async function pngSize(file) {
3540
3039
  try {
3541
- const buf = await readFile4(file);
3040
+ const buf = await readFile2(file);
3542
3041
  if (buf.length < 24 || buf.readUInt32BE(0) !== 2303741511) return null;
3543
3042
  return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };
3544
3043
  } catch {
@@ -3713,8 +3212,8 @@ async function framesTake(browser, dir, opts) {
3713
3212
  throw new Error(
3714
3213
  "no frame times \u2014 pass --times/--frame, --at-zooms, --at-moments or --at-still"
3715
3214
  );
3716
- const outDir = resolve2(opts.outDir ?? join5(dir, "stills"));
3717
- await mkdir4(outDir, { recursive: true });
3215
+ const outDir = resolve2(opts.outDir ?? join3(dir, "stills"));
3216
+ await mkdir2(outDir, { recursive: true });
3718
3217
  const named = shots.map((s, i) => ({
3719
3218
  ...s,
3720
3219
  name: s.kind === "moment" ? `moment-${s.momentId}-${s.time.toFixed(2)}s.png` : `${s.kind === "zoom" ? "zoom" : "frame"}-${String(i).padStart(2, "0")}-${s.time.toFixed(2)}s.png`
@@ -3758,9 +3257,9 @@ async function framesTake(browser, dir, opts) {
3758
3257
  );
3759
3258
  const frames = [];
3760
3259
  for (const n of named) {
3761
- const from = join5(dir, n.name);
3762
- const to = join5(outDir, n.name);
3763
- if (from !== to) await rename4(from, to);
3260
+ const from = join3(dir, n.name);
3261
+ const to = join3(outDir, n.name);
3262
+ if (from !== to) await rename2(from, to);
3764
3263
  frames.push({
3765
3264
  file: to,
3766
3265
  time: n.time,
@@ -3775,10 +3274,10 @@ async function framesTake(browser, dir, opts) {
3775
3274
  }
3776
3275
  }
3777
3276
  async function momentsFor(dir, doc) {
3778
- const file = join5(dir, DIGEST_DIR, "digest.json");
3779
- if (existsSync5(file)) {
3277
+ const file = join3(dir, DIGEST_DIR, "digest.json");
3278
+ if (existsSync3(file)) {
3780
3279
  try {
3781
- const d = JSON.parse(await readFile5(file, "utf8"));
3280
+ const d = JSON.parse(await readFile3(file, "utf8"));
3782
3281
  if (Array.isArray(d.moments)) return d.moments;
3783
3282
  } catch {
3784
3283
  }
@@ -3798,8 +3297,8 @@ function parseTimes2(spec, duration) {
3798
3297
  });
3799
3298
  }
3800
3299
  async function writeIndexJson(result) {
3801
- const index = join5(result.outDir, "stills.json");
3802
- await writeFile5(
3300
+ const index = join3(result.outDir, "stills.json");
3301
+ await writeFile3(
3803
3302
  index,
3804
3303
  JSON.stringify(
3805
3304
  {
@@ -3816,11 +3315,11 @@ async function writeIndexJson(result) {
3816
3315
  }
3817
3316
 
3818
3317
  // src/plugin/deliver.ts
3819
- import { mkdir as mkdir5, mkdtemp, rename as rename5, rm as rm4, stat as stat2, writeFile as writeFile7 } from "fs/promises";
3318
+ import { mkdir as mkdir3, mkdtemp, rename as rename3, rm as rm2, stat, writeFile as writeFile5 } from "fs/promises";
3820
3319
  import { tmpdir } from "os";
3821
- import { join as join7, relative, resolve as resolve4 } from "path";
3822
- import { existsSync as existsSync7 } from "fs";
3823
- import { readFile as readFile7 } from "fs/promises";
3320
+ import { join as join5, relative, resolve as resolve4 } from "path";
3321
+ import { existsSync as existsSync5 } from "fs";
3322
+ import { readFile as readFile5 } from "fs/promises";
3824
3323
  import { parseFrontmatter } from "@vosjs/shared/frontmatter";
3825
3324
  import {
3826
3325
  CHANNEL_SPECS_VERIFIED,
@@ -3834,7 +3333,7 @@ import {
3834
3333
  } from "@vosjs/studio-core";
3835
3334
 
3836
3335
  // src/plugin/renderTake.ts
3837
- import { writeFile as writeFile6 } from "fs/promises";
3336
+ import { writeFile as writeFile4 } from "fs/promises";
3838
3337
  import { compileVosConfig as compileVosConfig2 } from "@vosjs/core";
3839
3338
  import {
3840
3339
  audioProducerCode,
@@ -3923,7 +3422,7 @@ async function renderTake(browser, dir, outFile, opts) {
3923
3422
  } : void 0,
3924
3423
  onProgress: opts.onProgress
3925
3424
  });
3926
- await writeFile6(outFile, result.bytes);
3425
+ await writeFile4(outFile, result.bytes);
3927
3426
  return {
3928
3427
  out: outFile,
3929
3428
  bytes: result.bytes.length,
@@ -4478,9 +3977,9 @@ function proposeTransitions(input, kind) {
4478
3977
  for (const step of steps) {
4479
3978
  if (!step.navigated || step.skipped) continue;
4480
3979
  const name = step.id ?? String(step.step);
4481
- const wait2 = step.do === "wait";
4482
- const outEnd = round3(wait2 ? step.tStart : step.tEnd);
4483
- const inStart = round3(wait2 ? step.tEnd : step.tEnd + STEP_SETTLE_SECONDS);
3980
+ const wait = step.do === "wait";
3981
+ const outEnd = round3(wait ? step.tStart : step.tEnd);
3982
+ const inStart = round3(wait ? step.tEnd : step.tEnd + STEP_SETTLE_SECONDS);
4484
3983
  const segs = doc.segments;
4485
3984
  if (segs.some(
4486
3985
  (s) => !s.media && Math.abs(s.out - outEnd) < 0.05 && s !== segs.at(-1)
@@ -4799,9 +4298,9 @@ function isLightHexGround(hex2) {
4799
4298
  }
4800
4299
 
4801
4300
  // src/plugin/posterDoc.ts
4802
- import { existsSync as existsSync6 } from "fs";
4803
- import { readFile as readFile6 } from "fs/promises";
4804
- import { isAbsolute, join as join6, resolve as resolve3 } from "path";
4301
+ import { existsSync as existsSync4 } from "fs";
4302
+ import { readFile as readFile4 } from "fs/promises";
4303
+ import { isAbsolute, join as join4, resolve as resolve3 } from "path";
4805
4304
  import {
4806
4305
  computeCardLayout,
4807
4306
  docStillTime as docStillTime2,
@@ -4828,16 +4327,16 @@ var roleFor = (cls) => `poster-${cls}`;
4828
4327
  async function readPosterAt(cls, path, takeDir, from) {
4829
4328
  let file = path;
4830
4329
  let docDir = path;
4831
- if (existsSync6(join6(path, "doc.json"))) {
4832
- file = join6(path, "doc.json");
4833
- } else if (!/\.json$/i.test(path) || !existsSync6(path)) {
4330
+ if (existsSync4(join4(path, "doc.json"))) {
4331
+ file = join4(path, "doc.json");
4332
+ } else if (!/\.json$/i.test(path) || !existsSync4(path)) {
4834
4333
  return null;
4835
4334
  } else {
4836
4335
  docDir = resolve3(path, "..");
4837
4336
  }
4838
4337
  let raw;
4839
4338
  try {
4840
- raw = JSON.parse(await readFile6(file, "utf8"));
4339
+ raw = JSON.parse(await readFile4(file, "utf8"));
4841
4340
  } catch {
4842
4341
  return null;
4843
4342
  }
@@ -4847,7 +4346,7 @@ async function readPosterAt(cls, path, takeDir, from) {
4847
4346
  const doc = migrateHostedDoc(
4848
4347
  raw
4849
4348
  );
4850
- const ownFootage = existsSync6(join6(docDir, "meta.json")) && existsSync6(join6(docDir, source.videoKey));
4349
+ const ownFootage = existsSync4(join4(docDir, "meta.json")) && existsSync4(join4(docDir, source.videoKey));
4851
4350
  const sync = readSyncState(docDir);
4852
4351
  return {
4853
4352
  cls,
@@ -4870,10 +4369,10 @@ async function findPosterDocs(takeDir, launchRoles) {
4870
4369
  candidates.push({ path: at2(named.trim()), from: `LAUNCH.md ${role}` });
4871
4370
  }
4872
4371
  candidates.push({
4873
- path: join6(takeDir, "poster", cls),
4372
+ path: join4(takeDir, "poster", cls),
4874
4373
  from: `poster/${cls}`
4875
4374
  });
4876
- candidates.push({ path: join6(takeDir, "poster"), from: "poster" });
4375
+ candidates.push({ path: join4(takeDir, "poster"), from: "poster" });
4877
4376
  for (const c of candidates) {
4878
4377
  const ref = await readPosterAt(cls, c.path, takeDir, c.from);
4879
4378
  if (ref) {
@@ -4969,19 +4468,19 @@ function resolveChannels(raw) {
4969
4468
  return out;
4970
4469
  }
4971
4470
  async function readBrandBesideTake(dir, explicit) {
4972
- const candidates = explicit ? [explicit] : [join7(dir, "BRAND.md"), join7(dir, "..", "BRAND.md")];
4471
+ const candidates = explicit ? [explicit] : [join5(dir, "BRAND.md"), join5(dir, "..", "BRAND.md")];
4973
4472
  for (const file of candidates) {
4974
- if (!existsSync7(file)) continue;
4975
- const roles = parseFrontmatter(await readFile7(file, "utf8"));
4473
+ if (!existsSync5(file)) continue;
4474
+ const roles = parseFrontmatter(await readFile5(file, "utf8"));
4976
4475
  return { file, roles };
4977
4476
  }
4978
4477
  return null;
4979
4478
  }
4980
4479
  async function readLaunchBesideTake(dir, explicit) {
4981
- const candidates = explicit ? [explicit] : [join7(dir, "LAUNCH.md"), join7(dir, "..", "LAUNCH.md")];
4480
+ const candidates = explicit ? [explicit] : [join5(dir, "LAUNCH.md"), join5(dir, "..", "LAUNCH.md")];
4982
4481
  for (const file of candidates) {
4983
- if (!existsSync7(file)) continue;
4984
- return { file, roles: parseFrontmatter(await readFile7(file, "utf8")) };
4482
+ if (!existsSync5(file)) continue;
4483
+ return { file, roles: parseFrontmatter(await readFile5(file, "utf8")) };
4985
4484
  }
4986
4485
  return null;
4987
4486
  }
@@ -5045,7 +4544,7 @@ async function pickStillTimes(browser, dir, doc, duration, opts) {
5045
4544
  const meta = doc.source.meta;
5046
4545
  const vw = meta.captureWidth ?? meta.width;
5047
4546
  const vh = meta.captureHeight ?? meta.height;
5048
- const probeDir = await mkdtemp(join7(tmpdir(), "vos-moments-"));
4547
+ const probeDir = await mkdtemp(join5(tmpdir(), "vos-moments-"));
5049
4548
  try {
5050
4549
  const probe3 = await framesTake(browser, dir, {
5051
4550
  times: candidates.map((c) => c.time),
@@ -5059,7 +4558,7 @@ async function pickStillTimes(browser, dir, doc, duration, opts) {
5059
4558
  });
5060
4559
  const byTime = /* @__PURE__ */ new Map();
5061
4560
  for (const frame of probe3.frames) {
5062
- const img = decodePng(new Uint8Array(await readFile7(frame.file)));
4561
+ const img = decodePng(new Uint8Array(await readFile5(frame.file)));
5063
4562
  if (!img) continue;
5064
4563
  const whole = { x: 0, y: 0, w: img.w, h: img.h };
5065
4564
  byTime.set(+frame.time.toFixed(3), {
@@ -5084,7 +4583,7 @@ async function pickStillTimes(browser, dir, doc, duration, opts) {
5084
4583
  notes: [...notes, ...pick.dropped.map((d) => `moment: ${d}`)]
5085
4584
  };
5086
4585
  } finally {
5087
- await rm4(probeDir, { recursive: true, force: true });
4586
+ await rm2(probeDir, { recursive: true, force: true });
5088
4587
  }
5089
4588
  }
5090
4589
  var overCeiling = (bytes, maxBytes) => `${Math.ceil(bytes / 1024)} KB exceeds the ${Math.floor(maxBytes / 1024)} KB ceiling`;
@@ -5132,8 +4631,8 @@ async function deliverTake(browser, dir, opts) {
5132
4631
  const doc = take.doc;
5133
4632
  const duration = docOutputDuration2(doc);
5134
4633
  const videoSeconds = opts.range ? Math.min(opts.range[1], duration) - Math.min(opts.range[0], duration) : duration;
5135
- const outDir = resolve4(opts.outDir ?? join7(dir, "kit"));
5136
- await mkdir5(outDir, { recursive: true });
4634
+ const outDir = resolve4(opts.outDir ?? join5(dir, "kit"));
4635
+ await mkdir3(outDir, { recursive: true });
5137
4636
  const destinations = opts.channels.flatMap((c) => destinationsForChannel(c));
5138
4637
  const assets = [];
5139
4638
  const skipped = [];
@@ -5193,7 +4692,7 @@ async function deliverTake(browser, dir, opts) {
5193
4692
  opts.onPhase?.(
5194
4693
  `${label} (${specWords(d)}) from ${ref.from}${ref.vosId ? ` ${ref.vosId}` : ""}, the rest at ${time.toFixed(2)}s`
5195
4694
  );
5196
- const shotDir = await mkdtemp(join7(tmpdir(), "vos-poster-"));
4695
+ const shotDir = await mkdtemp(join5(tmpdir(), "vos-poster-"));
5197
4696
  try {
5198
4697
  const captured = await framesTake(browser, ref.takeDir, {
5199
4698
  times: [time],
@@ -5203,9 +4702,9 @@ async function deliverTake(browser, dir, opts) {
5203
4702
  doc: ref.doc,
5204
4703
  overrides: opts.overrides
5205
4704
  });
5206
- const to = join7(outDir, `${d.id}.png`);
5207
- await rename5(captured.frames[0].file, to);
5208
- const bytes = (await stat2(to)).size;
4705
+ const to = join5(outDir, `${d.id}.png`);
4706
+ await rename3(captured.frames[0].file, to);
4707
+ const bytes = (await stat(to)).size;
5209
4708
  if (d.maxBytes !== void 0 && bytes > d.maxBytes) {
5210
4709
  skipped.push(
5211
4710
  `${label}: ${overCeiling(bytes, d.maxBytes)} (kept at ${to})`
@@ -5234,7 +4733,7 @@ async function deliverTake(browser, dir, opts) {
5234
4733
  ...shot.x < 0 && shot.x + shot.w > 1 ? { crop: true } : {}
5235
4734
  });
5236
4735
  } finally {
5237
- await rm4(shotDir, { recursive: true, force: true });
4736
+ await rm2(shotDir, { recursive: true, force: true });
5238
4737
  }
5239
4738
  }
5240
4739
  if (missing.size) {
@@ -5275,7 +4774,7 @@ async function deliverTake(browser, dir, opts) {
5275
4774
  }
5276
4775
  }
5277
4776
  opts.onPhase?.(`${label} (${specWords(d)})`);
5278
- const outFile = join7(outDir, `${d.id}.${d.format}`);
4777
+ const outFile = join5(outDir, `${d.id}.${d.format}`);
5279
4778
  const bitrate = d.maxBytes !== void 0 ? Math.min(
5280
4779
  1e7,
5281
4780
  Math.floor(d.maxBytes * 8 / seconds * 0.85)
@@ -5322,9 +4821,9 @@ async function deliverTake(browser, dir, opts) {
5322
4821
  for (let i = 0; i < captured.frames.length; i++) {
5323
4822
  const frame = captured.frames[i];
5324
4823
  const name = captured.frames.length > 1 ? `${d.id}-${i + 1}.png` : `${d.id}.png`;
5325
- const to = join7(outDir, name);
5326
- await rename5(frame.file, to);
5327
- const bytes = (await stat2(to)).size;
4824
+ const to = join5(outDir, name);
4825
+ await rename3(frame.file, to);
4826
+ const bytes = (await stat(to)).size;
5328
4827
  if (d.maxBytes !== void 0 && bytes > d.maxBytes) {
5329
4828
  skipped.push(
5330
4829
  `${label}: ${overCeiling(bytes, d.maxBytes)} (kept at ${to})`
@@ -5360,15 +4859,15 @@ async function deliverTake(browser, dir, opts) {
5360
4859
  skipped,
5361
4860
  assets
5362
4861
  };
5363
- const kitFile = join7(outDir, "kit.json");
5364
- await writeFile7(kitFile, JSON.stringify(kit, null, 2));
4862
+ const kitFile = join5(outDir, "kit.json");
4863
+ await writeFile5(kitFile, JSON.stringify(kit, null, 2));
5365
4864
  return { kit, kitFile, outDir };
5366
4865
  }
5367
4866
 
5368
4867
  // src/plugin/markAsset.ts
5369
- import { mkdir as mkdir6, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
5370
- import { existsSync as existsSync8 } from "fs";
5371
- import { isAbsolute as isAbsolute2, join as join8, resolve as resolve5 } from "path";
4868
+ import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
4869
+ import { existsSync as existsSync6 } from "fs";
4870
+ import { isAbsolute as isAbsolute2, join as join6, resolve as resolve5 } from "path";
5372
4871
  function markExtension(url) {
5373
4872
  const m = /\.(svg|png|webp|jpe?g)(?:[?#]|$)/i.exec(url);
5374
4873
  if (!m) return null;
@@ -5450,8 +4949,8 @@ async function fetchBrandMarks(takeDir, roles) {
5450
4949
  bytes = new Uint8Array(await res.arrayBuffer());
5451
4950
  } else {
5452
4951
  const file = isAbsolute2(src) ? src : resolve5(takeDir, src);
5453
- if (!existsSync8(file)) throw new Error("no such file");
5454
- bytes = new Uint8Array(await readFile8(file));
4952
+ if (!existsSync6(file)) throw new Error("no such file");
4953
+ bytes = new Uint8Array(await readFile6(file));
5455
4954
  }
5456
4955
  } catch (e) {
5457
4956
  out.notes.push(
@@ -5466,8 +4965,8 @@ async function fetchBrandMarks(takeDir, roles) {
5466
4965
  );
5467
4966
  return null;
5468
4967
  }
5469
- await mkdir6(join8(takeDir, "brand"), { recursive: true });
5470
- await writeFile8(join8(takeDir, "brand", `${name}.${ext}`), bytes);
4968
+ await mkdir4(join6(takeDir, "brand"), { recursive: true });
4969
+ await writeFile6(join6(takeDir, "brand", `${name}.${ext}`), bytes);
5471
4970
  return { key: `/brand/${name}.${ext}`, aspect };
5472
4971
  };
5473
4972
  out.light = await one("logoUrl", "mark");
@@ -5503,9 +5002,9 @@ async function fetchMusicCatalog(origin) {
5503
5002
  }
5504
5003
 
5505
5004
  // src/plugin/judge.ts
5506
- import { existsSync as existsSync9 } from "fs";
5507
- import { mkdir as mkdir7, readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
5508
- import { dirname as dirname2, isAbsolute as isAbsolute3, join as join9, resolve as resolve6 } from "path";
5005
+ import { existsSync as existsSync7 } from "fs";
5006
+ import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
5007
+ import { dirname, isAbsolute as isAbsolute3, join as join7, resolve as resolve6 } from "path";
5509
5008
  import { DESTINATIONS as DESTINATIONS2 } from "@vosjs/studio-core";
5510
5009
 
5511
5010
  // src/plugin/shotBake.ts
@@ -5676,28 +5175,28 @@ function composeSheet(left, right, height = 540, gutter = 32) {
5676
5175
  return { w, h, data: out };
5677
5176
  }
5678
5177
  async function judgeKit(kitPath, manifestPath, outDir) {
5679
- const kit = JSON.parse(await readFile9(kitPath, "utf8"));
5178
+ const kit = JSON.parse(await readFile7(kitPath, "utf8"));
5680
5179
  const manifest = JSON.parse(
5681
- await readFile9(manifestPath, "utf8")
5180
+ await readFile7(manifestPath, "utf8")
5682
5181
  );
5683
- const manifestDir = dirname2(resolve6(manifestPath));
5684
- const kitDir = dirname2(resolve6(kitPath));
5685
- const out = resolve6(outDir ?? join9(kitDir, "judge"));
5686
- await mkdir7(out, { recursive: true });
5182
+ const manifestDir = dirname(resolve6(manifestPath));
5183
+ const kitDir = dirname(resolve6(kitPath));
5184
+ const out = resolve6(outDir ?? join7(kitDir, "judge"));
5185
+ await mkdir5(out, { recursive: true });
5687
5186
  const sheets = [];
5688
5187
  const skipped = [];
5689
5188
  const refCache = /* @__PURE__ */ new Map();
5690
5189
  const loadRef = async (file) => {
5691
5190
  if (refCache.has(file)) return refCache.get(file) ?? null;
5692
- const p = join9(manifestDir, file);
5693
- const img = existsSync9(p) ? decodePng(new Uint8Array(await readFile9(p))) : null;
5191
+ const p = join7(manifestDir, file);
5192
+ const img = existsSync7(p) ? decodePng(new Uint8Array(await readFile7(p))) : null;
5694
5193
  refCache.set(file, img);
5695
5194
  return img;
5696
5195
  };
5697
5196
  for (const a of kit.assets) {
5698
5197
  if (!/\.png$/i.test(a.path)) continue;
5699
- const file = isAbsolute3(a.path) && existsSync9(a.path) ? a.path : join9(kitDir, a.path.split("/").pop() ?? a.path);
5700
- const img = existsSync9(file) ? decodePng(new Uint8Array(await readFile9(file))) : null;
5198
+ const file = isAbsolute3(a.path) && existsSync7(a.path) ? a.path : join7(kitDir, a.path.split("/").pop() ?? a.path);
5199
+ const img = existsSync7(file) ? decodePng(new Uint8Array(await readFile7(file))) : null;
5701
5200
  if (!img) {
5702
5201
  skipped.push(`${a.destination ?? a.path}: unreadable`);
5703
5202
  continue;
@@ -5728,8 +5227,8 @@ async function judgeKit(kitPath, manifestPath, outDir) {
5728
5227
  const id = stem.replace(/[^A-Za-z0-9_-]+/g, "-") || a.destination || "asset";
5729
5228
  const nameA = `${id}--A.png`;
5730
5229
  const nameB = `${id}--B.png`;
5731
- await writeFile9(join9(out, nameA), encodePng(composeSheet(img, refImg)));
5732
- await writeFile9(join9(out, nameB), encodePng(composeSheet(refImg, img)));
5230
+ await writeFile7(join7(out, nameA), encodePng(composeSheet(img, refImg)));
5231
+ await writeFile7(join7(out, nameB), encodePng(composeSheet(refImg, img)));
5733
5232
  const rubric = [
5734
5233
  `# ${id} against ${ref.id} (${ref.file})`,
5735
5234
  "",
@@ -5749,7 +5248,7 @@ async function judgeKit(kitPath, manifestPath, outDir) {
5749
5248
  "In judge.json: `win` true when the kit asset is at least as good as the reference on the rules that apply to its genre, false when it is worse, null for a tie; `reasons` names the rules by number.",
5750
5249
  ""
5751
5250
  ].join("\n");
5752
- await writeFile9(join9(out, `${id}.md`), rubric);
5251
+ await writeFile7(join7(out, `${id}.md`), rubric);
5753
5252
  sheets.push({
5754
5253
  asset: id,
5755
5254
  reference: ref.id,
@@ -5758,9 +5257,9 @@ async function judgeKit(kitPath, manifestPath, outDir) {
5758
5257
  rubric: `${id}.md`
5759
5258
  });
5760
5259
  }
5761
- const verdictFile = join9(out, "judge.json");
5762
- if (!existsSync9(verdictFile)) {
5763
- await writeFile9(
5260
+ const verdictFile = join7(out, "judge.json");
5261
+ if (!existsSync7(verdictFile)) {
5262
+ await writeFile7(
5764
5263
  verdictFile,
5765
5264
  JSON.stringify(
5766
5265
  {
@@ -5811,9 +5310,9 @@ function catalogFamily(family) {
5811
5310
  }
5812
5311
 
5813
5312
  // src/plugin/callout.ts
5814
- import { existsSync as existsSync10 } from "fs";
5815
- import { readFile as readFile10 } from "fs/promises";
5816
- import { join as join10 } from "path";
5313
+ import { existsSync as existsSync8 } from "fs";
5314
+ import { readFile as readFile8 } from "fs/promises";
5315
+ import { join as join8 } from "path";
5817
5316
  import { lerpArray, sample } from "@vosjs/timeline";
5818
5317
  import {
5819
5318
  CALLOUT_DEFAULT_BODY_PX,
@@ -6010,13 +5509,13 @@ async function footageFrames(browser, dir, doc, times, framesTake2) {
6010
5509
  const out = /* @__PURE__ */ new Map();
6011
5510
  if (!times.length) return out;
6012
5511
  const bare = { ...doc, overlays: [] };
6013
- const outDir = join10(dir, ".ground-check");
5512
+ const outDir = join8(dir, ".ground-check");
6014
5513
  const res = await framesTake2(browser, dir, { times, outDir, doc: bare });
6015
5514
  for (const f of res.frames) {
6016
- if (!existsSync10(f.file)) continue;
5515
+ if (!existsSync8(f.file)) continue;
6017
5516
  out.set(
6018
5517
  +f.time.toFixed(3),
6019
- decodePng(new Uint8Array(await readFile10(f.file)))
5518
+ decodePng(new Uint8Array(await readFile8(f.file)))
6020
5519
  );
6021
5520
  }
6022
5521
  return out;
@@ -6025,7 +5524,7 @@ async function footageFrames(browser, dir, doc, times, framesTake2) {
6025
5524
  // src/plugin/kitPicture.ts
6026
5525
  import { execFile } from "child_process";
6027
5526
  import { promisify } from "util";
6028
- import { readFile as readFile11 } from "fs/promises";
5527
+ import { readFile as readFile9 } from "fs/promises";
6029
5528
  var execFileP = promisify(execFile);
6030
5529
  var SUBJECT_BAND = { min: 0.6, max: 0.92 };
6031
5530
  var BLANK_INK = 0.12;
@@ -6301,7 +5800,7 @@ async function pictureChecks(assets) {
6301
5800
  let ffmpeg2 = null;
6302
5801
  for (const a of assets) {
6303
5802
  if (/\.png$/i.test(a.file)) {
6304
- const img = decodePng(new Uint8Array(await readFile11(a.file)));
5803
+ const img = decodePng(new Uint8Array(await readFile9(a.file)));
6305
5804
  if (!img) {
6306
5805
  findings.push({
6307
5806
  code: "unreadable",
@@ -6394,9 +5893,9 @@ function formatFinding(f) {
6394
5893
  }
6395
5894
 
6396
5895
  // src/plugin/validateKit.ts
6397
- import { existsSync as existsSync11 } from "fs";
6398
- import { readFile as readFile12, stat as stat3 } from "fs/promises";
6399
- import { dirname as dirname3, isAbsolute as isAbsolute4, join as join11 } from "path";
5896
+ import { existsSync as existsSync9 } from "fs";
5897
+ import { readFile as readFile10, stat as stat2 } from "fs/promises";
5898
+ import { dirname as dirname2, isAbsolute as isAbsolute4, join as join9 } from "path";
6400
5899
  import { DESTINATIONS as DESTINATIONS3 } from "@vosjs/studio-core";
6401
5900
  var PNG_SIG2 = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
6402
5901
  function pngDimensions(bytes) {
@@ -6415,7 +5914,7 @@ async function probeVideo(path) {
6415
5914
  const MB = await import("mediabunny");
6416
5915
  const input = new MB.Input({
6417
5916
  formats: MB.ALL_FORMATS,
6418
- source: new MB.BufferSource(new Uint8Array(await readFile12(path)))
5917
+ source: new MB.BufferSource(new Uint8Array(await readFile10(path)))
6419
5918
  });
6420
5919
  try {
6421
5920
  const track = await input.getPrimaryVideoTrack();
@@ -6436,7 +5935,7 @@ async function validateKit(kitPath, opts = {}) {
6436
5935
  const measured = [];
6437
5936
  let kit;
6438
5937
  try {
6439
- kit = JSON.parse(await readFile12(kitPath, "utf8"));
5938
+ kit = JSON.parse(await readFile10(kitPath, "utf8"));
6440
5939
  } catch (e) {
6441
5940
  return {
6442
5941
  valid: false,
@@ -6453,10 +5952,10 @@ async function validateKit(kitPath, opts = {}) {
6453
5952
  measured
6454
5953
  };
6455
5954
  }
6456
- const base = dirname3(kitPath);
5955
+ const base = dirname2(kitPath);
6457
5956
  const resolvePath = (p) => {
6458
- if (isAbsolute4(p) && existsSync11(p)) return p;
6459
- const beside = join11(base, p.split("/").pop() ?? p);
5957
+ if (isAbsolute4(p) && existsSync9(p)) return p;
5958
+ const beside = join9(base, p.split("/").pop() ?? p);
6460
5959
  return beside;
6461
5960
  };
6462
5961
  const perDestination = /* @__PURE__ */ new Map();
@@ -6467,7 +5966,7 @@ async function validateKit(kitPath, opts = {}) {
6467
5966
  const file = resolvePath(a.path);
6468
5967
  let bytes;
6469
5968
  try {
6470
- bytes = (await stat3(file)).size;
5969
+ bytes = (await stat2(file)).size;
6471
5970
  } catch {
6472
5971
  problems.push(`${label}: ${a.path} is missing`);
6473
5972
  continue;
@@ -6482,7 +5981,7 @@ async function validateKit(kitPath, opts = {}) {
6482
5981
  let h = null;
6483
5982
  let seconds = null;
6484
5983
  if (/\.(png|jpe?g|webp)$/i.test(file)) {
6485
- const head = Buffer.from(await readFile12(file));
5984
+ const head = Buffer.from(await readFile10(file));
6486
5985
  const kind = sniffImage(head);
6487
5986
  if (file.toLowerCase().endsWith(".png") && kind !== "png")
6488
5987
  problems.push(
@@ -6592,7 +6091,7 @@ async function validateKit(kitPath, opts = {}) {
6592
6091
 
6593
6092
  // src/plugin/recorder.ts
6594
6093
  import { writeFileSync } from "fs";
6595
- import { join as join12 } from "path";
6094
+ import { join as join10 } from "path";
6596
6095
  import { recordTake as recordTakeCore } from "@vosjs/render-core/record";
6597
6096
  async function recordTake(browser, url, actions, paths, log, opts = {}) {
6598
6097
  const dry = opts.dryRun === true;
@@ -6602,7 +6101,7 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
6602
6101
  actions,
6603
6102
  {
6604
6103
  frame: (file, bytes) => {
6605
- writeFileSync(join12(paths.framesDir, file), bytes);
6104
+ writeFileSync(join10(paths.framesDir, file), bytes);
6606
6105
  }
6607
6106
  },
6608
6107
  log,
@@ -6654,11 +6153,11 @@ import {
6654
6153
 
6655
6154
  // src/plugin/session.ts
6656
6155
  import { spawn } from "child_process";
6657
- import { existsSync as existsSync12, mkdirSync, readdirSync as readdirSync2, rmSync, statSync as statSync3 } from "fs";
6156
+ import { existsSync as existsSync10, mkdirSync, readdirSync, rmSync, statSync as statSync2 } from "fs";
6658
6157
  import { homedir } from "os";
6659
- import { join as join13 } from "path";
6158
+ import { join as join11 } from "path";
6660
6159
  import { chromium } from "playwright";
6661
- var SESSIONS_DIR = join13(homedir(), ".config", "vos", "sessions");
6160
+ var SESSIONS_DIR = join11(homedir(), ".config", "vos", "sessions");
6662
6161
  var NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
6663
6162
  function sessionNameVerdict(name) {
6664
6163
  if (!name) return "a session needs a name: --name <app>";
@@ -6667,20 +6166,20 @@ function sessionNameVerdict(name) {
6667
6166
  return null;
6668
6167
  }
6669
6168
  function sessionDir(name) {
6670
- return join13(SESSIONS_DIR, name);
6169
+ return join11(SESSIONS_DIR, name);
6671
6170
  }
6672
6171
  function systemChromePath() {
6673
6172
  const explicit = process.env.VOS_BROWSER_PATH;
6674
6173
  if (explicit) return explicit;
6675
6174
  const candidates = process.platform === "darwin" ? ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"] : process.platform === "win32" ? [
6676
- join13(
6175
+ join11(
6677
6176
  process.env["PROGRAMFILES"] ?? "C:\\Program Files",
6678
6177
  "Google",
6679
6178
  "Chrome",
6680
6179
  "Application",
6681
6180
  "chrome.exe"
6682
6181
  ),
6683
- join13(
6182
+ join11(
6684
6183
  process.env["LOCALAPPDATA"] ?? "",
6685
6184
  "Google",
6686
6185
  "Chrome",
@@ -6692,7 +6191,7 @@ function systemChromePath() {
6692
6191
  "/usr/bin/google-chrome-stable",
6693
6192
  "/opt/google/chrome/chrome"
6694
6193
  ];
6695
- const found = candidates.find((c) => existsSync12(c));
6194
+ const found = candidates.find((c) => existsSync10(c));
6696
6195
  if (!found)
6697
6196
  throw new Error(
6698
6197
  "no Google Chrome found: a session needs the system Chrome (the bundled Chromium cannot read a profile Chrome wrote). Install Chrome, or set VOS_BROWSER_PATH"
@@ -6723,7 +6222,7 @@ async function openSession(name, url, opts = { log: () => {
6723
6222
  }
6724
6223
  async function withSession(name, fn, opts = {}) {
6725
6224
  const dir = sessionDir(name);
6726
- if (!existsSync12(dir))
6225
+ if (!existsSync10(dir))
6727
6226
  throw new Error(`no session "${name}" (vos session list)`);
6728
6227
  const explicit = process.env.VOS_BROWSER_PATH;
6729
6228
  const ctx = await chromium.launchPersistentContext(dir, {
@@ -6742,7 +6241,7 @@ async function withSession(name, fn, opts = {}) {
6742
6241
  }
6743
6242
  async function summarizeSession(name) {
6744
6243
  const dir = sessionDir(name);
6745
- const created = statSync3(dir).birthtimeMs || statSync3(dir).mtimeMs;
6244
+ const created = statSync2(dir).birthtimeMs || statSync2(dir).mtimeMs;
6746
6245
  return withSession(name, async (ctx) => {
6747
6246
  const cookies = await ctx.cookies();
6748
6247
  const byHost = /* @__PURE__ */ new Map();
@@ -6772,9 +6271,9 @@ function summaryLine(s) {
6772
6271
  return `session "${s.name}" holds cookies for ${hosts}${more}; ${exp}`;
6773
6272
  }
6774
6273
  function listSessions() {
6775
- if (!existsSync12(SESSIONS_DIR)) return [];
6776
- return readdirSync2(SESSIONS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && NAME.test(d.name)).map((d) => {
6777
- const st = statSync3(join13(SESSIONS_DIR, d.name));
6274
+ if (!existsSync10(SESSIONS_DIR)) return [];
6275
+ return readdirSync(SESSIONS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && NAME.test(d.name)).map((d) => {
6276
+ const st = statSync2(join11(SESSIONS_DIR, d.name));
6778
6277
  return {
6779
6278
  name: d.name,
6780
6279
  ageDays: Math.floor(
@@ -6785,7 +6284,7 @@ function listSessions() {
6785
6284
  }
6786
6285
  function removeSession(name) {
6787
6286
  const dir = sessionDir(name);
6788
- if (!existsSync12(dir)) return false;
6287
+ if (!existsSync10(dir)) return false;
6789
6288
  rmSync(dir, { recursive: true, force: true });
6790
6289
  return true;
6791
6290
  }
@@ -6850,9 +6349,9 @@ async function encodeRecording(browser, takeDir, onProgress) {
6850
6349
  }
6851
6350
 
6852
6351
  // src/plugin/plan.ts
6853
- import { existsSync as existsSync13 } from "fs";
6854
- import { readFile as readFile13 } from "fs/promises";
6855
- import { join as join14 } from "path";
6352
+ import { existsSync as existsSync11 } from "fs";
6353
+ import { readFile as readFile11 } from "fs/promises";
6354
+ import { join as join12 } from "path";
6856
6355
  import {
6857
6356
  DEFAULT_FRAME_STYLE,
6858
6357
  STYLE_FIELDS,
@@ -7074,10 +6573,10 @@ function retimeCut(prev, newSteps, newDurationMs) {
7074
6573
  // src/plugin/plan.ts
7075
6574
  var overlaps = (a, b) => a.in < b.out && b.in < a.out;
7076
6575
  async function readDigestActivity(dir) {
7077
- const file = join14(dir, "digest", "digest.json");
7078
- if (!existsSync13(file)) return null;
6576
+ const file = join12(dir, "digest", "digest.json");
6577
+ if (!existsSync11(file)) return null;
7079
6578
  try {
7080
- const d = JSON.parse(await readFile13(file, "utf8"));
6579
+ const d = JSON.parse(await readFile11(file, "utf8"));
7081
6580
  return Array.isArray(d.activity) && d.activity.every((v) => typeof v === "number") ? d.activity : null;
7082
6581
  } catch {
7083
6582
  return null;
@@ -7290,16 +6789,16 @@ async function planTake(dir, opts = {}) {
7290
6789
  }
7291
6790
 
7292
6791
  // src/plugin/sync.ts
7293
- import { createHash as createHash2 } from "crypto";
7294
- import { existsSync as existsSync14, readFileSync as readFileSync2, readdirSync as readdirSync3 } from "fs";
7295
- import { readFile as readFile14 } from "fs/promises";
6792
+ import { createHash } from "crypto";
6793
+ import { existsSync as existsSync12, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
6794
+ import { readFile as readFile12 } from "fs/promises";
7296
6795
  import { createInterface } from "readline/promises";
7297
- import { basename as basename3, join as join16 } from "path";
6796
+ import { basename, join as join14 } from "path";
7298
6797
  import { lowerToComposition as lowerToComposition3, migrateHostedDoc as migrateHostedDoc3 } from "@vosjs/studio-core";
7299
6798
 
7300
6799
  // src/plugin/folder.ts
7301
- import { mkdir as mkdir8, writeFile as writeFile10 } from "fs/promises";
7302
- import { join as join15 } from "path";
6800
+ import { mkdir as mkdir6, writeFile as writeFile8 } from "fs/promises";
6801
+ import { join as join13 } from "path";
7303
6802
  import { recipeHints } from "@vosjs/shared/frontmatter";
7304
6803
  import { migrateHostedDoc as migrateHostedDoc2 } from "@vosjs/studio-core";
7305
6804
  var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["json", "help", "media"]);
@@ -7409,7 +6908,7 @@ async function cmdMove(argv) {
7409
6908
  });
7410
6909
  const key = requireCredential(strFlag(flags, "key"));
7411
6910
  const folderId = toRef === "none" ? null : resolveFolder(await listFolders(origin, key), toRef).id;
7412
- let failed2 = 0;
6911
+ let failed = 0;
7413
6912
  const moved = [];
7414
6913
  for (const raw of positionals) {
7415
6914
  const id = parseVosId(raw);
@@ -7424,28 +6923,28 @@ async function cmdMove(argv) {
7424
6923
  continue;
7425
6924
  }
7426
6925
  if (asVos.status === 404) {
7427
- const asAsset2 = await apiJson(origin, `/api/assets/${id}`, {
6926
+ const asAsset = await apiJson(origin, `/api/assets/${id}`, {
7428
6927
  method: "PATCH",
7429
6928
  key,
7430
6929
  body: { folderId }
7431
6930
  });
7432
- if (asAsset2.status === 200) {
6931
+ if (asAsset.status === 200) {
7433
6932
  moved.push({ id, kind: "asset" });
7434
6933
  r.log(`moved asset ${id}`);
7435
6934
  continue;
7436
6935
  }
7437
- failed2++;
7438
- r.log(apiError(`move ${id}`, asAsset2));
6936
+ failed++;
6937
+ r.log(apiError(`move ${id}`, asAsset));
7439
6938
  continue;
7440
6939
  }
7441
- failed2++;
6940
+ failed++;
7442
6941
  r.log(apiError(`move ${id}`, asVos));
7443
6942
  }
7444
6943
  r.done(
7445
- { moved, failed: failed2, folderId },
7446
- `moved ${moved.length}/${positionals.length} into ${toRef === "none" ? "the top level" : toRef}${failed2 ? ` \u2014 ${failed2} failed` : ""}`
6944
+ { moved, failed, folderId },
6945
+ `moved ${moved.length}/${positionals.length} into ${toRef === "none" ? "the top level" : toRef}${failed ? ` \u2014 ${failed} failed` : ""}`
7447
6946
  );
7448
- return failed2 ? EXIT_ERROR : EXIT_OK;
6947
+ return failed ? EXIT_ERROR : EXIT_OK;
7449
6948
  }
7450
6949
  function recipeLine(filename, body) {
7451
6950
  const h = recipeHints(body);
@@ -7475,25 +6974,25 @@ async function cmdPull(argv) {
7475
6974
  throw new Error(apiError(`pull folder ${folder.slug}`, res));
7476
6975
  const payload = res.body;
7477
6976
  const out = strFlag(flags, "out") ?? folder.slug;
7478
- await mkdir8(join15(out, "recipes"), { recursive: true });
6977
+ await mkdir6(join13(out, "recipes"), { recursive: true });
7479
6978
  const lines = [];
7480
6979
  const recipes = [];
7481
6980
  for (const rec of payload.recipes) {
7482
- const path = join15(out, "recipes", safeName(rec.filename));
7483
- await writeFile10(path, rec.body ?? "");
6981
+ const path = join13(out, "recipes", safeName(rec.filename));
6982
+ await writeFile8(path, rec.body ?? "");
7484
6983
  recipes.push({ path, line: recipeLine(rec.filename, rec.body ?? "") });
7485
6984
  }
7486
6985
  for (const rec of payload.inheritedRecipes) {
7487
6986
  const from = safeName(rec.folderSlug ?? "inherited");
7488
- await mkdir8(join15(out, "recipes", "_inherited", from), { recursive: true });
7489
- const path = join15(
6987
+ await mkdir6(join13(out, "recipes", "_inherited", from), { recursive: true });
6988
+ const path = join13(
7490
6989
  out,
7491
6990
  "recipes",
7492
6991
  "_inherited",
7493
6992
  from,
7494
6993
  safeName(rec.filename)
7495
6994
  );
7496
- await writeFile10(path, rec.body ?? "");
6995
+ await writeFile8(path, rec.body ?? "");
7497
6996
  recipes.push({
7498
6997
  path,
7499
6998
  line: `${recipeLine(rec.filename, rec.body ?? "")} [inherited from ${rec.folderName ?? from}]`
@@ -7501,15 +7000,15 @@ async function cmdPull(argv) {
7501
7000
  }
7502
7001
  const members = [];
7503
7002
  for (const v of payload.voses) {
7504
- const dir = join15(out, "members", safeName(v.slug || v.title || v.id));
7505
- await mkdir8(dir, { recursive: true });
7003
+ const dir = join13(out, "members", safeName(v.slug || v.title || v.id));
7004
+ await mkdir6(dir, { recursive: true });
7506
7005
  const cfg = await apiJson(origin, `/api/vos/${v.id}/config`, { key });
7507
7006
  if (cfg.status !== 200) {
7508
7007
  lines.push(`skipped ${v.title}: ${apiError("fetch config", cfg)}`);
7509
7008
  continue;
7510
7009
  }
7511
- await writeFile10(
7512
- join15(dir, "config.json"),
7010
+ await writeFile8(
7011
+ join13(dir, "config.json"),
7513
7012
  JSON.stringify(cfg.body.config, null, 2)
7514
7013
  );
7515
7014
  let take = false;
@@ -7520,8 +7019,8 @@ async function cmdPull(argv) {
7520
7019
  { key }
7521
7020
  );
7522
7021
  if (doc.status === 200) {
7523
- await writeFile10(
7524
- join15(dir, "doc.json"),
7022
+ await writeFile8(
7023
+ join13(dir, "doc.json"),
7525
7024
  JSON.stringify(doc.body, null, 2)
7526
7025
  );
7527
7026
  take = true;
@@ -7653,12 +7152,12 @@ function programPushTarget(ask) {
7653
7152
  return { kind: "version", vosId: tracked, tracked: true };
7654
7153
  }
7655
7154
  function storageStateInDir(dir) {
7656
- for (const f of readdirSync3(dir)) {
7155
+ for (const f of readdirSync2(dir)) {
7657
7156
  if (!f.endsWith(".json") || f === "doc.json" || f === "meta.json" || f === "actions.json" || f === "cursor.json" || f === "vos.json" || f === "doc.prev.json")
7658
7157
  continue;
7659
7158
  try {
7660
- const v = JSON.parse(readFileSync2(join16(dir, f), "utf8"));
7661
- if (looksLikeStorageState(v)) return join16(dir, f);
7159
+ const v = JSON.parse(readFileSync2(join14(dir, f), "utf8"));
7160
+ if (looksLikeStorageState(v)) return join14(dir, f);
7662
7161
  } catch {
7663
7162
  }
7664
7163
  }
@@ -7679,7 +7178,7 @@ async function pushTake(dir, flags, r) {
7679
7178
  throw new Error(`doc.json fails lint:
7680
7179
  ${lint.problems.join("\n ")}`);
7681
7180
  }
7682
- if (!existsSync14(take.paths.recording)) {
7181
+ if (!existsSync12(take.paths.recording)) {
7683
7182
  throw new Error(
7684
7183
  "no recording in this take \u2014 record it, or `vos pull --media` to bring it home"
7685
7184
  );
@@ -7728,8 +7227,8 @@ async function pushTake(dir, flags, r) {
7728
7227
  throw new Error("push cancelled");
7729
7228
  }
7730
7229
  }
7731
- const bytes = await readFile14(take.paths.recording);
7732
- const hash = createHash2("sha256").update(bytes).digest("hex");
7230
+ const bytes = await readFile12(take.paths.recording);
7231
+ const hash = createHash("sha256").update(bytes).digest("hex");
7733
7232
  const { type: contentType } = resolveMediaType({
7734
7233
  head: bytes.subarray(0, MEDIA_HEAD_BYTES),
7735
7234
  filename: take.paths.recordingName
@@ -7776,7 +7275,7 @@ async function pushTake(dir, flags, r) {
7776
7275
  const lowered = lowerToComposition3(docForPush);
7777
7276
  const config = { ...lowered.config, data: lowered.data };
7778
7277
  if (!state) {
7779
- const title = flags.title ?? `Take ${basename3(dir)} ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
7278
+ const title = flags.title ?? `Take ${basename(dir)} ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
7780
7279
  const slug = `${title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "take"}-${Date.now().toString(36)}`;
7781
7280
  const created = await api(ctx, "/vos", {
7782
7281
  method: "POST",
@@ -7888,9 +7387,9 @@ async function pullTake(dir, flags, r) {
7888
7387
  };
7889
7388
  }
7890
7389
  let media2;
7891
- if (flags.media && existsSync14(join16(dir, "doc.json"))) {
7390
+ if (flags.media && existsSync12(join14(dir, "doc.json"))) {
7892
7391
  const local = JSON.parse(
7893
- await readFile14(join16(dir, "doc.json"), "utf8")
7392
+ await readFile12(join14(dir, "doc.json"), "utf8")
7894
7393
  );
7895
7394
  media2 = await pullMedia(ctx, dir, local, r.log);
7896
7395
  }
@@ -7951,10 +7450,10 @@ async function pullTake(dir, flags, r) {
7951
7450
  const hostedDoc = migrateHostedDoc3(docRes.json);
7952
7451
  const doc = hostedDoc;
7953
7452
  const localRecording = takeMediaFile(dir, "recording", RECORDING_NAME);
7954
- if (existsSync14(localRecording)) {
7955
- doc.source.videoKey = basename3(localRecording);
7453
+ if (existsSync12(localRecording)) {
7454
+ doc.source.videoKey = basename(localRecording);
7956
7455
  }
7957
- await writeJson(join16(dir, "doc.json"), doc, true);
7456
+ await writeJson(join14(dir, "doc.json"), doc, true);
7958
7457
  const media = flags.media ? await pullMedia(ctx, dir, doc, r.log) : void 0;
7959
7458
  const versions = await api(ctx, `/vos/${vosId}/versions`);
7960
7459
  const headRow = (versions.json.versions ?? []).find((v) => v.id === head);
@@ -7973,10 +7472,10 @@ async function pullTake(dir, flags, r) {
7973
7472
  }
7974
7473
 
7975
7474
  // src/plugin/program.ts
7976
- import { existsSync as existsSync15 } from "fs";
7977
- import { mkdir as mkdir9, readFile as readFile15, writeFile as writeFile11 } from "fs/promises";
7475
+ import { existsSync as existsSync13 } from "fs";
7476
+ import { mkdir as mkdir7, readFile as readFile13, writeFile as writeFile9 } from "fs/promises";
7978
7477
  import { createInterface as createInterface2 } from "readline/promises";
7979
- import { basename as basename4, dirname as dirname4, join as join17 } from "path";
7478
+ import { basename as basename2, dirname as dirname3, join as join15 } from "path";
7980
7479
  import {
7981
7480
  CURRENT_CONFIG_VERSION,
7982
7481
  migrateConfig,
@@ -8182,8 +7681,8 @@ function preflightConfig(parsed) {
8182
7681
  }
8183
7682
  function resolveConfigPath(target) {
8184
7683
  if (target.endsWith(".json")) return target;
8185
- const inDir = join17(target, "config.json");
8186
- if (existsSync15(inDir)) return inDir;
7684
+ const inDir = join15(target, "config.json");
7685
+ if (existsSync13(inDir)) return inDir;
8187
7686
  throw new UsageError(
8188
7687
  `${target} has no config.json (and is not a take \u2014 no doc.json)`
8189
7688
  );
@@ -8209,9 +7708,9 @@ async function cmdFetch(argv) {
8209
7708
  throw new Error(apiError(`fetch config for ${vosId}`, cfg));
8210
7709
  const slug = typeof vosMeta.slug === "string" && vosMeta.slug ? vosMeta.slug : vosId;
8211
7710
  const out = strFlag(flags, "out") ?? slug;
8212
- await mkdir9(out, { recursive: true });
8213
- await writeFile11(
8214
- join17(out, "config.json"),
7711
+ await mkdir7(out, { recursive: true });
7712
+ await writeFile9(
7713
+ join15(out, "config.json"),
8215
7714
  JSON.stringify(cfg.body.config, null, 2)
8216
7715
  );
8217
7716
  const title = typeof vosMeta.title === "string" ? vosMeta.title : "";
@@ -8236,18 +7735,40 @@ async function cmdFetch(argv) {
8236
7735
  const hosted = migrateHostedDoc4(doc.body);
8237
7736
  const { config: own } = await writeProgramDoc(out, hosted);
8238
7737
  if (own) {
8239
- await writeFile11(join17(out, "config.json"), JSON.stringify(own, null, 2));
7738
+ await writeFile9(join15(out, "config.json"), JSON.stringify(own, null, 2));
8240
7739
  }
8241
7740
  } else if (doc.status === 200) {
8242
7741
  take = true;
8243
7742
  const hosted = migrateHostedDoc4(doc.body);
8244
- await writeFile11(join17(out, "doc.json"), JSON.stringify(hosted, null, 2));
7743
+ await writeFile9(join15(out, "doc.json"), JSON.stringify(hosted, null, 2));
8245
7744
  if (flags.media === true) {
8246
7745
  const media = await pullMedia({ origin, key }, out, hosted, r.log);
8247
7746
  mediaLine = media.downloaded.length ? ` + ${media.downloaded.map((m) => m.file).join(", ")}` : "";
8248
7747
  }
8249
7748
  }
8250
7749
  }
7750
+ let assetsLine = "";
7751
+ if (!take) {
7752
+ const configPath = join15(out, "config.json");
7753
+ const written = JSON.parse(await readFile13(configPath, "utf8"));
7754
+ const hostedCount = manifestRefs(written).filter(
7755
+ ({ ref }) => hostedAssetId(ref)
7756
+ ).length;
7757
+ if (hostedCount && flags.media === true) {
7758
+ const pulled = await pullManifest({ origin, key }, out, written, r.log);
7759
+ if (pulled) {
7760
+ await writeFile9(
7761
+ configPath,
7762
+ JSON.stringify({ ...written, assets: pulled.assets }, null, 2)
7763
+ );
7764
+ assetsLine = `
7765
+ ${pulled.files.length} declared file${pulled.files.length === 1 ? "" : "s"} in ${out}/assets`;
7766
+ }
7767
+ } else if (hostedCount) {
7768
+ assetsLine = `
7769
+ ${hostedCount} declared file${hostedCount === 1 ? " is" : "s are"} hosted (a render reads ${hostedCount === 1 ? "it" : "them"} with your key). Add --media to bring ${hostedCount === 1 ? "it" : "them"} home`;
7770
+ }
7771
+ }
8251
7772
  r.done(
8252
7773
  {
8253
7774
  out,
@@ -8258,7 +7779,7 @@ async function cmdFetch(argv) {
8258
7779
  take
8259
7780
  },
8260
7781
  take ? `Wrote ${out}/doc.json + config.json + vos.json${mediaLine} (${title || id}, a take)
8261
- ` + (flags.media === true ? `Then: vos digest ${out} \u2014 look before you cut; edit doc.json; vos validate ${out}; vos push ${out}` : `Add --media to bring the recording home (digest/frames/render need it); then edit doc.json and vos push ${out}`) : `Wrote ${out}/config.json + vos.json (${title || id})
7782
+ ` + (flags.media === true ? `Then: vos digest ${out} \u2014 look before you cut; edit doc.json; vos validate ${out}; vos push ${out}` : `Add --media to bring the recording home (digest/frames/render need it); then edit doc.json and vos push ${out}`) : `Wrote ${out}/config.json + vos.json (${title || id})${assetsLine}
8262
7783
  Edit config.json, then: vos check ${out}/config.json && vos push ${out}/config.json`
8263
7784
  );
8264
7785
  return EXIT_OK;
@@ -8354,8 +7875,8 @@ async function cmdPushProgram(argv) {
8354
7875
  origin: strFlag(flags, "origin"),
8355
7876
  api: strFlag(flags, "api")
8356
7877
  });
8357
- const dir = dirname4(source);
8358
- const parsed = JSON.parse(await readFile15(source, "utf8"));
7878
+ const dir = dirname3(source);
7879
+ const parsed = JSON.parse(await readFile13(source, "utf8"));
8359
7880
  const pre = preflightConfig(parsed);
8360
7881
  if (!pre.ok || !pre.config) {
8361
7882
  for (const issue of pre.issues) r.log(`error ${issue}`);
@@ -8375,6 +7896,7 @@ async function cmdPushProgram(argv) {
8375
7896
  await setProgramStill(dir, t);
8376
7897
  r.log(`cover: ${t}s (doc.json "still")`);
8377
7898
  }
7899
+ for (const w of hostedLiteralWarnings(config)) r.log(`warning ${w}`);
8378
7900
  const programDoc = await readProgramDoc(dir, config);
8379
7901
  if (programDoc) {
8380
7902
  const lint = lintDoc(programDoc);
@@ -8390,7 +7912,7 @@ async function cmdPushProgram(argv) {
8390
7912
  "--claimable creates a NEW claimable vos and cannot iterate (--vos). After the human claims it, iterate with their key: vos push --vos <id>"
8391
7913
  );
8392
7914
  }
8393
- const fallback = state?.title ? `${state.title} remix` : basename4(source).replace(/\.json$/i, "") || "vos program";
7915
+ const fallback = state?.title ? `${state.title} remix` : basename2(source).replace(/\.json$/i, "") || "vos program";
8394
7916
  const title2 = (strFlag(flags, "title") ?? fallback).slice(0, 100);
8395
7917
  let claimDoc = programDoc;
8396
7918
  if (programDoc) {
@@ -8407,6 +7929,12 @@ async function cmdPushProgram(argv) {
8407
7929
  );
8408
7930
  }
8409
7931
  }
7932
+ const stay = localManifestRefs(config);
7933
+ if (stay.length) {
7934
+ throw new UsageError(
7935
+ `a claimable push carries no files, and this program declares ${stay.length} local file${stay.length === 1 ? "" : "s"} (${stay.join(", ")}). Push with a key instead (vos login, then vos push ${source}), which uploads ${stay.length === 1 ? "it" : "them"}`
7936
+ );
7937
+ }
8410
7938
  const body = {
8411
7939
  title: title2,
8412
7940
  config: storedProgramConfig(config, claimDoc)
@@ -8440,6 +7968,7 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
8440
7968
  (l) => r.log(l)
8441
7969
  );
8442
7970
  }
7971
+ await uploadManifest(config, dir, { origin, key }, (l) => r.log(l));
8443
7972
  const overrides = [
8444
7973
  ...multi.override ?? [],
8445
7974
  ...strFlag(flags, "overrides")?.split(",") ?? []
@@ -8539,7 +8068,7 @@ ${landedLines(landed2)}` : "")
8539
8068
  return EXIT_OK;
8540
8069
  }
8541
8070
  const remixOfId = pushTarget.kind === "create" ? pushTarget.remixOfId : void 0;
8542
- const fallbackTitle = state?.title ? `${state.title} remix` : basename4(source).replace(/\.json$/i, "") || "vos remix";
8071
+ const fallbackTitle = state?.title ? `${state.title} remix` : basename2(source).replace(/\.json$/i, "") || "vos remix";
8543
8072
  const title = (strFlag(flags, "title") ?? fallbackTitle).slice(0, 100);
8544
8073
  const folderId = folderRef ? resolveFolder(await listFolders(origin, key), folderRef).id : void 0;
8545
8074
  const created = await createProgramVos({
@@ -8607,7 +8136,7 @@ Iterate with: vos push ${source} --vos ${created.id}`
8607
8136
  async function cmdPullProgram(argv) {
8608
8137
  const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS2);
8609
8138
  const target = positionals[0] ?? ".";
8610
- const dir = target.endsWith(".json") ? dirname4(target) : target;
8139
+ const dir = target.endsWith(".json") ? dirname3(target) : target;
8611
8140
  const r = createReporter(flags.json === true);
8612
8141
  const origin = platformOrigin({
8613
8142
  origin: strFlag(flags, "origin"),
@@ -8671,10 +8200,10 @@ async function cmdPullProgram(argv) {
8671
8200
  const cfg = await apiJson(origin, `/api/vos/${vosId}/config`, { key });
8672
8201
  if (cfg.status !== 200)
8673
8202
  throw new Error(apiError(`fetch head config for ${vosId}`, cfg));
8674
- const configPath = join17(dir, "config.json");
8203
+ const configPath = join15(dir, "config.json");
8675
8204
  let backedUp = false;
8676
- if (existsSync15(configPath)) {
8677
- await writeFile11(join17(dir, "config.backup.json"), await readFile15(configPath));
8205
+ if (existsSync13(configPath)) {
8206
+ await writeFile9(join15(dir, "config.backup.json"), await readFile13(configPath));
8678
8207
  backedUp = true;
8679
8208
  }
8680
8209
  let headConfig = cfg.body.config;
@@ -8693,7 +8222,7 @@ async function cmdPullProgram(argv) {
8693
8222
  if (own) headConfig = own;
8694
8223
  }
8695
8224
  }
8696
- await writeFile11(configPath, JSON.stringify(headConfig, null, 2));
8225
+ await writeFile9(configPath, JSON.stringify(headConfig, null, 2));
8697
8226
  writeSyncState(dir, {
8698
8227
  vosId,
8699
8228
  versionId: typeof head.id === "string" ? head.id : since
@@ -8707,7 +8236,7 @@ async function cmdPullProgram(argv) {
8707
8236
  protected: protectedIds,
8708
8237
  changes,
8709
8238
  out: configPath,
8710
- backup: backedUp ? join17(dir, "config.backup.json") : null
8239
+ backup: backedUp ? join15(dir, "config.backup.json") : null
8711
8240
  },
8712
8241
  `Pulled ${changes.length} version${changes.length === 1 ? "" : "s"} \u2192 ${configPath}` + (backedUp ? ` (previous copy: config.backup.json)` : "") + `
8713
8242
  Re-apply your edit on the new head, then: vos push ${configPath} --vos ${vosId}`
@@ -8794,9 +8323,9 @@ function storedProgramConfig(config, programDoc) {
8794
8323
  return lowerProgramDoc(doc).config;
8795
8324
  }
8796
8325
  async function readProgramDoc(dir, config) {
8797
- const docPath = join17(dir, "doc.json");
8798
- if (!existsSync15(docPath)) return null;
8799
- const parsed = JSON.parse(await readFile15(docPath, "utf8"));
8326
+ const docPath = join15(dir, "doc.json");
8327
+ if (!existsSync13(docPath)) return null;
8328
+ const parsed = JSON.parse(await readFile13(docPath, "utf8"));
8800
8329
  if (typeof parsed !== "object" || parsed === null || "source" in parsed)
8801
8330
  return null;
8802
8331
  const raw = parsed;
@@ -8804,32 +8333,32 @@ async function readProgramDoc(dir, config) {
8804
8333
  return { ...raw, program: { ...program, config } };
8805
8334
  }
8806
8335
  async function setProgramStill(dir, t) {
8807
- const docPath = join17(dir, "doc.json");
8336
+ const docPath = join15(dir, "doc.json");
8808
8337
  let doc = { program: {}, audio: [] };
8809
- if (existsSync15(docPath)) {
8810
- const parsed = JSON.parse(await readFile15(docPath, "utf8"));
8338
+ if (existsSync13(docPath)) {
8339
+ const parsed = JSON.parse(await readFile13(docPath, "utf8"));
8811
8340
  if (!parsed || typeof parsed !== "object" || "source" in parsed)
8812
8341
  throw new UsageError(
8813
8342
  `${docPath} is a take's document; a take's cover is its own "still" field`
8814
8343
  );
8815
8344
  doc = parsed;
8816
8345
  }
8817
- await writeFile11(docPath, JSON.stringify({ ...doc, still: t }, null, 2) + "\n");
8346
+ await writeFile9(docPath, JSON.stringify({ ...doc, still: t }, null, 2) + "\n");
8818
8347
  }
8819
8348
  async function writeProgramDoc(dir, hosted) {
8820
8349
  const program = hosted.program && typeof hosted.program === "object" ? hosted.program : {};
8821
8350
  const { config, ...rest } = program;
8822
8351
  const onDisk = { ...hosted, program: rest };
8823
- await writeFile11(join17(dir, "doc.json"), JSON.stringify(onDisk, null, 2));
8352
+ await writeFile9(join15(dir, "doc.json"), JSON.stringify(onDisk, null, 2));
8824
8353
  return {
8825
8354
  config: config && typeof config === "object" ? config : null
8826
8355
  };
8827
8356
  }
8828
8357
 
8829
8358
  // src/plugin/asset.ts
8830
- import { createHash as createHash3 } from "crypto";
8359
+ import { createHash as createHash2 } from "crypto";
8831
8360
  import { readFileSync as readFileSync3 } from "fs";
8832
- import { basename as basename5, extname as extname2 } from "path";
8361
+ import { basename as basename3, extname as extname2 } from "path";
8833
8362
  var BOOLEAN_FLAGS3 = /* @__PURE__ */ new Set(["json", "help"]);
8834
8363
  var MIME_BY_EXT = {
8835
8364
  ".png": "image/png",
@@ -8881,7 +8410,7 @@ async function cmdPush(argv) {
8881
8410
  const uploaded = [];
8882
8411
  let unfiledMedia = 0;
8883
8412
  for (const file of positionals) {
8884
- const name = basename5(file);
8413
+ const name = basename3(file);
8885
8414
  const body = readFileSync3(file);
8886
8415
  const media = resolveMediaType({
8887
8416
  head: body.subarray(0, MEDIA_HEAD_BYTES),
@@ -8894,7 +8423,7 @@ async function cmdPush(argv) {
8894
8423
  put = await uploadAsset({ origin, key }, new Uint8Array(body), {
8895
8424
  filename: sent,
8896
8425
  contentType: media.type,
8897
- contentHash: createHash3("sha256").update(body).digest("hex"),
8426
+ contentHash: createHash2("sha256").update(body).digest("hex"),
8898
8427
  ...folder ? { folderId: folder.id } : {}
8899
8428
  });
8900
8429
  } catch (e) {
@@ -8965,8 +8494,8 @@ async function cmdAsset(argv) {
8965
8494
  }
8966
8495
 
8967
8496
  // src/plugin/remove.ts
8968
- import { existsSync as existsSync16, rmSync as rmSync2, statSync as statSync4 } from "fs";
8969
- import { join as join18 } from "path";
8497
+ import { existsSync as existsSync14, rmSync as rmSync2, statSync as statSync3 } from "fs";
8498
+ import { join as join16 } from "path";
8970
8499
  import { createInterface as createInterface3 } from "readline/promises";
8971
8500
 
8972
8501
  // src/plugin/trash.ts
@@ -9145,7 +8674,7 @@ async function cmdDelete(argv) {
9145
8674
  api: strFlag(flags, "api")
9146
8675
  }).replace(/\/+$/, "");
9147
8676
  const key = requireCredential(strFlag(flags, "key"));
9148
- const dir = existsSync16(target) && statSync4(target).isDirectory() ? target : null;
8677
+ const dir = existsSync14(target) && statSync3(target).isDirectory() ? target : null;
9149
8678
  const vosId = dir ? readSyncState(dir)?.vosId : parseVosId(target);
9150
8679
  if (!vosId)
9151
8680
  throw new UsageError(
@@ -9187,7 +8716,7 @@ async function cmdDelete(argv) {
9187
8716
  throw new Error(apiError(`delete ${vosId}`, res));
9188
8717
  let unlinked = false;
9189
8718
  if (dir) {
9190
- rmSync2(join18(dir, SYNC_STATE_NAME), { force: true });
8719
+ rmSync2(join16(dir, SYNC_STATE_NAME), { force: true });
9191
8720
  unlinked = true;
9192
8721
  }
9193
8722
  const trashed = res.body.trashed === true;
@@ -9213,7 +8742,7 @@ async function cmdDelete(argv) {
9213
8742
 
9214
8743
  // src/plugin/buildProgram.ts
9215
8744
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
9216
- import { basename as basename6, dirname as dirname5, join as join19, resolve as resolve7 } from "path";
8745
+ import { basename as basename4, dirname as dirname4, join as join17, resolve as resolve7 } from "path";
9217
8746
  import { pathToFileURL } from "url";
9218
8747
  import { parse } from "acorn";
9219
8748
  import { fullAncestor } from "acorn-walk";
@@ -9421,7 +8950,7 @@ async function cmdBuild(argv) {
9421
8950
  throw new UsageError(
9422
8951
  `${source} exports no program: \`export default { version: 2, duration, camera, createContent, createTimeline, \u2026 }\``
9423
8952
  );
9424
- const built = buildProgram(program, basename6(file), text);
8953
+ const built = buildProgram(program, basename4(file), text);
9425
8954
  for (const p of built.problems) r.log(`error ${p.fn}: ${p.message}`);
9426
8955
  if (built.problems.length) {
9427
8956
  r.done(
@@ -9431,7 +8960,7 @@ async function cmdBuild(argv) {
9431
8960
  return EXIT_ERROR;
9432
8961
  }
9433
8962
  const out = resolve7(
9434
- strFlag(flags, "out") ?? join19(dirname5(file), "config.json")
8963
+ strFlag(flags, "out") ?? join17(dirname4(file), "config.json")
9435
8964
  );
9436
8965
  writeFileSync2(out, JSON.stringify(built.config, null, 2) + "\n");
9437
8966
  const sidecar = out.replace(/\.json$/, "") + ".sources.json";
@@ -9448,8 +8977,8 @@ async function cmdBuild(argv) {
9448
8977
 
9449
8978
  // src/plugin/compare.ts
9450
8979
  import { spawnSync } from "child_process";
9451
- import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
9452
- import { join as join20, resolve as resolve8 } from "path";
8980
+ import { existsSync as existsSync15, mkdirSync as mkdirSync2, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
8981
+ import { dirname as dirname5, join as join18, resolve as resolve8 } from "path";
9453
8982
  var BOOLEAN_FLAGS7 = /* @__PURE__ */ new Set(["json", "help"]);
9454
8983
  function sampleTimes(duration, every) {
9455
8984
  const step = every > 0 ? every : 1;
@@ -9515,6 +9044,12 @@ async function cmdCompare(argv) {
9515
9044
  for (const w of loaded.warnings) r.log(`note: ${w}`);
9516
9045
  const config = loaded.config;
9517
9046
  const duration = configDuration(config) ?? 5;
9047
+ const assets = await programAssets(config, {
9048
+ baseDir: existsSync15(source) && statSync4(source).isDirectory() ? source : dirname5(source),
9049
+ origin: platformOrigin({}),
9050
+ key: resolveCredential(),
9051
+ log: (line) => r.log(line)
9052
+ });
9518
9053
  const probed = probeSize(ref);
9519
9054
  const half = probed && probed.width > 1280 ? 2 : 1;
9520
9055
  const width = Number(
@@ -9533,9 +9068,9 @@ async function cmdCompare(argv) {
9533
9068
  try {
9534
9069
  for (const t of times) {
9535
9070
  const tag = t.toFixed(2);
9536
- const refPng = join20(outDir, `src-${tag}.png`);
9537
- const vosPng = join20(outDir, `vos-${tag}.png`);
9538
- const sheet = join20(outDir, `sheet-${tag}.png`);
9071
+ const refPng = join18(outDir, `src-${tag}.png`);
9072
+ const vosPng = join18(outDir, `vos-${tag}.png`);
9073
+ const sheet = join18(outDir, `sheet-${tag}.png`);
9539
9074
  const grab = ffmpeg([
9540
9075
  "-v",
9541
9076
  "error",
@@ -9556,6 +9091,7 @@ async function cmdCompare(argv) {
9556
9091
  );
9557
9092
  const still = await renderStill(browser, {
9558
9093
  config,
9094
+ assets,
9559
9095
  width,
9560
9096
  height,
9561
9097
  time: t
@@ -9591,7 +9127,7 @@ async function cmdCompare(argv) {
9591
9127
  } finally {
9592
9128
  await browser.close();
9593
9129
  }
9594
- const overview = join20(outDir, "overview.png");
9130
+ const overview = join18(outDir, "overview.png");
9595
9131
  if (scores.length) {
9596
9132
  const inputs = scores.flatMap((s) => ["-i", s.sheet]);
9597
9133
  const scaled = scores.map((_, i) => `[${i}:v]scale=iw/3:-2[s${i}]`).join(";");
@@ -9617,7 +9153,7 @@ async function cmdCompare(argv) {
9617
9153
  overview
9618
9154
  };
9619
9155
  writeFileSync3(
9620
- join20(outDir, "report.json"),
9156
+ join18(outDir, "report.json"),
9621
9157
  JSON.stringify(report, null, 2) + "\n"
9622
9158
  );
9623
9159
  r.done(
@@ -9628,28 +9164,28 @@ async function cmdCompare(argv) {
9628
9164
  }
9629
9165
 
9630
9166
  // src/plugin/port.ts
9631
- import { existsSync as existsSync20, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
9632
- import { dirname as dirname8, join as join24, resolve as resolve11 } from "path";
9167
+ import { existsSync as existsSync19, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
9168
+ import { dirname as dirname8, join as join22, resolve as resolve11 } from "path";
9633
9169
  import { pathToFileURL as pathToFileURL3 } from "url";
9634
9170
 
9635
9171
  // src/plugin/portInventory.ts
9636
9172
  import { spawnSync as spawnSync2 } from "child_process";
9637
9173
  import {
9638
- existsSync as existsSync18,
9174
+ existsSync as existsSync17,
9639
9175
  mkdirSync as mkdirSync3,
9640
9176
  readFileSync as readFileSync6,
9641
- readdirSync as readdirSync5,
9177
+ readdirSync as readdirSync4,
9642
9178
  statSync as statSync6
9643
9179
  } from "fs";
9644
- import { dirname as dirname6, join as join22, relative as relative2, resolve as resolve9 } from "path";
9180
+ import { dirname as dirname6, join as join20, relative as relative2, resolve as resolve9 } from "path";
9645
9181
  import { pathToFileURL as pathToFileURL2 } from "url";
9646
9182
  import { findFontFamily as findFontFamily3 } from "@vosjs/shared";
9647
9183
 
9648
9184
  // src/plugin/portRemotion.ts
9649
- import { existsSync as existsSync17, readFileSync as readFileSync5, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
9185
+ import { existsSync as existsSync16, readFileSync as readFileSync5, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
9650
9186
  import { createServer as createServer2 } from "http";
9651
9187
  import { createRequire } from "module";
9652
- import { basename as basename7, extname as extname3, join as join21 } from "path";
9188
+ import { basename as basename5, extname as extname3, join as join19 } from "path";
9653
9189
  function themeColors(sources) {
9654
9190
  const out = {};
9655
9191
  const re = /\bconst\s+([A-Za-z_$][\w$]*)\s*(?::\s*string\s*)?=\s*['"](#[0-9a-fA-F]{3,8})['"]/g;
@@ -9713,16 +9249,16 @@ function sourceTexts(dir) {
9713
9249
  const out = [];
9714
9250
  const walk = (d, depth) => {
9715
9251
  if (depth > 4 || out.length > 200) return;
9716
- for (const name of readdirSync4(d)) {
9252
+ for (const name of readdirSync3(d)) {
9717
9253
  if (name === "node_modules" || name.startsWith(".")) continue;
9718
- const p = join21(d, name);
9254
+ const p = join19(d, name);
9719
9255
  if (statSync5(p).isDirectory()) walk(p, depth + 1);
9720
9256
  else if (/\.(tsx?|jsx?|mjs)$/.test(name))
9721
9257
  out.push(readFileSync5(p, "utf8"));
9722
9258
  }
9723
9259
  };
9724
- const src = join21(dir, "src");
9725
- walk(existsSync17(src) ? src : dir, 0);
9260
+ const src = join19(dir, "src");
9261
+ walk(existsSync16(src) ? src : dir, 0);
9726
9262
  return out;
9727
9263
  }
9728
9264
  function entryPoint(dir) {
@@ -9734,7 +9270,7 @@ function entryPoint(dir) {
9734
9270
  "remotion/index.ts",
9735
9271
  "remotion/index.tsx"
9736
9272
  ])
9737
- if (existsSync17(join21(dir, p))) return join21(dir, p);
9273
+ if (existsSync16(join19(dir, p))) return join19(dir, p);
9738
9274
  return null;
9739
9275
  }
9740
9276
  var TYPES = {
@@ -9809,7 +9345,7 @@ async function readRemotion(dir, log) {
9809
9345
  const entry = entryPoint(dir);
9810
9346
  let bundler;
9811
9347
  try {
9812
- bundler = createRequire(join21(dir, "package.json"))("@remotion/bundler");
9348
+ bundler = createRequire(join19(dir, "package.json"))("@remotion/bundler");
9813
9349
  } catch {
9814
9350
  log?.(
9815
9351
  "a Remotion project whose dependencies are not installed: run `npm install` in it so vos can read its compositions (words, scenes, faces); reading only the render"
@@ -9826,9 +9362,9 @@ async function readRemotion(dir, log) {
9826
9362
  } });
9827
9363
  const server = createServer2((rq, rs) => {
9828
9364
  const path = decodeURIComponent(new URL(rq.url ?? "/", "http://x").pathname);
9829
- let f = join21(out, path);
9830
- if (!f.startsWith(out) || !existsSync17(f) || statSync5(f).isDirectory())
9831
- f = join21(out, "index.html");
9365
+ let f = join19(out, path);
9366
+ if (!f.startsWith(out) || !existsSync16(f) || statSync5(f).isDirectory())
9367
+ f = join19(out, "index.html");
9832
9368
  rs.writeHead(200, {
9833
9369
  "content-type": TYPES[extname3(f)] ?? "application/octet-stream"
9834
9370
  });
@@ -9927,10 +9463,10 @@ async function readRemotion(dir, log) {
9927
9463
  perScene.push({ scene: s.name, texts: read.texts ?? [] });
9928
9464
  }
9929
9465
  const sources = sourceTexts(dir);
9930
- const publicDir = join21(dir, "public");
9466
+ const publicDir = join19(dir, "public");
9931
9467
  const media = tree.audio.map((a) => {
9932
- const name = basename7(a.src.split("?")[0]);
9933
- const local = existsSync17(join21(publicDir, name)) ? `public/${name}` : a.src;
9468
+ const name = basename5(a.src.split("?")[0]);
9469
+ const local = existsSync16(join19(publicDir, name)) ? `public/${name}` : a.src;
9934
9470
  return {
9935
9471
  kind: "audio",
9936
9472
  src: local,
@@ -9971,9 +9507,9 @@ async function readRemotion(dir, log) {
9971
9507
 
9972
9508
  // src/plugin/portInventory.ts
9973
9509
  function detectEngine(dir) {
9974
- if (existsSync18(join22(dir, "hyperframes.json"))) return "hyperframes";
9975
- const pkg = join22(dir, "package.json");
9976
- if (existsSync18(pkg)) {
9510
+ if (existsSync17(join20(dir, "hyperframes.json"))) return "hyperframes";
9511
+ const pkg = join20(dir, "package.json");
9512
+ if (existsSync17(pkg)) {
9977
9513
  try {
9978
9514
  const p = JSON.parse(readFileSync6(pkg, "utf8"));
9979
9515
  const deps = { ...p.dependencies ?? {}, ...p.devDependencies ?? {} };
@@ -9987,14 +9523,14 @@ function detectEngine(dir) {
9987
9523
  return "page";
9988
9524
  }
9989
9525
  function sourcePage(source) {
9990
- if (!existsSync18(source)) return null;
9526
+ if (!existsSync17(source)) return null;
9991
9527
  if (statSync6(source).isFile()) return /\.html?$/i.test(source) ? source : null;
9992
- const index = join22(source, "index.html");
9993
- return existsSync18(index) ? index : null;
9528
+ const index = join20(source, "index.html");
9529
+ return existsSync17(index) ? index : null;
9994
9530
  }
9995
9531
  function findRender(dir) {
9996
- const videos = (d) => existsSync18(d) ? readdirSync5(d).filter((f) => /\.(mp4|webm|mov)$/i.test(f)).map((f) => join22(d, f)) : [];
9997
- for (const d of [join22(dir, "renders"), join22(dir, "out"), dir]) {
9532
+ const videos = (d) => existsSync17(d) ? readdirSync4(d).filter((f) => /\.(mp4|webm|mov)$/i.test(f)).map((f) => join20(d, f)) : [];
9533
+ for (const d of [join20(dir, "renders"), join20(dir, "out"), dir]) {
9998
9534
  const found = videos(d);
9999
9535
  if (found.length) return found[0];
10000
9536
  }
@@ -10265,7 +9801,7 @@ var SEEK_AND_READ = `((args) => {
10265
9801
  })`;
10266
9802
  async function readInventory(source, opts) {
10267
9803
  const abs = resolve9(source);
10268
- const dir = existsSync18(abs) && statSync6(abs).isDirectory() ? abs : dirname6(abs);
9804
+ const dir = existsSync17(abs) && statSync6(abs).isDirectory() ? abs : dirname6(abs);
10269
9805
  const engine = detectEngine(dir);
10270
9806
  const page = sourcePage(abs);
10271
9807
  const render = opts.render === void 0 ? findRender(dir) : opts.render;
@@ -10368,7 +9904,7 @@ async function readInventory(source, opts) {
10368
9904
  if (render && duration) {
10369
9905
  const has = spawnSync2("ffmpeg", ["-version"]).status === 0;
10370
9906
  if (has) {
10371
- const refDir = join22(opts.outDir, "ref");
9907
+ const refDir = join20(opts.outDir, "ref");
10372
9908
  mkdirSync3(refDir, { recursive: true });
10373
9909
  const times = read.scenes.length >= 3 ? read.scenes.map(
10374
9910
  (s) => s.start + (s.duration ?? 1) / 2
@@ -10377,7 +9913,7 @@ async function readInventory(source, opts) {
10377
9913
  (_, i) => i + 0.5
10378
9914
  );
10379
9915
  for (const t of times.filter((t2) => t2 < duration)) {
10380
- const file = join22(refDir, `src-${t.toFixed(2)}.png`);
9916
+ const file = join20(refDir, `src-${t.toFixed(2)}.png`);
10381
9917
  const ok = spawnSync2("ffmpeg", [
10382
9918
  "-v",
10383
9919
  "error",
@@ -10431,8 +9967,8 @@ async function readInventory(source, opts) {
10431
9967
 
10432
9968
  // src/plugin/portScaffold.ts
10433
9969
  import { spawnSync as spawnSync3 } from "child_process";
10434
- import { copyFileSync, existsSync as existsSync19, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
10435
- import { basename as basename8, dirname as dirname7, join as join23, resolve as resolve10 } from "path";
9970
+ import { copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
9971
+ import { basename as basename6, dirname as dirname7, join as join21, resolve as resolve10 } from "path";
10436
9972
  import { findFontFamily as findFontFamily4, fontFaceUrl, nearestFontWeight } from "@vosjs/shared";
10437
9973
 
10438
9974
  // src/plugin/painterLib.ts
@@ -10842,8 +10378,8 @@ function planScaffold(inv, opts) {
10842
10378
  const copies = [];
10843
10379
  inv.media.filter((m) => m.kind === "audio" && m.src).forEach((m, i) => {
10844
10380
  const from = resolve10(opts.sourceDir, m.src);
10845
- const name = basename8(from);
10846
- if (!existsSync19(from)) {
10381
+ const name = basename6(from);
10382
+ if (!existsSync18(from)) {
10847
10383
  report.push(
10848
10384
  `- the audio ${m.src} is not in the source folder; add it to doc.json by hand`
10849
10385
  );
@@ -10986,7 +10522,7 @@ ${sceneTodos}${springHint}
10986
10522
  }
10987
10523
  function reportText(inv, r) {
10988
10524
  const lines = [
10989
- `# Port report: ${inv.source === "." && inv.root ? basename8(inv.root) : inv.source}`,
10525
+ `# Port report: ${inv.source === "." && inv.root ? basename6(inv.root) : inv.source}`,
10990
10526
  "",
10991
10527
  `${inv.engine} piece, ${inv.width}\xD7${inv.height}${inv.fps ? ` at ${inv.fps} fps` : ""}, ${inv.duration ?? "?"} s. A program has no size of its own: render it with \`--width ${inv.width} --height ${inv.height}\`.`,
10992
10528
  "",
@@ -11041,13 +10577,13 @@ function reportText(inv, r) {
11041
10577
  }
11042
10578
  function writeScaffold(plan, outDir) {
11043
10579
  mkdirSync4(outDir, { recursive: true });
11044
- writeFileSync4(join23(outDir, "program.mjs"), plan.program);
10580
+ writeFileSync4(join21(outDir, "program.mjs"), plan.program);
11045
10581
  writeFileSync4(
11046
- join23(outDir, "doc.json"),
10582
+ join21(outDir, "doc.json"),
11047
10583
  JSON.stringify(plan.doc, null, 2) + "\n"
11048
10584
  );
11049
- writeFileSync4(join23(dirname7(outDir), "REPORT.md"), plan.report);
11050
- for (const [from, name] of plan.copies) copyFileSync(from, join23(outDir, name));
10585
+ writeFileSync4(join21(dirname7(outDir), "REPORT.md"), plan.report);
10586
+ for (const [from, name] of plan.copies) copyFileSync(from, join21(outDir, name));
11051
10587
  }
11052
10588
  function probeAudioDuration(file) {
11053
10589
  const res = spawnSync3(
@@ -11081,7 +10617,7 @@ async function inventory(argv) {
11081
10617
  outDir,
11082
10618
  log: (l) => r.log(`note: ${l}`)
11083
10619
  });
11084
- const file = join24(outDir, "inventory.json");
10620
+ const file = join22(outDir, "inventory.json");
11085
10621
  writeFileSync5(file, JSON.stringify(inv, null, 2) + "\n");
11086
10622
  const missing = inv.fonts.filter((f) => !f.catalog).map((f) => f.family);
11087
10623
  r.log(
@@ -11113,32 +10649,32 @@ function summary(inv) {
11113
10649
  async function scaffold(argv) {
11114
10650
  const { flags } = parseArgs(argv, BOOLEAN_FLAGS8);
11115
10651
  const r = createReporter(flags.json === true);
11116
- const from = resolve11(strFlag(flags, "from") ?? join24("port", "inventory.json"));
11117
- if (!existsSync20(from))
10652
+ const from = resolve11(strFlag(flags, "from") ?? join22("port", "inventory.json"));
10653
+ if (!existsSync19(from))
11118
10654
  throw new UsageError(
11119
10655
  `no inventory at ${from}: run npx vos port inventory <source> first`
11120
10656
  );
11121
10657
  const inv = JSON.parse(readFileSync7(from, "utf8"));
11122
10658
  const outDir = resolve11(
11123
- strFlag(flags, "out") ?? join24(dirname8(from), "program")
10659
+ strFlag(flags, "out") ?? join22(dirname8(from), "program")
11124
10660
  );
11125
10661
  const plan = planScaffold(inv, {
11126
10662
  sourceDir: scaffoldSourceDir(inv),
11127
10663
  probeDuration: probeAudioDuration
11128
10664
  });
11129
10665
  writeScaffold(plan, outDir);
11130
- const programFile = join24(outDir, "program.mjs");
10666
+ const programFile = join22(outDir, "program.mjs");
11131
10667
  const mod = await import(`${pathToFileURL3(programFile).href}?t=${Date.now()}`);
11132
10668
  const built = buildProgram(mod.default, "program.mjs", plan.program);
11133
10669
  for (const p of built.problems) r.log(`error ${p.fn}: ${p.message}`);
11134
10670
  if (built.problems.length) return EXIT_ERROR;
11135
10671
  writeFileSync5(
11136
- join24(outDir, "config.json"),
10672
+ join22(outDir, "config.json"),
11137
10673
  JSON.stringify(built.config, null, 2) + "\n"
11138
10674
  );
11139
10675
  r.done(
11140
- { program: outDir, report: join24(dirname8(outDir), "REPORT.md") },
11141
- `Wrote ${outDir}/program.mjs, config.json and doc.json, and ${join24(dirname8(outDir), "REPORT.md")}. Translate the motion where it says TODO, then: npx vos build program.mjs && npx vos compare . --against <their render>`
10676
+ { program: outDir, report: join22(dirname8(outDir), "REPORT.md") },
10677
+ `Wrote ${outDir}/program.mjs, config.json and doc.json, and ${join22(dirname8(outDir), "REPORT.md")}. Translate the motion where it says TODO, then: npx vos build program.mjs && npx vos compare . --against <their render>`
11142
10678
  );
11143
10679
  return EXIT_OK;
11144
10680
  }
@@ -11149,11 +10685,11 @@ function scaffoldSourceDir(inv) {
11149
10685
 
11150
10686
  // src/plugin/recipe.ts
11151
10687
  import { readFileSync as readFileSync8 } from "fs";
11152
- import { basename as basename9 } from "path";
10688
+ import { basename as basename7 } from "path";
11153
10689
  var BOOLEAN_FLAGS9 = /* @__PURE__ */ new Set(["json", "help"]);
11154
10690
  var USAGE = "vos recipe push <file.md> (--folder <folderId|slug> | --asset <assetId>)";
11155
10691
  function recipeFilename(file) {
11156
- const name = basename9(file);
10692
+ const name = basename7(file);
11157
10693
  if (!/\.md$/i.test(name)) {
11158
10694
  throw new UsageError(`a recipe is a markdown file \u2014 ${name} is not .md`);
11159
10695
  }
@@ -11240,7 +10776,7 @@ async function cmdRecipe(argv) {
11240
10776
  }
11241
10777
 
11242
10778
  // src/plugin/brand.ts
11243
- import { writeFile as writeFile12 } from "fs/promises";
10779
+ import { writeFile as writeFile10 } from "fs/promises";
11244
10780
  import { resolve as resolve12 } from "path";
11245
10781
  import { parseFrontmatter as parseFrontmatter2 } from "@vosjs/shared/frontmatter";
11246
10782
  import { lookKindForGround } from "@vosjs/studio-core";
@@ -11594,7 +11130,7 @@ async function cmdBrand(argv) {
11594
11130
  composition,
11595
11131
  llms?.claim ?? design?.description ?? null
11596
11132
  );
11597
- await writeFile12(out, md);
11133
+ await writeFile10(out, md);
11598
11134
  const k = composition.kit;
11599
11135
  r.done(
11600
11136
  {
@@ -11709,7 +11245,7 @@ async function recordHosted(input) {
11709
11245
  }
11710
11246
 
11711
11247
  // src/plugin/agentBrowser.ts
11712
- import { writeFile as writeFile13 } from "fs/promises";
11248
+ import { writeFile as writeFile11 } from "fs/promises";
11713
11249
  import { resolve as resolve13 } from "path";
11714
11250
  function parseAgentBrowserLog(text) {
11715
11251
  const records = [];
@@ -12189,8 +11725,8 @@ async function cmdActions(argv) {
12189
11725
  if (!m) throw new UsageError("--viewport expects WxH, e.g. 1280x720");
12190
11726
  viewport = { width: Number(m[1]), height: Number(m[2]) };
12191
11727
  }
12192
- const { readFile: readFile18 } = await import("fs/promises");
12193
- const text = await readFile18(resolve13(input), "utf8");
11728
+ const { readFile: readFile16 } = await import("fs/promises");
11729
+ const text = await readFile16(resolve13(input), "utf8");
12194
11730
  const { records, problems } = parseAgentBrowserLog(text);
12195
11731
  for (const p of problems) r.log(` ${p}`);
12196
11732
  const result = convertAgentBrowser(records, {
@@ -12207,7 +11743,7 @@ async function cmdActions(argv) {
12207
11743
  );
12208
11744
  return EXIT_ERROR;
12209
11745
  }
12210
- await writeFile13(out, `${JSON.stringify(result.actions, null, 2)}
11746
+ await writeFile11(out, `${JSON.stringify(result.actions, null, 2)}
12211
11747
  `);
12212
11748
  r.done(
12213
11749
  { ok: true, out, url: result.actions.url ?? null, ...summary2(result) },
@@ -12227,9 +11763,9 @@ function summary2(result) {
12227
11763
  }
12228
11764
  async function cmdActionsScript(input, flags) {
12229
11765
  const r = createReporter(flags.json === true);
12230
- const { readFile: readFile18 } = await import("fs/promises");
11766
+ const { readFile: readFile16 } = await import("fs/promises");
12231
11767
  const { shotList, formatShotList } = await import("./shotList-KH6FTX4L.js");
12232
- const raw = JSON.parse(await readFile18(resolve13(input), "utf8"));
11768
+ const raw = JSON.parse(await readFile16(resolve13(input), "utf8"));
12233
11769
  const errors = validateActions(raw);
12234
11770
  if (errors.length) throw new UsageError(`${input}:
12235
11771
  ${errors.join("\n ")}`);
@@ -12239,9 +11775,9 @@ async function cmdActionsScript(input, flags) {
12239
11775
  }
12240
11776
 
12241
11777
  // src/plugin/ingest.ts
12242
- import { existsSync as existsSync21 } from "fs";
12243
- import { copyFile, mkdir as mkdir10, readFile as readFile16, stat as stat4 } from "fs/promises";
12244
- import { basename as basename10, join as join25, resolve as resolve14 } from "path";
11778
+ import { existsSync as existsSync20 } from "fs";
11779
+ import { copyFile, mkdir as mkdir8, readFile as readFile14, stat as stat3 } from "fs/promises";
11780
+ import { basename as basename8, join as join23, resolve as resolve14 } from "path";
12245
11781
  import { inflateRawSync } from "zlib";
12246
11782
  import {
12247
11783
  cursorFromPlaywrightTrace,
@@ -12322,14 +11858,14 @@ async function copyRecording(src, dest, ext) {
12322
11858
  }
12323
11859
  }
12324
11860
  async function readTraceFile(path, offsetMs) {
12325
- const kind = traceKindOf(basename10(path));
11861
+ const kind = traceKindOf(basename8(path));
12326
11862
  if (!kind) {
12327
11863
  throw new UsageError(
12328
11864
  `${path}: not a trace the recorder reads (trace.zip, .trace, .jsonl, .json or .csv)`
12329
11865
  );
12330
11866
  }
12331
11867
  if (kind === "zip") {
12332
- const bytes = new Uint8Array(await readFile16(path));
11868
+ const bytes = new Uint8Array(await readFile14(path));
12333
11869
  const entry = playwrightTraceEntry(zipEntries(bytes));
12334
11870
  if (!entry) {
12335
11871
  throw new UsageError(
@@ -12342,8 +11878,8 @@ async function readTraceFile(path, offsetMs) {
12342
11878
  );
12343
11879
  return cursorFromPlaywrightTrace(text2, { offsetMs });
12344
11880
  }
12345
- const text = await readFile16(path, "utf8");
12346
- return cursorFromTrace(basename10(path), text, { offsetMs });
11881
+ const text = await readFile14(path, "utf8");
11882
+ return cursorFromTrace(basename8(path), text, { offsetMs });
12347
11883
  }
12348
11884
  function parseViewport(v) {
12349
11885
  if (!v) return null;
@@ -12359,7 +11895,7 @@ async function cmdIngest(argv) {
12359
11895
  "vos ingest <video.webm|mp4> [--cursor <trace.zip|steps.jsonl|cursor.csv>] [--as footage|take] [--out take] [--offset <ms>] [--viewport WxH] [--background <slug|url|none>] [--json]"
12360
11896
  );
12361
11897
  const videoPath = resolve14(source);
12362
- if (!existsSync21(videoPath)) throw new UsageError(`${source}: no such file`);
11898
+ if (!existsSync20(videoPath)) throw new UsageError(`${source}: no such file`);
12363
11899
  const r = createReporter(flags.json === true);
12364
11900
  const outDir = resolve14(strFlag(flags, "out") ?? "take");
12365
11901
  const offsetMs = numFlag(flags, "offset", 0);
@@ -12374,7 +11910,7 @@ async function cmdIngest(argv) {
12374
11910
  "note: finished footage opens bare, so --background is not used (pass --as take for the screen-recording card and its ground)"
12375
11911
  );
12376
11912
  const backdrop = footage ? null : await takeBackdrop(flags, r);
12377
- r.log(`probing ${basename10(videoPath)}\u2026`);
11913
+ r.log(`probing ${basename8(videoPath)}\u2026`);
12378
11914
  const probed = await probeVideoFile(videoPath);
12379
11915
  if (!(probed.durationMs > 0)) {
12380
11916
  throw new UsageError(
@@ -12386,15 +11922,15 @@ async function cmdIngest(argv) {
12386
11922
  trace = await readTraceFile(resolve14(cursorPath), offsetMs);
12387
11923
  for (const n of trace.notes) r.log(`note: ${n}`);
12388
11924
  }
12389
- if (existsSync21(join25(outDir, "meta.json"))) {
11925
+ if (existsSync20(join23(outDir, "meta.json"))) {
12390
11926
  const { prevDoc, kept } = await prepareReRecord(outDir);
12391
11927
  r.log(
12392
11928
  `note: re-ingesting into ${outDir}; kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? `; apply the previous cut with: vos plan ${outDir} --reuse` : "")
12393
11929
  );
12394
11930
  }
12395
- await mkdir10(outDir, { recursive: true });
11931
+ await mkdir8(outDir, { recursive: true });
12396
11932
  const paths = await ensureTakeDir(outDir);
12397
- const recording = join25(outDir, `recording.${probed.ext}`);
11933
+ const recording = join23(outDir, `recording.${probed.ext}`);
12398
11934
  r.log("copying the recording\u2026");
12399
11935
  const remuxed = await copyRecording(videoPath, recording, probed.ext);
12400
11936
  const viewport = viewportFlag ?? trace?.viewport ?? {
@@ -12402,7 +11938,7 @@ async function cmdIngest(argv) {
12402
11938
  height: probed.height
12403
11939
  };
12404
11940
  const events = trace?.events ?? [];
12405
- const fileStat = await stat4(videoPath);
11941
+ const fileStat = await stat3(videoPath);
12406
11942
  const meta = {
12407
11943
  dpr: 1,
12408
11944
  zoom: 1,
@@ -12423,13 +11959,13 @@ async function cmdIngest(argv) {
12423
11959
  const clicks = events.filter((e) => e.type === "down").length;
12424
11960
  const seconds = (probed.durationMs / 1e3).toFixed(1);
12425
11961
  const facts = `${seconds}s, ${probed.width}\xD7${probed.height} @ ${meta.fps} fps${probed.hasAudio ? ", audio" : ", no audio"}${remuxed ? "" : ", copied as is"}`;
12426
- const cursorLine = trace ? `${events.length} cursor events (${clicks} clicks) from ${basename10(cursorPath)}${trace.dropped ? `, ${trace.dropped} not placed` : ""}; ${plan.zoomAuto} zoom${plan.zoomAuto === 1 ? "" : "s"} planned` : "no cursor track, so nothing was planned";
11962
+ const cursorLine = trace ? `${events.length} cursor events (${clicks} clicks) from ${basename8(cursorPath)}${trace.dropped ? `, ${trace.dropped} not placed` : ""}; ${plan.zoomAuto} zoom${plan.zoomAuto === 1 ? "" : "s"} planned` : "no cursor track, so nothing was planned";
12427
11963
  const opened = footage ? `opened as finished footage: no card, no browser bar, no drawn cursor, ${meta.fps > 30 ? 60 : 30} fps export (--as take for a screen recording's card)` : `opened as a screen recording: the card, its ground and the drawn cursor (--as footage to open it bare)`;
12428
11964
  const next = footage && !trace ? `Next: vos push ${outDir} --wait \u2014 it lands as it is; trim or add to it in the studio` : trace ? `Next: vos digest ${outDir} \u2014 look before you cut; edit doc.json; vos push ${outDir}` : `Add --cursor <trace> recorded beside the video (a Playwright trace.zip, stamped JSON records, or a t,x,y,type CSV) to plan zooms from its clicks; else place them by eye: vos frames ${outDir} --times 0,25%,50%,75%,100%, then edit doc.json`;
12429
11965
  r.done(
12430
11966
  {
12431
11967
  out: outDir,
12432
- recording: basename10(recording),
11968
+ recording: basename8(recording),
12433
11969
  durationMs: probed.durationMs,
12434
11970
  width: probed.width,
12435
11971
  height: probed.height,
@@ -12438,7 +11974,7 @@ async function cmdIngest(argv) {
12438
11974
  hasAudio: probed.hasAudio,
12439
11975
  remuxed,
12440
11976
  cursor: trace ? {
12441
- file: basename10(cursorPath),
11977
+ file: basename8(cursorPath),
12442
11978
  kind: trace.kind,
12443
11979
  events: events.length,
12444
11980
  clicks,
@@ -12450,7 +11986,7 @@ async function cmdIngest(argv) {
12450
11986
  backdrop: plan.backdrop ?? null,
12451
11987
  as: footage ? "footage" : "take"
12452
11988
  },
12453
- `Ingested ${basename10(videoPath)} \u2192 ${outDir}/${basename10(recording)} (${facts})
11989
+ `Ingested ${basename8(videoPath)} \u2192 ${outDir}/${basename8(recording)} (${facts})
12454
11990
  ${opened}
12455
11991
  ${cursorLine}
12456
11992
  ${next}`
@@ -12462,19 +11998,19 @@ async function cmdIngest(argv) {
12462
11998
  import { spawnSync as spawnSync4 } from "child_process";
12463
11999
  import {
12464
12000
  cpSync,
12465
- existsSync as existsSync23,
12001
+ existsSync as existsSync22,
12466
12002
  mkdirSync as mkdirSync6,
12467
12003
  readFileSync as readFileSync10,
12468
12004
  rmSync as rmSync3,
12469
12005
  writeFileSync as writeFileSync6
12470
12006
  } from "fs";
12471
12007
  import { homedir as homedir2 } from "os";
12472
- import { join as join27 } from "path";
12008
+ import { join as join25 } from "path";
12473
12009
  import { chromium as chromium2 } from "playwright";
12474
12010
 
12475
12011
  // src/plugin/machine.ts
12476
- import { existsSync as existsSync22, readFileSync as readFileSync9, readdirSync as readdirSync6, statSync as statSync7 } from "fs";
12477
- import { dirname as dirname9, join as join26 } from "path";
12012
+ import { existsSync as existsSync21, readFileSync as readFileSync9, readdirSync as readdirSync5, statSync as statSync7 } from "fs";
12013
+ import { dirname as dirname9, join as join24 } from "path";
12478
12014
  var AGENT_NAMES = [
12479
12015
  "claude",
12480
12016
  "cursor",
@@ -12494,17 +12030,17 @@ var HOME_DIRS = {
12494
12030
  copilot: [".copilot"]
12495
12031
  };
12496
12032
  function detectAgents(root, opts = {}) {
12497
- const exists = opts.exists ?? existsSync22;
12033
+ const exists = opts.exists ?? existsSync21;
12498
12034
  const out = [];
12499
12035
  for (const agent of AGENT_NAMES) {
12500
12036
  for (const rel of PROJECT_DIRS[agent]) {
12501
- const dir = join26(root, rel);
12037
+ const dir = join24(root, rel);
12502
12038
  if (exists(dir)) out.push({ agent, dir, scope: "project" });
12503
12039
  }
12504
- if (agent === "copilot" && !out.some((h) => h.agent === "copilot") && exists(join26(root, ".github", "copilot-instructions.md"))) {
12040
+ if (agent === "copilot" && !out.some((h) => h.agent === "copilot") && exists(join24(root, ".github", "copilot-instructions.md"))) {
12505
12041
  out.push({
12506
12042
  agent,
12507
- dir: join26(root, ".github", "copilot"),
12043
+ dir: join24(root, ".github", "copilot"),
12508
12044
  scope: "project"
12509
12045
  });
12510
12046
  }
@@ -12512,7 +12048,7 @@ function detectAgents(root, opts = {}) {
12512
12048
  if (opts.global && opts.home) {
12513
12049
  for (const agent of AGENT_NAMES) {
12514
12050
  for (const rel of HOME_DIRS[agent]) {
12515
- const dir = join26(opts.home, rel);
12051
+ const dir = join24(opts.home, rel);
12516
12052
  if (exists(dir)) out.push({ agent, dir, scope: "global" });
12517
12053
  }
12518
12054
  }
@@ -12530,26 +12066,26 @@ function agentsFromFlag(value, root) {
12530
12066
  return {
12531
12067
  homes: wanted.map((agent) => ({
12532
12068
  agent,
12533
- dir: join26(root, PROJECT_DIRS[agent][0]),
12069
+ dir: join24(root, PROJECT_DIRS[agent][0]),
12534
12070
  scope: "project"
12535
12071
  }))
12536
12072
  };
12537
12073
  }
12538
12074
  function skillsDirFor(home) {
12539
- return join26(home.dir, "skills");
12075
+ return join24(home.dir, "skills");
12540
12076
  }
12541
12077
  function fallbackSkillsHome(root) {
12542
- return { agent: "codex", dir: join26(root, ".agents"), scope: "project" };
12078
+ return { agent: "codex", dir: join24(root, ".agents"), scope: "project" };
12543
12079
  }
12544
- function installedSkills(dir, exists = existsSync22, list = (p) => {
12080
+ function installedSkills(dir, exists = existsSync21, list = (p) => {
12545
12081
  try {
12546
- return readdirSync6(p);
12082
+ return readdirSync5(p);
12547
12083
  } catch {
12548
12084
  return [];
12549
12085
  }
12550
12086
  }) {
12551
12087
  if (!exists(dir)) return [];
12552
- return list(dir).filter((name) => exists(join26(dir, name, "SKILL.md"))).sort();
12088
+ return list(dir).filter((name) => exists(join24(dir, name, "SKILL.md"))).sort();
12553
12089
  }
12554
12090
  var RULES_BEGIN = "<!-- vos:begin (written by vos setup; the block is replaced on the next run) -->";
12555
12091
  var RULES_END = "<!-- vos:end -->";
@@ -12582,10 +12118,10 @@ ${RULES_BLOCK}
12582
12118
  }
12583
12119
  return { text, changed: text !== current };
12584
12120
  }
12585
- function rulesFileFor(root, exists = existsSync22) {
12586
- const agents = join26(root, "AGENTS.md");
12121
+ function rulesFileFor(root, exists = existsSync21) {
12122
+ const agents = join24(root, "AGENTS.md");
12587
12123
  if (exists(agents)) return agents;
12588
- const claude = join26(root, "CLAUDE.md");
12124
+ const claude = join24(root, "CLAUDE.md");
12589
12125
  if (exists(claude)) return claude;
12590
12126
  return agents;
12591
12127
  }
@@ -12594,14 +12130,14 @@ function chromeCandidates(platform, env) {
12594
12130
  return ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"];
12595
12131
  if (platform === "win32")
12596
12132
  return [
12597
- join26(
12133
+ join24(
12598
12134
  env["PROGRAMFILES"] ?? "C:\\Program Files",
12599
12135
  "Google",
12600
12136
  "Chrome",
12601
12137
  "Application",
12602
12138
  "chrome.exe"
12603
12139
  ),
12604
- join26(
12140
+ join24(
12605
12141
  env["LOCALAPPDATA"] ?? "",
12606
12142
  "Google",
12607
12143
  "Chrome",
@@ -12688,13 +12224,13 @@ function doctorReport(facts) {
12688
12224
  function bundledSkillsDir(from) {
12689
12225
  let dir = dirname9(from);
12690
12226
  for (let i = 0; i < 6; i++) {
12691
- const pkg = join26(dir, "package.json");
12692
- if (existsSync22(pkg)) {
12227
+ const pkg = join24(dir, "package.json");
12228
+ if (existsSync21(pkg)) {
12693
12229
  try {
12694
12230
  const name = JSON.parse(readFileSync9(pkg, "utf8")).name;
12695
12231
  if (name === "@vosjs/cli") {
12696
- const skills = join26(dir, "skills");
12697
- return existsSync22(skills) && statSync7(skills).isDirectory() ? skills : null;
12232
+ const skills = join24(dir, "skills");
12233
+ return existsSync21(skills) && statSync7(skills).isDirectory() ? skills : null;
12698
12234
  }
12699
12235
  } catch {
12700
12236
  }
@@ -12725,7 +12261,7 @@ function npx() {
12725
12261
  function bundledChromium() {
12726
12262
  try {
12727
12263
  const p = chromium2.executablePath();
12728
- return p && existsSync23(p) ? p : null;
12264
+ return p && existsSync22(p) ? p : null;
12729
12265
  } catch {
12730
12266
  return null;
12731
12267
  }
@@ -12734,7 +12270,7 @@ function probe2() {
12734
12270
  return probeBrowser({
12735
12271
  platform: process.platform,
12736
12272
  env: process.env,
12737
- exists: existsSync23,
12273
+ exists: existsSync22,
12738
12274
  bundled: bundledChromium
12739
12275
  });
12740
12276
  }
@@ -12838,7 +12374,7 @@ async function cmdSetup(argv) {
12838
12374
  const dest = skillsDirFor(home);
12839
12375
  mkdirSync6(dest, { recursive: true });
12840
12376
  for (const name of installedSkills(bundled)) {
12841
- cpSync(join27(bundled, name), join27(dest, name), { recursive: true });
12377
+ cpSync(join25(bundled, name), join25(dest, name), { recursive: true });
12842
12378
  }
12843
12379
  }
12844
12380
  skillsVia = "bundled";
@@ -12873,7 +12409,7 @@ async function cmdSetup(argv) {
12873
12409
  let rules = null;
12874
12410
  if (!noRules) {
12875
12411
  const file = rulesFileFor(root);
12876
- const existing = existsSync23(file) ? readFileSync10(file, "utf8") : null;
12412
+ const existing = existsSync22(file) ? readFileSync10(file, "utf8") : null;
12877
12413
  const { text, changed } = applyRulesBlock(existing);
12878
12414
  if (changed) writeFileSync6(file, text);
12879
12415
  rules = { file, changed };
@@ -12987,7 +12523,7 @@ async function cmdWhoami(argv) {
12987
12523
  function cmdLogout(argv) {
12988
12524
  const { flags } = parseArgs(argv, BOOLEAN_FLAGS11);
12989
12525
  const r = reporterFor(flags);
12990
- const had = existsSync23(CREDENTIALS_PATH);
12526
+ const had = existsSync22(CREDENTIALS_PATH);
12991
12527
  rmSync3(CREDENTIALS_PATH, { force: true });
12992
12528
  const envKey = Boolean(process.env.VOS_API_KEY?.trim());
12993
12529
  r.done(
@@ -13341,7 +12877,7 @@ lint-gated, so a bad override fails like a bad doc.json):
13341
12877
  first ready loop (the house backdrop the studio opens on), or a flat ground offline
13342
12878
  `;
13343
12879
  async function loadActions(file) {
13344
- const raw = JSON.parse(await readFile17(file, "utf8"));
12880
+ const raw = JSON.parse(await readFile15(file, "utf8"));
13345
12881
  const errors = validateActions(raw);
13346
12882
  if (errors.length)
13347
12883
  throw new UsageError(`invalid actions file:
@@ -13404,7 +12940,7 @@ function takeStorageState(flags) {
13404
12940
  const raw = strFlag(flags, "storage-state");
13405
12941
  if (!raw) return void 0;
13406
12942
  const path = resolve15(raw);
13407
- if (!existsSync24(path))
12943
+ if (!existsSync23(path))
13408
12944
  throw new UsageError(`--storage-state: no such file: ${path}`);
13409
12945
  return path;
13410
12946
  }
@@ -13417,7 +12953,7 @@ function takeSessionName(flags, storageState) {
13417
12953
  throw new UsageError(
13418
12954
  "--session and --storage-state are two doors to one take: a persistent profile cannot take a storage state. Pass one"
13419
12955
  );
13420
- if (!existsSync24(sessionDir(name)))
12956
+ if (!existsSync23(sessionDir(name)))
13421
12957
  throw new UsageError(
13422
12958
  `no session "${name}". Make one: vos session open <url> --name ${name} (a Chrome window opens; sign in and close it)`
13423
12959
  );
@@ -13609,7 +13145,7 @@ async function cmdRecord(argv) {
13609
13145
  const what = s.selector ? ` ${s.selector}` : "";
13610
13146
  return ` ${s.skipped ? "\u2717" : "\u2713"} #${s.step}${s.id ? ` (${s.id})` : ""} ${s.do}${what}${s.skipped ? " NOT FOUND" : rect}${s.navigated ? " \u2192 navigated" : ""}`;
13611
13147
  });
13612
- const failed2 = rec.skipped.length > 0 || rec.navTimeout || maskMisses(rec).length > 0;
13148
+ const failed = rec.skipped.length > 0 || rec.navTimeout || maskMisses(rec).length > 0;
13613
13149
  const carried = [
13614
13150
  ...strFlag(flags, "storage-state") ? [`--storage-state ${strFlag(flags, "storage-state")}`] : [],
13615
13151
  ...browserArgs.map((a) => `--browser-arg=${a}`),
@@ -13621,7 +13157,7 @@ async function cmdRecord(argv) {
13621
13157
  r.done(
13622
13158
  {
13623
13159
  dryRun: true,
13624
- ok: !failed2,
13160
+ ok: !failed,
13625
13161
  seconds: +((Date.now() - started) / 1e3).toFixed(1),
13626
13162
  steps,
13627
13163
  skipped: rec.skipped,
@@ -13629,24 +13165,24 @@ async function cmdRecord(argv) {
13629
13165
  exposures: rec.exposures,
13630
13166
  masks: rec.masks
13631
13167
  },
13632
- `${failed2 ? "REHEARSAL FAILED" : "Rehearsal passed"}: ${steps.length - rec.skipped.length}/${steps.length} steps resolved in ${((Date.now() - started) / 1e3).toFixed(1)}s${rec.navTimeout ? " (the first load never reached networkidle)" : ""}
13168
+ `${failed ? "REHEARSAL FAILED" : "Rehearsal passed"}: ${steps.length - rec.skipped.length}/${steps.length} steps resolved in ${((Date.now() - started) / 1e3).toFixed(1)}s${rec.navTimeout ? " (the first load never reached networkidle)" : ""}
13633
13169
  ${lines.join("\n")}
13634
- ${maskMisses(rec).length ? `MASK hid nothing: ${maskMisses(rec).join(", ")} (the selector reached no element). ` : ""}${failed2 ? "Fix the script, rehearse again, then record." : `Rects are capture px (the step rects a pin or a callout reads). Next: vos record --actions ${actionsPath} --out ${nextOut}${carried ? ` ${carried}` : ""} --strict`}${exposureNote(rec)}`
13170
+ ${maskMisses(rec).length ? `MASK hid nothing: ${maskMisses(rec).join(", ")} (the selector reached no element). ` : ""}${failed ? "Fix the script, rehearse again, then record." : `Rects are capture px (the step rects a pin or a callout reads). Next: vos record --actions ${actionsPath} --out ${nextOut}${carried ? ` ${carried}` : ""} --strict`}${exposureNote(rec)}`
13635
13171
  );
13636
- return failed2 ? EXIT_USAGE : EXIT_OK;
13172
+ return failed ? EXIT_USAGE : EXIT_OK;
13637
13173
  } finally {
13638
13174
  await browser2.close();
13639
13175
  }
13640
13176
  }
13641
13177
  const paths = takePaths(outDir);
13642
13178
  const prepare = async () => {
13643
- if (existsSync24(join28(outDir, "meta.json"))) {
13179
+ if (existsSync23(join26(outDir, "meta.json"))) {
13644
13180
  const { prevDoc, kept } = await prepareReRecord(outDir);
13645
13181
  r.log(
13646
13182
  `note: re-recording ${outDir} \u2014 kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? "; apply the previous cut to the new footage with: vos plan " + outDir + " --reuse" : "")
13647
13183
  );
13648
13184
  }
13649
- await mkdir11(outDir, { recursive: true });
13185
+ await mkdir9(outDir, { recursive: true });
13650
13186
  await ensureTakeDir(outDir);
13651
13187
  };
13652
13188
  const browser = await launchBrowser(browserArgs);
@@ -13705,7 +13241,7 @@ ${lines.join("\n")}
13705
13241
  },
13706
13242
  strictFail ? `STRICT: take recorded but incomplete \u2014 ${strictReason(rec)}; fix the flow and re-record.${skippedNote}${exposureNote(rec)}` : `Take ready: ${outDir}
13707
13243
  ${(rec.meta.durationMs / 1e3).toFixed(1)}s \xB7 ${rec.frames.length} frames \xB7 ${rec.events.length} cursor events \xB7 ${clicks} clicks \xB7 ${plan.doc.zoom.length} zoom spans planned \xB7 ${rec.freezePct}% frozen \xB7 ${rec.dead.pct}% dead${deadWarns(rec.dead) ? " \u26A0 " + deadLine(rec.dead) : ""}${skippedNote}
13708
- Next: edit ${join28(outDir, "doc.json")} (optional), then: vos render ${outDir}${exposureNote(rec)}`
13244
+ Next: edit ${join26(outDir, "doc.json")} (optional), then: vos render ${outDir}${exposureNote(rec)}`
13709
13245
  );
13710
13246
  return strictFail ? EXIT_USAGE : EXIT_OK;
13711
13247
  } finally {
@@ -13731,7 +13267,7 @@ async function cmdCreate2(argv) {
13731
13267
  throw new UsageError('no URL \u2014 set "url" in the actions file or pass --url');
13732
13268
  const outDir = resolve15(strFlag(flags, "out") ?? "take");
13733
13269
  const format = videoFormat(strFlag(flags, "format"), positionals[0]);
13734
- const out = positionals[0] ?? join28(outDir, `out.${format}`);
13270
+ const out = positionals[0] ?? join26(outDir, `out.${format}`);
13735
13271
  const parallel = numFlag(flags, "parallel", 1);
13736
13272
  if (!Number.isInteger(parallel) || parallel < 1 || parallel > 16) {
13737
13273
  throw new UsageError("--parallel expects an integer between 1 and 16");
@@ -13744,13 +13280,13 @@ async function cmdCreate2(argv) {
13744
13280
  const headers = takeHeaders(multi);
13745
13281
  const paths = takePaths(outDir);
13746
13282
  const prepare = async () => {
13747
- if (existsSync24(join28(outDir, "meta.json"))) {
13283
+ if (existsSync23(join26(outDir, "meta.json"))) {
13748
13284
  const { prevDoc, kept } = await prepareReRecord(outDir);
13749
13285
  r.log(
13750
13286
  `note: re-recording ${outDir} \u2014 kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? "; apply the previous cut to the new footage with: vos plan " + outDir + " --reuse" : "")
13751
13287
  );
13752
13288
  }
13753
- await mkdir11(outDir, { recursive: true });
13289
+ await mkdir9(outDir, { recursive: true });
13754
13290
  await ensureTakeDir(outDir);
13755
13291
  };
13756
13292
  const browser = await launchBrowser(browserArgs);
@@ -13851,24 +13387,24 @@ async function cmdPlan(argv) {
13851
13387
  );
13852
13388
  const r = createReporter(flags.json === true);
13853
13389
  if (flags.fresh === true) {
13854
- await rm5(join28(dir, "doc.json"), { force: true });
13390
+ await rm3(join26(dir, "doc.json"), { force: true });
13855
13391
  r.log("note: --fresh discarded the existing doc.json");
13856
13392
  }
13857
13393
  let reuse;
13858
13394
  if (flags.reuse === true) {
13859
- const from = resolve15(strFlag(flags, "from") ?? join28(dir, PREV_DOC_NAME));
13860
- if (!existsSync24(from)) {
13395
+ const from = resolve15(strFlag(flags, "from") ?? join26(dir, PREV_DOC_NAME));
13396
+ if (!existsSync23(from)) {
13861
13397
  throw new UsageError(
13862
13398
  `--reuse: ${from} does not exist \u2014 a re-record into this take writes doc.prev.json, or pass --from <doc.json>`
13863
13399
  );
13864
13400
  }
13865
13401
  reuse = {
13866
13402
  from,
13867
- doc: JSON.parse(await readFile17(from, "utf8"))
13403
+ doc: JSON.parse(await readFile15(from, "utf8"))
13868
13404
  };
13869
13405
  }
13870
13406
  const style = await resolveStyleRef(flags);
13871
- const hasDoc = existsSync24(join28(dir, "doc.json"));
13407
+ const hasDoc = existsSync23(join26(dir, "doc.json"));
13872
13408
  const backdrop = hasDoc ? null : await takeBackdrop(flags, r);
13873
13409
  const release = await releaseInputs(dir, flags, r, multi.with ?? []);
13874
13410
  const motionWanted = !hasDoc || flags.motion === true || flags.fresh === true;
@@ -13926,7 +13462,7 @@ async function cmdPlan(argv) {
13926
13462
  ...s.motion ? { motion: s.motion } : {},
13927
13463
  ...s.reuse ? { reuse: s.reuse } : {}
13928
13464
  },
13929
- `${s.reuse ? "Reused" : s.fresh ? "Planned" : "Refreshed"} ${join28(dir, "doc.json")}: ${s.zoomAuto} auto + ${s.zoomManual} manual zoom spans${s.cursorKept ? "" : " (cursor track dropped)"}${layoutLines}${motionLines}${reuseLines}`
13465
+ `${s.reuse ? "Reused" : s.fresh ? "Planned" : "Refreshed"} ${join26(dir, "doc.json")}: ${s.zoomAuto} auto + ${s.zoomManual} manual zoom spans${s.cursorKept ? "" : " (cursor track dropped)"}${layoutLines}${motionLines}${reuseLines}`
13930
13466
  );
13931
13467
  return EXIT_OK;
13932
13468
  }
@@ -14049,7 +13585,7 @@ async function cmdRender(argv) {
14049
13585
  );
14050
13586
  const r = createReporter(flags.json === true);
14051
13587
  const format = videoFormat(strFlag(flags, "format"), positionals[1]);
14052
- const out = positionals[1] ?? join28(dir, `out.${format}`);
13588
+ const out = positionals[1] ?? join26(dir, `out.${format}`);
14053
13589
  const parallel = numFlag(flags, "parallel", 1);
14054
13590
  if (!Number.isInteger(parallel) || parallel < 1 || parallel > 16) {
14055
13591
  throw new UsageError("--parallel expects an integer between 1 and 16");
@@ -14296,15 +13832,15 @@ async function resolveStyleRef(flags) {
14296
13832
  }
14297
13833
  async function resolveDocRef(ref, flags, what) {
14298
13834
  const styleRef = ref;
14299
- const file = existsSync24(styleRef) ? resolve15(
13835
+ const file = existsSync23(styleRef) ? resolve15(
14300
13836
  styleRef,
14301
- existsSync24(join28(styleRef, "doc.json")) ? "doc.json" : ""
13837
+ existsSync23(join26(styleRef, "doc.json")) ? "doc.json" : ""
14302
13838
  ) : null;
14303
- if (file && existsSync24(file)) {
13839
+ if (file && existsSync23(file)) {
14304
13840
  return {
14305
13841
  from: file,
14306
13842
  doc: migrateHostedDoc5(
14307
- JSON.parse(await readFile17(file, "utf8"))
13843
+ JSON.parse(await readFile15(file, "utf8"))
14308
13844
  )
14309
13845
  };
14310
13846
  }
@@ -14356,7 +13892,7 @@ async function cmdDigest(argv) {
14356
13892
  const take = await loadTake(dir);
14357
13893
  if (!take.doc) throw new UsageError(`${dir} has no doc.json \u2014 run plan first`);
14358
13894
  const transcriptPath = strFlag(flags, "transcript");
14359
- const transcript = transcriptPath ? parseTranscript(JSON.parse(await readFile17(transcriptPath, "utf8"))) : null;
13895
+ const transcript = transcriptPath ? parseTranscript(JSON.parse(await readFile15(transcriptPath, "utf8"))) : null;
14360
13896
  const style = await resolveStyleRef(flags);
14361
13897
  const noFrames = flags["no-frames"] === true;
14362
13898
  let browser = null;
@@ -14387,7 +13923,7 @@ async function cmdDigest(argv) {
14387
13923
  kinds,
14388
13924
  frames: d.images.full,
14389
13925
  crops: d.images.crop,
14390
- sheet: d.images.sheet ? join28(result.outDir, d.images.sheet) : null,
13926
+ sheet: d.images.sheet ? join26(result.outDir, d.images.sheet) : null,
14391
13927
  scenes: kinds.scene ?? 0,
14392
13928
  sourceDuration: d.take.sourceDuration,
14393
13929
  outputDuration: d.take.outputDuration,
@@ -14415,7 +13951,7 @@ async function cmdOpen(argv) {
14415
13951
  if (!dir) throw new UsageError("vos open <take> [--studio <url>]");
14416
13952
  const r = createReporter(flags.json === true);
14417
13953
  const take = await loadTake(dir);
14418
- if (!existsSync24(take.paths.recording)) {
13954
+ if (!existsSync23(take.paths.recording)) {
14419
13955
  throw new UsageError(`${dir} has no recording \u2014 re-run record`);
14420
13956
  }
14421
13957
  const studio = (strFlag(flags, "studio") ?? platformOrigin()).replace(
@@ -14456,12 +13992,12 @@ async function cmdJudge(argv) {
14456
13992
  "vos judge <kit.json> --against <MANIFEST.json> [--out dir] [--json]\n(the manifest names the reference set: id, file, role, layout, facts, rule per asset)"
14457
13993
  );
14458
13994
  const r = createReporter(flags.json === true);
14459
- const kitFile = target.endsWith("kit.json") ? target : join28(target, "kit.json");
14460
- if (!existsSync24(kitFile)) throw new UsageError(`${kitFile}: no kit manifest`);
14461
- if (!existsSync24(against))
13995
+ const kitFile = target.endsWith("kit.json") ? target : join26(target, "kit.json");
13996
+ if (!existsSync23(kitFile)) throw new UsageError(`${kitFile}: no kit manifest`);
13997
+ if (!existsSync23(against))
14462
13998
  throw new UsageError(`${against}: no reference manifest`);
14463
13999
  const result = await judgeKit(kitFile, against, strFlag(flags, "out"));
14464
- const verdicts = JSON.parse(await readFile17(result.verdictFile, "utf8")).verdicts;
14000
+ const verdicts = JSON.parse(await readFile15(result.verdictFile, "utf8")).verdicts;
14465
14001
  const rate = winRate(verdicts);
14466
14002
  const lines = result.sheets.map(
14467
14003
  (s) => `${s.asset} vs ${s.reference}: ${s.sheetA}, ${s.sheetB}, ${s.rubric}`
@@ -14501,7 +14037,7 @@ async function cmdCallout(argv) {
14501
14037
  `no register: put a BRAND.md beside the take (vos brand <url> --out BRAND.md; its bgA and accent are the ground and the accent), or pass --ground #hex --accent #hex`
14502
14038
  );
14503
14039
  const codeFile = strFlag(flags, "code-file");
14504
- const code = codeFile ? await readFile17(codeFile, "utf8") : strFlag(flags, "code");
14040
+ const code = codeFile ? await readFile15(codeFile, "utf8") : strFlag(flags, "code");
14505
14041
  const words2 = {
14506
14042
  kicker: strFlag(flags, "kicker"),
14507
14043
  title: strFlag(flags, "title"),
@@ -14580,7 +14116,7 @@ async function cmdValidate(argv) {
14580
14116
  const target = positionals[0];
14581
14117
  if (!target) throw new UsageError("vos validate <actions.json|take>");
14582
14118
  const r = createReporter(flags.json === true);
14583
- const kitTarget = target.endsWith("kit.json") ? target : existsSync24(join28(target, "kit.json")) && !isTakeDir(target) ? join28(target, "kit.json") : null;
14119
+ const kitTarget = target.endsWith("kit.json") ? target : existsSync23(join26(target, "kit.json")) && !isTakeDir(target) ? join26(target, "kit.json") : null;
14584
14120
  if (kitTarget) {
14585
14121
  const picture = flags.picture === true;
14586
14122
  const verdict = await validateKit(kitTarget, { picture });
@@ -14612,9 +14148,9 @@ async function cmdValidate(argv) {
14612
14148
  r.done({ valid: true, target }, `${target}: valid actions file`);
14613
14149
  return EXIT_OK;
14614
14150
  }
14615
- if (!isTakeDir(target) && existsSync24(join28(target, "config.json"))) {
14151
+ if (!isTakeDir(target) && existsSync23(join26(target, "config.json"))) {
14616
14152
  const parsed = JSON.parse(
14617
- await readFile17(join28(target, "config.json"), "utf8")
14153
+ await readFile15(join26(target, "config.json"), "utf8")
14618
14154
  );
14619
14155
  const pre = preflightConfig(parsed);
14620
14156
  const problems2 = pre.ok ? [] : pre.issues.map((i) => `config.json: ${i}`);
@@ -14655,7 +14191,7 @@ async function cmdValidate(argv) {
14655
14191
  const take = await loadTake(target);
14656
14192
  const problems = [];
14657
14193
  const warnings = [];
14658
- if (!existsSync24(take.paths.recording)) {
14194
+ if (!existsSync23(take.paths.recording)) {
14659
14195
  problems.push("missing recording (re-run record)");
14660
14196
  } else {
14661
14197
  const mismatch = containerMismatch(
@@ -15007,7 +14543,7 @@ async function cmdSession(argv) {
15007
14543
  if (!arg) throw new UsageError(usage);
15008
14544
  const bad = sessionNameVerdict(arg);
15009
14545
  if (bad) throw new UsageError(bad);
15010
- if (!existsSync24(sessionDir(arg)))
14546
+ if (!existsSync23(sessionDir(arg)))
15011
14547
  throw new UsageError(`no session "${arg}" (vos session list)`);
15012
14548
  const summary3 = await summarizeSession(arg);
15013
14549
  const url = strFlag(flags, "url");
@@ -15088,10 +14624,6 @@ async function cmdSession(argv) {
15088
14624
 
15089
14625
  export {
15090
14626
  validateActions,
15091
- RECORDING_NAME,
15092
- takePaths,
15093
- loadTake,
15094
- pullMedia,
15095
14627
  startTakeServer,
15096
14628
  waitForPageDone,
15097
14629
  renderAnimation,
@@ -15111,4 +14643,4 @@ export {
15111
14643
  verbHelp,
15112
14644
  run
15113
14645
  };
15114
- //# sourceMappingURL=chunk-CYU7LVPB.js.map
14646
+ //# sourceMappingURL=chunk-NE5MHC3D.js.map