@orkestrel/browser 0.0.22 → 0.0.23

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
@@ -73,7 +73,7 @@ await client.close()
73
73
 
74
74
  ## Browse binary
75
75
 
76
- 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.
76
+ The package ships the `browse` binary, which serves the browser vocabulary and the journey tools over MCP on stdio and starts Chromium when the server starts, before the client's first request, 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`, `BROWSE_READONLY`, and `BROWSE_POOL` 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, the startup and recovery behavior, and a recorded exchange.
77
77
 
78
78
  ## Guide
79
79
 
package/dist/bin/main.js CHANGED
@@ -1,12 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { parseBoolean } from "@orkestrel/contract";
2
+ import { parseBoolean, parseInteger } from "@orkestrel/contract";
3
3
  import { BrowserError, isBrowserError } from "../src/core/index.js";
4
4
  import { createBrowserMCPServer } from "../src/server/index.js";
5
5
  //#region src/bin/main.ts
6
+ process.stderr.on("error", () => {
7
+ process.exitCode = 1;
8
+ });
6
9
  try {
7
- const { BROWSE_ROOT, BROWSE_HEADLESS, BROWSE_EXECUTABLE, BROWSE_READONLY } = process.env;
10
+ const { BROWSE_ROOT, BROWSE_HEADLESS, BROWSE_EXECUTABLE, BROWSE_READONLY, BROWSE_POOL } = process.env;
8
11
  const headless = parseBoolean(BROWSE_HEADLESS);
9
12
  const readonly = parseBoolean(BROWSE_READONLY);
13
+ const size = parseInteger(BROWSE_POOL);
10
14
  for (const [name, value, parsed] of [[
11
15
  "BROWSE_HEADLESS",
12
16
  BROWSE_HEADLESS,
@@ -19,11 +23,16 @@ try {
19
23
  name,
20
24
  value
21
25
  });
26
+ if (BROWSE_POOL !== void 0 && BROWSE_POOL !== "" && size === void 0) throw new BrowserError(`BROWSE_POOL must be an integer, not ${JSON.stringify(BROWSE_POOL)}`, "BROWSER_SERVER_ENVIRONMENT", {
27
+ name: "BROWSE_POOL",
28
+ value: BROWSE_POOL
29
+ });
22
30
  await createBrowserMCPServer({
23
31
  ...BROWSE_ROOT === void 0 || BROWSE_ROOT === "" ? {} : { root: BROWSE_ROOT },
24
32
  ...headless === void 0 ? {} : { headless },
25
33
  ...BROWSE_EXECUTABLE === void 0 || BROWSE_EXECUTABLE === "" ? {} : { executable: BROWSE_EXECUTABLE },
26
- ...readonly === void 0 ? {} : { readonly }
34
+ ...readonly === void 0 ? {} : { readonly },
35
+ ...size === void 0 ? {} : { pool: { size } }
27
36
  }).start();
28
37
  } catch (error) {
29
38
  if (!isBrowserError(error)) throw error;
@@ -1 +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"}
1
+ {"version":3,"file":"main.js","names":[],"sources":["../../src/bin/main.ts"],"sourcesContent":["import { parseBoolean, parseInteger } from '@orkestrel/contract'\nimport { BrowserError, isBrowserError } from '@src/core'\nimport { createBrowserMCPServer } from '@src/server'\n\n// The entry owns stderr; a closed reader must not interrupt the server's teardown barrier.\nprocess.stderr.on('error', () => {\n\tprocess.exitCode = 1\n})\n\ntry {\n\tconst { BROWSE_ROOT, BROWSE_HEADLESS, BROWSE_EXECUTABLE, BROWSE_READONLY, BROWSE_POOL } =\n\t\tprocess.env\n\tconst headless = parseBoolean(BROWSE_HEADLESS)\n\tconst readonly = parseBoolean(BROWSE_READONLY)\n\tconst size = parseInteger(BROWSE_POOL)\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\tif (BROWSE_POOL !== undefined && BROWSE_POOL !== '' && size === undefined)\n\t\tthrow new BrowserError(\n\t\t\t`BROWSE_POOL must be an integer, not ${JSON.stringify(BROWSE_POOL)}`,\n\t\t\t'BROWSER_SERVER_ENVIRONMENT',\n\t\t\t{ name: 'BROWSE_POOL', value: BROWSE_POOL },\n\t\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\t...(size === undefined ? {} : { pool: { size } }),\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":";;;;;AAKA,QAAQ,OAAO,GAAG,eAAe;CAChC,QAAQ,WAAW;AACpB,CAAC;AAED,IAAI;CACH,MAAM,EAAE,aAAa,iBAAiB,mBAAmB,iBAAiB,gBACzE,QAAQ;CACT,MAAM,WAAW,aAAa,eAAe;CAC7C,MAAM,WAAW,aAAa,eAAe;CAC7C,MAAM,OAAO,aAAa,WAAW;CAErC,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,IAAI,gBAAgB,KAAA,KAAa,gBAAgB,MAAM,SAAS,KAAA,GAC/D,MAAM,IAAI,aACT,uCAAuC,KAAK,UAAU,WAAW,KACjE,8BACA;EAAE,MAAM;EAAe,OAAO;CAAY,CAC3C;CACD,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;EAC7C,GAAI,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE;CAChD,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"}
@@ -163,7 +163,7 @@ var BROWSER_MOUSE_BUTTON_MASKS = Object.freeze({
163
163
  */
164
164
  var BROWSER_HAR_CREATOR = Object.freeze({
165
165
  name: "@orkestrel/browser",
166
- version: "0.0.22"
166
+ version: "0.0.23"
167
167
  });
168
168
  /** Names the attribute that tags temporary screenshot styles and masks. */
169
169
  var BROWSER_SCREENSHOT_ATTRIBUTE = "data-orkestrel-screenshot";
@@ -11184,6 +11184,7 @@ var BrowserContext = class {
11184
11184
  #viewport;
11185
11185
  #writer;
11186
11186
  #downloads;
11187
+ #allocator;
11187
11188
  #emitter;
11188
11189
  #cookies;
11189
11190
  #permissions;
@@ -11203,6 +11204,7 @@ var BrowserContext = class {
11203
11204
  this.#viewport = viewport;
11204
11205
  this.#writer = writer;
11205
11206
  this.#downloads = downloads;
11207
+ this.#allocator = options?.reference;
11206
11208
  this.#emitter = new _orkestrel_emitter.Emitter({
11207
11209
  ...options?.on !== void 0 ? { on: options.on } : {},
11208
11210
  ...options?.error !== void 0 ? { error: options.error } : {}
@@ -11339,7 +11341,7 @@ var BrowserContext = class {
11339
11341
  sessionId = await this.#openSession(targetId);
11340
11342
  await this.#enableSession(sessionId);
11341
11343
  const frameId = await this.#mainFrame(sessionId);
11342
- page = new BrowserPage(this.#client, targetId, sessionId, this.#writer, url, frameId, this.#id, void 0, options, this.#nextReference.bind(this), ready);
11344
+ page = new BrowserPage(this.#client, targetId, sessionId, this.#writer, url, frameId, this.#id, void 0, options, this.#allocator ?? this.#nextReference.bind(this), ready);
11343
11345
  this.#observe(page);
11344
11346
  await this.#configurePage(page);
11345
11347
  await this.#emulation.attach(page);
@@ -11358,7 +11360,7 @@ var BrowserContext = class {
11358
11360
  sessionId = await this.#openSession(targetId);
11359
11361
  await this.#enableSession(sessionId);
11360
11362
  const frameId = await this.#mainFrame(sessionId);
11361
- page = new BrowserPage(this.#client, targetId, sessionId, this.#writer, url, frameId, this.#id, void 0, void 0, this.#nextReference.bind(this), ready);
11363
+ page = new BrowserPage(this.#client, targetId, sessionId, this.#writer, url, frameId, this.#id, void 0, void 0, this.#allocator ?? this.#nextReference.bind(this), ready);
11362
11364
  this.#observe(page);
11363
11365
  await this.#configurePage(page);
11364
11366
  await this.#emulation.attach(page);