@vosjs/cli 0.42.0 → 0.44.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/dist/cli.js CHANGED
@@ -511,7 +511,7 @@ async function cmdCheck(argv) {
511
511
  return result.ok ? EXIT_OK : EXIT_ERROR;
512
512
  }
513
513
  async function delegate(argv, viaAlias = false) {
514
- const { run } = await import("./run-WN2IO3WL.js");
514
+ const { run } = await import("./run-CRRKT34P.js");
515
515
  if (viaAlias && argv[0]) {
516
516
  process.stderr.write(
517
517
  `note: "vos voila ${argv[0]}" is now "vos ${argv[0]}".
@@ -551,7 +551,7 @@ async function main() {
551
551
  const engine = HELP_ENGINE.split("\n").filter(
552
552
  (l) => l.startsWith(` vos ${cmd} `)
553
553
  );
554
- const { verbHelp } = await import("./run-WN2IO3WL.js");
554
+ const { verbHelp } = await import("./run-CRRKT34P.js");
555
555
  const take = verbHelp(cmd);
556
556
  process.stdout.write(
557
557
  `${engine.join("\n")}
@@ -583,7 +583,7 @@ ${take.includes("no such verb") ? "" : take}`
583
583
  main().then(async (code) => {
584
584
  if (code === EXIT_OK) {
585
585
  const verb = process.argv[2] ?? "";
586
- const { HELP } = await import("./run-WN2IO3WL.js");
586
+ const { HELP } = await import("./run-CRRKT34P.js");
587
587
  const documented = [
588
588
  ...helpFlagsFor(HELP_ENGINE, verb),
589
589
  ...helpFlagsFor(HELP, verb)
package/dist/index.d.ts CHANGED
@@ -141,6 +141,48 @@ interface MaskRule {
141
141
  text?: string;
142
142
  }
143
143
 
144
+ /**
145
+ * `setup`: the steps that run BEFORE the camera rolls. A sign-in form, a
146
+ * cookie banner, the "choose your editor" modal, an onboarding tour: things
147
+ * a take must get past and must not show. They run after the first
148
+ * navigation and before `Page.startScreencast`, with no cursor synthesis,
149
+ * no pace accounting and nothing written to `meta.steps`. Then the recorder
150
+ * navigates to `url` again and the take begins where the setup left it.
151
+ *
152
+ * A `type` step's `text` may be `{ "env": "DEMO_PASSWORD" }`: resolved at run
153
+ * time, never logged, never stored. The guarantee is narrow and real: the
154
+ * secret is never in a file that travels (`actions.json` is committed and
155
+ * pushed with the take; `meta.json` is read by the digest) and never in the
156
+ * footage. An agent with a shell can still read the variable; that is the
157
+ * maker's decision to make, not this file's to hide.
158
+ */
159
+
160
+ type SetupText = string | {
161
+ env: string;
162
+ };
163
+ type SetupStep = ({
164
+ do: 'wait';
165
+ ms: number;
166
+ } | {
167
+ do: 'click';
168
+ selector: string;
169
+ ms?: number;
170
+ } | {
171
+ do: 'type';
172
+ selector: string;
173
+ text: SetupText;
174
+ ms?: number;
175
+ } | {
176
+ do: 'press';
177
+ key: string;
178
+ ms?: number;
179
+ } | {
180
+ do: 'goto';
181
+ url: string;
182
+ }) & {
183
+ id?: string;
184
+ };
185
+
144
186
  /**
145
187
  * The action script — the declarative recipe an agent (or human) writes to
146
188
  * drive a take. Small on purpose: selectors + a handful of verbs. The recorder
@@ -163,6 +205,15 @@ interface ActionsFile {
163
205
  * copy or numbers: the video must stay true to the product.
164
206
  */
165
207
  mask?: MaskRule[];
208
+ /**
209
+ * Run BEFORE the camera rolls, after the first navigation: a sign-in form,
210
+ * a cookie banner, an onboarding tour. Plain actions with no cursor, no
211
+ * frames, no pace and nothing in `meta.steps`; then the recorder opens
212
+ * `url` again and the take begins where the setup left it. A `type`
213
+ * step's text may be `{ env: 'NAME' }`, read at run time and never logged
214
+ * or stored.
215
+ */
216
+ setup?: SetupStep[];
166
217
  steps: ActionStep[];
167
218
  }
168
219
  type ActionStep = ({
@@ -225,6 +276,12 @@ type ActionStep = ({
225
276
  * edits without breaking the cut anchored to it. Unique when present.
226
277
  */
227
278
  id?: string;
279
+ /**
280
+ * A beat's caption, two to eight words: `vos deliver` lands it as a
281
+ * lower-third at this step's moment on the cuts that take words, and
282
+ * `vos actions script` leads the beat with it.
283
+ */
284
+ caption?: string;
228
285
  };
229
286
  /** Structural validation with actionable messages. Returns [] when valid. */
230
287
  declare function validateActions(value: unknown): string[];
@@ -375,6 +432,8 @@ interface RecordOpts {
375
432
  * re-record would have replaced. A throw closes the context and propagates.
376
433
  */
377
434
  onArrival?: (arrival: Arrival) => Promise<WallVerdict | null | void> | WallVerdict | null | void;
435
+ /** Extra request headers on every request (`--header name=value`). */
436
+ headers?: Record<string, string>;
378
437
  }
379
438
  declare function recordTake(browser: Browser, url: string, actions: ActionsFile, paths: TakePaths, log: (msg: string) => void, opts?: RecordOpts): Promise<RecordResult>;
380
439
 
package/dist/index.js CHANGED
@@ -30,13 +30,14 @@ import {
30
30
  validateActions,
31
31
  waitForPageDone,
32
32
  writeSyncState
33
- } from "./chunk-7RMULTDM.js";
33
+ } from "./chunk-WCSAECM4.js";
34
34
  import {
35
35
  BrowserUnavailableError,
36
36
  configDuration,
37
37
  launchBrowser,
38
38
  loadVosConfig
39
39
  } from "./chunk-FACYY7VV.js";
40
+ import "./chunk-RDQYYRWN.js";
40
41
  import {
41
42
  manifest
42
43
  } from "./chunk-EFI3WUQB.js";
@@ -5,8 +5,9 @@ import {
5
5
  run,
6
6
  takeBrowserArgs,
7
7
  verbHelp
8
- } from "./chunk-7RMULTDM.js";
8
+ } from "./chunk-WCSAECM4.js";
9
9
  import "./chunk-FACYY7VV.js";
10
+ import "./chunk-RDQYYRWN.js";
10
11
  export {
11
12
  HELP,
12
13
  MULTI_FLAGS,
@@ -14,4 +15,4 @@ export {
14
15
  takeBrowserArgs,
15
16
  verbHelp
16
17
  };
17
- //# sourceMappingURL=run-WN2IO3WL.js.map
18
+ //# sourceMappingURL=run-CRRKT34P.js.map
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ askedMs
4
+ } from "./chunk-RDQYYRWN.js";
5
+
6
+ // src/plugin/shotList.ts
7
+ function sayTarget(selector) {
8
+ const text = /^text=(['"]?)(.+?)\1$/.exec(selector);
9
+ if (text) return `"${text[2]}"`;
10
+ const has = /:has-text\((['"])(.+?)\1\)/.exec(selector);
11
+ if (has) return `"${has[2]}"`;
12
+ const role = /getByRole\(['"](\w+)['"],\s*\{\s*name:\s*(['"])(.+?)\2/.exec(
13
+ selector
14
+ );
15
+ if (role) return `the ${role[1]} "${role[3]}"`;
16
+ const label = /\[aria-label=(['"])(.+?)\1\]/.exec(selector);
17
+ if (label) return `"${label[2]}"`;
18
+ const ph = /\[placeholder=(['"])(.+?)\1\]/.exec(selector);
19
+ if (ph) return `the "${ph[2]}" field`;
20
+ return `\`${selector}\``;
21
+ }
22
+ var s1 = (ms) => `${Math.round(ms / 100) / 10}`.replace(/\.0$/, "");
23
+ function sayId(id) {
24
+ if (!id || /^\d+$/.test(id) || id.length < 3) return null;
25
+ const words = id.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").toLowerCase().trim();
26
+ return words.length >= 3 ? words : null;
27
+ }
28
+ function sayStepTarget(step) {
29
+ const bySelector = sayTarget(step.selector);
30
+ if (!bySelector.startsWith("`")) return bySelector;
31
+ const byId = sayId(step.id);
32
+ return byId ? `${byId} (${bySelector})` : bySelector;
33
+ }
34
+ function beatOf(step) {
35
+ const cap = step.caption;
36
+ const named = cap ? `${cap}: ` : "";
37
+ switch (step.do) {
38
+ case "wait":
39
+ return null;
40
+ case "hover":
41
+ return `${named}Rest the pointer on ${sayStepTarget(step)}`;
42
+ case "click":
43
+ return `${named}Click ${sayStepTarget(step)}`;
44
+ case "type":
45
+ return `${named}Type "${step.text}" into ${sayStepTarget(step)}`;
46
+ case "scroll":
47
+ return `${named}Scroll ${step.dy < 0 ? "up" : "down"} ${step.dy < 0 ? "a little" : "a little"}`;
48
+ case "move":
49
+ return `${named}Move the pointer to the ${step.y < 360 ? "upper" : "lower"} ${step.x < 640 ? "left" : "right"}`;
50
+ case "drag":
51
+ return `${named}Drag ${step.selector ? sayStepTarget(step) : "from the start point"} across`;
52
+ }
53
+ }
54
+ function shotList(actions) {
55
+ const beats = [];
56
+ let pendingHold = 0;
57
+ let total = 0;
58
+ for (const step of actions.steps) {
59
+ total += askedMs(step);
60
+ if (step.do === "wait") {
61
+ if (beats.length) {
62
+ const last = beats[beats.length - 1];
63
+ last.holdS = (last.holdS ?? 0) + step.ms / 1e3;
64
+ } else pendingHold += step.ms;
65
+ continue;
66
+ }
67
+ const line = beatOf(step);
68
+ if (!line) continue;
69
+ const beat = { n: beats.length + 1, line };
70
+ if (step.do === "hover") beat.holdS = askedMs(step) / 1e3;
71
+ beats.push(beat);
72
+ }
73
+ void pendingHold;
74
+ return {
75
+ url: actions.url,
76
+ viewport: actions.viewport,
77
+ beats,
78
+ // gestures the recorder adds around each ask: about a second a beat
79
+ aboutS: Math.round((total + beats.length * 1e3) / 1e3),
80
+ setup: actions.setup?.length ?? 0
81
+ };
82
+ }
83
+ function formatShotList(list) {
84
+ const out = [];
85
+ out.push(
86
+ `Record this with the vosso extension in your own signed-in browser, about ${list.aboutS} seconds${list.viewport ? `, window about ${list.viewport.width}\xD7${list.viewport.height}` : ""}.`
87
+ );
88
+ if (list.url)
89
+ out.push(`Start on ${list.url}, signed in, with the page settled.`);
90
+ if (list.setup)
91
+ out.push(
92
+ `Before you press record: the script's ${list.setup} setup step${list.setup === 1 ? "" : "s"} (sign in, dismiss banners) are yours to do first; they are not part of the take.`
93
+ );
94
+ out.push("");
95
+ for (const b of list.beats) {
96
+ const hold = b.holdS && b.holdS >= 0.4 ? ` Hold ${s1(b.holdS * 1e3)} s.` : "";
97
+ out.push(`${b.n}. ${b.line}.${hold}`);
98
+ }
99
+ out.push("");
100
+ out.push(
101
+ "Move at a talking pace, let each screen settle before the next beat, and end on the finished state. Press the icon again to stop; the take opens in the studio and saves to your shelf."
102
+ );
103
+ return out.join("\n");
104
+ }
105
+ export {
106
+ formatShotList,
107
+ sayId,
108
+ sayTarget,
109
+ shotList
110
+ };
111
+ //# sourceMappingURL=shotList-FYDHO5AI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugin/shotList.ts"],"sourcesContent":["/**\n * The shot list: an `actions.json` said in words, for a person who will\n * record it by hand. The last rung of the session ladder is \"the human\n * records, the agent cuts\", and an agent that falls to it should hand over\n * more than an apology: the flow it worked out, as numbered beats with the\n * pace it wanted, so its knowledge survives the hop to a human. The take\n * comes back through the ordinary handoff and the agent cuts it.\n *\n * Pure: an ActionsFile in, lines out. Selectors are said as what they name\n * where the script says (an id, a caption, a text= locator), and as the\n * selector itself where it does not: a person reading `#filter-late` can\n * find it; a person reading nothing cannot.\n */\nimport type { ActionStep, ActionsFile } from './actions'\nimport { askedMs } from './pace'\n\nexport interface ShotBeat {\n n: number\n /** what to do, in words */\n line: string\n /** how long to hold after, in seconds, when the script asks for a hold */\n holdS?: number\n}\n\nexport interface ShotList {\n url?: string\n viewport?: { width: number; height: number }\n beats: ShotBeat[]\n /** the take's asked length, from the script's own pacing */\n aboutS: number\n setup: number\n}\n\n/** A selector as a person reads it: the text it names, or the selector itself. */\nexport function sayTarget(selector: string): string {\n const text = /^text=(['\"]?)(.+?)\\1$/.exec(selector)\n if (text) return `\"${text[2]}\"`\n const has = /:has-text\\((['\"])(.+?)\\1\\)/.exec(selector)\n if (has) return `\"${has[2]}\"`\n const role = /getByRole\\(['\"](\\w+)['\"],\\s*\\{\\s*name:\\s*(['\"])(.+?)\\2/.exec(\n selector,\n )\n if (role) return `the ${role[1]} \"${role[3]}\"`\n const label = /\\[aria-label=(['\"])(.+?)\\1\\]/.exec(selector)\n if (label) return `\"${label[2]}\"`\n const ph = /\\[placeholder=(['\"])(.+?)\\1\\]/.exec(selector)\n if (ph) return `the \"${ph[2]}\" field`\n return `\\`${selector}\\``\n}\n\nconst s1 = (ms: number) => `${Math.round(ms / 100) / 10}`.replace(/\\.0$/, '')\n\n/** A step's id, said as a name when it reads as one (`late`, `new-order`, `openBilling`). */\nexport function sayId(id: string | undefined): string | null {\n if (!id || /^\\d+$/.test(id) || id.length < 3) return null\n const words = id\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .replace(/[-_]+/g, ' ')\n .toLowerCase()\n .trim()\n return words.length >= 3 ? words : null\n}\n\n/** What a step points at, for a person: the words the selector names, else the id's, else the selector. */\nfunction sayStepTarget(step: { selector: string; id?: string }): string {\n const bySelector = sayTarget(step.selector)\n if (!bySelector.startsWith('`')) return bySelector\n const byId = sayId(step.id)\n return byId ? `${byId} (${bySelector})` : bySelector\n}\n\nfunction beatOf(step: ActionStep): string | null {\n const cap = step.caption\n const named = cap ? `${cap}: ` : ''\n switch (step.do) {\n case 'wait':\n return null\n case 'hover':\n return `${named}Rest the pointer on ${sayStepTarget(step)}`\n case 'click':\n return `${named}Click ${sayStepTarget(step)}`\n case 'type':\n return `${named}Type \"${step.text}\" into ${sayStepTarget(step)}`\n case 'scroll':\n return `${named}Scroll ${step.dy < 0 ? 'up' : 'down'} ${step.dy < 0 ? 'a little' : 'a little'}`\n case 'move':\n return `${named}Move the pointer to the ${step.y < 360 ? 'upper' : 'lower'} ${step.x < 640 ? 'left' : 'right'}`\n case 'drag':\n return `${named}Drag ${step.selector ? sayStepTarget(step as { selector: string; id?: string }) : 'from the start point'} across`\n }\n}\n\nexport function shotList(actions: ActionsFile): ShotList {\n const beats: ShotBeat[] = []\n let pendingHold = 0\n let total = 0\n for (const step of actions.steps) {\n total += askedMs(step)\n if (step.do === 'wait') {\n // A wait after a beat is that beat's hold; a leading wait is the\n // settle before the first, which a person does without being told.\n if (beats.length) {\n const last = beats[beats.length - 1]\n last.holdS = (last.holdS ?? 0) + step.ms / 1000\n } else pendingHold += step.ms\n continue\n }\n const line = beatOf(step)\n if (!line) continue\n const beat: ShotBeat = { n: beats.length + 1, line }\n if (step.do === 'hover') beat.holdS = askedMs(step) / 1000\n beats.push(beat)\n }\n void pendingHold\n return {\n url: actions.url,\n viewport: actions.viewport,\n beats,\n // gestures the recorder adds around each ask: about a second a beat\n aboutS: Math.round((total + beats.length * 1000) / 1000),\n setup: actions.setup?.length ?? 0,\n }\n}\n\n/** The list as the person reads it. */\nexport function formatShotList(list: ShotList): string {\n const out: string[] = []\n out.push(\n `Record this with the vosso extension in your own signed-in browser, about ${list.aboutS} seconds${list.viewport ? `, window about ${list.viewport.width}×${list.viewport.height}` : ''}.`,\n )\n if (list.url)\n out.push(`Start on ${list.url}, signed in, with the page settled.`)\n if (list.setup)\n out.push(\n `Before you press record: the script's ${list.setup} setup step${list.setup === 1 ? '' : 's'} (sign in, dismiss banners) are yours to do first; they are not part of the take.`,\n )\n out.push('')\n for (const b of list.beats) {\n const hold =\n b.holdS && b.holdS >= 0.4 ? ` Hold ${s1(b.holdS * 1000)} s.` : ''\n out.push(`${b.n}. ${b.line}.${hold}`)\n }\n out.push('')\n out.push(\n 'Move at a talking pace, let each screen settle before the next beat, and end on the finished state. Press the icon again to stop; the take opens in the studio and saves to your shelf.',\n )\n return out.join('\\n')\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vosjs/cli",
3
- "version": "0.42.0",
3
+ "version": "0.44.0",
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 and stills, deliver a release's media per destination spec, and sync with vos.so. One binary, every verb, MIT.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -50,12 +50,12 @@
50
50
  "playwright": "^1.49.0",
51
51
  "@vosjs/core": "^0.25.0",
52
52
  "@vosjs/elements": "^0.8.1",
53
+ "@vosjs/editor": "^1.3.1",
53
54
  "@vosjs/render-core": "^0.2.8",
55
+ "@vosjs/studio-core": "^0.29.0",
54
56
  "@vosjs/shared": "^0.4.1",
55
- "@vosjs/editor": "^1.3.1",
56
57
  "@vosjs/timeline": "^0.4.1",
57
- "@vosjs/tween": "^0.8.2",
58
- "@vosjs/studio-core": "^0.29.0"
58
+ "@vosjs/tween": "^0.8.2"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@types/node": "^22",
@@ -29,6 +29,51 @@
29
29
  }
30
30
  }
