@btraut/browser-bridge 0.14.0 → 0.15.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.
@@ -181,6 +181,14 @@ var readSitePermissionsMode = async () => {
181
181
  );
182
182
  });
183
183
  };
184
+ var writeSitePermissionsMode = async (mode) => {
185
+ return await new Promise((resolve) => {
186
+ chrome.storage.local.set(
187
+ { [SITE_PERMISSIONS_MODE_KEY]: mode },
188
+ () => resolve()
189
+ );
190
+ });
191
+ };
184
192
  var readPermissionPromptWaitMs = async () => {
185
193
  return await new Promise((resolve) => {
186
194
  chrome.storage.local.get(
@@ -212,6 +220,9 @@ var writeDebuggerCapabilityEnabled = async (enabled) => {
212
220
  );
213
221
  });
214
222
  };
223
+ var getAllowlistedSites = async () => {
224
+ return await readAllowlistRaw();
225
+ };
215
226
  var isSiteAllowed = async (siteKey) => {
216
227
  const key = normalizeSiteKey(siteKey);
217
228
  const allowlist = await readAllowlistRaw();
@@ -238,6 +249,15 @@ var touchSiteLastUsed = async (siteKey, now = /* @__PURE__ */ new Date()) => {
238
249
  allowlist[key] = { ...existing, lastUsedAt: now.toISOString() };
239
250
  await writeAllowlistRaw(allowlist);
240
251
  };
252
+ var revokeSite = async (siteKey) => {
253
+ const key = normalizeSiteKey(siteKey);
254
+ const allowlist = await readAllowlistRaw();
255
+ if (!allowlist[key]) {
256
+ return;
257
+ }
258
+ delete allowlist[key];
259
+ await writeAllowlistRaw(allowlist);
260
+ };
241
261
 
242
262
  // packages/extension/src/permission-prompt.ts
243
263
  var PERMISSION_PROMPT_PORT_NAME = "permission_prompt";
@@ -423,6 +443,281 @@ var PermissionPromptController = class {
423
443
  }
424
444
  };
425
445
 
446
+ // packages/extension/src/permissions-request.ts
447
+ var PERMISSIONS_REQUEST_PORT_NAME = "permissions_request_prompt";
448
+ var defaultMakeRequestId2 = () => {
449
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
450
+ return crypto.randomUUID();
451
+ }
452
+ return `perm-change-${Date.now()}-${Math.random().toString(16).slice(2)}`;
453
+ };
454
+ var defaultOpenWindow2 = async (url) => {
455
+ return await new Promise((resolve, reject) => {
456
+ chrome.windows.create(
457
+ {
458
+ type: "popup",
459
+ url,
460
+ focused: true,
461
+ width: 500,
462
+ height: 470
463
+ },
464
+ (win) => {
465
+ const err = chrome.runtime.lastError;
466
+ if (err) {
467
+ reject(new Error(err.message));
468
+ return;
469
+ }
470
+ const windowId = win?.id;
471
+ if (typeof windowId !== "number") {
472
+ reject(new Error("Prompt window id missing."));
473
+ return;
474
+ }
475
+ resolve(windowId);
476
+ }
477
+ );
478
+ });
479
+ };
480
+ var defaultCloseWindow2 = async (windowId) => {
481
+ return await new Promise((resolve) => {
482
+ chrome.windows.remove(windowId, () => resolve());
483
+ });
484
+ };
485
+ var delay2 = async (ms) => {
486
+ return await new Promise((resolve) => {
487
+ setTimeout(resolve, ms);
488
+ });
489
+ };
490
+ var normalizeSite = (site) => site.trim().toLowerCase();
491
+ var describeChange = (state) => {
492
+ switch (state.kind) {
493
+ case "allow_site":
494
+ return `Allow Browser Bridge actions on ${state.site ?? "this site"}.`;
495
+ case "revoke_site":
496
+ return `Revoke Browser Bridge actions on ${state.site ?? "this site"}.`;
497
+ case "set_mode":
498
+ return state.mode === "bypass" ? "Switch Browser Bridge to bypass mode." : "Switch Browser Bridge to granular mode.";
499
+ }
500
+ };
501
+ var buildWarning = (kind, mode) => {
502
+ if (kind === "set_mode" && mode === "bypass") {
503
+ return "Bypass mode lets the agent act on any website without asking first.";
504
+ }
505
+ return void 0;
506
+ };
507
+ var PermissionsRequestController = class {
508
+ constructor(deps) {
509
+ this.stateByRequestId = /* @__PURE__ */ new Map();
510
+ this.stateByWindowId = /* @__PURE__ */ new Map();
511
+ this.deps = {
512
+ openWindow: deps?.openWindow ?? defaultOpenWindow2,
513
+ closeWindow: deps?.closeWindow ?? defaultCloseWindow2,
514
+ getDefaultWaitMs: deps?.getDefaultWaitMs ?? readPermissionPromptWaitMs,
515
+ makeRequestId: deps?.makeRequestId ?? defaultMakeRequestId2,
516
+ now: deps?.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()),
517
+ allowSite: deps?.allowSite ?? allowSiteAlways,
518
+ revokeSite: deps?.revokeSite ?? revokeSite,
519
+ setMode: deps?.setMode ?? writeSitePermissionsMode
520
+ };
521
+ }
522
+ async requestChange(request) {
523
+ const state = await this.createState(request);
524
+ const waitMs = typeof request.timeoutMs === "number" && request.timeoutMs > 0 ? request.timeoutMs : await this.deps.getDefaultWaitMs();
525
+ const decision = await this.waitForDecisionOrTimeout(state, waitMs);
526
+ if (!decision) {
527
+ return {
528
+ request_id: state.requestId,
529
+ kind: state.kind,
530
+ status: "timed_out",
531
+ requested_at: state.requestedAt,
532
+ ...state.site ? { site: state.site } : {},
533
+ ...state.mode ? { mode: state.mode } : {},
534
+ ...state.source ? { source: state.source } : {},
535
+ ...state.warning ? { warning: state.warning } : {},
536
+ message: "Permission change request timed out waiting for approval."
537
+ };
538
+ }
539
+ return {
540
+ request_id: state.requestId,
541
+ kind: state.kind,
542
+ status: decision === "approve" ? "approved" : "denied",
543
+ requested_at: state.requestedAt,
544
+ ...state.site ? { site: state.site } : {},
545
+ ...state.mode ? { mode: state.mode } : {},
546
+ ...state.source ? { source: state.source } : {},
547
+ ...state.warning ? { warning: state.warning } : {},
548
+ message: decision === "approve" ? describeChange(state) : "Permission change request was denied."
549
+ };
550
+ }
551
+ listPendingRequests() {
552
+ return [...this.stateByRequestId.values()].filter((state) => state.decided === null).map((state) => ({
553
+ request_id: state.requestId,
554
+ kind: state.kind,
555
+ status: "pending",
556
+ requested_at: state.requestedAt,
557
+ ...state.site ? { site: state.site } : {},
558
+ ...state.mode ? { mode: state.mode } : {},
559
+ ...state.source ? { source: state.source } : {},
560
+ ...state.warning ? { warning: state.warning } : {}
561
+ })).sort((a, b) => a.requested_at.localeCompare(b.requested_at));
562
+ }
563
+ handleConnect(port) {
564
+ if (!port || typeof port !== "object") {
565
+ return;
566
+ }
567
+ const p = port;
568
+ if (p.name !== PERMISSIONS_REQUEST_PORT_NAME) {
569
+ return;
570
+ }
571
+ const onMessage = p.onMessage;
572
+ if (!onMessage || typeof onMessage.addListener !== "function") {
573
+ return;
574
+ }
575
+ onMessage.addListener((message) => {
576
+ void this.handlePortMessage(message).catch((error) => {
577
+ console.error(
578
+ "PermissionsRequestController handlePortMessage failed:",
579
+ error
580
+ );
581
+ });
582
+ });
583
+ }
584
+ handleWindowRemoved(windowId) {
585
+ const state = this.stateByWindowId.get(windowId);
586
+ if (!state) {
587
+ return;
588
+ }
589
+ this.cleanupState(state);
590
+ }
591
+ async createState(request) {
592
+ const requestId = this.deps.makeRequestId();
593
+ const requestedAt = this.deps.now();
594
+ const kind = request.kind;
595
+ const warning = buildWarning(kind, request.mode);
596
+ const state = {
597
+ requestId,
598
+ kind,
599
+ requestedAt,
600
+ site: request.site ? normalizeSite(request.site) : void 0,
601
+ mode: request.mode,
602
+ source: request.source,
603
+ warning,
604
+ windowId: null,
605
+ decided: null,
606
+ waiters: /* @__PURE__ */ new Set()
607
+ };
608
+ this.stateByRequestId.set(requestId, state);
609
+ const url = this.buildPromptUrl(state);
610
+ const windowId = await this.deps.openWindow(url);
611
+ state.windowId = windowId;
612
+ this.stateByWindowId.set(windowId, state);
613
+ if (state.decided) {
614
+ await this.deps.closeWindow(windowId);
615
+ this.cleanupState(state);
616
+ }
617
+ return state;
618
+ }
619
+ buildPromptUrl(state) {
620
+ const base = chrome.runtime.getURL("permissions-request.html");
621
+ const url = new URL(base);
622
+ url.searchParams.set("requestId", state.requestId);
623
+ url.searchParams.set("kind", state.kind);
624
+ url.searchParams.set("requestedAt", state.requestedAt);
625
+ if (state.site) {
626
+ url.searchParams.set("site", state.site);
627
+ }
628
+ if (state.mode) {
629
+ url.searchParams.set("mode", state.mode);
630
+ }
631
+ if (state.source) {
632
+ url.searchParams.set("source", state.source);
633
+ }
634
+ if (state.warning) {
635
+ url.searchParams.set("warning", state.warning);
636
+ }
637
+ if (state.kind === "set_mode" && state.mode === "bypass") {
638
+ url.searchParams.set("requireAcknowledge", "1");
639
+ }
640
+ return url.toString();
641
+ }
642
+ async waitForDecisionOrTimeout(state, waitMs) {
643
+ if (state.decided) {
644
+ return state.decided;
645
+ }
646
+ let waiter = null;
647
+ const decisionPromise = new Promise((resolve) => {
648
+ waiter = resolve;
649
+ state.waiters.add(resolve);
650
+ });
651
+ const winner = await Promise.race([
652
+ decisionPromise,
653
+ delay2(waitMs).then(() => null)
654
+ ]);
655
+ if (winner === null && waiter) {
656
+ state.waiters.delete(waiter);
657
+ }
658
+ return winner;
659
+ }
660
+ async handlePortMessage(message) {
661
+ if (!message || typeof message !== "object") {
662
+ return;
663
+ }
664
+ const m = message;
665
+ if (m.type !== "decision") {
666
+ return;
667
+ }
668
+ const requestId = m.requestId;
669
+ const decision = m.decision;
670
+ if (typeof requestId !== "string" || requestId.length === 0) {
671
+ return;
672
+ }
673
+ if (decision !== "approve" && decision !== "deny") {
674
+ return;
675
+ }
676
+ const state = this.stateByRequestId.get(requestId);
677
+ if (!state) {
678
+ return;
679
+ }
680
+ state.decided = decision;
681
+ if (decision === "approve") {
682
+ await this.applyApprovedChange(state);
683
+ }
684
+ for (const waiter of state.waiters) {
685
+ waiter(decision);
686
+ }
687
+ state.waiters.clear();
688
+ if (typeof state.windowId === "number") {
689
+ await this.deps.closeWindow(state.windowId);
690
+ this.cleanupState(state);
691
+ }
692
+ }
693
+ async applyApprovedChange(state) {
694
+ if (state.kind === "allow_site") {
695
+ if (!state.site) {
696
+ throw new Error("allow_site request is missing site.");
697
+ }
698
+ await this.deps.allowSite(state.site);
699
+ return;
700
+ }
701
+ if (state.kind === "revoke_site") {
702
+ if (!state.site) {
703
+ throw new Error("revoke_site request is missing site.");
704
+ }
705
+ await this.deps.revokeSite(state.site);
706
+ return;
707
+ }
708
+ if (!state.mode) {
709
+ throw new Error("set_mode request is missing mode.");
710
+ }
711
+ await this.deps.setMode(state.mode);
712
+ }
713
+ cleanupState(state) {
714
+ this.stateByRequestId.delete(state.requestId);
715
+ if (typeof state.windowId === "number") {
716
+ this.stateByWindowId.delete(state.windowId);
717
+ }
718
+ }
719
+ };
720
+
426
721
  // packages/extension/src/restricted-url.ts
