@trops/dash-core 0.1.492 → 0.1.494
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/electron/index.js +506 -175
- package/dist/electron/index.js.map +1 -1
- package/dist/index.esm.js +588 -41
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +588 -41
- package/dist/index.js.map +1 -1
- package/package.json +118 -114
- package/scripts/scanWidgetManifestCli.js +202 -0
package/dist/electron/index.js
CHANGED
|
@@ -1107,7 +1107,7 @@ var secureStoreController$1 = {
|
|
|
1107
1107
|
getData: getData$1,
|
|
1108
1108
|
};
|
|
1109
1109
|
|
|
1110
|
-
const path$
|
|
1110
|
+
const path$n = 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$
|
|
1128
|
+
var dirname = path$n.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$
|
|
1243
|
+
unlinkSync(path$n.join(directory, file), (err) => {
|
|
1244
1244
|
if (err) throw err;
|
|
1245
1245
|
});
|
|
1246
1246
|
}
|
|
@@ -1258,7 +1258,7 @@ var file = {
|
|
|
1258
1258
|
};
|
|
1259
1259
|
|
|
1260
1260
|
const { app: app$f } = require$$0$1;
|
|
1261
|
-
const path$
|
|
1261
|
+
const path$m = require$$1$2;
|
|
1262
1262
|
const { writeFileSync: writeFileSync$3 } = require$$0$2;
|
|
1263
1263
|
const { getFileContents: getFileContents$7 } = file;
|
|
1264
1264
|
|
|
@@ -1305,7 +1305,7 @@ 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$
|
|
1308
|
+
const filename = path$m.join(
|
|
1309
1309
|
app$f.getPath("userData"),
|
|
1310
1310
|
appName$7,
|
|
1311
1311
|
appId,
|
|
@@ -1354,7 +1354,7 @@ const workspaceController$3 = {
|
|
|
1354
1354
|
saveMenuItemsForApplication: (win, appId, menuItems) => {
|
|
1355
1355
|
try {
|
|
1356
1356
|
// filename to the workspaces file
|
|
1357
|
-
const filename = path$
|
|
1357
|
+
const filename = path$m.join(
|
|
1358
1358
|
app$f.getPath("userData"),
|
|
1359
1359
|
appName$7,
|
|
1360
1360
|
appId,
|
|
@@ -1403,7 +1403,7 @@ const workspaceController$3 = {
|
|
|
1403
1403
|
*/
|
|
1404
1404
|
deleteWorkspaceForApplication: (win, appId, workspaceId) => {
|
|
1405
1405
|
try {
|
|
1406
|
-
const filename = path$
|
|
1406
|
+
const filename = path$m.join(
|
|
1407
1407
|
app$f.getPath("userData"),
|
|
1408
1408
|
appName$7,
|
|
1409
1409
|
appId,
|
|
@@ -1437,7 +1437,7 @@ const workspaceController$3 = {
|
|
|
1437
1437
|
|
|
1438
1438
|
listWorkspacesForApplication: (win, appId) => {
|
|
1439
1439
|
try {
|
|
1440
|
-
const filename = path$
|
|
1440
|
+
const filename = path$m.join(
|
|
1441
1441
|
app$f.getPath("userData"),
|
|
1442
1442
|
appName$7,
|
|
1443
1443
|
appId,
|
|
@@ -1465,7 +1465,7 @@ const workspaceController$3 = {
|
|
|
1465
1465
|
|
|
1466
1466
|
listMenuItemsForApplication: (win, appId) => {
|
|
1467
1467
|
try {
|
|
1468
|
-
const filename = path$
|
|
1468
|
+
const filename = path$m.join(
|
|
1469
1469
|
app$f.getPath("userData"),
|
|
1470
1470
|
appName$7,
|
|
1471
1471
|
appId,
|
|
@@ -1510,7 +1510,7 @@ const workspaceController$3 = {
|
|
|
1510
1510
|
var workspaceController_1 = workspaceController$3;
|
|
1511
1511
|
|
|
1512
1512
|
const { app: app$e } = require$$0$1;
|
|
1513
|
-
const path$
|
|
1513
|
+
const path$l = require$$1$2;
|
|
1514
1514
|
const { writeFileSync: writeFileSync$2 } = require$$0$2;
|
|
1515
1515
|
const { getFileContents: getFileContents$6 } = file;
|
|
1516
1516
|
|
|
@@ -1530,7 +1530,7 @@ 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$
|
|
1533
|
+
const filename = path$l.join(
|
|
1534
1534
|
app$e.getPath("userData"),
|
|
1535
1535
|
appName$6,
|
|
1536
1536
|
appId,
|
|
@@ -1576,7 +1576,7 @@ const themeController$5 = {
|
|
|
1576
1576
|
*/
|
|
1577
1577
|
listThemesForApplication: (win, appId) => {
|
|
1578
1578
|
try {
|
|
1579
|
-
const filename = path$
|
|
1579
|
+
const filename = path$l.join(
|
|
1580
1580
|
app$e.getPath("userData"),
|
|
1581
1581
|
appName$6,
|
|
1582
1582
|
appId,
|
|
@@ -1618,7 +1618,7 @@ const themeController$5 = {
|
|
|
1618
1618
|
*/
|
|
1619
1619
|
deleteThemeForApplication: (win, appId, themeKey) => {
|
|
1620
1620
|
try {
|
|
1621
|
-
const filename = path$
|
|
1621
|
+
const filename = path$l.join(
|
|
1622
1622
|
app$e.getPath("userData"),
|
|
1623
1623
|
appName$6,
|
|
1624
1624
|
appId,
|
|
@@ -1695,8 +1695,8 @@ var themeController_1 = themeController$5;
|
|
|
1695
1695
|
* `/data/`.
|
|
1696
1696
|
*/
|
|
1697
1697
|
|
|
1698
|
-
const path$
|
|
1699
|
-
const fs$
|
|
1698
|
+
const path$k = require$$1$2;
|
|
1699
|
+
const fs$f = require$$0$2;
|
|
1700
1700
|
const { app: app$d } = require$$0$1;
|
|
1701
1701
|
|
|
1702
1702
|
const APP_NAME = "Dashboard";
|
|
@@ -1709,7 +1709,7 @@ function getAllowedRoots$2(category) {
|
|
|
1709
1709
|
const userData = app$d.getPath("userData");
|
|
1710
1710
|
switch (category) {
|
|
1711
1711
|
case "data": {
|
|
1712
|
-
const def = path$
|
|
1712
|
+
const def = path$k.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,11 +1719,11 @@ function getAllowedRoots$2(category) {
|
|
|
1719
1719
|
return override ? [def, override] : [def];
|
|
1720
1720
|
}
|
|
1721
1721
|
case "themes":
|
|
1722
|
-
return [path$
|
|
1722
|
+
return [path$k.join(userData, APP_NAME, "themes")];
|
|
1723
1723
|
case "widgets":
|
|
1724
|
-
return [path$
|
|
1724
|
+
return [path$k.join(userData, "widgets")];
|
|
1725
1725
|
case "plugins":
|
|
1726
|
-
return [path$
|
|
1726
|
+
return [path$k.join(userData, "plugins")];
|
|
1727
1727
|
case "downloads":
|
|
1728
1728
|
return [app$d.getPath("downloads")];
|
|
1729
1729
|
default:
|
|
@@ -1740,13 +1740,13 @@ function getAllowedRoots$2(category) {
|
|
|
1740
1740
|
*/
|
|
1741
1741
|
function readDataDirectoryFromSettings() {
|
|
1742
1742
|
try {
|
|
1743
|
-
const settingsPath = path$
|
|
1743
|
+
const settingsPath = path$k.join(
|
|
1744
1744
|
app$d.getPath("userData"),
|
|
1745
1745
|
APP_NAME,
|
|
1746
1746
|
"settings.json",
|
|
1747
1747
|
);
|
|
1748
|
-
if (!fs$
|
|
1749
|
-
const raw = fs$
|
|
1748
|
+
if (!fs$f.existsSync(settingsPath)) return undefined;
|
|
1749
|
+
const raw = fs$f.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;
|
|
@@ -1772,18 +1772,18 @@ function safePath$3(requested, allowedRoots) {
|
|
|
1772
1772
|
throw new Error("safePath: allowedRoots must be a non-empty array");
|
|
1773
1773
|
}
|
|
1774
1774
|
|
|
1775
|
-
const resolved = path$
|
|
1775
|
+
const resolved = path$k.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$
|
|
1782
|
+
real = fs$f.realpathSync(resolved);
|
|
1783
1783
|
} catch (_e) {
|
|
1784
1784
|
try {
|
|
1785
|
-
const parent = fs$
|
|
1786
|
-
real = path$
|
|
1785
|
+
const parent = fs$f.realpathSync(path$k.dirname(resolved));
|
|
1786
|
+
real = path$k.join(parent, path$k.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$3(requested, allowedRoots) {
|
|
|
1795
1795
|
for (const root of allowedRoots) {
|
|
1796
1796
|
let realRoot = root;
|
|
1797
1797
|
try {
|
|
1798
|
-
if (fs$
|
|
1798
|
+
if (fs$f.existsSync(root)) realRoot = fs$f.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$
|
|
1805
|
+
if (real === realRoot || real.startsWith(realRoot + path$k.sep)) {
|
|
1806
1806
|
return real;
|
|
1807
1807
|
}
|
|
1808
1808
|
}
|
|
@@ -2060,14 +2060,14 @@ var safeJsExecutor$1 = {
|
|
|
2060
2060
|
* - CSV -> JSON
|
|
2061
2061
|
*/
|
|
2062
2062
|
|
|
2063
|
-
var fs$
|
|
2063
|
+
var fs$e = 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$
|
|
2070
|
+
const path$j = require$$1$2;
|
|
2071
2071
|
const { app: app$c } = require$$0$1;
|
|
2072
2072
|
const { ensureDirectoryExistence: ensureDirectoryExistence$1 } = file;
|
|
2073
2073
|
const safeJsExecutor = safeJsExecutor$1;
|
|
@@ -2122,7 +2122,7 @@ let Transform$1 = class Transform {
|
|
|
2122
2122
|
let lineObject = [];
|
|
2123
2123
|
|
|
2124
2124
|
const readInterface = readline.createInterface({
|
|
2125
|
-
input: fs$
|
|
2125
|
+
input: fs$e.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$
|
|
2160
|
+
const readStream = fs$e.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$
|
|
2213
|
+
const xmlFileReadStream = fs$e.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$
|
|
2224
|
+
const writeStream = fs$e.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$
|
|
2276
|
+
const readStream = fs$e
|
|
2277
2277
|
.createReadStream(filepath)
|
|
2278
2278
|
.pipe(csv({ separator: delimiter }));
|
|
2279
|
-
const writeStream = fs$
|
|
2279
|
+
const writeStream = fs$e.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$
|
|
2366
|
-
const resolvedFilepath = path$
|
|
2367
|
-
const resolvedOutFilepath = path$
|
|
2365
|
+
const appDataDir = path$j.join(app$c.getPath("userData"), TRANSFORM_APP_NAME);
|
|
2366
|
+
const resolvedFilepath = path$j.resolve(filepath);
|
|
2367
|
+
const resolvedOutFilepath = path$j.resolve(outFilepath);
|
|
2368
2368
|
|
|
2369
|
-
if (!resolvedFilepath.startsWith(appDataDir + path$
|
|
2369
|
+
if (!resolvedFilepath.startsWith(appDataDir + path$j.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$
|
|
2376
|
+
if (!resolvedOutFilepath.startsWith(appDataDir + path$j.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$
|
|
2387
|
+
if (!fs$e.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$
|
|
2392
|
+
var readStream = fs$e.createReadStream(resolvedFilepath).pipe(parser);
|
|
2393
2393
|
|
|
2394
2394
|
ensureDirectoryExistence$1(resolvedOutFilepath);
|
|
2395
2395
|
|
|
2396
|
-
var writeStream = fs$
|
|
2396
|
+
var writeStream = fs$e.createWriteStream(resolvedOutFilepath);
|
|
2397
2397
|
|
|
2398
2398
|
let sep = "";
|
|
2399
2399
|
let count = 0;
|
|
@@ -2506,8 +2506,8 @@ let Transform$1 = class Transform {
|
|
|
2506
2506
|
var transform = Transform$1;
|
|
2507
2507
|
|
|
2508
2508
|
const { app: app$b } = require$$0$1;
|
|
2509
|
-
var fs$
|
|
2510
|
-
const path$
|
|
2509
|
+
var fs$d = require$$0$2;
|
|
2510
|
+
const path$i = require$$1$2;
|
|
2511
2511
|
const events$5 = events$8;
|
|
2512
2512
|
const { getFileContents: getFileContents$5, writeToFile: writeToFile$2 } = file;
|
|
2513
2513
|
const { safePath: safePath$2, getAllowedRoots: getAllowedRoots$1 } = safePath_1;
|
|
@@ -2532,7 +2532,7 @@ 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$
|
|
2535
|
+
const candidate = path$i.join(
|
|
2536
2536
|
app$b.getPath("userData"),
|
|
2537
2537
|
appName$5,
|
|
2538
2538
|
appId,
|
|
@@ -2691,7 +2691,7 @@ const dataController$1 = {
|
|
|
2691
2691
|
// intent, plus realpath/symlink protection.
|
|
2692
2692
|
const resolvedFilepath = safePath$2(toFilepath, getAllowedRoots$1("data"));
|
|
2693
2693
|
|
|
2694
|
-
const writeStream = fs$
|
|
2694
|
+
const writeStream = fs$d.createWriteStream(resolvedFilepath);
|
|
2695
2695
|
|
|
2696
2696
|
https$3
|
|
2697
2697
|
.get(url, (resp) => {
|
|
@@ -2855,7 +2855,7 @@ 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$
|
|
2858
|
+
const candidate = path$i.join(
|
|
2859
2859
|
app$b.getPath("userData"),
|
|
2860
2860
|
appName$5,
|
|
2861
2861
|
"data",
|
|
@@ -2948,7 +2948,7 @@ const dataController$1 = {
|
|
|
2948
2948
|
try {
|
|
2949
2949
|
if (filename) {
|
|
2950
2950
|
// filename to the pages file (live pages)
|
|
2951
|
-
const fromFilename = path$
|
|
2951
|
+
const fromFilename = path$i.join(
|
|
2952
2952
|
app$b.getPath("userData"),
|
|
2953
2953
|
appName$5,
|
|
2954
2954
|
"data",
|
|
@@ -3030,8 +3030,8 @@ var dataController_1 = dataController$1;
|
|
|
3030
3030
|
*/
|
|
3031
3031
|
|
|
3032
3032
|
const { app: app$a } = require$$0$1;
|
|
3033
|
-
const path$
|
|
3034
|
-
const fs$
|
|
3033
|
+
const path$h = require$$1$2;
|
|
3034
|
+
const fs$c = 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$
|
|
3043
|
-
fs$
|
|
3042
|
+
if (!fs$c.existsSync(destination)) {
|
|
3043
|
+
fs$c.mkdirSync(destination, { recursive: true });
|
|
3044
3044
|
}
|
|
3045
3045
|
|
|
3046
|
-
const files = fs$
|
|
3046
|
+
const files = fs$c.readdirSync(source);
|
|
3047
3047
|
for (const file of files) {
|
|
3048
|
-
const srcPath = path$
|
|
3049
|
-
const destPath = path$
|
|
3050
|
-
const stat = fs$
|
|
3048
|
+
const srcPath = path$h.join(source, file);
|
|
3049
|
+
const destPath = path$h.join(destination, file);
|
|
3050
|
+
const stat = fs$c.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$
|
|
3061
|
+
fs$c.copyFileSync(srcPath, destPath);
|
|
3062
3062
|
}
|
|
3063
3063
|
}
|
|
3064
3064
|
}
|
|
@@ -3074,7 +3074,7 @@ const settingsController$4 = {
|
|
|
3074
3074
|
try {
|
|
3075
3075
|
if (data) {
|
|
3076
3076
|
// <appId>/settings.json
|
|
3077
|
-
const filename = path$
|
|
3077
|
+
const filename = path$h.join(
|
|
3078
3078
|
app$a.getPath("userData"),
|
|
3079
3079
|
appName$4,
|
|
3080
3080
|
configFilename$3,
|
|
@@ -3110,7 +3110,7 @@ const settingsController$4 = {
|
|
|
3110
3110
|
getSettingsForApplication: (win) => {
|
|
3111
3111
|
try {
|
|
3112
3112
|
// <appId>/settings.json
|
|
3113
|
-
const filename = path$
|
|
3113
|
+
const filename = path$h.join(
|
|
3114
3114
|
app$a.getPath("userData"),
|
|
3115
3115
|
appName$4,
|
|
3116
3116
|
configFilename$3,
|
|
@@ -3141,7 +3141,7 @@ const settingsController$4 = {
|
|
|
3141
3141
|
*/
|
|
3142
3142
|
getDataDirectory: (win) => {
|
|
3143
3143
|
try {
|
|
3144
|
-
const settingsPath = path$
|
|
3144
|
+
const settingsPath = path$h.join(
|
|
3145
3145
|
app$a.getPath("userData"),
|
|
3146
3146
|
appName$4,
|
|
3147
3147
|
configFilename$3,
|
|
@@ -3149,7 +3149,7 @@ const settingsController$4 = {
|
|
|
3149
3149
|
const settings = getFileContents$4(settingsPath, {});
|
|
3150
3150
|
const userDataDir =
|
|
3151
3151
|
settings.userDataDirectory ||
|
|
3152
|
-
path$
|
|
3152
|
+
path$h.join(app$a.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,17 +3176,17 @@ const settingsController$4 = {
|
|
|
3176
3176
|
setDataDirectory: (win, newPath) => {
|
|
3177
3177
|
try {
|
|
3178
3178
|
// Validate the path exists and is a directory
|
|
3179
|
-
if (!fs$
|
|
3180
|
-
fs$
|
|
3179
|
+
if (!fs$c.existsSync(newPath)) {
|
|
3180
|
+
fs$c.mkdirSync(newPath, { recursive: true });
|
|
3181
3181
|
}
|
|
3182
3182
|
|
|
3183
|
-
const stats = fs$
|
|
3183
|
+
const stats = fs$c.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$
|
|
3189
|
+
const settingsPath = path$h.join(
|
|
3190
3190
|
app$a.getPath("userData"),
|
|
3191
3191
|
appName$4,
|
|
3192
3192
|
configFilename$3,
|
|
@@ -3220,11 +3220,11 @@ const settingsController$4 = {
|
|
|
3220
3220
|
migrateDataDirectory: (win, oldPath, newPath) => {
|
|
3221
3221
|
try {
|
|
3222
3222
|
// Resolve paths to prevent traversal
|
|
3223
|
-
const resolvedOldPath = path$
|
|
3224
|
-
const resolvedNewPath = path$
|
|
3223
|
+
const resolvedOldPath = path$h.resolve(oldPath);
|
|
3224
|
+
const resolvedNewPath = path$h.resolve(newPath);
|
|
3225
3225
|
|
|
3226
3226
|
// Validate oldPath is the current configured data directory
|
|
3227
|
-
const settingsCheckPath = path$
|
|
3227
|
+
const settingsCheckPath = path$h.join(
|
|
3228
3228
|
app$a.getPath("userData"),
|
|
3229
3229
|
appName$4,
|
|
3230
3230
|
configFilename$3,
|
|
@@ -3232,8 +3232,8 @@ const settingsController$4 = {
|
|
|
3232
3232
|
const currentSettings = getFileContents$4(settingsCheckPath, {});
|
|
3233
3233
|
const currentDataDir =
|
|
3234
3234
|
currentSettings.userDataDirectory ||
|
|
3235
|
-
path$
|
|
3236
|
-
if (resolvedOldPath !== path$
|
|
3235
|
+
path$h.join(app$a.getPath("userData"), appName$4);
|
|
3236
|
+
if (resolvedOldPath !== path$h.resolve(currentDataDir)) {
|
|
3237
3237
|
throw new Error("Source path must be the current data directory");
|
|
3238
3238
|
}
|
|
3239
3239
|
|
|
@@ -3257,19 +3257,19 @@ const settingsController$4 = {
|
|
|
3257
3257
|
}
|
|
3258
3258
|
|
|
3259
3259
|
// Validate paths
|
|
3260
|
-
if (!fs$
|
|
3260
|
+
if (!fs$c.existsSync(resolvedOldPath)) {
|
|
3261
3261
|
throw new Error("Source directory does not exist");
|
|
3262
3262
|
}
|
|
3263
3263
|
|
|
3264
|
-
if (!fs$
|
|
3265
|
-
fs$
|
|
3264
|
+
if (!fs$c.existsSync(resolvedNewPath)) {
|
|
3265
|
+
fs$c.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$
|
|
3272
|
+
const settingsPath = path$h.join(
|
|
3273
3273
|
app$a.getPath("userData"),
|
|
3274
3274
|
appName$4,
|
|
3275
3275
|
configFilename$3,
|
|
@@ -3935,7 +3935,7 @@ function requireProviderController () {
|
|
|
3935
3935
|
}
|
|
3936
3936
|
|
|
3937
3937
|
const { app: app$9 } = require$$0$1;
|
|
3938
|
-
const path$
|
|
3938
|
+
const path$g = 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,7 +3955,7 @@ 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$
|
|
3958
|
+
const filename = path$g.join(
|
|
3959
3959
|
app$9.getPath("userData"),
|
|
3960
3960
|
appName$3,
|
|
3961
3961
|
appId,
|
|
@@ -3988,7 +3988,7 @@ const layoutController$1 = {
|
|
|
3988
3988
|
*/
|
|
3989
3989
|
listLayoutsForApplication: (win, appId) => {
|
|
3990
3990
|
try {
|
|
3991
|
-
const filename = path$
|
|
3991
|
+
const filename = path$g.join(
|
|
3992
3992
|
app$9.getPath("userData"),
|
|
3993
3993
|
appName$3,
|
|
3994
3994
|
appId,
|
|
@@ -21192,8 +21192,8 @@ streamableHttp$1.StreamableHTTPClientTransport = StreamableHTTPClientTransport$1
|
|
|
21192
21192
|
* clearCache() → void // test-only
|
|
21193
21193
|
*/
|
|
21194
21194
|
|
|
21195
|
-
const fs$
|
|
21196
|
-
const path$
|
|
21195
|
+
const fs$b = require$$0$2;
|
|
21196
|
+
const path$f = require$$1$2;
|
|
21197
21197
|
const { app: app$8 } = require$$0$1;
|
|
21198
21198
|
|
|
21199
21199
|
const FILE_NAME = "widgetMcpGrants.json";
|
|
@@ -21203,14 +21203,14 @@ const FILE_NAME = "widgetMcpGrants.json";
|
|
|
21203
21203
|
let _cache$1 = null;
|
|
21204
21204
|
|
|
21205
21205
|
function grantsFilePath() {
|
|
21206
|
-
return path$
|
|
21206
|
+
return path$f.join(app$8.getPath("userData"), FILE_NAME);
|
|
21207
21207
|
}
|
|
21208
21208
|
|
|
21209
21209
|
function loadFromDisk() {
|
|
21210
21210
|
const p = grantsFilePath();
|
|
21211
|
-
if (!fs$
|
|
21211
|
+
if (!fs$b.existsSync(p)) return {};
|
|
21212
21212
|
try {
|
|
21213
|
-
const raw = fs$
|
|
21213
|
+
const raw = fs$b.readFileSync(p, "utf8");
|
|
21214
21214
|
const parsed = JSON.parse(raw);
|
|
21215
21215
|
if (!parsed || typeof parsed !== "object") return {};
|
|
21216
21216
|
return parsed;
|
|
@@ -21230,16 +21230,27 @@ function writeToDisk(data) {
|
|
|
21230
21230
|
const tmp = p + ".tmp";
|
|
21231
21231
|
// Ensure parent dir exists (userData should already, but be defensive
|
|
21232
21232
|
// for first-launch / freshly-cleared profile cases).
|
|
21233
|
-
fs$
|
|
21234
|
-
fs$
|
|
21235
|
-
fs$
|
|
21233
|
+
fs$b.mkdirSync(path$f.dirname(p), { recursive: true });
|
|
21234
|
+
fs$b.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf8");
|
|
21235
|
+
fs$b.renameSync(tmp, p);
|
|
21236
21236
|
}
|
|
21237
21237
|
|
|
21238
|
+
// Recognized origins for a persisted grant. "declared" means the user
|
|
21239
|
+
// approved against the developer's declared dash.permissions.mcp block;
|
|
21240
|
+
// "discovered" means the install-time scanner produced a synthetic
|
|
21241
|
+
// manifest the user approved; "manual" means the user typed entries
|
|
21242
|
+
// themselves in Settings → Privacy & Security with no manifest backing.
|
|
21243
|
+
// Other values are dropped on persist (legacy grants stay null).
|
|
21244
|
+
const ALLOWED_GRANT_ORIGINS = new Set(["declared", "discovered", "manual"]);
|
|
21245
|
+
|
|
21238
21246
|
/**
|
|
21239
21247
|
* Sanitize a perms object before persisting. Drops unknown keys, coerces
|
|
21240
21248
|
* arrays of strings, and silently ignores malformed servers. Mirrors the
|
|
21241
21249
|
* shape produced by parseManifestPermissions so the gate reads either
|
|
21242
21250
|
* declared or granted with the same code path.
|
|
21251
|
+
*
|
|
21252
|
+
* Optional `grantOrigin` field is preserved when it's one of the
|
|
21253
|
+
* recognized values; bogus values are dropped.
|
|
21243
21254
|
*/
|
|
21244
21255
|
function sanitizePerms(perms) {
|
|
21245
21256
|
if (!perms || typeof perms !== "object") return null;
|
|
@@ -21262,7 +21273,14 @@ function sanitizePerms(perms) {
|
|
|
21262
21273
|
: [],
|
|
21263
21274
|
};
|
|
21264
21275
|
}
|
|
21265
|
-
|
|
21276
|
+
const out = { servers };
|
|
21277
|
+
if (
|
|
21278
|
+
typeof perms.grantOrigin === "string" &&
|
|
21279
|
+
ALLOWED_GRANT_ORIGINS.has(perms.grantOrigin)
|
|
21280
|
+
) {
|
|
21281
|
+
out.grantOrigin = perms.grantOrigin;
|
|
21282
|
+
}
|
|
21283
|
+
return out;
|
|
21266
21284
|
}
|
|
21267
21285
|
|
|
21268
21286
|
function getGrant$2(widgetId) {
|
|
@@ -21358,6 +21376,7 @@ var grantedPermissions = {
|
|
|
21358
21376
|
revokeServer: revokeServer$1,
|
|
21359
21377
|
listAllGrants: listAllGrants$1,
|
|
21360
21378
|
clearCache: clearCache$1,
|
|
21379
|
+
ALLOWED_GRANT_ORIGINS,
|
|
21361
21380
|
};
|
|
21362
21381
|
|
|
21363
21382
|
/**
|
|
@@ -21719,8 +21738,8 @@ const {
|
|
|
21719
21738
|
const {
|
|
21720
21739
|
StreamableHTTPClientTransport,
|
|
21721
21740
|
} = streamableHttp$1;
|
|
21722
|
-
const path$
|
|
21723
|
-
const fs$
|
|
21741
|
+
const path$e = require$$1$2;
|
|
21742
|
+
const fs$a = require$$0$2;
|
|
21724
21743
|
const os$2 = require$$2$1;
|
|
21725
21744
|
const responseCache$2 = responseCache_1;
|
|
21726
21745
|
const { gateToolCall } = permissionGate;
|
|
@@ -21734,13 +21753,13 @@ const { app: app$7 } = require$$0$1;
|
|
|
21734
21753
|
// and electron/mcp/permissionGate.js for context.
|
|
21735
21754
|
function isWidgetPermissionEnforcementEnabled() {
|
|
21736
21755
|
try {
|
|
21737
|
-
const settingsPath = path$
|
|
21756
|
+
const settingsPath = path$e.join(
|
|
21738
21757
|
app$7.getPath("userData"),
|
|
21739
21758
|
"Dashboard",
|
|
21740
21759
|
"settings.json",
|
|
21741
21760
|
);
|
|
21742
|
-
if (!fs$
|
|
21743
|
-
const raw = fs$
|
|
21761
|
+
if (!fs$a.existsSync(settingsPath)) return false;
|
|
21762
|
+
const raw = fs$a.readFileSync(settingsPath, "utf8");
|
|
21744
21763
|
const settings = JSON.parse(raw);
|
|
21745
21764
|
return Boolean(settings?.security?.enforceWidgetMcpPermissions);
|
|
21746
21765
|
} catch (_e) {
|
|
@@ -21893,7 +21912,7 @@ function getShellPath$1() {
|
|
|
21893
21912
|
fallbackDirs.push(`${home}/.nodenv/shims`);
|
|
21894
21913
|
try {
|
|
21895
21914
|
const nvmDir = `${home}/.nvm/versions/node`;
|
|
21896
|
-
const versions = fs$
|
|
21915
|
+
const versions = fs$a.readdirSync(nvmDir).sort();
|
|
21897
21916
|
if (versions.length > 0) {
|
|
21898
21917
|
// Find the highest compatible version (v18/v20/v22)
|
|
21899
21918
|
for (let i = versions.length - 1; i >= 0; i--) {
|
|
@@ -22029,15 +22048,15 @@ async function refreshGoogleOAuthToken(tokenRefresh) {
|
|
|
22029
22048
|
const credPath = tokenRefresh.credentialsPath.replace(/^~/, home);
|
|
22030
22049
|
const keysPath = tokenRefresh.oauthKeysPath.replace(/^~/, home);
|
|
22031
22050
|
|
|
22032
|
-
if (!fs$
|
|
22051
|
+
if (!fs$a.existsSync(credPath) || !fs$a.existsSync(keysPath)) {
|
|
22033
22052
|
console.log(
|
|
22034
22053
|
"[mcpController] Token refresh skipped: credential files not found",
|
|
22035
22054
|
);
|
|
22036
22055
|
return;
|
|
22037
22056
|
}
|
|
22038
22057
|
|
|
22039
|
-
const credentials = JSON.parse(fs$
|
|
22040
|
-
const keysFile = JSON.parse(fs$
|
|
22058
|
+
const credentials = JSON.parse(fs$a.readFileSync(credPath, "utf8"));
|
|
22059
|
+
const keysFile = JSON.parse(fs$a.readFileSync(keysPath, "utf8"));
|
|
22041
22060
|
const keyData = keysFile.installed || keysFile.web;
|
|
22042
22061
|
|
|
22043
22062
|
if (
|
|
@@ -22106,7 +22125,7 @@ async function refreshGoogleOAuthToken(tokenRefresh) {
|
|
|
22106
22125
|
credentials.refresh_token = body.refresh_token;
|
|
22107
22126
|
}
|
|
22108
22127
|
|
|
22109
|
-
fs$
|
|
22128
|
+
fs$a.writeFileSync(credPath, JSON.stringify(credentials, null, 2));
|
|
22110
22129
|
console.log("[mcpController] Google OAuth token refreshed successfully");
|
|
22111
22130
|
}
|
|
22112
22131
|
|
|
@@ -22293,7 +22312,7 @@ const mcpController$3 = {
|
|
|
22293
22312
|
}
|
|
22294
22313
|
|
|
22295
22314
|
// Interpolate {{MCP_DIR}} in args to resolve local MCP server scripts
|
|
22296
|
-
const mcpDir = path$
|
|
22315
|
+
const mcpDir = path$e.join(__dirname, "..", "mcp");
|
|
22297
22316
|
for (let i = 0; i < args.length; i++) {
|
|
22298
22317
|
if (
|
|
22299
22318
|
typeof args[i] === "string" &&
|
|
@@ -22731,20 +22750,20 @@ const mcpController$3 = {
|
|
|
22731
22750
|
*/
|
|
22732
22751
|
getCatalog: (win) => {
|
|
22733
22752
|
try {
|
|
22734
|
-
const catalogPath = path$
|
|
22753
|
+
const catalogPath = path$e.join(
|
|
22735
22754
|
__dirname,
|
|
22736
22755
|
"..",
|
|
22737
22756
|
"mcp",
|
|
22738
22757
|
"mcpServerCatalog.json",
|
|
22739
22758
|
);
|
|
22740
22759
|
|
|
22741
|
-
if (!fs$
|
|
22760
|
+
if (!fs$a.existsSync(catalogPath)) {
|
|
22742
22761
|
return {
|
|
22743
22762
|
catalog: [],
|
|
22744
22763
|
};
|
|
22745
22764
|
}
|
|
22746
22765
|
|
|
22747
|
-
const catalogData = fs$
|
|
22766
|
+
const catalogData = fs$a.readFileSync(catalogPath, "utf8");
|
|
22748
22767
|
const catalog = JSON.parse(catalogData);
|
|
22749
22768
|
|
|
22750
22769
|
return {
|
|
@@ -22772,18 +22791,18 @@ const mcpController$3 = {
|
|
|
22772
22791
|
*/
|
|
22773
22792
|
getKnownExternalCatalog: () => {
|
|
22774
22793
|
try {
|
|
22775
|
-
const catalogPath = path$
|
|
22794
|
+
const catalogPath = path$e.join(
|
|
22776
22795
|
__dirname,
|
|
22777
22796
|
"..",
|
|
22778
22797
|
"mcp",
|
|
22779
22798
|
"knownExternalMcpServers.json",
|
|
22780
22799
|
);
|
|
22781
22800
|
|
|
22782
|
-
if (!fs$
|
|
22801
|
+
if (!fs$a.existsSync(catalogPath)) {
|
|
22783
22802
|
return { success: true, servers: [] };
|
|
22784
22803
|
}
|
|
22785
22804
|
|
|
22786
|
-
const catalogData = fs$
|
|
22805
|
+
const catalogData = fs$a.readFileSync(catalogPath, "utf8");
|
|
22787
22806
|
const catalog = JSON.parse(catalogData);
|
|
22788
22807
|
|
|
22789
22808
|
return {
|
|
@@ -22848,8 +22867,8 @@ const mcpController$3 = {
|
|
|
22848
22867
|
const destPath = to.replace(/^~/, os$2.homedir());
|
|
22849
22868
|
const destDir = require$$1$2.dirname(destPath);
|
|
22850
22869
|
try {
|
|
22851
|
-
fs$
|
|
22852
|
-
fs$
|
|
22870
|
+
fs$a.mkdirSync(destDir, { recursive: true });
|
|
22871
|
+
fs$a.copyFileSync(sourcePath, destPath);
|
|
22853
22872
|
} catch (err) {
|
|
22854
22873
|
return {
|
|
22855
22874
|
error: true,
|
|
@@ -22885,7 +22904,7 @@ const mcpController$3 = {
|
|
|
22885
22904
|
}
|
|
22886
22905
|
|
|
22887
22906
|
// Interpolate {{MCP_DIR}} in authCommand args (same as startServer)
|
|
22888
|
-
const mcpDir = path$
|
|
22907
|
+
const mcpDir = path$e.join(__dirname, "..", "mcp");
|
|
22889
22908
|
const resolvedArgs = (authCommand.args || []).map((arg) =>
|
|
22890
22909
|
typeof arg === "string" && arg.includes("{{MCP_DIR}}")
|
|
22891
22910
|
? arg.replace(/\{\{MCP_DIR\}\}/g, mcpDir)
|
|
@@ -23433,8 +23452,8 @@ function commonjsRequire(path) {
|
|
|
23433
23452
|
* Runs in the Electron main process at widget install time.
|
|
23434
23453
|
*/
|
|
23435
23454
|
|
|
23436
|
-
const fs$
|
|
23437
|
-
const path$
|
|
23455
|
+
const fs$9 = require$$0$2;
|
|
23456
|
+
const path$d = require$$1$2;
|
|
23438
23457
|
|
|
23439
23458
|
/**
|
|
23440
23459
|
* Structured error thrown by compileWidget() when the underlying
|
|
@@ -23469,7 +23488,7 @@ function getEsbuildDiagnostics() {
|
|
|
23469
23488
|
|
|
23470
23489
|
try {
|
|
23471
23490
|
const pkgJsonPath = require.resolve("esbuild/package.json");
|
|
23472
|
-
diagnostics.esbuildPackageDir = path$
|
|
23491
|
+
diagnostics.esbuildPackageDir = path$d.dirname(pkgJsonPath);
|
|
23473
23492
|
diagnostics.esbuildVersion = commonjsRequire(pkgJsonPath).version;
|
|
23474
23493
|
} catch (err) {
|
|
23475
23494
|
diagnostics.esbuildResolveError = err.message;
|
|
@@ -23479,15 +23498,15 @@ function getEsbuildDiagnostics() {
|
|
|
23479
23498
|
const archPkgJson = require.resolve(
|
|
23480
23499
|
`${diagnostics.archPackage}/package.json`,
|
|
23481
23500
|
);
|
|
23482
|
-
const archDir = path$
|
|
23501
|
+
const archDir = path$d.dirname(archPkgJson);
|
|
23483
23502
|
// esbuild's native binary on macOS/Linux is bin/esbuild;
|
|
23484
23503
|
// on Windows it's esbuild.exe at the package root.
|
|
23485
23504
|
const candidate =
|
|
23486
23505
|
process.platform === "win32"
|
|
23487
|
-
? path$
|
|
23488
|
-
: path$
|
|
23506
|
+
? path$d.join(archDir, "esbuild.exe")
|
|
23507
|
+
: path$d.join(archDir, "bin", "esbuild");
|
|
23489
23508
|
diagnostics.nativeBinaryPath = candidate;
|
|
23490
|
-
diagnostics.nativeBinaryExists = fs$
|
|
23509
|
+
diagnostics.nativeBinaryExists = fs$9.existsSync(candidate);
|
|
23491
23510
|
} catch (err) {
|
|
23492
23511
|
diagnostics.archResolveError = err.message;
|
|
23493
23512
|
}
|
|
@@ -23533,26 +23552,26 @@ async function healthCheck() {
|
|
|
23533
23552
|
* @returns {string|null} Path to the widgets/ directory, or null
|
|
23534
23553
|
*/
|
|
23535
23554
|
function findWidgetsDir$2(widgetPath) {
|
|
23536
|
-
const direct = path$
|
|
23537
|
-
if (fs$
|
|
23555
|
+
const direct = path$d.join(widgetPath, "widgets");
|
|
23556
|
+
if (fs$9.existsSync(direct)) {
|
|
23538
23557
|
return direct;
|
|
23539
23558
|
}
|
|
23540
23559
|
|
|
23541
23560
|
// Check configs/widgets/ (packageZip.js nests .dash.js files here)
|
|
23542
|
-
const configsWidgets = path$
|
|
23543
|
-
if (fs$
|
|
23561
|
+
const configsWidgets = path$d.join(widgetPath, "configs", "widgets");
|
|
23562
|
+
if (fs$9.existsSync(configsWidgets)) {
|
|
23544
23563
|
return configsWidgets;
|
|
23545
23564
|
}
|
|
23546
23565
|
|
|
23547
23566
|
// Check configs/ directory (used by packageZip.js for distributed widgets)
|
|
23548
|
-
const configs = path$
|
|
23549
|
-
if (fs$
|
|
23567
|
+
const configs = path$d.join(widgetPath, "configs");
|
|
23568
|
+
if (fs$9.existsSync(configs)) {
|
|
23550
23569
|
return configs;
|
|
23551
23570
|
}
|
|
23552
23571
|
|
|
23553
23572
|
// Check one level deeper for nested ZIP extraction
|
|
23554
23573
|
try {
|
|
23555
|
-
const entries = fs$
|
|
23574
|
+
const entries = fs$9.readdirSync(widgetPath, { withFileTypes: true });
|
|
23556
23575
|
const subdirs = entries.filter(
|
|
23557
23576
|
(e) =>
|
|
23558
23577
|
e.isDirectory() &&
|
|
@@ -23562,8 +23581,8 @@ function findWidgetsDir$2(widgetPath) {
|
|
|
23562
23581
|
);
|
|
23563
23582
|
|
|
23564
23583
|
for (const subdir of subdirs) {
|
|
23565
|
-
const nested = path$
|
|
23566
|
-
if (fs$
|
|
23584
|
+
const nested = path$d.join(widgetPath, subdir.name, "widgets");
|
|
23585
|
+
if (fs$9.existsSync(nested)) {
|
|
23567
23586
|
console.log(`[WidgetCompiler] Found nested widgets/ at ${nested}`);
|
|
23568
23587
|
return nested;
|
|
23569
23588
|
}
|
|
@@ -23597,7 +23616,7 @@ async function compileWidget$1(widgetPath) {
|
|
|
23597
23616
|
}
|
|
23598
23617
|
|
|
23599
23618
|
// Discover .dash.js config files
|
|
23600
|
-
const files = fs$
|
|
23619
|
+
const files = fs$9.readdirSync(widgetsDir);
|
|
23601
23620
|
const dashFiles = files.filter((f) => f.endsWith(".dash.js"));
|
|
23602
23621
|
|
|
23603
23622
|
if (dashFiles.length === 0) {
|
|
@@ -23611,15 +23630,15 @@ async function compileWidget$1(widgetPath) {
|
|
|
23611
23630
|
// Compute relative path from the entry file (in widgetPath) to widgetsDir,
|
|
23612
23631
|
// since widgetsDir may be nested (e.g., ./weather-widget/widgets/).
|
|
23613
23632
|
const relWidgetsDir =
|
|
23614
|
-
"./" + path$
|
|
23633
|
+
"./" + path$d.relative(widgetPath, widgetsDir).split(path$d.sep).join("/");
|
|
23615
23634
|
const imports = [];
|
|
23616
23635
|
const exportParts = [];
|
|
23617
23636
|
|
|
23618
23637
|
for (const dashFile of dashFiles) {
|
|
23619
23638
|
const componentName = dashFile.replace(".dash.js", "");
|
|
23620
23639
|
const componentFile = `${componentName}.js`;
|
|
23621
|
-
const componentFilePath = path$
|
|
23622
|
-
const hasComponent = fs$
|
|
23640
|
+
const componentFilePath = path$d.join(widgetsDir, componentFile);
|
|
23641
|
+
const hasComponent = fs$9.existsSync(componentFilePath);
|
|
23623
23642
|
|
|
23624
23643
|
// Import the config (always)
|
|
23625
23644
|
imports.push(
|
|
@@ -23651,17 +23670,17 @@ async function compileWidget$1(widgetPath) {
|
|
|
23651
23670
|
const entryContent = [...imports, "", ...exportParts, ""].join("\n");
|
|
23652
23671
|
|
|
23653
23672
|
// Write temporary entry file in the widget root
|
|
23654
|
-
const entryPath = path$
|
|
23655
|
-
const distDir = path$
|
|
23656
|
-
const outPath = path$
|
|
23673
|
+
const entryPath = path$d.join(widgetPath, "__compile_entry.js");
|
|
23674
|
+
const distDir = path$d.join(widgetPath, "dist");
|
|
23675
|
+
const outPath = path$d.join(distDir, "index.cjs.js");
|
|
23657
23676
|
|
|
23658
23677
|
try {
|
|
23659
23678
|
// Ensure dist/ directory exists
|
|
23660
|
-
if (!fs$
|
|
23661
|
-
fs$
|
|
23679
|
+
if (!fs$9.existsSync(distDir)) {
|
|
23680
|
+
fs$9.mkdirSync(distDir, { recursive: true });
|
|
23662
23681
|
}
|
|
23663
23682
|
|
|
23664
|
-
fs$
|
|
23683
|
+
fs$9.writeFileSync(entryPath, entryContent, "utf8");
|
|
23665
23684
|
|
|
23666
23685
|
console.log(
|
|
23667
23686
|
`[WidgetCompiler] Compiling ${dashFiles.length} component(s) from ${widgetPath}`,
|
|
@@ -23717,8 +23736,8 @@ async function compileWidget$1(widgetPath) {
|
|
|
23717
23736
|
} finally {
|
|
23718
23737
|
// Clean up temporary entry file
|
|
23719
23738
|
try {
|
|
23720
|
-
if (fs$
|
|
23721
|
-
fs$
|
|
23739
|
+
if (fs$9.existsSync(entryPath)) {
|
|
23740
|
+
fs$9.unlinkSync(entryPath);
|
|
23722
23741
|
}
|
|
23723
23742
|
} catch (cleanupError) {
|
|
23724
23743
|
// Non-fatal
|
|
@@ -23750,8 +23769,8 @@ var widgetCompiler$1 = {
|
|
|
23750
23769
|
* Integrates with ComponentManager for automatic registration
|
|
23751
23770
|
*/
|
|
23752
23771
|
|
|
23753
|
-
const fs$
|
|
23754
|
-
const path$
|
|
23772
|
+
const fs$8 = require$$0$2;
|
|
23773
|
+
const path$c = require$$1$2;
|
|
23755
23774
|
const vm = require$$2$2;
|
|
23756
23775
|
const { findWidgetsDir: findWidgetsDir$1 } = widgetCompiler$1;
|
|
23757
23776
|
|
|
@@ -23839,14 +23858,14 @@ class DynamicWidgetLoader {
|
|
|
23839
23858
|
);
|
|
23840
23859
|
|
|
23841
23860
|
const widgetsDir =
|
|
23842
|
-
findWidgetsDir$1(widgetPath) || path$
|
|
23843
|
-
const componentPath = path$
|
|
23844
|
-
const configPath = path$
|
|
23861
|
+
findWidgetsDir$1(widgetPath) || path$c.join(widgetPath, "widgets");
|
|
23862
|
+
const componentPath = path$c.join(widgetsDir, `${componentName}.js`);
|
|
23863
|
+
const configPath = path$c.join(widgetsDir, `${componentName}.dash.js`);
|
|
23845
23864
|
|
|
23846
|
-
if (!fs$
|
|
23865
|
+
if (!fs$8.existsSync(componentPath)) {
|
|
23847
23866
|
throw new Error(`Component file not found: ${componentPath}`);
|
|
23848
23867
|
}
|
|
23849
|
-
if (!fs$
|
|
23868
|
+
if (!fs$8.existsSync(configPath)) {
|
|
23850
23869
|
throw new Error(`Config file not found: ${configPath}`);
|
|
23851
23870
|
}
|
|
23852
23871
|
|
|
@@ -23897,7 +23916,7 @@ class DynamicWidgetLoader {
|
|
|
23897
23916
|
*/
|
|
23898
23917
|
async loadConfigFile(configPath) {
|
|
23899
23918
|
try {
|
|
23900
|
-
const source = fs$
|
|
23919
|
+
const source = fs$8.readFileSync(configPath, "utf8");
|
|
23901
23920
|
|
|
23902
23921
|
let exportMatch = source.match(/export\s+default\s+({[\s\S]*});?\s*$/);
|
|
23903
23922
|
|
|
@@ -23965,7 +23984,7 @@ class DynamicWidgetLoader {
|
|
|
23965
23984
|
return [];
|
|
23966
23985
|
}
|
|
23967
23986
|
|
|
23968
|
-
const files = fs$
|
|
23987
|
+
const files = fs$8.readdirSync(widgetsDir);
|
|
23969
23988
|
const widgets = new Set();
|
|
23970
23989
|
|
|
23971
23990
|
files.forEach((file) => {
|
|
@@ -24041,8 +24060,8 @@ var dynamicWidgetLoaderExports = dynamicWidgetLoader$3.exports;
|
|
|
24041
24060
|
* Test-only. Drops the in-process cache so tests can re-read.
|
|
24042
24061
|
*/
|
|
24043
24062
|
|
|
24044
|
-
const fs$
|
|
24045
|
-
const path$
|
|
24063
|
+
const fs$7 = require$$0$2;
|
|
24064
|
+
const path$b = require$$1$2;
|
|
24046
24065
|
const os$1 = require$$2$1;
|
|
24047
24066
|
const { app: app$6 } = require$$0$1;
|
|
24048
24067
|
|
|
@@ -24059,7 +24078,7 @@ function expandHome(p) {
|
|
|
24059
24078
|
if (typeof p !== "string" || !p) return p;
|
|
24060
24079
|
if (p === "~") return os$1.homedir();
|
|
24061
24080
|
if (p.startsWith("~/") || p.startsWith("~\\")) {
|
|
24062
|
-
return path$
|
|
24081
|
+
return path$b.join(os$1.homedir(), p.slice(2));
|
|
24063
24082
|
}
|
|
24064
24083
|
return p;
|
|
24065
24084
|
}
|
|
@@ -24099,10 +24118,10 @@ function parseManifestPermissions(packageJson) {
|
|
|
24099
24118
|
*/
|
|
24100
24119
|
function resolveWidgetPackagePath(widgetId) {
|
|
24101
24120
|
if (typeof widgetId !== "string" || !widgetId) return null;
|
|
24102
|
-
const widgetsRoot = path$
|
|
24121
|
+
const widgetsRoot = path$b.join(app$6.getPath("userData"), "widgets");
|
|
24103
24122
|
// Split scope from name for "@scope/name" form.
|
|
24104
24123
|
const parts = widgetId.startsWith("@") ? widgetId.split("/") : [widgetId];
|
|
24105
|
-
return path$
|
|
24124
|
+
return path$b.join(widgetsRoot, ...parts, "package.json");
|
|
24106
24125
|
}
|
|
24107
24126
|
|
|
24108
24127
|
/**
|
|
@@ -24116,12 +24135,12 @@ function resolveWidgetPackagePath(widgetId) {
|
|
|
24116
24135
|
function getWidgetMcpPermissions$1(widgetId) {
|
|
24117
24136
|
if (_cache.has(widgetId)) return _cache.get(widgetId);
|
|
24118
24137
|
const pkgPath = resolveWidgetPackagePath(widgetId);
|
|
24119
|
-
if (!pkgPath || !fs$
|
|
24138
|
+
if (!pkgPath || !fs$7.existsSync(pkgPath)) {
|
|
24120
24139
|
_cache.set(widgetId, null);
|
|
24121
24140
|
return null;
|
|
24122
24141
|
}
|
|
24123
24142
|
try {
|
|
24124
|
-
const raw = fs$
|
|
24143
|
+
const raw = fs$7.readFileSync(pkgPath, "utf8");
|
|
24125
24144
|
const pkg = JSON.parse(raw);
|
|
24126
24145
|
const perms = parseManifestPermissions(pkg);
|
|
24127
24146
|
_cache.set(widgetId, perms);
|
|
@@ -24149,6 +24168,200 @@ var widgetPermissions = {
|
|
|
24149
24168
|
clearCache,
|
|
24150
24169
|
};
|
|
24151
24170
|
|
|
24171
|
+
/**
|
|
24172
|
+
* manifestScanner.js
|
|
24173
|
+
*
|
|
24174
|
+
* Literal-only static scanner for widget MCP usage. Three callers:
|
|
24175
|
+
* 1. publish-time CLI (`dash-scan-manifest`) — the dev runs it before
|
|
24176
|
+
* shipping a widget; it diffs detected calls against the package's
|
|
24177
|
+
* `dash.permissions.mcp` block.
|
|
24178
|
+
* 2. install-time hook in widgetRegistry — when a widget arrives
|
|
24179
|
+
* without a manifest, the scanner is run on the installed source
|
|
24180
|
+
* and the result is offered as a "discovered" consent prompt.
|
|
24181
|
+
* 3. Future: lints during widget builds, IDE plugins, etc.
|
|
24182
|
+
*
|
|
24183
|
+
* Scope: detects literal-string `callTool("server","tool", ...)` and
|
|
24184
|
+
* `useMcpProvider("server")` patterns. Anything dynamic (variable,
|
|
24185
|
+
* template literal, function arg) is recorded as a `warnings[]` entry,
|
|
24186
|
+
* NOT a silent miss.
|
|
24187
|
+
*
|
|
24188
|
+
* This is a *linter*, not a security mechanism. The runtime gate
|
|
24189
|
+
* (Slices 1-3) is the actual boundary.
|
|
24190
|
+
*/
|
|
24191
|
+
|
|
24192
|
+
const fs$6 = require$$0$2;
|
|
24193
|
+
const path$a = require$$1$2;
|
|
24194
|
+
|
|
24195
|
+
const SOURCE_EXTENSIONS = new Set([
|
|
24196
|
+
".js",
|
|
24197
|
+
".jsx",
|
|
24198
|
+
".ts",
|
|
24199
|
+
".tsx",
|
|
24200
|
+
".mjs",
|
|
24201
|
+
".cjs",
|
|
24202
|
+
]);
|
|
24203
|
+
const SCAN_FILE_LIMIT = 200;
|
|
24204
|
+
|
|
24205
|
+
const CALL_TOOL_REGEX =
|
|
24206
|
+
/(?:mainApi\.mcp\.|window\.mainApi\.mcp\.|\b)callTool\s*\(\s*([^,)]+?)\s*,\s*([^,)]+?)\s*[,)]/g;
|
|
24207
|
+
|
|
24208
|
+
const USE_PROVIDER_REGEX = /useMcpProvider\s*\(\s*([^,)]+?)\s*[,)]/g;
|
|
24209
|
+
|
|
24210
|
+
function stripComments(src) {
|
|
24211
|
+
return src
|
|
24212
|
+
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
24213
|
+
.replace(/(^|[^:])\/\/.*$/gm, "$1");
|
|
24214
|
+
}
|
|
24215
|
+
|
|
24216
|
+
function tryLiteralString(arg) {
|
|
24217
|
+
if (typeof arg !== "string") return null;
|
|
24218
|
+
const trimmed = arg.trim();
|
|
24219
|
+
const m =
|
|
24220
|
+
/^"([^"\\]*(?:\\.[^"\\]*)*)"$/.exec(trimmed) ||
|
|
24221
|
+
/^'([^'\\]*(?:\\.[^'\\]*)*)'$/.exec(trimmed);
|
|
24222
|
+
if (!m) return null;
|
|
24223
|
+
return m[1];
|
|
24224
|
+
}
|
|
24225
|
+
|
|
24226
|
+
function lineNumberOf(src, charIndex) {
|
|
24227
|
+
let n = 1;
|
|
24228
|
+
for (let i = 0; i < charIndex && i < src.length; i++) {
|
|
24229
|
+
if (src.charCodeAt(i) === 10) n++;
|
|
24230
|
+
}
|
|
24231
|
+
return n;
|
|
24232
|
+
}
|
|
24233
|
+
|
|
24234
|
+
function readSourceFiles(dir) {
|
|
24235
|
+
const result = [];
|
|
24236
|
+
const skipDirs = new Set([
|
|
24237
|
+
"node_modules",
|
|
24238
|
+
"dist",
|
|
24239
|
+
"package",
|
|
24240
|
+
"build",
|
|
24241
|
+
".git",
|
|
24242
|
+
]);
|
|
24243
|
+
function walk(current, relBase) {
|
|
24244
|
+
let entries;
|
|
24245
|
+
try {
|
|
24246
|
+
entries = fs$6.readdirSync(current, { withFileTypes: true });
|
|
24247
|
+
} catch {
|
|
24248
|
+
return;
|
|
24249
|
+
}
|
|
24250
|
+
for (const entry of entries) {
|
|
24251
|
+
const abs = path$a.join(current, entry.name);
|
|
24252
|
+
const rel = relBase ? path$a.join(relBase, entry.name) : entry.name;
|
|
24253
|
+
if (entry.isDirectory()) {
|
|
24254
|
+
if (skipDirs.has(entry.name)) continue;
|
|
24255
|
+
walk(abs, rel);
|
|
24256
|
+
} else if (entry.isFile()) {
|
|
24257
|
+
const ext = path$a.extname(entry.name).toLowerCase();
|
|
24258
|
+
if (!SOURCE_EXTENSIONS.has(ext)) continue;
|
|
24259
|
+
if (result.length >= SCAN_FILE_LIMIT) return;
|
|
24260
|
+
try {
|
|
24261
|
+
result.push({
|
|
24262
|
+
relPath: rel,
|
|
24263
|
+
source: fs$6.readFileSync(abs, "utf8"),
|
|
24264
|
+
});
|
|
24265
|
+
} catch {
|
|
24266
|
+
// unreadable — skip
|
|
24267
|
+
}
|
|
24268
|
+
}
|
|
24269
|
+
}
|
|
24270
|
+
}
|
|
24271
|
+
walk(dir, "");
|
|
24272
|
+
return result;
|
|
24273
|
+
}
|
|
24274
|
+
|
|
24275
|
+
function scanForMcpUsage(input) {
|
|
24276
|
+
if (!input || typeof input !== "object") {
|
|
24277
|
+
return { servers: {}, warnings: [] };
|
|
24278
|
+
}
|
|
24279
|
+
|
|
24280
|
+
let fileList = [];
|
|
24281
|
+
if (input.files && typeof input.files === "object") {
|
|
24282
|
+
for (const [relPath, source] of Object.entries(input.files)) {
|
|
24283
|
+
const ext = path$a.extname(relPath).toLowerCase();
|
|
24284
|
+
if (!SOURCE_EXTENSIONS.has(ext)) continue;
|
|
24285
|
+
if (typeof source !== "string") continue;
|
|
24286
|
+
fileList.push({ relPath, source });
|
|
24287
|
+
}
|
|
24288
|
+
} else if (typeof input.dir === "string" && input.dir) {
|
|
24289
|
+
fileList = readSourceFiles(input.dir);
|
|
24290
|
+
}
|
|
24291
|
+
|
|
24292
|
+
const servers = {};
|
|
24293
|
+
const warnings = [];
|
|
24294
|
+
|
|
24295
|
+
function ensureServer(name) {
|
|
24296
|
+
if (!servers[name]) servers[name] = { tools: new Set() };
|
|
24297
|
+
return servers[name];
|
|
24298
|
+
}
|
|
24299
|
+
|
|
24300
|
+
for (const { relPath, source } of fileList) {
|
|
24301
|
+
const stripped = stripComments(source);
|
|
24302
|
+
|
|
24303
|
+
USE_PROVIDER_REGEX.lastIndex = 0;
|
|
24304
|
+
let m;
|
|
24305
|
+
while ((m = USE_PROVIDER_REGEX.exec(stripped)) !== null) {
|
|
24306
|
+
const lit = tryLiteralString(m[1]);
|
|
24307
|
+
const line = lineNumberOf(stripped, m.index);
|
|
24308
|
+
if (lit) {
|
|
24309
|
+
ensureServer(lit);
|
|
24310
|
+
} else {
|
|
24311
|
+
warnings.push({
|
|
24312
|
+
file: relPath,
|
|
24313
|
+
line,
|
|
24314
|
+
kind: "dynamic-server-name",
|
|
24315
|
+
snippet: m[0].trim(),
|
|
24316
|
+
});
|
|
24317
|
+
}
|
|
24318
|
+
}
|
|
24319
|
+
|
|
24320
|
+
CALL_TOOL_REGEX.lastIndex = 0;
|
|
24321
|
+
while ((m = CALL_TOOL_REGEX.exec(stripped)) !== null) {
|
|
24322
|
+
const serverLit = tryLiteralString(m[1]);
|
|
24323
|
+
const toolLit = tryLiteralString(m[2]);
|
|
24324
|
+
const line = lineNumberOf(stripped, m.index);
|
|
24325
|
+
|
|
24326
|
+
if (serverLit && toolLit) {
|
|
24327
|
+
ensureServer(serverLit).tools.add(toolLit);
|
|
24328
|
+
} else if (!serverLit && !toolLit) {
|
|
24329
|
+
warnings.push({
|
|
24330
|
+
file: relPath,
|
|
24331
|
+
line,
|
|
24332
|
+
kind: "dynamic-server-and-tool",
|
|
24333
|
+
snippet: m[0].trim(),
|
|
24334
|
+
});
|
|
24335
|
+
} else if (!serverLit) {
|
|
24336
|
+
warnings.push({
|
|
24337
|
+
file: relPath,
|
|
24338
|
+
line,
|
|
24339
|
+
kind: "dynamic-server-name",
|
|
24340
|
+
snippet: m[0].trim(),
|
|
24341
|
+
});
|
|
24342
|
+
} else {
|
|
24343
|
+
warnings.push({
|
|
24344
|
+
file: relPath,
|
|
24345
|
+
line,
|
|
24346
|
+
kind: "dynamic-tool-name",
|
|
24347
|
+
snippet: m[0].trim(),
|
|
24348
|
+
});
|
|
24349
|
+
}
|
|
24350
|
+
}
|
|
24351
|
+
}
|
|
24352
|
+
|
|
24353
|
+
const out = {};
|
|
24354
|
+
for (const [name, entry] of Object.entries(servers)) {
|
|
24355
|
+
out[name] = { tools: [...entry.tools].sort() };
|
|
24356
|
+
}
|
|
24357
|
+
return { servers: out, warnings };
|
|
24358
|
+
}
|
|
24359
|
+
|
|
24360
|
+
var manifestScanner = {
|
|
24361
|
+
scanForMcpUsage,
|
|
24362
|
+
SCAN_FILE_LIMIT,
|
|
24363
|
+
};
|
|
24364
|
+
|
|
24152
24365
|
/**
|
|
24153
24366
|
* schedulerController.js
|
|
24154
24367
|
*
|
|
@@ -24693,6 +24906,7 @@ var schedulerController_1 = schedulerController$2;
|
|
|
24693
24906
|
getWidgetMcpPermissions,
|
|
24694
24907
|
clearCache: clearWidgetPermsCache,
|
|
24695
24908
|
} = widgetPermissions;
|
|
24909
|
+
const { scanForMcpUsage } = manifestScanner;
|
|
24696
24910
|
|
|
24697
24911
|
let WIDGETS_CACHE_DIR = null;
|
|
24698
24912
|
let REGISTRY_CONFIG_FILE = null;
|
|
@@ -25774,6 +25988,14 @@ var schedulerController_1 = schedulerController$2;
|
|
|
25774
25988
|
* with the user's selections (Slice 2) or quietly drops the message
|
|
25775
25989
|
* (older renderer pre-Slice-2 — the gate still fail-closes).
|
|
25776
25990
|
*
|
|
25991
|
+
* Fallback: if there's no manifest, run the literal-only scanner on the
|
|
25992
|
+
* installed source. If the scan finds any literal MCP usage, emit the
|
|
25993
|
+
* same event with `discovered: true` and a synthetic declared blob —
|
|
25994
|
+
* the consent modal renders this with amber framing so the user knows
|
|
25995
|
+
* they're approving a guess, not the developer's declaration. If the
|
|
25996
|
+
* scan finds nothing, no event fires; the widget appears in
|
|
25997
|
+
* Settings → Privacy & Security with a "Grant manually" button.
|
|
25998
|
+
*
|
|
25777
25999
|
* Cache invalidation: widgetPermissions caches per-process, so an upgrade
|
|
25778
26000
|
* over a stale cached entry would otherwise keep the old manifest. Drop
|
|
25779
26001
|
* the whole cache here — cheap, infrequent.
|
|
@@ -25782,11 +26004,41 @@ var schedulerController_1 = schedulerController$2;
|
|
|
25782
26004
|
try {
|
|
25783
26005
|
clearWidgetPermsCache();
|
|
25784
26006
|
const declared = getWidgetMcpPermissions(widgetName);
|
|
25785
|
-
if (
|
|
26007
|
+
if (declared) {
|
|
26008
|
+
BrowserWindow.getAllWindows().forEach((win) => {
|
|
26009
|
+
win.webContents.send("widget:mcp-consent-required", {
|
|
26010
|
+
widgetId: widgetName,
|
|
26011
|
+
declared,
|
|
26012
|
+
});
|
|
26013
|
+
});
|
|
26014
|
+
return;
|
|
26015
|
+
}
|
|
26016
|
+
|
|
26017
|
+
// No manifest — try a scan of the installed source.
|
|
26018
|
+
if (!WIDGETS_CACHE_DIR) return;
|
|
26019
|
+
const widgetPath = path.join(WIDGETS_CACHE_DIR, ...widgetName.split("/"));
|
|
26020
|
+
if (!fs.existsSync(widgetPath)) return;
|
|
26021
|
+
const scanResult = scanForMcpUsage({ dir: widgetPath });
|
|
26022
|
+
const detectedServers = Object.keys(scanResult.servers);
|
|
26023
|
+
if (detectedServers.length === 0) return; // nothing actionable to prompt about
|
|
26024
|
+
|
|
26025
|
+
// Build a synthetic declared blob in the same shape parseManifestPermissions
|
|
26026
|
+
// produces so the consent modal can render it identically (modulo the
|
|
26027
|
+
// amber "discovered" framing).
|
|
26028
|
+
const syntheticServers = {};
|
|
26029
|
+
for (const [name, entry] of Object.entries(scanResult.servers)) {
|
|
26030
|
+
syntheticServers[name] = {
|
|
26031
|
+
tools: entry.tools,
|
|
26032
|
+
readPaths: [],
|
|
26033
|
+
writePaths: [],
|
|
26034
|
+
};
|
|
26035
|
+
}
|
|
25786
26036
|
BrowserWindow.getAllWindows().forEach((win) => {
|
|
25787
26037
|
win.webContents.send("widget:mcp-consent-required", {
|
|
25788
26038
|
widgetId: widgetName,
|
|
25789
|
-
declared,
|
|
26039
|
+
declared: { servers: syntheticServers },
|
|
26040
|
+
discovered: true,
|
|
26041
|
+
warnings: scanResult.warnings,
|
|
25790
26042
|
});
|
|
25791
26043
|
});
|
|
25792
26044
|
} catch (e) {
|
|
@@ -60080,6 +60332,91 @@ const webSocketController$1 = {
|
|
|
60080
60332
|
|
|
60081
60333
|
var webSocketController_1 = webSocketController$1;
|
|
60082
60334
|
|
|
60335
|
+
/**
|
|
60336
|
+
* widgetMcpGrantsListing.js
|
|
60337
|
+
*
|
|
60338
|
+
* Pure helper that joins three data sources into rows for the
|
|
60339
|
+
* Settings → Privacy & Security panel:
|
|
60340
|
+
* - installed widgets (from widgetRegistry.getWidgets())
|
|
60341
|
+
* - persisted grants (from grantedPermissions.listAllGrants())
|
|
60342
|
+
* - declared manifests (from widgetPermissions.getWidgetMcpPermissions())
|
|
60343
|
+
*
|
|
60344
|
+
* Output row shape:
|
|
60345
|
+
* {
|
|
60346
|
+
* widgetId: string,
|
|
60347
|
+
* declared: object|null, // dash.permissions.mcp block from package.json
|
|
60348
|
+
* granted: object|null, // user grant from widgetMcpGrants.json
|
|
60349
|
+
* hasManifest: boolean, // declared !== null
|
|
60350
|
+
* grantOrigin: string|null // "declared" | "discovered" | "manual" | null
|
|
60351
|
+
* }
|
|
60352
|
+
*
|
|
60353
|
+
* Includes ALL installed widgets (even unmanifested + ungranted) so the
|
|
60354
|
+
* panel can offer "Grant manually" for every widget. Also surfaces
|
|
60355
|
+
* orphan grants (granted but uninstalled — rare, only happens if
|
|
60356
|
+
* uninstall didn't revoke).
|
|
60357
|
+
*/
|
|
60358
|
+
|
|
60359
|
+
function buildGrantsListing$1(
|
|
60360
|
+
installedWidgets,
|
|
60361
|
+
grantsByWidgetId,
|
|
60362
|
+
declaredByWidgetId,
|
|
60363
|
+
) {
|
|
60364
|
+
const rows = [];
|
|
60365
|
+
const seen = new Set();
|
|
60366
|
+
|
|
60367
|
+
const installed = Array.isArray(installedWidgets) ? installedWidgets : [];
|
|
60368
|
+
const grants = grantsByWidgetId instanceof Map ? grantsByWidgetId : new Map();
|
|
60369
|
+
const declared =
|
|
60370
|
+
declaredByWidgetId instanceof Map ? declaredByWidgetId : new Map();
|
|
60371
|
+
|
|
60372
|
+
for (const w of installed) {
|
|
60373
|
+
if (!w || typeof w !== "object") continue;
|
|
60374
|
+
const widgetId = w.name;
|
|
60375
|
+
if (typeof widgetId !== "string" || !widgetId) continue;
|
|
60376
|
+
if (seen.has(widgetId)) continue;
|
|
60377
|
+
seen.add(widgetId);
|
|
60378
|
+
|
|
60379
|
+
const decl = declared.get(widgetId) || null;
|
|
60380
|
+
const grant = grants.get(widgetId) || null;
|
|
60381
|
+
const grantOrigin =
|
|
60382
|
+
grant &&
|
|
60383
|
+
typeof grant === "object" &&
|
|
60384
|
+
typeof grant.grantOrigin === "string"
|
|
60385
|
+
? grant.grantOrigin
|
|
60386
|
+
: null;
|
|
60387
|
+
|
|
60388
|
+
rows.push({
|
|
60389
|
+
widgetId,
|
|
60390
|
+
declared: decl,
|
|
60391
|
+
granted: grant,
|
|
60392
|
+
hasManifest: decl !== null,
|
|
60393
|
+
grantOrigin,
|
|
60394
|
+
});
|
|
60395
|
+
}
|
|
60396
|
+
|
|
60397
|
+
// Orphan grants: granted but not in the installed list.
|
|
60398
|
+
for (const [widgetId, grant] of grants) {
|
|
60399
|
+
if (seen.has(widgetId)) continue;
|
|
60400
|
+
const grantOrigin =
|
|
60401
|
+
grant &&
|
|
60402
|
+
typeof grant === "object" &&
|
|
60403
|
+
typeof grant.grantOrigin === "string"
|
|
60404
|
+
? grant.grantOrigin
|
|
60405
|
+
: null;
|
|
60406
|
+
rows.push({
|
|
60407
|
+
widgetId,
|
|
60408
|
+
declared: null,
|
|
60409
|
+
granted: grant,
|
|
60410
|
+
hasManifest: false,
|
|
60411
|
+
grantOrigin,
|
|
60412
|
+
});
|
|
60413
|
+
}
|
|
60414
|
+
|
|
60415
|
+
return rows;
|
|
60416
|
+
}
|
|
60417
|
+
|
|
60418
|
+
var widgetMcpGrantsListing = { buildGrantsListing: buildGrantsListing$1 };
|
|
60419
|
+
|
|
60083
60420
|
/**
|
|
60084
60421
|
* widgetMcpGrantsController.js
|
|
60085
60422
|
*
|
|
@@ -60105,6 +60442,7 @@ const {
|
|
|
60105
60442
|
} = grantedPermissions;
|
|
60106
60443
|
const { getWidgetMcpPermissions } = widgetPermissions;
|
|
60107
60444
|
const { getWidgetRegistry } = widgetRegistryExports;
|
|
60445
|
+
const { buildGrantsListing } = widgetMcpGrantsListing;
|
|
60108
60446
|
|
|
60109
60447
|
function setupWidgetMcpGrantsHandlers() {
|
|
60110
60448
|
ipcMain$1.handle("widget-mcp:get-grant", (event, widgetId) => {
|
|
@@ -60123,19 +60461,18 @@ function setupWidgetMcpGrantsHandlers() {
|
|
|
60123
60461
|
return revokeServer(widgetId, serverName);
|
|
60124
60462
|
});
|
|
60125
60463
|
|
|
60126
|
-
// Joins all installed widgets with their declared
|
|
60127
|
-
//
|
|
60128
|
-
// grant —
|
|
60129
|
-
// "
|
|
60464
|
+
// Joins all installed widgets with their declared manifests + persisted
|
|
60465
|
+
// grants. Returns ONE row per installed widget regardless of whether it
|
|
60466
|
+
// has a manifest or grant — that's how the Settings panel can offer
|
|
60467
|
+
// "Grant manually" for unmanifested widgets. Plus orphan-grant rows for
|
|
60468
|
+
// granted-but-uninstalled cases. Logic delegated to
|
|
60469
|
+
// widgetMcpGrantsListing.buildGrantsListing for unit-testability.
|
|
60130
60470
|
ipcMain$1.handle("widget-mcp:list-all", () => {
|
|
60131
60471
|
const grantsByWidget = new Map();
|
|
60132
60472
|
for (const { widgetId, granted } of listAllGrants()) {
|
|
60133
60473
|
grantsByWidget.set(widgetId, granted);
|
|
60134
60474
|
}
|
|
60135
60475
|
|
|
60136
|
-
const rows = [];
|
|
60137
|
-
const seen = new Set();
|
|
60138
|
-
|
|
60139
60476
|
let installedWidgets = [];
|
|
60140
60477
|
try {
|
|
60141
60478
|
installedWidgets = getWidgetRegistry().getWidgets() || [];
|
|
@@ -60143,25 +60480,19 @@ function setupWidgetMcpGrantsHandlers() {
|
|
|
60143
60480
|
// Registry not initialized yet; fall back to grants-only listing.
|
|
60144
60481
|
}
|
|
60145
60482
|
|
|
60483
|
+
const declaredByWidget = new Map();
|
|
60146
60484
|
for (const w of installedWidgets) {
|
|
60147
60485
|
const widgetId = w?.name;
|
|
60148
|
-
if (!widgetId
|
|
60149
|
-
seen.add(widgetId);
|
|
60486
|
+
if (!widgetId) continue;
|
|
60150
60487
|
const declared = getWidgetMcpPermissions(widgetId);
|
|
60151
|
-
|
|
60152
|
-
// Skip widgets with neither — they have nothing to show.
|
|
60153
|
-
if (!declared && !granted) continue;
|
|
60154
|
-
rows.push({ widgetId, declared, granted });
|
|
60488
|
+
if (declared) declaredByWidget.set(widgetId, declared);
|
|
60155
60489
|
}
|
|
60156
60490
|
|
|
60157
|
-
|
|
60158
|
-
|
|
60159
|
-
|
|
60160
|
-
|
|
60161
|
-
|
|
60162
|
-
}
|
|
60163
|
-
|
|
60164
|
-
return rows;
|
|
60491
|
+
return buildGrantsListing(
|
|
60492
|
+
installedWidgets,
|
|
60493
|
+
grantsByWidget,
|
|
60494
|
+
declaredByWidget,
|
|
60495
|
+
);
|
|
60165
60496
|
});
|
|
60166
60497
|
}
|
|
60167
60498
|
|