@quanta-intellect/vessel-browser 0.1.114 → 0.1.116
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
|
@@ -81,7 +81,7 @@ const defaults = {
|
|
|
81
81
|
const SAVE_DEBOUNCE_MS$6 = 150;
|
|
82
82
|
const CHAT_PROVIDER_SECRET_FILENAME = "vessel-chat-provider-secret";
|
|
83
83
|
const CODEX_TOKENS_FILENAME = "vessel-codex-tokens";
|
|
84
|
-
const logger$
|
|
84
|
+
const logger$r = createLogger("Settings");
|
|
85
85
|
const SETTABLE_KEYS = new Set(Object.keys(defaults));
|
|
86
86
|
let settings = null;
|
|
87
87
|
let settingsIssues = [];
|
|
@@ -289,7 +289,7 @@ function persistNow() {
|
|
|
289
289
|
JSON.stringify(buildPersistedSettings(settings), null, 2),
|
|
290
290
|
{ encoding: "utf-8", mode: 384 }
|
|
291
291
|
)
|
|
292
|
-
).then(() => fs.promises.chmod(getSettingsPath(), 384).catch(() => void 0)).catch((err) => logger$
|
|
292
|
+
).then(() => fs.promises.chmod(getSettingsPath(), 384).catch(() => void 0)).catch((err) => logger$r.error("Failed to save settings:", err));
|
|
293
293
|
}
|
|
294
294
|
function saveSettings() {
|
|
295
295
|
saveDirty = true;
|
|
@@ -420,7 +420,7 @@ function loadTrustedAppURL(wc, url) {
|
|
|
420
420
|
}
|
|
421
421
|
const MAX_CUSTOM_HISTORY = 50;
|
|
422
422
|
const READER_MODE_DATA_URL_PREFIX = "data:text/html;charset=utf-8,";
|
|
423
|
-
const logger$
|
|
423
|
+
const logger$q = createLogger("Tab");
|
|
424
424
|
const sessionCertExceptions = /* @__PURE__ */ new WeakMap();
|
|
425
425
|
const sessionsWithVerifyProc = /* @__PURE__ */ new WeakSet();
|
|
426
426
|
const CERT_VERIFY_TRUST = 0;
|
|
@@ -486,7 +486,7 @@ class Tab {
|
|
|
486
486
|
guardedLoadURL(url, options) {
|
|
487
487
|
const blockReason = this.getNavigationBlockReason(url);
|
|
488
488
|
if (blockReason) {
|
|
489
|
-
logger$
|
|
489
|
+
logger$q.warn(blockReason);
|
|
490
490
|
return blockReason;
|
|
491
491
|
}
|
|
492
492
|
void this.view.webContents.loadURL(url, options);
|
|
@@ -570,7 +570,7 @@ class Tab {
|
|
|
570
570
|
wc.setWindowOpenHandler(({ url, disposition }) => {
|
|
571
571
|
const error = this.getNavigationBlockReason(url);
|
|
572
572
|
if (error) {
|
|
573
|
-
logger$
|
|
573
|
+
logger$q.warn(error);
|
|
574
574
|
return { action: "deny" };
|
|
575
575
|
}
|
|
576
576
|
this.onOpenUrl?.({
|
|
@@ -584,7 +584,7 @@ class Tab {
|
|
|
584
584
|
const error = this.getNavigationBlockReason(url);
|
|
585
585
|
if (!error) return;
|
|
586
586
|
event.preventDefault();
|
|
587
|
-
logger$
|
|
587
|
+
logger$q.warn(`${context}: ${error}`);
|
|
588
588
|
};
|
|
589
589
|
wc.on("will-navigate", (event, url) => {
|
|
590
590
|
blockNavigation(event, url, "Blocked top-level navigation");
|
|
@@ -668,7 +668,7 @@ class Tab {
|
|
|
668
668
|
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 999px; }
|
|
669
669
|
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.22); }
|
|
670
670
|
::-webkit-scrollbar-corner { background: transparent; }
|
|
671
|
-
`).catch((err) => logger$
|
|
671
|
+
`).catch((err) => logger$q.warn("Failed to inject scrollbar CSS:", err));
|
|
672
672
|
});
|
|
673
673
|
wc.on("page-favicon-updated", (_, favicons) => {
|
|
674
674
|
this._state.favicon = favicons[0] || "";
|
|
@@ -704,7 +704,7 @@ class Tab {
|
|
|
704
704
|
).then((highlightedText) => {
|
|
705
705
|
this.buildContextMenu(wc, params, highlightedText.trim());
|
|
706
706
|
}).catch((err) => {
|
|
707
|
-
logger$
|
|
707
|
+
logger$q.warn("Failed to inspect highlighted text for context menu:", err);
|
|
708
708
|
this.buildContextMenu(wc, params, "");
|
|
709
709
|
});
|
|
710
710
|
});
|
|
@@ -905,7 +905,7 @@ class Tab {
|
|
|
905
905
|
"document.documentElement.outerHTML"
|
|
906
906
|
);
|
|
907
907
|
} catch (err) {
|
|
908
|
-
logger$
|
|
908
|
+
logger$q.warn("Failed to retrieve page source:", err);
|
|
909
909
|
return;
|
|
910
910
|
}
|
|
911
911
|
const escaped = html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -1033,7 +1033,7 @@ class Tab {
|
|
|
1033
1033
|
document.addEventListener('mouseup', window.__vesselHighlightHandler);
|
|
1034
1034
|
}
|
|
1035
1035
|
})()
|
|
1036
|
-
`).catch((err) => logger$
|
|
1036
|
+
`).catch((err) => logger$q.warn("Failed to inject highlight listener:", err));
|
|
1037
1037
|
} else {
|
|
1038
1038
|
void wc.executeJavaScript(`
|
|
1039
1039
|
(function() {
|
|
@@ -1044,7 +1044,7 @@ class Tab {
|
|
|
1044
1044
|
delete window.__vesselHighlightHandler;
|
|
1045
1045
|
}
|
|
1046
1046
|
})()
|
|
1047
|
-
`).catch((err) => logger$
|
|
1047
|
+
`).catch((err) => logger$q.warn("Failed to remove highlight listener:", err));
|
|
1048
1048
|
}
|
|
1049
1049
|
}
|
|
1050
1050
|
get webContentsId() {
|
|
@@ -1081,7 +1081,7 @@ const SEARCH_ENGINE_PRESETS = {
|
|
|
1081
1081
|
ecosia: { label: "Ecosia", url: "https://www.ecosia.org/search?q=" },
|
|
1082
1082
|
kagi: { label: "Kagi", url: "https://kagi.com/search?q=" }
|
|
1083
1083
|
};
|
|
1084
|
-
const logger$
|
|
1084
|
+
const logger$p = createLogger("JsonPersistence");
|
|
1085
1085
|
function canUseSafeStorage() {
|
|
1086
1086
|
try {
|
|
1087
1087
|
return electron.safeStorage.isEncryptionAvailable();
|
|
@@ -1146,7 +1146,7 @@ function createDebouncedJsonPersistence({
|
|
|
1146
1146
|
data,
|
|
1147
1147
|
typeof data === "string" ? { encoding: "utf-8", mode: 384 } : { mode: 384 }
|
|
1148
1148
|
)
|
|
1149
|
-
).then(() => fs.promises.chmod(filePath2, 384).catch(() => void 0)).catch((err) => logger$
|
|
1149
|
+
).then(() => fs.promises.chmod(filePath2, 384).catch(() => void 0)).catch((err) => logger$p.error(`Failed to save ${logLabel}:`, err));
|
|
1150
1150
|
};
|
|
1151
1151
|
const schedule = () => {
|
|
1152
1152
|
saveDirty2 = true;
|
|
@@ -2838,7 +2838,7 @@ function destroySession(tabId) {
|
|
|
2838
2838
|
sessions.delete(tabId);
|
|
2839
2839
|
}
|
|
2840
2840
|
}
|
|
2841
|
-
const logger$
|
|
2841
|
+
const logger$o = createLogger("TabManager");
|
|
2842
2842
|
function sanitizePdfFilename(title) {
|
|
2843
2843
|
const clean = title.replace(/[<>:"/\\|?*\x00-\x1f]/g, " ").replace(/\s+/g, " ").trim();
|
|
2844
2844
|
const base = (clean || "Vessel Page").replace(/\.pdf$/i, "");
|
|
@@ -3238,7 +3238,7 @@ class TabManager {
|
|
|
3238
3238
|
}));
|
|
3239
3239
|
if (entries.length > 0) {
|
|
3240
3240
|
void highlightBatchOnPage(wc, entries).catch(
|
|
3241
|
-
(err) => logger$
|
|
3241
|
+
(err) => logger$o.warn("Failed to batch highlight:", err)
|
|
3242
3242
|
);
|
|
3243
3243
|
}
|
|
3244
3244
|
}
|
|
@@ -3260,12 +3260,12 @@ class TabManager {
|
|
|
3260
3260
|
const result = await captureSelectionHighlight(wc);
|
|
3261
3261
|
if (result.success && result.text) {
|
|
3262
3262
|
await highlightOnPage(wc, null, result.text, void 0, void 0, "yellow").catch(
|
|
3263
|
-
(err) => logger$
|
|
3263
|
+
(err) => logger$o.warn("Failed to capture highlight:", err)
|
|
3264
3264
|
);
|
|
3265
3265
|
}
|
|
3266
3266
|
this.highlightCaptureCallback?.(result);
|
|
3267
3267
|
} catch (err) {
|
|
3268
|
-
logger$
|
|
3268
|
+
logger$o.warn("Failed to capture highlight from page:", err);
|
|
3269
3269
|
this.highlightCaptureCallback?.({
|
|
3270
3270
|
success: false,
|
|
3271
3271
|
message: "Could not capture selection"
|
|
@@ -3290,7 +3290,7 @@ class TabManager {
|
|
|
3290
3290
|
void this.removeHighlightMarksForText(wc, text);
|
|
3291
3291
|
}
|
|
3292
3292
|
} catch (err) {
|
|
3293
|
-
logger$
|
|
3293
|
+
logger$o.warn("Failed to remove highlight from matching tab:", err);
|
|
3294
3294
|
}
|
|
3295
3295
|
}
|
|
3296
3296
|
this.highlightCaptureCallback?.({
|
|
@@ -3321,12 +3321,12 @@ class TabManager {
|
|
|
3321
3321
|
void 0,
|
|
3322
3322
|
color
|
|
3323
3323
|
).catch(
|
|
3324
|
-
(err) => logger$
|
|
3324
|
+
(err) => logger$o.warn("Failed to update highlight color:", err)
|
|
3325
3325
|
);
|
|
3326
3326
|
});
|
|
3327
3327
|
}
|
|
3328
3328
|
} catch (err) {
|
|
3329
|
-
logger$
|
|
3329
|
+
logger$o.warn("Failed to iterate highlights for color change:", err);
|
|
3330
3330
|
}
|
|
3331
3331
|
}
|
|
3332
3332
|
this.highlightCaptureCallback?.({
|
|
@@ -3381,7 +3381,7 @@ class TabManager {
|
|
|
3381
3381
|
});
|
|
3382
3382
|
})()`
|
|
3383
3383
|
).catch(
|
|
3384
|
-
(err) => logger$
|
|
3384
|
+
(err) => logger$o.warn("Failed to remove highlight marks:", err)
|
|
3385
3385
|
);
|
|
3386
3386
|
}
|
|
3387
3387
|
broadcastState(meta = { persistSession: false }) {
|
|
@@ -3614,6 +3614,10 @@ const Channels = {
|
|
|
3614
3614
|
CODEX_CANCEL_AUTH: "codex:cancel-auth",
|
|
3615
3615
|
CODEX_AUTH_STATUS: "codex:auth-status",
|
|
3616
3616
|
CODEX_DISCONNECT: "codex:disconnect",
|
|
3617
|
+
// OpenRouter OAuth
|
|
3618
|
+
OPENROUTER_START_AUTH: "openrouter:start-auth",
|
|
3619
|
+
OPENROUTER_CANCEL_AUTH: "openrouter:cancel-auth",
|
|
3620
|
+
OPENROUTER_AUTH_STATUS: "openrouter:auth-status",
|
|
3617
3621
|
// Updates
|
|
3618
3622
|
UPDATES_CHECK: "updates:check",
|
|
3619
3623
|
UPDATES_OPEN_DOWNLOAD: "updates:open-download",
|
|
@@ -4578,7 +4582,7 @@ function errorResult(error, value) {
|
|
|
4578
4582
|
function getErrorMessage(error, fallback = "Unknown error") {
|
|
4579
4583
|
return error instanceof Error && error.message ? error.message : fallback;
|
|
4580
4584
|
}
|
|
4581
|
-
const logger$
|
|
4585
|
+
const logger$n = createLogger("Premium");
|
|
4582
4586
|
const VERIFICATION_API = process.env.VESSEL_PREMIUM_API || "https://vesselpremium.quantaintellect.com";
|
|
4583
4587
|
const FREE_TOOL_ITERATION_LIMIT = 50;
|
|
4584
4588
|
const REVALIDATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -4745,7 +4749,7 @@ async function verifySubscription$1(identifier) {
|
|
|
4745
4749
|
});
|
|
4746
4750
|
if (!res.ok) {
|
|
4747
4751
|
const detail = await readApiErrorDetail(res);
|
|
4748
|
-
logger$
|
|
4752
|
+
logger$n.warn(
|
|
4749
4753
|
"Verification API returned a non-OK status:",
|
|
4750
4754
|
res.status,
|
|
4751
4755
|
detail
|
|
@@ -4764,7 +4768,7 @@ async function verifySubscription$1(identifier) {
|
|
|
4764
4768
|
setSetting("premium", updated);
|
|
4765
4769
|
return updated;
|
|
4766
4770
|
} catch (err) {
|
|
4767
|
-
logger$
|
|
4771
|
+
logger$n.warn("Verification failed:", err);
|
|
4768
4772
|
return current;
|
|
4769
4773
|
}
|
|
4770
4774
|
}
|
|
@@ -5376,7 +5380,7 @@ const EXTRACT_TIMEOUT_MAX_MS = 2e4;
|
|
|
5376
5380
|
const MUTATION_CAPTURE_INTERVAL_MS = 5e3;
|
|
5377
5381
|
const MUTATION_SETTLE_AFTER_MS = 1500;
|
|
5378
5382
|
const AGENT_STREAM_IDLE_TIMEOUT_MS = 3e4;
|
|
5379
|
-
const logger$
|
|
5383
|
+
const logger$m = createLogger("Extractor");
|
|
5380
5384
|
const EXTRACTION_CACHE_TTL_MS = 1500;
|
|
5381
5385
|
const MAX_EXTRACTION_CACHE_ENTRIES = 50;
|
|
5382
5386
|
const extractionCache = /* @__PURE__ */ new Map();
|
|
@@ -6143,9 +6147,9 @@ async function executeScript(webContents, script, options = {}) {
|
|
|
6143
6147
|
const message = err instanceof Error ? err.message : String(err);
|
|
6144
6148
|
const detail = `Failed to execute page script${label} on ${url}: ${message}`;
|
|
6145
6149
|
if (options.warnOnFailure) {
|
|
6146
|
-
logger$
|
|
6150
|
+
logger$m.warn(detail);
|
|
6147
6151
|
} else {
|
|
6148
|
-
logger$
|
|
6152
|
+
logger$m.debug(detail);
|
|
6149
6153
|
}
|
|
6150
6154
|
return null;
|
|
6151
6155
|
} finally {
|
|
@@ -6254,7 +6258,7 @@ async function estimateExtractionTimeout(webContents) {
|
|
|
6254
6258
|
return EXTRACT_TIMEOUT_BASE_MS + extra;
|
|
6255
6259
|
}
|
|
6256
6260
|
} catch (err) {
|
|
6257
|
-
logger$
|
|
6261
|
+
logger$m.warn("Failed to estimate extraction timeout, using base timeout:", err);
|
|
6258
6262
|
}
|
|
6259
6263
|
return EXTRACT_TIMEOUT_BASE_MS;
|
|
6260
6264
|
}
|
|
@@ -7481,8 +7485,9 @@ const PROVIDERS = {
|
|
|
7481
7485
|
openrouter: {
|
|
7482
7486
|
id: "openrouter",
|
|
7483
7487
|
name: "OpenRouter",
|
|
7484
|
-
defaultModel: "
|
|
7488
|
+
defaultModel: "openrouter/free",
|
|
7485
7489
|
models: [
|
|
7490
|
+
"openrouter/free",
|
|
7486
7491
|
"anthropic/claude-sonnet-4",
|
|
7487
7492
|
"anthropic/claude-haiku-4",
|
|
7488
7493
|
"openai/gpt-4o",
|
|
@@ -8028,7 +8033,7 @@ function recoverNarratedActionToolCalls(text, availableToolNames) {
|
|
|
8028
8033
|
}
|
|
8029
8034
|
return recovered;
|
|
8030
8035
|
}
|
|
8031
|
-
const logger$
|
|
8036
|
+
const logger$l = createLogger("OpenAIProvider");
|
|
8032
8037
|
function shouldDebugAgentLoop() {
|
|
8033
8038
|
const value = process.env.VESSEL_DEBUG_AGENT_LOOP;
|
|
8034
8039
|
return value === "1" || value === "true";
|
|
@@ -8296,9 +8301,9 @@ function shouldRetryCompactToolLoop(profile, text, hasToolHistory, userMessage)
|
|
|
8296
8301
|
function logAgentLoopDebug(payload) {
|
|
8297
8302
|
if (!shouldDebugAgentLoop()) return;
|
|
8298
8303
|
try {
|
|
8299
|
-
logger$
|
|
8304
|
+
logger$l.info(`[agent-debug] ${JSON.stringify(payload)}`);
|
|
8300
8305
|
} catch (err) {
|
|
8301
|
-
logger$
|
|
8306
|
+
logger$l.warn("Failed to serialize debug payload:", err);
|
|
8302
8307
|
}
|
|
8303
8308
|
}
|
|
8304
8309
|
function formatOpenAICompatErrorMessage(providerId, message) {
|
|
@@ -8732,28 +8737,184 @@ async function openExternalAllowlisted(url, rule) {
|
|
|
8732
8737
|
}
|
|
8733
8738
|
await electron.shell.openExternal(parsed.toString());
|
|
8734
8739
|
}
|
|
8735
|
-
const logger$i = createLogger("CodexOAuth");
|
|
8736
|
-
const ISSUER = "https://auth.openai.com";
|
|
8737
|
-
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
8738
|
-
const SCOPE = "openid profile email offline_access api.connectors.read api.connectors.invoke";
|
|
8739
|
-
const AUTH_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
8740
|
-
const PREFERRED_PORT = 1455;
|
|
8741
|
-
const FALLBACK_PORT = 1457;
|
|
8742
|
-
let activeFlow = null;
|
|
8743
8740
|
function base64url(buffer) {
|
|
8744
8741
|
return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
8745
8742
|
}
|
|
8746
8743
|
function generatePkce() {
|
|
8747
8744
|
const codeVerifier = base64url(crypto$1.randomBytes(64));
|
|
8748
8745
|
const hash = crypto$1.createHash("sha256").update(codeVerifier).digest();
|
|
8749
|
-
|
|
8750
|
-
|
|
8746
|
+
return {
|
|
8747
|
+
codeVerifier,
|
|
8748
|
+
codeChallenge: base64url(hash)
|
|
8749
|
+
};
|
|
8751
8750
|
}
|
|
8752
8751
|
function generateState() {
|
|
8753
8752
|
return base64url(crypto$1.randomBytes(32));
|
|
8754
8753
|
}
|
|
8755
|
-
function
|
|
8756
|
-
|
|
8754
|
+
function buildCallbackUrl(port, path2) {
|
|
8755
|
+
return `http://localhost:${port}${path2}`;
|
|
8756
|
+
}
|
|
8757
|
+
async function bindServer(server, preferredPorts) {
|
|
8758
|
+
for (const port of preferredPorts) {
|
|
8759
|
+
try {
|
|
8760
|
+
await new Promise((resolve, reject) => {
|
|
8761
|
+
const onError = (err) => {
|
|
8762
|
+
server.off("listening", onListening);
|
|
8763
|
+
reject(err);
|
|
8764
|
+
};
|
|
8765
|
+
const onListening = () => {
|
|
8766
|
+
server.off("error", onError);
|
|
8767
|
+
resolve();
|
|
8768
|
+
};
|
|
8769
|
+
server.once("error", onError);
|
|
8770
|
+
server.once("listening", onListening);
|
|
8771
|
+
server.listen(port, "127.0.0.1");
|
|
8772
|
+
});
|
|
8773
|
+
return port;
|
|
8774
|
+
} catch (err) {
|
|
8775
|
+
if (err.code === "EADDRINUSE") {
|
|
8776
|
+
continue;
|
|
8777
|
+
}
|
|
8778
|
+
throw err;
|
|
8779
|
+
}
|
|
8780
|
+
}
|
|
8781
|
+
throw new Error(
|
|
8782
|
+
`Could not bind ${preferredPorts.join(", ")} callback ports`
|
|
8783
|
+
);
|
|
8784
|
+
}
|
|
8785
|
+
function createLocalPkceOAuthFlow(config) {
|
|
8786
|
+
let activeFlow = null;
|
|
8787
|
+
const cancel = () => {
|
|
8788
|
+
if (!activeFlow) return;
|
|
8789
|
+
activeFlow.server.close();
|
|
8790
|
+
clearTimeout(activeFlow.timeout);
|
|
8791
|
+
try {
|
|
8792
|
+
activeFlow.onStatus("idle");
|
|
8793
|
+
} catch {
|
|
8794
|
+
config.logger.warn(`${config.name} OAuth cancel status callback failed`);
|
|
8795
|
+
}
|
|
8796
|
+
activeFlow = null;
|
|
8797
|
+
};
|
|
8798
|
+
const start = (onStatus) => {
|
|
8799
|
+
if (activeFlow) {
|
|
8800
|
+
throw new Error(`${config.name} auth flow already in progress`);
|
|
8801
|
+
}
|
|
8802
|
+
const pkce = generatePkce();
|
|
8803
|
+
const state2 = generateState();
|
|
8804
|
+
const callbackPath = config.callbackPath(state2);
|
|
8805
|
+
return new Promise((resolve, reject) => {
|
|
8806
|
+
let settled = false;
|
|
8807
|
+
let boundPort = 0;
|
|
8808
|
+
const safeOnStatus = (status, error) => {
|
|
8809
|
+
try {
|
|
8810
|
+
onStatus(status, error);
|
|
8811
|
+
} catch {
|
|
8812
|
+
config.logger.warn(`${config.name} OAuth status callback failed`);
|
|
8813
|
+
}
|
|
8814
|
+
};
|
|
8815
|
+
const cleanup = () => {
|
|
8816
|
+
clearTimeout(activeFlow?.timeout);
|
|
8817
|
+
activeFlow?.server.close();
|
|
8818
|
+
activeFlow = null;
|
|
8819
|
+
};
|
|
8820
|
+
const wrappedResolve = (result) => {
|
|
8821
|
+
if (settled) return;
|
|
8822
|
+
settled = true;
|
|
8823
|
+
cleanup();
|
|
8824
|
+
safeOnStatus("connected");
|
|
8825
|
+
resolve(result);
|
|
8826
|
+
};
|
|
8827
|
+
const wrappedReject = (err) => {
|
|
8828
|
+
if (settled) return;
|
|
8829
|
+
settled = true;
|
|
8830
|
+
cleanup();
|
|
8831
|
+
safeOnStatus("error", err.message);
|
|
8832
|
+
reject(err);
|
|
8833
|
+
};
|
|
8834
|
+
const server = http.createServer(async (req, res) => {
|
|
8835
|
+
const url = new URL(req.url || "/", `http://localhost:${boundPort}`);
|
|
8836
|
+
if (url.pathname === callbackPath) {
|
|
8837
|
+
const authError = config.authErrorMessage?.(url) || url.searchParams.get("error");
|
|
8838
|
+
if (authError) {
|
|
8839
|
+
res.writeHead(400, { "Content-Type": "text/plain", Connection: "close" });
|
|
8840
|
+
res.end(`Authorization failed: ${authError}`);
|
|
8841
|
+
wrappedReject(new Error(authError));
|
|
8842
|
+
return;
|
|
8843
|
+
}
|
|
8844
|
+
if (config.readState(url) !== state2) {
|
|
8845
|
+
res.writeHead(400, { "Content-Type": "text/plain", Connection: "close" });
|
|
8846
|
+
res.end("State mismatch. Please try again.");
|
|
8847
|
+
wrappedReject(new Error("State mismatch"));
|
|
8848
|
+
return;
|
|
8849
|
+
}
|
|
8850
|
+
const code = url.searchParams.get("code");
|
|
8851
|
+
if (!code) {
|
|
8852
|
+
res.writeHead(400, { "Content-Type": "text/plain", Connection: "close" });
|
|
8853
|
+
res.end("Missing authorization code.");
|
|
8854
|
+
wrappedReject(new Error("Missing authorization code"));
|
|
8855
|
+
return;
|
|
8856
|
+
}
|
|
8857
|
+
try {
|
|
8858
|
+
safeOnStatus("exchanging");
|
|
8859
|
+
const result = await config.exchangeCode({
|
|
8860
|
+
code,
|
|
8861
|
+
codeVerifier: pkce.codeVerifier,
|
|
8862
|
+
callbackUrl: buildCallbackUrl(boundPort, callbackPath),
|
|
8863
|
+
port: boundPort
|
|
8864
|
+
});
|
|
8865
|
+
res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
|
|
8866
|
+
res.end(config.successHtml(result));
|
|
8867
|
+
wrappedResolve(result);
|
|
8868
|
+
} catch (err) {
|
|
8869
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
8870
|
+
res.writeHead(400, { "Content-Type": "text/plain", Connection: "close" });
|
|
8871
|
+
res.end(`${config.name} setup failed: ${message}`);
|
|
8872
|
+
wrappedReject(err instanceof Error ? err : new Error(`${config.name} setup failed`));
|
|
8873
|
+
}
|
|
8874
|
+
return;
|
|
8875
|
+
}
|
|
8876
|
+
res.writeHead(404, { Connection: "close" });
|
|
8877
|
+
res.end("Not found");
|
|
8878
|
+
});
|
|
8879
|
+
const timeout = setTimeout(() => {
|
|
8880
|
+
wrappedReject(new Error(`${config.name} setup timed out after 5 minutes`));
|
|
8881
|
+
}, config.timeoutMs);
|
|
8882
|
+
activeFlow = {
|
|
8883
|
+
server,
|
|
8884
|
+
timeout,
|
|
8885
|
+
onStatus
|
|
8886
|
+
};
|
|
8887
|
+
bindServer(server, config.preferredPorts).then((port) => {
|
|
8888
|
+
if (settled || !activeFlow) return;
|
|
8889
|
+
boundPort = port;
|
|
8890
|
+
const callbackUrl = buildCallbackUrl(port, callbackPath);
|
|
8891
|
+
const authUrl = config.buildAuthorizeUrl({
|
|
8892
|
+
port,
|
|
8893
|
+
pkce,
|
|
8894
|
+
state: state2,
|
|
8895
|
+
callbackUrl
|
|
8896
|
+
});
|
|
8897
|
+
safeOnStatus("waiting");
|
|
8898
|
+
openExternalAllowlisted(authUrl, { hosts: [...config.openHosts] }).catch((err) => {
|
|
8899
|
+
config.logger.warn(`Failed to open ${config.name} auth URL:`, err);
|
|
8900
|
+
});
|
|
8901
|
+
}).catch(wrappedReject);
|
|
8902
|
+
});
|
|
8903
|
+
};
|
|
8904
|
+
return {
|
|
8905
|
+
start,
|
|
8906
|
+
cancel,
|
|
8907
|
+
isInProgress: () => activeFlow !== null
|
|
8908
|
+
};
|
|
8909
|
+
}
|
|
8910
|
+
const logger$k = createLogger("CodexOAuth");
|
|
8911
|
+
const ISSUER = "https://auth.openai.com";
|
|
8912
|
+
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
8913
|
+
const SCOPE = "openid profile email offline_access api.connectors.read api.connectors.invoke";
|
|
8914
|
+
const AUTH_TIMEOUT_MS$1 = 5 * 60 * 1e3;
|
|
8915
|
+
const PREFERRED_PORT$1 = 1455;
|
|
8916
|
+
const FALLBACK_PORT$1 = 1457;
|
|
8917
|
+
function buildAuthorizeUrl(redirectUri, pkce, state2) {
|
|
8757
8918
|
const params = new URLSearchParams({
|
|
8758
8919
|
response_type: "code",
|
|
8759
8920
|
client_id: CLIENT_ID,
|
|
@@ -8872,172 +9033,36 @@ async function refreshAccessToken(tokens) {
|
|
|
8872
9033
|
};
|
|
8873
9034
|
return refreshedTokens;
|
|
8874
9035
|
}
|
|
8875
|
-
function startServer(port, pkce, expectedState, resolve, reject) {
|
|
8876
|
-
const server = http.createServer(async (req, res) => {
|
|
8877
|
-
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
8878
|
-
if (url.pathname === "/auth/callback") {
|
|
8879
|
-
const state2 = url.searchParams.get("state");
|
|
8880
|
-
const code = url.searchParams.get("code");
|
|
8881
|
-
const error = url.searchParams.get("error");
|
|
8882
|
-
const errorDescription = url.searchParams.get("error_description");
|
|
8883
|
-
if (error) {
|
|
8884
|
-
res.writeHead(400, { "Content-Type": "text/plain", "Connection": "close" });
|
|
8885
|
-
const msg = errorDescription || error;
|
|
8886
|
-
res.end(`Authorization failed: ${msg}`);
|
|
8887
|
-
reject(new Error(msg));
|
|
8888
|
-
return;
|
|
8889
|
-
}
|
|
8890
|
-
if (state2 !== expectedState) {
|
|
8891
|
-
res.writeHead(400, { "Content-Type": "text/plain", "Connection": "close" });
|
|
8892
|
-
res.end("State mismatch. Please try again.");
|
|
8893
|
-
reject(new Error("State mismatch"));
|
|
8894
|
-
return;
|
|
8895
|
-
}
|
|
8896
|
-
if (!code) {
|
|
8897
|
-
res.writeHead(400, { "Content-Type": "text/plain", "Connection": "close" });
|
|
8898
|
-
res.end("Missing authorization code.");
|
|
8899
|
-
reject(new Error("Missing authorization code"));
|
|
8900
|
-
return;
|
|
8901
|
-
}
|
|
8902
|
-
try {
|
|
8903
|
-
activeFlow?.onStatus("exchanging");
|
|
8904
|
-
const redirectUri = `http://localhost:${activeFlow?.port ?? port}/auth/callback`;
|
|
8905
|
-
const tokens = await exchangeCodeForTokens(code, redirectUri, pkce.codeVerifier);
|
|
8906
|
-
res.writeHead(302, {
|
|
8907
|
-
Location: `/success?email=${encodeURIComponent(tokens.accountEmail || tokens.accountId)}`,
|
|
8908
|
-
Connection: "close"
|
|
8909
|
-
});
|
|
8910
|
-
res.end();
|
|
8911
|
-
resolve(tokens);
|
|
8912
|
-
} catch (err) {
|
|
8913
|
-
res.writeHead(400, { "Content-Type": "text/plain", "Connection": "close" });
|
|
8914
|
-
res.end(`Token exchange failed: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
8915
|
-
reject(err instanceof Error ? err : new Error("Token exchange failed"));
|
|
8916
|
-
}
|
|
8917
|
-
return;
|
|
8918
|
-
}
|
|
8919
|
-
if (url.pathname === "/success") {
|
|
8920
|
-
const email = url.searchParams.get("email") || "";
|
|
8921
|
-
res.writeHead(200, { "Content-Type": "text/html", "Connection": "close" });
|
|
8922
|
-
res.end(`<!DOCTYPE html>
|
|
8923
|
-
<html><head><meta charset="utf-8"><title>Vessel — Signed In</title>
|
|
8924
|
-
<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>
|
|
8925
|
-
<body><div style="text-align:center"><h1>✓ Signed In</h1>
|
|
8926
|
-
<p>Connected as ${escapeHtml(email)}</p><p>You can close this tab.</p></div></body></html>`);
|
|
8927
|
-
return;
|
|
8928
|
-
}
|
|
8929
|
-
if (url.pathname === "/cancel") {
|
|
8930
|
-
res.writeHead(200, { "Content-Type": "text/plain", "Connection": "close" });
|
|
8931
|
-
res.end("Login cancelled");
|
|
8932
|
-
reject(new Error("Login cancelled by user"));
|
|
8933
|
-
return;
|
|
8934
|
-
}
|
|
8935
|
-
res.writeHead(404, { "Connection": "close" });
|
|
8936
|
-
res.end("Not found");
|
|
8937
|
-
});
|
|
8938
|
-
return server;
|
|
8939
|
-
}
|
|
8940
9036
|
function escapeHtml(text) {
|
|
8941
9037
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
8942
9038
|
}
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
continue;
|
|
8964
|
-
}
|
|
8965
|
-
throw err;
|
|
8966
|
-
}
|
|
8967
|
-
}
|
|
8968
|
-
throw new Error(
|
|
8969
|
-
`Could not bind Codex OAuth callback server to registered ports ${allowedPorts.join(", ")}`
|
|
8970
|
-
);
|
|
8971
|
-
}
|
|
9039
|
+
const codexOAuth = createLocalPkceOAuthFlow({
|
|
9040
|
+
name: "Codex",
|
|
9041
|
+
logger: logger$k,
|
|
9042
|
+
preferredPorts: [PREFERRED_PORT$1, FALLBACK_PORT$1],
|
|
9043
|
+
timeoutMs: AUTH_TIMEOUT_MS$1,
|
|
9044
|
+
callbackPath: () => "/auth/callback",
|
|
9045
|
+
readState: (url) => url.searchParams.get("state"),
|
|
9046
|
+
authErrorMessage: (url) => url.searchParams.get("error_description") || url.searchParams.get("error"),
|
|
9047
|
+
buildAuthorizeUrl: ({ callbackUrl, pkce, state: state2 }) => buildAuthorizeUrl(callbackUrl, pkce, state2),
|
|
9048
|
+
exchangeCode: ({ code, callbackUrl, codeVerifier }) => exchangeCodeForTokens(code, callbackUrl, codeVerifier),
|
|
9049
|
+
successHtml: (tokens) => {
|
|
9050
|
+
const label = escapeHtml(tokens.accountEmail || tokens.accountId);
|
|
9051
|
+
return `<!DOCTYPE html>
|
|
9052
|
+
<html><head><meta charset="utf-8"><title>Vessel — Signed In</title>
|
|
9053
|
+
<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>
|
|
9054
|
+
<body><div style="text-align:center"><h1>Signed In</h1>
|
|
9055
|
+
<p>Connected as ${label}</p><p>You can close this tab.</p></div></body></html>`;
|
|
9056
|
+
},
|
|
9057
|
+
openHosts: ["auth.openai.com"]
|
|
9058
|
+
});
|
|
8972
9059
|
async function startCodexOAuth(onStatus) {
|
|
8973
|
-
|
|
8974
|
-
throw new Error("Auth flow already in progress");
|
|
8975
|
-
}
|
|
8976
|
-
const pkce = generatePkce();
|
|
8977
|
-
const state2 = generateState();
|
|
8978
|
-
return new Promise((resolve, reject) => {
|
|
8979
|
-
let settled = false;
|
|
8980
|
-
const safeOnStatus = (status, error) => {
|
|
8981
|
-
try {
|
|
8982
|
-
onStatus(status, error);
|
|
8983
|
-
} catch {
|
|
8984
|
-
logger$i.warn("Codex OAuth status callback failed — window may be closed");
|
|
8985
|
-
}
|
|
8986
|
-
};
|
|
8987
|
-
const wrappedResolve = (tokens) => {
|
|
8988
|
-
if (settled) return;
|
|
8989
|
-
settled = true;
|
|
8990
|
-
cleanup();
|
|
8991
|
-
safeOnStatus("connected");
|
|
8992
|
-
resolve(tokens);
|
|
8993
|
-
};
|
|
8994
|
-
const wrappedReject = (err) => {
|
|
8995
|
-
if (settled) return;
|
|
8996
|
-
settled = true;
|
|
8997
|
-
cleanup();
|
|
8998
|
-
safeOnStatus("error", err.message);
|
|
8999
|
-
reject(err);
|
|
9000
|
-
};
|
|
9001
|
-
const server = startServer(0, pkce, state2, wrappedResolve, wrappedReject);
|
|
9002
|
-
const timeout = setTimeout(() => {
|
|
9003
|
-
wrappedReject(new Error("Auth flow timed out after 5 minutes"));
|
|
9004
|
-
}, AUTH_TIMEOUT_MS);
|
|
9005
|
-
activeFlow = {
|
|
9006
|
-
state: state2,
|
|
9007
|
-
codeVerifier: pkce.codeVerifier,
|
|
9008
|
-
port: 0,
|
|
9009
|
-
server,
|
|
9010
|
-
timeout,
|
|
9011
|
-
onStatus
|
|
9012
|
-
};
|
|
9013
|
-
const cleanup = () => {
|
|
9014
|
-
if (activeFlow?.timeout) clearTimeout(activeFlow.timeout);
|
|
9015
|
-
activeFlow?.server.close();
|
|
9016
|
-
activeFlow = null;
|
|
9017
|
-
};
|
|
9018
|
-
bindServer(server).then((port) => {
|
|
9019
|
-
if (settled) return;
|
|
9020
|
-
activeFlow.port = port;
|
|
9021
|
-
const authUrl = buildAuthorizeUrl(port, pkce, state2);
|
|
9022
|
-
safeOnStatus("waiting");
|
|
9023
|
-
openExternalAllowlisted(authUrl, { hosts: ["auth.openai.com"] }).catch((err) => {
|
|
9024
|
-
logger$i.warn("Failed to open browser, user will need the URL:", err);
|
|
9025
|
-
});
|
|
9026
|
-
}).catch(wrappedReject);
|
|
9027
|
-
});
|
|
9060
|
+
return codexOAuth.start(onStatus);
|
|
9028
9061
|
}
|
|
9029
9062
|
function cancelCodexOAuth() {
|
|
9030
|
-
|
|
9031
|
-
activeFlow.server.close();
|
|
9032
|
-
if (activeFlow.timeout) clearTimeout(activeFlow.timeout);
|
|
9033
|
-
try {
|
|
9034
|
-
activeFlow.onStatus("idle");
|
|
9035
|
-
} catch {
|
|
9036
|
-
logger$i.warn("Codex OAuth cancel status callback failed — window may be closed");
|
|
9037
|
-
}
|
|
9038
|
-
activeFlow = null;
|
|
9063
|
+
codexOAuth.cancel();
|
|
9039
9064
|
}
|
|
9040
|
-
const logger$
|
|
9065
|
+
const logger$j = createLogger("CodexProvider");
|
|
9041
9066
|
const REFRESH_WINDOW_MS = 5 * 60 * 1e3;
|
|
9042
9067
|
const CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api/codex";
|
|
9043
9068
|
const CODEX_CLIENT_VERSION = "0.129.0";
|
|
@@ -9102,7 +9127,7 @@ class CodexProvider {
|
|
|
9102
9127
|
async ensureFreshTokens() {
|
|
9103
9128
|
if (Date.now() < this.tokens.expiresAt - REFRESH_WINDOW_MS) return;
|
|
9104
9129
|
try {
|
|
9105
|
-
logger$
|
|
9130
|
+
logger$j.info("Refreshing Codex access token");
|
|
9106
9131
|
const fresh = await refreshAccessToken(this.tokens);
|
|
9107
9132
|
this.tokens = fresh;
|
|
9108
9133
|
writeStoredCodexTokens(fresh);
|
|
@@ -9250,7 +9275,7 @@ class CodexProvider {
|
|
|
9250
9275
|
} catch (err) {
|
|
9251
9276
|
if (err.name !== "AbortError") {
|
|
9252
9277
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9253
|
-
logger$
|
|
9278
|
+
logger$j.error("Codex streamQuery error:", err);
|
|
9254
9279
|
onChunk(`
|
|
9255
9280
|
|
|
9256
9281
|
[Error: ${msg}]`);
|
|
@@ -9318,7 +9343,7 @@ class CodexProvider {
|
|
|
9318
9343
|
} catch (err) {
|
|
9319
9344
|
if (err.name !== "AbortError") {
|
|
9320
9345
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9321
|
-
logger$
|
|
9346
|
+
logger$j.error("Codex streamAgentQuery error:", err);
|
|
9322
9347
|
onChunk(`
|
|
9323
9348
|
|
|
9324
9349
|
[Error: ${msg}]`);
|
|
@@ -9510,7 +9535,7 @@ function createProvider(config) {
|
|
|
9510
9535
|
return new OpenAICompatProvider(normalized);
|
|
9511
9536
|
}
|
|
9512
9537
|
const require$1 = node_module.createRequire(require("url").pathToFileURL(__filename).href);
|
|
9513
|
-
const logger$
|
|
9538
|
+
const logger$i = createLogger("DevTrace");
|
|
9514
9539
|
let cachedFactory;
|
|
9515
9540
|
function createNoopTraceSession() {
|
|
9516
9541
|
return {
|
|
@@ -9543,7 +9568,7 @@ function loadLocalFactory() {
|
|
|
9543
9568
|
return cachedFactory;
|
|
9544
9569
|
}
|
|
9545
9570
|
} catch (err) {
|
|
9546
|
-
logger$
|
|
9571
|
+
logger$i.warn("Failed to load local trace logger:", err);
|
|
9547
9572
|
}
|
|
9548
9573
|
}
|
|
9549
9574
|
return cachedFactory;
|
|
@@ -13671,7 +13696,7 @@ function formatDeadLinkMessage(label, result) {
|
|
|
13671
13696
|
const status = result.statusCode ? `HTTP ${result.statusCode}` : "dead link";
|
|
13672
13697
|
return `Skipped stale link "${label}" because ${destination} returned ${status}. Try a different link or URL instead.`;
|
|
13673
13698
|
}
|
|
13674
|
-
const logger$
|
|
13699
|
+
const logger$h = createLogger("Screenshot");
|
|
13675
13700
|
const SCREENSHOT_RETRY_COUNT = 3;
|
|
13676
13701
|
const SCREENSHOT_RETRY_BASE_DELAY_MS = 120;
|
|
13677
13702
|
async function captureScreenshot(wc) {
|
|
@@ -13693,7 +13718,7 @@ async function captureScreenshot(wc) {
|
|
|
13693
13718
|
}
|
|
13694
13719
|
}
|
|
13695
13720
|
} catch (err) {
|
|
13696
|
-
logger$
|
|
13721
|
+
logger$h.debug(
|
|
13697
13722
|
`capturePage attempt ${attempt + 1} failed; retrying if attempts remain.`,
|
|
13698
13723
|
getErrorMessage(err)
|
|
13699
13724
|
);
|
|
@@ -14669,7 +14694,7 @@ class TabMutex {
|
|
|
14669
14694
|
return run;
|
|
14670
14695
|
}
|
|
14671
14696
|
}
|
|
14672
|
-
const logger$
|
|
14697
|
+
const logger$g = createLogger("PageActions");
|
|
14673
14698
|
function getBookmarkMetadataFromArgs(args) {
|
|
14674
14699
|
return normalizeBookmarkMetadata({
|
|
14675
14700
|
intent: args.intent ?? args.intent,
|
|
@@ -14855,7 +14880,7 @@ async function executePageScript(wc, script, options) {
|
|
|
14855
14880
|
return result;
|
|
14856
14881
|
} catch (err) {
|
|
14857
14882
|
const label = options?.label ? ` (${options.label})` : "";
|
|
14858
|
-
logger$
|
|
14883
|
+
logger$g.warn(`Failed to execute page script${label}:`, err);
|
|
14859
14884
|
return null;
|
|
14860
14885
|
} finally {
|
|
14861
14886
|
if (timer) {
|
|
@@ -14956,7 +14981,7 @@ Search results snapshot:
|
|
|
14956
14981
|
${truncated}`;
|
|
14957
14982
|
}
|
|
14958
14983
|
} catch (err) {
|
|
14959
|
-
logger$
|
|
14984
|
+
logger$g.warn("Failed to build post-search summary, falling back to nav summary:", err);
|
|
14960
14985
|
}
|
|
14961
14986
|
const fallback = await getPostNavSummary(wc);
|
|
14962
14987
|
return fallback ? `${fallback}
|
|
@@ -14979,7 +15004,7 @@ Page snapshot after navigation:
|
|
|
14979
15004
|
${truncated}`;
|
|
14980
15005
|
}
|
|
14981
15006
|
} catch (err) {
|
|
14982
|
-
logger$
|
|
15007
|
+
logger$g.warn("Failed to build post-click navigation summary:", err);
|
|
14983
15008
|
}
|
|
14984
15009
|
return "";
|
|
14985
15010
|
}
|
|
@@ -15473,7 +15498,7 @@ async function restoreLocaleSnapshot(wc, snapshot2) {
|
|
|
15473
15498
|
}
|
|
15474
15499
|
}
|
|
15475
15500
|
} catch (err) {
|
|
15476
|
-
logger$
|
|
15501
|
+
logger$g.warn("Failed to restore locale via history navigation, trying URL reload fallback:", err);
|
|
15477
15502
|
}
|
|
15478
15503
|
if (snapshot2.url && snapshot2.url !== wc.getURL()) {
|
|
15479
15504
|
try {
|
|
@@ -15482,7 +15507,7 @@ async function restoreLocaleSnapshot(wc, snapshot2) {
|
|
|
15482
15507
|
await waitForLoad(wc, 3e3);
|
|
15483
15508
|
return;
|
|
15484
15509
|
} catch (err) {
|
|
15485
|
-
logger$
|
|
15510
|
+
logger$g.warn("Failed to restore locale via safe URL load, trying page reload fallback:", err);
|
|
15486
15511
|
}
|
|
15487
15512
|
}
|
|
15488
15513
|
if (snapshot2.url) {
|
|
@@ -15490,7 +15515,7 @@ async function restoreLocaleSnapshot(wc, snapshot2) {
|
|
|
15490
15515
|
await wc.reload();
|
|
15491
15516
|
await waitForLoad(wc, 3e3);
|
|
15492
15517
|
} catch (err) {
|
|
15493
|
-
logger$
|
|
15518
|
+
logger$g.warn("Failed to restore locale via page reload:", err);
|
|
15494
15519
|
}
|
|
15495
15520
|
}
|
|
15496
15521
|
}
|
|
@@ -15760,7 +15785,7 @@ ${postActivationOverlayHint}`;
|
|
|
15760
15785
|
return `${clickText} -> ${hrefFallbackUrl} (recovered via href fallback)`;
|
|
15761
15786
|
}
|
|
15762
15787
|
} catch (err) {
|
|
15763
|
-
logger$
|
|
15788
|
+
logger$g.warn("Failed href fallback after click, returning generic click result:", err);
|
|
15764
15789
|
}
|
|
15765
15790
|
}
|
|
15766
15791
|
}
|
|
@@ -15805,7 +15830,7 @@ async function tryAutoDismissCartDialog(wc) {
|
|
|
15805
15830
|
return result;
|
|
15806
15831
|
}
|
|
15807
15832
|
} catch (err) {
|
|
15808
|
-
logger$
|
|
15833
|
+
logger$g.warn("Failed to auto-dismiss cart dialog, falling back to dialog actions:", err);
|
|
15809
15834
|
}
|
|
15810
15835
|
return null;
|
|
15811
15836
|
}
|
|
@@ -18088,7 +18113,7 @@ async function executeAction(name, args, ctx) {
|
|
|
18088
18113
|
)
|
|
18089
18114
|
]);
|
|
18090
18115
|
} catch (err) {
|
|
18091
|
-
logger$
|
|
18116
|
+
logger$g.warn("Failed to extract content for read_page, falling back to lighter recovery:", err);
|
|
18092
18117
|
content = null;
|
|
18093
18118
|
}
|
|
18094
18119
|
if (!content || content.content.length === 0) {
|
|
@@ -18105,12 +18130,12 @@ async function executeAction(name, args, ctx) {
|
|
|
18105
18130
|
new Promise((resolve) => setTimeout(() => resolve(null), 3e3))
|
|
18106
18131
|
]);
|
|
18107
18132
|
} catch (err) {
|
|
18108
|
-
logger$
|
|
18133
|
+
logger$g.warn("Failed to re-extract content after iframe consent dismissal:", err);
|
|
18109
18134
|
content = null;
|
|
18110
18135
|
}
|
|
18111
18136
|
}
|
|
18112
18137
|
} catch (err) {
|
|
18113
|
-
logger$
|
|
18138
|
+
logger$g.warn("Failed iframe consent dismissal during read_page recovery:", err);
|
|
18114
18139
|
}
|
|
18115
18140
|
}
|
|
18116
18141
|
if (content && content.content.length > 0) {
|
|
@@ -18523,7 +18548,7 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
|
|
|
18523
18548
|
try {
|
|
18524
18549
|
page = await extractContent(wc);
|
|
18525
18550
|
} catch (err) {
|
|
18526
|
-
logger$
|
|
18551
|
+
logger$g.warn("Failed to extract content for suggest:", err);
|
|
18527
18552
|
return "Could not read page. Try navigate to a working URL.";
|
|
18528
18553
|
}
|
|
18529
18554
|
const suggestions = [];
|
|
@@ -19972,7 +19997,7 @@ Exception: ${result.exceptionDetails}`);
|
|
|
19972
19997
|
}
|
|
19973
19998
|
);
|
|
19974
19999
|
}
|
|
19975
|
-
const logger$
|
|
20000
|
+
const logger$f = createLogger("VaultShared");
|
|
19976
20001
|
const ALGORITHM = "aes-256-gcm";
|
|
19977
20002
|
const IV_LENGTH = 12;
|
|
19978
20003
|
const AUTH_TAG_LENGTH = 16;
|
|
@@ -20066,7 +20091,7 @@ function createVaultIO(vaultFilename, encrypt2, decrypt2) {
|
|
|
20066
20091
|
cachedEntries = JSON.parse(json);
|
|
20067
20092
|
return cachedEntries;
|
|
20068
20093
|
} catch (err) {
|
|
20069
|
-
logger$
|
|
20094
|
+
logger$f.error("Failed to load vault:", err);
|
|
20070
20095
|
throw new Error("Could not unlock the vault. Check OS secret storage availability.");
|
|
20071
20096
|
}
|
|
20072
20097
|
}
|
|
@@ -20149,7 +20174,7 @@ function createAuditLog(filename, maxEntries) {
|
|
|
20149
20174
|
} catch {
|
|
20150
20175
|
}
|
|
20151
20176
|
} catch (err) {
|
|
20152
|
-
logger$
|
|
20177
|
+
logger$f.error("Failed to write audit log:", err);
|
|
20153
20178
|
}
|
|
20154
20179
|
}
|
|
20155
20180
|
function readAuditLog2(limit = 100) {
|
|
@@ -20159,7 +20184,7 @@ function createAuditLog(filename, maxEntries) {
|
|
|
20159
20184
|
const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
|
|
20160
20185
|
return lines.slice(-Math.min(limit, maxEntries)).map((line) => JSON.parse(line)).reverse();
|
|
20161
20186
|
} catch (err) {
|
|
20162
|
-
logger$
|
|
20187
|
+
logger$f.error("Failed to read audit log:", err);
|
|
20163
20188
|
return [];
|
|
20164
20189
|
}
|
|
20165
20190
|
}
|
|
@@ -20263,7 +20288,7 @@ async function requestConsent(request) {
|
|
|
20263
20288
|
}
|
|
20264
20289
|
const AUDIT_FILENAME = "vessel-vault-audit.jsonl";
|
|
20265
20290
|
const MAX_ENTRIES = 1e3;
|
|
20266
|
-
const logger$
|
|
20291
|
+
const logger$e = createLogger("VaultAudit");
|
|
20267
20292
|
function getAuditPath() {
|
|
20268
20293
|
return path$1.join(electron.app.getPath("userData"), AUDIT_FILENAME);
|
|
20269
20294
|
}
|
|
@@ -20277,7 +20302,7 @@ function appendAuditEntry(entry) {
|
|
|
20277
20302
|
});
|
|
20278
20303
|
fs$1.chmodSync(auditPath, 384);
|
|
20279
20304
|
} catch (err) {
|
|
20280
|
-
logger$
|
|
20305
|
+
logger$e.error("Failed to write audit log:", err);
|
|
20281
20306
|
}
|
|
20282
20307
|
}
|
|
20283
20308
|
function readAuditLog$1(limit = 100) {
|
|
@@ -20287,7 +20312,7 @@ function readAuditLog$1(limit = 100) {
|
|
|
20287
20312
|
const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
|
|
20288
20313
|
return lines.slice(-Math.min(limit, MAX_ENTRIES)).map((line) => JSON.parse(line)).reverse();
|
|
20289
20314
|
} catch (err) {
|
|
20290
|
-
logger$
|
|
20315
|
+
logger$e.error("Failed to read audit log:", err);
|
|
20291
20316
|
return [];
|
|
20292
20317
|
}
|
|
20293
20318
|
}
|
|
@@ -20455,7 +20480,7 @@ async function requestHumanVaultConsent(request) {
|
|
|
20455
20480
|
}
|
|
20456
20481
|
let httpServer = null;
|
|
20457
20482
|
let mcpAuthToken = null;
|
|
20458
|
-
const logger$
|
|
20483
|
+
const logger$d = createLogger("MCP");
|
|
20459
20484
|
const MCP_AUTH_FILENAME = "mcp-auth.json";
|
|
20460
20485
|
function getMcpAuthFilePath() {
|
|
20461
20486
|
const configDir = process.env.VESSEL_CONFIG_DIR || path$1.join(
|
|
@@ -20492,7 +20517,7 @@ function writeMcpAuthFile(endpoint, token) {
|
|
|
20492
20517
|
);
|
|
20493
20518
|
fs$1.chmodSync(filePath2, 384);
|
|
20494
20519
|
} catch (err) {
|
|
20495
|
-
logger$
|
|
20520
|
+
logger$d.warn("Failed to write auth file:", err);
|
|
20496
20521
|
}
|
|
20497
20522
|
}
|
|
20498
20523
|
function clearMcpAuthFile() {
|
|
@@ -20518,7 +20543,7 @@ function clearMcpAuthFile() {
|
|
|
20518
20543
|
);
|
|
20519
20544
|
fs$1.chmodSync(filePath2, 384);
|
|
20520
20545
|
} catch (err) {
|
|
20521
|
-
logger$
|
|
20546
|
+
logger$d.warn("Failed to clear auth file:", err);
|
|
20522
20547
|
}
|
|
20523
20548
|
}
|
|
20524
20549
|
function regenerateMcpAuthToken() {
|
|
@@ -20628,7 +20653,7 @@ async function getPostActionState(tabManager, name) {
|
|
|
20628
20653
|
}
|
|
20629
20654
|
}
|
|
20630
20655
|
} catch (err) {
|
|
20631
|
-
logger$
|
|
20656
|
+
logger$d.warn("Failed to compute post-action state warning:", err);
|
|
20632
20657
|
}
|
|
20633
20658
|
return `${warning}
|
|
20634
20659
|
[state: url=${wc.getURL()}, canGoBack=${tab.canGoBack()}, canGoForward=${tab.canGoForward()}, loading=${wc.isLoading()}]`;
|
|
@@ -20733,7 +20758,7 @@ async function waitForConditionMcp(wc, text, selector, timeoutMs) {
|
|
|
20733
20758
|
}
|
|
20734
20759
|
})()
|
|
20735
20760
|
`).catch((err) => {
|
|
20736
|
-
logger$
|
|
20761
|
+
logger$d.warn("Failed to gather wait_for timeout diagnostic:", err);
|
|
20737
20762
|
return null;
|
|
20738
20763
|
});
|
|
20739
20764
|
if (typeof diagnostic === "string" && diagnostic.trim()) {
|
|
@@ -20820,7 +20845,7 @@ function registerTools(server, tabManager, runtime2) {
|
|
|
20820
20845
|
const page = await extractContent(wc);
|
|
20821
20846
|
pageType = detectPageType(page);
|
|
20822
20847
|
} catch (err) {
|
|
20823
|
-
logger$
|
|
20848
|
+
logger$d.warn("Failed to detect page type for tool scoring, falling back to GENERAL:", err);
|
|
20824
20849
|
}
|
|
20825
20850
|
}
|
|
20826
20851
|
const scored = TOOL_DEFINITIONS.map((def) => {
|
|
@@ -22220,7 +22245,7 @@ ${JSON.stringify(otherHighlights, null, 2)}`
|
|
|
22220
22245
|
void 0,
|
|
22221
22246
|
h.color
|
|
22222
22247
|
).catch(
|
|
22223
|
-
(err) => logger$
|
|
22248
|
+
(err) => logger$d.warn("Failed to restore highlight after removal:", err)
|
|
22224
22249
|
);
|
|
22225
22250
|
}
|
|
22226
22251
|
}
|
|
@@ -23076,7 +23101,7 @@ ${flow.steps.map((s, i) => ` ${i === 0 ? "→" : " "} ${s.label}`).join("\n")}`
|
|
|
23076
23101
|
try {
|
|
23077
23102
|
page = await extractContent(wc);
|
|
23078
23103
|
} catch (err) {
|
|
23079
|
-
logger$
|
|
23104
|
+
logger$d.warn("Failed to extract page while generating suggestions:", err);
|
|
23080
23105
|
return asTextResponse(
|
|
23081
23106
|
"Could not read page. Try navigate to a working URL."
|
|
23082
23107
|
);
|
|
@@ -23687,7 +23712,7 @@ ${JSON.stringify(tableJson, null, 2)}`;
|
|
|
23687
23712
|
try {
|
|
23688
23713
|
targetDomain = new URL(tab.state.url).hostname;
|
|
23689
23714
|
} catch (err) {
|
|
23690
|
-
logger$
|
|
23715
|
+
logger$d.warn("Failed to parse active tab URL for vault_status:", err);
|
|
23691
23716
|
return asErrorTextResponse("Could not parse active tab URL");
|
|
23692
23717
|
}
|
|
23693
23718
|
}
|
|
@@ -23755,7 +23780,7 @@ Use vault_login to fill the login form. Credentials are filled directly — you
|
|
|
23755
23780
|
try {
|
|
23756
23781
|
hostname = new URL(tab.state.url).hostname;
|
|
23757
23782
|
} catch (err) {
|
|
23758
|
-
logger$
|
|
23783
|
+
logger$d.warn("Failed to parse active tab URL for vault_login:", err);
|
|
23759
23784
|
return asErrorTextResponse("Could not parse active tab URL");
|
|
23760
23785
|
}
|
|
23761
23786
|
const matches = findEntriesForDomain(`https://${hostname}`);
|
|
@@ -23851,7 +23876,7 @@ Use vault_login to fill the login form. Credentials are filled directly — you
|
|
|
23851
23876
|
try {
|
|
23852
23877
|
hostname = new URL(tab.state.url).hostname;
|
|
23853
23878
|
} catch (err) {
|
|
23854
|
-
logger$
|
|
23879
|
+
logger$d.warn("Failed to parse active tab URL for vault_totp:", err);
|
|
23855
23880
|
return asErrorTextResponse("Could not parse active tab URL");
|
|
23856
23881
|
}
|
|
23857
23882
|
const matches = findEntriesForDomain(`https://${hostname}`);
|
|
@@ -24199,7 +24224,7 @@ function startMcpServer(tabManager, runtime2, port) {
|
|
|
24199
24224
|
await mcpServer.connect(transport);
|
|
24200
24225
|
await transport.handleRequest(req, res);
|
|
24201
24226
|
} catch (error) {
|
|
24202
|
-
logger$
|
|
24227
|
+
logger$d.error("Error handling request:", error);
|
|
24203
24228
|
if (!res.headersSent) {
|
|
24204
24229
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
24205
24230
|
res.end(
|
|
@@ -24218,7 +24243,7 @@ function startMcpServer(tabManager, runtime2, port) {
|
|
|
24218
24243
|
};
|
|
24219
24244
|
server.once("error", (error) => {
|
|
24220
24245
|
const message = error.code === "EADDRINUSE" ? `Port ${port} is already in use. MCP server not started.` : error.message;
|
|
24221
|
-
logger$
|
|
24246
|
+
logger$d.error("Server error:", error);
|
|
24222
24247
|
clearMcpAuthFile();
|
|
24223
24248
|
setMcpHealth({
|
|
24224
24249
|
configuredPort: port,
|
|
@@ -24250,7 +24275,7 @@ function startMcpServer(tabManager, runtime2, port) {
|
|
|
24250
24275
|
message: `MCP server listening on ${endpoint}.`
|
|
24251
24276
|
});
|
|
24252
24277
|
if (process.env.VESSEL_DEBUG_MCP === "1" || process.env.VESSEL_DEBUG_MCP === "true") {
|
|
24253
|
-
logger$
|
|
24278
|
+
logger$d.info(`Server listening on ${endpoint} (auth enabled)`);
|
|
24254
24279
|
}
|
|
24255
24280
|
if (mcpAuthToken) {
|
|
24256
24281
|
writeMcpAuthFile(endpoint, mcpAuthToken);
|
|
@@ -24289,7 +24314,7 @@ function stopMcpServer() {
|
|
|
24289
24314
|
message: "MCP server is stopped."
|
|
24290
24315
|
});
|
|
24291
24316
|
if (process.env.VESSEL_DEBUG_MCP === "1" || process.env.VESSEL_DEBUG_MCP === "true") {
|
|
24292
|
-
logger$
|
|
24317
|
+
logger$d.info("Server stopped");
|
|
24293
24318
|
}
|
|
24294
24319
|
resolve();
|
|
24295
24320
|
});
|
|
@@ -24310,7 +24335,7 @@ const KIT_ID_UNSAFE_CHAR_PATTERN = /[\/\\\0]/;
|
|
|
24310
24335
|
function isSafeAutomationKitId(id) {
|
|
24311
24336
|
return id.length > 0 && !KIT_ID_UNSAFE_CHAR_PATTERN.test(id);
|
|
24312
24337
|
}
|
|
24313
|
-
const logger$
|
|
24338
|
+
const logger$c = createLogger("KitRegistry");
|
|
24314
24339
|
function getUserKitsDir() {
|
|
24315
24340
|
return path$1.join(electron.app.getPath("userData"), "kits");
|
|
24316
24341
|
}
|
|
@@ -24348,10 +24373,10 @@ function getInstalledKits() {
|
|
|
24348
24373
|
if (isValidKit(parsed)) {
|
|
24349
24374
|
kits.push(parsed);
|
|
24350
24375
|
} else {
|
|
24351
|
-
logger$
|
|
24376
|
+
logger$c.warn(`Skipping invalid kit file: ${file}`);
|
|
24352
24377
|
}
|
|
24353
24378
|
} catch (err) {
|
|
24354
|
-
logger$
|
|
24379
|
+
logger$c.warn(`Failed to read kit file: ${file}`, err);
|
|
24355
24380
|
}
|
|
24356
24381
|
}
|
|
24357
24382
|
return kits;
|
|
@@ -24460,7 +24485,7 @@ function getActiveTabInfo(tabManager) {
|
|
|
24460
24485
|
if (wc.isDestroyed()) return null;
|
|
24461
24486
|
return { tab, wc };
|
|
24462
24487
|
}
|
|
24463
|
-
const logger$
|
|
24488
|
+
const logger$b = createLogger("Scheduler");
|
|
24464
24489
|
let jobs = [];
|
|
24465
24490
|
let pollInterval = null;
|
|
24466
24491
|
let alignStartTimeout = null;
|
|
@@ -24493,7 +24518,7 @@ function saveJobs() {
|
|
|
24493
24518
|
});
|
|
24494
24519
|
fs$1.chmodSync(jobsPath, 384);
|
|
24495
24520
|
} catch (err) {
|
|
24496
|
-
logger$
|
|
24521
|
+
logger$b.warn("Failed to save jobs:", err);
|
|
24497
24522
|
}
|
|
24498
24523
|
}
|
|
24499
24524
|
function normalizeJob(job, now = /* @__PURE__ */ new Date()) {
|
|
@@ -24617,7 +24642,7 @@ async function fireJob(job, windowState, runtime2) {
|
|
|
24617
24642
|
};
|
|
24618
24643
|
startActivity();
|
|
24619
24644
|
if (!settings2.chatProvider) {
|
|
24620
|
-
logger$
|
|
24645
|
+
logger$b.warn(`Job "${job.kitName}" skipped — no chat provider configured`);
|
|
24621
24646
|
appendActivity(
|
|
24622
24647
|
"Chat provider not configured. Open Settings (Ctrl+,) to choose a provider."
|
|
24623
24648
|
);
|
|
@@ -24625,7 +24650,7 @@ async function fireJob(job, windowState, runtime2) {
|
|
|
24625
24650
|
return;
|
|
24626
24651
|
}
|
|
24627
24652
|
if (process.env.VESSEL_DEBUG_SCHEDULER === "1" || process.env.VESSEL_DEBUG_SCHEDULER === "true") {
|
|
24628
|
-
logger$
|
|
24653
|
+
logger$b.info(`Firing scheduled job: ${job.kitName} (${job.id})`);
|
|
24629
24654
|
}
|
|
24630
24655
|
try {
|
|
24631
24656
|
const provider = createProvider(settings2.chatProvider);
|
|
@@ -24678,7 +24703,7 @@ function tick(windowState, runtime2) {
|
|
|
24678
24703
|
saveJobs();
|
|
24679
24704
|
broadcastFn?.(Channels.SCHEDULE_JOBS_UPDATE, jobs);
|
|
24680
24705
|
void fireJob(job, windowState, runtime2).catch((err) => {
|
|
24681
|
-
logger$
|
|
24706
|
+
logger$b.warn("Unexpected error firing job:", err);
|
|
24682
24707
|
}).finally(fireNext);
|
|
24683
24708
|
};
|
|
24684
24709
|
fireNext();
|
|
@@ -25280,7 +25305,7 @@ function renderReportAsMarkdown(report, traces) {
|
|
|
25280
25305
|
}
|
|
25281
25306
|
return sections.join("\n");
|
|
25282
25307
|
}
|
|
25283
|
-
const logger$
|
|
25308
|
+
const logger$a = createLogger("ResearchIPC");
|
|
25284
25309
|
function registerResearchHandlers(getOrchestrator) {
|
|
25285
25310
|
electron.ipcMain.handle(Channels.RESEARCH_STATE_GET, () => {
|
|
25286
25311
|
return getOrchestrator().getState();
|
|
@@ -25299,7 +25324,7 @@ function registerResearchHandlers(getOrchestrator) {
|
|
|
25299
25324
|
await getOrchestrator().startBrief(trimmedQuery);
|
|
25300
25325
|
return { accepted: true };
|
|
25301
25326
|
} catch (err) {
|
|
25302
|
-
logger$
|
|
25327
|
+
logger$a.error("RESEARCH_START_BRIEF failed", err);
|
|
25303
25328
|
return { accepted: false, reason: "error" };
|
|
25304
25329
|
}
|
|
25305
25330
|
}
|
|
@@ -25316,7 +25341,7 @@ function registerResearchHandlers(getOrchestrator) {
|
|
|
25316
25341
|
orchestrator.confirmBrief();
|
|
25317
25342
|
return { accepted: true };
|
|
25318
25343
|
} catch (err) {
|
|
25319
|
-
logger$
|
|
25344
|
+
logger$a.error("RESEARCH_CONFIRM_BRIEF failed", err);
|
|
25320
25345
|
return { accepted: false, reason: "error" };
|
|
25321
25346
|
}
|
|
25322
25347
|
});
|
|
@@ -25337,11 +25362,11 @@ function registerResearchHandlers(getOrchestrator) {
|
|
|
25337
25362
|
options.includeTraces
|
|
25338
25363
|
);
|
|
25339
25364
|
orchestrator.executeSubAgents().catch((err) => {
|
|
25340
|
-
logger$
|
|
25365
|
+
logger$a.error("Background sub-agent execution failed", err);
|
|
25341
25366
|
});
|
|
25342
25367
|
return { accepted: true };
|
|
25343
25368
|
} catch (err) {
|
|
25344
|
-
logger$
|
|
25369
|
+
logger$a.error("RESEARCH_APPROVE_OBJECTIVES failed", err);
|
|
25345
25370
|
return { accepted: false, reason: "error" };
|
|
25346
25371
|
}
|
|
25347
25372
|
}
|
|
@@ -25391,7 +25416,7 @@ function registerResearchHandlers(getOrchestrator) {
|
|
|
25391
25416
|
await promises.writeFile(filePath2, markdown, "utf-8");
|
|
25392
25417
|
return { accepted: true, savedPath: filePath2 };
|
|
25393
25418
|
} catch (err) {
|
|
25394
|
-
logger$
|
|
25419
|
+
logger$a.error("RESEARCH_EXPORT_REPORT failed", err);
|
|
25395
25420
|
return { accepted: false, reason: "error" };
|
|
25396
25421
|
}
|
|
25397
25422
|
});
|
|
@@ -25486,7 +25511,7 @@ RULES:
|
|
|
25486
25511
|
4. Omit empty arrays entirely (contradictions, gaps) — do not include "contradictions": [] if there are none.
|
|
25487
25512
|
5. Do not use emojis.`;
|
|
25488
25513
|
}
|
|
25489
|
-
const logger$
|
|
25514
|
+
const logger$9 = createLogger("ResearchOrchestrator");
|
|
25490
25515
|
const MAX_THREADS = 5;
|
|
25491
25516
|
const MAX_TRACE_ARGS_CHARS = 1200;
|
|
25492
25517
|
const MAX_TRACE_RESULT_CHARS = 2e3;
|
|
@@ -25675,7 +25700,7 @@ class ResearchOrchestrator {
|
|
|
25675
25700
|
}
|
|
25676
25701
|
stopAndSynthesizeCurrentFindings() {
|
|
25677
25702
|
if (this.state.phase !== "executing") {
|
|
25678
|
-
logger$
|
|
25703
|
+
logger$9.warn("Not executing, ignoring stopAndSynthesizeCurrentFindings");
|
|
25679
25704
|
return;
|
|
25680
25705
|
}
|
|
25681
25706
|
this.stopRequested = true;
|
|
@@ -25709,23 +25734,23 @@ class ResearchOrchestrator {
|
|
|
25709
25734
|
async startBrief(userQuery) {
|
|
25710
25735
|
const query = userQuery.trim();
|
|
25711
25736
|
if (!query) {
|
|
25712
|
-
logger$
|
|
25737
|
+
logger$9.warn("Ignoring empty Research Desk query");
|
|
25713
25738
|
return;
|
|
25714
25739
|
}
|
|
25715
25740
|
if (this.state.phase !== "idle") {
|
|
25716
|
-
logger$
|
|
25741
|
+
logger$9.warn("Research already in progress, ignoring startBrief");
|
|
25717
25742
|
return;
|
|
25718
25743
|
}
|
|
25719
25744
|
this.state = this.initialState();
|
|
25720
25745
|
this.state.originalQuery = query;
|
|
25721
25746
|
this.state.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
25722
25747
|
this.setPhase("briefing");
|
|
25723
|
-
logger$
|
|
25748
|
+
logger$9.info(`Brief started for query: ${query.slice(0, 120)}`);
|
|
25724
25749
|
}
|
|
25725
25750
|
// ── phase: briefing → planning ─────────────────────────────────
|
|
25726
25751
|
confirmBrief() {
|
|
25727
25752
|
if (this.state.phase !== "briefing") {
|
|
25728
|
-
logger$
|
|
25753
|
+
logger$9.warn("Not in briefing phase, ignoring confirmBrief");
|
|
25729
25754
|
return;
|
|
25730
25755
|
}
|
|
25731
25756
|
this.setPhase("planning");
|
|
@@ -25733,7 +25758,7 @@ class ResearchOrchestrator {
|
|
|
25733
25758
|
// ── phase: planning → awaiting_approval ────────────────────────
|
|
25734
25759
|
setObjectives(objectives) {
|
|
25735
25760
|
if (this.state.phase !== "planning") {
|
|
25736
|
-
logger$
|
|
25761
|
+
logger$9.warn("Not in planning phase, ignoring setObjectives");
|
|
25737
25762
|
return;
|
|
25738
25763
|
}
|
|
25739
25764
|
const threads = objectives.threads.slice(0, MAX_THREADS).map(mergeBlockedSourceDomains);
|
|
@@ -25762,11 +25787,11 @@ class ResearchOrchestrator {
|
|
|
25762
25787
|
try {
|
|
25763
25788
|
const parsed = JSON.parse(json);
|
|
25764
25789
|
if (typeof parsed.researchQuestion !== "string" || !parsed.researchQuestion.trim()) {
|
|
25765
|
-
logger$
|
|
25790
|
+
logger$9.warn("Missing researchQuestion in objectives JSON");
|
|
25766
25791
|
return false;
|
|
25767
25792
|
}
|
|
25768
25793
|
if (!Array.isArray(parsed.threads) || parsed.threads.length === 0) {
|
|
25769
|
-
logger$
|
|
25794
|
+
logger$9.warn("Missing or empty threads array in objectives JSON");
|
|
25770
25795
|
return false;
|
|
25771
25796
|
}
|
|
25772
25797
|
const threads = parsed.threads.map((t, i) => {
|
|
@@ -25784,7 +25809,7 @@ class ResearchOrchestrator {
|
|
|
25784
25809
|
};
|
|
25785
25810
|
}).filter((thread) => thread.question && thread.searchQueries.length > 0).slice(0, MAX_THREADS);
|
|
25786
25811
|
if (threads.length === 0) {
|
|
25787
|
-
logger$
|
|
25812
|
+
logger$9.warn("Objectives JSON did not contain any valid research threads");
|
|
25788
25813
|
return false;
|
|
25789
25814
|
}
|
|
25790
25815
|
const objectives = {
|
|
@@ -25795,17 +25820,17 @@ class ResearchOrchestrator {
|
|
|
25795
25820
|
totalSourceBudget: threads.reduce((sum, t) => sum + t.sourceBudget, 0)
|
|
25796
25821
|
};
|
|
25797
25822
|
this.setObjectives(objectives);
|
|
25798
|
-
logger$
|
|
25823
|
+
logger$9.info(`Parsed ${objectives.threads.length} threads from objectives`);
|
|
25799
25824
|
return true;
|
|
25800
25825
|
} catch (err) {
|
|
25801
|
-
logger$
|
|
25826
|
+
logger$9.warn("Failed to parse objectives JSON", err);
|
|
25802
25827
|
return false;
|
|
25803
25828
|
}
|
|
25804
25829
|
}
|
|
25805
25830
|
// ── phase: awaiting_approval → executing ───────────────────────
|
|
25806
25831
|
approveObjectives(mode, includeTraces) {
|
|
25807
25832
|
if (this.state.phase !== "awaiting_approval") {
|
|
25808
|
-
logger$
|
|
25833
|
+
logger$9.warn("Not awaiting approval, ignoring approveObjectives");
|
|
25809
25834
|
return;
|
|
25810
25835
|
}
|
|
25811
25836
|
if (mode) this.state.supervisionMode = mode;
|
|
@@ -25840,7 +25865,7 @@ class ResearchOrchestrator {
|
|
|
25840
25865
|
this.state.threads.map((thread) => {
|
|
25841
25866
|
if (this.state.phase !== "executing") return null;
|
|
25842
25867
|
return this.runSubAgent(thread, tabMutex).catch((err) => {
|
|
25843
|
-
logger$
|
|
25868
|
+
logger$9.error(`Sub-agent "${thread.label}" failed`, err);
|
|
25844
25869
|
return {
|
|
25845
25870
|
threadLabel: thread.label,
|
|
25846
25871
|
threadQuestion: thread.question,
|
|
@@ -25869,7 +25894,7 @@ class ResearchOrchestrator {
|
|
|
25869
25894
|
try {
|
|
25870
25895
|
await this.synthesizeReport();
|
|
25871
25896
|
} catch (err) {
|
|
25872
|
-
logger$
|
|
25897
|
+
logger$9.error("Auto-synthesis failed", err);
|
|
25873
25898
|
this.state.error = `Synthesis failed: ${String(err)}`;
|
|
25874
25899
|
this.setPhase("delivered");
|
|
25875
25900
|
}
|
|
@@ -25980,7 +26005,7 @@ Start by searching for: ${thread.searchQueries.join(" or ")}`;
|
|
|
25980
26005
|
try {
|
|
25981
26006
|
this.tabManager.closeTab(tabId);
|
|
25982
26007
|
} catch (err) {
|
|
25983
|
-
logger$
|
|
26008
|
+
logger$9.warn(`Failed to close sub-agent tab ${tabId}`, err);
|
|
25984
26009
|
}
|
|
25985
26010
|
}
|
|
25986
26011
|
}
|
|
@@ -25989,7 +26014,7 @@ Start by searching for: ${thread.searchQueries.join(" or ")}`;
|
|
|
25989
26014
|
try {
|
|
25990
26015
|
claims = await this.extractClaimsFromTranscript(thread, transcript);
|
|
25991
26016
|
} catch (err) {
|
|
25992
|
-
logger$
|
|
26017
|
+
logger$9.warn(`Claim extraction failed for "${thread.label}"`, err);
|
|
25993
26018
|
}
|
|
25994
26019
|
}
|
|
25995
26020
|
if (this.state.phase === "executing" && this.state.includeTraces) {
|
|
@@ -26079,7 +26104,7 @@ ${transcript.slice(0, 32e3)}`;
|
|
|
26079
26104
|
(claim) => claim.claim && claim.sourceUrl && claim.extractedQuote
|
|
26080
26105
|
);
|
|
26081
26106
|
} catch {
|
|
26082
|
-
logger$
|
|
26107
|
+
logger$9.warn(`Failed to parse claims JSON for "${thread.label}"`);
|
|
26083
26108
|
return [];
|
|
26084
26109
|
}
|
|
26085
26110
|
}
|
|
@@ -26164,7 +26189,7 @@ ${transcript.slice(0, 32e3)}`;
|
|
|
26164
26189
|
objectives
|
|
26165
26190
|
};
|
|
26166
26191
|
} catch (err) {
|
|
26167
|
-
logger$
|
|
26192
|
+
logger$9.warn("Failed to parse synthesis JSON, using sourced fallback report", err);
|
|
26168
26193
|
return buildFallbackReport(objectives, findings, String(err));
|
|
26169
26194
|
}
|
|
26170
26195
|
}
|
|
@@ -26903,7 +26928,7 @@ function createFindInPageBridge(tabManager, chromeView) {
|
|
|
26903
26928
|
}
|
|
26904
26929
|
};
|
|
26905
26930
|
}
|
|
26906
|
-
const logger$
|
|
26931
|
+
const logger$8 = createLogger("PrivateWindow");
|
|
26907
26932
|
const privateWindows = /* @__PURE__ */ new Set();
|
|
26908
26933
|
function layoutPrivateViews(state2) {
|
|
26909
26934
|
const { window: win, chromeView, tabManager } = state2;
|
|
@@ -27126,7 +27151,7 @@ function createPrivateWindow() {
|
|
|
27126
27151
|
privateSession.clearStorageData(),
|
|
27127
27152
|
privateSession.clearCache()
|
|
27128
27153
|
]).catch((error) => {
|
|
27129
|
-
logger$
|
|
27154
|
+
logger$8.warn("Failed to clear private browsing session:", error);
|
|
27130
27155
|
});
|
|
27131
27156
|
});
|
|
27132
27157
|
privateWindows.add(state2);
|
|
@@ -27136,7 +27161,7 @@ function createPrivateWindow() {
|
|
|
27136
27161
|
});
|
|
27137
27162
|
loadPrivateRenderer(chromeView);
|
|
27138
27163
|
win.show();
|
|
27139
|
-
logger$
|
|
27164
|
+
logger$8.info("Private browsing window opened");
|
|
27140
27165
|
return state2;
|
|
27141
27166
|
}
|
|
27142
27167
|
const secondaryWindows = /* @__PURE__ */ new Set();
|
|
@@ -27836,7 +27861,7 @@ function registerSecurityHandlers(tabManager) {
|
|
|
27836
27861
|
tabManager.goBackToSafety(tabId);
|
|
27837
27862
|
});
|
|
27838
27863
|
}
|
|
27839
|
-
const logger$
|
|
27864
|
+
const logger$7 = createLogger("CodexIPC");
|
|
27840
27865
|
function registerCodexHandlers() {
|
|
27841
27866
|
electron.ipcMain.handle(Channels.CODEX_START_AUTH, async (event) => {
|
|
27842
27867
|
assertTrustedIpcSender(event);
|
|
@@ -27851,7 +27876,7 @@ function registerCodexHandlers() {
|
|
|
27851
27876
|
try {
|
|
27852
27877
|
wc.send(Channels.CODEX_AUTH_STATUS, { status, error: error || null });
|
|
27853
27878
|
} catch {
|
|
27854
|
-
logger$
|
|
27879
|
+
logger$7.warn("Codex auth status send failed — window may be closed");
|
|
27855
27880
|
}
|
|
27856
27881
|
};
|
|
27857
27882
|
try {
|
|
@@ -27863,7 +27888,7 @@ function registerCodexHandlers() {
|
|
|
27863
27888
|
accountId: tokens.accountId
|
|
27864
27889
|
};
|
|
27865
27890
|
} catch (err) {
|
|
27866
|
-
logger$
|
|
27891
|
+
logger$7.error("Codex auth failed:", err);
|
|
27867
27892
|
return {
|
|
27868
27893
|
ok: false,
|
|
27869
27894
|
error: err instanceof Error ? err.message : "Unknown error"
|
|
@@ -27881,6 +27906,119 @@ function registerCodexHandlers() {
|
|
|
27881
27906
|
return { ok: true };
|
|
27882
27907
|
});
|
|
27883
27908
|
}
|
|
27909
|
+
const logger$6 = createLogger("OpenRouterOAuth");
|
|
27910
|
+
const AUTH_BASE_URL = "https://openrouter.ai/auth";
|
|
27911
|
+
const KEY_EXCHANGE_URL = "https://openrouter.ai/api/v1/auth/keys";
|
|
27912
|
+
const AUTH_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
27913
|
+
const PREFERRED_PORT = 1460;
|
|
27914
|
+
const FALLBACK_PORT = 1461;
|
|
27915
|
+
async function exchangeCodeForApiKey(code, codeVerifier) {
|
|
27916
|
+
const response = await fetch(KEY_EXCHANGE_URL, {
|
|
27917
|
+
method: "POST",
|
|
27918
|
+
headers: {
|
|
27919
|
+
"Content-Type": "application/json"
|
|
27920
|
+
},
|
|
27921
|
+
body: JSON.stringify({
|
|
27922
|
+
code,
|
|
27923
|
+
code_verifier: codeVerifier,
|
|
27924
|
+
code_challenge_method: "S256"
|
|
27925
|
+
})
|
|
27926
|
+
});
|
|
27927
|
+
if (!response.ok) {
|
|
27928
|
+
let errorMsg = `OpenRouter key exchange failed: ${response.status}`;
|
|
27929
|
+
try {
|
|
27930
|
+
const payload2 = await response.json();
|
|
27931
|
+
if (typeof payload2.error === "string") {
|
|
27932
|
+
errorMsg = payload2.error;
|
|
27933
|
+
} else if (typeof payload2.message === "string") {
|
|
27934
|
+
errorMsg = payload2.message;
|
|
27935
|
+
}
|
|
27936
|
+
} catch {
|
|
27937
|
+
}
|
|
27938
|
+
throw new Error(errorMsg);
|
|
27939
|
+
}
|
|
27940
|
+
const payload = await response.json();
|
|
27941
|
+
if (typeof payload.key !== "string" || !payload.key.trim()) {
|
|
27942
|
+
throw new Error("OpenRouter did not return an API key");
|
|
27943
|
+
}
|
|
27944
|
+
return payload.key.trim();
|
|
27945
|
+
}
|
|
27946
|
+
const openRouterOAuth = createLocalPkceOAuthFlow({
|
|
27947
|
+
name: "OpenRouter",
|
|
27948
|
+
logger: logger$6,
|
|
27949
|
+
preferredPorts: [PREFERRED_PORT, FALLBACK_PORT],
|
|
27950
|
+
timeoutMs: AUTH_TIMEOUT_MS,
|
|
27951
|
+
callbackPath: (state2) => `/auth/openrouter/callback/${state2}`,
|
|
27952
|
+
readState: (url) => decodeURIComponent(url.pathname.split("/").pop() || ""),
|
|
27953
|
+
buildAuthorizeUrl: ({ callbackUrl, pkce }) => {
|
|
27954
|
+
const params = new URLSearchParams({
|
|
27955
|
+
callback_url: callbackUrl,
|
|
27956
|
+
code_challenge: pkce.codeChallenge,
|
|
27957
|
+
code_challenge_method: "S256"
|
|
27958
|
+
});
|
|
27959
|
+
return `${AUTH_BASE_URL}?${params.toString()}`;
|
|
27960
|
+
},
|
|
27961
|
+
exchangeCode: ({ code, codeVerifier }) => exchangeCodeForApiKey(code, codeVerifier),
|
|
27962
|
+
successHtml: () => `<!DOCTYPE html>
|
|
27963
|
+
<html><head><meta charset="utf-8"><title>Vessel OpenRouter Setup</title>
|
|
27964
|
+
<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>
|
|
27965
|
+
<body><div style="text-align:center"><h1>OpenRouter connected</h1><p>Vessel is ready. You can close this tab.</p></div></body></html>`,
|
|
27966
|
+
openHosts: ["openrouter.ai"]
|
|
27967
|
+
});
|
|
27968
|
+
function startOpenRouterOAuth(onStatus) {
|
|
27969
|
+
return openRouterOAuth.start(onStatus);
|
|
27970
|
+
}
|
|
27971
|
+
function cancelOpenRouterOAuth() {
|
|
27972
|
+
openRouterOAuth.cancel();
|
|
27973
|
+
}
|
|
27974
|
+
const logger$5 = createLogger("OpenRouterIPC");
|
|
27975
|
+
function registerOpenRouterHandlers(applySettingChange) {
|
|
27976
|
+
electron.ipcMain.handle(Channels.OPENROUTER_START_AUTH, async (event) => {
|
|
27977
|
+
assertTrustedIpcSender(event);
|
|
27978
|
+
const wc = event.sender;
|
|
27979
|
+
if (!wc || wc.isDestroyed()) {
|
|
27980
|
+
return {
|
|
27981
|
+
ok: false,
|
|
27982
|
+
error: "No active window found for sender"
|
|
27983
|
+
};
|
|
27984
|
+
}
|
|
27985
|
+
const sendStatus = (status, error) => {
|
|
27986
|
+
try {
|
|
27987
|
+
wc.send(Channels.OPENROUTER_AUTH_STATUS, { status, error: error || null });
|
|
27988
|
+
} catch {
|
|
27989
|
+
logger$5.warn("OpenRouter auth status send failed - window may be closed");
|
|
27990
|
+
}
|
|
27991
|
+
};
|
|
27992
|
+
try {
|
|
27993
|
+
const apiKey = await startOpenRouterOAuth(sendStatus);
|
|
27994
|
+
const openRouterConfig = {
|
|
27995
|
+
id: "openrouter",
|
|
27996
|
+
apiKey,
|
|
27997
|
+
hasApiKey: true,
|
|
27998
|
+
model: PROVIDERS.openrouter.defaultModel,
|
|
27999
|
+
baseUrl: PROVIDERS.openrouter.defaultBaseUrl,
|
|
28000
|
+
reasoningEffort: "off"
|
|
28001
|
+
};
|
|
28002
|
+
await applySettingChange("chatProvider", openRouterConfig);
|
|
28003
|
+
return {
|
|
28004
|
+
ok: true,
|
|
28005
|
+
providerId: "openrouter",
|
|
28006
|
+
model: openRouterConfig.model
|
|
28007
|
+
};
|
|
28008
|
+
} catch (err) {
|
|
28009
|
+
logger$5.error("OpenRouter auth failed:", err);
|
|
28010
|
+
return {
|
|
28011
|
+
ok: false,
|
|
28012
|
+
error: err instanceof Error ? err.message : "Unknown error"
|
|
28013
|
+
};
|
|
28014
|
+
}
|
|
28015
|
+
});
|
|
28016
|
+
electron.ipcMain.handle(Channels.OPENROUTER_CANCEL_AUTH, (event) => {
|
|
28017
|
+
assertTrustedIpcSender(event);
|
|
28018
|
+
cancelOpenRouterOAuth();
|
|
28019
|
+
return { ok: true };
|
|
28020
|
+
});
|
|
28021
|
+
}
|
|
27884
28022
|
const SUPPORT_API = process.env.VESSEL_SUPPORT_API || process.env.VESSEL_PREMIUM_API || "https://vesselpremium.quantaintellect.com";
|
|
27885
28023
|
const MAX_FEEDBACK_MESSAGE_LENGTH = 5e3;
|
|
27886
28024
|
const FEEDBACK_REQUEST_TIMEOUT_MS = 15e3;
|
|
@@ -28250,6 +28388,26 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
28250
28388
|
const count = await getActiveHighlightCountSafe();
|
|
28251
28389
|
sendToRendererViews(Channels.HIGHLIGHT_COUNT_UPDATE, count);
|
|
28252
28390
|
};
|
|
28391
|
+
const applySettingChange = async (key2, value) => {
|
|
28392
|
+
const updatedSettings = setSetting(key2, value);
|
|
28393
|
+
trackSettingChanged(key2);
|
|
28394
|
+
if (key2 === "approvalMode") {
|
|
28395
|
+
runtime2.setApprovalMode(value);
|
|
28396
|
+
}
|
|
28397
|
+
if (key2 === "mcpPort") {
|
|
28398
|
+
await stopMcpServer();
|
|
28399
|
+
await startMcpServer(tabManager, runtime2, updatedSettings.mcpPort);
|
|
28400
|
+
}
|
|
28401
|
+
if (key2 === "chatProvider" && researchOrchestrator) {
|
|
28402
|
+
try {
|
|
28403
|
+
researchOrchestrator.setProvider(createProvider(value));
|
|
28404
|
+
} catch {
|
|
28405
|
+
}
|
|
28406
|
+
}
|
|
28407
|
+
const rendererSettings = getRendererSettings();
|
|
28408
|
+
sendToRendererViews(Channels.SETTINGS_UPDATE, rendererSettings);
|
|
28409
|
+
return rendererSettings;
|
|
28410
|
+
};
|
|
28253
28411
|
runtime2.setUpdateListener((state2) => {
|
|
28254
28412
|
scheduleRuntimeUpdate(state2);
|
|
28255
28413
|
});
|
|
@@ -28600,24 +28758,7 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
28600
28758
|
throw new Error(`Unknown setting key: ${key2}`);
|
|
28601
28759
|
}
|
|
28602
28760
|
const settingsKey = key2;
|
|
28603
|
-
|
|
28604
|
-
trackSettingChanged(key2);
|
|
28605
|
-
if (key2 === "approvalMode") {
|
|
28606
|
-
runtime2.setApprovalMode(value);
|
|
28607
|
-
}
|
|
28608
|
-
if (key2 === "mcpPort") {
|
|
28609
|
-
await stopMcpServer();
|
|
28610
|
-
await startMcpServer(tabManager, runtime2, updatedSettings.mcpPort);
|
|
28611
|
-
}
|
|
28612
|
-
if (key2 === "chatProvider" && researchOrchestrator) {
|
|
28613
|
-
try {
|
|
28614
|
-
researchOrchestrator.setProvider(createProvider(value));
|
|
28615
|
-
} catch {
|
|
28616
|
-
}
|
|
28617
|
-
}
|
|
28618
|
-
const rendererSettings = getRendererSettings();
|
|
28619
|
-
sendToRendererViews(Channels.SETTINGS_UPDATE, rendererSettings);
|
|
28620
|
-
return rendererSettings;
|
|
28761
|
+
return applySettingChange(settingsKey, value);
|
|
28621
28762
|
});
|
|
28622
28763
|
electron.ipcMain.handle(Channels.AGENT_RUNTIME_GET, (event) => {
|
|
28623
28764
|
requireTrusted(event);
|
|
@@ -28806,6 +28947,7 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
28806
28947
|
registerHumanVaultHandlers();
|
|
28807
28948
|
registerWindowControlHandlers(mainWindow);
|
|
28808
28949
|
registerCodexHandlers();
|
|
28950
|
+
registerOpenRouterHandlers(applySettingChange);
|
|
28809
28951
|
electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, (event) => {
|
|
28810
28952
|
requireTrusted(event);
|
|
28811
28953
|
return getInstalledKits();
|