427
722
  var RESTRICTED_URL_PREFIXES = [
428
723
  "chrome://",
@@ -840,6 +1135,9 @@ var TRANSIENT_TAB_CHANNEL_ERROR_PATTERNS = [
840
1135
  "the message port closed before a response was received",
841
1136
  "extension port is moved into back/forward cache"
842
1137
  ];
1138
+ var CONTENT_SCRIPT_RECOVERY_ERROR_PATTERNS = [
1139
+ "receiving end does not exist"
1140
+ ];
843
1141
  var TAB_CHANNEL_RETRY_DELAYS_MS = [120, 200, 320, 500, 750, 1e3, 1200];
844
1142
  var normalizePathname = (pathname) => {
845
1143
  if (pathname.length === 0) {
@@ -859,6 +1157,25 @@ var isTransientTabChannelError = (message) => {
859
1157
  (pattern) => normalized.includes(pattern)
860
1158
  );
861
1159
  };
1160
+ var canInjectContentScriptForUrl = (url) => {
1161
+ if (typeof url !== "string" || url.trim().length === 0) {
1162
+ return false;
1163
+ }
1164
+ const normalized = url.toLowerCase();
1165
+ if (normalized.startsWith("chrome://") || normalized.startsWith("chrome-extension://") || normalized.startsWith("devtools://") || normalized.startsWith("edge://") || normalized.startsWith("about:") || normalized.startsWith("file://")) {
1166
+ return false;
1167
+ }
1168
+ return normalized.startsWith("http://") || normalized.startsWith("https://");
1169
+ };
1170
+ var shouldReinjectContentScript = (message, tabUrl) => {
1171
+ if (typeof message !== "string") {
1172
+ return false;
1173
+ }
1174
+ const normalized = message.toLowerCase();
1175
+ return CONTENT_SCRIPT_RECOVERY_ERROR_PATTERNS.some(
1176
+ (pattern) => normalized.includes(pattern)
1177
+ ) && canInjectContentScriptForUrl(tabUrl);
1178
+ };
862
1179
  var shouldRetryTabChannelFailure = (action, error) => {
863
1180
  if (!error || typeof error !== "object") {
864
1181
  return false;
@@ -1224,7 +1541,13 @@ var BASE_NEGOTIATED_CAPABILITIES = Object.freeze({
1224
1541
  "drive.tab_activate": true,
1225
1542
  "drive.tab_close": true,
1226
1543
  "drive.set_debugger_capability": true,
1227
- "drive.ping": true
1544
+ "drive.ping": true,
1545
+ "permissions.list": true,
1546
+ "permissions.get_mode": true,
1547
+ "permissions.list_pending_requests": true,
1548
+ "permissions.request_allow_site": true,
1549
+ "permissions.request_revoke_site": true,
1550
+ "permissions.request_set_mode": true
1228
1551
  });
1229
1552
  var DEBUGGER_CAPABILITY_ACTIONS = [
1230
1553
  "debugger.attach",
@@ -1297,6 +1620,28 @@ var delayMs = async (ms) => {
1297
1620
  self.setTimeout(resolve, ms);
1298
1621
  });
1299
1622
  };
1623
+ var ensureTabContentScript = async (tabId) => {
1624
+ try {
1625
+ const tab = await getTab(tabId);
1626
+ const url = typeof tab.url === "string" ? tab.url : void 0;
1627
+ if (!canInjectContentScriptForUrl(url)) {
1628
+ return false;
1629
+ }
1630
+ await wrapChromeVoid(
1631
+ (callback) => chrome.scripting.executeScript(
1632
+ {
1633
+ target: { tabId },
1634
+ files: ["dist/content.js"]
1635
+ },
1636
+ () => callback()
1637
+ )
1638
+ );
1639
+ return true;
1640
+ } catch (error) {
1641
+ console.debug("Failed to re-inject content script.", { tabId, error });
1642
+ return false;
1643
+ }
1644
+ };
1300
1645
  var CAPTURE_VISIBLE_TAB_MIN_INTERVAL_MS = 400;
1301
1646
  var CAPTURE_VISIBLE_TAB_MAX_RETRIES = 3;
1302
1647
  var CAPTURE_VISIBLE_TAB_RETRY_BASE_DELAY_MS = 500;
@@ -1752,11 +2097,18 @@ var sendToTab = async (tabId, action, params, options) => {
1752
2097
  });
1753
2098
  });
