@orkestrel/browser 0.0.18 → 0.0.20

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 CHANGED
@@ -1,14 +1,11 @@
1
1
  # @orkestrel/browser
2
2
 
3
- > A lightweight Chrome DevTools Protocol automation layer for Chromium-family
4
- > browsers: an environment-agnostic core that drives pages, frames, locators, and
5
- > DOM snapshots over an injected transport, and a Node runtime that finds,
6
- > launches, and connects to the browser itself.
3
+ > A Chrome DevTools Protocol automation layer for Chromium-family browsers: an
4
+ > environment-agnostic core that drives pages, elements, and readings over an injected transport
5
+ > and publishes them as agent tools, an in-page face that drives a DOM document with native APIs,
6
+ > and a Node runtime that finds, launches, and connects to the browser itself.
7
7
 
8
- Connect to a running browser or launch one with the `createBrowser` function, open a page in its
9
- default context, and drive that page through locators, trusted input, network control, and DOM
10
- snapshots. Inject your own `CDPTransportInterface` where the runtime is not Node. Part of the
11
- `@orkestrel` line.
8
+ Connect to a running browser or launch one with the `createBrowser` function, open a page, and drive it through stable element references and trusted input. Read the page as Markdown in bounded slices, and hand the whole vocabulary to an agent as `@orkestrel/tool` tools with `createBrowserToolset`. Inside a page, `createDocumentToolset` drives a DOM document with native APIs, and `createSocketCDPTransport` carries the core client over the browser's own `WebSocket`. Record a journey, one user intent kept as JSON, from a model's tool calls or a person's gestures on a page, then list, edit, replay, and forget it through the six journey tools, or compile it with `compileBrowserJourney` into a module a developer runs and customizes. Part of the `@orkestrel` line.
12
9
 
13
10
  ## Install
14
11
 
@@ -18,30 +15,49 @@ npm install @orkestrel/browser
18
15
 
19
16
  ## Requirements
20
17
 
21
- - Node.js >= 22 for the server surface (the core surface is environment-agnostic)
22
- - ESM and CommonJS builds ship for both the core and server entry points
18
+ The package runs under the following conditions.
19
+
20
+ - Node.js 22.12.0 or later for the server face; the core runs in any JavaScript environment, and the browser face in a page, a worker, or an extension page.
21
+ - A Chromium-family browser on the host for the server face: Chrome, Chromium, or Microsoft Edge. The package downloads no browser.
22
+ - For `createSocketCDPTransport`, a browser launched with `--remote-allow-origins` naming the caller's origin; the browser refuses the handshake otherwise.
23
+ - For `page.registry`, a browser that exposes the experimental `WebMCP` protocol domain. The configuration reference of Chrome DevTools MCP 1.10.1, read on 2026-09-29, states that its WebMCP tools need Chrome 150 or later launched with `--enable-features=WebMCP`; see [the Chrome DevTools MCP configuration reference](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/configuration.md). Chromium 141 answers `WebMCP.enable` with `-32601`, and `registry.start()` resolves `false` there.
24
+ - ESM and CommonJS builds ship for the core and server entry points, and an ESM build for the browser entry point.
23
25
 
24
26
  ## Usage
25
27
 
26
- Launch or connect to a browser and drive a page from Node:
28
+ The following fence launches or connects to a browser and drives a page from Node.
27
29
 
28
30
  ```ts
29
31
  import { createBrowser } from '@orkestrel/browser/server'
30
32
 
31
- const browser = createBrowser()
33
+ const browser = createBrowser({ headless: true })
32
34
  await browser.connect()
33
35
  const page = await browser.create({ url: 'https://example.com' })
34
- await page.click('#submit')
35
- const frame = await page.frame('checkout')
36
- await frame?.fill('[name=email]', 'ada@example.com')
37
- const snapshot = await page.snapshot({ styles: ['display'] })
38
- const article = await page.article() // reader-facing prose, boilerplate pruned
36
+ const outline = await page.elements.outline() // the page's text and elements, with references such as e4
37
+ const [email] = await page.elements.find({ role: 'textbox', name: 'Email' })
38
+ await email?.fill('sam@example.test')
39
+ const [save] = await page.elements.find({ role: 'button', name: 'Save' })
40
+ await save?.click()
41
+ await page.wait('Saved')
42
+ const reading = await page.read()
43
+ reading.markdown({ limit: 4_000 }) // { text, offset, total }
39
44
  await page.screenshot({ path: 'example.png' })
40
45
  await browser.destroy()
41
46
  ```
42
47
 
