@volter-ai-dev/supercode-browser-playwright 0.1.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 ADDED
@@ -0,0 +1,136 @@
1
+ # Supercode browser provider over Playwright
2
+
3
+ `@volter-ai-dev/supercode-browser-playwright` is a Supercode browser provider
4
+ that is an unmodified `playwright-core` client. It connects to a CDP endpoint
5
+ with `chromium.connectOverCDP`, drives the first page of the first context, and
6
+ answers Supercode's existing wire unchanged: `supercode/browser-provider-v1`
7
+ envelopes carrying `supercode/browser-operation-v1` calls.
8
+
9
+ The package owns that wire: the operation vocabulary, input parsers, results
10
+ and error codes (`./protocol`). Every locator, action and snapshot is
11
+ playwright-core 1.63.0's own, with Playwright's actionability waits.
12
+
13
+ ## Operations
14
+
15
+ All 21 operations:
16
+
17
+ | Operation | Over Playwright |
18
+ |---|---|
19
+ | `browser.status` | `{available, features, syntheticEvents, implementation: "playwright-core 1.63.0", endpoint: browser.version()}` |
20
+ | `browser.snapshot` | `ariaSnapshot({mode: "ai"})` of the page or the locator's first match, bounded to 64 000 characters |
21
+ | `browser.query` | `count()` and up to 50 inspections |
22
+ | `browser.wait` | `locator.waitFor`, clamped to 9 s |
23
+ | `browser.click` `fill` `press` `hover` `focus` `check` `uncheck` | the locator action with a 5 s actionability budget; the result is the element's inspection |
24
+ | `browser.select` | `selectOption`, returning the selected values |
25
+ | `browser.scroll` | `scrollBy` in the page, returning `scrollX`/`scrollY` |
26
+ | `browser.box` | `boundingBox()` |
27
+ | `browser.mouse` `wheel` `drag` | `page.mouse` |
28
+ | `browser.back` `forward` `reload` | `goBack`/`goForward`/`reload`, waiting for commit |
29
+ | `browser.script` | the author's source as `async (page, args)` with the real `page`, clamped to 9 s |
30
+
31
+ Locators map one to one onto `page.locator`, `getByRole`, `getByText`,
32
+ `getByTestId`, `getByLabel`, `getByPlaceholder`, `getByAltText` and
33
+ `getByTitle`. A `{by: "ref"}` locator is a `[ref=…]` mark from
34
+ `browser.snapshot`'s AI-mode aria snapshot, resolved by Playwright's `aria-ref`
35
+ selector engine. An inspection's `ref` is the element's own aria ref, or `""`
36
+ when Playwright assigns it none (an element that is not visible or does not
37
+ receive pointer events).
38
+
39
+ The page revision is a mutation counter kept in the page by a
40
+ `MutationObserver` installed with `page.addInitScript`; a navigation starts a
41
+ new document and so a new count. Playwright timeouts are `TIMED_OUT`, a closed
42
+ page or disconnected browser is `NOT_AVAILABLE`, a strict-mode violation or a
43
+ locator matching nothing is `NOT_FOUND`, a protocol error that says
44
+ "unavailable" or "not supported" is `UNSUPPORTED`, anything else `FAILED`.
45
+
46
+ The harness abandons a provider call at 12 s, so a script's timeout (default
47
+ 30 s on the wire) is clamped to 9 s and the result says so in `notice`. A
48
+ script that outlives its deadline keeps running in the provider process; the
49
+ call still returns. Listeners a script attaches to `page` are removed when it
50
+ ends. A PNG a script returns (`await page.screenshot()`) crosses the wire as
51
+ an image record `{dataUrl, format, width, height, fidelity}`, bounded
52
+ to 768 KiB of data URL.
53
+
54
+ ## Framings
55
+
56
+ Both carry the same envelope, `{protocol, id, token, call}` in and
57
+ `{protocol, id, result}` out, with a 256 KiB request and 1 MiB response bound.
58
+
59
+ - **TCP** is the wire the harness speaks (`crates/harness/src/browser.rs`): one
60
+ newline-terminated JSON request per connection, one JSON response, then the
61
+ provider closes. The provider listens on `127.0.0.1` and writes an owner-only
62
+ discovery record (file `0600`, directory `0700`) to
63
+ `<providers dir>/browser/playwright.cdp.<port>.json`, scoped to the
64
+ canonical workspace, and removes it on stop.
65
+ - **HTTP** serves `POST /` with the request JSON as the body and answers `200`
66
+ with the response JSON, for a host that can reach the provider only by HTTP.
67
+ Other paths are `404`, other methods `405`, a body that is not JSON `400`,
68
+ an oversized one `413`.
69
+
70
+ Calls are answered one at a time. If the browser disconnected or the page
71
+ closed, the provider reconnects once for the call and otherwise answers
72
+ `NOT_AVAILABLE`.
73
+
74
+ ## CLI
75
+
76
+ ```sh
77
+ supercode-browser-playwright --cdp-url http://127.0.0.1:9222 --tcp 0 --workspace .
78
+ supercode-browser-playwright --cdp-url ws://127.0.0.1:9222/devtools/browser/… --http 8765 --token <hex>
79
+ ```
80
+
81
+ ```
82
+ --cdp-url <url> CDP endpoint (http:// resolved through /json/version, or ws://)
83
+ --tcp <port> the harness framing; 0 picks a free port
84
+ --providers-dir <dir> parent of browser/ (default: $SUPERCODE_HOME/providers, then
85
+ $XDG_CONFIG_HOME/supercode/providers, then ~/.config/supercode/providers)
86
+ --workspace <path> workspace the discovery record is scoped to (default: cwd)
87
+ --http <port> the HTTP framing; --token is required, the host passes it
88
+ --token <hex> at least 32 hex characters (default: 32 random bytes)
89
+ --synthetic-events the endpoint dispatches isTrusted:false input (an AlmostCDP endpoint)
90
+ ```
91
+
92
+ When listening it prints one JSON line, `{"listening":{"framing":"tcp","port":…}}`.
93
+
94
+ ## Fidelity
95
+
96
+ `browser.status` and the discovery record report `playwright-core 1.63.0` and
97
+ the endpoint's `browser.version()`. `syntheticEvents` comes from
98
+ `--synthetic-events`: native Chrome dispatches trusted input for CDP `Input.*`
99
+ commands, an AlmostCDP endpoint dispatches `isTrusted: false` DOM events, and
100
+ the provider cannot tell the two apart itself. `browser.script` runs with
101
+ the full Playwright `Page`, including `page.evaluate`: allowing it trusts the
102
+ agent with the page and the provider process.
103
+
104
+ ## Library
105
+
106
+ ```ts
107
+ import { createPlaywrightBrowserProvider, serveTcp } from '@volter-ai-dev/supercode-browser-playwright';
108
+
109
+ const provider = createPlaywrightBrowserProvider({
110
+ cdpUrl: 'http://127.0.0.1:9222',
111
+ token: crypto.randomBytes(32).toString('hex'),
112
+ syntheticEvents: false,
113
+ });
114
+ const server = await serveTcp(provider, { port: 0, syntheticEvents: false });
115
+ // provider.handle(request) answers one envelope without any framing.
116
+ ```
117
+
118
+ ## In a browser
119
+
120
+ Two entry points use no Node API:
121
+
122
+ - `@volter-ai-dev/supercode-browser-playwright/protocol`: the wire alone
123
+ (`parseBrowserOperationCall`, `parseBrowserOperationResult`, the protocol
124
+ names, `BrowserActionRefusal`), with no dependency, for a host that relays
125
+ calls.
126
+ - `@volter-ai-dev/supercode-browser-playwright/executor`:
127
+ `PlaywrightOperationExecutor` over any Playwright `Page`. It imports
128
+ `playwright-core`; a browser host aliases that to a browser build of it,
129
+ `@volter/almostcdp/playwright`, and answers the operations against an
130
+ AlmostCDP surface with no Node process.
131
+
132
+ A host's `actionGuard(action)` is asked before each locator action (`click`,
133
+ `fill`, `press`, `hover`, `focus`, `check`, `uncheck`, `select`, `drag`) with
134
+ the action's `Locator` and value; throwing a `BrowserActionRefusal` answers the
135
+ call with its code, such as `APPROVAL_REQUIRED`. `browser.script` is not
136
+ guarded: a script is trusted with the page.
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import("../dist/index.mjs").then(
3
+ (module) => module.main(process.argv.slice(2)),
4
+ ).catch((error) => {
5
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
6
+ process.exit(1);
7
+ });
package/dist/cli.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `supercode-browser-playwright --cdp-url <url>
3
+ * (--tcp <port> [--providers-dir <dir>] [--workspace <path>] | --http <port>)
4
+ * [--token <hex>] [--synthetic-events]`
5
+ */
6
+ import { type ServeHandle } from "./serve.js";
7
+ export declare function main(argv: readonly string[]): Promise<ServeHandle>;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Supercode's `supercode/browser-operation-v1` calls over a real Playwright
3
+ * `Page`: every locator, action and snapshot is playwright-core's own, driven
4
+ * over CDP. No Node API is used, so the executor also runs in a browser realm
5
+ * with a browser build of playwright-core (`@volter/almostcdp/playwright`).
6
+ */
7
+ import { type Locator, type Page } from "playwright-core";
8
+ import { type BrowserOperationErrorCode, type BrowserOperationResult } from "./protocol.js";
9
+ /** The implementation this provider reports; the pinned playwright-core. */
10
+ export declare const PLAYWRIGHT_IMPLEMENTATION = "playwright-core 1.63.0";
11
+ /** The harness abandons a provider call at 12 s; a script must end before that. */
12
+ export declare const SCRIPT_TIMEOUT_CLAMP_MS = 9000;
13
+ /** Actionability budget for one locator action. */
14
+ export declare const ACTION_TIMEOUT_MS = 5000;
15
+ /** `browser.wait` accepts 30 s on the wire; the harness deadline caps what can be honoured. */
16
+ export declare const WAIT_TIMEOUT_CLAMP_MS = 9000;
17
+ /** The Playwright features `browser.status` reports. */
18
+ export declare const PLAYWRIGHT_FEATURES: readonly string[];
19
+ export interface PlaywrightOperationExecutorOptions {
20
+ /** True when the endpoint dispatches `isTrusted:false` input (an AlmostCDP endpoint); false for native Chrome. */
21
+ syntheticEvents: boolean;
22
+ /** `browser.version()` of the connected endpoint, reported by `browser.status`. */
23
+ endpoint: string;
24
+ /**
25
+ * The host's policy for one locator action, asked before it runs; throwing
26
+ * a `BrowserActionRefusal` refuses it with that code. `browser.script` is
27
+ * not guarded: a script is trusted with the page.
28
+ */
29
+ actionGuard?: (action: PlaywrightAction) => void | Promise<void>;
30
+ }
31
+ /** One locator action an operation is about to perform. */
32
+ export interface PlaywrightAction {
33
+ action: "click" | "fill" | "press" | "hover" | "focus" | "check" | "uncheck" | "select" | "drag";
34
+ locator: Locator;
35
+ value?: unknown;
36
+ }
37
+ /** Map a Playwright failure onto the wire's error codes. */
38
+ export declare function classifyPlaywrightError(error: unknown): BrowserOperationErrorCode;
39
+ export declare class PlaywrightOperationExecutor {
40
+ private readonly page;
41
+ private readonly options;
42
+ private readonly ready;
43
+ constructor(page: Page, options: PlaywrightOperationExecutorOptions);
44
+ execute(raw: unknown): Promise<BrowserOperationResult>;
45
+ private locator;
46
+ /**
47
+ * Playwright's `aria-ref` engine resolves against the last AI-mode snapshot
48
+ * the page took. Refs are stable per element, so refreshing the whole-page
49
+ * snapshot before resolving one keeps every ref from `browser.snapshot`
50
+ * addressable even after an element-scoped snapshot replaced the map.
51
+ */
52
+ private refreshRefs;
53
+ private refOf;
54
+ private inspectAll;
55
+ private inspect;
56
+ /** Run a locator action; a failure on a locator that matches nothing is NOT_FOUND. */
57
+ private act;
58
+ /** The host's policy for an action; an element that is not there is the action's own failure. */
59
+ private guard;
60
+ private revision;
61
+ private executeCall;
62
+ private script;
63
+ private target;
64
+ private success;
65
+ private failure;
66
+ }