@vosjs/cli 0.82.0 → 0.83.1

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 CHANGED
@@ -190,7 +190,7 @@ vos look https://app.acme.com/projects --session acme --click '[data-testid="new
190
190
 
191
191
  It writes `<out>/look.png` (the viewport; `--out look` by default) and lists every control in view (buttons, links, tabs, menu items, checkboxes, switches, options, fields, editable regions, and anything with a test id) with its tag, role, accessible name, test id, link path and query, rect in viewport px, whether it is disabled, inside an open dialog or covered by something on top of it, and ONE proposed selector: its test id; else its `aria-label`, inside the open dialog it sits in; else a link's own `href`, query kept (two tiles to `?vos=a` and `?vos=b` are two links); else role and name (`role=button[name="Continue"]`, what `page.locator` reads), narrowed by its dialog or landmark when that is what makes it one; else a field's own `name` or `placeholder`, a written id, or its words inside its dialog or landmark. Never a position in the tree. Each proposal is counted with the recorder's own selector engine and marked `UNIQUE` (it reaches this element and no other) or `AMBIGUOUS` (it reaches others too, and the recorder would take the first). A control whose words hold an address, a key or a card number is listed with those words withheld, and the frame's exposures are said the way a rehearsal says them. Looking never spends or destroys: a `--click` whose target's words begin with a stem that does (delet, remov, revok, eras, discard, publish, deploy, purchas, subscri, unsubscri, pay, buy, generat, submit, send, transfer, upgrad, confirm, leav, destroy, so `Allow deleting` and `Revoke` are caught with `Delete`), or say move to trash, empty trash or cancel subscription, is refused in words before it is pressed (exit 2), and `--type` fills a field without pressing Enter. A look cannot know every spend: `Make a video` or `Create` on a primary button may start paid work and passes, because refusing every Make would refuse half of every app. The list is a floor under looking; in a recorded flow, a step's `guard: "spend"` in `actions.json` is the fence. A sign-in in front of the page is the wall check's verdict, exit 4; the sign-in page is still looked at, since it is what a `setup` step scripts past. `--wait <ms>` adds a pause before the look for a page that animates in; `--json` prints the whole look as one done event (`url`, `screenshot`, `controls`, `exposures`, `wall`).
192
192
 
193
- **`setup`: the steps that run before the camera rolls.** A sign-in form, a cookie banner, the "choose your editor" modal, an onboarding tour: things a take must get past and must not show. `actions.json` takes `setup: [...]` beside `steps`, with the verbs `goto`, `click`, `type`, `press`, `wait`. They run after the first navigation and before a frame is captured, as plain actions with no cursor, no frames, no pace and nothing in `meta.steps`; then the recorder opens `url` again and the take begins where the setup left it. A `type` step's `text` may be `{ "env": "DEMO_PASSWORD" }`, read from the shell at run time and never logged or stored (the log names the field, never the value; a literal typed into a password field is refused by `validate`, because `actions.json` is committed and pushed with the take). A selector that never appears fails the take before anything is recorded (exit 2), because a take that begins at a half-finished sign-in is the wall by another name; rehearse the setup with `--dry-run` like everything else. This is rung 2 of the session ladder made scriptable: a local or self-hosted instance with a seeded user, no state file needed.
193
+ **`setup`: the steps that run before the camera rolls.** A sign-in form, a cookie banner, the "choose your editor" modal, an onboarding tour: things a take must get past and must not show. `actions.json` takes `setup: [...]` beside `steps`, with the verbs `goto`, `click`, `type`, `press`, `wait`, and the guard `if`. They run after the first navigation and before a frame is captured, as plain actions with no cursor, no frames, no pace and nothing in `meta.steps`; then the recorder opens `url` again and the take begins where the setup left it. A `type` step's `text` may be `{ "env": "DEMO_PASSWORD" }`, read from the shell at run time and never logged or stored (the log names the field, never the value; a literal typed into a password field is refused by `validate`, because `actions.json` is committed and pushed with the take). A selector that never appears fails the take before anything is recorded (exit 2), because a take that begins at a half-finished sign-in is the wall by another name; rehearse the setup with `--dry-run` like everything else. This is rung 2 of the session ladder made scriptable: a local or self-hosted instance with a seeded user, no state file needed.
194
194
 
