@flrande/browserctl 0.4.0-dev.15.1 → 0.5.0-dev.19.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.
Files changed (52) hide show
  1. package/apps/browserctl/src/commands/act.test.ts +71 -0
  2. package/apps/browserctl/src/commands/act.ts +45 -1
  3. package/apps/browserctl/src/commands/command-wrappers.test.ts +302 -0
  4. package/apps/browserctl/src/commands/console-list.test.ts +102 -0
  5. package/apps/browserctl/src/commands/console-list.ts +89 -1
  6. package/apps/browserctl/src/commands/har-export.test.ts +112 -0
  7. package/apps/browserctl/src/commands/har-export.ts +120 -0
  8. package/apps/browserctl/src/commands/memory-delete.ts +20 -0
  9. package/apps/browserctl/src/commands/memory-inspect.ts +20 -0
  10. package/apps/browserctl/src/commands/memory-list.ts +90 -0
  11. package/apps/browserctl/src/commands/memory-mode-set.ts +29 -0
  12. package/apps/browserctl/src/commands/memory-purge.ts +16 -0
  13. package/apps/browserctl/src/commands/memory-resolve.ts +56 -0
  14. package/apps/browserctl/src/commands/memory-status.ts +16 -0
  15. package/apps/browserctl/src/commands/memory-ttl-set.ts +28 -0
  16. package/apps/browserctl/src/commands/memory-upsert.ts +142 -0
  17. package/apps/browserctl/src/commands/network-list.test.ts +110 -0
  18. package/apps/browserctl/src/commands/network-list.ts +112 -0
  19. package/apps/browserctl/src/commands/session-drop.test.ts +36 -0
  20. package/apps/browserctl/src/commands/session-drop.ts +16 -0
  21. package/apps/browserctl/src/commands/session-list.test.ts +81 -0
  22. package/apps/browserctl/src/commands/session-list.ts +70 -0
  23. package/apps/browserctl/src/commands/trace-get.test.ts +61 -0
  24. package/apps/browserctl/src/commands/trace-get.ts +62 -0
  25. package/apps/browserctl/src/commands/wait-element.test.ts +80 -0
  26. package/apps/browserctl/src/commands/wait-element.ts +76 -0
  27. package/apps/browserctl/src/commands/wait-text.test.ts +110 -0
  28. package/apps/browserctl/src/commands/wait-text.ts +93 -0
  29. package/apps/browserctl/src/commands/wait-url.test.ts +80 -0
  30. package/apps/browserctl/src/commands/wait-url.ts +76 -0
  31. package/apps/browserctl/src/main.dispatch.test.ts +206 -1
  32. package/apps/browserctl/src/main.test.ts +30 -0
  33. package/apps/browserctl/src/main.ts +246 -4
  34. package/apps/browserd/src/container.ts +1603 -48
  35. package/apps/browserd/src/main.test.ts +538 -1
  36. package/apps/browserd/src/tool-matrix.test.ts +492 -3
  37. package/package.json +5 -1
  38. package/packages/core/src/driver.ts +1 -1
  39. package/packages/core/src/index.ts +1 -0
  40. package/packages/core/src/navigation-memory.test.ts +259 -0
  41. package/packages/core/src/navigation-memory.ts +360 -0
  42. package/packages/core/src/session-store.test.ts +33 -0
  43. package/packages/core/src/session-store.ts +111 -6
  44. package/packages/driver-chrome-relay/src/chrome-relay-driver.test.ts +112 -2
  45. package/packages/driver-chrome-relay/src/chrome-relay-driver.ts +233 -10
  46. package/packages/driver-managed/src/managed-driver.test.ts +124 -0
  47. package/packages/driver-managed/src/managed-driver.ts +233 -17
  48. package/packages/driver-managed/src/managed-local-driver.test.ts +104 -2
  49. package/packages/driver-managed/src/managed-local-driver.ts +232 -10
  50. package/packages/driver-remote-cdp/src/remote-cdp-driver.test.ts +112 -2
  51. package/packages/driver-remote-cdp/src/remote-cdp-driver.ts +232 -10
  52. package/packages/transport-mcp-stdio/src/tool-map.ts +18 -1
@@ -41,7 +41,9 @@ function sendToolRequest(
41
41
  return responsePromise;
42
42
  }
43
43
 
