@quanta-intellect/vessel-browser 0.1.90 → 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) {
|
|
120
|
-
|
|
121
|
-
|
|
133
|
+
assertSafeStorageAvailable();
|
|
134
|
+
const filePath2 = getChatProviderSecretPath();
|
|
135
|
+
fs.mkdirSync(path.dirname(filePath2), { recursive: true });
|
|
122
136
|
const payload = JSON.stringify(secret);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
fs.writeFileSync(filePath, encrypted, { mode: 384 });
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
fs.writeFileSync(filePath, 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) {
|
|
152
|
-
|
|
153
|
-
|
|
163
|
+
assertSafeStorageAvailable();
|
|
164
|
+
const filePath2 = getCodexTokensPath();
|
|
165
|
+
fs.mkdirSync(path.dirname(filePath2), { recursive: true });
|
|
154
166
|
const payload = JSON.stringify(tokens);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
fs.writeFileSync(filePath, encrypted, { mode: 384 });
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
fs.writeFileSync(filePath, payload, { mode: 384 });
|
|
167
|
+
const encrypted = electron.safeStorage.encryptString(payload);
|
|
168
|
+
writePrivateFile(filePath2, encrypted);
|
|
161
169
|
}
|
|
162
170
|
function clearStoredCodexTokens() {
|
|
163
171
|
try {
|
|
@@ -282,11 +290,11 @@ function saveSettings() {
|
|
|
282
290
|
}
|
|
283
291
|
}, SAVE_DEBOUNCE_MS$6);
|
|
284
292
|
}
|
|
285
|
-
function setSetting(
|
|
293
|
+
function setSetting(key2, value) {
|
|
286
294
|
loadSettings();
|
|
287
|
-
if (
|
|
295
|
+
if (key2 === "mcpPort") {
|
|
288
296
|
settings.mcpPort = sanitizePort(value);
|
|
289
|
-
} else if (
|
|
297
|
+
} else if (key2 === "chatProvider") {
|
|
290
298
|
const nextProvider = value;
|
|
291
299
|
if (!nextProvider) {
|
|
292
300
|
clearStoredProviderSecret();
|
|
@@ -314,7 +322,7 @@ function setSetting(key, value) {
|
|
|
314
322
|
};
|
|
315
323
|
}
|
|
316
324
|
} else {
|
|
317
|
-
settings[
|
|
325
|
+
settings[key2] = value;
|
|
318
326
|
}
|
|
319
327
|
saveSettings();
|
|
320
328
|
return { ...settings };
|
|
@@ -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");
|
|
@@ -490,27 +520,27 @@ class Tab {
|
|
|
490
520
|
this.view.webContents.on("before-input-event", (event, input) => {
|
|
491
521
|
if (!input.control && !input.meta) return;
|
|
492
522
|
if (input.type !== "keyDown") return;
|
|
493
|
-
const
|
|
523
|
+
const key2 = input.key.toLowerCase();
|
|
494
524
|
const wc = this.view.webContents;
|
|
495
|
-
if (
|
|
525
|
+
if (key2 === "+" || key2 === "=") {
|
|
496
526
|
this.zoomIn();
|
|
497
527
|
event.preventDefault();
|
|
498
528
|
return;
|
|
499
529
|
}
|
|
500
|
-
if (
|
|
530
|
+
if (key2 === "-") {
|
|
501
531
|
this.zoomOut();
|
|
502
532
|
event.preventDefault();
|
|
503
533
|
return;
|
|
504
534
|
}
|
|
505
|
-
if (
|
|
535
|
+
if (key2 === "0") {
|
|
506
536
|
this.zoomReset();
|
|
507
537
|
event.preventDefault();
|
|
508
538
|
return;
|
|
509
539
|
}
|
|
510
|
-
if (
|
|
511
|
-
else if (
|
|
512
|
-
else if (
|
|
513
|
-
else if (
|
|
540
|
+
if (key2 === "c") wc.copy();
|
|
541
|
+
else if (key2 === "v") wc.paste();
|
|
542
|
+
else if (key2 === "x") wc.cut();
|
|
543
|
+
else if (key2 === "a") wc.selectAll();
|
|
514
544
|
});
|
|
515
545
|
this.setupListeners();
|
|
516
546
|
if (url) {
|
|
@@ -784,8 +814,8 @@ class Tab {
|
|
|
784
814
|
}
|
|
785
815
|
if (postBody) {
|
|
786
816
|
const params = new URLSearchParams();
|
|
787
|
-
for (const [
|
|
788
|
-
params.set(
|
|
817
|
+
for (const [key2, value] of Object.entries(postBody)) {
|
|
818
|
+
params.set(key2, value);
|
|
789
819
|
}
|
|
790
820
|
return this.guardedLoadURL(url, {
|
|
791
821
|
method: "POST",
|
|
@@ -1059,22 +1089,22 @@ function encodeStoredData(payload, secure) {
|
|
|
1059
1089
|
return payload;
|
|
1060
1090
|
}
|
|
1061
1091
|
function loadJsonFile({
|
|
1062
|
-
filePath,
|
|
1092
|
+
filePath: filePath2,
|
|
1063
1093
|
fallback,
|
|
1064
|
-
parse,
|
|
1094
|
+
parse: parse2,
|
|
1065
1095
|
secure = false
|
|
1066
1096
|
}) {
|
|
1067
1097
|
try {
|
|
1068
|
-
const raw = fs.readFileSync(
|
|
1098
|
+
const raw = fs.readFileSync(filePath2);
|
|
1069
1099
|
const decoded = decodeStoredData(raw, secure);
|
|
1070
|
-
return
|
|
1100
|
+
return parse2(JSON.parse(decoded));
|
|
1071
1101
|
} catch {
|
|
1072
1102
|
return fallback;
|
|
1073
1103
|
}
|
|
1074
1104
|
}
|
|
1075
1105
|
function createDebouncedJsonPersistence({
|
|
1076
1106
|
debounceMs,
|
|
1077
|
-
filePath,
|
|
1107
|
+
filePath: filePath2,
|
|
1078
1108
|
getValue,
|
|
1079
1109
|
logLabel,
|
|
1080
1110
|
resetOnSchedule = false,
|
|
@@ -1097,9 +1127,9 @@ function createDebouncedJsonPersistence({
|
|
|
1097
1127
|
2
|
|
1098
1128
|
);
|
|
1099
1129
|
const data = encodeStoredData(payload, secure);
|
|
1100
|
-
await fs.promises.mkdir(path.dirname(
|
|
1130
|
+
await fs.promises.mkdir(path.dirname(filePath2), { recursive: true }).then(
|
|
1101
1131
|
() => fs.promises.writeFile(
|
|
1102
|
-
|
|
1132
|
+
filePath2,
|
|
1103
1133
|
data,
|
|
1104
1134
|
typeof data === "string" ? { encoding: "utf-8", mode: 384 } : { mode: 384 }
|
|
1105
1135
|
)
|
|
@@ -1125,7 +1155,7 @@ function createDebouncedJsonPersistence({
|
|
|
1125
1155
|
flush: flush2
|
|
1126
1156
|
};
|
|
1127
1157
|
}
|
|
1128
|
-
let state$
|
|
1158
|
+
let state$5 = null;
|
|
1129
1159
|
const listeners$2 = /* @__PURE__ */ new Set();
|
|
1130
1160
|
const SAVE_DEBOUNCE_MS$5 = 250;
|
|
1131
1161
|
function getHighlightsPath() {
|
|
@@ -1135,19 +1165,19 @@ function createPersistence$1() {
|
|
|
1135
1165
|
return createDebouncedJsonPersistence({
|
|
1136
1166
|
debounceMs: SAVE_DEBOUNCE_MS$5,
|
|
1137
1167
|
filePath: getHighlightsPath(),
|
|
1138
|
-
getValue: () => state$
|
|
1168
|
+
getValue: () => state$5,
|
|
1139
1169
|
logLabel: "highlights",
|
|
1140
1170
|
resetOnSchedule: true
|
|
1141
1171
|
});
|
|
1142
1172
|
}
|
|
1143
|
-
let persistence$
|
|
1173
|
+
let persistence$7 = null;
|
|
1144
1174
|
function getPersistence$1() {
|
|
1145
|
-
persistence$
|
|
1146
|
-
return persistence$
|
|
1175
|
+
persistence$7 ??= createPersistence$1();
|
|
1176
|
+
return persistence$7;
|
|
1147
1177
|
}
|
|
1148
1178
|
function load$4() {
|
|
1149
|
-
if (state$
|
|
1150
|
-
state$
|
|
1179
|
+
if (state$5) return state$5;
|
|
1180
|
+
state$5 = loadJsonFile({
|
|
1151
1181
|
filePath: getHighlightsPath(),
|
|
1152
1182
|
fallback: { highlights: [] },
|
|
1153
1183
|
parse: (raw) => {
|
|
@@ -1157,16 +1187,16 @@ function load$4() {
|
|
|
1157
1187
|
};
|
|
1158
1188
|
}
|
|
1159
1189
|
});
|
|
1160
|
-
return state$
|
|
1190
|
+
return state$5;
|
|
1161
1191
|
}
|
|
1162
|
-
function save$
|
|
1192
|
+
function save$3() {
|
|
1163
1193
|
getPersistence$1().schedule();
|
|
1164
1194
|
}
|
|
1165
|
-
function emit$
|
|
1166
|
-
if (!state$
|
|
1167
|
-
const
|
|
1195
|
+
function emit$4() {
|
|
1196
|
+
if (!state$5) return;
|
|
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) {
|
|
@@ -1180,12 +1210,12 @@ function normalizeUrl$1(rawUrl) {
|
|
|
1180
1210
|
}
|
|
1181
1211
|
function getState$2() {
|
|
1182
1212
|
load$4();
|
|
1183
|
-
return { highlights: [...state$
|
|
1213
|
+
return { highlights: [...state$5.highlights] };
|
|
1184
1214
|
}
|
|
1185
1215
|
function getHighlightsForUrl(url) {
|
|
1186
1216
|
load$4();
|
|
1187
1217
|
const normalized = normalizeUrl$1(url);
|
|
1188
|
-
return state$
|
|
1218
|
+
return state$5.highlights.filter((h) => h.url === normalized);
|
|
1189
1219
|
}
|
|
1190
1220
|
function addHighlight(url, selector, text, label, color, source) {
|
|
1191
1221
|
load$4();
|
|
@@ -1199,45 +1229,45 @@ function addHighlight(url, selector, text, label, color, source) {
|
|
|
1199
1229
|
source: source || void 0,
|
|
1200
1230
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1201
1231
|
};
|
|
1202
|
-
state$
|
|
1203
|
-
save$
|
|
1204
|
-
emit$
|
|
1232
|
+
state$5.highlights.push(highlight);
|
|
1233
|
+
save$3();
|
|
1234
|
+
emit$4();
|
|
1205
1235
|
return highlight;
|
|
1206
1236
|
}
|
|
1207
1237
|
function removeHighlight(id) {
|
|
1208
1238
|
load$4();
|
|
1209
|
-
const index = state$
|
|
1239
|
+
const index = state$5.highlights.findIndex((h) => h.id === id);
|
|
1210
1240
|
if (index === -1) return null;
|
|
1211
|
-
const [removed] = state$
|
|
1212
|
-
save$
|
|
1213
|
-
emit$
|
|
1241
|
+
const [removed] = state$5.highlights.splice(index, 1);
|
|
1242
|
+
save$3();
|
|
1243
|
+
emit$4();
|
|
1214
1244
|
return removed;
|
|
1215
1245
|
}
|
|
1216
1246
|
function findHighlightByText(url, text) {
|
|
1217
1247
|
load$4();
|
|
1218
1248
|
const normalized = normalizeUrl$1(url);
|
|
1219
|
-
return state$
|
|
1249
|
+
return state$5.highlights.find(
|
|
1220
1250
|
(h) => h.url === normalized && h.text && h.text === text
|
|
1221
1251
|
) ?? null;
|
|
1222
1252
|
}
|
|
1223
1253
|
function updateHighlightColor(id, color) {
|
|
1224
1254
|
load$4();
|
|
1225
|
-
const highlight = state$
|
|
1255
|
+
const highlight = state$5.highlights.find((h) => h.id === id);
|
|
1226
1256
|
if (!highlight) return null;
|
|
1227
1257
|
highlight.color = color;
|
|
1228
|
-
save$
|
|
1229
|
-
emit$
|
|
1258
|
+
save$3();
|
|
1259
|
+
emit$4();
|
|
1230
1260
|
return highlight;
|
|
1231
1261
|
}
|
|
1232
1262
|
function clearHighlightsForUrl(url) {
|
|
1233
1263
|
load$4();
|
|
1234
1264
|
const normalized = normalizeUrl$1(url);
|
|
1235
|
-
const before = state$
|
|
1236
|
-
state$
|
|
1237
|
-
const removed = before - state$
|
|
1265
|
+
const before = state$5.highlights.length;
|
|
1266
|
+
state$5.highlights = state$5.highlights.filter((h) => h.url !== normalized);
|
|
1267
|
+
const removed = before - state$5.highlights.length;
|
|
1238
1268
|
if (removed > 0) {
|
|
1239
|
-
save$
|
|
1240
|
-
emit$
|
|
1269
|
+
save$3();
|
|
1270
|
+
emit$4();
|
|
1241
1271
|
}
|
|
1242
1272
|
return removed;
|
|
1243
1273
|
}
|
|
@@ -1867,14 +1897,14 @@ function persistHighlight(url, text) {
|
|
|
1867
1897
|
}
|
|
1868
1898
|
const MAX_HISTORY_ENTRIES = 5e3;
|
|
1869
1899
|
const SAVE_DEBOUNCE_MS$4 = 250;
|
|
1870
|
-
let state$
|
|
1900
|
+
let state$4 = null;
|
|
1871
1901
|
const listeners$1 = /* @__PURE__ */ new Set();
|
|
1872
1902
|
function getHistoryPath() {
|
|
1873
1903
|
return path.join(electron.app.getPath("userData"), "vessel-history.json");
|
|
1874
1904
|
}
|
|
1875
1905
|
function load$3() {
|
|
1876
|
-
if (state$
|
|
1877
|
-
state$
|
|
1906
|
+
if (state$4) return state$4;
|
|
1907
|
+
state$4 = loadJsonFile({
|
|
1878
1908
|
filePath: getHistoryPath(),
|
|
1879
1909
|
fallback: { entries: [] },
|
|
1880
1910
|
parse: (raw) => {
|
|
@@ -1884,27 +1914,27 @@ function load$3() {
|
|
|
1884
1914
|
};
|
|
1885
1915
|
}
|
|
1886
1916
|
});
|
|
1887
|
-
return state$
|
|
1917
|
+
return state$4;
|
|
1888
1918
|
}
|
|
1889
|
-
const persistence$
|
|
1919
|
+
const persistence$6 = createDebouncedJsonPersistence({
|
|
1890
1920
|
debounceMs: SAVE_DEBOUNCE_MS$4,
|
|
1891
1921
|
filePath: getHistoryPath(),
|
|
1892
|
-
getValue: () => state$
|
|
1922
|
+
getValue: () => state$4,
|
|
1893
1923
|
logLabel: "history"
|
|
1894
1924
|
});
|
|
1895
|
-
function save$
|
|
1896
|
-
persistence$
|
|
1925
|
+
function save$2() {
|
|
1926
|
+
persistence$6.schedule();
|
|
1897
1927
|
}
|
|
1898
|
-
function emit$
|
|
1899
|
-
if (!state$
|
|
1900
|
-
const
|
|
1928
|
+
function emit$3() {
|
|
1929
|
+
if (!state$4) return;
|
|
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() {
|
|
1906
1936
|
load$3();
|
|
1907
|
-
return { entries: [...state$
|
|
1937
|
+
return { entries: [...state$4.entries] };
|
|
1908
1938
|
}
|
|
1909
1939
|
function subscribe$1(listener) {
|
|
1910
1940
|
listeners$1.add(listener);
|
|
@@ -1915,12 +1945,12 @@ function subscribe$1(listener) {
|
|
|
1915
1945
|
function addEntry$1(url, title) {
|
|
1916
1946
|
if (!url || url === "about:blank") return;
|
|
1917
1947
|
load$3();
|
|
1918
|
-
const last = state$
|
|
1948
|
+
const last = state$4.entries[0];
|
|
1919
1949
|
if (last && last.url === url) {
|
|
1920
1950
|
if (title && title !== last.title) {
|
|
1921
1951
|
last.title = title;
|
|
1922
|
-
save$
|
|
1923
|
-
emit$
|
|
1952
|
+
save$2();
|
|
1953
|
+
emit$3();
|
|
1924
1954
|
}
|
|
1925
1955
|
return;
|
|
1926
1956
|
}
|
|
@@ -1929,25 +1959,25 @@ function addEntry$1(url, title) {
|
|
|
1929
1959
|
title: title || url,
|
|
1930
1960
|
visitedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1931
1961
|
};
|
|
1932
|
-
state$
|
|
1933
|
-
if (state$
|
|
1934
|
-
state$
|
|
1962
|
+
state$4.entries.unshift(entry);
|
|
1963
|
+
if (state$4.entries.length > MAX_HISTORY_ENTRIES) {
|
|
1964
|
+
state$4.entries = state$4.entries.slice(0, MAX_HISTORY_ENTRIES);
|
|
1935
1965
|
}
|
|
1936
|
-
save$
|
|
1937
|
-
emit$
|
|
1966
|
+
save$2();
|
|
1967
|
+
emit$3();
|
|
1938
1968
|
}
|
|
1939
1969
|
function search(query, limit = 50) {
|
|
1940
1970
|
load$3();
|
|
1941
|
-
if (!query.trim()) return state$
|
|
1971
|
+
if (!query.trim()) return state$4.entries.slice(0, limit);
|
|
1942
1972
|
const normalized = query.toLowerCase();
|
|
1943
|
-
return state$
|
|
1973
|
+
return state$4.entries.filter(
|
|
1944
1974
|
(e) => e.url.toLowerCase().includes(normalized) || e.title.toLowerCase().includes(normalized)
|
|
1945
1975
|
).slice(0, limit);
|
|
1946
1976
|
}
|
|
1947
1977
|
function clearAll$1() {
|
|
1948
|
-
state$
|
|
1949
|
-
save$
|
|
1950
|
-
emit$
|
|
1978
|
+
state$4 = { entries: [] };
|
|
1979
|
+
save$2();
|
|
1980
|
+
emit$3();
|
|
1951
1981
|
}
|
|
1952
1982
|
function clearByTimeRange(timeRange) {
|
|
1953
1983
|
load$3();
|
|
@@ -1957,12 +1987,12 @@ function clearByTimeRange(timeRange) {
|
|
|
1957
1987
|
}
|
|
1958
1988
|
const now = Date.now();
|
|
1959
1989
|
const cutoff = new Date(now - timeRangeToMs(timeRange));
|
|
1960
|
-
state$
|
|
1990
|
+
state$4.entries = state$4.entries.filter((entry) => {
|
|
1961
1991
|
const visitedAt = new Date(entry.visitedAt).getTime();
|
|
1962
1992
|
return Number.isNaN(visitedAt) || visitedAt < cutoff.getTime();
|
|
1963
1993
|
});
|
|
1964
|
-
save$
|
|
1965
|
-
emit$
|
|
1994
|
+
save$2();
|
|
1995
|
+
emit$3();
|
|
1966
1996
|
}
|
|
1967
1997
|
function timeRangeToMs(range) {
|
|
1968
1998
|
switch (range) {
|
|
@@ -2016,7 +2046,7 @@ function importHistoryFromJson(content) {
|
|
|
2016
2046
|
const parsed = JSON.parse(content);
|
|
2017
2047
|
const entries = Array.isArray(parsed?.entries) ? parsed.entries : [];
|
|
2018
2048
|
load$3();
|
|
2019
|
-
const existingUrls = new Set(state$
|
|
2049
|
+
const existingUrls = new Set(state$4.entries.map((e) => e.url));
|
|
2020
2050
|
for (const entry of entries) {
|
|
2021
2051
|
if (!entry?.url || typeof entry.url !== "string") {
|
|
2022
2052
|
errors++;
|
|
@@ -2026,7 +2056,7 @@ function importHistoryFromJson(content) {
|
|
|
2026
2056
|
skipped++;
|
|
2027
2057
|
continue;
|
|
2028
2058
|
}
|
|
2029
|
-
state$
|
|
2059
|
+
state$4.entries.push({
|
|
2030
2060
|
url: entry.url,
|
|
2031
2061
|
title: typeof entry.title === "string" ? entry.title : entry.url,
|
|
2032
2062
|
visitedAt: typeof entry.visitedAt === "string" ? entry.visitedAt : (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -2034,14 +2064,14 @@ function importHistoryFromJson(content) {
|
|
|
2034
2064
|
existingUrls.add(entry.url);
|
|
2035
2065
|
imported++;
|
|
2036
2066
|
}
|
|
2037
|
-
state$
|
|
2067
|
+
state$4.entries.sort(
|
|
2038
2068
|
(a, b) => new Date(b.visitedAt).getTime() - new Date(a.visitedAt).getTime()
|
|
2039
2069
|
);
|
|
2040
|
-
if (state$
|
|
2041
|
-
state$
|
|
2070
|
+
if (state$4.entries.length > MAX_HISTORY_ENTRIES) {
|
|
2071
|
+
state$4.entries = state$4.entries.slice(0, MAX_HISTORY_ENTRIES);
|
|
2042
2072
|
}
|
|
2043
|
-
save$
|
|
2044
|
-
emit$
|
|
2073
|
+
save$2();
|
|
2074
|
+
emit$3();
|
|
2045
2075
|
} catch {
|
|
2046
2076
|
errors++;
|
|
2047
2077
|
}
|
|
@@ -2052,7 +2082,7 @@ function importHistoryFromHtml(content) {
|
|
|
2052
2082
|
let skipped = 0;
|
|
2053
2083
|
let errors = 0;
|
|
2054
2084
|
load$3();
|
|
2055
|
-
const existingUrls = new Set(state$
|
|
2085
|
+
const existingUrls = new Set(state$4.entries.map((e) => e.url));
|
|
2056
2086
|
const hrefRegex = /<A\s+[^>]*HREF="([^"]+)"[^>]*>([^<]*)<\/A>/gi;
|
|
2057
2087
|
let match;
|
|
2058
2088
|
while ((match = hrefRegex.exec(content)) !== null) {
|
|
@@ -2063,7 +2093,7 @@ function importHistoryFromHtml(content) {
|
|
|
2063
2093
|
else errors++;
|
|
2064
2094
|
continue;
|
|
2065
2095
|
}
|
|
2066
|
-
state$
|
|
2096
|
+
state$4.entries.push({
|
|
2067
2097
|
url,
|
|
2068
2098
|
title,
|
|
2069
2099
|
visitedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -2071,18 +2101,18 @@ function importHistoryFromHtml(content) {
|
|
|
2071
2101
|
existingUrls.add(url);
|
|
2072
2102
|
imported++;
|
|
2073
2103
|
}
|
|
2074
|
-
state$
|
|
2104
|
+
state$4.entries.sort(
|
|
2075
2105
|
(a, b) => new Date(b.visitedAt).getTime() - new Date(a.visitedAt).getTime()
|
|
2076
2106
|
);
|
|
2077
|
-
if (state$
|
|
2078
|
-
state$
|
|
2107
|
+
if (state$4.entries.length > MAX_HISTORY_ENTRIES) {
|
|
2108
|
+
state$4.entries = state$4.entries.slice(0, MAX_HISTORY_ENTRIES);
|
|
2079
2109
|
}
|
|
2080
|
-
save$
|
|
2081
|
-
emit$
|
|
2110
|
+
save$2();
|
|
2111
|
+
emit$3();
|
|
2082
2112
|
return { imported, skipped, errors };
|
|
2083
2113
|
}
|
|
2084
2114
|
function flushPersist$3() {
|
|
2085
|
-
return persistence$
|
|
2115
|
+
return persistence$6.flush();
|
|
2086
2116
|
}
|
|
2087
2117
|
const MAX_CONSOLE_ENTRIES = 500;
|
|
2088
2118
|
const MAX_NETWORK_ENTRIES = 200;
|
|
@@ -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;
|
|
@@ -2439,18 +2471,18 @@ class DevToolsSession {
|
|
|
2439
2471
|
if (result?.__error) throw new Error(result.__error);
|
|
2440
2472
|
return { type, origin, entries: result ?? {} };
|
|
2441
2473
|
}
|
|
2442
|
-
async setStorage(type,
|
|
2474
|
+
async setStorage(type, key2, value) {
|
|
2443
2475
|
const storageType = type === "localStorage" ? "localStorage" : "sessionStorage";
|
|
2444
2476
|
if (value === null) {
|
|
2445
2477
|
await this.wc.executeJavaScript(
|
|
2446
|
-
`window.${storageType}.removeItem(${JSON.stringify(
|
|
2478
|
+
`window.${storageType}.removeItem(${JSON.stringify(key2)})`
|
|
2447
2479
|
);
|
|
2448
|
-
return `Removed "${
|
|
2480
|
+
return `Removed "${key2}" from ${type}`;
|
|
2449
2481
|
}
|
|
2450
2482
|
await this.wc.executeJavaScript(
|
|
2451
|
-
`window.${storageType}.setItem(${JSON.stringify(
|
|
2483
|
+
`window.${storageType}.setItem(${JSON.stringify(key2)}, ${JSON.stringify(value)})`
|
|
2452
2484
|
);
|
|
2453
|
-
return `Set ${type}["${
|
|
2485
|
+
return `Set ${type}["${key2}"] = ${value.length > 80 ? value.slice(0, 77) + "..." : value}`;
|
|
2454
2486
|
}
|
|
2455
2487
|
// ---------------------------------------------------------------------------
|
|
2456
2488
|
// Performance
|
|
@@ -3015,23 +3047,23 @@ class TabManager {
|
|
|
3015
3047
|
async saveTabAsPdf(id) {
|
|
3016
3048
|
const tab = this.tabs.get(id);
|
|
3017
3049
|
if (!tab) return null;
|
|
3018
|
-
const { canceled, filePath } = await electron.dialog.showSaveDialog({
|
|
3050
|
+
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
3019
3051
|
title: "Save Page as PDF",
|
|
3020
3052
|
defaultPath: sanitizePdfFilename(tab.state.title || "Vessel Page"),
|
|
3021
3053
|
filters: [{ name: "PDF", extensions: ["pdf"] }]
|
|
3022
3054
|
});
|
|
3023
|
-
if (canceled || !
|
|
3055
|
+
if (canceled || !filePath2) return null;
|
|
3024
3056
|
const data = await tab.view.webContents.printToPDF({
|
|
3025
3057
|
printBackground: true
|
|
3026
3058
|
});
|
|
3027
|
-
await fs.promises.writeFile(
|
|
3028
|
-
return
|
|
3059
|
+
await fs.promises.writeFile(filePath2, data);
|
|
3060
|
+
return filePath2;
|
|
3029
3061
|
}
|
|
3030
3062
|
async savePage(id, format = "MHTML") {
|
|
3031
3063
|
const tab = this.tabs.get(id);
|
|
3032
3064
|
if (!tab) return null;
|
|
3033
3065
|
const ext = format === "MHTML" ? "mhtml" : "html";
|
|
3034
|
-
const { canceled, filePath } = await electron.dialog.showSaveDialog({
|
|
3066
|
+
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
3035
3067
|
title: "Save Page As",
|
|
3036
3068
|
defaultPath: sanitizePageFilename(
|
|
3037
3069
|
tab.state.title || "Vessel Page",
|
|
@@ -3041,9 +3073,9 @@ class TabManager {
|
|
|
3041
3073
|
{ name: format === "MHTML" ? "MHTML" : "HTML", extensions: [ext] }
|
|
3042
3074
|
]
|
|
3043
3075
|
});
|
|
3044
|
-
if (canceled || !
|
|
3045
|
-
await tab.view.webContents.savePage(
|
|
3046
|
-
return
|
|
3076
|
+
if (canceled || !filePath2) return null;
|
|
3077
|
+
await tab.view.webContents.savePage(filePath2, format);
|
|
3078
|
+
return filePath2;
|
|
3047
3079
|
}
|
|
3048
3080
|
getActiveTab() {
|
|
3049
3081
|
return this.activeTabId ? this.tabs.get(this.activeTabId) : void 0;
|
|
@@ -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();
|
|
@@ -3127,11 +3159,11 @@ class TabManager {
|
|
|
3127
3159
|
this.pinTab(ids[index]);
|
|
3128
3160
|
}
|
|
3129
3161
|
if (tab.groupName && ids[index]) {
|
|
3130
|
-
const
|
|
3131
|
-
let groupId = restoredGroups.get(
|
|
3162
|
+
const key2 = `${tab.groupName}|${tab.groupColor ?? "blue"}`;
|
|
3163
|
+
let groupId = restoredGroups.get(key2);
|
|
3132
3164
|
if (!groupId) {
|
|
3133
3165
|
groupId = crypto$1.randomUUID();
|
|
3134
|
-
restoredGroups.set(
|
|
3166
|
+
restoredGroups.set(key2, groupId);
|
|
3135
3167
|
this.tabGroups.set(groupId, {
|
|
3136
3168
|
id: groupId,
|
|
3137
3169
|
name: tab.groupName,
|
|
@@ -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",
|
|
@@ -3456,6 +3489,11 @@ const Channels = {
|
|
|
3456
3489
|
DOWNLOAD_STARTED: "download:started",
|
|
3457
3490
|
DOWNLOAD_PROGRESS: "download:progress",
|
|
3458
3491
|
DOWNLOAD_DONE: "download:done",
|
|
3492
|
+
DOWNLOADS_GET: "downloads:get",
|
|
3493
|
+
DOWNLOADS_CLEAR: "downloads:clear",
|
|
3494
|
+
DOWNLOADS_OPEN: "downloads:open",
|
|
3495
|
+
DOWNLOADS_SHOW_IN_FOLDER: "downloads:show-in-folder",
|
|
3496
|
+
DOWNLOADS_UPDATE: "downloads:update",
|
|
3459
3497
|
// Premium
|
|
3460
3498
|
PREMIUM_GET_STATE: "premium:get-state",
|
|
3461
3499
|
PREMIUM_ACTIVATION_START: "premium:activation-start",
|
|
@@ -3518,7 +3556,14 @@ const Channels = {
|
|
|
3518
3556
|
CODEX_START_AUTH: "codex:start-auth",
|
|
3519
3557
|
CODEX_CANCEL_AUTH: "codex:cancel-auth",
|
|
3520
3558
|
CODEX_AUTH_STATUS: "codex:auth-status",
|
|
3521
|
-
CODEX_DISCONNECT: "codex:disconnect"
|
|
3559
|
+
CODEX_DISCONNECT: "codex:disconnect",
|
|
3560
|
+
// Updates
|
|
3561
|
+
UPDATES_CHECK: "updates:check",
|
|
3562
|
+
UPDATES_OPEN_DOWNLOAD: "updates:open-download",
|
|
3563
|
+
// Permissions
|
|
3564
|
+
PERMISSIONS_GET: "permissions:get",
|
|
3565
|
+
PERMISSIONS_CLEAR: "permissions:clear",
|
|
3566
|
+
PERMISSIONS_CLEAR_ORIGIN: "permissions:clear-origin"
|
|
3522
3567
|
};
|
|
3523
3568
|
const MAX_DETAIL_ITEMS = 3;
|
|
3524
3569
|
const MIN_BLOCK_SIMILARITY = 0.82;
|
|
@@ -3760,25 +3805,25 @@ function normalizeQueryValue(value) {
|
|
|
3760
3805
|
}
|
|
3761
3806
|
function serializeSnapshotParams(params) {
|
|
3762
3807
|
return params.map(
|
|
3763
|
-
([
|
|
3808
|
+
([key2, value]) => `${encodeURIComponent(key2)}=${encodeURIComponent(value)}`
|
|
3764
3809
|
).join("&");
|
|
3765
3810
|
}
|
|
3766
3811
|
function normalizeSnapshotParams(entries, pathname) {
|
|
3767
3812
|
return Array.from(entries).filter(
|
|
3768
|
-
([
|
|
3769
|
-
).map(([
|
|
3770
|
-
|
|
3813
|
+
([key2, value]) => shouldKeepSnapshotQueryParam(pathname, key2, value)
|
|
3814
|
+
).map(([key2, value]) => [
|
|
3815
|
+
key2.trim().toLowerCase(),
|
|
3771
3816
|
normalizeQueryValue(value)
|
|
3772
3817
|
]).sort(
|
|
3773
3818
|
([keyA, valueA], [keyB, valueB]) => keyA === keyB ? valueA.localeCompare(valueB) : keyA.localeCompare(keyB)
|
|
3774
3819
|
);
|
|
3775
3820
|
}
|
|
3776
3821
|
function shouldKeepSnapshotQueryParam(pathname, rawKey, value) {
|
|
3777
|
-
const
|
|
3778
|
-
if (!
|
|
3779
|
-
if (
|
|
3780
|
-
if (TRACKING_QUERY_KEYS.has(
|
|
3781
|
-
if (SNAPSHOT_QUERY_KEYS.has(
|
|
3822
|
+
const key2 = rawKey.trim().toLowerCase();
|
|
3823
|
+
if (!key2 || !value.trim()) return false;
|
|
3824
|
+
if (key2.startsWith("utm_")) return false;
|
|
3825
|
+
if (TRACKING_QUERY_KEYS.has(key2)) return false;
|
|
3826
|
+
if (SNAPSHOT_QUERY_KEYS.has(key2)) return true;
|
|
3782
3827
|
return /\/(search|results|browse|discover|find|category|tag|topics?|collections?|list)(\/|$)/i.test(
|
|
3783
3828
|
pathname
|
|
3784
3829
|
);
|
|
@@ -3863,15 +3908,15 @@ function load$2() {
|
|
|
3863
3908
|
const next = /* @__PURE__ */ new Map();
|
|
3864
3909
|
if (!Array.isArray(raw)) return next;
|
|
3865
3910
|
for (const entry of raw) {
|
|
3866
|
-
const
|
|
3867
|
-
if (
|
|
3911
|
+
const snapshot2 = normalizeStoredSnapshot(entry);
|
|
3912
|
+
if (snapshot2) next.set(snapshot2.url, snapshot2);
|
|
3868
3913
|
}
|
|
3869
3914
|
return next;
|
|
3870
3915
|
}
|
|
3871
3916
|
});
|
|
3872
3917
|
return snapshots;
|
|
3873
3918
|
}
|
|
3874
|
-
const persistence$
|
|
3919
|
+
const persistence$5 = createDebouncedJsonPersistence({
|
|
3875
3920
|
debounceMs: SAVE_DEBOUNCE_MS$3,
|
|
3876
3921
|
filePath: getFilePath$1(),
|
|
3877
3922
|
getValue: () => snapshots,
|
|
@@ -3890,21 +3935,21 @@ function getSnapshot(normalizedUrl) {
|
|
|
3890
3935
|
}
|
|
3891
3936
|
function saveSnapshot(rawUrl, title, textContent, headings) {
|
|
3892
3937
|
const s = load$2();
|
|
3893
|
-
const
|
|
3894
|
-
const
|
|
3895
|
-
url:
|
|
3938
|
+
const key2 = normalizeUrl(rawUrl);
|
|
3939
|
+
const snapshot2 = {
|
|
3940
|
+
url: key2,
|
|
3896
3941
|
title,
|
|
3897
3942
|
textContent: textContent.slice(0, MAX_TEXT_LENGTH),
|
|
3898
3943
|
headings: headings.map((h) => `${"#".repeat(h.level)} ${h.text}`).join("\n"),
|
|
3899
3944
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3900
3945
|
};
|
|
3901
|
-
s.delete(
|
|
3902
|
-
s.set(
|
|
3903
|
-
persistence$
|
|
3904
|
-
return
|
|
3946
|
+
s.delete(key2);
|
|
3947
|
+
s.set(key2, snapshot2);
|
|
3948
|
+
persistence$5.schedule();
|
|
3949
|
+
return snapshot2;
|
|
3905
3950
|
}
|
|
3906
3951
|
function flushPersist$2() {
|
|
3907
|
-
return persistence$
|
|
3952
|
+
return persistence$5.flush();
|
|
3908
3953
|
}
|
|
3909
3954
|
const SEARCH_ENGINE_HOSTS = [
|
|
3910
3955
|
"google.",
|
|
@@ -4219,11 +4264,11 @@ function sanitizeValue(value, depth = 0) {
|
|
|
4219
4264
|
const record = asRecord(value);
|
|
4220
4265
|
if (!record || depth >= MAX_DEPTH) return void 0;
|
|
4221
4266
|
const objectValue = {};
|
|
4222
|
-
for (const [
|
|
4223
|
-
if (SKIP_FIELDS.has(
|
|
4267
|
+
for (const [key2, entry] of Object.entries(record)) {
|
|
4268
|
+
if (SKIP_FIELDS.has(key2)) continue;
|
|
4224
4269
|
const normalized = sanitizeValue(entry, depth + 1);
|
|
4225
4270
|
if (normalized !== void 0) {
|
|
4226
|
-
objectValue[
|
|
4271
|
+
objectValue[key2] = normalized;
|
|
4227
4272
|
}
|
|
4228
4273
|
}
|
|
4229
4274
|
return Object.keys(objectValue).length > 0 ? objectValue : void 0;
|
|
@@ -4231,10 +4276,10 @@ function sanitizeValue(value, depth = 0) {
|
|
|
4231
4276
|
function buildNormalizedAttributes(record, types) {
|
|
4232
4277
|
const attributes = {};
|
|
4233
4278
|
const consumed = /* @__PURE__ */ new Set();
|
|
4234
|
-
const consume = (
|
|
4279
|
+
const consume = (key2, value) => {
|
|
4235
4280
|
if (value === void 0) return;
|
|
4236
|
-
consumed.add(
|
|
4237
|
-
attributes[
|
|
4281
|
+
consumed.add(key2);
|
|
4282
|
+
attributes[key2] = value;
|
|
4238
4283
|
};
|
|
4239
4284
|
if (types.includes("Recipe")) {
|
|
4240
4285
|
consume("yield", sanitizeValue(record.recipeYield));
|
|
@@ -4278,14 +4323,14 @@ function buildNormalizedAttributes(record, types) {
|
|
|
4278
4323
|
attributes.questions = questions;
|
|
4279
4324
|
}
|
|
4280
4325
|
}
|
|
4281
|
-
for (const [
|
|
4282
|
-
if (SKIP_FIELDS.has(
|
|
4283
|
-
if (
|
|
4326
|
+
for (const [key2, value] of Object.entries(record)) {
|
|
4327
|
+
if (SKIP_FIELDS.has(key2) || consumed.has(key2)) continue;
|
|
4328
|
+
if (key2 === "name" || key2 === "headline" || key2 === "url" || key2 === "description") {
|
|
4284
4329
|
continue;
|
|
4285
4330
|
}
|
|
4286
4331
|
const normalized = sanitizeValue(value);
|
|
4287
4332
|
if (normalized !== void 0) {
|
|
4288
|
-
attributes[
|
|
4333
|
+
attributes[key2] = normalized;
|
|
4289
4334
|
}
|
|
4290
4335
|
}
|
|
4291
4336
|
return attributes;
|
|
@@ -4305,7 +4350,7 @@ function collectCandidateEntities(value, results = [], seen = /* @__PURE__ */ ne
|
|
|
4305
4350
|
if (types.length > 0 || hasIdentity) {
|
|
4306
4351
|
results.push(record);
|
|
4307
4352
|
}
|
|
4308
|
-
for (const
|
|
4353
|
+
for (const key2 of [
|
|
4309
4354
|
"@graph",
|
|
4310
4355
|
"mainEntity",
|
|
4311
4356
|
"mainEntityOfPage",
|
|
@@ -4318,7 +4363,7 @@ function collectCandidateEntities(value, results = [], seen = /* @__PURE__ */ ne
|
|
|
4318
4363
|
"acceptedAnswer",
|
|
4319
4364
|
"suggestedAnswer"
|
|
4320
4365
|
]) {
|
|
4321
|
-
collectCandidateEntities(record[
|
|
4366
|
+
collectCandidateEntities(record[key2], results, seen);
|
|
4322
4367
|
}
|
|
4323
4368
|
return results;
|
|
4324
4369
|
}
|
|
@@ -4331,11 +4376,11 @@ function dedupeKey(entity) {
|
|
|
4331
4376
|
JSON.stringify(entity.attributes)
|
|
4332
4377
|
].join("::");
|
|
4333
4378
|
}
|
|
4334
|
-
function extractEntitiesFromRecords(
|
|
4335
|
-
if (!
|
|
4379
|
+
function extractEntitiesFromRecords(records2, source) {
|
|
4380
|
+
if (!records2 || records2.length === 0) return [];
|
|
4336
4381
|
const entities = [];
|
|
4337
4382
|
const seen = /* @__PURE__ */ new Set();
|
|
4338
|
-
for (const candidate of collectCandidateEntities(
|
|
4383
|
+
for (const candidate of collectCandidateEntities(records2)) {
|
|
4339
4384
|
const types = getTypes(candidate);
|
|
4340
4385
|
const name = firstString(candidate.name, candidate.headline);
|
|
4341
4386
|
const url = firstString(candidate.url, candidate["@id"]);
|
|
@@ -4355,9 +4400,9 @@ function extractEntitiesFromRecords(records, source) {
|
|
|
4355
4400
|
addIfPresent(entity, "name", name);
|
|
4356
4401
|
addIfPresent(entity, "url", url);
|
|
4357
4402
|
addIfPresent(entity, "description", description);
|
|
4358
|
-
const
|
|
4359
|
-
if (seen.has(
|
|
4360
|
-
seen.add(
|
|
4403
|
+
const key2 = dedupeKey(entity);
|
|
4404
|
+
if (seen.has(key2)) continue;
|
|
4405
|
+
seen.add(key2);
|
|
4361
4406
|
entities.push(entity);
|
|
4362
4407
|
}
|
|
4363
4408
|
return entities.slice(0, 25);
|
|
@@ -4380,13 +4425,13 @@ function extractEntityFromMetaTags(metaTags, pageTitle, pageUrl) {
|
|
|
4380
4425
|
const url = metaTags["og:url"] || metaTags.canonical || pageUrl;
|
|
4381
4426
|
const types = getMetaType(metaTags);
|
|
4382
4427
|
const attributes = {};
|
|
4383
|
-
for (const [
|
|
4384
|
-
if (
|
|
4428
|
+
for (const [key2, value] of Object.entries(metaTags)) {
|
|
4429
|
+
if (key2 === "og:title" || key2 === "twitter:title" || key2 === "title" || key2 === "og:description" || key2 === "description" || key2 === "twitter:description" || key2 === "og:url" || key2 === "canonical") {
|
|
4385
4430
|
continue;
|
|
4386
4431
|
}
|
|
4387
4432
|
const normalized = sanitizeValue(value);
|
|
4388
4433
|
if (normalized !== void 0) {
|
|
4389
|
-
attributes[
|
|
4434
|
+
attributes[key2] = normalized;
|
|
4390
4435
|
}
|
|
4391
4436
|
}
|
|
4392
4437
|
const entity = {
|
|
@@ -4439,9 +4484,9 @@ function extractStructuredDataFromJsonLd(jsonLd, microdata, rdfa, metaTags, page
|
|
|
4439
4484
|
const deduped = [];
|
|
4440
4485
|
const seen = /* @__PURE__ */ new Set();
|
|
4441
4486
|
for (const entity of candidates) {
|
|
4442
|
-
const
|
|
4443
|
-
if (seen.has(
|
|
4444
|
-
seen.add(
|
|
4487
|
+
const key2 = dedupeKey(entity);
|
|
4488
|
+
if (seen.has(key2)) continue;
|
|
4489
|
+
seen.add(key2);
|
|
4445
4490
|
deduped.push(entity);
|
|
4446
4491
|
}
|
|
4447
4492
|
if (deduped.length > 0) {
|
|
@@ -4455,9 +4500,9 @@ function extractStructuredDataFromJsonLd(jsonLd, microdata, rdfa, metaTags, page
|
|
|
4455
4500
|
pageHeadings
|
|
4456
4501
|
);
|
|
4457
4502
|
}
|
|
4458
|
-
function addIfPresent(target,
|
|
4503
|
+
function addIfPresent(target, key2, value) {
|
|
4459
4504
|
if (value !== void 0) {
|
|
4460
|
-
target[
|
|
4505
|
+
target[key2] = value;
|
|
4461
4506
|
}
|
|
4462
4507
|
}
|
|
4463
4508
|
function okResult(value) {
|
|
@@ -4714,6 +4759,7 @@ const POSTHOG_API_KEY = process.env.POSTHOG_API_KEY || "phc_OMeM3P5cxJwl14lOKxYa
|
|
|
4714
4759
|
const POSTHOG_HOST = process.env.POSTHOG_HOST || "https://us.i.posthog.com";
|
|
4715
4760
|
const BATCH_INTERVAL_MS = 6e4;
|
|
4716
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;
|
|
4717
4763
|
function getDeviceIdPath() {
|
|
4718
4764
|
return path.join(electron.app.getPath("userData"), ".vessel-device-id");
|
|
4719
4765
|
}
|
|
@@ -4742,12 +4788,22 @@ function isEnabled() {
|
|
|
4742
4788
|
if (process.env.VESSEL_DEV === "1") return false;
|
|
4743
4789
|
return loadSettings().telemetryEnabled !== false;
|
|
4744
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
|
+
}
|
|
4745
4801
|
function trackEvent(event, properties = {}) {
|
|
4746
4802
|
if (!isEnabled()) return;
|
|
4747
4803
|
eventQueue.push({
|
|
4748
4804
|
event,
|
|
4749
4805
|
properties: {
|
|
4750
|
-
...properties,
|
|
4806
|
+
...sanitizeTelemetryProperties(properties),
|
|
4751
4807
|
premium_status: isPremium() ? "premium" : "free",
|
|
4752
4808
|
app_version: electron.app.getVersion(),
|
|
4753
4809
|
platform: process.platform,
|
|
@@ -4770,8 +4826,8 @@ function trackProviderConfigured(providerId) {
|
|
|
4770
4826
|
provider_id: providerId
|
|
4771
4827
|
});
|
|
4772
4828
|
}
|
|
4773
|
-
function trackSettingChanged(
|
|
4774
|
-
trackEvent("setting_changed", { setting_key:
|
|
4829
|
+
function trackSettingChanged(key2) {
|
|
4830
|
+
trackEvent("setting_changed", { setting_key: key2 });
|
|
4775
4831
|
}
|
|
4776
4832
|
function trackApprovalModeChanged(mode) {
|
|
4777
4833
|
trackEvent("approval_mode_changed", { mode });
|
|
@@ -4782,8 +4838,8 @@ function trackBookmarkAction(action) {
|
|
|
4782
4838
|
function trackVaultAction(action) {
|
|
4783
4839
|
trackEvent("vault_action", { action });
|
|
4784
4840
|
}
|
|
4785
|
-
function trackExtractionFailed(
|
|
4786
|
-
trackEvent("extraction_failed", {
|
|
4841
|
+
function trackExtractionFailed(_domain, reason) {
|
|
4842
|
+
trackEvent("extraction_failed", { reason });
|
|
4787
4843
|
}
|
|
4788
4844
|
function trackPremiumFunnel(step, context) {
|
|
4789
4845
|
trackEvent("premium_funnel", { step, ...context });
|
|
@@ -4953,10 +5009,10 @@ function mapFormFields(forms, interactiveElements) {
|
|
|
4953
5009
|
}
|
|
4954
5010
|
}
|
|
4955
5011
|
for (const el of interactiveElements) {
|
|
4956
|
-
const
|
|
4957
|
-
if (formFieldSelectors.has(
|
|
5012
|
+
const key2 = el.selector || el.name || el.label || String(el.index);
|
|
5013
|
+
if (formFieldSelectors.has(key2)) {
|
|
4958
5014
|
fields.push({
|
|
4959
|
-
name: el.name || el.label ||
|
|
5015
|
+
name: el.name || el.label || key2,
|
|
4960
5016
|
type: mapInputType(el),
|
|
4961
5017
|
label: el.label,
|
|
4962
5018
|
required: el.required,
|
|
@@ -6195,12 +6251,12 @@ function loadHistory() {
|
|
|
6195
6251
|
return next;
|
|
6196
6252
|
}
|
|
6197
6253
|
});
|
|
6198
|
-
for (const [
|
|
6199
|
-
recentPageDiffBursts.set(
|
|
6254
|
+
for (const [key2, bursts] of loaded.entries()) {
|
|
6255
|
+
recentPageDiffBursts.set(key2, bursts);
|
|
6200
6256
|
}
|
|
6201
6257
|
return recentPageDiffBursts;
|
|
6202
6258
|
}
|
|
6203
|
-
const persistence$
|
|
6259
|
+
const persistence$4 = createDebouncedJsonPersistence({
|
|
6204
6260
|
debounceMs: SAVE_DEBOUNCE_MS$2,
|
|
6205
6261
|
filePath: getHistoryFilePath(),
|
|
6206
6262
|
getValue: () => recentPageDiffBursts,
|
|
@@ -6217,20 +6273,20 @@ function getLatestPageDiff(rawUrl) {
|
|
|
6217
6273
|
}
|
|
6218
6274
|
function getPageDiffBursts(rawUrl) {
|
|
6219
6275
|
if (!shouldTrackSnapshotUrl(rawUrl)) return [];
|
|
6220
|
-
const
|
|
6276
|
+
const key2 = normalizeUrl(rawUrl);
|
|
6221
6277
|
const history = loadHistory();
|
|
6222
|
-
const bursts = prunePageDiffHistory(history.get(
|
|
6278
|
+
const bursts = prunePageDiffHistory(history.get(key2) ?? [], {
|
|
6223
6279
|
maxAgeDays: MAX_HISTORY_DAYS,
|
|
6224
6280
|
maxItems: MAX_PERSISTED_DIFF_BURSTS
|
|
6225
6281
|
});
|
|
6226
|
-
const current = history.get(
|
|
6282
|
+
const current = history.get(key2) ?? [];
|
|
6227
6283
|
if (current.length !== bursts.length) {
|
|
6228
6284
|
if (bursts.length > 0) {
|
|
6229
|
-
history.set(
|
|
6285
|
+
history.set(key2, bursts);
|
|
6230
6286
|
} else {
|
|
6231
|
-
history.delete(
|
|
6287
|
+
history.delete(key2);
|
|
6232
6288
|
}
|
|
6233
|
-
persistence$
|
|
6289
|
+
persistence$4.schedule();
|
|
6234
6290
|
}
|
|
6235
6291
|
return bursts.slice().reverse();
|
|
6236
6292
|
}
|
|
@@ -6238,20 +6294,20 @@ function summarizeDiffBurst(diff) {
|
|
|
6238
6294
|
const items = diff.changes.slice(0, 2).map((change) => `${change.section}: ${change.summary}`);
|
|
6239
6295
|
return items.join(" | ");
|
|
6240
6296
|
}
|
|
6241
|
-
function enrichWithBurstHistory(
|
|
6297
|
+
function enrichWithBurstHistory(key2, diff) {
|
|
6242
6298
|
const detectedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6243
6299
|
const nextBurst = {
|
|
6244
6300
|
detectedAt,
|
|
6245
6301
|
summary: summarizeDiffBurst(diff)
|
|
6246
6302
|
};
|
|
6247
6303
|
const history = loadHistory();
|
|
6248
|
-
const bursts = appendPageDiffHistoryItem(history.get(
|
|
6304
|
+
const bursts = appendPageDiffHistoryItem(history.get(key2) ?? [], nextBurst, {
|
|
6249
6305
|
maxAgeDays: MAX_HISTORY_DAYS,
|
|
6250
6306
|
maxItems: MAX_PERSISTED_DIFF_BURSTS,
|
|
6251
6307
|
now: Date.parse(detectedAt)
|
|
6252
6308
|
});
|
|
6253
|
-
history.set(
|
|
6254
|
-
persistence$
|
|
6309
|
+
history.set(key2, bursts);
|
|
6310
|
+
persistence$4.schedule();
|
|
6255
6311
|
const recentBursts = bursts.slice(-5);
|
|
6256
6312
|
return {
|
|
6257
6313
|
...diff,
|
|
@@ -6264,8 +6320,8 @@ function enrichWithBurstHistory(key, diff) {
|
|
|
6264
6320
|
async function capturePageSnapshot(url, wc, sendToRendererViews) {
|
|
6265
6321
|
try {
|
|
6266
6322
|
if (!shouldTrackSnapshotUrl(url)) return;
|
|
6267
|
-
const
|
|
6268
|
-
const oldSnap = getSnapshot(
|
|
6323
|
+
const key2 = normalizeUrl(url);
|
|
6324
|
+
const oldSnap = getSnapshot(key2);
|
|
6269
6325
|
const content = await extractContent(wc);
|
|
6270
6326
|
const textContent = content.content || "";
|
|
6271
6327
|
const title = content.title || "";
|
|
@@ -6274,14 +6330,14 @@ async function capturePageSnapshot(url, wc, sendToRendererViews) {
|
|
|
6274
6330
|
if (oldSnap) {
|
|
6275
6331
|
const diff = diffSnapshots(oldSnap, textContent, title, currentHeadings);
|
|
6276
6332
|
if (diff.hasChanges) {
|
|
6277
|
-
const enrichedDiff = enrichWithBurstHistory(
|
|
6278
|
-
latestPageDiffs.set(
|
|
6333
|
+
const enrichedDiff = enrichWithBurstHistory(key2, diff);
|
|
6334
|
+
latestPageDiffs.set(key2, enrichedDiff);
|
|
6279
6335
|
sendToRendererViews(Channels.PAGE_CHANGED, enrichedDiff);
|
|
6280
6336
|
} else {
|
|
6281
|
-
latestPageDiffs.delete(
|
|
6337
|
+
latestPageDiffs.delete(key2);
|
|
6282
6338
|
}
|
|
6283
6339
|
} else {
|
|
6284
|
-
latestPageDiffs.delete(
|
|
6340
|
+
latestPageDiffs.delete(key2);
|
|
6285
6341
|
}
|
|
6286
6342
|
saveSnapshot(url, title, textContent, headings);
|
|
6287
6343
|
} catch {
|
|
@@ -6333,19 +6389,19 @@ function schedulePageSnapshotCapture(wc, sendToRendererViews, delayMs = 0) {
|
|
|
6333
6389
|
function enableClipboardShortcuts(view) {
|
|
6334
6390
|
view.webContents.on("before-input-event", (event, input) => {
|
|
6335
6391
|
if (!input.control && !input.meta) return;
|
|
6336
|
-
const
|
|
6392
|
+
const key2 = input.key.toLowerCase();
|
|
6337
6393
|
const wc = view.webContents;
|
|
6338
6394
|
if (input.type === "keyDown") {
|
|
6339
|
-
if (
|
|
6395
|
+
if (key2 === "c") {
|
|
6340
6396
|
wc.copy();
|
|
6341
6397
|
event.preventDefault();
|
|
6342
|
-
} else if (
|
|
6398
|
+
} else if (key2 === "v") {
|
|
6343
6399
|
wc.paste();
|
|
6344
6400
|
event.preventDefault();
|
|
6345
|
-
} else if (
|
|
6401
|
+
} else if (key2 === "x") {
|
|
6346
6402
|
wc.cut();
|
|
6347
6403
|
event.preventDefault();
|
|
6348
|
-
} else if (
|
|
6404
|
+
} else if (key2 === "a") {
|
|
6349
6405
|
wc.selectAll();
|
|
6350
6406
|
event.preventDefault();
|
|
6351
6407
|
}
|
|
@@ -6759,15 +6815,15 @@ function onRuntimeHealthChange(listener) {
|
|
|
6759
6815
|
};
|
|
6760
6816
|
}
|
|
6761
6817
|
function getMcpStatus() {
|
|
6762
|
-
return state$
|
|
6818
|
+
return state$3.mcp.status;
|
|
6763
6819
|
}
|
|
6764
6820
|
function emitRuntimeHealthChange() {
|
|
6765
|
-
const
|
|
6821
|
+
const snapshot2 = getRuntimeHealth();
|
|
6766
6822
|
for (const listener of runtimeHealthChangeListeners) {
|
|
6767
|
-
listener(
|
|
6823
|
+
listener(snapshot2);
|
|
6768
6824
|
}
|
|
6769
6825
|
}
|
|
6770
|
-
const state$
|
|
6826
|
+
const state$3 = {
|
|
6771
6827
|
userDataPath: "",
|
|
6772
6828
|
settingsPath: "",
|
|
6773
6829
|
startupIssues: [],
|
|
@@ -6780,43 +6836,43 @@ const state$2 = {
|
|
|
6780
6836
|
}
|
|
6781
6837
|
};
|
|
6782
6838
|
function initializeRuntimeHealth(paths) {
|
|
6783
|
-
state$
|
|
6784
|
-
state$
|
|
6785
|
-
state$
|
|
6786
|
-
state$
|
|
6787
|
-
state$
|
|
6788
|
-
state$
|
|
6789
|
-
state$
|
|
6839
|
+
state$3.userDataPath = paths.userDataPath;
|
|
6840
|
+
state$3.settingsPath = paths.settingsPath;
|
|
6841
|
+
state$3.mcp.configuredPort = paths.configuredPort;
|
|
6842
|
+
state$3.mcp.activePort = null;
|
|
6843
|
+
state$3.mcp.endpoint = null;
|
|
6844
|
+
state$3.mcp.status = "stopped";
|
|
6845
|
+
state$3.mcp.message = "MCP server has not started yet.";
|
|
6790
6846
|
emitRuntimeHealthChange();
|
|
6791
6847
|
}
|
|
6792
6848
|
function setStartupIssues(issues) {
|
|
6793
|
-
state$
|
|
6849
|
+
state$3.startupIssues = issues.map((issue) => ({ ...issue }));
|
|
6794
6850
|
emitRuntimeHealthChange();
|
|
6795
6851
|
}
|
|
6796
6852
|
function getRuntimeHealth() {
|
|
6797
6853
|
return {
|
|
6798
|
-
userDataPath: state$
|
|
6799
|
-
settingsPath: state$
|
|
6800
|
-
startupIssues: state$
|
|
6801
|
-
mcp: { ...state$
|
|
6854
|
+
userDataPath: state$3.userDataPath,
|
|
6855
|
+
settingsPath: state$3.settingsPath,
|
|
6856
|
+
startupIssues: state$3.startupIssues.map((issue) => ({ ...issue })),
|
|
6857
|
+
mcp: { ...state$3.mcp }
|
|
6802
6858
|
};
|
|
6803
6859
|
}
|
|
6804
6860
|
function setMcpHealth(update) {
|
|
6805
6861
|
if (typeof update.configuredPort === "number") {
|
|
6806
|
-
state$
|
|
6862
|
+
state$3.mcp.configuredPort = update.configuredPort;
|
|
6807
6863
|
}
|
|
6808
6864
|
if ("activePort" in update) {
|
|
6809
|
-
state$
|
|
6865
|
+
state$3.mcp.activePort = update.activePort ?? null;
|
|
6810
6866
|
}
|
|
6811
6867
|
if ("endpoint" in update) {
|
|
6812
|
-
state$
|
|
6868
|
+
state$3.mcp.endpoint = update.endpoint ?? null;
|
|
6813
6869
|
}
|
|
6814
|
-
const prevStatus = state$
|
|
6815
|
-
state$
|
|
6816
|
-
state$
|
|
6817
|
-
if (prevStatus !== state$
|
|
6870
|
+
const prevStatus = state$3.mcp.status;
|
|
6871
|
+
state$3.mcp.status = update.status;
|
|
6872
|
+
state$3.mcp.message = update.message;
|
|
6873
|
+
if (prevStatus !== state$3.mcp.status) {
|
|
6818
6874
|
for (const listener of mcpStatusChangeListeners) {
|
|
6819
|
-
listener(state$
|
|
6875
|
+
listener(state$3.mcp.status);
|
|
6820
6876
|
}
|
|
6821
6877
|
}
|
|
6822
6878
|
emitRuntimeHealthChange();
|
|
@@ -7728,8 +7784,8 @@ function scalarArgsForTool(name, scalar) {
|
|
|
7728
7784
|
return null;
|
|
7729
7785
|
}
|
|
7730
7786
|
function firstStringArg(args, keys) {
|
|
7731
|
-
for (const
|
|
7732
|
-
const value = args[
|
|
7787
|
+
for (const key2 of keys) {
|
|
7788
|
+
const value = args[key2];
|
|
7733
7789
|
if (typeof value === "string" && value.trim()) {
|
|
7734
7790
|
return value.trim();
|
|
7735
7791
|
}
|
|
@@ -8461,6 +8517,17 @@ class OpenAICompatProvider {
|
|
|
8461
8517
|
this.abortController?.abort();
|
|
8462
8518
|
}
|
|
8463
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
|
+
}
|
|
8464
8531
|
const logger$g = createLogger("CodexOAuth");
|
|
8465
8532
|
const ISSUER = "https://auth.openai.com";
|
|
8466
8533
|
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
@@ -8797,7 +8864,7 @@ async function startCodexOAuth(onStatus) {
|
|
|
8797
8864
|
activeFlow.port = port;
|
|
8798
8865
|
const authUrl = buildAuthorizeUrl(port, pkce, state2);
|
|
8799
8866
|
safeOnStatus("waiting");
|
|
8800
|
-
|
|
8867
|
+
openExternalAllowlisted(authUrl, { hosts: ["auth.openai.com"] }).catch((err) => {
|
|
8801
8868
|
logger$g.warn("Failed to open browser, user will need the URL:", err);
|
|
8802
8869
|
});
|
|
8803
8870
|
}).catch(wrappedReject);
|
|
@@ -9020,8 +9087,8 @@ function extractLlamaCppCtxSize(payload) {
|
|
|
9020
9087
|
const current = queue.shift();
|
|
9021
9088
|
if (!current || typeof current !== "object" || visited.has(current)) continue;
|
|
9022
9089
|
visited.add(current);
|
|
9023
|
-
for (const [
|
|
9024
|
-
if (typeof value === "number" && Number.isFinite(value) && /^(n_ctx|ctx_size|context_size)$/i.test(
|
|
9090
|
+
for (const [key2, value] of Object.entries(current)) {
|
|
9091
|
+
if (typeof value === "number" && Number.isFinite(value) && /^(n_ctx|ctx_size|context_size)$/i.test(key2)) {
|
|
9025
9092
|
return value;
|
|
9026
9093
|
}
|
|
9027
9094
|
if (value && typeof value === "object") {
|
|
@@ -9243,14 +9310,14 @@ function normalizeStoredRadioOption(option) {
|
|
|
9243
9310
|
function dedupeCandidates(actions) {
|
|
9244
9311
|
const seen = /* @__PURE__ */ new Set();
|
|
9245
9312
|
return actions.filter((action) => {
|
|
9246
|
-
const
|
|
9313
|
+
const key2 = [
|
|
9247
9314
|
action.selector || "",
|
|
9248
9315
|
action.label || "",
|
|
9249
9316
|
action.role || "",
|
|
9250
9317
|
action.labelSource || ""
|
|
9251
9318
|
].join("::");
|
|
9252
|
-
if (seen.has(
|
|
9253
|
-
seen.add(
|
|
9319
|
+
if (seen.has(key2)) return false;
|
|
9320
|
+
seen.add(key2);
|
|
9254
9321
|
return true;
|
|
9255
9322
|
});
|
|
9256
9323
|
}
|
|
@@ -9495,11 +9562,11 @@ function getQuantityElements(page) {
|
|
|
9495
9562
|
];
|
|
9496
9563
|
return elements.filter((el) => {
|
|
9497
9564
|
if (!isQuantityLike(el)) return false;
|
|
9498
|
-
const
|
|
9565
|
+
const key2 = String(
|
|
9499
9566
|
el.index ?? el.selector ?? `${el.type}|${el.name || ""}|${el.label || ""}|${el.value || ""}`
|
|
9500
9567
|
);
|
|
9501
|
-
if (seen.has(
|
|
9502
|
-
seen.add(
|
|
9568
|
+
if (seen.has(key2)) return false;
|
|
9569
|
+
seen.add(key2);
|
|
9503
9570
|
return true;
|
|
9504
9571
|
});
|
|
9505
9572
|
}
|
|
@@ -9541,9 +9608,9 @@ function getCartItemLinks(page) {
|
|
|
9541
9608
|
return false;
|
|
9542
9609
|
}
|
|
9543
9610
|
if (blockedText.test(text) || blockedHref.test(href)) return false;
|
|
9544
|
-
const
|
|
9545
|
-
if (seen.has(
|
|
9546
|
-
seen.add(
|
|
9611
|
+
const key2 = `${normalizeComparable(text)}|${normalizeUrlForMatch(href) || href}`;
|
|
9612
|
+
if (seen.has(key2)) return false;
|
|
9613
|
+
seen.add(key2);
|
|
9547
9614
|
return true;
|
|
9548
9615
|
}).slice(0, 12);
|
|
9549
9616
|
}
|
|
@@ -9644,11 +9711,11 @@ function getPurchaseActionElements(page, options) {
|
|
|
9644
9711
|
if (!isPurchaseActionElement(el)) return false;
|
|
9645
9712
|
if (visibleOnly && !isVisibleToUser(el)) return false;
|
|
9646
9713
|
if (el.blockedByOverlay) return false;
|
|
9647
|
-
const
|
|
9714
|
+
const key2 = String(
|
|
9648
9715
|
el.index ?? el.selector ?? `${el.type}|${el.text || ""}|${el.label || ""}|${el.href || ""}`
|
|
9649
9716
|
);
|
|
9650
|
-
if (seen.has(
|
|
9651
|
-
seen.add(
|
|
9717
|
+
if (seen.has(key2)) return false;
|
|
9718
|
+
seen.add(key2);
|
|
9652
9719
|
return true;
|
|
9653
9720
|
}).sort((a, b) => {
|
|
9654
9721
|
const delta = purchaseActionPriority(a) - purchaseActionPriority(b);
|
|
@@ -9665,10 +9732,10 @@ function getOffscreenPurchaseActionElements(page) {
|
|
|
9665
9732
|
)
|
|
9666
9733
|
);
|
|
9667
9734
|
return getPurchaseActionElements(page, { visibleOnly: false }).filter((el) => {
|
|
9668
|
-
const
|
|
9735
|
+
const key2 = String(
|
|
9669
9736
|
el.index ?? el.selector ?? `${el.type}|${el.text || ""}|${el.label || ""}|${el.href || ""}`
|
|
9670
9737
|
);
|
|
9671
|
-
return !visibleKeys.has(
|
|
9738
|
+
return !visibleKeys.has(key2) && el.visible !== false;
|
|
9672
9739
|
});
|
|
9673
9740
|
}
|
|
9674
9741
|
function getDialogFocusedElements(page) {
|
|
@@ -9934,7 +10001,7 @@ function formatStructuredValue(value, depth = 0) {
|
|
|
9934
10001
|
const rendered = value.map((item) => formatStructuredValue(item, depth + 1)).filter(Boolean).slice(0, depth === 0 ? 8 : 5);
|
|
9935
10002
|
return rendered.join(depth === 0 ? ", " : " | ");
|
|
9936
10003
|
}
|
|
9937
|
-
const entries = Object.entries(value).slice(0, 6).map(([
|
|
10004
|
+
const entries = Object.entries(value).slice(0, 6).map(([key2, entry]) => `${key2}: ${formatStructuredValue(entry, depth + 1)}`).filter((entry) => !entry.endsWith(": "));
|
|
9938
10005
|
return entries.join(", ");
|
|
9939
10006
|
}
|
|
9940
10007
|
function formatStructuredEntities(entities) {
|
|
@@ -9949,13 +10016,13 @@ function formatStructuredEntities(entities) {
|
|
|
9949
10016
|
if (entity.url && entity.url !== entity.name) {
|
|
9950
10017
|
lines.push(` url: ${entity.url}`);
|
|
9951
10018
|
}
|
|
9952
|
-
for (const [
|
|
10019
|
+
for (const [key2, value] of Object.entries(entity.attributes).slice(
|
|
9953
10020
|
0,
|
|
9954
10021
|
8
|
|
9955
10022
|
)) {
|
|
9956
10023
|
const rendered = formatStructuredValue(value);
|
|
9957
10024
|
if (rendered) {
|
|
9958
|
-
lines.push(` ${
|
|
10025
|
+
lines.push(` ${key2}: ${rendered}`);
|
|
9959
10026
|
}
|
|
9960
10027
|
}
|
|
9961
10028
|
return lines.join("\n");
|
|
@@ -10053,17 +10120,17 @@ function formatJsonLd(items) {
|
|
|
10053
10120
|
}
|
|
10054
10121
|
return String(val);
|
|
10055
10122
|
};
|
|
10056
|
-
for (const
|
|
10057
|
-
if (
|
|
10058
|
-
seen.add(
|
|
10059
|
-
const rendered = renderValue(item[
|
|
10060
|
-
if (rendered) lines.push(` ${
|
|
10123
|
+
for (const key2 of priorityFields) {
|
|
10124
|
+
if (key2 in item) {
|
|
10125
|
+
seen.add(key2);
|
|
10126
|
+
const rendered = renderValue(item[key2]);
|
|
10127
|
+
if (rendered) lines.push(` ${key2}: ${rendered}`);
|
|
10061
10128
|
}
|
|
10062
10129
|
}
|
|
10063
|
-
for (const [
|
|
10064
|
-
if (seen.has(
|
|
10130
|
+
for (const [key2, val] of Object.entries(item)) {
|
|
10131
|
+
if (seen.has(key2) || SKIP.has(key2) || key2 === "@type") continue;
|
|
10065
10132
|
const rendered = renderValue(val);
|
|
10066
|
-
if (rendered) lines.push(` ${
|
|
10133
|
+
if (rendered) lines.push(` ${key2}: ${rendered}`);
|
|
10067
10134
|
}
|
|
10068
10135
|
lines.push("");
|
|
10069
10136
|
}
|
|
@@ -10198,9 +10265,9 @@ function getResultCandidates(page) {
|
|
|
10198
10265
|
);
|
|
10199
10266
|
const seen = /* @__PURE__ */ new Set();
|
|
10200
10267
|
return scored.map(({ element }) => element).filter((element) => {
|
|
10201
|
-
const
|
|
10202
|
-
if (seen.has(
|
|
10203
|
-
seen.add(
|
|
10268
|
+
const key2 = `${normalizeComparable(element.text || "")}|${normalizeUrlForMatch(element.href) || ""}`;
|
|
10269
|
+
if (seen.has(key2)) return false;
|
|
10270
|
+
seen.add(key2);
|
|
10204
10271
|
return true;
|
|
10205
10272
|
});
|
|
10206
10273
|
}
|
|
@@ -10997,9 +11064,9 @@ function getCompactPrimaryResultLinks(page, options) {
|
|
|
10997
11064
|
})).filter(({ score }) => score >= (listingLike ? 5 : 7)).sort(
|
|
10998
11065
|
(a, b) => b.score - a.score || (a.element.index ?? Number.MAX_SAFE_INTEGER) - (b.element.index ?? Number.MAX_SAFE_INTEGER)
|
|
10999
11066
|
).map(({ element }) => element).filter((element) => {
|
|
11000
|
-
const
|
|
11001
|
-
if (seen.has(
|
|
11002
|
-
seen.add(
|
|
11067
|
+
const key2 = `${normalizeComparable(element.text)}|${normalizeUrlForMatch(element.href) || ""}`;
|
|
11068
|
+
if (seen.has(key2)) return false;
|
|
11069
|
+
seen.add(key2);
|
|
11003
11070
|
return true;
|
|
11004
11071
|
}).slice(0, max);
|
|
11005
11072
|
}
|
|
@@ -11045,9 +11112,9 @@ function formatElement(element) {
|
|
|
11045
11112
|
function uniqueElements(elements) {
|
|
11046
11113
|
const seen = /* @__PURE__ */ new Set();
|
|
11047
11114
|
return elements.filter((element) => {
|
|
11048
|
-
const
|
|
11049
|
-
if (seen.has(
|
|
11050
|
-
seen.add(
|
|
11115
|
+
const key2 = `${element.index ?? ""}|${element.type}|${elementLabel(element)}|${element.href ?? ""}`;
|
|
11116
|
+
if (seen.has(key2)) return false;
|
|
11117
|
+
seen.add(key2);
|
|
11051
11118
|
return true;
|
|
11052
11119
|
});
|
|
11053
11120
|
}
|
|
@@ -12370,12 +12437,12 @@ function normalizeAgentHints(value) {
|
|
|
12370
12437
|
return void 0;
|
|
12371
12438
|
}
|
|
12372
12439
|
const normalized = Object.fromEntries(
|
|
12373
|
-
Object.entries(value).map(([
|
|
12440
|
+
Object.entries(value).map(([key2, hint]) => [key2.trim(), normalizeOptionalString(hint)]).filter((entry) => Boolean(entry[0] && entry[1]))
|
|
12374
12441
|
);
|
|
12375
12442
|
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
12376
12443
|
}
|
|
12377
|
-
function hasOwn(value,
|
|
12378
|
-
return Object.prototype.hasOwnProperty.call(value,
|
|
12444
|
+
function hasOwn(value, key2) {
|
|
12445
|
+
return Object.prototype.hasOwnProperty.call(value, key2);
|
|
12379
12446
|
}
|
|
12380
12447
|
function normalizeBookmarkMetadata(input) {
|
|
12381
12448
|
const normalized = {};
|
|
@@ -12413,7 +12480,7 @@ const UNSORTED_ID = "unsorted";
|
|
|
12413
12480
|
const ARCHIVE_FOLDER_NAME = "Archive";
|
|
12414
12481
|
const NETSCAPE_BOOKMARKS_DOCTYPE = "<!DOCTYPE NETSCAPE-Bookmark-file-1>";
|
|
12415
12482
|
const SAVE_DEBOUNCE_MS$1 = 250;
|
|
12416
|
-
let state$
|
|
12483
|
+
let state$2 = null;
|
|
12417
12484
|
const listeners = /* @__PURE__ */ new Set();
|
|
12418
12485
|
function cloneState(current) {
|
|
12419
12486
|
return {
|
|
@@ -12428,18 +12495,18 @@ function createPersistence() {
|
|
|
12428
12495
|
return createDebouncedJsonPersistence({
|
|
12429
12496
|
debounceMs: SAVE_DEBOUNCE_MS$1,
|
|
12430
12497
|
filePath: getBookmarksPath(),
|
|
12431
|
-
getValue: () => state$
|
|
12498
|
+
getValue: () => state$2,
|
|
12432
12499
|
logLabel: "bookmarks"
|
|
12433
12500
|
});
|
|
12434
12501
|
}
|
|
12435
|
-
let persistence$
|
|
12502
|
+
let persistence$3 = null;
|
|
12436
12503
|
function getPersistence() {
|
|
12437
|
-
persistence$
|
|
12438
|
-
return persistence$
|
|
12504
|
+
persistence$3 ??= createPersistence();
|
|
12505
|
+
return persistence$3;
|
|
12439
12506
|
}
|
|
12440
12507
|
function load$1() {
|
|
12441
|
-
if (state$
|
|
12442
|
-
state$
|
|
12508
|
+
if (state$2) return state$2;
|
|
12509
|
+
state$2 = loadJsonFile({
|
|
12443
12510
|
filePath: getBookmarksPath(),
|
|
12444
12511
|
fallback: { folders: [], bookmarks: [] },
|
|
12445
12512
|
parse: (raw) => {
|
|
@@ -12450,23 +12517,23 @@ function load$1() {
|
|
|
12450
12517
|
};
|
|
12451
12518
|
}
|
|
12452
12519
|
});
|
|
12453
|
-
return state$
|
|
12520
|
+
return state$2;
|
|
12454
12521
|
}
|
|
12455
|
-
function save() {
|
|
12522
|
+
function save$1() {
|
|
12456
12523
|
getPersistence().schedule();
|
|
12457
12524
|
}
|
|
12458
12525
|
function assignDefinedBookmarkFields(bookmark, fields) {
|
|
12459
12526
|
if (!fields) return;
|
|
12460
|
-
for (const [
|
|
12527
|
+
for (const [key2, value] of Object.entries(fields)) {
|
|
12461
12528
|
if (value === void 0) continue;
|
|
12462
|
-
Object.assign(bookmark, { [
|
|
12529
|
+
Object.assign(bookmark, { [key2]: value });
|
|
12463
12530
|
}
|
|
12464
12531
|
}
|
|
12465
|
-
function emit() {
|
|
12466
|
-
if (!state$
|
|
12467
|
-
const
|
|
12532
|
+
function emit$2() {
|
|
12533
|
+
if (!state$2) return;
|
|
12534
|
+
const snapshot2 = cloneState(state$2);
|
|
12468
12535
|
for (const listener of listeners) {
|
|
12469
|
-
listener(
|
|
12536
|
+
listener(snapshot2);
|
|
12470
12537
|
}
|
|
12471
12538
|
}
|
|
12472
12539
|
function escapeBookmarkHtml(value) {
|
|
@@ -12483,7 +12550,7 @@ function getBookmarkDescription(bookmark) {
|
|
|
12483
12550
|
bookmark.intent ? `Intent: ${bookmark.intent}` : "",
|
|
12484
12551
|
bookmark.expectedContent ? `Expected content: ${bookmark.expectedContent}` : "",
|
|
12485
12552
|
bookmark.keyFields?.length ? `Key fields: ${bookmark.keyFields.join(", ")}` : "",
|
|
12486
|
-
bookmark.agentHints && Object.keys(bookmark.agentHints).length > 0 ? `Agent hints: ${Object.entries(bookmark.agentHints).map(([
|
|
12553
|
+
bookmark.agentHints && Object.keys(bookmark.agentHints).length > 0 ? `Agent hints: ${Object.entries(bookmark.agentHints).map(([key2, value]) => `${key2}: ${value}`).join("; ")}` : ""
|
|
12487
12554
|
].filter(Boolean);
|
|
12488
12555
|
return lines.join("\n");
|
|
12489
12556
|
}
|
|
@@ -12586,28 +12653,28 @@ function subscribe(listener) {
|
|
|
12586
12653
|
};
|
|
12587
12654
|
}
|
|
12588
12655
|
function clearAll() {
|
|
12589
|
-
state$
|
|
12590
|
-
save();
|
|
12591
|
-
emit();
|
|
12656
|
+
state$2 = { folders: [], bookmarks: [] };
|
|
12657
|
+
save$1();
|
|
12658
|
+
emit$2();
|
|
12592
12659
|
}
|
|
12593
12660
|
function getBookmark(id) {
|
|
12594
12661
|
load$1();
|
|
12595
|
-
const bookmark = state$
|
|
12662
|
+
const bookmark = state$2.bookmarks.find((item) => item.id === id);
|
|
12596
12663
|
return bookmark ? { ...bookmark } : null;
|
|
12597
12664
|
}
|
|
12598
12665
|
function getBookmarkByUrl(url) {
|
|
12599
12666
|
load$1();
|
|
12600
12667
|
const normalized = url.trim();
|
|
12601
12668
|
if (!normalized) return null;
|
|
12602
|
-
const bookmark = [...state$
|
|
12669
|
+
const bookmark = [...state$2.bookmarks].reverse().find((item) => item.url === normalized);
|
|
12603
12670
|
return bookmark ? { ...bookmark } : null;
|
|
12604
12671
|
}
|
|
12605
12672
|
function getBookmarkByUrlInFolder(url, folderId) {
|
|
12606
12673
|
load$1();
|
|
12607
12674
|
const normalizedUrl = url.trim();
|
|
12608
12675
|
if (!normalizedUrl) return null;
|
|
12609
|
-
const targetFolderId = folderId && folderId !== UNSORTED_ID ? state$
|
|
12610
|
-
const bookmark = [...state$
|
|
12676
|
+
const targetFolderId = folderId && folderId !== UNSORTED_ID ? state$2.folders.find((f) => f.id === folderId)?.id ?? UNSORTED_ID : UNSORTED_ID;
|
|
12677
|
+
const bookmark = [...state$2.bookmarks].reverse().find(
|
|
12611
12678
|
(item) => item.url === normalizedUrl && item.folderId === targetFolderId
|
|
12612
12679
|
);
|
|
12613
12680
|
return bookmark ? { ...bookmark } : null;
|
|
@@ -12615,14 +12682,14 @@ function getBookmarkByUrlInFolder(url, folderId) {
|
|
|
12615
12682
|
function getFolder(id) {
|
|
12616
12683
|
load$1();
|
|
12617
12684
|
if (!id || id === UNSORTED_ID) return null;
|
|
12618
|
-
const folder = state$
|
|
12685
|
+
const folder = state$2.folders.find((item) => item.id === id);
|
|
12619
12686
|
return folder ? { ...folder } : null;
|
|
12620
12687
|
}
|
|
12621
12688
|
function findFolderByName(name) {
|
|
12622
12689
|
load$1();
|
|
12623
12690
|
const normalized = name.trim().toLowerCase();
|
|
12624
12691
|
if (!normalized || normalized === "unsorted") return null;
|
|
12625
|
-
const folder = state$
|
|
12692
|
+
const folder = state$2.folders.find(
|
|
12626
12693
|
(item) => item.name.trim().toLowerCase() === normalized
|
|
12627
12694
|
);
|
|
12628
12695
|
return folder ? { ...folder } : null;
|
|
@@ -12630,7 +12697,7 @@ function findFolderByName(name) {
|
|
|
12630
12697
|
function listFolderOverviews() {
|
|
12631
12698
|
load$1();
|
|
12632
12699
|
const counts = /* @__PURE__ */ new Map();
|
|
12633
|
-
for (const bookmark of state$
|
|
12700
|
+
for (const bookmark of state$2.bookmarks) {
|
|
12634
12701
|
counts.set(bookmark.folderId, (counts.get(bookmark.folderId) ?? 0) + 1);
|
|
12635
12702
|
}
|
|
12636
12703
|
return [
|
|
@@ -12639,7 +12706,7 @@ function listFolderOverviews() {
|
|
|
12639
12706
|
name: "Unsorted",
|
|
12640
12707
|
count: counts.get(UNSORTED_ID) ?? 0
|
|
12641
12708
|
},
|
|
12642
|
-
...state$
|
|
12709
|
+
...state$2.folders.map((folder) => ({
|
|
12643
12710
|
id: folder.id,
|
|
12644
12711
|
name: folder.name,
|
|
12645
12712
|
summary: folder.summary,
|
|
@@ -12650,8 +12717,8 @@ function listFolderOverviews() {
|
|
|
12650
12717
|
function searchBookmarks(query) {
|
|
12651
12718
|
load$1();
|
|
12652
12719
|
if (!query.trim()) return [];
|
|
12653
|
-
return state$
|
|
12654
|
-
const folder = state$
|
|
12720
|
+
return state$2.bookmarks.map((bookmark) => {
|
|
12721
|
+
const folder = state$2.folders.find(
|
|
12655
12722
|
(item) => item.id === bookmark.folderId
|
|
12656
12723
|
);
|
|
12657
12724
|
const { matchedFields, score } = getBookmarkSearchMatch({
|
|
@@ -12689,9 +12756,9 @@ function createFolderWithSummary(name, summary) {
|
|
|
12689
12756
|
summary: summary?.trim() || void 0,
|
|
12690
12757
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12691
12758
|
};
|
|
12692
|
-
state$
|
|
12693
|
-
save();
|
|
12694
|
-
emit();
|
|
12759
|
+
state$2.folders.push(folder);
|
|
12760
|
+
save$1();
|
|
12761
|
+
emit$2();
|
|
12695
12762
|
return folder;
|
|
12696
12763
|
}
|
|
12697
12764
|
function ensureFolder(name, summary) {
|
|
@@ -12720,7 +12787,7 @@ function saveBookmarkWithPolicy(url, title, folderId, note, options) {
|
|
|
12720
12787
|
throw new Error("Bookmark URL cannot be empty");
|
|
12721
12788
|
}
|
|
12722
12789
|
const normalizedTitle = title.trim() || normalizedUrl;
|
|
12723
|
-
const targetId = folderId && folderId !== UNSORTED_ID ? state$
|
|
12790
|
+
const targetId = folderId && folderId !== UNSORTED_ID ? state$2.folders.find((f) => f.id === folderId)?.id ?? UNSORTED_ID : UNSORTED_ID;
|
|
12724
12791
|
const duplicatePolicy = options?.onDuplicate ?? "ask";
|
|
12725
12792
|
const existing = getBookmarkByUrlInFolder(normalizedUrl, targetId);
|
|
12726
12793
|
if (existing) {
|
|
@@ -12731,7 +12798,7 @@ function saveBookmarkWithPolicy(url, title, folderId, note, options) {
|
|
|
12731
12798
|
};
|
|
12732
12799
|
}
|
|
12733
12800
|
if (duplicatePolicy === "update") {
|
|
12734
|
-
const bookmark2 = state$
|
|
12801
|
+
const bookmark2 = state$2.bookmarks.find((item) => item.id === existing.id);
|
|
12735
12802
|
if (!bookmark2) {
|
|
12736
12803
|
return {
|
|
12737
12804
|
status: "conflict",
|
|
@@ -12744,8 +12811,8 @@ function saveBookmarkWithPolicy(url, title, folderId, note, options) {
|
|
|
12744
12811
|
}
|
|
12745
12812
|
assignDefinedBookmarkFields(bookmark2, options?.extra);
|
|
12746
12813
|
bookmark2.savedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12747
|
-
save();
|
|
12748
|
-
emit();
|
|
12814
|
+
save$1();
|
|
12815
|
+
emit$2();
|
|
12749
12816
|
return {
|
|
12750
12817
|
status: "updated",
|
|
12751
12818
|
bookmark: { ...bookmark2 }
|
|
@@ -12761,9 +12828,9 @@ function saveBookmarkWithPolicy(url, title, folderId, note, options) {
|
|
|
12761
12828
|
savedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12762
12829
|
...options?.extra
|
|
12763
12830
|
};
|
|
12764
|
-
state$
|
|
12765
|
-
save();
|
|
12766
|
-
emit();
|
|
12831
|
+
state$2.bookmarks.push(bookmark);
|
|
12832
|
+
save$1();
|
|
12833
|
+
emit$2();
|
|
12767
12834
|
return {
|
|
12768
12835
|
status: "created",
|
|
12769
12836
|
bookmark
|
|
@@ -12771,18 +12838,18 @@ function saveBookmarkWithPolicy(url, title, folderId, note, options) {
|
|
|
12771
12838
|
}
|
|
12772
12839
|
function removeBookmark(id) {
|
|
12773
12840
|
load$1();
|
|
12774
|
-
const before = state$
|
|
12775
|
-
state$
|
|
12776
|
-
if (state$
|
|
12777
|
-
save();
|
|
12778
|
-
emit();
|
|
12841
|
+
const before = state$2.bookmarks.length;
|
|
12842
|
+
state$2.bookmarks = state$2.bookmarks.filter((b) => b.id !== id);
|
|
12843
|
+
if (state$2.bookmarks.length !== before) {
|
|
12844
|
+
save$1();
|
|
12845
|
+
emit$2();
|
|
12779
12846
|
return true;
|
|
12780
12847
|
}
|
|
12781
12848
|
return false;
|
|
12782
12849
|
}
|
|
12783
12850
|
function updateBookmark(id, updates) {
|
|
12784
12851
|
load$1();
|
|
12785
|
-
const bookmark = state$
|
|
12852
|
+
const bookmark = state$2.bookmarks.find((item) => item.id === id);
|
|
12786
12853
|
if (!bookmark) return null;
|
|
12787
12854
|
const metadataUpdates = normalizeBookmarkMetadataUpdate({
|
|
12788
12855
|
intent: updates.intent,
|
|
@@ -12799,7 +12866,7 @@ function updateBookmark(id, updates) {
|
|
|
12799
12866
|
bookmark.note = trimmed || void 0;
|
|
12800
12867
|
}
|
|
12801
12868
|
if (typeof updates.folderId === "string") {
|
|
12802
|
-
bookmark.folderId = updates.folderId && updates.folderId !== UNSORTED_ID ? state$
|
|
12869
|
+
bookmark.folderId = updates.folderId && updates.folderId !== UNSORTED_ID ? state$2.folders.find((item) => item.id === updates.folderId)?.id ?? UNSORTED_ID : UNSORTED_ID;
|
|
12803
12870
|
}
|
|
12804
12871
|
if ("intent" in metadataUpdates) {
|
|
12805
12872
|
bookmark.intent = metadataUpdates.intent;
|
|
@@ -12816,36 +12883,36 @@ function updateBookmark(id, updates) {
|
|
|
12816
12883
|
if ("agentHints" in metadataUpdates) {
|
|
12817
12884
|
bookmark.agentHints = metadataUpdates.agentHints;
|
|
12818
12885
|
}
|
|
12819
|
-
save();
|
|
12820
|
-
emit();
|
|
12886
|
+
save$1();
|
|
12887
|
+
emit$2();
|
|
12821
12888
|
return { ...bookmark };
|
|
12822
12889
|
}
|
|
12823
12890
|
function removeFolder(id, deleteContents = false) {
|
|
12824
12891
|
load$1();
|
|
12825
|
-
const exists = state$
|
|
12892
|
+
const exists = state$2.folders.some((f) => f.id === id);
|
|
12826
12893
|
if (!exists) return false;
|
|
12827
12894
|
if (deleteContents) {
|
|
12828
|
-
state$
|
|
12895
|
+
state$2.bookmarks = state$2.bookmarks.filter((b) => b.folderId !== id);
|
|
12829
12896
|
} else {
|
|
12830
|
-
state$
|
|
12897
|
+
state$2.bookmarks = state$2.bookmarks.map(
|
|
12831
12898
|
(b) => b.folderId === id ? { ...b, folderId: UNSORTED_ID } : b
|
|
12832
12899
|
);
|
|
12833
12900
|
}
|
|
12834
|
-
state$
|
|
12835
|
-
save();
|
|
12836
|
-
emit();
|
|
12901
|
+
state$2.folders = state$2.folders.filter((f) => f.id !== id);
|
|
12902
|
+
save$1();
|
|
12903
|
+
emit$2();
|
|
12837
12904
|
return true;
|
|
12838
12905
|
}
|
|
12839
12906
|
function renameFolder(id, newName, summary) {
|
|
12840
12907
|
load$1();
|
|
12841
|
-
const folder = state$
|
|
12908
|
+
const folder = state$2.folders.find((f) => f.id === id);
|
|
12842
12909
|
if (!folder) return null;
|
|
12843
12910
|
const trimmed = newName.trim();
|
|
12844
12911
|
if (!trimmed) return null;
|
|
12845
12912
|
folder.name = trimmed;
|
|
12846
12913
|
folder.summary = summary?.trim() || void 0;
|
|
12847
|
-
save();
|
|
12848
|
-
emit();
|
|
12914
|
+
save$1();
|
|
12915
|
+
emit$2();
|
|
12849
12916
|
return { ...folder };
|
|
12850
12917
|
}
|
|
12851
12918
|
function flushPersist$1() {
|
|
@@ -12856,7 +12923,7 @@ function importBookmarksFromHtml(content) {
|
|
|
12856
12923
|
let skipped = 0;
|
|
12857
12924
|
let errors = 0;
|
|
12858
12925
|
load$1();
|
|
12859
|
-
const existingUrls = new Set(state$
|
|
12926
|
+
const existingUrls = new Set(state$2.bookmarks.map((b) => b.url));
|
|
12860
12927
|
let currentFolderId = UNSORTED_ID;
|
|
12861
12928
|
let currentFolderName = "Imported";
|
|
12862
12929
|
const lines = content.split("\n");
|
|
@@ -12865,7 +12932,7 @@ function importBookmarksFromHtml(content) {
|
|
|
12865
12932
|
const folderMatch = line.match(/<DT><H3[^>]*>([^<]+)<\/H3>/i);
|
|
12866
12933
|
if (folderMatch) {
|
|
12867
12934
|
currentFolderName = folderMatch[1].trim();
|
|
12868
|
-
const existing = state$
|
|
12935
|
+
const existing = state$2.folders.find(
|
|
12869
12936
|
(f) => f.name.toLowerCase() === currentFolderName.toLowerCase()
|
|
12870
12937
|
);
|
|
12871
12938
|
if (existing) {
|
|
@@ -12876,7 +12943,7 @@ function importBookmarksFromHtml(content) {
|
|
|
12876
12943
|
name: currentFolderName,
|
|
12877
12944
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12878
12945
|
};
|
|
12879
|
-
state$
|
|
12946
|
+
state$2.folders.push(folder);
|
|
12880
12947
|
currentFolderId = folder.id;
|
|
12881
12948
|
}
|
|
12882
12949
|
continue;
|
|
@@ -12897,7 +12964,7 @@ function importBookmarksFromHtml(content) {
|
|
|
12897
12964
|
else errors++;
|
|
12898
12965
|
continue;
|
|
12899
12966
|
}
|
|
12900
|
-
state$
|
|
12967
|
+
state$2.bookmarks.push({
|
|
12901
12968
|
id: crypto$1.randomUUID(),
|
|
12902
12969
|
url,
|
|
12903
12970
|
title,
|
|
@@ -12909,8 +12976,8 @@ function importBookmarksFromHtml(content) {
|
|
|
12909
12976
|
}
|
|
12910
12977
|
}
|
|
12911
12978
|
if (imported > 0) {
|
|
12912
|
-
save();
|
|
12913
|
-
emit();
|
|
12979
|
+
save$1();
|
|
12980
|
+
emit$2();
|
|
12914
12981
|
}
|
|
12915
12982
|
return { imported, skipped, errors };
|
|
12916
12983
|
}
|
|
@@ -12923,14 +12990,14 @@ function importBookmarksFromJson(content) {
|
|
|
12923
12990
|
const incomingFolders = Array.isArray(parsed?.folders) ? parsed.folders : [];
|
|
12924
12991
|
const incomingBookmarks = Array.isArray(parsed?.bookmarks) ? parsed.bookmarks : [];
|
|
12925
12992
|
load$1();
|
|
12926
|
-
const existingUrls = new Set(state$
|
|
12993
|
+
const existingUrls = new Set(state$2.bookmarks.map((b) => b.url));
|
|
12927
12994
|
const folderIdMap = /* @__PURE__ */ new Map();
|
|
12928
12995
|
for (const folder of incomingFolders) {
|
|
12929
12996
|
if (!folder?.id || !folder?.name) {
|
|
12930
12997
|
errors++;
|
|
12931
12998
|
continue;
|
|
12932
12999
|
}
|
|
12933
|
-
const existing = state$
|
|
13000
|
+
const existing = state$2.folders.find(
|
|
12934
13001
|
(f) => f.name.toLowerCase() === folder.name.toLowerCase()
|
|
12935
13002
|
);
|
|
12936
13003
|
if (existing) {
|
|
@@ -12942,7 +13009,7 @@ function importBookmarksFromJson(content) {
|
|
|
12942
13009
|
summary: folder.summary?.trim() || void 0,
|
|
12943
13010
|
createdAt: folder.createdAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
12944
13011
|
};
|
|
12945
|
-
state$
|
|
13012
|
+
state$2.folders.push(newFolder);
|
|
12946
13013
|
folderIdMap.set(folder.id, newFolder.id);
|
|
12947
13014
|
}
|
|
12948
13015
|
}
|
|
@@ -12956,7 +13023,7 @@ function importBookmarksFromJson(content) {
|
|
|
12956
13023
|
continue;
|
|
12957
13024
|
}
|
|
12958
13025
|
const mappedFolderId = bookmark.folderId ? folderIdMap.get(bookmark.folderId) ?? UNSORTED_ID : UNSORTED_ID;
|
|
12959
|
-
state$
|
|
13026
|
+
state$2.bookmarks.push({
|
|
12960
13027
|
id: crypto$1.randomUUID(),
|
|
12961
13028
|
url: bookmark.url.trim(),
|
|
12962
13029
|
title: typeof bookmark.title === "string" ? bookmark.title.trim() : bookmark.url,
|
|
@@ -12968,8 +13035,8 @@ function importBookmarksFromJson(content) {
|
|
|
12968
13035
|
imported++;
|
|
12969
13036
|
}
|
|
12970
13037
|
if (imported > 0 || errors > 0) {
|
|
12971
|
-
save();
|
|
12972
|
-
emit();
|
|
13038
|
+
save$1();
|
|
13039
|
+
emit$2();
|
|
12973
13040
|
}
|
|
12974
13041
|
} catch {
|
|
12975
13042
|
errors++;
|
|
@@ -12987,7 +13054,7 @@ async function captureLiveHighlightSnapshot(wc, savedHighlights = []) {
|
|
|
12987
13054
|
savedHighlights.map((highlight) => normalizeText(highlight.text)).filter(Boolean)
|
|
12988
13055
|
);
|
|
12989
13056
|
try {
|
|
12990
|
-
const
|
|
13057
|
+
const snapshot2 = await wc.executeJavaScript(`(() => {
|
|
12991
13058
|
const selection = window.getSelection?.()?.toString().trim() || "";
|
|
12992
13059
|
const pageHighlights = Array.from(
|
|
12993
13060
|
document.querySelectorAll("mark.__vessel-highlight-text[data-vessel-highlight]")
|
|
@@ -13009,36 +13076,36 @@ async function captureLiveHighlightSnapshot(wc, savedHighlights = []) {
|
|
|
13009
13076
|
};
|
|
13010
13077
|
})()`, true);
|
|
13011
13078
|
const seen = /* @__PURE__ */ new Set();
|
|
13012
|
-
const pageHighlights = (
|
|
13079
|
+
const pageHighlights = (snapshot2.pageHighlights ?? []).map((highlight) => ({
|
|
13013
13080
|
text: normalizeText(highlight.text),
|
|
13014
13081
|
color: highlight.color?.trim() || void 0
|
|
13015
13082
|
})).filter((highlight) => highlight.text.length > 0).filter((highlight) => {
|
|
13016
|
-
const
|
|
13017
|
-
if (seen.has(
|
|
13018
|
-
seen.add(
|
|
13083
|
+
const key2 = `${highlight.text}\0${highlight.color ?? ""}`;
|
|
13084
|
+
if (seen.has(key2)) return false;
|
|
13085
|
+
seen.add(key2);
|
|
13019
13086
|
return true;
|
|
13020
13087
|
}).map((highlight) => ({
|
|
13021
13088
|
...highlight,
|
|
13022
13089
|
persisted: savedTexts.has(highlight.text)
|
|
13023
13090
|
}));
|
|
13024
|
-
const activeSelection = normalizeText(
|
|
13091
|
+
const activeSelection = normalizeText(snapshot2.activeSelection) || void 0;
|
|
13025
13092
|
return { activeSelection, pageHighlights };
|
|
13026
13093
|
} catch {
|
|
13027
13094
|
return { pageHighlights: [] };
|
|
13028
13095
|
}
|
|
13029
13096
|
}
|
|
13030
|
-
function formatLiveSelectionSection(
|
|
13097
|
+
function formatLiveSelectionSection(snapshot2) {
|
|
13031
13098
|
const sections = [];
|
|
13032
|
-
if (
|
|
13033
|
-
const preview =
|
|
13099
|
+
if (snapshot2.activeSelection) {
|
|
13100
|
+
const preview = snapshot2.activeSelection.length > 400 ? `${snapshot2.activeSelection.slice(0, 397)}...` : snapshot2.activeSelection;
|
|
13034
13101
|
sections.push(
|
|
13035
13102
|
`## Active User Selection
|
|
13036
13103
|
The user currently has this text selected on screen:
|
|
13037
13104
|
- "${preview}"`
|
|
13038
13105
|
);
|
|
13039
13106
|
}
|
|
13040
|
-
if (
|
|
13041
|
-
const lines =
|
|
13107
|
+
if (snapshot2.pageHighlights.length > 0) {
|
|
13108
|
+
const lines = snapshot2.pageHighlights.map((highlight) => {
|
|
13042
13109
|
const preview = highlight.text.length > 180 ? `${highlight.text.slice(0, 177)}...` : highlight.text;
|
|
13043
13110
|
const details = [
|
|
13044
13111
|
highlight.persisted ? "saved" : "visible only",
|
|
@@ -13364,17 +13431,17 @@ function sessionFileName(name) {
|
|
|
13364
13431
|
function getSessionPath(name) {
|
|
13365
13432
|
return path$1.join(ensureSessionsDir(), sessionFileName(name));
|
|
13366
13433
|
}
|
|
13367
|
-
function writeSessionFile(
|
|
13434
|
+
function writeSessionFile(filePath2, data) {
|
|
13368
13435
|
fs$1.writeFileSync(
|
|
13369
|
-
|
|
13436
|
+
filePath2,
|
|
13370
13437
|
JSON.stringify({ version: SESSION_VERSION, ...data }, null, 2),
|
|
13371
13438
|
{ encoding: "utf-8", mode: 384 }
|
|
13372
13439
|
);
|
|
13373
|
-
fs$1.chmodSync(
|
|
13440
|
+
fs$1.chmodSync(filePath2, 384);
|
|
13374
13441
|
}
|
|
13375
|
-
function readSessionFile(
|
|
13442
|
+
function readSessionFile(filePath2) {
|
|
13376
13443
|
try {
|
|
13377
|
-
const raw = fs$1.readFileSync(
|
|
13444
|
+
const raw = fs$1.readFileSync(filePath2, "utf-8");
|
|
13378
13445
|
const parsed = JSON.parse(raw);
|
|
13379
13446
|
if (!parsed || typeof parsed.name !== "string") {
|
|
13380
13447
|
return null;
|
|
@@ -13539,7 +13606,7 @@ async function saveNamedSession(tabManager, name) {
|
|
|
13539
13606
|
const entries = await captureLocalStorageForOrigin(tabManager, origin);
|
|
13540
13607
|
localStorage.push({ origin, entries });
|
|
13541
13608
|
}
|
|
13542
|
-
const
|
|
13609
|
+
const snapshot2 = tabManager.snapshotSession(`Named session: ${normalizedName}`);
|
|
13543
13610
|
const domains = [...new Set(cookies.map((cookie) => cookie.domain))].sort();
|
|
13544
13611
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13545
13612
|
const data = {
|
|
@@ -13551,7 +13618,7 @@ async function saveNamedSession(tabManager, name) {
|
|
|
13551
13618
|
domains,
|
|
13552
13619
|
cookies,
|
|
13553
13620
|
localStorage,
|
|
13554
|
-
snapshot
|
|
13621
|
+
snapshot: snapshot2
|
|
13555
13622
|
};
|
|
13556
13623
|
writeSessionFile(getSessionPath(normalizedName), data);
|
|
13557
13624
|
return {
|
|
@@ -13595,9 +13662,9 @@ async function loadNamedSession(tabManager, name) {
|
|
|
13595
13662
|
};
|
|
13596
13663
|
}
|
|
13597
13664
|
function deleteNamedSession(name) {
|
|
13598
|
-
const
|
|
13599
|
-
if (!fs$1.existsSync(
|
|
13600
|
-
fs$1.unlinkSync(
|
|
13665
|
+
const filePath2 = getSessionPath(name);
|
|
13666
|
+
if (!fs$1.existsSync(filePath2)) return false;
|
|
13667
|
+
fs$1.unlinkSync(filePath2);
|
|
13601
13668
|
return true;
|
|
13602
13669
|
}
|
|
13603
13670
|
function isInvalidTextTargetQuery(rawQuery) {
|
|
@@ -13906,8 +13973,8 @@ function compactSearchLikeResult(text) {
|
|
|
13906
13973
|
return limitText(cleaned, 16, 1400);
|
|
13907
13974
|
}
|
|
13908
13975
|
const summary = cleaned.slice(0, markerIndex).trim();
|
|
13909
|
-
const
|
|
13910
|
-
return [summary, compactReadPageResult(
|
|
13976
|
+
const snapshot2 = cleaned.slice(markerIndex + marker.length).trim();
|
|
13977
|
+
return [summary, compactReadPageResult(snapshot2)].filter(Boolean).join("\n\n");
|
|
13911
13978
|
}
|
|
13912
13979
|
function compactCurrentTabResult(text) {
|
|
13913
13980
|
try {
|
|
@@ -14956,7 +15023,7 @@ async function inspectElement(wc, selector, limit = 8) {
|
|
|
14956
15023
|
return lines.join("\n");
|
|
14957
15024
|
}
|
|
14958
15025
|
async function getLocaleSnapshot(wc) {
|
|
14959
|
-
const
|
|
15026
|
+
const snapshot2 = await executePageScript(
|
|
14960
15027
|
wc,
|
|
14961
15028
|
`
|
|
14962
15029
|
(function() {
|
|
@@ -14975,13 +15042,13 @@ async function getLocaleSnapshot(wc) {
|
|
|
14975
15042
|
label: "locale snapshot"
|
|
14976
15043
|
}
|
|
14977
15044
|
);
|
|
14978
|
-
if (!
|
|
15045
|
+
if (!snapshot2 || snapshot2 === PAGE_SCRIPT_TIMEOUT || typeof snapshot2 !== "object") {
|
|
14979
15046
|
return null;
|
|
14980
15047
|
}
|
|
14981
15048
|
return {
|
|
14982
|
-
lang: typeof
|
|
14983
|
-
url: typeof
|
|
14984
|
-
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()
|
|
14985
15052
|
};
|
|
14986
15053
|
}
|
|
14987
15054
|
function primaryLanguageTag(value) {
|
|
@@ -14997,31 +15064,31 @@ function localeChanged(before, after) {
|
|
|
14997
15064
|
const localeHint = /[?&](lang|locale|language|hl)=|\/(ja|jp|en|fr|de|es|it|ko|zh)(\/|$)/i;
|
|
14998
15065
|
return before.url !== after.url && localeHint.test(after.url);
|
|
14999
15066
|
}
|
|
15000
|
-
async function restoreLocaleSnapshot(wc,
|
|
15001
|
-
if (!
|
|
15067
|
+
async function restoreLocaleSnapshot(wc, snapshot2) {
|
|
15068
|
+
if (!snapshot2 || wc.isDestroyed()) return;
|
|
15002
15069
|
try {
|
|
15003
15070
|
if (typeof wc.canGoBack === "function" && wc.canGoBack()) {
|
|
15004
15071
|
wc.goBack();
|
|
15005
15072
|
await waitForLoad(wc, 3e3);
|
|
15006
15073
|
const reverted = await getLocaleSnapshot(wc);
|
|
15007
|
-
if (!localeChanged(
|
|
15074
|
+
if (!localeChanged(snapshot2, reverted)) {
|
|
15008
15075
|
return;
|
|
15009
15076
|
}
|
|
15010
15077
|
}
|
|
15011
15078
|
} catch (err) {
|
|
15012
15079
|
logger$c.warn("Failed to restore locale via history navigation, trying URL reload fallback:", err);
|
|
15013
15080
|
}
|
|
15014
|
-
if (
|
|
15081
|
+
if (snapshot2.url && snapshot2.url !== wc.getURL()) {
|
|
15015
15082
|
try {
|
|
15016
|
-
assertSafeURL(
|
|
15017
|
-
await wc.loadURL(
|
|
15083
|
+
assertSafeURL(snapshot2.url);
|
|
15084
|
+
await wc.loadURL(snapshot2.url);
|
|
15018
15085
|
await waitForLoad(wc, 3e3);
|
|
15019
15086
|
return;
|
|
15020
15087
|
} catch (err) {
|
|
15021
15088
|
logger$c.warn("Failed to restore locale via safe URL load, trying page reload fallback:", err);
|
|
15022
15089
|
}
|
|
15023
15090
|
}
|
|
15024
|
-
if (
|
|
15091
|
+
if (snapshot2.url) {
|
|
15025
15092
|
try {
|
|
15026
15093
|
await wc.reload();
|
|
15027
15094
|
await waitForLoad(wc, 3e3);
|
|
@@ -15054,9 +15121,9 @@ function isAddToCartText(text) {
|
|
|
15054
15121
|
}
|
|
15055
15122
|
function recordCartClick(url, text) {
|
|
15056
15123
|
recentCartClicks.set(url, { text, ts: Date.now() });
|
|
15057
|
-
for (const [
|
|
15124
|
+
for (const [key2, entry] of recentCartClicks) {
|
|
15058
15125
|
if (Date.now() - entry.ts > CART_CLICK_COOLDOWN_MS) {
|
|
15059
|
-
recentCartClicks.delete(
|
|
15126
|
+
recentCartClicks.delete(key2);
|
|
15060
15127
|
}
|
|
15061
15128
|
}
|
|
15062
15129
|
}
|
|
@@ -15103,9 +15170,9 @@ function normalizeCartProductKey(url) {
|
|
|
15103
15170
|
}
|
|
15104
15171
|
}
|
|
15105
15172
|
function pruneCartAddedProducts(now = Date.now()) {
|
|
15106
|
-
for (const [
|
|
15173
|
+
for (const [key2, entry] of cartAddedProducts) {
|
|
15107
15174
|
if (now - entry.ts > CART_ADDED_TTL_MS) {
|
|
15108
|
-
cartAddedProducts.delete(
|
|
15175
|
+
cartAddedProducts.delete(key2);
|
|
15109
15176
|
}
|
|
15110
15177
|
}
|
|
15111
15178
|
}
|
|
@@ -15131,7 +15198,7 @@ function isProductAlreadyInCart(url) {
|
|
|
15131
15198
|
function getCartAddedSummary(url) {
|
|
15132
15199
|
pruneCartAddedProducts();
|
|
15133
15200
|
const origin = cartOrigin(url);
|
|
15134
|
-
const items = Array.from(cartAddedProducts.entries()).filter(([
|
|
15201
|
+
const items = Array.from(cartAddedProducts.entries()).filter(([key2]) => !origin || key2.startsWith(`${origin}/`)).map(([_path, info]) => `- ${info.title}`).join("\n");
|
|
15135
15202
|
if (!items) return "";
|
|
15136
15203
|
const count = items.split("\n").length;
|
|
15137
15204
|
return `
|
|
@@ -16787,8 +16854,8 @@ async function submitForm(wc, args) {
|
|
|
16787
16854
|
}
|
|
16788
16855
|
return "Submitted form";
|
|
16789
16856
|
}
|
|
16790
|
-
async function pressKeyDirect(wc,
|
|
16791
|
-
return pressKey(wc, { key, index, selector });
|
|
16857
|
+
async function pressKeyDirect(wc, key2, index, selector) {
|
|
16858
|
+
return pressKey(wc, { key: key2, index, selector });
|
|
16792
16859
|
}
|
|
16793
16860
|
async function submitFormDirect(wc, index, selector) {
|
|
16794
16861
|
return submitForm(wc, { index, selector });
|
|
@@ -17154,8 +17221,8 @@ async function searchPage(wc, args) {
|
|
|
17154
17221
|
return `Searched "${query}" (same page — results may have loaded dynamically)${await getPostSearchSummary(wc)}`;
|
|
17155
17222
|
}
|
|
17156
17223
|
async function pressKey(wc, args) {
|
|
17157
|
-
const
|
|
17158
|
-
if (!
|
|
17224
|
+
const key2 = typeof args.key === "string" ? args.key.trim() : "";
|
|
17225
|
+
if (!key2) return "Error: No key provided";
|
|
17159
17226
|
const selector = await resolveSelector(wc, args.index, args.selector);
|
|
17160
17227
|
const focusResult = await executePageScript(
|
|
17161
17228
|
wc,
|
|
@@ -17193,16 +17260,16 @@ async function pressKey(wc, args) {
|
|
|
17193
17260
|
return focusResult.error;
|
|
17194
17261
|
}
|
|
17195
17262
|
wc.focus();
|
|
17196
|
-
const normalizedKey =
|
|
17263
|
+
const normalizedKey = key2.length === 1 ? key2 : key2[0].toUpperCase() + key2.slice(1);
|
|
17197
17264
|
const electronKeyCode = normalizedKey === "Enter" ? "Return" : normalizedKey === "ArrowUp" ? "Up" : normalizedKey === "ArrowDown" ? "Down" : normalizedKey === "ArrowLeft" ? "Left" : normalizedKey === "ArrowRight" ? "Right" : normalizedKey;
|
|
17198
17265
|
wc.sendInputEvent({ type: "keyDown", keyCode: electronKeyCode });
|
|
17199
|
-
if (
|
|
17200
|
-
wc.sendInputEvent({ type: "char", keyCode:
|
|
17266
|
+
if (key2.length === 1) {
|
|
17267
|
+
wc.sendInputEvent({ type: "char", keyCode: key2 });
|
|
17201
17268
|
}
|
|
17202
17269
|
await sleep(16);
|
|
17203
17270
|
wc.sendInputEvent({ type: "keyUp", keyCode: electronKeyCode });
|
|
17204
17271
|
const label = "label" in focusResult && typeof focusResult.label === "string" ? focusResult.label : null;
|
|
17205
|
-
return label ? `Pressed key: ${
|
|
17272
|
+
return label ? `Pressed key: ${key2} on ${label}` : `Pressed key: ${key2}`;
|
|
17206
17273
|
}
|
|
17207
17274
|
async function getPostActionState$1(ctx, name) {
|
|
17208
17275
|
const tab = ctx.tabManager.getActiveTab();
|
|
@@ -17612,8 +17679,8 @@ async function executeAction(name, args, ctx) {
|
|
|
17612
17679
|
if (!wc) return "Error: No active tab";
|
|
17613
17680
|
const beforeUrl = wc.getURL();
|
|
17614
17681
|
const result2 = await pressKey(wc, args);
|
|
17615
|
-
const
|
|
17616
|
-
if (
|
|
17682
|
+
const key2 = typeof args.key === "string" ? args.key.trim() : "";
|
|
17683
|
+
if (key2 === "Enter") {
|
|
17617
17684
|
await waitForPotentialNavigation(wc, beforeUrl, 3e3);
|
|
17618
17685
|
const afterUrl = wc.getURL();
|
|
17619
17686
|
if (afterUrl !== beforeUrl) {
|
|
@@ -18718,17 +18785,17 @@ function escapeYaml(value) {
|
|
|
18718
18785
|
}
|
|
18719
18786
|
function renderFrontmatter(data) {
|
|
18720
18787
|
const lines = ["---"];
|
|
18721
|
-
for (const [
|
|
18788
|
+
for (const [key2, value] of Object.entries(data)) {
|
|
18722
18789
|
if (value == null) continue;
|
|
18723
18790
|
if (Array.isArray(value)) {
|
|
18724
18791
|
if (value.length === 0) continue;
|
|
18725
|
-
lines.push(`${
|
|
18792
|
+
lines.push(`${key2}:`);
|
|
18726
18793
|
for (const item of value) {
|
|
18727
18794
|
lines.push(` - ${escapeYaml(item)}`);
|
|
18728
18795
|
}
|
|
18729
18796
|
continue;
|
|
18730
18797
|
}
|
|
18731
|
-
lines.push(`${
|
|
18798
|
+
lines.push(`${key2}: ${escapeYaml(value)}`);
|
|
18732
18799
|
}
|
|
18733
18800
|
lines.push("---", "");
|
|
18734
18801
|
return lines.join("\n");
|
|
@@ -18781,11 +18848,11 @@ function parseFrontmatter(content) {
|
|
|
18781
18848
|
activeArrayKey = "";
|
|
18782
18849
|
const separatorIndex = trimmed.indexOf(":");
|
|
18783
18850
|
if (separatorIndex === -1) continue;
|
|
18784
|
-
const
|
|
18851
|
+
const key2 = trimmed.slice(0, separatorIndex).trim();
|
|
18785
18852
|
const value = trimmed.slice(separatorIndex + 1).trim();
|
|
18786
|
-
if (
|
|
18853
|
+
if (key2 === "title" && value) {
|
|
18787
18854
|
result.title = value.replace(/^["']|["']$/g, "");
|
|
18788
|
-
} else if (
|
|
18855
|
+
} else if (key2 === "tags") {
|
|
18789
18856
|
activeArrayKey = "tags";
|
|
18790
18857
|
if (value.startsWith("[") && value.endsWith("]")) {
|
|
18791
18858
|
const inline = value.slice(1, -1).split(",").map((item) => item.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
|
|
@@ -18971,9 +19038,9 @@ function capturePageToVault({
|
|
|
18971
19038
|
if (page.excerpt.trim()) {
|
|
18972
19039
|
bodyLines.push("## Excerpt", "", page.excerpt.trim(), "");
|
|
18973
19040
|
}
|
|
18974
|
-
const
|
|
18975
|
-
if (
|
|
18976
|
-
bodyLines.push("## Page Snapshot", "",
|
|
19041
|
+
const snapshot2 = trimContent(page.content);
|
|
19042
|
+
if (snapshot2) {
|
|
19043
|
+
bodyLines.push("## Page Snapshot", "", snapshot2, "");
|
|
18977
19044
|
}
|
|
18978
19045
|
return writeMemoryNote({
|
|
18979
19046
|
title: noteTitle,
|
|
@@ -19371,15 +19438,15 @@ Exception: ${result.exceptionDetails}`);
|
|
|
19371
19438
|
value: zod.z.string().nullable().describe("Value to set, or null to remove the key")
|
|
19372
19439
|
}
|
|
19373
19440
|
},
|
|
19374
|
-
async ({ type, key, value }) => {
|
|
19441
|
+
async ({ type, key: key2, value }) => {
|
|
19375
19442
|
return withDevToolsAction(
|
|
19376
19443
|
runtime2,
|
|
19377
19444
|
tabManager,
|
|
19378
19445
|
"devtools_set_storage",
|
|
19379
|
-
{ type, key, value: value ? value.slice(0, 100) : null },
|
|
19446
|
+
{ type, key: key2, value: value ? value.slice(0, 100) : null },
|
|
19380
19447
|
async () => {
|
|
19381
19448
|
const session = getOrCreateSession(tabManager);
|
|
19382
|
-
return session.setStorage(type,
|
|
19449
|
+
return session.setStorage(type, key2, value);
|
|
19383
19450
|
}
|
|
19384
19451
|
);
|
|
19385
19452
|
}
|
|
@@ -19398,8 +19465,8 @@ Exception: ${result.exceptionDetails}`);
|
|
|
19398
19465
|
{},
|
|
19399
19466
|
async () => {
|
|
19400
19467
|
const session = getOrCreateSession(tabManager);
|
|
19401
|
-
const
|
|
19402
|
-
return JSON.stringify(
|
|
19468
|
+
const snapshot2 = await session.getPerformanceSnapshot();
|
|
19469
|
+
return JSON.stringify(snapshot2, null, 2);
|
|
19403
19470
|
}
|
|
19404
19471
|
);
|
|
19405
19472
|
}
|
|
@@ -19471,11 +19538,12 @@ function getOrCreateEncryptionKey(keyFilename) {
|
|
|
19471
19538
|
const encryptedKey = fs$1.readFileSync(keyPath);
|
|
19472
19539
|
return Buffer.from(electron.safeStorage.decryptString(encryptedKey), "utf-8");
|
|
19473
19540
|
}
|
|
19474
|
-
const
|
|
19541
|
+
const key2 = crypto$2.randomBytes(32);
|
|
19475
19542
|
fs$1.mkdirSync(path$1.dirname(keyPath), { recursive: true });
|
|
19476
|
-
const encrypted = electron.safeStorage.encryptString(
|
|
19543
|
+
const encrypted = electron.safeStorage.encryptString(key2.toString("utf-8"));
|
|
19477
19544
|
fs$1.writeFileSync(keyPath, encrypted, { mode: 384 });
|
|
19478
|
-
|
|
19545
|
+
fs$1.chmodSync(keyPath, 384);
|
|
19546
|
+
return key2;
|
|
19479
19547
|
}
|
|
19480
19548
|
function createEncryptDecrypt(keyFilename) {
|
|
19481
19549
|
let cachedKey = null;
|
|
@@ -19484,9 +19552,9 @@ function createEncryptDecrypt(keyFilename) {
|
|
|
19484
19552
|
return cachedKey;
|
|
19485
19553
|
}
|
|
19486
19554
|
function encrypt2(plaintext) {
|
|
19487
|
-
const
|
|
19555
|
+
const key2 = getKey();
|
|
19488
19556
|
const iv = crypto$2.randomBytes(IV_LENGTH);
|
|
19489
|
-
const cipher = crypto$2.createCipheriv(ALGORITHM,
|
|
19557
|
+
const cipher = crypto$2.createCipheriv(ALGORITHM, key2, iv, {
|
|
19490
19558
|
authTagLength: AUTH_TAG_LENGTH
|
|
19491
19559
|
});
|
|
19492
19560
|
const encrypted = Buffer.concat([
|
|
@@ -19497,11 +19565,11 @@ function createEncryptDecrypt(keyFilename) {
|
|
|
19497
19565
|
return Buffer.concat([iv, authTag, encrypted]);
|
|
19498
19566
|
}
|
|
19499
19567
|
function decrypt2(data) {
|
|
19500
|
-
const
|
|
19568
|
+
const key2 = getKey();
|
|
19501
19569
|
const iv = data.subarray(0, IV_LENGTH);
|
|
19502
19570
|
const authTag = data.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);
|
|
19503
19571
|
const ciphertext = data.subarray(IV_LENGTH + AUTH_TAG_LENGTH);
|
|
19504
|
-
const decipher = crypto$2.createDecipheriv(ALGORITHM,
|
|
19572
|
+
const decipher = crypto$2.createDecipheriv(ALGORITHM, key2, iv, {
|
|
19505
19573
|
authTagLength: AUTH_TAG_LENGTH
|
|
19506
19574
|
});
|
|
19507
19575
|
decipher.setAuthTag(authTag);
|
|
@@ -19548,15 +19616,21 @@ function createVaultIO(vaultFilename, encrypt2, decrypt2) {
|
|
|
19548
19616
|
}
|
|
19549
19617
|
return { loadVault: loadVault2, saveVault: saveVault2, resetCache };
|
|
19550
19618
|
}
|
|
19551
|
-
function
|
|
19552
|
-
|
|
19553
|
-
|
|
19554
|
-
|
|
19555
|
-
|
|
19556
|
-
const
|
|
19557
|
-
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;
|
|
19558
19626
|
}
|
|
19559
|
-
|
|
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;
|
|
19560
19634
|
}
|
|
19561
19635
|
function generateTotpCode(secret) {
|
|
19562
19636
|
const epoch = Math.floor(Date.now() / 1e3);
|
|
@@ -19627,12 +19701,8 @@ function listEntries$1() {
|
|
|
19627
19701
|
return loadVault$1().map(({ password, totpSecret, ...rest }) => rest);
|
|
19628
19702
|
}
|
|
19629
19703
|
function findEntriesForDomain(url) {
|
|
19630
|
-
|
|
19631
|
-
|
|
19632
|
-
hostname = new URL(url).hostname;
|
|
19633
|
-
} catch {
|
|
19634
|
-
return [];
|
|
19635
|
-
}
|
|
19704
|
+
const hostname = normalizeCredentialHost(url);
|
|
19705
|
+
if (!hostname) return [];
|
|
19636
19706
|
return loadVault$1().filter((e) => domainMatches(e.domainPattern, hostname)).map(({ password, totpSecret, ...rest }) => rest);
|
|
19637
19707
|
}
|
|
19638
19708
|
function addEntry(entry) {
|
|
@@ -19755,12 +19825,7 @@ const auditLog = createAuditLog(
|
|
|
19755
19825
|
AUDIT_MAX_ENTRIES
|
|
19756
19826
|
);
|
|
19757
19827
|
function extractDomain(url) {
|
|
19758
|
-
|
|
19759
|
-
const parsed = new URL(url.startsWith("http") ? url : `https://${url}`);
|
|
19760
|
-
return parsed.hostname.toLowerCase();
|
|
19761
|
-
} catch {
|
|
19762
|
-
return url.toLowerCase().replace(/^(https?:\/\/)?(www\.)?/, "").replace(/\/.*$/, "");
|
|
19763
|
-
}
|
|
19828
|
+
return normalizeCredentialHost(url) ?? "";
|
|
19764
19829
|
}
|
|
19765
19830
|
function listEntries() {
|
|
19766
19831
|
return loadVault().map(({ password, totpSecret, ...rest }) => rest);
|
|
@@ -19938,13 +20003,14 @@ function getPersistentMcpAuthToken() {
|
|
|
19938
20003
|
}
|
|
19939
20004
|
function writeMcpAuthFile(endpoint, token) {
|
|
19940
20005
|
try {
|
|
19941
|
-
const
|
|
19942
|
-
fs$1.mkdirSync(path$1.dirname(
|
|
20006
|
+
const filePath2 = getMcpAuthFilePath();
|
|
20007
|
+
fs$1.mkdirSync(path$1.dirname(filePath2), { recursive: true });
|
|
19943
20008
|
fs$1.writeFileSync(
|
|
19944
|
-
|
|
20009
|
+
filePath2,
|
|
19945
20010
|
JSON.stringify({ endpoint, token, pid: process.pid }, null, 2) + "\n",
|
|
19946
20011
|
{ mode: 384 }
|
|
19947
20012
|
);
|
|
20013
|
+
fs$1.chmodSync(filePath2, 384);
|
|
19948
20014
|
} catch (err) {
|
|
19949
20015
|
logger$9.warn("Failed to write auth file:", err);
|
|
19950
20016
|
}
|
|
@@ -19959,10 +20025,10 @@ function clearMcpAuthFile() {
|
|
|
19959
20025
|
return;
|
|
19960
20026
|
}
|
|
19961
20027
|
try {
|
|
19962
|
-
const
|
|
19963
|
-
fs$1.mkdirSync(path$1.dirname(
|
|
20028
|
+
const filePath2 = getMcpAuthFilePath();
|
|
20029
|
+
fs$1.mkdirSync(path$1.dirname(filePath2), { recursive: true });
|
|
19964
20030
|
fs$1.writeFileSync(
|
|
19965
|
-
|
|
20031
|
+
filePath2,
|
|
19966
20032
|
JSON.stringify(
|
|
19967
20033
|
{ endpoint: "", token: existingToken, pid: null },
|
|
19968
20034
|
null,
|
|
@@ -19970,10 +20036,18 @@ function clearMcpAuthFile() {
|
|
|
19970
20036
|
) + "\n",
|
|
19971
20037
|
{ mode: 384 }
|
|
19972
20038
|
);
|
|
20039
|
+
fs$1.chmodSync(filePath2, 384);
|
|
19973
20040
|
} catch (err) {
|
|
19974
20041
|
logger$9.warn("Failed to clear auth file:", err);
|
|
19975
20042
|
}
|
|
19976
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
|
+
}
|
|
19977
20051
|
function asTextResponse(text) {
|
|
19978
20052
|
return { content: [{ type: "text", text }] };
|
|
19979
20053
|
}
|
|
@@ -19999,6 +20073,11 @@ function asPromptResponse(text) {
|
|
|
19999
20073
|
function isDangerousMcpAction(name) {
|
|
20000
20074
|
return name === "close_tab" || isDangerousAction(name);
|
|
20001
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
|
+
}
|
|
20002
20081
|
function getActiveTabSummary(tabManager) {
|
|
20003
20082
|
const activeTab = tabManager.getActiveTab();
|
|
20004
20083
|
const activeTabId = tabManager.getActiveTabId();
|
|
@@ -20087,6 +20166,7 @@ async function withAction(runtime2, tabManager, name, args, executor) {
|
|
|
20087
20166
|
args,
|
|
20088
20167
|
tabId: tabManager.getActiveTabId(),
|
|
20089
20168
|
dangerous: isDangerousMcpAction(name),
|
|
20169
|
+
requiresApproval: requiresExplicitMcpApproval(name, args),
|
|
20090
20170
|
executor
|
|
20091
20171
|
});
|
|
20092
20172
|
const stateInfo = await getPostActionState(tabManager, name);
|
|
@@ -20978,19 +21058,19 @@ ${buildScopedContext(pageContent, mode)}`;
|
|
|
20978
21058
|
selector: zod.z.string().optional().describe("CSS selector to focus first")
|
|
20979
21059
|
}
|
|
20980
21060
|
},
|
|
20981
|
-
async ({ key, index, selector }) => {
|
|
21061
|
+
async ({ key: key2, index, selector }) => {
|
|
20982
21062
|
const tab = tabManager.getActiveTab();
|
|
20983
21063
|
if (!tab) return asNoActiveTabResponse();
|
|
20984
21064
|
return withAction(
|
|
20985
21065
|
runtime2,
|
|
20986
21066
|
tabManager,
|
|
20987
21067
|
"press_key",
|
|
20988
|
-
{ key, index, selector },
|
|
21068
|
+
{ key: key2, index, selector },
|
|
20989
21069
|
async () => {
|
|
20990
21070
|
const wc = tab.view.webContents;
|
|
20991
21071
|
const beforeUrl = wc.getURL();
|
|
20992
|
-
const result = await pressKeyDirect(wc,
|
|
20993
|
-
if (
|
|
21072
|
+
const result = await pressKeyDirect(wc, key2, index, selector);
|
|
21073
|
+
if (key2 === "Enter") {
|
|
20994
21074
|
await waitForPotentialNavigation$1(wc, beforeUrl, 3e3);
|
|
20995
21075
|
const afterUrl = wc.getURL();
|
|
20996
21076
|
if (afterUrl !== beforeUrl) {
|
|
@@ -21456,7 +21536,7 @@ To analyze visually, call vision_analyze with image_url="${screenshotPath}"`
|
|
|
21456
21536
|
)
|
|
21457
21537
|
}
|
|
21458
21538
|
},
|
|
21459
|
-
async ({ index, selector, text, label, durationMs, persist, color }) => {
|
|
21539
|
+
async ({ index, selector, text, label, durationMs, persist: persist2, color }) => {
|
|
21460
21540
|
const tab = tabManager.getActiveTab();
|
|
21461
21541
|
if (!tab) return asNoActiveTabResponse();
|
|
21462
21542
|
const normalizedText = normalizeLooseString(text);
|
|
@@ -21470,7 +21550,7 @@ To analyze visually, call vision_analyze with image_url="${screenshotPath}"`
|
|
|
21470
21550
|
text: normalizedText,
|
|
21471
21551
|
label,
|
|
21472
21552
|
durationMs,
|
|
21473
|
-
persist,
|
|
21553
|
+
persist: persist2,
|
|
21474
21554
|
color
|
|
21475
21555
|
},
|
|
21476
21556
|
async () => {
|
|
@@ -21484,7 +21564,7 @@ To analyze visually, call vision_analyze with image_url="${screenshotPath}"`
|
|
|
21484
21564
|
durationMs,
|
|
21485
21565
|
color
|
|
21486
21566
|
);
|
|
21487
|
-
if (
|
|
21567
|
+
if (persist2 && !durationMs && !result.startsWith("Error") && !result.includes("not found")) {
|
|
21488
21568
|
const url = normalizeUrl$1(wc.getURL());
|
|
21489
21569
|
addHighlight(
|
|
21490
21570
|
url,
|
|
@@ -24110,6 +24190,16 @@ function registerScheduleHandlers(windowState, runtime2, sendToAll) {
|
|
|
24110
24190
|
return true;
|
|
24111
24191
|
});
|
|
24112
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
|
+
}
|
|
24113
24203
|
function assertString(value, name) {
|
|
24114
24204
|
if (typeof value !== "string") throw new Error(`${name} must be a string`);
|
|
24115
24205
|
}
|
|
@@ -24149,7 +24239,7 @@ const PROFILE_FIELDS = [
|
|
|
24149
24239
|
"postalCode",
|
|
24150
24240
|
"country"
|
|
24151
24241
|
];
|
|
24152
|
-
let state = null;
|
|
24242
|
+
let state$1 = null;
|
|
24153
24243
|
function getFilePath() {
|
|
24154
24244
|
return path.join(electron.app.getPath("userData"), "vessel-autofill.json");
|
|
24155
24245
|
}
|
|
@@ -24174,8 +24264,8 @@ function normalizeStoredProfile(value) {
|
|
|
24174
24264
|
return profile;
|
|
24175
24265
|
}
|
|
24176
24266
|
function load() {
|
|
24177
|
-
if (state) return state;
|
|
24178
|
-
state = loadJsonFile({
|
|
24267
|
+
if (state$1) return state$1;
|
|
24268
|
+
state$1 = loadJsonFile({
|
|
24179
24269
|
filePath: getFilePath(),
|
|
24180
24270
|
fallback: getDefaultState(),
|
|
24181
24271
|
secure: true,
|
|
@@ -24186,12 +24276,12 @@ function load() {
|
|
|
24186
24276
|
};
|
|
24187
24277
|
}
|
|
24188
24278
|
});
|
|
24189
|
-
return state;
|
|
24279
|
+
return state$1;
|
|
24190
24280
|
}
|
|
24191
|
-
const persistence = createDebouncedJsonPersistence({
|
|
24281
|
+
const persistence$2 = createDebouncedJsonPersistence({
|
|
24192
24282
|
debounceMs: SAVE_DEBOUNCE_MS,
|
|
24193
24283
|
filePath: getFilePath(),
|
|
24194
|
-
getValue: () => state,
|
|
24284
|
+
getValue: () => state$1,
|
|
24195
24285
|
logLabel: "autofill",
|
|
24196
24286
|
secure: true
|
|
24197
24287
|
});
|
|
@@ -24211,7 +24301,7 @@ function addProfile(input) {
|
|
|
24211
24301
|
updatedAt: now
|
|
24212
24302
|
};
|
|
24213
24303
|
s.profiles.push(profile);
|
|
24214
|
-
persistence.schedule();
|
|
24304
|
+
persistence$2.schedule();
|
|
24215
24305
|
return profile;
|
|
24216
24306
|
}
|
|
24217
24307
|
function updateProfile(id, updates) {
|
|
@@ -24223,7 +24313,7 @@ function updateProfile(id, updates) {
|
|
|
24223
24313
|
...updates,
|
|
24224
24314
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
24225
24315
|
};
|
|
24226
|
-
persistence.schedule();
|
|
24316
|
+
persistence$2.schedule();
|
|
24227
24317
|
return s.profiles[idx];
|
|
24228
24318
|
}
|
|
24229
24319
|
function deleteProfile(id) {
|
|
@@ -24231,11 +24321,11 @@ function deleteProfile(id) {
|
|
|
24231
24321
|
const len = s.profiles.length;
|
|
24232
24322
|
s.profiles = s.profiles.filter((p) => p.id !== id);
|
|
24233
24323
|
if (s.profiles.length === len) return false;
|
|
24234
|
-
persistence.schedule();
|
|
24324
|
+
persistence$2.schedule();
|
|
24235
24325
|
return true;
|
|
24236
24326
|
}
|
|
24237
24327
|
function flushPersist() {
|
|
24238
|
-
return persistence.flush();
|
|
24328
|
+
return persistence$2.flush();
|
|
24239
24329
|
}
|
|
24240
24330
|
const AUTOCOMPLETE_MAP = {
|
|
24241
24331
|
"given-name": "firstName",
|
|
@@ -24364,12 +24454,12 @@ function matchField(el, profile) {
|
|
|
24364
24454
|
if (inputType === "hidden" || inputType === "submit" || inputType === "button" || inputType === "file" || inputType === "image") return null;
|
|
24365
24455
|
if (inputType === "password" || inputType === "checkbox" || inputType === "radio") return null;
|
|
24366
24456
|
if (el.autocomplete) {
|
|
24367
|
-
const
|
|
24368
|
-
if (
|
|
24457
|
+
const key2 = el.autocomplete.replace(/section-\w+\s+/, "").replace(/^shipping\s+|^billing\s+/, "");
|
|
24458
|
+
if (key2 === "name" || key2 === "additional-name") {
|
|
24369
24459
|
const fullName = getFullName(profile);
|
|
24370
24460
|
if (fullName) return mk(fullName, 100, "autocomplete", "fullName");
|
|
24371
24461
|
}
|
|
24372
|
-
const pk = AUTOCOMPLETE_MAP[
|
|
24462
|
+
const pk = AUTOCOMPLETE_MAP[key2];
|
|
24373
24463
|
if (pk && profile[pk]) return mk(profile[pk], 100, "autocomplete", pk);
|
|
24374
24464
|
}
|
|
24375
24465
|
if (INPUT_TYPE_MAP[inputType]) {
|
|
@@ -24525,13 +24615,26 @@ function registerAutofillHandlers(windowState) {
|
|
|
24525
24615
|
});
|
|
24526
24616
|
}
|
|
24527
24617
|
function registerPageDiffHandlers(windowState, sendToRendererViews) {
|
|
24528
|
-
|
|
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);
|
|
24529
24631
|
const activeTab = windowState.tabManager.getActiveTab();
|
|
24530
24632
|
const wc = activeTab?.view.webContents;
|
|
24531
24633
|
if (!wc) return null;
|
|
24532
24634
|
return getLatestPageDiff(wc.getURL());
|
|
24533
24635
|
});
|
|
24534
|
-
electron.ipcMain.handle(Channels.PAGE_DIFF_HISTORY, () => {
|
|
24636
|
+
electron.ipcMain.handle(Channels.PAGE_DIFF_HISTORY, (event) => {
|
|
24637
|
+
assertTrustedIpcSender(event);
|
|
24535
24638
|
try {
|
|
24536
24639
|
if (!isPremiumActiveState(getPremiumState())) {
|
|
24537
24640
|
return { error: "Premium required" };
|
|
@@ -24547,21 +24650,25 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
|
|
|
24547
24650
|
electron.ipcMain.on(Channels.PAGE_DIFF_ACTIVITY, (event) => {
|
|
24548
24651
|
const wc = event.sender;
|
|
24549
24652
|
if (!wc || wc.isDestroyed()) return;
|
|
24653
|
+
if (!allowPageEvent(wc.id)) return;
|
|
24550
24654
|
notePageMutationActivity(wc, sendToRendererViews);
|
|
24551
24655
|
});
|
|
24552
24656
|
electron.ipcMain.on(Channels.PAGE_DIFF_DIRTY, (event) => {
|
|
24553
24657
|
const wc = event.sender;
|
|
24554
24658
|
if (!wc || wc.isDestroyed()) return;
|
|
24659
|
+
if (!allowPageEvent(wc.id)) return;
|
|
24555
24660
|
schedulePageSnapshotCapture(wc, sendToRendererViews);
|
|
24556
24661
|
});
|
|
24557
24662
|
}
|
|
24558
24663
|
function registerVaultHandlers() {
|
|
24559
|
-
electron.ipcMain.handle(Channels.VAULT_LIST, () => {
|
|
24664
|
+
electron.ipcMain.handle(Channels.VAULT_LIST, (event) => {
|
|
24665
|
+
assertTrustedIpcSender(event);
|
|
24560
24666
|
return listEntries$1();
|
|
24561
24667
|
});
|
|
24562
24668
|
electron.ipcMain.handle(
|
|
24563
24669
|
Channels.VAULT_ADD,
|
|
24564
|
-
(
|
|
24670
|
+
(event, entry) => {
|
|
24671
|
+
assertTrustedIpcSender(event);
|
|
24565
24672
|
if (!entry || typeof entry !== "object") {
|
|
24566
24673
|
throw new Error("Invalid vault entry");
|
|
24567
24674
|
}
|
|
@@ -24586,7 +24693,8 @@ function registerVaultHandlers() {
|
|
|
24586
24693
|
);
|
|
24587
24694
|
electron.ipcMain.handle(
|
|
24588
24695
|
Channels.VAULT_UPDATE,
|
|
24589
|
-
(
|
|
24696
|
+
(event, id, updates) => {
|
|
24697
|
+
assertTrustedIpcSender(event);
|
|
24590
24698
|
assertString(id, "id");
|
|
24591
24699
|
if (!updates || typeof updates !== "object") {
|
|
24592
24700
|
throw new Error("Invalid updates");
|
|
@@ -24594,12 +24702,14 @@ function registerVaultHandlers() {
|
|
|
24594
24702
|
return updateEntry$1(id, updates) !== null;
|
|
24595
24703
|
}
|
|
24596
24704
|
);
|
|
24597
|
-
electron.ipcMain.handle(Channels.VAULT_REMOVE, (
|
|
24705
|
+
electron.ipcMain.handle(Channels.VAULT_REMOVE, (event, id) => {
|
|
24706
|
+
assertTrustedIpcSender(event);
|
|
24598
24707
|
assertString(id, "id");
|
|
24599
24708
|
trackVaultAction("credential_removed");
|
|
24600
24709
|
return removeEntry$1(id);
|
|
24601
24710
|
});
|
|
24602
|
-
electron.ipcMain.handle(Channels.VAULT_AUDIT_LOG, (
|
|
24711
|
+
electron.ipcMain.handle(Channels.VAULT_AUDIT_LOG, (event, limit) => {
|
|
24712
|
+
assertTrustedIpcSender(event);
|
|
24603
24713
|
return readAuditLog$1(limit);
|
|
24604
24714
|
});
|
|
24605
24715
|
}
|
|
@@ -24618,17 +24728,20 @@ function normalizeTags(value) {
|
|
|
24618
24728
|
return tags.length > 0 ? tags : void 0;
|
|
24619
24729
|
}
|
|
24620
24730
|
function registerHumanVaultHandlers() {
|
|
24621
|
-
electron.ipcMain.handle(Channels.HUMAN_VAULT_LIST, (
|
|
24731
|
+
electron.ipcMain.handle(Channels.HUMAN_VAULT_LIST, (event, domain) => {
|
|
24732
|
+
assertTrustedIpcSender(event);
|
|
24622
24733
|
if (domain !== void 0) assertString(domain, "domain");
|
|
24623
24734
|
return domain ? findForDomain(domain) : listEntries();
|
|
24624
24735
|
});
|
|
24625
|
-
electron.ipcMain.handle(Channels.HUMAN_VAULT_GET, (
|
|
24736
|
+
electron.ipcMain.handle(Channels.HUMAN_VAULT_GET, (event, id) => {
|
|
24737
|
+
assertTrustedIpcSender(event);
|
|
24626
24738
|
assertString(id, "id");
|
|
24627
24739
|
return getEntrySafe(id);
|
|
24628
24740
|
});
|
|
24629
24741
|
electron.ipcMain.handle(
|
|
24630
24742
|
Channels.HUMAN_VAULT_SAVE,
|
|
24631
|
-
(
|
|
24743
|
+
(event, input) => {
|
|
24744
|
+
assertTrustedIpcSender(event);
|
|
24632
24745
|
if (!input || typeof input !== "object") {
|
|
24633
24746
|
throw new Error("Invalid credential entry");
|
|
24634
24747
|
}
|
|
@@ -24658,7 +24771,8 @@ function registerHumanVaultHandlers() {
|
|
|
24658
24771
|
);
|
|
24659
24772
|
electron.ipcMain.handle(
|
|
24660
24773
|
Channels.HUMAN_VAULT_UPDATE,
|
|
24661
|
-
(
|
|
24774
|
+
(event, id, updates) => {
|
|
24775
|
+
assertTrustedIpcSender(event);
|
|
24662
24776
|
assertString(id, "id");
|
|
24663
24777
|
if (!updates || typeof updates !== "object") {
|
|
24664
24778
|
throw new Error("Invalid updates");
|
|
@@ -24700,26 +24814,31 @@ function registerHumanVaultHandlers() {
|
|
|
24700
24814
|
return safe;
|
|
24701
24815
|
}
|
|
24702
24816
|
);
|
|
24703
|
-
electron.ipcMain.handle(Channels.HUMAN_VAULT_REMOVE, (
|
|
24817
|
+
electron.ipcMain.handle(Channels.HUMAN_VAULT_REMOVE, (event, id) => {
|
|
24818
|
+
assertTrustedIpcSender(event);
|
|
24704
24819
|
assertString(id, "id");
|
|
24705
24820
|
return removeEntry(id);
|
|
24706
24821
|
});
|
|
24707
|
-
electron.ipcMain.handle(Channels.HUMAN_VAULT_AUDIT_LOG, (
|
|
24822
|
+
electron.ipcMain.handle(Channels.HUMAN_VAULT_AUDIT_LOG, (event, limit) => {
|
|
24823
|
+
assertTrustedIpcSender(event);
|
|
24708
24824
|
return readAuditLog(limit);
|
|
24709
24825
|
});
|
|
24710
24826
|
}
|
|
24711
24827
|
function registerWindowControlHandlers(mainWindow) {
|
|
24712
|
-
electron.ipcMain.handle(Channels.WINDOW_MINIMIZE, () => {
|
|
24828
|
+
electron.ipcMain.handle(Channels.WINDOW_MINIMIZE, (event) => {
|
|
24829
|
+
assertTrustedIpcSender(event);
|
|
24713
24830
|
mainWindow.minimize();
|
|
24714
24831
|
});
|
|
24715
|
-
electron.ipcMain.handle(Channels.WINDOW_MAXIMIZE, () => {
|
|
24832
|
+
electron.ipcMain.handle(Channels.WINDOW_MAXIMIZE, (event) => {
|
|
24833
|
+
assertTrustedIpcSender(event);
|
|
24716
24834
|
if (mainWindow.isMaximized()) {
|
|
24717
24835
|
mainWindow.unmaximize();
|
|
24718
24836
|
} else {
|
|
24719
24837
|
mainWindow.maximize();
|
|
24720
24838
|
}
|
|
24721
24839
|
});
|
|
24722
|
-
electron.ipcMain.handle(Channels.WINDOW_CLOSE, () => {
|
|
24840
|
+
electron.ipcMain.handle(Channels.WINDOW_CLOSE, (event) => {
|
|
24841
|
+
assertTrustedIpcSender(event);
|
|
24723
24842
|
mainWindow.close();
|
|
24724
24843
|
});
|
|
24725
24844
|
}
|
|
@@ -24857,6 +24976,102 @@ function installAdBlockingForSession(ses, tabManager) {
|
|
|
24857
24976
|
callback(getAdBlockDecision(details));
|
|
24858
24977
|
});
|
|
24859
24978
|
}
|
|
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
|
+
}
|
|
25006
|
+
function parse(raw) {
|
|
25007
|
+
if (!raw || typeof raw !== "object") return { items: [] };
|
|
25008
|
+
const items = Array.isArray(raw.items) ? raw.items : [];
|
|
25009
|
+
return { items };
|
|
25010
|
+
}
|
|
25011
|
+
let state = loadJsonFile({ filePath: filePath$1(), fallback: { items: [] }, parse });
|
|
25012
|
+
const persistence$1 = createDebouncedJsonPersistence({
|
|
25013
|
+
debounceMs: 250,
|
|
25014
|
+
filePath: filePath$1(),
|
|
25015
|
+
getValue: () => state,
|
|
25016
|
+
logLabel: "downloads"
|
|
25017
|
+
});
|
|
25018
|
+
let broadcaster$1 = null;
|
|
25019
|
+
function persist() {
|
|
25020
|
+
state.items = state.items.slice(0, 200);
|
|
25021
|
+
persistence$1.schedule();
|
|
25022
|
+
}
|
|
25023
|
+
function emit$1() {
|
|
25024
|
+
broadcaster$1?.(Channels.DOWNLOADS_UPDATE, listDownloads());
|
|
25025
|
+
}
|
|
25026
|
+
function setDownloadBroadcaster(fn) {
|
|
25027
|
+
broadcaster$1 = fn;
|
|
25028
|
+
}
|
|
25029
|
+
function listDownloads() {
|
|
25030
|
+
return state.items.map((item) => ({ ...item }));
|
|
25031
|
+
}
|
|
25032
|
+
function upsertDownload(input) {
|
|
25033
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
25034
|
+
const existing = state.items.find((item) => item.savePath === input.savePath);
|
|
25035
|
+
if (existing) {
|
|
25036
|
+
Object.assign(existing, input, { updatedAt: now });
|
|
25037
|
+
persist();
|
|
25038
|
+
emit$1();
|
|
25039
|
+
return existing;
|
|
25040
|
+
}
|
|
25041
|
+
const record = { id: crypto$2.randomUUID(), ...input, startedAt: now, updatedAt: now };
|
|
25042
|
+
state.items = [record, ...state.items];
|
|
25043
|
+
persist();
|
|
25044
|
+
emit$1();
|
|
25045
|
+
return record;
|
|
25046
|
+
}
|
|
25047
|
+
function clearDownloads() {
|
|
25048
|
+
state.items = [];
|
|
25049
|
+
persist();
|
|
25050
|
+
emit$1();
|
|
25051
|
+
}
|
|
25052
|
+
async function openDownload(id) {
|
|
25053
|
+
const item = state.items.find((d) => d.id === id);
|
|
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
|
+
}
|
|
25067
|
+
return await electron.shell.openPath(item.savePath) === "";
|
|
25068
|
+
}
|
|
25069
|
+
async function showDownloadInFolder(id) {
|
|
25070
|
+
const item = state.items.find((d) => d.id === id);
|
|
25071
|
+
if (!item || !fs$1.existsSync(item.savePath)) return false;
|
|
25072
|
+
electron.shell.showItemInFolder(item.savePath);
|
|
25073
|
+
return true;
|
|
25074
|
+
}
|
|
24860
25075
|
const defaultDownloadViews = /* @__PURE__ */ new Set();
|
|
24861
25076
|
let defaultDownloadHandlerInstalled = false;
|
|
24862
25077
|
function resolveDownloadPath(downloadDir, filename) {
|
|
@@ -24899,21 +25114,26 @@ function installDownloadHandlerForSession(targetSession, chromeView) {
|
|
|
24899
25114
|
const info = {
|
|
24900
25115
|
filename,
|
|
24901
25116
|
savePath,
|
|
25117
|
+
url: item.getURL(),
|
|
25118
|
+
mimeType: typeof item.getMimeType === "function" ? item.getMimeType() : void 0,
|
|
24902
25119
|
totalBytes: item.getTotalBytes(),
|
|
24903
25120
|
receivedBytes: 0,
|
|
24904
25121
|
state: "progressing"
|
|
24905
25122
|
};
|
|
24906
|
-
|
|
25123
|
+
const record = upsertDownload(info);
|
|
25124
|
+
send(Channels.DOWNLOAD_STARTED, { ...info, id: record.id, startedAt: record.startedAt, updatedAt: record.updatedAt });
|
|
24907
25125
|
item.on("updated", (_event2, state2) => {
|
|
24908
25126
|
info.receivedBytes = item.getReceivedBytes();
|
|
24909
25127
|
info.totalBytes = item.getTotalBytes();
|
|
24910
25128
|
info.state = state2 === "progressing" ? "progressing" : "interrupted";
|
|
24911
|
-
|
|
25129
|
+
const record2 = upsertDownload(info);
|
|
25130
|
+
send(Channels.DOWNLOAD_PROGRESS, { ...info, id: record2.id, startedAt: record2.startedAt, updatedAt: record2.updatedAt });
|
|
24912
25131
|
});
|
|
24913
25132
|
item.once("done", (_event2, state2) => {
|
|
24914
25133
|
info.receivedBytes = item.getReceivedBytes();
|
|
24915
25134
|
info.state = state2 === "completed" ? "completed" : "cancelled";
|
|
24916
|
-
|
|
25135
|
+
const record2 = upsertDownload(info);
|
|
25136
|
+
send(Channels.DOWNLOAD_DONE, { ...info, id: record2.id, startedAt: record2.startedAt, updatedAt: record2.updatedAt });
|
|
24917
25137
|
});
|
|
24918
25138
|
});
|
|
24919
25139
|
}
|
|
@@ -24943,9 +25163,9 @@ function loadRenderers(chromeView, sidebarView, devtoolsPanelView) {
|
|
|
24943
25163
|
const sidebarUrl = rendererUrlFor("sidebar");
|
|
24944
25164
|
const devtoolsUrl = rendererUrlFor("devtools");
|
|
24945
25165
|
if (chromeUrl && sidebarUrl && devtoolsUrl) {
|
|
24946
|
-
chromeView.webContents
|
|
24947
|
-
sidebarView.webContents
|
|
24948
|
-
devtoolsPanelView.webContents
|
|
25166
|
+
void loadTrustedAppURL(chromeView.webContents, chromeUrl);
|
|
25167
|
+
void loadTrustedAppURL(sidebarView.webContents, sidebarUrl);
|
|
25168
|
+
void loadTrustedAppURL(devtoolsPanelView.webContents, devtoolsUrl);
|
|
24949
25169
|
} else {
|
|
24950
25170
|
const rendererFile = resolveRendererFile();
|
|
24951
25171
|
chromeView.webContents.loadFile(rendererFile, {
|
|
@@ -25171,7 +25391,7 @@ function loadPrivateRenderer(chromeView) {
|
|
|
25171
25391
|
const url = new URL(devUrl);
|
|
25172
25392
|
url.searchParams.set("view", "chrome");
|
|
25173
25393
|
url.searchParams.set("private", "1");
|
|
25174
|
-
chromeView.webContents
|
|
25394
|
+
void loadTrustedAppURL(chromeView.webContents, url.toString());
|
|
25175
25395
|
} else {
|
|
25176
25396
|
chromeView.webContents.loadFile(resolveRendererFile(), {
|
|
25177
25397
|
query: { view: "chrome", private: "1" }
|
|
@@ -25406,7 +25626,7 @@ function loadSecondaryRenderer(chromeView) {
|
|
|
25406
25626
|
const url = new URL(devUrl);
|
|
25407
25627
|
url.searchParams.set("view", "chrome");
|
|
25408
25628
|
url.searchParams.set("secondary", "1");
|
|
25409
|
-
chromeView.webContents
|
|
25629
|
+
void loadTrustedAppURL(chromeView.webContents, url.toString());
|
|
25410
25630
|
} else {
|
|
25411
25631
|
chromeView.webContents.loadFile(resolveRendererFile(), {
|
|
25412
25632
|
query: { view: "chrome", secondary: "1" }
|
|
@@ -25626,63 +25846,67 @@ function registerBookmarkHandlers() {
|
|
|
25626
25846
|
});
|
|
25627
25847
|
electron.ipcMain.handle(
|
|
25628
25848
|
Channels.BOOKMARKS_EXPORT_HTML,
|
|
25629
|
-
async (
|
|
25630
|
-
|
|
25849
|
+
async (event, options) => {
|
|
25850
|
+
assertTrustedIpcSender(event);
|
|
25851
|
+
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
25631
25852
|
title: "Export Bookmarks",
|
|
25632
25853
|
defaultPath: "vessel-bookmarks.html",
|
|
25633
25854
|
filters: [{ name: "HTML Bookmarks", extensions: ["html"] }]
|
|
25634
25855
|
});
|
|
25635
|
-
if (canceled || !
|
|
25856
|
+
if (canceled || !filePath2) return null;
|
|
25636
25857
|
const content = exportBookmarksHtml({
|
|
25637
25858
|
includeNotes: options?.includeNotes ?? false
|
|
25638
25859
|
});
|
|
25639
|
-
await fs.promises.writeFile(
|
|
25860
|
+
await fs.promises.writeFile(filePath2, content, "utf-8");
|
|
25640
25861
|
trackBookmarkAction("export");
|
|
25641
25862
|
return {
|
|
25642
|
-
filePath,
|
|
25863
|
+
filePath: filePath2,
|
|
25643
25864
|
count: getState().bookmarks.length
|
|
25644
25865
|
};
|
|
25645
25866
|
}
|
|
25646
25867
|
);
|
|
25647
|
-
electron.ipcMain.handle(Channels.BOOKMARKS_EXPORT_JSON, async () => {
|
|
25648
|
-
|
|
25868
|
+
electron.ipcMain.handle(Channels.BOOKMARKS_EXPORT_JSON, async (event) => {
|
|
25869
|
+
assertTrustedIpcSender(event);
|
|
25870
|
+
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
25649
25871
|
title: "Export Vessel Bookmark Archive",
|
|
25650
25872
|
defaultPath: "vessel-bookmarks.json",
|
|
25651
25873
|
filters: [{ name: "Vessel Bookmark Archive", extensions: ["json"] }]
|
|
25652
25874
|
});
|
|
25653
|
-
if (canceled || !
|
|
25875
|
+
if (canceled || !filePath2) return null;
|
|
25654
25876
|
const content = exportBookmarksJson();
|
|
25655
|
-
await fs.promises.writeFile(
|
|
25877
|
+
await fs.promises.writeFile(filePath2, content, "utf-8");
|
|
25656
25878
|
trackBookmarkAction("export");
|
|
25657
25879
|
return {
|
|
25658
|
-
filePath,
|
|
25880
|
+
filePath: filePath2,
|
|
25659
25881
|
count: getState().bookmarks.length
|
|
25660
25882
|
};
|
|
25661
25883
|
});
|
|
25662
25884
|
electron.ipcMain.handle(
|
|
25663
25885
|
Channels.FOLDER_EXPORT_HTML,
|
|
25664
|
-
async (
|
|
25886
|
+
async (event, folderId, options) => {
|
|
25887
|
+
assertTrustedIpcSender(event);
|
|
25665
25888
|
const folder = getFolder(folderId);
|
|
25666
25889
|
if (!folder) return null;
|
|
25667
|
-
const { canceled, filePath } = await electron.dialog.showSaveDialog({
|
|
25890
|
+
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
25668
25891
|
title: `Export ${folder.name}`,
|
|
25669
25892
|
defaultPath: `vessel-bookmarks-${getSafeBookmarkExportName(folder.name)}.html`,
|
|
25670
25893
|
filters: [{ name: "HTML Bookmarks", extensions: ["html"] }]
|
|
25671
25894
|
});
|
|
25672
|
-
if (canceled || !
|
|
25895
|
+
if (canceled || !filePath2) return null;
|
|
25673
25896
|
const result = exportBookmarkFolderHtml(folderId, {
|
|
25674
25897
|
includeNotes: options?.includeNotes ?? true
|
|
25675
25898
|
});
|
|
25676
25899
|
if (!result) return null;
|
|
25677
|
-
await fs.promises.writeFile(
|
|
25900
|
+
await fs.promises.writeFile(filePath2, result.content, "utf-8");
|
|
25678
25901
|
trackBookmarkAction("export");
|
|
25679
25902
|
return {
|
|
25680
|
-
filePath,
|
|
25903
|
+
filePath: filePath2,
|
|
25681
25904
|
count: result.count
|
|
25682
25905
|
};
|
|
25683
25906
|
}
|
|
25684
25907
|
);
|
|
25685
|
-
electron.ipcMain.handle(Channels.BOOKMARKS_IMPORT_HTML, async () => {
|
|
25908
|
+
electron.ipcMain.handle(Channels.BOOKMARKS_IMPORT_HTML, async (event) => {
|
|
25909
|
+
assertTrustedIpcSender(event);
|
|
25686
25910
|
const { canceled, filePaths } = await electron.dialog.showOpenDialog({
|
|
25687
25911
|
title: "Import Bookmarks",
|
|
25688
25912
|
filters: [
|
|
@@ -25695,7 +25919,8 @@ function registerBookmarkHandlers() {
|
|
|
25695
25919
|
trackBookmarkAction("import");
|
|
25696
25920
|
return importBookmarksFromHtml(content);
|
|
25697
25921
|
});
|
|
25698
|
-
electron.ipcMain.handle(Channels.BOOKMARKS_IMPORT_JSON, async () => {
|
|
25922
|
+
electron.ipcMain.handle(Channels.BOOKMARKS_IMPORT_JSON, async (event) => {
|
|
25923
|
+
assertTrustedIpcSender(event);
|
|
25699
25924
|
const { canceled, filePaths } = await electron.dialog.showOpenDialog({
|
|
25700
25925
|
title: "Import Bookmarks",
|
|
25701
25926
|
filters: [
|
|
@@ -25726,32 +25951,36 @@ function registerHistoryHandlers() {
|
|
|
25726
25951
|
electron.ipcMain.handle(Channels.HISTORY_SEARCH, (_, query) => {
|
|
25727
25952
|
return search(query);
|
|
25728
25953
|
});
|
|
25729
|
-
electron.ipcMain.handle(Channels.HISTORY_CLEAR, () => {
|
|
25954
|
+
electron.ipcMain.handle(Channels.HISTORY_CLEAR, (event) => {
|
|
25955
|
+
assertTrustedIpcSender(event);
|
|
25730
25956
|
clearAll$1();
|
|
25731
25957
|
});
|
|
25732
|
-
electron.ipcMain.handle(Channels.HISTORY_EXPORT_HTML, async () => {
|
|
25733
|
-
|
|
25958
|
+
electron.ipcMain.handle(Channels.HISTORY_EXPORT_HTML, async (event) => {
|
|
25959
|
+
assertTrustedIpcSender(event);
|
|
25960
|
+
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
25734
25961
|
title: "Export History",
|
|
25735
25962
|
defaultPath: "vessel-history.html",
|
|
25736
25963
|
filters: [{ name: "HTML", extensions: ["html"] }]
|
|
25737
25964
|
});
|
|
25738
|
-
if (canceled || !
|
|
25965
|
+
if (canceled || !filePath2) return null;
|
|
25739
25966
|
const content = exportHistoryHtml();
|
|
25740
|
-
await fs.promises.writeFile(
|
|
25741
|
-
return { filePath, count: getState$1().entries.length };
|
|
25967
|
+
await fs.promises.writeFile(filePath2, content, "utf-8");
|
|
25968
|
+
return { filePath: filePath2, count: getState$1().entries.length };
|
|
25742
25969
|
});
|
|
25743
|
-
electron.ipcMain.handle(Channels.HISTORY_EXPORT_JSON, async () => {
|
|
25744
|
-
|
|
25970
|
+
electron.ipcMain.handle(Channels.HISTORY_EXPORT_JSON, async (event) => {
|
|
25971
|
+
assertTrustedIpcSender(event);
|
|
25972
|
+
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
25745
25973
|
title: "Export History",
|
|
25746
25974
|
defaultPath: "vessel-history.json",
|
|
25747
25975
|
filters: [{ name: "JSON", extensions: ["json"] }]
|
|
25748
25976
|
});
|
|
25749
|
-
if (canceled || !
|
|
25977
|
+
if (canceled || !filePath2) return null;
|
|
25750
25978
|
const content = exportHistoryJson();
|
|
25751
|
-
await fs.promises.writeFile(
|
|
25752
|
-
return { filePath, count: getState$1().entries.length };
|
|
25979
|
+
await fs.promises.writeFile(filePath2, content, "utf-8");
|
|
25980
|
+
return { filePath: filePath2, count: getState$1().entries.length };
|
|
25753
25981
|
});
|
|
25754
|
-
electron.ipcMain.handle(Channels.HISTORY_IMPORT, async () => {
|
|
25982
|
+
electron.ipcMain.handle(Channels.HISTORY_IMPORT, async (event) => {
|
|
25983
|
+
assertTrustedIpcSender(event);
|
|
25755
25984
|
const { canceled, filePaths } = await electron.dialog.showOpenDialog({
|
|
25756
25985
|
title: "Import History",
|
|
25757
25986
|
filters: [
|
|
@@ -25760,9 +25989,9 @@ function registerHistoryHandlers() {
|
|
|
25760
25989
|
properties: ["openFile"]
|
|
25761
25990
|
});
|
|
25762
25991
|
if (canceled || filePaths.length === 0) return null;
|
|
25763
|
-
const
|
|
25764
|
-
const content = await fs.promises.readFile(
|
|
25765
|
-
const result =
|
|
25992
|
+
const filePath2 = filePaths[0];
|
|
25993
|
+
const content = await fs.promises.readFile(filePath2, "utf-8");
|
|
25994
|
+
const result = filePath2.endsWith(".json") ? importHistoryFromJson(content) : importHistoryFromHtml(content);
|
|
25766
25995
|
return result;
|
|
25767
25996
|
});
|
|
25768
25997
|
}
|
|
@@ -25962,7 +26191,8 @@ function buildCertificateDetailsHtml(state2) {
|
|
|
25962
26191
|
</html>`;
|
|
25963
26192
|
}
|
|
25964
26193
|
function registerSecurityHandlers(tabManager) {
|
|
25965
|
-
electron.ipcMain.handle(Channels.SECURITY_SHOW_DETAILS, async (
|
|
26194
|
+
electron.ipcMain.handle(Channels.SECURITY_SHOW_DETAILS, async (event, state2) => {
|
|
26195
|
+
assertTrustedIpcSender(event);
|
|
25966
26196
|
const domain = (() => {
|
|
25967
26197
|
try {
|
|
25968
26198
|
return new URL(state2.url).hostname || state2.url;
|
|
@@ -25983,13 +26213,15 @@ function registerSecurityHandlers(tabManager) {
|
|
|
25983
26213
|
spellcheck: false
|
|
25984
26214
|
}
|
|
25985
26215
|
});
|
|
25986
|
-
void win.
|
|
26216
|
+
void loadInternalDataURL(win.webContents, `data:text/html;charset=utf-8,${encodeURIComponent(content)}`);
|
|
25987
26217
|
});
|
|
25988
|
-
electron.ipcMain.handle(Channels.SECURITY_PROCEED_ANYWAY, (
|
|
26218
|
+
electron.ipcMain.handle(Channels.SECURITY_PROCEED_ANYWAY, (event, tabId) => {
|
|
26219
|
+
assertTrustedIpcSender(event);
|
|
25989
26220
|
assertString(tabId, "tabId");
|
|
25990
26221
|
tabManager.proceedAnyway(tabId);
|
|
25991
26222
|
});
|
|
25992
|
-
electron.ipcMain.handle(Channels.SECURITY_GO_BACK_TO_SAFETY, (
|
|
26223
|
+
electron.ipcMain.handle(Channels.SECURITY_GO_BACK_TO_SAFETY, (event, tabId) => {
|
|
26224
|
+
assertTrustedIpcSender(event);
|
|
25993
26225
|
assertString(tabId, "tabId");
|
|
25994
26226
|
tabManager.goBackToSafety(tabId);
|
|
25995
26227
|
});
|
|
@@ -25997,6 +26229,7 @@ function registerSecurityHandlers(tabManager) {
|
|
|
25997
26229
|
const logger$5 = createLogger("CodexIPC");
|
|
25998
26230
|
function registerCodexHandlers() {
|
|
25999
26231
|
electron.ipcMain.handle(Channels.CODEX_START_AUTH, async (event) => {
|
|
26232
|
+
assertTrustedIpcSender(event);
|
|
26000
26233
|
const wc = event.sender;
|
|
26001
26234
|
if (!wc || wc.isDestroyed()) {
|
|
26002
26235
|
return {
|
|
@@ -26027,15 +26260,190 @@ function registerCodexHandlers() {
|
|
|
26027
26260
|
};
|
|
26028
26261
|
}
|
|
26029
26262
|
});
|
|
26030
|
-
electron.ipcMain.handle(Channels.CODEX_CANCEL_AUTH, () => {
|
|
26263
|
+
electron.ipcMain.handle(Channels.CODEX_CANCEL_AUTH, (event) => {
|
|
26264
|
+
assertTrustedIpcSender(event);
|
|
26031
26265
|
cancelCodexOAuth();
|
|
26032
26266
|
return { ok: true };
|
|
26033
26267
|
});
|
|
26034
|
-
electron.ipcMain.handle(Channels.CODEX_DISCONNECT, () => {
|
|
26268
|
+
electron.ipcMain.handle(Channels.CODEX_DISCONNECT, (event) => {
|
|
26269
|
+
assertTrustedIpcSender(event);
|
|
26035
26270
|
clearStoredCodexTokens();
|
|
26036
26271
|
return { ok: true };
|
|
26037
26272
|
});
|
|
26038
26273
|
}
|
|
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
|
+
}
|
|
26297
|
+
let records = loadJsonFile({
|
|
26298
|
+
filePath: filePath(),
|
|
26299
|
+
fallback: [],
|
|
26300
|
+
parse: (raw) => Array.isArray(raw) ? raw.filter(isPermissionRecord) : []
|
|
26301
|
+
});
|
|
26302
|
+
const persistence = createDebouncedJsonPersistence({ debounceMs: 250, filePath: filePath(), getValue: () => records, logLabel: "permissions" });
|
|
26303
|
+
const sessionDecisions = /* @__PURE__ */ new Map();
|
|
26304
|
+
let broadcaster = null;
|
|
26305
|
+
function key(origin, permission) {
|
|
26306
|
+
return `${origin}
|
|
26307
|
+
${permission}`;
|
|
26308
|
+
}
|
|
26309
|
+
function snapshot() {
|
|
26310
|
+
return records.map((record) => ({ ...record }));
|
|
26311
|
+
}
|
|
26312
|
+
function emit() {
|
|
26313
|
+
broadcaster?.(Channels.PERMISSIONS_GET, snapshot());
|
|
26314
|
+
}
|
|
26315
|
+
function save(origin, permission, decision) {
|
|
26316
|
+
const k = key(origin, permission);
|
|
26317
|
+
const existing = records.find((r) => key(r.origin, r.permission) === k);
|
|
26318
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26319
|
+
if (existing) Object.assign(existing, { decision, updatedAt });
|
|
26320
|
+
else records.unshift({ origin, permission, decision, updatedAt });
|
|
26321
|
+
persistence.schedule();
|
|
26322
|
+
emit();
|
|
26323
|
+
}
|
|
26324
|
+
function listPermissions() {
|
|
26325
|
+
return snapshot();
|
|
26326
|
+
}
|
|
26327
|
+
function clearPermissions() {
|
|
26328
|
+
records = [];
|
|
26329
|
+
sessionDecisions.clear();
|
|
26330
|
+
persistence.schedule();
|
|
26331
|
+
emit();
|
|
26332
|
+
}
|
|
26333
|
+
function clearPermissionsForOrigin(origin) {
|
|
26334
|
+
if (!parseOrigin(origin)) return;
|
|
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
|
+
}
|
|
26340
|
+
persistence.schedule();
|
|
26341
|
+
emit();
|
|
26342
|
+
}
|
|
26343
|
+
function setPermissionBroadcaster(fn) {
|
|
26344
|
+
broadcaster = fn;
|
|
26345
|
+
}
|
|
26346
|
+
function installPermissionHandler() {
|
|
26347
|
+
electron.session.defaultSession.setPermissionRequestHandler((webContents, permission, callback, details) => {
|
|
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);
|
|
26358
|
+
const existing = records.find((r) => r.origin === origin && r.permission === permission);
|
|
26359
|
+
if (existing) {
|
|
26360
|
+
callback(existing.decision === "allow");
|
|
26361
|
+
return;
|
|
26362
|
+
}
|
|
26363
|
+
const sessionDecision = sessionDecisions.get(k);
|
|
26364
|
+
if (sessionDecision) {
|
|
26365
|
+
callback(sessionDecision === "allow");
|
|
26366
|
+
return;
|
|
26367
|
+
}
|
|
26368
|
+
const result = electron.dialog.showMessageBoxSync({
|
|
26369
|
+
type: "question",
|
|
26370
|
+
buttons: ["Deny", "Allow Once", "Allow Until Quit", "Always Allow"],
|
|
26371
|
+
defaultId: 0,
|
|
26372
|
+
cancelId: 0,
|
|
26373
|
+
title: "Site permission request",
|
|
26374
|
+
message: `${origin} wants to use ${permission}`,
|
|
26375
|
+
detail: "Temporary choices are safer for camera, microphone, location, and clipboard access. Persistent choices can be cleared in Settings > Privacy."
|
|
26376
|
+
});
|
|
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);
|
|
26393
|
+
});
|
|
26394
|
+
}
|
|
26395
|
+
const GITHUB_LATEST_RELEASE_API_URL = "https://api.github.com/repos/unmodeled-tyler/quanta-vessel-browser/releases/latest";
|
|
26396
|
+
const RELEASES_URL = "https://github.com/unmodeled-tyler/quanta-vessel-browser/releases/latest";
|
|
26397
|
+
function normalizeVersion(version) {
|
|
26398
|
+
return version.replace(/^v/i, "").split(/[.-]/).slice(0, 3).map((part) => {
|
|
26399
|
+
const n = Number.parseInt(part, 10);
|
|
26400
|
+
return Number.isFinite(n) ? n : 0;
|
|
26401
|
+
});
|
|
26402
|
+
}
|
|
26403
|
+
function compareVersions(a, b) {
|
|
26404
|
+
const av = normalizeVersion(a);
|
|
26405
|
+
const bv = normalizeVersion(b);
|
|
26406
|
+
for (let i = 0; i < 3; i += 1) {
|
|
26407
|
+
if ((av[i] ?? 0) > (bv[i] ?? 0)) return 1;
|
|
26408
|
+
if ((av[i] ?? 0) < (bv[i] ?? 0)) return -1;
|
|
26409
|
+
}
|
|
26410
|
+
return 0;
|
|
26411
|
+
}
|
|
26412
|
+
async function checkForUpdates() {
|
|
26413
|
+
const currentVersion = electron.app.getVersion();
|
|
26414
|
+
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26415
|
+
try {
|
|
26416
|
+
const response = await fetch(GITHUB_LATEST_RELEASE_API_URL, {
|
|
26417
|
+
headers: { accept: "application/vnd.github+json", "user-agent": `Vessel/${currentVersion}` }
|
|
26418
|
+
});
|
|
26419
|
+
if (!response.ok) {
|
|
26420
|
+
throw new Error(`GitHub Releases responded with ${response.status}`);
|
|
26421
|
+
}
|
|
26422
|
+
const body = await response.json();
|
|
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;
|
|
26426
|
+
return {
|
|
26427
|
+
currentVersion,
|
|
26428
|
+
latestVersion,
|
|
26429
|
+
updateAvailable: compareVersions(latestVersion, currentVersion) > 0,
|
|
26430
|
+
checkedAt,
|
|
26431
|
+
releaseUrl
|
|
26432
|
+
};
|
|
26433
|
+
} catch (error) {
|
|
26434
|
+
return {
|
|
26435
|
+
currentVersion,
|
|
26436
|
+
latestVersion: null,
|
|
26437
|
+
updateAvailable: false,
|
|
26438
|
+
checkedAt,
|
|
26439
|
+
releaseUrl: RELEASES_URL,
|
|
26440
|
+
error: error instanceof Error ? error.message : "Update check failed"
|
|
26441
|
+
};
|
|
26442
|
+
}
|
|
26443
|
+
}
|
|
26444
|
+
async function openUpdateDownload() {
|
|
26445
|
+
await openExternalAllowlisted(RELEASES_URL, { hosts: ["github.com"] });
|
|
26446
|
+
}
|
|
26039
26447
|
let activeChatProvider = null;
|
|
26040
26448
|
const logger$4 = createLogger("IPC");
|
|
26041
26449
|
const VALID_APPROVAL_MODES = ["auto", "confirm-dangerous", "manual"];
|
|
@@ -26069,10 +26477,18 @@ async function togglePictureInPicture(tabManager) {
|
|
|
26069
26477
|
}
|
|
26070
26478
|
function registerIpcHandlers(windowState, runtime2) {
|
|
26071
26479
|
const { tabManager, chromeView, sidebarView, devtoolsPanelView, mainWindow } = windowState;
|
|
26072
|
-
|
|
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);
|
|
26073
26488
|
createPrivateWindow();
|
|
26074
26489
|
});
|
|
26075
|
-
electron.ipcMain.handle(Channels.OPEN_NEW_WINDOW, () => {
|
|
26490
|
+
electron.ipcMain.handle(Channels.OPEN_NEW_WINDOW, (event) => {
|
|
26491
|
+
requireTrusted(event);
|
|
26076
26492
|
createSecondaryWindow();
|
|
26077
26493
|
});
|
|
26078
26494
|
electron.ipcMain.handle(Channels.IS_PRIVATE_MODE, () => false);
|
|
@@ -26344,14 +26760,15 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26344
26760
|
const originalUrl = activeTab.readerOriginalUrl;
|
|
26345
26761
|
activeTab.setReaderMode(false);
|
|
26346
26762
|
if (originalUrl) {
|
|
26347
|
-
activeTab.view.webContents
|
|
26763
|
+
void loadPermittedNavigationURL(activeTab.view.webContents, originalUrl);
|
|
26348
26764
|
}
|
|
26349
26765
|
} else {
|
|
26350
26766
|
const originalUrl = activeTab.state.url;
|
|
26351
26767
|
const content = await extractContent(activeTab.view.webContents);
|
|
26352
26768
|
const html = generateReaderHTML(content);
|
|
26353
26769
|
activeTab.setReaderMode(true, originalUrl);
|
|
26354
|
-
|
|
26770
|
+
void loadInternalDataURL(
|
|
26771
|
+
activeTab.view.webContents,
|
|
26355
26772
|
`data:text/html;charset=utf-8,${encodeURIComponent(html)}`
|
|
26356
26773
|
);
|
|
26357
26774
|
}
|
|
@@ -26433,18 +26850,23 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26433
26850
|
return getRendererSettings();
|
|
26434
26851
|
});
|
|
26435
26852
|
electron.ipcMain.handle(Channels.SETTINGS_HEALTH_GET, () => getRuntimeHealth());
|
|
26436
|
-
electron.ipcMain.handle(Channels.
|
|
26437
|
-
|
|
26438
|
-
|
|
26439
|
-
|
|
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);
|
|
26859
|
+
assertString(key2, "key");
|
|
26860
|
+
if (!SETTABLE_KEYS.has(key2)) {
|
|
26861
|
+
throw new Error(`Unknown setting key: ${key2}`);
|
|
26440
26862
|
}
|
|
26441
|
-
const settingsKey =
|
|
26863
|
+
const settingsKey = key2;
|
|
26442
26864
|
const updatedSettings = setSetting(settingsKey, value);
|
|
26443
|
-
trackSettingChanged(
|
|
26444
|
-
if (
|
|
26865
|
+
trackSettingChanged(key2);
|
|
26866
|
+
if (key2 === "approvalMode") {
|
|
26445
26867
|
runtime2.setApprovalMode(value);
|
|
26446
26868
|
}
|
|
26447
|
-
if (
|
|
26869
|
+
if (key2 === "mcpPort") {
|
|
26448
26870
|
await stopMcpServer();
|
|
26449
26871
|
await startMcpServer(tabManager, runtime2, updatedSettings.mcpPort);
|
|
26450
26872
|
}
|
|
@@ -26453,11 +26875,18 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26453
26875
|
return rendererSettings;
|
|
26454
26876
|
});
|
|
26455
26877
|
electron.ipcMain.handle(Channels.AGENT_RUNTIME_GET, () => runtime2.getState());
|
|
26456
|
-
electron.ipcMain.handle(Channels.AGENT_PAUSE, () =>
|
|
26457
|
-
|
|
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
|
+
});
|
|
26458
26886
|
electron.ipcMain.handle(
|
|
26459
26887
|
Channels.AGENT_SET_APPROVAL_MODE,
|
|
26460
|
-
(
|
|
26888
|
+
(event, mode) => {
|
|
26889
|
+
requireTrusted(event);
|
|
26461
26890
|
assertString(mode, "mode");
|
|
26462
26891
|
if (!VALID_APPROVAL_MODES.includes(mode)) {
|
|
26463
26892
|
throw new Error(`Invalid approval mode: ${mode}`);
|
|
@@ -26469,7 +26898,10 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26469
26898
|
);
|
|
26470
26899
|
electron.ipcMain.handle(
|
|
26471
26900
|
Channels.AGENT_APPROVAL_RESOLVE,
|
|
26472
|
-
(
|
|
26901
|
+
(event, approvalId, approved) => {
|
|
26902
|
+
requireTrusted(event);
|
|
26903
|
+
return runtime2.resolveApproval(approvalId, approved);
|
|
26904
|
+
}
|
|
26473
26905
|
);
|
|
26474
26906
|
electron.ipcMain.handle(
|
|
26475
26907
|
Channels.AGENT_CHECKPOINT_CREATE,
|
|
@@ -26493,7 +26925,10 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26493
26925
|
);
|
|
26494
26926
|
electron.ipcMain.handle(
|
|
26495
26927
|
Channels.AGENT_SESSION_RESTORE,
|
|
26496
|
-
(
|
|
26928
|
+
(event, snapshot2) => {
|
|
26929
|
+
requireTrusted(event);
|
|
26930
|
+
return runtime2.restoreSession(snapshot2);
|
|
26931
|
+
}
|
|
26497
26932
|
);
|
|
26498
26933
|
registerBookmarkHandlers();
|
|
26499
26934
|
electron.ipcMain.handle(Channels.HIGHLIGHT_CAPTURE, async () => {
|
|
@@ -26613,17 +27048,20 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26613
27048
|
electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, () => {
|
|
26614
27049
|
return getInstalledKits();
|
|
26615
27050
|
});
|
|
26616
|
-
electron.ipcMain.handle(Channels.AUTOMATION_INSTALL_FROM_FILE, async () => {
|
|
27051
|
+
electron.ipcMain.handle(Channels.AUTOMATION_INSTALL_FROM_FILE, async (event) => {
|
|
27052
|
+
requireTrusted(event);
|
|
26617
27053
|
return await installKitFromFile();
|
|
26618
27054
|
});
|
|
26619
|
-
electron.ipcMain.handle(Channels.AUTOMATION_UNINSTALL, (
|
|
27055
|
+
electron.ipcMain.handle(Channels.AUTOMATION_UNINSTALL, (event, id) => {
|
|
27056
|
+
requireTrusted(event);
|
|
26620
27057
|
assertString(id, "id");
|
|
26621
27058
|
return uninstallKit(id, getScheduledKitIds());
|
|
26622
27059
|
});
|
|
26623
27060
|
registerScheduleHandlers(windowState, runtime2, sendToRendererViews);
|
|
26624
27061
|
registerAutofillHandlers(windowState);
|
|
26625
27062
|
registerPageDiffHandlers(windowState, sendToRendererViews);
|
|
26626
|
-
electron.ipcMain.handle(Channels.CLEAR_BROWSING_DATA, async (
|
|
27063
|
+
electron.ipcMain.handle(Channels.CLEAR_BROWSING_DATA, async (event, options) => {
|
|
27064
|
+
requireTrusted(event);
|
|
26627
27065
|
const { cache, cookies, history, localStorage: clearLs, timeRange } = options;
|
|
26628
27066
|
if (cache) {
|
|
26629
27067
|
await electron.session.defaultSession.clearCache();
|
|
@@ -26638,6 +27076,38 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26638
27076
|
clearByTimeRange(timeRange);
|
|
26639
27077
|
}
|
|
26640
27078
|
});
|
|
27079
|
+
setDownloadBroadcaster(sendToRendererViews);
|
|
27080
|
+
setPermissionBroadcaster(sendToRendererViews);
|
|
27081
|
+
electron.ipcMain.handle(Channels.DOWNLOADS_GET, () => listDownloads());
|
|
27082
|
+
electron.ipcMain.handle(Channels.DOWNLOADS_CLEAR, (event) => {
|
|
27083
|
+
requireTrusted(event);
|
|
27084
|
+
clearDownloads();
|
|
27085
|
+
return true;
|
|
27086
|
+
});
|
|
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
|
+
});
|
|
27095
|
+
electron.ipcMain.handle(Channels.PERMISSIONS_GET, () => listPermissions());
|
|
27096
|
+
electron.ipcMain.handle(Channels.PERMISSIONS_CLEAR, (event) => {
|
|
27097
|
+
requireTrusted(event);
|
|
27098
|
+
clearPermissions();
|
|
27099
|
+
return true;
|
|
27100
|
+
});
|
|
27101
|
+
electron.ipcMain.handle(Channels.PERMISSIONS_CLEAR_ORIGIN, (event, origin) => {
|
|
27102
|
+
requireTrusted(event);
|
|
27103
|
+
clearPermissionsForOrigin(origin);
|
|
27104
|
+
return true;
|
|
27105
|
+
});
|
|
27106
|
+
electron.ipcMain.handle(Channels.UPDATES_CHECK, () => checkForUpdates());
|
|
27107
|
+
electron.ipcMain.handle(Channels.UPDATES_OPEN_DOWNLOAD, (event) => {
|
|
27108
|
+
requireTrusted(event);
|
|
27109
|
+
return openUpdateDownload();
|
|
27110
|
+
});
|
|
26641
27111
|
electron.ipcMain.handle(Channels.TAB_TOGGLE_PIP, async () => {
|
|
26642
27112
|
return togglePictureInPicture(tabManager);
|
|
26643
27113
|
});
|
|
@@ -27033,9 +27503,9 @@ function clone(value) {
|
|
|
27033
27503
|
function summarizeArgs(args) {
|
|
27034
27504
|
const entries = Object.entries(args).filter(([, value]) => value != null);
|
|
27035
27505
|
if (entries.length === 0) return "No arguments";
|
|
27036
|
-
return entries.map(([
|
|
27506
|
+
return entries.map(([key2, value]) => {
|
|
27037
27507
|
const rendered = typeof value === "string" ? value : JSON.stringify(value);
|
|
27038
|
-
return `${
|
|
27508
|
+
return `${key2}=${String(rendered).slice(0, 120)}`;
|
|
27039
27509
|
}).join(", ");
|
|
27040
27510
|
}
|
|
27041
27511
|
function summarizeText(value) {
|
|
@@ -27070,6 +27540,7 @@ class AgentRuntime {
|
|
|
27070
27540
|
this.state = this.loadPersistedState();
|
|
27071
27541
|
onMcpStatusChange(() => this.emit());
|
|
27072
27542
|
}
|
|
27543
|
+
tabManager;
|
|
27073
27544
|
state;
|
|
27074
27545
|
updateListener = null;
|
|
27075
27546
|
pendingResolvers = /* @__PURE__ */ new Map();
|
|
@@ -27081,11 +27552,11 @@ class AgentRuntime {
|
|
|
27081
27552
|
}
|
|
27082
27553
|
}
|
|
27083
27554
|
getState() {
|
|
27084
|
-
const
|
|
27085
|
-
|
|
27086
|
-
|
|
27087
|
-
|
|
27088
|
-
return
|
|
27555
|
+
const snapshot2 = clone(this.state);
|
|
27556
|
+
snapshot2.mcpStatus = getMcpStatus();
|
|
27557
|
+
snapshot2.canUndo = this.canUndo();
|
|
27558
|
+
snapshot2.undoInfo = this.getUndoInfo();
|
|
27559
|
+
return snapshot2;
|
|
27089
27560
|
}
|
|
27090
27561
|
onTabStateChanged() {
|
|
27091
27562
|
this.captureSession();
|
|
@@ -27121,13 +27592,13 @@ class AgentRuntime {
|
|
|
27121
27592
|
return this.getState();
|
|
27122
27593
|
}
|
|
27123
27594
|
createCheckpoint(name, note) {
|
|
27124
|
-
const
|
|
27595
|
+
const snapshot2 = this.captureSession(note);
|
|
27125
27596
|
const checkpoint = {
|
|
27126
27597
|
id: crypto$2.randomUUID(),
|
|
27127
27598
|
name: name?.trim() || `Checkpoint ${this.state.checkpoints.length + 1}`,
|
|
27128
27599
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27129
27600
|
note: note?.trim() || void 0,
|
|
27130
|
-
snapshot
|
|
27601
|
+
snapshot: snapshot2
|
|
27131
27602
|
};
|
|
27132
27603
|
this.state.checkpoints = [...this.state.checkpoints, checkpoint].slice(
|
|
27133
27604
|
-20
|
|
@@ -27161,26 +27632,26 @@ class AgentRuntime {
|
|
|
27161
27632
|
return { actionName: latest.actionName, capturedAt: latest.capturedAt };
|
|
27162
27633
|
}
|
|
27163
27634
|
undoLastAction() {
|
|
27164
|
-
const
|
|
27165
|
-
if (!
|
|
27635
|
+
const snapshot2 = this.undoSnapshots.at(-1);
|
|
27636
|
+
if (!snapshot2) return null;
|
|
27166
27637
|
try {
|
|
27167
|
-
this.tabManager.restoreSession(
|
|
27638
|
+
this.tabManager.restoreSession(snapshot2.snapshot);
|
|
27168
27639
|
this.undoSnapshots.pop();
|
|
27169
27640
|
} catch (error) {
|
|
27170
27641
|
logger$3.error("Failed to restore undo snapshot", error);
|
|
27171
27642
|
return null;
|
|
27172
27643
|
}
|
|
27173
|
-
this.captureSession(`Undid ${
|
|
27174
|
-
return
|
|
27644
|
+
this.captureSession(`Undid ${snapshot2.actionName}`);
|
|
27645
|
+
return snapshot2.actionName;
|
|
27175
27646
|
}
|
|
27176
27647
|
captureSession(note) {
|
|
27177
|
-
const
|
|
27178
|
-
this.state.session =
|
|
27648
|
+
const snapshot2 = this.tabManager.snapshotSession(note);
|
|
27649
|
+
this.state.session = snapshot2;
|
|
27179
27650
|
this.emit();
|
|
27180
|
-
return clone(
|
|
27651
|
+
return clone(snapshot2);
|
|
27181
27652
|
}
|
|
27182
|
-
restoreSession(
|
|
27183
|
-
const target =
|
|
27653
|
+
restoreSession(snapshot2) {
|
|
27654
|
+
const target = snapshot2 || this.state.session;
|
|
27184
27655
|
if (!target) {
|
|
27185
27656
|
return this.captureSession("No saved session to restore");
|
|
27186
27657
|
}
|
|
@@ -27322,6 +27793,7 @@ ${progress}
|
|
|
27322
27793
|
args = {},
|
|
27323
27794
|
tabId = null,
|
|
27324
27795
|
dangerous = false,
|
|
27796
|
+
requiresApproval = false,
|
|
27325
27797
|
undoable,
|
|
27326
27798
|
executor
|
|
27327
27799
|
}) {
|
|
@@ -27341,7 +27813,7 @@ ${progress}
|
|
|
27341
27813
|
streamId: transcriptStreamId,
|
|
27342
27814
|
mode: "replace"
|
|
27343
27815
|
});
|
|
27344
|
-
const approvalReason = this.getApprovalReason(dangerous);
|
|
27816
|
+
const approvalReason = this.getApprovalReason(dangerous, requiresApproval);
|
|
27345
27817
|
if (approvalReason) {
|
|
27346
27818
|
this.publishTranscript({
|
|
27347
27819
|
source,
|
|
@@ -27419,8 +27891,8 @@ ${progress}
|
|
|
27419
27891
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
27420
27892
|
};
|
|
27421
27893
|
}
|
|
27422
|
-
pushUndoSnapshot(
|
|
27423
|
-
this.undoSnapshots = [...this.undoSnapshots,
|
|
27894
|
+
pushUndoSnapshot(snapshot2) {
|
|
27895
|
+
this.undoSnapshots = [...this.undoSnapshots, snapshot2].slice(
|
|
27424
27896
|
-10
|
|
27425
27897
|
);
|
|
27426
27898
|
}
|
|
@@ -27564,10 +28036,13 @@ ${progress}
|
|
|
27564
28036
|
)
|
|
27565
28037
|
};
|
|
27566
28038
|
}
|
|
27567
|
-
getApprovalReason(dangerous) {
|
|
28039
|
+
getApprovalReason(dangerous, requiresApproval) {
|
|
27568
28040
|
if (this.state.supervisor.paused) {
|
|
27569
28041
|
return "Agent execution is paused";
|
|
27570
28042
|
}
|
|
28043
|
+
if (requiresApproval) {
|
|
28044
|
+
return "Approval required: high-risk action";
|
|
28045
|
+
}
|
|
27571
28046
|
if (this.state.supervisor.approvalMode === "manual") {
|
|
27572
28047
|
return "Approval required: ask every time mode";
|
|
27573
28048
|
}
|
|
@@ -28061,6 +28536,7 @@ async function bootstrap() {
|
|
|
28061
28536
|
sidebarView.webContents.send(Channels.HISTORY_UPDATE, state2);
|
|
28062
28537
|
});
|
|
28063
28538
|
installDownloadHandler(chromeView);
|
|
28539
|
+
installPermissionHandler();
|
|
28064
28540
|
startBackgroundRevalidation();
|
|
28065
28541
|
startTelemetry();
|
|
28066
28542
|
const initializeChromeRenderer = () => {
|