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.
Files changed (213) hide show
  1. package/README.md +28 -13
  2. package/dist/cli.js +783 -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/startup-composition-validation.test.ts +35 -0
  96. package/template/apps/server/src/startup-composition-validation.ts +54 -0
  97. package/template/apps/server/src/startup-preflight.test.ts +36 -0
  98. package/template/apps/server/src/startup-preflight.ts +37 -0
  99. package/template/apps/server/src/system/audit-routes.test.ts +9 -3
  100. package/template/apps/server/src/system/department-routes.test.ts +5 -3
  101. package/template/apps/server/src/system/feature-routes.test.ts +4 -3
  102. package/template/apps/server/src/system/interface-catalog.test.ts +5 -3
  103. package/template/apps/server/src/system/interface-catalog.ts +3 -0
  104. package/template/apps/server/src/system/menu-routes.test.ts +5 -3
  105. package/template/apps/server/src/system/route-test-runtime.ts +24 -0
  106. package/template/apps/server/src/system/routes-validation.test.ts +7 -3
  107. package/template/apps/server/src/system/routes.test.ts +21 -11
  108. package/template/apps/server/src/system/routes.ts +11 -2
  109. package/template/apps/server/src/work-order/foundation-modules.test.ts +6 -4
  110. package/template/apps/server/src/work-order/routes.test.ts +8 -0
  111. package/template/apps/server/src/work-order/task.test.ts +6 -1
  112. package/template/apps/web/Dockerfile +5 -0
  113. package/template/apps/web/package.json +1 -0
  114. package/template/apps/web/scripts/validate-admin-ui-composition.ts +106 -0
  115. package/template/apps/web/src/app/web-composition.ts +24 -0
  116. package/template/apps/web/src/app/web-composition.webtest.ts +15 -0
  117. package/template/apps/web/src/layout/AppSidebar.vue +8 -1
  118. package/template/apps/web/src/layout/NavigationSearchDialog.vue +8 -1
  119. package/template/apps/web/src/layout/navigation.ts +29 -4
  120. package/template/apps/web/src/layout/navigation.webtest.ts +51 -0
  121. package/template/apps/web/src/pages/dashboard/dashboard-presenter.ts +6 -1
  122. package/template/apps/web/src/pages/work-orders/WorkOrderListPage.vue +202 -0
  123. package/template/apps/web/src/pages/work-orders/WorkOrderListPage.webtest.ts +51 -0
  124. package/template/apps/web/src/router/admin-extension-loader.ts +44 -0
  125. package/template/apps/web/src/router/admin-extension-loader.webtest.ts +49 -0
  126. package/template/apps/web/src/router/admin-ui-composition-plugin.ts +33 -0
  127. package/template/apps/web/src/router/admin-ui-composition-plugin.webtest.ts +20 -0
  128. package/template/apps/web/src/router/admin-ui-composition.ts +313 -0
  129. package/template/apps/web/src/router/admin-ui-composition.webtest.ts +225 -0
  130. package/template/apps/web/src/router/admin-ui-module-registry-plugin.ts +55 -0
  131. package/template/apps/web/src/router/admin-ui-module-registry-plugin.webtest.ts +46 -0
  132. package/template/apps/web/src/router/business-routes.ts +32 -45
  133. package/template/apps/web/src/router/business-routes.webtest.ts +40 -44
  134. package/template/apps/web/src/router/index.ts +64 -54
  135. package/template/apps/web/src/router/manifest-routes.ts +27 -7
  136. package/template/apps/web/src/router/manifest-routes.webtest.ts +10 -2
  137. package/template/apps/web/src/router/platform-admin-route-bindings.ts +58 -0
  138. package/template/apps/web/src/router/reserved-route-paths.ts +14 -0
  139. package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +10 -4
  140. package/template/apps/web/src/router/web-composition-host.ts +20 -0
  141. package/template/apps/web/src/services/work-order-api.ts +16 -0
  142. package/template/apps/web/src/services/work-order-api.webtest.ts +21 -0
  143. package/template/apps/web/src/virtual-admin-ui-modules.d.ts +5 -0
  144. package/template/apps/web/vite.config.ts +22 -1
  145. package/template/docs/architecture/migration-bundle.md +10 -7
  146. package/template/docs/architecture/module-manifest-composition.md +85 -3
  147. package/template/docs/architecture/search-provider-protocol.md +6 -1
  148. package/template/docs/architecture/server-module-interface.md +70 -7
  149. package/template/docs/development/business-web-routes.md +53 -50
  150. package/template/docs/development/module-lifecycle.md +48 -54
  151. package/template/docs/platform/navigation-registry.md +5 -4
  152. package/template/package.json +1 -1
  153. package/template/packages/config/package.json +1 -1
  154. package/template/packages/database/package.json +1 -1
  155. package/template/packages/example-work-order/package.json +6 -0
  156. package/template/packages/example-work-order/src/admin-page.ts +78 -0
  157. package/template/packages/example-work-order/src/admin.ts +35 -0
  158. package/template/packages/example-work-order/src/manifest.test.ts +62 -5
  159. package/template/packages/example-work-order/src/manifest.ts +54 -7
  160. package/template/packages/example-work-order/src/migration-bundle.ts +8 -0
  161. package/template/packages/example-work-order/src/server-module.ts +2 -2
  162. package/template/packages/jobs/package.json +1 -1
  163. package/template/packages/modules/index.ts +4 -0
  164. package/template/packages/modules/package.json +3 -2
  165. package/template/packages/modules/src/admin-ui-entry.ts +8 -0
  166. package/template/packages/modules/src/admin-ui-manifest.test.ts +52 -0
  167. package/template/packages/modules/src/admin-ui-manifest.ts +53 -0
  168. package/template/packages/modules/src/capability-audit-validation.test.ts +127 -0
  169. package/template/packages/modules/src/capability-audit-validation.ts +38 -0
  170. package/template/packages/modules/src/capability-guard-validation.ts +148 -0
  171. package/template/packages/modules/src/capability-key-validation.test.ts +150 -0
  172. package/template/packages/modules/src/composition-fixtures.test-support.ts +7 -5
  173. package/template/packages/modules/src/composition.test.ts +74 -9
  174. package/template/packages/modules/src/composition.ts +8 -0
  175. package/template/packages/modules/src/manifest.ts +1 -0
  176. package/template/packages/modules/src/migration-bundle-validation.test.ts +23 -0
  177. package/template/packages/modules/src/module-host-range.test.ts +57 -0
  178. package/template/packages/modules/src/module-host-range.ts +125 -0
  179. package/template/packages/modules/src/project-module-assembly.test.ts +133 -0
  180. package/template/packages/modules/src/project-module-assembly.ts +126 -0
  181. package/template/packages/modules/src/system-admin-routes.ts +6 -5
  182. package/template/packages/modules/src/system-admin-ui-catalog.ts +72 -0
  183. package/template/packages/modules/src/system-agent-tools.ts +2 -2
  184. package/template/packages/modules/src/system-api-binding.ts +43 -9
  185. package/template/packages/modules/src/system-api-control-plane.ts +202 -0
  186. package/template/packages/modules/src/system-api-data-governance.ts +4 -2
  187. package/template/packages/modules/src/system-api-operational.ts +57 -8
  188. package/template/packages/modules/src/system-api-permissions.ts +115 -21
  189. package/template/packages/modules/src/system-api-watermark.ts +3 -0
  190. package/template/packages/modules/src/system-audit-actions.ts +7 -0
  191. package/template/packages/modules/src/system-data-governance.ts +5 -2
  192. package/template/packages/modules/src/system-features.ts +31 -0
  193. package/template/packages/modules/src/system-governed-export-bindings.ts +8 -4
  194. package/template/packages/modules/src/system-module-catalog.test.ts +39 -4
  195. package/template/packages/modules/src/system-module-catalog.ts +68 -7
  196. package/template/packages/modules/src/system-module-tasks.ts +8 -1
  197. package/template/packages/modules/src/system-modules-access.test.ts +4 -0
  198. package/template/packages/modules/src/system-modules.test.ts +10 -2
  199. package/template/packages/modules/src/system-modules.ts +6 -5
  200. package/template/packages/modules/src/system-permissions.ts +4 -0
  201. package/template/packages/modules/user/module.ts +19 -2
  202. package/template/packages/server-sdk/index.ts +8 -0
  203. package/template/packages/server-sdk/package.json +2 -1
  204. package/template/packages/server-sdk/src/durable-handler.ts +16 -1
  205. package/template/packages/server-sdk/src/module-adapter.test.ts +44 -0
  206. package/template/packages/server-sdk/src/module-adapter.ts +59 -0
  207. package/template/packages/server-sdk/src/route-definition.ts +3 -2
  208. package/template/packages/shared/package.json +1 -1
  209. package/template/packages/shared/src/audit.test.ts +7 -0
  210. package/template/packages/shared/src/audit.ts +16 -0
  211. package/template/packages/shared/src/migration-bundle-validation.ts +9 -11
  212. package/template/packages/shared/src/rbac.ts +2 -0
  213. package/template/apps/web/src/app/business-routes.ts +0 -22
