@quanta-intellect/vessel-browser 0.1.92 → 0.1.94
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
|
@@ -104,10 +104,23 @@ function canUseSafeStorage$1() {
|
|
|
104
104
|
return false;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
+
function writePrivateFile(filePath2, data) {
|
|
108
|
+
fs.writeFileSync(filePath2, data, { mode: 384 });
|
|
109
|
+
try {
|
|
110
|
+
fs.chmodSync(filePath2, 384);
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function assertSafeStorageAvailable() {
|
|
115
|
+
if (!canUseSafeStorage$1()) {
|
|
116
|
+
throw new Error("OS-backed secret storage is unavailable; refusing to store secrets on disk.");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
107
119
|
function readStoredProviderSecret() {
|
|
108
120
|
try {
|
|
121
|
+
if (!canUseSafeStorage$1()) return null;
|
|
109
122
|
const raw = fs.readFileSync(getChatProviderSecretPath());
|
|
110
|
-
const decoded =
|
|
123
|
+
const decoded = electron.safeStorage.decryptString(raw);
|
|
111
124
|
const parsed = JSON.parse(decoded);
|
|
112
125
|
if (parsed && typeof parsed === "object" && typeof parsed.providerId === "string" && typeof parsed.apiKey === "string") {
|
|
113
126
|
return parsed;
|
|
@@ -117,15 +130,12 @@ function readStoredProviderSecret() {
|
|
|
117
130
|
return null;
|
|
118
131
|
}
|
|
119
132
|
function writeStoredProviderSecret(secret) {
|
|
133
|
+
assertSafeStorageAvailable();
|
|
120
134
|
const filePath2 = getChatProviderSecretPath();
|
|
121
135
|
fs.mkdirSync(path.dirname(filePath2), { recursive: true });
|
|
122
136
|
const payload = JSON.stringify(secret);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
fs.writeFileSync(filePath2, encrypted, { mode: 384 });
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
fs.writeFileSync(filePath2, payload, { mode: 384 });
|
|
137
|
+
const encrypted = electron.safeStorage.encryptString(payload);
|
|
138
|
+
writePrivateFile(filePath2, encrypted);
|
|
129
139
|
}
|
|
130
140
|
function clearStoredProviderSecret() {
|
|
131
141
|
try {
|
|
@@ -138,8 +148,9 @@ function getCodexTokensPath() {
|
|
|
138
148
|
}
|
|
139
149
|
function readStoredCodexTokens() {
|
|
140
150
|
try {
|
|
151
|
+
if (!canUseSafeStorage$1()) return null;
|
|
141
152
|
const raw = fs.readFileSync(getCodexTokensPath());
|
|
142
|
-
const decoded =
|
|
153
|
+
const decoded = electron.safeStorage.decryptString(raw);
|
|
143
154
|
const parsed = JSON.parse(decoded);
|
|
144
155
|
if (parsed && typeof parsed === "object" && typeof parsed.accessToken === "string" && typeof parsed.refreshToken === "string") {
|
|
145
156
|
return parsed;
|
|
@@ -149,15 +160,12 @@ function readStoredCodexTokens() {
|
|
|
149
160
|
return null;
|
|
150
161
|
}
|
|
151
162
|
function writeStoredCodexTokens(tokens) {
|
|
163
|
+
assertSafeStorageAvailable();
|
|
152
164
|
const filePath2 = getCodexTokensPath();
|
|
153
165
|
fs.mkdirSync(path.dirname(filePath2), { recursive: true });
|
|
154
166
|
const payload = JSON.stringify(tokens);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
fs.writeFileSync(filePath2, encrypted, { mode: 384 });
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
fs.writeFileSync(filePath2, payload, { mode: 384 });
|
|
167
|
+
const encrypted = electron.safeStorage.encryptString(payload);
|
|
168
|
+
writePrivateFile(filePath2, encrypted);
|
|
161
169
|
}
|
|
162
170
|
function clearStoredCodexTokens() {
|
|
163
171
|
try {
|
|
@@ -375,6 +383,28 @@ function assertPermittedNavigationURL(url) {
|
|
|
375
383
|
throw new Error(policyError);
|
|
376
384
|
}
|
|
377
385
|
}
|
|
386
|
+
function loadPermittedNavigationURL(wc, url) {
|
|
387
|
+
assertPermittedNavigationURL(url);
|
|
388
|
+
return wc.loadURL(url);
|
|
389
|
+
}
|
|
390
|
+
function loadInternalDataURL(wc, dataUrl) {
|
|
391
|
+
if (!dataUrl.startsWith("data:text/html;charset=utf-8,")) {
|
|
392
|
+
throw new Error("Blocked unexpected internal data URL");
|
|
393
|
+
}
|
|
394
|
+
return wc.loadURL(dataUrl);
|
|
395
|
+
}
|
|
396
|
+
function loadTrustedAppURL(wc, url) {
|
|
397
|
+
const parsed = new URL(url);
|
|
398
|
+
if (!["file:", "http:", "https:"].includes(parsed.protocol)) {
|
|
399
|
+
throw new Error(`Blocked unexpected app URL scheme: ${parsed.protocol}`);
|
|
400
|
+
}
|
|
401
|
+
const isHttp = parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
402
|
+
const isLocalhost = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
403
|
+
if (isHttp && !isLocalhost) {
|
|
404
|
+
throw new Error(`Blocked unexpected app URL host: ${parsed.hostname}`);
|
|
405
|
+
}
|
|
406
|
+
return wc.loadURL(parsed.toString());
|
|
407
|
+
}
|
|
378
408
|
const MAX_CUSTOM_HISTORY = 50;
|
|
379
409
|
const READER_MODE_DATA_URL_PREFIX = "data:text/html;charset=utf-8,";
|
|
380
410
|
const logger$m = createLogger("Tab");
|
|
@@ -1164,9 +1194,9 @@ function save$3() {
|
|
|
1164
1194
|
}
|
|
1165
1195
|
function emit$4() {
|
|
1166
1196
|
if (!state$5) return;
|
|
1167
|
-
const
|
|
1197
|
+
const snapshot2 = { highlights: [...state$5.highlights] };
|
|
1168
1198
|
for (const listener of listeners$2) {
|
|
1169
|
-
listener(
|
|
1199
|
+
listener(snapshot2);
|
|
1170
1200
|
}
|
|
1171
1201
|
}
|
|
1172
1202
|
function normalizeUrl$1(rawUrl) {
|
|
@@ -1897,9 +1927,9 @@ function save$2() {
|
|
|
1897
1927
|
}
|
|
1898
1928
|
function emit$3() {
|
|
1899
1929
|
if (!state$4) return;
|
|
1900
|
-
const
|
|
1930
|
+
const snapshot2 = { entries: [...state$4.entries] };
|
|
1901
1931
|
for (const listener of listeners$1) {
|
|
1902
|
-
listener(
|
|
1932
|
+
listener(snapshot2);
|
|
1903
1933
|
}
|
|
1904
1934
|
}
|
|
1905
1935
|
function getState$1() {
|
|
@@ -2093,6 +2123,8 @@ class DevToolsSession {
|
|
|
2093
2123
|
this.tabId = tabId;
|
|
2094
2124
|
this.wc = wc;
|
|
2095
2125
|
}
|
|
2126
|
+
tabId;
|
|
2127
|
+
wc;
|
|
2096
2128
|
attached = false;
|
|
2097
2129
|
attachingPromise = null;
|
|
2098
2130
|
consoleDomainEnabled = false;
|
|
@@ -3108,11 +3140,11 @@ class TabManager {
|
|
|
3108
3140
|
note
|
|
3109
3141
|
};
|
|
3110
3142
|
}
|
|
3111
|
-
restoreSession(
|
|
3112
|
-
const tabs =
|
|
3143
|
+
restoreSession(snapshot2) {
|
|
3144
|
+
const tabs = snapshot2.tabs.length > 0 ? snapshot2.tabs : [{ id: "", url: "about:blank", title: "New Tab" }];
|
|
3113
3145
|
const activeIndex = Math.max(
|
|
3114
3146
|
0,
|
|
3115
|
-
Math.min(
|
|
3147
|
+
Math.min(snapshot2.activeIndex, tabs.length - 1)
|
|
3116
3148
|
);
|
|
3117
3149
|
this.destroyAllTabs();
|
|
3118
3150
|
const restoredGroups = /* @__PURE__ */ new Map();
|
|
@@ -3375,6 +3407,7 @@ const Channels = {
|
|
|
3375
3407
|
SETTINGS_UPDATE: "settings:update",
|
|
3376
3408
|
SETTINGS_HEALTH_GET: "settings:health:get",
|
|
3377
3409
|
SETTINGS_HEALTH_UPDATE: "settings:health:update",
|
|
3410
|
+
MCP_REGENERATE_TOKEN: "mcp:regenerate-token",
|
|
3378
3411
|
// Bookmarks
|
|
3379
3412
|
BOOKMARKS_GET: "bookmarks:get",
|
|
3380
3413
|
BOOKMARKS_UPDATE: "bookmarks:update",
|
|
@@ -3875,8 +3908,8 @@ function load$2() {
|
|
|
3875
3908
|
const next = /* @__PURE__ */ new Map();
|
|
3876
3909
|
if (!Array.isArray(raw)) return next;
|
|
3877
3910
|
for (const entry of raw) {
|
|
3878
|
-
const
|
|
3879
|
-
if (
|
|
3911
|
+
const snapshot2 = normalizeStoredSnapshot(entry);
|
|
3912
|
+
if (snapshot2) next.set(snapshot2.url, snapshot2);
|
|
3880
3913
|
}
|
|
3881
3914
|
return next;
|
|
3882
3915
|
}
|
|
@@ -3903,7 +3936,7 @@ function getSnapshot(normalizedUrl) {
|
|
|
3903
3936
|
function saveSnapshot(rawUrl, title, textContent, headings) {
|
|
3904
3937
|
const s = load$2();
|
|
3905
3938
|
const key2 = normalizeUrl(rawUrl);
|
|
3906
|
-
const
|
|
3939
|
+
const snapshot2 = {
|
|
3907
3940
|
url: key2,
|
|
3908
3941
|
title,
|
|
3909
3942
|
textContent: textContent.slice(0, MAX_TEXT_LENGTH),
|
|
@@ -3911,9 +3944,9 @@ function saveSnapshot(rawUrl, title, textContent, headings) {
|
|
|
3911
3944
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3912
3945
|
};
|
|
3913
3946
|
s.delete(key2);
|
|
3914
|
-
s.set(key2,
|
|
3947
|
+
s.set(key2, snapshot2);
|
|
3915
3948
|
persistence$5.schedule();
|
|
3916
|
-
return
|
|
3949
|
+
return snapshot2;
|
|
3917
3950
|
}
|
|
3918
3951
|
function flushPersist$2() {
|
|
3919
3952
|
return persistence$5.flush();
|
|
@@ -4726,6 +4759,7 @@ const POSTHOG_API_KEY = process.env.POSTHOG_API_KEY || "phc_OMeM3P5cxJwl14lOKxYa
|
|
|
4726
4759
|
const POSTHOG_HOST = process.env.POSTHOG_HOST || "https://us.i.posthog.com";
|
|
4727
4760
|
const BATCH_INTERVAL_MS = 6e4;
|
|
4728
4761
|
const MAX_BATCH_SIZE = 50;
|
|
4762
|
+
const SENSITIVE_PROPERTY_RE = /url|uri|query|prompt|content|text|token|secret|key|password|credential|email|domain/i;
|
|
4729
4763
|
function getDeviceIdPath() {
|
|
4730
4764
|
return path.join(electron.app.getPath("userData"), ".vessel-device-id");
|
|
4731
4765
|
}
|
|
@@ -4754,12 +4788,22 @@ function isEnabled() {
|
|
|
4754
4788
|
if (process.env.VESSEL_DEV === "1") return false;
|
|
4755
4789
|
return loadSettings().telemetryEnabled !== false;
|
|
4756
4790
|
}
|
|
4791
|
+
function sanitizeTelemetryProperties(properties) {
|
|
4792
|
+
const safe = {};
|
|
4793
|
+
for (const [key2, value] of Object.entries(properties)) {
|
|
4794
|
+
if (SENSITIVE_PROPERTY_RE.test(key2)) continue;
|
|
4795
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
4796
|
+
safe[key2] = typeof value === "string" ? value.slice(0, 120) : value;
|
|
4797
|
+
}
|
|
4798
|
+
}
|
|
4799
|
+
return safe;
|
|
4800
|
+
}
|
|
4757
4801
|
function trackEvent(event, properties = {}) {
|
|
4758
4802
|
if (!isEnabled()) return;
|
|
4759
4803
|
eventQueue.push({
|
|
4760
4804
|
event,
|
|
4761
4805
|
properties: {
|
|
4762
|
-
...properties,
|
|
4806
|
+
...sanitizeTelemetryProperties(properties),
|
|
4763
4807
|
premium_status: isPremium() ? "premium" : "free",
|
|
4764
4808
|
app_version: electron.app.getVersion(),
|
|
4765
4809
|
platform: process.platform,
|
|
@@ -4794,8 +4838,8 @@ function trackBookmarkAction(action) {
|
|
|
4794
4838
|
function trackVaultAction(action) {
|
|
4795
4839
|
trackEvent("vault_action", { action });
|
|
4796
4840
|
}
|
|
4797
|
-
function trackExtractionFailed(
|
|
4798
|
-
trackEvent("extraction_failed", {
|
|
4841
|
+
function trackExtractionFailed(_domain, reason) {
|
|
4842
|
+
trackEvent("extraction_failed", { reason });
|
|
4799
4843
|
}
|
|
4800
4844
|
function trackPremiumFunnel(step, context) {
|
|
4801
4845
|
trackEvent("premium_funnel", { step, ...context });
|
|
@@ -6774,9 +6818,9 @@ function getMcpStatus() {
|
|
|
6774
6818
|
return state$3.mcp.status;
|
|
6775
6819
|
}
|
|
6776
6820
|
function emitRuntimeHealthChange() {
|
|
6777
|
-
const
|
|
6821
|
+
const snapshot2 = getRuntimeHealth();
|
|
6778
6822
|
for (const listener of runtimeHealthChangeListeners) {
|
|
6779
|
-
listener(
|
|
6823
|
+
listener(snapshot2);
|
|
6780
6824
|
}
|
|
6781
6825
|
}
|
|
6782
6826
|
const state$3 = {
|
|
@@ -8473,6 +8517,17 @@ class OpenAICompatProvider {
|
|
|
8473
8517
|
this.abortController?.abort();
|
|
8474
8518
|
}
|
|
8475
8519
|
}
|
|
8520
|
+
async function openExternalAllowlisted(url, rule) {
|
|
8521
|
+
const parsed = new URL(url);
|
|
8522
|
+
const schemes = rule.schemes ?? ["https:"];
|
|
8523
|
+
if (!schemes.includes(parsed.protocol)) {
|
|
8524
|
+
throw new Error(`Blocked external URL scheme: ${parsed.protocol}`);
|
|
8525
|
+
}
|
|
8526
|
+
if (rule.hosts && !rule.hosts.includes(parsed.hostname)) {
|
|
8527
|
+
throw new Error(`Blocked external URL host: ${parsed.hostname}`);
|
|
8528
|
+
}
|
|
8529
|
+
await electron.shell.openExternal(parsed.toString());
|
|
8530
|
+
}
|
|
8476
8531
|
const logger$g = createLogger("CodexOAuth");
|
|
8477
8532
|
const ISSUER = "https://auth.openai.com";
|
|
8478
8533
|
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
@@ -8809,7 +8864,7 @@ async function startCodexOAuth(onStatus) {
|
|
|
8809
8864
|
activeFlow.port = port;
|
|
8810
8865
|
const authUrl = buildAuthorizeUrl(port, pkce, state2);
|
|
8811
8866
|
safeOnStatus("waiting");
|
|
8812
|
-
|
|
8867
|
+
openExternalAllowlisted(authUrl, { hosts: ["auth.openai.com"] }).catch((err) => {
|
|
8813
8868
|
logger$g.warn("Failed to open browser, user will need the URL:", err);
|
|
8814
8869
|
});
|
|
8815
8870
|
}).catch(wrappedReject);
|
|
@@ -12476,9 +12531,9 @@ function assignDefinedBookmarkFields(bookmark, fields) {
|
|
|
12476
12531
|
}
|
|
12477
12532
|
function emit$2() {
|
|
12478
12533
|
if (!state$2) return;
|
|
12479
|
-
const
|
|
12534
|
+
const snapshot2 = cloneState(state$2);
|
|
12480
12535
|
for (const listener of listeners) {
|
|
12481
|
-
listener(
|
|
12536
|
+
listener(snapshot2);
|
|
12482
12537
|
}
|
|
12483
12538
|
}
|
|
12484
12539
|
function escapeBookmarkHtml(value) {
|
|
@@ -12999,7 +13054,7 @@ async function captureLiveHighlightSnapshot(wc, savedHighlights = []) {
|
|
|
12999
13054
|
savedHighlights.map((highlight) => normalizeText(highlight.text)).filter(Boolean)
|
|
13000
13055
|
);
|
|
13001
13056
|
try {
|
|
13002
|
-
const
|
|
13057
|
+
const snapshot2 = await wc.executeJavaScript(`(() => {
|
|
13003
13058
|
const selection = window.getSelection?.()?.toString().trim() || "";
|
|
13004
13059
|
const pageHighlights = Array.from(
|
|
13005
13060
|
document.querySelectorAll("mark.__vessel-highlight-text[data-vessel-highlight]")
|
|
@@ -13021,7 +13076,7 @@ async function captureLiveHighlightSnapshot(wc, savedHighlights = []) {
|
|
|
13021
13076
|
};
|
|
13022
13077
|
})()`, true);
|
|
13023
13078
|
const seen = /* @__PURE__ */ new Set();
|
|
13024
|
-
const pageHighlights = (
|
|
13079
|
+
const pageHighlights = (snapshot2.pageHighlights ?? []).map((highlight) => ({
|
|
13025
13080
|
text: normalizeText(highlight.text),
|
|
13026
13081
|
color: highlight.color?.trim() || void 0
|
|
13027
13082
|
})).filter((highlight) => highlight.text.length > 0).filter((highlight) => {
|
|
@@ -13033,24 +13088,24 @@ async function captureLiveHighlightSnapshot(wc, savedHighlights = []) {
|
|
|
13033
13088
|
...highlight,
|
|
13034
13089
|
persisted: savedTexts.has(highlight.text)
|
|
13035
13090
|
}));
|
|
13036
|
-
const activeSelection = normalizeText(
|
|
13091
|
+
const activeSelection = normalizeText(snapshot2.activeSelection) || void 0;
|
|
13037
13092
|
return { activeSelection, pageHighlights };
|
|
13038
13093
|
} catch {
|
|
13039
13094
|
return { pageHighlights: [] };
|
|
13040
13095
|
}
|
|
13041
13096
|
}
|
|
13042
|
-
function formatLiveSelectionSection(
|
|
13097
|
+
function formatLiveSelectionSection(snapshot2) {
|
|
13043
13098
|
const sections = [];
|
|
13044
|
-
if (
|
|
13045
|
-
const preview =
|
|
13099
|
+
if (snapshot2.activeSelection) {
|
|
13100
|
+
const preview = snapshot2.activeSelection.length > 400 ? `${snapshot2.activeSelection.slice(0, 397)}...` : snapshot2.activeSelection;
|
|
13046
13101
|
sections.push(
|
|
13047
13102
|
`## Active User Selection
|
|
13048
13103
|
The user currently has this text selected on screen:
|
|
13049
13104
|
- "${preview}"`
|
|
13050
13105
|
);
|
|
13051
13106
|
}
|
|
13052
|
-
if (
|
|
13053
|
-
const lines =
|
|
13107
|
+
if (snapshot2.pageHighlights.length > 0) {
|
|
13108
|
+
const lines = snapshot2.pageHighlights.map((highlight) => {
|
|
13054
13109
|
const preview = highlight.text.length > 180 ? `${highlight.text.slice(0, 177)}...` : highlight.text;
|
|
13055
13110
|
const details = [
|
|
13056
13111
|
highlight.persisted ? "saved" : "visible only",
|
|
@@ -13551,7 +13606,7 @@ async function saveNamedSession(tabManager, name) {
|
|
|
13551
13606
|
const entries = await captureLocalStorageForOrigin(tabManager, origin);
|
|
13552
13607
|
localStorage.push({ origin, entries });
|
|
13553
13608
|
}
|
|
13554
|
-
const
|
|
13609
|
+
const snapshot2 = tabManager.snapshotSession(`Named session: ${normalizedName}`);
|
|
13555
13610
|
const domains = [...new Set(cookies.map((cookie) => cookie.domain))].sort();
|
|
13556
13611
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13557
13612
|
const data = {
|
|
@@ -13563,7 +13618,7 @@ async function saveNamedSession(tabManager, name) {
|
|
|
13563
13618
|
domains,
|
|
13564
13619
|
cookies,
|
|
13565
13620
|
localStorage,
|
|
13566
|
-
snapshot
|
|
13621
|
+
snapshot: snapshot2
|
|
13567
13622
|
};
|
|
13568
13623
|
writeSessionFile(getSessionPath(normalizedName), data);
|
|
13569
13624
|
return {
|
|
@@ -13918,8 +13973,8 @@ function compactSearchLikeResult(text) {
|
|
|
13918
13973
|
return limitText(cleaned, 16, 1400);
|
|
13919
13974
|
}
|
|
13920
13975
|
const summary = cleaned.slice(0, markerIndex).trim();
|
|
13921
|
-
const
|
|
13922
|
-
return [summary, compactReadPageResult(
|
|
13976
|
+
const snapshot2 = cleaned.slice(markerIndex + marker.length).trim();
|
|
13977
|
+
return [summary, compactReadPageResult(snapshot2)].filter(Boolean).join("\n\n");
|
|
13923
13978
|
}
|
|
13924
13979
|
function compactCurrentTabResult(text) {
|
|
13925
13980
|
try {
|
|
@@ -14968,7 +15023,7 @@ async function inspectElement(wc, selector, limit = 8) {
|
|
|
14968
15023
|
return lines.join("\n");
|
|
14969
15024
|
}
|
|
14970
15025
|
async function getLocaleSnapshot(wc) {
|
|
14971
|
-
const
|
|
15026
|
+
const snapshot2 = await executePageScript(
|
|
14972
15027
|
wc,
|
|
14973
15028
|
`
|
|
14974
15029
|
(function() {
|
|
@@ -14987,13 +15042,13 @@ async function getLocaleSnapshot(wc) {
|
|
|
14987
15042
|
label: "locale snapshot"
|
|
14988
15043
|
}
|
|
14989
15044
|
);
|
|
14990
|
-
if (!
|
|
15045
|
+
if (!snapshot2 || snapshot2 === PAGE_SCRIPT_TIMEOUT || typeof snapshot2 !== "object") {
|
|
14991
15046
|
return null;
|
|
14992
15047
|
}
|
|
14993
15048
|
return {
|
|
14994
|
-
lang: typeof
|
|
14995
|
-
url: typeof
|
|
14996
|
-
title: typeof
|
|
15049
|
+
lang: typeof snapshot2.lang === "string" ? snapshot2.lang.trim() : "",
|
|
15050
|
+
url: typeof snapshot2.url === "string" ? snapshot2.url : wc.getURL(),
|
|
15051
|
+
title: typeof snapshot2.title === "string" ? snapshot2.title : wc.getTitle()
|
|
14997
15052
|
};
|
|
14998
15053
|
}
|
|
14999
15054
|
function primaryLanguageTag(value) {
|
|
@@ -15009,31 +15064,31 @@ function localeChanged(before, after) {
|
|
|
15009
15064
|
const localeHint = /[?&](lang|locale|language|hl)=|\/(ja|jp|en|fr|de|es|it|ko|zh)(\/|$)/i;
|
|
15010
15065
|
return before.url !== after.url && localeHint.test(after.url);
|
|
15011
15066
|
}
|
|
15012
|
-
async function restoreLocaleSnapshot(wc,
|
|
15013
|
-
if (!
|
|
15067
|
+
async function restoreLocaleSnapshot(wc, snapshot2) {
|
|
15068
|
+
if (!snapshot2 || wc.isDestroyed()) return;
|
|
15014
15069
|
try {
|
|
15015
15070
|
if (typeof wc.canGoBack === "function" && wc.canGoBack()) {
|
|
15016
15071
|
wc.goBack();
|
|
15017
15072
|
await waitForLoad(wc, 3e3);
|
|
15018
15073
|
const reverted = await getLocaleSnapshot(wc);
|
|
15019
|
-
if (!localeChanged(
|
|
15074
|
+
if (!localeChanged(snapshot2, reverted)) {
|
|
15020
15075
|
return;
|
|
15021
15076
|
}
|
|
15022
15077
|
}
|
|
15023
15078
|
} catch (err) {
|
|
15024
15079
|
logger$c.warn("Failed to restore locale via history navigation, trying URL reload fallback:", err);
|
|
15025
15080
|
}
|
|
15026
|
-
if (
|
|
15081
|
+
if (snapshot2.url && snapshot2.url !== wc.getURL()) {
|
|
15027
15082
|
try {
|
|
15028
|
-
assertSafeURL(
|
|
15029
|
-
await wc.loadURL(
|
|
15083
|
+
assertSafeURL(snapshot2.url);
|
|
15084
|
+
await wc.loadURL(snapshot2.url);
|
|
15030
15085
|
await waitForLoad(wc, 3e3);
|
|
15031
15086
|
return;
|
|
15032
15087
|
} catch (err) {
|
|
15033
15088
|
logger$c.warn("Failed to restore locale via safe URL load, trying page reload fallback:", err);
|
|
15034
15089
|
}
|
|
15035
15090
|
}
|
|
15036
|
-
if (
|
|
15091
|
+
if (snapshot2.url) {
|
|
15037
15092
|
try {
|
|
15038
15093
|
await wc.reload();
|
|
15039
15094
|
await waitForLoad(wc, 3e3);
|
|
@@ -18983,9 +19038,9 @@ function capturePageToVault({
|
|
|
18983
19038
|
if (page.excerpt.trim()) {
|
|
18984
19039
|
bodyLines.push("## Excerpt", "", page.excerpt.trim(), "");
|
|
18985
19040
|
}
|
|
18986
|
-
const
|
|
18987
|
-
if (
|
|
18988
|
-
bodyLines.push("## Page Snapshot", "",
|
|
19041
|
+
const snapshot2 = trimContent(page.content);
|
|
19042
|
+
if (snapshot2) {
|
|
19043
|
+
bodyLines.push("## Page Snapshot", "", snapshot2, "");
|
|
18989
19044
|
}
|
|
18990
19045
|
return writeMemoryNote({
|
|
18991
19046
|
title: noteTitle,
|
|
@@ -19410,8 +19465,8 @@ Exception: ${result.exceptionDetails}`);
|
|
|
19410
19465
|
{},
|
|
19411
19466
|
async () => {
|
|
19412
19467
|
const session = getOrCreateSession(tabManager);
|
|
19413
|
-
const
|
|
19414
|
-
return JSON.stringify(
|
|
19468
|
+
const snapshot2 = await session.getPerformanceSnapshot();
|
|
19469
|
+
return JSON.stringify(snapshot2, null, 2);
|
|
19415
19470
|
}
|
|
19416
19471
|
);
|
|
19417
19472
|
}
|
|
@@ -19487,6 +19542,7 @@ function getOrCreateEncryptionKey(keyFilename) {
|
|
|
19487
19542
|
fs$1.mkdirSync(path$1.dirname(keyPath), { recursive: true });
|
|
19488
19543
|
const encrypted = electron.safeStorage.encryptString(key2.toString("utf-8"));
|
|
19489
19544
|
fs$1.writeFileSync(keyPath, encrypted, { mode: 384 });
|
|
19545
|
+
fs$1.chmodSync(keyPath, 384);
|
|
19490
19546
|
return key2;
|
|
19491
19547
|
}
|
|
19492
19548
|
function createEncryptDecrypt(keyFilename) {
|
|
@@ -19560,15 +19616,21 @@ function createVaultIO(vaultFilename, encrypt2, decrypt2) {
|
|
|
19560
19616
|
}
|
|
19561
19617
|
return { loadVault: loadVault2, saveVault: saveVault2, resetCache };
|
|
19562
19618
|
}
|
|
19563
|
-
function
|
|
19564
|
-
|
|
19565
|
-
|
|
19566
|
-
|
|
19567
|
-
|
|
19568
|
-
const
|
|
19569
|
-
return
|
|
19619
|
+
function normalizeCredentialHost(value) {
|
|
19620
|
+
try {
|
|
19621
|
+
const parsed = new URL(value.includes("://") ? value : `https://${value}`);
|
|
19622
|
+
return parsed.hostname.toLowerCase().replace(/^www\./, "");
|
|
19623
|
+
} catch {
|
|
19624
|
+
const normalized = value.toLowerCase().trim().replace(/^(https?:\/\/)?(www\.)?/, "").replace(/\/.*$/, "");
|
|
19625
|
+
return normalized && !normalized.includes(" ") ? normalized : null;
|
|
19570
19626
|
}
|
|
19571
|
-
|
|
19627
|
+
}
|
|
19628
|
+
function domainMatches(pattern, hostname) {
|
|
19629
|
+
const isWildcard = pattern.trim().startsWith("*.");
|
|
19630
|
+
const p = normalizeCredentialHost(isWildcard ? pattern.slice(2) : pattern);
|
|
19631
|
+
const h = normalizeCredentialHost(hostname);
|
|
19632
|
+
if (!p || !h) return false;
|
|
19633
|
+
return isWildcard ? h.endsWith("." + p) : p === h;
|
|
19572
19634
|
}
|
|
19573
19635
|
function generateTotpCode(secret) {
|
|
19574
19636
|
const epoch = Math.floor(Date.now() / 1e3);
|
|
@@ -19639,12 +19701,8 @@ function listEntries$1() {
|
|
|
19639
19701
|
return loadVault$1().map(({ password, totpSecret, ...rest }) => rest);
|
|
19640
19702
|
}
|
|
19641
19703
|
function findEntriesForDomain(url) {
|
|
19642
|
-
|
|
19643
|
-
|
|
19644
|
-
hostname = new URL(url).hostname;
|
|
19645
|
-
} catch {
|
|
19646
|
-
return [];
|
|
19647
|
-
}
|
|
19704
|
+
const hostname = normalizeCredentialHost(url);
|
|
19705
|
+
if (!hostname) return [];
|
|
19648
19706
|
return loadVault$1().filter((e) => domainMatches(e.domainPattern, hostname)).map(({ password, totpSecret, ...rest }) => rest);
|
|
19649
19707
|
}
|
|
19650
19708
|
function addEntry(entry) {
|
|
@@ -19767,12 +19825,7 @@ const auditLog = createAuditLog(
|
|
|
19767
19825
|
AUDIT_MAX_ENTRIES
|
|
19768
19826
|
);
|
|
19769
19827
|
function extractDomain(url) {
|
|
19770
|
-
|
|
19771
|
-
const parsed = new URL(url.startsWith("http") ? url : `https://${url}`);
|
|
19772
|
-
return parsed.hostname.toLowerCase();
|
|
19773
|
-
} catch {
|
|
19774
|
-
return url.toLowerCase().replace(/^(https?:\/\/)?(www\.)?/, "").replace(/\/.*$/, "");
|
|
19775
|
-
}
|
|
19828
|
+
return normalizeCredentialHost(url) ?? "";
|
|
19776
19829
|
}
|
|
19777
19830
|
function listEntries() {
|
|
19778
19831
|
return loadVault().map(({ password, totpSecret, ...rest }) => rest);
|
|
@@ -19957,6 +20010,7 @@ function writeMcpAuthFile(endpoint, token) {
|
|
|
19957
20010
|
JSON.stringify({ endpoint, token, pid: process.pid }, null, 2) + "\n",
|
|
19958
20011
|
{ mode: 384 }
|
|
19959
20012
|
);
|
|
20013
|
+
fs$1.chmodSync(filePath2, 384);
|
|
19960
20014
|
} catch (err) {
|
|
19961
20015
|
logger$9.warn("Failed to write auth file:", err);
|
|
19962
20016
|
}
|
|
@@ -19982,10 +20036,18 @@ function clearMcpAuthFile() {
|
|
|
19982
20036
|
) + "\n",
|
|
19983
20037
|
{ mode: 384 }
|
|
19984
20038
|
);
|
|
20039
|
+
fs$1.chmodSync(filePath2, 384);
|
|
19985
20040
|
} catch (err) {
|
|
19986
20041
|
logger$9.warn("Failed to clear auth file:", err);
|
|
19987
20042
|
}
|
|
19988
20043
|
}
|
|
20044
|
+
function regenerateMcpAuthToken() {
|
|
20045
|
+
const endpoint = getRuntimeHealth().mcp.endpoint;
|
|
20046
|
+
if (!httpServer || !endpoint) return null;
|
|
20047
|
+
mcpAuthToken = crypto$2.randomBytes(32).toString("hex");
|
|
20048
|
+
writeMcpAuthFile(endpoint, mcpAuthToken);
|
|
20049
|
+
return { endpoint };
|
|
20050
|
+
}
|
|
19989
20051
|
function asTextResponse(text) {
|
|
19990
20052
|
return { content: [{ type: "text", text }] };
|
|
19991
20053
|
}
|
|
@@ -20011,6 +20073,11 @@ function asPromptResponse(text) {
|
|
|
20011
20073
|
function isDangerousMcpAction(name) {
|
|
20012
20074
|
return name === "close_tab" || isDangerousAction(name);
|
|
20013
20075
|
}
|
|
20076
|
+
function requiresExplicitMcpApproval(name, args) {
|
|
20077
|
+
if (name === "delete_session" || name === "close_tab" || name === "load_session") return true;
|
|
20078
|
+
if (name === "remove_folder" && args.delete_contents === true) return true;
|
|
20079
|
+
return false;
|
|
20080
|
+
}
|
|
20014
20081
|
function getActiveTabSummary(tabManager) {
|
|
20015
20082
|
const activeTab = tabManager.getActiveTab();
|
|
20016
20083
|
const activeTabId = tabManager.getActiveTabId();
|
|
@@ -20099,6 +20166,7 @@ async function withAction(runtime2, tabManager, name, args, executor) {
|
|
|
20099
20166
|
args,
|
|
20100
20167
|
tabId: tabManager.getActiveTabId(),
|
|
20101
20168
|
dangerous: isDangerousMcpAction(name),
|
|
20169
|
+
requiresApproval: requiresExplicitMcpApproval(name, args),
|
|
20102
20170
|
executor
|
|
20103
20171
|
});
|
|
20104
20172
|
const stateInfo = await getPostActionState(tabManager, name);
|
|
@@ -24122,6 +24190,16 @@ function registerScheduleHandlers(windowState, runtime2, sendToAll) {
|
|
|
24122
24190
|
return true;
|
|
24123
24191
|
});
|
|
24124
24192
|
}
|
|
24193
|
+
const trustedIpcSenderIds = /* @__PURE__ */ new Set();
|
|
24194
|
+
function registerTrustedIpcSender(wc) {
|
|
24195
|
+
trustedIpcSenderIds.add(wc.id);
|
|
24196
|
+
wc.once("destroyed", () => trustedIpcSenderIds.delete(wc.id));
|
|
24197
|
+
}
|
|
24198
|
+
function assertTrustedIpcSender(event) {
|
|
24199
|
+
if (!trustedIpcSenderIds.has(event.sender.id)) {
|
|
24200
|
+
throw new Error("Blocked IPC from untrusted renderer");
|
|
24201
|
+
}
|
|
24202
|
+
}
|
|
24125
24203
|
function assertString(value, name) {
|
|
24126
24204
|
if (typeof value !== "string") throw new Error(`${name} must be a string`);
|
|
24127
24205
|
}
|
|
@@ -24537,13 +24615,26 @@ function registerAutofillHandlers(windowState) {
|
|
|
24537
24615
|
});
|
|
24538
24616
|
}
|
|
24539
24617
|
function registerPageDiffHandlers(windowState, sendToRendererViews) {
|
|
24540
|
-
|
|
24618
|
+
const pageEventBuckets = /* @__PURE__ */ new Map();
|
|
24619
|
+
const allowPageEvent = (webContentsId) => {
|
|
24620
|
+
const now = Date.now();
|
|
24621
|
+
const bucket = pageEventBuckets.get(webContentsId);
|
|
24622
|
+
if (!bucket || bucket.resetAt <= now) {
|
|
24623
|
+
pageEventBuckets.set(webContentsId, { count: 1, resetAt: now + 1e3 });
|
|
24624
|
+
return true;
|
|
24625
|
+
}
|
|
24626
|
+
bucket.count += 1;
|
|
24627
|
+
return bucket.count <= 20;
|
|
24628
|
+
};
|
|
24629
|
+
electron.ipcMain.handle(Channels.PAGE_DIFF_GET, (event) => {
|
|
24630
|
+
assertTrustedIpcSender(event);
|
|
24541
24631
|
const activeTab = windowState.tabManager.getActiveTab();
|
|
24542
24632
|
const wc = activeTab?.view.webContents;
|
|
24543
24633
|
if (!wc) return null;
|
|
24544
24634
|
return getLatestPageDiff(wc.getURL());
|
|
24545
24635
|
});
|
|
24546
|
-
electron.ipcMain.handle(Channels.PAGE_DIFF_HISTORY, () => {
|
|
24636
|
+
electron.ipcMain.handle(Channels.PAGE_DIFF_HISTORY, (event) => {
|
|
24637
|
+
assertTrustedIpcSender(event);
|
|
24547
24638
|
try {
|
|
24548
24639
|
if (!isPremiumActiveState(getPremiumState())) {
|
|
24549
24640
|
return { error: "Premium required" };
|
|
@@ -24559,21 +24650,25 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
|
|
|
24559
24650
|
electron.ipcMain.on(Channels.PAGE_DIFF_ACTIVITY, (event) => {
|
|
24560
24651
|
const wc = event.sender;
|
|
24561
24652
|
if (!wc || wc.isDestroyed()) return;
|
|
24653
|
+
if (!allowPageEvent(wc.id)) return;
|
|
24562
24654
|
notePageMutationActivity(wc, sendToRendererViews);
|
|
24563
24655
|
});
|
|
24564
24656
|
electron.ipcMain.on(Channels.PAGE_DIFF_DIRTY, (event) => {
|
|
24565
24657
|
const wc = event.sender;
|
|
24566
24658
|
if (!wc || wc.isDestroyed()) return;
|
|
24659
|
+
if (!allowPageEvent(wc.id)) return;
|
|
24567
24660
|
schedulePageSnapshotCapture(wc, sendToRendererViews);
|
|
24568
24661
|
});
|
|
24569
24662
|
}
|
|
24570
24663
|
function registerVaultHandlers() {
|
|
24571
|
-
electron.ipcMain.handle(Channels.VAULT_LIST, () => {
|
|
24664
|
+
electron.ipcMain.handle(Channels.VAULT_LIST, (event) => {
|
|
24665
|
+
assertTrustedIpcSender(event);
|
|
24572
24666
|
return listEntries$1();
|
|
24573
24667
|
});
|
|
24574
24668
|
electron.ipcMain.handle(
|
|
24575
24669
|
Channels.VAULT_ADD,
|
|
24576
|
-
(
|
|
24670
|
+
(event, entry) => {
|
|
24671
|
+
assertTrustedIpcSender(event);
|
|
24577
24672
|
if (!entry || typeof entry !== "object") {
|
|
24578
24673
|
throw new Error("Invalid vault entry");
|
|
24579
24674
|
}
|
|
@@ -24598,7 +24693,8 @@ function registerVaultHandlers() {
|
|
|
24598
24693
|
);
|
|
24599
24694
|
electron.ipcMain.handle(
|
|
24600
24695
|
Channels.VAULT_UPDATE,
|
|
24601
|
-
(
|
|
24696
|
+
(event, id, updates) => {
|
|
24697
|
+
assertTrustedIpcSender(event);
|
|
24602
24698
|
assertString(id, "id");
|
|
24603
24699
|
if (!updates || typeof updates !== "object") {
|
|
24604
24700
|
throw new Error("Invalid updates");
|
|
@@ -24606,12 +24702,14 @@ function registerVaultHandlers() {
|
|
|
24606
24702
|
return updateEntry$1(id, updates) !== null;
|
|
24607
24703
|
}
|
|
24608
24704
|
);
|
|
24609
|
-
electron.ipcMain.handle(Channels.VAULT_REMOVE, (
|
|
24705
|
+
electron.ipcMain.handle(Channels.VAULT_REMOVE, (event, id) => {
|
|
24706
|
+
assertTrustedIpcSender(event);
|
|
24610
24707
|
assertString(id, "id");
|
|
24611
24708
|
trackVaultAction("credential_removed");
|
|
24612
24709
|
return removeEntry$1(id);
|
|
24613
24710
|
});
|
|
24614
|
-
electron.ipcMain.handle(Channels.VAULT_AUDIT_LOG, (
|
|
24711
|
+
electron.ipcMain.handle(Channels.VAULT_AUDIT_LOG, (event, limit) => {
|
|
24712
|
+
assertTrustedIpcSender(event);
|
|
24615
24713
|
return readAuditLog$1(limit);
|
|
24616
24714
|
});
|
|
24617
24715
|
}
|
|
@@ -24630,17 +24728,20 @@ function normalizeTags(value) {
|
|
|
24630
24728
|
return tags.length > 0 ? tags : void 0;
|
|
24631
24729
|
}
|
|
24632
24730
|
function registerHumanVaultHandlers() {
|
|
24633
|
-
electron.ipcMain.handle(Channels.HUMAN_VAULT_LIST, (
|
|
24731
|
+
electron.ipcMain.handle(Channels.HUMAN_VAULT_LIST, (event, domain) => {
|
|
24732
|
+
assertTrustedIpcSender(event);
|
|
24634
24733
|
if (domain !== void 0) assertString(domain, "domain");
|
|
24635
24734
|
return domain ? findForDomain(domain) : listEntries();
|
|
24636
24735
|
});
|
|
24637
|
-
electron.ipcMain.handle(Channels.HUMAN_VAULT_GET, (
|
|
24736
|
+
electron.ipcMain.handle(Channels.HUMAN_VAULT_GET, (event, id) => {
|
|
24737
|
+
assertTrustedIpcSender(event);
|
|
24638
24738
|
assertString(id, "id");
|
|
24639
24739
|
return getEntrySafe(id);
|
|
24640
24740
|
});
|
|
24641
24741
|
electron.ipcMain.handle(
|
|
24642
24742
|
Channels.HUMAN_VAULT_SAVE,
|
|
24643
|
-
(
|
|
24743
|
+
(event, input) => {
|
|
24744
|
+
assertTrustedIpcSender(event);
|
|
24644
24745
|
if (!input || typeof input !== "object") {
|
|
24645
24746
|
throw new Error("Invalid credential entry");
|
|
24646
24747
|
}
|
|
@@ -24670,7 +24771,8 @@ function registerHumanVaultHandlers() {
|
|
|
24670
24771
|
);
|
|
24671
24772
|
electron.ipcMain.handle(
|
|
24672
24773
|
Channels.HUMAN_VAULT_UPDATE,
|
|
24673
|
-
(
|
|
24774
|
+
(event, id, updates) => {
|
|
24775
|
+
assertTrustedIpcSender(event);
|
|
24674
24776
|
assertString(id, "id");
|
|
24675
24777
|
if (!updates || typeof updates !== "object") {
|
|
24676
24778
|
throw new Error("Invalid updates");
|
|
@@ -24712,26 +24814,31 @@ function registerHumanVaultHandlers() {
|
|
|
24712
24814
|
return safe;
|
|
24713
24815
|
}
|
|
24714
24816
|
);
|
|
24715
|
-
electron.ipcMain.handle(Channels.HUMAN_VAULT_REMOVE, (
|
|
24817
|
+
electron.ipcMain.handle(Channels.HUMAN_VAULT_REMOVE, (event, id) => {
|
|
24818
|
+
assertTrustedIpcSender(event);
|
|
24716
24819
|
assertString(id, "id");
|
|
24717
24820
|
return removeEntry(id);
|
|
24718
24821
|
});
|
|
24719
|
-
electron.ipcMain.handle(Channels.HUMAN_VAULT_AUDIT_LOG, (
|
|
24822
|
+
electron.ipcMain.handle(Channels.HUMAN_VAULT_AUDIT_LOG, (event, limit) => {
|
|
24823
|
+
assertTrustedIpcSender(event);
|
|
24720
24824
|
return readAuditLog(limit);
|
|
24721
24825
|
});
|
|
24722
24826
|
}
|
|
24723
24827
|
function registerWindowControlHandlers(mainWindow) {
|
|
24724
|
-
electron.ipcMain.handle(Channels.WINDOW_MINIMIZE, () => {
|
|
24828
|
+
electron.ipcMain.handle(Channels.WINDOW_MINIMIZE, (event) => {
|
|
24829
|
+
assertTrustedIpcSender(event);
|
|
24725
24830
|
mainWindow.minimize();
|
|
24726
24831
|
});
|
|
24727
|
-
electron.ipcMain.handle(Channels.WINDOW_MAXIMIZE, () => {
|
|
24832
|
+
electron.ipcMain.handle(Channels.WINDOW_MAXIMIZE, (event) => {
|
|
24833
|
+
assertTrustedIpcSender(event);
|
|
24728
24834
|
if (mainWindow.isMaximized()) {
|
|
24729
24835
|
mainWindow.unmaximize();
|
|
24730
24836
|
} else {
|
|
24731
24837
|
mainWindow.maximize();
|
|
24732
24838
|
}
|
|
24733
24839
|
});
|
|
24734
|
-
electron.ipcMain.handle(Channels.WINDOW_CLOSE, () => {
|
|
24840
|
+
electron.ipcMain.handle(Channels.WINDOW_CLOSE, (event) => {
|
|
24841
|
+
assertTrustedIpcSender(event);
|
|
24735
24842
|
mainWindow.close();
|
|
24736
24843
|
});
|
|
24737
24844
|
}
|
|
@@ -24870,6 +24977,32 @@ function installAdBlockingForSession(ses, tabManager) {
|
|
|
24870
24977
|
});
|
|
24871
24978
|
}
|
|
24872
24979
|
const filePath$1 = () => path$1.join(electron.app.getPath("userData"), "vessel-downloads.json");
|
|
24980
|
+
const EXECUTABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
24981
|
+
".appimage",
|
|
24982
|
+
".bat",
|
|
24983
|
+
".cmd",
|
|
24984
|
+
".command",
|
|
24985
|
+
".desktop",
|
|
24986
|
+
".exe",
|
|
24987
|
+
".msi",
|
|
24988
|
+
".ps1",
|
|
24989
|
+
".scr",
|
|
24990
|
+
".sh"
|
|
24991
|
+
]);
|
|
24992
|
+
function hasMisleadingDoubleExtension(filename) {
|
|
24993
|
+
return /\.(pdf|docx?|xlsx?|pptx?|png|jpe?g|gif|txt|zip)\.(exe|msi|bat|cmd|ps1|sh|scr|appimage)$/i.test(filename);
|
|
24994
|
+
}
|
|
24995
|
+
function isExecutableDownload(savePath) {
|
|
24996
|
+
return EXECUTABLE_EXTENSIONS.has(path$1.extname(savePath).toLowerCase());
|
|
24997
|
+
}
|
|
24998
|
+
function executableWarningDetail(item) {
|
|
24999
|
+
return [
|
|
25000
|
+
"This file can run code on your computer. Only open it if you trust the source.",
|
|
25001
|
+
item.url ? `Source: ${item.url}` : null,
|
|
25002
|
+
item.mimeType ? `Type: ${item.mimeType}` : null,
|
|
25003
|
+
hasMisleadingDoubleExtension(item.filename) ? "Warning: this filename uses a misleading double extension." : null
|
|
25004
|
+
].filter(Boolean).join("\n");
|
|
25005
|
+
}
|
|
24873
25006
|
function parse(raw) {
|
|
24874
25007
|
if (!raw || typeof raw !== "object") return { items: [] };
|
|
24875
25008
|
const items = Array.isArray(raw.items) ? raw.items : [];
|
|
@@ -24888,13 +25021,13 @@ function persist() {
|
|
|
24888
25021
|
persistence$1.schedule();
|
|
24889
25022
|
}
|
|
24890
25023
|
function emit$1() {
|
|
24891
|
-
broadcaster$1?.(Channels.DOWNLOADS_UPDATE,
|
|
25024
|
+
broadcaster$1?.(Channels.DOWNLOADS_UPDATE, listDownloads());
|
|
24892
25025
|
}
|
|
24893
25026
|
function setDownloadBroadcaster(fn) {
|
|
24894
25027
|
broadcaster$1 = fn;
|
|
24895
25028
|
}
|
|
24896
25029
|
function listDownloads() {
|
|
24897
|
-
return state.items;
|
|
25030
|
+
return state.items.map((item) => ({ ...item }));
|
|
24898
25031
|
}
|
|
24899
25032
|
function upsertDownload(input) {
|
|
24900
25033
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -24918,7 +25051,19 @@ function clearDownloads() {
|
|
|
24918
25051
|
}
|
|
24919
25052
|
async function openDownload(id) {
|
|
24920
25053
|
const item = state.items.find((d) => d.id === id);
|
|
24921
|
-
if (!item || !fs$1.existsSync(item.savePath)) return false;
|
|
25054
|
+
if (!item || item.state !== "completed" || !fs$1.existsSync(item.savePath)) return false;
|
|
25055
|
+
if (isExecutableDownload(item.savePath)) {
|
|
25056
|
+
const result = electron.dialog.showMessageBoxSync({
|
|
25057
|
+
type: "warning",
|
|
25058
|
+
buttons: ["Cancel", "Open Anyway"],
|
|
25059
|
+
defaultId: 0,
|
|
25060
|
+
cancelId: 0,
|
|
25061
|
+
title: "Open executable download?",
|
|
25062
|
+
message: `Open ${item.filename}?`,
|
|
25063
|
+
detail: executableWarningDetail(item)
|
|
25064
|
+
});
|
|
25065
|
+
if (result !== 1) return false;
|
|
25066
|
+
}
|
|
24922
25067
|
return await electron.shell.openPath(item.savePath) === "";
|
|
24923
25068
|
}
|
|
24924
25069
|
async function showDownloadInFolder(id) {
|
|
@@ -24969,6 +25114,8 @@ function installDownloadHandlerForSession(targetSession, chromeView) {
|
|
|
24969
25114
|
const info = {
|
|
24970
25115
|
filename,
|
|
24971
25116
|
savePath,
|
|
25117
|
+
url: item.getURL(),
|
|
25118
|
+
mimeType: typeof item.getMimeType === "function" ? item.getMimeType() : void 0,
|
|
24972
25119
|
totalBytes: item.getTotalBytes(),
|
|
24973
25120
|
receivedBytes: 0,
|
|
24974
25121
|
state: "progressing"
|
|
@@ -25016,9 +25163,9 @@ function loadRenderers(chromeView, sidebarView, devtoolsPanelView) {
|
|
|
25016
25163
|
const sidebarUrl = rendererUrlFor("sidebar");
|
|
25017
25164
|
const devtoolsUrl = rendererUrlFor("devtools");
|
|
25018
25165
|
if (chromeUrl && sidebarUrl && devtoolsUrl) {
|
|
25019
|
-
chromeView.webContents
|
|
25020
|
-
sidebarView.webContents
|
|
25021
|
-
devtoolsPanelView.webContents
|
|
25166
|
+
void loadTrustedAppURL(chromeView.webContents, chromeUrl);
|
|
25167
|
+
void loadTrustedAppURL(sidebarView.webContents, sidebarUrl);
|
|
25168
|
+
void loadTrustedAppURL(devtoolsPanelView.webContents, devtoolsUrl);
|
|
25022
25169
|
} else {
|
|
25023
25170
|
const rendererFile = resolveRendererFile();
|
|
25024
25171
|
chromeView.webContents.loadFile(rendererFile, {
|
|
@@ -25244,7 +25391,7 @@ function loadPrivateRenderer(chromeView) {
|
|
|
25244
25391
|
const url = new URL(devUrl);
|
|
25245
25392
|
url.searchParams.set("view", "chrome");
|
|
25246
25393
|
url.searchParams.set("private", "1");
|
|
25247
|
-
chromeView.webContents
|
|
25394
|
+
void loadTrustedAppURL(chromeView.webContents, url.toString());
|
|
25248
25395
|
} else {
|
|
25249
25396
|
chromeView.webContents.loadFile(resolveRendererFile(), {
|
|
25250
25397
|
query: { view: "chrome", private: "1" }
|
|
@@ -25479,7 +25626,7 @@ function loadSecondaryRenderer(chromeView) {
|
|
|
25479
25626
|
const url = new URL(devUrl);
|
|
25480
25627
|
url.searchParams.set("view", "chrome");
|
|
25481
25628
|
url.searchParams.set("secondary", "1");
|
|
25482
|
-
chromeView.webContents
|
|
25629
|
+
void loadTrustedAppURL(chromeView.webContents, url.toString());
|
|
25483
25630
|
} else {
|
|
25484
25631
|
chromeView.webContents.loadFile(resolveRendererFile(), {
|
|
25485
25632
|
query: { view: "chrome", secondary: "1" }
|
|
@@ -25699,7 +25846,8 @@ function registerBookmarkHandlers() {
|
|
|
25699
25846
|
});
|
|
25700
25847
|
electron.ipcMain.handle(
|
|
25701
25848
|
Channels.BOOKMARKS_EXPORT_HTML,
|
|
25702
|
-
async (
|
|
25849
|
+
async (event, options) => {
|
|
25850
|
+
assertTrustedIpcSender(event);
|
|
25703
25851
|
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
25704
25852
|
title: "Export Bookmarks",
|
|
25705
25853
|
defaultPath: "vessel-bookmarks.html",
|
|
@@ -25717,7 +25865,8 @@ function registerBookmarkHandlers() {
|
|
|
25717
25865
|
};
|
|
25718
25866
|
}
|
|
25719
25867
|
);
|
|
25720
|
-
electron.ipcMain.handle(Channels.BOOKMARKS_EXPORT_JSON, async () => {
|
|
25868
|
+
electron.ipcMain.handle(Channels.BOOKMARKS_EXPORT_JSON, async (event) => {
|
|
25869
|
+
assertTrustedIpcSender(event);
|
|
25721
25870
|
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
25722
25871
|
title: "Export Vessel Bookmark Archive",
|
|
25723
25872
|
defaultPath: "vessel-bookmarks.json",
|
|
@@ -25734,7 +25883,8 @@ function registerBookmarkHandlers() {
|
|
|
25734
25883
|
});
|
|
25735
25884
|
electron.ipcMain.handle(
|
|
25736
25885
|
Channels.FOLDER_EXPORT_HTML,
|
|
25737
|
-
async (
|
|
25886
|
+
async (event, folderId, options) => {
|
|
25887
|
+
assertTrustedIpcSender(event);
|
|
25738
25888
|
const folder = getFolder(folderId);
|
|
25739
25889
|
if (!folder) return null;
|
|
25740
25890
|
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
@@ -25755,7 +25905,8 @@ function registerBookmarkHandlers() {
|
|
|
25755
25905
|
};
|
|
25756
25906
|
}
|
|
25757
25907
|
);
|
|
25758
|
-
electron.ipcMain.handle(Channels.BOOKMARKS_IMPORT_HTML, async () => {
|
|
25908
|
+
electron.ipcMain.handle(Channels.BOOKMARKS_IMPORT_HTML, async (event) => {
|
|
25909
|
+
assertTrustedIpcSender(event);
|
|
25759
25910
|
const { canceled, filePaths } = await electron.dialog.showOpenDialog({
|
|
25760
25911
|
title: "Import Bookmarks",
|
|
25761
25912
|
filters: [
|
|
@@ -25768,7 +25919,8 @@ function registerBookmarkHandlers() {
|
|
|
25768
25919
|
trackBookmarkAction("import");
|
|
25769
25920
|
return importBookmarksFromHtml(content);
|
|
25770
25921
|
});
|
|
25771
|
-
electron.ipcMain.handle(Channels.BOOKMARKS_IMPORT_JSON, async () => {
|
|
25922
|
+
electron.ipcMain.handle(Channels.BOOKMARKS_IMPORT_JSON, async (event) => {
|
|
25923
|
+
assertTrustedIpcSender(event);
|
|
25772
25924
|
const { canceled, filePaths } = await electron.dialog.showOpenDialog({
|
|
25773
25925
|
title: "Import Bookmarks",
|
|
25774
25926
|
filters: [
|
|
@@ -25799,10 +25951,12 @@ function registerHistoryHandlers() {
|
|
|
25799
25951
|
electron.ipcMain.handle(Channels.HISTORY_SEARCH, (_, query) => {
|
|
25800
25952
|
return search(query);
|
|
25801
25953
|
});
|
|
25802
|
-
electron.ipcMain.handle(Channels.HISTORY_CLEAR, () => {
|
|
25954
|
+
electron.ipcMain.handle(Channels.HISTORY_CLEAR, (event) => {
|
|
25955
|
+
assertTrustedIpcSender(event);
|
|
25803
25956
|
clearAll$1();
|
|
25804
25957
|
});
|
|
25805
|
-
electron.ipcMain.handle(Channels.HISTORY_EXPORT_HTML, async () => {
|
|
25958
|
+
electron.ipcMain.handle(Channels.HISTORY_EXPORT_HTML, async (event) => {
|
|
25959
|
+
assertTrustedIpcSender(event);
|
|
25806
25960
|
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
25807
25961
|
title: "Export History",
|
|
25808
25962
|
defaultPath: "vessel-history.html",
|
|
@@ -25813,7 +25967,8 @@ function registerHistoryHandlers() {
|
|
|
25813
25967
|
await fs.promises.writeFile(filePath2, content, "utf-8");
|
|
25814
25968
|
return { filePath: filePath2, count: getState$1().entries.length };
|
|
25815
25969
|
});
|
|
25816
|
-
electron.ipcMain.handle(Channels.HISTORY_EXPORT_JSON, async () => {
|
|
25970
|
+
electron.ipcMain.handle(Channels.HISTORY_EXPORT_JSON, async (event) => {
|
|
25971
|
+
assertTrustedIpcSender(event);
|
|
25817
25972
|
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
25818
25973
|
title: "Export History",
|
|
25819
25974
|
defaultPath: "vessel-history.json",
|
|
@@ -25824,7 +25979,8 @@ function registerHistoryHandlers() {
|
|
|
25824
25979
|
await fs.promises.writeFile(filePath2, content, "utf-8");
|
|
25825
25980
|
return { filePath: filePath2, count: getState$1().entries.length };
|
|
25826
25981
|
});
|
|
25827
|
-
electron.ipcMain.handle(Channels.HISTORY_IMPORT, async () => {
|
|
25982
|
+
electron.ipcMain.handle(Channels.HISTORY_IMPORT, async (event) => {
|
|
25983
|
+
assertTrustedIpcSender(event);
|
|
25828
25984
|
const { canceled, filePaths } = await electron.dialog.showOpenDialog({
|
|
25829
25985
|
title: "Import History",
|
|
25830
25986
|
filters: [
|
|
@@ -26035,7 +26191,8 @@ function buildCertificateDetailsHtml(state2) {
|
|
|
26035
26191
|
</html>`;
|
|
26036
26192
|
}
|
|
26037
26193
|
function registerSecurityHandlers(tabManager) {
|
|
26038
|
-
electron.ipcMain.handle(Channels.SECURITY_SHOW_DETAILS, async (
|
|
26194
|
+
electron.ipcMain.handle(Channels.SECURITY_SHOW_DETAILS, async (event, state2) => {
|
|
26195
|
+
assertTrustedIpcSender(event);
|
|
26039
26196
|
const domain = (() => {
|
|
26040
26197
|
try {
|
|
26041
26198
|
return new URL(state2.url).hostname || state2.url;
|
|
@@ -26056,13 +26213,15 @@ function registerSecurityHandlers(tabManager) {
|
|
|
26056
26213
|
spellcheck: false
|
|
26057
26214
|
}
|
|
26058
26215
|
});
|
|
26059
|
-
void win.
|
|
26216
|
+
void loadInternalDataURL(win.webContents, `data:text/html;charset=utf-8,${encodeURIComponent(content)}`);
|
|
26060
26217
|
});
|
|
26061
|
-
electron.ipcMain.handle(Channels.SECURITY_PROCEED_ANYWAY, (
|
|
26218
|
+
electron.ipcMain.handle(Channels.SECURITY_PROCEED_ANYWAY, (event, tabId) => {
|
|
26219
|
+
assertTrustedIpcSender(event);
|
|
26062
26220
|
assertString(tabId, "tabId");
|
|
26063
26221
|
tabManager.proceedAnyway(tabId);
|
|
26064
26222
|
});
|
|
26065
|
-
electron.ipcMain.handle(Channels.SECURITY_GO_BACK_TO_SAFETY, (
|
|
26223
|
+
electron.ipcMain.handle(Channels.SECURITY_GO_BACK_TO_SAFETY, (event, tabId) => {
|
|
26224
|
+
assertTrustedIpcSender(event);
|
|
26066
26225
|
assertString(tabId, "tabId");
|
|
26067
26226
|
tabManager.goBackToSafety(tabId);
|
|
26068
26227
|
});
|
|
@@ -26070,6 +26229,7 @@ function registerSecurityHandlers(tabManager) {
|
|
|
26070
26229
|
const logger$5 = createLogger("CodexIPC");
|
|
26071
26230
|
function registerCodexHandlers() {
|
|
26072
26231
|
electron.ipcMain.handle(Channels.CODEX_START_AUTH, async (event) => {
|
|
26232
|
+
assertTrustedIpcSender(event);
|
|
26073
26233
|
const wc = event.sender;
|
|
26074
26234
|
if (!wc || wc.isDestroyed()) {
|
|
26075
26235
|
return {
|
|
@@ -26100,29 +26260,57 @@ function registerCodexHandlers() {
|
|
|
26100
26260
|
};
|
|
26101
26261
|
}
|
|
26102
26262
|
});
|
|
26103
|
-
electron.ipcMain.handle(Channels.CODEX_CANCEL_AUTH, () => {
|
|
26263
|
+
electron.ipcMain.handle(Channels.CODEX_CANCEL_AUTH, (event) => {
|
|
26264
|
+
assertTrustedIpcSender(event);
|
|
26104
26265
|
cancelCodexOAuth();
|
|
26105
26266
|
return { ok: true };
|
|
26106
26267
|
});
|
|
26107
|
-
electron.ipcMain.handle(Channels.CODEX_DISCONNECT, () => {
|
|
26268
|
+
electron.ipcMain.handle(Channels.CODEX_DISCONNECT, (event) => {
|
|
26269
|
+
assertTrustedIpcSender(event);
|
|
26108
26270
|
clearStoredCodexTokens();
|
|
26109
26271
|
return { ok: true };
|
|
26110
26272
|
});
|
|
26111
26273
|
}
|
|
26112
26274
|
const filePath = () => path$1.join(electron.app.getPath("userData"), "vessel-permissions.json");
|
|
26275
|
+
const ALLOWED_PERMISSION_TYPES = /* @__PURE__ */ new Set([
|
|
26276
|
+
"clipboard-read",
|
|
26277
|
+
"fullscreen",
|
|
26278
|
+
"geolocation",
|
|
26279
|
+
"media",
|
|
26280
|
+
"midiSysex",
|
|
26281
|
+
"notifications",
|
|
26282
|
+
"pointerLock"
|
|
26283
|
+
]);
|
|
26284
|
+
function parseOrigin(value) {
|
|
26285
|
+
try {
|
|
26286
|
+
const origin = new URL(value).origin;
|
|
26287
|
+
return origin === "null" ? null : origin;
|
|
26288
|
+
} catch {
|
|
26289
|
+
return null;
|
|
26290
|
+
}
|
|
26291
|
+
}
|
|
26292
|
+
function isPermissionRecord(value) {
|
|
26293
|
+
if (!value || typeof value !== "object") return false;
|
|
26294
|
+
const record = value;
|
|
26295
|
+
return typeof record.origin === "string" && parseOrigin(record.origin) === record.origin && typeof record.permission === "string" && ALLOWED_PERMISSION_TYPES.has(record.permission) && (record.decision === "allow" || record.decision === "deny") && typeof record.updatedAt === "string";
|
|
26296
|
+
}
|
|
26113
26297
|
let records = loadJsonFile({
|
|
26114
26298
|
filePath: filePath(),
|
|
26115
26299
|
fallback: [],
|
|
26116
|
-
parse: (raw) => Array.isArray(raw) ? raw : []
|
|
26300
|
+
parse: (raw) => Array.isArray(raw) ? raw.filter(isPermissionRecord) : []
|
|
26117
26301
|
});
|
|
26118
26302
|
const persistence = createDebouncedJsonPersistence({ debounceMs: 250, filePath: filePath(), getValue: () => records, logLabel: "permissions" });
|
|
26303
|
+
const sessionDecisions = /* @__PURE__ */ new Map();
|
|
26119
26304
|
let broadcaster = null;
|
|
26120
26305
|
function key(origin, permission) {
|
|
26121
26306
|
return `${origin}
|
|
26122
26307
|
${permission}`;
|
|
26123
26308
|
}
|
|
26309
|
+
function snapshot() {
|
|
26310
|
+
return records.map((record) => ({ ...record }));
|
|
26311
|
+
}
|
|
26124
26312
|
function emit() {
|
|
26125
|
-
broadcaster?.(Channels.PERMISSIONS_GET,
|
|
26313
|
+
broadcaster?.(Channels.PERMISSIONS_GET, snapshot());
|
|
26126
26314
|
}
|
|
26127
26315
|
function save(origin, permission, decision) {
|
|
26128
26316
|
const k = key(origin, permission);
|
|
@@ -26134,15 +26322,21 @@ function save(origin, permission, decision) {
|
|
|
26134
26322
|
emit();
|
|
26135
26323
|
}
|
|
26136
26324
|
function listPermissions() {
|
|
26137
|
-
return
|
|
26325
|
+
return snapshot();
|
|
26138
26326
|
}
|
|
26139
26327
|
function clearPermissions() {
|
|
26140
26328
|
records = [];
|
|
26329
|
+
sessionDecisions.clear();
|
|
26141
26330
|
persistence.schedule();
|
|
26142
26331
|
emit();
|
|
26143
26332
|
}
|
|
26144
26333
|
function clearPermissionsForOrigin(origin) {
|
|
26334
|
+
if (!parseOrigin(origin)) return;
|
|
26145
26335
|
records = records.filter((record) => record.origin !== origin);
|
|
26336
|
+
for (const storedKey of sessionDecisions.keys()) {
|
|
26337
|
+
if (storedKey.startsWith(`${origin}
|
|
26338
|
+
`)) sessionDecisions.delete(storedKey);
|
|
26339
|
+
}
|
|
26146
26340
|
persistence.schedule();
|
|
26147
26341
|
emit();
|
|
26148
26342
|
}
|
|
@@ -26151,27 +26345,54 @@ function setPermissionBroadcaster(fn) {
|
|
|
26151
26345
|
}
|
|
26152
26346
|
function installPermissionHandler() {
|
|
26153
26347
|
electron.session.defaultSession.setPermissionRequestHandler((webContents, permission, callback, details) => {
|
|
26154
|
-
|
|
26348
|
+
if (!ALLOWED_PERMISSION_TYPES.has(permission)) {
|
|
26349
|
+
callback(false);
|
|
26350
|
+
return;
|
|
26351
|
+
}
|
|
26352
|
+
const origin = parseOrigin(details.requestingUrl || webContents.getURL());
|
|
26353
|
+
if (!origin) {
|
|
26354
|
+
callback(false);
|
|
26355
|
+
return;
|
|
26356
|
+
}
|
|
26357
|
+
const k = key(origin, permission);
|
|
26155
26358
|
const existing = records.find((r) => r.origin === origin && r.permission === permission);
|
|
26156
26359
|
if (existing) {
|
|
26157
26360
|
callback(existing.decision === "allow");
|
|
26158
26361
|
return;
|
|
26159
26362
|
}
|
|
26363
|
+
const sessionDecision = sessionDecisions.get(k);
|
|
26364
|
+
if (sessionDecision) {
|
|
26365
|
+
callback(sessionDecision === "allow");
|
|
26366
|
+
return;
|
|
26367
|
+
}
|
|
26160
26368
|
const result = electron.dialog.showMessageBoxSync({
|
|
26161
26369
|
type: "question",
|
|
26162
|
-
buttons: ["Deny", "Allow"],
|
|
26370
|
+
buttons: ["Deny", "Allow Once", "Allow Until Quit", "Always Allow"],
|
|
26163
26371
|
defaultId: 0,
|
|
26164
26372
|
cancelId: 0,
|
|
26165
26373
|
title: "Site permission request",
|
|
26166
26374
|
message: `${origin} wants to use ${permission}`,
|
|
26167
|
-
detail: "
|
|
26375
|
+
detail: "Temporary choices are safer for camera, microphone, location, and clipboard access. Persistent choices can be cleared in Settings > Privacy."
|
|
26168
26376
|
});
|
|
26169
|
-
|
|
26170
|
-
|
|
26171
|
-
|
|
26377
|
+
if (result === 1) {
|
|
26378
|
+
callback(true);
|
|
26379
|
+
return;
|
|
26380
|
+
}
|
|
26381
|
+
if (result === 2) {
|
|
26382
|
+
sessionDecisions.set(k, "allow");
|
|
26383
|
+
callback(true);
|
|
26384
|
+
return;
|
|
26385
|
+
}
|
|
26386
|
+
if (result === 3) {
|
|
26387
|
+
save(origin, permission, "allow");
|
|
26388
|
+
callback(true);
|
|
26389
|
+
return;
|
|
26390
|
+
}
|
|
26391
|
+
save(origin, permission, "deny");
|
|
26392
|
+
callback(false);
|
|
26172
26393
|
});
|
|
26173
26394
|
}
|
|
26174
|
-
const
|
|
26395
|
+
const GITHUB_LATEST_RELEASE_API_URL = "https://api.github.com/repos/unmodeled-tyler/quanta-vessel-browser/releases/latest";
|
|
26175
26396
|
const RELEASES_URL = "https://github.com/unmodeled-tyler/quanta-vessel-browser/releases/latest";
|
|
26176
26397
|
function normalizeVersion(version) {
|
|
26177
26398
|
return version.replace(/^v/i, "").split(/[.-]/).slice(0, 3).map((part) => {
|
|
@@ -26192,21 +26413,22 @@ async function checkForUpdates() {
|
|
|
26192
26413
|
const currentVersion = electron.app.getVersion();
|
|
26193
26414
|
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26194
26415
|
try {
|
|
26195
|
-
const response = await fetch(
|
|
26196
|
-
headers: { accept: "application/json", "user-agent": `Vessel/${currentVersion}` }
|
|
26416
|
+
const response = await fetch(GITHUB_LATEST_RELEASE_API_URL, {
|
|
26417
|
+
headers: { accept: "application/vnd.github+json", "user-agent": `Vessel/${currentVersion}` }
|
|
26197
26418
|
});
|
|
26198
26419
|
if (!response.ok) {
|
|
26199
|
-
throw new Error(`
|
|
26420
|
+
throw new Error(`GitHub Releases responded with ${response.status}`);
|
|
26200
26421
|
}
|
|
26201
26422
|
const body = await response.json();
|
|
26202
|
-
const latestVersion = typeof body.
|
|
26203
|
-
if (!latestVersion) throw new Error("
|
|
26423
|
+
const latestVersion = typeof body.tag_name === "string" ? body.tag_name : null;
|
|
26424
|
+
if (!latestVersion) throw new Error("GitHub release response did not include a tag name");
|
|
26425
|
+
const releaseUrl = typeof body.html_url === "string" && body.html_url.startsWith("https://github.com/") ? body.html_url : RELEASES_URL;
|
|
26204
26426
|
return {
|
|
26205
26427
|
currentVersion,
|
|
26206
26428
|
latestVersion,
|
|
26207
26429
|
updateAvailable: compareVersions(latestVersion, currentVersion) > 0,
|
|
26208
26430
|
checkedAt,
|
|
26209
|
-
releaseUrl
|
|
26431
|
+
releaseUrl
|
|
26210
26432
|
};
|
|
26211
26433
|
} catch (error) {
|
|
26212
26434
|
return {
|
|
@@ -26220,7 +26442,7 @@ async function checkForUpdates() {
|
|
|
26220
26442
|
}
|
|
26221
26443
|
}
|
|
26222
26444
|
async function openUpdateDownload() {
|
|
26223
|
-
await
|
|
26445
|
+
await openExternalAllowlisted(RELEASES_URL, { hosts: ["github.com"] });
|
|
26224
26446
|
}
|
|
26225
26447
|
let activeChatProvider = null;
|
|
26226
26448
|
const logger$4 = createLogger("IPC");
|
|
@@ -26255,10 +26477,18 @@ async function togglePictureInPicture(tabManager) {
|
|
|
26255
26477
|
}
|
|
26256
26478
|
function registerIpcHandlers(windowState, runtime2) {
|
|
26257
26479
|
const { tabManager, chromeView, sidebarView, devtoolsPanelView, mainWindow } = windowState;
|
|
26258
|
-
|
|
26480
|
+
registerTrustedIpcSender(chromeView.webContents);
|
|
26481
|
+
registerTrustedIpcSender(sidebarView.webContents);
|
|
26482
|
+
registerTrustedIpcSender(devtoolsPanelView.webContents);
|
|
26483
|
+
const requireTrusted = (event) => {
|
|
26484
|
+
assertTrustedIpcSender(event);
|
|
26485
|
+
};
|
|
26486
|
+
electron.ipcMain.handle(Channels.OPEN_PRIVATE_WINDOW, (event) => {
|
|
26487
|
+
requireTrusted(event);
|
|
26259
26488
|
createPrivateWindow();
|
|
26260
26489
|
});
|
|
26261
|
-
electron.ipcMain.handle(Channels.OPEN_NEW_WINDOW, () => {
|
|
26490
|
+
electron.ipcMain.handle(Channels.OPEN_NEW_WINDOW, (event) => {
|
|
26491
|
+
requireTrusted(event);
|
|
26262
26492
|
createSecondaryWindow();
|
|
26263
26493
|
});
|
|
26264
26494
|
electron.ipcMain.handle(Channels.IS_PRIVATE_MODE, () => false);
|
|
@@ -26530,14 +26760,15 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26530
26760
|
const originalUrl = activeTab.readerOriginalUrl;
|
|
26531
26761
|
activeTab.setReaderMode(false);
|
|
26532
26762
|
if (originalUrl) {
|
|
26533
|
-
activeTab.view.webContents
|
|
26763
|
+
void loadPermittedNavigationURL(activeTab.view.webContents, originalUrl);
|
|
26534
26764
|
}
|
|
26535
26765
|
} else {
|
|
26536
26766
|
const originalUrl = activeTab.state.url;
|
|
26537
26767
|
const content = await extractContent(activeTab.view.webContents);
|
|
26538
26768
|
const html = generateReaderHTML(content);
|
|
26539
26769
|
activeTab.setReaderMode(true, originalUrl);
|
|
26540
|
-
|
|
26770
|
+
void loadInternalDataURL(
|
|
26771
|
+
activeTab.view.webContents,
|
|
26541
26772
|
`data:text/html;charset=utf-8,${encodeURIComponent(html)}`
|
|
26542
26773
|
);
|
|
26543
26774
|
}
|
|
@@ -26619,7 +26850,12 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26619
26850
|
return getRendererSettings();
|
|
26620
26851
|
});
|
|
26621
26852
|
electron.ipcMain.handle(Channels.SETTINGS_HEALTH_GET, () => getRuntimeHealth());
|
|
26622
|
-
electron.ipcMain.handle(Channels.
|
|
26853
|
+
electron.ipcMain.handle(Channels.MCP_REGENERATE_TOKEN, (event) => {
|
|
26854
|
+
requireTrusted(event);
|
|
26855
|
+
return regenerateMcpAuthToken();
|
|
26856
|
+
});
|
|
26857
|
+
electron.ipcMain.handle(Channels.SETTINGS_SET, async (event, key2, value) => {
|
|
26858
|
+
requireTrusted(event);
|
|
26623
26859
|
assertString(key2, "key");
|
|
26624
26860
|
if (!SETTABLE_KEYS.has(key2)) {
|
|
26625
26861
|
throw new Error(`Unknown setting key: ${key2}`);
|
|
@@ -26639,11 +26875,18 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26639
26875
|
return rendererSettings;
|
|
26640
26876
|
});
|
|
26641
26877
|
electron.ipcMain.handle(Channels.AGENT_RUNTIME_GET, () => runtime2.getState());
|
|
26642
|
-
electron.ipcMain.handle(Channels.AGENT_PAUSE, () =>
|
|
26643
|
-
|
|
26878
|
+
electron.ipcMain.handle(Channels.AGENT_PAUSE, (event) => {
|
|
26879
|
+
requireTrusted(event);
|
|
26880
|
+
return runtime2.pause();
|
|
26881
|
+
});
|
|
26882
|
+
electron.ipcMain.handle(Channels.AGENT_RESUME, (event) => {
|
|
26883
|
+
requireTrusted(event);
|
|
26884
|
+
return runtime2.resume();
|
|
26885
|
+
});
|
|
26644
26886
|
electron.ipcMain.handle(
|
|
26645
26887
|
Channels.AGENT_SET_APPROVAL_MODE,
|
|
26646
|
-
(
|
|
26888
|
+
(event, mode) => {
|
|
26889
|
+
requireTrusted(event);
|
|
26647
26890
|
assertString(mode, "mode");
|
|
26648
26891
|
if (!VALID_APPROVAL_MODES.includes(mode)) {
|
|
26649
26892
|
throw new Error(`Invalid approval mode: ${mode}`);
|
|
@@ -26655,7 +26898,10 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26655
26898
|
);
|
|
26656
26899
|
electron.ipcMain.handle(
|
|
26657
26900
|
Channels.AGENT_APPROVAL_RESOLVE,
|
|
26658
|
-
(
|
|
26901
|
+
(event, approvalId, approved) => {
|
|
26902
|
+
requireTrusted(event);
|
|
26903
|
+
return runtime2.resolveApproval(approvalId, approved);
|
|
26904
|
+
}
|
|
26659
26905
|
);
|
|
26660
26906
|
electron.ipcMain.handle(
|
|
26661
26907
|
Channels.AGENT_CHECKPOINT_CREATE,
|
|
@@ -26679,7 +26925,10 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26679
26925
|
);
|
|
26680
26926
|
electron.ipcMain.handle(
|
|
26681
26927
|
Channels.AGENT_SESSION_RESTORE,
|
|
26682
|
-
(
|
|
26928
|
+
(event, snapshot2) => {
|
|
26929
|
+
requireTrusted(event);
|
|
26930
|
+
return runtime2.restoreSession(snapshot2);
|
|
26931
|
+
}
|
|
26683
26932
|
);
|
|
26684
26933
|
registerBookmarkHandlers();
|
|
26685
26934
|
electron.ipcMain.handle(Channels.HIGHLIGHT_CAPTURE, async () => {
|
|
@@ -26799,17 +27048,20 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26799
27048
|
electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, () => {
|
|
26800
27049
|
return getInstalledKits();
|
|
26801
27050
|
});
|
|
26802
|
-
electron.ipcMain.handle(Channels.AUTOMATION_INSTALL_FROM_FILE, async () => {
|
|
27051
|
+
electron.ipcMain.handle(Channels.AUTOMATION_INSTALL_FROM_FILE, async (event) => {
|
|
27052
|
+
requireTrusted(event);
|
|
26803
27053
|
return await installKitFromFile();
|
|
26804
27054
|
});
|
|
26805
|
-
electron.ipcMain.handle(Channels.AUTOMATION_UNINSTALL, (
|
|
27055
|
+
electron.ipcMain.handle(Channels.AUTOMATION_UNINSTALL, (event, id) => {
|
|
27056
|
+
requireTrusted(event);
|
|
26806
27057
|
assertString(id, "id");
|
|
26807
27058
|
return uninstallKit(id, getScheduledKitIds());
|
|
26808
27059
|
});
|
|
26809
27060
|
registerScheduleHandlers(windowState, runtime2, sendToRendererViews);
|
|
26810
27061
|
registerAutofillHandlers(windowState);
|
|
26811
27062
|
registerPageDiffHandlers(windowState, sendToRendererViews);
|
|
26812
|
-
electron.ipcMain.handle(Channels.CLEAR_BROWSING_DATA, async (
|
|
27063
|
+
electron.ipcMain.handle(Channels.CLEAR_BROWSING_DATA, async (event, options) => {
|
|
27064
|
+
requireTrusted(event);
|
|
26813
27065
|
const { cache, cookies, history, localStorage: clearLs, timeRange } = options;
|
|
26814
27066
|
if (cache) {
|
|
26815
27067
|
await electron.session.defaultSession.clearCache();
|
|
@@ -26827,23 +27079,35 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26827
27079
|
setDownloadBroadcaster(sendToRendererViews);
|
|
26828
27080
|
setPermissionBroadcaster(sendToRendererViews);
|
|
26829
27081
|
electron.ipcMain.handle(Channels.DOWNLOADS_GET, () => listDownloads());
|
|
26830
|
-
electron.ipcMain.handle(Channels.DOWNLOADS_CLEAR, () => {
|
|
27082
|
+
electron.ipcMain.handle(Channels.DOWNLOADS_CLEAR, (event) => {
|
|
27083
|
+
requireTrusted(event);
|
|
26831
27084
|
clearDownloads();
|
|
26832
27085
|
return true;
|
|
26833
27086
|
});
|
|
26834
|
-
electron.ipcMain.handle(Channels.DOWNLOADS_OPEN, (
|
|
26835
|
-
|
|
27087
|
+
electron.ipcMain.handle(Channels.DOWNLOADS_OPEN, (event, id) => {
|
|
27088
|
+
requireTrusted(event);
|
|
27089
|
+
return openDownload(id);
|
|
27090
|
+
});
|
|
27091
|
+
electron.ipcMain.handle(Channels.DOWNLOADS_SHOW_IN_FOLDER, (event, id) => {
|
|
27092
|
+
requireTrusted(event);
|
|
27093
|
+
return showDownloadInFolder(id);
|
|
27094
|
+
});
|
|
26836
27095
|
electron.ipcMain.handle(Channels.PERMISSIONS_GET, () => listPermissions());
|
|
26837
|
-
electron.ipcMain.handle(Channels.PERMISSIONS_CLEAR, () => {
|
|
27096
|
+
electron.ipcMain.handle(Channels.PERMISSIONS_CLEAR, (event) => {
|
|
27097
|
+
requireTrusted(event);
|
|
26838
27098
|
clearPermissions();
|
|
26839
27099
|
return true;
|
|
26840
27100
|
});
|
|
26841
|
-
electron.ipcMain.handle(Channels.PERMISSIONS_CLEAR_ORIGIN, (
|
|
27101
|
+
electron.ipcMain.handle(Channels.PERMISSIONS_CLEAR_ORIGIN, (event, origin) => {
|
|
27102
|
+
requireTrusted(event);
|
|
26842
27103
|
clearPermissionsForOrigin(origin);
|
|
26843
27104
|
return true;
|
|
26844
27105
|
});
|
|
26845
27106
|
electron.ipcMain.handle(Channels.UPDATES_CHECK, () => checkForUpdates());
|
|
26846
|
-
electron.ipcMain.handle(Channels.UPDATES_OPEN_DOWNLOAD, () =>
|
|
27107
|
+
electron.ipcMain.handle(Channels.UPDATES_OPEN_DOWNLOAD, (event) => {
|
|
27108
|
+
requireTrusted(event);
|
|
27109
|
+
return openUpdateDownload();
|
|
27110
|
+
});
|
|
26847
27111
|
electron.ipcMain.handle(Channels.TAB_TOGGLE_PIP, async () => {
|
|
26848
27112
|
return togglePictureInPicture(tabManager);
|
|
26849
27113
|
});
|
|
@@ -27276,6 +27540,7 @@ class AgentRuntime {
|
|
|
27276
27540
|
this.state = this.loadPersistedState();
|
|
27277
27541
|
onMcpStatusChange(() => this.emit());
|
|
27278
27542
|
}
|
|
27543
|
+
tabManager;
|
|
27279
27544
|
state;
|
|
27280
27545
|
updateListener = null;
|
|
27281
27546
|
pendingResolvers = /* @__PURE__ */ new Map();
|
|
@@ -27287,11 +27552,11 @@ class AgentRuntime {
|
|
|
27287
27552
|
}
|
|
27288
27553
|
}
|
|
27289
27554
|
getState() {
|
|
27290
|
-
const
|
|
27291
|
-
|
|
27292
|
-
|
|
27293
|
-
|
|
27294
|
-
return
|
|
27555
|
+
const snapshot2 = clone(this.state);
|
|
27556
|
+
snapshot2.mcpStatus = getMcpStatus();
|
|
27557
|
+
snapshot2.canUndo = this.canUndo();
|
|
27558
|
+
snapshot2.undoInfo = this.getUndoInfo();
|
|
27559
|
+
return snapshot2;
|
|
27295
27560
|
}
|
|
27296
27561
|
onTabStateChanged() {
|
|
27297
27562
|
this.captureSession();
|
|
@@ -27327,13 +27592,13 @@ class AgentRuntime {
|
|
|
27327
27592
|
return this.getState();
|
|
27328
27593
|
}
|
|
27329
27594
|
createCheckpoint(name, note) {
|
|
27330
|
-
const
|
|
27595
|
+
const snapshot2 = this.captureSession(note);
|
|
27331
27596
|
const checkpoint = {
|
|
27332
27597
|
id: crypto$2.randomUUID(),
|
|
27333
27598
|
name: name?.trim() || `Checkpoint ${this.state.checkpoints.length + 1}`,
|
|
27334
27599
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27335
27600
|
note: note?.trim() || void 0,
|
|
27336
|
-
snapshot
|
|
27601
|
+
snapshot: snapshot2
|
|
27337
27602
|
};
|
|
27338
27603
|
this.state.checkpoints = [...this.state.checkpoints, checkpoint].slice(
|
|
27339
27604
|
-20
|
|
@@ -27367,26 +27632,26 @@ class AgentRuntime {
|
|
|
27367
27632
|
return { actionName: latest.actionName, capturedAt: latest.capturedAt };
|
|
27368
27633
|
}
|
|
27369
27634
|
undoLastAction() {
|
|
27370
|
-
const
|
|
27371
|
-
if (!
|
|
27635
|
+
const snapshot2 = this.undoSnapshots.at(-1);
|
|
27636
|
+
if (!snapshot2) return null;
|
|
27372
27637
|
try {
|
|
27373
|
-
this.tabManager.restoreSession(
|
|
27638
|
+
this.tabManager.restoreSession(snapshot2.snapshot);
|
|
27374
27639
|
this.undoSnapshots.pop();
|
|
27375
27640
|
} catch (error) {
|
|
27376
27641
|
logger$3.error("Failed to restore undo snapshot", error);
|
|
27377
27642
|
return null;
|
|
27378
27643
|
}
|
|
27379
|
-
this.captureSession(`Undid ${
|
|
27380
|
-
return
|
|
27644
|
+
this.captureSession(`Undid ${snapshot2.actionName}`);
|
|
27645
|
+
return snapshot2.actionName;
|
|
27381
27646
|
}
|
|
27382
27647
|
captureSession(note) {
|
|
27383
|
-
const
|
|
27384
|
-
this.state.session =
|
|
27648
|
+
const snapshot2 = this.tabManager.snapshotSession(note);
|
|
27649
|
+
this.state.session = snapshot2;
|
|
27385
27650
|
this.emit();
|
|
27386
|
-
return clone(
|
|
27651
|
+
return clone(snapshot2);
|
|
27387
27652
|
}
|
|
27388
|
-
restoreSession(
|
|
27389
|
-
const target =
|
|
27653
|
+
restoreSession(snapshot2) {
|
|
27654
|
+
const target = snapshot2 || this.state.session;
|
|
27390
27655
|
if (!target) {
|
|
27391
27656
|
return this.captureSession("No saved session to restore");
|
|
27392
27657
|
}
|
|
@@ -27528,6 +27793,7 @@ ${progress}
|
|
|
27528
27793
|
args = {},
|
|
27529
27794
|
tabId = null,
|
|
27530
27795
|
dangerous = false,
|
|
27796
|
+
requiresApproval = false,
|
|
27531
27797
|
undoable,
|
|
27532
27798
|
executor
|
|
27533
27799
|
}) {
|
|
@@ -27547,7 +27813,7 @@ ${progress}
|
|
|
27547
27813
|
streamId: transcriptStreamId,
|
|
27548
27814
|
mode: "replace"
|
|
27549
27815
|
});
|
|
27550
|
-
const approvalReason = this.getApprovalReason(dangerous);
|
|
27816
|
+
const approvalReason = this.getApprovalReason(dangerous, requiresApproval);
|
|
27551
27817
|
if (approvalReason) {
|
|
27552
27818
|
this.publishTranscript({
|
|
27553
27819
|
source,
|
|
@@ -27625,8 +27891,8 @@ ${progress}
|
|
|
27625
27891
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
27626
27892
|
};
|
|
27627
27893
|
}
|
|
27628
|
-
pushUndoSnapshot(
|
|
27629
|
-
this.undoSnapshots = [...this.undoSnapshots,
|
|
27894
|
+
pushUndoSnapshot(snapshot2) {
|
|
27895
|
+
this.undoSnapshots = [...this.undoSnapshots, snapshot2].slice(
|
|
27630
27896
|
-10
|
|
27631
27897
|
);
|
|
27632
27898
|
}
|
|
@@ -27770,10 +28036,13 @@ ${progress}
|
|
|
27770
28036
|
)
|
|
27771
28037
|
};
|
|
27772
28038
|
}
|
|
27773
|
-
getApprovalReason(dangerous) {
|
|
28039
|
+
getApprovalReason(dangerous, requiresApproval) {
|
|
27774
28040
|
if (this.state.supervisor.paused) {
|
|
27775
28041
|
return "Agent execution is paused";
|
|
27776
28042
|
}
|
|
28043
|
+
if (requiresApproval) {
|
|
28044
|
+
return "Approval required: high-risk action";
|
|
28045
|
+
}
|
|
27777
28046
|
if (this.state.supervisor.approvalMode === "manual") {
|
|
27778
28047
|
return "Approval required: ask every time mode";
|
|
27779
28048
|
}
|