@quanta-intellect/vessel-browser 0.1.92 → 0.1.95
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/README.md +0 -4
- package/out/main/index.js +955 -418
- package/out/preload/index.js +2 -0
- package/out/renderer/assets/{index-DRzbebKR.js → index-BP-7cF0c.js} +117 -91
- package/out/renderer/assets/{index-BFKx2klB.css → index-Bf1d0lUq.css} +0 -17
- package/out/renderer/index.html +3 -3
- package/package.json +14 -6
package/out/main/index.js
CHANGED
|
@@ -104,10 +104,23 @@ function canUseSafeStorage$1() {
|
|
|
104
104
|
return false;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
+
function writePrivateFile(filePath2, data) {
|
|
108
|
+
fs.writeFileSync(filePath2, data, { mode: 384 });
|
|
109
|
+
try {
|
|
110
|
+
fs.chmodSync(filePath2, 384);
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function assertSafeStorageAvailable() {
|
|
115
|
+
if (!canUseSafeStorage$1()) {
|
|
116
|
+
throw new Error("OS-backed secret storage is unavailable; refusing to store secrets on disk.");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
107
119
|
function readStoredProviderSecret() {
|
|
108
120
|
try {
|
|
121
|
+
if (!canUseSafeStorage$1()) return null;
|
|
109
122
|
const raw = fs.readFileSync(getChatProviderSecretPath());
|
|
110
|
-
const decoded =
|
|
123
|
+
const decoded = electron.safeStorage.decryptString(raw);
|
|
111
124
|
const parsed = JSON.parse(decoded);
|
|
112
125
|
if (parsed && typeof parsed === "object" && typeof parsed.providerId === "string" && typeof parsed.apiKey === "string") {
|
|
113
126
|
return parsed;
|
|
@@ -117,15 +130,12 @@ function readStoredProviderSecret() {
|
|
|
117
130
|
return null;
|
|
118
131
|
}
|
|
119
132
|
function writeStoredProviderSecret(secret) {
|
|
133
|
+
assertSafeStorageAvailable();
|
|
120
134
|
const filePath2 = getChatProviderSecretPath();
|
|
121
135
|
fs.mkdirSync(path.dirname(filePath2), { recursive: true });
|
|
122
136
|
const payload = JSON.stringify(secret);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
fs.writeFileSync(filePath2, encrypted, { mode: 384 });
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
fs.writeFileSync(filePath2, payload, { mode: 384 });
|
|
137
|
+
const encrypted = electron.safeStorage.encryptString(payload);
|
|
138
|
+
writePrivateFile(filePath2, encrypted);
|
|
129
139
|
}
|
|
130
140
|
function clearStoredProviderSecret() {
|
|
131
141
|
try {
|
|
@@ -138,8 +148,9 @@ function getCodexTokensPath() {
|
|
|
138
148
|
}
|
|
139
149
|
function readStoredCodexTokens() {
|
|
140
150
|
try {
|
|
151
|
+
if (!canUseSafeStorage$1()) return null;
|
|
141
152
|
const raw = fs.readFileSync(getCodexTokensPath());
|
|
142
|
-
const decoded =
|
|
153
|
+
const decoded = electron.safeStorage.decryptString(raw);
|
|
143
154
|
const parsed = JSON.parse(decoded);
|
|
144
155
|
if (parsed && typeof parsed === "object" && typeof parsed.accessToken === "string" && typeof parsed.refreshToken === "string") {
|
|
145
156
|
return parsed;
|
|
@@ -149,15 +160,12 @@ function readStoredCodexTokens() {
|
|
|
149
160
|
return null;
|
|
150
161
|
}
|
|
151
162
|
function writeStoredCodexTokens(tokens) {
|
|
163
|
+
assertSafeStorageAvailable();
|
|
152
164
|
const filePath2 = getCodexTokensPath();
|
|
153
165
|
fs.mkdirSync(path.dirname(filePath2), { recursive: true });
|
|
154
166
|
const payload = JSON.stringify(tokens);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
fs.writeFileSync(filePath2, encrypted, { mode: 384 });
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
fs.writeFileSync(filePath2, payload, { mode: 384 });
|
|
167
|
+
const encrypted = electron.safeStorage.encryptString(payload);
|
|
168
|
+
writePrivateFile(filePath2, encrypted);
|
|
161
169
|
}
|
|
162
170
|
function clearStoredCodexTokens() {
|
|
163
171
|
try {
|
|
@@ -268,9 +276,10 @@ function persistNow() {
|
|
|
268
276
|
return fs.promises.mkdir(path.dirname(getSettingsPath()), { recursive: true }).then(
|
|
269
277
|
() => fs.promises.writeFile(
|
|
270
278
|
getSettingsPath(),
|
|
271
|
-
JSON.stringify(buildPersistedSettings(settings), null, 2)
|
|
279
|
+
JSON.stringify(buildPersistedSettings(settings), null, 2),
|
|
280
|
+
{ encoding: "utf-8", mode: 384 }
|
|
272
281
|
)
|
|
273
|
-
).catch((err) => logger$n.error("Failed to save settings:", err));
|
|
282
|
+
).then(() => fs.promises.chmod(getSettingsPath(), 384).catch(() => void 0)).catch((err) => logger$n.error("Failed to save settings:", err));
|
|
274
283
|
}
|
|
275
284
|
function saveSettings() {
|
|
276
285
|
saveDirty = true;
|
|
@@ -375,6 +384,28 @@ function assertPermittedNavigationURL(url) {
|
|
|
375
384
|
throw new Error(policyError);
|
|
376
385
|
}
|
|
377
386
|
}
|
|
387
|
+
function loadPermittedNavigationURL(wc, url) {
|
|
388
|
+
assertPermittedNavigationURL(url);
|
|
389
|
+
return wc.loadURL(url);
|
|
390
|
+
}
|
|
391
|
+
function loadInternalDataURL(wc, dataUrl) {
|
|
392
|
+
if (!dataUrl.startsWith("data:text/html;charset=utf-8,")) {
|
|
393
|
+
throw new Error("Blocked unexpected internal data URL");
|
|
394
|
+
}
|
|
395
|
+
return wc.loadURL(dataUrl);
|
|
396
|
+
}
|
|
397
|
+
function loadTrustedAppURL(wc, url) {
|
|
398
|
+
const parsed = new URL(url);
|
|
399
|
+
if (!["file:", "http:", "https:"].includes(parsed.protocol)) {
|
|
400
|
+
throw new Error(`Blocked unexpected app URL scheme: ${parsed.protocol}`);
|
|
401
|
+
}
|
|
402
|
+
const isHttp = parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
403
|
+
const isLocalhost = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
404
|
+
if (isHttp && !isLocalhost) {
|
|
405
|
+
throw new Error(`Blocked unexpected app URL host: ${parsed.hostname}`);
|
|
406
|
+
}
|
|
407
|
+
return wc.loadURL(parsed.toString());
|
|
408
|
+
}
|
|
378
409
|
const MAX_CUSTOM_HISTORY = 50;
|
|
379
410
|
const READER_MODE_DATA_URL_PREFIX = "data:text/html;charset=utf-8,";
|
|
380
411
|
const logger$m = createLogger("Tab");
|
|
@@ -1103,7 +1134,7 @@ function createDebouncedJsonPersistence({
|
|
|
1103
1134
|
data,
|
|
1104
1135
|
typeof data === "string" ? { encoding: "utf-8", mode: 384 } : { mode: 384 }
|
|
1105
1136
|
)
|
|
1106
|
-
).catch((err) => logger$l.error(`Failed to save ${logLabel}:`, err));
|
|
1137
|
+
).then(() => fs.promises.chmod(filePath2, 384).catch(() => void 0)).catch((err) => logger$l.error(`Failed to save ${logLabel}:`, err));
|
|
1107
1138
|
};
|
|
1108
1139
|
const schedule = () => {
|
|
1109
1140
|
saveDirty2 = true;
|
|
@@ -1164,9 +1195,9 @@ function save$3() {
|
|
|
1164
1195
|
}
|
|
1165
1196
|
function emit$4() {
|
|
1166
1197
|
if (!state$5) return;
|
|
1167
|
-
const
|
|
1198
|
+
const snapshot2 = { highlights: [...state$5.highlights] };
|
|
1168
1199
|
for (const listener of listeners$2) {
|
|
1169
|
-
listener(
|
|
1200
|
+
listener(snapshot2);
|
|
1170
1201
|
}
|
|
1171
1202
|
}
|
|
1172
1203
|
function normalizeUrl$1(rawUrl) {
|
|
@@ -1897,9 +1928,9 @@ function save$2() {
|
|
|
1897
1928
|
}
|
|
1898
1929
|
function emit$3() {
|
|
1899
1930
|
if (!state$4) return;
|
|
1900
|
-
const
|
|
1931
|
+
const snapshot2 = { entries: [...state$4.entries] };
|
|
1901
1932
|
for (const listener of listeners$1) {
|
|
1902
|
-
listener(
|
|
1933
|
+
listener(snapshot2);
|
|
1903
1934
|
}
|
|
1904
1935
|
}
|
|
1905
1936
|
function getState$1() {
|
|
@@ -2093,6 +2124,8 @@ class DevToolsSession {
|
|
|
2093
2124
|
this.tabId = tabId;
|
|
2094
2125
|
this.wc = wc;
|
|
2095
2126
|
}
|
|
2127
|
+
tabId;
|
|
2128
|
+
wc;
|
|
2096
2129
|
attached = false;
|
|
2097
2130
|
attachingPromise = null;
|
|
2098
2131
|
consoleDomainEnabled = false;
|
|
@@ -3108,11 +3141,11 @@ class TabManager {
|
|
|
3108
3141
|
note
|
|
3109
3142
|
};
|
|
3110
3143
|
}
|
|
3111
|
-
restoreSession(
|
|
3112
|
-
const tabs =
|
|
3144
|
+
restoreSession(snapshot2) {
|
|
3145
|
+
const tabs = snapshot2.tabs.length > 0 ? snapshot2.tabs : [{ id: "", url: "about:blank", title: "New Tab" }];
|
|
3113
3146
|
const activeIndex = Math.max(
|
|
3114
3147
|
0,
|
|
3115
|
-
Math.min(
|
|
3148
|
+
Math.min(snapshot2.activeIndex, tabs.length - 1)
|
|
3116
3149
|
);
|
|
3117
3150
|
this.destroyAllTabs();
|
|
3118
3151
|
const restoredGroups = /* @__PURE__ */ new Map();
|
|
@@ -3375,6 +3408,7 @@ const Channels = {
|
|
|
3375
3408
|
SETTINGS_UPDATE: "settings:update",
|
|
3376
3409
|
SETTINGS_HEALTH_GET: "settings:health:get",
|
|
3377
3410
|
SETTINGS_HEALTH_UPDATE: "settings:health:update",
|
|
3411
|
+
MCP_REGENERATE_TOKEN: "mcp:regenerate-token",
|
|
3378
3412
|
// Bookmarks
|
|
3379
3413
|
BOOKMARKS_GET: "bookmarks:get",
|
|
3380
3414
|
BOOKMARKS_UPDATE: "bookmarks:update",
|
|
@@ -3875,8 +3909,8 @@ function load$2() {
|
|
|
3875
3909
|
const next = /* @__PURE__ */ new Map();
|
|
3876
3910
|
if (!Array.isArray(raw)) return next;
|
|
3877
3911
|
for (const entry of raw) {
|
|
3878
|
-
const
|
|
3879
|
-
if (
|
|
3912
|
+
const snapshot2 = normalizeStoredSnapshot(entry);
|
|
3913
|
+
if (snapshot2) next.set(snapshot2.url, snapshot2);
|
|
3880
3914
|
}
|
|
3881
3915
|
return next;
|
|
3882
3916
|
}
|
|
@@ -3903,7 +3937,7 @@ function getSnapshot(normalizedUrl) {
|
|
|
3903
3937
|
function saveSnapshot(rawUrl, title, textContent, headings) {
|
|
3904
3938
|
const s = load$2();
|
|
3905
3939
|
const key2 = normalizeUrl(rawUrl);
|
|
3906
|
-
const
|
|
3940
|
+
const snapshot2 = {
|
|
3907
3941
|
url: key2,
|
|
3908
3942
|
title,
|
|
3909
3943
|
textContent: textContent.slice(0, MAX_TEXT_LENGTH),
|
|
@@ -3911,9 +3945,9 @@ function saveSnapshot(rawUrl, title, textContent, headings) {
|
|
|
3911
3945
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3912
3946
|
};
|
|
3913
3947
|
s.delete(key2);
|
|
3914
|
-
s.set(key2,
|
|
3948
|
+
s.set(key2, snapshot2);
|
|
3915
3949
|
persistence$5.schedule();
|
|
3916
|
-
return
|
|
3950
|
+
return snapshot2;
|
|
3917
3951
|
}
|
|
3918
3952
|
function flushPersist$2() {
|
|
3919
3953
|
return persistence$5.flush();
|
|
@@ -4726,6 +4760,22 @@ const POSTHOG_API_KEY = process.env.POSTHOG_API_KEY || "phc_OMeM3P5cxJwl14lOKxYa
|
|
|
4726
4760
|
const POSTHOG_HOST = process.env.POSTHOG_HOST || "https://us.i.posthog.com";
|
|
4727
4761
|
const BATCH_INTERVAL_MS = 6e4;
|
|
4728
4762
|
const MAX_BATCH_SIZE = 50;
|
|
4763
|
+
const SENSITIVE_PROPERTY_RE = /url|uri|query|prompt|content|text|token|secret|key|password|credential|email|domain/i;
|
|
4764
|
+
const SENSITIVE_STRING_VALUE_RE = /https?:\/\/|www\.|[^\s@]+@[^\s@]+\.[^\s@]+|bearer\s+\S+|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.|(?:sk|pk|rk|gh[pousr]|xox[baprs])-[-_A-Za-z0-9]{12,}/i;
|
|
4765
|
+
const EMPTY_PROPERTY_ALLOWLIST = /* @__PURE__ */ new Set();
|
|
4766
|
+
const TELEMETRY_PROPERTY_ALLOWLIST = {
|
|
4767
|
+
app_launched: /* @__PURE__ */ new Set(["electron_version", "chrome_version"]),
|
|
4768
|
+
app_session_ended: /* @__PURE__ */ new Set(["duration_minutes"]),
|
|
4769
|
+
tool_called: /* @__PURE__ */ new Set(["tool_name", "page_type"]),
|
|
4770
|
+
provider_configured: /* @__PURE__ */ new Set(["provider_id"]),
|
|
4771
|
+
page_type_detected: /* @__PURE__ */ new Set(["page_type"]),
|
|
4772
|
+
setting_changed: /* @__PURE__ */ new Set(["setting_key"]),
|
|
4773
|
+
approval_mode_changed: /* @__PURE__ */ new Set(["mode"]),
|
|
4774
|
+
bookmark_action: /* @__PURE__ */ new Set(["action"]),
|
|
4775
|
+
vault_action: /* @__PURE__ */ new Set(["action"]),
|
|
4776
|
+
extraction_failed: /* @__PURE__ */ new Set(["reason"]),
|
|
4777
|
+
premium_funnel: /* @__PURE__ */ new Set(["step", "status", "reason"])
|
|
4778
|
+
};
|
|
4729
4779
|
function getDeviceIdPath() {
|
|
4730
4780
|
return path.join(electron.app.getPath("userData"), ".vessel-device-id");
|
|
4731
4781
|
}
|
|
@@ -4741,7 +4791,8 @@ function getDeviceId() {
|
|
|
4741
4791
|
deviceId = crypto$1.randomUUID();
|
|
4742
4792
|
try {
|
|
4743
4793
|
fs.mkdirSync(path.dirname(idPath), { recursive: true });
|
|
4744
|
-
fs.writeFileSync(idPath, deviceId, "utf-8");
|
|
4794
|
+
fs.writeFileSync(idPath, deviceId, { encoding: "utf-8", mode: 384 });
|
|
4795
|
+
fs.chmodSync(idPath, 384);
|
|
4745
4796
|
} catch {
|
|
4746
4797
|
}
|
|
4747
4798
|
return deviceId;
|
|
@@ -4754,12 +4805,27 @@ function isEnabled() {
|
|
|
4754
4805
|
if (process.env.VESSEL_DEV === "1") return false;
|
|
4755
4806
|
return loadSettings().telemetryEnabled !== false;
|
|
4756
4807
|
}
|
|
4808
|
+
function sanitizeTelemetryProperties(properties, allowedKeys) {
|
|
4809
|
+
const safe = {};
|
|
4810
|
+
for (const [key2, value] of Object.entries(properties)) {
|
|
4811
|
+
if (allowedKeys && !allowedKeys.has(key2)) continue;
|
|
4812
|
+
if (!allowedKeys?.has(key2) && SENSITIVE_PROPERTY_RE.test(key2)) continue;
|
|
4813
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
4814
|
+
if (typeof value === "string" && SENSITIVE_STRING_VALUE_RE.test(value)) {
|
|
4815
|
+
continue;
|
|
4816
|
+
}
|
|
4817
|
+
safe[key2] = typeof value === "string" ? value.slice(0, 120) : value;
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
return safe;
|
|
4821
|
+
}
|
|
4757
4822
|
function trackEvent(event, properties = {}) {
|
|
4758
4823
|
if (!isEnabled()) return;
|
|
4824
|
+
const allowedKeys = TELEMETRY_PROPERTY_ALLOWLIST[event] ?? EMPTY_PROPERTY_ALLOWLIST;
|
|
4759
4825
|
eventQueue.push({
|
|
4760
4826
|
event,
|
|
4761
4827
|
properties: {
|
|
4762
|
-
...properties,
|
|
4828
|
+
...sanitizeTelemetryProperties(properties, allowedKeys),
|
|
4763
4829
|
premium_status: isPremium() ? "premium" : "free",
|
|
4764
4830
|
app_version: electron.app.getVersion(),
|
|
4765
4831
|
platform: process.platform,
|
|
@@ -4794,8 +4860,8 @@ function trackBookmarkAction(action) {
|
|
|
4794
4860
|
function trackVaultAction(action) {
|
|
4795
4861
|
trackEvent("vault_action", { action });
|
|
4796
4862
|
}
|
|
4797
|
-
function trackExtractionFailed(
|
|
4798
|
-
trackEvent("extraction_failed", {
|
|
4863
|
+
function trackExtractionFailed(_domain, reason) {
|
|
4864
|
+
trackEvent("extraction_failed", { reason });
|
|
4799
4865
|
}
|
|
4800
4866
|
function trackPremiumFunnel(step, context) {
|
|
4801
4867
|
trackEvent("premium_funnel", { step, ...context });
|
|
@@ -6774,9 +6840,9 @@ function getMcpStatus() {
|
|
|
6774
6840
|
return state$3.mcp.status;
|
|
6775
6841
|
}
|
|
6776
6842
|
function emitRuntimeHealthChange() {
|
|
6777
|
-
const
|
|
6843
|
+
const snapshot2 = getRuntimeHealth();
|
|
6778
6844
|
for (const listener of runtimeHealthChangeListeners) {
|
|
6779
|
-
listener(
|
|
6845
|
+
listener(snapshot2);
|
|
6780
6846
|
}
|
|
6781
6847
|
}
|
|
6782
6848
|
const state$3 = {
|
|
@@ -6942,7 +7008,7 @@ function isClickReadLoop(names) {
|
|
|
6942
7008
|
return clickReadPairs >= 2;
|
|
6943
7009
|
}
|
|
6944
7010
|
const ANTHROPIC_MAX_TOKENS = 4096;
|
|
6945
|
-
function isRecord(value) {
|
|
7011
|
+
function isRecord$1(value) {
|
|
6946
7012
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6947
7013
|
}
|
|
6948
7014
|
function anthropicModelLikelySupportsThinking(model) {
|
|
@@ -7066,7 +7132,7 @@ class AnthropicProvider {
|
|
|
7066
7132
|
} else if (event.type === "content_block_stop" && currentToolUse) {
|
|
7067
7133
|
try {
|
|
7068
7134
|
const input = JSON.parse(currentToolUse.inputJson || "{}");
|
|
7069
|
-
if (!isRecord(input)) {
|
|
7135
|
+
if (!isRecord$1(input)) {
|
|
7070
7136
|
throw new Error("Tool input must be a JSON object");
|
|
7071
7137
|
}
|
|
7072
7138
|
toolUseBlocks.push({
|
|
@@ -8473,6 +8539,20 @@ class OpenAICompatProvider {
|
|
|
8473
8539
|
this.abortController?.abort();
|
|
8474
8540
|
}
|
|
8475
8541
|
}
|
|
8542
|
+
async function openExternalAllowlisted(url, rule) {
|
|
8543
|
+
const parsed = new URL(url);
|
|
8544
|
+
const schemes = rule.schemes ?? ["https:"];
|
|
8545
|
+
if (!schemes.includes(parsed.protocol)) {
|
|
8546
|
+
throw new Error(`Blocked external URL scheme: ${parsed.protocol}`);
|
|
8547
|
+
}
|
|
8548
|
+
if (parsed.username || parsed.password) {
|
|
8549
|
+
throw new Error("Blocked external URL with embedded credentials");
|
|
8550
|
+
}
|
|
8551
|
+
if (rule.hosts && !rule.hosts.includes(parsed.hostname)) {
|
|
8552
|
+
throw new Error(`Blocked external URL host: ${parsed.hostname}`);
|
|
8553
|
+
}
|
|
8554
|
+
await electron.shell.openExternal(parsed.toString());
|
|
8555
|
+
}
|
|
8476
8556
|
const logger$g = createLogger("CodexOAuth");
|
|
8477
8557
|
const ISSUER = "https://auth.openai.com";
|
|
8478
8558
|
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
@@ -8538,54 +8618,6 @@ function parseTokenExpiry(accessToken) {
|
|
|
8538
8618
|
}
|
|
8539
8619
|
return Date.now() + 36e5;
|
|
8540
8620
|
}
|
|
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
8621
|
async function exchangeCodeForTokens(code, redirectUri, codeVerifier) {
|
|
8590
8622
|
const body = new URLSearchParams({
|
|
8591
8623
|
grant_type: "authorization_code",
|
|
@@ -8623,7 +8655,7 @@ async function exchangeCodeForTokens(code, redirectUri, codeVerifier) {
|
|
|
8623
8655
|
accountId: claims?.accountId || "",
|
|
8624
8656
|
accountEmail: claims?.email
|
|
8625
8657
|
};
|
|
8626
|
-
return
|
|
8658
|
+
return tokens;
|
|
8627
8659
|
}
|
|
8628
8660
|
async function refreshAccessToken(tokens) {
|
|
8629
8661
|
const body = new URLSearchParams({
|
|
@@ -8659,7 +8691,7 @@ async function refreshAccessToken(tokens) {
|
|
|
8659
8691
|
accountId: claims?.accountId || tokens.accountId || "",
|
|
8660
8692
|
accountEmail: claims?.email || tokens.accountEmail
|
|
8661
8693
|
};
|
|
8662
|
-
return
|
|
8694
|
+
return refreshedTokens;
|
|
8663
8695
|
}
|
|
8664
8696
|
function startServer(port, pkce, expectedState, resolve, reject) {
|
|
8665
8697
|
const server = http.createServer(async (req, res) => {
|
|
@@ -8809,7 +8841,7 @@ async function startCodexOAuth(onStatus) {
|
|
|
8809
8841
|
activeFlow.port = port;
|
|
8810
8842
|
const authUrl = buildAuthorizeUrl(port, pkce, state2);
|
|
8811
8843
|
safeOnStatus("waiting");
|
|
8812
|
-
|
|
8844
|
+
openExternalAllowlisted(authUrl, { hosts: ["auth.openai.com"] }).catch((err) => {
|
|
8813
8845
|
logger$g.warn("Failed to open browser, user will need the URL:", err);
|
|
8814
8846
|
});
|
|
8815
8847
|
}).catch(wrappedReject);
|
|
@@ -8830,12 +8862,57 @@ const logger$f = createLogger("CodexProvider");
|
|
|
8830
8862
|
const REFRESH_WINDOW_MS = 5 * 60 * 1e3;
|
|
8831
8863
|
const CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api/codex";
|
|
8832
8864
|
const CODEX_CLIENT_VERSION = "0.129.0";
|
|
8865
|
+
function isRecord(value) {
|
|
8866
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
8867
|
+
}
|
|
8868
|
+
async function createCodexFunctionCallOutput(functionCall, availableToolNames, onChunk, onToolCall) {
|
|
8869
|
+
const callId = functionCall.call_id || functionCall.id || "";
|
|
8870
|
+
const name = functionCall.name || "";
|
|
8871
|
+
if (!callId) {
|
|
8872
|
+
return {
|
|
8873
|
+
type: "function_call_output",
|
|
8874
|
+
call_id: callId,
|
|
8875
|
+
output: "Error: Function call was missing a call_id. Please retry the tool call."
|
|
8876
|
+
};
|
|
8877
|
+
}
|
|
8878
|
+
if (!name || !availableToolNames.has(name)) {
|
|
8879
|
+
onChunk(`
|
|
8880
|
+
<<tool:${name || "unknown"}:⚠ unsupported>>
|
|
8881
|
+
`);
|
|
8882
|
+
return {
|
|
8883
|
+
type: "function_call_output",
|
|
8884
|
+
call_id: callId,
|
|
8885
|
+
output: `Error: Unsupported tool${name ? `: ${name}` : ""}. Use one of the provided tools.`
|
|
8886
|
+
};
|
|
8887
|
+
}
|
|
8888
|
+
let args;
|
|
8889
|
+
try {
|
|
8890
|
+
const parsed = JSON.parse(functionCall.arguments || "{}");
|
|
8891
|
+
if (!isRecord(parsed)) throw new Error("Tool arguments must be a JSON object");
|
|
8892
|
+
args = parsed;
|
|
8893
|
+
} catch {
|
|
8894
|
+
onChunk(`
|
|
8895
|
+
<<tool:${name}:⚠ invalid args>>
|
|
8896
|
+
`);
|
|
8897
|
+
return {
|
|
8898
|
+
type: "function_call_output",
|
|
8899
|
+
call_id: callId,
|
|
8900
|
+
output: "Error: Invalid JSON in tool arguments. Please retry with a valid JSON object."
|
|
8901
|
+
};
|
|
8902
|
+
}
|
|
8903
|
+
const output = await onToolCall(name, args);
|
|
8904
|
+
return {
|
|
8905
|
+
type: "function_call_output",
|
|
8906
|
+
call_id: callId,
|
|
8907
|
+
output
|
|
8908
|
+
};
|
|
8909
|
+
}
|
|
8833
8910
|
class CodexProvider {
|
|
8834
8911
|
agentToolProfile;
|
|
8835
8912
|
tokens;
|
|
8836
8913
|
model;
|
|
8837
8914
|
abortController = null;
|
|
8838
|
-
constructor(tokens, model
|
|
8915
|
+
constructor(tokens, model) {
|
|
8839
8916
|
this.tokens = tokens;
|
|
8840
8917
|
this.model = model;
|
|
8841
8918
|
this.agentToolProfile = "default";
|
|
@@ -8854,7 +8931,7 @@ class CodexProvider {
|
|
|
8854
8931
|
);
|
|
8855
8932
|
}
|
|
8856
8933
|
}
|
|
8857
|
-
backendHeaders() {
|
|
8934
|
+
backendHeaders(turnState) {
|
|
8858
8935
|
const headers = {
|
|
8859
8936
|
Authorization: `Bearer ${this.tokens.accessToken}`,
|
|
8860
8937
|
"Content-Type": "application/json",
|
|
@@ -8865,6 +8942,9 @@ class CodexProvider {
|
|
|
8865
8942
|
if (this.tokens.accountId) {
|
|
8866
8943
|
headers["ChatGPT-Account-ID"] = this.tokens.accountId;
|
|
8867
8944
|
}
|
|
8945
|
+
if (turnState) {
|
|
8946
|
+
headers["x-codex-turn-state"] = turnState;
|
|
8947
|
+
}
|
|
8868
8948
|
return headers;
|
|
8869
8949
|
}
|
|
8870
8950
|
buildInput(userMessage, history) {
|
|
@@ -8873,7 +8953,7 @@ class CodexProvider {
|
|
|
8873
8953
|
input.push({
|
|
8874
8954
|
type: "message",
|
|
8875
8955
|
role: msg.role,
|
|
8876
|
-
content: [{ type: "input_text", text: msg.content }]
|
|
8956
|
+
content: [{ type: msg.role === "assistant" ? "output_text" : "input_text", text: msg.content }]
|
|
8877
8957
|
});
|
|
8878
8958
|
}
|
|
8879
8959
|
input.push({
|
|
@@ -8883,7 +8963,7 @@ class CodexProvider {
|
|
|
8883
8963
|
});
|
|
8884
8964
|
return input;
|
|
8885
8965
|
}
|
|
8886
|
-
handleStreamEvent(raw, onChunk,
|
|
8966
|
+
handleStreamEvent(raw, onChunk, acc) {
|
|
8887
8967
|
if (!raw.trim() || raw.trim() === "[DONE]") return;
|
|
8888
8968
|
let event;
|
|
8889
8969
|
try {
|
|
@@ -8892,13 +8972,28 @@ class CodexProvider {
|
|
|
8892
8972
|
return;
|
|
8893
8973
|
}
|
|
8894
8974
|
if (event.type === "response.output_text.delta" && event.delta) {
|
|
8895
|
-
emittedTextFromDelta
|
|
8975
|
+
acc.emittedTextFromDelta = true;
|
|
8976
|
+
acc.text += event.delta;
|
|
8896
8977
|
onChunk(event.delta);
|
|
8897
8978
|
return;
|
|
8898
8979
|
}
|
|
8899
|
-
if (event.type === "response.
|
|
8900
|
-
const
|
|
8901
|
-
if (
|
|
8980
|
+
if (event.type === "response.function_call_arguments.delta" && event.delta) {
|
|
8981
|
+
const key2 = event.call_id || event.item_id || "";
|
|
8982
|
+
if (key2) {
|
|
8983
|
+
acc.functionCallArgs.set(key2, (acc.functionCallArgs.get(key2) || "") + event.delta);
|
|
8984
|
+
}
|
|
8985
|
+
return;
|
|
8986
|
+
}
|
|
8987
|
+
if (event.type === "response.output_item.done" && event.item) {
|
|
8988
|
+
const item = event.item;
|
|
8989
|
+
if (item.type === "function_call") {
|
|
8990
|
+
const key2 = item.call_id || item.id || "";
|
|
8991
|
+
const args = acc.functionCallArgs.get(key2) || item.arguments || "";
|
|
8992
|
+
acc.functionCallArgs.delete(key2);
|
|
8993
|
+
acc.items.push({ ...item, arguments: args });
|
|
8994
|
+
} else if (item.type === "message") {
|
|
8995
|
+
acc.items.push(item);
|
|
8996
|
+
}
|
|
8902
8997
|
return;
|
|
8903
8998
|
}
|
|
8904
8999
|
if (event.type === "response.failed") {
|
|
@@ -8907,18 +9002,12 @@ class CodexProvider {
|
|
|
8907
9002
|
throw new Error(message);
|
|
8908
9003
|
}
|
|
8909
9004
|
}
|
|
8910
|
-
async streamCodexResponse(
|
|
9005
|
+
async streamCodexResponse(requestBody, onChunk, turnState) {
|
|
8911
9006
|
const response = await fetch(`${CODEX_BACKEND_BASE_URL}/responses`, {
|
|
8912
9007
|
method: "POST",
|
|
8913
|
-
headers: this.backendHeaders(),
|
|
9008
|
+
headers: this.backendHeaders(turnState),
|
|
8914
9009
|
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
|
-
})
|
|
9010
|
+
body: JSON.stringify(requestBody)
|
|
8922
9011
|
});
|
|
8923
9012
|
if (!response.ok) {
|
|
8924
9013
|
const text = await response.text().catch(() => "");
|
|
@@ -8929,33 +9018,53 @@ class CodexProvider {
|
|
|
8929
9018
|
if (!response.body) {
|
|
8930
9019
|
throw new Error("Codex backend returned an empty response stream");
|
|
8931
9020
|
}
|
|
9021
|
+
const newTurnState = response.headers.get("x-codex-turn-state") || null;
|
|
8932
9022
|
const reader = response.body.getReader();
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
9023
|
+
try {
|
|
9024
|
+
const decoder = new TextDecoder();
|
|
9025
|
+
let buffer = "";
|
|
9026
|
+
const acc = {
|
|
9027
|
+
text: "",
|
|
9028
|
+
items: [],
|
|
9029
|
+
emittedTextFromDelta: false,
|
|
9030
|
+
functionCallArgs: /* @__PURE__ */ new Map()
|
|
9031
|
+
};
|
|
9032
|
+
while (true) {
|
|
9033
|
+
const { value, done } = await reader.read();
|
|
9034
|
+
if (done) break;
|
|
9035
|
+
buffer += decoder.decode(value, { stream: true });
|
|
9036
|
+
let separatorIndex;
|
|
9037
|
+
while ((separatorIndex = buffer.indexOf("\n\n")) !== -1) {
|
|
9038
|
+
const block = buffer.slice(0, separatorIndex);
|
|
9039
|
+
buffer = buffer.slice(separatorIndex + 2);
|
|
9040
|
+
const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
9041
|
+
this.handleStreamEvent(data, onChunk, acc);
|
|
9042
|
+
}
|
|
9043
|
+
}
|
|
9044
|
+
const trailing = buffer.trim();
|
|
9045
|
+
if (trailing) {
|
|
9046
|
+
const data = trailing.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
9047
|
+
this.handleStreamEvent(data, onChunk, acc);
|
|
9048
|
+
}
|
|
9049
|
+
return { text: acc.text, items: acc.items, turnState: newTurnState };
|
|
9050
|
+
} finally {
|
|
9051
|
+
reader.releaseLock();
|
|
8952
9052
|
}
|
|
8953
9053
|
}
|
|
8954
9054
|
async streamQuery(systemPrompt, userMessage, onChunk, onEnd, history) {
|
|
8955
9055
|
await this.ensureFreshTokens();
|
|
8956
9056
|
this.abortController = new AbortController();
|
|
8957
9057
|
try {
|
|
8958
|
-
await this.streamCodexResponse(
|
|
9058
|
+
await this.streamCodexResponse(
|
|
9059
|
+
{
|
|
9060
|
+
model: this.model,
|
|
9061
|
+
instructions: systemPrompt,
|
|
9062
|
+
input: this.buildInput(userMessage, history),
|
|
9063
|
+
stream: true,
|
|
9064
|
+
store: false
|
|
9065
|
+
},
|
|
9066
|
+
onChunk
|
|
9067
|
+
);
|
|
8959
9068
|
} catch (err) {
|
|
8960
9069
|
if (err.name !== "AbortError") {
|
|
8961
9070
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -8969,11 +9078,59 @@ class CodexProvider {
|
|
|
8969
9078
|
onEnd();
|
|
8970
9079
|
}
|
|
8971
9080
|
}
|
|
8972
|
-
async streamAgentQuery(systemPrompt, userMessage,
|
|
9081
|
+
async streamAgentQuery(systemPrompt, userMessage, tools, onChunk, onToolCall, onEnd, history) {
|
|
8973
9082
|
await this.ensureFreshTokens();
|
|
8974
9083
|
this.abortController = new AbortController();
|
|
9084
|
+
const maxIterations = getEffectiveMaxIterations();
|
|
9085
|
+
const availableToolNames = new Set(tools.map((tool) => tool.name));
|
|
9086
|
+
let iterationsUsed = 0;
|
|
9087
|
+
const convertedTools = tools.map((tool) => ({
|
|
9088
|
+
type: "function",
|
|
9089
|
+
name: tool.name,
|
|
9090
|
+
description: tool.description || "",
|
|
9091
|
+
parameters: tool.input_schema
|
|
9092
|
+
}));
|
|
9093
|
+
let currentInput = this.buildInput(userMessage, history);
|
|
9094
|
+
let turnState = null;
|
|
8975
9095
|
try {
|
|
8976
|
-
|
|
9096
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
9097
|
+
iterationsUsed = i + 1;
|
|
9098
|
+
const result = await this.streamCodexResponse(
|
|
9099
|
+
{
|
|
9100
|
+
model: this.model,
|
|
9101
|
+
instructions: systemPrompt,
|
|
9102
|
+
input: currentInput,
|
|
9103
|
+
tools: convertedTools,
|
|
9104
|
+
stream: true,
|
|
9105
|
+
store: false
|
|
9106
|
+
},
|
|
9107
|
+
onChunk,
|
|
9108
|
+
turnState || void 0
|
|
9109
|
+
);
|
|
9110
|
+
turnState = result.turnState || turnState;
|
|
9111
|
+
const functionCalls = result.items.filter(
|
|
9112
|
+
(item) => item.type === "function_call"
|
|
9113
|
+
);
|
|
9114
|
+
if (functionCalls.length === 0) {
|
|
9115
|
+
break;
|
|
9116
|
+
}
|
|
9117
|
+
currentInput = [];
|
|
9118
|
+
for (const fc of functionCalls) {
|
|
9119
|
+
currentInput.push(
|
|
9120
|
+
await createCodexFunctionCallOutput(
|
|
9121
|
+
fc,
|
|
9122
|
+
availableToolNames,
|
|
9123
|
+
onChunk,
|
|
9124
|
+
onToolCall
|
|
9125
|
+
)
|
|
9126
|
+
);
|
|
9127
|
+
}
|
|
9128
|
+
}
|
|
9129
|
+
if (iterationsUsed >= maxIterations) {
|
|
9130
|
+
onChunk(`
|
|
9131
|
+
|
|
9132
|
+
[Reached maximum tool call limit (${maxIterations} steps). You can adjust this in Settings → Max Tool Iterations, or continue by sending another message.]`);
|
|
9133
|
+
}
|
|
8977
9134
|
} catch (err) {
|
|
8978
9135
|
if (err.name !== "AbortError") {
|
|
8979
9136
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -9057,11 +9214,11 @@ function buildLlamaCppCtxWarning(ctxSize) {
|
|
|
9057
9214
|
}
|
|
9058
9215
|
async function fetchCodexBackendModels(tokens) {
|
|
9059
9216
|
const url = new URL("https://chatgpt.com/backend-api/codex/models");
|
|
9060
|
-
url.searchParams.set("client_version",
|
|
9217
|
+
url.searchParams.set("client_version", CODEX_CLIENT_VERSION);
|
|
9061
9218
|
const headers = {
|
|
9062
9219
|
Authorization: `Bearer ${tokens.accessToken}`,
|
|
9063
9220
|
originator: "codex_cli_rs",
|
|
9064
|
-
"User-Agent":
|
|
9221
|
+
"User-Agent": `codex_cli_rs/${CODEX_CLIENT_VERSION} Vessel`
|
|
9065
9222
|
};
|
|
9066
9223
|
if (tokens.accountId) {
|
|
9067
9224
|
headers["ChatGPT-Account-ID"] = tokens.accountId;
|
|
@@ -9164,7 +9321,7 @@ function createProvider(config) {
|
|
|
9164
9321
|
"OpenAI Codex requires authentication. Open settings to connect your ChatGPT account."
|
|
9165
9322
|
);
|
|
9166
9323
|
}
|
|
9167
|
-
return new CodexProvider(tokens, normalized.model
|
|
9324
|
+
return new CodexProvider(tokens, normalized.model);
|
|
9168
9325
|
}
|
|
9169
9326
|
return new OpenAICompatProvider(normalized);
|
|
9170
9327
|
}
|
|
@@ -12476,9 +12633,9 @@ function assignDefinedBookmarkFields(bookmark, fields) {
|
|
|
12476
12633
|
}
|
|
12477
12634
|
function emit$2() {
|
|
12478
12635
|
if (!state$2) return;
|
|
12479
|
-
const
|
|
12636
|
+
const snapshot2 = cloneState(state$2);
|
|
12480
12637
|
for (const listener of listeners) {
|
|
12481
|
-
listener(
|
|
12638
|
+
listener(snapshot2);
|
|
12482
12639
|
}
|
|
12483
12640
|
}
|
|
12484
12641
|
function escapeBookmarkHtml(value) {
|
|
@@ -12999,7 +13156,7 @@ async function captureLiveHighlightSnapshot(wc, savedHighlights = []) {
|
|
|
12999
13156
|
savedHighlights.map((highlight) => normalizeText(highlight.text)).filter(Boolean)
|
|
13000
13157
|
);
|
|
13001
13158
|
try {
|
|
13002
|
-
const
|
|
13159
|
+
const snapshot2 = await wc.executeJavaScript(`(() => {
|
|
13003
13160
|
const selection = window.getSelection?.()?.toString().trim() || "";
|
|
13004
13161
|
const pageHighlights = Array.from(
|
|
13005
13162
|
document.querySelectorAll("mark.__vessel-highlight-text[data-vessel-highlight]")
|
|
@@ -13021,7 +13178,7 @@ async function captureLiveHighlightSnapshot(wc, savedHighlights = []) {
|
|
|
13021
13178
|
};
|
|
13022
13179
|
})()`, true);
|
|
13023
13180
|
const seen = /* @__PURE__ */ new Set();
|
|
13024
|
-
const pageHighlights = (
|
|
13181
|
+
const pageHighlights = (snapshot2.pageHighlights ?? []).map((highlight) => ({
|
|
13025
13182
|
text: normalizeText(highlight.text),
|
|
13026
13183
|
color: highlight.color?.trim() || void 0
|
|
13027
13184
|
})).filter((highlight) => highlight.text.length > 0).filter((highlight) => {
|
|
@@ -13033,24 +13190,24 @@ async function captureLiveHighlightSnapshot(wc, savedHighlights = []) {
|
|
|
13033
13190
|
...highlight,
|
|
13034
13191
|
persisted: savedTexts.has(highlight.text)
|
|
13035
13192
|
}));
|
|
13036
|
-
const activeSelection = normalizeText(
|
|
13193
|
+
const activeSelection = normalizeText(snapshot2.activeSelection) || void 0;
|
|
13037
13194
|
return { activeSelection, pageHighlights };
|
|
13038
13195
|
} catch {
|
|
13039
13196
|
return { pageHighlights: [] };
|
|
13040
13197
|
}
|
|
13041
13198
|
}
|
|
13042
|
-
function formatLiveSelectionSection(
|
|
13199
|
+
function formatLiveSelectionSection(snapshot2) {
|
|
13043
13200
|
const sections = [];
|
|
13044
|
-
if (
|
|
13045
|
-
const preview =
|
|
13201
|
+
if (snapshot2.activeSelection) {
|
|
13202
|
+
const preview = snapshot2.activeSelection.length > 400 ? `${snapshot2.activeSelection.slice(0, 397)}...` : snapshot2.activeSelection;
|
|
13046
13203
|
sections.push(
|
|
13047
13204
|
`## Active User Selection
|
|
13048
13205
|
The user currently has this text selected on screen:
|
|
13049
13206
|
- "${preview}"`
|
|
13050
13207
|
);
|
|
13051
13208
|
}
|
|
13052
|
-
if (
|
|
13053
|
-
const lines =
|
|
13209
|
+
if (snapshot2.pageHighlights.length > 0) {
|
|
13210
|
+
const lines = snapshot2.pageHighlights.map((highlight) => {
|
|
13054
13211
|
const preview = highlight.text.length > 180 ? `${highlight.text.slice(0, 177)}...` : highlight.text;
|
|
13055
13212
|
const details = [
|
|
13056
13213
|
highlight.persisted ? "saved" : "visible only",
|
|
@@ -13551,7 +13708,7 @@ async function saveNamedSession(tabManager, name) {
|
|
|
13551
13708
|
const entries = await captureLocalStorageForOrigin(tabManager, origin);
|
|
13552
13709
|
localStorage.push({ origin, entries });
|
|
13553
13710
|
}
|
|
13554
|
-
const
|
|
13711
|
+
const snapshot2 = tabManager.snapshotSession(`Named session: ${normalizedName}`);
|
|
13555
13712
|
const domains = [...new Set(cookies.map((cookie) => cookie.domain))].sort();
|
|
13556
13713
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13557
13714
|
const data = {
|
|
@@ -13563,7 +13720,7 @@ async function saveNamedSession(tabManager, name) {
|
|
|
13563
13720
|
domains,
|
|
13564
13721
|
cookies,
|
|
13565
13722
|
localStorage,
|
|
13566
|
-
snapshot
|
|
13723
|
+
snapshot: snapshot2
|
|
13567
13724
|
};
|
|
13568
13725
|
writeSessionFile(getSessionPath(normalizedName), data);
|
|
13569
13726
|
return {
|
|
@@ -13918,8 +14075,8 @@ function compactSearchLikeResult(text) {
|
|
|
13918
14075
|
return limitText(cleaned, 16, 1400);
|
|
13919
14076
|
}
|
|
13920
14077
|
const summary = cleaned.slice(0, markerIndex).trim();
|
|
13921
|
-
const
|
|
13922
|
-
return [summary, compactReadPageResult(
|
|
14078
|
+
const snapshot2 = cleaned.slice(markerIndex + marker.length).trim();
|
|
14079
|
+
return [summary, compactReadPageResult(snapshot2)].filter(Boolean).join("\n\n");
|
|
13923
14080
|
}
|
|
13924
14081
|
function compactCurrentTabResult(text) {
|
|
13925
14082
|
try {
|
|
@@ -14968,7 +15125,7 @@ async function inspectElement(wc, selector, limit = 8) {
|
|
|
14968
15125
|
return lines.join("\n");
|
|
14969
15126
|
}
|
|
14970
15127
|
async function getLocaleSnapshot(wc) {
|
|
14971
|
-
const
|
|
15128
|
+
const snapshot2 = await executePageScript(
|
|
14972
15129
|
wc,
|
|
14973
15130
|
`
|
|
14974
15131
|
(function() {
|
|
@@ -14987,13 +15144,13 @@ async function getLocaleSnapshot(wc) {
|
|
|
14987
15144
|
label: "locale snapshot"
|
|
14988
15145
|
}
|
|
14989
15146
|
);
|
|
14990
|
-
if (!
|
|
15147
|
+
if (!snapshot2 || snapshot2 === PAGE_SCRIPT_TIMEOUT || typeof snapshot2 !== "object") {
|
|
14991
15148
|
return null;
|
|
14992
15149
|
}
|
|
14993
15150
|
return {
|
|
14994
|
-
lang: typeof
|
|
14995
|
-
url: typeof
|
|
14996
|
-
title: typeof
|
|
15151
|
+
lang: typeof snapshot2.lang === "string" ? snapshot2.lang.trim() : "",
|
|
15152
|
+
url: typeof snapshot2.url === "string" ? snapshot2.url : wc.getURL(),
|
|
15153
|
+
title: typeof snapshot2.title === "string" ? snapshot2.title : wc.getTitle()
|
|
14997
15154
|
};
|
|
14998
15155
|
}
|
|
14999
15156
|
function primaryLanguageTag(value) {
|
|
@@ -15009,31 +15166,31 @@ function localeChanged(before, after) {
|
|
|
15009
15166
|
const localeHint = /[?&](lang|locale|language|hl)=|\/(ja|jp|en|fr|de|es|it|ko|zh)(\/|$)/i;
|
|
15010
15167
|
return before.url !== after.url && localeHint.test(after.url);
|
|
15011
15168
|
}
|
|
15012
|
-
async function restoreLocaleSnapshot(wc,
|
|
15013
|
-
if (!
|
|
15169
|
+
async function restoreLocaleSnapshot(wc, snapshot2) {
|
|
15170
|
+
if (!snapshot2 || wc.isDestroyed()) return;
|
|
15014
15171
|
try {
|
|
15015
15172
|
if (typeof wc.canGoBack === "function" && wc.canGoBack()) {
|
|
15016
15173
|
wc.goBack();
|
|
15017
15174
|
await waitForLoad(wc, 3e3);
|
|
15018
15175
|
const reverted = await getLocaleSnapshot(wc);
|
|
15019
|
-
if (!localeChanged(
|
|
15176
|
+
if (!localeChanged(snapshot2, reverted)) {
|
|
15020
15177
|
return;
|
|
15021
15178
|
}
|
|
15022
15179
|
}
|
|
15023
15180
|
} catch (err) {
|
|
15024
15181
|
logger$c.warn("Failed to restore locale via history navigation, trying URL reload fallback:", err);
|
|
15025
15182
|
}
|
|
15026
|
-
if (
|
|
15183
|
+
if (snapshot2.url && snapshot2.url !== wc.getURL()) {
|
|
15027
15184
|
try {
|
|
15028
|
-
assertSafeURL(
|
|
15029
|
-
await wc.loadURL(
|
|
15185
|
+
assertSafeURL(snapshot2.url);
|
|
15186
|
+
await wc.loadURL(snapshot2.url);
|
|
15030
15187
|
await waitForLoad(wc, 3e3);
|
|
15031
15188
|
return;
|
|
15032
15189
|
} catch (err) {
|
|
15033
15190
|
logger$c.warn("Failed to restore locale via safe URL load, trying page reload fallback:", err);
|
|
15034
15191
|
}
|
|
15035
15192
|
}
|
|
15036
|
-
if (
|
|
15193
|
+
if (snapshot2.url) {
|
|
15037
15194
|
try {
|
|
15038
15195
|
await wc.reload();
|
|
15039
15196
|
await waitForLoad(wc, 3e3);
|
|
@@ -18983,9 +19140,9 @@ function capturePageToVault({
|
|
|
18983
19140
|
if (page.excerpt.trim()) {
|
|
18984
19141
|
bodyLines.push("## Excerpt", "", page.excerpt.trim(), "");
|
|
18985
19142
|
}
|
|
18986
|
-
const
|
|
18987
|
-
if (
|
|
18988
|
-
bodyLines.push("## Page Snapshot", "",
|
|
19143
|
+
const snapshot2 = trimContent(page.content);
|
|
19144
|
+
if (snapshot2) {
|
|
19145
|
+
bodyLines.push("## Page Snapshot", "", snapshot2, "");
|
|
18989
19146
|
}
|
|
18990
19147
|
return writeMemoryNote({
|
|
18991
19148
|
title: noteTitle,
|
|
@@ -19410,8 +19567,8 @@ Exception: ${result.exceptionDetails}`);
|
|
|
19410
19567
|
{},
|
|
19411
19568
|
async () => {
|
|
19412
19569
|
const session = getOrCreateSession(tabManager);
|
|
19413
|
-
const
|
|
19414
|
-
return JSON.stringify(
|
|
19570
|
+
const snapshot2 = await session.getPerformanceSnapshot();
|
|
19571
|
+
return JSON.stringify(snapshot2, null, 2);
|
|
19415
19572
|
}
|
|
19416
19573
|
);
|
|
19417
19574
|
}
|
|
@@ -19469,6 +19626,16 @@ const logger$b = createLogger("VaultShared");
|
|
|
19469
19626
|
const ALGORITHM = "aes-256-gcm";
|
|
19470
19627
|
const IV_LENGTH = 12;
|
|
19471
19628
|
const AUTH_TAG_LENGTH = 16;
|
|
19629
|
+
const KEY_STORAGE_PREFIX = "base64:";
|
|
19630
|
+
function encodeEncryptionKeyForStorage(key2) {
|
|
19631
|
+
return `${KEY_STORAGE_PREFIX}${key2.toString("base64")}`;
|
|
19632
|
+
}
|
|
19633
|
+
function decodeEncryptionKeyFromStorage(value) {
|
|
19634
|
+
if (value.startsWith(KEY_STORAGE_PREFIX)) {
|
|
19635
|
+
return Buffer.from(value.slice(KEY_STORAGE_PREFIX.length), "base64");
|
|
19636
|
+
}
|
|
19637
|
+
return Buffer.from(value, "utf-8");
|
|
19638
|
+
}
|
|
19472
19639
|
function assertSecretStorageAvailable(customMessage) {
|
|
19473
19640
|
if (!electron.safeStorage.isEncryptionAvailable()) {
|
|
19474
19641
|
throw new Error(
|
|
@@ -19481,12 +19648,19 @@ function getOrCreateEncryptionKey(keyFilename) {
|
|
|
19481
19648
|
const keyPath = path$1.join(electron.app.getPath("userData"), keyFilename);
|
|
19482
19649
|
if (fs$1.existsSync(keyPath)) {
|
|
19483
19650
|
const encryptedKey = fs$1.readFileSync(keyPath);
|
|
19484
|
-
|
|
19651
|
+
const key22 = decodeEncryptionKeyFromStorage(
|
|
19652
|
+
electron.safeStorage.decryptString(encryptedKey)
|
|
19653
|
+
);
|
|
19654
|
+
if (key22.length !== 32) {
|
|
19655
|
+
throw new Error("Stored vault encryption key has an invalid length.");
|
|
19656
|
+
}
|
|
19657
|
+
return key22;
|
|
19485
19658
|
}
|
|
19486
19659
|
const key2 = crypto$2.randomBytes(32);
|
|
19487
19660
|
fs$1.mkdirSync(path$1.dirname(keyPath), { recursive: true });
|
|
19488
|
-
const encrypted = electron.safeStorage.encryptString(key2
|
|
19661
|
+
const encrypted = electron.safeStorage.encryptString(encodeEncryptionKeyForStorage(key2));
|
|
19489
19662
|
fs$1.writeFileSync(keyPath, encrypted, { mode: 384 });
|
|
19663
|
+
fs$1.chmodSync(keyPath, 384);
|
|
19490
19664
|
return key2;
|
|
19491
19665
|
}
|
|
19492
19666
|
function createEncryptDecrypt(keyFilename) {
|
|
@@ -19551,7 +19725,7 @@ function createVaultIO(vaultFilename, encrypt2, decrypt2) {
|
|
|
19551
19725
|
const encrypted = encrypt2(json);
|
|
19552
19726
|
const vaultPath = getVaultPath();
|
|
19553
19727
|
fs$1.mkdirSync(path$1.dirname(vaultPath), { recursive: true });
|
|
19554
|
-
fs$1.writeFileSync(vaultPath, encrypted);
|
|
19728
|
+
fs$1.writeFileSync(vaultPath, encrypted, { mode: 384 });
|
|
19555
19729
|
fs$1.chmodSync(vaultPath, 384);
|
|
19556
19730
|
cachedEntries = entries;
|
|
19557
19731
|
}
|
|
@@ -19560,15 +19734,21 @@ function createVaultIO(vaultFilename, encrypt2, decrypt2) {
|
|
|
19560
19734
|
}
|
|
19561
19735
|
return { loadVault: loadVault2, saveVault: saveVault2, resetCache };
|
|
19562
19736
|
}
|
|
19563
|
-
function
|
|
19564
|
-
|
|
19565
|
-
|
|
19566
|
-
|
|
19567
|
-
|
|
19568
|
-
const
|
|
19569
|
-
return
|
|
19737
|
+
function normalizeCredentialHost(value) {
|
|
19738
|
+
try {
|
|
19739
|
+
const parsed = new URL(value.includes("://") ? value : `https://${value}`);
|
|
19740
|
+
return parsed.hostname.toLowerCase().replace(/^www\./, "");
|
|
19741
|
+
} catch {
|
|
19742
|
+
const normalized = value.toLowerCase().trim().replace(/^(https?:\/\/)?(www\.)?/, "").replace(/\/.*$/, "");
|
|
19743
|
+
return normalized && !normalized.includes(" ") ? normalized : null;
|
|
19570
19744
|
}
|
|
19571
|
-
|
|
19745
|
+
}
|
|
19746
|
+
function domainMatches(pattern, hostname) {
|
|
19747
|
+
const isWildcard = pattern.trim().startsWith("*.");
|
|
19748
|
+
const p = normalizeCredentialHost(isWildcard ? pattern.slice(2) : pattern);
|
|
19749
|
+
const h = normalizeCredentialHost(hostname);
|
|
19750
|
+
if (!p || !h) return false;
|
|
19751
|
+
return isWildcard ? h.endsWith("." + p) : p === h;
|
|
19572
19752
|
}
|
|
19573
19753
|
function generateTotpCode(secret) {
|
|
19574
19754
|
const epoch = Math.floor(Date.now() / 1e3);
|
|
@@ -19601,12 +19781,20 @@ function createAuditLog(filename, maxEntries) {
|
|
|
19601
19781
|
try {
|
|
19602
19782
|
const auditPath = getAuditPath2();
|
|
19603
19783
|
fs$1.mkdirSync(path$1.dirname(auditPath), { recursive: true });
|
|
19604
|
-
fs$1.appendFileSync(auditPath, JSON.stringify(entry) + "\n"
|
|
19784
|
+
fs$1.appendFileSync(auditPath, JSON.stringify(entry) + "\n", {
|
|
19785
|
+
encoding: "utf-8",
|
|
19786
|
+
mode: 384
|
|
19787
|
+
});
|
|
19788
|
+
fs$1.chmodSync(auditPath, 384);
|
|
19605
19789
|
try {
|
|
19606
19790
|
const lines = fs$1.readFileSync(auditPath, "utf-8").split("\n").filter((l) => l.trim());
|
|
19607
19791
|
if (lines.length > maxEntries) {
|
|
19608
19792
|
const trimmed = lines.slice(-maxEntries);
|
|
19609
|
-
fs$1.writeFileSync(auditPath, trimmed.join("\n") + "\n"
|
|
19793
|
+
fs$1.writeFileSync(auditPath, trimmed.join("\n") + "\n", {
|
|
19794
|
+
encoding: "utf-8",
|
|
19795
|
+
mode: 384
|
|
19796
|
+
});
|
|
19797
|
+
fs$1.chmodSync(auditPath, 384);
|
|
19610
19798
|
}
|
|
19611
19799
|
} catch {
|
|
19612
19800
|
}
|
|
@@ -19639,12 +19827,8 @@ function listEntries$1() {
|
|
|
19639
19827
|
return loadVault$1().map(({ password, totpSecret, ...rest }) => rest);
|
|
19640
19828
|
}
|
|
19641
19829
|
function findEntriesForDomain(url) {
|
|
19642
|
-
|
|
19643
|
-
|
|
19644
|
-
hostname = new URL(url).hostname;
|
|
19645
|
-
} catch {
|
|
19646
|
-
return [];
|
|
19647
|
-
}
|
|
19830
|
+
const hostname = normalizeCredentialHost(url);
|
|
19831
|
+
if (!hostname) return [];
|
|
19648
19832
|
return loadVault$1().filter((e) => domainMatches(e.domainPattern, hostname)).map(({ password, totpSecret, ...rest }) => rest);
|
|
19649
19833
|
}
|
|
19650
19834
|
function addEntry(entry) {
|
|
@@ -19737,7 +19921,11 @@ function appendAuditEntry(entry) {
|
|
|
19737
19921
|
try {
|
|
19738
19922
|
const auditPath = getAuditPath();
|
|
19739
19923
|
fs$1.mkdirSync(path$1.dirname(auditPath), { recursive: true });
|
|
19740
|
-
fs$1.appendFileSync(auditPath, JSON.stringify(entry) + "\n"
|
|
19924
|
+
fs$1.appendFileSync(auditPath, JSON.stringify(entry) + "\n", {
|
|
19925
|
+
encoding: "utf-8",
|
|
19926
|
+
mode: 384
|
|
19927
|
+
});
|
|
19928
|
+
fs$1.chmodSync(auditPath, 384);
|
|
19741
19929
|
} catch (err) {
|
|
19742
19930
|
logger$a.error("Failed to write audit log:", err);
|
|
19743
19931
|
}
|
|
@@ -19767,12 +19955,7 @@ const auditLog = createAuditLog(
|
|
|
19767
19955
|
AUDIT_MAX_ENTRIES
|
|
19768
19956
|
);
|
|
19769
19957
|
function extractDomain(url) {
|
|
19770
|
-
|
|
19771
|
-
const parsed = new URL(url.startsWith("http") ? url : `https://${url}`);
|
|
19772
|
-
return parsed.hostname.toLowerCase();
|
|
19773
|
-
} catch {
|
|
19774
|
-
return url.toLowerCase().replace(/^(https?:\/\/)?(www\.)?/, "").replace(/\/.*$/, "");
|
|
19775
|
-
}
|
|
19958
|
+
return normalizeCredentialHost(url) ?? "";
|
|
19776
19959
|
}
|
|
19777
19960
|
function listEntries() {
|
|
19778
19961
|
return loadVault().map(({ password, totpSecret, ...rest }) => rest);
|
|
@@ -19957,6 +20140,7 @@ function writeMcpAuthFile(endpoint, token) {
|
|
|
19957
20140
|
JSON.stringify({ endpoint, token, pid: process.pid }, null, 2) + "\n",
|
|
19958
20141
|
{ mode: 384 }
|
|
19959
20142
|
);
|
|
20143
|
+
fs$1.chmodSync(filePath2, 384);
|
|
19960
20144
|
} catch (err) {
|
|
19961
20145
|
logger$9.warn("Failed to write auth file:", err);
|
|
19962
20146
|
}
|
|
@@ -19982,10 +20166,18 @@ function clearMcpAuthFile() {
|
|
|
19982
20166
|
) + "\n",
|
|
19983
20167
|
{ mode: 384 }
|
|
19984
20168
|
);
|
|
20169
|
+
fs$1.chmodSync(filePath2, 384);
|
|
19985
20170
|
} catch (err) {
|
|
19986
20171
|
logger$9.warn("Failed to clear auth file:", err);
|
|
19987
20172
|
}
|
|
19988
20173
|
}
|
|
20174
|
+
function regenerateMcpAuthToken() {
|
|
20175
|
+
const endpoint = getRuntimeHealth().mcp.endpoint;
|
|
20176
|
+
if (!httpServer || !endpoint) return null;
|
|
20177
|
+
mcpAuthToken = crypto$2.randomBytes(32).toString("hex");
|
|
20178
|
+
writeMcpAuthFile(endpoint, mcpAuthToken);
|
|
20179
|
+
return { endpoint };
|
|
20180
|
+
}
|
|
19989
20181
|
function asTextResponse(text) {
|
|
19990
20182
|
return { content: [{ type: "text", text }] };
|
|
19991
20183
|
}
|
|
@@ -20011,6 +20203,11 @@ function asPromptResponse(text) {
|
|
|
20011
20203
|
function isDangerousMcpAction(name) {
|
|
20012
20204
|
return name === "close_tab" || isDangerousAction(name);
|
|
20013
20205
|
}
|
|
20206
|
+
function requiresExplicitMcpApproval(name, args) {
|
|
20207
|
+
if (name === "delete_session" || name === "close_tab" || name === "load_session") return true;
|
|
20208
|
+
if (name === "remove_bookmark_folder" && args.delete_contents === true) return true;
|
|
20209
|
+
return false;
|
|
20210
|
+
}
|
|
20014
20211
|
function getActiveTabSummary(tabManager) {
|
|
20015
20212
|
const activeTab = tabManager.getActiveTab();
|
|
20016
20213
|
const activeTabId = tabManager.getActiveTabId();
|
|
@@ -20099,6 +20296,7 @@ async function withAction(runtime2, tabManager, name, args, executor) {
|
|
|
20099
20296
|
args,
|
|
20100
20297
|
tabId: tabManager.getActiveTabId(),
|
|
20101
20298
|
dangerous: isDangerousMcpAction(name),
|
|
20299
|
+
requiresApproval: requiresExplicitMcpApproval(name, args),
|
|
20102
20300
|
executor
|
|
20103
20301
|
});
|
|
20104
20302
|
const stateInfo = await getPostActionState(tabManager, name);
|
|
@@ -23841,6 +24039,43 @@ function uninstallKit(id, scheduledKitIds) {
|
|
|
23841
24039
|
return errorResult("Failed to remove the kit file.");
|
|
23842
24040
|
}
|
|
23843
24041
|
}
|
|
24042
|
+
const trustedIpcSenderIds = /* @__PURE__ */ new Set();
|
|
24043
|
+
function registerTrustedIpcSender(wc) {
|
|
24044
|
+
trustedIpcSenderIds.add(wc.id);
|
|
24045
|
+
wc.once("destroyed", () => trustedIpcSenderIds.delete(wc.id));
|
|
24046
|
+
}
|
|
24047
|
+
function assertTrustedIpcSender(event) {
|
|
24048
|
+
if (!trustedIpcSenderIds.has(event.sender.id)) {
|
|
24049
|
+
throw new Error("Blocked IPC from untrusted renderer");
|
|
24050
|
+
}
|
|
24051
|
+
}
|
|
24052
|
+
function isManagedTabIpcSender(event, tabManager) {
|
|
24053
|
+
return Boolean(tabManager.findTabByWebContentsId(event.sender.id));
|
|
24054
|
+
}
|
|
24055
|
+
function assertString(value, name) {
|
|
24056
|
+
if (typeof value !== "string") throw new Error(`${name} must be a string`);
|
|
24057
|
+
}
|
|
24058
|
+
function assertOptionalString(value, name) {
|
|
24059
|
+
if (value !== void 0 && typeof value !== "string") {
|
|
24060
|
+
throw new Error(`${name} must be a string`);
|
|
24061
|
+
}
|
|
24062
|
+
}
|
|
24063
|
+
function assertNumber(value, name) {
|
|
24064
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
24065
|
+
throw new Error(`${name} must be a number`);
|
|
24066
|
+
}
|
|
24067
|
+
}
|
|
24068
|
+
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
24069
|
+
function isValidEmail(value) {
|
|
24070
|
+
return EMAIL_RE.test(value.trim());
|
|
24071
|
+
}
|
|
24072
|
+
function getActiveTabInfo(tabManager) {
|
|
24073
|
+
const tab = tabManager.getActiveTab();
|
|
24074
|
+
if (!tab) return null;
|
|
24075
|
+
const wc = tab.view.webContents;
|
|
24076
|
+
if (wc.isDestroyed()) return null;
|
|
24077
|
+
return { tab, wc };
|
|
24078
|
+
}
|
|
23844
24079
|
const logger$7 = createLogger("Scheduler");
|
|
23845
24080
|
let jobs = [];
|
|
23846
24081
|
let removeIdleListener = null;
|
|
@@ -23864,7 +24099,13 @@ function loadJobs() {
|
|
|
23864
24099
|
}
|
|
23865
24100
|
function saveJobs() {
|
|
23866
24101
|
try {
|
|
23867
|
-
|
|
24102
|
+
const jobsPath = getJobsPath();
|
|
24103
|
+
fs$1.mkdirSync(path$1.dirname(jobsPath), { recursive: true });
|
|
24104
|
+
fs$1.writeFileSync(jobsPath, JSON.stringify(jobs, null, 2), {
|
|
24105
|
+
encoding: "utf-8",
|
|
24106
|
+
mode: 384
|
|
24107
|
+
});
|
|
24108
|
+
fs$1.chmodSync(jobsPath, 384);
|
|
23868
24109
|
} catch (err) {
|
|
23869
24110
|
logger$7.warn("Failed to save jobs:", err);
|
|
23870
24111
|
}
|
|
@@ -24068,8 +24309,12 @@ function registerScheduleHandlers(windowState, runtime2, sendToAll) {
|
|
|
24068
24309
|
tick(windowState, runtime2);
|
|
24069
24310
|
setInterval(() => tick(windowState, runtime2), 6e4);
|
|
24070
24311
|
}, msToNextMinute);
|
|
24071
|
-
electron.ipcMain.handle(Channels.SCHEDULE_GET_ALL, () =>
|
|
24072
|
-
|
|
24312
|
+
electron.ipcMain.handle(Channels.SCHEDULE_GET_ALL, (event) => {
|
|
24313
|
+
assertTrustedIpcSender(event);
|
|
24314
|
+
return jobs;
|
|
24315
|
+
});
|
|
24316
|
+
electron.ipcMain.handle(Channels.SCHEDULE_CREATE, (event, rawJob) => {
|
|
24317
|
+
assertTrustedIpcSender(event);
|
|
24073
24318
|
if (!isValidJobData(rawJob)) {
|
|
24074
24319
|
throw new Error(
|
|
24075
24320
|
"Invalid job data. Required: kitId, kitName, kitIcon, renderedPrompt, schedule, enabled."
|
|
@@ -24086,7 +24331,8 @@ function registerScheduleHandlers(windowState, runtime2, sendToAll) {
|
|
|
24086
24331
|
sendToAll(Channels.SCHEDULE_JOBS_UPDATE, jobs);
|
|
24087
24332
|
return newJob;
|
|
24088
24333
|
});
|
|
24089
|
-
electron.ipcMain.handle(Channels.SCHEDULE_UPDATE, (
|
|
24334
|
+
electron.ipcMain.handle(Channels.SCHEDULE_UPDATE, (event, id, updates) => {
|
|
24335
|
+
assertTrustedIpcSender(event);
|
|
24090
24336
|
if (typeof id !== "string") throw new Error("id must be a string");
|
|
24091
24337
|
const job = jobs.find((j) => j.id === id);
|
|
24092
24338
|
if (!job) return null;
|
|
@@ -24112,7 +24358,8 @@ function registerScheduleHandlers(windowState, runtime2, sendToAll) {
|
|
|
24112
24358
|
sendToAll(Channels.SCHEDULE_JOBS_UPDATE, jobs);
|
|
24113
24359
|
return job;
|
|
24114
24360
|
});
|
|
24115
|
-
electron.ipcMain.handle(Channels.SCHEDULE_DELETE, (
|
|
24361
|
+
electron.ipcMain.handle(Channels.SCHEDULE_DELETE, (event, id) => {
|
|
24362
|
+
assertTrustedIpcSender(event);
|
|
24116
24363
|
if (typeof id !== "string") throw new Error("id must be a string");
|
|
24117
24364
|
const before = jobs.length;
|
|
24118
24365
|
jobs = jobs.filter((j) => j.id !== id);
|
|
@@ -24122,30 +24369,6 @@ function registerScheduleHandlers(windowState, runtime2, sendToAll) {
|
|
|
24122
24369
|
return true;
|
|
24123
24370
|
});
|
|
24124
24371
|
}
|
|
24125
|
-
function assertString(value, name) {
|
|
24126
|
-
if (typeof value !== "string") throw new Error(`${name} must be a string`);
|
|
24127
|
-
}
|
|
24128
|
-
function assertOptionalString(value, name) {
|
|
24129
|
-
if (value !== void 0 && typeof value !== "string") {
|
|
24130
|
-
throw new Error(`${name} must be a string`);
|
|
24131
|
-
}
|
|
24132
|
-
}
|
|
24133
|
-
function assertNumber(value, name) {
|
|
24134
|
-
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
24135
|
-
throw new Error(`${name} must be a number`);
|
|
24136
|
-
}
|
|
24137
|
-
}
|
|
24138
|
-
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
24139
|
-
function isValidEmail(value) {
|
|
24140
|
-
return EMAIL_RE.test(value.trim());
|
|
24141
|
-
}
|
|
24142
|
-
function getActiveTabInfo(tabManager) {
|
|
24143
|
-
const tab = tabManager.getActiveTab();
|
|
24144
|
-
if (!tab) return null;
|
|
24145
|
-
const wc = tab.view.webContents;
|
|
24146
|
-
if (wc.isDestroyed()) return null;
|
|
24147
|
-
return { tab, wc };
|
|
24148
|
-
}
|
|
24149
24372
|
const SAVE_DEBOUNCE_MS = 250;
|
|
24150
24373
|
const PROFILE_FIELDS = [
|
|
24151
24374
|
"label",
|
|
@@ -24485,24 +24708,29 @@ function sanitizeAutofillUpdates(value) {
|
|
|
24485
24708
|
return updates;
|
|
24486
24709
|
}
|
|
24487
24710
|
function registerAutofillHandlers(windowState) {
|
|
24488
|
-
electron.ipcMain.handle(Channels.AUTOFILL_LIST, () => {
|
|
24711
|
+
electron.ipcMain.handle(Channels.AUTOFILL_LIST, (event) => {
|
|
24712
|
+
assertTrustedIpcSender(event);
|
|
24489
24713
|
return listProfiles();
|
|
24490
24714
|
});
|
|
24491
24715
|
electron.ipcMain.handle(
|
|
24492
24716
|
Channels.AUTOFILL_ADD,
|
|
24493
|
-
(
|
|
24717
|
+
(event, profile) => {
|
|
24718
|
+
assertTrustedIpcSender(event);
|
|
24494
24719
|
return addProfile(sanitizeAutofillProfile(profile));
|
|
24495
24720
|
}
|
|
24496
24721
|
);
|
|
24497
|
-
electron.ipcMain.handle(Channels.AUTOFILL_UPDATE, (
|
|
24722
|
+
electron.ipcMain.handle(Channels.AUTOFILL_UPDATE, (event, id, updates) => {
|
|
24723
|
+
assertTrustedIpcSender(event);
|
|
24498
24724
|
assertString(id, "id");
|
|
24499
24725
|
return updateProfile(id, sanitizeAutofillUpdates(updates));
|
|
24500
24726
|
});
|
|
24501
|
-
electron.ipcMain.handle(Channels.AUTOFILL_DELETE, (
|
|
24727
|
+
electron.ipcMain.handle(Channels.AUTOFILL_DELETE, (event, id) => {
|
|
24728
|
+
assertTrustedIpcSender(event);
|
|
24502
24729
|
assertString(id, "id");
|
|
24503
24730
|
return deleteProfile(id);
|
|
24504
24731
|
});
|
|
24505
|
-
electron.ipcMain.handle(Channels.AUTOFILL_FILL, async (
|
|
24732
|
+
electron.ipcMain.handle(Channels.AUTOFILL_FILL, async (event, profileId) => {
|
|
24733
|
+
assertTrustedIpcSender(event);
|
|
24506
24734
|
assertString(profileId, "profileId");
|
|
24507
24735
|
const profile = getProfile(profileId);
|
|
24508
24736
|
if (!profile) throw new Error("Profile not found");
|
|
@@ -24537,13 +24765,26 @@ function registerAutofillHandlers(windowState) {
|
|
|
24537
24765
|
});
|
|
24538
24766
|
}
|
|
24539
24767
|
function registerPageDiffHandlers(windowState, sendToRendererViews) {
|
|
24540
|
-
|
|
24768
|
+
const pageEventBuckets = /* @__PURE__ */ new Map();
|
|
24769
|
+
const allowPageEvent = (webContentsId) => {
|
|
24770
|
+
const now = Date.now();
|
|
24771
|
+
const bucket = pageEventBuckets.get(webContentsId);
|
|
24772
|
+
if (!bucket || bucket.resetAt <= now) {
|
|
24773
|
+
pageEventBuckets.set(webContentsId, { count: 1, resetAt: now + 1e3 });
|
|
24774
|
+
return true;
|
|
24775
|
+
}
|
|
24776
|
+
bucket.count += 1;
|
|
24777
|
+
return bucket.count <= 20;
|
|
24778
|
+
};
|
|
24779
|
+
electron.ipcMain.handle(Channels.PAGE_DIFF_GET, (event) => {
|
|
24780
|
+
assertTrustedIpcSender(event);
|
|
24541
24781
|
const activeTab = windowState.tabManager.getActiveTab();
|
|
24542
24782
|
const wc = activeTab?.view.webContents;
|
|
24543
24783
|
if (!wc) return null;
|
|
24544
24784
|
return getLatestPageDiff(wc.getURL());
|
|
24545
24785
|
});
|
|
24546
|
-
electron.ipcMain.handle(Channels.PAGE_DIFF_HISTORY, () => {
|
|
24786
|
+
electron.ipcMain.handle(Channels.PAGE_DIFF_HISTORY, (event) => {
|
|
24787
|
+
assertTrustedIpcSender(event);
|
|
24547
24788
|
try {
|
|
24548
24789
|
if (!isPremiumActiveState(getPremiumState())) {
|
|
24549
24790
|
return { error: "Premium required" };
|
|
@@ -24559,21 +24800,27 @@ function registerPageDiffHandlers(windowState, sendToRendererViews) {
|
|
|
24559
24800
|
electron.ipcMain.on(Channels.PAGE_DIFF_ACTIVITY, (event) => {
|
|
24560
24801
|
const wc = event.sender;
|
|
24561
24802
|
if (!wc || wc.isDestroyed()) return;
|
|
24803
|
+
if (!isManagedTabIpcSender(event, windowState.tabManager)) return;
|
|
24804
|
+
if (!allowPageEvent(wc.id)) return;
|
|
24562
24805
|
notePageMutationActivity(wc, sendToRendererViews);
|
|
24563
24806
|
});
|
|
24564
24807
|
electron.ipcMain.on(Channels.PAGE_DIFF_DIRTY, (event) => {
|
|
24565
24808
|
const wc = event.sender;
|
|
24566
24809
|
if (!wc || wc.isDestroyed()) return;
|
|
24810
|
+
if (!isManagedTabIpcSender(event, windowState.tabManager)) return;
|
|
24811
|
+
if (!allowPageEvent(wc.id)) return;
|
|
24567
24812
|
schedulePageSnapshotCapture(wc, sendToRendererViews);
|
|
24568
24813
|
});
|
|
24569
24814
|
}
|
|
24570
24815
|
function registerVaultHandlers() {
|
|
24571
|
-
electron.ipcMain.handle(Channels.VAULT_LIST, () => {
|
|
24816
|
+
electron.ipcMain.handle(Channels.VAULT_LIST, (event) => {
|
|
24817
|
+
assertTrustedIpcSender(event);
|
|
24572
24818
|
return listEntries$1();
|
|
24573
24819
|
});
|
|
24574
24820
|
electron.ipcMain.handle(
|
|
24575
24821
|
Channels.VAULT_ADD,
|
|
24576
|
-
(
|
|
24822
|
+
(event, entry) => {
|
|
24823
|
+
assertTrustedIpcSender(event);
|
|
24577
24824
|
if (!entry || typeof entry !== "object") {
|
|
24578
24825
|
throw new Error("Invalid vault entry");
|
|
24579
24826
|
}
|
|
@@ -24598,7 +24845,8 @@ function registerVaultHandlers() {
|
|
|
24598
24845
|
);
|
|
24599
24846
|
electron.ipcMain.handle(
|
|
24600
24847
|
Channels.VAULT_UPDATE,
|
|
24601
|
-
(
|
|
24848
|
+
(event, id, updates) => {
|
|
24849
|
+
assertTrustedIpcSender(event);
|
|
24602
24850
|
assertString(id, "id");
|
|
24603
24851
|
if (!updates || typeof updates !== "object") {
|
|
24604
24852
|
throw new Error("Invalid updates");
|
|
@@ -24606,12 +24854,14 @@ function registerVaultHandlers() {
|
|
|
24606
24854
|
return updateEntry$1(id, updates) !== null;
|
|
24607
24855
|
}
|
|
24608
24856
|
);
|
|
24609
|
-
electron.ipcMain.handle(Channels.VAULT_REMOVE, (
|
|
24857
|
+
electron.ipcMain.handle(Channels.VAULT_REMOVE, (event, id) => {
|
|
24858
|
+
assertTrustedIpcSender(event);
|
|
24610
24859
|
assertString(id, "id");
|
|
24611
24860
|
trackVaultAction("credential_removed");
|
|
24612
24861
|
return removeEntry$1(id);
|
|
24613
24862
|
});
|
|
24614
|
-
electron.ipcMain.handle(Channels.VAULT_AUDIT_LOG, (
|
|
24863
|
+
electron.ipcMain.handle(Channels.VAULT_AUDIT_LOG, (event, limit) => {
|
|
24864
|
+
assertTrustedIpcSender(event);
|
|
24615
24865
|
return readAuditLog$1(limit);
|
|
24616
24866
|
});
|
|
24617
24867
|
}
|
|
@@ -24630,17 +24880,20 @@ function normalizeTags(value) {
|
|
|
24630
24880
|
return tags.length > 0 ? tags : void 0;
|
|
24631
24881
|
}
|
|
24632
24882
|
function registerHumanVaultHandlers() {
|
|
24633
|
-
electron.ipcMain.handle(Channels.HUMAN_VAULT_LIST, (
|
|
24883
|
+
electron.ipcMain.handle(Channels.HUMAN_VAULT_LIST, (event, domain) => {
|
|
24884
|
+
assertTrustedIpcSender(event);
|
|
24634
24885
|
if (domain !== void 0) assertString(domain, "domain");
|
|
24635
24886
|
return domain ? findForDomain(domain) : listEntries();
|
|
24636
24887
|
});
|
|
24637
|
-
electron.ipcMain.handle(Channels.HUMAN_VAULT_GET, (
|
|
24888
|
+
electron.ipcMain.handle(Channels.HUMAN_VAULT_GET, (event, id) => {
|
|
24889
|
+
assertTrustedIpcSender(event);
|
|
24638
24890
|
assertString(id, "id");
|
|
24639
24891
|
return getEntrySafe(id);
|
|
24640
24892
|
});
|
|
24641
24893
|
electron.ipcMain.handle(
|
|
24642
24894
|
Channels.HUMAN_VAULT_SAVE,
|
|
24643
|
-
(
|
|
24895
|
+
(event, input) => {
|
|
24896
|
+
assertTrustedIpcSender(event);
|
|
24644
24897
|
if (!input || typeof input !== "object") {
|
|
24645
24898
|
throw new Error("Invalid credential entry");
|
|
24646
24899
|
}
|
|
@@ -24670,7 +24923,8 @@ function registerHumanVaultHandlers() {
|
|
|
24670
24923
|
);
|
|
24671
24924
|
electron.ipcMain.handle(
|
|
24672
24925
|
Channels.HUMAN_VAULT_UPDATE,
|
|
24673
|
-
(
|
|
24926
|
+
(event, id, updates) => {
|
|
24927
|
+
assertTrustedIpcSender(event);
|
|
24674
24928
|
assertString(id, "id");
|
|
24675
24929
|
if (!updates || typeof updates !== "object") {
|
|
24676
24930
|
throw new Error("Invalid updates");
|
|
@@ -24712,26 +24966,31 @@ function registerHumanVaultHandlers() {
|
|
|
24712
24966
|
return safe;
|
|
24713
24967
|
}
|
|
24714
24968
|
);
|
|
24715
|
-
electron.ipcMain.handle(Channels.HUMAN_VAULT_REMOVE, (
|
|
24969
|
+
electron.ipcMain.handle(Channels.HUMAN_VAULT_REMOVE, (event, id) => {
|
|
24970
|
+
assertTrustedIpcSender(event);
|
|
24716
24971
|
assertString(id, "id");
|
|
24717
24972
|
return removeEntry(id);
|
|
24718
24973
|
});
|
|
24719
|
-
electron.ipcMain.handle(Channels.HUMAN_VAULT_AUDIT_LOG, (
|
|
24974
|
+
electron.ipcMain.handle(Channels.HUMAN_VAULT_AUDIT_LOG, (event, limit) => {
|
|
24975
|
+
assertTrustedIpcSender(event);
|
|
24720
24976
|
return readAuditLog(limit);
|
|
24721
24977
|
});
|
|
24722
24978
|
}
|
|
24723
24979
|
function registerWindowControlHandlers(mainWindow) {
|
|
24724
|
-
electron.ipcMain.handle(Channels.WINDOW_MINIMIZE, () => {
|
|
24980
|
+
electron.ipcMain.handle(Channels.WINDOW_MINIMIZE, (event) => {
|
|
24981
|
+
assertTrustedIpcSender(event);
|
|
24725
24982
|
mainWindow.minimize();
|
|
24726
24983
|
});
|
|
24727
|
-
electron.ipcMain.handle(Channels.WINDOW_MAXIMIZE, () => {
|
|
24984
|
+
electron.ipcMain.handle(Channels.WINDOW_MAXIMIZE, (event) => {
|
|
24985
|
+
assertTrustedIpcSender(event);
|
|
24728
24986
|
if (mainWindow.isMaximized()) {
|
|
24729
24987
|
mainWindow.unmaximize();
|
|
24730
24988
|
} else {
|
|
24731
24989
|
mainWindow.maximize();
|
|
24732
24990
|
}
|
|
24733
24991
|
});
|
|
24734
|
-
electron.ipcMain.handle(Channels.WINDOW_CLOSE, () => {
|
|
24992
|
+
electron.ipcMain.handle(Channels.WINDOW_CLOSE, (event) => {
|
|
24993
|
+
assertTrustedIpcSender(event);
|
|
24735
24994
|
mainWindow.close();
|
|
24736
24995
|
});
|
|
24737
24996
|
}
|
|
@@ -24870,6 +25129,32 @@ function installAdBlockingForSession(ses, tabManager) {
|
|
|
24870
25129
|
});
|
|
24871
25130
|
}
|
|
24872
25131
|
const filePath$1 = () => path$1.join(electron.app.getPath("userData"), "vessel-downloads.json");
|
|
25132
|
+
const EXECUTABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
25133
|
+
".appimage",
|
|
25134
|
+
".bat",
|
|
25135
|
+
".cmd",
|
|
25136
|
+
".command",
|
|
25137
|
+
".desktop",
|
|
25138
|
+
".exe",
|
|
25139
|
+
".msi",
|
|
25140
|
+
".ps1",
|
|
25141
|
+
".scr",
|
|
25142
|
+
".sh"
|
|
25143
|
+
]);
|
|
25144
|
+
function hasMisleadingDoubleExtension(filename) {
|
|
25145
|
+
return /\.(pdf|docx?|xlsx?|pptx?|png|jpe?g|gif|txt|zip)\.(exe|msi|bat|cmd|ps1|sh|scr|appimage)$/i.test(filename);
|
|
25146
|
+
}
|
|
25147
|
+
function isExecutableDownload(savePath) {
|
|
25148
|
+
return EXECUTABLE_EXTENSIONS.has(path$1.extname(savePath).toLowerCase());
|
|
25149
|
+
}
|
|
25150
|
+
function executableWarningDetail(item) {
|
|
25151
|
+
return [
|
|
25152
|
+
"This file can run code on your computer. Only open it if you trust the source.",
|
|
25153
|
+
item.url ? `Source: ${item.url}` : null,
|
|
25154
|
+
item.mimeType ? `Type: ${item.mimeType}` : null,
|
|
25155
|
+
hasMisleadingDoubleExtension(item.filename) ? "Warning: this filename uses a misleading double extension." : null
|
|
25156
|
+
].filter(Boolean).join("\n");
|
|
25157
|
+
}
|
|
24873
25158
|
function parse(raw) {
|
|
24874
25159
|
if (!raw || typeof raw !== "object") return { items: [] };
|
|
24875
25160
|
const items = Array.isArray(raw.items) ? raw.items : [];
|
|
@@ -24888,13 +25173,13 @@ function persist() {
|
|
|
24888
25173
|
persistence$1.schedule();
|
|
24889
25174
|
}
|
|
24890
25175
|
function emit$1() {
|
|
24891
|
-
broadcaster$1?.(Channels.DOWNLOADS_UPDATE,
|
|
25176
|
+
broadcaster$1?.(Channels.DOWNLOADS_UPDATE, listDownloads());
|
|
24892
25177
|
}
|
|
24893
25178
|
function setDownloadBroadcaster(fn) {
|
|
24894
25179
|
broadcaster$1 = fn;
|
|
24895
25180
|
}
|
|
24896
25181
|
function listDownloads() {
|
|
24897
|
-
return state.items;
|
|
25182
|
+
return state.items.map((item) => ({ ...item }));
|
|
24898
25183
|
}
|
|
24899
25184
|
function upsertDownload(input) {
|
|
24900
25185
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -24918,7 +25203,19 @@ function clearDownloads() {
|
|
|
24918
25203
|
}
|
|
24919
25204
|
async function openDownload(id) {
|
|
24920
25205
|
const item = state.items.find((d) => d.id === id);
|
|
24921
|
-
if (!item || !fs$1.existsSync(item.savePath)) return false;
|
|
25206
|
+
if (!item || item.state !== "completed" || !fs$1.existsSync(item.savePath)) return false;
|
|
25207
|
+
if (isExecutableDownload(item.savePath)) {
|
|
25208
|
+
const result = electron.dialog.showMessageBoxSync({
|
|
25209
|
+
type: "warning",
|
|
25210
|
+
buttons: ["Cancel", "Open Anyway"],
|
|
25211
|
+
defaultId: 0,
|
|
25212
|
+
cancelId: 0,
|
|
25213
|
+
title: "Open executable download?",
|
|
25214
|
+
message: `Open ${item.filename}?`,
|
|
25215
|
+
detail: executableWarningDetail(item)
|
|
25216
|
+
});
|
|
25217
|
+
if (result !== 1) return false;
|
|
25218
|
+
}
|
|
24922
25219
|
return await electron.shell.openPath(item.savePath) === "";
|
|
24923
25220
|
}
|
|
24924
25221
|
async function showDownloadInFolder(id) {
|
|
@@ -24969,6 +25266,8 @@ function installDownloadHandlerForSession(targetSession, chromeView) {
|
|
|
24969
25266
|
const info = {
|
|
24970
25267
|
filename,
|
|
24971
25268
|
savePath,
|
|
25269
|
+
url: item.getURL(),
|
|
25270
|
+
mimeType: typeof item.getMimeType === "function" ? item.getMimeType() : void 0,
|
|
24972
25271
|
totalBytes: item.getTotalBytes(),
|
|
24973
25272
|
receivedBytes: 0,
|
|
24974
25273
|
state: "progressing"
|
|
@@ -25016,9 +25315,9 @@ function loadRenderers(chromeView, sidebarView, devtoolsPanelView) {
|
|
|
25016
25315
|
const sidebarUrl = rendererUrlFor("sidebar");
|
|
25017
25316
|
const devtoolsUrl = rendererUrlFor("devtools");
|
|
25018
25317
|
if (chromeUrl && sidebarUrl && devtoolsUrl) {
|
|
25019
|
-
chromeView.webContents
|
|
25020
|
-
sidebarView.webContents
|
|
25021
|
-
devtoolsPanelView.webContents
|
|
25318
|
+
void loadTrustedAppURL(chromeView.webContents, chromeUrl);
|
|
25319
|
+
void loadTrustedAppURL(sidebarView.webContents, sidebarUrl);
|
|
25320
|
+
void loadTrustedAppURL(devtoolsPanelView.webContents, devtoolsUrl);
|
|
25022
25321
|
} else {
|
|
25023
25322
|
const rendererFile = resolveRendererFile();
|
|
25024
25323
|
chromeView.webContents.loadFile(rendererFile, {
|
|
@@ -25244,7 +25543,7 @@ function loadPrivateRenderer(chromeView) {
|
|
|
25244
25543
|
const url = new URL(devUrl);
|
|
25245
25544
|
url.searchParams.set("view", "chrome");
|
|
25246
25545
|
url.searchParams.set("private", "1");
|
|
25247
|
-
chromeView.webContents
|
|
25546
|
+
void loadTrustedAppURL(chromeView.webContents, url.toString());
|
|
25248
25547
|
} else {
|
|
25249
25548
|
chromeView.webContents.loadFile(resolveRendererFile(), {
|
|
25250
25549
|
query: { view: "chrome", private: "1" }
|
|
@@ -25479,7 +25778,7 @@ function loadSecondaryRenderer(chromeView) {
|
|
|
25479
25778
|
const url = new URL(devUrl);
|
|
25480
25779
|
url.searchParams.set("view", "chrome");
|
|
25481
25780
|
url.searchParams.set("secondary", "1");
|
|
25482
|
-
chromeView.webContents
|
|
25781
|
+
void loadTrustedAppURL(chromeView.webContents, url.toString());
|
|
25483
25782
|
} else {
|
|
25484
25783
|
chromeView.webContents.loadFile(resolveRendererFile(), {
|
|
25485
25784
|
query: { view: "chrome", secondary: "1" }
|
|
@@ -25655,19 +25954,22 @@ function getSafeBookmarkExportName(name) {
|
|
|
25655
25954
|
return safeName || "folder";
|
|
25656
25955
|
}
|
|
25657
25956
|
function registerBookmarkHandlers() {
|
|
25658
|
-
electron.ipcMain.handle(Channels.BOOKMARKS_GET, () => {
|
|
25957
|
+
electron.ipcMain.handle(Channels.BOOKMARKS_GET, (event) => {
|
|
25958
|
+
assertTrustedIpcSender(event);
|
|
25659
25959
|
return getState();
|
|
25660
25960
|
});
|
|
25661
25961
|
electron.ipcMain.handle(
|
|
25662
25962
|
Channels.FOLDER_CREATE,
|
|
25663
|
-
(
|
|
25963
|
+
(event, name, summary) => {
|
|
25964
|
+
assertTrustedIpcSender(event);
|
|
25664
25965
|
trackBookmarkAction("folder_create");
|
|
25665
25966
|
return createFolderWithSummary(name, summary);
|
|
25666
25967
|
}
|
|
25667
25968
|
);
|
|
25668
25969
|
electron.ipcMain.handle(
|
|
25669
25970
|
Channels.BOOKMARK_SAVE,
|
|
25670
|
-
(
|
|
25971
|
+
(event, url, title, folderId, note, intent, expectedContent, keyFields, agentHints) => {
|
|
25972
|
+
assertTrustedIpcSender(event);
|
|
25671
25973
|
trackBookmarkAction("save");
|
|
25672
25974
|
const result = saveBookmarkWithPolicy(url, title, folderId, note, {
|
|
25673
25975
|
onDuplicate: "update",
|
|
@@ -25688,18 +25990,21 @@ function registerBookmarkHandlers() {
|
|
|
25688
25990
|
);
|
|
25689
25991
|
electron.ipcMain.handle(
|
|
25690
25992
|
Channels.BOOKMARK_UPDATE,
|
|
25691
|
-
(
|
|
25993
|
+
(event, id, updates) => {
|
|
25994
|
+
assertTrustedIpcSender(event);
|
|
25692
25995
|
trackBookmarkAction("save");
|
|
25693
25996
|
return updateBookmark(id, updates);
|
|
25694
25997
|
}
|
|
25695
25998
|
);
|
|
25696
|
-
electron.ipcMain.handle(Channels.BOOKMARK_REMOVE, (
|
|
25999
|
+
electron.ipcMain.handle(Channels.BOOKMARK_REMOVE, (event, id) => {
|
|
26000
|
+
assertTrustedIpcSender(event);
|
|
25697
26001
|
trackBookmarkAction("remove");
|
|
25698
26002
|
return removeBookmark(id);
|
|
25699
26003
|
});
|
|
25700
26004
|
electron.ipcMain.handle(
|
|
25701
26005
|
Channels.BOOKMARKS_EXPORT_HTML,
|
|
25702
|
-
async (
|
|
26006
|
+
async (event, options) => {
|
|
26007
|
+
assertTrustedIpcSender(event);
|
|
25703
26008
|
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
25704
26009
|
title: "Export Bookmarks",
|
|
25705
26010
|
defaultPath: "vessel-bookmarks.html",
|
|
@@ -25717,7 +26022,8 @@ function registerBookmarkHandlers() {
|
|
|
25717
26022
|
};
|
|
25718
26023
|
}
|
|
25719
26024
|
);
|
|
25720
|
-
electron.ipcMain.handle(Channels.BOOKMARKS_EXPORT_JSON, async () => {
|
|
26025
|
+
electron.ipcMain.handle(Channels.BOOKMARKS_EXPORT_JSON, async (event) => {
|
|
26026
|
+
assertTrustedIpcSender(event);
|
|
25721
26027
|
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
25722
26028
|
title: "Export Vessel Bookmark Archive",
|
|
25723
26029
|
defaultPath: "vessel-bookmarks.json",
|
|
@@ -25734,7 +26040,8 @@ function registerBookmarkHandlers() {
|
|
|
25734
26040
|
});
|
|
25735
26041
|
electron.ipcMain.handle(
|
|
25736
26042
|
Channels.FOLDER_EXPORT_HTML,
|
|
25737
|
-
async (
|
|
26043
|
+
async (event, folderId, options) => {
|
|
26044
|
+
assertTrustedIpcSender(event);
|
|
25738
26045
|
const folder = getFolder(folderId);
|
|
25739
26046
|
if (!folder) return null;
|
|
25740
26047
|
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
@@ -25755,7 +26062,8 @@ function registerBookmarkHandlers() {
|
|
|
25755
26062
|
};
|
|
25756
26063
|
}
|
|
25757
26064
|
);
|
|
25758
|
-
electron.ipcMain.handle(Channels.BOOKMARKS_IMPORT_HTML, async () => {
|
|
26065
|
+
electron.ipcMain.handle(Channels.BOOKMARKS_IMPORT_HTML, async (event) => {
|
|
26066
|
+
assertTrustedIpcSender(event);
|
|
25759
26067
|
const { canceled, filePaths } = await electron.dialog.showOpenDialog({
|
|
25760
26068
|
title: "Import Bookmarks",
|
|
25761
26069
|
filters: [
|
|
@@ -25768,7 +26076,8 @@ function registerBookmarkHandlers() {
|
|
|
25768
26076
|
trackBookmarkAction("import");
|
|
25769
26077
|
return importBookmarksFromHtml(content);
|
|
25770
26078
|
});
|
|
25771
|
-
electron.ipcMain.handle(Channels.BOOKMARKS_IMPORT_JSON, async () => {
|
|
26079
|
+
electron.ipcMain.handle(Channels.BOOKMARKS_IMPORT_JSON, async (event) => {
|
|
26080
|
+
assertTrustedIpcSender(event);
|
|
25772
26081
|
const { canceled, filePaths } = await electron.dialog.showOpenDialog({
|
|
25773
26082
|
title: "Import Bookmarks",
|
|
25774
26083
|
filters: [
|
|
@@ -25781,28 +26090,34 @@ function registerBookmarkHandlers() {
|
|
|
25781
26090
|
trackBookmarkAction("import");
|
|
25782
26091
|
return importBookmarksFromJson(content);
|
|
25783
26092
|
});
|
|
25784
|
-
electron.ipcMain.handle(Channels.FOLDER_REMOVE, (
|
|
26093
|
+
electron.ipcMain.handle(Channels.FOLDER_REMOVE, (event, id, deleteContents) => {
|
|
26094
|
+
assertTrustedIpcSender(event);
|
|
25785
26095
|
trackBookmarkAction("folder_remove");
|
|
25786
26096
|
return removeFolder(id, deleteContents ?? false);
|
|
25787
26097
|
});
|
|
25788
26098
|
electron.ipcMain.handle(
|
|
25789
26099
|
Channels.FOLDER_RENAME,
|
|
25790
|
-
(
|
|
26100
|
+
(event, id, newName, summary) => {
|
|
26101
|
+
assertTrustedIpcSender(event);
|
|
25791
26102
|
return renameFolder(id, newName, summary);
|
|
25792
26103
|
}
|
|
25793
26104
|
);
|
|
25794
26105
|
}
|
|
25795
26106
|
function registerHistoryHandlers() {
|
|
25796
|
-
electron.ipcMain.handle(Channels.HISTORY_GET, () => {
|
|
26107
|
+
electron.ipcMain.handle(Channels.HISTORY_GET, (event) => {
|
|
26108
|
+
assertTrustedIpcSender(event);
|
|
25797
26109
|
return getState$1();
|
|
25798
26110
|
});
|
|
25799
|
-
electron.ipcMain.handle(Channels.HISTORY_SEARCH, (
|
|
26111
|
+
electron.ipcMain.handle(Channels.HISTORY_SEARCH, (event, query) => {
|
|
26112
|
+
assertTrustedIpcSender(event);
|
|
25800
26113
|
return search(query);
|
|
25801
26114
|
});
|
|
25802
|
-
electron.ipcMain.handle(Channels.HISTORY_CLEAR, () => {
|
|
26115
|
+
electron.ipcMain.handle(Channels.HISTORY_CLEAR, (event) => {
|
|
26116
|
+
assertTrustedIpcSender(event);
|
|
25803
26117
|
clearAll$1();
|
|
25804
26118
|
});
|
|
25805
|
-
electron.ipcMain.handle(Channels.HISTORY_EXPORT_HTML, async () => {
|
|
26119
|
+
electron.ipcMain.handle(Channels.HISTORY_EXPORT_HTML, async (event) => {
|
|
26120
|
+
assertTrustedIpcSender(event);
|
|
25806
26121
|
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
25807
26122
|
title: "Export History",
|
|
25808
26123
|
defaultPath: "vessel-history.html",
|
|
@@ -25813,7 +26128,8 @@ function registerHistoryHandlers() {
|
|
|
25813
26128
|
await fs.promises.writeFile(filePath2, content, "utf-8");
|
|
25814
26129
|
return { filePath: filePath2, count: getState$1().entries.length };
|
|
25815
26130
|
});
|
|
25816
|
-
electron.ipcMain.handle(Channels.HISTORY_EXPORT_JSON, async () => {
|
|
26131
|
+
electron.ipcMain.handle(Channels.HISTORY_EXPORT_JSON, async (event) => {
|
|
26132
|
+
assertTrustedIpcSender(event);
|
|
25817
26133
|
const { canceled, filePath: filePath2 } = await electron.dialog.showSaveDialog({
|
|
25818
26134
|
title: "Export History",
|
|
25819
26135
|
defaultPath: "vessel-history.json",
|
|
@@ -25824,7 +26140,8 @@ function registerHistoryHandlers() {
|
|
|
25824
26140
|
await fs.promises.writeFile(filePath2, content, "utf-8");
|
|
25825
26141
|
return { filePath: filePath2, count: getState$1().entries.length };
|
|
25826
26142
|
});
|
|
25827
|
-
electron.ipcMain.handle(Channels.HISTORY_IMPORT, async () => {
|
|
26143
|
+
electron.ipcMain.handle(Channels.HISTORY_IMPORT, async (event) => {
|
|
26144
|
+
assertTrustedIpcSender(event);
|
|
25828
26145
|
const { canceled, filePaths } = await electron.dialog.showOpenDialog({
|
|
25829
26146
|
title: "Import History",
|
|
25830
26147
|
filters: [
|
|
@@ -25917,10 +26234,12 @@ function registerPremiumHandlers(tabManager, sendToRendererViews) {
|
|
|
25917
26234
|
void handleUrl(currentUrl);
|
|
25918
26235
|
}
|
|
25919
26236
|
};
|
|
25920
|
-
electron.ipcMain.handle(Channels.PREMIUM_GET_STATE, () => {
|
|
26237
|
+
electron.ipcMain.handle(Channels.PREMIUM_GET_STATE, (event) => {
|
|
26238
|
+
assertTrustedIpcSender(event);
|
|
25921
26239
|
return getPremiumState();
|
|
25922
26240
|
});
|
|
25923
|
-
electron.ipcMain.handle(Channels.PREMIUM_ACTIVATION_START, async (
|
|
26241
|
+
electron.ipcMain.handle(Channels.PREMIUM_ACTIVATION_START, async (event, email) => {
|
|
26242
|
+
assertTrustedIpcSender(event);
|
|
25924
26243
|
assertString(email, "email");
|
|
25925
26244
|
if (!isValidEmail(email)) {
|
|
25926
26245
|
return errorResult("Invalid email format");
|
|
@@ -25934,7 +26253,8 @@ function registerPremiumHandlers(tabManager, sendToRendererViews) {
|
|
|
25934
26253
|
});
|
|
25935
26254
|
electron.ipcMain.handle(
|
|
25936
26255
|
Channels.PREMIUM_ACTIVATION_VERIFY,
|
|
25937
|
-
async (
|
|
26256
|
+
async (event, email, code, challengeToken) => {
|
|
26257
|
+
assertTrustedIpcSender(event);
|
|
25938
26258
|
assertString(email, "email");
|
|
25939
26259
|
assertString(code, "code");
|
|
25940
26260
|
assertString(challengeToken, "challengeToken");
|
|
@@ -25956,7 +26276,8 @@ function registerPremiumHandlers(tabManager, sendToRendererViews) {
|
|
|
25956
26276
|
return result;
|
|
25957
26277
|
}
|
|
25958
26278
|
);
|
|
25959
|
-
electron.ipcMain.handle(Channels.PREMIUM_CHECKOUT, async (
|
|
26279
|
+
electron.ipcMain.handle(Channels.PREMIUM_CHECKOUT, async (event, email) => {
|
|
26280
|
+
assertTrustedIpcSender(event);
|
|
25960
26281
|
trackPremiumFunnel("checkout_clicked");
|
|
25961
26282
|
const result = await getCheckoutUrl(email);
|
|
25962
26283
|
if (result.ok && result.url) {
|
|
@@ -25965,19 +26286,22 @@ function registerPremiumHandlers(tabManager, sendToRendererViews) {
|
|
|
25965
26286
|
}
|
|
25966
26287
|
return result;
|
|
25967
26288
|
});
|
|
25968
|
-
electron.ipcMain.handle(Channels.PREMIUM_RESET, () => {
|
|
26289
|
+
electron.ipcMain.handle(Channels.PREMIUM_RESET, (event) => {
|
|
26290
|
+
assertTrustedIpcSender(event);
|
|
25969
26291
|
trackPremiumFunnel("reset");
|
|
25970
26292
|
const state2 = resetPremium();
|
|
25971
26293
|
sendToRendererViews(Channels.PREMIUM_UPDATE, state2);
|
|
25972
26294
|
return state2;
|
|
25973
26295
|
});
|
|
25974
|
-
electron.ipcMain.handle(Channels.PREMIUM_TRACK_CONTEXT, (
|
|
26296
|
+
electron.ipcMain.handle(Channels.PREMIUM_TRACK_CONTEXT, (event, step) => {
|
|
26297
|
+
assertTrustedIpcSender(event);
|
|
25975
26298
|
assertString(step, "step");
|
|
25976
26299
|
if (PREMIUM_TRACKABLE_STEPS.includes(step)) {
|
|
25977
26300
|
trackPremiumFunnel(step);
|
|
25978
26301
|
}
|
|
25979
26302
|
});
|
|
25980
|
-
electron.ipcMain.handle(Channels.PREMIUM_PORTAL, async () => {
|
|
26303
|
+
electron.ipcMain.handle(Channels.PREMIUM_PORTAL, async (event) => {
|
|
26304
|
+
assertTrustedIpcSender(event);
|
|
25981
26305
|
trackPremiumFunnel("portal_opened");
|
|
25982
26306
|
const result = await getPortalUrl();
|
|
25983
26307
|
if (result.ok && result.url) {
|
|
@@ -25987,18 +26311,22 @@ function registerPremiumHandlers(tabManager, sendToRendererViews) {
|
|
|
25987
26311
|
});
|
|
25988
26312
|
}
|
|
25989
26313
|
function registerSessionHandlers(tabManager) {
|
|
25990
|
-
electron.ipcMain.handle(Channels.SESSION_LIST, () => {
|
|
26314
|
+
electron.ipcMain.handle(Channels.SESSION_LIST, (event) => {
|
|
26315
|
+
assertTrustedIpcSender(event);
|
|
25991
26316
|
return listNamedSessions();
|
|
25992
26317
|
});
|
|
25993
|
-
electron.ipcMain.handle(Channels.SESSION_SAVE, async (
|
|
26318
|
+
electron.ipcMain.handle(Channels.SESSION_SAVE, async (event, name) => {
|
|
26319
|
+
assertTrustedIpcSender(event);
|
|
25994
26320
|
assertString(name, "name");
|
|
25995
26321
|
return await saveNamedSession(tabManager, name);
|
|
25996
26322
|
});
|
|
25997
|
-
electron.ipcMain.handle(Channels.SESSION_LOAD, async (
|
|
26323
|
+
electron.ipcMain.handle(Channels.SESSION_LOAD, async (event, name) => {
|
|
26324
|
+
assertTrustedIpcSender(event);
|
|
25998
26325
|
assertString(name, "name");
|
|
25999
26326
|
return await loadNamedSession(tabManager, name);
|
|
26000
26327
|
});
|
|
26001
|
-
electron.ipcMain.handle(Channels.SESSION_DELETE, (
|
|
26328
|
+
electron.ipcMain.handle(Channels.SESSION_DELETE, (event, name) => {
|
|
26329
|
+
assertTrustedIpcSender(event);
|
|
26002
26330
|
assertString(name, "name");
|
|
26003
26331
|
return deleteNamedSession(name);
|
|
26004
26332
|
});
|
|
@@ -26035,7 +26363,8 @@ function buildCertificateDetailsHtml(state2) {
|
|
|
26035
26363
|
</html>`;
|
|
26036
26364
|
}
|
|
26037
26365
|
function registerSecurityHandlers(tabManager) {
|
|
26038
|
-
electron.ipcMain.handle(Channels.SECURITY_SHOW_DETAILS, async (
|
|
26366
|
+
electron.ipcMain.handle(Channels.SECURITY_SHOW_DETAILS, async (event, state2) => {
|
|
26367
|
+
assertTrustedIpcSender(event);
|
|
26039
26368
|
const domain = (() => {
|
|
26040
26369
|
try {
|
|
26041
26370
|
return new URL(state2.url).hostname || state2.url;
|
|
@@ -26056,13 +26385,15 @@ function registerSecurityHandlers(tabManager) {
|
|
|
26056
26385
|
spellcheck: false
|
|
26057
26386
|
}
|
|
26058
26387
|
});
|
|
26059
|
-
void win.
|
|
26388
|
+
void loadInternalDataURL(win.webContents, `data:text/html;charset=utf-8,${encodeURIComponent(content)}`);
|
|
26060
26389
|
});
|
|
26061
|
-
electron.ipcMain.handle(Channels.SECURITY_PROCEED_ANYWAY, (
|
|
26390
|
+
electron.ipcMain.handle(Channels.SECURITY_PROCEED_ANYWAY, (event, tabId) => {
|
|
26391
|
+
assertTrustedIpcSender(event);
|
|
26062
26392
|
assertString(tabId, "tabId");
|
|
26063
26393
|
tabManager.proceedAnyway(tabId);
|
|
26064
26394
|
});
|
|
26065
|
-
electron.ipcMain.handle(Channels.SECURITY_GO_BACK_TO_SAFETY, (
|
|
26395
|
+
electron.ipcMain.handle(Channels.SECURITY_GO_BACK_TO_SAFETY, (event, tabId) => {
|
|
26396
|
+
assertTrustedIpcSender(event);
|
|
26066
26397
|
assertString(tabId, "tabId");
|
|
26067
26398
|
tabManager.goBackToSafety(tabId);
|
|
26068
26399
|
});
|
|
@@ -26070,6 +26401,7 @@ function registerSecurityHandlers(tabManager) {
|
|
|
26070
26401
|
const logger$5 = createLogger("CodexIPC");
|
|
26071
26402
|
function registerCodexHandlers() {
|
|
26072
26403
|
electron.ipcMain.handle(Channels.CODEX_START_AUTH, async (event) => {
|
|
26404
|
+
assertTrustedIpcSender(event);
|
|
26073
26405
|
const wc = event.sender;
|
|
26074
26406
|
if (!wc || wc.isDestroyed()) {
|
|
26075
26407
|
return {
|
|
@@ -26100,29 +26432,61 @@ function registerCodexHandlers() {
|
|
|
26100
26432
|
};
|
|
26101
26433
|
}
|
|
26102
26434
|
});
|
|
26103
|
-
electron.ipcMain.handle(Channels.CODEX_CANCEL_AUTH, () => {
|
|
26435
|
+
electron.ipcMain.handle(Channels.CODEX_CANCEL_AUTH, (event) => {
|
|
26436
|
+
assertTrustedIpcSender(event);
|
|
26104
26437
|
cancelCodexOAuth();
|
|
26105
26438
|
return { ok: true };
|
|
26106
26439
|
});
|
|
26107
|
-
electron.ipcMain.handle(Channels.CODEX_DISCONNECT, () => {
|
|
26440
|
+
electron.ipcMain.handle(Channels.CODEX_DISCONNECT, (event) => {
|
|
26441
|
+
assertTrustedIpcSender(event);
|
|
26108
26442
|
clearStoredCodexTokens();
|
|
26109
26443
|
return { ok: true };
|
|
26110
26444
|
});
|
|
26111
26445
|
}
|
|
26112
26446
|
const filePath = () => path$1.join(electron.app.getPath("userData"), "vessel-permissions.json");
|
|
26447
|
+
const ALLOWED_PERMISSION_TYPES = /* @__PURE__ */ new Set([
|
|
26448
|
+
"clipboard-read",
|
|
26449
|
+
"fullscreen",
|
|
26450
|
+
"geolocation",
|
|
26451
|
+
"media",
|
|
26452
|
+
"midiSysex",
|
|
26453
|
+
"notifications",
|
|
26454
|
+
"pointerLock"
|
|
26455
|
+
]);
|
|
26456
|
+
function parseOrigin(value) {
|
|
26457
|
+
try {
|
|
26458
|
+
const origin = new URL(value).origin;
|
|
26459
|
+
return origin === "null" ? null : origin;
|
|
26460
|
+
} catch {
|
|
26461
|
+
return null;
|
|
26462
|
+
}
|
|
26463
|
+
}
|
|
26464
|
+
function isPermissionRecord(value) {
|
|
26465
|
+
if (!value || typeof value !== "object") return false;
|
|
26466
|
+
const record = value;
|
|
26467
|
+
return typeof record.origin === "string" && parseOrigin(record.origin) === record.origin && typeof record.permission === "string" && ALLOWED_PERMISSION_TYPES.has(record.permission) && (record.decision === "allow" || record.decision === "deny") && typeof record.updatedAt === "string";
|
|
26468
|
+
}
|
|
26113
26469
|
let records = loadJsonFile({
|
|
26114
26470
|
filePath: filePath(),
|
|
26115
26471
|
fallback: [],
|
|
26116
|
-
parse: (raw) => Array.isArray(raw) ? raw : []
|
|
26472
|
+
parse: (raw) => Array.isArray(raw) ? raw.filter(isPermissionRecord) : []
|
|
26117
26473
|
});
|
|
26118
26474
|
const persistence = createDebouncedJsonPersistence({ debounceMs: 250, filePath: filePath(), getValue: () => records, logLabel: "permissions" });
|
|
26475
|
+
const sessionDecisions = /* @__PURE__ */ new Map();
|
|
26119
26476
|
let broadcaster = null;
|
|
26120
26477
|
function key(origin, permission) {
|
|
26121
26478
|
return `${origin}
|
|
26122
26479
|
${permission}`;
|
|
26123
26480
|
}
|
|
26481
|
+
function snapshot() {
|
|
26482
|
+
return records.map((record) => ({ ...record }));
|
|
26483
|
+
}
|
|
26124
26484
|
function emit() {
|
|
26125
|
-
broadcaster?.(Channels.PERMISSIONS_GET,
|
|
26485
|
+
broadcaster?.(Channels.PERMISSIONS_GET, snapshot());
|
|
26486
|
+
}
|
|
26487
|
+
function getDecision(origin, permission) {
|
|
26488
|
+
const existing = records.find((r) => r.origin === origin && r.permission === permission);
|
|
26489
|
+
return existing?.decision ?? sessionDecisions.get(key(origin, permission)) ?? null;
|
|
26126
26490
|
}
|
|
26127
26491
|
function save(origin, permission, decision) {
|
|
26128
26492
|
const k = key(origin, permission);
|
|
@@ -26134,15 +26498,21 @@ function save(origin, permission, decision) {
|
|
|
26134
26498
|
emit();
|
|
26135
26499
|
}
|
|
26136
26500
|
function listPermissions() {
|
|
26137
|
-
return
|
|
26501
|
+
return snapshot();
|
|
26138
26502
|
}
|
|
26139
26503
|
function clearPermissions() {
|
|
26140
26504
|
records = [];
|
|
26505
|
+
sessionDecisions.clear();
|
|
26141
26506
|
persistence.schedule();
|
|
26142
26507
|
emit();
|
|
26143
26508
|
}
|
|
26144
26509
|
function clearPermissionsForOrigin(origin) {
|
|
26510
|
+
if (!parseOrigin(origin)) return;
|
|
26145
26511
|
records = records.filter((record) => record.origin !== origin);
|
|
26512
|
+
for (const storedKey of sessionDecisions.keys()) {
|
|
26513
|
+
if (storedKey.startsWith(`${origin}
|
|
26514
|
+
`)) sessionDecisions.delete(storedKey);
|
|
26515
|
+
}
|
|
26146
26516
|
persistence.schedule();
|
|
26147
26517
|
emit();
|
|
26148
26518
|
}
|
|
@@ -26150,28 +26520,72 @@ function setPermissionBroadcaster(fn) {
|
|
|
26150
26520
|
broadcaster = fn;
|
|
26151
26521
|
}
|
|
26152
26522
|
function installPermissionHandler() {
|
|
26523
|
+
electron.session.defaultSession.setPermissionCheckHandler((webContents, permission, requestingOrigin) => {
|
|
26524
|
+
if (!ALLOWED_PERMISSION_TYPES.has(permission)) return false;
|
|
26525
|
+
const origin = parseOrigin(requestingOrigin || webContents.getURL());
|
|
26526
|
+
if (!origin) return false;
|
|
26527
|
+
return getDecision(origin, permission) === "allow";
|
|
26528
|
+
});
|
|
26153
26529
|
electron.session.defaultSession.setPermissionRequestHandler((webContents, permission, callback, details) => {
|
|
26154
|
-
|
|
26155
|
-
|
|
26156
|
-
|
|
26157
|
-
|
|
26530
|
+
if (!ALLOWED_PERMISSION_TYPES.has(permission)) {
|
|
26531
|
+
callback(false);
|
|
26532
|
+
return;
|
|
26533
|
+
}
|
|
26534
|
+
const origin = parseOrigin(details.requestingUrl || webContents.getURL());
|
|
26535
|
+
if (!origin) {
|
|
26536
|
+
callback(false);
|
|
26537
|
+
return;
|
|
26538
|
+
}
|
|
26539
|
+
const k = key(origin, permission);
|
|
26540
|
+
const decision = getDecision(origin, permission);
|
|
26541
|
+
if (decision) {
|
|
26542
|
+
callback(decision === "allow");
|
|
26158
26543
|
return;
|
|
26159
26544
|
}
|
|
26160
26545
|
const result = electron.dialog.showMessageBoxSync({
|
|
26161
26546
|
type: "question",
|
|
26162
|
-
buttons: [
|
|
26547
|
+
buttons: [
|
|
26548
|
+
"Deny Once",
|
|
26549
|
+
"Deny Until Quit",
|
|
26550
|
+
"Always Deny",
|
|
26551
|
+
"Allow Once",
|
|
26552
|
+
"Allow Until Quit",
|
|
26553
|
+
"Always Allow"
|
|
26554
|
+
],
|
|
26163
26555
|
defaultId: 0,
|
|
26164
26556
|
cancelId: 0,
|
|
26165
26557
|
title: "Site permission request",
|
|
26166
26558
|
message: `${origin} wants to use ${permission}`,
|
|
26167
|
-
detail: "
|
|
26559
|
+
detail: "Temporary choices are safer for camera, microphone, location, and clipboard access. Persistent choices can be cleared in Settings > Privacy."
|
|
26168
26560
|
});
|
|
26169
|
-
|
|
26170
|
-
|
|
26171
|
-
|
|
26561
|
+
if (result === 1) {
|
|
26562
|
+
sessionDecisions.set(k, "deny");
|
|
26563
|
+
callback(false);
|
|
26564
|
+
return;
|
|
26565
|
+
}
|
|
26566
|
+
if (result === 2) {
|
|
26567
|
+
save(origin, permission, "deny");
|
|
26568
|
+
callback(false);
|
|
26569
|
+
return;
|
|
26570
|
+
}
|
|
26571
|
+
if (result === 3) {
|
|
26572
|
+
callback(true);
|
|
26573
|
+
return;
|
|
26574
|
+
}
|
|
26575
|
+
if (result === 4) {
|
|
26576
|
+
sessionDecisions.set(k, "allow");
|
|
26577
|
+
callback(true);
|
|
26578
|
+
return;
|
|
26579
|
+
}
|
|
26580
|
+
if (result === 5) {
|
|
26581
|
+
save(origin, permission, "allow");
|
|
26582
|
+
callback(true);
|
|
26583
|
+
return;
|
|
26584
|
+
}
|
|
26585
|
+
callback(false);
|
|
26172
26586
|
});
|
|
26173
26587
|
}
|
|
26174
|
-
const
|
|
26588
|
+
const GITHUB_LATEST_RELEASE_API_URL = "https://api.github.com/repos/unmodeled-tyler/quanta-vessel-browser/releases/latest";
|
|
26175
26589
|
const RELEASES_URL = "https://github.com/unmodeled-tyler/quanta-vessel-browser/releases/latest";
|
|
26176
26590
|
function normalizeVersion(version) {
|
|
26177
26591
|
return version.replace(/^v/i, "").split(/[.-]/).slice(0, 3).map((part) => {
|
|
@@ -26192,21 +26606,22 @@ async function checkForUpdates() {
|
|
|
26192
26606
|
const currentVersion = electron.app.getVersion();
|
|
26193
26607
|
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26194
26608
|
try {
|
|
26195
|
-
const response = await fetch(
|
|
26196
|
-
headers: { accept: "application/json", "user-agent": `Vessel/${currentVersion}` }
|
|
26609
|
+
const response = await fetch(GITHUB_LATEST_RELEASE_API_URL, {
|
|
26610
|
+
headers: { accept: "application/vnd.github+json", "user-agent": `Vessel/${currentVersion}` }
|
|
26197
26611
|
});
|
|
26198
26612
|
if (!response.ok) {
|
|
26199
|
-
throw new Error(`
|
|
26613
|
+
throw new Error(`GitHub Releases responded with ${response.status}`);
|
|
26200
26614
|
}
|
|
26201
26615
|
const body = await response.json();
|
|
26202
|
-
const latestVersion = typeof body.
|
|
26203
|
-
if (!latestVersion) throw new Error("
|
|
26616
|
+
const latestVersion = typeof body.tag_name === "string" ? body.tag_name : null;
|
|
26617
|
+
if (!latestVersion) throw new Error("GitHub release response did not include a tag name");
|
|
26618
|
+
const releaseUrl = typeof body.html_url === "string" && body.html_url.startsWith("https://github.com/") ? body.html_url : RELEASES_URL;
|
|
26204
26619
|
return {
|
|
26205
26620
|
currentVersion,
|
|
26206
26621
|
latestVersion,
|
|
26207
26622
|
updateAvailable: compareVersions(latestVersion, currentVersion) > 0,
|
|
26208
26623
|
checkedAt,
|
|
26209
|
-
releaseUrl
|
|
26624
|
+
releaseUrl
|
|
26210
26625
|
};
|
|
26211
26626
|
} catch (error) {
|
|
26212
26627
|
return {
|
|
@@ -26220,7 +26635,7 @@ async function checkForUpdates() {
|
|
|
26220
26635
|
}
|
|
26221
26636
|
}
|
|
26222
26637
|
async function openUpdateDownload() {
|
|
26223
|
-
await
|
|
26638
|
+
await openExternalAllowlisted(RELEASES_URL, { hosts: ["github.com"] });
|
|
26224
26639
|
}
|
|
26225
26640
|
let activeChatProvider = null;
|
|
26226
26641
|
const logger$4 = createLogger("IPC");
|
|
@@ -26255,13 +26670,24 @@ async function togglePictureInPicture(tabManager) {
|
|
|
26255
26670
|
}
|
|
26256
26671
|
function registerIpcHandlers(windowState, runtime2) {
|
|
26257
26672
|
const { tabManager, chromeView, sidebarView, devtoolsPanelView, mainWindow } = windowState;
|
|
26258
|
-
|
|
26673
|
+
registerTrustedIpcSender(chromeView.webContents);
|
|
26674
|
+
registerTrustedIpcSender(sidebarView.webContents);
|
|
26675
|
+
registerTrustedIpcSender(devtoolsPanelView.webContents);
|
|
26676
|
+
const requireTrusted = (event) => {
|
|
26677
|
+
assertTrustedIpcSender(event);
|
|
26678
|
+
};
|
|
26679
|
+
electron.ipcMain.handle(Channels.OPEN_PRIVATE_WINDOW, (event) => {
|
|
26680
|
+
requireTrusted(event);
|
|
26259
26681
|
createPrivateWindow();
|
|
26260
26682
|
});
|
|
26261
|
-
electron.ipcMain.handle(Channels.OPEN_NEW_WINDOW, () => {
|
|
26683
|
+
electron.ipcMain.handle(Channels.OPEN_NEW_WINDOW, (event) => {
|
|
26684
|
+
requireTrusted(event);
|
|
26262
26685
|
createSecondaryWindow();
|
|
26263
26686
|
});
|
|
26264
|
-
electron.ipcMain.handle(Channels.IS_PRIVATE_MODE, () =>
|
|
26687
|
+
electron.ipcMain.handle(Channels.IS_PRIVATE_MODE, (event) => {
|
|
26688
|
+
requireTrusted(event);
|
|
26689
|
+
return false;
|
|
26690
|
+
});
|
|
26265
26691
|
let sidebarResizeRecoveryTimer = null;
|
|
26266
26692
|
let sidebarResizeActive = false;
|
|
26267
26693
|
let runtimeUpdateTimer = null;
|
|
@@ -26342,37 +26768,45 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26342
26768
|
onAIStreamIdle(() => {
|
|
26343
26769
|
sendToRendererViews(Channels.AI_STREAM_IDLE);
|
|
26344
26770
|
});
|
|
26345
|
-
electron.ipcMain.handle(Channels.TAB_CREATE, (
|
|
26771
|
+
electron.ipcMain.handle(Channels.TAB_CREATE, (event, url) => {
|
|
26772
|
+
requireTrusted(event);
|
|
26346
26773
|
const id = tabManager.createTab(url || loadSettings().defaultUrl);
|
|
26347
26774
|
layoutViews(windowState);
|
|
26348
26775
|
return id;
|
|
26349
26776
|
});
|
|
26350
|
-
electron.ipcMain.handle(Channels.TAB_CLOSE, (
|
|
26777
|
+
electron.ipcMain.handle(Channels.TAB_CLOSE, (event, id) => {
|
|
26778
|
+
requireTrusted(event);
|
|
26351
26779
|
tabManager.closeTab(id);
|
|
26352
26780
|
layoutViews(windowState);
|
|
26353
26781
|
});
|
|
26354
|
-
electron.ipcMain.handle(Channels.TAB_SWITCH, (
|
|
26782
|
+
electron.ipcMain.handle(Channels.TAB_SWITCH, (event, id) => {
|
|
26783
|
+
requireTrusted(event);
|
|
26355
26784
|
tabManager.switchTab(id);
|
|
26356
26785
|
layoutViews(windowState);
|
|
26357
26786
|
});
|
|
26358
26787
|
electron.ipcMain.handle(
|
|
26359
26788
|
Channels.TAB_NAVIGATE,
|
|
26360
|
-
(
|
|
26789
|
+
(event, id, url, postBody) => {
|
|
26790
|
+
requireTrusted(event);
|
|
26361
26791
|
assertString(id, "tabId");
|
|
26362
26792
|
assertString(url, "url");
|
|
26363
26793
|
return tabManager.navigateTab(id, url, postBody);
|
|
26364
26794
|
}
|
|
26365
26795
|
);
|
|
26366
|
-
electron.ipcMain.handle(Channels.TAB_BACK, (
|
|
26796
|
+
electron.ipcMain.handle(Channels.TAB_BACK, (event, id) => {
|
|
26797
|
+
requireTrusted(event);
|
|
26367
26798
|
tabManager.goBack(id);
|
|
26368
26799
|
});
|
|
26369
|
-
electron.ipcMain.handle(Channels.TAB_FORWARD, (
|
|
26800
|
+
electron.ipcMain.handle(Channels.TAB_FORWARD, (event, id) => {
|
|
26801
|
+
requireTrusted(event);
|
|
26370
26802
|
tabManager.goForward(id);
|
|
26371
26803
|
});
|
|
26372
|
-
electron.ipcMain.handle(Channels.TAB_RELOAD, (
|
|
26804
|
+
electron.ipcMain.handle(Channels.TAB_RELOAD, (event, id) => {
|
|
26805
|
+
requireTrusted(event);
|
|
26373
26806
|
tabManager.reloadTab(id);
|
|
26374
26807
|
});
|
|
26375
|
-
electron.ipcMain.handle(Channels.TAB_TOGGLE_AD_BLOCK, (
|
|
26808
|
+
electron.ipcMain.handle(Channels.TAB_TOGGLE_AD_BLOCK, (event, id) => {
|
|
26809
|
+
requireTrusted(event);
|
|
26376
26810
|
assertString(id, "id");
|
|
26377
26811
|
const tab = tabManager.getTab(id);
|
|
26378
26812
|
if (!tab) return null;
|
|
@@ -26380,87 +26814,108 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26380
26814
|
tab.setAdBlockingEnabled(newState);
|
|
26381
26815
|
return newState;
|
|
26382
26816
|
});
|
|
26383
|
-
electron.ipcMain.handle(Channels.TAB_ZOOM_IN, (
|
|
26817
|
+
electron.ipcMain.handle(Channels.TAB_ZOOM_IN, (event, id) => {
|
|
26818
|
+
requireTrusted(event);
|
|
26384
26819
|
assertString(id, "id");
|
|
26385
26820
|
tabManager.zoomIn(id);
|
|
26386
26821
|
});
|
|
26387
|
-
electron.ipcMain.handle(Channels.TAB_ZOOM_OUT, (
|
|
26822
|
+
electron.ipcMain.handle(Channels.TAB_ZOOM_OUT, (event, id) => {
|
|
26823
|
+
requireTrusted(event);
|
|
26388
26824
|
assertString(id, "id");
|
|
26389
26825
|
tabManager.zoomOut(id);
|
|
26390
26826
|
});
|
|
26391
|
-
electron.ipcMain.handle(Channels.TAB_ZOOM_RESET, (
|
|
26827
|
+
electron.ipcMain.handle(Channels.TAB_ZOOM_RESET, (event, id) => {
|
|
26828
|
+
requireTrusted(event);
|
|
26392
26829
|
assertString(id, "id");
|
|
26393
26830
|
tabManager.zoomReset(id);
|
|
26394
26831
|
});
|
|
26395
|
-
electron.ipcMain.handle(Channels.TAB_REOPEN_CLOSED, () => {
|
|
26832
|
+
electron.ipcMain.handle(Channels.TAB_REOPEN_CLOSED, (event) => {
|
|
26833
|
+
requireTrusted(event);
|
|
26396
26834
|
const id = tabManager.reopenClosedTab();
|
|
26397
26835
|
if (id) layoutViews(windowState);
|
|
26398
26836
|
return id;
|
|
26399
26837
|
});
|
|
26400
|
-
electron.ipcMain.handle(Channels.TAB_DUPLICATE, (
|
|
26838
|
+
electron.ipcMain.handle(Channels.TAB_DUPLICATE, (event, id) => {
|
|
26839
|
+
requireTrusted(event);
|
|
26401
26840
|
assertString(id, "id");
|
|
26402
26841
|
const newId = tabManager.duplicateTab(id);
|
|
26403
26842
|
if (newId) layoutViews(windowState);
|
|
26404
26843
|
return newId;
|
|
26405
26844
|
});
|
|
26406
|
-
electron.ipcMain.handle(Channels.TAB_PIN, (
|
|
26845
|
+
electron.ipcMain.handle(Channels.TAB_PIN, (event, id) => {
|
|
26846
|
+
requireTrusted(event);
|
|
26407
26847
|
assertString(id, "id");
|
|
26408
26848
|
tabManager.pinTab(id);
|
|
26409
26849
|
});
|
|
26410
|
-
electron.ipcMain.handle(Channels.TAB_UNPIN, (
|
|
26850
|
+
electron.ipcMain.handle(Channels.TAB_UNPIN, (event, id) => {
|
|
26851
|
+
requireTrusted(event);
|
|
26411
26852
|
assertString(id, "id");
|
|
26412
26853
|
tabManager.unpinTab(id);
|
|
26413
26854
|
});
|
|
26414
|
-
electron.ipcMain.handle(Channels.TAB_GROUP_CREATE, (
|
|
26855
|
+
electron.ipcMain.handle(Channels.TAB_GROUP_CREATE, (event, id) => {
|
|
26856
|
+
requireTrusted(event);
|
|
26415
26857
|
assertString(id, "id");
|
|
26416
26858
|
return tabManager.createGroupFromTab(id);
|
|
26417
26859
|
});
|
|
26418
|
-
electron.ipcMain.handle(Channels.TAB_GROUP_ADD_TAB, (
|
|
26860
|
+
electron.ipcMain.handle(Channels.TAB_GROUP_ADD_TAB, (event, id, groupId) => {
|
|
26861
|
+
requireTrusted(event);
|
|
26419
26862
|
assertString(id, "id");
|
|
26420
26863
|
assertString(groupId, "groupId");
|
|
26421
26864
|
tabManager.assignTabToGroup(id, groupId);
|
|
26422
26865
|
});
|
|
26423
|
-
electron.ipcMain.handle(Channels.TAB_GROUP_REMOVE_TAB, (
|
|
26866
|
+
electron.ipcMain.handle(Channels.TAB_GROUP_REMOVE_TAB, (event, id) => {
|
|
26867
|
+
requireTrusted(event);
|
|
26424
26868
|
assertString(id, "id");
|
|
26425
26869
|
tabManager.removeTabFromGroup(id);
|
|
26426
26870
|
});
|
|
26427
|
-
electron.ipcMain.handle(Channels.TAB_GROUP_TOGGLE_COLLAPSED, (
|
|
26871
|
+
electron.ipcMain.handle(Channels.TAB_GROUP_TOGGLE_COLLAPSED, (event, groupId) => {
|
|
26872
|
+
requireTrusted(event);
|
|
26428
26873
|
assertString(groupId, "groupId");
|
|
26429
26874
|
return tabManager.toggleGroupCollapsed(groupId);
|
|
26430
26875
|
});
|
|
26431
26876
|
electron.ipcMain.handle(
|
|
26432
26877
|
Channels.TAB_GROUP_SET_COLOR,
|
|
26433
|
-
(
|
|
26878
|
+
(event, groupId, color) => {
|
|
26879
|
+
requireTrusted(event);
|
|
26434
26880
|
assertString(groupId, "groupId");
|
|
26435
26881
|
assertString(color, "color");
|
|
26436
26882
|
tabManager.setGroupColor(groupId, color);
|
|
26437
26883
|
}
|
|
26438
26884
|
);
|
|
26439
|
-
electron.ipcMain.handle(Channels.TAB_TOGGLE_MUTE, (
|
|
26885
|
+
electron.ipcMain.handle(Channels.TAB_TOGGLE_MUTE, (event, id) => {
|
|
26886
|
+
requireTrusted(event);
|
|
26440
26887
|
assertString(id, "id");
|
|
26441
26888
|
return tabManager.toggleMuted(id);
|
|
26442
26889
|
});
|
|
26443
|
-
electron.ipcMain.handle(Channels.TAB_PRINT, (
|
|
26890
|
+
electron.ipcMain.handle(Channels.TAB_PRINT, (event, id) => {
|
|
26891
|
+
requireTrusted(event);
|
|
26444
26892
|
assertString(id, "id");
|
|
26445
26893
|
tabManager.printTab(id);
|
|
26446
26894
|
});
|
|
26447
|
-
electron.ipcMain.handle(Channels.TAB_PRINT_TO_PDF, (
|
|
26895
|
+
electron.ipcMain.handle(Channels.TAB_PRINT_TO_PDF, (event, id) => {
|
|
26896
|
+
requireTrusted(event);
|
|
26448
26897
|
assertString(id, "id");
|
|
26449
26898
|
return tabManager.saveTabAsPdf(id);
|
|
26450
26899
|
});
|
|
26451
|
-
electron.ipcMain.on(Channels.TAB_CONTEXT_MENU, (
|
|
26900
|
+
electron.ipcMain.on(Channels.TAB_CONTEXT_MENU, (event, id) => {
|
|
26901
|
+
requireTrusted(event);
|
|
26452
26902
|
assertString(id, "id");
|
|
26453
26903
|
showTabContextMenu(tabManager, id, mainWindow, () => layoutViews(windowState));
|
|
26454
26904
|
});
|
|
26455
|
-
electron.ipcMain.on(Channels.TAB_GROUP_CONTEXT_MENU, (
|
|
26905
|
+
electron.ipcMain.on(Channels.TAB_GROUP_CONTEXT_MENU, (event, groupId) => {
|
|
26906
|
+
requireTrusted(event);
|
|
26456
26907
|
assertString(groupId, "groupId");
|
|
26457
26908
|
showGroupContextMenu(tabManager, groupId, mainWindow);
|
|
26458
26909
|
});
|
|
26459
|
-
electron.ipcMain.handle(Channels.TAB_STATE_GET, () =>
|
|
26460
|
-
|
|
26461
|
-
|
|
26462
|
-
|
|
26463
|
-
|
|
26910
|
+
electron.ipcMain.handle(Channels.TAB_STATE_GET, (event) => {
|
|
26911
|
+
requireTrusted(event);
|
|
26912
|
+
return {
|
|
26913
|
+
tabs: tabManager.getAllStates(),
|
|
26914
|
+
activeId: tabManager.getActiveTabId() || ""
|
|
26915
|
+
};
|
|
26916
|
+
});
|
|
26917
|
+
electron.ipcMain.handle(Channels.AI_QUERY, async (event, query, history) => {
|
|
26918
|
+
requireTrusted(event);
|
|
26464
26919
|
const settings2 = loadSettings();
|
|
26465
26920
|
const chatConfig = settings2.chatProvider;
|
|
26466
26921
|
if (!chatConfig) {
|
|
@@ -26503,10 +26958,12 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26503
26958
|
})();
|
|
26504
26959
|
return { accepted: true };
|
|
26505
26960
|
});
|
|
26506
|
-
electron.ipcMain.handle(Channels.AI_CANCEL, () => {
|
|
26961
|
+
electron.ipcMain.handle(Channels.AI_CANCEL, (event) => {
|
|
26962
|
+
requireTrusted(event);
|
|
26507
26963
|
activeChatProvider?.cancel();
|
|
26508
26964
|
});
|
|
26509
|
-
electron.ipcMain.handle(Channels.AI_FETCH_MODELS, async (
|
|
26965
|
+
electron.ipcMain.handle(Channels.AI_FETCH_MODELS, async (event, config) => {
|
|
26966
|
+
requireTrusted(event);
|
|
26510
26967
|
try {
|
|
26511
26968
|
if (!config || typeof config !== "object" || !("id" in config)) {
|
|
26512
26969
|
return errorResult("Invalid provider configuration", { models: [] });
|
|
@@ -26518,31 +26975,35 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26518
26975
|
return errorResult(getErrorMessage(err), { models: [] });
|
|
26519
26976
|
}
|
|
26520
26977
|
});
|
|
26521
|
-
electron.ipcMain.handle(Channels.CONTENT_EXTRACT, async () => {
|
|
26978
|
+
electron.ipcMain.handle(Channels.CONTENT_EXTRACT, async (event) => {
|
|
26979
|
+
requireTrusted(event);
|
|
26522
26980
|
const activeTab = tabManager.getActiveTab();
|
|
26523
26981
|
if (!activeTab) return null;
|
|
26524
26982
|
return extractContent(activeTab.view.webContents);
|
|
26525
26983
|
});
|
|
26526
|
-
electron.ipcMain.handle(Channels.READER_MODE_TOGGLE, async () => {
|
|
26984
|
+
electron.ipcMain.handle(Channels.READER_MODE_TOGGLE, async (event) => {
|
|
26985
|
+
requireTrusted(event);
|
|
26527
26986
|
const activeTab = tabManager.getActiveTab();
|
|
26528
26987
|
if (!activeTab) return;
|
|
26529
26988
|
if (activeTab.state.isReaderMode) {
|
|
26530
26989
|
const originalUrl = activeTab.readerOriginalUrl;
|
|
26531
26990
|
activeTab.setReaderMode(false);
|
|
26532
26991
|
if (originalUrl) {
|
|
26533
|
-
activeTab.view.webContents
|
|
26992
|
+
void loadPermittedNavigationURL(activeTab.view.webContents, originalUrl);
|
|
26534
26993
|
}
|
|
26535
26994
|
} else {
|
|
26536
26995
|
const originalUrl = activeTab.state.url;
|
|
26537
26996
|
const content = await extractContent(activeTab.view.webContents);
|
|
26538
26997
|
const html = generateReaderHTML(content);
|
|
26539
26998
|
activeTab.setReaderMode(true, originalUrl);
|
|
26540
|
-
|
|
26999
|
+
void loadInternalDataURL(
|
|
27000
|
+
activeTab.view.webContents,
|
|
26541
27001
|
`data:text/html;charset=utf-8,${encodeURIComponent(html)}`
|
|
26542
27002
|
);
|
|
26543
27003
|
}
|
|
26544
27004
|
});
|
|
26545
|
-
electron.ipcMain.handle(Channels.SIDEBAR_TOGGLE, () => {
|
|
27005
|
+
electron.ipcMain.handle(Channels.SIDEBAR_TOGGLE, (event) => {
|
|
27006
|
+
requireTrusted(event);
|
|
26546
27007
|
windowState.uiState.sidebarOpen = !windowState.uiState.sidebarOpen;
|
|
26547
27008
|
layoutViews(windowState);
|
|
26548
27009
|
return {
|
|
@@ -26550,7 +27011,8 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26550
27011
|
width: windowState.uiState.sidebarWidth
|
|
26551
27012
|
};
|
|
26552
27013
|
});
|
|
26553
|
-
electron.ipcMain.handle(Channels.SIDEBAR_NAVIGATE, (
|
|
27014
|
+
electron.ipcMain.handle(Channels.SIDEBAR_NAVIGATE, (event, tab) => {
|
|
27015
|
+
requireTrusted(event);
|
|
26554
27016
|
assertString(tab, "tab");
|
|
26555
27017
|
if (!windowState.uiState.sidebarOpen) {
|
|
26556
27018
|
windowState.uiState.sidebarOpen = true;
|
|
@@ -26564,7 +27026,8 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26564
27026
|
width: windowState.uiState.sidebarWidth
|
|
26565
27027
|
};
|
|
26566
27028
|
});
|
|
26567
|
-
electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_START, () => {
|
|
27029
|
+
electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_START, (event) => {
|
|
27030
|
+
requireTrusted(event);
|
|
26568
27031
|
sidebarResizeActive = true;
|
|
26569
27032
|
clearSidebarResizeRecoveryTimer();
|
|
26570
27033
|
const [width, height] = windowState.mainWindow.getContentSize();
|
|
@@ -26579,14 +27042,16 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26579
27042
|
});
|
|
26580
27043
|
scheduleSidebarResizeRecovery();
|
|
26581
27044
|
});
|
|
26582
|
-
electron.ipcMain.handle(Channels.SIDEBAR_RESIZE, (
|
|
27045
|
+
electron.ipcMain.handle(Channels.SIDEBAR_RESIZE, (event, width) => {
|
|
27046
|
+
requireTrusted(event);
|
|
26583
27047
|
assertNumber(width, "width");
|
|
26584
27048
|
const clamped = Math.max(240, Math.min(800, Math.round(width)));
|
|
26585
27049
|
windowState.uiState.sidebarWidth = clamped;
|
|
26586
27050
|
resizeSidebarViews(windowState);
|
|
26587
27051
|
return clamped;
|
|
26588
27052
|
});
|
|
26589
|
-
electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_COMMIT, () => {
|
|
27053
|
+
electron.ipcMain.handle(Channels.SIDEBAR_RESIZE_COMMIT, (event) => {
|
|
27054
|
+
requireTrusted(event);
|
|
26590
27055
|
sidebarResizeActive = false;
|
|
26591
27056
|
clearSidebarResizeRecoveryTimer();
|
|
26592
27057
|
setSetting("sidebarWidth", windowState.uiState.sidebarWidth);
|
|
@@ -26594,7 +27059,8 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26594
27059
|
});
|
|
26595
27060
|
electron.ipcMain.on(
|
|
26596
27061
|
Channels.RENDERER_VIEW_READY,
|
|
26597
|
-
(
|
|
27062
|
+
(event, view) => {
|
|
27063
|
+
requireTrusted(event);
|
|
26598
27064
|
if (view !== "sidebar") return;
|
|
26599
27065
|
if (!windowState.uiState.sidebarOpen) {
|
|
26600
27066
|
windowState.uiState.sidebarOpen = true;
|
|
@@ -26602,12 +27068,14 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26602
27068
|
}
|
|
26603
27069
|
}
|
|
26604
27070
|
);
|
|
26605
|
-
electron.ipcMain.handle(Channels.FOCUS_MODE_TOGGLE, () => {
|
|
27071
|
+
electron.ipcMain.handle(Channels.FOCUS_MODE_TOGGLE, (event) => {
|
|
27072
|
+
requireTrusted(event);
|
|
26606
27073
|
windowState.uiState.focusMode = !windowState.uiState.focusMode;
|
|
26607
27074
|
layoutViews(windowState);
|
|
26608
27075
|
return windowState.uiState.focusMode;
|
|
26609
27076
|
});
|
|
26610
|
-
electron.ipcMain.handle(Channels.SETTINGS_VISIBILITY, (
|
|
27077
|
+
electron.ipcMain.handle(Channels.SETTINGS_VISIBILITY, (event, open) => {
|
|
27078
|
+
requireTrusted(event);
|
|
26611
27079
|
windowState.uiState.settingsOpen = open;
|
|
26612
27080
|
if (open) {
|
|
26613
27081
|
windowState.uiState.sidebarOpen = false;
|
|
@@ -26615,11 +27083,20 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26615
27083
|
layoutViews(windowState);
|
|
26616
27084
|
return windowState.uiState.settingsOpen;
|
|
26617
27085
|
});
|
|
26618
|
-
electron.ipcMain.handle(Channels.SETTINGS_GET, () => {
|
|
27086
|
+
electron.ipcMain.handle(Channels.SETTINGS_GET, (event) => {
|
|
27087
|
+
requireTrusted(event);
|
|
26619
27088
|
return getRendererSettings();
|
|
26620
27089
|
});
|
|
26621
|
-
electron.ipcMain.handle(Channels.SETTINGS_HEALTH_GET, () =>
|
|
26622
|
-
|
|
27090
|
+
electron.ipcMain.handle(Channels.SETTINGS_HEALTH_GET, (event) => {
|
|
27091
|
+
requireTrusted(event);
|
|
27092
|
+
return getRuntimeHealth();
|
|
27093
|
+
});
|
|
27094
|
+
electron.ipcMain.handle(Channels.MCP_REGENERATE_TOKEN, (event) => {
|
|
27095
|
+
requireTrusted(event);
|
|
27096
|
+
return regenerateMcpAuthToken();
|
|
27097
|
+
});
|
|
27098
|
+
electron.ipcMain.handle(Channels.SETTINGS_SET, async (event, key2, value) => {
|
|
27099
|
+
requireTrusted(event);
|
|
26623
27100
|
assertString(key2, "key");
|
|
26624
27101
|
if (!SETTABLE_KEYS.has(key2)) {
|
|
26625
27102
|
throw new Error(`Unknown setting key: ${key2}`);
|
|
@@ -26638,12 +27115,22 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26638
27115
|
sendToRendererViews(Channels.SETTINGS_UPDATE, rendererSettings);
|
|
26639
27116
|
return rendererSettings;
|
|
26640
27117
|
});
|
|
26641
|
-
electron.ipcMain.handle(Channels.AGENT_RUNTIME_GET, () =>
|
|
26642
|
-
|
|
26643
|
-
|
|
27118
|
+
electron.ipcMain.handle(Channels.AGENT_RUNTIME_GET, (event) => {
|
|
27119
|
+
requireTrusted(event);
|
|
27120
|
+
return runtime2.getState();
|
|
27121
|
+
});
|
|
27122
|
+
electron.ipcMain.handle(Channels.AGENT_PAUSE, (event) => {
|
|
27123
|
+
requireTrusted(event);
|
|
27124
|
+
return runtime2.pause();
|
|
27125
|
+
});
|
|
27126
|
+
electron.ipcMain.handle(Channels.AGENT_RESUME, (event) => {
|
|
27127
|
+
requireTrusted(event);
|
|
27128
|
+
return runtime2.resume();
|
|
27129
|
+
});
|
|
26644
27130
|
electron.ipcMain.handle(
|
|
26645
27131
|
Channels.AGENT_SET_APPROVAL_MODE,
|
|
26646
|
-
(
|
|
27132
|
+
(event, mode) => {
|
|
27133
|
+
requireTrusted(event);
|
|
26647
27134
|
assertString(mode, "mode");
|
|
26648
27135
|
if (!VALID_APPROVAL_MODES.includes(mode)) {
|
|
26649
27136
|
throw new Error(`Invalid approval mode: ${mode}`);
|
|
@@ -26655,34 +27142,44 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26655
27142
|
);
|
|
26656
27143
|
electron.ipcMain.handle(
|
|
26657
27144
|
Channels.AGENT_APPROVAL_RESOLVE,
|
|
26658
|
-
(
|
|
27145
|
+
(event, approvalId, approved) => {
|
|
27146
|
+
requireTrusted(event);
|
|
27147
|
+
return runtime2.resolveApproval(approvalId, approved);
|
|
27148
|
+
}
|
|
26659
27149
|
);
|
|
26660
27150
|
electron.ipcMain.handle(
|
|
26661
27151
|
Channels.AGENT_CHECKPOINT_CREATE,
|
|
26662
|
-
(
|
|
26663
|
-
|
|
26664
|
-
|
|
26665
|
-
|
|
26666
|
-
(_, checkpointId) => runtime2.restoreCheckpoint(checkpointId)
|
|
26667
|
-
);
|
|
26668
|
-
electron.ipcMain.handle(
|
|
26669
|
-
Channels.AGENT_CHECKPOINT_UPDATE_NOTE,
|
|
26670
|
-
(_, checkpointId, note) => runtime2.updateCheckpointNote(checkpointId, note || "")
|
|
26671
|
-
);
|
|
26672
|
-
electron.ipcMain.handle(
|
|
26673
|
-
Channels.AGENT_UNDO_LAST_ACTION,
|
|
26674
|
-
() => runtime2.undoLastAction()
|
|
26675
|
-
);
|
|
26676
|
-
electron.ipcMain.handle(
|
|
26677
|
-
Channels.AGENT_SESSION_CAPTURE,
|
|
26678
|
-
(_, note) => runtime2.captureSession(note)
|
|
27152
|
+
(event, name, note) => {
|
|
27153
|
+
requireTrusted(event);
|
|
27154
|
+
return runtime2.createCheckpoint(name, note);
|
|
27155
|
+
}
|
|
26679
27156
|
);
|
|
27157
|
+
electron.ipcMain.handle(Channels.AGENT_CHECKPOINT_RESTORE, (event, checkpointId) => {
|
|
27158
|
+
requireTrusted(event);
|
|
27159
|
+
return runtime2.restoreCheckpoint(checkpointId);
|
|
27160
|
+
});
|
|
27161
|
+
electron.ipcMain.handle(Channels.AGENT_CHECKPOINT_UPDATE_NOTE, (event, checkpointId, note) => {
|
|
27162
|
+
requireTrusted(event);
|
|
27163
|
+
return runtime2.updateCheckpointNote(checkpointId, note || "");
|
|
27164
|
+
});
|
|
27165
|
+
electron.ipcMain.handle(Channels.AGENT_UNDO_LAST_ACTION, (event) => {
|
|
27166
|
+
requireTrusted(event);
|
|
27167
|
+
return runtime2.undoLastAction();
|
|
27168
|
+
});
|
|
27169
|
+
electron.ipcMain.handle(Channels.AGENT_SESSION_CAPTURE, (event, note) => {
|
|
27170
|
+
requireTrusted(event);
|
|
27171
|
+
return runtime2.captureSession(note);
|
|
27172
|
+
});
|
|
26680
27173
|
electron.ipcMain.handle(
|
|
26681
27174
|
Channels.AGENT_SESSION_RESTORE,
|
|
26682
|
-
(
|
|
27175
|
+
(event, snapshot2) => {
|
|
27176
|
+
requireTrusted(event);
|
|
27177
|
+
return runtime2.restoreSession(snapshot2);
|
|
27178
|
+
}
|
|
26683
27179
|
);
|
|
26684
27180
|
registerBookmarkHandlers();
|
|
26685
|
-
electron.ipcMain.handle(Channels.HIGHLIGHT_CAPTURE, async () => {
|
|
27181
|
+
electron.ipcMain.handle(Channels.HIGHLIGHT_CAPTURE, async (event) => {
|
|
27182
|
+
requireTrusted(event);
|
|
26686
27183
|
try {
|
|
26687
27184
|
const activeTab = tabManager.getActiveTab();
|
|
26688
27185
|
if (!activeTab) {
|
|
@@ -26726,10 +27223,12 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26726
27223
|
logger$4.warn("Failed to persist auto-highlight selection:", err);
|
|
26727
27224
|
}
|
|
26728
27225
|
});
|
|
26729
|
-
electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_COUNT, () => {
|
|
27226
|
+
electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_COUNT, (event) => {
|
|
27227
|
+
requireTrusted(event);
|
|
26730
27228
|
return getActiveHighlightCountSafe();
|
|
26731
27229
|
});
|
|
26732
|
-
electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_SCROLL, (
|
|
27230
|
+
electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_SCROLL, (event, index) => {
|
|
27231
|
+
requireTrusted(event);
|
|
26733
27232
|
const info = getActiveTabInfo(tabManager);
|
|
26734
27233
|
if (!info) return false;
|
|
26735
27234
|
try {
|
|
@@ -26739,7 +27238,8 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26739
27238
|
return false;
|
|
26740
27239
|
}
|
|
26741
27240
|
});
|
|
26742
|
-
electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_REMOVE, async (
|
|
27241
|
+
electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_REMOVE, async (event, index) => {
|
|
27242
|
+
requireTrusted(event);
|
|
26743
27243
|
const info = getActiveTabInfo(tabManager);
|
|
26744
27244
|
if (!info) return false;
|
|
26745
27245
|
try {
|
|
@@ -26753,7 +27253,8 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26753
27253
|
return false;
|
|
26754
27254
|
}
|
|
26755
27255
|
});
|
|
26756
|
-
electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_CLEAR, async () => {
|
|
27256
|
+
electron.ipcMain.handle(Channels.HIGHLIGHT_NAV_CLEAR, async (event) => {
|
|
27257
|
+
requireTrusted(event);
|
|
26757
27258
|
const info = getActiveTabInfo(tabManager);
|
|
26758
27259
|
if (!info) return false;
|
|
26759
27260
|
try {
|
|
@@ -26768,22 +27269,27 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26768
27269
|
}
|
|
26769
27270
|
});
|
|
26770
27271
|
const findBridge = createFindInPageBridge(tabManager, chromeView);
|
|
26771
|
-
electron.ipcMain.handle(Channels.FIND_IN_PAGE_START, (
|
|
27272
|
+
electron.ipcMain.handle(Channels.FIND_IN_PAGE_START, (event, text, options) => {
|
|
27273
|
+
requireTrusted(event);
|
|
26772
27274
|
return findBridge.start(text, options);
|
|
26773
27275
|
});
|
|
26774
|
-
electron.ipcMain.handle(Channels.FIND_IN_PAGE_NEXT, (
|
|
27276
|
+
electron.ipcMain.handle(Channels.FIND_IN_PAGE_NEXT, (event, forward) => {
|
|
27277
|
+
requireTrusted(event);
|
|
26775
27278
|
return findBridge.next(forward);
|
|
26776
27279
|
});
|
|
26777
|
-
electron.ipcMain.handle(Channels.FIND_IN_PAGE_STOP, (
|
|
27280
|
+
electron.ipcMain.handle(Channels.FIND_IN_PAGE_STOP, (event, action) => {
|
|
27281
|
+
requireTrusted(event);
|
|
26778
27282
|
findBridge.stop(action);
|
|
26779
27283
|
});
|
|
26780
27284
|
registerHistoryHandlers();
|
|
26781
|
-
electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_TOGGLE, () => {
|
|
27285
|
+
electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_TOGGLE, (event) => {
|
|
27286
|
+
requireTrusted(event);
|
|
26782
27287
|
windowState.uiState.devtoolsPanelOpen = !windowState.uiState.devtoolsPanelOpen;
|
|
26783
27288
|
layoutViews(windowState);
|
|
26784
27289
|
return { open: windowState.uiState.devtoolsPanelOpen };
|
|
26785
27290
|
});
|
|
26786
|
-
electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_RESIZE, (
|
|
27291
|
+
electron.ipcMain.handle(Channels.DEVTOOLS_PANEL_RESIZE, (event, height) => {
|
|
27292
|
+
requireTrusted(event);
|
|
26787
27293
|
const clamped = Math.max(MIN_DEVTOOLS_PANEL, Math.min(MAX_DEVTOOLS_PANEL, Math.round(height)));
|
|
26788
27294
|
windowState.uiState.devtoolsPanelHeight = clamped;
|
|
26789
27295
|
layoutViews(windowState);
|
|
@@ -26796,20 +27302,24 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26796
27302
|
registerHumanVaultHandlers();
|
|
26797
27303
|
registerWindowControlHandlers(mainWindow);
|
|
26798
27304
|
registerCodexHandlers();
|
|
26799
|
-
electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, () => {
|
|
27305
|
+
electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, (event) => {
|
|
27306
|
+
requireTrusted(event);
|
|
26800
27307
|
return getInstalledKits();
|
|
26801
27308
|
});
|
|
26802
|
-
electron.ipcMain.handle(Channels.AUTOMATION_INSTALL_FROM_FILE, async () => {
|
|
27309
|
+
electron.ipcMain.handle(Channels.AUTOMATION_INSTALL_FROM_FILE, async (event) => {
|
|
27310
|
+
requireTrusted(event);
|
|
26803
27311
|
return await installKitFromFile();
|
|
26804
27312
|
});
|
|
26805
|
-
electron.ipcMain.handle(Channels.AUTOMATION_UNINSTALL, (
|
|
27313
|
+
electron.ipcMain.handle(Channels.AUTOMATION_UNINSTALL, (event, id) => {
|
|
27314
|
+
requireTrusted(event);
|
|
26806
27315
|
assertString(id, "id");
|
|
26807
27316
|
return uninstallKit(id, getScheduledKitIds());
|
|
26808
27317
|
});
|
|
26809
27318
|
registerScheduleHandlers(windowState, runtime2, sendToRendererViews);
|
|
26810
27319
|
registerAutofillHandlers(windowState);
|
|
26811
27320
|
registerPageDiffHandlers(windowState, sendToRendererViews);
|
|
26812
|
-
electron.ipcMain.handle(Channels.CLEAR_BROWSING_DATA, async (
|
|
27321
|
+
electron.ipcMain.handle(Channels.CLEAR_BROWSING_DATA, async (event, options) => {
|
|
27322
|
+
requireTrusted(event);
|
|
26813
27323
|
const { cache, cookies, history, localStorage: clearLs, timeRange } = options;
|
|
26814
27324
|
if (cache) {
|
|
26815
27325
|
await electron.session.defaultSession.clearCache();
|
|
@@ -26826,25 +27336,47 @@ function registerIpcHandlers(windowState, runtime2) {
|
|
|
26826
27336
|
});
|
|
26827
27337
|
setDownloadBroadcaster(sendToRendererViews);
|
|
26828
27338
|
setPermissionBroadcaster(sendToRendererViews);
|
|
26829
|
-
electron.ipcMain.handle(Channels.DOWNLOADS_GET, () =>
|
|
26830
|
-
|
|
27339
|
+
electron.ipcMain.handle(Channels.DOWNLOADS_GET, (event) => {
|
|
27340
|
+
requireTrusted(event);
|
|
27341
|
+
return listDownloads();
|
|
27342
|
+
});
|
|
27343
|
+
electron.ipcMain.handle(Channels.DOWNLOADS_CLEAR, (event) => {
|
|
27344
|
+
requireTrusted(event);
|
|
26831
27345
|
clearDownloads();
|
|
26832
27346
|
return true;
|
|
26833
27347
|
});
|
|
26834
|
-
electron.ipcMain.handle(Channels.DOWNLOADS_OPEN, (
|
|
26835
|
-
|
|
26836
|
-
|
|
26837
|
-
|
|
27348
|
+
electron.ipcMain.handle(Channels.DOWNLOADS_OPEN, (event, id) => {
|
|
27349
|
+
requireTrusted(event);
|
|
27350
|
+
return openDownload(id);
|
|
27351
|
+
});
|
|
27352
|
+
electron.ipcMain.handle(Channels.DOWNLOADS_SHOW_IN_FOLDER, (event, id) => {
|
|
27353
|
+
requireTrusted(event);
|
|
27354
|
+
return showDownloadInFolder(id);
|
|
27355
|
+
});
|
|
27356
|
+
electron.ipcMain.handle(Channels.PERMISSIONS_GET, (event) => {
|
|
27357
|
+
requireTrusted(event);
|
|
27358
|
+
return listPermissions();
|
|
27359
|
+
});
|
|
27360
|
+
electron.ipcMain.handle(Channels.PERMISSIONS_CLEAR, (event) => {
|
|
27361
|
+
requireTrusted(event);
|
|
26838
27362
|
clearPermissions();
|
|
26839
27363
|
return true;
|
|
26840
27364
|
});
|
|
26841
|
-
electron.ipcMain.handle(Channels.PERMISSIONS_CLEAR_ORIGIN, (
|
|
27365
|
+
electron.ipcMain.handle(Channels.PERMISSIONS_CLEAR_ORIGIN, (event, origin) => {
|
|
27366
|
+
requireTrusted(event);
|
|
26842
27367
|
clearPermissionsForOrigin(origin);
|
|
26843
27368
|
return true;
|
|
26844
27369
|
});
|
|
26845
|
-
electron.ipcMain.handle(Channels.UPDATES_CHECK, () =>
|
|
26846
|
-
|
|
26847
|
-
|
|
27370
|
+
electron.ipcMain.handle(Channels.UPDATES_CHECK, (event) => {
|
|
27371
|
+
requireTrusted(event);
|
|
27372
|
+
return checkForUpdates();
|
|
27373
|
+
});
|
|
27374
|
+
electron.ipcMain.handle(Channels.UPDATES_OPEN_DOWNLOAD, (event) => {
|
|
27375
|
+
requireTrusted(event);
|
|
27376
|
+
return openUpdateDownload();
|
|
27377
|
+
});
|
|
27378
|
+
electron.ipcMain.handle(Channels.TAB_TOGGLE_PIP, async (event) => {
|
|
27379
|
+
requireTrusted(event);
|
|
26848
27380
|
return togglePictureInPicture(tabManager);
|
|
26849
27381
|
});
|
|
26850
27382
|
}
|
|
@@ -27276,6 +27808,7 @@ class AgentRuntime {
|
|
|
27276
27808
|
this.state = this.loadPersistedState();
|
|
27277
27809
|
onMcpStatusChange(() => this.emit());
|
|
27278
27810
|
}
|
|
27811
|
+
tabManager;
|
|
27279
27812
|
state;
|
|
27280
27813
|
updateListener = null;
|
|
27281
27814
|
pendingResolvers = /* @__PURE__ */ new Map();
|
|
@@ -27287,11 +27820,11 @@ class AgentRuntime {
|
|
|
27287
27820
|
}
|
|
27288
27821
|
}
|
|
27289
27822
|
getState() {
|
|
27290
|
-
const
|
|
27291
|
-
|
|
27292
|
-
|
|
27293
|
-
|
|
27294
|
-
return
|
|
27823
|
+
const snapshot2 = clone(this.state);
|
|
27824
|
+
snapshot2.mcpStatus = getMcpStatus();
|
|
27825
|
+
snapshot2.canUndo = this.canUndo();
|
|
27826
|
+
snapshot2.undoInfo = this.getUndoInfo();
|
|
27827
|
+
return snapshot2;
|
|
27295
27828
|
}
|
|
27296
27829
|
onTabStateChanged() {
|
|
27297
27830
|
this.captureSession();
|
|
@@ -27327,13 +27860,13 @@ class AgentRuntime {
|
|
|
27327
27860
|
return this.getState();
|
|
27328
27861
|
}
|
|
27329
27862
|
createCheckpoint(name, note) {
|
|
27330
|
-
const
|
|
27863
|
+
const snapshot2 = this.captureSession(note);
|
|
27331
27864
|
const checkpoint = {
|
|
27332
27865
|
id: crypto$2.randomUUID(),
|
|
27333
27866
|
name: name?.trim() || `Checkpoint ${this.state.checkpoints.length + 1}`,
|
|
27334
27867
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27335
27868
|
note: note?.trim() || void 0,
|
|
27336
|
-
snapshot
|
|
27869
|
+
snapshot: snapshot2
|
|
27337
27870
|
};
|
|
27338
27871
|
this.state.checkpoints = [...this.state.checkpoints, checkpoint].slice(
|
|
27339
27872
|
-20
|
|
@@ -27367,26 +27900,26 @@ class AgentRuntime {
|
|
|
27367
27900
|
return { actionName: latest.actionName, capturedAt: latest.capturedAt };
|
|
27368
27901
|
}
|
|
27369
27902
|
undoLastAction() {
|
|
27370
|
-
const
|
|
27371
|
-
if (!
|
|
27903
|
+
const snapshot2 = this.undoSnapshots.at(-1);
|
|
27904
|
+
if (!snapshot2) return null;
|
|
27372
27905
|
try {
|
|
27373
|
-
this.tabManager.restoreSession(
|
|
27906
|
+
this.tabManager.restoreSession(snapshot2.snapshot);
|
|
27374
27907
|
this.undoSnapshots.pop();
|
|
27375
27908
|
} catch (error) {
|
|
27376
27909
|
logger$3.error("Failed to restore undo snapshot", error);
|
|
27377
27910
|
return null;
|
|
27378
27911
|
}
|
|
27379
|
-
this.captureSession(`Undid ${
|
|
27380
|
-
return
|
|
27912
|
+
this.captureSession(`Undid ${snapshot2.actionName}`);
|
|
27913
|
+
return snapshot2.actionName;
|
|
27381
27914
|
}
|
|
27382
27915
|
captureSession(note) {
|
|
27383
|
-
const
|
|
27384
|
-
this.state.session =
|
|
27916
|
+
const snapshot2 = this.tabManager.snapshotSession(note);
|
|
27917
|
+
this.state.session = snapshot2;
|
|
27385
27918
|
this.emit();
|
|
27386
|
-
return clone(
|
|
27919
|
+
return clone(snapshot2);
|
|
27387
27920
|
}
|
|
27388
|
-
restoreSession(
|
|
27389
|
-
const target =
|
|
27921
|
+
restoreSession(snapshot2) {
|
|
27922
|
+
const target = snapshot2 || this.state.session;
|
|
27390
27923
|
if (!target) {
|
|
27391
27924
|
return this.captureSession("No saved session to restore");
|
|
27392
27925
|
}
|
|
@@ -27528,6 +28061,7 @@ ${progress}
|
|
|
27528
28061
|
args = {},
|
|
27529
28062
|
tabId = null,
|
|
27530
28063
|
dangerous = false,
|
|
28064
|
+
requiresApproval = false,
|
|
27531
28065
|
undoable,
|
|
27532
28066
|
executor
|
|
27533
28067
|
}) {
|
|
@@ -27547,7 +28081,7 @@ ${progress}
|
|
|
27547
28081
|
streamId: transcriptStreamId,
|
|
27548
28082
|
mode: "replace"
|
|
27549
28083
|
});
|
|
27550
|
-
const approvalReason = this.getApprovalReason(dangerous);
|
|
28084
|
+
const approvalReason = this.getApprovalReason(dangerous, requiresApproval);
|
|
27551
28085
|
if (approvalReason) {
|
|
27552
28086
|
this.publishTranscript({
|
|
27553
28087
|
source,
|
|
@@ -27625,8 +28159,8 @@ ${progress}
|
|
|
27625
28159
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
27626
28160
|
};
|
|
27627
28161
|
}
|
|
27628
|
-
pushUndoSnapshot(
|
|
27629
|
-
this.undoSnapshots = [...this.undoSnapshots,
|
|
28162
|
+
pushUndoSnapshot(snapshot2) {
|
|
28163
|
+
this.undoSnapshots = [...this.undoSnapshots, snapshot2].slice(
|
|
27630
28164
|
-10
|
|
27631
28165
|
);
|
|
27632
28166
|
}
|
|
@@ -27770,10 +28304,13 @@ ${progress}
|
|
|
27770
28304
|
)
|
|
27771
28305
|
};
|
|
27772
28306
|
}
|
|
27773
|
-
getApprovalReason(dangerous) {
|
|
28307
|
+
getApprovalReason(dangerous, requiresApproval) {
|
|
27774
28308
|
if (this.state.supervisor.paused) {
|
|
27775
28309
|
return "Agent execution is paused";
|
|
27776
28310
|
}
|
|
28311
|
+
if (requiresApproval) {
|
|
28312
|
+
return "Approval required: high-risk action";
|
|
28313
|
+
}
|
|
27777
28314
|
if (this.state.supervisor.approvalMode === "manual") {
|
|
27778
28315
|
return "Approval required: ask every time mode";
|
|
27779
28316
|
}
|