@@ -1,72 +1,66 @@
1
- # 部署期模块生命周期
1
+ # 项目模块装配与生命周期
2
2
 
3
- Windy 已部署项目通过项目锁定版本的 `create-windy` 管理模块,不重新运行创建命令,
4
- 也不手工复制源码:
3
+ 更新时间:2026-07-29。
4
+
5
+ ## 当前可用命令
6
+
7
+ 已部署项目通过项目锁定版本的 `create-windy` 管理装配选择:
5
8
 
6
9
  ```bash
7
10
  bun run windy module list
8
- bun run windy module add work-order --dry-run
9
- bun run windy module add work-order
10
- bun run windy module update work-order
11
- bun run windy module remove work-order --dry-run
12
- bun run windy module remove work-order --confirm-data-disposition
11
+ bun run windy module enable <module>
12
+ bun run windy module disable <module>
13
13
  ```
14
14
 
15
- ## 计划与事务
16
-
17
- 模块命令读取 `.windy/generation.json` 的当前集合,再用
18
- `ModuleDistributionManifest` 解析依赖、冲突、Windy 兼容范围、package、文件、
19
- Schema、migration 与卸载策略。目标组合仍由 immutable template 生成,之后进入与
20
- `windy update` 相同的 Base/Ours/Theirs 计划、文件 expectation、workspace 检查、
21
- CAS 写入、验证、Project Lock/受管清单提交和 `doctor --repair` 恢复流程。
15
+ `windy.modules.json` 是 project-owned 装配声明,记录 Module 名称、无版本 package
16
+ 入口、`enabled`、顺序、Admin 可见性和配置;实际 package 版本只来自
17
+ `package.json` 与 `bun.lock`。`enable/disable` 使用 CAS 事务只修改这份声明,不改
18
+ `apps/web/src`、Server Composition、Router、layout 或 package 内容。
22
19
 
