@vosjs/cli 0.37.0 → 0.38.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.
@@ -2,8 +2,10 @@
2
2
  import {
3
3
  BrowserUnavailableError,
4
4
  directoryKind,
5
- launchBrowser
6
- } from "./chunk-NHKHTIDR.js";
5
+ launchBrowser,
6
+ noteGivenFlag,
7
+ trackFlags
8
+ } from "./chunk-FACYY7VV.js";
7
9
 
8
10
  // src/plugin/run.ts
9
11
  import { mkdir as mkdir10, readFile as readFile15, rm as rm5 } from "fs/promises";
@@ -52,7 +54,9 @@ function parseArgs(argv, booleanFlags, multiFlags = /* @__PURE__ */ new Set()) {
52
54
  }
53
55
  positionals.push(arg);
54
56
  }
55
- return { positionals, flags, multi };
57
+ for (const name of Object.keys(flags)) noteGivenFlag(name);
58
+ for (const name of Object.keys(multi)) noteGivenFlag(name);
59
+ return { positionals, flags: trackFlags(flags), multi: trackFlags(multi) };
56
60
  }
57
61
  function numFlag(flags, name, fallback) {
58
62
  const v = flags[name];
@@ -943,6 +947,17 @@ function lintDoc(docIn) {
943
947
  }
944
948
  const TRANSITIONS = ["none", "fade", "rise"];
945
949
  const PRESETS = ["title", "caption", "label"];
950
+ let footageEnd = null;
951
+ if (recording && Array.isArray(doc.segments)) {
952
+ try {
953
+ footageEnd = ratedSegments(doc).reduce(
954
+ (a, s) => a + (s.out - s.in) / (s.rate ?? 1),
955
+ 0
956
+ );
957
+ } catch {
958
+ footageEnd = null;
959
+ }
960
+ }
946
961
  for (let i = 0; i < (overlays?.length ?? 0); i++) {
947
962
  const o = overlays[i];
948
963
  const name = `overlays[${i}]`;
@@ -960,6 +975,14 @@ function lintDoc(docIn) {
960
975
  if (!isNum(o.duration) || o.duration <= 0) {
961
976
  problems.push(`${name}.duration must be > 0 (seconds)`);
962
977
  }
978
+ if (footageEnd !== null && footageEnd > 0 && isNum(o.start) && isNum(o.duration)) {
979
+ const over = o.start + o.duration - footageEnd;
980
+ if (over > 0.05) {
981
+ warnings.push(
982
+ o.start >= footageEnd ? `${name} starts at ${o.start.toFixed(2)}s, after the footage ends (${footageEnd.toFixed(2)}s): it plays on bare backdrop and lengthens the output by ${over.toFixed(2)}s. A trim moves the footage, never an OUTPUT-anchored layer: re-place it (vos callout with the same --id), or put a freeze under it` : `${name} ends ${over.toFixed(2)}s after the footage (${footageEnd.toFixed(2)}s): the output extends over bare backdrop. Shorten it, or put a freeze under it`
983
+ );
984
+ }
985
+ }
963
986
  checkPin(o, name, docIn, recording, problems, warnings);
964
987
  if (o.kind === "text") {
965
988
  if (typeof o.text !== "string")
@@ -1635,7 +1658,7 @@ function framingWarnings(docIn, doc, zoom, overlays, warnings) {
1635
1658
  if (pinned) {
1636
1659
  if (pinned.clamped > 4) {
1637
1660
  warnings.push(
1638
- `overlays[${i}] is pinned ${pinned.side} of its referent but the frame has no room there \u2014 it sits ${Math.round(pinned.clamped)} design px off; a smaller box, another side, or a lower zoom level keeps it beside what it names`
1661
+ `overlays[${i}] is pinned ${pinned.side} of its referent but the frame has no room there \u2014 it sits ${Math.round(pinned.clamped)} design px off; a smaller box, another side, or a lower zoom level keeps it beside what it names. A pin resolves THROUGH the camera, so author the zoom first: a callout composed before it is measured against a frame the viewer never sees`
1639
1662
  );
1640
1663
  }
1641
1664
  return;
@@ -1798,7 +1821,7 @@ import { pipeline } from "stream/promises";
1798
1821
 
1799
1822
  // src/plugin/take.ts
1800
1823
  import { mkdir, readFile, rename, rm, writeFile } from "fs/promises";
1801
- import { existsSync } from "fs";
1824
+ import { existsSync, readdirSync, statSync } from "fs";
1802
1825
  import { basename, join } from "path";
1803
1826
  var RECORDING_NAME = "recording.webm";
1804
1827
  function takeMediaFiles(dir, stem) {
@@ -1829,6 +1852,20 @@ async function ensureTakeDir(dir) {
1829
1852
  await mkdir(p.framesDir, { recursive: true });
1830
1853
  return p;
1831
1854
  }
1855
+ async function dropScreencastFrames(p) {
1856
+ if (!existsSync(p.recording) || statSync(p.recording).size === 0) return 0;
1857
+ if (!existsSync(p.framesDir)) return 0;
1858
+ let freed = 0;
1859
+ for (const name of readdirSync(p.framesDir)) {
1860
+ try {
1861
+ freed += statSync(join(p.framesDir, name)).size;
1862
+ } catch {
1863
+ }
1864
+ }
1865
+ await rm(p.framesDir, { recursive: true, force: true });
1866
+ await rm(p.framesIndex, { force: true });
1867
+ return freed;
1868
+ }
1832
1869
  async function prepareReRecord(dir) {
1833
1870
  const p = takePaths(dir);
1834
1871
  const kept = [];
@@ -2154,7 +2191,7 @@ import {
2154
2191
 
2155
2192
  // src/plugin/server.ts
2156
2193
  import { createServer } from "http";
2157
- import { existsSync as existsSync3, readFileSync, statSync } from "fs";
2194
+ import { existsSync as existsSync3, readFileSync, statSync as statSync2 } from "fs";
2158
2195
  import { writeFile as writeFile3 } from "fs/promises";
2159
2196
  import { extname, join as join3 } from "path";
2160
2197
  var MIME = {
@@ -2203,8 +2240,8 @@ function startTakeServer(rootDir, pages) {
2203
2240
  return;
2204
2241
  }
2205
2242
  const file = join3(rootDir, decodeURIComponent(url.pathname));
2206
- if (existsSync3(file) && statSync(file).isFile()) {
2207
- const size = statSync(file).size;
2243
+ if (existsSync3(file) && statSync2(file).isFile()) {
2244
+ const size = statSync2(file).size;
2208
2245
  const buf = readFileSync(file);
2209
2246
  const type = sniffMediaType(buf.subarray(0, MEDIA_HEAD_BYTES)) ?? MIME[extname(file)] ?? "application/octet-stream";
2210
2247
  const range = req.headers.range;
@@ -5659,6 +5696,19 @@ function winRate(verdicts) {
5659
5696
  };
5660
5697
  }
5661
5698
 
5699
+ // src/plugin/fontName.ts
5700
+ import { findFontFamily as findFontFamily2 } from "@vosjs/shared";
5701
+ function catalogFamily(family) {
5702
+ const name = family.trim();
5703
+ if (findFontFamily2(name)) return name;
5704
+ const base = name.replace(/\s+Variable$/i, "");
5705
+ if (base !== name) {
5706
+ const hit = findFontFamily2(base);
5707
+ if (hit) return hit.family;
5708
+ }
5709
+ return family;
5710
+ }
5711
+
5662
5712
  // src/plugin/callout.ts
5663
5713
  import { existsSync as existsSync10 } from "fs";
5664
5714
  import { readFile as readFile9 } from "fs/promises";
@@ -5668,6 +5718,7 @@ import {
5668
5718
  CALLOUT_DEFAULT_BODY_PX,
5669
5719
  CALLOUT_SHAPES,
5670
5720
  calloutClip,
5721
+ calloutLook,
5671
5722
  calloutGroundOf,
5672
5723
  cameraModel as cameraModel2,
5673
5724
  deltaE,
@@ -5686,10 +5737,11 @@ var CALLOUT_DELTA_E_PROBLEM = 8;
5686
5737
  var CALLOUT_DELTA_E_WARNING = 16;
5687
5738
  function registerFrom(roles, flags) {
5688
5739
  const ground = flags.ground ?? roles?.bgA;
5689
- const accent = flags.accent ?? roles?.accent;
5740
+ const accent = flags.accent ?? roles?.callout ?? roles?.accent;
5690
5741
  if (!ground || !accent) return null;
5691
5742
  if (!hexToRgb(ground) || !hexToRgb(accent)) return null;
5692
- const face = flags.face ?? roles?.fontBody;
5743
+ const named = flags.face ?? roles?.fontBody;
5744
+ const face = named ? catalogFamily(named) : named;
5693
5745
  return {
5694
5746
  ground,
5695
5747
  accent,
@@ -5755,16 +5807,28 @@ function composeCallout(doc, register, ask) {
5755
5807
  let n = 2;
5756
5808
  while (taken.has(id) && !ask.id)
5757
5809
  id = `${ask.shape}-${ask.step ?? Math.round(window2.start * 10)}-${n++}`;
5810
+ const seen = appScaleAt(doc, window2.start + 0.35);
5811
+ const scale = ask.bodyPxGiven ? 1 : seen;
5812
+ const look = calloutLook(register, scale);
5758
5813
  const clip = calloutClip(ask.shape, register, ask.words, {
5759
5814
  id,
5760
5815
  start: window2.start,
5761
5816
  duration: window2.duration,
5762
- scale: appScaleAt(doc, window2.start + 0.35),
5817
+ scale,
5763
5818
  pin,
5764
5819
  // The fallback place, in the margin: the lower third.
5765
5820
  at: { x: 0.5, y: 0.82 }
5766
5821
  });
5767
- return { clip, window: window2 };
5822
+ return {
5823
+ clip,
5824
+ window: window2,
5825
+ sizes: {
5826
+ scale: +seen.toFixed(3),
5827
+ kickerPx: look.kickerPx,
5828
+ titlePx: look.titlePx,
5829
+ bodyPx: look.bodyPx
5830
+ }
5831
+ };
5768
5832
  }
5769
5833
  function isCalloutShape(s) {
5770
5834
  return CALLOUT_SHAPES.includes(s);
@@ -6585,7 +6649,8 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
6585
6649
  const vh = actions.viewport?.height ?? 720;
6586
6650
  const context = await browser.newContext({
6587
6651
  viewport: { width: vw, height: vh },
6588
- deviceScaleFactor: 1
6652
+ deviceScaleFactor: 1,
6653
+ ...opts.storageState ? { storageState: opts.storageState } : {}
6589
6654
  });
6590
6655
  const page = await context.newPage();
6591
6656
  page.on("console", (m) => {
@@ -7890,6 +7955,15 @@ function printChanges(r, changes, protectedIds) {
7890
7955
  r.event({ event: "protected", nodes: protectedIds });
7891
7956
  }
7892
7957
  }
7958
+ function takePushRefusal(ask) {
7959
+ if (ask.programOnly.length) {
7960
+ const list = ask.programOnly.map((f) => `--${f}`).join(", ");
7961
+ return `${list} ${ask.programOnly.length > 1 ? "belong" : "belongs"} to a PROGRAM push (a config.json); a take push reads --vos, --title, --label, --note, --folder, --override and --yes`;
7962
+ }
7963
+ if (ask.vos !== void 0 && ask.trackedVosId !== null && ask.trackedVosId !== ask.vos)
7964
+ return `this take tracks vos ${ask.trackedVosId} (its vos.json), not ${ask.vos}. Drop --vos to push there, or remove vos.json to adopt ${ask.vos} instead`;
7965
+ return null;
7966
+ }
7893
7967
  async function pushTake(dir, flags, r) {
7894
7968
  const take = await loadTake(dir);
7895
7969
  if (!take.doc) {
@@ -7906,7 +7980,22 @@ async function pushTake(dir, flags, r) {
7906
7980
  );
7907
7981
  }
7908
7982
  const ctx = apiContext(flags);
7909
- const state = readSyncState(dir);
7983
+ const tracked = readSyncState(dir);
7984
+ const firstPush = tracked === null;
7985
+ let state = tracked;
7986
+ if (!state && flags.vos) {
7987
+ const meta = await api(ctx, `/vos/${flags.vos}`);
7988
+ const head = meta.json.vos?.currentVersionId;
7989
+ if (meta.status !== 200 || !head) {
7990
+ throw new Error(
7991
+ `--vos ${flags.vos}: cannot adopt it (${meta.status}${meta.json.error ? `: ${String(meta.json.error)}` : ""}) \u2014 it must be a vos this credential can read, with a version to build on`
7992
+ );
7993
+ }
7994
+ r.log(
7995
+ `adopting vos ${flags.vos}: this take becomes the next version after its head (${head})`
7996
+ );
7997
+ state = { vosId: flags.vos, versionId: head };
7998
+ }
7910
7999
  let folderId;
7911
8000
  if (flags.folder) {
7912
8001
  if (state) {
@@ -7919,7 +8008,7 @@ async function pushTake(dir, flags, r) {
7919
8008
  flags.folder
7920
8009
  ).id;
7921
8010
  }
7922
- if (!state && !flags.yes) {
8011
+ if (firstPush && !flags.yes) {
7923
8012
  if (!process.stdin.isTTY) {
7924
8013
  throw new Error(
7925
8014
  "first push of this take uploads its recording \u2014 pass --yes to consent headlessly"
@@ -9311,10 +9400,12 @@ function composeBrand(input) {
9311
9400
  const bgC = mixHex(bgA, accent, 0.14);
9312
9401
  p.bgC = `bgA tinted 14% toward the accent (a highlight ground)`;
9313
9402
  p.ink = w.h1 ? `the h1's colour` : `the body's colour`;
9314
- const fontDisplay = design?.fonts[0] ?? (w.h1 ? firstFamily(w.h1.fontFamily) : firstFamily(w.body.fontFamily));
9403
+ const fontDisplay = catalogFamily(
9404
+ design?.fonts[0] ?? (w.h1 ? firstFamily(w.h1.fontFamily) : firstFamily(w.body.fontFamily))
9405
+ );
9315
9406
  p.fontDisplay = design?.fonts[0] ? `named in design.md` : w.h1 ? `the h1's computed face` : `the body's computed face (no h1)`;
9316
9407
  const designBody = design?.fonts.slice(1).find((f) => !/\bmono\b/i.test(f));
9317
- const fontBody = designBody ?? firstFamily(w.body.fontFamily);
9408
+ const fontBody = catalogFamily(designBody ?? firstFamily(w.body.fontFamily));
9318
9409
  p.fontBody = designBody ? `named in design.md` : `the body's computed face`;
9319
9410
  const isIcon = (u) => /favicon|apple-touch|icon/i.test(u);
9320
9411
  const onDark = (u) => /on-dark|white/i.test(u);
@@ -10016,14 +10107,15 @@ var BOOLEAN_FLAGS5 = /* @__PURE__ */ new Set([
10016
10107
  "composed",
10017
10108
  "check",
10018
10109
  "motion",
10019
- "leader"
10110
+ "leader",
10111
+ "keep-frames"
10020
10112
  ]);
10021
- var MULTI_FLAGS2 = /* @__PURE__ */ new Set(["set", "override"]);
10113
+ var MULTI_FLAGS2 = /* @__PURE__ */ new Set(["set", "override", "browser-arg"]);
10022
10114
  var HELP = `vos \u2014 record a browser flow, plan effects, render a product video; sync with vos.so
10023
10115
 
10024
10116
  Take pipeline
10025
- vos create --actions actions.json [--url <url>] [--out take] [out.webm] [--strict] [--max-duration <s>] [--background <slug|url|none>] [render flags] [--json]
10026
- vos record --actions actions.json [--url <url>] [--out take] [--strict] [--max-duration <s>] [--background <slug|url|none>] [--json]
10117
+ vos create --actions actions.json [--url <url>] [--out take] [out.webm] [--strict] [--keep-frames] [--max-duration <s>] [--storage-state <file>] [--browser-arg=<switch>]... [--background <slug|url|none>] [render flags] [--json]
10118
+ vos record --actions actions.json [--url <url>] [--out take] [--strict] [--keep-frames] [--max-duration <s>] [--storage-state <file>] [--browser-arg=<switch>]... [--background <slug|url|none>] [--json]
10027
10119
  vos plan <take> [--fresh] [--reuse [--from <doc.json>]] [--style <doc.json|vosId>] [--with <doc.json|vosId>[@end|@start|@step:<id>|@<s>]]... [--background <slug|url|none>] [--motion] [--headline "\u2026"] [--kicker "\u2026"] [--launch LAUNCH.md] [--brand BRAND.md] [--music <slug|mood|none>] [--entrance tilt-in|pull-out|rise|fade|slide|none] [--transitions slide|fade|scale|none] [--end-card on|none|<doc.json|vosId>] [--captions none] [--clicks none] [--still <t>] [--release v2.1] [--json]
10028
10120
  vos render <take> [out.webm] [--width] [--height] [--fps] [--format webm|mp4] [--parallel N] [--range a..b] [--draft] [--frame <kind>] [--background <url|slug>] [--set <path=value>]... [--json]
10029
10121
  vos frames <take> [--times 0,25%,50%,75%,100%] [--frame <t>] [--at-zooms] [--at-moments] [--at-still] [--size WxH] [--out dir] [--background <url|slug>] [--set <path=value>]... [--json]
@@ -10031,7 +10123,7 @@ Take pipeline
10031
10123
  vos digest <take> [--out dir] [--full 960] [--crop 640] [--no-frames] [--transcript <file.json>] [--style <doc.json|vosId>] [--json]
10032
10124
  vos brand <url> [--out BRAND.md] [--json]
10033
10125
  vos open <take> [--studio <url>] [--print]
10034
- vos callout <take> <note|tag|code> --step <id> [--title "\u2026"] [--body "\u2026"] [--kicker "\u2026"] [--code "\u2026"|--code-file f] [--seconds 3] [--side auto|right|left|below|above] [--mark ring|underline|none] [--leader] [--color #hex] [--brand BRAND.md] [--ground #hex] [--accent #hex] [--font "\u2026"] [--body-px 14] [--id x] [--print] [--json]
10126
+ vos callout <take> <note|tag|code> --step <id> [--title "\u2026"] [--body "\u2026"] [--kicker "\u2026"] [--code "\u2026"|--code-file f] [--seconds 3] [--side auto|right|left|below|above] [--mark ring|underline|none] [--leader] [--color #hex (the mark and leader; the kicker is --accent)] [--brand BRAND.md] [--ground #hex] [--accent #hex] [--font "\u2026"] [--body-px <n> (the body size on the delivered frame)] [--id x] [--print] [--json]
10035
10127
  vos validate <actions.json|take|kit.json> [--picture] [--json]
10036
10128
  vos judge <kit.json> --against <MANIFEST.json> [--out dir] [--json]
10037
10129
  vos actions from-agent-browser <steps.jsonl> [--out actions.json] [--url <url>] [--viewport WxH] [--json]
@@ -10051,7 +10143,10 @@ Platform (vos.so) \u2014 fetch, edit, push, pull, repeat
10051
10143
  a take DIRECTORY (doc.json) pushes recording + doc; a config.json
10052
10144
  pushes the program. No --vos: create a PRIVATE vos; with --vos:
10053
10145
  add a version against the tracked base \u2014 a stale push 409s WITH
10054
- the platform's typed changelog. --claimable (programs only, NO
10146
+ the platform's typed changelog. A TAKE pushes to the vos in its
10147
+ vos.json; --vos on a take with none ADOPTS that vos (the take
10148
+ becomes its next version, built on its head), and --vos that
10149
+ disagrees with vos.json is refused before anything uploads. --claimable (programs only, NO
10055
10150
  credential): creates a 72h claim link instead \u2014 hand it to the
10056
10151
  user and nowhere else; unclaimed work is deleted after 72h.
10057
10152
  --override consents to touching
@@ -10298,6 +10393,18 @@ function strictReason(rec) {
10298
10393
  if (rec.capped) parts.push("stopped at --max-duration");
10299
10394
  return parts.join(" + ");
10300
10395
  }
10396
+ function takeBrowserArgs(multi) {
10397
+ const given = multi["browser-arg"];
10398
+ return (given ?? []).filter(Boolean);
10399
+ }
10400
+ function takeStorageState(flags) {
10401
+ const raw = strFlag(flags, "storage-state");
10402
+ if (!raw) return void 0;
10403
+ const path = resolve9(raw);
10404
+ if (!existsSync15(path))
10405
+ throw new UsageError(`--storage-state: no such file: ${path}`);
10406
+ return path;
10407
+ }
10301
10408
  async function takeBackdrop(flags, r) {
10302
10409
  const { backdrop, note } = await openingBackdrop(
10303
10410
  strFlag(flags, "background"),
@@ -10309,8 +10416,20 @@ async function takeBackdrop(flags, r) {
10309
10416
  if (note) r.log(`note: ${note}`);
10310
10417
  return backdrop;
10311
10418
  }
10419
+ async function releaseFrames(paths, flags, log) {
10420
+ if (flags["keep-frames"] === true) return;
10421
+ const freed = await dropScreencastFrames(paths);
10422
+ if (freed >= 1e6)
10423
+ log(
10424
+ `released ${Math.round(freed / 1e6)} MB of screencast frames (the recording holds them now; --keep-frames keeps them)`
10425
+ );
10426
+ }
10312
10427
  async function cmdRecord(argv) {
10313
- const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS5);
10428
+ const { positionals, flags, multi } = parseArgs(
10429
+ argv,
10430
+ BOOLEAN_FLAGS5,
10431
+ MULTI_FLAGS2
10432
+ );
10314
10433
  const actionsPath = strFlag(flags, "actions") ?? positionals[0];
10315
10434
  if (!actionsPath)
10316
10435
  throw new UsageError(
@@ -10324,6 +10443,8 @@ async function cmdRecord(argv) {
10324
10443
  const outDir = resolve9(strFlag(flags, "out") ?? "take");
10325
10444
  const backdrop = await takeBackdrop(flags, r);
10326
10445
  const maxDurationSeconds = await maxDuration(flags, r);
10446
+ const storageState = takeStorageState(flags);
10447
+ const browserArgs = takeBrowserArgs(multi);
10327
10448
  if (existsSync15(join18(outDir, "meta.json"))) {
10328
10449
  const { prevDoc, kept } = await prepareReRecord(outDir);
10329
10450
  r.log(
@@ -10332,12 +10453,13 @@ async function cmdRecord(argv) {
10332
10453
  }
10333
10454
  await mkdir10(outDir, { recursive: true });
10334
10455
  const paths = await ensureTakeDir(outDir);
10335
- const browser = await launchBrowser();
10456
+ const browser = await launchBrowser(browserArgs);
10336
10457
  try {
10337
10458
  r.log("recording\u2026");
10338
10459
  r.event({ event: "phase", phase: "record" });
10339
10460
  const rec = await recordTake(browser, url, actions, paths, r.log, {
10340
- maxDurationSeconds
10461
+ maxDurationSeconds,
10462
+ storageState
10341
10463
  });
10342
10464
  r.event({ event: "phase", phase: "encode" });
10343
10465
  r.log("encoding\u2026");
@@ -10346,6 +10468,7 @@ async function cmdRecord(argv) {
10346
10468
  outDir,
10347
10469
  (p) => r.event({ event: "progress", phase: "encode", fraction: p })
10348
10470
  );
10471
+ await releaseFrames(paths, flags, r.log);
10349
10472
  r.event({ event: "phase", phase: "plan" });
10350
10473
  const plan = await planTake(outDir, { backdrop });
10351
10474
  const clicks = rec.events.filter((e) => e.type === "down").length;
@@ -10408,6 +10531,8 @@ async function cmdCreate2(argv) {
10408
10531
  }
10409
10532
  const backdrop = await takeBackdrop(flags, r);
10410
10533
  const maxDurationSeconds = await maxDuration(flags, r);
10534
+ const storageState = takeStorageState(flags);
10535
+ const browserArgs = takeBrowserArgs(multi);
10411
10536
  if (existsSync15(join18(outDir, "meta.json"))) {
10412
10537
  const { prevDoc, kept } = await prepareReRecord(outDir);
10413
10538
  r.log(
@@ -10416,12 +10541,13 @@ async function cmdCreate2(argv) {
10416
10541
  }
10417
10542
  await mkdir10(outDir, { recursive: true });
10418
10543
  const paths = await ensureTakeDir(outDir);
10419
- const browser = await launchBrowser();
10544
+ const browser = await launchBrowser(browserArgs);
10420
10545
  try {
10421
10546
  r.log("recording\u2026");
10422
10547
  r.event({ event: "phase", phase: "record" });
10423
10548
  const rec = await recordTake(browser, url, actions, paths, r.log, {
10424
- maxDurationSeconds
10549
+ maxDurationSeconds,
10550
+ storageState
10425
10551
  });
10426
10552
  r.event({ event: "phase", phase: "encode" });
10427
10553
  r.log("encoding\u2026");
@@ -10430,6 +10556,7 @@ async function cmdCreate2(argv) {
10430
10556
  outDir,
10431
10557
  (p) => r.event({ event: "progress", phase: "encode", fraction: p })
10432
10558
  );
10559
+ await releaseFrames(paths, flags, r.log);
10433
10560
  r.event({ event: "phase", phase: "plan" });
10434
10561
  await planTake(outDir, { backdrop });
10435
10562
  if (flags.strict === true && (rec.skipped.length > 0 || rec.navTimeout || rec.capped)) {
@@ -11129,7 +11256,7 @@ async function cmdCallout(argv) {
11129
11256
  const shape = positionals[1];
11130
11257
  if (!dir || !shape || !isCalloutShape(shape))
11131
11258
  throw new UsageError(
11132
- 'vos callout <take> <note|tag|code> --step <id> [--title "\u2026"] [--body "\u2026"] [--kicker "\u2026"] [--code "\u2026"|--code-file f] [--seconds 3] [--side \u2026] [--mark ring|underline|none] [--leader] [--color #hex] [--brand BRAND.md] [--ground #hex --accent #hex] [--font "\u2026"] [--body-px 14] [--id x] [--print] [--json]'
11259
+ 'vos callout <take> <note|tag|code> --step <id> [--title "\u2026"] [--body "\u2026"] [--kicker "\u2026"] [--code "\u2026"|--code-file f] [--seconds 3] [--side \u2026] [--mark ring|underline|none] [--leader] [--color #hex (the mark and leader; the kicker is --accent)] [--brand BRAND.md] [--ground #hex --accent #hex] [--font "\u2026"] [--body-px <n> (the body size on the delivered frame)] [--id x] [--print] [--json]'
11133
11260
  );
11134
11261
  const r = createReporter(flags.json === true);
11135
11262
  const take = await loadTake(dir);
@@ -11180,15 +11307,16 @@ async function cmdCallout(argv) {
11180
11307
  side,
11181
11308
  mark,
11182
11309
  leader: flags.leader === true,
11183
- color: strFlag(flags, "color")
11310
+ color: strFlag(flags, "color"),
11311
+ bodyPxGiven: bodyPx !== void 0
11184
11312
  });
11185
11313
  } catch (e) {
11186
11314
  throw new UsageError(e instanceof Error ? e.message : String(e));
11187
11315
  }
11188
- const { clip, window: window2 } = composed;
11316
+ const { clip, window: window2, sizes } = composed;
11189
11317
  if (flags.print === true) {
11190
11318
  r.done(
11191
- { clip, window: window2, register, brand: brand?.file ?? null },
11319
+ { clip, window: window2, sizes, register, brand: brand?.file ?? null },
11192
11320
  JSON.stringify(clip, null, 2)
11193
11321
  );
11194
11322
  return EXIT_OK;
@@ -11210,11 +11338,12 @@ async function cmdCallout(argv) {
11210
11338
  {
11211
11339
  clip,
11212
11340
  window: window2,
11341
+ sizes,
11213
11342
  register,
11214
11343
  brand: brand?.file ?? null,
11215
11344
  warnings: lint.warnings
11216
11345
  },
11217
- `${shape} "${clip.id}" at ${window2.start.toFixed(2)}s for ${window2.duration.toFixed(2)}s${clip.pin ? `, pinned to ${clip.pin.step !== void 0 ? `step ${String(clip.pin.step)}` : `the press at ${String(clip.pin.press)}s`}` : ""}, ground ${register.ground} \u2192 card in the product's hue, written to ${take.paths.doc}${lint.warnings.length ? `
11346
+ `${shape} "${clip.id}" at ${window2.start.toFixed(2)}s for ${window2.duration.toFixed(2)}s${clip.pin ? `, pinned to ${clip.pin.step !== void 0 ? `step ${String(clip.pin.step)}` : `the press at ${String(clip.pin.press)}s`}` : ""}, ground ${register.ground} \u2192 card in the product's hue, type ${sizes.kickerPx} / ${sizes.titlePx} / ${sizes.bodyPx} px (kicker / title / body; ${bodyPx !== void 0 ? `--body-px ${bodyPx} as delivered` : `the app's body as seen, footage scale ${sizes.scale}; --body-px <n> sets the delivered size`}), written to ${take.paths.doc}${lint.warnings.length ? `
11218
11347
  warnings:
11219
11348
  ${lint.warnings.join("\n ")}` : ""}`
11220
11349
  );
@@ -11377,8 +11506,16 @@ async function cmdPush3(argv) {
11377
11506
  const dir = positionals[0];
11378
11507
  if (!dir)
11379
11508
  throw new UsageError(
11380
- "vos push <take> [--title|--label|--note|--folder|--override|--yes|--key|--api]"
11509
+ "vos push <take> [--vos|--title|--label|--note|--folder|--override|--yes|--key|--api]"
11381
11510
  );
11511
+ const refusal = takePushRefusal({
11512
+ vos: strFlag(flags, "vos"),
11513
+ trackedVosId: readSyncState(resolve9(dir))?.vosId ?? null,
11514
+ programOnly: ["slug", "desc", "tags", "base", "remix-of"].filter(
11515
+ (name) => hasFlag(flags, name)
11516
+ )
11517
+ });
11518
+ if (refusal) throw new UsageError(refusal);
11382
11519
  const r = createReporter(flags.json === true);
11383
11520
  const overrides = multi.override;
11384
11521
  const result = await pushTake(
@@ -11388,6 +11525,7 @@ async function cmdPush3(argv) {
11388
11525
  api: strFlag(flags, "api"),
11389
11526
  origin: strFlag(flags, "origin"),
11390
11527
  yes: flags.yes === true,
11528
+ vos: strFlag(flags, "vos"),
11391
11529
  title: strFlag(flags, "title"),
11392
11530
  label: strFlag(flags, "label"),
11393
11531
  note: strFlag(flags, "note"),
@@ -11548,6 +11686,9 @@ export {
11548
11686
  parseAgentBrowserLog,
11549
11687
  splitCommand,
11550
11688
  convertAgentBrowser,
11689
+ MULTI_FLAGS2 as MULTI_FLAGS,
11690
+ HELP,
11691
+ takeBrowserArgs,
11551
11692
  run
11552
11693
  };
11553
- //# sourceMappingURL=chunk-DD6OXVFJ.js.map
11694
+ //# sourceMappingURL=chunk-LGRNHJ2U.js.map