@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.
@@ -1,3 +1,4 @@
1
+ import type { BrowserCallOptions } from '@orkestrel/browser';
1
2
  import type { BrowserContextInterface } from '@orkestrel/browser';
2
3
  import type { BrowserContextOptions } from '@orkestrel/browser';
3
4
  import { BrowserError } from '@orkestrel/browser';
@@ -11,6 +12,7 @@ import type { BrowserRunSlot } from '@orkestrel/browser';
11
12
  import type { BrowserRunStoreInterface } from '@orkestrel/browser';
12
13
  import type { BrowserStoreOptions } from '@orkestrel/browser';
13
14
  import type { BrowserStorePage } from '@orkestrel/browser';
15
+ import type { BrowserToolsetInterface } from '@orkestrel/browser';
14
16
  import type { BrowserViewport } from '@orkestrel/browser';
15
17
  import type { BrowserWriterInterface } from '@orkestrel/browser';
16
18
  import type { CDPTarget } from '@orkestrel/browser';
@@ -46,6 +48,8 @@ export declare class Browser implements BrowserInterface {
46
48
  get connection(): BrowserConnection | undefined;
47
49
  get owned(): boolean | undefined;
48
50
  get pid(): number | undefined;
51
+ get endpoint(): string | undefined;
52
+ ping(options?: BrowserCallOptions): Promise<void>;
49
53
  discover(): Promise<BrowserDiscoveryResult>;
50
54
  connect(): Promise<void>;
51
55
  adopt(): void;
@@ -181,6 +185,42 @@ export declare const BROWSER_RUN_DIRECTORY = "runs";
181
185
  /** Names the persisted run snapshot. */
182
186
  export declare const BROWSER_RUN_FILE = "run.json";
183
187
 
188
+ /** Names the notice that a browser and its session state were lost. */
189
+ export declare const BROWSER_SERVER_CRASH = "BROWSER_SERVER_CRASH";
190
+
191
+ /** Names the diagnostic when the warm floor spends its restart bound. */
192
+ export declare const BROWSER_SERVER_EXHAUSTED = "BROWSER_SERVER_EXHAUSTED";
193
+
194
+ /** Names a failed attempt to warm a browser. */
195
+ export declare const BROWSER_SERVER_LAUNCH = "BROWSER_SERVER_LAUNCH";
196
+
197
+ /** Names a refused browser server option. */
198
+ export declare const BROWSER_SERVER_OPTIONS = "BROWSER_SERVER_OPTIONS";
199
+
200
+ /** Limits the number of warm browsers to 3. */
201
+ export declare const BROWSER_SERVER_POOL_LIMIT = 3;
202
+
203
+ /** Sets the default number of warm browsers to 1. */
204
+ export declare const BROWSER_SERVER_POOL_SIZE = 1;
205
+
206
+ /** Names the browser process record in each profile. */
207
+ export declare const BROWSER_SERVER_RECORD = "browse.json";
208
+
209
+ /** Permits one failed refill before the next failure spends the bound. */
210
+ export declare const BROWSER_SERVER_RESTARTS = 1;
211
+
212
+ /** Names a failed profile sweep. */
213
+ export declare const BROWSER_SERVER_SWEEP = "BROWSER_SERVER_SWEEP";
214
+
215
+ /** Names a failed browser server teardown. */
216
+ export declare const BROWSER_SERVER_TEARDOWN = "BROWSER_SERVER_TEARDOWN";
217
+
218
+ /** Names the refusal when no browser can serve a call. */
219
+ export declare const BROWSER_SERVER_UNAVAILABLE = "BROWSER_SERVER_UNAVAILABLE";
220
+
221
+ /** Names an interrupted call whose outcome is unknown. */
222
+ export declare const BROWSER_SERVER_UNRESOLVED = "BROWSER_SERVER_UNRESOLVED";
223
+
184
224
  /** Names the per-OS default Playwright browser cache directory, relative to the home directory (win32 uses `LOCALAPPDATA` directly). */
185
225
  export declare const BROWSER_STORE_CACHE_DIRS: Readonly<Record<string, string>>;
186
226
 
@@ -237,7 +277,7 @@ export declare const BROWSER_WINDOWS_SUFFIXES: readonly string[];
237
277
  * - `endpoint` — explicit CDP WebSocket URL; when provided, skips discovery
238
278
  * - `discover` — whether `connect()` passively probes for an existing browser
239
279
  * before launching (default `true`); set `false` to skip discovery and go
240
- * straight to launch — a short probe of `port` still runs first and rejects
280
+ * straight to launch — when `port` is explicit, a short probe runs first and rejects
241
281
  * with a coded error naming the occupied port if something is already
242
282
  * listening there, so a demanded fresh launch never silently attaches to a
243
283
  * stranger browser
@@ -355,6 +395,10 @@ export declare type BrowserEventMap = {
355
395
  * - `create(options?)` → shortcut to open a page in the default context
356
396
  */
357
397
  export declare interface BrowserInterface {
398
+ /** Reports the CDP WebSocket endpoint of the represented session, or undefined when none is. */
399
+ readonly endpoint: string | undefined;
400
+ /** Sends CDP `Browser.getVersion` and resolves when the browser answers, changing no state. */
401
+ ping(options?: BrowserCallOptions): Promise<void>;
358
402
  readonly emitter: EmitterInterface<BrowserEventMap>;
359
403
  readonly engine: BrowserEngine;
360
404
  readonly status: BrowserStatus;
@@ -435,7 +479,7 @@ export declare interface BrowserInterface {
435
479
  }
436
480
 
437
481
  /**
438
- * Creates the browser a browse server connects on its first tool call.
482
+ * Creates a browser a browse server connects while warming its pool.
439
483
  *
440
484
  * @param options - The launch the server composes: `headless`, `executable`, the profile it
441
485
  * owns, `cdp.discover` set to `false`, and the signal its `destroy()` aborts
@@ -445,7 +489,7 @@ export declare type BrowserLaunchFunction = (options: BrowserOptions) => Browser
445
489
 
446
490
  /**
447
491
  * Implements `BrowserMCPServerInterface`: serves the browser vocabulary and the journey tools over
448
- * the Model Context Protocol on stdio, and launches Chromium on the first tool call.
492
+ * the Model Context Protocol on stdio, and warms Chromium at server start.
449
493
  *
450
494
  * @remarks
451
495
  * The server's own manager holds one dispatcher per name of the vocabulary — `look`, `read`, `plain`,
@@ -455,18 +499,22 @@ export declare type BrowserLaunchFunction = (options: BrowserOptions) => Browser
455
499
  * absent. After the launch, a tool the toolset's manager adds under another name, such as a page
456
500
  * tool it adopts, is mirrored as a dispatcher with that tool's definition, and the mirror is
457
501
  * removed when the toolset withdraws the tool, so the server's tool list changes and a subscribed
458
- * client is notified. The first call of any dispatcher launches Chromium one time: concurrent callers await
459
- * the same launch, a failed launch rejects every one of them, and the next call launches again.
460
- * A launch creates `ROOT/.profiles/ID/` exclusively, connects a browser that never attaches to an
502
+ * client is notified. Startup warms the pool and leases one validated browser. The legacy
503
+ * handshake and every tool call await that setup; concurrent callers share a replacement lease.
504
+ * A launch creates `ROOT/.profiles/PID-UUID/` exclusively, connects a browser that never attaches to an
461
505
  * existing endpoint, opens one page in an isolated context, and constructs the page toolset with
462
506
  * that context and the file journey and run stores under the root, on a manager the toolset owns.
463
507
  * A dispatcher forwards its arguments and the call's context, signal included, to the toolset's
464
508
  * manager and returns that manager's value, or rejects with its failure message; a text value
465
509
  * reaches the client as one text block rather than as its JSON text.
510
+ * A disconnect or current-page crash retires its slot. A failed call receives a liveness ping;
511
+ * loss makes its outcome unresolved, while a known success remains successful. Pending losses
512
+ * prefix the next outcome on a successor, or a refusal, and calls are never repeated.
466
513
  *
467
514
  * `start()` serves stdio and destroys the server at the end of its input, on `SIGINT`, and on
468
- * `SIGTERM`. `destroy()` stops reading requests and removes every dispatcher, then destroys the
469
- * toolset, which aborts the active replay, then the browser it launched, then removes the profile.
515
+ * `SIGTERM`. `destroy()` stops reading requests and removes every dispatcher, then destroys every
516
+ * slot through the pool — its toolset, which aborts the active replay, then its browser, then its
517
+ * profile — and rechecks the profile folders this server made or kept.
470
518
  *
471
519
  * Chromium on Linux refuses to start as the root user unless its sandbox is disabled, so a server
472
520
  * running as root on Linux launches Chromium with `--no-sandbox`; a launch that could run
@@ -496,13 +544,14 @@ export declare class BrowserMCPServer implements BrowserMCPServerInterface {
496
544
  /** Serves the browser vocabulary and the journey tools over MCP on stdio. */
497
545
  export declare interface BrowserMCPServerInterface {
498
546
  /**
499
- * Serves the tool list over stdio before Chromium starts; the first tool call launches Chromium
500
- * with a profile the server owns.
547
+ * Serves stdio, sweeps the profiles ended servers left, warms the pool, and resolves after the
548
+ * session leases a warm browser; the legacy handshake and every tool call await the same setup.
549
+ * Rejects with `BROWSER_SERVER_UNAVAILABLE` when no browser can serve and with
550
+ * `BROWSER_TOOLSET_ENDED` after `destroy()`, and resolves when `destroy()` interrupts setup.
501
551
  */
502
552
  start(): Promise<void>;
503
553
  /**
504
- * Stops admission, aborts the active replay, destroys the toolset and the browser it launched,
505
- * and removes its profile.
554
+ * Stops admission, tears down every browser, its toolset, and its profile, then rechecks the folders it answers for.
506
555
  */
507
556
  destroy(): Promise<void>;
508
557
  }
@@ -517,11 +566,15 @@ export declare interface BrowserMCPServerInterface {
517
566
  * `true`
518
567
  * - `executable` — the path of the Chromium executable the server launches. Default: the browser
519
568
  * `findSystemBrowser` finds
520
- * - `readonly` — if `true`, refuses `record`, `save`, and `edit`, and `replay` still writes runs;
569
+ * - `readonly` — if `true`, refuses `record`, `save`, `edit`, and `forget`, and `replay` still writes runs;
521
570
  * if `false` or omitted, every tool runs
522
- * - `launch` — creates the browser the first tool call connects. Default: `createBrowser`
571
+ * - `launch` — creates each browser the pool warms. Default: `createBrowser`
523
572
  * - `stdio` — the streams the server reads requests from and writes answers to; the end of
524
573
  * `input` destroys the server. Default: `process.stdin` and `process.stdout`
574
+ * - `pool.size` — the integer number of browsers kept warm, from 1 through
575
+ * `BROWSER_SERVER_POOL_LIMIT`. Default: `BROWSER_SERVER_POOL_SIZE`.
576
+ * - `log` — receives diagnostic lines. Default: `process.stderr`
577
+ * @throws Thrown when `pool.size` is outside that range or is not an integer, with `BROWSER_SERVER_OPTIONS`
525
578
  */
526
579
  export declare interface BrowserMCPServerOptions {
527
580
  readonly root?: string;
@@ -530,6 +583,10 @@ export declare interface BrowserMCPServerOptions {
530
583
  readonly readonly?: boolean;
531
584
  readonly launch?: BrowserLaunchFunction;
532
585
  readonly stdio?: StdioServerOptions;
586
+ readonly pool?: {
587
+ readonly size?: number;
588
+ };
589
+ readonly log?: NodeJS.WritableStream;
533
590
  }
534
591
 
535
592
  /**
@@ -577,6 +634,12 @@ export declare interface BrowserOptions {
577
634
  readonly browsers?: SystemBrowserOptions;
578
635
  }
579
636
 
637
+ /** Names the browser a browse profile serves, which a later start's sweep reads. */
638
+ export declare interface BrowserProfileRecord {
639
+ readonly pid: number;
640
+ readonly endpoint: string;
641
+ }
642
+
580
643
  /**
581
644
  * Describes the resolved browser profile directory used for a Chromium-family launch.
582
645
  *
@@ -590,6 +653,23 @@ export declare interface BrowserProfileResult {
590
653
  readonly temporary: boolean;
591
654
  }
592
655
 
656
+ /** Holds one warm browser a browse server owns: the browser, its profile, its isolated context, and its started toolset. */
657
+ export declare interface BrowserSlot {
658
+ readonly browser: BrowserInterface;
659
+ readonly profile: string;
660
+ readonly context: BrowserContextInterface;
661
+ readonly toolset: BrowserToolsetInterface;
662
+ }
663
+
664
+ /** Holds the listeners and resolver of one browser slot's loss watch. */
665
+ export declare interface BrowserSlotWatch {
666
+ readonly resolve: (cause: unknown) => void;
667
+ readonly disconnect: () => void;
668
+ readonly page: (page: BrowserPageInterface) => void;
669
+ readonly crashes: ReadonlyMap<BrowserPageInterface, () => void>;
670
+ readonly subscription: Disposable;
671
+ }
672
+
593
673
  /** Names the lifecycle status of a browser wrapper. */
594
674
  export declare type BrowserStatus = 'idle' | 'connecting' | 'connected' | 'disconnected' | 'error';
595
675
 
@@ -628,7 +708,7 @@ export declare function createBrowser(options?: BrowserOptions): BrowserInterfac
628
708
 
629
709
  /**
630
710
  * Creates the browse server, which serves the browser vocabulary and the journey tools over the
631
- * Model Context Protocol on stdio and launches Chromium on the first tool call.
711
+ * Model Context Protocol on stdio and warms Chromium at server start.
632
712
  *
633
713
  * @param options - The root, the launch, the journeys' read-only switch, and the streams
634
714
  * @returns A {@link BrowserMCPServerInterface} that reads nothing until `start()`
@@ -638,7 +718,7 @@ export declare function createBrowser(options?: BrowserOptions): BrowserInterfac
638
718
  * import { createBrowserMCPServer } from '@orkestrel/browser/server'
639
719
  *
640
720
  * const server = createBrowserMCPServer({ root: 'tmp/browsers', headless: true })
641
- * await server.start() // answers tools/list before Chromium starts
721
+ * await server.start() // resolves after leasing a connected browser
642
722
  * await server.destroy()
643
723
  * ```
644
724
  */
@@ -686,6 +766,17 @@ export declare function createFileBrowserJourneyStore(options: FileBrowserStoreO
686
766
  */
687
767
  export declare function createFileBrowserRunStore(options: FileBrowserStoreOptions): BrowserRunStoreInterface;
688
768
 
769
+ /**
770
+ * Describes a browser loss with its code, cause, and the session state it invalidates.
771
+ * @param code - Browser server diagnostic code
772
+ * @param cause - The loss or refusal, including its process context when present
773
+ * @param url - The lost page's last URL, when known
774
+ * @returns The coded diagnostic and any operation-lifecycle guidance
775
+ * @example
776
+ * describeBrowserServerLoss('BROWSER_SERVER_UNAVAILABLE', new Error('Browser exited'))
777
+ */
778
+ export declare function describeBrowserServerLoss(code: string, cause: unknown, url?: string): string;
779
+
689
780
  /**
690
781
  * Fetches the current CDP target list from a browser's `/json/list` endpoint, as a `Result`
691
782
  * carrying either the targets or a coded `BrowserConnectionError`.
@@ -903,9 +994,27 @@ export declare function parseBrowserEngine(executable: string): BrowserEngine |
903
994
  */
904
995
  export declare function parseBrowserLockEntry(name: string): number | undefined;
905
996
 
997
+ /**
998
+ * Parses a profile record naming a positive process identifier and a loopback DevTools endpoint.
999
+ * @param text - JSON record read from a browse profile
1000
+ * @returns The record, or undefined when its shape or endpoint is refused
1001
+ * @example
1002
+ * parseBrowserProfileRecord('{"pid":123,"endpoint":"ws://127.0.0.1:9222/devtools/browser/session"}')
1003
+ */
1004
+ export declare function parseBrowserProfileRecord(text: string): BrowserProfileRecord | undefined;
1005
+
906
1006
  /** Probes PATH (`which`/`where`) for every resolvable command name, in the given order. */
907
1007
  export declare function probePathNames(names: readonly string[], platform: string): readonly string[];
908
1008
 
1009
+ /**
1010
+ * Probes whether a process has not been confirmed absent.
1011
+ * @param pid - Process identifier to probe with signal zero
1012
+ * @returns True if the host has not confirmed absence with ESRCH; false otherwise
1013
+ * @example
1014
+ * probeProcess(process.pid) // true
1015
+ */
1016
+ export declare function probeProcess(pid: number): boolean;
1017
+
909
1018
  /**
910
1019
  * Reads the CDP endpoint a launched browser announces on its standard error.
911
1020
  *
@@ -1,3 +1,4 @@
1
+ import type { BrowserCallOptions } from '@orkestrel/browser';
1
2
  import type { BrowserContextInterface } from '@orkestrel/browser';
2
3
  import type { BrowserContextOptions } from '@orkestrel/browser';
3
4
  import { BrowserError } from '@orkestrel/browser';
@@ -11,6 +12,7 @@ import type { BrowserRunSlot } from '@orkestrel/browser';
11
12
  import type { BrowserRunStoreInterface } from '@orkestrel/browser';
12
13
  import type { BrowserStoreOptions } from '@orkestrel/browser';
13
14
  import type { BrowserStorePage } from '@orkestrel/browser';
15
+ import type { BrowserToolsetInterface } from '@orkestrel/browser';
14
16
  import type { BrowserViewport } from '@orkestrel/browser';
15
17
  import type { BrowserWriterInterface } from '@orkestrel/browser';
16
18
  import type { CDPTarget } from '@orkestrel/browser';
@@ -46,6 +48,8 @@ export declare class Browser implements BrowserInterface {
46
48
  get connection(): BrowserConnection | undefined;
47
49
  get owned(): boolean | undefined;
48
50
  get pid(): number | undefined;
51
+ get endpoint(): string | undefined;
52
+ ping(options?: BrowserCallOptions): Promise<void>;
49
53
  discover(): Promise<BrowserDiscoveryResult>;
50
54
  connect(): Promise<void>;
51
55
  adopt(): void;
@@ -181,6 +185,42 @@ export declare const BROWSER_RUN_DIRECTORY = "runs";
181
185
  /** Names the persisted run snapshot. */
182
186
  export declare const BROWSER_RUN_FILE = "run.json";
183
187
 
188
+ /** Names the notice that a browser and its session state were lost. */
189
+ export declare const BROWSER_SERVER_CRASH = "BROWSER_SERVER_CRASH";
190
+
191
+ /** Names the diagnostic when the warm floor spends its restart bound. */
192
+ export declare const BROWSER_SERVER_EXHAUSTED = "BROWSER_SERVER_EXHAUSTED";
193
+
194
+ /** Names a failed attempt to warm a browser. */
195
+ export declare const BROWSER_SERVER_LAUNCH = "BROWSER_SERVER_LAUNCH";
196
+
197
+ /** Names a refused browser server option. */
198
+ export declare const BROWSER_SERVER_OPTIONS = "BROWSER_SERVER_OPTIONS";
199
+
200
+ /** Limits the number of warm browsers to 3. */
201
+ export declare const BROWSER_SERVER_POOL_LIMIT = 3;
202
+
203
+ /** Sets the default number of warm browsers to 1. */
204
+ export declare const BROWSER_SERVER_POOL_SIZE = 1;
205
+
206
+ /** Names the browser process record in each profile. */
207
+ export declare const BROWSER_SERVER_RECORD = "browse.json";
208
+
209
+ /** Permits one failed refill before the next failure spends the bound. */
210
+ export declare const BROWSER_SERVER_RESTARTS = 1;
211
+
212
+ /** Names a failed profile sweep. */
213
+ export declare const BROWSER_SERVER_SWEEP = "BROWSER_SERVER_SWEEP";
214
+
215
+ /** Names a failed browser server teardown. */
216
+ export declare const BROWSER_SERVER_TEARDOWN = "BROWSER_SERVER_TEARDOWN";
217
+
218
+ /** Names the refusal when no browser can serve a call. */
219
+ export declare const BROWSER_SERVER_UNAVAILABLE = "BROWSER_SERVER_UNAVAILABLE";
220
+
221
+ /** Names an interrupted call whose outcome is unknown. */
222
+ export declare const BROWSER_SERVER_UNRESOLVED = "BROWSER_SERVER_UNRESOLVED";
223
+
184
224
  /** Names the per-OS default Playwright browser cache directory, relative to the home directory (win32 uses `LOCALAPPDATA` directly). */
185
225
  export declare const BROWSER_STORE_CACHE_DIRS: Readonly<Record<string, string>>;
186
226
 
@@ -237,7 +277,7 @@ export declare const BROWSER_WINDOWS_SUFFIXES: readonly string[];
237
277
  * - `endpoint` — explicit CDP WebSocket URL; when provided, skips discovery
238
278
  * - `discover` — whether `connect()` passively probes for an existing browser
239
279
  * before launching (default `true`); set `false` to skip discovery and go
240
- * straight to launch — a short probe of `port` still runs first and rejects
280
+ * straight to launch — when `port` is explicit, a short probe runs first and rejects
241
281
  * with a coded error naming the occupied port if something is already
242
282
  * listening there, so a demanded fresh launch never silently attaches to a
243
283
  * stranger browser
@@ -355,6 +395,10 @@ export declare type BrowserEventMap = {
355
395
  * - `create(options?)` → shortcut to open a page in the default context
356
396
  */
357
397
  export declare interface BrowserInterface {
398
+ /** Reports the CDP WebSocket endpoint of the represented session, or undefined when none is. */
399
+ readonly endpoint: string | undefined;
400
+ /** Sends CDP `Browser.getVersion` and resolves when the browser answers, changing no state. */
401
+ ping(options?: BrowserCallOptions): Promise<void>;
358
402
  readonly emitter: EmitterInterface<BrowserEventMap>;
359
403
  readonly engine: BrowserEngine;
360
404
  readonly status: BrowserStatus;
@@ -435,7 +479,7 @@ export declare interface BrowserInterface {
435
479
  }
436
480
 
437
481
  /**
438
- * Creates the browser a browse server connects on its first tool call.
482
+ * Creates a browser a browse server connects while warming its pool.
439
483
  *
440
484
  * @param options - The launch the server composes: `headless`, `executable`, the profile it
441
485
  * owns, `cdp.discover` set to `false`, and the signal its `destroy()` aborts
@@ -445,7 +489,7 @@ export declare type BrowserLaunchFunction = (options: BrowserOptions) => Browser
445
489
 
446
490
  /**
447
491
  * Implements `BrowserMCPServerInterface`: serves the browser vocabulary and the journey tools over
448
- * the Model Context Protocol on stdio, and launches Chromium on the first tool call.
492
+ * the Model Context Protocol on stdio, and warms Chromium at server start.
449
493
  *
450
494
  * @remarks
451
495
  * The server's own manager holds one dispatcher per name of the vocabulary — `look`, `read`, `plain`,
@@ -455,18 +499,22 @@ export declare type BrowserLaunchFunction = (options: BrowserOptions) => Browser
455
499
  * absent. After the launch, a tool the toolset's manager adds under another name, such as a page
456
500
  * tool it adopts, is mirrored as a dispatcher with that tool's definition, and the mirror is
457
501
  * removed when the toolset withdraws the tool, so the server's tool list changes and a subscribed
458
- * client is notified. The first call of any dispatcher launches Chromium one time: concurrent callers await
459
- * the same launch, a failed launch rejects every one of them, and the next call launches again.
460
- * A launch creates `ROOT/.profiles/ID/` exclusively, connects a browser that never attaches to an
502
+ * client is notified. Startup warms the pool and leases one validated browser. The legacy
503
+ * handshake and every tool call await that setup; concurrent callers share a replacement lease.
504
+ * A launch creates `ROOT/.profiles/PID-UUID/` exclusively, connects a browser that never attaches to an
461
505
  * existing endpoint, opens one page in an isolated context, and constructs the page toolset with
462
506
  * that context and the file journey and run stores under the root, on a manager the toolset owns.
463
507
  * A dispatcher forwards its arguments and the call's context, signal included, to the toolset's
464
508
  * manager and returns that manager's value, or rejects with its failure message; a text value
465
509
  * reaches the client as one text block rather than as its JSON text.
510
+ * A disconnect or current-page crash retires its slot. A failed call receives a liveness ping;
511
+ * loss makes its outcome unresolved, while a known success remains successful. Pending losses
512
+ * prefix the next outcome on a successor, or a refusal, and calls are never repeated.
466
513
  *
467
514
  * `start()` serves stdio and destroys the server at the end of its input, on `SIGINT`, and on
468
- * `SIGTERM`. `destroy()` stops reading requests and removes every dispatcher, then destroys the
469
- * toolset, which aborts the active replay, then the browser it launched, then removes the profile.
515
+ * `SIGTERM`. `destroy()` stops reading requests and removes every dispatcher, then destroys every
516
+ * slot through the pool — its toolset, which aborts the active replay, then its browser, then its
517
+ * profile — and rechecks the profile folders this server made or kept.
470
518
  *
471
519
  * Chromium on Linux refuses to start as the root user unless its sandbox is disabled, so a server
472
520
  * running as root on Linux launches Chromium with `--no-sandbox`; a launch that could run
@@ -496,13 +544,14 @@ export declare class BrowserMCPServer implements BrowserMCPServerInterface {
496
544
  /** Serves the browser vocabulary and the journey tools over MCP on stdio. */
497
545
  export declare interface BrowserMCPServerInterface {
498
546
  /**
499
- * Serves the tool list over stdio before Chromium starts; the first tool call launches Chromium
500
- * with a profile the server owns.
547
+ * Serves stdio, sweeps the profiles ended servers left, warms the pool, and resolves after the
548
+ * session leases a warm browser; the legacy handshake and every tool call await the same setup.
549
+ * Rejects with `BROWSER_SERVER_UNAVAILABLE` when no browser can serve and with
550
+ * `BROWSER_TOOLSET_ENDED` after `destroy()`, and resolves when `destroy()` interrupts setup.
501
551
  */
502
552
  start(): Promise<void>;
503
553
  /**
504
- * Stops admission, aborts the active replay, destroys the toolset and the browser it launched,
505
- * and removes its profile.
554
+ * Stops admission, tears down every browser, its toolset, and its profile, then rechecks the folders it answers for.
506
555
  */
507
556
  destroy(): Promise<void>;
508
557
  }
@@ -517,11 +566,15 @@ export declare interface BrowserMCPServerInterface {
517
566
  * `true`
518
567
  * - `executable` — the path of the Chromium executable the server launches. Default: the browser
519
568
  * `findSystemBrowser` finds
520
- * - `readonly` — if `true`, refuses `record`, `save`, and `edit`, and `replay` still writes runs;
569
+ * - `readonly` — if `true`, refuses `record`, `save`, `edit`, and `forget`, and `replay` still writes runs;
521
570
  * if `false` or omitted, every tool runs
522
- * - `launch` — creates the browser the first tool call connects. Default: `createBrowser`
571
+ * - `launch` — creates each browser the pool warms. Default: `createBrowser`
523
572
  * - `stdio` — the streams the server reads requests from and writes answers to; the end of
524
573
  * `input` destroys the server. Default: `process.stdin` and `process.stdout`
574
+ * - `pool.size` — the integer number of browsers kept warm, from 1 through
575
+ * `BROWSER_SERVER_POOL_LIMIT`. Default: `BROWSER_SERVER_POOL_SIZE`.
576
+ * - `log` — receives diagnostic lines. Default: `process.stderr`
577
+ * @throws Thrown when `pool.size` is outside that range or is not an integer, with `BROWSER_SERVER_OPTIONS`
525
578
  */
526
579
  export declare interface BrowserMCPServerOptions {
527
580
  readonly root?: string;
@@ -530,6 +583,10 @@ export declare interface BrowserMCPServerOptions {
530
583
  readonly readonly?: boolean;
531
584
  readonly launch?: BrowserLaunchFunction;
532
585
  readonly stdio?: StdioServerOptions;
586
+ readonly pool?: {
587
+ readonly size?: number;
588
+ };
589
+ readonly log?: NodeJS.WritableStream;
533
590
  }
534
591
 
535
592
  /**
@@ -577,6 +634,12 @@ export declare interface BrowserOptions {
577
634
  readonly browsers?: SystemBrowserOptions;
578
635
  }
579
636
 
637
+ /** Names the browser a browse profile serves, which a later start's sweep reads. */
638
+ export declare interface BrowserProfileRecord {
639
+ readonly pid: number;
640
+ readonly endpoint: string;
641
+ }
642
+
580
643
  /**
581
644
  * Describes the resolved browser profile directory used for a Chromium-family launch.
582
645
  *
@@ -590,6 +653,23 @@ export declare interface BrowserProfileResult {
590
653
  readonly temporary: boolean;
591
654
  }
592
655
 
656
+ /** Holds one warm browser a browse server owns: the browser, its profile, its isolated context, and its started toolset. */
657
+ export declare interface BrowserSlot {
658
+ readonly browser: BrowserInterface;
659
+ readonly profile: string;
660
+ readonly context: BrowserContextInterface;
661
+ readonly toolset: BrowserToolsetInterface;
662
+ }
663
+
664
+ /** Holds the listeners and resolver of one browser slot's loss watch. */
665
+ export declare interface BrowserSlotWatch {
666
+ readonly resolve: (cause: unknown) => void;
667
+ readonly disconnect: () => void;
668
+ readonly page: (page: BrowserPageInterface) => void;
669
+ readonly crashes: ReadonlyMap<BrowserPageInterface, () => void>;
670
+ readonly subscription: Disposable;
671
+ }
672
+
593
673
  /** Names the lifecycle status of a browser wrapper. */
594
674
  export declare type BrowserStatus = 'idle' | 'connecting' | 'connected' | 'disconnected' | 'error';
595
675
 
@@ -628,7 +708,7 @@ export declare function createBrowser(options?: BrowserOptions): BrowserInterfac
628
708
 
629
709
  /**
630
710
  * Creates the browse server, which serves the browser vocabulary and the journey tools over the
631
- * Model Context Protocol on stdio and launches Chromium on the first tool call.
711
+ * Model Context Protocol on stdio and warms Chromium at server start.
632
712
  *
633
713
  * @param options - The root, the launch, the journeys' read-only switch, and the streams
634
714
  * @returns A {@link BrowserMCPServerInterface} that reads nothing until `start()`
@@ -638,7 +718,7 @@ export declare function createBrowser(options?: BrowserOptions): BrowserInterfac
638
718
  * import { createBrowserMCPServer } from '@orkestrel/browser/server'
639
719
  *
640
720
  * const server = createBrowserMCPServer({ root: 'tmp/browsers', headless: true })
641
- * await server.start() // answers tools/list before Chromium starts
721
+ * await server.start() // resolves after leasing a connected browser
642
722
  * await server.destroy()
643
723
  * ```
644
724
  */
@@ -686,6 +766,17 @@ export declare function createFileBrowserJourneyStore(options: FileBrowserStoreO
686
766
  */
687
767
  export declare function createFileBrowserRunStore(options: FileBrowserStoreOptions): BrowserRunStoreInterface;
688
768
 
769
+ /**
770
+ * Describes a browser loss with its code, cause, and the session state it invalidates.
771
+ * @param code - Browser server diagnostic code
772
+ * @param cause - The loss or refusal, including its process context when present
773
+ * @param url - The lost page's last URL, when known
774
+ * @returns The coded diagnostic and any operation-lifecycle guidance
775
+ * @example
776
+ * describeBrowserServerLoss('BROWSER_SERVER_UNAVAILABLE', new Error('Browser exited'))
777
+ */
778
+ export declare function describeBrowserServerLoss(code: string, cause: unknown, url?: string): string;
779
+
689
780
  /**
690
781
  * Fetches the current CDP target list from a browser's `/json/list` endpoint, as a `Result`
691
782
  * carrying either the targets or a coded `BrowserConnectionError`.
@@ -903,9 +994,27 @@ export declare function parseBrowserEngine(executable: string): BrowserEngine |
903
994
  */
904
995
  export declare function parseBrowserLockEntry(name: string): number | undefined;
905
996
 
997
+ /**
998
+ * Parses a profile record naming a positive process identifier and a loopback DevTools endpoint.
999
+ * @param text - JSON record read from a browse profile
1000
+ * @returns The record, or undefined when its shape or endpoint is refused
1001
+ * @example
1002
+ * parseBrowserProfileRecord('{"pid":123,"endpoint":"ws://127.0.0.1:9222/devtools/browser/session"}')
1003
+ */
1004
+ export declare function parseBrowserProfileRecord(text: string): BrowserProfileRecord | undefined;
1005
+
906
1006
  /** Probes PATH (`which`/`where`) for every resolvable command name, in the given order. */
907
1007
  export declare function probePathNames(names: readonly string[], platform: string): readonly string[];
908
1008
 
1009
+ /**
1010
+ * Probes whether a process has not been confirmed absent.
1011
+ * @param pid - Process identifier to probe with signal zero
1012
+ * @returns True if the host has not confirmed absence with ESRCH; false otherwise
1013
+ * @example
1014
+ * probeProcess(process.pid) // true
1015
+ */
1016
+ export declare function probeProcess(pid: number): boolean;
1017
+
909
1018
  /**
910
1019
  * Reads the CDP endpoint a launched browser announces on its standard error.
911
1020
  *