local-operator-ui 0.33.16 → 0.33.17

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.
Files changed (85) hide show
  1. package/out/main/index.js +490 -90
  2. package/out/preload/index.js +2087 -6
  3. package/out/renderer/assets/{_basePickBy-CGsf5s5y.js → _basePickBy-C6nvmA2O.js} +1 -1
  4. package/out/renderer/assets/{_baseUniq-C43UnLjk.js → _baseUniq-D-t5jxGe.js} +1 -1
  5. package/out/renderer/assets/{agent-details-page-BIwam1AY.js → agent-details-page-mE7vTnG6.js} +2 -2
  6. package/out/renderer/assets/{agent-hub-page-01a3MLYP.js → agent-hub-page-CaqTDoWq.js} +2 -2
  7. package/out/renderer/assets/{agents-page-CRnd_ZL5.js → agents-page-DA1i3841.js} +3 -3
  8. package/out/renderer/assets/{arc-M0pLW_S9.js → arc-DImPBqfb.js} +1 -1
  9. package/out/renderer/assets/{architectureDiagram-IEHRJDOE-BSBbZOP-.js → architectureDiagram-IEHRJDOE-f1d6V-bd.js} +1 -1
  10. package/out/renderer/assets/{blockDiagram-JOT3LUYC-B927zHwX.js → blockDiagram-JOT3LUYC-CMcoLCyx.js} +1 -1
  11. package/out/renderer/assets/{book-open-BOhsj37a.js → book-open-BHF9CFNT.js} +1 -1
  12. package/out/renderer/assets/browser-page-s-y7nb7P.js +1 -0
  13. package/out/renderer/assets/{browser-webauthn-prompt-k8f91jC5.js → browser-webauthn-prompt-DTozoEE6.js} +1 -1
  14. package/out/renderer/assets/{c4Diagram-VJAJSXHY-Db4tt7Tq.js → c4Diagram-VJAJSXHY-Ddsy3KqL.js} +1 -1
  15. package/out/renderer/assets/channel-JtmJZ6rG.js +1 -0
  16. package/out/renderer/assets/{chunk-4BMEZGHF-Esvq7q-I.js → chunk-4BMEZGHF-CDjCGQ2m.js} +1 -1
  17. package/out/renderer/assets/{chunk-A2AXSNBT-YDyKUqYa.js → chunk-A2AXSNBT-Baqc6iAL.js} +1 -1
  18. package/out/renderer/assets/{chunk-AEK57VVT-D7xfN31Q.js → chunk-AEK57VVT-CCyw5JVf.js} +1 -1
  19. package/out/renderer/assets/{chunk-D6G4REZN-B7GPJrWB.js → chunk-D6G4REZN-EvFtaW-d.js} +1 -1
  20. package/out/renderer/assets/{chunk-RZ5BOZE2-OrSabd2d.js → chunk-RZ5BOZE2-GTO1bXU_.js} +1 -1
  21. package/out/renderer/assets/{chunk-XZIHB7SX-Ls62ZuT-.js → chunk-XZIHB7SX-DoyGDc-_.js} +1 -1
  22. package/out/renderer/assets/classDiagram-GIVACNV2-BI50x_TJ.js +1 -0
  23. package/out/renderer/assets/classDiagram-v2-COTLJTTW-BI50x_TJ.js +1 -0
  24. package/out/renderer/assets/clone-XzOZppxb.js +1 -0
  25. package/out/renderer/assets/{console-mirror-D5I24PC-.js → console-mirror-a7yB-s_0.js} +1 -1
  26. package/out/renderer/assets/{consoleCapture-BkjkGzZU.js → consoleCapture-CesHMPtd.js} +1 -1
  27. package/out/renderer/assets/{conversation-input-sync-CvO1w7en.js → conversation-input-sync-B4PMPza6.js} +235 -236
  28. package/out/renderer/assets/{dagre-OKDRZEBW-BF8NMghr.js → dagre-OKDRZEBW-JuntHNlb.js} +1 -1
  29. package/out/renderer/assets/{diagram-SSKATNLV-DYPlCY6D.js → diagram-SSKATNLV-DyRsGLiH.js} +1 -1
  30. package/out/renderer/assets/{diagram-VNBRO52H-htbXA7Fo.js → diagram-VNBRO52H-B9gGTGdS.js} +1 -1
  31. package/out/renderer/assets/{erDiagram-Q7BY3M3F-4Mm_pKd-.js → erDiagram-Q7BY3M3F-B26A6UsD.js} +1 -1
  32. package/out/renderer/assets/{error-boundary-CYrSujeA.js → error-boundary-D9cFOE8L.js} +1 -1
  33. package/out/renderer/assets/{flowDiagram-4HSFHLVR-DDzUlC1i.js → flowDiagram-4HSFHLVR-CitlL0-h.js} +1 -1
  34. package/out/renderer/assets/{ganttDiagram-APWFNJXF-4vH_i6M9.js → ganttDiagram-APWFNJXF-BHkObFqC.js} +1 -1
  35. package/out/renderer/assets/{gitGraphDiagram-7IBYFJ6S-PTmOOixv.js → gitGraphDiagram-7IBYFJ6S-PmAB3iwh.js} +1 -1
  36. package/out/renderer/assets/{graph-016lbgzw.js → graph-kQDtoPNV.js} +1 -1
  37. package/out/renderer/assets/{index-DWsjHqzk.js → index-31_gb9wt.js} +153 -153
  38. package/out/renderer/assets/index-BDUcsMbE.css +1 -0
  39. package/out/renderer/assets/{index-5rWUB2pk.js → index-CoTpZA1x.js} +1 -1
  40. package/out/renderer/assets/{index-I0rfR2G9.js → index-DatO4ILn.js} +1 -1
  41. package/out/renderer/assets/{infoDiagram-PH2N3AL5-BUH-2kNx.js → infoDiagram-PH2N3AL5-Bthl21hU.js} +1 -1
  42. package/out/renderer/assets/{installer-DzJgD7dC.js → installer-0DM8a2O5.js} +1 -1
  43. package/out/renderer/assets/{journeyDiagram-U35MCT3I-B0GPzUDZ.js → journeyDiagram-U35MCT3I-CS2dhA53.js} +1 -1
  44. package/out/renderer/assets/{kanban-definition-NDS4AKOZ-DPMTM-PB.js → kanban-definition-NDS4AKOZ-BuYZJpFN.js} +1 -1
  45. package/out/renderer/assets/{layout-CZCsyyxh.js → layout-BDAgAb56.js} +1 -1
  46. package/out/renderer/assets/{legacy-agents-page-BrIHqr1U.js → legacy-agents-page-ByVaCmw5.js} +4 -4
  47. package/out/renderer/assets/{mermaid.core-DIr_Z2eU.js → mermaid.core-BNyEpqlY.js} +5 -5
  48. package/out/renderer/assets/{mesh-page-CY9vZlLm.js → mesh-page-Bpcc_Lcj.js} +1 -1
  49. package/out/renderer/assets/{mindmap-definition-ALO5MXBD-Dk9hiZoP.js → mindmap-definition-ALO5MXBD-Km_IXn-L.js} +1 -1
  50. package/out/renderer/assets/mini-CueIa1FR.js +1 -0
  51. package/out/renderer/assets/{org-surface-gate-vsaHuMo7.js → org-surface-gate-BNNGp-Zg.js} +1 -1
  52. package/out/renderer/assets/{page-header-DWAQV8z7.js → page-header-Bj15XDRT.js} +1 -1
  53. package/out/renderer/assets/{pieDiagram-IB7DONF6-CT-KDbbB.js → pieDiagram-IB7DONF6-CgQ2nspc.js} +1 -1
  54. package/out/renderer/assets/{projects-page-B-Uqww5s.js → projects-page-BiqCz3gt.js} +1 -1
  55. package/out/renderer/assets/{quadrantDiagram-7GDLP6J5-CbuvxmHL.js → quadrantDiagram-7GDLP6J5-C9VRMhAK.js} +1 -1
  56. package/out/renderer/assets/{radar-MK3ICKWK-DWgcYgI1.js → radar-MK3ICKWK-Ca5oUXiG.js} +1 -1
  57. package/out/renderer/assets/{radient-auth-buttons-BwMgaUi2.js → radient-auth-buttons-wx9BULzn.js} +1 -1
  58. package/out/renderer/assets/{requirementDiagram-KVF5MWMF-gL2w5pv1.js → requirementDiagram-KVF5MWMF-Dl3vif4l.js} +1 -1
  59. package/out/renderer/assets/{sankeyDiagram-QLVOVGJD-COy-DHKN.js → sankeyDiagram-QLVOVGJD-Dp6MvwZ8.js} +1 -1
  60. package/out/renderer/assets/{schedules-page-WYM7voi8.js → schedules-page-BlByjsrj.js} +1 -1
  61. package/out/renderer/assets/{sequenceDiagram-X6HHIX6F-D8fMPudr.js → sequenceDiagram-X6HHIX6F-DBC8EXEO.js} +1 -1
  62. package/out/renderer/assets/{settings-page-73bm6YFk.js → settings-page-BPjWIUIw.js} +7 -7
  63. package/out/renderer/assets/{settings-section-CfFkk5yW.js → settings-section-C9vsPtq9.js} +1 -1
  64. package/out/renderer/assets/{stateDiagram-DGXRK772-DJv925EE.js → stateDiagram-DGXRK772-ZaVJUkT9.js} +1 -1
  65. package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-rmZg0oCb.js +1 -0
  66. package/out/renderer/assets/{timeline-definition-BDJGKUSR-D4Mikzrt.js → timeline-definition-BDJGKUSR-D-lVRJfh.js} +1 -1
  67. package/out/renderer/assets/{ui-preferences-store-DrvWjHl4.js → ui-preferences-store-CGuje1Kj.js} +1 -1
  68. package/out/renderer/assets/{use-download-agent-mutation-EYt16YWD.js → use-download-agent-mutation-DwLETtX-.js} +2 -2
  69. package/out/renderer/assets/{use-memberships-query-CTlFzQjW.js → use-memberships-query-BO0DbD7e.js} +1 -1
  70. package/out/renderer/assets/{use-session-snapshot-JyEJO1f9.js → use-session-snapshot-CrrB5aTS.js} +1 -1
  71. package/out/renderer/assets/{xychartDiagram-VJFVF3MP-7I7MMjXI.js → xychartDiagram-VJFVF3MP-Dfu_vxvq.js} +1 -1
  72. package/out/renderer/console-capture.html +5 -5
  73. package/out/renderer/index.html +7 -7
  74. package/out/renderer/installer.html +4 -4
  75. package/out/renderer/mini.html +7 -7
  76. package/package.json +3 -2
  77. package/out/renderer/assets/browser-page-Dk6im9oj.js +0 -1
  78. package/out/renderer/assets/channel-CILm-Wsr.js +0 -1
  79. package/out/renderer/assets/classDiagram-GIVACNV2-BjqKgY46.js +0 -1
  80. package/out/renderer/assets/classDiagram-v2-COTLJTTW-BjqKgY46.js +0 -1
  81. package/out/renderer/assets/clone-Dj2cZSoa.js +0 -1
  82. package/out/renderer/assets/index-CTM-knBb.css +0 -1
  83. package/out/renderer/assets/mini-BGlRDs7P.js +0 -1
  84. package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-BL621Pie.js +0 -1
  85. /package/out/renderer/assets/{index-nSWgbpx8.js → index-WYMv0ga-.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({ op: zod.z.literal("models.catalogue"), live: zod.z.boolean().optional() }).strict(),
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(),
@@ -3106,11 +3118,13 @@ function desktopEndpoint(request) {
3106
3118
  path: `/v1/desktop/sessions/${request.sessionId}/command-entities?command=${encodeURIComponent(request.command)}${request.name ? `&name=${encodeURIComponent(request.name)}` : ""}`,
3107
3119
  method: "GET"
3108
3120
  };
3109
- case "models.catalogue":
3121
+ case "models.catalogue": {
3122
+ const scope = request.scope ? `&scope=${request.scope}` : "";
3110
3123
  return {
3111
- path: `/v1/desktop/models?live=${request.live ?? false}`,
3124
+ path: `/v1/desktop/models?live=${request.live ?? false}${scope}`,
3112
3125
  method: "GET"
3113
3126
  };
3127
+ }
3114
3128
  case "usage.get": {
3115
3129
  const query = new URLSearchParams({
3116
3130
  live: String(request.live ?? false),
@@ -3484,6 +3498,7 @@ function desktopEndpoint(request) {
3484
3498
  const MAX_PROBE_PATHS = 64;
3485
3499
  const DIRECTORY_ENTRY_LIMIT = 200;
3486
3500
  const MAX_FILE_READ_BYTES = 64 * 1024 * 1024;
3501
+ const EXTERNAL_OPEN_REFUSED_CHANNEL = "external-open-refused";
3487
3502
  const MINI_VIEW_SUMMONED = "mini-view:summoned";
3488
3503
  const MINI_VIEW_DISMISS = "mini-view:dismiss";
3489
3504
  const MINI_VIEW_REGISTRATION = "mini-view:registration";
@@ -3827,9 +3842,9 @@ function classifyRecord(record, now, liveness = recordPidLiveness(record, now))
3827
3842
  return age > HEARTBEAT_TIMEOUT_MS ? "wedged" : "live";
3828
3843
  }
3829
3844
  function recordAddress(record) {
3830
- return addressOf("http:", canonicalHost(record.host), record.port);
3845
+ return addressOf("http:", canonicalHost$1(record.host), record.port);
3831
3846
  }
3832
- function canonicalHost(host) {
3847
+ function canonicalHost$1(host) {
3833
3848
  const bare = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
3834
3849
  const unqualified = bare.endsWith(".") ? bare.slice(0, -1) : bare;
3835
3850
  const lower = unqualified.toLowerCase();
@@ -4098,7 +4113,7 @@ function normaliseAddress(url) {
4098
4113
  const port = parsed.port || (parsed.protocol === "https:" ? "443" : "80");
4099
4114
  return addressOf(
4100
4115
  parsed.protocol,
4101
- canonicalHost(parsed.hostname),
4116
+ canonicalHost$1(parsed.hostname),
4102
4117
  Number(port)
4103
4118
  );
4104
4119
  } catch {
@@ -42824,6 +42839,374 @@ function attachWindowChrome(options) {
42824
42839
  };
42825
42840
  return { sync, dispose };
42826
42841
  }
42842
+ const ALLOW = { allowed: true };
42843
+ const deny = (reason) => ({ allowed: false, reason });
42844
+ const EXTERNAL_SCHEMES = /* @__PURE__ */ new Set([
42845
+ "https:",
42846
+ "http:",
42847
+ "mailto:"
42848
+ ]);
42849
+ const MAX_EXTERNAL_URL_LENGTH = 16384;
42850
+ const IPV4_PART = /^\d{1,3}$/;
42851
+ const IPV6_MAPPED_PREFIX = /^::ffff:(.+)$/;
42852
+ const TRAILING_DOT = /\.$/;
42853
+ function isPrivateIpv4(host) {
42854
+ const parts = host.split(".");
42855
+ if (parts.length !== 4) return false;
42856
+ const octets = [];
42857
+ for (const part of parts) {
42858
+ if (!IPV4_PART.test(part)) return false;
42859
+ const value = Number(part);
42860
+ if (value > 255) return false;
42861
+ octets.push(value);
42862
+ }
42863
+ const [a, b] = octets;
42864
+ if (a === 0 || a === 127) return true;
42865
+ if (a === 10) return true;
42866
+ if (a === 172 && b >= 16 && b <= 31) return true;
42867
+ if (a === 192 && b === 168) return true;
42868
+ if (a === 169 && b === 254) return true;
42869
+ return false;
42870
+ }
42871
+ function isPrivateIpv6(host) {
42872
+ if (host === "::" || host === "::1") return true;
42873
+ const mapped = IPV6_MAPPED_PREFIX.exec(host);
42874
+ if (mapped) {
42875
+ const rest = mapped[1];
42876
+ if (rest.includes(".")) return isPrivateIpv4(rest);
42877
+ const groups = rest.split(":");
42878
+ if (groups.length === 2) {
42879
+ const hi = Number.parseInt(groups[0], 16);
42880
+ const lo = Number.parseInt(groups[1], 16);
42881
+ if (Number.isFinite(hi) && Number.isFinite(lo)) {
42882
+ return isPrivateIpv4(
42883
+ [hi >> 8, hi & 255, lo >> 8, lo & 255].join(".")
42884
+ );
42885
+ }
42886
+ }
42887
+ return true;
42888
+ }
42889
+ const first = Number.parseInt(host.split(":")[0] || "0", 16);
42890
+ if (!Number.isFinite(first)) return true;
42891
+ if ((first & 65472) === 65152) return true;
42892
+ if ((first & 65024) === 64512) return true;
42893
+ return false;
42894
+ }
42895
+ const canonicalHost = (hostname) => hostname.toLowerCase().replace(TRAILING_DOT, "");
42896
+ function isLocalOrPrivateHost(hostname) {
42897
+ const host = canonicalHost(hostname);
42898
+ if (host === "localhost" || host.endsWith(".localhost")) return true;
42899
+ if (host.startsWith("[") && host.endsWith("]"))
42900
+ return isPrivateIpv6(host.slice(1, -1));
42901
+ return isPrivateIpv4(host);
42902
+ }
42903
+ function externalUrlVerdict(raw) {
42904
+ if (typeof raw !== "string" || raw.length === 0)
42905
+ return { allowed: false, reason: "not a string" };
42906
+ if (raw.length > MAX_EXTERNAL_URL_LENGTH)
42907
+ return { allowed: false, reason: "too long" };
42908
+ let url;
42909
+ try {
42910
+ url = new URL(raw);
42911
+ } catch {
42912
+ return { allowed: false, reason: "not a URL" };
42913
+ }
42914
+ if (!EXTERNAL_SCHEMES.has(url.protocol))
42915
+ return {
42916
+ allowed: false,
42917
+ reason: `scheme ${url.protocol} is not handed out`
42918
+ };
42919
+ if (url.protocol !== "mailto:") {
42920
+ if (url.username || url.password)
42921
+ return { allowed: false, reason: "URL carries credentials" };
42922
+ if (!url.hostname) return { allowed: false, reason: "URL has no host" };
42923
+ if (isLocalOrPrivateHost(url.hostname))
42924
+ return {
42925
+ allowed: false,
42926
+ reason: "URL names a loopback or private-network host"
42927
+ };
42928
+ } else if (/(^|[?&])attach(ment)?=/i.test(url.search)) {
42929
+ return { allowed: false, reason: "mailto: names an attachment" };
42930
+ }
42931
+ return { allowed: true, url: url.href };
42932
+ }
42933
+ async function openVettedExternal(raw, open2, log2) {
42934
+ const verdict = externalUrlVerdict(raw);
42935
+ if (!verdict.allowed) {
42936
+ log2(`[window-guard] refused to open externally: ${verdict.reason}`);
42937
+ return { ok: false, reason: verdict.reason };
42938
+ }
42939
+ try {
42940
+ await open2(verdict.url);
42941
+ return { ok: true };
42942
+ } catch (error) {
42943
+ const reason = `the OS could not open the URL: ${error instanceof Error ? error.message : String(error)}`;
42944
+ log2(`[window-guard] ${reason}`);
42945
+ return { ok: false, reason };
42946
+ }
42947
+ }
42948
+ const AUTH_HOSTS = [
42949
+ "accounts.google.com",
42950
+ "oauth.googleusercontent.com",
42951
+ "content.googleapis.com",
42952
+ "ssl.gstatic.com",
42953
+ "login.microsoftonline.com",
42954
+ "microsoftonline.com",
42955
+ "login.live.com",
42956
+ "login.windows.net",
42957
+ "login.microsoft.com"
42958
+ ];
42959
+ const AUTH_SCHEMES = /* @__PURE__ */ new Set([
42960
+ "storagerelay:",
42961
+ "msauth:",
42962
+ "msftauth:"
42963
+ ]);
42964
+ function isAuthPopupUrl(url) {
42965
+ if (url.protocol !== "https:") return false;
42966
+ if (url.username || url.password) return false;
42967
+ if (url.port !== "") return false;
42968
+ const host = url.hostname.toLowerCase();
42969
+ return AUTH_HOSTS.some(
42970
+ (domain) => host === domain || host.endsWith(`.${domain}`)
42971
+ );
42972
+ }
42973
+ function popupVerdict(raw) {
42974
+ if (raw === "about:blank")
42975
+ return {
42976
+ action: "deny",
42977
+ reason: "about:blank is refused as a popup start"
42978
+ };
42979
+ let url = null;
42980
+ try {
42981
+ url = new URL(raw);
42982
+ } catch {
42983
+ }
42984
+ if (url && (isAuthPopupUrl(url) || AUTH_SCHEMES.has(url.protocol)))
42985
+ return { action: "auth" };
42986
+ const external = externalUrlVerdict(raw);
42987
+ return external.allowed ? { action: "external", url: external.url } : { action: "deny", reason: external.reason };
42988
+ }
42989
+ function popupNavigationVerdict(rawUrl) {
42990
+ if (rawUrl === "about:blank") return ALLOW;
42991
+ let url;
42992
+ try {
42993
+ url = new URL(rawUrl);
42994
+ } catch {
42995
+ return deny("not a URL");
42996
+ }
42997
+ if (isAuthPopupUrl(url) || AUTH_SCHEMES.has(url.protocol)) return ALLOW;
42998
+ return deny(`the sign-in window may not navigate to ${url.protocol}`);
42999
+ }
43000
+ const STATIC_FRAME_PREFIX = "/v1/static/";
43001
+ function frameNavigationVerdict(input, trustedRendererUrls) {
43002
+ let url;
43003
+ try {
43004
+ url = new URL(input.url);
43005
+ } catch {
43006
+ return deny("not a URL");
43007
+ }
43008
+ if (input.isMainFrame) {
43009
+ return trustedRendererUrls.some(
43010
+ (trusted) => trustedDesktopFrame(input.url, trusted)
43011
+ ) ? ALLOW : deny("the main window may only show the app's own document");
43012
+ }
43013
+ if (url.protocol === "blob:") {
43014
+ let minted;
43015
+ try {
43016
+ minted = new URL(url.pathname);
43017
+ } catch {
43018
+ return deny("blob: URL names no origin");
43019
+ }
43020
+ const own = trustedRendererUrls.some((trusted) => {
43021
+ try {
43022
+ const t = new URL(trusted);
43023
+ return t.protocol === "file:" ? minted.protocol === "file:" : minted.origin === t.origin;
43024
+ } catch {
43025
+ return false;
43026
+ }
43027
+ });
43028
+ return own ? ALLOW : deny("blob: minted by a document that is not the app");
43029
+ }
43030
+ if ((url.protocol === "http:" || url.protocol === "https:") && isLocalOrPrivateHost(url.hostname) && url.pathname.startsWith(STATIC_FRAME_PREFIX))
43031
+ return ALLOW;
43032
+ return deny(`a child frame may not navigate to ${url.protocol}`);
43033
+ }
43034
+ function permissionVerdict(query, trustedRendererUrls) {
43035
+ if (!query.isMainFrame) return false;
43036
+ if (!trustedRendererUrls.some(
43037
+ (trusted) => trustedDesktopFrame(query.requestingUrl, trusted)
43038
+ ))
43039
+ return false;
43040
+ if (query.permission === "clipboard-sanitized-write" || query.permission === "fullscreen")
43041
+ return true;
43042
+ if (query.permission !== "media") return false;
43043
+ if (query.mediaTypes)
43044
+ return query.mediaTypes.length > 0 && query.mediaTypes.every((type2) => type2 === "audio");
43045
+ return query.mediaType === "audio";
43046
+ }
43047
+ function decodedPathname(pathname) {
43048
+ let current = pathname;
43049
+ for (let round = 0; round < 3; round += 1) {
43050
+ let next;
43051
+ try {
43052
+ next = decodeURIComponent(current);
43053
+ } catch {
43054
+ break;
43055
+ }
43056
+ if (next === current) break;
43057
+ current = next;
43058
+ }
43059
+ return current;
43060
+ }
43061
+ function isStaticServeRequest(rawUrl) {
43062
+ try {
43063
+ const url = new URL(rawUrl);
43064
+ if (url.protocol !== "http:" && url.protocol !== "https:") return false;
43065
+ if (!isLocalOrPrivateHost(url.hostname)) return false;
43066
+ return decodedPathname(url.pathname).toLowerCase().startsWith(STATIC_FRAME_PREFIX);
43067
+ } catch {
43068
+ return false;
43069
+ }
43070
+ }
43071
+ const PREVIEW_CSP = [
43072
+ "default-src 'none'",
43073
+ "script-src 'unsafe-inline' 'unsafe-eval' https:",
43074
+ "style-src 'unsafe-inline' https:",
43075
+ "img-src data: blob: https:",
43076
+ "font-src data: https:",
43077
+ "media-src data: blob: https:",
43078
+ "connect-src https:",
43079
+ "worker-src blob:",
43080
+ "form-action 'none'",
43081
+ "base-uri 'none'"
43082
+ ].join("; ");
43083
+ function withPreviewPolicy(headers) {
43084
+ const out = { ...headers };
43085
+ const existing = (name) => Object.keys(out).find((key) => key.toLowerCase() === name);
43086
+ const cspKey = existing("content-security-policy");
43087
+ if (cspKey) out[cspKey] = [...out[cspKey], PREVIEW_CSP];
43088
+ else out["Content-Security-Policy"] = [PREVIEW_CSP];
43089
+ if (!existing("x-content-type-options"))
43090
+ out["X-Content-Type-Options"] = ["nosniff"];
43091
+ return out;
43092
+ }
43093
+ function downloadVerdict(itemUrl, initiatorUrl, trustedRendererUrls) {
43094
+ if (!itemUrl.startsWith("blob:"))
43095
+ return deny("only the app's own blob: exports may write files");
43096
+ if (!trustedRendererUrls.some(
43097
+ (trusted) => trustedDesktopFrame(initiatorUrl, trusted)
43098
+ ))
43099
+ return deny("the download was not started by one of the app's documents");
43100
+ return ALLOW;
43101
+ }
43102
+ function installNavigationVerdict(contents, decide, log2) {
43103
+ const onNavigation = (event) => {
43104
+ if (event.isSameDocument) return;
43105
+ const verdict = decide(event.url, event.isMainFrame);
43106
+ if (verdict.allowed) return;
43107
+ event.preventDefault();
43108
+ log2(
43109
+ `[window-guard] blocked ${event.isMainFrame ? "main-frame" : "child-frame"} navigation to ${event.url.slice(0, 200)}: ${verdict.reason}`
43110
+ );
43111
+ };
43112
+ contents.on("will-frame-navigate", onNavigation);
43113
+ contents.on("will-redirect", onNavigation);
43114
+ }
43115
+ function guardNavigation(contents, trustedUrls, log2) {
43116
+ installNavigationVerdict(
43117
+ contents,
43118
+ (url, isMainFrame) => frameNavigationVerdict({ url, isMainFrame }, trustedUrls()),
43119
+ log2
43120
+ );
43121
+ }
43122
+ function guardWindowOpen(contents, decide, openExternal, authWindowOptions, log2, notifyRefused) {
43123
+ contents.setWindowOpenHandler((details) => {
43124
+ const verdict = decide(details.url);
43125
+ if (verdict.action === "auth") {
43126
+ return {
43127
+ action: "allow",
43128
+ overrideBrowserWindowOptions: authWindowOptions
43129
+ };
43130
+ }
43131
+ if (verdict.action === "external") {
43132
+ void openExternal(verdict.url).then((outcome) => {
43133
+ if (!outcome.ok) notifyRefused?.(verdict.url, outcome.reason);
43134
+ });
43135
+ } else {
43136
+ log2(`[window-guard] denied window.open: ${verdict.reason}`);
43137
+ notifyRefused?.(details.url, verdict.reason);
43138
+ }
43139
+ return { action: "deny" };
43140
+ });
43141
+ contents.on("did-create-window", (window) => {
43142
+ guardPopupNavigation(window.webContents, log2);
43143
+ });
43144
+ }
43145
+ function guardPopupNavigation(contents, log2) {
43146
+ installNavigationVerdict(contents, (url) => popupNavigationVerdict(url), log2);
43147
+ contents.setWindowOpenHandler(() => {
43148
+ log2("[window-guard] denied window.open from the sign-in popup");
43149
+ return { action: "deny" };
43150
+ });
43151
+ }
43152
+ function guardPermissions(ses, trustedUrls, log2) {
43153
+ ses.setPermissionRequestHandler(
43154
+ (_contents, permission, callback, details) => {
43155
+ const granted = permissionVerdict(
43156
+ {
43157
+ permission,
43158
+ mediaTypes: "mediaTypes" in details ? details.mediaTypes ?? [] : void 0,
43159
+ isMainFrame: details.isMainFrame,
43160
+ requestingUrl: details.requestingUrl
43161
+ },
43162
+ trustedUrls()
43163
+ );
43164
+ if (!granted)
43165
+ log2(
43166
+ `[window-guard] denied permission request ${permission} from ${details.requestingUrl.slice(0, 200)}`
43167
+ );
43168
+ callback(granted);
43169
+ }
43170
+ );
43171
+ ses.setPermissionCheckHandler(
43172
+ (_contents, permission, _origin, details) => permissionVerdict(
43173
+ {
43174
+ permission,
43175
+ mediaType: details.mediaType,
43176
+ isMainFrame: details.isMainFrame,
43177
+ requestingUrl: details.requestingUrl ?? ""
43178
+ },
43179
+ trustedUrls()
43180
+ )
43181
+ );
43182
+ }
43183
+ function guardPreviewResponses(ses) {
43184
+ ses.webRequest.onHeadersReceived((details, callback) => {
43185
+ if (!isStaticServeRequest(details.url)) {
43186
+ callback({});
43187
+ return;
43188
+ }
43189
+ callback({
43190
+ responseHeaders: withPreviewPolicy(
43191
+ details.responseHeaders ?? {}
43192
+ )
43193
+ });
43194
+ });
43195
+ }
43196
+ function guardDownloads(ses, trustedUrls, log2) {
43197
+ ses.on("will-download", (event, item, webContents) => {
43198
+ const verdict = downloadVerdict(
43199
+ item.getURL(),
43200
+ webContents?.getURL() ?? "",
43201
+ trustedUrls()
43202
+ );
43203
+ if (verdict.allowed) return;
43204
+ event.preventDefault();
43205
+ log2(
43206
+ `[window-guard] blocked a download from ${item.getURL().slice(0, 200)}: ${verdict.reason}`
43207
+ );
43208
+ });
43209
+ }
42827
43210
  const BASE64_FILE_EXTENSIONS = ["csv", "tsv", "xls", "xlsx", "ods"];
42828
43211
  const resolveUserPath = (filePath, cwd) => resolveUserPath$1(filePath, cwd, electron.app.getPath("home"));
42829
43212
  const windowLaunch = resolveWindowLaunchPlan({
@@ -43098,6 +43481,45 @@ const ownLaunchRequest = () => ({
43098
43481
  show: windowLaunch.show,
43099
43482
  trigger: "initial-present"
43100
43483
  });
43484
+ const rendererDocumentUrl = () => process.env.ELECTRON_RENDERER_URL || node_url.pathToFileURL(path$1.join(__dirname, "../renderer/index.html")).href;
43485
+ const trustedRendererDocuments = () => [
43486
+ rendererDocumentUrl(),
43487
+ miniViewUrlFor(rendererDocumentUrl())
43488
+ ];
43489
+ const openExternalVetted = (raw) => openVettedExternal(
43490
+ raw,
43491
+ (url) => electron.shell.openExternal(url),
43492
+ (message) => logger.warn(message, LogFileType.BACKEND)
43493
+ );
43494
+ const authPopupWindowOptions = {
43495
+ width: 800,
43496
+ height: 700,
43497
+ // Increased height for better visibility
43498
+ minWidth: 600,
43499
+ minHeight: 500,
43500
+ center: true,
43501
+ frame: true,
43502
+ autoHideMenuBar: false,
43503
+ backgroundColor: "#FFFFFF",
43504
+ webPreferences: {
43505
+ contextIsolation: true,
43506
+ nodeIntegration: false,
43507
+ webSecurity: true,
43508
+ allowRunningInsecureContent: false,
43509
+ sandbox: true,
43510
+ // Enable sandbox for additional security
43511
+ // Disable various features that aren't needed for auth
43512
+ enableWebSQL: false,
43513
+ navigateOnDragDrop: false,
43514
+ spellcheck: false,
43515
+ /*
43516
+ * A JS dialog in a popup must not be able to wedge it (round-2 S-7): a
43517
+ * renderer-reached `confirm()`/`alert()` needs no permission and blocks
43518
+ * every later evaluation on the page behind its modal.
43519
+ */
43520
+ disableDialogs: true
43521
+ }
43522
+ };
43101
43523
  function createWindow(initialSession = null, openCatalogue = false, request = ownLaunchRequest()) {
43102
43524
  const chrome = resolveLaunchWindowChrome({
43103
43525
  argv: process.argv,
@@ -43242,66 +43664,22 @@ function createWindow(initialSession = null, openCatalogue = false, request = ow
43242
43664
  );
43243
43665
  }, 1500);
43244
43666
  });
43245
- mainWindow2.webContents.setWindowOpenHandler((details) => {
43246
- const url = new URL(details.url);
43247
- const trustedAuthDomains = [
43248
- // Google auth domains
43249
- "accounts.google.com",
43250
- "oauth.googleusercontent.com",
43251
- "content.googleapis.com",
43252
- "ssl.gstatic.com",
43253
- // Microsoft auth domains
43254
- "login.microsoftonline.com",
43255
- "login.live.com",
43256
- "login.windows.net",
43257
- "login.microsoft.com",
43258
- "microsoftonline.com",
43259
- "msauth",
43260
- "msftauth",
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
- });
43667
+ guardNavigation(
43668
+ mainWindow2.webContents,
43669
+ trustedRendererDocuments,
43670
+ (m) => logger.warn(m, LogFileType.BACKEND)
43671
+ );
43672
+ guardWindowOpen(
43673
+ mainWindow2.webContents,
43674
+ popupVerdict,
43675
+ openExternalVetted,
43676
+ authPopupWindowOptions,
43677
+ (m) => logger.warn(m, LogFileType.BACKEND),
43678
+ (url, reason) => mainWindow2.webContents.send(EXTERNAL_OPEN_REFUSED_CHANNEL, {
43679
+ url,
43680
+ reason
43681
+ })
43682
+ );
43305
43683
  if (utils.is.dev && process.env.ELECTRON_RENDERER_URL) {
43306
43684
  mainWindow2.loadURL(process.env.ELECTRON_RENDERER_URL);
43307
43685
  } else {
@@ -43340,8 +43718,8 @@ const reportBackendFailure = (message, fileType) => {
43340
43718
  const reportRaise = (line) => {
43341
43719
  logger.info(`[window-raise] ${line}`, LogFileType.BACKEND);
43342
43720
  };
43343
- function describeForwardedLaunch(profile, session) {
43344
- const named = session === null ? null : `the conversation it named (${session})`;
43721
+ function describeForwardedLaunch(profile, session2) {
43722
+ const named = session2 === null ? null : `the conversation it named (${session2})`;
43345
43723
  const handed = named === null ? "it named no conversation" : `${named} rides with it`;
43346
43724
  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
43725
  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 +43772,15 @@ function rendererArgumentFlags(initialSession, openCatalogue, chromeMode) {
43394
43772
  }
43395
43773
  const parkedLaunches = [];
43396
43774
  const PARKED_LAUNCH_LIMIT = 16;
43397
- function parkLaunch(session, request, why = "unreachable") {
43398
- parkedLaunches.push({ session, request });
43775
+ function parkLaunch(session2, request, why = "unreachable") {
43776
+ parkedLaunches.push({ session: session2, request });
43399
43777
  const parkLine = {
43400
43778
  trigger: request.trigger,
43401
43779
  requester: request.requester,
43402
43780
  report: reportRaise
43403
43781
  };
43404
- if (why === "in-use") reportParkedInUse(session, request.show, parkLine);
43405
- else reportParked(session, parkLine, request.show);
43782
+ if (why === "in-use") reportParkedInUse(session2, request.show, parkLine);
43783
+ else reportParked(session2, parkLine, request.show);
43406
43784
  if (parkedLaunches.length <= PARKED_LAUNCH_LIMIT) return;
43407
43785
  const evicted = parkedLaunches.shift();
43408
43786
  if (evicted) {
@@ -43460,12 +43838,12 @@ let openOwnWindow = null;
43460
43838
  let attachBrowserHostToWindow = null;
43461
43839
  const CONVERSATION_PAINT_FALLBACK_MS = 3e3;
43462
43840
  const heldForConversation = /* @__PURE__ */ new Map();
43463
- function holdPresentUntilConversation(window, session, request) {
43841
+ function holdPresentUntilConversation(window, session2, request) {
43464
43842
  const timer = setTimeout(() => {
43465
43843
  releaseHeldWindow(window.id);
43466
43844
  }, CONVERSATION_PAINT_FALLBACK_MS);
43467
43845
  timer.unref?.();
43468
- heldForConversation.set(window.id, { session, timer, request });
43846
+ heldForConversation.set(window.id, { session: session2, timer, request });
43469
43847
  }
43470
43848
  function releaseHeldWindow(windowId) {
43471
43849
  const held = heldForConversation.get(windowId);
@@ -43482,9 +43860,9 @@ function releaseHeldWindow(windowId) {
43482
43860
  }
43483
43861
  return true;
43484
43862
  }
43485
- function releaseHeldWindowFor(session) {
43863
+ function releaseHeldWindowFor(session2) {
43486
43864
  for (const [windowId, held] of heldForConversation) {
43487
- if (held.session !== session) continue;
43865
+ if (held.session !== session2) continue;
43488
43866
  releaseHeldWindow(windowId);
43489
43867
  }
43490
43868
  }
@@ -43564,8 +43942,8 @@ if (!gotTheLock) {
43564
43942
  show: request.show,
43565
43943
  argv: commandLine
43566
43944
  }),
43567
- openConversation: openConversationInWindow ? (session, request) => openConversationInWindow?.(
43568
- session,
43945
+ openConversation: openConversationInWindow ? (session2, request) => openConversationInWindow?.(
43946
+ session2,
43569
43947
  secondInstanceRequest(request)
43570
43948
  ) : null,
43571
43949
  /*
@@ -43584,8 +43962,8 @@ if (!gotTheLock) {
43584
43962
  // WITH it, so the flush cannot re-derive it from argv that is gone, and
43585
43963
  // the queue holds EVERY parked request rather than the last one
43586
43964
  // (review round 2, MAJOR-1).
43587
- queue: (session, request) => {
43588
- parkLaunch(session, secondInstanceRequest(request));
43965
+ queue: (session2, request) => {
43966
+ parkLaunch(session2, secondInstanceRequest(request));
43589
43967
  },
43590
43968
  report: reportRaise
43591
43969
  }
@@ -43595,6 +43973,17 @@ if (!gotTheLock) {
43595
43973
  }
43596
43974
  electron.app.whenReady().then(async () => {
43597
43975
  utils.electronApp.setAppUserModelId("com.local-operator");
43976
+ guardPermissions(
43977
+ electron.session.defaultSession,
43978
+ trustedRendererDocuments,
43979
+ (m) => logger.warn(m, LogFileType.BACKEND)
43980
+ );
43981
+ guardPreviewResponses(electron.session.defaultSession);
43982
+ guardDownloads(
43983
+ electron.session.defaultSession,
43984
+ trustedRendererDocuments,
43985
+ (m) => logger.warn(m, LogFileType.BACKEND)
43986
+ );
43598
43987
  if (process.env.LOCAL_OPERATOR_UI_SMOKE_TEST === "true") {
43599
43988
  console.log(
43600
43989
  `LOCAL_OPERATOR_UI_READY electron=${process.versions.electron}`
@@ -43698,7 +44087,7 @@ electron.app.whenReady().then(async () => {
43698
44087
  } catch {
43699
44088
  }
43700
44089
  });
43701
- const rendererUrl = process.env.ELECTRON_RENDERER_URL || node_url.pathToFileURL(path$1.join(__dirname, "../renderer/index.html")).href;
44090
+ const rendererUrl = rendererDocumentUrl();
43702
44091
  const consoleCaptureUrl = consoleCaptureUrlFor(rendererUrl);
43703
44092
  backendService.observeDesktopFeed(
43704
44093
  (frame) => {
@@ -43785,7 +44174,7 @@ electron.app.whenReady().then(async () => {
43785
44174
  windowMode: windowLaunch.mode
43786
44175
  });
43787
44176
  if (hotkeysAllowed(windowLaunch.mode) || miniViewExerciser) {
43788
- miniView = createMiniView({
44177
+ const quickSend = createMiniView({
43789
44178
  url: miniViewUrlFor(rendererUrl),
43790
44179
  preloadPath: path$1.join(__dirname, "../preload/index.js"),
43791
44180
  show: windowLaunch.show,
@@ -43807,6 +44196,23 @@ electron.app.whenReady().then(async () => {
43807
44196
  }).mode,
43808
44197
  report: reportRaise
43809
44198
  });
44199
+ miniView = quickSend;
44200
+ guardNavigation(
44201
+ quickSend.window.webContents,
44202
+ trustedRendererDocuments,
44203
+ (m) => logger.warn(m, LogFileType.BACKEND)
44204
+ );
44205
+ guardWindowOpen(
44206
+ quickSend.window.webContents,
44207
+ popupVerdict,
44208
+ openExternalVetted,
44209
+ authPopupWindowOptions,
44210
+ (m) => logger.warn(m, LogFileType.BACKEND),
44211
+ (url, reason) => quickSend.window.webContents.send(EXTERNAL_OPEN_REFUSED_CHANNEL, {
44212
+ url,
44213
+ reason
44214
+ })
44215
+ );
43810
44216
  }
43811
44217
  if (hotkeysAllowed(windowLaunch.mode)) {
43812
44218
  const initial = readQuickSendValue();
@@ -44017,13 +44423,7 @@ electron.app.whenReady().then(async () => {
44017
44423
  }
44018
44424
  }
44019
44425
  );
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
- });
44426
+ electron.ipcMain.handle("open-external", async (_, url) => openExternalVetted(url));
44027
44427
  electron.ipcMain.handle(
44028
44428
  "show-item-in-folder",
44029
44429
  async (_, filePath) => {
@@ -44330,7 +44730,7 @@ electron.app.whenReady().then(async () => {
44330
44730
  const windowId = mainWindow.webContents.id;
44331
44731
  claimParkedFor(
44332
44732
  mainWindow,
44333
- (session, request2) => openSessionInWindow(session, request2, { fromPark: true }),
44733
+ (session2, request2) => openSessionInWindow(session2, request2, { fromPark: true }),
44334
44734
  parked
44335
44735
  );
44336
44736
  claimParkedConsentAttention(mainWindow);