195
195
  ```json
196
196
  "setup": [
@@ -203,9 +203,12 @@ It writes `<out>/look.png` (the viewport; `--out look` by default) and lists eve
203
203
 
204
204
  **`teardown`: the steps that run after it.** A flow that CREATES something in the account (an API key, a project, an invite) creates it on every rehearsal and every take, since `--dry-run` clicks for real. `teardown: [...]` beside `setup` removes it again: the same verbs and the same rules (a `{ "env": "NAME" }` text, no literal in a password field, http and https only), run off camera once the screencast has stopped, on the take's own page and session, so nothing of it is captured or lands in `meta.steps`. It runs after a rehearsal too, and when the take stops early (a failed `await`, `--strict` on a page that stopped answering, the cap) or throws once its first step ran; not when the take never began (a setup that failed, a wall). The steps together get 60 s. A teardown never fails the take or changes the exit code: it stops at the first step that fails, the done event carries `teardown: { ran, of, failed?: { step, do, selector?, error } }`, and the summary says `teardown: 3 step(s) ran` or `TEARDOWN FAILED at teardown[1] (click #confirm): <reason>. What the take created may still be in the product; remove it by hand.` A `{ env }` the shell does not hold refuses the take before it begins, since the take would create what its teardown cannot remove.
205
205
 
206
+ **`if`: a guard for either list.** `{ "do": "if", "selector": "…" }` looks for its selector (3 s, or its `ms`): on the page, the steps after it run; not on the page, they are skipped and the list ends there, which is never a failure. A take that deletes on camera what it created, and keeps a teardown as the net for a take that stops early, starts the undoing with one, so a good take says `teardown: nothing to undo, <selector> is not on the page` instead of a failure; the done event carries `skipped: { step, selector, rest }`. A setup that signs in guards its sign-in the same way (`if` the email field), so a session already signed in passes. A step that fails after a guard still fails.
207
+
206
208
  ```json
207
209
  "teardown": [
208
210
  { "do": "goto", "url": "http://localhost:3000/settings/keys" },
211
+ { "do": "if", "selector": "[data-testid=key-row]:first-child" },
209
212
  { "do": "click", "selector": "[data-testid=key-row]:first-child button[aria-label=Revoke]" },
210
213
  { "do": "click", "selector": "role=button[name=\"Revoke key\"]" }
211
214
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vosjs/cli",
3
- "version": "0.82.0",
3
+ "version": "0.83.1",
4
4
  "description": "The vos CLI: record the real product from a scripted browser flow, auto-zoom from the cursor track, cut as data in doc.json, render deterministic video, deliver a release's video cuts per destination spec, and sync with vos.so. One binary, every verb, MIT.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -52,10 +52,10 @@
52
52
  "mediabunny": "^1.55.7",
53
53
  "playwright": "^1.49.0",
54
54
  "@vosjs/core": "^0.31.0",
55
- "@vosjs/editor": "^1.6.0",
56
- "@vosjs/elements": "^0.12.1",
57
- "@vosjs/render-core": "^0.10.0",
58
55
  "@vosjs/shared": "^0.7.0",
56
+ "@vosjs/elements": "^0.12.1",
57
+ "@vosjs/editor": "^1.6.0",
58
+ "@vosjs/render-core": "^0.11.0",
59
59
  "@vosjs/studio-core": "^0.46.0",
60
60
  "@vosjs/tween": "^0.8.3",
61
61
  "@vosjs/timeline": "^0.5.0"
@@ -1078,7 +1078,8 @@
1078
1078
  "properties": {
1079
1079
  "do": {
1080
1080
  "type": "string",
1081
- "enum": ["wait", "click", "type", "press", "goto"]
1081
+ "enum": ["wait", "click", "type", "press", "goto", "if"],
1082
+ "description": "if: a guard; when its selector is not on the page, the steps after it are skipped and the list ends there, which is not a failure"
1082
1083
  },
1083
1084
  "id": {
1084
1085
  "type": "string"
@@ -1114,7 +1115,7 @@
1114
1115
  },
1115
1116
  "ms": {
1116
1117
  "type": "number",
1117
- "description": "wait: the pause; click: the read after the page settled, held from its last visual change (default 150); type and scroll: the settle after (defaults 150/200)"
1118
+ "description": "wait: the pause; click: the read after the page settled, held from its last visual change (default 150); type and scroll: the settle after (defaults 150/200); if: how long to look for its selector (default 3000)"
1118
1119
  }
1119
1120
  },
1120
1121
  "description": "One setup or teardown step: a plain action, with no cursor and no frames."
package/skills/VERSION CHANGED
@@ -1 +1 @@
1
- 0.27.0
1
+ 0.28.0
@@ -89,6 +89,25 @@ person before recording**; an answer that does neither was not an answer.
89
89
  the product is a real account, and report it at the end so they can
90
90
  delete what was made. A product where creating is cheap (a sandbox, a
91
91
  seeded local instance) needs the ledger written, not asked.
92
+ **Undo it in `teardown`** (`@vosjs/cli` 0.81 and later): the setup verbs,
93
+ run off camera after every rehearsal and take, and when one stops
94
+ early, so no rehearsal leaves a draft behind. When the video itself
95
+ deletes on camera what it made (a key created, then revoked), the
96
+ teardown is the net for a take that stopped before the delete: open it
97
+ with a guard on what the take created (`@vosjs/cli` 0.83 and later),
98
+ so a good take says `teardown: nothing to undo` instead of a failure.
99
+ ```json
100
+ "teardown": [
101
+ { "do": "press", "key": "Escape" },
102
+ { "do": "if", "selector": "[aria-label='Actions for CI deploy']" },
103
+ { "do": "click", "selector": "[aria-label='Actions for CI deploy']" },
104
+ { "do": "click", "selector": "role=menuitem[name='Revoke']" },
105
+ { "do": "click", "selector": "role=alertdialog >> role=button[name='Revoke']" }
106
+ ]
107
+ ```
108
+ A step that fails AFTER the guard is a real leftover: say it to the
109
+ person. Read the account back before you report (a `vos session exec`
110
+ script lists it) and say it holds what it held before.
92
111
  4. **What it SPENDS.** A step that costs something real (a generation, a
93
112
  purchase, a message sent, a deploy) carries `"guard": "spend"` and an
94
113
  `id`. A rehearsal hovers it and never presses it; `vos record` refuses
@@ -154,6 +173,17 @@ person before recording**; an answer that does neither was not an answer.
154
173
  steps as they are. Then the side-effect ledger (pre-flight, 3), then
155
174
  rehearse.
156
175
 
176
+ **Past what `look` reaches, script inside the session.** A state no
177
+ step reaches yet, what the account holds, a check after a flow: write
178
+ an ES module and run `vos session exec <app> script.mjs --url <page>`
179
+ (`@vosjs/cli` 0.81 and later). Its default export gets
180
+ `{ context, page, url, name }` on the profile exactly as `record` opens
181
+ it, and what it returns is printed. **Never open the session's profile
182
+ yourself** (Playwright's `launchPersistentContext`, a bundled Chromium, a
183
+ keychain flag): a browser that cannot read the profile's cookies starts
184
+ them over, the session is signed out for good, and the person signs in
185
+ again. Run one verb at a time on a session; two at once lock the profile.
186
+
157
187
  **Verified the feature with agent-browser already?** Keep that walk and
158
188
  skip the second script. agent-browser's `--json` result does not say
159
189
  what ran (`scroll` answers `{scrolled:true}`), so wrap each call so the
@@ -250,6 +280,12 @@ person before recording**; an answer that does neither was not an answer.
250
280
  flow is broken — fix it, never ship around it. A guarded step records
251
281
  only with `--confirm <id>` (pre-flight, 4). The take auto-encodes and
252
282
  auto-plans.
283
+ **Recording again into a take you already cut** (a fixed step, a longer
284
+ hold, the product changed): add `--reuse` (`@vosjs/cli` 0.81 and later).
285
+ The new footage replaces the old and your cut (zooms, captions, layers,
286
+ cover, sound) is re-timed onto it in the same command; the last line
287
+ names what it kept and what could not follow. Without it the take is
288
+ planned fresh and the cut waits in `doc.prev.json`.
253
289
  (`vos create --actions actions.json out.webm --strict` is the one-shot
254
290
  record+render verb — fine for a quick first pass, but THIS skill's loop
255
291
  reviews frames before rendering, so prefer the separate verbs here.)
@@ -302,9 +338,14 @@ person before recording**; an answer that does neither was not an answer.
302
338
  every zoom span is draggable.
303
339
 
304
340
  6c. **Pushing it to vos.so** (`vos push take`)? Set `still` in `doc.json`
305
- (OUTPUT seconds) to the payoff first, the result the video is about,
306
- never the opening: without one the cover is derived near the start,
307
- usually a zoom on a navigation control under the first caption.
341
+ (OUTPUT seconds) to the payoff BEFORE the first push, the result the
342
+ video is about, never the opening: without one the cover is derived near
343
+ the start, usually a zoom on a navigation control or an empty field
344
+ under the first caption, and moving it after costs a second version.
345
+ `vos plan take --still <t>` writes it and `vos frames take --at-still`
346
+ shows it: look before the push. The push writes
347
+ `take/vos.json`: its `vosId` is the link (`https://vos.so/vos/<vosId>`).
348
+ Read the link there, never from filtered logs or a lookup script.
308
349
 
309
350
  7. **Package for the destination**: `references/destinations.md`.
310
351
 
@@ -383,3 +424,14 @@ when the ask is a release, not one video.
383
424
  - Scripting a signed-in product blind: `vos look` first, then the ledger,
384
425
  then the rehearsal. Guessing selectors from a public page costs a take
385
426
  per guess.
427
+ - Opening a session's profile with your own Playwright: it signed a
428
+ founder's session out minutes after `vos look` had used it. Script
429
+ inside it with `vos session exec`.
430
+ - Recording again into a cut take without `--reuse`: the fresh plan takes
431
+ the cut's place.
432
+ - A teardown that undoes an on-camera delete with no `if` in front of its
433
+ undoing: every good take reports `TEARDOWN FAILED`, and a person learns
434
+ to ignore the line that one day is true.
435
+ - Cutting a caption's `duration` because the plan said it outlasts its
436
+ step: the words then cannot be read. Shorten the words, or give the step
437
+ more `ms` and record again.
@@ -26,6 +26,13 @@ how-tos and the cuts a person had to redo on them.
26
26
  the step in `actions.json`; `vos plan` and `vos join --captions` land
27
27
  each one anchored to its step, so a re-record carries it). One caption
