create-windy 0.9.1 → 0.10.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 (253) hide show
  1. package/dist/cli.js +1691 -1151
  2. package/package.json +2 -2
  3. package/template/.agents/skills/windy-business-module/SKILL.md +33 -1
  4. package/template/.windy-template.json +6 -2
  5. package/template/AGENTS.md +4 -0
  6. package/template/README.md +2 -0
  7. package/template/apps/agent-server/Dockerfile +1 -1
  8. package/template/apps/agent-server/README.md +1 -1
  9. package/template/apps/agent-server/native/EXTENSIONS.md +2 -2
  10. package/template/apps/agent-server/native/parallel-harness.ts +2 -0
  11. package/template/apps/agent-server/native/storage-lifecycle.test.ts +2 -1
  12. package/template/apps/agent-server/package.json +42 -41
  13. package/template/apps/agent-server/plugins/downloads/README.md +1 -1
  14. package/template/apps/agent-server/plugins/drop-overlay/README.md +1 -1
  15. package/template/apps/agent-server/plugins/settings/README.md +1 -1
  16. package/template/apps/agent-server/plugins/skill-library/client-chunks.test.ts +4 -1
  17. package/template/apps/agent-server/plugins/theme/README.md +5 -1
  18. package/template/apps/agent-server/plugins/theme/source-tokens.ts +0 -1
  19. package/template/apps/agent-server/plugins/theme/tokens.json +0 -4
  20. package/template/apps/agent-server/plugins/tool-catalog/README.md +6 -3
  21. package/template/apps/agent-server/plugins/tool-catalog/discovery.mjs +13 -10
  22. package/template/apps/agent-server/plugins/tool-catalog/discovery.test.ts +68 -6
  23. package/template/apps/agent-server/runtime/compatibility.ts +7 -1
  24. package/template/apps/agent-server/runtime/dsh-engine.ts +2 -0
  25. package/template/apps/agent-server/runtime/questions-workflow.nodecheck.ts +2 -0
  26. package/template/apps/server/Dockerfile +2 -1
  27. package/template/apps/server/package.json +11 -11
  28. package/template/apps/server/scripts/validate-module-composition-once.ts +2 -0
  29. package/template/apps/server/scripts/validate-module-composition.ts +8 -4
  30. package/template/apps/server/src/admin/routes.test.ts +29 -1
  31. package/template/apps/server/src/admin/routes.ts +20 -2
  32. package/template/apps/server/src/agent/client-session/completion.ts +1 -1
  33. package/template/apps/server/src/agent/client-session/manifest-admission.ts +1 -1
  34. package/template/apps/server/src/agent/client-session/request.ts +1 -1
  35. package/template/apps/server/src/agent/context-version-routes.ts +37 -13
  36. package/template/apps/server/src/agent/run-facade.ts +1 -1
  37. package/template/apps/server/src/agent/run-input-verification.ts +1 -1
  38. package/template/apps/server/src/agent/run-routes.ts +1 -14
  39. package/template/apps/server/src/agent/run-start-preparation.ts +1 -1
  40. package/template/apps/server/src/agent/runtime-bootstrap-input.ts +2 -0
  41. package/template/apps/server/src/agent/runtime-bootstrap.ts +49 -157
  42. package/template/apps/server/src/agent/runtime-disabled-composition.ts +2 -1
  43. package/template/apps/server/src/agent/session-invalidation/authorization-revocation.ts +54 -0
  44. package/template/apps/server/src/agent/session-runtime-composition.ts +1 -51
  45. package/template/apps/server/src/agent/unavailable-routes.ts +13 -2
  46. package/template/apps/server/src/agent-workflow/runtime-composition.ts +3 -0
  47. package/template/apps/server/src/agent-workflow/service.ts +17 -1
  48. package/template/apps/server/src/ai-artifact/media-resource-route.test.ts +134 -0
  49. package/template/apps/server/src/ai-artifact/routes.ts +1 -1
  50. package/template/apps/server/src/ai-artifact/runtime.ts +12 -0
  51. package/template/apps/server/src/ai-runtime-readiness/operation-port.test-support.ts +1 -1
  52. package/template/apps/server/src/ai-runtime-readiness/operation-port.ts +8 -0
  53. package/template/apps/server/src/ai-runtime-security/internal-token.ts +14 -0
  54. package/template/apps/server/src/app/antivirus-composition.test.ts +63 -0
  55. package/template/apps/server/src/app/antivirus-composition.ts +29 -0
  56. package/template/apps/server/src/app/dsh-workbench-socket-audit.test.ts +2 -1
  57. package/template/apps/server/src/configuration/definition.ts +15 -11
  58. package/template/apps/server/src/document/facade-support.ts +2 -2
  59. package/template/apps/server/src/document/facade.ts +2 -2
  60. package/template/apps/server/src/document/output-artifact-composition.ts +9 -5
  61. package/template/apps/server/src/document/output-artifact.test-support.ts +1 -1
  62. package/template/apps/server/src/embedding/facade.ts +1 -1
  63. package/template/apps/server/src/embedding/input.ts +1 -1
  64. package/template/apps/server/src/embedding/routes.ts +1 -1
  65. package/template/apps/server/src/index.ts +60 -60
  66. package/template/apps/server/src/license/routes.ts +4 -1
  67. package/template/apps/server/src/license/runtime-business-access.test.ts +261 -0
  68. package/template/apps/server/src/media-runtime/composition.ts +157 -0
  69. package/template/apps/server/src/media-runtime/media-only-composition.ts +44 -0
  70. package/template/apps/server/src/media-runtime/module-ai-port-factory.test.ts +69 -0
  71. package/template/apps/server/src/media-runtime/module-ai-port-factory.ts +72 -0
  72. package/template/apps/server/src/{agent → media-runtime}/runtime-readiness-sources.ts +14 -6
  73. package/template/apps/server/src/media-runtime/shared-composition.ts +104 -0
  74. package/template/apps/server/src/module-composition.test.ts +22 -0
  75. package/template/apps/server/src/module-composition.ts +48 -0
  76. package/template/apps/server/src/module-host/ai-operation-port.ts +97 -30
  77. package/template/apps/server/src/module-host/ai-operation-speech-normalize.test.ts +1 -1
  78. package/template/apps/server/src/module-host/http-runtime.ts +9 -3
  79. package/template/apps/server/src/module-host/static-route-authorization.test.ts +6 -3
  80. package/template/apps/server/src/module-host/storage-authorization-binding.test.ts +4 -0
  81. package/template/apps/server/src/module-runtime-validation.ts +2 -0
  82. package/template/apps/server/src/observability/diagnostic-index.ts +26 -1
  83. package/template/apps/server/src/observability/diagnostic-repository.ts +40 -11
  84. package/template/apps/server/src/platform-http-runtime.ts +1 -1
  85. package/template/apps/server/src/runtime-feature.ts +5 -1
  86. package/template/apps/server/src/runtime.ts +3 -0
  87. package/template/apps/server/src/settings/drizzle-watermark-repository.ts +35 -4
  88. package/template/apps/server/src/settings/watermark-repository.ts +14 -0
  89. package/template/apps/server/src/settings/watermark-routes.test.ts +84 -2
  90. package/template/apps/server/src/settings/watermark-routes.ts +109 -0
  91. package/template/apps/server/src/settings/watermark-transfer.ts +79 -0
  92. package/template/apps/server/src/speech/facade-validation.ts +1 -1
  93. package/template/apps/server/src/speech/facade.ts +2 -2
  94. package/template/apps/server/src/startup-composition-validation.test.ts +32 -0
  95. package/template/apps/server/src/startup-finalization.ts +2 -0
  96. package/template/apps/server/src/startup-runtime.ts +77 -0
  97. package/template/apps/server/src/system/ai-runtime-build.ts +3 -0
  98. package/template/apps/server/src/system/ai-runtime-configuration.test.ts +36 -0
  99. package/template/apps/server/src/system/ai-runtime-configuration.ts +58 -0
  100. package/template/apps/server/src/system/diagnostic-routes.test.ts +45 -0
  101. package/template/apps/server/src/system/diagnostic-routes.ts +35 -0
  102. package/template/apps/server/src/system/environment-policy-reader.test.ts +74 -0
  103. package/template/apps/server/src/system/environment-policy-reader.ts +62 -0
  104. package/template/apps/server/src/system/feature-environment-assessment.test.ts +148 -0
  105. package/template/apps/server/src/system/feature-environment-assessment.ts +179 -0
  106. package/template/apps/server/src/system/host-diagnostics.test.ts +155 -0
  107. package/template/apps/server/src/system/host-diagnostics.ts +225 -0
  108. package/template/apps/server/src/system/menu-registry.test.ts +26 -0
  109. package/template/apps/server/src/system/menu-registry.ts +9 -7
  110. package/template/apps/server/src/system/operations.test.ts +5 -0
  111. package/template/apps/server/src/system/operations.ts +36 -12
  112. package/template/apps/server/src/system/role-authorization-postgres.integration.test.ts +181 -0
  113. package/template/apps/web/Dockerfile +7 -3
  114. package/template/apps/web/dev/dev-port.test.ts +6 -6
  115. package/template/apps/web/dev/dev-port.ts +1 -1
  116. package/template/apps/web/dev/start.ts +6 -1
  117. package/template/apps/web/index.html +1 -0
  118. package/template/apps/web/package.json +16 -16
  119. package/template/apps/web/runtime-server.test.ts +22 -1
  120. package/template/apps/web/runtime-server.ts +6 -0
  121. package/template/apps/web/src/App.vue +6 -3
  122. package/template/apps/web/src/assets/windy.svg +4 -0
  123. package/template/apps/web/src/components/auth/LoginForm.vue +2 -2
  124. package/template/apps/web/src/components/auth/PasswordChangeForm.vue +14 -3
  125. package/template/apps/web/src/components/common/FormDialog.vue +1 -1
  126. package/template/apps/web/src/components/license/LicenseFlow.webtest.ts +3 -0
  127. package/template/apps/web/src/components/license/LicenseMachineCodeQr.vue +99 -0
  128. package/template/apps/web/src/components/license/LicenseMachineCodeQr.webtest.ts +83 -0
  129. package/template/apps/web/src/components/license/LicensePairingCard.vue +1 -1
  130. package/template/apps/web/src/components/license/LicenseStatusCard.vue +14 -2
  131. package/template/apps/web/src/composables/useLoginFlow.ts +2 -2
  132. package/template/apps/web/src/composables/usePlatformSettings.ts +1 -1
  133. package/template/apps/web/src/composables/useWatermarkSettings.ts +1 -0
  134. package/template/apps/web/src/layout/AppSidebar.vue +5 -9
  135. package/template/apps/web/src/layout/AppSidebar.webtest.ts +4 -1
  136. package/template/apps/web/src/pages/auth/ChangePasswordPage.vue +67 -6
  137. package/template/apps/web/src/pages/auth/LoginPage.vue +3 -12
  138. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.vue +30 -0
  139. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationWorkbench.vue +25 -14
  140. package/template/apps/web/src/pages/configuration/components/TimeZoneSelect.vue +55 -0
  141. package/template/apps/web/src/pages/profile/PersonalSettingsPage.vue +64 -11
  142. package/template/apps/web/src/pages/profile/PersonalSettingsPage.webtest.ts +21 -1
  143. package/template/apps/web/src/pages/settings/BrandingSettingsSection.vue +7 -18
  144. package/template/apps/web/src/pages/settings/BrandingSettingsSection.webtest.ts +9 -4
  145. package/template/apps/web/src/pages/settings/GlobalSettingsPage.vue +51 -6
  146. package/template/apps/web/src/pages/settings/GlobalSettingsPage.webtest.ts +19 -3
  147. package/template/apps/web/src/pages/settings/WatermarkSettingsSection.vue +1 -0
  148. package/template/apps/web/src/pages/settings/WatermarkYamlTransfer.vue +211 -0
  149. package/template/apps/web/src/pages/system/DiagnosticLookupPage.vue +18 -3
  150. package/template/apps/web/src/pages/system/DiagnosticLookupPage.webtest.ts +4 -1
  151. package/template/apps/web/src/pages/system/SystemOperationsPage.vue +23 -0
  152. package/template/apps/web/src/pages/system/components/DiagnosticLogList.vue +120 -0
  153. package/template/apps/web/src/pages/system/components/OperationEnvironmentPanel.vue +237 -0
  154. package/template/apps/web/src/pages/system/components/OperationFeatureAssessmentPanel.vue +131 -0
  155. package/template/apps/web/src/pages/system/components/PagedSelectionList.vue +31 -24
  156. package/template/apps/web/src/pages/system/components/SystemResourceActions.vue +2 -1
  157. package/template/apps/web/src/pages/system/components/SystemResourceEditor.vue +11 -2
  158. package/template/apps/web/src/pages/system/components/SystemResourcePage.vue +1 -0
  159. package/template/apps/web/src/pages/system/components/SystemResourcePage.webtest.ts +6 -2
  160. package/template/apps/web/src/pages/system/components/agent-skills/AgentSkillImportDialog.vue +18 -9
  161. package/template/apps/web/src/pages/system/components/agent-workbench/SessionReplayTimeline.vue +18 -9
  162. package/template/apps/web/src/pages/system/components/agent-workbench/TestRunComposer.vue +66 -34
  163. package/template/apps/web/src/pages/system/components/agent-workbench/ToolCatalogPanel.vue +48 -33
  164. package/template/apps/web/src/pages/system/components/agent-workbench/ToolCatalogPanel.webtest.ts +9 -2
  165. package/template/apps/web/src/pages/system/components/agent-workbench/ToolRuleDetail.vue +23 -16
  166. package/template/apps/web/src/pages/system/components/dictionary-bulk/DictionaryBulkJobDetail.vue +10 -7
  167. package/template/apps/web/src/pages/system/components/user-account/RolePermissionSummary.vue +81 -57
  168. package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.vue +6 -4
  169. package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.webtest.ts +20 -4
  170. package/template/apps/web/src/pages/system/resource-config.ts +9 -0
  171. package/template/apps/web/src/pages/work-orders/WorkOrderListPage.vue +1 -1
  172. package/template/apps/web/src/router/disabled-feature-loading.webtest.ts +78 -0
  173. package/template/apps/web/src/router/index.ts +3 -6
  174. package/template/apps/web/src/router/manifest-routes.ts +2 -3
  175. package/template/apps/web/src/router/platform-admin-pages.ts +21 -62
  176. package/template/apps/web/src/router/platform-admin-route-bindings.ts +4 -4
  177. package/template/apps/web/src/services/diagnostics-api.ts +13 -1
  178. package/template/apps/web/src/services/operations-api.ts +4 -0
  179. package/template/apps/web/src/services/platform-settings-api.ts +35 -1
  180. package/template/apps/web/vite.config.ts +9 -3
  181. package/template/deploy/environment/windy.environment.example.yaml +53 -0
  182. package/template/docker-compose.yml +4 -2
  183. package/template/docs/architecture/agent-admin-workbench-implementation.md +1 -1
  184. package/template/docs/architecture/agent-admin-workbench-plan.md +1 -1
  185. package/template/docs/architecture/agent-workflow.md +8 -0
  186. package/template/docs/architecture/durable-jobs.md +17 -0
  187. package/template/docs/architecture/embedded-dsh-integration.md +1 -1
  188. package/template/docs/development/dsh-plugins.md +1 -1
  189. package/template/docs/development/performance-engineering.md +24 -0
  190. package/template/docs/operations/admin-settings-yaml.md +29 -0
  191. package/template/docs/operations/dsh-agent-migration.md +2 -2
  192. package/template/docs/operations/environment-capabilities.md +185 -0
  193. package/template/docs/platform/module-configuration.md +18 -3
  194. package/template/docs/platform/platform-authentication.md +1 -1
  195. package/template/package.json +10 -8
  196. package/template/packages/config/package.json +2 -2
  197. package/template/packages/config/src/definition.ts +14 -4
  198. package/template/packages/config/src/environment-loader.ts +27 -3
  199. package/template/packages/config/src/platform-ai-runtime.test.ts +94 -0
  200. package/template/packages/config/src/platform.ts +5 -2
  201. package/template/packages/database/drizzle/0065_durable_job_lease_snapshot_lookup.sql +1 -0
  202. package/template/packages/database/drizzle/0066_agent_source_sequence_bigint.sql +3 -0
  203. package/template/packages/database/drizzle/0067_media_runtime_access.sql +46 -0
  204. package/template/packages/database/drizzle/meta/0065_snapshot.json +15370 -0
  205. package/template/packages/database/drizzle/meta/0066_snapshot.json +15370 -0
  206. package/template/packages/database/drizzle/meta/0067_snapshot.json +15370 -0
  207. package/template/packages/database/drizzle/meta/_journal.json +21 -0
  208. package/template/packages/database/package.json +3 -3
  209. package/template/packages/database/src/schema/agent-session.ts +4 -3
  210. package/template/packages/database/src/schema/durable-jobs.ts +4 -0
  211. package/template/packages/database/src/schema/index.ts +3 -1
  212. package/template/packages/database/src/schema-durable-recovery.test.ts +21 -1
  213. package/template/packages/database/src/schema-source-sequence.test.ts +24 -0
  214. package/template/packages/example-work-order/package.json +2 -2
  215. package/template/packages/modules/package.json +1 -1
  216. package/template/packages/modules/src/admin-ui-boundary.test.ts +33 -0
  217. package/template/packages/modules/src/composition.ts +11 -28
  218. package/template/packages/modules/src/manifest.ts +2 -0
  219. package/template/packages/modules/src/menu-composition.ts +24 -0
  220. package/template/packages/modules/src/system-admin-routes.ts +2 -2
  221. package/template/packages/modules/src/system-admin-ui-catalog.ts +6 -1
  222. package/template/packages/modules/src/system-ai-api-bindings.ts +0 -16
  223. package/template/packages/modules/src/system-api-control-plane.ts +9 -1
  224. package/template/packages/modules/src/system-api-permissions.ts +7 -0
  225. package/template/packages/modules/src/system-api-watermark.ts +21 -0
  226. package/template/packages/modules/src/system-audit-actions.ts +3 -1
  227. package/template/packages/modules/src/system-features.ts +21 -3
  228. package/template/packages/modules/src/system-media-api-bindings.ts +97 -0
  229. package/template/packages/modules/src/system-module-audit-selection.ts +2 -0
  230. package/template/packages/modules/src/system-module-catalog.test.ts +13 -6
  231. package/template/packages/modules/src/system-module-catalog.ts +6 -241
  232. package/template/packages/modules/src/system-module-definitions.ts +260 -0
  233. package/template/packages/modules/src/system-modules-access.test.ts +2 -0
  234. package/template/packages/modules/src/system-modules.test.ts +7 -1
  235. package/template/packages/modules/src/system-modules.ts +25 -13
  236. package/template/packages/modules/src/system-permissions.ts +12 -0
  237. package/template/packages/server-sdk/package.json +1 -1
  238. package/template/packages/shared/index.ts +3 -0
  239. package/template/packages/shared/package.json +1 -1
  240. package/template/packages/shared/src/environment-assessment.ts +68 -0
  241. package/template/packages/shared/src/environment-policy.ts +205 -0
  242. package/template/packages/shared/src/license-console-capabilities.ts +7 -0
  243. package/template/packages/shared/src/license.ts +1 -0
  244. package/template/packages/shared/src/module-configuration.ts +2 -1
  245. package/template/scripts/operations/collect-device-info.py +230 -0
  246. package/template/apps/server/src/agent/module-ai-port-factory.ts +0 -41
  247. /package/template/apps/server/src/{agent → ai-runtime-security}/execution-grants.ts +0 -0
  248. /package/template/apps/server/src/{agent → ai-runtime-security}/idempotency-key.test.ts +0 -0
  249. /package/template/apps/server/src/{agent → ai-runtime-security}/idempotency-key.ts +0 -0
  250. /package/template/apps/server/src/{agent → ai-runtime-security}/provider-input-governance.test.ts +0 -0
  251. /package/template/apps/server/src/{agent → ai-runtime-security}/provider-input-governance.ts +0 -0
  252. /package/template/apps/server/src/{agent → media-runtime}/required-environment.ts +0 -0
  253. /package/template/apps/server/src/{agent → media-runtime}/runtime-readiness-sources.test.ts +0 -0
