@trops/dash-core 0.1.486 → 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.
- package/dist/electron/index.js +748 -128
- package/dist/electron/index.js.map +1 -1
- package/package.json +1 -1
package/dist/electron/index.js
CHANGED
|
@@ -4,7 +4,7 @@ var require$$0$1 = require('electron');
|
|
|
4
4
|
var require$$1$1 = require('electron-store');
|
|
5
5
|
var require$$1$2 = require('path');
|
|
6
6
|
var require$$0$2 = require('fs');
|
|
7
|
-
var require$$
|
|
7
|
+
var require$$6$1 = require('objects-to-csv');
|
|
8
8
|
var require$$1$3 = require('readline');
|
|
9
9
|
var require$$2 = require('xtreamer');
|
|
10
10
|
var require$$3$1 = require('xml2js');
|
|
@@ -12,12 +12,12 @@ var require$$4 = require('JSONStream');
|
|
|
12
12
|
var require$$5 = require('stream');
|
|
13
13
|
var require$$6 = require('csv-parser');
|
|
14
14
|
var require$$0$3 = require('quickjs-emscripten');
|
|
15
|
-
var require$$
|
|
15
|
+
var require$$8$1 = require('https');
|
|
16
16
|
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$$
|
|
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');
|
|
@@ -28,7 +28,7 @@ var require$$0$6 = require('openai');
|
|
|
28
28
|
require('live-plugin-manager');
|
|
29
29
|
var require$$0$9 = require('@anthropic-ai/sdk');
|
|
30
30
|
var require$$3$4 = require('crypto');
|
|
31
|
-
var require$$8$
|
|
31
|
+
var require$$8$2 = require('zod');
|
|
32
32
|
var require$$0$7 = require('http');
|
|
33
33
|
var require$$1$6 = require('http2');
|
|
34
34
|
var require$$2$4 = require('node-forge');
|
|
@@ -1107,7 +1107,7 @@ var secureStoreController$1 = {
|
|
|
1107
1107
|
getData: getData$1,
|
|
1108
1108
|
};
|
|
1109
1109
|
|
|
1110
|
-
const path$
|
|
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$
|
|
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$
|
|
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$
|
|
1261
|
-
const path$
|
|
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$
|
|
1309
|
-
app$
|
|
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$
|
|
1358
|
-
app$
|
|
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$
|
|
1407
|
-
app$
|
|
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$
|
|
1441
|
-
app$
|
|
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$
|
|
1469
|
-
app$
|
|
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$
|
|
1513
|
-
const path$
|
|
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$
|
|
1534
|
-
app$
|
|
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$
|
|
1580
|
-
app$
|
|
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$
|
|
1622
|
-
app$
|
|
1621
|
+
const filename = path$j.join(
|
|
1622
|
+
app$d.getPath("userData"),
|
|
1623
1623
|
appName$6,
|
|
1624
1624
|
appId,
|
|
1625
1625
|
configFilename$4,
|
|
@@ -1650,6 +1650,179 @@ const themeController$5 = {
|
|
|
1650
1650
|
|
|
1651
1651
|
var themeController_1 = themeController$5;
|
|
1652
1652
|
|
|
1653
|
+
/**
|
|
1654
|
+
* safePath.js
|
|
1655
|
+
*
|
|
1656
|
+
* Path-traversal containment for IPC handlers that accept renderer-
|
|
1657
|
+
* supplied paths.
|
|
1658
|
+
*
|
|
1659
|
+
* Why: dash-core exposes IPC handlers (mainApi.data.saveData,
|
|
1660
|
+
* mainApi.data.parseXMLStream, mainApi.algolia.createBatchesFromFile,
|
|
1661
|
+
* etc.) that historically passed renderer-controlled paths directly to
|
|
1662
|
+
* fs.writeFileSync / fs.createReadStream. A widget could pass
|
|
1663
|
+
* "../../etc/passwd" and the handler would write/read OUTSIDE the
|
|
1664
|
+
* intended app data directory because path.join doesn't reject `..`
|
|
1665
|
+
* segments. See docs/security/ipc-filesystem-audit.md for the full
|
|
1666
|
+
* finding set.
|
|
1667
|
+
*
|
|
1668
|
+
* This utility resolves the requested path, walks symlinks, and asserts
|
|
1669
|
+
* containment within at least one explicitly-allowed root. Any handler
|
|
1670
|
+
* that takes a renderer path runs it through `safePath(p, roots)` and
|
|
1671
|
+
* either gets back a validated absolute real-path, or throws.
|
|
1672
|
+
*
|
|
1673
|
+
* Public API:
|
|
1674
|
+
*
|
|
1675
|
+
* safePath(requested, allowedRoots[]) → string
|
|
1676
|
+
* Throws on traversal, missing input, or empty roots. Returns the
|
|
1677
|
+
* resolved real-path (which is what the caller should pass to fs).
|
|
1678
|
+
*
|
|
1679
|
+
* getAllowedRoots(category) → string[]
|
|
1680
|
+
* Canonical roots per category. Categories:
|
|
1681
|
+
* "data" — userData/Dashboard/data + user-configured override
|
|
1682
|
+
* "themes" — userData/Dashboard/themes
|
|
1683
|
+
* "widgets" — userData/widgets
|
|
1684
|
+
* "plugins" — userData/plugins
|
|
1685
|
+
* "downloads"— OS Downloads folder
|
|
1686
|
+
*
|
|
1687
|
+
* Defense layers:
|
|
1688
|
+
* 1. path.resolve() to absolute form.
|
|
1689
|
+
* 2. fs.realpathSync() through any symlinks. If the path doesn't
|
|
1690
|
+
* exist yet, realpath the parent directory (so a symlink-in-parent
|
|
1691
|
+
* can't trick a future create operation).
|
|
1692
|
+
* 3. startsWith(realRoot + path.sep) test — single-equals check
|
|
1693
|
+
* handles "exactly the root" case, prefix-with-sep handles
|
|
1694
|
+
* "inside the root" without false-matching `/data-evil/` against
|
|
1695
|
+
* `/data/`.
|
|
1696
|
+
*/
|
|
1697
|
+
|
|
1698
|
+
const path$i = require$$1$2;
|
|
1699
|
+
const fs$d = require$$0$2;
|
|
1700
|
+
const { app: app$c } = require$$0$1;
|
|
1701
|
+
|
|
1702
|
+
const APP_NAME = "Dashboard";
|
|
1703
|
+
|
|
1704
|
+
/**
|
|
1705
|
+
* @param {string} category
|
|
1706
|
+
* @returns {string[]} ordered allowed roots for that category
|
|
1707
|
+
*/
|
|
1708
|
+
function getAllowedRoots$2(category) {
|
|
1709
|
+
const userData = app$c.getPath("userData");
|
|
1710
|
+
switch (category) {
|
|
1711
|
+
case "data": {
|
|
1712
|
+
const def = path$i.join(userData, APP_NAME, "data");
|
|
1713
|
+
// The user can configure a custom data directory in
|
|
1714
|
+
// Settings → General → Data Directory. If set, that
|
|
1715
|
+
// location is ALSO an allowed root. We don't replace the
|
|
1716
|
+
// default — both are valid because legacy data may still
|
|
1717
|
+
// live in the default while new data goes to the override.
|
|
1718
|
+
const override = readDataDirectoryFromSettings();
|
|
1719
|
+
return override ? [def, override] : [def];
|
|
1720
|
+
}
|
|
1721
|
+
case "themes":
|
|
1722
|
+
return [path$i.join(userData, APP_NAME, "themes")];
|
|
1723
|
+
case "widgets":
|
|
1724
|
+
return [path$i.join(userData, "widgets")];
|
|
1725
|
+
case "plugins":
|
|
1726
|
+
return [path$i.join(userData, "plugins")];
|
|
1727
|
+
case "downloads":
|
|
1728
|
+
return [app$c.getPath("downloads")];
|
|
1729
|
+
default:
|
|
1730
|
+
throw new Error("safePath: unknown allowed-roots category: " + category);
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
/**
|
|
1735
|
+
* Read the user-configured data directory from settings.json. Returns
|
|
1736
|
+
* undefined if not set or unreadable.
|
|
1737
|
+
*
|
|
1738
|
+
* Inlined to avoid a circular require with settingsController. Reads
|
|
1739
|
+
* the same settings.json file directly.
|
|
1740
|
+
*/
|
|
1741
|
+
function readDataDirectoryFromSettings() {
|
|
1742
|
+
try {
|
|
1743
|
+
const settingsPath = path$i.join(
|
|
1744
|
+
app$c.getPath("userData"),
|
|
1745
|
+
APP_NAME,
|
|
1746
|
+
"settings.json",
|
|
1747
|
+
);
|
|
1748
|
+
if (!fs$d.existsSync(settingsPath)) return undefined;
|
|
1749
|
+
const raw = fs$d.readFileSync(settingsPath, "utf8");
|
|
1750
|
+
const settings = JSON.parse(raw);
|
|
1751
|
+
const dir = settings && settings.dataDirectory;
|
|
1752
|
+
if (typeof dir === "string" && dir) return dir;
|
|
1753
|
+
} catch (_e) {
|
|
1754
|
+
// best-effort — fall through to default
|
|
1755
|
+
}
|
|
1756
|
+
return undefined;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
/**
|
|
1760
|
+
* Resolve and validate a path against allowed roots.
|
|
1761
|
+
*
|
|
1762
|
+
* @param {string} requested the path the renderer asked for
|
|
1763
|
+
* @param {string[]} allowedRoots list of absolute paths the result must be inside
|
|
1764
|
+
* @returns {string} validated absolute real-path
|
|
1765
|
+
* @throws if requested is not contained within any allowed root
|
|
1766
|
+
*/
|
|
1767
|
+
function safePath$3(requested, allowedRoots) {
|
|
1768
|
+
if (typeof requested !== "string" || !requested) {
|
|
1769
|
+
throw new Error("safePath: requested must be a non-empty string");
|
|
1770
|
+
}
|
|
1771
|
+
if (!Array.isArray(allowedRoots) || allowedRoots.length === 0) {
|
|
1772
|
+
throw new Error("safePath: allowedRoots must be a non-empty array");
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
const resolved = path$i.resolve(requested);
|
|
1776
|
+
|
|
1777
|
+
// Real-path through symlinks. If the file doesn't exist yet (a
|
|
1778
|
+
// create-new operation), real-path the parent so a symlink in the
|
|
1779
|
+
// parent chain can't trick us.
|
|
1780
|
+
let real = resolved;
|
|
1781
|
+
try {
|
|
1782
|
+
real = fs$d.realpathSync(resolved);
|
|
1783
|
+
} catch (_e) {
|
|
1784
|
+
try {
|
|
1785
|
+
const parent = fs$d.realpathSync(path$i.dirname(resolved));
|
|
1786
|
+
real = path$i.join(parent, path$i.basename(resolved));
|
|
1787
|
+
} catch (_e2) {
|
|
1788
|
+
// Parent doesn't exist either. Use the resolved-but-not-
|
|
1789
|
+
// real path; the caller's mkdirSync will happen inside the
|
|
1790
|
+
// validated root, and any symlinks underneath will be
|
|
1791
|
+
// re-checked the next time safePath sees the same path.
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
for (const root of allowedRoots) {
|
|
1796
|
+
let realRoot = root;
|
|
1797
|
+
try {
|
|
1798
|
+
if (fs$d.existsSync(root)) realRoot = fs$d.realpathSync(root);
|
|
1799
|
+
} catch (_e) {
|
|
1800
|
+
// root doesn't exist or isn't reachable — keep as-is for
|
|
1801
|
+
// the comparison below
|
|
1802
|
+
}
|
|
1803
|
+
// Exact match OR strictly-inside (with separator to prevent
|
|
1804
|
+
// /data-evil/ matching /data/).
|
|
1805
|
+
if (real === realRoot || real.startsWith(realRoot + path$i.sep)) {
|
|
1806
|
+
return real;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
throw new Error(
|
|
1811
|
+
"safePath: requested path is not within any allowed root. " +
|
|
1812
|
+
"Requested: " +
|
|
1813
|
+
requested +
|
|
1814
|
+
" (resolved to " +
|
|
1815
|
+
real +
|
|
1816
|
+
"). Allowed roots: " +
|
|
1817
|
+
allowedRoots.join(", "),
|
|
1818
|
+
);
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
var safePath_1 = {
|
|
1822
|
+
safePath: safePath$3,
|
|
1823
|
+
getAllowedRoots: getAllowedRoots$2,
|
|
1824
|
+
};
|
|
1825
|
+
|
|
1653
1826
|
/**
|
|
1654
1827
|
* safeJsExecutor.js
|
|
1655
1828
|
*
|
|
@@ -1887,15 +2060,15 @@ var safeJsExecutor$1 = {
|
|
|
1887
2060
|
* - CSV -> JSON
|
|
1888
2061
|
*/
|
|
1889
2062
|
|
|
1890
|
-
var fs$
|
|
2063
|
+
var fs$c = require$$0$2;
|
|
1891
2064
|
var readline = require$$1$3;
|
|
1892
2065
|
const xtreamer = require$$2;
|
|
1893
2066
|
var xmlParser = require$$3$1;
|
|
1894
2067
|
var JSONStream$1 = require$$4;
|
|
1895
2068
|
const stream$1 = require$$5;
|
|
1896
2069
|
var csv = require$$6;
|
|
1897
|
-
const path$
|
|
1898
|
-
const { app: app$
|
|
2070
|
+
const path$h = require$$1$2;
|
|
2071
|
+
const { app: app$b } = require$$0$1;
|
|
1899
2072
|
const { ensureDirectoryExistence: ensureDirectoryExistence$1 } = file;
|
|
1900
2073
|
const safeJsExecutor = safeJsExecutor$1;
|
|
1901
2074
|
|
|
@@ -1949,7 +2122,7 @@ let Transform$1 = class Transform {
|
|
|
1949
2122
|
let lineObject = [];
|
|
1950
2123
|
|
|
1951
2124
|
const readInterface = readline.createInterface({
|
|
1952
|
-
input: fs$
|
|
2125
|
+
input: fs$c.createReadStream(filepath),
|
|
1953
2126
|
output: process.stdout,
|
|
1954
2127
|
console: false,
|
|
1955
2128
|
});
|
|
@@ -1984,7 +2157,7 @@ let Transform$1 = class Transform {
|
|
|
1984
2157
|
return new Promise((resolve, reject) => {
|
|
1985
2158
|
try {
|
|
1986
2159
|
const parser = JSONStream$1.parse("*");
|
|
1987
|
-
const readStream = fs$
|
|
2160
|
+
const readStream = fs$c.createReadStream(file).pipe(parser);
|
|
1988
2161
|
|
|
1989
2162
|
let count = 0;
|
|
1990
2163
|
|
|
@@ -2037,7 +2210,7 @@ let Transform$1 = class Transform {
|
|
|
2037
2210
|
parseXMLStream = (filepath, outpath, start) => {
|
|
2038
2211
|
return new Promise((resolve, reject) => {
|
|
2039
2212
|
try {
|
|
2040
|
-
const xmlFileReadStream = fs$
|
|
2213
|
+
const xmlFileReadStream = fs$c.createReadStream(filepath);
|
|
2041
2214
|
|
|
2042
2215
|
xmlFileReadStream.on("end", () => {
|
|
2043
2216
|
writeStream.write("\n]");
|
|
@@ -2048,7 +2221,7 @@ let Transform$1 = class Transform {
|
|
|
2048
2221
|
resolve("Read Finish");
|
|
2049
2222
|
});
|
|
2050
2223
|
|
|
2051
|
-
const writeStream = fs$
|
|
2224
|
+
const writeStream = fs$c.createWriteStream(outpath);
|
|
2052
2225
|
writeStream.write("[\n");
|
|
2053
2226
|
|
|
2054
2227
|
const options = {
|
|
@@ -2100,10 +2273,10 @@ let Transform$1 = class Transform {
|
|
|
2100
2273
|
) => {
|
|
2101
2274
|
return new Promise((resolve, reject) => {
|
|
2102
2275
|
try {
|
|
2103
|
-
const readStream = fs$
|
|
2276
|
+
const readStream = fs$c
|
|
2104
2277
|
.createReadStream(filepath)
|
|
2105
2278
|
.pipe(csv({ separator: delimiter }));
|
|
2106
|
-
const writeStream = fs$
|
|
2279
|
+
const writeStream = fs$c.createWriteStream(outpath);
|
|
2107
2280
|
|
|
2108
2281
|
let canParse = true;
|
|
2109
2282
|
|
|
@@ -2189,18 +2362,18 @@ let Transform$1 = class Transform {
|
|
|
2189
2362
|
}
|
|
2190
2363
|
|
|
2191
2364
|
// Validate file paths are within app data directory
|
|
2192
|
-
const appDataDir = path$
|
|
2193
|
-
const resolvedFilepath = path$
|
|
2194
|
-
const resolvedOutFilepath = path$
|
|
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);
|
|
2195
2368
|
|
|
2196
|
-
if (!resolvedFilepath.startsWith(appDataDir + path$
|
|
2369
|
+
if (!resolvedFilepath.startsWith(appDataDir + path$h.sep)) {
|
|
2197
2370
|
return reject(
|
|
2198
2371
|
new Error(
|
|
2199
2372
|
"Input file path must be within the application data directory",
|
|
2200
2373
|
),
|
|
2201
2374
|
);
|
|
2202
2375
|
}
|
|
2203
|
-
if (!resolvedOutFilepath.startsWith(appDataDir + path$
|
|
2376
|
+
if (!resolvedOutFilepath.startsWith(appDataDir + path$h.sep)) {
|
|
2204
2377
|
return reject(
|
|
2205
2378
|
new Error(
|
|
2206
2379
|
"Output file path must be within the application data directory",
|
|
@@ -2211,16 +2384,16 @@ let Transform$1 = class Transform {
|
|
|
2211
2384
|
// JSON parser
|
|
2212
2385
|
var parser = JSONStream$1.parse("*");
|
|
2213
2386
|
|
|
2214
|
-
if (!fs$
|
|
2387
|
+
if (!fs$c.existsSync(resolvedFilepath)) {
|
|
2215
2388
|
return reject(new Error("File doesnt exist"));
|
|
2216
2389
|
}
|
|
2217
2390
|
console.log("file exists ", resolvedFilepath);
|
|
2218
2391
|
// create the readStream to parse the large file (json)
|
|
2219
|
-
var readStream = fs$
|
|
2392
|
+
var readStream = fs$c.createReadStream(resolvedFilepath).pipe(parser);
|
|
2220
2393
|
|
|
2221
2394
|
ensureDirectoryExistence$1(resolvedOutFilepath);
|
|
2222
2395
|
|
|
2223
|
-
var writeStream = fs$
|
|
2396
|
+
var writeStream = fs$c.createWriteStream(resolvedOutFilepath);
|
|
2224
2397
|
|
|
2225
2398
|
let sep = "";
|
|
2226
2399
|
let count = 0;
|
|
@@ -2332,16 +2505,17 @@ let Transform$1 = class Transform {
|
|
|
2332
2505
|
|
|
2333
2506
|
var transform = Transform$1;
|
|
2334
2507
|
|
|
2335
|
-
const { app: app$
|
|
2336
|
-
var fs$
|
|
2337
|
-
const path$
|
|
2508
|
+
const { app: app$a } = require$$0$1;
|
|
2509
|
+
var fs$b = require$$0$2;
|
|
2510
|
+
const path$g = require$$1$2;
|
|
2338
2511
|
const events$5 = events$8;
|
|
2339
2512
|
const { getFileContents: getFileContents$5, writeToFile: writeToFile$2 } = file;
|
|
2513
|
+
const { safePath: safePath$2, getAllowedRoots: getAllowedRoots$1 } = safePath_1;
|
|
2340
2514
|
|
|
2341
2515
|
// Convert Json to Csv
|
|
2342
|
-
const ObjectsToCsv = require$$
|
|
2516
|
+
const ObjectsToCsv = require$$6$1;
|
|
2343
2517
|
const Transform = transform;
|
|
2344
|
-
const https$3 = require$$
|
|
2518
|
+
const https$3 = require$$8$1;
|
|
2345
2519
|
const appName$5 = "Dashboard";
|
|
2346
2520
|
|
|
2347
2521
|
const dataController$1 = {
|
|
@@ -2355,14 +2529,25 @@ const dataController$1 = {
|
|
|
2355
2529
|
*/
|
|
2356
2530
|
convertJsonToCsvFile: (win, appId, jsonObject, toFilename = "test.csv") => {
|
|
2357
2531
|
try {
|
|
2358
|
-
//
|
|
2359
|
-
|
|
2360
|
-
|
|
2532
|
+
// Validate the renderer-supplied filename is contained within
|
|
2533
|
+
// the data directory. path.join doesn't reject `..` segments;
|
|
2534
|
+
// safePath does.
|
|
2535
|
+
const candidate = path$g.join(
|
|
2536
|
+
app$a.getPath("userData"),
|
|
2361
2537
|
appName$5,
|
|
2362
2538
|
appId,
|
|
2363
2539
|
"data",
|
|
2364
2540
|
toFilename,
|
|
2365
2541
|
);
|
|
2542
|
+
let filename;
|
|
2543
|
+
try {
|
|
2544
|
+
filename = safePath$2(candidate, getAllowedRoots$1("data"));
|
|
2545
|
+
} catch (pathErr) {
|
|
2546
|
+
win.webContents.send(events$5.DATA_JSON_TO_CSV_FILE_ERROR, {
|
|
2547
|
+
error: pathErr.message,
|
|
2548
|
+
});
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2366
2551
|
|
|
2367
2552
|
// make sure the file exists...
|
|
2368
2553
|
const fileContents = getFileContents$5(filename, "");
|
|
@@ -2420,8 +2605,17 @@ const dataController$1 = {
|
|
|
2420
2605
|
|
|
2421
2606
|
readLinesFromFile: (win, filepath, lineCount) => {
|
|
2422
2607
|
try {
|
|
2608
|
+
let validated;
|
|
2609
|
+
try {
|
|
2610
|
+
validated = safePath$2(filepath, getAllowedRoots$1("data"));
|
|
2611
|
+
} catch (pathErr) {
|
|
2612
|
+
win.webContents.send(events$5.READ_LINES_ERROR, {
|
|
2613
|
+
error: pathErr.message,
|
|
2614
|
+
});
|
|
2615
|
+
return;
|
|
2616
|
+
}
|
|
2423
2617
|
const t = new Transform();
|
|
2424
|
-
t.readLinesFromFile(win,
|
|
2618
|
+
t.readLinesFromFile(win, validated, lineCount, events$5.READ_LINES_UPDATE)
|
|
2425
2619
|
.then((res) => {
|
|
2426
2620
|
win.webContents.send(events$5.READ_LINES_COMPLETE, {
|
|
2427
2621
|
success: true,
|
|
@@ -2445,9 +2639,18 @@ const dataController$1 = {
|
|
|
2445
2639
|
|
|
2446
2640
|
readJSONFromFile: (win, filepath, objectCount = null) => {
|
|
2447
2641
|
try {
|
|
2448
|
-
|
|
2642
|
+
let validated;
|
|
2643
|
+
try {
|
|
2644
|
+
validated = safePath$2(filepath, getAllowedRoots$1("data"));
|
|
2645
|
+
} catch (pathErr) {
|
|
2646
|
+
win.webContents.send(events$5.READ_JSON_ERROR, {
|
|
2647
|
+
error: pathErr.message,
|
|
2648
|
+
});
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
console.log("reading json from file ", validated, objectCount);
|
|
2449
2652
|
const t = new Transform();
|
|
2450
|
-
t.readJSONFromFile(win,
|
|
2653
|
+
t.readJSONFromFile(win, validated, objectCount, events$5.READ_JSON_UPDATE)
|
|
2451
2654
|
.then((res) => {
|
|
2452
2655
|
win.webContents.send(events$5.READ_JSON_COMPLETE, {
|
|
2453
2656
|
success: true,
|
|
@@ -2483,16 +2686,12 @@ const dataController$1 = {
|
|
|
2483
2686
|
);
|
|
2484
2687
|
}
|
|
2485
2688
|
|
|
2486
|
-
// Validate toFilepath is within the app data directory
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
throw new Error(
|
|
2491
|
-
"File path must be within the application data directory",
|
|
2492
|
-
);
|
|
2493
|
-
}
|
|
2689
|
+
// Validate toFilepath is within the app data directory.
|
|
2690
|
+
// safePath replaces the previous inline check; same containment
|
|
2691
|
+
// intent, plus realpath/symlink protection.
|
|
2692
|
+
const resolvedFilepath = safePath$2(toFilepath, getAllowedRoots$1("data"));
|
|
2494
2693
|
|
|
2495
|
-
const writeStream = fs$
|
|
2694
|
+
const writeStream = fs$b.createWriteStream(resolvedFilepath);
|
|
2496
2695
|
|
|
2497
2696
|
https$3
|
|
2498
2697
|
.get(url, (resp) => {
|
|
@@ -2537,10 +2736,21 @@ const dataController$1 = {
|
|
|
2537
2736
|
objectIdKey = null,
|
|
2538
2737
|
) => {
|
|
2539
2738
|
try {
|
|
2739
|
+
let validatedIn, validatedOut;
|
|
2740
|
+
try {
|
|
2741
|
+
const roots = getAllowedRoots$1("data");
|
|
2742
|
+
validatedIn = safePath$2(filepath, roots);
|
|
2743
|
+
validatedOut = safePath$2(outpath, roots);
|
|
2744
|
+
} catch (pathErr) {
|
|
2745
|
+
win.webContents.send(events$5.PARSE_XML_STREAM_ERROR, {
|
|
2746
|
+
error: pathErr.message,
|
|
2747
|
+
});
|
|
2748
|
+
return;
|
|
2749
|
+
}
|
|
2540
2750
|
const t = new Transform();
|
|
2541
2751
|
t.parseXMLStream(
|
|
2542
|
-
|
|
2543
|
-
|
|
2752
|
+
validatedIn,
|
|
2753
|
+
validatedOut,
|
|
2544
2754
|
start,
|
|
2545
2755
|
// recordNode,
|
|
2546
2756
|
// objectIdKey,
|
|
@@ -2586,10 +2796,21 @@ const dataController$1 = {
|
|
|
2586
2796
|
limit = null,
|
|
2587
2797
|
) => {
|
|
2588
2798
|
try {
|
|
2799
|
+
let validatedIn, validatedOut;
|
|
2800
|
+
try {
|
|
2801
|
+
const roots = getAllowedRoots$1("data");
|
|
2802
|
+
validatedIn = safePath$2(filepath, roots);
|
|
2803
|
+
validatedOut = safePath$2(outpath, roots);
|
|
2804
|
+
} catch (pathErr) {
|
|
2805
|
+
win.webContents.send(events$5.PARSE_CSV_STREAM_ERROR, {
|
|
2806
|
+
error: pathErr.message,
|
|
2807
|
+
});
|
|
2808
|
+
return;
|
|
2809
|
+
}
|
|
2589
2810
|
const t = new Transform();
|
|
2590
2811
|
t.parseCSVStream(
|
|
2591
|
-
|
|
2592
|
-
|
|
2812
|
+
validatedIn,
|
|
2813
|
+
validatedOut,
|
|
2593
2814
|
delimiter,
|
|
2594
2815
|
objectIdKey,
|
|
2595
2816
|
headers,
|
|
@@ -2632,13 +2853,25 @@ const dataController$1 = {
|
|
|
2632
2853
|
saveToFile: (win, data, filename, append, returnEmpty = {}) => {
|
|
2633
2854
|
try {
|
|
2634
2855
|
if (data) {
|
|
2635
|
-
// filename
|
|
2636
|
-
|
|
2637
|
-
|
|
2856
|
+
// Validate filename is contained within the data directory.
|
|
2857
|
+
// path.join doesn't reject `..` segments; safePath does.
|
|
2858
|
+
const candidate = path$g.join(
|
|
2859
|
+
app$a.getPath("userData"),
|
|
2638
2860
|
appName$5,
|
|
2639
2861
|
"data",
|
|
2640
2862
|
filename,
|
|
2641
2863
|
);
|
|
2864
|
+
let toFilename;
|
|
2865
|
+
try {
|
|
2866
|
+
toFilename = safePath$2(candidate, getAllowedRoots$1("data"));
|
|
2867
|
+
} catch (pathErr) {
|
|
2868
|
+
win.webContents.send(events$5.DATA_SAVE_TO_FILE_ERROR, {
|
|
2869
|
+
success: false,
|
|
2870
|
+
filename,
|
|
2871
|
+
message: pathErr.message,
|
|
2872
|
+
});
|
|
2873
|
+
return;
|
|
2874
|
+
}
|
|
2642
2875
|
|
|
2643
2876
|
//console.log("saving to file ", toFilename);
|
|
2644
2877
|
|
|
@@ -2715,8 +2948,8 @@ const dataController$1 = {
|
|
|
2715
2948
|
try {
|
|
2716
2949
|
if (filename) {
|
|
2717
2950
|
// filename to the pages file (live pages)
|
|
2718
|
-
const fromFilename = path$
|
|
2719
|
-
app$
|
|
2951
|
+
const fromFilename = path$g.join(
|
|
2952
|
+
app$a.getPath("userData"),
|
|
2720
2953
|
appName$5,
|
|
2721
2954
|
"data",
|
|
2722
2955
|
filename,
|
|
@@ -2796,9 +3029,9 @@ var dataController_1 = dataController$1;
|
|
|
2796
3029
|
* settingsController
|
|
2797
3030
|
*/
|
|
2798
3031
|
|
|
2799
|
-
const { app: app$
|
|
2800
|
-
const path$
|
|
2801
|
-
const fs$
|
|
3032
|
+
const { app: app$9 } = require$$0$1;
|
|
3033
|
+
const path$f = require$$1$2;
|
|
3034
|
+
const fs$a = require$$0$2;
|
|
2802
3035
|
const { getFileContents: getFileContents$4, writeToFile: writeToFile$1 } = file;
|
|
2803
3036
|
|
|
2804
3037
|
const configFilename$3 = "settings.json";
|
|
@@ -2806,15 +3039,15 @@ const appName$4 = "Dashboard";
|
|
|
2806
3039
|
|
|
2807
3040
|
// Helper function to recursively copy directory
|
|
2808
3041
|
function copyDirectory(source, destination) {
|
|
2809
|
-
if (!fs$
|
|
2810
|
-
fs$
|
|
3042
|
+
if (!fs$a.existsSync(destination)) {
|
|
3043
|
+
fs$a.mkdirSync(destination, { recursive: true });
|
|
2811
3044
|
}
|
|
2812
3045
|
|
|
2813
|
-
const files = fs$
|
|
3046
|
+
const files = fs$a.readdirSync(source);
|
|
2814
3047
|
for (const file of files) {
|
|
2815
|
-
const srcPath = path$
|
|
2816
|
-
const destPath = path$
|
|
2817
|
-
const stat = fs$
|
|
3048
|
+
const srcPath = path$f.join(source, file);
|
|
3049
|
+
const destPath = path$f.join(destination, file);
|
|
3050
|
+
const stat = fs$a.lstatSync(srcPath);
|
|
2818
3051
|
|
|
2819
3052
|
// Skip symlinks to prevent following links to sensitive files
|
|
2820
3053
|
if (stat.isSymbolicLink()) {
|
|
@@ -2825,7 +3058,7 @@ function copyDirectory(source, destination) {
|
|
|
2825
3058
|
if (stat.isDirectory()) {
|
|
2826
3059
|
copyDirectory(srcPath, destPath);
|
|
2827
3060
|
} else {
|
|
2828
|
-
fs$
|
|
3061
|
+
fs$a.copyFileSync(srcPath, destPath);
|
|
2829
3062
|
}
|
|
2830
3063
|
}
|
|
2831
3064
|
}
|
|
@@ -2841,8 +3074,8 @@ const settingsController$4 = {
|
|
|
2841
3074
|
try {
|
|
2842
3075
|
if (data) {
|
|
2843
3076
|
// <appId>/settings.json
|
|
2844
|
-
const filename = path$
|
|
2845
|
-
app$
|
|
3077
|
+
const filename = path$f.join(
|
|
3078
|
+
app$9.getPath("userData"),
|
|
2846
3079
|
appName$4,
|
|
2847
3080
|
configFilename$3,
|
|
2848
3081
|
);
|
|
@@ -2877,8 +3110,8 @@ const settingsController$4 = {
|
|
|
2877
3110
|
getSettingsForApplication: (win) => {
|
|
2878
3111
|
try {
|
|
2879
3112
|
// <appId>/settings.json
|
|
2880
|
-
const filename = path$
|
|
2881
|
-
app$
|
|
3113
|
+
const filename = path$f.join(
|
|
3114
|
+
app$9.getPath("userData"),
|
|
2882
3115
|
appName$4,
|
|
2883
3116
|
configFilename$3,
|
|
2884
3117
|
);
|
|
@@ -2908,15 +3141,15 @@ const settingsController$4 = {
|
|
|
2908
3141
|
*/
|
|
2909
3142
|
getDataDirectory: (win) => {
|
|
2910
3143
|
try {
|
|
2911
|
-
const settingsPath = path$
|
|
2912
|
-
app$
|
|
3144
|
+
const settingsPath = path$f.join(
|
|
3145
|
+
app$9.getPath("userData"),
|
|
2913
3146
|
appName$4,
|
|
2914
3147
|
configFilename$3,
|
|
2915
3148
|
);
|
|
2916
3149
|
const settings = getFileContents$4(settingsPath, {});
|
|
2917
3150
|
const userDataDir =
|
|
2918
3151
|
settings.userDataDirectory ||
|
|
2919
|
-
path$
|
|
3152
|
+
path$f.join(app$9.getPath("userData"), appName$4);
|
|
2920
3153
|
|
|
2921
3154
|
console.log("[settingsController] Data directory retrieved successfully");
|
|
2922
3155
|
// Return the data for ipcMain.handle() - modern promise-based approach
|
|
@@ -2943,18 +3176,18 @@ const settingsController$4 = {
|
|
|
2943
3176
|
setDataDirectory: (win, newPath) => {
|
|
2944
3177
|
try {
|
|
2945
3178
|
// Validate the path exists and is a directory
|
|
2946
|
-
if (!fs$
|
|
2947
|
-
fs$
|
|
3179
|
+
if (!fs$a.existsSync(newPath)) {
|
|
3180
|
+
fs$a.mkdirSync(newPath, { recursive: true });
|
|
2948
3181
|
}
|
|
2949
3182
|
|
|
2950
|
-
const stats = fs$
|
|
3183
|
+
const stats = fs$a.statSync(newPath);
|
|
2951
3184
|
if (!stats.isDirectory()) {
|
|
2952
3185
|
throw new Error("Path is not a directory");
|
|
2953
3186
|
}
|
|
2954
3187
|
|
|
2955
3188
|
// Update settings
|
|
2956
|
-
const settingsPath = path$
|
|
2957
|
-
app$
|
|
3189
|
+
const settingsPath = path$f.join(
|
|
3190
|
+
app$9.getPath("userData"),
|
|
2958
3191
|
appName$4,
|
|
2959
3192
|
configFilename$3,
|
|
2960
3193
|
);
|
|
@@ -2987,20 +3220,20 @@ const settingsController$4 = {
|
|
|
2987
3220
|
migrateDataDirectory: (win, oldPath, newPath) => {
|
|
2988
3221
|
try {
|
|
2989
3222
|
// Resolve paths to prevent traversal
|
|
2990
|
-
const resolvedOldPath = path$
|
|
2991
|
-
const resolvedNewPath = path$
|
|
3223
|
+
const resolvedOldPath = path$f.resolve(oldPath);
|
|
3224
|
+
const resolvedNewPath = path$f.resolve(newPath);
|
|
2992
3225
|
|
|
2993
3226
|
// Validate oldPath is the current configured data directory
|
|
2994
|
-
const settingsCheckPath = path$
|
|
2995
|
-
app$
|
|
3227
|
+
const settingsCheckPath = path$f.join(
|
|
3228
|
+
app$9.getPath("userData"),
|
|
2996
3229
|
appName$4,
|
|
2997
3230
|
configFilename$3,
|
|
2998
3231
|
);
|
|
2999
3232
|
const currentSettings = getFileContents$4(settingsCheckPath, {});
|
|
3000
3233
|
const currentDataDir =
|
|
3001
3234
|
currentSettings.userDataDirectory ||
|
|
3002
|
-
path$
|
|
3003
|
-
if (resolvedOldPath !== path$
|
|
3235
|
+
path$f.join(app$9.getPath("userData"), appName$4);
|
|
3236
|
+
if (resolvedOldPath !== path$f.resolve(currentDataDir)) {
|
|
3004
3237
|
throw new Error("Source path must be the current data directory");
|
|
3005
3238
|
}
|
|
3006
3239
|
|
|
@@ -3024,20 +3257,20 @@ const settingsController$4 = {
|
|
|
3024
3257
|
}
|
|
3025
3258
|
|
|
3026
3259
|
// Validate paths
|
|
3027
|
-
if (!fs$
|
|
3260
|
+
if (!fs$a.existsSync(resolvedOldPath)) {
|
|
3028
3261
|
throw new Error("Source directory does not exist");
|
|
3029
3262
|
}
|
|
3030
3263
|
|
|
3031
|
-
if (!fs$
|
|
3032
|
-
fs$
|
|
3264
|
+
if (!fs$a.existsSync(resolvedNewPath)) {
|
|
3265
|
+
fs$a.mkdirSync(resolvedNewPath, { recursive: true });
|
|
3033
3266
|
}
|
|
3034
3267
|
|
|
3035
3268
|
// Copy files
|
|
3036
3269
|
copyDirectory(resolvedOldPath, resolvedNewPath);
|
|
3037
3270
|
|
|
3038
3271
|
// Update settings to use new path
|
|
3039
|
-
const settingsPath = path$
|
|
3040
|
-
app$
|
|
3272
|
+
const settingsPath = path$f.join(
|
|
3273
|
+
app$9.getPath("userData"),
|
|
3041
3274
|
appName$4,
|
|
3042
3275
|
configFilename$3,
|
|
3043
3276
|
);
|
|
@@ -3701,8 +3934,8 @@ function requireProviderController () {
|
|
|
3701
3934
|
return providerController_1;
|
|
3702
3935
|
}
|
|
3703
3936
|
|
|
3704
|
-
const { app: app$
|
|
3705
|
-
const path$
|
|
3937
|
+
const { app: app$8 } = require$$0$1;
|
|
3938
|
+
const path$e = require$$1$2;
|
|
3706
3939
|
const { writeFileSync: writeFileSync$1 } = require$$0$2;
|
|
3707
3940
|
const events$4 = events$8;
|
|
3708
3941
|
const { getFileContents: getFileContents$3 } = file;
|
|
@@ -3722,8 +3955,8 @@ const layoutController$1 = {
|
|
|
3722
3955
|
saveLayoutForApplication: (win, appId, layoutObject) => {
|
|
3723
3956
|
try {
|
|
3724
3957
|
// filename to the pages file (live pages)
|
|
3725
|
-
const filename = path$
|
|
3726
|
-
app$
|
|
3958
|
+
const filename = path$e.join(
|
|
3959
|
+
app$8.getPath("userData"),
|
|
3727
3960
|
appName$3,
|
|
3728
3961
|
appId,
|
|
3729
3962
|
configFilename$2,
|
|
@@ -3755,8 +3988,8 @@ const layoutController$1 = {
|
|
|
3755
3988
|
*/
|
|
3756
3989
|
listLayoutsForApplication: (win, appId) => {
|
|
3757
3990
|
try {
|
|
3758
|
-
const filename = path$
|
|
3759
|
-
app$
|
|
3991
|
+
const filename = path$e.join(
|
|
3992
|
+
app$8.getPath("userData"),
|
|
3760
3993
|
appName$3,
|
|
3761
3994
|
appId,
|
|
3762
3995
|
configFilename$2,
|
|
@@ -20919,6 +21152,311 @@ let StreamableHTTPClientTransport$1 = class StreamableHTTPClientTransport {
|
|
|
20919
21152
|
};
|
|
20920
21153
|
streamableHttp$1.StreamableHTTPClientTransport = StreamableHTTPClientTransport$1;
|
|
20921
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
|
+
|
|
20922
21460
|
/**
|
|
20923
21461
|
* mcpController.js
|
|
20924
21462
|
*
|
|
@@ -20943,6 +21481,28 @@ const path$c = require$$1$2;
|
|
|
20943
21481
|
const fs$8 = require$$0$2;
|
|
20944
21482
|
const os$1 = require$$2$1;
|
|
20945
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
|
+
}
|
|
20946
21506
|
|
|
20947
21507
|
/**
|
|
20948
21508
|
* Tool name prefixes considered safe to cache (read-only).
|
|
@@ -21078,7 +21638,7 @@ function getShellPath$1() {
|
|
|
21078
21638
|
return _shellPath$1;
|
|
21079
21639
|
}
|
|
21080
21640
|
|
|
21081
|
-
const { execSync } = require$$
|
|
21641
|
+
const { execSync } = require$$9$1;
|
|
21082
21642
|
const fallbackDirs = ["/usr/local/bin", "/opt/homebrew/bin"];
|
|
21083
21643
|
|
|
21084
21644
|
// Scan nvm versions, tracking both latest and best compatible version
|
|
@@ -21258,7 +21818,7 @@ async function refreshGoogleOAuthToken(tokenRefresh) {
|
|
|
21258
21818
|
|
|
21259
21819
|
console.log("[mcpController] Refreshing Google OAuth token...");
|
|
21260
21820
|
|
|
21261
|
-
const https = require$$
|
|
21821
|
+
const https = require$$8$1;
|
|
21262
21822
|
const postData = [
|
|
21263
21823
|
`client_id=${encodeURIComponent(keyData.client_id)}`,
|
|
21264
21824
|
`client_secret=${encodeURIComponent(keyData.client_secret)}`,
|
|
@@ -21647,14 +22207,41 @@ const mcpController$3 = {
|
|
|
21647
22207
|
* @param {Array<string>} allowedTools optional whitelist of allowed tool names
|
|
21648
22208
|
* @returns {{ result } | { error, message }}
|
|
21649
22209
|
*/
|
|
21650
|
-
callTool: async (
|
|
22210
|
+
callTool: async (
|
|
22211
|
+
win,
|
|
22212
|
+
serverName,
|
|
22213
|
+
toolName,
|
|
22214
|
+
args,
|
|
22215
|
+
allowedTools = null,
|
|
22216
|
+
widgetId = null,
|
|
22217
|
+
) => {
|
|
21651
22218
|
try {
|
|
21652
22219
|
const server = activeServers.get(serverName);
|
|
21653
22220
|
if (!server || !server.client) {
|
|
21654
22221
|
throw new Error(`Server not connected: ${serverName}`);
|
|
21655
22222
|
}
|
|
21656
22223
|
|
|
21657
|
-
//
|
|
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.
|
|
21658
22245
|
if (allowedTools && !allowedTools.includes(toolName)) {
|
|
21659
22246
|
throw new Error(
|
|
21660
22247
|
`Tool "${toolName}" is not in the allowed tools list for this widget. Allowed: ${allowedTools.join(
|
|
@@ -21951,7 +22538,7 @@ const mcpController$3 = {
|
|
|
21951
22538
|
* @returns {{ success } | { error, message }}
|
|
21952
22539
|
*/
|
|
21953
22540
|
runAuth: async (win, mcpConfig, credentials, authCommand) => {
|
|
21954
|
-
const { spawn } = require$$
|
|
22541
|
+
const { spawn } = require$$9$1;
|
|
21955
22542
|
|
|
21956
22543
|
const env = cleanEnvForChildProcess();
|
|
21957
22544
|
|
|
@@ -25973,6 +26560,7 @@ const algoliasearch = require$$2$3;
|
|
|
25973
26560
|
const events$3 = events$8;
|
|
25974
26561
|
const AlgoliaIndex = algolia;
|
|
25975
26562
|
var fs$3 = require$$0$2;
|
|
26563
|
+
const { safePath, getAllowedRoots } = safePath_1;
|
|
25976
26564
|
|
|
25977
26565
|
const algoliaController$1 = {
|
|
25978
26566
|
/**
|
|
@@ -26129,10 +26717,19 @@ const algoliaController$1 = {
|
|
|
26129
26717
|
createIfNotExists = false,
|
|
26130
26718
|
) {
|
|
26131
26719
|
try {
|
|
26720
|
+
let validatedDir;
|
|
26721
|
+
try {
|
|
26722
|
+
validatedDir = safePath(dir, getAllowedRoots("data"));
|
|
26723
|
+
} catch (pathErr) {
|
|
26724
|
+
win.webContents.send(events$3.ALGOLIA_PARTIAL_UPDATE_OBJECTS_ERROR, {
|
|
26725
|
+
error: pathErr.message,
|
|
26726
|
+
});
|
|
26727
|
+
return;
|
|
26728
|
+
}
|
|
26132
26729
|
const a = new AlgoliaIndex(appId, apiKey, indexName);
|
|
26133
26730
|
// now we can make the call to the utility and we are passing in the createIfNotExists FALSE by default
|
|
26134
26731
|
a.partialUpdateObjectsFromDirectorySync(
|
|
26135
|
-
|
|
26732
|
+
validatedDir,
|
|
26136
26733
|
createIfNotExists,
|
|
26137
26734
|
(data) => {
|
|
26138
26735
|
win.webContents.send(
|
|
@@ -26172,10 +26769,21 @@ const algoliaController$1 = {
|
|
|
26172
26769
|
batchSize = 500,
|
|
26173
26770
|
) => {
|
|
26174
26771
|
try {
|
|
26772
|
+
let validatedIn, validatedOut;
|
|
26773
|
+
try {
|
|
26774
|
+
const roots = getAllowedRoots("data");
|
|
26775
|
+
validatedIn = safePath(filepath, roots);
|
|
26776
|
+
validatedOut = safePath(batchFilepath, roots);
|
|
26777
|
+
} catch (pathErr) {
|
|
26778
|
+
win.webContents.send(events$3.ALGOLIA_CREATE_BATCH_ERROR, {
|
|
26779
|
+
error: pathErr.message,
|
|
26780
|
+
});
|
|
26781
|
+
return;
|
|
26782
|
+
}
|
|
26175
26783
|
const a = new AlgoliaIndex();
|
|
26176
26784
|
a.createBatchesFromJSONFile(
|
|
26177
|
-
|
|
26178
|
-
|
|
26785
|
+
validatedIn,
|
|
26786
|
+
validatedOut,
|
|
26179
26787
|
batchSize,
|
|
26180
26788
|
(data) => {
|
|
26181
26789
|
win.webContents.send(events$3.ALGOLIA_CREATE_BATCH_UPDATE, data);
|
|
@@ -43641,7 +44249,7 @@ const completable_js_1 = completable;
|
|
|
43641
44249
|
const uriTemplate_js_1 = uriTemplate;
|
|
43642
44250
|
const toolNameValidation_js_1 = toolNameValidation;
|
|
43643
44251
|
const mcp_server_js_1 = mcpServer$1;
|
|
43644
|
-
const zod_1 = require$$8$
|
|
44252
|
+
const zod_1 = require$$8$2;
|
|
43645
44253
|
/**
|
|
43646
44254
|
* High-level MCP server that provides a simpler API for working with resources, tools, and prompts.
|
|
43647
44255
|
* For advanced usage (like sending notifications or setting custom request handlers), use the underlying
|
|
@@ -46274,7 +46882,7 @@ var tlsCert = { getOrCreateCert: getOrCreateCert$1 };
|
|
|
46274
46882
|
* for Zod schemas in tool input validation (safeParseAsync).
|
|
46275
46883
|
*/
|
|
46276
46884
|
|
|
46277
|
-
const z$1 = require$$8$
|
|
46885
|
+
const z$1 = require$$8$2;
|
|
46278
46886
|
|
|
46279
46887
|
/**
|
|
46280
46888
|
* Convert a JSON Schema property definition to a Zod v3 schema.
|
|
@@ -46367,7 +46975,7 @@ var jsonSchemaToZod_1 = { jsonSchemaToZod: jsonSchemaToZod$1, jsonSchemaProperty
|
|
|
46367
46975
|
* - Rate limiting via token bucket (60 req/min)
|
|
46368
46976
|
*/
|
|
46369
46977
|
|
|
46370
|
-
const https$1 = require$$
|
|
46978
|
+
const https$1 = require$$8$1;
|
|
46371
46979
|
const { randomUUID } = require$$3$4;
|
|
46372
46980
|
const { BrowserWindow: BrowserWindow$1 } = require$$0$1;
|
|
46373
46981
|
const { McpServer } = mcp;
|
|
@@ -46498,7 +47106,7 @@ function registerPrompt$1(promptDef) {
|
|
|
46498
47106
|
registeredPrompts.push(promptDef);
|
|
46499
47107
|
}
|
|
46500
47108
|
|
|
46501
|
-
const z = require$$8$
|
|
47109
|
+
const z = require$$8$2;
|
|
46502
47110
|
const { jsonSchemaToZod } = jsonSchemaToZod_1;
|
|
46503
47111
|
|
|
46504
47112
|
/**
|
|
@@ -46879,7 +47487,7 @@ var mcpDashServerController_1 = mcpDashServerController$4;
|
|
|
46879
47487
|
* can use the Chat widget without a separate API key.
|
|
46880
47488
|
*/
|
|
46881
47489
|
|
|
46882
|
-
const { spawn, execSync } = require$$
|
|
47490
|
+
const { spawn, execSync } = require$$9$1;
|
|
46883
47491
|
const {
|
|
46884
47492
|
LLM_STREAM_DELTA: LLM_STREAM_DELTA$2,
|
|
46885
47493
|
LLM_STREAM_TOOL_CALL: LLM_STREAM_TOOL_CALL$2,
|
|
@@ -48176,7 +48784,7 @@ var themeFromUrlErrors$1 = {
|
|
|
48176
48784
|
|
|
48177
48785
|
const css = require$$0$8;
|
|
48178
48786
|
const { Vibrant } = require$$1$7;
|
|
48179
|
-
const https = require$$
|
|
48787
|
+
const https = require$$8$1;
|
|
48180
48788
|
const http = require$$0$7;
|
|
48181
48789
|
const { URL: URL$1 } = require$$4$1;
|
|
48182
48790
|
const {
|
|
@@ -61216,15 +61824,27 @@ const mcpApi$2 = {
|
|
|
61216
61824
|
* @param {string} serverName the server name
|
|
61217
61825
|
* @param {string} toolName the tool to call
|
|
61218
61826
|
* @param {object} args tool arguments
|
|
61219
|
-
* @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").
|
|
61220
61833
|
* @returns {Promise<{ result } | { error, message }>}
|
|
61221
61834
|
*/
|
|
61222
|
-
callTool: (
|
|
61835
|
+
callTool: (
|
|
61836
|
+
serverName,
|
|
61837
|
+
toolName,
|
|
61838
|
+
args,
|
|
61839
|
+
allowedTools = null,
|
|
61840
|
+
widgetId = null,
|
|
61841
|
+
) =>
|
|
61223
61842
|
ipcRenderer$i.invoke(MCP_CALL_TOOL, {
|
|
61224
61843
|
serverName,
|
|
61225
61844
|
toolName,
|
|
61226
61845
|
args,
|
|
61227
61846
|
allowedTools,
|
|
61847
|
+
widgetId,
|
|
61228
61848
|
}),
|
|
61229
61849
|
|
|
61230
61850
|
/**
|