local-browser-bridge 0.1.0

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.
Files changed (92) hide show
  1. package/README.md +724 -0
  2. package/dist/package.json +61 -0
  3. package/dist/src/browser/chrome.d.ts +19 -0
  4. package/dist/src/browser/chrome.js +778 -0
  5. package/dist/src/browser/index.d.ts +3 -0
  6. package/dist/src/browser/index.js +25 -0
  7. package/dist/src/browser/safari.d.ts +41 -0
  8. package/dist/src/browser/safari.js +827 -0
  9. package/dist/src/browser-attach-ux-helper.d.ts +39 -0
  10. package/dist/src/browser-attach-ux-helper.js +157 -0
  11. package/dist/src/capabilities.d.ts +3 -0
  12. package/dist/src/capabilities.js +182 -0
  13. package/dist/src/chrome-relay-error-helper.d.ts +19 -0
  14. package/dist/src/chrome-relay-error-helper.js +78 -0
  15. package/dist/src/chrome-relay-helper-cli.d.ts +2 -0
  16. package/dist/src/chrome-relay-helper-cli.js +97 -0
  17. package/dist/src/chrome-relay-helper.d.ts +29 -0
  18. package/dist/src/chrome-relay-helper.js +151 -0
  19. package/dist/src/chrome-relay-state.d.ts +23 -0
  20. package/dist/src/chrome-relay-state.js +108 -0
  21. package/dist/src/claude-code.d.ts +20 -0
  22. package/dist/src/claude-code.js +66 -0
  23. package/dist/src/cli-reference-adapter.d.ts +13 -0
  24. package/dist/src/cli-reference-adapter.js +48 -0
  25. package/dist/src/cli.d.ts +3 -0
  26. package/dist/src/cli.js +200 -0
  27. package/dist/src/codex.d.ts +17 -0
  28. package/dist/src/codex.js +25 -0
  29. package/dist/src/connection-ux.d.ts +61 -0
  30. package/dist/src/connection-ux.js +256 -0
  31. package/dist/src/errors.d.ts +12 -0
  32. package/dist/src/errors.js +58 -0
  33. package/dist/src/http-reference-adapter.d.ts +34 -0
  34. package/dist/src/http-reference-adapter.js +61 -0
  35. package/dist/src/http.d.ts +3 -0
  36. package/dist/src/http.js +161 -0
  37. package/dist/src/index.d.ts +17 -0
  38. package/dist/src/index.js +43 -0
  39. package/dist/src/mcp-stdio.d.ts +2 -0
  40. package/dist/src/mcp-stdio.js +10 -0
  41. package/dist/src/mcp.d.ts +25 -0
  42. package/dist/src/mcp.js +483 -0
  43. package/dist/src/reference-adapter.d.ts +32 -0
  44. package/dist/src/reference-adapter.js +42 -0
  45. package/dist/src/service/attach-service.d.ts +28 -0
  46. package/dist/src/service/attach-service.js +272 -0
  47. package/dist/src/session-metadata.d.ts +4 -0
  48. package/dist/src/session-metadata.js +88 -0
  49. package/dist/src/store/session-store.d.ts +14 -0
  50. package/dist/src/store/session-store.js +52 -0
  51. package/dist/src/target.d.ts +9 -0
  52. package/dist/src/target.js +61 -0
  53. package/dist/src/types.d.ts +397 -0
  54. package/dist/src/types.js +2 -0
  55. package/dist/tests/attach-service.test.d.ts +1 -0
  56. package/dist/tests/attach-service.test.js +1367 -0
  57. package/dist/tests/browser-attach-ux-helper.test.d.ts +1 -0
  58. package/dist/tests/browser-attach-ux-helper.test.js +139 -0
  59. package/dist/tests/chrome-relay-error-helper.test.d.ts +1 -0
  60. package/dist/tests/chrome-relay-error-helper.test.js +67 -0
  61. package/dist/tests/chrome-relay-helper.test.d.ts +1 -0
  62. package/dist/tests/chrome-relay-helper.test.js +142 -0
  63. package/dist/tests/chrome-relay-state-schema.test.d.ts +1 -0
  64. package/dist/tests/chrome-relay-state-schema.test.js +96 -0
  65. package/dist/tests/claude-code-wrapper.test.d.ts +1 -0
  66. package/dist/tests/claude-code-wrapper.test.js +170 -0
  67. package/dist/tests/codex.test.d.ts +1 -0
  68. package/dist/tests/codex.test.js +210 -0
  69. package/dist/tests/demo-client-smoke.test.d.ts +1 -0
  70. package/dist/tests/demo-client-smoke.test.js +405 -0
  71. package/dist/tests/docs-fixtures.test.d.ts +1 -0
  72. package/dist/tests/docs-fixtures.test.js +255 -0
  73. package/dist/tests/doctor-connect-wrapper.test.d.ts +1 -0
  74. package/dist/tests/doctor-connect-wrapper.test.js +62 -0
  75. package/dist/tests/fixtures/doctor-connect-cli-stub.d.ts +1 -0
  76. package/dist/tests/fixtures/doctor-connect-cli-stub.js +93 -0
  77. package/dist/tests/fixtures/public-root-cli-stub.d.ts +210 -0
  78. package/dist/tests/fixtures/public-root-cli-stub.js +143 -0
  79. package/dist/tests/fixtures/public-root-consumer.js +67 -0
  80. package/dist/tests/mcp.test.d.ts +1 -0
  81. package/dist/tests/mcp.test.js +345 -0
  82. package/dist/tests/public-consumer-helpers.test.d.ts +1 -0
  83. package/dist/tests/public-consumer-helpers.test.js +33 -0
  84. package/dist/tests/public-package-git-consumption.test.d.ts +1 -0
  85. package/dist/tests/public-package-git-consumption.test.js +56 -0
  86. package/dist/tests/public-root-consumer-smoke.test.d.ts +1 -0
  87. package/dist/tests/public-root-consumer-smoke.test.js +214 -0
  88. package/dist/tests/reference-adapter.test.d.ts +1 -0
  89. package/dist/tests/reference-adapter.test.js +220 -0
  90. package/dist/tests/transport-reference-adapters.test.d.ts +1 -0
  91. package/dist/tests/transport-reference-adapters.test.js +214 -0
  92. package/package.json +61 -0
