@quanta-intellect/vessel-browser 0.1.88 → 0.1.92

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
@@ -6,11 +6,12 @@ const fs = require("fs");
6
6
  const crypto$1 = require("crypto");
7
7
  const Anthropic = require("@anthropic-ai/sdk");
8
8
  const OpenAI = require("openai");
9
+ const http = require("http");
9
10
  const path$1 = require("node:path");
10
11
  const node_module = require("node:module");
11
12
  const zod = require("zod");
12
13
  const crypto$2 = require("node:crypto");
13
- const http = require("node:http");
14
+ const http$1 = require("node:http");
14
15
  const os = require("node:os");
15
16
  const mcp_js = require("@modelcontextprotocol/sdk/server/mcp.js");
16
17
  const streamableHttp_js = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
@@ -77,7 +78,8 @@ const defaults = {
77
78
  };
78
79
  const SAVE_DEBOUNCE_MS$6 = 150;
79
80
  const CHAT_PROVIDER_SECRET_FILENAME = "vessel-chat-provider-secret";
80
- const logger$k = createLogger("Settings");
81
+ const CODEX_TOKENS_FILENAME = "vessel-codex-tokens";
82
+ const logger$n = createLogger("Settings");
81
83
  const SETTABLE_KEYS = new Set(Object.keys(defaults));
82
84
  let settings = null;
83
85
  let settingsIssues = [];
@@ -115,15 +117,15 @@ function readStoredProviderSecret() {
115
117
  return null;
116
118
  }
117
119
  function writeStoredProviderSecret(secret) {
118
- const filePath = getChatProviderSecretPath();
119
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
120
+ const filePath2 = getChatProviderSecretPath();
121
+ fs.mkdirSync(path.dirname(filePath2), { recursive: true });
120
122
  const payload = JSON.stringify(secret);
121
123
  if (canUseSafeStorage$1()) {
122
124
  const encrypted = electron.safeStorage.encryptString(payload);
123
- fs.writeFileSync(filePath, encrypted, { mode: 384 });
125
+ fs.writeFileSync(filePath2, encrypted, { mode: 384 });
124
126
  return;
125
127
  }
126
- fs.writeFileSync(filePath, payload, { mode: 384 });
128
+ fs.writeFileSync(filePath2, payload, { mode: 384 });
127
129
  }
128
130
  function clearStoredProviderSecret() {
129
131
  try {
@@ -131,6 +133,38 @@ function clearStoredProviderSecret() {
131
133
  } catch {
132
134
  }
133
135
  }
136
+ function getCodexTokensPath() {
137
+ return path.join(getUserDataPath(), CODEX_TOKENS_FILENAME);
138
+ }
139
+ function readStoredCodexTokens() {
140
+ try {
141
+ const raw = fs.readFileSync(getCodexTokensPath());
142
+ const decoded = canUseSafeStorage$1() && electron.safeStorage.decryptString ? electron.safeStorage.decryptString(raw) : raw.toString("utf-8");
143
+ const parsed = JSON.parse(decoded);
144
+ if (parsed && typeof parsed === "object" && typeof parsed.accessToken === "string" && typeof parsed.refreshToken === "string") {
145
+ return parsed;
146
+ }
147
+ } catch {
148
+ }
149
+ return null;
150
+ }
151
+ function writeStoredCodexTokens(tokens) {
152
+ const filePath2 = getCodexTokensPath();
153
+ fs.mkdirSync(path.dirname(filePath2), { recursive: true });
154
+ const payload = JSON.stringify(tokens);
155
+ if (canUseSafeStorage$1()) {
156
+ const encrypted = electron.safeStorage.encryptString(payload);
157
+ fs.writeFileSync(filePath2, encrypted, { mode: 384 });
158
+ return;
159
+ }
160
+ fs.writeFileSync(filePath2, payload, { mode: 384 });
161
+ }
162
+ function clearStoredCodexTokens() {
163
+ try {
164
+ fs.unlinkSync(getCodexTokensPath());
165
+ } catch {
166
+ }
167
+ }
134
168
  function mergeChatProviderSecret(provider) {
135
169
  if (!provider) return null;
136
170
  const stored = readStoredProviderSecret();
@@ -157,12 +191,14 @@ function buildPersistedSettings(source) {
157
191
  }
158
192
  function getRendererSettings() {
159
193
  const current = loadSettings();
194
+ const provider = current.chatProvider;
195
+ const hasCodexTokens = provider?.id === "openai_codex" && readStoredCodexTokens() !== null;
160
196
  return {
161
197
  ...current,
162
- chatProvider: current.chatProvider ? {
163
- ...current.chatProvider,
198
+ chatProvider: provider ? {
199
+ ...provider,
164
200
  apiKey: "",
165
- hasApiKey: Boolean(current.chatProvider.apiKey)
201
+ hasApiKey: Boolean(provider.apiKey) || hasCodexTokens
166
202
  } : null
167
203
  };
168
204
  }
@@ -234,7 +270,7 @@ function persistNow() {
234
270
  getSettingsPath(),
235
271
  JSON.stringify(buildPersistedSettings(settings), null, 2)
236
272
  )
237
- ).catch((err) => logger$k.error("Failed to save settings:", err));
273
+ ).catch((err) => logger$n.error("Failed to save settings:", err));
238
274
  }
239
275
  function saveSettings() {
240
276
  saveDirty = true;
@@ -246,11 +282,11 @@ function saveSettings() {
246
282
  }
247
283
  }, SAVE_DEBOUNCE_MS$6);
248
284
  }
