create-windy 0.3.19 → 0.4.1

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.
Files changed (234) hide show
  1. package/README.md +28 -13
  2. package/dist/cli.js +788 -280
  3. package/package.json +1 -1
  4. package/template/.agents/skills/windy-business-module/SKILL.md +34 -10
  5. package/template/.windy-template.json +2 -2
  6. package/template/AGENTS.md +18 -8
  7. package/template/README.md +32 -22
  8. package/template/apps/server/Dockerfile +1 -0
  9. package/template/apps/server/package.json +1 -0
  10. package/template/apps/server/scripts/validate-module-composition-once.ts +4 -0
  11. package/template/apps/server/scripts/validate-module-composition.ts +28 -0
  12. package/template/apps/server/src/agent/data-scope-routes.test.ts +20 -5
  13. package/template/apps/server/src/agent/routes.test.ts +20 -7
  14. package/template/apps/server/src/agent/routes.ts +14 -0
  15. package/template/apps/server/src/agent/runtime-bootstrap.ts +22 -6
  16. package/template/apps/server/src/agent/sandbox-worker.ts +2 -2
  17. package/template/apps/server/src/agent/unavailable-routes.ts +33 -0
  18. package/template/apps/server/src/app/server-composition.ts +56 -0
  19. package/template/apps/server/src/application-services.ts +2 -0
  20. package/template/apps/server/src/bulk-data/service.ts +14 -0
  21. package/template/apps/server/src/data-governance/export/authorization-port.ts +1 -1
  22. package/template/apps/server/src/data-governance/export/postgres-api-fixture.ts +18 -2
  23. package/template/apps/server/src/data-governance/export/postgres-recovery.integration.test.ts +10 -9
  24. package/template/apps/server/src/data-governance/export/routes-test-fixture.ts +6 -8
  25. package/template/apps/server/src/data-governance/user-plaintext-routes.test.ts +4 -2
  26. package/template/apps/server/src/feature/routes.authorization.test.ts +26 -1
  27. package/template/apps/server/src/feature/routes.ts +33 -15
  28. package/template/apps/server/src/foundation-activation.test.ts +60 -0
  29. package/template/apps/server/src/foundation.ts +64 -5
  30. package/template/apps/server/src/guards.test.ts +23 -0
  31. package/template/apps/server/src/guards.ts +12 -0
  32. package/template/apps/server/src/index.ts +28 -34
  33. package/template/apps/server/src/installed-business-modules.ts +13 -24
  34. package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +1 -0
  35. package/template/apps/server/src/jobs/legacy-adapter.test.ts +106 -0
  36. package/template/apps/server/src/jobs/legacy-service-runtime.ts +40 -0
  37. package/template/apps/server/src/license/activation.test.ts +30 -0
  38. package/template/apps/server/src/license/activation.ts +26 -0
  39. package/template/apps/server/src/license/restriction.test.ts +16 -0
  40. package/template/apps/server/src/license/routes-offline-code.test.ts +6 -2
  41. package/template/apps/server/src/license/routes.ts +20 -4
  42. package/template/apps/server/src/license/runtime-service.test.ts +10 -2
  43. package/template/apps/server/src/module-bootstrap.ts +14 -23
  44. package/template/apps/server/src/module-composition.test.ts +43 -2
  45. package/template/apps/server/src/module-composition.ts +29 -0
  46. package/template/apps/server/src/module-host/durable-context-guard.test.ts +172 -0
  47. package/template/apps/server/src/module-host/durable-context-unavailable.test.ts +50 -1
  48. package/template/apps/server/src/module-host/durable-context.test.ts +42 -25
  49. package/template/apps/server/src/module-host/durable-context.ts +42 -2
  50. package/template/apps/server/src/module-host/durable-handler-registration.ts +96 -5
  51. package/template/apps/server/src/module-host/implementation-completeness.test.ts +185 -0
  52. package/template/apps/server/src/module-host/implementation-completeness.ts +132 -0
  53. package/template/apps/server/src/module-host/initialize-contracts.ts +14 -0
  54. package/template/apps/server/src/module-host/initialize.test.ts +78 -2
  55. package/template/apps/server/src/module-host/initialize.ts +15 -4
  56. package/template/apps/server/src/module-host/project-attachments.test.ts +92 -0
  57. package/template/apps/server/src/module-host/project-attachments.ts +58 -0
  58. package/template/apps/server/src/module-host/project-composition-package.test.ts +186 -0
  59. package/template/apps/server/src/module-host/project-composition.test.ts +260 -0
  60. package/template/apps/server/src/module-host/project-composition.ts +311 -0
  61. package/template/apps/server/src/module-host/request-context.ts +3 -0
  62. package/template/apps/server/src/module-host/route-declaration.ts +15 -6
  63. package/template/apps/server/src/module-host/route-installer.test.ts +15 -2
  64. package/template/apps/server/src/module-host/route-installer.ts +12 -2
  65. package/template/apps/server/src/module-host/route-registration.test.ts +13 -4
  66. package/template/apps/server/src/module-host/runtime-composition-compatibility.test.ts +63 -0
  67. package/template/apps/server/src/module-host/runtime-composition-compatibility.ts +46 -0
  68. package/template/apps/server/src/module-host/runtime-composition.ts +34 -0
  69. package/template/apps/server/src/module-host/search-provider-registration.test.ts +64 -0
  70. package/template/apps/server/src/module-host/search-provider-registration.ts +35 -0
  71. package/template/apps/server/src/module-host/static-route-authorization.test.ts +99 -0
  72. package/template/apps/server/src/module-host/static-route-authorization.ts +90 -0
  73. package/template/apps/server/src/module-host/task-definitions.ts +1 -0
  74. package/template/apps/server/src/module-host/user-directory-composition.test.ts +4 -1
  75. package/template/apps/server/src/module-host.test.ts +2 -0
  76. package/template/apps/server/src/module-preflight.test.ts +289 -0
  77. package/template/apps/server/src/module-preflight.ts +96 -0
  78. package/template/apps/server/src/module-runtime-validation.ts +1 -0
  79. package/template/apps/server/src/platform-app-bootstrap.ts +50 -0
  80. package/template/apps/server/src/platform-http-runtime.ts +32 -7
  81. package/template/apps/server/src/route-guards.test.ts +22 -0
  82. package/template/apps/server/src/route-guards.ts +10 -3
  83. package/template/apps/server/src/runtime-audit-query.ts +50 -0
  84. package/template/apps/server/src/runtime-feature.test.ts +46 -0
  85. package/template/apps/server/src/runtime-feature.ts +5 -0
  86. package/template/apps/server/src/runtime.ts +10 -49
  87. package/template/apps/server/src/scheduler/audit-action.test.ts +49 -0
  88. package/template/apps/server/src/scheduler/audit.ts +1 -1
  89. package/template/apps/server/src/scheduler/execution-recorder.ts +10 -5
  90. package/template/apps/server/src/scheduler/recovery-service.test.ts +4 -3
  91. package/template/apps/server/src/scheduler/service-idle.test.ts +4 -3
  92. package/template/apps/server/src/scheduler/service.test.ts +1 -0
  93. package/template/apps/server/src/scheduler/service.ts +18 -21
  94. package/template/apps/server/src/scheduler/types.ts +5 -1
  95. package/template/apps/server/src/settings/drizzle-watermark-repository.integration.test.ts +4 -0
  96. package/template/apps/server/src/settings/drizzle-watermark-repository.ts +11 -3
  97. package/template/apps/server/src/settings/watermark-routes.test.ts +54 -1
  98. package/template/apps/server/src/settings/watermark-routes.ts +29 -1
  99. package/template/apps/server/src/startup-composition-validation.test.ts +35 -0
  100. package/template/apps/server/src/startup-composition-validation.ts +54 -0
  101. package/template/apps/server/src/startup-preflight.test.ts +36 -0
  102. package/template/apps/server/src/startup-preflight.ts +37 -0
  103. package/template/apps/server/src/system/audit-routes.test.ts +9 -3
  104. package/template/apps/server/src/system/department-routes.test.ts +5 -3
  105. package/template/apps/server/src/system/feature-routes.test.ts +4 -3
  106. package/template/apps/server/src/system/interface-catalog.test.ts +5 -3
  107. package/template/apps/server/src/system/interface-catalog.ts +3 -0
  108. package/template/apps/server/src/system/menu-routes.test.ts +5 -3
  109. package/template/apps/server/src/system/route-test-runtime.ts +24 -0
  110. package/template/apps/server/src/system/routes-validation.test.ts +7 -3
  111. package/template/apps/server/src/system/routes.test.ts +21 -11
  112. package/template/apps/server/src/system/routes.ts +11 -2
  113. package/template/apps/server/src/work-order/foundation-modules.test.ts +6 -4
  114. package/template/apps/server/src/work-order/routes.test.ts +8 -0
  115. package/template/apps/server/src/work-order/task.test.ts +6 -1
  116. package/template/apps/web/Dockerfile +5 -0
  117. package/template/apps/web/package.json +1 -0
  118. package/template/apps/web/scripts/validate-admin-ui-composition.ts +106 -0
  119. package/template/apps/web/src/app/web-composition.ts +24 -0
  120. package/template/apps/web/src/app/web-composition.webtest.ts +15 -0
  121. package/template/apps/web/src/composables/useWatermarkSettings.webtest.ts +14 -0
  122. package/template/apps/web/src/composables/watermark-settings.ts +40 -11
  123. package/template/apps/web/src/composables/watermark-settings.webtest.ts +19 -6
  124. package/template/apps/web/src/layout/AppSidebar.vue +8 -1
  125. package/template/apps/web/src/layout/GlobalWatermark.layering.webtest.ts +2 -0
  126. package/template/apps/web/src/layout/GlobalWatermark.vue +4 -8
  127. package/template/apps/web/src/layout/GlobalWatermark.webtest.ts +12 -5
  128. package/template/apps/web/src/layout/NavigationSearchDialog.vue +8 -1
  129. package/template/apps/web/src/layout/navigation.ts +29 -4
  130. package/template/apps/web/src/layout/navigation.webtest.ts +51 -0
  131. package/template/apps/web/src/pages/dashboard/dashboard-presenter.ts +6 -1
  132. package/template/apps/web/src/pages/settings/WatermarkPreview.vue +72 -0
  133. package/template/apps/web/src/pages/settings/WatermarkPreview.webtest.ts +55 -0
  134. package/template/apps/web/src/pages/settings/WatermarkSettingsSection.vue +80 -24
  135. package/template/apps/web/src/pages/settings/WatermarkSettingsSection.webtest.ts +115 -0
  136. package/template/apps/web/src/pages/work-orders/WorkOrderListPage.vue +202 -0
  137. package/template/apps/web/src/pages/work-orders/WorkOrderListPage.webtest.ts +51 -0
  138. package/template/apps/web/src/router/admin-extension-loader.ts +44 -0
  139. package/template/apps/web/src/router/admin-extension-loader.webtest.ts +49 -0
  140. package/template/apps/web/src/router/admin-ui-composition-plugin.ts +33 -0
  141. package/template/apps/web/src/router/admin-ui-composition-plugin.webtest.ts +20 -0
  142. package/template/apps/web/src/router/admin-ui-composition.ts +313 -0
  143. package/template/apps/web/src/router/admin-ui-composition.webtest.ts +225 -0
  144. package/template/apps/web/src/router/admin-ui-module-registry-plugin.ts +55 -0
  145. package/template/apps/web/src/router/admin-ui-module-registry-plugin.webtest.ts +46 -0
  146. package/template/apps/web/src/router/business-routes.ts +32 -45
  147. package/template/apps/web/src/router/business-routes.webtest.ts +40 -44
  148. package/template/apps/web/src/router/index.ts +64 -54
  149. package/template/apps/web/src/router/manifest-routes.ts +27 -7
  150. package/template/apps/web/src/router/manifest-routes.webtest.ts +10 -2
  151. package/template/apps/web/src/router/platform-admin-route-bindings.ts +58 -0
  152. package/template/apps/web/src/router/reserved-route-paths.ts +14 -0
  153. package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +10 -4
  154. package/template/apps/web/src/router/web-composition-host.ts +20 -0
  155. package/template/apps/web/src/services/work-order-api.ts +16 -0
  156. package/template/apps/web/src/services/work-order-api.webtest.ts +21 -0
  157. package/template/apps/web/src/virtual-admin-ui-modules.d.ts +5 -0
  158. package/template/apps/web/vite.config.ts +22 -1
  159. package/template/docs/architecture/migration-bundle.md +10 -7
  160. package/template/docs/architecture/module-manifest-composition.md +85 -3
  161. package/template/docs/architecture/search-provider-protocol.md +6 -1
  162. package/template/docs/architecture/server-module-interface.md +70 -7
  163. package/template/docs/development/business-web-routes.md +53 -50
  164. package/template/docs/development/module-lifecycle.md +48 -54
  165. package/template/docs/platform/navigation-registry.md +5 -4
  166. package/template/docs/platform/platform-shell-settings.md +15 -10
  167. package/template/docs/platform/web-system-crud.md +1 -1
  168. package/template/package.json +1 -1
  169. package/template/packages/config/package.json +1 -1
  170. package/template/packages/database/drizzle/0044_sweet_tarantula.sql +6 -0
  171. package/template/packages/database/drizzle/meta/0044_snapshot.json +8349 -0
  172. package/template/packages/database/drizzle/meta/_journal.json +7 -0
  173. package/template/packages/database/package.json +1 -1
  174. package/template/packages/database/src/schema/platform-settings.ts +3 -0
  175. package/template/packages/example-work-order/package.json +6 -0
  176. package/template/packages/example-work-order/src/admin-page.ts +78 -0
  177. package/template/packages/example-work-order/src/admin.ts +35 -0
  178. package/template/packages/example-work-order/src/manifest.test.ts +62 -5
  179. package/template/packages/example-work-order/src/manifest.ts +54 -7
  180. package/template/packages/example-work-order/src/migration-bundle.ts +8 -0
  181. package/template/packages/example-work-order/src/server-module.ts +2 -2
  182. package/template/packages/jobs/package.json +1 -1
  183. package/template/packages/modules/index.ts +4 -0
  184. package/template/packages/modules/package.json +3 -2
  185. package/template/packages/modules/src/admin-ui-entry.ts +8 -0
  186. package/template/packages/modules/src/admin-ui-manifest.test.ts +52 -0
  187. package/template/packages/modules/src/admin-ui-manifest.ts +53 -0
  188. package/template/packages/modules/src/capability-audit-validation.test.ts +127 -0
  189. package/template/packages/modules/src/capability-audit-validation.ts +38 -0
  190. package/template/packages/modules/src/capability-guard-validation.ts +148 -0
  191. package/template/packages/modules/src/capability-key-validation.test.ts +150 -0
  192. package/template/packages/modules/src/composition-fixtures.test-support.ts +7 -5
  193. package/template/packages/modules/src/composition.test.ts +74 -9
  194. package/template/packages/modules/src/composition.ts +8 -0
  195. package/template/packages/modules/src/manifest.ts +1 -0
  196. package/template/packages/modules/src/migration-bundle-validation.test.ts +23 -0
  197. package/template/packages/modules/src/module-host-range.test.ts +57 -0
  198. package/template/packages/modules/src/module-host-range.ts +125 -0
  199. package/template/packages/modules/src/project-module-assembly.test.ts +133 -0
  200. package/template/packages/modules/src/project-module-assembly.ts +126 -0
  201. package/template/packages/modules/src/system-admin-routes.ts +6 -5
  202. package/template/packages/modules/src/system-admin-ui-catalog.ts +72 -0
  203. package/template/packages/modules/src/system-agent-tools.ts +2 -2
  204. package/template/packages/modules/src/system-api-binding.ts +43 -9
  205. package/template/packages/modules/src/system-api-control-plane.ts +202 -0
  206. package/template/packages/modules/src/system-api-data-governance.ts +4 -2
  207. package/template/packages/modules/src/system-api-operational.ts +57 -8
  208. package/template/packages/modules/src/system-api-permissions.ts +115 -21
  209. package/template/packages/modules/src/system-api-watermark.ts +3 -0
  210. package/template/packages/modules/src/system-audit-actions.ts +7 -0
  211. package/template/packages/modules/src/system-data-governance.ts +5 -2
  212. package/template/packages/modules/src/system-features.ts +31 -0
  213. package/template/packages/modules/src/system-governed-export-bindings.ts +8 -4
  214. package/template/packages/modules/src/system-module-catalog.test.ts +39 -4
  215. package/template/packages/modules/src/system-module-catalog.ts +68 -7
  216. package/template/packages/modules/src/system-module-tasks.ts +8 -1
  217. package/template/packages/modules/src/system-modules-access.test.ts +4 -0
  218. package/template/packages/modules/src/system-modules.test.ts +10 -2
  219. package/template/packages/modules/src/system-modules.ts +6 -5
  220. package/template/packages/modules/src/system-permissions.ts +4 -0
  221. package/template/packages/modules/user/module.ts +19 -2
  222. package/template/packages/server-sdk/index.ts +8 -0
  223. package/template/packages/server-sdk/package.json +2 -1
  224. package/template/packages/server-sdk/src/durable-handler.ts +16 -1
  225. package/template/packages/server-sdk/src/module-adapter.test.ts +44 -0
  226. package/template/packages/server-sdk/src/module-adapter.ts +59 -0
  227. package/template/packages/server-sdk/src/route-definition.ts +3 -2
  228. package/template/packages/shared/package.json +1 -1
  229. package/template/packages/shared/src/audit.test.ts +7 -0
  230. package/template/packages/shared/src/audit.ts +16 -0
  231. package/template/packages/shared/src/migration-bundle-validation.ts +9 -11
  232. package/template/packages/shared/src/platform-settings.ts +17 -0
  233. package/template/packages/shared/src/rbac.ts +2 -0
  234. package/template/apps/web/src/app/business-routes.ts +0 -22