31
31
  },
32
+ "setup": {
33
+ "type": "array",
34
+ "description": "Run BEFORE the camera rolls, after the first navigation: a sign-in form, a cookie banner, an onboarding tour. 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 selector that never appears fails the take before anything is recorded (exit 2). A `type` step's text may be { \"env\": \"NAME\" }, read at run time and never logged or stored; a literal typed into a password field is refused, because actions.json is committed and pushed.",
35
+ "items": {
36
+ "type": "object",
37
+ "required": ["do"],
38
+ "additionalProperties": false,
39
+ "properties": {
40
+ "do": {
41
+ "type": "string",
42
+ "enum": ["wait", "click", "type", "press", "goto"]
43
+ },
44
+ "id": { "type": "string" },
45
+ "selector": { "type": "string" },
46
+ "text": {
47
+ "oneOf": [
48
+ { "type": "string" },
49
+ {
50
+ "type": "object",
51
+ "required": ["env"],
52
+ "additionalProperties": false,
53
+ "properties": {
54
+ "env": {
55
+ "type": "string",
56
+ "description": "the environment variable holding the value"
57
+ }
58
+ }
59
+ }
60
+ ]
61
+ },
62
+ "key": {
63
+ "type": "string",
64
+ "description": "press: a Playwright key name (Enter, Tab, Escape)"
65
+ },
66
+ "url": {
67
+ "type": "string",
68
+ "description": "goto: a page to open, for a sign-in that lives elsewhere"
69
+ },
70
+ "ms": {
71
+ "type": "number",
72
+ "description": "wait: the pause; other verbs: the settle after (default 150)"
73
+ }
74
+ }
75
+ }
76
+ },
32
77
  "mask": {
33
78
  "type": "array",
34
79
  "description": "Hidden BEFORE the first frame is captured and kept hidden across navigations and re-renders: what a signed-in account shows that must not ship. The real value is never in a frame, never in the recording, never pushed. `vos record` reports what it still sees afterwards (meta.exposures), and a mask whose selector reached nothing fails --strict.",