@trops/dash-core 0.1.523 → 0.1.525
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 +249 -0
- package/dist/electron/index.js.map +1 -1
- package/dist/index.esm.js +406 -130
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +407 -129
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/electron/index.js
CHANGED
|
@@ -3801,6 +3801,214 @@ var manifestScanner = {
|
|
|
3801
3801
|
SCAN_FILE_LIMIT,
|
|
3802
3802
|
};
|
|
3803
3803
|
|
|
3804
|
+
/**
|
|
3805
|
+
* scanWidgetPackagePermissions
|
|
3806
|
+
*
|
|
3807
|
+
* Walks a widget package directory, statically extracts MCP usage
|
|
3808
|
+
* (`useMcpProvider("type")` + `callTool("name", ...)`) from every
|
|
3809
|
+
* source file, and returns the canonical `dash.permissions.mcp` block
|
|
3810
|
+
* for embedding in the package's `package.json`.
|
|
3811
|
+
*
|
|
3812
|
+
* Used at three boundaries to keep declared permissions in sync with
|
|
3813
|
+
* the actual code:
|
|
3814
|
+
* - Widget publish flow (registry:publish-widget) — declarations
|
|
3815
|
+
* ship with the package.
|
|
3816
|
+
* - Widget install flow (installFromLocalPath) — declarations are
|
|
3817
|
+
* re-derived for already-published packages whose authors didn't
|
|
3818
|
+
* run the scanner, AND for new versions that updated their tool
|
|
3819
|
+
* set without updating the manifest.
|
|
3820
|
+
* - AI builder (widget:ai-build) — same scanner, single source of
|
|
3821
|
+
* truth.
|
|
3822
|
+
*
|
|
3823
|
+
* MERGE policy: scanner output is additive. Hand-authored entries in
|
|
3824
|
+
* `package.json.dash.permissions.mcp` are preserved; scanner-found
|
|
3825
|
+
* entries the human missed are appended. Idempotent — repeated runs
|
|
3826
|
+
* produce the same package.json (assuming source code unchanged).
|
|
3827
|
+
*
|
|
3828
|
+
* Limitations (acceptable, documented):
|
|
3829
|
+
* - Single-string regex scan. Variable-indirected calls like
|
|
3830
|
+
* `callTool(toolName, ...)` are skipped — runtime gate is the
|
|
3831
|
+
* safety net.
|
|
3832
|
+
* - Comments are stripped (line `//` only) so commented-out
|
|
3833
|
+
* examples don't pollute the declaration.
|
|
3834
|
+
* - Walks `widgets/`, `src/`, and the package root for `.js`,
|
|
3835
|
+
* `.jsx`, `.ts`, `.tsx` files. Skips `node_modules/` and `dist/`.
|
|
3836
|
+
*/
|
|
3837
|
+
|
|
3838
|
+
var scanWidgetPackagePermissions_1;
|
|
3839
|
+
var hasRequiredScanWidgetPackagePermissions;
|
|
3840
|
+
|
|
3841
|
+
function requireScanWidgetPackagePermissions () {
|
|
3842
|
+
if (hasRequiredScanWidgetPackagePermissions) return scanWidgetPackagePermissions_1;
|
|
3843
|
+
hasRequiredScanWidgetPackagePermissions = 1;
|
|
3844
|
+
|
|
3845
|
+
const fs = require$$0$2;
|
|
3846
|
+
const path = require$$1$1;
|
|
3847
|
+
|
|
3848
|
+
const SOURCE_EXTS = new Set([".js", ".jsx", ".ts", ".tsx"]);
|
|
3849
|
+
const SKIP_DIRS = new Set([
|
|
3850
|
+
"node_modules",
|
|
3851
|
+
"dist",
|
|
3852
|
+
"build",
|
|
3853
|
+
".git",
|
|
3854
|
+
"__tests__",
|
|
3855
|
+
"__mocks__",
|
|
3856
|
+
]);
|
|
3857
|
+
|
|
3858
|
+
function _stripLineComments(code) {
|
|
3859
|
+
return code.replace(/\/\/[^\n]*/g, "");
|
|
3860
|
+
}
|
|
3861
|
+
|
|
3862
|
+
function _captureAll(code, pattern) {
|
|
3863
|
+
const out = [];
|
|
3864
|
+
for (const match of code.matchAll(pattern)) {
|
|
3865
|
+
out.push(match[1]);
|
|
3866
|
+
}
|
|
3867
|
+
return out;
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
/**
|
|
3871
|
+
* Scan a single file's contents and return any detected MCP usage.
|
|
3872
|
+
* @param {string} code
|
|
3873
|
+
* @returns {{providers: string[], tools: string[]}}
|
|
3874
|
+
*/
|
|
3875
|
+
function scanFileForMcpUsage(code) {
|
|
3876
|
+
if (typeof code !== "string" || !code) return { providers: [], tools: [] };
|
|
3877
|
+
const stripped = _stripLineComments(code);
|
|
3878
|
+
const providerPattern = /useMcpProvider\s*\(\s*["'`]([^"'`]+)["'`]/g;
|
|
3879
|
+
const callPattern = /callTool\s*\(\s*["'`]([^"'`]+)["'`]/g;
|
|
3880
|
+
return {
|
|
3881
|
+
providers: Array.from(new Set(_captureAll(stripped, providerPattern))),
|
|
3882
|
+
tools: Array.from(new Set(_captureAll(stripped, callPattern))),
|
|
3883
|
+
};
|
|
3884
|
+
}
|
|
3885
|
+
|
|
3886
|
+
function _walkSourceFiles(dir) {
|
|
3887
|
+
const out = [];
|
|
3888
|
+
if (!fs.existsSync(dir)) return out;
|
|
3889
|
+
let entries;
|
|
3890
|
+
try {
|
|
3891
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
3892
|
+
} catch (_) {
|
|
3893
|
+
return out;
|
|
3894
|
+
}
|
|
3895
|
+
for (const entry of entries) {
|
|
3896
|
+
if (entry.isDirectory()) {
|
|
3897
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
3898
|
+
out.push(..._walkSourceFiles(path.join(dir, entry.name)));
|
|
3899
|
+
} else if (entry.isFile()) {
|
|
3900
|
+
const ext = path.extname(entry.name);
|
|
3901
|
+
if (SOURCE_EXTS.has(ext)) out.push(path.join(dir, entry.name));
|
|
3902
|
+
}
|
|
3903
|
+
}
|
|
3904
|
+
return out;
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3907
|
+
/**
|
|
3908
|
+
* Walk packageDir, scan every source file, return the canonical
|
|
3909
|
+
* `dash.permissions.mcp` block. Returns `{}` when no MCP usage found.
|
|
3910
|
+
*/
|
|
3911
|
+
function scanWidgetPackagePermissions(packageDir) {
|
|
3912
|
+
if (typeof packageDir !== "string" || !fs.existsSync(packageDir)) return {};
|
|
3913
|
+
|
|
3914
|
+
const allProviders = new Set();
|
|
3915
|
+
const allTools = new Set();
|
|
3916
|
+
for (const filePath of _walkSourceFiles(packageDir)) {
|
|
3917
|
+
let code;
|
|
3918
|
+
try {
|
|
3919
|
+
code = fs.readFileSync(filePath, "utf8");
|
|
3920
|
+
} catch (_) {
|
|
3921
|
+
continue;
|
|
3922
|
+
}
|
|
3923
|
+
const { providers, tools } = scanFileForMcpUsage(code);
|
|
3924
|
+
for (const p of providers) allProviders.add(p);
|
|
3925
|
+
for (const t of tools) allTools.add(t);
|
|
3926
|
+
}
|
|
3927
|
+
|
|
3928
|
+
if (allProviders.size === 0 || allTools.size === 0) return {};
|
|
3929
|
+
|
|
3930
|
+
const out = {};
|
|
3931
|
+
for (const provider of allProviders) {
|
|
3932
|
+
out[provider] = { tools: Array.from(allTools).sort() };
|
|
3933
|
+
}
|
|
3934
|
+
return out;
|
|
3935
|
+
}
|
|
3936
|
+
|
|
3937
|
+
/**
|
|
3938
|
+
* Merge scanner output with any hand-authored block. Additive:
|
|
3939
|
+
* - Servers in `human` are kept as-is.
|
|
3940
|
+
* - Servers in `scanned` not in `human` are added.
|
|
3941
|
+
* - When the same server is in both, the union of `tools` arrays is
|
|
3942
|
+
* written, preserving any read/write paths the human declared.
|
|
3943
|
+
*/
|
|
3944
|
+
function mergePermissions(human, scanned) {
|
|
3945
|
+
const out = {};
|
|
3946
|
+
// Start with human entries (preserves their shape including paths).
|
|
3947
|
+
if (human && typeof human === "object") {
|
|
3948
|
+
for (const [name, perms] of Object.entries(human)) {
|
|
3949
|
+
out[name] = { ...perms };
|
|
3950
|
+
if (Array.isArray(perms.tools)) out[name].tools = [...perms.tools];
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
if (scanned && typeof scanned === "object") {
|
|
3954
|
+
for (const [name, perms] of Object.entries(scanned)) {
|
|
3955
|
+
if (!out[name]) {
|
|
3956
|
+
out[name] = { tools: [...(perms.tools || [])] };
|
|
3957
|
+
} else {
|
|
3958
|
+
const existing = new Set(out[name].tools || []);
|
|
3959
|
+
for (const t of perms.tools || []) existing.add(t);
|
|
3960
|
+
out[name].tools = Array.from(existing).sort();
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
return out;
|
|
3965
|
+
}
|
|
3966
|
+
|
|
3967
|
+
/**
|
|
3968
|
+
* Apply the scanner's findings to `package.json` in `packageDir` —
|
|
3969
|
+
* read, merge with any existing `dash.permissions.mcp` block, write
|
|
3970
|
+
* back. Returns the resulting block (or null if no package.json).
|
|
3971
|
+
*/
|
|
3972
|
+
function applyScanToPackageJson(packageDir) {
|
|
3973
|
+
const pkgPath = path.join(packageDir, "package.json");
|
|
3974
|
+
let existing = {};
|
|
3975
|
+
if (fs.existsSync(pkgPath)) {
|
|
3976
|
+
try {
|
|
3977
|
+
existing = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
3978
|
+
} catch (_) {
|
|
3979
|
+
existing = {};
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
const scanned = scanWidgetPackagePermissions(packageDir);
|
|
3983
|
+
const human = existing.dash?.permissions?.mcp || null;
|
|
3984
|
+
const merged = mergePermissions(human, scanned);
|
|
3985
|
+
if (Object.keys(merged).length === 0) {
|
|
3986
|
+
// Nothing to write. Don't churn the file.
|
|
3987
|
+
return null;
|
|
3988
|
+
}
|
|
3989
|
+
const next = {
|
|
3990
|
+
...existing,
|
|
3991
|
+
dash: {
|
|
3992
|
+
...(existing.dash || {}),
|
|
3993
|
+
permissions: {
|
|
3994
|
+
...((existing.dash || {}).permissions || {}),
|
|
3995
|
+
mcp: merged,
|
|
3996
|
+
},
|
|
3997
|
+
},
|
|
3998
|
+
};
|
|
3999
|
+
fs.writeFileSync(pkgPath, JSON.stringify(next, null, 2), "utf8");
|
|
4000
|
+
return merged;
|
|
4001
|
+
}
|
|
4002
|
+
|
|
4003
|
+
scanWidgetPackagePermissions_1 = {
|
|
4004
|
+
scanFileForMcpUsage,
|
|
4005
|
+
scanWidgetPackagePermissions,
|
|
4006
|
+
mergePermissions,
|
|
4007
|
+
applyScanToPackageJson,
|
|
4008
|
+
};
|
|
4009
|
+
return scanWidgetPackagePermissions_1;
|
|
4010
|
+
}
|
|
4011
|
+
|
|
3804
4012
|
/**
|
|
3805
4013
|
* schedulerController.js
|
|
3806
4014
|
*
|
|
@@ -4823,6 +5031,23 @@ var schedulerController_1 = schedulerController$2;
|
|
|
4823
5031
|
throw new Error(`Unsupported local source type: ${resolvedPath}`);
|
|
4824
5032
|
}
|
|
4825
5033
|
|
|
5034
|
+
// Slice 13a: scan the freshly-installed widget for MCP usage and
|
|
5035
|
+
// update the package.json's `dash.permissions.mcp` block. Always
|
|
5036
|
+
// runs (even when a manifest already exists) — catches author
|
|
5037
|
+
// forgetfulness and version updates that introduced new tools.
|
|
5038
|
+
// Merge is additive; hand-authored entries are preserved.
|
|
5039
|
+
try {
|
|
5040
|
+
const {
|
|
5041
|
+
applyScanToPackageJson,
|
|
5042
|
+
} = requireScanWidgetPackagePermissions();
|
|
5043
|
+
applyScanToPackageJson(widgetPath);
|
|
5044
|
+
} catch (e) {
|
|
5045
|
+
console.warn(
|
|
5046
|
+
`[WidgetRegistry] Permission scan failed for ${widgetName}: ${e.message}`,
|
|
5047
|
+
);
|
|
5048
|
+
// Non-fatal — the gate's runtime JIT still backs us up.
|
|
5049
|
+
}
|
|
5050
|
+
|
|
4826
5051
|
let config = await this.loadWidgetConfig(widgetName, widgetPath);
|
|
4827
5052
|
|
|
4828
5053
|
if (dashConfigPath) {
|
|
@@ -63166,6 +63391,30 @@ async function prepareWidgetForPublish$1(appId, packageId, options = {}) {
|
|
|
63166
63391
|
const parsedName = parsePackageName(pkgJson.name || "");
|
|
63167
63392
|
const resolvedScope = options.scope || callerScope;
|
|
63168
63393
|
|
|
63394
|
+
// Slice 13a: scan source for MCP usage and refresh the
|
|
63395
|
+
// `dash.permissions.mcp` block in package.json BEFORE the zip is
|
|
63396
|
+
// built. Always runs (even when a manifest exists) — catches
|
|
63397
|
+
// author forgetfulness and version updates that introduced new
|
|
63398
|
+
// tools. Merge is additive; hand-authored entries are preserved.
|
|
63399
|
+
try {
|
|
63400
|
+
const {
|
|
63401
|
+
applyScanToPackageJson,
|
|
63402
|
+
} = requireScanWidgetPackagePermissions();
|
|
63403
|
+
const merged = applyScanToPackageJson(widget.path);
|
|
63404
|
+
if (merged) {
|
|
63405
|
+
// Re-read pkgJson so subsequent steps in this function see the
|
|
63406
|
+
// updated manifest (the version bump below uses pkgJson too).
|
|
63407
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
63408
|
+
pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
|
|
63409
|
+
}
|
|
63410
|
+
}
|
|
63411
|
+
} catch (e) {
|
|
63412
|
+
console.warn(
|
|
63413
|
+
`[widgetRegistryController] Permission scan failed for ${packageId}: ${e.message}`,
|
|
63414
|
+
);
|
|
63415
|
+
// Non-fatal — runtime gate is still in place.
|
|
63416
|
+
}
|
|
63417
|
+
|
|
63169
63418
|
// 3.5 Pre-zip privacy scan. Flag any personal filesystem paths baked
|
|
63170
63419
|
// into shipped source (e.g. someone edited a `.dash.js`'s
|
|
63171
63420
|
// `defaultValue` from `~/Library/...` to `/Users/me/...` to skip
|