@@ -0,0 +1,214 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_child_process_1 = require("node:child_process");
8
+ const node_http_1 = require("node:http");
9
+ const node_path_1 = require("node:path");
10
+ const node_util_1 = require("node:util");
11
+ const node_test_1 = __importDefault(require("node:test"));
12
+ const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
13
+ const capabilities = {
14
+ schemaVersion: 1,
15
+ kind: "local-browser-bridge",
16
+ product: { name: "local-browser-bridge", version: "0.1.0" }
17
+ };
18
+ const diagnostics = {
19
+ browser: "chrome",
20
+ checkedAt: "2026-03-30T12:00:00.000Z",
21
+ runtime: {
22
+ platform: "darwin",
23
+ arch: "arm64",
24
+ nodeVersion: "v25.8.0",
25
+ safariRunning: true
26
+ },
27
+ host: {
28
+ osascriptAvailable: true,
29
+ screencaptureAvailable: true,
30
+ safariApplicationAvailable: true
31
+ },
32
+ supportedFeatures: {
33
+ inspectTabs: true,
34
+ attach: true,
35
+ activate: true,
36
+ navigate: true,
37
+ screenshot: true,
38
+ savedSessions: true,
39
+ cli: true,
40
+ httpApi: true
41
+ },
42
+ constraints: [],
43
+ attach: {
44
+ direct: {
45
+ mode: "direct",
46
+ source: "user-browser",
47
+ scope: "browser",
48
+ supported: true,
49
+ ready: false,
50
+ state: "attention-required",
51
+ blockers: []
52
+ },
53
+ relay: {
54
+ mode: "relay",
55
+ source: "extension-relay",
56
+ scope: "tab",
57
+ supported: true,
58
+ ready: true,
59
+ state: "ready",
60
+ blockers: []
61
+ }
62
+ }
63
+ };
64
+ const session = {
65
+ schemaVersion: 1,
66
+ id: "sess-http-relay",
67
+ kind: "chrome-readonly",
68
+ browser: "chrome",
69
+ target: { type: "front" },
70
+ tab: {
71
+ browser: "chrome",
72
+ windowIndex: 1,
73
+ tabIndex: 1,
74
+ title: "Shared tab",
75
+ url: "https://example.com",
76
+ attachedAt: "2026-03-30T12:00:00.000Z",
77
+ identity: {
78
+ signature: "sig",
79
+ urlKey: "https://example.com",
80
+ titleKey: "Shared tab",
81
+ origin: "https://example.com",
82
+ pathname: "/"
83
+ }
84
+ },
85
+ frontTab: {
86
+ browser: "chrome",
87
+ windowIndex: 1,
88
+ tabIndex: 1,
89
+ title: "Shared tab",
90
+ url: "https://example.com",
91
+ attachedAt: "2026-03-30T12:00:00.000Z",
92
+ identity: {
93
+ signature: "sig",
94
+ urlKey: "https://example.com",
95
+ titleKey: "Shared tab",
96
+ origin: "https://example.com",
97
+ pathname: "/"
98
+ }
99
+ },
100
+ attach: {
101
+ mode: "relay",
102
+ source: "extension-relay",
103
+ scope: "tab",
104
+ resumable: true
105
+ },
106
+ semantics: {
107
+ inspect: "shared-tab-only",
108
+ list: "saved-session",
109
+ resume: "current-shared-tab",
110
+ tabReference: {
111
+ windowIndex: "synthetic-shared-tab-position",
112
+ tabIndex: "synthetic-shared-tab-position"
113
+ }
114
+ },
115
+ capabilities: {
116
+ resume: true,
117
+ activate: false,
118
+ navigate: false,
119
+ screenshot: false
120
+ },
121
+ status: {
122
+ state: "read-only",
123
+ canAct: false
124
+ },
125
+ createdAt: "2026-03-30T12:00:00.000Z"
126
+ };
127
+ const resumedSession = {
128
+ session: {
129
+ ...session,
130
+ id: "sess-cli-relay"
131
+ },
132
+ tab: session.tab,
133
+ resumedAt: "2026-03-30T12:05:00.000Z",
134
+ resolution: {
135
+ strategy: "front",
136
+ matched: true,
137
+ attachMode: "relay",
138
+ semantics: "current-shared-tab"
139
+ }
140
+ };
141
+ async function readJsonBody(request) {
142
+ const chunks = [];
143
+ for await (const chunk of request) {
144
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
145
+ }
146
+ if (chunks.length === 0) {
147
+ return undefined;
148
+ }
149
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
150
+ }
151
+ async function withStubBridge(state, run) {
152
+ const server = (0, node_http_1.createServer)(async (request, response) => {
153
+ const url = new URL(request.url ?? "/", "http://127.0.0.1");
154
+ const body = request.method === "POST" ? await readJsonBody(request) : undefined;
155
+ state.requests.push({ method: request.method, url: `${url.pathname}${url.search}`, body });
156
+ if (request.method === "GET" && url.pathname === "/v1/capabilities") {
157
+ response.writeHead(200, { "content-type": "application/json" });
158
+ response.end(JSON.stringify({ capabilities }));
159
+ return;
160
+ }
161
+ if (request.method === "GET" && url.pathname === "/v1/diagnostics") {
162
+ response.writeHead(200, { "content-type": "application/json" });
163
+ response.end(JSON.stringify({ diagnostics }));
164
+ return;
165
+ }
166
+ if (request.method === "POST" && url.pathname === "/v1/attach") {
167
+ response.writeHead(200, { "content-type": "application/json" });
168
+ response.end(JSON.stringify({ session }));
169
+ return;
170
+ }
171
+ response.writeHead(404, { "content-type": "application/json" });
172
+ response.end(JSON.stringify({ error: { code: "not_found", message: "not found" } }));
173
+ });
174
+ await new Promise((resolvePromise) => server.listen(0, "127.0.0.1", resolvePromise));
175
+ const address = server.address();
176
+ strict_1.default.ok(address && typeof address === "object");
177
+ try {
178
+ return await run(`http://127.0.0.1:${address.port}`);
179
+ }
180
+ finally {
181
+ await new Promise((resolvePromise, reject) => server.close((error) => (error ? reject(error) : resolvePromise())));
182
+ }
183
+ }
184
+ async function runPublicConsumer(transport, arg1, route, sessionId) {
185
+ const fixtureEntrypoint = (0, node_path_1.resolve)(process.cwd(), "tests/fixtures/public-root-consumer-runner.js");
186
+ const result = await execFileAsync(process.execPath, [fixtureEntrypoint, transport, arg1, route, ...(sessionId ? [sessionId] : [])], {
187
+ cwd: process.cwd()
188
+ });
189
+ return JSON.parse(result.stdout);
190
+ }
191
+ (0, node_test_1.default)("public-root smoke: external-style HTTP and CLI consumers traverse the shared adapter stack", async (t) => {
192
+ await t.test("http attach via package root import returns shared-tab session UX", async () => {
193
+ const state = { requests: [] };
194
+ await withStubBridge(state, async (baseUrl) => {
195
+ const connection = await runPublicConsumer("http", baseUrl, "chrome-relay");
196
+ strict_1.default.equal(connection.routeUx.label, "Chrome (shared tab, read-only)");
197
+ strict_1.default.equal(connection.sessionUx.sharedTabScoped, true);
198
+ strict_1.default.equal(connection.session.id, "sess-http-relay");
199
+ strict_1.default.deepEqual(state.requests.map((entry) => `${entry.method} ${entry.url}`), ["GET /v1/capabilities", "GET /v1/diagnostics?browser=chrome", "POST /v1/attach"]);
200
+ strict_1.default.deepEqual(state.requests[2]?.body, {
201
+ browser: "chrome",
202
+ attach: { mode: "relay" }
203
+ });
204
+ });
205
+ });
206
+ await t.test("cli resume via package root import returns resumed shared-tab semantics", async () => {
207
+ const cliEntrypoint = (0, node_path_1.resolve)(__dirname, "fixtures/public-root-cli-stub.js");
208
+ const connection = await runPublicConsumer("cli", cliEntrypoint, "chrome-relay", "sess-cli-relay");
209
+ strict_1.default.equal(connection.operation, "resumeSession");
210
+ strict_1.default.equal(connection.routeUx.label, "Chrome (shared tab, read-only)");
211
+ strict_1.default.equal(connection.session.id, resumedSession.session.id);
212
+ strict_1.default.equal(connection.session.semantics.resume, "current-shared-tab");
213
+ });
214
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_test_1 = __importDefault(require("node:test"));
8
+ const reference_adapter_1 = require("../src/reference-adapter");
9
+ (0, node_test_1.default)("connectViaBridge preserves a transport-neutral adapter loop for attach", async () => {
10
+ const adapter = (0, reference_adapter_1.createBridgeAdapter)({
11
+ async getCapabilities() {
12
+ return { schemaVersion: 1, product: { name: "test-bridge" } };
13
+ },
14
+ async getDiagnostics(browser) {
15
+ return {
16
+ browser,
17
+ checkedAt: "2026-03-30T12:00:00.000Z",
18
+ runtime: {
19
+ platform: "darwin",
20
+ arch: "arm64",
21
+ nodeVersion: "v25.8.0",
22
+ safariRunning: true
23
+ },
24
+ host: {
25
+ osascriptAvailable: true,
26
+ screencaptureAvailable: true,
27
+ safariApplicationAvailable: true
28
+ },
29
+ supportedFeatures: {
30
+ inspectTabs: true,
31
+ attach: true,
32
+ activate: true,
33
+ navigate: true,
34
+ screenshot: true,
35
+ savedSessions: true,
36
+ cli: true,
37
+ httpApi: true
38
+ },
39
+ constraints: [],
40
+ attach: {
41
+ direct: {
42
+ mode: "direct",
43
+ source: "user-browser",
44
+ scope: "browser",
45
+ supported: true,
46
+ ready: true,
47
+ state: "ready",
48
+ blockers: []
49
+ },
50
+ relay: {
51
+ mode: "relay",
52
+ source: "extension-relay",
53
+ scope: "tab",
54
+ supported: true,
55
+ ready: false,
56
+ state: "attention-required",
57
+ blockers: []
58
+ }
59
+ }
60
+ };
61
+ },
62
+ async attach({ browser, attachMode }) {
63
+ strict_1.default.equal(browser, "chrome");
64
+ strict_1.default.equal(attachMode, "relay");
65
+ return {
66
+ session: {
67
+ schemaVersion: 1,
68
+ id: "sess-1",
69
+ kind: "chrome-readonly",
70
+ browser: "chrome",
71
+ target: { type: "front" },
72
+ tab: {
73
+ browser: "chrome",
74
+ windowIndex: 1,
75
+ tabIndex: 1,
76
+ title: "Shared tab",
77
+ url: "https://example.com",
78
+ attachedAt: "2026-03-30T12:00:00.000Z",
79
+ identity: {
80
+ signature: "sig",
81
+ urlKey: "https://example.com",
82
+ titleKey: "Shared tab",
83
+ origin: "https://example.com",
84
+ pathname: "/"
85
+ }
86
+ },
87
+ frontTab: {
88
+ browser: "chrome",
89
+ windowIndex: 1,
90
+ tabIndex: 1,
91
+ title: "Shared tab",
92
+ url: "https://example.com",
93
+ attachedAt: "2026-03-30T12:00:00.000Z",
94
+ identity: {
95
+ signature: "sig",
96
+ urlKey: "https://example.com",
97
+ titleKey: "Shared tab",
98
+ origin: "https://example.com",
99
+ pathname: "/"
100
+ }
101
+ },
102
+ attach: {
103
+ mode: "relay",
104
+ source: "extension-relay",
105
+ scope: "tab",
106
+ resumable: true
107
+ },
108
+ semantics: {
109
+ inspect: "shared-tab-only",
110
+ list: "saved-session",
111
+ resume: "current-shared-tab",
112
+ tabReference: {
113
+ windowIndex: "synthetic-shared-tab-position",
114
+ tabIndex: "synthetic-shared-tab-position"
115
+ }
116
+ },
117
+ capabilities: {
118
+ resume: true,
119
+ activate: false,
120
+ navigate: false,
121
+ screenshot: false
122
+ },
123
+ status: {
124
+ state: "read-only",
125
+ canAct: false
126
+ },
127
+ createdAt: "2026-03-30T12:00:00.000Z"
128
+ }
129
+ };
130
+ },
131
+ async resume() {
132
+ throw new Error("resume should not be called");
133
+ }
134
+ });
135
+ const result = await (0, reference_adapter_1.connectViaBridge)(adapter, {
136
+ browser: "chrome",
137
+ attachMode: "relay"
138
+ });
139
+ strict_1.default.equal(result.operation, "attach");
140
+ strict_1.default.equal(result.routeUx.label, "Chrome (shared tab, read-only)");
141
+ strict_1.default.equal(result.sessionUx.sharedTabScoped, true);
142
+ strict_1.default.equal(result.session.id, "sess-1");
143
+ strict_1.default.equal(result.capabilities.schemaVersion, 1);
144
+ });
145
+ (0, node_test_1.default)("sessionFromBridgeResult supports raw sessions and resumed-session envelopes", () => {
146
+ const rawSession = {
147
+ schemaVersion: 1,
148
+ id: "sess-raw",
149
+ kind: "safari-actionable",
150
+ browser: "safari",
151
+ target: { type: "front" },
152
+ tab: {
153
+ browser: "safari",
154
+ windowIndex: 1,
155
+ tabIndex: 1,
156
+ title: "Example",
157
+ url: "https://example.com",
158
+ attachedAt: "2026-03-30T12:00:00.000Z",
159
+ identity: {
160
+ signature: "sig",
161
+ urlKey: "https://example.com",
162
+ titleKey: "Example",
163
+ origin: "https://example.com",
164
+ pathname: "/"
165
+ }
166
+ },
167
+ frontTab: {
168
+ browser: "safari",
169
+ windowIndex: 1,
170
+ tabIndex: 1,
171
+ title: "Example",
172
+ url: "https://example.com",
173
+ attachedAt: "2026-03-30T12:00:00.000Z",
174
+ identity: {
175
+ signature: "sig",
176
+ urlKey: "https://example.com",
177
+ titleKey: "Example",
178
+ origin: "https://example.com",
179
+ pathname: "/"
180
+ }
181
+ },
182
+ attach: {
183
+ mode: "direct",
184
+ source: "user-browser",
185
+ scope: "browser"
186
+ },
187
+ semantics: {
188
+ inspect: "browser-tabs",
189
+ list: "saved-session",
190
+ resume: "saved-browser-target",
191
+ tabReference: {
192
+ windowIndex: "browser-position",
193
+ tabIndex: "browser-position"
194
+ }
195
+ },
196
+ capabilities: {
197
+ resume: true,
198
+ activate: true,
199
+ navigate: true,
200
+ screenshot: true
201
+ },
202
+ status: {
203
+ state: "actionable",
204
+ canAct: true
205
+ },
206
+ createdAt: "2026-03-30T12:00:00.000Z"
207
+ };
208
+ strict_1.default.equal((0, reference_adapter_1.sessionFromBridgeResult)(rawSession).id, "sess-raw");
209
+ strict_1.default.equal((0, reference_adapter_1.sessionFromBridgeResult)({
210
+ session: rawSession,
211
+ tab: rawSession.tab,
212
+ resumedAt: "2026-03-30T12:05:00.000Z",
213
+ resolution: {
214
+ strategy: "front",
215
+ matched: true,
216
+ attachMode: "direct",
217
+ semantics: "saved-browser-target"
218
+ }
219
+ }).id, "sess-raw");
220
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,214 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_test_1 = __importDefault(require("node:test"));
8
+ const cli_reference_adapter_1 = require("../src/cli-reference-adapter");
9
+ const http_reference_adapter_1 = require("../src/http-reference-adapter");
10
+ const capabilities = {
11
+ schemaVersion: 1,
12
+ kind: "local-browser-bridge",
13
+ product: { name: "local-browser-bridge", version: "0.1.0" }
14
+ };
15
+ const diagnostics = {
16
+ browser: "chrome",
17
+ checkedAt: "2026-03-30T12:00:00.000Z",
18
+ runtime: {
19
+ platform: "darwin",
20
+ arch: "arm64",
21
+ nodeVersion: "v25.8.0",
22
+ safariRunning: true
23
+ },
24
+ host: {
25
+ osascriptAvailable: true,
26
+ screencaptureAvailable: true,
27
+ safariApplicationAvailable: true
28
+ },
29
+ supportedFeatures: {
30
+ inspectTabs: true,
31
+ attach: true,
32
+ activate: true,
33
+ navigate: true,
34
+ screenshot: true,
35
+ savedSessions: true,
36
+ cli: true,
37
+ httpApi: true
38
+ },
39
+ constraints: [],
40
+ attach: {
41
+ direct: {
42
+ mode: "direct",
43
+ source: "user-browser",
44
+ scope: "browser",
45
+ supported: true,
46
+ ready: true,
47
+ state: "ready",
48
+ blockers: []
49
+ },
50
+ relay: {
51
+ mode: "relay",
52
+ source: "extension-relay",
53
+ scope: "tab",
54
+ supported: true,
55
+ ready: true,
56
+ state: "ready",
57
+ blockers: []
58
+ }
59
+ }
60
+ };
61
+ const session = {
62
+ schemaVersion: 1,
63
+ id: "sess-1",
64
+ kind: "chrome-readonly",
65
+ browser: "chrome",
66
+ target: { type: "front" },
67
+ tab: {
68
+ browser: "chrome",
69
+ windowIndex: 1,
70
+ tabIndex: 1,
71
+ title: "Shared tab",
72
+ url: "https://example.com",
73
+ attachedAt: "2026-03-30T12:00:00.000Z",
74
+ identity: {
75
+ signature: "sig",
76
+ urlKey: "https://example.com",
77
+ titleKey: "Shared tab",
78
+ origin: "https://example.com",
79
+ pathname: "/"
80
+ }
81
+ },
82
+ frontTab: {
83
+ browser: "chrome",
84
+ windowIndex: 1,
85
+ tabIndex: 1,
86
+ title: "Shared tab",
87
+ url: "https://example.com",
88
+ attachedAt: "2026-03-30T12:00:00.000Z",
89
+ identity: {
90
+ signature: "sig",
91
+ urlKey: "https://example.com",
92
+ titleKey: "Shared tab",
93
+ origin: "https://example.com",
94
+ pathname: "/"
95
+ }
96
+ },
97
+ attach: {
98
+ mode: "relay",
99
+ source: "extension-relay",
100
+ scope: "tab",
101
+ resumable: true
102
+ },
103
+ semantics: {
104
+ inspect: "shared-tab-only",
105
+ list: "saved-session",
106
+ resume: "current-shared-tab",
107
+ tabReference: {
108
+ windowIndex: "synthetic-shared-tab-position",
109
+ tabIndex: "synthetic-shared-tab-position"
110
+ }
111
+ },
112
+ capabilities: {
113
+ resume: true,
114
+ activate: false,
115
+ navigate: false,
116
+ screenshot: false
117
+ },
118
+ status: {
119
+ state: "read-only",
120
+ canAct: false
121
+ },
122
+ createdAt: "2026-03-30T12:00:00.000Z"
123
+ };
124
+ const resumedSession = {
125
+ session,
126
+ tab: session.tab,
127
+ resumedAt: "2026-03-30T12:05:00.000Z",
128
+ resolution: {
129
+ strategy: "front",
130
+ matched: true,
131
+ attachMode: "relay",
132
+ semantics: "current-shared-tab"
133
+ }
134
+ };
135
+ (0, node_test_1.default)("createHttpBridgeAdapter maps bridge operations into HTTP envelopes", async () => {
136
+ const requests = [];
137
+ const adapter = (0, http_reference_adapter_1.createHttpBridgeAdapter)({
138
+ async execute(request) {
139
+ requests.push(request);
140
+ if (request.method === "GET" && request.path === "/v1/capabilities") {
141
+ return { body: { capabilities } };
142
+ }
143
+ if (request.method === "GET" && request.path === "/v1/diagnostics?browser=chrome") {
144
+ return { body: { diagnostics } };
145
+ }
146
+ if (request.method === "POST" && request.path === "/v1/attach") {
147
+ return { body: { session } };
148
+ }
149
+ if (request.method === "POST" && request.path === "/v1/sessions/sess-1/resume") {
150
+ return { body: { resumedSession } };
151
+ }
152
+ throw new Error(`Unexpected request: ${request.method} ${request.path}`);
153
+ }
154
+ });
155
+ strict_1.default.deepEqual(await adapter.getCapabilities(), capabilities);
156
+ strict_1.default.deepEqual(await adapter.getDiagnostics("chrome"), diagnostics);
157
+ strict_1.default.deepEqual(await adapter.attach({ browser: "chrome", attachMode: "relay" }), session);
158
+ strict_1.default.deepEqual(await adapter.resume("sess-1"), resumedSession);
159
+ strict_1.default.deepEqual(requests, [
160
+ { method: "GET", path: "/v1/capabilities" },
161
+ { method: "GET", path: "/v1/diagnostics?browser=chrome" },
162
+ {
163
+ method: "POST",
164
+ path: "/v1/attach",
165
+ body: { browser: "chrome", attach: { mode: "relay" } }
166
+ },
167
+ { method: "POST", path: "/v1/sessions/sess-1/resume" }
168
+ ]);
169
+ });
170
+ (0, node_test_1.default)("createCliBridgeAdapter maps bridge operations into CLI commands", async () => {
171
+ const commands = [];
172
+ const adapter = (0, cli_reference_adapter_1.createCliBridgeAdapter)({
173
+ async execute(command) {
174
+ commands.push(command.args);
175
+ if (command.args[0] === "capabilities") {
176
+ return { stdout: JSON.stringify({ capabilities }) };
177
+ }
178
+ if (command.args[0] === "diagnostics") {
179
+ return { stdout: JSON.stringify({ diagnostics }) };
180
+ }
181
+ if (command.args[0] === "attach") {
182
+ return { stdout: JSON.stringify({ session }) };
183
+ }
184
+ if (command.args[0] === "resume") {
185
+ return { stdout: JSON.stringify({ resumedSession }) };
186
+ }
187
+ throw new Error(`Unexpected command: ${command.args.join(" ")}`);
188
+ }
189
+ });
190
+ strict_1.default.deepEqual(await adapter.getCapabilities(), capabilities);
191
+ strict_1.default.deepEqual(await adapter.getDiagnostics("chrome"), diagnostics);
192
+ strict_1.default.deepEqual(await adapter.attach({ browser: "chrome", attachMode: "relay" }), session);
193
+ strict_1.default.deepEqual(await adapter.resume("sess-1"), resumedSession);
194
+ strict_1.default.deepEqual(commands, [
195
+ ["capabilities"],
196
+ ["diagnostics", "--browser", "chrome"],
197
+ ["attach", "--browser", "chrome", "--attach-mode", "relay"],
198
+ ["resume", "--id", "sess-1"]
199
+ ]);
200
+ });
201
+ (0, node_test_1.default)("transport adapters fail clearly when required envelopes are missing", async () => {
202
+ const httpAdapter = (0, http_reference_adapter_1.createHttpBridgeAdapter)({
203
+ async execute() {
204
+ return { body: {} };
205
+ }
206
+ });
207
+ const cliAdapter = (0, cli_reference_adapter_1.createCliBridgeAdapter)({
208
+ async execute() {
209
+ return { stdout: JSON.stringify({}) };
210
+ }
211
+ });
212
+ await strict_1.default.rejects(() => httpAdapter.getCapabilities(), /Expected capabilities response to include capabilities\./);
213
+ await strict_1.default.rejects(() => cliAdapter.resume("sess-1"), /Expected resumeSession command output to include resumedSession\./);
214
+ });