@trops/dash-core 0.1.487 → 0.1.489

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.
@@ -17,7 +17,7 @@ var require$$0$5 = require('@modelcontextprotocol/sdk/client/index.js');
17
17
  var require$$1$4 = require('@modelcontextprotocol/sdk/client/stdio.js');
18
18
  var require$$0$4 = require('pkce-challenge');
19
19
  var require$$2$1 = require('os');
20
- var require$$7 = require('child_process');
20
+ var require$$9$1 = require('child_process');
21
21
  var require$$3$2 = require('adm-zip');
22
22
  var require$$4$1 = require('url');
23
23
  var require$$2$2 = require('vm');
@@ -1107,7 +1107,7 @@ var secureStoreController$1 = {
1107
1107
  getData: getData$1,
1108
1108
  };
1109
1109
 
1110
- const path$k = require$$1$2;
1110
+ const path$l = require$$1$2;
1111
1111
  const {
1112
1112
  readFileSync,
1113
1113
  writeFileSync: writeFileSync$4,
@@ -1125,7 +1125,7 @@ const {
1125
1125
  function ensureDirectoryExistence$2(filePath) {
1126
1126
  try {
1127
1127
  // isDirectory
1128
- var dirname = path$k.dirname(filePath);
1128
+ var dirname = path$l.dirname(filePath);
1129
1129
  // check if the directory exists...return true
1130
1130
  // if not, we can pass in the dirname as the filepath
1131
1131
  // and check each directory recursively.
@@ -1240,7 +1240,7 @@ function removeFilesFromDirectory(directory, excludeFiles = []) {
1240
1240
 
1241
1241
  for (const file of files) {
1242
1242
  if (!excludeFiles.includes(file)) {
1243
- unlinkSync(path$k.join(directory, file), (err) => {
1243
+ unlinkSync(path$l.join(directory, file), (err) => {
1244
1244
  if (err) throw err;
1245
1245
  });
1246
1246
  }
@@ -1257,8 +1257,8 @@ var file = {
1257
1257
  checkDirectory: checkDirectory$1,
1258
1258
  };
1259
1259
 
1260
- const { app: app$c } = require$$0$1;
1261
- const path$j = require$$1$2;
1260
+ const { app: app$e } = require$$0$1;
1261
+ const path$k = require$$1$2;
1262
1262
  const { writeFileSync: writeFileSync$3 } = require$$0$2;
1263
1263
  const { getFileContents: getFileContents$7 } = file;
1264
1264
 
@@ -1305,8 +1305,8 @@ const workspaceController$3 = {
1305
1305
  saveWorkspaceForApplication: (win, appId, workspaceObject) => {
1306
1306
  try {
1307
1307
  // filename to the pages file (live pages)
1308
- const filename = path$j.join(
1309
- app$c.getPath("userData"),
1308
+ const filename = path$k.join(
1309
+ app$e.getPath("userData"),
1310
1310
  appName$7,
1311
1311
  appId,
1312
1312
  configFilename$5,
@@ -1354,8 +1354,8 @@ const workspaceController$3 = {
1354
1354
  saveMenuItemsForApplication: (win, appId, menuItems) => {
1355
1355
  try {
1356
1356
  // filename to the workspaces file
1357
- const filename = path$j.join(
1358
- app$c.getPath("userData"),
1357
+ const filename = path$k.join(
1358
+ app$e.getPath("userData"),
1359
1359
  appName$7,
1360
1360
  appId,
1361
1361
  configFilename$5,
@@ -1403,8 +1403,8 @@ const workspaceController$3 = {
1403
1403
  */
1404
1404
  deleteWorkspaceForApplication: (win, appId, workspaceId) => {
1405
1405
  try {
1406
- const filename = path$j.join(
1407
- app$c.getPath("userData"),
1406
+ const filename = path$k.join(
1407
+ app$e.getPath("userData"),
1408
1408
  appName$7,
1409
1409
  appId,
1410
1410
  configFilename$5,
@@ -1437,8 +1437,8 @@ const workspaceController$3 = {
1437
1437
 
1438
1438
  listWorkspacesForApplication: (win, appId) => {
1439
1439
  try {
1440
- const filename = path$j.join(
1441
- app$c.getPath("userData"),
1440
+ const filename = path$k.join(
1441
+ app$e.getPath("userData"),
1442
1442
  appName$7,
1443
1443
  appId,
1444
1444
  configFilename$5,
@@ -1465,8 +1465,8 @@ const workspaceController$3 = {
1465
1465
 
1466
1466
  listMenuItemsForApplication: (win, appId) => {
1467
1467
  try {
1468
- const filename = path$j.join(
1469
- app$c.getPath("userData"),
1468
+ const filename = path$k.join(
1469
+ app$e.getPath("userData"),
1470
1470
  appName$7,
1471
1471
  appId,
1472
1472
  configFilename$5,
@@ -1509,8 +1509,8 @@ const workspaceController$3 = {
1509
1509
 
1510
1510
  var workspaceController_1 = workspaceController$3;
1511
1511
 
1512
- const { app: app$b } = require$$0$1;
1513
- const path$i = require$$1$2;
1512
+ const { app: app$d } = require$$0$1;
1513
+ const path$j = require$$1$2;
1514
1514
  const { writeFileSync: writeFileSync$2 } = require$$0$2;
1515
1515
  const { getFileContents: getFileContents$6 } = file;
1516
1516
 
@@ -1530,8 +1530,8 @@ const themeController$5 = {
1530
1530
  saveThemeForApplication: (win, appId, name, obj) => {
1531
1531
  try {
1532
1532
  // filename to the pages file (live pages)
1533
- const filename = path$i.join(
1534
- app$b.getPath("userData"),
1533
+ const filename = path$j.join(
1534
+ app$d.getPath("userData"),
1535
1535
  appName$6,
1536
1536
  appId,
1537
1537
  configFilename$4,
@@ -1576,8 +1576,8 @@ const themeController$5 = {
1576
1576
  */
1577
1577
  listThemesForApplication: (win, appId) => {
1578
1578
  try {
1579
- const filename = path$i.join(
1580
- app$b.getPath("userData"),
1579
+ const filename = path$j.join(
1580
+ app$d.getPath("userData"),
1581
1581
  appName$6,
1582
1582
  appId,
1583
1583
  configFilename$4,
@@ -1618,8 +1618,8 @@ const themeController$5 = {
1618
1618
  */
1619
1619
  deleteThemeForApplication: (win, appId, themeKey) => {
1620
1620
  try {
1621
- const filename = path$i.join(
1622
- app$b.getPath("userData"),
1621
+ const filename = path$j.join(
1622
+ app$d.getPath("userData"),
1623
1623
  appName$6,
1624
1624
  appId,
1625
1625
  configFilename$4,
@@ -1695,9 +1695,9 @@ var themeController_1 = themeController$5;
1695
1695
  * `/data/`.
1696
1696
  */
1697
1697
 
1698
- const path$h = require$$1$2;
1699
- const fs$c = require$$0$2;
1700
- const { app: app$a } = require$$0$1;
1698
+ const path$i = require$$1$2;
1699
+ const fs$d = require$$0$2;
1700
+ const { app: app$c } = require$$0$1;
1701
1701
 
1702
1702
  const APP_NAME = "Dashboard";
1703
1703
 
@@ -1706,10 +1706,10 @@ const APP_NAME = "Dashboard";
1706
1706
  * @returns {string[]} ordered allowed roots for that category
1707
1707
  */
1708
1708
  function getAllowedRoots$2(category) {
1709
- const userData = app$a.getPath("userData");
1709
+ const userData = app$c.getPath("userData");
1710
1710
  switch (category) {
1711
1711
  case "data": {
1712
- const def = path$h.join(userData, APP_NAME, "data");
1712
+ const def = path$i.join(userData, APP_NAME, "data");
1713
1713
  // The user can configure a custom data directory in
1714
1714
  // Settings → General → Data Directory. If set, that
1715
1715
  // location is ALSO an allowed root. We don't replace the
@@ -1719,13 +1719,13 @@ function getAllowedRoots$2(category) {
1719
1719
  return override ? [def, override] : [def];
1720
1720
  }
1721
1721
  case "themes":
1722
- return [path$h.join(userData, APP_NAME, "themes")];
1722
+ return [path$i.join(userData, APP_NAME, "themes")];
1723
1723
  case "widgets":
1724
- return [path$h.join(userData, "widgets")];
1724
+ return [path$i.join(userData, "widgets")];
1725
1725
  case "plugins":
1726
- return [path$h.join(userData, "plugins")];
1726
+ return [path$i.join(userData, "plugins")];
1727
1727
  case "downloads":
1728
- return [app$a.getPath("downloads")];
1728
+ return [app$c.getPath("downloads")];
1729
1729
  default:
1730
1730
  throw new Error("safePath: unknown allowed-roots category: " + category);
1731
1731
  }
@@ -1740,13 +1740,13 @@ function getAllowedRoots$2(category) {
1740
1740
  */
1741
1741
  function readDataDirectoryFromSettings() {
1742
1742
  try {
1743
- const settingsPath = path$h.join(
1744
- app$a.getPath("userData"),
1743
+ const settingsPath = path$i.join(
1744
+ app$c.getPath("userData"),
1745
1745
  APP_NAME,
1746
1746
  "settings.json",
1747
1747
  );
1748
- if (!fs$c.existsSync(settingsPath)) return undefined;
1749
- const raw = fs$c.readFileSync(settingsPath, "utf8");
1748
+ if (!fs$d.existsSync(settingsPath)) return undefined;
1749
+ const raw = fs$d.readFileSync(settingsPath, "utf8");
1750
1750
  const settings = JSON.parse(raw);
1751
1751
  const dir = settings && settings.dataDirectory;
1752
1752
  if (typeof dir === "string" && dir) return dir;
@@ -1764,7 +1764,7 @@ function readDataDirectoryFromSettings() {
1764
1764
  * @returns {string} validated absolute real-path
1765
1765
  * @throws if requested is not contained within any allowed root
1766
1766
  */
1767
- function safePath$2(requested, allowedRoots) {
1767
+ function safePath$3(requested, allowedRoots) {
1768
1768
  if (typeof requested !== "string" || !requested) {
1769
1769
  throw new Error("safePath: requested must be a non-empty string");
1770
1770
  }
@@ -1772,18 +1772,18 @@ function safePath$2(requested, allowedRoots) {
1772
1772
  throw new Error("safePath: allowedRoots must be a non-empty array");
1773
1773
  }
1774
1774
 
1775
- const resolved = path$h.resolve(requested);
1775
+ const resolved = path$i.resolve(requested);
1776
1776
 
1777
1777
  // Real-path through symlinks. If the file doesn't exist yet (a
1778
1778
  // create-new operation), real-path the parent so a symlink in the
1779
1779
  // parent chain can't trick us.
1780
1780
  let real = resolved;
1781
1781
  try {
1782
- real = fs$c.realpathSync(resolved);
1782
+ real = fs$d.realpathSync(resolved);
1783
1783
  } catch (_e) {
1784
1784
  try {
1785
- const parent = fs$c.realpathSync(path$h.dirname(resolved));
1786
- real = path$h.join(parent, path$h.basename(resolved));
1785
+ const parent = fs$d.realpathSync(path$i.dirname(resolved));
1786
+ real = path$i.join(parent, path$i.basename(resolved));
1787
1787
  } catch (_e2) {
1788
1788
  // Parent doesn't exist either. Use the resolved-but-not-
1789
1789
  // real path; the caller's mkdirSync will happen inside the
@@ -1795,14 +1795,14 @@ function safePath$2(requested, allowedRoots) {
1795
1795
  for (const root of allowedRoots) {
1796
1796
  let realRoot = root;
1797
1797
  try {
1798
- if (fs$c.existsSync(root)) realRoot = fs$c.realpathSync(root);
1798
+ if (fs$d.existsSync(root)) realRoot = fs$d.realpathSync(root);
1799
1799
  } catch (_e) {
1800
1800
  // root doesn't exist or isn't reachable — keep as-is for
1801
1801
  // the comparison below
1802
1802
  }
1803
1803
  // Exact match OR strictly-inside (with separator to prevent
1804
1804
  // /data-evil/ matching /data/).
1805
- if (real === realRoot || real.startsWith(realRoot + path$h.sep)) {
1805
+ if (real === realRoot || real.startsWith(realRoot + path$i.sep)) {
1806
1806
  return real;
1807
1807
  }
1808
1808
  }
@@ -1819,7 +1819,7 @@ function safePath$2(requested, allowedRoots) {
1819
1819
  }
1820
1820
 
1821
1821
  var safePath_1 = {
1822
- safePath: safePath$2,
1822
+ safePath: safePath$3,
1823
1823
  getAllowedRoots: getAllowedRoots$2,
1824
1824
  };
1825
1825
 
@@ -2060,15 +2060,15 @@ var safeJsExecutor$1 = {
2060
2060
  * - CSV -> JSON
2061
2061
  */
2062
2062
 
2063
- var fs$b = require$$0$2;
2063
+ var fs$c = require$$0$2;
2064
2064
  var readline = require$$1$3;
2065
2065
  const xtreamer = require$$2;
2066
2066
  var xmlParser = require$$3$1;
2067
2067
  var JSONStream$1 = require$$4;
2068
2068
  const stream$1 = require$$5;
2069
2069
  var csv = require$$6;
2070
- const path$g = require$$1$2;
2071
- const { app: app$9 } = require$$0$1;
2070
+ const path$h = require$$1$2;
2071
+ const { app: app$b } = require$$0$1;
2072
2072
  const { ensureDirectoryExistence: ensureDirectoryExistence$1 } = file;
2073
2073
  const safeJsExecutor = safeJsExecutor$1;
2074
2074
 
@@ -2122,7 +2122,7 @@ let Transform$1 = class Transform {
2122
2122
  let lineObject = [];
2123
2123
 
2124
2124
  const readInterface = readline.createInterface({
2125
- input: fs$b.createReadStream(filepath),
2125
+ input: fs$c.createReadStream(filepath),
2126
2126
  output: process.stdout,
2127
2127
  console: false,
2128
2128
  });
@@ -2157,7 +2157,7 @@ let Transform$1 = class Transform {
2157
2157
  return new Promise((resolve, reject) => {
2158
2158
  try {
2159
2159
  const parser = JSONStream$1.parse("*");
2160
- const readStream = fs$b.createReadStream(file).pipe(parser);
2160
+ const readStream = fs$c.createReadStream(file).pipe(parser);
2161
2161
 
2162
2162
  let count = 0;
2163
2163
 
@@ -2210,7 +2210,7 @@ let Transform$1 = class Transform {
2210
2210
  parseXMLStream = (filepath, outpath, start) => {
2211
2211
  return new Promise((resolve, reject) => {
2212
2212
  try {
2213
- const xmlFileReadStream = fs$b.createReadStream(filepath);
2213
+ const xmlFileReadStream = fs$c.createReadStream(filepath);
2214
2214
 
2215
2215
  xmlFileReadStream.on("end", () => {
2216
2216
  writeStream.write("\n]");
@@ -2221,7 +2221,7 @@ let Transform$1 = class Transform {
2221
2221
  resolve("Read Finish");
2222
2222
  });
2223
2223
 
2224
- const writeStream = fs$b.createWriteStream(outpath);
2224
+ const writeStream = fs$c.createWriteStream(outpath);
2225
2225
  writeStream.write("[\n");
2226
2226
 
2227
2227
  const options = {
@@ -2273,10 +2273,10 @@ let Transform$1 = class Transform {
2273
2273
  ) => {
2274
2274
  return new Promise((resolve, reject) => {
2275
2275
  try {
2276
- const readStream = fs$b
2276
+ const readStream = fs$c
2277
2277
  .createReadStream(filepath)
2278
2278
  .pipe(csv({ separator: delimiter }));
2279
- const writeStream = fs$b.createWriteStream(outpath);
2279
+ const writeStream = fs$c.createWriteStream(outpath);
2280
2280
 
2281
2281
  let canParse = true;
2282
2282
 
@@ -2362,18 +2362,18 @@ let Transform$1 = class Transform {
2362
2362
  }
2363
2363
 
2364
2364
  // Validate file paths are within app data directory
2365
- const appDataDir = path$g.join(app$9.getPath("userData"), TRANSFORM_APP_NAME);
2366
- const resolvedFilepath = path$g.resolve(filepath);
2367
- const resolvedOutFilepath = path$g.resolve(outFilepath);
2365
+ const appDataDir = path$h.join(app$b.getPath("userData"), TRANSFORM_APP_NAME);
2366
+ const resolvedFilepath = path$h.resolve(filepath);
2367
+ const resolvedOutFilepath = path$h.resolve(outFilepath);
2368
2368
 
2369
- if (!resolvedFilepath.startsWith(appDataDir + path$g.sep)) {
2369
+ if (!resolvedFilepath.startsWith(appDataDir + path$h.sep)) {
2370
2370
  return reject(
2371
2371
  new Error(
2372
2372
  "Input file path must be within the application data directory",
2373
2373
  ),
2374
2374
  );
2375
2375
  }
2376
- if (!resolvedOutFilepath.startsWith(appDataDir + path$g.sep)) {
2376
+ if (!resolvedOutFilepath.startsWith(appDataDir + path$h.sep)) {
2377
2377
  return reject(
2378
2378
  new Error(
2379
2379
  "Output file path must be within the application data directory",
@@ -2384,16 +2384,16 @@ let Transform$1 = class Transform {
2384
2384
  // JSON parser
2385
2385
  var parser = JSONStream$1.parse("*");
2386
2386
 
2387
- if (!fs$b.existsSync(resolvedFilepath)) {
2387
+ if (!fs$c.existsSync(resolvedFilepath)) {
2388
2388
  return reject(new Error("File doesnt exist"));
2389
2389
  }
2390
2390
  console.log("file exists ", resolvedFilepath);
2391
2391
  // create the readStream to parse the large file (json)
2392
- var readStream = fs$b.createReadStream(resolvedFilepath).pipe(parser);
2392
+ var readStream = fs$c.createReadStream(resolvedFilepath).pipe(parser);
2393
2393
 
2394
2394
  ensureDirectoryExistence$1(resolvedOutFilepath);
2395
2395
 
2396
- var writeStream = fs$b.createWriteStream(resolvedOutFilepath);
2396
+ var writeStream = fs$c.createWriteStream(resolvedOutFilepath);
2397
2397
 
2398
2398
  let sep = "";
2399
2399
  let count = 0;
@@ -2505,12 +2505,12 @@ let Transform$1 = class Transform {
2505
2505
 
2506
2506
  var transform = Transform$1;
2507
2507
 
2508
- const { app: app$8 } = require$$0$1;
2509
- var fs$a = require$$0$2;
2510
- const path$f = require$$1$2;
2508
+ const { app: app$a } = require$$0$1;
2509
+ var fs$b = require$$0$2;
2510
+ const path$g = require$$1$2;
2511
2511
  const events$5 = events$8;
2512
2512
  const { getFileContents: getFileContents$5, writeToFile: writeToFile$2 } = file;
2513
- const { safePath: safePath$1, getAllowedRoots: getAllowedRoots$1 } = safePath_1;
2513
+ const { safePath: safePath$2, getAllowedRoots: getAllowedRoots$1 } = safePath_1;
2514
2514
 
2515
2515
  // Convert Json to Csv
2516
2516
  const ObjectsToCsv = require$$6$1;
@@ -2532,8 +2532,8 @@ const dataController$1 = {
2532
2532
  // Validate the renderer-supplied filename is contained within
2533
2533
  // the data directory. path.join doesn't reject `..` segments;
2534
2534
  // safePath does.
2535
- const candidate = path$f.join(
2536
- app$8.getPath("userData"),
2535
+ const candidate = path$g.join(
2536
+ app$a.getPath("userData"),
2537
2537
  appName$5,
2538
2538
  appId,
2539
2539
  "data",
@@ -2541,7 +2541,7 @@ const dataController$1 = {
2541
2541
  );
2542
2542
  let filename;
2543
2543
  try {
2544
- filename = safePath$1(candidate, getAllowedRoots$1("data"));
2544
+ filename = safePath$2(candidate, getAllowedRoots$1("data"));
2545
2545
  } catch (pathErr) {
2546
2546
  win.webContents.send(events$5.DATA_JSON_TO_CSV_FILE_ERROR, {
2547
2547
  error: pathErr.message,
@@ -2607,7 +2607,7 @@ const dataController$1 = {
2607
2607
  try {
2608
2608
  let validated;
2609
2609
  try {
2610
- validated = safePath$1(filepath, getAllowedRoots$1("data"));
2610
+ validated = safePath$2(filepath, getAllowedRoots$1("data"));
2611
2611
  } catch (pathErr) {
2612
2612
  win.webContents.send(events$5.READ_LINES_ERROR, {
2613
2613
  error: pathErr.message,
@@ -2641,7 +2641,7 @@ const dataController$1 = {
2641
2641
  try {
2642
2642
  let validated;
2643
2643
  try {
2644
- validated = safePath$1(filepath, getAllowedRoots$1("data"));
2644
+ validated = safePath$2(filepath, getAllowedRoots$1("data"));
2645
2645
  } catch (pathErr) {
2646
2646
  win.webContents.send(events$5.READ_JSON_ERROR, {
2647
2647
  error: pathErr.message,
@@ -2689,9 +2689,9 @@ const dataController$1 = {
2689
2689
  // Validate toFilepath is within the app data directory.
2690
2690
  // safePath replaces the previous inline check; same containment
2691
2691
  // intent, plus realpath/symlink protection.
2692
- const resolvedFilepath = safePath$1(toFilepath, getAllowedRoots$1("data"));
2692
+ const resolvedFilepath = safePath$2(toFilepath, getAllowedRoots$1("data"));
2693
2693
 
2694
- const writeStream = fs$a.createWriteStream(resolvedFilepath);
2694
+ const writeStream = fs$b.createWriteStream(resolvedFilepath);
2695
2695
 
2696
2696
  https$3
2697
2697
  .get(url, (resp) => {
@@ -2739,8 +2739,8 @@ const dataController$1 = {
2739
2739
  let validatedIn, validatedOut;
2740
2740
  try {
2741
2741
  const roots = getAllowedRoots$1("data");
2742
- validatedIn = safePath$1(filepath, roots);
2743
- validatedOut = safePath$1(outpath, roots);
2742
+ validatedIn = safePath$2(filepath, roots);
2743
+ validatedOut = safePath$2(outpath, roots);
2744
2744
  } catch (pathErr) {
2745
2745
  win.webContents.send(events$5.PARSE_XML_STREAM_ERROR, {
2746
2746
  error: pathErr.message,
@@ -2799,8 +2799,8 @@ const dataController$1 = {
2799
2799
  let validatedIn, validatedOut;
2800
2800
  try {
2801
2801
  const roots = getAllowedRoots$1("data");
2802
- validatedIn = safePath$1(filepath, roots);
2803
- validatedOut = safePath$1(outpath, roots);
2802
+ validatedIn = safePath$2(filepath, roots);
2803
+ validatedOut = safePath$2(outpath, roots);
2804
2804
  } catch (pathErr) {
2805
2805
  win.webContents.send(events$5.PARSE_CSV_STREAM_ERROR, {
2806
2806
  error: pathErr.message,
@@ -2855,15 +2855,15 @@ const dataController$1 = {
2855
2855
  if (data) {
2856
2856
  // Validate filename is contained within the data directory.
2857
2857
  // path.join doesn't reject `..` segments; safePath does.
2858
- const candidate = path$f.join(
2859
- app$8.getPath("userData"),
2858
+ const candidate = path$g.join(
2859
+ app$a.getPath("userData"),
2860
2860
  appName$5,
2861
2861
  "data",
2862
2862
  filename,
2863
2863
  );
2864
2864
  let toFilename;
2865
2865
  try {
2866
- toFilename = safePath$1(candidate, getAllowedRoots$1("data"));
2866
+ toFilename = safePath$2(candidate, getAllowedRoots$1("data"));
2867
2867
  } catch (pathErr) {
2868
2868
  win.webContents.send(events$5.DATA_SAVE_TO_FILE_ERROR, {
2869
2869
  success: false,
@@ -2948,8 +2948,8 @@ const dataController$1 = {
2948
2948
  try {
2949
2949
  if (filename) {
2950
2950
  // filename to the pages file (live pages)
2951
- const fromFilename = path$f.join(
2952
- app$8.getPath("userData"),
2951
+ const fromFilename = path$g.join(
2952
+ app$a.getPath("userData"),
2953
2953
  appName$5,
2954
2954
  "data",
2955
2955
  filename,
@@ -3029,9 +3029,9 @@ var dataController_1 = dataController$1;
3029
3029
  * settingsController
3030
3030
  */
3031
3031
 
3032
- const { app: app$7 } = require$$0$1;
3033
- const path$e = require$$1$2;
3034
- const fs$9 = require$$0$2;
3032
+ const { app: app$9 } = require$$0$1;
3033
+ const path$f = require$$1$2;
3034
+ const fs$a = require$$0$2;
3035
3035
  const { getFileContents: getFileContents$4, writeToFile: writeToFile$1 } = file;
3036
3036
 
3037
3037
  const configFilename$3 = "settings.json";
@@ -3039,15 +3039,15 @@ const appName$4 = "Dashboard";
3039
3039
 
3040
3040
  // Helper function to recursively copy directory
3041
3041
  function copyDirectory(source, destination) {
3042
- if (!fs$9.existsSync(destination)) {
3043
- fs$9.mkdirSync(destination, { recursive: true });
3042
+ if (!fs$a.existsSync(destination)) {
3043
+ fs$a.mkdirSync(destination, { recursive: true });
3044
3044
  }
3045
3045
 
3046
- const files = fs$9.readdirSync(source);
3046
+ const files = fs$a.readdirSync(source);
3047
3047
  for (const file of files) {
3048
- const srcPath = path$e.join(source, file);
3049
- const destPath = path$e.join(destination, file);
3050
- const stat = fs$9.lstatSync(srcPath);
3048
+ const srcPath = path$f.join(source, file);
3049
+ const destPath = path$f.join(destination, file);
3050
+ const stat = fs$a.lstatSync(srcPath);
3051
3051
 
3052
3052
  // Skip symlinks to prevent following links to sensitive files
3053
3053
  if (stat.isSymbolicLink()) {
@@ -3058,7 +3058,7 @@ function copyDirectory(source, destination) {
3058
3058
  if (stat.isDirectory()) {
3059
3059
  copyDirectory(srcPath, destPath);
3060
3060
  } else {
3061
- fs$9.copyFileSync(srcPath, destPath);
3061
+ fs$a.copyFileSync(srcPath, destPath);
3062
3062
  }
3063
3063
  }
3064
3064
  }
@@ -3074,8 +3074,8 @@ const settingsController$4 = {
3074
3074
  try {
3075
3075
  if (data) {
3076
3076
  // <appId>/settings.json
3077
- const filename = path$e.join(
3078
- app$7.getPath("userData"),
3077
+ const filename = path$f.join(
3078
+ app$9.getPath("userData"),
3079
3079
  appName$4,
3080
3080
  configFilename$3,
3081
3081
  );
@@ -3110,8 +3110,8 @@ const settingsController$4 = {
3110
3110
  getSettingsForApplication: (win) => {
3111
3111
  try {
3112
3112
  // <appId>/settings.json
3113
- const filename = path$e.join(
3114
- app$7.getPath("userData"),
3113
+ const filename = path$f.join(
3114
+ app$9.getPath("userData"),
3115
3115
  appName$4,
3116
3116
  configFilename$3,
3117
3117
  );
@@ -3141,15 +3141,15 @@ const settingsController$4 = {
3141
3141
  */
3142
3142
  getDataDirectory: (win) => {
3143
3143
  try {
3144
- const settingsPath = path$e.join(
3145
- app$7.getPath("userData"),
3144
+ const settingsPath = path$f.join(
3145
+ app$9.getPath("userData"),
3146
3146
  appName$4,
3147
3147
  configFilename$3,
3148
3148
  );
3149
3149
  const settings = getFileContents$4(settingsPath, {});
3150
3150
  const userDataDir =
3151
3151
  settings.userDataDirectory ||
3152
- path$e.join(app$7.getPath("userData"), appName$4);
3152
+ path$f.join(app$9.getPath("userData"), appName$4);
3153
3153
 
3154
3154
  console.log("[settingsController] Data directory retrieved successfully");
3155
3155
  // Return the data for ipcMain.handle() - modern promise-based approach
@@ -3176,18 +3176,18 @@ const settingsController$4 = {
3176
3176
  setDataDirectory: (win, newPath) => {
3177
3177
  try {
3178
3178
  // Validate the path exists and is a directory
3179
- if (!fs$9.existsSync(newPath)) {
3180
- fs$9.mkdirSync(newPath, { recursive: true });
3179
+ if (!fs$a.existsSync(newPath)) {
3180
+ fs$a.mkdirSync(newPath, { recursive: true });
3181
3181
  }
3182
3182
 
3183
- const stats = fs$9.statSync(newPath);
3183
+ const stats = fs$a.statSync(newPath);
3184
3184
  if (!stats.isDirectory()) {
3185
3185
  throw new Error("Path is not a directory");
3186
3186
  }
3187
3187
 
3188
3188
  // Update settings
3189
- const settingsPath = path$e.join(
3190
- app$7.getPath("userData"),
3189
+ const settingsPath = path$f.join(
3190
+ app$9.getPath("userData"),
3191
3191
  appName$4,
3192
3192
  configFilename$3,
3193
3193
  );
@@ -3220,20 +3220,20 @@ const settingsController$4 = {
3220
3220
  migrateDataDirectory: (win, oldPath, newPath) => {
3221
3221
  try {
3222
3222
  // Resolve paths to prevent traversal
3223
- const resolvedOldPath = path$e.resolve(oldPath);
3224
- const resolvedNewPath = path$e.resolve(newPath);
3223
+ const resolvedOldPath = path$f.resolve(oldPath);
3224
+ const resolvedNewPath = path$f.resolve(newPath);
3225
3225
 
3226
3226
  // Validate oldPath is the current configured data directory
3227
- const settingsCheckPath = path$e.join(
3228
- app$7.getPath("userData"),
3227
+ const settingsCheckPath = path$f.join(
3228
+ app$9.getPath("userData"),
3229
3229
  appName$4,
3230
3230
  configFilename$3,
3231
3231
  );
3232
3232
  const currentSettings = getFileContents$4(settingsCheckPath, {});
3233
3233
  const currentDataDir =
3234
3234
  currentSettings.userDataDirectory ||
3235
- path$e.join(app$7.getPath("userData"), appName$4);
3236
- if (resolvedOldPath !== path$e.resolve(currentDataDir)) {
3235
+ path$f.join(app$9.getPath("userData"), appName$4);
3236
+ if (resolvedOldPath !== path$f.resolve(currentDataDir)) {
3237
3237
  throw new Error("Source path must be the current data directory");
3238
3238
  }
3239
3239
 
@@ -3257,20 +3257,20 @@ const settingsController$4 = {
3257
3257
  }
3258
3258
 
3259
3259
  // Validate paths
3260
- if (!fs$9.existsSync(resolvedOldPath)) {
3260
+ if (!fs$a.existsSync(resolvedOldPath)) {
3261
3261
  throw new Error("Source directory does not exist");
3262
3262
  }
3263
3263
 
3264
- if (!fs$9.existsSync(resolvedNewPath)) {
3265
- fs$9.mkdirSync(resolvedNewPath, { recursive: true });
3264
+ if (!fs$a.existsSync(resolvedNewPath)) {
3265
+ fs$a.mkdirSync(resolvedNewPath, { recursive: true });
3266
3266
  }
3267
3267
 
3268
3268
  // Copy files
3269
3269
  copyDirectory(resolvedOldPath, resolvedNewPath);
3270
3270
 
3271
3271
  // Update settings to use new path
3272
- const settingsPath = path$e.join(
3273
- app$7.getPath("userData"),
3272
+ const settingsPath = path$f.join(
3273
+ app$9.getPath("userData"),
3274
3274
  appName$4,
3275
3275
  configFilename$3,
3276
3276
  );
@@ -3934,8 +3934,8 @@ function requireProviderController () {
3934
3934
  return providerController_1;
3935
3935
  }
3936
3936
 
3937
- const { app: app$6 } = require$$0$1;
3938
- const path$d = require$$1$2;
3937
+ const { app: app$8 } = require$$0$1;
3938
+ const path$e = require$$1$2;
3939
3939
  const { writeFileSync: writeFileSync$1 } = require$$0$2;
3940
3940
  const events$4 = events$8;
3941
3941
  const { getFileContents: getFileContents$3 } = file;
@@ -3955,8 +3955,8 @@ const layoutController$1 = {
3955
3955
  saveLayoutForApplication: (win, appId, layoutObject) => {
3956
3956
  try {
3957
3957
  // filename to the pages file (live pages)
3958
- const filename = path$d.join(
3959
- app$6.getPath("userData"),
3958
+ const filename = path$e.join(
3959
+ app$8.getPath("userData"),
3960
3960
  appName$3,
3961
3961
  appId,
3962
3962
  configFilename$2,
@@ -3988,8 +3988,8 @@ const layoutController$1 = {
3988
3988
  */
3989
3989
  listLayoutsForApplication: (win, appId) => {
3990
3990
  try {
3991
- const filename = path$d.join(
3992
- app$6.getPath("userData"),
3991
+ const filename = path$e.join(
3992
+ app$8.getPath("userData"),
3993
3993
  appName$3,
3994
3994
  appId,
3995
3995
  configFilename$2,
@@ -21152,6 +21152,311 @@ let StreamableHTTPClientTransport$1 = class StreamableHTTPClientTransport {
21152
21152
  };
21153
21153
  streamableHttp$1.StreamableHTTPClientTransport = StreamableHTTPClientTransport$1;
21154
21154
 
21155
+ /**
21156
+ * widgetPermissions.js
21157
+ *
21158
+ * Read and parse the `dash.permissions.mcp` block from an installed
21159
+ * widget's package.json.
21160
+ *
21161
+ * Manifest format (declared by widget authors in their package.json):
21162
+ *
21163
+ * {
21164
+ * "name": "@trops/notes-summarizer",
21165
+ * "dash": {
21166
+ * "permissions": {
21167
+ * "mcp": {
21168
+ * "filesystem": {
21169
+ * "tools": ["read_file", "list_directory"],
21170
+ * "readPaths": ["~/Documents/notes"],
21171
+ * "writePaths": []
21172
+ * },
21173
+ * "github": {
21174
+ * "tools": ["search_repositories", "get_file_contents"]
21175
+ * }
21176
+ * }
21177
+ * }
21178
+ * }
21179
+ * }
21180
+ *
21181
+ * Path strings beginning with `~` are expanded to the user's home
21182
+ * directory at parse time. Tool-only servers (no path I/O, e.g.
21183
+ * github) omit the `readPaths`/`writePaths` keys.
21184
+ *
21185
+ * Public API:
21186
+ *
21187
+ * getWidgetMcpPermissions(widgetId) → permissions | null
21188
+ * Returns the parsed permissions for a widget, or null if the
21189
+ * widget is unmanifested. Cached per process.
21190
+ *
21191
+ * parseManifestPermissions(packageJson) → permissions | null
21192
+ * Pure function — exposed for tests.
21193
+ *
21194
+ * clearCache() → void
21195
+ * Test-only. Drops the in-process cache so tests can re-read.
21196
+ */
21197
+
21198
+ const fs$9 = require$$0$2;
21199
+ const path$d = require$$1$2;
21200
+ const os$2 = require$$2$1;
21201
+ const { app: app$7 } = require$$0$1;
21202
+
21203
+ // Cache: widgetId → permissions | null. Populated lazily on first
21204
+ // lookup; invalidated when a widget is installed/uninstalled (the
21205
+ // install/uninstall paths call clearCache()).
21206
+ const _cache = new Map();
21207
+
21208
+ /**
21209
+ * Expand a leading "~" to the user's home directory. Other paths are
21210
+ * returned as-is.
21211
+ */
21212
+ function expandHome(p) {
21213
+ if (typeof p !== "string" || !p) return p;
21214
+ if (p === "~") return os$2.homedir();
21215
+ if (p.startsWith("~/") || p.startsWith("~\\")) {
21216
+ return path$d.join(os$2.homedir(), p.slice(2));
21217
+ }
21218
+ return p;
21219
+ }
21220
+
21221
+ /**
21222
+ * Parse a widget's package.json contents into a normalized permissions
21223
+ * object. Returns null if no `dash.permissions.mcp` block exists.
21224
+ */
21225
+ function parseManifestPermissions(packageJson) {
21226
+ if (!packageJson || typeof packageJson !== "object") return null;
21227
+ const mcp = packageJson?.dash?.permissions?.mcp;
21228
+ if (!mcp || typeof mcp !== "object") return null;
21229
+
21230
+ const servers = {};
21231
+ for (const [serverName, raw] of Object.entries(mcp)) {
21232
+ if (!raw || typeof raw !== "object") continue;
21233
+ const tools = Array.isArray(raw.tools)
21234
+ ? raw.tools.filter((t) => typeof t === "string")
21235
+ : [];
21236
+ const readPaths = Array.isArray(raw.readPaths)
21237
+ ? raw.readPaths.filter((p) => typeof p === "string").map(expandHome)
21238
+ : [];
21239
+ const writePaths = Array.isArray(raw.writePaths)
21240
+ ? raw.writePaths.filter((p) => typeof p === "string").map(expandHome)
21241
+ : [];
21242
+ servers[serverName] = { tools, readPaths, writePaths };
21243
+ }
21244
+
21245
+ return { servers };
21246
+ }
21247
+
21248
+ /**
21249
+ * Find a widget's installed package.json on disk. Widgets live under
21250
+ * userData/widgets/<scope>/<name>/ for scoped packages or
21251
+ * userData/widgets/<name>/ for unscoped. The widgetId is the npm
21252
+ * package name (e.g. "@trops/notes-summarizer" or "notes-summarizer").
21253
+ */
21254
+ function resolveWidgetPackagePath(widgetId) {
21255
+ if (typeof widgetId !== "string" || !widgetId) return null;
21256
+ const widgetsRoot = path$d.join(app$7.getPath("userData"), "widgets");
21257
+ // Split scope from name for "@scope/name" form.
21258
+ const parts = widgetId.startsWith("@") ? widgetId.split("/") : [widgetId];
21259
+ return path$d.join(widgetsRoot, ...parts, "package.json");
21260
+ }
21261
+
21262
+ /**
21263
+ * Read and parse a widget's MCP permissions. Returns null if:
21264
+ * - the widget directory doesn't exist
21265
+ * - package.json is unreadable / malformed
21266
+ * - the widget hasn't declared dash.permissions.mcp
21267
+ *
21268
+ * Result is cached per widgetId for the lifetime of this process.
21269
+ */
21270
+ function getWidgetMcpPermissions$1(widgetId) {
21271
+ if (_cache.has(widgetId)) return _cache.get(widgetId);
21272
+ const pkgPath = resolveWidgetPackagePath(widgetId);
21273
+ if (!pkgPath || !fs$9.existsSync(pkgPath)) {
21274
+ _cache.set(widgetId, null);
21275
+ return null;
21276
+ }
21277
+ try {
21278
+ const raw = fs$9.readFileSync(pkgPath, "utf8");
21279
+ const pkg = JSON.parse(raw);
21280
+ const perms = parseManifestPermissions(pkg);
21281
+ _cache.set(widgetId, perms);
21282
+ return perms;
21283
+ } catch (e) {
21284
+ console.warn(
21285
+ "[widgetPermissions] failed to read package.json for " +
21286
+ widgetId +
21287
+ ": " +
21288
+ e.message,
21289
+ );
21290
+ _cache.set(widgetId, null);
21291
+ return null;
21292
+ }
21293
+ }
21294
+
21295
+ function clearCache() {
21296
+ _cache.clear();
21297
+ }
21298
+
21299
+ var widgetPermissions = {
21300
+ getWidgetMcpPermissions: getWidgetMcpPermissions$1,
21301
+ parseManifestPermissions,
21302
+ expandHome,
21303
+ clearCache,
21304
+ };
21305
+
21306
+ /**
21307
+ * permissionGate.js
21308
+ *
21309
+ * Per-widget gating for MCP tool calls.
21310
+ *
21311
+ * When `gateToolCall` is invoked with a widget identity, server name,
21312
+ * tool name, and tool arguments, it consults the widget's installed
21313
+ * permission manifest (electron/mcp/widgetPermissions.js) and either
21314
+ * permits the call or returns a clear denial reason.
21315
+ *
21316
+ * Two layers:
21317
+ *
21318
+ * 1. **Tool-name allowlist** — the manifest's `tools[]` array for the
21319
+ * target server determines which tool names this widget may
21320
+ * invoke. Anything outside the list is rejected.
21321
+ *
21322
+ * 2. **Path-argument containment** — for tools whose arguments
21323
+ * include a path-shaped key (`path`, `uri`, `filepath`, `file`,
21324
+ * `directory`), the supplied path is validated with safePath()
21325
+ * against the widget's declared `readPaths` or `writePaths` for
21326
+ * the target server. The read/write distinction is heuristic
21327
+ * based on the tool name (e.g. `write_file` is treated as a
21328
+ * write).
21329
+ *
21330
+ * This is the runtime enforcement layer. Install-time consent UI and
21331
+ * per-dashboard MCP-server scope reconfiguration are separate plans
21332
+ * (Slices 2 and 3). When the feature flag is OFF (default), this gate
21333
+ * is bypassed entirely; mcpController behaves as before. When ON,
21334
+ * every callTool dispatch goes through this gate.
21335
+ */
21336
+
21337
+ const { getWidgetMcpPermissions } = widgetPermissions;
21338
+ const { safePath: safePath$1 } = safePath_1;
21339
+
21340
+ // Argument keys that look like paths. Different MCP servers use
21341
+ // different conventions; this list covers the common filesystem-style
21342
+ // servers. Extensible — add as new patterns surface.
21343
+ const PATH_ARG_KEYS = ["path", "uri", "filepath", "file", "directory"];
21344
+
21345
+ // Heuristic: tool names matching this regex are treated as writes for
21346
+ // purposes of choosing readPaths vs writePaths. The match is intentionally
21347
+ // broad — we'd rather treat an ambiguous tool as a write (stricter) than
21348
+ // as a read.
21349
+ const WRITE_TOOL_PATTERN =
21350
+ /(^|_)(write|create|edit|delete|remove|append|move|rename|chmod|chown|mkdir)/i;
21351
+
21352
+ function isWriteTool(toolName) {
21353
+ if (typeof toolName !== "string") return false;
21354
+ return WRITE_TOOL_PATTERN.test(toolName);
21355
+ }
21356
+
21357
+ /**
21358
+ * @returns {{ allow: true } | { allow: false, reason: string }}
21359
+ */
21360
+ function gateToolCall$1({ widgetId, serverName, toolName, args }) {
21361
+ if (!widgetId) {
21362
+ return {
21363
+ allow: false,
21364
+ reason: "no widgetId supplied; cannot determine permissions",
21365
+ };
21366
+ }
21367
+
21368
+ const perms = getWidgetMcpPermissions(widgetId);
21369
+ if (!perms) {
21370
+ return {
21371
+ allow: false,
21372
+ reason:
21373
+ "widget '" +
21374
+ widgetId +
21375
+ "' has no MCP permission manifest; declare dash.permissions.mcp in its package.json to grant access",
21376
+ };
21377
+ }
21378
+
21379
+ const serverPerms = perms.servers[serverName];
21380
+ if (!serverPerms) {
21381
+ return {
21382
+ allow: false,
21383
+ reason:
21384
+ "widget '" +
21385
+ widgetId +
21386
+ "' is not authorized to call '" +
21387
+ serverName +
21388
+ "'",
21389
+ };
21390
+ }
21391
+
21392
+ if (!serverPerms.tools.includes(toolName)) {
21393
+ return {
21394
+ allow: false,
21395
+ reason:
21396
+ "tool '" +
21397
+ toolName +
21398
+ "' is not in the allowlist for widget '" +
21399
+ widgetId +
21400
+ "' on server '" +
21401
+ serverName +
21402
+ "'",
21403
+ };
21404
+ }
21405
+
21406
+ // Path-argument containment. Only checked when the tool's args
21407
+ // include a path-shaped key.
21408
+ const isWrite = isWriteTool(toolName);
21409
+ // Write tools must use writePaths; read tools may use either
21410
+ // readPaths or writePaths (write access implies read access).
21411
+ const allowedPaths = isWrite
21412
+ ? serverPerms.writePaths
21413
+ : [...serverPerms.readPaths, ...serverPerms.writePaths];
21414
+
21415
+ if (args && typeof args === "object") {
21416
+ for (const key of PATH_ARG_KEYS) {
21417
+ const v = args[key];
21418
+ if (typeof v !== "string" || !v) continue;
21419
+ if (allowedPaths.length === 0) {
21420
+ return {
21421
+ allow: false,
21422
+ reason:
21423
+ "tool '" +
21424
+ toolName +
21425
+ "' uses path argument '" +
21426
+ key +
21427
+ "' but widget '" +
21428
+ widgetId +
21429
+ "' has no " +
21430
+ (isWrite ? "writePaths" : "readPaths or writePaths") +
21431
+ " declared for server '" +
21432
+ serverName +
21433
+ "'",
21434
+ };
21435
+ }
21436
+ try {
21437
+ safePath$1(v, allowedPaths);
21438
+ } catch (e) {
21439
+ return {
21440
+ allow: false,
21441
+ reason:
21442
+ "path argument '" +
21443
+ key +
21444
+ "' rejected: " +
21445
+ (e && e.message ? e.message : String(e)),
21446
+ };
21447
+ }
21448
+ }
21449
+ }
21450
+
21451
+ return { allow: true };
21452
+ }
21453
+
21454
+ var permissionGate = {
21455
+ gateToolCall: gateToolCall$1,
21456
+ isWriteTool,
21457
+ PATH_ARG_KEYS,
21458
+ };
21459
+
21155
21460
  /**
21156
21461
  * mcpController.js
21157
21462
  *
@@ -21176,6 +21481,28 @@ const path$c = require$$1$2;
21176
21481
  const fs$8 = require$$0$2;
21177
21482
  const os$1 = require$$2$1;
21178
21483
  const responseCache$2 = responseCache_1;
21484
+ const { gateToolCall } = permissionGate;
21485
+ const { app: app$6 } = require$$0$1;
21486
+
21487
+ // Read the widget-MCP-enforcement feature flag from settings.json.
21488
+ // Default is OFF — flipping ON activates per-widget gating in
21489
+ // permissionGate.gateToolCall(). See docs/security/ipc-filesystem-audit.md
21490
+ // and electron/mcp/permissionGate.js for context.
21491
+ function isWidgetPermissionEnforcementEnabled() {
21492
+ try {
21493
+ const settingsPath = path$c.join(
21494
+ app$6.getPath("userData"),
21495
+ "Dashboard",
21496
+ "settings.json",
21497
+ );
21498
+ if (!fs$8.existsSync(settingsPath)) return false;
21499
+ const raw = fs$8.readFileSync(settingsPath, "utf8");
21500
+ const settings = JSON.parse(raw);
21501
+ return Boolean(settings?.security?.enforceWidgetMcpPermissions);
21502
+ } catch (_e) {
21503
+ return false;
21504
+ }
21505
+ }
21179
21506
 
21180
21507
  /**
21181
21508
  * Tool name prefixes considered safe to cache (read-only).
@@ -21311,7 +21638,7 @@ function getShellPath$1() {
21311
21638
  return _shellPath$1;
21312
21639
  }
21313
21640
 
21314
- const { execSync } = require$$7;
21641
+ const { execSync } = require$$9$1;
21315
21642
  const fallbackDirs = ["/usr/local/bin", "/opt/homebrew/bin"];
21316
21643
 
21317
21644
  // Scan nvm versions, tracking both latest and best compatible version
@@ -21880,14 +22207,41 @@ const mcpController$3 = {
21880
22207
  * @param {Array<string>} allowedTools optional whitelist of allowed tool names
21881
22208
  * @returns {{ result } | { error, message }}
21882
22209
  */
21883
- callTool: async (win, serverName, toolName, args, allowedTools = null) => {
22210
+ callTool: async (
22211
+ win,
22212
+ serverName,
22213
+ toolName,
22214
+ args,
22215
+ allowedTools = null,
22216
+ widgetId = null,
22217
+ ) => {
21884
22218
  try {
21885
22219
  const server = activeServers.get(serverName);
21886
22220
  if (!server || !server.client) {
21887
22221
  throw new Error(`Server not connected: ${serverName}`);
21888
22222
  }
21889
22223
 
21890
- // Enforce tool scoping if allowedTools is specified
22224
+ // Per-widget manifest gate. Activated by the
22225
+ // security.enforceWidgetMcpPermissions setting. When enabled
22226
+ // and a widgetId is supplied, the widget's installed
22227
+ // package.json's dash.permissions.mcp block determines what
22228
+ // tools and paths are allowed.
22229
+ if (isWidgetPermissionEnforcementEnabled() && widgetId) {
22230
+ const gate = gateToolCall({
22231
+ widgetId,
22232
+ serverName,
22233
+ toolName,
22234
+ args,
22235
+ });
22236
+ if (!gate.allow) {
22237
+ throw new Error(`Widget permission gate: ${gate.reason}`);
22238
+ }
22239
+ }
22240
+
22241
+ // Legacy renderer-supplied allowedTools whitelist. Kept for
22242
+ // backward compatibility with callers that pre-date the
22243
+ // manifest-based gate. Once the manifest gate is enforced
22244
+ // everywhere, this can be removed.
21891
22245
  if (allowedTools && !allowedTools.includes(toolName)) {
21892
22246
  throw new Error(
21893
22247
  `Tool "${toolName}" is not in the allowed tools list for this widget. Allowed: ${allowedTools.join(
@@ -22184,7 +22538,7 @@ const mcpController$3 = {
22184
22538
  * @returns {{ success } | { error, message }}
22185
22539
  */
22186
22540
  runAuth: async (win, mcpConfig, credentials, authCommand) => {
22187
- const { spawn } = require$$7;
22541
+ const { spawn } = require$$9$1;
22188
22542
 
22189
22543
  const env = cleanEnvForChildProcess();
22190
22544
 
@@ -47133,7 +47487,7 @@ var mcpDashServerController_1 = mcpDashServerController$4;
47133
47487
  * can use the Chat widget without a separate API key.
47134
47488
  */
47135
47489
 
47136
- const { spawn, execSync } = require$$7;
47490
+ const { spawn, execSync } = require$$9$1;
47137
47491
  const {
47138
47492
  LLM_STREAM_DELTA: LLM_STREAM_DELTA$2,
47139
47493
  LLM_STREAM_TOOL_CALL: LLM_STREAM_TOOL_CALL$2,
@@ -61470,15 +61824,27 @@ const mcpApi$2 = {
61470
61824
  * @param {string} serverName the server name
61471
61825
  * @param {string} toolName the tool to call
61472
61826
  * @param {object} args tool arguments
61473
- * @param {Array<string>} allowedTools optional whitelist of allowed tool names
61827
+ * @param {Array<string>} allowedTools optional whitelist of allowed tool names (legacy — prefer per-widget manifest)
61828
+ * @param {string} widgetId optional widget identity. When the
61829
+ * security.enforceWidgetMcpPermissions setting is enabled, this is
61830
+ * used to look up the widget's MCP permission manifest and gate
61831
+ * the call accordingly. Should be the npm package name of the
61832
+ * calling widget (e.g. "@trops/notes-summarizer").
61474
61833
  * @returns {Promise<{ result } | { error, message }>}
61475
61834
  */
61476
- callTool: (serverName, toolName, args, allowedTools = null) =>
61835
+ callTool: (
61836
+ serverName,
61837
+ toolName,
61838
+ args,
61839
+ allowedTools = null,
61840
+ widgetId = null,
61841
+ ) =>
61477
61842
  ipcRenderer$i.invoke(MCP_CALL_TOOL, {
61478
61843
  serverName,
61479
61844
  toolName,
61480
61845
  args,
61481
61846
  allowedTools,
61847
+ widgetId,
61482
61848
  }),
61483
61849
 
61484
61850
  /**