@trops/dash-core 0.1.509 → 0.1.510

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.
@@ -3,6 +3,7 @@
3
3
  var require$$0$1 = require('electron');
4
4
  var require$$1$1 = require('path');
5
5
  var require$$0$2 = require('fs');
6
+ var require$$0$3 = require('crypto');
6
7
  var require$$9$1 = require('objects-to-csv');
7
8
  var require$$1$2 = require('readline');
8
9
  var require$$2 = require('xtreamer');
@@ -10,30 +11,29 @@ var require$$3$1 = require('xml2js');
10
11
  var require$$4 = require('JSONStream');
11
12
  var require$$5 = require('stream');
12
13
  var require$$6 = require('csv-parser');
13
- var require$$0$3 = require('quickjs-emscripten');
14
+ var require$$0$4 = require('quickjs-emscripten');
14
15
  var require$$11 = require('https');
15
- var require$$0$5 = require('@modelcontextprotocol/sdk/client/index.js');
16
+ var require$$0$6 = require('@modelcontextprotocol/sdk/client/index.js');
16
17
  var require$$1$3 = require('@modelcontextprotocol/sdk/client/stdio.js');
17
- var require$$0$4 = require('pkce-challenge');
18
+ var require$$0$5 = require('pkce-challenge');
18
19
  var require$$2$1 = require('os');
19
20
  var require$$12 = require('child_process');
20
- var require$$0$6 = require('electron-store');
21
+ var require$$0$7 = require('electron-store');
21
22
  var require$$3$2 = require('adm-zip');
22
23
  var require$$4$1 = require('url');
23
24
  var require$$2$2 = require('vm');
24
25
  var require$$1$4 = require('croner');
25
26
  var require$$2$3 = require('algoliasearch');
26
27
  var require$$3$3 = require('node:path');
27
- var require$$0$7 = require('openai');
28
- var require$$0$a = require('@anthropic-ai/sdk');
29
- var require$$3$4 = require('crypto');
28
+ var require$$0$8 = require('openai');
29
+ var require$$0$b = require('@anthropic-ai/sdk');
30
30
  var require$$8$1 = require('zod');
31
- var require$$0$8 = require('http');
31
+ var require$$0$9 = require('http');
32
32
  var require$$1$5 = require('http2');
33
33
  var require$$2$4 = require('node-forge');
34
- var require$$0$9 = require('css');
34
+ var require$$0$a = require('css');
35
35
  var require$$1$6 = require('node-vibrant/node');
36
- var require$$3$5 = require('ws');
36
+ var require$$3$4 = require('ws');
37
37
 
38
38
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
39
39
 