23
- 模块命令要求项目的 `windyVersion`、`generatorVersion` 与当前 CLI 的 template 版本
24
- 完全一致;旧项目先运行 `bun run windy update`。这样目录、recipe 和目标模板不会跨
25
- 版本混用。
20
+ 启用会递归启用已经安装的依赖;依赖缺失、版本不兼容、冲突、核心模块停用或仍有启用
21
+ 模块依赖待停用模块时,在写入前拒绝。`system.settings` 的 Admin Route 引用
22
+ `system.admin`,`system.storage` 承载 Settings 声明的公共品牌文件 Route,
23
+ `system.data-governance` 也向其它 Feature 贡献治理能力;三者都不能
24
+ 只靠运行时开关安全停用,因此 `disable` 会 fail closed。部署后需要排除它们时必须由
25
+ 项目执行人工物理迁移,不能把修改 registry 当作已卸载。
26
26
 
27
- `add` 与 `remove` 是幂等操作:集合已满足时不启动事务。`update` 不改变集合,用当前
28
- CLI 重建所选模块并应用目录声明的更新 recipe。正式操作只处理计划涉及的文件,不检查
29
- 无关 Git 工作区改动;客户修改过的受管文件或模块 project-owned 扩展点会形成冲突,
30
- 不会被静默覆盖。
27
+ ## 创建期模块与部署期安装的边界
31
28
 
