agentic-browser 2.4.0 → 2.5.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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { r as createCliRuntime } from "../runtime-PnWvjdzU.mjs";
2
+ import { r as createCliRuntime } from "../runtime-C3Y-UgC1.mjs";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import crypto from "node:crypto";
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { i as createMockAgenticBrowserCore, n as createAgenticBrowserCore, t as AgenticBrowserCore } from "./runtime-PnWvjdzU.mjs";
2
+ import { i as createMockAgenticBrowserCore, n as createAgenticBrowserCore, t as AgenticBrowserCore } from "./runtime-C3Y-UgC1.mjs";
3
3
  export { AgenticBrowserCore, createAgenticBrowserCore, createMockAgenticBrowserCore };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as createAgenticBrowserCore } from "../runtime-PnWvjdzU.mjs";
2
+ import { n as createAgenticBrowserCore } from "../runtime-C3Y-UgC1.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -1932,27 +1932,122 @@ var ChromeCdpBrowserController = class {
1932
1932
  * Returns silently on failure — never blocks navigation.
1933
1933
  */
1934
1934
  async dismissCookieBanner(targetWsUrl) {
1935
- try {
1936
- const domResult = await this.dismissViaDom(targetWsUrl);
1937
- if (domResult.dismissed) return domResult;
1938
- } catch {}
1939
- try {
1940
- const a11yResult = await this.dismissViaA11y(targetWsUrl);
1941
- if (a11yResult.dismissed) return a11yResult;
1942
- } catch {}
1935
+ for (let attempt = 0; attempt < 2; attempt++) {
1936
+ try {
1937
+ const domResult = await this.dismissViaDom(targetWsUrl);
1938
+ if (domResult.dismissed) return domResult;
1939
+ } catch {}
1940
+ try {
1941
+ const iframeResult = await this.dismissViaIframe(targetWsUrl);
1942
+ if (iframeResult.dismissed) return iframeResult;
1943
+ } catch {}
1944
+ try {
1945
+ const a11yResult = await this.dismissViaA11y(targetWsUrl);
1946
+ if (a11yResult.dismissed) return a11yResult;
1947
+ } catch {}
1948
+ if (attempt === 0) await new Promise((resolve) => setTimeout(resolve, 800));
1949
+ }
1943
1950
  return { dismissed: false };
1944
1951
  }
