@vosjs/cli 0.33.0 → 0.35.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 +2 -2
- package/dist/{chunk-6O55OD6D.js → chunk-VQXOM2NU.js} +170 -43
- package/dist/chunk-VQXOM2NU.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +28 -2
- package/dist/index.js +1 -1
- package/dist/{run-X6XJCMIZ.js → run-4DNLUCJH.js} +2 -2
- package/package.json +3 -3
- package/schema/actions.schema.json +24 -38
- package/schema/doc.schema.json +4 -4
- package/dist/chunk-6O55OD6D.js.map +0 -1
- /package/dist/{run-X6XJCMIZ.js.map → run-4DNLUCJH.js.map} +0 -0
package/README.md
CHANGED
|
@@ -143,7 +143,7 @@ A re-record replaces the footage, the cursor track, the frames and everything de
|
|
|
143
143
|
}
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
-
Seven verbs: `wait`, `hover` (`ms` 700), `click
|
|
146
|
+
Seven verbs: `wait`, `hover` (`ms` 700, the dwell), `click` (`ms` the settle after the press, 150), `type` (`delayMs` 40 per key, paced by the clock; `focus: false` skips the focusing click, for a submitting Enter; `ms` the settle after, 150), `scroll` (`ms` the settle, 200), `move`, `drag` (press, move, release: a range input, a canvas element, a timeline clip; `ms` the travel, 700). The recorder's pace is the script's: a gesture is driven by the clock, so a page that answers each pointer sample slowly costs samples, never seconds, and the only pauses are the ones the script names (`wait`, a hover's dwell, a settle). `vos record` ends with a pace line, what the script asked, what the gestures added, what the page cost, and the steps that ran slow, and the `--json` done event carries it as `pace`. Every step takes an optional unique `id`; give steps ids so a span anchored to a step survives script edits and `vos plan --reuse` can follow it, and so a layer can name the element a step touched (`overlays[].pin.step`; the recorder keeps each hover, click, type and drag step's element rect on `meta.steps[].rect`). Because the CLI issues every input itself, the cursor track is synthesized with exact coordinates, exact timing and fresh element rects, which is what powers element-aware auto-zoom and click effects downstream. The schema is [`schema/actions.schema.json`](./schema/actions.schema.json); `vos validate actions.json` checks a script without running anything.
|
|
147
147
|
|
|
148
148
|
Verified the flow in agent-browser already? `vos actions from-agent-browser steps.jsonl [--out actions.json] [--url] [--viewport WxH]` writes the script from that walk (each command kept beside its `--json` result, the batch record shape; refs resolve through the last `snapshot -i`), and names every step the recorder cannot follow rather than dropping it.
|
|
149
149
|
|
|
@@ -153,7 +153,7 @@ Verified the flow in agent-browser already? `vos actions from-agent-browser step
|
|
|
153
153
|
|
|
154
154
|
**Contracts that bite.** Time is source seconds in `zoom`, `segments`, `speed` and `tilt`, and output seconds in `overlays` and `audio`. Zoom `cx`/`cy`, overlay `transform.x`/`y` and a pin's `rect` are normalized fractions of the frame in `[0, 1]` (`0.5, 0.5` is the centre), never pixels. `level` is 1..5. The planners write `source: "auto"`; spans you add or edit carry `source: "manual"`, which a re-plan never touches, and a deleted automatic span is recorded in `doc.rejected` so a re-plan does not bring it back.
|
|
155
155
|
|
|
156
|
-
**Camera styles.** `doc.zoomStyle` is one of `glide` (default), `focus
|
|
156
|
+
**Camera styles.** `doc.zoomStyle` is one of `glide` (default: one steady zoom that travels between clicks), `focus` (a zoom per click, framed and released), `cinema` (slow moves and long holds), `snappy` (quick cycles), `cut` (instant zoom-ins) or `none`. Every style times the tilt track to its own zoom tempo, so a lean lands with its zoom; how far the card leans is `tiltStyle` (`off` | `subtle` | `medium` | `strong`), which the studio stamps when a style is picked and `doc.json` sets itself, with `tilt` spans. `keynote` and `drift` are retired names that still read: `glide` + `tiltStyle: medium` and `cinema` + `tiltStyle: subtle`.
|
|
157
157
|
|
|
158
158
|
**Tilt.** `doc.tilt`: source-anchored, non-overlapping spans where the card leans to a pose and returns to rest: `[{ "id": "u0", "in": 4, "out": 8, "rx": 6, "ry": -9, "source": "manual" }]`. `rx`/`ry` are degrees (±45 hard limit; ±5..18 reads well); `+rx` brings the top edge toward the camera, `+ry` the left edge, so a lean toward a right-side focus is a negative `ry`. Rest is flat. `doc.tiltStyle` (`off`, `subtle`, `medium`, `strong`) is the intensity the planner derives automatic spans from.
|
|
159
159
|
|
|
@@ -156,6 +156,8 @@ function validateActions(value) {
|
|
|
156
156
|
errors.push(`${at2}: ${s.do} needs a selector`);
|
|
157
157
|
if (s.do === "wait" && typeof s.ms !== "number")
|
|
158
158
|
errors.push(`${at2}: wait needs ms`);
|
|
159
|
+
if (s.ms !== void 0 && (typeof s.ms !== "number" || !Number.isFinite(s.ms) || s.ms < 0))
|
|
160
|
+
errors.push(`${at2}: ms must be a number of milliseconds \u2265 0`);
|
|
159
161
|
if (s.do === "type" && typeof s.text !== "string")
|
|
160
162
|
errors.push(`${at2}: type needs text`);
|
|
161
163
|
if (s.do === "type" && s.focus !== void 0 && typeof s.focus !== "boolean")
|
|
@@ -205,6 +207,7 @@ import {
|
|
|
205
207
|
TEXT_ENTER_KINDS,
|
|
206
208
|
TEXT_EXIT_KINDS,
|
|
207
209
|
TILT_DEG_MAX,
|
|
210
|
+
RETIRED_ZOOM_STYLES,
|
|
208
211
|
TILT_SPAN_MIN,
|
|
209
212
|
ZOOM_LEVEL_MAX,
|
|
210
213
|
ZOOM_LEVEL_MIN,
|
|
@@ -664,6 +667,12 @@ function lintDoc(docIn) {
|
|
|
664
667
|
);
|
|
665
668
|
}
|
|
666
669
|
}
|
|
670
|
+
if (typeof doc.zoomStyle === "string" && doc.zoomStyle in RETIRED_ZOOM_STYLES) {
|
|
671
|
+
const r = RETIRED_ZOOM_STYLES[doc.zoomStyle];
|
|
672
|
+
warnings.push(
|
|
673
|
+
`zoomStyle "${doc.zoomStyle}" is retired: it reads as "${r.style}" with tiltStyle "${r.tilt}" (write those instead)`
|
|
674
|
+
);
|
|
675
|
+
}
|
|
667
676
|
if (doc.tiltStyle !== void 0 && !["off", "subtle", "medium", "strong"].includes(doc.tiltStyle)) {
|
|
668
677
|
problems.push(
|
|
669
678
|
`tiltStyle must be "off" | "subtle" | "medium" | "strong" (got ${String(doc.tiltStyle)})`
|
|
@@ -6120,9 +6129,99 @@ function cappedLine(capSeconds) {
|
|
|
6120
6129
|
return `stopped at ${formatDurationCap(capSeconds)} (--max-duration ${capSeconds}); the remaining steps did not run`;
|
|
6121
6130
|
}
|
|
6122
6131
|
|
|
6132
|
+
// src/plugin/pace.ts
|
|
6133
|
+
var POINTER_MS_PER_PX = 0.7;
|
|
6134
|
+
var POINTER_MIN_MS = 250;
|
|
6135
|
+
var POINTER_MAX_MS = 800;
|
|
6136
|
+
var MOTION_TICK_MS = 16;
|
|
6137
|
+
var SETTLE_MS = {
|
|
6138
|
+
click: 150,
|
|
6139
|
+
type: 150,
|
|
6140
|
+
scroll: 200,
|
|
6141
|
+
drag: 80
|
|
6142
|
+
};
|
|
6143
|
+
var PRESS_LEAD_MS = 80;
|
|
6144
|
+
var PRESS_HOLD_MS = 70;
|
|
6145
|
+
var SCROLL_SETTLE_MS = 120;
|
|
6146
|
+
var TRAILING_HOLD_MS = 400;
|
|
6147
|
+
function pointerTravelMs(dist) {
|
|
6148
|
+
return Math.min(
|
|
6149
|
+
POINTER_MAX_MS,
|
|
6150
|
+
Math.max(POINTER_MIN_MS, Math.round(dist * POINTER_MS_PER_PX))
|
|
6151
|
+
);
|
|
6152
|
+
}
|
|
6153
|
+
function settleMs(step) {
|
|
6154
|
+
if (typeof step.ms === "number" && step.ms >= 0) return step.ms;
|
|
6155
|
+
const d = step.do;
|
|
6156
|
+
return SETTLE_MS[d] ?? 0;
|
|
6157
|
+
}
|
|
6158
|
+
var easeInOutCubic = (u) => u < 0.5 ? 4 * u * u * u : 1 - Math.pow(-2 * u + 2, 3) / 2;
|
|
6159
|
+
async function clockMotion(dur, at2, clock) {
|
|
6160
|
+
const start = clock.now();
|
|
6161
|
+
let samples = 0;
|
|
6162
|
+
for (; ; ) {
|
|
6163
|
+
const elapsed = clock.now() - start;
|
|
6164
|
+
if (elapsed >= dur) break;
|
|
6165
|
+
await at2(easeInOutCubic(Math.min(1, elapsed / dur)));
|
|
6166
|
+
samples++;
|
|
6167
|
+
const next = start + Math.ceil((clock.now() - start) / MOTION_TICK_MS) * MOTION_TICK_MS;
|
|
6168
|
+
const wait = Math.min(next, start + dur) - clock.now();
|
|
6169
|
+
if (wait > 0) await clock.sleep(wait);
|
|
6170
|
+
}
|
|
6171
|
+
await at2(1);
|
|
6172
|
+
return samples + 1;
|
|
6173
|
+
}
|
|
6174
|
+
async function clockTyping(chars, delay, type, clock) {
|
|
6175
|
+
const start = clock.now();
|
|
6176
|
+
for (let i = 0; i < chars.length; i++) {
|
|
6177
|
+
await type(chars[i], i);
|
|
6178
|
+
const due = start + (i + 1) * delay;
|
|
6179
|
+
const wait = due - clock.now();
|
|
6180
|
+
if (wait > 0 && i < chars.length - 1) await clock.sleep(wait);
|
|
6181
|
+
}
|
|
6182
|
+
}
|
|
6183
|
+
function askedMs(step) {
|
|
6184
|
+
switch (step.do) {
|
|
6185
|
+
case "wait":
|
|
6186
|
+
return step.ms ?? 0;
|
|
6187
|
+
case "hover":
|
|
6188
|
+
return step.ms ?? 700;
|
|
6189
|
+
case "drag":
|
|
6190
|
+
return step.ms ?? 700;
|
|
6191
|
+
case "type":
|
|
6192
|
+
return (step.text?.length ?? 0) * (step.delayMs ?? 40);
|
|
6193
|
+
default:
|
|
6194
|
+
return 0;
|
|
6195
|
+
}
|
|
6196
|
+
}
|
|
6197
|
+
function paceReport(steps) {
|
|
6198
|
+
const askedMs2 = steps.reduce((a, s) => a + s.askedMs, 0);
|
|
6199
|
+
const gestureMs = steps.reduce((a, s) => a + s.gestureMs, 0);
|
|
6200
|
+
const wallMs = steps.reduce((a, s) => a + s.wallMs, 0);
|
|
6201
|
+
const overheadMs = Math.max(0, wallMs - askedMs2 - gestureMs);
|
|
6202
|
+
const slow = steps.filter((s) => s.wallMs > (s.askedMs + s.gestureMs) * 1.34 + 60).map((s) => ({
|
|
6203
|
+
step: s.step,
|
|
6204
|
+
do: s.do,
|
|
6205
|
+
askedMs: s.askedMs,
|
|
6206
|
+
wallMs: s.wallMs
|
|
6207
|
+
}));
|
|
6208
|
+
return {
|
|
6209
|
+
askedMs: askedMs2,
|
|
6210
|
+
gestureMs,
|
|
6211
|
+
wallMs,
|
|
6212
|
+
overheadMs,
|
|
6213
|
+
overheadPct: wallMs > 0 ? Math.round(overheadMs / wallMs * 100) : 0,
|
|
6214
|
+
slow
|
|
6215
|
+
};
|
|
6216
|
+
}
|
|
6217
|
+
function paceLine(r) {
|
|
6218
|
+
const s = (ms) => `${(ms / 1e3).toFixed(1)} s`;
|
|
6219
|
+
const slow = r.slow.length ? `; slow: ${r.slow.map((x) => `#${x.step} ${x.do} ${s(x.wallMs)} for ${s(x.askedMs)} asked`).join(", ")}` : "";
|
|
6220
|
+
return `pace: the script asked ${s(r.askedMs)}, the gestures added ${s(r.gestureMs)}, the take ran ${s(r.wallMs)} (${r.overheadPct} % overhead${slow})`;
|
|
6221
|
+
}
|
|
6222
|
+
|
|
6123
6223
|
// src/plugin/recorder.ts
|
|
6124
6224
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
6125
|
-
var easeInOutCubic = (u) => u < 0.5 ? 4 * u * u * u : 1 - Math.pow(-2 * u + 2, 3) / 2;
|
|
6126
6225
|
function jpegDims(buf) {
|
|
6127
6226
|
let i = 2;
|
|
6128
6227
|
while (i < buf.length - 9) {
|
|
@@ -6181,26 +6280,30 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6181
6280
|
const emit = (e) => events.push({ t: now(), ...e });
|
|
6182
6281
|
await page.mouse.move(cur.x, cur.y);
|
|
6183
6282
|
emit({ x: cur.x, y: cur.y, type: "move" });
|
|
6283
|
+
const clock = { now: () => Date.now(), sleep };
|
|
6184
6284
|
const moveTo = async (tx, ty) => {
|
|
6185
6285
|
const dist = Math.hypot(tx - cur.x, ty - cur.y);
|
|
6186
|
-
const dur = Math.min(1200, Math.max(350, dist * 1.4));
|
|
6187
|
-
const steps = Math.max(6, Math.round(dur / 16));
|
|
6188
6286
|
const from = { ...cur };
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6287
|
+
await clockMotion(
|
|
6288
|
+
pointerTravelMs(dist),
|
|
6289
|
+
async (u) => {
|
|
6290
|
+
cur.x = from.x + (tx - from.x) * u;
|
|
6291
|
+
cur.y = from.y + (ty - from.y) * u;
|
|
6292
|
+
await page.mouse.move(cur.x, cur.y);
|
|
6293
|
+
emit({ x: Math.round(cur.x), y: Math.round(cur.y), type: "move" });
|
|
6294
|
+
},
|
|
6295
|
+
clock
|
|
6296
|
+
);
|
|
6197
6297
|
};
|
|
6198
6298
|
const boxOf = async (selector) => {
|
|
6199
6299
|
const loc = page.locator(selector).first();
|
|
6200
6300
|
try {
|
|
6201
6301
|
await loc.waitFor({ state: "visible", timeout: 8e3 });
|
|
6302
|
+
const before = await page.evaluate(() => [window.scrollX, window.scrollY]).catch(() => [0, 0]);
|
|
6202
6303
|
await loc.scrollIntoViewIfNeeded();
|
|
6203
|
-
await
|
|
6304
|
+
const after = await page.evaluate(() => [window.scrollX, window.scrollY]).catch(() => before);
|
|
6305
|
+
if (before[0] !== after[0] || before[1] !== after[1])
|
|
6306
|
+
await sleep(SCROLL_SETTLE_MS);
|
|
6204
6307
|
const bb = await loc.boundingBox();
|
|
6205
6308
|
if (!bb) return null;
|
|
6206
6309
|
return { x: bb.x, y: bb.y, w: bb.width, h: bb.height };
|
|
@@ -6211,7 +6314,7 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6211
6314
|
};
|
|
6212
6315
|
const clickAt = async (rect) => {
|
|
6213
6316
|
await moveTo(rect.x + rect.w / 2, rect.y + rect.h / 2);
|
|
6214
|
-
await sleep(
|
|
6317
|
+
await sleep(PRESS_LEAD_MS);
|
|
6215
6318
|
emit({
|
|
6216
6319
|
x: Math.round(cur.x),
|
|
6217
6320
|
y: Math.round(cur.y),
|
|
@@ -6220,7 +6323,7 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6220
6323
|
rect
|
|
6221
6324
|
});
|
|
6222
6325
|
await page.mouse.down();
|
|
6223
|
-
await sleep(
|
|
6326
|
+
await sleep(PRESS_HOLD_MS);
|
|
6224
6327
|
await page.mouse.up();
|
|
6225
6328
|
emit({
|
|
6226
6329
|
x: Math.round(cur.x),
|
|
@@ -6232,6 +6335,7 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6232
6335
|
};
|
|
6233
6336
|
const skipped = [];
|
|
6234
6337
|
const stepSpans = [];
|
|
6338
|
+
const paces = [];
|
|
6235
6339
|
let capped = false;
|
|
6236
6340
|
for (const [stepIdx, step] of actions.steps.entries()) {
|
|
6237
6341
|
if (capReached(now(), maxSeconds)) {
|
|
@@ -6239,6 +6343,13 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6239
6343
|
break;
|
|
6240
6344
|
}
|
|
6241
6345
|
const stepStart = now();
|
|
6346
|
+
let gestureMs = 0;
|
|
6347
|
+
const gesture = async (fn) => {
|
|
6348
|
+
const t = now();
|
|
6349
|
+
const out = await fn();
|
|
6350
|
+
gestureMs += now() - t;
|
|
6351
|
+
return out;
|
|
6352
|
+
};
|
|
6242
6353
|
const skippedBefore = skipped.length;
|
|
6243
6354
|
const urlBefore = page.url();
|
|
6244
6355
|
let stepRect = null;
|
|
@@ -6253,7 +6364,7 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6253
6364
|
const rect = await boxOf(step.selector);
|
|
6254
6365
|
stepRect = rect;
|
|
6255
6366
|
if (rect) {
|
|
6256
|
-
await moveTo(rect.x + rect.w / 2, rect.y + rect.h / 2);
|
|
6367
|
+
await gesture(() => moveTo(rect.x + rect.w / 2, rect.y + rect.h / 2));
|
|
6257
6368
|
log(`hover ${step.selector}`);
|
|
6258
6369
|
await sleep(step.ms ?? 700);
|
|
6259
6370
|
} else
|
|
@@ -6264,9 +6375,9 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6264
6375
|
const rect = await boxOf(step.selector);
|
|
6265
6376
|
stepRect = rect;
|
|
6266
6377
|
if (rect) {
|
|
6267
|
-
await clickAt(rect);
|
|
6378
|
+
await gesture(() => clickAt(rect));
|
|
6268
6379
|
log(`click ${step.selector}`);
|
|
6269
|
-
await sleep(
|
|
6380
|
+
await gesture(() => sleep(settleMs(step)));
|
|
6270
6381
|
} else
|
|
6271
6382
|
skipped.push({ step: stepIdx, do: step.do, selector: step.selector });
|
|
6272
6383
|
break;
|
|
@@ -6275,7 +6386,7 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6275
6386
|
const rect = await boxOf(step.selector);
|
|
6276
6387
|
stepRect = rect;
|
|
6277
6388
|
if (rect) {
|
|
6278
|
-
if (step.focus !== false) await clickAt(rect);
|
|
6389
|
+
if (step.focus !== false) await gesture(() => clickAt(rect));
|
|
6279
6390
|
const delay = step.delayMs ?? 40;
|
|
6280
6391
|
const ping = () => emit({
|
|
6281
6392
|
x: Math.round(rect.x + rect.w / 2),
|
|
@@ -6284,17 +6395,21 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6284
6395
|
rect
|
|
6285
6396
|
});
|
|
6286
6397
|
let lastPing = -Infinity;
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6398
|
+
await clockTyping(
|
|
6399
|
+
[...step.text],
|
|
6400
|
+
delay,
|
|
6401
|
+
async (ch) => {
|
|
6402
|
+
if (now() - lastPing >= 350) {
|
|
6403
|
+
lastPing = now();
|
|
6404
|
+
ping();
|
|
6405
|
+
}
|
|
6406
|
+
await page.keyboard.type(ch);
|
|
6407
|
+
},
|
|
6408
|
+
clock
|
|
6409
|
+
);
|
|
6295
6410
|
ping();
|
|
6296
6411
|
log(`type "${step.text}" into ${step.selector}`);
|
|
6297
|
-
await sleep(
|
|
6412
|
+
await gesture(() => sleep(settleMs(step)));
|
|
6298
6413
|
} else
|
|
6299
6414
|
skipped.push({ step: stepIdx, do: step.do, selector: step.selector });
|
|
6300
6415
|
break;
|
|
@@ -6307,7 +6422,7 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6307
6422
|
emit({ x: Math.round(cur.x), y: Math.round(cur.y), type: "scroll" });
|
|
6308
6423
|
await sleep(40);
|
|
6309
6424
|
}
|
|
6310
|
-
await sleep(
|
|
6425
|
+
await gesture(() => sleep(settleMs(step)));
|
|
6311
6426
|
break;
|
|
6312
6427
|
}
|
|
6313
6428
|
case "drag": {
|
|
@@ -6331,8 +6446,8 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6331
6446
|
start = { x: step.x, y: step.y };
|
|
6332
6447
|
}
|
|
6333
6448
|
if (!start) break;
|
|
6334
|
-
await moveTo(start.x, start.y);
|
|
6335
|
-
await sleep(
|
|
6449
|
+
await gesture(() => moveTo(start.x, start.y));
|
|
6450
|
+
await gesture(() => sleep(PRESS_LEAD_MS));
|
|
6336
6451
|
emit({
|
|
6337
6452
|
x: Math.round(cur.x),
|
|
6338
6453
|
y: Math.round(cur.y),
|
|
@@ -6341,19 +6456,20 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6341
6456
|
rect
|
|
6342
6457
|
});
|
|
6343
6458
|
await page.mouse.down();
|
|
6344
|
-
await sleep(
|
|
6459
|
+
await gesture(() => sleep(PRESS_HOLD_MS));
|
|
6345
6460
|
const dur = step.ms ?? 700;
|
|
6346
|
-
const steps = Math.max(8, Math.round(dur / 16));
|
|
6347
6461
|
const from = { ...cur };
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6462
|
+
await clockMotion(
|
|
6463
|
+
dur,
|
|
6464
|
+
async (u) => {
|
|
6465
|
+
cur.x = from.x + (step.tx - from.x) * u;
|
|
6466
|
+
cur.y = from.y + (step.ty - from.y) * u;
|
|
6467
|
+
await page.mouse.move(cur.x, cur.y);
|
|
6468
|
+
emit({ x: Math.round(cur.x), y: Math.round(cur.y), type: "move" });
|
|
6469
|
+
},
|
|
6470
|
+
clock
|
|
6471
|
+
);
|
|
6472
|
+
await gesture(() => sleep(settleMs({ do: "drag" })));
|
|
6357
6473
|
await page.mouse.up();
|
|
6358
6474
|
emit({
|
|
6359
6475
|
x: Math.round(cur.x),
|
|
@@ -6388,8 +6504,15 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6388
6504
|
}
|
|
6389
6505
|
} : {}
|
|
6390
6506
|
});
|
|
6507
|
+
paces.push({
|
|
6508
|
+
step: stepIdx,
|
|
6509
|
+
do: step.do,
|
|
6510
|
+
askedMs: askedMs(step),
|
|
6511
|
+
gestureMs,
|
|
6512
|
+
wallMs: now() - stepStart
|
|
6513
|
+
});
|
|
6391
6514
|
}
|
|
6392
|
-
if (!capped) await sleep(
|
|
6515
|
+
if (!capped) await sleep(TRAILING_HOLD_MS);
|
|
6393
6516
|
if (capped || capReached(now(), maxSeconds)) {
|
|
6394
6517
|
capped = true;
|
|
6395
6518
|
log(` ${cappedLine(maxSeconds)}`);
|
|
@@ -6441,10 +6564,13 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6441
6564
|
await writeJson(paths.meta, meta, true);
|
|
6442
6565
|
await writeJson(paths.framesIndex, frames);
|
|
6443
6566
|
await writeJson(paths.actions, { ...actions, url }, true);
|
|
6567
|
+
const pace = paceReport(paces);
|
|
6568
|
+
log(` ${paceLine(pace)}`);
|
|
6444
6569
|
return {
|
|
6445
6570
|
events,
|
|
6446
6571
|
frames,
|
|
6447
6572
|
meta,
|
|
6573
|
+
pace,
|
|
6448
6574
|
skipped,
|
|
6449
6575
|
navTimeout,
|
|
6450
6576
|
freezes,
|
|
@@ -9944,6 +10070,7 @@ async function cmdRecord(argv) {
|
|
|
9944
10070
|
freezes: rec.freezes,
|
|
9945
10071
|
freezePct: rec.freezePct,
|
|
9946
10072
|
capped: rec.capped,
|
|
10073
|
+
pace: rec.pace,
|
|
9947
10074
|
...strictFail ? { strictFailed: true } : {}
|
|
9948
10075
|
},
|
|
9949
10076
|
strictFail ? `STRICT: take recorded but incomplete \u2014 ${strictReason(rec)}; fix the flow and re-record.${skippedNote}` : `Take ready: ${outDir}
|
|
@@ -11119,4 +11246,4 @@ export {
|
|
|
11119
11246
|
convertAgentBrowser,
|
|
11120
11247
|
run
|
|
11121
11248
|
};
|
|
11122
|
-
//# sourceMappingURL=chunk-
|
|
11249
|
+
//# sourceMappingURL=chunk-VQXOM2NU.js.map
|