43
- Drive the same protocol from any environment by injecting your own transport
44
- over the environment-agnostic core:
48
+ The following fence publishes the same page to an agent as tools.
49
+
50
+ ```ts
51
+ import { createBrowserToolset } from '@orkestrel/browser'
52
+ import { createToolManager } from '@orkestrel/tool'
53
+
54
+ const toolset = createBrowserToolset(page, { tools: createToolManager() })
55
+ await toolset.start()
56
+ await toolset.tools.execute({ id: '1', name: 'look', arguments: { what: 'the form' } })
57
+ await toolset.destroy()
58
+ ```
59
+
60
+ The following fence drives the same protocol from any environment over a transport you inject.
45
61
 
46
62
  ```ts
47
63
  import { createCDPClient } from '@orkestrel/browser'
@@ -52,20 +68,23 @@ const result = await client.send('Page.navigate', { url: 'https://example.com' }
52
68
  await client.close()
53
69
  ```
54
70
 
71
+ ## Browse binary
72
+
73
+ The package ships the `browse` binary, which serves the browser vocabulary and the journey tools over MCP on stdio and launches Chromium on the first tool call, keeping journeys and runs under `tmp/browsers`. Register it for a checkout with `claude mcp add --scope project browse -- node node_modules/@orkestrel/browser/dist/bin/main.js`, which writes the `browse` entry of the project's `.mcp.json`, then start `claude` in the checkout and approve the server; `BROWSE_ROOT`, `BROWSE_HEADLESS`, `BROWSE_EXECUTABLE`, and `BROWSE_READONLY` configure it. See [Register the browse binary with Claude Code](guides/browser.md#register-the-browse-binary-with-claude-code) for the entry, the approval, and a recorded exchange.
74
+
55
75
  ## Guide
56
76
 
57
- For the full surface — the CDP dispatch core, the `BrowserContext`,
58
- `BrowserPage`, and `BrowserCodegen` classes, the server transports, and usage
59
- patterns — see [`guides/browser.md`](guides/browser.md).
77
+ For the full surface of the three faces, the method tables, the toolset vocabulary, the journeys, the relation to WebMCP, and the contract, see [`guides/browser.md`](guides/browser.md).
60
78
 
61
79
  ## Package
62
80
 
63
- Published with the entry points the `exports` field in `package.json` names:
64
- the environment-agnostic core (`.`) — `CDPClient`, `BrowserContext`,
65
- `BrowserPage`, `BrowserFrame`, DOM snapshot traversal helpers,
66
- `BrowserCodegen`, `createCDPClient`, `CDPTransportInterface` —
67
- and the Node-only server surface (`./server`) — `Browser`, `createBrowser`,
68
- `createCDPTransport`, `createBrowserWriter`, `WebSocketCDPTransport`.
81
+ The `exports` field in `package.json` names three entry points:
82
+
83
+ - `.`, the environment-agnostic core: `CDPClient`, `BrowserContext`, `BrowserPage`, `BrowserFrame`, `BrowserNavigationManager`, the element managers, `BrowserPageElement`, `BrowserReading`, `BrowserToolset`, `BrowserSnapshot`, `BrowserCodegen`, `BrowserRecorder`, `BrowserReplay`, `BrowserJourneyToolset`, the memory journey and run stores, `compileBrowserJourney`, and the factories `createCDPClient`, `createBrowserReading`, `createBrowserSnapshot`, `createBrowserToolset`, `createBrowserRecorder`, `createBrowserReplay`, `createMemoryBrowserJourneyStore`, and `createMemoryBrowserRunStore`;
84
+ - `./browser`, the in-page face: `BrowserDOMView`, `BrowserDOMElement`, `BrowserDOMElementManager`, `SocketCDPTransport`, and the factories `createBrowserDOMView`, `createDocumentToolset`, and `createSocketCDPTransport`;
85
+ - `./server`, the Node runtime: `Browser`, `WebSocketCDPTransport`, `FileBrowserWriter`, the file journey and run stores, `BrowserMCPServer`, and the factories `createBrowser`, `createCDPTransport`, `createBrowserWriter`, `createFileBrowserJourneyStore`, `createFileBrowserRunStore`, and `createBrowserMCPServer`.
86
+
87
+ The `bin` field names `browse`, which runs `dist/bin/main.js`.
69
88
 
70
89
  ## License
