@webhands/core 0.1.0 → 0.3.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/LICENSE +661 -0
- package/README.md +177 -0
- package/dist/cookies-export.d.ts +5 -5
- package/dist/cookies-export.d.ts.map +1 -1
- package/dist/cookies-export.js +4 -4
- package/dist/errors.d.ts +24 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +24 -0
- package/dist/errors.js.map +1 -1
- package/dist/hand-host.d.ts +217 -0
- package/dist/hand-host.d.ts.map +1 -0
- package/dist/hand-host.js +351 -0
- package/dist/hand-host.js.map +1 -0
- package/dist/hand-loading.d.ts +128 -0
- package/dist/hand-loading.d.ts.map +1 -0
- package/dist/hand-loading.js +143 -0
- package/dist/hand-loading.js.map +1 -0
- package/dist/index.d.ts +6 -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 +9 -0
- package/dist/playwright-attach-transport.d.ts.map +1 -1
- package/dist/playwright-attach-transport.js +53 -91
- package/dist/playwright-attach-transport.js.map +1 -1
- package/dist/playwright-launch-transport.d.ts +81 -62
- package/dist/playwright-launch-transport.d.ts.map +1 -1
- package/dist/playwright-launch-transport.js +143 -210
- package/dist/playwright-launch-transport.js.map +1 -1
- package/dist/remote-session.d.ts +12 -2
- package/dist/remote-session.d.ts.map +1 -1
- package/dist/remote-session.js +37 -6
- package/dist/remote-session.js.map +1 -1
- package/dist/seam.d.ts +13 -5
- package/dist/seam.d.ts.map +1 -1
- package/dist/session-rpc.d.ts +76 -12
- package/dist/session-rpc.d.ts.map +1 -1
- package/dist/session-rpc.js +76 -8
- package/dist/session-rpc.js.map +1 -1
- package/dist/stub-transport.d.ts +2 -2
- package/dist/stub-transport.d.ts.map +1 -1
- package/dist/stub-transport.js +11 -0
- package/dist/stub-transport.js.map +1 -1
- package/package.json +24 -2
- package/src/cookies-export.ts +5 -5
- package/src/errors.ts +31 -0
- package/src/hand-host.ts +511 -0
- package/src/hand-loading.ts +254 -0
- package/src/index.ts +24 -2
- package/src/playwright-attach-transport.ts +65 -119
- package/src/playwright-launch-transport.ts +235 -249
- package/src/remote-session.ts +43 -5
- package/src/seam.ts +13 -5
- package/src/session-rpc.ts +121 -11
- package/src/stub-transport.ts +15 -3
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import { stat } from 'node:fs/promises';
|
|
2
|
-
import { chromium
|
|
3
|
-
import { MissingBrowserBinaryError, MissingProfileError } from './errors.js';
|
|
2
|
+
import { chromium } from 'playwright';
|
|
3
|
+
import { MissingBrowserBinaryError, MissingProfileError, MissingStealthDependencyError, } from './errors.js';
|
|
4
|
+
import { composeWithHands } from './hand-host.js';
|
|
4
5
|
import { resolveProfileLocation, } from './profile-location.js';
|
|
6
|
+
/**
|
|
7
|
+
* The package name of the optional stealth dependency. Kept as a runtime value
|
|
8
|
+
* (not an `import('patchright')` literal) so TypeScript does NOT try to resolve
|
|
9
|
+
* its types at build time, since it is an OPTIONAL dependency that is legitimately
|
|
10
|
+
* absent when stealth is never enabled.
|
|
11
|
+
*/
|
|
12
|
+
const STEALTH_PACKAGE = 'patchright';
|
|
13
|
+
/** The default lazy import of the OPTIONAL `patchright` dependency. */
|
|
14
|
+
const defaultStealthImporter = async () => {
|
|
15
|
+
// Indirect (non-literal specifier) so tsc/bundlers do not resolve the
|
|
16
|
+
// optional dep eagerly, and the module load never fails when it is absent;
|
|
17
|
+
// the import only runs when stealth is opted in.
|
|
18
|
+
const specifier = STEALTH_PACKAGE;
|
|
19
|
+
return (await import(specifier));
|
|
20
|
+
};
|
|
5
21
|
/**
|
|
6
22
|
* The v1 concrete transport: a Playwright browser the controller LAUNCHES
|
|
7
23
|
* against a dedicated, persistent profile directory it owns (PRD "Solution,
|
|
@@ -18,16 +34,40 @@ import { resolveProfileLocation, } from './profile-location.js';
|
|
|
18
34
|
* `WEBHANDS_HOME` env var, or `~/.webhands`). See
|
|
19
35
|
* {@link resolveProfileLocation}. Because that is a SHARED location, tests pass
|
|
20
36
|
* a temp `root` (or set the env var) and assert the real home is untouched.
|
|
37
|
+
*
|
|
38
|
+
* STEALTH (opt-in, default OFF): the third constructor arg can enable a
|
|
39
|
+
* Patchright-backed launch ({@link PlaywrightLaunchTransportOptions}). Patchright
|
|
40
|
+
* is an OPTIONAL dependency imported lazily only when stealth is enabled; if it
|
|
41
|
+
* is absent the transport throws {@link MissingStealthDependencyError} rather
|
|
42
|
+
* than falling back to vanilla. This addresses ONLY the CDP `Runtime.enable`
|
|
43
|
+
* automation tell; a real profile/IP/session reputation still matter (ADR-0002).
|
|
21
44
|
*/
|
|
22
45
|
export class PlaywrightLaunchTransport {
|
|
23
46
|
#location;
|
|
47
|
+
#hands;
|
|
48
|
+
#stealth;
|
|
49
|
+
#systemBrowser;
|
|
50
|
+
#importStealthChromium;
|
|
24
51
|
/**
|
|
25
52
|
* @param location overrides for where profiles live (a `root` dir and/or an
|
|
26
53
|
* `env`). Omit in production to use `~/.webhands`; pass a temp
|
|
27
54
|
* `root` in tests to isolate the shared profile location.
|
|
55
|
+
* @param hands explicitly-loaded third-party hands to compose alongside the
|
|
56
|
+
* built-ins (Phase 2, ADR-0007). These come from {@link loadHands} against
|
|
57
|
+
* the operator's explicit config; the transport does NOT discover them. Omit
|
|
58
|
+
* for the built-ins-only surface.
|
|
59
|
+
* @param options transport-construction policy, notably the opt-in `stealth`
|
|
60
|
+
* toggle and optional `systemBrowser` (see
|
|
61
|
+
* {@link PlaywrightLaunchTransportOptions}). Defaults to vanilla Playwright,
|
|
62
|
+
* bundled Chromium, stealth OFF.
|
|
28
63
|
*/
|
|
29
|
-
constructor(location = {}) {
|
|
64
|
+
constructor(location = {}, hands = [], options = {}) {
|
|
30
65
|
this.#location = location;
|
|
66
|
+
this.#hands = hands;
|
|
67
|
+
this.#stealth = options.stealth === true;
|
|
68
|
+
this.#systemBrowser = options.systemBrowser;
|
|
69
|
+
this.#importStealthChromium =
|
|
70
|
+
options.importStealthChromium ?? defaultStealthImporter;
|
|
31
71
|
}
|
|
32
72
|
async open(target) {
|
|
33
73
|
if (target.mode !== 'launch') {
|
|
@@ -45,15 +85,35 @@ export class PlaywrightLaunchTransport {
|
|
|
45
85
|
throw new MissingProfileError(loc.profile, loc.profileDir);
|
|
46
86
|
}
|
|
47
87
|
const headless = target.headed !== true;
|
|
88
|
+
// Pick the engine: the lazily-imported stealth (Patchright) chromium when
|
|
89
|
+
// opted in, else vanilla Playwright's. Resolving the stealth module is where
|
|
90
|
+
// an absent optional dependency surfaces as the typed
|
|
91
|
+
// MissingStealthDependencyError (we never fall back to vanilla silently).
|
|
92
|
+
const launcher = this.#stealth
|
|
93
|
+
? await this.#resolveStealthLauncher()
|
|
94
|
+
: chromium;
|
|
95
|
+
// Launch options: forward headless, the optional systemBrowser (Playwright's
|
|
96
|
+
// `channel`, e.g. 'chrome' to drive system Chrome, Patchright's recommended
|
|
97
|
+
// setup), and for stealth drop Playwright's automation-flavoured default
|
|
98
|
+
// args so they cannot re-add the fingerprint Patchright just removed.
|
|
99
|
+
const launchOptions = { headless };
|
|
100
|
+
if (this.#systemBrowser !== undefined) {
|
|
101
|
+
launchOptions.channel = this.#systemBrowser;
|
|
102
|
+
}
|
|
103
|
+
if (this.#stealth) {
|
|
104
|
+
launchOptions.ignoreDefaultArgs = ['--enable-automation'];
|
|
105
|
+
}
|
|
48
106
|
let context;
|
|
49
107
|
try {
|
|
50
|
-
context = await
|
|
51
|
-
headless,
|
|
52
|
-
});
|
|
108
|
+
context = await launcher.launchPersistentContext(loc.profileDir, launchOptions);
|
|
53
109
|
}
|
|
54
110
|
catch (cause) {
|
|
55
111
|
if (isMissingBrowserBinary(cause)) {
|
|
56
|
-
|
|
112
|
+
// With systemBrowser set (e.g. 'chrome') the "binary missing" failure
|
|
113
|
+
// means the SYSTEM browser is absent, not the bundled Chromium; name
|
|
114
|
+
// what is actually missing so the CLI's fix message is accurate.
|
|
115
|
+
const browser = this.#systemBrowser ?? 'chromium';
|
|
116
|
+
throw new MissingBrowserBinaryError(browser, undefined, { cause });
|
|
57
117
|
}
|
|
58
118
|
throw cause;
|
|
59
119
|
}
|
|
@@ -61,7 +121,32 @@ export class PlaywrightLaunchTransport {
|
|
|
61
121
|
// the single active page (PRD: single active session in v1). Create one if
|
|
62
122
|
// the build ever changes that invariant.
|
|
63
123
|
const pwPage = context.pages()[0] ?? (await context.newPage());
|
|
64
|
-
return makeSession(context, pwPage);
|
|
124
|
+
return makeSession(context, pwPage, this.#hands);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Resolve the stealth (`patchright`) chromium via the injected lazy importer.
|
|
128
|
+
*
|
|
129
|
+
* Confines the brittle "optional dependency absent" detection to ONE spot
|
|
130
|
+
* (mirroring {@link isMissingBrowserBinary}): any failure to import the
|
|
131
|
+
* optional package becomes the typed {@link MissingStealthDependencyError}, so
|
|
132
|
+
* the caller never silently degrades to vanilla Playwright.
|
|
133
|
+
*/
|
|
134
|
+
async #resolveStealthLauncher() {
|
|
135
|
+
let mod;
|
|
136
|
+
try {
|
|
137
|
+
mod = await this.#importStealthChromium();
|
|
138
|
+
}
|
|
139
|
+
catch (cause) {
|
|
140
|
+
throw new MissingStealthDependencyError('patchright', undefined, {
|
|
141
|
+
cause,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
if (mod === null ||
|
|
145
|
+
typeof mod !== 'object' ||
|
|
146
|
+
typeof mod.chromium?.launchPersistentContext !== 'function') {
|
|
147
|
+
throw new MissingStealthDependencyError('patchright');
|
|
148
|
+
}
|
|
149
|
+
return mod.chromium;
|
|
65
150
|
}
|
|
66
151
|
}
|
|
67
152
|
/** True iff `path` exists and is a directory. */
|
|
@@ -79,227 +164,75 @@ async function isExistingDirectory(path) {
|
|
|
79
164
|
* does not export a typed error for this, so we detect on the message (it
|
|
80
165
|
* instructs the user to run `playwright install`). We confine that brittle
|
|
81
166
|
* string match to this one spot and re-raise as a stable typed error.
|
|
167
|
+
*
|
|
168
|
+
* This also covers the `channel: 'chrome'` case, where the missing binary is the
|
|
169
|
+
* SYSTEM Chrome, not the bundled Chromium. Playwright phrases that as the
|
|
170
|
+
* channel/distribution not being found; we match those variants too so the
|
|
171
|
+
* stealth+system-Chrome path still yields the typed MissingBrowserBinaryError.
|
|
82
172
|
*/
|
|
83
173
|
function isMissingBrowserBinary(cause) {
|
|
84
174
|
const message = cause instanceof Error ? cause.message : String(cause ?? '');
|
|
85
175
|
return (/Executable doesn't exist/i.test(message) ||
|
|
86
176
|
/please run the following command to download new browsers/i.test(message) ||
|
|
87
|
-
/playwright install/i.test(message)
|
|
177
|
+
/playwright install/i.test(message) ||
|
|
178
|
+
// channel: 'chrome' (or other system channels) not installed on the host.
|
|
179
|
+
/Chromium distribution '.*' is not found/i.test(message) ||
|
|
180
|
+
/No "?(chrome|msedge|chromium)"? .* found/i.test(message));
|
|
88
181
|
}
|
|
89
|
-
/**
|
|
90
|
-
|
|
182
|
+
/**
|
|
183
|
+
* Wrap a live Playwright persistent context into the seam's {@link Session}.
|
|
184
|
+
*
|
|
185
|
+
* The VERB surface comes from the shared hand-host ({@link composeBuiltInPage}),
|
|
186
|
+
* which is the single place the eight built-in verbs are composed (no duplicated
|
|
187
|
+
* page-object literal). Only the SESSION LIFECYCLE is per-transport here: the
|
|
188
|
+
* launch transport listens on the context's `'close'` event and its `close()`
|
|
189
|
+
* calls `context.close()`, which KILLS the browser this transport spawned
|
|
190
|
+
* (contrast the attach transport, which detaches without killing the user's
|
|
191
|
+
* browser, ADR-0002).
|
|
192
|
+
*/
|
|
193
|
+
function makeSession(context, pwPage, extraHands) {
|
|
91
194
|
let closed = false;
|
|
92
195
|
const ensureOpen = () => {
|
|
93
196
|
if (closed) {
|
|
94
197
|
throw new Error('session is closed');
|
|
95
198
|
}
|
|
96
199
|
};
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
async snapshot(options) {
|
|
111
|
-
ensureOpen();
|
|
112
|
-
const url = pwPage.url();
|
|
113
|
-
if (options?.full === true) {
|
|
114
|
-
// `--full`: the raw DOM. `documentElement.outerHTML` is the serialized
|
|
115
|
-
// live DOM (post-script render), which is what an agent that wants the
|
|
116
|
-
// real HTML expects — not the original network response.
|
|
117
|
-
const content = await pwPage.evaluate(() => document.documentElement.outerHTML);
|
|
118
|
-
return { url, view: 'full', content };
|
|
119
|
-
}
|
|
120
|
-
// Default: the token-cheap accessibility tree + visible text with stable
|
|
121
|
-
// `[ref=...]` element refs. Playwright's `ariaSnapshot({mode: 'ai'})`
|
|
122
|
-
// emits exactly that — a YAML aria tree (roles + accessible names +
|
|
123
|
-
// text) where each node carries a stable `[ref=eN]` reference, assigned
|
|
124
|
-
// deterministically by traversal order so re-snapshotting an unchanged
|
|
125
|
-
// page yields the same refs. The string crosses the seam as opaque,
|
|
126
|
-
// transport-neutral text (no Playwright type leaks, ADR-0003).
|
|
127
|
-
const content = await pwPage.ariaSnapshot({ mode: 'ai' });
|
|
128
|
-
return { url, view: 'accessibility', content };
|
|
129
|
-
},
|
|
130
|
-
async click(t) {
|
|
131
|
-
ensureOpen();
|
|
132
|
-
await clickLocator(pwPage, t);
|
|
133
|
-
},
|
|
134
|
-
async type(t, text) {
|
|
135
|
-
ensureOpen();
|
|
136
|
-
await resolveLocator(pwPage, t).fill(text);
|
|
137
|
-
},
|
|
138
|
-
async eval(expression) {
|
|
139
|
-
ensureOpen();
|
|
140
|
-
// The `eval` escape hatch (PRD story 9): run the raw JS EXPRESSION in the
|
|
141
|
-
// page and return its serializable result. Playwright's `evaluate`
|
|
142
|
-
// already IS the seam's serialization contract (see {@link Page.eval}):
|
|
143
|
-
// it passes a string as an expression, awaits a returned Promise, and
|
|
144
|
-
// structurally clones the result out of the page by VALUE. That clone is
|
|
145
|
-
// richer than JSON: it preserves NaN/Infinity/BigInt and circular
|
|
146
|
-
// structures (back-refs become a `[Circular]` marker), yields `undefined`
|
|
147
|
-
// for functions/symbols, and returns an opaque preview string for a live
|
|
148
|
-
// host object (a DOM node never crosses the process boundary). A page-side
|
|
149
|
-
// throw rejects. We pass it straight through rather than re-encode it:
|
|
150
|
-
// wrapping the value in a transport-specific envelope would invent a
|
|
151
|
-
// dialect the seam deliberately avoids. The thrown error is a plain
|
|
152
|
-
// `Error`, so no Playwright/CDP type leaks across the seam (ADR-0003).
|
|
153
|
-
return pwPage.evaluate(expression);
|
|
154
|
-
},
|
|
155
|
-
async wait(condition) {
|
|
156
|
-
ensureOpen();
|
|
157
|
-
await waitFor(pwPage, condition);
|
|
158
|
-
},
|
|
159
|
-
async cookies() {
|
|
160
|
-
ensureOpen();
|
|
161
|
-
const raw = await context.cookies();
|
|
162
|
-
return raw.map(toSeamCookie);
|
|
163
|
-
},
|
|
164
|
-
async setCookies(cookies) {
|
|
165
|
-
ensureOpen();
|
|
166
|
-
await context.addCookies(cookies.map(fromSeamCookie));
|
|
167
|
-
},
|
|
200
|
+
// Resolves the first time the context is gone — whether the USER closed the
|
|
201
|
+
// window (Playwright fires the context 'close' event) or our own close()
|
|
202
|
+
// ran. This is what lets `setup-profile` hold the headed window open and
|
|
203
|
+
// block on waitForClose() until the human is done.
|
|
204
|
+
let resolveClosed;
|
|
205
|
+
const closedSignal = new Promise((resolve) => {
|
|
206
|
+
resolveClosed = resolve;
|
|
207
|
+
});
|
|
208
|
+
const markClosed = () => {
|
|
209
|
+
if (closed)
|
|
210
|
+
return;
|
|
211
|
+
closed = true;
|
|
212
|
+
resolveClosed();
|
|
168
213
|
};
|
|
214
|
+
context.on('close', markClosed);
|
|
215
|
+
// Build the verb surface from the built-in hands over a live hand-context.
|
|
216
|
+
// The host keeps the live `pwPage`/`context` in-process (they never cross the
|
|
217
|
+
// seam, ADR-0003); the hand-context carries live page access only.
|
|
218
|
+
const handContext = { pwPage, context, ensureOpen };
|
|
219
|
+
const { page, dispose: disposeHands } = composeWithHands(handContext, extraHands);
|
|
169
220
|
return {
|
|
170
221
|
page,
|
|
171
222
|
async close() {
|
|
172
|
-
if (closed)
|
|
223
|
+
if (closed) {
|
|
173
224
|
return;
|
|
174
|
-
|
|
225
|
+
}
|
|
226
|
+
// Dispose the hands first (their in-process resources), THEN tear down
|
|
227
|
+
// the browser: context.close() fires the 'close' event, which runs
|
|
228
|
+
// markClosed and KILLS the browser this transport spawned.
|
|
229
|
+
await disposeHands();
|
|
175
230
|
await context.close();
|
|
231
|
+
markClosed();
|
|
232
|
+
},
|
|
233
|
+
waitForClose() {
|
|
234
|
+
return closedSignal;
|
|
176
235
|
},
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Run the `wait` verb's three forms (PRD story 10) against a Playwright page.
|
|
181
|
-
*
|
|
182
|
-
* - `timeout` — pace by a fixed delay (`waitForTimeout`), so an agent can act
|
|
183
|
-
* like a human and let XHR-rendered content land.
|
|
184
|
-
* - `locator` — block until the addressed element appears (`Locator.waitFor()`),
|
|
185
|
-
* the form for content rendered AFTER `goto` settled on `load`.
|
|
186
|
-
* - `navigation` — block until the NEXT navigation settles to `load`. We use
|
|
187
|
-
* `waitForNavigation()` even though Playwright marks it `@deprecated` ("racy,
|
|
188
|
-
* use waitForURL"): that deprecation targets in-process TEST code that can arm
|
|
189
|
-
* the wait BEFORE the action and pass a target URL. Neither holds here. Across
|
|
190
|
-
* this seam verbs are DISCRETE sequential calls (`click` then `wait`), so we
|
|
191
|
-
* CANNOT arm before the trigger; and the realistic trigger is an async,
|
|
192
|
-
* JS-driven transition (a redirect / SPA route change that fires AFTER the
|
|
193
|
-
* agent's action, the "let XHR-rendered content load" case of story 10), so
|
|
194
|
-
* "wait for the NEXT navigation" is exactly right — whereas `waitForLoadState`
|
|
195
|
-
* would see the already-loaded current page and return before the pending
|
|
196
|
-
* transition. `waitForURL` is unusable because the verb has no target URL by
|
|
197
|
-
* design (the agent waits for "a navigation", not a known address). (See the
|
|
198
|
-
* task's ## Decisions note.)
|
|
199
|
-
*
|
|
200
|
-
* Shared by both Playwright transports so the verb behaviour stays identical
|
|
201
|
-
* (the forward-note's "do NOT write a parallel second implementation").
|
|
202
|
-
*/
|
|
203
|
-
export async function waitFor(page, condition) {
|
|
204
|
-
switch (condition.kind) {
|
|
205
|
-
case 'timeout':
|
|
206
|
-
await page.waitForTimeout(condition.ms);
|
|
207
|
-
return;
|
|
208
|
-
case 'locator':
|
|
209
|
-
await resolveLocator(page, condition.target).waitFor();
|
|
210
|
-
return;
|
|
211
|
-
case 'navigation':
|
|
212
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
213
|
-
await page.waitForNavigation();
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Resolve a raw Playwright locator EXPRESSION (ADR-0004) against the page. The
|
|
219
|
-
* verb surface passes locator expressions like `getByRole('button', …)`; we
|
|
220
|
-
* evaluate them in a small sandbox where `page`/`p` is the page, so the full
|
|
221
|
-
* Playwright locator grammar is available without leaking the type across the
|
|
222
|
-
* seam.
|
|
223
|
-
*
|
|
224
|
-
* Exported (with {@link clickLocator}/{@link waitFor}) so the attach transport
|
|
225
|
-
* resolves locators IDENTICALLY — one resolution path, no parallel addressing
|
|
226
|
-
* scheme (the forward-note's "do NOT write a parallel second implementation").
|
|
227
|
-
*/
|
|
228
|
-
export function resolveLocator(page, expression) {
|
|
229
|
-
// eslint-disable-next-line no-new-func
|
|
230
|
-
const factory = new Function('page', 'p', `return (${expression});`);
|
|
231
|
-
return factory(page, page);
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* How long a normal, actionability-checked `click` may wait before we treat the
|
|
235
|
-
* element as un-clickable and fall back to a dispatched click. Short on purpose:
|
|
236
|
-
* a hidden custom input never becomes actionable, so the regular click would
|
|
237
|
-
* otherwise burn Playwright's full default timeout (30s) before the escape path
|
|
238
|
-
* runs. The visible-element happy path clicks immediately and never hits this;
|
|
239
|
-
* this bound is the latency cost paid ONLY on the hidden/non-actionable path,
|
|
240
|
-
* and is long enough to tolerate a slow-but-eventually-actionable element
|
|
241
|
-
* (animations, late layout) before deciding to dispatch.
|
|
242
|
-
*/
|
|
243
|
-
const NORMAL_CLICK_TIMEOUT_MS = 1_000;
|
|
244
|
-
/**
|
|
245
|
-
* Run the `click` verb against a Playwright page (PRD story 8), shared by both
|
|
246
|
-
* Playwright transports so the verb behaves identically (mirrors {@link waitFor};
|
|
247
|
-
* the forward-note's "do NOT write a parallel second implementation").
|
|
248
|
-
*
|
|
249
|
-
* First try a normal `Locator.click()`, which AUTO-WAITS for the element to be
|
|
250
|
-
* visible and actionable — the right behaviour for a real button. A hidden
|
|
251
|
-
* custom input (the case the prd calls out) NEVER becomes actionable, so that
|
|
252
|
-
* click times out; on a Playwright `TimeoutError` we fall back to
|
|
253
|
-
* `dispatchEvent('click')`, which fires a click WITHOUT the actionability
|
|
254
|
-
* checks. The fallback is deliberately the documented Playwright escape (a
|
|
255
|
-
* sibling to the `eval` hatch, ADR-0004), not a reimplemented click: we keep
|
|
256
|
-
* the locator a raw resolved expression and only change HOW the resolved
|
|
257
|
-
* locator is clicked.
|
|
258
|
-
*
|
|
259
|
-
* Only a timeout triggers the fallback. The fallback `dispatchEvent` is itself
|
|
260
|
-
* bounded by the same short timeout, so a locator that resolves NO element (a
|
|
261
|
-
* bad locator) surfaces its timeout quickly instead of hanging the dispatch on
|
|
262
|
-
* Playwright's 30s default — the dispatch escape is for elements that EXIST but
|
|
263
|
-
* are not actionable (hidden custom inputs), not for absent ones.
|
|
264
|
-
*/
|
|
265
|
-
export async function clickLocator(page, expression) {
|
|
266
|
-
const target = resolveLocator(page, expression);
|
|
267
|
-
try {
|
|
268
|
-
await target.click({ timeout: NORMAL_CLICK_TIMEOUT_MS });
|
|
269
|
-
}
|
|
270
|
-
catch (cause) {
|
|
271
|
-
if (!(cause instanceof pwErrors.TimeoutError)) {
|
|
272
|
-
throw cause;
|
|
273
|
-
}
|
|
274
|
-
// The element never became actionable (e.g. a hidden custom input). Fire
|
|
275
|
-
// the click without actionability checks, the prd's explicit escape path.
|
|
276
|
-
await target.dispatchEvent('click', { timeout: NORMAL_CLICK_TIMEOUT_MS });
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
/** Map a Playwright cookie to the transport-neutral seam {@link Cookie}. */
|
|
280
|
-
function toSeamCookie(c) {
|
|
281
|
-
return {
|
|
282
|
-
name: c.name,
|
|
283
|
-
value: c.value,
|
|
284
|
-
domain: c.domain,
|
|
285
|
-
path: c.path,
|
|
286
|
-
expires: c.expires,
|
|
287
|
-
httpOnly: c.httpOnly,
|
|
288
|
-
secure: c.secure,
|
|
289
|
-
sameSite: c.sameSite,
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
/** Map a seam {@link Cookie} to a Playwright cookie shape. */
|
|
293
|
-
function fromSeamCookie(c) {
|
|
294
|
-
return {
|
|
295
|
-
name: c.name,
|
|
296
|
-
value: c.value,
|
|
297
|
-
domain: c.domain,
|
|
298
|
-
path: c.path,
|
|
299
|
-
expires: c.expires,
|
|
300
|
-
httpOnly: c.httpOnly,
|
|
301
|
-
secure: c.secure,
|
|
302
|
-
sameSite: c.sameSite,
|
|
303
236
|
};
|
|
304
237
|
}
|
|
305
238
|
//# sourceMappingURL=playwright-launch-transport.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-launch-transport.js","sourceRoot":"","sources":["../src/playwright-launch-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAC;AACtC,OAAO,
|
|
1
|
+
{"version":3,"file":"playwright-launch-transport.js","sourceRoot":"","sources":["../src/playwright-launch-transport.ts"],"names":[],"mappings":"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"}
|
package/dist/remote-session.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { Session } from './seam.js';
|
|
|
4
4
|
* long-lived `serve` process over HTTP (ADR-0005).
|
|
5
5
|
*
|
|
6
6
|
* Each `webhands <verb>` is a thin client: it cannot hold a JS
|
|
7
|
-
* reference to the server's live page, so this proxy turns every {@link
|
|
7
|
+
* reference to the server's live page, so this proxy turns every {@link WebHandsPage}
|
|
8
8
|
* verb into a session-RPC call to the running server (see `session-rpc.ts`) and
|
|
9
9
|
* returns the result. The verb command code is UNCHANGED — it still calls
|
|
10
10
|
* `provider(target)` then runs verbs against the returned `Session.page` then
|
|
@@ -17,6 +17,16 @@ import type { Session } from './seam.js';
|
|
|
17
17
|
* (`stop`), exactly as ADR-0005 requires. This is the whole reason cross-
|
|
18
18
|
* invocation persistence works: the page state survives because the client's
|
|
19
19
|
* `close()` does not reach across to the server's session.
|
|
20
|
+
*
|
|
21
|
+
* THIRD-PARTY HAND VERBS (Phase 2, Model B; ADR-0007). Pass the NAMES of the
|
|
22
|
+
* hand verbs the served process loaded as `handVerbs`; each is attached to the
|
|
23
|
+
* returned `page` as a dynamic method forwarding over the RPC via
|
|
24
|
+
* {@link callHandVerb}, so the agent gains those tools WITHOUT ever holding a
|
|
25
|
+
* live page handle. They are NOT on the seam `WebHandsPage` type (the seam knows only
|
|
26
|
+
* the eight built-ins), so a caller reaches them through a cast, exactly as a
|
|
27
|
+
* third-party hand verb is reached on the in-process composed page. The result
|
|
28
|
+
* crosses the wire as a serializable value and a page/in-hand throw rejects
|
|
29
|
+
* faithfully, the same contract as the built-in verbs.
|
|
20
30
|
*/
|
|
21
|
-
export declare function connectRemoteSession(baseUrl: string): Session;
|
|
31
|
+
export declare function connectRemoteSession(baseUrl: string, handVerbs?: readonly string[]): Session;
|
|
22
32
|
//# sourceMappingURL=remote-session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-session.d.ts","sourceRoot":"","sources":["../src/remote-session.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"remote-session.d.ts","sourceRoot":"","sources":["../src/remote-session.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,oBAAoB,CACnC,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,SAAS,MAAM,EAAO,GAC/B,OAAO,CA8DT"}
|
package/dist/remote-session.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { makeRpcPage, SESSION_RPC_PATH, } from './session-rpc.js';
|
|
1
|
+
import { callHandVerb, makeRpcPage, SESSION_RPC_PATH, } from './session-rpc.js';
|
|
2
2
|
/**
|
|
3
3
|
* A client-side {@link Session} that drives a session living in a SEPARATE
|
|
4
4
|
* long-lived `serve` process over HTTP (ADR-0005).
|
|
5
5
|
*
|
|
6
6
|
* Each `webhands <verb>` is a thin client: it cannot hold a JS
|
|
7
|
-
* reference to the server's live page, so this proxy turns every {@link
|
|
7
|
+
* reference to the server's live page, so this proxy turns every {@link WebHandsPage}
|
|
8
8
|
* verb into a session-RPC call to the running server (see `session-rpc.ts`) and
|
|
9
9
|
* returns the result. The verb command code is UNCHANGED — it still calls
|
|
10
10
|
* `provider(target)` then runs verbs against the returned `Session.page` then
|
|
@@ -17,8 +17,18 @@ import { makeRpcPage, SESSION_RPC_PATH, } from './session-rpc.js';
|
|
|
17
17
|
* (`stop`), exactly as ADR-0005 requires. This is the whole reason cross-
|
|
18
18
|
* invocation persistence works: the page state survives because the client's
|
|
19
19
|
* `close()` does not reach across to the server's session.
|
|
20
|
+
*
|
|
21
|
+
* THIRD-PARTY HAND VERBS (Phase 2, Model B; ADR-0007). Pass the NAMES of the
|
|
22
|
+
* hand verbs the served process loaded as `handVerbs`; each is attached to the
|
|
23
|
+
* returned `page` as a dynamic method forwarding over the RPC via
|
|
24
|
+
* {@link callHandVerb}, so the agent gains those tools WITHOUT ever holding a
|
|
25
|
+
* live page handle. They are NOT on the seam `WebHandsPage` type (the seam knows only
|
|
26
|
+
* the eight built-ins), so a caller reaches them through a cast, exactly as a
|
|
27
|
+
* third-party hand verb is reached on the in-process composed page. The result
|
|
28
|
+
* crosses the wire as a serializable value and a page/in-hand throw rejects
|
|
29
|
+
* faithfully, the same contract as the built-in verbs.
|
|
20
30
|
*/
|
|
21
|
-
export function connectRemoteSession(baseUrl) {
|
|
31
|
+
export function connectRemoteSession(baseUrl, handVerbs = []) {
|
|
22
32
|
const endpoint = new URL(SESSION_RPC_PATH, baseUrl).toString();
|
|
23
33
|
const send = async (request) => {
|
|
24
34
|
let res;
|
|
@@ -46,11 +56,32 @@ export function connectRemoteSession(baseUrl) {
|
|
|
46
56
|
// process boundary.
|
|
47
57
|
throw new Error(reply.error);
|
|
48
58
|
};
|
|
59
|
+
let resolveClosed;
|
|
60
|
+
const closedSignal = new Promise((resolve) => {
|
|
61
|
+
resolveClosed = resolve;
|
|
62
|
+
});
|
|
63
|
+
const page = makeRpcPage(send);
|
|
64
|
+
// Attach each loaded hand verb as a dynamic method that forwards over the same
|
|
65
|
+
// RPC `send`. The seam `WebHandsPage` type names only the built-ins, so these live on
|
|
66
|
+
// the runtime object alongside them (mirroring how a hand verb composes into
|
|
67
|
+
// the in-process page object); callers reach them through a cast.
|
|
68
|
+
const pageWithHands = page;
|
|
69
|
+
for (const name of handVerbs) {
|
|
70
|
+
pageWithHands[name] = (...args) => callHandVerb(send, name, ...args);
|
|
71
|
+
}
|
|
49
72
|
return {
|
|
50
|
-
page
|
|
73
|
+
page,
|
|
51
74
|
async close() {
|
|
52
|
-
// Intentionally a no-op: the served process owns the
|
|
53
|
-
// (see this module's overview). Teardown is the
|
|
75
|
+
// Intentionally a no-op against the SERVER: the served process owns the
|
|
76
|
+
// session's lifetime (see this module's overview). Teardown is the
|
|
77
|
+
// explicit `stop` verb. We still resolve the local close signal so a
|
|
78
|
+
// caller awaiting waitForClose() on this client handle unblocks.
|
|
79
|
+
resolveClosed();
|
|
80
|
+
},
|
|
81
|
+
waitForClose() {
|
|
82
|
+
// A client never waits on the user closing the window — that is the
|
|
83
|
+
// server's concern; this resolves on a local close() call.
|
|
84
|
+
return closedSignal;
|
|
54
85
|
},
|
|
55
86
|
};
|
|
56
87
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-session.js","sourceRoot":"","sources":["../src/remote-session.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,WAAW,EACX,gBAAgB,GAGhB,MAAM,kBAAkB,CAAC;AAG1B
|
|
1
|
+
{"version":3,"file":"remote-session.js","sourceRoot":"","sources":["../src/remote-session.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,YAAY,EACZ,WAAW,EACX,gBAAgB,GAGhB,MAAM,kBAAkB,CAAC;AAG1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,oBAAoB,CACnC,OAAe,EACf,YAA+B,EAAE;IAEjC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IAE/D,MAAM,IAAI,GAAG,KAAK,EAAE,OAA0B,EAAoB,EAAE;QACnE,IAAI,GAAa,CAAC;QAClB,IAAI,CAAC;YACJ,GAAG,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;gBAC3B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAC,cAAc,EAAE,kBAAkB,EAAC;gBAC7C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;aAC7B,CAAC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,qEAAqE;YACrE,yEAAyE;YACzE,wEAAwE;YACxE,mEAAmE;YACnE,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,IAAI,KAAK,CACd,yCAAyC,OAAO,KAAK,OAAO,EAAE,CAC9D,CAAC;QACH,CAAC;QACD,MAAM,KAAK,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAC;QACvD,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;YACd,OAAO,KAAK,CAAC,KAAK,CAAC;QACpB,CAAC;QACD,4EAA4E;QAC5E,0EAA0E;QAC1E,oBAAoB;QACpB,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC,CAAC;IAEF,IAAI,aAA0B,CAAC;IAC/B,MAAM,YAAY,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClD,aAAa,GAAG,OAAO,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAC/B,+EAA+E;IAC/E,sFAAsF;IACtF,6EAA6E;IAC7E,kEAAkE;IAClE,MAAM,aAAa,GAAG,IAA0C,CAAC;IACjE,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC9B,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAwB,EAAoB,EAAE,CACvE,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,OAAO;QACN,IAAI;QACJ,KAAK,CAAC,KAAK;YACV,wEAAwE;YACxE,mEAAmE;YACnE,qEAAqE;YACrE,iEAAiE;YACjE,aAAa,EAAE,CAAC;QACjB,CAAC;QACD,YAAY;YACX,oEAAoE;YACpE,2DAA2D;YAC3D,OAAO,YAAY,CAAC;QACrB,CAAC;KACD,CAAC;AACH,CAAC"}
|
package/dist/seam.d.ts
CHANGED
|
@@ -65,7 +65,7 @@ export interface Cookie {
|
|
|
65
65
|
readonly secure?: boolean;
|
|
66
66
|
readonly sameSite?: 'Strict' | 'Lax' | 'None';
|
|
67
67
|
}
|
|
68
|
-
/** What to wait for in the {@link
|
|
68
|
+
/** What to wait for in the {@link WebHandsPage.wait} verb. */
|
|
69
69
|
export type WaitCondition = {
|
|
70
70
|
readonly kind: 'timeout';
|
|
71
71
|
readonly ms: number;
|
|
@@ -87,7 +87,7 @@ export type WaitCondition = {
|
|
|
87
87
|
* `needsAnswers` Q3): default is the accessibility view, `--full` is raw DOM.
|
|
88
88
|
*/
|
|
89
89
|
export type SnapshotView = 'accessibility' | 'full';
|
|
90
|
-
/** Options for the {@link
|
|
90
|
+
/** Options for the {@link WebHandsPage.snapshot} verb. */
|
|
91
91
|
export interface SnapshotOptions {
|
|
92
92
|
/**
|
|
93
93
|
* When `true`, return the raw DOM (`view: 'full'`) instead of the default
|
|
@@ -124,7 +124,7 @@ export interface Snapshot {
|
|
|
124
124
|
* The page-level verb surface. One method per verb in the domain glossary.
|
|
125
125
|
* All element addressing flows through {@link LocatorString}.
|
|
126
126
|
*/
|
|
127
|
-
export interface
|
|
127
|
+
export interface WebHandsPage {
|
|
128
128
|
/** Navigate the active page to a URL and let it settle. */
|
|
129
129
|
navigate(url: string): Promise<void>;
|
|
130
130
|
/**
|
|
@@ -187,16 +187,24 @@ export interface Page {
|
|
|
187
187
|
setCookies(cookies: readonly Cookie[]): Promise<void>;
|
|
188
188
|
}
|
|
189
189
|
/**
|
|
190
|
-
* A live browser session owning one active {@link
|
|
190
|
+
* A live browser session owning one active {@link WebHandsPage}. The session lifetime
|
|
191
191
|
* spans from {@link Transport.open} to {@link Session.close}; it is the unit a
|
|
192
192
|
* long-lived controller process keeps between CLI invocations (PRD
|
|
193
193
|
* "session/daemon question").
|
|
194
194
|
*/
|
|
195
195
|
export interface Session {
|
|
196
196
|
/** The active page the verbs act on. */
|
|
197
|
-
readonly page:
|
|
197
|
+
readonly page: WebHandsPage;
|
|
198
198
|
/** Tear down the session and release the underlying browser resources. */
|
|
199
199
|
close(): Promise<void>;
|
|
200
|
+
/**
|
|
201
|
+
* Resolve when the session is closed — either by the USER closing the browser
|
|
202
|
+
* window/context, or by a {@link Session.close} call. This is what lets a
|
|
203
|
+
* headed flow (notably `setup-profile`) HOLD the window open and block until
|
|
204
|
+
* the human is done, instead of tearing it down immediately. Resolves once
|
|
205
|
+
* (idempotent); resolves immediately if the session is already closed.
|
|
206
|
+
*/
|
|
207
|
+
waitForClose(): Promise<void>;
|
|
200
208
|
}
|
|
201
209
|
/**
|
|
202
210
|
* The transport seam. A `Transport` (a.k.a. driver) knows how to OPEN a
|
package/dist/seam.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seam.d.ts","sourceRoot":"","sources":["../src/seam.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG;IACpC,QAAQ,CAAC,OAAO,EAAE,yBAAyB,CAAC;CAC5C,CAAC;AAEF,sEAAsE;AACtE,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAEzD;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GACnB;IACA,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CACzB,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CACzB,CAAC;AAEL,2DAA2D;AAC3D,MAAM,WAAW,MAAM;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;CAC9C;AAED,
|
|
1
|
+
{"version":3,"file":"seam.d.ts","sourceRoot":"","sources":["../src/seam.ts"],"names":[],"mappings":"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"}
|