create-windy 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/dist/cli.js +1769 -1277
  2. package/package.json +2 -2
  3. package/template/.agents/skills/windy-business-module/SKILL.md +33 -1
  4. package/template/.windy-template.json +6 -2
  5. package/template/AGENTS.md +4 -0
  6. package/template/README.md +2 -0
  7. package/template/apps/agent-server/Dockerfile +1 -1
  8. package/template/apps/agent-server/README.md +1 -1
  9. package/template/apps/agent-server/native/EXTENSIONS.md +2 -2
  10. package/template/apps/agent-server/native/parallel-harness.ts +2 -0
  11. package/template/apps/agent-server/native/storage-lifecycle.test.ts +2 -1
  12. package/template/apps/agent-server/package.json +42 -41
  13. package/template/apps/agent-server/plugins/downloads/README.md +1 -1
  14. package/template/apps/agent-server/plugins/drop-overlay/README.md +1 -1
  15. package/template/apps/agent-server/plugins/settings/README.md +1 -1
  16. package/template/apps/agent-server/plugins/skill-library/client-chunks.test.ts +4 -1
  17. package/template/apps/agent-server/plugins/theme/README.md +5 -1
  18. package/template/apps/agent-server/plugins/theme/source-tokens.ts +0 -1
  19. package/template/apps/agent-server/plugins/theme/tokens.json +0 -4
  20. package/template/apps/agent-server/plugins/tool-catalog/README.md +6 -3
  21. package/template/apps/agent-server/plugins/tool-catalog/discovery.mjs +13 -10
  22. package/template/apps/agent-server/plugins/tool-catalog/discovery.test.ts +68 -6
  23. package/template/apps/agent-server/runtime/compatibility.ts +7 -1
  24. package/template/apps/agent-server/runtime/dsh-engine.ts +2 -0
  25. package/template/apps/agent-server/runtime/questions-workflow.nodecheck.ts +2 -0
  26. package/template/apps/server/Dockerfile +2 -1
  27. package/template/apps/server/package.json +11 -11
  28. package/template/apps/server/scripts/validate-module-composition-once.ts +2 -0
  29. package/template/apps/server/scripts/validate-module-composition.ts +8 -4
  30. package/template/apps/server/src/admin/routes.test.ts +29 -1
  31. package/template/apps/server/src/admin/routes.ts +20 -2
  32. package/template/apps/server/src/agent/client-session/completion.ts +1 -1
  33. package/template/apps/server/src/agent/client-session/manifest-admission.ts +1 -1
  34. package/template/apps/server/src/agent/client-session/request.ts +1 -1
  35. package/template/apps/server/src/agent/context-version-routes.ts +37 -13
  36. package/template/apps/server/src/agent/run-facade.ts +1 -1
  37. package/template/apps/server/src/agent/run-input-verification.ts +1 -1
  38. package/template/apps/server/src/agent/run-routes.ts +1 -14
  39. package/template/apps/server/src/agent/run-start-preparation.ts +1 -1
  40. package/template/apps/server/src/agent/runtime-bootstrap-input.ts +2 -0
  41. package/template/apps/server/src/agent/runtime-bootstrap.ts +49 -157
  42. package/template/apps/server/src/agent/runtime-disabled-composition.ts +2 -1
  43. package/template/apps/server/src/agent/session-invalidation/authorization-revocation.ts +54 -0
  44. package/template/apps/server/src/agent/session-runtime-composition.ts +1 -51
  45. package/template/apps/server/src/agent/unavailable-routes.ts +13 -2
  46. package/template/apps/server/src/agent-workflow/runtime-composition.ts +3 -0
  47. package/template/apps/server/src/agent-workflow/service.ts +17 -1
  48. package/template/apps/server/src/ai-artifact/media-resource-route.test.ts +134 -0
  49. package/template/apps/server/src/ai-artifact/routes.ts +1 -1
  50. package/template/apps/server/src/ai-artifact/runtime.ts +12 -0
  51. package/template/apps/server/src/ai-runtime-readiness/operation-port.test-support.ts +1 -1
  52. package/template/apps/server/src/ai-runtime-readiness/operation-port.ts +8 -0
  53. package/template/apps/server/src/ai-runtime-security/internal-token.ts +14 -0
  54. package/template/apps/server/src/app/antivirus-composition.test.ts +63 -0
  55. package/template/apps/server/src/app/antivirus-composition.ts +29 -0
  56. package/template/apps/server/src/app/dsh-workbench-socket-audit.test.ts +2 -1
  57. package/template/apps/server/src/configuration/definition.ts +15 -11
  58. package/template/apps/server/src/document/facade-support.ts +2 -2
  59. package/template/apps/server/src/document/facade.ts +2 -2
  60. package/template/apps/server/src/document/output-artifact-composition.ts +9 -5
  61. package/template/apps/server/src/document/output-artifact.test-support.ts +1 -1
  62. package/template/apps/server/src/embedding/facade.ts +1 -1
  63. package/template/apps/server/src/embedding/input.ts +1 -1
  64. package/template/apps/server/src/embedding/routes.ts +1 -1
  65. package/template/apps/server/src/index.ts +60 -60
  66. package/template/apps/server/src/license/routes.ts +4 -1
  67. package/template/apps/server/src/license/runtime-business-access.test.ts +261 -0
  68. package/template/apps/server/src/media-runtime/composition.ts +157 -0
  69. package/template/apps/server/src/media-runtime/media-only-composition.ts +44 -0
  70. package/template/apps/server/src/media-runtime/module-ai-port-factory.test.ts +69 -0
  71. package/template/apps/server/src/media-runtime/module-ai-port-factory.ts +72 -0
  72. package/template/apps/server/src/{agent → media-runtime}/runtime-readiness-sources.ts +14 -6
  73. package/template/apps/server/src/media-runtime/shared-composition.ts +104 -0
  74. package/template/apps/server/src/module-composition.test.ts +22 -0
  75. package/template/apps/server/src/module-composition.ts +48 -0
  76. package/template/apps/server/src/module-host/ai-operation-port.ts +97 -30
  77. package/template/apps/server/src/module-host/ai-operation-speech-normalize.test.ts +1 -1
  78. package/template/apps/server/src/module-host/http-runtime.ts +9 -3
  79. package/template/apps/server/src/module-host/static-route-authorization.test.ts +6 -3
  80. package/template/apps/server/src/module-host/storage-authorization-binding.test.ts +4 -0
  81. package/template/apps/server/src/module-runtime-validation.ts +2 -0
  82. package/template/apps/server/src/observability/diagnostic-index.ts +26 -1
  83. package/template/apps/server/src/observability/diagnostic-repository.ts +40 -11
  84. package/template/apps/server/src/platform-http-runtime.ts +1 -1
  85. package/template/apps/server/src/runtime-feature.ts +5 -1
  86. package/template/apps/server/src/runtime.ts +3 -0
  87. package/template/apps/server/src/settings/drizzle-watermark-repository.ts +35 -4
  88. package/template/apps/server/src/settings/watermark-repository.ts +14 -0
  89. package/template/apps/server/src/settings/watermark-routes.test.ts +84 -2
  90. package/template/apps/server/src/settings/watermark-routes.ts +109 -0
  91. package/template/apps/server/src/settings/watermark-transfer.ts +79 -0
  92. package/template/apps/server/src/speech/facade-validation.ts +1 -1
  93. package/template/apps/server/src/speech/facade.ts +2 -2
  94. package/template/apps/server/src/startup-composition-validation.test.ts +32 -0
  95. package/template/apps/server/src/startup-finalization.ts +2 -0
  96. package/template/apps/server/src/startup-runtime.ts +77 -0
  97. package/template/apps/server/src/system/ai-runtime-build.ts +3 -0
  98. package/template/apps/server/src/system/ai-runtime-configuration.test.ts +36 -0
  99. package/template/apps/server/src/system/ai-runtime-configuration.ts +58 -0
  100. package/template/apps/server/src/system/diagnostic-routes.test.ts +45 -0
  101. package/template/apps/server/src/system/diagnostic-routes.ts +35 -0
  102. package/template/apps/server/src/system/environment-policy-reader.test.ts +74 -0
  103. package/template/apps/server/src/system/environment-policy-reader.ts +62 -0
  104. package/template/apps/server/src/system/feature-environment-assessment.test.ts +148 -0
  105. package/template/apps/server/src/system/feature-environment-assessment.ts +179 -0
  106. package/template/apps/server/src/system/host-diagnostics.test.ts +155 -0
  107. package/template/apps/server/src/system/host-diagnostics.ts +225 -0
  108. package/template/apps/server/src/system/menu-registry.test.ts +26 -0
  109. package/template/apps/server/src/system/menu-registry.ts +9 -7
  110. package/template/apps/server/src/system/operations.test.ts +5 -0
  111. package/template/apps/server/src/system/operations.ts +36 -12
  112. package/template/apps/server/src/system/role-authorization-postgres.integration.test.ts +181 -0
  113. package/template/apps/web/Dockerfile +7 -3
  114. package/template/apps/web/dev/dev-port.test.ts +6 -6
  115. package/template/apps/web/dev/dev-port.ts +1 -1
  116. package/template/apps/web/dev/start.ts +6 -1
  117. package/template/apps/web/index.html +1 -0
  118. package/template/apps/web/package.json +16 -16
  119. package/template/apps/web/runtime-server.test.ts +22 -1
  120. package/template/apps/web/runtime-server.ts +6 -0
  121. package/template/apps/web/src/App.vue +6 -3
  122. package/template/apps/web/src/assets/windy.svg +4 -0
  123. package/template/apps/web/src/components/auth/LoginForm.vue +2 -2
  124. package/template/apps/web/src/components/auth/PasswordChangeForm.vue +14 -3
  125. package/template/apps/web/src/components/common/FormDialog.vue +1 -1
  126. package/template/apps/web/src/components/license/LicenseFlow.webtest.ts +3 -0
  127. package/template/apps/web/src/components/license/LicenseMachineCodeQr.vue +99 -0
  128. package/template/apps/web/src/components/license/LicenseMachineCodeQr.webtest.ts +83 -0
  129. package/template/apps/web/src/components/license/LicensePairingCard.vue +1 -1
  130. package/template/apps/web/src/components/license/LicenseStatusCard.vue +14 -2
  131. package/template/apps/web/src/composables/useLoginFlow.ts +2 -2
  132. package/template/apps/web/src/composables/usePlatformSettings.ts +1 -1
  133. package/template/apps/web/src/composables/useWatermarkSettings.ts +1 -0
  134. package/template/apps/web/src/layout/AppSidebar.vue +5 -9
  135. package/template/apps/web/src/layout/AppSidebar.webtest.ts +4 -1
  136. package/template/apps/web/src/pages/auth/ChangePasswordPage.vue +67 -6
  137. package/template/apps/web/src/pages/auth/LoginPage.vue +3 -12
  138. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.vue +30 -0
  139. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationWorkbench.vue +25 -14
  140. package/template/apps/web/src/pages/configuration/components/TimeZoneSelect.vue +55 -0
  141. package/template/apps/web/src/pages/profile/PersonalSettingsPage.vue +64 -11
  142. package/template/apps/web/src/pages/profile/PersonalSettingsPage.webtest.ts +21 -1
  143. package/template/apps/web/src/pages/settings/BrandingSettingsSection.vue +7 -18
  144. package/template/apps/web/src/pages/settings/BrandingSettingsSection.webtest.ts +9 -4
  145. package/template/apps/web/src/pages/settings/GlobalSettingsPage.vue +51 -6
  146. package/template/apps/web/src/pages/settings/GlobalSettingsPage.webtest.ts +19 -3
  147. package/template/apps/web/src/pages/settings/WatermarkSettingsSection.vue +1 -0
  148. package/template/apps/web/src/pages/settings/WatermarkYamlTransfer.vue +211 -0
  149. package/template/apps/web/src/pages/system/DiagnosticLookupPage.vue +18 -3
  150. package/template/apps/web/src/pages/system/DiagnosticLookupPage.webtest.ts +4 -1
  151. package/template/apps/web/src/pages/system/SystemOperationsPage.vue +23 -0
  152. package/template/apps/web/src/pages/system/components/DiagnosticLogList.vue +120 -0
  153. package/template/apps/web/src/pages/system/components/OperationEnvironmentPanel.vue +237 -0
  154. package/template/apps/web/src/pages/system/components/OperationFeatureAssessmentPanel.vue +131 -0
  155. package/template/apps/web/src/pages/system/components/PagedSelectionList.vue +31 -24
  156. package/template/apps/web/src/pages/system/components/SystemResourceActions.vue +2 -1
  157. package/template/apps/web/src/pages/system/components/SystemResourceEditor.vue +11 -2
  158. package/template/apps/web/src/pages/system/components/SystemResourcePage.vue +1 -0
  159. package/template/apps/web/src/pages/system/components/SystemResourcePage.webtest.ts +6 -2
  160. package/template/apps/web/src/pages/system/components/agent-skills/AgentSkillImportDialog.vue +18 -9
  161. package/template/apps/web/src/pages/system/components/agent-workbench/SessionReplayTimeline.vue +18 -9
  162. package/template/apps/web/src/pages/system/components/agent-workbench/TestRunComposer.vue +66 -34
  163. package/template/apps/web/src/pages/system/components/agent-workbench/ToolCatalogPanel.vue +48 -33
  164. package/template/apps/web/src/pages/system/components/agent-workbench/ToolCatalogPanel.webtest.ts +9 -2
  165. package/template/apps/web/src/pages/system/components/agent-workbench/ToolRuleDetail.vue +23 -16
  166. package/template/apps/web/src/pages/system/components/dictionary-bulk/DictionaryBulkJobDetail.vue +10 -7
  167. package/template/apps/web/src/pages/system/components/user-account/RolePermissionSummary.vue +81 -57
  168. package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.vue +6 -4
  169. package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.webtest.ts +20 -4
  170. package/template/apps/web/src/pages/system/resource-config.ts +9 -0
  171. package/template/apps/web/src/pages/work-orders/WorkOrderListPage.vue +1 -1
  172. package/template/apps/web/src/router/disabled-feature-loading.webtest.ts +78 -0
  173. package/template/apps/web/src/router/index.ts +3 -6
  174. package/template/apps/web/src/router/manifest-routes.ts +2 -3
  175. package/template/apps/web/src/router/platform-admin-pages.ts +21 -62
  176. package/template/apps/web/src/router/platform-admin-route-bindings.ts +4 -4
  177. package/template/apps/web/src/services/diagnostics-api.ts +13 -1
  178. package/template/apps/web/src/services/operations-api.ts +4 -0
  179. package/template/apps/web/src/services/platform-settings-api.ts +35 -1
  180. package/template/apps/web/vite.config.ts +9 -3
  181. package/template/deploy/environment/windy.environment.example.yaml +53 -0
  182. package/template/docker-compose.yml +4 -2
  183. package/template/docs/architecture/agent-admin-workbench-implementation.md +1 -1
  184. package/template/docs/architecture/agent-admin-workbench-plan.md +1 -1
  185. package/template/docs/architecture/agent-workflow.md +8 -0
  186. package/template/docs/architecture/durable-jobs.md +17 -0
  187. package/template/docs/architecture/embedded-dsh-integration.md +1 -1
  188. package/template/docs/development/dsh-plugins.md +1 -1
  189. package/template/docs/development/performance-engineering.md +24 -0
  190. package/template/docs/operations/admin-settings-yaml.md +29 -0
  191. package/template/docs/operations/dsh-agent-migration.md +2 -2
  192. package/template/docs/operations/environment-capabilities.md +185 -0
  193. package/template/docs/platform/module-configuration.md +18 -3
  194. package/template/docs/platform/platform-authentication.md +1 -1
  195. package/template/package.json +10 -8
  196. package/template/packages/config/package.json +2 -2
  197. package/template/packages/config/src/definition.ts +14 -4
  198. package/template/packages/config/src/environment-loader.ts +27 -3
  199. package/template/packages/config/src/platform-ai-runtime.test.ts +94 -0
  200. package/template/packages/config/src/platform.ts +5 -2
  201. package/template/packages/database/drizzle/0065_durable_job_lease_snapshot_lookup.sql +1 -0
  202. package/template/packages/database/drizzle/0066_agent_source_sequence_bigint.sql +3 -0
  203. package/template/packages/database/drizzle/0067_media_runtime_access.sql +46 -0
  204. package/template/packages/database/drizzle/meta/0065_snapshot.json +15370 -0
  205. package/template/packages/database/drizzle/meta/0066_snapshot.json +15370 -0
  206. package/template/packages/database/drizzle/meta/0067_snapshot.json +15370 -0
  207. package/template/packages/database/drizzle/meta/_journal.json +21 -0
  208. package/template/packages/database/package.json +3 -3
  209. package/template/packages/database/src/schema/agent-session.ts +4 -3
  210. package/template/packages/database/src/schema/durable-jobs.ts +4 -0
  211. package/template/packages/database/src/schema/index.ts +3 -1
  212. package/template/packages/database/src/schema-durable-recovery.test.ts +21 -1
  213. package/template/packages/database/src/schema-source-sequence.test.ts +24 -0
  214. package/template/packages/example-work-order/package.json +2 -2
  215. package/template/packages/modules/package.json +1 -1
  216. package/template/packages/modules/src/admin-ui-boundary.test.ts +33 -0
  217. package/template/packages/modules/src/composition.ts +11 -28
  218. package/template/packages/modules/src/manifest.ts +2 -0
  219. package/template/packages/modules/src/menu-composition.ts +24 -0
  220. package/template/packages/modules/src/system-admin-routes.ts +2 -2
  221. package/template/packages/modules/src/system-admin-ui-catalog.ts +6 -1
  222. package/template/packages/modules/src/system-ai-api-bindings.ts +0 -16
  223. package/template/packages/modules/src/system-api-control-plane.ts +9 -1
  224. package/template/packages/modules/src/system-api-permissions.ts +7 -0
  225. package/template/packages/modules/src/system-api-watermark.ts +21 -0
  226. package/template/packages/modules/src/system-audit-actions.ts +3 -1
  227. package/template/packages/modules/src/system-features.ts +21 -3
  228. package/template/packages/modules/src/system-media-api-bindings.ts +97 -0
  229. package/template/packages/modules/src/system-module-audit-selection.ts +2 -0
  230. package/template/packages/modules/src/system-module-catalog.test.ts +13 -6
  231. package/template/packages/modules/src/system-module-catalog.ts +6 -241
  232. package/template/packages/modules/src/system-module-definitions.ts +260 -0
  233. package/template/packages/modules/src/system-modules-access.test.ts +2 -0
  234. package/template/packages/modules/src/system-modules.test.ts +7 -1
  235. package/template/packages/modules/src/system-modules.ts +25 -13
  236. package/template/packages/modules/src/system-permissions.ts +12 -0
  237. package/template/packages/server-sdk/package.json +1 -1
  238. package/template/packages/shared/index.ts +3 -0
  239. package/template/packages/shared/package.json +1 -1
  240. package/template/packages/shared/src/environment-assessment.ts +68 -0
  241. package/template/packages/shared/src/environment-policy.ts +205 -0
  242. package/template/packages/shared/src/license-console-capabilities.ts +7 -0
  243. package/template/packages/shared/src/license.ts +1 -0
  244. package/template/packages/shared/src/module-configuration.ts +2 -1
  245. package/template/scripts/operations/collect-device-info.py +230 -0
  246. package/template/apps/server/src/agent/module-ai-port-factory.ts +0 -41
  247. /package/template/apps/server/src/{agent → ai-runtime-security}/execution-grants.ts +0 -0
  248. /package/template/apps/server/src/{agent → ai-runtime-security}/idempotency-key.test.ts +0 -0
  249. /package/template/apps/server/src/{agent → ai-runtime-security}/idempotency-key.ts +0 -0
  250. /package/template/apps/server/src/{agent → ai-runtime-security}/provider-input-governance.test.ts +0 -0
  251. /package/template/apps/server/src/{agent → ai-runtime-security}/provider-input-governance.ts +0 -0
  252. /package/template/apps/server/src/{agent → media-runtime}/required-environment.ts +0 -0
  253. /package/template/apps/server/src/{agent → media-runtime}/runtime-readiness-sources.test.ts +0 -0
