@btraut/browser-bridge 0.4.0 → 0.4.2
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/CHANGELOG.md +20 -0
- package/README.md +57 -32
- package/dist/api.js +189 -26
- package/dist/api.js.map +3 -3
- package/dist/index.js +222 -1
- package/dist/index.js.map +4 -4
- package/extension/dist/background.js +413 -0
- package/extension/dist/background.js.map +3 -3
- package/extension/dist/content.js +71 -0
- package/extension/dist/content.js.map +2 -2
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
- package/skills/browser-bridge/skill.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,26 @@ _TBD_
|
|
|
14
14
|
|
|
15
15
|
_TBD_
|
|
16
16
|
|
|
17
|
+
## [0.4.2] - 2026-02-07
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Fix the GitHub release workflow tag/version verification step so tag pushes reliably create a GitHub Release and upload the extension zip.
|
|
22
|
+
|
|
23
|
+
## [0.4.1] - 2026-02-07
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- `health_check` MCP tool and core endpoint (`/health_check`) for uptime/memory/session/extension status.
|
|
28
|
+
- Full-page scrolling screenshots for `artifacts.screenshot` via `fullPage: true` (scroll + stitch, up to ~50K px tall).
|
|
29
|
+
- MCP Streamable HTTP server transport (in addition to stdio).
|
|
30
|
+
- Pre-built Chrome extension zip attached to GitHub releases.
|
|
31
|
+
- Element-targeted screenshots for `artifacts.screenshot` via `selector`.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
_TBD_
|
|
36
|
+
|
|
17
37
|
## [0.4.0] - 2026-02-06
|
|
18
38
|
|
|
19
39
|
### Added
|
package/README.md
CHANGED
|
@@ -1,10 +1,47 @@
|
|
|
1
1
|
<img src="docs/assets/readme-header.png" alt="Browser Bridge header graphic" width="720" />
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@btraut/browser-bridge) [](https://github.com/btraut/browser-bridge/actions/workflows/ci.yml) [](LICENSE)
|
|
3
|
+
[](https://www.npmjs.com/package/@btraut/browser-bridge) [](https://www.npmjs.com/package/@btraut/browser-bridge) [](https://github.com/btraut/browser-bridge/actions/workflows/ci.yml) [](LICENSE)
|
|
4
4
|
|
|
5
5
|
# Browser Bridge
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Reliable local Chrome control for coding agents.**
|
|
8
|
+
|
|
9
|
+
Browser Bridge drives your real, local Chrome (not headless) and inspects page state through a Chrome extension plus a local daemon. You stay in the loop with your existing tabs and login state.
|
|
10
|
+
|
|
11
|
+
What makes it different:
|
|
12
|
+
|
|
13
|
+
- **Real browser state**: operate on your actual Chrome profile (tabs, cookies, logins, extensions).
|
|
14
|
+
- **Two-plane architecture**: a **drive** plane that does what a user does (click, type, navigate), plus an **inspect** plane that reads state (DOM, console, screenshots). This separation makes runs less flaky and lets inspection happen in parallel.
|
|
15
|
+
- **Token-efficient inspection**: stable element refs like `@e1` (find once, reuse everywhere) plus knobs to bound output (`--max-nodes`, `--compact`, `--interactive`, `--selector`).
|
|
16
|
+
- **Structured errors for agents**: stable error codes with a `retryable` flag (no more guessing whether to retry).
|
|
17
|
+
- **Recovery-first**: sessions have an explicit state machine with `session.recover()` and `diagnostics doctor`.
|
|
18
|
+
- **Inspect beyond screenshots**: DOM snapshots (AX + HTML) and `inspect dom-diff` to detect page changes.
|
|
19
|
+
|
|
20
|
+
## Why Browser Bridge
|
|
21
|
+
|
|
22
|
+
Browser Bridge is built for agent reliability and "stay logged in" workflows in your real Chrome, not for headless test automation.
|
|
23
|
+
|
|
24
|
+
If you're coming from Playwright/Puppeteer-style tooling:
|
|
25
|
+
|
|
26
|
+
- Browser Bridge targets the user's existing, interactive Chrome session by default (typical Playwright/Puppeteer flows spin up a separate browser/context).
|
|
27
|
+
- Browser Bridge surfaces retry guidance in the API (`retryable`) instead of forcing the agent to infer it from exceptions and timing.
|
|
28
|
+
- Browser Bridge ships a first-class inspect plane (DOM snapshots, diffs, diagnostics) designed for LLM consumption, with output-bounding options to keep agent context small.
|
|
29
|
+
|
|
30
|
+
If you're coming from an extension-only MCP tool:
|
|
31
|
+
|
|
32
|
+
- Browser Bridge puts a stateful local Core daemon behind the tools (sessions, recovery, diagnostics, artifacts).
|
|
33
|
+
- Drive actions are serialized for determinism; inspect is a separate plane that can keep producing structured state.
|
|
34
|
+
- CLI works everywhere; MCP is optional.
|
|
35
|
+
|
|
36
|
+
## How It Works
|
|
37
|
+
|
|
38
|
+
Core keeps a session state machine and exposes a small set of stable tools:
|
|
39
|
+
|
|
40
|
+
- `session.*` - lifecycle + recovery
|
|
41
|
+
- `drive.*` - navigation + input (single-flight)
|
|
42
|
+
- `inspect.*` - DOM snapshots/diffs + evaluation
|
|
43
|
+
- `diagnostics.*` - health checks
|
|
44
|
+
- `artifacts.*` - screenshots
|
|
8
45
|
|
|
9
46
|
## Requirements
|
|
10
47
|
|
|
@@ -13,7 +50,7 @@ Local Chrome control for coding agents. Browser Bridge provides a CLI and an opt
|
|
|
13
50
|
- Browser Bridge extension (Chrome Web Store listing pending; see manual install below)
|
|
14
51
|
- Local-only usage (all services bind to 127.0.0.1)
|
|
15
52
|
|
|
16
|
-
## Install
|
|
53
|
+
## Install (CLI)
|
|
17
54
|
|
|
18
55
|
```bash
|
|
19
56
|
npm i -g @btraut/browser-bridge
|
|
@@ -24,6 +61,10 @@ browser-bridge --help
|
|
|
24
61
|
|
|
25
62
|
Chrome Web Store listing is pending. For now, install the extension manually:
|
|
26
63
|
|
|
64
|
+
1. Download the latest pre-built extension zip from [GitHub Releases](https://github.com/btraut/browser-bridge/releases) (Assets), unzip it, and use the unzipped folder for step 3.
|
|
65
|
+
|
|
66
|
+
Alternative (build from source):
|
|
67
|
+
|
|
27
68
|
1. Clone this repo.
|
|
28
69
|
2. Install deps and build:
|
|
29
70
|
|
|
@@ -33,13 +74,13 @@ npm run build
|
|
|
33
74
|
```
|
|
34
75
|
|
|
35
76
|
3. Open Chrome and navigate to `chrome://extensions`.
|
|
36
|
-
4. Enable **Developer mode**, click **Load unpacked**, and select
|
|
77
|
+
4. Enable **Developer mode**, click **Load unpacked**, and select the extension folder (the folder with `manifest.json`).
|
|
37
78
|
|
|
38
79
|
## Quickstart
|
|
39
80
|
|
|
40
81
|
1. Install the extension.
|
|
41
|
-
2. Run `browser-bridge install` (skill + optional MCP).
|
|
42
|
-
3. Run a quick CLI check:
|
|
82
|
+
2. (Optional) Run `browser-bridge install` (skill + optional MCP).
|
|
83
|
+
3. Run a quick CLI check (Core auto-starts by default):
|
|
43
84
|
|
|
44
85
|
```bash
|
|
45
86
|
browser-bridge session create
|
|
@@ -53,7 +94,9 @@ Notes:
|
|
|
53
94
|
|
|
54
95
|
- `inspect dom-snapshot` defaults to `--format ax`; `--max-nodes` is only supported for AX snapshots.
|
|
55
96
|
|
|
56
|
-
## Skills (
|
|
97
|
+
## Skills (Agent Clients)
|
|
98
|
+
|
|
99
|
+
Browser Bridge skills work across many agent clients, including Codex and Claude Code.
|
|
57
100
|
|
|
58
101
|
Easiest option (recommended):
|
|
59
102
|
|
|
@@ -128,34 +171,16 @@ claude mcp add --transport stdio browser-bridge \
|
|
|
128
171
|
- CLI: `browser-bridge diagnostics doctor --session-id <id>`
|
|
129
172
|
- Reports extension and debugger status alongside session state.
|
|
130
173
|
|
|
174
|
+
## Recovery
|
|
175
|
+
|
|
176
|
+
If drive or inspect gets into a bad state, recovery is explicit:
|
|
177
|
+
|
|
178
|
+
- `browser-bridge session recover --session-id <id>`
|
|
179
|
+
- Then retry the failed operation once (tools report whether failures are `retryable`).
|
|
180
|
+
|
|
131
181
|
## Session TTL (Core Daemon)
|
|
132
182
|
|
|
133
183
|
The Core daemon keeps sessions in memory. By default, it automatically cleans up idle sessions after 1 hour.
|
|
134
184
|
|
|
135
185
|
- `BROWSER_BRIDGE_SESSION_TTL_MS`: Idle session TTL in milliseconds. Set to `0` to disable cleanup.
|
|
136
186
|
- `BROWSER_BRIDGE_SESSION_CLEANUP_INTERVAL_MS`: Cleanup interval in milliseconds. Defaults to a small value relative to the TTL.
|
|
137
|
-
|
|
138
|
-
## Changelog
|
|
139
|
-
|
|
140
|
-
See `CHANGELOG.md`.
|
|
141
|
-
|
|
142
|
-
## Releasing
|
|
143
|
-
|
|
144
|
-
See `docs/releasing.md`.
|
|
145
|
-
|
|
146
|
-
## Security Model (v1)
|
|
147
|
-
|
|
148
|
-
- Extension <-> Core WebSocket has no authentication; trust local machine only.
|
|
149
|
-
- Do not expose the port or run the Core daemon on shared hosts.
|
|
150
|
-
|
|
151
|
-
## Development Notes
|
|
152
|
-
|
|
153
|
-
If you are contributing locally, load the extension unpacked:
|
|
154
|
-
|
|
155
|
-
1. Open Chrome and navigate to `chrome://extensions`.
|
|
156
|
-
2. Enable **Developer mode**.
|
|
157
|
-
3. Click **Load unpacked** and select `packages/extension` (repo).
|
|
158
|
-
4. Confirm the extension's background service worker is running.
|
|
159
|
-
5. Start the Core daemon (or run `browser-bridge session create`) so the extension can connect to `127.0.0.1`.
|
|
160
|
-
|
|
161
|
-
Additional manual test flows live in `docs/manual-test.md`.
|
package/dist/api.js
CHANGED
|
@@ -2521,8 +2521,125 @@ var InspectService = class {
|
|
|
2521
2521
|
async screenshot(input) {
|
|
2522
2522
|
this.requireSession(input.sessionId);
|
|
2523
2523
|
const selection = await this.resolveTab(input.targetHint);
|
|
2524
|
-
await this.debuggerCommand(selection.tabId, "Page.enable", {});
|
|
2525
2524
|
const format = input.format ?? "png";
|
|
2525
|
+
const writeArtifact = async (data2) => {
|
|
2526
|
+
try {
|
|
2527
|
+
const rootDir = await ensureArtifactRootDir(input.sessionId);
|
|
2528
|
+
const artifactId = (0, import_crypto3.randomUUID)();
|
|
2529
|
+
const extension = format === "jpeg" ? "jpg" : format;
|
|
2530
|
+
const filePath = import_node_path2.default.join(
|
|
2531
|
+
rootDir,
|
|
2532
|
+
`screenshot-${artifactId}.${extension}`
|
|
2533
|
+
);
|
|
2534
|
+
await (0, import_promises2.writeFile)(filePath, Buffer.from(data2, "base64"));
|
|
2535
|
+
const mime = format === "jpeg" ? "image/jpeg" : `image/${format}`;
|
|
2536
|
+
const output = {
|
|
2537
|
+
artifact_id: artifactId,
|
|
2538
|
+
path: filePath,
|
|
2539
|
+
mime
|
|
2540
|
+
};
|
|
2541
|
+
this.markInspectConnected(input.sessionId);
|
|
2542
|
+
return output;
|
|
2543
|
+
} catch {
|
|
2544
|
+
const error = new InspectError(
|
|
2545
|
+
"ARTIFACT_IO_ERROR",
|
|
2546
|
+
"Failed to write screenshot file."
|
|
2547
|
+
);
|
|
2548
|
+
this.recordError(error);
|
|
2549
|
+
throw error;
|
|
2550
|
+
}
|
|
2551
|
+
};
|
|
2552
|
+
if (input.selector) {
|
|
2553
|
+
if (!this.extensionBridge?.request) {
|
|
2554
|
+
const error = new InspectError(
|
|
2555
|
+
"NOT_SUPPORTED",
|
|
2556
|
+
"Element screenshots require an extension that supports drive.screenshot."
|
|
2557
|
+
);
|
|
2558
|
+
this.recordError(error);
|
|
2559
|
+
throw error;
|
|
2560
|
+
}
|
|
2561
|
+
const response = await this.extensionBridge.request(
|
|
2562
|
+
"drive.screenshot",
|
|
2563
|
+
{
|
|
2564
|
+
tab_id: selection.tabId,
|
|
2565
|
+
mode: "element",
|
|
2566
|
+
selector: input.selector,
|
|
2567
|
+
format,
|
|
2568
|
+
...typeof input.quality === "number" ? { quality: input.quality } : {}
|
|
2569
|
+
},
|
|
2570
|
+
12e4
|
|
2571
|
+
);
|
|
2572
|
+
if (response.status === "error") {
|
|
2573
|
+
const error = new InspectError(
|
|
2574
|
+
response.error?.code ?? "INSPECT_UNAVAILABLE",
|
|
2575
|
+
response.error?.message ?? "Failed to capture element screenshot.",
|
|
2576
|
+
{
|
|
2577
|
+
retryable: response.error?.retryable ?? false,
|
|
2578
|
+
...response.error?.details ? { details: response.error.details } : {}
|
|
2579
|
+
}
|
|
2580
|
+
);
|
|
2581
|
+
this.recordError(error);
|
|
2582
|
+
throw error;
|
|
2583
|
+
}
|
|
2584
|
+
const result2 = response.result;
|
|
2585
|
+
if (!result2?.data_base64 || typeof result2.data_base64 !== "string") {
|
|
2586
|
+
const error = new InspectError(
|
|
2587
|
+
"INSPECT_UNAVAILABLE",
|
|
2588
|
+
"Failed to capture element screenshot."
|
|
2589
|
+
);
|
|
2590
|
+
this.recordError(error);
|
|
2591
|
+
throw error;
|
|
2592
|
+
}
|
|
2593
|
+
return await writeArtifact(result2.data_base64);
|
|
2594
|
+
}
|
|
2595
|
+
if (input.target === "full" && this.extensionBridge?.request) {
|
|
2596
|
+
try {
|
|
2597
|
+
const response = await this.extensionBridge.request(
|
|
2598
|
+
"drive.screenshot",
|
|
2599
|
+
{
|
|
2600
|
+
tab_id: selection.tabId,
|
|
2601
|
+
mode: "full_page",
|
|
2602
|
+
format,
|
|
2603
|
+
...typeof input.quality === "number" ? { quality: input.quality } : {}
|
|
2604
|
+
},
|
|
2605
|
+
12e4
|
|
2606
|
+
);
|
|
2607
|
+
if (response.status === "error") {
|
|
2608
|
+
const error = new InspectError(
|
|
2609
|
+
response.error?.code ?? "INSPECT_UNAVAILABLE",
|
|
2610
|
+
response.error?.message ?? "Failed to capture full page screenshot.",
|
|
2611
|
+
{
|
|
2612
|
+
retryable: response.error?.retryable ?? false,
|
|
2613
|
+
...response.error?.details ? { details: response.error.details } : {}
|
|
2614
|
+
}
|
|
2615
|
+
);
|
|
2616
|
+
this.recordError(error);
|
|
2617
|
+
throw error;
|
|
2618
|
+
}
|
|
2619
|
+
const result2 = response.result;
|
|
2620
|
+
if (!result2?.data_base64 || typeof result2.data_base64 !== "string") {
|
|
2621
|
+
const error = new InspectError(
|
|
2622
|
+
"INSPECT_UNAVAILABLE",
|
|
2623
|
+
"Failed to capture full page screenshot."
|
|
2624
|
+
);
|
|
2625
|
+
this.recordError(error);
|
|
2626
|
+
throw error;
|
|
2627
|
+
}
|
|
2628
|
+
return await writeArtifact(result2.data_base64);
|
|
2629
|
+
} catch (error) {
|
|
2630
|
+
if (error instanceof InspectError) {
|
|
2631
|
+
const code = String(error.code);
|
|
2632
|
+
if (![
|
|
2633
|
+
"NOT_SUPPORTED",
|
|
2634
|
+
"NOT_IMPLEMENTED",
|
|
2635
|
+
"INSPECT_UNAVAILABLE"
|
|
2636
|
+
].includes(code)) {
|
|
2637
|
+
throw error;
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
await this.debuggerCommand(selection.tabId, "Page.enable", {});
|
|
2526
2643
|
let captureParams = {
|
|
2527
2644
|
format,
|
|
2528
2645
|
fromSurface: true
|
|
@@ -2567,31 +2684,7 @@ var InspectService = class {
|
|
|
2567
2684
|
this.recordError(error);
|
|
2568
2685
|
throw error;
|
|
2569
2686
|
}
|
|
2570
|
-
|
|
2571
|
-
const rootDir = await ensureArtifactRootDir(input.sessionId);
|
|
2572
|
-
const artifactId = (0, import_crypto3.randomUUID)();
|
|
2573
|
-
const extension = format === "jpeg" ? "jpg" : format;
|
|
2574
|
-
const filePath = import_node_path2.default.join(
|
|
2575
|
-
rootDir,
|
|
2576
|
-
`screenshot-${artifactId}.${extension}`
|
|
2577
|
-
);
|
|
2578
|
-
await (0, import_promises2.writeFile)(filePath, Buffer.from(data, "base64"));
|
|
2579
|
-
const mime = format === "jpeg" ? "image/jpeg" : `image/${format}`;
|
|
2580
|
-
const output = {
|
|
2581
|
-
artifact_id: artifactId,
|
|
2582
|
-
path: filePath,
|
|
2583
|
-
mime
|
|
2584
|
-
};
|
|
2585
|
-
this.markInspectConnected(input.sessionId);
|
|
2586
|
-
return output;
|
|
2587
|
-
} catch {
|
|
2588
|
-
const error = new InspectError(
|
|
2589
|
-
"ARTIFACT_IO_ERROR",
|
|
2590
|
-
"Failed to write screenshot file."
|
|
2591
|
-
);
|
|
2592
|
-
this.recordError(error);
|
|
2593
|
-
throw error;
|
|
2594
|
-
}
|
|
2687
|
+
return await writeArtifact(data);
|
|
2595
2688
|
}
|
|
2596
2689
|
ensureDebugger() {
|
|
2597
2690
|
if (!this.debugger) {
|
|
@@ -3266,10 +3359,30 @@ var ArtifactsScreenshotInputSchema = import_zod2.z.object({
|
|
|
3266
3359
|
session_id: import_zod2.z.string().min(1),
|
|
3267
3360
|
target: import_zod2.z.enum(["viewport", "full"]).default("viewport"),
|
|
3268
3361
|
fullPage: import_zod2.z.boolean().default(false),
|
|
3362
|
+
selector: import_zod2.z.string().min(1).optional(),
|
|
3269
3363
|
format: import_zod2.z.enum(["png", "jpeg", "webp"]).default("png"),
|
|
3270
3364
|
quality: import_zod2.z.number().min(0).max(100).optional()
|
|
3271
3365
|
});
|
|
3272
3366
|
var ArtifactsScreenshotOutputSchema = ArtifactInfoSchema;
|
|
3367
|
+
var HealthCheckInputSchema = import_zod2.z.object({});
|
|
3368
|
+
var HealthCheckOutputSchema = import_zod2.z.object({
|
|
3369
|
+
started_at: import_zod2.z.string().min(1),
|
|
3370
|
+
uptime_ms: import_zod2.z.number().finite().nonnegative(),
|
|
3371
|
+
memory: import_zod2.z.object({
|
|
3372
|
+
rss: import_zod2.z.number().finite().nonnegative(),
|
|
3373
|
+
heapTotal: import_zod2.z.number().finite().nonnegative(),
|
|
3374
|
+
heapUsed: import_zod2.z.number().finite().nonnegative(),
|
|
3375
|
+
external: import_zod2.z.number().finite().nonnegative(),
|
|
3376
|
+
arrayBuffers: import_zod2.z.number().finite().nonnegative().optional()
|
|
3377
|
+
}).passthrough(),
|
|
3378
|
+
sessions: import_zod2.z.object({
|
|
3379
|
+
active: import_zod2.z.number().finite().nonnegative()
|
|
3380
|
+
}).passthrough(),
|
|
3381
|
+
extension: import_zod2.z.object({
|
|
3382
|
+
connected: import_zod2.z.boolean(),
|
|
3383
|
+
last_seen_at: import_zod2.z.string().min(1).optional()
|
|
3384
|
+
}).passthrough()
|
|
3385
|
+
}).passthrough();
|
|
3273
3386
|
var DiagnosticsDoctorInputSchema = import_zod2.z.object({
|
|
3274
3387
|
session_id: import_zod2.z.string().min(1).optional()
|
|
3275
3388
|
});
|
|
@@ -3325,6 +3438,7 @@ var registerArtifactsRoutes = (router, options = {}) => {
|
|
|
3325
3438
|
const result = await inspect.screenshot({
|
|
3326
3439
|
sessionId: input.session_id,
|
|
3327
3440
|
target,
|
|
3441
|
+
selector: input.selector,
|
|
3328
3442
|
format: input.format,
|
|
3329
3443
|
quality: input.quality,
|
|
3330
3444
|
targetHint: hint
|
|
@@ -3461,7 +3575,44 @@ var buildDiagnosticReport = (sessionId, context = {}) => {
|
|
|
3461
3575
|
};
|
|
3462
3576
|
|
|
3463
3577
|
// packages/core/src/routes/diagnostics.ts
|
|
3578
|
+
var PROCESS_STARTED_AT = new Date(
|
|
3579
|
+
Date.now() - Math.floor(process.uptime() * 1e3)
|
|
3580
|
+
).toISOString();
|
|
3464
3581
|
var registerDiagnosticsRoutes = (router, options = {}) => {
|
|
3582
|
+
router.post("/health_check", (req, res) => {
|
|
3583
|
+
const body = req.body ?? {};
|
|
3584
|
+
if (!isRecord(body)) {
|
|
3585
|
+
sendError(res, 400, {
|
|
3586
|
+
code: "INVALID_ARGUMENT",
|
|
3587
|
+
message: "Request body must be an object.",
|
|
3588
|
+
retryable: false
|
|
3589
|
+
});
|
|
3590
|
+
return;
|
|
3591
|
+
}
|
|
3592
|
+
const parsed = HealthCheckInputSchema.safeParse(body);
|
|
3593
|
+
if (!parsed.success) {
|
|
3594
|
+
const issue = parsed.error.issues[0];
|
|
3595
|
+
sendError(res, 400, {
|
|
3596
|
+
code: "INVALID_ARGUMENT",
|
|
3597
|
+
message: issue?.message ?? "Invalid health check request.",
|
|
3598
|
+
retryable: false,
|
|
3599
|
+
details: issue?.path.length ? { field: issue.path.map((part) => String(part)).join(".") } : void 0
|
|
3600
|
+
});
|
|
3601
|
+
return;
|
|
3602
|
+
}
|
|
3603
|
+
const sessionsActive = options.registry ? options.registry.list().length : 0;
|
|
3604
|
+
const extensionStatus = options.extensionBridge?.getStatus();
|
|
3605
|
+
sendResult(res, {
|
|
3606
|
+
started_at: PROCESS_STARTED_AT,
|
|
3607
|
+
uptime_ms: Math.floor(process.uptime() * 1e3),
|
|
3608
|
+
memory: process.memoryUsage(),
|
|
3609
|
+
sessions: { active: sessionsActive },
|
|
3610
|
+
extension: {
|
|
3611
|
+
connected: extensionStatus?.connected ?? false,
|
|
3612
|
+
...extensionStatus?.lastSeenAt ? { last_seen_at: extensionStatus.lastSeenAt } : {}
|
|
3613
|
+
}
|
|
3614
|
+
});
|
|
3615
|
+
});
|
|
3465
3616
|
router.post("/diagnostics/doctor", (req, res) => {
|
|
3466
3617
|
let sessionId;
|
|
3467
3618
|
if (req.body !== void 0) {
|
|
@@ -4826,6 +4977,16 @@ var TOOL_DEFINITIONS = [
|
|
|
4826
4977
|
corePath: "/artifacts/screenshot"
|
|
4827
4978
|
}
|
|
4828
4979
|
},
|
|
4980
|
+
{
|
|
4981
|
+
name: "health_check",
|
|
4982
|
+
config: {
|
|
4983
|
+
title: "Health Check",
|
|
4984
|
+
description: "Check server health including uptime, memory usage, active session count, and extension connection status.",
|
|
4985
|
+
inputSchema: HealthCheckInputSchema,
|
|
4986
|
+
outputSchema: envelope(HealthCheckOutputSchema),
|
|
4987
|
+
corePath: "/health_check"
|
|
4988
|
+
}
|
|
4989
|
+
},
|
|
4829
4990
|
{
|
|
4830
4991
|
name: "diagnostics.doctor",
|
|
4831
4992
|
config: {
|
|
@@ -4870,6 +5031,8 @@ var registerBrowserBridgeTools = (server, client) => {
|
|
|
4870
5031
|
// packages/mcp-adapter/src/server.ts
|
|
4871
5032
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
4872
5033
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
5034
|
+
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
5035
|
+
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
4873
5036
|
var DEFAULT_SERVER_NAME = "browser-bridge";
|
|
4874
5037
|
var DEFAULT_SERVER_VERSION = "0.0.0";
|
|
4875
5038
|
var createMcpServer = (options = {}) => {
|