@vosjs/cli 0.38.0 → 0.40.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.
- package/README.md +8 -4
- package/dist/{chunk-LGRNHJ2U.js → chunk-7TDL3N24.js} +103 -30
- package/dist/chunk-7TDL3N24.js.map +1 -0
- package/dist/cli.js +15 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1 -1
- package/dist/{run-ZNQKSGAF.js → run-SDPKU6AV.js} +2 -2
- package/package.json +7 -7
- package/dist/chunk-LGRNHJ2U.js.map +0 -1
- /package/dist/{run-ZNQKSGAF.js.map → run-SDPKU6AV.js.map} +0 -0
package/README.md
CHANGED
|
@@ -60,6 +60,7 @@ A **take** is a directory: the recording of a scripted browser flow, its exact c
|
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
62
|
vos create --actions actions.json out.webm --strict # one shot: record, auto-plan, render
|
|
63
|
+
vos record --actions actions.json --out take --dry-run # rehearse the script first: which selectors resolve, their rects, in seconds
|
|
63
64
|
vos record --actions actions.json --out take --strict # drive the page, record it with an exact cursor track, plan the cut
|
|
64
65
|
vos digest take # SEE the recording before cutting: moments, frames, crops (an agent's eyes)
|
|
65
66
|
# … edit take/doc.json (zoom spans, trims, speed, overlays) by hand or by agent …
|
|
@@ -79,7 +80,7 @@ vos plan take --reuse # re-time that cut onto
|
|
|
79
80
|
|
|
80
81
|
| Verb | Flags |
|
|
81
82
|
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
82
|
-
| `record` | `--actions <file>` (or positional) `--url` `--out take` `--strict` `--max-duration <s>` `--background <slug\|url\|none>`
|
|
83
|
+
| `record` | `--actions <file>` (or positional) `--url` `--out take` `--strict` `--dry-run` `--keep-frames` `--storage-state <file>` `--browser-arg=<switch>`... `--max-duration <s>` `--background <slug\|url\|none>` |
|
|
83
84
|
| `create` | The `record` flags plus the render flags (`--width` `--height` `--fps` `--format` `--parallel` `--draft` `--frame` `--set`), no `--range`. With `--strict` an incomplete recording exits 2 before anything is rendered |
|
|
84
85
|
| `plan` | `--fresh` (discard the current plan) `--reuse` `--from <doc.json>` (defaults to `<take>/doc.prev.json`) `--style <doc.json\|take\|vosId>` `--with <doc.json\|take\|vosId>[@end\|@start\|@step:<id>\|@<seconds>]` (a template, repeatable) `--background` `--motion` (re-propose the motion) `--headline` `--kicker` `--launch` `--brand` `--music` `--entrance` `--transitions slide\|fade\|scale\|none` `--end-card on\|none\|<ref>` `--captions` `--clicks` `--release` |
|
|
85
86
|
| `digest` | `--out <take>/digest` `--full 960` `--crop 640` (image long edges, the token budget) `--no-frames` `--transcript <file>` (Whisper-shaped segments merged as `said`) `--style <ref>` (report a reference document's style fields) |
|
|
@@ -87,6 +88,8 @@ vos plan take --reuse # re-time that cut onto
|
|
|
87
88
|
| `render` | `--width` `--height` `--fps` `--format webm\|mp4` `--parallel N` (1..16 sessions) `--range a..b` (output seconds; keeps its audio) `--draft` `--frame <kind>` `--background` `--set …`; `out` defaults to `<take>/out.<format>` |
|
|
88
89
|
| `open` | `--studio http://localhost:6060` `--print` (print the URL, do not launch a browser) |
|
|
89
90
|
|
|
91
|
+
**Rehearse before you record.** `vos record … --dry-run` runs every step against the real page, in order, because a later selector usually exists only after an earlier click. Nothing is captured and nothing is written: the pointer lands instead of travelling, every pause is cut to a beat, and the take directory beside it keeps its footage, its cut and its script exactly as they were (a real re-record moves `doc.json` aside; a rehearsal does not). Selector lookups keep their whole timeout, so a miss here is a miss in the take. It prints each step with the rect it resolved, in capture px, which are the rects a pin or `vos callout --step` reads, and exits 2 on any miss or a first load that never settled. Add `--dry-run` to the exact command you were about to run; `--storage-state` and `--browser-arg=` apply to it too.
|
|
92
|
+
|
|
90
93
|
**Digest first.** `vos digest <take>` is how an agent sees a recording without reading the video. It writes `digest/digest.json`: one moment per thing the cursor track says mattered (click clusters, typing sessions, scroll runs, dwells, idle gaps, head, tail, and frame-diff scene changes), each with source and output extents, a normalized `focus` and `rect` you can copy into a zoom span, per-second `activity`, and the planners' `proposed` span ids; plus one footage frame and a crop around the target per moment, and `sheet.png`, the contact sheet. Read the JSON, then the sheet, then a crop only where you must decide. `vos validate` then warns when a zoom does not contain what was clicked under it, and `vos frames --at-moments` renders the composed output at every moment so a still and its footage crop share an id.
|
|
91
94
|
|
|
92
95
|
**Many media in one take (concat).** `doc.json` may carry `media: [{ id, videoKey, cursor, meta, … }]`, the take's OTHER recordings or uploads, each the `source` shape with an `id`; a segment on one (`{ in, out, media: "m1" }`) plays it, and a zoom, tilt, speed, freeze or cam-move span names the media its source seconds belong to (absent = the primary, `source`). The Video row shows the clips in order, a span is drawn where its media plays, `vos plan` proposes zoom and speed spans on every media from its own cursor track, `vos validate` measures each span against its own media's length, and `vos push` and `vos pull --media` carry every media through the recording door. A media wears its OWN card: `media[].frame` carries the card-owned fields (its placement and size as `inset`, the browser bar, the corner, the shadow, the border, the cover fit and its focus) over the take's frame while it plays; the bar names that media's recorded page, an upload with no page wears none, and the frame-wide fields (the aspect, the padding, the ground, the backdrop, the card's animation) stay the take's. Sound: the primary's tracks play at the primary's moments; another media's own audio is not spliced into the cut yet. **Many cards**: an image or video overlay may show a document media by reference (`key: "media:<id>"`, `media:` alone the primary; its cursor track and recorded page come with it) and wear a card (`frame`: `browserBar`, a `lean` `{rx, ry}` in degrees, `shadow`, `shadowContact`, `shadowColor`, `cursor`), drawn by the card painter on its own plane above the primary card with the media's cursor dot and click rings inside it; a layer without `frame` stays the flat picture. The primary card stays primary: the sequence, the camera and the cut are its.
|
|
@@ -214,8 +217,9 @@ Everything that runs on your machine is open source; [vos.so](https://vos.so) is
|
|
|
214
217
|
vos login [--key <k>] [--label <name>] [--no-browser] # browser sign-in: a code and a vos.so/cli/auth URL, a human approves, the key stores itself
|
|
215
218
|
vos fetch <vosId|url> [--out <slug>] [--media] # a program writes config.json + vos.json; a take writes doc.json (+ the recording with --media)
|
|
216
219
|
vos check bright-loop/config.json # full local validation
|
|
217
|
-
vos push bright-loop/config.json # create a PRIVATE vos (
|
|
218
|
-
vos push bright-loop/config.json --vos <id> # add a version
|
|
220
|
+
vos push bright-loop/config.json # untracked: create a PRIVATE vos. Tracked (vos.json): add a version to it
|
|
221
|
+
vos push bright-loop/config.json --vos <id> # add a version to a vos this directory does not track
|
|
222
|
+
vos push bright-loop/config.json --remix-of <id> # create a SEPARATE vos from this config, with that lineage
|
|
219
223
|
vos push bright-loop/config.json --claimable # no credential: a 72 h claim link instead (programs only)
|
|
220
224
|
vos push take --yes --label "first pass" --note "…" # host a take: private vos + version history (the recording uploads once)
|
|
221
225
|
vos pull bright-loop [--since <versionId>] [--check] # what changed on vos.so since your base; syncs config.json (backup kept), or doc.json for a take
|
|
@@ -226,7 +230,7 @@ vos asset push <file…> [--folder <slug>] | rename <id> <name.ext>
|
|
|
226
230
|
vos recipe push <FILE.md> --folder <slug> | --asset <id> # the one recipe write: create, or replace in place
|
|
227
231
|
```
|
|
228
232
|
|
|
229
|
-
`push` is polymorphic by a deterministic sniff, never a flag: a take directory (a `doc.json` carrying `source`) pushes recording and document through the take pipeline; a `config.json` (or a directory holding one) pushes the program, and a `doc.json` beside it that carries `program` (a program document: overlays, objects, audio, speed, tween edits, its own length; `program.config` omitted on disk) rides along, lint-gated. `fetch` and `pull` write a program document back the same way. `pull` takes `--since <versionId>` (walk the changelog from that base instead of the tracked one) and `--check` (print what changed and stop: nothing on disk moves) on both paths. Program pushes take `--vos`, `--title`, `--slug`, `--desc`, `--tags`, `--folder`, `--remix-of`, `--base`, `--label`, `--note`, `--override <id>` (repeatable) and `--claimable`; take pushes take `--title`, `--label`, `--note`, `--folder`, `--override` and `--yes`.
|
|
233
|
+
`push` is polymorphic by a deterministic sniff, never a flag: a take directory (a `doc.json` carrying `source`) pushes recording and document through the take pipeline; a `config.json` (or a directory holding one) pushes the program, and a `doc.json` beside it that carries `program` (a program document: overlays, objects, audio, speed, tween edits, its own length; `program.config` omitted on disk) rides along, lint-gated. `fetch` and `pull` write a program document back the same way. `pull` takes `--since <versionId>` (walk the changelog from that base instead of the tracked one) and `--check` (print what changed and stop: nothing on disk moves) on both paths. A program push LANDS where `vos.json` says: a directory that tracks a vos iterates it, so the fetch, edit, check, push, pull loop needs no flags, and making something separate from the same config is the explicit `--remix-of` door. Program pushes take `--vos`, `--title`, `--slug`, `--desc`, `--tags`, `--folder`, `--remix-of`, `--base`, `--label`, `--note`, `--override <id>` (repeatable) and `--claimable`; take pushes take `--title`, `--label`, `--note`, `--folder`, `--override` and `--yes`.
|
|
230
234
|
|
|
231
235
|
Both paths share the same base tracking and the same two 409 shapes. `stale_base` replays the platform's typed changelog: run `vos pull`, re-apply, push again. `protected_conflict` lists nodes a human edited in the studio: keep their values, or re-push with `--override <id>` only when the user asked for that exact change. The first push of a take asks before uploading (`--yes` for headless); agents never upload unprompted. The take's duration rides the upload, and the platform refuses a take over the hosted recording cap. Every push should carry `--label` (what changed, one line) and `--note` (why: the user's ask); the version history reads as a conversation, and an unlabelled push is a turn the human cannot read.
|
|
232
236
|
|
|
@@ -6629,7 +6629,8 @@ function paceLine(r) {
|
|
|
6629
6629
|
}
|
|
6630
6630
|
|
|
6631
6631
|
// src/plugin/recorder.ts
|
|
6632
|
-
var
|
|
6632
|
+
var realSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
6633
|
+
var DRY_PAUSE_MS = 120;
|
|
6633
6634
|
function jpegDims(buf) {
|
|
6634
6635
|
let i = 2;
|
|
6635
6636
|
while (i < buf.length - 9) {
|
|
@@ -6644,7 +6645,9 @@ function jpegDims(buf) {
|
|
|
6644
6645
|
return null;
|
|
6645
6646
|
}
|
|
6646
6647
|
async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
6647
|
-
const
|
|
6648
|
+
const dry = opts.dryRun === true;
|
|
6649
|
+
const sleep = dry ? (ms) => realSleep(Math.min(ms, DRY_PAUSE_MS)) : realSleep;
|
|
6650
|
+
const maxSeconds = dry ? Infinity : opts.maxDurationSeconds ?? Infinity;
|
|
6648
6651
|
const vw = actions.viewport?.width ?? 1280;
|
|
6649
6652
|
const vh = actions.viewport?.height ?? 720;
|
|
6650
6653
|
const context = await browser.newContext({
|
|
@@ -6667,22 +6670,24 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6667
6670
|
const frames = [];
|
|
6668
6671
|
let frameIdx = 0;
|
|
6669
6672
|
let t0 = 0;
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6673
|
+
if (!dry)
|
|
6674
|
+
cdp.on("Page.screencastFrame", (ev) => {
|
|
6675
|
+
const tsMs = ev.metadata.timestamp ? ev.metadata.timestamp * 1e3 : Date.now();
|
|
6676
|
+
const file = `frame-${String(frameIdx++).padStart(5, "0")}.jpg`;
|
|
6677
|
+
writeFileSync2(join13(paths.framesDir, file), Buffer.from(ev.data, "base64"));
|
|
6678
|
+
frames.push({ file, tMs: Math.max(0, Math.round(tsMs - t0)) });
|
|
6679
|
+
cdp.send("Page.screencastFrameAck", { sessionId: ev.sessionId }).catch(() => {
|
|
6680
|
+
});
|
|
6676
6681
|
});
|
|
6677
|
-
});
|
|
6678
6682
|
t0 = Date.now();
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6683
|
+
if (!dry)
|
|
6684
|
+
await cdp.send("Page.startScreencast", {
|
|
6685
|
+
format: "jpeg",
|
|
6686
|
+
quality: 90,
|
|
6687
|
+
maxWidth: vw,
|
|
6688
|
+
maxHeight: vh,
|
|
6689
|
+
everyNthFrame: 1
|
|
6690
|
+
});
|
|
6686
6691
|
const events = [];
|
|
6687
6692
|
const cur = { x: 48, y: 48 };
|
|
6688
6693
|
const now = () => Date.now() - t0;
|
|
@@ -6694,7 +6699,7 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6694
6699
|
const dist = Math.hypot(tx - cur.x, ty - cur.y);
|
|
6695
6700
|
const from = { ...cur };
|
|
6696
6701
|
await clockMotion(
|
|
6697
|
-
pointerTravelMs(dist),
|
|
6702
|
+
dry ? 0 : pointerTravelMs(dist),
|
|
6698
6703
|
async (u) => {
|
|
6699
6704
|
cur.x = from.x + (tx - from.x) * u;
|
|
6700
6705
|
cur.y = from.y + (ty - from.y) * u;
|
|
@@ -6969,12 +6974,14 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6969
6974
|
` WARNING: ${freezePct}% of the take is visually frozen (${freezes.length} freezes, longest ${Math.max(...freezes.map((f) => f.ms))}ms) \u2014 keep motion in frame or trim these spans in doc.json`
|
|
6970
6975
|
);
|
|
6971
6976
|
}
|
|
6972
|
-
await writeJson(paths.cursor, events);
|
|
6973
|
-
await writeJson(paths.meta, meta, true);
|
|
6974
|
-
await writeJson(paths.framesIndex, frames);
|
|
6975
|
-
await writeJson(paths.actions, { ...actions, url }, true);
|
|
6976
6977
|
const pace = paceReport(paces);
|
|
6977
|
-
|
|
6978
|
+
if (!dry) {
|
|
6979
|
+
await writeJson(paths.cursor, events);
|
|
6980
|
+
await writeJson(paths.meta, meta, true);
|
|
6981
|
+
await writeJson(paths.framesIndex, frames);
|
|
6982
|
+
await writeJson(paths.actions, { ...actions, url }, true);
|
|
6983
|
+
log(` ${paceLine(pace)}`);
|
|
6984
|
+
}
|
|
6978
6985
|
return {
|
|
6979
6986
|
events,
|
|
6980
6987
|
frames,
|
|
@@ -7964,6 +7971,21 @@ function takePushRefusal(ask) {
|
|
|
7964
7971
|
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
7972
|
return null;
|
|
7966
7973
|
}
|
|
7974
|
+
function programPushTarget(ask) {
|
|
7975
|
+
if (ask.vos) return { kind: "version", vosId: ask.vos, tracked: false };
|
|
7976
|
+
if (ask.remixOf) return { kind: "create", remixOfId: ask.remixOf };
|
|
7977
|
+
const tracked = ask.trackedVosId;
|
|
7978
|
+
if (!tracked) return { kind: "create" };
|
|
7979
|
+
const extra = ask.createOnly ?? [];
|
|
7980
|
+
if (extra.length) {
|
|
7981
|
+
const list = extra.map((f) => `--${f}`).join(", ");
|
|
7982
|
+
return {
|
|
7983
|
+
kind: "refuse",
|
|
7984
|
+
why: `this directory tracks vos ${tracked} (vos.json), so a bare push adds a VERSION to it, and ${list} ${extra.length > 1 ? "apply" : "applies"} only when creating. To make a separate vos from this config: vos push <config.json> --remix-of ${tracked} ${list}. To change the tracked vos's title or slug, edit it on vos.so`
|
|
7985
|
+
};
|
|
7986
|
+
}
|
|
7987
|
+
return { kind: "version", vosId: tracked, tracked: true };
|
|
7988
|
+
}
|
|
7967
7989
|
async function pushTake(dir, flags, r) {
|
|
7968
7990
|
const take = await loadTake(dir);
|
|
7969
7991
|
if (!take.doc) {
|
|
@@ -8345,12 +8367,12 @@ async function browserLogin(origin, r, opts) {
|
|
|
8345
8367
|
r.log(`Sign in at ${verifyUrl}`);
|
|
8346
8368
|
if (opts.openBrowser) void launchDefaultBrowser(verifyUrl);
|
|
8347
8369
|
r.log("Waiting for approval\u2026 (Ctrl-C to cancel)");
|
|
8348
|
-
const
|
|
8370
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((res) => setTimeout(res, ms)));
|
|
8349
8371
|
let intervalMs = baseIntervalMs;
|
|
8350
8372
|
let failures = 0;
|
|
8351
8373
|
let capNoted = false;
|
|
8352
8374
|
while (Date.now() < expiresAt) {
|
|
8353
|
-
await
|
|
8375
|
+
await sleep(intervalMs);
|
|
8354
8376
|
let res;
|
|
8355
8377
|
try {
|
|
8356
8378
|
res = await apiJson(origin, "/api/cli/login/poll", {
|
|
@@ -8652,7 +8674,7 @@ async function cmdPushProgram(argv) {
|
|
|
8652
8674
|
const config = pre.config;
|
|
8653
8675
|
const state = readSyncState(dir);
|
|
8654
8676
|
const vosFlag = strFlag(flags, "vos");
|
|
8655
|
-
const
|
|
8677
|
+
const explicitVosId = vosFlag ? parseVosId(vosFlag) : null;
|
|
8656
8678
|
const programDoc = await readProgramDoc(dir, config);
|
|
8657
8679
|
if (programDoc) {
|
|
8658
8680
|
const lint = lintDoc(programDoc);
|
|
@@ -8663,7 +8685,7 @@ async function cmdPushProgram(argv) {
|
|
|
8663
8685
|
for (const w of lint.warnings) r.log(`warning doc.json: ${w}`);
|
|
8664
8686
|
}
|
|
8665
8687
|
if (flags.claimable === true) {
|
|
8666
|
-
if (
|
|
8688
|
+
if (explicitVosId) {
|
|
8667
8689
|
throw new UsageError(
|
|
8668
8690
|
"--claimable creates a NEW claimable vos and cannot iterate (--vos). After the human claims it, iterate with their key: vos push --vos <id>"
|
|
8669
8691
|
);
|
|
@@ -8697,6 +8719,15 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
|
|
|
8697
8719
|
const desc = strFlag(flags, "desc");
|
|
8698
8720
|
const tagsFlag = strFlag(flags, "tags");
|
|
8699
8721
|
const folderRef = strFlag(flags, "folder");
|
|
8722
|
+
const remixOfFlag = strFlag(flags, "remix-of");
|
|
8723
|
+
const pushTarget = programPushTarget({
|
|
8724
|
+
vos: explicitVosId,
|
|
8725
|
+
remixOf: remixOfFlag,
|
|
8726
|
+
trackedVosId: state?.vosId ?? null,
|
|
8727
|
+
createOnly: ["title", "slug"].filter((f) => strFlag(flags, f))
|
|
8728
|
+
});
|
|
8729
|
+
if (pushTarget.kind === "refuse") throw new UsageError(pushTarget.why);
|
|
8730
|
+
const vosId = pushTarget.kind === "version" ? pushTarget.vosId : null;
|
|
8700
8731
|
if (vosId && (desc || tagsFlag || folderRef)) {
|
|
8701
8732
|
throw new UsageError(
|
|
8702
8733
|
"--desc/--tags/--folder apply when CREATING a vos. On an existing one: vos folder move <id> --to <folder> for filing; edit title/description/tags on vos.so"
|
|
@@ -8765,7 +8796,7 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
|
|
|
8765
8796
|
);
|
|
8766
8797
|
return EXIT_OK;
|
|
8767
8798
|
}
|
|
8768
|
-
const remixOfId =
|
|
8799
|
+
const remixOfId = pushTarget.kind === "create" ? pushTarget.remixOfId : void 0;
|
|
8769
8800
|
const fallbackTitle = state?.title ? `${state.title} remix` : basename4(source).replace(/\.json$/i, "") || "vos remix";
|
|
8770
8801
|
const title = (strFlag(flags, "title") ?? fallbackTitle).slice(0, 100);
|
|
8771
8802
|
const folderId = folderRef ? resolveFolder(await listFolders(origin, key), folderRef).id : void 0;
|
|
@@ -10108,14 +10139,15 @@ var BOOLEAN_FLAGS5 = /* @__PURE__ */ new Set([
|
|
|
10108
10139
|
"check",
|
|
10109
10140
|
"motion",
|
|
10110
10141
|
"leader",
|
|
10111
|
-
"keep-frames"
|
|
10142
|
+
"keep-frames",
|
|
10143
|
+
"dry-run"
|
|
10112
10144
|
]);
|
|
10113
10145
|
var MULTI_FLAGS2 = /* @__PURE__ */ new Set(["set", "override", "browser-arg"]);
|
|
10114
10146
|
var HELP = `vos \u2014 record a browser flow, plan effects, render a product video; sync with vos.so
|
|
10115
10147
|
|
|
10116
10148
|
Take pipeline
|
|
10117
10149
|
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]
|
|
10150
|
+
vos record --actions actions.json [--url <url>] [--out take] [--strict] [--dry-run] [--keep-frames] [--max-duration <s>] [--storage-state <file>] [--browser-arg=<switch>]... [--background <slug|url|none>] [--json]
|
|
10119
10151
|
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]
|
|
10120
10152
|
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]
|
|
10121
10153
|
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]
|
|
@@ -10445,6 +10477,47 @@ async function cmdRecord(argv) {
|
|
|
10445
10477
|
const maxDurationSeconds = await maxDuration(flags, r);
|
|
10446
10478
|
const storageState = takeStorageState(flags);
|
|
10447
10479
|
const browserArgs = takeBrowserArgs(multi);
|
|
10480
|
+
if (flags["dry-run"] === true) {
|
|
10481
|
+
void flags.strict;
|
|
10482
|
+
void flags["keep-frames"];
|
|
10483
|
+
const browser2 = await launchBrowser(browserArgs);
|
|
10484
|
+
try {
|
|
10485
|
+
r.log("rehearsing (nothing is captured or written)\u2026");
|
|
10486
|
+
r.event({ event: "phase", phase: "rehearse" });
|
|
10487
|
+
const started = Date.now();
|
|
10488
|
+
const rec = await recordTake(
|
|
10489
|
+
browser2,
|
|
10490
|
+
url,
|
|
10491
|
+
actions,
|
|
10492
|
+
takePaths(outDir),
|
|
10493
|
+
r.log,
|
|
10494
|
+
{ storageState, dryRun: true }
|
|
10495
|
+
);
|
|
10496
|
+
const steps = rec.meta.steps ?? [];
|
|
10497
|
+
const lines = steps.map((s) => {
|
|
10498
|
+
const rect = s.rect ? ` rect ${s.rect.x},${s.rect.y} ${s.rect.w}\xD7${s.rect.h}` : "";
|
|
10499
|
+
const what = s.selector ? ` ${s.selector}` : "";
|
|
10500
|
+
return ` ${s.skipped ? "\u2717" : "\u2713"} #${s.step}${s.id ? ` (${s.id})` : ""} ${s.do}${what}${s.skipped ? " NOT FOUND" : rect}${s.navigated ? " \u2192 navigated" : ""}`;
|
|
10501
|
+
});
|
|
10502
|
+
const failed2 = rec.skipped.length > 0 || rec.navTimeout;
|
|
10503
|
+
r.done(
|
|
10504
|
+
{
|
|
10505
|
+
dryRun: true,
|
|
10506
|
+
ok: !failed2,
|
|
10507
|
+
seconds: +((Date.now() - started) / 1e3).toFixed(1),
|
|
10508
|
+
steps,
|
|
10509
|
+
skipped: rec.skipped,
|
|
10510
|
+
navTimeout: rec.navTimeout
|
|
10511
|
+
},
|
|
10512
|
+
`${failed2 ? "REHEARSAL FAILED" : "Rehearsal passed"}: ${steps.length - rec.skipped.length}/${steps.length} steps resolved in ${((Date.now() - started) / 1e3).toFixed(1)}s${rec.navTimeout ? " (the first load never reached networkidle)" : ""}
|
|
10513
|
+
${lines.join("\n")}
|
|
10514
|
+
${failed2 ? "Fix the script, rehearse again, then record." : `Rects are capture px (the step rects a pin or a callout reads). Next: vos record --actions ${actionsPath} --out ${outDir} --strict`}`
|
|
10515
|
+
);
|
|
10516
|
+
return failed2 ? EXIT_USAGE : EXIT_OK;
|
|
10517
|
+
} finally {
|
|
10518
|
+
await browser2.close();
|
|
10519
|
+
}
|
|
10520
|
+
}
|
|
10448
10521
|
if (existsSync15(join18(outDir, "meta.json"))) {
|
|
10449
10522
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
10450
10523
|
r.log(
|
|
@@ -11691,4 +11764,4 @@ export {
|
|
|
11691
11764
|
takeBrowserArgs,
|
|
11692
11765
|
run
|
|
11693
11766
|
};
|
|
11694
|
-
//# sourceMappingURL=chunk-
|
|
11767
|
+
//# sourceMappingURL=chunk-7TDL3N24.js.map
|