32
- ## 数据处置
29
+ 当前目录中的 15 个平台模块和示例 `work-order` 都是 source/template distribution:
30
+ 它们在首次创建时通过 `--module`、`--all-modules`、`--no-optional-modules` 或
31
+ `--no-example` 物理组合。其文件仍包含 project-owned Web、Server Composition、
32
+ Repository 或业务源码,不是可以在已部署项目中独立落盘的 self-contained package。
33
33
 
34
- 卸载首先输出数据处置计划。`--dry-run` 只展示文件和冲突;正式卸载必须显式传入
35
- `--confirm-data-disposition`。
34
+ 因此对这些内置模块执行下列命令时,CLI 会在生成更新计划和写项目之前 fail closed:
36
35
 
37
- | 策略 | 行为 |
38
- | -------------- | ------------------------------------------------------------------ |
39
- | `retain-data` | 默认;保留业务表与记录,不执行 down migration |
40
- | `archive-data` | 目录要求外部先归档;通过 `--data-plan <文件>` 绑定已审阅的处置文档 |
41
- | `manual` | 目录要求人工决定;同样必须绑定处置文档 |
42
-
43
- CLI 不连接生产数据库,确认参数表示部署操作人已完成并审阅相应动作,不代表 Windy
44
- 自动执行 SQL 或数据库归档。即使策略名为 `archive-data`,CLI 对数据库本身仍是
45
- retain:数据删除或归档由处置文档约束的外部运维流程负责。目录策略只能被提高,不能
46
- 从 `archive-data` 或 `manual` 降为 `retain-data`。
36
+ ```bash
37
+ bun run windy module add <built-in-module>
38
+ bun run windy module update <built-in-module>
39
+ bun run windy module remove <built-in-module>
40
+ ```
47
41
 
48
- 卸载不会删除已发布 migration。模块自有 migration 源文件按原相对路径保存到
49
- `.windy/module-history/<module>/`;模块重装时历史仍保留,数据库运行期继续以
50
- `windy_migration_history` 的 migration id 与 checksum 判定,不会因为代码重装而
51
- 重复执行已应用迁移。历史目录受清单 hash 保护,缺失登记、符号链接或内容修改都会
52
- fail closed。
42
+ 重复 add 已安装模块、重复 remove 未安装模块仍按幂等 no-op 处理;只有实际集合会变化
43
+ 时才检查部署期能力。这不能作为安装、更新或卸载已经交付的证据。
53
44
 
54
- ## 物理残留边界
45
+ `ModuleDistributionManifest.deploymentInstall` 只有在模块完成以下闭环后才能标为
46
+ `supported`:
55
47
 
56
- 目标组合会重新物化 package、workspace、宿主 Adapter、Composition、Module
57
- Manifest、Feature、Permission、菜单、任务、Provider、Tool、Schema 和文档。卸载后
58
- 这些运行时或生成期引用不得残留;允许保留的只有:
48
+ - package 提供 browser-safe `./admin`、canonical `./manifest` 与
49
+ self-contained 根 `windyServerModule`;
50
+ - 安装只更新依赖图和 `windy.modules.json`,不改写 project-owned Web/Server 源码;
51
+ - Manifest、Host range、依赖闭包和实现完整性在安装、构建与监听前均 fail closed;
52
+ - 数据处置、不可改写 migration、失败恢复和重复操作有真实部署回归证据。
59
53
 
