@orkestrel/browser 0.0.17 → 0.0.19
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 +47 -28
- package/dist/bin/main.js +35 -0
- package/dist/bin/main.js.map +1 -0
- package/dist/src/browser/index.d.ts +841 -0
- package/dist/src/browser/index.js +1640 -0
- package/dist/src/browser/index.js.map +1 -0
- package/dist/src/core/index.cjs +8372 -2494
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +4410 -2110
- package/dist/src/core/index.d.ts +4410 -2110
- package/dist/src/core/index.js +8256 -2473
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +1013 -92
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +288 -39
- package/dist/src/server/index.d.ts +288 -39
- package/dist/src/server/index.js +975 -69
- package/dist/src/server/index.js.map +1 -1
- package/package.json +42 -19
package/README.md
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
# @orkestrel/browser
|
|
2
2
|
|
|
3
|
-
> A
|
|
4
|
-
>
|
|
5
|
-
>
|
|
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
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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.
|
|
35
|
-
const
|
|
36
|
-
await
|
|
37
|
-
const
|
|
38
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
`BrowserPage`, `BrowserFrame`,
|
|
66
|
-
`
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
package/dist/bin/main.js
ADDED
|
@@ -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"}
|