create-windy 0.3.18 → 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 +788 -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/agent-server/src/southwind_agent_server/adk_debug.py +38 -5
- package/template/apps/agent-server/tests/test_adk_debug_result_polling.py +139 -0
- 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/agent-runtime.md +2 -0
- 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
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type ModuleToolHandlers,
|
|
15
15
|
type PlatformToolExecutionContext,
|
|
16
16
|
} from "./tool-registry.js";
|
|
17
|
+
import type { StaticRouteAuthorizer } from "../module-host/static-route-authorization.js";
|
|
17
18
|
|
|
18
19
|
type ServerRuntime = ReturnType<typeof createServerRuntime>;
|
|
19
20
|
|
|
@@ -85,9 +86,16 @@ export function createSystemToolHandlers(
|
|
|
85
86
|
export function registerAgentRoutes(
|
|
86
87
|
app: RouteApp,
|
|
87
88
|
registry: GovernedToolRegistry,
|
|
89
|
+
authorizer: StaticRouteAuthorizer,
|
|
88
90
|
) {
|
|
89
91
|
app.get("/agent/tools", async (rawContext) => {
|
|
90
92
|
const { guardContext } = rawContext as RouteContext;
|
|
93
|
+
const denied = await authorizer.authorize(
|
|
94
|
+
guardContext,
|
|
95
|
+
"GET",
|
|
96
|
+
"/api/agent/tools",
|
|
97
|
+
);
|
|
98
|
+
if (denied) return denied;
|
|
91
99
|
return Promise.all(
|
|
92
100
|
registry.definitions().map(async (tool) => ({
|
|
93
101
|
key: tool.key,
|
|
@@ -103,6 +111,12 @@ export function registerAgentRoutes(
|
|
|
103
111
|
|
|
104
112
|
app.post("/agent/tools/:key/execute", async (rawContext) => {
|
|
105
113
|
const context = rawContext as RouteContext;
|
|
114
|
+
const denied = await authorizer.authorize(
|
|
115
|
+
context.guardContext,
|
|
116
|
+
"POST",
|
|
117
|
+
"/api/agent/tools/:key/execute",
|
|
118
|
+
);
|
|
119
|
+
if (denied) return denied;
|
|
106
120
|
try {
|
|
107
121
|
return await registry.execute(
|
|
108
122
|
context.params.key ?? "",
|
|
@@ -46,6 +46,11 @@ import { AgentBackgroundOperationExecutor } from "./background-operation.js";
|
|
|
46
46
|
import type { DeferredAgentBackgroundOperationPortFactory } from "./background-operation-port.js";
|
|
47
47
|
import { UnixSandboxToolClient } from "./sandbox-client.js";
|
|
48
48
|
import { createModuleAiOperationPort } from "../module-host/ai-operation-port.js";
|
|
49
|
+
import {
|
|
50
|
+
createManifestAuthorizedRouteApp,
|
|
51
|
+
createStaticRouteAuthorizer,
|
|
52
|
+
} from "../module-host/static-route-authorization.js";
|
|
53
|
+
import { registerUnavailableAiRoutes } from "./unavailable-routes.js";
|
|
49
54
|
|
|
50
55
|
export function registerAgentPlatformRuntime(input: {
|
|
51
56
|
app: RouteApp;
|
|
@@ -79,18 +84,29 @@ export function registerAgentPlatformRuntime(input: {
|
|
|
79
84
|
toolHandlers,
|
|
80
85
|
input.runtime,
|
|
81
86
|
);
|
|
82
|
-
|
|
87
|
+
const routeAuthorizer = createStaticRouteAuthorizer(
|
|
88
|
+
input.runtime,
|
|
89
|
+
input.modules,
|
|
90
|
+
);
|
|
91
|
+
registerAgentRoutes(input.app, toolRegistry, routeAuthorizer);
|
|
83
92
|
registerMcpRoutes(
|
|
84
93
|
input.app,
|
|
85
94
|
new PlatformMcpAdapter(toolRegistry, input.runtime, features),
|
|
86
95
|
);
|
|
87
96
|
if (!input.platformConfig.enableAI) {
|
|
97
|
+
registerUnavailableAiRoutes(
|
|
98
|
+
createManifestAuthorizedRouteApp(input.app, routeAuthorizer),
|
|
99
|
+
);
|
|
88
100
|
return {
|
|
89
101
|
toolHandlers,
|
|
90
102
|
aiArtifacts: undefined,
|
|
91
103
|
aiOperations: undefined,
|
|
92
104
|
};
|
|
93
105
|
}
|
|
106
|
+
const aiRouteApp = createManifestAuthorizedRouteApp(
|
|
107
|
+
input.app,
|
|
108
|
+
routeAuthorizer,
|
|
109
|
+
);
|
|
94
110
|
const internalToken = requiredEnvironment("AGENT_SERVER_INTERNAL_TOKEN");
|
|
95
111
|
const aiArtifacts = new AiArtifactRuntime(
|
|
96
112
|
new AiArtifactCodec(requiredEnvironment("AI_ARTIFACT_ENCRYPTION_SECRET")),
|
|
@@ -100,7 +116,7 @@ export function registerAgentPlatformRuntime(input: {
|
|
|
100
116
|
input.storage,
|
|
101
117
|
resolveAgentActor,
|
|
102
118
|
);
|
|
103
|
-
registerAiArtifactRoutes(
|
|
119
|
+
registerAiArtifactRoutes(aiRouteApp, aiArtifacts, internalToken);
|
|
104
120
|
const remote = new RemoteAgentRuntime({
|
|
105
121
|
baseUrl: process.env.AGENT_SERVER_URL || "http://agent-server:8080",
|
|
106
122
|
internalToken,
|
|
@@ -127,7 +143,7 @@ export function registerAgentPlatformRuntime(input: {
|
|
|
127
143
|
aiArtifacts,
|
|
128
144
|
grants,
|
|
129
145
|
);
|
|
130
|
-
registerDocumentRunRoutes(
|
|
146
|
+
registerDocumentRunRoutes(aiRouteApp, documentFacade, documentRemote);
|
|
131
147
|
const speechRemote = process.env.SPEECH_SERVER_URL?.trim()
|
|
132
148
|
? new RemoteSpeechRuntime({
|
|
133
149
|
baseUrl: process.env.SPEECH_SERVER_URL,
|
|
@@ -142,7 +158,7 @@ export function registerAgentPlatformRuntime(input: {
|
|
|
142
158
|
aiArtifacts,
|
|
143
159
|
grants,
|
|
144
160
|
);
|
|
145
|
-
registerSpeechRunRoutes(
|
|
161
|
+
registerSpeechRunRoutes(aiRouteApp, speechFacade, speechRemote);
|
|
146
162
|
const embeddingRemote = process.env.EMBEDDING_SERVER_URL?.trim()
|
|
147
163
|
? new RemoteEmbeddingRuntime({
|
|
148
164
|
baseUrl: process.env.EMBEDDING_SERVER_URL,
|
|
@@ -156,7 +172,7 @@ export function registerAgentPlatformRuntime(input: {
|
|
|
156
172
|
embeddingRemote,
|
|
157
173
|
aiArtifacts,
|
|
158
174
|
);
|
|
159
|
-
registerEmbeddingRoutes(
|
|
175
|
+
registerEmbeddingRoutes(aiRouteApp, embeddingFacade);
|
|
160
176
|
const agentFacade = new AgentRunFacade(
|
|
161
177
|
input.modules,
|
|
162
178
|
input.hosts,
|
|
@@ -166,7 +182,7 @@ export function registerAgentPlatformRuntime(input: {
|
|
|
166
182
|
aiArtifacts,
|
|
167
183
|
);
|
|
168
184
|
registerAgentRunRoutes(
|
|
169
|
-
|
|
185
|
+
aiRouteApp,
|
|
170
186
|
agentFacade,
|
|
171
187
|
remote,
|
|
172
188
|
new RestrictedAgentToolHost(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { installedBusinessModules } from "../installed-business-modules.js";
|
|
2
1
|
import { SandboxToolRegistry } from "./sandbox-registry.js";
|
|
3
2
|
import { runSandboxWorkerUntilSignal } from "./sandbox-worker-server.js";
|
|
3
|
+
import { runtimeBusinessModules } from "../module-host/runtime-composition.js";
|
|
4
4
|
|
|
5
5
|
const socketPath =
|
|
6
6
|
process.env.TOOL_SANDBOX_SOCKET?.trim() ||
|
|
@@ -8,5 +8,5 @@ const socketPath =
|
|
|
8
8
|
|
|
9
9
|
await runSandboxWorkerUntilSignal(
|
|
10
10
|
socketPath,
|
|
11
|
-
new SandboxToolRegistry(
|
|
11
|
+
new SandboxToolRegistry(runtimeBusinessModules),
|
|
12
12
|
);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { RouteApp } from "../system/route-types.js";
|
|
2
|
+
|
|
3
|
+
const unavailableRoutes = [
|
|
4
|
+
["GET", "/internal/ai/artifacts"],
|
|
5
|
+
["POST", "/document/runs"],
|
|
6
|
+
["GET", "/document/runs/:runId/events"],
|
|
7
|
+
["GET", "/document/runs/:runId/result"],
|
|
8
|
+
["POST", "/document/runs/:runId/cancel"],
|
|
9
|
+
["POST", "/speech/runs"],
|
|
10
|
+
["GET", "/speech/runs/:runId/events"],
|
|
11
|
+
["GET", "/speech/runs/:runId/result"],
|
|
12
|
+
["GET", "/speech/runs/:runId/artifact"],
|
|
13
|
+
["POST", "/speech/runs/:runId/cancel"],
|
|
14
|
+
["POST", "/embeddings"],
|
|
15
|
+
["POST", "/agent/runs"],
|
|
16
|
+
["GET", "/agent/runs/:runId/events"],
|
|
17
|
+
["GET", "/agent/runs/:runId/result"],
|
|
18
|
+
["POST", "/agent/runs/:runId/cancel"],
|
|
19
|
+
["POST", "/agent/runs/:runId/attach"],
|
|
20
|
+
["POST", "/internal/agent/tools/execute"],
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
23
|
+
export function registerUnavailableAiRoutes(app: RouteApp): void {
|
|
24
|
+
const unavailable = () =>
|
|
25
|
+
Response.json(
|
|
26
|
+
{ code: "RUNTIME_UNAVAILABLE", message: "AI Runtime 未启用" },
|
|
27
|
+
{ status: 503 },
|
|
28
|
+
);
|
|
29
|
+
for (const [method, path] of unavailableRoutes) {
|
|
30
|
+
if (method === "GET") app.get(path, unavailable);
|
|
31
|
+
else app.post(path, unavailable);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import moduleAssembly from "../../../../windy.modules.json";
|
|
2
|
+
import {
|
|
3
|
+
defineWindyServerComposition,
|
|
4
|
+
type WindyServerModuleAdapter,
|
|
5
|
+
type WindyServerModuleAttachment,
|
|
6
|
+
} from "@southwind-ai/server-sdk";
|
|
7
|
+
import type { ModuleManifest } from "@southwind-ai/modules";
|
|
8
|
+
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
9
|
+
import type { ManagedDepartment } from "../system/entities.js";
|
|
10
|
+
import type { EntityRepository } from "../system/repository.js";
|
|
11
|
+
// @windy-module work-order begin
|
|
12
|
+
import { createWorkOrderModule } from "../example-modules.js";
|
|
13
|
+
// @windy-module work-order end
|
|
14
|
+
|
|
15
|
+
export interface ProjectBusinessModuleRuntime {
|
|
16
|
+
readonly db?: NodePgDatabase;
|
|
17
|
+
readonly departments: EntityRepository<ManagedDepartment>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// @windy-module work-order begin
|
|
21
|
+
const workOrderModule = createWorkOrderModule();
|
|
22
|
+
// @windy-module work-order end
|
|
23
|
+
|
|
24
|
+
const projectAdapters: readonly WindyServerModuleAdapter<ModuleManifest>[] = [
|
|
25
|
+
// @windy-module work-order begin
|
|
26
|
+
{
|
|
27
|
+
name: "work-order",
|
|
28
|
+
hostRange: "^0.1.0",
|
|
29
|
+
runtimeMode: "project-attachment",
|
|
30
|
+
create: () => workOrderModule,
|
|
31
|
+
},
|
|
32
|
+
// @windy-module work-order end
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const projectAttachments: readonly WindyServerModuleAttachment<ProjectBusinessModuleRuntime>[] =
|
|
36
|
+
[
|
|
37
|
+
// @windy-module work-order begin
|
|
38
|
+
{
|
|
39
|
+
name: "work-order",
|
|
40
|
+
attach: (runtime) => workOrderModule.attach(runtime),
|
|
41
|
+
},
|
|
42
|
+
// @windy-module work-order end
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 客户拥有的 Server 组合声明。
|
|
47
|
+
*
|
|
48
|
+
* 标准 package Adapter 由 Host 动态加载;只有需要客户 Repository 的本地 Adapter
|
|
49
|
+
* 才在这里覆盖,并通过显式 attachment 获得项目运行时。动态 package 永远不会收到
|
|
50
|
+
* 数据库或平台内部 Repository。
|
|
51
|
+
*/
|
|
52
|
+
export const serverCompositionDeclaration = defineWindyServerComposition({
|
|
53
|
+
assembly: moduleAssembly,
|
|
54
|
+
adapters: projectAdapters,
|
|
55
|
+
attachments: projectAttachments,
|
|
56
|
+
});
|
|
@@ -219,6 +219,8 @@ export async function createApplicationServices(
|
|
|
219
219
|
: new InMemorySchedulerRepository(),
|
|
220
220
|
taskDefinitions,
|
|
221
221
|
runtime: input.runtime,
|
|
222
|
+
resolveActor: (actorId) =>
|
|
223
|
+
resolveCurrentSystemActor(input.systemRepositories, actorId),
|
|
222
224
|
// @windy-module system.workflow begin
|
|
223
225
|
workflow,
|
|
224
226
|
// @windy-module system.workflow end
|
|
@@ -28,6 +28,10 @@ import type {
|
|
|
28
28
|
} from "./types.js";
|
|
29
29
|
export * from "./input-validation.js";
|
|
30
30
|
|
|
31
|
+
export type BulkResumeAuthorization = (
|
|
32
|
+
job: BulkDataJob,
|
|
33
|
+
) => Promise<string | undefined>;
|
|
34
|
+
|
|
31
35
|
export class BulkDataService {
|
|
32
36
|
private readonly handlers = new Map<string, BulkResourceHandler>();
|
|
33
37
|
private readonly limits: BulkDataLimits;
|
|
@@ -40,6 +44,7 @@ export class BulkDataService {
|
|
|
40
44
|
private readonly now: () => Date = () => new Date(),
|
|
41
45
|
blobStore?: BlobStore,
|
|
42
46
|
private readonly durable?: LegacyDurableAdapter,
|
|
47
|
+
private readonly authorizeResume?: BulkResumeAuthorization,
|
|
43
48
|
) {
|
|
44
49
|
this.limits = { ...defaultBulkDataLimits, ...limits };
|
|
45
50
|
this.artifacts = new BulkArtifactStore(repository, blobStore);
|
|
@@ -155,6 +160,15 @@ export class BulkDataService {
|
|
|
155
160
|
if (!job || job.status !== "running" || job.attempt !== generation) {
|
|
156
161
|
return;
|
|
157
162
|
}
|
|
163
|
+
const denial = this.authorizeResume
|
|
164
|
+
? await this.authorizeResume(job)
|
|
165
|
+
: this.durable
|
|
166
|
+
? "resume-authorization-unavailable"
|
|
167
|
+
: undefined;
|
|
168
|
+
if (denial) {
|
|
169
|
+
await this.repository.retryOrFail(job.id, denial, this.now());
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
158
172
|
await this.processClaimed(job);
|
|
159
173
|
}
|
|
160
174
|
|
|
@@ -77,7 +77,7 @@ export class SystemGovernedExportAuthorization implements GovernedExportAuthoriz
|
|
|
77
77
|
key: request.policyKey,
|
|
78
78
|
resourceType: request.resourceType,
|
|
79
79
|
version: request.policyVersion,
|
|
80
|
-
featureKey: featureKey("system.
|
|
80
|
+
featureKey: featureKey("system.user"),
|
|
81
81
|
exportPermissionKey: "system.user.export",
|
|
82
82
|
}
|
|
83
83
|
: undefined;
|
|
@@ -14,7 +14,11 @@ import { DrizzleDurableJobRepository } from "../../jobs/drizzle-repository.js";
|
|
|
14
14
|
import { DurableJobService } from "../../jobs/service.js";
|
|
15
15
|
import { DurableJobWorker } from "../../jobs/worker.js";
|
|
16
16
|
import { createPlatformObservability } from "../../observability/bootstrap.js";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
createServerRuntime,
|
|
19
|
+
type AuditEventWriter,
|
|
20
|
+
type ServerRuntimeOptions,
|
|
21
|
+
} from "../../runtime.js";
|
|
18
22
|
import { identityRouteConfigs } from "../../system/identity-route-config.js";
|
|
19
23
|
import { registerSystemRoutes } from "../../system/routes.js";
|
|
20
24
|
import { createSystemRepositories } from "../../system/seed.js";
|
|
@@ -67,7 +71,7 @@ export async function createPostgresApiFixture(
|
|
|
67
71
|
const actors = governanceActors();
|
|
68
72
|
const actorResolver = bearerActorResolver(actors);
|
|
69
73
|
const logs: Array<Record<string, unknown>> = [];
|
|
70
|
-
const runtime =
|
|
74
|
+
const runtime = createGovernanceRuntime({ actorResolver, auditWriter });
|
|
71
75
|
const stack = createGovernanceStack({
|
|
72
76
|
db,
|
|
73
77
|
runtime,
|
|
@@ -94,6 +98,18 @@ export async function createPostgresApiFixture(
|
|
|
94
98
|
};
|
|
95
99
|
}
|
|
96
100
|
|
|
101
|
+
export function createGovernanceRuntime(
|
|
102
|
+
options: Pick<ServerRuntimeOptions, "actorResolver" | "auditWriter">,
|
|
103
|
+
) {
|
|
104
|
+
return createServerRuntime(
|
|
105
|
+
{},
|
|
106
|
+
{
|
|
107
|
+
...options,
|
|
108
|
+
featureCatalog: createFoundationSnapshot().features,
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
97
113
|
export function createGovernanceStack(input: {
|
|
98
114
|
db: NodePgDatabase;
|
|
99
115
|
runtime: ReturnType<typeof createServerRuntime>;
|
package/template/apps/server/src/data-governance/export/postgres-recovery.integration.test.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Pool } from "pg";
|
|
|
5
5
|
import { FileAuditRecoveryQueue } from "../../audit/recovery-queue.js";
|
|
6
6
|
import { ReliableAuditWriter } from "../../audit/reliable-writer.js";
|
|
7
7
|
import { DrizzleAuditLogWriter } from "../../audit/drizzle-log-store.js";
|
|
8
|
-
import {
|
|
8
|
+
import type { AuditEventWriter } from "../../runtime.js";
|
|
9
9
|
import {
|
|
10
10
|
assertNoCanaries,
|
|
11
11
|
createExport,
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import {
|
|
18
18
|
apiRequest,
|
|
19
19
|
approveExport,
|
|
20
|
+
createGovernanceRuntime,
|
|
20
21
|
createGovernanceStack,
|
|
21
22
|
createPostgresApiFixture,
|
|
22
23
|
ownerCanaries,
|
|
@@ -249,10 +250,10 @@ async function exerciseRecoveryQueue(
|
|
|
249
250
|
join(fixture.storageRoot, "audit-recovery.json"),
|
|
250
251
|
);
|
|
251
252
|
const reliable = reliableWriter(primary, queue);
|
|
252
|
-
const runtime =
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
);
|
|
253
|
+
const runtime = createGovernanceRuntime({
|
|
254
|
+
actorResolver: fixture.actorResolver,
|
|
255
|
+
auditWriter: reliable,
|
|
256
|
+
});
|
|
256
257
|
const stack = createGovernanceStack({
|
|
257
258
|
db: fixture.db,
|
|
258
259
|
runtime,
|
|
@@ -294,10 +295,10 @@ async function exerciseDoubleFault(
|
|
|
294
295
|
},
|
|
295
296
|
},
|
|
296
297
|
);
|
|
297
|
-
const runtime =
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
);
|
|
298
|
+
const runtime = createGovernanceRuntime({
|
|
299
|
+
actorResolver: fixture.actorResolver,
|
|
300
|
+
auditWriter: reliable,
|
|
301
|
+
});
|
|
301
302
|
const stack = createGovernanceStack({
|
|
302
303
|
db: fixture.db,
|
|
303
304
|
runtime,
|
|
@@ -11,7 +11,8 @@ import { DurableArtifactService } from "../../jobs/artifact-service.js";
|
|
|
11
11
|
import { InMemoryDurableExecutionRepository } from "../../jobs/in-memory-execution.js";
|
|
12
12
|
import { DurableJobService } from "../../jobs/service.js";
|
|
13
13
|
import { DurableJobWorker } from "../../jobs/worker.js";
|
|
14
|
-
import {
|
|
14
|
+
import type { AuditEventWriter } from "../../runtime.js";
|
|
15
|
+
import { createRouteTestRuntime } from "../../system/route-test-runtime.js";
|
|
15
16
|
import { FakeRouteApp } from "../../system/route-test-app.js";
|
|
16
17
|
import type { GovernedExportApprovalPort } from "./approval-port.js";
|
|
17
18
|
import { GovernedExportJobRuntime } from "./job-runtime.js";
|
|
@@ -34,7 +35,9 @@ export async function createFixture(
|
|
|
34
35
|
await auditWriter?.write(event, policy);
|
|
35
36
|
},
|
|
36
37
|
};
|
|
37
|
-
const
|
|
38
|
+
const foundation = createFoundationSnapshot();
|
|
39
|
+
const runtime = createRouteTestRuntime(
|
|
40
|
+
foundation,
|
|
38
41
|
{ WINDY_DEV_ADMIN_TOKEN: "token" },
|
|
39
42
|
{ auditWriter: writer },
|
|
40
43
|
);
|
|
@@ -86,12 +89,7 @@ export async function createFixture(
|
|
|
86
89
|
{ approvalRequired: true, retentionSeconds: 3600 },
|
|
87
90
|
);
|
|
88
91
|
const app = new FakeRouteApp();
|
|
89
|
-
registerGovernedUserExportRoutes(
|
|
90
|
-
app,
|
|
91
|
-
service,
|
|
92
|
-
runtime,
|
|
93
|
-
createFoundationSnapshot().features,
|
|
94
|
-
);
|
|
92
|
+
registerGovernedUserExportRoutes(app, service, runtime, foundation.features);
|
|
95
93
|
return {
|
|
96
94
|
app,
|
|
97
95
|
approval,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import { createFoundationSnapshot } from "../foundation.js";
|
|
3
|
-
import {
|
|
3
|
+
import type { AuditEventWriter } from "../runtime.js";
|
|
4
|
+
import { createRouteTestRuntime } from "../system/route-test-runtime.js";
|
|
4
5
|
import { FakeRouteApp } from "../system/route-test-app.js";
|
|
5
6
|
import { registerSystemRoutes } from "../system/routes.js";
|
|
6
7
|
import { createSystemRepositories } from "../system/seed.js";
|
|
@@ -249,7 +250,8 @@ describe("用户敏感字段 API", () => {
|
|
|
249
250
|
async function fixture(auditWriter?: AuditEventWriter) {
|
|
250
251
|
const snapshot = createFoundationSnapshot();
|
|
251
252
|
const repositories = createSystemRepositories(snapshot);
|
|
252
|
-
const runtime =
|
|
253
|
+
const runtime = createRouteTestRuntime(
|
|
254
|
+
snapshot,
|
|
253
255
|
{ WINDY_DEV_ADMIN_TOKEN: "token" },
|
|
254
256
|
{ auditWriter },
|
|
255
257
|
);
|
|
@@ -95,6 +95,26 @@ describe("Feature Flag 管理授权", () => {
|
|
|
95
95
|
expect(item).toMatchObject({ key: "system.rbac", enabled: true });
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
+
test("Feature 管理核心能力不能关闭或隐藏", async () => {
|
|
99
|
+
const fixture = await createFixture(actor(["developer"], true));
|
|
100
|
+
|
|
101
|
+
for (const body of [
|
|
102
|
+
{ enabled: false },
|
|
103
|
+
{ visible: "hidden" },
|
|
104
|
+
{ licenseVersionOverride: ["advanced"] },
|
|
105
|
+
]) {
|
|
106
|
+
const response = (await updateFeature(
|
|
107
|
+
fixture,
|
|
108
|
+
body,
|
|
109
|
+
"system.feature",
|
|
110
|
+
)) as Response;
|
|
111
|
+
expect(response.status).toBe(409);
|
|
112
|
+
await expect(response.json()).resolves.toMatchObject({
|
|
113
|
+
error: "FEATURE_CONTROL_IMMUTABLE",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
98
118
|
test("Feature Flag 更新明确使用关键审计策略", async () => {
|
|
99
119
|
const policies: string[] = [];
|
|
100
120
|
const fixture = await createFixture(actor(["developer"], true), {
|
|
@@ -166,7 +186,12 @@ async function createFixture(
|
|
|
166
186
|
},
|
|
167
187
|
);
|
|
168
188
|
const app = new FakeRouteApp();
|
|
169
|
-
registerFeatureFlagRoutes(
|
|
189
|
+
registerFeatureFlagRoutes(
|
|
190
|
+
app,
|
|
191
|
+
repositories.features,
|
|
192
|
+
runtime,
|
|
193
|
+
snapshot.features.find(({ key }) => key === "system.feature")!,
|
|
194
|
+
);
|
|
170
195
|
const guardContext = await runtime.createGuardContext(
|
|
171
196
|
new Request("http://localhost/api/system/features/system.profile"),
|
|
172
197
|
);
|
|
@@ -25,21 +25,16 @@ export function registerFeatureFlagRoutes(
|
|
|
25
25
|
app: RouteApp,
|
|
26
26
|
repository: ManifestFeatureRepository,
|
|
27
27
|
runtime: ServerRuntime,
|
|
28
|
+
feature: FeatureFlagDefinition,
|
|
28
29
|
) {
|
|
29
30
|
app.get("/system/features", async (rawContext) => {
|
|
30
31
|
const { guardContext, query } = routeContext(rawContext);
|
|
31
|
-
return withFeaturePermission(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const listQuery = parseListQuery(query);
|
|
38
|
-
return listQuery instanceof Response
|
|
39
|
-
? listQuery
|
|
40
|
-
: repository.list(listQuery, ["key", "label", "module"]);
|
|
41
|
-
},
|
|
42
|
-
);
|
|
32
|
+
return withFeaturePermission(runtime, guardContext, "read", feature, () => {
|
|
33
|
+
const listQuery = parseListQuery(query);
|
|
34
|
+
return listQuery instanceof Response
|
|
35
|
+
? listQuery
|
|
36
|
+
: repository.list(listQuery, ["key", "label", "module"]);
|
|
37
|
+
});
|
|
43
38
|
});
|
|
44
39
|
|
|
45
40
|
app.get("/system/features/export", async (rawContext) => {
|
|
@@ -48,7 +43,7 @@ export function registerFeatureFlagRoutes(
|
|
|
48
43
|
runtime,
|
|
49
44
|
guardContext,
|
|
50
45
|
"read",
|
|
51
|
-
|
|
46
|
+
feature,
|
|
52
47
|
async () => {
|
|
53
48
|
const listQuery = parseListQuery(query);
|
|
54
49
|
if (listQuery instanceof Response) return listQuery;
|
|
@@ -78,7 +73,7 @@ export function registerFeatureFlagRoutes(
|
|
|
78
73
|
runtime,
|
|
79
74
|
guardContext,
|
|
80
75
|
"read",
|
|
81
|
-
|
|
76
|
+
feature,
|
|
82
77
|
async () => {
|
|
83
78
|
const id = requiredId(params);
|
|
84
79
|
return (await repository.get(id)) || notFound("features", id);
|
|
@@ -92,11 +87,21 @@ export function registerFeatureFlagRoutes(
|
|
|
92
87
|
runtime,
|
|
93
88
|
guardContext,
|
|
94
89
|
"manage",
|
|
95
|
-
|
|
90
|
+
feature,
|
|
96
91
|
async () => {
|
|
97
92
|
const id = requiredId(params);
|
|
98
93
|
const input = readStatePatch(body);
|
|
99
94
|
if (input instanceof Response) return input;
|
|
95
|
+
if (id === feature.key && wouldDisableFeatureControl(input)) {
|
|
96
|
+
return Response.json(
|
|
97
|
+
{
|
|
98
|
+
error: "FEATURE_CONTROL_IMMUTABLE",
|
|
99
|
+
message:
|
|
100
|
+
"功能开关管理是核心恢复能力,不能关闭、隐藏或限制 License。",
|
|
101
|
+
},
|
|
102
|
+
{ status: 409 },
|
|
103
|
+
);
|
|
104
|
+
}
|
|
100
105
|
const item = await repository.updateState(
|
|
101
106
|
id,
|
|
102
107
|
input,
|
|
@@ -156,6 +161,19 @@ function readStatePatch(
|
|
|
156
161
|
};
|
|
157
162
|
}
|
|
158
163
|
|
|
164
|
+
function wouldDisableFeatureControl(
|
|
165
|
+
input: Partial<
|
|
166
|
+
Pick<ManagedFeature, "enabled" | "visible" | "licenseVersionOverride">
|
|
167
|
+
>,
|
|
168
|
+
): boolean {
|
|
169
|
+
return (
|
|
170
|
+
input.enabled === false ||
|
|
171
|
+
input.visible === "hidden" ||
|
|
172
|
+
(input.licenseVersionOverride !== undefined &&
|
|
173
|
+
input.licenseVersionOverride !== null)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
159
177
|
function readLicenseVersionOverride(
|
|
160
178
|
record: Record<string, unknown>,
|
|
161
179
|
): LicenseVersionKey[] | null | undefined | Response {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
// @windy-module system.settings begin
|
|
4
|
+
composeModuleMenus,
|
|
5
|
+
validateModuleComposition,
|
|
6
|
+
// @windy-module system.settings end
|
|
7
|
+
systemModuleManifests,
|
|
8
|
+
} from "@southwind-ai/modules";
|
|
9
|
+
import { applyPlatformModuleActivation } from "./foundation.js";
|
|
10
|
+
|
|
11
|
+
describe("平台 Module 运行时启停", () => {
|
|
12
|
+
// @windy-module system.operations begin
|
|
13
|
+
test("停用只关闭能力与入口,保留静态实现声明用于一致性校验", () => {
|
|
14
|
+
const source = systemModuleManifests().find(
|
|
15
|
+
({ name }) => name === "system.operations",
|
|
16
|
+
)!;
|
|
17
|
+
const disabled = applyPlatformModuleActivation(source, false);
|
|
18
|
+
|
|
19
|
+
expect(disabled.features.every(({ enabled }) => !enabled)).toBe(true);
|
|
20
|
+
expect(disabled.features.every(({ visible }) => visible === "hidden")).toBe(
|
|
21
|
+
true,
|
|
22
|
+
);
|
|
23
|
+
expect(
|
|
24
|
+
disabled.menus
|
|
25
|
+
.flatMap(({ children }) => children ?? [])
|
|
26
|
+
.every(({ visible }) => !visible),
|
|
27
|
+
).toBe(true);
|
|
28
|
+
expect(
|
|
29
|
+
disabled.menus
|
|
30
|
+
.filter(({ type }) => type === "catalog")
|
|
31
|
+
.every(({ visible }) => visible),
|
|
32
|
+
).toBe(true);
|
|
33
|
+
expect(disabled.apiPermissions).toEqual(source.apiPermissions);
|
|
34
|
+
expect(disabled.adminRoutes).toEqual(source.adminRoutes);
|
|
35
|
+
});
|
|
36
|
+
// @windy-module system.operations end
|
|
37
|
+
|
|
38
|
+
// @windy-module system.settings begin
|
|
39
|
+
test("停用一个平台 Module 不改变共享菜单目录壳", () => {
|
|
40
|
+
const modules = systemModuleManifests().map((manifest) =>
|
|
41
|
+
applyPlatformModuleActivation(
|
|
42
|
+
manifest,
|
|
43
|
+
manifest.name !== "system.settings",
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(() => validateModuleComposition(modules)).not.toThrow();
|
|
48
|
+
expect(
|
|
49
|
+
composeModuleMenus(modules)
|
|
50
|
+
.find(({ key }) => key === "system.configuration")
|
|
51
|
+
?.children?.find(({ key }) => key === "system.settings")?.visible,
|
|
52
|
+
).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
// @windy-module system.settings end
|
|
55
|
+
|
|
56
|
+
test("启用时保留原始 Manifest 身份", () => {
|
|
57
|
+
const source = systemModuleManifests()[0]!;
|
|
58
|
+
expect(applyPlatformModuleActivation(source, true)).toBe(source);
|
|
59
|
+
});
|
|
60
|
+
});
|