@@ -309,6 +309,13 @@
309
309
  "when": 1785269877377,
310
310
  "tag": "0043_activate_agent_dependencies",
311
311
  "breakpoints": true
312
+ },
313
+ {
314
+ "idx": 44,
315
+ "version": "7",
316
+ "when": 1785400645941,
317
+ "tag": "0044_sweet_tarantula",
318
+ "breakpoints": true
312
319
  }
313
320
  ]
314
321
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@southwind-ai/database",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "engines": {
@@ -19,6 +19,7 @@ import {
19
19
  // @windy-module system.settings begin
20
20
  import {
21
21
  boolean,
22
+ integer as settingsInteger,
22
23
  jsonb as settingsJsonb,
23
24
  pgTable as settingsTable,
24
25
  text,
@@ -58,6 +59,8 @@ export const platformWatermarkPolicies = settingsTable(
58
59
  .default(["username", "displayName"])
59
60
  .notNull(),
60
61
  customContent: settingsVarchar("custom_content", { length: 80 }).notNull(),
62
+ opacityPercent: settingsInteger("opacity_percent").default(8).notNull(),
63
+ fontSize: settingsInteger("font_size").default(14).notNull(),
61
64
  ...auditColumns,
62
65
  },
63
66
  );
@@ -13,8 +13,14 @@
13
13
  "drizzle-orm": "^0.45.2",
14
14
  "zod": "^4.4.3"
15
15
  },
