@dforge-core/dforge-mcp 0.1.0-rc.3 → 0.1.0-rc.4
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/README.md +122 -37
- package/dist/server.js +785 -121
- package/package.json +2 -2
- package/resources/schemas/entity.schema.json +1 -2
- package/resources/schemas/folders.schema.json +52 -15
- package/resources/schemas/manifest.schema.json +34 -3
- package/resources/schemas/menus.schema.json +1 -1
- package/resources/schemas/reports.schema.json +18 -1
- package/skills/dforge-mcp-author/SKILL.md +190 -79
package/dist/server.js
CHANGED
|
@@ -2981,7 +2981,7 @@ var require_compile = __commonJS({
|
|
|
2981
2981
|
const schOrFunc = root.refs[ref];
|
|
2982
2982
|
if (schOrFunc)
|
|
2983
2983
|
return schOrFunc;
|
|
2984
|
-
let _sch =
|
|
2984
|
+
let _sch = resolve5.call(this, root, ref);
|
|
2985
2985
|
if (_sch === void 0) {
|
|
2986
2986
|
const schema2 = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
2987
2987
|
const { schemaId } = this.opts;
|
|
@@ -3008,7 +3008,7 @@ var require_compile = __commonJS({
|
|
|
3008
3008
|
function sameSchemaEnv(s1, s2) {
|
|
3009
3009
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3010
3010
|
}
|
|
3011
|
-
function
|
|
3011
|
+
function resolve5(root, ref) {
|
|
3012
3012
|
let sch;
|
|
3013
3013
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3014
3014
|
ref = sch;
|
|
@@ -3226,8 +3226,8 @@ var require_utils = __commonJS({
|
|
|
3226
3226
|
}
|
|
3227
3227
|
return ind;
|
|
3228
3228
|
}
|
|
3229
|
-
function removeDotSegments(
|
|
3230
|
-
let input =
|
|
3229
|
+
function removeDotSegments(path8) {
|
|
3230
|
+
let input = path8;
|
|
3231
3231
|
const output = [];
|
|
3232
3232
|
let nextSlash = -1;
|
|
3233
3233
|
let len = 0;
|
|
@@ -3479,8 +3479,8 @@ var require_schemes = __commonJS({
|
|
|
3479
3479
|
wsComponent.secure = void 0;
|
|
3480
3480
|
}
|
|
3481
3481
|
if (wsComponent.resourceName) {
|
|
3482
|
-
const [
|
|
3483
|
-
wsComponent.path =
|
|
3482
|
+
const [path8, query] = wsComponent.resourceName.split("?");
|
|
3483
|
+
wsComponent.path = path8 && path8 !== "/" ? path8 : void 0;
|
|
3484
3484
|
wsComponent.query = query;
|
|
3485
3485
|
wsComponent.resourceName = void 0;
|
|
3486
3486
|
}
|
|
@@ -3639,55 +3639,55 @@ var require_fast_uri = __commonJS({
|
|
|
3639
3639
|
}
|
|
3640
3640
|
return uri;
|
|
3641
3641
|
}
|
|
3642
|
-
function
|
|
3642
|
+
function resolve5(baseURI, relativeURI, options) {
|
|
3643
3643
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3644
3644
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
3645
3645
|
schemelessOptions.skipEscape = true;
|
|
3646
3646
|
return serialize(resolved, schemelessOptions);
|
|
3647
3647
|
}
|
|
3648
|
-
function resolveComponent(base,
|
|
3648
|
+
function resolveComponent(base, relative2, options, skipNormalization) {
|
|
3649
3649
|
const target = {};
|
|
3650
3650
|
if (!skipNormalization) {
|
|
3651
3651
|
base = parse3(serialize(base, options), options);
|
|
3652
|
-
|
|
3652
|
+
relative2 = parse3(serialize(relative2, options), options);
|
|
3653
3653
|
}
|
|
3654
3654
|
options = options || {};
|
|
3655
|
-
if (!options.tolerant &&
|
|
3656
|
-
target.scheme =
|
|
3657
|
-
target.userinfo =
|
|
3658
|
-
target.host =
|
|
3659
|
-
target.port =
|
|
3660
|
-
target.path = removeDotSegments(
|
|
3661
|
-
target.query =
|
|
3655
|
+
if (!options.tolerant && relative2.scheme) {
|
|
3656
|
+
target.scheme = relative2.scheme;
|
|
3657
|
+
target.userinfo = relative2.userinfo;
|
|
3658
|
+
target.host = relative2.host;
|
|
3659
|
+
target.port = relative2.port;
|
|
3660
|
+
target.path = removeDotSegments(relative2.path || "");
|
|
3661
|
+
target.query = relative2.query;
|
|
3662
3662
|
} else {
|
|
3663
|
-
if (
|
|
3664
|
-
target.userinfo =
|
|
3665
|
-
target.host =
|
|
3666
|
-
target.port =
|
|
3667
|
-
target.path = removeDotSegments(
|
|
3668
|
-
target.query =
|
|
3663
|
+
if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
|
|
3664
|
+
target.userinfo = relative2.userinfo;
|
|
3665
|
+
target.host = relative2.host;
|
|
3666
|
+
target.port = relative2.port;
|
|
3667
|
+
target.path = removeDotSegments(relative2.path || "");
|
|
3668
|
+
target.query = relative2.query;
|
|
3669
3669
|
} else {
|
|
3670
|
-
if (!
|
|
3670
|
+
if (!relative2.path) {
|
|
3671
3671
|
target.path = base.path;
|
|
3672
|
-
if (
|
|
3673
|
-
target.query =
|
|
3672
|
+
if (relative2.query !== void 0) {
|
|
3673
|
+
target.query = relative2.query;
|
|
3674
3674
|
} else {
|
|
3675
3675
|
target.query = base.query;
|
|
3676
3676
|
}
|
|
3677
3677
|
} else {
|
|
3678
|
-
if (
|
|
3679
|
-
target.path = removeDotSegments(
|
|
3678
|
+
if (relative2.path[0] === "/") {
|
|
3679
|
+
target.path = removeDotSegments(relative2.path);
|
|
3680
3680
|
} else {
|
|
3681
3681
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
3682
|
-
target.path = "/" +
|
|
3682
|
+
target.path = "/" + relative2.path;
|
|
3683
3683
|
} else if (!base.path) {
|
|
3684
|
-
target.path =
|
|
3684
|
+
target.path = relative2.path;
|
|
3685
3685
|
} else {
|
|
3686
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
3686
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
|
|
3687
3687
|
}
|
|
3688
3688
|
target.path = removeDotSegments(target.path);
|
|
3689
3689
|
}
|
|
3690
|
-
target.query =
|
|
3690
|
+
target.query = relative2.query;
|
|
3691
3691
|
}
|
|
3692
3692
|
target.userinfo = base.userinfo;
|
|
3693
3693
|
target.host = base.host;
|
|
@@ -3695,7 +3695,7 @@ var require_fast_uri = __commonJS({
|
|
|
3695
3695
|
}
|
|
3696
3696
|
target.scheme = base.scheme;
|
|
3697
3697
|
}
|
|
3698
|
-
target.fragment =
|
|
3698
|
+
target.fragment = relative2.fragment;
|
|
3699
3699
|
return target;
|
|
3700
3700
|
}
|
|
3701
3701
|
function equal(uriA, uriB, options) {
|
|
@@ -3897,7 +3897,7 @@ var require_fast_uri = __commonJS({
|
|
|
3897
3897
|
var fastUri = {
|
|
3898
3898
|
SCHEMES,
|
|
3899
3899
|
normalize,
|
|
3900
|
-
resolve:
|
|
3900
|
+
resolve: resolve5,
|
|
3901
3901
|
resolveComponent,
|
|
3902
3902
|
equal,
|
|
3903
3903
|
serialize,
|
|
@@ -6873,12 +6873,12 @@ var require_dist = __commonJS({
|
|
|
6873
6873
|
throw new Error(`Unknown format "${name}"`);
|
|
6874
6874
|
return f;
|
|
6875
6875
|
};
|
|
6876
|
-
function addFormats(ajv, list,
|
|
6876
|
+
function addFormats(ajv, list, fs6, exportName) {
|
|
6877
6877
|
var _a3;
|
|
6878
6878
|
var _b;
|
|
6879
6879
|
(_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
6880
6880
|
for (const f of list)
|
|
6881
|
-
ajv.addFormat(f,
|
|
6881
|
+
ajv.addFormat(f, fs6[f]);
|
|
6882
6882
|
}
|
|
6883
6883
|
module2.exports = exports2 = formatsPlugin;
|
|
6884
6884
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
@@ -7245,8 +7245,8 @@ function getErrorMap() {
|
|
|
7245
7245
|
|
|
7246
7246
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
7247
7247
|
var makeIssue = (params) => {
|
|
7248
|
-
const { data, path:
|
|
7249
|
-
const fullPath = [...
|
|
7248
|
+
const { data, path: path8, errorMaps, issueData } = params;
|
|
7249
|
+
const fullPath = [...path8, ...issueData.path || []];
|
|
7250
7250
|
const fullIssue = {
|
|
7251
7251
|
...issueData,
|
|
7252
7252
|
path: fullPath
|
|
@@ -7361,11 +7361,11 @@ var errorUtil;
|
|
|
7361
7361
|
|
|
7362
7362
|
// node_modules/zod/v3/types.js
|
|
7363
7363
|
var ParseInputLazyPath = class {
|
|
7364
|
-
constructor(parent, value,
|
|
7364
|
+
constructor(parent, value, path8, key) {
|
|
7365
7365
|
this._cachedPath = [];
|
|
7366
7366
|
this.parent = parent;
|
|
7367
7367
|
this.data = value;
|
|
7368
|
-
this._path =
|
|
7368
|
+
this._path = path8;
|
|
7369
7369
|
this._key = key;
|
|
7370
7370
|
}
|
|
7371
7371
|
get path() {
|
|
@@ -9112,8 +9112,8 @@ function deepPartialify(schema2) {
|
|
|
9112
9112
|
if (schema2 instanceof ZodObject) {
|
|
9113
9113
|
const newShape = {};
|
|
9114
9114
|
for (const key in schema2.shape) {
|
|
9115
|
-
const
|
|
9116
|
-
newShape[key] = ZodOptional.create(deepPartialify(
|
|
9115
|
+
const fieldSchema2 = schema2.shape[key];
|
|
9116
|
+
newShape[key] = ZodOptional.create(deepPartialify(fieldSchema2));
|
|
9117
9117
|
}
|
|
9118
9118
|
return new ZodObject({
|
|
9119
9119
|
...schema2._def,
|
|
@@ -9410,11 +9410,11 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
9410
9410
|
partial(mask) {
|
|
9411
9411
|
const newShape = {};
|
|
9412
9412
|
for (const key of util.objectKeys(this.shape)) {
|
|
9413
|
-
const
|
|
9413
|
+
const fieldSchema2 = this.shape[key];
|
|
9414
9414
|
if (mask && !mask[key]) {
|
|
9415
|
-
newShape[key] =
|
|
9415
|
+
newShape[key] = fieldSchema2;
|
|
9416
9416
|
} else {
|
|
9417
|
-
newShape[key] =
|
|
9417
|
+
newShape[key] = fieldSchema2.optional();
|
|
9418
9418
|
}
|
|
9419
9419
|
}
|
|
9420
9420
|
return new _ZodObject({
|
|
@@ -9428,8 +9428,8 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
9428
9428
|
if (mask && !mask[key]) {
|
|
9429
9429
|
newShape[key] = this.shape[key];
|
|
9430
9430
|
} else {
|
|
9431
|
-
const
|
|
9432
|
-
let newField =
|
|
9431
|
+
const fieldSchema2 = this.shape[key];
|
|
9432
|
+
let newField = fieldSchema2;
|
|
9433
9433
|
while (newField instanceof ZodOptional) {
|
|
9434
9434
|
newField = newField._def.innerType;
|
|
9435
9435
|
}
|
|
@@ -11285,10 +11285,10 @@ function mergeDefs(...defs) {
|
|
|
11285
11285
|
function cloneDef(schema2) {
|
|
11286
11286
|
return mergeDefs(schema2._zod.def);
|
|
11287
11287
|
}
|
|
11288
|
-
function getElementAtPath(obj,
|
|
11289
|
-
if (!
|
|
11288
|
+
function getElementAtPath(obj, path8) {
|
|
11289
|
+
if (!path8)
|
|
11290
11290
|
return obj;
|
|
11291
|
-
return
|
|
11291
|
+
return path8.reduce((acc, key) => acc?.[key], obj);
|
|
11292
11292
|
}
|
|
11293
11293
|
function promiseAllObject(promisesObj) {
|
|
11294
11294
|
const keys = Object.keys(promisesObj);
|
|
@@ -11697,11 +11697,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
11697
11697
|
}
|
|
11698
11698
|
return false;
|
|
11699
11699
|
}
|
|
11700
|
-
function prefixIssues(
|
|
11700
|
+
function prefixIssues(path8, issues) {
|
|
11701
11701
|
return issues.map((iss) => {
|
|
11702
11702
|
var _a3;
|
|
11703
11703
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
11704
|
-
iss.path.unshift(
|
|
11704
|
+
iss.path.unshift(path8);
|
|
11705
11705
|
return iss;
|
|
11706
11706
|
});
|
|
11707
11707
|
}
|
|
@@ -11848,16 +11848,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
11848
11848
|
}
|
|
11849
11849
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
11850
11850
|
const fieldErrors = { _errors: [] };
|
|
11851
|
-
const processError = (error52,
|
|
11851
|
+
const processError = (error52, path8 = []) => {
|
|
11852
11852
|
for (const issue2 of error52.issues) {
|
|
11853
11853
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
11854
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
11854
|
+
issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
|
|
11855
11855
|
} else if (issue2.code === "invalid_key") {
|
|
11856
|
-
processError({ issues: issue2.issues }, [...
|
|
11856
|
+
processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
|
|
11857
11857
|
} else if (issue2.code === "invalid_element") {
|
|
11858
|
-
processError({ issues: issue2.issues }, [...
|
|
11858
|
+
processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
|
|
11859
11859
|
} else {
|
|
11860
|
-
const fullpath = [...
|
|
11860
|
+
const fullpath = [...path8, ...issue2.path];
|
|
11861
11861
|
if (fullpath.length === 0) {
|
|
11862
11862
|
fieldErrors._errors.push(mapper(issue2));
|
|
11863
11863
|
} else {
|
|
@@ -11884,17 +11884,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
11884
11884
|
}
|
|
11885
11885
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
11886
11886
|
const result = { errors: [] };
|
|
11887
|
-
const processError = (error52,
|
|
11887
|
+
const processError = (error52, path8 = []) => {
|
|
11888
11888
|
var _a3, _b;
|
|
11889
11889
|
for (const issue2 of error52.issues) {
|
|
11890
11890
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
11891
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
11891
|
+
issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
|
|
11892
11892
|
} else if (issue2.code === "invalid_key") {
|
|
11893
|
-
processError({ issues: issue2.issues }, [...
|
|
11893
|
+
processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
|
|
11894
11894
|
} else if (issue2.code === "invalid_element") {
|
|
11895
|
-
processError({ issues: issue2.issues }, [...
|
|
11895
|
+
processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
|
|
11896
11896
|
} else {
|
|
11897
|
-
const fullpath = [...
|
|
11897
|
+
const fullpath = [...path8, ...issue2.path];
|
|
11898
11898
|
if (fullpath.length === 0) {
|
|
11899
11899
|
result.errors.push(mapper(issue2));
|
|
11900
11900
|
continue;
|
|
@@ -11926,8 +11926,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
11926
11926
|
}
|
|
11927
11927
|
function toDotPath(_path) {
|
|
11928
11928
|
const segs = [];
|
|
11929
|
-
const
|
|
11930
|
-
for (const seg of
|
|
11929
|
+
const path8 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
11930
|
+
for (const seg of path8) {
|
|
11931
11931
|
if (typeof seg === "number")
|
|
11932
11932
|
segs.push(`[${seg}]`);
|
|
11933
11933
|
else if (typeof seg === "symbol")
|
|
@@ -25052,13 +25052,13 @@ function resolveRef(ref, ctx) {
|
|
|
25052
25052
|
if (!ref.startsWith("#")) {
|
|
25053
25053
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
25054
25054
|
}
|
|
25055
|
-
const
|
|
25056
|
-
if (
|
|
25055
|
+
const path8 = ref.slice(1).split("/").filter(Boolean);
|
|
25056
|
+
if (path8.length === 0) {
|
|
25057
25057
|
return ctx.rootSchema;
|
|
25058
25058
|
}
|
|
25059
25059
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
25060
|
-
if (
|
|
25061
|
-
const key =
|
|
25060
|
+
if (path8[0] === defsKey) {
|
|
25061
|
+
const key = path8[1];
|
|
25062
25062
|
if (!key || !ctx.defs[key]) {
|
|
25063
25063
|
throw new Error(`Reference not found: ${ref}`);
|
|
25064
25064
|
}
|
|
@@ -28828,7 +28828,7 @@ var Protocol = class {
|
|
|
28828
28828
|
return;
|
|
28829
28829
|
}
|
|
28830
28830
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
28831
|
-
await new Promise((
|
|
28831
|
+
await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
|
|
28832
28832
|
options?.signal?.throwIfAborted();
|
|
28833
28833
|
}
|
|
28834
28834
|
} catch (error51) {
|
|
@@ -28845,7 +28845,7 @@ var Protocol = class {
|
|
|
28845
28845
|
*/
|
|
28846
28846
|
request(request, resultSchema, options) {
|
|
28847
28847
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
28848
|
-
return new Promise((
|
|
28848
|
+
return new Promise((resolve5, reject) => {
|
|
28849
28849
|
const earlyReject = (error51) => {
|
|
28850
28850
|
reject(error51);
|
|
28851
28851
|
};
|
|
@@ -28923,7 +28923,7 @@ var Protocol = class {
|
|
|
28923
28923
|
if (!parseResult.success) {
|
|
28924
28924
|
reject(parseResult.error);
|
|
28925
28925
|
} else {
|
|
28926
|
-
|
|
28926
|
+
resolve5(parseResult.data);
|
|
28927
28927
|
}
|
|
28928
28928
|
} catch (error51) {
|
|
28929
28929
|
reject(error51);
|
|
@@ -29184,12 +29184,12 @@ var Protocol = class {
|
|
|
29184
29184
|
}
|
|
29185
29185
|
} catch {
|
|
29186
29186
|
}
|
|
29187
|
-
return new Promise((
|
|
29187
|
+
return new Promise((resolve5, reject) => {
|
|
29188
29188
|
if (signal.aborted) {
|
|
29189
29189
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
29190
29190
|
return;
|
|
29191
29191
|
}
|
|
29192
|
-
const timeoutId = setTimeout(
|
|
29192
|
+
const timeoutId = setTimeout(resolve5, interval);
|
|
29193
29193
|
signal.addEventListener("abort", () => {
|
|
29194
29194
|
clearTimeout(timeoutId);
|
|
29195
29195
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -30289,7 +30289,7 @@ var McpServer = class {
|
|
|
30289
30289
|
let task = createTaskResult.task;
|
|
30290
30290
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
30291
30291
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
30292
|
-
await new Promise((
|
|
30292
|
+
await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
|
|
30293
30293
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
30294
30294
|
if (!updatedTask) {
|
|
30295
30295
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -30938,12 +30938,12 @@ var StdioServerTransport = class {
|
|
|
30938
30938
|
this.onclose?.();
|
|
30939
30939
|
}
|
|
30940
30940
|
send(message) {
|
|
30941
|
-
return new Promise((
|
|
30941
|
+
return new Promise((resolve5) => {
|
|
30942
30942
|
const json2 = serializeMessage(message);
|
|
30943
30943
|
if (this._stdout.write(json2)) {
|
|
30944
|
-
|
|
30944
|
+
resolve5();
|
|
30945
30945
|
} else {
|
|
30946
|
-
this._stdout.once("drain",
|
|
30946
|
+
this._stdout.once("drain", resolve5);
|
|
30947
30947
|
}
|
|
30948
30948
|
});
|
|
30949
30949
|
}
|
|
@@ -31006,18 +31006,16 @@ function buildDataViews(entities) {
|
|
|
31006
31006
|
}
|
|
31007
31007
|
function buildFolders(opts) {
|
|
31008
31008
|
return {
|
|
31009
|
-
|
|
31010
|
-
|
|
31011
|
-
|
|
31012
|
-
|
|
31013
|
-
|
|
31014
|
-
entities
|
|
31015
|
-
|
|
31016
|
-
|
|
31017
|
-
|
|
31018
|
-
|
|
31019
|
-
)
|
|
31020
|
-
}
|
|
31009
|
+
label: opts.displayName,
|
|
31010
|
+
description: opts.description || opts.displayName,
|
|
31011
|
+
icon: "bi-folder",
|
|
31012
|
+
color: "#2196F3",
|
|
31013
|
+
entities: Object.fromEntries(
|
|
31014
|
+
opts.entities.map((e) => [
|
|
31015
|
+
e.name,
|
|
31016
|
+
{ viewName: "default", quickAdd: true }
|
|
31017
|
+
])
|
|
31018
|
+
)
|
|
31021
31019
|
};
|
|
31022
31020
|
}
|
|
31023
31021
|
function buildMenus(opts) {
|
|
@@ -31151,11 +31149,11 @@ function createModuleFiles(args) {
|
|
|
31151
31149
|
entities: args.entities
|
|
31152
31150
|
};
|
|
31153
31151
|
const files = {};
|
|
31154
|
-
const write = (
|
|
31155
|
-
files[
|
|
31152
|
+
const write = (rel2, obj) => {
|
|
31153
|
+
files[rel2] = JSON.stringify(obj, null, " ") + "\n";
|
|
31156
31154
|
};
|
|
31157
|
-
const writeText = (
|
|
31158
|
-
files[
|
|
31155
|
+
const writeText = (rel2, text) => {
|
|
31156
|
+
files[rel2] = text;
|
|
31159
31157
|
};
|
|
31160
31158
|
write("manifest.json", buildManifest(opts, moduleId));
|
|
31161
31159
|
for (const e of opts.entities) {
|
|
@@ -31361,10 +31359,583 @@ function dbmlImport(_args) {
|
|
|
31361
31359
|
};
|
|
31362
31360
|
}
|
|
31363
31361
|
|
|
31364
|
-
// src/
|
|
31362
|
+
// src/tools/entity-field.ts
|
|
31363
|
+
var path4 = __toESM(require("path"));
|
|
31364
|
+
|
|
31365
|
+
// src/tools/_helpers.ts
|
|
31365
31366
|
var fs3 = __toESM(require("fs"));
|
|
31366
31367
|
var path3 = __toESM(require("path"));
|
|
31367
|
-
|
|
31368
|
+
function modulePaths(moduleDir) {
|
|
31369
|
+
const root = path3.resolve(moduleDir);
|
|
31370
|
+
return {
|
|
31371
|
+
root,
|
|
31372
|
+
manifest: path3.join(root, "manifest.json"),
|
|
31373
|
+
entitiesDir: path3.join(root, "entities"),
|
|
31374
|
+
uiDir: path3.join(root, "ui"),
|
|
31375
|
+
securityDir: path3.join(root, "security"),
|
|
31376
|
+
logicDir: path3.join(root, "logic"),
|
|
31377
|
+
seedDataDir: path3.join(root, "seed-data"),
|
|
31378
|
+
translationsDir: path3.join(root, "translations"),
|
|
31379
|
+
dataViews: path3.join(root, "ui", "data_views.json"),
|
|
31380
|
+
folders: path3.join(root, "ui", "folders.json"),
|
|
31381
|
+
menus: path3.join(root, "ui", "menus.json"),
|
|
31382
|
+
actions: path3.join(root, "ui", "actions.json"),
|
|
31383
|
+
reports: path3.join(root, "ui", "reports.json"),
|
|
31384
|
+
roles: path3.join(root, "security", "roles.json"),
|
|
31385
|
+
jobs: path3.join(root, "logic", "jobs.json"),
|
|
31386
|
+
settings: path3.join(root, "settings.json")
|
|
31387
|
+
};
|
|
31388
|
+
}
|
|
31389
|
+
function readJson(absPath) {
|
|
31390
|
+
if (!fs3.existsSync(absPath)) {
|
|
31391
|
+
throw new Error(`Not found: ${absPath}`);
|
|
31392
|
+
}
|
|
31393
|
+
try {
|
|
31394
|
+
return JSON.parse(fs3.readFileSync(absPath, "utf8"));
|
|
31395
|
+
} catch (e) {
|
|
31396
|
+
throw new Error(`${absPath}: ${e.message}`);
|
|
31397
|
+
}
|
|
31398
|
+
}
|
|
31399
|
+
function readJsonOrDefault(absPath, dflt) {
|
|
31400
|
+
if (!fs3.existsSync(absPath)) return dflt;
|
|
31401
|
+
try {
|
|
31402
|
+
return JSON.parse(fs3.readFileSync(absPath, "utf8"));
|
|
31403
|
+
} catch (e) {
|
|
31404
|
+
throw new Error(`${absPath}: ${e.message}`);
|
|
31405
|
+
}
|
|
31406
|
+
}
|
|
31407
|
+
function jsonText(obj) {
|
|
31408
|
+
return JSON.stringify(obj, null, " ") + "\n";
|
|
31409
|
+
}
|
|
31410
|
+
function rel(root, abs) {
|
|
31411
|
+
return path3.relative(root, abs);
|
|
31412
|
+
}
|
|
31413
|
+
function loadManifest(moduleDir) {
|
|
31414
|
+
const paths = modulePaths(moduleDir);
|
|
31415
|
+
if (!fs3.existsSync(paths.manifest)) {
|
|
31416
|
+
throw new Error(
|
|
31417
|
+
`No manifest.json at ${paths.manifest} \u2014 is this a dForge module directory?`
|
|
31418
|
+
);
|
|
31419
|
+
}
|
|
31420
|
+
const manifest = readJson(paths.manifest);
|
|
31421
|
+
if (!manifest.code) {
|
|
31422
|
+
throw new Error("manifest.json has no `code` field \u2014 corrupt module?");
|
|
31423
|
+
}
|
|
31424
|
+
return { manifest, paths };
|
|
31425
|
+
}
|
|
31426
|
+
function makeResult(summary, files, warning) {
|
|
31427
|
+
const out = { summary, files };
|
|
31428
|
+
if (warning) out.warning = warning;
|
|
31429
|
+
return out;
|
|
31430
|
+
}
|
|
31431
|
+
function withTodayStamp(manifest) {
|
|
31432
|
+
return { ...manifest, updated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10) };
|
|
31433
|
+
}
|
|
31434
|
+
|
|
31435
|
+
// src/tools/entity-field.ts
|
|
31436
|
+
var fieldSchema = external_exports.object({
|
|
31437
|
+
dbDatatype: external_exports.string().optional(),
|
|
31438
|
+
fieldTypeCd: external_exports.string().optional(),
|
|
31439
|
+
baseDatatypeCd: external_exports.string().optional(),
|
|
31440
|
+
columnType: external_exports.string().optional(),
|
|
31441
|
+
flags: external_exports.string().optional(),
|
|
31442
|
+
isNullable: external_exports.boolean().optional(),
|
|
31443
|
+
maxLen: external_exports.number().int().optional(),
|
|
31444
|
+
orderNum: external_exports.number().int().optional(),
|
|
31445
|
+
description: external_exports.string().optional(),
|
|
31446
|
+
defaultValue: external_exports.unknown().optional(),
|
|
31447
|
+
formula: external_exports.string().optional(),
|
|
31448
|
+
link: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
31449
|
+
params: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
31450
|
+
}).passthrough().describe(
|
|
31451
|
+
"Field spec. Common keys: dbDatatype, fieldTypeCd, flags (VEMHI letters), isNullable, maxLen, orderNum, description, link ({entity, otherKey}) for refs, formula for computed columns. Pass through whatever the entity.schema.json allows."
|
|
31452
|
+
);
|
|
31453
|
+
var entityFieldAddSchema = {
|
|
31454
|
+
moduleDir: external_exports.string().describe("Path to the module root."),
|
|
31455
|
+
entityName: external_exports.string().regex(/^[a-z][a-z0-9_]*$/).describe("Entity code to add the field to."),
|
|
31456
|
+
fieldName: external_exports.string().regex(/^[a-z][a-z0-9_]*$/).describe("New field's code."),
|
|
31457
|
+
field: fieldSchema
|
|
31458
|
+
};
|
|
31459
|
+
function entityFieldAdd(args) {
|
|
31460
|
+
const { paths, manifest } = loadManifest(args.moduleDir);
|
|
31461
|
+
const entityPath = path4.join(paths.entitiesDir, `${args.entityName}.json`);
|
|
31462
|
+
const entity = readJson(entityPath);
|
|
31463
|
+
const fields = entity.fields ?? {};
|
|
31464
|
+
if (Object.prototype.hasOwnProperty.call(fields, args.fieldName)) {
|
|
31465
|
+
throw new Error(
|
|
31466
|
+
`Field '${args.fieldName}' already exists on entity '${args.entityName}'. Use entity_field_modify to change it.`
|
|
31467
|
+
);
|
|
31468
|
+
}
|
|
31469
|
+
entity.fields = { ...fields, [args.fieldName]: args.field };
|
|
31470
|
+
const files = {
|
|
31471
|
+
[rel(paths.root, entityPath)]: jsonText(entity),
|
|
31472
|
+
"manifest.json": jsonText(withTodayStamp(manifest))
|
|
31473
|
+
};
|
|
31474
|
+
return makeResult(
|
|
31475
|
+
`Added field '${args.fieldName}' to entity '${args.entityName}'.`,
|
|
31476
|
+
files
|
|
31477
|
+
);
|
|
31478
|
+
}
|
|
31479
|
+
var entityFieldModifySchema = {
|
|
31480
|
+
moduleDir: external_exports.string(),
|
|
31481
|
+
entityName: external_exports.string().regex(/^[a-z][a-z0-9_]*$/),
|
|
31482
|
+
fieldName: external_exports.string().regex(/^[a-z][a-z0-9_]*$/),
|
|
31483
|
+
field: fieldSchema.describe(
|
|
31484
|
+
"Replacement spec. Replaces the existing field entirely \u2014 pass the full desired shape, not a partial patch."
|
|
31485
|
+
)
|
|
31486
|
+
};
|
|
31487
|
+
function entityFieldModify(args) {
|
|
31488
|
+
const { paths, manifest } = loadManifest(args.moduleDir);
|
|
31489
|
+
const entityPath = path4.join(paths.entitiesDir, `${args.entityName}.json`);
|
|
31490
|
+
const entity = readJson(entityPath);
|
|
31491
|
+
const fields = entity.fields ?? {};
|
|
31492
|
+
if (!Object.prototype.hasOwnProperty.call(fields, args.fieldName)) {
|
|
31493
|
+
throw new Error(
|
|
31494
|
+
`Field '${args.fieldName}' not found on entity '${args.entityName}'.`
|
|
31495
|
+
);
|
|
31496
|
+
}
|
|
31497
|
+
entity.fields = { ...fields, [args.fieldName]: args.field };
|
|
31498
|
+
return makeResult(
|
|
31499
|
+
`Modified field '${args.fieldName}' on entity '${args.entityName}'.`,
|
|
31500
|
+
{
|
|
31501
|
+
[rel(paths.root, entityPath)]: jsonText(entity),
|
|
31502
|
+
"manifest.json": jsonText(withTodayStamp(manifest))
|
|
31503
|
+
}
|
|
31504
|
+
);
|
|
31505
|
+
}
|
|
31506
|
+
var entityFieldRemoveSchema = {
|
|
31507
|
+
moduleDir: external_exports.string(),
|
|
31508
|
+
entityName: external_exports.string().regex(/^[a-z][a-z0-9_]*$/),
|
|
31509
|
+
fieldName: external_exports.string().regex(/^[a-z][a-z0-9_]*$/)
|
|
31510
|
+
};
|
|
31511
|
+
function entityFieldRemove(args) {
|
|
31512
|
+
const { paths, manifest } = loadManifest(args.moduleDir);
|
|
31513
|
+
const entityPath = path4.join(paths.entitiesDir, `${args.entityName}.json`);
|
|
31514
|
+
const entity = readJson(entityPath);
|
|
31515
|
+
const fields = entity.fields ?? {};
|
|
31516
|
+
if (!Object.prototype.hasOwnProperty.call(fields, args.fieldName)) {
|
|
31517
|
+
throw new Error(
|
|
31518
|
+
`Field '${args.fieldName}' not found on entity '${args.entityName}'.`
|
|
31519
|
+
);
|
|
31520
|
+
}
|
|
31521
|
+
const { [args.fieldName]: _removed, ...rest } = fields;
|
|
31522
|
+
void _removed;
|
|
31523
|
+
entity.fields = rest;
|
|
31524
|
+
return makeResult(
|
|
31525
|
+
`Removed field '${args.fieldName}' from entity '${args.entityName}'. Note: dependent views / formulas referencing this field will break \u2014 review them.`,
|
|
31526
|
+
{
|
|
31527
|
+
[rel(paths.root, entityPath)]: jsonText(entity),
|
|
31528
|
+
"manifest.json": jsonText(withTodayStamp(manifest))
|
|
31529
|
+
},
|
|
31530
|
+
"Removing fields can break dependent views, role rights, formulas, action DSL, and seed data. Run `dforge_module_inspect` after writing to spot broken references."
|
|
31531
|
+
);
|
|
31532
|
+
}
|
|
31533
|
+
|
|
31534
|
+
// src/tools/action-add.ts
|
|
31535
|
+
var path5 = __toESM(require("path"));
|
|
31536
|
+
var actionAddSchema = {
|
|
31537
|
+
moduleDir: external_exports.string(),
|
|
31538
|
+
code: external_exports.string().regex(/^[a-z][a-z0-9_]*$/).describe("Action code (becomes the DSL filename and registry key)."),
|
|
31539
|
+
entityCode: external_exports.string().regex(/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)?$/).describe(
|
|
31540
|
+
"Entity this action targets. May be cross-module via dot notation ('fin.invoice')."
|
|
31541
|
+
),
|
|
31542
|
+
label: external_exports.string().min(1).describe("Display label in the action menu."),
|
|
31543
|
+
mode: external_exports.enum(["single", "each", "batch"]).default("single").describe(
|
|
31544
|
+
"Execution mode. 'single' = whole selection at once, 'each' = per-record, 'batch' = explicit `for x in records` loop in DSL."
|
|
31545
|
+
),
|
|
31546
|
+
icon: external_exports.string().optional().describe("Bootstrap icon class (e.g. 'play-fill')."),
|
|
31547
|
+
background: external_exports.boolean().default(false).describe("Run asynchronously (queues to background_action table)."),
|
|
31548
|
+
dslBody: external_exports.string().describe(
|
|
31549
|
+
"Full DSL source. Should contain `params:`, optional `canExecute:`, optional `onBeforeStart:` (async only), and required `execute:` blocks."
|
|
31550
|
+
)
|
|
31551
|
+
};
|
|
31552
|
+
function actionAdd(args) {
|
|
31553
|
+
const { paths, manifest } = loadManifest(args.moduleDir);
|
|
31554
|
+
const actionsJson = readJsonOrDefault(paths.actions, {});
|
|
31555
|
+
if (Object.prototype.hasOwnProperty.call(actionsJson, args.code)) {
|
|
31556
|
+
throw new Error(
|
|
31557
|
+
`Action '${args.code}' already exists in ui/actions.json. Use a different code or remove the existing entry first.`
|
|
31558
|
+
);
|
|
31559
|
+
}
|
|
31560
|
+
const actionEntry = {
|
|
31561
|
+
entity: args.entityCode,
|
|
31562
|
+
label: args.label,
|
|
31563
|
+
mode: args.mode,
|
|
31564
|
+
background: args.background,
|
|
31565
|
+
dsl: `./logic/actions/${args.code}.dsl`
|
|
31566
|
+
};
|
|
31567
|
+
if (args.icon) actionEntry.icon = args.icon;
|
|
31568
|
+
actionsJson[args.code] = actionEntry;
|
|
31569
|
+
const dslPath = path5.join(paths.logicDir, "actions", `${args.code}.dsl`);
|
|
31570
|
+
const dslBody = args.dslBody.endsWith("\n") ? args.dslBody : args.dslBody + "\n";
|
|
31571
|
+
return makeResult(
|
|
31572
|
+
`Added action '${args.code}' targeting entity '${args.entityCode}' (mode=${args.mode}${args.background ? ", background" : ""}).`,
|
|
31573
|
+
{
|
|
31574
|
+
[rel(paths.root, paths.actions)]: jsonText(actionsJson),
|
|
31575
|
+
[rel(paths.root, dslPath)]: dslBody,
|
|
31576
|
+
"manifest.json": jsonText(withTodayStamp(manifest))
|
|
31577
|
+
}
|
|
31578
|
+
);
|
|
31579
|
+
}
|
|
31580
|
+
|
|
31581
|
+
// src/tools/view.ts
|
|
31582
|
+
var dataSourceSchema = external_exports.object({
|
|
31583
|
+
entityCode: external_exports.string(),
|
|
31584
|
+
level: external_exports.number().int().min(0).optional(),
|
|
31585
|
+
label: external_exports.string().optional(),
|
|
31586
|
+
columns: external_exports.array(external_exports.unknown()).optional(),
|
|
31587
|
+
filter: external_exports.unknown().optional(),
|
|
31588
|
+
order: external_exports.unknown().optional(),
|
|
31589
|
+
parentSetField: external_exports.string().optional()
|
|
31590
|
+
}).passthrough();
|
|
31591
|
+
var dataViewSchema = external_exports.object({
|
|
31592
|
+
viewType: external_exports.enum([
|
|
31593
|
+
"grid",
|
|
31594
|
+
"list",
|
|
31595
|
+
"kanban",
|
|
31596
|
+
"calendar",
|
|
31597
|
+
"gallery",
|
|
31598
|
+
"tree-grid",
|
|
31599
|
+
"diagram",
|
|
31600
|
+
"master-detail",
|
|
31601
|
+
"library"
|
|
31602
|
+
]),
|
|
31603
|
+
label: external_exports.string().optional(),
|
|
31604
|
+
description: external_exports.string().optional(),
|
|
31605
|
+
icon: external_exports.string().optional(),
|
|
31606
|
+
dataSources: external_exports.array(dataSourceSchema).min(1),
|
|
31607
|
+
filter: external_exports.unknown().optional(),
|
|
31608
|
+
order: external_exports.unknown().optional(),
|
|
31609
|
+
viewConfig: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
31610
|
+
}).passthrough();
|
|
31611
|
+
var viewAddSchema = {
|
|
31612
|
+
moduleDir: external_exports.string(),
|
|
31613
|
+
code: external_exports.string().regex(/^[a-z][a-z0-9_]*$/).describe("View code (keys ui/data_views.json + URL slug)."),
|
|
31614
|
+
view: dataViewSchema
|
|
31615
|
+
};
|
|
31616
|
+
function viewAdd(args) {
|
|
31617
|
+
const { paths, manifest } = loadManifest(args.moduleDir);
|
|
31618
|
+
const views = readJsonOrDefault(paths.dataViews, {});
|
|
31619
|
+
if (Object.prototype.hasOwnProperty.call(views, args.code)) {
|
|
31620
|
+
throw new Error(
|
|
31621
|
+
`View '${args.code}' already exists in ui/data_views.json. Use view_modify to change it.`
|
|
31622
|
+
);
|
|
31623
|
+
}
|
|
31624
|
+
views[args.code] = args.view;
|
|
31625
|
+
return makeResult(
|
|
31626
|
+
`Added ${args.view.viewType} view '${args.code}' over [${args.view.dataSources.map((d) => d.entityCode).join(", ")}].`,
|
|
31627
|
+
{
|
|
31628
|
+
[rel(paths.root, paths.dataViews)]: jsonText(views),
|
|
31629
|
+
"manifest.json": jsonText(withTodayStamp(manifest))
|
|
31630
|
+
}
|
|
31631
|
+
);
|
|
31632
|
+
}
|
|
31633
|
+
var viewModifySchema = {
|
|
31634
|
+
moduleDir: external_exports.string(),
|
|
31635
|
+
code: external_exports.string().regex(/^[a-z][a-z0-9_]*$/),
|
|
31636
|
+
view: dataViewSchema.describe(
|
|
31637
|
+
"Replacement view spec \u2014 full shape, not a partial patch."
|
|
31638
|
+
)
|
|
31639
|
+
};
|
|
31640
|
+
function viewModify(args) {
|
|
31641
|
+
const { paths, manifest } = loadManifest(args.moduleDir);
|
|
31642
|
+
const views = readJsonOrDefault(paths.dataViews, {});
|
|
31643
|
+
if (!Object.prototype.hasOwnProperty.call(views, args.code)) {
|
|
31644
|
+
throw new Error(
|
|
31645
|
+
`View '${args.code}' not found. Use view_add to create it.`
|
|
31646
|
+
);
|
|
31647
|
+
}
|
|
31648
|
+
views[args.code] = args.view;
|
|
31649
|
+
return makeResult(`Modified view '${args.code}'.`, {
|
|
31650
|
+
[rel(paths.root, paths.dataViews)]: jsonText(views),
|
|
31651
|
+
"manifest.json": jsonText(withTodayStamp(manifest))
|
|
31652
|
+
});
|
|
31653
|
+
}
|
|
31654
|
+
|
|
31655
|
+
// src/tools/adds.ts
|
|
31656
|
+
var reportAddSchema = {
|
|
31657
|
+
moduleDir: external_exports.string(),
|
|
31658
|
+
code: external_exports.string().regex(/^[a-z][a-z0-9_]*$/).describe("Report code (keys ui/reports.json)."),
|
|
31659
|
+
report: external_exports.object({
|
|
31660
|
+
description: external_exports.string(),
|
|
31661
|
+
layout: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
31662
|
+
datasets: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
31663
|
+
parameters: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
31664
|
+
}).passthrough().describe(
|
|
31665
|
+
"Full report spec per reports.schema.json: { description, layout: { panels: [...] }, datasets: { code: {...} }, parameters? }."
|
|
31666
|
+
)
|
|
31667
|
+
};
|
|
31668
|
+
function reportAdd(args) {
|
|
31669
|
+
const { paths, manifest } = loadManifest(args.moduleDir);
|
|
31670
|
+
const reports = readJsonOrDefault(paths.reports, {});
|
|
31671
|
+
if (Object.prototype.hasOwnProperty.call(reports, args.code)) {
|
|
31672
|
+
throw new Error(`Report '${args.code}' already exists in ui/reports.json.`);
|
|
31673
|
+
}
|
|
31674
|
+
reports[args.code] = args.report;
|
|
31675
|
+
return makeResult(`Added report '${args.code}'.`, {
|
|
31676
|
+
[rel(paths.root, paths.reports)]: jsonText(reports),
|
|
31677
|
+
"manifest.json": jsonText(withTodayStamp(manifest))
|
|
31678
|
+
});
|
|
31679
|
+
}
|
|
31680
|
+
var settingAddSchema = {
|
|
31681
|
+
moduleDir: external_exports.string(),
|
|
31682
|
+
code: external_exports.string().regex(/^[a-z][a-z0-9_]*$/).describe("Setting code (keys settings.json)."),
|
|
31683
|
+
setting: external_exports.object({
|
|
31684
|
+
fieldTypeCd: external_exports.string(),
|
|
31685
|
+
baseDatatypeCd: external_exports.string().optional(),
|
|
31686
|
+
label: external_exports.string().optional(),
|
|
31687
|
+
description: external_exports.string().optional(),
|
|
31688
|
+
defaultValue: external_exports.unknown().optional(),
|
|
31689
|
+
formula: external_exports.string().optional(),
|
|
31690
|
+
required: external_exports.boolean().optional(),
|
|
31691
|
+
params: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
31692
|
+
}).passthrough()
|
|
31693
|
+
};
|
|
31694
|
+
function settingAdd(args) {
|
|
31695
|
+
const { paths, manifest } = loadManifest(args.moduleDir);
|
|
31696
|
+
const settings = readJsonOrDefault(paths.settings, {});
|
|
31697
|
+
if (Object.prototype.hasOwnProperty.call(settings, args.code)) {
|
|
31698
|
+
throw new Error(`Setting '${args.code}' already exists.`);
|
|
31699
|
+
}
|
|
31700
|
+
settings[args.code] = args.setting;
|
|
31701
|
+
return makeResult(`Added setting '${args.code}'.`, {
|
|
31702
|
+
[rel(paths.root, paths.settings)]: jsonText(settings),
|
|
31703
|
+
"manifest.json": jsonText(withTodayStamp(manifest))
|
|
31704
|
+
});
|
|
31705
|
+
}
|
|
31706
|
+
var roleAddSchema = {
|
|
31707
|
+
moduleDir: external_exports.string(),
|
|
31708
|
+
code: external_exports.string().regex(/^[a-z][a-z0-9_-]*\.[a-z][a-z0-9_-]*$/).describe(
|
|
31709
|
+
"Role code, namespaced as `<module>.<role>` (e.g. 'crm.admin'). Conventional namespacing prevents collisions across modules."
|
|
31710
|
+
),
|
|
31711
|
+
description: external_exports.string(),
|
|
31712
|
+
rights: external_exports.record(external_exports.string(), external_exports.string()).describe(
|
|
31713
|
+
"Map: entityCode (or actionCode/reportCode) \u2192 rights string. For entities: any combination of 'S' (Select), 'I' (Insert), 'U' (Update), 'D' (Delete), 'C' (Clone), e.g. 'SIUDC' for full. For actions/reports: 'E' (Execute)."
|
|
31714
|
+
)
|
|
31715
|
+
};
|
|
31716
|
+
function roleAdd(args) {
|
|
31717
|
+
const { paths, manifest } = loadManifest(args.moduleDir);
|
|
31718
|
+
const roles = readJsonOrDefault(paths.roles, {});
|
|
31719
|
+
if (Object.prototype.hasOwnProperty.call(roles, args.code)) {
|
|
31720
|
+
throw new Error(`Role '${args.code}' already exists.`);
|
|
31721
|
+
}
|
|
31722
|
+
roles[args.code] = { description: args.description, rights: args.rights };
|
|
31723
|
+
return makeResult(
|
|
31724
|
+
`Added role '${args.code}' with rights on ${Object.keys(args.rights).length} object(s).`,
|
|
31725
|
+
{
|
|
31726
|
+
[rel(paths.root, paths.roles)]: jsonText(roles),
|
|
31727
|
+
"manifest.json": jsonText(withTodayStamp(manifest))
|
|
31728
|
+
}
|
|
31729
|
+
);
|
|
31730
|
+
}
|
|
31731
|
+
var folderAddSchema = {
|
|
31732
|
+
moduleDir: external_exports.string(),
|
|
31733
|
+
parentPath: external_exports.string().default("").describe(
|
|
31734
|
+
"Slash-separated folder codes from root, e.g. 'central/east' nests under root \u2192 central \u2192 east. Empty = add directly under root."
|
|
31735
|
+
),
|
|
31736
|
+
code: external_exports.string().regex(/^[a-z][a-z0-9_-]*$/).describe("New folder's code (key under parent.children)."),
|
|
31737
|
+
folder: external_exports.object({
|
|
31738
|
+
label: external_exports.string(),
|
|
31739
|
+
description: external_exports.string().optional(),
|
|
31740
|
+
color: external_exports.string().optional(),
|
|
31741
|
+
icon: external_exports.string().optional(),
|
|
31742
|
+
inheritSecurity: external_exports.boolean().optional(),
|
|
31743
|
+
entities: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
31744
|
+
children: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
31745
|
+
}).passthrough()
|
|
31746
|
+
};
|
|
31747
|
+
function folderAdd(args) {
|
|
31748
|
+
const { paths, manifest } = loadManifest(args.moduleDir);
|
|
31749
|
+
const root = readJsonOrDefault(paths.folders, {
|
|
31750
|
+
label: manifest.displayName
|
|
31751
|
+
});
|
|
31752
|
+
let cursor = root;
|
|
31753
|
+
const segments = args.parentPath.split("/").filter(Boolean);
|
|
31754
|
+
for (const seg of segments) {
|
|
31755
|
+
const children2 = cursor.children ?? {};
|
|
31756
|
+
if (!children2[seg]) {
|
|
31757
|
+
throw new Error(
|
|
31758
|
+
`Folder path segment '${seg}' not found under '${segments.slice(0, segments.indexOf(seg)).join("/") || "(root)"}'.`
|
|
31759
|
+
);
|
|
31760
|
+
}
|
|
31761
|
+
cursor = children2[seg];
|
|
31762
|
+
}
|
|
31763
|
+
const children = cursor.children ?? {};
|
|
31764
|
+
if (children[args.code]) {
|
|
31765
|
+
throw new Error(`Folder '${args.code}' already exists at '${args.parentPath || "(root)"}'.`);
|
|
31766
|
+
}
|
|
31767
|
+
children[args.code] = args.folder;
|
|
31768
|
+
cursor.children = children;
|
|
31769
|
+
return makeResult(
|
|
31770
|
+
`Added folder '${args.code}' under '${args.parentPath || "(root)"}'.`,
|
|
31771
|
+
{
|
|
31772
|
+
[rel(paths.root, paths.folders)]: jsonText(root),
|
|
31773
|
+
"manifest.json": jsonText(withTodayStamp(manifest))
|
|
31774
|
+
}
|
|
31775
|
+
);
|
|
31776
|
+
}
|
|
31777
|
+
var dependencyAddSchema = {
|
|
31778
|
+
moduleDir: external_exports.string(),
|
|
31779
|
+
moduleCode: external_exports.string().regex(/^[a-z][a-z0-9_-]*$/).describe("Module to depend on."),
|
|
31780
|
+
version: external_exports.string().default(">=0.1.0").describe("Semver range."),
|
|
31781
|
+
entities: external_exports.array(external_exports.string()).optional().describe(
|
|
31782
|
+
"If only specific entities from the dependency are used, list them \u2014 produces the object-style dep `{ version, entities }` for partial coupling. Omit to depend on the whole module."
|
|
31783
|
+
)
|
|
31784
|
+
};
|
|
31785
|
+
function dependencyAdd(args) {
|
|
31786
|
+
const { manifest } = loadManifest(args.moduleDir);
|
|
31787
|
+
if (manifest.code === args.moduleCode) {
|
|
31788
|
+
throw new Error("A module can't depend on itself.");
|
|
31789
|
+
}
|
|
31790
|
+
const deps = manifest.dependencies ?? {};
|
|
31791
|
+
if (Object.prototype.hasOwnProperty.call(deps, args.moduleCode)) {
|
|
31792
|
+
throw new Error(
|
|
31793
|
+
`Dependency on '${args.moduleCode}' already exists. Edit manifest.json directly to change the version.`
|
|
31794
|
+
);
|
|
31795
|
+
}
|
|
31796
|
+
const value = args.entities && args.entities.length > 0 ? { version: args.version, entities: args.entities } : args.version;
|
|
31797
|
+
const newManifest = withTodayStamp({
|
|
31798
|
+
...manifest,
|
|
31799
|
+
dependencies: { ...deps, [args.moduleCode]: value }
|
|
31800
|
+
});
|
|
31801
|
+
return makeResult(
|
|
31802
|
+
`Added dependency on '${args.moduleCode}' (${args.version})${args.entities ? ` for entities [${args.entities.join(", ")}]` : ""}.`,
|
|
31803
|
+
{ "manifest.json": jsonText(newManifest) }
|
|
31804
|
+
);
|
|
31805
|
+
}
|
|
31806
|
+
|
|
31807
|
+
// src/tools/role-right.ts
|
|
31808
|
+
var roleRightSetSchema = {
|
|
31809
|
+
moduleDir: external_exports.string(),
|
|
31810
|
+
roleCode: external_exports.string().describe("Role code as it appears in security/roles.json (e.g. 'crm.admin')."),
|
|
31811
|
+
object: external_exports.string().describe(
|
|
31812
|
+
"Entity / action / report code to grant rights on. For cross-module rights, use dotted form ('fin.invoice')."
|
|
31813
|
+
),
|
|
31814
|
+
rights: external_exports.string().describe(
|
|
31815
|
+
"Rights string. Entities: any combination of S/I/U/D/C (use '' to revoke all). Actions/reports: 'E' or ''."
|
|
31816
|
+
)
|
|
31817
|
+
};
|
|
31818
|
+
function roleRightSet(args) {
|
|
31819
|
+
const { paths, manifest } = loadManifest(args.moduleDir);
|
|
31820
|
+
const roles = readJson(paths.roles);
|
|
31821
|
+
const role = roles[args.roleCode];
|
|
31822
|
+
if (!role) {
|
|
31823
|
+
throw new Error(
|
|
31824
|
+
`Role '${args.roleCode}' not found in security/roles.json. Use role_add to create it.`
|
|
31825
|
+
);
|
|
31826
|
+
}
|
|
31827
|
+
const rights = role.rights ?? {};
|
|
31828
|
+
if (args.rights === "") {
|
|
31829
|
+
const { [args.object]: _gone, ...rest } = rights;
|
|
31830
|
+
void _gone;
|
|
31831
|
+
role.rights = rest;
|
|
31832
|
+
} else {
|
|
31833
|
+
role.rights = { ...rights, [args.object]: args.rights };
|
|
31834
|
+
}
|
|
31835
|
+
return makeResult(
|
|
31836
|
+
args.rights === "" ? `Revoked all rights on '${args.object}' from role '${args.roleCode}'.` : `Set rights on '${args.object}' for role '${args.roleCode}' to '${args.rights}'.`,
|
|
31837
|
+
{
|
|
31838
|
+
[rel(paths.root, paths.roles)]: jsonText(roles),
|
|
31839
|
+
"manifest.json": jsonText(withTodayStamp(manifest))
|
|
31840
|
+
}
|
|
31841
|
+
);
|
|
31842
|
+
}
|
|
31843
|
+
|
|
31844
|
+
// src/tools/module-inspect.ts
|
|
31845
|
+
var fs4 = __toESM(require("fs"));
|
|
31846
|
+
var path6 = __toESM(require("path"));
|
|
31847
|
+
var moduleInspectSchema = {
|
|
31848
|
+
moduleDir: external_exports.string()
|
|
31849
|
+
};
|
|
31850
|
+
function moduleInspect(args) {
|
|
31851
|
+
const { paths, manifest } = loadManifest(args.moduleDir);
|
|
31852
|
+
const entities = manifest.entities ?? {};
|
|
31853
|
+
const entitySummaries = Object.entries(entities).map(([name, relPath]) => {
|
|
31854
|
+
const abs = path6.join(paths.root, relPath.replace(/^\.\//, ""));
|
|
31855
|
+
const e = readJsonOrDefault(abs, {});
|
|
31856
|
+
const fields = e.fields ?? {};
|
|
31857
|
+
return {
|
|
31858
|
+
name,
|
|
31859
|
+
isExtension: Boolean(e.extends),
|
|
31860
|
+
traits: e.traits ?? [],
|
|
31861
|
+
fieldCount: Object.keys(fields).length,
|
|
31862
|
+
fields: Object.keys(fields),
|
|
31863
|
+
hasNumberSequence: Boolean(e.numberSequence),
|
|
31864
|
+
toString: e.toString
|
|
31865
|
+
};
|
|
31866
|
+
});
|
|
31867
|
+
const views = readJsonOrDefault(paths.dataViews, {});
|
|
31868
|
+
const viewSummaries = Object.entries(views).map(([code, v]) => ({
|
|
31869
|
+
code,
|
|
31870
|
+
viewType: v.viewType ?? "?",
|
|
31871
|
+
sources: (v.dataSources ?? []).map(
|
|
31872
|
+
(s) => s.entityCode ?? "?"
|
|
31873
|
+
)
|
|
31874
|
+
}));
|
|
31875
|
+
const foldersTree = readJsonOrDefault(paths.folders, {});
|
|
31876
|
+
const folderDepth = computeDepth(foldersTree);
|
|
31877
|
+
const menus = readJsonOrDefault(paths.menus, {});
|
|
31878
|
+
const menuSummaries = Object.entries(menus).map(([code, m]) => ({
|
|
31879
|
+
code,
|
|
31880
|
+
itemCount: Object.keys(m.items ?? {}).length
|
|
31881
|
+
}));
|
|
31882
|
+
const rolesJson = readJsonOrDefault(paths.roles, {});
|
|
31883
|
+
const roleSummaries = Object.entries(rolesJson).map(([code, r]) => {
|
|
31884
|
+
const rights = r.rights ?? {};
|
|
31885
|
+
return { code, objectCount: Object.keys(rights).length, rights };
|
|
31886
|
+
});
|
|
31887
|
+
const actions = readJsonOrDefault(paths.actions, {});
|
|
31888
|
+
const actionSummaries = Object.entries(actions).map(([code, a]) => ({
|
|
31889
|
+
code,
|
|
31890
|
+
entity: a.entity ?? "?",
|
|
31891
|
+
mode: a.mode ?? "single",
|
|
31892
|
+
background: Boolean(a.background)
|
|
31893
|
+
}));
|
|
31894
|
+
const reports = Object.keys(readJsonOrDefault(paths.reports, {}));
|
|
31895
|
+
const settings = Object.keys(readJsonOrDefault(paths.settings, {}));
|
|
31896
|
+
const jobs = Object.keys(readJsonOrDefault(paths.jobs, {}));
|
|
31897
|
+
const seedFiles = fs4.existsSync(paths.seedDataDir) ? fs4.readdirSync(paths.seedDataDir).filter((f) => f.endsWith(".json")).sort() : [];
|
|
31898
|
+
const translations = fs4.existsSync(paths.translationsDir) ? fs4.readdirSync(paths.translationsDir).filter((f) => f.endsWith(".json")).sort() : [];
|
|
31899
|
+
const summary = {
|
|
31900
|
+
module: {
|
|
31901
|
+
code: manifest.code,
|
|
31902
|
+
displayName: manifest.displayName,
|
|
31903
|
+
version: manifest.version,
|
|
31904
|
+
dependencies: manifest.dependencies ?? {},
|
|
31905
|
+
auditHistory: manifest.auditHistory,
|
|
31906
|
+
kind: manifest.kind,
|
|
31907
|
+
tags: manifest.tags
|
|
31908
|
+
},
|
|
31909
|
+
entities: entitySummaries,
|
|
31910
|
+
views: viewSummaries,
|
|
31911
|
+
folders: { tree: foldersTree, depth: folderDepth },
|
|
31912
|
+
menus: menuSummaries,
|
|
31913
|
+
roles: roleSummaries,
|
|
31914
|
+
actions: actionSummaries,
|
|
31915
|
+
reports,
|
|
31916
|
+
settings,
|
|
31917
|
+
jobs,
|
|
31918
|
+
seedFiles,
|
|
31919
|
+
translations
|
|
31920
|
+
};
|
|
31921
|
+
return {
|
|
31922
|
+
summary: `Module '${manifest.code}' v${manifest.version}: ${entitySummaries.length} entities, ${viewSummaries.length} views, ${roleSummaries.length} roles, ${actionSummaries.length} actions, ${reports.length} reports.`,
|
|
31923
|
+
files: { "_inspect.json": JSON.stringify(summary, null, " ") + "\n" }
|
|
31924
|
+
};
|
|
31925
|
+
}
|
|
31926
|
+
function computeDepth(folder, current = 1) {
|
|
31927
|
+
const children = folder.children;
|
|
31928
|
+
if (!children) return current;
|
|
31929
|
+
const childDepths = Object.values(children).map(
|
|
31930
|
+
(c) => computeDepth(c, current + 1)
|
|
31931
|
+
);
|
|
31932
|
+
return childDepths.length === 0 ? current : Math.max(...childDepths);
|
|
31933
|
+
}
|
|
31934
|
+
|
|
31935
|
+
// src/resources/index.ts
|
|
31936
|
+
var fs5 = __toESM(require("fs"));
|
|
31937
|
+
var path7 = __toESM(require("path"));
|
|
31938
|
+
var RESOURCES_DIR = path7.resolve(__dirname, "..", "resources");
|
|
31368
31939
|
function schema(name, label, description) {
|
|
31369
31940
|
return {
|
|
31370
31941
|
uri: `dforge://schema/${name}`,
|
|
@@ -31443,13 +32014,13 @@ var resources = [
|
|
|
31443
32014
|
read: () => readVendored("docs/conventions.md")
|
|
31444
32015
|
}
|
|
31445
32016
|
];
|
|
31446
|
-
function readVendored(
|
|
31447
|
-
const p =
|
|
31448
|
-
if (!
|
|
31449
|
-
return `// Resource missing at build time: ${
|
|
32017
|
+
function readVendored(rel2) {
|
|
32018
|
+
const p = path7.join(RESOURCES_DIR, rel2);
|
|
32019
|
+
if (!fs5.existsSync(p)) {
|
|
32020
|
+
return `// Resource missing at build time: ${rel2}
|
|
31450
32021
|
// Run scripts/vendor-resources.sh in dforge-mcp to refresh from dForge-core.`;
|
|
31451
32022
|
}
|
|
31452
|
-
return
|
|
32023
|
+
return fs5.readFileSync(p, "utf8");
|
|
31453
32024
|
}
|
|
31454
32025
|
|
|
31455
32026
|
// src/server.ts
|
|
@@ -31457,9 +32028,26 @@ var server = new McpServer({
|
|
|
31457
32028
|
name: "dforge-mcp",
|
|
31458
32029
|
version: "0.1.0"
|
|
31459
32030
|
});
|
|
32031
|
+
function envelope(fn) {
|
|
32032
|
+
return async (args) => {
|
|
32033
|
+
try {
|
|
32034
|
+
const r = fn(args);
|
|
32035
|
+
return {
|
|
32036
|
+
content: [{ type: "text", text: JSON.stringify(r, null, 2) }]
|
|
32037
|
+
};
|
|
32038
|
+
} catch (e) {
|
|
32039
|
+
return {
|
|
32040
|
+
content: [
|
|
32041
|
+
{ type: "text", text: `Error: ${e.message}` }
|
|
32042
|
+
],
|
|
32043
|
+
isError: true
|
|
32044
|
+
};
|
|
32045
|
+
}
|
|
32046
|
+
};
|
|
32047
|
+
}
|
|
31460
32048
|
server.tool(
|
|
31461
32049
|
"dforge_module_create",
|
|
31462
|
-
"Build the file map for a brand-new dForge module. Returns { files: { '<relPath>': '<contents>' } } \u2014 the client decides whether to write them. Always preview the file map with the user before writing.",
|
|
32050
|
+
"PHASE 1: Build the file map for a brand-new dForge module. Returns { files: { '<relPath>': '<contents>' } } \u2014 the client decides whether to write them. Always preview the file map with the user before writing.",
|
|
31463
32051
|
createModuleSchema,
|
|
31464
32052
|
async (args) => {
|
|
31465
32053
|
const files = createModuleFiles(args);
|
|
@@ -31480,9 +32068,36 @@ server.tool(
|
|
|
31480
32068
|
};
|
|
31481
32069
|
}
|
|
31482
32070
|
);
|
|
32071
|
+
server.tool(
|
|
32072
|
+
"dforge_module_inspect",
|
|
32073
|
+
"Read the current state of an existing module from disk and return a structured summary. Call this BEFORE any patch tool so you know what entities/views/roles already exist and don't try to re-create them.",
|
|
32074
|
+
moduleInspectSchema,
|
|
32075
|
+
envelope(moduleInspect)
|
|
32076
|
+
);
|
|
32077
|
+
server.tool(
|
|
32078
|
+
"dforge_module_pack",
|
|
32079
|
+
"Pack a module directory into a .dforge tarball. Requires the dforge-cli native binary on PATH (or set DFORGE_CLI_BINARY).",
|
|
32080
|
+
packModuleSchema,
|
|
32081
|
+
async (args) => {
|
|
32082
|
+
const result = packModule(args);
|
|
32083
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
32084
|
+
}
|
|
32085
|
+
);
|
|
32086
|
+
server.tool(
|
|
32087
|
+
"dforge_module_install",
|
|
32088
|
+
"PHASE 6: Install a module (directory or .dforge tarball) to a running tenant. Runs the FULL server-side validator \u2014 the only real validator. Reads DFORGE_URL / DFORGE_TOKEN env if not passed as args.",
|
|
32089
|
+
installModuleSchema,
|
|
32090
|
+
async (args) => {
|
|
32091
|
+
const result = installModule(args);
|
|
32092
|
+
return {
|
|
32093
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
32094
|
+
isError: !result.ok
|
|
32095
|
+
};
|
|
32096
|
+
}
|
|
32097
|
+
);
|
|
31483
32098
|
server.tool(
|
|
31484
32099
|
"dforge_entity_add",
|
|
31485
|
-
"Add an entity to an existing module. Reads manifest from disk, returns the updated file map (manifest + new entity + regenerated UI/security). Other files on disk are NOT touched.",
|
|
32100
|
+
"PHASE 2: Add an entity to an existing module. Reads manifest from disk, returns the updated file map (manifest + new entity + regenerated UI/security). Other files on disk are NOT touched.",
|
|
31486
32101
|
addEntitySchema,
|
|
31487
32102
|
async (args) => {
|
|
31488
32103
|
const { files, warning } = addEntityFiles(args);
|
|
@@ -31492,7 +32107,7 @@ server.tool(
|
|
|
31492
32107
|
type: "text",
|
|
31493
32108
|
text: JSON.stringify(
|
|
31494
32109
|
{
|
|
31495
|
-
summary: `Adds entity '${args.entity.name}' to ${args.moduleDir}
|
|
32110
|
+
summary: `Adds entity '${args.entity.name}' to ${args.moduleDir}.`,
|
|
31496
32111
|
warning,
|
|
31497
32112
|
files
|
|
31498
32113
|
},
|
|
@@ -31505,27 +32120,76 @@ server.tool(
|
|
|
31505
32120
|
}
|
|
31506
32121
|
);
|
|
31507
32122
|
server.tool(
|
|
31508
|
-
"
|
|
31509
|
-
"
|
|
31510
|
-
|
|
31511
|
-
|
|
31512
|
-
const result = packModule(args);
|
|
31513
|
-
return {
|
|
31514
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
31515
|
-
};
|
|
31516
|
-
}
|
|
32123
|
+
"dforge_entity_field_add",
|
|
32124
|
+
"PHASE 2 / backtrack: Add a single new field to an existing entity. Use this (not entity_add) when refining an existing entity \u2014 preserves the rest of the entity definition.",
|
|
32125
|
+
entityFieldAddSchema,
|
|
32126
|
+
envelope(entityFieldAdd)
|
|
31517
32127
|
);
|
|
31518
32128
|
server.tool(
|
|
31519
|
-
"
|
|
31520
|
-
"
|
|
31521
|
-
|
|
31522
|
-
|
|
31523
|
-
|
|
31524
|
-
|
|
31525
|
-
|
|
31526
|
-
|
|
31527
|
-
|
|
31528
|
-
|
|
32129
|
+
"dforge_entity_field_modify",
|
|
32130
|
+
"PHASE 2 / backtrack: Replace an existing field's spec on an entity. Pass the full desired field shape, not a partial diff.",
|
|
32131
|
+
entityFieldModifySchema,
|
|
32132
|
+
envelope(entityFieldModify)
|
|
32133
|
+
);
|
|
32134
|
+
server.tool(
|
|
32135
|
+
"dforge_entity_field_remove",
|
|
32136
|
+
"PHASE 2 / backtrack: Remove a field from an entity. WARNING: may break dependent views, role rights, formulas, action DSL, seed data \u2014 re-run dforge_module_inspect after.",
|
|
32137
|
+
entityFieldRemoveSchema,
|
|
32138
|
+
envelope(entityFieldRemove)
|
|
32139
|
+
);
|
|
32140
|
+
server.tool(
|
|
32141
|
+
"dforge_action_add",
|
|
32142
|
+
"PHASE 3: Add a DSL action targeting an entity. Writes logic/actions/<code>.dsl plus an entry in ui/actions.json. Phase 3 is optional \u2014 only call when business logic genuinely requires custom actions.",
|
|
32143
|
+
actionAddSchema,
|
|
32144
|
+
envelope(actionAdd)
|
|
32145
|
+
);
|
|
32146
|
+
server.tool(
|
|
32147
|
+
"dforge_view_add",
|
|
32148
|
+
"PHASE 4: Add a data view to ui/data_views.json. viewType-specific viewConfig is supplied verbatim \u2014 pull dforge://schema/data-views first to know the shape.",
|
|
32149
|
+
viewAddSchema,
|
|
32150
|
+
envelope(viewAdd)
|
|
32151
|
+
);
|
|
32152
|
+
server.tool(
|
|
32153
|
+
"dforge_view_modify",
|
|
32154
|
+
"PHASE 4 / backtrack: Replace an existing view spec. Pass the full desired shape.",
|
|
32155
|
+
viewModifySchema,
|
|
32156
|
+
envelope(viewModify)
|
|
32157
|
+
);
|
|
32158
|
+
server.tool(
|
|
32159
|
+
"dforge_report_add",
|
|
32160
|
+
"PHASE 4: Add a report to ui/reports.json. Read dforge://schema/reports for the layout/datasets/parameters shape.",
|
|
32161
|
+
reportAddSchema,
|
|
32162
|
+
envelope(reportAdd)
|
|
32163
|
+
);
|
|
32164
|
+
server.tool(
|
|
32165
|
+
"dforge_setting_add",
|
|
32166
|
+
"PHASE 4 (polish): Add a configurable module-level setting to settings.json. Settings are folder-scoped at runtime \u2014 values inherit through the folder tree.",
|
|
32167
|
+
settingAddSchema,
|
|
32168
|
+
envelope(settingAdd)
|
|
32169
|
+
);
|
|
32170
|
+
server.tool(
|
|
32171
|
+
"dforge_role_add",
|
|
32172
|
+
"PHASE 5: Add a role to security/roles.json. Roles are namespaced (e.g. 'crm.admin'). Rights are S/I/U/D/C for entities, E for actions/reports.",
|
|
32173
|
+
roleAddSchema,
|
|
32174
|
+
envelope(roleAdd)
|
|
32175
|
+
);
|
|
32176
|
+
server.tool(
|
|
32177
|
+
"dforge_role_right_set",
|
|
32178
|
+
"PHASE 5 / backtrack: Grant or revoke a single right on a single object for a role. Cheaper than role_add when iterating on rights matrix.",
|
|
32179
|
+
roleRightSetSchema,
|
|
32180
|
+
envelope(roleRightSet)
|
|
32181
|
+
);
|
|
32182
|
+
server.tool(
|
|
32183
|
+
"dforge_folder_add",
|
|
32184
|
+
"PHASE 5 (optional): Add a sub-folder to ui/folders.json. Folders are SECURITY boundaries (row-level filters + per-folder role mappings). Most modules don't need any beyond the root \u2014 only use when intake said data must be separated per folder.",
|
|
32185
|
+
folderAddSchema,
|
|
32186
|
+
envelope(folderAdd)
|
|
32187
|
+
);
|
|
32188
|
+
server.tool(
|
|
32189
|
+
"dforge_dependency_add",
|
|
32190
|
+
"Add a dependency on another dForge module to manifest.json. Use the entities list form when only specific entities are imported (smaller coupling).",
|
|
32191
|
+
dependencyAddSchema,
|
|
32192
|
+
envelope(dependencyAdd)
|
|
31529
32193
|
);
|
|
31530
32194
|
server.tool(
|
|
31531
32195
|
"dforge_dbml_import",
|