60
- - 数据库中的业务数据与 migration ledger;
61
- - `.windy/module-history/` 下的不可执行迁移证据;
62
- - `.windy/reports/` 下的生命周期报告。
54
+ 当前目录没有 `deploymentInstall: "supported"` 的模块,所以没有 H3 模块,也不支持
55
+ 任意 npm package 名称的 H3 安装。需要客户 Repository 或项目运行时 attachment 的模块
56
+ 只能在 `apps/server/src/app/server-composition.ts` 由项目显式装配。
63
57
 
64
- 如果其它已安装模块仍依赖待卸载模块,计划在写文件前拒绝。`system` 与
65
- `system.identity` 是 profile 核心,不支持卸载。
58
+ ## 数据与升级原则
66
59
 
67
- ## H3 证据
60
+ 部署期物理卸载将来仍必须默认保留数据,不执行 down migration;`archive-data` 或
61
+ `manual` 只能绑定已审阅的外部运维计划,CLI 不连接生产数据库,也不能声称已自动
62
+ 归档。已发布 Migration Bundle 的 ID 与 checksum 必须保留。
68
63
 
69
- 通用 CLI 的存在不自动提升所有模块可靠度。`work-order` 的 H3 回归从纯核心项目开始,
70
- 覆盖安装、重复安装、更新、保留数据卸载、重复卸载和重装,并检查 package、
71
- Project Lock 与 migration history artifact。其它模块保持 H2,直到分别通过同等级的
72
- 安装、数据库、Server、卸载残留和恢复门禁。
64
+ 普通 `windy update` 只升级平台依赖、受管 Wrapper 和兼容运行内核,不重写
65
+ `apps/web/src` 或 `apps/server/src/app`。模块协议不兼容时应
66
+ 诊断并停止,不能用模板覆盖客户 Composition 来制造成功。
@@ -35,7 +35,8 @@ PUT /api/system/menus/:id/placement
35
35
 
36
36
  新增页面时先在所属 Module Manifest 用同一能力 Key 注册 Feature、权限、菜单和 API 绑定,再新增 Router 页面。Router 契约测试会比较所有 `type=menu` 的 Manifest 路径与 Web 路由,避免出现“菜单有入口但页面不存在”或“页面绕过注册表”的漂移。
37
37
 
38
- 客户业务页面不直接修改 shared-scaffold Router 或侧栏。页面实现注册到
39
- project-owned 的 `apps/web/src/app/business-routes.ts`,平台 Router Adapter 统一补齐
40
- Feature/RBAC Guard;侧栏仍从服务端 Manifest 快照生成。完整接入与升级边界见
41
- [project-owned 业务 Web 路由](../development/business-web-routes.md)。
38
+ 业务 package 的 browser-safe `./admin` 提供 Manifest 窄投影与默认页面,项目可在
39
+ project-owned `apps/web/src/app/web-composition.ts` 按 route key 覆盖 loader 和呈现;
40
+ 路径与 Feature/RBAC Guard 不在 Web 重复声明。侧栏仍从服务端 Manifest 快照生成。完整
41
+ 接入与升级边界见
42
+ [project-owned Web 与 Admin UI 组合](../development/business-web-routes.md)。
@@ -27,7 +27,7 @@
27
27
  "generate:crud": "bun run packages/crud-generator/src/cli.ts",
28
28
  "create:windy": "bun run packages/create-windy/src/cli.ts",
29
29
  "test:create-windy": "bun run packages/create-windy/src/regression.ts",
30
- "build": "bun run --cwd apps/web build",
30
+ "build": "bun run --cwd apps/server build && bun run --cwd apps/web build",
31
31
  "build:license": "bun run --cwd apps/license build:web",
32
32
  "db:generate": "drizzle-kit generate --config drizzle.config.ts",
33
33
  "db:migrate": "drizzle-kit migrate --config drizzle.config.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@southwind-ai/config",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "engines": {
@@ -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": {
@@ -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
+ });