create-windy 0.3.19 → 0.4.0
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 +28 -13
- package/dist/cli.js +783 -280
- package/package.json +1 -1
- package/template/.agents/skills/windy-business-module/SKILL.md +34 -10
- package/template/.windy-template.json +2 -2
- package/template/AGENTS.md +18 -8
- package/template/README.md +32 -22
- package/template/apps/server/Dockerfile +1 -0
- package/template/apps/server/package.json +1 -0
- package/template/apps/server/scripts/validate-module-composition-once.ts +4 -0
- package/template/apps/server/scripts/validate-module-composition.ts +28 -0
- package/template/apps/server/src/agent/data-scope-routes.test.ts +20 -5
- package/template/apps/server/src/agent/routes.test.ts +20 -7
- package/template/apps/server/src/agent/routes.ts +14 -0
- package/template/apps/server/src/agent/runtime-bootstrap.ts +22 -6
- package/template/apps/server/src/agent/sandbox-worker.ts +2 -2
- package/template/apps/server/src/agent/unavailable-routes.ts +33 -0
- package/template/apps/server/src/app/server-composition.ts +56 -0
- package/template/apps/server/src/application-services.ts +2 -0
- package/template/apps/server/src/bulk-data/service.ts +14 -0
- package/template/apps/server/src/data-governance/export/authorization-port.ts +1 -1
- package/template/apps/server/src/data-governance/export/postgres-api-fixture.ts +18 -2
- package/template/apps/server/src/data-governance/export/postgres-recovery.integration.test.ts +10 -9
- package/template/apps/server/src/data-governance/export/routes-test-fixture.ts +6 -8
- package/template/apps/server/src/data-governance/user-plaintext-routes.test.ts +4 -2
- package/template/apps/server/src/feature/routes.authorization.test.ts +26 -1
- package/template/apps/server/src/feature/routes.ts +33 -15
- package/template/apps/server/src/foundation-activation.test.ts +60 -0
- package/template/apps/server/src/foundation.ts +64 -5
- package/template/apps/server/src/guards.test.ts +23 -0
- package/template/apps/server/src/guards.ts +12 -0
- package/template/apps/server/src/index.ts +28 -34
- package/template/apps/server/src/installed-business-modules.ts +13 -24
- package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +1 -0
- package/template/apps/server/src/jobs/legacy-adapter.test.ts +106 -0
- package/template/apps/server/src/jobs/legacy-service-runtime.ts +40 -0
- package/template/apps/server/src/license/activation.test.ts +30 -0
- package/template/apps/server/src/license/activation.ts +26 -0
- package/template/apps/server/src/license/restriction.test.ts +16 -0
- package/template/apps/server/src/license/routes-offline-code.test.ts +6 -2
- package/template/apps/server/src/license/routes.ts +20 -4
- package/template/apps/server/src/license/runtime-service.test.ts +10 -2
- package/template/apps/server/src/module-bootstrap.ts +14 -23
- package/template/apps/server/src/module-composition.test.ts +43 -2
- package/template/apps/server/src/module-composition.ts +29 -0
- package/template/apps/server/src/module-host/durable-context-guard.test.ts +172 -0
- package/template/apps/server/src/module-host/durable-context-unavailable.test.ts +50 -1
- package/template/apps/server/src/module-host/durable-context.test.ts +42 -25
- package/template/apps/server/src/module-host/durable-context.ts +42 -2
- package/template/apps/server/src/module-host/durable-handler-registration.ts +96 -5
- package/template/apps/server/src/module-host/implementation-completeness.test.ts +185 -0
- package/template/apps/server/src/module-host/implementation-completeness.ts +132 -0
- package/template/apps/server/src/module-host/initialize-contracts.ts +14 -0
- package/template/apps/server/src/module-host/initialize.test.ts +78 -2
- package/template/apps/server/src/module-host/initialize.ts +15 -4
- package/template/apps/server/src/module-host/project-attachments.test.ts +92 -0
- package/template/apps/server/src/module-host/project-attachments.ts +58 -0
- package/template/apps/server/src/module-host/project-composition-package.test.ts +186 -0
- package/template/apps/server/src/module-host/project-composition.test.ts +260 -0
- package/template/apps/server/src/module-host/project-composition.ts +311 -0
- package/template/apps/server/src/module-host/request-context.ts +3 -0
- package/template/apps/server/src/module-host/route-declaration.ts +15 -6
- package/template/apps/server/src/module-host/route-installer.test.ts +15 -2
- package/template/apps/server/src/module-host/route-installer.ts +12 -2
- package/template/apps/server/src/module-host/route-registration.test.ts +13 -4
- package/template/apps/server/src/module-host/runtime-composition-compatibility.test.ts +63 -0
- package/template/apps/server/src/module-host/runtime-composition-compatibility.ts +46 -0
- package/template/apps/server/src/module-host/runtime-composition.ts +34 -0
- package/template/apps/server/src/module-host/search-provider-registration.test.ts +64 -0
- package/template/apps/server/src/module-host/search-provider-registration.ts +35 -0
- package/template/apps/server/src/module-host/static-route-authorization.test.ts +99 -0
- package/template/apps/server/src/module-host/static-route-authorization.ts +90 -0
- package/template/apps/server/src/module-host/task-definitions.ts +1 -0
- package/template/apps/server/src/module-host/user-directory-composition.test.ts +4 -1
- package/template/apps/server/src/module-host.test.ts +2 -0
- package/template/apps/server/src/module-preflight.test.ts +289 -0
- package/template/apps/server/src/module-preflight.ts +96 -0
- package/template/apps/server/src/module-runtime-validation.ts +1 -0
- package/template/apps/server/src/platform-app-bootstrap.ts +50 -0
- package/template/apps/server/src/platform-http-runtime.ts +32 -7
- package/template/apps/server/src/route-guards.test.ts +22 -0
- package/template/apps/server/src/route-guards.ts +10 -3
- package/template/apps/server/src/runtime-audit-query.ts +50 -0
- package/template/apps/server/src/runtime-feature.test.ts +46 -0
- package/template/apps/server/src/runtime-feature.ts +5 -0
- package/template/apps/server/src/runtime.ts +10 -49
- package/template/apps/server/src/scheduler/audit-action.test.ts +49 -0
- package/template/apps/server/src/scheduler/audit.ts +1 -1
- package/template/apps/server/src/scheduler/execution-recorder.ts +10 -5
- package/template/apps/server/src/scheduler/recovery-service.test.ts +4 -3
- package/template/apps/server/src/scheduler/service-idle.test.ts +4 -3
- package/template/apps/server/src/scheduler/service.test.ts +1 -0
- package/template/apps/server/src/scheduler/service.ts +18 -21
- package/template/apps/server/src/scheduler/types.ts +5 -1
- package/template/apps/server/src/startup-composition-validation.test.ts +35 -0
- package/template/apps/server/src/startup-composition-validation.ts +54 -0
- package/template/apps/server/src/startup-preflight.test.ts +36 -0
- package/template/apps/server/src/startup-preflight.ts +37 -0
- package/template/apps/server/src/system/audit-routes.test.ts +9 -3
- package/template/apps/server/src/system/department-routes.test.ts +5 -3
- package/template/apps/server/src/system/feature-routes.test.ts +4 -3
- package/template/apps/server/src/system/interface-catalog.test.ts +5 -3
- package/template/apps/server/src/system/interface-catalog.ts +3 -0
- package/template/apps/server/src/system/menu-routes.test.ts +5 -3
- package/template/apps/server/src/system/route-test-runtime.ts +24 -0
- package/template/apps/server/src/system/routes-validation.test.ts +7 -3
- package/template/apps/server/src/system/routes.test.ts +21 -11
- package/template/apps/server/src/system/routes.ts +11 -2
- package/template/apps/server/src/work-order/foundation-modules.test.ts +6 -4
- package/template/apps/server/src/work-order/routes.test.ts +8 -0
- package/template/apps/server/src/work-order/task.test.ts +6 -1
- package/template/apps/web/Dockerfile +5 -0
- package/template/apps/web/package.json +1 -0
- package/template/apps/web/scripts/validate-admin-ui-composition.ts +106 -0
- package/template/apps/web/src/app/web-composition.ts +24 -0
- package/template/apps/web/src/app/web-composition.webtest.ts +15 -0
- package/template/apps/web/src/layout/AppSidebar.vue +8 -1
- package/template/apps/web/src/layout/NavigationSearchDialog.vue +8 -1
- package/template/apps/web/src/layout/navigation.ts +29 -4
- package/template/apps/web/src/layout/navigation.webtest.ts +51 -0
- package/template/apps/web/src/pages/dashboard/dashboard-presenter.ts +6 -1
- package/template/apps/web/src/pages/work-orders/WorkOrderListPage.vue +202 -0
- package/template/apps/web/src/pages/work-orders/WorkOrderListPage.webtest.ts +51 -0
- package/template/apps/web/src/router/admin-extension-loader.ts +44 -0
- package/template/apps/web/src/router/admin-extension-loader.webtest.ts +49 -0
- package/template/apps/web/src/router/admin-ui-composition-plugin.ts +33 -0
- package/template/apps/web/src/router/admin-ui-composition-plugin.webtest.ts +20 -0
- package/template/apps/web/src/router/admin-ui-composition.ts +313 -0
- package/template/apps/web/src/router/admin-ui-composition.webtest.ts +225 -0
- package/template/apps/web/src/router/admin-ui-module-registry-plugin.ts +55 -0
- package/template/apps/web/src/router/admin-ui-module-registry-plugin.webtest.ts +46 -0
- package/template/apps/web/src/router/business-routes.ts +32 -45
- package/template/apps/web/src/router/business-routes.webtest.ts +40 -44
- package/template/apps/web/src/router/index.ts +64 -54
- package/template/apps/web/src/router/manifest-routes.ts +27 -7
- package/template/apps/web/src/router/manifest-routes.webtest.ts +10 -2
- package/template/apps/web/src/router/platform-admin-route-bindings.ts +58 -0
- package/template/apps/web/src/router/reserved-route-paths.ts +14 -0
- package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +10 -4
- package/template/apps/web/src/router/web-composition-host.ts +20 -0
- package/template/apps/web/src/services/work-order-api.ts +16 -0
- package/template/apps/web/src/services/work-order-api.webtest.ts +21 -0
- package/template/apps/web/src/virtual-admin-ui-modules.d.ts +5 -0
- package/template/apps/web/vite.config.ts +22 -1
- package/template/docs/architecture/migration-bundle.md +10 -7
- package/template/docs/architecture/module-manifest-composition.md +85 -3
- package/template/docs/architecture/search-provider-protocol.md +6 -1
- package/template/docs/architecture/server-module-interface.md +70 -7
- package/template/docs/development/business-web-routes.md +53 -50
- package/template/docs/development/module-lifecycle.md +48 -54
- package/template/docs/platform/navigation-registry.md +5 -4
- package/template/package.json +1 -1
- package/template/packages/config/package.json +1 -1
- package/template/packages/database/package.json +1 -1
- package/template/packages/example-work-order/package.json +6 -0
- package/template/packages/example-work-order/src/admin-page.ts +78 -0
- package/template/packages/example-work-order/src/admin.ts +35 -0
- package/template/packages/example-work-order/src/manifest.test.ts +62 -5
- package/template/packages/example-work-order/src/manifest.ts +54 -7
- package/template/packages/example-work-order/src/migration-bundle.ts +8 -0
- package/template/packages/example-work-order/src/server-module.ts +2 -2
- package/template/packages/jobs/package.json +1 -1
- package/template/packages/modules/index.ts +4 -0
- package/template/packages/modules/package.json +3 -2
- package/template/packages/modules/src/admin-ui-entry.ts +8 -0
- package/template/packages/modules/src/admin-ui-manifest.test.ts +52 -0
- package/template/packages/modules/src/admin-ui-manifest.ts +53 -0
- package/template/packages/modules/src/capability-audit-validation.test.ts +127 -0
- package/template/packages/modules/src/capability-audit-validation.ts +38 -0
- package/template/packages/modules/src/capability-guard-validation.ts +148 -0
- package/template/packages/modules/src/capability-key-validation.test.ts +150 -0
- package/template/packages/modules/src/composition-fixtures.test-support.ts +7 -5
- package/template/packages/modules/src/composition.test.ts +74 -9
- package/template/packages/modules/src/composition.ts +8 -0
- package/template/packages/modules/src/manifest.ts +1 -0
- package/template/packages/modules/src/migration-bundle-validation.test.ts +23 -0
- package/template/packages/modules/src/module-host-range.test.ts +57 -0
- package/template/packages/modules/src/module-host-range.ts +125 -0
- package/template/packages/modules/src/project-module-assembly.test.ts +133 -0
- package/template/packages/modules/src/project-module-assembly.ts +126 -0
- package/template/packages/modules/src/system-admin-routes.ts +6 -5
- package/template/packages/modules/src/system-admin-ui-catalog.ts +72 -0
- package/template/packages/modules/src/system-agent-tools.ts +2 -2
- package/template/packages/modules/src/system-api-binding.ts +43 -9
- package/template/packages/modules/src/system-api-control-plane.ts +202 -0
- package/template/packages/modules/src/system-api-data-governance.ts +4 -2
- package/template/packages/modules/src/system-api-operational.ts +57 -8
- package/template/packages/modules/src/system-api-permissions.ts +115 -21
- package/template/packages/modules/src/system-api-watermark.ts +3 -0
- package/template/packages/modules/src/system-audit-actions.ts +7 -0
- package/template/packages/modules/src/system-data-governance.ts +5 -2
- package/template/packages/modules/src/system-features.ts +31 -0
- package/template/packages/modules/src/system-governed-export-bindings.ts +8 -4
- package/template/packages/modules/src/system-module-catalog.test.ts +39 -4
- package/template/packages/modules/src/system-module-catalog.ts +68 -7
- package/template/packages/modules/src/system-module-tasks.ts +8 -1
- package/template/packages/modules/src/system-modules-access.test.ts +4 -0
- package/template/packages/modules/src/system-modules.test.ts +10 -2
- package/template/packages/modules/src/system-modules.ts +6 -5
- package/template/packages/modules/src/system-permissions.ts +4 -0
- package/template/packages/modules/user/module.ts +19 -2
- package/template/packages/server-sdk/index.ts +8 -0
- package/template/packages/server-sdk/package.json +2 -1
- package/template/packages/server-sdk/src/durable-handler.ts +16 -1
- package/template/packages/server-sdk/src/module-adapter.test.ts +44 -0
- package/template/packages/server-sdk/src/module-adapter.ts +59 -0
- package/template/packages/server-sdk/src/route-definition.ts +3 -2
- package/template/packages/shared/package.json +1 -1
- package/template/packages/shared/src/audit.test.ts +7 -0
- package/template/packages/shared/src/audit.ts +16 -0
- package/template/packages/shared/src/migration-bundle-validation.ts +9 -11
- package/template/packages/shared/src/rbac.ts +2 -0
- package/template/apps/web/src/app/business-routes.ts +0 -22
|
@@ -4,6 +4,11 @@ import type {
|
|
|
4
4
|
ModuleManifest,
|
|
5
5
|
} from "./manifest.js";
|
|
6
6
|
import { validateAiOperations } from "./ai-operation-composition.js";
|
|
7
|
+
import { validateCapabilityAuditActions } from "./capability-audit-validation.js";
|
|
8
|
+
import {
|
|
9
|
+
validateCapabilityGuardCompleteness,
|
|
10
|
+
validateCapabilityKeyAlignment,
|
|
11
|
+
} from "./capability-guard-validation.js";
|
|
7
12
|
import { validateImplementationBindings } from "./implementation-bindings.js";
|
|
8
13
|
import { composeModuleMenus } from "./menu-composition.js";
|
|
9
14
|
import { validateCapabilityCatalogs } from "./catalog-validation.js";
|
|
@@ -27,6 +32,7 @@ export function validateModuleComposition(
|
|
|
27
32
|
modules.map(({ name }) => name),
|
|
28
33
|
);
|
|
29
34
|
const moduleOrder = validateDependencies(modules, moduleNames);
|
|
35
|
+
validateCapabilityGuardCompleteness(modules);
|
|
30
36
|
const permissions = unique(
|
|
31
37
|
"权限",
|
|
32
38
|
modules.flatMap((module) => module.permissions.map(({ key }) => key)),
|
|
@@ -35,9 +41,11 @@ export function validateModuleComposition(
|
|
|
35
41
|
"Feature",
|
|
36
42
|
modules.flatMap((module) => module.features.map(({ key }) => key)),
|
|
37
43
|
);
|
|
44
|
+
validateCapabilityKeyAlignment(modules);
|
|
38
45
|
const catalogs = validateCapabilityCatalogs(modules);
|
|
39
46
|
const jsonSchemas = validateToolContracts(modules);
|
|
40
47
|
const audits = new Set(catalogs.auditActions.map(({ key }) => key));
|
|
48
|
+
validateCapabilityAuditActions(modules, audits);
|
|
41
49
|
const menus = composeModuleMenus(modules).flatMap((menu) =>
|
|
42
50
|
flattenMenus([menu]),
|
|
43
51
|
);
|
|
@@ -41,6 +41,29 @@ describe("Migration Bundle 启动校验", () => {
|
|
|
41
41
|
expect(() => validateMigrationBundleCatalog([feature, base])).not.toThrow();
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
+
test("Manifest 可声明不参与迁移排序的额外运行时依赖", () => {
|
|
45
|
+
const module = bundledModule("feature", [
|
|
46
|
+
migration("feature", "feature.0001"),
|
|
47
|
+
]);
|
|
48
|
+
module.dependencies = ["system"];
|
|
49
|
+
|
|
50
|
+
expect(() => validateMigrationBundleCatalog([module])).not.toThrow();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("Bundle 依赖必须先在 Manifest 中声明", () => {
|
|
54
|
+
const base = bundledModule("base", [migration("base", "base.0001")]);
|
|
55
|
+
const feature = bundledModule(
|
|
56
|
+
"feature",
|
|
57
|
+
[migration("feature", "feature.0001")],
|
|
58
|
+
["base"],
|
|
59
|
+
);
|
|
60
|
+
feature.dependencies = [];
|
|
61
|
+
|
|
62
|
+
expect(() => validateMigrationBundleCatalog([base, feature])).toThrow(
|
|
63
|
+
"Migration Bundle feature 依赖未在 Manifest 声明:base",
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
44
67
|
test("重复 Migration ID 被拒绝", () => {
|
|
45
68
|
const first = bundledModule("first", [migration("first", "shared.0001")]);
|
|
46
69
|
const second = bundledModule("second", [
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { satisfiesModuleHostRange } from "./module-host-range.js";
|
|
3
|
+
|
|
4
|
+
describe("Module Host SemVer range", () => {
|
|
5
|
+
test("exact 忽略 build metadata,但区分 prerelease", () => {
|
|
6
|
+
expect(satisfiesModuleHostRange("1.2.3+host.7", "1.2.3")).toBeTrue();
|
|
7
|
+
expect(
|
|
8
|
+
satisfiesModuleHostRange("1.2.3-beta.2+host.7", "1.2.3-beta.2+module.1"),
|
|
9
|
+
).toBeTrue();
|
|
10
|
+
expect(
|
|
11
|
+
satisfiesModuleHostRange("1.2.3-beta.2", "1.2.3-beta.1"),
|
|
12
|
+
).toBeFalse();
|
|
13
|
+
expect(satisfiesModuleHostRange("1.2.3-beta.2", "1.2.3")).toBeFalse();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("prerelease 标识按 SemVer 数字和字符串规则排序", () => {
|
|
17
|
+
expect(
|
|
18
|
+
satisfiesModuleHostRange("1.2.3-beta.10", "^1.2.3-beta.2"),
|
|
19
|
+
).toBeTrue();
|
|
20
|
+
expect(
|
|
21
|
+
satisfiesModuleHostRange("1.2.3-beta.2", "^1.2.3-beta.10"),
|
|
22
|
+
).toBeFalse();
|
|
23
|
+
expect(satisfiesModuleHostRange("1.2.3-rc.1", "^1.2.3-beta.9")).toBeTrue();
|
|
24
|
+
expect(satisfiesModuleHostRange("1.2.3", "^1.2.3-beta.9")).toBeTrue();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("caret 遵循 0.x 的兼容上界", () => {
|
|
28
|
+
expect(satisfiesModuleHostRange("2.9.9", "^2.1.0")).toBeTrue();
|
|
29
|
+
expect(satisfiesModuleHostRange("3.0.0", "^2.1.0")).toBeFalse();
|
|
30
|
+
expect(satisfiesModuleHostRange("0.2.9", "^0.2.3")).toBeTrue();
|
|
31
|
+
expect(satisfiesModuleHostRange("0.3.0", "^0.2.3")).toBeFalse();
|
|
32
|
+
expect(satisfiesModuleHostRange("0.0.4", "^0.0.3")).toBeFalse();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("tilde 只允许同一 minor 内升级", () => {
|
|
36
|
+
expect(satisfiesModuleHostRange("1.2.99", "~1.2.3")).toBeTrue();
|
|
37
|
+
expect(satisfiesModuleHostRange("1.3.0", "~1.2.3")).toBeFalse();
|
|
38
|
+
expect(satisfiesModuleHostRange("1.2.2", "~1.2.3")).toBeFalse();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("普通稳定范围不隐式接纳 prerelease", () => {
|
|
42
|
+
expect(satisfiesModuleHostRange("1.3.0-beta.1", "^1.2.3")).toBeFalse();
|
|
43
|
+
expect(satisfiesModuleHostRange("1.2.4-beta.1", "~1.2.3")).toBeFalse();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("非法版本或不支持的 range 以中文错误拒绝", () => {
|
|
47
|
+
expect(() => satisfiesModuleHostRange("1.2", "^1.2.3")).toThrow(
|
|
48
|
+
"无效 SemVer:1.2",
|
|
49
|
+
);
|
|
50
|
+
expect(() => satisfiesModuleHostRange("1.2.3", ">=1.2.0")).toThrow(
|
|
51
|
+
"不支持的 Module Host range:>=1.2.0",
|
|
52
|
+
);
|
|
53
|
+
expect(() => satisfiesModuleHostRange("1.2.3", "^01.2.3")).toThrow(
|
|
54
|
+
"无效 SemVer:01.2.3",
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
interface SemVerIdentifier {
|
|
2
|
+
readonly numeric: boolean;
|
|
3
|
+
readonly value: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface SemVer {
|
|
7
|
+
readonly core: readonly [bigint, bigint, bigint];
|
|
8
|
+
readonly prerelease: readonly SemVerIdentifier[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function satisfiesModuleHostRange(
|
|
12
|
+
hostVersion: string,
|
|
13
|
+
range: string,
|
|
14
|
+
): boolean {
|
|
15
|
+
const current = parseSemVer(hostVersion);
|
|
16
|
+
const normalizedRange = range.trim();
|
|
17
|
+
const operator = normalizedRange.startsWith("^")
|
|
18
|
+
? "^"
|
|
19
|
+
: normalizedRange.startsWith("~")
|
|
20
|
+
? "~"
|
|
21
|
+
: "";
|
|
22
|
+
const versionText = operator ? normalizedRange.slice(1) : normalizedRange;
|
|
23
|
+
|
|
24
|
+
if (!operator && !/^\d/.test(versionText)) {
|
|
25
|
+
throw new Error(`不支持的 Module Host range:${range}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const minimum = parseSemVer(versionText);
|
|
29
|
+
if (!operator) return compareSemVer(current, minimum) === 0;
|
|
30
|
+
|
|
31
|
+
const maximum = compatibleUpperBound(minimum, operator);
|
|
32
|
+
if (
|
|
33
|
+
compareSemVer(current, minimum) < 0 ||
|
|
34
|
+
compareSemVer(current, maximum) >= 0
|
|
35
|
+
) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
if (current.prerelease.length === 0) return true;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
minimum.prerelease.length > 0 &&
|
|
42
|
+
compareCore(current.core, minimum.core) === 0
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function compatibleUpperBound(minimum: SemVer, operator: "^" | "~"): SemVer {
|
|
47
|
+
const [major, minor, patch] = minimum.core;
|
|
48
|
+
const core: readonly [bigint, bigint, bigint] =
|
|
49
|
+
operator === "~"
|
|
50
|
+
? [major, minor + 1n, 0n]
|
|
51
|
+
: major > 0n
|
|
52
|
+
? [major + 1n, 0n, 0n]
|
|
53
|
+
: minor > 0n
|
|
54
|
+
? [0n, minor + 1n, 0n]
|
|
55
|
+
: [0n, 0n, patch + 1n];
|
|
56
|
+
return { core, prerelease: [] };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseSemVer(value: string): SemVer {
|
|
60
|
+
const normalized = value.trim();
|
|
61
|
+
const match =
|
|
62
|
+
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.exec(
|
|
63
|
+
normalized,
|
|
64
|
+
);
|
|
65
|
+
if (!match) throw new Error(`无效 SemVer:${value}`);
|
|
66
|
+
|
|
67
|
+
const prerelease = match[4]
|
|
68
|
+
? match[4].split(".").map(parsePrereleaseIdentifier)
|
|
69
|
+
: [];
|
|
70
|
+
return {
|
|
71
|
+
core: [BigInt(match[1]!), BigInt(match[2]!), BigInt(match[3]!)],
|
|
72
|
+
prerelease,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function parsePrereleaseIdentifier(value: string): SemVerIdentifier {
|
|
77
|
+
const numeric = /^\d+$/.test(value);
|
|
78
|
+
if (numeric && value.length > 1 && value.startsWith("0")) {
|
|
79
|
+
throw new Error(`无效 SemVer prerelease 标识:${value}`);
|
|
80
|
+
}
|
|
81
|
+
return { numeric, value };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function compareSemVer(left: SemVer, right: SemVer): number {
|
|
85
|
+
const coreComparison = compareCore(left.core, right.core);
|
|
86
|
+
if (coreComparison !== 0) return coreComparison;
|
|
87
|
+
if (left.prerelease.length === 0) {
|
|
88
|
+
return right.prerelease.length === 0 ? 0 : 1;
|
|
89
|
+
}
|
|
90
|
+
if (right.prerelease.length === 0) return -1;
|
|
91
|
+
|
|
92
|
+
const length = Math.max(left.prerelease.length, right.prerelease.length);
|
|
93
|
+
for (let index = 0; index < length; index += 1) {
|
|
94
|
+
const leftIdentifier = left.prerelease[index];
|
|
95
|
+
const rightIdentifier = right.prerelease[index];
|
|
96
|
+
if (!leftIdentifier) return -1;
|
|
97
|
+
if (!rightIdentifier) return 1;
|
|
98
|
+
const comparison = compareIdentifier(leftIdentifier, rightIdentifier);
|
|
99
|
+
if (comparison !== 0) return comparison;
|
|
100
|
+
}
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function compareIdentifier(
|
|
105
|
+
left: SemVerIdentifier,
|
|
106
|
+
right: SemVerIdentifier,
|
|
107
|
+
): number {
|
|
108
|
+
if (left.numeric && right.numeric) {
|
|
109
|
+
return compareBigInt(BigInt(left.value), BigInt(right.value));
|
|
110
|
+
}
|
|
111
|
+
if (left.numeric !== right.numeric) return left.numeric ? -1 : 1;
|
|
112
|
+
return left.value < right.value ? -1 : left.value > right.value ? 1 : 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function compareCore(left: SemVer["core"], right: SemVer["core"]): number {
|
|
116
|
+
for (let index = 0; index < 3; index += 1) {
|
|
117
|
+
const comparison = compareBigInt(left[index]!, right[index]!);
|
|
118
|
+
if (comparison !== 0) return comparison;
|
|
119
|
+
}
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function compareBigInt(left: bigint, right: bigint): number {
|
|
124
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
125
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
indexProjectModuleAssembly,
|
|
4
|
+
validateProjectModuleAssembly,
|
|
5
|
+
} from "./project-module-assembly.js";
|
|
6
|
+
|
|
7
|
+
const validAssembly = {
|
|
8
|
+
schemaVersion: 1,
|
|
9
|
+
modules: [
|
|
10
|
+
{
|
|
11
|
+
name: "system",
|
|
12
|
+
package: null,
|
|
13
|
+
enabled: true,
|
|
14
|
+
order: 100,
|
|
15
|
+
admin: { visible: true },
|
|
16
|
+
config: {},
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "work-order",
|
|
20
|
+
package: "@southwind-ai/example-work-order",
|
|
21
|
+
enabled: false,
|
|
22
|
+
order: 200,
|
|
23
|
+
admin: { visible: false },
|
|
24
|
+
config: { queue: "default" },
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
} as const;
|
|
28
|
+
|
|
29
|
+
describe("项目 Module 装配清单", () => {
|
|
30
|
+
test("合法清单通过校验并可按名称索引", () => {
|
|
31
|
+
const assembly = validateProjectModuleAssembly(validAssembly);
|
|
32
|
+
const index = indexProjectModuleAssembly(assembly);
|
|
33
|
+
|
|
34
|
+
expect(assembly.schemaVersion).toBe(1);
|
|
35
|
+
expect(index.get("system")).toEqual(validAssembly.modules[0]);
|
|
36
|
+
expect(index.get("work-order")?.config).toEqual({ queue: "default" });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("只接受 schemaVersion=1 和 Module 数组", () => {
|
|
40
|
+
expect(() =>
|
|
41
|
+
validateProjectModuleAssembly({ ...validAssembly, schemaVersion: 2 }),
|
|
42
|
+
).toThrow("windy.modules.json schemaVersion 不受支持:2");
|
|
43
|
+
expect(() =>
|
|
44
|
+
validateProjectModuleAssembly({ schemaVersion: 1, modules: null }),
|
|
45
|
+
).toThrow("windy.modules.json modules 必须是数组");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("名称必须是非空字符串且不能重复", () => {
|
|
49
|
+
expect(() =>
|
|
50
|
+
validateProjectModuleAssembly({
|
|
51
|
+
...validAssembly,
|
|
52
|
+
modules: [{ ...validAssembly.modules[0], name: " " }],
|
|
53
|
+
}),
|
|
54
|
+
).toThrow("windy.modules.json Module 名称不能为空");
|
|
55
|
+
expect(() =>
|
|
56
|
+
validateProjectModuleAssembly({
|
|
57
|
+
...validAssembly,
|
|
58
|
+
modules: [
|
|
59
|
+
validAssembly.modules[0],
|
|
60
|
+
{ ...validAssembly.modules[1], name: "system" },
|
|
61
|
+
],
|
|
62
|
+
}),
|
|
63
|
+
).toThrow("windy.modules.json Module 重复:system");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("package 只接受 null 或合法 npm package 名称", () => {
|
|
67
|
+
for (const packageName of [
|
|
68
|
+
"",
|
|
69
|
+
"UpperCase",
|
|
70
|
+
".hidden",
|
|
71
|
+
"@scope",
|
|
72
|
+
"@scope/package/subpath",
|
|
73
|
+
"has space",
|
|
74
|
+
]) {
|
|
75
|
+
expect(() =>
|
|
76
|
+
validateProjectModuleAssembly({
|
|
77
|
+
...validAssembly,
|
|
78
|
+
modules: [{ ...validAssembly.modules[0], package: packageName }],
|
|
79
|
+
}),
|
|
80
|
+
).toThrow("windy.modules.json Module package 无效:system");
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("状态、顺序和 config 必须满足结构约束", () => {
|
|
85
|
+
expect(() =>
|
|
86
|
+
validateProjectModuleAssembly({
|
|
87
|
+
...validAssembly,
|
|
88
|
+
modules: [{ ...validAssembly.modules[0], enabled: "yes" }],
|
|
89
|
+
}),
|
|
90
|
+
).toThrow("windy.modules.json Module enabled 无效:system");
|
|
91
|
+
expect(() =>
|
|
92
|
+
validateProjectModuleAssembly({
|
|
93
|
+
...validAssembly,
|
|
94
|
+
modules: [
|
|
95
|
+
{
|
|
96
|
+
...validAssembly.modules[0],
|
|
97
|
+
admin: { visible: 1 },
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
}),
|
|
101
|
+
).toThrow("windy.modules.json Module admin.visible 无效:system");
|
|
102
|
+
|
|
103
|
+
for (const order of [-1, 1.5, Number.MAX_SAFE_INTEGER + 1]) {
|
|
104
|
+
expect(() =>
|
|
105
|
+
validateProjectModuleAssembly({
|
|
106
|
+
...validAssembly,
|
|
107
|
+
modules: [{ ...validAssembly.modules[0], order }],
|
|
108
|
+
}),
|
|
109
|
+
).toThrow("windy.modules.json Module order 无效:system");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const config of [null, [], new Date(), "配置"]) {
|
|
113
|
+
expect(() =>
|
|
114
|
+
validateProjectModuleAssembly({
|
|
115
|
+
...validAssembly,
|
|
116
|
+
modules: [{ ...validAssembly.modules[0], config }],
|
|
117
|
+
}),
|
|
118
|
+
).toThrow("windy.modules.json Module config 无效:system");
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("order 重复时拒绝,避免装配顺序不确定", () => {
|
|
123
|
+
expect(() =>
|
|
124
|
+
validateProjectModuleAssembly({
|
|
125
|
+
...validAssembly,
|
|
126
|
+
modules: [
|
|
127
|
+
validAssembly.modules[0],
|
|
128
|
+
{ ...validAssembly.modules[1], order: 100 },
|
|
129
|
+
],
|
|
130
|
+
}),
|
|
131
|
+
).toThrow("windy.modules.json Module order 重复:100");
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export interface ProjectModuleAdminSettings {
|
|
2
|
+
readonly visible: boolean;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface ProjectModuleRegistration {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly package: string | null;
|
|
8
|
+
readonly enabled: boolean;
|
|
9
|
+
readonly order: number;
|
|
10
|
+
readonly admin: ProjectModuleAdminSettings;
|
|
11
|
+
readonly config: Readonly<Record<string, unknown>>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ProjectModuleAssembly {
|
|
15
|
+
readonly schemaVersion: 1;
|
|
16
|
+
readonly modules: readonly ProjectModuleRegistration[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function validateProjectModuleAssembly(
|
|
20
|
+
value: unknown,
|
|
21
|
+
): ProjectModuleAssembly {
|
|
22
|
+
if (!isPlainObject(value)) {
|
|
23
|
+
throw new Error("windy.modules.json 必须是对象");
|
|
24
|
+
}
|
|
25
|
+
if (value.schemaVersion !== 1) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`windy.modules.json schemaVersion 不受支持:${String(value.schemaVersion)}`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
if (!Array.isArray(value.modules)) {
|
|
31
|
+
throw new Error("windy.modules.json modules 必须是数组");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const names = new Set<string>();
|
|
35
|
+
const orders = new Set<number>();
|
|
36
|
+
const modules = value.modules.map((entry) => {
|
|
37
|
+
const registration = validateRegistration(entry);
|
|
38
|
+
if (names.has(registration.name)) {
|
|
39
|
+
throw new Error(`windy.modules.json Module 重复:${registration.name}`);
|
|
40
|
+
}
|
|
41
|
+
if (orders.has(registration.order)) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`windy.modules.json Module order 重复:${registration.order}`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
names.add(registration.name);
|
|
47
|
+
orders.add(registration.order);
|
|
48
|
+
return registration;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return { schemaVersion: 1, modules };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function indexProjectModuleAssembly(
|
|
55
|
+
assembly: ProjectModuleAssembly,
|
|
56
|
+
): ReadonlyMap<string, ProjectModuleRegistration> {
|
|
57
|
+
const validated = validateProjectModuleAssembly(assembly);
|
|
58
|
+
return new Map(validated.modules.map((entry) => [entry.name, entry]));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function validateRegistration(value: unknown): ProjectModuleRegistration {
|
|
62
|
+
if (!isPlainObject(value)) {
|
|
63
|
+
throw new Error("windy.modules.json Module 必须是对象");
|
|
64
|
+
}
|
|
65
|
+
if (typeof value.name !== "string" || !value.name.trim()) {
|
|
66
|
+
throw new Error("windy.modules.json Module 名称不能为空");
|
|
67
|
+
}
|
|
68
|
+
const name = value.name;
|
|
69
|
+
if (
|
|
70
|
+
value.package !== null &&
|
|
71
|
+
(typeof value.package !== "string" || !isValidNpmPackageName(value.package))
|
|
72
|
+
) {
|
|
73
|
+
throw new Error(`windy.modules.json Module package 无效:${name}`);
|
|
74
|
+
}
|
|
75
|
+
if (typeof value.enabled !== "boolean") {
|
|
76
|
+
throw new Error(`windy.modules.json Module enabled 无效:${name}`);
|
|
77
|
+
}
|
|
78
|
+
if (!isPlainObject(value.admin) || typeof value.admin.visible !== "boolean") {
|
|
79
|
+
throw new Error(`windy.modules.json Module admin.visible 无效:${name}`);
|
|
80
|
+
}
|
|
81
|
+
if (!Number.isSafeInteger(value.order) || Number(value.order) < 0) {
|
|
82
|
+
throw new Error(`windy.modules.json Module order 无效:${name}`);
|
|
83
|
+
}
|
|
84
|
+
if (!isPlainObject(value.config)) {
|
|
85
|
+
throw new Error(`windy.modules.json Module config 无效:${name}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
name,
|
|
90
|
+
package: value.package,
|
|
91
|
+
enabled: value.enabled,
|
|
92
|
+
order: Number(value.order),
|
|
93
|
+
admin: { visible: value.admin.visible },
|
|
94
|
+
config: value.config,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function isValidNpmPackageName(value: string): boolean {
|
|
99
|
+
if (!value || value.length > 214 || value !== value.toLowerCase()) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
const parts = value.startsWith("@") ? value.slice(1).split("/") : [value];
|
|
103
|
+
if (parts.length !== (value.startsWith("@") ? 2 : 1)) return false;
|
|
104
|
+
return parts.every(isValidNpmPackageNamePart);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function isValidNpmPackageNamePart(value: string): boolean {
|
|
108
|
+
if (
|
|
109
|
+
!value ||
|
|
110
|
+
value === "node_modules" ||
|
|
111
|
+
value === "favicon.ico" ||
|
|
112
|
+
value.startsWith(".") ||
|
|
113
|
+
value.startsWith("_")
|
|
114
|
+
) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
return /^[a-z0-9!~*'()._-]+$/.test(value);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
121
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
const prototype = Object.getPrototypeOf(value) as unknown;
|
|
125
|
+
return prototype === Object.prototype || prototype === null;
|
|
126
|
+
}
|
|
@@ -15,7 +15,7 @@ export const systemAdminRoutes: ModuleManifest["adminRoutes"] = [
|
|
|
15
15
|
"用户体系",
|
|
16
16
|
"sidebar",
|
|
17
17
|
"system.user.read",
|
|
18
|
-
"system.
|
|
18
|
+
"system.user",
|
|
19
19
|
),
|
|
20
20
|
route(
|
|
21
21
|
"system.role",
|
|
@@ -23,7 +23,7 @@ export const systemAdminRoutes: ModuleManifest["adminRoutes"] = [
|
|
|
23
23
|
"权限策略",
|
|
24
24
|
"sidebar",
|
|
25
25
|
"system.role.read",
|
|
26
|
-
"system.
|
|
26
|
+
"system.role",
|
|
27
27
|
),
|
|
28
28
|
route(
|
|
29
29
|
"system.department",
|
|
@@ -31,7 +31,7 @@ export const systemAdminRoutes: ModuleManifest["adminRoutes"] = [
|
|
|
31
31
|
"部门组织",
|
|
32
32
|
"sidebar",
|
|
33
33
|
"system.department.read",
|
|
34
|
-
"system.
|
|
34
|
+
"system.department",
|
|
35
35
|
),
|
|
36
36
|
// @windy-module system.settings begin
|
|
37
37
|
{
|
|
@@ -52,7 +52,7 @@ export const systemAdminRoutes: ModuleManifest["adminRoutes"] = [
|
|
|
52
52
|
"菜单管理",
|
|
53
53
|
"sidebar",
|
|
54
54
|
"system.menu.read",
|
|
55
|
-
"system.
|
|
55
|
+
"system.menu",
|
|
56
56
|
),
|
|
57
57
|
route(
|
|
58
58
|
"system.dictionary",
|
|
@@ -60,7 +60,7 @@ export const systemAdminRoutes: ModuleManifest["adminRoutes"] = [
|
|
|
60
60
|
"字典管理",
|
|
61
61
|
"sidebar",
|
|
62
62
|
"system.dict.read",
|
|
63
|
-
"system.
|
|
63
|
+
"system.dict",
|
|
64
64
|
),
|
|
65
65
|
route(
|
|
66
66
|
"system.feature",
|
|
@@ -68,6 +68,7 @@ export const systemAdminRoutes: ModuleManifest["adminRoutes"] = [
|
|
|
68
68
|
"功能开关",
|
|
69
69
|
"sidebar",
|
|
70
70
|
"system.feature.read",
|
|
71
|
+
"system.feature",
|
|
71
72
|
),
|
|
72
73
|
// @windy-module system.notification begin
|
|
73
74
|
route(
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { AdminUiManifest } from "./admin-ui-manifest.js";
|
|
2
|
+
import { systemAdminRoutes } from "./system-admin-routes.js";
|
|
3
|
+
|
|
4
|
+
const definitions = [
|
|
5
|
+
["system", ["system.feature"]],
|
|
6
|
+
[
|
|
7
|
+
"system.identity",
|
|
8
|
+
[
|
|
9
|
+
"system.profile",
|
|
10
|
+
"system.user",
|
|
11
|
+
"system.role",
|
|
12
|
+
"system.department",
|
|
13
|
+
"system.menu",
|
|
14
|
+
"system.dictionary",
|
|
15
|
+
],
|
|
16
|
+
],
|
|
17
|
+
// @windy-module system.settings begin
|
|
18
|
+
["system.settings", ["system.settings"]],
|
|
19
|
+
// @windy-module system.settings end
|
|
20
|
+
// @windy-module system.configuration begin
|
|
21
|
+
["system.configuration", ["system.config"]],
|
|
22
|
+
// @windy-module system.configuration end
|
|
23
|
+
// @windy-module system.license begin
|
|
24
|
+
["system.license", ["system.license"]],
|
|
25
|
+
// @windy-module system.license end
|
|
26
|
+
// @windy-module system.audit begin
|
|
27
|
+
["system.audit", ["system.audit"]],
|
|
28
|
+
// @windy-module system.audit end
|
|
29
|
+
// @windy-module system.storage begin
|
|
30
|
+
["system.storage", []],
|
|
31
|
+
// @windy-module system.storage end
|
|
32
|
+
// @windy-module system.agent begin
|
|
33
|
+
["system.agent", []],
|
|
34
|
+
// @windy-module system.agent end
|
|
35
|
+
// @windy-module system.operations begin
|
|
36
|
+
["system.operations", ["system.ops"]],
|
|
37
|
+
// @windy-module system.operations end
|
|
38
|
+
// @windy-module system.scheduler begin
|
|
39
|
+
["system.scheduler", []],
|
|
40
|
+
// @windy-module system.scheduler end
|
|
41
|
+
// @windy-module system.notification begin
|
|
42
|
+
["system.notification", ["system.notification"]],
|
|
43
|
+
// @windy-module system.notification end
|
|
44
|
+
// @windy-module system.workflow begin
|
|
45
|
+
["system.workflow", []],
|
|
46
|
+
// @windy-module system.workflow end
|
|
47
|
+
// @windy-module system.bulk-data begin
|
|
48
|
+
["system.bulk-data", []],
|
|
49
|
+
// @windy-module system.bulk-data end
|
|
50
|
+
// @windy-module system.search begin
|
|
51
|
+
["system.search", []],
|
|
52
|
+
// @windy-module system.search end
|
|
53
|
+
// @windy-module system.data-governance begin
|
|
54
|
+
["system.data-governance", []],
|
|
55
|
+
// @windy-module system.data-governance end
|
|
56
|
+
] as const;
|
|
57
|
+
|
|
58
|
+
const routeByKey = new Map(
|
|
59
|
+
systemAdminRoutes.map((route) => [route.key, route] as const),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
export function systemAdminUiManifests(): AdminUiManifest[] {
|
|
63
|
+
return definitions.map(([name, routeKeys]) => ({
|
|
64
|
+
name,
|
|
65
|
+
version: "0.1.0",
|
|
66
|
+
adminRoutes: routeKeys.map((key) => {
|
|
67
|
+
const route = routeByKey.get(key);
|
|
68
|
+
if (!route) throw new Error(`系统 Admin Route 未定义:${key}`);
|
|
69
|
+
return { ...route };
|
|
70
|
+
}),
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
@@ -55,7 +55,7 @@ export const systemAgentTools: ModuleManifest["tools"] = [
|
|
|
55
55
|
label: "查询用户",
|
|
56
56
|
description: "分页查询平台用户账号。",
|
|
57
57
|
permissionKey: "system.user.read",
|
|
58
|
-
featureKey: "system.
|
|
58
|
+
featureKey: "system.user",
|
|
59
59
|
auditAction: "agent.tool.execute",
|
|
60
60
|
scopeMode: "request",
|
|
61
61
|
},
|
|
@@ -65,7 +65,7 @@ export const systemAgentTools: ModuleManifest["tools"] = [
|
|
|
65
65
|
label: "查询功能开关",
|
|
66
66
|
description: "分页查询平台功能开关状态。",
|
|
67
67
|
permissionKey: "system.feature.read",
|
|
68
|
-
featureKey: "system.
|
|
68
|
+
featureKey: "system.feature",
|
|
69
69
|
auditAction: "agent.tool.execute",
|
|
70
70
|
},
|
|
71
71
|
{
|