249
- function setSetting(key, value) {
285
+ function setSetting(key2, value) {
250
286
  loadSettings();
251
- if (key === "mcpPort") {
287
+ if (key2 === "mcpPort") {
252
288
  settings.mcpPort = sanitizePort(value);
253
- } else if (key === "chatProvider") {
289
+ } else if (key2 === "chatProvider") {
254
290
  const nextProvider = value;
255
291
  if (!nextProvider) {
256
292
  clearStoredProviderSecret();
@@ -278,7 +314,7 @@ function setSetting(key, value) {
278
314
  };
279
315
  }
280
316
  } else {
281
- settings[key] = value;
317
+ settings[key2] = value;
282
318
  }
283
319
  saveSettings();
284
320
  return { ...settings };
@@ -341,7 +377,7 @@ function assertPermittedNavigationURL(url) {
341
377
  }
342
378
  const MAX_CUSTOM_HISTORY = 50;
343
379
  const READER_MODE_DATA_URL_PREFIX = "data:text/html;charset=utf-8,";
344
- const logger$j = createLogger("Tab");
380
+ const logger$m = createLogger("Tab");
345
381
  const sessionCertExceptions = /* @__PURE__ */ new WeakMap();
346
382
  const sessionsWithVerifyProc = /* @__PURE__ */ new WeakSet();
347
383
  const CERT_VERIFY_TRUST = 0;
@@ -407,7 +443,7 @@ class Tab {
407
443
  guardedLoadURL(url, options) {
408
444
  const blockReason = this.getNavigationBlockReason(url);
409
445
  if (blockReason) {
410
- logger$j.warn(blockReason);
446
+ logger$m.warn(blockReason);
411
447
  return blockReason;
412
448
  }
413
449
  void this.view.webContents.loadURL(url, options);
@@ -454,27 +490,27 @@ class Tab {
454
490
  this.view.webContents.on("before-input-event", (event, input) => {
455
491
  if (!input.control && !input.meta) return;
456
492
  if (input.type !== "keyDown") return;
457
- const key = input.key.toLowerCase();
493
+ const key2 = input.key.toLowerCase();
458
494
  const wc = this.view.webContents;
459
- if (key === "+" || key === "=") {
495
+ if (key2 === "+" || key2 === "=") {
460
496
  this.zoomIn();
461
497
  event.preventDefault();
462
498
  return;
463
499
  }
464
- if (key === "-") {
500
+ if (key2 === "-") {
465
501
  this.zoomOut();
466
502
  event.preventDefault();
467
503
  return;
468
504
  }
469
- if (key === "0") {
505
+ if (key2 === "0") {
470
506
  this.zoomReset();
471
507
  event.preventDefault();
472
508
  return;
473
509
  }
474
- if (key === "c") wc.copy();
475
- else if (key === "v") wc.paste();
476
- else if (key === "x") wc.cut();
477
- else if (key === "a") wc.selectAll();
510
+ if (key2 === "c") wc.copy();
511
+ else if (key2 === "v") wc.paste();
512
+ else if (key2 === "x") wc.cut();
513
+ else if (key2 === "a") wc.selectAll();
478
514
  });
479
515
  this.setupListeners();
480
516
  if (url) {
@@ -491,7 +527,7 @@ class Tab {
491
527
  wc.setWindowOpenHandler(({ url, disposition }) => {
492
528
  const error = this.getNavigationBlockReason(url);
493
529
  if (error) {
494
- logger$j.warn(error);
530
+ logger$m.warn(error);
495
531
  return { action: "deny" };
496
532
  }
497
533
  this.onOpenUrl?.({
@@ -505,7 +541,7 @@ class Tab {
505
541
  const error = this.getNavigationBlockReason(url);
506
542
  if (!error) return;
507
543
  event.preventDefault();
508
- logger$j.warn(`${context}: ${error}`);
544
+ logger$m.warn(`${context}: ${error}`);
509
545
  };
510
546
  wc.on("will-navigate", (event, url) => {
511
547
  blockNavigation(event, url, "Blocked top-level navigation");
@@ -589,7 +625,7 @@ class Tab {
589
625
  ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 999px; }
590
626
  ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.22); }
591
627
  ::-webkit-scrollbar-corner { background: transparent; }
592
- `).catch((err) => logger$j.warn("Failed to inject scrollbar CSS:", err));
628
+ `).catch((err) => logger$m.warn("Failed to inject scrollbar CSS:", err));
593
629
  });
594
630
  wc.on("page-favicon-updated", (_, favicons) => {
595
631
  this._state.favicon = favicons[0] || "";
@@ -625,7 +661,7 @@ class Tab {
625
661
  ).then((highlightedText) => {
626
662
  this.buildContextMenu(wc, params, highlightedText.trim());
627
663
  }).catch((err) => {
628
- logger$j.warn("Failed to inspect highlighted text for context menu:", err);
664
+ logger$m.warn("Failed to inspect highlighted text for context menu:", err);
629
665
  this.buildContextMenu(wc, params, "");
630
666
  });
631
667
  });
@@ -748,8 +784,8 @@ class Tab {
748
784
  }
749
785
  if (postBody) {
750
786
  const params = new URLSearchParams();
751
- for (const [key, value] of Object.entries(postBody)) {
752
- params.set(key, value);
787
+ for (const [key2, value] of Object.entries(postBody)) {
788
+ params.set(key2, value);
753
789
  }
754
790
  return this.guardedLoadURL(url, {
755
791
  method: "POST",
@@ -826,7 +862,7 @@ class Tab {
826
862
  "document.documentElement.outerHTML"
827
863
  );
828
864
  } catch (err) {
829
- logger$j.warn("Failed to retrieve page source:", err);
865
+ logger$m.warn("Failed to retrieve page source:", err);
830
866
  return;
831
867
  }
832
868
  const escaped = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -954,7 +990,7 @@ class Tab {
954
990
  document.addEventListener('mouseup', window.__vesselHighlightHandler);
955
991
  }
956
992
  })()
957
- `).catch((err) => logger$j.warn("Failed to inject highlight listener:", err));
993
+ `).catch((err) => logger$m.warn("Failed to inject highlight listener:", err));
958
994
  } else {
959
995
  void wc.executeJavaScript(`
960
996
  (function() {
@@ -965,7 +1001,7 @@ class Tab {
965
1001
  delete window.__vesselHighlightHandler;
966
1002
  }
967
1003
  })()
968
- `).catch((err) => logger$j.warn("Failed to remove highlight listener:", err));
1004
+ `).catch((err) => logger$m.warn("Failed to remove highlight listener:", err));
969
1005
  }
970
1006
  }
971
1007
  get webContentsId() {
@@ -1002,7 +1038,7 @@ const SEARCH_ENGINE_PRESETS = {
1002
1038
  ecosia: { label: "Ecosia", url: "https://www.ecosia.org/search?q=" },
1003
1039
  kagi: { label: "Kagi", url: "https://kagi.com/search?q=" }
1004
1040
  };
1005
- const logger$i = createLogger("JsonPersistence");
1041
+ const logger$l = createLogger("JsonPersistence");
1006
1042
  function canUseSafeStorage() {
1007
1043
  try {
1008
1044
  return electron.safeStorage.isEncryptionAvailable();
@@ -1023,22 +1059,22 @@ function encodeStoredData(payload, secure) {
1023
1059
  return payload;
1024
1060
  }
1025
1061
  function loadJsonFile({
1026
- filePath,
1062
+ filePath: filePath2,
1027
1063
  fallback,
1028
- parse,
1064
+ parse: parse2,
1029
1065
  secure = false
1030
1066
  }) {
1031
1067
  try {
1032
- const raw = fs.readFileSync(filePath);
1068
+ const raw = fs.readFileSync(filePath2);
1033
1069
  const decoded = decodeStoredData(raw, secure);
1034
- return parse(JSON.parse(decoded));
1070
+ return parse2(JSON.parse(decoded));
1035
1071
  } catch {
1036
1072
  return fallback;
1037
1073
  }
1038
1074
  }
1039
1075
  function createDebouncedJsonPersistence({
1040
1076
  debounceMs,
1041
- filePath,
1077
+ filePath: filePath2,
1042
1078
  getValue,
1043
1079
  logLabel,
1044
1080
  resetOnSchedule = false,
@@ -1061,13 +1097,13 @@ function createDebouncedJsonPersistence({
1061
1097
  2
1062
1098
  );
1063
1099
  const data = encodeStoredData(payload, secure);
1064
- await fs.promises.mkdir(path.dirname(filePath), { recursive: true }).then(
1100
+ await fs.promises.mkdir(path.dirname(filePath2), { recursive: true }).then(
1065
1101
  () => fs.promises.writeFile(
1066
- filePath,
1102
+ filePath2,
1067
1103
  data,
1068
1104
  typeof data === "string" ? { encoding: "utf-8", mode: 384 } : { mode: 384 }
1069
1105
  )
1070
- ).catch((err) => logger$i.error(`Failed to save ${logLabel}:`, err));
1106
+ ).catch((err) => logger$l.error(`Failed to save ${logLabel}:`, err));
1071
1107
  };
1072
1108
  const schedule = () => {
1073
1109
  saveDirty2 = true;
@@ -1089,7 +1125,7 @@ function createDebouncedJsonPersistence({
1089
1125
  flush: flush2
1090
1126
  };
1091
1127
  }
1092
- let state$4 = null;
1128
+ let state$5 = null;
1093
1129
  const listeners$2 = /* @__PURE__ */ new Set();
1094
1130
  const SAVE_DEBOUNCE_MS$5 = 250;
1095
1131
  function getHighlightsPath() {
@@ -1099,19 +1135,19 @@ function createPersistence$1() {
1099
1135
  return createDebouncedJsonPersistence({
1100
1136
  debounceMs: SAVE_DEBOUNCE_MS$5,
1101
1137
  filePath: getHighlightsPath(),
1102
- getValue: () => state$4,
1138
+ getValue: () => state$5,
1103
1139
  logLabel: "highlights",
1104
1140
  resetOnSchedule: true
1105
1141
  });
1106
1142
  }
1107
- let persistence$5 = null;
1143
+ let persistence$7 = null;
1108
1144
  function getPersistence$1() {
1109
- persistence$5 ??= createPersistence$1();
1110
- return persistence$5;
1145
+ persistence$7 ??= createPersistence$1();
1146
+ return persistence$7;
1111
1147
  }
1112
1148
  function load$4() {
1113
- if (state$4) return state$4;
1114
- state$4 = loadJsonFile({
1149
+ if (state$5) return state$5;
1150
+ state$5 = loadJsonFile({
1115
1151
  filePath: getHighlightsPath(),
1116
1152
  fallback: { highlights: [] },
1117
1153
  parse: (raw) => {
@@ -1121,14 +1157,14 @@ function load$4() {
1121
1157
  };
1122
1158
  }
1123
1159
  });
1124
- return state$4;
1160
+ return state$5;
1125
1161
  }
1126
- function save$2() {
1162
+ function save$3() {
1127
1163
  getPersistence$1().schedule();
1128
1164
  }
1129
- function emit$2() {
1130
- if (!state$4) return;
1131
- const snapshot = { highlights: [...state$4.highlights] };
1165
+ function emit$4() {
1166
+ if (!state$5) return;
1167
+ const snapshot = { highlights: [...state$5.highlights] };
1132
1168
  for (const listener of listeners$2) {
1133
1169
  listener(snapshot);
1134
1170
  }
@@ -1144,12 +1180,12 @@ function normalizeUrl$1(rawUrl) {
1144
1180
  }
1145
1181
  function getState$2() {
1146
1182
  load$4();
1147
- return { highlights: [...state$4.highlights] };
1183
+ return { highlights: [...state$5.highlights] };
1148
1184
  }
1149
1185
  function getHighlightsForUrl(url) {
1150
1186
  load$4();
1151
1187
  const normalized = normalizeUrl$1(url);
1152
- return state$4.highlights.filter((h) => h.url === normalized);
1188
+ return state$5.highlights.filter((h) => h.url === normalized);
1153
1189
  }
1154
1190
  function addHighlight(url, selector, text, label, color, source) {
1155
1191
  load$4();
@@ -1163,45 +1199,45 @@ function addHighlight(url, selector, text, label, color, source) {
1163
1199
  source: source || void 0,
1164
1200
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1165
1201
  };
1166
- state$4.highlights.push(highlight);
1167
- save$2();
1168
- emit$2();
1202
+ state$5.highlights.push(highlight);
1203
+ save$3();
1204
+ emit$4();
1169
1205
  return highlight;
1170
1206
  }
1171
1207
  function removeHighlight(id) {
1172
1208
  load$4();
1173
- const index = state$4.highlights.findIndex((h) => h.id === id);
1209
+ const index = state$5.highlights.findIndex((h) => h.id === id);
1174
1210
  if (index === -1) return null;
1175
- const [removed] = state$4.highlights.splice(index, 1);
1176
- save$2();
1177
- emit$2();
1211
+ const [removed] = state$5.highlights.splice(index, 1);
1212
+ save$3();
1213
+ emit$4();
1178
1214
  return removed;
1179
1215
  }
1180
1216
  function findHighlightByText(url, text) {
1181
1217
  load$4();
1182
1218
  const normalized = normalizeUrl$1(url);
1183
- return state$4.highlights.find(
1219
+ return state$5.highlights.find(
1184
1220
  (h) => h.url === normalized && h.text && h.text === text
1185
1221
  ) ?? null;
1186
1222
  }
1187
1223
  function updateHighlightColor(id, color) {
1188
1224
  load$4();
1189
- const highlight = state$4.highlights.find((h) => h.id === id);
1225
+ const highlight = state$5.highlights.find((h) => h.id === id);
1190
1226
  if (!highlight) return null;
1191
1227
  highlight.color = color;
1192
- save$2();
1193
- emit$2();
1228
+ save$3();
1229
+ emit$4();
1194
1230
  return highlight;
1195
1231
  }
1196
1232
  function clearHighlightsForUrl(url) {
1197
1233
  load$4();
1198
1234
  const normalized = normalizeUrl$1(url);
1199
- const before = state$4.highlights.length;
1200
- state$4.highlights = state$4.highlights.filter((h) => h.url !== normalized);
1201
- const removed = before - state$4.highlights.length;
1235
+ const before = state$5.highlights.length;
1236
+ state$5.highlights = state$5.highlights.filter((h) => h.url !== normalized);
1237
+ const removed = before - state$5.highlights.length;
1202
1238
  if (removed > 0) {
1203
- save$2();
1204
- emit$2();
1239
+ save$3();
1240
+ emit$4();
1205
1241
  }
1206
1242
  return removed;
1207
1243
  }
@@ -1831,14 +1867,14 @@ function persistHighlight(url, text) {
1831
1867
  }
1832
1868
  const MAX_HISTORY_ENTRIES = 5e3;
1833
1869
  const SAVE_DEBOUNCE_MS$4 = 250;
1834
- let state$3 = null;
1870
+ let state$4 = null;
1835
1871
  const listeners$1 = /* @__PURE__ */ new Set();
1836
1872
  function getHistoryPath() {
1837
1873
  return path.join(electron.app.getPath("userData"), "vessel-history.json");
1838
1874
  }
1839
1875
  function load$3() {
1840
- if (state$3) return state$3;
1841
- state$3 = loadJsonFile({
1876
+ if (state$4) return state$4;
1877
+ state$4 = loadJsonFile({
1842
1878
  filePath: getHistoryPath(),
1843
1879
  fallback: { entries: [] },
1844
1880
  parse: (raw) => {
@@ -1848,27 +1884,27 @@ function load$3() {
1848
1884
  };
1849
1885
  }
1850
1886
  });
1851
- return state$3;
1887
+ return state$4;
1852
1888
  }
1853
- const persistence$4 = createDebouncedJsonPersistence({
1889
+ const persistence$6 = createDebouncedJsonPersistence({
1854
1890
  debounceMs: SAVE_DEBOUNCE_MS$4,
1855
1891
  filePath: getHistoryPath(),
1856
- getValue: () => state$3,
1892
+ getValue: () => state$4,
1857
1893
  logLabel: "history"
1858
1894
  });
1859
- function save$1() {
1860
- persistence$4.schedule();
1895
+ function save$2() {
1896
+ persistence$6.schedule();
1861
1897
  }
1862
- function emit$1() {
1863
- if (!state$3) return;
1864
- const snapshot = { entries: [...state$3.entries] };
1898
+ function emit$3() {
1899
+ if (!state$4) return;
1900
+ const snapshot = { entries: [...state$4.entries] };
1865
1901
  for (const listener of listeners$1) {
1866
1902
  listener(snapshot);
1867
1903
  }
1868
1904
  }
1869
1905
  function getState$1() {
1870
1906
  load$3();
1871
- return { entries: [...state$3.entries] };
1907
+ return { entries: [...state$4.entries] };
1872
1908
  }
1873
1909
  function subscribe$1(listener) {
1874
1910
  listeners$1.add(listener);
@@ -1879,12 +1915,12 @@ function subscribe$1(listener) {
1879
1915
  function addEntry$1(url, title) {
1880
1916
  if (!url || url === "about:blank") return;
1881
1917
  load$3();
1882
- const last = state$3.entries[0];
1918
+ const last = state$4.entries[0];
1883
1919
  if (last && last.url === url) {
1884
1920
  if (title && title !== last.title) {
1885
1921
  last.title = title;
1886
- save$1();
1887
- emit$1();
1922
+ save$2();
1923
+ emit$3();
1888
1924
  }
1889
1925
  return;
1890
1926
  }
@@ -1893,25 +1929,25 @@ function addEntry$1(url, title) {
1893
1929
  title: title || url,
1894
1930
  visitedAt: (/* @__PURE__ */ new Date()).toISOString()
1895
1931
  };
1896
- state$3.entries.unshift(entry);
1897
- if (state$3.entries.length > MAX_HISTORY_ENTRIES) {
1898
- state$3.entries = state$3.entries.slice(0, MAX_HISTORY_ENTRIES);
1932
+ state$4.entries.unshift(entry);
1933
+ if (state$4.entries.length > MAX_HISTORY_ENTRIES) {
1934
+ state$4.entries = state$4.entries.slice(0, MAX_HISTORY_ENTRIES);
1899
1935
  }
1900
- save$1();
1901
- emit$1();
1936
+ save$2();
1937
+ emit$3();
1902
1938
  }
1903
1939
  function search(query, limit = 50) {
1904
1940
  load$3();
1905
- if (!query.trim()) return state$3.entries.slice(0, limit);
1941
+ if (!query.trim()) return state$4.entries.slice(0, limit);
1906
1942
  const normalized = query.toLowerCase();
1907
- return state$3.entries.filter(
1943
+ return state$4.entries.filter(
1908
1944
  (e) => e.url.toLowerCase().includes(normalized) || e.title.toLowerCase().includes(normalized)
1909
1945
  ).slice(0, limit);
1910
1946
  }
1911
1947
  function clearAll$1() {
1912
- state$3 = { entries: [] };
1913
- save$1();
1914
- emit$1();
1948
+ state$4 = { entries: [] };
1949
+ save$2();
1950
+ emit$3();
1915
1951
  }
1916
1952
  function clearByTimeRange(timeRange) {
1917
1953
  load$3();
@@ -1921,12 +1957,12 @@ function clearByTimeRange(timeRange) {
1921
1957
  }
1922
1958
  const now = Date.now();
1923
1959
  const cutoff = new Date(now - timeRangeToMs(timeRange));
1924
- state$3.entries = state$3.entries.filter((entry) => {
1960
+ state$4.entries = state$4.entries.filter((entry) => {
1925
1961
  const visitedAt = new Date(entry.visitedAt).getTime();
1926
1962
  return Number.isNaN(visitedAt) || visitedAt < cutoff.getTime();
1927
1963
  });
1928
- save$1();
1929
- emit$1();
1964
+ save$2();
1965
+ emit$3();
1930
1966
  }
1931
1967
  function timeRangeToMs(range) {
1932
1968
  switch (range) {
@@ -1980,7 +2016,7 @@ function importHistoryFromJson(content) {
1980
2016
  const parsed = JSON.parse(content);
1981
2017
  const entries = Array.isArray(parsed?.entries) ? parsed.entries : [];
1982
2018
  load$3();
1983
- const existingUrls = new Set(state$3.entries.map((e) => e.url));
2019
+ const existingUrls = new Set(state$4.entries.map((e) => e.url));
1984
2020
  for (const entry of entries) {
1985
2021
  if (!entry?.url || typeof entry.url !== "string") {
1986
2022
  errors++;
@@ -1990,7 +2026,7 @@ function importHistoryFromJson(content) {
1990
2026
  skipped++;
1991
2027
  continue;
1992
2028
  }
1993
- state$3.entries.push({
2029
+ state$4.entries.push({
1994
2030
  url: entry.url,
1995
2031
  title: typeof entry.title === "string" ? entry.title : entry.url,
1996
2032
  visitedAt: typeof entry.visitedAt === "string" ? entry.visitedAt : (/* @__PURE__ */ new Date()).toISOString()
@@ -1998,14 +2034,14 @@ function importHistoryFromJson(content) {
1998
2034
  existingUrls.add(entry.url);
1999
2035
  imported++;
2000
2036
  }
2001
- state$3.entries.sort(
2037
+ state$4.entries.sort(
2002
2038
  (a, b) => new Date(b.visitedAt).getTime() - new Date(a.visitedAt).getTime()
2003
2039
  );
2004
- if (state$3.entries.length > MAX_HISTORY_ENTRIES) {
2005
- state$3.entries = state$3.entries.slice(0, MAX_HISTORY_ENTRIES);
2040
+ if (state$4.entries.length > MAX_HISTORY_ENTRIES) {
2041
+ state$4.entries = state$4.entries.slice(0, MAX_HISTORY_ENTRIES);
2006
2042
  }
2007
- save$1();
2008
- emit$1();
2043
+ save$2();
2044
+ emit$3();
2009
2045
  } catch {
2010
2046
  errors++;
2011
2047
  }
@@ -2016,7 +2052,7 @@ function importHistoryFromHtml(content) {
2016
2052
  let skipped = 0;
2017
2053
  let errors = 0;
2018
2054
  load$3();
2019
- const existingUrls = new Set(state$3.entries.map((e) => e.url));
2055
+ const existingUrls = new Set(state$4.entries.map((e) => e.url));
2020
2056
  const hrefRegex = /<A\s+[^>]*HREF="([^"]+)"[^>]*>([^<]*)<\/A>/gi;
2021
2057
  let match;
2022
2058
  while ((match = hrefRegex.exec(content)) !== null) {
@@ -2027,7 +2063,7 @@ function importHistoryFromHtml(content) {
2027
2063
  else errors++;
2028
2064
  continue;
2029
2065
  }
2030
- state$3.entries.push({
2066
+ state$4.entries.push({
2031
2067
  url,
2032
2068
  title,
2033
2069
  visitedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -2035,18 +2071,18 @@ function importHistoryFromHtml(content) {
2035
2071
  existingUrls.add(url);
2036
2072
  imported++;
2037
2073
  }
2038
- state$3.entries.sort(
2074
+ state$4.entries.sort(
2039
2075
  (a, b) => new Date(b.visitedAt).getTime() - new Date(a.visitedAt).getTime()
2040
2076
  );
2041
- if (state$3.entries.length > MAX_HISTORY_ENTRIES) {
2042
- state$3.entries = state$3.entries.slice(0, MAX_HISTORY_ENTRIES);
2077
+ if (state$4.entries.length > MAX_HISTORY_ENTRIES) {
2078
+ state$4.entries = state$4.entries.slice(0, MAX_HISTORY_ENTRIES);
2043
2079
  }
2044
- save$1();
2045
- emit$1();
2080
+ save$2();
2081
+ emit$3();
2046
2082
  return { imported, skipped, errors };
2047
2083
  }
2048
2084
  function flushPersist$3() {
2049
- return persistence$4.flush();
2085
+ return persistence$6.flush();
2050
2086
  }
2051
2087
  const MAX_CONSOLE_ENTRIES = 500;
2052
2088
  const MAX_NETWORK_ENTRIES = 200;
@@ -2403,18 +2439,18 @@ class DevToolsSession {
2403
2439
  if (result?.__error) throw new Error(result.__error);
2404
2440
  return { type, origin, entries: result ?? {} };
2405
2441
  }
2406
- async setStorage(type, key, value) {
2442
+ async setStorage(type, key2, value) {
2407
2443
  const storageType = type === "localStorage" ? "localStorage" : "sessionStorage";
2408
2444
  if (value === null) {
2409
2445
  await this.wc.executeJavaScript(
2410
- `window.${storageType}.removeItem(${JSON.stringify(key)})`
2446
+ `window.${storageType}.removeItem(${JSON.stringify(key2)})`
2411
2447
  );
2412
- return `Removed "${key}" from ${type}`;
2448
+ return `Removed "${key2}" from ${type}`;
2413
2449
  }
2414
2450
  await this.wc.executeJavaScript(
2415
- `window.${storageType}.setItem(${JSON.stringify(key)}, ${JSON.stringify(value)})`
2451
+ `window.${storageType}.setItem(${JSON.stringify(key2)}, ${JSON.stringify(value)})`
2416
2452
  );
2417
- return `Set ${type}["${key}"] = ${value.length > 80 ? value.slice(0, 77) + "..." : value}`;
2453
+ return `Set ${type}["${key2}"] = ${value.length > 80 ? value.slice(0, 77) + "..." : value}`;
2418
2454
  }
2419
2455
  // ---------------------------------------------------------------------------
2420
2456
  // Performance
@@ -2746,7 +2782,7 @@ function destroySession(tabId) {
2746
2782
  sessions.delete(tabId);
2747
2783
  }
2748
2784
  }
2749
- const logger$h = createLogger("TabManager");
2785
+ const logger$k = createLogger("TabManager");
2750
2786
  function sanitizePdfFilename(title) {
2751
2787
  const clean = title.replace(/[<>:"/\\|?*\x00-\x1f]/g, " ").replace(/\s+/g, " ").trim();
2752
2788
  const base = (clean || "Vessel Page").replace(/\.pdf$/i, "");
@@ -2979,23 +3015,23 @@ class TabManager {
2979
3015
  async saveTabAsPdf(id) {
2980
3016
  const tab = this.tabs.get(id);
2981
3017
  if (!tab) return null;
2982
- const { canceled, filePath } = await electron.dialog.showSaveDialog({
3018
+ const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
2983
3019
  title: "Save Page as PDF",
2984
3020
  defaultPath: sanitizePdfFilename(tab.state.title || "Vessel Page"),
2985
3021
  filters: [{ name: "PDF", extensions: ["pdf"] }]
2986
3022
  });
2987
- if (canceled || !filePath) return null;
3023
+ if (canceled || !filePath2) return null;
2988
3024
  const data = await tab.view.webContents.printToPDF({
2989
3025
  printBackground: true
2990
3026
  });
2991
- await fs.promises.writeFile(filePath, data);
2992
- return filePath;
3027
+ await fs.promises.writeFile(filePath2, data);
3028
+ return filePath2;
2993
3029
  }
2994
3030
  async savePage(id, format = "MHTML") {
2995
3031
  const tab = this.tabs.get(id);
2996
3032
  if (!tab) return null;
2997
3033
  const ext = format === "MHTML" ? "mhtml" : "html";
2998
- const { canceled, filePath } = await electron.dialog.showSaveDialog({
3034
+ const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
2999
3035
  title: "Save Page As",
3000
3036
  defaultPath: sanitizePageFilename(
3001
3037
  tab.state.title || "Vessel Page",
@@ -3005,9 +3041,9 @@ class TabManager {
3005
3041
  { name: format === "MHTML" ? "MHTML" : "HTML", extensions: [ext] }
3006
3042
  ]
3007
3043
  });
3008
- if (canceled || !filePath) return null;
3009
- await tab.view.webContents.savePage(filePath, format);
3010
- return filePath;
3044
+ if (canceled || !filePath2) return null;
3045
+ await tab.view.webContents.savePage(filePath2, format);
3046
+ return filePath2;
3011
3047
  }
3012
3048
  getActiveTab() {
3013
3049
  return this.activeTabId ? this.tabs.get(this.activeTabId) : void 0;
@@ -3091,11 +3127,11 @@ class TabManager {
3091
3127
  this.pinTab(ids[index]);
3092
3128
  }
3093
3129
  if (tab.groupName && ids[index]) {
3094
- const key = `${tab.groupName}|${tab.groupColor ?? "blue"}`;
3095
- let groupId = restoredGroups.get(key);
3130
+ const key2 = `${tab.groupName}|${tab.groupColor ?? "blue"}`;
3131
+ let groupId = restoredGroups.get(key2);
3096
3132
  if (!groupId) {
3097
3133
  groupId = crypto$1.randomUUID();
3098
- restoredGroups.set(key, groupId);
3134
+ restoredGroups.set(key2, groupId);
3099
3135
  this.tabGroups.set(groupId, {
3100
3136
  id: groupId,
3101
3137
  name: tab.groupName,
@@ -3145,7 +3181,7 @@ class TabManager {
3145
3181
  }));
3146
3182
  if (entries.length > 0) {
3147
3183
  void highlightBatchOnPage(wc, entries).catch(
3148
- (err) => logger$h.warn("Failed to batch highlight:", err)
3184
+ (err) => logger$k.warn("Failed to batch highlight:", err)
3149
3185
  );
3150
3186
  }
3151
3187
  }
@@ -3167,12 +3203,12 @@ class TabManager {
3167
3203
  const result = await captureSelectionHighlight(wc);
3168
3204
  if (result.success && result.text) {
3169
3205
  await highlightOnPage(wc, null, result.text, void 0, void 0, "yellow").catch(
3170
- (err) => logger$h.warn("Failed to capture highlight:", err)
3206
+ (err) => logger$k.warn("Failed to capture highlight:", err)
3171
3207
  );
3172
3208
  }
3173
3209
  this.highlightCaptureCallback?.(result);
3174
3210
  } catch (err) {
3175
- logger$h.warn("Failed to capture highlight from page:", err);
3211
+ logger$k.warn("Failed to capture highlight from page:", err);
3176
3212
  this.highlightCaptureCallback?.({
3177
3213
  success: false,
3178
3214
  message: "Could not capture selection"
@@ -3197,7 +3233,7 @@ class TabManager {
3197
3233
  void this.removeHighlightMarksForText(wc, text);
3198
3234
  }
3199
3235
  } catch (err) {
3200
- logger$h.warn("Failed to remove highlight from matching tab:", err);
3236
+ logger$k.warn("Failed to remove highlight from matching tab:", err);
3201
3237
  }
3202
3238
  }
3203
3239
  this.highlightCaptureCallback?.({
@@ -3228,12 +3264,12 @@ class TabManager {
3228
3264
  void 0,
3229
3265
  color
3230
3266
  ).catch(
3231
- (err) => logger$h.warn("Failed to update highlight color:", err)
3267
+ (err) => logger$k.warn("Failed to update highlight color:", err)
3232
3268
  );
3233
3269
  });
3234
3270
  }
3235
3271
  } catch (err) {
3236
- logger$h.warn("Failed to iterate highlights for color change:", err);
3272
+ logger$k.warn("Failed to iterate highlights for color change:", err);
3237
3273
  }
3238
3274
  }
3239
3275
  this.highlightCaptureCallback?.({
@@ -3274,7 +3310,7 @@ class TabManager {
3274
3310
  });
3275
3311
  })()`
3276
3312
  ).catch(
3277
- (err) => logger$h.warn("Failed to remove highlight marks:", err)
3313
+ (err) => logger$k.warn("Failed to remove highlight marks:", err)
3278
3314
  );
3279
3315
  }
3280
3316
  broadcastState() {
@@ -3420,6 +3456,11 @@ const Channels = {
3420
3456
  DOWNLOAD_STARTED: "download:started",
3421
3457
  DOWNLOAD_PROGRESS: "download:progress",
3422
3458
  DOWNLOAD_DONE: "download:done",
3459
+ DOWNLOADS_GET: "downloads:get",
3460
+ DOWNLOADS_CLEAR: "downloads:clear",
3461
+ DOWNLOADS_OPEN: "downloads:open",
3462
+ DOWNLOADS_SHOW_IN_FOLDER: "downloads:show-in-folder",
3463
+ DOWNLOADS_UPDATE: "downloads:update",
3423
3464
  // Premium
3424
3465
  PREMIUM_GET_STATE: "premium:get-state",
3425
3466
  PREMIUM_ACTIVATION_START: "premium:activation-start",
@@ -3477,7 +3518,19 @@ const Channels = {
3477
3518
  CLEAR_BROWSING_DATA: "browsing-data:clear",
3478
3519
  CLEAR_BROWSING_DATA_OPEN: "browsing-data:open",
3479
3520
  // Picture-in-Picture
3480
- TAB_TOGGLE_PIP: "tab:toggle-pip"
3521
+ TAB_TOGGLE_PIP: "tab:toggle-pip",
3522
+ // Codex OAuth
3523
+ CODEX_START_AUTH: "codex:start-auth",
3524
+ CODEX_CANCEL_AUTH: "codex:cancel-auth",
3525
+ CODEX_AUTH_STATUS: "codex:auth-status",
3526
+ CODEX_DISCONNECT: "codex:disconnect",
3527
+ // Updates
3528
+ UPDATES_CHECK: "updates:check",
3529
+ UPDATES_OPEN_DOWNLOAD: "updates:open-download",
3530
+ // Permissions
3531
+ PERMISSIONS_GET: "permissions:get",
3532
+ PERMISSIONS_CLEAR: "permissions:clear",
3533
+ PERMISSIONS_CLEAR_ORIGIN: "permissions:clear-origin"
3481
3534
  };
3482
3535
  const MAX_DETAIL_ITEMS = 3;
3483
3536
  const MIN_BLOCK_SIMILARITY = 0.82;
@@ -3719,25 +3772,25 @@ function normalizeQueryValue(value) {
3719
3772
  }
3720
3773
  function serializeSnapshotParams(params) {
3721
3774
  return params.map(
3722
- ([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
3775
+ ([key2, value]) => `${encodeURIComponent(key2)}=${encodeURIComponent(value)}`
3723
3776
  ).join("&");
3724
3777
  }
3725
3778
  function normalizeSnapshotParams(entries, pathname) {
3726
3779
  return Array.from(entries).filter(
3727
- ([key, value]) => shouldKeepSnapshotQueryParam(pathname, key, value)
3728
- ).map(([key, value]) => [
3729
- key.trim().toLowerCase(),
3780
+ ([key2, value]) => shouldKeepSnapshotQueryParam(pathname, key2, value)
3781
+ ).map(([key2, value]) => [
3782
+ key2.trim().toLowerCase(),
3730
3783
  normalizeQueryValue(value)
3731
3784
  ]).sort(
3732
3785
  ([keyA, valueA], [keyB, valueB]) => keyA === keyB ? valueA.localeCompare(valueB) : keyA.localeCompare(keyB)
3733
3786
  );
3734
3787
  }
3735
3788
  function shouldKeepSnapshotQueryParam(pathname, rawKey, value) {
3736
- const key = rawKey.trim().toLowerCase();
3737
- if (!key || !value.trim()) return false;
3738
- if (key.startsWith("utm_")) return false;
3739
- if (TRACKING_QUERY_KEYS.has(key)) return false;
3740
- if (SNAPSHOT_QUERY_KEYS.has(key)) return true;
3789
+ const key2 = rawKey.trim().toLowerCase();
3790
+ if (!key2 || !value.trim()) return false;
3791
+ if (key2.startsWith("utm_")) return false;
3792
+ if (TRACKING_QUERY_KEYS.has(key2)) return false;
3793
+ if (SNAPSHOT_QUERY_KEYS.has(key2)) return true;
3741
3794
  return /\/(search|results|browse|discover|find|category|tag|topics?|collections?|list)(\/|$)/i.test(
3742
3795
  pathname
3743
3796
  );
@@ -3830,7 +3883,7 @@ function load$2() {
3830
3883
  });
3831
3884
  return snapshots;
3832
3885
  }
3833
- const persistence$3 = createDebouncedJsonPersistence({
3886
+ const persistence$5 = createDebouncedJsonPersistence({
3834
3887
  debounceMs: SAVE_DEBOUNCE_MS$3,
3835
3888
  filePath: getFilePath$1(),
3836
3889
  getValue: () => snapshots,
@@ -3849,21 +3902,21 @@ function getSnapshot(normalizedUrl) {
3849
3902
  }
3850
3903
  function saveSnapshot(rawUrl, title, textContent, headings) {
3851
3904
  const s = load$2();
3852
- const key = normalizeUrl(rawUrl);
3905
+ const key2 = normalizeUrl(rawUrl);
3853
3906
  const snapshot = {
3854
- url: key,
3907
+ url: key2,
3855
3908
  title,
3856
3909
  textContent: textContent.slice(0, MAX_TEXT_LENGTH),
3857
3910
  headings: headings.map((h) => `${"#".repeat(h.level)} ${h.text}`).join("\n"),
3858
3911
  capturedAt: (/* @__PURE__ */ new Date()).toISOString()
3859
3912
  };
3860
- s.delete(key);
3861
- s.set(key, snapshot);
3862
- persistence$3.schedule();
3913
+ s.delete(key2);
3914
+ s.set(key2, snapshot);
3915
+ persistence$5.schedule();
3863
3916
  return snapshot;
3864
3917
  }
3865
3918
  function flushPersist$2() {
3866
- return persistence$3.flush();
3919
+ return persistence$5.flush();
3867
3920
  }
3868
3921
  const SEARCH_ENGINE_HOSTS = [
3869
3922
  "google.",
@@ -4178,11 +4231,11 @@ function sanitizeValue(value, depth = 0) {
4178
4231
  const record = asRecord(value);
4179
4232
  if (!record || depth >= MAX_DEPTH) return void 0;
4180
4233
  const objectValue = {};
4181
- for (const [key, entry] of Object.entries(record)) {
4182
- if (SKIP_FIELDS.has(key)) continue;
4234
+ for (const [key2, entry] of Object.entries(record)) {
4235
+ if (SKIP_FIELDS.has(key2)) continue;
4183
4236
  const normalized = sanitizeValue(entry, depth + 1);
4184
4237
  if (normalized !== void 0) {
4185
- objectValue[key] = normalized;
4238
+ objectValue[key2] = normalized;
4186
4239
  }
4187
4240
  }
4188
4241
  return Object.keys(objectValue).length > 0 ? objectValue : void 0;
@@ -4190,10 +4243,10 @@ function sanitizeValue(value, depth = 0) {
4190
4243
  function buildNormalizedAttributes(record, types) {
4191
4244
  const attributes = {};
4192
4245
  const consumed = /* @__PURE__ */ new Set();
4193
- const consume = (key, value) => {
4246
+ const consume = (key2, value) => {
4194
4247
  if (value === void 0) return;
4195
- consumed.add(key);
4196
- attributes[key] = value;
4248
+ consumed.add(key2);
4249
+ attributes[key2] = value;
4197
4250
  };
4198
4251
  if (types.includes("Recipe")) {
4199
4252
  consume("yield", sanitizeValue(record.recipeYield));
@@ -4237,14 +4290,14 @@ function buildNormalizedAttributes(record, types) {
4237
4290
  attributes.questions = questions;
4238
4291
  }
4239
4292
  }
4240
- for (const [key, value] of Object.entries(record)) {
4241
- if (SKIP_FIELDS.has(key) || consumed.has(key)) continue;
4242
- if (key === "name" || key === "headline" || key === "url" || key === "description") {
4293
+ for (const [key2, value] of Object.entries(record)) {
4294
+ if (SKIP_FIELDS.has(key2) || consumed.has(key2)) continue;
4295
+ if (key2 === "name" || key2 === "headline" || key2 === "url" || key2 === "description") {
4243
4296
  continue;
4244
4297
  }
4245
4298
  const normalized = sanitizeValue(value);
4246
4299
  if (normalized !== void 0) {
4247
- attributes[key] = normalized;
4300
+ attributes[key2] = normalized;
4248
4301
  }
4249
4302
  }
4250
4303
  return attributes;
@@ -4264,7 +4317,7 @@ function collectCandidateEntities(value, results = [], seen = /* @__PURE__ */ ne
4264
4317
  if (types.length > 0 || hasIdentity) {
4265
4318
  results.push(record);
4266
4319
  }
4267
- for (const key of [
4320
+ for (const key2 of [
4268
4321
  "@graph",
4269
4322
  "mainEntity",
4270
4323
  "mainEntityOfPage",
@@ -4277,7 +4330,7 @@ function collectCandidateEntities(value, results = [], seen = /* @__PURE__ */ ne
4277
4330
  "acceptedAnswer",
4278
4331
  "suggestedAnswer"
4279
4332
  ]) {
4280
- collectCandidateEntities(record[key], results, seen);
4333
+ collectCandidateEntities(record[key2], results, seen);
4281
4334
  }
4282
4335
  return results;
4283
4336
  }
@@ -4290,11 +4343,11 @@ function dedupeKey(entity) {
4290
4343
  JSON.stringify(entity.attributes)
4291
4344
  ].join("::");
4292
4345
  }
4293
- function extractEntitiesFromRecords(records, source) {
4294
- if (!records || records.length === 0) return [];
4346
+ function extractEntitiesFromRecords(records2, source) {
4347
+ if (!records2 || records2.length === 0) return [];
4295
4348
  const entities = [];
4296
4349
  const seen = /* @__PURE__ */ new Set();
4297
- for (const candidate of collectCandidateEntities(records)) {
4350
+ for (const candidate of collectCandidateEntities(records2)) {
4298
4351
  const types = getTypes(candidate);
4299
4352
  const name = firstString(candidate.name, candidate.headline);
4300
4353
  const url = firstString(candidate.url, candidate["@id"]);
@@ -4314,9 +4367,9 @@ function extractEntitiesFromRecords(records, source) {
4314
4367
  addIfPresent(entity, "name", name);
4315
4368
  addIfPresent(entity, "url", url);
4316
4369
  addIfPresent(entity, "description", description);
4317
- const key = dedupeKey(entity);
4318
- if (seen.has(key)) continue;
4319
- seen.add(key);
4370
+ const key2 = dedupeKey(entity);
4371
+ if (seen.has(key2)) continue;
4372
+ seen.add(key2);
4320
4373
  entities.push(entity);
4321
4374
  }
4322
4375
  return entities.slice(0, 25);
@@ -4339,13 +4392,13 @@ function extractEntityFromMetaTags(metaTags, pageTitle, pageUrl) {
4339
4392
  const url = metaTags["og:url"] || metaTags.canonical || pageUrl;
4340
4393
  const types = getMetaType(metaTags);
4341
4394
  const attributes = {};
4342
- for (const [key, value] of Object.entries(metaTags)) {
4343
- if (key === "og:title" || key === "twitter:title" || key === "title" || key === "og:description" || key === "description" || key === "twitter:description" || key === "og:url" || key === "canonical") {
4395
+ for (const [key2, value] of Object.entries(metaTags)) {
4396
+ if (key2 === "og:title" || key2 === "twitter:title" || key2 === "title" || key2 === "og:description" || key2 === "description" || key2 === "twitter:description" || key2 === "og:url" || key2 === "canonical") {
4344
4397
  continue;
4345
4398
  }
4346
4399
  const normalized = sanitizeValue(value);
4347
4400
  if (normalized !== void 0) {
4348
- attributes[key] = normalized;
4401
+ attributes[key2] = normalized;
4349
4402
  }
4350
4403
  }
4351
4404
  const entity = {
@@ -4398,9 +4451,9 @@ function extractStructuredDataFromJsonLd(jsonLd, microdata, rdfa, metaTags, page
4398
4451
  const deduped = [];
4399
4452
  const seen = /* @__PURE__ */ new Set();
4400
4453
  for (const entity of candidates) {
4401
- const key = dedupeKey(entity);
4402
- if (seen.has(key)) continue;
4403
- seen.add(key);
4454
+ const key2 = dedupeKey(entity);
4455
+ if (seen.has(key2)) continue;
4456
+ seen.add(key2);
4404
4457
  deduped.push(entity);
4405
4458
  }
4406
4459
  if (deduped.length > 0) {
@@ -4414,9 +4467,9 @@ function extractStructuredDataFromJsonLd(jsonLd, microdata, rdfa, metaTags, page
4414
4467
  pageHeadings
4415
4468
  );
4416
4469
  }
4417
- function addIfPresent(target, key, value) {
4470
+ function addIfPresent(target, key2, value) {
4418
4471
  if (value !== void 0) {
4419
- target[key] = value;
4472
+ target[key2] = value;
4420
4473
  }
4421
4474
  }
4422
4475
  function okResult(value) {
@@ -4435,7 +4488,7 @@ function errorResult(error, value) {
4435
4488
  function getErrorMessage(error, fallback = "Unknown error") {
4436
4489
  return error instanceof Error && error.message ? error.message : fallback;
4437
4490
  }
4438
- const logger$g = createLogger("Premium");
4491
+ const logger$j = createLogger("Premium");
4439
4492
  const VERIFICATION_API = process.env.VESSEL_PREMIUM_API || "https://vesselpremium.quantaintellect.com";
4440
4493
  const FREE_TOOL_ITERATION_LIMIT = 50;
4441
4494
  const REVALIDATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
@@ -4551,7 +4604,7 @@ async function verifySubscription$1(identifier) {
4551
4604
  });
4552
4605
  if (!res.ok) {
4553
4606
  const detail = await readApiErrorDetail(res);
4554
- logger$g.warn(
4607
+ logger$j.warn(
4555
4608
  "Verification API returned a non-OK status:",
4556
4609
  res.status,
4557
4610
  detail
@@ -4570,7 +4623,7 @@ async function verifySubscription$1(identifier) {
4570
4623
  setSetting("premium", updated);
4571
4624
  return updated;
4572
4625
  } catch (err) {
4573
- logger$g.warn("Verification failed:", err);
4626
+ logger$j.warn("Verification failed:", err);
4574
4627
  return current;
4575
4628
  }
4576
4629
  }
@@ -4729,8 +4782,8 @@ function trackProviderConfigured(providerId) {
4729
4782
  provider_id: providerId
4730
4783
  });
4731
4784
  }
4732
- function trackSettingChanged(key) {
4733
- trackEvent("setting_changed", { setting_key: key });
4785
+ function trackSettingChanged(key2) {
4786
+ trackEvent("setting_changed", { setting_key: key2 });
4734
4787
  }
4735
4788
  function trackApprovalModeChanged(mode) {
4736
4789
  trackEvent("approval_mode_changed", { mode });
@@ -4912,10 +4965,10 @@ function mapFormFields(forms, interactiveElements) {
4912
4965
  }
4913
4966
  }
4914
4967
  for (const el of interactiveElements) {
4915
- const key = el.selector || el.name || el.label || String(el.index);
4916
- if (formFieldSelectors.has(key)) {
4968
+ const key2 = el.selector || el.name || el.label || String(el.index);
4969
+ if (formFieldSelectors.has(key2)) {
4917
4970
  fields.push({
4918
- name: el.name || el.label || key,
4971
+ name: el.name || el.label || key2,
4919
4972
  type: mapInputType(el),
4920
4973
  label: el.label,
4921
4974
  required: el.required,
@@ -5143,7 +5196,7 @@ const EXTRACT_TIMEOUT_MAX_MS = 2e4;
5143
5196
  const MUTATION_CAPTURE_INTERVAL_MS = 5e3;
5144
5197
  const MUTATION_SETTLE_AFTER_MS = 1500;
5145
5198
  const AGENT_STREAM_IDLE_TIMEOUT_MS = 3e4;
5146
- const logger$f = createLogger("Extractor");
5199
+ const logger$i = createLogger("Extractor");
5147
5200
  const EMPTY_PAGE_CONTENT = {
5148
5201
  title: "",
5149
5202
  content: "",
@@ -5893,7 +5946,7 @@ async function executeScript(webContents, script) {
5893
5946
  })
5894
5947
  ]);
5895
5948
  } catch (err) {
5896
- logger$f.warn("Failed to execute page script:", err);
5949
+ logger$i.warn("Failed to execute page script:", err);
5897
5950
  return null;
5898
5951
  } finally {
5899
5952
  if (timer) {
@@ -6000,7 +6053,7 @@ async function estimateExtractionTimeout(webContents) {
6000
6053
  return EXTRACT_TIMEOUT_BASE_MS + extra;
6001
6054
  }
6002
6055
  } catch (err) {
6003
- logger$f.warn("Failed to estimate extraction timeout, using base timeout:", err);
6056
+ logger$i.warn("Failed to estimate extraction timeout, using base timeout:", err);
6004
6057
  }
6005
6058
  return EXTRACT_TIMEOUT_BASE_MS;
6006
6059
  }
@@ -6154,12 +6207,12 @@ function loadHistory() {
6154
6207
  return next;
6155
6208
  }
6156
6209
  });
6157
- for (const [key, bursts] of loaded.entries()) {
6158
- recentPageDiffBursts.set(key, bursts);
6210
+ for (const [key2, bursts] of loaded.entries()) {
6211
+ recentPageDiffBursts.set(key2, bursts);
6159
6212
  }
6160
6213
  return recentPageDiffBursts;
6161
6214
  }
6162
- const persistence$2 = createDebouncedJsonPersistence({
6215
+ const persistence$4 = createDebouncedJsonPersistence({
6163
6216
  debounceMs: SAVE_DEBOUNCE_MS$2,
6164
6217
  filePath: getHistoryFilePath(),
6165
6218
  getValue: () => recentPageDiffBursts,
@@ -6176,20 +6229,20 @@ function getLatestPageDiff(rawUrl) {
6176
6229
  }
6177
6230
  function getPageDiffBursts(rawUrl) {
6178
6231
  if (!shouldTrackSnapshotUrl(rawUrl)) return [];
6179
- const key = normalizeUrl(rawUrl);
6232
+ const key2 = normalizeUrl(rawUrl);
6180
6233
  const history = loadHistory();
6181
- const bursts = prunePageDiffHistory(history.get(key) ?? [], {
6234
+ const bursts = prunePageDiffHistory(history.get(key2) ?? [], {
6182
6235
  maxAgeDays: MAX_HISTORY_DAYS,
6183
6236
  maxItems: MAX_PERSISTED_DIFF_BURSTS
6184
6237
  });
6185
- const current = history.get(key) ?? [];
6238
+ const current = history.get(key2) ?? [];
6186
6239
  if (current.length !== bursts.length) {
6187
6240
  if (bursts.length > 0) {
6188
- history.set(key, bursts);
6241
+ history.set(key2, bursts);
6189
6242
  } else {
6190
- history.delete(key);
6243
+ history.delete(key2);
6191
6244
  }
6192
- persistence$2.schedule();
6245
+ persistence$4.schedule();
6193
6246
  }
6194
6247
  return bursts.slice().reverse();
6195
6248
  }
@@ -6197,20 +6250,20 @@ function summarizeDiffBurst(diff) {
6197
6250
  const items = diff.changes.slice(0, 2).map((change) => `${change.section}: ${change.summary}`);
6198
6251
  return items.join(" | ");
6199
6252
  }
6200
- function enrichWithBurstHistory(key, diff) {
6253
+ function enrichWithBurstHistory(key2, diff) {
6201
6254
  const detectedAt = (/* @__PURE__ */ new Date()).toISOString();
6202
6255
  const nextBurst = {
6203
6256
  detectedAt,
6204
6257
  summary: summarizeDiffBurst(diff)
6205
6258
  };
6206
6259
  const history = loadHistory();
6207
- const bursts = appendPageDiffHistoryItem(history.get(key) ?? [], nextBurst, {
6260
+ const bursts = appendPageDiffHistoryItem(history.get(key2) ?? [], nextBurst, {
6208
6261
  maxAgeDays: MAX_HISTORY_DAYS,
6209
6262
  maxItems: MAX_PERSISTED_DIFF_BURSTS,
6210
6263
  now: Date.parse(detectedAt)
6211
6264
  });
6212
- history.set(key, bursts);
6213
- persistence$2.schedule();
6265
+ history.set(key2, bursts);
6266
+ persistence$4.schedule();
6214
6267
  const recentBursts = bursts.slice(-5);
6215
6268
  return {
6216
6269
  ...diff,
@@ -6223,8 +6276,8 @@ function enrichWithBurstHistory(key, diff) {
6223
6276
  async function capturePageSnapshot(url, wc, sendToRendererViews) {
6224
6277
  try {
6225
6278
  if (!shouldTrackSnapshotUrl(url)) return;
6226
- const key = normalizeUrl(url);
6227
- const oldSnap = getSnapshot(key);
6279
+ const key2 = normalizeUrl(url);
6280
+ const oldSnap = getSnapshot(key2);
6228
6281
  const content = await extractContent(wc);
6229
6282
  const textContent = content.content || "";
6230
6283
  const title = content.title || "";
@@ -6233,14 +6286,14 @@ async function capturePageSnapshot(url, wc, sendToRendererViews) {
6233
6286
  if (oldSnap) {
6234
6287
  const diff = diffSnapshots(oldSnap, textContent, title, currentHeadings);
6235
6288
  if (diff.hasChanges) {
6236
- const enrichedDiff = enrichWithBurstHistory(key, diff);
6237
- latestPageDiffs.set(key, enrichedDiff);
6289
+ const enrichedDiff = enrichWithBurstHistory(key2, diff);
6290
+ latestPageDiffs.set(key2, enrichedDiff);
6238
6291
  sendToRendererViews(Channels.PAGE_CHANGED, enrichedDiff);
6239
6292
  } else {
6240
- latestPageDiffs.delete(key);
6293
+ latestPageDiffs.delete(key2);
6241
6294
  }
6242
6295
  } else {
6243
- latestPageDiffs.delete(key);
6296
+ latestPageDiffs.delete(key2);
6244
6297
  }
6245
6298
  saveSnapshot(url, title, textContent, headings);
6246
6299
  } catch {
@@ -6292,19 +6345,19 @@ function schedulePageSnapshotCapture(wc, sendToRendererViews, delayMs = 0) {
6292
6345
  function enableClipboardShortcuts(view) {
6293
6346
  view.webContents.on("before-input-event", (event, input) => {
6294
6347
  if (!input.control && !input.meta) return;
6295
- const key = input.key.toLowerCase();
6348
+ const key2 = input.key.toLowerCase();
6296
6349
  const wc = view.webContents;
6297
6350
  if (input.type === "keyDown") {
6298
- if (key === "c") {
6351
+ if (key2 === "c") {
6299
6352
  wc.copy();
6300
6353
  event.preventDefault();
6301
- } else if (key === "v") {
6354
+ } else if (key2 === "v") {
6302
6355
  wc.paste();
6303
6356
  event.preventDefault();
6304
- } else if (key === "x") {
6357
+ } else if (key2 === "x") {
6305
6358
  wc.cut();
6306
6359
  event.preventDefault();
6307
- } else if (key === "a") {
6360
+ } else if (key2 === "a") {
6308
6361
  wc.selectAll();
6309
6362
  event.preventDefault();
6310
6363
  }
@@ -6609,8 +6662,8 @@ function resizeSidebarViews(state2) {
6609
6662
  }
6610
6663
  }
6611
6664
  function generateReaderHTML(page) {
6612
- const escapedTitle = escapeHtml(page.title);
6613
- const escapedByline = escapeHtml(page.byline);
6665
+ const escapedTitle = escapeHtml$1(page.title);
6666
+ const escapedByline = escapeHtml$1(page.byline);
6614
6667
  const renderedContent = renderReaderContent(page);
6615
6668
  return `<!DOCTYPE html>
6616
6669
  <html lang="en">
@@ -6698,9 +6751,9 @@ function renderReaderContent(page) {
6698
6751
  if (!source) {
6699
6752
  return "<p>No readable content was available for this page.</p>";
6700
6753
  }
6701
- return source.split(/\n{2,}/).map((block) => block.trim()).filter(Boolean).map((block) => `<p>${escapeHtml(block).replace(/\n/g, "<br>")}</p>`).join("\n");
6754
+ return source.split(/\n{2,}/).map((block) => block.trim()).filter(Boolean).map((block) => `<p>${escapeHtml$1(block).replace(/\n/g, "<br>")}</p>`).join("\n");
6702
6755
  }
6703
- function escapeHtml(str) {
6756
+ function escapeHtml$1(str) {
6704
6757
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
6705
6758
  }
6706
6759
  const mcpStatusChangeListeners = /* @__PURE__ */ new Set();
@@ -6718,7 +6771,7 @@ function onRuntimeHealthChange(listener) {
6718
6771
  };
6719
6772
  }
6720
6773
  function getMcpStatus() {
6721
- return state$2.mcp.status;
6774
+ return state$3.mcp.status;
6722
6775
  }
6723
6776
  function emitRuntimeHealthChange() {
6724
6777
  const snapshot = getRuntimeHealth();
@@ -6726,7 +6779,7 @@ function emitRuntimeHealthChange() {
6726
6779
  listener(snapshot);
6727
6780
  }
6728
6781
  }
6729
- const state$2 = {
6782
+ const state$3 = {
6730
6783
  userDataPath: "",
6731
6784
  settingsPath: "",
6732
6785
  startupIssues: [],
@@ -6739,43 +6792,43 @@ const state$2 = {
6739
6792
  }
6740
6793
  };
6741
6794
  function initializeRuntimeHealth(paths) {
6742
- state$2.userDataPath = paths.userDataPath;
6743
- state$2.settingsPath = paths.settingsPath;
6744
- state$2.mcp.configuredPort = paths.configuredPort;
6745
- state$2.mcp.activePort = null;
6746
- state$2.mcp.endpoint = null;
6747
- state$2.mcp.status = "stopped";
6748
- state$2.mcp.message = "MCP server has not started yet.";
6795
+ state$3.userDataPath = paths.userDataPath;
6796
+ state$3.settingsPath = paths.settingsPath;
6797
+ state$3.mcp.configuredPort = paths.configuredPort;
6798
+ state$3.mcp.activePort = null;
6799
+ state$3.mcp.endpoint = null;
6800
+ state$3.mcp.status = "stopped";
6801
+ state$3.mcp.message = "MCP server has not started yet.";
6749
6802
  emitRuntimeHealthChange();
6750
6803
  }
6751
6804
  function setStartupIssues(issues) {
6752
- state$2.startupIssues = issues.map((issue) => ({ ...issue }));
6805
+ state$3.startupIssues = issues.map((issue) => ({ ...issue }));
6753
6806
  emitRuntimeHealthChange();
6754
6807
  }
6755
6808
  function getRuntimeHealth() {
6756
6809
  return {
6757
- userDataPath: state$2.userDataPath,
6758
- settingsPath: state$2.settingsPath,
6759
- startupIssues: state$2.startupIssues.map((issue) => ({ ...issue })),
6760
- mcp: { ...state$2.mcp }
6810
+ userDataPath: state$3.userDataPath,
6811
+ settingsPath: state$3.settingsPath,
6812
+ startupIssues: state$3.startupIssues.map((issue) => ({ ...issue })),
6813
+ mcp: { ...state$3.mcp }
6761
6814
  };
6762
6815
  }
6763
6816
  function setMcpHealth(update) {
6764
6817
  if (typeof update.configuredPort === "number") {
6765
- state$2.mcp.configuredPort = update.configuredPort;
6818
+ state$3.mcp.configuredPort = update.configuredPort;
6766
6819
  }
6767
6820
  if ("activePort" in update) {
6768
- state$2.mcp.activePort = update.activePort ?? null;
6821
+ state$3.mcp.activePort = update.activePort ?? null;
6769
6822
  }
6770
6823
  if ("endpoint" in update) {
6771
- state$2.mcp.endpoint = update.endpoint ?? null;
6824
+ state$3.mcp.endpoint = update.endpoint ?? null;
6772
6825
  }
6773
- const prevStatus = state$2.mcp.status;
6774
- state$2.mcp.status = update.status;
6775
- state$2.mcp.message = update.message;
6776
- if (prevStatus !== state$2.mcp.status) {
6826
+ const prevStatus = state$3.mcp.status;
6827
+ state$3.mcp.status = update.status;
6828
+ state$3.mcp.message = update.message;
6829
+ if (prevStatus !== state$3.mcp.status) {
6777
6830
  for (const listener of mcpStatusChangeListeners) {
6778
- listener(state$2.mcp.status);
6831
+ listener(state$3.mcp.status);
6779
6832
  }
6780
6833
  }
6781
6834
  emitRuntimeHealthChange();
@@ -7177,6 +7230,17 @@ const PROVIDERS = {
7177
7230
  apiKeyPlaceholder: "sk-...",
7178
7231
  apiKeyHint: "Get your key from platform.openai.com"
7179
7232
  },
7233
+ openai_codex: {
7234
+ id: "openai_codex",
7235
+ name: "OpenAI Codex",
7236
+ type: "codex_oauth",
7237
+ defaultModel: "gpt-5",
7238
+ models: ["gpt-5", "gpt-5-mini", "gpt-5-nano", "o4", "o4-mini"],
7239
+ requiresApiKey: false,
7240
+ defaultBaseUrl: "https://api.openai.com/v1",
7241
+ apiKeyPlaceholder: "",
7242
+ apiKeyHint: "Sign in with your ChatGPT Plus or Pro subscription"
7243
+ },
7180
7244
  openrouter: {
7181
7245
  id: "openrouter",
7182
7246
  name: "OpenRouter",
@@ -7348,7 +7412,7 @@ const MAX_MCP_NAV_CONTENT_LENGTH = 3e4;
7348
7412
  const MAX_AGENT_DEBUG_CONTENT_LENGTH = 2e4;
7349
7413
  const LLAMA_CPP_MIN_CTX_TOKENS = 16384;
7350
7414
  const LLAMA_CPP_RECOMMENDED_CTX_TOKENS = 32768;
7351
- const logger$e = createLogger("OpenAIProvider");
7415
+ const logger$h = createLogger("OpenAIProvider");
7352
7416
  function shouldDebugAgentLoop() {
7353
7417
  const value = process.env.VESSEL_DEBUG_AGENT_LOOP;
7354
7418
  return value === "1" || value === "true";
@@ -7676,8 +7740,8 @@ function scalarArgsForTool(name, scalar) {
7676
7740
  return null;
7677
7741
  }
7678
7742
  function firstStringArg(args, keys) {
7679
- for (const key of keys) {
7680
- const value = args[key];
7743
+ for (const key2 of keys) {
7744
+ const value = args[key2];
7681
7745
  if (typeof value === "string" && value.trim()) {
7682
7746
  return value.trim();
7683
7747
  }
@@ -7873,9 +7937,9 @@ function resolveToolCallName(rawName, args, availableToolNames) {
7873
7937
  function logAgentLoopDebug(payload) {
7874
7938
  if (!shouldDebugAgentLoop()) return;
7875
7939
  try {
7876
- logger$e.info(`[agent-debug] ${JSON.stringify(payload)}`);
7940
+ logger$h.info(`[agent-debug] ${JSON.stringify(payload)}`);
7877
7941
  } catch (err) {
7878
- logger$e.warn("Failed to serialize debug payload:", err);
7942
+ logger$h.warn("Failed to serialize debug payload:", err);
7879
7943
  }
7880
7944
  }
7881
7945
  function recoverTextEncodedToolCalls(text, availableToolNames) {
@@ -8409,6 +8473,525 @@ class OpenAICompatProvider {
8409
8473
  this.abortController?.abort();
8410
8474
  }
8411
8475
  }
8476
+ const logger$g = createLogger("CodexOAuth");
8477
+ const ISSUER = "https://auth.openai.com";
8478
+ const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
8479
+ const SCOPE = "openid profile email offline_access api.connectors.read api.connectors.invoke";
8480
+ const AUTH_TIMEOUT_MS = 5 * 60 * 1e3;
8481
+ const PREFERRED_PORT = 1455;
8482
+ const FALLBACK_PORT = 1457;
8483
+ let activeFlow = null;
8484
+ function base64url(buffer) {
8485
+ return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
8486
+ }
8487
+ function generatePkce() {
8488
+ const codeVerifier = base64url(crypto$1.randomBytes(64));
8489
+ const hash = crypto$1.createHash("sha256").update(codeVerifier).digest();
8490
+ const codeChallenge = base64url(hash);
8491
+ return { codeVerifier, codeChallenge };
8492
+ }
8493
+ function generateState() {
8494
+ return base64url(crypto$1.randomBytes(32));
8495
+ }
8496
+ function buildAuthorizeUrl(port, pkce, state2) {
8497
+ const redirectUri = `http://localhost:${port}/auth/callback`;
8498
+ const params = new URLSearchParams({
8499
+ response_type: "code",
8500
+ client_id: CLIENT_ID,
8501
+ redirect_uri: redirectUri,
8502
+ scope: SCOPE,
8503
+ code_challenge: pkce.codeChallenge,
8504
+ code_challenge_method: "S256",
8505
+ state: state2,
8506
+ id_token_add_organizations: "true",
8507
+ codex_cli_simplified_flow: "true",
8508
+ originator: "codex_cli_rs"
8509
+ });
8510
+ return `${ISSUER}/oauth/authorize?${params.toString()}`;
8511
+ }
8512
+ function parseJwtClaims(idToken) {
8513
+ try {
8514
+ const parts = idToken.split(".");
8515
+ if (parts.length !== 3) return null;
8516
+ const payload = JSON.parse(
8517
+ Buffer.from(parts[1], "base64url").toString("utf-8")
8518
+ );
8519
+ const authClaims = payload["https://api.openai.com/auth"] || {};
8520
+ const accountId = authClaims.chatgpt_account_id || payload.chatgpt_account_id || payload.sub || "";
8521
+ const email = authClaims.email || payload.email || void 0;
8522
+ return { accountId, email };
8523
+ } catch {
8524
+ return null;
8525
+ }
8526
+ }
8527
+ function parseTokenExpiry(accessToken) {
8528
+ try {
8529
+ const parts = accessToken.split(".");
8530
+ if (parts.length !== 3) return Date.now() + 36e5;
8531
+ const payload = JSON.parse(
8532
+ Buffer.from(parts[1], "base64url").toString("utf-8")
8533
+ );
8534
+ if (payload.exp && typeof payload.exp === "number") {
8535
+ return payload.exp * 1e3;
8536
+ }
8537
+ } catch {
8538
+ }
8539
+ return Date.now() + 36e5;
8540
+ }
8541
+ async function exchangeIdTokenForApiKey(idToken) {
8542
+ const body = new URLSearchParams({
8543
+ grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
8544
+ client_id: CLIENT_ID,
8545
+ requested_token: "openai-api-key",
8546
+ subject_token: idToken,
8547
+ subject_token_type: "urn:ietf:params:oauth:token-type:id_token"
8548
+ });
8549
+ const response = await fetch(`${ISSUER}/oauth/token`, {
8550
+ method: "POST",
8551
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
8552
+ body: body.toString()
8553
+ });
8554
+ if (!response.ok) {
8555
+ let errorMsg = `OpenAI API token exchange failed: ${response.status}`;
8556
+ try {
8557
+ const err = await response.json();
8558
+ if (typeof err.error_description === "string") {
8559
+ errorMsg = err.error_description;
8560
+ } else if (typeof err.error === "string") {
8561
+ errorMsg = err.error;
8562
+ }
8563
+ } catch {
8564
+ }
8565
+ throw new Error(errorMsg);
8566
+ }
8567
+ const data = await response.json();
8568
+ if (!data.access_token) {
8569
+ throw new Error("OpenAI API token exchange did not return an access token");
8570
+ }
8571
+ return data.access_token;
8572
+ }
8573
+ async function ensureCodexApiKey(tokens) {
8574
+ if (tokens.apiKey) return tokens;
8575
+ if (!tokens.idToken) return tokens;
8576
+ try {
8577
+ return {
8578
+ ...tokens,
8579
+ apiKey: await exchangeIdTokenForApiKey(tokens.idToken)
8580
+ };
8581
+ } catch (err) {
8582
+ logger$g.warn(
8583
+ "Codex API-key token exchange failed; continuing with ChatGPT OAuth tokens:",
8584
+ err
8585
+ );
8586
+ return tokens;
8587
+ }
8588
+ }
8589
+ async function exchangeCodeForTokens(code, redirectUri, codeVerifier) {
8590
+ const body = new URLSearchParams({
8591
+ grant_type: "authorization_code",
8592
+ code,
8593
+ redirect_uri: redirectUri,
8594
+ client_id: CLIENT_ID,
8595
+ code_verifier: codeVerifier
8596
+ });
8597
+ const response = await fetch(`${ISSUER}/oauth/token`, {
8598
+ method: "POST",
8599
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
8600
+ body: body.toString()
8601
+ });
8602
+ if (!response.ok) {
8603
+ let errorMsg = `Token exchange failed: ${response.status}`;
8604
+ try {
8605
+ const err = await response.json();
8606
+ if (typeof err.error_description === "string") {
8607
+ errorMsg = err.error_description;
8608
+ } else if (typeof err.error === "string") {
8609
+ errorMsg = err.error;
8610
+ }
8611
+ } catch {
8612
+ }
8613
+ throw new Error(errorMsg);
8614
+ }
8615
+ const data = await response.json();
8616
+ const claims = parseJwtClaims(data.id_token);
8617
+ const expiresAt = parseTokenExpiry(data.access_token);
8618
+ const tokens = {
8619
+ accessToken: data.access_token,
8620
+ refreshToken: data.refresh_token,
8621
+ idToken: data.id_token,
8622
+ expiresAt,
8623
+ accountId: claims?.accountId || "",
8624
+ accountEmail: claims?.email
8625
+ };
8626
+ return ensureCodexApiKey(tokens);
8627
+ }
8628
+ async function refreshAccessToken(tokens) {
8629
+ const body = new URLSearchParams({
8630
+ grant_type: "refresh_token",
8631
+ refresh_token: tokens.refreshToken,
8632
+ client_id: CLIENT_ID
8633
+ });
8634
+ const response = await fetch(`${ISSUER}/oauth/token`, {
8635
+ method: "POST",
8636
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
8637
+ body: body.toString()
8638
+ });
8639
+ if (!response.ok) {
8640
+ let errorMsg = `Token refresh failed: ${response.status}`;
8641
+ try {
8642
+ const err = await response.json();
8643
+ if (typeof err.error_description === "string") errorMsg = err.error_description;
8644
+ else if (typeof err.error === "string") errorMsg = err.error;
8645
+ } catch {
8646
+ }
8647
+ throw new Error(errorMsg);
8648
+ }
8649
+ const data = await response.json();
8650
+ const idToken = data.id_token || tokens.idToken || "";
8651
+ const claims = idToken ? parseJwtClaims(idToken) : null;
8652
+ const expiresAt = parseTokenExpiry(data.access_token);
8653
+ const refreshedTokens = {
8654
+ accessToken: data.access_token,
8655
+ refreshToken: data.refresh_token || tokens.refreshToken,
8656
+ idToken,
8657
+ apiKey: tokens.apiKey,
8658
+ expiresAt,
8659
+ accountId: claims?.accountId || tokens.accountId || "",
8660
+ accountEmail: claims?.email || tokens.accountEmail
8661
+ };
8662
+ return ensureCodexApiKey(refreshedTokens);
8663
+ }
8664
+ function startServer(port, pkce, expectedState, resolve, reject) {
8665
+ const server = http.createServer(async (req, res) => {
8666
+ const url = new URL(req.url || "/", `http://localhost:${port}`);
8667
+ if (url.pathname === "/auth/callback") {
8668
+ const state2 = url.searchParams.get("state");
8669
+ const code = url.searchParams.get("code");
8670
+ const error = url.searchParams.get("error");
8671
+ const errorDescription = url.searchParams.get("error_description");
8672
+ if (error) {
8673
+ res.writeHead(400, { "Content-Type": "text/plain", "Connection": "close" });
8674
+ const msg = errorDescription || error;
8675
+ res.end(`Authorization failed: ${msg}`);
8676
+ reject(new Error(msg));
8677
+ return;
8678
+ }
8679
+ if (state2 !== expectedState) {
8680
+ res.writeHead(400, { "Content-Type": "text/plain", "Connection": "close" });
8681
+ res.end("State mismatch. Please try again.");
8682
+ reject(new Error("State mismatch"));
8683
+ return;
8684
+ }
8685
+ if (!code) {
8686
+ res.writeHead(400, { "Content-Type": "text/plain", "Connection": "close" });
8687
+ res.end("Missing authorization code.");
8688
+ reject(new Error("Missing authorization code"));
8689
+ return;
8690
+ }
8691
+ try {
8692
+ activeFlow?.onStatus("exchanging");
8693
+ const redirectUri = `http://localhost:${activeFlow?.port ?? port}/auth/callback`;
8694
+ const tokens = await exchangeCodeForTokens(code, redirectUri, pkce.codeVerifier);
8695
+ res.writeHead(302, {
8696
+ Location: `/success?email=${encodeURIComponent(tokens.accountEmail || tokens.accountId)}`,
8697
+ Connection: "close"
8698
+ });
8699
+ res.end();
8700
+ resolve(tokens);
8701
+ } catch (err) {
8702
+ res.writeHead(400, { "Content-Type": "text/plain", "Connection": "close" });
8703
+ res.end(`Token exchange failed: ${err instanceof Error ? err.message : "Unknown error"}`);
8704
+ reject(err instanceof Error ? err : new Error("Token exchange failed"));
8705
+ }
8706
+ return;
8707
+ }
8708
+ if (url.pathname === "/success") {
8709
+ const email = url.searchParams.get("email") || "";
8710
+ res.writeHead(200, { "Content-Type": "text/html", "Connection": "close" });
8711
+ res.end(`<!DOCTYPE html>
8712
+ <html><head><meta charset="utf-8"><title>Vessel — Signed In</title>
8713
+ <style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#111;color:#eee}</style></head>
8714
+ <body><div style="text-align:center"><h1>✓ Signed In</h1>
8715
+ <p>Connected as ${escapeHtml(email)}</p><p>You can close this tab.</p></div></body></html>`);
8716
+ return;
8717
+ }
8718
+ if (url.pathname === "/cancel") {
8719
+ res.writeHead(200, { "Content-Type": "text/plain", "Connection": "close" });
8720
+ res.end("Login cancelled");
8721
+ reject(new Error("Login cancelled by user"));
8722
+ return;
8723
+ }
8724
+ res.writeHead(404, { "Connection": "close" });
8725
+ res.end("Not found");
8726
+ });
8727
+ return server;
8728
+ }
8729
+ function escapeHtml(text) {
8730
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
8731
+ }
8732
+ async function bindServer(server) {
8733
+ const allowedPorts = [PREFERRED_PORT, FALLBACK_PORT];
8734
+ for (const port of allowedPorts) {
8735
+ try {
8736
+ await new Promise((resolve, reject) => {
8737
+ const onError = (err) => {
8738
+ server.off("listening", onListening);
8739
+ reject(err);
8740
+ };
8741
+ const onListening = () => {
8742
+ server.off("error", onError);
8743
+ resolve();
8744
+ };
8745
+ server.once("error", onError);
8746
+ server.once("listening", onListening);
8747
+ server.listen(port, "127.0.0.1");
8748
+ });
8749
+ return port;
8750
+ } catch (err) {
8751
+ if (err.code === "EADDRINUSE") {
8752
+ continue;
8753
+ }
8754
+ throw err;
8755
+ }
8756
+ }
8757
+ throw new Error(
8758
+ `Could not bind Codex OAuth callback server to registered ports ${allowedPorts.join(", ")}`
8759
+ );
8760
+ }
8761
+ async function startCodexOAuth(onStatus) {
8762
+ if (activeFlow) {
8763
+ throw new Error("Auth flow already in progress");
8764
+ }
8765
+ const pkce = generatePkce();
8766
+ const state2 = generateState();
8767
+ return new Promise((resolve, reject) => {
8768
+ let settled = false;
8769
+ const safeOnStatus = (status, error) => {
8770
+ try {
8771
+ onStatus(status, error);
8772
+ } catch {
8773
+ logger$g.warn("Codex OAuth status callback failed — window may be closed");
8774
+ }
8775
+ };
8776
+ const wrappedResolve = (tokens) => {
8777
+ if (settled) return;
8778
+ settled = true;
8779
+ cleanup();
8780
+ safeOnStatus("connected");
8781
+ resolve(tokens);
8782
+ };
8783
+ const wrappedReject = (err) => {
8784
+ if (settled) return;
8785
+ settled = true;
8786
+ cleanup();
8787
+ safeOnStatus("error", err.message);
8788
+ reject(err);
8789
+ };
8790
+ const server = startServer(0, pkce, state2, wrappedResolve, wrappedReject);
8791
+ const timeout = setTimeout(() => {
8792
+ wrappedReject(new Error("Auth flow timed out after 5 minutes"));
8793
+ }, AUTH_TIMEOUT_MS);
8794
+ activeFlow = {
8795
+ state: state2,
8796
+ codeVerifier: pkce.codeVerifier,
8797
+ port: 0,
8798
+ server,
8799
+ timeout,
8800
+ onStatus
8801
+ };
8802
+ const cleanup = () => {
8803
+ if (activeFlow?.timeout) clearTimeout(activeFlow.timeout);
8804
+ activeFlow?.server.close();
8805
+ activeFlow = null;
8806
+ };
8807
+ bindServer(server).then((port) => {
8808
+ if (settled) return;
8809
+ activeFlow.port = port;
8810
+ const authUrl = buildAuthorizeUrl(port, pkce, state2);
8811
+ safeOnStatus("waiting");
8812
+ electron.shell.openExternal(authUrl).catch((err) => {
8813
+ logger$g.warn("Failed to open browser, user will need the URL:", err);
8814
+ });
8815
+ }).catch(wrappedReject);
8816
+ });
8817
+ }
8818
+ function cancelCodexOAuth() {
8819
+ if (!activeFlow) return;
8820
+ activeFlow.server.close();
8821
+ if (activeFlow.timeout) clearTimeout(activeFlow.timeout);
8822
+ try {
8823
+ activeFlow.onStatus("idle");
8824
+ } catch {
8825
+ logger$g.warn("Codex OAuth cancel status callback failed — window may be closed");
8826
+ }
8827
+ activeFlow = null;
8828
+ }
8829
+ const logger$f = createLogger("CodexProvider");
8830
+ const REFRESH_WINDOW_MS = 5 * 60 * 1e3;
8831
+ const CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api/codex";
8832
+ const CODEX_CLIENT_VERSION = "0.129.0";
8833
+ class CodexProvider {
8834
+ agentToolProfile;
8835
+ tokens;
8836
+ model;
8837
+ abortController = null;
8838
+ constructor(tokens, model, _baseUrl) {
8839
+ this.tokens = tokens;
8840
+ this.model = model;
8841
+ this.agentToolProfile = "default";
8842
+ }
8843
+ async ensureFreshTokens() {
8844
+ if (Date.now() < this.tokens.expiresAt - REFRESH_WINDOW_MS) return;
8845
+ try {
8846
+ logger$f.info("Refreshing Codex access token");
8847
+ const fresh = await refreshAccessToken(this.tokens);
8848
+ this.tokens = fresh;
8849
+ writeStoredCodexTokens(fresh);
8850
+ } catch (err) {
8851
+ clearStoredCodexTokens();
8852
+ throw new Error(
8853
+ `Codex token refresh failed — please re-authenticate. ${err instanceof Error ? err.message : ""}`
8854
+ );
8855
+ }
8856
+ }
8857
+ backendHeaders() {
8858
+ const headers = {
8859
+ Authorization: `Bearer ${this.tokens.accessToken}`,
8860
+ "Content-Type": "application/json",
8861
+ Accept: "text/event-stream",
8862
+ originator: "codex_cli_rs",
8863
+ "User-Agent": `codex_cli_rs/${CODEX_CLIENT_VERSION} Vessel`
8864
+ };
8865
+ if (this.tokens.accountId) {
8866
+ headers["ChatGPT-Account-ID"] = this.tokens.accountId;
8867
+ }
8868
+ return headers;
8869
+ }
8870
+ buildInput(userMessage, history) {
8871
+ const input = [];
8872
+ for (const msg of history ?? []) {
8873
+ input.push({
8874
+ type: "message",
8875
+ role: msg.role,
8876
+ content: [{ type: "input_text", text: msg.content }]
8877
+ });
8878
+ }
8879
+ input.push({
8880
+ type: "message",
8881
+ role: "user",
8882
+ content: [{ type: "input_text", text: userMessage }]
8883
+ });
8884
+ return input;
8885
+ }
8886
+ handleStreamEvent(raw, onChunk, emittedTextFromDelta) {
8887
+ if (!raw.trim() || raw.trim() === "[DONE]") return;
8888
+ let event;
8889
+ try {
8890
+ event = JSON.parse(raw);
8891
+ } catch {
8892
+ return;
8893
+ }
8894
+ if (event.type === "response.output_text.delta" && event.delta) {
8895
+ emittedTextFromDelta.value = true;
8896
+ onChunk(event.delta);
8897
+ return;
8898
+ }
8899
+ if (event.type === "response.output_item.done" && !emittedTextFromDelta.value) {
8900
+ const text = event.item?.content?.filter((item) => item.type === "output_text" && item.text).map((item) => item.text).join("");
8901
+ if (text) onChunk(text);
8902
+ return;
8903
+ }
8904
+ if (event.type === "response.failed") {
8905
+ const error = event.response?.error;
8906
+ const message = error?.message || error?.code || "Codex response failed";
8907
+ throw new Error(message);
8908
+ }
8909
+ }
8910
+ async streamCodexResponse(systemPrompt, userMessage, onChunk, history) {
8911
+ const response = await fetch(`${CODEX_BACKEND_BASE_URL}/responses`, {
8912
+ method: "POST",
8913
+ headers: this.backendHeaders(),
8914
+ signal: this.abortController?.signal,
8915
+ body: JSON.stringify({
8916
+ model: this.model,
8917
+ instructions: systemPrompt,
8918
+ input: this.buildInput(userMessage, history),
8919
+ stream: true,
8920
+ store: false
8921
+ })
8922
+ });
8923
+ if (!response.ok) {
8924
+ const text = await response.text().catch(() => "");
8925
+ throw new Error(
8926
+ `Codex backend request failed: ${response.status}${text ? ` ${text}` : ""}`
8927
+ );
8928
+ }
8929
+ if (!response.body) {
8930
+ throw new Error("Codex backend returned an empty response stream");
8931
+ }
8932
+ const reader = response.body.getReader();
8933
+ const decoder = new TextDecoder();
8934
+ let buffer = "";
8935
+ const emittedTextFromDelta = { value: false };
8936
+ while (true) {
8937
+ const { value, done } = await reader.read();
8938
+ if (done) break;
8939
+ buffer += decoder.decode(value, { stream: true });
8940
+ let separatorIndex;
8941
+ while ((separatorIndex = buffer.indexOf("\n\n")) !== -1) {
8942
+ const block = buffer.slice(0, separatorIndex);
8943
+ buffer = buffer.slice(separatorIndex + 2);
8944
+ const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
8945
+ this.handleStreamEvent(data, onChunk, emittedTextFromDelta);
8946
+ }
8947
+ }
8948
+ const trailing = buffer.trim();
8949
+ if (trailing) {
8950
+ const data = trailing.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
8951
+ this.handleStreamEvent(data, onChunk, emittedTextFromDelta);
8952
+ }
8953
+ }
8954
+ async streamQuery(systemPrompt, userMessage, onChunk, onEnd, history) {
8955
+ await this.ensureFreshTokens();
8956
+ this.abortController = new AbortController();
8957
+ try {
8958
+ await this.streamCodexResponse(systemPrompt, userMessage, onChunk, history);
8959
+ } catch (err) {
8960
+ if (err.name !== "AbortError") {
8961
+ const msg = err instanceof Error ? err.message : String(err);
8962
+ logger$f.error("Codex streamQuery error:", err);
8963
+ onChunk(`
8964
+
8965
+ [Error: ${msg}]`);
8966
+ }
8967
+ } finally {
8968
+ this.abortController = null;
8969
+ onEnd();
8970
+ }
8971
+ }
8972
+ async streamAgentQuery(systemPrompt, userMessage, _tools, onChunk, _onToolCall, onEnd, history) {
8973
+ await this.ensureFreshTokens();
8974
+ this.abortController = new AbortController();
8975
+ try {
8976
+ await this.streamCodexResponse(systemPrompt, userMessage, onChunk, history);
8977
+ } catch (err) {
8978
+ if (err.name !== "AbortError") {
8979
+ const msg = err instanceof Error ? err.message : String(err);
8980
+ logger$f.error("Codex streamAgentQuery error:", err);
8981
+ onChunk(`
8982
+
8983
+ [Error: ${msg}]`);
8984
+ }
8985
+ } finally {
8986
+ this.abortController = null;
8987
+ onEnd();
8988
+ }
8989
+ }
8990
+ cancel() {
8991
+ this.abortController?.abort();
8992
+ this.abortController = null;
8993
+ }
8994
+ }
8412
8995
  function sanitizeProviderConfig(config) {
8413
8996
  return {
8414
8997
  ...config,
@@ -8430,7 +9013,7 @@ function validateProviderConnection(config, options = { requireModel: true }) {
8430
9013
  if (!meta) {
8431
9014
  return "Selected AI provider is not supported.";
8432
9015
  }
8433
- if (meta.requiresApiKey && !normalized.apiKey) {
9016
+ if (meta.type !== "codex_oauth" && meta.requiresApiKey && !normalized.apiKey) {
8434
9017
  return `${meta.name} requires an API key. Open settings (Ctrl+,) to add one.`;
8435
9018
  }
8436
9019
  if (options.requireModel && !normalized.model) {
@@ -8449,8 +9032,8 @@ function extractLlamaCppCtxSize(payload) {
8449
9032
  const current = queue.shift();
8450
9033
  if (!current || typeof current !== "object" || visited.has(current)) continue;
8451
9034
  visited.add(current);
8452
- for (const [key, value] of Object.entries(current)) {
8453
- if (typeof value === "number" && Number.isFinite(value) && /^(n_ctx|ctx_size|context_size)$/i.test(key)) {
9035
+ for (const [key2, value] of Object.entries(current)) {
9036
+ if (typeof value === "number" && Number.isFinite(value) && /^(n_ctx|ctx_size|context_size)$/i.test(key2)) {
8454
9037
  return value;
8455
9038
  }
8456
9039
  if (value && typeof value === "object") {
@@ -8472,6 +9055,34 @@ function buildLlamaCppCtxWarning(ctxSize) {
8472
9055
  }
8473
9056
  return void 0;
8474
9057
  }
9058
+ async function fetchCodexBackendModels(tokens) {
9059
+ const url = new URL("https://chatgpt.com/backend-api/codex/models");
9060
+ url.searchParams.set("client_version", "0.129.0");
9061
+ const headers = {
9062
+ Authorization: `Bearer ${tokens.accessToken}`,
9063
+ originator: "codex_cli_rs",
9064
+ "User-Agent": "codex_cli_rs/0.129.0 Vessel"
9065
+ };
9066
+ if (tokens.accountId) {
9067
+ headers["ChatGPT-Account-ID"] = tokens.accountId;
9068
+ }
9069
+ const response = await fetch(url.toString(), { headers });
9070
+ if (!response.ok) {
9071
+ throw new Error(`Codex backend model discovery failed: ${response.status}`);
9072
+ }
9073
+ const payload = await response.json();
9074
+ if (!Array.isArray(payload.models)) {
9075
+ throw new Error("Codex backend model discovery returned an invalid response");
9076
+ }
9077
+ return payload.models.map((model) => {
9078
+ if (!model || typeof model !== "object") return null;
9079
+ const record = model;
9080
+ const id = record.slug || record.id || record.model;
9081
+ const visibility = record.visibility;
9082
+ if (visibility === "hidden") return null;
9083
+ return typeof id === "string" && id.trim() ? id.trim() : null;
9084
+ }).filter((id) => id !== null);
9085
+ }
8475
9086
  async function probeLlamaCppCtxWarning(baseURL) {
8476
9087
  try {
8477
9088
  const root = new URL(baseURL);
@@ -8499,6 +9110,24 @@ async function fetchProviderModels(config) {
8499
9110
  const page2 = await client2.models.list();
8500
9111
  return okResult({ models: page2.data.map((model) => model.id) });
8501
9112
  }
9113
+ if (normalized.id === "openai_codex") {
9114
+ const tokens = readStoredCodexTokens();
9115
+ if (!tokens) {
9116
+ throw new Error("Codex provider requires authentication. Connect your ChatGPT account in settings.");
9117
+ }
9118
+ try {
9119
+ const models2 = await fetchCodexBackendModels(tokens);
9120
+ if (models2.length > 0) {
9121
+ return okResult({ models: models2 });
9122
+ }
9123
+ throw new Error("Codex backend model discovery returned no models");
9124
+ } catch (err) {
9125
+ return okResult({
9126
+ models: PROVIDERS.openai_codex.models,
9127
+ warning: `Using built-in Codex model list because live discovery failed: ${err instanceof Error ? err.message : "Unknown error"}`
9128
+ });
9129
+ }
9130
+ }
8502
9131
  const meta = PROVIDERS[normalized.id];
8503
9132
  const baseURL = normalized.baseUrl || meta?.defaultBaseUrl || "https://api.openai.com/v1";
8504
9133
  const client = new OpenAI({
@@ -8528,10 +9157,19 @@ function createProvider(config) {
8528
9157
  normalized.reasoningEffort
8529
9158
  );
8530
9159
  }
9160
+ if (normalized.id === "openai_codex") {
9161
+ const tokens = readStoredCodexTokens();
9162
+ if (!tokens) {
9163
+ throw new Error(
9164
+ "OpenAI Codex requires authentication. Open settings to connect your ChatGPT account."
9165
+ );
9166
+ }
9167
+ return new CodexProvider(tokens, normalized.model, normalized.baseUrl);
9168
+ }
8531
9169
  return new OpenAICompatProvider(normalized);
8532
9170
  }
8533
9171
  const require$1 = node_module.createRequire(require("url").pathToFileURL(__filename).href);
8534
- const logger$d = createLogger("DevTrace");
9172
+ const logger$e = createLogger("DevTrace");
8535
9173
  let cachedFactory;
8536
9174
  function createNoopTraceSession() {
8537
9175
  return {
@@ -8564,7 +9202,7 @@ function loadLocalFactory() {
8564
9202
  return cachedFactory;
8565
9203
  }
8566
9204
  } catch (err) {
8567
- logger$d.warn("Failed to load local trace logger:", err);
9205
+ logger$e.warn("Failed to load local trace logger:", err);
8568
9206
  }
8569
9207
  }
8570
9208
  return cachedFactory;
@@ -8617,14 +9255,14 @@ function normalizeStoredRadioOption(option) {
8617
9255
  function dedupeCandidates(actions) {
8618
9256
  const seen = /* @__PURE__ */ new Set();
8619
9257
  return actions.filter((action) => {
8620
- const key = [
9258
+ const key2 = [
8621
9259
  action.selector || "",
8622
9260
  action.label || "",
8623
9261
  action.role || "",
8624
9262
  action.labelSource || ""
8625
9263
  ].join("::");
8626
- if (seen.has(key)) return false;
8627
- seen.add(key);
9264
+ if (seen.has(key2)) return false;
9265
+ seen.add(key2);
8628
9266
  return true;
8629
9267
  });
8630
9268
  }
@@ -8869,11 +9507,11 @@ function getQuantityElements(page) {
8869
9507
  ];
8870
9508
  return elements.filter((el) => {
8871
9509
  if (!isQuantityLike(el)) return false;
8872
- const key = String(
9510
+ const key2 = String(
8873
9511
  el.index ?? el.selector ?? `${el.type}|${el.name || ""}|${el.label || ""}|${el.value || ""}`
8874
9512
  );
8875
- if (seen.has(key)) return false;
8876
- seen.add(key);
9513
+ if (seen.has(key2)) return false;
9514
+ seen.add(key2);
8877
9515
  return true;
8878
9516
  });
8879
9517
  }
@@ -8915,9 +9553,9 @@ function getCartItemLinks(page) {
8915
9553
  return false;
8916
9554
  }
8917
9555
  if (blockedText.test(text) || blockedHref.test(href)) return false;
8918
- const key = `${normalizeComparable(text)}|${normalizeUrlForMatch(href) || href}`;
8919
- if (seen.has(key)) return false;
8920
- seen.add(key);
9556
+ const key2 = `${normalizeComparable(text)}|${normalizeUrlForMatch(href) || href}`;
9557
+ if (seen.has(key2)) return false;
9558
+ seen.add(key2);
8921
9559
  return true;
8922
9560
  }).slice(0, 12);
8923
9561
  }
@@ -9018,11 +9656,11 @@ function getPurchaseActionElements(page, options) {
9018
9656
  if (!isPurchaseActionElement(el)) return false;
9019
9657
  if (visibleOnly && !isVisibleToUser(el)) return false;
9020
9658
  if (el.blockedByOverlay) return false;
9021
- const key = String(
9659
+ const key2 = String(
9022
9660
  el.index ?? el.selector ?? `${el.type}|${el.text || ""}|${el.label || ""}|${el.href || ""}`
9023
9661
  );
9024
- if (seen.has(key)) return false;
9025
- seen.add(key);
9662
+ if (seen.has(key2)) return false;
9663
+ seen.add(key2);
9026
9664
  return true;
9027
9665
  }).sort((a, b) => {
9028
9666
  const delta = purchaseActionPriority(a) - purchaseActionPriority(b);
@@ -9039,10 +9677,10 @@ function getOffscreenPurchaseActionElements(page) {
9039
9677
  )
9040
9678
  );
9041
9679
  return getPurchaseActionElements(page, { visibleOnly: false }).filter((el) => {
9042
- const key = String(
9680
+ const key2 = String(
9043
9681
  el.index ?? el.selector ?? `${el.type}|${el.text || ""}|${el.label || ""}|${el.href || ""}`
9044
9682
  );
9045
- return !visibleKeys.has(key) && el.visible !== false;
9683
+ return !visibleKeys.has(key2) && el.visible !== false;
9046
9684
  });
9047
9685
  }
9048
9686
  function getDialogFocusedElements(page) {
@@ -9308,7 +9946,7 @@ function formatStructuredValue(value, depth = 0) {
9308
9946
  const rendered = value.map((item) => formatStructuredValue(item, depth + 1)).filter(Boolean).slice(0, depth === 0 ? 8 : 5);
9309
9947
  return rendered.join(depth === 0 ? ", " : " | ");
9310
9948
  }
9311
- const entries = Object.entries(value).slice(0, 6).map(([key, entry]) => `${key}: ${formatStructuredValue(entry, depth + 1)}`).filter((entry) => !entry.endsWith(": "));
9949
+ const entries = Object.entries(value).slice(0, 6).map(([key2, entry]) => `${key2}: ${formatStructuredValue(entry, depth + 1)}`).filter((entry) => !entry.endsWith(": "));
9312
9950
  return entries.join(", ");
9313
9951
  }
9314
9952
  function formatStructuredEntities(entities) {
@@ -9323,13 +9961,13 @@ function formatStructuredEntities(entities) {
9323
9961
  if (entity.url && entity.url !== entity.name) {
9324
9962
  lines.push(` url: ${entity.url}`);
9325
9963
  }
9326
- for (const [key, value] of Object.entries(entity.attributes).slice(
9964
+ for (const [key2, value] of Object.entries(entity.attributes).slice(
9327
9965
  0,
9328
9966
  8
9329
9967
  )) {
9330
9968
  const rendered = formatStructuredValue(value);
9331
9969
  if (rendered) {
9332
- lines.push(` ${key}: ${rendered}`);
9970
+ lines.push(` ${key2}: ${rendered}`);
9333
9971
  }
9334
9972
  }
9335
9973
  return lines.join("\n");
@@ -9427,17 +10065,17 @@ function formatJsonLd(items) {
9427
10065
  }
9428
10066
  return String(val);
9429
10067
  };
9430
- for (const key of priorityFields) {
9431
- if (key in item) {
9432
- seen.add(key);
9433
- const rendered = renderValue(item[key]);
9434
- if (rendered) lines.push(` ${key}: ${rendered}`);
10068
+ for (const key2 of priorityFields) {
10069
+ if (key2 in item) {
10070
+ seen.add(key2);
10071
+ const rendered = renderValue(item[key2]);
10072
+ if (rendered) lines.push(` ${key2}: ${rendered}`);
9435
10073
  }
9436
10074
  }
9437
- for (const [key, val] of Object.entries(item)) {
9438
- if (seen.has(key) || SKIP.has(key) || key === "@type") continue;
10075
+ for (const [key2, val] of Object.entries(item)) {
10076
+ if (seen.has(key2) || SKIP.has(key2) || key2 === "@type") continue;
9439
10077
  const rendered = renderValue(val);
9440
- if (rendered) lines.push(` ${key}: ${rendered}`);
10078
+ if (rendered) lines.push(` ${key2}: ${rendered}`);
9441
10079
  }
9442
10080
  lines.push("");
9443
10081
  }
@@ -9572,9 +10210,9 @@ function getResultCandidates(page) {
9572
10210
  );
9573
10211
  const seen = /* @__PURE__ */ new Set();
9574
10212
  return scored.map(({ element }) => element).filter((element) => {
9575
- const key = `${normalizeComparable(element.text || "")}|${normalizeUrlForMatch(element.href) || ""}`;
9576
- if (seen.has(key)) return false;
9577
- seen.add(key);
10213
+ const key2 = `${normalizeComparable(element.text || "")}|${normalizeUrlForMatch(element.href) || ""}`;
10214
+ if (seen.has(key2)) return false;
10215
+ seen.add(key2);
9578
10216
  return true;
9579
10217
  });
9580
10218
  }
@@ -10371,9 +11009,9 @@ function getCompactPrimaryResultLinks(page, options) {
10371
11009
  })).filter(({ score }) => score >= (listingLike ? 5 : 7)).sort(
10372
11010
  (a, b) => b.score - a.score || (a.element.index ?? Number.MAX_SAFE_INTEGER) - (b.element.index ?? Number.MAX_SAFE_INTEGER)
10373
11011
  ).map(({ element }) => element).filter((element) => {
10374
- const key = `${normalizeComparable(element.text)}|${normalizeUrlForMatch(element.href) || ""}`;
10375
- if (seen.has(key)) return false;
10376
- seen.add(key);
11012
+ const key2 = `${normalizeComparable(element.text)}|${normalizeUrlForMatch(element.href) || ""}`;
11013
+ if (seen.has(key2)) return false;
11014
+ seen.add(key2);
10377
11015
  return true;
10378
11016
  }).slice(0, max);
10379
11017
  }
@@ -10419,9 +11057,9 @@ function formatElement(element) {
10419
11057
  function uniqueElements(elements) {
10420
11058
  const seen = /* @__PURE__ */ new Set();
10421
11059
  return elements.filter((element) => {
10422
- const key = `${element.index ?? ""}|${element.type}|${elementLabel(element)}|${element.href ?? ""}`;
10423
- if (seen.has(key)) return false;
10424
- seen.add(key);
11060
+ const key2 = `${element.index ?? ""}|${element.type}|${elementLabel(element)}|${element.href ?? ""}`;
11061
+ if (seen.has(key2)) return false;
11062
+ seen.add(key2);
10425
11063
  return true;
10426
11064
  });
10427
11065
  }
@@ -11744,12 +12382,12 @@ function normalizeAgentHints(value) {
11744
12382
  return void 0;
11745
12383
  }
11746
12384
  const normalized = Object.fromEntries(
11747
- Object.entries(value).map(([key, hint]) => [key.trim(), normalizeOptionalString(hint)]).filter((entry) => Boolean(entry[0] && entry[1]))
12385
+ Object.entries(value).map(([key2, hint]) => [key2.trim(), normalizeOptionalString(hint)]).filter((entry) => Boolean(entry[0] && entry[1]))
11748
12386
  );
11749
12387
  return Object.keys(normalized).length > 0 ? normalized : void 0;
11750
12388
  }
11751
- function hasOwn(value, key) {
11752
- return Object.prototype.hasOwnProperty.call(value, key);
12389
+ function hasOwn(value, key2) {
12390
+ return Object.prototype.hasOwnProperty.call(value, key2);
11753
12391
  }
11754
12392
  function normalizeBookmarkMetadata(input) {
11755
12393
  const normalized = {};
@@ -11787,7 +12425,7 @@ const UNSORTED_ID = "unsorted";
11787
12425
  const ARCHIVE_FOLDER_NAME = "Archive";
11788
12426
  const NETSCAPE_BOOKMARKS_DOCTYPE = "<!DOCTYPE NETSCAPE-Bookmark-file-1>";
11789
12427
  const SAVE_DEBOUNCE_MS$1 = 250;
11790
- let state$1 = null;
12428
+ let state$2 = null;
11791
12429
  const listeners = /* @__PURE__ */ new Set();
11792
12430
  function cloneState(current) {
11793
12431
  return {
@@ -11802,18 +12440,18 @@ function createPersistence() {
11802
12440
  return createDebouncedJsonPersistence({
11803
12441
  debounceMs: SAVE_DEBOUNCE_MS$1,
11804
12442
  filePath: getBookmarksPath(),
11805
- getValue: () => state$1,
12443
+ getValue: () => state$2,
11806
12444
  logLabel: "bookmarks"
11807
12445
  });
11808
12446
  }
11809
- let persistence$1 = null;
12447
+ let persistence$3 = null;
11810
12448
  function getPersistence() {
11811
- persistence$1 ??= createPersistence();
11812
- return persistence$1;
12449
+ persistence$3 ??= createPersistence();
12450
+ return persistence$3;
11813
12451
  }
11814
12452
  function load$1() {
11815
- if (state$1) return state$1;
11816
- state$1 = loadJsonFile({
12453
+ if (state$2) return state$2;
12454
+ state$2 = loadJsonFile({
11817
12455
  filePath: getBookmarksPath(),
11818
12456
  fallback: { folders: [], bookmarks: [] },
11819
12457
  parse: (raw) => {
@@ -11824,21 +12462,21 @@ function load$1() {
11824
12462
  };
11825
12463
  }
11826
12464
  });
11827
- return state$1;
12465
+ return state$2;
11828
12466
  }
11829
- function save() {
12467
+ function save$1() {
11830
12468
  getPersistence().schedule();
11831
12469
  }
11832
12470
  function assignDefinedBookmarkFields(bookmark, fields) {
11833
12471
  if (!fields) return;
11834
- for (const [key, value] of Object.entries(fields)) {
12472
+ for (const [key2, value] of Object.entries(fields)) {
11835
12473
  if (value === void 0) continue;
11836
- Object.assign(bookmark, { [key]: value });
12474
+ Object.assign(bookmark, { [key2]: value });
11837
12475
  }
11838
12476
  }
11839
- function emit() {
11840
- if (!state$1) return;
11841
- const snapshot = cloneState(state$1);
12477
+ function emit$2() {
12478
+ if (!state$2) return;
12479
+ const snapshot = cloneState(state$2);
11842
12480
  for (const listener of listeners) {
11843
12481
  listener(snapshot);
11844
12482
  }
@@ -11857,7 +12495,7 @@ function getBookmarkDescription(bookmark) {
11857
12495
  bookmark.intent ? `Intent: ${bookmark.intent}` : "",
11858
12496
  bookmark.expectedContent ? `Expected content: ${bookmark.expectedContent}` : "",
11859
12497
  bookmark.keyFields?.length ? `Key fields: ${bookmark.keyFields.join(", ")}` : "",
11860
- bookmark.agentHints && Object.keys(bookmark.agentHints).length > 0 ? `Agent hints: ${Object.entries(bookmark.agentHints).map(([key, value]) => `${key}: ${value}`).join("; ")}` : ""
12498
+ bookmark.agentHints && Object.keys(bookmark.agentHints).length > 0 ? `Agent hints: ${Object.entries(bookmark.agentHints).map(([key2, value]) => `${key2}: ${value}`).join("; ")}` : ""
11861
12499
  ].filter(Boolean);
11862
12500
  return lines.join("\n");
11863
12501
  }
@@ -11960,28 +12598,28 @@ function subscribe(listener) {
11960
12598
  };
11961
12599
  }
11962
12600
  function clearAll() {
11963
- state$1 = { folders: [], bookmarks: [] };
11964
- save();
11965
- emit();
12601
+ state$2 = { folders: [], bookmarks: [] };
12602
+ save$1();
12603
+ emit$2();
11966
12604
  }
11967
12605
  function getBookmark(id) {
11968
12606
  load$1();
11969
- const bookmark = state$1.bookmarks.find((item) => item.id === id);
12607
+ const bookmark = state$2.bookmarks.find((item) => item.id === id);
11970
12608
  return bookmark ? { ...bookmark } : null;
11971
12609
  }
11972
12610
  function getBookmarkByUrl(url) {
11973
12611
  load$1();
11974
12612
  const normalized = url.trim();
11975
12613
  if (!normalized) return null;
11976
- const bookmark = [...state$1.bookmarks].reverse().find((item) => item.url === normalized);
12614
+ const bookmark = [...state$2.bookmarks].reverse().find((item) => item.url === normalized);
11977
12615
  return bookmark ? { ...bookmark } : null;
11978
12616
  }
11979
12617
  function getBookmarkByUrlInFolder(url, folderId) {
11980
12618
  load$1();
11981
12619
  const normalizedUrl = url.trim();
11982
12620
  if (!normalizedUrl) return null;
11983
- const targetFolderId = folderId && folderId !== UNSORTED_ID ? state$1.folders.find((f) => f.id === folderId)?.id ?? UNSORTED_ID : UNSORTED_ID;
11984
- const bookmark = [...state$1.bookmarks].reverse().find(
12621
+ const targetFolderId = folderId && folderId !== UNSORTED_ID ? state$2.folders.find((f) => f.id === folderId)?.id ?? UNSORTED_ID : UNSORTED_ID;
12622
+ const bookmark = [...state$2.bookmarks].reverse().find(
11985
12623
  (item) => item.url === normalizedUrl && item.folderId === targetFolderId
11986
12624
  );
11987
12625
  return bookmark ? { ...bookmark } : null;
@@ -11989,14 +12627,14 @@ function getBookmarkByUrlInFolder(url, folderId) {
11989
12627
  function getFolder(id) {
11990
12628
  load$1();
11991
12629
  if (!id || id === UNSORTED_ID) return null;
11992
- const folder = state$1.folders.find((item) => item.id === id);
12630
+ const folder = state$2.folders.find((item) => item.id === id);
11993
12631
  return folder ? { ...folder } : null;
11994
12632
  }
11995
12633
  function findFolderByName(name) {
11996
12634
  load$1();
11997
12635
  const normalized = name.trim().toLowerCase();
11998
12636
  if (!normalized || normalized === "unsorted") return null;
11999
- const folder = state$1.folders.find(
12637
+ const folder = state$2.folders.find(
12000
12638
  (item) => item.name.trim().toLowerCase() === normalized
12001
12639
  );
12002
12640
  return folder ? { ...folder } : null;
@@ -12004,7 +12642,7 @@ function findFolderByName(name) {
12004
12642
  function listFolderOverviews() {
12005
12643
  load$1();
12006
12644
  const counts = /* @__PURE__ */ new Map();
12007
- for (const bookmark of state$1.bookmarks) {
12645
+ for (const bookmark of state$2.bookmarks) {
12008
12646
  counts.set(bookmark.folderId, (counts.get(bookmark.folderId) ?? 0) + 1);
12009
12647
  }
12010
12648
  return [
@@ -12013,7 +12651,7 @@ function listFolderOverviews() {
12013
12651
  name: "Unsorted",
12014
12652
  count: counts.get(UNSORTED_ID) ?? 0
12015
12653
  },
12016
- ...state$1.folders.map((folder) => ({
12654
+ ...state$2.folders.map((folder) => ({
12017
12655
  id: folder.id,
12018
12656
  name: folder.name,
12019
12657
  summary: folder.summary,
@@ -12024,8 +12662,8 @@ function listFolderOverviews() {
12024
12662
  function searchBookmarks(query) {
12025
12663
  load$1();
12026
12664
  if (!query.trim()) return [];
12027
- return state$1.bookmarks.map((bookmark) => {
12028
- const folder = state$1.folders.find(
12665
+ return state$2.bookmarks.map((bookmark) => {
12666
+ const folder = state$2.folders.find(
12029
12667
  (item) => item.id === bookmark.folderId
12030
12668
  );
12031
12669
  const { matchedFields, score } = getBookmarkSearchMatch({
@@ -12063,9 +12701,9 @@ function createFolderWithSummary(name, summary) {
12063
12701
  summary: summary?.trim() || void 0,
12064
12702
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
12065
12703
  };
12066
- state$1.folders.push(folder);
12067
- save();
12068
- emit();
12704
+ state$2.folders.push(folder);
12705
+ save$1();
12706
+ emit$2();
12069
12707
  return folder;
12070
12708
  }
12071
12709
  function ensureFolder(name, summary) {
@@ -12094,7 +12732,7 @@ function saveBookmarkWithPolicy(url, title, folderId, note, options) {
12094
12732
  throw new Error("Bookmark URL cannot be empty");
12095
12733
  }
12096
12734
  const normalizedTitle = title.trim() || normalizedUrl;
12097
- const targetId = folderId && folderId !== UNSORTED_ID ? state$1.folders.find((f) => f.id === folderId)?.id ?? UNSORTED_ID : UNSORTED_ID;
12735
+ const targetId = folderId && folderId !== UNSORTED_ID ? state$2.folders.find((f) => f.id === folderId)?.id ?? UNSORTED_ID : UNSORTED_ID;
12098
12736
  const duplicatePolicy = options?.onDuplicate ?? "ask";
12099
12737
  const existing = getBookmarkByUrlInFolder(normalizedUrl, targetId);
12100
12738
  if (existing) {
@@ -12105,7 +12743,7 @@ function saveBookmarkWithPolicy(url, title, folderId, note, options) {
12105
12743
  };
12106
12744
  }
12107
12745
  if (duplicatePolicy === "update") {
12108
- const bookmark2 = state$1.bookmarks.find((item) => item.id === existing.id);
12746
+ const bookmark2 = state$2.bookmarks.find((item) => item.id === existing.id);
12109
12747
  if (!bookmark2) {
12110
12748
  return {
12111
12749
  status: "conflict",
@@ -12118,8 +12756,8 @@ function saveBookmarkWithPolicy(url, title, folderId, note, options) {
12118
12756
  }
12119
12757
  assignDefinedBookmarkFields(bookmark2, options?.extra);
12120
12758
  bookmark2.savedAt = (/* @__PURE__ */ new Date()).toISOString();
12121
- save();
12122
- emit();
12759
+ save$1();
12760
+ emit$2();
12123
12761
  return {
12124
12762
  status: "updated",
12125
12763
  bookmark: { ...bookmark2 }
@@ -12135,9 +12773,9 @@ function saveBookmarkWithPolicy(url, title, folderId, note, options) {
12135
12773
  savedAt: (/* @__PURE__ */ new Date()).toISOString(),
12136
12774
  ...options?.extra
12137
12775
  };
12138
- state$1.bookmarks.push(bookmark);
12139
- save();
12140
- emit();
12776
+ state$2.bookmarks.push(bookmark);
12777
+ save$1();
12778
+ emit$2();
12141
12779
  return {
12142
12780
  status: "created",
12143
12781
  bookmark
@@ -12145,18 +12783,18 @@ function saveBookmarkWithPolicy(url, title, folderId, note, options) {
12145
12783
  }
12146
12784
  function removeBookmark(id) {
12147
12785
  load$1();
12148
- const before = state$1.bookmarks.length;
12149
- state$1.bookmarks = state$1.bookmarks.filter((b) => b.id !== id);
12150
- if (state$1.bookmarks.length !== before) {
12151
- save();
12152
- emit();
12786
+ const before = state$2.bookmarks.length;
12787
+ state$2.bookmarks = state$2.bookmarks.filter((b) => b.id !== id);
12788
+ if (state$2.bookmarks.length !== before) {
12789
+ save$1();
12790
+ emit$2();
12153
12791
  return true;
12154
12792
  }
12155
12793
  return false;
12156
12794
  }
12157
12795
  function updateBookmark(id, updates) {
12158
12796
  load$1();
12159
- const bookmark = state$1.bookmarks.find((item) => item.id === id);
12797
+ const bookmark = state$2.bookmarks.find((item) => item.id === id);
12160
12798
  if (!bookmark) return null;
12161
12799
  const metadataUpdates = normalizeBookmarkMetadataUpdate({
12162
12800
  intent: updates.intent,
@@ -12173,7 +12811,7 @@ function updateBookmark(id, updates) {
12173
12811
  bookmark.note = trimmed || void 0;
12174
12812
  }
12175
12813
  if (typeof updates.folderId === "string") {
12176
- bookmark.folderId = updates.folderId && updates.folderId !== UNSORTED_ID ? state$1.folders.find((item) => item.id === updates.folderId)?.id ?? UNSORTED_ID : UNSORTED_ID;
12814
+ bookmark.folderId = updates.folderId && updates.folderId !== UNSORTED_ID ? state$2.folders.find((item) => item.id === updates.folderId)?.id ?? UNSORTED_ID : UNSORTED_ID;
12177
12815
  }
12178
12816
  if ("intent" in metadataUpdates) {
12179
12817
  bookmark.intent = metadataUpdates.intent;
@@ -12190,36 +12828,36 @@ function updateBookmark(id, updates) {
12190
12828
  if ("agentHints" in metadataUpdates) {
12191
12829
  bookmark.agentHints = metadataUpdates.agentHints;
12192
12830
  }
12193
- save();
12194
- emit();
12831
+ save$1();
12832
+ emit$2();
12195
12833
  return { ...bookmark };
12196
12834
  }
12197
12835
  function removeFolder(id, deleteContents = false) {
12198
12836
  load$1();
12199
- const exists = state$1.folders.some((f) => f.id === id);
12837
+ const exists = state$2.folders.some((f) => f.id === id);
12200
12838
  if (!exists) return false;
12201
12839
  if (deleteContents) {
12202
- state$1.bookmarks = state$1.bookmarks.filter((b) => b.folderId !== id);
12840
+ state$2.bookmarks = state$2.bookmarks.filter((b) => b.folderId !== id);
12203
12841
  } else {
12204
- state$1.bookmarks = state$1.bookmarks.map(
12842
+ state$2.bookmarks = state$2.bookmarks.map(
12205
12843
  (b) => b.folderId === id ? { ...b, folderId: UNSORTED_ID } : b
12206
12844
  );
12207
12845
  }
12208
- state$1.folders = state$1.folders.filter((f) => f.id !== id);
12209
- save();
12210
- emit();
12846
+ state$2.folders = state$2.folders.filter((f) => f.id !== id);
12847
+ save$1();
12848
+ emit$2();
12211
12849
  return true;
12212
12850
  }
12213
12851
  function renameFolder(id, newName, summary) {
12214
12852
  load$1();
12215
- const folder = state$1.folders.find((f) => f.id === id);
12853
+ const folder = state$2.folders.find((f) => f.id === id);
12216
12854
  if (!folder) return null;
12217
12855
  const trimmed = newName.trim();
12218
12856
  if (!trimmed) return null;
12219
12857
  folder.name = trimmed;
12220
12858
  folder.summary = summary?.trim() || void 0;
12221
- save();
12222
- emit();
12859
+ save$1();
12860
+ emit$2();
12223
12861
  return { ...folder };
12224
12862
  }
12225
12863
  function flushPersist$1() {
@@ -12230,7 +12868,7 @@ function importBookmarksFromHtml(content) {
12230
12868
  let skipped = 0;
12231
12869
  let errors = 0;
12232
12870
  load$1();
12233
- const existingUrls = new Set(state$1.bookmarks.map((b) => b.url));
12871
+ const existingUrls = new Set(state$2.bookmarks.map((b) => b.url));
12234
12872
  let currentFolderId = UNSORTED_ID;
12235
12873
  let currentFolderName = "Imported";
12236
12874
  const lines = content.split("\n");
@@ -12239,7 +12877,7 @@ function importBookmarksFromHtml(content) {
12239
12877
  const folderMatch = line.match(/<DT><H3[^>]*>([^<]+)<\/H3>/i);
12240
12878
  if (folderMatch) {
12241
12879
  currentFolderName = folderMatch[1].trim();
12242
- const existing = state$1.folders.find(
12880
+ const existing = state$2.folders.find(
12243
12881
  (f) => f.name.toLowerCase() === currentFolderName.toLowerCase()
12244
12882
  );
12245
12883
  if (existing) {
@@ -12250,7 +12888,7 @@ function importBookmarksFromHtml(content) {
12250
12888
  name: currentFolderName,
12251
12889
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
12252
12890
  };
12253
- state$1.folders.push(folder);
12891
+ state$2.folders.push(folder);
12254
12892
  currentFolderId = folder.id;
12255
12893
  }
12256
12894
  continue;
@@ -12271,7 +12909,7 @@ function importBookmarksFromHtml(content) {
12271
12909
  else errors++;
12272
12910
  continue;
12273
12911
  }
12274
- state$1.bookmarks.push({
12912
+ state$2.bookmarks.push({
12275
12913
  id: crypto$1.randomUUID(),
12276
12914
  url,
12277
12915
  title,
@@ -12283,8 +12921,8 @@ function importBookmarksFromHtml(content) {
12283
12921
  }
12284
12922
  }
12285
12923
  if (imported > 0) {
12286
- save();
12287
- emit();
12924
+ save$1();
12925
+ emit$2();
12288
12926
  }
12289
12927
  return { imported, skipped, errors };
12290
12928
  }
@@ -12297,14 +12935,14 @@ function importBookmarksFromJson(content) {
12297
12935
  const incomingFolders = Array.isArray(parsed?.folders) ? parsed.folders : [];
12298
12936
  const incomingBookmarks = Array.isArray(parsed?.bookmarks) ? parsed.bookmarks : [];
12299
12937
  load$1();
12300
- const existingUrls = new Set(state$1.bookmarks.map((b) => b.url));
12938
+ const existingUrls = new Set(state$2.bookmarks.map((b) => b.url));
12301
12939
  const folderIdMap = /* @__PURE__ */ new Map();
12302
12940
  for (const folder of incomingFolders) {
12303
12941
  if (!folder?.id || !folder?.name) {
12304
12942
  errors++;
12305
12943
  continue;
12306
12944
  }
12307
- const existing = state$1.folders.find(
12945
+ const existing = state$2.folders.find(
12308
12946
  (f) => f.name.toLowerCase() === folder.name.toLowerCase()
12309
12947
  );
12310
12948
  if (existing) {
@@ -12316,7 +12954,7 @@ function importBookmarksFromJson(content) {
12316
12954
  summary: folder.summary?.trim() || void 0,
12317
12955
  createdAt: folder.createdAt || (/* @__PURE__ */ new Date()).toISOString()
12318
12956
  };
12319
- state$1.folders.push(newFolder);
12957
+ state$2.folders.push(newFolder);
12320
12958
  folderIdMap.set(folder.id, newFolder.id);
12321
12959
  }
12322
12960
  }
@@ -12330,7 +12968,7 @@ function importBookmarksFromJson(content) {
12330
12968
  continue;
12331
12969
  }
12332
12970
  const mappedFolderId = bookmark.folderId ? folderIdMap.get(bookmark.folderId) ?? UNSORTED_ID : UNSORTED_ID;
12333
- state$1.bookmarks.push({
12971
+ state$2.bookmarks.push({
12334
12972
  id: crypto$1.randomUUID(),
12335
12973
  url: bookmark.url.trim(),
12336
12974
  title: typeof bookmark.title === "string" ? bookmark.title.trim() : bookmark.url,
@@ -12342,8 +12980,8 @@ function importBookmarksFromJson(content) {
12342
12980
  imported++;
12343
12981
  }
12344
12982
  if (imported > 0 || errors > 0) {
12345
- save();
12346
- emit();
12983
+ save$1();
12984
+ emit$2();
12347
12985
  }
12348
12986
  } catch {
12349
12987
  errors++;
@@ -12387,9 +13025,9 @@ async function captureLiveHighlightSnapshot(wc, savedHighlights = []) {
12387
13025
  text: normalizeText(highlight.text),
12388
13026
  color: highlight.color?.trim() || void 0
12389
13027
  })).filter((highlight) => highlight.text.length > 0).filter((highlight) => {
12390
- const key = `${highlight.text}\0${highlight.color ?? ""}`;
12391
- if (seen.has(key)) return false;
12392
- seen.add(key);
13028
+ const key2 = `${highlight.text}\0${highlight.color ?? ""}`;
13029
+ if (seen.has(key2)) return false;
13030
+ seen.add(key2);
12393
13031
  return true;
12394
13032
  }).map((highlight) => ({
12395
13033
  ...highlight,
@@ -12683,7 +13321,7 @@ function formatDeadLinkMessage(label, result) {
12683
13321
  const status = result.statusCode ? `HTTP ${result.statusCode}` : "dead link";
12684
13322
  return `Skipped stale link "${label}" because ${destination} returned ${status}. Try a different link or URL instead.`;
12685
13323
  }
12686
- const logger$c = createLogger("Screenshot");
13324
+ const logger$d = createLogger("Screenshot");
12687
13325
  const SCREENSHOT_RETRY_COUNT = 3;
12688
13326
  const SCREENSHOT_RETRY_BASE_DELAY_MS = 120;
12689
13327
  async function captureScreenshot(wc) {
@@ -12705,7 +13343,7 @@ async function captureScreenshot(wc) {
12705
13343
  }
12706
13344
  }
12707
13345
  } catch (err) {
12708
- logger$c.debug(
13346
+ logger$d.debug(
12709
13347
  `capturePage attempt ${attempt + 1} failed; retrying if attempts remain.`,
12710
13348
  getErrorMessage(err)
12711
13349
  );
@@ -12738,17 +13376,17 @@ function sessionFileName(name) {
12738
13376
  function getSessionPath(name) {
12739
13377
  return path$1.join(ensureSessionsDir(), sessionFileName(name));
12740
13378
  }
12741
- function writeSessionFile(filePath, data) {
13379
+ function writeSessionFile(filePath2, data) {
12742
13380
  fs$1.writeFileSync(
12743
- filePath,
13381
+ filePath2,
12744
13382
  JSON.stringify({ version: SESSION_VERSION, ...data }, null, 2),
12745
13383
  { encoding: "utf-8", mode: 384 }
12746
13384
  );
12747
- fs$1.chmodSync(filePath, 384);
13385
+ fs$1.chmodSync(filePath2, 384);
12748
13386
  }
12749
- function readSessionFile(filePath) {
13387
+ function readSessionFile(filePath2) {
12750
13388
  try {
12751
- const raw = fs$1.readFileSync(filePath, "utf-8");
13389
+ const raw = fs$1.readFileSync(filePath2, "utf-8");
12752
13390
  const parsed = JSON.parse(raw);
12753
13391
  if (!parsed || typeof parsed.name !== "string") {
12754
13392
  return null;
@@ -12969,9 +13607,9 @@ async function loadNamedSession(tabManager, name) {
12969
13607
  };
12970
13608
  }
12971
13609
  function deleteNamedSession(name) {
12972
- const filePath = getSessionPath(name);
12973
- if (!fs$1.existsSync(filePath)) return false;
12974
- fs$1.unlinkSync(filePath);
13610
+ const filePath2 = getSessionPath(name);
13611
+ if (!fs$1.existsSync(filePath2)) return false;
13612
+ fs$1.unlinkSync(filePath2);
12975
13613
  return true;
12976
13614
  }
12977
13615
  function isInvalidTextTargetQuery(rawQuery) {
@@ -13579,7 +14217,7 @@ function buildHuggingFaceSearchShortcut(currentUrl, rawQuery) {
13579
14217
  appliedFilters
13580
14218
  };
13581
14219
  }
13582
- const logger$b = createLogger("PageActions");
14220
+ const logger$c = createLogger("PageActions");
13583
14221
  function getBookmarkMetadataFromArgs(args) {
13584
14222
  return normalizeBookmarkMetadata({
13585
14223
  intent: args.intent ?? args.intent,
@@ -13765,7 +14403,7 @@ async function executePageScript(wc, script, options) {
13765
14403
  return result;
13766
14404
  } catch (err) {
13767
14405
  const label = options?.label ? ` (${options.label})` : "";
13768
- logger$b.warn(`Failed to execute page script${label}:`, err);
14406
+ logger$c.warn(`Failed to execute page script${label}:`, err);
13769
14407
  return null;
13770
14408
  } finally {
13771
14409
  if (timer) {
@@ -13866,7 +14504,7 @@ Search results snapshot:
13866
14504
  ${truncated}`;
13867
14505
  }
13868
14506
  } catch (err) {
13869
- logger$b.warn("Failed to build post-search summary, falling back to nav summary:", err);
14507
+ logger$c.warn("Failed to build post-search summary, falling back to nav summary:", err);
13870
14508
  }
13871
14509
  const fallback = await getPostNavSummary(wc);
13872
14510
  return fallback ? `${fallback}
@@ -13889,7 +14527,7 @@ Page snapshot after navigation:
13889
14527
  ${truncated}`;
13890
14528
  }
13891
14529
  } catch (err) {
13892
- logger$b.warn("Failed to build post-click navigation summary:", err);
14530
+ logger$c.warn("Failed to build post-click navigation summary:", err);
13893
14531
  }
13894
14532
  return "";
13895
14533
  }
@@ -14383,7 +15021,7 @@ async function restoreLocaleSnapshot(wc, snapshot) {
14383
15021
  }
14384
15022
  }
14385
15023
  } catch (err) {
14386
- logger$b.warn("Failed to restore locale via history navigation, trying URL reload fallback:", err);
15024
+ logger$c.warn("Failed to restore locale via history navigation, trying URL reload fallback:", err);
14387
15025
  }
14388
15026
  if (snapshot.url && snapshot.url !== wc.getURL()) {
14389
15027
  try {
@@ -14392,7 +15030,7 @@ async function restoreLocaleSnapshot(wc, snapshot) {
14392
15030
  await waitForLoad(wc, 3e3);
14393
15031
  return;
14394
15032
  } catch (err) {
14395
- logger$b.warn("Failed to restore locale via safe URL load, trying page reload fallback:", err);
15033
+ logger$c.warn("Failed to restore locale via safe URL load, trying page reload fallback:", err);
14396
15034
  }
14397
15035
  }
14398
15036
  if (snapshot.url) {
@@ -14400,7 +15038,7 @@ async function restoreLocaleSnapshot(wc, snapshot) {
14400
15038
  await wc.reload();
14401
15039
  await waitForLoad(wc, 3e3);
14402
15040
  } catch (err) {
14403
- logger$b.warn("Failed to restore locale via page reload:", err);
15041
+ logger$c.warn("Failed to restore locale via page reload:", err);
14404
15042
  }
14405
15043
  }
14406
15044
  }
@@ -14428,9 +15066,9 @@ function isAddToCartText(text) {
14428
15066
  }
14429
15067
  function recordCartClick(url, text) {
14430
15068
  recentCartClicks.set(url, { text, ts: Date.now() });
14431
- for (const [key, entry] of recentCartClicks) {
15069
+ for (const [key2, entry] of recentCartClicks) {
14432
15070
  if (Date.now() - entry.ts > CART_CLICK_COOLDOWN_MS) {
14433
- recentCartClicks.delete(key);
15071
+ recentCartClicks.delete(key2);
14434
15072
  }
14435
15073
  }
14436
15074
  }
@@ -14477,9 +15115,9 @@ function normalizeCartProductKey(url) {
14477
15115
  }
14478
15116
  }
14479
15117
  function pruneCartAddedProducts(now = Date.now()) {
14480
- for (const [key, entry] of cartAddedProducts) {
15118
+ for (const [key2, entry] of cartAddedProducts) {
14481
15119
  if (now - entry.ts > CART_ADDED_TTL_MS) {
14482
- cartAddedProducts.delete(key);
15120
+ cartAddedProducts.delete(key2);
14483
15121
  }
14484
15122
  }
14485
15123
  }
@@ -14505,7 +15143,7 @@ function isProductAlreadyInCart(url) {
14505
15143
  function getCartAddedSummary(url) {
14506
15144
  pruneCartAddedProducts();
14507
15145
  const origin = cartOrigin(url);
14508
- const items = Array.from(cartAddedProducts.entries()).filter(([key]) => !origin || key.startsWith(`${origin}/`)).map(([_path, info]) => `- ${info.title}`).join("\n");
15146
+ const items = Array.from(cartAddedProducts.entries()).filter(([key2]) => !origin || key2.startsWith(`${origin}/`)).map(([_path, info]) => `- ${info.title}`).join("\n");
14509
15147
  if (!items) return "";
14510
15148
  const count = items.split("\n").length;
14511
15149
  return `
@@ -14752,7 +15390,7 @@ ${postActivationOverlayHint}`;
14752
15390
  return `${clickText} -> ${hrefFallbackUrl} (recovered via href fallback)`;
14753
15391
  }
14754
15392
  } catch (err) {
14755
- logger$b.warn("Failed href fallback after click, returning generic click result:", err);
15393
+ logger$c.warn("Failed href fallback after click, returning generic click result:", err);
14756
15394
  }
14757
15395
  }
14758
15396
  }
@@ -14797,7 +15435,7 @@ async function tryAutoDismissCartDialog(wc) {
14797
15435
  return result;
14798
15436
  }
14799
15437
  } catch (err) {
14800
- logger$b.warn("Failed to auto-dismiss cart dialog, falling back to dialog actions:", err);
15438
+ logger$c.warn("Failed to auto-dismiss cart dialog, falling back to dialog actions:", err);
14801
15439
  }
14802
15440
  return null;
14803
15441
  }
@@ -16161,8 +16799,8 @@ async function submitForm(wc, args) {
16161
16799
  }
16162
16800
  return "Submitted form";
16163
16801
  }
16164
- async function pressKeyDirect(wc, key, index, selector) {
16165
- return pressKey(wc, { key, index, selector });
16802
+ async function pressKeyDirect(wc, key2, index, selector) {
16803
+ return pressKey(wc, { key: key2, index, selector });
16166
16804
  }
16167
16805
  async function submitFormDirect(wc, index, selector) {
16168
16806
  return submitForm(wc, { index, selector });
@@ -16528,8 +17166,8 @@ async function searchPage(wc, args) {
16528
17166
  return `Searched "${query}" (same page — results may have loaded dynamically)${await getPostSearchSummary(wc)}`;
16529
17167
  }
16530
17168
  async function pressKey(wc, args) {
16531
- const key = typeof args.key === "string" ? args.key.trim() : "";
16532
- if (!key) return "Error: No key provided";
17169
+ const key2 = typeof args.key === "string" ? args.key.trim() : "";
17170
+ if (!key2) return "Error: No key provided";
16533
17171
  const selector = await resolveSelector(wc, args.index, args.selector);
16534
17172
  const focusResult = await executePageScript(
16535
17173
  wc,
@@ -16567,16 +17205,16 @@ async function pressKey(wc, args) {
16567
17205
  return focusResult.error;
16568
17206
  }
16569
17207
  wc.focus();
16570
- const normalizedKey = key.length === 1 ? key : key[0].toUpperCase() + key.slice(1);
17208
+ const normalizedKey = key2.length === 1 ? key2 : key2[0].toUpperCase() + key2.slice(1);
16571
17209
  const electronKeyCode = normalizedKey === "Enter" ? "Return" : normalizedKey === "ArrowUp" ? "Up" : normalizedKey === "ArrowDown" ? "Down" : normalizedKey === "ArrowLeft" ? "Left" : normalizedKey === "ArrowRight" ? "Right" : normalizedKey;
16572
17210
  wc.sendInputEvent({ type: "keyDown", keyCode: electronKeyCode });
16573
- if (key.length === 1) {
16574
- wc.sendInputEvent({ type: "char", keyCode: key });
17211
+ if (key2.length === 1) {
17212
+ wc.sendInputEvent({ type: "char", keyCode: key2 });
16575
17213
  }
16576
17214
  await sleep(16);
16577
17215
  wc.sendInputEvent({ type: "keyUp", keyCode: electronKeyCode });
16578
17216
  const label = "label" in focusResult && typeof focusResult.label === "string" ? focusResult.label : null;
16579
- return label ? `Pressed key: ${key} on ${label}` : `Pressed key: ${key}`;
17217
+ return label ? `Pressed key: ${key2} on ${label}` : `Pressed key: ${key2}`;
16580
17218
  }
16581
17219
  async function getPostActionState$1(ctx, name) {
16582
17220
  const tab = ctx.tabManager.getActiveTab();
@@ -16986,8 +17624,8 @@ async function executeAction(name, args, ctx) {
16986
17624
  if (!wc) return "Error: No active tab";
16987
17625
  const beforeUrl = wc.getURL();
16988
17626
  const result2 = await pressKey(wc, args);
16989
- const key = typeof args.key === "string" ? args.key.trim() : "";
16990
- if (key === "Enter") {
17627
+ const key2 = typeof args.key === "string" ? args.key.trim() : "";
17628
+ if (key2 === "Enter") {
16991
17629
  await waitForPotentialNavigation(wc, beforeUrl, 3e3);
16992
17630
  const afterUrl = wc.getURL();
16993
17631
  if (afterUrl !== beforeUrl) {
@@ -17067,7 +17705,7 @@ async function executeAction(name, args, ctx) {
17067
17705
  )
17068
17706
  ]);
17069
17707
  } catch (err) {
17070
- logger$b.warn("Failed to extract content for read_page, falling back to lighter recovery:", err);
17708
+ logger$c.warn("Failed to extract content for read_page, falling back to lighter recovery:", err);
17071
17709
  content = null;
17072
17710
  }
17073
17711
  if (!content || content.content.length === 0) {
@@ -17084,12 +17722,12 @@ async function executeAction(name, args, ctx) {
17084
17722
  new Promise((resolve) => setTimeout(() => resolve(null), 3e3))
17085
17723
  ]);
17086
17724
  } catch (err) {
17087
- logger$b.warn("Failed to re-extract content after iframe consent dismissal:", err);
17725
+ logger$c.warn("Failed to re-extract content after iframe consent dismissal:", err);
17088
17726
  content = null;
17089
17727
  }
17090
17728
  }
17091
17729
  } catch (err) {
17092
- logger$b.warn("Failed iframe consent dismissal during read_page recovery:", err);
17730
+ logger$c.warn("Failed iframe consent dismissal during read_page recovery:", err);
17093
17731
  }
17094
17732
  }
17095
17733
  if (content && content.content.length > 0) {
@@ -17502,7 +18140,7 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
17502
18140
  try {
17503
18141
  page = await extractContent(wc);
17504
18142
  } catch (err) {
17505
- logger$b.warn("Failed to extract content for suggest:", err);
18143
+ logger$c.warn("Failed to extract content for suggest:", err);
17506
18144
  return "Could not read page. Try navigate to a working URL.";
17507
18145
  }
17508
18146
  const suggestions = [];
@@ -18092,17 +18730,17 @@ function escapeYaml(value) {
18092
18730
  }
18093
18731
  function renderFrontmatter(data) {
18094
18732
  const lines = ["---"];
18095
- for (const [key, value] of Object.entries(data)) {
18733
+ for (const [key2, value] of Object.entries(data)) {
18096
18734
  if (value == null) continue;
18097
18735
  if (Array.isArray(value)) {
18098
18736
  if (value.length === 0) continue;
18099
- lines.push(`${key}:`);
18737
+ lines.push(`${key2}:`);
18100
18738
  for (const item of value) {
18101
18739
  lines.push(` - ${escapeYaml(item)}`);
18102
18740
  }
18103
18741
  continue;
18104
18742
  }
18105
- lines.push(`${key}: ${escapeYaml(value)}`);
18743
+ lines.push(`${key2}: ${escapeYaml(value)}`);
18106
18744
  }
18107
18745
  lines.push("---", "");
18108
18746
  return lines.join("\n");
@@ -18155,11 +18793,11 @@ function parseFrontmatter(content) {
18155
18793
  activeArrayKey = "";
18156
18794
  const separatorIndex = trimmed.indexOf(":");
18157
18795
  if (separatorIndex === -1) continue;
18158
- const key = trimmed.slice(0, separatorIndex).trim();
18796
+ const key2 = trimmed.slice(0, separatorIndex).trim();
18159
18797
  const value = trimmed.slice(separatorIndex + 1).trim();
18160
- if (key === "title" && value) {
18798
+ if (key2 === "title" && value) {
18161
18799
  result.title = value.replace(/^["']|["']$/g, "");
18162
- } else if (key === "tags") {
18800
+ } else if (key2 === "tags") {
18163
18801
  activeArrayKey = "tags";
18164
18802
  if (value.startsWith("[") && value.endsWith("]")) {
18165
18803
  const inline = value.slice(1, -1).split(",").map((item) => item.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
@@ -18745,15 +19383,15 @@ Exception: ${result.exceptionDetails}`);
18745
19383
  value: zod.z.string().nullable().describe("Value to set, or null to remove the key")
18746
19384
  }
18747
19385
  },
18748
- async ({ type, key, value }) => {
19386
+ async ({ type, key: key2, value }) => {
18749
19387
  return withDevToolsAction(
18750
19388
  runtime2,
18751
19389
  tabManager,
18752
19390
  "devtools_set_storage",
18753
- { type, key, value: value ? value.slice(0, 100) : null },
19391
+ { type, key: key2, value: value ? value.slice(0, 100) : null },
18754
19392
  async () => {
18755
19393
  const session = getOrCreateSession(tabManager);
18756
- return session.setStorage(type, key, value);
19394
+ return session.setStorage(type, key2, value);
18757
19395
  }
18758
19396
  );
18759
19397
  }
@@ -18827,7 +19465,7 @@ Exception: ${result.exceptionDetails}`);
18827
19465
  }
18828
19466
  );
18829
19467
  }
18830
- const logger$a = createLogger("VaultShared");
19468
+ const logger$b = createLogger("VaultShared");
18831
19469
  const ALGORITHM = "aes-256-gcm";
18832
19470
  const IV_LENGTH = 12;
18833
19471
  const AUTH_TAG_LENGTH = 16;
@@ -18845,11 +19483,11 @@ function getOrCreateEncryptionKey(keyFilename) {
18845
19483
  const encryptedKey = fs$1.readFileSync(keyPath);
18846
19484
  return Buffer.from(electron.safeStorage.decryptString(encryptedKey), "utf-8");
18847
19485
  }
18848
- const key = crypto$2.randomBytes(32);
19486
+ const key2 = crypto$2.randomBytes(32);
18849
19487
  fs$1.mkdirSync(path$1.dirname(keyPath), { recursive: true });
18850
- const encrypted = electron.safeStorage.encryptString(key.toString("utf-8"));
19488
+ const encrypted = electron.safeStorage.encryptString(key2.toString("utf-8"));
18851
19489
  fs$1.writeFileSync(keyPath, encrypted, { mode: 384 });
18852
- return key;
19490
+ return key2;
18853
19491
  }
18854
19492
  function createEncryptDecrypt(keyFilename) {
18855
19493
  let cachedKey = null;
@@ -18858,9 +19496,9 @@ function createEncryptDecrypt(keyFilename) {
18858
19496
  return cachedKey;
18859
19497
  }
18860
19498
  function encrypt2(plaintext) {
18861
- const key = getKey();
19499
+ const key2 = getKey();
18862
19500
  const iv = crypto$2.randomBytes(IV_LENGTH);
18863
- const cipher = crypto$2.createCipheriv(ALGORITHM, key, iv, {
19501
+ const cipher = crypto$2.createCipheriv(ALGORITHM, key2, iv, {
18864
19502
  authTagLength: AUTH_TAG_LENGTH
18865
19503
  });
18866
19504
  const encrypted = Buffer.concat([
@@ -18871,11 +19509,11 @@ function createEncryptDecrypt(keyFilename) {
18871
19509
  return Buffer.concat([iv, authTag, encrypted]);
18872
19510
  }
18873
19511
  function decrypt2(data) {
18874
- const key = getKey();
19512
+ const key2 = getKey();
18875
19513
  const iv = data.subarray(0, IV_LENGTH);
18876
19514
  const authTag = data.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);
18877
19515
  const ciphertext = data.subarray(IV_LENGTH + AUTH_TAG_LENGTH);
18878
- const decipher = crypto$2.createDecipheriv(ALGORITHM, key, iv, {
19516
+ const decipher = crypto$2.createDecipheriv(ALGORITHM, key2, iv, {
18879
19517
  authTagLength: AUTH_TAG_LENGTH
18880
19518
  });
18881
19519
  decipher.setAuthTag(authTag);
@@ -18904,7 +19542,7 @@ function createVaultIO(vaultFilename, encrypt2, decrypt2) {
18904
19542
  cachedEntries = JSON.parse(json);
18905
19543
  return cachedEntries;
18906
19544
  } catch (err) {
18907
- logger$a.error("Failed to load vault:", err);
19545
+ logger$b.error("Failed to load vault:", err);
18908
19546
  throw new Error("Could not unlock the vault. Check OS secret storage availability.");
18909
19547
  }
18910
19548
  }
@@ -18973,7 +19611,7 @@ function createAuditLog(filename, maxEntries) {
18973
19611
  } catch {
18974
19612
  }
18975
19613
  } catch (err) {
18976
- logger$a.error("Failed to write audit log:", err);
19614
+ logger$b.error("Failed to write audit log:", err);
18977
19615
  }
18978
19616
  }
18979
19617
  function readAuditLog2(limit = 100) {
@@ -18983,7 +19621,7 @@ function createAuditLog(filename, maxEntries) {
18983
19621
  const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
18984
19622
  return lines.slice(-Math.min(limit, maxEntries)).map((line) => JSON.parse(line)).reverse();
18985
19623
  } catch (err) {
18986
- logger$a.error("Failed to read audit log:", err);
19624
+ logger$b.error("Failed to read audit log:", err);
18987
19625
  return [];
18988
19626
  }
18989
19627
  }
@@ -19091,7 +19729,7 @@ async function requestConsent(request) {
19091
19729
  }
19092
19730
  const AUDIT_FILENAME = "vessel-vault-audit.jsonl";
19093
19731
  const MAX_ENTRIES = 1e3;
19094
- const logger$9 = createLogger("VaultAudit");
19732
+ const logger$a = createLogger("VaultAudit");
19095
19733
  function getAuditPath() {
19096
19734
  return path$1.join(electron.app.getPath("userData"), AUDIT_FILENAME);
19097
19735
  }
@@ -19101,7 +19739,7 @@ function appendAuditEntry(entry) {
19101
19739
  fs$1.mkdirSync(path$1.dirname(auditPath), { recursive: true });
19102
19740
  fs$1.appendFileSync(auditPath, JSON.stringify(entry) + "\n");
19103
19741
  } catch (err) {
19104
- logger$9.error("Failed to write audit log:", err);
19742
+ logger$a.error("Failed to write audit log:", err);
19105
19743
  }
19106
19744
  }
19107
19745
  function readAuditLog$1(limit = 100) {
@@ -19111,7 +19749,7 @@ function readAuditLog$1(limit = 100) {
19111
19749
  const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
19112
19750
  return lines.slice(-Math.min(limit, MAX_ENTRIES)).map((line) => JSON.parse(line)).reverse();
19113
19751
  } catch (err) {
19114
- logger$9.error("Failed to read audit log:", err);
19752
+ logger$a.error("Failed to read audit log:", err);
19115
19753
  return [];
19116
19754
  }
19117
19755
  }
@@ -19284,7 +19922,7 @@ async function requestHumanVaultConsent(request) {
19284
19922
  }
19285
19923
  let httpServer = null;
19286
19924
  let mcpAuthToken = null;
19287
- const logger$8 = createLogger("MCP");
19925
+ const logger$9 = createLogger("MCP");
19288
19926
  const MCP_AUTH_FILENAME = "mcp-auth.json";
19289
19927
  function getMcpAuthFilePath() {
19290
19928
  const configDir = process.env.VESSEL_CONFIG_DIR || path$1.join(
@@ -19312,15 +19950,15 @@ function getPersistentMcpAuthToken() {
19312
19950
  }
19313
19951
  function writeMcpAuthFile(endpoint, token) {
19314
19952
  try {
19315
- const filePath = getMcpAuthFilePath();
19316
- fs$1.mkdirSync(path$1.dirname(filePath), { recursive: true });
19953
+ const filePath2 = getMcpAuthFilePath();
19954
+ fs$1.mkdirSync(path$1.dirname(filePath2), { recursive: true });
19317
19955
  fs$1.writeFileSync(
19318
- filePath,
19956
+ filePath2,
19319
19957
  JSON.stringify({ endpoint, token, pid: process.pid }, null, 2) + "\n",
19320
19958
  { mode: 384 }
19321
19959
  );
19322
19960
  } catch (err) {
19323
- logger$8.warn("Failed to write auth file:", err);
19961
+ logger$9.warn("Failed to write auth file:", err);
19324
19962
  }
19325
19963
  }
19326
19964
  function clearMcpAuthFile() {
@@ -19333,10 +19971,10 @@ function clearMcpAuthFile() {
19333
19971
  return;
19334
19972
  }
19335
19973
  try {
19336
- const filePath = getMcpAuthFilePath();
19337
- fs$1.mkdirSync(path$1.dirname(filePath), { recursive: true });
19974
+ const filePath2 = getMcpAuthFilePath();
19975
+ fs$1.mkdirSync(path$1.dirname(filePath2), { recursive: true });
19338
19976
  fs$1.writeFileSync(
19339
- filePath,
19977
+ filePath2,
19340
19978
  JSON.stringify(
19341
19979
  { endpoint: "", token: existingToken, pid: null },
19342
19980
  null,
@@ -19345,7 +19983,7 @@ function clearMcpAuthFile() {
19345
19983
  { mode: 384 }
19346
19984
  );
19347
19985
  } catch (err) {
19348
- logger$8.warn("Failed to clear auth file:", err);
19986
+ logger$9.warn("Failed to clear auth file:", err);
19349
19987
  }
19350
19988
  }
19351
19989
  function asTextResponse(text) {
@@ -19435,7 +20073,7 @@ async function getPostActionState(tabManager, name) {
19435
20073
  }
19436
20074
  }
19437
20075
  } catch (err) {
19438
- logger$8.warn("Failed to compute post-action state warning:", err);
20076
+ logger$9.warn("Failed to compute post-action state warning:", err);
19439
20077
  }
19440
20078
  return `${warning}
19441
20079
  [state: url=${wc.getURL()}, canGoBack=${tab.canGoBack()}, canGoForward=${tab.canGoForward()}, loading=${wc.isLoading()}]`;
@@ -19537,7 +20175,7 @@ async function waitForConditionMcp(wc, text, selector, timeoutMs) {
19537
20175
  }
19538
20176
  })()
19539
20177
  `).catch((err) => {
19540
- logger$8.warn("Failed to gather wait_for timeout diagnostic:", err);
20178
+ logger$9.warn("Failed to gather wait_for timeout diagnostic:", err);
19541
20179
  return null;
19542
20180
  });
19543
20181
  if (typeof diagnostic === "string" && diagnostic.trim()) {
@@ -19624,7 +20262,7 @@ function registerTools(server, tabManager, runtime2) {
19624
20262
  const page = await extractContent(wc);
19625
20263
  pageType = detectPageType(page);
19626
20264
  } catch (err) {
19627
- logger$8.warn("Failed to detect page type for tool scoring, falling back to GENERAL:", err);
20265
+ logger$9.warn("Failed to detect page type for tool scoring, falling back to GENERAL:", err);
19628
20266
  }
19629
20267
  }
19630
20268
  const scored = TOOL_DEFINITIONS.map((def) => {
@@ -20352,19 +20990,19 @@ ${buildScopedContext(pageContent, mode)}`;
20352
20990
  selector: zod.z.string().optional().describe("CSS selector to focus first")
20353
20991
  }
20354
20992
  },
20355
- async ({ key, index, selector }) => {
20993
+ async ({ key: key2, index, selector }) => {
20356
20994
  const tab = tabManager.getActiveTab();
20357
20995
  if (!tab) return asNoActiveTabResponse();
20358
20996
  return withAction(
20359
20997
  runtime2,
20360
20998
  tabManager,
20361
20999
  "press_key",
20362
- { key, index, selector },
21000
+ { key: key2, index, selector },
20363
21001
  async () => {
20364
21002
  const wc = tab.view.webContents;
20365
21003
  const beforeUrl = wc.getURL();
20366
- const result = await pressKeyDirect(wc, key, index, selector);
20367
- if (key === "Enter") {
21004
+ const result = await pressKeyDirect(wc, key2, index, selector);
21005
+ if (key2 === "Enter") {
20368
21006
  await waitForPotentialNavigation$1(wc, beforeUrl, 3e3);
20369
21007
  const afterUrl = wc.getURL();
20370
21008
  if (afterUrl !== beforeUrl) {
@@ -20830,7 +21468,7 @@ To analyze visually, call vision_analyze with image_url="${screenshotPath}"`
20830
21468
  )
20831
21469
  }
20832
21470
  },
20833
- async ({ index, selector, text, label, durationMs, persist, color }) => {
21471
+ async ({ index, selector, text, label, durationMs, persist: persist2, color }) => {
20834
21472
  const tab = tabManager.getActiveTab();
20835
21473
  if (!tab) return asNoActiveTabResponse();
20836
21474
  const normalizedText = normalizeLooseString(text);
@@ -20844,7 +21482,7 @@ To analyze visually, call vision_analyze with image_url="${screenshotPath}"`
20844
21482
  text: normalizedText,
20845
21483
  label,
20846
21484
  durationMs,
20847
- persist,
21485
+ persist: persist2,
20848
21486
  color
20849
21487
  },
20850
21488
  async () => {
@@ -20858,7 +21496,7 @@ To analyze visually, call vision_analyze with image_url="${screenshotPath}"`
20858
21496
  durationMs,
20859
21497
  color
20860
21498
  );
20861
- if (persist && !durationMs && !result.startsWith("Error") && !result.includes("not found")) {
21499
+ if (persist2 && !durationMs && !result.startsWith("Error") && !result.includes("not found")) {
20862
21500
  const url = normalizeUrl$1(wc.getURL());
20863
21501
  addHighlight(
20864
21502
  url,
@@ -21022,7 +21660,7 @@ ${JSON.stringify(otherHighlights, null, 2)}`
21022
21660
  void 0,
21023
21661
  h.color
21024
21662
  ).catch(
21025
- (err) => logger$8.warn("Failed to restore highlight after removal:", err)
21663
+ (err) => logger$9.warn("Failed to restore highlight after removal:", err)
21026
21664
  );
21027
21665
  }
21028
21666
  }
@@ -21870,7 +22508,7 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
21870
22508
  try {
21871
22509
  page = await extractContent(wc);
21872
22510
  } catch (err) {
21873
- logger$8.warn("Failed to extract page while generating suggestions:", err);
22511
+ logger$9.warn("Failed to extract page while generating suggestions:", err);
21874
22512
  return asTextResponse(
21875
22513
  "Could not read page. Try navigate to a working URL."
21876
22514
  );
@@ -22479,7 +23117,7 @@ ${JSON.stringify(tableJson, null, 2)}`;
22479
23117
  try {
22480
23118
  targetDomain = new URL(tab.state.url).hostname;
22481
23119
  } catch (err) {
22482
- logger$8.warn("Failed to parse active tab URL for vault_status:", err);
23120
+ logger$9.warn("Failed to parse active tab URL for vault_status:", err);
22483
23121
  return asErrorTextResponse("Could not parse active tab URL");
22484
23122
  }
22485
23123
  }
@@ -22545,7 +23183,7 @@ Use vault_login to fill the login form. Credentials are filled directly — you
22545
23183
  try {
22546
23184
  hostname = new URL(tab.state.url).hostname;
22547
23185
  } catch (err) {
22548
- logger$8.warn("Failed to parse active tab URL for vault_login:", err);
23186
+ logger$9.warn("Failed to parse active tab URL for vault_login:", err);
22549
23187
  return asErrorTextResponse("Could not parse active tab URL");
22550
23188
  }
22551
23189
  const matches = findEntriesForDomain(`https://${hostname}`);
@@ -22639,7 +23277,7 @@ Use vault_login to fill the login form. Credentials are filled directly — you
22639
23277
  try {
22640
23278
  hostname = new URL(tab.state.url).hostname;
22641
23279
  } catch (err) {
22642
- logger$8.warn("Failed to parse active tab URL for vault_totp:", err);
23280
+ logger$9.warn("Failed to parse active tab URL for vault_totp:", err);
22643
23281
  return asErrorTextResponse("Could not parse active tab URL");
22644
23282
  }
22645
23283
  const matches = findEntriesForDomain(`https://${hostname}`);
@@ -22937,7 +23575,7 @@ function startMcpServer(tabManager, runtime2, port) {
22937
23575
  });
22938
23576
  mcpAuthToken = getPersistentMcpAuthToken();
22939
23577
  return new Promise((resolve) => {
22940
- const server = http.createServer(async (req, res) => {
23578
+ const server = http$1.createServer(async (req, res) => {
22941
23579
  const url = new URL(req.url || "/", `http://localhost:${port}`);
22942
23580
  if (url.pathname !== "/mcp") {
22943
23581
  res.writeHead(404);
@@ -22979,7 +23617,7 @@ function startMcpServer(tabManager, runtime2, port) {
22979
23617
  await mcpServer.connect(transport);
22980
23618
  await transport.handleRequest(req, res);
22981
23619
  } catch (error) {
22982
- logger$8.error("Error handling request:", error);
23620
+ logger$9.error("Error handling request:", error);
22983
23621
  if (!res.headersSent) {
22984
23622
  res.writeHead(500, { "Content-Type": "application/json" });
22985
23623
  res.end(
@@ -22998,7 +23636,7 @@ function startMcpServer(tabManager, runtime2, port) {
22998
23636
  };
22999
23637
  server.once("error", (error) => {
23000
23638
  const message = error.code === "EADDRINUSE" ? `Port ${port} is already in use. MCP server not started.` : error.message;
23001
- logger$8.error("Server error:", error);
23639
+ logger$9.error("Server error:", error);
23002
23640
  clearMcpAuthFile();
23003
23641
  setMcpHealth({
23004
23642
  configuredPort: port,
@@ -23030,7 +23668,7 @@ function startMcpServer(tabManager, runtime2, port) {
23030
23668
  message: `MCP server listening on ${endpoint}.`
23031
23669
  });
23032
23670
  if (process.env.VESSEL_DEBUG_MCP === "1" || process.env.VESSEL_DEBUG_MCP === "true") {
23033
- logger$8.info(`Server listening on ${endpoint} (auth enabled)`);
23671
+ logger$9.info(`Server listening on ${endpoint} (auth enabled)`);
23034
23672
  }
23035
23673
  if (mcpAuthToken) {
23036
23674
  writeMcpAuthFile(endpoint, mcpAuthToken);
@@ -23069,7 +23707,7 @@ function stopMcpServer() {
23069
23707
  message: "MCP server is stopped."
23070
23708
  });
23071
23709
  if (process.env.VESSEL_DEBUG_MCP === "1" || process.env.VESSEL_DEBUG_MCP === "true") {
23072
- logger$8.info("Server stopped");
23710
+ logger$9.info("Server stopped");
23073
23711
  }
23074
23712
  resolve();
23075
23713
  });
@@ -23090,7 +23728,7 @@ const KIT_ID_UNSAFE_CHAR_PATTERN = /[\/\\\0]/;
23090
23728
  function isSafeAutomationKitId(id) {
23091
23729
  return id.length > 0 && !KIT_ID_UNSAFE_CHAR_PATTERN.test(id);
23092
23730
  }
23093
- const logger$7 = createLogger("KitRegistry");
23731
+ const logger$8 = createLogger("KitRegistry");
23094
23732
  function getUserKitsDir() {
23095
23733
  return path$1.join(electron.app.getPath("userData"), "kits");
23096
23734
  }
@@ -23128,10 +23766,10 @@ function getInstalledKits() {
23128
23766
  if (isValidKit(parsed)) {
23129
23767
  kits.push(parsed);
23130
23768
  } else {
23131
- logger$7.warn(`Skipping invalid kit file: ${file}`);
23769
+ logger$8.warn(`Skipping invalid kit file: ${file}`);
23132
23770
  }
23133
23771
  } catch (err) {
23134
- logger$7.warn(`Failed to read kit file: ${file}`, err);
23772
+ logger$8.warn(`Failed to read kit file: ${file}`, err);
23135
23773
  }
23136
23774
  }
23137
23775
  return kits;
@@ -23203,7 +23841,7 @@ function uninstallKit(id, scheduledKitIds) {
23203
23841
  return errorResult("Failed to remove the kit file.");
23204
23842
  }
23205
23843
  }
23206
- const logger$6 = createLogger("Scheduler");
23844
+ const logger$7 = createLogger("Scheduler");
23207
23845
  let jobs = [];
23208
23846
  let removeIdleListener = null;
23209
23847
  let broadcastFn = null;
@@ -23228,7 +23866,7 @@ function saveJobs() {
23228
23866
  try {
23229
23867
  fs$1.writeFileSync(getJobsPath(), JSON.stringify(jobs, null, 2), "utf-8");
23230
23868
  } catch (err) {
23231
- logger$6.warn("Failed to save jobs:", err);
23869
+ logger$7.warn("Failed to save jobs:", err);
23232
23870
  }
23233
23871
  }
23234
23872
  function normalizeJob(job, now = /* @__PURE__ */ new Date()) {
@@ -23350,7 +23988,7 @@ async function fireJob(job, windowState, runtime2) {
23350
23988
  };
23351
23989
  startActivity();
23352
23990
  if (!settings2.chatProvider) {
23353
- logger$6.warn(`Job "${job.kitName}" skipped — no chat provider configured`);
23991
+ logger$7.warn(`Job "${job.kitName}" skipped — no chat provider configured`);
23354
23992
  appendActivity(
23355
23993
  "Chat provider not configured. Open Settings (Ctrl+,) to choose a provider."
23356
23994
  );
@@ -23358,7 +23996,7 @@ async function fireJob(job, windowState, runtime2) {
23358
23996
  return;
23359
23997
  }
23360
23998
  if (process.env.VESSEL_DEBUG_SCHEDULER === "1" || process.env.VESSEL_DEBUG_SCHEDULER === "true") {
23361
- logger$6.info(`Firing scheduled job: ${job.kitName} (${job.id})`);
23999
+ logger$7.info(`Firing scheduled job: ${job.kitName} (${job.id})`);
23362
24000
  }
23363
24001
  try {
23364
24002
  const provider = createProvider(settings2.chatProvider);
@@ -23411,7 +24049,7 @@ function tick(windowState, runtime2) {
23411
24049
  saveJobs();
23412
24050
  broadcastFn?.(Channels.SCHEDULE_JOBS_UPDATE, jobs);
23413
24051
  void fireJob(job, windowState, runtime2).catch((err) => {
23414
- logger$6.warn("Unexpected error firing job:", err);
24052
+ logger$7.warn("Unexpected error firing job:", err);
23415
24053
  }).finally(fireNext);
23416
24054
  };
23417
24055
  fireNext();
@@ -23523,7 +24161,7 @@ const PROFILE_FIELDS = [
23523
24161
  "postalCode",
23524
24162
  "country"
23525
24163
  ];
23526
- let state = null;
24164
+ let state$1 = null;
23527
24165
  function getFilePath() {
23528
24166
  return path.join(electron.app.getPath("userData"), "vessel-autofill.json");
23529
24167
  }
@@ -23548,8 +24186,8 @@ function normalizeStoredProfile(value) {
23548
24186
  return profile;
23549
24187
  }
23550
24188
  function load() {
23551
- if (state) return state;
23552
- state = loadJsonFile({
24189
+ if (state$1) return state$1;
24190
+ state$1 = loadJsonFile({
23553
24191
  filePath: getFilePath(),
23554
24192
  fallback: getDefaultState(),
23555
24193
  secure: true,
@@ -23560,12 +24198,12 @@ function load() {
23560
24198
  };
23561
24199
  }
23562
24200
  });
23563
- return state;
24201
+ return state$1;
23564
24202
  }
23565
- const persistence = createDebouncedJsonPersistence({
24203
+ const persistence$2 = createDebouncedJsonPersistence({
23566
24204
  debounceMs: SAVE_DEBOUNCE_MS,
23567
24205
  filePath: getFilePath(),
23568
- getValue: () => state,
24206
+ getValue: () => state$1,
23569
24207
  logLabel: "autofill",
23570
24208
  secure: true
23571
24209
  });
@@ -23585,7 +24223,7 @@ function addProfile(input) {
23585
24223
  updatedAt: now
23586
24224
  };
23587
24225
  s.profiles.push(profile);
23588
- persistence.schedule();
24226
+ persistence$2.schedule();
23589
24227
  return profile;
23590
24228
  }
23591
24229
  function updateProfile(id, updates) {
@@ -23597,7 +24235,7 @@ function updateProfile(id, updates) {
23597
24235
  ...updates,
23598
24236
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
23599
24237
  };
23600
- persistence.schedule();
24238
+ persistence$2.schedule();
23601
24239
  return s.profiles[idx];
23602
24240
  }
23603
24241
  function deleteProfile(id) {
@@ -23605,11 +24243,11 @@ function deleteProfile(id) {
23605
24243
  const len = s.profiles.length;
23606
24244
  s.profiles = s.profiles.filter((p) => p.id !== id);
23607
24245
  if (s.profiles.length === len) return false;
23608
- persistence.schedule();
24246
+ persistence$2.schedule();
23609
24247
  return true;
23610
24248
  }
23611
24249
  function flushPersist() {
23612
- return persistence.flush();
24250
+ return persistence$2.flush();
23613
24251
  }
23614
24252
  const AUTOCOMPLETE_MAP = {
23615
24253
  "given-name": "firstName",
@@ -23738,12 +24376,12 @@ function matchField(el, profile) {
23738
24376
  if (inputType === "hidden" || inputType === "submit" || inputType === "button" || inputType === "file" || inputType === "image") return null;
23739
24377
  if (inputType === "password" || inputType === "checkbox" || inputType === "radio") return null;
23740
24378
  if (el.autocomplete) {
23741
- const key = el.autocomplete.replace(/section-\w+\s+/, "").replace(/^shipping\s+|^billing\s+/, "");
23742
- if (key === "name" || key === "additional-name") {
24379
+ const key2 = el.autocomplete.replace(/section-\w+\s+/, "").replace(/^shipping\s+|^billing\s+/, "");
24380
+ if (key2 === "name" || key2 === "additional-name") {
23743
24381
  const fullName = getFullName(profile);
23744
24382
  if (fullName) return mk(fullName, 100, "autocomplete", "fullName");
23745
24383
  }
23746
- const pk = AUTOCOMPLETE_MAP[key];
24384
+ const pk = AUTOCOMPLETE_MAP[key2];
23747
24385
  if (pk && profile[pk]) return mk(profile[pk], 100, "autocomplete", pk);
23748
24386
  }
23749
24387
  if (INPUT_TYPE_MAP[inputType]) {
@@ -24231,6 +24869,64 @@ function installAdBlockingForSession(ses, tabManager) {
24231
24869
  callback(getAdBlockDecision(details));
24232
24870
  });
24233
24871
  }
24872
+ const filePath$1 = () => path$1.join(electron.app.getPath("userData"), "vessel-downloads.json");
24873
+ function parse(raw) {
24874
+ if (!raw || typeof raw !== "object") return { items: [] };
24875
+ const items = Array.isArray(raw.items) ? raw.items : [];
24876
+ return { items };
24877
+ }
24878
+ let state = loadJsonFile({ filePath: filePath$1(), fallback: { items: [] }, parse });
24879
+ const persistence$1 = createDebouncedJsonPersistence({
24880
+ debounceMs: 250,
24881
+ filePath: filePath$1(),
24882
+ getValue: () => state,
24883
+ logLabel: "downloads"
24884
+ });
24885
+ let broadcaster$1 = null;
24886
+ function persist() {
24887
+ state.items = state.items.slice(0, 200);
24888
+ persistence$1.schedule();
24889
+ }
24890
+ function emit$1() {
24891
+ broadcaster$1?.(Channels.DOWNLOADS_UPDATE, state.items);
24892
+ }
24893
+ function setDownloadBroadcaster(fn) {
24894
+ broadcaster$1 = fn;
24895
+ }
24896
+ function listDownloads() {
24897
+ return state.items;
24898
+ }
24899
+ function upsertDownload(input) {
24900
+ const now = (/* @__PURE__ */ new Date()).toISOString();
24901
+ const existing = state.items.find((item) => item.savePath === input.savePath);
24902
+ if (existing) {
24903
+ Object.assign(existing, input, { updatedAt: now });
24904
+ persist();
24905
+ emit$1();
24906
+ return existing;
24907
+ }
24908
+ const record = { id: crypto$2.randomUUID(), ...input, startedAt: now, updatedAt: now };
24909
+ state.items = [record, ...state.items];
24910
+ persist();
24911
+ emit$1();
24912
+ return record;
24913
+ }
24914
+ function clearDownloads() {
24915
+ state.items = [];
24916
+ persist();
24917
+ emit$1();
24918
+ }
24919
+ async function openDownload(id) {
24920
+ const item = state.items.find((d) => d.id === id);
24921
+ if (!item || !fs$1.existsSync(item.savePath)) return false;
24922
+ return await electron.shell.openPath(item.savePath) === "";
24923
+ }
24924
+ async function showDownloadInFolder(id) {
24925
+ const item = state.items.find((d) => d.id === id);
24926
+ if (!item || !fs$1.existsSync(item.savePath)) return false;
24927
+ electron.shell.showItemInFolder(item.savePath);
24928
+ return true;
24929
+ }
24234
24930
  const defaultDownloadViews = /* @__PURE__ */ new Set();
24235
24931
  let defaultDownloadHandlerInstalled = false;
24236
24932
  function resolveDownloadPath(downloadDir, filename) {
@@ -24277,17 +24973,20 @@ function installDownloadHandlerForSession(targetSession, chromeView) {
24277
24973
  receivedBytes: 0,
24278
24974
  state: "progressing"
24279
24975
  };
24280
- send(Channels.DOWNLOAD_STARTED, info);
24976
+ const record = upsertDownload(info);
24977
+ send(Channels.DOWNLOAD_STARTED, { ...info, id: record.id, startedAt: record.startedAt, updatedAt: record.updatedAt });
24281
24978
  item.on("updated", (_event2, state2) => {
24282
24979
  info.receivedBytes = item.getReceivedBytes();
24283
24980
  info.totalBytes = item.getTotalBytes();
24284
24981
  info.state = state2 === "progressing" ? "progressing" : "interrupted";
24285
- send(Channels.DOWNLOAD_PROGRESS, info);
24982
+ const record2 = upsertDownload(info);
24983
+ send(Channels.DOWNLOAD_PROGRESS, { ...info, id: record2.id, startedAt: record2.startedAt, updatedAt: record2.updatedAt });
24286
24984
  });
24287
24985
  item.once("done", (_event2, state2) => {
24288
24986
  info.receivedBytes = item.getReceivedBytes();
24289
24987
  info.state = state2 === "completed" ? "completed" : "cancelled";
24290
- send(Channels.DOWNLOAD_DONE, info);
24988
+ const record2 = upsertDownload(info);
24989
+ send(Channels.DOWNLOAD_DONE, { ...info, id: record2.id, startedAt: record2.startedAt, updatedAt: record2.updatedAt });
24291
24990
  });
24292
24991
  });
24293
24992
  }
@@ -24521,7 +25220,7 @@ function createFindInPageBridge(tabManager, chromeView) {
24521
25220
  }
24522
25221
  };
24523
25222
  }
24524
- const logger$5 = createLogger("PrivateWindow");
25223
+ const logger$6 = createLogger("PrivateWindow");
24525
25224
  const privateWindows = /* @__PURE__ */ new Set();
24526
25225
  function layoutPrivateViews(state2) {
24527
25226
  const { window: win, chromeView, tabManager } = state2;
@@ -24744,7 +25443,7 @@ function createPrivateWindow() {
24744
25443
  privateSession.clearStorageData(),
24745
25444
  privateSession.clearCache()
24746
25445
  ]).catch((error) => {
24747
- logger$5.warn("Failed to clear private browsing session:", error);
25446
+ logger$6.warn("Failed to clear private browsing session:", error);
24748
25447
  });
24749
25448
  });
24750
25449
  privateWindows.add(state2);
@@ -24754,7 +25453,7 @@ function createPrivateWindow() {
24754
25453
  });
24755
25454
  loadPrivateRenderer(chromeView);
24756
25455
  win.show();
24757
- logger$5.info("Private browsing window opened");
25456
+ logger$6.info("Private browsing window opened");
24758
25457
  return state2;
24759
25458
  }
24760
25459
  const secondaryWindows = /* @__PURE__ */ new Set();
@@ -25001,35 +25700,35 @@ function registerBookmarkHandlers() {
25001
25700
  electron.ipcMain.handle(
25002
25701
  Channels.BOOKMARKS_EXPORT_HTML,
25003
25702
  async (_, options) => {
25004
- const { canceled, filePath } = await electron.dialog.showSaveDialog({
25703
+ const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
25005
25704
  title: "Export Bookmarks",
25006
25705
  defaultPath: "vessel-bookmarks.html",
25007
25706
  filters: [{ name: "HTML Bookmarks", extensions: ["html"] }]
25008
25707
  });
25009
- if (canceled || !filePath) return null;
25708
+ if (canceled || !filePath2) return null;
25010
25709
  const content = exportBookmarksHtml({
25011
25710
  includeNotes: options?.includeNotes ?? false
25012
25711
  });
25013
- await fs.promises.writeFile(filePath, content, "utf-8");
25712
+ await fs.promises.writeFile(filePath2, content, "utf-8");
25014
25713
  trackBookmarkAction("export");
25015
25714
  return {
25016
- filePath,
25715
+ filePath: filePath2,
25017
25716
  count: getState().bookmarks.length
25018
25717
  };
25019
25718
  }
25020
25719
  );
25021
25720
  electron.ipcMain.handle(Channels.BOOKMARKS_EXPORT_JSON, async () => {
25022
- const { canceled, filePath } = await electron.dialog.showSaveDialog({
25721
+ const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
25023
25722
  title: "Export Vessel Bookmark Archive",
25024
25723
  defaultPath: "vessel-bookmarks.json",
25025
25724
  filters: [{ name: "Vessel Bookmark Archive", extensions: ["json"] }]
25026
25725
  });
25027
- if (canceled || !filePath) return null;
25726
+ if (canceled || !filePath2) return null;
25028
25727
  const content = exportBookmarksJson();
25029
- await fs.promises.writeFile(filePath, content, "utf-8");
25728
+ await fs.promises.writeFile(filePath2, content, "utf-8");
25030
25729
  trackBookmarkAction("export");
25031
25730
  return {
25032
- filePath,
25731
+ filePath: filePath2,
25033
25732
  count: getState().bookmarks.length
25034
25733
  };
25035
25734
  });
@@ -25038,20 +25737,20 @@ function registerBookmarkHandlers() {
25038
25737
  async (_, folderId, options) => {
25039
25738
  const folder = getFolder(folderId);
25040
25739
  if (!folder) return null;
25041
- const { canceled, filePath } = await electron.dialog.showSaveDialog({
25740
+ const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
25042
25741
  title: `Export ${folder.name}`,
25043
25742
  defaultPath: `vessel-bookmarks-${getSafeBookmarkExportName(folder.name)}.html`,
25044
25743
  filters: [{ name: "HTML Bookmarks", extensions: ["html"] }]
25045
25744
  });
25046
- if (canceled || !filePath) return null;
25745
+ if (canceled || !filePath2) return null;
25047
25746
  const result = exportBookmarkFolderHtml(folderId, {
25048
25747
  includeNotes: options?.includeNotes ?? true
25049
25748
  });
25050
25749
  if (!result) return null;
25051
- await fs.promises.writeFile(filePath, result.content, "utf-8");
25750
+ await fs.promises.writeFile(filePath2, result.content, "utf-8");
25052
25751
  trackBookmarkAction("export");
25053
25752
  return {
25054
- filePath,
25753
+ filePath: filePath2,
25055
25754
  count: result.count
25056
25755
  };
25057
25756
  }
@@ -25104,26 +25803,26 @@ function registerHistoryHandlers() {
25104
25803
  clearAll$1();
25105
25804
  });
25106
25805
  electron.ipcMain.handle(Channels.HISTORY_EXPORT_HTML, async () => {
25107
- const { canceled, filePath } = await electron.dialog.showSaveDialog({
25806
+ const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
25108
25807
  title: "Export History",
25109
25808
  defaultPath: "vessel-history.html",
25110
25809
  filters: [{ name: "HTML", extensions: ["html"] }]
25111
25810
  });
25112
- if (canceled || !filePath) return null;
25811
+ if (canceled || !filePath2) return null;
25113
25812
  const content = exportHistoryHtml();
25114
- await fs.promises.writeFile(filePath, content, "utf-8");
25115
- return { filePath, count: getState$1().entries.length };
25813
+ await fs.promises.writeFile(filePath2, content, "utf-8");
25814
+ return { filePath: filePath2, count: getState$1().entries.length };
25116
25815
  });
25117
25816
  electron.ipcMain.handle(Channels.HISTORY_EXPORT_JSON, async () => {
25118
- const { canceled, filePath } = await electron.dialog.showSaveDialog({
25817
+ const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
25119
25818
  title: "Export History",
25120
25819
  defaultPath: "vessel-history.json",
25121
25820
  filters: [{ name: "JSON", extensions: ["json"] }]
25122
25821
  });
25123
- if (canceled || !filePath) return null;
25822
+ if (canceled || !filePath2) return null;
25124
25823
  const content = exportHistoryJson();
25125
- await fs.promises.writeFile(filePath, content, "utf-8");
25126
- return { filePath, count: getState$1().entries.length };
25824
+ await fs.promises.writeFile(filePath2, content, "utf-8");
25825
+ return { filePath: filePath2, count: getState$1().entries.length };
25127
25826
  });
25128
25827
  electron.ipcMain.handle(Channels.HISTORY_IMPORT, async () => {
25129
25828
  const { canceled, filePaths } = await electron.dialog.showOpenDialog({
@@ -25134,9 +25833,9 @@ function registerHistoryHandlers() {
25134
25833
  properties: ["openFile"]
25135
25834
  });
25136
25835
  if (canceled || filePaths.length === 0) return null;
25137
- const filePath = filePaths[0];
25138
- const content = await fs.promises.readFile(filePath, "utf-8");
25139
- const result = filePath.endsWith(".json") ? importHistoryFromJson(content) : importHistoryFromHtml(content);
25836
+ const filePath2 = filePaths[0];
25837
+ const content = await fs.promises.readFile(filePath2, "utf-8");
25838
+ const result = filePath2.endsWith(".json") ? importHistoryFromJson(content) : importHistoryFromHtml(content);
25140
25839
  return result;
25141
25840
  });
25142
25841
  }
@@ -25368,6 +26067,161 @@ function registerSecurityHandlers(tabManager) {
25368
26067
  tabManager.goBackToSafety(tabId);
25369
26068
  });
25370
26069
  }
26070
+ const logger$5 = createLogger("CodexIPC");
26071
+ function registerCodexHandlers() {
26072
+ electron.ipcMain.handle(Channels.CODEX_START_AUTH, async (event) => {
26073
+ const wc = event.sender;
26074
+ if (!wc || wc.isDestroyed()) {
26075
+ return {
26076
+ ok: false,
26077
+ error: "No active window found for sender"
26078
+ };
26079
+ }
26080
+ const sendStatus = (status, error) => {
26081
+ try {
26082
+ wc.send(Channels.CODEX_AUTH_STATUS, { status, error: error || null });
26083
+ } catch {
26084
+ logger$5.warn("Codex auth status send failed — window may be closed");
26085
+ }
26086
+ };
26087
+ try {
26088
+ const tokens = await startCodexOAuth(sendStatus);
26089
+ writeStoredCodexTokens(tokens);
26090
+ return {
26091
+ ok: true,
26092
+ accountEmail: tokens.accountEmail || tokens.accountId,
26093
+ accountId: tokens.accountId
26094
+ };
26095
+ } catch (err) {
26096
+ logger$5.error("Codex auth failed:", err);
26097
+ return {
26098
+ ok: false,
26099
+ error: err instanceof Error ? err.message : "Unknown error"
26100
+ };
26101
+ }
26102
+ });
26103
+ electron.ipcMain.handle(Channels.CODEX_CANCEL_AUTH, () => {
26104
+ cancelCodexOAuth();
26105
+ return { ok: true };
26106
+ });
26107
+ electron.ipcMain.handle(Channels.CODEX_DISCONNECT, () => {
26108
+ clearStoredCodexTokens();
26109
+ return { ok: true };
26110
+ });
26111
+ }
26112
+ const filePath = () => path$1.join(electron.app.getPath("userData"), "vessel-permissions.json");
26113
+ let records = loadJsonFile({
26114
+ filePath: filePath(),
26115
+ fallback: [],
26116
+ parse: (raw) => Array.isArray(raw) ? raw : []
26117
+ });
26118
+ const persistence = createDebouncedJsonPersistence({ debounceMs: 250, filePath: filePath(), getValue: () => records, logLabel: "permissions" });
26119
+ let broadcaster = null;
26120
+ function key(origin, permission) {
26121
+ return `${origin}
26122
+ ${permission}`;
26123
+ }
26124
+ function emit() {
26125
+ broadcaster?.(Channels.PERMISSIONS_GET, records);
26126
+ }
26127
+ function save(origin, permission, decision) {
26128
+ const k = key(origin, permission);
26129
+ const existing = records.find((r) => key(r.origin, r.permission) === k);
26130
+ const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
26131
+ if (existing) Object.assign(existing, { decision, updatedAt });
26132
+ else records.unshift({ origin, permission, decision, updatedAt });
26133
+ persistence.schedule();
26134
+ emit();
26135
+ }
26136
+ function listPermissions() {
26137
+ return records;
26138
+ }
26139
+ function clearPermissions() {
26140
+ records = [];
26141
+ persistence.schedule();
26142
+ emit();
26143
+ }
26144
+ function clearPermissionsForOrigin(origin) {
26145
+ records = records.filter((record) => record.origin !== origin);
26146
+ persistence.schedule();
26147
+ emit();
26148
+ }
26149
+ function setPermissionBroadcaster(fn) {
26150
+ broadcaster = fn;
26151
+ }
26152
+ function installPermissionHandler() {
26153
+ electron.session.defaultSession.setPermissionRequestHandler((webContents, permission, callback, details) => {
26154
+ const origin = new URL(details.requestingUrl || webContents.getURL()).origin;
26155
+ const existing = records.find((r) => r.origin === origin && r.permission === permission);
26156
+ if (existing) {
26157
+ callback(existing.decision === "allow");
26158
+ return;
26159
+ }
26160
+ const result = electron.dialog.showMessageBoxSync({
26161
+ type: "question",
26162
+ buttons: ["Deny", "Allow"],
26163
+ defaultId: 0,
26164
+ cancelId: 0,
26165
+ title: "Site permission request",
26166
+ message: `${origin} wants to use ${permission}`,
26167
+ detail: "Vessel will remember your choice. You can clear saved permissions in Settings > Privacy."
26168
+ });
26169
+ const decision = result === 1 ? "allow" : "deny";
26170
+ save(origin, permission, decision);
26171
+ callback(decision === "allow");
26172
+ });
26173
+ }
26174
+ const NPM_PACKAGE_URL = "https://registry.npmjs.org/@quanta-intellect%2Fvessel-browser/latest";
26175
+ const RELEASES_URL = "https://github.com/unmodeled-tyler/quanta-vessel-browser/releases/latest";
26176
+ function normalizeVersion(version) {
26177
+ return version.replace(/^v/i, "").split(/[.-]/).slice(0, 3).map((part) => {
26178
+ const n = Number.parseInt(part, 10);
26179
+ return Number.isFinite(n) ? n : 0;
26180
+ });
26181
+ }
26182
+ function compareVersions(a, b) {
26183
+ const av = normalizeVersion(a);
26184
+ const bv = normalizeVersion(b);
26185
+ for (let i = 0; i < 3; i += 1) {
26186
+ if ((av[i] ?? 0) > (bv[i] ?? 0)) return 1;
26187
+ if ((av[i] ?? 0) < (bv[i] ?? 0)) return -1;
26188
+ }
26189
+ return 0;
26190
+ }
26191
+ async function checkForUpdates() {
26192
+ const currentVersion = electron.app.getVersion();
26193
+ const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
26194
+ try {
26195
+ const response = await fetch(NPM_PACKAGE_URL, {
26196
+ headers: { accept: "application/json", "user-agent": `Vessel/${currentVersion}` }
26197
+ });
26198
+ if (!response.ok) {
26199
+ throw new Error(`Registry responded with ${response.status}`);
26200
+ }
26201
+ const body = await response.json();
26202
+ const latestVersion = typeof body.version === "string" ? body.version : null;
26203
+ if (!latestVersion) throw new Error("Registry response did not include a version");
26204
+ return {
26205
+ currentVersion,
26206
+ latestVersion,
26207
+ updateAvailable: compareVersions(latestVersion, currentVersion) > 0,
26208
+ checkedAt,
26209
+ releaseUrl: RELEASES_URL
26210
+ };
26211
+ } catch (error) {
26212
+ return {
26213
+ currentVersion,
26214
+ latestVersion: null,
26215
+ updateAvailable: false,
26216
+ checkedAt,
26217
+ releaseUrl: RELEASES_URL,
26218
+ error: error instanceof Error ? error.message : "Update check failed"
26219
+ };
26220
+ }
26221
+ }
26222
+ async function openUpdateDownload() {
26223
+ await electron.shell.openExternal(RELEASES_URL);
26224
+ }
25371
26225
  let activeChatProvider = null;
25372
26226
  const logger$4 = createLogger("IPC");
25373
26227
  const VALID_APPROVAL_MODES = ["auto", "confirm-dangerous", "manual"];
@@ -25765,18 +26619,18 @@ function registerIpcHandlers(windowState, runtime2) {
25765
26619
  return getRendererSettings();
25766
26620
  });
25767
26621
  electron.ipcMain.handle(Channels.SETTINGS_HEALTH_GET, () => getRuntimeHealth());
25768
- electron.ipcMain.handle(Channels.SETTINGS_SET, async (_, key, value) => {
25769
- assertString(key, "key");
25770
- if (!SETTABLE_KEYS.has(key)) {
25771
- throw new Error(`Unknown setting key: ${key}`);
26622
+ electron.ipcMain.handle(Channels.SETTINGS_SET, async (_, key2, value) => {
26623
+ assertString(key2, "key");
26624
+ if (!SETTABLE_KEYS.has(key2)) {
26625
+ throw new Error(`Unknown setting key: ${key2}`);
25772
26626
  }
25773
- const settingsKey = key;
26627
+ const settingsKey = key2;
25774
26628
  const updatedSettings = setSetting(settingsKey, value);
25775
- trackSettingChanged(key);
25776
- if (key === "approvalMode") {
26629
+ trackSettingChanged(key2);
26630
+ if (key2 === "approvalMode") {
25777
26631
  runtime2.setApprovalMode(value);
25778
26632
  }
25779
- if (key === "mcpPort") {
26633
+ if (key2 === "mcpPort") {
25780
26634
  await stopMcpServer();
25781
26635
  await startMcpServer(tabManager, runtime2, updatedSettings.mcpPort);
25782
26636
  }
@@ -25941,6 +26795,7 @@ function registerIpcHandlers(windowState, runtime2) {
25941
26795
  registerVaultHandlers();
25942
26796
  registerHumanVaultHandlers();
25943
26797
  registerWindowControlHandlers(mainWindow);
26798
+ registerCodexHandlers();
25944
26799
  electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, () => {
25945
26800
  return getInstalledKits();
25946
26801
  });
@@ -25969,6 +26824,26 @@ function registerIpcHandlers(windowState, runtime2) {
25969
26824
  clearByTimeRange(timeRange);
25970
26825
  }
25971
26826
  });
26827
+ setDownloadBroadcaster(sendToRendererViews);
26828
+ setPermissionBroadcaster(sendToRendererViews);
26829
+ electron.ipcMain.handle(Channels.DOWNLOADS_GET, () => listDownloads());
26830
+ electron.ipcMain.handle(Channels.DOWNLOADS_CLEAR, () => {
26831
+ clearDownloads();
26832
+ return true;
26833
+ });
26834
+ electron.ipcMain.handle(Channels.DOWNLOADS_OPEN, (_event, id) => openDownload(id));
26835
+ electron.ipcMain.handle(Channels.DOWNLOADS_SHOW_IN_FOLDER, (_event, id) => showDownloadInFolder(id));
26836
+ electron.ipcMain.handle(Channels.PERMISSIONS_GET, () => listPermissions());
26837
+ electron.ipcMain.handle(Channels.PERMISSIONS_CLEAR, () => {
26838
+ clearPermissions();
26839
+ return true;
26840
+ });
26841
+ electron.ipcMain.handle(Channels.PERMISSIONS_CLEAR_ORIGIN, (_event, origin) => {
26842
+ clearPermissionsForOrigin(origin);
26843
+ return true;
26844
+ });
26845
+ electron.ipcMain.handle(Channels.UPDATES_CHECK, () => checkForUpdates());
26846
+ electron.ipcMain.handle(Channels.UPDATES_OPEN_DOWNLOAD, () => openUpdateDownload());
25972
26847
  electron.ipcMain.handle(Channels.TAB_TOGGLE_PIP, async () => {
25973
26848
  return togglePictureInPicture(tabManager);
25974
26849
  });
@@ -26364,9 +27239,9 @@ function clone(value) {
26364
27239
  function summarizeArgs(args) {
26365
27240
  const entries = Object.entries(args).filter(([, value]) => value != null);
26366
27241
  if (entries.length === 0) return "No arguments";
26367
- return entries.map(([key, value]) => {
27242
+ return entries.map(([key2, value]) => {
26368
27243
  const rendered = typeof value === "string" ? value : JSON.stringify(value);
26369
- return `${key}=${String(rendered).slice(0, 120)}`;
27244
+ return `${key2}=${String(rendered).slice(0, 120)}`;
26370
27245
  }).join(", ");
26371
27246
  }
26372
27247
  function summarizeText(value) {
@@ -27392,6 +28267,7 @@ async function bootstrap() {
27392
28267
  sidebarView.webContents.send(Channels.HISTORY_UPDATE, state2);
27393
28268
  });
27394
28269
  installDownloadHandler(chromeView);
28270
+ installPermissionHandler();
27395
28271
  startBackgroundRevalidation();
27396
28272
  startTelemetry();
27397
28273
  const initializeChromeRenderer = () => {