28
28
  is one beat; a step with no caption is part of the beat before it.
29
+ It leaves when its step ends, or once its words are read when that is
30
+ later (17 characters a second, 1.2 to 3 s; `@vosjs/cli` 0.82 and later),
31
+ so a short caption on a quick step never lingers over the next one. A
32
+ caption the plan names as staying past its step has words that outrun
33
+ the beat: shorten them (an edit) or give the step more `ms` (a
34
+ re-record); never cut its `duration`, which takes the time its words
35
+ need to be read.
29
36
 
30
37
  3. **The result is the payoff, and the cold open.** Open on the finished
31
38
  result for a beat (a repeated segment at camera rest,
@@ -129,6 +129,19 @@ vos record --actions actions.json --out take --session acme --dry-run
129
129
  vos record --actions actions.json --out take --session acme --strict --json
130
130
  ```
131
131
 
132
+ Anything else you need from the signed-in browser (what the account holds,
133
+ a state no step reaches yet, a check that a flow cleaned up after itself)
134
+ runs INSIDE the session (`@vosjs/cli` 0.81 and later):
135
+
136
+ ```bash
137
+ vos session exec acme list-keys.mjs --url https://app.example.com/settings/keys
138
+ ```
139
+
140
+ `list-keys.mjs` is an ES module whose default export receives
141
+ `{ context, page, url, name }`, opened exactly as `record` opens the
142
+ profile; what it returns is printed (`result` in `--json`). Never open the
143
+ profile any other way: see the rules below.
144
+
132
145
  `--session` and `--storage-state` are two doors to one take; pass one.
133
146
  No file to mint, nothing in the take, nothing to delete: the profile lives
134
147
  under `~/.config/vos/sessions/` and `vos push` refuses a take that holds a
@@ -191,6 +204,13 @@ show, before anything is pushed further or handed over.
191
204
  mint again and it is a live credential for as long as it sits there.
192
205
  - **A session expires.** When a re-record that worked last week skips its
193
206
  first selector, re-walk the ladder before touching the script.
207
+ - **Never open a session's profile yourself.** Not with Playwright's
208
+ `launchPersistentContext`, not with a bundled Chromium, not with a
209
+ keychain flag patched in. A browser that cannot decrypt the profile's
210
+ cookies starts the store over: measured, one such launch signed a session
211
+ out for the system Chrome too, and the person had to sign in again. Every
212
+ read goes through a vos verb (`look`, `record`, `session exec`), one at a
213
+ time: two verbs on one profile at once lock it.
194
214
  - **A person signing in will use their REAL account**, whatever you asked
195
215
  for: it is the one they have. The recorder looks for you (`@vosjs/cli`
196
216
  0.42 and later): the rehearsal ends with `EXPOSED in the frame`, naming
@@ -232,7 +232,9 @@ shows it): the result the cut is about, never the opening. Without one the
232
232
  cover is derived near the start, usually a zoom on a navigation control
233
233
  under the first caption.
234
234
 
235
- The base comes from `vos.json`. `409 stale_base` → `vos pull`, re-apply on
235
+ The push writes `<take>/vos.json`: its `vosId` is the link
236
+ (`https://vos.so/vos/<vosId>`); read it there, never from filtered logs or
237
+ a lookup script. The base comes from `vos.json`. `409 stale_base` → `vos pull`, re-apply on
236
238
  top, push again. `409 protected_conflict` → keep the human's values unless
237
239
  the ask named that node (`--override <id>` only then). A 400 prints the
238
240
  field and its limit. Then `GET /api/vos/<id>`, follow