16
+ "peerDependencies": {
17
+ "vue": "^3.5.0"
18
+ },
16
19
  "exports": {
17
20
  ".": "./index.ts",
21
+ "./admin": "./src/admin.ts",
22
+ "./contract": "./src/contract.ts",
23
+ "./manifest": "./src/manifest.ts",
18
24
  "./migration-bundle": "./src/migration-bundle.ts"
19
25
  }
20
26
  }
@@ -0,0 +1,78 @@
1
+ import { defineComponent, h, onMounted, ref } from "vue";
2
+ import type { WorkOrder, WorkOrderPage } from "./contract.js";
3
+
4
+ export default defineComponent({
5
+ name: "DefaultWorkOrderAdminPage",
6
+ setup() {
7
+ const items = ref<WorkOrder[]>([]);
8
+ const loading = ref(true);
9
+ const error = ref("");
10
+
11
+ onMounted(async () => {
12
+ try {
13
+ const response = await fetch("/api/work-orders?page=1&pageSize=20");
14
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
15
+ const payload: unknown = await response.json();
16
+ if (!isWorkOrderPage(payload)) throw new Error("响应结构无效");
17
+ items.value = payload.items;
18
+ } catch {
19
+ error.value = "工单加载失败,请稍后重试。";
20
+ } finally {
21
+ loading.value = false;
22
+ }
23
+ });
24
+
25
+ return () =>
26
+ h("main", { class: "space-y-4 p-6" }, [
27
+ h("header", { class: "space-y-1" }, [
28
+ h("h1", { class: "text-2xl font-semibold" }, "工单管理"),
29
+ h(
30
+ "p",
31
+ { class: "text-sm text-muted-foreground" },
32
+ "模块 package 提供的默认页面;项目可在 Web Composition 中替换。",
33
+ ),
34
+ ]),
35
+ loading.value
36
+ ? h("p", "正在加载工单…")
37
+ : error.value
38
+ ? h("p", { role: "alert" }, error.value)
39
+ : h(
40
+ "ul",
41
+ { class: "divide-y rounded-md border" },
42
+ items.value.map((item) =>
43
+ h("li", { class: "space-y-1 p-4", key: item.id }, [
44
+ h("p", { class: "font-medium" }, item.title),
45
+ h(
46
+ "p",
47
+ { class: "text-sm text-muted-foreground" },
48
+ `${item.workflowStatus} · ${item.priority}`,
49
+ ),
50
+ ]),
51
+ ),
52
+ ),
53
+ ]);
54
+ },
55
+ });
56
+
57
+ function isWorkOrderPage(value: unknown): value is WorkOrderPage {
58
+ return (
59
+ isRecord(value) &&
60
+ Array.isArray(value.items) &&
61
+ value.items.every(
62
+ (item) =>
63
+ isRecord(item) &&
64
+ typeof item.id === "string" &&
65
+ typeof item.title === "string" &&
66
+ typeof item.description === "string" &&
67
+ typeof item.priority === "string" &&
68
+ typeof item.workflowStatus === "string",
69
+ ) &&
70
+ typeof value.page === "number" &&
71
+ typeof value.pageSize === "number" &&
72
+ typeof value.total === "number"
73
+ );
74
+ }
75
+
76
+ function isRecord(value: unknown): value is Record<string, unknown> {
77
+ return typeof value === "object" && value !== null && !Array.isArray(value);
78
+ }
@@ -0,0 +1,35 @@
1
+ import {
2
+ defineAdminUiManifest,
3
+ defineWindyAdminUiModule,
4
+ } from "@southwind-ai/modules/admin-ui";
5
+
6
+ export const workOrderAdminUiManifest = defineAdminUiManifest({
7
+ name: "work-order",
8
+ version: "0.1.0",
9
+ adminRoutes: [
10
+ {
11
+ key: "work-order.ticket.list",
12
+ path: "/admin/work-orders",
13
+ title: "工单管理",
14
+ surface: "sidebar",
15
+ directAccess: "guarded",
16
+ permissionKey: "work-order.ticket.read",
17
+ featureKey: "work-order.ticket",
18
+ },
19
+ ],
20
+ });
21
+
22
+ /**
23
+ * package 标准 Admin 导出,提供可独立装配的默认页面。
24
+ * 项目可以通过 project-owned Web Composition 覆盖页面 loader。
25
+ */
26
+ export const windyAdminUiModule = defineWindyAdminUiModule({
27
+ manifest: workOrderAdminUiManifest,
28
+ hostRange: "^0.1.0",
29
+ pages: [
30
+ {
31
+ routeKey: "work-order.ticket.list",
32
+ load: () => import("./admin-page.js"),
33
+ },
34
+ ],
35
+ });
@@ -1,8 +1,15 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { workOrderModule } from "./manifest.js";
2
+ import {
3
+ windyAdminUiModule,
4
+ workOrderAdminUiManifest,
5
+ } from "@southwind-ai/example-work-order/admin";
6
+ import {
7
+ windyModuleManifest,
8
+ workOrderModule,
9
+ } from "@southwind-ai/example-work-order/manifest";
3
10
 
