agentic-browser 2.4.0 → 2.5.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.
- package/dist/cli/index.mjs +3 -3
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/mcp/index.mjs +2 -2
- package/dist/{runtime-PnWvjdzU.mjs → runtime-BeD8ZcjH.mjs} +474 -76
- package/package.json +13 -13
package/dist/cli/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { r as createCliRuntime } from "../runtime-
|
|
2
|
+
import { r as createCliRuntime } from "../runtime-BeD8ZcjH.mjs";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import crypto from "node:crypto";
|
|
@@ -242,7 +242,7 @@ async function runCommand(runtime, input) {
|
|
|
242
242
|
payload: input.payload
|
|
243
243
|
});
|
|
244
244
|
} catch (error) {
|
|
245
|
-
throw new Error(`Command rejected: ${error.message}
|
|
245
|
+
throw new Error(`Command rejected: ${error.message}`, { cause: error });
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
//#endregion
|
|
@@ -279,11 +279,11 @@ async function runMemoryStats(runtime) {
|
|
|
279
279
|
}
|
|
280
280
|
//#endregion
|
|
281
281
|
//#region src/cli/index.ts
|
|
282
|
+
const collect = (value, previous = []) => [...previous, value];
|
|
282
283
|
async function main() {
|
|
283
284
|
const runtime = createCliRuntime();
|
|
284
285
|
const program = new Command();
|
|
285
286
|
program.name("agentic-browser").description("Agentic browser CLI");
|
|
286
|
-
const collect = (value, previous = []) => [...previous, value];
|
|
287
287
|
program.command("session:start").option("--cdp-url <url>", "connect to existing Chrome via CDP endpoint URL").option("--user-profile <path>", "use 'default' for system Chrome profile or an absolute path").option("--headless", "run Chrome in headless mode (no visible window)").option("--user-agent <string>", "override the browser user-agent string").action(async (options) => {
|
|
288
288
|
if (options.cdpUrl) runtime.context.config.cdpUrl = options.cdpUrl;
|
|
289
289
|
if (options.userProfile) runtime.context.config.userProfileDir = options.userProfile === "true" || options.userProfile === "default" ? "default" : options.userProfile;
|
package/dist/index.d.mts
CHANGED
|
@@ -99,6 +99,7 @@ interface BrowserController {
|
|
|
99
99
|
getCurrentUrl(targetWsUrl: string): Promise<string>;
|
|
100
100
|
terminate(pid: number): void;
|
|
101
101
|
closeConnection?(targetWsUrl: string): void;
|
|
102
|
+
dispose?(): void;
|
|
102
103
|
}
|
|
103
104
|
//#endregion
|
|
104
105
|
//#region src/memory/memory-schemas.d.ts
|
|
@@ -600,8 +601,7 @@ declare class SessionManager {
|
|
|
600
601
|
//#region src/transport/control-api.d.ts
|
|
601
602
|
declare class ControlApi {
|
|
602
603
|
private readonly sessions;
|
|
603
|
-
|
|
604
|
-
constructor(sessions: SessionManager, eventStore: EventStore);
|
|
604
|
+
constructor(sessions: SessionManager);
|
|
605
605
|
createSession(input: {
|
|
606
606
|
browser: "chrome";
|
|
607
607
|
}): Promise<{
|
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-
|
|
2
|
+
import { i as createMockAgenticBrowserCore, n as createAgenticBrowserCore, t as AgenticBrowserCore } from "./runtime-BeD8ZcjH.mjs";
|
|
3
3
|
export { AgenticBrowserCore, createAgenticBrowserCore, createMockAgenticBrowserCore };
|
package/dist/mcp/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as createAgenticBrowserCore } from "../runtime-
|
|
2
|
+
import { n as createAgenticBrowserCore } from "../runtime-BeD8ZcjH.mjs";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -155,7 +155,7 @@ function summarizeElements(elements) {
|
|
|
155
155
|
});
|
|
156
156
|
}
|
|
157
157
|
return {
|
|
158
|
-
countsByRole: Object.fromEntries([...byRole.entries()].
|
|
158
|
+
countsByRole: Object.fromEntries([...byRole.entries()].toSorted((a, b) => b[1] - a[1])),
|
|
159
159
|
primaryActions
|
|
160
160
|
};
|
|
161
161
|
}
|
|
@@ -175,6 +175,10 @@ async function getJson(url, timeoutMs = 5e3) {
|
|
|
175
175
|
if (!response.ok) throw new Error(`HTTP ${response.status}: ${url}`);
|
|
176
176
|
return await response.json();
|
|
177
177
|
}
|
|
178
|
+
/** Debugger connection timing */
|
|
179
|
+
const DEBUGGER_READY_TIMEOUT_MS = 15e3;
|
|
180
|
+
const DEBUGGER_INITIAL_RETRY_DELAY_MS = 50;
|
|
181
|
+
const DEBUGGER_MAX_RETRY_DELAY_MS = 250;
|
|
178
182
|
/** Check if the debug port is accepting TCP connections (faster than an HTTP fetch). */
|
|
179
183
|
function probePort(port) {
|
|
180
184
|
return new Promise((resolve) => {
|
|
@@ -193,16 +197,15 @@ function probePort(port) {
|
|
|
193
197
|
});
|
|
194
198
|
}
|
|
195
199
|
async function waitForDebugger(port) {
|
|
196
|
-
const maxMs = 15e3;
|
|
197
200
|
const start = Date.now();
|
|
198
|
-
let delay =
|
|
199
|
-
while (Date.now() - start <
|
|
201
|
+
let delay = DEBUGGER_INITIAL_RETRY_DELAY_MS;
|
|
202
|
+
while (Date.now() - start < DEBUGGER_READY_TIMEOUT_MS) {
|
|
200
203
|
if (await probePort(port)) try {
|
|
201
204
|
await getJson(`http://127.0.0.1:${port}/json/version`);
|
|
202
205
|
return;
|
|
203
206
|
} catch {}
|
|
204
207
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
205
|
-
delay = Math.min(delay * 2,
|
|
208
|
+
delay = Math.min(delay * 2, DEBUGGER_MAX_RETRY_DELAY_MS);
|
|
206
209
|
}
|
|
207
210
|
throw new Error("Chrome debug endpoint did not become ready in time");
|
|
208
211
|
}
|
|
@@ -694,6 +697,9 @@ function buildKeyDispatchPayload(inputKey) {
|
|
|
694
697
|
nativeVirtualKeyCode: 0
|
|
695
698
|
};
|
|
696
699
|
}
|
|
700
|
+
/** Connection pool configuration */
|
|
701
|
+
const CONNECTION_IDLE_TIMEOUT_MS = 300 * 1e3;
|
|
702
|
+
const CONNECTION_CLEANUP_INTERVAL_MS = 6e4;
|
|
697
703
|
var ChromeCdpBrowserController = class {
|
|
698
704
|
connections = /* @__PURE__ */ new Map();
|
|
699
705
|
cleanupInterval;
|
|
@@ -701,11 +707,16 @@ var ChromeCdpBrowserController = class {
|
|
|
701
707
|
this.baseDir = baseDir;
|
|
702
708
|
this.connectionFactory = connectionFactory;
|
|
703
709
|
this.cleanupInterval = setInterval(() => {
|
|
704
|
-
const cutoff = Date.now() -
|
|
710
|
+
const cutoff = Date.now() - CONNECTION_IDLE_TIMEOUT_MS;
|
|
705
711
|
for (const [url, entry] of this.connections) if (entry.lastUsedAt < cutoff) this.dropConnection(url);
|
|
706
|
-
},
|
|
712
|
+
}, CONNECTION_CLEANUP_INTERVAL_MS);
|
|
707
713
|
this.cleanupInterval.unref();
|
|
708
714
|
}
|
|
715
|
+
/** Stop the cleanup interval and close all connections. Call when disposing the controller. */
|
|
716
|
+
dispose() {
|
|
717
|
+
clearInterval(this.cleanupInterval);
|
|
718
|
+
for (const url of this.connections.keys()) this.dropConnection(url);
|
|
719
|
+
}
|
|
709
720
|
async getConnection(targetWsUrl) {
|
|
710
721
|
const cached = this.connections.get(targetWsUrl);
|
|
711
722
|
if (cached) {
|
|
@@ -849,12 +860,18 @@ var ChromeCdpBrowserController = class {
|
|
|
849
860
|
});
|
|
850
861
|
}
|
|
851
862
|
async handleDialogAction(targetWsUrl, payload) {
|
|
863
|
+
const DIALOG_WAIT_TIMEOUT_MS = 500;
|
|
864
|
+
const DIALOG_POLL_INTERVAL_MS = 10;
|
|
865
|
+
const MAX_POLL_ATTEMPTS = 10;
|
|
852
866
|
return await this.withRetry(targetWsUrl, async (conn) => {
|
|
853
867
|
await this.ensureEnabled(targetWsUrl);
|
|
854
868
|
const cached = this.connections.get(targetWsUrl);
|
|
855
869
|
if (!cached?.pendingDialog) try {
|
|
856
|
-
await conn.waitForEvent("Page.javascriptDialogOpening",
|
|
857
|
-
|
|
870
|
+
await conn.waitForEvent("Page.javascriptDialogOpening", DIALOG_WAIT_TIMEOUT_MS);
|
|
871
|
+
for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
|
|
872
|
+
if (cached?.pendingDialog) break;
|
|
873
|
+
await new Promise((r) => setTimeout(r, DIALOG_POLL_INTERVAL_MS));
|
|
874
|
+
}
|
|
858
875
|
} catch {
|
|
859
876
|
return "no dialog present";
|
|
860
877
|
}
|
|
@@ -1932,27 +1949,122 @@ var ChromeCdpBrowserController = class {
|
|
|
1932
1949
|
* Returns silently on failure — never blocks navigation.
|
|
1933
1950
|
*/
|
|
1934
1951
|
async dismissCookieBanner(targetWsUrl) {
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1952
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1953
|
+
try {
|
|
1954
|
+
const domResult = await this.dismissViaDom(targetWsUrl);
|
|
1955
|
+
if (domResult.dismissed) return domResult;
|
|
1956
|
+
} catch {}
|
|
1957
|
+
try {
|
|
1958
|
+
const iframeResult = await this.dismissViaIframe(targetWsUrl);
|
|
1959
|
+
if (iframeResult.dismissed) return iframeResult;
|
|
1960
|
+
} catch {}
|
|
1961
|
+
try {
|
|
1962
|
+
const a11yResult = await this.dismissViaA11y(targetWsUrl);
|
|
1963
|
+
if (a11yResult.dismissed) return a11yResult;
|
|
1964
|
+
} catch {}
|
|
1965
|
+
if (attempt === 0) await new Promise((resolve) => setTimeout(resolve, 800));
|
|
1966
|
+
}
|
|
1943
1967
|
return { dismissed: false };
|
|
1944
1968
|
}
|
|
1945
1969
|
async dismissViaDom(targetWsUrl) {
|
|
1946
1970
|
const expression = `(() => {
|
|
1947
|
-
|
|
1971
|
+
function isVisible(el) {
|
|
1972
|
+
if (!el) return false;
|
|
1973
|
+
const style = getComputedStyle(el);
|
|
1974
|
+
return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0' && el.offsetWidth > 0;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
// Comprehensive CMP selectors (ordered by specificity/popularity)
|
|
1948
1978
|
const selectors = [
|
|
1979
|
+
// OneTrust
|
|
1949
1980
|
'#onetrust-accept-btn-handler',
|
|
1981
|
+
// Cookiebot
|
|
1950
1982
|
'#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll',
|
|
1951
1983
|
'#CybotCookiebotDialogBodyButtonAccept',
|
|
1984
|
+
// Usercentrics
|
|
1985
|
+
'#uc-btn-accept-banner',
|
|
1986
|
+
'[data-testid="uc-accept-all-button"]',
|
|
1987
|
+
'.uc-btn-accept',
|
|
1988
|
+
// Didomi
|
|
1989
|
+
'#didomi-notice-agree-button',
|
|
1990
|
+
'.didomi-button[data-action="accept"]',
|
|
1991
|
+
'.didomi-components-button--color.didomi-button-highlight',
|
|
1992
|
+
// Quantcast / GDPR
|
|
1993
|
+
'.qc-cmp2-summary-buttons button[mode="primary"]',
|
|
1994
|
+
'.qc-cmp-button[data-action="accept"]',
|
|
1995
|
+
'#qc-cmp2-ui button.css-1litn2c',
|
|
1996
|
+
// TrustArc / TrueVault
|
|
1997
|
+
'#truste-consent-button',
|
|
1998
|
+
'.trustarc-agree-btn',
|
|
1999
|
+
'#consent_agree',
|
|
2000
|
+
// Consentmanager (zeit.de, etc.)
|
|
2001
|
+
'#cmpbox .cmpboxbtn.cmpboxbtnyes',
|
|
2002
|
+
'#cmpwelcomebtnyes a',
|
|
2003
|
+
'#cmpbntyestxt',
|
|
2004
|
+
'.cmpboxbtnyes',
|
|
2005
|
+
'a.cmpboxbtn.cmpboxbtnyes',
|
|
2006
|
+
// iubenda
|
|
2007
|
+
'.iubenda-cs-accept-btn',
|
|
2008
|
+
'#iubenda-cs-banner .iubenda-cs-accept-btn',
|
|
2009
|
+
// Klaro
|
|
2010
|
+
'.klaro .cm-btn-accept',
|
|
2011
|
+
'.klaro .cm-btn-success',
|
|
2012
|
+
// Osano
|
|
2013
|
+
'.osano-cm-accept-all',
|
|
2014
|
+
'.osano-cm-button--type_accept',
|
|
2015
|
+
// CookieYes / GDPR Cookie Consent
|
|
2016
|
+
'#cookie_action_close_header',
|
|
2017
|
+
'.cky-btn-accept',
|
|
2018
|
+
'[data-cky-tag="accept-button"]',
|
|
2019
|
+
// Termly
|
|
2020
|
+
'.t-acceptAllButton',
|
|
2021
|
+
'#termly-code-snippet-support .t-acceptAllButton',
|
|
2022
|
+
// Axeptio
|
|
2023
|
+
'#axeptio_btn_acceptAll',
|
|
2024
|
+
'[data-axeptio="accept"]',
|
|
2025
|
+
// Complianz / Really Simple Cookie Consent
|
|
2026
|
+
'.cmplz-accept',
|
|
2027
|
+
'#cmplz-cookiebanner-container .cmplz-accept',
|
|
2028
|
+
// CookieFirst
|
|
2029
|
+
'[data-cookiefirst-action="accept"]',
|
|
2030
|
+
// Cookie Notice / Cookie Law Info
|
|
2031
|
+
'#cookie_action_close_header_reject',
|
|
2032
|
+
'#cn-accept-cookie',
|
|
2033
|
+
// Borlabs Cookie (WordPress)
|
|
2034
|
+
'#CookieBoxSaveButton',
|
|
2035
|
+
'a[data-cookie-accept]',
|
|
2036
|
+
// Piwik PRO
|
|
2037
|
+
'#PiwikPROConsentForm-agree-to-all',
|
|
2038
|
+
'.PiwikPROConsentForm-agree-to-all-button',
|
|
2039
|
+
// Civic Cookie Control
|
|
2040
|
+
'#ccc-recommended-settings',
|
|
2041
|
+
'#ccc-dismiss-button',
|
|
2042
|
+
'.ccc-accept-settings',
|
|
2043
|
+
// Moove GDPR (WordPress)
|
|
2044
|
+
'.moove-gdpr-infobar-allow-all',
|
|
2045
|
+
'#moove_gdpr_cookie_modal .moove-gdpr-modal-allow-all',
|
|
2046
|
+
// CookieHub
|
|
2047
|
+
'.cookiehub-accept',
|
|
2048
|
+
'#cookiehub-accept-btn',
|
|
2049
|
+
'.ch2-btn-primary',
|
|
2050
|
+
// Cookie Script
|
|
2051
|
+
'#cookiescript_accept',
|
|
2052
|
+
'#cookiescript_injected .cookiescript_accept',
|
|
2053
|
+
// HubSpot Cookie Banner
|
|
2054
|
+
'#hs-eu-confirmation-button',
|
|
2055
|
+
'#hs-eu-cookie-confirmation .hs-eu-cta',
|
|
2056
|
+
// MediaVine
|
|
2057
|
+
'[data-name="mediavine-gdpr-cmp"] .pur-accept',
|
|
2058
|
+
// Sirdata
|
|
2059
|
+
'#sd-cmp .sd-cmp-JEaB',
|
|
2060
|
+
// Google Consent Mode / Funding Choices
|
|
2061
|
+
'.fc-cta-consent',
|
|
2062
|
+
'.fc-button-background[aria-label="Consent"]',
|
|
2063
|
+
// Generic selectors
|
|
1952
2064
|
'#accept-cookies', '#acceptCookies',
|
|
1953
2065
|
'#cookie-accept', '#cookieAccept',
|
|
1954
2066
|
'#gdpr-accept', '#consent-accept',
|
|
1955
|
-
'#
|
|
2067
|
+
'#cookies-accept-all', '#accept-all-cookies',
|
|
1956
2068
|
'.cc-accept', '.cc-btn.cc-dismiss',
|
|
1957
2069
|
'.cookie-consent-accept', '.cookie-accept-btn',
|
|
1958
2070
|
'.js-cookie-accept', '.js-accept-cookies',
|
|
@@ -1960,39 +2072,99 @@ var ChromeCdpBrowserController = class {
|
|
|
1960
2072
|
'.gdpr-accept',
|
|
1961
2073
|
'[data-testid="cookie-accept"]',
|
|
1962
2074
|
'[data-action="accept-cookies"]',
|
|
1963
|
-
'[data-
|
|
2075
|
+
'[data-gdpr="accept"]',
|
|
2076
|
+
'[data-consent="accept"]',
|
|
1964
2077
|
];
|
|
1965
2078
|
|
|
1966
|
-
|
|
2079
|
+
// Query selector in main DOM and open shadow roots
|
|
2080
|
+
function deepQuery(sel) {
|
|
1967
2081
|
const el = document.querySelector(sel);
|
|
1968
|
-
if (el
|
|
1969
|
-
|
|
1970
|
-
|
|
2082
|
+
if (el) return el;
|
|
2083
|
+
for (const host of document.querySelectorAll('*')) {
|
|
2084
|
+
if (host.shadowRoot) {
|
|
2085
|
+
const inner = host.shadowRoot.querySelector(sel);
|
|
2086
|
+
if (inner) return inner;
|
|
2087
|
+
}
|
|
1971
2088
|
}
|
|
2089
|
+
return null;
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
for (const sel of selectors) {
|
|
2093
|
+
try {
|
|
2094
|
+
const el = deepQuery(sel);
|
|
2095
|
+
if (el && isVisible(el)) {
|
|
2096
|
+
el.click();
|
|
2097
|
+
return JSON.stringify({ dismissed: true, method: 'selector', detail: sel });
|
|
2098
|
+
}
|
|
2099
|
+
} catch { /* invalid selector — skip */ }
|
|
1972
2100
|
}
|
|
1973
2101
|
|
|
1974
2102
|
// Text-based fallback: find visible buttons with consent text
|
|
2103
|
+
// Patterns are intentionally loose — they use .* instead of strict ^/$ anchors
|
|
1975
2104
|
const patterns = [
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
/^got\\s*it$/i,
|
|
2105
|
+
// English
|
|
2106
|
+
/accept\\s*(all)?\\s*(cookies)?/i,
|
|
2107
|
+
/allow\\s*(all)?\\s*(cookies)?/i,
|
|
2108
|
+
/(i\\s+)?agree(\\s+to\\s+all)?/i,
|
|
2109
|
+
/got\\s*it/i,
|
|
1982
2110
|
/^ok$/i,
|
|
1983
2111
|
/^consent$/i,
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
2112
|
+
/continue\\s+without\\s+accepting/i,
|
|
2113
|
+
// German
|
|
2114
|
+
/(alle\\s+)?akzeptieren/i,
|
|
2115
|
+
/alle\\s+cookies\\s+akzeptieren/i,
|
|
2116
|
+
/(alle\\s+)?zustimmen/i,
|
|
2117
|
+
/einwilligen/i,
|
|
2118
|
+
/einverstanden/i,
|
|
2119
|
+
/alles\\s+erlauben/i,
|
|
2120
|
+
/alles\\s+akzeptieren/i,
|
|
2121
|
+
/geht\\s+klar/i,
|
|
2122
|
+
/verstanden/i,
|
|
2123
|
+
// French
|
|
2124
|
+
/(j')?accepte(r)?(\\s+tout)?/i,
|
|
2125
|
+
/(tout\\s+)?accepter/i,
|
|
2126
|
+
/autoriser/i,
|
|
2127
|
+
// Spanish
|
|
2128
|
+
/aceptar(\\s+todo)?/i,
|
|
2129
|
+
/aceptar\\s+cookies/i,
|
|
2130
|
+
// Italian
|
|
2131
|
+
/accett(a|o)(\\s+tutt[oi])?/i,
|
|
2132
|
+
/accetta\\s+cookies/i,
|
|
2133
|
+
// Dutch
|
|
2134
|
+
/(alle\\s+)?accepteren/i,
|
|
2135
|
+
/alles\\s+accepteren/i,
|
|
2136
|
+
// Portuguese
|
|
2137
|
+
/aceitar(\\s+tudo)?/i,
|
|
2138
|
+
/aceitar\\s+cookies/i,
|
|
2139
|
+
// Polish
|
|
2140
|
+
/akceptuj(\\s+wszystk[oi]e)?/i,
|
|
2141
|
+
/zgadzam\\s+si/i,
|
|
2142
|
+
// Swedish
|
|
2143
|
+
/acceptera(\\s+alla)?/i,
|
|
2144
|
+
// Danish
|
|
2145
|
+
/accepter(\\s+alle)?/i,
|
|
2146
|
+
// Norwegian
|
|
2147
|
+
/aksepter(\\s+alle)?/i,
|
|
1991
2148
|
];
|
|
2149
|
+
// Collect candidates from main DOM and open shadow roots
|
|
2150
|
+
function collectButtons(root) {
|
|
2151
|
+
const results = [];
|
|
2152
|
+
for (const tag of ['button', 'a[role="button"]', '[role="button"]', 'div[role="button"]', 'span[role="button"]']) {
|
|
2153
|
+
results.push(...root.querySelectorAll(tag));
|
|
2154
|
+
}
|
|
2155
|
+
// Traverse open shadow roots for CMPs that use web components
|
|
2156
|
+
for (const el of root.querySelectorAll('*')) {
|
|
2157
|
+
if (el.shadowRoot) {
|
|
2158
|
+
results.push(...collectButtons(el.shadowRoot));
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
return results;
|
|
2162
|
+
}
|
|
2163
|
+
const candidates = collectButtons(document);
|
|
1992
2164
|
for (const el of candidates) {
|
|
1993
|
-
if (!el
|
|
2165
|
+
if (!isVisible(el)) continue;
|
|
1994
2166
|
const text = (el.textContent || '').trim();
|
|
1995
|
-
if (text.length >
|
|
2167
|
+
if (text.length > 60) continue;
|
|
1996
2168
|
for (const pat of patterns) {
|
|
1997
2169
|
if (pat.test(text)) {
|
|
1998
2170
|
el.click();
|
|
@@ -2001,6 +2173,90 @@ var ChromeCdpBrowserController = class {
|
|
|
2001
2173
|
}
|
|
2002
2174
|
}
|
|
2003
2175
|
|
|
2176
|
+
return JSON.stringify({ dismissed: false });
|
|
2177
|
+
})()`;
|
|
2178
|
+
return await this.withRetry(targetWsUrl, async (conn) => {
|
|
2179
|
+
await this.ensureEnabled(targetWsUrl);
|
|
2180
|
+
const raw = (await conn.send("Runtime.evaluate", {
|
|
2181
|
+
expression,
|
|
2182
|
+
returnByValue: true
|
|
2183
|
+
})).result.value ?? "{\"dismissed\":false}";
|
|
2184
|
+
try {
|
|
2185
|
+
return JSON.parse(raw);
|
|
2186
|
+
} catch {
|
|
2187
|
+
return { dismissed: false };
|
|
2188
|
+
}
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
/**
|
|
2192
|
+
* Handle iframe-based consent banners (Sourcepoint, some OneTrust configurations, etc.)
|
|
2193
|
+
* These banners render inside an iframe that needs to be accessed separately.
|
|
2194
|
+
*/
|
|
2195
|
+
async dismissViaIframe(targetWsUrl) {
|
|
2196
|
+
const expression = `(() => {
|
|
2197
|
+
// Known consent iframe selectors
|
|
2198
|
+
const iframeSelectors = [
|
|
2199
|
+
'iframe[id^="sp_message_iframe"]', // Sourcepoint (dynamic ID suffix)
|
|
2200
|
+
'#sp_message_iframe', // Sourcepoint (static ID)
|
|
2201
|
+
'iframe[id*="sp_message"]', // Sourcepoint variants
|
|
2202
|
+
'div[id^="sp_message_container"] iframe', // Sourcepoint container with iframe
|
|
2203
|
+
'iframe[title*="cookie"]', // Generic cookie iframes
|
|
2204
|
+
'iframe[title*="consent"]', // Generic consent iframes
|
|
2205
|
+
'iframe[title*="Cookie"]',
|
|
2206
|
+
'iframe[title*="Consent"]',
|
|
2207
|
+
'iframe[title*="Privacy"]',
|
|
2208
|
+
'iframe[title*="privacy"]',
|
|
2209
|
+
'iframe[title*="GDPR"]',
|
|
2210
|
+
'iframe[src*="consent"]',
|
|
2211
|
+
'iframe[src*="cookie"]',
|
|
2212
|
+
'iframe[src*="privacy"]',
|
|
2213
|
+
'iframe[src*="sourcepoint"]',
|
|
2214
|
+
'iframe[src*="consentmanager"]',
|
|
2215
|
+
];
|
|
2216
|
+
|
|
2217
|
+
for (const sel of iframeSelectors) {
|
|
2218
|
+
try {
|
|
2219
|
+
const iframe = document.querySelector(sel);
|
|
2220
|
+
if (!iframe) continue;
|
|
2221
|
+
const doc = iframe.contentDocument || iframe.contentWindow?.document;
|
|
2222
|
+
if (!doc) continue;
|
|
2223
|
+
|
|
2224
|
+
// Try known selectors inside the iframe
|
|
2225
|
+
const innerSelectors = [
|
|
2226
|
+
// Sourcepoint specific
|
|
2227
|
+
'button.sp_choice_type_11', // Accept all
|
|
2228
|
+
'button.sp_choice_type_ACCEPT_ALL', // Accept all variant
|
|
2229
|
+
'button.sp_choice_type_SAVE_AND_EXIT', // Save & exit
|
|
2230
|
+
'.priv-save-btn', // Sourcepoint frame
|
|
2231
|
+
'.message-button', // Generic Sourcepoint
|
|
2232
|
+
// Title-based
|
|
2233
|
+
'button[title*="Accept"]', 'button[title*="accept"]',
|
|
2234
|
+
'button[title*="Agree"]', 'button[title*="agree"]',
|
|
2235
|
+
'button[title*="Allow"]',
|
|
2236
|
+
'button[title*="Akzeptieren"]', 'button[title*="akzeptieren"]',
|
|
2237
|
+
'button[title*="Zustimmen"]',
|
|
2238
|
+
];
|
|
2239
|
+
for (const innerSel of innerSelectors) {
|
|
2240
|
+
const btn = doc.querySelector(innerSel);
|
|
2241
|
+
if (btn && btn.offsetWidth > 0) {
|
|
2242
|
+
btn.click();
|
|
2243
|
+
return JSON.stringify({ dismissed: true, method: 'iframe', detail: sel + ' > ' + innerSel });
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
// Text-based fallback inside iframe
|
|
2248
|
+
const buttons = doc.querySelectorAll('button, [role="button"]');
|
|
2249
|
+
for (const btn of buttons) {
|
|
2250
|
+
const text = (btn.textContent || '').trim();
|
|
2251
|
+
if (text.length > 60) continue;
|
|
2252
|
+
if (/accept|agree|allow|akzeptieren|zustimmen|einwilligen|accepter|aceptar/i.test(text)) {
|
|
2253
|
+
btn.click();
|
|
2254
|
+
return JSON.stringify({ dismissed: true, method: 'iframe-text', detail: text });
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
} catch { /* cross-origin iframe — skip */ }
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2004
2260
|
return JSON.stringify({ dismissed: false });
|
|
2005
2261
|
})()`;
|
|
2006
2262
|
return await this.withRetry(targetWsUrl, async (conn) => {
|
|
@@ -2018,32 +2274,60 @@ var ChromeCdpBrowserController = class {
|
|
|
2018
2274
|
}
|
|
2019
2275
|
/**
|
|
2020
2276
|
* Use the CDP Accessibility tree to find and click consent buttons.
|
|
2021
|
-
* This works even when consent banners use shadow DOM
|
|
2277
|
+
* This works even when consent banners use shadow DOM or obfuscated class names.
|
|
2278
|
+
* Also queries cross-origin iframe frames via Page.getFrameTree for Sourcepoint etc.
|
|
2022
2279
|
*/
|
|
2023
2280
|
async dismissViaA11y(targetWsUrl) {
|
|
2024
2281
|
return await this.withRetry(targetWsUrl, async (conn) => {
|
|
2025
2282
|
await conn.send("Accessibility.enable");
|
|
2283
|
+
await conn.send("Page.enable");
|
|
2026
2284
|
const { nodes } = await conn.send("Accessibility.getFullAXTree");
|
|
2285
|
+
const allNodes = [...nodes];
|
|
2286
|
+
try {
|
|
2287
|
+
const { frameTree } = await conn.send("Page.getFrameTree");
|
|
2288
|
+
for (const child of frameTree.childFrames ?? []) {
|
|
2289
|
+
const url = child.frame.url ?? "";
|
|
2290
|
+
if (/consent|cookie|privacy|sourcepoint|didomi|cmp|gdpr|trustarc|consentmanager|sp_message|onetrust|usercentrics|consent-cdn/i.test(url) || url === "about:blank") try {
|
|
2291
|
+
const frameResult = await conn.send("Accessibility.getFullAXTree", { frameId: child.frame.id });
|
|
2292
|
+
allNodes.push(...frameResult.nodes);
|
|
2293
|
+
} catch {}
|
|
2294
|
+
}
|
|
2295
|
+
} catch {}
|
|
2027
2296
|
const consentPatterns = [
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2297
|
+
/accept\s*(all)?\s*(cookies)?/i,
|
|
2298
|
+
/allow\s*(all)?\s*(cookies)?/i,
|
|
2299
|
+
/(i\s+)?agree(\s+to\s+all)?/i,
|
|
2300
|
+
/got\s*it/i,
|
|
2032
2301
|
/^ok$/i,
|
|
2033
2302
|
/^consent$/i,
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2303
|
+
/(alle\s+)?akzeptieren/i,
|
|
2304
|
+
/alle\s+cookies\s+akzeptieren/i,
|
|
2305
|
+
/(alle\s+)?zustimmen/i,
|
|
2306
|
+
/einwilligen/i,
|
|
2307
|
+
/einverstanden/i,
|
|
2308
|
+
/alles\s+erlauben/i,
|
|
2309
|
+
/alles\s+akzeptieren/i,
|
|
2310
|
+
/geht\s+klar/i,
|
|
2311
|
+
/verstanden/i,
|
|
2312
|
+
/(j')?accepte(r)?(\s+tout)?/i,
|
|
2313
|
+
/(tout\s+)?accepter/i,
|
|
2314
|
+
/autoriser/i,
|
|
2315
|
+
/aceptar(\s+todo)?/i,
|
|
2316
|
+
/aceptar\s+cookies/i,
|
|
2317
|
+
/accett(a|o)(\s+tutt[oi])?/i,
|
|
2318
|
+
/accetta\s+cookies/i,
|
|
2319
|
+
/(alle\s+)?accepteren/i,
|
|
2320
|
+
/alles\s+accepteren/i,
|
|
2321
|
+
/aceitar(\s+tudo)?/i,
|
|
2322
|
+
/aceitar\s+cookies/i,
|
|
2323
|
+
/akceptuj(\s+wszystk[oi]e)?/i,
|
|
2324
|
+
/zgadzam\s+si/i,
|
|
2325
|
+
/acceptera(\s+alla)?/i,
|
|
2326
|
+
/accepter(\s+alle)?/i,
|
|
2327
|
+
/aksepter(\s+alle)?/i
|
|
2044
2328
|
];
|
|
2045
2329
|
const candidates = [];
|
|
2046
|
-
for (const node of
|
|
2330
|
+
for (const node of allNodes) {
|
|
2047
2331
|
if (node.ignored) continue;
|
|
2048
2332
|
const role = node.role?.value;
|
|
2049
2333
|
if (role !== "button" && role !== "link") continue;
|
|
@@ -2200,6 +2484,113 @@ var MockBrowserController = class {
|
|
|
2200
2484
|
closeConnection(_targetWsUrl) {}
|
|
2201
2485
|
};
|
|
2202
2486
|
//#endregion
|
|
2487
|
+
//#region src/lib/domain-schemas.ts
|
|
2488
|
+
const InteractActionSchema = z.enum([
|
|
2489
|
+
"click",
|
|
2490
|
+
"type",
|
|
2491
|
+
"press",
|
|
2492
|
+
"waitFor",
|
|
2493
|
+
"evaluate",
|
|
2494
|
+
"scroll",
|
|
2495
|
+
"hover",
|
|
2496
|
+
"select",
|
|
2497
|
+
"toggle",
|
|
2498
|
+
"goBack",
|
|
2499
|
+
"goForward",
|
|
2500
|
+
"refresh",
|
|
2501
|
+
"dialog"
|
|
2502
|
+
]);
|
|
2503
|
+
const InteractPayloadSchema = z.object({
|
|
2504
|
+
action: InteractActionSchema,
|
|
2505
|
+
selector: z.string().optional(),
|
|
2506
|
+
fallbackSelectors: z.array(z.string()).optional(),
|
|
2507
|
+
text: z.string().optional(),
|
|
2508
|
+
key: z.string().optional(),
|
|
2509
|
+
value: z.string().optional(),
|
|
2510
|
+
scrollX: z.number().optional(),
|
|
2511
|
+
scrollY: z.number().optional(),
|
|
2512
|
+
timeoutMs: z.number().optional()
|
|
2513
|
+
});
|
|
2514
|
+
const SessionStatusSchema = z.enum([
|
|
2515
|
+
"starting",
|
|
2516
|
+
"ready",
|
|
2517
|
+
"disconnected",
|
|
2518
|
+
"failed",
|
|
2519
|
+
"terminated"
|
|
2520
|
+
]);
|
|
2521
|
+
const CommandTypeSchema = z.enum([
|
|
2522
|
+
"navigate",
|
|
2523
|
+
"interact",
|
|
2524
|
+
"restart",
|
|
2525
|
+
"terminate"
|
|
2526
|
+
]);
|
|
2527
|
+
const ResultStatusSchema = z.enum([
|
|
2528
|
+
"success",
|
|
2529
|
+
"failed",
|
|
2530
|
+
"timed_out"
|
|
2531
|
+
]);
|
|
2532
|
+
const ConnectionStateSchema = z.enum([
|
|
2533
|
+
"connecting",
|
|
2534
|
+
"ready",
|
|
2535
|
+
"disconnected",
|
|
2536
|
+
"failed"
|
|
2537
|
+
]);
|
|
2538
|
+
const EventCategorySchema = z.enum([
|
|
2539
|
+
"lifecycle",
|
|
2540
|
+
"security",
|
|
2541
|
+
"command"
|
|
2542
|
+
]);
|
|
2543
|
+
const EventSeveritySchema = z.enum([
|
|
2544
|
+
"info",
|
|
2545
|
+
"warning",
|
|
2546
|
+
"error"
|
|
2547
|
+
]);
|
|
2548
|
+
z.object({
|
|
2549
|
+
sessionId: z.string().min(1),
|
|
2550
|
+
status: SessionStatusSchema,
|
|
2551
|
+
browserType: z.literal("chrome"),
|
|
2552
|
+
startedAt: z.string().datetime(),
|
|
2553
|
+
endedAt: z.string().datetime().optional(),
|
|
2554
|
+
authTokenRef: z.string().min(1)
|
|
2555
|
+
}).refine((session) => session.status === "terminated" ? Boolean(session.endedAt) : true, {
|
|
2556
|
+
message: "endedAt is required for terminated sessions",
|
|
2557
|
+
path: ["endedAt"]
|
|
2558
|
+
});
|
|
2559
|
+
z.object({
|
|
2560
|
+
commandId: z.string().min(1),
|
|
2561
|
+
sessionId: z.string().min(1),
|
|
2562
|
+
type: CommandTypeSchema,
|
|
2563
|
+
payload: z.record(z.string(), z.unknown()),
|
|
2564
|
+
submittedAt: z.string().datetime(),
|
|
2565
|
+
completedAt: z.string().datetime().optional(),
|
|
2566
|
+
resultStatus: ResultStatusSchema.optional(),
|
|
2567
|
+
resultMessage: z.string().optional()
|
|
2568
|
+
}).refine((command) => command.completedAt ? Boolean(command.resultStatus) : true, {
|
|
2569
|
+
message: "resultStatus is required when completedAt is set",
|
|
2570
|
+
path: ["resultStatus"]
|
|
2571
|
+
});
|
|
2572
|
+
z.object({
|
|
2573
|
+
sessionId: z.string().min(1),
|
|
2574
|
+
state: ConnectionStateSchema,
|
|
2575
|
+
lastHeartbeatAt: z.string().datetime().optional(),
|
|
2576
|
+
disconnectReason: z.string().optional()
|
|
2577
|
+
});
|
|
2578
|
+
z.object({
|
|
2579
|
+
sessionId: z.string().min(1),
|
|
2580
|
+
tokenId: z.string().min(1),
|
|
2581
|
+
issuedAt: z.string().datetime(),
|
|
2582
|
+
expiresAt: z.string().datetime().optional(),
|
|
2583
|
+
revokedAt: z.string().datetime().optional()
|
|
2584
|
+
});
|
|
2585
|
+
z.object({
|
|
2586
|
+
eventId: z.string().min(1),
|
|
2587
|
+
sessionId: z.string().min(1),
|
|
2588
|
+
category: EventCategorySchema,
|
|
2589
|
+
message: z.string().min(1),
|
|
2590
|
+
createdAt: z.string().datetime(),
|
|
2591
|
+
severity: EventSeveritySchema
|
|
2592
|
+
});
|
|
2593
|
+
//#endregion
|
|
2203
2594
|
//#region src/session/session-store.ts
|
|
2204
2595
|
var SessionStore = class {
|
|
2205
2596
|
filePath;
|
|
@@ -2343,7 +2734,7 @@ var SessionManager = class {
|
|
|
2343
2734
|
return this.mustGetRecord(recovered.sessionId);
|
|
2344
2735
|
} catch (restartError) {
|
|
2345
2736
|
this.recordEvent(sessionId, "lifecycle", "error", `Recovery failed: ${restartError.message}`);
|
|
2346
|
-
throw new Error(`Session is not ready and recovery failed: ${restartError.message}
|
|
2737
|
+
throw new Error(`Session is not ready and recovery failed: ${restartError.message}`, { cause: restartError });
|
|
2347
2738
|
}
|
|
2348
2739
|
throw new Error("Session is terminated. Start a new session.");
|
|
2349
2740
|
}
|
|
@@ -2372,8 +2763,11 @@ var SessionManager = class {
|
|
|
2372
2763
|
const finalUrl = await this.browser.navigate(record.targetWsUrl, url);
|
|
2373
2764
|
this.store.setLastUrl(sessionId, finalUrl);
|
|
2374
2765
|
resultMessage = `Navigated to ${finalUrl}`;
|
|
2375
|
-
} else if (input.type === "interact")
|
|
2376
|
-
|
|
2766
|
+
} else if (input.type === "interact") {
|
|
2767
|
+
const parsedPayload = InteractPayloadSchema.safeParse(input.payload);
|
|
2768
|
+
if (!parsedPayload.success) throw new Error(`Invalid interact payload: ${parsedPayload.error.message}`);
|
|
2769
|
+
resultMessage = `Interaction result: ${await this.browser.interact(record.targetWsUrl, parsedPayload.data)}`;
|
|
2770
|
+
} else if (input.type === "restart") {
|
|
2377
2771
|
await this.restartSession(sessionId);
|
|
2378
2772
|
resultMessage = "Session restarted";
|
|
2379
2773
|
} else if (input.type === "terminate") {
|
|
@@ -2685,9 +3079,8 @@ var SessionManager = class {
|
|
|
2685
3079
|
//#endregion
|
|
2686
3080
|
//#region src/transport/control-api.ts
|
|
2687
3081
|
var ControlApi = class {
|
|
2688
|
-
constructor(sessions
|
|
3082
|
+
constructor(sessions) {
|
|
2689
3083
|
this.sessions = sessions;
|
|
2690
|
-
this.eventStore = eventStore;
|
|
2691
3084
|
}
|
|
2692
3085
|
async createSession(input) {
|
|
2693
3086
|
return await this.sessions.createSession(input);
|
|
@@ -2723,7 +3116,7 @@ var ControlApi = class {
|
|
|
2723
3116
|
return await this.sessions.getCurrentUrl(sessionId);
|
|
2724
3117
|
}
|
|
2725
3118
|
listEvents(sessionId, limit = 100) {
|
|
2726
|
-
return { events: this.
|
|
3119
|
+
return { events: this.sessions.listEvents(sessionId, limit) };
|
|
2727
3120
|
}
|
|
2728
3121
|
searchMemory(input) {
|
|
2729
3122
|
return { results: this.sessions.searchMemory(input) };
|
|
@@ -2948,7 +3341,7 @@ function buildSelectorHints(insight) {
|
|
|
2948
3341
|
if (!evidence.selector || evidence.result !== "success") continue;
|
|
2949
3342
|
weightedSelectors.set(evidence.selector, (weightedSelectors.get(evidence.selector) ?? 0) + 3);
|
|
2950
3343
|
}
|
|
2951
|
-
return [...weightedSelectors.entries()].
|
|
3344
|
+
return [...weightedSelectors.entries()].toSorted((a, b) => b[1] - a[1]).slice(0, 5).map(([selector]) => selector);
|
|
2952
3345
|
}
|
|
2953
3346
|
function buildSelectorAliases(insight) {
|
|
2954
3347
|
return insight.selectorAliases ?? [];
|
|
@@ -3024,7 +3417,7 @@ var MemoryIndex = class {
|
|
|
3024
3417
|
}
|
|
3025
3418
|
return best ? [best] : [];
|
|
3026
3419
|
}
|
|
3027
|
-
return candidates.map((insight) => scoreInsight(insight, normalizedIntent, normalizedDomain, profileLookup)).filter((result) => result.score > 0).
|
|
3420
|
+
return candidates.map((insight) => scoreInsight(insight, normalizedIntent, normalizedDomain, profileLookup)).filter((result) => result.score > 0).toSorted((a, b) => b.score - a.score).slice(0, limit);
|
|
3028
3421
|
}
|
|
3029
3422
|
};
|
|
3030
3423
|
//#endregion
|
|
@@ -3401,10 +3794,19 @@ var TaskInsightStore = class {
|
|
|
3401
3794
|
};
|
|
3402
3795
|
//#endregion
|
|
3403
3796
|
//#region src/memory/memory-service.ts
|
|
3797
|
+
/** Memory service configuration */
|
|
3404
3798
|
const SEARCH_CACHE_TTL_MS = 2e3;
|
|
3799
|
+
const MAX_EVIDENCE_RECORDS = 50;
|
|
3800
|
+
const MAX_RECIPE_STEPS = 8;
|
|
3801
|
+
const MAX_SELECTOR_ALIASES = 10;
|
|
3802
|
+
/** Site profile limits */
|
|
3405
3803
|
const MAX_NAVIGATION_PATHS = 20;
|
|
3406
3804
|
const MAX_LAYOUT_FINGERPRINTS = 10;
|
|
3407
3805
|
const MAX_SELECTOR_PATTERNS = 15;
|
|
3806
|
+
function normalizePathSegment(value) {
|
|
3807
|
+
const [firstSegment = ""] = value.replace(/\/\*$/, "").split("/").filter(Boolean);
|
|
3808
|
+
return firstSegment.toLowerCase();
|
|
3809
|
+
}
|
|
3408
3810
|
var MemoryService = class {
|
|
3409
3811
|
store;
|
|
3410
3812
|
profileStore;
|
|
@@ -3462,7 +3864,7 @@ var MemoryService = class {
|
|
|
3462
3864
|
fresh: insights.filter((x) => x.freshness === "fresh").length,
|
|
3463
3865
|
suspect: insights.filter((x) => x.freshness === "suspect").length,
|
|
3464
3866
|
stale: insights.filter((x) => x.freshness === "stale").length,
|
|
3465
|
-
topDomains: [...byDomain.entries()].
|
|
3867
|
+
topDomains: [...byDomain.entries()].toSorted((a, b) => b[1] - a[1]).slice(0, 10).map(([domain, count]) => ({
|
|
3466
3868
|
domain,
|
|
3467
3869
|
count
|
|
3468
3870
|
}))
|
|
@@ -3514,7 +3916,7 @@ var MemoryService = class {
|
|
|
3514
3916
|
const refreshed = applySuccess({
|
|
3515
3917
|
...matched,
|
|
3516
3918
|
useCount: matched.useCount + 1,
|
|
3517
|
-
evidence: [...matched.evidence.slice(-
|
|
3919
|
+
evidence: [...matched.evidence.slice(-(MAX_EVIDENCE_RECORDS - 1)), evidence],
|
|
3518
3920
|
actionRecipe: this.mergeRecipe(matched.actionRecipe, input.step),
|
|
3519
3921
|
expectedOutcome: input.expectedOutcome
|
|
3520
3922
|
});
|
|
@@ -3548,7 +3950,7 @@ var MemoryService = class {
|
|
|
3548
3950
|
const failed = applyFailure({
|
|
3549
3951
|
...matched,
|
|
3550
3952
|
useCount: matched.useCount + 1,
|
|
3551
|
-
evidence: [...matched.evidence.slice(-
|
|
3953
|
+
evidence: [...matched.evidence.slice(-(MAX_EVIDENCE_RECORDS - 1)), evidence]
|
|
3552
3954
|
}, signal);
|
|
3553
3955
|
this.store.upsert(failed);
|
|
3554
3956
|
this.invalidateSearchCache();
|
|
@@ -3570,7 +3972,7 @@ var MemoryService = class {
|
|
|
3570
3972
|
if (fpIndex >= 0) profile.layoutFingerprints[fpIndex] = fingerprint;
|
|
3571
3973
|
else {
|
|
3572
3974
|
profile.layoutFingerprints.push(fingerprint);
|
|
3573
|
-
if (profile.layoutFingerprints.length > MAX_LAYOUT_FINGERPRINTS) profile.layoutFingerprints = profile.layoutFingerprints.
|
|
3975
|
+
if (profile.layoutFingerprints.length > MAX_LAYOUT_FINGERPRINTS) profile.layoutFingerprints = profile.layoutFingerprints.toSorted((a, b) => b.lastSeenAt.localeCompare(a.lastSeenAt)).slice(0, MAX_LAYOUT_FINGERPRINTS);
|
|
3574
3976
|
}
|
|
3575
3977
|
profile.lastVisitAt = now;
|
|
3576
3978
|
this.profileStore.upsert(profile);
|
|
@@ -3662,12 +4064,8 @@ var MemoryService = class {
|
|
|
3662
4064
|
return score;
|
|
3663
4065
|
}
|
|
3664
4066
|
sameFirstPathSegment(left, right) {
|
|
3665
|
-
const
|
|
3666
|
-
|
|
3667
|
-
return firstSegment.toLowerCase();
|
|
3668
|
-
};
|
|
3669
|
-
const leftSegment = normalize(left);
|
|
3670
|
-
const rightSegment = normalize(right);
|
|
4067
|
+
const leftSegment = normalizePathSegment(left);
|
|
4068
|
+
const rightSegment = normalizePathSegment(right);
|
|
3671
4069
|
return Boolean(leftSegment) && leftSegment === rightSegment;
|
|
3672
4070
|
}
|
|
3673
4071
|
createEvidence(input, result, reason) {
|
|
@@ -3696,11 +4094,11 @@ var MemoryService = class {
|
|
|
3696
4094
|
selector,
|
|
3697
4095
|
fallbackSelectors: []
|
|
3698
4096
|
});
|
|
3699
|
-
if (aliasMap.size >=
|
|
4097
|
+
if (aliasMap.size >= MAX_SELECTOR_ALIASES) break;
|
|
3700
4098
|
}
|
|
3701
4099
|
return {
|
|
3702
4100
|
...insight,
|
|
3703
|
-
selectorAliases: [...aliasMap.values()].slice(0,
|
|
4101
|
+
selectorAliases: [...aliasMap.values()].slice(0, MAX_SELECTOR_ALIASES)
|
|
3704
4102
|
};
|
|
3705
4103
|
}
|
|
3706
4104
|
deriveAliasName(selector) {
|
|
@@ -3719,7 +4117,7 @@ var MemoryService = class {
|
|
|
3719
4117
|
}
|
|
3720
4118
|
mergeRecipe(recipe, step) {
|
|
3721
4119
|
if (recipe.find((existing) => existing.summary === step.summary && existing.selector === step.selector)) return recipe;
|
|
3722
|
-
return [...recipe, step].slice(-
|
|
4120
|
+
return [...recipe, step].slice(-MAX_RECIPE_STEPS);
|
|
3723
4121
|
}
|
|
3724
4122
|
};
|
|
3725
4123
|
//#endregion
|
|
@@ -3751,7 +4149,7 @@ var AgenticBrowserCore = class {
|
|
|
3751
4149
|
constructor(context, browserController) {
|
|
3752
4150
|
this.context = context;
|
|
3753
4151
|
this.sessions = new SessionManager(context, browserController);
|
|
3754
|
-
this.api = new ControlApi(this.sessions
|
|
4152
|
+
this.api = new ControlApi(this.sessions);
|
|
3755
4153
|
}
|
|
3756
4154
|
async startSession(input = { browser: "chrome" }) {
|
|
3757
4155
|
return await this.api.createSession(input);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-browser",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "CLI and MCP server for AI agents to control Chrome via CDP",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -66,9 +66,9 @@
|
|
|
66
66
|
"typecheck": "tsc"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
69
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
70
70
|
"commander": "^14.0.3",
|
|
71
|
-
"ws": "^8.
|
|
71
|
+
"ws": "^8.20.0",
|
|
72
72
|
"zod": "^4.3.6"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
@@ -76,20 +76,20 @@
|
|
|
76
76
|
"@semantic-release/github": "^12.0.6",
|
|
77
77
|
"@semantic-release/npm": "^13.1.5",
|
|
78
78
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
79
|
-
"@types/node": "^25.
|
|
79
|
+
"@types/node": "^25.6.0",
|
|
80
80
|
"@types/ws": "^8.18.1",
|
|
81
|
-
"oxfmt": "^0.
|
|
82
|
-
"oxlint": "^1.
|
|
83
|
-
"react": "^19.2.
|
|
84
|
-
"react-dom": "^19.2.
|
|
81
|
+
"oxfmt": "^0.44.0",
|
|
82
|
+
"oxlint": "^1.59.0",
|
|
83
|
+
"react": "^19.2.5",
|
|
84
|
+
"react-dom": "^19.2.5",
|
|
85
85
|
"semantic-release": "^25.0.3",
|
|
86
|
-
"tsdown": "^0.21.
|
|
87
|
-
"typescript": "^
|
|
88
|
-
"vitest": "^4.
|
|
86
|
+
"tsdown": "^0.21.7",
|
|
87
|
+
"typescript": "^6.0.2",
|
|
88
|
+
"vitest": "^4.1.4",
|
|
89
89
|
"vocs": "1.4.1"
|
|
90
90
|
},
|
|
91
91
|
"engines": {
|
|
92
|
-
"node": ">=
|
|
92
|
+
"node": ">=22"
|
|
93
93
|
},
|
|
94
|
-
"packageManager": "pnpm@10.
|
|
94
|
+
"packageManager": "pnpm@10.33.0"
|
|
95
95
|
}
|