create-windy 0.3.2 → 0.3.5

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 (343) hide show
  1. package/README.md +27 -9
  2. package/dist/cli.js +825 -117
  3. package/package.json +1 -1
  4. package/template/.agents/skills/windy-business-module/SKILL.md +16 -3
  5. package/template/.windy-template.json +2 -2
  6. package/template/AGENTS.md +4 -0
  7. package/template/README.md +18 -11
  8. package/template/_gitignore +1 -0
  9. package/template/apps/agent-server/README.md +2 -0
  10. package/template/apps/agent-server/pyproject.toml +8 -0
  11. package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +87 -10
  12. package/template/apps/agent-server/src/southwind_agent_server/adk_model.py +18 -3
  13. package/template/apps/agent-server/src/southwind_agent_server/adk_tools.py +78 -11
  14. package/template/apps/agent-server/src/southwind_agent_server/app.py +120 -115
  15. package/template/apps/agent-server/src/southwind_agent_server/artifact_input.py +153 -0
  16. package/template/apps/agent-server/src/southwind_agent_server/config.py +29 -0
  17. package/template/apps/agent-server/src/southwind_agent_server/contracts.py +81 -1
  18. package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +8 -0
  19. package/template/apps/agent-server/src/southwind_agent_server/engine.py +28 -0
  20. package/template/apps/agent-server/src/southwind_agent_server/fingerprint.py +41 -0
  21. package/template/apps/agent-server/src/southwind_agent_server/http_helpers.py +62 -0
  22. package/template/apps/agent-server/src/southwind_agent_server/lease.py +15 -0
  23. package/template/apps/agent-server/src/southwind_agent_server/multimodal.py +76 -0
  24. package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +112 -20
  25. package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +14 -1
  26. package/template/apps/agent-server/src/southwind_agent_server/postgres_store.py +329 -0
  27. package/template/apps/agent-server/src/southwind_agent_server/postgres_store_backend.py +332 -0
  28. package/template/apps/agent-server/src/southwind_agent_server/provider_governance.py +64 -3
  29. package/template/apps/agent-server/src/southwind_agent_server/runtime_factory.py +68 -0
  30. package/template/apps/agent-server/src/southwind_agent_server/service.py +218 -56
  31. package/template/apps/agent-server/src/southwind_agent_server/signal_events.py +36 -0
  32. package/template/apps/agent-server/src/southwind_agent_server/sqlite_catalog.py +107 -0
  33. package/template/apps/agent-server/src/southwind_agent_server/sqlite_schema.py +65 -0
  34. package/template/apps/agent-server/src/southwind_agent_server/sqlite_serialization.py +16 -0
  35. package/template/apps/agent-server/src/southwind_agent_server/sqlite_store.py +89 -57
  36. package/template/apps/agent-server/src/southwind_agent_server/store.py +105 -2
  37. package/template/apps/agent-server/src/southwind_agent_server/store_cipher.py +40 -0
  38. package/template/apps/agent-server/src/southwind_agent_server/telemetry.py +89 -0
  39. package/template/apps/agent-server/src/southwind_agent_server/tool_host.py +72 -0
  40. package/template/apps/agent-server/tests/test_adk_adapter.py +6 -2
  41. package/template/apps/agent-server/tests/test_api.py +24 -1
  42. package/template/apps/agent-server/tests/test_artifact_input.py +110 -0
  43. package/template/apps/agent-server/tests/test_config.py +4 -0
  44. package/template/apps/agent-server/tests/test_fingerprint.py +10 -0
  45. package/template/apps/agent-server/tests/test_multimodal.py +51 -0
  46. package/template/apps/agent-server/tests/test_openai_provider.py +6 -2
  47. package/template/apps/agent-server/tests/test_postgres_store.py +131 -0
  48. package/template/apps/agent-server/tests/test_provider_governance.py +61 -0
  49. package/template/apps/agent-server/tests/test_sqlite_store.py +95 -10
  50. package/template/apps/agent-server/tests/test_telemetry.py +25 -0
  51. package/template/apps/agent-server/tests/test_tool_approval.py +28 -0
  52. package/template/apps/agent-server/uv.lock +165 -0
  53. package/template/apps/document-server/Dockerfile +19 -0
  54. package/template/apps/document-server/pyproject.toml +43 -0
  55. package/template/apps/document-server/src/southwind_document_server/__init__.py +1 -0
  56. package/template/apps/document-server/src/southwind_document_server/adapter.py +337 -0
  57. package/template/apps/document-server/src/southwind_document_server/app.py +151 -0
  58. package/template/apps/document-server/src/southwind_document_server/config.py +94 -0
  59. package/template/apps/document-server/src/southwind_document_server/contracts.py +103 -0
  60. package/template/apps/document-server/src/southwind_document_server/service.py +149 -0
  61. package/template/apps/document-server/src/southwind_document_server/store.py +185 -0
  62. package/template/apps/document-server/tests/__init__.py +1 -0
  63. package/template/apps/document-server/tests/test_config.py +42 -0
  64. package/template/apps/document-server/tests/test_service.py +140 -0
  65. package/template/apps/document-server/uv.lock +430 -0
  66. package/template/apps/embedding-server/Dockerfile +17 -0
  67. package/template/apps/embedding-server/pyproject.toml +44 -0
  68. package/template/apps/embedding-server/src/southwind_embedding_server/__init__.py +1 -0
  69. package/template/apps/embedding-server/src/southwind_embedding_server/adapter.py +254 -0
  70. package/template/apps/embedding-server/src/southwind_embedding_server/app.py +119 -0
  71. package/template/apps/embedding-server/src/southwind_embedding_server/config.py +120 -0
  72. package/template/apps/embedding-server/src/southwind_embedding_server/contracts.py +106 -0
  73. package/template/apps/embedding-server/src/southwind_embedding_server/error.py +6 -0
  74. package/template/apps/embedding-server/src/southwind_embedding_server/image_input.py +87 -0
  75. package/template/apps/embedding-server/tests/__init__.py +1 -0
  76. package/template/apps/embedding-server/tests/test_adapter.py +92 -0
  77. package/template/apps/embedding-server/tests/test_config.py +38 -0
  78. package/template/apps/embedding-server/uv.lock +449 -0
  79. package/template/apps/server/src/agent/background-operation-port.ts +37 -0
  80. package/template/apps/server/src/agent/background-operation.test.ts +231 -0
  81. package/template/apps/server/src/agent/background-operation.ts +252 -0
  82. package/template/apps/server/src/agent/drizzle-run-binding-repository.ts +114 -0
  83. package/template/apps/server/src/agent/execution-grants.ts +7 -1
  84. package/template/apps/server/src/agent/fake-runtime.test-support.ts +43 -0
  85. package/template/apps/server/src/agent/idempotency-key.test.ts +28 -0
  86. package/template/apps/server/src/agent/idempotency-key.ts +11 -0
  87. package/template/apps/server/src/agent/remote-runtime.test.ts +115 -1
  88. package/template/apps/server/src/agent/remote-runtime.ts +173 -5
  89. package/template/apps/server/src/agent/run-authorization.test-support.ts +9 -0
  90. package/template/apps/server/src/agent/run-authorization.test.ts +126 -0
  91. package/template/apps/server/src/agent/run-authorization.ts +180 -0
  92. package/template/apps/server/src/agent/run-binding-repository.ts +93 -0
  93. package/template/apps/server/src/agent/run-contracts.ts +88 -1
  94. package/template/apps/server/src/agent/run-definition-support.ts +58 -0
  95. package/template/apps/server/src/agent/run-errors.ts +25 -0
  96. package/template/apps/server/src/agent/run-facade.test.ts +57 -46
  97. package/template/apps/server/src/agent/run-facade.ts +185 -65
  98. package/template/apps/server/src/agent/run-release.ts +78 -0
  99. package/template/apps/server/src/agent/run-route-input.ts +169 -0
  100. package/template/apps/server/src/agent/run-routes.ts +63 -76
  101. package/template/apps/server/src/agent/runtime-bootstrap.ts +169 -13
  102. package/template/apps/server/src/agent/sandbox-client.ts +200 -0
  103. package/template/apps/server/src/agent/sandbox-protocol.ts +76 -0
  104. package/template/apps/server/src/agent/sandbox-registry.ts +205 -0
  105. package/template/apps/server/src/agent/sandbox-runtime.test.ts +137 -0
  106. package/template/apps/server/src/agent/sandbox-worker-server.ts +195 -0
  107. package/template/apps/server/src/agent/sandbox-worker.ts +12 -0
  108. package/template/apps/server/src/agent/tool-approval.test.ts +75 -0
  109. package/template/apps/server/src/agent/tool-approval.ts +115 -0
  110. package/template/apps/server/src/agent/tool-audit.ts +62 -0
  111. package/template/apps/server/src/agent/tool-host.test.ts +39 -11
  112. package/template/apps/server/src/agent/tool-host.ts +184 -17
  113. package/template/apps/server/src/agent/tool-registry.ts +52 -47
  114. package/template/apps/server/src/agent/vision-facade.test.ts +256 -0
  115. package/template/apps/server/src/agent/vision-input.ts +120 -0
  116. package/template/apps/server/src/ai-artifact/codec.test.ts +44 -0
  117. package/template/apps/server/src/ai-artifact/codec.ts +137 -0
  118. package/template/apps/server/src/ai-artifact/routes.ts +37 -0
  119. package/template/apps/server/src/ai-artifact/runtime.ts +205 -0
  120. package/template/apps/server/src/application-services.ts +62 -2
  121. package/template/apps/server/src/auth/reauthentication.test.ts +40 -0
  122. package/template/apps/server/src/auth/reauthentication.ts +19 -0
  123. package/template/apps/server/src/auth/runtime.ts +9 -1
  124. package/template/apps/server/src/data-access/context.ts +6 -4
  125. package/template/apps/server/src/data-access/scope-snapshot.ts +27 -0
  126. package/template/apps/server/src/data-governance/export/approval-port.ts +6 -3
  127. package/template/apps/server/src/data-governance/export/authorization-port.test.ts +23 -4
  128. package/template/apps/server/src/data-governance/export/authorization-port.ts +89 -25
  129. package/template/apps/server/src/data-governance/export/drizzle-repository.integration.test.ts +2 -0
  130. package/template/apps/server/src/data-governance/export/drizzle-repository.ts +1 -0
  131. package/template/apps/server/src/data-governance/export/job-runtime.test.ts +15 -4
  132. package/template/apps/server/src/data-governance/export/job-runtime.ts +68 -15
  133. package/template/apps/server/src/data-governance/export/ndjson-stream.ts +25 -6
  134. package/template/apps/server/src/data-governance/export/postgres-recovery.integration.test.ts +4 -2
  135. package/template/apps/server/src/data-governance/export/renderer-job-runtime.test.ts +235 -0
  136. package/template/apps/server/src/data-governance/export/renderer-runtime.ts +293 -0
  137. package/template/apps/server/src/data-governance/export/renderer-selection.test.ts +67 -0
  138. package/template/apps/server/src/data-governance/export/repository.test.ts +2 -0
  139. package/template/apps/server/src/data-governance/export/request-fingerprint.test.ts +13 -0
  140. package/template/apps/server/src/data-governance/export/request-fingerprint.ts +2 -0
  141. package/template/apps/server/src/data-governance/export/routes.ts +11 -1
  142. package/template/apps/server/src/data-governance/export/scope-snapshot.ts +6 -18
  143. package/template/apps/server/src/data-governance/export/service-support.ts +188 -0
  144. package/template/apps/server/src/data-governance/export/service.ts +90 -113
  145. package/template/apps/server/src/data-governance/export/source.ts +101 -0
  146. package/template/apps/server/src/data-governance/export/types.ts +27 -0
  147. package/template/apps/server/src/document/contracts.ts +76 -0
  148. package/template/apps/server/src/document/facade.ts +339 -0
  149. package/template/apps/server/src/document/remote-runtime.test.ts +96 -0
  150. package/template/apps/server/src/document/remote-runtime.ts +223 -0
  151. package/template/apps/server/src/document/routes.ts +193 -0
  152. package/template/apps/server/src/embedding/contracts.ts +83 -0
  153. package/template/apps/server/src/embedding/error.ts +10 -0
  154. package/template/apps/server/src/embedding/facade.ts +217 -0
  155. package/template/apps/server/src/embedding/input.ts +172 -0
  156. package/template/apps/server/src/embedding/remote-runtime.test.ts +66 -0
  157. package/template/apps/server/src/embedding/remote-runtime.ts +218 -0
  158. package/template/apps/server/src/embedding/routes.ts +152 -0
  159. package/template/apps/server/src/index.ts +40 -43
  160. package/template/apps/server/src/jobs/artifact-service.test.ts +14 -2
  161. package/template/apps/server/src/jobs/artifact-service.ts +10 -0
  162. package/template/apps/server/src/jobs/drizzle-execution-repository.ts +17 -18
  163. package/template/apps/server/src/jobs/drizzle-execution.integration.test.ts +19 -1
  164. package/template/apps/server/src/jobs/drizzle-mappers.ts +4 -1
  165. package/template/apps/server/src/jobs/drizzle-recovery-repository.ts +49 -2
  166. package/template/apps/server/src/jobs/drizzle-repository.ts +1 -0
  167. package/template/apps/server/src/jobs/execution-repository.ts +17 -0
  168. package/template/apps/server/src/jobs/execution.test.ts +34 -0
  169. package/template/apps/server/src/jobs/in-memory-control.ts +135 -0
  170. package/template/apps/server/src/jobs/in-memory-execution.ts +34 -65
  171. package/template/apps/server/src/jobs/legacy-service-runtime.ts +5 -2
  172. package/template/apps/server/src/jobs/repository.ts +3 -0
  173. package/template/apps/server/src/jobs/worker.ts +80 -41
  174. package/template/apps/server/src/module-host/ai-operation-port.ts +170 -0
  175. package/template/apps/server/src/module-host/durable-context.test.ts +312 -0
  176. package/template/apps/server/src/module-host/durable-context.ts +124 -0
  177. package/template/apps/server/src/module-host/durable-handler-registration.ts +72 -0
  178. package/template/apps/server/src/module-host/governed-export-route-support.ts +171 -0
  179. package/template/apps/server/src/module-host/governed-export-routes.test.ts +327 -0
  180. package/template/apps/server/src/module-host/governed-export-routes.ts +238 -0
  181. package/template/apps/server/src/module-host/governed-renderer-registration.test.ts +247 -0
  182. package/template/apps/server/src/module-host/governed-resource-routes.ts +331 -0
  183. package/template/apps/server/src/module-host/governed-resources.test.ts +344 -0
  184. package/template/apps/server/src/module-host/governed-resources.ts +203 -0
  185. package/template/apps/server/src/module-host/http-runtime.ts +95 -0
  186. package/template/apps/server/src/module-host/initialize-contracts.ts +14 -0
  187. package/template/apps/server/src/module-host/initialize.test.ts +49 -1
  188. package/template/apps/server/src/module-host/initialize.ts +39 -16
  189. package/template/apps/server/src/module-host/job-port.test.ts +262 -0
  190. package/template/apps/server/src/module-host/job-port.ts +334 -0
  191. package/template/apps/server/src/module-host/request-context.test.ts +88 -2
  192. package/template/apps/server/src/module-host/request-context.ts +56 -22
  193. package/template/apps/server/src/module-host/route-installer.test.ts +10 -0
  194. package/template/apps/server/src/module-host/route-installer.ts +36 -1
  195. package/template/apps/server/src/module-host/sandbox-tool-registration.ts +42 -0
  196. package/template/apps/server/src/module-host/tool-handlers.ts +67 -31
  197. package/template/apps/server/src/module-host.ts +1 -0
  198. package/template/apps/server/src/module-runtime-validation.ts +7 -3
  199. package/template/apps/server/src/speech/contracts.ts +76 -0
  200. package/template/apps/server/src/speech/facade.ts +316 -0
  201. package/template/apps/server/src/speech/remote-runtime.test.ts +88 -0
  202. package/template/apps/server/src/speech/remote-runtime.ts +227 -0
  203. package/template/apps/server/src/speech/routes.ts +180 -0
  204. package/template/apps/server/src/system/actor.ts +39 -0
  205. package/template/apps/server/src/system/route-helpers.ts +1 -0
  206. package/template/apps/server/src/workflow/service.ts +2 -1
  207. package/template/apps/speech-server/Dockerfile +22 -0
  208. package/template/apps/speech-server/pyproject.toml +43 -0
  209. package/template/apps/speech-server/src/southwind_speech_server/__init__.py +1 -0
  210. package/template/apps/speech-server/src/southwind_speech_server/adapter.py +249 -0
  211. package/template/apps/speech-server/src/southwind_speech_server/app.py +183 -0
  212. package/template/apps/speech-server/src/southwind_speech_server/config.py +100 -0
  213. package/template/apps/speech-server/src/southwind_speech_server/contracts.py +103 -0
  214. package/template/apps/speech-server/src/southwind_speech_server/errors.py +6 -0
  215. package/template/apps/speech-server/src/southwind_speech_server/normalizer.py +108 -0
  216. package/template/apps/speech-server/src/southwind_speech_server/output_validation.py +128 -0
  217. package/template/apps/speech-server/src/southwind_speech_server/service.py +162 -0
  218. package/template/apps/speech-server/src/southwind_speech_server/store.py +209 -0
  219. package/template/apps/speech-server/tests/__init__.py +1 -0
  220. package/template/apps/speech-server/tests/test_config.py +33 -0
  221. package/template/apps/speech-server/tests/test_service.py +154 -0
  222. package/template/apps/speech-server/uv.lock +430 -0
  223. package/template/apps/web/src/app/business-routes.ts +22 -0
  224. package/template/apps/web/src/components/common/AccessSnapshotBoundary.vue +112 -0
  225. package/template/apps/web/src/components/common/AccessSnapshotBoundary.webtest.ts +130 -0
  226. package/template/apps/web/src/components/common/AccessSnapshotNotice.vue +117 -0
  227. package/template/apps/web/src/components/common/FormDialog.vue +6 -0
  228. package/template/apps/web/src/components/common/ModalLayout.webtest.ts +13 -0
  229. package/template/apps/web/src/composables/access-snapshot-change.ts +131 -0
  230. package/template/apps/web/src/composables/access-snapshot-change.webtest.ts +103 -0
  231. package/template/apps/web/src/composables/useAccessSnapshot.ts +48 -2
  232. package/template/apps/web/src/composables/useAccessSnapshot.webtest.ts +54 -0
  233. package/template/apps/web/src/composables/useAccessSnapshotMonitor.ts +42 -0
  234. package/template/apps/web/src/layout/AppShell.vue +28 -25
  235. package/template/apps/web/src/layout/AppShell.webtest.ts +1 -0
  236. package/template/apps/web/src/router/business-routes.ts +68 -0
  237. package/template/apps/web/src/router/business-routes.webtest.ts +85 -0
  238. package/template/apps/web/src/router/index.ts +7 -0
  239. package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +12 -0
  240. package/template/apps/web/src/services/access-api.ts +5 -1
  241. package/template/apps/web/src/services/access-api.webtest.ts +35 -0
  242. package/template/docker-compose.yml +127 -0
  243. package/template/docs/architecture/ai-egress.md +12 -9
  244. package/template/docs/architecture/ai-runtime.md +23 -11
  245. package/template/docs/architecture/durable-jobs.md +25 -3
  246. package/template/docs/architecture/foundation-infrastructure.md +163 -0
  247. package/template/docs/architecture/module-host.md +115 -0
  248. package/template/docs/architecture/server-module-interface.md +39 -0
  249. package/template/docs/architecture/web-access-snapshot-resilience.md +60 -0
  250. package/template/docs/development/business-web-routes.md +66 -0
  251. package/template/docs/development/custom-login-page.md +3 -3
  252. package/template/docs/development/module-lifecycle.md +71 -0
  253. package/template/docs/operations/observability.md +5 -1
  254. package/template/docs/platform/agent-runtime.md +21 -11
  255. package/template/docs/platform/agent-tool-sandbox.md +58 -0
  256. package/template/docs/platform/agent-tools.md +5 -0
  257. package/template/docs/platform/document-ai-runtime.md +204 -0
  258. package/template/docs/platform/governed-export-renderers.md +132 -0
  259. package/template/docs/platform/navigation-registry.md +5 -0
  260. package/template/docs/platform/organization-membership.md +2 -1
  261. package/template/docs/platform/speech-ai-runtime.md +156 -0
  262. package/template/docs/platform/system-crud-api.md +7 -1
  263. package/template/docs/platform/vision-ai-runtime.md +97 -0
  264. package/template/docs/platform/web-system-crud.md +6 -0
  265. package/template/package.json +16 -2
  266. package/template/packages/ai-client/index.ts +4 -0
  267. package/template/packages/ai-client/package.json +5 -2
  268. package/template/packages/ai-client/src/agent/client/artifact.ts +26 -0
  269. package/template/packages/ai-client/src/agent/client/attach.test.ts +124 -0
  270. package/template/packages/ai-client/src/agent/client/client.ts +119 -7
  271. package/template/packages/ai-client/src/agent/client/run.ts +14 -0
  272. package/template/packages/ai-client/src/agent/client/types.ts +18 -0
  273. package/template/packages/ai-client/src/agent/client/vision.test.ts +53 -0
  274. package/template/packages/ai-client/src/agent/contracts/contracts.test.ts +1 -0
  275. package/template/packages/ai-client/src/agent/contracts/events.ts +33 -0
  276. package/template/packages/ai-client/src/agent/contracts/runs.ts +71 -0
  277. package/template/packages/ai-client/src/agent/index.ts +1 -0
  278. package/template/packages/ai-client/src/artifact-ref.ts +8 -0
  279. package/template/packages/ai-client/src/document/client.test.ts +114 -0
  280. package/template/packages/ai-client/src/document/client.ts +134 -0
  281. package/template/packages/ai-client/src/document/contracts.ts +152 -0
  282. package/template/packages/ai-client/src/document/error.ts +43 -0
  283. package/template/packages/ai-client/src/document/index.ts +3 -0
  284. package/template/packages/ai-client/src/document/run.ts +104 -0
  285. package/template/packages/ai-client/src/document/validation.ts +115 -0
  286. package/template/packages/ai-client/src/embedding/client.test.ts +77 -0
  287. package/template/packages/ai-client/src/embedding/client.ts +96 -0
  288. package/template/packages/ai-client/src/embedding/contracts.ts +46 -0
  289. package/template/packages/ai-client/src/embedding/error.ts +61 -0
  290. package/template/packages/ai-client/src/embedding/index.ts +3 -0
  291. package/template/packages/ai-client/src/embedding/validation.ts +117 -0
  292. package/template/packages/ai-client/src/speech/client.test.ts +135 -0
  293. package/template/packages/ai-client/src/speech/client.ts +104 -0
  294. package/template/packages/ai-client/src/speech/contracts.ts +129 -0
  295. package/template/packages/ai-client/src/speech/error.ts +43 -0
  296. package/template/packages/ai-client/src/speech/index.ts +3 -0
  297. package/template/packages/ai-client/src/speech/run.ts +133 -0
  298. package/template/packages/ai-client/src/speech/validation.ts +132 -0
  299. package/template/packages/config/package.json +2 -2
  300. package/template/packages/database/drizzle/0039_early_ezekiel_stane.sql +1 -0
  301. package/template/packages/database/drizzle/0040_colossal_baron_strucker.sql +63 -0
  302. package/template/packages/database/drizzle/0041_yummy_prowler.sql +21 -0
  303. package/template/packages/database/drizzle/0042_puzzling_rumiko_fujikawa.sql +27 -0
  304. package/template/packages/database/drizzle/meta/0039_snapshot.json +7755 -0
  305. package/template/packages/database/drizzle/meta/0040_snapshot.json +8150 -0
  306. package/template/packages/database/drizzle/meta/0041_snapshot.json +8323 -0
  307. package/template/packages/database/drizzle/meta/0042_snapshot.json +8541 -0
  308. package/template/packages/database/drizzle/meta/_journal.json +28 -0
  309. package/template/packages/database/package.json +2 -2
  310. package/template/packages/database/src/agent-runtime-schema.test.ts +54 -0
  311. package/template/packages/database/src/schema/agent-runtime.ts +149 -0
  312. package/template/packages/database/src/schema/common.ts +2 -14
  313. package/template/packages/database/src/schema/durable-jobs.ts +5 -0
  314. package/template/packages/database/src/schema/governed-exports.ts +17 -5
  315. package/template/packages/database/src/schema/index.ts +3 -0
  316. package/template/packages/database/src/schema-durable-recovery.test.ts +31 -0
  317. package/template/packages/database/src/schema-governed-exports.test.ts +21 -1
  318. package/template/packages/database/src/schema.test.ts +6 -23
  319. package/template/packages/jobs/package.json +3 -2
  320. package/template/packages/jobs/src/handlers.test.ts +13 -0
  321. package/template/packages/jobs/src/handlers.ts +7 -1
  322. package/template/packages/jobs/src/types.ts +4 -0
  323. package/template/packages/modules/package.json +2 -2
  324. package/template/packages/modules/src/ai-operation-composition.test-support.ts +165 -0
  325. package/template/packages/modules/src/ai-operation-composition.test.ts +91 -127
  326. package/template/packages/modules/src/ai-operation-composition.ts +59 -1
  327. package/template/packages/modules/src/manifest.ts +25 -1
  328. package/template/packages/modules/src/tool-composition.ts +18 -0
  329. package/template/packages/server-sdk/index.ts +50 -0
  330. package/template/packages/server-sdk/package.json +10 -4
  331. package/template/packages/server-sdk/src/ai-artifact-port.ts +34 -0
  332. package/template/packages/server-sdk/src/ai-operation-port.ts +65 -0
  333. package/template/packages/server-sdk/src/ai-operation-registration.ts +42 -1
  334. package/template/packages/server-sdk/src/background-ai-port.ts +43 -0
  335. package/template/packages/server-sdk/src/data-governance-registration.ts +116 -0
  336. package/template/packages/server-sdk/src/durable-handler.ts +29 -0
  337. package/template/packages/server-sdk/src/jobs-port.ts +69 -0
  338. package/template/packages/server-sdk/src/module-host.ts +16 -2
  339. package/template/packages/server-sdk/src/request-context.ts +6 -2
  340. package/template/packages/server-sdk/src/sandbox-tool.ts +21 -0
  341. package/template/packages/server-sdk/src/tool-registration.ts +2 -0
  342. package/template/packages/shared/package.json +1 -1
  343. package/template/packages/shared/src/rbac.ts +30 -0