4
11
  describe("示例工单 Module Manifest", () => {
5
- test("业务能力保留 API 与任务但不进入 Windy Admin Surface", () => {
12
+ test("业务能力注册 API、任务与 Windy Admin Surface", () => {
6
13
  const manifest = workOrderModule();
7
14
 
8
15
  expect(manifest.name).not.toBe("system");
@@ -16,16 +23,54 @@ describe("示例工单 Module Manifest", () => {
16
23
  "work-order.ticket.manage",
17
24
  ]);
18
25
  expect(manifest.apiPermissions).toHaveLength(5);
19
- expect(manifest.menus).toEqual([]);
20
- expect(manifest.adminRoutes).toEqual([]);
26
+ expect(
27
+ manifest.apiPermissions.map(({ auditAction }) => auditAction),
28
+ ).toEqual([
29
+ "data.read",
30
+ "data.read",
31
+ "crud.create",
32
+ "crud.update",
33
+ "crud.delete",
34
+ ]);
35
+ expect(manifest.menus).toEqual([
36
+ {
37
+ key: "work-order.ticket.list",
38
+ title: "工单管理",
39
+ type: "menu",
40
+ order: 100,
41
+ visible: true,
42
+ path: "/admin/work-orders",
43
+ icon: "clipboard-list",
44
+ featureKey: "work-order.ticket",
45
+ permissionKey: "work-order.ticket.read",
46
+ },
47
+ ]);
48
+ expect(manifest.adminRoutes).toEqual([
49
+ {
50
+ key: "work-order.ticket.list",
51
+ path: "/admin/work-orders",
52
+ title: "工单管理",
53
+ surface: "sidebar",
54
+ directAccess: "guarded",
55
+ featureKey: "work-order.ticket",
56
+ permissionKey: "work-order.ticket.read",
57
+ },
58
+ ]);
21
59
  expect(manifest.auditActions).toEqual(
22
- expect.arrayContaining(["crud.create", "crud.update", "crud.delete"]),
60
+ expect.arrayContaining([
61
+ "crud.create",
62
+ "crud.update",
63
+ "crud.delete",
64
+ "data.read",
65
+ "task.execute",
66
+ ]),
23
67
  );
24
68
  expect(manifest.tasks).toContainEqual(
25
69
  expect.objectContaining({
26
70
  key: "work-order.ticket.snapshot",
27
71
  featureKey: "work-order.ticket",
28
72
  permissionKey: "work-order.ticket.manage",
73
+ auditAction: "task.execute",
29
74
  }),
30
75
  );
31
76
  expect(manifest.providers).toEqual([
@@ -50,5 +95,17 @@ describe("示例工单 Module Manifest", () => {
50
95
  persistence: "postgresql-with-memory-fallback",
51
96
  uiComposition: "external-business-app",
52
97
  });
98
+ expect(workOrderAdminUiManifest.adminRoutes).toEqual(manifest.adminRoutes);
99
+ expect(windyModuleManifest).toEqual(manifest);
100
+ expect(windyAdminUiModule).toMatchObject({
101
+ manifest: workOrderAdminUiManifest,
102
+ hostRange: "^0.1.0",
103
+ pages: [
104
+ expect.objectContaining({
105
+ routeKey: "work-order.ticket.list",
106
+ load: expect.any(Function),
107
+ }),
108
+ ],
109
+ });
53
110
  });
54
111
  });
@@ -2,11 +2,14 @@ import {
2
2
  defineSearchProvider,
3
3
  featureKey,
4
4
  licenseVersionKey,
5
+ type AuditActionType,
5
6
  } from "@southwind-ai/shared";
6
7
  import {
7
8
  WORK_ORDER_INITIAL_MIGRATION_ID,
9
+ WORK_ORDER_MODULE_DEPENDENCIES,
8
10
  workOrderMigrationBundle,
9
11
  } from "./migration-bundle.js";
12
+ import { workOrderAdminUiManifest } from "./admin.js";
10
13
 
11
14
  const FEATURE_KEY = featureKey("work-order.ticket");
12
15
 
@@ -16,19 +19,52 @@ export function workOrderModule() {
16
19
  version: "0.1.0",
17
20
  label: "示例工单",
18
21
  description: "可删除的业务模块示例,演示完整平台能力接入。",
19
- menus: [],
20
- adminRoutes: [],
22
+ dependencies: [...WORK_ORDER_MODULE_DEPENDENCIES],
23
+ menus: [
24
+ {
25
+ key: "work-order.ticket.list",
26
+ title: "工单管理",
27
+ type: "menu" as const,
28
+ order: 100,
29
+ visible: true,
30
+ path: "/admin/work-orders",
31
+ icon: "clipboard-list",
32
+ permissionKey: "work-order.ticket.read",
33
+ featureKey: FEATURE_KEY,
34
+ },
35
+ ],
36
+ adminRoutes: [...workOrderAdminUiManifest.adminRoutes],
21
37
  permissions: [
22
38
  permission("read", "查看工单"),
23
39
  permission("create", "创建工单"),
24
40
  permission("manage", "管理工单"),
25
41
  ],
26
42
  apiPermissions: [
27
- binding("GET", "/api/work-orders", "work-order.ticket.read"),
28
- binding("GET", "/api/work-orders/:id", "work-order.ticket.read"),
29
- binding("POST", "/api/work-orders", "work-order.ticket.create"),
30
- binding("PUT", "/api/work-orders/:id", "work-order.ticket.manage"),
31
- binding("DELETE", "/api/work-orders/:id", "work-order.ticket.manage"),
43
+ binding("GET", "/api/work-orders", "work-order.ticket.read", "data.read"),
44
+ binding(
45
+ "GET",
46
+ "/api/work-orders/:id",
47
+ "work-order.ticket.read",
48
+ "data.read",
49
+ ),
50
+ binding(
51
+ "POST",
52
+ "/api/work-orders",
53
+ "work-order.ticket.create",
54
+ "crud.create",
55
+ ),
56
+ binding(
57
+ "PUT",
58
+ "/api/work-orders/:id",
59
+ "work-order.ticket.manage",
60
+ "crud.update",
61
+ ),
62
+ binding(
63
+ "DELETE",
64
+ "/api/work-orders/:id",
65
+ "work-order.ticket.manage",
66
+ "crud.delete",
67
+ ),
32
68
  ],
33
69
  features: [
34
70
  {
@@ -61,6 +97,7 @@ export function workOrderModule() {
61
97
  description: "统计当前工单待办和高优先级积压,写入调度执行结果。",
62
98
  permissionKey: "work-order.ticket.manage",
63
99
  featureKey: FEATURE_KEY,
100
+ auditAction: "task.execute",
64
101
  intervalSeconds: 900,
65
102
  maxAttempts: 3,
66
103
  retryDelaySeconds: 60,
@@ -88,7 +125,9 @@ export function workOrderModule() {
88
125
  "crud.create" as const,
89
126
  "crud.update" as const,
90
127
  "crud.delete" as const,
128
+ "data.read" as const,
91
129
  "data.search" as const,
130
+ "task.execute" as const,
92
131
  ],
93
132
  metadata: {
94
133
  removableExample: true,
@@ -99,6 +138,12 @@ export function workOrderModule() {
99
138
  };
100
139
  }
101
140
 
141
+ /**
142
+ * package 标准 Manifest 导出。构建与 Server 可用它核对窄 Admin 投影,
143
+ * 浏览器只能从 `./admin` 子路径加载。
144
+ */
145
+ export const windyModuleManifest = workOrderModule();
146
+
102
147
  function permission(action: "read" | "create" | "manage", label: string) {
103
148
  return {
104
149
  key: `work-order.ticket.${action}`,
@@ -115,11 +160,13 @@ function binding(
115
160
  method: "GET" | "POST" | "PUT" | "DELETE",
116
161
  path: string,
117
162
  permissionKey: string,
163
+ auditAction: AuditActionType,
118
164
  ) {
119
165
  return {
120
166
  method,
121
167
  path,
122
168
  permissionKey,
123
169
  featureKey: "work-order.ticket",
170
+ auditAction,
124
171
  };
125
172
  }
@@ -7,6 +7,14 @@ import {
7
7
  export const WORK_ORDER_INITIAL_MIGRATION_ID =
8
8
  "work-order.0001.create-work-orders";
9
9
 
10
+ export const WORK_ORDER_MODULE_DEPENDENCIES = [
11
+ "system",
12
+ "system.identity",
13
+ "system.search",
14
+ "system.scheduler",
15
+ "system.storage",
16
+ ] as const;
17
+
10
18
  export const workOrderMigrationBundle: MigrationBundle = {
11
19
  formatVersion: MIGRATION_BUNDLE_FORMAT_VERSION,
12
20
  module: "work-order",
@@ -5,7 +5,7 @@ import type {
5
5
  ModuleSearchProvider,
6
6
  ModuleTaskHandler,
7
7
  } from "@southwind-ai/server-sdk";
8
- import { workOrderModule } from "./manifest.js";
8
+ import { windyModuleManifest } from "./manifest.js";
9
9
 
10
10
  export interface WorkOrderServerBindings {
11
11
  readonly routes: readonly ModuleRouteDefinition[];
@@ -20,7 +20,7 @@ export function createWorkOrderServerModule(
20
20
  bindings: WorkOrderServerBindings,
21
21
  ): InstallableServerModule<ModuleManifest> {
22
22
  return {
23
- manifest: workOrderModule(),
23
+ manifest: windyModuleManifest,
24
24
  setup(host) {
25
25
  host.registerUploadPolicy({
26
26
  purpose: "work-order.ticket.attachment",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@southwind-ai/jobs",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "engines": {
@@ -8,3 +8,7 @@ export * from "./src/system-module-catalog.js";
8
8
  export * from "./src/system-api-permissions.js";
9
9
  export * from "./src/tool-composition.js";
10
10
  export * from "./src/ai-network-policy.js";
11
+ export * from "./src/project-module-assembly.js";
12
+ export * from "./src/module-host-range.js";
13
+ export * from "./src/admin-ui-manifest.js";
14
+ export * from "./src/system-admin-ui-catalog.js";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@southwind-ai/modules",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "engines": {
@@ -15,7 +15,8 @@
15
15
  "@southwind-ai/shared": "workspace:^"
16
16
  },
17
17
  "exports": {
18
- ".": "./index.ts"
18
+ ".": "./index.ts",
19
+ "./admin-ui": "./src/admin-ui-entry.ts"
19
20
  },
20
21
  "publishConfig": {
21
22
  "access": "public"
@@ -0,0 +1,8 @@
1
+ export * from "./admin-ui-manifest.js";
2
+ export * from "./module-host-range.js";
3
+ export * from "./project-module-assembly.js";
4
+ export * from "./system-admin-ui-catalog.js";
5
+ export type {
6
+ AdminRouteSurface,
7
+ ModuleAdminRouteDefinition,
8
+ } from "./manifest.js";
@@ -0,0 +1,52 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import {
3
+ defineAdminUiManifest,
4
+ defineWindyAdminUiModule,
5
+ projectAdminUiManifest,
6
+ } from "./admin-ui-manifest.js";
7
+ import { systemModuleManifests } from "./system-module-catalog.js";
8
+ import { systemAdminUiManifests } from "./system-admin-ui-catalog.js";
9
+
10
+ describe("Admin UI 窄 Manifest", () => {
11
+ test("从完整 Manifest 只投影身份与 Admin Route", () => {
12
+ const full = systemModuleManifests().find(
13
+ ({ name }) => name === "system.identity",
14
+ )!;
15
+ const projected = projectAdminUiManifest(full);
16
+
17
+ expect(projected).toEqual({
18
+ name: full.name,
19
+ version: full.version,
20
+ adminRoutes: full.adminRoutes,
21
+ });
22
+ expect(projected).not.toHaveProperty("permissions");
23
+ expect(projected).not.toHaveProperty("migrations");
24
+ });
25
+
26
+ test("系统窄目录与完整 Manifest 的 Admin Route 保持同源", () => {
27
+ const full = new Map(
28
+ systemModuleManifests().map((manifest) => [
29
+ manifest.name,
30
+ projectAdminUiManifest(manifest),
31
+ ]),
32
+ );
33
+
34
+ expect(systemAdminUiManifests()).toEqual([...full.values()]);
35
+ });
36
+
37
+ test("标准 Admin UI Module 保留 Host range 与页面绑定", () => {
38
+ const manifest = defineAdminUiManifest({
39
+ name: "case",
40
+ version: "1.0.0",
41
+ adminRoutes: [],
42
+ });
43
+ const module = defineWindyAdminUiModule({
44
+ manifest,
45
+ hostRange: "^0.1.0",
46
+ pages: [],
47
+ });
48
+
49
+ expect(module.manifest).toBe(manifest);
50
+ expect(module.hostRange).toBe("^0.1.0");
51
+ });
52
+ });
@@ -0,0 +1,53 @@
1
+ import type { ModuleAdminRouteDefinition, ModuleManifest } from "./manifest.js";
2
+
3
+ /**
4
+ * 浏览器可见的最小 Module 契约。Server Manifest 的 Migration、Task、Provider、
5
+ * Audit 和持久化元数据不得进入这个类型或 Web bundle。
6
+ */
7
+ export interface AdminUiManifest {
8
+ readonly name: string;
9
+ readonly version: string;
10
+ readonly adminRoutes: readonly ModuleAdminRouteDefinition[];
11
+ }
12
+
13
+ export type AdminPageLoader<TPage = unknown> = () => Promise<{
14
+ readonly default: TPage;
15
+ }>;
16
+
17
+ export interface AdminPageBinding<TPage = unknown> {
18
+ readonly routeKey: string;
19
+ readonly load: AdminPageLoader<TPage>;
20
+ }
21
+
22
+ export interface WindyAdminUiModule<TPage = unknown> {
23
+ readonly manifest: AdminUiManifest;
24
+ readonly hostRange: string;
25
+ readonly pages: readonly AdminPageBinding<TPage>[];
26
+ }
27
+
28
+ export type AdminUiManifestSource = Pick<
29
+ ModuleManifest,
30
+ "name" | "version" | "adminRoutes"
31
+ >;
32
+
33
+ export function defineAdminUiManifest(
34
+ manifest: AdminUiManifest,
35
+ ): AdminUiManifest {
36
+ return manifest;
37
+ }
38
+
39
+ export function defineWindyAdminUiModule<TPage>(
40
+ module: WindyAdminUiModule<TPage>,
41
+ ): WindyAdminUiModule<TPage> {
42
+ return module;
43
+ }
44
+
45
+ export function projectAdminUiManifest(
46
+ manifest: AdminUiManifestSource,
47
+ ): AdminUiManifest {
48
+ return {
49
+ name: manifest.name,
50
+ version: manifest.version,
51
+ adminRoutes: manifest.adminRoutes.map((route) => ({ ...route })),
52
+ };
53
+ }