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.
- package/README.md +724 -0
- package/dist/package.json +61 -0
- package/dist/src/browser/chrome.d.ts +19 -0
- package/dist/src/browser/chrome.js +778 -0
- package/dist/src/browser/index.d.ts +3 -0
- package/dist/src/browser/index.js +25 -0
- package/dist/src/browser/safari.d.ts +41 -0
- package/dist/src/browser/safari.js +827 -0
- package/dist/src/browser-attach-ux-helper.d.ts +39 -0
- package/dist/src/browser-attach-ux-helper.js +157 -0
- package/dist/src/capabilities.d.ts +3 -0
- package/dist/src/capabilities.js +182 -0
- package/dist/src/chrome-relay-error-helper.d.ts +19 -0
- package/dist/src/chrome-relay-error-helper.js +78 -0
- package/dist/src/chrome-relay-helper-cli.d.ts +2 -0
- package/dist/src/chrome-relay-helper-cli.js +97 -0
- package/dist/src/chrome-relay-helper.d.ts +29 -0
- package/dist/src/chrome-relay-helper.js +151 -0
- package/dist/src/chrome-relay-state.d.ts +23 -0
- package/dist/src/chrome-relay-state.js +108 -0
- package/dist/src/claude-code.d.ts +20 -0
- package/dist/src/claude-code.js +66 -0
- package/dist/src/cli-reference-adapter.d.ts +13 -0
- package/dist/src/cli-reference-adapter.js +48 -0
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.js +200 -0
- package/dist/src/codex.d.ts +17 -0
- package/dist/src/codex.js +25 -0
- package/dist/src/connection-ux.d.ts +61 -0
- package/dist/src/connection-ux.js +256 -0
- package/dist/src/errors.d.ts +12 -0
- package/dist/src/errors.js +58 -0
- package/dist/src/http-reference-adapter.d.ts +34 -0
- package/dist/src/http-reference-adapter.js +61 -0
- package/dist/src/http.d.ts +3 -0
- package/dist/src/http.js +161 -0
- package/dist/src/index.d.ts +17 -0
- package/dist/src/index.js +43 -0
- package/dist/src/mcp-stdio.d.ts +2 -0
- package/dist/src/mcp-stdio.js +10 -0
- package/dist/src/mcp.d.ts +25 -0
- package/dist/src/mcp.js +483 -0
- package/dist/src/reference-adapter.d.ts +32 -0
- package/dist/src/reference-adapter.js +42 -0
- package/dist/src/service/attach-service.d.ts +28 -0
- package/dist/src/service/attach-service.js +272 -0
- package/dist/src/session-metadata.d.ts +4 -0
- package/dist/src/session-metadata.js +88 -0
- package/dist/src/store/session-store.d.ts +14 -0
- package/dist/src/store/session-store.js +52 -0
- package/dist/src/target.d.ts +9 -0
- package/dist/src/target.js +61 -0
- package/dist/src/types.d.ts +397 -0
- package/dist/src/types.js +2 -0
- package/dist/tests/attach-service.test.d.ts +1 -0
- package/dist/tests/attach-service.test.js +1367 -0
- package/dist/tests/browser-attach-ux-helper.test.d.ts +1 -0
- package/dist/tests/browser-attach-ux-helper.test.js +139 -0
- package/dist/tests/chrome-relay-error-helper.test.d.ts +1 -0
- package/dist/tests/chrome-relay-error-helper.test.js +67 -0
- package/dist/tests/chrome-relay-helper.test.d.ts +1 -0
- package/dist/tests/chrome-relay-helper.test.js +142 -0
- package/dist/tests/chrome-relay-state-schema.test.d.ts +1 -0
- package/dist/tests/chrome-relay-state-schema.test.js +96 -0
- package/dist/tests/claude-code-wrapper.test.d.ts +1 -0
- package/dist/tests/claude-code-wrapper.test.js +170 -0
- package/dist/tests/codex.test.d.ts +1 -0
- package/dist/tests/codex.test.js +210 -0
- package/dist/tests/demo-client-smoke.test.d.ts +1 -0
- package/dist/tests/demo-client-smoke.test.js +405 -0
- package/dist/tests/docs-fixtures.test.d.ts +1 -0
- package/dist/tests/docs-fixtures.test.js +255 -0
- package/dist/tests/doctor-connect-wrapper.test.d.ts +1 -0
- package/dist/tests/doctor-connect-wrapper.test.js +62 -0
- package/dist/tests/fixtures/doctor-connect-cli-stub.d.ts +1 -0
- package/dist/tests/fixtures/doctor-connect-cli-stub.js +93 -0
- package/dist/tests/fixtures/public-root-cli-stub.d.ts +210 -0
- package/dist/tests/fixtures/public-root-cli-stub.js +143 -0
- package/dist/tests/fixtures/public-root-consumer.js +67 -0
- package/dist/tests/mcp.test.d.ts +1 -0
- package/dist/tests/mcp.test.js +345 -0
- package/dist/tests/public-consumer-helpers.test.d.ts +1 -0
- package/dist/tests/public-consumer-helpers.test.js +33 -0
- package/dist/tests/public-package-git-consumption.test.d.ts +1 -0
- package/dist/tests/public-package-git-consumption.test.js +56 -0
- package/dist/tests/public-root-consumer-smoke.test.d.ts +1 -0
- package/dist/tests/public-root-consumer-smoke.test.js +214 -0
- package/dist/tests/reference-adapter.test.d.ts +1 -0
- package/dist/tests/reference-adapter.test.js +220 -0
- package/dist/tests/transport-reference-adapters.test.d.ts +1 -0
- package/dist/tests/transport-reference-adapters.test.js +214 -0
- 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
|
+
});
|