package/dist/cli.js CHANGED
@@ -229,7 +229,9 @@ var optionalModuleManifests = [
229
229
  {
230
230
  ...module("system.agent", "Agent Tool", "\u53D7\u63A7 Agent \u5DE5\u5177\u5BBF\u4E3B\u3002", [
231
231
  "system",
232
- "system.audit"
232
+ "system.audit",
233
+ "system.storage",
234
+ "system.scheduler"
233
235
  ]),
234
236
  registryDependencies: [
235
237
  {
@@ -245,17 +247,41 @@ var optionalModuleManifests = [
245
247
  ],
246
248
  fileRoots: [
247
249
  "apps/agent-server",
250
+ "apps/document-server",
251
+ "apps/speech-server",
252
+ "apps/embedding-server",
248
253
  "apps/server/src/agent",
254
+ "apps/server/src/ai-artifact",
255
+ "apps/server/src/document",
256
+ "apps/server/src/speech",
257
+ "apps/server/src/embedding",
249
258
  "apps/server/src/mcp",
250
259
  "apps/server/src/module-host/tool-handlers.ts",
251
260
  "apps/server/src/module-host/tool-handlers.test.ts",
252
- "packages/modules/src/system-agent-tools.ts"
261
+ "packages/modules/src/system-agent-tools.ts",
262
+ "packages/database/src/agent-runtime-schema.test.ts"
253
263
  ],
254
264
  documentation: [
255
265
  "docs/architecture/ai-runtime.md",
256
266
  "docs/architecture/mcp-business-capabilities.md",
257
267
  "docs/platform/agent-runtime.md",
258
- "docs/platform/agent-tools.md"
268
+ "docs/platform/agent-tools.md",
269
+ "docs/platform/agent-tool-approval.md",
270
+ "docs/platform/agent-tool-sandbox.md",
271
+ "docs/platform/agent-production-store.md",
272
+ "docs/platform/agent-run-reattachment.md",
273
+ "docs/platform/background-ai-operations.md",
274
+ "docs/platform/document-ai-runtime.md",
275
+ "docs/platform/speech-ai-runtime.md",
276
+ "docs/platform/embedding-ai-runtime.md"
277
+ ],
278
+ schemaFiles: ["packages/database/src/schema/agent-runtime.ts"],
279
+ databaseTables: [
280
+ "agent_operation_releases",
281
+ "agent_runtime_runs",
282
+ "agent_runtime_events",
283
+ "agent_evaluation_results",
284
+ "agent_run_bindings"
259
285
  ]
260
286
  },
261
287
  {
@@ -267,7 +293,11 @@ var optionalModuleManifests = [
267
293
  "apps/server/src/scheduler",
268
294
  "apps/server/src/jobs",
269
295
  "apps/server/src/application-services.ts",
296
+ "apps/server/src/module-host/durable-context.ts",
297
+ "apps/server/src/module-host/durable-context.test.ts",
298
+ "apps/server/src/module-host/job-port.test.ts",
270
299
  "apps/server/src/module-host/task-definitions.ts",
300
+ "packages/database/src/schema-durable-recovery.test.ts",
271
301
  "apps/web/src/pages/system/components/SchedulerOperationsPanel.vue",
272
302
  "apps/web/src/pages/system/components/SchedulerOperationsPanel.webtest.ts",
273
303
  "apps/web/src/pages/system/components/SchedulerRunTable.vue",
@@ -401,6 +431,11 @@ var optionalModuleManifests = [
401
431
  ]),
402
432
  fileRoots: [
403
433
  "apps/server/src/data-governance",
434
+ "apps/server/src/module-host/governed-resource-routes.ts",
435
+ "apps/server/src/module-host/governed-export-routes.ts",
436
+ "apps/server/src/module-host/governed-export-route-support.ts",
437
+ "apps/server/src/module-host/governed-export-routes.test.ts",
438
+ "apps/server/src/module-host/governed-resources.test.ts",
404
439
  "packages/modules/src/system-api-data-governance.ts",
405
440
  "packages/modules/src/system-data-governance.ts",
406
441
  "packages/modules/src/system-governed-export-bindings.ts",
@@ -426,6 +461,7 @@ var optionalModuleManifests = [
426
461
  ],
427
462
  documentation: ["docs/development/example-work-order.md"],
428
463
  schemaFiles: ["packages/example-work-order/src/drizzle-schema.ts"],
464
+ migrationRoots: ["packages/example-work-order/drizzle"],
429
465
  databaseTables: ["work_orders"],
430
466
  databaseLineageOwner: "module"
431
467
  }
@@ -435,6 +471,7 @@ function module(name, label, description, dependencies) {
435
471
  return {
436
472
  name,
437
473
  version: "0.1.0",
474
+ windyRange: "^0.3.0",
438
475
  label,
439
476
  description,
440
477
  selection,
@@ -445,11 +482,15 @@ function module(name, label, description, dependencies) {
445
482
  fileRoots: [],
446
483
  documentation: [],
447
484
  schemaFiles: [],
485
+ migrationRoots: [],
448
486
  databaseTables: [],
449
487
  databaseLineageOwner: "platform",
450
488
  transientDatabaseTables: [],
451
489
  databaseStatementTokens: [],
452
- uninstallPolicy: "retain-data"
490
+ uninstallPolicy: "retain-data",
491
+ installRecipe: `module-${name}-install`,
492
+ updateRecipes: [`module-${name}-update-0.1.0`],
493
+ uninstallRecipe: `module-${name}-uninstall`
453
494
  };
454
495
  }
455
496
 
@@ -507,7 +548,7 @@ var platformModules = [
507
548
  {
508
549
  sourceName: "@southwind-ai/license",
509
550
  name: "@southwind-ai/license",
510
- version: "^0.1.3"
551
+ version: "^0.1.4"
511
552
  }
512
553
  ],
513
554
  fileRoots: [
@@ -654,6 +695,7 @@ function module2(name, label, description, selection, dependencies = []) {
654
695
  return {
655
696
  name,
656
697
  version: "0.1.0",
698
+ windyRange: "^0.3.0",
657
699
  label,
658
700
  description,
659
701
  selection,
@@ -664,11 +706,15 @@ function module2(name, label, description, selection, dependencies = []) {
664
706
  fileRoots: [],
665
707
  documentation: [],
666
708
  schemaFiles: [],
709
+ migrationRoots: [],
667
710
  databaseTables: [],
668
711
  databaseLineageOwner: "platform",
669
712
  transientDatabaseTables: [],
670
713
  databaseStatementTokens: [],
671
- uninstallPolicy: "retain-data"
714
+ uninstallPolicy: "retain-data",
715
+ installRecipe: `module-${name}-install`,
716
+ updateRecipes: [`module-${name}-update-0.1.0`],
717
+ uninstallRecipe: `module-${name}-uninstall`
672
718
  };
673
719
  }
674
720
  function validateCatalog(manifests) {
@@ -689,6 +735,12 @@ function validateCatalog(manifests) {
689
735
  if (!names.has(conflict))
690
736
  throw new Error(`${manifest.name} \u51B2\u7A81\u6A21\u5757\u672A\u77E5\uFF1A${conflict}`);
691
737
  }
738
+ if (!manifest.installRecipe) {
739
+ throw new Error(`${manifest.name} \u7F3A\u5C11\u5B89\u88C5 recipe`);
740
+ }
741
+ if (!manifest.uninstallRecipe) {
742
+ throw new Error(`${manifest.name} \u7F3A\u5C11\u5378\u8F7D recipe`);
743
+ }
692
744
  }
693
745
  return manifests;
694
746
  }
@@ -773,6 +825,11 @@ var helpText = `create-windy <\u9879\u76EE\u540D> [\u9009\u9879]
773
825
  create-windy update --check [--cwd <\u76EE\u5F55>]
774
826
  create-windy update --dry-run [--to <\u7248\u672C>] [--cwd <\u76EE\u5F55>]
775
827
  create-windy update [--to <\u7248\u672C>] [--cwd <\u76EE\u5F55>]
828
+ create-windy module list [--cwd <\u76EE\u5F55>]
829
+ create-windy module add <key...> [--dry-run] [--cwd <\u76EE\u5F55>]
830
+ create-windy module update [key...] [--dry-run] [--cwd <\u76EE\u5F55>]
831
+ create-windy module remove <key...> [--dry-run] [--data-disposition <\u7B56\u7565>]
832
+ [--data-plan <\u6587\u4EF6>] [--confirm-data-disposition] [--cwd <\u76EE\u5F55>]
776
833
  create-windy reconcile --to <\u7CBE\u786E\u7248\u672C> [--cwd <\u76EE\u5F55>] [--apply]
777
834
  create-windy schema handoff [--config <JSON>] [--cwd <\u76EE\u5F55>]
778
835
 
@@ -962,6 +1019,9 @@ async function initializeLocalEnvironment(projectRoot, generateSecret = defaultS
962
1019
  if (/^AGENT_SERVER_INTERNAL_TOKEN=[ \t\r]*$/m.test(content)) {
963
1020
  content = setEmptyVariable(content, "AGENT_SERVER_INTERNAL_TOKEN", generateSecret());
964
1021
  }
1022
+ if (/^AI_ARTIFACT_ENCRYPTION_SECRET=[ \t\r]*$/m.test(content)) {
1023
+ content = setEmptyVariable(content, "AI_ARTIFACT_ENCRYPTION_SECRET", generateSecret());
1024
+ }
965
1025
  let handle;
966
1026
  try {
967
1027
  handle = await open(target, "wx", 384);
@@ -979,7 +1039,11 @@ async function initializeLocalEnvironment(projectRoot, generateSecret = defaultS
979
1039
  return "created";
980
1040
  }
981
1041
  function setEmptyVariable(source, key, value) {
982
- if (value.length < 12 || /\s|=/.test(value)) {
1042
+ const minimumLength = [
1043
+ "AGENT_SERVER_INTERNAL_TOKEN",
1044
+ "AI_ARTIFACT_ENCRYPTION_SECRET"
1045
+ ].includes(key) ? 32 : 12;
1046
+ if (value.length < minimumLength || /\s|=/.test(value)) {
983
1047
  throw new Error(`${key} \u7684\u968F\u673A\u503C\u4E0D\u6EE1\u8DB3\u672C\u5730\u5BC6\u7801\u5B89\u5168\u8981\u6C42`);
984
1048
  }
985
1049
  const pattern = new RegExp(`^${key}=[ \\t\\r]*$`, "m");
@@ -3212,9 +3276,9 @@ MCP \u6BCF\u6B21\u8C03\u7528\u90FD\u4F1A\u91CD\u65B0\u6267\u884C Feature\u3001RB
3212
3276
  \u548C \`dev:agent\` \u662F\u53E6\u4E00\u6761\u53EF\u9009\u94FE\u8DEF\u3002\u5B8C\u6574\u8FB9\u754C\u89C1
3213
3277
  [MCP \u4E1A\u52A1\u80FD\u529B](./docs/architecture/mcp-business-capabilities.md) \u4E0E
3214
3278
  [Agent Tool](./docs/platform/agent-tools.md)\u3002` : `\u5F53\u524D\u7EC4\u5408\u672A\u5305\u542B \`system.agent\`\uFF0CMCP Server\u3001Agent Tool Host \u548C\u76F8\u5173\u6587\u6863\u5DF2\u88AB
3215
- \u7269\u7406\u88C1\u526A\u3002\u82E5\u9879\u76EE\u5C1A\u53EF\u91CD\u65B0\u751F\u6210\uFF0C\u8BF7\u5728\u521B\u5EFA\u65F6\u589E\u52A0 \`--module system.agent\` \u6216\u4F7F\u7528
3216
- \`--all-modules\`\uFF1B\u90E8\u7F72\u540E\u7684\u901A\u7528 \`module add\` \u5C1A\u672A\u4EA4\u4ED8\uFF0C\u4E0D\u8981\u624B\u5DE5\u590D\u5236 MCP Adapter
3217
- \u6216\u53E6\u5EFA\u6388\u6743\u94FE\u3002\u4ED3\u5E93 Skill \u4ECD\u53EF\u7528\u4E8E\u666E\u901A\u4E1A\u52A1\u6A21\u5757\u5F00\u53D1\u3002`}
3279
+ \u7269\u7406\u88C1\u526A\u3002\u521B\u5EFA\u65F6\u53EF\u589E\u52A0 \`--module system.agent\` \u6216\u4F7F\u7528 \`--all-modules\`\uFF1B\u5DF2\u90E8\u7F72
3280
+ \u9879\u76EE\u4F7F\u7528 \`bun run windy module add system.agent\`\uFF0C\u4E0D\u8981\u624B\u5DE5\u590D\u5236 MCP Adapter \u6216
3281
+ \u53E6\u5EFA\u6388\u6743\u94FE\u3002\u4ED3\u5E93 Skill \u4ECD\u53EF\u7528\u4E8E\u666E\u901A\u4E1A\u52A1\u6A21\u5757\u5F00\u53D1\u3002`}
3218
3282
  `;
3219
3283
  }
3220
3284
 
@@ -3266,7 +3330,13 @@ var starterComposeBase = `services:
3266
3330
  PLATFORM_ENABLE_AI: \${PLATFORM_ENABLE_AI:-false}
3267
3331
  AGENT_SERVER_URL: http://agent-server:8080
3268
3332
  AGENT_TOOL_HOST_URL: http://server:9747
3333
+ __TOOL_SANDBOX_SERVER_ENV__
3269
3334
  AGENT_SERVER_INTERNAL_TOKEN: \${AGENT_SERVER_INTERNAL_TOKEN:-}
3335
+ AI_ARTIFACT_URL: http://server:9747/api/internal/ai/artifacts
3336
+ AI_ARTIFACT_ENCRYPTION_SECRET: \${AI_ARTIFACT_ENCRYPTION_SECRET:-}
3337
+ DOCUMENT_SERVER_URL: \${DOCUMENT_SERVER_URL:-}
3338
+ SPEECH_SERVER_URL: \${SPEECH_SERVER_URL:-}
3339
+ EMBEDDING_SERVER_URL: \${EMBEDDING_SERVER_URL:-}
3270
3340
  __OPENSEARCH_SERVER_ENV__
3271
3341
  ports:
3272
3342
  - "9747:9747"
@@ -3274,6 +3344,7 @@ __OPENSEARCH_SERVER_ENV__
3274
3344
  - ./apps/server:/app/apps/server
3275
3345
  - ./packages:/app/packages
3276
3346
  - server-audit-data:/app/data/audit
3347
+ __TOOL_SANDBOX_SERVER_VOLUME__
3277
3348
  depends_on:
3278
3349
  migrate:
3279
3350
  condition: service_completed_successfully
@@ -3337,7 +3408,53 @@ var openSearchComposeService = ` opensearch:
3337
3408
  retries: 12
3338
3409
 
3339
3410
  `;
3340
- var agentComposeService = ` agent-server:
3411
+ var agentComposeService = ` tool-sandbox-init:
3412
+ profiles: ["agent"]
3413
+ build:
3414
+ context: .
3415
+ dockerfile: apps/server/Dockerfile
3416
+ user: "0:0"
3417
+ command:
3418
+ [
3419
+ "sh",
3420
+ "-c",
3421
+ "chmod 0700 /run/windy-tool-sandbox && chown 1000:1000 /run/windy-tool-sandbox",
3422
+ ]
3423
+ network_mode: none
3424
+ read_only: true
3425
+ cap_drop: ["ALL"]
3426
+ cap_add: ["CHOWN"]
3427
+ security_opt:
3428
+ - no-new-privileges:true
3429
+ volumes:
3430
+ - tool-sandbox-ipc:/run/windy-tool-sandbox
3431
+ healthcheck:
3432
+ disable: true
3433
+
3434
+ tool-sandbox:
3435
+ profiles: ["agent"]
3436
+ build:
3437
+ context: .
3438
+ dockerfile: apps/server/Dockerfile
3439
+ command: ["bun", "run", "apps/server/src/agent/sandbox-worker.ts"]
3440
+ environment:
3441
+ TOOL_SANDBOX_SOCKET: /run/windy-tool-sandbox/worker.sock
3442
+ network_mode: none
3443
+ read_only: true
3444
+ tmpfs:
3445
+ - /tmp:rw,noexec,nosuid,nodev,size=16m
3446
+ cap_drop: ["ALL"]
3447
+ security_opt:
3448
+ - no-new-privileges:true
3449
+ volumes:
3450
+ - tool-sandbox-ipc:/run/windy-tool-sandbox
3451
+ depends_on:
3452
+ tool-sandbox-init:
3453
+ condition: service_completed_successfully
3454
+ healthcheck:
3455
+ disable: true
3456
+
3457
+ agent-server:
3341
3458
  profiles: ["agent"]
3342
3459
  build:
3343
3460
  context: .
@@ -3345,6 +3462,10 @@ var agentComposeService = ` agent-server:
3345
3462
  environment:
3346
3463
  AGENT_SERVER_INTERNAL_TOKEN: \${AGENT_SERVER_INTERNAL_TOKEN:-}
3347
3464
  AGENT_RUN_STORE_PATH: /app/data/agent/runs.sqlite3
3465
+ AGENT_DATABASE_URL: postgresql://windy:\${POSTGRES_PASSWORD}@postgres:5432/windy
3466
+ AGENT_RUN_ENCRYPTION_SECRET: \${AGENT_RUN_ENCRYPTION_SECRET:-}
3467
+ OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: \${OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:-}
3468
+ OTEL_EXPORTER_OTLP_HEADERS: \${OTEL_EXPORTER_OTLP_HEADERS:-}
3348
3469
  AI_PROVIDER_AUDIT_STORE_PATH: /app/data/agent/provider-audit.sqlite3
3349
3470
  AI_PROVIDER_AUDIT_RETENTION_DAYS: \${AI_PROVIDER_AUDIT_RETENTION_DAYS:-90}
3350
3471
  QWEN_API_URL: \${QWEN_API_URL:-}
@@ -3354,8 +3475,75 @@ var agentComposeService = ` agent-server:
3354
3475
  AI_PROVIDER_CONNECT_TIMEOUT_MS: \${AI_PROVIDER_CONNECT_TIMEOUT_MS:-5000}
3355
3476
  AI_PROVIDER_REQUEST_TIMEOUT_MS: \${AI_PROVIDER_REQUEST_TIMEOUT_MS:-60000}
3356
3477
  AI_PROVIDER_MAX_RESPONSE_BYTES: \${AI_PROVIDER_MAX_RESPONSE_BYTES:-2097152}
3478
+ AI_PROVIDER_CAPABILITIES: \${AI_PROVIDER_CAPABILITIES:-text-generation}
3479
+ AI_PROVIDER_INPUT_MODALITIES: \${AI_PROVIDER_INPUT_MODALITIES:-text}
3357
3480
  volumes:
3358
3481
  - agent-run-data:/app/data/agent
3482
+ depends_on:
3483
+ migrate:
3484
+ condition: service_completed_successfully
3485
+ server:
3486
+ condition: service_started
3487
+
3488
+ document-server:
3489
+ profiles: ["document"]
3490
+ build:
3491
+ context: .
3492
+ dockerfile: apps/document-server/Dockerfile
3493
+ environment:
3494
+ DOCUMENT_SERVER_INTERNAL_TOKEN: \${AGENT_SERVER_INTERNAL_TOKEN:-}
3495
+ DOCUMENT_RUN_STORE_PATH: /app/data/document/runs.sqlite3
3496
+ DOCUMENT_ARTIFACT_URL: \${DOCUMENT_ARTIFACT_URL:-http://server:9747/api/internal/ai/artifacts}
3497
+ DOCUMENT_ADAPTER_URL: \${DOCUMENT_ADAPTER_URL:-}
3498
+ DOCUMENT_ADAPTER_TOKEN: \${DOCUMENT_ADAPTER_TOKEN:-}
3499
+ DOCUMENT_DEPLOYMENT_KEY: \${DOCUMENT_DEPLOYMENT_KEY:-}
3500
+ DOCUMENT_ADAPTER_CAPABILITIES: \${DOCUMENT_ADAPTER_CAPABILITIES:-document.ocr,document.layout,document.table}
3501
+ DOCUMENT_MAX_UNITS: \${DOCUMENT_MAX_UNITS:-500}
3502
+ volumes:
3503
+ - document-run-data:/app/data/document
3504
+ depends_on:
3505
+ server:
3506
+ condition: service_started
3507
+
3508
+ speech-server:
3509
+ profiles: ["speech"]
3510
+ build:
3511
+ context: .
3512
+ dockerfile: apps/speech-server/Dockerfile
3513
+ environment:
3514
+ SPEECH_SERVER_INTERNAL_TOKEN: \${AGENT_SERVER_INTERNAL_TOKEN:-}
3515
+ SPEECH_RUN_STORE_PATH: /app/data/speech/runs.sqlite3
3516
+ SPEECH_ARTIFACT_URL: \${SPEECH_ARTIFACT_URL:-http://server:9747/api/internal/ai/artifacts}
3517
+ SPEECH_ADAPTER_URL: \${SPEECH_ADAPTER_URL:-}
3518
+ SPEECH_ADAPTER_TOKEN: \${SPEECH_ADAPTER_TOKEN:-}
3519
+ SPEECH_DEPLOYMENT_KEY: \${SPEECH_DEPLOYMENT_KEY:-}
3520
+ SPEECH_ADAPTER_CAPABILITIES: \${SPEECH_ADAPTER_CAPABILITIES:-speech.transcribe.batch,speech.normalize,speech.vad,speech.timestamps}
3521
+ SPEECH_MAX_UNITS: \${SPEECH_MAX_UNITS:-100000}
3522
+ SPEECH_MAX_MEDIA_DURATION_SECONDS: \${SPEECH_MAX_MEDIA_DURATION_SECONDS:-86400}
3523
+ volumes:
3524
+ - speech-run-data:/app/data/speech
3525
+ depends_on:
3526
+ server:
3527
+ condition: service_started
3528
+
3529
+ embedding-server:
3530
+ profiles: ["embedding"]
3531
+ build:
3532
+ context: .
3533
+ dockerfile: apps/embedding-server/Dockerfile
3534
+ environment:
3535
+ EMBEDDING_SERVER_INTERNAL_TOKEN: \${AGENT_SERVER_INTERNAL_TOKEN:-}
3536
+ EMBEDDING_ARTIFACT_URL: \${EMBEDDING_ARTIFACT_URL:-http://server:9747/api/internal/ai/artifacts}
3537
+ EMBEDDING_ADAPTER_URL: \${EMBEDDING_ADAPTER_URL:-}
3538
+ EMBEDDING_ADAPTER_TOKEN: \${EMBEDDING_ADAPTER_TOKEN:-}
3539
+ EMBEDDING_DEPLOYMENT_KEY: \${EMBEDDING_DEPLOYMENT_KEY:-}
3540
+ EMBEDDING_DEPLOYMENT_ALIAS: \${EMBEDDING_DEPLOYMENT_ALIAS:-}
3541
+ EMBEDDING_MODEL: \${EMBEDDING_MODEL:-}
3542
+ EMBEDDING_MODEL_VERSION: \${EMBEDDING_MODEL_VERSION:-}
3543
+ EMBEDDING_DIMENSIONS: \${EMBEDDING_DIMENSIONS:-}
3544
+ EMBEDDING_NORMALIZED: \${EMBEDDING_NORMALIZED:-true}
3545
+ EMBEDDING_CAPABILITIES: \${EMBEDDING_CAPABILITIES:-embedding.text}
3546
+ EMBEDDING_MAX_UNITS: \${EMBEDDING_MAX_UNITS:-256}
3359
3547
  depends_on:
3360
3548
  server:
3361
3549
  condition: service_started
@@ -3402,16 +3590,20 @@ var agentComposeService = ` agent-server:
3402
3590
  function starterComposeFor(includeAgent, includeSearch = true) {
3403
3591
  return starterComposeBase.replace(` web:
3404
3592
  `, `${includeAgent ? agentComposeService : ""} web:
3405
- `).replace("__OPENSEARCH_SERVER_ENV__", includeSearch ? ` OPENSEARCH_URL: \${OPENSEARCH_URL:-}
3593
+ `).replace("__TOOL_SANDBOX_SERVER_ENV__", includeAgent ? " TOOL_SANDBOX_SOCKET: /run/windy-tool-sandbox/worker.sock" : "").replace("__TOOL_SANDBOX_SERVER_VOLUME__", includeAgent ? " - tool-sandbox-ipc:/run/windy-tool-sandbox" : "").replace("__OPENSEARCH_SERVER_ENV__", includeSearch ? ` OPENSEARCH_URL: \${OPENSEARCH_URL:-}
3406
3594
  OPENSEARCH_USERNAME: \${OPENSEARCH_USERNAME:-}
3407
3595
  OPENSEARCH_PASSWORD: \${OPENSEARCH_PASSWORD:-}
3408
3596
  OPENSEARCH_WORK_ORDER_INDEX: \${OPENSEARCH_WORK_ORDER_INDEX:-windy-work-orders-v1}` : "").replace("__OPENSEARCH_SERVICE__", includeSearch ? openSearchComposeService : "").replace("__OPTIONAL_VOLUMES__", [
3409
3597
  includeAgent ? " agent-run-data:" : "",
3598
+ includeAgent ? " tool-sandbox-ipc:" : "",
3599
+ includeAgent ? " document-run-data:" : "",
3600
+ includeAgent ? " speech-run-data:" : "",
3410
3601
  includeSearch ? " opensearch-data:" : ""
3411
3602
  ].filter(Boolean).join(`
3412
3603
  `));
3413
3604
  }
3414
3605
  var starterCompose = starterComposeFor(true);
3606
+ // src/runtime-environment-example.ts
3415
3607
  function environmentExample(includeExample, includeAgent = true, includeSearch = true) {
3416
3608
  return `# \u590D\u5236\u4E3A .env \u540E\u586B\u5199\u3002\u4E0D\u8981\u63D0\u4EA4\u771F\u5B9E\u5BC6\u7801\u3002
3417
3609
  POSTGRES_PASSWORD=
@@ -3421,10 +3613,47 @@ ${includeAgent ? `
3421
3613
  # ADK Web \u8C03\u8BD5 profile \u4E0D\u8BFB\u53D6\u4EE5\u4E0B\u53D8\u91CF\uFF0C\u4E5F\u4E0D\u80FD\u8C03\u7528\u771F\u5B9E\u4E1A\u52A1 Operation \u6216 Tool\u3002
3422
3614
  PLATFORM_ENABLE_AI=false
3423
3615
  AGENT_SERVER_INTERNAL_TOKEN=
3616
+ AGENT_RUN_ENCRYPTION_SECRET=
3617
+ # \u591A\u526F\u672C\u751F\u4EA7\u9ED8\u8BA4\u4F7F\u7528 Compose \u6CE8\u5165\u7684 PostgreSQL\uFF1B\u7559\u7A7A\u624D\u56DE\u9000\u5355\u526F\u672C SQLite\u3002
3618
+ AGENT_DATABASE_URL=
3619
+ # \u53EF\u9009\uFF1A\u53EA\u53D1\u9001\u65E0 Prompt/\u8F93\u5165/\u8F93\u51FA\u7684 Agent Run \u5143\u6570\u636E Span\u3002
3620
+ OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=
3621
+ OTEL_EXPORTER_OTLP_HEADERS=
3622
+ AI_ARTIFACT_ENCRYPTION_SECRET=
3623
+ DOCUMENT_SERVER_URL=
3624
+ DOCUMENT_ARTIFACT_URL=
3625
+ DOCUMENT_ADAPTER_URL=
3626
+ DOCUMENT_ADAPTER_TOKEN=
3627
+ DOCUMENT_DEPLOYMENT_KEY=
3628
+ # DOCUMENT_ADAPTER_CAPABILITIES=document.ocr,document.layout,document.table
3629
+ # DOCUMENT_MAX_UNITS=500
3630
+ SPEECH_SERVER_URL=
3631
+ SPEECH_ARTIFACT_URL=
3632
+ SPEECH_ADAPTER_URL=
3633
+ SPEECH_ADAPTER_TOKEN=
3634
+ SPEECH_DEPLOYMENT_KEY=
3635
+ # SPEECH_ADAPTER_CAPABILITIES=speech.transcribe.batch,speech.normalize,speech.vad,speech.timestamps
3636
+ # SPEECH_MAX_UNITS=100000
3637
+ # SPEECH_MAX_MEDIA_DURATION_SECONDS=86400
3638
+ EMBEDDING_SERVER_URL=
3639
+ EMBEDDING_ARTIFACT_URL=
3640
+ EMBEDDING_ADAPTER_URL=
3641
+ EMBEDDING_ADAPTER_TOKEN=
3642
+ EMBEDDING_DEPLOYMENT_KEY=
3643
+ EMBEDDING_DEPLOYMENT_ALIAS=
3644
+ EMBEDDING_MODEL=
3645
+ EMBEDDING_MODEL_VERSION=
3646
+ EMBEDDING_DIMENSIONS=
3647
+ # EMBEDDING_NORMALIZED=true
3648
+ # EMBEDDING_CAPABILITIES=embedding.text,embedding.image
3649
+ # EMBEDDING_MAX_UNITS=256
3424
3650
  QWEN_API_URL=
3425
3651
  QWEN_API_KEY=
3426
3652
  QWEN_MODEL=
3427
3653
  AI_PROVIDER_HTTPS_HOST_ALLOWLIST=
3654
+ # VLM Deployment \u793A\u4F8B\uFF1Atext-generation,vision\uFF1B\u8F93\u5165\u6A21\u6001\uFF1Atext,image
3655
+ # AI_PROVIDER_CAPABILITIES=text-generation
3656
+ # AI_PROVIDER_INPUT_MODALITIES=text
3428
3657
  # AI_PROVIDER_AUDIT_RETENTION_DAYS=90
3429
3658
  # AI_PROVIDER_CONNECT_TIMEOUT_MS=5000
3430
3659
  # AI_PROVIDER_REQUEST_TIMEOUT_MS=60000
@@ -3448,7 +3677,7 @@ OPENSEARCH_PASSWORD=
3448
3677
  `;
3449
3678
  }
3450
3679
  // src/project-files.ts
3451
- function starterDocsReadme(includeOfflineLicense = true, includeAgent = true) {
3680
+ function starterDocsReadme(includeOfflineLicense = true, includeAgent = true, includeOperations = true) {
3452
3681
  return `# Windy Starter \u6587\u6863
3453
3682
 
3454
3683
  \u672C\u76EE\u5F55\u53EA\u5305\u542B\u5BA2\u6237\u8FD0\u884C\u9879\u76EE\u9700\u8981\u7684\u5E73\u53F0\u67B6\u6784\u3001\u5F00\u53D1\u63A5\u5165\u548C\u8FD0\u7EF4\u53EF\u89C2\u6D4B\u6027\u6587\u6863\u3002
@@ -3458,10 +3687,12 @@ License \u7B7E\u53D1\u4E2D\u5FC3\u3001\u72EC\u7ACB\u7B7E\u540D\u670D\u52A1\u3001
3458
3687
  - \u67B6\u6784\uFF1A\u4ECE [Module Manifest \u7EC4\u5408](./architecture/module-manifest-composition.md) \u5F00\u59CB\u3002
3459
3688
  - \u6570\u636E\u5E93\uFF1A\u4E1A\u52A1\u8868\u4F7F\u7528 [Migration Bundle \u4E0E\u72EC\u7ACB Drizzle lineage](./architecture/migration-bundle.md)\u3002
3460
3689
  - \u5E73\u53F0\u5F00\u53D1\uFF1A\u4ECE [\u7CFB\u7EDF CRUD API](./platform/system-crud-api.md) \u548C [CRUD \u751F\u6210\u5668](./development/crud-generator.md) \u5F00\u59CB\u3002
3690
+ - Web \u4E1A\u52A1\u9875\u9762\uFF1A[project-owned \u4E1A\u52A1 Web \u8DEF\u7531](./development/business-web-routes.md)\u3002
3691
+ - \u90E8\u7F72\u671F\u6A21\u5757\u7BA1\u7406\uFF1A[\u6A21\u5757\u751F\u547D\u5468\u671F](./development/module-lifecycle.md)\u3002
3461
3692
  - \u4E1A\u52A1\u6A21\u5757\u5F00\u53D1\uFF1A[windy-business-module Skill](../.agents/skills/windy-business-module/SKILL.md)\u3002
3462
3693
  ${includeOfflineLicense ? "- License \u6D88\u8D39\u7AEF\uFF1A[\u79BB\u7EBF\u6FC0\u6D3B\u4E0E\u5B89\u88C5\u534F\u8BAE](./license/license-offline-activation-code.md)\uFF1B\u8FD0\u884C\u65F6\u80FD\u529B\u7531 npm \u4F9D\u8D56 `@southwind-ai/license` \u63D0\u4F9B\u3002" : ""}
3463
- - \u8FD0\u7EF4\uFF1A[\u53EF\u89C2\u6D4B\u6027](./operations/observability.md) \u4E0E [\u8FD0\u7EF4\u603B\u89C8](./platform/operations-overview.md)\u3002
3464
- ${includeAgent ? "- Agent \u4E0E MCP\uFF1A[Agent Runtime \u63A5\u5165](./platform/agent-runtime.md)\u3001[\u53D7\u63A7\u5DE5\u5177](./platform/agent-tools.md)\u4E0E[MCP \u4E1A\u52A1\u80FD\u529B](./architecture/mcp-business-capabilities.md)\u3002" : ""}
3694
+ - \u8FD0\u7EF4\uFF1A[\u53EF\u89C2\u6D4B\u6027](./operations/observability.md)${includeOperations ? " \u4E0E [\u8FD0\u7EF4\u603B\u89C8](./platform/operations-overview.md)" : ""}\u3002
3695
+ ${includeAgent ? "- Agent\u3001\u591A\u6A21\u6001 Runtime \u4E0E MCP\uFF1A[Agent Runtime \u63A5\u5165](./platform/agent-runtime.md)\u3001[Document AI Runtime](./platform/document-ai-runtime.md)\u3001[Speech AI Runtime](./platform/speech-ai-runtime.md)\u3001[\u53D7\u63A7\u5DE5\u5177](./platform/agent-tools.md)\u4E0E[MCP \u4E1A\u52A1\u80FD\u529B](./architecture/mcp-business-capabilities.md)\u3002" : ""}
3465
3696
  - \u517C\u5BB9\uFF1A[RuoYi-Vue Adapter](./adapters/ruoyi-vue-compatibility.md)\u3002
3466
3697
  `;
3467
3698
  }
@@ -3469,7 +3700,7 @@ function starterReadme(projectName, includeExample = true, includeOxc = true, pr
3469
3700
  const selected = new Set(selectedModules);
3470
3701
  const includeAgent = selected.has("system.agent");
3471
3702
  const includeSearch = selected.has("system.search");
3472
- const moduleRows = moduleDistributionCatalog.map(({ name, description, selection }) => `| \`${name}\` | ${selected.has(name) ? "\u5DF2\u5305\u542B" : "\u672A\u5305\u542B"} | ${selection === "required" ? "H0" : "H2"} | ${description} |`).join(`
3703
+ const moduleRows = moduleDistributionCatalog.map(({ name, description, selection }) => `| \`${name}\` | ${selected.has(name) ? "\u5DF2\u5305\u542B" : "\u672A\u5305\u542B"} | ${selection === "required" ? "H0" : name === "work-order" ? "H3" : "H2"} | ${description} |`).join(`
3473
3704
  `);
3474
3705
  return `# ${projectName}
3475
3706
 
@@ -3543,6 +3774,30 @@ bun run dev:agent
3543
3774
  \`AI_PROVIDER_HTTPS_HOST_ALLOWLIST\`\u3002\u8FD0\u884C\u4E0E\u6A21\u5757
3544
3775
  \u63A5\u5165\u7EC6\u8282\u89C1 [Agent Runtime \u63A5\u5165](./docs/platform/agent-runtime.md)\u3002
3545
3776
 
3777
+ Document Runtime \u540C\u6837\u9ED8\u8BA4\u5173\u95ED\u3002\u914D\u7F6E\u81F3\u5C11 32 \u5B57\u7B26\u7684
3778
+ \`AI_ARTIFACT_ENCRYPTION_SECRET\`\u3001\u672C\u5730\u6216\u5185\u7F51 \`DOCUMENT_ADAPTER_URL\` \u53CA\u5176
3779
+ capability \u540E\u8FD0\u884C\uFF1A
3780
+
3781
+ \`\`\`bash
3782
+ bun run dev:document
3783
+ \`\`\`
3784
+
3785
+ \u8BE5 profile \u53EA\u63A5\u53D7\u5E73\u53F0\u7B7E\u53D1\u7684\u77ED\u65F6 opaque ArtifactRef\uFF1B\u4E1A\u52A1\u4E0D\u5F97\u4F20 Base64\u3001\u5BBF\u4E3B\u8DEF\u5F84\u6216
3786
+ \u4EFB\u610F\u516C\u7F51 URL\u3002\u672A\u914D\u7F6E Document Adapter \u65F6\u4EC5\u5BF9\u5E94 Operation \u8FD4\u56DE\u7A33\u5B9A unavailable\uFF0C
3787
+ \u666E\u901A\u4E1A\u52A1\u4E0E\u6587\u672C Agent \u4E0D\u53D7\u5F71\u54CD\u3002\u8BE6\u89C1
3788
+ [Document AI Runtime](./docs/platform/document-ai-runtime.md)\u3002
3789
+
3790
+ \u6279\u91CF\u8BED\u97F3\u8F6C\u5199\u4F7F\u7528\u72EC\u7ACB Speech profile\u3002\u914D\u7F6E\u672C\u5730/\u5185\u7F51
3791
+ \`SPEECH_ADAPTER_URL\` \u4E0E capability \u540E\u8FD0\u884C\uFF1A
3792
+
3793
+ \`\`\`bash
3794
+ bun run dev:speech
3795
+ \`\`\`
3796
+
3797
+ Speech Runtime \u53EA\u63A5\u6536\u6388\u6743 Artifact\uFF0C\u8D1F\u8D23\u683C\u5F0F\u5F52\u4E00\u5316\u3001VAD/\u65F6\u95F4\u6233\u5951\u7EA6\u3001\u7A33\u5B9A\u5206\u6BB5\u4E0E\u7ED3\u679C
3798
+ Artifact\uFF1B\u5B9E\u65F6 ASR\u3001TTS \u548C Dictation \u5C1A\u672A\u5F00\u653E\u3002\u8BE6\u89C1
3799
+ [Speech AI Runtime](./docs/platform/speech-ai-runtime.md)\u3002
3800
+
3546
3801
  ADK Web \u4EC5\u7528\u4E8E\u672C\u673A\u5F00\u53D1\u8C03\u8BD5\uFF1A
3547
3802
 
3548
3803
  \`\`\`bash
@@ -3602,19 +3857,22 @@ create-windy \u5728\u672C\u673A \`.env\` \u4E2D\u968F\u673A\u751F\u6210\u7684 \`
3602
3857
  | \u6CD5\u5F8B\u8BB8\u53EF | \`${projectLicense}\` |
3603
3858
 
3604
3859
  \u5E73\u53F0\u5F53\u524D\u6CE8\u518C\u7684\u5168\u90E8\u6A21\u5757\u5982\u4E0B\u3002H0 \u8868\u793A profile \u9501\u5B9A\u6838\u5FC3\uFF1BH2 \u8868\u793A\u521B\u5EFA\u65F6\u53EF\u4EE5\u7269\u7406\u5305\u542B\u6216
3605
- \u6392\u9664\uFF0C\u5E76\u5DF2\u6709\u751F\u6210\u3001\u7C7B\u578B\u68C0\u67E5\u3001\u6D4B\u8BD5\u548C\u6784\u5EFA\u8BC1\u636E\u3002H2 \u4E0D\u4EE3\u8868\u9879\u76EE\u90E8\u7F72\u540E\u5DF2\u7ECF\u652F\u6301\u65E0\u635F\u5B89\u88C5\u3001
3606
- \u5347\u7EA7\u6216\u5378\u8F7D\uFF1B\u90A3\u9700\u8981 H3 \u7684\u751F\u547D\u5468\u671F\u547D\u4EE4\u4E0E\u6570\u636E\u5904\u7F6E\u8BC1\u636E\u3002
3860
+ \u6392\u9664\uFF1BH3 \u8FD8\u5177\u5907\u90E8\u7F72\u671F\u5B89\u88C5\u3001\u66F4\u65B0\u3001\u6570\u636E\u4FDD\u7559\u5378\u8F7D\u548C\u91CD\u88C5\u8BC1\u636E\u3002\u901A\u7528\u751F\u547D\u5468\u671F\u547D\u4EE4\u4F1A\u4FDD\u5B88
3861
+ \u5904\u7406\u6240\u6709\u76EE\u5F55\u6A21\u5757\uFF0C\u4F46\u53EA\u6709\u901A\u8FC7\u5B8C\u6574 H3 \u56DE\u5F52\u7684\u6A21\u5757\u624D\u6807\u8BB0\u4E3A H3\u3002
3607
3862
 
3608
3863
  | Module Key | \u5F53\u524D\u9879\u76EE | \u70ED\u63D2\u62D4\u53EF\u9760\u5EA6 | \u8BF4\u660E |
3609
3864
  | ---------- | -------- | ------------ | ---- |
3610
3865
  ${moduleRows}
3611
3866
 
3612
3867
  \u9879\u76EE\u81EA\u6709\u4E1A\u52A1\u6A21\u5757\u4EE5 \`apps/server/src/installed-business-modules.ts\` \u88C5\u914D\u7684 Module Manifest
3613
- \u4E3A\u51C6\uFF1Bshared-scaffold README \u4E0D\u590D\u5236\u9879\u76EE\u4E1A\u52A1 Key\uFF0C\u907F\u514D\u5E73\u53F0\u5347\u7EA7\u56FA\u5316\u4EA7\u54C1\u547D\u540D\u3002
3868
+ \u4E3A\u51C6\uFF1B\u5BF9\u5E94 Vue \u9875\u9762\u5728 project-owned \u7684
3869
+ \`apps/web/src/app/business-routes.ts\` \u6CE8\u518C\uFF0C\u4FA7\u680F\u7EE7\u7EED\u4ECE\u670D\u52A1\u7AEF Manifest \u5FEB\u7167\u751F\u6210\u3002
3870
+ \u4E0D\u8981\u4E3A\u589E\u52A0\u4E1A\u52A1\u9875\u9762\u4FEE\u6539 shared-scaffold Router \u6216 AppSidebar\u3002shared-scaffold README
3871
+ \u4E0D\u590D\u5236\u9879\u76EE\u4E1A\u52A1 Key\uFF0C\u907F\u514D\u5E73\u53F0\u5347\u7EA7\u56FA\u5316\u4EA7\u54C1\u547D\u540D\u3002
3614
3872
 
3615
3873
  \u7CBE\u786E\u751F\u6210\u6765\u6E90\u548C\u9009\u62E9\u8BB0\u5F55\u5728 \`.windy/generation.json\`\u3002\u5F53\u524D\u7248\u672C\u5DF2\u63D0\u4F9B\u53EF\u56DE\u6EDA\u7684
3616
- \`windy update\`\uFF1B\u901A\u7528 \`module add/remove\` \u5C1A\u672A\u4EA4\u4ED8\uFF0C\u4E0D\u8981\u628A Feature Flag \u5173\u95ED\u5F53\u6210
3617
- \u6A21\u5757\u5DF2\u7ECF\u5378\u8F7D\u3002
3874
+ \`windy update\` \u4E0E \`windy module list/add/update/remove\`\u3002Feature Flag \u5173\u95ED\u4ECD\u4E0D
3875
+ \u7B49\u4E8E\u6A21\u5757\u5378\u8F7D\u3002
3618
3876
 
3619
3877
  ## \u5E38\u7528\u547D\u4EE4
3620
3878
 
@@ -3630,6 +3888,9 @@ bun run build
3630
3888
  bun run windy doctor
3631
3889
  bun run windy update --check
3632
3890
  bun run windy update --dry-run
3891
+ bun run windy module list
3892
+ bun run windy module add work-order --dry-run
3893
+ bun run windy module remove work-order --dry-run
3633
3894
  bun run windy reconcile --to <\u7CBE\u786E\u76EE\u6807\u7248\u672C>
3634
3895
  ${includeOxc ? `bun run lint
3635
3896
  bun run format` : "# \u672C\u9879\u76EE\u672A\u9884\u88C5 lint/format \u5DE5\u5177\uFF0C\u53EF\u6309\u56E2\u961F\u9700\u8981\u81EA\u884C\u9009\u62E9\u3002"}
@@ -3666,8 +3927,9 @@ create-windy \u9ED8\u8BA4\u5EFA\u7ACB \`main\` \u5206\u652F\u5E76\u521B\u5EFA \`
3666
3927
  \u6CA1\u6709\u914D\u7F6E Git \u7528\u6237\u8EAB\u4EFD\uFF0C\u6587\u4EF6\u4F1A\u4FDD\u7559\u5728\u6682\u5B58\u533A\uFF1B\u914D\u7F6E\u8EAB\u4EFD\u540E\u624B\u52A8\u63D0\u4EA4\u5373\u53EF\u3002
3667
3928
 
3668
3929
  \u5F53\u524D\u5DF2\u4EA4\u4ED8\u7684 Windy \u66F4\u65B0\u5668\u4F1A\u6839\u636E Project Lock \u548C\u6587\u4EF6\u6240\u6709\u6743\uFF0C\u4ECE npm \u5386\u53F2 artifact
3669
- \u91CD\u5EFA Base \u5E76\u6267\u884C\u4E09\u65B9\u5408\u5E76\uFF1B\u5B83\u4E0D\u8FDE\u63A5\u79C1\u6709\u6E90\u4ED3\u5E93\uFF0C\u4E5F\u4E0D\u4F1A\u9759\u9ED8\u8986\u76D6\u9879\u76EE\u4EE3\u7801\u3002\u90E8\u7F72\u671F\u901A\u7528
3670
- \`module add/update/remove\` \u5C1A\u672A\u4EA4\u4ED8\u3002
3930
+ \u91CD\u5EFA Base \u5E76\u6267\u884C\u4E09\u65B9\u5408\u5E76\uFF1B\u5B83\u4E0D\u8FDE\u63A5\u79C1\u6709\u6E90\u4ED3\u5E93\uFF0C\u4E5F\u4E0D\u4F1A\u9759\u9ED8\u8986\u76D6\u9879\u76EE\u4EE3\u7801\u3002\u6A21\u5757
3931
+ \u751F\u547D\u5468\u671F\u547D\u4EE4\u590D\u7528\u540C\u4E00\u8BA1\u5212\u3001\u4E8B\u52A1\u3001\u9A8C\u8BC1\u4E0E\u6062\u590D\u8FB9\u754C\uFF1B\u5378\u8F7D\u9ED8\u8BA4\u4FDD\u7559\u6570\u636E\u5E93\u6570\u636E\uFF0C\u5E76\u8981\u6C42
3932
+ \u663E\u5F0F\u786E\u8BA4\u6570\u636E\u5904\u7F6E\u8BA1\u5212\u3002
3671
3933
 
3672
3934
  ## License
3673
3935
 
@@ -3764,6 +4026,9 @@ var templateDirectories = [
3764
4026
  ".agents/skills/windy-business-module",
3765
4027
  "apps/server",
3766
4028
  "apps/agent-server",
4029
+ "apps/document-server",
4030
+ "apps/speech-server",
4031
+ "apps/embedding-server",
3767
4032
  "apps/web",
3768
4033
  "examples",
3769
4034
  "packages/ai-client",
@@ -3784,23 +4049,32 @@ var templateDocumentationFiles = [
3784
4049
  "docs/architecture/ai-runtime.md",
3785
4050
  "docs/architecture/data-scope-query-enforcement.md",
3786
4051
  "docs/architecture/durable-jobs.md",
4052
+ "docs/architecture/foundation-infrastructure.md",
3787
4053
  "docs/architecture/minimal-approval-workflow.md",
3788
4054
  "docs/architecture/mcp-business-capabilities.md",
3789
4055
  "docs/architecture/migration-bundle.md",
4056
+ "docs/architecture/module-host.md",
3790
4057
  "docs/architecture/module-manifest-composition.md",
3791
4058
  "docs/architecture/object-storage.md",
3792
4059
  "docs/architecture/search-provider-protocol.md",
4060
+ "docs/architecture/server-module-interface.md",
4061
+ "docs/architecture/web-access-snapshot-resilience.md",
4062
+ "docs/development/business-web-routes.md",
3793
4063
  "docs/development/crud-generator.md",
3794
4064
  "docs/development/custom-error-pages.md",
3795
4065
  "docs/development/custom-login-page.md",
4066
+ "docs/development/module-lifecycle.md",
3796
4067
  "docs/license/license-offline-activation-code.md",
3797
4068
  "docs/operations/observability.md",
3798
4069
  "docs/platform/agent-tools.md",
3799
4070
  "docs/platform/agent-runtime.md",
4071
+ "docs/platform/agent-tool-sandbox.md",
3800
4072
  "docs/platform/audit-reliability.md",
3801
4073
  "docs/platform/bulk-data-jobs.md",
3802
4074
  "docs/platform/business-user-directory.md",
3803
4075
  "docs/platform/data-access-control.md",
4076
+ "docs/platform/document-ai-runtime.md",
4077
+ "docs/platform/governed-export-renderers.md",
3804
4078
  "docs/platform/module-configuration.md",
3805
4079
  "docs/platform/navigation-registry.md",
3806
4080
  "docs/platform/notifications.md",
@@ -3809,7 +4083,9 @@ var templateDocumentationFiles = [
3809
4083
  "docs/platform/platform-authentication.md",
3810
4084
  "docs/platform/platform-shell-settings.md",
3811
4085
  "docs/platform/scheduled-task-executor.md",
4086
+ "docs/platform/speech-ai-runtime.md",
3812
4087
  "docs/platform/system-crud-api.md",
4088
+ "docs/platform/vision-ai-runtime.md",
3813
4089
  "docs/platform/web-system-crud.md",
3814
4090
  "docs/product/built-in-roles.md"
3815
4091
  ];
@@ -4030,7 +4306,7 @@ async function generateProject(input) {
4030
4306
  await writeFile8(join12(input.targetDirectory, "docker-compose.yml"), starterComposeFor(selectedModules.includes("system.agent"), selectedModules.includes("system.search")));
4031
4307
  await writeFile8(join12(input.targetDirectory, ".env.example"), environmentExample(includeExample, selectedModules.includes("system.agent"), selectedModules.includes("system.search")));
4032
4308
  await writeFile8(join12(input.targetDirectory, "README.md"), starterReadme(input.projectName, includeExample, includeOxc, projectLicense, selectedModules));
4033
- await writeFile8(join12(input.targetDirectory, "docs/README.md"), starterDocsReadme(selectedModules.includes("system.license"), selectedModules.includes("system.agent")));
4309
+ await writeFile8(join12(input.targetDirectory, "docs/README.md"), starterDocsReadme(selectedModules.includes("system.license"), selectedModules.includes("system.agent"), selectedModules.includes("system.operations")));
4034
4310
  await reflectCodeQualityChoice(input.targetDirectory, includeOxc);
4035
4311
  await applyWindyBaseLicense(input.targetDirectory);
4036
4312
  await applyProjectLicense(input.targetDirectory, projectLicense, input.licenseHolder);
@@ -5957,8 +6233,8 @@ function sentenceWithoutStop(value) {
5957
6233
  }
5958
6234
 
5959
6235
  // src/update/cli.ts
5960
- import { rm as rm10 } from "fs/promises";
5961
- import { resolve as resolve7 } from "path";
6236
+ import { rm as rm11 } from "fs/promises";
6237
+ import { resolve as resolve8 } from "path";
5962
6238
 
5963
6239
  // src/reconcile/managed-reconciler.ts
5964
6240
  import { rm as rm6 } from "fs/promises";
@@ -8366,8 +8642,51 @@ function isObject(value) {
8366
8642
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8367
8643
  }
8368
8644
 
8645
+ // src/update/planner-project-owned.ts
8646
+ function planGeneratedProjectOwned(path2, source, target, ours, theirs, expected) {
8647
+ if (!source && target) {
8648
+ return !ours && theirs ? ready(path2, "create", "\u6A21\u5757\u5B89\u88C5\u65B0\u589E\u9879\u76EE\u6269\u5C55\u70B9", expected, theirs) : conflict(path2, "\u6A21\u5757\u65B0\u589E\u8DEF\u5F84\u5DF2\u88AB\u9879\u76EE\u5360\u7528", expected);
8649
+ }
8650
+ if (source && !target) {
8651
+ if (!ours) {
8652
+ return ready(path2, "preserve", "\u6A21\u5757\u8DEF\u5F84\u5DF2\u79FB\u9664", expected);
8653
+ }
8654
+ return hashContent(ours) === source.hash ? ready(path2, "delete", "\u6A21\u5757\u5378\u8F7D\u79FB\u9664\u672A\u4FEE\u6539\u6269\u5C55\u70B9", expected) : conflict(path2, "\u5BA2\u6237\u4FEE\u6539\u8FC7\u6A21\u5757\u6269\u5C55\u70B9\uFF0C\u62D2\u7EDD\u5378\u8F7D", expected);
8655
+ }
8656
+ if (!source || !target || !ours || !theirs) {
8657
+ return ready(path2, "preserve", "\u9879\u76EE\u62E5\u6709\uFF0C\u6A21\u5757\u7EC4\u5408\u672A\u6539\u53D8", expected);
8658
+ }
8659
+ if (hashContent(ours) === target.hash) {
8660
+ return ready(path2, "preserve", "\u5DF2\u5305\u542B\u76EE\u6807\u6A21\u5757\u7EC4\u5408", expected);
8661
+ }
8662
+ if (source.hash === target.hash) {
8663
+ return ready(path2, "preserve", "\u6A21\u5757\u7EC4\u5408\u672A\u6539\u53D8", expected);
8664
+ }
8665
+ return hashContent(ours) === source.hash ? ready(path2, "update", "\u66F4\u65B0\u672A\u4FEE\u6539\u7684\u6A21\u5757\u7EC4\u5408\u6269\u5C55\u70B9", expected, theirs) : conflict(path2, "\u5BA2\u6237\u4FEE\u6539\u8FC7\u6A21\u5757\u7EC4\u5408\u6269\u5C55\u70B9", expected);
8666
+ }
8667
+ function ready(path2, action, reason, expectedProjectContent, content) {
8668
+ return {
8669
+ path: path2,
8670
+ ownership: "project-owned",
8671
+ action,
8672
+ reason,
8673
+ expectedProjectContent,
8674
+ content
8675
+ };
8676
+ }
8677
+ function conflict(path2, reason, expectedProjectContent) {
8678
+ return {
8679
+ path: path2,
8680
+ ownership: "project-owned",
8681
+ action: "preserve",
8682
+ reason: "\u5BA2\u6237\u5DF2\u4FEE\u6539",
8683
+ expectedProjectContent,
8684
+ conflict: reason
8685
+ };
8686
+ }
8687
+
8369
8688
  // src/update/planner-file.ts
8370
- async function planFile(path2, baseRoot, targetRoot, source, target, ours, provenance, targetContentOverride) {
8689
+ async function planFile(path2, baseRoot, targetRoot, source, target, ours, provenance, targetContentOverride, manageGeneratedProjectOwned = false) {
8371
8690
  const [base, theirs] = await Promise.all([
8372
8691
  readOptional(join19(baseRoot, path2)),
8373
8692
  targetContentOverride ? Promise.resolve(targetContentOverride) : readOptional(join19(targetRoot, path2))
@@ -8375,6 +8694,9 @@ async function planFile(path2, baseRoot, targetRoot, source, target, ours, prove
8375
8694
  const ownership = source?.ownership === "project-owned" ? source.ownership : target?.ownership || source?.ownership || classifyOwnership(path2);
8376
8695
  const expected = expectationForSnapshot(ours);
8377
8696
  if (ownership === "project-owned") {
8697
+ if (manageGeneratedProjectOwned) {
8698
+ return planGeneratedProjectOwned(path2, source, target, ours, theirs, expected);
8699
+ }
8378
8700
  return !source && !ours && theirs ? readyPlannedFile(path2, ownership, "create", "\u65B0\u589E\u9879\u76EE\u6269\u5C55\u70B9", expected, theirs) : readyPlannedFile(path2, ownership, "preserve", "\u9879\u76EE\u62E5\u6709\uFF0CWindy \u4E0D\u8986\u76D6", expected);
8379
8701
  }
8380
8702
  const repaired = ours && theirs ? repairKnownDerivedFile({
@@ -8389,26 +8711,26 @@ async function planFile(path2, baseRoot, targetRoot, source, target, ours, prove
8389
8711
  }
8390
8712
  if (path2.startsWith("packages/database/drizzle/") && path2.endsWith(".sql")) {
8391
8713
  if (source && !target) {
8392
- return conflict(path2, ownership, "\u5DF2\u53D1\u5E03 migration \u4E0D\u53EF\u5220\u9664", expected);
8714
+ return conflict2(path2, ownership, "\u5DF2\u53D1\u5E03 migration \u4E0D\u53EF\u5220\u9664", expected);
8393
8715
  }
8394
8716
  if (source && target && source.hash !== target.hash) {
8395
- return conflict(path2, ownership, "\u5DF2\u53D1\u5E03 migration \u4E0D\u53EF\u6539\u5199\uFF0C\u53EA\u80FD\u8FFD\u52A0\u65B0\u6587\u4EF6", expected);
8717
+ return conflict2(path2, ownership, "\u5DF2\u53D1\u5E03 migration \u4E0D\u53EF\u6539\u5199\uFF0C\u53EA\u80FD\u8FFD\u52A0\u65B0\u6587\u4EF6", expected);
8396
8718
  }
8397
8719
  }
8398
8720
  if (!source && target) {
8399
8721
  if (!ours) {
8400
8722
  return readyPlannedFile(path2, ownership, "create", "\u76EE\u6807\u7248\u672C\u65B0\u589E", expected, theirs);
8401
8723
  }
8402
- return theirs && hashContent(ours) === target.hash ? readyPlannedFile(path2, ownership, "preserve", "\u5DF2\u5305\u542B\u76EE\u6807\u5185\u5BB9", expected) : conflict(path2, ownership, "\u76EE\u6807\u7248\u672C\u65B0\u589E\u8DEF\u5F84\u5DF2\u88AB\u9879\u76EE\u5360\u7528", expected);
8724
+ return theirs && hashContent(ours) === target.hash ? readyPlannedFile(path2, ownership, "preserve", "\u5DF2\u5305\u542B\u76EE\u6807\u5185\u5BB9", expected) : conflict2(path2, ownership, "\u76EE\u6807\u7248\u672C\u65B0\u589E\u8DEF\u5F84\u5DF2\u88AB\u9879\u76EE\u5360\u7528", expected);
8403
8725
  }
8404
8726
  if (source && !target) {
8405
8727
  if (!ours) {
8406
8728
  return readyPlannedFile(path2, ownership, "preserve", "\u9879\u76EE\u5DF2\u5220\u9664", expected);
8407
8729
  }
8408
- return hashContent(ours) === source.hash ? readyPlannedFile(path2, ownership, "delete", "\u76EE\u6807\u7248\u672C\u5220\u9664", expected) : conflict(path2, ownership, "\u5BA2\u6237\u4FEE\u6539\u540E Windy \u53C8\u5220\u9664", expected);
8730
+ return hashContent(ours) === source.hash ? readyPlannedFile(path2, ownership, "delete", "\u76EE\u6807\u7248\u672C\u5220\u9664", expected) : conflict2(path2, ownership, "\u5BA2\u6237\u4FEE\u6539\u540E Windy \u53C8\u5220\u9664", expected);
8409
8731
  }
8410
8732
  if (!source || !target || !ours || !theirs) {
8411
- return conflict(path2, ownership, "\u6587\u4EF6\u72B6\u6001\u4E0D\u5B8C\u6574\uFF0C\u4E0D\u80FD\u5B89\u5168\u63A8\u65AD", expected);
8733
+ return conflict2(path2, ownership, "\u6587\u4EF6\u72B6\u6001\u4E0D\u5B8C\u6574\uFF0C\u4E0D\u80FD\u5B89\u5168\u63A8\u65AD", expected);
8412
8734
  }
8413
8735
  const oursChanged = hashContent(ours) !== source.hash;
8414
8736
  const sourceArtifactHash = artifactHashOf(source);
@@ -8428,13 +8750,13 @@ async function planFile(path2, baseRoot, targetRoot, source, target, ours, prove
8428
8750
  }
8429
8751
  if (path2.endsWith("package.json") && base) {
8430
8752
  const merged = mergePackageJson(base, ours, theirs);
8431
- return merged.content ? readyPlannedFile(path2, ownership, "update", "package.json \u5B57\u6BB5\u7EA7\u5408\u5E76", expected, merged.content) : conflict(path2, ownership, `\u5B57\u6BB5\u51B2\u7A81\uFF1A${merged.conflicts.join("\u3001")}`, expected);
8753
+ return merged.content ? readyPlannedFile(path2, ownership, "update", "package.json \u5B57\u6BB5\u7EA7\u5408\u5E76", expected, merged.content) : conflict2(path2, ownership, `\u5B57\u6BB5\u51B2\u7A81\uFF1A${merged.conflicts.join("\u3001")}`, expected);
8432
8754
  }
8433
8755
  if (ownership === "shared-scaffold" && base) {
8434
8756
  const merged = await mergeTextFile(base, ours, theirs);
8435
- return merged.content ? readyPlannedFile(path2, ownership, "update", "\u4E09\u65B9\u5408\u5E76", expected, merged.content) : conflict(path2, ownership, merged.conflict || "\u4E09\u65B9\u5408\u5E76\u5931\u8D25", expected);
8757
+ return merged.content ? readyPlannedFile(path2, ownership, "update", "\u4E09\u65B9\u5408\u5E76", expected, merged.content) : conflict2(path2, ownership, merged.conflict || "\u4E09\u65B9\u5408\u5E76\u5931\u8D25", expected);
8436
8758
  }
8437
- return conflict(path2, ownership, "\u5BA2\u6237\u548C Windy \u5747\u5DF2\u4FEE\u6539", expected);
8759
+ return conflict2(path2, ownership, "\u5BA2\u6237\u548C Windy \u5747\u5DF2\u4FEE\u6539", expected);
8438
8760
  }
8439
8761
  function targetChangedSinceBase(sourceHash, base, target) {
8440
8762
  return base && target ? hashContent(base) !== hashContent(target) : !!target && hashContent(target) !== sourceHash;
@@ -8452,7 +8774,7 @@ function readyPlannedFile(path2, ownership, action, reason, expectedProjectConte
8452
8774
  function expectationForSnapshot(content) {
8453
8775
  return content ? { kind: "hash", value: hashContent(content) } : { kind: "missing" };
8454
8776
  }
8455
- function conflict(path2, ownership, reason, expectedProjectContent) {
8777
+ function conflict2(path2, ownership, reason, expectedProjectContent) {
8456
8778
  return {
8457
8779
  path: path2,
8458
8780
  ownership,
@@ -8633,7 +8955,10 @@ var recipes = [
8633
8955
  { id: "starter-0.2.33-to-0.2.34", from: "0.2.33", to: "0.2.34" },
8634
8956
  { id: "starter-0.2.34-to-0.3.0", from: "0.2.34", to: "0.3.0" },
8635
8957
  { id: "starter-0.3.0-to-0.3.1", from: "0.3.0", to: "0.3.1" },
8636
- { id: "starter-0.3.1-to-0.3.2", from: "0.3.1", to: "0.3.2" }
8958
+ { id: "starter-0.3.1-to-0.3.2", from: "0.3.1", to: "0.3.2" },
8959
+ { id: "starter-0.3.2-to-0.3.3", from: "0.3.2", to: "0.3.3" },
8960
+ { id: "starter-0.3.3-to-0.3.4", from: "0.3.3", to: "0.3.4" },
8961
+ { id: "starter-0.3.4-to-0.3.5", from: "0.3.4", to: "0.3.5" }
8637
8962
  ];
8638
8963
  function resolveRecipeChain(sourceVersion, targetVersion) {
8639
8964
  if (sourceVersion === targetVersion)
@@ -8674,13 +8999,15 @@ async function createUpdatePlan(request, artifacts) {
8674
8999
  const expectedManifestHash = hashContent(await readFile17(manifestPath));
8675
9000
  const generationSnapshot = await readGenerationProvenanceSnapshot(request.projectDirectory);
8676
9001
  const provenance = generationSnapshot.provenance;
8677
- const artifactSet = await artifacts.prepare(request.projectDirectory, provenance);
9002
+ const artifactSet = await artifacts.prepare(request.projectDirectory, provenance, request.targetModules);
8678
9003
  const targetProvenance = await readGenerationProvenance(artifactSet.targetDirectory);
8679
9004
  const targetVersion = request.targetVersion || targetProvenance.generatorVersion;
8680
9005
  if (targetVersion !== targetProvenance.generatorVersion) {
8681
9006
  throw new Error(`\u5F53\u524D CLI \u53EA\u80FD\u66F4\u65B0\u5230 ${targetProvenance.generatorVersion}\uFF0C\u8BF7\u8FD0\u884C create-windy@${targetVersion}`);
8682
9007
  }
8683
- const recipes2 = resolveRecipeChain(provenance.windyVersion, targetVersion);
9008
+ const versionRecipes = resolveRecipeChain(provenance.windyVersion, targetVersion);
9009
+ const moduleRecipes = resolveModuleRecipeIds(request);
9010
+ const recipes2 = [...versionRecipes.map(({ id }) => id), ...moduleRecipes];
8684
9011
  const [rawSourceManifest, rawTargetManifest] = await Promise.all([
8685
9012
  readManagedFilesManifest(request.projectDirectory),
8686
9013
  readManagedFilesManifest(artifactSet.targetDirectory)
@@ -8693,6 +9020,7 @@ async function createUpdatePlan(request, artifacts) {
8693
9020
  ...generatedTargetManifest.files.map(({ path: path2 }) => path2)
8694
9021
  ]);
8695
9022
  const workspaceSnapshot = await snapshotWorkspacePackages(request.projectDirectory, { allowMissingRoot: true });
9023
+ const targetWorkspaceSnapshot = await snapshotWorkspacePackages(artifactSet.targetDirectory, { allowMissingRoot: true });
8696
9024
  const projectSnapshots = await snapshotProjectFiles(request.projectDirectory, initialPaths, workspaceSnapshot.manifests);
8697
9025
  projectSnapshots.set(generationMetadataFile, generationSnapshot.content);
8698
9026
  const projectModes = await snapshotProjectModes(request.projectDirectory, initialPaths);
@@ -8706,8 +9034,7 @@ async function createUpdatePlan(request, artifacts) {
8706
9034
  });
8707
9035
  const targetManifest = await carryReconciledBaselines(sourceManifest, migrations.targetManifest, projectSnapshots, artifactSet.targetDirectory);
8708
9036
  targetProvenance.appliedRecipes = [
8709
- ...provenance.appliedRecipes,
8710
- ...recipes2.map(({ id }) => id)
9037
+ ...new Set([...provenance.appliedRecipes, ...recipes2])
8711
9038
  ];
8712
9039
  const sourceEntries = new Map(sourceManifest.files.map((item) => [item.path, item]));
8713
9040
  const targetEntries = new Map(targetManifest.files.map((item) => [item.path, item]));
@@ -8725,7 +9052,8 @@ async function createUpdatePlan(request, artifacts) {
8725
9052
  targetEntries,
8726
9053
  migrations,
8727
9054
  artifactSet,
8728
- provenance
9055
+ provenance,
9056
+ manageGeneratedProjectOwned: request.targetModules !== undefined
8729
9057
  });
8730
9058
  deriveWebDockerfilePlan(files, projectSnapshots, new Set([...paths, ...workspaceSnapshot.manifests.keys()]));
8731
9059
  for (const file of files) {
@@ -8772,18 +9100,33 @@ async function createUpdatePlan(request, artifacts) {
8772
9100
  projectDirectory: request.projectDirectory,
8773
9101
  sourceVersion: provenance.windyVersion,
8774
9102
  targetVersion,
8775
- recipes: recipes2.map(({ id }) => id),
9103
+ recipes: recipes2,
8776
9104
  files,
8777
9105
  dependencyExpectations,
8778
9106
  workspaceMembershipExpectations: workspaceSnapshot.membershipExpectations,
9107
+ finalWorkspaceMembershipExpectations: targetWorkspaceSnapshot.membershipExpectations,
8779
9108
  conflicts,
8780
9109
  detachedToolchain,
8781
9110
  targetProvenance,
8782
9111
  sourceManifest,
8783
9112
  targetManifest,
8784
- temporaryRoot: artifactSet.temporaryRoot
9113
+ temporaryRoot: artifactSet.temporaryRoot,
9114
+ lifecycle: request.lifecycle
8785
9115
  };
8786
9116
  }
9117
+ function resolveModuleRecipeIds(request) {
9118
+ const lifecycle = request.lifecycle;
9119
+ if (!lifecycle)
9120
+ return [];
9121
+ const catalog = new Map(moduleDistributionCatalog.map((manifest) => [manifest.name, manifest]));
9122
+ if (lifecycle.action === "add") {
9123
+ return lifecycle.addedModules.map((name) => catalog.get(name).installRecipe);
9124
+ }
9125
+ if (lifecycle.action === "remove") {
9126
+ return lifecycle.removedModules.map((name) => catalog.get(name).uninstallRecipe);
9127
+ }
9128
+ return lifecycle.updatedModules.flatMap((name) => catalog.get(name).updateRecipes);
9129
+ }
8787
9130
  async function planFiles(input) {
8788
9131
  const files = [];
8789
9132
  for (const path2 of [...input.paths].sort()) {
@@ -8797,7 +9140,7 @@ async function planFiles(input) {
8797
9140
  } else if (forcedContent) {
8798
9141
  files.push(readyPlannedFile(path2, input.targetEntries.get(path2)?.ownership ?? input.sourceEntries.get(path2)?.ownership ?? classifyOwnership(path2), "update", "\u5408\u5E76\u5E73\u53F0\u4E0E\u4E0B\u6E38 migration \u5386\u53F2", expected, forcedContent));
8799
9142
  } else {
8800
- files.push(await planFile(path2, input.artifactSet.baseDirectory, input.artifactSet.targetDirectory, input.sourceEntries.get(path2), input.targetEntries.get(path2), ours, input.provenance, input.migrations.contentOverrides.get(path2)));
9143
+ files.push(await planFile(path2, input.artifactSet.baseDirectory, input.artifactSet.targetDirectory, input.sourceEntries.get(path2), input.targetEntries.get(path2), ours, input.provenance, input.migrations.contentOverrides.get(path2), input.manageGeneratedProjectOwned));
8801
9144
  }
8802
9145
  }
8803
9146
  return files;
@@ -9514,23 +9857,113 @@ async function diagnoseProject(projectDirectory, repair = false) {
9514
9857
  }
9515
9858
 
9516
9859
  // src/update/updater.ts
9517
- import { readFile as readFile23, rm as rm9 } from "fs/promises";
9518
- import { join as join30 } from "path";
9860
+ import { readFile as readFile24, rm as rm9 } from "fs/promises";
9861
+ import { join as join31 } from "path";
9519
9862
 
9520
9863
  // src/update/artifacts.ts
9521
- import { mkdtemp as mkdtemp3, readFile as readFile21 } from "fs/promises";
9864
+ import { mkdtemp as mkdtemp3, readFile as readFile22 } from "fs/promises";
9522
9865
  import { tmpdir as tmpdir3 } from "os";
9523
- import { basename as basename8, join as join28 } from "path";
9866
+ import { basename as basename8, join as join29 } from "path";
9867
+
9868
+ // src/module-history.ts
9869
+ import {
9870
+ lstat as lstat5,
9871
+ mkdir as mkdir7,
9872
+ readdir as readdir8,
9873
+ readFile as readFile21,
9874
+ writeFile as writeFile11
9875
+ } from "fs/promises";
9876
+ import { dirname as dirname7, join as join28, relative as relative4, sep as sep4 } from "path";
9877
+ async function archiveRemovedModuleMigrations(input) {
9878
+ await assertExistingHistoryIntegrity(input.projectDirectory);
9879
+ let changed = await copyMigrationTree(input.projectDirectory, ".windy/module-history", join28(input.targetDirectory, ".windy/module-history"));
9880
+ const target = new Set(input.targetModules);
9881
+ const removed = moduleDistributionCatalog.filter(({ name }) => input.sourceModules.includes(name) && !target.has(name));
9882
+ for (const manifest of removed) {
9883
+ for (const root of manifest.migrationRoots) {
9884
+ changed = await copyMigrationTree(input.projectDirectory, root, join28(input.targetDirectory, ".windy/module-history", manifest.name, root)) || changed;
9885
+ }
9886
+ }
9887
+ if (changed) {
9888
+ await writeManagedFilesManifest(input.targetDirectory, input.generatorVersion);
9889
+ }
9890
+ }
9891
+ async function assertExistingHistoryIntegrity(projectDirectory) {
9892
+ const historyRoot = join28(projectDirectory, ".windy/module-history");
9893
+ const metadata = await readOptionalMetadata(historyRoot);
9894
+ if (!metadata)
9895
+ return;
9896
+ if (metadata.isSymbolicLink() || !metadata.isDirectory()) {
9897
+ throw new Error("\u6A21\u5757 migration \u5386\u53F2\u76EE\u5F55\u65E0\u6548");
9898
+ }
9899
+ const manifest = await readManagedFilesManifest(projectDirectory);
9900
+ const expected = new Map(manifest.files.map((entry) => [entry.path, entry]));
9901
+ for (const path2 of await collectRegularFiles(historyRoot)) {
9902
+ const relativePath2 = relative4(projectDirectory, path2).split(sep4).join("/");
9903
+ const entry = expected.get(relativePath2);
9904
+ if (!entry || isMissingManagedFile(entry)) {
9905
+ throw new Error(`\u6A21\u5757 migration \u5386\u53F2\u672A\u767B\u8BB0\u5230\u53D7\u7BA1\u6E05\u5355\uFF1A${relativePath2}`);
9906
+ }
9907
+ if (hashContent(await readFile21(path2)) !== entry.hash) {
9908
+ throw new Error(`\u6A21\u5757 migration \u5386\u53F2\u5DF2\u88AB\u4FEE\u6539\uFF1A${relativePath2}`);
9909
+ }
9910
+ }
9911
+ }
9912
+ async function copyMigrationTree(projectRoot, relativeRoot, targetRoot) {
9913
+ const sourceRoot = join28(projectRoot, relativeRoot);
9914
+ const rootMetadata = await readOptionalMetadata(sourceRoot);
9915
+ if (!rootMetadata)
9916
+ return false;
9917
+ if (rootMetadata.isSymbolicLink() || !rootMetadata.isDirectory()) {
9918
+ throw new Error(`\u6A21\u5757 migration \u6839\u76EE\u5F55\u65E0\u6548\uFF1A${relativeRoot}`);
9919
+ }
9920
+ for (const source of await collectRegularFiles(sourceRoot)) {
9921
+ const target = join28(targetRoot, relative4(sourceRoot, source).split(sep4).join("/"));
9922
+ await mkdir7(dirname7(target), { recursive: true });
9923
+ await writeFile11(target, await readFile21(source));
9924
+ }
9925
+ return true;
9926
+ }
9927
+ async function collectRegularFiles(root) {
9928
+ const files = [];
9929
+ await visit(root);
9930
+ return files.sort();
9931
+ async function visit(directory) {
9932
+ for (const name of (await readdir8(directory)).sort()) {
9933
+ const path2 = join28(directory, name);
9934
+ const metadata = await lstat5(path2);
9935
+ if (metadata.isSymbolicLink()) {
9936
+ throw new Error(`\u6A21\u5757 migration \u4E0D\u5141\u8BB8\u7B26\u53F7\u94FE\u63A5\uFF1A${path2}`);
9937
+ }
9938
+ if (metadata.isDirectory())
9939
+ await visit(path2);
9940
+ else if (metadata.isFile())
9941
+ files.push(path2);
9942
+ }
9943
+ }
9944
+ }
9945
+ async function readOptionalMetadata(path2) {
9946
+ try {
9947
+ return await lstat5(path2);
9948
+ } catch (error) {
9949
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
9950
+ return;
9951
+ }
9952
+ throw error;
9953
+ }
9954
+ }
9955
+
9956
+ // src/update/artifacts.ts
9524
9957
  class PublishedArtifactProvider {
9525
9958
  #fetch;
9526
9959
  constructor(fetcher = globalThis.fetch) {
9527
9960
  this.#fetch = fetcher;
9528
9961
  }
9529
- async prepare(projectDirectory, provenance) {
9530
- const temporaryRoot = await mkdtemp3(join28(tmpdir3(), "windy-update-"));
9962
+ async prepare(projectDirectory, provenance, targetModules) {
9963
+ const temporaryRoot = await mkdtemp3(join29(tmpdir3(), "windy-update-"));
9531
9964
  const projectName = await readProjectName3(projectDirectory);
9532
- const baseDirectory = join28(temporaryRoot, "base", projectName);
9533
- const targetDirectory = join28(temporaryRoot, "target", projectName);
9965
+ const baseDirectory = join29(temporaryRoot, "base", projectName);
9966
+ const targetDirectory = join29(temporaryRoot, "target", projectName);
9534
9967
  const baseArtifact = await downloadPublicArtifact(temporaryRoot, "base", provenance.generatorVersion, undefined, this.#fetch);
9535
9968
  if (baseArtifact.identity.templateCommit !== provenance.templateCommit) {
9536
9969
  throw new Error("Project Lock \u4E0E\u6765\u6E90 registry artifact \u7684 template commit \u4E0D\u4E00\u81F4");
@@ -9540,18 +9973,25 @@ class PublishedArtifactProvider {
9540
9973
  sourceRoot: await resolveTemplateRoot(),
9541
9974
  targetDirectory,
9542
9975
  projectName,
9543
- selectedModules: provenance.modules.map(({ name }) => name),
9544
- includeExample: provenance.choices.includeExample,
9976
+ selectedModules: targetModules ?? provenance.modules.map(({ name }) => name),
9977
+ includeExample: (targetModules ?? provenance.modules.map(({ name }) => name)).includes("work-order"),
9545
9978
  includeOxc: provenance.choices.includeOxc,
9546
9979
  projectLicense: provenance.legalLicense,
9547
9980
  licenseHolder: provenance.legalLicense === "MIT" ? "Windy project owner" : undefined,
9548
9981
  profile: provenance.profile
9549
9982
  });
9983
+ await archiveRemovedModuleMigrations({
9984
+ projectDirectory,
9985
+ targetDirectory,
9986
+ sourceModules: provenance.modules.map(({ name }) => name),
9987
+ targetModules: targetModules ?? provenance.modules.map(({ name }) => name),
9988
+ generatorVersion: (await readGenerationProvenance(targetDirectory)).generatorVersion
9989
+ });
9550
9990
  return { temporaryRoot, baseDirectory, targetDirectory };
9551
9991
  }
9552
9992
  }
9553
9993
  async function readProjectName3(projectDirectory) {
9554
- const source = JSON.parse(await readFile21(join28(projectDirectory, "package.json"), "utf8"));
9994
+ const source = JSON.parse(await readFile22(join29(projectDirectory, "package.json"), "utf8"));
9555
9995
  return source.name || basename8(projectDirectory);
9556
9996
  }
9557
9997
 
@@ -9618,9 +10058,9 @@ function frozenManifestHash(file, finalizedHashes) {
9618
10058
  }
9619
10059
 
9620
10060
  // src/update/format-stabilizer.ts
9621
- import { mkdir as mkdir7, mkdtemp as mkdtemp4, readFile as readFile22, rm as rm8, writeFile as writeFile11 } from "fs/promises";
10061
+ import { mkdir as mkdir8, mkdtemp as mkdtemp4, readFile as readFile23, rm as rm8, writeFile as writeFile12 } from "fs/promises";
9622
10062
  import { tmpdir as tmpdir4 } from "os";
9623
- import { dirname as dirname7, extname as extname4, join as join29, resolve as resolve6, sep as sep4 } from "path";
10063
+ import { dirname as dirname8, extname as extname4, join as join30, resolve as resolve6, sep as sep5 } from "path";
9624
10064
  var formattedExtensions = new Set([
9625
10065
  ".cjs",
9626
10066
  ".css",
@@ -9659,7 +10099,7 @@ async function stabilizeManagedContent(result2, execute, writeProjectFile) {
9659
10099
  }
9660
10100
  }
9661
10101
  inputs.set(".windy/generation.json", new TextEncoder().encode(generationContent));
9662
- const temporaryRoot = await mkdtemp4(join29(tmpdir4(), "windy-update-format-"));
10102
+ const temporaryRoot = await mkdtemp4(join30(tmpdir4(), "windy-update-format-"));
9663
10103
  try {
9664
10104
  const stagedPaths = await stageInputs(temporaryRoot, inputs);
9665
10105
  for (let offset = 0;offset < stagedPaths.length; offset += 80) {
@@ -9667,14 +10107,14 @@ async function stabilizeManagedContent(result2, execute, writeProjectFile) {
9667
10107
  "x",
9668
10108
  "oxfmt",
9669
10109
  "--write",
9670
- `--config=${join29(result2.plan.projectDirectory, ".oxfmtrc.json")}`,
10110
+ `--config=${join30(result2.plan.projectDirectory, ".oxfmtrc.json")}`,
9671
10111
  ...stagedPaths.slice(offset, offset + 80)
9672
10112
  ], result2.plan.projectDirectory);
9673
10113
  }
9674
10114
  const hashes = plannedContentHashes(result2);
9675
10115
  let formattedGeneration = generationContent;
9676
10116
  for (const [path2, original] of inputs) {
9677
- const formatted = await readFile22(safeTemporaryPath(temporaryRoot, path2));
10117
+ const formatted = await readFile23(safeTemporaryPath(temporaryRoot, path2));
9678
10118
  if (path2 === ".windy/generation.json") {
9679
10119
  formattedGeneration = formatted.toString();
9680
10120
  continue;
@@ -9700,15 +10140,15 @@ async function stageInputs(temporaryRoot, inputs) {
9700
10140
  const paths = [];
9701
10141
  for (const [path2, content] of inputs) {
9702
10142
  const target = safeTemporaryPath(temporaryRoot, path2);
9703
- await mkdir7(dirname7(target), { recursive: true });
9704
- await writeFile11(target, content);
10143
+ await mkdir8(dirname8(target), { recursive: true });
10144
+ await writeFile12(target, content);
9705
10145
  paths.push(target);
9706
10146
  }
9707
10147
  return paths;
9708
10148
  }
9709
10149
  function safeTemporaryPath(root, path2) {
9710
10150
  const target = resolve6(root, path2);
9711
- if (!target.startsWith(`${resolve6(root)}${sep4}`)) {
10151
+ if (!target.startsWith(`${resolve6(root)}${sep5}`)) {
9712
10152
  throw new Error(`\u683C\u5F0F\u5316\u8DEF\u5F84\u8D8A\u51FA\u4E34\u65F6\u76EE\u5F55\uFF1A${path2}`);
9713
10153
  }
9714
10154
  return target;
@@ -9771,7 +10211,7 @@ class DefaultStarterUpdater {
9771
10211
  const plannedPaths = new Set(result2.plan.files.map(({ path: path2 }) => path2));
9772
10212
  replaceExpectations(result2, verificationSnapshot.filter(({ path: path2 }) => !metadataPaths2.has(path2) && !plannedPaths.has(path2)));
9773
10213
  verificationExpectationsFrozen = true;
9774
- const packageJson = JSON.parse(await readFile23(join30(result2.plan.projectDirectory, "package.json"), "utf8"));
10214
+ const packageJson = JSON.parse(await readFile24(join31(result2.plan.projectDirectory, "package.json"), "utf8"));
9775
10215
  for (const name of ["lint", "typecheck", "test", "build"]) {
9776
10216
  if (!packageJson.scripts?.[name]) {
9777
10217
  steps.push({ name: `bun run ${name}`, status: "skipped" });
@@ -9790,7 +10230,7 @@ class DefaultStarterUpdater {
9790
10230
  projectDirectory: result2.plan.projectDirectory,
9791
10231
  ownerToken: result2.transactionOwnerToken,
9792
10232
  expectations: result2.finalizationExpectations,
9793
- workspaceMembershipExpectations: result2.plan.workspaceMembershipExpectations
10233
+ workspaceMembershipExpectations: result2.plan.finalWorkspaceMembershipExpectations ?? result2.plan.workspaceMembershipExpectations
9794
10234
  });
9795
10235
  return { updateId: result2.plan.id, status: "passed", steps, reportPath };
9796
10236
  } catch (error) {
@@ -9854,6 +10294,12 @@ async function writeReport(result2, steps, relativePath2) {
9854
10294
  `- \u6765\u6E90\u7248\u672C\uFF1A${result2.plan.sourceVersion}`,
9855
10295
  `- \u76EE\u6807\u7248\u672C\uFF1A${result2.plan.targetVersion}`,
9856
10296
  `- Recipe\uFF1A${result2.plan.recipes.join("\u3001") || "\u65E0"}`,
10297
+ ...result2.plan.lifecycle ? [
10298
+ `- \u6A21\u5757\u52A8\u4F5C\uFF1A${result2.plan.lifecycle.action}`,
10299
+ `- \u8BF7\u6C42\u6A21\u5757\uFF1A${result2.plan.lifecycle.requestedModules.join("\u3001")}`,
10300
+ `- \u6570\u636E\u5904\u7F6E\uFF1A${result2.plan.lifecycle.dataDisposition ?? "\u4E0D\u9002\u7528"}`,
10301
+ `- \u6570\u636E\u5904\u7F6E\u6587\u6863\uFF1A${result2.plan.lifecycle.dataPlan ?? "\u65E0"}`
10302
+ ] : [],
9857
10303
  `- \u4FEE\u6539\u6587\u4EF6\uFF1A${result2.changedFiles.length}`,
9858
10304
  `- Oxc \u4EE3\u7BA1\uFF1A${result2.plan.detachedToolchain ? "\u5DF2\u68C0\u6D4B\u5230\u5BA2\u6237\u63A5\u7BA1\u5E76\u4FDD\u7559" : "\u4FDD\u6301"}`,
9859
10305
  "",
@@ -9877,13 +10323,275 @@ function expectedForPath(result2, path2) {
9877
10323
  return expectation2.expected;
9878
10324
  }
9879
10325
 
10326
+ // src/module-lifecycle.ts
10327
+ import { access as access2, rm as rm10 } from "fs/promises";
10328
+ import { resolve as resolve7 } from "path";
10329
+
10330
+ // src/module-version-range.ts
10331
+ function isVersionInCaretRange(version, range) {
10332
+ const current = parseVersion2(version);
10333
+ const base = range.startsWith("^") ? parseVersion2(range.slice(1)) : undefined;
10334
+ if (!current || !base || compareVersion(current, base) < 0)
10335
+ return false;
10336
+ const upper = base[0] > 0 ? [base[0] + 1, 0, 0] : base[1] > 0 ? [0, base[1] + 1, 0] : [0, 0, base[2] + 1];
10337
+ return compareVersion(current, upper) < 0;
10338
+ }
10339
+ function parseVersion2(value) {
10340
+ const match = value.match(/^(\d+)\.(\d+)\.(\d+)$/);
10341
+ if (!match)
10342
+ return;
10343
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
10344
+ }
10345
+ function compareVersion(left, right) {
10346
+ for (let index = 0;index < 3; index += 1) {
10347
+ const difference = left[index] - right[index];
10348
+ if (difference)
10349
+ return difference;
10350
+ }
10351
+ return 0;
10352
+ }
10353
+
10354
+ // src/module-lifecycle.ts
10355
+ async function runModuleLifecycleCommand(args, projectDirectory, dependencies = {}) {
10356
+ const action = args[0];
10357
+ if (!isAction(action)) {
10358
+ throw new Error("module \u53EA\u652F\u6301 list\u3001add\u3001update\u3001remove");
10359
+ }
10360
+ const log = dependencies.log ?? console.log;
10361
+ const provenance = await readGenerationProvenance(projectDirectory);
10362
+ const installed = provenance.modules.map(({ name }) => name);
10363
+ if (action === "list") {
10364
+ printModuleList(provenance.modules, log);
10365
+ return;
10366
+ }
10367
+ const cliVersion = dependencies.cliVersion ?? (await readTemplateMetadata(await resolveTemplateRoot())).generatorVersion;
10368
+ if (provenance.generatorVersion !== cliVersion || provenance.windyVersion !== cliVersion) {
10369
+ throw new Error(`module \u547D\u4EE4\u8981\u6C42 CLI \u4E0E\u9879\u76EE Windy \u7248\u672C\u4E00\u81F4\uFF1A\u9879\u76EE ${provenance.windyVersion}\uFF0CCLI ${cliVersion}\uFF1B\u8BF7\u5148\u8FD0\u884C bun run windy update`);
10370
+ }
10371
+ const requested = positionalModules(args.slice(1));
10372
+ const lifecycle = await resolveLifecyclePlan({
10373
+ action,
10374
+ requested,
10375
+ installed,
10376
+ windyVersion: provenance.windyVersion,
10377
+ args,
10378
+ projectDirectory
10379
+ });
10380
+ printDataPlan(lifecycle, log);
10381
+ if (!hasChanges(lifecycle)) {
10382
+ log("\u6A21\u5757\u96C6\u5408\u5DF2\u6EE1\u8DB3\u8BF7\u6C42\uFF0C\u65E0\u9700\u4FEE\u6539");
10383
+ return;
10384
+ }
10385
+ const targetModules = resolveTargetModules(installed, lifecycle);
10386
+ const updater = dependencies.updater ?? new DefaultStarterUpdater;
10387
+ const plan = await updater.plan({
10388
+ projectDirectory,
10389
+ targetVersion: provenance.windyVersion,
10390
+ targetModules,
10391
+ lifecycle
10392
+ });
10393
+ printModulePlan(plan, log);
10394
+ if (args.includes("--dry-run")) {
10395
+ await rm10(plan.temporaryRoot, { recursive: true, force: true });
10396
+ log(`
10397
+ \u6F14\u7EC3\u5B8C\u6210\uFF1A\u672A\u5199\u5165\u9879\u76EE\u6587\u4EF6`);
10398
+ return;
10399
+ }
10400
+ if (action === "remove" && !args.includes("--confirm-data-disposition")) {
10401
+ await rm10(plan.temporaryRoot, { recursive: true, force: true });
10402
+ throw new Error("\u5378\u8F7D\u524D\u5FC5\u987B\u5BA1\u9605\u6570\u636E\u5904\u7F6E\u8BA1\u5212\u5E76\u4F20\u5165 --confirm-data-disposition");
10403
+ }
10404
+ if (plan.conflicts.length) {
10405
+ await rm10(plan.temporaryRoot, { recursive: true, force: true });
10406
+ throw new Error("\u5B58\u5728\u6A21\u5757\u751F\u547D\u5468\u671F\u51B2\u7A81\uFF0C\u9879\u76EE\u672A\u88AB\u4FEE\u6539");
10407
+ }
10408
+ const result2 = await updater.apply(plan);
10409
+ const report = await updater.verify(result2);
10410
+ log(`
10411
+ \u6A21\u5757${actionLabel(action)}\u5B8C\u6210\uFF1A${requested.join("\u3001")}`);
10412
+ log(`\u62A5\u544A\uFF1A${report.reportPath}`);
10413
+ }
10414
+ async function resolveLifecyclePlan(input) {
10415
+ const known = new Map(moduleDistributionCatalog.map((manifest) => [manifest.name, manifest]));
10416
+ const requested = input.action === "update" && input.requested.length === 0 ? [...input.installed] : input.requested;
10417
+ if (!requested.length) {
10418
+ throw new Error(`module ${input.action} \u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u6A21\u5757 key`);
10419
+ }
10420
+ for (const name of requested) {
10421
+ const manifest = known.get(name);
10422
+ if (!manifest)
10423
+ throw new Error(`\u672A\u77E5\u6A21\u5757\uFF1A${name}`);
10424
+ assertWindyCompatible(input.windyVersion, manifest.windyRange, name);
10425
+ }
10426
+ if (input.action === "add") {
10427
+ const target = resolveModuleSelection({
10428
+ requested: [...input.installed, ...requested],
10429
+ includeRecommended: false
10430
+ });
10431
+ for (const name of target) {
10432
+ const manifest = known.get(name);
10433
+ assertWindyCompatible(input.windyVersion, manifest.windyRange, manifest.name);
10434
+ }
10435
+ return {
10436
+ action: input.action,
10437
+ requestedModules: requested,
10438
+ addedModules: target.filter((name) => !input.installed.includes(name)),
10439
+ updatedModules: [],
10440
+ removedModules: []
10441
+ };
10442
+ }
10443
+ if (input.action === "update") {
10444
+ const absent = requested.filter((name) => !input.installed.includes(name));
10445
+ if (absent.length) {
10446
+ throw new Error(`\u672A\u5B89\u88C5\u6A21\u5757\u4E0D\u80FD\u66F4\u65B0\uFF1A${absent.join("\u3001")}`);
10447
+ }
10448
+ return {
10449
+ action: input.action,
10450
+ requestedModules: requested,
10451
+ addedModules: [],
10452
+ updatedModules: requested,
10453
+ removedModules: []
10454
+ };
10455
+ }
10456
+ const required = requested.filter((name) => requiredModuleNames.includes(name));
10457
+ if (required.length) {
10458
+ throw new Error(`\u6838\u5FC3\u6A21\u5757\u4E0D\u53EF\u5378\u8F7D\uFF1A${required.join("\u3001")}`);
10459
+ }
10460
+ const removed = requested.filter((name) => input.installed.includes(name));
10461
+ assertRemovalDependencies(input.installed, removed);
10462
+ const dataDisposition = resolveDataDisposition(removed.map((name) => known.get(name).uninstallPolicy), readValue2(input.args, "--data-disposition"));
10463
+ const dataPlan = readValue2(input.args, "--data-plan");
10464
+ if (dataDisposition !== "retain-data") {
10465
+ if (!dataPlan) {
10466
+ throw new Error(`${dataDisposition} \u5378\u8F7D\u5FC5\u987B\u901A\u8FC7 --data-plan \u63D0\u4F9B\u5DF2\u5BA1\u9605\u7684\u5904\u7F6E\u6587\u6863`);
10467
+ }
10468
+ await access2(resolve7(input.projectDirectory, dataPlan));
10469
+ }
10470
+ return {
10471
+ action: input.action,
10472
+ requestedModules: requested,
10473
+ addedModules: [],
10474
+ updatedModules: [],
10475
+ removedModules: removed,
10476
+ dataDisposition,
10477
+ dataPlan
10478
+ };
10479
+ }
10480
+ function resolveTargetModules(installed, lifecycle) {
10481
+ if (lifecycle.action === "add") {
10482
+ return resolveModuleSelection({
10483
+ requested: [...installed, ...lifecycle.addedModules],
10484
+ includeRecommended: false
10485
+ });
10486
+ }
10487
+ if (lifecycle.action === "remove") {
10488
+ const removed = new Set(lifecycle.removedModules);
10489
+ return installed.filter((name) => !removed.has(name));
10490
+ }
10491
+ return installed;
10492
+ }
10493
+ function resolveDataDisposition(policies, requested) {
10494
+ const disposition = requested ?? strongestPolicy(policies);
10495
+ if (disposition !== "retain-data" && disposition !== "archive-data" && disposition !== "manual") {
10496
+ throw new Error(`\u4E0D\u652F\u6301\u7684\u6570\u636E\u5904\u7F6E\u7B56\u7565\uFF1A${disposition}`);
10497
+ }
10498
+ const minimum = strongestPolicy(policies);
10499
+ const rank = {
10500
+ "retain-data": 0,
10501
+ "archive-data": 1,
10502
+ manual: 2
10503
+ };
10504
+ if (rank[disposition] < rank[minimum]) {
10505
+ throw new Error(`\u6A21\u5757\u76EE\u5F55\u8981\u6C42 ${minimum}\uFF0C\u4E0D\u80FD\u964D\u7EA7\u4E3A ${disposition}`);
10506
+ }
10507
+ return disposition;
10508
+ }
10509
+ function strongestPolicy(policies) {
10510
+ if (policies.includes("manual"))
10511
+ return "manual";
10512
+ if (policies.includes("archive-data"))
10513
+ return "archive-data";
10514
+ return "retain-data";
10515
+ }
10516
+ function assertRemovalDependencies(installed, removed) {
10517
+ const removedSet = new Set(removed);
10518
+ const blockers = moduleDistributionCatalog.filter(({ name, dependencies }) => installed.includes(name) && !removedSet.has(name) && dependencies.some((dependency) => removedSet.has(dependency))).map(({ name }) => name);
10519
+ if (blockers.length) {
10520
+ throw new Error(`\u4ECD\u6709\u5DF2\u5B89\u88C5\u6A21\u5757\u4F9D\u8D56\u5F85\u5378\u8F7D\u6A21\u5757\uFF1A${blockers.join("\u3001")}`);
10521
+ }
10522
+ }
10523
+ function assertWindyCompatible(version, range, moduleName) {
10524
+ if (!isVersionInCaretRange(version, range)) {
10525
+ throw new Error(`${moduleName} \u8981\u6C42 Windy ${range}\uFF0C\u5F53\u524D\u4E3A ${version}`);
10526
+ }
10527
+ }
10528
+ function positionalModules(args) {
10529
+ const valueFlags = new Set(["--data-disposition", "--data-plan", "--cwd"]);
10530
+ const output = [];
10531
+ for (let index = 0;index < args.length; index += 1) {
10532
+ const value = args[index];
10533
+ if (valueFlags.has(value)) {
10534
+ index += 1;
10535
+ } else if (!value.startsWith("-")) {
10536
+ output.push(value);
10537
+ }
10538
+ }
10539
+ return [...new Set(output)];
10540
+ }
10541
+ function printModuleList(installed, log) {
10542
+ const installedVersions = new Map(installed.map(({ name, version }) => [name, version]));
10543
+ log("Windy \u6A21\u5757\uFF1A");
10544
+ for (const manifest of moduleDistributionCatalog) {
10545
+ const installedVersion = installedVersions.get(manifest.name);
10546
+ const status = installedVersion ? "installed" : "available";
10547
+ const version = installedVersion ? `${manifest.name}@${installedVersion}\uFF08catalog ${manifest.version}\uFF09` : `${manifest.name}@${manifest.version}`;
10548
+ log(` ${status.padEnd(9)} ${version}`);
10549
+ }
10550
+ }
10551
+ function printDataPlan(lifecycle, log) {
10552
+ if (lifecycle.action !== "remove")
10553
+ return;
10554
+ log("\u6A21\u5757\u5378\u8F7D\u6570\u636E\u5904\u7F6E\u8BA1\u5212\uFF1A");
10555
+ log(` \u6A21\u5757\uFF1A${lifecycle.removedModules.join("\u3001") || "\u5747\u672A\u5B89\u88C5"}`);
10556
+ log(` \u6570\u636E\uFF1A${lifecycle.dataDisposition ?? "retain-data"}`);
10557
+ log(" migration\uFF1A\u4FDD\u7559\u5230 .windy/module-history\uFF0C\u4E0D\u6267\u884C down migration");
10558
+ if (lifecycle.dataPlan)
10559
+ log(` \u8FD0\u7EF4\u5904\u7F6E\u6587\u6863\uFF1A${lifecycle.dataPlan}`);
10560
+ }
10561
+ function printModulePlan(plan, log) {
10562
+ const changed = plan.files.filter(({ action }) => action !== "preserve");
10563
+ log(`\u6A21\u5757\u6587\u4EF6\u8BA1\u5212\uFF1A${changed.length} \u4E2A\u53D8\u66F4\uFF1B${plan.conflicts.length} \u4E2A\u51B2\u7A81`);
10564
+ for (const file of changed) {
10565
+ log(` ${file.action.padEnd(6)} ${file.path}\uFF08${file.reason}\uFF09`);
10566
+ }
10567
+ for (const conflict3 of plan.conflicts)
10568
+ log(` conflict ${conflict3}`);
10569
+ }
10570
+ function hasChanges(lifecycle) {
10571
+ return lifecycle.addedModules.length > 0 || lifecycle.updatedModules.length > 0 || lifecycle.removedModules.length > 0;
10572
+ }
10573
+ function readValue2(args, flag) {
10574
+ const index = args.indexOf(flag);
10575
+ return index >= 0 ? args[index + 1] : undefined;
10576
+ }
10577
+ function isAction(value) {
10578
+ return value === "list" || value === "add" || value === "update" || value === "remove";
10579
+ }
10580
+ function actionLabel(action) {
10581
+ return action === "add" ? "\u5B89\u88C5" : action === "update" ? "\u66F4\u65B0" : "\u5378\u8F7D";
10582
+ }
10583
+
9880
10584
  // src/update/cli.ts
9881
10585
  async function runLifecycleCommand(args) {
9882
10586
  const command = args[0];
9883
- if (command !== "doctor" && command !== "update" && command !== "reconcile" && command !== "sync-docker") {
10587
+ if (command !== "doctor" && command !== "update" && command !== "module" && command !== "reconcile" && command !== "sync-docker") {
9884
10588
  return false;
9885
10589
  }
9886
- const projectDirectory = resolve7(readValue2(args, "--cwd") || process.cwd());
10590
+ const projectDirectory = resolve8(readValue3(args, "--cwd") || process.cwd());
10591
+ if (command === "module") {
10592
+ await runModuleLifecycleCommand(args.slice(1), projectDirectory);
10593
+ return true;
10594
+ }
9887
10595
  if (command === "sync-docker") {
9888
10596
  const result2 = await planWebDockerfileInProject(projectDirectory, {
9889
10597
  requireAnchors: true
@@ -9940,7 +10648,7 @@ async function runReconcile(args, projectDirectory) {
9940
10648
  });
9941
10649
  const plan = await reconciler.plan({
9942
10650
  projectDirectory,
9943
- targetVersion: readValue2(args, "--to") ?? ""
10651
+ targetVersion: readValue3(args, "--to") ?? ""
9944
10652
  });
9945
10653
  console.log(`Windy \u5BF9\u8D26\u8BA1\u5212\uFF1A${plan.sourceVersion} \u2192 ${plan.targetVersion}\uFF1B\u53EF\u8BC1\u660E ${plan.reconciled.length}\uFF1B\u4FDD\u5B88\u672A\u51B3 ${plan.unresolved.length}`);
9946
10654
  for (const item of plan.reconciled) {
@@ -9950,7 +10658,7 @@ async function runReconcile(args, projectDirectory) {
9950
10658
  console.log(` unresolved ${item.path}\uFF08${item.reason}\uFF09`);
9951
10659
  }
9952
10660
  if (!args.includes("--apply")) {
9953
- await rm10(plan.temporaryRoot, { recursive: true, force: true });
10661
+ await rm11(plan.temporaryRoot, { recursive: true, force: true });
9954
10662
  console.log(`
9955
10663
  \u6F14\u7EC3\u5B8C\u6210\uFF1A\u672A\u5199\u5165\u9879\u76EE\u6587\u4EF6\uFF1B\u4F7F\u7528 --apply \u4EC5\u63D0\u4EA4\u5BF9\u8D26\u5143\u6570\u636E`);
9956
10664
  return;
@@ -9961,12 +10669,12 @@ async function runReconcile(args, projectDirectory) {
9961
10669
  \u5BF9\u8D26\u5B8C\u6210\uFF1AProject Lock \u4ECD\u4E3A ${plan.sourceVersion}`);
9962
10670
  console.log(`\u62A5\u544A\uFF1A${result2.reportPath}`);
9963
10671
  } finally {
9964
- await rm10(plan.temporaryRoot, { recursive: true, force: true });
10672
+ await rm11(plan.temporaryRoot, { recursive: true, force: true });
9965
10673
  }
9966
10674
  }
9967
10675
  async function runUpdate(args, projectDirectory) {
9968
10676
  const template = await readTemplateMetadata(await resolveTemplateRoot());
9969
- const requested = readValue2(args, "--to");
10677
+ const requested = readValue3(args, "--to");
9970
10678
  const registryVersion = requested ? undefined : await readRegistryLatest();
9971
10679
  if (registryVersion && compareVersions2(registryVersion, template.generatorVersion) > 0 && !args.includes("--no-delegate")) {
9972
10680
  if (args.includes("--check")) {
@@ -9996,13 +10704,13 @@ async function runUpdate(args, projectDirectory) {
9996
10704
  const plan = await updater.plan({ projectDirectory, targetVersion: target });
9997
10705
  printPlan(plan);
9998
10706
  if (args.includes("--dry-run")) {
9999
- await rm10(plan.temporaryRoot, { recursive: true, force: true });
10707
+ await rm11(plan.temporaryRoot, { recursive: true, force: true });
10000
10708
  console.log(`
10001
10709
  \u6F14\u7EC3\u5B8C\u6210\uFF1A\u672A\u5199\u5165\u9879\u76EE\u6587\u4EF6`);
10002
10710
  return;
10003
10711
  }
10004
10712
  if (plan.conflicts.length) {
10005
- await rm10(plan.temporaryRoot, { recursive: true, force: true });
10713
+ await rm11(plan.temporaryRoot, { recursive: true, force: true });
10006
10714
  throw new Error("\u5B58\u5728\u66F4\u65B0\u51B2\u7A81\uFF1B\u8BF7\u6309\u4E0A\u65B9\u6E05\u5355\u5904\u7406\u540E\u91CD\u65B0\u8FD0\u884C\uFF0C\u9879\u76EE\u672A\u88AB\u4FEE\u6539");
10007
10715
  }
10008
10716
  const result2 = await updater.apply(plan);
@@ -10036,8 +10744,8 @@ function printPlan(plan) {
10036
10744
  console.log(`\u8BA1\u5212\u6539\u52A8\uFF1A${changed.length} \u4E2A\u6587\u4EF6\uFF1B\u51B2\u7A81\uFF1A${plan.conflicts.length}`);
10037
10745
  for (const file of changed)
10038
10746
  console.log(` ${file.action.padEnd(6)} ${file.path}\uFF08${file.reason}\uFF09`);
10039
- for (const conflict2 of plan.conflicts)
10040
- console.log(` conflict ${conflict2}`);
10747
+ for (const conflict3 of plan.conflicts)
10748
+ console.log(` conflict ${conflict3}`);
10041
10749
  if (plan.detachedToolchain)
10042
10750
  console.log("Oxc\uFF1A\u68C0\u6D4B\u5230\u5BA2\u6237\u63A5\u7BA1\uFF0C\u76F8\u5173\u914D\u7F6E\u4FDD\u6301\u4E0D\u53D8");
10043
10751
  }
@@ -10060,7 +10768,7 @@ function printDoctor(report) {
10060
10768
  console.log("\u6062\u590D\uFF1A\u5DF2\u4ECE\u4E8B\u52A1\u5907\u4EFD\u56DE\u6EDA");
10061
10769
  }
10062
10770
  }
10063
- function readValue2(args, flag) {
10771
+ function readValue3(args, flag) {
10064
10772
  const index = args.indexOf(flag);
10065
10773
  return index >= 0 ? args[index + 1] : undefined;
10066
10774
  }
@@ -10087,28 +10795,28 @@ function compareVersions2(left, right) {
10087
10795
  }
10088
10796
 
10089
10797
  // src/module-schema/cli.ts
10090
- import { resolve as resolve9 } from "path";
10798
+ import { resolve as resolve10 } from "path";
10091
10799
 
10092
10800
  // src/module-schema/handoff.ts
10093
10801
  import {
10094
- access as access2,
10802
+ access as access3,
10095
10803
  cp,
10096
- mkdir as mkdir8,
10804
+ mkdir as mkdir9,
10097
10805
  mkdtemp as mkdtemp5,
10098
- readFile as readFile25,
10099
- readdir as readdir8,
10806
+ readFile as readFile26,
10807
+ readdir as readdir9,
10100
10808
  rename,
10101
- rm as rm11,
10102
- writeFile as writeFile12
10809
+ rm as rm12,
10810
+ writeFile as writeFile13
10103
10811
  } from "fs/promises";
10104
- import { dirname as dirname8, join as join31, relative as relative5 } from "path";
10812
+ import { dirname as dirname9, join as join32, relative as relative6 } from "path";
10105
10813
 
10106
10814
  // src/module-schema/config.ts
10107
- import { readFile as readFile24 } from "fs/promises";
10108
- import { isAbsolute as isAbsolute2, relative as relative4, resolve as resolve8 } from "path";
10815
+ import { readFile as readFile25 } from "fs/promises";
10816
+ import { isAbsolute as isAbsolute2, relative as relative5, resolve as resolve9 } from "path";
10109
10817
  async function readModuleSchemaLineageConfig(projectDirectory, configPath) {
10110
10818
  const absolutePath = resolveProjectPath(projectDirectory, configPath);
10111
- const parsed = JSON.parse(await readFile24(absolutePath, "utf8"));
10819
+ const parsed = JSON.parse(await readFile25(absolutePath, "utf8"));
10112
10820
  if (!isConfig(parsed)) {
10113
10821
  throw new Error("Module schema lineage \u914D\u7F6E\u65E0\u6548");
10114
10822
  }
@@ -10122,8 +10830,8 @@ async function readModuleSchemaLineageConfig(projectDirectory, configPath) {
10122
10830
  function resolveProjectPath(projectDirectory, path2) {
10123
10831
  if (isAbsolute2(path2))
10124
10832
  throw new Error(`\u8DEF\u5F84\u5FC5\u987B\u76F8\u5BF9\u9879\u76EE\u6839\u76EE\u5F55\uFF1A${path2}`);
10125
- const resolved = resolve8(projectDirectory, path2);
10126
- const projectRelative = relative4(projectDirectory, resolved);
10833
+ const resolved = resolve9(projectDirectory, path2);
10834
+ const projectRelative = relative5(projectDirectory, resolved);
10127
10835
  if (projectRelative.startsWith("..") || isAbsolute2(projectRelative)) {
10128
10836
  throw new Error(`\u8DEF\u5F84\u8D8A\u51FA\u9879\u76EE\u6839\u76EE\u5F55\uFF1A${path2}`);
10129
10837
  }
@@ -10232,7 +10940,7 @@ async function handoffModuleSchemaLineages(options) {
10232
10940
  const stagedPlatform = await stagePlatformLineage(project, temporaryRoot, config, execute);
10233
10941
  await installStagedLineages(project, temporaryRoot, config, stagedModules, stagedPlatform);
10234
10942
  } finally {
10235
- await rm11(temporaryRoot, { recursive: true, force: true });
10943
+ await rm12(temporaryRoot, { recursive: true, force: true });
10236
10944
  }
10237
10945
  return {
10238
10946
  moduleCount: config.modules.length,
@@ -10242,17 +10950,17 @@ async function handoffModuleSchemaLineages(options) {
10242
10950
  async function stageModuleLineages(project, temporaryRoot, config, execute) {
10243
10951
  const outputs = [];
10244
10952
  for (const [index, module3] of config.modules.entries()) {
10245
- const output = join31(temporaryRoot, `module-${index}`);
10246
- await mkdir8(output, { recursive: true });
10247
- const generatedConfig = join31(temporaryRoot, `module-${index}.config.ts`);
10248
- await writeDrizzleConfig(generatedConfig, project, module3.schema, relative5(temporaryRoot, output));
10953
+ const output = join32(temporaryRoot, `module-${index}`);
10954
+ await mkdir9(output, { recursive: true });
10955
+ const generatedConfig = join32(temporaryRoot, `module-${index}.config.ts`);
10956
+ await writeDrizzleConfig(generatedConfig, project, module3.schema, relative6(temporaryRoot, output));
10249
10957
  await execute(generatedConfig, temporaryRoot);
10250
10958
  const sqlFiles = await listSqlFiles(output);
10251
10959
  if (sqlFiles.length !== 1) {
10252
10960
  throw new Error(`${module3.key} baseline \u5FC5\u987B\u6070\u597D\u751F\u6210\u4E00\u4E2A migration`);
10253
10961
  }
10254
- await writeFile12(join31(output, sqlFiles[0]), metadataOnlySql("module"));
10255
- await writeFile12(join31(output, "lineage.json"), `${JSON.stringify({
10962
+ await writeFile13(join32(output, sqlFiles[0]), metadataOnlySql("module"));
10963
+ await writeFile13(join32(output, "lineage.json"), `${JSON.stringify({
10256
10964
  schemaVersion: 1,
10257
10965
  moduleKey: module3.key,
10258
10966
  schema: module3.schema,
@@ -10266,25 +10974,25 @@ async function stageModuleLineages(project, temporaryRoot, config, execute) {
10266
10974
  }
10267
10975
  async function stagePlatformLineage(project, temporaryRoot, config, execute) {
10268
10976
  const source = resolveProjectPath(project, config.platform.out);
10269
- const output = join31(temporaryRoot, "platform");
10977
+ const output = join32(temporaryRoot, "platform");
10270
10978
  await cp(source, output, { recursive: true });
10271
10979
  const before = new Set(await listSqlFiles(output));
10272
- const generatedConfig = join31(temporaryRoot, "platform.config.ts");
10273
- await writeDrizzleConfig(generatedConfig, project, config.platform.schema, relative5(temporaryRoot, output));
10980
+ const generatedConfig = join32(temporaryRoot, "platform.config.ts");
10981
+ await writeDrizzleConfig(generatedConfig, project, config.platform.schema, relative6(temporaryRoot, output));
10274
10982
  await execute(generatedConfig, temporaryRoot);
10275
10983
  const created = (await listSqlFiles(output)).filter((path2) => !before.has(path2));
10276
10984
  if (created.length !== 1) {
10277
10985
  throw new Error("\u5E73\u53F0 lineage handoff \u5FC5\u987B\u6070\u597D\u751F\u6210\u4E00\u4E2A migration");
10278
10986
  }
10279
- const sqlPath = join31(output, created[0]);
10987
+ const sqlPath = join32(output, created[0]);
10280
10988
  const allowed = config.modules.flatMap(({ legacyPlatformTables }) => legacyPlatformTables);
10281
- validatePlatformHandoffSql(await readFile25(sqlPath, "utf8"), allowed);
10282
- await writeFile12(sqlPath, metadataOnlySql("platform"));
10989
+ validatePlatformHandoffSql(await readFile26(sqlPath, "utf8"), allowed);
10990
+ await writeFile13(sqlPath, metadataOnlySql("platform"));
10283
10991
  return output;
10284
10992
  }
10285
10993
  async function installStagedLineages(project, temporaryRoot, config, stagedModules, stagedPlatform) {
10286
10994
  const platform = resolveProjectPath(project, config.platform.out);
10287
- const backup = join31(temporaryRoot, "platform-backup");
10995
+ const backup = join32(temporaryRoot, "platform-backup");
10288
10996
  const installed = [];
10289
10997
  await rename(platform, backup);
10290
10998
  try {
@@ -10294,21 +11002,21 @@ async function installStagedLineages(project, temporaryRoot, config, stagedModul
10294
11002
  if (await exists(output)) {
10295
11003
  throw new Error(`${module3.key} lineage \u8F93\u51FA\u5DF2\u5B58\u5728\uFF0C\u7981\u6B62\u8986\u76D6`);
10296
11004
  }
10297
- await mkdir8(dirname8(output), { recursive: true });
11005
+ await mkdir9(dirname9(output), { recursive: true });
10298
11006
  await rename(stagedModules[index], output);
10299
11007
  installed.push(output);
10300
11008
  }
10301
11009
  } catch (error) {
10302
- await rm11(platform, { recursive: true, force: true });
11010
+ await rm12(platform, { recursive: true, force: true });
10303
11011
  await rename(backup, platform);
10304
11012
  for (const output of installed)
10305
- await rm11(output, { recursive: true, force: true });
11013
+ await rm12(output, { recursive: true, force: true });
10306
11014
  throw error;
10307
11015
  }
10308
11016
  }
10309
11017
  async function writeDrizzleConfig(path2, project, schemas, out) {
10310
11018
  const absoluteSchemas = schemas.map((schema) => resolveProjectPath(project, schema));
10311
- await writeFile12(path2, `import { defineConfig } from "drizzle-kit";
11019
+ await writeFile13(path2, `import { defineConfig } from "drizzle-kit";
10312
11020
  export default defineConfig(${JSON.stringify({
10313
11021
  dialect: "postgresql",
10314
11022
  schema: absoluteSchemas,
@@ -10322,7 +11030,7 @@ async function validateSchemaSources(project, config) {
10322
11030
  ...config.platform.schema,
10323
11031
  ...config.modules.flatMap(({ schema: schema2 }) => schema2)
10324
11032
  ]) {
10325
- await access2(resolveProjectPath(project, schema));
11033
+ await access3(resolveProjectPath(project, schema));
10326
11034
  }
10327
11035
  for (const module3 of config.modules) {
10328
11036
  if (await exists(resolveProjectPath(project, module3.out))) {
@@ -10331,12 +11039,12 @@ async function validateSchemaSources(project, config) {
10331
11039
  }
10332
11040
  }
10333
11041
  async function createTemporaryRoot(project) {
10334
- const windy = join31(project, ".windy");
10335
- await mkdir8(windy, { recursive: true });
10336
- return await mkdtemp5(join31(windy, "module-schema-handoff-"));
11042
+ const windy = join32(project, ".windy");
11043
+ await mkdir9(windy, { recursive: true });
11044
+ return await mkdtemp5(join32(windy, "module-schema-handoff-"));
10337
11045
  }
10338
11046
  async function listSqlFiles(root) {
10339
- return (await readdir8(root)).filter((path2) => path2.endsWith(".sql")).sort();
11047
+ return (await readdir9(root)).filter((path2) => path2.endsWith(".sql")).sort();
10340
11048
  }
10341
11049
  async function executeDrizzleGenerate(configPath, cwd) {
10342
11050
  await assertProcess("bunx", ["--bun", "drizzle-kit", "generate", "--config", configPath], cwd, true);
@@ -10349,7 +11057,7 @@ async function assertCleanGit(project) {
10349
11057
  }
10350
11058
  async function exists(path2) {
10351
11059
  try {
10352
- await access2(path2);
11060
+ await access3(path2);
10353
11061
  return true;
10354
11062
  } catch {
10355
11063
  return false;
@@ -10363,15 +11071,15 @@ async function runModuleSchemaCommand(args) {
10363
11071
  if (args[1] !== "handoff") {
10364
11072
  throw new Error("\u5F53\u524D schema \u547D\u4EE4\u53EA\u652F\u6301 handoff");
10365
11073
  }
10366
- const projectDirectory = resolve9(readValue3(args, "--cwd") || process.cwd());
11074
+ const projectDirectory = resolve10(readValue4(args, "--cwd") || process.cwd());
10367
11075
  const result2 = await handoffModuleSchemaLineages({
10368
11076
  projectDirectory,
10369
- configPath: readValue3(args, "--config")
11077
+ configPath: readValue4(args, "--config")
10370
11078
  });
10371
11079
  console.log(`Module schema lineage \u4EA4\u63A5\u5B8C\u6210\uFF1A${result2.moduleCount} \u4E2A\u6A21\u5757\u3001${result2.tableCount} \u5F20\u65E7\u5E73\u53F0 snapshot \u8868`);
10372
11080
  return true;
10373
11081
  }
10374
- function readValue3(args, flag) {
11082
+ function readValue4(args, flag) {
10375
11083
  const index = args.indexOf(flag);
10376
11084
  return index >= 0 ? args[index + 1] : undefined;
10377
11085
  }