@quanta-intellect/vessel-browser 0.1.68 → 0.1.71
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/out/main/index.js
CHANGED
|
@@ -65,6 +65,7 @@ const defaults = {
|
|
|
65
65
|
domainPolicy: { allowedDomains: [], blockedDomains: [] },
|
|
66
66
|
downloadPath: "",
|
|
67
67
|
telemetryEnabled: true,
|
|
68
|
+
defaultSearchEngine: "duckduckgo",
|
|
68
69
|
premium: {
|
|
69
70
|
status: "free",
|
|
70
71
|
customerId: "",
|
|
@@ -76,7 +77,7 @@ const defaults = {
|
|
|
76
77
|
};
|
|
77
78
|
const SAVE_DEBOUNCE_MS$6 = 150;
|
|
78
79
|
const CHAT_PROVIDER_SECRET_FILENAME = "vessel-chat-provider-secret";
|
|
79
|
-
const logger$
|
|
80
|
+
const logger$k = createLogger("Settings");
|
|
80
81
|
const SETTABLE_KEYS = new Set(Object.keys(defaults));
|
|
81
82
|
let settings = null;
|
|
82
83
|
let settingsIssues = [];
|
|
@@ -222,7 +223,7 @@ function persistNow() {
|
|
|
222
223
|
getSettingsPath(),
|
|
223
224
|
JSON.stringify(buildPersistedSettings(settings), null, 2)
|
|
224
225
|
)
|
|
225
|
-
).catch((err) => logger$
|
|
226
|
+
).catch((err) => logger$k.error("Failed to save settings:", err));
|
|
226
227
|
}
|
|
227
228
|
function saveSettings() {
|
|
228
229
|
saveDirty = true;
|
|
@@ -326,7 +327,7 @@ function assertPermittedNavigationURL(url) {
|
|
|
326
327
|
}
|
|
327
328
|
const MAX_CUSTOM_HISTORY = 50;
|
|
328
329
|
const READER_MODE_DATA_URL_PREFIX = "data:text/html;charset=utf-8,";
|
|
329
|
-
const logger$
|
|
330
|
+
const logger$j = createLogger("Tab");
|
|
330
331
|
class Tab {
|
|
331
332
|
id;
|
|
332
333
|
view;
|
|
@@ -367,7 +368,7 @@ class Tab {
|
|
|
367
368
|
guardedLoadURL(url, options) {
|
|
368
369
|
const blockReason = this.getNavigationBlockReason(url);
|
|
369
370
|
if (blockReason) {
|
|
370
|
-
logger$
|
|
371
|
+
logger$j.warn(blockReason);
|
|
371
372
|
return blockReason;
|
|
372
373
|
}
|
|
373
374
|
void this.view.webContents.loadURL(url, options);
|
|
@@ -382,14 +383,16 @@ class Tab {
|
|
|
382
383
|
this.onHighlightSelection = options?.onHighlightSelection;
|
|
383
384
|
this.onHighlightRemove = options?.onHighlightRemove;
|
|
384
385
|
this.onHighlightRecolor = options?.onHighlightRecolor;
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
386
|
+
const webPreferences = {
|
|
387
|
+
preload: path.join(__dirname, "../preload/content-script.js"),
|
|
388
|
+
sandbox: true,
|
|
389
|
+
contextIsolation: true,
|
|
390
|
+
nodeIntegration: false
|
|
391
|
+
};
|
|
392
|
+
if (options?.sessionPartition) {
|
|
393
|
+
webPreferences.session = electron.session.fromPartition(options.sessionPartition);
|
|
394
|
+
}
|
|
395
|
+
this.view = new electron.WebContentsView({ webPreferences });
|
|
393
396
|
const initialUrl = url || "about:blank";
|
|
394
397
|
this._state = {
|
|
395
398
|
id,
|
|
@@ -403,11 +406,26 @@ class Tab {
|
|
|
403
406
|
adBlockingEnabled: options?.adBlockingEnabled ?? true,
|
|
404
407
|
role: options?.role
|
|
405
408
|
};
|
|
406
|
-
this.view.webContents.on("before-input-event", (
|
|
409
|
+
this.view.webContents.on("before-input-event", (event, input) => {
|
|
407
410
|
if (!input.control && !input.meta) return;
|
|
408
411
|
if (input.type !== "keyDown") return;
|
|
409
412
|
const key = input.key.toLowerCase();
|
|
410
413
|
const wc = this.view.webContents;
|
|
414
|
+
if (key === "+" || key === "=") {
|
|
415
|
+
this.zoomIn();
|
|
416
|
+
event.preventDefault();
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
if (key === "-") {
|
|
420
|
+
this.zoomOut();
|
|
421
|
+
event.preventDefault();
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
if (key === "0") {
|
|
425
|
+
this.zoomReset();
|
|
426
|
+
event.preventDefault();
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
411
429
|
if (key === "c") wc.copy();
|
|
412
430
|
else if (key === "v") wc.paste();
|
|
413
431
|
else if (key === "x") wc.cut();
|
|
@@ -428,7 +446,7 @@ class Tab {
|
|
|
428
446
|
wc.setWindowOpenHandler(({ url, disposition }) => {
|
|
429
447
|
const error = this.getNavigationBlockReason(url);
|
|
430
448
|
if (error) {
|
|
431
|
-
logger$
|
|
449
|
+
logger$j.warn(error);
|
|
432
450
|
return { action: "deny" };
|
|
433
451
|
}
|
|
434
452
|
this.onOpenUrl?.({
|
|
@@ -442,7 +460,7 @@ class Tab {
|
|
|
442
460
|
const error = this.getNavigationBlockReason(url);
|
|
443
461
|
if (!error) return;
|
|
444
462
|
event.preventDefault();
|
|
445
|
-
logger$
|
|
463
|
+
logger$j.warn(`${context}: ${error}`);
|
|
446
464
|
};
|
|
447
465
|
wc.on("will-navigate", (event, url) => {
|
|
448
466
|
blockNavigation(event, url, "Blocked top-level navigation");
|
|
@@ -506,7 +524,7 @@ class Tab {
|
|
|
506
524
|
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 999px; }
|
|
507
525
|
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.22); }
|
|
508
526
|
::-webkit-scrollbar-corner { background: transparent; }
|
|
509
|
-
`).catch((err) => logger$
|
|
527
|
+
`).catch((err) => logger$j.warn("Failed to inject scrollbar CSS:", err));
|
|
510
528
|
});
|
|
511
529
|
wc.on("page-favicon-updated", (_, favicons) => {
|
|
512
530
|
this._state.favicon = favicons[0] || "";
|
|
@@ -529,7 +547,7 @@ class Tab {
|
|
|
529
547
|
).then((highlightedText) => {
|
|
530
548
|
this.buildContextMenu(wc, params, highlightedText.trim());
|
|
531
549
|
}).catch((err) => {
|
|
532
|
-
logger$
|
|
550
|
+
logger$j.warn("Failed to inspect highlighted text for context menu:", err);
|
|
533
551
|
this.buildContextMenu(wc, params, "");
|
|
534
552
|
});
|
|
535
553
|
});
|
|
@@ -675,6 +693,19 @@ class Tab {
|
|
|
675
693
|
reload() {
|
|
676
694
|
this.view.webContents.reload();
|
|
677
695
|
}
|
|
696
|
+
zoomIn() {
|
|
697
|
+
const wc = this.view.webContents;
|
|
698
|
+
const level = wc.getZoomLevel();
|
|
699
|
+
wc.setZoomLevel(level + 0.5);
|
|
700
|
+
}
|
|
701
|
+
zoomOut() {
|
|
702
|
+
const wc = this.view.webContents;
|
|
703
|
+
const level = wc.getZoomLevel();
|
|
704
|
+
wc.setZoomLevel(level - 0.5);
|
|
705
|
+
}
|
|
706
|
+
zoomReset() {
|
|
707
|
+
this.view.webContents.setZoomLevel(0);
|
|
708
|
+
}
|
|
678
709
|
setAdBlockingEnabled(enabled) {
|
|
679
710
|
if (this._state.adBlockingEnabled === enabled) return false;
|
|
680
711
|
this._state.adBlockingEnabled = enabled;
|
|
@@ -760,7 +791,7 @@ class Tab {
|
|
|
760
791
|
document.addEventListener('mouseup', window.__vesselHighlightHandler);
|
|
761
792
|
}
|
|
762
793
|
})()
|
|
763
|
-
`).catch((err) => logger$
|
|
794
|
+
`).catch((err) => logger$j.warn("Failed to inject highlight listener:", err));
|
|
764
795
|
} else {
|
|
765
796
|
void wc.executeJavaScript(`
|
|
766
797
|
(function() {
|
|
@@ -771,7 +802,7 @@ class Tab {
|
|
|
771
802
|
delete window.__vesselHighlightHandler;
|
|
772
803
|
}
|
|
773
804
|
})()
|
|
774
|
-
`).catch((err) => logger$
|
|
805
|
+
`).catch((err) => logger$j.warn("Failed to remove highlight listener:", err));
|
|
775
806
|
}
|
|
776
807
|
}
|
|
777
808
|
get webContentsId() {
|
|
@@ -782,7 +813,7 @@ class Tab {
|
|
|
782
813
|
this.view.webContents.close();
|
|
783
814
|
}
|
|
784
815
|
}
|
|
785
|
-
const logger$
|
|
816
|
+
const logger$i = createLogger("JsonPersistence");
|
|
786
817
|
function canUseSafeStorage() {
|
|
787
818
|
try {
|
|
788
819
|
return electron.safeStorage.isEncryptionAvailable();
|
|
@@ -847,7 +878,7 @@ function createDebouncedJsonPersistence({
|
|
|
847
878
|
data,
|
|
848
879
|
typeof data === "string" ? { encoding: "utf-8", mode: 384 } : { mode: 384 }
|
|
849
880
|
)
|
|
850
|
-
).catch((err) => logger$
|
|
881
|
+
).catch((err) => logger$i.error(`Failed to save ${logLabel}:`, err));
|
|
851
882
|
};
|
|
852
883
|
const schedule = () => {
|
|
853
884
|
saveDirty2 = true;
|
|
@@ -2335,13 +2366,7 @@ function destroySession(tabId) {
|
|
|
2335
2366
|
sessions.delete(tabId);
|
|
2336
2367
|
}
|
|
2337
2368
|
}
|
|
2338
|
-
|
|
2339
|
-
for (const session of sessions.values()) {
|
|
2340
|
-
session.destroy();
|
|
2341
|
-
}
|
|
2342
|
-
sessions.clear();
|
|
2343
|
-
}
|
|
2344
|
-
const logger$g = createLogger("TabManager");
|
|
2369
|
+
const logger$h = createLogger("TabManager");
|
|
2345
2370
|
class TabManager {
|
|
2346
2371
|
tabs = /* @__PURE__ */ new Map();
|
|
2347
2372
|
order = [];
|
|
@@ -2350,9 +2375,15 @@ class TabManager {
|
|
|
2350
2375
|
onStateChange;
|
|
2351
2376
|
highlightCaptureCallback = null;
|
|
2352
2377
|
pageLoadCallback = null;
|
|
2353
|
-
|
|
2378
|
+
closedTabs = [];
|
|
2379
|
+
MAX_CLOSED_TABS = 20;
|
|
2380
|
+
isPrivate;
|
|
2381
|
+
sessionPartition;
|
|
2382
|
+
constructor(window2, onStateChange, options) {
|
|
2354
2383
|
this.window = window2;
|
|
2355
2384
|
this.onStateChange = onStateChange;
|
|
2385
|
+
this.isPrivate = options?.isPrivate ?? false;
|
|
2386
|
+
this.sessionPartition = options?.sessionPartition ?? (this.isPrivate ? "private-mode" : void 0);
|
|
2356
2387
|
}
|
|
2357
2388
|
onPageLoad(cb) {
|
|
2358
2389
|
this.pageLoadCallback = cb;
|
|
@@ -2363,12 +2394,15 @@ class TabManager {
|
|
|
2363
2394
|
const tab = new Tab(id, url, () => this.broadcastState(), {
|
|
2364
2395
|
adBlockingEnabled: options?.adBlockingEnabled,
|
|
2365
2396
|
parentWindow: this.window,
|
|
2397
|
+
sessionPartition: this.sessionPartition,
|
|
2366
2398
|
onOpenUrl: ({ url: requestedUrl, background: background2, adBlockingEnabled }) => {
|
|
2367
2399
|
this.createTab(requestedUrl, { background: background2, adBlockingEnabled });
|
|
2368
2400
|
},
|
|
2369
2401
|
onPageLoad: (pageUrl, wc) => {
|
|
2370
2402
|
this.reapplyHighlights(pageUrl, wc);
|
|
2371
|
-
|
|
2403
|
+
if (!this.isPrivate) {
|
|
2404
|
+
addEntry$1(pageUrl, wc.getTitle());
|
|
2405
|
+
}
|
|
2372
2406
|
this.pageLoadCallback?.(pageUrl, wc);
|
|
2373
2407
|
},
|
|
2374
2408
|
onHighlightSelection: (wc) => this.captureHighlightFromPage(wc),
|
|
@@ -2400,6 +2434,14 @@ class TabManager {
|
|
|
2400
2434
|
closeTab(id) {
|
|
2401
2435
|
const tab = this.tabs.get(id);
|
|
2402
2436
|
if (!tab) return;
|
|
2437
|
+
this.closedTabs.push({
|
|
2438
|
+
url: tab.state.url,
|
|
2439
|
+
title: tab.state.title,
|
|
2440
|
+
adBlockingEnabled: tab.state.adBlockingEnabled
|
|
2441
|
+
});
|
|
2442
|
+
if (this.closedTabs.length > this.MAX_CLOSED_TABS) {
|
|
2443
|
+
this.closedTabs.shift();
|
|
2444
|
+
}
|
|
2403
2445
|
const wcId = tab.webContentsId;
|
|
2404
2446
|
if (wcId !== void 0) {
|
|
2405
2447
|
this.lastReapply.delete(wcId);
|
|
@@ -2433,6 +2475,25 @@ class TabManager {
|
|
|
2433
2475
|
reloadTab(id) {
|
|
2434
2476
|
this.tabs.get(id)?.reload();
|
|
2435
2477
|
}
|
|
2478
|
+
zoomIn(id) {
|
|
2479
|
+
this.tabs.get(id)?.zoomIn();
|
|
2480
|
+
}
|
|
2481
|
+
zoomOut(id) {
|
|
2482
|
+
this.tabs.get(id)?.zoomOut();
|
|
2483
|
+
}
|
|
2484
|
+
zoomReset(id) {
|
|
2485
|
+
this.tabs.get(id)?.zoomReset();
|
|
2486
|
+
}
|
|
2487
|
+
reopenClosedTab() {
|
|
2488
|
+
const last = this.closedTabs.pop();
|
|
2489
|
+
if (!last) return null;
|
|
2490
|
+
return this.createTab(last.url, { adBlockingEnabled: last.adBlockingEnabled });
|
|
2491
|
+
}
|
|
2492
|
+
duplicateTab(id) {
|
|
2493
|
+
const tab = this.tabs.get(id);
|
|
2494
|
+
if (!tab) return null;
|
|
2495
|
+
return this.createTab(tab.state.url, { adBlockingEnabled: tab.state.adBlockingEnabled });
|
|
2496
|
+
}
|
|
2436
2497
|
getActiveTab() {
|
|
2437
2498
|
return this.activeTabId ? this.tabs.get(this.activeTabId) : void 0;
|
|
2438
2499
|
}
|
|
@@ -2506,10 +2567,10 @@ class TabManager {
|
|
|
2506
2567
|
return ids;
|
|
2507
2568
|
}
|
|
2508
2569
|
destroyAllTabs() {
|
|
2509
|
-
|
|
2510
|
-
for (const id of this.order) {
|
|
2570
|
+
for (const id of [...this.order]) {
|
|
2511
2571
|
const tab = this.tabs.get(id);
|
|
2512
2572
|
if (!tab) continue;
|
|
2573
|
+
destroySession(id);
|
|
2513
2574
|
this.window.contentView.removeChildView(tab.view);
|
|
2514
2575
|
tab.destroy();
|
|
2515
2576
|
}
|
|
@@ -2535,7 +2596,7 @@ class TabManager {
|
|
|
2535
2596
|
}));
|
|
2536
2597
|
if (entries.length > 0) {
|
|
2537
2598
|
void highlightBatchOnPage(wc, entries).catch(
|
|
2538
|
-
(err) => logger$
|
|
2599
|
+
(err) => logger$h.warn("Failed to batch highlight:", err)
|
|
2539
2600
|
);
|
|
2540
2601
|
}
|
|
2541
2602
|
}
|
|
@@ -2557,12 +2618,12 @@ class TabManager {
|
|
|
2557
2618
|
const result = await captureSelectionHighlight(wc);
|
|
2558
2619
|
if (result.success && result.text) {
|
|
2559
2620
|
await highlightOnPage(wc, null, result.text, void 0, void 0, "yellow").catch(
|
|
2560
|
-
(err) => logger$
|
|
2621
|
+
(err) => logger$h.warn("Failed to capture highlight:", err)
|
|
2561
2622
|
);
|
|
2562
2623
|
}
|
|
2563
2624
|
this.highlightCaptureCallback?.(result);
|
|
2564
2625
|
} catch (err) {
|
|
2565
|
-
logger$
|
|
2626
|
+
logger$h.warn("Failed to capture highlight from page:", err);
|
|
2566
2627
|
this.highlightCaptureCallback?.({
|
|
2567
2628
|
success: false,
|
|
2568
2629
|
message: "Could not capture selection"
|
|
@@ -2587,7 +2648,7 @@ class TabManager {
|
|
|
2587
2648
|
void this.removeHighlightMarksForText(wc, text);
|
|
2588
2649
|
}
|
|
2589
2650
|
} catch (err) {
|
|
2590
|
-
logger$
|
|
2651
|
+
logger$h.warn("Failed to remove highlight from matching tab:", err);
|
|
2591
2652
|
}
|
|
2592
2653
|
}
|
|
2593
2654
|
this.highlightCaptureCallback?.({
|
|
@@ -2618,12 +2679,12 @@ class TabManager {
|
|
|
2618
2679
|
void 0,
|
|
2619
2680
|
color
|
|
2620
2681
|
).catch(
|
|
2621
|
-
(err) => logger$
|
|
2682
|
+
(err) => logger$h.warn("Failed to update highlight color:", err)
|
|
2622
2683
|
);
|
|
2623
2684
|
});
|
|
2624
2685
|
}
|
|
2625
2686
|
} catch (err) {
|
|
2626
|
-
logger$
|
|
2687
|
+
logger$h.warn("Failed to iterate highlights for color change:", err);
|
|
2627
2688
|
}
|
|
2628
2689
|
}
|
|
2629
2690
|
this.highlightCaptureCallback?.({
|
|
@@ -2645,7 +2706,7 @@ class TabManager {
|
|
|
2645
2706
|
});
|
|
2646
2707
|
})()`
|
|
2647
2708
|
).catch(
|
|
2648
|
-
(err) => logger$
|
|
2709
|
+
(err) => logger$h.warn("Failed to remove highlight marks:", err)
|
|
2649
2710
|
);
|
|
2650
2711
|
}
|
|
2651
2712
|
broadcastState() {
|
|
@@ -2732,6 +2793,17 @@ const Channels = {
|
|
|
2732
2793
|
DEVTOOLS_PANEL_RESIZE: "devtools-panel:resize",
|
|
2733
2794
|
// Ad blocking
|
|
2734
2795
|
TAB_TOGGLE_AD_BLOCK: "tab:toggle-ad-block",
|
|
2796
|
+
// Zoom
|
|
2797
|
+
TAB_ZOOM_IN: "tab:zoom-in",
|
|
2798
|
+
TAB_ZOOM_OUT: "tab:zoom-out",
|
|
2799
|
+
TAB_ZOOM_RESET: "tab:zoom-reset",
|
|
2800
|
+
// Closed tabs / duplication
|
|
2801
|
+
TAB_REOPEN_CLOSED: "tab:reopen-closed",
|
|
2802
|
+
TAB_DUPLICATE: "tab:duplicate",
|
|
2803
|
+
TAB_CONTEXT_MENU: "tab:context-menu",
|
|
2804
|
+
// Private browsing
|
|
2805
|
+
OPEN_PRIVATE_WINDOW: "private:open-window",
|
|
2806
|
+
IS_PRIVATE_MODE: "private:is-private",
|
|
2735
2807
|
// Find in page
|
|
2736
2808
|
FIND_IN_PAGE_START: "find:start",
|
|
2737
2809
|
FIND_IN_PAGE_NEXT: "find:next",
|
|
@@ -3749,11 +3821,29 @@ function errorResult(error, value) {
|
|
|
3749
3821
|
function getErrorMessage(error, fallback = "Unknown error") {
|
|
3750
3822
|
return error instanceof Error && error.message ? error.message : fallback;
|
|
3751
3823
|
}
|
|
3752
|
-
const logger$
|
|
3824
|
+
const logger$g = createLogger("Premium");
|
|
3753
3825
|
const VERIFICATION_API = process.env.VESSEL_PREMIUM_API || "https://vesselpremium.quantaintellect.com";
|
|
3754
3826
|
const FREE_TOOL_ITERATION_LIMIT = 50;
|
|
3755
3827
|
const REVALIDATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
3756
3828
|
const OFFLINE_GRACE_PERIOD_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
3829
|
+
const MAX_API_ERROR_LOG_LENGTH = 300;
|
|
3830
|
+
async function readApiErrorDetail(res) {
|
|
3831
|
+
try {
|
|
3832
|
+
const text = (await res.text()).trim();
|
|
3833
|
+
if (!text) return "";
|
|
3834
|
+
try {
|
|
3835
|
+
const data = JSON.parse(text);
|
|
3836
|
+
const detail = data.error || data.message;
|
|
3837
|
+
if (typeof detail === "string" && detail.trim()) {
|
|
3838
|
+
return detail.trim().slice(0, MAX_API_ERROR_LOG_LENGTH);
|
|
3839
|
+
}
|
|
3840
|
+
} catch {
|
|
3841
|
+
}
|
|
3842
|
+
return text.slice(0, MAX_API_ERROR_LOG_LENGTH);
|
|
3843
|
+
} catch {
|
|
3844
|
+
return "";
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3757
3847
|
const PREMIUM_TOOLS = /* @__PURE__ */ new Set([
|
|
3758
3848
|
"screenshot",
|
|
3759
3849
|
"save_session",
|
|
@@ -3843,7 +3933,12 @@ async function verifySubscription(identifier) {
|
|
|
3843
3933
|
body: JSON.stringify({ identifier: verificationIdentifier })
|
|
3844
3934
|
});
|
|
3845
3935
|
if (!res.ok) {
|
|
3846
|
-
|
|
3936
|
+
const detail = await readApiErrorDetail(res);
|
|
3937
|
+
logger$g.warn(
|
|
3938
|
+
"Verification API returned a non-OK status:",
|
|
3939
|
+
res.status,
|
|
3940
|
+
detail
|
|
3941
|
+
);
|
|
3847
3942
|
return current;
|
|
3848
3943
|
}
|
|
3849
3944
|
const data = await res.json();
|
|
@@ -3858,7 +3953,7 @@ async function verifySubscription(identifier) {
|
|
|
3858
3953
|
setSetting("premium", updated);
|
|
3859
3954
|
return updated;
|
|
3860
3955
|
} catch (err) {
|
|
3861
|
-
logger$
|
|
3956
|
+
logger$g.warn("Verification failed:", err);
|
|
3862
3957
|
return current;
|
|
3863
3958
|
}
|
|
3864
3959
|
}
|
|
@@ -4431,7 +4526,7 @@ const EXTRACT_TIMEOUT_MAX_MS = 2e4;
|
|
|
4431
4526
|
const MUTATION_CAPTURE_INTERVAL_MS = 5e3;
|
|
4432
4527
|
const MUTATION_SETTLE_AFTER_MS = 1500;
|
|
4433
4528
|
const AGENT_STREAM_IDLE_TIMEOUT_MS = 3e4;
|
|
4434
|
-
const logger$
|
|
4529
|
+
const logger$f = createLogger("Extractor");
|
|
4435
4530
|
const EMPTY_PAGE_CONTENT = {
|
|
4436
4531
|
title: "",
|
|
4437
4532
|
content: "",
|
|
@@ -5181,7 +5276,7 @@ async function executeScript(webContents, script) {
|
|
|
5181
5276
|
})
|
|
5182
5277
|
]);
|
|
5183
5278
|
} catch (err) {
|
|
5184
|
-
logger$
|
|
5279
|
+
logger$f.warn("Failed to execute page script:", err);
|
|
5185
5280
|
return null;
|
|
5186
5281
|
} finally {
|
|
5187
5282
|
if (timer) {
|
|
@@ -5288,7 +5383,7 @@ async function estimateExtractionTimeout(webContents) {
|
|
|
5288
5383
|
return EXTRACT_TIMEOUT_BASE_MS + extra;
|
|
5289
5384
|
}
|
|
5290
5385
|
} catch (err) {
|
|
5291
|
-
logger$
|
|
5386
|
+
logger$f.warn("Failed to estimate extraction timeout, using base timeout:", err);
|
|
5292
5387
|
}
|
|
5293
5388
|
return EXTRACT_TIMEOUT_BASE_MS;
|
|
5294
5389
|
}
|
|
@@ -5599,7 +5694,7 @@ function enableClipboardShortcuts(view) {
|
|
|
5599
5694
|
}
|
|
5600
5695
|
});
|
|
5601
5696
|
}
|
|
5602
|
-
const CHROME_HEIGHT = 110;
|
|
5697
|
+
const CHROME_HEIGHT$1 = 110;
|
|
5603
5698
|
const DEFAULT_DEVTOOLS_PANEL_HEIGHT = 250;
|
|
5604
5699
|
const MIN_DEVTOOLS_PANEL = 120;
|
|
5605
5700
|
const MAX_DEVTOOLS_PANEL = 600;
|
|
@@ -5814,7 +5909,7 @@ function layoutViews(state2) {
|
|
|
5814
5909
|
uiState
|
|
5815
5910
|
} = state2;
|
|
5816
5911
|
const [width, height] = mainWindow.getContentSize();
|
|
5817
|
-
const chromeHeight = uiState.focusMode ? 0 : CHROME_HEIGHT;
|
|
5912
|
+
const chromeHeight = uiState.focusMode ? 0 : CHROME_HEIGHT$1;
|
|
5818
5913
|
const sidebarWidth = uiState.sidebarOpen ? uiState.sidebarWidth : 0;
|
|
5819
5914
|
const devtoolsHeight = uiState.devtoolsPanelOpen ? uiState.devtoolsPanelHeight : 0;
|
|
5820
5915
|
const chromeNeedsFullHeight = uiState.settingsOpen;
|
|
@@ -5864,7 +5959,7 @@ function layoutViews(state2) {
|
|
|
5864
5959
|
function resizeSidebarViews(state2) {
|
|
5865
5960
|
const { mainWindow, sidebarView, devtoolsPanelView, tabManager, uiState } = state2;
|
|
5866
5961
|
const [width, height] = mainWindow.getContentSize();
|
|
5867
|
-
const chromeHeight = uiState.focusMode ? 0 : CHROME_HEIGHT;
|
|
5962
|
+
const chromeHeight = uiState.focusMode ? 0 : CHROME_HEIGHT$1;
|
|
5868
5963
|
const sidebarWidth = uiState.sidebarOpen ? uiState.sidebarWidth : 0;
|
|
5869
5964
|
const devtoolsHeight = uiState.devtoolsPanelOpen ? uiState.devtoolsPanelHeight : 0;
|
|
5870
5965
|
const resizeHandleOverlap = 6;
|
|
@@ -6326,7 +6421,7 @@ class AnthropicProvider {
|
|
|
6326
6421
|
});
|
|
6327
6422
|
continue;
|
|
6328
6423
|
}
|
|
6329
|
-
const argSummary = [tb.input.url, tb.input.text, tb.input.direction].map((v) => typeof v === "string" ? v : "").find((v) => v.length > 0) ?? "";
|
|
6424
|
+
const argSummary = [tb.input.url, tb.input.query, tb.input.text, tb.input.direction].map((v) => typeof v === "string" ? v : "").find((v) => v.length > 0) ?? "";
|
|
6330
6425
|
onChunk(`
|
|
6331
6426
|
<<tool:${tb.name}${argSummary ? ":" + argSummary : ""}>>
|
|
6332
6427
|
`);
|
|
@@ -6517,11 +6612,52 @@ const SAFE_TOOL_ALIASES = {
|
|
|
6517
6612
|
scroll_up: "scroll",
|
|
6518
6613
|
read: "read_page",
|
|
6519
6614
|
read_current_page: "read_page",
|
|
6520
|
-
scan_page: "read_page"
|
|
6615
|
+
scan_page: "read_page",
|
|
6616
|
+
save_bookmark: "save_bookmark",
|
|
6617
|
+
bookmark: "save_bookmark",
|
|
6618
|
+
bookmark_page: "save_bookmark",
|
|
6619
|
+
bookmark_url: "save_bookmark",
|
|
6620
|
+
add_bookmark: "save_bookmark",
|
|
6621
|
+
create_bookmark: "save_bookmark"
|
|
6521
6622
|
};
|
|
6623
|
+
const CANONICAL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
6624
|
+
"archive_bookmark",
|
|
6625
|
+
"click",
|
|
6626
|
+
"create_bookmark_folder",
|
|
6627
|
+
"current_tab",
|
|
6628
|
+
"go_back",
|
|
6629
|
+
"go_forward",
|
|
6630
|
+
"inspect_element",
|
|
6631
|
+
"list_bookmarks",
|
|
6632
|
+
"navigate",
|
|
6633
|
+
"open_bookmark",
|
|
6634
|
+
"organize_bookmark",
|
|
6635
|
+
"read_page",
|
|
6636
|
+
"save_bookmark",
|
|
6637
|
+
"scroll",
|
|
6638
|
+
"search",
|
|
6639
|
+
"type_text"
|
|
6640
|
+
]);
|
|
6641
|
+
function repeatedTokenMatch(value, token) {
|
|
6642
|
+
if (value === token) return true;
|
|
6643
|
+
if (token.length === 0 || value.length <= token.length) return false;
|
|
6644
|
+
if (value.length % token.length !== 0) return false;
|
|
6645
|
+
return token.repeat(value.length / token.length) === value;
|
|
6646
|
+
}
|
|
6522
6647
|
function normalizeToolAlias(name) {
|
|
6523
6648
|
const normalized = name.trim().toLowerCase().replace(/[.\s/-]+/g, "_");
|
|
6524
|
-
|
|
6649
|
+
const direct = SAFE_TOOL_ALIASES[normalized] ?? normalized;
|
|
6650
|
+
if (CANONICAL_TOOL_NAMES.has(direct)) return direct;
|
|
6651
|
+
const knownTokens = [
|
|
6652
|
+
...Object.keys(SAFE_TOOL_ALIASES),
|
|
6653
|
+
...CANONICAL_TOOL_NAMES
|
|
6654
|
+
];
|
|
6655
|
+
for (const token of knownTokens) {
|
|
6656
|
+
if (repeatedTokenMatch(normalized, token)) {
|
|
6657
|
+
return SAFE_TOOL_ALIASES[token] ?? token;
|
|
6658
|
+
}
|
|
6659
|
+
}
|
|
6660
|
+
return name;
|
|
6525
6661
|
}
|
|
6526
6662
|
function parseModelSizeInBillions(model) {
|
|
6527
6663
|
const match = model.toLowerCase().match(/(?:^|[:/_\-\s])(\d+(?:\.\d+)?)b(?:$|[:/_\-\s])/i);
|
|
@@ -6553,7 +6689,7 @@ const MAX_MCP_NAV_CONTENT_LENGTH = 3e4;
|
|
|
6553
6689
|
const MAX_AGENT_DEBUG_CONTENT_LENGTH = 2e4;
|
|
6554
6690
|
const LLAMA_CPP_MIN_CTX_TOKENS = 16384;
|
|
6555
6691
|
const LLAMA_CPP_RECOMMENDED_CTX_TOKENS = 32768;
|
|
6556
|
-
const logger$
|
|
6692
|
+
const logger$e = createLogger("OpenAIProvider");
|
|
6557
6693
|
function shouldDebugAgentLoop() {
|
|
6558
6694
|
const value = process.env.VESSEL_DEBUG_AGENT_LOOP;
|
|
6559
6695
|
return value === "1" || value === "true";
|
|
@@ -6845,8 +6981,62 @@ function scalarArgsForTool(name, scalar) {
|
|
|
6845
6981
|
const mode = trimmed.replace(/^["']|["']$/g, "").toLowerCase();
|
|
6846
6982
|
if (mode) return { mode };
|
|
6847
6983
|
}
|
|
6984
|
+
if (name === "save_bookmark") {
|
|
6985
|
+
const url = toLikelyUrl(trimmed);
|
|
6986
|
+
if (url) return { url };
|
|
6987
|
+
const lastSpace = trimmed.lastIndexOf(" ");
|
|
6988
|
+
if (lastSpace > 0) {
|
|
6989
|
+
const maybeUrl = toLikelyUrl(trimmed.slice(lastSpace + 1));
|
|
6990
|
+
if (maybeUrl) return { url: maybeUrl, title: trimmed.slice(0, lastSpace).replace(/^["']|["']$/g, "") };
|
|
6991
|
+
}
|
|
6992
|
+
}
|
|
6993
|
+
return null;
|
|
6994
|
+
}
|
|
6995
|
+
function firstStringArg(args, keys) {
|
|
6996
|
+
for (const key of keys) {
|
|
6997
|
+
const value = args[key];
|
|
6998
|
+
if (typeof value === "string" && value.trim()) {
|
|
6999
|
+
return value.trim();
|
|
7000
|
+
}
|
|
7001
|
+
}
|
|
6848
7002
|
return null;
|
|
6849
7003
|
}
|
|
7004
|
+
function normalizeElementTargetArgs(args) {
|
|
7005
|
+
const normalized = { ...args };
|
|
7006
|
+
if (typeof normalized.index === "string" && /^\d+$/.test(normalized.index.trim())) {
|
|
7007
|
+
normalized.index = Number(normalized.index.trim());
|
|
7008
|
+
}
|
|
7009
|
+
if (typeof normalized.selector !== "string" || !normalized.selector.trim()) {
|
|
7010
|
+
const selector = firstStringArg(normalized, [
|
|
7011
|
+
"cssSelector",
|
|
7012
|
+
"css_selector",
|
|
7013
|
+
"querySelector",
|
|
7014
|
+
"query_selector"
|
|
7015
|
+
]);
|
|
7016
|
+
if (selector) normalized.selector = selector;
|
|
7017
|
+
}
|
|
7018
|
+
if (typeof normalized.text !== "string" || !normalized.text.trim()) {
|
|
7019
|
+
const text = firstStringArg(normalized, [
|
|
7020
|
+
"label",
|
|
7021
|
+
"title",
|
|
7022
|
+
"name",
|
|
7023
|
+
"target",
|
|
7024
|
+
"element",
|
|
7025
|
+
"linkText",
|
|
7026
|
+
"link_text",
|
|
7027
|
+
"ariaLabel",
|
|
7028
|
+
"aria_label"
|
|
7029
|
+
]);
|
|
7030
|
+
if (text) normalized.text = text;
|
|
7031
|
+
}
|
|
7032
|
+
return normalized;
|
|
7033
|
+
}
|
|
7034
|
+
function hasElementTarget(args) {
|
|
7035
|
+
return typeof args.index === "number" || typeof args.selector === "string" && args.selector.trim().length > 0 || typeof args.text === "string" && args.text.trim().length > 0;
|
|
7036
|
+
}
|
|
7037
|
+
function isTargetlessClickArgs(args) {
|
|
7038
|
+
return !hasElementTarget(normalizeElementTargetArgs(args));
|
|
7039
|
+
}
|
|
6850
7040
|
function tryParseJsonWithCommonRepairs(raw) {
|
|
6851
7041
|
const trimmed = raw.trim();
|
|
6852
7042
|
if (!trimmed) return {};
|
|
@@ -6900,7 +7090,10 @@ function parseToolArgsWithRepair(name, argsJson) {
|
|
|
6900
7090
|
return scalarArgs ? { args: scalarArgs, repaired: true } : null;
|
|
6901
7091
|
}
|
|
6902
7092
|
function coerceToolArgsForExecution(name, args) {
|
|
6903
|
-
|
|
7093
|
+
let coerced = { ...args };
|
|
7094
|
+
if (name === "click" || name === "inspect_element" || name === "scroll_to_element") {
|
|
7095
|
+
coerced = normalizeElementTargetArgs(coerced);
|
|
7096
|
+
}
|
|
6904
7097
|
if (name === "search") {
|
|
6905
7098
|
if (typeof coerced.query !== "string" || !coerced.query.trim()) {
|
|
6906
7099
|
if (typeof coerced.text === "string" && coerced.text.trim()) {
|
|
@@ -6921,6 +7114,25 @@ function coerceToolArgsForExecution(name, args) {
|
|
|
6921
7114
|
}
|
|
6922
7115
|
}
|
|
6923
7116
|
}
|
|
7117
|
+
if (name === "save_bookmark") {
|
|
7118
|
+
if (typeof coerced.url !== "string" || !coerced.url.trim()) {
|
|
7119
|
+
if (typeof coerced.link === "string" && coerced.link.trim()) {
|
|
7120
|
+
coerced.url = coerced.link.trim();
|
|
7121
|
+
} else if (typeof coerced.href === "string" && coerced.href.trim()) {
|
|
7122
|
+
coerced.url = coerced.href.trim();
|
|
7123
|
+
}
|
|
7124
|
+
}
|
|
7125
|
+
if (typeof coerced.folderName !== "string" || !coerced.folderName.trim()) {
|
|
7126
|
+
if (typeof coerced.folder === "string" && coerced.folder.trim()) {
|
|
7127
|
+
coerced.folderName = coerced.folder.trim();
|
|
7128
|
+
} else if (typeof coerced.category === "string" && coerced.category.trim()) {
|
|
7129
|
+
coerced.folderName = coerced.category.trim();
|
|
7130
|
+
}
|
|
7131
|
+
}
|
|
7132
|
+
if (coerced.folderName && typeof coerced.createFolderIfMissing === "undefined") {
|
|
7133
|
+
coerced.createFolderIfMissing = true;
|
|
7134
|
+
}
|
|
7135
|
+
}
|
|
6924
7136
|
return coerced;
|
|
6925
7137
|
}
|
|
6926
7138
|
function canonicalizeArgsForTool(name, args) {
|
|
@@ -6937,6 +7149,24 @@ function canonicalizeArgsForTool(name, args) {
|
|
|
6937
7149
|
}
|
|
6938
7150
|
return canonical;
|
|
6939
7151
|
}
|
|
7152
|
+
function unsupportedToolHint(name) {
|
|
7153
|
+
const normalized = name.trim().toLowerCase().replace(/[.\s/-]+/g, "_");
|
|
7154
|
+
const BOOKMARK_NAMES = [
|
|
7155
|
+
"organize_bookmark",
|
|
7156
|
+
"organize_bookmarks",
|
|
7157
|
+
"manage_bookmark",
|
|
7158
|
+
"manage_bookmarks",
|
|
7159
|
+
"add_to_bookmarks",
|
|
7160
|
+
"save_to_bookmarks",
|
|
7161
|
+
"bookmark_link",
|
|
7162
|
+
"save_link",
|
|
7163
|
+
"store_bookmark"
|
|
7164
|
+
];
|
|
7165
|
+
if (BOOKMARK_NAMES.includes(normalized) || /bookmark|save.*link|organize/.test(normalized)) {
|
|
7166
|
+
return `Error: "${name}" is not a supported tool. Use save_bookmark to save a page as a bookmark, or create_bookmark_folder to create a folder. Example: save_bookmark with {"url": "...", "title": "...", "folderName": "..."}`;
|
|
7167
|
+
}
|
|
7168
|
+
return `Error: ${name} is not a supported tool. Choose one of the available browser tools instead.`;
|
|
7169
|
+
}
|
|
6940
7170
|
function resolveToolCallName(rawName, args, availableToolNames) {
|
|
6941
7171
|
const aliased = normalizeToolAlias(rawName);
|
|
6942
7172
|
if (availableToolNames.has(aliased)) return aliased;
|
|
@@ -6960,9 +7190,9 @@ function resolveToolCallName(rawName, args, availableToolNames) {
|
|
|
6960
7190
|
function logAgentLoopDebug(payload) {
|
|
6961
7191
|
if (!shouldDebugAgentLoop()) return;
|
|
6962
7192
|
try {
|
|
6963
|
-
logger$
|
|
7193
|
+
logger$e.info(`[agent-debug] ${JSON.stringify(payload)}`);
|
|
6964
7194
|
} catch (err) {
|
|
6965
|
-
logger$
|
|
7195
|
+
logger$e.warn("Failed to serialize debug payload:", err);
|
|
6966
7196
|
}
|
|
6967
7197
|
}
|
|
6968
7198
|
function recoverTextEncodedToolCalls(text, availableToolNames) {
|
|
@@ -7241,6 +7471,7 @@ class OpenAICompatProvider {
|
|
|
7241
7471
|
);
|
|
7242
7472
|
if (recoveredToolCalls.length > 0) {
|
|
7243
7473
|
toolCalls = recoveredToolCalls;
|
|
7474
|
+
if (textAccum.trim()) onChunk("<<erase_prev>>");
|
|
7244
7475
|
} else {
|
|
7245
7476
|
const narratedToolCalls = recoverNarratedActionToolCalls(
|
|
7246
7477
|
textAccum,
|
|
@@ -7248,6 +7479,7 @@ class OpenAICompatProvider {
|
|
|
7248
7479
|
);
|
|
7249
7480
|
if (narratedToolCalls.length > 0) {
|
|
7250
7481
|
toolCalls = narratedToolCalls;
|
|
7482
|
+
if (textAccum.trim()) onChunk("<<erase_prev>>");
|
|
7251
7483
|
}
|
|
7252
7484
|
}
|
|
7253
7485
|
}
|
|
@@ -7314,6 +7546,25 @@ class OpenAICompatProvider {
|
|
|
7314
7546
|
compactRecoveryCount = 0;
|
|
7315
7547
|
const iterationToolResultPreviews = [];
|
|
7316
7548
|
for (const tc of toolCalls) {
|
|
7549
|
+
if (!availableToolNames.has(tc.name)) {
|
|
7550
|
+
const hint = unsupportedToolHint(tc.name);
|
|
7551
|
+
onChunk(`
|
|
7552
|
+
<<tool:${tc.name}:⚠ unsupported>>
|
|
7553
|
+
`);
|
|
7554
|
+
messages.push({
|
|
7555
|
+
role: "tool",
|
|
7556
|
+
tool_call_id: tc.id,
|
|
7557
|
+
content: hint
|
|
7558
|
+
});
|
|
7559
|
+
compactCorrectionCount += 1;
|
|
7560
|
+
if (compactCorrectionCount >= 2) {
|
|
7561
|
+
messages.push({
|
|
7562
|
+
role: "user",
|
|
7563
|
+
content: `[System] You are calling unsupported tools. Stop inventing tool names. Use the supported tools you were given and take the next concrete step.`
|
|
7564
|
+
});
|
|
7565
|
+
}
|
|
7566
|
+
continue;
|
|
7567
|
+
}
|
|
7317
7568
|
if (malformedToolCalls.has(tc.id)) {
|
|
7318
7569
|
onChunk(`
|
|
7319
7570
|
<<tool:${tc.name}:⚠ invalid args>>
|
|
@@ -7340,25 +7591,23 @@ class OpenAICompatProvider {
|
|
|
7340
7591
|
}
|
|
7341
7592
|
args = repairedArgs.args;
|
|
7342
7593
|
args = coerceToolArgsForExecution(tc.name, args);
|
|
7343
|
-
|
|
7594
|
+
const toolSignature = stableToolSignature(tc.name, args);
|
|
7595
|
+
if (this.agentToolProfile === "compact" && tc.name === "click" && isTargetlessClickArgs(args)) {
|
|
7344
7596
|
onChunk(`
|
|
7345
|
-
<<tool
|
|
7597
|
+
<<tool:${tc.name}:⚠ missing target>>
|
|
7346
7598
|
`);
|
|
7347
7599
|
messages.push({
|
|
7348
7600
|
role: "tool",
|
|
7349
7601
|
tool_call_id: tc.id,
|
|
7350
|
-
content: `Error:
|
|
7602
|
+
content: `Error: click requires an element target. Use click with {"index": N} from the latest read_page result, or {"text": "exact visible link/button text"}. If you do not have a current result index, call read_page(mode="results_only") first and then click one listed result.`
|
|
7603
|
+
});
|
|
7604
|
+
messages.push({
|
|
7605
|
+
role: "user",
|
|
7606
|
+
content: `[System] Your last click had no target. Do not call click with empty arguments. Refresh the page state with read_page(mode="results_only") if needed, then click exactly one result by index or exact visible text.`
|
|
7351
7607
|
});
|
|
7352
7608
|
compactCorrectionCount += 1;
|
|
7353
|
-
if (compactCorrectionCount >= 2) {
|
|
7354
|
-
messages.push({
|
|
7355
|
-
role: "user",
|
|
7356
|
-
content: `[System] You are calling unsupported tools. Stop inventing tool names. Use the supported tools you were given and take the next concrete step.`
|
|
7357
|
-
});
|
|
7358
|
-
}
|
|
7359
7609
|
continue;
|
|
7360
7610
|
}
|
|
7361
|
-
const toolSignature = stableToolSignature(tc.name, args);
|
|
7362
7611
|
const neverSuppressDuplicate = [
|
|
7363
7612
|
"read_page",
|
|
7364
7613
|
"current_tab",
|
|
@@ -7577,7 +7826,7 @@ function createProvider(config) {
|
|
|
7577
7826
|
return new OpenAICompatProvider(normalized);
|
|
7578
7827
|
}
|
|
7579
7828
|
const require$1 = node_module.createRequire(require("url").pathToFileURL(__filename).href);
|
|
7580
|
-
const logger$
|
|
7829
|
+
const logger$d = createLogger("DevTrace");
|
|
7581
7830
|
let cachedFactory;
|
|
7582
7831
|
function createNoopTraceSession() {
|
|
7583
7832
|
return {
|
|
@@ -7610,7 +7859,7 @@ function loadLocalFactory() {
|
|
|
7610
7859
|
return cachedFactory;
|
|
7611
7860
|
}
|
|
7612
7861
|
} catch (err) {
|
|
7613
|
-
logger$
|
|
7862
|
+
logger$d.warn("Failed to load local trace logger:", err);
|
|
7614
7863
|
}
|
|
7615
7864
|
}
|
|
7616
7865
|
return cachedFactory;
|
|
@@ -10579,6 +10828,14 @@ function pruneToolsForContext(tools, pageType, query = "", options = {}) {
|
|
|
10579
10828
|
return description !== tool.description ? { ...tool, description } : tool;
|
|
10580
10829
|
});
|
|
10581
10830
|
}
|
|
10831
|
+
const SEARCH_ENGINE_PRESETS = {
|
|
10832
|
+
duckduckgo: { label: "DuckDuckGo", url: "https://duckduckgo.com/?q=" },
|
|
10833
|
+
google: { label: "Google", url: "https://www.google.com/search?q=" },
|
|
10834
|
+
bing: { label: "Bing", url: "https://www.bing.com/search?q=" },
|
|
10835
|
+
brave: { label: "Brave Search", url: "https://search.brave.com/search?q=" },
|
|
10836
|
+
ecosia: { label: "Ecosia", url: "https://www.ecosia.org/search?q=" },
|
|
10837
|
+
kagi: { label: "Kagi", url: "https://kagi.com/search?q=" }
|
|
10838
|
+
};
|
|
10582
10839
|
function trimText(value) {
|
|
10583
10840
|
return typeof value === "string" ? value.trim() : "";
|
|
10584
10841
|
}
|
|
@@ -11417,7 +11674,7 @@ function formatDeadLinkMessage(label, result) {
|
|
|
11417
11674
|
const status = result.statusCode ? `HTTP ${result.statusCode}` : "dead link";
|
|
11418
11675
|
return `Skipped stale link "${label}" because ${destination} returned ${status}. Try a different link or URL instead.`;
|
|
11419
11676
|
}
|
|
11420
|
-
const logger$
|
|
11677
|
+
const logger$c = createLogger("Screenshot");
|
|
11421
11678
|
const SCREENSHOT_RETRY_COUNT = 3;
|
|
11422
11679
|
const SCREENSHOT_RETRY_BASE_DELAY_MS = 120;
|
|
11423
11680
|
async function captureScreenshot(wc) {
|
|
@@ -11439,7 +11696,7 @@ async function captureScreenshot(wc) {
|
|
|
11439
11696
|
}
|
|
11440
11697
|
}
|
|
11441
11698
|
} catch (err) {
|
|
11442
|
-
logger$
|
|
11699
|
+
logger$c.debug(
|
|
11443
11700
|
`capturePage attempt ${attempt + 1} failed; retrying if attempts remain.`,
|
|
11444
11701
|
getErrorMessage(err)
|
|
11445
11702
|
);
|
|
@@ -12313,7 +12570,7 @@ function buildHuggingFaceSearchShortcut(currentUrl, rawQuery) {
|
|
|
12313
12570
|
appliedFilters
|
|
12314
12571
|
};
|
|
12315
12572
|
}
|
|
12316
|
-
const logger$
|
|
12573
|
+
const logger$b = createLogger("PageActions");
|
|
12317
12574
|
function getBookmarkMetadataFromArgs(args) {
|
|
12318
12575
|
return normalizeBookmarkMetadata({
|
|
12319
12576
|
intent: args.intent ?? args.intent,
|
|
@@ -12499,7 +12756,7 @@ async function executePageScript(wc, script, options) {
|
|
|
12499
12756
|
return result;
|
|
12500
12757
|
} catch (err) {
|
|
12501
12758
|
const label = options?.label ? ` (${options.label})` : "";
|
|
12502
|
-
logger$
|
|
12759
|
+
logger$b.warn(`Failed to execute page script${label}:`, err);
|
|
12503
12760
|
return null;
|
|
12504
12761
|
} finally {
|
|
12505
12762
|
if (timer) {
|
|
@@ -12600,7 +12857,7 @@ Search results snapshot:
|
|
|
12600
12857
|
${truncated}`;
|
|
12601
12858
|
}
|
|
12602
12859
|
} catch (err) {
|
|
12603
|
-
logger$
|
|
12860
|
+
logger$b.warn("Failed to build post-search summary, falling back to nav summary:", err);
|
|
12604
12861
|
}
|
|
12605
12862
|
const fallback = await getPostNavSummary(wc);
|
|
12606
12863
|
return fallback ? `${fallback}
|
|
@@ -12623,7 +12880,7 @@ Page snapshot after navigation:
|
|
|
12623
12880
|
${truncated}`;
|
|
12624
12881
|
}
|
|
12625
12882
|
} catch (err) {
|
|
12626
|
-
logger$
|
|
12883
|
+
logger$b.warn("Failed to build post-click navigation summary:", err);
|
|
12627
12884
|
}
|
|
12628
12885
|
return "";
|
|
12629
12886
|
}
|
|
@@ -13117,7 +13374,7 @@ async function restoreLocaleSnapshot(wc, snapshot) {
|
|
|
13117
13374
|
}
|
|
13118
13375
|
}
|
|
13119
13376
|
} catch (err) {
|
|
13120
|
-
logger$
|
|
13377
|
+
logger$b.warn("Failed to restore locale via history navigation, trying URL reload fallback:", err);
|
|
13121
13378
|
}
|
|
13122
13379
|
if (snapshot.url && snapshot.url !== wc.getURL()) {
|
|
13123
13380
|
try {
|
|
@@ -13126,7 +13383,7 @@ async function restoreLocaleSnapshot(wc, snapshot) {
|
|
|
13126
13383
|
await waitForLoad(wc, 3e3);
|
|
13127
13384
|
return;
|
|
13128
13385
|
} catch (err) {
|
|
13129
|
-
logger$
|
|
13386
|
+
logger$b.warn("Failed to restore locale via safe URL load, trying page reload fallback:", err);
|
|
13130
13387
|
}
|
|
13131
13388
|
}
|
|
13132
13389
|
if (snapshot.url) {
|
|
@@ -13134,7 +13391,7 @@ async function restoreLocaleSnapshot(wc, snapshot) {
|
|
|
13134
13391
|
await wc.reload();
|
|
13135
13392
|
await waitForLoad(wc, 3e3);
|
|
13136
13393
|
} catch (err) {
|
|
13137
|
-
logger$
|
|
13394
|
+
logger$b.warn("Failed to restore locale via page reload:", err);
|
|
13138
13395
|
}
|
|
13139
13396
|
}
|
|
13140
13397
|
}
|
|
@@ -13486,7 +13743,7 @@ ${postActivationOverlayHint}`;
|
|
|
13486
13743
|
return `${clickText} -> ${hrefFallbackUrl} (recovered via href fallback)`;
|
|
13487
13744
|
}
|
|
13488
13745
|
} catch (err) {
|
|
13489
|
-
logger$
|
|
13746
|
+
logger$b.warn("Failed href fallback after click, returning generic click result:", err);
|
|
13490
13747
|
}
|
|
13491
13748
|
}
|
|
13492
13749
|
}
|
|
@@ -13531,7 +13788,7 @@ async function tryAutoDismissCartDialog(wc) {
|
|
|
13531
13788
|
return result;
|
|
13532
13789
|
}
|
|
13533
13790
|
} catch (err) {
|
|
13534
|
-
logger$
|
|
13791
|
+
logger$b.warn("Failed to auto-dismiss cart dialog, falling back to dialog actions:", err);
|
|
13535
13792
|
}
|
|
13536
13793
|
return null;
|
|
13537
13794
|
}
|
|
@@ -14966,8 +15223,19 @@ function buildCommonSearchUrlShortcut(currentUrl, rawQuery) {
|
|
|
14966
15223
|
appliedFilters: existingParam ? [`updated ${existingParam} query`] : []
|
|
14967
15224
|
};
|
|
14968
15225
|
}
|
|
14969
|
-
function
|
|
14970
|
-
|
|
15226
|
+
function buildDefaultEngineShortcut(rawQuery) {
|
|
15227
|
+
const settings2 = loadSettings();
|
|
15228
|
+
const engineId = settings2.defaultSearchEngine ?? "duckduckgo";
|
|
15229
|
+
if (engineId === "none") return null;
|
|
15230
|
+
const preset = SEARCH_ENGINE_PRESETS[engineId];
|
|
15231
|
+
if (!preset) return null;
|
|
15232
|
+
const query = normalizeSearchQuery(rawQuery);
|
|
15233
|
+
if (!query) return null;
|
|
15234
|
+
return {
|
|
15235
|
+
url: preset.url + encodeURIComponent(query),
|
|
15236
|
+
source: "default search engine",
|
|
15237
|
+
appliedFilters: []
|
|
15238
|
+
};
|
|
14971
15239
|
}
|
|
14972
15240
|
async function locateSearchTarget(wc, explicitSelector) {
|
|
14973
15241
|
if (explicitSelector) {
|
|
@@ -15020,7 +15288,7 @@ async function locateSearchTarget(wc, explicitSelector) {
|
|
|
15020
15288
|
const seen = new Set();
|
|
15021
15289
|
const ordered = [];
|
|
15022
15290
|
const specific = document.querySelectorAll(
|
|
15023
|
-
'input[type="search"], input[name="q"], input[name="query"], input[name="search"], input[role="searchbox"], input[aria-label*="search" i], input[placeholder*="search" i]'
|
|
15291
|
+
'input[type="search"], input[name="q"], input[name="query"], input[name="search"], input[role="searchbox"], input[aria-label*="search" i], input[placeholder*="search" i], textarea[name="q"], textarea[name="query"], textarea[name="search"], textarea[role="searchbox"], textarea[aria-label*="search" i], textarea[placeholder*="search" i]'
|
|
15024
15292
|
);
|
|
15025
15293
|
specific.forEach((el) => {
|
|
15026
15294
|
if (!seen.has(el)) {
|
|
@@ -15028,7 +15296,7 @@ async function locateSearchTarget(wc, explicitSelector) {
|
|
|
15028
15296
|
ordered.push(el);
|
|
15029
15297
|
}
|
|
15030
15298
|
});
|
|
15031
|
-
document.querySelectorAll('input[type="text"], input:not([type])').forEach((el) => {
|
|
15299
|
+
document.querySelectorAll('input[type="text"], input:not([type]), textarea').forEach((el) => {
|
|
15032
15300
|
if (seen.has(el)) return;
|
|
15033
15301
|
const scope = nearestSearchScope(el);
|
|
15034
15302
|
if (!scope) return;
|
|
@@ -15039,9 +15307,10 @@ async function locateSearchTarget(wc, explicitSelector) {
|
|
|
15039
15307
|
}
|
|
15040
15308
|
|
|
15041
15309
|
function scoreInput(el) {
|
|
15042
|
-
if (!(el instanceof HTMLInputElement)) return -1;
|
|
15310
|
+
if (!(el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) return -1;
|
|
15043
15311
|
if (isDisabled(el) || !isVisible(el)) return -1;
|
|
15044
|
-
const
|
|
15312
|
+
const isTextarea = el instanceof HTMLTextAreaElement;
|
|
15313
|
+
const type = isTextarea ? "text" : normalize(el.getAttribute("type") || el.type);
|
|
15045
15314
|
if (type && !["search", "text", ""].includes(type)) return -1;
|
|
15046
15315
|
|
|
15047
15316
|
let score = 0;
|
|
@@ -15188,16 +15457,19 @@ async function searchPage(wc, args) {
|
|
|
15188
15457
|
if (looksLikeCurrentSiteNameQuery(query, wc.getURL(), wc.getTitle() || "")) {
|
|
15189
15458
|
return `Error: "${query}" looks like the current site's name, not a product query. You are already on ${wc.getURL()}. Open a section like staff picks/new releases or search for actual book titles, authors, or genres instead.`;
|
|
15190
15459
|
}
|
|
15460
|
+
const runShortcut = async (shortcut) => {
|
|
15461
|
+
const beforeUrl2 = wc.getURL();
|
|
15462
|
+
await loadPermittedUrl(wc, shortcut.url);
|
|
15463
|
+
await waitForPotentialNavigation(wc, beforeUrl2, 4e3);
|
|
15464
|
+
const afterUrl2 = wc.getURL();
|
|
15465
|
+
const applied = shortcut.appliedFilters.length > 0 ? ` (${shortcut.appliedFilters.join(", ")})` : "";
|
|
15466
|
+
const destination = shortcut.section ? ` ${shortcut.section}` : "";
|
|
15467
|
+
return `Searched "${query}" via ${shortcut.source}${destination} shortcut${applied} → ${afterUrl2}${await getPostSearchSummary(wc)}`;
|
|
15468
|
+
};
|
|
15191
15469
|
if (typeof args.selector !== "string") {
|
|
15192
|
-
const shortcut =
|
|
15470
|
+
const shortcut = buildHuggingFaceSearchShortcut(wc.getURL(), query) ?? buildCommonSearchUrlShortcut(wc.getURL(), query);
|
|
15193
15471
|
if (shortcut) {
|
|
15194
|
-
|
|
15195
|
-
await loadPermittedUrl(wc, shortcut.url);
|
|
15196
|
-
await waitForPotentialNavigation(wc, beforeUrl2, 4e3);
|
|
15197
|
-
const afterUrl2 = wc.getURL();
|
|
15198
|
-
const applied = shortcut.appliedFilters.length > 0 ? ` (${shortcut.appliedFilters.join(", ")})` : "";
|
|
15199
|
-
const destination = shortcut.section ? ` ${shortcut.section}` : "";
|
|
15200
|
-
return `Searched "${query}" via ${shortcut.source}${destination} shortcut${applied} → ${afterUrl2}${await getPostSearchSummary(wc)}`;
|
|
15472
|
+
return runShortcut(shortcut);
|
|
15201
15473
|
}
|
|
15202
15474
|
}
|
|
15203
15475
|
const searchInfo = await locateSearchTarget(
|
|
@@ -15208,6 +15480,12 @@ async function searchPage(wc, args) {
|
|
|
15208
15480
|
return pageBusyError("search");
|
|
15209
15481
|
}
|
|
15210
15482
|
if (!searchInfo?.selector) {
|
|
15483
|
+
if (typeof args.selector !== "string") {
|
|
15484
|
+
const fallback = buildDefaultEngineShortcut(query);
|
|
15485
|
+
if (fallback) {
|
|
15486
|
+
return runShortcut(fallback);
|
|
15487
|
+
}
|
|
15488
|
+
}
|
|
15211
15489
|
return 'Error: Could not find a visible search input. Try read_page(mode="visible_only") or provide a selector.';
|
|
15212
15490
|
}
|
|
15213
15491
|
const fillResult = await setElementValue(wc, searchInfo.selector, query);
|
|
@@ -15780,7 +16058,7 @@ async function executeAction(name, args, ctx) {
|
|
|
15780
16058
|
)
|
|
15781
16059
|
]);
|
|
15782
16060
|
} catch (err) {
|
|
15783
|
-
logger$
|
|
16061
|
+
logger$b.warn("Failed to extract content for read_page, falling back to lighter recovery:", err);
|
|
15784
16062
|
content = null;
|
|
15785
16063
|
}
|
|
15786
16064
|
if (!content || content.content.length === 0) {
|
|
@@ -15797,12 +16075,12 @@ async function executeAction(name, args, ctx) {
|
|
|
15797
16075
|
new Promise((resolve) => setTimeout(() => resolve(null), 3e3))
|
|
15798
16076
|
]);
|
|
15799
16077
|
} catch (err) {
|
|
15800
|
-
logger$
|
|
16078
|
+
logger$b.warn("Failed to re-extract content after iframe consent dismissal:", err);
|
|
15801
16079
|
content = null;
|
|
15802
16080
|
}
|
|
15803
16081
|
}
|
|
15804
16082
|
} catch (err) {
|
|
15805
|
-
logger$
|
|
16083
|
+
logger$b.warn("Failed iframe consent dismissal during read_page recovery:", err);
|
|
15806
16084
|
}
|
|
15807
16085
|
}
|
|
15808
16086
|
if (content && content.content.length > 0) {
|
|
@@ -16215,7 +16493,7 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
|
|
|
16215
16493
|
try {
|
|
16216
16494
|
page = await extractContent(wc);
|
|
16217
16495
|
} catch (err) {
|
|
16218
|
-
logger$
|
|
16496
|
+
logger$b.warn("Failed to extract content for suggest:", err);
|
|
16219
16497
|
return "Could not read page. Try navigate to a working URL.";
|
|
16220
16498
|
}
|
|
16221
16499
|
const suggestions = [];
|
|
@@ -17546,7 +17824,7 @@ const ALGORITHM = "aes-256-gcm";
|
|
|
17546
17824
|
const IV_LENGTH = 12;
|
|
17547
17825
|
const AUTH_TAG_LENGTH = 16;
|
|
17548
17826
|
let cachedEntries = null;
|
|
17549
|
-
const logger$
|
|
17827
|
+
const logger$a = createLogger("Vault");
|
|
17550
17828
|
function getVaultDir() {
|
|
17551
17829
|
return electron.app.getPath("userData");
|
|
17552
17830
|
}
|
|
@@ -17613,7 +17891,7 @@ function loadVault() {
|
|
|
17613
17891
|
cachedEntries = JSON.parse(json);
|
|
17614
17892
|
return cachedEntries;
|
|
17615
17893
|
} catch (err) {
|
|
17616
|
-
logger$
|
|
17894
|
+
logger$a.error("Failed to load vault:", err);
|
|
17617
17895
|
throw new Error(
|
|
17618
17896
|
"Could not unlock the Agent Credential Vault. Check that OS secret storage is available and that the stored vault key can be decrypted."
|
|
17619
17897
|
);
|
|
@@ -17755,7 +18033,7 @@ async function requestConsent(request) {
|
|
|
17755
18033
|
}
|
|
17756
18034
|
const AUDIT_FILENAME = "vessel-vault-audit.jsonl";
|
|
17757
18035
|
const MAX_ENTRIES = 1e3;
|
|
17758
|
-
const logger$
|
|
18036
|
+
const logger$9 = createLogger("VaultAudit");
|
|
17759
18037
|
function getAuditPath() {
|
|
17760
18038
|
return path$1.join(electron.app.getPath("userData"), AUDIT_FILENAME);
|
|
17761
18039
|
}
|
|
@@ -17765,7 +18043,7 @@ function appendAuditEntry(entry) {
|
|
|
17765
18043
|
fs$1.mkdirSync(path$1.dirname(auditPath), { recursive: true });
|
|
17766
18044
|
fs$1.appendFileSync(auditPath, JSON.stringify(entry) + "\n");
|
|
17767
18045
|
} catch (err) {
|
|
17768
|
-
logger$
|
|
18046
|
+
logger$9.error("Failed to write audit log:", err);
|
|
17769
18047
|
}
|
|
17770
18048
|
}
|
|
17771
18049
|
function readAuditLog(limit = 100) {
|
|
@@ -17775,13 +18053,13 @@ function readAuditLog(limit = 100) {
|
|
|
17775
18053
|
const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
|
|
17776
18054
|
return lines.slice(-Math.min(limit, MAX_ENTRIES)).map((line) => JSON.parse(line)).reverse();
|
|
17777
18055
|
} catch (err) {
|
|
17778
|
-
logger$
|
|
18056
|
+
logger$9.error("Failed to read audit log:", err);
|
|
17779
18057
|
return [];
|
|
17780
18058
|
}
|
|
17781
18059
|
}
|
|
17782
18060
|
let httpServer = null;
|
|
17783
18061
|
let mcpAuthToken = null;
|
|
17784
|
-
const logger$
|
|
18062
|
+
const logger$8 = createLogger("MCP");
|
|
17785
18063
|
const MCP_AUTH_FILENAME = "mcp-auth.json";
|
|
17786
18064
|
function getMcpAuthFilePath() {
|
|
17787
18065
|
const configDir = process.env.VESSEL_CONFIG_DIR || path$1.join(
|
|
@@ -17817,7 +18095,7 @@ function writeMcpAuthFile(endpoint, token) {
|
|
|
17817
18095
|
{ mode: 384 }
|
|
17818
18096
|
);
|
|
17819
18097
|
} catch (err) {
|
|
17820
|
-
logger$
|
|
18098
|
+
logger$8.warn("Failed to write auth file:", err);
|
|
17821
18099
|
}
|
|
17822
18100
|
}
|
|
17823
18101
|
function clearMcpAuthFile() {
|
|
@@ -17842,7 +18120,7 @@ function clearMcpAuthFile() {
|
|
|
17842
18120
|
{ mode: 384 }
|
|
17843
18121
|
);
|
|
17844
18122
|
} catch (err) {
|
|
17845
|
-
logger$
|
|
18123
|
+
logger$8.warn("Failed to clear auth file:", err);
|
|
17846
18124
|
}
|
|
17847
18125
|
}
|
|
17848
18126
|
function asTextResponse(text) {
|
|
@@ -17932,7 +18210,7 @@ async function getPostActionState(tabManager, name) {
|
|
|
17932
18210
|
}
|
|
17933
18211
|
}
|
|
17934
18212
|
} catch (err) {
|
|
17935
|
-
logger$
|
|
18213
|
+
logger$8.warn("Failed to compute post-action state warning:", err);
|
|
17936
18214
|
}
|
|
17937
18215
|
return `${warning}
|
|
17938
18216
|
[state: url=${wc.getURL()}, canGoBack=${tab.canGoBack()}, canGoForward=${tab.canGoForward()}, loading=${wc.isLoading()}]`;
|
|
@@ -18034,7 +18312,7 @@ async function waitForConditionMcp(wc, text, selector, timeoutMs) {
|
|
|
18034
18312
|
}
|
|
18035
18313
|
})()
|
|
18036
18314
|
`).catch((err) => {
|
|
18037
|
-
logger$
|
|
18315
|
+
logger$8.warn("Failed to gather wait_for timeout diagnostic:", err);
|
|
18038
18316
|
return null;
|
|
18039
18317
|
});
|
|
18040
18318
|
if (typeof diagnostic === "string" && diagnostic.trim()) {
|
|
@@ -18121,7 +18399,7 @@ function registerTools(server, tabManager, runtime2) {
|
|
|
18121
18399
|
const page = await extractContent(wc);
|
|
18122
18400
|
pageType = detectPageType(page);
|
|
18123
18401
|
} catch (err) {
|
|
18124
|
-
logger$
|
|
18402
|
+
logger$8.warn("Failed to detect page type for tool scoring, falling back to GENERAL:", err);
|
|
18125
18403
|
}
|
|
18126
18404
|
}
|
|
18127
18405
|
const scored = TOOL_DEFINITIONS.map((def) => {
|
|
@@ -19405,7 +19683,7 @@ ${JSON.stringify(otherHighlights, null, 2)}`
|
|
|
19405
19683
|
void 0,
|
|
19406
19684
|
h.color
|
|
19407
19685
|
).catch(
|
|
19408
|
-
(err) => logger$
|
|
19686
|
+
(err) => logger$8.warn("Failed to restore highlight after removal:", err)
|
|
19409
19687
|
);
|
|
19410
19688
|
}
|
|
19411
19689
|
}
|
|
@@ -20253,7 +20531,7 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
|
|
|
20253
20531
|
try {
|
|
20254
20532
|
page = await extractContent(wc);
|
|
20255
20533
|
} catch (err) {
|
|
20256
|
-
logger$
|
|
20534
|
+
logger$8.warn("Failed to extract page while generating suggestions:", err);
|
|
20257
20535
|
return asTextResponse(
|
|
20258
20536
|
"Could not read page. Try navigate to a working URL."
|
|
20259
20537
|
);
|
|
@@ -20862,7 +21140,7 @@ ${JSON.stringify(tableJson, null, 2)}`;
|
|
|
20862
21140
|
try {
|
|
20863
21141
|
targetDomain = new URL(tab.state.url).hostname;
|
|
20864
21142
|
} catch (err) {
|
|
20865
|
-
logger$
|
|
21143
|
+
logger$8.warn("Failed to parse active tab URL for vault_status:", err);
|
|
20866
21144
|
return asErrorTextResponse("Could not parse active tab URL");
|
|
20867
21145
|
}
|
|
20868
21146
|
}
|
|
@@ -20928,7 +21206,7 @@ Use vault_login to fill the login form. Credentials are filled directly — you
|
|
|
20928
21206
|
try {
|
|
20929
21207
|
hostname = new URL(tab.state.url).hostname;
|
|
20930
21208
|
} catch (err) {
|
|
20931
|
-
logger$
|
|
21209
|
+
logger$8.warn("Failed to parse active tab URL for vault_login:", err);
|
|
20932
21210
|
return asErrorTextResponse("Could not parse active tab URL");
|
|
20933
21211
|
}
|
|
20934
21212
|
const matches = findEntriesForDomain(`https://${hostname}`);
|
|
@@ -21022,7 +21300,7 @@ Use vault_login to fill the login form. Credentials are filled directly — you
|
|
|
21022
21300
|
try {
|
|
21023
21301
|
hostname = new URL(tab.state.url).hostname;
|
|
21024
21302
|
} catch (err) {
|
|
21025
|
-
logger$
|
|
21303
|
+
logger$8.warn("Failed to parse active tab URL for vault_totp:", err);
|
|
21026
21304
|
return asErrorTextResponse("Could not parse active tab URL");
|
|
21027
21305
|
}
|
|
21028
21306
|
const matches = findEntriesForDomain(`https://${hostname}`);
|
|
@@ -21194,7 +21472,7 @@ function startMcpServer(tabManager, runtime2, port) {
|
|
|
21194
21472
|
await mcpServer.connect(transport);
|
|
21195
21473
|
await transport.handleRequest(req, res);
|
|
21196
21474
|
} catch (error) {
|
|
21197
|
-
logger$
|
|
21475
|
+
logger$8.error("Error handling request:", error);
|
|
21198
21476
|
if (!res.headersSent) {
|
|
21199
21477
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
21200
21478
|
res.end(
|
|
@@ -21213,7 +21491,7 @@ function startMcpServer(tabManager, runtime2, port) {
|
|
|
21213
21491
|
};
|
|
21214
21492
|
server.once("error", (error) => {
|
|
21215
21493
|
const message = error.code === "EADDRINUSE" ? `Port ${port} is already in use. MCP server not started.` : error.message;
|
|
21216
|
-
logger$
|
|
21494
|
+
logger$8.error("Server error:", error);
|
|
21217
21495
|
clearMcpAuthFile();
|
|
21218
21496
|
setMcpHealth({
|
|
21219
21497
|
configuredPort: port,
|
|
@@ -21245,7 +21523,7 @@ function startMcpServer(tabManager, runtime2, port) {
|
|
|
21245
21523
|
message: `MCP server listening on ${endpoint}.`
|
|
21246
21524
|
});
|
|
21247
21525
|
if (process.env.VESSEL_DEBUG_MCP === "1" || process.env.VESSEL_DEBUG_MCP === "true") {
|
|
21248
|
-
logger$
|
|
21526
|
+
logger$8.info(`Server listening on ${endpoint} (auth enabled)`);
|
|
21249
21527
|
}
|
|
21250
21528
|
if (mcpAuthToken) {
|
|
21251
21529
|
writeMcpAuthFile(endpoint, mcpAuthToken);
|
|
@@ -21284,7 +21562,7 @@ function stopMcpServer() {
|
|
|
21284
21562
|
message: "MCP server is stopped."
|
|
21285
21563
|
});
|
|
21286
21564
|
if (process.env.VESSEL_DEBUG_MCP === "1" || process.env.VESSEL_DEBUG_MCP === "true") {
|
|
21287
|
-
logger$
|
|
21565
|
+
logger$8.info("Server stopped");
|
|
21288
21566
|
}
|
|
21289
21567
|
resolve();
|
|
21290
21568
|
});
|
|
@@ -21305,7 +21583,7 @@ const KIT_ID_UNSAFE_CHAR_PATTERN = /[\/\\\0]/;
|
|
|
21305
21583
|
function isSafeAutomationKitId(id) {
|
|
21306
21584
|
return id.length > 0 && !KIT_ID_UNSAFE_CHAR_PATTERN.test(id);
|
|
21307
21585
|
}
|
|
21308
|
-
const logger$
|
|
21586
|
+
const logger$7 = createLogger("KitRegistry");
|
|
21309
21587
|
function getUserKitsDir() {
|
|
21310
21588
|
return path$1.join(electron.app.getPath("userData"), "kits");
|
|
21311
21589
|
}
|
|
@@ -21343,10 +21621,10 @@ function getInstalledKits() {
|
|
|
21343
21621
|
if (isValidKit(parsed)) {
|
|
21344
21622
|
kits.push(parsed);
|
|
21345
21623
|
} else {
|
|
21346
|
-
logger$
|
|
21624
|
+
logger$7.warn(`Skipping invalid kit file: ${file}`);
|
|
21347
21625
|
}
|
|
21348
21626
|
} catch (err) {
|
|
21349
|
-
logger$
|
|
21627
|
+
logger$7.warn(`Failed to read kit file: ${file}`, err);
|
|
21350
21628
|
}
|
|
21351
21629
|
}
|
|
21352
21630
|
return kits;
|
|
@@ -21418,7 +21696,7 @@ function uninstallKit(id, scheduledKitIds) {
|
|
|
21418
21696
|
return errorResult("Failed to remove the kit file.");
|
|
21419
21697
|
}
|
|
21420
21698
|
}
|
|
21421
|
-
const logger$
|
|
21699
|
+
const logger$6 = createLogger("Scheduler");
|
|
21422
21700
|
let jobs = [];
|
|
21423
21701
|
let removeIdleListener = null;
|
|
21424
21702
|
let broadcastFn = null;
|
|
@@ -21443,7 +21721,7 @@ function saveJobs() {
|
|
|
21443
21721
|
try {
|
|
21444
21722
|
fs$1.writeFileSync(getJobsPath(), JSON.stringify(jobs, null, 2), "utf-8");
|
|
21445
21723
|
} catch (err) {
|
|
21446
|
-
logger$
|
|
21724
|
+
logger$6.warn("Failed to save jobs:", err);
|
|
21447
21725
|
}
|
|
21448
21726
|
}
|
|
21449
21727
|
function normalizeJob(job, now = /* @__PURE__ */ new Date()) {
|
|
@@ -21565,7 +21843,7 @@ async function fireJob(job, windowState, runtime2) {
|
|
|
21565
21843
|
};
|
|
21566
21844
|
startActivity();
|
|
21567
21845
|
if (!settings2.chatProvider) {
|
|
21568
|
-
logger$
|
|
21846
|
+
logger$6.warn(`Job "${job.kitName}" skipped — no chat provider configured`);
|
|
21569
21847
|
appendActivity(
|
|
21570
21848
|
"Chat provider not configured. Open Settings (Ctrl+,) to choose a provider."
|
|
21571
21849
|
);
|
|
@@ -21573,7 +21851,7 @@ async function fireJob(job, windowState, runtime2) {
|
|
|
21573
21851
|
return;
|
|
21574
21852
|
}
|
|
21575
21853
|
if (process.env.VESSEL_DEBUG_SCHEDULER === "1" || process.env.VESSEL_DEBUG_SCHEDULER === "true") {
|
|
21576
|
-
logger$
|
|
21854
|
+
logger$6.info(`Firing scheduled job: ${job.kitName} (${job.id})`);
|
|
21577
21855
|
}
|
|
21578
21856
|
try {
|
|
21579
21857
|
const provider = createProvider(settings2.chatProvider);
|
|
@@ -21626,7 +21904,7 @@ function tick(windowState, runtime2) {
|
|
|
21626
21904
|
saveJobs();
|
|
21627
21905
|
broadcastFn?.(Channels.SCHEDULE_JOBS_UPDATE, jobs);
|
|
21628
21906
|
void fireJob(job, windowState, runtime2).catch((err) => {
|
|
21629
|
-
logger$
|
|
21907
|
+
logger$6.warn("Unexpected error firing job:", err);
|
|
21630
21908
|
}).finally(fireNext);
|
|
21631
21909
|
};
|
|
21632
21910
|
fireNext();
|
|
@@ -22196,68 +22474,515 @@ function registerWindowControlHandlers(mainWindow) {
|
|
|
22196
22474
|
mainWindow.close();
|
|
22197
22475
|
});
|
|
22198
22476
|
}
|
|
22199
|
-
|
|
22200
|
-
|
|
22201
|
-
|
|
22202
|
-
|
|
22203
|
-
|
|
22204
|
-
|
|
22205
|
-
|
|
22206
|
-
|
|
22207
|
-
|
|
22208
|
-
|
|
22209
|
-
|
|
22210
|
-
|
|
22211
|
-
|
|
22212
|
-
|
|
22213
|
-
|
|
22214
|
-
|
|
22215
|
-
|
|
22216
|
-
|
|
22217
|
-
|
|
22218
|
-
|
|
22219
|
-
|
|
22220
|
-
|
|
22221
|
-
|
|
22222
|
-
|
|
22223
|
-
|
|
22224
|
-
|
|
22225
|
-
|
|
22226
|
-
|
|
22227
|
-
|
|
22228
|
-
|
|
22229
|
-
|
|
22230
|
-
|
|
22231
|
-
|
|
22232
|
-
|
|
22233
|
-
|
|
22234
|
-
|
|
22235
|
-
|
|
22236
|
-
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
22240
|
-
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
|
|
22244
|
-
|
|
22245
|
-
|
|
22246
|
-
|
|
22247
|
-
|
|
22248
|
-
|
|
22249
|
-
};
|
|
22250
|
-
|
|
22251
|
-
|
|
22252
|
-
|
|
22253
|
-
|
|
22477
|
+
const BLOCKED_RESOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
22478
|
+
"script",
|
|
22479
|
+
"image",
|
|
22480
|
+
"xhr",
|
|
22481
|
+
"fetch",
|
|
22482
|
+
"subFrame",
|
|
22483
|
+
"media",
|
|
22484
|
+
"ping",
|
|
22485
|
+
"webSocket"
|
|
22486
|
+
]);
|
|
22487
|
+
const BLOCKED_HOST_SUFFIXES = [
|
|
22488
|
+
"doubleclick.net",
|
|
22489
|
+
"googlesyndication.com",
|
|
22490
|
+
"googleadservices.com",
|
|
22491
|
+
"adservice.google.com",
|
|
22492
|
+
"adnxs.com",
|
|
22493
|
+
"adsrvr.org",
|
|
22494
|
+
"taboola.com",
|
|
22495
|
+
"outbrain.com",
|
|
22496
|
+
"criteo.com",
|
|
22497
|
+
"criteo.net",
|
|
22498
|
+
"pubmatic.com",
|
|
22499
|
+
"rubiconproject.com",
|
|
22500
|
+
"openx.net",
|
|
22501
|
+
"casalemedia.com",
|
|
22502
|
+
"advertising.com",
|
|
22503
|
+
"amazon-adsystem.com",
|
|
22504
|
+
"adsymptotic.com",
|
|
22505
|
+
"moatads.com",
|
|
22506
|
+
"quantserve.com",
|
|
22507
|
+
"scorecardresearch.com"
|
|
22508
|
+
];
|
|
22509
|
+
const THIRD_PARTY_PATH_PATTERNS = [
|
|
22510
|
+
/\/ads?[/?._-]/i,
|
|
22511
|
+
/\/adservice/i,
|
|
22512
|
+
/\/advert/i,
|
|
22513
|
+
/\/prebid/i,
|
|
22514
|
+
/\/banner/i,
|
|
22515
|
+
/\/sponsor/i,
|
|
22516
|
+
/\/promotions?\//i,
|
|
22517
|
+
/\/trk\//i,
|
|
22518
|
+
/\/track(ing)?\//i,
|
|
22519
|
+
/\/beacon/i,
|
|
22520
|
+
/\/pixel/i
|
|
22521
|
+
];
|
|
22522
|
+
let installed = false;
|
|
22523
|
+
function normalizeHostname(value) {
|
|
22524
|
+
return value.trim().toLowerCase().replace(/\.$/, "");
|
|
22525
|
+
}
|
|
22526
|
+
function hostnameMatches(hostname, suffix) {
|
|
22527
|
+
return hostname === suffix || hostname.endsWith(`.${suffix}`);
|
|
22528
|
+
}
|
|
22529
|
+
function parseHostname(url) {
|
|
22530
|
+
try {
|
|
22531
|
+
const parsed = new URL(url);
|
|
22532
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
22533
|
+
return null;
|
|
22254
22534
|
}
|
|
22255
|
-
|
|
22256
|
-
}
|
|
22257
|
-
|
|
22258
|
-
|
|
22259
|
-
|
|
22260
|
-
|
|
22535
|
+
return normalizeHostname(parsed.hostname);
|
|
22536
|
+
} catch {
|
|
22537
|
+
return null;
|
|
22538
|
+
}
|
|
22539
|
+
}
|
|
22540
|
+
function isThirdParty(url, firstPartyHost) {
|
|
22541
|
+
if (!firstPartyHost) return true;
|
|
22542
|
+
const target = normalizeHostname(url.hostname);
|
|
22543
|
+
return !(target === firstPartyHost || target.endsWith(`.${firstPartyHost}`));
|
|
22544
|
+
}
|
|
22545
|
+
function shouldBlockRequest(details) {
|
|
22546
|
+
if (!BLOCKED_RESOURCE_TYPES.has(details.resourceType)) return false;
|
|
22547
|
+
let parsed;
|
|
22548
|
+
try {
|
|
22549
|
+
parsed = new URL(details.url);
|
|
22550
|
+
} catch {
|
|
22551
|
+
return false;
|
|
22552
|
+
}
|
|
22553
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
22554
|
+
return false;
|
|
22555
|
+
}
|
|
22556
|
+
const hostname = normalizeHostname(parsed.hostname);
|
|
22557
|
+
if (BLOCKED_HOST_SUFFIXES.some((suffix) => hostnameMatches(hostname, suffix))) {
|
|
22558
|
+
return true;
|
|
22559
|
+
}
|
|
22560
|
+
const firstPartyHost = parseHostname(details.referrer) || parseHostname(details.initiator || "");
|
|
22561
|
+
if (!isThirdParty(parsed, firstPartyHost)) return false;
|
|
22562
|
+
const candidate = `${hostname}${parsed.pathname}${parsed.search}`;
|
|
22563
|
+
return THIRD_PARTY_PATH_PATTERNS.some((pattern) => pattern.test(candidate));
|
|
22564
|
+
}
|
|
22565
|
+
function installAdBlocking(tabManager) {
|
|
22566
|
+
if (installed) return;
|
|
22567
|
+
installed = true;
|
|
22568
|
+
electron.session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
|
|
22569
|
+
const webContentsId = typeof details.webContentsId === "number" ? details.webContentsId : null;
|
|
22570
|
+
if (webContentsId == null) {
|
|
22571
|
+
callback({});
|
|
22572
|
+
return;
|
|
22573
|
+
}
|
|
22574
|
+
if (!tabManager.isAdBlockingEnabledForWebContents(webContentsId)) {
|
|
22575
|
+
callback({});
|
|
22576
|
+
return;
|
|
22577
|
+
}
|
|
22578
|
+
callback({ cancel: shouldBlockRequest(details) });
|
|
22579
|
+
});
|
|
22580
|
+
}
|
|
22581
|
+
function installAdBlockingForSession(ses, tabManager) {
|
|
22582
|
+
ses.webRequest.onBeforeRequest((details, callback) => {
|
|
22583
|
+
const webContentsId = typeof details.webContentsId === "number" ? details.webContentsId : null;
|
|
22584
|
+
if (webContentsId == null) {
|
|
22585
|
+
callback({});
|
|
22586
|
+
return;
|
|
22587
|
+
}
|
|
22588
|
+
if (!tabManager.isAdBlockingEnabledForWebContents(webContentsId)) {
|
|
22589
|
+
callback({});
|
|
22590
|
+
return;
|
|
22591
|
+
}
|
|
22592
|
+
callback({ cancel: shouldBlockRequest(details) });
|
|
22593
|
+
});
|
|
22594
|
+
}
|
|
22595
|
+
function resolveDownloadPath(downloadDir, filename) {
|
|
22596
|
+
fs$1.mkdirSync(downloadDir, { recursive: true });
|
|
22597
|
+
const parsed = path.parse(filename);
|
|
22598
|
+
let attempt = 0;
|
|
22599
|
+
while (true) {
|
|
22600
|
+
const candidateName = attempt === 0 ? filename : `${parsed.name} (${attempt})${parsed.ext}`;
|
|
22601
|
+
const candidatePath = path.join(downloadDir, candidateName);
|
|
22602
|
+
if (!fs$1.existsSync(candidatePath)) {
|
|
22603
|
+
return candidatePath;
|
|
22604
|
+
}
|
|
22605
|
+
attempt += 1;
|
|
22606
|
+
}
|
|
22607
|
+
}
|
|
22608
|
+
function installDownloadHandler(chromeView) {
|
|
22609
|
+
installDownloadHandlerForSession(electron.session.defaultSession, chromeView);
|
|
22610
|
+
}
|
|
22611
|
+
function installDownloadHandlerForSession(targetSession, chromeView) {
|
|
22612
|
+
targetSession.on("will-download", (_event, item) => {
|
|
22613
|
+
const settings2 = loadSettings();
|
|
22614
|
+
const downloadDir = settings2.downloadPath.trim() || electron.app.getPath("downloads");
|
|
22615
|
+
const filename = item.getFilename();
|
|
22616
|
+
const savePath = resolveDownloadPath(downloadDir, filename);
|
|
22617
|
+
item.setSavePath(savePath);
|
|
22618
|
+
const info = {
|
|
22619
|
+
filename,
|
|
22620
|
+
savePath,
|
|
22621
|
+
totalBytes: item.getTotalBytes(),
|
|
22622
|
+
receivedBytes: 0,
|
|
22623
|
+
state: "progressing"
|
|
22624
|
+
};
|
|
22625
|
+
if (!chromeView.webContents.isDestroyed()) {
|
|
22626
|
+
chromeView.webContents.send(Channels.DOWNLOAD_STARTED, info);
|
|
22627
|
+
}
|
|
22628
|
+
item.on("updated", (_event2, state2) => {
|
|
22629
|
+
info.receivedBytes = item.getReceivedBytes();
|
|
22630
|
+
info.totalBytes = item.getTotalBytes();
|
|
22631
|
+
info.state = state2 === "progressing" ? "progressing" : "interrupted";
|
|
22632
|
+
if (!chromeView.webContents.isDestroyed()) {
|
|
22633
|
+
chromeView.webContents.send(Channels.DOWNLOAD_PROGRESS, info);
|
|
22634
|
+
}
|
|
22635
|
+
});
|
|
22636
|
+
item.once("done", (_event2, state2) => {
|
|
22637
|
+
info.receivedBytes = item.getReceivedBytes();
|
|
22638
|
+
info.state = state2 === "completed" ? "completed" : "cancelled";
|
|
22639
|
+
if (!chromeView.webContents.isDestroyed()) {
|
|
22640
|
+
chromeView.webContents.send(Channels.DOWNLOAD_DONE, info);
|
|
22641
|
+
}
|
|
22642
|
+
});
|
|
22643
|
+
});
|
|
22644
|
+
}
|
|
22645
|
+
const logger$5 = createLogger("PrivateWindow");
|
|
22646
|
+
const CHROME_HEIGHT = 110;
|
|
22647
|
+
const privateWindows = /* @__PURE__ */ new Set();
|
|
22648
|
+
function resolveRendererFile$1() {
|
|
22649
|
+
const candidates = [
|
|
22650
|
+
path.join(__dirname, "../../out/renderer/index.html"),
|
|
22651
|
+
path.join(__dirname, "../../../out/renderer/index.html")
|
|
22652
|
+
];
|
|
22653
|
+
for (const c of candidates) {
|
|
22654
|
+
try {
|
|
22655
|
+
fs.accessSync(c);
|
|
22656
|
+
return c;
|
|
22657
|
+
} catch {
|
|
22658
|
+
}
|
|
22659
|
+
}
|
|
22660
|
+
return path.join(__dirname, "../../out/renderer/index.html");
|
|
22661
|
+
}
|
|
22662
|
+
function layoutPrivateViews(state2) {
|
|
22663
|
+
const { window: win, chromeView, tabManager } = state2;
|
|
22664
|
+
const [width, height] = win.getContentSize();
|
|
22665
|
+
chromeView.setBounds({ x: 0, y: 0, width, height: CHROME_HEIGHT });
|
|
22666
|
+
win.contentView.removeChildView(chromeView);
|
|
22667
|
+
win.contentView.addChildView(chromeView);
|
|
22668
|
+
const activeTab = tabManager.getActiveTab();
|
|
22669
|
+
if (activeTab) {
|
|
22670
|
+
activeTab.view.setBounds({
|
|
22671
|
+
x: 0,
|
|
22672
|
+
y: CHROME_HEIGHT,
|
|
22673
|
+
width,
|
|
22674
|
+
height: height - CHROME_HEIGHT
|
|
22675
|
+
});
|
|
22676
|
+
}
|
|
22677
|
+
}
|
|
22678
|
+
function loadPrivateRenderer(chromeView) {
|
|
22679
|
+
const devUrl = process.env.ELECTRON_RENDERER_URL;
|
|
22680
|
+
if (devUrl) {
|
|
22681
|
+
const url = new URL(devUrl);
|
|
22682
|
+
url.searchParams.set("view", "chrome");
|
|
22683
|
+
url.searchParams.set("private", "1");
|
|
22684
|
+
chromeView.webContents.loadURL(url.toString());
|
|
22685
|
+
} else {
|
|
22686
|
+
chromeView.webContents.loadFile(resolveRendererFile$1(), {
|
|
22687
|
+
query: { view: "chrome", private: "1" }
|
|
22688
|
+
});
|
|
22689
|
+
}
|
|
22690
|
+
}
|
|
22691
|
+
function registerPrivateIpcHandlers(state2) {
|
|
22692
|
+
const { chromeView, tabManager } = state2;
|
|
22693
|
+
const ipc = chromeView.webContents.ipc;
|
|
22694
|
+
let findResultListener = null;
|
|
22695
|
+
let findWiredWcId = null;
|
|
22696
|
+
const wireFindEvents = (wc) => {
|
|
22697
|
+
if (findWiredWcId === wc.id && findResultListener) return;
|
|
22698
|
+
if (findWiredWcId && findResultListener) {
|
|
22699
|
+
const previous = tabManager.findTabByWebContentsId(findWiredWcId);
|
|
22700
|
+
previous?.view.webContents.removeListener(
|
|
22701
|
+
"found-in-page",
|
|
22702
|
+
findResultListener
|
|
22703
|
+
);
|
|
22704
|
+
}
|
|
22705
|
+
findWiredWcId = wc.id;
|
|
22706
|
+
if (wc.isDestroyed()) return;
|
|
22707
|
+
const listener = (_event, result) => {
|
|
22708
|
+
if (!chromeView.webContents.isDestroyed()) {
|
|
22709
|
+
chromeView.webContents.send(Channels.FIND_IN_PAGE_RESULT, result);
|
|
22710
|
+
}
|
|
22711
|
+
};
|
|
22712
|
+
findResultListener = listener;
|
|
22713
|
+
wc.on("found-in-page", listener);
|
|
22714
|
+
const capturedWcId = wc.id;
|
|
22715
|
+
wc.once("destroyed", () => {
|
|
22716
|
+
if (findWiredWcId === capturedWcId) {
|
|
22717
|
+
findWiredWcId = null;
|
|
22718
|
+
findResultListener = null;
|
|
22719
|
+
}
|
|
22720
|
+
});
|
|
22721
|
+
};
|
|
22722
|
+
ipc.handle(Channels.TAB_CREATE, (_e, url) => {
|
|
22723
|
+
return tabManager.createTab(url);
|
|
22724
|
+
});
|
|
22725
|
+
ipc.handle(Channels.TAB_CLOSE, (_e, id) => {
|
|
22726
|
+
tabManager.closeTab(id);
|
|
22727
|
+
layoutPrivateViews(state2);
|
|
22728
|
+
});
|
|
22729
|
+
ipc.handle(Channels.TAB_SWITCH, (_e, id) => {
|
|
22730
|
+
tabManager.switchTab(id);
|
|
22731
|
+
layoutPrivateViews(state2);
|
|
22732
|
+
});
|
|
22733
|
+
ipc.handle(Channels.TAB_NAVIGATE, (_e, id, url) => {
|
|
22734
|
+
return tabManager.navigateTab(id, url);
|
|
22735
|
+
});
|
|
22736
|
+
ipc.handle(Channels.TAB_BACK, (_e, id) => {
|
|
22737
|
+
tabManager.goBack(id);
|
|
22738
|
+
});
|
|
22739
|
+
ipc.handle(Channels.TAB_FORWARD, (_e, id) => {
|
|
22740
|
+
tabManager.goForward(id);
|
|
22741
|
+
});
|
|
22742
|
+
ipc.handle(Channels.TAB_RELOAD, (_e, id) => {
|
|
22743
|
+
tabManager.reloadTab(id);
|
|
22744
|
+
});
|
|
22745
|
+
ipc.handle(Channels.TAB_TOGGLE_AD_BLOCK, (_e, id) => {
|
|
22746
|
+
const tab = tabManager.getTab(id);
|
|
22747
|
+
if (!tab) return null;
|
|
22748
|
+
const newState = !tab.state.adBlockingEnabled;
|
|
22749
|
+
tab.setAdBlockingEnabled(newState);
|
|
22750
|
+
return newState;
|
|
22751
|
+
});
|
|
22752
|
+
ipc.handle(Channels.TAB_ZOOM_IN, (_e, id) => {
|
|
22753
|
+
tabManager.zoomIn(id);
|
|
22754
|
+
});
|
|
22755
|
+
ipc.handle(Channels.TAB_ZOOM_OUT, (_e, id) => {
|
|
22756
|
+
tabManager.zoomOut(id);
|
|
22757
|
+
});
|
|
22758
|
+
ipc.handle(Channels.TAB_ZOOM_RESET, (_e, id) => {
|
|
22759
|
+
tabManager.zoomReset(id);
|
|
22760
|
+
});
|
|
22761
|
+
ipc.handle(Channels.TAB_STATE_GET, () => ({
|
|
22762
|
+
tabs: tabManager.getAllStates(),
|
|
22763
|
+
activeId: tabManager.getActiveTabId() || ""
|
|
22764
|
+
}));
|
|
22765
|
+
ipc.handle(Channels.TAB_REOPEN_CLOSED, () => {
|
|
22766
|
+
const id = tabManager.reopenClosedTab();
|
|
22767
|
+
if (id) layoutPrivateViews(state2);
|
|
22768
|
+
return id;
|
|
22769
|
+
});
|
|
22770
|
+
ipc.handle(Channels.TAB_DUPLICATE, (_e, id) => {
|
|
22771
|
+
const newId = tabManager.duplicateTab(id);
|
|
22772
|
+
if (newId) layoutPrivateViews(state2);
|
|
22773
|
+
return newId;
|
|
22774
|
+
});
|
|
22775
|
+
ipc.on(Channels.TAB_CONTEXT_MENU, (_e, id) => {
|
|
22776
|
+
const { Menu, MenuItem } = require("electron");
|
|
22777
|
+
const menu = new Menu();
|
|
22778
|
+
menu.append(
|
|
22779
|
+
new MenuItem({
|
|
22780
|
+
label: "Duplicate Tab",
|
|
22781
|
+
click: () => {
|
|
22782
|
+
const newId = tabManager.duplicateTab(id);
|
|
22783
|
+
if (newId) layoutPrivateViews(state2);
|
|
22784
|
+
}
|
|
22785
|
+
})
|
|
22786
|
+
);
|
|
22787
|
+
menu.append(
|
|
22788
|
+
new MenuItem({
|
|
22789
|
+
label: "Close Tab",
|
|
22790
|
+
click: () => {
|
|
22791
|
+
tabManager.closeTab(id);
|
|
22792
|
+
layoutPrivateViews(state2);
|
|
22793
|
+
}
|
|
22794
|
+
})
|
|
22795
|
+
);
|
|
22796
|
+
menu.popup({ window: state2.window });
|
|
22797
|
+
});
|
|
22798
|
+
ipc.handle(Channels.IS_PRIVATE_MODE, () => true);
|
|
22799
|
+
ipc.handle(Channels.OPEN_PRIVATE_WINDOW, () => {
|
|
22800
|
+
createPrivateWindow();
|
|
22801
|
+
});
|
|
22802
|
+
ipc.handle(Channels.WINDOW_MINIMIZE, () => {
|
|
22803
|
+
state2.window.minimize();
|
|
22804
|
+
});
|
|
22805
|
+
ipc.handle(Channels.WINDOW_MAXIMIZE, () => {
|
|
22806
|
+
if (state2.window.isMaximized()) {
|
|
22807
|
+
state2.window.unmaximize();
|
|
22808
|
+
} else {
|
|
22809
|
+
state2.window.maximize();
|
|
22810
|
+
}
|
|
22811
|
+
});
|
|
22812
|
+
ipc.handle(Channels.WINDOW_CLOSE, () => {
|
|
22813
|
+
state2.window.close();
|
|
22814
|
+
});
|
|
22815
|
+
ipc.handle(Channels.SETTINGS_VISIBILITY, () => {
|
|
22816
|
+
return false;
|
|
22817
|
+
});
|
|
22818
|
+
ipc.handle(Channels.FOCUS_MODE_TOGGLE, () => false);
|
|
22819
|
+
ipc.handle(Channels.SIDEBAR_TOGGLE, () => ({ open: false, width: 0 }));
|
|
22820
|
+
ipc.handle(Channels.DEVTOOLS_PANEL_TOGGLE, () => ({ open: false }));
|
|
22821
|
+
ipc.handle(
|
|
22822
|
+
Channels.FIND_IN_PAGE_START,
|
|
22823
|
+
(_e, text, options) => {
|
|
22824
|
+
const tab = tabManager.getActiveTab();
|
|
22825
|
+
if (!tab) return null;
|
|
22826
|
+
const wc = tab.view.webContents;
|
|
22827
|
+
if (wc.isDestroyed()) return null;
|
|
22828
|
+
wireFindEvents(wc);
|
|
22829
|
+
return wc.findInPage(text, {
|
|
22830
|
+
forward: options?.forward ?? true,
|
|
22831
|
+
findNext: options?.findNext ?? false
|
|
22832
|
+
});
|
|
22833
|
+
}
|
|
22834
|
+
);
|
|
22835
|
+
ipc.handle(Channels.FIND_IN_PAGE_NEXT, (_e, forward) => {
|
|
22836
|
+
const tab = tabManager.getActiveTab();
|
|
22837
|
+
if (!tab) return null;
|
|
22838
|
+
const wc = tab.view.webContents;
|
|
22839
|
+
if (wc.isDestroyed()) return null;
|
|
22840
|
+
wireFindEvents(wc);
|
|
22841
|
+
return wc.findInPage("", { forward: forward ?? true, findNext: true });
|
|
22842
|
+
});
|
|
22843
|
+
ipc.handle(
|
|
22844
|
+
Channels.FIND_IN_PAGE_STOP,
|
|
22845
|
+
(_e, action) => {
|
|
22846
|
+
const tab = tabManager.getActiveTab();
|
|
22847
|
+
if (!tab) return;
|
|
22848
|
+
const wc = tab.view.webContents;
|
|
22849
|
+
if (wc.isDestroyed()) return;
|
|
22850
|
+
wc.stopFindInPage(action ?? "clearSelection");
|
|
22851
|
+
}
|
|
22852
|
+
);
|
|
22853
|
+
}
|
|
22854
|
+
function createPrivateWindow() {
|
|
22855
|
+
const privateSessionPartition = `private-${crypto$1.randomUUID()}`;
|
|
22856
|
+
const privateSession = electron.session.fromPartition(privateSessionPartition);
|
|
22857
|
+
privateSession.setUserAgent(electron.session.defaultSession.getUserAgent());
|
|
22858
|
+
const win = new electron.BaseWindow({
|
|
22859
|
+
width: 1280,
|
|
22860
|
+
height: 800,
|
|
22861
|
+
minWidth: 800,
|
|
22862
|
+
minHeight: 600,
|
|
22863
|
+
frame: false,
|
|
22864
|
+
show: false,
|
|
22865
|
+
backgroundColor: "#1e1a2e",
|
|
22866
|
+
title: "Vessel - Private Browsing"
|
|
22867
|
+
});
|
|
22868
|
+
const chromeView = new electron.WebContentsView({
|
|
22869
|
+
webPreferences: {
|
|
22870
|
+
preload: path.join(__dirname, "../preload/index.js"),
|
|
22871
|
+
sandbox: true,
|
|
22872
|
+
contextIsolation: true,
|
|
22873
|
+
nodeIntegration: false
|
|
22874
|
+
}
|
|
22875
|
+
});
|
|
22876
|
+
chromeView.setBackgroundColor("#00000000");
|
|
22877
|
+
win.contentView.addChildView(chromeView);
|
|
22878
|
+
const tabManager = new TabManager(
|
|
22879
|
+
win,
|
|
22880
|
+
(tabs, activeId) => {
|
|
22881
|
+
if (!chromeView.webContents.isDestroyed()) {
|
|
22882
|
+
chromeView.webContents.send(Channels.TAB_STATE_UPDATE, tabs, activeId);
|
|
22883
|
+
}
|
|
22884
|
+
layoutPrivateViews(state2);
|
|
22885
|
+
},
|
|
22886
|
+
{ isPrivate: true, sessionPartition: privateSessionPartition }
|
|
22887
|
+
);
|
|
22888
|
+
const state2 = {
|
|
22889
|
+
window: win,
|
|
22890
|
+
chromeView,
|
|
22891
|
+
tabManager,
|
|
22892
|
+
session: privateSession,
|
|
22893
|
+
sessionPartition: privateSessionPartition
|
|
22894
|
+
};
|
|
22895
|
+
installAdBlockingForSession(privateSession, tabManager);
|
|
22896
|
+
installDownloadHandlerForSession(privateSession, chromeView);
|
|
22897
|
+
registerPrivateIpcHandlers(state2);
|
|
22898
|
+
win.on("resize", () => layoutPrivateViews(state2));
|
|
22899
|
+
win.on("show", () => layoutPrivateViews(state2));
|
|
22900
|
+
win.on("closed", () => {
|
|
22901
|
+
privateWindows.delete(state2);
|
|
22902
|
+
tabManager.destroyAllTabs();
|
|
22903
|
+
void Promise.all([
|
|
22904
|
+
privateSession.clearStorageData(),
|
|
22905
|
+
privateSession.clearCache()
|
|
22906
|
+
]).catch((error) => {
|
|
22907
|
+
logger$5.warn("Failed to clear private browsing session:", error);
|
|
22908
|
+
});
|
|
22909
|
+
});
|
|
22910
|
+
privateWindows.add(state2);
|
|
22911
|
+
chromeView.webContents.once("dom-ready", () => {
|
|
22912
|
+
tabManager.createTab("about:blank");
|
|
22913
|
+
layoutPrivateViews(state2);
|
|
22914
|
+
});
|
|
22915
|
+
loadPrivateRenderer(chromeView);
|
|
22916
|
+
win.show();
|
|
22917
|
+
logger$5.info("Private browsing window opened");
|
|
22918
|
+
return state2;
|
|
22919
|
+
}
|
|
22920
|
+
let activeChatProvider = null;
|
|
22921
|
+
const logger$4 = createLogger("IPC");
|
|
22922
|
+
const VALID_APPROVAL_MODES = ["auto", "confirm-dangerous", "manual"];
|
|
22923
|
+
function registerIpcHandlers(windowState, runtime2) {
|
|
22924
|
+
const { tabManager, chromeView, sidebarView, devtoolsPanelView, mainWindow } = windowState;
|
|
22925
|
+
electron.ipcMain.handle(Channels.OPEN_PRIVATE_WINDOW, () => {
|
|
22926
|
+
createPrivateWindow();
|
|
22927
|
+
});
|
|
22928
|
+
electron.ipcMain.handle(Channels.IS_PRIVATE_MODE, () => false);
|
|
22929
|
+
let sidebarResizeRecoveryTimer = null;
|
|
22930
|
+
let sidebarResizeActive = false;
|
|
22931
|
+
let runtimeUpdateTimer = null;
|
|
22932
|
+
let pendingRuntimeState = null;
|
|
22933
|
+
const premiumApiOrigin = process.env.VESSEL_PREMIUM_API ? new URL(process.env.VESSEL_PREMIUM_API).origin : "https://vesselpremium.quantaintellect.com";
|
|
22934
|
+
const clearSidebarResizeRecoveryTimer = () => {
|
|
22935
|
+
if (!sidebarResizeRecoveryTimer) return;
|
|
22936
|
+
clearTimeout(sidebarResizeRecoveryTimer);
|
|
22937
|
+
sidebarResizeRecoveryTimer = null;
|
|
22938
|
+
};
|
|
22939
|
+
const restoreSidebarLayoutAfterResize = () => {
|
|
22940
|
+
clearSidebarResizeRecoveryTimer();
|
|
22941
|
+
if (!sidebarResizeActive) return;
|
|
22942
|
+
sidebarResizeActive = false;
|
|
22943
|
+
layoutViews(windowState);
|
|
22944
|
+
};
|
|
22945
|
+
const scheduleSidebarResizeRecovery = () => {
|
|
22946
|
+
clearSidebarResizeRecoveryTimer();
|
|
22947
|
+
sidebarResizeRecoveryTimer = setTimeout(() => {
|
|
22948
|
+
restoreSidebarLayoutAfterResize();
|
|
22949
|
+
}, 1200);
|
|
22950
|
+
};
|
|
22951
|
+
const flushRuntimeUpdate = () => {
|
|
22952
|
+
runtimeUpdateTimer = null;
|
|
22953
|
+
if (!pendingRuntimeState) return;
|
|
22954
|
+
if (!chromeView.webContents.isDestroyed()) {
|
|
22955
|
+
chromeView.webContents.send(
|
|
22956
|
+
Channels.AGENT_RUNTIME_UPDATE,
|
|
22957
|
+
pendingRuntimeState
|
|
22958
|
+
);
|
|
22959
|
+
}
|
|
22960
|
+
if (!sidebarView.webContents.isDestroyed()) {
|
|
22961
|
+
sidebarView.webContents.send(
|
|
22962
|
+
Channels.AGENT_RUNTIME_UPDATE,
|
|
22963
|
+
pendingRuntimeState
|
|
22964
|
+
);
|
|
22965
|
+
}
|
|
22966
|
+
pendingRuntimeState = null;
|
|
22967
|
+
};
|
|
22968
|
+
const scheduleRuntimeUpdate = (state2) => {
|
|
22969
|
+
pendingRuntimeState = state2;
|
|
22970
|
+
if (runtimeUpdateTimer) return;
|
|
22971
|
+
runtimeUpdateTimer = setTimeout(() => {
|
|
22972
|
+
flushRuntimeUpdate();
|
|
22973
|
+
}, 32);
|
|
22974
|
+
};
|
|
22975
|
+
electron.app.on("before-quit", () => {
|
|
22976
|
+
if (runtimeUpdateTimer) {
|
|
22977
|
+
clearTimeout(runtimeUpdateTimer);
|
|
22978
|
+
runtimeUpdateTimer = null;
|
|
22979
|
+
}
|
|
22980
|
+
flushRuntimeUpdate();
|
|
22981
|
+
});
|
|
22982
|
+
const sendToRendererViews = (channel, ...args) => {
|
|
22983
|
+
chromeView.webContents.send(channel, ...args);
|
|
22984
|
+
sidebarView.webContents.send(channel, ...args);
|
|
22985
|
+
devtoolsPanelView.webContents.send(channel, ...args);
|
|
22261
22986
|
};
|
|
22262
22987
|
const watchPremiumCheckoutTab = (tabId) => {
|
|
22263
22988
|
const tab = tabManager.getTab(tabId);
|
|
@@ -22388,6 +23113,52 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
22388
23113
|
tab.setAdBlockingEnabled(newState);
|
|
22389
23114
|
return newState;
|
|
22390
23115
|
});
|
|
23116
|
+
electron.ipcMain.handle(Channels.TAB_ZOOM_IN, (_, id) => {
|
|
23117
|
+
assertString(id, "id");
|
|
23118
|
+
tabManager.zoomIn(id);
|
|
23119
|
+
});
|
|
23120
|
+
electron.ipcMain.handle(Channels.TAB_ZOOM_OUT, (_, id) => {
|
|
23121
|
+
assertString(id, "id");
|
|
23122
|
+
tabManager.zoomOut(id);
|
|
23123
|
+
});
|
|
23124
|
+
electron.ipcMain.handle(Channels.TAB_ZOOM_RESET, (_, id) => {
|
|
23125
|
+
assertString(id, "id");
|
|
23126
|
+
tabManager.zoomReset(id);
|
|
23127
|
+
});
|
|
23128
|
+
electron.ipcMain.handle(Channels.TAB_REOPEN_CLOSED, () => {
|
|
23129
|
+
const id = tabManager.reopenClosedTab();
|
|
23130
|
+
if (id) layoutViews(windowState);
|
|
23131
|
+
return id;
|
|
23132
|
+
});
|
|
23133
|
+
electron.ipcMain.handle(Channels.TAB_DUPLICATE, (_, id) => {
|
|
23134
|
+
assertString(id, "id");
|
|
23135
|
+
const newId = tabManager.duplicateTab(id);
|
|
23136
|
+
if (newId) layoutViews(windowState);
|
|
23137
|
+
return newId;
|
|
23138
|
+
});
|
|
23139
|
+
electron.ipcMain.on(Channels.TAB_CONTEXT_MENU, (_event, id) => {
|
|
23140
|
+
assertString(id, "id");
|
|
23141
|
+
const menu = new electron.Menu();
|
|
23142
|
+
menu.append(
|
|
23143
|
+
new electron.MenuItem({
|
|
23144
|
+
label: "Duplicate Tab",
|
|
23145
|
+
click: () => {
|
|
23146
|
+
const newId = tabManager.duplicateTab(id);
|
|
23147
|
+
if (newId) layoutViews(windowState);
|
|
23148
|
+
}
|
|
23149
|
+
})
|
|
23150
|
+
);
|
|
23151
|
+
menu.append(
|
|
23152
|
+
new electron.MenuItem({
|
|
23153
|
+
label: "Close Tab",
|
|
23154
|
+
click: () => {
|
|
23155
|
+
tabManager.closeTab(id);
|
|
23156
|
+
layoutViews(windowState);
|
|
23157
|
+
}
|
|
23158
|
+
})
|
|
23159
|
+
);
|
|
23160
|
+
menu.popup({ window: mainWindow });
|
|
23161
|
+
});
|
|
22391
23162
|
electron.ipcMain.handle(Channels.TAB_STATE_GET, () => ({
|
|
22392
23163
|
tabs: tabManager.getAllStates(),
|
|
22393
23164
|
activeId: tabManager.getActiveTabId() || ""
|
|
@@ -23387,6 +24158,21 @@ class AgentRuntime {
|
|
|
23387
24158
|
}
|
|
23388
24159
|
setApprovalMode(mode) {
|
|
23389
24160
|
this.state.supervisor.approvalMode = mode;
|
|
24161
|
+
if (mode === "auto" && !this.state.supervisor.paused) {
|
|
24162
|
+
const approvals = this.state.supervisor.pendingApprovals;
|
|
24163
|
+
if (approvals.length > 0) {
|
|
24164
|
+
const actionIds = new Set(approvals.map((approval) => approval.actionId));
|
|
24165
|
+
this.state.supervisor.pendingApprovals = [];
|
|
24166
|
+
this.state.actions = this.state.actions.map(
|
|
24167
|
+
(action) => actionIds.has(action.id) ? { ...action, status: "running", error: void 0 } : action
|
|
24168
|
+
);
|
|
24169
|
+
for (const approval of approvals) {
|
|
24170
|
+
const resolve = this.pendingResolvers.get(approval.id);
|
|
24171
|
+
this.pendingResolvers.delete(approval.id);
|
|
24172
|
+
resolve?.(true);
|
|
24173
|
+
}
|
|
24174
|
+
}
|
|
24175
|
+
}
|
|
23390
24176
|
this.emit();
|
|
23391
24177
|
return this.getState();
|
|
23392
24178
|
}
|
|
@@ -23880,157 +24666,6 @@ ${progress}
|
|
|
23880
24666
|
});
|
|
23881
24667
|
}
|
|
23882
24668
|
}
|
|
23883
|
-
const BLOCKED_RESOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
23884
|
-
"script",
|
|
23885
|
-
"image",
|
|
23886
|
-
"xhr",
|
|
23887
|
-
"fetch",
|
|
23888
|
-
"subFrame",
|
|
23889
|
-
"media",
|
|
23890
|
-
"ping",
|
|
23891
|
-
"webSocket"
|
|
23892
|
-
]);
|
|
23893
|
-
const BLOCKED_HOST_SUFFIXES = [
|
|
23894
|
-
"doubleclick.net",
|
|
23895
|
-
"googlesyndication.com",
|
|
23896
|
-
"googleadservices.com",
|
|
23897
|
-
"adservice.google.com",
|
|
23898
|
-
"adnxs.com",
|
|
23899
|
-
"adsrvr.org",
|
|
23900
|
-
"taboola.com",
|
|
23901
|
-
"outbrain.com",
|
|
23902
|
-
"criteo.com",
|
|
23903
|
-
"criteo.net",
|
|
23904
|
-
"pubmatic.com",
|
|
23905
|
-
"rubiconproject.com",
|
|
23906
|
-
"openx.net",
|
|
23907
|
-
"casalemedia.com",
|
|
23908
|
-
"advertising.com",
|
|
23909
|
-
"amazon-adsystem.com",
|
|
23910
|
-
"adsymptotic.com",
|
|
23911
|
-
"moatads.com",
|
|
23912
|
-
"quantserve.com",
|
|
23913
|
-
"scorecardresearch.com"
|
|
23914
|
-
];
|
|
23915
|
-
const THIRD_PARTY_PATH_PATTERNS = [
|
|
23916
|
-
/\/ads?[/?._-]/i,
|
|
23917
|
-
/\/adservice/i,
|
|
23918
|
-
/\/advert/i,
|
|
23919
|
-
/\/prebid/i,
|
|
23920
|
-
/\/banner/i,
|
|
23921
|
-
/\/sponsor/i,
|
|
23922
|
-
/\/promotions?\//i,
|
|
23923
|
-
/\/trk\//i,
|
|
23924
|
-
/\/track(ing)?\//i,
|
|
23925
|
-
/\/beacon/i,
|
|
23926
|
-
/\/pixel/i
|
|
23927
|
-
];
|
|
23928
|
-
let installed = false;
|
|
23929
|
-
function normalizeHostname(value) {
|
|
23930
|
-
return value.trim().toLowerCase().replace(/\.$/, "");
|
|
23931
|
-
}
|
|
23932
|
-
function hostnameMatches(hostname, suffix) {
|
|
23933
|
-
return hostname === suffix || hostname.endsWith(`.${suffix}`);
|
|
23934
|
-
}
|
|
23935
|
-
function parseHostname(url) {
|
|
23936
|
-
try {
|
|
23937
|
-
const parsed = new URL(url);
|
|
23938
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
23939
|
-
return null;
|
|
23940
|
-
}
|
|
23941
|
-
return normalizeHostname(parsed.hostname);
|
|
23942
|
-
} catch {
|
|
23943
|
-
return null;
|
|
23944
|
-
}
|
|
23945
|
-
}
|
|
23946
|
-
function isThirdParty(url, firstPartyHost) {
|
|
23947
|
-
if (!firstPartyHost) return true;
|
|
23948
|
-
const target = normalizeHostname(url.hostname);
|
|
23949
|
-
return !(target === firstPartyHost || target.endsWith(`.${firstPartyHost}`));
|
|
23950
|
-
}
|
|
23951
|
-
function shouldBlockRequest(details) {
|
|
23952
|
-
if (!BLOCKED_RESOURCE_TYPES.has(details.resourceType)) return false;
|
|
23953
|
-
let parsed;
|
|
23954
|
-
try {
|
|
23955
|
-
parsed = new URL(details.url);
|
|
23956
|
-
} catch {
|
|
23957
|
-
return false;
|
|
23958
|
-
}
|
|
23959
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
23960
|
-
return false;
|
|
23961
|
-
}
|
|
23962
|
-
const hostname = normalizeHostname(parsed.hostname);
|
|
23963
|
-
if (BLOCKED_HOST_SUFFIXES.some((suffix) => hostnameMatches(hostname, suffix))) {
|
|
23964
|
-
return true;
|
|
23965
|
-
}
|
|
23966
|
-
const firstPartyHost = parseHostname(details.referrer) || parseHostname(details.initiator || "");
|
|
23967
|
-
if (!isThirdParty(parsed, firstPartyHost)) return false;
|
|
23968
|
-
const candidate = `${hostname}${parsed.pathname}${parsed.search}`;
|
|
23969
|
-
return THIRD_PARTY_PATH_PATTERNS.some((pattern) => pattern.test(candidate));
|
|
23970
|
-
}
|
|
23971
|
-
function installAdBlocking(tabManager) {
|
|
23972
|
-
if (installed) return;
|
|
23973
|
-
installed = true;
|
|
23974
|
-
electron.session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
|
|
23975
|
-
const webContentsId = typeof details.webContentsId === "number" ? details.webContentsId : null;
|
|
23976
|
-
if (webContentsId == null) {
|
|
23977
|
-
callback({});
|
|
23978
|
-
return;
|
|
23979
|
-
}
|
|
23980
|
-
if (!tabManager.isAdBlockingEnabledForWebContents(webContentsId)) {
|
|
23981
|
-
callback({});
|
|
23982
|
-
return;
|
|
23983
|
-
}
|
|
23984
|
-
callback({ cancel: shouldBlockRequest(details) });
|
|
23985
|
-
});
|
|
23986
|
-
}
|
|
23987
|
-
function resolveDownloadPath(downloadDir, filename) {
|
|
23988
|
-
fs$1.mkdirSync(downloadDir, { recursive: true });
|
|
23989
|
-
const parsed = path.parse(filename);
|
|
23990
|
-
let attempt = 0;
|
|
23991
|
-
while (true) {
|
|
23992
|
-
const candidateName = attempt === 0 ? filename : `${parsed.name} (${attempt})${parsed.ext}`;
|
|
23993
|
-
const candidatePath = path.join(downloadDir, candidateName);
|
|
23994
|
-
if (!fs$1.existsSync(candidatePath)) {
|
|
23995
|
-
return candidatePath;
|
|
23996
|
-
}
|
|
23997
|
-
attempt += 1;
|
|
23998
|
-
}
|
|
23999
|
-
}
|
|
24000
|
-
function installDownloadHandler(chromeView) {
|
|
24001
|
-
electron.session.defaultSession.on("will-download", (_event, item) => {
|
|
24002
|
-
const settings2 = loadSettings();
|
|
24003
|
-
const downloadDir = settings2.downloadPath.trim() || electron.app.getPath("downloads");
|
|
24004
|
-
const filename = item.getFilename();
|
|
24005
|
-
const savePath = resolveDownloadPath(downloadDir, filename);
|
|
24006
|
-
item.setSavePath(savePath);
|
|
24007
|
-
const info = {
|
|
24008
|
-
filename,
|
|
24009
|
-
savePath,
|
|
24010
|
-
totalBytes: item.getTotalBytes(),
|
|
24011
|
-
receivedBytes: 0,
|
|
24012
|
-
state: "progressing"
|
|
24013
|
-
};
|
|
24014
|
-
if (!chromeView.webContents.isDestroyed()) {
|
|
24015
|
-
chromeView.webContents.send(Channels.DOWNLOAD_STARTED, info);
|
|
24016
|
-
}
|
|
24017
|
-
item.on("updated", (_event2, state2) => {
|
|
24018
|
-
info.receivedBytes = item.getReceivedBytes();
|
|
24019
|
-
info.totalBytes = item.getTotalBytes();
|
|
24020
|
-
info.state = state2 === "progressing" ? "progressing" : "interrupted";
|
|
24021
|
-
if (!chromeView.webContents.isDestroyed()) {
|
|
24022
|
-
chromeView.webContents.send(Channels.DOWNLOAD_PROGRESS, info);
|
|
24023
|
-
}
|
|
24024
|
-
});
|
|
24025
|
-
item.once("done", (_event2, state2) => {
|
|
24026
|
-
info.receivedBytes = item.getReceivedBytes();
|
|
24027
|
-
info.state = state2 === "completed" ? "completed" : "cancelled";
|
|
24028
|
-
if (!chromeView.webContents.isDestroyed()) {
|
|
24029
|
-
chromeView.webContents.send(Channels.DOWNLOAD_DONE, info);
|
|
24030
|
-
}
|
|
24031
|
-
});
|
|
24032
|
-
});
|
|
24033
|
-
}
|
|
24034
24669
|
const logger$2 = createLogger("Shortcuts");
|
|
24035
24670
|
function registerHighlightShortcut(mainWindow, tabManager) {
|
|
24036
24671
|
const register = () => {
|
|
@@ -24051,8 +24686,18 @@ function registerHighlightShortcut(mainWindow, tabManager) {
|
|
|
24051
24686
|
mainWindow.removeListener("focus", register);
|
|
24052
24687
|
};
|
|
24053
24688
|
}
|
|
24054
|
-
function setupAppMenu() {
|
|
24689
|
+
function setupAppMenu(handlers) {
|
|
24055
24690
|
const appMenu = electron.Menu.buildFromTemplate([
|
|
24691
|
+
{
|
|
24692
|
+
label: "File",
|
|
24693
|
+
submenu: [
|
|
24694
|
+
{
|
|
24695
|
+
label: "Reopen Closed Tab",
|
|
24696
|
+
accelerator: "CommandOrControl+Shift+T",
|
|
24697
|
+
click: handlers.reopenClosedTab
|
|
24698
|
+
}
|
|
24699
|
+
]
|
|
24700
|
+
},
|
|
24056
24701
|
{
|
|
24057
24702
|
label: "Edit",
|
|
24058
24703
|
submenu: [
|
|
@@ -24064,6 +24709,26 @@ function setupAppMenu() {
|
|
|
24064
24709
|
{ role: "paste" },
|
|
24065
24710
|
{ role: "selectAll" }
|
|
24066
24711
|
]
|
|
24712
|
+
},
|
|
24713
|
+
{
|
|
24714
|
+
label: "View",
|
|
24715
|
+
submenu: [
|
|
24716
|
+
{
|
|
24717
|
+
label: "Zoom In",
|
|
24718
|
+
accelerator: "CommandOrControl+Plus",
|
|
24719
|
+
click: handlers.zoomIn
|
|
24720
|
+
},
|
|
24721
|
+
{
|
|
24722
|
+
label: "Zoom Out",
|
|
24723
|
+
accelerator: "CommandOrControl+-",
|
|
24724
|
+
click: handlers.zoomOut
|
|
24725
|
+
},
|
|
24726
|
+
{
|
|
24727
|
+
label: "Actual Size",
|
|
24728
|
+
accelerator: "CommandOrControl+0",
|
|
24729
|
+
click: handlers.zoomReset
|
|
24730
|
+
}
|
|
24731
|
+
]
|
|
24067
24732
|
}
|
|
24068
24733
|
]);
|
|
24069
24734
|
electron.Menu.setApplicationMenu(appMenu);
|
|
@@ -24285,6 +24950,11 @@ function closeSplash(splash, delayMs = 0) {
|
|
|
24285
24950
|
}
|
|
24286
24951
|
const logger = createLogger("Bootstrap");
|
|
24287
24952
|
let runtime = null;
|
|
24953
|
+
function configureUserAgent() {
|
|
24954
|
+
const originalUA = electron.session.defaultSession.getUserAgent();
|
|
24955
|
+
const maskedUA = originalUA.replace(/ Electron\/[^\s]+/, "") + " Vessel/" + electron.app.getVersion();
|
|
24956
|
+
electron.session.defaultSession.setUserAgent(maskedUA);
|
|
24957
|
+
}
|
|
24288
24958
|
function checkWritableUserData(userDataPath) {
|
|
24289
24959
|
const issues = [];
|
|
24290
24960
|
try {
|
|
@@ -24348,6 +25018,7 @@ Action: Open Settings (Ctrl+,) to choose a different port, then save to restart
|
|
|
24348
25018
|
});
|
|
24349
25019
|
}
|
|
24350
25020
|
async function bootstrap() {
|
|
25021
|
+
configureUserAgent();
|
|
24351
25022
|
const splash = createSplashWindow();
|
|
24352
25023
|
const settings2 = loadSettings();
|
|
24353
25024
|
const userDataPath = electron.app.getPath("userData");
|
|
@@ -24416,7 +25087,24 @@ async function bootstrap() {
|
|
|
24416
25087
|
});
|
|
24417
25088
|
registerIpcHandlers(windowState, runtime);
|
|
24418
25089
|
registerHighlightShortcut(windowState.mainWindow, tabManager);
|
|
24419
|
-
setupAppMenu(
|
|
25090
|
+
setupAppMenu({
|
|
25091
|
+
reopenClosedTab: () => {
|
|
25092
|
+
const id = tabManager.reopenClosedTab();
|
|
25093
|
+
if (id) layoutViews(windowState);
|
|
25094
|
+
},
|
|
25095
|
+
zoomIn: () => {
|
|
25096
|
+
const id = tabManager.getActiveTabId();
|
|
25097
|
+
if (id) tabManager.zoomIn(id);
|
|
25098
|
+
},
|
|
25099
|
+
zoomOut: () => {
|
|
25100
|
+
const id = tabManager.getActiveTabId();
|
|
25101
|
+
if (id) tabManager.zoomOut(id);
|
|
25102
|
+
},
|
|
25103
|
+
zoomReset: () => {
|
|
25104
|
+
const id = tabManager.getActiveTabId();
|
|
25105
|
+
if (id) tabManager.zoomReset(id);
|
|
25106
|
+
}
|
|
25107
|
+
});
|
|
24420
25108
|
subscribe((state2) => {
|
|
24421
25109
|
chromeView.webContents.send(Channels.BOOKMARKS_UPDATE, state2);
|
|
24422
25110
|
sidebarView.webContents.send(Channels.BOOKMARKS_UPDATE, state2);
|