@vosjs/cli 0.69.1 → 0.70.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.
@@ -131,9 +131,9 @@ function landedLines(l) {
131
131
  }
132
132
 
133
133
  // src/plugin/run.ts
134
- import { mkdir as mkdir8, readFile as readFile12, rm as rm2 } from "fs/promises";
135
- import { existsSync as existsSync22 } from "fs";
136
- import { join as join24, resolve as resolve13 } from "path";
134
+ import { mkdir as mkdir8, readFile as readFile13, rm as rm2 } from "fs/promises";
135
+ import { existsSync as existsSync23 } from "fs";
136
+ import { join as join25, resolve as resolve13 } from "path";
137
137
  import {
138
138
  END_CARD_FROM as END_CARD_FROM2,
139
139
  docOutputDuration as docOutputDuration3,
@@ -5341,8 +5341,12 @@ import {
5341
5341
  } from "@vosjs/studio-core";
5342
5342
 
5343
5343
  // src/plugin/reuse.ts
5344
- import { docFreezes } from "@vosjs/studio-core";
5344
+ import { docFreezes, ratedSegments as ratedSegments6 } from "@vosjs/studio-core";
5345
5345
  var MIN_SPAN = 0.15;
5346
+ var AFTER_STEP = 1;
5347
+ var LAYER_AFTER_STEP = 5;
5348
+ var WINDOW_SLACK = 0.05;
5349
+ var LAYER_DRIFT = 0.5;
5346
5350
  function matchStep(old, newSteps) {
5347
5351
  const usable = (s) => !!s && s.skipped !== true;
5348
5352
  if (old.id) {
@@ -5358,6 +5362,19 @@ function matchStep(old, newSteps) {
5358
5362
  );
5359
5363
  return old.selector !== void 0 && bySignature.length === 1 ? bySignature[0] : usable(atIndex) && old.selector === void 0 && atIndex.do === old.do ? atIndex : null;
5360
5364
  }
5365
+ function stepByRef(steps, ref) {
5366
+ return typeof ref === "string" ? steps.find((s) => s.id === ref) : steps.find((s) => s.step === ref);
5367
+ }
5368
+ var stepName = (s) => String(s.id ?? s.step);
5369
+ function stepUnder(t, steps, after = AFTER_STEP) {
5370
+ const gestures = steps.filter((s) => s.do !== "wait" && !s.skipped);
5371
+ const holding = gestures.find(
5372
+ (s) => s.tStart - WINDOW_SLACK <= t && t <= s.tEnd + WINDOW_SLACK
5373
+ );
5374
+ if (holding) return holding;
5375
+ const before = gestures.filter((s) => s.tEnd <= t && t - s.tEnd <= after).sort((a, b) => b.tEnd - a.tEnd);
5376
+ return before[0] ?? null;
5377
+ }
5361
5378
  function buildStepMap(oldSteps, newSteps, oldDuration, newDuration) {
5362
5379
  const points = [
5363
5380
  { from: 0, to: 0 },
@@ -5401,34 +5418,57 @@ function buildStepMap(oldSteps, newSteps, oldDuration, newDuration) {
5401
5418
  return { map, unmatched, matched };
5402
5419
  }
5403
5420
  function resolveAnchor2(anchor, newSteps) {
5404
- const step = typeof anchor.step === "string" ? newSteps.find((s) => s.id === anchor.step) : newSteps.find((s) => s.step === anchor.step);
5421
+ const step = stepByRef(newSteps, anchor.step);
5405
5422
  if (!step || step.skipped) return null;
5406
5423
  const base = anchor.at === "end" ? step.tEnd : step.tStart;
5407
5424
  return base + (anchor.offset ?? 0);
5408
5425
  }
5409
- function retimeSpans(kind, spans, stepMap, newSteps, newDuration, report) {
5426
+ function framed(span) {
5427
+ const s = span;
5428
+ if (typeof s.level === "number" && typeof s.cx === "number") {
5429
+ return ` (it framed ${s.cx.toFixed(2)}, ${(s.cy ?? 0).toFixed(2)} at ${s.level.toFixed(1)}\xD7)`;
5430
+ }
5431
+ if (typeof s.rate === "number") return ` (${s.rate}\xD7 speed)`;
5432
+ if (typeof s.rx === "number" || typeof s.ry === "number") {
5433
+ return ` (lean ${s.rx ?? 0}\xB0, ${s.ry ?? 0}\xB0)`;
5434
+ }
5435
+ if (typeof s.seconds === "number") return ` (a ${s.seconds} s hold)`;
5436
+ return "";
5437
+ }
5438
+ function placeMoment(t, anchor, ctx) {
5439
+ if (anchor) {
5440
+ const at2 = resolveAnchor2(anchor, ctx.newSteps);
5441
+ if (at2 === null) return { gone: String(anchor.step) };
5442
+ ctx.report.anchored++;
5443
+ return { at: at2, via: "anchor" };
5444
+ }
5445
+ const under = stepUnder(t, ctx.oldSteps);
5446
+ if (under) {
5447
+ const hit = matchStep(under, ctx.newSteps);
5448
+ if (!hit) return { gone: under };
5449
+ ctx.report.anchored++;
5450
+ return { at: hit.tStart + (t - under.tStart), via: "step" };
5451
+ }
5452
+ ctx.report.mapped++;
5453
+ return { at: ctx.stepMap.map(t), via: "map" };
5454
+ }
5455
+ var goneWords = (gone) => typeof gone === "string" ? `its anchored step (${gone}) is missing or skipped in the new recording` : `it sat on step ${stepName(gone)} (${gone.do}${gone.selector ? ` ${gone.selector}` : ""}), which is missing or skipped in the new recording`;
5456
+ function retimeSpans(kind, spans, ctx, opts = { overlap: "trim" }) {
5410
5457
  const out = [];
5411
5458
  for (const span of spans) {
5412
5459
  const length = span.out - span.in;
5413
- let nextIn = null;
5414
- if (span.anchor) {
5415
- nextIn = resolveAnchor2(span.anchor, newSteps);
5416
- if (nextIn === null) {
5417
- report.flagged.push(
5418
- `${kind} ${span.id}: its anchored step (${String(span.anchor.step)}) is missing or skipped in the new recording \u2014 fell back to the step map`
5419
- );
5420
- } else {
5421
- report.anchored++;
5422
- }
5423
- }
5424
- if (nextIn === null) {
5425
- nextIn = stepMap.map(span.in);
5426
- report.mapped++;
5460
+ const placed = placeMoment(span.in, span.anchor, ctx);
5461
+ if ("gone" in placed) {
5462
+ ctx.report.flagged.push(
5463
+ `${kind} ${span.id}: ${goneWords(placed.gone)} \u2014 dropped; re-aim it on the new footage${framed(span)}`
5464
+ );
5465
+ continue;
5427
5466
  }
5428
- const nextOut = Math.min(nextIn + length, newDuration);
5429
- const clampedIn = Math.max(0, Math.min(nextIn, newDuration));
5467
+ const nextIn = placed.at;
5468
+ const nextOut = Math.min(nextIn + length, ctx.newDuration);
5469
+ const clampedIn = Math.max(0, Math.min(nextIn, ctx.newDuration));
5430
5470
  if (nextOut - clampedIn < MIN_SPAN) {
5431
- report.flagged.push(
5471
+ ctx.report.flagged.push(
5432
5472
  `${kind} ${span.id}: lands outside the new recording (${clampedIn.toFixed(2)}s) \u2014 dropped`
5433
5473
  );
5434
5474
  continue;
@@ -5436,18 +5476,19 @@ function retimeSpans(kind, spans, stepMap, newSteps, newDuration, report) {
5436
5476
  out.push({ ...span, in: +clampedIn.toFixed(3), out: +nextOut.toFixed(3) });
5437
5477
  }
5438
5478
  out.sort((a, b) => a.in - b.in);
5479
+ if (opts.overlap === "keep") return out;
5439
5480
  const kept = [];
5440
5481
  for (const span of out) {
5441
5482
  const prev = kept.at(-1);
5442
5483
  if (prev && span.in < prev.out) {
5443
5484
  const trimmed = { ...span, in: +prev.out.toFixed(3) };
5444
5485
  if (trimmed.out - trimmed.in < MIN_SPAN) {
5445
- report.flagged.push(
5486
+ ctx.report.flagged.push(
5446
5487
  `${kind} ${span.id}: fully overlapped ${prev.id} after re-timing \u2014 dropped`
5447
5488
  );
5448
5489
  continue;
5449
5490
  }
5450
- report.flagged.push(
5491
+ ctx.report.flagged.push(
5451
5492
  `${kind} ${span.id}: overlapped ${prev.id} after re-timing \u2014 trimmed to ${trimmed.in.toFixed(2)}s`
5452
5493
  );
5453
5494
  kept.push(trimmed);
@@ -5466,9 +5507,10 @@ function retimeCut(prev, newSteps, newDurationMs) {
5466
5507
  for (const miss of stepMap.unmatched) {
5467
5508
  if (miss.do === "wait") continue;
5468
5509
  report.flagged.push(
5469
- `step ${miss.id ?? miss.step} (${miss.do}${miss.selector ? ` ${miss.selector}` : ""}) has no match in the new recording \u2014 times near it interpolate`
5510
+ `step ${stepName(miss)} (${miss.do}${miss.selector ? ` ${miss.selector}` : ""}) has no match in the new recording \u2014 times near it interpolate`
5470
5511
  );
5471
5512
  }
5513
+ const ctx = { oldSteps, newSteps, stepMap, newDuration, report };
5472
5514
  const untrimmed = prev.segments.length === 0 || prev.segments.length === 1 && prev.segments[0].in <= 0.05 && prev.segments[0].out >= oldDuration - 0.05;
5473
5515
  const segments = untrimmed ? [{ in: 0, out: +newDuration.toFixed(3) }] : prev.segments.map((s) => ({
5474
5516
  in: +stepMap.map(s.in).toFixed(3),
@@ -5482,49 +5524,34 @@ function retimeCut(prev, newSteps, newDurationMs) {
5482
5524
  const zoom = retimeSpans(
5483
5525
  "zoom",
5484
5526
  prev.zoom.filter((z) => z.source === "manual"),
5485
- stepMap,
5486
- newSteps,
5487
- newDuration,
5488
- report
5527
+ ctx
5489
5528
  );
5490
5529
  const speed = retimeSpans(
5491
5530
  "speed",
5492
5531
  (prev.speed ?? []).filter((s) => s.source !== "auto"),
5493
- stepMap,
5494
- newSteps,
5495
- newDuration,
5496
- report
5532
+ ctx
5497
5533
  );
5498
5534
  const tilt = retimeSpans(
5499
5535
  "tilt",
5500
5536
  (prev.tilt ?? []).filter((t) => t.source !== "auto"),
5501
- stepMap,
5502
- newSteps,
5503
- newDuration,
5504
- report
5537
+ ctx
5505
5538
  );
5506
5539
  const freeze = [];
5507
5540
  for (const f of docFreezes(prev)) {
5508
- let at2 = null;
5509
- if (f.anchor) {
5510
- at2 = resolveAnchor2(f.anchor, newSteps);
5511
- if (at2 === null) {
5512
- report.flagged.push(
5513
- `freeze ${f.id}: its anchored step (${String(f.anchor.step)}) is missing or skipped in the new recording \u2014 fell back to the step map`
5514
- );
5515
- } else report.anchored++;
5516
- }
5517
- if (at2 === null) {
5518
- at2 = stepMap.map(f.at);
5519
- report.mapped++;
5541
+ const placed = placeMoment(f.at, f.anchor, ctx);
5542
+ if ("gone" in placed) {
5543
+ report.flagged.push(
5544
+ `freeze ${f.id}: ${goneWords(placed.gone)} \u2014 dropped; re-place it on the new footage${framed(f)}`
5545
+ );
5546
+ continue;
5520
5547
  }
5521
- if (at2 < 0 || at2 > newDuration) {
5548
+ if (placed.at < 0 || placed.at > newDuration) {
5522
5549
  report.flagged.push(
5523
- `freeze ${f.id}: lands outside the new recording (${at2.toFixed(2)}s) \u2014 dropped`
5550
+ `freeze ${f.id}: lands outside the new recording (${placed.at.toFixed(2)}s) \u2014 dropped`
5524
5551
  );
5525
5552
  continue;
5526
5553
  }
5527
- freeze.push({ ...f, at: +at2.toFixed(3) });
5554
+ freeze.push({ ...f, at: +placed.at.toFixed(3) });
5528
5555
  }
5529
5556
  freeze.sort((a, b) => a.at - b.at);
5530
5557
  const rejected = [];
@@ -5533,15 +5560,97 @@ function retimeCut(prev, newSteps, newDurationMs) {
5533
5560
  ...retimeSpans(
5534
5561
  `rejected ${lane}`,
5535
5562
  (prev.rejected ?? []).filter((r) => r.lane === lane),
5536
- stepMap,
5537
- newSteps,
5538
- newDuration,
5539
- report
5563
+ ctx,
5564
+ { overlap: "keep" }
5540
5565
  )
5541
5566
  );
5542
5567
  }
5543
5568
  return { segments, zoom, speed, freeze, tilt, rejected, report };
5544
5569
  }
5570
+ function sourceToOutput(pieces, s) {
5571
+ let acc = 0;
5572
+ for (const p of pieces) {
5573
+ const rate = p.rate && p.rate > 0 ? p.rate : 1;
5574
+ const len = Math.max(0, p.out - p.in) / rate;
5575
+ if (s < p.in) return acc;
5576
+ if (s <= p.out) return acc + (s - p.in) / rate;
5577
+ acc += len;
5578
+ }
5579
+ return acc;
5580
+ }
5581
+ function outputToSource(pieces, t) {
5582
+ let acc = 0;
5583
+ for (const p of pieces) {
5584
+ const rate = p.rate && p.rate > 0 ? p.rate : 1;
5585
+ const len = Math.max(0, p.out - p.in) / rate;
5586
+ if (acc + len >= t) return p.in + Math.max(0, t - acc) * rate;
5587
+ acc += len;
5588
+ }
5589
+ return pieces.at(-1)?.out ?? t;
5590
+ }
5591
+ function retimeOverlays(prev, next, report) {
5592
+ const overlays = prev.overlays ?? [];
5593
+ if (!overlays.length) return [];
5594
+ const oldSteps = prev.source.meta.steps ?? [];
5595
+ const newSteps = next.source.meta.steps ?? [];
5596
+ const oldPieces = ratedSegments6(prev);
5597
+ const newPieces = ratedSegments6(next);
5598
+ const oldDuration = prev.source.meta.durationMs / 1e3;
5599
+ const newDuration = next.source.meta.durationMs / 1e3;
5600
+ const stepMap = buildStepMap(oldSteps, newSteps, oldDuration, newDuration);
5601
+ const ctx = { oldSteps, newSteps, stepMap, report };
5602
+ const follow = (o, oldRef, newRef) => {
5603
+ const offset = o.start - sourceToOutput(oldPieces, oldRef);
5604
+ const start = Math.max(0, sourceToOutput(newPieces, newRef) + offset);
5605
+ return { ...o, start: +start.toFixed(3) };
5606
+ };
5607
+ return overlays.map((o) => {
5608
+ const pin = o.pin;
5609
+ if (pin?.step !== void 0) {
5610
+ const was = stepByRef(oldSteps, pin.step);
5611
+ const now2 = was ? matchStep(was, newSteps) : stepByRef(newSteps, pin.step);
5612
+ if (!now2 || now2.skipped) {
5613
+ report.flagged.push(
5614
+ `overlay ${o.id}: pinned to step ${String(pin.step)}, which is missing or skipped in the new recording \u2014 kept at ${o.start.toFixed(2)}s; re-pin or re-place it`
5615
+ );
5616
+ return o;
5617
+ }
5618
+ report.anchored++;
5619
+ return follow(o, was?.tStart ?? now2.tStart, now2.tStart);
5620
+ }
5621
+ if (typeof pin?.press === "number") {
5622
+ const placed = placeMoment(pin.press, void 0, ctx);
5623
+ if ("gone" in placed) {
5624
+ report.flagged.push(
5625
+ `overlay ${o.id}: pinned to a press at ${pin.press.toFixed(2)}s, and ${goneWords(placed.gone)} \u2014 kept at ${o.start.toFixed(2)}s; re-pin or re-place it`
5626
+ );
5627
+ return o;
5628
+ }
5629
+ const moved = follow(o, pin.press, placed.at);
5630
+ return { ...moved, pin: { ...pin, press: +placed.at.toFixed(3) } };
5631
+ }
5632
+ const under = stepUnder(
5633
+ outputToSource(oldPieces, o.start),
5634
+ oldSteps,
5635
+ LAYER_AFTER_STEP
5636
+ );
5637
+ if (!under) return o;
5638
+ const now = matchStep(under, newSteps);
5639
+ if (!now) {
5640
+ report.flagged.push(
5641
+ `overlay ${o.id} (${o.start.toFixed(2)}s): it sat over step ${stepName(under)}, which is missing or skipped in the new recording \u2014 kept at its output time; re-place it`
5642
+ );
5643
+ return o;
5644
+ }
5645
+ const expected = follow(o, under.tStart, now.tStart).start;
5646
+ if (Math.abs(expected - o.start) > LAYER_DRIFT) {
5647
+ report.flagged.push(
5648
+ `overlay ${o.id} (${o.start.toFixed(2)}s): it sat over step ${stepName(under)}, which now plays at ${expected.toFixed(2)}s in the cut \u2014 kept at ${o.start.toFixed(2)}s by the output-time contract; pin it to follow (pin: { step: ${JSON.stringify(under.id ?? under.step)} }) or move it`
5649
+ );
5650
+ }
5651
+ return o;
5652
+ });
5653
+ }
5545
5654
 
5546
5655
  // src/plugin/plan.ts
5547
5656
  var overlaps = (a, b) => a.in < b.out && b.in < a.out;
@@ -5655,7 +5764,9 @@ async function planTake(dir, opts = {}) {
5655
5764
  ].sort((a, b) => a.in - b.in);
5656
5765
  }
5657
5766
  if (rt.tilt.length) doc.tilt = rt.tilt;
5658
- if (prev.overlays?.length) doc.overlays = prev.overlays;
5767
+ if (prev.overlays?.length) {
5768
+ doc.overlays = retimeOverlays(prev, doc, rt.report);
5769
+ }
5659
5770
  if (prev.objects?.length) doc.objects = prev.objects;
5660
5771
  if (prev.audio.length) doc.audio = prev.audio;
5661
5772
  if (prev.camMotion?.length) doc.camMotion = prev.camMotion;
@@ -5763,15 +5874,48 @@ async function planTake(dir, opts = {}) {
5763
5874
 
5764
5875
  // src/plugin/sync.ts
5765
5876
  import { createHash } from "crypto";
5766
- import { existsSync as existsSync10, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
5767
- import { readFile as readFile9 } from "fs/promises";
5877
+ import { existsSync as existsSync11, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
5878
+ import { readFile as readFile10 } from "fs/promises";
5768
5879
  import { createInterface } from "readline/promises";
5769
- import { basename, join as join12 } from "path";
5880
+ import { basename, join as join13 } from "path";
5770
5881
  import { lowerToComposition as lowerToComposition3, migrateHostedDoc as migrateHostedDoc2 } from "@vosjs/studio-core";
5771
5882
 
5772
- // src/plugin/folder.ts
5773
- import { mkdir as mkdir5, writeFile as writeFile7 } from "fs/promises";
5883
+ // src/plugin/takeScript.ts
5884
+ import { existsSync as existsSync10 } from "fs";
5885
+ import { readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
5886
+ import { readFileSync as readFileSync2 } from "fs";
5774
5887
  import { join as join11 } from "path";
5888
+ function scriptFromText(text) {
5889
+ try {
5890
+ const v = JSON.parse(text);
5891
+ return v && typeof v === "object" && !Array.isArray(v) ? v : null;
5892
+ } catch {
5893
+ return null;
5894
+ }
5895
+ }
5896
+ function scriptToWrite(doc, hasLocal) {
5897
+ const script = doc.source.script;
5898
+ if (hasLocal || !script || typeof script !== "object") return null;
5899
+ return `${JSON.stringify(script, null, 2)}
5900
+ `;
5901
+ }
5902
+ function embedScript(doc, dir) {
5903
+ const path = join11(dir, "actions.json");
5904
+ if (!existsSync10(path)) return;
5905
+ const script = scriptFromText(readFileSync2(path, "utf8"));
5906
+ if (script) doc.source.script = script;
5907
+ }
5908
+ async function writeScriptBeside(dir, doc) {
5909
+ const path = join11(dir, "actions.json");
5910
+ const text = scriptToWrite(doc, existsSync10(path));
5911
+ if (text === null) return false;
5912
+ await writeFile7(path, text, "utf8");
5913
+ return true;
5914
+ }
5915
+
5916
+ // src/plugin/folder.ts
5917
+ import { mkdir as mkdir5, writeFile as writeFile8 } from "fs/promises";
5918
+ import { join as join12 } from "path";
5775
5919
  import { recipeHints } from "@vosjs/shared/frontmatter";
5776
5920
  import { migrateHostedDoc } from "@vosjs/studio-core";
5777
5921
  var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["json", "help", "media"]);
@@ -5947,25 +6091,25 @@ async function cmdPull(argv) {
5947
6091
  throw new Error(apiError(`pull folder ${folder.slug}`, res));
5948
6092
  const payload = res.body;
5949
6093
  const out = strFlag(flags, "out") ?? folder.slug;
5950
- await mkdir5(join11(out, "recipes"), { recursive: true });
6094
+ await mkdir5(join12(out, "recipes"), { recursive: true });
5951
6095
  const lines = [];
5952
6096
  const recipes = [];
5953
6097
  for (const rec of payload.recipes) {
5954
- const path = join11(out, "recipes", safeName(rec.filename));
5955
- await writeFile7(path, rec.body ?? "");
6098
+ const path = join12(out, "recipes", safeName(rec.filename));
6099
+ await writeFile8(path, rec.body ?? "");
5956
6100
  recipes.push({ path, line: recipeLine(rec.filename, rec.body ?? "") });
5957
6101
  }
5958
6102
  for (const rec of payload.inheritedRecipes) {
5959
6103
  const from = safeName(rec.folderSlug ?? "inherited");
5960
- await mkdir5(join11(out, "recipes", "_inherited", from), { recursive: true });
5961
- const path = join11(
6104
+ await mkdir5(join12(out, "recipes", "_inherited", from), { recursive: true });
6105
+ const path = join12(
5962
6106
  out,
5963
6107
  "recipes",
5964
6108
  "_inherited",
5965
6109
  from,
5966
6110
  safeName(rec.filename)
5967
6111
  );
5968
- await writeFile7(path, rec.body ?? "");
6112
+ await writeFile8(path, rec.body ?? "");
5969
6113
  recipes.push({
5970
6114
  path,
5971
6115
  line: `${recipeLine(rec.filename, rec.body ?? "")} [inherited from ${rec.folderName ?? from}]`
@@ -5973,15 +6117,15 @@ async function cmdPull(argv) {
5973
6117
  }
5974
6118
  const members = [];
5975
6119
  for (const v of payload.voses) {
5976
- const dir = join11(out, "members", safeName(v.slug || v.title || v.id));
6120
+ const dir = join12(out, "members", safeName(v.slug || v.title || v.id));
5977
6121
  await mkdir5(dir, { recursive: true });
5978
6122
  const cfg = await apiJson(origin, `/api/vos/${v.id}/config`, { key });
5979
6123
  if (cfg.status !== 200) {
5980
6124
  lines.push(`skipped ${v.title}: ${apiError("fetch config", cfg)}`);
5981
6125
  continue;
5982
6126
  }
5983
- await writeFile7(
5984
- join11(dir, "config.json"),
6127
+ await writeFile8(
6128
+ join12(dir, "config.json"),
5985
6129
  JSON.stringify(cfg.body.config, null, 2)
5986
6130
  );
5987
6131
  let take = false;
@@ -5992,8 +6136,8 @@ async function cmdPull(argv) {
5992
6136
  { key }
5993
6137
  );
5994
6138
  if (doc.status === 200) {
5995
- await writeFile7(
5996
- join11(dir, "doc.json"),
6139
+ await writeFile8(
6140
+ join12(dir, "doc.json"),
5997
6141
  JSON.stringify(doc.body, null, 2)
5998
6142
  );
5999
6143
  take = true;
@@ -6119,8 +6263,8 @@ function storageStateInDir(dir) {
6119
6263
  if (!f.endsWith(".json") || f === "doc.json" || f === "meta.json" || f === "actions.json" || f === "cursor.json" || f === "vos.json" || f === "doc.prev.json")
6120
6264
  continue;
6121
6265
  try {
6122
- const v = JSON.parse(readFileSync2(join12(dir, f), "utf8"));
6123
- if (looksLikeStorageState(v)) return join12(dir, f);
6266
+ const v = JSON.parse(readFileSync3(join13(dir, f), "utf8"));
6267
+ if (looksLikeStorageState(v)) return join13(dir, f);
6124
6268
  } catch {
6125
6269
  }
6126
6270
  }
@@ -6141,7 +6285,7 @@ async function pushTake(dir, flags, r) {
6141
6285
  throw new Error(`doc.json fails lint:
6142
6286
  ${lint.problems.join("\n ")}`);
6143
6287
  }
6144
- if (!existsSync10(take.paths.recording)) {
6288
+ if (!existsSync11(take.paths.recording)) {
6145
6289
  throw new Error(
6146
6290
  "no recording in this take \u2014 record it, or `vos pull --media` to bring it home"
6147
6291
  );
@@ -6190,7 +6334,7 @@ async function pushTake(dir, flags, r) {
6190
6334
  throw new Error("push cancelled");
6191
6335
  }
6192
6336
  }
6193
- const bytes = await readFile9(take.paths.recording);
6337
+ const bytes = await readFile10(take.paths.recording);
6194
6338
  const hash = createHash("sha256").update(bytes).digest("hex");
6195
6339
  const { type: contentType } = resolveMediaType({
6196
6340
  head: bytes.subarray(0, MEDIA_HEAD_BYTES),
@@ -6225,6 +6369,7 @@ async function pushTake(dir, flags, r) {
6225
6369
  if (upload.reused) r.log(" recording already hosted \u2014 reused");
6226
6370
  const docForPush = structuredClone(take.doc);
6227
6371
  docForPush.source.videoKey = assetUrl;
6372
+ embedScript(docForPush, dir);
6228
6373
  await uploadDocRefs(sourceSidecarRefs(docForPush), dir, ctx, (l) => r.log(l));
6229
6374
  for (const field of ["micKey", "camKey"]) {
6230
6375
  const key = docForPush.source[field];
@@ -6362,9 +6507,9 @@ async function pullTake(dir, flags, r) {
6362
6507
  };
6363
6508
  }
6364
6509
  let media2;
6365
- if (flags.media && existsSync10(join12(dir, "doc.json"))) {
6510
+ if (flags.media && existsSync11(join13(dir, "doc.json"))) {
6366
6511
  const local = JSON.parse(
6367
- await readFile9(join12(dir, "doc.json"), "utf8")
6512
+ await readFile10(join13(dir, "doc.json"), "utf8")
6368
6513
  );
6369
6514
  media2 = await pullMedia(ctx, dir, local, r.log);
6370
6515
  }
@@ -6425,11 +6570,14 @@ async function pullTake(dir, flags, r) {
6425
6570
  const hostedDoc = migrateHostedDoc2(docRes.json);
6426
6571
  const doc = hostedDoc;
6427
6572
  const localRecording = takeMediaFile(dir, "recording", RECORDING_NAME);
6428
- if (existsSync10(localRecording)) {
6573
+ if (existsSync11(localRecording)) {
6429
6574
  doc.source.videoKey = basename(localRecording);
6430
6575
  }
6431
- await writeJson(join12(dir, "doc.json"), doc, true);
6576
+ await writeJson(join13(dir, "doc.json"), doc, true);
6432
6577
  const media = flags.media ? await pullMedia(ctx, dir, doc, r.log) : void 0;
6578
+ if (flags.media && await writeScriptBeside(dir, doc)) {
6579
+ r.log(" actions.json written from the document");
6580
+ }
6433
6581
  const versions = await api(ctx, `/vos/${vosId}/versions`);
6434
6582
  const headRow = (versions.json.versions ?? []).find((v) => v.id === head);
6435
6583
  writeSyncState(dir, {
@@ -6463,6 +6611,7 @@ async function pushTakeClaimable(dir, flags, r) {
6463
6611
  const state = readSyncState(dir);
6464
6612
  const hosted = state?.hostedMedia ?? {};
6465
6613
  const docForPush = structuredClone(take.doc);
6614
+ embedScript(docForPush, dir);
6466
6615
  const refs = [
6467
6616
  ...isTakeRelativeKey(docForPush.source.videoKey) ? [
6468
6617
  {
@@ -6484,8 +6633,8 @@ async function pushTakeClaimable(dir, flags, r) {
6484
6633
  `${ref.where} (${file}) is not a file this take fetched from vos.so, and a claimable push never uploads footage. Push the take with an account (vos login, then vos push ${dir}), or remix a public take fetched with \`vos fetch <url> --media\``
6485
6634
  );
6486
6635
  }
6487
- const path = join12(dir, file);
6488
- const now = existsSync10(path) ? await fileSha256(path) : null;
6636
+ const path = join13(dir, file);
6637
+ const now = existsSync11(path) ? await fileSha256(path) : null;
6489
6638
  if (now !== origin2.sha256) {
6490
6639
  throw new Error(
6491
6640
  `${ref.where} (${file}) changed since it was fetched, so it is no longer the hosted file. A claimable push names only the original; push with an account to upload the new one`
@@ -6519,10 +6668,10 @@ async function pushTakeClaimable(dir, flags, r) {
6519
6668
  }
6520
6669
 
6521
6670
  // src/plugin/program.ts
6522
- import { existsSync as existsSync12 } from "fs";
6523
- import { mkdir as mkdir6, readFile as readFile10, writeFile as writeFile8 } from "fs/promises";
6671
+ import { existsSync as existsSync13 } from "fs";
6672
+ import { mkdir as mkdir6, readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
6524
6673
  import { createInterface as createInterface2 } from "readline/promises";
6525
- import { basename as basename3, dirname as dirname2, join as join13 } from "path";
6674
+ import { basename as basename3, dirname as dirname2, join as join14 } from "path";
6526
6675
  import {
6527
6676
  CURRENT_CONFIG_VERSION,
6528
6677
  migrateConfig,
@@ -6656,12 +6805,12 @@ async function handoffLogin(url, r, opts = {}) {
6656
6805
 
6657
6806
  // src/plugin/programFiles.ts
6658
6807
  import { basename as basename2, extname as extname2, relative as relative2 } from "path";
6659
- import { existsSync as existsSync11, realpathSync } from "fs";
6808
+ import { existsSync as existsSync12, realpathSync } from "fs";
6660
6809
  function isLocalName(value) {
6661
6810
  if (/^(https?:|data:|blob:|asset:|media:)/.test(value)) return false;
6662
6811
  if (value.startsWith("$")) return false;
6663
6812
  if (value.startsWith("<svg") || value.startsWith("<?xml")) return false;
6664
- if (value.startsWith("/")) return existsSync11(value);
6813
+ if (value.startsWith("/")) return existsSync12(value);
6665
6814
  return true;
6666
6815
  }
6667
6816
  var FILE_EXT = /\.(png|jpe?g|webp|gif|avif|svg|mp4|webm|mov|mp3|m4a|ogg|wav|flac|aac|glb|gltf|woff2?|ttf|otf|hdr|exr)$/i;
@@ -6836,8 +6985,8 @@ function preflightConfig(parsed) {
6836
6985
  }
6837
6986
  function resolveConfigPath(target) {
6838
6987
  if (target.endsWith(".json")) return target;
6839
- const inDir = join13(target, "config.json");
6840
- if (existsSync12(inDir)) return inDir;
6988
+ const inDir = join14(target, "config.json");
6989
+ if (existsSync13(inDir)) return inDir;
6841
6990
  throw new UsageError(
6842
6991
  `${target} has no config.json (and is not a take \u2014 no doc.json)`
6843
6992
  );
@@ -6864,8 +7013,8 @@ async function cmdFetch(argv) {
6864
7013
  const slug = typeof vosMeta.slug === "string" && vosMeta.slug ? vosMeta.slug : vosId;
6865
7014
  const out = strFlag(flags, "out") ?? slug;
6866
7015
  await mkdir6(out, { recursive: true });
6867
- await writeFile8(
6868
- join13(out, "config.json"),
7016
+ await writeFile9(
7017
+ join14(out, "config.json"),
6869
7018
  JSON.stringify(cfg.body.config, null, 2)
6870
7019
  );
6871
7020
  const title = typeof vosMeta.title === "string" ? vosMeta.title : "";
@@ -6890,30 +7039,33 @@ async function cmdFetch(argv) {
6890
7039
  const hosted = migrateHostedDoc3(doc.body);
6891
7040
  const { config: own } = await writeProgramDoc(out, hosted);
6892
7041
  if (own) {
6893
- await writeFile8(join13(out, "config.json"), JSON.stringify(own, null, 2));
7042
+ await writeFile9(join14(out, "config.json"), JSON.stringify(own, null, 2));
6894
7043
  }
6895
7044
  } else if (doc.status === 200) {
6896
7045
  take = true;
6897
7046
  const hosted = migrateHostedDoc3(doc.body);
6898
- await writeFile8(join13(out, "doc.json"), JSON.stringify(hosted, null, 2));
7047
+ await writeFile9(join14(out, "doc.json"), JSON.stringify(hosted, null, 2));
6899
7048
  if (flags.media === true) {
6900
7049
  const media = await pullMedia({ origin, key }, out, hosted, r.log);
6901
7050
  await recordHostedMedia(out, vosId, media);
7051
+ if (await writeScriptBeside(out, hosted)) {
7052
+ r.log(" actions.json written from the document");
7053
+ }
6902
7054
  mediaLine = media.downloaded.length ? ` + ${media.downloaded.map((m) => m.file).join(", ")}` : "";
6903
7055
  }
6904
7056
  }
6905
7057
  }
6906
7058
  let assetsLine = "";
6907
7059
  if (!take) {
6908
- const configPath = join13(out, "config.json");
6909
- const written = JSON.parse(await readFile10(configPath, "utf8"));
7060
+ const configPath = join14(out, "config.json");
7061
+ const written = JSON.parse(await readFile11(configPath, "utf8"));
6910
7062
  const hostedCount = manifestRefs(written).filter(
6911
7063
  ({ ref }) => hostedAssetId(ref, [origin, "https://vos.so", "https://www.vos.so"])
6912
7064
  ).length;
6913
7065
  if (hostedCount && flags.media === true) {
6914
7066
  const pulled = await pullManifest({ origin, key }, out, written, r.log);
6915
7067
  if (pulled) {
6916
- await writeFile8(
7068
+ await writeFile9(
6917
7069
  configPath,
6918
7070
  JSON.stringify({ ...written, assets: pulled.assets }, null, 2)
6919
7071
  );
@@ -7033,7 +7185,7 @@ async function cmdPushProgram(argv) {
7033
7185
  api: strFlag(flags, "api")
7034
7186
  });
7035
7187
  const dir = dirname2(source);
7036
- const parsed = JSON.parse(await readFile10(source, "utf8"));
7188
+ const parsed = JSON.parse(await readFile11(source, "utf8"));
7037
7189
  const pre = preflightConfig(parsed);
7038
7190
  if (!pre.ok || !pre.config) {
7039
7191
  for (const issue of pre.issues) r.log(`error ${issue}`);
@@ -7381,10 +7533,10 @@ async function cmdPullProgram(argv) {
7381
7533
  const cfg = await apiJson(origin, `/api/vos/${vosId}/config`, { key });
7382
7534
  if (cfg.status !== 200)
7383
7535
  throw new Error(apiError(`fetch head config for ${vosId}`, cfg));
7384
- const configPath = join13(dir, "config.json");
7536
+ const configPath = join14(dir, "config.json");
7385
7537
  let backedUp = false;
7386
- if (existsSync12(configPath)) {
7387
- await writeFile8(join13(dir, "config.backup.json"), await readFile10(configPath));
7538
+ if (existsSync13(configPath)) {
7539
+ await writeFile9(join14(dir, "config.backup.json"), await readFile11(configPath));
7388
7540
  backedUp = true;
7389
7541
  }
7390
7542
  let headConfig = cfg.body.config;
@@ -7403,7 +7555,7 @@ async function cmdPullProgram(argv) {
7403
7555
  if (own) headConfig = own;
7404
7556
  }
7405
7557
  }
7406
- await writeFile8(configPath, JSON.stringify(headConfig, null, 2));
7558
+ await writeFile9(configPath, JSON.stringify(headConfig, null, 2));
7407
7559
  writeSyncState(dir, {
7408
7560
  vosId,
7409
7561
  versionId: typeof head.id === "string" ? head.id : since
@@ -7417,7 +7569,7 @@ async function cmdPullProgram(argv) {
7417
7569
  protected: protectedIds,
7418
7570
  changes,
7419
7571
  out: configPath,
7420
- backup: backedUp ? join13(dir, "config.backup.json") : null
7572
+ backup: backedUp ? join14(dir, "config.backup.json") : null
7421
7573
  },
7422
7574
  `Pulled ${changes.length} version${changes.length === 1 ? "" : "s"} \u2192 ${configPath}` + (backedUp ? ` (previous copy: config.backup.json)` : "") + `
7423
7575
  Re-apply your edit on the new head, then: vos push ${configPath} --vos ${vosId}`
@@ -7525,9 +7677,9 @@ function storedProgramConfig(config, programDoc) {
7525
7677
  return lowerProgramDoc(doc).config;
7526
7678
  }
7527
7679
  async function readProgramDoc(dir, config) {
7528
- const docPath = join13(dir, "doc.json");
7529
- if (!existsSync12(docPath)) return null;
7530
- const parsed = JSON.parse(await readFile10(docPath, "utf8"));
7680
+ const docPath = join14(dir, "doc.json");
7681
+ if (!existsSync13(docPath)) return null;
7682
+ const parsed = JSON.parse(await readFile11(docPath, "utf8"));
7531
7683
  if (typeof parsed !== "object" || parsed === null || "source" in parsed)
7532
7684
  return null;
7533
7685
  const raw = parsed;
@@ -7535,23 +7687,23 @@ async function readProgramDoc(dir, config) {
7535
7687
  return { ...raw, program: { ...program, config } };
7536
7688
  }
7537
7689
  async function setProgramStill(dir, t) {
7538
- const docPath = join13(dir, "doc.json");
7690
+ const docPath = join14(dir, "doc.json");
7539
7691
  let doc = { program: {}, audio: [] };
7540
- if (existsSync12(docPath)) {
7541
- const parsed = JSON.parse(await readFile10(docPath, "utf8"));
7692
+ if (existsSync13(docPath)) {
7693
+ const parsed = JSON.parse(await readFile11(docPath, "utf8"));
7542
7694
  if (!parsed || typeof parsed !== "object" || "source" in parsed)
7543
7695
  throw new UsageError(
7544
7696
  `${docPath} is a take's document; a take's cover is its own "still" field`
7545
7697
  );
7546
7698
  doc = parsed;
7547
7699
  }
7548
- await writeFile8(docPath, JSON.stringify({ ...doc, still: t }, null, 2) + "\n");
7700
+ await writeFile9(docPath, JSON.stringify({ ...doc, still: t }, null, 2) + "\n");
7549
7701
  }
7550
7702
  async function writeProgramDoc(dir, hosted) {
7551
7703
  const program = hosted.program && typeof hosted.program === "object" ? hosted.program : {};
7552
7704
  const { config, ...rest } = program;
7553
7705
  const onDisk = { ...hosted, program: rest };
7554
- await writeFile8(join13(dir, "doc.json"), JSON.stringify(onDisk, null, 2));
7706
+ await writeFile9(join14(dir, "doc.json"), JSON.stringify(onDisk, null, 2));
7555
7707
  return {
7556
7708
  config: config && typeof config === "object" ? config : null
7557
7709
  };
@@ -7559,7 +7711,7 @@ async function writeProgramDoc(dir, hosted) {
7559
7711
 
7560
7712
  // src/plugin/asset.ts
7561
7713
  import { createHash as createHash2 } from "crypto";
7562
- import { readFileSync as readFileSync3 } from "fs";
7714
+ import { readFileSync as readFileSync4 } from "fs";
7563
7715
  import { basename as basename4 } from "path";
7564
7716
 
7565
7717
  // src/plugin/assetWords.ts
@@ -7702,7 +7854,7 @@ async function cmdPush(argv) {
7702
7854
  const uploaded = [];
7703
7855
  for (const file of positionals) {
7704
7856
  const name = basename4(file);
7705
- const body = readFileSync3(file);
7857
+ const body = readFileSync4(file);
7706
7858
  const media = resolveMediaType({
7707
7859
  head: body.subarray(0, MEDIA_HEAD_BYTES),
7708
7860
  filename: name
@@ -7963,8 +8115,8 @@ async function cmdAsset(argv) {
7963
8115
  }
7964
8116
 
7965
8117
  // src/plugin/remove.ts
7966
- import { existsSync as existsSync13, rmSync as rmSync2, statSync as statSync3 } from "fs";
7967
- import { join as join14 } from "path";
8118
+ import { existsSync as existsSync14, rmSync as rmSync2, statSync as statSync3 } from "fs";
8119
+ import { join as join15 } from "path";
7968
8120
  import { createInterface as createInterface3 } from "readline/promises";
7969
8121
 
7970
8122
  // src/plugin/trash.ts
@@ -8143,7 +8295,7 @@ async function cmdDelete(argv) {
8143
8295
  api: strFlag(flags, "api")
8144
8296
  }).replace(/\/+$/, "");
8145
8297
  const key = requireCredential(strFlag(flags, "key"));
8146
- const dir = existsSync13(target) && statSync3(target).isDirectory() ? target : null;
8298
+ const dir = existsSync14(target) && statSync3(target).isDirectory() ? target : null;
8147
8299
  const vosId = dir ? readSyncState(dir)?.vosId : parseVosId(target);
8148
8300
  if (!vosId)
8149
8301
  throw new UsageError(
@@ -8186,7 +8338,7 @@ async function cmdDelete(argv) {
8186
8338
  const tracking = dir ?? trackingCwd(vosId);
8187
8339
  let unlinked = false;
8188
8340
  if (tracking) {
8189
- rmSync2(join14(tracking, SYNC_STATE_NAME), { force: true });
8341
+ rmSync2(join15(tracking, SYNC_STATE_NAME), { force: true });
8190
8342
  unlinked = true;
8191
8343
  }
8192
8344
  const trashed = res.body.trashed === true;
@@ -8211,13 +8363,13 @@ async function cmdDelete(argv) {
8211
8363
  }
8212
8364
  function trackingCwd(vosId) {
8213
8365
  const cwd = process.cwd();
8214
- if (!existsSync13(join14(cwd, SYNC_STATE_NAME))) return null;
8366
+ if (!existsSync14(join15(cwd, SYNC_STATE_NAME))) return null;
8215
8367
  return readSyncState(cwd)?.vosId === vosId ? cwd : null;
8216
8368
  }
8217
8369
 
8218
8370
  // src/plugin/buildProgram.ts
8219
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
8220
- import { basename as basename5, dirname as dirname3, join as join15, resolve as resolve5 } from "path";
8371
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
8372
+ import { basename as basename5, dirname as dirname3, join as join16, resolve as resolve5 } from "path";
8221
8373
  import { pathToFileURL } from "url";
8222
8374
  import { parse } from "acorn";
8223
8375
  import { fullAncestor } from "acorn-walk";
@@ -8418,7 +8570,7 @@ async function cmdBuild(argv) {
8418
8570
  throw new UsageError("vos build <program.mjs> [--out config.json] [--json]");
8419
8571
  const r = createReporter(flags.json === true);
8420
8572
  const file = resolve5(source);
8421
- const text = readFileSync4(file, "utf8");
8573
+ const text = readFileSync5(file, "utf8");
8422
8574
  const mod = await import(`${pathToFileURL(file).href}?t=${Date.now()}`);
8423
8575
  const program = mod.default ?? mod.program;
8424
8576
  if (!program || typeof program !== "object")
@@ -8435,7 +8587,7 @@ async function cmdBuild(argv) {
8435
8587
  return EXIT_ERROR;
8436
8588
  }
8437
8589
  const out = resolve5(
8438
- strFlag(flags, "out") ?? join15(dirname3(file), "config.json")
8590
+ strFlag(flags, "out") ?? join16(dirname3(file), "config.json")
8439
8591
  );
8440
8592
  writeFileSync2(out, JSON.stringify(built.config, null, 2) + "\n");
8441
8593
  const sidecar = out.replace(/\.json$/, "") + ".sources.json";
@@ -8452,8 +8604,8 @@ async function cmdBuild(argv) {
8452
8604
 
8453
8605
  // src/plugin/compare.ts
8454
8606
  import { spawnSync } from "child_process";
8455
- import { existsSync as existsSync14, mkdirSync as mkdirSync2, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
8456
- import { dirname as dirname4, join as join16, resolve as resolve6 } from "path";
8607
+ import { existsSync as existsSync15, mkdirSync as mkdirSync2, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
8608
+ import { dirname as dirname4, join as join17, resolve as resolve6 } from "path";
8457
8609
  var BOOLEAN_FLAGS7 = /* @__PURE__ */ new Set(["json", "help"]);
8458
8610
  function sampleTimes(duration, every) {
8459
8611
  const step = every > 0 ? every : 1;
@@ -8520,7 +8672,7 @@ async function cmdCompare(argv) {
8520
8672
  const config = loaded.config;
8521
8673
  const duration = configDuration(config) ?? 5;
8522
8674
  const assets = await programAssets(config, {
8523
- baseDir: /^https?:\/\//.test(source) ? null : existsSync14(source) && statSync4(source).isDirectory() ? source : dirname4(source),
8675
+ baseDir: /^https?:\/\//.test(source) ? null : existsSync15(source) && statSync4(source).isDirectory() ? source : dirname4(source),
8524
8676
  origin: platformOrigin({}),
8525
8677
  key: resolveCredential(),
8526
8678
  log: (line) => r.log(line)
@@ -8543,9 +8695,9 @@ async function cmdCompare(argv) {
8543
8695
  try {
8544
8696
  for (const t of times) {
8545
8697
  const tag = t.toFixed(2);
8546
- const refPng = join16(outDir, `src-${tag}.png`);
8547
- const vosPng = join16(outDir, `vos-${tag}.png`);
8548
- const sheet = join16(outDir, `sheet-${tag}.png`);
8698
+ const refPng = join17(outDir, `src-${tag}.png`);
8699
+ const vosPng = join17(outDir, `vos-${tag}.png`);
8700
+ const sheet = join17(outDir, `sheet-${tag}.png`);
8549
8701
  const grab = ffmpeg([
8550
8702
  "-v",
8551
8703
  "error",
@@ -8602,7 +8754,7 @@ async function cmdCompare(argv) {
8602
8754
  } finally {
8603
8755
  await browser.close();
8604
8756
  }
8605
- const overview = join16(outDir, "overview.png");
8757
+ const overview = join17(outDir, "overview.png");
8606
8758
  if (scores.length) {
8607
8759
  const inputs = scores.flatMap((s) => ["-i", s.sheet]);
8608
8760
  const scaled = scores.map((_, i) => `[${i}:v]scale=iw/3:-2[s${i}]`).join(";");
@@ -8628,7 +8780,7 @@ async function cmdCompare(argv) {
8628
8780
  overview
8629
8781
  };
8630
8782
  writeFileSync3(
8631
- join16(outDir, "report.json"),
8783
+ join17(outDir, "report.json"),
8632
8784
  JSON.stringify(report, null, 2) + "\n"
8633
8785
  );
8634
8786
  r.done(
@@ -8639,28 +8791,28 @@ async function cmdCompare(argv) {
8639
8791
  }
8640
8792
 
8641
8793
  // src/plugin/port.ts
8642
- import { existsSync as existsSync18, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
8643
- import { dirname as dirname7, join as join20, resolve as resolve9 } from "path";
8794
+ import { existsSync as existsSync19, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
8795
+ import { dirname as dirname7, join as join21, resolve as resolve9 } from "path";
8644
8796
  import { pathToFileURL as pathToFileURL3 } from "url";
8645
8797
 
8646
8798
  // src/plugin/portInventory.ts
8647
8799
  import { spawnSync as spawnSync2 } from "child_process";
8648
8800
  import {
8649
- existsSync as existsSync16,
8801
+ existsSync as existsSync17,
8650
8802
  mkdirSync as mkdirSync3,
8651
- readFileSync as readFileSync6,
8803
+ readFileSync as readFileSync7,
8652
8804
  readdirSync as readdirSync4,
8653
8805
  statSync as statSync6
8654
8806
  } from "fs";
8655
- import { dirname as dirname5, join as join18, relative as relative3, resolve as resolve7 } from "path";
8807
+ import { dirname as dirname5, join as join19, relative as relative3, resolve as resolve7 } from "path";
8656
8808
  import { pathToFileURL as pathToFileURL2 } from "url";
8657
8809
  import { findFontFamily as findFontFamily3 } from "@vosjs/shared";
8658
8810
 
8659
8811
  // src/plugin/portRemotion.ts
8660
- import { existsSync as existsSync15, readFileSync as readFileSync5, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
8812
+ import { existsSync as existsSync16, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
8661
8813
  import { createServer as createServer2 } from "http";
8662
8814
  import { createRequire } from "module";
8663
- import { basename as basename6, extname as extname3, join as join17 } from "path";
8815
+ import { basename as basename6, extname as extname3, join as join18 } from "path";
8664
8816
  function themeColors(sources) {
8665
8817
  const out = {};
8666
8818
  const re = /\bconst\s+([A-Za-z_$][\w$]*)\s*(?::\s*string\s*)?=\s*['"](#[0-9a-fA-F]{3,8})['"]/g;
@@ -8726,14 +8878,14 @@ function sourceTexts(dir) {
8726
8878
  if (depth > 4 || out.length > 200) return;
8727
8879
  for (const name of readdirSync3(d)) {
8728
8880
  if (name === "node_modules" || name.startsWith(".")) continue;
8729
- const p = join17(d, name);
8881
+ const p = join18(d, name);
8730
8882
  if (statSync5(p).isDirectory()) walk(p, depth + 1);
8731
8883
  else if (/\.(tsx?|jsx?|mjs)$/.test(name))
8732
- out.push(readFileSync5(p, "utf8"));
8884
+ out.push(readFileSync6(p, "utf8"));
8733
8885
  }
8734
8886
  };
8735
- const src = join17(dir, "src");
8736
- walk(existsSync15(src) ? src : dir, 0);
8887
+ const src = join18(dir, "src");
8888
+ walk(existsSync16(src) ? src : dir, 0);
8737
8889
  return out;
8738
8890
  }
8739
8891
  function entryPoint(dir) {
@@ -8745,7 +8897,7 @@ function entryPoint(dir) {
8745
8897
  "remotion/index.ts",
8746
8898
  "remotion/index.tsx"
8747
8899
  ])
8748
- if (existsSync15(join17(dir, p))) return join17(dir, p);
8900
+ if (existsSync16(join18(dir, p))) return join18(dir, p);
8749
8901
  return null;
8750
8902
  }
8751
8903
  var TYPES = {
@@ -8820,7 +8972,7 @@ async function readRemotion(dir, log) {
8820
8972
  const entry = entryPoint(dir);
8821
8973
  let bundler;
8822
8974
  try {
8823
- bundler = createRequire(join17(dir, "package.json"))("@remotion/bundler");
8975
+ bundler = createRequire(join18(dir, "package.json"))("@remotion/bundler");
8824
8976
  } catch {
8825
8977
  log?.(
8826
8978
  "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"
@@ -8837,13 +8989,13 @@ async function readRemotion(dir, log) {
8837
8989
  } });
8838
8990
  const server = createServer2((rq, rs) => {
8839
8991
  const path = decodeURIComponent(new URL(rq.url ?? "/", "http://x").pathname);
8840
- let f = join17(out, path);
8841
- if (!f.startsWith(out) || !existsSync15(f) || statSync5(f).isDirectory())
8842
- f = join17(out, "index.html");
8992
+ let f = join18(out, path);
8993
+ if (!f.startsWith(out) || !existsSync16(f) || statSync5(f).isDirectory())
8994
+ f = join18(out, "index.html");
8843
8995
  rs.writeHead(200, {
8844
8996
  "content-type": TYPES[extname3(f)] ?? "application/octet-stream"
8845
8997
  });
8846
- rs.end(readFileSync5(f));
8998
+ rs.end(readFileSync6(f));
8847
8999
  }).listen(0, "127.0.0.1");
8848
9000
  await new Promise((r) => server.once("listening", r));
8849
9001
  const port = server.address().port;
@@ -8938,10 +9090,10 @@ async function readRemotion(dir, log) {
8938
9090
  perScene.push({ scene: s.name, texts: read.texts ?? [] });
8939
9091
  }
8940
9092
  const sources = sourceTexts(dir);
8941
- const publicDir = join17(dir, "public");
9093
+ const publicDir = join18(dir, "public");
8942
9094
  const media = tree.audio.map((a) => {
8943
9095
  const name = basename6(a.src.split("?")[0]);
8944
- const local = existsSync15(join17(publicDir, name)) ? `public/${name}` : a.src;
9096
+ const local = existsSync16(join18(publicDir, name)) ? `public/${name}` : a.src;
8945
9097
  return {
8946
9098
  kind: "audio",
8947
9099
  src: local,
@@ -8982,11 +9134,11 @@ async function readRemotion(dir, log) {
8982
9134
 
8983
9135
  // src/plugin/portInventory.ts
8984
9136
  function detectEngine(dir) {
8985
- if (existsSync16(join18(dir, "hyperframes.json"))) return "hyperframes";
8986
- const pkg = join18(dir, "package.json");
8987
- if (existsSync16(pkg)) {
9137
+ if (existsSync17(join19(dir, "hyperframes.json"))) return "hyperframes";
9138
+ const pkg = join19(dir, "package.json");
9139
+ if (existsSync17(pkg)) {
8988
9140
  try {
8989
- const p = JSON.parse(readFileSync6(pkg, "utf8"));
9141
+ const p = JSON.parse(readFileSync7(pkg, "utf8"));
8990
9142
  const deps = { ...p.dependencies ?? {}, ...p.devDependencies ?? {} };
8991
9143
  if ("remotion" in deps) return "remotion";
8992
9144
  if ("hyperframes" in deps) return "hyperframes";
@@ -8998,14 +9150,14 @@ function detectEngine(dir) {
8998
9150
  return "page";
8999
9151
  }
9000
9152
  function sourcePage(source) {
9001
- if (!existsSync16(source)) return null;
9153
+ if (!existsSync17(source)) return null;
9002
9154
  if (statSync6(source).isFile()) return /\.html?$/i.test(source) ? source : null;
9003
- const index = join18(source, "index.html");
9004
- return existsSync16(index) ? index : null;
9155
+ const index = join19(source, "index.html");
9156
+ return existsSync17(index) ? index : null;
9005
9157
  }
9006
9158
  function findRender(dir) {
9007
- const videos = (d) => existsSync16(d) ? readdirSync4(d).filter((f) => /\.(mp4|webm|mov)$/i.test(f)).map((f) => join18(d, f)) : [];
9008
- for (const d of [join18(dir, "renders"), join18(dir, "out"), dir]) {
9159
+ const videos = (d) => existsSync17(d) ? readdirSync4(d).filter((f) => /\.(mp4|webm|mov)$/i.test(f)).map((f) => join19(d, f)) : [];
9160
+ for (const d of [join19(dir, "renders"), join19(dir, "out"), dir]) {
9009
9161
  const found = videos(d);
9010
9162
  if (found.length) return found[0];
9011
9163
  }
@@ -9276,7 +9428,7 @@ var SEEK_AND_READ = `((args) => {
9276
9428
  })`;
9277
9429
  async function readInventory(source, opts) {
9278
9430
  const abs = resolve7(source);
9279
- const dir = existsSync16(abs) && statSync6(abs).isDirectory() ? abs : dirname5(abs);
9431
+ const dir = existsSync17(abs) && statSync6(abs).isDirectory() ? abs : dirname5(abs);
9280
9432
  const engine = detectEngine(dir);
9281
9433
  const page = sourcePage(abs);
9282
9434
  const render = opts.render === void 0 ? findRender(dir) : opts.render;
@@ -9379,7 +9531,7 @@ async function readInventory(source, opts) {
9379
9531
  if (render && duration) {
9380
9532
  const has = spawnSync2("ffmpeg", ["-version"]).status === 0;
9381
9533
  if (has) {
9382
- const refDir = join18(opts.outDir, "ref");
9534
+ const refDir = join19(opts.outDir, "ref");
9383
9535
  mkdirSync3(refDir, { recursive: true });
9384
9536
  const times = read.scenes.length >= 3 ? read.scenes.map(
9385
9537
  (s) => s.start + (s.duration ?? 1) / 2
@@ -9388,7 +9540,7 @@ async function readInventory(source, opts) {
9388
9540
  (_, i) => i + 0.5
9389
9541
  );
9390
9542
  for (const t of times.filter((t2) => t2 < duration)) {
9391
- const file = join18(refDir, `src-${t.toFixed(2)}.png`);
9543
+ const file = join19(refDir, `src-${t.toFixed(2)}.png`);
9392
9544
  const ok = spawnSync2("ffmpeg", [
9393
9545
  "-v",
9394
9546
  "error",
@@ -9442,8 +9594,8 @@ async function readInventory(source, opts) {
9442
9594
 
9443
9595
  // src/plugin/portScaffold.ts
9444
9596
  import { spawnSync as spawnSync3 } from "child_process";
9445
- import { copyFileSync, existsSync as existsSync17, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
9446
- import { basename as basename7, dirname as dirname6, join as join19, resolve as resolve8 } from "path";
9597
+ import { copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
9598
+ import { basename as basename7, dirname as dirname6, join as join20, resolve as resolve8 } from "path";
9447
9599
  import { findFontFamily as findFontFamily4, fontFaceUrl, nearestFontWeight } from "@vosjs/shared";
9448
9600
 
9449
9601
  // src/plugin/painterLib.ts
@@ -9854,7 +10006,7 @@ function planScaffold(inv, opts) {
9854
10006
  inv.media.filter((m) => m.kind === "audio" && m.src).forEach((m, i) => {
9855
10007
  const from = resolve8(opts.sourceDir, m.src);
9856
10008
  const name = basename7(from);
9857
- if (!existsSync17(from)) {
10009
+ if (!existsSync18(from)) {
9858
10010
  report.push(
9859
10011
  `- the audio ${m.src} is not in the source folder; add it to doc.json by hand`
9860
10012
  );
@@ -10052,13 +10204,13 @@ function reportText(inv, r) {
10052
10204
  }
10053
10205
  function writeScaffold(plan, outDir) {
10054
10206
  mkdirSync4(outDir, { recursive: true });
10055
- writeFileSync4(join19(outDir, "program.mjs"), plan.program);
10207
+ writeFileSync4(join20(outDir, "program.mjs"), plan.program);
10056
10208
  writeFileSync4(
10057
- join19(outDir, "doc.json"),
10209
+ join20(outDir, "doc.json"),
10058
10210
  JSON.stringify(plan.doc, null, 2) + "\n"
10059
10211
  );
10060
- writeFileSync4(join19(dirname6(outDir), "REPORT.md"), plan.report);
10061
- for (const [from, name] of plan.copies) copyFileSync(from, join19(outDir, name));
10212
+ writeFileSync4(join20(dirname6(outDir), "REPORT.md"), plan.report);
10213
+ for (const [from, name] of plan.copies) copyFileSync(from, join20(outDir, name));
10062
10214
  }
10063
10215
  function probeAudioDuration(file) {
10064
10216
  const res = spawnSync3(
@@ -10092,7 +10244,7 @@ async function inventory(argv) {
10092
10244
  outDir,
10093
10245
  log: (l) => r.log(`note: ${l}`)
10094
10246
  });
10095
- const file = join20(outDir, "inventory.json");
10247
+ const file = join21(outDir, "inventory.json");
10096
10248
  writeFileSync5(file, JSON.stringify(inv, null, 2) + "\n");
10097
10249
  const missing = inv.fonts.filter((f) => !f.catalog).map((f) => f.family);
10098
10250
  r.log(
@@ -10124,32 +10276,32 @@ function summary(inv) {
10124
10276
  async function scaffold(argv) {
10125
10277
  const { flags } = parseArgs(argv, BOOLEAN_FLAGS8);
10126
10278
  const r = createReporter(flags.json === true);
10127
- const from = resolve9(strFlag(flags, "from") ?? join20("port", "inventory.json"));
10128
- if (!existsSync18(from))
10279
+ const from = resolve9(strFlag(flags, "from") ?? join21("port", "inventory.json"));
10280
+ if (!existsSync19(from))
10129
10281
  throw new UsageError(
10130
10282
  `no inventory at ${from}: run npx vos port inventory <source> first`
10131
10283
  );
10132
- const inv = JSON.parse(readFileSync7(from, "utf8"));
10284
+ const inv = JSON.parse(readFileSync8(from, "utf8"));
10133
10285
  const outDir = resolve9(
10134
- strFlag(flags, "out") ?? join20(dirname7(from), "program")
10286
+ strFlag(flags, "out") ?? join21(dirname7(from), "program")
10135
10287
  );
10136
10288
  const plan = planScaffold(inv, {
10137
10289
  sourceDir: scaffoldSourceDir(inv),
10138
10290
  probeDuration: probeAudioDuration
10139
10291
  });
10140
10292
  writeScaffold(plan, outDir);
10141
- const programFile = join20(outDir, "program.mjs");
10293
+ const programFile = join21(outDir, "program.mjs");
10142
10294
  const mod = await import(`${pathToFileURL3(programFile).href}?t=${Date.now()}`);
10143
10295
  const built = buildProgram(mod.default, "program.mjs", plan.program);
10144
10296
  for (const p of built.problems) r.log(`error ${p.fn}: ${p.message}`);
10145
10297
  if (built.problems.length) return EXIT_ERROR;
10146
10298
  writeFileSync5(
10147
- join20(outDir, "config.json"),
10299
+ join21(outDir, "config.json"),
10148
10300
  JSON.stringify(built.config, null, 2) + "\n"
10149
10301
  );
10150
10302
  r.done(
10151
- { program: outDir, report: join20(dirname7(outDir), "REPORT.md") },
10152
- `Wrote ${outDir}/program.mjs, config.json and doc.json, and ${join20(dirname7(outDir), "REPORT.md")}. Translate the motion where it says TODO, then: npx vos build program.mjs && npx vos compare . --against <their render>`
10303
+ { program: outDir, report: join21(dirname7(outDir), "REPORT.md") },
10304
+ `Wrote ${outDir}/program.mjs, config.json and doc.json, and ${join21(dirname7(outDir), "REPORT.md")}. Translate the motion where it says TODO, then: npx vos build program.mjs && npx vos compare . --against <their render>`
10153
10305
  );
10154
10306
  return EXIT_OK;
10155
10307
  }
@@ -10160,7 +10312,7 @@ function scaffoldSourceDir(inv) {
10160
10312
 
10161
10313
  // src/plugin/recipe.ts
10162
10314
  import { createHash as createHash3 } from "crypto";
10163
- import { readFileSync as readFileSync8 } from "fs";
10315
+ import { readFileSync as readFileSync9 } from "fs";
10164
10316
  import { basename as basename8 } from "path";
10165
10317
  var BOOLEAN_FLAGS9 = /* @__PURE__ */ new Set(["json", "help"]);
10166
10318
  var USAGE = "vos recipe push <file.md> (--folder <folderId|slug> | --asset <assetId>)";
@@ -10180,7 +10332,7 @@ async function cmdPush2(argv) {
10180
10332
  throw new UsageError(USAGE);
10181
10333
  }
10182
10334
  const filename = recipeFilename(file);
10183
- const body = readFileSync8(file);
10335
+ const body = readFileSync9(file);
10184
10336
  const r = createReporter(flags.json === true);
10185
10337
  const origin = platformOrigin({
10186
10338
  origin: strFlag(flags, "origin"),
@@ -10246,7 +10398,7 @@ async function cmdRecipe(argv) {
10246
10398
  }
10247
10399
 
10248
10400
  // src/plugin/brand.ts
10249
- import { writeFile as writeFile9 } from "fs/promises";
10401
+ import { writeFile as writeFile10 } from "fs/promises";
10250
10402
  import { resolve as resolve10 } from "path";
10251
10403
  import { parseFrontmatter as parseFrontmatter2 } from "@vosjs/shared/frontmatter";
10252
10404
  import { lookKindForGround } from "@vosjs/studio-core";
@@ -10600,7 +10752,7 @@ async function cmdBrand(argv) {
10600
10752
  composition,
10601
10753
  llms?.claim ?? design?.description ?? null
10602
10754
  );
10603
- await writeFile9(out, md);
10755
+ await writeFile10(out, md);
10604
10756
  const k = composition.kit;
10605
10757
  r.done(
10606
10758
  {
@@ -10715,7 +10867,7 @@ async function recordHosted(input) {
10715
10867
  }
10716
10868
 
10717
10869
  // src/plugin/agentBrowser.ts
10718
- import { writeFile as writeFile10 } from "fs/promises";
10870
+ import { writeFile as writeFile11 } from "fs/promises";
10719
10871
  import { resolve as resolve11 } from "path";
10720
10872
  function parseAgentBrowserLog(text) {
10721
10873
  const records = [];
@@ -11195,8 +11347,8 @@ async function cmdActions(argv) {
11195
11347
  if (!m) throw new UsageError("--viewport expects WxH, e.g. 1280x720");
11196
11348
  viewport = { width: Number(m[1]), height: Number(m[2]) };
11197
11349
  }
11198
- const { readFile: readFile13 } = await import("fs/promises");
11199
- const text = await readFile13(resolve11(input), "utf8");
11350
+ const { readFile: readFile14 } = await import("fs/promises");
11351
+ const text = await readFile14(resolve11(input), "utf8");
11200
11352
  const { records, problems } = parseAgentBrowserLog(text);
11201
11353
  for (const p of problems) r.log(` ${p}`);
11202
11354
  const result = convertAgentBrowser(records, {
@@ -11213,7 +11365,7 @@ async function cmdActions(argv) {
11213
11365
  );
11214
11366
  return EXIT_ERROR;
11215
11367
  }
11216
- await writeFile10(out, `${JSON.stringify(result.actions, null, 2)}
11368
+ await writeFile11(out, `${JSON.stringify(result.actions, null, 2)}
11217
11369
  `);
11218
11370
  r.done(
11219
11371
  { ok: true, out, url: result.actions.url ?? null, ...summary2(result) },
@@ -11233,9 +11385,9 @@ function summary2(result) {
11233
11385
  }
11234
11386
  async function cmdActionsScript(input, flags) {
11235
11387
  const r = createReporter(flags.json === true);
11236
- const { readFile: readFile13 } = await import("fs/promises");
11388
+ const { readFile: readFile14 } = await import("fs/promises");
11237
11389
  const { shotList, formatShotList } = await import("./shotList-KH6FTX4L.js");
11238
- const raw = JSON.parse(await readFile13(resolve11(input), "utf8"));
11390
+ const raw = JSON.parse(await readFile14(resolve11(input), "utf8"));
11239
11391
  const errors = validateActions(raw);
11240
11392
  if (errors.length) throw new UsageError(`${input}:
11241
11393
  ${errors.join("\n ")}`);
@@ -11245,9 +11397,9 @@ async function cmdActionsScript(input, flags) {
11245
11397
  }
11246
11398
 
11247
11399
  // src/plugin/ingest.ts
11248
- import { existsSync as existsSync19 } from "fs";
11249
- import { copyFile, mkdir as mkdir7, readFile as readFile11, stat as stat2 } from "fs/promises";
11250
- import { basename as basename9, join as join21, resolve as resolve12 } from "path";
11400
+ import { existsSync as existsSync20 } from "fs";
11401
+ import { copyFile, mkdir as mkdir7, readFile as readFile12, stat as stat2 } from "fs/promises";
11402
+ import { basename as basename9, join as join22, resolve as resolve12 } from "path";
11251
11403
  import { inflateRawSync } from "zlib";
11252
11404
  import {
11253
11405
  cursorFromPlaywrightTrace,
@@ -11335,7 +11487,7 @@ async function readTraceFile(path, offsetMs) {
11335
11487
  );
11336
11488
  }
11337
11489
  if (kind === "zip") {
11338
- const bytes = new Uint8Array(await readFile11(path));
11490
+ const bytes = new Uint8Array(await readFile12(path));
11339
11491
  const entry = playwrightTraceEntry(zipEntries(bytes));
11340
11492
  if (!entry) {
11341
11493
  throw new UsageError(
@@ -11348,7 +11500,7 @@ async function readTraceFile(path, offsetMs) {
11348
11500
  );
11349
11501
  return cursorFromPlaywrightTrace(text2, { offsetMs });
11350
11502
  }
11351
- const text = await readFile11(path, "utf8");
11503
+ const text = await readFile12(path, "utf8");
11352
11504
  return cursorFromTrace(basename9(path), text, { offsetMs });
11353
11505
  }
11354
11506
  function parseViewport(v) {
@@ -11365,7 +11517,7 @@ async function cmdIngest(argv) {
11365
11517
  "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]"
11366
11518
  );
11367
11519
  const videoPath = resolve12(source);
11368
- if (!existsSync19(videoPath)) throw new UsageError(`${source}: no such file`);
11520
+ if (!existsSync20(videoPath)) throw new UsageError(`${source}: no such file`);
11369
11521
  const r = createReporter(flags.json === true);
11370
11522
  const outDir = resolve12(strFlag(flags, "out") ?? "take");
11371
11523
  const offsetMs = numFlag(flags, "offset", 0);
@@ -11392,7 +11544,7 @@ async function cmdIngest(argv) {
11392
11544
  trace = await readTraceFile(resolve12(cursorPath), offsetMs);
11393
11545
  for (const n of trace.notes) r.log(`note: ${n}`);
11394
11546
  }
11395
- if (existsSync19(join21(outDir, "meta.json"))) {
11547
+ if (existsSync20(join22(outDir, "meta.json"))) {
11396
11548
  const { prevDoc, kept } = await prepareReRecord(outDir);
11397
11549
  r.log(
11398
11550
  `note: re-ingesting into ${outDir}; kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? `; apply the previous cut with: vos plan ${outDir} --reuse` : "")
@@ -11400,7 +11552,7 @@ async function cmdIngest(argv) {
11400
11552
  }
11401
11553
  await mkdir7(outDir, { recursive: true });
11402
11554
  const paths = await ensureTakeDir(outDir);
11403
- const recording = join21(outDir, `recording.${probed.ext}`);
11555
+ const recording = join22(outDir, `recording.${probed.ext}`);
11404
11556
  r.log("copying the recording\u2026");
11405
11557
  const remuxed = await copyRecording(videoPath, recording, probed.ext);
11406
11558
  const viewport = viewportFlag ?? trace?.viewport ?? {
@@ -11468,19 +11620,19 @@ async function cmdIngest(argv) {
11468
11620
  import { spawnSync as spawnSync4 } from "child_process";
11469
11621
  import {
11470
11622
  cpSync,
11471
- existsSync as existsSync21,
11623
+ existsSync as existsSync22,
11472
11624
  mkdirSync as mkdirSync6,
11473
- readFileSync as readFileSync10,
11625
+ readFileSync as readFileSync11,
11474
11626
  rmSync as rmSync3,
11475
11627
  writeFileSync as writeFileSync6
11476
11628
  } from "fs";
11477
11629
  import { homedir as homedir2 } from "os";
11478
- import { join as join23 } from "path";
11630
+ import { join as join24 } from "path";
11479
11631
  import { chromium as chromium2 } from "playwright";
11480
11632
 
11481
11633
  // src/plugin/machine.ts
11482
- import { existsSync as existsSync20, readFileSync as readFileSync9, readdirSync as readdirSync5, statSync as statSync7 } from "fs";
11483
- import { dirname as dirname8, join as join22 } from "path";
11634
+ import { existsSync as existsSync21, readFileSync as readFileSync10, readdirSync as readdirSync5, statSync as statSync7 } from "fs";
11635
+ import { dirname as dirname8, join as join23 } from "path";
11484
11636
  var AGENT_NAMES = [
11485
11637
  "claude",
11486
11638
  "cursor",
@@ -11500,17 +11652,17 @@ var HOME_DIRS = {
11500
11652
  copilot: [".copilot"]
11501
11653
  };
11502
11654
  function detectAgents(root, opts = {}) {
11503
- const exists = opts.exists ?? existsSync20;
11655
+ const exists = opts.exists ?? existsSync21;
11504
11656
  const out = [];
11505
11657
  for (const agent of AGENT_NAMES) {
11506
11658
  for (const rel of PROJECT_DIRS[agent]) {
11507
- const dir = join22(root, rel);
11659
+ const dir = join23(root, rel);
11508
11660
  if (exists(dir)) out.push({ agent, dir, scope: "project" });
11509
11661
  }
11510
- if (agent === "copilot" && !out.some((h) => h.agent === "copilot") && exists(join22(root, ".github", "copilot-instructions.md"))) {
11662
+ if (agent === "copilot" && !out.some((h) => h.agent === "copilot") && exists(join23(root, ".github", "copilot-instructions.md"))) {
11511
11663
  out.push({
11512
11664
  agent,
11513
- dir: join22(root, ".github", "copilot"),
11665
+ dir: join23(root, ".github", "copilot"),
11514
11666
  scope: "project"
11515
11667
  });
11516
11668
  }
@@ -11518,7 +11670,7 @@ function detectAgents(root, opts = {}) {
11518
11670
  if (opts.global && opts.home) {
11519
11671
  for (const agent of AGENT_NAMES) {
11520
11672
  for (const rel of HOME_DIRS[agent]) {
11521
- const dir = join22(opts.home, rel);
11673
+ const dir = join23(opts.home, rel);
11522
11674
  if (exists(dir)) out.push({ agent, dir, scope: "global" });
11523
11675
  }
11524
11676
  }
@@ -11536,18 +11688,18 @@ function agentsFromFlag(value, root) {
11536
11688
  return {
11537
11689
  homes: wanted.map((agent) => ({
11538
11690
  agent,
11539
- dir: join22(root, PROJECT_DIRS[agent][0]),
11691
+ dir: join23(root, PROJECT_DIRS[agent][0]),
11540
11692
  scope: "project"
11541
11693
  }))
11542
11694
  };
11543
11695
  }
11544
11696
  function skillsDirFor(home) {
11545
- return join22(home.dir, "skills");
11697
+ return join23(home.dir, "skills");
11546
11698
  }
11547
11699
  function fallbackSkillsHome(root) {
11548
- return { agent: "codex", dir: join22(root, ".agents"), scope: "project" };
11700
+ return { agent: "codex", dir: join23(root, ".agents"), scope: "project" };
11549
11701
  }
11550
- function installedSkills(dir, exists = existsSync20, list = (p) => {
11702
+ function installedSkills(dir, exists = existsSync21, list = (p) => {
11551
11703
  try {
11552
11704
  return readdirSync5(p);
11553
11705
  } catch {
@@ -11555,7 +11707,7 @@ function installedSkills(dir, exists = existsSync20, list = (p) => {
11555
11707
  }
11556
11708
  }) {
11557
11709
  if (!exists(dir)) return [];
11558
- return list(dir).filter((name) => exists(join22(dir, name, "SKILL.md"))).sort();
11710
+ return list(dir).filter((name) => exists(join23(dir, name, "SKILL.md"))).sort();
11559
11711
  }
11560
11712
  var RULES_BEGIN = "<!-- vos:begin (written by vos setup; the block is replaced on the next run) -->";
11561
11713
  var RULES_END = "<!-- vos:end -->";
@@ -11588,10 +11740,10 @@ ${RULES_BLOCK}
11588
11740
  }
11589
11741
  return { text, changed: text !== current };
11590
11742
  }
11591
- function rulesFileFor(root, exists = existsSync20) {
11592
- const agents = join22(root, "AGENTS.md");
11743
+ function rulesFileFor(root, exists = existsSync21) {
11744
+ const agents = join23(root, "AGENTS.md");
11593
11745
  if (exists(agents)) return agents;
11594
- const claude = join22(root, "CLAUDE.md");
11746
+ const claude = join23(root, "CLAUDE.md");
11595
11747
  if (exists(claude)) return claude;
11596
11748
  return agents;
11597
11749
  }
@@ -11600,14 +11752,14 @@ function chromeCandidates(platform, env) {
11600
11752
  return ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"];
11601
11753
  if (platform === "win32")
11602
11754
  return [
11603
- join22(
11755
+ join23(
11604
11756
  env["PROGRAMFILES"] ?? "C:\\Program Files",
11605
11757
  "Google",
11606
11758
  "Chrome",
11607
11759
  "Application",
11608
11760
  "chrome.exe"
11609
11761
  ),
11610
- join22(
11762
+ join23(
11611
11763
  env["LOCALAPPDATA"] ?? "",
11612
11764
  "Google",
11613
11765
  "Chrome",
@@ -11694,13 +11846,13 @@ function doctorReport(facts) {
11694
11846
  function bundledSkillsDir(from) {
11695
11847
  let dir = dirname8(from);
11696
11848
  for (let i = 0; i < 6; i++) {
11697
- const pkg = join22(dir, "package.json");
11698
- if (existsSync20(pkg)) {
11849
+ const pkg = join23(dir, "package.json");
11850
+ if (existsSync21(pkg)) {
11699
11851
  try {
11700
- const name = JSON.parse(readFileSync9(pkg, "utf8")).name;
11852
+ const name = JSON.parse(readFileSync10(pkg, "utf8")).name;
11701
11853
  if (name === "@vosjs/cli") {
11702
- const skills = join22(dir, "skills");
11703
- return existsSync20(skills) && statSync7(skills).isDirectory() ? skills : null;
11854
+ const skills = join23(dir, "skills");
11855
+ return existsSync21(skills) && statSync7(skills).isDirectory() ? skills : null;
11704
11856
  }
11705
11857
  } catch {
11706
11858
  }
@@ -11731,7 +11883,7 @@ function npx() {
11731
11883
  function bundledChromium() {
11732
11884
  try {
11733
11885
  const p = chromium2.executablePath();
11734
- return p && existsSync21(p) ? p : null;
11886
+ return p && existsSync22(p) ? p : null;
11735
11887
  } catch {
11736
11888
  return null;
11737
11889
  }
@@ -11740,7 +11892,7 @@ function probe2() {
11740
11892
  return probeBrowser({
11741
11893
  platform: process.platform,
11742
11894
  env: process.env,
11743
- exists: existsSync21,
11895
+ exists: existsSync22,
11744
11896
  bundled: bundledChromium
11745
11897
  });
11746
11898
  }
@@ -11844,7 +11996,7 @@ async function cmdSetup(argv) {
11844
11996
  const dest = skillsDirFor(home);
11845
11997
  mkdirSync6(dest, { recursive: true });
11846
11998
  for (const name of installedSkills(bundled)) {
11847
- cpSync(join23(bundled, name), join23(dest, name), { recursive: true });
11999
+ cpSync(join24(bundled, name), join24(dest, name), { recursive: true });
11848
12000
  }
11849
12001
  }
11850
12002
  skillsVia = "bundled";
@@ -11879,7 +12031,7 @@ async function cmdSetup(argv) {
11879
12031
  let rules = null;
11880
12032
  if (!noRules) {
11881
12033
  const file = rulesFileFor(root);
11882
- const existing = existsSync21(file) ? readFileSync10(file, "utf8") : null;
12034
+ const existing = existsSync22(file) ? readFileSync11(file, "utf8") : null;
11883
12035
  const { text, changed } = applyRulesBlock(existing);
11884
12036
  if (changed) writeFileSync6(file, text);
11885
12037
  rules = { file, changed };
@@ -11993,7 +12145,7 @@ async function cmdWhoami(argv) {
11993
12145
  function cmdLogout(argv) {
11994
12146
  const { flags } = parseArgs(argv, BOOLEAN_FLAGS11);
11995
12147
  const r = reporterFor(flags);
11996
- const had = existsSync21(CREDENTIALS_PATH);
12148
+ const had = existsSync22(CREDENTIALS_PATH);
11997
12149
  rmSync3(CREDENTIALS_PATH, { force: true });
11998
12150
  const envKey = Boolean(process.env.VOS_API_KEY?.trim());
11999
12151
  r.done(
@@ -12199,12 +12351,16 @@ The re-render loop (re-recording the same script after the product
12199
12351
  changed): record into the SAME take dir \u2014 footage/cursor/meta are
12200
12352
  replaced, the cut survives as doc.prev.json \u2014 then plan --reuse re-times
12201
12353
  it onto the new recording. meta.json carries steps (when each actions
12202
- step ran); a span may carry anchor {step, at, offset} tying it to a step
12203
- id/index for exact re-timing; unanchored human spans map piecewise
12204
- through matched step edges; autos re-plan; overlays/audio keep OUTPUT
12205
- times. The done event's reuse block NAMES whatever could not follow \u2014
12206
- fix by exception, never re-cut from scratch. Give steps ids in
12207
- actions.json so anchors survive script edits.
12354
+ step ran); a human span follows the step it was made on (its window, or
12355
+ the gesture just before it), keeping its offset exactly, or an explicit
12356
+ anchor {step, at, offset}; a span between steps maps piecewise through
12357
+ matched step edges; a span whose step vanished is dropped and named with
12358
+ what it framed; autos re-plan; a layer pinned to a step follows its new
12359
+ output time, every other layer and audio keep OUTPUT times, and a layer
12360
+ over a step that moved is named with the pin to write. The done event's
12361
+ reuse block NAMES whatever could not follow \u2014 fix by exception, never
12362
+ re-cut from scratch. Give steps ids in actions.json so re-timing survives
12363
+ script edits.
12208
12364
 
12209
12365
  record --strict exits 2 when any selector was skipped, the page never
12210
12366
  reached networkidle, or the take hit --max-duration (the done event lists
@@ -12328,7 +12484,7 @@ lint-gated, so a bad override fails like a bad doc.json):
12328
12484
  first ready loop (the house backdrop the studio opens on), or a flat ground offline
12329
12485
  `;
12330
12486
  async function loadActions(file) {
12331
- const raw = JSON.parse(await readFile12(file, "utf8"));
12487
+ const raw = JSON.parse(await readFile13(file, "utf8"));
12332
12488
  const errors = validateActions(raw);
12333
12489
  if (errors.length)
12334
12490
  throw new UsageError(`invalid actions file:
@@ -12391,7 +12547,7 @@ function takeStorageState(flags) {
12391
12547
  const raw = strFlag(flags, "storage-state");
12392
12548
  if (!raw) return void 0;
12393
12549
  const path = resolve13(raw);
12394
- if (!existsSync22(path))
12550
+ if (!existsSync23(path))
12395
12551
  throw new UsageError(`--storage-state: no such file: ${path}`);
12396
12552
  return path;
12397
12553
  }
@@ -12404,7 +12560,7 @@ function takeSessionName(flags, storageState) {
12404
12560
  throw new UsageError(
12405
12561
  "--session and --storage-state are two doors to one take: a persistent profile cannot take a storage state. Pass one"
12406
12562
  );
12407
- if (!existsSync22(sessionDir(name)))
12563
+ if (!existsSync23(sessionDir(name)))
12408
12564
  throw new UsageError(
12409
12565
  `no session "${name}". Make one: vos session open <url> --name ${name} (a Chrome window opens; sign in and close it)`
12410
12566
  );
@@ -12627,7 +12783,7 @@ ${lines.join("\n")}
12627
12783
  }
12628
12784
  const paths = takePaths(outDir);
12629
12785
  const prepare = async () => {
12630
- if (existsSync22(join24(outDir, "meta.json"))) {
12786
+ if (existsSync23(join25(outDir, "meta.json"))) {
12631
12787
  const { prevDoc, kept } = await prepareReRecord(outDir);
12632
12788
  r.log(
12633
12789
  `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" : "")
@@ -12692,7 +12848,7 @@ ${lines.join("\n")}
12692
12848
  },
12693
12849
  strictFail ? `STRICT: take recorded but incomplete \u2014 ${strictReason(rec)}; fix the flow and re-record.${skippedNote}${exposureNote(rec)}` : `Take ready: ${outDir}
12694
12850
  ${(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}
12695
- Next: edit ${join24(outDir, "doc.json")} (optional), then: vos render ${outDir}${exposureNote(rec)}`
12851
+ Next: edit ${join25(outDir, "doc.json")} (optional), then: vos render ${outDir}${exposureNote(rec)}`
12696
12852
  );
12697
12853
  return strictFail ? EXIT_USAGE : EXIT_OK;
12698
12854
  } finally {
@@ -12718,7 +12874,7 @@ async function cmdCreate2(argv) {
12718
12874
  throw new UsageError('no URL \u2014 set "url" in the actions file or pass --url');
12719
12875
  const outDir = resolve13(strFlag(flags, "out") ?? "take");
12720
12876
  const format = videoFormat(strFlag(flags, "format"), positionals[0]);
12721
- const out = positionals[0] ?? join24(outDir, `out.${format}`);
12877
+ const out = positionals[0] ?? join25(outDir, `out.${format}`);
12722
12878
  const parallel = numFlag(flags, "parallel", 1);
12723
12879
  if (!Number.isInteger(parallel) || parallel < 1 || parallel > 16) {
12724
12880
  throw new UsageError("--parallel expects an integer between 1 and 16");
@@ -12731,7 +12887,7 @@ async function cmdCreate2(argv) {
12731
12887
  const headers = takeHeaders(multi);
12732
12888
  const paths = takePaths(outDir);
12733
12889
  const prepare = async () => {
12734
- if (existsSync22(join24(outDir, "meta.json"))) {
12890
+ if (existsSync23(join25(outDir, "meta.json"))) {
12735
12891
  const { prevDoc, kept } = await prepareReRecord(outDir);
12736
12892
  r.log(
12737
12893
  `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" : "")
@@ -12838,24 +12994,24 @@ async function cmdPlan(argv) {
12838
12994
  );
12839
12995
  const r = createReporter(flags.json === true);
12840
12996
  if (flags.fresh === true) {
12841
- await rm2(join24(dir, "doc.json"), { force: true });
12997
+ await rm2(join25(dir, "doc.json"), { force: true });
12842
12998
  r.log("note: --fresh discarded the existing doc.json");
12843
12999
  }
12844
13000
  let reuse;
12845
13001
  if (flags.reuse === true) {
12846
- const from = resolve13(strFlag(flags, "from") ?? join24(dir, PREV_DOC_NAME));
12847
- if (!existsSync22(from)) {
13002
+ const from = resolve13(strFlag(flags, "from") ?? join25(dir, PREV_DOC_NAME));
13003
+ if (!existsSync23(from)) {
12848
13004
  throw new UsageError(
12849
13005
  `--reuse: ${from} does not exist \u2014 a re-record into this take writes doc.prev.json, or pass --from <doc.json>`
12850
13006
  );
12851
13007
  }
12852
13008
  reuse = {
12853
13009
  from,
12854
- doc: JSON.parse(await readFile12(from, "utf8"))
13010
+ doc: JSON.parse(await readFile13(from, "utf8"))
12855
13011
  };
12856
13012
  }
12857
13013
  const style = await resolveStyleRef(flags);
12858
- const hasDoc = existsSync22(join24(dir, "doc.json"));
13014
+ const hasDoc = existsSync23(join25(dir, "doc.json"));
12859
13015
  const backdrop = hasDoc ? null : await takeBackdrop(flags, r);
12860
13016
  const release = await releaseInputs(dir, flags, r, multi.with ?? []);
12861
13017
  const motionWanted = !hasDoc || flags.motion === true || flags.fresh === true;
@@ -12913,7 +13069,7 @@ async function cmdPlan(argv) {
12913
13069
  ...s.motion ? { motion: s.motion } : {},
12914
13070
  ...s.reuse ? { reuse: s.reuse } : {}
12915
13071
  },
12916
- `${s.reuse ? "Reused" : s.fresh ? "Planned" : "Refreshed"} ${join24(dir, "doc.json")}: ${s.zoomAuto} auto + ${s.zoomManual} manual zoom spans${s.cursorKept ? "" : " (cursor track dropped)"}${layoutLines}${motionLines}${reuseLines}`
13072
+ `${s.reuse ? "Reused" : s.fresh ? "Planned" : "Refreshed"} ${join25(dir, "doc.json")}: ${s.zoomAuto} auto + ${s.zoomManual} manual zoom spans${s.cursorKept ? "" : " (cursor track dropped)"}${layoutLines}${motionLines}${reuseLines}`
12917
13073
  );
12918
13074
  return EXIT_OK;
12919
13075
  }
@@ -13036,7 +13192,7 @@ async function cmdRender(argv) {
13036
13192
  );
13037
13193
  const r = createReporter(flags.json === true);
13038
13194
  const format = videoFormat(strFlag(flags, "format"), positionals[1]);
13039
- const out = positionals[1] ?? join24(dir, `out.${format}`);
13195
+ const out = positionals[1] ?? join25(dir, `out.${format}`);
13040
13196
  const parallel = numFlag(flags, "parallel", 1);
13041
13197
  if (!Number.isInteger(parallel) || parallel < 1 || parallel > 16) {
13042
13198
  throw new UsageError("--parallel expects an integer between 1 and 16");
@@ -13267,15 +13423,15 @@ async function resolveStyleRef(flags) {
13267
13423
  }
13268
13424
  async function resolveDocRef(ref, flags, what) {
13269
13425
  const styleRef = ref;
13270
- const file = existsSync22(styleRef) ? resolve13(
13426
+ const file = existsSync23(styleRef) ? resolve13(
13271
13427
  styleRef,
13272
- existsSync22(join24(styleRef, "doc.json")) ? "doc.json" : ""
13428
+ existsSync23(join25(styleRef, "doc.json")) ? "doc.json" : ""
13273
13429
  ) : null;
13274
- if (file && existsSync22(file)) {
13430
+ if (file && existsSync23(file)) {
13275
13431
  return {
13276
13432
  from: file,
13277
13433
  doc: migrateHostedDoc4(
13278
- JSON.parse(await readFile12(file, "utf8"))
13434
+ JSON.parse(await readFile13(file, "utf8"))
13279
13435
  )
13280
13436
  };
13281
13437
  }
@@ -13327,7 +13483,7 @@ async function cmdDigest(argv) {
13327
13483
  const take = await loadTake(dir);
13328
13484
  if (!take.doc) throw new UsageError(`${dir} has no doc.json \u2014 run plan first`);
13329
13485
  const transcriptPath = strFlag(flags, "transcript");
13330
- const transcript = transcriptPath ? parseTranscript(JSON.parse(await readFile12(transcriptPath, "utf8"))) : null;
13486
+ const transcript = transcriptPath ? parseTranscript(JSON.parse(await readFile13(transcriptPath, "utf8"))) : null;
13331
13487
  const style = await resolveStyleRef(flags);
13332
13488
  const noFrames = flags["no-frames"] === true;
13333
13489
  let browser = null;
@@ -13358,7 +13514,7 @@ async function cmdDigest(argv) {
13358
13514
  kinds,
13359
13515
  frames: d.images.full,
13360
13516
  crops: d.images.crop,
13361
- sheet: d.images.sheet ? join24(result.outDir, d.images.sheet) : null,
13517
+ sheet: d.images.sheet ? join25(result.outDir, d.images.sheet) : null,
13362
13518
  scenes: kinds.scene ?? 0,
13363
13519
  sourceDuration: d.take.sourceDuration,
13364
13520
  outputDuration: d.take.outputDuration,
@@ -13386,7 +13542,7 @@ async function cmdOpen(argv) {
13386
13542
  if (!dir) throw new UsageError("vos open <take> [--studio <url>]");
13387
13543
  const r = createReporter(flags.json === true);
13388
13544
  const take = await loadTake(dir);
13389
- if (!existsSync22(take.paths.recording)) {
13545
+ if (!existsSync23(take.paths.recording)) {
13390
13546
  throw new UsageError(`${dir} has no recording \u2014 re-run record`);
13391
13547
  }
13392
13548
  const studio = (strFlag(flags, "studio") ?? platformOrigin()).replace(
@@ -13443,7 +13599,7 @@ async function cmdCallout(argv) {
13443
13599
  `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`
13444
13600
  );
13445
13601
  const codeFile = strFlag(flags, "code-file");
13446
- const code = codeFile ? await readFile12(codeFile, "utf8") : strFlag(flags, "code");
13602
+ const code = codeFile ? await readFile13(codeFile, "utf8") : strFlag(flags, "code");
13447
13603
  const words2 = {
13448
13604
  kicker: strFlag(flags, "kicker"),
13449
13605
  title: strFlag(flags, "title"),
@@ -13522,7 +13678,7 @@ async function cmdValidate(argv) {
13522
13678
  const target = positionals[0];
13523
13679
  if (!target) throw new UsageError("vos validate <actions.json|take>");
13524
13680
  const r = createReporter(flags.json === true);
13525
- const kitTarget = target.endsWith("kit.json") ? target : existsSync22(join24(target, "kit.json")) && !isTakeDir(target) ? join24(target, "kit.json") : null;
13681
+ const kitTarget = target.endsWith("kit.json") ? target : existsSync23(join25(target, "kit.json")) && !isTakeDir(target) ? join25(target, "kit.json") : null;
13526
13682
  if (kitTarget) {
13527
13683
  const picture = flags.picture === true;
13528
13684
  const verdict = await validateKit(kitTarget, { picture });
@@ -13554,9 +13710,9 @@ async function cmdValidate(argv) {
13554
13710
  r.done({ valid: true, target }, `${target}: valid actions file`);
13555
13711
  return EXIT_OK;
13556
13712
  }
13557
- if (!isTakeDir(target) && existsSync22(join24(target, "config.json"))) {
13713
+ if (!isTakeDir(target) && existsSync23(join25(target, "config.json"))) {
13558
13714
  const parsed = JSON.parse(
13559
- await readFile12(join24(target, "config.json"), "utf8")
13715
+ await readFile13(join25(target, "config.json"), "utf8")
13560
13716
  );
13561
13717
  const pre = preflightConfig(parsed);
13562
13718
  const problems2 = pre.ok ? [] : pre.issues.map((i) => `config.json: ${i}`);
@@ -13597,7 +13753,7 @@ async function cmdValidate(argv) {
13597
13753
  const take = await loadTake(target);
13598
13754
  const problems = [];
13599
13755
  const warnings = [];
13600
- if (!existsSync22(take.paths.recording)) {
13756
+ if (!existsSync23(take.paths.recording)) {
13601
13757
  problems.push("missing recording (re-run record)");
13602
13758
  } else {
13603
13759
  const mismatch = containerMismatch(
@@ -13982,7 +14138,7 @@ async function cmdSession(argv) {
13982
14138
  if (!arg) throw new UsageError(usage);
13983
14139
  const bad = sessionNameVerdict(arg);
13984
14140
  if (bad) throw new UsageError(bad);
13985
- if (!existsSync22(sessionDir(arg)))
14141
+ if (!existsSync23(sessionDir(arg)))
13986
14142
  throw new UsageError(`no session "${arg}" (vos session list)`);
13987
14143
  const summary3 = await summarizeSession(arg);
13988
14144
  const url = strFlag(flags, "url");
@@ -14082,4 +14238,4 @@ export {
14082
14238
  verbHelp,
14083
14239
  run
14084
14240
  };
14085
- //# sourceMappingURL=chunk-NHJRD2XC.js.map
14241
+ //# sourceMappingURL=chunk-KIFXVPBR.js.map