@vosjs/cli 0.83.2 → 0.85.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 CHANGED
@@ -135,6 +135,15 @@ Every join writes `join.json` beside the document: each input's path (relative t
135
135
  ```bash
136
136
  vos score film --track fresh-focus --at m1:share --dry-run # a catalog slug, a step on media m1: print the plan and the envelope
137
137
  vos score film --track ./theme.wav --at 12 --gain 0.4 # a local file (copied into film/music/), output seconds
138
+ vos score film --track best # the bed vos music pick ranks first, placed on the film's payoff
139
+ ```
140
+
141
+ **Choosing the bed.** An agent cannot listen, so the library says what each track is (`GET /api/music`, manifest version 2: tempo and the beat grid with a confidence, the quiet intro, the loop seam, how it ends, its sections, how busy it is; and, written by a person who listened, vocals, tags from a closed vocabulary, one sentence, loops). `vos music ls` prints it one line per track, filtered by `--mood`, `--vocals none|chops|lead`, `--loops`, `--bpm 90-120`, `--min-length <s>` and `--q <words>`. `vos music pick <take|config.json>` ranks every track against THIS film: its length, its cuts (captions landed on steps, joins, page changes, steps marked `"zoom": true`), its payoff (`--at`, else the last marked step, else the last cut) and whether a voice plays are read from the document; the hard rules go first (the track covers the film or loops, its placement does not start inside a quiet intro, no sung lead under a voice), then the scores (a swell landing on the payoff, cuts on the beat (never scored on an uncertain beat), the asked `--mood` or `--words`, a calm bed under a voice, an ending that meets the film's). The top three come back with their reasons and the exact `vos score` line that places each; `--json` carries every track, the refused ones with their reason; `--like <vos id|slug>` nudges the bed a project already uses. Ties fall to the take's own name, so two films asking the same thing get different beds and one film always gets the same answer. `LAUNCH.md`'s `music: <mood or words>` goes through the same ranking in `vos plan` (a slug stays that track), and the plan's report names the pick and the runner-up. When no bed fits, `pick` says so and a composed score (the `vos-score` skill) is the way.
142
+
143
+ ```bash
144
+ vos music ls --mood chill --vocals none --loops # what the library has for a calm loop
145
+ vos music pick film --at share --words "warm piano" # three beds for this film, the payoff on the share step
146
+ vos music pick program/config.json --json # a program: its scene labels are the cuts
138
147
  ```
139
148
 
140
149
  The track is decoded (mediabunny in Node for PCM; a compressed track in the headless browser) and measured as a short-term loudness curve, a 1 s window every 0.5 s: K-weighted LUFS (BS.1770) for mono and stereo audio, RMS dBFS otherwise, and the output says which. Its strongest swell is the largest sustained rise to a peak (at least 3 dB, holding within 3 dB of the peak for 1.5 s), measured once the track has established its level: a rise starts no earlier than 5 s after the track first comes within 12 dB of its median level, and from no lower than that, so a fade in from silence at the top is the track starting, never its swell; it lands where it ARRIVES, within 1 dB of the peak. `--at` is where it should land: a step (its id, `step:<index>` by its index, `<media>:<step>` for another media's), resolved through the cut exactly as an anchored caption is, or output seconds. The clip's `in` is set so the swell plays there, `start` is 0, `out` is `in` plus the film's length, with `--gain` (0.5), `--fade-out` (1.5 s) and, whenever the track is entered mid-song (`in` > 0), a 0.8 s fade-in (`--fade-in` sets it, 0 for none); when the track cannot both hold the swell there and cover the film, the clamp is said in words. Without `--at` the track starts at its first moment above its median level. A second run replaces the clip; `--dry-run` writes nothing and prints the swell, the offset and the curve as one line of blocks (`--json` carries every point). The clip carries what the track measures (`loudness`, below) and a loudness target, `normalize: -16` (`--normalize <lufs>` sets another, `--normalize off` none), with `--gain` a trim on the target: a catalog track, mastered at about -16 LUFS, plays at the default 0.5 as it did before it had a target, and a file of your own lands at the same level.
@@ -192,6 +201,12 @@ vos look https://app.acme.com/projects --session acme --click '[data-testid="new
192
201
 
193
202
  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`).
194
203
 
204
+ **Past a step that creates: `vos look --actions actions.json --until <step id>`.** The state a flow is about often exists only after a click that makes something (the dialog that shows a new key appears after Create), and `--click` takes one hand-written step. `--until` walks the script's own steps the way a rehearsal does, on the same door (its `setup`, its `mask`, `--session` or `--storage-state`, a `guard: "spend"` step hovered and never pressed), stops after the step whose `id` it names, and looks at the page as that step left it; the steps after it never run. The URL and viewport are the script's unless given. The walk clicks for real, so the script's `teardown` runs after the look and its line ends the report. A step skipped on the way is named and exits 2 (the page may not be the one the script reaches), a step that is never reached says why, and an `--until` that names no step lists the ids the script has. It never takes `--click` or `--type` beside it.
205
+
206
+ ```bash
207
+ vos look --actions actions.json --until create --session acme # the page after the step with "id": "create"
208
+ ```
209
+
195
210
  **`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.
196
211
 
197
212
  ```json
@@ -544,7 +559,7 @@ vos assets ls [--kind <kind>] [--q <text>] [--sort size] | usage | why <id> #
544
559
  vos recipe push <FILE.md> --folder <slug> | --asset <id> # the one recipe write: create, or replace in place
545
560
  ```
546
561
 
547
- `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`, `--yes`, `--bake` and `--claimable`. Both take `--wait`. A take push first asks the platform which `@vosjs/studio-core` it renders with (`GET /api/engine`, public, `{ studioCore, core }`) and refuses (exit 2), before anything uploads, a take that uses a field newer than it: the program a push compiles plays right, but the platform's studio lowers the stored document again with its own release, so it would show an anchored caption at its fallback start or play a clip with a loudness target at its gain, and every version saved from the studio would keep it that way. `--bake` sends a copy any renderer plays: every such field the take uses, said in what an older release reads (an anchored caption at the start it plays at in this cut with its `{elapsed}` filled, a loudness target as its gain, up to 1), whatever the platform answered, behind, current or nothing at all, and it never touches `doc.json`. The table of fields and their releases is `DOC_FEATURES` in `@vosjs/studio-core`. A platform that cannot say (no such endpoint, a refusal, no answer in 2 s) is warned of and pushed to. A new vos pushed without `--folder` lands unfiled, at the root of the shelf, and the done line says so. `--folder` takes a project on your own shelf (its slug or id) or anyone's project by its address, `<handle>/<slug>` as its page on vos.so names it: the server answers where the push lands, CONTINUING that project when the key is its owner's and otherwise STARTING the person's own from it (a private project made once, named after it, with the recipes they may read copied in, never its videos). The push says which.
562
+ `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`, `--desc`, `--prompt`, `--label`, `--note`, `--folder`, `--override`, `--yes`, `--bake` and `--claimable`. Both take `--wait`. `--desc` (up to 500 characters, checked before anything uploads) and `--prompt` are the watch page's words, kept on the vos and never on a version: a first push creates the vos with them, a later push that names them replaces them, one that names neither leaves them as they are, on a program and a take alike. A take push first asks the platform which `@vosjs/studio-core` it renders with (`GET /api/engine`, public, `{ studioCore, core }`) and refuses (exit 2), before anything uploads, a take that uses a field newer than it: the program a push compiles plays right, but the platform's studio lowers the stored document again with its own release, so it would show an anchored caption at its fallback start or play a clip with a loudness target at its gain, and every version saved from the studio would keep it that way. `--bake` sends a copy any renderer plays: every such field the take uses, said in what an older release reads (an anchored caption at the start it plays at in this cut with its `{elapsed}` filled, a loudness target as its gain, up to 1), whatever the platform answered, behind, current or nothing at all, and it never touches `doc.json`. The table of fields and their releases is `DOC_FEATURES` in `@vosjs/studio-core`. A platform that cannot say (no such endpoint, a refusal, no answer in 2 s) is warned of and pushed to. A new vos pushed without `--folder` lands unfiled, at the root of the shelf, and the done line says so. `--folder` takes a project on your own shelf (its slug or id) or anyone's project by its address, `<handle>/<slug>` as its page on vos.so names it: the server answers where the push lands, CONTINUING that project when the key is its owner's and otherwise STARTING the person's own from it (a private project made once, named after it, with the recipes they may read copied in, never its videos). The push says which.
548
563
 
549
564
  **A program's files are declared, never typed into a function.** `config.json` names each file it uses in `assets` (`"assets": { "logo": { "ref": "./logo.png", "kind": "image" }, "shots": { "ref": ["./shots/1.png", "./shots/2.png"] } }`) and reads `ctx.assets.logo`, or `"$assets.logo"` in an element, object or font. On disk a `ref` is a path in the config's own directory or below, so `vos render`, `vos still` and `vos preview` serve the file to the page from disk. A path that climbs out of that directory (`../`, an absolute path, a link that points away) is not served and not uploaded, and a config loaded from a URL reads no local file: a program's code runs in a page, and what the page is served the program can send anywhere. `vos push` uploads each local file (content-addressed, so a re-push sends nothing: pictures, SVG, video, sound, `.glb` models, fonts and HDR environments) and what it stores names the hosted file as `asset:<id>`, the one spelling the platform resolves for every render; the config on disk keeps its paths. A file an element names as `src` or a font names as `url` is lifted into the manifest on the way (the element then names `"$assets.<name>"`), and a path in `data` that is a file beside the config is uploaded and rewritten to the hosted file's path, so every place a program can name a file crosses. A file typed inside a function cannot be rewritten: `vos push` says so, with the manifest line to write. `vos fetch --media` brings a program's hosted files home under `assets/` and re-points the manifest at them (`assets/.hosted.json` remembers which hosted file each one is, so a later fetch replaces one that changed); without `--media` the manifest keeps `asset:<id>` and a local render reads each file with your key. `vos check` says when a `"$assets.<name>"` names nothing, when a declared path is not a file, and when a function names a hosted file in code instead of the manifest: a file named only in code does not load on a render of a private program and does not follow a remix. A `--claimable` push carries them too, through the claim's own upload session (below).
550
565