@@ -153,12 +153,16 @@ Lease 或 timeout sweep 会终结旧 attempt 并写 Failure;只有 `currentAtt
153
153
 
154
154
  这是一条渐进迁移边界:新业务应直接注册 durable handler,不再新增另一套 `running` 状态机;Scheduler/Bulk Adapter 只服务现有接口兼容。
155
155
 
156
+ <!-- @windy-module system.workflow begin -->
157
+
156
158
  ## 审批超时接入
157
159
 
158
160
  最小审批注册稳定 handler `system.workflow.timeout` 和独立 queue。创建审批时以 WorkItem ID 作为提交幂等键,把 Job 的 `availableAt` 固定为审批 `dueAt`;payload 只保存 WorkItem ID。handler 到期后重新读取当前 WorkItem 与版本,只有 `pending` 或 `claimed` 才执行服务账号专用的 `timeout` 命令,已终结任务重复执行为空操作。
159
161
 
160
162
  启动与每次派发前会分页扫描尚未终结的审批并幂等补交 Job,用于覆盖“审批事实已提交、进程在 durable submit 前退出”的短窗口。调度器只负责唤醒派发,真实任务、可用时间、重试和执行结果都在 Durable Job 表中;新实例重建 Repository、Registry 和 Worker 后可以继续领取,不依赖旧进程 timer 或闭包。完整审批边界见[最小审批工作流](./minimal-approval-workflow.md)。
161
163
 
164
+ <!-- @windy-module system.workflow end -->
165
+
162
166
  ## 运维与验证
163
167
 
164
168
  事实模型、执行恢复与兼容关联分别使用 `0023_fresh_thunderbolt_ross.sql`、
@@ -182,3 +186,16 @@ PostgreSQL Repository 集成测试通过 `JOBS_TEST_DATABASE_URL` 启用,覆
182
186
  - token 原文只允许短暂存在于 worker 进程,持久化层保存 SHA-256;数据库泄露不能直接伪造心跳或完成请求。
183
187
  - 自动重试有界,但人工恢复可产生新的 attempt;每次人工恢复都需要理由和幂等请求号,由运维/RBAC/审计入口在接入层约束。
184
188
  - 当前使用 PostgreSQL 作为 durable runtime 的并发真相。其它数据库必须实现同等 claim/lease 语义后才能声明兼容。
189
+
190
+ ## 租约快照索引
191
+
192
+ 快照按 `job_id` 读取全部历史租约,并按 `acquired_at` 排序。迁移
193
+ `0065_durable_job_lease_snapshot_lookup` 增加 `(job_id, acquired_at)` 索引。
194
+ 阶段活跃租约唯一约束、到期索引、历史状态和 fencing 契约继续由现有逻辑维护。
195
+ 生成项目未选择 `system.scheduler` 时,模板裁剪会随租约表移除该索引 DDL。
196
+
197
+ 在隔离的 PostgreSQL 18.3 实例中,使用平台租约表 DDL 和 20 万条合成租约,
198
+ 执行 `EXPLAIN (ANALYZE, BUFFERS)` 检查按单个任务读取完整快照。应用迁移前为并行全表扫描,
199
+ 应用后命中新增索引并对 20 行结果排序,结果保留 active、expired 和 released 状态。
200
+ `pg_index` 确认索引 valid、ready。实际部署按正式迁移执行,大表安排维护窗口,
201
+ 并在目标数据库用代表性任务 ID 复核执行计划、等待事件和历史状态。
@@ -8,7 +8,7 @@
8
8
 
9
9
  ## 实现与职责
10
10
 
11
- Agent Server 是 Node 24.7+ 与 TypeScript workspace,固定官方 DSH `0.2.1-rc.2` 和 Cordis `4.0.5`。Bun 管理依赖及构建;DSH 原生插件安装器需要的 pnpm 随同镜像交付。通过公开 AgentLoop、Skill Provider、MCP、生命周期及 UI slot 接入,没有自维护的 DSH 内核。
11
+ Agent Server 是 Node 24.9+ 与 TypeScript workspace,使用官方 DSH `~0.2.2-rc.1` 和 Cordis `^4.1.0`,实际版本由 Bun 锁文件确定。Bun 管理依赖及构建;DSH 原生插件安装器需要的 pnpm 随同镜像交付。通过公开 AgentLoop、Skill Provider、MCP、生命周期及 UI slot 接入,没有自维护的 DSH 内核。
12
12
 
13
13
  | 所有者 | 职责 |
14
14
  | -------------- | --------------------------------------------------------------------------------------------------------- |
@@ -1,6 +1,6 @@
1
1
  # DSH 插件与项目接入
2
2
 
3
- 运行基线固定为官方DSH `0.2.1-rc.2`、Cordis `4.0.5`和Node 24.7+。不要把本次回迁同时当作上游升级。官方依赖通过Bun与锁文件安装;不修改node_modules或复制上游内核。
3
+ 运行基线为官方 DSH `~0.2.2-rc.1`、Cordis `^4.1.0` 和 Node 24.9+。所有直接依赖的 DSH 包使用同一版本范围,升级时一起更新。官方依赖通过Bun与锁文件安装;不修改node_modules或复制上游内核。
4
4
 
5
5
  ## 项目扩展
6
6
 
@@ -0,0 +1,24 @@
1
+ # 性能开发与验收
2
+
3
+ ## 启动与模块装配
4
+
5
+ - 浏览器通过 `@southwind-ai/modules/admin-ui` 读取路由目录,模块名称和版本来自无运行时导入的 `system-module-definitions.ts`。服务端完整 Manifest 从同一文件装配权限、审计、API 和 Agent。新增浏览器入口时检查 tree-shaking 前的解析图,开发模式会请求静态依赖。
6
+ - Router 使用原生路由 loader,受保护的管理页面在认证、Feature、Permission 和 License 守卫允许后解析。公共入口的主题从 `main.ts` 引入 `style.css`,应用布局和管理壳按实际布局加载。品牌主题若由项目扩展提供,由公开页面或公共入口显式导入。为登录和公开错误页验收浅色与深色。
7
+ - 生成时未选择的模块应同时裁剪源码、路由绑定、依赖和相应数据库对象。禁用的外部模块包从 Vite 虚拟 Admin 注册表排除。内置模块在通用产物中仍可能有异步 chunk。运行时关闭 Feature 或拒绝权限应阻止导航加载和隐藏入口,浏览器已取得的字节仍可访问。
8
+ - 使用生产构建 manifest 检查 HTML 预加载、递归静态 imports 和所有输出 chunk。物理裁剪需要同时检查动态入口和产物文件。开发模式另查浏览器请求或预摇树解析图。Secret、内部执行策略和敏感算法留在服务端,客户端只拿当前用户授权的最小投影。服务端逐请求校验 Feature、RBAC、License 和 Scope。
9
+ - 可选 Copilot、图表和 PageAgent 按功能启用、有效权限和实际使用时机加载。异步导入完成后复核会话、权限、取消信号与页面范围再创建控制器。撤权或离开作用域时卸载和停止运行。授权或网络快照失败沿用现有恢复路径。
10
+
11
+ ## 数据与交互
12
+
13
+ - 默认读取当前展示投影,编辑时才补全。保存保留未展示的配置与版本约束。提交等价数据时保留未变化对象的引用,避免重建图表。
14
+ - 有合法快照时先展示并后台刷新,已展示内容保持可操作。缓存绑定身份与权限,退出、身份变更及 401/403 时失效。网络与缓存读取可并行,迟到缓存或旧会话请求不得覆盖新结果。敏感会话快照复用项目加密和有效期机制,普通网络失败沿用页面既有恢复契约。
15
+ - 图谱的结构、照片与选择分别更新。用绘图库公开的局部更新接口保持视口,菜单打开时才计算菜单模型,读取已绘制 ID。几何缓存限定在一次布局或转换,验证 Unicode、换行、字体变化与导出结果。
16
+ - 骨架、指标和时间戳预留空间,未知值显示“—”。同时验收对比度、键盘焦点、标签和点击区域。过期的 Vite 构建资源在 `/assets/` 返回 404,应用深链接继续进入 SPA。
17
+ - 测试断言失败时追溯字段所有者、当前 API 和引入字段的历史。使用独立预期字段、实体 ID 和关系断言覆盖完整投影,避免为通过测试而删字段。
18
+
19
+ ## 可复现的验收
20
+
21
+ 1. 在独立目录构建并冻结候选产物,记录提交和构建标识、真实路由、数据规模、权限、设备、视口、节流、扩展、冷缓存和热缓存条件。
22
+ 2. 覆盖匿名登录和公开错误页、已登录首页、实际管理页面以及安装了可选模块的代表性交互。检查 LCP 对应业务内容,记录代表性操作的 Event Timing 和 INP。桌面目标为 LCP < 2 秒、INP < 300 ms、Lighthouse Performance、Accessibility、Best Practices、SEO 各 ≥ 96。分别保存 Lighthouse 的 TBT 与交互测量。
23
+ 3. 保留失败历史并标明错路由、运行时错误和不完整样本,按最近有效样本报告。区分本地短会话指标与生产分位数。Windy 的验收使用自身产物和数据。
24
+ 4. Windy 源仓库的执行层级见 `docs/agents/testing-gates.md`。生成项目使用自身的验收门禁。浏览器测量按项目授权执行。
@@ -0,0 +1,29 @@
1
+ # Admin 配置 YAML 迁移
2
+
3
+ 在源实例打开 **Admin → 全局设置 → 配置迁移**,保存配置后点击 **导出 YAML**。
4
+ 在客户实例打开同一页面,选择 **导入 YAML**,上传文件并查看校验结果及当前值和导入值。
5
+ 确认差异后点击 **确认导入**,页面会刷新水印表单与当前会话的水印设置。
6
+ 如果目标配置在预览之后发生变化,请重新选择文件获取预览。
7
+
8
+ 版本 1 支持以下水印字段。
9
+
10
+ ```yaml
11
+ version: 1
12
+ watermark:
13
+ enabled: true
14
+ density: medium
15
+ contentFields:
16
+ - username
17
+ - displayName
18
+ opacityPercent: 8
19
+ fontSize: 14
20
+ ```
21
+
22
+ `contentFields` 存放显示字段的枚举值。例如 `phone` 表示按目标实例当前登录者显示手机号,
23
+ `custom` 表示使用目标实例已有的自定义文字。文件只包含上述白名单字段。
24
+ 密钥、客户记录、账号实际字段值、品牌资源和自定义水印文字均由各实例管理。
25
+
26
+ 导出需要水印查看权限。预览和导入需要水印管理权限,均受水印功能开关控制。
27
+ 服务器拒绝未知字段、未知版本、多文档、重复键、别名、锚点、显式标签及超过 8 KiB 的文件,
28
+ 并复用水印表单的字段和值域校验。确认时重新校验文件与预览坐标,写入时检查目标基线;
29
+ 成功导入在 `config.update` 审计中标记 `source: yaml`,记录变更字段名。
@@ -15,7 +15,7 @@
15
15
 
16
16
  ## 配置与资源
17
17
 
18
- - Node要求24.7+;Linux内核必须满足Landlock完整约束及seccomp。协调器使用root切换UID,容器drop ALL后仅补SETUID、SETGID、CHOWN、DAC_OVERRIDE、KILL,启用no-new-privileges。
18
+ - Node要求24.9+;Linux内核必须满足Landlock完整约束及seccomp。协调器使用root切换UID,容器drop ALL后仅补SETUID、SETGID、CHOWN、DAC_OVERRIDE、KILL,启用no-new-privileges。
19
19
  - `AGENT_SERVER_INTERNAL_TOKEN`是内部服务认证;Provider继续使用`QWEN_API_URL/QWEN_API_KEY/QWEN_MODEL`。HTTPS Provider域名须在`AI_PROVIDER_HTTPS_HOST_ALLOWLIST`,内部结果加密使用`AGENT_INTERNAL_OUTPUT_ENCRYPTION_SECRET`。不向用户进程下发这些真实凭据。
20
20
  - 全局模型容量由`AI_PROVIDER_MAX_CONCURRENT_RUNS`控制。Compose保留1槽、0队列默认值;因此原生模型任务显示容量预留不可用。现场确认模型和内存后才能提高上限;原生请求始终保留至少1槽给既有调用链。
21
21
  - 原生任务上限`AGENT_DSH_MAX_ACTIVE_RUNS=2`,用户Worker默认2,工作区软配额512MiB,容器4GiB/2CPU/512PID。软配额不代替磁盘硬配额,WASM峰值RSS也不等于V8 heap限制。
@@ -83,7 +83,7 @@ bun run --cwd apps/agent-server test:confinement
83
83
  | 生产 Web 镜像 | Node构建和Bun生产启动通过;只读容器中用合成同容器API上游验证就绪、静态首页、原生HTTP/WS转发及伪造内部身份头剥离,不涉及浏览器。 |
84
84
  | 技能与诊断持久化 | 专用 PostgreSQL 18 容器执行0064三表迁移及真实 Repository:4项、37个断言通过,包括并发 CAS、幂等、重建连接池后的待审计恢复、停用/回滚及30天精确过期边界/有界清理。未连接业务数据库。 |
85
85
  | 技能界面与权限 | 导入包边界、共享范围、依赖、读取与变更审计、版本冲突、停用/回滚及未保存输入保护由服务/API/组件检查覆盖;没有浏览器 E2E。 |
86
- | Workflow/错误诊断 | 动态预设只读注册、当前授权与可用性说明;实际 HTTP 失败生成诊断号,只存路由模板/安全码,拒绝越权查询并审计。查询立即排除过期记录,物理清理在失败记录写入时按分钟节流、有界执行。 |
86
+ | Workflow/错误诊断 | 动态预设只读注册、当前授权与可用性说明;实际 HTTP 失败生成诊断号,只存路由模板/安全码,拒绝越权查询并审计。管理面可分页浏览最近 30 天的脱敏失败记录,也可按编号定位。两种查询复用诊断读取权限和审计,立即排除过期记录。物理清理在失败记录写入时按分钟节流、有界执行。 |
87
87
  | 非案件示例 | 真实平台 ToolRegistry 的 `system.users.list` 经真实 DSH Loop 调用1次,合成 Provider 调用2次,canonical SSE 与结构化汇总返回授权示例用户数1。没有利剑案件依赖。 |
88
88
  | 文档与下载 | Linux UID20000、无外网、只读输入下读取 `windy-office` 技能,使用镜像文档工具生成新 DOCX/XLSX 并独立回读,保留来源摘要和文本编号001;生产下载插件+真实 LocalFileSystem+HTTP 验证字节/摘要/中文文件名及匿名、跨会话、越界、软链拒绝。Session坐标和连接身份明确使用合成 fixture。 |
89
89
  | Office 预览 | 固定 WASM 将36778字节合成 DOCX 转为29653字节完整 PDF,用时2402ms,峰值RSS1527008KiB、缺失字体为空;另行回读PDF有1页且包含中文平台用户汇总。超限前拒绝、预取消、禁用后台转换均通过。 |
@@ -0,0 +1,185 @@
1
+ # 环境能力评估与构建预设
2
+
3
+ Windy 将交付内容、运行时功能开关和设备能力分开处理。没有 GPU 时,普通业务功能与 CPU
4
+ 可执行的任务仍可保留;模型或 Adapter 是否支持 CPU,需要检查实际实现与验收证据。
5
+ 环境评估只给出建议,不替管理员自动关闭功能。评估状态 `available` 只表示声明条件
6
+ 满足,不表示模型、Provider 或业务已实测可用。
7
+
8
+ 可直接复制[中文环境预设模板](../../deploy/environment/windy.environment.example.yaml),
9
+ 按实际模块与业务能力修改;模板保留共享 Runtime 的当前依赖边界。
10
+
11
+ ## 首次生成的 YAML / JSON 入口
12
+
13
+ ```sh
14
+ bunx create-windy my-app --environment-profile ./windy.environment.yaml --yes
15
+ ```
16
+
17
+ `--environment-profile` 同时接受 YAML 与 JSON,严格校验结构与模块名;输入及生成后的策略
18
+ 均不超过 128 KiB,与 Server 读取上限一致。不与 `--module`、
19
+ `--all-modules`、`--no-optional-modules` 混用。它不改变原有 `--profile private-enterprise`
20
+ 的部署档位语义。`build.modules` 走现有模块依赖解析与生成裁剪流程:必需模块及依赖自动
21
+ 补齐,未选择模块按分发清单裁剪源码、依赖、路由绑定与数据库对象。
22
+
23
+ 以下示例生成不需要 Agent 或 Document Runtime 的基础项目;它不是“带 CPU OCR”预设:
24
+
25
+ ```yaml
26
+ schemaVersion: 1
27
+ build:
28
+ modules:
29
+ - system
30
+ - system.identity
31
+ - system.configuration
32
+ - system.operations
33
+ # 环境建议单独维护,不参与模块裁剪,也不自动开关 Feature。
34
+ features: []
35
+ ```
36
+
37
+ 生成器始终发放带中文注释的 `windy.environment.yaml`,其中 `build.modules` 记录实际解析后的
38
+ 模块集合。它由项目所有者维护,后续模板升级保留客户文件;原有生成版本与选项仍由
39
+ `.windy/generation.json` 记录。修改已有项目的 YAML 不会删除已经构建的模块或安装中的
40
+ 数据库对象;需按该项目正式的生成、升级或交付流程处理。
41
+
42
+ ## 启动运行时
43
+
44
+ 配置 `runtime.agent`、`runtime.document`、`runtime.speech` 和 `runtime.embedding` 可分别
45
+ 启停 Server 装配,修改文件后重启 Server 生效。省略的开关保留现有装配行为。
46
+ `PLATFORM_ENABLE_AI_RUNTIME` 仍是总开关,`runtime.*: false` 对应的 Feature 会在 Access
47
+ Snapshot 中关闭,即使数据库 Feature 配置为开启。格式无效的环境文件会阻止 Server 启动。若总开关关闭,YAML 显式启用任一运行时也会阻止启动,避免容器正常启动但业务能力静默不可用。
48
+
49
+ 纯 CPU OCR 的 Server 配置为 `agent: false`、`document: true`,并分别选择 Speech 和
50
+ Embedding。仍须交付包含媒体代码的模块,配置 Document 端点和共享 Artifact 凭据。
51
+ 生成时可通过 `build.runtimes` 分别省去独立运行时应用源码、Compose 服务、镜像构建入口与专用卷。
52
+ `build.runtimes.*: false` 与 `runtime.*: true` 冲突时拒绝配置。显式启用且已交付的运行时
53
+ 会进入默认 Compose 启动集合,并默认开启共享运行时总开关。生成的 Compose 默认以只读方式挂载环境文件,即使首次生成
54
+ 未显式提供预设。生成后修改 YAML
55
+ 会影响 Server 重启后的装配,Compose 服务集合则由生成时的配置决定。生成时写入的 Server
56
+ 交付上限会拒绝 YAML 尝试启用已裁剪的运行时。裁掉 Agent 时,生成器也会移除 Agent 专属 Admin
57
+ 页面源码、路由和菜单声明。媒体和公共 Server 装配由 `system.media-runtime` 提供。
58
+
59
+ 使用环境预设生成项目时,初始预设写入 Project Lock。后续升级用该快照重建来源和目标模板,
60
+ 保留已裁剪的运行时应用,客户可继续维护自己的运行时 YAML。旧版 Agent 项目在解析目标模块
61
+ 依赖时会补齐 `system.media-runtime`。迁移会一次性复制原来能调用媒体 API 的角色授权,
62
+ 并沿用旧 Agent Feature 的启用、可见性和许可版本限制。已有媒体 Feature 记录优先;
63
+ 升级完成后可分别管理三个媒体 Feature 和对应角色授权。
64
+
65
+ ## 运行时评估建议
66
+
67
+ Server 通过 `WINDY_ENVIRONMENT_PROFILE_FILE` 显式指定策略文件。容器部署需将文件只读
68
+ 挂载到容器中,并配置容器内路径;只修改宿主机路径或生成文件,不会自动接通容器。
69
+ 宿主诊断报告由 `WINDY_HOST_DIAGNOSTICS_FILE` 指定,同样只读挂载;服务端读取报告,
70
+ 不会自行提权执行宿主命令。报告缺失或过期时相关环境结论保持 `unknown`,不能假定宿主
71
+ 没有 GPU,亦不能用容器自身的资源信息冒充宿主实况。
72
+
73
+ `build.modules` 在运行时仅是交付记录,不能用它替代当前 Manifest、安装清单或权限判断。
74
+
75
+ `features` 的每项针对一个真实 Manifest Feature,使用以下字段:
76
+
77
+ | 字段 | 中文语义 |
78
+ | ---------------------------------------------- | ------------------------------------------------------------------------------ |
79
+ | `key`、`title` | 正式 Feature Key 与说明名称 |
80
+ | `execution` | `none` 无执行资源要求、`cpu` CPU 执行、`local-gpu` 本地 GPU、`remote` 远程服务 |
81
+ | `recommendedEnabled` | 管理员可评估的启停建议,不直接改写已有开关 |
82
+ | `supportedPlatforms`、`supportedArchitectures` | 该实现实际支持的操作系统/架构范围;应依据软件适配证据填写 |
83
+ | `minMemoryGiB` | 已知最低内存要求;不能用它承诺延迟或吞吐 |
84
+ | `requiredEnvironmentKeys` | 必需配置的变量名,仅检查配置条件,不在报告中公开值 |
85
+
86
+ 填写 `features` 前核对当前模块是否已交付、Feature 是否真实注册、软件是否适配以及服务是否
87
+ 就绪。设备探测结果不能替代模型初始化、端点鉴权、业务输出或质量验证;未知状态应保留
88
+ 为待确认。报告与建议供管理员判断,既有 RBAC、License、Scope 和业务 Guard 继续生效。
89
+
90
+ ## 宿主诊断报告
91
+
92
+ 在需要评估的宿主机运行随模板交付的采集脚本,报告只包含有界诊断结果:
93
+
94
+ ```sh
95
+ umask 077
96
+ mkdir -p .local/diagnostics
97
+ python3 scripts/operations/collect-device-info.py > .local/diagnostics/host.json
98
+ # 可选:补充具体服务状态与已安装发布标识
99
+ python3 scripts/operations/collect-device-info.py \
100
+ --systemd-unit windy.service --manifest /opt/windy/manifest.json \
101
+ > .local/diagnostics/host.json
102
+ ```
103
+
104
+ 报告采用新的结构化 JSON 协议,不直接导入历史 TXT 诊断文本。可选 manifest 只读取
105
+ `releaseId`、`gitSha`、`platform` 白名单字段。采集器不安装软件、不重启服务、不读取应用
106
+ 凭据;缺少工具会记录缺失状态。报告可能包含
107
+ 主机名、网络地址、磁盘挂载和容器名称,应保存在运维可控目录,不纳入 Git 或公开附件。
108
+ 服务端评估和页面展示仍受运维权限控制。
109
+
110
+ 以下为添加到现有 Server 服务的只读挂载示例,路径与文件名由部署方确认:
111
+
112
+ ```yaml
113
+ services:
114
+ server:
115
+ environment:
116
+ WINDY_ENVIRONMENT_PROFILE_FILE: /app/diagnostics/windy.environment.yaml
117
+ WINDY_HOST_DIAGNOSTICS_FILE: /app/diagnostics/host.json
118
+ volumes:
119
+ - ./windy.environment.yaml:/app/diagnostics/windy.environment.yaml:ro
120
+ - ./.local/diagnostics/host.json:/app/diagnostics/host.json:ro
121
+ ```
122
+
123
+ 先生成报告再启动容器,避免 Docker 把缺失文件路径创建为目录。后续刷新报告后重新读取
124
+ 运维快照;报告是否新鲜由服务端校验,旧报告不能当作当前设备状态。
125
+
126
+ ## CPU OCR、Agent 与病毒扫描的边界
127
+
128
+ Agent、Document、Speech 与 Embedding 共用的 Provider 输入治理、执行凭据、内部令牌校验
129
+ 和幂等键位于 `apps/server/src/ai-runtime-security/`。Document、Speech 与 Embedding 的远端连接和
130
+ 路由装配位于 `apps/server/src/media-runtime/composition.ts`。这两部分当前仍随现有模块交付,
131
+ 共享 Artifact、媒体 readiness 与业务端口通过 `media-runtime/shared-composition.ts` 装配,
132
+ 当 YAML 关闭 Agent 时,Server 直接装配媒体和共享端口,跳过 Agent Host、工具注册与 MCP。
133
+
134
+ `system.media-runtime` 可单独选择,为 Document、Speech、Embedding 与公共 Artifact 提供
135
+ Manifest、Server 装配和数据库归属。选择 `system.agent` 时,生成器会自动补齐这个依赖。
136
+ 媒体直接 API 使用各自的 Feature。公共文件入口使用
137
+ `system.ai-artifact`,仍要求内部令牌、文件引用和当前对象授权。读取 Agent 截图另需启用
138
+ `system.agent`。
139
+
140
+ 病毒扫描通过独立的 `antivirus.packaged` 和 `antivirus.enabled` 声明,与 AI 运行时和 GPU
141
+ 无关。Windy 通用模板默认两项均为 `false`,也允许省略此段以沿用项目原有配置。
142
+ 通用生成器只交付平台能力,若请求 `packaged: true` 会明确拒绝。
143
+
144
+ 需要扫描的项目负责打包扫描器、病毒库及更新服务,并在项目拥有的
145
+ `apps/server/src/app/antivirus-composition.ts` 中装配 `ProjectAntivirusBinding`。
146
+ 该绑定须接入项目实际的入库流程。Server 启动时,`enabled: true` 传递 `required`,
147
+ 扫描器必须完成就绪检查并返回 `ready`,否则拒绝启动。交付声明必须与项目实际装配一致。
148
+ 已打包扫描器、但暂时停用时使用 `packaged: true`、`enabled: false`,Server 传递
149
+ `disabled`,项目应停止扫描并保留原有文件解析流程。只改 YAML 无法安装扫描器或病毒库。
150
+
151
+ 若扫描器是独立 Worker,项目的启动器须用同一份环境预设选择 Worker 模式,并在 Worker
152
+ 重启后再启动 Server。对于只在启动时读取扫描模式的 Worker,项目绑定应核对 Worker 实际
153
+ 行为和就绪状态;修改 Server 自身的环境变量不会切换已经运行的 Worker。
154
+ 验收时通过实际入库接口送入干净文件与匹配测试签名的合成文件,核对启用时的拦截结果和
155
+ 停用时的跳过记录,并确认停用扫描后仍可正常解析。
156
+
157
+ 管理员确认启停时,记录受影响的业务功能、当前证据和选择;已有用户授权可直接沿用。
158
+ 因未知设备、暂时探测失败或未验证性能而自动全局停用 AI,会误伤 CPU 或远程服务能力。
159
+ 回滚通过恢复此前经过确认的功能设置完成;构建时移除的能力需要重新交付,运行时开关
160
+ 无法补回缺失代码。
161
+
162
+ ## Skill 分发与验证层级
163
+
164
+ 开发约定由根目录 `.agents/skills/windy-business-module/SKILL.md` 维护。create-windy 的
165
+ `template-manifest.ts` 将该目录纳入模板,`template:sync` 从已提交的 Git HEAD 生成发布副本;
166
+ 不维护第二份容易漂移的手写 Skill。本次新增的环境适配约定随正式模板分发。
167
+
168
+ 普通本地修改只运行项目允许的静态检查。参数、YAML/JSON 校验、依赖闭包、裁剪结果和
169
+ 客户文件保留由合并候选测试覆盖,完整生成、构建与 pack consumer 属于发版门禁。
170
+ 模板被发放不代表目标主机或业务质量已通过验收。
171
+
172
+ ### 业务能力开关
173
+
174
+ 业务页面和服务入口使用 Access Snapshot 中对应的 Feature Key。拥有业务权限的调用方仍须通过目标
175
+ Operation 自身的权限和数据范围校验。
176
+
177
+ | 能力 | Feature Key | 平台直接 API 权限 |
178
+ | -------------- | ------------------ | ----------------------- |
179
+ | Agent | `system.agent` | `system.agent.read` |
180
+ | Document / OCR | `system.document` | `system.document.read` |
181
+ | Speech | `system.speech` | `system.speech.read` |
182
+ | Embedding | `system.embedding` | `system.embedding.read` |
183
+
184
+ 自定义角色调用媒体直接 API 时需要对应的新权限。当前 Feature 控制业务入口和 API 访问,
185
+ 生成裁剪仍需独立模块边界。
@@ -25,6 +25,21 @@
25
25
 
26
26
  unknown 检查只作用于 Definition 明确拥有的 prefix 或 exact key,避免把操作系统、Bun、测试工具或其它服务环境变量误判为平台配置。
27
27
 
28
+ ## 共享 AI Runtime 开关
29
+
30
+ 推荐环境变量为 `PLATFORM_ENABLE_AI_RUNTIME`,控制共享 AI Runtime 基础设施,包含 CPU OCR
31
+ 所需的 Document Runtime;它不代表已提供 GPU、大模型或 Copilot,也不是构建功能预设。
32
+ 内部逻辑字段保持 `enableAI`,不迁移已有配置版本。
33
+
34
+ 旧变量 `PLATFORM_ENABLE_AI` 作为兼容别名继续读取。只配置其中之一即可;进程环境同时包含两者时必须
35
+ 解析为相同布尔值,否则启动返回 `CONFIG_INVALID_VALUE`(字段 `enableAI`),不静默选择
36
+ 优先级。两者均未配置时仍默认 `false`;空值、非法值和拼错的变量名仍拒绝。Compose
37
+ 先读取新名称,未设置时回退旧名称,只向容器传新名称;迁移 `.env` 时删除旧名称以避免歧义。已有只配置旧名的安装不因模板升级被关闭。
38
+
39
+ 环境别名显式登记在 Definition,严格白名单同时识别新旧名;当前别名仅支持非 Secret
40
+ 字段,避免改变 SecretResolver 的引用边界。该开关保留共享基础设施,具体业务功能仍由
41
+ 模块装配、Manifest Feature、RBAC、License 与 Scope 判定。
42
+
28
43
  ## SecretRef 与 SecretResolver
29
44
 
30
45
  Secret 配置分成引用和运行时值两层:
@@ -66,12 +81,12 @@ handle/connection/purpose,业务模块看不到物理 locator。
66
81
 
67
82
  当前 `platform.runtime` 白名单包括:
68
83
 
69
- - `enableAI`:更新 foundation 平台能力投影;
84
+ - `enableAI`:重启后装配或停用 AI 与 Agent Runtime,并同步管理面「AI 与 Agent」分组与页面准入;
70
85
  - `cache.healthTimeoutMs`:后续 Redis 探活按最新值创建 probe。
71
86
  - `security.sessionTtlSeconds`:即时影响新签发会话,已有会话保持原过期时间;
72
87
  - `security.passwordMinLength/passwordMaxLength`:即时影响 Argon2 密码修改、重置校验和密码页服务端策略;
73
- - `storage.bulkDataMaxByteSize/storage.brandingLogoMaxByteSize`:即时影响后续上传会话;
74
- - `regional.timeZone`:即时影响公共平台设置投影和 Web 后续日期格式化;
88
+ - `storage.bulkDataMaxByteSize/storage.brandingLogoMaxByteSize`:即时影响后续上传会话。Logo 上限以字节保存,界面同时显示易读的换算值,默认 5 MiB(5,242,880 字节);
89
+ - `regional.timeZone`:即时影响公共平台设置投影和 Web 后续日期格式化。界面通过下拉选择 IANA 时区,显示其标识和当前 UTC 偏移;
75
90
  - `mail.enabled/host/port/secure/fromAddress/connectTimeoutMs`:保存为
76
91
  `pending-restart`,下次 Server 启动后进入 typed runtime config。
77
92
 
@@ -36,7 +36,7 @@ POST /api/auth/users/:id/password
36
36
  - `POST /auth/sessions/revoke-all` 默认只吊销本人其它会话并保留当前会话,响应返回吊销数量以及当前会话是否保留。只有显式提交 `{ "includeCurrent": true }` 才会吊销包括当前会话在内的全部本人会话并清除 Cookie;客户端不能提交目标用户 ID。
37
37
  - `GET /auth/login-events` 返回当前 Session Actor 最近 20 条登录成功/失败事件。查询条件固定为服务端 Actor,客户端即使提交 `actorId` 也不能读取他人的事件;响应只保留结果、时间、IP、User-Agent 和稳定失败原因,不暴露审计 Actor 或目标账号字段。未知账号登录失败继续归属匿名 Actor,不能借该接口探测账号是否存在。
38
38
  - 只有具备 `system.user.manage` 权限的用户可以重置密码。接口接收 `password`,服务端生成 Argon2id hash;通用用户 CRUD 不读写或返回 `password_hash`。重置后账号进入 `must-change-password`,`password_changed_at` 保持空值,并立即吊销该用户全部活跃会话。
39
- - 已认证用户通过 `POST /api/auth/password/change` 提交当前密码和新密码。服务端固定使用 Session Actor 作为目标用户,不接受客户端 `userId`;校验当前密码、拒绝复用当前密码,并在成功后把状态恢复为 `active`、记录 `password_changed_at`。成功改密会吊销包括当前会话在内的全部会话、清除 Cookie,用户必须用新密码重新登录。
39
+ - 用户可从「个人设置 → 登录密码 → 修改密码」发起改密。已认证用户通过 `POST /api/auth/password/change` 提交当前密码和新密码。服务端固定使用 Session Actor 作为目标用户,不接受客户端 `userId`;校验当前密码、拒绝复用当前密码,并在成功后把状态恢复为 `active`、记录 `password_changed_at`。成功改密会吊销包括当前会话在内的全部会话、清除 Cookie,用户必须用新密码重新登录。
40
40
 
41
41
  登录成功、登录失败、退出、会话吊销、密码更新和越权拒绝都会写入审计。审计只记录用户、Session ID、结果和稳定原因,不记录明文密码、Bearer Token 或 Cookie。
42
42
 
@@ -67,7 +67,9 @@
67
67
  "check:document": "uv run --directory apps/document-server ruff check . && uv run --directory apps/document-server ruff format --check . && uv run --directory apps/document-server mypy",
68
68
  "check:speech": "uv run --directory apps/speech-server ruff check . && uv run --directory apps/speech-server ruff format --check . && uv run --directory apps/speech-server mypy",
69
69
  "check:embedding": "uv run --directory apps/embedding-server ruff check . && uv run --directory apps/embedding-server ruff format --check . && uv run --directory apps/embedding-server mypy",
70
- "typecheck": "bun run --cwd apps/web typecheck && bun run --cwd apps/server typecheck && bun run --cwd apps/license typecheck && bun run --cwd apps/signing-service typecheck && bun run --cwd packages/ai-client typecheck && bun run --cwd packages/license-sdk typecheck && bun run --cwd packages/jobs typecheck && bun run --cwd packages/server-sdk typecheck && bun run --cwd packages/storage typecheck && bun run --cwd packages/database typecheck && bun run --cwd packages/crud-generator typecheck && bun run --cwd packages/delivery typecheck && bun run --cwd packages/create-windy typecheck && bun run --cwd apps/agent-server typecheck && bun run --cwd apps/windy-gateway typecheck && bun run --cwd packages/model-gateway typecheck",
70
+ "typecheck:quality": "tsc --noEmit --project scripts/quality/tsconfig.json",
71
+ "typecheck:affected": "bun run scripts/typecheck-affected.ts",
72
+ "typecheck": "bun run --cwd apps/web typecheck && bun run --cwd apps/server typecheck && bun run --cwd apps/license typecheck && bun run --cwd apps/signing-service typecheck && bun run --cwd packages/ai-client typecheck && bun run --cwd packages/license-sdk typecheck && bun run --cwd packages/jobs typecheck && bun run --cwd packages/server-sdk typecheck && bun run --cwd packages/storage typecheck && bun run --cwd packages/database typecheck && bun run --cwd packages/crud-generator typecheck && bun run --cwd packages/delivery typecheck && bun run --cwd packages/create-windy typecheck && bun run --cwd apps/agent-server typecheck && bun run --cwd apps/windy-gateway typecheck && bun run --cwd packages/model-gateway typecheck && bun run typecheck:quality",
71
73
  "lint": "oxlint .",
72
74
  "format": "oxfmt",
73
75
  "check:static": "bun run lint && bun run typecheck && bun run check:agent && bun run check:document && bun run check:speech && bun run check:embedding",
@@ -82,15 +84,15 @@
82
84
  ]
83
85
  },
84
86
  "devDependencies": {
85
- "@types/bun": "^1.3.14",
86
- "@types/pg": "^8.20.0",
87
- "drizzle-kit": "^0.31.10",
88
- "drizzle-orm": "^0.45.2",
87
+ "@types/bun": "~1.3.14",
88
+ "@types/pg": "^8.23.1",
89
+ "drizzle-kit": "^0.31.11",
90
+ "drizzle-orm": "^0.45.4",
89
91
  "husky": "^9.1.7",
90
- "lint-staged": "^17.1.1",
92
+ "lint-staged": "^17.6.0",
91
93
  "oxfmt": "^0.59.0",
92
- "oxlint": "^1.74.0",
93
- "pg": "^8.22.0",
94
+ "oxlint": "^1.92.0",
95
+ "pg": "^8.23.1",
94
96
  "typescript": "5.9.3"
95
97
  },
96
98
  "dependencies": {}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@southwind-ai/config",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "engines": {
@@ -14,7 +14,7 @@
14
14
  ],
15
15
  "dependencies": {
16
16
  "@southwind-ai/shared": "workspace:^0.3.0",
17
- "zod": "^4.4.3"
17
+ "zod": "^4.6.7"
18
18
  },
19
19
  "exports": {
20
20
  ".": "./index.ts"
@@ -5,6 +5,8 @@ export type EnvironmentValueDecoder = (value: string) => unknown;
5
5
 
6
6
  export interface ModuleConfigEnvironmentField {
7
7
  key: string;
8
+ /** Legacy environment names; conflicting values are rejected. */
9
+ aliases?: readonly string[];
8
10
  path: string;
9
11
  defaultValue?: unknown;
10
12
  decode?: EnvironmentValueDecoder;
@@ -94,16 +96,24 @@ function validateDefinition<T>(definition: ModuleConfigDefinition<T>): void {
94
96
  const environmentKeys = new Set<string>();
95
97
  const paths = new Set<string>();
96
98
  for (const field of definition.environment) {
97
- if (!/^[A-Z][A-Z0-9_]*$/.test(field.key)) {
98
- throw new Error(`配置模块 ${definition.key} 包含无效环境变量名`);
99
+ if (field.secret && field.aliases?.length) {
100
+ throw new Error(`配置模块 ${definition.key} 的 Secret 不支持环境别名`);
101
+ }
102
+ for (const key of [field.key, ...(field.aliases ?? [])]) {
103
+ if (!/^[A-Z][A-Z0-9_]*$/.test(key)) {
104
+ throw new Error(`配置模块 ${definition.key} 包含无效环境变量名`);
105
+ }
106
+ if (environmentKeys.has(key)) {
107
+ throw new Error(`配置模块 ${definition.key} 包含重复字段`);
108
+ }
109
+ environmentKeys.add(key);
99
110
  }
100
111
  if (!/^[a-z][A-Za-z0-9]*(\.[a-z][A-Za-z0-9]*)*$/.test(field.path)) {
101
112
  throw new Error(`配置模块 ${definition.key} 包含无效字段路径`);
102
113
  }
103
- if (environmentKeys.has(field.key) || paths.has(field.path)) {
114
+ if (paths.has(field.path)) {
104
115
  throw new Error(`配置模块 ${definition.key} 包含重复字段`);
105
116
  }
106
- environmentKeys.add(field.key);
107
117
  paths.add(field.path);
108
118
  }
109
119
  }
@@ -78,7 +78,7 @@ async function readField(
78
78
  environment: ConfigEnvironment,
79
79
  resolver: SecretResolver,
80
80
  ): Promise<unknown> {
81
- const raw = environment[field.key];
81
+ const raw = readEnvironmentValue(moduleKey, field, environment);
82
82
  if (!field.secret) return decodeField(moduleKey, field, raw);
83
83
  const ref = createSecretRef(field, raw);
84
84
  if (!ref) return missingSecret(moduleKey, field);
@@ -101,7 +101,7 @@ function readFieldSync(
101
101
  environment: ConfigEnvironment,
102
102
  resolver: SecretResolver,
103
103
  ): unknown {
104
- const raw = environment[field.key];
104
+ const raw = readEnvironmentValue(moduleKey, field, environment);
105
105
  if (!field.secret) return decodeField(moduleKey, field, raw);
106
106
  const ref = createSecretRef(field, raw);
107
107
  if (!ref) return missingSecret(moduleKey, field);
@@ -126,6 +126,25 @@ function readFieldSync(
126
126
  }
127
127
  }
128
128
 
129
+ function readEnvironmentValue(
130
+ moduleKey: string,
131
+ field: ModuleConfigEnvironmentField,
132
+ environment: ConfigEnvironment,
133
+ ): string | undefined {
134
+ if (!field.aliases?.length) return environment[field.key];
135
+ const values = [field.key, ...field.aliases]
136
+ .map((key) => environment[key])
137
+ .filter((value): value is string => value !== undefined);
138
+ const first = values[0];
139
+ if (first === undefined || values.length < 2) return first;
140
+ const decode = (raw: string) => decodeField(moduleKey, field, raw);
141
+ const selected = decode(first);
142
+ if (values.slice(1).some((raw) => !Object.is(decode(raw), selected))) {
143
+ throw new ConfigLoadError("CONFIG_INVALID_VALUE", moduleKey, field.path);
144
+ }
145
+ return first;
146
+ }
147
+
129
148
  function createSecretRef(
130
149
  field: ModuleConfigEnvironmentField,
131
150
  raw: string | undefined,
@@ -181,7 +200,12 @@ function assertKnownEnvironment<T>(
181
200
  definition: ModuleConfigDefinition<T>,
182
201
  environment: ConfigEnvironment,
183
202
  ): void {
184
- const known = new Set(definition.environment.map((field) => field.key));
203
+ const known = new Set(
204
+ definition.environment.flatMap((field) => [
205
+ field.key,
206
+ ...(field.aliases ?? []),
207
+ ]),
208
+ );
185
209
  const exact = new Set(definition.ownedEnvironmentKeys || []);
186
210
  const prefixes = definition.ownedEnvironmentPrefixes || [];
187
211
  const unknown = Object.keys(environment).filter(
@@ -0,0 +1,94 @@
1
+ import { expect, test } from "bun:test";
2
+ import { z } from "zod";
3
+ import { defineModuleConfig } from "./definition.js";
4
+ import { ConfigLoadError, loadModuleConfig } from "./environment-loader.js";
5
+ import { loadPlatformConfig, platformConfigDefinition } from "./platform.js";
6
+ import { EnvironmentSecretResolver } from "./secrets.js";
7
+
8
+ test("共享 AI Runtime 使用明确新名,兼容旧名与缺省关闭", () => {
9
+ expect(loadPlatformConfig({}).enableAI).toBe(false);
10
+ for (const key of ["PLATFORM_ENABLE_AI_RUNTIME", "PLATFORM_ENABLE_AI"]) {
11
+ expect(loadPlatformConfig({ [key]: "true" }).enableAI).toBe(true);
12
+ expect(loadPlatformConfig({ [key]: "false" }).enableAI).toBe(false);
13
+ }
14
+ expect(
15
+ loadPlatformConfig({
16
+ PLATFORM_ENABLE_AI_RUNTIME: "true",
17
+ PLATFORM_ENABLE_AI: "1",
18
+ }).enableAI,
19
+ ).toBe(true);
20
+ });
21
+
22
+ test("新旧变量冲突、空值或非法值均 fail closed 且不回显输入", () => {
23
+ for (const legacy of ["false", "", "DO-NOT-LEAK"]) {
24
+ try {
25
+ loadPlatformConfig({
26
+ PLATFORM_ENABLE_AI_RUNTIME: "true",
27
+ PLATFORM_ENABLE_AI: legacy,
28
+ });
29
+ throw new Error("Expected configuration rejection");
30
+ } catch (error) {
31
+ expect(error).toBeInstanceOf(ConfigLoadError);
32
+ expect(error).toMatchObject({
33
+ code: "CONFIG_INVALID_VALUE",
34
+ fieldPath: "enableAI",
35
+ });
36
+ expect(String(error)).not.toContain("DO-NOT-LEAK");
37
+ }
38
+ }
39
+ expect(() => loadPlatformConfig({ PLATFORM_ENABLE_AI_RUNTIME: "" })).toThrow(
40
+ ConfigLoadError,
41
+ );
42
+ expect(() =>
43
+ loadPlatformConfig({ PLATFORM_ENABLE_AI_RUNTIM: "true" }),
44
+ ).toThrow(expect.objectContaining({ code: "CONFIG_UNKNOWN_ENV" }));
45
+ });
46
+
47
+ test("异步 Definition Loader 也读取旧名而不绕过冲突校验", async () => {
48
+ const environment = { PLATFORM_ENABLE_AI: "true" };
49
+ expect(
50
+ (
51
+ await loadModuleConfig(
52
+ platformConfigDefinition,
53
+ environment,
54
+ new EnvironmentSecretResolver(environment),
55
+ )
56
+ ).enableAI,
57
+ ).toBe(true);
58
+ await expect(
59
+ loadModuleConfig(
60
+ platformConfigDefinition,
61
+ {
62
+ PLATFORM_ENABLE_AI: "false",
63
+ PLATFORM_ENABLE_AI_RUNTIME: "true",
64
+ },
65
+ new EnvironmentSecretResolver({}),
66
+ ),
67
+ ).rejects.toMatchObject({ code: "CONFIG_INVALID_VALUE" });
68
+ });
69
+
70
+ test("别名不能与其他字段重复,也不能用于 Secret 引用", () => {
71
+ const definition = {
72
+ key: "test.alias",
73
+ label: "Alias",
74
+ schema: z.object({ first: z.string(), second: z.string() }),
75
+ environment: [
76
+ { key: "FIRST", aliases: ["SECOND"], path: "first" },
77
+ { key: "SECOND", path: "second" },
78
+ ],
79
+ };
80
+ expect(() => defineModuleConfig(definition)).toThrow("重复字段");
81
+ expect(() =>
82
+ defineModuleConfig({
83
+ ...definition,
84
+ environment: [
85
+ {
86
+ key: "TOKEN",
87
+ aliases: ["OLD_TOKEN"],
88
+ path: "first",
89
+ secret: { provider: "env" },
90
+ },
91
+ ],
92
+ }),
93
+ ).toThrow("Secret 不支持环境别名");
94
+ });
@@ -206,9 +206,12 @@ export const platformConfigDefinition = defineModuleConfig<PlatformConfig>({
206
206
  // @windy-module system.agent end
207
207
  ],
208
208
  environment: [
209
- value("PLATFORM_NAME", "name", "Windy Platform"),
209
+ value("PLATFORM_NAME", "name", "Windy"),
210
210
  value("PLATFORM_LOGO_URL", "logoUrl"),
211
- value("PLATFORM_ENABLE_AI", "enableAI", false, strictBoolean),
211
+ {
212
+ ...value("PLATFORM_ENABLE_AI_RUNTIME", "enableAI", false, strictBoolean),
213
+ aliases: ["PLATFORM_ENABLE_AI"],
214
+ },
212
215
  value("DATABASE_DIALECT", "database.dialect", "postgresql"),
213
216
  secret("DATABASE_URL", "database.url"),
214
217
  secret("REDIS_URL", "cache.redisUrl"),
@@ -0,0 +1 @@
1
+ CREATE INDEX "idx_durable_job_leases_job_acquired" ON "durable_job_leases" USING btree ("job_id","acquired_at");