@flrande/browserctl 0.1.0 → 0.2.0-dev.12.1

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,9 +1,10 @@
1
- import { describe, expect, it } from "vitest";
1
+ import { beforeEach, describe, expect, it } from "vitest";
2
2
  import { PassThrough } from "node:stream";
3
3
  import { createConnection } from "node:net";
4
4
 
5
5
  import { createContainer, loadBrowserdConfig } from "./container";
6
6
  import { bootstrapBrowserd } from "./bootstrap";
7
+ import { reserveLoopbackPort } from "./test-port";
7
8
 
8
9
  function waitForNextJsonLine(stream: PassThrough): Promise<Record<string, unknown>> {
9
10
  return new Promise((resolve, reject) => {
@@ -78,11 +79,33 @@ function sendTcpToolRequest(
78
79
  });
79
80
  }
80
81
 
82
+ let testRelayPort = 0;
83
+ let testTcpPort = 0;
84
+
85
+ beforeEach(async () => {
86
+ testRelayPort = await reserveLoopbackPort();
87
+ testTcpPort = await reserveLoopbackPort();
88
+ while (testTcpPort === testRelayPort) {
89
+ testTcpPort = await reserveLoopbackPort();
90
+ }
91
+ });
92
+
93
+ function createTestEnv(
94
+ overrides: Record<string, string | undefined> = {}
95
+ ): Record<string, string | undefined> {
96
+ return {
97
+ BROWSERD_CHROME_RELAY_URL: `http://127.0.0.1:${testRelayPort}`,
98
+ ...overrides
99
+ };
100
+ }
101
+
81
102
  describe("browserd container", () => {
82
103
  it("uses secure defaults for new security config fields", () => {
83
104
  const config = loadBrowserdConfig({});
84
105
 
85
- expect(config.defaultDriver).toBe("managed-local");
106
+ expect(config.defaultDriver).toBe("chrome-relay");
107
+ expect(config.chromeRelayMode).toBe("extension");
108
+ expect(config.chromeRelayExtensionToken).toBe("browserctl-relay");
86
109
  expect(config.managedLocalEnabled).toBe(true);
87
110
  expect(config.uploadRoot).toBeUndefined();
88
111
  expect(config.downloadRoot).toBeUndefined();
@@ -91,12 +114,13 @@ describe("browserd container", () => {
91
114
  });
92
115
 
93
116
  it("registers managed-local, managed, chrome-relay, and remote-cdp drivers by default", () => {
94
- const c = createContainer();
117
+ const c = createContainer(loadBrowserdConfig(createTestEnv()));
95
118
 
96
119
  expect(c.drivers.has("managed-local")).toBe(true);
97
120
  expect(c.drivers.has("managed")).toBe(true);
98
121
  expect(c.drivers.has("chrome-relay")).toBe(true);
99
122
  expect(c.drivers.has("remote-cdp")).toBe(true);
123
+ c.close();
100
124
  });
101
125
 
102
126
  it("uses env override in loadBrowserdConfig", () => {
@@ -110,7 +134,7 @@ describe("browserd container", () => {
110
134
 
111
135
  expect(config.remoteCdpUrl).toBe("http://127.0.0.1:9333/devtools/browser/override");
112
136
  expect(config.chromeRelayUrl).toBe("http://127.0.0.1:9223");
113
- expect(config.defaultDriver).toBe("managed-local");
137
+ expect(config.defaultDriver).toBe("chrome-relay");
114
138
  expect(config.managedLocalEnabled).toBe(true);
115
139
  expect(config.uploadRoot).toBe("C:\\safe\\uploads");
116
140
  expect(config.downloadRoot).toBe("C:\\safe\\downloads");
@@ -132,31 +156,42 @@ describe("browserd container", () => {
132
156
  expect(config.chromeRelayExtensionRequestTimeoutMs).toBe(7000);
133
157
  });
134
158
 
135
- it("requires extension relay token when extension mode is enabled", () => {
136
- expect(() =>
137
- loadBrowserdConfig({
138
- BROWSERD_CHROME_RELAY_MODE: "extension"
139
- })
140
- ).toThrow("BROWSERD_CHROME_RELAY_EXTENSION_TOKEN");
159
+ it("respects explicit cdp relay mode override", () => {
160
+ const config = loadBrowserdConfig({
161
+ BROWSERD_CHROME_RELAY_MODE: "cdp"
162
+ });
163
+
164
+ expect(config.chromeRelayMode).toBe("cdp");
165
+ });
166
+
167
+ it("uses default extension relay token when extension mode is enabled", () => {
168
+ const config = loadBrowserdConfig({
169
+ BROWSERD_CHROME_RELAY_MODE: "extension"
170
+ });
171
+
172
+ expect(config.chromeRelayMode).toBe("extension");
173
+ expect(config.chromeRelayExtensionToken).toBe("browserctl-relay");
141
174
  });
142
175
 
143
- it("defaults to managed when managed-local is explicitly disabled", () => {
176
+ it("keeps chrome-relay as default when managed-local is explicitly disabled", () => {
144
177
  const config = loadBrowserdConfig({
145
178
  BROWSERD_MANAGED_LOCAL_ENABLED: "false"
146
179
  });
147
180
 
148
181
  expect(config.managedLocalEnabled).toBe(false);
149
- expect(config.defaultDriver).toBe("managed");
182
+ expect(config.defaultDriver).toBe("chrome-relay");
150
183
  });
151
184
 
152
185
  it("does not register managed-local driver when explicitly disabled", () => {
153
186
  const c = createContainer(
154
187
  loadBrowserdConfig({
188
+ ...createTestEnv(),
155
189
  BROWSERD_MANAGED_LOCAL_ENABLED: "false"
156
190
  })
157
191
  );
158
192
 
159
193
  expect(c.drivers.has("managed-local")).toBe(false);
194
+ c.close();
160
195
  });
161
196
  });
162
197
 
@@ -165,7 +200,7 @@ describe("browserd bootstrap", () => {
165
200
  const input = new PassThrough();
166
201
  const output = new PassThrough();
167
202
 
168
- const runtime = bootstrapBrowserd({ input, output });
203
+ const runtime = bootstrapBrowserd({ env: createTestEnv(), input, output });
169
204
 
170
205
  expect(runtime.mcpStdioStarted).toBe(true);
171
206
  expect(runtime.container.drivers.has("managed")).toBe(true);
@@ -179,13 +214,13 @@ describe("browserd bootstrap", () => {
179
214
  });
180
215
 
181
216
  it("supports tcp transport mode for persistent daemon use", async () => {
182
- const port = 41419;
217
+ const port = testTcpPort;
183
218
  const runtime = bootstrapBrowserd({
184
- env: {
219
+ env: createTestEnv({
185
220
  BROWSERD_TRANSPORT: "tcp",
186
221
  BROWSERD_PORT: String(port),
187
222
  BROWSERD_AUTH_TOKEN: "tcp-token"
188
- }
223
+ })
189
224
  });
190
225
 
191
226
  const response = await sendTcpToolRequest(port, {
@@ -215,10 +250,10 @@ describe("browserd bootstrap", () => {
215
250
  try {
216
251
  expect(() => {
217
252
  runtime = bootstrapBrowserd({
218
- env: {
253
+ env: createTestEnv({
219
254
  BROWSERD_TRANSPORT: "tcp",
220
- BROWSERD_PORT: "41420"
221
- }
255
+ BROWSERD_PORT: String(testTcpPort)
256
+ })
222
257
  });
223
258
  }).toThrow("BROWSERD_AUTH_TOKEN");
224
259
  } finally {
@@ -229,7 +264,7 @@ describe("browserd bootstrap", () => {
229
264
  it("processes line-delimited requests and writes response with id", async () => {
230
265
  const input = new PassThrough();
231
266
  const output = new PassThrough();
232
- const runtime = bootstrapBrowserd({ input, output, stdioProtocol: "legacy" });
267
+ const runtime = bootstrapBrowserd({ env: createTestEnv(), input, output, stdioProtocol: "legacy" });
233
268
 
234
269
  const response = await sendToolRequest(input, output, {
235
270
  id: "request-1",
@@ -247,7 +282,7 @@ describe("browserd bootstrap", () => {
247
282
  expect(response.data).toMatchObject({
248
283
  kind: "browserd",
249
284
  ready: true,
250
- driver: "managed-local"
285
+ driver: "chrome-relay"
251
286
  });
252
287
 
253
288
  runtime.close();
@@ -259,10 +294,10 @@ describe("browserd bootstrap", () => {
259
294
  const input = new PassThrough();
260
295
  const output = new PassThrough();
261
296
  const runtime = bootstrapBrowserd({
262
- env: {
297
+ env: createTestEnv({
263
298
  BROWSERD_MANAGED_LOCAL_ENABLED: "true",
264
299
  BROWSERD_DEFAULT_DRIVER: "managed-local"
265
- },
300
+ }),
266
301
  input,
267
302
  output,
268
303
  stdioProtocol: "legacy"
@@ -297,10 +332,10 @@ describe("browserd bootstrap", () => {
297
332
  const input = new PassThrough();
298
333
  const output = new PassThrough();
299
334
  const runtime = bootstrapBrowserd({
300
- env: {
335
+ env: createTestEnv({
301
336
  BROWSERD_DEFAULT_DRIVER: "managed-local",
302
337
  BROWSERD_MANAGED_LOCAL_ENABLED: "false"
303
- },
338
+ }),
304
339
  input,
305
340
  output,
306
341
  stdioProtocol: "legacy"
@@ -331,9 +366,9 @@ describe("browserd bootstrap", () => {
331
366
  const input = new PassThrough();
332
367
  const output = new PassThrough();
333
368
  const runtime = bootstrapBrowserd({
334
- env: {
369
+ env: createTestEnv({
335
370
  BROWSERD_AUTH_TOKEN: "secret-token"
336
- },
371
+ }),
337
372
  input,
338
373
  output,
339
374
  stdioProtocol: "legacy"
@@ -362,9 +397,9 @@ describe("browserd bootstrap", () => {
362
397
  const input = new PassThrough();
363
398
  const output = new PassThrough();
364
399
  const runtime = bootstrapBrowserd({
365
- env: {
400
+ env: createTestEnv({
366
401
  BROWSERD_AUTH_TOKEN: "secret-token"
367
- },
402
+ }),
368
403
  input,
369
404
  output,
370
405
  stdioProtocol: "legacy"
@@ -394,11 +429,11 @@ describe("browserd bootstrap", () => {
394
429
  const input = new PassThrough();
395
430
  const output = new PassThrough();
396
431
  const runtime = bootstrapBrowserd({
397
- env: {
432
+ env: createTestEnv({
398
433
  BROWSERD_DEFAULT_DRIVER: "managed",
399
434
  BROWSERD_AUTH_TOKEN: "secret-token",
400
435
  BROWSERD_AUTH_SCOPES: "read"
401
- },
436
+ }),
402
437
  input,
403
438
  output,
404
439
  stdioProtocol: "legacy"
@@ -429,11 +464,11 @@ describe("browserd bootstrap", () => {
429
464
  const input = new PassThrough();
430
465
  const output = new PassThrough();
431
466
  const runtime = bootstrapBrowserd({
432
- env: {
467
+ env: createTestEnv({
433
468
  BROWSERD_DEFAULT_DRIVER: "managed",
434
469
  BROWSERD_AUTH_TOKEN: "secret-token",
435
470
  BROWSERD_AUTH_SCOPES: "read,act"
436
- },
471
+ }),
437
472
  input,
438
473
  output,
439
474
  stdioProtocol: "legacy"
@@ -494,10 +529,10 @@ describe("browserd bootstrap", () => {
494
529
  const input = new PassThrough();
495
530
  const output = new PassThrough();
496
531
  const runtime = bootstrapBrowserd({
497
- env: {
532
+ env: createTestEnv({
498
533
  BROWSERD_DEFAULT_DRIVER: "managed",
499
534
  BROWSERD_UPLOAD_ROOT: "C:\\allowed\\uploads"
500
- },
535
+ }),
501
536
  input,
502
537
  output,
503
538
  stdioProtocol: "legacy"
@@ -539,9 +574,9 @@ describe("browserd bootstrap", () => {
539
574
  const input = new PassThrough();
540
575
  const output = new PassThrough();
541
576
  const runtime = bootstrapBrowserd({
542
- env: {
577
+ env: createTestEnv({
543
578
  BROWSERD_DEFAULT_DRIVER: "managed"
544
- },
579
+ }),
545
580
  input,
546
581
  output,
547
582
  stdioProtocol: "legacy"
@@ -584,10 +619,10 @@ describe("browserd bootstrap", () => {
584
619
  const input = new PassThrough();
585
620
  const output = new PassThrough();
586
621
  const runtime = bootstrapBrowserd({
587
- env: {
622
+ env: createTestEnv({
588
623
  BROWSERD_DEFAULT_DRIVER: "managed",
589
624
  BROWSERD_DOWNLOAD_ROOT: "C:\\allowed\\downloads"
590
- },
625
+ }),
591
626
  input,
592
627
  output,
593
628
  stdioProtocol: "legacy"
@@ -646,9 +681,9 @@ describe("browserd bootstrap", () => {
646
681
  const input = new PassThrough();
647
682
  const output = new PassThrough();
648
683
  const runtime = bootstrapBrowserd({
649
- env: {
684
+ env: createTestEnv({
650
685
  BROWSERD_DEFAULT_DRIVER: "managed"
651
- },
686
+ }),
652
687
  input,
653
688
  output,
654
689
  stdioProtocol: "legacy"
@@ -690,9 +725,9 @@ describe("browserd bootstrap", () => {
690
725
  const input = new PassThrough();
691
726
  const output = new PassThrough();
692
727
  const runtime = bootstrapBrowserd({
693
- env: {
728
+ env: createTestEnv({
694
729
  BROWSERD_DEFAULT_DRIVER: "managed"
695
- },
730
+ }),
696
731
  input,
697
732
  output,
698
733
  stdioProtocol: "legacy"
@@ -739,9 +774,9 @@ describe("browserd bootstrap", () => {
739
774
  const input = new PassThrough();
740
775
  const output = new PassThrough();
741
776
  const runtime = bootstrapBrowserd({
742
- env: {
777
+ env: createTestEnv({
743
778
  BROWSERD_DEFAULT_DRIVER: "managed"
744
- },
779
+ }),
745
780
  input,
746
781
  output,
747
782
  stdioProtocol: "legacy"
@@ -819,7 +854,7 @@ describe("browserd bootstrap", () => {
819
854
  it("preserves id and trace/session metadata when queue-level error handling runs", async () => {
820
855
  const input = new PassThrough();
821
856
  const output = new PassThrough();
822
- const runtime = bootstrapBrowserd({ input, output, stdioProtocol: "legacy" });
857
+ const runtime = bootstrapBrowserd({ env: createTestEnv(), input, output, stdioProtocol: "legacy" });
823
858
 
824
859
  const originalWrite = output.write.bind(output);
825
860
  let shouldThrow = true;
@@ -0,0 +1,26 @@
1
+ import { createServer } from "node:net";
2
+
3
+ export async function reserveLoopbackPort(): Promise<number> {
4
+ return await new Promise<number>((resolve, reject) => {
5
+ const server = createServer();
6
+
7
+ server.once("error", reject);
8
+ server.listen(0, "127.0.0.1", () => {
9
+ const address = server.address();
10
+ if (typeof address !== "object" || address === null) {
11
+ server.close(() => reject(new Error("Failed to reserve loopback port.")));
12
+ return;
13
+ }
14
+
15
+ const port = address.port;
16
+ server.close((error) => {
17
+ if (error !== undefined) {
18
+ reject(error);
19
+ return;
20
+ }
21
+
22
+ resolve(port);
23
+ });
24
+ });
25
+ });
26
+ }