package/dist/cli.js CHANGED
@@ -33,7 +33,7 @@ var __toESM = (mod, isNodeMode, target) => {
33
33
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
34
  var __require = import.meta.require;
35
35
 
36
- // ../../node_modules/.bun/cli-width@4.1.0/node_modules/cli-width/index.js
36
+ // ../../node_modules/cli-width/index.js
37
37
  var require_cli_width = __commonJS((exports, module2) => {
38
38
  module2.exports = cliWidth;
39
39
  function normalizeOpts(options) {
@@ -73,7 +73,7 @@ var require_cli_width = __commonJS((exports, module2) => {
73
73
  }
74
74
  });
75
75
 
76
- // ../../node_modules/.bun/mute-stream@3.0.0/node_modules/mute-stream/lib/index.js
76
+ // ../../node_modules/mute-stream/lib/index.js
77
77
  var require_lib = __commonJS((exports, module2) => {
78
78
  var Stream = __require("stream");
79
79
 
@@ -310,6 +310,48 @@ function optionalModule(name, label, description, dependencies) {
310
310
  };
311
311
  }
312
312
 
313
+ // src/optional-module-media.ts
314
+ var mediaOptionalModule = {
315
+ ...optionalModule("system.media-runtime", "\u5A92\u4F53\u8FD0\u884C\u65F6", "\u53D7\u6CBB\u7406\u7684\u6587\u6863\u3001\u8BED\u97F3\u3001\u5D4C\u5165\u4E0E\u516C\u5171\u6587\u4EF6\u5F15\u7528\u3002", ["system", "system.audit", "system.storage"]),
316
+ runtimeActivation: "physical-only",
317
+ fileRoots: [
318
+ "apps/document-server",
319
+ "apps/speech-server",
320
+ "apps/embedding-server",
321
+ "apps/server/src/ai-runtime-readiness",
322
+ "apps/server/src/app/document-output-artifact-bindings.ts",
323
+ "apps/server/src/app/speech-output-artifact-bindings.ts",
324
+ "apps/server/src/ai-artifact",
325
+ "apps/server/src/ai-runtime-security",
326
+ "apps/server/src/media-runtime",
327
+ "apps/server/src/ai-output-artifact",
328
+ "apps/server/src/document",
329
+ "apps/server/src/speech",
330
+ "apps/server/src/embedding",
331
+ "apps/server/src/module-host/ai-operation-port.ts",
332
+ "apps/server/src/module-host/ai-operation-port.test.ts",
333
+ "scripts/provision-speech-readiness.ts",
334
+ "packages/database/src/ai-output-artifacts.test.ts",
335
+ "packages/modules/src/system-media-api-bindings.ts"
336
+ ],
337
+ documentation: [
338
+ "docs/architecture/ai-runtime-readiness.md",
339
+ "docs/platform/document-ai-runtime.md",
340
+ "docs/platform/speech-ai-runtime.md",
341
+ "docs/platform/embedding-ai-runtime.md"
342
+ ],
343
+ schemaFiles: [
344
+ "packages/database/src/schema/embedding-runtime.ts",
345
+ "packages/database/src/schema/ai-output-artifacts.ts"
346
+ ],
347
+ databaseStatementTokens: ["windy_media_runtime_access"],
348
+ databaseTables: [
349
+ "embedding_run_bindings",
350
+ "ai_output_artifact_scopes",
351
+ "ai_output_artifacts"
352
+ ]
353
+ };
354
+
313
355
  // src/optional-module-agent.ts
314
356
  var agentOptionalModule = {
315
357
  ...optionalModule("system.agent", "Agent Tool", "\u53D7\u63A7 Agent \u5DE5\u5177\u5BBF\u4E3B\u3002", [
@@ -317,7 +359,8 @@ var agentOptionalModule = {
317
359
  "system.audit",
318
360
  "system.storage",
319
361
  "system.scheduler",
320
- "system.workflow"
362
+ "system.workflow",
363
+ "system.media-runtime"
321
364
  ]),
322
365
  registryDependencies: [
323
366
  { sourceName: "fflate", name: "fflate", version: "0.8.3" },
@@ -336,15 +379,11 @@ var agentOptionalModule = {
336
379
  fileRoots: [
337
380
  "apps/agent-server",
338
381
  "apps/server/scripts/check-dsh-platform-example.ts",
339
- "apps/document-server",
340
- "apps/speech-server",
341
- "apps/embedding-server",
342
382
  "apps/server/src/agent",
343
383
  "apps/server/src/agent-skills",
344
384
  "deploy/dsh",
345
385
  "apps/server/src/agent-workflow",
346
386
  "apps/server/src/ai-runtime-report",
347
- "apps/server/src/ai-runtime-readiness",
348
387
  "apps/server/src/app/ai-screenshot-intake-binding.ts",
349
388
  "apps/server/src/app/agent-runtime-composition.ts",
350
389
  "apps/server/src/app/workbench-public-origin.ts",
@@ -370,16 +409,7 @@ var agentOptionalModule = {
370
409
  "apps/server/src/app/dsh-workbench.ts",
371
410
  "apps/server/src/app/client-agent-host-auth-binding.ts",
372
411
  "apps/server/src/app/client-agent-workflow-artifact-binding.ts",
373
- "apps/server/src/app/document-output-artifact-bindings.ts",
374
- "apps/server/src/app/speech-output-artifact-bindings.ts",
375
- "apps/server/src/ai-artifact",
376
- "apps/server/src/ai-output-artifact",
377
- "apps/server/src/document",
378
- "apps/server/src/speech",
379
- "apps/server/src/embedding",
380
412
  "apps/server/src/mcp",
381
- "apps/server/src/module-host/ai-operation-port.ts",
382
- "apps/server/src/module-host/ai-operation-port.test.ts",
383
413
  "apps/server/src/module-host/agent-session-memory-port.ts",
384
414
  "apps/server/src/module-host/agent-source-invalidation-port.ts",
385
415
  "apps/server/src/module-host/workflow-input-artifact-port.ts",
@@ -395,12 +425,12 @@ var agentOptionalModule = {
395
425
  "apps/web/src/pages/system/AiRuntimePage.vue",
396
426
  "apps/web/src/pages/system/AgentToolsPage.vue",
397
427
  "apps/web/src/pages/system/SystemAgentSkillsPage.vue",
398
- "apps/web/src/pages/system/SystemAgentSkillsPage.webtest.ts",
399
428
  "apps/web/src/pages/system/components/agent-skills",
400
429
  "apps/web/src/pages/system/composables/useAgentSkillsGovernance.ts",
401
430
  "apps/web/src/pages/system/composables/useAgentWorkflowPresets.ts",
402
431
  "apps/web/src/services/agent-skills-api.ts",
403
432
  "apps/web/src/pages/system/AgentDebugPage.vue",
433
+ "apps/web/src/pages/system/AgentNativePage.vue",
404
434
  "apps/web/src/pages/system/components/agent-workbench",
405
435
  "apps/web/src/pages/system/composables/useAgentWorkbench.ts",
406
436
  "apps/web/src/pages/system/composables/useAgentWorkbench.webtest.ts",
@@ -426,7 +456,6 @@ var agentOptionalModule = {
426
456
  "apps/web/src/services/agent-session-api.webtest.ts",
427
457
  "apps/web/src/services/ai-runtime-api.ts",
428
458
  "apps/web/src/services/ai-runtime-api.webtest.ts",
429
- "scripts/provision-speech-readiness.ts",
430
459
  "packages/modules/src/system-agent-admin-registration.test.ts",
431
460
  "packages/modules/src/system-agent-admin-menus.ts",
432
461
  "packages/modules/src/system-agent-admin-bindings.ts",
@@ -445,7 +474,6 @@ var agentOptionalModule = {
445
474
  "packages/server-sdk/src/workflow-input-artifact-validation.contract.test.ts",
446
475
  "packages/server-sdk/src/workflow-input-materialization-handler.ts",
447
476
  "packages/database/src/agent-runtime-schema.test.ts",
448
- "packages/database/src/ai-output-artifacts.test.ts",
449
477
  "packages/shared/src/agent-admin-catalog.ts",
450
478
  "packages/shared/src/agent-workbench.ts",
451
479
  "packages/shared/src/agent-skills.ts",
@@ -472,9 +500,7 @@ var agentOptionalModule = {
472
500
  "docs/development/dsh-plugins.md",
473
501
  "docs/architecture/agent-runtime-compatibility.md",
474
502
  "docs/architecture/agent-workflow.md",
475
- "docs/architecture/durable-jobs.md",
476
503
  "docs/architecture/ai-egress.md",
477
- "docs/architecture/ai-runtime-readiness.md",
478
504
  "docs/architecture/ai-runtime.md",
479
505
  "docs/architecture/mcp-business-capabilities.md",
480
506
  "docs/operations/agent-session-memory.md",
@@ -492,10 +518,7 @@ var agentOptionalModule = {
492
518
  "docs/platform/agent-tool-sandbox.md",
493
519
  "docs/platform/agent-production-store.md",
494
520
  "docs/platform/agent-run-reattachment.md",
495
- "docs/platform/background-ai-operations.md",
496
- "docs/platform/document-ai-runtime.md",
497
- "docs/platform/speech-ai-runtime.md",
498
- "docs/platform/embedding-ai-runtime.md"
521
+ "docs/platform/background-ai-operations.md"
499
522
  ],
500
523
  schemaFiles: [
501
524
  "packages/database/src/schema/agent-runtime.ts",
@@ -508,9 +531,7 @@ var agentOptionalModule = {
508
531
  "packages/database/src/schema/agent-workflow-effects.ts",
509
532
  "packages/database/src/schema/agent-workflow-evaluation.ts",
510
533
  "packages/database/src/schema/client-agent.ts",
511
- "packages/database/src/schema/client-agent-workflow-artifact.ts",
512
- "packages/database/src/schema/embedding-runtime.ts",
513
- "packages/database/src/schema/ai-output-artifacts.ts"
534
+ "packages/database/src/schema/client-agent-workflow-artifact.ts"
514
535
  ],
515
536
  databaseTables: [
516
537
  "agent_operation_releases",
@@ -526,9 +547,6 @@ var agentOptionalModule = {
526
547
  "client_agent_sessions",
527
548
  "client_agent_receipts",
528
549
  "client_agent_workflow_artifacts",
529
- "embedding_run_bindings",
530
- "ai_output_artifact_scopes",
531
- "ai_output_artifacts",
532
550
  "agent_sessions",
533
551
  "agent_session_events",
534
552
  "agent_session_checkpoints",
@@ -612,6 +630,7 @@ var storageOptionalModule = {
612
630
  // src/optional-module-distribution.ts
613
631
  var optionalModuleManifests = [
614
632
  storageOptionalModule,
633
+ mediaOptionalModule,
615
634
  agentOptionalModule,
616
635
  {
617
636
  ...optionalModule("system.scheduler", "\u4EFB\u52A1\u8C03\u5EA6", "\u4EFB\u52A1\u3001\u91CD\u8BD5\u3001\u5386\u53F2\u548C\u6062\u590D\u3002", ["system", "system.audit"]),
@@ -808,6 +827,9 @@ var platformModules = [
808
827
  {
809
828
  ...module("system.settings", "\u5E73\u53F0\u8BBE\u7F6E", "\u54C1\u724C\u3001Logo \u4E0E\u6C34\u5370\u3002", "recommended", ["system"]),
810
829
  runtimeActivation: "physical-only",
830
+ registryDependencies: [
831
+ { sourceName: "yaml", name: "yaml", version: "2.9.0" }
832
+ ],
811
833
  fileRoots: [
812
834
  "apps/server/src/settings",
813
835
  "apps/web/src/pages/settings",
@@ -822,7 +844,10 @@ var platformModules = [
822
844
  "packages/modules/src/system-api-watermark.ts",
823
845
  "packages/modules/src/system-watermark-policy.test.ts"
824
846
  ],
825
- documentation: ["docs/platform/platform-shell-settings.md"],
847
+ documentation: [
848
+ "docs/platform/platform-shell-settings.md",
849
+ "docs/operations/admin-settings-yaml.md"
850
+ ],
826
851
  databaseTables: [
827
852
  "platform_branding_settings",
828
853
  "platform_watermark_policies"
@@ -1025,6 +1050,170 @@ function module(name, label, description, selection, dependencies = []) {
1025
1050
  };
1026
1051
  }
1027
1052
 
1053
+ // src/environment-profile.ts
1054
+ import { readFileSync, statSync } from "fs";
1055
+
1056
+ // ../shared/src/environment-policy.ts
1057
+ var AI_RUNTIME_KINDS = [
1058
+ "agent",
1059
+ "document",
1060
+ "speech",
1061
+ "embedding"
1062
+ ];
1063
+ function object(value, required, optional = []) {
1064
+ if (!value || typeof value !== "object" || Array.isArray(value))
1065
+ throw new Error("\u73AF\u5883\u9884\u8BBE\u5FC5\u987B\u4E3A\u5BF9\u8C61");
1066
+ const record = value;
1067
+ if (required.some((key) => !Object.hasOwn(record, key)) || Object.keys(record).some((key) => ![...required, ...optional].includes(key)))
1068
+ throw new Error("\u73AF\u5883\u9884\u8BBE\u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5\u6216\u5305\u542B\u672A\u77E5\u5B57\u6BB5");
1069
+ return record;
1070
+ }
1071
+ function strings(value, pattern, max = 128) {
1072
+ if (!Array.isArray(value) || value.length > max || value.some((item) => typeof item !== "string" || item.length > 128 || !pattern.test(item)) || new Set(value).size !== value.length)
1073
+ throw new Error("\u73AF\u5883\u9884\u8BBE\u5217\u8868\u65E0\u6548\u6216\u91CD\u590D");
1074
+ return value;
1075
+ }
1076
+ function parseEnvironmentPolicy(value) {
1077
+ const root = object(value, ["schemaVersion", "build", "features"], ["runtime", "antivirus"]);
1078
+ if (root.schemaVersion !== 1)
1079
+ throw new Error("\u4E0D\u652F\u6301\u7684\u73AF\u5883\u9884\u8BBE\u7248\u672C");
1080
+ const build = object(root.build, ["modules"], ["runtimes"]);
1081
+ const modules = strings(build.modules, /^[a-z][a-z0-9.-]*$/);
1082
+ if (!Array.isArray(root.features) || root.features.length > 128)
1083
+ throw new Error("\u529F\u80FD\u8BC4\u4F30\u914D\u7F6E\u8D85\u51FA\u4E0A\u9650");
1084
+ const features = root.features.map((value2) => {
1085
+ const row = object(value2, ["key", "title", "execution"], [
1086
+ "recommendedEnabled",
1087
+ "supportedPlatforms",
1088
+ "supportedArchitectures",
1089
+ "minMemoryGiB",
1090
+ "requiredEnvironmentKeys"
1091
+ ]);
1092
+ const [key] = strings([row.key], /^[a-z][a-z0-9.-]*$/);
1093
+ if (typeof row.title !== "string" || !row.title.trim() || row.title.length > 120)
1094
+ throw new Error("\u529F\u80FD\u8BC4\u4F30\u6807\u9898\u65E0\u6548");
1095
+ if (!["none", "cpu", "local-gpu", "remote"].includes(String(row.execution)))
1096
+ throw new Error("\u529F\u80FD\u6267\u884C\u65B9\u5F0F\u65E0\u6548");
1097
+ if (row.recommendedEnabled !== undefined && typeof row.recommendedEnabled !== "boolean")
1098
+ throw new Error("\u63A8\u8350\u5F00\u5173\u5FC5\u987B\u4E3A\u5E03\u5C14\u503C");
1099
+ if (row.minMemoryGiB !== undefined && (typeof row.minMemoryGiB !== "number" || !Number.isFinite(row.minMemoryGiB) || row.minMemoryGiB <= 0 || row.minMemoryGiB > 65536))
1100
+ throw new Error("\u6700\u4F4E\u5185\u5B58\u8981\u6C42\u65E0\u6548");
1101
+ return {
1102
+ key,
1103
+ title: row.title,
1104
+ execution: row.execution,
1105
+ ...row.recommendedEnabled === undefined ? {} : { recommendedEnabled: row.recommendedEnabled },
1106
+ ...row.minMemoryGiB === undefined ? {} : { minMemoryGiB: row.minMemoryGiB },
1107
+ ...row.supportedPlatforms === undefined ? {} : {
1108
+ supportedPlatforms: strings(row.supportedPlatforms, /^(linux|darwin|win32)$/)
1109
+ },
1110
+ ...row.supportedArchitectures === undefined ? {} : {
1111
+ supportedArchitectures: strings(row.supportedArchitectures, /^(x64|arm64|arm|ia32|ppc64|s390x|riscv64|loong64)$/)
1112
+ },
1113
+ ...row.requiredEnvironmentKeys === undefined ? {} : {
1114
+ requiredEnvironmentKeys: strings(row.requiredEnvironmentKeys, /^[A-Z][A-Z0-9_]*$/)
1115
+ }
1116
+ };
1117
+ });
1118
+ if (new Set(features.map(({ key }) => key)).size !== features.length)
1119
+ throw new Error("\u529F\u80FD\u8BC4\u4F30 Key \u91CD\u590D");
1120
+ const runtime = Object.hasOwn(root, "runtime") ? parseRuntimeSwitches(root.runtime) : undefined;
1121
+ const runtimes = Object.hasOwn(build, "runtimes") ? parseRuntimeSwitches(build.runtimes) : undefined;
1122
+ for (const kind of AI_RUNTIME_KINDS) {
1123
+ if (runtimes?.[kind] === false && runtime?.[kind] === true)
1124
+ throw new Error(`\u8FD0\u884C\u65F6\u672A\u968F\u5305\u4EA4\u4ED8\uFF1A${kind}`);
1125
+ }
1126
+ const antivirus = Object.hasOwn(root, "antivirus") ? parseAntivirusPolicy(root.antivirus) : undefined;
1127
+ return {
1128
+ schemaVersion: 1,
1129
+ build: { modules, ...runtimes ? { runtimes } : {} },
1130
+ features,
1131
+ ...runtime ? { runtime } : {},
1132
+ ...antivirus ? { antivirus } : {}
1133
+ };
1134
+ }
1135
+ function parseRuntimeSwitches(value) {
1136
+ const switches = object(value, [], [...AI_RUNTIME_KINDS]);
1137
+ const runtime = {};
1138
+ for (const kind of AI_RUNTIME_KINDS) {
1139
+ if (!Object.hasOwn(switches, kind))
1140
+ continue;
1141
+ const enabled = switches[kind];
1142
+ if (typeof enabled !== "boolean")
1143
+ throw new Error("\u8FD0\u884C\u65F6\u5F00\u5173\u5FC5\u987B\u4E3A\u5E03\u5C14\u503C");
1144
+ runtime[kind] = enabled;
1145
+ }
1146
+ return runtime;
1147
+ }
1148
+ function parseAntivirusPolicy(value) {
1149
+ const { packaged, enabled } = object(value, ["packaged", "enabled"]);
1150
+ if (typeof packaged !== "boolean" || typeof enabled !== "boolean")
1151
+ throw new Error("\u75C5\u6BD2\u626B\u63CF\u4EA4\u4ED8\u548C\u542F\u7528\u8BBE\u7F6E\u5FC5\u987B\u4E3A\u5E03\u5C14\u503C");
1152
+ if (enabled && !packaged)
1153
+ throw new Error("\u75C5\u6BD2\u626B\u63CF\u672A\u968F\u9879\u76EE\u4EA4\u4ED8\uFF0C\u4E0D\u80FD\u542F\u7528");
1154
+ return { packaged, enabled };
1155
+ }
1156
+
1157
+ // src/environment-profile.ts
1158
+ function readEnvironmentProfile(path) {
1159
+ const metadata = statSync(path);
1160
+ if (!metadata.isFile() || metadata.size > 128 * 1024)
1161
+ throw new Error("\u73AF\u5883\u9884\u8BBE\u5FC5\u987B\u4E3A\u4E0D\u8D85\u8FC7 128 KiB \u7684\u666E\u901A\u6587\u4EF6");
1162
+ const source = readFileSync(path, "utf8");
1163
+ if (Buffer.byteLength(source, "utf8") > 128 * 1024)
1164
+ throw new Error("\u73AF\u5883\u9884\u8BBE\u6587\u4EF6\u4E0D\u80FD\u8D85\u8FC7 128 KiB");
1165
+ return parseEnvironmentPolicy(Bun.YAML.parse(source));
1166
+ }
1167
+ function environmentProfileModules(profile) {
1168
+ const selected = resolveModuleSelection({
1169
+ requested: profile.build.modules,
1170
+ includeRecommended: false
1171
+ });
1172
+ for (const kind of AI_RUNTIME_KINDS) {
1173
+ const module2 = runtimeDistributionModule(kind);
1174
+ if (!selected.includes(module2) && (profile.build.runtimes?.[kind] === true || profile.runtime?.[kind] === true))
1175
+ throw new Error(`\u8FD0\u884C\u65F6 ${kind} \u9700\u8981\u9009\u62E9 ${module2} \u6A21\u5757`);
1176
+ }
1177
+ return selected;
1178
+ }
1179
+ function runtimeDistributionModule(kind) {
1180
+ return kind === "agent" ? "system.agent" : "system.media-runtime";
1181
+ }
1182
+ function starterEnvironmentProfile(selectedModules, profile) {
1183
+ const source = `# Windy \u73AF\u5883\u4E0E\u529F\u80FD\u9884\u8BBE\uFF08\u9879\u76EE\u6240\u6709\u8005\u7EF4\u62A4\uFF09
1184
+ # build.modules \u4EC5\u5728 create-windy --environment-profile \u9996\u6B21\u751F\u6210\u65F6\u7528\u4E8E\u7269\u7406\u88C1\u526A\u3002
1185
+ # \u5FC5\u9700\u6A21\u5757\u4E0E\u4F9D\u8D56\u4F1A\u81EA\u52A8\u8865\u9F50\uFF1B\u8FD9\u91CC\u8BB0\u5F55\u5B9E\u9645\u4EA4\u4ED8\u96C6\u5408\u3002
1186
+ # \u4FEE\u6539\u5DF2\u6709\u9879\u76EE\u7684\u6B64\u6587\u4EF6\u4E0D\u4F1A\u5220\u9664\u5DF2\u6784\u5EFA\u4EE3\u7801\uFF0C\u540E\u7EED\u88C1\u526A\u9700\u8D70\u6B63\u5F0F\u751F\u6210/\u5347\u7EA7\u6D41\u7A0B\u3002
1187
+ # features \u662F\u73AF\u5883\u8BC4\u4F30\u5EFA\u8BAE\uFF0C\u4E0D\u81EA\u52A8\u5F00\u5173\u529F\u80FD\uFF1B\u7BA1\u7406\u5458\u786E\u8BA4\u540E\u901A\u8FC7 Feature \u7BA1\u7406\u5E94\u7528\u3002
1188
+ # Server \u4EC5\u5728 WINDY_ENVIRONMENT_PROFILE_FILE \u663E\u5F0F\u6307\u5411\u6B64\u6587\u4EF6\u65F6\u52A0\u8F7D\u8BC4\u4F30\u7B56\u7565\u3002
1189
+ # CPU OCR \u9009\u62E9 system.media-runtime\uFF0C\u901A\u8FC7 build.runtimes \u548C runtime \u542F\u7528 document\u3002
1190
+ # PLATFORM_ENABLE_AI_RUNTIME \u63A7\u5236\u5171\u4EAB\u8FD0\u884C\u65F6\uFF0C\u4E0D\u80FD\u5F53\u4F5C\u7EAF Agent \u5F00\u5173\u3002
1191
+ # \u75C5\u6BD2\u626B\u63CF\u662F\u72EC\u7ACB\u53EF\u9009\u80FD\u529B\uFF0C\u6309\u660E\u786E\u9884\u8BBE\u9009\u62E9\u626B\u63CF\u6216\u4E0D\u626B\u63CF\uFF0C\u4E0E GPU \u72B6\u6001\u65E0\u5173\u3002
1192
+ ${Bun.YAML.stringify({
1193
+ schemaVersion: 1,
1194
+ build: {
1195
+ modules: [...selectedModules],
1196
+ ...profile?.build.runtimes ? { runtimes: profile.build.runtimes } : {}
1197
+ },
1198
+ ...profile?.runtime ? { runtime: profile.runtime } : {},
1199
+ ...profile?.antivirus ? { antivirus: profile.antivirus } : {},
1200
+ features: profile?.features ?? []
1201
+ })}
1202
+ # features \u793A\u4F8B\uFF08\u66FF\u6362\u4E3A\u672C\u9879\u76EE Manifest \u4E2D\u771F\u5B9E\u5B58\u5728\u7684 Feature Key \u540E\u518D\u586B\u5199\uFF09\uFF1A
1203
+ # - key: example.cpu-ocr
1204
+ # title: CPU \u6587\u6863\u8BC6\u522B
1205
+ # execution: cpu
1206
+ # recommendedEnabled: true
1207
+ # supportedPlatforms: [linux]
1208
+ # supportedArchitectures: [arm64, x64]
1209
+ # requiredEnvironmentKeys: [DOCUMENT_SERVER_URL]
1210
+ # \u8BBE\u5907\u63A2\u6D4B\u3001\u8F6F\u4EF6\u9002\u914D\u3001\u670D\u52A1 readiness \u4E0E\u8BC6\u522B\u8D28\u91CF\u662F\u4E0D\u540C\u8BC1\u636E\uFF1B\u672A\u77E5\u4E0D\u7B49\u4E8E\u4E0D\u652F\u6301\u3002
1211
+ `;
1212
+ if (Buffer.byteLength(source, "utf8") > 128 * 1024)
1213
+ throw new Error("\u751F\u6210\u7684\u73AF\u5883\u9884\u8BBE\u4E0D\u80FD\u8D85\u8FC7 128 KiB\uFF0C\u8BF7\u7F29\u51CF\u529F\u80FD\u8BC4\u4F30\u6761\u76EE");
1214
+ return source;
1215
+ }
1216
+
1028
1217
  // src/arguments.ts
1029
1218
  function parseArguments(args) {
1030
1219
  if (args.includes("--help") || args.includes("-h"))
@@ -1042,13 +1231,19 @@ function parseArguments(args) {
1042
1231
  if (includeAll && noOptionalModules) {
1043
1232
  throw new Error("--all-modules \u4E0D\u80FD\u4E0E --no-optional-modules \u540C\u65F6\u4F7F\u7528");
1044
1233
  }
1234
+ const environmentProfilePath = readValue(args, "--environment-profile");
1235
+ if (args.includes("--environment-profile") && (!environmentProfilePath || environmentProfilePath.startsWith("--")))
1236
+ throw new Error("--environment-profile \u9700\u8981 YAML \u6216 JSON \u6587\u4EF6\u8DEF\u5F84");
1237
+ if (environmentProfilePath && (requestedModules.length || includeAll || noOptionalModules))
1238
+ throw new Error("--environment-profile \u4E0D\u80FD\u4E0E\u6A21\u5757\u9009\u62E9\u53C2\u6570\u540C\u65F6\u4F7F\u7528");
1239
+ const environmentProfile = environmentProfilePath ? readEnvironmentProfile(environmentProfilePath) : undefined;
1045
1240
  const projectLicense = readProjectLicense(args);
1046
- const selectedModules = resolveModuleSelection({
1241
+ const selectedModules = environmentProfile ? environmentProfileModules(environmentProfile) : resolveModuleSelection({
1047
1242
  requested: requestedModules,
1048
1243
  includeRecommended: !noOptionalModules,
1049
1244
  includeAll
1050
1245
  });
1051
- const explicitSelection = requestedModules.length > 0 || includeAll || noOptionalModules;
1246
+ const explicitSelection = requestedModules.length > 0 || includeAll || noOptionalModules || Boolean(environmentProfile);
1052
1247
  return {
1053
1248
  projectName,
1054
1249
  targetDirectory: resolve(process.cwd(), projectName),
@@ -1057,6 +1252,7 @@ function parseArguments(args) {
1057
1252
  verify: args.includes("--verify"),
1058
1253
  keepOnError: args.includes("--keep-on-error"),
1059
1254
  selectedModules,
1255
+ ...environmentProfile ? { environmentProfile } : {},
1060
1256
  includeExample: selectedModules.includes("work-order"),
1061
1257
  includeOxc: !args.includes("--no-oxc"),
1062
1258
  projectLicense,
@@ -1068,6 +1264,7 @@ function parseArguments(args) {
1068
1264
  function readProjectName(args) {
1069
1265
  const flagsWithValues = new Set([
1070
1266
  "--profile",
1267
+ "--environment-profile",
1071
1268
  "--module",
1072
1269
  "--license",
1073
1270
  "--license-holder"
@@ -1117,6 +1314,7 @@ var helpText = `create-windy <\u9879\u76EE\u540D> [\u9009\u9879]
1117
1314
 
1118
1315
  \u9009\u9879\uFF1A
1119
1316
  --profile private-enterprise \u5355\u7EC4\u7EC7\u3001\u5355\u79DF\u6237\u3001\u79C1\u6709\u90E8\u7F72 profile
1317
+ --environment-profile <\u6587\u4EF6> \u4ECE YAML/JSON build.modules \u9009\u62E9\u6A21\u5757\u5E76\u53D1\u653E\u73AF\u5883\u8BC4\u4F30\u5EFA\u8BAE
1120
1318
  --skip-install \u751F\u6210\u6216\u66F4\u65B0\u65F6\u4E0D\u6267\u884C bun install
1121
1319
  --verify \u5B89\u88C5\u540E\u6267\u884C lint\uFF08\u82E5\u6709\uFF09\u3001typecheck\u3001test \u548C build
1122
1320
  --keep-on-error \u5931\u8D25\u65F6\u4FDD\u7559\u65B0\u751F\u6210\u76EE\u5F55\u7528\u4E8E\u8BCA\u65AD
@@ -1585,6 +1783,7 @@ var ignoredFiles = new Set([
1585
1783
  ".windy/update-state.json"
1586
1784
  ]);
1587
1785
  var projectOwnedRoots = [
1786
+ "windy.environment.yaml",
1588
1787
  "windy.modules.json",
1589
1788
  "module-schema.lineages.json",
1590
1789
  "module-schema",
@@ -1735,7 +1934,7 @@ import {
1735
1934
  fchmodSync,
1736
1935
  fstatSync as fstatSync2,
1737
1936
  fsyncSync as fsyncSync2,
1738
- readFileSync,
1937
+ readFileSync as readFileSync2,
1739
1938
  writeSync
1740
1939
  } from "fs";
1741
1940
  import { open as open3 } from "fs/promises";
@@ -2189,7 +2388,7 @@ function readFileAt(parent) {
2189
2388
  const info = fstatSync2(fd);
2190
2389
  if (!info.isFile())
2191
2390
  throw new Error(`\u4E8B\u52A1\u76EE\u6807\u4E0D\u662F\u666E\u901A\u6587\u4EF6\uFF1A${parent.leaf}`);
2192
- return readFileSync(fd);
2391
+ return readFileSync2(fd);
2193
2392
  } finally {
2194
2393
  closeSync2(fd);
2195
2394
  }
@@ -3100,16 +3299,26 @@ var textExtensions = new Set([
3100
3299
  ".yaml",
3101
3300
  ".yml"
3102
3301
  ]);
3103
- async function materializeSelectedModules(targetDirectory, selectedNames) {
3104
- selectedModuleManifests(selectedNames);
3302
+ async function materializeSelectedModules(targetDirectory, selectedNames, excludedRuntimeApps = []) {
3303
+ const retainedDependencies = new Set(selectedModuleManifests(selectedNames).flatMap(({ registryDependencies }) => registryDependencies).flatMap(({ sourceName, name }) => [sourceName, name]));
3105
3304
  const selected = new Set(selectedNames);
3106
3305
  const removed = moduleDistributionCatalog.filter(({ name }) => !selected.has(name));
3107
- const removedPackageNames = await readRemovedPackageNames(targetDirectory, removed.flatMap(({ packages }) => packages));
3306
+ const removedPackageNames = await readRemovedPackageNames(targetDirectory, [
3307
+ ...removed.flatMap(({ packages }) => packages),
3308
+ ...excludedRuntimeApps
3309
+ ]);
3108
3310
  for (const dependency of removed.flatMap(({ registryDependencies }) => registryDependencies)) {
3109
- removedPackageNames.add(dependency.sourceName);
3110
- removedPackageNames.add(dependency.name);
3111
- }
3112
- await stripModuleBlocks(targetDirectory, selected);
3311
+ if (!retainedDependencies.has(dependency.sourceName))
3312
+ removedPackageNames.add(dependency.sourceName);
3313
+ if (!retainedDependencies.has(dependency.name))
3314
+ removedPackageNames.add(dependency.name);
3315
+ }
3316
+ const agent = moduleDistributionCatalog.find(({ name }) => name === "system.agent");
3317
+ const includeAgentUi = selected.has("system.agent") && !excludedRuntimeApps.includes("apps/agent-server");
3318
+ const sourceTags = new Set(selected);
3319
+ if (includeAgentUi)
3320
+ sourceTags.add("system.agent-ui");
3321
+ await stripModuleBlocks(targetDirectory, sourceTags);
3113
3322
  await pruneDatabaseArtifacts(targetDirectory, [
3114
3323
  ...removed.flatMap(({ databaseTables, transientDatabaseTables }) => [
3115
3324
  ...databaseTables,
@@ -3117,12 +3326,16 @@ async function materializeSelectedModules(targetDirectory, selectedNames) {
3117
3326
  ]),
3118
3327
  ...moduleDistributionCatalog.filter(({ databaseLineageOwner }) => databaseLineageOwner === "module").flatMap(({ databaseTables }) => databaseTables)
3119
3328
  ], removed.flatMap(({ databaseStatementTokens }) => databaseStatementTokens));
3120
- const removedRoots = collapseRemovalRoots(removed.flatMap((manifest) => [
3121
- ...manifest.packages,
3122
- ...manifest.fileRoots,
3123
- ...manifest.documentation,
3124
- ...manifest.schemaFiles
3125
- ]));
3329
+ const removedRoots = collapseRemovalRoots([
3330
+ ...removed.flatMap((manifest) => [
3331
+ ...manifest.packages,
3332
+ ...manifest.fileRoots,
3333
+ ...manifest.documentation,
3334
+ ...manifest.schemaFiles
3335
+ ]),
3336
+ ...excludedRuntimeApps,
3337
+ ...!includeAgentUi && agent ? agent.fileRoots.filter((path) => path.startsWith("apps/web/src/") || path === "packages/modules/src/system-agent-admin-menus.ts" || path === "packages/modules/src/system-agent-admin-registration.test.ts") : []
3338
+ ]);
3126
3339
  await Promise.all([...new Set(removedRoots)].map((path) => rm3(join8(targetDirectory, path), { recursive: true, force: true })));
3127
3340
  await pruneWorkspaceReferences(targetDirectory, removedPackageNames, removedRoots);
3128
3341
  }
@@ -3178,7 +3391,10 @@ async function readRemovedPackageNames(root, roots) {
3178
3391
  const names = new Set;
3179
3392
  for (const packageRoot2 of roots) {
3180
3393
  try {
3181
- const value = JSON.parse(await readFile6(join8(root, packageRoot2, "package.json"), "utf8"));
3394
+ const directory = join8(root, packageRoot2);
3395
+ if (!(await lstat3(directory)).isDirectory())
3396
+ continue;
3397
+ const value = JSON.parse(await readFile6(join8(directory, "package.json"), "utf8"));
3182
3398
  if (value.name)
3183
3399
  names.add(value.name);
3184
3400
  } catch (error) {
@@ -3211,7 +3427,12 @@ async function pruneWorkspaceReferences(root, removedPackages, removedRoots) {
3211
3427
  continue;
3212
3428
  const source = await readFile6(path, "utf8");
3213
3429
  const output = source.split(`
3214
- `).filter((line) => !removedRoots.some((root2) => line.includes(root2))).join(`
3430
+ `).map((line) => {
3431
+ for (const removed of removedRoots) {
3432
+ line = line.replaceAll(`,"--filter","!./${removed}"`, "");
3433
+ }
3434
+ return line;
3435
+ }).filter((line) => !removedRoots.some((root2) => line.includes(root2))).join(`
3215
3436
  `);
3216
3437
  if (output !== source)
3217
3438
  await writeFile5(path, output);
@@ -3327,15 +3548,12 @@ var starterServerBuild = ` build:
3327
3548
  function agentWorkflowServerEnvironment(includeAgent) {
3328
3549
  return includeAgent ? " WINDY_AGENT_WORKFLOW_MODEL_PRICING_JSON: ${WINDY_AGENT_WORKFLOW_MODEL_PRICING_JSON:-}" : "";
3329
3550
  }
3330
- function optionalComposeVolumes(includeAgent, includeSearch, includeStorage) {
3551
+ function optionalComposeVolumes(include, includeSearch, includeStorage) {
3331
3552
  return [
3332
- ...includeAgent ? [
3333
- " agent-run-data:",
3334
- " tool-sandbox-ipc:",
3335
- " document-run-data:",
3336
- " speech-run-data:",
3337
- " embedding-run-data:"
3338
- ] : [],
3553
+ ...include("agent") ? [" agent-run-data:", " tool-sandbox-ipc:"] : [],
3554
+ ...include("document") ? [" document-run-data:"] : [],
3555
+ ...include("speech") ? [" speech-run-data:"] : [],
3556
+ ...include("embedding") ? [" embedding-run-data:"] : [],
3339
3557
  ...includeSearch ? [" opensearch-data:"] : [],
3340
3558
  ...includeStorage ? [" server-storage-data:"] : []
3341
3559
  ].join(`
@@ -3442,7 +3660,8 @@ ${starterServerBuild}
3442
3660
  condition: service_completed_successfully
3443
3661
  server:
3444
3662
  condition: service_started
3445
- document-server:
3663
+ `;
3664
+ var documentComposeService = ` document-server:
3446
3665
  profiles: ["document"]
3447
3666
  build:
3448
3667
  context: .
@@ -3466,7 +3685,8 @@ ${starterServerBuild}
3466
3685
  server:
3467
3686
  condition: service_started
3468
3687
 
3469
- speech-server:
3688
+ `;
3689
+ var speechComposeService = ` speech-server:
3470
3690
  profiles: ["speech"]
3471
3691
  build:
3472
3692
  context: .
@@ -3498,7 +3718,8 @@ ${starterServerBuild}
3498
3718
  server:
3499
3719
  condition: service_started
3500
3720
 
3501
- embedding-server:
3721
+ `;
3722
+ var embeddingComposeService = ` embedding-server:
3502
3723
  profiles: ["embedding"]
3503
3724
  build:
3504
3725
  context: .
@@ -3606,10 +3827,10 @@ __STORAGE_SERVER_ENV__
3606
3827
  WINDY_CONTAINERIZED: "1"
3607
3828
  BUN_CONFIG_NO_CLEAR_TERMINAL_ON_RELOAD: "true"
3608
3829
  WINDY_SYSTEM_USAGE_PATHS: /app/data/audit
3609
- PLATFORM_ENABLE_AI: \${PLATFORM_ENABLE_AI:-false}
3610
- AGENT_SERVER_URL: http://agent-server:8080
3830
+ PLATFORM_ENABLE_AI_RUNTIME: \${PLATFORM_ENABLE_AI_RUNTIME:-\${PLATFORM_ENABLE_AI:-false}}
3831
+ __ENVIRONMENT_PROFILE_ENV__
3832
+ __AGENT_SERVER_ENV__
3611
3833
  WINDY_PUBLIC_BASE_URL: "\${WINDY_PUBLIC_BASE_URL:-}"
3612
- AGENT_TOOL_HOST_URL: http://server:9747
3613
3834
  __TOOL_SANDBOX_SERVER_ENV__
3614
3835
  AGENT_SERVER_INTERNAL_TOKEN: \${AGENT_SERVER_INTERNAL_TOKEN:-}
3615
3836
  __AGENT_WORKFLOW_SERVER_ENV__
@@ -3628,6 +3849,7 @@ __OPENSEARCH_SERVER_ENV__
3628
3849
  - ./apps/server:/app/apps/server
3629
3850
  - ./packages:/app/packages
3630
3851
  - server-audit-data:/app/data/audit
3852
+ __ENVIRONMENT_PROFILE_VOLUME__
3631
3853
  __STORAGE_SERVER_VOLUME__
3632
3854
  __TOOL_SANDBOX_SERVER_VOLUME__
3633
3855
  depends_on:
@@ -3670,17 +3892,30 @@ volumes:
3670
3892
  server-audit-data:
3671
3893
  __OPTIONAL_VOLUMES__
3672
3894
  `;
3673
- function starterComposeFor(includeAgent, includeSearch = true, includeStorage = true) {
3895
+ function starterComposeFor(includeAgent, includeSearch = true, includeStorage = true, profile, includeMedia = includeAgent) {
3896
+ const include = (kind) => (kind === "agent" ? includeAgent : includeMedia) && profile?.build?.[kind] !== false;
3897
+ const starts = (kind) => include(kind) && profile?.runtime?.[kind] === true;
3898
+ const services = [
3899
+ ["agent", agentComposeService],
3900
+ ["document", documentComposeService],
3901
+ ["speech", speechComposeService],
3902
+ ["embedding", embeddingComposeService]
3903
+ ];
3904
+ const aiServices = services.filter(([kind]) => include(kind)).map(([kind, source]) => starts(kind) ? source.replaceAll(` profiles: ["${kind}"]
3905
+ `, "") : source).join("");
3906
+ const hasProfile = !!(profile?.build || profile?.runtime || profile?.mountEnvironment);
3907
+ const startAi = services.some(([kind]) => starts(kind));
3674
3908
  return starterComposeBase.replace(` web:
3675
- `, `${includeAgent ? agentComposeService : ""} web:
3676
- `).replace("__TOOL_SANDBOX_SERVER_ENV__", includeAgent ? " TOOL_SANDBOX_SOCKET: /run/windy-tool-sandbox/worker.sock" : "").replace("__AGENT_WORKFLOW_SERVER_ENV__", agentWorkflowServerEnvironment(includeAgent)).replace("__TOOL_SANDBOX_SERVER_VOLUME__", includeAgent ? " - tool-sandbox-ipc:/run/windy-tool-sandbox" : "").replace("__STORAGE_SERVER_ENV__", includeStorage ? " WINDY_STORAGE_ROOT: /app/data/storage" : "").replace("__STORAGE_SERVER_VOLUME__", includeStorage ? " - server-storage-data:/app/data/storage" : "").replace("__OPENSEARCH_SERVER_ENV__", includeSearch ? ` OPENSEARCH_URL: \${OPENSEARCH_URL:-}
3909
+ `, `${aiServices} web:
3910
+ `).replace("__ENVIRONMENT_PROFILE_ENV__", hasProfile ? " WINDY_ENVIRONMENT_PROFILE_FILE: /app/windy.environment.yaml" : "").replace("__ENVIRONMENT_PROFILE_VOLUME__", hasProfile ? " - ./windy.environment.yaml:/app/windy.environment.yaml:ro" : "").replace("__AGENT_SERVER_ENV__", include("agent") ? ` AGENT_SERVER_URL: http://agent-server:8080
3911
+ AGENT_TOOL_HOST_URL: http://server:9747` : "").replace("${PLATFORM_ENABLE_AI:-false}", startAi ? "${PLATFORM_ENABLE_AI:-true}" : "${PLATFORM_ENABLE_AI:-false}").replace("${DOCUMENT_SERVER_URL:-}", starts("document") ? "${DOCUMENT_SERVER_URL:-http://document-server:8090}" : "${DOCUMENT_SERVER_URL:-}").replace("${SPEECH_SERVER_URL:-}", starts("speech") ? "${SPEECH_SERVER_URL:-http://speech-server:8091}" : "${SPEECH_SERVER_URL:-}").replace("${EMBEDDING_SERVER_URL:-}", starts("embedding") ? "${EMBEDDING_SERVER_URL:-http://embedding-server:8092}" : "${EMBEDDING_SERVER_URL:-}").replace("__TOOL_SANDBOX_SERVER_ENV__", include("agent") ? " TOOL_SANDBOX_SOCKET: /run/windy-tool-sandbox/worker.sock" : "").replace("__AGENT_WORKFLOW_SERVER_ENV__", agentWorkflowServerEnvironment(include("agent"))).replace("__TOOL_SANDBOX_SERVER_VOLUME__", include("agent") ? " - tool-sandbox-ipc:/run/windy-tool-sandbox" : "").replace("__STORAGE_SERVER_ENV__", includeStorage ? " WINDY_STORAGE_ROOT: /app/data/storage" : "").replace("__STORAGE_SERVER_VOLUME__", includeStorage ? " - server-storage-data:/app/data/storage" : "").replace("__OPENSEARCH_SERVER_ENV__", includeSearch ? ` OPENSEARCH_URL: \${OPENSEARCH_URL:-}
3677
3912
  OPENSEARCH_USERNAME: \${OPENSEARCH_USERNAME:-}
3678
3913
  OPENSEARCH_PASSWORD: \${OPENSEARCH_PASSWORD:-}
3679
- OPENSEARCH_WORK_ORDER_INDEX: \${OPENSEARCH_WORK_ORDER_INDEX:-windy-work-orders-v1}` : "").replace("__OPENSEARCH_SERVICE__", includeSearch ? openSearchComposeService : "").replace("__OPTIONAL_VOLUMES__", optionalComposeVolumes(includeAgent, includeSearch, includeStorage));
3914
+ OPENSEARCH_WORK_ORDER_INDEX: \${OPENSEARCH_WORK_ORDER_INDEX:-windy-work-orders-v1}` : "").replace("__OPENSEARCH_SERVICE__", includeSearch ? openSearchComposeService : "").replace("__OPTIONAL_VOLUMES__", optionalComposeVolumes(include, includeSearch, includeStorage));
3680
3915
  }
3681
3916
  var starterCompose = starterComposeFor(true);
3682
3917
  // src/runtime-environment-example.ts
3683
- function environmentExample(includeExample, includeAgent = true, includeSearch = true, includeStorage = true) {
3918
+ function environmentExample(includeExample, includeMedia = true, includeSearch = true, includeStorage = true, excludedRuntimes = []) {
3684
3919
  return `# \u590D\u5236\u4E3A .env \u540E\u586B\u5199\u3002\u4E0D\u8981\u63D0\u4EA4\u771F\u5B9E\u5BC6\u7801\u3002
3685
3920
  POSTGRES_PASSWORD=
3686
3921
  WINDY_BOOTSTRAP_ADMIN_PASSWORD=
@@ -3689,11 +3924,14 @@ ${includeStorage ? `
3689
3924
  # \u5982\u9700\u72EC\u7ACB\u6302\u8F7D\u62A5\u544A\u6682\u5B58\u76EE\u5F55\uFF0C\u8BF7\u540C\u65F6\u4FEE\u6539 Compose volume\u3002
3690
3925
  # WINDY_REPORT_STAGING_ROOT=/app/data/storage/report-staging
3691
3926
  ` : ""}
3692
- ${includeAgent ? `
3693
- # Agent profile \u4EC5\u7531\u670D\u52A1\u7AEF Agent Runtime \u8BFB\u53D6\uFF1B\u4E0D\u8981\u4F7F\u7528 VITE_ \u524D\u7F00\u3002
3694
- PLATFORM_ENABLE_AI=false
3927
+ ${includeMedia ? `
3928
+ # \u5171\u4EAB AI Runtime \u5305\u62EC CPU OCR \u7684 Document Runtime\uFF1B\u4E0D\u662F\u4EC5\u63A7\u5236\u5927\u6A21\u578B\u3002
3929
+ # \u65E7\u540D PLATFORM_ENABLE_AI \u4ECD\u517C\u5BB9\uFF0C\u5EFA\u8BAE\u53EA\u914D\u7F6E\u5176\u4E2D\u4E00\u4E2A\uFF1B\u4E0D\u8981\u4F7F\u7528 VITE_ \u524D\u7F00\u3002
3930
+ # \u7559\u7A7A\u65F6\u4F7F\u7528\u751F\u6210\u7684 Compose \u9ED8\u8BA4\u503C\uFF1B\u9700\u8981\u8986\u76D6\u65F6\u518D\u53D6\u6D88\u6CE8\u91CA\u3002
3931
+ # PLATFORM_ENABLE_AI_RUNTIME=false
3695
3932
  AGENT_SERVER_INTERNAL_TOKEN=
3696
- AGENT_INTERNAL_OUTPUT_ENCRYPTION_SECRET=
3933
+ AI_ARTIFACT_ENCRYPTION_SECRET=
3934
+ ${!excludedRuntimes.includes("agent") ? `AGENT_INTERNAL_OUTPUT_ENCRYPTION_SECRET=
3697
3935
  # Model \u8282\u70B9\u542F\u7528\u524D\u5FC5\u987B\u586B\u5165\u7531\u8FD0\u7EF4\u51BB\u7ED3\u7684 deployment/model \u4EF7\u683C\u76EE\u5F55\uFF1B\u7F3A\u5931\u65F6 readiness \u660E\u786E unavailable\u3002
3698
3936
  WINDY_AGENT_WORKFLOW_MODEL_PRICING_JSON=
3699
3937
  # \u975E Compose \u90E8\u7F72\u9700\u6307\u5411 Windy Server\uFF1B\u957F\u671F Session \u7684\u6A21\u578B\u8C03\u7528\u4F1A fail closed\u3002
@@ -3710,12 +3948,16 @@ AGENT_DSH_USER_EXTENSIONS=1
3710
3948
  # \u6784\u5EFA\u671F\u955C\u50CF\u6E90\u4ECD\u9700\u7B26\u5408\u9501\u6587\u4EF6\u7684hash\uFF1B\u8FD0\u884C\u65F6\u4E0D\u4E0B\u8F7D\u539F\u751F\u4F9D\u8D56\u3002
3711
3949
  # WINDY_AGENT_NATIVE_REGISTRY=https://registry.npmjs.org
3712
3950
  # WINDY_PYPI_INDEX_URL=https://pypi.org/simple
3713
- AI_ARTIFACT_ENCRYPTION_SECRET=
3714
3951
  # \u5F00\u53D1\u7559\u7A7A\u65F6\u5DE5\u4F5C\u53F0\u8DDF\u968F\u6D4F\u89C8\u5668\u540C\u6E90 Host\uFF0C\u652F\u6301 Web \u7AEF\u53E3\u548C\u52A8\u6001\u5C40\u57DF\u7F51\u5730\u5740\u3002
3715
3952
  # HTTPS \u53CD\u5411\u4EE3\u7406\u6216\u751F\u4EA7 Client Agent Session \u5FC5\u987B\u586B\u5199\u7528\u6237\u5B9E\u9645\u8BBF\u95EE\u7684 Web origin\u3002
3716
3953
  # WINDY_PUBLIC_BASE_URL=https://windy.example
3717
3954
  WINDY_PUBLIC_BASE_URL=
3718
- DOCUMENT_SERVER_URL=
3955
+ ` : ""}
3956
+ ${!excludedRuntimes.includes("document") || !excludedRuntimes.includes("speech") ? `AI_OUTPUT_ARTIFACT_REFERENCE_SECRET=
3957
+ DOCUMENT_OUTPUT_ARTIFACT_SINK_IDENTITY=
3958
+ DOCUMENT_OUTPUT_ARTIFACT_SINK_RELEASE_DIGEST=
3959
+ ` : ""}
3960
+ ${!excludedRuntimes.includes("document") ? `DOCUMENT_SERVER_URL=
3719
3961
  DOCUMENT_ARTIFACT_URL=
3720
3962
  DOCUMENT_OUTPUT_ARTIFACT_URL=
3721
3963
  DOCUMENT_ADAPTER_URL=
@@ -3724,12 +3966,10 @@ DOCUMENT_DEPLOYMENT_KEY=
3724
3966
  DOCUMENT_DEPLOYMENT_IDENTITY=
3725
3967
  DOCUMENT_RELEASE_DIGEST=
3726
3968
  DOCUMENT_OFFERS_DIGEST=
3727
- AI_OUTPUT_ARTIFACT_REFERENCE_SECRET=
3728
- DOCUMENT_OUTPUT_ARTIFACT_SINK_IDENTITY=
3729
- DOCUMENT_OUTPUT_ARTIFACT_SINK_RELEASE_DIGEST=
3730
3969
  # DOCUMENT_ADAPTER_CAPABILITIES=document.ocr,document.layout,document.table
3731
3970
  # DOCUMENT_MAX_UNITS=500
3732
- SPEECH_SERVER_URL=
3971
+ ` : ""}
3972
+ ${!excludedRuntimes.includes("speech") ? `SPEECH_SERVER_URL=
3733
3973
  SPEECH_ARTIFACT_URL=
3734
3974
  SPEECH_OUTPUT_ARTIFACT_URL=
3735
3975
  SPEECH_ADAPTER_URL=
@@ -3748,7 +3988,8 @@ SPEECH_OFFERS_DIGEST=
3748
3988
  # SPEECH_MAX_QUEUE_DEPTH=0
3749
3989
  # SPEECH_READINESS_TIMEOUT_SECONDS=5
3750
3990
  # SPEECH_READINESS_TTL_SECONDS=60
3751
- EMBEDDING_SERVER_URL=
3991
+ ` : ""}
3992
+ ${!excludedRuntimes.includes("embedding") ? `EMBEDDING_SERVER_URL=
3752
3993
  EMBEDDING_ARTIFACT_URL=
3753
3994
  EMBEDDING_RUN_ARTIFACT_URL=
3754
3995
  EMBEDDING_ADAPTER_URL=
@@ -3775,7 +4016,8 @@ EMBEDDING_RUN_ENCRYPTION_SECRET=
3775
4016
  # EMBEDDING_READINESS_TIMEOUT_SECONDS=5
3776
4017
  # EMBEDDING_READINESS_TTL_SECONDS=60
3777
4018
  # EMBEDDING_RUN_TERMINAL_TTL_SECONDS=604800
3778
- QWEN_API_URL=
4019
+ ` : ""}
4020
+ ${!excludedRuntimes.includes("agent") ? `QWEN_API_URL=
3779
4021
  QWEN_API_KEY=
3780
4022
  QWEN_MODEL=
3781
4023
  # QWEN_STRUCTURED_OUTPUT_MODE=json_schema
@@ -3800,6 +4042,7 @@ AI_PROVIDER_HTTPS_HOST_ALLOWLIST=
3800
4042
  # AI_PROVIDER_MAX_ARTIFACTS=16
3801
4043
  # AI_PROVIDER_MAX_PIXELS_PER_ARTIFACT=100000000
3802
4044
  ` : ""}
4045
+ ` : ""}
3803
4046
 
3804
4047
  ${includeSearch ? `# OpenSearch profile \u9ED8\u8BA4\u4E0D\u542F\u52A8\uFF1Bbun run dev:search \u4F1A\u53EA\u5411 Server \u6CE8\u5165\u5BB9\u5668\u5185\u5730\u5740\u3002
3805
4048
  OPENSEARCH_URL=
@@ -3818,7 +4061,7 @@ OPENSEARCH_PASSWORD=
3818
4061
  `;
3819
4062
  }
3820
4063
  // src/starter-docs-readme.ts
3821
- function starterDocsReadme(includeOfflineLicense = true, includeAgent = true, includeOperations = true, includeStorage = true) {
4064
+ function starterDocsReadme(includeOfflineLicense = true, includeAgent = true, includeOperations = true, includeStorage = true, includeMedia = includeAgent) {
3822
4065
  return `# Windy Starter \u6587\u6863
3823
4066
 
3824
4067
  \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
@@ -3830,20 +4073,22 @@ License \u7B7E\u53D1\u4E2D\u5FC3\u3001\u72EC\u7ACB\u7B7E\u540D\u670D\u52A1\u3001
3830
4073
  - \u6570\u636E\u5E93\uFF1A\u4E1A\u52A1\u8868\u4F7F\u7528 [Migration Bundle \u4E0E\u72EC\u7ACB Drizzle lineage](./architecture/migration-bundle.md)\u3002
3831
4074
  - \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
3832
4075
  - Web \u4E1A\u52A1\u9875\u9762\uFF1A[project-owned \u4E1A\u52A1 Web \u8DEF\u7531](./development/business-web-routes.md)\u3002
4076
+ - \u73AF\u5883\u9002\u914D\uFF1A[\u73AF\u5883\u80FD\u529B\u8BC4\u4F30\u4E0E\u6784\u5EFA\u9884\u8BBE](./operations/environment-capabilities.md)\u3002
3833
4077
  - \u90E8\u7F72\u671F\u6A21\u5757\u7BA1\u7406\uFF1A[\u6A21\u5757\u751F\u547D\u5468\u671F](./development/module-lifecycle.md)\u3002
3834
4078
  - \u4E1A\u52A1\u6A21\u5757\u5F00\u53D1\uFF1A[windy-business-module Skill](../.agents/skills/windy-business-module/SKILL.md)\u3002
3835
4079
  ${includeStorage ? "- \u5927\u62A5\u544A\u5BFC\u5165\uFF1A[\u53D7\u6CBB\u7406\u7684\u5927\u62A5\u544A\u6682\u5B58](./architecture/report-staging.md)\u3002" : ""}
3836
4080
  ${includeOfflineLicense ? "- License \u6D88\u8D39\u7AEF\uFF1A[\u79BB\u7EBF\u6FC0\u6D3B\u4E0E\u5B89\u88C5\u534F\u8BAE](./license/license-offline-activation-code.md)\u4E0E[\u624B\u673A\u626B\u7801\u914D\u5BF9\u6FC0\u6D3B](./license/license-pairing-activation.md)\uFF1B\u8FD0\u884C\u65F6\u80FD\u529B\u7531 npm \u4F9D\u8D56 `@southwind-ai/license` \u63D0\u4F9B\u3002" : ""}
3837
4081
  - \u8FD0\u7EF4\uFF1A[\u53EF\u89C2\u6D4B\u6027](./operations/observability.md)${includeOperations ? " \u4E0E [\u8FD0\u7EF4\u603B\u89C8](./platform/operations-overview.md)" : ""}\u3002
3838
4082
  - \u4EA4\u4ED8\uFF1A[\u6309 registry digest \u4E0E OCI blob \u5DEE\u91CF\u53D1\u5E03](./operations/incremental-delivery.md)\u3002
3839
- ${includeAgent ? "- Agent\u3001\u591A\u6A21\u6001 Runtime \u4E0E MCP\uFF1A[Agent Harness](./architecture/agent-harness.md)\u3001[Agent Runtime \u63A5\u5165](./platform/agent-runtime.md)\u3001[Session/Memory \u8FD0\u7EF4](./operations/agent-session-memory.md)\u3001[Session \u6545\u969C\u901F\u67E5](./operations/agent-session-troubleshooting.md)\u3001[Agent Workflow](./architecture/agent-workflow.md)\u3001[Workflow \u6062\u590D\u8FD0\u7EF4](./operations/agent-workflow-recovery.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" : ""}
4083
+ ${includeAgent ? "- Agent \u4E0E MCP\uFF1A[Agent Harness](./architecture/agent-harness.md)\u3001[Agent Runtime \u63A5\u5165](./platform/agent-runtime.md)\u3001[Session/Memory \u8FD0\u7EF4](./operations/agent-session-memory.md)\u3001[Session \u6545\u969C\u901F\u67E5](./operations/agent-session-troubleshooting.md)\u3001[Agent Workflow](./architecture/agent-workflow.md)\u3001[Workflow \u6062\u590D\u8FD0\u7EF4](./operations/agent-workflow-recovery.md)\u3001[\u53D7\u63A7\u5DE5\u5177](./platform/agent-tools.md)\u4E0E[MCP \u4E1A\u52A1\u80FD\u529B](./architecture/mcp-business-capabilities.md)\u3002" : ""}
4084
+ ${includeMedia ? "- \u5A92\u4F53\u8FD0\u884C\u65F6\uFF1A[Document/OCR](./platform/document-ai-runtime.md)\u3001[Speech](./platform/speech-ai-runtime.md)\u4E0E [Embedding](./platform/embedding-ai-runtime.md)\u3002" : ""}
3840
4085
  - \u6570\u636E\u8FC1\u79FB\uFF1A[\u65E7\u7CFB\u7EDF\u6570\u636E\u8FC1\u79FB\u7B56\u7565](./operations/legacy-data-migration.md)\u3002
3841
4086
  `;
3842
4087
  }
3843
4088
  // src/project-files.ts
3844
- function starterReadme(projectName, includeExample = true, includeOxc = true, projectLicense = "UNLICENSED", selectedModules = []) {
4089
+ function starterReadme(projectName, includeExample = true, includeOxc = true, projectLicense = "UNLICENSED", selectedModules = [], includeAgentRuntime = selectedModules.includes("system.agent"), excludedRuntimes = []) {
3845
4090
  const selected = new Set(selectedModules);
3846
- const includeAgent = selected.has("system.agent");
4091
+ const includeAgent = includeAgentRuntime;
3847
4092
  const includeSearch = selected.has("system.search");
3848
4093
  const toggleableModule = moduleDistributionCatalog.find(({ name, selection, runtimeActivation }) => selected.has(name) && selection !== "required" && runtimeActivation === "toggle" && !moduleDistributionCatalog.some((candidate) => selected.has(candidate.name) && candidate.dependencies.includes(name)))?.name;
3849
4094
  const moduleActivationCommands = toggleableModule ? [
@@ -3919,7 +4164,7 @@ ${includeAgent ? `
3919
4164
  ## DSH Agent profile
3920
4165
 
3921
4166
  Agent Runtime \u9ED8\u8BA4\u4E0D\u542F\u52A8\u3002\u5148\u5728\u4EC5\u4F9B\u672C\u673A\u4F7F\u7528\u7684 \`.env\` \u4E2D\u8BBE\u7F6E
3922
- \`PLATFORM_ENABLE_AI=true\`\u3001\`AGENT_SERVER_INTERNAL_TOKEN\`\u3001\`QWEN_API_URL\`\u3001
4167
+ \`PLATFORM_ENABLE_AI_RUNTIME=true\`\u3001\`AGENT_SERVER_INTERNAL_TOKEN\`\u3001\`QWEN_API_URL\`\u3001
3923
4168
  \`QWEN_API_KEY\` \u4E0E \`QWEN_MODEL\`\uFF0C\u518D\u8FD0\u884C\uFF1A
3924
4169
 
3925
4170
  \`\`\`bash
@@ -3931,39 +4176,28 @@ bun run dev:agent
3931
4176
  \u73AF\u5883\u6216\u8FD4\u56DE\u7ED9\u5BA2\u6237\u7AEF\u3002\u516C\u7F51 HTTPS Endpoint \u7684\u4E3B\u673A\u5FC5\u987B\u663E\u5F0F\u52A0\u5165
3932
4177
  \`AI_PROVIDER_HTTPS_HOST_ALLOWLIST\`\u3002\u8FD0\u884C\u4E0E\u6A21\u5757
3933
4178
  \u63A5\u5165\u7EC6\u8282\u89C1 [Agent Runtime \u63A5\u5165](./docs/platform/agent-runtime.md)\u53CA
3934
- [DSH \u90E8\u7F72\u4E0E\u8FC1\u79FB](./docs/operations/dsh-agent-migration.md)\u3002Agent Server \u4EE5 Node 24.7+
4179
+ [DSH \u90E8\u7F72\u4E0E\u8FC1\u79FB](./docs/operations/dsh-agent-migration.md)\u3002Agent Server \u4EE5 Node 24.9+
3935
4180
  \u6267\u884C\uFF0C\u4F7F\u7528\u65B0 SQLite \u6570\u636E\u8DEF\u5F84\uFF1B\u539F\u751F\u5DE5\u4F5C\u53F0\u9700\u8981 Linux \u9694\u79BB\u80FD\u529B\u3002\u5168\u5C40\u5BB9\u91CF\u9ED8\u8BA4 1 \u65F6\u4E3A\u65E2\u6709
3936
4181
  \u8C03\u7528\u94FE\u9884\u7559\uFF0C\u5DE5\u4F5C\u53F0\u4F1A\u660E\u786E\u663E\u793A\u5BB9\u91CF\u672A\u5F00\u653E\u3002\u4E2A\u4EBA\u6269\u5C55\u9ED8\u8BA4\u5F00\u653E\uFF0C\u7531\u90E8\u7F72\u5F00\u5173\u63A7\u5236\u3002
3937
4182
 
3938
- Document Runtime \u540C\u6837\u9ED8\u8BA4\u5173\u95ED\u3002\u914D\u7F6E\u81F3\u5C11 32 \u5B57\u7B26\u7684
3939
- \`AI_ARTIFACT_ENCRYPTION_SECRET\`\u3001\u672C\u5730\u6216\u5185\u7F51 \`DOCUMENT_ADAPTER_URL\` \u53CA\u5176
3940
- capability \u540E\u8FD0\u884C\uFF1A
3941
-
3942
- \`\`\`bash
3943
- bun run dev:document
3944
- \`\`\`
3945
-
3946
- \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
3947
- \u4EFB\u610F\u516C\u7F51 URL\u3002\u672A\u914D\u7F6E Document Adapter \u65F6\u4EC5\u5BF9\u5E94 Operation \u8FD4\u56DE\u7A33\u5B9A unavailable\uFF0C
3948
- \u666E\u901A\u4E1A\u52A1\u4E0E\u6587\u672C Agent \u4E0D\u53D7\u5F71\u54CD\u3002\u8BE6\u89C1
3949
- [Document AI Runtime](./docs/platform/document-ai-runtime.md)\u3002
3950
-
3951
- \u6279\u91CF\u8BED\u97F3\u8F6C\u5199\u4F7F\u7528\u72EC\u7ACB Speech profile\u3002\u914D\u7F6E\u672C\u5730/\u5185\u7F51
3952
- \`SPEECH_ADAPTER_URL\` \u4E0E capability \u540E\u8FD0\u884C\uFF1A
3953
-
3954
- \`\`\`bash
3955
- bun run dev:speech
3956
- \`\`\`
3957
-
3958
- 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
3959
- Artifact\uFF1B\u5B9E\u65F6 ASR\u3001TTS \u548C Dictation \u5C1A\u672A\u5F00\u653E\u3002\u8BE6\u89C1
3960
- [Speech AI Runtime](./docs/platform/speech-ai-runtime.md)\u3002
3961
-
3962
4183
  \u5F00\u53D1\u4E0E\u5BA1\u8BA1\u7EDF\u4E00\u4F7F\u7528 Windy \u4E3B\u7AD9\uFF1A\`/admin/system/ai-runtime\` \u63D0\u4F9B\u53D7\u6743\u9650\u4FDD\u62A4\u7684 Run\u3001
3963
4184
  Provider Attempt \u4E0E\u8131\u654F Trajectory\uFF1B\u4E1A\u52A1\u6A21\u5757\u53EF\u901A\u8FC7 \`registerAgentSessionProviders()\`
3964
4185
  \u6CE8\u518C\u5386\u53F2\u4F1A\u8BDD\u53EA\u8BFB\u6295\u5F71\u3002\u4E24\u8005\u5747\u590D\u7528\u5F53\u524D\u767B\u5F55\u8EAB\u4EFD\u3001Manifest Feature/RBAC \u4E0E Audit\uFF0C\u4E0D\u521B\u5EFA
3965
4186
  \u7B2C\u4E8C\u79CD\u8C03\u8BD5 Token\uFF0C\u4E5F\u4E0D\u4F1A\u5C55\u793A\u8F93\u5165\u8F93\u51FA\u6B63\u6587\u3001Tool \u53C2\u6570\u6216 Provider \u539F\u59CB\u54CD\u5E94\u3002
3966
4187
  ` : ""}
4188
+ ${selected.has("system.media-runtime") ? `
4189
+ ## \u5A92\u4F53\u8FD0\u884C\u65F6
4190
+
4191
+ \u5728 \`windy.environment.yaml\` \u4E2D\u8BBE\u7F6E\u5DF2\u4EA4\u4ED8\u80FD\u529B\u7684 \`runtime\` \u5F00\u5173\u3002\u4FEE\u6539\u540E\u91CD\u542F Server\u3002
4192
+ \u53EA\u5728\u672C\u5730 \`.env\` \u914D\u7F6E\u5171\u4EAB\u5185\u90E8\u4EE4\u724C \`AGENT_SERVER_INTERNAL_TOKEN\`\u3001\u81F3\u5C11 32 \u5B57\u7B26\u7684
4193
+ \`AI_ARTIFACT_ENCRYPTION_SECRET\` \u548C\u5404 Adapter \u7684\u8FDE\u63A5\u4FE1\u606F\u3002\u4E1A\u52A1\u4EC5\u4F20\u5E73\u53F0\u7B7E\u53D1\u7684 ArtifactRef\u3002
4194
+ ${!excludedRuntimes.includes("document") ? "- Document/OCR\uFF1A\u914D\u7F6E `DOCUMENT_ADAPTER_URL`\uFF0C\u4F7F\u7528 `bun run dev:document`\u3002\u8BE6\u89C1 [Document AI Runtime](./docs/platform/document-ai-runtime.md)\u3002" : ""}
4195
+ ${!excludedRuntimes.includes("speech") ? "- Speech\uFF1A\u914D\u7F6E `SPEECH_ADAPTER_URL`\uFF0C\u4F7F\u7528 `bun run dev:speech`\u3002\u8BE6\u89C1 [Speech AI Runtime](./docs/platform/speech-ai-runtime.md)\u3002" : ""}
4196
+ ${!excludedRuntimes.includes("embedding") ? "- Embedding\uFF1A\u914D\u7F6E `EMBEDDING_ADAPTER_URL`\u3002\u8BE6\u89C1 [Embedding AI Runtime](./docs/platform/embedding-ai-runtime.md)\u3002" : ""}
4197
+
4198
+ \u4EE5 YAML \u663E\u5F0F\u5F00\u542F\u5E76\u751F\u6210\u7684\u670D\u52A1\u968F\u9ED8\u8BA4 Compose \u542F\u52A8\u3002\u8BE6\u89C1
4199
+ [\u73AF\u5883\u9884\u8BBE\u4E0E\u4EA4\u4ED8\u8FB9\u754C](./docs/operations/environment-capabilities.md)\u3002
4200
+ ` : ""}
3967
4201
  ${includeSearch ? `
3968
4202
  ## OpenSearch \u641C\u7D22 profile
3969
4203
 
@@ -4114,7 +4348,8 @@ var migrationCliDependencies = {
4114
4348
  "drizzle-orm": "^0.45.2",
4115
4349
  pg: "^8.22.0"
4116
4350
  };
4117
- function createStarterPackage(source, projectName, includeOxc = true, projectLicense = "UNLICENSED", selectedModules = [], generatorVersion) {
4351
+ function createStarterPackage(source, projectName, includeOxc = true, projectLicense = "UNLICENSED", selectedModules = [], generatorVersion, disabledRuntimes = []) {
4352
+ const includeRuntime = (kind) => selectedModules.includes(kind === "agent" ? "system.agent" : "system.media-runtime") && !disabledRuntimes.includes(kind);
4118
4353
  const { "lint-staged": sourceLintStaged, ...sourceWithoutLintStaged } = source;
4119
4354
  const devDependencies = { ...source.devDependencies };
4120
4355
  if (!includeOxc) {
@@ -4129,7 +4364,11 @@ function createStarterPackage(source, projectName, includeOxc = true, projectLic
4129
4364
  private: true,
4130
4365
  license: projectLicense,
4131
4366
  packageManager: "bun@1.3.14",
4132
- engines: { ...source.engines, bun: ">=1.3.14", node: ">=24.7.0" },
4367
+ engines: {
4368
+ ...source.engines,
4369
+ bun: ">=1.3.14",
4370
+ node: selectedModules.includes("system.agent") ? ">=24.9.0" : ">=24.7.0"
4371
+ },
4133
4372
  ...includeOxc ? { "lint-staged": sourceLintStaged ?? defaultLintStaged } : {},
4134
4373
  scripts: {
4135
4374
  ...includeOxc ? {
@@ -4141,24 +4380,32 @@ function createStarterPackage(source, projectName, includeOxc = true, projectLic
4141
4380
  ...selectedModules.includes("system.search") ? {
4142
4381
  "dev:search": "OPENSEARCH_URL=http://opensearch:9200 docker compose --profile search up --build"
4143
4382
  } : {},
4144
- ...selectedModules.includes("system.agent") ? {
4383
+ ...includeRuntime("agent") ? {
4145
4384
  "dev:agent": "bun run docker:sync && docker compose --profile agent up --build",
4146
4385
  "check:agent": "bun run --cwd apps/agent-server typecheck",
4147
- "test:agent": "bun run --cwd apps/agent-server test",
4148
- "dev:speech": "PLATFORM_ENABLE_AI=true SPEECH_SERVER_URL=http://speech-server:8091 docker compose --profile agent --profile speech up --build",
4386
+ "test:agent": "bun run --cwd apps/agent-server test"
4387
+ } : {},
4388
+ ...includeRuntime("speech") ? {
4389
+ "dev:speech": "PLATFORM_ENABLE_AI_RUNTIME=true SPEECH_SERVER_URL=http://speech-server:8091 docker compose --profile speech up --build",
4149
4390
  "speech:pins": "bun run scripts/provision-speech-readiness.ts"
4150
4391
  } : {},
4392
+ ...includeRuntime("document") ? {
4393
+ "dev:document": "PLATFORM_ENABLE_AI_RUNTIME=true DOCUMENT_SERVER_URL=http://document-server:8090 docker compose --profile document up --build"
4394
+ } : {},
4395
+ ...includeRuntime("embedding") ? {
4396
+ "dev:embedding": "PLATFORM_ENABLE_AI_RUNTIME=true EMBEDDING_SERVER_URL=http://embedding-server:8092 docker compose --profile embedding up --build"
4397
+ } : {},
4151
4398
  ...generatorVersion ? { windy: "create-windy" } : {},
4152
4399
  "dev:web": "bun run --cwd apps/web dev",
4153
4400
  "dev:server": "bun run --cwd apps/server dev",
4154
- build: selectedModules.includes("system.agent") ? "bun run --cwd apps/agent-server build && bun run --cwd apps/web build" : "bun run --cwd apps/web build",
4401
+ build: includeRuntime("agent") ? "bun run --cwd apps/agent-server build && bun run --cwd apps/web build" : "bun run --cwd apps/web build",
4155
4402
  "db:generate": "drizzle-kit generate --config drizzle.config.ts",
4156
4403
  "db:migrate": "drizzle-kit migrate --config drizzle.config.ts",
4157
4404
  "generate:crud": "bun run packages/crud-generator/src/cli.ts",
4158
4405
  typecheck: [
4159
4406
  "bun run --cwd apps/web typecheck",
4160
4407
  "bun run --cwd apps/server typecheck",
4161
- ...selectedModules.includes("system.agent") ? ["bun run --cwd apps/agent-server typecheck"] : [],
4408
+ ...includeRuntime("agent") ? ["bun run --cwd apps/agent-server typecheck"] : [],
4162
4409
  "bun run --cwd packages/ai-client typecheck",
4163
4410
  "bun run --cwd packages/jobs typecheck",
4164
4411
  "bun run --cwd packages/shared typecheck",
@@ -4172,7 +4419,7 @@ function createStarterPackage(source, projectName, includeOxc = true, projectLic
4172
4419
  format: "oxfmt",
4173
4420
  "format:staged": "lint-staged"
4174
4421
  } : {},
4175
- test: "bun test && bun run --cwd apps/web test" + (selectedModules.includes("system.agent") ? " && bun run test:agent" : "")
4422
+ test: "bun test && bun run --cwd apps/web test" + (includeRuntime("agent") ? " && bun run test:agent" : "")
4176
4423
  },
4177
4424
  devDependencies: {
4178
4425
  ...migrationCliDependencies,
@@ -4198,7 +4445,9 @@ var templateRootFiles = [
4198
4445
  "package.json",
4199
4446
  "scripts/notification-postgres-regression.ts",
4200
4447
  "scripts/lib/postgres-regression.ts",
4201
- "scripts/provision-speech-readiness.ts"
4448
+ "scripts/provision-speech-readiness.ts",
4449
+ "scripts/operations/collect-device-info.py",
4450
+ "deploy/environment/windy.environment.example.yaml"
4202
4451
  ];
4203
4452
  var templateDirectories = [
4204
4453
  ".agents/skills/windy-business-module",
@@ -4223,6 +4472,7 @@ var templateDirectories = [
4223
4472
  "packages/storage"
4224
4473
  ];
4225
4474
  var templateDocumentationFiles = [
4475
+ "docs/operations/environment-capabilities.md",
4226
4476
  "docs/operations/legacy-data-migration.md",
4227
4477
  "docs/adr/0003-agent-session-event-source.md",
4228
4478
  "docs/adr/0004-embedded-dsh-runtime.md",
@@ -4261,6 +4511,7 @@ var templateDocumentationFiles = [
4261
4511
  "docs/development/custom-error-pages.md",
4262
4512
  "docs/development/custom-login-page.md",
4263
4513
  "docs/development/module-lifecycle.md",
4514
+ "docs/development/performance-engineering.md",
4264
4515
  "docs/development/ui-component-gallery.md",
4265
4516
  "docs/license/license-offline-activation-code.md",
4266
4517
  "docs/license/license-pairing-activation.md",
@@ -4295,6 +4546,7 @@ var templateDocumentationFiles = [
4295
4546
  "docs/platform/organization-membership.md",
4296
4547
  "docs/platform/platform-authentication.md",
4297
4548
  "docs/platform/platform-shell-settings.md",
4549
+ "docs/operations/admin-settings-yaml.md",
4298
4550
  "docs/platform/scheduled-task-executor.md",
4299
4551
  "docs/platform/speech-ai-runtime.md",
4300
4552
  "docs/platform/system-crud-api.md",
@@ -4369,7 +4621,7 @@ function parseGenerationProvenance(content) {
4369
4621
  }
4370
4622
  return parsed;
4371
4623
  }
4372
- async function writeGenerationProvenance(targetDirectory, metadata, profile, selectedModules, includeOxc, legalLicense) {
4624
+ async function writeGenerationProvenance(targetDirectory, metadata, profile, selectedModules, includeOxc, legalLicense, environmentProfile) {
4373
4625
  const requested = selectedModuleManifests(selectedModules);
4374
4626
  const versions = new Map(metadata.modules.map((item) => [item.name, item]));
4375
4627
  const modules = requested.map(({ name }) => {
@@ -4388,6 +4640,7 @@ async function writeGenerationProvenance(targetDirectory, metadata, profile, sel
4388
4640
  codeQuality: includeOxc ? "oxc" : "none"
4389
4641
  },
4390
4642
  legalLicense,
4643
+ ...environmentProfile ? { environmentProfile: parseEnvironmentPolicy(environmentProfile) } : {},
4391
4644
  appliedRecipes: [],
4392
4645
  managedFilesManifest: ".windy/files.json",
4393
4646
  choices: {
@@ -4411,6 +4664,13 @@ function isGenerationProvenance(value) {
4411
4664
  return false;
4412
4665
  const metadata = value;
4413
4666
  const toolchain = metadata.toolchain;
4667
+ if (Object.hasOwn(metadata, "environmentProfile")) {
4668
+ try {
4669
+ parseEnvironmentPolicy(metadata.environmentProfile);
4670
+ } catch {
4671
+ return false;
4672
+ }
4673
+ }
4414
4674
  const choices = metadata.choices;
4415
4675
  return typeof metadata.windyVersion === "string" && metadata.profile === "private-enterprise" && toolchain?.runtime === "bun" && (toolchain?.codeQuality === "oxc" || toolchain?.codeQuality === "none") && (metadata.legalLicense === "UNLICENSED" || metadata.legalLicense === "MIT" || metadata.legalLicense === "Apache-2.0") && Array.isArray(metadata.appliedRecipes) && metadata.appliedRecipes.every((item) => typeof item === "string") && metadata.managedFilesManifest === ".windy/files.json" && typeof choices?.includeExample === "boolean" && typeof choices.includeOxc === "boolean";
4416
4676
  }
@@ -4608,26 +4868,46 @@ async function generateProject(input) {
4608
4868
  const includeOxc = input.includeOxc ?? true;
4609
4869
  const projectLicense = input.projectLicense ?? "UNLICENSED";
4610
4870
  const templateMetadata = await readTemplateMetadata(input.sourceRoot);
4611
- const selectedModules = input.selectedModules ?? templateMetadata.modules.filter(({ name }) => requestedIncludeExample || name !== "work-order").map(({ name }) => name);
4871
+ if (input.environmentProfile?.antivirus?.packaged) {
4872
+ throw new Error("Windy \u901A\u7528\u751F\u6210\u5668\u4E0D\u5305\u542B\u75C5\u6BD2\u626B\u63CF\u5668\uFF0C\u8BF7\u7531\u9879\u76EE\u4EA4\u4ED8\u626B\u63CF\u5668\u540E\u914D\u7F6E\u73AF\u5883\u9884\u8BBE");
4873
+ }
4874
+ const selectedModules = (input.environmentProfile ? environmentProfileModules(input.environmentProfile) : input.selectedModules) ?? templateMetadata.modules.filter(({ name }) => requestedIncludeExample || name !== "work-order").map(({ name }) => name);
4612
4875
  const includeExample = selectedModules.includes("work-order");
4613
4876
  for (const relativePath2 of generationTemplatePaths(true, includeOxc)) {
4614
4877
  await copyTree(await resolveTemplateSource(input.sourceRoot, relativePath2), join12(input.targetDirectory, relativePath2));
4615
4878
  }
4616
- await materializeSelectedModules(input.targetDirectory, selectedModules);
4879
+ const disabledRuntimes = AI_RUNTIME_KINDS.filter((kind) => !selectedModules.includes(runtimeDistributionModule(kind)) || input.environmentProfile?.build.runtimes?.[kind] === false);
4880
+ await materializeSelectedModules(input.targetDirectory, selectedModules, disabledRuntimes.flatMap((kind) => kind === "agent" ? [
4881
+ "apps/agent-server",
4882
+ "apps/server/scripts/check-dsh-platform-example.ts"
4883
+ ] : [`apps/${kind}-server`]));
4884
+ await mkdir3(join12(input.targetDirectory, "apps/server/src/system"), {
4885
+ recursive: true
4886
+ });
4887
+ await writeFile9(join12(input.targetDirectory, "apps/server/src/system/ai-runtime-build.ts"), `// Generated by create-windy from the runtime delivery selection.
4888
+ ` + `import type { AiRuntimeKind } from "@southwind-ai/shared";
4889
+
4890
+ ` + `export const excludedAiRuntimes: readonly AiRuntimeKind[] = ${JSON.stringify(disabledRuntimes)};
4891
+ `);
4617
4892
  await materializeRegistryDependencies(input.targetDirectory, selectedModuleManifests(selectedModules).flatMap(({ registryDependencies }) => registryDependencies));
4618
4893
  const packagePath = join12(input.targetDirectory, "package.json");
4619
4894
  const packageJson = JSON.parse(await readFile10(packagePath, "utf8"));
4620
- await writeFile9(packagePath, `${JSON.stringify(createStarterPackage(packageJson, input.projectName, includeOxc, projectLicense, selectedModules, templateMetadata.generatorVersion), null, 2)}
4895
+ await writeFile9(packagePath, `${JSON.stringify(createStarterPackage(packageJson, input.projectName, includeOxc, projectLicense, selectedModules, templateMetadata.generatorVersion, disabledRuntimes), null, 2)}
4621
4896
  `);
4622
- await writeFile9(join12(input.targetDirectory, "docker-compose.yml"), starterComposeFor(selectedModules.includes("system.agent"), selectedModules.includes("system.search"), selectedModules.includes("system.storage")));
4623
- await writeFile9(join12(input.targetDirectory, ".env.example"), environmentExample(includeExample, selectedModules.includes("system.agent"), selectedModules.includes("system.search"), selectedModules.includes("system.storage")));
4624
- await writeFile9(join12(input.targetDirectory, "README.md"), starterReadme(input.projectName, includeExample, includeOxc, projectLicense, selectedModules));
4625
- await writeFile9(join12(input.targetDirectory, "docs/README.md"), starterDocsReadme(selectedModules.includes("system.license"), selectedModules.includes("system.agent"), selectedModules.includes("system.operations"), selectedModules.includes("system.storage")));
4897
+ await writeFile9(join12(input.targetDirectory, "docker-compose.yml"), starterComposeFor(selectedModules.includes("system.agent"), selectedModules.includes("system.search"), selectedModules.includes("system.storage"), {
4898
+ build: input.environmentProfile?.build.runtimes,
4899
+ runtime: input.environmentProfile?.runtime,
4900
+ mountEnvironment: true
4901
+ }, selectedModules.includes("system.media-runtime")));
4902
+ await writeFile9(join12(input.targetDirectory, ".env.example"), environmentExample(includeExample, selectedModules.includes("system.media-runtime"), selectedModules.includes("system.search"), selectedModules.includes("system.storage"), disabledRuntimes));
4903
+ await writeFile9(join12(input.targetDirectory, "README.md"), starterReadme(input.projectName, includeExample, includeOxc, projectLicense, selectedModules, !disabledRuntimes.includes("agent"), disabledRuntimes));
4904
+ await writeFile9(join12(input.targetDirectory, "docs/README.md"), starterDocsReadme(selectedModules.includes("system.license"), !disabledRuntimes.includes("agent"), selectedModules.includes("system.operations"), selectedModules.includes("system.storage"), selectedModules.includes("system.media-runtime")));
4905
+ await writeFile9(join12(input.targetDirectory, "windy.environment.yaml"), starterEnvironmentProfile(selectedModules, input.environmentProfile));
4626
4906
  await reflectCodeQualityChoice(input.targetDirectory, includeOxc);
4627
4907
  await applyWindyBaseLicense(input.targetDirectory);
4628
4908
  await applyProjectLicense(input.targetDirectory, projectLicense, input.licenseHolder);
4629
4909
  await prepareCustomerDockerfiles(input.targetDirectory);
4630
- await writeGenerationProvenance(input.targetDirectory, templateMetadata, input.profile ?? starterProfile.key, selectedModules, includeOxc, projectLicense);
4910
+ await writeGenerationProvenance(input.targetDirectory, templateMetadata, input.profile ?? starterProfile.key, selectedModules, includeOxc, projectLicense, input.environmentProfile);
4631
4911
  await writeInitialProjectModuleRegistry(input.targetDirectory, selectedModules);
4632
4912
  await rm4(join12(input.targetDirectory, "bun.lock"), { force: true });
4633
4913
  await writeManagedFilesManifest(input.targetDirectory, templateMetadata.generatorVersion);
@@ -4741,6 +5021,7 @@ async function createProject(options, dependencies = {}) {
4741
5021
  includeExample: options.includeExample,
4742
5022
  includeOxc: options.includeOxc,
4743
5023
  selectedModules: options.selectedModules,
5024
+ environmentProfile: options.environmentProfile,
4744
5025
  projectLicense: options.projectLicense,
4745
5026
  licenseHolder: options.licenseHolder,
4746
5027
  profile: options.profile
@@ -4814,7 +5095,7 @@ function isMissing6(error) {
4814
5095
  import { createInterface as createInterface2 } from "readline/promises";
4815
5096
  import { stdin, stdout } from "process";
4816
5097
 
4817
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/key.js
5098
+ // ../../node_modules/@inquirer/core/dist/lib/key.js
4818
5099
  var keybindings = ["emacs", "vim"];
4819
5100
  var keybindingLookup = new Set(keybindings);
4820
5101
  function isKeybinding(value) {
@@ -4831,7 +5112,7 @@ var isDownKey = (key, keybindings2 = []) => key.name === "down" || keybindings2.
4831
5112
  var isSpaceKey = (key) => key.name === "space";
4832
5113
  var isNumberKey = (key) => "1234567890".includes(key.name);
4833
5114
  var isEnterKey = (key) => key.name === "enter" || key.name === "return";
4834
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/errors.js
5115
+ // ../../node_modules/@inquirer/core/dist/lib/errors.js
4835
5116
  class AbortPromptError extends Error {
4836
5117
  name = "AbortPromptError";
4837
5118
  message = "Prompt was aborted";
@@ -4857,10 +5138,10 @@ class HookError extends Error {
4857
5138
  class ValidationError extends Error {
4858
5139
  name = "ValidationError";
4859
5140
  }
4860
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-state.js
5141
+ // ../../node_modules/@inquirer/core/dist/lib/use-state.js
4861
5142
  import { AsyncResource as AsyncResource2 } from "async_hooks";
4862
5143
 
4863
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/hook-engine.js
5144
+ // ../../node_modules/@inquirer/core/dist/lib/hook-engine.js
4864
5145
  import { AsyncLocalStorage, AsyncResource } from "async_hooks";
4865
5146
  var hookStorage = new AsyncLocalStorage;
4866
5147
  function createStore(rl) {
@@ -4876,16 +5157,14 @@ function createStore(rl) {
4876
5157
  }
4877
5158
  function withHooks(rl, cb) {
4878
5159
  const store = createStore(rl);
4879
- return hookStorage.run(store, () => {
4880
- function cycle(render) {
4881
- store.handleChange = () => {
4882
- store.index = 0;
4883
- render();
4884
- };
4885
- store.handleChange();
4886
- }
4887
- return cb(cycle);
4888
- });
5160
+ function cycle(render) {
5161
+ store.handleChange = () => {
5162
+ store.index = 0;
5163
+ render();
5164
+ };
5165
+ store.handleChange();
5166
+ }
5167
+ return hookStorage.run(store, () => cb(cycle));
4889
5168
  }
4890
5169
  function getStore() {
4891
5170
  const store = hookStorage.getStore();
@@ -4965,15 +5244,20 @@ var effectScheduler = {
4965
5244
  }
4966
5245
  };
4967
5246
 
4968
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-state.js
5247
+ // ../../node_modules/@inquirer/core/dist/lib/use-state.js
4969
5248
  function isFactory(value) {
4970
5249
  return typeof value === "function";
4971
5250
  }
5251
+ function isReducer(value) {
5252
+ return typeof value === "function";
5253
+ }
4972
5254
  function useState(defaultValue) {
4973
5255
  return withPointer((pointer) => {
4974
5256
  const setState = AsyncResource2.bind(function setState2(newValue) {
4975
- if (pointer.get() !== newValue) {
4976
- pointer.set(newValue);
5257
+ const currentValue = pointer.get();
5258
+ const nextValue = isReducer(newValue) ? newValue(currentValue) : newValue;
5259
+ if (!Object.is(currentValue, nextValue)) {
5260
+ pointer.set(nextValue);
4977
5261
  handleChange();
4978
5262
  }
4979
5263
  });
@@ -4986,7 +5270,7 @@ function useState(defaultValue) {
4986
5270
  });
4987
5271
  }
4988
5272
 
4989
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-effect.js
5273
+ // ../../node_modules/@inquirer/core/dist/lib/use-effect.js
4990
5274
  function useEffect(cb, depArray) {
4991
5275
  withPointer((pointer) => {
4992
5276
  const oldDeps = pointer.get();
@@ -4998,10 +5282,10 @@ function useEffect(cb, depArray) {
4998
5282
  });
4999
5283
  }
5000
5284
 
5001
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/theme.js
5285
+ // ../../node_modules/@inquirer/core/dist/lib/theme.js
5002
5286
  import { styleText } from "util";
5003
5287
 
5004
- // ../../node_modules/.bun/@inquirer+figures@2.0.7/node_modules/@inquirer/figures/dist/index.js
5288
+ // ../../node_modules/@inquirer/figures/dist/index.js
5005
5289
  import process2 from "process";
5006
5290
  function isUnicodeSupported() {
5007
5291
  if (!process2.platform.startsWith("win")) {
@@ -5290,7 +5574,7 @@ var figures = shouldUseMain ? mainSymbols : fallbackSymbols;
5290
5574
  var dist_default = figures;
5291
5575
  var replacements = Object.entries(specialMainSymbols);
5292
5576
 
5293
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/theme.js
5577
+ // ../../node_modules/@inquirer/core/dist/lib/theme.js
5294
5578
  var defaultTheme = {
5295
5579
  prefix: {
5296
5580
  idle: styleText("blue", "?"),
@@ -5318,7 +5602,7 @@ function getDefaultTheme() {
5318
5602
  };
5319
5603
  }
5320
5604
 
5321
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/make-theme.js
5605
+ // ../../node_modules/@inquirer/core/dist/lib/make-theme.js
5322
5606
  function isPlainObject(value) {
5323
5607
  if (typeof value !== "object" || value === null)
5324
5608
  return false;
@@ -5346,7 +5630,7 @@ function makeTheme(...themes) {
5346
5630
  return deepMerge(...themesToMerge);
5347
5631
  }
5348
5632
 
5349
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-prefix.js
5633
+ // ../../node_modules/@inquirer/core/dist/lib/use-prefix.js
5350
5634
  function usePrefix({ status = "idle", theme }) {
5351
5635
  const [showLoader, setShowLoader] = useState(false);
5352
5636
  const [tick, setTick] = useState(0);
@@ -5376,11 +5660,11 @@ function usePrefix({ status = "idle", theme }) {
5376
5660
  const iconName = status === "loading" ? "idle" : status;
5377
5661
  return typeof prefix === "string" ? prefix : prefix[iconName] ?? prefix["idle"];
5378
5662
  }
5379
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-memo.js
5663
+ // ../../node_modules/@inquirer/core/dist/lib/use-memo.js
5380
5664
  function useMemo(fn, dependencies) {
5381
5665
  return withPointer((pointer) => {
5382
5666
  const prev = pointer.get();
5383
- if (!prev || prev.dependencies.length !== dependencies.length || prev.dependencies.some((dep, i) => dep !== dependencies[i])) {
5667
+ if (!pointer.initialized || prev.dependencies.length !== dependencies.length || prev.dependencies.some((dep, i) => dep !== dependencies[i])) {
5384
5668
  const value = fn();
5385
5669
  pointer.set({ value, dependencies });
5386
5670
  return value;
@@ -5388,12 +5672,12 @@ function useMemo(fn, dependencies) {
5388
5672
  return prev.value;
5389
5673
  });
5390
5674
  }
5391
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-ref.js
5675
+ // ../../node_modules/@inquirer/core/dist/lib/use-ref.js
5392
5676
  function useRef(val) {
5393
5677
  return useState({ current: val })[0];
5394
5678
  }
5395
5679
 
5396
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-keypress.js
5680
+ // ../../node_modules/@inquirer/core/dist/lib/use-keypress.js
5397
5681
  function useKeypress(userHandler) {
5398
5682
  const signal = useRef(userHandler);
5399
5683
  signal.current = userHandler;
@@ -5411,10 +5695,10 @@ function useKeypress(userHandler) {
5411
5695
  };
5412
5696
  }, []);
5413
5697
  }
5414
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/utils.js
5698
+ // ../../node_modules/@inquirer/core/dist/lib/utils.js
5415
5699
  var import_cli_width = __toESM(require_cli_width(), 1);
5416
5700
 
5417
- // ../../node_modules/.bun/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/utils.js
5701
+ // ../../node_modules/fast-string-truncated-width/dist/utils.js
5418
5702
  var getCodePointsLength = (() => {
5419
5703
  const SURROGATE_PAIR_RE = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
5420
5704
  return (input) => {
@@ -5433,7 +5717,7 @@ var isWideNotCJKTNotEmoji = (x) => {
5433
5717
  return x === 8987 || x === 9001 || x >= 12272 && x <= 12287 || x >= 12289 && x <= 12350 || x >= 12441 && x <= 12543 || x >= 12549 && x <= 12591 || x >= 12593 && x <= 12686 || x >= 12688 && x <= 12771 || x >= 12783 && x <= 12830 || x >= 12832 && x <= 12871 || x >= 12880 && x <= 19903 || x >= 65040 && x <= 65049 || x >= 65072 && x <= 65106 || x >= 65108 && x <= 65126 || x >= 65128 && x <= 65131 || x >= 127488 && x <= 127490 || x >= 127504 && x <= 127547 || x >= 127552 && x <= 127560 || x >= 131072 && x <= 196605 || x >= 196608 && x <= 262141;
5434
5718
  };
5435
5719
 
5436
- // ../../node_modules/.bun/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/index.js
5720
+ // ../../node_modules/fast-string-truncated-width/dist/index.js
5437
5721
  var ANSI_RE = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y;
5438
5722
  var CONTROL_RE = /[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y;
5439
5723
  var CJKT_WIDE_RE = /(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/yu;
@@ -5532,7 +5816,7 @@ var getStringTruncatedWidth = (input, truncationOptions = {}, widthOptions = {})
5532
5816
  };
5533
5817
  var dist_default2 = getStringTruncatedWidth;
5534
5818
 
5535
- // ../../node_modules/.bun/fast-string-width@3.0.2/node_modules/fast-string-width/dist/index.js
5819
+ // ../../node_modules/fast-string-width/dist/index.js
5536
5820
  var NO_TRUNCATION2 = {
5537
5821
  limit: Infinity,
5538
5822
  ellipsis: "",
@@ -5543,7 +5827,7 @@ var fastStringWidth = (input, options = {}) => {
5543
5827
  };
5544
5828
  var dist_default3 = fastStringWidth;
5545
5829
 
5546
- // ../../node_modules/.bun/fast-wrap-ansi@0.2.2/node_modules/fast-wrap-ansi/lib/main.js
5830
+ // ../../node_modules/fast-wrap-ansi/lib/main.js
5547
5831
  var ESC = "\x1B";
5548
5832
  var CSI = "\x9B";
5549
5833
  var END_CODE = 39;
@@ -5755,7 +6039,7 @@ function wrapAnsi(string, columns, options) {
5755
6039
  `);
5756
6040
  }
5757
6041
 
5758
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/utils.js
6042
+ // ../../node_modules/@inquirer/core/dist/lib/utils.js
5759
6043
  function breakLines(content, width) {
5760
6044
  return content.split(`
5761
6045
  `).flatMap((line) => wrapAnsi(line, width, { trim: false, wordWrap: false }).split(`
@@ -5766,7 +6050,7 @@ function readlineWidth() {
5766
6050
  return import_cli_width.default({ defaultWidth: 80, output: readline().output });
5767
6051
  }
5768
6052
 
5769
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/pagination/use-pagination.js
6053
+ // ../../node_modules/@inquirer/core/dist/lib/pagination/use-pagination.js
5770
6054
  function usePointerPosition({ active, renderedItems, pageSize, loop }) {
5771
6055
  const state = useRef({
5772
6056
  lastPointer: active,
@@ -5832,12 +6116,25 @@ function usePagination({ items, active, renderItem, pageSize, loop = true }) {
5832
6116
  return pageBuffer.filter((line) => typeof line === "string").join(`
5833
6117
  `);
5834
6118
  }
5835
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/create-prompt.js
5836
- var import_mute_stream = __toESM(require_lib(), 1);
6119
+ // ../../node_modules/@inquirer/core/dist/lib/create-prompt.js
5837
6120
  import * as readline2 from "readline";
5838
6121
  import { AsyncResource as AsyncResource3 } from "async_hooks";
5839
6122
 
5840
- // ../../node_modules/.bun/signal-exit@4.1.0/node_modules/signal-exit/dist/mjs/signals.js
6123
+ // ../../node_modules/@inquirer/type/dist/utils.js
6124
+ function withResolver() {
6125
+ let resolve6;
6126
+ let reject;
6127
+ const promise = new Promise((res, rej) => {
6128
+ resolve6 = res;
6129
+ reject = rej;
6130
+ });
6131
+ return { promise, resolve: resolve6, reject };
6132
+ }
6133
+
6134
+ // ../../node_modules/@inquirer/core/dist/lib/create-prompt.js
6135
+ var import_mute_stream = __toESM(require_lib(), 1);
6136
+
6137
+ // ../../node_modules/signal-exit/dist/mjs/signals.js
5841
6138
  var signals = [];
5842
6139
  signals.push("SIGHUP", "SIGINT", "SIGTERM");
5843
6140
  if (process.platform !== "win32") {
@@ -5847,7 +6144,7 @@ if (process.platform === "linux") {
5847
6144
  signals.push("SIGIO", "SIGPOLL", "SIGPWR", "SIGSTKFLT");
5848
6145
  }
5849
6146
 
5850
- // ../../node_modules/.bun/signal-exit@4.1.0/node_modules/signal-exit/dist/mjs/index.js
6147
+ // ../../node_modules/signal-exit/dist/mjs/index.js
5851
6148
  var processOk = (process3) => !!process3 && typeof process3 === "object" && typeof process3.removeListener === "function" && typeof process3.emit === "function" && typeof process3.reallyExit === "function" && typeof process3.listeners === "function" && typeof process3.kill === "function" && typeof process3.pid === "number" && typeof process3.on === "function";
5852
6149
  var kExitEmitter = Symbol.for("signal-exit emitter");
5853
6150
  var global = globalThis;
@@ -6045,10 +6342,10 @@ var {
6045
6342
  unload
6046
6343
  } = signalExitWrap(processOk(process3) ? new SignalExit(process3) : new SignalExitFallback);
6047
6344
 
6048
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/screen-manager.js
6345
+ // ../../node_modules/@inquirer/core/dist/lib/screen-manager.js
6049
6346
  import { stripVTControlCharacters } from "util";
6050
6347
 
6051
- // ../../node_modules/.bun/@inquirer+ansi@2.0.7/node_modules/@inquirer/ansi/dist/index.js
6348
+ // ../../node_modules/@inquirer/ansi/dist/index.js
6052
6349
  var ESC2 = "\x1B[";
6053
6350
  var cursorLeft = ESC2 + "G";
6054
6351
  var cursorHide = ESC2 + "?25l";
@@ -6064,7 +6361,7 @@ var cursorTo = (x, y) => {
6064
6361
  var eraseLine = ESC2 + "2K";
6065
6362
  var eraseLines = (lines) => lines > 0 ? (eraseLine + cursorUp(1)).repeat(lines - 1) + eraseLine + cursorLeft : "";
6066
6363
 
6067
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/screen-manager.js
6364
+ // ../../node_modules/@inquirer/core/dist/lib/screen-manager.js
6068
6365
  var height = (content) => content.split(`
6069
6366
  `).length;
6070
6367
  var lastLine = (content) => content.split(`
@@ -6130,22 +6427,18 @@ class ScreenManager {
6130
6427
  }
6131
6428
  }
6132
6429
 
6133
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/promise-polyfill.js
6134
- class PromisePolyfill extends Promise {
6135
- static withResolver() {
6136
- let resolve6;
6137
- let reject;
6138
- const promise = new Promise((res, rej) => {
6139
- resolve6 = res;
6140
- reject = rej;
6141
- });
6142
- return { promise, resolve: resolve6, reject };
6143
- }
6144
- }
6145
-
6146
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/create-prompt.js
6430
+ // ../../node_modules/@inquirer/core/dist/lib/create-prompt.js
6147
6431
  import path from "path";
6148
6432
  var nativeSetImmediate = globalThis.setImmediate;
6433
+ var nativeClearImmediate = globalThis.clearImmediate;
6434
+ function listenTo(target, event, listener) {
6435
+ if ("on" in target) {
6436
+ target.on(event, listener);
6437
+ return () => target.removeListener(event, listener);
6438
+ }
6439
+ target.addEventListener(event, listener);
6440
+ return () => target.removeEventListener(event, listener);
6441
+ }
6149
6442
  function getCallSites() {
6150
6443
  const savedPrepareStackTrace = Error.prepareStackTrace;
6151
6444
  let result = [];
@@ -6176,38 +6469,46 @@ function createPrompt(view) {
6176
6469
  });
6177
6470
  output.mute();
6178
6471
  const screen = new ScreenManager(rl);
6179
- const { promise, resolve: resolve6, reject } = PromisePolyfill.withResolver();
6180
- const cancel = () => reject(new CancelPromptError);
6181
- if (signal) {
6182
- const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
6183
- if (signal.aborted) {
6184
- abort();
6185
- return Object.assign(promise, { cancel });
6186
- }
6187
- signal.addEventListener("abort", abort);
6188
- cleanups.add(() => signal.removeEventListener("abort", abort));
6189
- }
6190
- cleanups.add(onExit((code, signal2) => {
6191
- reject(new ExitPromptError(`User force closed the prompt with ${code} ${signal2}`));
6192
- }));
6193
- const sigint = () => reject(new ExitPromptError(`User force closed the prompt with SIGINT`));
6194
- rl.on("SIGINT", sigint);
6195
- cleanups.add(() => rl.removeListener("SIGINT", sigint));
6472
+ const { promise, resolve: resolve6, reject } = withResolver();
6196
6473
  return withHooks(rl, (cycle) => {
6197
- const hooksCleanup = AsyncResource3.bind(() => effectScheduler.clearAll());
6198
- rl.on("close", hooksCleanup);
6199
- cleanups.add(() => rl.removeListener("close", hooksCleanup));
6474
+ const clearEffects = AsyncResource3.bind(() => effectScheduler.clearAll());
6475
+ const settlePrompt = (settle) => {
6476
+ try {
6477
+ clearEffects();
6478
+ settle();
6479
+ } catch (error) {
6480
+ reject(error);
6481
+ }
6482
+ };
6483
+ const resolvePrompt = (value) => settlePrompt(() => resolve6(value));
6484
+ const rejectPrompt = (error) => settlePrompt(() => reject(error));
6485
+ const promptPromise = Object.assign(promise.finally(() => {
6486
+ cleanups.forEach((cleanup) => cleanup());
6487
+ screen.done({ clearContent: Boolean(context.clearPromptOnDone) });
6488
+ output.end();
6489
+ }).then(() => promise), { cancel: () => rejectPrompt(new CancelPromptError) });
6490
+ if (signal) {
6491
+ const abort = () => rejectPrompt(new AbortPromptError({ cause: signal.reason }));
6492
+ if (signal.aborted) {
6493
+ abort();
6494
+ return promptPromise;
6495
+ }
6496
+ cleanups.add(listenTo(signal, "abort", abort));
6497
+ }
6498
+ cleanups.add(onExit((code, signal2) => {
6499
+ rejectPrompt(new ExitPromptError(`User force closed the prompt with ${code} ${signal2}`));
6500
+ }));
6501
+ cleanups.add(listenTo(rl, "SIGINT", () => rejectPrompt(new ExitPromptError(`User force closed the prompt with SIGINT`))));
6502
+ cleanups.add(listenTo(rl, "close", clearEffects));
6200
6503
  const startCycle = () => {
6201
- const checkCursorPos = () => screen.checkCursorPos();
6202
- rl.input.on("keypress", checkCursorPos);
6203
- cleanups.add(() => rl.input.removeListener("keypress", checkCursorPos));
6504
+ cleanups.add(listenTo(rl.input, "keypress", () => screen.checkCursorPos()));
6204
6505
  let pendingDone = null;
6205
6506
  cycle(() => {
6206
6507
  let effectsSettled = false;
6207
6508
  try {
6208
6509
  const nextView = view(config, (value) => {
6209
6510
  if (effectsSettled) {
6210
- resolve6(value);
6511
+ resolvePrompt(value);
6211
6512
  } else {
6212
6513
  pendingDone = { value };
6213
6514
  }
@@ -6224,37 +6525,28 @@ function createPrompt(view) {
6224
6525
  screen.render(content, bottomContent);
6225
6526
  effectScheduler.run();
6226
6527
  } catch (error) {
6227
- reject(error);
6528
+ rejectPrompt(error);
6228
6529
  }
6229
6530
  effectsSettled = true;
6230
6531
  if (pendingDone !== null) {
6231
6532
  const { value } = pendingDone;
6232
6533
  pendingDone = null;
6233
- resolve6(value);
6534
+ resolvePrompt(value);
6234
6535
  }
6235
6536
  });
6236
6537
  };
6237
- if ("readableFlowing" in input) {
6238
- nativeSetImmediate(startCycle);
6538
+ if ("readableFlowing" in input && input.isTTY) {
6539
+ const immediate = nativeSetImmediate(startCycle);
6540
+ cleanups.add(() => nativeClearImmediate(immediate));
6239
6541
  } else {
6240
6542
  startCycle();
6241
6543
  }
6242
- return Object.assign(promise.then((answer) => {
6243
- effectScheduler.clearAll();
6244
- return answer;
6245
- }, (error) => {
6246
- effectScheduler.clearAll();
6247
- throw error;
6248
- }).finally(() => {
6249
- cleanups.forEach((cleanup) => cleanup());
6250
- screen.done({ clearContent: Boolean(context.clearPromptOnDone) });
6251
- output.end();
6252
- }).then(() => promise), { cancel });
6544
+ return promptPromise;
6253
6545
  });
6254
6546
  };
6255
6547
  return prompt;
6256
6548
  }
6257
- // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/Separator.js
6549
+ // ../../node_modules/@inquirer/core/dist/lib/Separator.js
6258
6550
  import { styleText as styleText2 } from "util";
6259
6551
  class Separator {
6260
6552
  separator = styleText2("dim", Array.from({ length: 15 }).join(dist_default.line));
@@ -6268,7 +6560,7 @@ class Separator {
6268
6560
  return Boolean(choice && typeof choice === "object" && "type" in choice && choice.type === "separator");
6269
6561
  }
6270
6562
  }
6271
- // ../../node_modules/.bun/@inquirer+checkbox@5.2.1+7cb241fc07b679d9/node_modules/@inquirer/checkbox/dist/index.js
6563
+ // ../../node_modules/@inquirer/checkbox/dist/index.js
6272
6564
  import { styleText as styleText3 } from "util";
6273
6565
  var checkboxTheme = {
6274
6566
  icon: {
@@ -6352,9 +6644,13 @@ var dist_default4 = createPrompt((config, done) => {
6352
6644
  const [active, setActive] = useState(bounds.first);
6353
6645
  const [errorMsg, setError] = useState();
6354
6646
  useKeypress(async (key) => {
6647
+ if (status !== "idle")
6648
+ return;
6355
6649
  if (isEnterKey(key)) {
6650
+ setStatus("loading");
6356
6651
  const selection = items.filter(isChecked);
6357
6652
  const isValid = await validate([...selection]);
6653
+ setStatus("idle");
6358
6654
  if (required && !selection.length) {
6359
6655
  setError("At least one choice must be selected");
6360
6656
  } else if (isValid === true) {
@@ -8715,7 +9011,8 @@ var recipes = [
8715
9011
  { id: "starter-0.8.1-to-0.8.2", from: "0.8.1", to: "0.8.2" },
8716
9012
  { id: "starter-0.8.2-to-0.8.3", from: "0.8.2", to: "0.8.3" },
8717
9013
  { id: "starter-0.8.3-to-0.9.0", from: "0.8.3", to: "0.9.0" },
8718
- { id: "starter-0.9.0-to-0.9.1", from: "0.9.0", to: "0.9.1" }
9014
+ { id: "starter-0.9.0-to-0.9.1", from: "0.9.0", to: "0.9.1" },
9015
+ { id: "starter-0.9.1-to-0.10.0", from: "0.9.1", to: "0.10.0" }
8719
9016
  ];
8720
9017
  function resolveRecipeChain(sourceVersion, targetVersion) {
8721
9018
  if (compareVersions2(sourceVersion, minimumSupportedUpdateVersion) < 0) {
@@ -8762,6 +9059,33 @@ function parseVersion2(version) {
8762
9059
  // src/update/updater.ts
8763
9060
  import { rm as rm8 } from "fs/promises";
8764
9061
 
9062
+ // src/update/module-selection.ts
9063
+ function resolveUpdateTargetModules(sourceModules, explicitTarget) {
9064
+ return explicitTarget ? [...explicitTarget] : resolveModuleSelection({
9065
+ requested: sourceModules,
9066
+ includeRecommended: false
9067
+ });
9068
+ }
9069
+ function environmentProfileForUpdate(profile, selectedModules) {
9070
+ if (!profile)
9071
+ return;
9072
+ const runtimes = profile.build.runtimes ? { ...profile.build.runtimes } : undefined;
9073
+ const runtime = profile.runtime ? { ...profile.runtime } : undefined;
9074
+ for (const kind of AI_RUNTIME_KINDS) {
9075
+ if (selectedModules.includes(runtimeDistributionModule(kind)))
9076
+ continue;
9077
+ if (runtimes)
9078
+ delete runtimes[kind];
9079
+ if (runtime)
9080
+ delete runtime[kind];
9081
+ }
9082
+ return {
9083
+ ...profile,
9084
+ build: { modules: [...selectedModules], ...runtimes ? { runtimes } : {} },
9085
+ ...runtime ? { runtime } : {}
9086
+ };
9087
+ }
9088
+
8765
9089
  // src/update/artifacts.ts
8766
9090
  import { mkdtemp, readFile as readFile15 } from "fs/promises";
8767
9091
  import { tmpdir } from "os";
@@ -8972,7 +9296,6 @@ async function generatePublicArtifactProject(temporaryRoot, label, projectName,
8972
9296
  "--yes",
8973
9297
  "--skip-install",
8974
9298
  "--no-git",
8975
- "--no-optional-modules",
8976
9299
  "--license",
8977
9300
  provenance.legalLicense
8978
9301
  ];
@@ -8981,9 +9304,16 @@ async function generatePublicArtifactProject(temporaryRoot, label, projectName,
8981
9304
  if (provenance.legalLicense === "MIT") {
8982
9305
  args.push("--license-holder", "Windy project owner");
8983
9306
  }
8984
- for (const { name } of provenance.modules) {
8985
- if (!requiredModuleNames.includes(name)) {
8986
- args.push("--module", name);
9307
+ if (provenance.environmentProfile) {
9308
+ const path2 = join18(temporaryRoot, `${label}.environment.json`);
9309
+ await writeFile10(path2, JSON.stringify(provenance.environmentProfile));
9310
+ args.push("--environment-profile", path2);
9311
+ } else {
9312
+ args.push("--no-optional-modules");
9313
+ for (const { name } of provenance.modules) {
9314
+ if (!requiredModuleNames.includes(name)) {
9315
+ args.push("--module", name);
9316
+ }
8987
9317
  }
8988
9318
  }
8989
9319
  await assertProcess("bun", args, parent);
@@ -9094,12 +9424,14 @@ class PublishedArtifactProvider {
9094
9424
  throw new Error("Project Lock \u4E0E\u6765\u6E90 registry artifact \u7684 template commit \u4E0D\u4E00\u81F4");
9095
9425
  }
9096
9426
  await generatePublicArtifactProject(temporaryRoot, "base", projectName, baseArtifact.root, provenance);
9427
+ const selectedModules = targetModules ?? provenance.modules.map(({ name }) => name);
9097
9428
  await generateProject({
9098
9429
  sourceRoot: await resolveTemplateRoot(),
9099
9430
  targetDirectory,
9100
9431
  projectName,
9101
- selectedModules: targetModules ?? provenance.modules.map(({ name }) => name),
9102
- includeExample: (targetModules ?? provenance.modules.map(({ name }) => name)).includes("work-order"),
9432
+ selectedModules,
9433
+ environmentProfile: environmentProfileForUpdate(provenance.environmentProfile, selectedModules),
9434
+ includeExample: selectedModules.includes("work-order"),
9103
9435
  includeOxc: provenance.choices.includeOxc,
9104
9436
  projectLicense: provenance.legalLicense,
9105
9437
  licenseHolder: provenance.legalLicense === "MIT" ? "Windy project owner" : undefined,
@@ -9120,660 +9452,262 @@ async function readProjectName2(projectDirectory) {
9120
9452
  return source.name || basename10(projectDirectory);
9121
9453
  }
9122
9454
 
9123
- // src/update/planner.ts
9124
- import { readFile as readFile23 } from "fs/promises";
9125
- import { dirname as dirname9, join as join29 } from "path";
9455
+ // src/update/admin-settings-yaml-plan.ts
9456
+ import { join as join25 } from "path";
9126
9457
 
9127
- // src/update/docker-copy-update-plan.ts
9128
- async function deriveDockerfilePlans(files, snapshots, paths, targetWorkspaceManifests) {
9129
- const rootPackage = virtualPathContent("package.json", files, snapshots, targetWorkspaceManifests);
9130
- if (!rootPackage)
9131
- return;
9132
- const rootManifest = parsePackage(rootPackage, "package.json");
9133
- const workspacePatterns = rootManifest.workspaces ?? [];
9134
- const packages = [];
9135
- for (const path2 of [
9136
- ...new Set([...paths, ...targetWorkspaceManifests.keys()])
9137
- ].filter((path3) => path3.endsWith("package.json"))) {
9138
- const directory = path2 === "package.json" ? "." : path2.slice(0, -13);
9139
- if (directory === ".")
9140
- continue;
9141
- if (!workspacePatterns.some((pattern) => matchesWorkspace(pattern, directory))) {
9142
- continue;
9143
- }
9144
- const content = virtualPathContent(path2, files, snapshots, targetWorkspaceManifests);
9145
- if (!content)
9146
- continue;
9147
- const manifest = parsePackage(content, path2);
9148
- packages.push({
9149
- name: manifest.name,
9150
- directory,
9151
- dependencies: [
9152
- ...Object.keys(manifest.dependencies ?? {}),
9153
- ...Object.keys(manifest.devDependencies ?? {})
9154
- ],
9155
- workspaceDependencies: Object.entries({
9156
- ...manifest.dependencies,
9157
- ...manifest.devDependencies
9158
- }).filter(([, range]) => range.startsWith("workspace:")).map(([name]) => name)
9159
- });
9458
+ // src/update/planner-snapshots.ts
9459
+ import { readFile as readFile17, stat as stat2 } from "fs/promises";
9460
+ import { join as join24 } from "path";
9461
+
9462
+ // src/update/recipe-admin-api.ts
9463
+ var apiRecipes = [
9464
+ {
9465
+ path: "apps/web/src/services/platform-settings-api.ts",
9466
+ beforeImport: 'import { apiRequest } from "./http";',
9467
+ afterImport: 'import { apiRequest, apiResponse } from "./http";',
9468
+ addedImport: "apiResponse",
9469
+ marker: 'const WATERMARK_YAML_PATH = "/system/settings/watermark/yaml";',
9470
+ symbols: [
9471
+ "WATERMARK_YAML_PATH",
9472
+ "WatermarkYamlPreview",
9473
+ "exportWatermarkYaml",
9474
+ "previewWatermarkYaml",
9475
+ "importWatermarkYaml"
9476
+ ]
9477
+ },
9478
+ {
9479
+ path: "apps/web/src/services/diagnostics-api.ts",
9480
+ beforeImport: 'import type { DiagnosticRecord } from "@southwind-ai/shared";',
9481
+ afterImport: 'import type { DiagnosticRecord, PageResult } from "@southwind-ai/shared";',
9482
+ addedImport: "PageResult",
9483
+ marker: "export function listDiagnostics(",
9484
+ symbols: ["listDiagnostics"]
9160
9485
  }
9161
- packages.sort((left, right) => left.directory.localeCompare(right.directory));
9162
- for (const target of dockerfileCopyTargets) {
9163
- const dockerPlan = files.find(({ path: path2 }) => path2 === target.dockerfilePath);
9164
- if (!dockerPlan || dockerPlan.conflict)
9165
- continue;
9166
- const dockerSource = virtualContent(dockerPlan, snapshots);
9167
- if (!dockerSource)
9168
- continue;
9169
- const source = new TextDecoder().decode(dockerSource);
9170
- if (!hasDockerfileCopyAnchors(source, target)) {
9171
- dockerPlan.conflict = "Dockerfile \u7F3A\u5C11 0.6.6 \u57FA\u7EBF\u7684 COPY \u951A\u70B9\uFF0C\u5DF2\u505C\u6B62\u81EA\u52A8\u8FC1\u79FB\u65E7\u683C\u5F0F";
9172
- continue;
9173
- }
9174
- let next;
9175
- try {
9176
- next = syncDockerfileSource(source, target, packages);
9177
- } catch (error) {
9178
- dockerPlan.conflict = error instanceof Error ? error.message : "Docker COPY \u951A\u70B9\u65E0\u6548";
9179
- continue;
9486
+ ];
9487
+ var adminApiRecipePaths = apiRecipes.map(({ path: path2 }) => path2);
9488
+ function isAdminApiRecipePath(path2) {
9489
+ return adminApiRecipePaths.includes(path2);
9490
+ }
9491
+ function addAdminApiExports(path2, source, target) {
9492
+ const recipe = apiRecipes.find((item) => item.path === path2);
9493
+ if (!recipe || !target)
9494
+ return { error: "Admin API recipe \u76EE\u6807\u7F3A\u5931" };
9495
+ if (occurrences(target, recipe.marker) !== 1 || occurrences(target, recipe.afterImport) !== 1) {
9496
+ return { error: "Admin API recipe \u76EE\u6807\u951A\u70B9\u4E0D\u552F\u4E00" };
9497
+ }
9498
+ const block = target.slice(target.indexOf(recipe.marker)).trimEnd();
9499
+ if (recipe.symbols.some((symbol) => !block.includes(symbol))) {
9500
+ return { error: "Admin API recipe \u76EE\u6807\u5BFC\u51FA\u4E0D\u5B8C\u6574" };
9501
+ }
9502
+ if (recipe.symbols.some((symbol) => source.includes(symbol))) {
9503
+ const blockIndex = source.indexOf(block);
9504
+ const remainder = blockIndex < 0 ? source : source.slice(0, blockIndex) + source.slice(blockIndex + block.length);
9505
+ if (blockIndex < 0 || occurrences(source, block) !== 1 || occurrences(source, recipe.afterImport) !== 1 || occurrences(source, recipe.beforeImport) !== 0 || recipe.symbols.some((symbol) => remainder.includes(symbol))) {
9506
+ return { error: "Admin API recipe \u5DF2\u6709\u5BFC\u51FA\u4E0D\u5B8C\u6574\u3001\u91CD\u590D\u6216\u5DF2\u88AB\u4FEE\u6539" };
9180
9507
  }
9181
- if (next === source)
9182
- continue;
9183
- dockerPlan.action = "update";
9184
- dockerPlan.reason = "\u6309\u4E8B\u52A1\u5B89\u5168 workspace \u89C6\u56FE\u540C\u6B65 Docker COPY \u6E05\u5355";
9185
- dockerPlan.content = new TextEncoder().encode(next);
9508
+ return { content: source };
9186
9509
  }
9187
- }
9188
- function virtualPathContent(path2, files, snapshots, targetWorkspaceManifests) {
9189
- const plan = files.find((item) => item.path === path2);
9190
- return plan ? virtualContent(plan, snapshots) : snapshots.get(path2) ?? targetWorkspaceManifests.get(path2);
9191
- }
9192
- function virtualContent(plan, snapshots) {
9193
- if (plan.action === "delete")
9194
- return;
9195
- if (plan.action === "create" || plan.action === "update")
9196
- return plan.content;
9197
- return snapshots.get(plan.path);
9198
- }
9199
- function parsePackage(content, path2) {
9200
- try {
9201
- return JSON.parse(new TextDecoder().decode(content));
9202
- } catch (error) {
9203
- throw new Error(`\u65E0\u6CD5\u89E3\u6790 Docker workspace \u8F93\u5165\uFF1A${path2}`, {
9204
- cause: error
9205
- });
9510
+ if (occurrences(source, recipe.beforeImport) !== 1 || occurrences(source, recipe.afterImport) !== 0 || new RegExp(`\\b${recipe.addedImport}\\b`).test(source)) {
9511
+ return { error: "Admin API recipe \u539F\u59CB import \u951A\u70B9\u4E0D\u552F\u4E00" };
9206
9512
  }
9513
+ return {
9514
+ content: source.replace(recipe.beforeImport, recipe.afterImport) + `${source.endsWith(`
9515
+ `) ? `
9516
+ ` : `
9517
+
9518
+ `}${block}
9519
+ `
9520
+ };
9207
9521
  }
9208
- function matchesWorkspace(pattern, directory) {
9209
- return pattern.endsWith("/*") && directory.startsWith(pattern.slice(0, -1));
9522
+ function occurrences(source, value) {
9523
+ return source.split(value).length - 1;
9210
9524
  }
9211
9525
 
9212
- // src/update/migration-reconciler.ts
9213
- import { readFile as readFile19 } from "fs/promises";
9526
+ // src/update/planner-file.ts
9527
+ import { readFile as readFile16 } from "fs/promises";
9214
9528
  import { join as join23 } from "path";
9215
9529
 
9216
- // src/update/migration-adoption.ts
9217
- import { readFile as readFile18 } from "fs/promises";
9218
- import { join as join22 } from "path";
9219
-
9220
- // src/update/migration-project-file.ts
9221
- import { readFile as readFile16 } from "fs/promises";
9222
- import { join as join21 } from "path";
9223
- async function readProjectJson(input, path2) {
9224
- const content = await readProjectFile(input, path2);
9225
- if (!content) {
9226
- const error = new Error(`\u5F53\u524D\u9879\u76EE\u6587\u4EF6\u7F3A\u5931\uFF1A${path2}`);
9227
- error.code = "ENOENT";
9228
- throw error;
9229
- }
9230
- return JSON.parse(new TextDecoder().decode(content));
9530
+ // src/update/planner-file-support.ts
9531
+ function readyPlannedFile(path2, ownership, action, reason, expectedProjectContent, content) {
9532
+ return {
9533
+ path: path2,
9534
+ ownership,
9535
+ action,
9536
+ reason,
9537
+ expectedProjectContent,
9538
+ content
9539
+ };
9231
9540
  }
9232
- async function readProjectFile(input, path2) {
9233
- if (input.projectSnapshots?.has(path2)) {
9234
- return input.projectSnapshots.get(path2);
9235
- }
9236
- let content;
9237
- try {
9238
- content = await readFile16(join21(input.projectDirectory, path2));
9239
- } catch (error) {
9240
- if (!isMissingFileError(error))
9241
- throw error;
9242
- }
9243
- if (input.projectSnapshots) {
9244
- input.projectSnapshots.set(path2, content);
9245
- }
9246
- return content;
9541
+ function expectationForSnapshot(content) {
9542
+ return content ? { kind: "hash", value: hashContent(content) } : { kind: "missing" };
9247
9543
  }
9248
- function isMissingFileError(error) {
9249
- return error instanceof Error && "code" in error && error.code === "ENOENT";
9544
+ function conflict(path2, ownership, reason, expectedProjectContent) {
9545
+ return {
9546
+ path: path2,
9547
+ ownership,
9548
+ action: "preserve",
9549
+ reason: "\u5BA2\u6237\u5DF2\u4FEE\u6539",
9550
+ expectedProjectContent,
9551
+ conflict: reason
9552
+ };
9250
9553
  }
9554
+ // src/update/merge.ts
9555
+ import { mkdtemp as mkdtemp2, rm as rm6, writeFile as writeFile12 } from "fs/promises";
9556
+ import { tmpdir as tmpdir2 } from "os";
9557
+ import { join as join21 } from "path";
9251
9558
 
9252
- // src/update/migration-history.ts
9253
- import { readFile as readFile17 } from "fs/promises";
9254
- var drizzleRoot = "packages/database/drizzle";
9255
- var journalPath = `${drizzleRoot}/meta/_journal.json`;
9256
- var migrationPattern = /^(\d{4})_(.+)$/;
9257
- function snapshotPath(tag) {
9258
- const match = migrationPattern.exec(tag);
9259
- if (!match) {
9260
- throw new Error(`${journalPath}\uFF1Amigration tag \u683C\u5F0F\u65E0\u6548\uFF1A${tag}`);
9559
+ // src/update/merge-convergent-additions.ts
9560
+ async function reconcileConvergentAdditions(input) {
9561
+ const [oursDiff, theirsDiff] = await Promise.all([
9562
+ diff(input.root, input.basePath, input.oursPath),
9563
+ diff(input.root, input.basePath, input.theirsPath)
9564
+ ]);
9565
+ if (oursDiff === undefined || theirsDiff === undefined)
9566
+ return;
9567
+ const oursAdditions = pureAdditions(oursDiff);
9568
+ const theirsAdditions = pureAdditions(theirsDiff);
9569
+ const oursByContent = new Map;
9570
+ for (const addition of oursAdditions) {
9571
+ const key = addition.lines.join(`
9572
+ `);
9573
+ oursByContent.set(key, [...oursByContent.get(key) ?? [], addition]);
9261
9574
  }
9262
- return `${drizzleRoot}/meta/${match[1]}_snapshot.json`;
9263
- }
9264
- function validateUniqueNames(journal, label, conflicts) {
9265
- const seen = new Set;
9266
- for (const { tag } of journal.entries) {
9267
- const name = stableName(tag);
9268
- if (seen.has(name)) {
9269
- conflicts.push(`${journalPath}\uFF1A${label}\u5B58\u5728\u91CD\u590D migration \u540D\u79F0 ${name}`);
9575
+ const decode = (content) => new TextDecoder().decode(content);
9576
+ const baseLines = decode(input.base).split(`
9577
+ `);
9578
+ const oursLines = decode(input.ours).split(`
9579
+ `);
9580
+ const theirsLines = decode(input.theirs).split(`
9581
+ `);
9582
+ const mergedLines = input.merged.split(`
9583
+ `);
9584
+ const convergent = theirsAdditions.flatMap((addition) => {
9585
+ if (!isSubstantial(addition.lines) || !isNamedClassMethod(addition.lines)) {
9586
+ return [];
9270
9587
  }
9271
- seen.add(name);
9588
+ const key = addition.lines.join(`
9589
+ `);
9590
+ const matchingOurs = oursByContent.get(key);
9591
+ if (matchingOurs?.length !== 1)
9592
+ return [];
9593
+ if (theirsAdditions.filter((candidate2) => candidate2.lines.join(`
9594
+ `) === key).length !== 1) {
9595
+ return [];
9596
+ }
9597
+ const oursContainer = enclosingClass(oursLines, matchingOurs[0]);
9598
+ const theirsContainer = enclosingClass(theirsLines, addition);
9599
+ if (!oursContainer || oursContainer !== theirsContainer)
9600
+ return [];
9601
+ return countSequence(baseLines, addition.lines) === 0 && countSequence(oursLines, addition.lines) === 1 && countSequence(theirsLines, addition.lines) === 1 && countSequence(mergedLines, addition.lines) > 1 ? [{ ours: matchingOurs[0], theirs: addition }] : [];
9602
+ });
9603
+ if (convergent.length !== 1)
9604
+ return;
9605
+ const candidate = convergent[0];
9606
+ if (!candidate)
9607
+ return;
9608
+ const oursWithout = withoutAddition(oursLines, candidate.ours);
9609
+ const theirsWithout = withoutAddition(theirsLines, candidate.theirs);
9610
+ if (!oursWithout || !theirsWithout || !sameLines(oursWithout, theirsWithout)) {
9611
+ return;
9272
9612
  }
9613
+ return input.ours;
9273
9614
  }
9274
- function stableName(tag) {
9275
- return migrationPattern.exec(tag)?.[2] ?? tag;
9615
+ async function diff(root, basePath, branchPath) {
9616
+ const result = await captureProcess("git", ["diff", "--no-index", "--unified=0", "--no-color", basePath, branchPath], root);
9617
+ return result.code === 0 || result.code === 1 ? result.stdout : undefined;
9276
9618
  }
9277
- function highestPrefix(journal) {
9278
- return Math.max(-1, ...journal.entries.map(({ tag }) => Number(migrationPattern.exec(tag)?.[1] ?? -1)));
9619
+ function pureAdditions(diff2) {
9620
+ const lines = diff2.split(`
9621
+ `);
9622
+ const additions = [];
9623
+ for (let index = 0;index < lines.length; index += 1) {
9624
+ const match = /^@@ -\d+(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(lines[index] ?? "");
9625
+ if (!match)
9626
+ continue;
9627
+ const oldCount = match[1] === undefined ? 1 : Number(match[1]);
9628
+ const start = Number(match[2]);
9629
+ const newCount = match[3] === undefined ? 1 : Number(match[3]);
9630
+ if (oldCount !== 0 || newCount === 0)
9631
+ continue;
9632
+ const added = [];
9633
+ for (let cursor = index + 1;cursor < lines.length; cursor += 1) {
9634
+ const line = lines[cursor] ?? "";
9635
+ if (line.startsWith("@@ "))
9636
+ break;
9637
+ if (line.startsWith("+"))
9638
+ added.push(line.slice(1));
9639
+ }
9640
+ if (added.length === newCount)
9641
+ additions.push({ start, lines: added });
9642
+ }
9643
+ return additions;
9279
9644
  }
9280
- function highestIndex(journal) {
9281
- return Math.max(-1, ...journal.entries.map(({ idx }) => idx));
9645
+ function isSubstantial(lines) {
9646
+ return lines.filter((line) => line.trim()).length >= 3 && lines.join(`
9647
+ `).length >= 32;
9282
9648
  }
9283
- function isDrizzleHistoryPath(path2) {
9284
- return path2 === journalPath || isMigrationArtifact(path2);
9649
+ function isNamedClassMethod(lines) {
9650
+ const first = lines.find((line) => line.trim())?.trim() ?? "";
9651
+ const last = [...lines].reverse().find((line) => line.trim())?.trim() ?? "";
9652
+ return /^(?:(?:public|protected|private|static|abstract|override|async|declare|get|set)\s+)*(?:\*\s*)?[A-Za-z_$][\w$]*\s*(?:<[^>{}]*>)?\s*\(/.test(first) && lines.some((line) => line.includes("{")) && /^}\s*;?$/.test(last);
9285
9653
  }
9286
- function isMigrationArtifact(path2) {
9287
- return isSqlMigration(path2) || new RegExp(`^${drizzleRoot}/meta/\\d{4}_snapshot\\.json$`).test(path2);
9654
+ function enclosingClass(lines, addition) {
9655
+ const memberIndent = leadingWhitespace(addition.lines.find((line) => line.trim()) ?? "");
9656
+ for (let index = addition.start - 2;index >= 0; index -= 1) {
9657
+ const line = lines[index] ?? "";
9658
+ if (!line.trim() || leadingWhitespace(line) >= memberIndent)
9659
+ continue;
9660
+ const match = /^(\s*)(?:(?:export|default|abstract|declare)\s+)*class\s+([A-Za-z_$][\w$]*)\b/.exec(line);
9661
+ if (!match)
9662
+ return;
9663
+ const name = match[2] ?? "";
9664
+ const ordinal = lines.slice(0, index + 1).filter((candidate) => new RegExp(`^\\s*(?:(?:export|default|abstract|declare)\\s+)*class\\s+${escapeRegExp2(name)}\\b`).test(candidate)).length;
9665
+ return `${name}#${ordinal}`;
9666
+ }
9288
9667
  }
9289
- function isSqlMigration(path2) {
9290
- return new RegExp(`^${drizzleRoot}/\\d{4}_.+\\.sql$`).test(path2);
9668
+ function leadingWhitespace(line) {
9669
+ return /^\s*/.exec(line)?.[0].length ?? 0;
9291
9670
  }
9292
- function managedEntry(path2, content) {
9293
- return { path: path2, ownership: "windy-managed", hash: hashContent(content) };
9671
+ function escapeRegExp2(value) {
9672
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9294
9673
  }
9295
- function deduplicate(files) {
9296
- return [...new Map(files.map((entry) => [entry.path, entry])).values()];
9674
+ function countSequence(lines, sequence) {
9675
+ let count = 0;
9676
+ for (let index = 0;index <= lines.length - sequence.length; index += 1) {
9677
+ if (sameLines(lines.slice(index, index + sequence.length), sequence)) {
9678
+ count += 1;
9679
+ }
9680
+ }
9681
+ return count;
9297
9682
  }
9298
- function encodeJson(value) {
9299
- return new TextEncoder().encode(`${JSON.stringify(value, null, 2)}
9300
- `);
9683
+ function withoutAddition(lines, addition) {
9684
+ const index = addition.start - 1;
9685
+ if (!sameLines(lines.slice(index, index + addition.lines.length), addition.lines)) {
9686
+ return;
9687
+ }
9688
+ return [
9689
+ ...lines.slice(0, index),
9690
+ ...lines.slice(index + addition.lines.length)
9691
+ ];
9301
9692
  }
9302
- async function readJson(path2) {
9303
- return JSON.parse(await readFile17(path2, "utf8"));
9304
- }
9305
-
9306
- // src/update/snapshot-delta.ts
9307
- function applySnapshotDelta(before, after, current, path2, conflicts, conflictPath) {
9308
- if (deepEqual(before, after))
9309
- return structuredClone(current);
9310
- if (!isRecord(before) || !isRecord(after) || !isRecord(current)) {
9311
- if (deepEqual(current, before) || deepEqual(current, after)) {
9312
- return structuredClone(after);
9313
- }
9314
- conflicts.push(`${conflictPath}\uFF1A\u5E73\u53F0\u4E0E\u4E1A\u52A1\u540C\u65F6\u4FEE\u6539 snapshot \u8282\u70B9 ${path2.join(".") || "<root>"}`);
9315
- return structuredClone(current);
9316
- }
9317
- const merged = structuredClone(current);
9318
- const keys = new Set([...Object.keys(before), ...Object.keys(after)]);
9319
- for (const key of keys) {
9320
- if (path2.length === 0 && (key === "id" || key === "prevId"))
9321
- continue;
9322
- const nextPath = [...path2, key];
9323
- if (!(key in after)) {
9324
- if (!(key in current))
9325
- continue;
9326
- if (deepEqual(current[key], before[key]))
9327
- delete merged[key];
9328
- else
9329
- conflicts.push(`${conflictPath}\uFF1A\u5E73\u53F0\u5220\u9664\u4E86\u4E1A\u52A1\u5DF2\u4FEE\u6539\u7684 snapshot \u8282\u70B9 ${nextPath.join(".")}`);
9330
- continue;
9331
- }
9332
- if (!(key in before)) {
9333
- if (!(key in current) || deepEqual(current[key], after[key])) {
9334
- merged[key] = structuredClone(after[key]);
9335
- } else {
9336
- conflicts.push(`${conflictPath}\uFF1A\u5E73\u53F0\u65B0\u589E\u7684 snapshot \u8282\u70B9\u5DF2\u88AB\u4E1A\u52A1\u5360\u7528 ${nextPath.join(".")}`);
9337
- }
9338
- continue;
9339
- }
9340
- merged[key] = applySnapshotDelta(before[key], after[key], current[key], nextPath, conflicts, conflictPath);
9341
- }
9342
- return merged;
9343
- }
9344
- function isRecord(value) {
9345
- return !!value && typeof value === "object" && !Array.isArray(value);
9346
- }
9347
- function deepEqual(left, right) {
9348
- return JSON.stringify(left) === JSON.stringify(right);
9349
- }
9350
-
9351
- // src/update/migration-adoption.ts
9352
- async function inspectPreexistingMigrationAdoptions(input) {
9353
- const conflicts = [];
9354
- const adoptions = [];
9355
- const projectByName = new Map(input.project.entries.map((entry) => [stableName(entry.tag), entry]));
9356
- for (const candidate of input.candidates) {
9357
- const name = stableName(candidate.tag);
9358
- const projectEntry = projectByName.get(name);
9359
- if (!projectEntry || input.managedNames.has(name))
9360
- continue;
9361
- const targetPosition = input.target.entries.indexOf(candidate);
9362
- const projectPosition = input.project.entries.indexOf(projectEntry);
9363
- const previousTarget = input.target.entries[targetPosition - 1];
9364
- const previousProject = input.project.entries[projectPosition - 1];
9365
- if (!previousTarget || !previousProject) {
9366
- conflicts.push(`${journalPath}\uFF1A\u65E0\u6CD5\u91C7\u7EB3\u73B0\u6709\u5E73\u53F0 migration ${name}\uFF0C\u7F3A\u5C11\u524D\u7F6E\u5386\u53F2`);
9367
- continue;
9368
- }
9369
- const projectSqlPath = `${drizzleRoot}/${projectEntry.tag}.sql`;
9370
- const projectSnapshotPath = snapshotPath(projectEntry.tag);
9371
- const [
9372
- targetSql,
9373
- projectSql,
9374
- targetBefore,
9375
- targetAfter,
9376
- projectBeforeBytes,
9377
- projectAfterBytes
9378
- ] = await Promise.all([
9379
- readFile18(join22(input.targetDirectory, `${drizzleRoot}/${candidate.tag}.sql`)),
9380
- readProjectFile(input, projectSqlPath),
9381
- readJson(join22(input.targetDirectory, snapshotPath(previousTarget.tag))),
9382
- readJson(join22(input.targetDirectory, snapshotPath(candidate.tag))),
9383
- readProjectFile(input, snapshotPath(previousProject.tag)),
9384
- readProjectFile(input, projectSnapshotPath)
9385
- ]);
9386
- if (!projectSql || !projectBeforeBytes || !projectAfterBytes) {
9387
- conflicts.push(`${journalPath}\uFF1A\u65E0\u6CD5\u91C7\u7EB3\u73B0\u6709\u5E73\u53F0 migration ${name}\uFF0C\u9879\u76EE migration \u6587\u4EF6\u4E0D\u5B8C\u6574`);
9388
- continue;
9389
- }
9390
- const projectBefore = decodeSnapshot(projectBeforeBytes);
9391
- const projectAfter = decodeSnapshot(projectAfterBytes);
9392
- const reason = validateAdoption({
9393
- targetSql,
9394
- projectSql,
9395
- targetBefore,
9396
- targetAfter,
9397
- projectBefore,
9398
- projectAfter
9399
- });
9400
- if (reason) {
9401
- conflicts.push(`${journalPath}\uFF1A\u65E0\u6CD5\u91C7\u7EB3\u73B0\u6709\u5E73\u53F0 migration ${name}\uFF0C${reason}`);
9402
- continue;
9403
- }
9404
- adoptions.push({
9405
- sqlPath: projectSqlPath,
9406
- snapshotPath: projectSnapshotPath,
9407
- sql: projectSql,
9408
- snapshot: projectAfterBytes
9409
- });
9410
- }
9411
- return { adoptions, conflicts };
9412
- }
9413
- function validateAdoption(input) {
9414
- if (!equalBytes(input.targetSql, input.projectSql)) {
9415
- return "SQL \u5185\u5BB9\u4E0D\u4E00\u81F4";
9416
- }
9417
- if (input.projectAfter.prevId !== input.projectBefore.id) {
9418
- return "\u9879\u76EE snapshot lineage \u4E0D\u8FDE\u7EED";
9419
- }
9420
- const deltaConflicts = [];
9421
- const expected = applySnapshotDelta(input.targetBefore, input.targetAfter, input.projectBefore, [], deltaConflicts, journalPath);
9422
- if (deltaConflicts.length || canonicalSnapshot(expected) !== canonicalSnapshot(input.projectAfter)) {
9423
- return "snapshot delta \u4E0E\u9879\u76EE\u5386\u53F2\u4E0D\u4E00\u81F4";
9424
- }
9425
- }
9426
- function decodeSnapshot(content) {
9427
- return JSON.parse(new TextDecoder().decode(content));
9428
- }
9429
- function equalBytes(left, right) {
9430
- return left.byteLength === right.byteLength && left.every((value, index) => value === right[index]);
9431
- }
9432
- function canonicalSnapshot(value) {
9433
- return JSON.stringify(canonicalize(value, true));
9434
- }
9435
- function canonicalize(value, root = false) {
9436
- if (Array.isArray(value))
9437
- return value.map((item) => canonicalize(item));
9438
- if (!value || typeof value !== "object")
9439
- return value;
9440
- return Object.fromEntries(Object.entries(value).filter(([key]) => !root || key !== "id" && key !== "prevId").sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, canonicalize(item)]));
9441
- }
9442
-
9443
- // src/update/migration-reconciler.ts
9444
- async function reconcileDrizzleMigrations(input) {
9445
- const journals = await readJournals(input);
9446
- if (!journals) {
9447
- return unchanged(input.targetManifest);
9448
- }
9449
- const { base, project, target } = journals;
9450
- const conflicts = [];
9451
- validateUniqueNames(base, "\u6765\u6E90\u6A21\u677F", conflicts);
9452
- validateUniqueNames(project, "\u5F53\u524D\u9879\u76EE", conflicts);
9453
- validateUniqueNames(target, "\u76EE\u6807\u6A21\u677F", conflicts);
9454
- if (conflicts.length) {
9455
- return { ...unchanged(input.targetManifest), conflicts };
9456
- }
9457
- const baseNames = new Set(base.entries.map(({ tag }) => stableName(tag)));
9458
- const projectNames = new Set(project.entries.map(({ tag }) => stableName(tag)));
9459
- const managedNames = new Set(input.sourceManifest.files.filter(({ path: path2 }) => isSqlMigration(path2)).map(({ path: path2 }) => stableName(path2.slice(drizzleRoot.length + 1, -4))));
9460
- const candidates = target.entries.filter(({ tag }) => !baseNames.has(stableName(tag)));
9461
- const adoption = await inspectPreexistingMigrationAdoptions({
9462
- ...input,
9463
- project,
9464
- target,
9465
- candidates,
9466
- managedNames
9467
- });
9468
- conflicts.push(...adoption.conflicts);
9469
- const additions = candidates.filter(({ tag }) => !projectNames.has(stableName(tag)));
9470
- const contentOverrides = new Map;
9471
- const files = await preservePublishedHistory(input.sourceManifest, input, contentOverrides);
9472
- const publishedPaths = new Set(files.map(({ path: path2 }) => path2));
9473
- for (const adopted of adoption.adoptions) {
9474
- contentOverrides.set(adopted.sqlPath, adopted.sql);
9475
- contentOverrides.set(adopted.snapshotPath, adopted.snapshot);
9476
- files.push(managedEntry(adopted.sqlPath, adopted.sql), managedEntry(adopted.snapshotPath, adopted.snapshot));
9477
- publishedPaths.add(adopted.sqlPath);
9478
- publishedPaths.add(adopted.snapshotPath);
9479
- }
9480
- files.push(...input.targetManifest.files.filter(({ path: path2 }) => !isDrizzleHistoryPath(path2)));
9481
- if (!additions.length) {
9482
- const journalContent2 = encodeJson(project);
9483
- contentOverrides.set(journalPath, journalContent2);
9484
- files.push(managedEntry(journalPath, journalContent2));
9485
- return result(input.targetManifest, files, contentOverrides, publishedPaths, conflicts);
9486
- }
9487
- let nextPrefix = highestPrefix(project) + 1;
9488
- let nextIndex = highestIndex(project) + 1;
9489
- let previousWhen = project.entries.at(-1)?.when ?? 0;
9490
- let currentSnapshot = await readLatestSnapshot(input, project);
9491
- const mergedJournal = structuredClone(project);
9492
- for (const addition of additions) {
9493
- const targetPosition = target.entries.indexOf(addition);
9494
- const previousTarget = target.entries[targetPosition - 1];
9495
- if (!previousTarget) {
9496
- conflicts.push(`${journalPath}\uFF1A\u76EE\u6807 migration \u7F3A\u5C11\u524D\u7F6E\u5FEB\u7167`);
9497
- break;
9498
- }
9499
- const targetBefore = await readSnapshot(input.targetDirectory, previousTarget.tag);
9500
- const targetAfter = await readSnapshot(input.targetDirectory, addition.tag);
9501
- const merged = applySnapshotDelta(targetBefore, targetAfter, currentSnapshot, [], conflicts, journalPath);
9502
- if (conflicts.length)
9503
- break;
9504
- const prefix = String(nextPrefix).padStart(4, "0");
9505
- const name = stableName(addition.tag);
9506
- const tag = `${prefix}_${name}`;
9507
- const sqlPath = `${drizzleRoot}/${tag}.sql`;
9508
- const snapshotPath2 = `${drizzleRoot}/meta/${prefix}_snapshot.json`;
9509
- const sql = await readFile19(join23(input.targetDirectory, `${drizzleRoot}/${addition.tag}.sql`));
9510
- merged.id = targetAfter.id;
9511
- merged.prevId = currentSnapshot.id;
9512
- const snapshot2 = encodeJson(merged);
9513
- contentOverrides.set(sqlPath, sql);
9514
- contentOverrides.set(snapshotPath2, snapshot2);
9515
- files.push(managedEntry(sqlPath, sql), managedEntry(snapshotPath2, snapshot2));
9516
- previousWhen = Math.max(previousWhen + 1, addition.when);
9517
- mergedJournal.entries.push({
9518
- ...addition,
9519
- idx: nextIndex,
9520
- when: previousWhen,
9521
- tag
9522
- });
9523
- currentSnapshot = merged;
9524
- nextPrefix += 1;
9525
- nextIndex += 1;
9526
- }
9527
- const journalContent = encodeJson(mergedJournal);
9528
- contentOverrides.set(journalPath, journalContent);
9529
- files.push(managedEntry(journalPath, journalContent));
9530
- return result(input.targetManifest, files, contentOverrides, publishedPaths, conflicts);
9531
- }
9532
- function result(targetManifest, files, contentOverrides, forcePreserves, conflicts) {
9533
- return {
9534
- targetManifest: {
9535
- ...targetManifest,
9536
- files: deduplicate(files).sort((left, right) => left.path.localeCompare(right.path))
9537
- },
9538
- contentOverrides,
9539
- forceUpdates: new Set([journalPath]),
9540
- forcePreserves,
9541
- conflicts
9542
- };
9543
- }
9544
- function unchanged(targetManifest) {
9545
- return {
9546
- targetManifest,
9547
- contentOverrides: new Map,
9548
- forceUpdates: new Set,
9549
- forcePreserves: new Set,
9550
- conflicts: []
9551
- };
9552
- }
9553
- async function readJournals(input) {
9554
- try {
9555
- const [base, project, target] = await Promise.all([
9556
- readJson(join23(input.baseDirectory, journalPath)),
9557
- readProjectJson(input, journalPath),
9558
- readJson(join23(input.targetDirectory, journalPath))
9559
- ]);
9560
- return { base, project, target };
9561
- } catch (error) {
9562
- if (isMissingFileError(error))
9563
- return;
9564
- throw error;
9565
- }
9566
- }
9567
- async function preservePublishedHistory(manifest, input, overrides) {
9568
- const entries = manifest.files.filter(({ path: path2 }) => isMigrationArtifact(path2) && path2 !== journalPath);
9569
- await Promise.all(entries.map(async ({ path: path2 }) => {
9570
- const content = await readProjectFile(input, path2);
9571
- if (!content)
9572
- throw new Error(`\u5F53\u524D\u9879\u76EE migration \u6587\u4EF6\u7F3A\u5931\uFF1A${path2}`);
9573
- overrides.set(path2, content);
9574
- }));
9575
- return entries;
9576
- }
9577
- async function readLatestSnapshot(input, journal) {
9578
- const latest = journal.entries.at(-1);
9579
- if (!latest)
9580
- throw new Error(`${journalPath}\uFF1A\u5F53\u524D\u9879\u76EE\u6CA1\u6709 migration`);
9581
- const path2 = snapshotPath(latest.tag);
9582
- const content = await readProjectFile(input, path2);
9583
- if (!content) {
9584
- throw new Error(`\u5F53\u524D\u9879\u76EE migration \u6587\u4EF6\u7F3A\u5931\uFF1A${path2}`);
9585
- }
9586
- return JSON.parse(new TextDecoder().decode(content));
9587
- }
9588
- async function readSnapshot(root, tag) {
9589
- return await readJson(join23(root, snapshotPath(tag)));
9590
- }
9591
-
9592
- // src/update/planner-file.ts
9593
- import { readFile as readFile21 } from "fs/promises";
9594
- import { join as join27 } from "path";
9595
-
9596
- // src/update/planner-file-support.ts
9597
- function readyPlannedFile(path2, ownership, action, reason, expectedProjectContent, content) {
9598
- return {
9599
- path: path2,
9600
- ownership,
9601
- action,
9602
- reason,
9603
- expectedProjectContent,
9604
- content
9605
- };
9606
- }
9607
- function expectationForSnapshot(content) {
9608
- return content ? { kind: "hash", value: hashContent(content) } : { kind: "missing" };
9609
- }
9610
- function conflict(path2, ownership, reason, expectedProjectContent) {
9611
- return {
9612
- path: path2,
9613
- ownership,
9614
- action: "preserve",
9615
- reason: "\u5BA2\u6237\u5DF2\u4FEE\u6539",
9616
- expectedProjectContent,
9617
- conflict: reason
9618
- };
9619
- }
9620
- // src/update/merge.ts
9621
- import { mkdtemp as mkdtemp2, rm as rm6, writeFile as writeFile12 } from "fs/promises";
9622
- import { tmpdir as tmpdir2 } from "os";
9623
- import { join as join24 } from "path";
9624
-
9625
- // src/update/merge-convergent-additions.ts
9626
- async function reconcileConvergentAdditions(input) {
9627
- const [oursDiff, theirsDiff] = await Promise.all([
9628
- diff(input.root, input.basePath, input.oursPath),
9629
- diff(input.root, input.basePath, input.theirsPath)
9630
- ]);
9631
- if (oursDiff === undefined || theirsDiff === undefined)
9632
- return;
9633
- const oursAdditions = pureAdditions(oursDiff);
9634
- const theirsAdditions = pureAdditions(theirsDiff);
9635
- const oursByContent = new Map;
9636
- for (const addition of oursAdditions) {
9637
- const key = addition.lines.join(`
9638
- `);
9639
- oursByContent.set(key, [...oursByContent.get(key) ?? [], addition]);
9640
- }
9641
- const decode = (content) => new TextDecoder().decode(content);
9642
- const baseLines = decode(input.base).split(`
9643
- `);
9644
- const oursLines = decode(input.ours).split(`
9645
- `);
9646
- const theirsLines = decode(input.theirs).split(`
9647
- `);
9648
- const mergedLines = input.merged.split(`
9649
- `);
9650
- const convergent = theirsAdditions.flatMap((addition) => {
9651
- if (!isSubstantial(addition.lines) || !isNamedClassMethod(addition.lines)) {
9652
- return [];
9653
- }
9654
- const key = addition.lines.join(`
9655
- `);
9656
- const matchingOurs = oursByContent.get(key);
9657
- if (matchingOurs?.length !== 1)
9658
- return [];
9659
- if (theirsAdditions.filter((candidate2) => candidate2.lines.join(`
9660
- `) === key).length !== 1) {
9661
- return [];
9662
- }
9663
- const oursContainer = enclosingClass(oursLines, matchingOurs[0]);
9664
- const theirsContainer = enclosingClass(theirsLines, addition);
9665
- if (!oursContainer || oursContainer !== theirsContainer)
9666
- return [];
9667
- return countSequence(baseLines, addition.lines) === 0 && countSequence(oursLines, addition.lines) === 1 && countSequence(theirsLines, addition.lines) === 1 && countSequence(mergedLines, addition.lines) > 1 ? [{ ours: matchingOurs[0], theirs: addition }] : [];
9668
- });
9669
- if (convergent.length !== 1)
9670
- return;
9671
- const candidate = convergent[0];
9672
- if (!candidate)
9673
- return;
9674
- const oursWithout = withoutAddition(oursLines, candidate.ours);
9675
- const theirsWithout = withoutAddition(theirsLines, candidate.theirs);
9676
- if (!oursWithout || !theirsWithout || !sameLines(oursWithout, theirsWithout)) {
9677
- return;
9678
- }
9679
- return input.ours;
9680
- }
9681
- async function diff(root, basePath, branchPath) {
9682
- const result2 = await captureProcess("git", ["diff", "--no-index", "--unified=0", "--no-color", basePath, branchPath], root);
9683
- return result2.code === 0 || result2.code === 1 ? result2.stdout : undefined;
9684
- }
9685
- function pureAdditions(diff2) {
9686
- const lines = diff2.split(`
9687
- `);
9688
- const additions = [];
9689
- for (let index = 0;index < lines.length; index += 1) {
9690
- const match = /^@@ -\d+(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(lines[index] ?? "");
9691
- if (!match)
9692
- continue;
9693
- const oldCount = match[1] === undefined ? 1 : Number(match[1]);
9694
- const start = Number(match[2]);
9695
- const newCount = match[3] === undefined ? 1 : Number(match[3]);
9696
- if (oldCount !== 0 || newCount === 0)
9697
- continue;
9698
- const added = [];
9699
- for (let cursor = index + 1;cursor < lines.length; cursor += 1) {
9700
- const line = lines[cursor] ?? "";
9701
- if (line.startsWith("@@ "))
9702
- break;
9703
- if (line.startsWith("+"))
9704
- added.push(line.slice(1));
9705
- }
9706
- if (added.length === newCount)
9707
- additions.push({ start, lines: added });
9708
- }
9709
- return additions;
9710
- }
9711
- function isSubstantial(lines) {
9712
- return lines.filter((line) => line.trim()).length >= 3 && lines.join(`
9713
- `).length >= 32;
9714
- }
9715
- function isNamedClassMethod(lines) {
9716
- const first = lines.find((line) => line.trim())?.trim() ?? "";
9717
- const last = [...lines].reverse().find((line) => line.trim())?.trim() ?? "";
9718
- return /^(?:(?:public|protected|private|static|abstract|override|async|declare|get|set)\s+)*(?:\*\s*)?[A-Za-z_$][\w$]*\s*(?:<[^>{}]*>)?\s*\(/.test(first) && lines.some((line) => line.includes("{")) && /^}\s*;?$/.test(last);
9719
- }
9720
- function enclosingClass(lines, addition) {
9721
- const memberIndent = leadingWhitespace(addition.lines.find((line) => line.trim()) ?? "");
9722
- for (let index = addition.start - 2;index >= 0; index -= 1) {
9723
- const line = lines[index] ?? "";
9724
- if (!line.trim() || leadingWhitespace(line) >= memberIndent)
9725
- continue;
9726
- const match = /^(\s*)(?:(?:export|default|abstract|declare)\s+)*class\s+([A-Za-z_$][\w$]*)\b/.exec(line);
9727
- if (!match)
9728
- return;
9729
- const name = match[2] ?? "";
9730
- const ordinal = lines.slice(0, index + 1).filter((candidate) => new RegExp(`^\\s*(?:(?:export|default|abstract|declare)\\s+)*class\\s+${escapeRegExp2(name)}\\b`).test(candidate)).length;
9731
- return `${name}#${ordinal}`;
9732
- }
9733
- }
9734
- function leadingWhitespace(line) {
9735
- return /^\s*/.exec(line)?.[0].length ?? 0;
9736
- }
9737
- function escapeRegExp2(value) {
9738
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9739
- }
9740
- function countSequence(lines, sequence) {
9741
- let count = 0;
9742
- for (let index = 0;index <= lines.length - sequence.length; index += 1) {
9743
- if (sameLines(lines.slice(index, index + sequence.length), sequence)) {
9744
- count += 1;
9745
- }
9746
- }
9747
- return count;
9748
- }
9749
- function withoutAddition(lines, addition) {
9750
- const index = addition.start - 1;
9751
- if (!sameLines(lines.slice(index, index + addition.lines.length), addition.lines)) {
9752
- return;
9753
- }
9754
- return [
9755
- ...lines.slice(0, index),
9756
- ...lines.slice(index + addition.lines.length)
9757
- ];
9758
- }
9759
- function sameLines(left, right) {
9760
- return left.length === right.length && left.every((line, index) => line === right[index]);
9693
+ function sameLines(left, right) {
9694
+ return left.length === right.length && left.every((line, index) => line === right[index]);
9761
9695
  }
9762
9696
 
9763
9697
  // src/update/merge.ts
9764
9698
  async function mergeTextFile(base, ours, theirs) {
9765
- const root = await mkdtemp2(join24(tmpdir2(), "windy-merge-"));
9699
+ const root = await mkdtemp2(join21(tmpdir2(), "windy-merge-"));
9766
9700
  try {
9767
- const oursPath = join24(root, "ours");
9768
- const basePath = join24(root, "base");
9769
- const theirsPath = join24(root, "theirs");
9701
+ const oursPath = join21(root, "ours");
9702
+ const basePath = join21(root, "base");
9703
+ const theirsPath = join21(root, "theirs");
9770
9704
  await Promise.all([
9771
9705
  writeFile12(oursPath, ours),
9772
9706
  writeFile12(basePath, base),
9773
9707
  writeFile12(theirsPath, theirs)
9774
9708
  ]);
9775
- const result2 = await captureProcess("git", ["merge-file", "-p", "--diff3", oursPath, basePath, theirsPath], root);
9776
- if (result2.code === 0) {
9709
+ const result = await captureProcess("git", ["merge-file", "-p", "--diff3", oursPath, basePath, theirsPath], root);
9710
+ if (result.code === 0) {
9777
9711
  const reconciledContent = await reconcileConvergentAdditions({
9778
9712
  root,
9779
9713
  basePath,
@@ -9782,18 +9716,18 @@ async function mergeTextFile(base, ours, theirs) {
9782
9716
  base,
9783
9717
  ours,
9784
9718
  theirs,
9785
- merged: result2.stdout
9719
+ merged: result.stdout
9786
9720
  });
9787
9721
  if (reconciledContent !== undefined) {
9788
9722
  return { content: reconciledContent };
9789
9723
  }
9790
- return { content: new TextEncoder().encode(result2.stdout) };
9724
+ return { content: new TextEncoder().encode(result.stdout) };
9791
9725
  }
9792
- if (result2.code > 0 && result2.code <= 127) {
9793
- const resolved = resolveConservativeConflicts(result2.stdout);
9726
+ if (result.code > 0 && result.code <= 127) {
9727
+ const resolved = resolveConservativeConflicts(result.stdout);
9794
9728
  return resolved ? { content: new TextEncoder().encode(resolved) } : { conflict: "\u4E09\u65B9\u5408\u5E76\u5B58\u5728\u91CD\u53E0\u4FEE\u6539" };
9795
9729
  }
9796
- return { conflict: result2.stderr.trim() || "git merge-file \u6267\u884C\u5931\u8D25" };
9730
+ return { conflict: result.stderr.trim() || "git merge-file \u6267\u884C\u5931\u8D25" };
9797
9731
  } finally {
9798
9732
  await rm6(root, { recursive: true, force: true });
9799
9733
  }
@@ -10197,7 +10131,7 @@ ${workflowArtifactProperty}`
10197
10131
  ];
10198
10132
  let content = source;
10199
10133
  for (const replacement of replacements2) {
10200
- if (occurrences(content, replacement.anchor) !== 1) {
10134
+ if (occurrences2(content, replacement.anchor) !== 1) {
10201
10135
  return conflict3(input, `Agent Runtime Composition \u951A\u70B9\u4E0D\u552F\u4E00\uFF1A${replacement.anchor}`);
10202
10136
  }
10203
10137
  content = content.replace(replacement.anchor, replacement.value);
@@ -10227,7 +10161,7 @@ function conflict3(input, message) {
10227
10161
  function decode(content) {
10228
10162
  return new TextDecoder().decode(content);
10229
10163
  }
10230
- function occurrences(source, value) {
10164
+ function occurrences2(source, value) {
10231
10165
  return source.split(value).length - 1;
10232
10166
  }
10233
10167
  function validateLegacyRuntimeObject(source) {
@@ -10235,22 +10169,22 @@ function validateLegacyRuntimeObject(source) {
10235
10169
  ...input,`;
10236
10170
  const endAnchor = `
10237
10171
  });`;
10238
- if (occurrences(source, startAnchor) !== 1 || occurrences(source, endAnchor) !== 1) {
10172
+ if (occurrences2(source, startAnchor) !== 1 || occurrences2(source, endAnchor) !== 1) {
10239
10173
  return "Agent Runtime register object \u951A\u70B9\u4E0D\u552F\u4E00";
10240
10174
  }
10241
10175
  const start = source.indexOf(startAnchor);
10242
10176
  const end = source.indexOf(endAnchor, start + startAnchor.length);
10243
10177
  if (end < 0)
10244
10178
  return "Agent Runtime register object \u7ED3\u675F\u951A\u70B9\u7F3A\u5931";
10245
- const object = source.slice(start, end);
10246
- const host = object.indexOf(hostAuthProperty);
10179
+ const object2 = source.slice(start, end);
10180
+ const host = object2.indexOf(hostAuthProperty);
10247
10181
  if (host < 0)
10248
10182
  return "Agent Runtime Host Auth property \u951A\u70B9\u7F3A\u5931";
10249
- const tail = object.slice(host + hostAuthProperty.length);
10183
+ const tail = object2.slice(host + hostAuthProperty.length);
10250
10184
  if (/^\s*\.\.\./m.test(tail)) {
10251
10185
  return "Workflow binding \u63D2\u5165\u70B9\u540E\u5B58\u5728\u53EF\u8986\u76D6\u5B83\u7684 spread";
10252
10186
  }
10253
- if (/["']clientAgentWorkflowArtifactBindings["']\s*:/.test(object) || /\[\s*["']clientAgentWorkflowArtifactBindings["']\s*\]/.test(object)) {
10187
+ if (/["']clientAgentWorkflowArtifactBindings["']\s*:/.test(object2) || /\[\s*["']clientAgentWorkflowArtifactBindings["']\s*\]/.test(object2)) {
10254
10188
  return "Agent Runtime \u5DF2\u5B58\u5728\u52A8\u6001 Workflow binding property";
10255
10189
  }
10256
10190
  return;
@@ -10285,266 +10219,451 @@ function addAiRuntimeTrajectoryTestFixture(source) {
10285
10219
  };
10286
10220
  }
10287
10221
 
10288
- // src/update/recipe-project-owned.ts
10289
- var managedPathsByRecipe = {
10290
- "starter-0.6.8-to-0.6.9": [
10291
- "apps/web/src/router/index.ts",
10292
- "apps/web/src/router/platform-admin-route-bindings.ts"
10293
- ],
10294
- "starter-0.6.13-to-0.6.14": [
10295
- "apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunDetailDialog.webtest.ts",
10296
- "apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunDetailDialog.vue",
10297
- "apps/web/src/router/index.ts",
10298
- "apps/web/src/router/platform-admin-route-bindings.ts",
10299
- "apps/web/src/services/ai-runtime-api.ts"
10300
- ],
10301
- "starter-0.6.14-to-0.6.15": [
10302
- clientWorkflowCompositionPath,
10303
- clientWorkflowBindingPath
10304
- ]
10305
- };
10306
- function recipeManagedProjectOwnedPaths(recipes2) {
10307
- return new Set(recipes2.flatMap((recipe) => managedPathsByRecipe[recipe] ?? []));
10222
+ // src/update/recipe-project-owned.ts
10223
+ var managedPathsByRecipe = {
10224
+ "starter-0.9.1-to-0.10.0": adminApiRecipePaths,
10225
+ "starter-0.6.8-to-0.6.9": [
10226
+ "apps/web/src/router/index.ts",
10227
+ "apps/web/src/router/platform-admin-route-bindings.ts"
10228
+ ],
10229
+ "starter-0.6.13-to-0.6.14": [
10230
+ "apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunDetailDialog.webtest.ts",
10231
+ "apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunDetailDialog.vue",
10232
+ "apps/web/src/router/index.ts",
10233
+ "apps/web/src/router/platform-admin-route-bindings.ts",
10234
+ "apps/web/src/services/ai-runtime-api.ts"
10235
+ ],
10236
+ "starter-0.6.14-to-0.6.15": [
10237
+ clientWorkflowCompositionPath,
10238
+ clientWorkflowBindingPath
10239
+ ]
10240
+ };
10241
+ function recipeManagedProjectOwnedPaths(recipes2) {
10242
+ return new Set(recipes2.flatMap((recipe) => managedPathsByRecipe[recipe] ?? []));
10243
+ }
10244
+ function recipeCanManageMissingProjectOwnedPath(path2) {
10245
+ return path2 === clientWorkflowCompositionPath || path2 === clientWorkflowBindingPath;
10246
+ }
10247
+ async function planRecipeProjectOwned(input) {
10248
+ if (input.path === clientWorkflowCompositionPath || input.path === clientWorkflowBindingPath) {
10249
+ if (!input.target) {
10250
+ return conflict4(input, "\u7248\u672C recipe \u6240\u9700\u7684\u76EE\u6807\u6587\u4EF6\u7F3A\u5931");
10251
+ }
10252
+ return planClientWorkflowProjectOwned({ ...input, target: input.target });
10253
+ }
10254
+ if (!input.ours) {
10255
+ return conflict4(input, "\u7248\u672C recipe \u6240\u9700\u7684\u9879\u76EE\u951A\u70B9\u7F3A\u5931");
10256
+ }
10257
+ const source = new TextDecoder().decode(input.ours);
10258
+ const target = input.target ? new TextDecoder().decode(input.target) : undefined;
10259
+ const transformed = transformProjectOwnedAnchor(input.path, source, target);
10260
+ if ("error" in transformed)
10261
+ return conflict4(input, transformed.error);
10262
+ if (transformed.content === source) {
10263
+ return preserve(input, "\u5DF2\u5305\u542B\u7248\u672C recipe \u76EE\u6807\u5185\u5BB9");
10264
+ }
10265
+ return update(input, new TextEncoder().encode(transformed.content));
10266
+ }
10267
+ function transformProjectOwnedAnchor(path2, source, target) {
10268
+ if (isAdminApiRecipePath(path2))
10269
+ return addAdminApiExports(path2, source, target);
10270
+ if (path2 === "apps/web/src/router/index.ts") {
10271
+ return addComponentPageLoader(source, target);
10272
+ }
10273
+ if (path2 === "apps/web/src/router/platform-admin-route-bindings.ts") {
10274
+ return addComponentRouteBinding(source, target);
10275
+ }
10276
+ if (path2 === "apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunDetailDialog.webtest.ts") {
10277
+ return addAiRuntimeTrajectoryTestFixture(source);
10278
+ }
10279
+ if (path2 === "apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunDetailDialog.vue") {
10280
+ return addAiRuntimeTrajectoryView(source);
10281
+ }
10282
+ if (path2 === "apps/web/src/services/ai-runtime-api.ts") {
10283
+ return addAiRuntimeTrajectoryContract(source);
10284
+ }
10285
+ return { error: `\u7248\u672C recipe \u672A\u767B\u8BB0\u9879\u76EE\u951A\u70B9\uFF1A${path2}` };
10286
+ }
10287
+ function addAiRuntimeTrajectoryContract(source) {
10288
+ let content = source;
10289
+ const typeField = ' readonly trajectory: RuntimeRunDetail["trajectory"];';
10290
+ if (!content.includes(typeField)) {
10291
+ const anchors = [
10292
+ ...content.matchAll(/^(\s*)readonly attemptItems: readonly AiRuntimeAttempt\[\];$/gm)
10293
+ ];
10294
+ if (anchors.length !== 1) {
10295
+ return { error: "AiRuntimeRunDetail attemptItems \u951A\u70B9\u4E0D\u552F\u4E00" };
10296
+ }
10297
+ content = content.replace(anchors[0][0], `${anchors[0][0]}
10298
+ ${anchors[0][1]}readonly trajectory: RuntimeRunDetail["trajectory"];`);
10299
+ }
10300
+ const mapperField = " trajectory: value.trajectory,";
10301
+ if (!content.includes(mapperField)) {
10302
+ const anchors = [
10303
+ ...content.matchAll(/^(\s*)attemptItems: value\.attempts\.map\(attemptItem\),$/gm)
10304
+ ];
10305
+ if (anchors.length !== 1) {
10306
+ return { error: "runDetail attemptItems mapper \u951A\u70B9\u4E0D\u552F\u4E00" };
10307
+ }
10308
+ content = content.replace(anchors[0][0], `${anchors[0][0]}
10309
+ ${anchors[0][1]}trajectory: value.trajectory,`);
10310
+ }
10311
+ return { content };
10312
+ }
10313
+ function addAiRuntimeTrajectoryView(source) {
10314
+ let content = source;
10315
+ const componentImport = 'import AiRuntimeTrajectory from "./AiRuntimeTrajectory.vue";';
10316
+ if (!content.includes(componentImport)) {
10317
+ const anchors = [
10318
+ ...content.matchAll(/^} from "\.\.\/\.\.\/ai-runtime-presenter";$/gm)
10319
+ ];
10320
+ if (anchors.length !== 1) {
10321
+ return { error: "AI Runtime presenter import \u951A\u70B9\u4E0D\u552F\u4E00" };
10322
+ }
10323
+ content = content.replace(anchors[0][0], `${anchors[0][0]}
10324
+ ${componentImport}`);
10325
+ }
10326
+ const componentUsage = '<AiRuntimeTrajectory :trajectory="detail.trajectory" />';
10327
+ if (!content.includes(componentUsage)) {
10328
+ const providerSections = [
10329
+ ...content.matchAll(/^(\s*)<section class="space-y-3">\n\s*<h3 class="text-sm font-medium">Provider Attempts<\/h3>$/gm)
10330
+ ];
10331
+ if (providerSections.length !== 1) {
10332
+ return { error: "Provider Attempts section \u951A\u70B9\u4E0D\u552F\u4E00" };
10333
+ }
10334
+ const providerSection = providerSections[0];
10335
+ content = content.replace(providerSection[0], `${providerSection[1]}${componentUsage}
10336
+
10337
+ ${providerSection[0]}`);
10338
+ }
10339
+ return { content };
10340
+ }
10341
+ function addComponentPageLoader(source, target) {
10342
+ const desired = [
10343
+ {
10344
+ key: "ui-components",
10345
+ value: '() => import("@/pages/ui/UiComponentPage.vue")'
10346
+ },
10347
+ ...target?.includes('"agent-session": AgentSessionPage') ? [
10348
+ {
10349
+ key: "agent-session",
10350
+ value: '() => import("@/pages/system/AgentSessionPage.vue")'
10351
+ },
10352
+ {
10353
+ key: "agent-workflow",
10354
+ value: '() => import("@/pages/system/AgentWorkflowPage.vue")'
10355
+ }
10356
+ ] : []
10357
+ ];
10358
+ const missing = desired.filter(({ key }) => !new RegExp(`^\\s*"${key}"\\s*:`, "m").test(source));
10359
+ if (!missing.length)
10360
+ return { content: source };
10361
+ const declaration = source.indexOf("const platformAdminPageComponents");
10362
+ const start = source.indexOf("= {", declaration);
10363
+ const end = source.indexOf(`
10364
+ };`, start);
10365
+ if (declaration < 0 || start < 0 || end < 0) {
10366
+ return { error: "\u627E\u4E0D\u5230 platformAdminPageComponents \u9879\u76EE\u951A\u70B9" };
10367
+ }
10368
+ const block = source.slice(start, end);
10369
+ const profiles = [...block.matchAll(/^(\s*)profile:\s*.*,$/gm)];
10370
+ if (profiles.length !== 1) {
10371
+ return { error: "platformAdminPageComponents profile \u951A\u70B9\u4E0D\u552F\u4E00" };
10372
+ }
10373
+ const profile = profiles[0];
10374
+ const insertion = [
10375
+ profile[0],
10376
+ ...missing.map(({ key, value }) => `${profile[1]}"${key}": ${value},`)
10377
+ ].join(`
10378
+ `);
10379
+ return {
10380
+ content: source.slice(0, start) + block.replace(profile[0], insertion) + source.slice(end)
10381
+ };
10382
+ }
10383
+ function addComponentRouteBinding(source, target) {
10384
+ const unionStart = source.indexOf("export type PlatformAdminPageKey =");
10385
+ const interfaceStart = source.indexOf("export interface PlatformAdminRouteBinding", unionStart);
10386
+ if (unionStart < 0 || interfaceStart < 0) {
10387
+ return { error: "\u627E\u4E0D\u5230 PlatformAdminPageKey \u9879\u76EE\u951A\u70B9" };
10388
+ }
10389
+ const desiredPages = [
10390
+ "ui-components",
10391
+ ...target?.includes('routeKey: "system.agent-session"') ? ["agent-session", "agent-workflow"] : []
10392
+ ];
10393
+ let content = source;
10394
+ const union = content.slice(unionStart, interfaceStart);
10395
+ const missingPages = desiredPages.filter((page) => !union.includes(`"${page}"`));
10396
+ if (missingPages.length) {
10397
+ const semicolon = content.lastIndexOf(";", interfaceStart);
10398
+ if (semicolon < unionStart) {
10399
+ return { error: "PlatformAdminPageKey \u7ED3\u675F\u951A\u70B9\u7F3A\u5931" };
10400
+ }
10401
+ content = `${content.slice(0, semicolon)}
10402
+ ${missingPages.map((page) => ` | "${page}"`).join(`
10403
+ `)}${content.slice(semicolon)}`;
10404
+ }
10405
+ const desiredBindings = [
10406
+ { routeKey: "system.ui-components", page: "ui-components" },
10407
+ ...target?.includes('routeKey: "system.agent-session"') ? [
10408
+ { routeKey: "system.agent-session", page: "agent-session" },
10409
+ { routeKey: "system.agent-workflow", page: "agent-workflow" }
10410
+ ] : []
10411
+ ];
10412
+ const missingBindings = desiredBindings.filter(({ routeKey }) => !content.includes(`routeKey: "${routeKey}"`));
10413
+ if (!missingBindings.length)
10414
+ return { content };
10415
+ const features = [
10416
+ ...content.matchAll(/^(\s*)\{ routeKey: "system\.feature", page: "resource", resourceKey: "features" \},$/gm)
10417
+ ];
10418
+ if (features.length !== 1) {
10419
+ return { error: "system.feature Admin Route \u951A\u70B9\u4E0D\u552F\u4E00" };
10420
+ }
10421
+ const feature = features[0];
10422
+ return {
10423
+ content: content.replace(feature[0], [
10424
+ feature[0],
10425
+ ...missingBindings.map(({ routeKey, page }) => `${feature[1]}{ routeKey: "${routeKey}", page: "${page}" },`)
10426
+ ].join(`
10427
+ `))
10428
+ };
10429
+ }
10430
+ function preserve(input, reason) {
10431
+ return {
10432
+ path: input.path,
10433
+ ownership: "project-owned",
10434
+ action: "preserve",
10435
+ reason,
10436
+ expectedProjectContent: input.expected
10437
+ };
10438
+ }
10439
+ function update(input, content) {
10440
+ return {
10441
+ path: input.path,
10442
+ ownership: "project-owned",
10443
+ action: "update",
10444
+ reason: "\u7248\u672C recipe \u66F4\u65B0\u9879\u76EE\u6269\u5C55\u951A\u70B9",
10445
+ expectedProjectContent: input.expected,
10446
+ content
10447
+ };
10448
+ }
10449
+ function conflict4(input, message) {
10450
+ return {
10451
+ path: input.path,
10452
+ ownership: "project-owned",
10453
+ action: "preserve",
10454
+ reason: "\u5BA2\u6237\u5DF2\u4FEE\u6539",
10455
+ expectedProjectContent: input.expected,
10456
+ conflict: message
10457
+ };
10458
+ }
10459
+
10460
+ // src/update/dsh-platform-recipe.ts
10461
+ import { join as join22 } from "path";
10462
+ var dshPlatformRecipeId = "dsh-platform-v1";
10463
+ var dshPlatformRecipePaths = new Set([
10464
+ "apps/server/src/app/agent-workflow-presets.ts",
10465
+ "apps/server/src/app/workbench-public-origin.ts",
10466
+ "apps/web/src/router/index.ts",
10467
+ "apps/web/src/router/platform-admin-pages.ts",
10468
+ "apps/web/src/router/platform-admin-route-bindings.ts",
10469
+ "apps/web/src/composables/useLoginFlow.ts",
10470
+ "apps/web/src/pages/system/AgentWorkflowPage.vue",
10471
+ "apps/web/src/services/agent-workflow-api.ts",
10472
+ "apps/web/vite.config.ts",
10473
+ "apps/web/server.ts",
10474
+ "apps/web/runtime-server.ts",
10475
+ "apps/web/dev/start.ts"
10476
+ ]);
10477
+ function isDshPlatformRecipePath(path2) {
10478
+ return dshPlatformRecipePaths.has(path2) || path2.startsWith("apps/server/src/app/dsh-") && path2.endsWith(".ts");
10308
10479
  }
10309
- function recipeCanManageMissingProjectOwnedPath(path2) {
10310
- return path2 === clientWorkflowCompositionPath || path2 === clientWorkflowBindingPath;
10480
+ async function isDshPlatformTransition(input) {
10481
+ if (!input.sourceModules.includes("system.agent") || !input.targetModules.includes("system.agent"))
10482
+ return false;
10483
+ const path2 = "apps/agent-server/package.json";
10484
+ const [base, target] = await Promise.all([
10485
+ readOptional(join22(input.baseDirectory, path2)),
10486
+ readOptional(join22(input.targetDirectory, path2))
10487
+ ]);
10488
+ return hasDsh(target) && !hasDsh(base);
10311
10489
  }
10312
- async function planRecipeProjectOwned(input) {
10313
- if (input.path === clientWorkflowCompositionPath || input.path === clientWorkflowBindingPath) {
10314
- if (!input.target) {
10315
- return conflict4(input, "\u7248\u672C recipe \u6240\u9700\u7684\u76EE\u6807\u6587\u4EF6\u7F3A\u5931");
10316
- }
10317
- return planClientWorkflowProjectOwned({ ...input, target: input.target });
10490
+ function planDshPlatformFile(input) {
10491
+ const common2 = {
10492
+ path: input.path,
10493
+ ownership: input.ownership,
10494
+ expectedProjectContent: input.expected
10495
+ };
10496
+ if (input.target && input.ours && hashContent(input.ours) === hashContent(input.target)) {
10497
+ return {
10498
+ ...common2,
10499
+ action: "preserve",
10500
+ reason: "\u5DF2\u5305\u542B DSH \u5E73\u53F0\u63A5\u7EBF\u76EE\u6807\u5185\u5BB9"
10501
+ };
10318
10502
  }
10319
- if (!input.ours) {
10320
- return conflict4(input, "\u7248\u672C recipe \u6240\u9700\u7684\u9879\u76EE\u951A\u70B9\u7F3A\u5931");
10503
+ if (input.target && !input.ours && !input.base) {
10504
+ return {
10505
+ ...common2,
10506
+ action: "create",
10507
+ reason: "DSH \u5E73\u53F0\u65B0\u589E\u9879\u76EE\u6269\u5C55\u70B9",
10508
+ content: input.target
10509
+ };
10321
10510
  }
10322
- const source = new TextDecoder().decode(input.ours);
10323
- const target = input.target ? new TextDecoder().decode(input.target) : undefined;
10324
- const transformed = transformProjectOwnedAnchor(input.path, source, target);
10325
- if ("error" in transformed)
10326
- return conflict4(input, transformed.error);
10327
- if (transformed.content === source) {
10328
- return preserve(input, "\u5DF2\u5305\u542B\u7248\u672C recipe \u76EE\u6807\u5185\u5BB9");
10511
+ if (input.target && input.base && input.ours && hashContent(input.ours) === hashContent(input.base)) {
10512
+ return {
10513
+ ...common2,
10514
+ action: "update",
10515
+ reason: "DSH \u5E73\u53F0\u5347\u7EA7\u7CBE\u786E\u6765\u6E90\u63A5\u7EBF",
10516
+ content: input.target
10517
+ };
10329
10518
  }
10330
- return update(input, new TextEncoder().encode(transformed.content));
10519
+ return {
10520
+ ...common2,
10521
+ action: "preserve",
10522
+ reason: "\u4FDD\u7559\u5BA2\u6237 DSH \u63A5\u7EBF",
10523
+ conflict: "DSH \u5E73\u53F0\u63A5\u7EBF\u4E0E\u7CBE\u786E\u6765\u6E90\u6216\u76EE\u6807\u4E0D\u4E00\u81F4\uFF1B\u8BF7\u5BA1\u6838\u9879\u76EE\u4FEE\u6539\u540E\u663E\u5F0F\u96C6\u6210\uFF0C\u7981\u6B62\u731C\u6D4B\u8986\u76D6"
10524
+ };
10331
10525
  }
10332
- function transformProjectOwnedAnchor(path2, source, target) {
10333
- if (path2 === "apps/web/src/router/index.ts") {
10334
- return addComponentPageLoader(source, target);
10335
- }
10336
- if (path2 === "apps/web/src/router/platform-admin-route-bindings.ts") {
10337
- return addComponentRouteBinding(source, target);
10338
- }
10339
- if (path2 === "apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunDetailDialog.webtest.ts") {
10340
- return addAiRuntimeTrajectoryTestFixture(source);
10341
- }
10342
- if (path2 === "apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunDetailDialog.vue") {
10343
- return addAiRuntimeTrajectoryView(source);
10344
- }
10345
- if (path2 === "apps/web/src/services/ai-runtime-api.ts") {
10346
- return addAiRuntimeTrajectoryContract(source);
10347
- }
10348
- return { error: `\u7248\u672C recipe \u672A\u767B\u8BB0\u9879\u76EE\u951A\u70B9\uFF1A${path2}` };
10526
+ function hasDsh(content) {
10527
+ if (!content)
10528
+ return false;
10529
+ const value = JSON.parse(new TextDecoder().decode(content));
10530
+ return typeof value.dependencies?.["@deepseek-ai/dsh"] === "string";
10349
10531
  }
10350
- function addAiRuntimeTrajectoryContract(source) {
10351
- let content = source;
10352
- const typeField = ' readonly trajectory: RuntimeRunDetail["trajectory"];';
10353
- if (!content.includes(typeField)) {
10354
- const anchors = [
10355
- ...content.matchAll(/^(\s*)readonly attemptItems: readonly AiRuntimeAttempt\[\];$/gm)
10356
- ];
10357
- if (anchors.length !== 1) {
10358
- return { error: "AiRuntimeRunDetail attemptItems \u951A\u70B9\u4E0D\u552F\u4E00" };
10532
+
10533
+ // src/update/planner-file.ts
10534
+ async function planFile(path2, baseRoot, targetRoot, source, target, ours, provenance, targetContentOverride, manageGeneratedProjectOwned = false, forcePlatformBaseline = false, manageRecipeProjectOwned = false, migrateDshPlatform = false) {
10535
+ const [base, theirs] = await Promise.all([
10536
+ readOptional2(join23(baseRoot, path2)),
10537
+ targetContentOverride ? Promise.resolve(targetContentOverride) : readOptional2(join23(targetRoot, path2))
10538
+ ]);
10539
+ const ownership = source?.ownership ?? target?.ownership ?? classifyOwnership(path2);
10540
+ const expected = expectationForSnapshot(ours);
10541
+ if (migrateDshPlatform && isDshPlatformRecipePath(path2)) {
10542
+ return planDshPlatformFile({
10543
+ path: path2,
10544
+ ownership,
10545
+ ours,
10546
+ base,
10547
+ target: theirs,
10548
+ expected
10549
+ });
10550
+ }
10551
+ if (ownership === "project-owned") {
10552
+ const hasExistingRecipeSource = source !== undefined && source.state !== "missing" && ours !== undefined;
10553
+ if (manageRecipeProjectOwned && target && (hasExistingRecipeSource || source !== undefined && isAdminApiRecipePath(path2) || recipeCanManageMissingProjectOwnedPath(path2))) {
10554
+ return planRecipeProjectOwned({ path: path2, ours, target: theirs, expected });
10359
10555
  }
10360
- content = content.replace(anchors[0][0], `${anchors[0][0]}
10361
- ${anchors[0][1]}readonly trajectory: RuntimeRunDetail["trajectory"];`);
10556
+ if (manageGeneratedProjectOwned) {
10557
+ return planGeneratedProjectOwned(path2, source, target, ours, theirs, expected);
10558
+ }
10559
+ return !source && !ours && theirs ? readyPlannedFile(path2, ownership, "create", "\u65B0\u589E\u9879\u76EE\u6269\u5C55\u70B9", expected, theirs) : readyPlannedFile(path2, ownership, "preserve", "\u9ED8\u8BA4\u4FDD\u7559\u9879\u76EE\u7248\u672C", expected);
10362
10560
  }
10363
- const mapperField = " trajectory: value.trajectory,";
10364
- if (!content.includes(mapperField)) {
10365
- const anchors = [
10366
- ...content.matchAll(/^(\s*)attemptItems: value\.attempts\.map\(attemptItem\),$/gm)
10367
- ];
10368
- if (anchors.length !== 1) {
10369
- return { error: "runDetail attemptItems mapper \u951A\u70B9\u4E0D\u552F\u4E00" };
10561
+ if (path2.startsWith("packages/database/drizzle/") && path2.endsWith(".sql")) {
10562
+ if (source && !target) {
10563
+ return conflict(path2, ownership, "\u5DF2\u53D1\u5E03 migration \u4E0D\u53EF\u5220\u9664", expected);
10564
+ }
10565
+ if (source && target && source.hash !== target.hash) {
10566
+ return conflict(path2, ownership, "\u5DF2\u53D1\u5E03 migration \u4E0D\u53EF\u6539\u5199\uFF0C\u53EA\u80FD\u8FFD\u52A0\u65B0\u6587\u4EF6", expected);
10370
10567
  }
10371
- content = content.replace(anchors[0][0], `${anchors[0][0]}
10372
- ${anchors[0][1]}trajectory: value.trajectory,`);
10373
10568
  }
10374
- return { content };
10375
- }
10376
- function addAiRuntimeTrajectoryView(source) {
10377
- let content = source;
10378
- const componentImport = 'import AiRuntimeTrajectory from "./AiRuntimeTrajectory.vue";';
10379
- if (!content.includes(componentImport)) {
10380
- const anchors = [
10381
- ...content.matchAll(/^} from "\.\.\/\.\.\/ai-runtime-presenter";$/gm)
10382
- ];
10383
- if (anchors.length !== 1) {
10384
- return { error: "AI Runtime presenter import \u951A\u70B9\u4E0D\u552F\u4E00" };
10569
+ if (!source && target) {
10570
+ if (!ours) {
10571
+ return readyPlannedFile(path2, ownership, "create", "\u76EE\u6807\u7248\u672C\u65B0\u589E", expected, theirs);
10385
10572
  }
10386
- content = content.replace(anchors[0][0], `${anchors[0][0]}
10387
- ${componentImport}`);
10573
+ 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);
10388
10574
  }
10389
- const componentUsage = '<AiRuntimeTrajectory :trajectory="detail.trajectory" />';
10390
- if (!content.includes(componentUsage)) {
10391
- const providerSections = [
10392
- ...content.matchAll(/^(\s*)<section class="space-y-3">\n\s*<h3 class="text-sm font-medium">Provider Attempts<\/h3>$/gm)
10393
- ];
10394
- if (providerSections.length !== 1) {
10395
- return { error: "Provider Attempts section \u951A\u70B9\u4E0D\u552F\u4E00" };
10575
+ if (source && !target) {
10576
+ if (!ours) {
10577
+ return readyPlannedFile(path2, ownership, "preserve", "\u9879\u76EE\u5DF2\u5220\u9664", expected);
10396
10578
  }
10397
- const providerSection = providerSections[0];
10398
- content = content.replace(providerSection[0], `${providerSection[1]}${componentUsage}
10399
-
10400
- ${providerSection[0]}`);
10579
+ 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);
10580
+ }
10581
+ if (source && target && !ours) {
10582
+ return readyPlannedFile(path2, "project-owned", "preserve", "\u9879\u76EE\u5DF2\u5220\u9664", expected);
10583
+ }
10584
+ if (!source || !target || !ours || !theirs) {
10585
+ return conflict(path2, ownership, "\u6587\u4EF6\u72B6\u6001\u4E0D\u5B8C\u6574\uFF0C\u4E0D\u80FD\u5B89\u5168\u63A8\u65AD", expected);
10586
+ }
10587
+ const oursChanged = hashContent(ours) !== source.hash;
10588
+ const sourceArtifactHash = artifactHashOf(source);
10589
+ const targetChanged = targetChangedSinceBase(sourceArtifactHash, base, theirs);
10590
+ const adoptedProjectDelta = source.artifactHash !== undefined && source.artifactHash !== source.hash;
10591
+ const forceTargetBaseline = !adoptedProjectDelta && shouldForcePlatformBaseline({
10592
+ forcePlatformBaseline,
10593
+ oursChanged,
10594
+ sourceArtifactHash,
10595
+ base
10596
+ });
10597
+ if (forceTargetBaseline && path2.endsWith("package.json") && base) {
10598
+ const merged = mergePackageJson(base, ours, theirs);
10599
+ 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);
10600
+ }
10601
+ if (forceTargetBaseline) {
10602
+ return readyPlannedFile(path2, ownership, "update", "\u5F3A\u5236\u91C7\u7528\u76EE\u6807\u5E73\u53F0\u57FA\u7EBF", expected, theirs);
10603
+ }
10604
+ if (!oursChanged && !targetChanged) {
10605
+ return readyPlannedFile(path2, ownership, "preserve", "\u53CC\u65B9\u672A\u4FEE\u6539", expected);
10401
10606
  }
10402
- return { content };
10403
- }
10404
- function addComponentPageLoader(source, target) {
10405
- const desired = [
10406
- {
10407
- key: "ui-components",
10408
- value: '() => import("@/pages/ui/UiComponentPage.vue")'
10409
- },
10410
- ...target?.includes('"agent-session": AgentSessionPage') ? [
10411
- {
10412
- key: "agent-session",
10413
- value: '() => import("@/pages/system/AgentSessionPage.vue")'
10414
- },
10415
- {
10416
- key: "agent-workflow",
10417
- value: '() => import("@/pages/system/AgentWorkflowPage.vue")'
10418
- }
10419
- ] : []
10420
- ];
10421
- const missing = desired.filter(({ key }) => !new RegExp(`^\\s*"${key}"\\s*:`, "m").test(source));
10422
- if (!missing.length)
10423
- return { content: source };
10424
- const declaration = source.indexOf("const platformAdminPageComponents");
10425
- const start = source.indexOf("= {", declaration);
10426
- const end = source.indexOf(`
10427
- };`, start);
10428
- if (declaration < 0 || start < 0 || end < 0) {
10429
- return { error: "\u627E\u4E0D\u5230 platformAdminPageComponents \u9879\u76EE\u951A\u70B9" };
10607
+ if (targetChanged && path2.endsWith("package.json") && base) {
10608
+ const merged = mergePackageJson(base, ours, theirs);
10609
+ 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);
10430
10610
  }
10431
- const block = source.slice(start, end);
10432
- const profiles = [...block.matchAll(/^(\s*)profile:\s*.*,$/gm)];
10433
- if (profiles.length !== 1) {
10434
- return { error: "platformAdminPageComponents profile \u951A\u70B9\u4E0D\u552F\u4E00" };
10611
+ if (!oursChanged && targetChanged && adoptedProjectDelta && base) {
10612
+ const merged = await mergeTextFile(base, ours, theirs);
10613
+ return merged.content ? readyPlannedFile(path2, ownership, "update", "\u4E09\u65B9\u5408\u5E76\u5DF2\u91C7\u7EB3\u9879\u76EE\u589E\u91CF", expected, merged.content) : conflict(path2, ownership, merged.conflict || "\u4E09\u65B9\u5408\u5E76\u5931\u8D25", expected);
10435
10614
  }
10436
- const profile = profiles[0];
10437
- const insertion = [
10438
- profile[0],
10439
- ...missing.map(({ key, value }) => `${profile[1]}"${key}": ${value},`)
10440
- ].join(`
10441
- `);
10442
- return {
10443
- content: source.slice(0, start) + block.replace(profile[0], insertion) + source.slice(end)
10444
- };
10445
- }
10446
- function addComponentRouteBinding(source, target) {
10447
- const unionStart = source.indexOf("export type PlatformAdminPageKey =");
10448
- const interfaceStart = source.indexOf("export interface PlatformAdminRouteBinding", unionStart);
10449
- if (unionStart < 0 || interfaceStart < 0) {
10450
- return { error: "\u627E\u4E0D\u5230 PlatformAdminPageKey \u9879\u76EE\u951A\u70B9" };
10615
+ if (!oursChanged) {
10616
+ return readyPlannedFile(path2, ownership, "update", "\u4EC5 Windy \u4FEE\u6539", expected, theirs);
10451
10617
  }
10452
- const desiredPages = [
10453
- "ui-components",
10454
- ...target?.includes('routeKey: "system.agent-session"') ? ["agent-session", "agent-workflow"] : []
10455
- ];
10456
- let content = source;
10457
- const union = content.slice(unionStart, interfaceStart);
10458
- const missingPages = desiredPages.filter((page) => !union.includes(`"${page}"`));
10459
- if (missingPages.length) {
10460
- const semicolon = content.lastIndexOf(";", interfaceStart);
10461
- if (semicolon < unionStart) {
10462
- return { error: "PlatformAdminPageKey \u7ED3\u675F\u951A\u70B9\u7F3A\u5931" };
10463
- }
10464
- content = `${content.slice(0, semicolon)}
10465
- ${missingPages.map((page) => ` | "${page}"`).join(`
10466
- `)}${content.slice(semicolon)}`;
10618
+ if (!targetChanged) {
10619
+ return readyPlannedFile(path2, ownership, "preserve", "\u5BA2\u6237\u5DF2\u4FEE\u6539", expected);
10467
10620
  }
10468
- const desiredBindings = [
10469
- { routeKey: "system.ui-components", page: "ui-components" },
10470
- ...target?.includes('routeKey: "system.agent-session"') ? [
10471
- { routeKey: "system.agent-session", page: "agent-session" },
10472
- { routeKey: "system.agent-workflow", page: "agent-workflow" }
10473
- ] : []
10474
- ];
10475
- const missingBindings = desiredBindings.filter(({ routeKey }) => !content.includes(`routeKey: "${routeKey}"`));
10476
- if (!missingBindings.length)
10477
- return { content };
10478
- const features = [
10479
- ...content.matchAll(/^(\s*)\{ routeKey: "system\.feature", page: "resource", resourceKey: "features" \},$/gm)
10480
- ];
10481
- if (features.length !== 1) {
10482
- return { error: "system.feature Admin Route \u951A\u70B9\u4E0D\u552F\u4E00" };
10621
+ if (hashContent(ours) === target.hash) {
10622
+ return readyPlannedFile(path2, ownership, "preserve", "\u5DF2\u5305\u542B\u76EE\u6807\u5185\u5BB9", expected);
10483
10623
  }
10484
- const feature = features[0];
10485
- return {
10486
- content: content.replace(feature[0], [
10487
- feature[0],
10488
- ...missingBindings.map(({ routeKey, page }) => `${feature[1]}{ routeKey: "${routeKey}", page: "${page}" },`)
10489
- ].join(`
10490
- `))
10491
- };
10624
+ if (base) {
10625
+ const merged = await mergeTextFile(base, ours, theirs);
10626
+ 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);
10627
+ }
10628
+ return conflict(path2, ownership, "\u5BA2\u6237\u548C Windy \u5747\u5DF2\u4FEE\u6539", expected);
10492
10629
  }
10493
- function preserve(input, reason) {
10494
- return {
10495
- path: input.path,
10496
- ownership: "project-owned",
10497
- action: "preserve",
10498
- reason,
10499
- expectedProjectContent: input.expected
10500
- };
10630
+ function shouldForcePlatformBaseline(input) {
10631
+ return input.forcePlatformBaseline && !input.oursChanged && input.base !== undefined && hashContent(input.base) !== input.sourceArtifactHash;
10501
10632
  }
10502
- function update(input, content) {
10503
- return {
10504
- path: input.path,
10505
- ownership: "project-owned",
10506
- action: "update",
10507
- reason: "\u7248\u672C recipe \u66F4\u65B0\u9879\u76EE\u6269\u5C55\u951A\u70B9",
10508
- expectedProjectContent: input.expected,
10509
- content
10510
- };
10633
+ function targetChangedSinceBase(sourceHash, base, target) {
10634
+ return base && target ? hashContent(base) !== hashContent(target) : !!target && hashContent(target) !== sourceHash;
10511
10635
  }
10512
- function conflict4(input, message) {
10513
- return {
10514
- path: input.path,
10515
- ownership: "project-owned",
10516
- action: "preserve",
10517
- reason: "\u5BA2\u6237\u5DF2\u4FEE\u6539",
10518
- expectedProjectContent: input.expected,
10519
- conflict: message
10520
- };
10636
+ async function readOptional2(path2) {
10637
+ try {
10638
+ return await readFile16(path2);
10639
+ } catch (error) {
10640
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
10641
+ return;
10642
+ }
10643
+ throw error;
10644
+ }
10521
10645
  }
10522
10646
 
10523
- // src/update/dsh-platform-recipe.ts
10524
- import { join as join26 } from "path";
10525
-
10526
10647
  // src/update/planner-snapshots.ts
10527
- import { readFile as readFile20, stat as stat2 } from "fs/promises";
10528
- import { join as join25 } from "path";
10529
10648
  async function snapshotProjectFiles(project, paths, seed) {
10530
10649
  const snapshots = new Map(seed);
10531
10650
  await Promise.all([...paths].filter((path2) => !snapshots.has(path2)).map(async (path2) => {
10532
- snapshots.set(path2, await readOptional(join25(project, path2)));
10651
+ snapshots.set(path2, await readOptional(join24(project, path2)));
10533
10652
  }));
10534
10653
  return snapshots;
10535
10654
  }
10536
10655
  async function snapshotProjectModes(project, paths) {
10537
- return new Map(await Promise.all([...paths].map(async (path2) => [path2, await readModeOptional(join25(project, path2))])));
10656
+ return new Map(await Promise.all([...paths].map(async (path2) => [path2, await readModeOptional(join24(project, path2))])));
10538
10657
  }
10539
10658
  async function assertPlanSnapshots(project, expectations) {
10540
10659
  const changed = [];
10541
10660
  for (const expectation2 of expectations) {
10542
- const actual = expectationForSnapshot(await readOptional(join25(project, expectation2.path)));
10661
+ const actual = expectationForSnapshot(await readOptional(join24(project, expectation2.path)));
10543
10662
  if (JSON.stringify(actual) !== JSON.stringify(expectation2.expected)) {
10544
10663
  changed.push(expectation2.path);
10545
10664
  continue;
10546
10665
  }
10547
- if ("mode" in expectation2 && await readModeOptional(join25(project, expectation2.path)) !== expectation2.mode) {
10666
+ if ("mode" in expectation2 && await readModeOptional(join24(project, expectation2.path)) !== expectation2.mode) {
10548
10667
  changed.push(expectation2.path);
10549
10668
  }
10550
10669
  }
@@ -10574,204 +10693,573 @@ async function readSourceParentMode(path2) {
10574
10693
  throw error;
10575
10694
  }
10576
10695
  }
10577
- async function readOptional(path2) {
10696
+ async function readOptional(path2) {
10697
+ try {
10698
+ return await readFile17(path2);
10699
+ } catch (error) {
10700
+ if (isMissing7(error))
10701
+ return;
10702
+ throw error;
10703
+ }
10704
+ }
10705
+ function isMissing7(error) {
10706
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
10707
+ }
10708
+
10709
+ // src/update/admin-settings-yaml-plan.ts
10710
+ var transferPath = "apps/web/src/pages/settings/WatermarkYamlTransfer.vue";
10711
+ var settingsPaths = [
10712
+ "apps/web/src/composables/useWatermarkSettings.ts",
10713
+ "apps/web/src/pages/settings/GlobalSettingsPage.vue",
10714
+ "apps/web/src/pages/settings/WatermarkSettingsSection.vue",
10715
+ "apps/web/src/pages/settings/BrandingSettingsSection.vue"
10716
+ ];
10717
+ async function reconcileAdminSettingsYamlPlan(input) {
10718
+ if (input.sourceEntries.has(transferPath) || !input.targetEntries.has(transferPath))
10719
+ return;
10720
+ for (const path2 of settingsPaths) {
10721
+ const plan = input.files.find((file) => file.path === path2);
10722
+ if (!plan || !input.sourceEntries.has(path2))
10723
+ continue;
10724
+ const [base, target] = await Promise.all([
10725
+ readOptional(join25(input.baseDirectory, path2)),
10726
+ readOptional(join25(input.targetDirectory, path2))
10727
+ ]);
10728
+ const current = input.projectSnapshots.get(path2);
10729
+ if (current && target && hashContent(current) === hashContent(target))
10730
+ continue;
10731
+ if (!base || !current || !target || hashContent(base) !== hashContent(current)) {
10732
+ plan.conflict = "\u5168\u5C40\u8BBE\u7F6E YAML \u8FC1\u79FB\u9700\u8981\u5408\u5E76\u5DF2\u81EA\u5B9A\u4E49\u7684\u9879\u76EE\u6587\u4EF6";
10733
+ continue;
10734
+ }
10735
+ plan.action = "update";
10736
+ plan.reason = "\u8865\u9F50\u5168\u5C40\u8BBE\u7F6E YAML \u7684\u914D\u5957\u9875\u9762\u4E0E API";
10737
+ plan.content = target;
10738
+ delete plan.conflict;
10739
+ }
10740
+ }
10741
+
10742
+ // src/update/environment-mount-plan.ts
10743
+ var environmentProfile = "windy.environment.yaml";
10744
+ var generatedMount = "./windy.environment.yaml:/app/windy.environment.yaml:ro";
10745
+ function validateEnvironmentMountPlan(files, currentProfile) {
10746
+ const compose = files.find(({ path: path2 }) => path2 === "docker-compose.yml");
10747
+ if (!compose?.content || compose.action !== "create" && compose.action !== "update" || compose.conflict || !new TextDecoder().decode(compose.content).includes(generatedMount))
10748
+ return;
10749
+ const profile = files.find(({ path: path2 }) => path2 === environmentProfile);
10750
+ const available = profile?.action === "create" || profile?.action === "update" ? profile.content !== undefined : profile?.action !== "delete" && currentProfile !== undefined;
10751
+ if (available)
10752
+ return;
10753
+ compose.action = "preserve";
10754
+ delete compose.content;
10755
+ compose.conflict = "\u76EE\u6807 Compose \u9700\u8981\u6302\u8F7D windy.environment.yaml\uFF0C\u8BF7\u5148\u6062\u590D\u9879\u76EE\u7684\u73AF\u5883\u914D\u7F6E\u6587\u4EF6";
10756
+ }
10757
+
10758
+ // src/update/media-runtime-registry.ts
10759
+ function reconcileMediaRuntimeRegistry(files, current, sourceModules, targetModules) {
10760
+ if (!current || !sourceModules.includes("system.agent") || sourceModules.includes("system.media-runtime") || !targetModules.includes("system.media-runtime"))
10761
+ return;
10762
+ const file = files.find(({ path: path2 }) => path2 === projectModuleRegistryPath);
10763
+ if (!file)
10764
+ throw new Error("\u5A92\u4F53\u8FD0\u884C\u65F6\u8FC1\u79FB\u7F3A\u5C11\u9879\u76EE\u6A21\u5757\u88C5\u914D\u6E05\u5355");
10765
+ const registry = parseProjectModuleRegistry(current);
10766
+ if (registry.modules.some(({ name }) => name === "system.media-runtime"))
10767
+ return;
10768
+ if (!registry.modules.find(({ name }) => name === "system.agent")?.enabled) {
10769
+ file.conflict = "\u65E7 Agent \u88C5\u914D\u5DF2\u505C\u7528\u6216\u7F3A\u5931\uFF0C\u8BF7\u5148\u660E\u786E\u5A92\u4F53\u8FD0\u884C\u65F6\u7684\u4EA4\u4ED8\u8303\u56F4";
10770
+ return;
10771
+ }
10772
+ const orders = new Set(registry.modules.map(({ order: order2 }) => order2));
10773
+ const lastOrder = Math.max(0, ...orders);
10774
+ let order = lastOrder + 100;
10775
+ if (!Number.isSafeInteger(order)) {
10776
+ order = 0;
10777
+ while (orders.has(order))
10778
+ order += 1;
10779
+ }
10780
+ registry.modules.push({
10781
+ name: "system.media-runtime",
10782
+ package: null,
10783
+ enabled: true,
10784
+ order,
10785
+ admin: { visible: true },
10786
+ config: {}
10787
+ });
10788
+ file.action = "update";
10789
+ file.reason = "\u6CE8\u518C\u65E7 Agent \u4EA4\u4ED8\u4E2D\u5DF2\u6709\u7684\u5A92\u4F53\u8FD0\u884C\u65F6";
10790
+ file.content = new TextEncoder().encode(`${JSON.stringify(registry, null, 2)}
10791
+ `);
10792
+ delete file.conflict;
10793
+ }
10794
+
10795
+ // src/update/planner.ts
10796
+ import { readFile as readFile23 } from "fs/promises";
10797
+ import { dirname as dirname9, join as join30 } from "path";
10798
+
10799
+ // src/update/docker-copy-update-plan.ts
10800
+ async function deriveDockerfilePlans(files, snapshots, paths, targetWorkspaceManifests) {
10801
+ const rootPackage = virtualPathContent("package.json", files, snapshots, targetWorkspaceManifests);
10802
+ if (!rootPackage)
10803
+ return;
10804
+ const rootManifest = parsePackage(rootPackage, "package.json");
10805
+ const workspacePatterns = rootManifest.workspaces ?? [];
10806
+ const packages = [];
10807
+ for (const path2 of [
10808
+ ...new Set([...paths, ...targetWorkspaceManifests.keys()])
10809
+ ].filter((path3) => path3.endsWith("package.json"))) {
10810
+ const directory = path2 === "package.json" ? "." : path2.slice(0, -13);
10811
+ if (directory === ".")
10812
+ continue;
10813
+ if (!workspacePatterns.some((pattern) => matchesWorkspace(pattern, directory))) {
10814
+ continue;
10815
+ }
10816
+ const content = virtualPathContent(path2, files, snapshots, targetWorkspaceManifests);
10817
+ if (!content)
10818
+ continue;
10819
+ const manifest = parsePackage(content, path2);
10820
+ packages.push({
10821
+ name: manifest.name,
10822
+ directory,
10823
+ dependencies: [
10824
+ ...Object.keys(manifest.dependencies ?? {}),
10825
+ ...Object.keys(manifest.devDependencies ?? {})
10826
+ ],
10827
+ workspaceDependencies: Object.entries({
10828
+ ...manifest.dependencies,
10829
+ ...manifest.devDependencies
10830
+ }).filter(([, range]) => range.startsWith("workspace:")).map(([name]) => name)
10831
+ });
10832
+ }
10833
+ packages.sort((left, right) => left.directory.localeCompare(right.directory));
10834
+ for (const target of dockerfileCopyTargets) {
10835
+ const dockerPlan = files.find(({ path: path2 }) => path2 === target.dockerfilePath);
10836
+ if (!dockerPlan || dockerPlan.conflict)
10837
+ continue;
10838
+ const dockerSource = virtualContent(dockerPlan, snapshots);
10839
+ if (!dockerSource)
10840
+ continue;
10841
+ const source = new TextDecoder().decode(dockerSource);
10842
+ if (!hasDockerfileCopyAnchors(source, target)) {
10843
+ dockerPlan.conflict = "Dockerfile \u7F3A\u5C11 0.6.6 \u57FA\u7EBF\u7684 COPY \u951A\u70B9\uFF0C\u5DF2\u505C\u6B62\u81EA\u52A8\u8FC1\u79FB\u65E7\u683C\u5F0F";
10844
+ continue;
10845
+ }
10846
+ let next;
10847
+ try {
10848
+ next = syncDockerfileSource(source, target, packages);
10849
+ } catch (error) {
10850
+ dockerPlan.conflict = error instanceof Error ? error.message : "Docker COPY \u951A\u70B9\u65E0\u6548";
10851
+ continue;
10852
+ }
10853
+ if (next === source)
10854
+ continue;
10855
+ dockerPlan.action = "update";
10856
+ dockerPlan.reason = "\u6309\u4E8B\u52A1\u5B89\u5168 workspace \u89C6\u56FE\u540C\u6B65 Docker COPY \u6E05\u5355";
10857
+ dockerPlan.content = new TextEncoder().encode(next);
10858
+ }
10859
+ }
10860
+ function virtualPathContent(path2, files, snapshots, targetWorkspaceManifests) {
10861
+ const plan = files.find((item) => item.path === path2);
10862
+ return plan ? virtualContent(plan, snapshots) : snapshots.get(path2) ?? targetWorkspaceManifests.get(path2);
10863
+ }
10864
+ function virtualContent(plan, snapshots) {
10865
+ if (plan.action === "delete")
10866
+ return;
10867
+ if (plan.action === "create" || plan.action === "update")
10868
+ return plan.content;
10869
+ return snapshots.get(plan.path);
10870
+ }
10871
+ function parsePackage(content, path2) {
10578
10872
  try {
10579
- return await readFile20(path2);
10873
+ return JSON.parse(new TextDecoder().decode(content));
10580
10874
  } catch (error) {
10581
- if (isMissing7(error))
10582
- return;
10583
- throw error;
10875
+ throw new Error(`\u65E0\u6CD5\u89E3\u6790 Docker workspace \u8F93\u5165\uFF1A${path2}`, {
10876
+ cause: error
10877
+ });
10584
10878
  }
10585
10879
  }
10586
- function isMissing7(error) {
10587
- return error instanceof Error && "code" in error && error.code === "ENOENT";
10880
+ function matchesWorkspace(pattern, directory) {
10881
+ return pattern.endsWith("/*") && directory.startsWith(pattern.slice(0, -1));
10588
10882
  }
10589
10883
 
10590
- // src/update/dsh-platform-recipe.ts
10591
- var dshPlatformRecipeId = "dsh-platform-v1";
10592
- var dshPlatformRecipePaths = new Set([
10593
- "apps/server/src/app/agent-workflow-presets.ts",
10594
- "apps/server/src/app/workbench-public-origin.ts",
10595
- "apps/web/src/router/index.ts",
10596
- "apps/web/src/router/platform-admin-pages.ts",
10597
- "apps/web/src/router/platform-admin-route-bindings.ts",
10598
- "apps/web/src/composables/useLoginFlow.ts",
10599
- "apps/web/src/pages/system/AgentWorkflowPage.vue",
10600
- "apps/web/src/services/agent-workflow-api.ts",
10601
- "apps/web/vite.config.ts",
10602
- "apps/web/server.ts",
10603
- "apps/web/runtime-server.ts",
10604
- "apps/web/dev/start.ts"
10605
- ]);
10606
- function isDshPlatformRecipePath(path2) {
10607
- return dshPlatformRecipePaths.has(path2) || path2.startsWith("apps/server/src/app/dsh-") && path2.endsWith(".ts");
10608
- }
10609
- async function isDshPlatformTransition(input) {
10610
- if (!input.sourceModules.includes("system.agent") || !input.targetModules.includes("system.agent"))
10611
- return false;
10612
- const path2 = "apps/agent-server/package.json";
10613
- const [base, target] = await Promise.all([
10614
- readOptional(join26(input.baseDirectory, path2)),
10615
- readOptional(join26(input.targetDirectory, path2))
10616
- ]);
10617
- return hasDsh(target) && !hasDsh(base);
10884
+ // src/update/migration-reconciler.ts
10885
+ import { readFile as readFile21 } from "fs/promises";
10886
+ import { join as join28 } from "path";
10887
+
10888
+ // src/update/migration-adoption.ts
10889
+ import { readFile as readFile20 } from "fs/promises";
10890
+ import { join as join27 } from "path";
10891
+
10892
+ // src/update/migration-project-file.ts
10893
+ import { readFile as readFile18 } from "fs/promises";
10894
+ import { join as join26 } from "path";
10895
+ async function readProjectJson(input, path2) {
10896
+ const content = await readProjectFile(input, path2);
10897
+ if (!content) {
10898
+ const error = new Error(`\u5F53\u524D\u9879\u76EE\u6587\u4EF6\u7F3A\u5931\uFF1A${path2}`);
10899
+ error.code = "ENOENT";
10900
+ throw error;
10901
+ }
10902
+ return JSON.parse(new TextDecoder().decode(content));
10618
10903
  }
10619
- function planDshPlatformFile(input) {
10620
- const common2 = {
10621
- path: input.path,
10622
- ownership: input.ownership,
10623
- expectedProjectContent: input.expected
10624
- };
10625
- if (input.target && input.ours && hashContent(input.ours) === hashContent(input.target)) {
10626
- return {
10627
- ...common2,
10628
- action: "preserve",
10629
- reason: "\u5DF2\u5305\u542B DSH \u5E73\u53F0\u63A5\u7EBF\u76EE\u6807\u5185\u5BB9"
10630
- };
10904
+ async function readProjectFile(input, path2) {
10905
+ if (input.projectSnapshots?.has(path2)) {
10906
+ return input.projectSnapshots.get(path2);
10631
10907
  }
10632
- if (input.target && !input.ours && !input.base) {
10633
- return {
10634
- ...common2,
10635
- action: "create",
10636
- reason: "DSH \u5E73\u53F0\u65B0\u589E\u9879\u76EE\u6269\u5C55\u70B9",
10637
- content: input.target
10638
- };
10908
+ let content;
10909
+ try {
10910
+ content = await readFile18(join26(input.projectDirectory, path2));
10911
+ } catch (error) {
10912
+ if (!isMissingFileError(error))
10913
+ throw error;
10639
10914
  }
10640
- if (input.target && input.base && input.ours && hashContent(input.ours) === hashContent(input.base)) {
10641
- return {
10642
- ...common2,
10643
- action: "update",
10644
- reason: "DSH \u5E73\u53F0\u5347\u7EA7\u7CBE\u786E\u6765\u6E90\u63A5\u7EBF",
10645
- content: input.target
10646
- };
10915
+ if (input.projectSnapshots) {
10916
+ input.projectSnapshots.set(path2, content);
10647
10917
  }
10648
- return {
10649
- ...common2,
10650
- action: "preserve",
10651
- reason: "\u4FDD\u7559\u5BA2\u6237 DSH \u63A5\u7EBF",
10652
- conflict: "DSH \u5E73\u53F0\u63A5\u7EBF\u4E0E\u7CBE\u786E\u6765\u6E90\u6216\u76EE\u6807\u4E0D\u4E00\u81F4\uFF1B\u8BF7\u5BA1\u6838\u9879\u76EE\u4FEE\u6539\u540E\u663E\u5F0F\u96C6\u6210\uFF0C\u7981\u6B62\u731C\u6D4B\u8986\u76D6"
10653
- };
10918
+ return content;
10654
10919
  }
10655
- function hasDsh(content) {
10656
- if (!content)
10657
- return false;
10658
- const value = JSON.parse(new TextDecoder().decode(content));
10659
- return typeof value.dependencies?.["@deepseek-ai/dsh"] === "string";
10920
+ function isMissingFileError(error) {
10921
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
10660
10922
  }
10661
10923
 
10662
- // src/update/planner-file.ts
10663
- async function planFile(path2, baseRoot, targetRoot, source, target, ours, provenance, targetContentOverride, manageGeneratedProjectOwned = false, forcePlatformBaseline = false, manageRecipeProjectOwned = false, migrateDshPlatform = false) {
10664
- const [base, theirs] = await Promise.all([
10665
- readOptional2(join27(baseRoot, path2)),
10666
- targetContentOverride ? Promise.resolve(targetContentOverride) : readOptional2(join27(targetRoot, path2))
10667
- ]);
10668
- const ownership = source?.ownership ?? target?.ownership ?? classifyOwnership(path2);
10669
- const expected = expectationForSnapshot(ours);
10670
- if (migrateDshPlatform && isDshPlatformRecipePath(path2)) {
10671
- return planDshPlatformFile({
10672
- path: path2,
10673
- ownership,
10674
- ours,
10675
- base,
10676
- target: theirs,
10677
- expected
10678
- });
10924
+ // src/update/migration-history.ts
10925
+ import { readFile as readFile19 } from "fs/promises";
10926
+ var drizzleRoot = "packages/database/drizzle";
10927
+ var journalPath = `${drizzleRoot}/meta/_journal.json`;
10928
+ var migrationPattern = /^(\d{4})_(.+)$/;
10929
+ function snapshotPath(tag) {
10930
+ const match = migrationPattern.exec(tag);
10931
+ if (!match) {
10932
+ throw new Error(`${journalPath}\uFF1Amigration tag \u683C\u5F0F\u65E0\u6548\uFF1A${tag}`);
10679
10933
  }
10680
- if (ownership === "project-owned") {
10681
- const hasExistingRecipeSource = source !== undefined && source.state !== "missing" && ours !== undefined;
10682
- if (manageRecipeProjectOwned && target && (hasExistingRecipeSource || recipeCanManageMissingProjectOwnedPath(path2))) {
10683
- return planRecipeProjectOwned({ path: path2, ours, target: theirs, expected });
10934
+ return `${drizzleRoot}/meta/${match[1]}_snapshot.json`;
10935
+ }
10936
+ function validateUniqueNames(journal, label, conflicts) {
10937
+ const seen = new Set;
10938
+ for (const { tag } of journal.entries) {
10939
+ const name = stableName(tag);
10940
+ if (seen.has(name)) {
10941
+ conflicts.push(`${journalPath}\uFF1A${label}\u5B58\u5728\u91CD\u590D migration \u540D\u79F0 ${name}`);
10684
10942
  }
10685
- if (manageGeneratedProjectOwned) {
10686
- return planGeneratedProjectOwned(path2, source, target, ours, theirs, expected);
10943
+ seen.add(name);
10944
+ }
10945
+ }
10946
+ function stableName(tag) {
10947
+ return migrationPattern.exec(tag)?.[2] ?? tag;
10948
+ }
10949
+ function highestPrefix(journal) {
10950
+ return Math.max(-1, ...journal.entries.map(({ tag }) => Number(migrationPattern.exec(tag)?.[1] ?? -1)));
10951
+ }
10952
+ function highestIndex(journal) {
10953
+ return Math.max(-1, ...journal.entries.map(({ idx }) => idx));
10954
+ }
10955
+ function isDrizzleHistoryPath(path2) {
10956
+ return path2 === journalPath || isMigrationArtifact(path2);
10957
+ }
10958
+ function isMigrationArtifact(path2) {
10959
+ return isSqlMigration(path2) || new RegExp(`^${drizzleRoot}/meta/\\d{4}_snapshot\\.json$`).test(path2);
10960
+ }
10961
+ function isSqlMigration(path2) {
10962
+ return new RegExp(`^${drizzleRoot}/\\d{4}_.+\\.sql$`).test(path2);
10963
+ }
10964
+ function managedEntry(path2, content) {
10965
+ return { path: path2, ownership: "windy-managed", hash: hashContent(content) };
10966
+ }
10967
+ function deduplicate(files) {
10968
+ return [...new Map(files.map((entry) => [entry.path, entry])).values()];
10969
+ }
10970
+ function encodeJson(value) {
10971
+ return new TextEncoder().encode(`${JSON.stringify(value, null, 2)}
10972
+ `);
10973
+ }
10974
+ async function readJson(path2) {
10975
+ return JSON.parse(await readFile19(path2, "utf8"));
10976
+ }
10977
+
10978
+ // src/update/snapshot-delta.ts
10979
+ function applySnapshotDelta(before, after, current, path2, conflicts, conflictPath) {
10980
+ if (deepEqual(before, after))
10981
+ return structuredClone(current);
10982
+ if (!isRecord(before) || !isRecord(after) || !isRecord(current)) {
10983
+ if (deepEqual(current, before) || deepEqual(current, after)) {
10984
+ return structuredClone(after);
10687
10985
  }
10688
- return !source && !ours && theirs ? readyPlannedFile(path2, ownership, "create", "\u65B0\u589E\u9879\u76EE\u6269\u5C55\u70B9", expected, theirs) : readyPlannedFile(path2, ownership, "preserve", "\u9ED8\u8BA4\u4FDD\u7559\u9879\u76EE\u7248\u672C", expected);
10986
+ conflicts.push(`${conflictPath}\uFF1A\u5E73\u53F0\u4E0E\u4E1A\u52A1\u540C\u65F6\u4FEE\u6539 snapshot \u8282\u70B9 ${path2.join(".") || "<root>"}`);
10987
+ return structuredClone(current);
10689
10988
  }
10690
- if (path2.startsWith("packages/database/drizzle/") && path2.endsWith(".sql")) {
10691
- if (source && !target) {
10692
- return conflict(path2, ownership, "\u5DF2\u53D1\u5E03 migration \u4E0D\u53EF\u5220\u9664", expected);
10989
+ const merged = structuredClone(current);
10990
+ const keys = new Set([...Object.keys(before), ...Object.keys(after)]);
10991
+ for (const key of keys) {
10992
+ if (path2.length === 0 && (key === "id" || key === "prevId"))
10993
+ continue;
10994
+ const nextPath = [...path2, key];
10995
+ if (!(key in after)) {
10996
+ if (!(key in current))
10997
+ continue;
10998
+ if (deepEqual(current[key], before[key]))
10999
+ delete merged[key];
11000
+ else
11001
+ conflicts.push(`${conflictPath}\uFF1A\u5E73\u53F0\u5220\u9664\u4E86\u4E1A\u52A1\u5DF2\u4FEE\u6539\u7684 snapshot \u8282\u70B9 ${nextPath.join(".")}`);
11002
+ continue;
10693
11003
  }
10694
- if (source && target && source.hash !== target.hash) {
10695
- return conflict(path2, ownership, "\u5DF2\u53D1\u5E03 migration \u4E0D\u53EF\u6539\u5199\uFF0C\u53EA\u80FD\u8FFD\u52A0\u65B0\u6587\u4EF6", expected);
11004
+ if (!(key in before)) {
11005
+ if (!(key in current) || deepEqual(current[key], after[key])) {
11006
+ merged[key] = structuredClone(after[key]);
11007
+ } else {
11008
+ conflicts.push(`${conflictPath}\uFF1A\u5E73\u53F0\u65B0\u589E\u7684 snapshot \u8282\u70B9\u5DF2\u88AB\u4E1A\u52A1\u5360\u7528 ${nextPath.join(".")}`);
11009
+ }
11010
+ continue;
10696
11011
  }
11012
+ merged[key] = applySnapshotDelta(before[key], after[key], current[key], nextPath, conflicts, conflictPath);
10697
11013
  }
10698
- if (!source && target) {
10699
- if (!ours) {
10700
- return readyPlannedFile(path2, ownership, "create", "\u76EE\u6807\u7248\u672C\u65B0\u589E", expected, theirs);
11014
+ return merged;
11015
+ }
11016
+ function isRecord(value) {
11017
+ return !!value && typeof value === "object" && !Array.isArray(value);
11018
+ }
11019
+ function deepEqual(left, right) {
11020
+ return JSON.stringify(left) === JSON.stringify(right);
11021
+ }
11022
+
11023
+ // src/update/migration-adoption.ts
11024
+ async function inspectPreexistingMigrationAdoptions(input) {
11025
+ const conflicts = [];
11026
+ const adoptions = [];
11027
+ const projectByName = new Map(input.project.entries.map((entry) => [stableName(entry.tag), entry]));
11028
+ for (const candidate of input.candidates) {
11029
+ const name = stableName(candidate.tag);
11030
+ const projectEntry = projectByName.get(name);
11031
+ if (!projectEntry || input.managedNames.has(name))
11032
+ continue;
11033
+ const targetPosition = input.target.entries.indexOf(candidate);
11034
+ const projectPosition = input.project.entries.indexOf(projectEntry);
11035
+ const previousTarget = input.target.entries[targetPosition - 1];
11036
+ const previousProject = input.project.entries[projectPosition - 1];
11037
+ if (!previousTarget || !previousProject) {
11038
+ conflicts.push(`${journalPath}\uFF1A\u65E0\u6CD5\u91C7\u7EB3\u73B0\u6709\u5E73\u53F0 migration ${name}\uFF0C\u7F3A\u5C11\u524D\u7F6E\u5386\u53F2`);
11039
+ continue;
11040
+ }
11041
+ const projectSqlPath = `${drizzleRoot}/${projectEntry.tag}.sql`;
11042
+ const projectSnapshotPath = snapshotPath(projectEntry.tag);
11043
+ const [
11044
+ targetSql,
11045
+ projectSql,
11046
+ targetBefore,
11047
+ targetAfter,
11048
+ projectBeforeBytes,
11049
+ projectAfterBytes
11050
+ ] = await Promise.all([
11051
+ readFile20(join27(input.targetDirectory, `${drizzleRoot}/${candidate.tag}.sql`)),
11052
+ readProjectFile(input, projectSqlPath),
11053
+ readJson(join27(input.targetDirectory, snapshotPath(previousTarget.tag))),
11054
+ readJson(join27(input.targetDirectory, snapshotPath(candidate.tag))),
11055
+ readProjectFile(input, snapshotPath(previousProject.tag)),
11056
+ readProjectFile(input, projectSnapshotPath)
11057
+ ]);
11058
+ if (!projectSql || !projectBeforeBytes || !projectAfterBytes) {
11059
+ conflicts.push(`${journalPath}\uFF1A\u65E0\u6CD5\u91C7\u7EB3\u73B0\u6709\u5E73\u53F0 migration ${name}\uFF0C\u9879\u76EE migration \u6587\u4EF6\u4E0D\u5B8C\u6574`);
11060
+ continue;
10701
11061
  }
10702
- 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);
10703
- }
10704
- if (source && !target) {
10705
- if (!ours) {
10706
- return readyPlannedFile(path2, ownership, "preserve", "\u9879\u76EE\u5DF2\u5220\u9664", expected);
11062
+ const projectBefore = decodeSnapshot(projectBeforeBytes);
11063
+ const projectAfter = decodeSnapshot(projectAfterBytes);
11064
+ const reason = validateAdoption({
11065
+ targetSql,
11066
+ projectSql,
11067
+ targetBefore,
11068
+ targetAfter,
11069
+ projectBefore,
11070
+ projectAfter
11071
+ });
11072
+ if (reason) {
11073
+ conflicts.push(`${journalPath}\uFF1A\u65E0\u6CD5\u91C7\u7EB3\u73B0\u6709\u5E73\u53F0 migration ${name}\uFF0C${reason}`);
11074
+ continue;
10707
11075
  }
10708
- 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);
10709
- }
10710
- if (source && target && !ours) {
10711
- return readyPlannedFile(path2, "project-owned", "preserve", "\u9879\u76EE\u5DF2\u5220\u9664", expected);
10712
- }
10713
- if (!source || !target || !ours || !theirs) {
10714
- return conflict(path2, ownership, "\u6587\u4EF6\u72B6\u6001\u4E0D\u5B8C\u6574\uFF0C\u4E0D\u80FD\u5B89\u5168\u63A8\u65AD", expected);
10715
- }
10716
- const oursChanged = hashContent(ours) !== source.hash;
10717
- const sourceArtifactHash = artifactHashOf(source);
10718
- const targetChanged = targetChangedSinceBase(sourceArtifactHash, base, theirs);
10719
- const adoptedProjectDelta = source.artifactHash !== undefined && source.artifactHash !== source.hash;
10720
- const forceTargetBaseline = !adoptedProjectDelta && shouldForcePlatformBaseline({
10721
- forcePlatformBaseline,
10722
- oursChanged,
10723
- sourceArtifactHash,
10724
- base
10725
- });
10726
- if (forceTargetBaseline && path2.endsWith("package.json") && base) {
10727
- const merged = mergePackageJson(base, ours, theirs);
10728
- 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);
11076
+ adoptions.push({
11077
+ sqlPath: projectSqlPath,
11078
+ snapshotPath: projectSnapshotPath,
11079
+ sql: projectSql,
11080
+ snapshot: projectAfterBytes
11081
+ });
10729
11082
  }
10730
- if (forceTargetBaseline) {
10731
- return readyPlannedFile(path2, ownership, "update", "\u5F3A\u5236\u91C7\u7528\u76EE\u6807\u5E73\u53F0\u57FA\u7EBF", expected, theirs);
11083
+ return { adoptions, conflicts };
11084
+ }
11085
+ function validateAdoption(input) {
11086
+ if (!equalBytes(input.targetSql, input.projectSql)) {
11087
+ return "SQL \u5185\u5BB9\u4E0D\u4E00\u81F4";
10732
11088
  }
10733
- if (!oursChanged && !targetChanged) {
10734
- return readyPlannedFile(path2, ownership, "preserve", "\u53CC\u65B9\u672A\u4FEE\u6539", expected);
11089
+ if (input.projectAfter.prevId !== input.projectBefore.id) {
11090
+ return "\u9879\u76EE snapshot lineage \u4E0D\u8FDE\u7EED";
10735
11091
  }
10736
- if (targetChanged && path2.endsWith("package.json") && base) {
10737
- const merged = mergePackageJson(base, ours, theirs);
10738
- 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);
11092
+ const deltaConflicts = [];
11093
+ const expected = applySnapshotDelta(input.targetBefore, input.targetAfter, input.projectBefore, [], deltaConflicts, journalPath);
11094
+ if (deltaConflicts.length || canonicalSnapshot(expected) !== canonicalSnapshot(input.projectAfter)) {
11095
+ return "snapshot delta \u4E0E\u9879\u76EE\u5386\u53F2\u4E0D\u4E00\u81F4";
10739
11096
  }
10740
- if (!oursChanged && targetChanged && adoptedProjectDelta && base) {
10741
- const merged = await mergeTextFile(base, ours, theirs);
10742
- return merged.content ? readyPlannedFile(path2, ownership, "update", "\u4E09\u65B9\u5408\u5E76\u5DF2\u91C7\u7EB3\u9879\u76EE\u589E\u91CF", expected, merged.content) : conflict(path2, ownership, merged.conflict || "\u4E09\u65B9\u5408\u5E76\u5931\u8D25", expected);
11097
+ }
11098
+ function decodeSnapshot(content) {
11099
+ return JSON.parse(new TextDecoder().decode(content));
11100
+ }
11101
+ function equalBytes(left, right) {
11102
+ return left.byteLength === right.byteLength && left.every((value, index) => value === right[index]);
11103
+ }
11104
+ function canonicalSnapshot(value) {
11105
+ return JSON.stringify(canonicalize(value, true));
11106
+ }
11107
+ function canonicalize(value, root = false) {
11108
+ if (Array.isArray(value))
11109
+ return value.map((item) => canonicalize(item));
11110
+ if (!value || typeof value !== "object")
11111
+ return value;
11112
+ return Object.fromEntries(Object.entries(value).filter(([key]) => !root || key !== "id" && key !== "prevId").sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, canonicalize(item)]));
11113
+ }
11114
+
11115
+ // src/update/migration-reconciler.ts
11116
+ async function reconcileDrizzleMigrations(input) {
11117
+ const journals = await readJournals(input);
11118
+ if (!journals) {
11119
+ return unchanged(input.targetManifest);
10743
11120
  }
10744
- if (!oursChanged) {
10745
- return readyPlannedFile(path2, ownership, "update", "\u4EC5 Windy \u4FEE\u6539", expected, theirs);
11121
+ const { base, project, target } = journals;
11122
+ const conflicts = [];
11123
+ validateUniqueNames(base, "\u6765\u6E90\u6A21\u677F", conflicts);
11124
+ validateUniqueNames(project, "\u5F53\u524D\u9879\u76EE", conflicts);
11125
+ validateUniqueNames(target, "\u76EE\u6807\u6A21\u677F", conflicts);
11126
+ if (conflicts.length) {
11127
+ return { ...unchanged(input.targetManifest), conflicts };
10746
11128
  }
10747
- if (!targetChanged) {
10748
- return readyPlannedFile(path2, ownership, "preserve", "\u5BA2\u6237\u5DF2\u4FEE\u6539", expected);
11129
+ const baseNames = new Set(base.entries.map(({ tag }) => stableName(tag)));
11130
+ const projectNames = new Set(project.entries.map(({ tag }) => stableName(tag)));
11131
+ const managedNames = new Set(input.sourceManifest.files.filter(({ path: path2 }) => isSqlMigration(path2)).map(({ path: path2 }) => stableName(path2.slice(drizzleRoot.length + 1, -4))));
11132
+ const candidates = target.entries.filter(({ tag }) => !baseNames.has(stableName(tag)));
11133
+ const adoption = await inspectPreexistingMigrationAdoptions({
11134
+ ...input,
11135
+ project,
11136
+ target,
11137
+ candidates,
11138
+ managedNames
11139
+ });
11140
+ conflicts.push(...adoption.conflicts);
11141
+ const additions = candidates.filter(({ tag }) => !projectNames.has(stableName(tag)));
11142
+ const contentOverrides = new Map;
11143
+ const files = await preservePublishedHistory(input.sourceManifest, input, contentOverrides);
11144
+ const publishedPaths = new Set(files.map(({ path: path2 }) => path2));
11145
+ for (const adopted of adoption.adoptions) {
11146
+ contentOverrides.set(adopted.sqlPath, adopted.sql);
11147
+ contentOverrides.set(adopted.snapshotPath, adopted.snapshot);
11148
+ files.push(managedEntry(adopted.sqlPath, adopted.sql), managedEntry(adopted.snapshotPath, adopted.snapshot));
11149
+ publishedPaths.add(adopted.sqlPath);
11150
+ publishedPaths.add(adopted.snapshotPath);
10749
11151
  }
10750
- if (hashContent(ours) === target.hash) {
10751
- return readyPlannedFile(path2, ownership, "preserve", "\u5DF2\u5305\u542B\u76EE\u6807\u5185\u5BB9", expected);
11152
+ files.push(...input.targetManifest.files.filter(({ path: path2 }) => !isDrizzleHistoryPath(path2)));
11153
+ if (!additions.length) {
11154
+ const journalContent2 = encodeJson(project);
11155
+ contentOverrides.set(journalPath, journalContent2);
11156
+ files.push(managedEntry(journalPath, journalContent2));
11157
+ return result(input.targetManifest, files, contentOverrides, publishedPaths, conflicts);
10752
11158
  }
10753
- if (base) {
10754
- const merged = await mergeTextFile(base, ours, theirs);
10755
- 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);
11159
+ let nextPrefix = highestPrefix(project) + 1;
11160
+ let nextIndex = highestIndex(project) + 1;
11161
+ let previousWhen = project.entries.at(-1)?.when ?? 0;
11162
+ let currentSnapshot = await readLatestSnapshot(input, project);
11163
+ const mergedJournal = structuredClone(project);
11164
+ for (const addition of additions) {
11165
+ const targetPosition = target.entries.indexOf(addition);
11166
+ const previousTarget = target.entries[targetPosition - 1];
11167
+ if (!previousTarget) {
11168
+ conflicts.push(`${journalPath}\uFF1A\u76EE\u6807 migration \u7F3A\u5C11\u524D\u7F6E\u5FEB\u7167`);
11169
+ break;
11170
+ }
11171
+ const targetBefore = await readSnapshot(input.targetDirectory, previousTarget.tag);
11172
+ const targetAfter = await readSnapshot(input.targetDirectory, addition.tag);
11173
+ const merged = applySnapshotDelta(targetBefore, targetAfter, currentSnapshot, [], conflicts, journalPath);
11174
+ if (conflicts.length)
11175
+ break;
11176
+ const prefix = String(nextPrefix).padStart(4, "0");
11177
+ const name = stableName(addition.tag);
11178
+ const tag = `${prefix}_${name}`;
11179
+ const sqlPath = `${drizzleRoot}/${tag}.sql`;
11180
+ const snapshotPath2 = `${drizzleRoot}/meta/${prefix}_snapshot.json`;
11181
+ const sql = await readFile21(join28(input.targetDirectory, `${drizzleRoot}/${addition.tag}.sql`));
11182
+ merged.id = targetAfter.id;
11183
+ merged.prevId = currentSnapshot.id;
11184
+ const snapshot2 = encodeJson(merged);
11185
+ contentOverrides.set(sqlPath, sql);
11186
+ contentOverrides.set(snapshotPath2, snapshot2);
11187
+ files.push(managedEntry(sqlPath, sql), managedEntry(snapshotPath2, snapshot2));
11188
+ previousWhen = Math.max(previousWhen + 1, addition.when);
11189
+ mergedJournal.entries.push({
11190
+ ...addition,
11191
+ idx: nextIndex,
11192
+ when: previousWhen,
11193
+ tag
11194
+ });
11195
+ currentSnapshot = merged;
11196
+ nextPrefix += 1;
11197
+ nextIndex += 1;
10756
11198
  }
10757
- return conflict(path2, ownership, "\u5BA2\u6237\u548C Windy \u5747\u5DF2\u4FEE\u6539", expected);
11199
+ const journalContent = encodeJson(mergedJournal);
11200
+ contentOverrides.set(journalPath, journalContent);
11201
+ files.push(managedEntry(journalPath, journalContent));
11202
+ return result(input.targetManifest, files, contentOverrides, publishedPaths, conflicts);
10758
11203
  }
10759
- function shouldForcePlatformBaseline(input) {
10760
- return input.forcePlatformBaseline && !input.oursChanged && input.base !== undefined && hashContent(input.base) !== input.sourceArtifactHash;
11204
+ function result(targetManifest, files, contentOverrides, forcePreserves, conflicts) {
11205
+ return {
11206
+ targetManifest: {
11207
+ ...targetManifest,
11208
+ files: deduplicate(files).sort((left, right) => left.path.localeCompare(right.path))
11209
+ },
11210
+ contentOverrides,
11211
+ forceUpdates: new Set([journalPath]),
11212
+ forcePreserves,
11213
+ conflicts
11214
+ };
10761
11215
  }
10762
- function targetChangedSinceBase(sourceHash, base, target) {
10763
- return base && target ? hashContent(base) !== hashContent(target) : !!target && hashContent(target) !== sourceHash;
11216
+ function unchanged(targetManifest) {
11217
+ return {
11218
+ targetManifest,
11219
+ contentOverrides: new Map,
11220
+ forceUpdates: new Set,
11221
+ forcePreserves: new Set,
11222
+ conflicts: []
11223
+ };
10764
11224
  }
10765
- async function readOptional2(path2) {
11225
+ async function readJournals(input) {
10766
11226
  try {
10767
- return await readFile21(path2);
11227
+ const [base, project, target] = await Promise.all([
11228
+ readJson(join28(input.baseDirectory, journalPath)),
11229
+ readProjectJson(input, journalPath),
11230
+ readJson(join28(input.targetDirectory, journalPath))
11231
+ ]);
11232
+ return { base, project, target };
10768
11233
  } catch (error) {
10769
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
11234
+ if (isMissingFileError(error))
10770
11235
  return;
10771
- }
10772
11236
  throw error;
10773
11237
  }
10774
11238
  }
11239
+ async function preservePublishedHistory(manifest, input, overrides) {
11240
+ const entries = manifest.files.filter(({ path: path2 }) => isMigrationArtifact(path2) && path2 !== journalPath);
11241
+ await Promise.all(entries.map(async ({ path: path2 }) => {
11242
+ const content = await readProjectFile(input, path2);
11243
+ if (!content)
11244
+ throw new Error(`\u5F53\u524D\u9879\u76EE migration \u6587\u4EF6\u7F3A\u5931\uFF1A${path2}`);
11245
+ overrides.set(path2, content);
11246
+ }));
11247
+ return entries;
11248
+ }
11249
+ async function readLatestSnapshot(input, journal) {
11250
+ const latest = journal.entries.at(-1);
11251
+ if (!latest)
11252
+ throw new Error(`${journalPath}\uFF1A\u5F53\u524D\u9879\u76EE\u6CA1\u6709 migration`);
11253
+ const path2 = snapshotPath(latest.tag);
11254
+ const content = await readProjectFile(input, path2);
11255
+ if (!content) {
11256
+ throw new Error(`\u5F53\u524D\u9879\u76EE migration \u6587\u4EF6\u7F3A\u5931\uFF1A${path2}`);
11257
+ }
11258
+ return JSON.parse(new TextDecoder().decode(content));
11259
+ }
11260
+ async function readSnapshot(root, tag) {
11261
+ return await readJson(join28(root, snapshotPath(tag)));
11262
+ }
10775
11263
 
10776
11264
  // src/update/planner-files.ts
10777
11265
  var metadataPaths = new Set([
@@ -10852,17 +11340,9 @@ async function assertNoPendingUpdate(projectDirectory) {
10852
11340
  throw new Error(`\u68C0\u6D4B\u5230\u672A\u5B8C\u6210\u66F4\u65B0\uFF08\u9636\u6BB5\uFF1A${pending.phase ?? "ready"}\uFF09\uFF0C\u8BF7\u5148\u8FD0\u884C bun run windy doctor --repair`);
10853
11341
  }
10854
11342
 
10855
- // src/update/module-selection.ts
10856
- function resolveUpdateTargetModules(sourceModules, explicitTarget) {
10857
- return explicitTarget ? [...explicitTarget] : resolveModuleSelection({
10858
- requested: sourceModules,
10859
- includeRecommended: false
10860
- });
10861
- }
10862
-
10863
11343
  // src/update/legacy-python-agent-retirement.ts
10864
11344
  import { lstat as lstat8, readFile as readFile22 } from "fs/promises";
10865
- import { join as join28 } from "path";
11345
+ import { join as join29 } from "path";
10866
11346
  var retiredKeys = [
10867
11347
  "AGENT_DATABASE_URL",
10868
11348
  "AGENT_RUN_STORE_PATH",
@@ -10873,7 +11353,7 @@ var retiredKeys = [
10873
11353
  "OTEL_EXPORTER_OTLP_HEADERS"
10874
11354
  ];
10875
11355
  async function inspectPythonAgentRetirement(projectDirectory) {
10876
- const path2 = join28(projectDirectory, ".env");
11356
+ const path2 = join29(projectDirectory, ".env");
10877
11357
  try {
10878
11358
  const stat3 = await lstat8(path2);
10879
11359
  if (stat3.isSymbolicLink() || !stat3.isFile())
@@ -10906,7 +11386,7 @@ var oxcManagedPaths = new Set([
10906
11386
  ]);
10907
11387
  async function createUpdatePlan(request, artifacts, dependencies = {}) {
10908
11388
  await assertProjectReadyForUpdate(request.projectDirectory);
10909
- const manifestPath = join29(request.projectDirectory, ".windy/files.json");
11389
+ const manifestPath = join30(request.projectDirectory, ".windy/files.json");
10910
11390
  const expectedManifestHash = hashContent(await readFile23(manifestPath));
10911
11391
  const generationSnapshot = await readGenerationProvenanceSnapshot(request.projectDirectory);
10912
11392
  const provenance = generationSnapshot.provenance;
@@ -10964,10 +11444,10 @@ async function createUpdatePlan(request, artifacts, dependencies = {}) {
10964
11444
  const paths = new Set([...sourceEntries.keys(), ...targetEntries.keys()]);
10965
11445
  for (const path2 of paths) {
10966
11446
  if (!projectSnapshots.has(path2)) {
10967
- projectSnapshots.set(path2, await readOptional(join29(request.projectDirectory, path2)));
11447
+ projectSnapshots.set(path2, await readOptional(join30(request.projectDirectory, path2)));
10968
11448
  }
10969
11449
  if (!projectModes.has(path2)) {
10970
- projectModes.set(path2, await readModeOptional(join29(request.projectDirectory, path2)));
11450
+ projectModes.set(path2, await readModeOptional(join30(request.projectDirectory, path2)));
10971
11451
  }
10972
11452
  }
10973
11453
  const files = await planFiles({
@@ -10983,19 +11463,31 @@ async function createUpdatePlan(request, artifacts, dependencies = {}) {
10983
11463
  recipeProjectOwnedPaths,
10984
11464
  migrateDshPlatform
10985
11465
  });
11466
+ if (!request.lifecycle) {
11467
+ reconcileMediaRuntimeRegistry(files, projectSnapshots.get(projectModuleRegistryPath), provenance.modules.map(({ name }) => name), targetProvenance.modules.map(({ name }) => name));
11468
+ }
11469
+ await reconcileAdminSettingsYamlPlan({
11470
+ files,
11471
+ sourceEntries,
11472
+ targetEntries,
11473
+ projectSnapshots,
11474
+ baseDirectory: artifactSet.baseDirectory,
11475
+ targetDirectory: artifactSet.targetDirectory
11476
+ });
10986
11477
  preserveTestsForExistingProjectOwnedSources(files, sourceEntries);
10987
11478
  await deriveDockerfilePlans(files, projectSnapshots, new Set([
10988
11479
  ...paths,
10989
11480
  ...workspaceSnapshot.manifests.keys(),
10990
11481
  ...targetWorkspaceSnapshot.manifests.keys()
10991
11482
  ]), targetWorkspaceSnapshot.manifests);
11483
+ validateEnvironmentMountPlan(files, projectSnapshots.get("windy.environment.yaml"));
10992
11484
  for (const file of files) {
10993
11485
  file.expectedProjectMode = projectModes.get(file.path);
10994
11486
  if (file.action === "create" || file.action === "update") {
10995
- file.mode = file.expectedProjectMode ?? await readModeOptional(join29(artifactSet.targetDirectory, file.path));
11487
+ file.mode = file.expectedProjectMode ?? await readModeOptional(join30(artifactSet.targetDirectory, file.path));
10996
11488
  }
10997
11489
  if (file.action === "create") {
10998
- file.parentMode = await readSourceParentMode(join29(artifactSet.targetDirectory, dirname9(file.path)));
11490
+ file.parentMode = await readSourceParentMode(join30(artifactSet.targetDirectory, dirname9(file.path)));
10999
11491
  }
11000
11492
  }
11001
11493
  const plannedPaths = new Set(files.map(({ path: path2 }) => path2));
@@ -11129,7 +11621,7 @@ function frozenManifestHash(file, finalizedHashes) {
11129
11621
  // src/update/format-stabilizer.ts
11130
11622
  import { mkdir as mkdir8, mkdtemp as mkdtemp3, readFile as readFile24, rm as rm7, writeFile as writeFile13 } from "fs/promises";
11131
11623
  import { tmpdir as tmpdir3 } from "os";
11132
- import { dirname as dirname10, extname as extname4, join as join30, resolve as resolve6, sep as sep5 } from "path";
11624
+ import { dirname as dirname10, extname as extname4, join as join31, resolve as resolve6, sep as sep5 } from "path";
11133
11625
  var formattedExtensions = new Set([
11134
11626
  ".cjs",
11135
11627
  ".css",
@@ -11168,7 +11660,7 @@ async function stabilizeManagedContent(result2, execute, writeProjectFile) {
11168
11660
  }
11169
11661
  }
11170
11662
  inputs.set(".windy/generation.json", new TextEncoder().encode(generationContent));
11171
- const temporaryRoot = await mkdtemp3(join30(tmpdir3(), "windy-update-format-"));
11663
+ const temporaryRoot = await mkdtemp3(join31(tmpdir3(), "windy-update-format-"));
11172
11664
  try {
11173
11665
  const stagedPaths = await stageInputs(temporaryRoot, inputs);
11174
11666
  for (let offset = 0;offset < stagedPaths.length; offset += 80) {
@@ -11176,7 +11668,7 @@ async function stabilizeManagedContent(result2, execute, writeProjectFile) {
11176
11668
  "x",
11177
11669
  "oxfmt",
11178
11670
  "--write",
11179
- `--config=${join30(result2.plan.projectDirectory, ".oxfmtrc.json")}`,
11671
+ `--config=${join31(result2.plan.projectDirectory, ".oxfmtrc.json")}`,
11180
11672
  ...stagedPaths.slice(offset, offset + 80)
11181
11673
  ], result2.plan.projectDirectory);
11182
11674
  }
@@ -11225,7 +11717,7 @@ function safeTemporaryPath(root, path2) {
11225
11717
 
11226
11718
  // src/update/project-quality-verification.ts
11227
11719
  import { readFile as readFile25 } from "fs/promises";
11228
- import { join as join31 } from "path";
11720
+ import { join as join32 } from "path";
11229
11721
  var qualityScriptNames = ["lint", "typecheck", "test", "build"];
11230
11722
  function skippedProjectQualityVerification() {
11231
11723
  return {
@@ -11239,7 +11731,7 @@ function skippedProjectQualityVerification() {
11239
11731
  };
11240
11732
  }
11241
11733
  async function runProjectQualityVerification(projectDirectory, execute) {
11242
- const packageJson = JSON.parse(await readFile25(join31(projectDirectory, "package.json"), "utf8"));
11734
+ const packageJson = JSON.parse(await readFile25(join32(projectDirectory, "package.json"), "utf8"));
11243
11735
  const steps = [];
11244
11736
  for (const name of qualityScriptNames) {
11245
11737
  const stepName = `bun run ${name}`;
@@ -12322,7 +12814,7 @@ import {
12322
12814
  rm as rm11,
12323
12815
  writeFile as writeFile14
12324
12816
  } from "fs/promises";
12325
- import { dirname as dirname11, join as join32, relative as relative7 } from "path";
12817
+ import { dirname as dirname11, join as join33, relative as relative7 } from "path";
12326
12818
 
12327
12819
  // src/module-schema/config.ts
12328
12820
  import { readFile as readFile26 } from "fs/promises";
@@ -12463,17 +12955,17 @@ async function handoffModuleSchemaLineages(options) {
12463
12955
  async function stageModuleLineages(project, temporaryRoot, config, execute) {
12464
12956
  const outputs = [];
12465
12957
  for (const [index, module2] of config.modules.entries()) {
12466
- const output = join32(temporaryRoot, `module-${index}`);
12958
+ const output = join33(temporaryRoot, `module-${index}`);
12467
12959
  await mkdir9(output, { recursive: true });
12468
- const generatedConfig = join32(temporaryRoot, `module-${index}.config.ts`);
12960
+ const generatedConfig = join33(temporaryRoot, `module-${index}.config.ts`);
12469
12961
  await writeDrizzleConfig(generatedConfig, project, module2.schema, relative7(temporaryRoot, output));
12470
12962
  await execute(generatedConfig, temporaryRoot);
12471
12963
  const sqlFiles = await listSqlFiles(output);
12472
12964
  if (sqlFiles.length !== 1) {
12473
12965
  throw new Error(`${module2.key} baseline \u5FC5\u987B\u6070\u597D\u751F\u6210\u4E00\u4E2A migration`);
12474
12966
  }
12475
- await writeFile14(join32(output, sqlFiles[0]), metadataOnlySql("module"));
12476
- await writeFile14(join32(output, "lineage.json"), `${JSON.stringify({
12967
+ await writeFile14(join33(output, sqlFiles[0]), metadataOnlySql("module"));
12968
+ await writeFile14(join33(output, "lineage.json"), `${JSON.stringify({
12477
12969
  schemaVersion: 1,
12478
12970
  moduleKey: module2.key,
12479
12971
  schema: module2.schema,
@@ -12487,17 +12979,17 @@ async function stageModuleLineages(project, temporaryRoot, config, execute) {
12487
12979
  }
12488
12980
  async function stagePlatformLineage(project, temporaryRoot, config, execute) {
12489
12981
  const source = resolveProjectPath(project, config.platform.out);
12490
- const output = join32(temporaryRoot, "platform");
12982
+ const output = join33(temporaryRoot, "platform");
12491
12983
  await cp(source, output, { recursive: true });
12492
12984
  const before = new Set(await listSqlFiles(output));
12493
- const generatedConfig = join32(temporaryRoot, "platform.config.ts");
12985
+ const generatedConfig = join33(temporaryRoot, "platform.config.ts");
12494
12986
  await writeDrizzleConfig(generatedConfig, project, config.platform.schema, relative7(temporaryRoot, output));
12495
12987
  await execute(generatedConfig, temporaryRoot);
12496
12988
  const created = (await listSqlFiles(output)).filter((path2) => !before.has(path2));
12497
12989
  if (created.length !== 1) {
12498
12990
  throw new Error("\u5E73\u53F0 lineage handoff \u5FC5\u987B\u6070\u597D\u751F\u6210\u4E00\u4E2A migration");
12499
12991
  }
12500
- const sqlPath = join32(output, created[0]);
12992
+ const sqlPath = join33(output, created[0]);
12501
12993
  const allowed = config.modules.flatMap(({ legacyPlatformTables }) => legacyPlatformTables);
12502
12994
  validatePlatformHandoffSql(await readFile27(sqlPath, "utf8"), allowed);
12503
12995
  await writeFile14(sqlPath, metadataOnlySql("platform"));
@@ -12505,7 +12997,7 @@ async function stagePlatformLineage(project, temporaryRoot, config, execute) {
12505
12997
  }
12506
12998
  async function installStagedLineages(project, temporaryRoot, config, stagedModules, stagedPlatform) {
12507
12999
  const platform = resolveProjectPath(project, config.platform.out);
12508
- const backup = join32(temporaryRoot, "platform-backup");
13000
+ const backup = join33(temporaryRoot, "platform-backup");
12509
13001
  const installed = [];
12510
13002
  await rename2(platform, backup);
12511
13003
  try {
@@ -12552,9 +13044,9 @@ async function validateSchemaSources(project, config) {
12552
13044
  }
12553
13045
  }
12554
13046
  async function createTemporaryRoot(project) {
12555
- const windy = join32(project, ".windy");
13047
+ const windy = join33(project, ".windy");
12556
13048
  await mkdir9(windy, { recursive: true });
12557
- return await mkdtemp4(join32(windy, "module-schema-handoff-"));
13049
+ return await mkdtemp4(join33(windy, "module-schema-handoff-"));
12558
13050
  }
12559
13051
  async function listSqlFiles(root) {
12560
13052
  return (await readdir9(root)).filter((path2) => path2.endsWith(".sql")).sort();
@@ -12645,7 +13137,7 @@ async function readPackageConfiguration(projectDirectory) {
12645
13137
  }
12646
13138
  }
12647
13139
  function parsePackageConfiguration(value) {
12648
- const root = object(value, "windy.build.json");
13140
+ const root = object2(value, "windy.build.json");
12649
13141
  rejectUnknown(root, ["schemaVersion", "product", "platforms", "profiles"], "windy.build.json");
12650
13142
  if (root.schemaVersion !== 1)
12651
13143
  throw new Error("windy.build.json \u53EA\u652F\u6301 schemaVersion: 1");
@@ -12671,7 +13163,7 @@ function parsePackageConfiguration(value) {
12671
13163
  };
12672
13164
  }
12673
13165
  function parseProfile(value) {
12674
- const profile = object(value, "profile");
13166
+ const profile = object2(value, "profile");
12675
13167
  rejectUnknown(profile, [
12676
13168
  "key",
12677
13169
  "label",
@@ -12684,7 +13176,7 @@ function parseProfile(value) {
12684
13176
  const key = identifier(profile.key, "profile key");
12685
13177
  const inputs = array(profile.inputs, `profile ${key}.inputs`).map((item) => parseInput(item, key));
12686
13178
  unique2(inputs.map(({ key: inputKey }) => inputKey), `profile ${key} input key`);
12687
- const command = object(profile.command, `profile ${key}.command`);
13179
+ const command = object2(profile.command, `profile ${key}.command`);
12688
13180
  rejectUnknown(command, ["executable", "arguments"], `profile ${key}.command`);
12689
13181
  const executable = string(command.executable, `profile ${key}.command.executable`);
12690
13182
  if (shellExecutables.has(executable.toLowerCase()) || shellCharacters.test(executable) || !/^[a-zA-Z0-9._-]+$/u.test(executable)) {
@@ -12698,7 +13190,7 @@ function parseProfile(value) {
12698
13190
  }
12699
13191
  return argument;
12700
13192
  });
12701
- const output = object(profile.output, `profile ${key}.output`);
13193
+ const output = object2(profile.output, `profile ${key}.output`);
12702
13194
  rejectUnknown(output, ["directory", "artifactSuffix"], `profile ${key}.output`);
12703
13195
  const directory = string(output.directory, `profile ${key}.output.directory`);
12704
13196
  if (!isSafeRelativePath(directory) || !/^[a-zA-Z0-9._/-]+$/u.test(directory))
@@ -12707,7 +13199,7 @@ function parseProfile(value) {
12707
13199
  if (!artifactSuffix.startsWith(".") || shellCharacters.test(artifactSuffix) || !/^\.[a-zA-Z0-9._-]+$/u.test(artifactSuffix)) {
12708
13200
  throw new Error(`profile ${key} \u7684 output.artifactSuffix \u975E\u6CD5`);
12709
13201
  }
12710
- const requirements = object(profile.requirements, `profile ${key}.requirements`);
13202
+ const requirements = object2(profile.requirements, `profile ${key}.requirements`);
12711
13203
  rejectUnknown(requirements, ["docker", "buildx"], `profile ${key}.requirements`);
12712
13204
  const docker = boolean(requirements.docker, `profile ${key}.requirements.docker`);
12713
13205
  const buildx = boolean(requirements.buildx, `profile ${key}.requirements.buildx`);
@@ -12724,7 +13216,7 @@ function parseProfile(value) {
12724
13216
  };
12725
13217
  }
12726
13218
  function parseInput(value, profile) {
12727
- const input = object(value, `profile ${profile}.input`);
13219
+ const input = object2(value, `profile ${profile}.input`);
12728
13220
  rejectUnknown(input, ["key", "label", "required", "pattern"], `profile ${profile}.input`);
12729
13221
  const key = identifier(input.key, `profile ${profile}.input.key`);
12730
13222
  if (secretName.test(key))
@@ -12755,7 +13247,7 @@ function validatePlaceholders(value, inputs, profile) {
12755
13247
  if (value.replace(placeholder, "").includes("${"))
12756
13248
  throw new Error(`profile ${profile} \u5305\u542B\u672A\u58F0\u660E\u5360\u4F4D\u7B26`);
12757
13249
  }
12758
- function object(value, label) {
13250
+ function object2(value, label) {
12759
13251
  if (!value || typeof value !== "object" || Array.isArray(value))
12760
13252
  throw new Error(`${label} \u5FC5\u987B\u662F\u5BF9\u8C61`);
12761
13253
  return value;