1945
1952
  async dismissViaDom(targetWsUrl) {
1946
1953
  const expression = `(() => {
1947
- // Common cookie-consent button selectors (ordered by specificity)
1954
+ function isVisible(el) {
1955
+ if (!el) return false;
1956
+ const style = getComputedStyle(el);
1957
+ return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0' && el.offsetWidth > 0;
1958
+ }
1959
+
1960
+ // Comprehensive CMP selectors (ordered by specificity/popularity)
1948
1961
  const selectors = [
1962
+ // OneTrust
1949
1963
  '#onetrust-accept-btn-handler',
1964
+ // Cookiebot
1950
1965
  '#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll',
1951
1966
  '#CybotCookiebotDialogBodyButtonAccept',
1967
+ // Usercentrics
1968
+ '#uc-btn-accept-banner',
1969
+ '[data-testid="uc-accept-all-button"]',
1970
+ '.uc-btn-accept',
1971
+ // Didomi
1972
+ '#didomi-notice-agree-button',
1973
+ '.didomi-button[data-action="accept"]',
1974
+ '.didomi-components-button--color.didomi-button-highlight',
1975
+ // Quantcast / GDPR
1976
+ '.qc-cmp2-summary-buttons button[mode="primary"]',
1977
+ '.qc-cmp-button[data-action="accept"]',
1978
+ '#qc-cmp2-ui button.css-1litn2c',
1979
+ // TrustArc / TrueVault
1980
+ '#truste-consent-button',
1981
+ '.trustarc-agree-btn',
1982
+ '#consent_agree',
1983
+ // Consentmanager (zeit.de, etc.)
1984
+ '#cmpbox .cmpboxbtn.cmpboxbtnyes',
1985
+ '#cmpwelcomebtnyes a',
1986
+ '#cmpbntyestxt',
1987
+ '.cmpboxbtnyes',
1988
+ 'a.cmpboxbtn.cmpboxbtnyes',
1989
+ // iubenda
1990
+ '.iubenda-cs-accept-btn',
1991
+ '#iubenda-cs-banner .iubenda-cs-accept-btn',
1992
+ // Klaro
1993
+ '.klaro .cm-btn-accept',
1994
+ '.klaro .cm-btn-success',
1995
+ // Osano
1996
+ '.osano-cm-accept-all',
1997
+ '.osano-cm-button--type_accept',
1998
+ // CookieYes / GDPR Cookie Consent
1999
+ '#cookie_action_close_header',
2000
+ '.cky-btn-accept',
2001
+ '[data-cky-tag="accept-button"]',
2002
+ // Termly
2003
+ '.t-acceptAllButton',
2004
+ '#termly-code-snippet-support .t-acceptAllButton',
2005
+ // Axeptio
2006
+ '#axeptio_btn_acceptAll',
2007
+ '[data-axeptio="accept"]',
2008
+ // Complianz / Really Simple Cookie Consent
2009
+ '.cmplz-accept',
2010
+ '#cmplz-cookiebanner-container .cmplz-accept',
2011
+ // CookieFirst
2012
+ '[data-cookiefirst-action="accept"]',
2013
+ // Cookie Notice / Cookie Law Info
2014
+ '#cookie_action_close_header_reject',
2015
+ '#cn-accept-cookie',
2016
+ // Borlabs Cookie (WordPress)
2017
+ '#CookieBoxSaveButton',
2018
+ 'a[data-cookie-accept]',
2019
+ // Piwik PRO
2020
+ '#PiwikPROConsentForm-agree-to-all',
2021
+ '.PiwikPROConsentForm-agree-to-all-button',
2022
+ // Civic Cookie Control
2023
+ '#ccc-recommended-settings',
2024
+ '#ccc-dismiss-button',
2025
+ '.ccc-accept-settings',
2026
+ // Moove GDPR (WordPress)
2027
+ '.moove-gdpr-infobar-allow-all',
2028
+ '#moove_gdpr_cookie_modal .moove-gdpr-modal-allow-all',
2029
+ // CookieHub
2030
+ '.cookiehub-accept',
2031
+ '#cookiehub-accept-btn',
2032
+ '.ch2-btn-primary',
2033
+ // Cookie Script
2034
+ '#cookiescript_accept',
2035
+ '#cookiescript_injected .cookiescript_accept',
2036
+ // HubSpot Cookie Banner
2037
+ '#hs-eu-confirmation-button',
2038
+ '#hs-eu-cookie-confirmation .hs-eu-cta',
2039
+ // MediaVine
2040
+ '[data-name="mediavine-gdpr-cmp"] .pur-accept',
2041
+ // Sirdata
2042
+ '#sd-cmp .sd-cmp-JEaB',
2043
+ // Google Consent Mode / Funding Choices
2044
+ '.fc-cta-consent',
2045
+ '.fc-button-background[aria-label="Consent"]',
2046
+ // Generic selectors
1952
2047
  '#accept-cookies', '#acceptCookies',
1953
2048
  '#cookie-accept', '#cookieAccept',
1954
2049
  '#gdpr-accept', '#consent-accept',
1955
- '#uc-btn-accept-banner',
2050
+ '#cookies-accept-all', '#accept-all-cookies',
1956
2051
  '.cc-accept', '.cc-btn.cc-dismiss',
1957
2052
  '.cookie-consent-accept', '.cookie-accept-btn',
1958
2053
  '.js-cookie-accept', '.js-accept-cookies',
@@ -1960,39 +2055,99 @@ var ChromeCdpBrowserController = class {
1960
2055
  '.gdpr-accept',
1961
2056
  '[data-testid="cookie-accept"]',
1962
2057
  '[data-action="accept-cookies"]',
1963
- '[data-cookiefirst-action="accept"]',
2058
+ '[data-gdpr="accept"]',
2059
+ '[data-consent="accept"]',
1964
2060
  ];
1965
2061
 
1966
- for (const sel of selectors) {
2062
+ // Query selector in main DOM and open shadow roots
2063
+ function deepQuery(sel) {
1967
2064
  const el = document.querySelector(sel);
1968
- if (el && el.offsetParent !== null) {
1969
- el.click();
1970
- return JSON.stringify({ dismissed: true, method: 'selector', detail: sel });
2065
+ if (el) return el;
2066
+ for (const host of document.querySelectorAll('*')) {
2067
+ if (host.shadowRoot) {
2068
+ const inner = host.shadowRoot.querySelector(sel);
2069
+ if (inner) return inner;
2070
+ }
1971
2071
  }
2072
+ return null;
2073
+ }
2074
+
2075
+ for (const sel of selectors) {
2076
+ try {
2077
+ const el = deepQuery(sel);
2078
+ if (el && isVisible(el)) {
2079
+ el.click();
2080
+ return JSON.stringify({ dismissed: true, method: 'selector', detail: sel });
2081
+ }
2082
+ } catch { /* invalid selector — skip */ }
1972
2083
  }
1973
2084
 
1974
2085
  // Text-based fallback: find visible buttons with consent text
2086
+ // Patterns are intentionally loose — they use .* instead of strict ^/$ anchors
1975
2087
  const patterns = [
1976
- /^accept\\s*(all)?\\s*(cookies)?$/i,
1977
- /^(alle\\s+)?akzeptieren$/i,
1978
- /^(i\\s+)?agree$/i,
1979
- /^allow\\s*(all)?\\s*(cookies)?$/i,
1980
- /^(alle\\s+)?zustimmen$/i,
1981
- /^got\\s*it$/i,
2088
+ // English
2089
+ /accept\\s*(all)?\\s*(cookies)?/i,
2090
+ /allow\\s*(all)?\\s*(cookies)?/i,
2091
+ /(i\\s+)?agree(\\s+to\\s+all)?/i,
2092
+ /got\\s*it/i,
1982
2093
  /^ok$/i,
1983
2094
  /^consent$/i,
1984
- /^(j')?accepte(r)?$/i,
1985
- /^(tout\\s+)?accepter$/i,
1986
- ];
1987
- const candidates = [
1988
- ...document.querySelectorAll('button'),
1989
- ...document.querySelectorAll('a[role="button"]'),
1990
- ...document.querySelectorAll('[role="button"]'),
2095
+ /continue\\s+without\\s+accepting/i,
2096
+ // German
2097
+ /(alle\\s+)?akzeptieren/i,
2098
+ /alle\\s+cookies\\s+akzeptieren/i,
2099
+ /(alle\\s+)?zustimmen/i,
2100
+ /einwilligen/i,
2101
+ /einverstanden/i,
2102
+ /alles\\s+erlauben/i,
2103
+ /alles\\s+akzeptieren/i,
2104
+ /geht\\s+klar/i,
2105
+ /verstanden/i,
2106
+ // French
2107
+ /(j')?accepte(r)?(\\s+tout)?/i,
2108
+ /(tout\\s+)?accepter/i,
2109
+ /autoriser/i,
2110
+ // Spanish
2111
+ /aceptar(\\s+todo)?/i,
2112
+ /aceptar\\s+cookies/i,
2113
+ // Italian
2114
+ /accett(a|o)(\\s+tutt[oi])?/i,
2115
+ /accetta\\s+cookies/i,
2116
+ // Dutch
2117
+ /(alle\\s+)?accepteren/i,
2118
+ /alles\\s+accepteren/i,
2119
+ // Portuguese
2120
+ /aceitar(\\s+tudo)?/i,
2121
+ /aceitar\\s+cookies/i,
2122
+ // Polish
2123
+ /akceptuj(\\s+wszystk[oi]e)?/i,
2124
+ /zgadzam\\s+si/i,
2125
+ // Swedish
2126
+ /acceptera(\\s+alla)?/i,
2127
+ // Danish
2128
+ /accepter(\\s+alle)?/i,
2129
+ // Norwegian
2130
+ /aksepter(\\s+alle)?/i,
1991
2131
  ];
2132
+ // Collect candidates from main DOM and open shadow roots
2133
+ function collectButtons(root) {
2134
+ const results = [];
2135
+ for (const tag of ['button', 'a[role="button"]', '[role="button"]', 'div[role="button"]', 'span[role="button"]']) {
2136
+ results.push(...root.querySelectorAll(tag));
2137
+ }
2138
+ // Traverse open shadow roots for CMPs that use web components
2139
+ for (const el of root.querySelectorAll('*')) {
2140
+ if (el.shadowRoot) {
2141
+ results.push(...collectButtons(el.shadowRoot));
2142
+ }
2143
+ }
2144
+ return results;
2145
+ }
2146
+ const candidates = collectButtons(document);
1992
2147
  for (const el of candidates) {
1993
- if (!el.offsetParent) continue;
2148
+ if (!isVisible(el)) continue;
1994
2149
  const text = (el.textContent || '').trim();
1995
- if (text.length > 40) continue;
2150
+ if (text.length > 60) continue;
1996
2151
  for (const pat of patterns) {
1997
2152
  if (pat.test(text)) {
1998
2153
  el.click();
@@ -2001,6 +2156,90 @@ var ChromeCdpBrowserController = class {
2001
2156
  }
2002
2157
  }
2003
2158
 
2159
+ return JSON.stringify({ dismissed: false });
2160
+ })()`;
2161
+ return await this.withRetry(targetWsUrl, async (conn) => {
2162
+ await this.ensureEnabled(targetWsUrl);
2163
+ const raw = (await conn.send("Runtime.evaluate", {
2164
+ expression,
2165
+ returnByValue: true
2166
+ })).result.value ?? "{\"dismissed\":false}";
2167
+ try {
2168
+ return JSON.parse(raw);
2169
+ } catch {
2170
+ return { dismissed: false };
2171
+ }
2172
+ });
2173
+ }
2174
+ /**
2175
+ * Handle iframe-based consent banners (Sourcepoint, some OneTrust configurations, etc.)
2176
+ * These banners render inside an iframe that needs to be accessed separately.
2177
+ */
2178
+ async dismissViaIframe(targetWsUrl) {
2179
+ const expression = `(() => {
2180
+ // Known consent iframe selectors
2181
+ const iframeSelectors = [
2182
+ 'iframe[id^="sp_message_iframe"]', // Sourcepoint (dynamic ID suffix)
2183
+ '#sp_message_iframe', // Sourcepoint (static ID)
2184
+ 'iframe[id*="sp_message"]', // Sourcepoint variants
2185
+ 'div[id^="sp_message_container"] iframe', // Sourcepoint container with iframe
2186
+ 'iframe[title*="cookie"]', // Generic cookie iframes
2187
+ 'iframe[title*="consent"]', // Generic consent iframes
2188
+ 'iframe[title*="Cookie"]',
2189
+ 'iframe[title*="Consent"]',
2190
+ 'iframe[title*="Privacy"]',
2191
+ 'iframe[title*="privacy"]',
2192
+ 'iframe[title*="GDPR"]',
2193
+ 'iframe[src*="consent"]',
2194
+ 'iframe[src*="cookie"]',
2195
+ 'iframe[src*="privacy"]',
2196
+ 'iframe[src*="sourcepoint"]',
2197
+ 'iframe[src*="consentmanager"]',
2198
+ ];
2199
+
2200
+ for (const sel of iframeSelectors) {
2201
+ try {
2202
+ const iframe = document.querySelector(sel);
2203
+ if (!iframe) continue;
2204
+ const doc = iframe.contentDocument || iframe.contentWindow?.document;
2205
+ if (!doc) continue;
2206
+
2207
+ // Try known selectors inside the iframe
2208
+ const innerSelectors = [
2209
+ // Sourcepoint specific
2210
+ 'button.sp_choice_type_11', // Accept all
2211
+ 'button.sp_choice_type_ACCEPT_ALL', // Accept all variant
2212
+ 'button.sp_choice_type_SAVE_AND_EXIT', // Save & exit
2213
+ '.priv-save-btn', // Sourcepoint frame
2214
+ '.message-button', // Generic Sourcepoint
2215
+ // Title-based
2216
+ 'button[title*="Accept"]', 'button[title*="accept"]',
2217
+ 'button[title*="Agree"]', 'button[title*="agree"]',
2218
+ 'button[title*="Allow"]',
2219
+ 'button[title*="Akzeptieren"]', 'button[title*="akzeptieren"]',
2220
+ 'button[title*="Zustimmen"]',
2221
+ ];
2222
+ for (const innerSel of innerSelectors) {
2223
+ const btn = doc.querySelector(innerSel);
2224
+ if (btn && btn.offsetWidth > 0) {
2225
+ btn.click();
2226
+ return JSON.stringify({ dismissed: true, method: 'iframe', detail: sel + ' > ' + innerSel });
2227
+ }
2228
+ }
2229
+
2230
+ // Text-based fallback inside iframe
2231
+ const buttons = doc.querySelectorAll('button, [role="button"]');
2232
+ for (const btn of buttons) {
2233
+ const text = (btn.textContent || '').trim();
2234
+ if (text.length > 60) continue;
2235
+ if (/accept|agree|allow|akzeptieren|zustimmen|einwilligen|accepter|aceptar/i.test(text)) {
2236
+ btn.click();
2237
+ return JSON.stringify({ dismissed: true, method: 'iframe-text', detail: text });
2238
+ }
2239
+ }
2240
+ } catch { /* cross-origin iframe — skip */ }
2241
+ }
2242
+
2004
2243
  return JSON.stringify({ dismissed: false });
2005
2244
  })()`;
2006
2245
  return await this.withRetry(targetWsUrl, async (conn) => {
@@ -2018,32 +2257,60 @@ var ChromeCdpBrowserController = class {
2018
2257
  }
2019
2258
  /**
2020
2259
  * Use the CDP Accessibility tree to find and click consent buttons.
2021
- * This works even when consent banners use shadow DOM, iframes, or obfuscated class names.
2260
+ * This works even when consent banners use shadow DOM or obfuscated class names.
2261
+ * Also queries cross-origin iframe frames via Page.getFrameTree for Sourcepoint etc.
2022
2262
  */
2023
2263
  async dismissViaA11y(targetWsUrl) {
2024
2264
  return await this.withRetry(targetWsUrl, async (conn) => {
2025
2265
  await conn.send("Accessibility.enable");
2266
+ await conn.send("Page.enable");
2026
2267
  const { nodes } = await conn.send("Accessibility.getFullAXTree");
2268
+ const allNodes = [...nodes];
2269
+ try {
2270
+ const { frameTree } = await conn.send("Page.getFrameTree");
2271
+ for (const child of frameTree.childFrames ?? []) {
2272
+ const url = child.frame.url ?? "";
2273
+ if (/consent|cookie|privacy|sourcepoint|didomi|cmp|gdpr|trustarc|consentmanager|sp_message|onetrust|usercentrics|consent-cdn/i.test(url) || url === "about:blank") try {
2274
+ const frameResult = await conn.send("Accessibility.getFullAXTree", { frameId: child.frame.id });
2275
+ allNodes.push(...frameResult.nodes);
2276
+ } catch {}
2277
+ }
2278
+ } catch {}
2027
2279
  const consentPatterns = [
2028
- /^accept\s*(all)?\s*(cookies)?$/i,
2029
- /^(i\s+)?agree(\s+to\s+all)?$/i,
2030
- /^allow\s*(all)?\s*(cookies)?$/i,
2031
- /^got\s*it$/i,
2280
+ /accept\s*(all)?\s*(cookies)?/i,
2281
+ /allow\s*(all)?\s*(cookies)?/i,
2282
+ /(i\s+)?agree(\s+to\s+all)?/i,
2283
+ /got\s*it/i,
2032
2284
  /^ok$/i,
2033
2285
  /^consent$/i,
2034
- /^(alle\s+)?akzeptieren$/i,
2035
- /^(alle\s+)?zustimmen$/i,
2036
- /^einverstanden$/i,
2037
- /^(j')?accepte(r)?(\s+tout)?$/i,
2038
- /^(tout\s+)?accepter$/i,
2039
- /^aceptar(\s+todo)?$/i,
2040
- /^accetta(\s+tutto)?$/i,
2041
- /^accepteren$/i,
2042
- /^alle\s+accepteren$/i,
2043
- /^aceitar(\s+tudo)?$/i
2286
+ /(alle\s+)?akzeptieren/i,
2287
+ /alle\s+cookies\s+akzeptieren/i,
2288
+ /(alle\s+)?zustimmen/i,
2289
+ /einwilligen/i,
2290
+ /einverstanden/i,
2291
+ /alles\s+erlauben/i,
2292
+ /alles\s+akzeptieren/i,
2293
+ /geht\s+klar/i,
2294
+ /verstanden/i,
2295
+ /(j')?accepte(r)?(\s+tout)?/i,
2296
+ /(tout\s+)?accepter/i,
2297
+ /autoriser/i,
2298
+ /aceptar(\s+todo)?/i,
2299
+ /aceptar\s+cookies/i,
2300
+ /accett(a|o)(\s+tutt[oi])?/i,
2301
+ /accetta\s+cookies/i,
2302
+ /(alle\s+)?accepteren/i,
2303
+ /alles\s+accepteren/i,
2304
+ /aceitar(\s+tudo)?/i,
2305
+ /aceitar\s+cookies/i,
2306
+ /akceptuj(\s+wszystk[oi]e)?/i,
2307
+ /zgadzam\s+si/i,
2308
+ /acceptera(\s+alla)?/i,
2309
+ /accepter(\s+alle)?/i,
2310
+ /aksepter(\s+alle)?/i
2044
2311
  ];
2045
2312
  const candidates = [];
2046
- for (const node of nodes) {
2313
+ for (const node of allNodes) {
2047
2314
  if (node.ignored) continue;
2048
2315
  const role = node.role?.value;
2049
2316
  if (role !== "button" && role !== "link") continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-browser",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "CLI and MCP server for AI agents to control Chrome via CDP",
5
5
  "keywords": [
6
6
  "agent",