@@ -2083,7 +2083,7 @@ var grantedPermissions = {
2083
2083
  * _resetForTest() → void (test-only)
2084
2084
  */
2085
2085
 
2086
- const { BrowserWindow: BrowserWindow$3, ipcMain: ipcMain$2 } = require$$0$1;
2086
+ const { BrowserWindow: BrowserWindow$3, ipcMain: ipcMain$3 } = require$$0$1;
2087
2087
 
2088
2088
  const REQUEST_CHANNEL = "widget:permission-required";
2089
2089
  const RESPONSE_CHANNEL = "widget:permission-response";
@@ -2248,8 +2248,8 @@ let _handlersRegistered = false;
2248
2248
  */
2249
2249
  function setupJitConsentHandlers() {
2250
2250
  if (_handlersRegistered) return;
2251
- if (!ipcMain$2 || typeof ipcMain$2.on !== "function") return;
2252
- ipcMain$2.on(RESPONSE_CHANNEL, (_event, payload) => {
2251
+ if (!ipcMain$3 || typeof ipcMain$3.on !== "function") return;
2252
+ ipcMain$3.on(RESPONSE_CHANNEL, (_event, payload) => {
2253
2253
  _handleResponse(payload);
2254
2254
  });
2255
2255
  _handlersRegistered = true;
@@ -2265,6 +2265,93 @@ var jitConsent$1 = {
2265
2265
  DEFAULT_TIMEOUT_MS,
2266
2266
  };
2267
2267
 
2268
+ /**
2269
+ * mountTokenRegistry.js
2270
+ *
2271
+ * Trusted root of widget identity at the IPC boundary. Each time
2272
+ * `WidgetFactory` mounts a widget it calls
2273
+ * `framework:register-widget-mount`, which calls `register(widgetId)`
2274
+ * here. The returned token is baked into the widget's bound API
2275
+ * (`makeBoundApi`) and sent on every gated IPC call. Gates resolve the
2276
+ * widgetId via `lookup(token)` instead of trusting whatever the
2277
+ * renderer claims.
2278
+ *
2279
+ * Why server-generated tokens: the renderer-supplied widgetId path was
2280
+ * the original consent-bypass surface. Tokens are produced by
2281
+ * `crypto.randomBytes(24)` here (192 bits) and are NEVER accepted from
2282
+ * the renderer — the renderer can only present tokens it received from
2283
+ * a prior `register` call. A widget cannot fabricate a token for
2284
+ * another widgetId.
2285
+ *
2286
+ * Limit: in single-renderer (one BrowserWindow shared across all
2287
+ * widgets), a malicious widget can still walk the React fiber tree to
2288
+ * find another widget's bound API and call its functions — the bound
2289
+ * function fires IPC with the *victim's* token. Fully closing that
2290
+ * residual requires per-widget BrowserView (multi-week refactor). The
2291
+ * token model raises the bar from "type a widgetId string" to "walk
2292
+ * the fiber tree and call another widget's bound function," which is
2293
+ * a deliberate malicious step that's visible at install-time review.
2294
+ */
2295
+
2296
+ const crypto$1 = require$$0$3;
2297
+
2298
+ const _byToken = new Map(); // token → widgetId
2299
+
2300
+ function _generateToken() {
2301
+ // 24 random bytes = 48 hex chars = 192 bits of entropy. More than
2302
+ // enough; collision probability is negligible.
2303
+ return crypto$1.randomBytes(24).toString("hex");
2304
+ }
2305
+
2306
+ /**
2307
+ * Register a widgetId mount and return a fresh token bound to it.
2308
+ * @param {string} widgetId
2309
+ * @returns {string} token
2310
+ */
2311
+ function register$1(widgetId) {
2312
+ if (typeof widgetId !== "string" || widgetId.length === 0) {
2313
+ throw new Error(
2314
+ "mountTokenRegistry.register: widgetId must be a non-empty string",
2315
+ );
2316
+ }
2317
+ let token = _generateToken();
2318
+ // Defensive — collision is astronomically unlikely but if it did
2319
+ // happen we'd silently overwrite a valid mapping. Regenerate.
2320
+ while (_byToken.has(token)) {
2321
+ token = _generateToken();
2322
+ }
2323
+ _byToken.set(token, widgetId);
2324
+ return token;
2325
+ }
2326
+
2327
+ /**
2328
+ * Resolve a token to the widgetId it was bound to.
2329
+ * @param {string} token
2330
+ * @returns {string|null}
2331
+ */
2332
+ function lookup(token) {
2333
+ if (typeof token !== "string") return null;
2334
+ return _byToken.has(token) ? _byToken.get(token) : null;
2335
+ }
2336
+
2337
+ /**
2338
+ * Drop a token. Silent no-op for unknown tokens or non-strings —
2339
+ * unregister is called from unmount cleanup paths and should never
2340
+ * throw.
2341
+ * @param {string} token
2342
+ */
2343
+ function unregister$1(token) {
2344
+ if (typeof token !== "string") return;
2345
+ _byToken.delete(token);
2346
+ }
2347
+
2348
+ /** Test-only — clears the registry between cases. */
2349
+ function _resetForTests() {
2350
+ _byToken.clear();
2351
+ }
2352
+
2353
+ var mountTokenRegistry = { register: register$1, lookup, unregister: unregister$1, _resetForTests };
2354
+
2268
2355
  /**
2269
2356
  * fsGate.js
2270
2357
  *
@@ -2302,6 +2389,25 @@ var jitConsent$1 = {
2302
2389
 
2303
2390
  const { getGrant: getGrant$3, setGrant: setGrant$3 } = grantedPermissions;
2304
2391
  const { requestApproval: requestApproval$2 } = jitConsent$1;
2392
+ const { lookup: lookupMountToken$2 } = mountTokenRegistry;
2393
+
2394
+ // If a token is supplied, the gate resolves widgetId via the mount
2395
+ // registry and ignores any renderer-supplied widgetId. Tokens are
2396
+ // server-generated and bound to a widgetId at WidgetFactory mount —
2397
+ // the renderer cannot fabricate a token for another widget. See
2398
+ // `mountTokenRegistry.js`.
2399
+ //
2400
+ // Legacy callers without a token fall through to the existing
2401
+ // widgetId-based path (Slice 1 keeps this for back-compat; Slice 2
2402
+ // flips to deny).
2403
+ function _resolveIdentity$2({ token, widgetId }) {
2404
+ if (typeof token === "string" && token.length > 0) {
2405
+ const resolved = lookupMountToken$2(token);
2406
+ if (resolved) return { widgetId: resolved, source: "token" };
2407
+ return { widgetId: null, source: "token-unknown" };
2408
+ }
2409
+ return { widgetId: widgetId || null, source: "legacy" };
2410
+ }
2305
2411
 
2306
2412
  // Action names treated as writes. Anything not in this set is a read.
2307
2413
  // Conservative — when in doubt, classify as a read so write-protected
@@ -2336,7 +2442,15 @@ function _filenameMatches(filename, allowedList) {
2336
2442
  * Synchronous gate evaluation.
2337
2443
  * @returns {{ allow: true } | { allow: false, reason: string }}
2338
2444
  */
2339
- function gateFsCall$1({ widgetId, action, args }) {
2445
+ function gateFsCall$1({ widgetId, token, action, args }) {
2446
+ const resolved = _resolveIdentity$2({ token, widgetId });
2447
+ if (resolved.source === "token-unknown") {
2448
+ return {
2449
+ allow: false,
2450
+ reason: "fs gate: unknown mount token; widget identity not verifiable",
2451
+ };
2452
+ }
2453
+ widgetId = resolved.widgetId;
2340
2454
  if (!widgetId) {
2341
2455
  return {
2342
2456
  allow: false,
@@ -2460,11 +2574,21 @@ async function gateFsCallWithJit$1(req, opts = {}) {
2460
2574
  if (!opts.enableJit) return initial;
2461
2575
  if (!_isNoGrantDenial$2(initial.reason)) return initial;
2462
2576
 
2577
+ // Resolve verified identity once (token wins over claimed widgetId).
2578
+ // Re-using the same resolution as gateFsCall keeps the JIT prompt
2579
+ // and grant write tied to the same identity the gate just denied.
2580
+ const resolved = _resolveIdentity$2({
2581
+ token: req.token,
2582
+ widgetId: req.widgetId,
2583
+ });
2584
+ const verifiedWidgetId = resolved.widgetId;
2585
+ if (!verifiedWidgetId) return initial;
2586
+
2463
2587
  let decision;
2464
2588
  try {
2465
2589
  decision = await requestApproval$2(
2466
2590
  {
2467
- widgetId: req.widgetId,
2591
+ widgetId: verifiedWidgetId,
2468
2592
  domain: "fs",
2469
2593
  action: req.action,
2470
2594
  args: req.args || {},
@@ -2487,7 +2611,7 @@ async function gateFsCallWithJit$1(req, opts = {}) {
2487
2611
  allow: false,
2488
2612
  reason:
2489
2613
  "user declined JIT consent for widget '" +
2490
- req.widgetId +
2614
+ verifiedWidgetId +
2491
2615
  "' calling fs '" +
2492
2616
  req.action +
2493
2617
  "'",
@@ -2511,9 +2635,9 @@ async function gateFsCallWithJit$1(req, opts = {}) {
2511
2635
  addition.grantOrigin = "live";
2512
2636
 
2513
2637
  try {
2514
- const current = getGrant$3(req.widgetId);
2638
+ const current = getGrant$3(verifiedWidgetId);
2515
2639
  const merged = _mergeFsGrant(current, addition);
2516
- setGrant$3(req.widgetId, merged);
2640
+ setGrant$3(verifiedWidgetId, merged);
2517
2641
  } catch (e) {
2518
2642
  return {
2519
2643
  allow: false,
@@ -2575,6 +2699,20 @@ var fsGate = {
2575
2699
 
2576
2700
  const { getGrant: getGrant$2, setGrant: setGrant$2 } = grantedPermissions;
2577
2701
  const { requestApproval: requestApproval$1 } = jitConsent$1;
2702
+ const { lookup: lookupMountToken$1 } = mountTokenRegistry;
2703
+
2704
+ // See `mountTokenRegistry.js` and the matching block in fsGate.js —
2705
+ // when a token is supplied, it's the trusted identity source; the
2706
+ // renderer-claimed widgetId is ignored. Legacy widgetId-only callers
2707
+ // still work in slice 1 (additive); slice 2 will flip to deny.
2708
+ function _resolveIdentity$1({ token, widgetId }) {
2709
+ if (typeof token === "string" && token.length > 0) {
2710
+ const resolved = lookupMountToken$1(token);
2711
+ if (resolved) return { widgetId: resolved, source: "token" };
2712
+ return { widgetId: null, source: "token-unknown" };
2713
+ }
2714
+ return { widgetId: widgetId || null, source: "legacy" };
2715
+ }
2578
2716
 
2579
2717
  function _isNoGrantDenial$1(reason) {
2580
2718
  return (
@@ -2617,7 +2755,16 @@ function _parseHost(url) {
2617
2755
  * Synchronous gate evaluation.
2618
2756
  * @returns {{ allow: true } | { allow: false, reason: string }}
2619
2757
  */
2620
- function gateNetworkCall$2({ widgetId, action, args }) {
2758
+ function gateNetworkCall$2({ widgetId, token, action, args }) {
2759
+ const resolved = _resolveIdentity$1({ token, widgetId });
2760
+ if (resolved.source === "token-unknown") {
2761
+ return {
2762
+ allow: false,
2763
+ reason:
2764
+ "network gate: unknown mount token; widget identity not verifiable",
2765
+ };
2766
+ }
2767
+ widgetId = resolved.widgetId;
2621
2768
  if (!widgetId) {
2622
2769
  return {
2623
2770
  allow: false,
@@ -2708,11 +2855,21 @@ async function gateNetworkCallWithJit$2(req, opts = {}) {
2708
2855
  if (!opts.enableJit) return initial;
2709
2856
  if (!_isNoGrantDenial$1(initial.reason)) return initial;
2710
2857
 
2858
+ // Same identity-resolution as the sync gate — the JIT prompt and
2859
+ // grant write must use the verified widgetId, not whatever the
2860
+ // renderer claimed.
2861
+ const resolved = _resolveIdentity$1({
2862
+ token: req.token,
2863
+ widgetId: req.widgetId,
2864
+ });
2865
+ const verifiedWidgetId = resolved.widgetId;
2866
+ if (!verifiedWidgetId) return initial;
2867
+
2711
2868
  let decision;
2712
2869
  try {
2713
2870
  decision = await requestApproval$1(
2714
2871
  {
2715
- widgetId: req.widgetId,
2872
+ widgetId: verifiedWidgetId,
2716
2873
  domain: "network",
2717
2874
  action: req.action,
2718
2875
  args: req.args || {},
@@ -2735,7 +2892,7 @@ async function gateNetworkCallWithJit$2(req, opts = {}) {
2735
2892
  allow: false,
2736
2893
  reason:
2737
2894
  "user declined JIT consent for widget '" +
2738
- req.widgetId +
2895
+ verifiedWidgetId +
2739
2896
  "' calling network '" +
2740
2897
  req.action +
2741
2898
  "'",
@@ -2753,9 +2910,9 @@ async function gateNetworkCallWithJit$2(req, opts = {}) {
2753
2910
  addition.grantOrigin = "live";
2754
2911
 
2755
2912
  try {
2756
- const current = getGrant$2(req.widgetId);
2913
+ const current = getGrant$2(verifiedWidgetId);
2757
2914
  const merged = _mergeNetworkGrant(current, addition);
2758
- setGrant$2(req.widgetId, merged);
2915
+ setGrant$2(verifiedWidgetId, merged);
2759
2916
  } catch (e) {
2760
2917
  return {
2761
2918
  allow: false,
@@ -2867,7 +3024,7 @@ function requireSafeJsExecutor () {
2867
3024
  let _modulePromise = null;
2868
3025
  function getModule() {
2869
3026
  if (!_modulePromise) {
2870
- const { getQuickJS } = require$$0$3;
3027
+ const { getQuickJS } = require$$0$4;
2871
3028
  _modulePromise = getQuickJS();
2872
3029
  }
2873
3030
  return _modulePromise;
@@ -3541,13 +3698,19 @@ function _loadFlags$1() {
3541
3698
  *
3542
3699
  * @returns {Promise<boolean>}
3543
3700
  */
3544
- async function _runFsGate(win, action, widgetId, args, errorEvent) {
3701
+ async function _runFsGate(win, action, widgetId, args, errorEvent, token) {
3545
3702
  const settings = _loadFlags$1();
3546
3703
  if (!readEnforceFlag$2(settings)) return true; // gate disabled
3547
- if (!widgetId) return true; // legacy callers without widgetId — see plan
3704
+ // Slice 1 (additive): token is the trusted identity when present.
3705
+ // If neither token nor widgetId is supplied, legacy bypass still
3706
+ // applies; slice 2 will flip this to deny.
3707
+ if (!widgetId && !token) return true;
3548
3708
  const gate = readJitFlag$2(settings)
3549
- ? await gateFsCallWithJit({ widgetId, action, args }, { enableJit: true })
3550
- : gateFsCall({ widgetId, action, args });
3709
+ ? await gateFsCallWithJit(
3710
+ { widgetId, token, action, args },
3711
+ { enableJit: true },
3712
+ )
3713
+ : gateFsCall({ widgetId, token, action, args });
3551
3714
  if (gate.allow) return true;
3552
3715
  if (win && errorEvent) {
3553
3716
  win.webContents.send(errorEvent, {
@@ -3562,16 +3725,16 @@ async function _runFsGate(win, action, widgetId, args, errorEvent) {
3562
3725
  * Phase 3 network gate. Same shape as _runFsGate but for outbound
3563
3726
  * URLs. Mirrors fs's "disabled / no widgetId / sync vs async" branching.
3564
3727
  */
3565
- async function _runNetworkGate$1(win, action, widgetId, args, errorEvent) {
3728
+ async function _runNetworkGate$1(win, action, widgetId, args, errorEvent, token) {
3566
3729
  const settings = _loadFlags$1();
3567
3730
  if (!readEnforceFlag$2(settings)) return true;
3568
- if (!widgetId) return true;
3731
+ if (!widgetId && !token) return true;
3569
3732
  const gate = readJitFlag$2(settings)
3570
3733
  ? await gateNetworkCallWithJit$1(
3571
- { widgetId, action, args },
3734
+ { widgetId, token, action, args },
3572
3735
  { enableJit: true },
3573
3736
  )
3574
- : gateNetworkCall$1({ widgetId, action, args });
3737
+ : gateNetworkCall$1({ widgetId, token, action, args });
3575
3738
  if (gate.allow) return true;
3576
3739
  if (win && errorEvent) {
3577
3740
  win.webContents.send(errorEvent, {
@@ -3741,7 +3904,13 @@ const dataController$1 = {
3741
3904
  }
3742
3905
  },
3743
3906
 
3744
- readDataFromURL: async (win, url, toFilepath, widgetId = null) => {
3907
+ readDataFromURL: async (
3908
+ win,
3909
+ url,
3910
+ toFilepath,
3911
+ widgetId = null,
3912
+ token = null,
3913
+ ) => {
3745
3914
  // Phase 3 network gate. Runs before HTTPS-protocol + safePath
3746
3915
  // checks so JIT can prompt the user without leaking URL parser
3747
3916
  // edge cases through error timing.
@@ -3751,6 +3920,7 @@ const dataController$1 = {
3751
3920
  widgetId,
3752
3921
  { url },
3753
3922
  events$5.READ_DATA_URL_ERROR,
3923
+ token,
3754
3924
  );
3755
3925
  if (!gateOk) return;
3756
3926
  try {
@@ -3938,6 +4108,7 @@ const dataController$1 = {
3938
4108
  append,
3939
4109
  returnEmpty = {},
3940
4110
  widgetId = null,
4111
+ token = null,
3941
4112
  ) => {
3942
4113
  // Phase 2 fs gate. Runs before safePath containment so JIT can
3943
4114
  // prompt the user without leaking path-shape information through
@@ -3948,6 +4119,7 @@ const dataController$1 = {
3948
4119
  widgetId,
3949
4120
  { filename },
3950
4121
  events$5.DATA_SAVE_TO_FILE_ERROR,
4122
+ token,
3951
4123
  );
3952
4124
  if (!gateOk) return;
3953
4125
  try {
@@ -4043,7 +4215,13 @@ const dataController$1 = {
4043
4215
  }
4044
4216
  },
4045
4217
 
4046
- readFromFile: async (win, filename, returnIfEmpty = {}, widgetId = null) => {
4218
+ readFromFile: async (
4219
+ win,
4220
+ filename,
4221
+ returnIfEmpty = {},
4222
+ widgetId = null,
4223
+ token = null,
4224
+ ) => {
4047
4225
  // Phase 2 fs gate — same as saveToFile.
4048
4226
  const gateOk = await _runFsGate(
4049
4227
  win,
@@ -4051,6 +4229,7 @@ const dataController$1 = {
4051
4229
  widgetId,
4052
4230
  { filename },
4053
4231
  events$5.DATA_READ_FROM_FILE_ERROR,
4232
+ token,
4054
4233
  );
4055
4234
  if (!gateOk) return;
4056
4235
  try {
@@ -20707,7 +20886,7 @@ auth$2.exchangeAuthorization = exchangeAuthorization;
20707
20886
  auth$2.refreshAuthorization = refreshAuthorization;
20708
20887
  auth$2.fetchToken = fetchToken;
20709
20888
  auth$2.registerClient = registerClient;
20710
- const pkce_challenge_1 = __importDefault$2(require$$0$4);
20889
+ const pkce_challenge_1 = __importDefault$2(require$$0$5);
20711
20890
  const types_js_1$5 = types$2;
20712
20891
  const auth_js_1$1 = auth$1;
20713
20892
  const auth_js_2 = auth$1;
@@ -22265,6 +22444,21 @@ streamableHttp$1.StreamableHTTPClientTransport = StreamableHTTPClientTransport$1
22265
22444
  const { getGrant: getGrant$1, setGrant: setGrant$1 } = grantedPermissions;
22266
22445
  const { safePath: safePath$1 } = safePath_1;
22267
22446
  const { requestApproval } = jitConsent$1;
22447
+ const { lookup: lookupMountToken } = mountTokenRegistry;
22448
+
22449
+ // See `electron/security/mountTokenRegistry.js`. When a token is
22450
+ // supplied it's the trusted identity source — gates resolve widgetId
22451
+ // via lookupMountToken and ignore renderer-claimed widgetId. Slice 1
22452
+ // keeps the legacy widgetId-only path working (additive); slice 2 will
22453
+ // flip to deny-without-token.
22454
+ function _resolveIdentity({ token, widgetId }) {
22455
+ if (typeof token === "string" && token.length > 0) {
22456
+ const resolved = lookupMountToken(token);
22457
+ if (resolved) return { widgetId: resolved, source: "token" };
22458
+ return { widgetId: null, source: "token-unknown" };
22459
+ }
22460
+ return { widgetId: widgetId || null, source: "legacy" };
22461
+ }
22268
22462
 
22269
22463
  // Argument keys that look like paths. Different MCP servers use
22270
22464
  // different conventions; this list covers the common filesystem-style
@@ -22286,7 +22480,15 @@ function isWriteTool(toolName) {
22286
22480
  /**
22287
22481
  * @returns {{ allow: true } | { allow: false, reason: string }}
22288
22482
  */
22289
- function gateToolCall$1({ widgetId, serverName, toolName, args }) {
22483
+ function gateToolCall$1({ widgetId, token, serverName, toolName, args }) {
22484
+ const resolved = _resolveIdentity({ token, widgetId });
22485
+ if (resolved.source === "token-unknown") {
22486
+ return {
22487
+ allow: false,
22488
+ reason: "MCP gate: unknown mount token; widget identity not verifiable",
22489
+ };
22490
+ }
22491
+ widgetId = resolved.widgetId;
22290
22492
  if (!widgetId) {
22291
22493
  return {
22292
22494
  allow: false,
@@ -22440,11 +22642,20 @@ async function gateToolCallWithJit$1(req, opts = {}) {
22440
22642
  if (!opts.enableJit) return initial;
22441
22643
  if (!_isNoGrantDenial(initial.reason)) return initial;
22442
22644
 
22645
+ // Same identity-resolution as the sync gate — JIT prompt and grant
22646
+ // write must use the verified widgetId.
22647
+ const resolved = _resolveIdentity({
22648
+ token: req.token,
22649
+ widgetId: req.widgetId,
22650
+ });
22651
+ const verifiedWidgetId = resolved.widgetId;
22652
+ if (!verifiedWidgetId) return initial;
22653
+
22443
22654
  let decision;
22444
22655
  try {
22445
22656
  decision = await requestApproval(
22446
22657
  {
22447
- widgetId: req.widgetId,
22658
+ widgetId: verifiedWidgetId,
22448
22659
  domain: "mcp",
22449
22660
  action: "callTool",
22450
22661
  args: {
@@ -22471,7 +22682,7 @@ async function gateToolCallWithJit$1(req, opts = {}) {
22471
22682
  allow: false,
22472
22683
  reason:
22473
22684
  "user declined JIT consent for widget '" +
22474
- req.widgetId +
22685
+ verifiedWidgetId +
22475
22686
  "' calling '" +
22476
22687
  req.toolName +
22477
22688
  "' on '" +
@@ -22501,9 +22712,9 @@ async function gateToolCallWithJit$1(req, opts = {}) {
22501
22712
  addition.grantOrigin = "live";
22502
22713
 
22503
22714
  try {
22504
- const current = getGrant$1(req.widgetId);
22715
+ const current = getGrant$1(verifiedWidgetId);
22505
22716
  const merged = _mergeGrant(current, addition);
22506
- setGrant$1(req.widgetId, merged);
22717
+ setGrant$1(verifiedWidgetId, merged);
22507
22718
  } catch (e) {
22508
22719
  return {
22509
22720
  allow: false,
@@ -22716,7 +22927,7 @@ var mcpScopeResolver = {
22716
22927
  * Uses @modelcontextprotocol/sdk for protocol handling.
22717
22928
  */
22718
22929
 
22719
- const { Client } = require$$0$5;
22930
+ const { Client } = require$$0$6;
22720
22931
  const {
22721
22932
  StdioClientTransport,
22722
22933
  } = require$$1$3;
@@ -23521,6 +23732,7 @@ const mcpController$3 = {
23521
23732
  allowedTools = null,
23522
23733
  widgetId = null,
23523
23734
  workspaceId = null,
23735
+ token = null,
23524
23736
  ) => {
23525
23737
  const key = serverKey(workspaceId, serverName);
23526
23738
  try {
@@ -23544,8 +23756,12 @@ const mcpController$3 = {
23544
23756
  // startServer happens after the gate decides — and (b) avoids
23545
23757
  // leaking server-running state through error timing to a
23546
23758
  // probing widget that doesn't have permission anyway.
23547
- if (isWidgetPermissionEnforcementEnabled() && widgetId) {
23548
- const gateReq = { widgetId, serverName, toolName, args };
23759
+ // Slice 1 (additive): if a mount token is supplied it's the
23760
+ // trusted identity source; widgetId is verified-or-overwritten
23761
+ // inside the gate via mountTokenRegistry. Gate gets to run
23762
+ // whenever EITHER token or widgetId is present.
23763
+ if (isWidgetPermissionEnforcementEnabled() && (widgetId || token)) {
23764
+ const gateReq = { widgetId, token, serverName, toolName, args };
23549
23765
  const gate = isJitConsentEnabled()
23550
23766
  ? await gateToolCallWithJit(gateReq, { enableJit: true })
23551
23767
  : gateToolCall(gateReq);
@@ -24099,7 +24315,7 @@ const REGISTRY_BASE_URL$1 =
24099
24315
  let store$3 = null;
24100
24316
  function getStore$1() {
24101
24317
  if (!store$3) {
24102
- const Store = require$$0$6;
24318
+ const Store = require$$0$7;
24103
24319
  store$3 = new Store({
24104
24320
  name: "dash-registry-auth",
24105
24321
  encryptionKey: "dash-registry-v1",
@@ -25384,7 +25600,7 @@ var manifestScanner = {
25384
25600
  * and dispatching task-fired events to renderer windows.
25385
25601
  */
25386
25602
 
25387
- const Store$1 = require$$0$6;
25603
+ const Store$1 = require$$0$7;
25388
25604
  const { Cron } = require$$1$4;
25389
25605
 
25390
25606
  const store$2 = new Store$1({ name: "dash-scheduler" });
@@ -28600,7 +28816,7 @@ const algoliaController$1 = {
28600
28816
 
28601
28817
  var algoliaController_1 = algoliaController$1;
28602
28818
 
28603
- const OpenAI = require$$0$7;
28819
+ const OpenAI = require$$0$8;
28604
28820
  const events$2 = events$8;
28605
28821
 
28606
28822
  const openaiController$1 = {
@@ -46960,7 +47176,7 @@ __export(src_exports, {
46960
47176
  var dist = __toCommonJS(src_exports);
46961
47177
 
46962
47178
  // src/server.ts
46963
- var import_node_http = require$$0$8;
47179
+ var import_node_http = require$$0$9;
46964
47180
 
46965
47181
  // src/listener.ts
46966
47182
  var import_node_http22 = require$$1$5;
@@ -47311,7 +47527,7 @@ var buildOutgoingHttpHeaders = (headers) => {
47311
47527
  var X_ALREADY_SENT = "x-hono-already-sent";
47312
47528
 
47313
47529
  // src/globals.ts
47314
- var import_node_crypto = __toESM(require$$3$4);
47530
+ var import_node_crypto = __toESM(require$$0$3);
47315
47531
  if (typeof commonjsGlobal.crypto === "undefined") {
47316
47532
  commonjsGlobal.crypto = import_node_crypto.default;
47317
47533
  }
@@ -48739,7 +48955,7 @@ var jsonSchemaToZod_1 = { jsonSchemaToZod: jsonSchemaToZod$1, jsonSchemaProperty
48739
48955
  */
48740
48956
 
48741
48957
  const https$1 = require$$11;
48742
- const { randomUUID } = require$$3$4;
48958
+ const { randomUUID } = require$$0$3;
48743
48959
  const { BrowserWindow: BrowserWindow$2 } = require$$0$1;
48744
48960
  const { McpServer } = mcp;
48745
48961
  const {
@@ -50545,10 +50761,10 @@ var themeFromUrlErrors$1 = {
50545
50761
  * computed styles, and favicon/logo images (via node-vibrant).
50546
50762
  */
50547
50763
 
50548
- const css = require$$0$9;
50764
+ const css = require$$0$a;
50549
50765
  const { Vibrant } = require$$1$6;
50550
50766
  const https = require$$11;
50551
- const http = require$$0$8;
50767
+ const http = require$$0$9;
50552
50768
  const { URL: URL$1 } = require$$4$1;
50553
50769
  const {
50554
50770
  UrlUnreachableError,
@@ -55108,7 +55324,7 @@ var toolHandlers$1 = {
55108
55324
  * per-request, receiving the full messages array each time.
55109
55325
  */
55110
55326
 
55111
- const Anthropic = require$$0$a;
55327
+ const Anthropic = require$$0$b;
55112
55328
  const mcpController$2 = mcpControllerExports;
55113
55329
  const cliController$1 = cliController_1;
55114
55330
  const toolDefinitions = toolDefinitions$1;
@@ -60273,7 +60489,7 @@ var dashboardConfigController$1 = {
60273
60489
  */
60274
60490
 
60275
60491
  const { Notification } = require$$0$1;
60276
- const Store = require$$0$6;
60492
+ const Store = require$$0$7;
60277
60493
 
60278
60494
  const store$1 = new Store({ name: "dash-notifications" });
60279
60495
 
@@ -60536,7 +60752,7 @@ var notificationController_1 = notificationController$2;
60536
60752
  const { app: app$2 } = require$$0$1;
60537
60753
  const fs$1 = require$$0$2;
60538
60754
  const path$2 = require$$1$1;
60539
- const WebSocket = require$$3$5;
60755
+ const WebSocket = require$$3$4;
60540
60756
  const {
60541
60757
  gateNetworkCall,
60542
60758
  gateNetworkCallWithJit,
@@ -60557,16 +60773,16 @@ function _loadFlags() {
60557
60773
  }
60558
60774
  }
60559
60775
 
60560
- async function _runNetworkGate(action, widgetId, args) {
60776
+ async function _runNetworkGate(action, widgetId, args, token) {
60561
60777
  const settings = _loadFlags();
60562
60778
  if (!readEnforceFlag(settings)) return { allow: true };
60563
- if (!widgetId) return { allow: true }; // legacy callers
60779
+ if (!widgetId && !token) return { allow: true }; // legacy callers
60564
60780
  return readJitFlag(settings)
60565
60781
  ? await gateNetworkCallWithJit(
60566
- { widgetId, action, args },
60782
+ { widgetId, token, action, args },
60567
60783
  { enableJit: true },
60568
60784
  )
60569
- : gateNetworkCall({ widgetId, action, args });
60785
+ : gateNetworkCall({ widgetId, token, action, args });
60570
60786
  }
60571
60787
 
60572
60788
  /**
@@ -60998,17 +61214,20 @@ const webSocketController$1 = {
60998
61214
  * @param {object} config - { url, headers, subprotocols, credentials }
60999
61215
  * @returns {{ success, providerName, status } | { error, message }}
61000
61216
  */
61001
- connect: async (win, providerName, config, widgetId = null) => {
61002
- // Phase 3 network gate — fires only when an explicit widgetId is
61003
- // supplied. The interpolated URL (with credentials substituted) is
61004
- // what we actually open the socket to, so the gate uses it for
61217
+ connect: async (win, providerName, config, widgetId = null, token = null) => {
61218
+ // Phase 3 network gate — fires when an explicit widgetId or token
61219
+ // is supplied. The interpolated URL (with credentials substituted)
61220
+ // is what we actually open the socket to, so the gate uses it for
61005
61221
  // hostname extraction.
61006
61222
  const interpolatedForGate = config?.credentials
61007
61223
  ? interpolate(config.url, config.credentials)
61008
61224
  : config?.url;
61009
- const gateResult = await _runNetworkGate("wsConnect", widgetId, {
61010
- url: interpolatedForGate,
61011
- });
61225
+ const gateResult = await _runNetworkGate(
61226
+ "wsConnect",
61227
+ widgetId,
61228
+ { url: interpolatedForGate },
61229
+ token,
61230
+ );
61012
61231
  if (!gateResult.allow) {
61013
61232
  return {
61014
61233
  error: true,
@@ -61614,7 +61833,7 @@ var grantDiff = { isBroadening: isBroadening$1 };
61614
61833
  * grant are also surfaced — those are the install-consent retroactive prompts.
61615
61834
  */
61616
61835
 
61617
- const { ipcMain: ipcMain$1, dialog, BrowserWindow: BrowserWindow$1 } = require$$0$1;
61836
+ const { ipcMain: ipcMain$2, dialog, BrowserWindow: BrowserWindow$1 } = require$$0$1;
61618
61837
  const {
61619
61838
  getGrant,
61620
61839
  setGrant,
@@ -61670,11 +61889,11 @@ async function _confirmBroadening(event, widgetId, summary) {
61670
61889
  }
61671
61890
 
61672
61891
  function setupWidgetMcpGrantsHandlers() {
61673
- ipcMain$1.handle("widget-mcp:get-grant", (event, widgetId) => {
61892
+ ipcMain$2.handle("widget-mcp:get-grant", (event, widgetId) => {
61674
61893
  return getGrant(widgetId);
61675
61894
  });
61676
61895
 
61677
- ipcMain$1.handle("widget-mcp:set-grant", async (event, widgetId, perms) => {
61896
+ ipcMain$2.handle("widget-mcp:set-grant", async (event, widgetId, perms) => {
61678
61897
  const current = getGrant(widgetId);
61679
61898
  const diff = isBroadening(current, perms);
61680
61899
  if (diff.broadening) {
@@ -61684,11 +61903,11 @@ function setupWidgetMcpGrantsHandlers() {
61684
61903
  return setGrant(widgetId, perms);
61685
61904
  });
61686
61905
 
61687
- ipcMain$1.handle("widget-mcp:revoke", (event, widgetId) => {
61906
+ ipcMain$2.handle("widget-mcp:revoke", (event, widgetId) => {
61688
61907
  return revokeGrant(widgetId);
61689
61908
  });
61690
61909
 
61691
- ipcMain$1.handle("widget-mcp:revoke-server", (event, widgetId, serverName) => {
61910
+ ipcMain$2.handle("widget-mcp:revoke-server", (event, widgetId, serverName) => {
61692
61911
  return revokeServer(widgetId, serverName);
61693
61912
  });
61694
61913
 
@@ -61698,7 +61917,7 @@ function setupWidgetMcpGrantsHandlers() {
61698
61917
  // "Grant manually" for unmanifested widgets. Plus orphan-grant rows for
61699
61918
  // granted-but-uninstalled cases. Logic delegated to
61700
61919
  // widgetMcpGrantsListing.buildGrantsListing for unit-testability.
61701
- ipcMain$1.handle("widget-mcp:list-all", () => {
61920
+ ipcMain$2.handle("widget-mcp:list-all", () => {
61702
61921
  const grantsByWidget = new Map();
61703
61922
  for (const { widgetId, granted } of listAllGrants()) {
61704
61923
  grantsByWidget.set(widgetId, granted);
@@ -61729,6 +61948,46 @@ function setupWidgetMcpGrantsHandlers() {
61729
61948
 
61730
61949
  var widgetMcpGrantsController$1 = { setupWidgetMcpGrantsHandlers };
61731
61950
 
61951
+ /**
61952
+ * widgetMountTokenController.js
61953
+ *
61954
+ * IPC handlers for the widget mount-token registry. Called by
61955
+ * `WidgetFactory` at React mount/unmount time. Returns a fresh
61956
+ * server-generated token that the widget framework bakes into the
61957
+ * widget's bound API; the renderer never picks the token itself.
61958
+ *
61959
+ * See `electron/security/mountTokenRegistry.js` for the registry and
61960
+ * the longer threat-model note.
61961
+ *
61962
+ * Channels:
61963
+ * - "framework:register-widget-mount" (widgetId) → token string
61964
+ * - "framework:unregister-widget-mount" (token) → boolean (true = unregistered)
61965
+ */
61966
+
61967
+ const { ipcMain: ipcMain$1 } = require$$0$1;
61968
+ const { register, unregister } = mountTokenRegistry;
61969
+
61970
+ function setupWidgetMountTokenHandlers() {
61971
+ ipcMain$1.handle("framework:register-widget-mount", (_event, widgetId) => {
61972
+ if (typeof widgetId !== "string" || widgetId.length === 0) {
61973
+ return null;
61974
+ }
61975
+ try {
61976
+ return register(widgetId);
61977
+ } catch {
61978
+ return null;
61979
+ }
61980
+ });
61981
+
61982
+ ipcMain$1.handle("framework:unregister-widget-mount", (_event, token) => {
61983
+ if (typeof token !== "string" || token.length === 0) return false;
61984
+ unregister(token);
61985
+ return true;
61986
+ });
61987
+ }
61988
+
61989
+ var widgetMountTokenController$1 = { setupWidgetMountTokenHandlers };
61990
+
61732
61991
  /**
61733
61992
  * clientFactories.js
61734
61993
  *
@@ -61752,7 +62011,7 @@ clientCache$1.registerFactory("algolia", (credentials) => {
61752
62011
 
61753
62012
  // --- OpenAI ---
61754
62013
  clientCache$1.registerFactory("openai", (credentials) => {
61755
- const OpenAI = require$$0$7;
62014
+ const OpenAI = require$$0$8;
61756
62015
  return new OpenAI({ apiKey: credentials.apiKey });
61757
62016
  });
61758
62017
 
@@ -61771,7 +62030,7 @@ const MAX_RECENTS = 20;
61771
62030
  let store = null;
61772
62031
  function getStore() {
61773
62032
  if (!store) {
61774
- const Store = require$$0$6;
62033
+ const Store = require$$0$7;
61775
62034
  store = new Store({ name: "dash-session" });
61776
62035
  }
61777
62036
  return store;
@@ -63227,8 +63486,8 @@ const dataApi$2 = {
63227
63486
  ipcRenderer$l.invoke(READ_JSON, { filepath, objectCount });
63228
63487
  },
63229
63488
 
63230
- readDataFromURL: (url, toFilepath, widgetId = null) => {
63231
- ipcRenderer$l.invoke(READ_DATA_URL, { url, toFilepath, widgetId });
63489
+ readDataFromURL: (url, toFilepath, widgetId = null, token = null) => {
63490
+ ipcRenderer$l.invoke(READ_DATA_URL, { url, toFilepath, widgetId, token });
63232
63491
  },
63233
63492
 
63234
63493
  /*
@@ -63236,13 +63495,21 @@ const dataApi$2 = {
63236
63495
  * @param {object} options { filename, extension }
63237
63496
  * @param {object} returnEmpty the return empty object
63238
63497
  */
63239
- saveData: (data, filename, append, returnEmpty, widgetId = null) =>
63498
+ saveData: (
63499
+ data,
63500
+ filename,
63501
+ append,
63502
+ returnEmpty,
63503
+ widgetId = null,
63504
+ token = null,
63505
+ ) =>
63240
63506
  ipcRenderer$l.invoke(DATA_SAVE_TO_FILE, {
63241
63507
  data,
63242
63508
  filename,
63243
63509
  append,
63244
63510
  returnEmpty,
63245
63511
  widgetId,
63512
+ token,
63246
63513
  }),
63247
63514
 
63248
63515
  /*
@@ -63254,11 +63521,12 @@ const dataApi$2 = {
63254
63521
  * for legacy callers (`enforceWidgetMcpPermissions` flag still
63255
63522
  * gates the gate itself).
63256
63523
  */
63257
- readData: (filename, returnEmpty = [], widgetId = null) =>
63524
+ readData: (filename, returnEmpty = [], widgetId = null, token = null) =>
63258
63525
  ipcRenderer$l.invoke(DATA_READ_FROM_FILE, {
63259
63526
  filename,
63260
63527
  returnEmpty,
63261
63528
  widgetId,
63529
+ token,
63262
63530
  }),
63263
63531
 
63264
63532
  /**
@@ -64031,6 +64299,7 @@ const mcpApi$2 = {
64031
64299
  allowedTools = null,
64032
64300
  widgetId = null,
64033
64301
  workspaceId = null,
64302
+ token = null,
64034
64303
  ) =>
64035
64304
  ipcRenderer$g.invoke(MCP_CALL_TOOL, {
64036
64305
  serverName,
@@ -64039,6 +64308,7 @@ const mcpApi$2 = {
64039
64308
  allowedTools,
64040
64309
  widgetId,
64041
64310
  workspaceId,
64311
+ token,
64042
64312
  }),
64043
64313
 
64044
64314
  /**
@@ -65214,8 +65484,8 @@ const webSocketApi$2 = {
65214
65484
  * @param {object} config { url, headers, subprotocols, credentials }
65215
65485
  * @returns {Promise<{ success, providerName, status } | { error, message }>}
65216
65486
  */
65217
- connect: (providerName, config, widgetId = null) =>
65218
- ipcRenderer$4.invoke(WS_CONNECT, { providerName, config, widgetId }),
65487
+ connect: (providerName, config, widgetId = null, token = null) =>
65488
+ ipcRenderer$4.invoke(WS_CONNECT, { providerName, config, widgetId, token }),
65219
65489
 
65220
65490
  /**
65221
65491
  * disconnect
@@ -66735,6 +67005,7 @@ const webSocketController = webSocketController_1;
66735
67005
  const extractionCacheController = extractionCacheController_1;
66736
67006
  const mcpDashServerController = mcpDashServerController_1;
66737
67007
  const widgetMcpGrantsController = widgetMcpGrantsController$1;
67008
+ const widgetMountTokenController = widgetMountTokenController$1;
66738
67009
  const jitConsent = jitConsent$1;
66739
67010
 
66740
67011
  // --- Errors ---
@@ -66838,6 +67109,7 @@ var electron = {
66838
67109
  extractionCacheController,
66839
67110
  mcpDashServerController,
66840
67111
  widgetMcpGrantsController,
67112
+ widgetMountTokenController,
66841
67113
  jitConsent,
66842
67114
 
66843
67115
  // Controller functions (flat) — spread for convenient destructuring