versioncam 0.5.3 → 0.7.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/CHANGELOG.md +71 -0
- package/README.md +82 -2
- package/dist/cli/commands/check.js +25 -0
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/doctor.js +54 -1
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/record.d.ts +6 -0
- package/dist/cli/commands/record.js +24 -3
- package/dist/cli/commands/record.js.map +1 -1
- package/dist/cli/commands/stability.js +10 -3
- package/dist/cli/commands/stability.js.map +1 -1
- package/dist/cli/usage.d.ts +1 -1
- package/dist/cli/usage.js +2 -0
- package/dist/cli/usage.js.map +1 -1
- package/dist/config.d.ts +37 -1
- package/dist/config.js +115 -3
- package/dist/config.js.map +1 -1
- package/dist/core/cursor.d.ts +22 -0
- package/dist/core/cursor.js +6 -0
- package/dist/core/cursor.js.map +1 -1
- package/dist/core/motion-defaults.d.ts +8 -0
- package/dist/core/motion-defaults.js +8 -0
- package/dist/core/motion-defaults.js.map +1 -1
- package/dist/core/timeline.d.ts +24 -0
- package/dist/core/timeline.js.map +1 -1
- package/dist/driver/clip.d.ts +6 -0
- package/dist/driver/clip.js +33 -2
- package/dist/driver/clip.js.map +1 -1
- package/dist/driver/file-window.d.ts +66 -0
- package/dist/driver/file-window.js +245 -0
- package/dist/driver/file-window.js.map +1 -0
- package/dist/driver/page-hooks.d.ts +23 -1
- package/dist/driver/page-hooks.js +36 -1
- package/dist/driver/page-hooks.js.map +1 -1
- package/dist/driver/replay.d.ts +263 -0
- package/dist/driver/replay.js +558 -0
- package/dist/driver/replay.js.map +1 -0
- package/dist/driver/session.d.ts +70 -4
- package/dist/driver/session.js +267 -29
- package/dist/driver/session.js.map +1 -1
- package/dist/driver/settle.js +6 -0
- package/dist/driver/settle.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js.map +1 -1
- package/dist/page/assets/index-CnRDAiUz.js +2 -0
- package/dist/page/index.html +1 -1
- package/dist/publish/protocol.d.ts +12 -1
- package/dist/publish/protocol.js.map +1 -1
- package/dsl.md +68 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/skills/versioncam/authoring.md +6 -1
- package/plugin/skills/versioncam/onboarding.md +9 -3
- package/dist/page/assets/index-BtUbUEdN.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,77 @@ and versions follow [semantic versioning](https://semver.org/). While the
|
|
|
6
6
|
major version is 0, a minor version may change the config or the clip DSL,
|
|
7
7
|
and its entry here says how.
|
|
8
8
|
|
|
9
|
+
## 0.7.0 (2026-11-02)
|
|
10
|
+
|
|
11
|
+
A minor version: the config gains `replay`, the clip DSL gains
|
|
12
|
+
`s.fileWindow`, a second form of `s.drag` and `modifiers` on `s.click`,
|
|
13
|
+
and `record` and `stability` take `--replay`. A config without `replay`
|
|
14
|
+
records as it did with 0.6.0, and the render config is unchanged, so a
|
|
15
|
+
publish needs nothing new of version.cam.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- `replay` in the config records a clip's responses the first time it runs
|
|
20
|
+
and serves them on every run after, so a map's tiles, a model's answer or
|
|
21
|
+
a job's status are the same in every recording. `record` names the
|
|
22
|
+
requests by URL; each clip's replay is a HAR in `replay/` beside the
|
|
23
|
+
config, with binary bodies under `replay/bodies/` by hash, and is meant to
|
|
24
|
+
be committed. A request the replay covers and does not hold fails the clip
|
|
25
|
+
and names it. `pace` delivers a streamed response, an event stream over
|
|
26
|
+
`fetch` or `EventSource`, or lines of JSON, a piece at a time on the
|
|
27
|
+
page's clock. Authorization, cookie and API key headers are never written,
|
|
28
|
+
and `redact` leaves query parameters out.
|
|
29
|
+
- `s.fileWindow(files, options?)` draws a file window over the page, as
|
|
30
|
+
Finder draws one, to select files in and drag onto the app's dropzone,
|
|
31
|
+
which receives them as `File` objects through the events a real drag
|
|
32
|
+
sends: the upload a clip can film. A click selects a row, with Shift it
|
|
33
|
+
extends, with Meta it adds or removes.
|
|
34
|
+
- `s.drag(from, to, options?)`, from one locator or point to another, beside
|
|
35
|
+
the form in fractions of one box; and `modifiers` on `s.click`, for a
|
|
36
|
+
shift-click. Both strokes take Meta and Control as well as Shift and Alt.
|
|
37
|
+
- `versioncam record` and `stability` take `--replay auto|record|replay|live`.
|
|
38
|
+
`doctor` lists each clip's replay, and `check` points out one older than
|
|
39
|
+
ninety days.
|
|
40
|
+
|
|
41
|
+
## 0.6.0 (2026-11-02)
|
|
42
|
+
|
|
43
|
+
A minor version: the clip DSL gains `s.scroll`, the config gains
|
|
44
|
+
`theme.cursor` and `motion.scrollDurationMs`, and a page's own smooth
|
|
45
|
+
scrolling is instant while it is recorded. A config that sets none of
|
|
46
|
+
the new fields renders and publishes as it did with 0.5.3, and records
|
|
47
|
+
the same unless its app scrolls smoothly from a script.
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- `s.scroll(target, where, options?)` scrolls a pane, or the page, on the
|
|
52
|
+
clip's clock: `{ by: px }`, `{ to: "top" | "bottom" | px }`, or
|
|
53
|
+
`{ into: locator, block? }`, one eased step a frame, so the frames inside
|
|
54
|
+
a scroll are the same on every run. The end is clamped to what the element
|
|
55
|
+
can scroll, and an element that cannot scroll at all is refused by name.
|
|
56
|
+
`motion.scrollDurationMs` (700) is its default length. A recording keeps
|
|
57
|
+
its scrolls in the timeline's new `scrolls`, which a recording without one
|
|
58
|
+
does not have.
|
|
59
|
+
- `theme.cursor: { style, ripple }` in the config. `style: "dark"` draws
|
|
60
|
+
the system's black arrow with a white hairline, as a screen recording of
|
|
61
|
+
a Mac shows it, and `ripple: false` draws no ring on a click. The default,
|
|
62
|
+
`{ style: "light", ripple: true }`, is the white arrow and the ring every
|
|
63
|
+
earlier version drew, to the byte.
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- A page's script can no longer scroll smoothly while it is recorded:
|
|
68
|
+
`scrollIntoView`, `scrollTo`, `scrollBy` and `scroll` given
|
|
69
|
+
`{ behavior: "smooth" }` scroll at once, as a stylesheet's
|
|
70
|
+
`scroll-behavior: smooth` already did. The browser animated those scrolls
|
|
71
|
+
on its own clock, so the frames during one differed between runs. A clip
|
|
72
|
+
whose app scrolled itself that way now shows the scroll land in a frame;
|
|
73
|
+
`s.scroll` is how a clip shows one move.
|
|
74
|
+
- `versioncam publish` sends `theme.cursor` and `motion.scrollDurationMs`
|
|
75
|
+
only when the config changes them, so a config that changes neither
|
|
76
|
+
publishes exactly what 0.5.3 did. One that changes either needs
|
|
77
|
+
version.cam from cloud-v1.8.0, which knows both; before it, the publish
|
|
78
|
+
is refused with a message that names the key.
|
|
79
|
+
|
|
9
80
|
## 0.5.3 (2026-11-01)
|
|
10
81
|
|
|
11
82
|
A patch version: the config and the clip DSL are as they were. The browser
|
package/README.md
CHANGED
|
@@ -152,8 +152,9 @@ cannot say, that is a missing field. It is not a reason to fork.
|
|
|
152
152
|
| `review` | `minSeconds: 9`, `maxSeconds: 11` | How long a clip should run. `versioncam review` holds clips to it. |
|
|
153
153
|
| `cursorStart` | `{ x: 0.72, y: 0.28 }` | Where the cursor rests in the first frame, as fractions of the viewport. |
|
|
154
154
|
| `clock` | `time: "2026-09-01T09:00:00Z"`, `timezoneId: "UTC"`, `locale: "en-GB"` | The instant, time zone and locale the app sees. |
|
|
155
|
-
| `theme` | `accent: "#2f6fed"`, `cursorScale: 2.1` | The colour of ripples and rings, the cursor's size, and an optional `presentation`. See *Presentation*. |
|
|
156
|
-
| `motion` | `MOTION` | Dwells, strokes and camera timing. `MOTION` is exported, with the reason for each number beside it. |
|
|
155
|
+
| `theme` | `accent: "#2f6fed"`, `cursorScale: 2.1`, `cursor: { style: "light", ripple: true }` | The colour of ripples and rings, the cursor's size and look, and an optional `presentation`. `cursor: { style: "dark", ripple: false }` draws the system's black arrow and no ring on a click, as a screen recording shows them. See *Presentation*. |
|
|
156
|
+
| `motion` | `MOTION` | Dwells, strokes, scrolls and camera timing. `MOTION` is exported, with the reason for each number beside it. |
|
|
157
|
+
| `replay` | none | Responses recorded once and served on every run after: tiles, a model's answers, a job's status. See *Live data*. |
|
|
157
158
|
| `outDir` | `".versioncam"` | Where everything the recorder writes goes. Gitignore it. |
|
|
158
159
|
| `scenes` | none | A module whose default export is `Record<string, Scene>`, keyed by scene id: `export default { [intro.id]: intro }`. A named export is not read, and a module with no default export is refused. |
|
|
159
160
|
| `sequence` | none | Clip ids, in order, for `versioncam render --sequence`. |
|
|
@@ -255,6 +256,31 @@ for a straight one, `s.lasso` for a closed loop) are written in fractions of a
|
|
|
255
256
|
target's box for the same reason. A clip built from locators survives a layout
|
|
256
257
|
change.
|
|
257
258
|
|
|
259
|
+
A long answer or a list is read by scrolling it, and `s.scroll` does that on
|
|
260
|
+
the clip's clock, one step a frame, so every frame of it is the same on every
|
|
261
|
+
run. Give it the pane that scrolls, or `"page"`, and where to go: `{ by: 400 }`,
|
|
262
|
+
`{ to: "bottom" }`, or `{ into: s.byTestId("citation-3") }` to bring one thing
|
|
263
|
+
into view.
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
await s.moveTo(s.byTestId("answer"));
|
|
267
|
+
await s.scroll(s.byTestId("answer"), { into: s.byTestId("citation-3") });
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
The cursor stays put while the pane moves, so a clip moves it over the pane
|
|
271
|
+
first, where a hand on a mouse wheel would be.
|
|
272
|
+
|
|
273
|
+
An upload is filmed from `s.fileWindow`, a file window drawn over the page,
|
|
274
|
+
since the browser's own file dialog is outside it. The clip selects files in
|
|
275
|
+
it and drags them onto the app's dropzone, which receives them as real files
|
|
276
|
+
through the events a real drag sends. The window is staged; say so where the
|
|
277
|
+
clip is shown.
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
const files = await s.fileWindow([{ name: "cases.csv", content: csv }], { title: "Case data" });
|
|
281
|
+
await s.drag(files.item(0), s.byTestId("dropzone"));
|
|
282
|
+
```
|
|
283
|
+
|
|
258
284
|
`npx versioncam dsl` prints every method with its options and defaults, and a
|
|
259
285
|
test holds that list to the session's code. The same reference is
|
|
260
286
|
*The clip DSL*.
|
|
@@ -360,11 +386,13 @@ the wreckage would help nobody.
|
|
|
360
386
|
```
|
|
361
387
|
versioncam record [id…] drive the app and capture frames
|
|
362
388
|
--draft 15fps at CSS scale: cheap, for writing clips
|
|
389
|
+
--replay <mode> auto, record, replay or live, over the config's replay
|
|
363
390
|
versioncam check [id…] record every clip; exit 1 if any breaks
|
|
364
391
|
--report <file> and write each clip's result there, as JSON
|
|
365
392
|
versioncam stability [id…] record each clip twice the same way; stable or not
|
|
366
393
|
--runs <n> how many times (default 2)
|
|
367
394
|
--full at full quality rather than draft
|
|
395
|
+
--replay <mode> as for record
|
|
368
396
|
```
|
|
369
397
|
|
|
370
398
|
### Review
|
|
@@ -859,12 +887,64 @@ its CSS motion:
|
|
|
859
887
|
up it finishes, as in a browser nobody is filming: its `finished` promise
|
|
860
888
|
resolves and `animationend` fires, so a dialog that unmounts after its exit
|
|
861
889
|
animation goes, and does not sit invisibly over the page.
|
|
890
|
+
- **Smooth scrolling is instant.** A stylesheet's `scroll-behavior: smooth`
|
|
891
|
+
and a script's `{ behavior: "smooth" }` both scroll at once, since the
|
|
892
|
+
browser would animate either on a clock the recorder does not move. A chat
|
|
893
|
+
that keeps its newest message in view jumps to it. To film a scroll, use
|
|
894
|
+
`s.scroll`.
|
|
862
895
|
- **Not on the clock:** animations a script makes with `element.animate()`,
|
|
863
896
|
Chromium's smooth scrolling of a mouse wheel, and transitions inside an
|
|
864
897
|
iframe, which stay zeroed since nothing drives an iframe's clock. A clip
|
|
865
898
|
that films one of the first two may differ between runs: `stability` says
|
|
866
899
|
so.
|
|
867
900
|
|
|
901
|
+
### Live data
|
|
902
|
+
|
|
903
|
+
A map's tiles, a model's answer, a job's status, a count that grows: each
|
|
904
|
+
comes from a server, at the server's speed and in its words of the day, so
|
|
905
|
+
two recordings of a clip that shows one differ wherever it reaches the page.
|
|
906
|
+
`replay` records those responses the first time a clip runs and serves them,
|
|
907
|
+
from a file, on every run after:
|
|
908
|
+
|
|
909
|
+
```ts
|
|
910
|
+
replay: {
|
|
911
|
+
record: ["https://tiles.example.com/**", "**/api/chat/**"],
|
|
912
|
+
// A stream the page reads over time, one event every 300 ms of the clip.
|
|
913
|
+
pace: [{ url: "**/api/chat/*/stream", chunkMs: 300 }],
|
|
914
|
+
},
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
| Field | Default | What it sets |
|
|
918
|
+
|---|---|---|
|
|
919
|
+
| `record` | required | The requests to record and replay: URL globs, as Playwright writes them, or regular expressions. |
|
|
920
|
+
| `mode` | `"auto"` | `"auto"` replays a clip whose file exists and records one whose file does not. `"record"` always records, `"replay"` fails without a file, `"live"` ignores it. `--replay <mode>` on `record` and `stability` overrides it for one run. |
|
|
921
|
+
| `dir` | `"replay"` | Where the files go, beside the config. Commit them: a replay is an input, like a fixture, and CI needs it to record the clip the same way. |
|
|
922
|
+
| `pace` | none | Responses the page reads a piece at a time, an event stream or lines of JSON: delivered one event or one line every `chunkMs` of the page's clock, so each piece lands on the same frame every run, at any frame rate. |
|
|
923
|
+
| `redact` | none | Query parameters left out of the file, and of the match, by name. |
|
|
924
|
+
| `onMissing` | `"fail"` | A request the replay covers and does not hold fails the clip, naming it, since a request that quietly went live is what this exists to stop. `"live"` sends it to the network with a warning instead. |
|
|
925
|
+
|
|
926
|
+
Each clip has its own file, `replay/<clip>.har`, a HAR that a browser's
|
|
927
|
+
developer tools can open. Text bodies are in it, where a diff can read them;
|
|
928
|
+
binary ones, tiles and images, sit beside it under `replay/bodies/`, named by
|
|
929
|
+
their hash, so clips that load the same tiles keep them once. The
|
|
930
|
+
`Authorization`, `Cookie`, `Set-Cookie` and `X-API-Key` headers are never
|
|
931
|
+
written. The bodies are written as the server sent them, so read a replay
|
|
932
|
+
before committing it if the server's answers can hold anything private.
|
|
933
|
+
|
|
934
|
+
A request the same URL answers several times, a job's status polled until it
|
|
935
|
+
is done, is answered in the order it was recorded, and the last answer again
|
|
936
|
+
after that. A request the clip answers itself, with `page.route` from
|
|
937
|
+
`offCamera`, never reaches the replay, so a hand-written mock still wins
|
|
938
|
+
where a clip has one. The frame loop waits for the replay's answers, so a
|
|
939
|
+
response arrives in the frame it was asked for on every run.
|
|
940
|
+
|
|
941
|
+
`doctor` lists each clip's replay, how many requests it holds and when it
|
|
942
|
+
was recorded, and `check` points out one older than ninety days. That is by
|
|
943
|
+
age alone: a clip that passes against its replay proves the app still asks
|
|
944
|
+
for what it asked for, not that the server still answers the same way. When
|
|
945
|
+
the app changes what it fetches, the clip fails and names the request; record
|
|
946
|
+
it again with `versioncam record <id> --replay record`.
|
|
947
|
+
|
|
868
948
|
### When a clip will not reproduce
|
|
869
949
|
|
|
870
950
|
The recorder can only be as deterministic as the app it films. An app that is
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
|
+
import { STALE_DAYS, describeReplay } from "../../driver/replay.js";
|
|
4
5
|
import { launchBrowser } from "../../driver/launch.js";
|
|
5
6
|
import { RECORDER_VERSION, runClip } from "../../driver/clip.js";
|
|
6
7
|
import { loadClips, loadConfig } from "../../loader.js";
|
|
@@ -51,6 +52,8 @@ async function checkAll(config, clips, reportPath) {
|
|
|
51
52
|
await browser.close();
|
|
52
53
|
}
|
|
53
54
|
process.stdout.write(`\n${clips.length - failures.length}/${clips.length} clips still match the app.\n`);
|
|
55
|
+
for (const line of staleReplays(config, clips))
|
|
56
|
+
process.stdout.write(line);
|
|
54
57
|
for (const { id, error } of failures) {
|
|
55
58
|
process.stderr.write(`\n${id}\n ${error.message}\n`);
|
|
56
59
|
}
|
|
@@ -59,6 +62,28 @@ async function checkAll(config, clips, reportPath) {
|
|
|
59
62
|
}
|
|
60
63
|
return failures.length > 0 ? 1 : 0;
|
|
61
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* A line for each clip whose replay is older than `STALE_DAYS`.
|
|
67
|
+
*
|
|
68
|
+
* By age alone, and said to be: a clip that passes against its replay
|
|
69
|
+
* proves the app still makes the requests it made, not that the server
|
|
70
|
+
* still answers them that way, and nothing here can know whether it does.
|
|
71
|
+
* Three months is long enough that it is worth a person asking.
|
|
72
|
+
*/
|
|
73
|
+
function staleReplays(config, clips) {
|
|
74
|
+
if (!config.replay)
|
|
75
|
+
return [];
|
|
76
|
+
const lines = [];
|
|
77
|
+
for (const clip of clips) {
|
|
78
|
+
const found = describeReplay(config.replay.dir, clip.id);
|
|
79
|
+
if (!found.exists || !(found.ageDays > STALE_DAYS))
|
|
80
|
+
continue;
|
|
81
|
+
lines.push(`warn ${clip.id}: its replay was recorded ${found.ageDays} days ago. ` +
|
|
82
|
+
"The clip shows the server's answers of that day; if they have changed, " +
|
|
83
|
+
`record it again with \`versioncam record ${clip.id} --replay record\`.\n`);
|
|
84
|
+
}
|
|
85
|
+
return lines;
|
|
86
|
+
}
|
|
62
87
|
/**
|
|
63
88
|
* What `--report` writes: each clip's result, the step a failure stopped at,
|
|
64
89
|
* and where its picture is, for a machine to read. The Action builds its pull
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/cli/commands/check.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/cli/commands/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { browserPaths } from "./install.js";
|
|
2
2
|
import { ffmpegStatus } from "../../render/ffmpeg.js";
|
|
3
3
|
import { ensureApp, probe } from "../../app-server.js";
|
|
4
|
-
import { NO_CONFIG, findConfigFile, loadConfig, loadSceneIds, } from "../../loader.js";
|
|
4
|
+
import { NO_CONFIG, findConfigFile, loadClips, loadConfig, loadSceneIds, } from "../../loader.js";
|
|
5
|
+
import { STALE_DAYS, describeReplay } from "../../driver/replay.js";
|
|
5
6
|
const NOT_SERVING = "Start the app, point `baseUrl` at where it is served, or add `webServer` to the config.";
|
|
6
7
|
/**
|
|
7
8
|
* Is the app the config points at serving — and if not, can it be started?
|
|
@@ -95,6 +96,55 @@ async function scenesCheck(cwd) {
|
|
|
95
96
|
return { name: "scenes", ok: false, detail: first, fix: rest.join("\n") };
|
|
96
97
|
}
|
|
97
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Each clip's replay: whether it has one, how many requests it holds, and
|
|
101
|
+
* how old it is. Never a failure: a clip without one records it on its first
|
|
102
|
+
* run. Null for a config without `replay`.
|
|
103
|
+
*
|
|
104
|
+
* The age is the only staleness anyone can tell from here. Whether the
|
|
105
|
+
* server's answers have changed since is not knowable without asking it, so
|
|
106
|
+
* an old replay is pointed out, not judged.
|
|
107
|
+
*/
|
|
108
|
+
async function replayCheck(cwd) {
|
|
109
|
+
let config;
|
|
110
|
+
try {
|
|
111
|
+
config = await loadConfig(cwd);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
const replay = config.replay;
|
|
117
|
+
if (!replay)
|
|
118
|
+
return null;
|
|
119
|
+
let ids;
|
|
120
|
+
try {
|
|
121
|
+
ids = (await loadClips(config, [])).map((clip) => clip.id);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
return {
|
|
125
|
+
name: "replay",
|
|
126
|
+
ok: false,
|
|
127
|
+
detail: `the clips did not load: ${error.message.split("\n")[0]}`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const lines = ids.map((id) => {
|
|
131
|
+
const found = describeReplay(replay.dir, id);
|
|
132
|
+
if (!found.exists)
|
|
133
|
+
return `${id}: none yet; its first recording makes one`;
|
|
134
|
+
const age = Number.isNaN(found.ageDays)
|
|
135
|
+
? "of unknown age"
|
|
136
|
+
: `${found.ageDays} days old`;
|
|
137
|
+
const stale = found.ageDays > STALE_DAYS
|
|
138
|
+
? ", older than any answer should be trusted to stay"
|
|
139
|
+
: "";
|
|
140
|
+
return `${id}: ${found.requests} requests, recorded ${found.recorded.slice(0, 10)}, ${age}${stale}`;
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
name: "replay",
|
|
144
|
+
ok: true,
|
|
145
|
+
detail: [`${replay.dir} (mode ${replay.mode})`, ...lines].join("\n "),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
98
148
|
/**
|
|
99
149
|
* Can this machine record and encode?
|
|
100
150
|
*
|
|
@@ -154,6 +204,9 @@ export async function doctor() {
|
|
|
154
204
|
const scenes = await scenesCheck(process.cwd());
|
|
155
205
|
if (scenes)
|
|
156
206
|
checks.push(scenes);
|
|
207
|
+
const replay = await replayCheck(process.cwd());
|
|
208
|
+
if (replay)
|
|
209
|
+
checks.push(replay);
|
|
157
210
|
checks.push(await appCheck(process.cwd()));
|
|
158
211
|
}
|
|
159
212
|
for (const check of checks) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,EACL,SAAS,EACT,cAAc,EACd,UAAU,EACV,YAAY,GACb,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ReplayMode } from "../../driver/replay.js";
|
|
1
2
|
/**
|
|
2
3
|
* Record every clip the config points at, serially.
|
|
3
4
|
*
|
|
@@ -8,3 +9,8 @@
|
|
|
8
9
|
* error alone.
|
|
9
10
|
*/
|
|
10
11
|
export declare function record(argv: string[]): Promise<number>;
|
|
12
|
+
/**
|
|
13
|
+
* `--replay <mode>`, checked: a mode misspelt would otherwise be read as the
|
|
14
|
+
* config's, and record over a replay someone meant to keep.
|
|
15
|
+
*/
|
|
16
|
+
export declare function replayModeOf(value: string | boolean | undefined): ReplayMode | undefined | Error;
|
|
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import { launchBrowser } from "../../driver/launch.js";
|
|
5
5
|
import { runClip } from "../../driver/clip.js";
|
|
6
|
+
import { isReplayMode } from "../../config.js";
|
|
6
7
|
import { formatTiming } from "../../core/timing.js";
|
|
7
8
|
import { loadClips, loadConfig } from "../../loader.js";
|
|
8
9
|
import { drivingApp } from "../app.js";
|
|
@@ -19,11 +20,19 @@ import { ensureFailure } from "../../driver/reports.js";
|
|
|
19
20
|
export async function record(argv) {
|
|
20
21
|
const { values, positionals } = parseArgs({
|
|
21
22
|
args: argv,
|
|
22
|
-
options: {
|
|
23
|
+
options: {
|
|
24
|
+
draft: { type: "boolean", default: false },
|
|
25
|
+
replay: { type: "string" },
|
|
26
|
+
},
|
|
23
27
|
strict: false,
|
|
24
28
|
allowPositionals: true,
|
|
25
29
|
});
|
|
26
30
|
const draft = values.draft === true;
|
|
31
|
+
const replay = replayModeOf(values.replay);
|
|
32
|
+
if (replay instanceof Error) {
|
|
33
|
+
process.stderr.write(`${replay.message}\n`);
|
|
34
|
+
return 1;
|
|
35
|
+
}
|
|
27
36
|
const config = await loadConfig(process.cwd());
|
|
28
37
|
const clips = await loadClips(config, positionals);
|
|
29
38
|
if (clips.length === 0) {
|
|
@@ -32,9 +41,20 @@ export async function record(argv) {
|
|
|
32
41
|
: `No clips found. The config looks for ${config.clips.join(", ")} under ${config.root}.\n`);
|
|
33
42
|
return 1;
|
|
34
43
|
}
|
|
35
|
-
return drivingApp(config, () => recordAll(config, clips, draft));
|
|
44
|
+
return drivingApp(config, () => recordAll(config, clips, draft, replay));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* `--replay <mode>`, checked: a mode misspelt would otherwise be read as the
|
|
48
|
+
* config's, and record over a replay someone meant to keep.
|
|
49
|
+
*/
|
|
50
|
+
export function replayModeOf(value) {
|
|
51
|
+
if (value === undefined)
|
|
52
|
+
return undefined;
|
|
53
|
+
if (isReplayMode(value))
|
|
54
|
+
return value;
|
|
55
|
+
return new Error(`--replay is auto, record, replay or live, got ${JSON.stringify(value)}.`);
|
|
36
56
|
}
|
|
37
|
-
async function recordAll(config, clips, draft) {
|
|
57
|
+
async function recordAll(config, clips, draft, replay) {
|
|
38
58
|
const browser = await launchBrowser();
|
|
39
59
|
const failures = [];
|
|
40
60
|
try {
|
|
@@ -43,6 +63,7 @@ async function recordAll(config, clips, draft) {
|
|
|
43
63
|
try {
|
|
44
64
|
const { timeline, cost } = await runClip(browser, config, clip, {
|
|
45
65
|
draft,
|
|
66
|
+
replay,
|
|
46
67
|
});
|
|
47
68
|
// Both seconds are named. The line used to print "(7.5s) … — 2.6s" and
|
|
48
69
|
// leave the reader to work out that the first is the clip and the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../../src/cli/commands/record.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAG/C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAc;IACzC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;QACxC,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../../src/cli/commands/record.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { copyFileSync, mkdirSync } from "node:fs";
|
|
2
2
|
import { extname, join, relative } from "node:path";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
|
+
import { replayModeOf } from "./record.js";
|
|
4
5
|
import { runClip } from "../../driver/clip.js";
|
|
5
6
|
import { compareRecordings, stateFileAt } from "../../driver/compare.js";
|
|
6
7
|
import { launchBrowser } from "../../driver/launch.js";
|
|
@@ -27,6 +28,7 @@ export async function stability(argv) {
|
|
|
27
28
|
// Accepted for saying so explicitly; drafting is the default.
|
|
28
29
|
draft: { type: "boolean" },
|
|
29
30
|
full: { type: "boolean" },
|
|
31
|
+
replay: { type: "string" },
|
|
30
32
|
},
|
|
31
33
|
strict: false,
|
|
32
34
|
allowPositionals: true,
|
|
@@ -37,6 +39,11 @@ export async function stability(argv) {
|
|
|
37
39
|
return 1;
|
|
38
40
|
}
|
|
39
41
|
const draft = values.full !== true;
|
|
42
|
+
const replay = replayModeOf(values.replay);
|
|
43
|
+
if (replay instanceof Error) {
|
|
44
|
+
process.stderr.write(`${replay.message}\n`);
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
40
47
|
const config = await loadConfig(process.cwd());
|
|
41
48
|
const clips = await loadClips(config, positionals);
|
|
42
49
|
if (clips.length === 0) {
|
|
@@ -45,9 +52,9 @@ export async function stability(argv) {
|
|
|
45
52
|
: `No clips found. The config looks for ${config.clips.join(", ")} under ${config.root}.\n`);
|
|
46
53
|
return 1;
|
|
47
54
|
}
|
|
48
|
-
return drivingApp(config, () => compareRuns(config, clips, runs, draft));
|
|
55
|
+
return drivingApp(config, () => compareRuns(config, clips, runs, draft, replay));
|
|
49
56
|
}
|
|
50
|
-
async function compareRuns(config, clips, runs, draft) {
|
|
57
|
+
async function compareRuns(config, clips, runs, draft, replay) {
|
|
51
58
|
const browser = await launchBrowser();
|
|
52
59
|
let stable = true;
|
|
53
60
|
try {
|
|
@@ -59,7 +66,7 @@ async function compareRuns(config, clips, runs, draft) {
|
|
|
59
66
|
// read, and a stability check must not replace it.
|
|
60
67
|
const into = join(config.recordingsDir, "__stability", `run-${run}`);
|
|
61
68
|
try {
|
|
62
|
-
const { dir, timeline } = await runClip(browser, { ...config, recordingsDir: into }, clip, { draft });
|
|
69
|
+
const { dir, timeline } = await runClip(browser, { ...config, recordingsDir: into }, clip, { draft, replay });
|
|
63
70
|
recordings.push({ dir, timeline });
|
|
64
71
|
}
|
|
65
72
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stability.js","sourceRoot":"","sources":["../../../src/cli/commands/stability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"stability.js","sourceRoot":"","sources":["../../../src/cli/commands/stability.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/usage.d.ts
CHANGED
|
@@ -13,4 +13,4 @@
|
|
|
13
13
|
* is what comes after publishing: `versioncam cloud`, whose own usage is that
|
|
14
14
|
* group. The last group is not a stage but a way to run any of them.
|
|
15
15
|
*/
|
|
16
|
-
export declare const USAGE = "versioncam: record and render demo clips of a real app\n\nSet up\n versioncam install the headless Chromium this version of versioncam records with\n --with-deps and its system libraries (Linux CI)\n --full and the full browser, which login and preview open\n versioncam init install the authoring skill into .claude/\n --into <dir> into <dir>/versioncam/ instead, for another host\n --force overwrite files that are already there\n versioncam doctor check this machine can record, and that the app starts\n versioncam login sign in once and save the browser session\n\nWrite\n versioncam inspect <path> what a clip can click, and where each name came from\n --json machine-readable\n versioncam measure <path> <sel> where a selector's matches are, and what they say\n versioncam dsl the clip DSL: every method, its options and defaults\n\nRecord\n versioncam record [id\u2026] drive the app and capture frames\n --draft 15fps at CSS scale: cheap, for writing clips\n versioncam check [id\u2026] record every clip; exit 1 if any breaks\n --report <file> and write each clip's result there, as JSON\n versioncam stability [id\u2026] record each clip twice the same way; stable or not\n --runs <n> how many times (default 2)\n --full at full quality rather than draft\n\nReview\n versioncam sheet <id> an eight-frame contact sheet\n --at-marks sample the beats instead of even spacing\n versioncam frame <id> <n> one frame at output size, to a png\n --t <seconds> pick the frame by time instead\n --at <label> or by the beat it shows\n versioncam review <id> the checks that need no eyes; PASS/FAIL, exit 0/1\n --beats <file> [{ label, expect }] the clip promised\n --min <s> --max <s> duration bounds (default 9 and 11)\n versioncam preview <id> open the scrubber in a real browser\n --scene <id> preview a scene instead\n --frame <n> start at this frame\n\nOutput\n versioncam render [id\u2026] composite and encode recordings and scenes\n no ids: every one, stale recordings included\n --scene <id> and this scene, whatever the ids say (repeatable)\n --sequence stitch the results into one piece\n --report <file> and write what it made there, as JSON\n\nPublish\n versioncam publish [id\u2026] send the recordings and their checks to version.cam\n --dry-run print what would be sent, and send nothing\n --report <file> and write the living URLs there, as JSON\n\nManage\n versioncam cloud login sign in to version.cam, with a code you approve in a browser\n --label <name> what the approval page calls this machine\n versioncam cloud logout sign out: revoke the token, and delete it\n versioncam cloud status [project] each clip, current or broken, and what plays meanwhile\n versioncam cloud failure <clip> why a clip broke, and its picture, into .versioncam/failures/\n versioncam cloud ack <clip> \"I know, keep the old one\": the reminders stop\n versioncam cloud embed <clip> the snippets that embed a clip, and its files\n versioncam cloud set <name> <val> change a setting, once you approve it in a browser:\n visibility public|unlisted|private, retention <days>|off,\n viewers on|off, alerts email|slack <address>|off,\n alerts issues on|off\n versioncam cloud plan [account] the plan, and the pages to upgrade and pay on\n --project <owner/repo> which project, when not this repository's\n --json machine-readable, for status, failure, embed and plan\n\nFrom another directory\n versioncam <command> --cwd <dir> as if run in <dir>, which holds versioncam.config.ts\n versioncam --version which versioncam this is\n\nRun from a directory containing versioncam.config.ts, or name it with --cwd.\n";
|
|
16
|
+
export declare const USAGE = "versioncam: record and render demo clips of a real app\n\nSet up\n versioncam install the headless Chromium this version of versioncam records with\n --with-deps and its system libraries (Linux CI)\n --full and the full browser, which login and preview open\n versioncam init install the authoring skill into .claude/\n --into <dir> into <dir>/versioncam/ instead, for another host\n --force overwrite files that are already there\n versioncam doctor check this machine can record, and that the app starts\n versioncam login sign in once and save the browser session\n\nWrite\n versioncam inspect <path> what a clip can click, and where each name came from\n --json machine-readable\n versioncam measure <path> <sel> where a selector's matches are, and what they say\n versioncam dsl the clip DSL: every method, its options and defaults\n\nRecord\n versioncam record [id\u2026] drive the app and capture frames\n --draft 15fps at CSS scale: cheap, for writing clips\n --replay <mode> auto, record, replay or live, over the config's replay\n versioncam check [id\u2026] record every clip; exit 1 if any breaks\n --report <file> and write each clip's result there, as JSON\n versioncam stability [id\u2026] record each clip twice the same way; stable or not\n --runs <n> how many times (default 2)\n --full at full quality rather than draft\n --replay <mode> as for record\n\nReview\n versioncam sheet <id> an eight-frame contact sheet\n --at-marks sample the beats instead of even spacing\n versioncam frame <id> <n> one frame at output size, to a png\n --t <seconds> pick the frame by time instead\n --at <label> or by the beat it shows\n versioncam review <id> the checks that need no eyes; PASS/FAIL, exit 0/1\n --beats <file> [{ label, expect }] the clip promised\n --min <s> --max <s> duration bounds (default 9 and 11)\n versioncam preview <id> open the scrubber in a real browser\n --scene <id> preview a scene instead\n --frame <n> start at this frame\n\nOutput\n versioncam render [id\u2026] composite and encode recordings and scenes\n no ids: every one, stale recordings included\n --scene <id> and this scene, whatever the ids say (repeatable)\n --sequence stitch the results into one piece\n --report <file> and write what it made there, as JSON\n\nPublish\n versioncam publish [id\u2026] send the recordings and their checks to version.cam\n --dry-run print what would be sent, and send nothing\n --report <file> and write the living URLs there, as JSON\n\nManage\n versioncam cloud login sign in to version.cam, with a code you approve in a browser\n --label <name> what the approval page calls this machine\n versioncam cloud logout sign out: revoke the token, and delete it\n versioncam cloud status [project] each clip, current or broken, and what plays meanwhile\n versioncam cloud failure <clip> why a clip broke, and its picture, into .versioncam/failures/\n versioncam cloud ack <clip> \"I know, keep the old one\": the reminders stop\n versioncam cloud embed <clip> the snippets that embed a clip, and its files\n versioncam cloud set <name> <val> change a setting, once you approve it in a browser:\n visibility public|unlisted|private, retention <days>|off,\n viewers on|off, alerts email|slack <address>|off,\n alerts issues on|off\n versioncam cloud plan [account] the plan, and the pages to upgrade and pay on\n --project <owner/repo> which project, when not this repository's\n --json machine-readable, for status, failure, embed and plan\n\nFrom another directory\n versioncam <command> --cwd <dir> as if run in <dir>, which holds versioncam.config.ts\n versioncam --version which versioncam this is\n\nRun from a directory containing versioncam.config.ts, or name it with --cwd.\n";
|
package/dist/cli/usage.js
CHANGED
|
@@ -34,11 +34,13 @@ Write
|
|
|
34
34
|
Record
|
|
35
35
|
versioncam record [id…] drive the app and capture frames
|
|
36
36
|
--draft 15fps at CSS scale: cheap, for writing clips
|
|
37
|
+
--replay <mode> auto, record, replay or live, over the config's replay
|
|
37
38
|
versioncam check [id…] record every clip; exit 1 if any breaks
|
|
38
39
|
--report <file> and write each clip's result there, as JSON
|
|
39
40
|
versioncam stability [id…] record each clip twice the same way; stable or not
|
|
40
41
|
--runs <n> how many times (default 2)
|
|
41
42
|
--full at full quality rather than draft
|
|
43
|
+
--replay <mode> as for record
|
|
42
44
|
|
|
43
45
|
Review
|
|
44
46
|
versioncam sheet <id> an eight-frame contact sheet
|
package/dist/cli/usage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/cli/usage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG
|
|
1
|
+
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/cli/usage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2EpB,CAAC"}
|