@webhands/core 0.4.0 → 0.6.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 +69 -6
- package/dist/errors.d.ts +112 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +121 -0
- package/dist/errors.js.map +1 -1
- package/dist/hand-host.d.ts +198 -5
- package/dist/hand-host.d.ts.map +1 -1
- package/dist/hand-host.js +664 -21
- package/dist/hand-host.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/playwright-attach-transport.d.ts +8 -1
- package/dist/playwright-attach-transport.d.ts.map +1 -1
- package/dist/playwright-attach-transport.js +19 -4
- package/dist/playwright-attach-transport.js.map +1 -1
- package/dist/playwright-launch-transport.d.ts +23 -0
- package/dist/playwright-launch-transport.d.ts.map +1 -1
- package/dist/playwright-launch-transport.js +40 -6
- package/dist/playwright-launch-transport.js.map +1 -1
- package/dist/profile-location.d.ts +19 -0
- package/dist/profile-location.d.ts.map +1 -1
- package/dist/profile-location.js +21 -0
- package/dist/profile-location.js.map +1 -1
- package/dist/seam.d.ts +501 -7
- package/dist/seam.d.ts.map +1 -1
- package/dist/seam.js +31 -0
- package/dist/seam.js.map +1 -1
- package/dist/session-rpc.d.ts +63 -1
- package/dist/session-rpc.d.ts.map +1 -1
- package/dist/session-rpc.js +174 -11
- package/dist/session-rpc.js.map +1 -1
- package/dist/socks-proxy.d.ts +61 -0
- package/dist/socks-proxy.d.ts.map +1 -0
- package/dist/socks-proxy.js +84 -0
- package/dist/socks-proxy.js.map +1 -0
- package/dist/stub-transport.d.ts.map +1 -1
- package/dist/stub-transport.js +74 -6
- package/dist/stub-transport.js.map +1 -1
- package/dist/test-fixtures/fixture-pages.d.ts.map +1 -1
- package/dist/test-fixtures/fixture-pages.js +994 -0
- package/dist/test-fixtures/fixture-pages.js.map +1 -1
- package/dist/test-fixtures/fixture-server.d.ts.map +1 -1
- package/dist/test-fixtures/fixture-server.js +33 -3
- package/dist/test-fixtures/fixture-server.js.map +1 -1
- package/package.json +1 -1
- package/src/errors.ts +164 -1
- package/src/hand-host.ts +797 -21
- package/src/index.ts +27 -1
- package/src/playwright-attach-transport.ts +25 -3
- package/src/playwright-launch-transport.ts +63 -4
- package/src/profile-location.ts +25 -0
- package/src/seam.ts +535 -7
- package/src/session-rpc.ts +276 -14
- package/src/socks-proxy.ts +127 -0
- package/src/stub-transport.ts +83 -6
- package/src/test-fixtures/fixture-pages.ts +1010 -0
- package/src/test-fixtures/fixture-server.ts +32 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixture-pages.js","sourceRoot":"","sources":["../../src/test-fixtures/fixture-pages.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,KAAK,GAAG;;;;;;;;;;;;;CAab,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;CAqBvB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BlB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;CAenB,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BZ,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;CAgBf,CAAC;AAEF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,aAAa,GAAqC;IAC9D,YAAY,EAAE,KAAK;IACnB,iBAAiB,EAAE,UAAU;IAC7B,cAAc,EAAE,eAAe;IAC/B,kBAAkB,EAAE,WAAW;IAC/B,WAAW,EAAE,IAAI;IACjB,cAAc,EAAE,OAAO;
|
|
1
|
+
{"version":3,"file":"fixture-pages.js","sourceRoot":"","sources":["../../src/test-fixtures/fixture-pages.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixture-server.d.ts","sourceRoot":"","sources":["../../src/test-fixtures/fixture-server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fixture-server.d.ts","sourceRoot":"","sources":["../../src/test-fixtures/fixture-server.ts"],"names":[],"mappings":"AAiBA,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,SAAI,GAAG,OAAO,CAAC,aAAa,CAAC,CA+CzE"}
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { createServer } from 'node:http';
|
|
2
2
|
import { FIXTURE_PAGES } from './fixture-pages.js';
|
|
3
|
+
/** Largest artificial response delay a fixture request may ask for (a guard so
|
|
4
|
+
* a stray `?delayMs=` can never hang the suite). */
|
|
5
|
+
const MAX_DELAY_MS = 5_000;
|
|
6
|
+
/** Parse `?delayMs=N` from a request URL, clamped to `[0, MAX_DELAY_MS]`. */
|
|
7
|
+
function parseDelayMs(reqUrl) {
|
|
8
|
+
const q = reqUrl.indexOf('?');
|
|
9
|
+
if (q === -1)
|
|
10
|
+
return 0;
|
|
11
|
+
const raw = new URLSearchParams(reqUrl.slice(q + 1)).get('delayMs');
|
|
12
|
+
const n = raw === null ? 0 : Number.parseInt(raw, 10);
|
|
13
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
14
|
+
return 0;
|
|
15
|
+
return Math.min(n, MAX_DELAY_MS);
|
|
16
|
+
}
|
|
3
17
|
/**
|
|
4
18
|
* Start a local HTTP server that serves the controlled static fixture pages
|
|
5
19
|
* from {@link FIXTURE_PAGES}. This is the DETERMINISTIC target for later
|
|
@@ -12,7 +26,8 @@ import { FIXTURE_PAGES } from './fixture-pages.js';
|
|
|
12
26
|
*/
|
|
13
27
|
export async function startFixtureServer(port = 0) {
|
|
14
28
|
const server = createServer((req, res) => {
|
|
15
|
-
const
|
|
29
|
+
const reqUrl = req.url ?? '/';
|
|
30
|
+
const rawPath = reqUrl.split('?')[0];
|
|
16
31
|
const key = rawPath === '/' ? 'index.html' : rawPath.replace(/^\/+/, '');
|
|
17
32
|
const body = FIXTURE_PAGES[key];
|
|
18
33
|
if (body === undefined) {
|
|
@@ -20,8 +35,23 @@ export async function startFixtureServer(port = 0) {
|
|
|
20
35
|
res.end('not found');
|
|
21
36
|
return;
|
|
22
37
|
}
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
// A `?delayMs=N` query holds the RESPONSE back by N ms before sending the
|
|
39
|
+
// (otherwise normal) page. This makes a slow NAVIGATION deterministic: a
|
|
40
|
+
// click that submits to `index.html?delayMs=1500` performs instantly but
|
|
41
|
+
// the navigation only commits ~1.5s later, the case the `click` verb must
|
|
42
|
+
// not mistake for a non-actionable element (it auto-waits for navigation
|
|
43
|
+
// only when not told otherwise). Capped so a bad value cannot hang a test.
|
|
44
|
+
const delayMs = parseDelayMs(reqUrl);
|
|
45
|
+
const send = () => {
|
|
46
|
+
res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
|
|
47
|
+
res.end(body);
|
|
48
|
+
};
|
|
49
|
+
if (delayMs > 0) {
|
|
50
|
+
setTimeout(send, delayMs);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
send();
|
|
54
|
+
}
|
|
25
55
|
});
|
|
26
56
|
await new Promise((resolve) => server.listen(port, '127.0.0.1', resolve));
|
|
27
57
|
const address = server.address();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixture-server.js","sourceRoot":"","sources":["../../src/test-fixtures/fixture-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAc,MAAM,WAAW,CAAC;AACpD,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"fixture-server.js","sourceRoot":"","sources":["../../src/test-fixtures/fixture-server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/errors.ts
CHANGED
|
@@ -19,11 +19,15 @@
|
|
|
19
19
|
export type ControllerErrorCode =
|
|
20
20
|
| 'missing-browser-binary'
|
|
21
21
|
| 'missing-stealth-dependency'
|
|
22
|
+
| 'invalid-proxy'
|
|
22
23
|
| 'missing-profile'
|
|
23
24
|
| 'attach-not-chromium'
|
|
24
25
|
| 'attach-no-context'
|
|
25
26
|
| 'no-live-server'
|
|
26
|
-
| 'session-already-active'
|
|
27
|
+
| 'session-already-active'
|
|
28
|
+
| 'cross-origin-frame'
|
|
29
|
+
| 'screenshot-path-outside-managed-dir'
|
|
30
|
+
| 'stale-ref';
|
|
27
31
|
|
|
28
32
|
/**
|
|
29
33
|
* Base class for every identifiable `core` error. Branch on {@link code}.
|
|
@@ -96,6 +100,35 @@ export class MissingStealthDependencyError extends ControllerError {
|
|
|
96
100
|
}
|
|
97
101
|
}
|
|
98
102
|
|
|
103
|
+
/**
|
|
104
|
+
* The `--proxy` value (a SOCKS URL) could not be parsed into a usable proxy
|
|
105
|
+
* config. webhands routes ALL traffic and DNS through one SOCKS proxy, so the
|
|
106
|
+
* value must be a `socks5://` or `socks5h://` URL with a host and port (an
|
|
107
|
+
* optional `user:pass@` is allowed). We refuse a malformed value LOUDLY with
|
|
108
|
+
* this typed condition rather than silently launching with no proxy (which
|
|
109
|
+
* would leak the very traffic the user asked to tunnel). The CLI maps the
|
|
110
|
+
* {@link code} to a fix message showing the expected URL shape.
|
|
111
|
+
*
|
|
112
|
+
* Mirrors {@link MissingStealthDependencyError}: a stable typed error whose
|
|
113
|
+
* brittle detection is confined to one spot (the proxy parser).
|
|
114
|
+
*/
|
|
115
|
+
export class InvalidProxyError extends ControllerError {
|
|
116
|
+
readonly code = 'invalid-proxy';
|
|
117
|
+
/** The offending raw `--proxy` value, echoed back so the user can see it. */
|
|
118
|
+
readonly value: string;
|
|
119
|
+
|
|
120
|
+
constructor(
|
|
121
|
+
value: string,
|
|
122
|
+
message: string = `Invalid --proxy value ${JSON.stringify(
|
|
123
|
+
value,
|
|
124
|
+
)}. Expected a SOCKS URL like socks5h://host:1080 or socks5://user:pass@host:1080 (socks5h tunnels DNS too; both route all traffic through the proxy).`,
|
|
125
|
+
options?: {cause?: unknown},
|
|
126
|
+
) {
|
|
127
|
+
super(message, options);
|
|
128
|
+
this.value = value;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
99
132
|
/**
|
|
100
133
|
* The named profile has not been set up yet: its dedicated profile directory
|
|
101
134
|
* does not exist on disk. A profile is created by the headed `setup-profile`
|
|
@@ -200,6 +233,136 @@ export class SessionAlreadyActiveError extends ControllerError {
|
|
|
200
233
|
}
|
|
201
234
|
}
|
|
202
235
|
|
|
236
|
+
/**
|
|
237
|
+
* A frame-scoped `eval` (or any same-origin-frame op) addressed a CROSS-ORIGIN
|
|
238
|
+
* child frame. Page-world JS cannot cross a browser security boundary, so the
|
|
239
|
+
* frame-scoped `eval` reaches the top document and SAME-ORIGIN descendant frames
|
|
240
|
+
* ONLY (the idea's honest-scope note); a cross-origin frame is unreachable BY
|
|
241
|
+
* DESIGN, not a missing feature. We refuse LOUDLY with this typed condition
|
|
242
|
+
* rather than return a silent empty result, because a silent empty would let an
|
|
243
|
+
* agent believe its callback fired / its read succeeded when the security
|
|
244
|
+
* boundary actually blocked it.
|
|
245
|
+
*
|
|
246
|
+
* Cross-origin frame reach is the SEPARATE Tier-4 `frameLocator`/coordinate
|
|
247
|
+
* surface, not this verb; the message points there so the agent is not left
|
|
248
|
+
* guessing. Mirrors the other typed conditions: the CLI maps {@link code} to a
|
|
249
|
+
* message, and {@link isControllerError} narrows it across a bundle boundary.
|
|
250
|
+
*/
|
|
251
|
+
export class CrossOriginFrameError extends ControllerError {
|
|
252
|
+
readonly code = 'cross-origin-frame';
|
|
253
|
+
/** The frame selector the caller passed (echoed back so it is visible). */
|
|
254
|
+
readonly frame: string;
|
|
255
|
+
/** The cross-origin frame's origin, when known (e.g. `https://hcaptcha.com`). */
|
|
256
|
+
readonly frameOrigin?: string;
|
|
257
|
+
/** The page's own (main-frame) origin the frame had to match. */
|
|
258
|
+
readonly pageOrigin?: string;
|
|
259
|
+
|
|
260
|
+
constructor(
|
|
261
|
+
frame: string,
|
|
262
|
+
details?: {frameOrigin?: string; pageOrigin?: string},
|
|
263
|
+
message: string = `The frame ${JSON.stringify(frame)} is CROSS-ORIGIN${
|
|
264
|
+
details?.frameOrigin !== undefined && details?.pageOrigin !== undefined
|
|
265
|
+
? ` (frame origin ${details.frameOrigin}, page origin ${details.pageOrigin})`
|
|
266
|
+
: ''
|
|
267
|
+
} and is unreachable from page-world JS. eval --frame reaches the top document and SAME-ORIGIN child frames only; a cross-origin frame is a browser security boundary. Reach cross-origin frames with the Tier-4 frameLocator/coordinate ops instead.`,
|
|
268
|
+
options?: {cause?: unknown},
|
|
269
|
+
) {
|
|
270
|
+
super(message, options);
|
|
271
|
+
this.frame = frame;
|
|
272
|
+
this.frameOrigin = details?.frameOrigin;
|
|
273
|
+
this.pageOrigin = details?.pageOrigin;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* A `screenshot --out <path>` override pointed OUTSIDE the managed screenshots
|
|
279
|
+
* dir. webhands MINTS screenshots under one managed directory it owns (under the
|
|
280
|
+
* controller home root, beside `profiles/`); a caller MAY override the output
|
|
281
|
+
* path, but only WITHIN that managed dir (prd `broaden-agent-verb-surface`, R3:
|
|
282
|
+
* "validate it stays under a sane managed dir"). An override that escapes it
|
|
283
|
+
* (an absolute path elsewhere, or a `..` traversal out) is refused LOUDLY with
|
|
284
|
+
* this typed condition rather than silently writing a PNG to an arbitrary
|
|
285
|
+
* filesystem location — the seam returns a path, so an unbounded path would let
|
|
286
|
+
* the verb clobber any file the process can write.
|
|
287
|
+
*
|
|
288
|
+
* Mirrors the other typed conditions: the CLI maps {@link code} to a message,
|
|
289
|
+
* and {@link isControllerError} narrows it across a bundle boundary.
|
|
290
|
+
*/
|
|
291
|
+
export class ScreenshotPathError extends ControllerError {
|
|
292
|
+
readonly code = 'screenshot-path-outside-managed-dir';
|
|
293
|
+
/** The offending caller-supplied `out` path, echoed back so it is visible. */
|
|
294
|
+
readonly path: string;
|
|
295
|
+
/** The managed screenshots dir the path had to stay under. */
|
|
296
|
+
readonly managedDir: string;
|
|
297
|
+
|
|
298
|
+
constructor(
|
|
299
|
+
path: string,
|
|
300
|
+
managedDir: string,
|
|
301
|
+
message: string = `The screenshot output path ${JSON.stringify(
|
|
302
|
+
path,
|
|
303
|
+
)} is OUTSIDE the managed screenshots dir (${managedDir}). A caller may override --out only within the managed dir; webhands never writes a screenshot to an arbitrary location.`,
|
|
304
|
+
options?: {cause?: unknown},
|
|
305
|
+
) {
|
|
306
|
+
super(message, options);
|
|
307
|
+
this.path = path;
|
|
308
|
+
this.managedDir = managedDir;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* A durable `query` `ref` (prd `broaden-agent-verb-surface`, R4; finding
|
|
314
|
+
* `query-ref-mint-mechanism-attribute-beats-weakmap`) failed to resolve to
|
|
315
|
+
* EXACTLY ONE element when an action verb (`click`/`type`) tried to act on it.
|
|
316
|
+
* A `ref` is a SHORT-LIVED handle, not a stable identity: between the `query`
|
|
317
|
+
* that minted it and the act, the page may have re-rendered (React keyed-list /
|
|
318
|
+
* Svelte `{#each}` NODE REPLACEMENT), navigated, or cloned a subtree carrying
|
|
319
|
+
* our minted attribute. Two stale shapes, BOTH this one error, never a silent
|
|
320
|
+
* wrong-element action:
|
|
321
|
+
*
|
|
322
|
+
* - resolve-to-ZERO ({@link matched} `=== 0`) — the element was removed or
|
|
323
|
+
* replaced (its minted node is gone, or a reused stable attribute it carried
|
|
324
|
+
* no longer exists). The agent must re-`query` against the fresh DOM.
|
|
325
|
+
* - resolve-to-MANY ({@link matched} `> 1`) — the ref matches more than one
|
|
326
|
+
* element (a framework cloned a subtree carrying our minted attribute, or a
|
|
327
|
+
* reused attribute turned out non-unique after a mutation). We refuse to
|
|
328
|
+
* "pick the first", because that is exactly the silent wrong-element click the
|
|
329
|
+
* ref exists to PREVENT.
|
|
330
|
+
*
|
|
331
|
+
* This is STRICTLY SAFER than re-addressing by a positional `.nth(i)`, which
|
|
332
|
+
* SILENTLY clicks whatever now sits at that index. The agent is told "re-query,
|
|
333
|
+
* the page changed" — its natural loop anyway. Mirrors the other typed
|
|
334
|
+
* conditions: the CLI maps {@link code} to a message, and
|
|
335
|
+
* {@link isControllerError} narrows it across a bundle boundary.
|
|
336
|
+
*/
|
|
337
|
+
export class StaleRefError extends ControllerError {
|
|
338
|
+
readonly code = 'stale-ref';
|
|
339
|
+
/** The ref locator string that went stale (echoed back so it is visible). */
|
|
340
|
+
readonly ref: string;
|
|
341
|
+
/** How many elements the ref resolved to (0 = removed/replaced, >1 = ambiguous). */
|
|
342
|
+
readonly matched: number;
|
|
343
|
+
/** Which verb tried to act on the stale ref (e.g. `click`, `type`). */
|
|
344
|
+
readonly verb: string;
|
|
345
|
+
|
|
346
|
+
constructor(
|
|
347
|
+
ref: string,
|
|
348
|
+
matched: number,
|
|
349
|
+
verb: string,
|
|
350
|
+
message: string = matched === 0
|
|
351
|
+
? `${verb}: the ref ${JSON.stringify(
|
|
352
|
+
ref,
|
|
353
|
+
)} is STALE — it now matches NO element (the page changed: the element was removed, replaced by a re-render, or the page navigated). Re-run query to get a fresh ref.`
|
|
354
|
+
: `${verb}: the ref ${JSON.stringify(
|
|
355
|
+
ref,
|
|
356
|
+
)} is AMBIGUOUS — it now matches ${matched} elements (the page changed: a subtree was cloned, or the attribute is no longer unique). Refusing to act on a guessed element; re-run query to get a fresh ref.`,
|
|
357
|
+
options?: {cause?: unknown},
|
|
358
|
+
) {
|
|
359
|
+
super(message, options);
|
|
360
|
+
this.ref = ref;
|
|
361
|
+
this.matched = matched;
|
|
362
|
+
this.verb = verb;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
203
366
|
/**
|
|
204
367
|
* Narrow an unknown caught value to a {@link ControllerError}. Prefer this over
|
|
205
368
|
* `instanceof` at package boundaries: it checks the {@link ControllerError.isControllerError}
|