@quanta-intellect/vessel-browser 0.1.137 → 0.1.138
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 +4 -4
- package/out/main/index.js +707 -83
- package/out/preload/content-script.js +18 -0
- package/out/preload/index.js +4 -0
- package/out/renderer/assets/{index-k2scA5OB.js → index-Cjl9fej2.js} +1070 -678
- package/out/renderer/assets/{index-CdUTXTU4.css → index-DMcjRzqj.css} +259 -19
- package/out/renderer/index.html +2 -2
- package/package.json +2 -2
package/out/main/index.js
CHANGED
|
@@ -10,6 +10,8 @@ const OpenAI = require("openai");
|
|
|
10
10
|
const crypto$2 = require("node:crypto");
|
|
11
11
|
const http = require("http");
|
|
12
12
|
const path$1 = require("node:path");
|
|
13
|
+
const readability = require("@mozilla/readability");
|
|
14
|
+
const linkedom = require("linkedom");
|
|
13
15
|
const node_module = require("node:module");
|
|
14
16
|
const http$1 = require("node:http");
|
|
15
17
|
const os = require("node:os");
|
|
@@ -494,6 +496,10 @@ function assertSafeURL(url) {
|
|
|
494
496
|
}
|
|
495
497
|
function assertPermittedNavigationURL(url) {
|
|
496
498
|
assertSafeURL(url);
|
|
499
|
+
const airGapError = getAirGapBlockReason(url);
|
|
500
|
+
if (airGapError) {
|
|
501
|
+
throw new Error(airGapError);
|
|
502
|
+
}
|
|
497
503
|
const policyError = checkDomainPolicy(url);
|
|
498
504
|
if (policyError) {
|
|
499
505
|
throw new Error(policyError);
|
|
@@ -589,11 +595,11 @@ class Tab {
|
|
|
589
595
|
return null;
|
|
590
596
|
}
|
|
591
597
|
try {
|
|
592
|
-
|
|
598
|
+
assertPermittedNavigationURL(url);
|
|
593
599
|
} catch (error) {
|
|
594
600
|
return error instanceof Error ? error.message : "Blocked unsafe navigation";
|
|
595
601
|
}
|
|
596
|
-
return
|
|
602
|
+
return null;
|
|
597
603
|
}
|
|
598
604
|
guardedLoadURL(url, options) {
|
|
599
605
|
const blockReason = this.getNavigationBlockReason(url);
|
|
@@ -3705,6 +3711,8 @@ const AutofillChannels = {
|
|
|
3705
3711
|
const AutomationChannels = {
|
|
3706
3712
|
AUTOMATION_GET_INSTALLED: "automation:get-installed",
|
|
3707
3713
|
AUTOMATION_INSTALL_FROM_FILE: "automation:install-from-file",
|
|
3714
|
+
AUTOMATION_CREATE_FROM_TEXT: "automation:create-from-text",
|
|
3715
|
+
AUTOMATION_UPDATE_FROM_TEXT: "automation:update-from-text",
|
|
3708
3716
|
AUTOMATION_UNINSTALL: "automation:uninstall",
|
|
3709
3717
|
AUTOMATION_ACTIVITY_START: "automation:activity-start",
|
|
3710
3718
|
AUTOMATION_ACTIVITY_CHUNK: "automation:activity-chunk",
|
|
@@ -4251,10 +4259,28 @@ function buildPageSnapshotKey(rawUrl) {
|
|
|
4251
4259
|
return normalizePageUrl(rawUrl);
|
|
4252
4260
|
}
|
|
4253
4261
|
}
|
|
4262
|
+
function isDocumentViewerUrl(rawUrl) {
|
|
4263
|
+
try {
|
|
4264
|
+
const url = new URL(rawUrl);
|
|
4265
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
4266
|
+
const pathname = decodeURIComponent(url.pathname).toLowerCase();
|
|
4267
|
+
if (/\.(pdf|epub|mobi|cbz|cbr)(?:$|[?#])/.test(pathname)) {
|
|
4268
|
+
return true;
|
|
4269
|
+
}
|
|
4270
|
+
const host = url.hostname.toLowerCase().replace(/^www\./, "");
|
|
4271
|
+
if (host === "archive.org") {
|
|
4272
|
+
return /^\/(details|stream|download)\//.test(pathname);
|
|
4273
|
+
}
|
|
4274
|
+
return false;
|
|
4275
|
+
} catch {
|
|
4276
|
+
return false;
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4254
4279
|
function isTrackablePageUrl(rawUrl) {
|
|
4255
4280
|
try {
|
|
4256
4281
|
const url = new URL(rawUrl);
|
|
4257
|
-
|
|
4282
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
4283
|
+
return !isDocumentViewerUrl(rawUrl);
|
|
4258
4284
|
} catch {
|
|
4259
4285
|
return false;
|
|
4260
4286
|
}
|
|
@@ -4951,6 +4977,11 @@ const PREMIUM_TOOLS = /* @__PURE__ */ new Set([
|
|
|
4951
4977
|
"human_vault_list",
|
|
4952
4978
|
"human_vault_fill",
|
|
4953
4979
|
"human_vault_remove",
|
|
4980
|
+
"memory_note_create",
|
|
4981
|
+
"memory_note_append",
|
|
4982
|
+
"memory_note_list",
|
|
4983
|
+
"memory_note_search",
|
|
4984
|
+
"memory_page_capture",
|
|
4954
4985
|
"research_confirm_brief",
|
|
4955
4986
|
"research_approve_objectives",
|
|
4956
4987
|
"research_export_report"
|
|
@@ -6663,7 +6694,7 @@ async function extractContentInner(webContents) {
|
|
|
6663
6694
|
webContents
|
|
6664
6695
|
);
|
|
6665
6696
|
}
|
|
6666
|
-
async function extractContent
|
|
6697
|
+
async function extractContent(webContents) {
|
|
6667
6698
|
const cacheKey = `${webContents.id}:${webContents.getURL() || ""}`;
|
|
6668
6699
|
const cached = extractionCache.get(cacheKey);
|
|
6669
6700
|
if (cached) {
|
|
@@ -6881,7 +6912,7 @@ async function capturePageSnapshot(url, wc, sendToRendererViews) {
|
|
|
6881
6912
|
if (!shouldTrackSnapshotUrl(url)) return;
|
|
6882
6913
|
const key2 = normalizeUrl(url);
|
|
6883
6914
|
const oldSnap = getSnapshot(key2);
|
|
6884
|
-
const content = await extractContent
|
|
6915
|
+
const content = await extractContent(wc);
|
|
6885
6916
|
const textContent = content.content || "";
|
|
6886
6917
|
const title = content.title || "";
|
|
6887
6918
|
const headings = content.headings || [];
|
|
@@ -12113,7 +12144,7 @@ async function resolveSelector(wc, index, selector) {
|
|
|
12113
12144
|
if (typeof fallbackSelector === "string" && fallbackSelector) {
|
|
12114
12145
|
return fallbackSelector;
|
|
12115
12146
|
}
|
|
12116
|
-
const page = await extractContent
|
|
12147
|
+
const page = await extractContent(wc);
|
|
12117
12148
|
const extractedSelector = findSelectorByIndex(page, index);
|
|
12118
12149
|
if (extractedSelector) return extractedSelector;
|
|
12119
12150
|
return null;
|
|
@@ -12186,6 +12217,15 @@ async function validateLinkDestination(url, timeoutMs = 3500) {
|
|
|
12186
12217
|
detail: "Non-HTTP URL"
|
|
12187
12218
|
};
|
|
12188
12219
|
}
|
|
12220
|
+
try {
|
|
12221
|
+
assertPermittedNavigationURL(url);
|
|
12222
|
+
} catch (error) {
|
|
12223
|
+
return {
|
|
12224
|
+
status: "unknown",
|
|
12225
|
+
checkedUrl: url,
|
|
12226
|
+
detail: error instanceof Error ? error.message : "Navigation policy blocked URL"
|
|
12227
|
+
};
|
|
12228
|
+
}
|
|
12189
12229
|
try {
|
|
12190
12230
|
const headResponse = await requestUrl(url, "HEAD", timeoutMs);
|
|
12191
12231
|
if (!HEAD_FALLBACK_STATUS_CODES.has(headResponse.status)) {
|
|
@@ -14849,6 +14889,22 @@ function getGlanceExtractScript() {
|
|
|
14849
14889
|
};
|
|
14850
14890
|
})()`;
|
|
14851
14891
|
}
|
|
14892
|
+
function cleanArticleText(value) {
|
|
14893
|
+
return value.replace(/\u00a0/g, " ").replace(/[ \t]+/g, " ").replace(/\n[ \t]+/g, "\n").replace(/(\n\s*){3,}/g, "\n\n").trim();
|
|
14894
|
+
}
|
|
14895
|
+
function articleTextResultToOutput(result, mode, source, elapsedMs) {
|
|
14896
|
+
const sections = [
|
|
14897
|
+
`# ${result.title || "(untitled)"}`,
|
|
14898
|
+
`URL: ${result.url}`,
|
|
14899
|
+
"",
|
|
14900
|
+
`[read_page mode=${mode} — ${source}, ${elapsedMs}ms]`
|
|
14901
|
+
];
|
|
14902
|
+
if (result.headings.length > 0) {
|
|
14903
|
+
sections.push("", "## Headings", ...result.headings);
|
|
14904
|
+
}
|
|
14905
|
+
sections.push("", "## Article Text", "", result.text);
|
|
14906
|
+
return sections.join("\n");
|
|
14907
|
+
}
|
|
14852
14908
|
async function glanceExtract(wc) {
|
|
14853
14909
|
const startMs = Date.now();
|
|
14854
14910
|
const result = await executePageScript(wc, getGlanceExtractScript(), { timeoutMs: 2500, label: "glance-extract" });
|
|
@@ -14896,6 +14952,168 @@ async function glanceExtract(wc) {
|
|
|
14896
14952
|
}
|
|
14897
14953
|
return sections.join("\n");
|
|
14898
14954
|
}
|
|
14955
|
+
async function fastArticleTextExtract(wc, mode) {
|
|
14956
|
+
const startMs = Date.now();
|
|
14957
|
+
const result = await executePageScript(
|
|
14958
|
+
wc,
|
|
14959
|
+
`(function() {
|
|
14960
|
+
function clean(value) {
|
|
14961
|
+
return String(value || '').replace(/[ \\t]+/g, ' ').replace(/(\\n\\s*){3,}/g, '\\n\\n').trim();
|
|
14962
|
+
}
|
|
14963
|
+
|
|
14964
|
+
var rootSelectors = [
|
|
14965
|
+
'#mw-content-text .mw-parser-output',
|
|
14966
|
+
'#mw-content-text',
|
|
14967
|
+
'main article',
|
|
14968
|
+
'article',
|
|
14969
|
+
'main',
|
|
14970
|
+
'[role="main"]',
|
|
14971
|
+
'#content'
|
|
14972
|
+
];
|
|
14973
|
+
var root = null;
|
|
14974
|
+
for (var i = 0; i < rootSelectors.length; i++) {
|
|
14975
|
+
var candidate = document.querySelector(rootSelectors[i]);
|
|
14976
|
+
if (candidate && clean(candidate.textContent).length > 300) {
|
|
14977
|
+
root = candidate;
|
|
14978
|
+
break;
|
|
14979
|
+
}
|
|
14980
|
+
}
|
|
14981
|
+
if (!root) return null;
|
|
14982
|
+
|
|
14983
|
+
var unwantedSelector = [
|
|
14984
|
+
'script',
|
|
14985
|
+
'style',
|
|
14986
|
+
'noscript',
|
|
14987
|
+
'nav',
|
|
14988
|
+
'header',
|
|
14989
|
+
'footer',
|
|
14990
|
+
'aside',
|
|
14991
|
+
'.mw-editsection',
|
|
14992
|
+
'.reference',
|
|
14993
|
+
'.reflist',
|
|
14994
|
+
'.navbox',
|
|
14995
|
+
'.infobox',
|
|
14996
|
+
'.metadata',
|
|
14997
|
+
'.ambox',
|
|
14998
|
+
'.toc',
|
|
14999
|
+
'#toc'
|
|
15000
|
+
].join(',');
|
|
15001
|
+
|
|
15002
|
+
var headings = [];
|
|
15003
|
+
var parts = [];
|
|
15004
|
+
var nodes = root.querySelectorAll('h1, h2, h3, p, li');
|
|
15005
|
+
for (var j = 0; j < nodes.length && parts.length < 180; j++) {
|
|
15006
|
+
var node = nodes[j];
|
|
15007
|
+
if (node.closest && node.closest(unwantedSelector)) continue;
|
|
15008
|
+
var tag = String(node.tagName || '').toLowerCase();
|
|
15009
|
+
var text = clean(node.textContent);
|
|
15010
|
+
if (!text) continue;
|
|
15011
|
+
if (/^h[1-3]$/.test(tag)) {
|
|
15012
|
+
if (text.length < 180) headings.push(tag + ': ' + text);
|
|
15013
|
+
parts.push('\\n## ' + text);
|
|
15014
|
+
continue;
|
|
15015
|
+
}
|
|
15016
|
+
if (text.length < 40) continue;
|
|
15017
|
+
parts.push(text);
|
|
15018
|
+
}
|
|
15019
|
+
|
|
15020
|
+
var articleText = clean(parts.join('\\n\\n'));
|
|
15021
|
+
if (articleText.length < 300) {
|
|
15022
|
+
articleText = clean(root.textContent).slice(0, 12000);
|
|
15023
|
+
}
|
|
15024
|
+
if (articleText.length < 300) return null;
|
|
15025
|
+
|
|
15026
|
+
return {
|
|
15027
|
+
title: document.title || '',
|
|
15028
|
+
url: location.href,
|
|
15029
|
+
headings: headings.slice(0, 18),
|
|
15030
|
+
text: articleText.slice(0, ${mode === "summary" ? 9e3 : 14e3}),
|
|
15031
|
+
};
|
|
15032
|
+
})()`,
|
|
15033
|
+
{
|
|
15034
|
+
timeoutMs: 1800,
|
|
15035
|
+
label: "fast article text"
|
|
15036
|
+
}
|
|
15037
|
+
);
|
|
15038
|
+
if (!result || result === PAGE_SCRIPT_TIMEOUT || !result.text.trim()) {
|
|
15039
|
+
return null;
|
|
15040
|
+
}
|
|
15041
|
+
return articleTextResultToOutput(
|
|
15042
|
+
{
|
|
15043
|
+
title: result.title || wc.getTitle() || "(untitled)",
|
|
15044
|
+
url: result.url || wc.getURL(),
|
|
15045
|
+
headings: result.headings,
|
|
15046
|
+
text: result.text
|
|
15047
|
+
},
|
|
15048
|
+
mode,
|
|
15049
|
+
"fast article text",
|
|
15050
|
+
Date.now() - startMs
|
|
15051
|
+
);
|
|
15052
|
+
}
|
|
15053
|
+
async function fetchArticleTextExtract(wc, mode) {
|
|
15054
|
+
const startMs = Date.now();
|
|
15055
|
+
const url = wc.getURL();
|
|
15056
|
+
try {
|
|
15057
|
+
assertSafeURL(url);
|
|
15058
|
+
} catch {
|
|
15059
|
+
return null;
|
|
15060
|
+
}
|
|
15061
|
+
const controller = new AbortController();
|
|
15062
|
+
const timeout = setTimeout(() => controller.abort(), 4500);
|
|
15063
|
+
try {
|
|
15064
|
+
const response = await fetch(url, {
|
|
15065
|
+
signal: controller.signal,
|
|
15066
|
+
headers: {
|
|
15067
|
+
Accept: "text/html,application/xhtml+xml",
|
|
15068
|
+
"User-Agent": "VesselBrowser/0.1 read-page-fallback"
|
|
15069
|
+
}
|
|
15070
|
+
});
|
|
15071
|
+
if (!response.ok) return null;
|
|
15072
|
+
const contentType = response.headers.get("content-type") || "";
|
|
15073
|
+
if (contentType && !/text\/html|application\/xhtml\+xml/i.test(contentType)) {
|
|
15074
|
+
return null;
|
|
15075
|
+
}
|
|
15076
|
+
const contentLength = Number(response.headers.get("content-length") || "0");
|
|
15077
|
+
if (contentLength > 5e6) return null;
|
|
15078
|
+
const html = await response.text();
|
|
15079
|
+
if (html.trim().length < 300) return null;
|
|
15080
|
+
const { document } = linkedom.parseHTML(html);
|
|
15081
|
+
const readable = new readability.Readability(document, {
|
|
15082
|
+
charThreshold: 300
|
|
15083
|
+
}).parse();
|
|
15084
|
+
const title = cleanArticleText(readable?.title || document.title || wc.getTitle()) || wc.getTitle() || "(untitled)";
|
|
15085
|
+
const headings = Array.from(document.querySelectorAll("h1, h2, h3")).map((node) => {
|
|
15086
|
+
const tag = String(node.tagName || "").toLowerCase();
|
|
15087
|
+
const text2 = cleanArticleText(node.textContent || "");
|
|
15088
|
+
return text2.length > 0 && text2.length < 180 ? `${tag}: ${text2}` : "";
|
|
15089
|
+
}).filter(Boolean).slice(0, 18);
|
|
15090
|
+
const fallbackRoot = document.querySelector("article") || document.querySelector("main") || document.querySelector('[role="main"]') || document.body;
|
|
15091
|
+
const text = cleanArticleText(
|
|
15092
|
+
readable?.textContent || fallbackRoot?.textContent || ""
|
|
15093
|
+
);
|
|
15094
|
+
if (text.length < 300) return null;
|
|
15095
|
+
return articleTextResultToOutput(
|
|
15096
|
+
{
|
|
15097
|
+
title,
|
|
15098
|
+
url,
|
|
15099
|
+
headings,
|
|
15100
|
+
text: text.slice(0, mode === "summary" ? 9e3 : 14e3)
|
|
15101
|
+
},
|
|
15102
|
+
mode,
|
|
15103
|
+
"network article fallback",
|
|
15104
|
+
Date.now() - startMs
|
|
15105
|
+
);
|
|
15106
|
+
} catch (err) {
|
|
15107
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
15108
|
+
logger$k.warn("Network article fallback timed out:", url);
|
|
15109
|
+
} else {
|
|
15110
|
+
logger$k.warn("Network article fallback failed:", err);
|
|
15111
|
+
}
|
|
15112
|
+
return null;
|
|
15113
|
+
} finally {
|
|
15114
|
+
clearTimeout(timeout);
|
|
15115
|
+
}
|
|
15116
|
+
}
|
|
14899
15117
|
function normalizeReadPageMode(mode, pageContent) {
|
|
14900
15118
|
if (typeof mode === "string") {
|
|
14901
15119
|
const normalized = mode.trim().toLowerCase();
|
|
@@ -14967,7 +15185,7 @@ async function getPostSearchSummary(wc) {
|
|
|
14967
15185
|
await waitForLoad(wc, 2e3);
|
|
14968
15186
|
try {
|
|
14969
15187
|
const content = await Promise.race([
|
|
14970
|
-
extractContent
|
|
15188
|
+
extractContent(wc),
|
|
14971
15189
|
new Promise((resolve) => setTimeout(() => resolve(null), 2500))
|
|
14972
15190
|
]);
|
|
14973
15191
|
if (content && content.content.length > 0) {
|
|
@@ -14989,7 +15207,7 @@ Search results snapshot unavailable. Use read_page(mode="results_only") if neede
|
|
|
14989
15207
|
async function getPostClickNavSummary(wc, toolProfile) {
|
|
14990
15208
|
try {
|
|
14991
15209
|
const content = await Promise.race([
|
|
14992
|
-
extractContent
|
|
15210
|
+
extractContent(wc),
|
|
14993
15211
|
new Promise((resolve) => setTimeout(() => resolve(null), 3e3))
|
|
14994
15212
|
]);
|
|
14995
15213
|
if (content && content.content.length > 0) {
|
|
@@ -16715,8 +16933,11 @@ async function tryAcceptCookiesQuickly(wc) {
|
|
|
16715
16933
|
const dismissed = await executePageScript(
|
|
16716
16934
|
wc,
|
|
16717
16935
|
`
|
|
16718
|
-
(function() {
|
|
16719
|
-
var
|
|
16936
|
+
(async function() {
|
|
16937
|
+
var delay = function(ms) {
|
|
16938
|
+
return new Promise(function(resolve) { setTimeout(resolve, ms); });
|
|
16939
|
+
};
|
|
16940
|
+
var selectorTargets = [
|
|
16720
16941
|
'#onetrust-accept-btn-handler',
|
|
16721
16942
|
'#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll',
|
|
16722
16943
|
'[data-cookiefirst-action="accept"]',
|
|
@@ -16736,60 +16957,237 @@ async function tryAcceptCookiesQuickly(wc) {
|
|
|
16736
16957
|
'.message-component.message-button.no-children.focusable.sp_choice_type_11',
|
|
16737
16958
|
'[class*="truste"] [class*="accept"]',
|
|
16738
16959
|
'[id*="consent-accept"]',
|
|
16739
|
-
'[class*="cmp-accept"]'
|
|
16960
|
+
'[class*="cmp-accept"]'
|
|
16740
16961
|
];
|
|
16741
|
-
var
|
|
16742
|
-
'
|
|
16743
|
-
'
|
|
16744
|
-
'
|
|
16745
|
-
'
|
|
16746
|
-
'
|
|
16747
|
-
'
|
|
16748
|
-
'
|
|
16749
|
-
'i
|
|
16750
|
-
'i
|
|
16751
|
-
'
|
|
16752
|
-
'
|
|
16753
|
-
'
|
|
16754
|
-
'
|
|
16962
|
+
var surfaceSelectors = [
|
|
16963
|
+
'#onetrust-consent-sdk',
|
|
16964
|
+
'#CybotCookiebotDialog',
|
|
16965
|
+
'[id*="cookie" i]',
|
|
16966
|
+
'[id*="consent" i]',
|
|
16967
|
+
'[id*="cmp" i]',
|
|
16968
|
+
'[id*="sp_message" i]',
|
|
16969
|
+
'[class*="cookie" i]',
|
|
16970
|
+
'[class*="consent" i]',
|
|
16971
|
+
'[class*="cmp" i]',
|
|
16972
|
+
'[class*="sp_message" i]',
|
|
16973
|
+
'[class*="truste" i]',
|
|
16974
|
+
'[class*="didomi" i]',
|
|
16975
|
+
'[data-testid*="cookie" i]',
|
|
16976
|
+
'[data-testid*="consent" i]',
|
|
16977
|
+
'[aria-label*="cookie" i]',
|
|
16978
|
+
'[aria-label*="consent" i]',
|
|
16979
|
+
'.fc-consent-root'
|
|
16755
16980
|
];
|
|
16756
|
-
|
|
16757
|
-
|
|
16758
|
-
|
|
16759
|
-
|
|
16760
|
-
|
|
16981
|
+
var actionSelector = [
|
|
16982
|
+
'button',
|
|
16983
|
+
'[role="button"]',
|
|
16984
|
+
'a[role="button"]',
|
|
16985
|
+
'a.message-component',
|
|
16986
|
+
'input[type="button"]',
|
|
16987
|
+
'input[type="submit"]'
|
|
16988
|
+
].join(',');
|
|
16989
|
+
var seen = [];
|
|
16990
|
+
|
|
16991
|
+
function normalize(text) {
|
|
16992
|
+
return String(text || '').replace(/\\s+/g, ' ').trim();
|
|
16993
|
+
}
|
|
16994
|
+
|
|
16995
|
+
function lower(text) {
|
|
16996
|
+
return normalize(text).toLowerCase();
|
|
16997
|
+
}
|
|
16998
|
+
|
|
16999
|
+
function isElementVisible(el) {
|
|
17000
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
17001
|
+
var style = window.getComputedStyle(el);
|
|
17002
|
+
if (style.display === 'none' || style.visibility === 'hidden') return false;
|
|
17003
|
+
if (Number(style.opacity || '1') < 0.05) return false;
|
|
17004
|
+
var rect = el.getBoundingClientRect();
|
|
17005
|
+
return rect.width > 2 &&
|
|
17006
|
+
rect.height > 2 &&
|
|
17007
|
+
rect.bottom > 0 &&
|
|
17008
|
+
rect.right > 0 &&
|
|
17009
|
+
rect.top < window.innerHeight &&
|
|
17010
|
+
rect.left < window.innerWidth;
|
|
17011
|
+
}
|
|
17012
|
+
|
|
17013
|
+
function elementText(el) {
|
|
17014
|
+
if (!(el instanceof HTMLElement)) return '';
|
|
17015
|
+
return normalize([
|
|
17016
|
+
el.getAttribute('aria-label'),
|
|
17017
|
+
el.getAttribute('title'),
|
|
17018
|
+
el.getAttribute('value'),
|
|
17019
|
+
el.textContent
|
|
17020
|
+
].filter(Boolean).join(' '));
|
|
17021
|
+
}
|
|
17022
|
+
|
|
17023
|
+
function looksLikeCookieSurface(el) {
|
|
17024
|
+
if (!isElementVisible(el)) return false;
|
|
17025
|
+
var text = lower([
|
|
17026
|
+
el.getAttribute('aria-label'),
|
|
17027
|
+
el.getAttribute('id'),
|
|
17028
|
+
el.getAttribute('class'),
|
|
17029
|
+
el.textContent
|
|
17030
|
+
].filter(Boolean).join(' ')).slice(0, 1600);
|
|
17031
|
+
if (!/(cookie|consent|privacy|tracking|personalise|personalize|advertis|data choices|your choices|cmp|onetrust|truste|didomi)/.test(text)) {
|
|
17032
|
+
return false;
|
|
16761
17033
|
}
|
|
17034
|
+
|
|
17035
|
+
var rect = el.getBoundingClientRect();
|
|
17036
|
+
var style = window.getComputedStyle(el);
|
|
17037
|
+
var fixedLike = style.position === 'fixed' || style.position === 'sticky';
|
|
17038
|
+
var sizeable = rect.width >= Math.min(window.innerWidth * 0.35, 360) && rect.height >= 48;
|
|
17039
|
+
var bottomBanner = fixedLike && rect.bottom > window.innerHeight * 0.58 && rect.height >= 64;
|
|
17040
|
+
var dialog = el.getAttribute('role') === 'dialog' || el.getAttribute('aria-modal') === 'true';
|
|
17041
|
+
var namedSurface = /(cookie|consent|cmp|onetrust|truste|didomi|sp_message)/.test(lower([
|
|
17042
|
+
el.getAttribute('id'),
|
|
17043
|
+
el.getAttribute('class'),
|
|
17044
|
+
el.getAttribute('data-testid')
|
|
17045
|
+
].filter(Boolean).join(' ')));
|
|
17046
|
+
|
|
17047
|
+
return sizeable && (namedSurface || bottomBanner || dialog);
|
|
16762
17048
|
}
|
|
16763
|
-
|
|
16764
|
-
|
|
16765
|
-
var
|
|
16766
|
-
|
|
16767
|
-
|
|
16768
|
-
|
|
16769
|
-
btn.click();
|
|
16770
|
-
return "Dismissed cookie banner via text match: " + text;
|
|
17049
|
+
|
|
17050
|
+
function cookieSurfaces() {
|
|
17051
|
+
var surfaces = [];
|
|
17052
|
+
function addSurface(el) {
|
|
17053
|
+
if (surfaces.indexOf(el) === -1 && looksLikeCookieSurface(el)) {
|
|
17054
|
+
surfaces.push(el);
|
|
16771
17055
|
}
|
|
16772
17056
|
}
|
|
17057
|
+
|
|
17058
|
+
for (var i = 0; i < surfaceSelectors.length; i++) {
|
|
17059
|
+
try {
|
|
17060
|
+
document.querySelectorAll(surfaceSelectors[i]).forEach(addSurface);
|
|
17061
|
+
} catch {
|
|
17062
|
+
// Ignore unsupported selectors in older page engines.
|
|
17063
|
+
}
|
|
17064
|
+
}
|
|
17065
|
+
|
|
17066
|
+
document.querySelectorAll('div, section, aside, footer, form, [role="dialog"]').forEach(function(el) {
|
|
17067
|
+
if (!(el instanceof HTMLElement)) return;
|
|
17068
|
+
var style = window.getComputedStyle(el);
|
|
17069
|
+
if (style.position === 'fixed' || style.position === 'sticky' || el.getAttribute('role') === 'dialog') {
|
|
17070
|
+
addSurface(el);
|
|
17071
|
+
}
|
|
17072
|
+
});
|
|
17073
|
+
|
|
17074
|
+
return surfaces;
|
|
17075
|
+
}
|
|
17076
|
+
|
|
17077
|
+
function hasCookieSurface() {
|
|
17078
|
+
return cookieSurfaces().length > 0;
|
|
17079
|
+
}
|
|
17080
|
+
|
|
17081
|
+
function labelScore(label) {
|
|
17082
|
+
var text = lower(label);
|
|
17083
|
+
if (!text) return 0;
|
|
17084
|
+
if (/^(accept all|accept cookies|allow all|allow cookies|accept and continue|accept & continue|agree and continue)$/.test(text)) {
|
|
17085
|
+
return 220;
|
|
17086
|
+
}
|
|
17087
|
+
if (/^(i agree|i accept|agree|accept|allow|ok|okay|got it|continue|yes)$/.test(text)) {
|
|
17088
|
+
return 190;
|
|
17089
|
+
}
|
|
17090
|
+
if (/^(reject all|decline|deny|necessary only|essential only|save preferences|confirm choices|submit preferences)$/.test(text)) {
|
|
17091
|
+
return 170;
|
|
17092
|
+
}
|
|
17093
|
+
if (/\\b(accept all|allow all|accept cookies|allow cookies|accept and continue|accept & continue|agree and continue)\\b/.test(text)) {
|
|
17094
|
+
return 160;
|
|
17095
|
+
}
|
|
17096
|
+
if (/\\b(reject all|save preferences|confirm choices|necessary only|essential only)\\b/.test(text)) {
|
|
17097
|
+
return 145;
|
|
17098
|
+
}
|
|
17099
|
+
if (/^(consent|cookie consent|cookies|privacy|privacy policy|cookie policy|learn more|more information|settings|preferences|manage options|customize|customise)$/.test(text)) {
|
|
17100
|
+
return 0;
|
|
17101
|
+
}
|
|
17102
|
+
return 0;
|
|
17103
|
+
}
|
|
17104
|
+
|
|
17105
|
+
function addCandidate(el, source, baseScore) {
|
|
17106
|
+
if (!(el instanceof HTMLElement) || seen.indexOf(el) !== -1 || !isElementVisible(el)) return;
|
|
17107
|
+
var label = elementText(el);
|
|
17108
|
+
var score = labelScore(label);
|
|
17109
|
+
if (score <= 0 && baseScore < 160) return;
|
|
17110
|
+
seen.push(el);
|
|
17111
|
+
candidates.push({
|
|
17112
|
+
el: el,
|
|
17113
|
+
label: label || source,
|
|
17114
|
+
source: source,
|
|
17115
|
+
score: baseScore + score
|
|
17116
|
+
});
|
|
17117
|
+
}
|
|
17118
|
+
|
|
17119
|
+
var beforeHadSurface = hasCookieSurface();
|
|
17120
|
+
var candidates = [];
|
|
17121
|
+
|
|
17122
|
+
for (var i = 0; i < selectorTargets.length; i++) {
|
|
17123
|
+
try {
|
|
17124
|
+
document.querySelectorAll(selectorTargets[i]).forEach(function(el) {
|
|
17125
|
+
addCandidate(el, selectorTargets[i], 160);
|
|
17126
|
+
});
|
|
17127
|
+
} catch {
|
|
17128
|
+
// Ignore unsupported selectors in older page engines.
|
|
17129
|
+
}
|
|
17130
|
+
}
|
|
17131
|
+
|
|
17132
|
+
var surfaces = cookieSurfaces();
|
|
17133
|
+
surfaces.forEach(function(surface) {
|
|
17134
|
+
surface.querySelectorAll(actionSelector).forEach(function(el) {
|
|
17135
|
+
addCandidate(el, 'cookie surface', 120);
|
|
17136
|
+
});
|
|
17137
|
+
});
|
|
17138
|
+
|
|
17139
|
+
if (beforeHadSurface) {
|
|
17140
|
+
document.querySelectorAll(actionSelector).forEach(function(el) {
|
|
17141
|
+
addCandidate(el, 'page action', 40);
|
|
17142
|
+
});
|
|
16773
17143
|
}
|
|
17144
|
+
|
|
17145
|
+
candidates.sort(function(a, b) { return b.score - a.score; });
|
|
17146
|
+
|
|
17147
|
+
var tried = 0;
|
|
17148
|
+
for (var j = 0; j < Math.min(candidates.length, 8); j++) {
|
|
17149
|
+
var candidate = candidates[j];
|
|
17150
|
+
tried += 1;
|
|
17151
|
+
candidate.el.click();
|
|
17152
|
+
await delay(220);
|
|
17153
|
+
if (!hasCookieSurface()) {
|
|
17154
|
+
return {
|
|
17155
|
+
status: 'dismissed',
|
|
17156
|
+
message: 'Dismissed cookie banner via: ' + candidate.label.slice(0, 80)
|
|
17157
|
+
};
|
|
17158
|
+
}
|
|
17159
|
+
}
|
|
17160
|
+
|
|
17161
|
+
if (beforeHadSurface || tried > 0) {
|
|
17162
|
+
return {
|
|
17163
|
+
status: 'still_visible',
|
|
17164
|
+
message: tried > 0
|
|
17165
|
+
? 'Cookie consent banner is still visible after trying ' + tried + ' candidate control(s). Try clear_overlays or dismiss_popup.'
|
|
17166
|
+
: 'Cookie consent banner appears visible, but no reliable accept/reject button was found. Try clear_overlays or dismiss_popup.'
|
|
17167
|
+
};
|
|
17168
|
+
}
|
|
17169
|
+
|
|
16774
17170
|
return null;
|
|
16775
17171
|
})()
|
|
16776
17172
|
`,
|
|
16777
17173
|
{
|
|
16778
17174
|
label: "accept cookies",
|
|
16779
|
-
timeoutMs:
|
|
17175
|
+
timeoutMs: 2200,
|
|
17176
|
+
userGesture: true
|
|
16780
17177
|
}
|
|
16781
17178
|
);
|
|
16782
17179
|
if (dismissed) return dismissed;
|
|
16783
|
-
|
|
17180
|
+
const iframeDismissed = await tryDismissConsentIframe(wc);
|
|
17181
|
+
return iframeDismissed ? { status: "dismissed", message: iframeDismissed } : null;
|
|
16784
17182
|
}
|
|
16785
17183
|
async function clearOverlays(wc, strategy = "auto") {
|
|
16786
17184
|
const quickCookieResult = await tryAcceptCookiesQuickly(wc);
|
|
16787
17185
|
if (quickCookieResult === PAGE_SCRIPT_TIMEOUT) {
|
|
16788
17186
|
return pageBusyError("clear_overlays");
|
|
16789
17187
|
}
|
|
16790
|
-
if (quickCookieResult) {
|
|
17188
|
+
if (quickCookieResult?.status === "dismissed") {
|
|
16791
17189
|
return [
|
|
16792
|
-
quickCookieResult,
|
|
17190
|
+
quickCookieResult.message,
|
|
16793
17191
|
"Stopped after a lightweight consent pass to keep the page responsive. Re-run only if the banner is still blocking the page."
|
|
16794
17192
|
].join("\n");
|
|
16795
17193
|
}
|
|
@@ -17738,6 +18136,94 @@ async function locateSearchTarget(wc, explicitSelector) {
|
|
|
17738
18136
|
}
|
|
17739
18137
|
);
|
|
17740
18138
|
}
|
|
18139
|
+
async function locateImplicitTextTarget(wc) {
|
|
18140
|
+
return executePageScript(
|
|
18141
|
+
wc,
|
|
18142
|
+
`
|
|
18143
|
+
(function() {
|
|
18144
|
+
function normalize(value) {
|
|
18145
|
+
return value == null ? "" : String(value).trim().toLowerCase();
|
|
18146
|
+
}
|
|
18147
|
+
|
|
18148
|
+
function isVisible(el) {
|
|
18149
|
+
if (!(el instanceof HTMLElement)) return true;
|
|
18150
|
+
const style = window.getComputedStyle(el);
|
|
18151
|
+
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") return false;
|
|
18152
|
+
if (el.hasAttribute("hidden") || el.getAttribute("aria-hidden") === "true") return false;
|
|
18153
|
+
const rect = el.getBoundingClientRect();
|
|
18154
|
+
return rect.width > 0 && rect.height > 0;
|
|
18155
|
+
}
|
|
18156
|
+
|
|
18157
|
+
function inViewport(el) {
|
|
18158
|
+
if (!(el instanceof HTMLElement)) return true;
|
|
18159
|
+
const rect = el.getBoundingClientRect();
|
|
18160
|
+
const vw = window.innerWidth || document.documentElement?.clientWidth || 0;
|
|
18161
|
+
const vh = window.innerHeight || document.documentElement?.clientHeight || 0;
|
|
18162
|
+
return rect.bottom > 0 && rect.right > 0 && rect.top < vh && rect.left < vw;
|
|
18163
|
+
}
|
|
18164
|
+
|
|
18165
|
+
function isFillable(el) {
|
|
18166
|
+
if (!(el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) return false;
|
|
18167
|
+
if (el.disabled || el.readOnly || el.getAttribute("aria-disabled") === "true") return false;
|
|
18168
|
+
const type = el instanceof HTMLTextAreaElement ? "text" : normalize(el.getAttribute("type") || el.type || "text");
|
|
18169
|
+
return ["", "search", "text", "email", "url", "tel", "number", "password"].includes(type);
|
|
18170
|
+
}
|
|
18171
|
+
|
|
18172
|
+
function nearestSearchScope(input) {
|
|
18173
|
+
return input.closest('[role="search"], form, header, nav, [class*="search" i], [id*="search" i]');
|
|
18174
|
+
}
|
|
18175
|
+
|
|
18176
|
+
${selectorHelpersJS(["data-testid", "name", "form", "aria-label", "placeholder"])}
|
|
18177
|
+
|
|
18178
|
+
const active = document.activeElement;
|
|
18179
|
+
if (active && isFillable(active) && isVisible(active) && inViewport(active)) {
|
|
18180
|
+
return selectorFor(active);
|
|
18181
|
+
}
|
|
18182
|
+
|
|
18183
|
+
const candidates = Array.from(
|
|
18184
|
+
document.querySelectorAll('input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="image"]), textarea')
|
|
18185
|
+
).filter((el) => isFillable(el) && isVisible(el));
|
|
18186
|
+
|
|
18187
|
+
let best = null;
|
|
18188
|
+
let bestScore = -1;
|
|
18189
|
+
for (const el of candidates) {
|
|
18190
|
+
let score = 0;
|
|
18191
|
+
if (inViewport(el)) score += 100;
|
|
18192
|
+
const rect = el.getBoundingClientRect();
|
|
18193
|
+
score += Math.max(0, 36 - Math.min(36, Math.floor(Math.max(0, rect.top) / 22)));
|
|
18194
|
+
|
|
18195
|
+
const type = el instanceof HTMLTextAreaElement ? "text" : normalize(el.getAttribute("type") || el.type);
|
|
18196
|
+
const name = normalize(el.getAttribute("name"));
|
|
18197
|
+
const placeholder = normalize(el.getAttribute("placeholder"));
|
|
18198
|
+
const aria = normalize(el.getAttribute("aria-label"));
|
|
18199
|
+
const role = normalize(el.getAttribute("role"));
|
|
18200
|
+
const id = normalize(el.getAttribute("id"));
|
|
18201
|
+
|
|
18202
|
+
if (type === "search") score += 80;
|
|
18203
|
+
if (role === "searchbox") score += 70;
|
|
18204
|
+
if (name === "q" || name === "query" || name === "search") score += 65;
|
|
18205
|
+
if (placeholder.includes("search")) score += 55;
|
|
18206
|
+
if (aria.includes("search")) score += 55;
|
|
18207
|
+
if (id.includes("search")) score += 35;
|
|
18208
|
+
|
|
18209
|
+
const scope = nearestSearchScope(el);
|
|
18210
|
+
if (scope) score += 35;
|
|
18211
|
+
|
|
18212
|
+
if (score > bestScore) {
|
|
18213
|
+
best = el;
|
|
18214
|
+
bestScore = score;
|
|
18215
|
+
}
|
|
18216
|
+
}
|
|
18217
|
+
|
|
18218
|
+
return best ? selectorFor(best) : null;
|
|
18219
|
+
})()
|
|
18220
|
+
`,
|
|
18221
|
+
{
|
|
18222
|
+
timeoutMs: 2200,
|
|
18223
|
+
label: "find implicit text input"
|
|
18224
|
+
}
|
|
18225
|
+
);
|
|
18226
|
+
}
|
|
17741
18227
|
async function searchPage(wc, args) {
|
|
17742
18228
|
const query = String(args.query || "");
|
|
17743
18229
|
if (!query) return "Error: No search query provided.";
|
|
@@ -18227,8 +18713,19 @@ async function executeAction(name, args, ctx) {
|
|
|
18227
18713
|
}
|
|
18228
18714
|
case "type_text": {
|
|
18229
18715
|
if (!wc) return "Error: No active tab";
|
|
18230
|
-
|
|
18231
|
-
if (
|
|
18716
|
+
let selector = await resolveSelector(wc, args.index, args.selector);
|
|
18717
|
+
if (selector === PAGE_SCRIPT_TIMEOUT) {
|
|
18718
|
+
return pageBusyError("type_text");
|
|
18719
|
+
}
|
|
18720
|
+
if (!selector) {
|
|
18721
|
+
selector = await locateImplicitTextTarget(wc);
|
|
18722
|
+
}
|
|
18723
|
+
if (selector === PAGE_SCRIPT_TIMEOUT) {
|
|
18724
|
+
return pageBusyError("type_text");
|
|
18725
|
+
}
|
|
18726
|
+
if (!selector) {
|
|
18727
|
+
return "Error: No element index or selector provided, and no focused or visible text input could be found.";
|
|
18728
|
+
}
|
|
18232
18729
|
const mode = typeof args.mode === "string" ? args.mode : "default";
|
|
18233
18730
|
if (mode === "keystroke") {
|
|
18234
18731
|
return typeKeystroke(wc, selector, String(args.text || ""));
|
|
@@ -18324,6 +18821,23 @@ async function executeAction(name, args, ctx) {
|
|
|
18324
18821
|
if (requestedGlance) {
|
|
18325
18822
|
return glanceExtract(wc);
|
|
18326
18823
|
}
|
|
18824
|
+
const requestedTextMode = typeof args.mode === "string" ? args.mode.trim().toLowerCase() : "";
|
|
18825
|
+
if (requestedTextMode === "summary" || requestedTextMode === "text_only") {
|
|
18826
|
+
const fastArticleText = await fastArticleTextExtract(
|
|
18827
|
+
wc,
|
|
18828
|
+
requestedTextMode
|
|
18829
|
+
);
|
|
18830
|
+
if (fastArticleText) {
|
|
18831
|
+
return fastArticleText;
|
|
18832
|
+
}
|
|
18833
|
+
const fetchedArticleText = await fetchArticleTextExtract(
|
|
18834
|
+
wc,
|
|
18835
|
+
requestedTextMode
|
|
18836
|
+
);
|
|
18837
|
+
if (fetchedArticleText) {
|
|
18838
|
+
return fetchedArticleText;
|
|
18839
|
+
}
|
|
18840
|
+
}
|
|
18327
18841
|
let content = null;
|
|
18328
18842
|
try {
|
|
18329
18843
|
content = await Promise.race([
|
|
@@ -19020,7 +19534,7 @@ ${steps.join("\n")}`;
|
|
|
19020
19534
|
if (dismissed === PAGE_SCRIPT_TIMEOUT) {
|
|
19021
19535
|
return pageBusyError("accept_cookies");
|
|
19022
19536
|
}
|
|
19023
|
-
if (dismissed) return dismissed;
|
|
19537
|
+
if (dismissed) return dismissed.message;
|
|
19024
19538
|
return "No cookie consent banner detected. Try dismiss_popup for other overlays.";
|
|
19025
19539
|
}
|
|
19026
19540
|
case "extract_table": {
|
|
@@ -20272,13 +20786,31 @@ async function showDownloadInFolder(id) {
|
|
|
20272
20786
|
}
|
|
20273
20787
|
const defaultDownloadViews = /* @__PURE__ */ new Set();
|
|
20274
20788
|
let defaultDownloadHandlerInstalled = false;
|
|
20789
|
+
function sanitizeDownloadFilename(filename) {
|
|
20790
|
+
const normalized = filename.replace(/\\/g, "/");
|
|
20791
|
+
const basename = path.posix.basename(normalized).trim();
|
|
20792
|
+
const safeName = basename.replace(/[\0-\x1f\x7f]/g, "_");
|
|
20793
|
+
if (!safeName || safeName === "." || safeName === "..") {
|
|
20794
|
+
return "download";
|
|
20795
|
+
}
|
|
20796
|
+
return safeName;
|
|
20797
|
+
}
|
|
20798
|
+
function isPathInside(parentDir, candidatePath) {
|
|
20799
|
+
const relative = path.relative(parentDir, candidatePath);
|
|
20800
|
+
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
20801
|
+
}
|
|
20275
20802
|
function resolveDownloadPath(downloadDir, filename) {
|
|
20276
20803
|
fs$1.mkdirSync(downloadDir, { recursive: true });
|
|
20277
|
-
const
|
|
20804
|
+
const rootDir = path.resolve(downloadDir);
|
|
20805
|
+
const safeFilename = sanitizeDownloadFilename(filename);
|
|
20806
|
+
const parsed = path.parse(safeFilename);
|
|
20278
20807
|
let attempt = 0;
|
|
20279
20808
|
while (true) {
|
|
20280
|
-
const candidateName = attempt === 0 ?
|
|
20281
|
-
const candidatePath = path.
|
|
20809
|
+
const candidateName = attempt === 0 ? safeFilename : `${parsed.name} (${attempt})${parsed.ext}`;
|
|
20810
|
+
const candidatePath = path.resolve(rootDir, candidateName);
|
|
20811
|
+
if (!isPathInside(rootDir, candidatePath)) {
|
|
20812
|
+
throw new Error("Blocked unsafe download filename");
|
|
20813
|
+
}
|
|
20282
20814
|
if (!fs$1.existsSync(candidatePath)) {
|
|
20283
20815
|
return candidatePath;
|
|
20284
20816
|
}
|
|
@@ -21455,7 +21987,7 @@ async function handleAIQuery(query, provider, activeWebContents, onChunk, onEnd,
|
|
|
21455
21987
|
const isSummarize = lowerQuery.startsWith("summarize") || lowerQuery.startsWith("tldr") || lowerQuery === "summary";
|
|
21456
21988
|
if (provider.streamAgentQuery && tabManager && activeWebContents && runtime2) {
|
|
21457
21989
|
try {
|
|
21458
|
-
const pageContent = await extractContent
|
|
21990
|
+
const pageContent = await extractContent(activeWebContents);
|
|
21459
21991
|
const pageType = detectPageType(pageContent);
|
|
21460
21992
|
const defaultReadMode = chooseAgentReadMode(pageContent);
|
|
21461
21993
|
if (provider.agentToolProfile === "compact") {
|
|
@@ -21556,7 +22088,7 @@ ${trackerCtx}`;
|
|
|
21556
22088
|
let prompt;
|
|
21557
22089
|
if (activeWebContents) {
|
|
21558
22090
|
try {
|
|
21559
|
-
const pageContent = await extractContent
|
|
22091
|
+
const pageContent = await extractContent(activeWebContents);
|
|
21560
22092
|
if (isSummarize) {
|
|
21561
22093
|
prompt = buildSummarizePrompt(pageContent);
|
|
21562
22094
|
} else {
|
|
@@ -21837,7 +22369,7 @@ function registerContentHandlers(windowState2) {
|
|
|
21837
22369
|
assertTrustedIpcSender(event);
|
|
21838
22370
|
const activeTab = tabManager.getActiveTab();
|
|
21839
22371
|
if (!activeTab) return null;
|
|
21840
|
-
return extractContent
|
|
22372
|
+
return extractContent(activeTab.view.webContents);
|
|
21841
22373
|
});
|
|
21842
22374
|
electron.ipcMain.handle(Channels.READER_MODE_TOGGLE, async (event) => {
|
|
21843
22375
|
assertTrustedIpcSender(event);
|
|
@@ -21851,7 +22383,7 @@ function registerContentHandlers(windowState2) {
|
|
|
21851
22383
|
}
|
|
21852
22384
|
} else {
|
|
21853
22385
|
const originalUrl = activeTab.state.url;
|
|
21854
|
-
const content = await extractContent
|
|
22386
|
+
const content = await extractContent(activeTab.view.webContents);
|
|
21855
22387
|
const html = generateReaderHTML(content);
|
|
21856
22388
|
activeTab.setReaderMode(true, originalUrl);
|
|
21857
22389
|
void loadInternalDataURL(
|
|
@@ -22177,6 +22709,13 @@ function broadcastState(tabManager) {
|
|
|
22177
22709
|
stateListener(getDevToolsPanelState(tabId));
|
|
22178
22710
|
}
|
|
22179
22711
|
async function withDevToolsAction(runtime2, tabManager, name, args, executor) {
|
|
22712
|
+
try {
|
|
22713
|
+
assertFeatureUnlocked("devtools", "DevTools");
|
|
22714
|
+
} catch (error) {
|
|
22715
|
+
return asTextResponse$1(
|
|
22716
|
+
`Error: ${error instanceof Error ? error.message : "DevTools require Vessel Premium."}`
|
|
22717
|
+
);
|
|
22718
|
+
}
|
|
22180
22719
|
const activityEntry = {
|
|
22181
22720
|
id: ++activityCounter,
|
|
22182
22721
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -23034,7 +23573,7 @@ async function getPostActionState(tabManager, name) {
|
|
|
23034
23573
|
if (navActions.includes(name)) {
|
|
23035
23574
|
let warning = "";
|
|
23036
23575
|
try {
|
|
23037
|
-
const page = await extractContent
|
|
23576
|
+
const page = await extractContent(wc);
|
|
23038
23577
|
const issue = getRecoverableAccessIssue(page);
|
|
23039
23578
|
if (issue) {
|
|
23040
23579
|
const blockedUrl = wc.getURL();
|
|
@@ -23900,7 +24439,7 @@ function registerMemoryTools(server, tabManager, runtime2) {
|
|
|
23900
24439
|
"memory_page_capture",
|
|
23901
24440
|
{ title, folder, tags },
|
|
23902
24441
|
async () => {
|
|
23903
|
-
const page = await extractContent
|
|
24442
|
+
const page = await extractContent(tab.view.webContents);
|
|
23904
24443
|
const saved = await capturePageToVault({
|
|
23905
24444
|
page,
|
|
23906
24445
|
title,
|
|
@@ -24667,7 +25206,7 @@ function registerTools(server, tabManager, runtime2) {
|
|
|
24667
25206
|
const wc = activeTab.view.webContents;
|
|
24668
25207
|
pageUrl = wc.getURL();
|
|
24669
25208
|
pageTitle = wc.getTitle();
|
|
24670
|
-
const page = await extractContent
|
|
25209
|
+
const page = await extractContent(wc);
|
|
24671
25210
|
pageType = detectPageType(page);
|
|
24672
25211
|
} catch (err) {
|
|
24673
25212
|
logger$b.warn("Failed to detect page type for tool scoring, falling back to GENERAL:", err);
|
|
@@ -24833,7 +25372,7 @@ ${buildScopedContext(pageContent, mode)}`;
|
|
|
24833
25372
|
const tab = tabManager.getActiveTab();
|
|
24834
25373
|
if (!tab) return asNoActiveTabResponse();
|
|
24835
25374
|
try {
|
|
24836
|
-
const pageContent = await extractContent
|
|
25375
|
+
const pageContent = await extractContent(tab.view.webContents);
|
|
24837
25376
|
const effectiveMode = mode || "full";
|
|
24838
25377
|
return asTextResponse(
|
|
24839
25378
|
await buildExtractResponse(
|
|
@@ -24865,7 +25404,7 @@ ${buildScopedContext(pageContent, mode)}`;
|
|
|
24865
25404
|
const tab = tabManager.getActiveTab();
|
|
24866
25405
|
if (!tab) return asNoActiveTabResponse();
|
|
24867
25406
|
try {
|
|
24868
|
-
const pageContent = await extractContent
|
|
25407
|
+
const pageContent = await extractContent(tab.view.webContents);
|
|
24869
25408
|
const effectiveMode = mode || "full";
|
|
24870
25409
|
return asTextResponse(
|
|
24871
25410
|
await buildExtractResponse(
|
|
@@ -25000,7 +25539,7 @@ ${buildScopedContext(pageContent, mode)}`;
|
|
|
25000
25539
|
const tab = tabManager.getActiveTab();
|
|
25001
25540
|
if (!tab) return asNoActiveTabResponse();
|
|
25002
25541
|
try {
|
|
25003
|
-
const pageContent = await extractContent
|
|
25542
|
+
const pageContent = await extractContent(tab.view.webContents);
|
|
25004
25543
|
const requestedType = typeof type === "string" && type.trim() ? type.trim().toLowerCase() : "";
|
|
25005
25544
|
const entities = (pageContent.structuredData ?? []).filter(
|
|
25006
25545
|
(entity) => requestedType ? entity.types.some(
|
|
@@ -26082,7 +26621,7 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
|
|
|
26082
26621
|
const wc = tab.view.webContents;
|
|
26083
26622
|
let page;
|
|
26084
26623
|
try {
|
|
26085
|
-
page = await extractContent
|
|
26624
|
+
page = await extractContent(wc);
|
|
26086
26625
|
} catch (err) {
|
|
26087
26626
|
logger$b.warn("Failed to extract page while generating suggestions:", err);
|
|
26088
26627
|
return asTextResponse(
|
|
@@ -27643,11 +28182,7 @@ const VALID_KIT_CATEGORIES = /* @__PURE__ */ new Set([
|
|
|
27643
28182
|
"productivity",
|
|
27644
28183
|
"forms"
|
|
27645
28184
|
]);
|
|
27646
|
-
const BUNDLED_KIT_IDS = /* @__PURE__ */ new Set(
|
|
27647
|
-
"research-collect",
|
|
27648
|
-
"price-scout",
|
|
27649
|
-
"form-filler"
|
|
27650
|
-
]);
|
|
28185
|
+
const BUNDLED_KIT_IDS = /* @__PURE__ */ new Set();
|
|
27651
28186
|
const KIT_ID_UNSAFE_CHAR_PATTERN = /[/\\\0]/;
|
|
27652
28187
|
function isSafeAutomationKitId(id) {
|
|
27653
28188
|
return id.length > 0 && !KIT_ID_UNSAFE_CHAR_PATTERN.test(id);
|
|
@@ -27697,18 +28232,18 @@ async function getInstalledKits() {
|
|
|
27697
28232
|
if (isValidKit(parsed)) {
|
|
27698
28233
|
kits.push(parsed);
|
|
27699
28234
|
} else {
|
|
27700
|
-
logger$9.warn(`Skipping invalid
|
|
28235
|
+
logger$9.warn(`Skipping invalid skill file: ${file}`);
|
|
27701
28236
|
}
|
|
27702
28237
|
} catch (err) {
|
|
27703
|
-
logger$9.warn(`Failed to read
|
|
28238
|
+
logger$9.warn(`Failed to read skill file: ${file}`, err);
|
|
27704
28239
|
}
|
|
27705
28240
|
}
|
|
27706
28241
|
return kits;
|
|
27707
28242
|
}
|
|
27708
28243
|
async function installKitFromFile() {
|
|
27709
28244
|
const { canceled, filePaths } = await electron.dialog.showOpenDialog({
|
|
27710
|
-
title: "
|
|
27711
|
-
filters: [{ name: "
|
|
28245
|
+
title: "Import Skill",
|
|
28246
|
+
filters: [{ name: "Skills", extensions: ["skill.json", "json"] }],
|
|
27712
28247
|
properties: ["openFile"]
|
|
27713
28248
|
});
|
|
27714
28249
|
if (canceled || filePaths.length === 0) {
|
|
@@ -27718,62 +28253,128 @@ async function installKitFromFile() {
|
|
|
27718
28253
|
try {
|
|
27719
28254
|
raw = await readFile(filePaths[0], "utf-8");
|
|
27720
28255
|
} catch (err) {
|
|
27721
|
-
logger$9.warn("Failed to read selected
|
|
28256
|
+
logger$9.warn("Failed to read selected skill file:", err);
|
|
27722
28257
|
return errorResult("Could not read the selected file.");
|
|
27723
28258
|
}
|
|
27724
28259
|
let parsed;
|
|
27725
28260
|
try {
|
|
27726
28261
|
parsed = JSON.parse(raw);
|
|
27727
28262
|
} catch (err) {
|
|
27728
|
-
logger$9.warn("Selected
|
|
28263
|
+
logger$9.warn("Selected skill file is not valid JSON:", err);
|
|
27729
28264
|
return errorResult("File is not valid JSON.");
|
|
27730
28265
|
}
|
|
27731
28266
|
if (!isValidKit(parsed)) {
|
|
27732
28267
|
return errorResult(
|
|
27733
|
-
"File is not a valid
|
|
28268
|
+
"File is not a valid skill. Required fields: id, name, description, icon, inputs, promptTemplate."
|
|
28269
|
+
);
|
|
28270
|
+
}
|
|
28271
|
+
if (BUNDLED_KIT_IDS.has(parsed.id)) {
|
|
28272
|
+
return errorResult(
|
|
28273
|
+
`Skill id "${parsed.id}" conflicts with a built-in skill and cannot be overwritten.`
|
|
28274
|
+
);
|
|
28275
|
+
}
|
|
28276
|
+
await ensureKitsDir();
|
|
28277
|
+
const dest = getKitFilePath(parsed.id);
|
|
28278
|
+
if (!dest) {
|
|
28279
|
+
return errorResult("Skill id contains unsupported characters.");
|
|
28280
|
+
}
|
|
28281
|
+
try {
|
|
28282
|
+
await writeFile(dest, JSON.stringify(parsed, null, 2), "utf-8");
|
|
28283
|
+
} catch (err) {
|
|
28284
|
+
logger$9.warn("Failed to save skill file:", err);
|
|
28285
|
+
return errorResult("Failed to save the skill file.");
|
|
28286
|
+
}
|
|
28287
|
+
return okResult({ kit: parsed });
|
|
28288
|
+
}
|
|
28289
|
+
async function createKitFromText(source) {
|
|
28290
|
+
let parsed;
|
|
28291
|
+
try {
|
|
28292
|
+
parsed = JSON.parse(source);
|
|
28293
|
+
} catch (err) {
|
|
28294
|
+
logger$9.warn("Created skill text is not valid JSON:", err);
|
|
28295
|
+
return errorResult("Skill text is not valid JSON.");
|
|
28296
|
+
}
|
|
28297
|
+
if (!isValidKit(parsed)) {
|
|
28298
|
+
return errorResult(
|
|
28299
|
+
"Text is not a valid skill. Required fields: id, name, description, icon, inputs, promptTemplate."
|
|
27734
28300
|
);
|
|
27735
28301
|
}
|
|
27736
28302
|
if (BUNDLED_KIT_IDS.has(parsed.id)) {
|
|
27737
28303
|
return errorResult(
|
|
27738
|
-
`
|
|
28304
|
+
`Skill id "${parsed.id}" conflicts with a built-in skill and cannot be overwritten.`
|
|
27739
28305
|
);
|
|
27740
28306
|
}
|
|
27741
28307
|
await ensureKitsDir();
|
|
27742
28308
|
const dest = getKitFilePath(parsed.id);
|
|
27743
28309
|
if (!dest) {
|
|
27744
|
-
return errorResult("
|
|
28310
|
+
return errorResult("Skill id contains unsupported characters.");
|
|
27745
28311
|
}
|
|
27746
28312
|
try {
|
|
27747
28313
|
await writeFile(dest, JSON.stringify(parsed, null, 2), "utf-8");
|
|
27748
28314
|
} catch (err) {
|
|
27749
|
-
logger$9.warn("Failed to save
|
|
27750
|
-
return errorResult("Failed to save the
|
|
28315
|
+
logger$9.warn("Failed to save created skill:", err);
|
|
28316
|
+
return errorResult("Failed to save the skill.");
|
|
28317
|
+
}
|
|
28318
|
+
return okResult({ kit: parsed });
|
|
28319
|
+
}
|
|
28320
|
+
async function updateKitFromText(id, source) {
|
|
28321
|
+
if (BUNDLED_KIT_IDS.has(id)) {
|
|
28322
|
+
return errorResult("Built-in skills cannot be edited.");
|
|
28323
|
+
}
|
|
28324
|
+
const target = getKitFilePath(id);
|
|
28325
|
+
if (!target) {
|
|
28326
|
+
return errorResult("Skill id contains unsupported characters.");
|
|
28327
|
+
}
|
|
28328
|
+
let parsed;
|
|
28329
|
+
try {
|
|
28330
|
+
parsed = JSON.parse(source);
|
|
28331
|
+
} catch (err) {
|
|
28332
|
+
logger$9.warn("Updated skill text is not valid JSON:", err);
|
|
28333
|
+
return errorResult("Skill text is not valid JSON.");
|
|
28334
|
+
}
|
|
28335
|
+
if (!isValidKit(parsed)) {
|
|
28336
|
+
return errorResult(
|
|
28337
|
+
"Text is not a valid skill. Required fields: id, name, description, icon, inputs, promptTemplate."
|
|
28338
|
+
);
|
|
28339
|
+
}
|
|
28340
|
+
if (parsed.id !== id) {
|
|
28341
|
+
return errorResult("Skill id cannot be changed while editing.");
|
|
28342
|
+
}
|
|
28343
|
+
await ensureKitsDir();
|
|
28344
|
+
if (!await pathExists(target)) {
|
|
28345
|
+
return errorResult("Skill not found.");
|
|
28346
|
+
}
|
|
28347
|
+
try {
|
|
28348
|
+
await writeFile(target, JSON.stringify(parsed, null, 2), "utf-8");
|
|
28349
|
+
} catch (err) {
|
|
28350
|
+
logger$9.warn("Failed to update skill:", err);
|
|
28351
|
+
return errorResult("Failed to update the skill.");
|
|
27751
28352
|
}
|
|
27752
28353
|
return okResult({ kit: parsed });
|
|
27753
28354
|
}
|
|
27754
28355
|
async function uninstallKit(id, scheduledKitIds) {
|
|
27755
28356
|
if (BUNDLED_KIT_IDS.has(id)) {
|
|
27756
|
-
return errorResult("Built-in
|
|
28357
|
+
return errorResult("Built-in skills cannot be removed.");
|
|
27757
28358
|
}
|
|
27758
28359
|
if (scheduledKitIds?.has(id)) {
|
|
27759
28360
|
return errorResult(
|
|
27760
|
-
"This
|
|
28361
|
+
"This skill has active scheduled jobs. Delete or reassign them first."
|
|
27761
28362
|
);
|
|
27762
28363
|
}
|
|
27763
28364
|
await ensureKitsDir();
|
|
27764
28365
|
const target = getKitFilePath(id);
|
|
27765
28366
|
if (!target) {
|
|
27766
|
-
return errorResult("
|
|
28367
|
+
return errorResult("Skill id contains unsupported characters.");
|
|
27767
28368
|
}
|
|
27768
28369
|
if (!await pathExists(target)) {
|
|
27769
|
-
return errorResult("
|
|
28370
|
+
return errorResult("Skill not found.");
|
|
27770
28371
|
}
|
|
27771
28372
|
try {
|
|
27772
28373
|
await unlink(target);
|
|
27773
28374
|
return okResult();
|
|
27774
28375
|
} catch (err) {
|
|
27775
|
-
logger$9.warn("Failed to remove
|
|
27776
|
-
return errorResult("Failed to remove the
|
|
28376
|
+
logger$9.warn("Failed to remove skill file:", err);
|
|
28377
|
+
return errorResult("Failed to remove the skill file.");
|
|
27777
28378
|
}
|
|
27778
28379
|
}
|
|
27779
28380
|
const logger$8 = createLogger("Scheduler");
|
|
@@ -27958,7 +28559,7 @@ async function fireJob(job, windowState2, runtime2) {
|
|
|
27958
28559
|
} catch (err) {
|
|
27959
28560
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
27960
28561
|
appendActivity(`
|
|
27961
|
-
[Scheduled
|
|
28562
|
+
[Scheduled Skill Error: ${msg}]`);
|
|
27962
28563
|
finishActivity("failed");
|
|
27963
28564
|
}
|
|
27964
28565
|
}
|
|
@@ -28016,10 +28617,12 @@ function registerScheduleHandlers(windowState2, runtime2, sendToAll) {
|
|
|
28016
28617
|
}, msToNextMinute);
|
|
28017
28618
|
electron.ipcMain.handle(Channels.SCHEDULE_GET_ALL, (event) => {
|
|
28018
28619
|
assertTrustedIpcSender(event);
|
|
28620
|
+
assertFeatureUnlocked("automation_kits", "Skills");
|
|
28019
28621
|
return jobs;
|
|
28020
28622
|
});
|
|
28021
28623
|
electron.ipcMain.handle(Channels.SCHEDULE_CREATE, (event, rawJob) => {
|
|
28022
28624
|
assertTrustedIpcSender(event);
|
|
28625
|
+
assertFeatureUnlocked("automation_kits", "Skills");
|
|
28023
28626
|
if (!isValidJobData(rawJob)) {
|
|
28024
28627
|
throw new Error(
|
|
28025
28628
|
"Invalid job data. Required: kitId, kitName, kitIcon, renderedPrompt, schedule, enabled."
|
|
@@ -28038,6 +28641,7 @@ function registerScheduleHandlers(windowState2, runtime2, sendToAll) {
|
|
|
28038
28641
|
});
|
|
28039
28642
|
electron.ipcMain.handle(Channels.SCHEDULE_UPDATE, (event, id, updates) => {
|
|
28040
28643
|
assertTrustedIpcSender(event);
|
|
28644
|
+
assertFeatureUnlocked("automation_kits", "Skills");
|
|
28041
28645
|
if (typeof id !== "string") throw new Error("id must be a string");
|
|
28042
28646
|
const job = jobs.find((j) => j.id === id);
|
|
28043
28647
|
if (!job) return null;
|
|
@@ -28065,6 +28669,7 @@ function registerScheduleHandlers(windowState2, runtime2, sendToAll) {
|
|
|
28065
28669
|
});
|
|
28066
28670
|
electron.ipcMain.handle(Channels.SCHEDULE_DELETE, (event, id) => {
|
|
28067
28671
|
assertTrustedIpcSender(event);
|
|
28672
|
+
assertFeatureUnlocked("automation_kits", "Skills");
|
|
28068
28673
|
if (typeof id !== "string") throw new Error("id must be a string");
|
|
28069
28674
|
const before = jobs.length;
|
|
28070
28675
|
jobs = jobs.filter((j) => j.id !== id);
|
|
@@ -28089,6 +28694,7 @@ function stopScheduler() {
|
|
|
28089
28694
|
}
|
|
28090
28695
|
}
|
|
28091
28696
|
const KitIdSchema = zod.z.string().min(1);
|
|
28697
|
+
const SkillSourceSchema = zod.z.string().min(1).max(1e5);
|
|
28092
28698
|
const OriginSchema = zod.z.string().min(1);
|
|
28093
28699
|
function registerSystemHandlers(windowState2, sendToRendererViews) {
|
|
28094
28700
|
const { tabManager } = windowState2;
|
|
@@ -28107,14 +28713,32 @@ function registerSystemHandlers(windowState2, sendToRendererViews) {
|
|
|
28107
28713
|
});
|
|
28108
28714
|
electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, async (event) => {
|
|
28109
28715
|
assertTrustedIpcSender(event);
|
|
28716
|
+
assertFeatureUnlocked("automation_kits", "Skills");
|
|
28110
28717
|
return await getInstalledKits();
|
|
28111
28718
|
});
|
|
28112
28719
|
electron.ipcMain.handle(Channels.AUTOMATION_INSTALL_FROM_FILE, async (event) => {
|
|
28113
28720
|
assertTrustedIpcSender(event);
|
|
28721
|
+
assertFeatureUnlocked("automation_kits", "Skills");
|
|
28114
28722
|
return await installKitFromFile();
|
|
28115
28723
|
});
|
|
28724
|
+
electron.ipcMain.handle(Channels.AUTOMATION_CREATE_FROM_TEXT, async (event, source) => {
|
|
28725
|
+
assertTrustedIpcSender(event);
|
|
28726
|
+
assertFeatureUnlocked("automation_kits", "Skills");
|
|
28727
|
+
return await createKitFromText(
|
|
28728
|
+
parseIpc(SkillSourceSchema, source, "source")
|
|
28729
|
+
);
|
|
28730
|
+
});
|
|
28731
|
+
electron.ipcMain.handle(Channels.AUTOMATION_UPDATE_FROM_TEXT, async (event, id, source) => {
|
|
28732
|
+
assertTrustedIpcSender(event);
|
|
28733
|
+
assertFeatureUnlocked("automation_kits", "Skills");
|
|
28734
|
+
return await updateKitFromText(
|
|
28735
|
+
parseIpc(KitIdSchema, id, "id"),
|
|
28736
|
+
parseIpc(SkillSourceSchema, source, "source")
|
|
28737
|
+
);
|
|
28738
|
+
});
|
|
28116
28739
|
electron.ipcMain.handle(Channels.AUTOMATION_UNINSTALL, async (event, id) => {
|
|
28117
28740
|
assertTrustedIpcSender(event);
|
|
28741
|
+
assertFeatureUnlocked("automation_kits", "Skills");
|
|
28118
28742
|
return await uninstallKit(
|
|
28119
28743
|
parseIpc(KitIdSchema, id, "id"),
|
|
28120
28744
|
getScheduledKitIds()
|
|
@@ -29577,7 +30201,7 @@ function registerAutofillHandlers(windowState2) {
|
|
|
29577
30201
|
const activeTab = windowState2.tabManager.getActiveTab();
|
|
29578
30202
|
const wc = activeTab?.view.webContents;
|
|
29579
30203
|
if (!wc) throw new Error("No active tab");
|
|
29580
|
-
const content = await extractContent
|
|
30204
|
+
const content = await extractContent(wc);
|
|
29581
30205
|
const elements = content.interactiveElements || [];
|
|
29582
30206
|
const matches = matchFields(elements, profile);
|
|
29583
30207
|
if (matches.length === 0) {
|