@quanta-intellect/vessel-browser 0.1.27 → 0.1.29
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/README.md +1 -1
- package/out/main/index.js +596 -265
- package/out/preload/index.js +1 -1
- package/out/renderer/assets/{index-v71lXiVB.js → index-BnUB1gZc.js} +339 -254
- package/out/renderer/assets/{index-DP2yMHwF.css → index-Ct7z7yP_.css} +21 -0
- package/out/renderer/index.html +2 -2
- package/package.json +1 -1
package/out/main/index.js
CHANGED
|
@@ -2750,6 +2750,38 @@ function layoutViews(state2) {
|
|
|
2750
2750
|
});
|
|
2751
2751
|
}
|
|
2752
2752
|
}
|
|
2753
|
+
function resizeSidebarViews(state2) {
|
|
2754
|
+
const { mainWindow, sidebarView, devtoolsPanelView, tabManager, uiState } = state2;
|
|
2755
|
+
const [width, height] = mainWindow.getContentSize();
|
|
2756
|
+
const chromeHeight = uiState.focusMode ? 0 : CHROME_HEIGHT;
|
|
2757
|
+
const sidebarWidth = uiState.sidebarOpen ? uiState.sidebarWidth : 0;
|
|
2758
|
+
const devtoolsHeight = uiState.devtoolsPanelOpen ? uiState.devtoolsPanelHeight : 0;
|
|
2759
|
+
const resizeHandleOverlap = 6;
|
|
2760
|
+
const contentWidth = width - sidebarWidth;
|
|
2761
|
+
sidebarView.setBounds({
|
|
2762
|
+
x: width - sidebarWidth - resizeHandleOverlap,
|
|
2763
|
+
y: 0,
|
|
2764
|
+
width: sidebarWidth + resizeHandleOverlap,
|
|
2765
|
+
height
|
|
2766
|
+
});
|
|
2767
|
+
if (uiState.devtoolsPanelOpen) {
|
|
2768
|
+
devtoolsPanelView.setBounds({
|
|
2769
|
+
x: 0,
|
|
2770
|
+
y: height - devtoolsHeight,
|
|
2771
|
+
width: contentWidth,
|
|
2772
|
+
height: devtoolsHeight
|
|
2773
|
+
});
|
|
2774
|
+
}
|
|
2775
|
+
const activeTab = tabManager.getActiveTab();
|
|
2776
|
+
if (activeTab) {
|
|
2777
|
+
activeTab.view.setBounds({
|
|
2778
|
+
x: 0,
|
|
2779
|
+
y: chromeHeight,
|
|
2780
|
+
width: contentWidth,
|
|
2781
|
+
height: height - chromeHeight - devtoolsHeight
|
|
2782
|
+
});
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2753
2785
|
const SEARCH_ENGINE_HOSTS = [
|
|
2754
2786
|
"google.",
|
|
2755
2787
|
"bing.com",
|
|
@@ -5391,8 +5423,8 @@ const PROVIDERS = {
|
|
|
5391
5423
|
models: [],
|
|
5392
5424
|
requiresApiKey: false,
|
|
5393
5425
|
defaultBaseUrl: "http://localhost:8080/v1",
|
|
5394
|
-
apiKeyPlaceholder: "",
|
|
5395
|
-
apiKeyHint: "
|
|
5426
|
+
apiKeyPlaceholder: "Bearer token or API key",
|
|
5427
|
+
apiKeyHint: "Optional — only if your endpoint requires authentication"
|
|
5396
5428
|
}
|
|
5397
5429
|
};
|
|
5398
5430
|
function toOpenAITools(tools) {
|
|
@@ -6978,27 +7010,38 @@ function detectPageType(page) {
|
|
|
6978
7010
|
const hasPasswordField = page.forms.some(
|
|
6979
7011
|
(f) => f.fields.some((el) => el.inputType === "password")
|
|
6980
7012
|
);
|
|
6981
|
-
const
|
|
7013
|
+
const searchInputs = page.interactiveElements.filter(
|
|
6982
7014
|
(el) => el.inputType === "search" || el.name === "q" || el.name === "query" || el.name === "search" || (el.placeholder || "").toLowerCase().includes("search")
|
|
6983
|
-
)
|
|
7015
|
+
);
|
|
7016
|
+
const hasSearchInput = searchInputs.length > 0 || page.forms.some(
|
|
6984
7017
|
(f) => f.fields.some(
|
|
6985
7018
|
(el) => el.inputType === "search" || el.name === "q" || el.name === "query"
|
|
6986
7019
|
)
|
|
6987
7020
|
);
|
|
7021
|
+
const hasVisibleSearchInput = searchInputs.some(
|
|
7022
|
+
(el) => el.visible === true && el.inViewport === true && el.obscured !== true && el.blockedByOverlay !== true
|
|
7023
|
+
);
|
|
6988
7024
|
const formCount = page.forms.length;
|
|
6989
7025
|
const hasCart = page.interactiveElements.some(
|
|
6990
7026
|
(el) => (el.text || "").toLowerCase().includes("cart") || (el.text || "").toLowerCase().includes("checkout")
|
|
6991
7027
|
) || url.includes("cart") || url.includes("checkout");
|
|
6992
|
-
const
|
|
7028
|
+
const contentLinks = page.interactiveElements.filter(
|
|
7029
|
+
(el) => el.type === "link" && el.context !== "nav" && el.context !== "header" && el.context !== "sidebar" && (el.href || "").startsWith("http")
|
|
7030
|
+
);
|
|
7031
|
+
const hasResults = contentLinks.length > 10;
|
|
6993
7032
|
const hasPagination = page.interactiveElements.some(
|
|
6994
7033
|
(el) => (el.text || "").toLowerCase() === "next" || el.text === "›" || el.text === "»" || (el.label || "").toLowerCase().includes("next page")
|
|
6995
7034
|
);
|
|
7035
|
+
const listingLike = isSearchOrListingPage(page) || hasPagination || /[?&](d|q|query|search)=/.test(url) || /\/(search|results)\b/.test(url) || /\/p\/pl\b/.test(url);
|
|
6996
7036
|
if (hasPasswordField) return "LOGIN";
|
|
6997
|
-
if (hasSearchInput && !
|
|
6998
|
-
|
|
7037
|
+
if (hasSearchInput && hasVisibleSearchInput && !listingLike) {
|
|
7038
|
+
return "SEARCH_READY";
|
|
7039
|
+
}
|
|
7040
|
+
if (hasResults && hasSearchInput && listingLike) return "SEARCH_RESULTS";
|
|
6999
7041
|
if (hasCart) return "SHOPPING";
|
|
7000
7042
|
if (formCount > 0 && !hasPasswordField) return "FORM";
|
|
7001
|
-
if (hasPagination) return "PAGINATED_LIST";
|
|
7043
|
+
if (hasPagination && listingLike) return "PAGINATED_LIST";
|
|
7044
|
+
if (hasSearchInput && !listingLike) return "SEARCH_READY";
|
|
7002
7045
|
if (page.content.length > 3e3 && page.interactiveElements.length < 10)
|
|
7003
7046
|
return "ARTICLE";
|
|
7004
7047
|
return "GENERAL";
|
|
@@ -7030,6 +7073,9 @@ function analyzePageIntent(page) {
|
|
|
7030
7073
|
hints.push(
|
|
7031
7074
|
"Suggested: vessel_search → auto-finds search box, types query, and submits"
|
|
7032
7075
|
);
|
|
7076
|
+
hints.push(
|
|
7077
|
+
"Treat the visible site search box as the primary navigation control before jumping to direct URLs."
|
|
7078
|
+
);
|
|
7033
7079
|
break;
|
|
7034
7080
|
case "SEARCH_RESULTS":
|
|
7035
7081
|
hints.push("Page type: SEARCH RESULTS");
|
|
@@ -7918,6 +7964,16 @@ const CONTEXT_HINTS = {
|
|
|
7918
7964
|
function scoreForContext(toolName, pageType) {
|
|
7919
7965
|
const def = defByName[toolName];
|
|
7920
7966
|
if (!def) return 500;
|
|
7967
|
+
if (pageType === "SEARCH_READY") {
|
|
7968
|
+
if (toolName === "search") return -20;
|
|
7969
|
+
if (toolName === "type_text") return 5;
|
|
7970
|
+
if (toolName === "press_key") return 6;
|
|
7971
|
+
}
|
|
7972
|
+
if (pageType === "SEARCH_RESULTS") {
|
|
7973
|
+
if (toolName === "search") return -10;
|
|
7974
|
+
if (toolName === "type_text") return 12;
|
|
7975
|
+
if (toolName === "press_key") return 13;
|
|
7976
|
+
}
|
|
7921
7977
|
const tier = def.tier ?? 1;
|
|
7922
7978
|
if (tier === 0) return 0;
|
|
7923
7979
|
const isRelevant = !def.relevance || def.relevance.includes(pageType);
|
|
@@ -8029,20 +8085,21 @@ function pruneToolsForContext(tools, pageType, query = "") {
|
|
|
8029
8085
|
const ctx = pageType ?? "GENERAL";
|
|
8030
8086
|
const hints = CONTEXT_HINTS[ctx] ?? {};
|
|
8031
8087
|
const intents = inferIntent(query);
|
|
8032
|
-
const scored = tools.filter((tool) =>
|
|
8088
|
+
const scored = tools.filter((tool) => shouldIncludeTool(tool.name, ctx, intents)).map((tool) => ({
|
|
8033
8089
|
tool,
|
|
8034
8090
|
score: scoreForContext(tool.name, ctx)
|
|
8035
8091
|
}));
|
|
8036
8092
|
scored.sort((a, b) => a.score - b.score);
|
|
8037
8093
|
return scored.map(({ tool, score }) => {
|
|
8094
|
+
let description = tool.description ?? "";
|
|
8038
8095
|
const hint = hints[tool.name];
|
|
8039
8096
|
if (hint && score <= 20) {
|
|
8040
|
-
|
|
8041
|
-
...tool,
|
|
8042
|
-
description: hint + tool.description
|
|
8043
|
-
};
|
|
8097
|
+
description = hint + description;
|
|
8044
8098
|
}
|
|
8045
|
-
|
|
8099
|
+
if (isToolGated(tool.name)) {
|
|
8100
|
+
description = `[Premium — requires Vessel Premium] ${description}`;
|
|
8101
|
+
}
|
|
8102
|
+
return description !== tool.description ? { ...tool, description } : tool;
|
|
8046
8103
|
});
|
|
8047
8104
|
}
|
|
8048
8105
|
function trimText(value) {
|
|
@@ -8456,13 +8513,17 @@ function updateBookmark(id, updates) {
|
|
|
8456
8513
|
emit();
|
|
8457
8514
|
return { ...bookmark };
|
|
8458
8515
|
}
|
|
8459
|
-
function removeFolder(id) {
|
|
8516
|
+
function removeFolder(id, deleteContents = false) {
|
|
8460
8517
|
load();
|
|
8461
8518
|
const exists = state.folders.some((f) => f.id === id);
|
|
8462
8519
|
if (!exists) return false;
|
|
8463
|
-
|
|
8464
|
-
(b) => b.folderId
|
|
8465
|
-
|
|
8520
|
+
if (deleteContents) {
|
|
8521
|
+
state.bookmarks = state.bookmarks.filter((b) => b.folderId !== id);
|
|
8522
|
+
} else {
|
|
8523
|
+
state.bookmarks = state.bookmarks.map(
|
|
8524
|
+
(b) => b.folderId === id ? { ...b, folderId: UNSORTED_ID } : b
|
|
8525
|
+
);
|
|
8526
|
+
}
|
|
8466
8527
|
state.folders = state.folders.filter((f) => f.id !== id);
|
|
8467
8528
|
save();
|
|
8468
8529
|
emit();
|
|
@@ -9148,6 +9209,18 @@ async function executePageScript(wc, script, options) {
|
|
|
9148
9209
|
}
|
|
9149
9210
|
}
|
|
9150
9211
|
}
|
|
9212
|
+
async function waitForJsReady(wc, timeout = 8e3) {
|
|
9213
|
+
const start = Date.now();
|
|
9214
|
+
while (Date.now() - start < timeout) {
|
|
9215
|
+
const ready = await executePageScript(wc, "1", {
|
|
9216
|
+
timeoutMs: 250,
|
|
9217
|
+
userGesture: true,
|
|
9218
|
+
label: "js-ready probe"
|
|
9219
|
+
});
|
|
9220
|
+
if (ready === 1) return;
|
|
9221
|
+
await sleep$1(250);
|
|
9222
|
+
}
|
|
9223
|
+
}
|
|
9151
9224
|
function waitForLoad$1(wc, timeout = 5e3) {
|
|
9152
9225
|
return new Promise((resolve) => {
|
|
9153
9226
|
let finished = false;
|
|
@@ -10436,7 +10509,89 @@ async function tryDismissConsentIframe(wc) {
|
|
|
10436
10509
|
}
|
|
10437
10510
|
return null;
|
|
10438
10511
|
}
|
|
10512
|
+
async function tryAcceptCookiesQuickly(wc) {
|
|
10513
|
+
const dismissed = await executePageScript(
|
|
10514
|
+
wc,
|
|
10515
|
+
`
|
|
10516
|
+
(function() {
|
|
10517
|
+
var selectors = [
|
|
10518
|
+
'#onetrust-accept-btn-handler',
|
|
10519
|
+
'#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll',
|
|
10520
|
+
'[data-cookiefirst-action="accept"]',
|
|
10521
|
+
'.cookie-consent-accept-all',
|
|
10522
|
+
'#accept-cookies',
|
|
10523
|
+
'.cc-accept',
|
|
10524
|
+
'.cc-btn.cc-allow',
|
|
10525
|
+
'[aria-label="Accept cookies"]',
|
|
10526
|
+
'[aria-label="Accept all cookies"]',
|
|
10527
|
+
'[data-testid="cookie-accept"]',
|
|
10528
|
+
'[data-testid="consent-accept"]',
|
|
10529
|
+
'[data-testid="accept-all"]',
|
|
10530
|
+
'button[class*="consent"][class*="accept"]',
|
|
10531
|
+
'button[class*="privacy"][class*="accept"]',
|
|
10532
|
+
'.fc-cta-consent',
|
|
10533
|
+
'#sp_choice_button_accept',
|
|
10534
|
+
'.message-component.message-button.no-children.focusable.sp_choice_type_11',
|
|
10535
|
+
'[class*="truste"] [class*="accept"]',
|
|
10536
|
+
'[id*="consent-accept"]',
|
|
10537
|
+
'[class*="cmp-accept"]',
|
|
10538
|
+
];
|
|
10539
|
+
var textPatterns = [
|
|
10540
|
+
'accept all',
|
|
10541
|
+
'accept cookies',
|
|
10542
|
+
'allow all',
|
|
10543
|
+
'allow cookies',
|
|
10544
|
+
'agree',
|
|
10545
|
+
'got it',
|
|
10546
|
+
'ok',
|
|
10547
|
+
'i agree',
|
|
10548
|
+
'i accept',
|
|
10549
|
+
'consent',
|
|
10550
|
+
'continue',
|
|
10551
|
+
'accept and continue',
|
|
10552
|
+
'accept & continue'
|
|
10553
|
+
];
|
|
10554
|
+
for (var i = 0; i < selectors.length; i++) {
|
|
10555
|
+
var el = document.querySelector(selectors[i]);
|
|
10556
|
+
if (el && el instanceof HTMLElement) {
|
|
10557
|
+
el.click();
|
|
10558
|
+
return "Dismissed cookie banner via: " + selectors[i];
|
|
10559
|
+
}
|
|
10560
|
+
}
|
|
10561
|
+
var buttons = document.querySelectorAll('button, a[role="button"], [type="submit"]');
|
|
10562
|
+
for (var j = 0; j < buttons.length; j++) {
|
|
10563
|
+
var btn = buttons[j];
|
|
10564
|
+
var text = (btn.textContent || '').trim().toLowerCase();
|
|
10565
|
+
for (var k = 0; k < textPatterns.length; k++) {
|
|
10566
|
+
if (text === textPatterns[k] || text.startsWith(textPatterns[k])) {
|
|
10567
|
+
btn.click();
|
|
10568
|
+
return "Dismissed cookie banner via text match: " + text;
|
|
10569
|
+
}
|
|
10570
|
+
}
|
|
10571
|
+
}
|
|
10572
|
+
return null;
|
|
10573
|
+
})()
|
|
10574
|
+
`,
|
|
10575
|
+
{
|
|
10576
|
+
label: "accept cookies",
|
|
10577
|
+
timeoutMs: 1200
|
|
10578
|
+
}
|
|
10579
|
+
);
|
|
10580
|
+
if (dismissed) return dismissed;
|
|
10581
|
+
return tryDismissConsentIframe(wc);
|
|
10582
|
+
}
|
|
10439
10583
|
async function clearOverlays(wc, strategy = "auto") {
|
|
10584
|
+
const quickCookieResult = await tryAcceptCookiesQuickly(wc);
|
|
10585
|
+
if (quickCookieResult === PAGE_SCRIPT_TIMEOUT) {
|
|
10586
|
+
return pageBusyError("clear_overlays");
|
|
10587
|
+
}
|
|
10588
|
+
if (quickCookieResult) {
|
|
10589
|
+
return [
|
|
10590
|
+
quickCookieResult,
|
|
10591
|
+
"Stopped after a lightweight consent pass to keep the page responsive. Re-run only if the banner is still blocking the page."
|
|
10592
|
+
].join("\n");
|
|
10593
|
+
}
|
|
10594
|
+
await waitForJsReady(wc, 1500);
|
|
10440
10595
|
const steps = [];
|
|
10441
10596
|
let cleared = 0;
|
|
10442
10597
|
const maxIterations = 8;
|
|
@@ -10500,6 +10655,12 @@ Submitted modal: ${submitResult}`;
|
|
|
10500
10655
|
actionMessage = `Fallback popup handling: ${await dismissPopup$1(wc)}`;
|
|
10501
10656
|
}
|
|
10502
10657
|
steps.push(actionMessage);
|
|
10658
|
+
if (overlay.kind === "cookie_consent") {
|
|
10659
|
+
steps.push(
|
|
10660
|
+
"Stopped after a lightweight consent pass to keep the page responsive. Re-run only if the banner is still blocking the page."
|
|
10661
|
+
);
|
|
10662
|
+
return steps.join("\n");
|
|
10663
|
+
}
|
|
10503
10664
|
await sleep$1(250);
|
|
10504
10665
|
const after = await extractContent(wc);
|
|
10505
10666
|
const afterState = describeOverlayState(after);
|
|
@@ -11350,6 +11511,315 @@ async function submitForm$1(wc, args) {
|
|
|
11350
11511
|
}
|
|
11351
11512
|
return "Submitted form";
|
|
11352
11513
|
}
|
|
11514
|
+
async function clickElementBySelector(wc, selector) {
|
|
11515
|
+
return clickResolvedSelector$1(wc, selector);
|
|
11516
|
+
}
|
|
11517
|
+
async function locateSearchTarget(wc, explicitSelector) {
|
|
11518
|
+
if (explicitSelector) {
|
|
11519
|
+
return { selector: explicitSelector, submitSelector: null };
|
|
11520
|
+
}
|
|
11521
|
+
return executePageScript(
|
|
11522
|
+
wc,
|
|
11523
|
+
`
|
|
11524
|
+
(function() {
|
|
11525
|
+
function text(value) {
|
|
11526
|
+
return value == null ? "" : String(value).trim();
|
|
11527
|
+
}
|
|
11528
|
+
|
|
11529
|
+
function normalize(value) {
|
|
11530
|
+
return text(value).toLowerCase();
|
|
11531
|
+
}
|
|
11532
|
+
|
|
11533
|
+
function isVisible(el) {
|
|
11534
|
+
if (!(el instanceof HTMLElement)) return true;
|
|
11535
|
+
const style = window.getComputedStyle(el);
|
|
11536
|
+
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
|
|
11537
|
+
return false;
|
|
11538
|
+
}
|
|
11539
|
+
if (el.hasAttribute("hidden") || el.getAttribute("aria-hidden") === "true") {
|
|
11540
|
+
return false;
|
|
11541
|
+
}
|
|
11542
|
+
const rect = el.getBoundingClientRect();
|
|
11543
|
+
return rect.width > 0 && rect.height > 0;
|
|
11544
|
+
}
|
|
11545
|
+
|
|
11546
|
+
function inViewport(el) {
|
|
11547
|
+
if (!(el instanceof HTMLElement)) return true;
|
|
11548
|
+
const rect = el.getBoundingClientRect();
|
|
11549
|
+
const vw = window.innerWidth || document.documentElement?.clientWidth || 0;
|
|
11550
|
+
const vh = window.innerHeight || document.documentElement?.clientHeight || 0;
|
|
11551
|
+
return rect.bottom > 0 && rect.right > 0 && rect.top < vh && rect.left < vw;
|
|
11552
|
+
}
|
|
11553
|
+
|
|
11554
|
+
function escapeSelectorValue(value) {
|
|
11555
|
+
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
|
|
11556
|
+
return CSS.escape(value);
|
|
11557
|
+
}
|
|
11558
|
+
return String(value).replace(/["\\\\]/g, "\\\\$&");
|
|
11559
|
+
}
|
|
11560
|
+
|
|
11561
|
+
function uniqueSelector(candidate) {
|
|
11562
|
+
if (!candidate) return null;
|
|
11563
|
+
try {
|
|
11564
|
+
return document.querySelectorAll(candidate).length === 1 ? candidate : null;
|
|
11565
|
+
} catch {
|
|
11566
|
+
return null;
|
|
11567
|
+
}
|
|
11568
|
+
}
|
|
11569
|
+
|
|
11570
|
+
function uniqueAttributeSelector(el, attribute) {
|
|
11571
|
+
const value = text(el.getAttribute && el.getAttribute(attribute));
|
|
11572
|
+
if (!value) return null;
|
|
11573
|
+
const candidate = el.tagName.toLowerCase() + "[" + attribute + "=\\"" + escapeSelectorValue(value) + "\\"]";
|
|
11574
|
+
return uniqueSelector(candidate);
|
|
11575
|
+
}
|
|
11576
|
+
|
|
11577
|
+
function selectorFor(el) {
|
|
11578
|
+
if (!el) return null;
|
|
11579
|
+
if (el.id) return "#" + escapeSelectorValue(el.id);
|
|
11580
|
+
for (const attribute of ["data-testid", "name", "form", "aria-label", "placeholder"]) {
|
|
11581
|
+
const candidate = uniqueAttributeSelector(el, attribute);
|
|
11582
|
+
if (candidate) return candidate;
|
|
11583
|
+
}
|
|
11584
|
+
const parts = [];
|
|
11585
|
+
let current = el;
|
|
11586
|
+
while (current) {
|
|
11587
|
+
if (current.id) {
|
|
11588
|
+
parts.unshift("#" + escapeSelectorValue(current.id));
|
|
11589
|
+
break;
|
|
11590
|
+
}
|
|
11591
|
+
const tag = current.tagName.toLowerCase();
|
|
11592
|
+
const parent = current.parentElement;
|
|
11593
|
+
if (!parent) {
|
|
11594
|
+
parts.unshift(tag);
|
|
11595
|
+
break;
|
|
11596
|
+
}
|
|
11597
|
+
const siblings = Array.from(parent.children).filter((child) => child.tagName === current.tagName);
|
|
11598
|
+
const index = siblings.indexOf(current) + 1;
|
|
11599
|
+
parts.unshift(siblings.length > 1 ? tag + ":nth-of-type(" + index + ")" : tag);
|
|
11600
|
+
current = parent;
|
|
11601
|
+
}
|
|
11602
|
+
const candidate = parts.join(" > ");
|
|
11603
|
+
return uniqueSelector(candidate) || candidate;
|
|
11604
|
+
}
|
|
11605
|
+
|
|
11606
|
+
function isDisabled(el) {
|
|
11607
|
+
return !!(el && el.hasAttribute && (el.hasAttribute("disabled") || el.getAttribute("aria-disabled") === "true"));
|
|
11608
|
+
}
|
|
11609
|
+
|
|
11610
|
+
function nearestSearchScope(input) {
|
|
11611
|
+
return input.closest('[role="search"], form, header, nav, [class*="search" i], [id*="search" i]');
|
|
11612
|
+
}
|
|
11613
|
+
|
|
11614
|
+
function collectCandidates() {
|
|
11615
|
+
const seen = new Set();
|
|
11616
|
+
const ordered = [];
|
|
11617
|
+
const specific = document.querySelectorAll(
|
|
11618
|
+
'input[type="search"], input[name="q"], input[name="query"], input[name="search"], input[role="searchbox"], input[aria-label*="search" i], input[placeholder*="search" i]'
|
|
11619
|
+
);
|
|
11620
|
+
specific.forEach((el) => {
|
|
11621
|
+
if (!seen.has(el)) {
|
|
11622
|
+
seen.add(el);
|
|
11623
|
+
ordered.push(el);
|
|
11624
|
+
}
|
|
11625
|
+
});
|
|
11626
|
+
document.querySelectorAll('input[type="text"], input:not([type])').forEach((el) => {
|
|
11627
|
+
if (seen.has(el)) return;
|
|
11628
|
+
const scope = nearestSearchScope(el);
|
|
11629
|
+
if (!scope) return;
|
|
11630
|
+
seen.add(el);
|
|
11631
|
+
ordered.push(el);
|
|
11632
|
+
});
|
|
11633
|
+
return ordered;
|
|
11634
|
+
}
|
|
11635
|
+
|
|
11636
|
+
function scoreInput(el) {
|
|
11637
|
+
if (!(el instanceof HTMLInputElement)) return -1;
|
|
11638
|
+
if (isDisabled(el) || !isVisible(el)) return -1;
|
|
11639
|
+
const type = normalize(el.getAttribute("type") || el.type);
|
|
11640
|
+
if (type && !["search", "text", ""].includes(type)) return -1;
|
|
11641
|
+
|
|
11642
|
+
let score = 0;
|
|
11643
|
+
if (inViewport(el)) score += 120;
|
|
11644
|
+
const rect = el.getBoundingClientRect();
|
|
11645
|
+
score += Math.max(0, 40 - Math.min(40, Math.floor(Math.max(0, rect.top) / 20)));
|
|
11646
|
+
|
|
11647
|
+
const name = normalize(el.name);
|
|
11648
|
+
const placeholder = normalize(el.getAttribute("placeholder"));
|
|
11649
|
+
const aria = normalize(el.getAttribute("aria-label"));
|
|
11650
|
+
if (type === "search") score += 80;
|
|
11651
|
+
if (name === "q" || name === "query" || name === "search") score += 70;
|
|
11652
|
+
if (placeholder.includes("search")) score += 55;
|
|
11653
|
+
if (aria.includes("search")) score += 55;
|
|
11654
|
+
|
|
11655
|
+
const scope = nearestSearchScope(el);
|
|
11656
|
+
if (scope) {
|
|
11657
|
+
score += 35;
|
|
11658
|
+
const scopeRole = normalize(scope.getAttribute && scope.getAttribute("role"));
|
|
11659
|
+
const scopeLabel = normalize(
|
|
11660
|
+
[
|
|
11661
|
+
scope.id,
|
|
11662
|
+
scope.className,
|
|
11663
|
+
scope.getAttribute && scope.getAttribute("aria-label"),
|
|
11664
|
+
scope.getAttribute && scope.getAttribute("action"),
|
|
11665
|
+
].filter(Boolean).join(" ")
|
|
11666
|
+
);
|
|
11667
|
+
if (scopeRole === "search") score += 35;
|
|
11668
|
+
if (scopeLabel.includes("search")) score += 30;
|
|
11669
|
+
const tag = normalize(scope.tagName);
|
|
11670
|
+
if (tag === "header" || tag === "nav") score += 20;
|
|
11671
|
+
}
|
|
11672
|
+
|
|
11673
|
+
return score;
|
|
11674
|
+
}
|
|
11675
|
+
|
|
11676
|
+
function pickSearchButton(input) {
|
|
11677
|
+
const scopes = [
|
|
11678
|
+
input.closest('[role="search"]'),
|
|
11679
|
+
input.closest('form'),
|
|
11680
|
+
nearestSearchScope(input),
|
|
11681
|
+
input.parentElement,
|
|
11682
|
+
].filter(Boolean);
|
|
11683
|
+
const seen = new Set();
|
|
11684
|
+
let best = null;
|
|
11685
|
+
let bestScore = -1;
|
|
11686
|
+
|
|
11687
|
+
for (const scope of scopes) {
|
|
11688
|
+
scope.querySelectorAll('button, input[type="submit"], input[type="button"], [role="button"]').forEach((candidate) => {
|
|
11689
|
+
if (seen.has(candidate)) return;
|
|
11690
|
+
seen.add(candidate);
|
|
11691
|
+
if (!(candidate instanceof HTMLElement)) return;
|
|
11692
|
+
if (candidate === input || isDisabled(candidate) || !isVisible(candidate)) return;
|
|
11693
|
+
|
|
11694
|
+
const label = normalize(
|
|
11695
|
+
candidate.getAttribute("aria-label") ||
|
|
11696
|
+
candidate.textContent ||
|
|
11697
|
+
candidate.getAttribute("title") ||
|
|
11698
|
+
candidate.getAttribute("value")
|
|
11699
|
+
);
|
|
11700
|
+
const rect = candidate.getBoundingClientRect();
|
|
11701
|
+
const inputRect = input.getBoundingClientRect();
|
|
11702
|
+
const closeToInput =
|
|
11703
|
+
Math.abs(rect.top - inputRect.top) < 80 &&
|
|
11704
|
+
Math.abs(rect.left - inputRect.right) < 260;
|
|
11705
|
+
|
|
11706
|
+
let score = 0;
|
|
11707
|
+
if (inViewport(candidate)) score += 40;
|
|
11708
|
+
if (closeToInput) score += 35;
|
|
11709
|
+
if (label.includes("search") || label.includes("go") || label.includes("submit")) score += 45;
|
|
11710
|
+
if (candidate.getAttribute("type") === "submit") score += 20;
|
|
11711
|
+
if (candidate.closest('[role="search"]') === input.closest('[role="search"]')) score += 20;
|
|
11712
|
+
if (candidate.closest('form') && candidate.closest('form') === input.closest('form')) score += 15;
|
|
11713
|
+
|
|
11714
|
+
if (score > bestScore) {
|
|
11715
|
+
best = candidate;
|
|
11716
|
+
bestScore = score;
|
|
11717
|
+
}
|
|
11718
|
+
});
|
|
11719
|
+
}
|
|
11720
|
+
|
|
11721
|
+
return bestScore >= 35 ? best : null;
|
|
11722
|
+
}
|
|
11723
|
+
|
|
11724
|
+
let bestInput = null;
|
|
11725
|
+
let bestScore = -1;
|
|
11726
|
+
for (const candidate of collectCandidates()) {
|
|
11727
|
+
const score = scoreInput(candidate);
|
|
11728
|
+
if (score > bestScore) {
|
|
11729
|
+
bestInput = candidate;
|
|
11730
|
+
bestScore = score;
|
|
11731
|
+
}
|
|
11732
|
+
}
|
|
11733
|
+
|
|
11734
|
+
if (!bestInput) return null;
|
|
11735
|
+
const selector = selectorFor(bestInput);
|
|
11736
|
+
if (!selector) return null;
|
|
11737
|
+
const submit = pickSearchButton(bestInput);
|
|
11738
|
+
return {
|
|
11739
|
+
selector: selector,
|
|
11740
|
+
submitSelector: submit ? selectorFor(submit) : null,
|
|
11741
|
+
};
|
|
11742
|
+
})()
|
|
11743
|
+
`,
|
|
11744
|
+
{
|
|
11745
|
+
timeoutMs: 2200,
|
|
11746
|
+
label: "find search input"
|
|
11747
|
+
}
|
|
11748
|
+
);
|
|
11749
|
+
}
|
|
11750
|
+
async function searchPage(wc, args) {
|
|
11751
|
+
const query = String(args.query || "");
|
|
11752
|
+
if (!query) return "Error: No search query provided.";
|
|
11753
|
+
const queryLower = query.toLowerCase().trim();
|
|
11754
|
+
const buttonLikePatterns = [
|
|
11755
|
+
"add to cart",
|
|
11756
|
+
"add to bag",
|
|
11757
|
+
"add to basket",
|
|
11758
|
+
"buy now",
|
|
11759
|
+
"buy it now",
|
|
11760
|
+
"purchase",
|
|
11761
|
+
"continue shopping",
|
|
11762
|
+
"keep shopping",
|
|
11763
|
+
"view cart",
|
|
11764
|
+
"view bag",
|
|
11765
|
+
"view basket",
|
|
11766
|
+
"go to cart",
|
|
11767
|
+
"go to checkout",
|
|
11768
|
+
"checkout",
|
|
11769
|
+
"check out",
|
|
11770
|
+
"proceed to checkout",
|
|
11771
|
+
"place order",
|
|
11772
|
+
"submit",
|
|
11773
|
+
"subscribe",
|
|
11774
|
+
"sign up",
|
|
11775
|
+
"sign in",
|
|
11776
|
+
"log in",
|
|
11777
|
+
"register",
|
|
11778
|
+
"continue"
|
|
11779
|
+
];
|
|
11780
|
+
if (buttonLikePatterns.some((p) => queryLower.includes(p))) {
|
|
11781
|
+
return `Error: "${query}" looks like a button label, not a search query. Use the click tool to interact with this element instead.`;
|
|
11782
|
+
}
|
|
11783
|
+
const searchInfo = await locateSearchTarget(
|
|
11784
|
+
wc,
|
|
11785
|
+
typeof args.selector === "string" ? args.selector : void 0
|
|
11786
|
+
);
|
|
11787
|
+
if (searchInfo === PAGE_SCRIPT_TIMEOUT) {
|
|
11788
|
+
return pageBusyError("search");
|
|
11789
|
+
}
|
|
11790
|
+
if (!searchInfo?.selector) {
|
|
11791
|
+
return 'Error: Could not find a visible search input. Try read_page(mode="visible_only") or provide a selector.';
|
|
11792
|
+
}
|
|
11793
|
+
const fillResult = await setElementValue$1(wc, searchInfo.selector, query);
|
|
11794
|
+
if (fillResult.startsWith("Error:")) {
|
|
11795
|
+
return fillResult;
|
|
11796
|
+
}
|
|
11797
|
+
await sleep$1(100);
|
|
11798
|
+
const beforeUrl = wc.getURL();
|
|
11799
|
+
const keyResult = await pressKey$1(wc, {
|
|
11800
|
+
key: "Enter",
|
|
11801
|
+
selector: searchInfo.selector
|
|
11802
|
+
});
|
|
11803
|
+
if (keyResult.startsWith("Error:")) {
|
|
11804
|
+
return keyResult;
|
|
11805
|
+
}
|
|
11806
|
+
await waitForPotentialNavigation$1(wc, beforeUrl, 3e3);
|
|
11807
|
+
let afterUrl = wc.getURL();
|
|
11808
|
+
if (afterUrl !== beforeUrl) {
|
|
11809
|
+
return `Searched "${query}" → ${afterUrl}`;
|
|
11810
|
+
}
|
|
11811
|
+
if (searchInfo.submitSelector) {
|
|
11812
|
+
const clickResult = await clickElementBySelector(wc, searchInfo.submitSelector);
|
|
11813
|
+
if (!clickResult.startsWith("Error:")) {
|
|
11814
|
+
await waitForPotentialNavigation$1(wc, beforeUrl, 3e3);
|
|
11815
|
+
afterUrl = wc.getURL();
|
|
11816
|
+
if (afterUrl !== beforeUrl) {
|
|
11817
|
+
return `Searched "${query}" (via search button) → ${afterUrl}`;
|
|
11818
|
+
}
|
|
11819
|
+
}
|
|
11820
|
+
}
|
|
11821
|
+
return `Searched "${query}" (same page — results may have loaded dynamically; inspect with read_page(mode="results_only") or read_page(mode="visible_only") before navigating directly elsewhere)`;
|
|
11822
|
+
}
|
|
11353
11823
|
async function pressKey$1(wc, args) {
|
|
11354
11824
|
const key = typeof args.key === "string" ? args.key.trim() : "";
|
|
11355
11825
|
if (!key) return "Error: No key provided";
|
|
@@ -12234,10 +12704,18 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
|
|
|
12234
12704
|
(el) => (el.text || "").toLowerCase() === "next" || el.text === "›" || el.text === "»"
|
|
12235
12705
|
);
|
|
12236
12706
|
const hasOverlays = page.overlays.some((o) => o.blocksInteraction);
|
|
12707
|
+
const hasCookieConsent = page.overlays.some(
|
|
12708
|
+
(overlay) => overlay.blocksInteraction && overlay.kind === "cookie_consent"
|
|
12709
|
+
);
|
|
12237
12710
|
if (hasOverlays) {
|
|
12238
12711
|
suggestions.push("BLOCKING OVERLAY detected — dismiss it first:");
|
|
12239
|
-
|
|
12240
|
-
|
|
12712
|
+
if (hasCookieConsent) {
|
|
12713
|
+
suggestions.push(" → accept_cookies for consent banners");
|
|
12714
|
+
suggestions.push(" → clear_overlays only if consent handling does not unblock the page");
|
|
12715
|
+
} else {
|
|
12716
|
+
suggestions.push(" → clear_overlays for stacked modals");
|
|
12717
|
+
suggestions.push(" → or dismiss_popup for a single popup");
|
|
12718
|
+
}
|
|
12241
12719
|
suggestions.push("");
|
|
12242
12720
|
}
|
|
12243
12721
|
if (hasPasswordField) {
|
|
@@ -12396,164 +12874,7 @@ ${steps.join("\n")}`;
|
|
|
12396
12874
|
}
|
|
12397
12875
|
case "search": {
|
|
12398
12876
|
if (!wc) return "Error: No active tab";
|
|
12399
|
-
|
|
12400
|
-
if (!query) return "Error: No search query provided.";
|
|
12401
|
-
const queryLower = query.toLowerCase().trim();
|
|
12402
|
-
const buttonLikePatterns = [
|
|
12403
|
-
"add to cart",
|
|
12404
|
-
"add to bag",
|
|
12405
|
-
"add to basket",
|
|
12406
|
-
"buy now",
|
|
12407
|
-
"buy it now",
|
|
12408
|
-
"purchase",
|
|
12409
|
-
"continue shopping",
|
|
12410
|
-
"keep shopping",
|
|
12411
|
-
"view cart",
|
|
12412
|
-
"view bag",
|
|
12413
|
-
"view basket",
|
|
12414
|
-
"go to cart",
|
|
12415
|
-
"go to checkout",
|
|
12416
|
-
"checkout",
|
|
12417
|
-
"check out",
|
|
12418
|
-
"proceed to checkout",
|
|
12419
|
-
"place order",
|
|
12420
|
-
"submit",
|
|
12421
|
-
"subscribe",
|
|
12422
|
-
"sign up",
|
|
12423
|
-
"sign in",
|
|
12424
|
-
"log in",
|
|
12425
|
-
"register",
|
|
12426
|
-
"continue"
|
|
12427
|
-
];
|
|
12428
|
-
if (buttonLikePatterns.some((p) => queryLower.includes(p))) {
|
|
12429
|
-
return `Error: "${query}" looks like a button label, not a search query. Use the click tool to interact with this element instead.`;
|
|
12430
|
-
}
|
|
12431
|
-
const searchInfo = args.selector ? { selector: args.selector, formAction: null, formMethod: null } : await executePageScript(
|
|
12432
|
-
wc,
|
|
12433
|
-
`
|
|
12434
|
-
(function() {
|
|
12435
|
-
var SELECTORS = 'input[type="search"], input[name="q"], input[name="query"], input[name="search"], input[role="searchbox"], input[aria-label*="search" i], input[placeholder*="search" i]';
|
|
12436
|
-
function find() {
|
|
12437
|
-
var el = document.querySelector(SELECTORS);
|
|
12438
|
-
if (!el) {
|
|
12439
|
-
var inputs = document.querySelectorAll('input[type="text"]');
|
|
12440
|
-
for (var i = 0; i < inputs.length; i++) {
|
|
12441
|
-
var form = inputs[i].closest('form');
|
|
12442
|
-
if (form && (form.getAttribute('role') === 'search' || (form.action && form.action.includes('search')))) {
|
|
12443
|
-
el = inputs[i];
|
|
12444
|
-
break;
|
|
12445
|
-
}
|
|
12446
|
-
}
|
|
12447
|
-
}
|
|
12448
|
-
if (!el) return null;
|
|
12449
|
-
var sel = el.id ? '#' + CSS.escape(el.id) : el.name ? 'input[name="' + el.name + '"]' : null;
|
|
12450
|
-
var form = el.closest('form');
|
|
12451
|
-
return {
|
|
12452
|
-
selector: sel,
|
|
12453
|
-
formAction: form ? form.action : null,
|
|
12454
|
-
formMethod: form ? (form.method || 'GET').toUpperCase() : null,
|
|
12455
|
-
inputName: el.name || 'q',
|
|
12456
|
-
};
|
|
12457
|
-
}
|
|
12458
|
-
return new Promise(function(resolve) {
|
|
12459
|
-
var result = find();
|
|
12460
|
-
if (result) { resolve(result); return; }
|
|
12461
|
-
var attempts = 0;
|
|
12462
|
-
var timer = setInterval(function() {
|
|
12463
|
-
result = find();
|
|
12464
|
-
if (result || ++attempts >= 20) {
|
|
12465
|
-
clearInterval(timer);
|
|
12466
|
-
resolve(result);
|
|
12467
|
-
}
|
|
12468
|
-
}, 250);
|
|
12469
|
-
});
|
|
12470
|
-
})()
|
|
12471
|
-
`,
|
|
12472
|
-
{
|
|
12473
|
-
timeoutMs: 1800,
|
|
12474
|
-
label: "find search input"
|
|
12475
|
-
}
|
|
12476
|
-
);
|
|
12477
|
-
if (searchInfo === PAGE_SCRIPT_TIMEOUT) {
|
|
12478
|
-
return pageBusyError("search");
|
|
12479
|
-
}
|
|
12480
|
-
if (!searchInfo?.selector)
|
|
12481
|
-
return "Error: Could not find search input. Try providing a selector.";
|
|
12482
|
-
const fillResult = await setElementValue$1(
|
|
12483
|
-
wc,
|
|
12484
|
-
searchInfo.selector,
|
|
12485
|
-
query
|
|
12486
|
-
);
|
|
12487
|
-
if (fillResult.startsWith("Error:")) {
|
|
12488
|
-
return fillResult;
|
|
12489
|
-
}
|
|
12490
|
-
await sleep$1(100);
|
|
12491
|
-
const focusResult = await executePageScript(
|
|
12492
|
-
wc,
|
|
12493
|
-
`
|
|
12494
|
-
(function() {
|
|
12495
|
-
var el = document.querySelector(${JSON.stringify(searchInfo.selector)});
|
|
12496
|
-
if (el) el.focus();
|
|
12497
|
-
})()
|
|
12498
|
-
`,
|
|
12499
|
-
{
|
|
12500
|
-
label: "focus search input"
|
|
12501
|
-
}
|
|
12502
|
-
);
|
|
12503
|
-
if (focusResult === PAGE_SCRIPT_TIMEOUT) {
|
|
12504
|
-
return pageBusyError("search");
|
|
12505
|
-
}
|
|
12506
|
-
await sleep$1(50);
|
|
12507
|
-
const beforeUrl = wc.getURL();
|
|
12508
|
-
wc.sendInputEvent({ type: "keyDown", keyCode: "Return" });
|
|
12509
|
-
await sleep$1(16);
|
|
12510
|
-
wc.sendInputEvent({ type: "keyUp", keyCode: "Return" });
|
|
12511
|
-
await waitForPotentialNavigation$1(wc, beforeUrl, 3e3);
|
|
12512
|
-
let afterUrl = wc.getURL();
|
|
12513
|
-
if (afterUrl !== beforeUrl) {
|
|
12514
|
-
return `Searched "${query}" → ${afterUrl}`;
|
|
12515
|
-
}
|
|
12516
|
-
const clickedSubmit = await executePageScript(
|
|
12517
|
-
wc,
|
|
12518
|
-
`
|
|
12519
|
-
(function() {
|
|
12520
|
-
var form = document.querySelector(${JSON.stringify(searchInfo.selector)})?.closest('form');
|
|
12521
|
-
if (!form) return false;
|
|
12522
|
-
var btn = form.querySelector('button[type="submit"], input[type="submit"], button:not([type])');
|
|
12523
|
-
if (btn) { btn.click(); return true; }
|
|
12524
|
-
// Try any button in the form
|
|
12525
|
-
var anyBtn = form.querySelector('button');
|
|
12526
|
-
if (anyBtn) { anyBtn.click(); return true; }
|
|
12527
|
-
return false;
|
|
12528
|
-
})()
|
|
12529
|
-
`,
|
|
12530
|
-
{
|
|
12531
|
-
label: "click search submit"
|
|
12532
|
-
}
|
|
12533
|
-
);
|
|
12534
|
-
if (clickedSubmit === PAGE_SCRIPT_TIMEOUT) {
|
|
12535
|
-
return pageBusyError("search");
|
|
12536
|
-
}
|
|
12537
|
-
if (clickedSubmit) {
|
|
12538
|
-
await waitForPotentialNavigation$1(wc, beforeUrl, 3e3);
|
|
12539
|
-
afterUrl = wc.getURL();
|
|
12540
|
-
if (afterUrl !== beforeUrl) {
|
|
12541
|
-
return `Searched "${query}" (via submit button) → ${afterUrl}`;
|
|
12542
|
-
}
|
|
12543
|
-
}
|
|
12544
|
-
if (searchInfo.formAction && searchInfo.formMethod === "GET") {
|
|
12545
|
-
try {
|
|
12546
|
-
const url = new URL(searchInfo.formAction);
|
|
12547
|
-
url.searchParams.set(searchInfo.inputName || "q", query);
|
|
12548
|
-
assertSafeURL(url.toString());
|
|
12549
|
-
wc.loadURL(url.toString());
|
|
12550
|
-
await waitForPotentialNavigation$1(wc, beforeUrl);
|
|
12551
|
-
afterUrl = wc.getURL();
|
|
12552
|
-
return `Searched "${query}" (via direct URL) → ${afterUrl}`;
|
|
12553
|
-
} catch {
|
|
12554
|
-
}
|
|
12555
|
-
}
|
|
12556
|
-
return `Searched "${query}" (same page — results may have loaded dynamically)`;
|
|
12877
|
+
return searchPage(wc, args);
|
|
12557
12878
|
}
|
|
12558
12879
|
case "paginate": {
|
|
12559
12880
|
if (!wc) return "Error: No active tab";
|
|
@@ -12599,64 +12920,11 @@ ${steps.join("\n")}`;
|
|
|
12599
12920
|
}
|
|
12600
12921
|
case "accept_cookies": {
|
|
12601
12922
|
if (!wc) return "Error: No active tab";
|
|
12602
|
-
const dismissed = await
|
|
12603
|
-
wc,
|
|
12604
|
-
`
|
|
12605
|
-
(function() {
|
|
12606
|
-
// Common cookie consent selectors — OneTrust, CookieBot, GDPR banners
|
|
12607
|
-
var selectors = [
|
|
12608
|
-
'#onetrust-accept-btn-handler',
|
|
12609
|
-
'#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll',
|
|
12610
|
-
'[data-cookiefirst-action="accept"]',
|
|
12611
|
-
'.cookie-consent-accept-all',
|
|
12612
|
-
'#accept-cookies',
|
|
12613
|
-
'.cc-accept',
|
|
12614
|
-
'.cc-btn.cc-allow',
|
|
12615
|
-
'[aria-label="Accept cookies"]',
|
|
12616
|
-
'[aria-label="Accept all cookies"]',
|
|
12617
|
-
'[data-testid="cookie-accept"]',
|
|
12618
|
-
// CNN / WarnerMedia / common consent SDKs
|
|
12619
|
-
'[data-testid="consent-accept"]',
|
|
12620
|
-
'[data-testid="accept-all"]',
|
|
12621
|
-
'button[class*="consent"][class*="accept"]',
|
|
12622
|
-
'button[class*="privacy"][class*="accept"]',
|
|
12623
|
-
'.fc-cta-consent',
|
|
12624
|
-
'#sp_choice_button_accept',
|
|
12625
|
-
'.message-component.message-button.no-children.focusable.sp_choice_type_11',
|
|
12626
|
-
'[class*="truste"] [class*="accept"]',
|
|
12627
|
-
'[id*="consent-accept"]',
|
|
12628
|
-
'[class*="cmp-accept"]',
|
|
12629
|
-
];
|
|
12630
|
-
// Also try text-matching on buttons
|
|
12631
|
-
var textPatterns = ['accept all', 'accept cookies', 'allow all', 'allow cookies', 'agree', 'got it', 'ok', 'i agree', 'i accept', 'consent', 'continue', 'accept and continue', 'accept & continue'];
|
|
12632
|
-
for (var i = 0; i < selectors.length; i++) {
|
|
12633
|
-
var el = document.querySelector(selectors[i]);
|
|
12634
|
-
if (el && el instanceof HTMLElement) { el.click(); return "Dismissed cookie banner via: " + selectors[i]; }
|
|
12635
|
-
}
|
|
12636
|
-
var buttons = document.querySelectorAll('button, a[role="button"], [type="submit"]');
|
|
12637
|
-
for (var j = 0; j < buttons.length; j++) {
|
|
12638
|
-
var btn = buttons[j];
|
|
12639
|
-
var text = (btn.textContent || '').trim().toLowerCase();
|
|
12640
|
-
for (var k = 0; k < textPatterns.length; k++) {
|
|
12641
|
-
if (text === textPatterns[k] || text.startsWith(textPatterns[k])) {
|
|
12642
|
-
btn.click();
|
|
12643
|
-
return "Dismissed cookie banner via text match: " + text;
|
|
12644
|
-
}
|
|
12645
|
-
}
|
|
12646
|
-
}
|
|
12647
|
-
return null;
|
|
12648
|
-
})()
|
|
12649
|
-
`,
|
|
12650
|
-
{
|
|
12651
|
-
label: "accept cookies"
|
|
12652
|
-
}
|
|
12653
|
-
);
|
|
12923
|
+
const dismissed = await tryAcceptCookiesQuickly(wc);
|
|
12654
12924
|
if (dismissed === PAGE_SCRIPT_TIMEOUT) {
|
|
12655
12925
|
return pageBusyError("accept_cookies");
|
|
12656
12926
|
}
|
|
12657
12927
|
if (dismissed) return dismissed;
|
|
12658
|
-
const iframeResult = await tryDismissConsentIframe(wc);
|
|
12659
|
-
if (iframeResult) return iframeResult;
|
|
12660
12928
|
return "No cookie consent banner detected. Try dismiss_popup for other overlays.";
|
|
12661
12929
|
}
|
|
12662
12930
|
case "extract_table": {
|
|
@@ -12808,6 +13076,7 @@ Instructions:
|
|
|
12808
13076
|
- Use save_session after completing a login flow you may need again later, and load_session to resume that authenticated state in future runs.
|
|
12809
13077
|
- Prefer select_option for dropdowns and submit_form for forms instead of guessing with clicks.
|
|
12810
13078
|
- After navigating to a new site, DO NOT call read_page immediately. Instead, act on what you already know: use the search tool to search the site, type_text to enter queries in search bars, or click on known navigation patterns. You know what major sites look like — use that knowledge. Only call read_page if you're genuinely stuck and need to discover unfamiliar page structure.
|
|
13079
|
+
- On retail and marketplace sites (like Newegg, Amazon, Walmart, Etsy, eBay), prefer the site's visible search box, filters, and result pages over direct product URLs. Only navigate directly to a product page if the user gave you that URL or the site's own search UI is clearly unavailable after a reasonable attempt.
|
|
12811
13080
|
- The page brief you start with is intentionally sparse. It is optimized for navigation speed, not completeness.
|
|
12812
13081
|
- When you only need detail on one product/result/card/form section, use inspect_element instead of reading the page.
|
|
12813
13082
|
- Escalate page reads progressively: read_page(mode="glance") for a fast viewport snapshot on heavy/slow pages, then read_page(mode="visible_only"), read_page(mode="results_only"), read_page(mode="forms_only"), read_page(mode="summary"), or read_page(mode="text_only") depending on what you need.
|
|
@@ -17007,22 +17276,29 @@ ${JSON.stringify(otherHighlights, null, 2)}`
|
|
|
17007
17276
|
"folder_remove",
|
|
17008
17277
|
{
|
|
17009
17278
|
title: "Remove Bookmark Folder",
|
|
17010
|
-
description: "Remove a folder.
|
|
17279
|
+
description: "Remove a folder. By default bookmarks in it are moved to Unsorted. Set delete_contents to true to delete them with the folder.",
|
|
17011
17280
|
inputSchema: {
|
|
17012
|
-
folder_id: zod.z.string().describe("ID of the folder to remove")
|
|
17281
|
+
folder_id: zod.z.string().describe("ID of the folder to remove"),
|
|
17282
|
+
delete_contents: zod.z.boolean().optional().default(false).describe(
|
|
17283
|
+
"If true, delete all bookmarks in the folder. If false (default), move them to Unsorted."
|
|
17284
|
+
)
|
|
17013
17285
|
}
|
|
17014
17286
|
},
|
|
17015
|
-
async ({ folder_id }) => {
|
|
17287
|
+
async ({ folder_id, delete_contents }) => {
|
|
17016
17288
|
return withAction(
|
|
17017
17289
|
runtime2,
|
|
17018
17290
|
tabManager,
|
|
17019
17291
|
"remove_bookmark_folder",
|
|
17020
|
-
{ folder_id },
|
|
17292
|
+
{ folder_id, delete_contents },
|
|
17021
17293
|
async () => {
|
|
17022
|
-
const removed = removeFolder(
|
|
17023
|
-
|
|
17024
|
-
|
|
17025
|
-
)
|
|
17294
|
+
const removed = removeFolder(
|
|
17295
|
+
folder_id,
|
|
17296
|
+
delete_contents
|
|
17297
|
+
);
|
|
17298
|
+
if (!removed) return `Folder ${folder_id} not found`;
|
|
17299
|
+
return composeFolderAwareResponse(
|
|
17300
|
+
delete_contents ? `Removed folder ${folder_id} and deleted its bookmarks.` : `Removed folder ${folder_id}. Bookmarks moved to Unsorted.`
|
|
17301
|
+
);
|
|
17026
17302
|
}
|
|
17027
17303
|
);
|
|
17028
17304
|
}
|
|
@@ -18863,7 +19139,53 @@ function assertNumber(value, name) {
|
|
|
18863
19139
|
const VALID_APPROVAL_MODES = /* @__PURE__ */ new Set(["auto", "confirm-dangerous", "manual"]);
|
|
18864
19140
|
function registerIpcHandlers(windowState, runtime2) {
|
|
18865
19141
|
const { tabManager, chromeView, sidebarView, devtoolsPanelView, mainWindow } = windowState;
|
|
19142
|
+
let sidebarResizeRecoveryTimer = null;
|
|
19143
|
+
let sidebarResizeActive = false;
|
|
19144
|
+
let runtimeUpdateTimer = null;
|
|
19145
|
+
let pendingRuntimeState = null;
|
|
18866
19146
|
const premiumApiOrigin = process.env.VESSEL_PREMIUM_API ? new URL(process.env.VESSEL_PREMIUM_API).origin : "https://vesselpremium.quantaintellect.com";
|
|
19147
|
+
const clearSidebarResizeRecoveryTimer = () => {
|
|
19148
|
+
if (sidebarResizeRecoveryTimer) {
|
|
19149
|
+
clearTimeout(sidebarResizeRecoveryTimer);
|
|
19150
|
+
sidebarResizeRecoveryTimer = null;
|
|
19151
|
+
}
|
|
19152
|
+
};
|
|
19153
|
+
const restoreSidebarLayoutAfterResize = () => {
|
|
19154
|
+
clearSidebarResizeRecoveryTimer();
|
|
19155
|
+
if (!sidebarResizeActive) return;
|
|
19156
|
+
sidebarResizeActive = false;
|
|
19157
|
+
layoutViews(windowState);
|
|
19158
|
+
};
|
|
19159
|
+
const scheduleSidebarResizeRecovery = () => {
|
|
19160
|
+
clearSidebarResizeRecoveryTimer();
|
|
19161
|
+
sidebarResizeRecoveryTimer = setTimeout(() => {
|
|
19162
|
+
restoreSidebarLayoutAfterResize();
|
|
19163
|
+
}, 1200);
|
|
19164
|
+
};
|
|
19165
|
+
const flushRuntimeUpdate = () => {
|
|
19166
|
+
runtimeUpdateTimer = null;
|
|
19167
|
+
if (!pendingRuntimeState) return;
|
|
19168
|
+
if (!chromeView.webContents.isDestroyed()) {
|
|
19169
|
+
chromeView.webContents.send(
|
|
19170
|
+
Channels.AGENT_RUNTIME_UPDATE,
|
|
19171
|
+
pendingRuntimeState
|
|
19172
|
+
);
|
|
19173
|
+
}
|
|
19174
|
+
if (!sidebarView.webContents.isDestroyed()) {
|
|
19175
|
+
sidebarView.webContents.send(
|
|
19176
|
+
Channels.AGENT_RUNTIME_UPDATE,
|
|
19177
|
+
pendingRuntimeState
|
|
19178
|
+
);
|
|
19179
|
+
}
|
|
19180
|
+
pendingRuntimeState = null;
|
|
19181
|
+
};
|
|
19182
|
+
const scheduleRuntimeUpdate = (state2) => {
|
|
19183
|
+
pendingRuntimeState = state2;
|
|
19184
|
+
if (runtimeUpdateTimer) return;
|
|
19185
|
+
runtimeUpdateTimer = setTimeout(() => {
|
|
19186
|
+
flushRuntimeUpdate();
|
|
19187
|
+
}, 32);
|
|
19188
|
+
};
|
|
18867
19189
|
const sendToRendererViews = (channel, ...args) => {
|
|
18868
19190
|
chromeView.webContents.send(channel, ...args);
|
|
18869
19191
|
sidebarView.webContents.send(channel, ...args);
|
|
@@ -18950,7 +19272,7 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
18950
19272
|
sendToRendererViews(Channels.HIGHLIGHT_COUNT_UPDATE, count);
|
|
18951
19273
|
};
|
|
18952
19274
|
runtime2.setUpdateListener((state2) => {
|
|
18953
|
-
|
|
19275
|
+
scheduleRuntimeUpdate(state2);
|
|
18954
19276
|
});
|
|
18955
19277
|
onRuntimeHealthChange((health) => {
|
|
18956
19278
|
sendToRendererViews(Channels.SETTINGS_HEALTH_UPDATE, health);
|
|
@@ -19004,29 +19326,31 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
19004
19326
|
return { accepted: false, reason: "busy" };
|
|
19005
19327
|
}
|
|
19006
19328
|
sendToRendererViews(Channels.AI_STREAM_START, query);
|
|
19007
|
-
|
|
19008
|
-
|
|
19009
|
-
|
|
19010
|
-
|
|
19011
|
-
|
|
19012
|
-
|
|
19013
|
-
|
|
19014
|
-
|
|
19015
|
-
|
|
19016
|
-
|
|
19017
|
-
|
|
19018
|
-
|
|
19019
|
-
|
|
19020
|
-
|
|
19021
|
-
|
|
19022
|
-
|
|
19023
|
-
|
|
19329
|
+
(async () => {
|
|
19330
|
+
try {
|
|
19331
|
+
activeChatProvider = createProvider(chatConfig);
|
|
19332
|
+
trackProviderConfigured(chatConfig.id);
|
|
19333
|
+
const activeTab = tabManager.getActiveTab();
|
|
19334
|
+
await handleAIQuery(
|
|
19335
|
+
query,
|
|
19336
|
+
activeChatProvider,
|
|
19337
|
+
activeTab?.view.webContents,
|
|
19338
|
+
(chunk) => sendToRendererViews(Channels.AI_STREAM_CHUNK, chunk),
|
|
19339
|
+
() => sendToRendererViews(Channels.AI_STREAM_END),
|
|
19340
|
+
tabManager,
|
|
19341
|
+
runtime2,
|
|
19342
|
+
history
|
|
19343
|
+
);
|
|
19344
|
+
} catch (err) {
|
|
19345
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
19346
|
+
sendToRendererViews(Channels.AI_STREAM_CHUNK, `
|
|
19024
19347
|
[Error: ${msg}]`);
|
|
19025
|
-
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
19029
|
-
|
|
19348
|
+
sendToRendererViews(Channels.AI_STREAM_END);
|
|
19349
|
+
} finally {
|
|
19350
|
+
activeChatProvider = null;
|
|
19351
|
+
endAIStream("manual");
|
|
19352
|
+
}
|
|
19353
|
+
})();
|
|
19030
19354
|
return { accepted: true };
|
|
19031
19355
|
});
|
|
19032
19356
|
electron.ipcMain.handle(Channels.AI_CANCEL, () => {
|
|
@@ -19076,16 +19400,23 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
19076
19400
|
};
|
|
19077
19401
|
});
|
|
19078
19402
|
electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_START, () => {
|
|
19403
|
+
sidebarResizeActive = true;
|
|
19404
|
+
clearSidebarResizeRecoveryTimer();
|
|
19079
19405
|
const [width, height] = windowState.mainWindow.getContentSize();
|
|
19080
19406
|
windowState.sidebarView.setBounds({ x: 0, y: 0, width, height });
|
|
19407
|
+
scheduleSidebarResizeRecovery();
|
|
19081
19408
|
});
|
|
19082
19409
|
electron.ipcMain.handle(Channels.SIDEBAR_RESIZE, (_, width) => {
|
|
19083
19410
|
assertNumber(width, "width");
|
|
19084
19411
|
const clamped = Math.max(240, Math.min(800, Math.round(width)));
|
|
19085
19412
|
windowState.uiState.sidebarWidth = clamped;
|
|
19413
|
+
resizeSidebarViews(windowState);
|
|
19414
|
+
scheduleSidebarResizeRecovery();
|
|
19086
19415
|
return clamped;
|
|
19087
19416
|
});
|
|
19088
19417
|
electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_COMMIT, () => {
|
|
19418
|
+
sidebarResizeActive = false;
|
|
19419
|
+
clearSidebarResizeRecoveryTimer();
|
|
19089
19420
|
setSetting("sidebarWidth", windowState.uiState.sidebarWidth);
|
|
19090
19421
|
layoutViews(windowState);
|
|
19091
19422
|
});
|
|
@@ -19177,9 +19508,9 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
19177
19508
|
trackBookmarkAction("remove");
|
|
19178
19509
|
return removeBookmark(id);
|
|
19179
19510
|
});
|
|
19180
|
-
electron.ipcMain.handle(Channels.FOLDER_REMOVE, (_, id) => {
|
|
19511
|
+
electron.ipcMain.handle(Channels.FOLDER_REMOVE, (_, id, deleteContents) => {
|
|
19181
19512
|
trackBookmarkAction("folder_remove");
|
|
19182
|
-
return removeFolder(id);
|
|
19513
|
+
return removeFolder(id, deleteContents ?? false);
|
|
19183
19514
|
});
|
|
19184
19515
|
electron.ipcMain.handle(
|
|
19185
19516
|
Channels.FOLDER_RENAME,
|