1754
2099
  };
2100
+ let attemptedContentRecovery = false;
1755
2101
  for (let attempt = 1; ; attempt += 1) {
1756
2102
  const result = await attemptSend();
1757
2103
  if (result.ok) {
1758
2104
  return result;
1759
2105
  }
2106
+ if (!attemptedContentRecovery && shouldReinjectContentScript(
2107
+ result.error.message,
2108
+ (await getTab(tabId).catch(() => void 0))?.url
2109
+ )) {
2110
+ attemptedContentRecovery = await ensureTabContentScript(tabId);
2111
+ }
1760
2112
  if (!shouldRetryTabChannelFailure(action, result.error)) {
1761
2113
  return result;
1762
2114
  }
@@ -1909,15 +2261,15 @@ var DriveSocket = class {
1909
2261
  if (this.reconnectTimer !== null) {
1910
2262
  return;
1911
2263
  }
1912
- const delay2 = this.reconnectDelayMs;
1913
- this.connection.markBackoff(delay2);
2264
+ const delay3 = this.reconnectDelayMs;
2265
+ this.connection.markBackoff(delay3);
1914
2266
  this.reconnectTimer = self.setTimeout(() => {
1915
2267
  this.reconnectTimer = null;
1916
2268
  this.connection.markConnecting();
1917
2269
  void this.connect().catch((error) => {
1918
2270
  this.recordConnectionFailure("reconnect", error);
1919
2271
  });
1920
- }, delay2);
2272
+ }, delay3);
1921
2273
  this.reconnectDelayMs = Math.min(
1922
2274
  this.maxReconnectDelayMs,
1923
2275
  this.reconnectDelayMs * 2
@@ -2132,11 +2484,11 @@ var DriveSocket = class {
2132
2484
  this.refreshCapabilities();
2133
2485
  }
2134
2486
  async handleRequest(message) {
2135
- let driveMessage = null;
2487
+ let requestMessage = null;
2136
2488
  let gatedSiteKey = null;
2137
2489
  let touchGatedSiteOnSuccess = false;
2138
2490
  const respondOk = (result) => {
2139
- if (!driveMessage) {
2491
+ if (!requestMessage) {
2140
2492
  return;
2141
2493
  }
2142
2494
  if (touchGatedSiteOnSuccess && gatedSiteKey) {
@@ -2145,20 +2497,20 @@ var DriveSocket = class {
2145
2497
  });
2146
2498
  }
2147
2499
  const response = {
2148
- id: driveMessage.id,
2149
- action: driveMessage.action,
2500
+ id: requestMessage.id,
2501
+ action: requestMessage.action,
2150
2502
  status: "ok",
2151
2503
  result
2152
2504
  };
2153
2505
  this.sendMessage(response);
2154
2506
  };
2155
2507
  const respondError = (error) => {
2156
- if (!driveMessage) {
2508
+ if (!requestMessage) {
2157
2509
  return;
2158
2510
  }
2159
2511
  const response = {
2160
- id: driveMessage.id,
2161
- action: driveMessage.action,
2512
+ id: requestMessage.id,
2513
+ action: requestMessage.action,
2162
2514
  status: "error",
2163
2515
  error: sanitizeDriveErrorInfo(error)
2164
2516
  };
@@ -2168,16 +2520,111 @@ var DriveSocket = class {
2168
2520
  if (!message || typeof message !== "object" || typeof message.id !== "string" || typeof message.action !== "string") {
2169
2521
  return;
2170
2522
  }
2171
- if (message.action.startsWith("debugger.")) {
2523
+ const action = message.action;
2524
+ if (action.startsWith("debugger.")) {
2172
2525
  await this.handleDebuggerRequest(message);
2173
2526
  return;
2174
2527
  }
2175
- if (!message.action.startsWith("drive.")) {
2528
+ requestMessage = message;
2529
+ if (action.startsWith("permissions.")) {
2530
+ const params = message.params ?? {};
2531
+ const rawTimeoutMs = params.timeout_ms;
2532
+ const timeoutMs = typeof rawTimeoutMs === "number" && Number.isFinite(rawTimeoutMs) && rawTimeoutMs > 0 ? Math.floor(rawTimeoutMs) : void 0;
2533
+ const rawSource = params.source;
2534
+ const source = rawSource === "cli" || rawSource === "mcp" || rawSource === "api" ? rawSource : void 0;
2535
+ switch (action) {
2536
+ case "permissions.list": {
2537
+ const allowlist = await getAllowlistedSites();
2538
+ const sites = Object.entries(allowlist).map(([site, entry]) => ({
2539
+ site,
2540
+ created_at: entry.createdAt,
2541
+ last_used_at: entry.lastUsedAt
2542
+ })).sort((a, b) => a.site.localeCompare(b.site));
2543
+ respondOk({ sites });
2544
+ return;
2545
+ }
2546
+ case "permissions.get_mode": {
2547
+ respondOk({
2548
+ mode: await readSitePermissionsMode()
2549
+ });
2550
+ return;
2551
+ }
2552
+ case "permissions.list_pending_requests": {
2553
+ respondOk({
2554
+ requests: permissionsRequests.listPendingRequests()
2555
+ });
2556
+ return;
2557
+ }
2558
+ case "permissions.request_allow_site": {
2559
+ const site = params.site;
2560
+ if (typeof site !== "string" || site.trim().length === 0) {
2561
+ respondError({
2562
+ code: "INVALID_ARGUMENT",
2563
+ message: "site must be a non-empty string.",
2564
+ retryable: false,
2565
+ details: { field: "site" }
2566
+ });
2567
+ return;
2568
+ }
2569
+ respondOk(
2570
+ await permissionsRequests.requestChange({
2571
+ kind: "allow_site",
2572
+ site,
2573
+ timeoutMs,
2574
+ source
2575
+ })
2576
+ );
2577
+ return;
2578
+ }
2579
+ case "permissions.request_revoke_site": {
2580
+ const site = params.site;
2581
+ if (typeof site !== "string" || site.trim().length === 0) {
2582
+ respondError({
2583
+ code: "INVALID_ARGUMENT",
2584
+ message: "site must be a non-empty string.",
2585
+ retryable: false,
2586
+ details: { field: "site" }
2587
+ });
2588
+ return;
2589
+ }
2590
+ respondOk(
2591
+ await permissionsRequests.requestChange({
2592
+ kind: "revoke_site",
2593
+ site,
2594
+ timeoutMs,
2595
+ source
2596
+ })
2597
+ );
2598
+ return;
2599
+ }
2600
+ case "permissions.request_set_mode": {
2601
+ const mode = params.mode;
2602
+ if (mode !== "granular" && mode !== "bypass") {
2603
+ respondError({
2604
+ code: "INVALID_ARGUMENT",
2605
+ message: "mode must be granular or bypass.",
2606
+ retryable: false,
2607
+ details: { field: "mode" }
2608
+ });
2609
+ return;
2610
+ }
2611
+ respondOk(
2612
+ await permissionsRequests.requestChange({
2613
+ kind: "set_mode",
2614
+ mode,
2615
+ timeoutMs,
2616
+ source
2617
+ })
2618
+ );
2619
+ return;
2620
+ }
2621
+ }
2622
+ }
2623
+ if (!action.startsWith("drive.")) {
2176
2624
  return;
2177
2625
  }
2178
- driveMessage = message;
2179
2626
  const gated = await gateDriveAction({
2180
- action: message.action,
2627
+ action,
2181
2628
  params: message.params ?? {},
2182
2629
  getDefaultTabId,
2183
2630
  getTab,
@@ -2433,8 +2880,8 @@ var DriveSocket = class {
2433
2880
  }
2434
2881
  case "drive.handle_dialog": {
2435
2882
  const params = message.params ?? {};
2436
- const action = params.action;
2437
- if (action !== "accept" && action !== "dismiss") {
2883
+ const action2 = params.action;
2884
+ if (action2 !== "accept" && action2 !== "dismiss") {
2438
2885
  respondError({
2439
2886
  code: "INVALID_ARGUMENT",
2440
2887
  message: "action must be accept or dismiss.",
@@ -2467,7 +2914,7 @@ var DriveSocket = class {
2467
2914
  tabId,
2468
2915
  "Page.handleJavaScriptDialog",
2469
2916
  {
2470
- accept: action === "accept",
2917
+ accept: action2 === "accept",
2471
2918
  ...promptText ? { promptText } : {}
2472
2919
  },
2473
2920
  DEFAULT_DEBUGGER_COMMAND_TIMEOUT_MS
@@ -3869,11 +4316,14 @@ var DebuggerTimeoutError = class extends Error {
3869
4316
  };
3870
4317
  var socket = new DriveSocket();
3871
4318
  var permissionPrompts = new PermissionPromptController();
4319
+ var permissionsRequests = new PermissionsRequestController();
3872
4320
  chrome.runtime.onConnect.addListener((port) => {
3873
4321
  permissionPrompts.handleConnect(port);
4322
+ permissionsRequests.handleConnect(port);
3874
4323
  });
3875
4324
  chrome.windows.onRemoved.addListener((windowId) => {
3876
4325
  permissionPrompts.handleWindowRemoved(windowId);
4326
+ permissionsRequests.handleWindowRemoved(windowId);
3877
4327
  });
3878
4328
  chrome.windows.onFocusChanged.addListener((windowId) => {
3879
4329
  if (windowId === chrome.windows.WINDOW_ID_NONE) {