44
- async function createManagedLegacyRuntime() {
44
+ async function createManagedLegacyRuntime(
45
+ overrides: Record<string, string | undefined> = {}
46
+ ) {
45
47
  const relayPort = await reserveLoopbackPort();
46
48
 
47
49
  const input = new PassThrough();
@@ -49,7 +51,8 @@ async function createManagedLegacyRuntime() {
49
51
  const runtime = bootstrapBrowserd({
50
52
  env: {
51
53
  BROWSERD_DEFAULT_DRIVER: "managed",
52
- BROWSERD_CHROME_RELAY_URL: `http://127.0.0.1:${relayPort}`
54
+ BROWSERD_CHROME_RELAY_URL: `http://127.0.0.1:${relayPort}`,
55
+ ...overrides
53
56
  },
54
57
  input,
55
58
  output,
@@ -77,6 +80,9 @@ describe("browserd tool matrix", () => {
77
80
  "browser.dom.queryAll",
78
81
  "browser.element.screenshot",
79
82
  "browser.a11y.snapshot",
83
+ "browser.wait.element",
84
+ "browser.wait.text",
85
+ "browser.wait.url",
80
86
  "browser.cookie.get",
81
87
  "browser.cookie.set",
82
88
  "browser.cookie.clear",
@@ -87,7 +93,11 @@ describe("browserd tool matrix", () => {
87
93
  "browser.act",
88
94
  "browser.dialog.arm",
89
95
  "browser.download.trigger",
90
- "browser.network.waitFor"
96
+ "browser.network.list",
97
+ "browser.network.harExport",
98
+ "browser.network.waitFor",
99
+ "browser.console.list",
100
+ "browser.session.drop"
91
101
  ] as const;
92
102
 
93
103
  for (const [index, toolName] of toolNames.entries()) {
@@ -112,6 +122,392 @@ describe("browserd tool matrix", () => {
112
122
  }
113
123
  });
114
124
 
125
+ it("routes browser.memory tools with validation and deterministic state", async () => {
126
+ const { input, output, runtime } = await createManagedLegacyRuntime();
127
+
128
+ try {
129
+ const statusResponse = await sendToolRequest(input, output, {
130
+ id: "request-memory-status",
131
+ name: "browser.memory.status",
132
+ traceId: "trace:memory:status",
133
+ arguments: {
134
+ sessionId: "session:memory"
135
+ }
136
+ });
137
+ expect(statusResponse.ok).toBe(true);
138
+ const statusPayload = statusResponse.data as {
139
+ path?: unknown;
140
+ mode?: unknown;
141
+ ttlDays?: unknown;
142
+ totalEntries?: unknown;
143
+ };
144
+ expect(typeof statusPayload.path).toBe("string");
145
+ expect((statusPayload.path as string).length).toBeGreaterThan(0);
146
+ expect(statusPayload.mode).toBe("ask");
147
+ expect(typeof statusPayload.ttlDays).toBe("number");
148
+ expect(typeof statusPayload.totalEntries).toBe("number");
149
+
150
+ const resolveMissingArgResponse = await sendToolRequest(input, output, {
151
+ id: "request-memory-resolve-missing",
152
+ name: "browser.memory.resolve",
153
+ traceId: "trace:memory:resolve:missing",
154
+ arguments: {
155
+ sessionId: "session:memory"
156
+ }
157
+ });
158
+ expect(resolveMissingArgResponse.ok).toBe(false);
159
+ expect(resolveMissingArgResponse.error).toMatchObject({
160
+ code: "E_INVALID_ARG"
161
+ });
162
+
163
+ const upsertMissingArgResponse = await sendToolRequest(input, output, {
164
+ id: "request-memory-upsert-missing",
165
+ name: "browser.memory.upsert",
166
+ traceId: "trace:memory:upsert:missing",
167
+ arguments: {
168
+ sessionId: "session:memory"
169
+ }
170
+ });
171
+ expect(upsertMissingArgResponse.ok).toBe(false);
172
+ expect(upsertMissingArgResponse.error).toMatchObject({
173
+ code: "E_INVALID_ARG"
174
+ });
175
+
176
+ const upsertFreeTextSignalResponse = await sendToolRequest(input, output, {
177
+ id: "request-memory-upsert-free-text-signal",
178
+ name: "browser.memory.upsert",
179
+ traceId: "trace:memory:upsert:free-text-signal",
180
+ arguments: {
181
+ sessionId: "session:memory",
182
+ domain: "example.com",
183
+ profileId: "managed",
184
+ intentKey: "checkout",
185
+ signals: [
186
+ {
187
+ kind: "urlPattern",
188
+ value: "this is copied page body text that should never be stored"
189
+ }
190
+ ],
191
+ confirmed: true
192
+ }
193
+ });
194
+ expect(upsertFreeTextSignalResponse.ok).toBe(false);
195
+ expect(upsertFreeTextSignalResponse.error).toMatchObject({
196
+ code: "E_INVALID_ARG"
197
+ });
198
+
199
+ const upsertResponse = await sendToolRequest(input, output, {
200
+ id: "request-memory-upsert",
201
+ name: "browser.memory.upsert",
202
+ traceId: "trace:memory:upsert",
203
+ arguments: {
204
+ sessionId: "session:memory",
205
+ domain: "example.com",
206
+ profileId: "managed",
207
+ intentKey: "checkout",
208
+ signals: [
209
+ {
210
+ kind: "urlPattern",
211
+ value: "/checkout"
212
+ }
213
+ ],
214
+ confidence: 0.8,
215
+ confirmed: true
216
+ }
217
+ });
218
+ expect(upsertResponse.ok).toBe(true);
219
+ const upsertPayload = upsertResponse.data as {
220
+ created?: unknown;
221
+ entry?: {
222
+ id?: unknown;
223
+ domain?: unknown;
224
+ profileId?: unknown;
225
+ intentKey?: unknown;
226
+ confidence?: unknown;
227
+ hitCount?: unknown;
228
+ };
229
+ };
230
+ expect(upsertPayload.created).toBe(true);
231
+ expect(upsertPayload.entry?.domain).toBe("example.com");
232
+ expect(upsertPayload.entry?.profileId).toBe("managed");
233
+ expect(upsertPayload.entry?.intentKey).toBe("checkout");
234
+ expect(upsertPayload.entry?.confidence).toBe(0.8);
235
+ expect(upsertPayload.entry?.hitCount).toBe(0);
236
+ expect(typeof upsertPayload.entry?.id).toBe("string");
237
+ expect((upsertPayload.entry?.id as string).length).toBeGreaterThan(0);
238
+
239
+ const memoryId = upsertPayload.entry?.id as string;
240
+
241
+ const listResponse = await sendToolRequest(input, output, {
242
+ id: "request-memory-list",
243
+ name: "browser.memory.list",
244
+ traceId: "trace:memory:list",
245
+ arguments: {
246
+ sessionId: "session:memory"
247
+ }
248
+ });
249
+ expect(listResponse.ok).toBe(true);
250
+ expect(listResponse.data).toMatchObject({
251
+ entries: [{ id: memoryId }]
252
+ });
253
+
254
+ const inspectResponse = await sendToolRequest(input, output, {
255
+ id: "request-memory-inspect",
256
+ name: "browser.memory.inspect",
257
+ traceId: "trace:memory:inspect",
258
+ arguments: {
259
+ sessionId: "session:memory",
260
+ id: memoryId
261
+ }
262
+ });
263
+ expect(inspectResponse.ok).toBe(true);
264
+ expect(inspectResponse.data).toMatchObject({
265
+ entry: {
266
+ id: memoryId
267
+ }
268
+ });
269
+
270
+ const resolveResponse = await sendToolRequest(input, output, {
271
+ id: "request-memory-resolve",
272
+ name: "browser.memory.resolve",
273
+ traceId: "trace:memory:resolve",
274
+ arguments: {
275
+ sessionId: "session:memory",
276
+ domain: "example.com",
277
+ profileId: "managed",
278
+ intentKey: "checkout"
279
+ }
280
+ });
281
+ expect(resolveResponse.ok).toBe(true);
282
+ expect(resolveResponse.data).toMatchObject({
283
+ hit: true,
284
+ entry: {
285
+ id: memoryId,
286
+ hitCount: 1
287
+ }
288
+ });
289
+
290
+ const deleteResponse = await sendToolRequest(input, output, {
291
+ id: "request-memory-delete",
292
+ name: "browser.memory.delete",
293
+ traceId: "trace:memory:delete",
294
+ arguments: {
295
+ sessionId: "session:memory",
296
+ id: memoryId
297
+ }
298
+ });
299
+ expect(deleteResponse.ok).toBe(true);
300
+ expect(deleteResponse.data).toMatchObject({
301
+ deleted: true
302
+ });
303
+
304
+ const purgeResponse = await sendToolRequest(input, output, {
305
+ id: "request-memory-purge",
306
+ name: "browser.memory.purge",
307
+ traceId: "trace:memory:purge",
308
+ arguments: {
309
+ sessionId: "session:memory"
310
+ }
311
+ });
312
+ expect(purgeResponse.ok).toBe(true);
313
+ expect(purgeResponse.data).toMatchObject({
314
+ purged: 0
315
+ });
316
+
317
+ const modeSetResponse = await sendToolRequest(input, output, {
318
+ id: "request-memory-mode-set",
319
+ name: "browser.memory.mode.set",
320
+ traceId: "trace:memory:mode:set",
321
+ arguments: {
322
+ sessionId: "session:memory",
323
+ mode: "ask"
324
+ }
325
+ });
326
+ expect(modeSetResponse.ok).toBe(true);
327
+ expect(modeSetResponse.data).toMatchObject({
328
+ mode: "ask"
329
+ });
330
+
331
+ const modeOffResponse = await sendToolRequest(input, output, {
332
+ id: "request-memory-mode-set-off",
333
+ name: "browser.memory.mode.set",
334
+ traceId: "trace:memory:mode:set:off",
335
+ arguments: {
336
+ sessionId: "session:memory",
337
+ mode: "off"
338
+ }
339
+ });
340
+ expect(modeOffResponse.ok).toBe(true);
341
+ expect(modeOffResponse.data).toMatchObject({
342
+ mode: "off"
343
+ });
344
+
345
+ const policyRejectResponse = await sendToolRequest(input, output, {
346
+ id: "request-memory-upsert-policy-reject",
347
+ name: "browser.memory.upsert",
348
+ traceId: "trace:memory:upsert:policy:reject",
349
+ arguments: {
350
+ sessionId: "session:memory",
351
+ domain: "example.com",
352
+ profileId: "managed",
353
+ intentKey: "checkout",
354
+ signals: [
355
+ {
356
+ kind: "urlPattern",
357
+ value: "/checkout"
358
+ }
359
+ ],
360
+ confidence: 0.6
361
+ }
362
+ });
363
+ expect(policyRejectResponse.ok).toBe(false);
364
+ expect(policyRejectResponse.error).toMatchObject({
365
+ code: "E_PERMISSION"
366
+ });
367
+
368
+ const ttlSetResponse = await sendToolRequest(input, output, {
369
+ id: "request-memory-ttl-set",
370
+ name: "browser.memory.ttl.set",
371
+ traceId: "trace:memory:ttl:set",
372
+ arguments: {
373
+ sessionId: "session:memory",
374
+ ttlDays: 7
375
+ }
376
+ });
377
+ expect(ttlSetResponse.ok).toBe(true);
378
+ expect(ttlSetResponse.data).toMatchObject({
379
+ ttlDays: 7
380
+ });
381
+ } finally {
382
+ runtime.close();
383
+ input.end();
384
+ output.end();
385
+ }
386
+ });
387
+
388
+ it("records memory-hit marker in browser.trace.get after browser.memory.resolve", async () => {
389
+ const { input, output, runtime } = await createManagedLegacyRuntime();
390
+
391
+ try {
392
+ const upsertResponse = await sendToolRequest(input, output, {
393
+ id: "request-memory-hit-upsert",
394
+ name: "browser.memory.upsert",
395
+ traceId: "trace:memory:hit:upsert",
396
+ arguments: {
397
+ sessionId: "session:memory-hit",
398
+ domain: "example.com",
399
+ profileId: "managed",
400
+ intentKey: "checkout",
401
+ signals: [
402
+ {
403
+ kind: "urlPattern",
404
+ value: "/checkout"
405
+ }
406
+ ],
407
+ confidence: 0.9,
408
+ confirmed: true
409
+ }
410
+ });
411
+ expect(upsertResponse.ok).toBe(true);
412
+
413
+ const resolveResponse = await sendToolRequest(input, output, {
414
+ id: "request-memory-hit-resolve",
415
+ name: "browser.memory.resolve",
416
+ traceId: "trace:memory:hit:resolve",
417
+ arguments: {
418
+ sessionId: "session:memory-hit",
419
+ domain: "example.com",
420
+ profileId: "managed",
421
+ intentKey: "checkout"
422
+ }
423
+ });
424
+ expect(resolveResponse.ok).toBe(true);
425
+ expect(resolveResponse.data).toMatchObject({
426
+ hit: true
427
+ });
428
+
429
+ const traceGetResponse = await sendToolRequest(input, output, {
430
+ id: "request-memory-hit-trace-get",
431
+ name: "browser.trace.get",
432
+ traceId: "trace:memory:hit:trace-get",
433
+ arguments: {
434
+ sessionId: "session:memory-hit",
435
+ limit: 20
436
+ }
437
+ });
438
+ expect(traceGetResponse.ok).toBe(true);
439
+ const tracePayload = traceGetResponse.data as {
440
+ keyResponses?: Array<{
441
+ kind?: string;
442
+ data?: {
443
+ memoryHit?: boolean;
444
+ };
445
+ }>;
446
+ };
447
+ expect(tracePayload.keyResponses?.some((response) =>
448
+ response.kind === "memory.resolve" && response.data?.memoryHit === true
449
+ )).toBe(true);
450
+ } finally {
451
+ runtime.close();
452
+ input.end();
453
+ output.end();
454
+ }
455
+ });
456
+
457
+ it("keeps browser.memory disabled when BROWSERD_MEMORY_ENABLED is false", async () => {
458
+ const { input, output, runtime } = await createManagedLegacyRuntime({
459
+ BROWSERD_MEMORY_ENABLED: "false"
460
+ });
461
+
462
+ try {
463
+ const statusResponse = await sendToolRequest(input, output, {
464
+ id: "request-memory-disabled-status",
465
+ name: "browser.memory.status",
466
+ traceId: "trace:memory:disabled:status",
467
+ arguments: {
468
+ sessionId: "session:memory:disabled"
469
+ }
470
+ });
471
+
472
+ expect(statusResponse.ok).toBe(true);
473
+ expect(statusResponse.data).toMatchObject({
474
+ mode: "off"
475
+ });
476
+
477
+ const modeSetResponse = await sendToolRequest(input, output, {
478
+ id: "request-memory-disabled-mode-set",
479
+ name: "browser.memory.mode.set",
480
+ traceId: "trace:memory:disabled:mode:set",
481
+ arguments: {
482
+ sessionId: "session:memory:disabled",
483
+ mode: "auto"
484
+ }
485
+ });
486
+
487
+ expect(modeSetResponse.ok).toBe(false);
488
+ expect(modeSetResponse.error).toMatchObject({
489
+ code: "E_PERMISSION"
490
+ });
491
+
492
+ const statusAfterResponse = await sendToolRequest(input, output, {
493
+ id: "request-memory-disabled-status-after",
494
+ name: "browser.memory.status",
495
+ traceId: "trace:memory:disabled:status:after",
496
+ arguments: {
497
+ sessionId: "session:memory:disabled"
498
+ }
499
+ });
500
+ expect(statusAfterResponse.ok).toBe(true);
501
+ expect(statusAfterResponse.data).toMatchObject({
502
+ mode: "off"
503
+ });
504
+ } finally {
505
+ runtime.close();
506
+ input.end();
507
+ output.end();
508
+ }
509
+ });
510
+
115
511
  it("returns E_DRIVER_UNAVAILABLE for structured action tools on managed driver", async () => {
116
512
  const { input, output, runtime } = await createManagedLegacyRuntime();
117
513
 
@@ -159,6 +555,27 @@ describe("browserd tool matrix", () => {
159
555
  targetId
160
556
  }
161
557
  },
558
+ {
559
+ name: "browser.wait.element",
560
+ arguments: {
561
+ sessionId: "session:unavailable",
562
+ targetId,
563
+ selector: "#ready",
564
+ timeoutMs: 5,
565
+ pollMs: 1
566
+ }
567
+ },
568
+ {
569
+ name: "browser.wait.text",
570
+ arguments: {
571
+ sessionId: "session:unavailable",
572
+ targetId,
573
+ text: "ready",
574
+ selector: "#status",
575
+ timeoutMs: 5,
576
+ pollMs: 1
577
+ }
578
+ },
162
579
  {
163
580
  name: "browser.cookie.get",
164
581
  arguments: {
@@ -374,6 +791,78 @@ describe("browserd tool matrix", () => {
374
791
  code: "E_TIMEOUT"
375
792
  });
376
793
 
794
+ const networkListResponse = await sendToolRequest(input, output, {
795
+ id: "request-flow-network-list",
796
+ name: "browser.network.list",
797
+ traceId: "trace:matrix:network-list",
798
+ arguments: {
799
+ sessionId: "session:flow",
800
+ targetId
801
+ }
802
+ });
803
+ expect(networkListResponse.ok).toBe(true);
804
+ expect(networkListResponse.data).toMatchObject({
805
+ driver: "managed",
806
+ targetId,
807
+ requests: []
808
+ });
809
+
810
+ const harExportResponse = await sendToolRequest(input, output, {
811
+ id: "request-flow-har-export",
812
+ name: "browser.network.harExport",
813
+ traceId: "trace:matrix:har-export",
814
+ arguments: {
815
+ sessionId: "session:flow",
816
+ targetId
817
+ }
818
+ });
819
+ expect(harExportResponse.ok).toBe(true);
820
+ expect(harExportResponse.data).toMatchObject({
821
+ driver: "managed",
822
+ targetId,
823
+ har: {
824
+ log: {
825
+ entries: []
826
+ }
827
+ }
828
+ });
829
+
830
+ const traceGetResponse = await sendToolRequest(input, output, {
831
+ id: "request-flow-trace-get",
832
+ name: "browser.trace.get",
833
+ traceId: "trace:matrix:trace-get",
834
+ arguments: {
835
+ sessionId: "session:flow",
836
+ limit: 20
837
+ }
838
+ });
839
+ expect(traceGetResponse.ok).toBe(true);
840
+ expect(traceGetResponse.data).toMatchObject({
841
+ sessionId: "session:flow"
842
+ });
843
+ const tracePayload = traceGetResponse.data as {
844
+ steps?: Array<{ tool?: string }>;
845
+ };
846
+ expect(Array.isArray(tracePayload.steps)).toBe(true);
847
+ expect(tracePayload.steps?.some((step) => step.tool === "browser.tab.open")).toBe(true);
848
+
849
+ const waitUrlResponse = await sendToolRequest(input, output, {
850
+ id: "request-flow-waiturl",
851
+ name: "browser.wait.url",
852
+ traceId: "trace:matrix:waiturl",
853
+ arguments: {
854
+ sessionId: "session:flow",
855
+ targetId,
856
+ urlPattern: "/never-match",
857
+ timeoutMs: 5,
858
+ pollMs: 1
859
+ }
860
+ });
861
+ expect(waitUrlResponse.ok).toBe(false);
862
+ expect(waitUrlResponse.error).toMatchObject({
863
+ code: "E_TIMEOUT"
864
+ });
865
+
377
866
  const closeResponse = await sendToolRequest(input, output, {
378
867
  id: "request-flow-close",
379
868
  name: "browser.tab.close",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flrande/browserctl",
3
- "version": "0.4.0-dev.15.1",
3
+ "version": "0.5.0-dev.19.1",
4
4
  "private": false,
5
5
  "bin": {
6
6
  "browserctl": "bin/browserctl.cjs",
@@ -32,6 +32,7 @@
32
32
  "playwright-core": "^1.52.0",
33
33
  "tsx": "^4.21.0",
34
34
  "ws": "^8.19.0",
35
+ "yaml": "^2.8.1",
35
36
  "zod": "^4.3.6"
36
37
  },
37
38
  "devDependencies": {
@@ -44,6 +45,9 @@
44
45
  "test:unit": "vitest run --config vitest.config.ts",
45
46
  "test:contract": "vitest run --config vitest.contract.config.ts",
46
47
  "test:e2e": "vitest run --config vitest.e2e.config.ts",
48
+ "test:e2e:smoke": "vitest run --config vitest.e2e.smoke.config.ts",
49
+ "test:e2e:full": "vitest run --config vitest.e2e.full.config.ts",
50
+ "test:e2e:stress": "vitest run --config vitest.e2e.stress.config.ts",
47
51
  "test:smoke": "vitest run --config vitest.smoke.config.ts",
48
52
  "test:all": "pnpm run test:unit && pnpm run test:contract && pnpm run test:e2e && pnpm run test:smoke",
49
53
  "build": "npm pack --dry-run",
@@ -42,6 +42,6 @@ export interface BrowserDriver<
42
42
 
43
43
  armUpload(targetId: TargetId, files: string[], profile?: ProfileId): Promise<void>;
44
44
  armDialog(targetId: TargetId, profile?: ProfileId): Promise<void>;
45
- waitDownload(targetId: TargetId, profile?: ProfileId): Promise<TDownload>;
45
+ waitDownload(targetId: TargetId, profile?: ProfileId, path?: string): Promise<TDownload>;
46
46
  triggerDownload(targetId: TargetId, profile?: ProfileId): Promise<void>;
47
47
  }
@@ -1,5 +1,6 @@
1
1
  export * from "./types";
2
2
  export * from "./session-store";
3
+ export * from "./navigation-memory";
3
4
  export * from "./ref-cache";
4
5
  export * from "./driver";
5
6
  export * from "./driver-registry";