@btraut/browser-bridge 0.7.3 → 0.8.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.
@@ -418,6 +418,10 @@ var DEBUGGER_PROTOCOL_VERSION = "1.3";
418
418
  var DEBUGGER_IDLE_TIMEOUT_KEY = "debuggerIdleTimeoutMs";
419
419
  var DEFAULT_DEBUGGER_IDLE_TIMEOUT_MS = 15e3;
420
420
  var DEFAULT_DEBUGGER_COMMAND_TIMEOUT_MS = 1e4;
421
+ var DEFAULT_SEND_TO_TAB_TIMEOUT_MS = 1e4;
422
+ var HISTORY_DISPATCH_TIMEOUT_MS = 2e3;
423
+ var HISTORY_NAVIGATION_SIGNAL_TIMEOUT_MS = 8e3;
424
+ var HISTORY_POST_NAV_DOM_GRACE_TIMEOUT_MS = 2e3;
421
425
  var AGENT_TAB_ID_KEY = "agentTabId";
422
426
  var AGENT_TAB_GROUP_TITLE = "\u{1F309} Browser Bridge";
423
427
  var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
@@ -809,14 +813,42 @@ var getDefaultTabId = async () => {
809
813
  return await getActiveTabId();
810
814
  }
811
815
  };
812
- var sendToTab = async (tabId, action, params) => {
816
+ var sendToTab = async (tabId, action, params, options) => {
817
+ const timeoutMs = typeof options?.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(1, Math.floor(options.timeoutMs)) : DEFAULT_SEND_TO_TAB_TIMEOUT_MS;
813
818
  const attemptSend = async () => {
814
819
  return await new Promise((resolve) => {
815
820
  const message = { action, params };
821
+ let settled = false;
822
+ const finish = (result) => {
823
+ if (settled) {
824
+ return;
825
+ }
826
+ settled = true;
827
+ if (timeout !== void 0) {
828
+ clearTimeout(timeout);
829
+ }
830
+ resolve(result);
831
+ };
832
+ let timeout;
833
+ timeout = self.setTimeout(() => {
834
+ finish({
835
+ ok: false,
836
+ error: {
837
+ code: "TIMEOUT",
838
+ message: `Timed out waiting for content response after ${timeoutMs}ms.`,
839
+ retryable: true,
840
+ details: {
841
+ action,
842
+ tab_id: tabId,
843
+ timeout_ms: timeoutMs
844
+ }
845
+ }
846
+ });
847
+ }, timeoutMs);
816
848
  chrome.tabs.sendMessage(tabId, message, (response) => {
817
849
  const error = chrome.runtime.lastError;
818
850
  if (error) {
819
- resolve({
851
+ finish({
820
852
  ok: false,
821
853
  error: {
822
854
  code: "EVALUATION_FAILED",
@@ -827,7 +859,7 @@ var sendToTab = async (tabId, action, params) => {
827
859
  return;
828
860
  }
829
861
  if (!response || typeof response !== "object") {
830
- resolve({
862
+ finish({
831
863
  ok: false,
832
864
  error: {
833
865
  code: "EVALUATION_FAILED",
@@ -837,7 +869,7 @@ var sendToTab = async (tabId, action, params) => {
837
869
  });
838
870
  return;
839
871
  }
840
- resolve(response);
872
+ finish(response);
841
873
  });
842
874
  });
843
875
  };
@@ -863,6 +895,67 @@ var sendToTab = async (tabId, action, params) => {
863
895
  }
864
896
  };
865
897
  };
898
+ var waitForHistoryNavigationSignal = async (tabId, timeoutMs) => {
899
+ return await new Promise((resolve, reject) => {
900
+ let timeout;
901
+ const cleanup = () => {
902
+ if (timeout !== void 0) {
903
+ clearTimeout(timeout);
904
+ }
905
+ chrome.webNavigation.onCommitted.removeListener(onCommitted);
906
+ chrome.webNavigation.onHistoryStateUpdated.removeListener(
907
+ onHistoryStateUpdated
908
+ );
909
+ chrome.webNavigation.onReferenceFragmentUpdated.removeListener(
910
+ onReferenceFragmentUpdated
911
+ );
912
+ chrome.tabs.onUpdated.removeListener(onTabUpdated);
913
+ };
914
+ const resolveSignal = () => {
915
+ cleanup();
916
+ resolve();
917
+ };
918
+ const onCommitted = (details) => {
919
+ if (details.tabId !== tabId || details.frameId !== 0) {
920
+ return;
921
+ }
922
+ resolveSignal();
923
+ };
924
+ const onHistoryStateUpdated = (details) => {
925
+ if (details.tabId !== tabId || details.frameId !== 0) {
926
+ return;
927
+ }
928
+ resolveSignal();
929
+ };
930
+ const onReferenceFragmentUpdated = (details) => {
931
+ if (details.tabId !== tabId || details.frameId !== 0) {
932
+ return;
933
+ }
934
+ resolveSignal();
935
+ };
936
+ const onTabUpdated = (updatedTabId, changeInfo) => {
937
+ if (updatedTabId !== tabId) {
938
+ return;
939
+ }
940
+ if (typeof changeInfo.url !== "string" || changeInfo.url.length === 0) {
941
+ return;
942
+ }
943
+ resolveSignal();
944
+ };
945
+ chrome.webNavigation.onCommitted.addListener(onCommitted);
946
+ chrome.webNavigation.onHistoryStateUpdated.addListener(
947
+ onHistoryStateUpdated
948
+ );
949
+ chrome.webNavigation.onReferenceFragmentUpdated.addListener(
950
+ onReferenceFragmentUpdated
951
+ );
952
+ chrome.tabs.onUpdated.addListener(onTabUpdated);
953
+ timeout = self.setTimeout(() => {
954
+ cleanup();
955
+ reject(new Error("Timed out waiting for history navigation signal."));
956
+ }, timeoutMs);
957
+ });
958
+ };
866
959
  var waitForDomContentLoaded = async (tabId, timeoutMs) => {
867
960
  return await new Promise((resolve, reject) => {
868
961
  let timeout;
@@ -1313,21 +1406,37 @@ var DriveSocket = class {
1313
1406
  if (tabId === void 0) {
1314
1407
  tabId = await getDefaultTabId();
1315
1408
  }
1316
- const result = await sendToTab(tabId, message.action);
1317
- if (!result.ok) {
1409
+ const navigationSignal = waitForHistoryNavigationSignal(
1410
+ tabId,
1411
+ HISTORY_NAVIGATION_SIGNAL_TIMEOUT_MS
1412
+ );
1413
+ const result = await sendToTab(
1414
+ tabId,
1415
+ message.action,
1416
+ void 0,
1417
+ {
1418
+ timeoutMs: HISTORY_DISPATCH_TIMEOUT_MS
1419
+ }
1420
+ );
1421
+ if (!result.ok && result.error.code !== "TIMEOUT") {
1318
1422
  respondError(result.error);
1319
1423
  return;
1320
1424
  }
1321
1425
  markTabActive(tabId);
1322
1426
  try {
1323
- await waitForDomContentLoaded(tabId, 3e4);
1324
- } catch (error) {
1325
- respondError({
1326
- code: "TIMEOUT",
1327
- message: error instanceof Error ? error.message : "Timed out waiting.",
1328
- retryable: true
1329
- });
1330
- return;
1427
+ await navigationSignal;
1428
+ try {
1429
+ await waitForDomContentLoaded(
1430
+ tabId,
1431
+ HISTORY_POST_NAV_DOM_GRACE_TIMEOUT_MS
1432
+ );
1433
+ } catch {
1434
+ }
1435
+ } catch {
1436
+ if (!result.ok) {
1437
+ respondError(result.error);
1438
+ return;
1439
+ }
1331
1440
  }
1332
1441
  respondOk({ ok: true });
1333
1442
  return;
@@ -1445,14 +1554,447 @@ var DriveSocket = class {
1445
1554
  }
1446
1555
  return;
1447
1556
  }
1448
- case "drive.click":
1449
- case "drive.hover":
1450
- case "drive.select":
1451
- case "drive.type":
1452
- case "drive.fill_form":
1453
- case "drive.drag":
1454
- case "drive.key":
1455
- case "drive.key_press":
1557
+ case "drive.click": {
1558
+ const params = message.params ?? {};
1559
+ let tabId = params.tab_id;
1560
+ if (tabId !== void 0 && typeof tabId !== "number") {
1561
+ respondError({
1562
+ code: "INVALID_ARGUMENT",
1563
+ message: "tab_id must be a number when provided.",
1564
+ retryable: false
1565
+ });
1566
+ return;
1567
+ }
1568
+ if (tabId === void 0) {
1569
+ tabId = await getDefaultTabId();
1570
+ }
1571
+ const clickCount = params.click_count;
1572
+ const count = typeof clickCount === "number" && Number.isFinite(clickCount) ? Math.max(1, Math.floor(clickCount)) : 1;
1573
+ const error = await this.ensureDebuggerAttached(tabId);
1574
+ if (error) {
1575
+ respondError(error);
1576
+ return;
1577
+ }
1578
+ const pointResult = await this.resolveLocatorPoint(
1579
+ tabId,
1580
+ params.locator
1581
+ );
1582
+ if (!pointResult.ok) {
1583
+ respondError(pointResult.error);
1584
+ return;
1585
+ }
1586
+ const { x, y } = pointResult.point;
1587
+ self.setTimeout(() => {
1588
+ void this.dispatchCdpClick(tabId, x, y, count).catch(
1589
+ (error2) => {
1590
+ console.debug("Deferred CDP click failed.", error2);
1591
+ }
1592
+ );
1593
+ }, 0);
1594
+ respondOk({ ok: true });
1595
+ return;
1596
+ }
1597
+ case "drive.hover": {
1598
+ const params = message.params ?? {};
1599
+ let tabId = params.tab_id;
1600
+ if (tabId !== void 0 && typeof tabId !== "number") {
1601
+ respondError({
1602
+ code: "INVALID_ARGUMENT",
1603
+ message: "tab_id must be a number when provided.",
1604
+ retryable: false
1605
+ });
1606
+ return;
1607
+ }
1608
+ if (tabId === void 0) {
1609
+ tabId = await getDefaultTabId();
1610
+ }
1611
+ const error = await this.ensureDebuggerAttached(tabId);
1612
+ if (error) {
1613
+ respondError(error);
1614
+ return;
1615
+ }
1616
+ const pointResult = await this.resolveLocatorPoint(
1617
+ tabId,
1618
+ params.locator
1619
+ );
1620
+ if (!pointResult.ok) {
1621
+ respondError(pointResult.error);
1622
+ return;
1623
+ }
1624
+ const { x, y } = pointResult.point;
1625
+ const waitMs = typeof params.delay_ms === "number" && Number.isFinite(params.delay_ms) ? Math.min(Math.max(params.delay_ms, 0), 1e4) : 0;
1626
+ try {
1627
+ await this.dispatchCdpMouseMove(tabId, x, y, 0);
1628
+ if (waitMs > 0) {
1629
+ await delayMs(waitMs);
1630
+ }
1631
+ const snapshot = await sendToTab(
1632
+ tabId,
1633
+ "drive.snapshot_html"
1634
+ );
1635
+ if (!snapshot.ok) {
1636
+ respondError(snapshot.error);
1637
+ return;
1638
+ }
1639
+ respondOk(snapshot.result ?? { format: "html", snapshot: "" });
1640
+ } catch (error2) {
1641
+ const info = mapDebuggerErrorMessage(
1642
+ error2 instanceof Error ? error2.message : "Hover dispatch failed."
1643
+ );
1644
+ respondError(info);
1645
+ }
1646
+ return;
1647
+ }
1648
+ case "drive.drag": {
1649
+ const params = message.params ?? {};
1650
+ let tabId = params.tab_id;
1651
+ if (tabId !== void 0 && typeof tabId !== "number") {
1652
+ respondError({
1653
+ code: "INVALID_ARGUMENT",
1654
+ message: "tab_id must be a number when provided.",
1655
+ retryable: false
1656
+ });
1657
+ return;
1658
+ }
1659
+ if (tabId === void 0) {
1660
+ tabId = await getDefaultTabId();
1661
+ }
1662
+ const error = await this.ensureDebuggerAttached(tabId);
1663
+ if (error) {
1664
+ respondError(error);
1665
+ return;
1666
+ }
1667
+ const fromResult = await this.resolveLocatorPoint(
1668
+ tabId,
1669
+ params.from
1670
+ );
1671
+ if (!fromResult.ok) {
1672
+ respondError(fromResult.error);
1673
+ return;
1674
+ }
1675
+ const toResult = await this.resolveLocatorPoint(
1676
+ tabId,
1677
+ params.to
1678
+ );
1679
+ if (!toResult.ok) {
1680
+ respondError(toResult.error);
1681
+ return;
1682
+ }
1683
+ const steps = typeof params.steps === "number" && Number.isFinite(params.steps) ? Math.max(1, Math.min(50, Math.floor(params.steps))) : 12;
1684
+ try {
1685
+ await this.dispatchCdpDrag(
1686
+ tabId,
1687
+ fromResult.point,
1688
+ toResult.point,
1689
+ steps
1690
+ );
1691
+ respondOk({ ok: true });
1692
+ } catch (error2) {
1693
+ const info = mapDebuggerErrorMessage(
1694
+ error2 instanceof Error ? error2.message : "Drag dispatch failed."
1695
+ );
1696
+ respondError(info);
1697
+ }
1698
+ return;
1699
+ }
1700
+ case "drive.key_press": {
1701
+ const params = message.params ?? {};
1702
+ const key = params.key;
1703
+ if (typeof key !== "string" || key.length === 0) {
1704
+ respondError({
1705
+ code: "INVALID_ARGUMENT",
1706
+ message: "key must be a non-empty string.",
1707
+ retryable: false
1708
+ });
1709
+ return;
1710
+ }
1711
+ let tabId = params.tab_id;
1712
+ if (tabId !== void 0 && typeof tabId !== "number") {
1713
+ respondError({
1714
+ code: "INVALID_ARGUMENT",
1715
+ message: "tab_id must be a number when provided.",
1716
+ retryable: false
1717
+ });
1718
+ return;
1719
+ }
1720
+ if (tabId === void 0) {
1721
+ tabId = await getDefaultTabId();
1722
+ }
1723
+ const error = await this.ensureDebuggerAttached(tabId);
1724
+ if (error) {
1725
+ respondError(error);
1726
+ return;
1727
+ }
1728
+ try {
1729
+ await this.dispatchCdpKeyPress(
1730
+ tabId,
1731
+ key,
1732
+ params.modifiers
1733
+ );
1734
+ respondOk({ ok: true });
1735
+ } catch (error2) {
1736
+ const info = mapDebuggerErrorMessage(
1737
+ error2 instanceof Error ? error2.message : "Keyboard dispatch failed."
1738
+ );
1739
+ respondError(info);
1740
+ }
1741
+ return;
1742
+ }
1743
+ case "drive.key": {
1744
+ const params = message.params ?? {};
1745
+ const key = params.key;
1746
+ if (typeof key !== "string" || key.length === 0) {
1747
+ respondError({
1748
+ code: "INVALID_ARGUMENT",
1749
+ message: "key must be a non-empty string.",
1750
+ retryable: false
1751
+ });
1752
+ return;
1753
+ }
1754
+ let tabId = params.tab_id;
1755
+ if (tabId !== void 0 && typeof tabId !== "number") {
1756
+ respondError({
1757
+ code: "INVALID_ARGUMENT",
1758
+ message: "tab_id must be a number when provided.",
1759
+ retryable: false
1760
+ });
1761
+ return;
1762
+ }
1763
+ if (tabId === void 0) {
1764
+ tabId = await getDefaultTabId();
1765
+ }
1766
+ const count = typeof params.repeat === "number" && Number.isFinite(params.repeat) ? Math.max(1, Math.min(50, Math.floor(params.repeat))) : 1;
1767
+ const error = await this.ensureDebuggerAttached(tabId);
1768
+ if (error) {
1769
+ respondError(error);
1770
+ return;
1771
+ }
1772
+ try {
1773
+ for (let i = 0; i < count; i += 1) {
1774
+ await this.dispatchCdpKeyPress(
1775
+ tabId,
1776
+ key,
1777
+ params.modifiers
1778
+ );
1779
+ }
1780
+ respondOk({ ok: true });
1781
+ } catch (error2) {
1782
+ const info = mapDebuggerErrorMessage(
1783
+ error2 instanceof Error ? error2.message : "Keyboard dispatch failed."
1784
+ );
1785
+ respondError(info);
1786
+ }
1787
+ return;
1788
+ }
1789
+ case "drive.type": {
1790
+ const params = message.params ?? {};
1791
+ const text = params.text;
1792
+ if (typeof text !== "string") {
1793
+ respondError({
1794
+ code: "INVALID_ARGUMENT",
1795
+ message: "text must be a string.",
1796
+ retryable: false
1797
+ });
1798
+ return;
1799
+ }
1800
+ let tabId = params.tab_id;
1801
+ if (tabId !== void 0 && typeof tabId !== "number") {
1802
+ respondError({
1803
+ code: "INVALID_ARGUMENT",
1804
+ message: "tab_id must be a number when provided.",
1805
+ retryable: false
1806
+ });
1807
+ return;
1808
+ }
1809
+ if (tabId === void 0) {
1810
+ tabId = await getDefaultTabId();
1811
+ }
1812
+ const error = await this.ensureDebuggerAttached(tabId);
1813
+ if (error) {
1814
+ respondError(error);
1815
+ return;
1816
+ }
1817
+ const result = await this.performCdpType(tabId, {
1818
+ locator: params.locator,
1819
+ text,
1820
+ clear: Boolean(params.clear),
1821
+ submit: Boolean(params.submit)
1822
+ });
1823
+ if (!result.ok) {
1824
+ respondError(result.error);
1825
+ return;
1826
+ }
1827
+ respondOk({ ok: true });
1828
+ return;
1829
+ }
1830
+ case "drive.select": {
1831
+ const params = message.params ?? {};
1832
+ let tabId = params.tab_id;
1833
+ if (tabId !== void 0 && typeof tabId !== "number") {
1834
+ respondError({
1835
+ code: "INVALID_ARGUMENT",
1836
+ message: "tab_id must be a number when provided.",
1837
+ retryable: false
1838
+ });
1839
+ return;
1840
+ }
1841
+ if (tabId === void 0) {
1842
+ tabId = await getDefaultTabId();
1843
+ }
1844
+ const error = await this.ensureDebuggerAttached(tabId);
1845
+ if (error) {
1846
+ respondError(error);
1847
+ return;
1848
+ }
1849
+ const pointResult = await this.resolveLocatorPoint(
1850
+ tabId,
1851
+ params.locator
1852
+ );
1853
+ if (!pointResult.ok) {
1854
+ respondError(pointResult.error);
1855
+ return;
1856
+ }
1857
+ try {
1858
+ await this.dispatchCdpClick(
1859
+ tabId,
1860
+ pointResult.point.x,
1861
+ pointResult.point.y,
1862
+ 1
1863
+ );
1864
+ } catch (error2) {
1865
+ const info = mapDebuggerErrorMessage(
1866
+ error2 instanceof Error ? error2.message : "Select click failed."
1867
+ );
1868
+ respondError(info);
1869
+ return;
1870
+ }
1871
+ const selectResult = await sendToTab(
1872
+ tabId,
1873
+ "drive.select",
1874
+ params
1875
+ );
1876
+ if (!selectResult.ok) {
1877
+ respondError(selectResult.error);
1878
+ return;
1879
+ }
1880
+ respondOk(selectResult.result ?? { ok: true });
1881
+ return;
1882
+ }
1883
+ case "drive.fill_form": {
1884
+ const params = message.params ?? {};
1885
+ const fields = params.fields;
1886
+ if (!Array.isArray(fields) || fields.length === 0) {
1887
+ respondError({
1888
+ code: "INVALID_ARGUMENT",
1889
+ message: "fields must be a non-empty array.",
1890
+ retryable: false
1891
+ });
1892
+ return;
1893
+ }
1894
+ let tabId = params.tab_id;
1895
+ if (tabId !== void 0 && typeof tabId !== "number") {
1896
+ respondError({
1897
+ code: "INVALID_ARGUMENT",
1898
+ message: "tab_id must be a number when provided.",
1899
+ retryable: false
1900
+ });
1901
+ return;
1902
+ }
1903
+ if (tabId === void 0) {
1904
+ tabId = await getDefaultTabId();
1905
+ }
1906
+ const error = await this.ensureDebuggerAttached(tabId);
1907
+ if (error) {
1908
+ respondError(error);
1909
+ return;
1910
+ }
1911
+ let filled = 0;
1912
+ const errors = [];
1913
+ for (let index = 0; index < fields.length; index += 1) {
1914
+ const field = fields[index];
1915
+ if (!field || typeof field !== "object") {
1916
+ errors.push(`Field ${index} is not an object.`);
1917
+ continue;
1918
+ }
1919
+ const record = field;
1920
+ const value = record.value;
1921
+ if (typeof value !== "string" && typeof value !== "boolean") {
1922
+ errors.push(`Field ${index} has invalid value.`);
1923
+ continue;
1924
+ }
1925
+ const selector = typeof record.selector === "string" ? record.selector : void 0;
1926
+ const locator = record.locator && typeof record.locator === "object" ? record.locator : selector ? { css: selector } : void 0;
1927
+ let resolvedType = typeof record.type === "string" && record.type.length > 0 ? record.type : "auto";
1928
+ if (resolvedType === "auto") {
1929
+ const detected = await sendToTab(
1930
+ tabId,
1931
+ "drive.detect_field_type",
1932
+ { locator: record.locator, selector }
1933
+ );
1934
+ if (!detected.ok) {
1935
+ errors.push(`Field ${index} could not be resolved.`);
1936
+ continue;
1937
+ }
1938
+ const payload2 = detected.result;
1939
+ if (!payload2 || typeof payload2 !== "object") {
1940
+ errors.push(`Field ${index} returned invalid type payload.`);
1941
+ continue;
1942
+ }
1943
+ const detectedType = payload2.fieldType;
1944
+ if (typeof detectedType !== "string" || detectedType.length === 0) {
1945
+ errors.push(`Field ${index} returned invalid field type.`);
1946
+ continue;
1947
+ }
1948
+ resolvedType = detectedType;
1949
+ }
1950
+ if ((resolvedType === "text" || resolvedType === "contentEditable") && locator) {
1951
+ const typed = await this.performCdpType(tabId, {
1952
+ locator,
1953
+ text: String(value),
1954
+ clear: true,
1955
+ submit: Boolean(record.submit)
1956
+ });
1957
+ if (!typed.ok) {
1958
+ errors.push(
1959
+ `Field ${index} could not be filled: ${typed.error.message}`
1960
+ );
1961
+ continue;
1962
+ }
1963
+ filled += 1;
1964
+ continue;
1965
+ }
1966
+ const fallback = await sendToTab(
1967
+ tabId,
1968
+ "drive.fill_form",
1969
+ {
1970
+ fields: [field]
1971
+ }
1972
+ );
1973
+ if (!fallback.ok) {
1974
+ errors.push(
1975
+ `Field ${index} could not be filled: ${fallback.error.message}`
1976
+ );
1977
+ continue;
1978
+ }
1979
+ const payload = fallback.result;
1980
+ if (!payload || typeof payload !== "object") {
1981
+ errors.push(`Field ${index} returned invalid fallback payload.`);
1982
+ continue;
1983
+ }
1984
+ const fallbackFilled = payload.filled;
1985
+ if (typeof fallbackFilled === "number" && Number.isFinite(fallbackFilled) && fallbackFilled > 0) {
1986
+ filled += 1;
1987
+ continue;
1988
+ }
1989
+ errors.push(`Field ${index} could not be filled.`);
1990
+ }
1991
+ respondOk({
1992
+ filled,
1993
+ attempted: fields.length,
1994
+ errors: errors.length > 0 ? errors : []
1995
+ });
1996
+ return;
1997
+ }
1456
1998
  case "drive.scroll":
1457
1999
  case "drive.wait_for": {
1458
2000
  const params = message.params ?? {};
@@ -1468,10 +2010,14 @@ var DriveSocket = class {
1468
2010
  if (tabId === void 0) {
1469
2011
  tabId = await getDefaultTabId();
1470
2012
  }
2013
+ const timeoutMs = message.action === "drive.wait_for" && typeof params.timeout_ms === "number" && Number.isFinite(params.timeout_ms) ? Math.max(1, Math.floor(params.timeout_ms) + 1e3) : void 0;
1471
2014
  const result = await sendToTab(
1472
2015
  tabId,
1473
2016
  message.action,
1474
- params
2017
+ params,
2018
+ {
2019
+ timeoutMs
2020
+ }
1475
2021
  );
1476
2022
  if (result.ok) {
1477
2023
  respondOk(result.result ?? { ok: true });
@@ -1856,6 +2402,290 @@ var DriveSocket = class {
1856
2402
  });
1857
2403
  }
1858
2404
  }
2405
+ async dispatchCdpClick(tabId, x, y, clickCount) {
2406
+ await this.dispatchCdpMouseMove(tabId, x, y, 0);
2407
+ for (let i = 0; i < clickCount; i += 1) {
2408
+ const normalizedClickCount = i + 1;
2409
+ await this.sendDebuggerCommand(
2410
+ tabId,
2411
+ "Input.dispatchMouseEvent",
2412
+ {
2413
+ type: "mousePressed",
2414
+ x,
2415
+ y,
2416
+ button: "left",
2417
+ clickCount: normalizedClickCount
2418
+ },
2419
+ DEFAULT_DEBUGGER_COMMAND_TIMEOUT_MS
2420
+ );
2421
+ await this.sendDebuggerCommand(
2422
+ tabId,
2423
+ "Input.dispatchMouseEvent",
2424
+ {
2425
+ type: "mouseReleased",
2426
+ x,
2427
+ y,
2428
+ button: "left",
2429
+ clickCount: normalizedClickCount
2430
+ },
2431
+ DEFAULT_DEBUGGER_COMMAND_TIMEOUT_MS
2432
+ );
2433
+ }
2434
+ this.touchDebuggerSession(tabId);
2435
+ }
2436
+ async dispatchCdpMouseMove(tabId, x, y, buttons) {
2437
+ await this.sendDebuggerCommand(
2438
+ tabId,
2439
+ "Input.dispatchMouseEvent",
2440
+ {
2441
+ type: "mouseMoved",
2442
+ x,
2443
+ y,
2444
+ button: "none",
2445
+ buttons
2446
+ },
2447
+ DEFAULT_DEBUGGER_COMMAND_TIMEOUT_MS
2448
+ );
2449
+ }
2450
+ async dispatchCdpDrag(tabId, from, to, steps) {
2451
+ await this.dispatchCdpMouseMove(tabId, from.x, from.y, 0);
2452
+ await this.sendDebuggerCommand(
2453
+ tabId,
2454
+ "Input.dispatchMouseEvent",
2455
+ {
2456
+ type: "mousePressed",
2457
+ x: from.x,
2458
+ y: from.y,
2459
+ button: "left",
2460
+ clickCount: 1
2461
+ },
2462
+ DEFAULT_DEBUGGER_COMMAND_TIMEOUT_MS
2463
+ );
2464
+ for (let i = 1; i <= steps; i += 1) {
2465
+ const progress = i / steps;
2466
+ const x = from.x + (to.x - from.x) * progress;
2467
+ const y = from.y + (to.y - from.y) * progress;
2468
+ await this.dispatchCdpMouseMove(tabId, x, y, 1);
2469
+ await delayMs(10);
2470
+ }
2471
+ await this.sendDebuggerCommand(
2472
+ tabId,
2473
+ "Input.dispatchMouseEvent",
2474
+ {
2475
+ type: "mouseReleased",
2476
+ x: to.x,
2477
+ y: to.y,
2478
+ button: "left",
2479
+ clickCount: 1
2480
+ },
2481
+ DEFAULT_DEBUGGER_COMMAND_TIMEOUT_MS
2482
+ );
2483
+ this.touchDebuggerSession(tabId);
2484
+ }
2485
+ async resolveLocatorPoint(tabId, locator) {
2486
+ const point = await sendToTab(tabId, "drive.locator_point", {
2487
+ locator
2488
+ });
2489
+ if (!point.ok) {
2490
+ return point;
2491
+ }
2492
+ const payload = point.result;
2493
+ if (!payload || typeof payload !== "object") {
2494
+ return {
2495
+ ok: false,
2496
+ error: {
2497
+ code: "EVALUATION_FAILED",
2498
+ message: "Invalid locator point payload.",
2499
+ retryable: false
2500
+ }
2501
+ };
2502
+ }
2503
+ const record = payload;
2504
+ const x = record.x;
2505
+ const y = record.y;
2506
+ if (typeof x !== "number" || !Number.isFinite(x) || typeof y !== "number" || !Number.isFinite(y)) {
2507
+ return {
2508
+ ok: false,
2509
+ error: {
2510
+ code: "EVALUATION_FAILED",
2511
+ message: "Invalid locator point coordinates.",
2512
+ retryable: false
2513
+ }
2514
+ };
2515
+ }
2516
+ return { ok: true, point: { x, y } };
2517
+ }
2518
+ async performCdpType(tabId, options) {
2519
+ const targetPoint = await sendToTab(tabId, "drive.type_target_point", {
2520
+ locator: options.locator
2521
+ });
2522
+ if (!targetPoint.ok) {
2523
+ return targetPoint;
2524
+ }
2525
+ const payload = targetPoint.result;
2526
+ if (!payload || typeof payload !== "object") {
2527
+ return {
2528
+ ok: false,
2529
+ error: {
2530
+ code: "EVALUATION_FAILED",
2531
+ message: "Invalid type target payload.",
2532
+ retryable: false
2533
+ }
2534
+ };
2535
+ }
2536
+ const record = payload;
2537
+ const x = record.x;
2538
+ const y = record.y;
2539
+ if (typeof x !== "number" || !Number.isFinite(x) || typeof y !== "number" || !Number.isFinite(y)) {
2540
+ return {
2541
+ ok: false,
2542
+ error: {
2543
+ code: "EVALUATION_FAILED",
2544
+ message: "Invalid type target coordinates.",
2545
+ retryable: false
2546
+ }
2547
+ };
2548
+ }
2549
+ try {
2550
+ await this.dispatchCdpClick(tabId, x, y, 1);
2551
+ if (options.clear) {
2552
+ const clearResult = await sendToTab(
2553
+ tabId,
2554
+ "drive.clear_active_editable"
2555
+ );
2556
+ if (!clearResult.ok) {
2557
+ return clearResult;
2558
+ }
2559
+ }
2560
+ if (options.text.length > 0) {
2561
+ await this.sendDebuggerCommand(
2562
+ tabId,
2563
+ "Input.insertText",
2564
+ { text: options.text },
2565
+ DEFAULT_DEBUGGER_COMMAND_TIMEOUT_MS
2566
+ );
2567
+ }
2568
+ if (options.submit) {
2569
+ await this.dispatchCdpKeyPress(tabId, "Enter", void 0);
2570
+ }
2571
+ this.touchDebuggerSession(tabId);
2572
+ return { ok: true };
2573
+ } catch (error) {
2574
+ return {
2575
+ ok: false,
2576
+ error: mapDebuggerErrorMessage(
2577
+ error instanceof Error ? error.message : "Type dispatch failed."
2578
+ )
2579
+ };
2580
+ }
2581
+ }
2582
+ normalizeModifierMask(modifiers) {
2583
+ const MOD_ALT = 1;
2584
+ const MOD_CTRL = 2;
2585
+ const MOD_META = 4;
2586
+ const MOD_SHIFT = 8;
2587
+ let mask = 0;
2588
+ if (Array.isArray(modifiers)) {
2589
+ for (const modifier of modifiers) {
2590
+ if (typeof modifier !== "string") {
2591
+ continue;
2592
+ }
2593
+ const normalized = modifier.toLowerCase();
2594
+ if (normalized === "alt") {
2595
+ mask |= MOD_ALT;
2596
+ } else if (normalized === "ctrl") {
2597
+ mask |= MOD_CTRL;
2598
+ } else if (normalized === "meta") {
2599
+ mask |= MOD_META;
2600
+ } else if (normalized === "shift") {
2601
+ mask |= MOD_SHIFT;
2602
+ }
2603
+ }
2604
+ return mask;
2605
+ }
2606
+ if (!modifiers || typeof modifiers !== "object") {
2607
+ return mask;
2608
+ }
2609
+ const record = modifiers;
2610
+ if (record.alt) {
2611
+ mask |= MOD_ALT;
2612
+ }
2613
+ if (record.ctrl) {
2614
+ mask |= MOD_CTRL;
2615
+ }
2616
+ if (record.meta) {
2617
+ mask |= MOD_META;
2618
+ }
2619
+ if (record.shift) {
2620
+ mask |= MOD_SHIFT;
2621
+ }
2622
+ return mask;
2623
+ }
2624
+ keyToCode(key) {
2625
+ const map = {
2626
+ Enter: "Enter",
2627
+ Tab: "Tab",
2628
+ Escape: "Escape",
2629
+ Esc: "Escape",
2630
+ Backspace: "Backspace",
2631
+ Delete: "Delete",
2632
+ ArrowUp: "ArrowUp",
2633
+ ArrowDown: "ArrowDown",
2634
+ ArrowLeft: "ArrowLeft",
2635
+ ArrowRight: "ArrowRight",
2636
+ Home: "Home",
2637
+ End: "End",
2638
+ PageUp: "PageUp",
2639
+ PageDown: "PageDown",
2640
+ " ": "Space",
2641
+ Space: "Space"
2642
+ };
2643
+ if (map[key]) {
2644
+ return map[key];
2645
+ }
2646
+ if (key.length === 1) {
2647
+ if (/[a-zA-Z]/.test(key)) {
2648
+ return `Key${key.toUpperCase()}`;
2649
+ }
2650
+ if (/[0-9]/.test(key)) {
2651
+ return `Digit${key}`;
2652
+ }
2653
+ }
2654
+ return key;
2655
+ }
2656
+ async dispatchCdpKeyPress(tabId, key, modifiers) {
2657
+ const code = this.keyToCode(key);
2658
+ const modifierMask = this.normalizeModifierMask(modifiers);
2659
+ const isTextInput = key.length === 1 && (modifierMask & (1 | 2 | 4)) === 0;
2660
+ const keyDownParams = {
2661
+ type: "keyDown",
2662
+ key,
2663
+ code,
2664
+ modifiers: modifierMask
2665
+ };
2666
+ if (isTextInput) {
2667
+ keyDownParams.text = key;
2668
+ keyDownParams.unmodifiedText = key;
2669
+ }
2670
+ await this.sendDebuggerCommand(
2671
+ tabId,
2672
+ "Input.dispatchKeyEvent",
2673
+ keyDownParams,
2674
+ DEFAULT_DEBUGGER_COMMAND_TIMEOUT_MS
2675
+ );
2676
+ await this.sendDebuggerCommand(
2677
+ tabId,
2678
+ "Input.dispatchKeyEvent",
2679
+ {
2680
+ type: "keyUp",
2681
+ key,
2682
+ code,
2683
+ modifiers: modifierMask
2684
+ },
2685
+ DEFAULT_DEBUGGER_COMMAND_TIMEOUT_MS
2686
+ );
2687
+ this.touchDebuggerSession(tabId);
2688
+ }
1859
2689
  async handleDebuggerRequest(message) {
1860
2690
  const respondAck = (result) => {
1861
2691
  this.sendMessage({