local-operator-ui 0.33.16 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/main/index.js +539 -90
- package/out/preload/index.js +2115 -6
- package/out/renderer/assets/{_basePickBy-CGsf5s5y.js → _basePickBy-9TBYQa7o.js} +1 -1
- package/out/renderer/assets/{_baseUniq-C43UnLjk.js → _baseUniq-LWPq37Nv.js} +1 -1
- package/out/renderer/assets/{agent-details-page-BIwam1AY.js → agent-details-page-w0feeNbR.js} +2 -2
- package/out/renderer/assets/{agent-hub-page-01a3MLYP.js → agent-hub-page-MpNGaBsQ.js} +2 -2
- package/out/renderer/assets/{agents-page-CRnd_ZL5.js → agents-page-CS_TizSE.js} +3 -3
- package/out/renderer/assets/{arc-M0pLW_S9.js → arc-Cl2fdKKj.js} +1 -1
- package/out/renderer/assets/{architectureDiagram-IEHRJDOE-BSBbZOP-.js → architectureDiagram-IEHRJDOE-BjnbTm2l.js} +1 -1
- package/out/renderer/assets/{blockDiagram-JOT3LUYC-B927zHwX.js → blockDiagram-JOT3LUYC-pkJso5xt.js} +1 -1
- package/out/renderer/assets/{book-open-BOhsj37a.js → book-open-NI0D1Wqb.js} +1 -1
- package/out/renderer/assets/browser-page-CXJ90U-n.js +1 -0
- package/out/renderer/assets/{browser-webauthn-prompt-k8f91jC5.js → browser-webauthn-prompt-B_cRbltg.js} +1 -1
- package/out/renderer/assets/{c4Diagram-VJAJSXHY-Db4tt7Tq.js → c4Diagram-VJAJSXHY-CsiT1ln1.js} +1 -1
- package/out/renderer/assets/channel-CjpL-D3q.js +1 -0
- package/out/renderer/assets/{chunk-4BMEZGHF-Esvq7q-I.js → chunk-4BMEZGHF-BCo1pqHD.js} +1 -1
- package/out/renderer/assets/{chunk-A2AXSNBT-YDyKUqYa.js → chunk-A2AXSNBT-DG75mkPI.js} +1 -1
- package/out/renderer/assets/{chunk-AEK57VVT-D7xfN31Q.js → chunk-AEK57VVT-nm3o7842.js} +1 -1
- package/out/renderer/assets/{chunk-D6G4REZN-B7GPJrWB.js → chunk-D6G4REZN-DwdG2uCi.js} +1 -1
- package/out/renderer/assets/{chunk-RZ5BOZE2-OrSabd2d.js → chunk-RZ5BOZE2-DhfAnKjd.js} +1 -1
- package/out/renderer/assets/{chunk-XZIHB7SX-Ls62ZuT-.js → chunk-XZIHB7SX-CpBM6cFt.js} +1 -1
- package/out/renderer/assets/classDiagram-GIVACNV2-BD5hmHYM.js +1 -0
- package/out/renderer/assets/classDiagram-v2-COTLJTTW-BD5hmHYM.js +1 -0
- package/out/renderer/assets/clone-Ba8sZt4_.js +1 -0
- package/out/renderer/assets/{console-mirror-D5I24PC-.js → console-mirror-CBdCHzj8.js} +1 -1
- package/out/renderer/assets/{consoleCapture-BkjkGzZU.js → consoleCapture-Bud3UbKT.js} +1 -1
- package/out/renderer/assets/{conversation-input-sync-CvO1w7en.js → conversation-input-sync-gc-8lTMK.js} +243 -239
- package/out/renderer/assets/{dagre-OKDRZEBW-BF8NMghr.js → dagre-OKDRZEBW-BOkEddRs.js} +1 -1
- package/out/renderer/assets/{diagram-SSKATNLV-DYPlCY6D.js → diagram-SSKATNLV-CQn_39Sz.js} +1 -1
- package/out/renderer/assets/{diagram-VNBRO52H-htbXA7Fo.js → diagram-VNBRO52H-DroiF3Bk.js} +1 -1
- package/out/renderer/assets/{erDiagram-Q7BY3M3F-4Mm_pKd-.js → erDiagram-Q7BY3M3F-CWW8Quok.js} +1 -1
- package/out/renderer/assets/{error-boundary-CYrSujeA.js → error-boundary-AUcef_lG.js} +1 -1
- package/out/renderer/assets/{flowDiagram-4HSFHLVR-DDzUlC1i.js → flowDiagram-4HSFHLVR-BwQjXnik.js} +1 -1
- package/out/renderer/assets/{ganttDiagram-APWFNJXF-4vH_i6M9.js → ganttDiagram-APWFNJXF-B1QNh_bE.js} +1 -1
- package/out/renderer/assets/{gitGraphDiagram-7IBYFJ6S-PTmOOixv.js → gitGraphDiagram-7IBYFJ6S-Wj10byRK.js} +1 -1
- package/out/renderer/assets/{graph-016lbgzw.js → graph-CmV1xTJR.js} +1 -1
- package/out/renderer/assets/index-BeS_HPbS.js +807 -0
- package/out/renderer/assets/{index-5rWUB2pk.js → index-BivhAAGx.js} +1 -1
- package/out/renderer/assets/{index-I0rfR2G9.js → index-QtFRh-C-.js} +1 -1
- package/out/renderer/assets/index-mRyzMIoR.css +1 -0
- package/out/renderer/assets/{infoDiagram-PH2N3AL5-BUH-2kNx.js → infoDiagram-PH2N3AL5-CZTZ4QP3.js} +1 -1
- package/out/renderer/assets/{installer-DzJgD7dC.js → installer-DeUwkvHe.js} +1 -1
- package/out/renderer/assets/{journeyDiagram-U35MCT3I-B0GPzUDZ.js → journeyDiagram-U35MCT3I-BXorYXyE.js} +1 -1
- package/out/renderer/assets/{kanban-definition-NDS4AKOZ-DPMTM-PB.js → kanban-definition-NDS4AKOZ-BNfLQEdM.js} +1 -1
- package/out/renderer/assets/{layout-CZCsyyxh.js → layout-BgGtCNb_.js} +1 -1
- package/out/renderer/assets/{legacy-agents-page-BrIHqr1U.js → legacy-agents-page-DyNtw5de.js} +4 -4
- package/out/renderer/assets/{mermaid.core-DIr_Z2eU.js → mermaid.core-C5N5tFD-.js} +5 -5
- package/out/renderer/assets/{mesh-page-CY9vZlLm.js → mesh-page-nKes20vh.js} +1 -1
- package/out/renderer/assets/{mindmap-definition-ALO5MXBD-Dk9hiZoP.js → mindmap-definition-ALO5MXBD-DsqW-F-E.js} +1 -1
- package/out/renderer/assets/mini-CYMmaq4M.js +1 -0
- package/out/renderer/assets/{org-surface-gate-vsaHuMo7.js → org-surface-gate-CM0x2fAa.js} +1 -1
- package/out/renderer/assets/{page-header-DWAQV8z7.js → page-header-BMHnL0kj.js} +1 -1
- package/out/renderer/assets/{pieDiagram-IB7DONF6-CT-KDbbB.js → pieDiagram-IB7DONF6-C9uKRUck.js} +1 -1
- package/out/renderer/assets/{projects-page-B-Uqww5s.js → projects-page-BsWuzpbw.js} +1 -1
- package/out/renderer/assets/{quadrantDiagram-7GDLP6J5-CbuvxmHL.js → quadrantDiagram-7GDLP6J5-xZZ3WcYT.js} +1 -1
- package/out/renderer/assets/{radar-MK3ICKWK-DWgcYgI1.js → radar-MK3ICKWK-08z-9TMb.js} +1 -1
- package/out/renderer/assets/{radient-auth-buttons-BwMgaUi2.js → radient-auth-buttons-CzmYII93.js} +1 -1
- package/out/renderer/assets/{requirementDiagram-KVF5MWMF-gL2w5pv1.js → requirementDiagram-KVF5MWMF-CA8yDZr7.js} +1 -1
- package/out/renderer/assets/{sankeyDiagram-QLVOVGJD-COy-DHKN.js → sankeyDiagram-QLVOVGJD-C_S67_wP.js} +1 -1
- package/out/renderer/assets/{schedules-page-WYM7voi8.js → schedules-page-CTlQizzj.js} +19 -24
- package/out/renderer/assets/{sequenceDiagram-X6HHIX6F-D8fMPudr.js → sequenceDiagram-X6HHIX6F-BgeEbPQZ.js} +1 -1
- package/out/renderer/assets/{settings-page-73bm6YFk.js → settings-page-JBT67VrP.js} +7 -7
- package/out/renderer/assets/{settings-section-CfFkk5yW.js → settings-section-BzPM14cn.js} +1 -1
- package/out/renderer/assets/{stateDiagram-DGXRK772-DJv925EE.js → stateDiagram-DGXRK772-CZKqMaPx.js} +1 -1
- package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-ClyI23kk.js +1 -0
- package/out/renderer/assets/{timeline-definition-BDJGKUSR-D4Mikzrt.js → timeline-definition-BDJGKUSR-o4J6Cn1Y.js} +1 -1
- package/out/renderer/assets/ui-preferences-store-Bg2HUvBc.js +1 -0
- package/out/renderer/assets/{use-download-agent-mutation-EYt16YWD.js → use-download-agent-mutation-DoSIu0x-.js} +2 -2
- package/out/renderer/assets/{use-memberships-query-CTlFzQjW.js → use-memberships-query-BIiDf0qJ.js} +1 -1
- package/out/renderer/assets/{use-session-snapshot-JyEJO1f9.js → use-session-snapshot-Ctn7Q-U9.js} +1 -1
- package/out/renderer/assets/{xychartDiagram-VJFVF3MP-7I7MMjXI.js → xychartDiagram-VJFVF3MP-QsBQWdLs.js} +1 -1
- package/out/renderer/console-capture.html +5 -5
- package/out/renderer/index.html +7 -7
- package/out/renderer/installer.html +4 -4
- package/out/renderer/mini.html +7 -7
- package/package.json +3 -2
- package/out/renderer/assets/browser-page-Dk6im9oj.js +0 -1
- package/out/renderer/assets/channel-CILm-Wsr.js +0 -1
- package/out/renderer/assets/classDiagram-GIVACNV2-BjqKgY46.js +0 -1
- package/out/renderer/assets/classDiagram-v2-COTLJTTW-BjqKgY46.js +0 -1
- package/out/renderer/assets/clone-Dj2cZSoa.js +0 -1
- package/out/renderer/assets/index-CTM-knBb.css +0 -1
- package/out/renderer/assets/index-DWsjHqzk.js +0 -802
- package/out/renderer/assets/mini-BGlRDs7P.js +0 -1
- package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-BL621Pie.js +0 -1
- package/out/renderer/assets/ui-preferences-store-DrvWjHl4.js +0 -1
- /package/out/renderer/assets/{index-nSWgbpx8.js → index-CrBT28zA.js} +0 -0
package/out/main/index.js
CHANGED
|
@@ -1242,7 +1242,19 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
|
|
|
1242
1242
|
command: id$1,
|
|
1243
1243
|
name: zod.z.string().max(128).optional()
|
|
1244
1244
|
}).strict(),
|
|
1245
|
-
zod.z.object({
|
|
1245
|
+
zod.z.object({
|
|
1246
|
+
op: zod.z.literal("models.catalogue"),
|
|
1247
|
+
live: zod.z.boolean().optional(),
|
|
1248
|
+
/*
|
|
1249
|
+
* Which view of the catalogue to answer with: `usable` (the rows this
|
|
1250
|
+
* machine can run, plus the session's current model) or `all`. Absent
|
|
1251
|
+
* means `all`, which is also what a backend that predates the
|
|
1252
|
+
* parameter does with it — the picker stays correct either way, because
|
|
1253
|
+
* a compile-time-correct request against an old backend is filtered
|
|
1254
|
+
* client-side by `scopeCatalogue`.
|
|
1255
|
+
*/
|
|
1256
|
+
scope: zod.z.enum(["usable", "all"]).optional()
|
|
1257
|
+
}).strict(),
|
|
1246
1258
|
zod.z.object({
|
|
1247
1259
|
op: zod.z.literal("usage.get"),
|
|
1248
1260
|
provider: id$1.optional(),
|
|
@@ -1995,6 +2007,34 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
|
|
|
1995
2007
|
* user reads as a defect of their press.
|
|
1996
2008
|
*/
|
|
1997
2009
|
action: zod.z.enum(["open", "pause", "resume", "greet", "status"])
|
|
2010
|
+
}).strict(),
|
|
2011
|
+
/*
|
|
2012
|
+
* THE CODE REQUEST LEDGER (the per-session PR/MR list, and its refresh).
|
|
2013
|
+
*
|
|
2014
|
+
* BOTH ROUTES SIT UNDER THE SESSION because the LEDGER is the session's: the
|
|
2015
|
+
* rows are derived from THIS conversation's transcript, so a read that did not
|
|
2016
|
+
* name the session could only be some other session's list or a fleet scan
|
|
2017
|
+
* nobody asked for. The pair follows the monitors/projects routes' pattern
|
|
2018
|
+
* (`Depends(require_desktop)` behind the desktop bearer) and the refresh is a
|
|
2019
|
+
* POST because it mutates the cache's TTL state; a GET with a side effect is
|
|
2020
|
+
* the shape the monitors routes already refused.
|
|
2021
|
+
*
|
|
2022
|
+
* `keys` narrows the refresh to named rows and `force` bypasses the TTL but
|
|
2023
|
+
* NOT a host's rate-limit window (`cooling` in the list answer says which);
|
|
2024
|
+
* both are optional because "refresh what is dirty" and "per the TTL" are the
|
|
2025
|
+
* route's defaults, which is what the pane's untouched Refresh press wants.
|
|
2026
|
+
*/
|
|
2027
|
+
zod.z.object({ op: zod.z.literal("code_requests.list"), sessionId }).strict(),
|
|
2028
|
+
zod.z.object({
|
|
2029
|
+
op: zod.z.literal("code_requests.refresh"),
|
|
2030
|
+
sessionId,
|
|
2031
|
+
/*
|
|
2032
|
+
* A key is one row's `key` from the list answer; the bound mirrors the
|
|
2033
|
+
* fetch routes' own ceilings rather than inventing one here, and the
|
|
2034
|
+
* cap keeps a mis-read list from being re-sent whole.
|
|
2035
|
+
*/
|
|
2036
|
+
keys: zod.z.array(zod.z.string().min(1).max(512)).max(256).optional(),
|
|
2037
|
+
force: zod.z.boolean().optional()
|
|
1998
2038
|
}).strict()
|
|
1999
2039
|
]);
|
|
2000
2040
|
const desktopRequestSchema = desktopRequestUnion.superRefine(
|
|
@@ -3106,11 +3146,13 @@ function desktopEndpoint(request) {
|
|
|
3106
3146
|
path: `/v1/desktop/sessions/${request.sessionId}/command-entities?command=${encodeURIComponent(request.command)}${request.name ? `&name=${encodeURIComponent(request.name)}` : ""}`,
|
|
3107
3147
|
method: "GET"
|
|
3108
3148
|
};
|
|
3109
|
-
case "models.catalogue":
|
|
3149
|
+
case "models.catalogue": {
|
|
3150
|
+
const scope = request.scope ? `&scope=${request.scope}` : "";
|
|
3110
3151
|
return {
|
|
3111
|
-
path: `/v1/desktop/models?live=${request.live ?? false}`,
|
|
3152
|
+
path: `/v1/desktop/models?live=${request.live ?? false}${scope}`,
|
|
3112
3153
|
method: "GET"
|
|
3113
3154
|
};
|
|
3155
|
+
}
|
|
3114
3156
|
case "usage.get": {
|
|
3115
3157
|
const query = new URLSearchParams({
|
|
3116
3158
|
live: String(request.live ?? false),
|
|
@@ -3479,11 +3521,33 @@ function desktopEndpoint(request) {
|
|
|
3479
3521
|
*/
|
|
3480
3522
|
body: { op: request.action }
|
|
3481
3523
|
};
|
|
3524
|
+
/*
|
|
3525
|
+
* THE CODE REQUEST LEDGER, the session-scoped pair (see the union members'
|
|
3526
|
+
* comment for why both routes name the session). The GET is the read the pane
|
|
3527
|
+
* and the chip share; the POST asks the host to re-fetch, and its body
|
|
3528
|
+
* carries only the fields the CALLER set, so an untouched Refresh press is
|
|
3529
|
+
* the route's own defaults rather than this client's opinion of them.
|
|
3530
|
+
*/
|
|
3531
|
+
case "code_requests.list":
|
|
3532
|
+
return {
|
|
3533
|
+
path: `/v1/desktop/sessions/${request.sessionId}/code-requests`,
|
|
3534
|
+
method: "GET"
|
|
3535
|
+
};
|
|
3536
|
+
case "code_requests.refresh":
|
|
3537
|
+
return {
|
|
3538
|
+
path: `/v1/desktop/sessions/${request.sessionId}/code-requests/refresh`,
|
|
3539
|
+
method: "POST",
|
|
3540
|
+
body: {
|
|
3541
|
+
...request.keys !== void 0 ? { keys: request.keys } : {},
|
|
3542
|
+
...request.force !== void 0 ? { force: request.force } : {}
|
|
3543
|
+
}
|
|
3544
|
+
};
|
|
3482
3545
|
}
|
|
3483
3546
|
}
|
|
3484
3547
|
const MAX_PROBE_PATHS = 64;
|
|
3485
3548
|
const DIRECTORY_ENTRY_LIMIT = 200;
|
|
3486
3549
|
const MAX_FILE_READ_BYTES = 64 * 1024 * 1024;
|
|
3550
|
+
const EXTERNAL_OPEN_REFUSED_CHANNEL = "external-open-refused";
|
|
3487
3551
|
const MINI_VIEW_SUMMONED = "mini-view:summoned";
|
|
3488
3552
|
const MINI_VIEW_DISMISS = "mini-view:dismiss";
|
|
3489
3553
|
const MINI_VIEW_REGISTRATION = "mini-view:registration";
|
|
@@ -3827,9 +3891,9 @@ function classifyRecord(record, now, liveness = recordPidLiveness(record, now))
|
|
|
3827
3891
|
return age > HEARTBEAT_TIMEOUT_MS ? "wedged" : "live";
|
|
3828
3892
|
}
|
|
3829
3893
|
function recordAddress(record) {
|
|
3830
|
-
return addressOf("http:", canonicalHost(record.host), record.port);
|
|
3894
|
+
return addressOf("http:", canonicalHost$1(record.host), record.port);
|
|
3831
3895
|
}
|
|
3832
|
-
function canonicalHost(host) {
|
|
3896
|
+
function canonicalHost$1(host) {
|
|
3833
3897
|
const bare = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
|
|
3834
3898
|
const unqualified = bare.endsWith(".") ? bare.slice(0, -1) : bare;
|
|
3835
3899
|
const lower = unqualified.toLowerCase();
|
|
@@ -4098,7 +4162,7 @@ function normaliseAddress(url) {
|
|
|
4098
4162
|
const port = parsed.port || (parsed.protocol === "https:" ? "443" : "80");
|
|
4099
4163
|
return addressOf(
|
|
4100
4164
|
parsed.protocol,
|
|
4101
|
-
canonicalHost(parsed.hostname),
|
|
4165
|
+
canonicalHost$1(parsed.hostname),
|
|
4102
4166
|
Number(port)
|
|
4103
4167
|
);
|
|
4104
4168
|
} catch {
|
|
@@ -42824,6 +42888,374 @@ function attachWindowChrome(options) {
|
|
|
42824
42888
|
};
|
|
42825
42889
|
return { sync, dispose };
|
|
42826
42890
|
}
|
|
42891
|
+
const ALLOW = { allowed: true };
|
|
42892
|
+
const deny = (reason) => ({ allowed: false, reason });
|
|
42893
|
+
const EXTERNAL_SCHEMES = /* @__PURE__ */ new Set([
|
|
42894
|
+
"https:",
|
|
42895
|
+
"http:",
|
|
42896
|
+
"mailto:"
|
|
42897
|
+
]);
|
|
42898
|
+
const MAX_EXTERNAL_URL_LENGTH = 16384;
|
|
42899
|
+
const IPV4_PART = /^\d{1,3}$/;
|
|
42900
|
+
const IPV6_MAPPED_PREFIX = /^::ffff:(.+)$/;
|
|
42901
|
+
const TRAILING_DOT = /\.$/;
|
|
42902
|
+
function isPrivateIpv4(host) {
|
|
42903
|
+
const parts = host.split(".");
|
|
42904
|
+
if (parts.length !== 4) return false;
|
|
42905
|
+
const octets = [];
|
|
42906
|
+
for (const part of parts) {
|
|
42907
|
+
if (!IPV4_PART.test(part)) return false;
|
|
42908
|
+
const value = Number(part);
|
|
42909
|
+
if (value > 255) return false;
|
|
42910
|
+
octets.push(value);
|
|
42911
|
+
}
|
|
42912
|
+
const [a, b] = octets;
|
|
42913
|
+
if (a === 0 || a === 127) return true;
|
|
42914
|
+
if (a === 10) return true;
|
|
42915
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
42916
|
+
if (a === 192 && b === 168) return true;
|
|
42917
|
+
if (a === 169 && b === 254) return true;
|
|
42918
|
+
return false;
|
|
42919
|
+
}
|
|
42920
|
+
function isPrivateIpv6(host) {
|
|
42921
|
+
if (host === "::" || host === "::1") return true;
|
|
42922
|
+
const mapped = IPV6_MAPPED_PREFIX.exec(host);
|
|
42923
|
+
if (mapped) {
|
|
42924
|
+
const rest = mapped[1];
|
|
42925
|
+
if (rest.includes(".")) return isPrivateIpv4(rest);
|
|
42926
|
+
const groups = rest.split(":");
|
|
42927
|
+
if (groups.length === 2) {
|
|
42928
|
+
const hi = Number.parseInt(groups[0], 16);
|
|
42929
|
+
const lo = Number.parseInt(groups[1], 16);
|
|
42930
|
+
if (Number.isFinite(hi) && Number.isFinite(lo)) {
|
|
42931
|
+
return isPrivateIpv4(
|
|
42932
|
+
[hi >> 8, hi & 255, lo >> 8, lo & 255].join(".")
|
|
42933
|
+
);
|
|
42934
|
+
}
|
|
42935
|
+
}
|
|
42936
|
+
return true;
|
|
42937
|
+
}
|
|
42938
|
+
const first = Number.parseInt(host.split(":")[0] || "0", 16);
|
|
42939
|
+
if (!Number.isFinite(first)) return true;
|
|
42940
|
+
if ((first & 65472) === 65152) return true;
|
|
42941
|
+
if ((first & 65024) === 64512) return true;
|
|
42942
|
+
return false;
|
|
42943
|
+
}
|
|
42944
|
+
const canonicalHost = (hostname) => hostname.toLowerCase().replace(TRAILING_DOT, "");
|
|
42945
|
+
function isLocalOrPrivateHost(hostname) {
|
|
42946
|
+
const host = canonicalHost(hostname);
|
|
42947
|
+
if (host === "localhost" || host.endsWith(".localhost")) return true;
|
|
42948
|
+
if (host.startsWith("[") && host.endsWith("]"))
|
|
42949
|
+
return isPrivateIpv6(host.slice(1, -1));
|
|
42950
|
+
return isPrivateIpv4(host);
|
|
42951
|
+
}
|
|
42952
|
+
function externalUrlVerdict(raw) {
|
|
42953
|
+
if (typeof raw !== "string" || raw.length === 0)
|
|
42954
|
+
return { allowed: false, reason: "not a string" };
|
|
42955
|
+
if (raw.length > MAX_EXTERNAL_URL_LENGTH)
|
|
42956
|
+
return { allowed: false, reason: "too long" };
|
|
42957
|
+
let url;
|
|
42958
|
+
try {
|
|
42959
|
+
url = new URL(raw);
|
|
42960
|
+
} catch {
|
|
42961
|
+
return { allowed: false, reason: "not a URL" };
|
|
42962
|
+
}
|
|
42963
|
+
if (!EXTERNAL_SCHEMES.has(url.protocol))
|
|
42964
|
+
return {
|
|
42965
|
+
allowed: false,
|
|
42966
|
+
reason: `scheme ${url.protocol} is not handed out`
|
|
42967
|
+
};
|
|
42968
|
+
if (url.protocol !== "mailto:") {
|
|
42969
|
+
if (url.username || url.password)
|
|
42970
|
+
return { allowed: false, reason: "URL carries credentials" };
|
|
42971
|
+
if (!url.hostname) return { allowed: false, reason: "URL has no host" };
|
|
42972
|
+
if (isLocalOrPrivateHost(url.hostname))
|
|
42973
|
+
return {
|
|
42974
|
+
allowed: false,
|
|
42975
|
+
reason: "URL names a loopback or private-network host"
|
|
42976
|
+
};
|
|
42977
|
+
} else if (/(^|[?&])attach(ment)?=/i.test(url.search)) {
|
|
42978
|
+
return { allowed: false, reason: "mailto: names an attachment" };
|
|
42979
|
+
}
|
|
42980
|
+
return { allowed: true, url: url.href };
|
|
42981
|
+
}
|
|
42982
|
+
async function openVettedExternal(raw, open2, log2) {
|
|
42983
|
+
const verdict = externalUrlVerdict(raw);
|
|
42984
|
+
if (!verdict.allowed) {
|
|
42985
|
+
log2(`[window-guard] refused to open externally: ${verdict.reason}`);
|
|
42986
|
+
return { ok: false, reason: verdict.reason };
|
|
42987
|
+
}
|
|
42988
|
+
try {
|
|
42989
|
+
await open2(verdict.url);
|
|
42990
|
+
return { ok: true };
|
|
42991
|
+
} catch (error) {
|
|
42992
|
+
const reason = `the OS could not open the URL: ${error instanceof Error ? error.message : String(error)}`;
|
|
42993
|
+
log2(`[window-guard] ${reason}`);
|
|
42994
|
+
return { ok: false, reason };
|
|
42995
|
+
}
|
|
42996
|
+
}
|
|
42997
|
+
const AUTH_HOSTS = [
|
|
42998
|
+
"accounts.google.com",
|
|
42999
|
+
"oauth.googleusercontent.com",
|
|
43000
|
+
"content.googleapis.com",
|
|
43001
|
+
"ssl.gstatic.com",
|
|
43002
|
+
"login.microsoftonline.com",
|
|
43003
|
+
"microsoftonline.com",
|
|
43004
|
+
"login.live.com",
|
|
43005
|
+
"login.windows.net",
|
|
43006
|
+
"login.microsoft.com"
|
|
43007
|
+
];
|
|
43008
|
+
const AUTH_SCHEMES = /* @__PURE__ */ new Set([
|
|
43009
|
+
"storagerelay:",
|
|
43010
|
+
"msauth:",
|
|
43011
|
+
"msftauth:"
|
|
43012
|
+
]);
|
|
43013
|
+
function isAuthPopupUrl(url) {
|
|
43014
|
+
if (url.protocol !== "https:") return false;
|
|
43015
|
+
if (url.username || url.password) return false;
|
|
43016
|
+
if (url.port !== "") return false;
|
|
43017
|
+
const host = url.hostname.toLowerCase();
|
|
43018
|
+
return AUTH_HOSTS.some(
|
|
43019
|
+
(domain) => host === domain || host.endsWith(`.${domain}`)
|
|
43020
|
+
);
|
|
43021
|
+
}
|
|
43022
|
+
function popupVerdict(raw) {
|
|
43023
|
+
if (raw === "about:blank")
|
|
43024
|
+
return {
|
|
43025
|
+
action: "deny",
|
|
43026
|
+
reason: "about:blank is refused as a popup start"
|
|
43027
|
+
};
|
|
43028
|
+
let url = null;
|
|
43029
|
+
try {
|
|
43030
|
+
url = new URL(raw);
|
|
43031
|
+
} catch {
|
|
43032
|
+
}
|
|
43033
|
+
if (url && (isAuthPopupUrl(url) || AUTH_SCHEMES.has(url.protocol)))
|
|
43034
|
+
return { action: "auth" };
|
|
43035
|
+
const external = externalUrlVerdict(raw);
|
|
43036
|
+
return external.allowed ? { action: "external", url: external.url } : { action: "deny", reason: external.reason };
|
|
43037
|
+
}
|
|
43038
|
+
function popupNavigationVerdict(rawUrl) {
|
|
43039
|
+
if (rawUrl === "about:blank") return ALLOW;
|
|
43040
|
+
let url;
|
|
43041
|
+
try {
|
|
43042
|
+
url = new URL(rawUrl);
|
|
43043
|
+
} catch {
|
|
43044
|
+
return deny("not a URL");
|
|
43045
|
+
}
|
|
43046
|
+
if (isAuthPopupUrl(url) || AUTH_SCHEMES.has(url.protocol)) return ALLOW;
|
|
43047
|
+
return deny(`the sign-in window may not navigate to ${url.protocol}`);
|
|
43048
|
+
}
|
|
43049
|
+
const STATIC_FRAME_PREFIX = "/v1/static/";
|
|
43050
|
+
function frameNavigationVerdict(input, trustedRendererUrls) {
|
|
43051
|
+
let url;
|
|
43052
|
+
try {
|
|
43053
|
+
url = new URL(input.url);
|
|
43054
|
+
} catch {
|
|
43055
|
+
return deny("not a URL");
|
|
43056
|
+
}
|
|
43057
|
+
if (input.isMainFrame) {
|
|
43058
|
+
return trustedRendererUrls.some(
|
|
43059
|
+
(trusted) => trustedDesktopFrame(input.url, trusted)
|
|
43060
|
+
) ? ALLOW : deny("the main window may only show the app's own document");
|
|
43061
|
+
}
|
|
43062
|
+
if (url.protocol === "blob:") {
|
|
43063
|
+
let minted;
|
|
43064
|
+
try {
|
|
43065
|
+
minted = new URL(url.pathname);
|
|
43066
|
+
} catch {
|
|
43067
|
+
return deny("blob: URL names no origin");
|
|
43068
|
+
}
|
|
43069
|
+
const own = trustedRendererUrls.some((trusted) => {
|
|
43070
|
+
try {
|
|
43071
|
+
const t = new URL(trusted);
|
|
43072
|
+
return t.protocol === "file:" ? minted.protocol === "file:" : minted.origin === t.origin;
|
|
43073
|
+
} catch {
|
|
43074
|
+
return false;
|
|
43075
|
+
}
|
|
43076
|
+
});
|
|
43077
|
+
return own ? ALLOW : deny("blob: minted by a document that is not the app");
|
|
43078
|
+
}
|
|
43079
|
+
if ((url.protocol === "http:" || url.protocol === "https:") && isLocalOrPrivateHost(url.hostname) && url.pathname.startsWith(STATIC_FRAME_PREFIX))
|
|
43080
|
+
return ALLOW;
|
|
43081
|
+
return deny(`a child frame may not navigate to ${url.protocol}`);
|
|
43082
|
+
}
|
|
43083
|
+
function permissionVerdict(query, trustedRendererUrls) {
|
|
43084
|
+
if (!query.isMainFrame) return false;
|
|
43085
|
+
if (!trustedRendererUrls.some(
|
|
43086
|
+
(trusted) => trustedDesktopFrame(query.requestingUrl, trusted)
|
|
43087
|
+
))
|
|
43088
|
+
return false;
|
|
43089
|
+
if (query.permission === "clipboard-sanitized-write" || query.permission === "fullscreen")
|
|
43090
|
+
return true;
|
|
43091
|
+
if (query.permission !== "media") return false;
|
|
43092
|
+
if (query.mediaTypes)
|
|
43093
|
+
return query.mediaTypes.length > 0 && query.mediaTypes.every((type2) => type2 === "audio");
|
|
43094
|
+
return query.mediaType === "audio";
|
|
43095
|
+
}
|
|
43096
|
+
function decodedPathname(pathname) {
|
|
43097
|
+
let current = pathname;
|
|
43098
|
+
for (let round = 0; round < 3; round += 1) {
|
|
43099
|
+
let next;
|
|
43100
|
+
try {
|
|
43101
|
+
next = decodeURIComponent(current);
|
|
43102
|
+
} catch {
|
|
43103
|
+
break;
|
|
43104
|
+
}
|
|
43105
|
+
if (next === current) break;
|
|
43106
|
+
current = next;
|
|
43107
|
+
}
|
|
43108
|
+
return current;
|
|
43109
|
+
}
|
|
43110
|
+
function isStaticServeRequest(rawUrl) {
|
|
43111
|
+
try {
|
|
43112
|
+
const url = new URL(rawUrl);
|
|
43113
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
43114
|
+
if (!isLocalOrPrivateHost(url.hostname)) return false;
|
|
43115
|
+
return decodedPathname(url.pathname).toLowerCase().startsWith(STATIC_FRAME_PREFIX);
|
|
43116
|
+
} catch {
|
|
43117
|
+
return false;
|
|
43118
|
+
}
|
|
43119
|
+
}
|
|
43120
|
+
const PREVIEW_CSP = [
|
|
43121
|
+
"default-src 'none'",
|
|
43122
|
+
"script-src 'unsafe-inline' 'unsafe-eval' https:",
|
|
43123
|
+
"style-src 'unsafe-inline' https:",
|
|
43124
|
+
"img-src data: blob: https:",
|
|
43125
|
+
"font-src data: https:",
|
|
43126
|
+
"media-src data: blob: https:",
|
|
43127
|
+
"connect-src https:",
|
|
43128
|
+
"worker-src blob:",
|
|
43129
|
+
"form-action 'none'",
|
|
43130
|
+
"base-uri 'none'"
|
|
43131
|
+
].join("; ");
|
|
43132
|
+
function withPreviewPolicy(headers) {
|
|
43133
|
+
const out = { ...headers };
|
|
43134
|
+
const existing = (name) => Object.keys(out).find((key) => key.toLowerCase() === name);
|
|
43135
|
+
const cspKey = existing("content-security-policy");
|
|
43136
|
+
if (cspKey) out[cspKey] = [...out[cspKey], PREVIEW_CSP];
|
|
43137
|
+
else out["Content-Security-Policy"] = [PREVIEW_CSP];
|
|
43138
|
+
if (!existing("x-content-type-options"))
|
|
43139
|
+
out["X-Content-Type-Options"] = ["nosniff"];
|
|
43140
|
+
return out;
|
|
43141
|
+
}
|
|
43142
|
+
function downloadVerdict(itemUrl, initiatorUrl, trustedRendererUrls) {
|
|
43143
|
+
if (!itemUrl.startsWith("blob:"))
|
|
43144
|
+
return deny("only the app's own blob: exports may write files");
|
|
43145
|
+
if (!trustedRendererUrls.some(
|
|
43146
|
+
(trusted) => trustedDesktopFrame(initiatorUrl, trusted)
|
|
43147
|
+
))
|
|
43148
|
+
return deny("the download was not started by one of the app's documents");
|
|
43149
|
+
return ALLOW;
|
|
43150
|
+
}
|
|
43151
|
+
function installNavigationVerdict(contents, decide, log2) {
|
|
43152
|
+
const onNavigation = (event) => {
|
|
43153
|
+
if (event.isSameDocument) return;
|
|
43154
|
+
const verdict = decide(event.url, event.isMainFrame);
|
|
43155
|
+
if (verdict.allowed) return;
|
|
43156
|
+
event.preventDefault();
|
|
43157
|
+
log2(
|
|
43158
|
+
`[window-guard] blocked ${event.isMainFrame ? "main-frame" : "child-frame"} navigation to ${event.url.slice(0, 200)}: ${verdict.reason}`
|
|
43159
|
+
);
|
|
43160
|
+
};
|
|
43161
|
+
contents.on("will-frame-navigate", onNavigation);
|
|
43162
|
+
contents.on("will-redirect", onNavigation);
|
|
43163
|
+
}
|
|
43164
|
+
function guardNavigation(contents, trustedUrls, log2) {
|
|
43165
|
+
installNavigationVerdict(
|
|
43166
|
+
contents,
|
|
43167
|
+
(url, isMainFrame) => frameNavigationVerdict({ url, isMainFrame }, trustedUrls()),
|
|
43168
|
+
log2
|
|
43169
|
+
);
|
|
43170
|
+
}
|
|
43171
|
+
function guardWindowOpen(contents, decide, openExternal, authWindowOptions, log2, notifyRefused) {
|
|
43172
|
+
contents.setWindowOpenHandler((details) => {
|
|
43173
|
+
const verdict = decide(details.url);
|
|
43174
|
+
if (verdict.action === "auth") {
|
|
43175
|
+
return {
|
|
43176
|
+
action: "allow",
|
|
43177
|
+
overrideBrowserWindowOptions: authWindowOptions
|
|
43178
|
+
};
|
|
43179
|
+
}
|
|
43180
|
+
if (verdict.action === "external") {
|
|
43181
|
+
void openExternal(verdict.url).then((outcome) => {
|
|
43182
|
+
if (!outcome.ok) notifyRefused?.(verdict.url, outcome.reason);
|
|
43183
|
+
});
|
|
43184
|
+
} else {
|
|
43185
|
+
log2(`[window-guard] denied window.open: ${verdict.reason}`);
|
|
43186
|
+
notifyRefused?.(details.url, verdict.reason);
|
|
43187
|
+
}
|
|
43188
|
+
return { action: "deny" };
|
|
43189
|
+
});
|
|
43190
|
+
contents.on("did-create-window", (window) => {
|
|
43191
|
+
guardPopupNavigation(window.webContents, log2);
|
|
43192
|
+
});
|
|
43193
|
+
}
|
|
43194
|
+
function guardPopupNavigation(contents, log2) {
|
|
43195
|
+
installNavigationVerdict(contents, (url) => popupNavigationVerdict(url), log2);
|
|
43196
|
+
contents.setWindowOpenHandler(() => {
|
|
43197
|
+
log2("[window-guard] denied window.open from the sign-in popup");
|
|
43198
|
+
return { action: "deny" };
|
|
43199
|
+
});
|
|
43200
|
+
}
|
|
43201
|
+
function guardPermissions(ses, trustedUrls, log2) {
|
|
43202
|
+
ses.setPermissionRequestHandler(
|
|
43203
|
+
(_contents, permission, callback, details) => {
|
|
43204
|
+
const granted = permissionVerdict(
|
|
43205
|
+
{
|
|
43206
|
+
permission,
|
|
43207
|
+
mediaTypes: "mediaTypes" in details ? details.mediaTypes ?? [] : void 0,
|
|
43208
|
+
isMainFrame: details.isMainFrame,
|
|
43209
|
+
requestingUrl: details.requestingUrl
|
|
43210
|
+
},
|
|
43211
|
+
trustedUrls()
|
|
43212
|
+
);
|
|
43213
|
+
if (!granted)
|
|
43214
|
+
log2(
|
|
43215
|
+
`[window-guard] denied permission request ${permission} from ${details.requestingUrl.slice(0, 200)}`
|
|
43216
|
+
);
|
|
43217
|
+
callback(granted);
|
|
43218
|
+
}
|
|
43219
|
+
);
|
|
43220
|
+
ses.setPermissionCheckHandler(
|
|
43221
|
+
(_contents, permission, _origin, details) => permissionVerdict(
|
|
43222
|
+
{
|
|
43223
|
+
permission,
|
|
43224
|
+
mediaType: details.mediaType,
|
|
43225
|
+
isMainFrame: details.isMainFrame,
|
|
43226
|
+
requestingUrl: details.requestingUrl ?? ""
|
|
43227
|
+
},
|
|
43228
|
+
trustedUrls()
|
|
43229
|
+
)
|
|
43230
|
+
);
|
|
43231
|
+
}
|
|
43232
|
+
function guardPreviewResponses(ses) {
|
|
43233
|
+
ses.webRequest.onHeadersReceived((details, callback) => {
|
|
43234
|
+
if (!isStaticServeRequest(details.url)) {
|
|
43235
|
+
callback({});
|
|
43236
|
+
return;
|
|
43237
|
+
}
|
|
43238
|
+
callback({
|
|
43239
|
+
responseHeaders: withPreviewPolicy(
|
|
43240
|
+
details.responseHeaders ?? {}
|
|
43241
|
+
)
|
|
43242
|
+
});
|
|
43243
|
+
});
|
|
43244
|
+
}
|
|
43245
|
+
function guardDownloads(ses, trustedUrls, log2) {
|
|
43246
|
+
ses.on("will-download", (event, item, webContents) => {
|
|
43247
|
+
const verdict = downloadVerdict(
|
|
43248
|
+
item.getURL(),
|
|
43249
|
+
webContents?.getURL() ?? "",
|
|
43250
|
+
trustedUrls()
|
|
43251
|
+
);
|
|
43252
|
+
if (verdict.allowed) return;
|
|
43253
|
+
event.preventDefault();
|
|
43254
|
+
log2(
|
|
43255
|
+
`[window-guard] blocked a download from ${item.getURL().slice(0, 200)}: ${verdict.reason}`
|
|
43256
|
+
);
|
|
43257
|
+
});
|
|
43258
|
+
}
|
|
42827
43259
|
const BASE64_FILE_EXTENSIONS = ["csv", "tsv", "xls", "xlsx", "ods"];
|
|
42828
43260
|
const resolveUserPath = (filePath, cwd) => resolveUserPath$1(filePath, cwd, electron.app.getPath("home"));
|
|
42829
43261
|
const windowLaunch = resolveWindowLaunchPlan({
|
|
@@ -43098,6 +43530,45 @@ const ownLaunchRequest = () => ({
|
|
|
43098
43530
|
show: windowLaunch.show,
|
|
43099
43531
|
trigger: "initial-present"
|
|
43100
43532
|
});
|
|
43533
|
+
const rendererDocumentUrl = () => process.env.ELECTRON_RENDERER_URL || node_url.pathToFileURL(path$1.join(__dirname, "../renderer/index.html")).href;
|
|
43534
|
+
const trustedRendererDocuments = () => [
|
|
43535
|
+
rendererDocumentUrl(),
|
|
43536
|
+
miniViewUrlFor(rendererDocumentUrl())
|
|
43537
|
+
];
|
|
43538
|
+
const openExternalVetted = (raw) => openVettedExternal(
|
|
43539
|
+
raw,
|
|
43540
|
+
(url) => electron.shell.openExternal(url),
|
|
43541
|
+
(message) => logger.warn(message, LogFileType.BACKEND)
|
|
43542
|
+
);
|
|
43543
|
+
const authPopupWindowOptions = {
|
|
43544
|
+
width: 800,
|
|
43545
|
+
height: 700,
|
|
43546
|
+
// Increased height for better visibility
|
|
43547
|
+
minWidth: 600,
|
|
43548
|
+
minHeight: 500,
|
|
43549
|
+
center: true,
|
|
43550
|
+
frame: true,
|
|
43551
|
+
autoHideMenuBar: false,
|
|
43552
|
+
backgroundColor: "#FFFFFF",
|
|
43553
|
+
webPreferences: {
|
|
43554
|
+
contextIsolation: true,
|
|
43555
|
+
nodeIntegration: false,
|
|
43556
|
+
webSecurity: true,
|
|
43557
|
+
allowRunningInsecureContent: false,
|
|
43558
|
+
sandbox: true,
|
|
43559
|
+
// Enable sandbox for additional security
|
|
43560
|
+
// Disable various features that aren't needed for auth
|
|
43561
|
+
enableWebSQL: false,
|
|
43562
|
+
navigateOnDragDrop: false,
|
|
43563
|
+
spellcheck: false,
|
|
43564
|
+
/*
|
|
43565
|
+
* A JS dialog in a popup must not be able to wedge it (round-2 S-7): a
|
|
43566
|
+
* renderer-reached `confirm()`/`alert()` needs no permission and blocks
|
|
43567
|
+
* every later evaluation on the page behind its modal.
|
|
43568
|
+
*/
|
|
43569
|
+
disableDialogs: true
|
|
43570
|
+
}
|
|
43571
|
+
};
|
|
43101
43572
|
function createWindow(initialSession = null, openCatalogue = false, request = ownLaunchRequest()) {
|
|
43102
43573
|
const chrome = resolveLaunchWindowChrome({
|
|
43103
43574
|
argv: process.argv,
|
|
@@ -43242,66 +43713,22 @@ function createWindow(initialSession = null, openCatalogue = false, request = ow
|
|
|
43242
43713
|
);
|
|
43243
43714
|
}, 1500);
|
|
43244
43715
|
});
|
|
43245
|
-
|
|
43246
|
-
|
|
43247
|
-
|
|
43248
|
-
|
|
43249
|
-
|
|
43250
|
-
|
|
43251
|
-
|
|
43252
|
-
|
|
43253
|
-
|
|
43254
|
-
|
|
43255
|
-
|
|
43256
|
-
|
|
43257
|
-
|
|
43258
|
-
|
|
43259
|
-
|
|
43260
|
-
|
|
43261
|
-
// Auth relay domains
|
|
43262
|
-
"storagerelay",
|
|
43263
|
-
// Special case for initial blank page
|
|
43264
|
-
"about:blank"
|
|
43265
|
-
];
|
|
43266
|
-
const isTrustedAuthDomain = (
|
|
43267
|
-
// Special case for about:blank which is used by MSAL to initialize the popup
|
|
43268
|
-
details.url === "about:blank" || // Check other trusted domains
|
|
43269
|
-
trustedAuthDomains.some(
|
|
43270
|
-
(domain) => url.hostname.includes(domain) || url.protocol.includes(domain) || // Special case for storage relay URLs
|
|
43271
|
-
details.url.startsWith("storagerelay:") || details.url.includes("storagerelay")
|
|
43272
|
-
)
|
|
43273
|
-
);
|
|
43274
|
-
if (isTrustedAuthDomain) {
|
|
43275
|
-
return {
|
|
43276
|
-
action: "allow",
|
|
43277
|
-
overrideBrowserWindowOptions: {
|
|
43278
|
-
width: 800,
|
|
43279
|
-
height: 700,
|
|
43280
|
-
// Increased height for better visibility
|
|
43281
|
-
minWidth: 600,
|
|
43282
|
-
minHeight: 500,
|
|
43283
|
-
center: true,
|
|
43284
|
-
frame: true,
|
|
43285
|
-
autoHideMenuBar: false,
|
|
43286
|
-
backgroundColor: "#FFFFFF",
|
|
43287
|
-
webPreferences: {
|
|
43288
|
-
contextIsolation: true,
|
|
43289
|
-
nodeIntegration: false,
|
|
43290
|
-
webSecurity: true,
|
|
43291
|
-
allowRunningInsecureContent: false,
|
|
43292
|
-
sandbox: true,
|
|
43293
|
-
// Enable sandbox for additional security
|
|
43294
|
-
// Disable various features that aren't needed for auth
|
|
43295
|
-
enableWebSQL: false,
|
|
43296
|
-
navigateOnDragDrop: false,
|
|
43297
|
-
spellcheck: false
|
|
43298
|
-
}
|
|
43299
|
-
}
|
|
43300
|
-
};
|
|
43301
|
-
}
|
|
43302
|
-
electron.shell.openExternal(details.url);
|
|
43303
|
-
return { action: "deny" };
|
|
43304
|
-
});
|
|
43716
|
+
guardNavigation(
|
|
43717
|
+
mainWindow2.webContents,
|
|
43718
|
+
trustedRendererDocuments,
|
|
43719
|
+
(m) => logger.warn(m, LogFileType.BACKEND)
|
|
43720
|
+
);
|
|
43721
|
+
guardWindowOpen(
|
|
43722
|
+
mainWindow2.webContents,
|
|
43723
|
+
popupVerdict,
|
|
43724
|
+
openExternalVetted,
|
|
43725
|
+
authPopupWindowOptions,
|
|
43726
|
+
(m) => logger.warn(m, LogFileType.BACKEND),
|
|
43727
|
+
(url, reason) => mainWindow2.webContents.send(EXTERNAL_OPEN_REFUSED_CHANNEL, {
|
|
43728
|
+
url,
|
|
43729
|
+
reason
|
|
43730
|
+
})
|
|
43731
|
+
);
|
|
43305
43732
|
if (utils.is.dev && process.env.ELECTRON_RENDERER_URL) {
|
|
43306
43733
|
mainWindow2.loadURL(process.env.ELECTRON_RENDERER_URL);
|
|
43307
43734
|
} else {
|
|
@@ -43340,8 +43767,8 @@ const reportBackendFailure = (message, fileType) => {
|
|
|
43340
43767
|
const reportRaise = (line) => {
|
|
43341
43768
|
logger.info(`[window-raise] ${line}`, LogFileType.BACKEND);
|
|
43342
43769
|
};
|
|
43343
|
-
function describeForwardedLaunch(profile,
|
|
43344
|
-
const named =
|
|
43770
|
+
function describeForwardedLaunch(profile, session2) {
|
|
43771
|
+
const named = session2 === null ? null : `the conversation it named (${session2})`;
|
|
43345
43772
|
const handed = named === null ? "it named no conversation" : `${named} rides with it`;
|
|
43346
43773
|
const effect = windowLaunch.show === "never" ? named === null ? `${handed}, so nothing is waiting to be opened, and it will not raise a window in the meantime` : `${handed}: the app will open it in the window it already has open, or in the next window it creates when that window is in use or there is none, and it will not raise a window in the meantime (up to ${PARKED_LAUNCH_LIMIT} conversations wait; an older one is dropped and logged)` : windowLaunch.show === "inactive" ? `${handed}; the app may order its window forward without activating it` : `${handed}; the app will raise its window`;
|
|
43347
43774
|
return `[second-instance] this launch did not start a window of its own: the app already open is the instance answering (profile: ${profile}), and this launch's window mode (${windowLaunch.mode}) was handed to it — ${effect}. Quit that app to start a fresh instance. Exiting.`;
|
|
@@ -43394,15 +43821,15 @@ function rendererArgumentFlags(initialSession, openCatalogue, chromeMode) {
|
|
|
43394
43821
|
}
|
|
43395
43822
|
const parkedLaunches = [];
|
|
43396
43823
|
const PARKED_LAUNCH_LIMIT = 16;
|
|
43397
|
-
function parkLaunch(
|
|
43398
|
-
parkedLaunches.push({ session, request });
|
|
43824
|
+
function parkLaunch(session2, request, why = "unreachable") {
|
|
43825
|
+
parkedLaunches.push({ session: session2, request });
|
|
43399
43826
|
const parkLine = {
|
|
43400
43827
|
trigger: request.trigger,
|
|
43401
43828
|
requester: request.requester,
|
|
43402
43829
|
report: reportRaise
|
|
43403
43830
|
};
|
|
43404
|
-
if (why === "in-use") reportParkedInUse(
|
|
43405
|
-
else reportParked(
|
|
43831
|
+
if (why === "in-use") reportParkedInUse(session2, request.show, parkLine);
|
|
43832
|
+
else reportParked(session2, parkLine, request.show);
|
|
43406
43833
|
if (parkedLaunches.length <= PARKED_LAUNCH_LIMIT) return;
|
|
43407
43834
|
const evicted = parkedLaunches.shift();
|
|
43408
43835
|
if (evicted) {
|
|
@@ -43460,12 +43887,12 @@ let openOwnWindow = null;
|
|
|
43460
43887
|
let attachBrowserHostToWindow = null;
|
|
43461
43888
|
const CONVERSATION_PAINT_FALLBACK_MS = 3e3;
|
|
43462
43889
|
const heldForConversation = /* @__PURE__ */ new Map();
|
|
43463
|
-
function holdPresentUntilConversation(window,
|
|
43890
|
+
function holdPresentUntilConversation(window, session2, request) {
|
|
43464
43891
|
const timer = setTimeout(() => {
|
|
43465
43892
|
releaseHeldWindow(window.id);
|
|
43466
43893
|
}, CONVERSATION_PAINT_FALLBACK_MS);
|
|
43467
43894
|
timer.unref?.();
|
|
43468
|
-
heldForConversation.set(window.id, { session, timer, request });
|
|
43895
|
+
heldForConversation.set(window.id, { session: session2, timer, request });
|
|
43469
43896
|
}
|
|
43470
43897
|
function releaseHeldWindow(windowId) {
|
|
43471
43898
|
const held = heldForConversation.get(windowId);
|
|
@@ -43482,9 +43909,9 @@ function releaseHeldWindow(windowId) {
|
|
|
43482
43909
|
}
|
|
43483
43910
|
return true;
|
|
43484
43911
|
}
|
|
43485
|
-
function releaseHeldWindowFor(
|
|
43912
|
+
function releaseHeldWindowFor(session2) {
|
|
43486
43913
|
for (const [windowId, held] of heldForConversation) {
|
|
43487
|
-
if (held.session !==
|
|
43914
|
+
if (held.session !== session2) continue;
|
|
43488
43915
|
releaseHeldWindow(windowId);
|
|
43489
43916
|
}
|
|
43490
43917
|
}
|
|
@@ -43564,8 +43991,8 @@ if (!gotTheLock) {
|
|
|
43564
43991
|
show: request.show,
|
|
43565
43992
|
argv: commandLine
|
|
43566
43993
|
}),
|
|
43567
|
-
openConversation: openConversationInWindow ? (
|
|
43568
|
-
|
|
43994
|
+
openConversation: openConversationInWindow ? (session2, request) => openConversationInWindow?.(
|
|
43995
|
+
session2,
|
|
43569
43996
|
secondInstanceRequest(request)
|
|
43570
43997
|
) : null,
|
|
43571
43998
|
/*
|
|
@@ -43584,8 +44011,8 @@ if (!gotTheLock) {
|
|
|
43584
44011
|
// WITH it, so the flush cannot re-derive it from argv that is gone, and
|
|
43585
44012
|
// the queue holds EVERY parked request rather than the last one
|
|
43586
44013
|
// (review round 2, MAJOR-1).
|
|
43587
|
-
queue: (
|
|
43588
|
-
parkLaunch(
|
|
44014
|
+
queue: (session2, request) => {
|
|
44015
|
+
parkLaunch(session2, secondInstanceRequest(request));
|
|
43589
44016
|
},
|
|
43590
44017
|
report: reportRaise
|
|
43591
44018
|
}
|
|
@@ -43595,6 +44022,17 @@ if (!gotTheLock) {
|
|
|
43595
44022
|
}
|
|
43596
44023
|
electron.app.whenReady().then(async () => {
|
|
43597
44024
|
utils.electronApp.setAppUserModelId("com.local-operator");
|
|
44025
|
+
guardPermissions(
|
|
44026
|
+
electron.session.defaultSession,
|
|
44027
|
+
trustedRendererDocuments,
|
|
44028
|
+
(m) => logger.warn(m, LogFileType.BACKEND)
|
|
44029
|
+
);
|
|
44030
|
+
guardPreviewResponses(electron.session.defaultSession);
|
|
44031
|
+
guardDownloads(
|
|
44032
|
+
electron.session.defaultSession,
|
|
44033
|
+
trustedRendererDocuments,
|
|
44034
|
+
(m) => logger.warn(m, LogFileType.BACKEND)
|
|
44035
|
+
);
|
|
43598
44036
|
if (process.env.LOCAL_OPERATOR_UI_SMOKE_TEST === "true") {
|
|
43599
44037
|
console.log(
|
|
43600
44038
|
`LOCAL_OPERATOR_UI_READY electron=${process.versions.electron}`
|
|
@@ -43698,7 +44136,7 @@ electron.app.whenReady().then(async () => {
|
|
|
43698
44136
|
} catch {
|
|
43699
44137
|
}
|
|
43700
44138
|
});
|
|
43701
|
-
const rendererUrl =
|
|
44139
|
+
const rendererUrl = rendererDocumentUrl();
|
|
43702
44140
|
const consoleCaptureUrl = consoleCaptureUrlFor(rendererUrl);
|
|
43703
44141
|
backendService.observeDesktopFeed(
|
|
43704
44142
|
(frame) => {
|
|
@@ -43785,7 +44223,7 @@ electron.app.whenReady().then(async () => {
|
|
|
43785
44223
|
windowMode: windowLaunch.mode
|
|
43786
44224
|
});
|
|
43787
44225
|
if (hotkeysAllowed(windowLaunch.mode) || miniViewExerciser) {
|
|
43788
|
-
|
|
44226
|
+
const quickSend = createMiniView({
|
|
43789
44227
|
url: miniViewUrlFor(rendererUrl),
|
|
43790
44228
|
preloadPath: path$1.join(__dirname, "../preload/index.js"),
|
|
43791
44229
|
show: windowLaunch.show,
|
|
@@ -43807,6 +44245,23 @@ electron.app.whenReady().then(async () => {
|
|
|
43807
44245
|
}).mode,
|
|
43808
44246
|
report: reportRaise
|
|
43809
44247
|
});
|
|
44248
|
+
miniView = quickSend;
|
|
44249
|
+
guardNavigation(
|
|
44250
|
+
quickSend.window.webContents,
|
|
44251
|
+
trustedRendererDocuments,
|
|
44252
|
+
(m) => logger.warn(m, LogFileType.BACKEND)
|
|
44253
|
+
);
|
|
44254
|
+
guardWindowOpen(
|
|
44255
|
+
quickSend.window.webContents,
|
|
44256
|
+
popupVerdict,
|
|
44257
|
+
openExternalVetted,
|
|
44258
|
+
authPopupWindowOptions,
|
|
44259
|
+
(m) => logger.warn(m, LogFileType.BACKEND),
|
|
44260
|
+
(url, reason) => quickSend.window.webContents.send(EXTERNAL_OPEN_REFUSED_CHANNEL, {
|
|
44261
|
+
url,
|
|
44262
|
+
reason
|
|
44263
|
+
})
|
|
44264
|
+
);
|
|
43810
44265
|
}
|
|
43811
44266
|
if (hotkeysAllowed(windowLaunch.mode)) {
|
|
43812
44267
|
const initial = readQuickSendValue();
|
|
@@ -44017,13 +44472,7 @@ electron.app.whenReady().then(async () => {
|
|
|
44017
44472
|
}
|
|
44018
44473
|
}
|
|
44019
44474
|
);
|
|
44020
|
-
electron.ipcMain.handle("open-external", async (_, url) =>
|
|
44021
|
-
try {
|
|
44022
|
-
await electron.shell.openExternal(url);
|
|
44023
|
-
} catch (error) {
|
|
44024
|
-
console.error("Error opening URL:", error);
|
|
44025
|
-
}
|
|
44026
|
-
});
|
|
44475
|
+
electron.ipcMain.handle("open-external", async (_, url) => openExternalVetted(url));
|
|
44027
44476
|
electron.ipcMain.handle(
|
|
44028
44477
|
"show-item-in-folder",
|
|
44029
44478
|
async (_, filePath) => {
|
|
@@ -44330,7 +44779,7 @@ electron.app.whenReady().then(async () => {
|
|
|
44330
44779
|
const windowId = mainWindow.webContents.id;
|
|
44331
44780
|
claimParkedFor(
|
|
44332
44781
|
mainWindow,
|
|
44333
|
-
(
|
|
44782
|
+
(session2, request2) => openSessionInWindow(session2, request2, { fromPark: true }),
|
|
44334
44783
|
parked
|
|
44335
44784
|
);
|
|
44336
44785
|
claimParkedConsentAttention(mainWindow);
|