71
90
 
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ import { parseBoolean } from "@orkestrel/contract";
3
+ import { BrowserError, isBrowserError } from "../src/core/index.js";
4
+ import { createBrowserMCPServer } from "../src/server/index.js";
5
+ //#region src/bin/main.ts
6
+ try {
7
+ const { BROWSE_ROOT, BROWSE_HEADLESS, BROWSE_EXECUTABLE, BROWSE_READONLY } = process.env;
8
+ const headless = parseBoolean(BROWSE_HEADLESS);
9
+ const readonly = parseBoolean(BROWSE_READONLY);
10
+ for (const [name, value, parsed] of [[
11
+ "BROWSE_HEADLESS",
12
+ BROWSE_HEADLESS,
13
+ headless
14
+ ], [
15
+ "BROWSE_READONLY",
16
+ BROWSE_READONLY,
17
+ readonly
18
+ ]]) if (value !== void 0 && value !== "" && parsed === void 0) throw new BrowserError(`${name} must be true, false, 1, or 0, not ${JSON.stringify(value)}`, "BROWSER_SERVER_ENVIRONMENT", {
19
+ name,
20
+ value
21
+ });
22
+ await createBrowserMCPServer({
23
+ ...BROWSE_ROOT === void 0 || BROWSE_ROOT === "" ? {} : { root: BROWSE_ROOT },
24
+ ...headless === void 0 ? {} : { headless },
25
+ ...BROWSE_EXECUTABLE === void 0 || BROWSE_EXECUTABLE === "" ? {} : { executable: BROWSE_EXECUTABLE },
26
+ ...readonly === void 0 ? {} : { readonly }
27
+ }).start();
28
+ } catch (error) {
29
+ if (!isBrowserError(error)) throw error;
30
+ console.error(`browse: ${error.code}: ${error.message.split(/\r\n|\n/u).join(" ")}`);
31
+ process.exitCode = 1;
32
+ }
33
+ //#endregion
34
+
35
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","names":[],"sources":["../../src/bin/main.ts"],"sourcesContent":["import { parseBoolean } from '@orkestrel/contract'\nimport { BrowserError, isBrowserError } from '@src/core'\nimport { createBrowserMCPServer } from '@src/server'\n\ntry {\n\tconst { BROWSE_ROOT, BROWSE_HEADLESS, BROWSE_EXECUTABLE, BROWSE_READONLY } = process.env\n\tconst headless = parseBoolean(BROWSE_HEADLESS)\n\tconst readonly = parseBoolean(BROWSE_READONLY)\n\t// An empty variable counts as unset, as a shell's `NAME=` leaves it.\n\tfor (const [name, value, parsed] of [\n\t\t['BROWSE_HEADLESS', BROWSE_HEADLESS, headless],\n\t\t['BROWSE_READONLY', BROWSE_READONLY, readonly],\n\t] as const) {\n\t\tif (value !== undefined && value !== '' && parsed === undefined) {\n\t\t\tthrow new BrowserError(\n\t\t\t\t`${name} must be true, false, 1, or 0, not ${JSON.stringify(value)}`,\n\t\t\t\t'BROWSER_SERVER_ENVIRONMENT',\n\t\t\t\t{ name, value },\n\t\t\t)\n\t\t}\n\t}\n\tawait createBrowserMCPServer({\n\t\t...(BROWSE_ROOT === undefined || BROWSE_ROOT === '' ? {} : { root: BROWSE_ROOT }),\n\t\t...(headless === undefined ? {} : { headless }),\n\t\t...(BROWSE_EXECUTABLE === undefined || BROWSE_EXECUTABLE === ''\n\t\t\t? {}\n\t\t\t: { executable: BROWSE_EXECUTABLE }),\n\t\t...(readonly === undefined ? {} : { readonly }),\n\t}).start()\n} catch (error) {\n\tif (!isBrowserError(error)) throw error\n\tconsole.error(`browse: ${error.code}: ${error.message.split(/\\r\\n|\\n/u).join(' ')}`)\n\tprocess.exitCode = 1\n}\n"],"mappings":";;;;;AAIA,IAAI;CACH,MAAM,EAAE,aAAa,iBAAiB,mBAAmB,oBAAoB,QAAQ;CACrF,MAAM,WAAW,aAAa,eAAe;CAC7C,MAAM,WAAW,aAAa,eAAe;CAE7C,KAAK,MAAM,CAAC,MAAM,OAAO,WAAW,CACnC;EAAC;EAAmB;EAAiB;CAAQ,GAC7C;EAAC;EAAmB;EAAiB;CAAQ,CAC9C,GACC,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM,WAAW,KAAA,GACrD,MAAM,IAAI,aACT,GAAG,KAAK,qCAAqC,KAAK,UAAU,KAAK,KACjE,8BACA;EAAE;EAAM;CAAM,CACf;CAGF,MAAM,uBAAuB;EAC5B,GAAI,gBAAgB,KAAA,KAAa,gBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,YAAY;EAC/E,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;EAC7C,GAAI,sBAAsB,KAAA,KAAa,sBAAsB,KAC1D,CAAC,IACD,EAAE,YAAY,kBAAkB;EACnC,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;CAC9C,CAAC,CAAC,CAAC,MAAM;AACV,SAAS,OAAO;CACf,IAAI,CAAC,eAAe,KAAK,GAAG,MAAM;CAClC,QAAQ,MAAM,WAAW,MAAM,KAAK,IAAI,MAAM,QAAQ,MAAM,UAAU,CAAC,CAAC,KAAK,GAAG,GAAG;CACnF,QAAQ,WAAW;AACpB"}