create-windy 0.3.4 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.md +20 -3
  2. package/dist/cli.js +582 -109
  3. package/package.json +1 -1
  4. package/template/.windy-template.json +2 -2
  5. package/template/AGENTS.md +4 -0
  6. package/template/README.md +18 -11
  7. package/template/apps/server/src/application-services.ts +16 -0
  8. package/template/apps/server/src/data-governance/export/authorization-port.test.ts +5 -1
  9. package/template/apps/server/src/data-governance/export/drizzle-repository.integration.test.ts +2 -0
  10. package/template/apps/server/src/data-governance/export/drizzle-repository.ts +1 -0
  11. package/template/apps/server/src/data-governance/export/job-runtime.test.ts +3 -1
  12. package/template/apps/server/src/data-governance/export/job-runtime.ts +53 -14
  13. package/template/apps/server/src/data-governance/export/ndjson-stream.ts +8 -0
  14. package/template/apps/server/src/data-governance/export/postgres-recovery.integration.test.ts +2 -0
  15. package/template/apps/server/src/data-governance/export/renderer-job-runtime.test.ts +235 -0
  16. package/template/apps/server/src/data-governance/export/renderer-runtime.ts +293 -0
  17. package/template/apps/server/src/data-governance/export/renderer-selection.test.ts +67 -0
  18. package/template/apps/server/src/data-governance/export/repository.test.ts +2 -0
  19. package/template/apps/server/src/data-governance/export/request-fingerprint.test.ts +13 -0
  20. package/template/apps/server/src/data-governance/export/request-fingerprint.ts +2 -0
  21. package/template/apps/server/src/data-governance/export/routes.ts +11 -1
  22. package/template/apps/server/src/data-governance/export/service-support.ts +61 -4
  23. package/template/apps/server/src/data-governance/export/service.ts +8 -2
  24. package/template/apps/server/src/data-governance/export/types.ts +27 -0
  25. package/template/apps/server/src/index.ts +1 -0
  26. package/template/apps/server/src/jobs/artifact-service.test.ts +14 -2
  27. package/template/apps/server/src/jobs/artifact-service.ts +10 -0
  28. package/template/apps/server/src/jobs/drizzle-execution-repository.ts +17 -18
  29. package/template/apps/server/src/jobs/drizzle-execution.integration.test.ts +19 -1
  30. package/template/apps/server/src/jobs/drizzle-mappers.ts +3 -1
  31. package/template/apps/server/src/jobs/drizzle-recovery-repository.ts +49 -2
  32. package/template/apps/server/src/jobs/execution-repository.ts +17 -0
  33. package/template/apps/server/src/jobs/in-memory-control.ts +135 -0
  34. package/template/apps/server/src/jobs/in-memory-execution.ts +34 -65
  35. package/template/apps/server/src/jobs/worker.ts +27 -3
  36. package/template/apps/server/src/module-host/governed-export-route-support.ts +24 -5
  37. package/template/apps/server/src/module-host/governed-export-routes.test.ts +5 -1
  38. package/template/apps/server/src/module-host/governed-export-routes.ts +1 -2
  39. package/template/apps/server/src/module-host/governed-renderer-registration.test.ts +247 -0
  40. package/template/apps/server/src/module-host/governed-resources.test.ts +1 -0
  41. package/template/apps/server/src/module-host/governed-resources.ts +56 -0
  42. package/template/apps/server/src/module-host/http-runtime.ts +3 -0
  43. package/template/apps/server/src/module-host/job-port.test.ts +262 -0
  44. package/template/apps/server/src/module-host/job-port.ts +334 -0
  45. package/template/apps/server/src/module-host/request-context.ts +14 -26
  46. package/template/apps/server/src/module-host/route-installer.ts +11 -0
  47. package/template/apps/web/src/app/business-routes.ts +22 -0
  48. package/template/apps/web/src/components/common/AccessSnapshotBoundary.vue +112 -0
  49. package/template/apps/web/src/components/common/AccessSnapshotBoundary.webtest.ts +130 -0
  50. package/template/apps/web/src/components/common/AccessSnapshotNotice.vue +117 -0
  51. package/template/apps/web/src/composables/access-snapshot-change.ts +131 -0
  52. package/template/apps/web/src/composables/access-snapshot-change.webtest.ts +103 -0
  53. package/template/apps/web/src/composables/useAccessSnapshot.ts +48 -2
  54. package/template/apps/web/src/composables/useAccessSnapshot.webtest.ts +54 -0
  55. package/template/apps/web/src/composables/useAccessSnapshotMonitor.ts +42 -0
  56. package/template/apps/web/src/layout/AppShell.vue +28 -25
  57. package/template/apps/web/src/layout/AppShell.webtest.ts +1 -0
  58. package/template/apps/web/src/router/business-routes.ts +68 -0
  59. package/template/apps/web/src/router/business-routes.webtest.ts +85 -0
  60. package/template/apps/web/src/router/index.ts +7 -0
  61. package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +12 -0
  62. package/template/apps/web/src/services/access-api.ts +5 -1
  63. package/template/apps/web/src/services/access-api.webtest.ts +35 -0
  64. package/template/docs/architecture/ai-egress.md +12 -9
  65. package/template/docs/architecture/durable-jobs.md +7 -1
  66. package/template/docs/architecture/foundation-infrastructure.md +163 -0
  67. package/template/docs/architecture/module-host.md +115 -0
  68. package/template/docs/architecture/server-module-interface.md +39 -0
  69. package/template/docs/architecture/web-access-snapshot-resilience.md +60 -0
  70. package/template/docs/development/business-web-routes.md +66 -0
  71. package/template/docs/development/custom-login-page.md +3 -3
  72. package/template/docs/development/module-lifecycle.md +71 -0
  73. package/template/docs/operations/observability.md +5 -1
  74. package/template/docs/platform/agent-runtime.md +6 -6
  75. package/template/docs/platform/agent-tool-sandbox.md +58 -0
  76. package/template/docs/platform/document-ai-runtime.md +204 -0
  77. package/template/docs/platform/governed-export-renderers.md +132 -0
  78. package/template/docs/platform/navigation-registry.md +5 -0
  79. package/template/docs/platform/organization-membership.md +2 -1
  80. package/template/docs/platform/speech-ai-runtime.md +156 -0
  81. package/template/docs/platform/system-crud-api.md +7 -1
  82. package/template/docs/platform/vision-ai-runtime.md +97 -0
  83. package/template/docs/platform/web-system-crud.md +6 -0
  84. package/template/packages/config/package.json +1 -1
  85. package/template/packages/database/drizzle/0042_puzzling_rumiko_fujikawa.sql +27 -0
  86. package/template/packages/database/drizzle/meta/0042_snapshot.json +8541 -0
  87. package/template/packages/database/drizzle/meta/_journal.json +7 -0
  88. package/template/packages/database/package.json +1 -1
  89. package/template/packages/database/src/schema/durable-jobs.ts +1 -0
  90. package/template/packages/database/src/schema/governed-exports.ts +15 -0
  91. package/template/packages/database/src/schema-governed-exports.test.ts +21 -1
  92. package/template/packages/jobs/package.json +1 -1
  93. package/template/packages/jobs/src/handlers.ts +1 -0
  94. package/template/packages/jobs/src/types.ts +1 -0
  95. package/template/packages/modules/package.json +1 -1
  96. package/template/packages/server-sdk/index.ts +11 -0
  97. package/template/packages/server-sdk/package.json +1 -1
  98. package/template/packages/server-sdk/src/data-governance-registration.ts +46 -0
  99. package/template/packages/server-sdk/src/jobs-port.ts +69 -0
  100. package/template/packages/server-sdk/src/request-context.ts +2 -2
  101. package/template/packages/shared/package.json +1 -1
package/dist/cli.js CHANGED
@@ -295,6 +295,7 @@ var optionalModuleManifests = [
295
295
  "apps/server/src/application-services.ts",
296
296
  "apps/server/src/module-host/durable-context.ts",
297
297
  "apps/server/src/module-host/durable-context.test.ts",
298
+ "apps/server/src/module-host/job-port.test.ts",
298
299
  "apps/server/src/module-host/task-definitions.ts",
299
300
  "packages/database/src/schema-durable-recovery.test.ts",
300
301
  "apps/web/src/pages/system/components/SchedulerOperationsPanel.vue",
@@ -460,6 +461,7 @@ var optionalModuleManifests = [
460
461
  ],
461
462
  documentation: ["docs/development/example-work-order.md"],
462
463
  schemaFiles: ["packages/example-work-order/src/drizzle-schema.ts"],
464
+ migrationRoots: ["packages/example-work-order/drizzle"],
463
465
  databaseTables: ["work_orders"],
464
466
  databaseLineageOwner: "module"
465
467
  }
@@ -469,6 +471,7 @@ function module(name, label, description, dependencies) {
469
471
  return {
470
472
  name,
471
473
  version: "0.1.0",
474
+ windyRange: "^0.3.0",
472
475
  label,
473
476
  description,
474
477
  selection,
@@ -479,11 +482,15 @@ function module(name, label, description, dependencies) {
479
482
  fileRoots: [],
480
483
  documentation: [],
481
484
  schemaFiles: [],
485
+ migrationRoots: [],
482
486
  databaseTables: [],
483
487
  databaseLineageOwner: "platform",
484
488
  transientDatabaseTables: [],
485
489
  databaseStatementTokens: [],
486
- uninstallPolicy: "retain-data"
490
+ uninstallPolicy: "retain-data",
491
+ installRecipe: `module-${name}-install`,
492
+ updateRecipes: [`module-${name}-update-0.1.0`],
493
+ uninstallRecipe: `module-${name}-uninstall`
487
494
  };
488
495
  }
489
496
 
@@ -688,6 +695,7 @@ function module2(name, label, description, selection, dependencies = []) {
688
695
  return {
689
696
  name,
690
697
  version: "0.1.0",
698
+ windyRange: "^0.3.0",
691
699
  label,
692
700
  description,
693
701
  selection,
@@ -698,11 +706,15 @@ function module2(name, label, description, selection, dependencies = []) {
698
706
  fileRoots: [],
699
707
  documentation: [],
700
708
  schemaFiles: [],
709
+ migrationRoots: [],
701
710
  databaseTables: [],
702
711
  databaseLineageOwner: "platform",
703
712
  transientDatabaseTables: [],
704
713
  databaseStatementTokens: [],
705
- uninstallPolicy: "retain-data"
714
+ uninstallPolicy: "retain-data",
715
+ installRecipe: `module-${name}-install`,
716
+ updateRecipes: [`module-${name}-update-0.1.0`],
717
+ uninstallRecipe: `module-${name}-uninstall`
706
718
  };
707
719
  }
708
720
  function validateCatalog(manifests) {
@@ -723,6 +735,12 @@ function validateCatalog(manifests) {
723
735
  if (!names.has(conflict))
724
736
  throw new Error(`${manifest.name} \u51B2\u7A81\u6A21\u5757\u672A\u77E5\uFF1A${conflict}`);
725
737
  }
738
+ if (!manifest.installRecipe) {
739
+ throw new Error(`${manifest.name} \u7F3A\u5C11\u5B89\u88C5 recipe`);
740
+ }
741
+ if (!manifest.uninstallRecipe) {
742
+ throw new Error(`${manifest.name} \u7F3A\u5C11\u5378\u8F7D recipe`);
743
+ }
726
744
  }
727
745
  return manifests;
728
746
  }
@@ -807,6 +825,11 @@ var helpText = `create-windy <\u9879\u76EE\u540D> [\u9009\u9879]
807
825
  create-windy update --check [--cwd <\u76EE\u5F55>]
808
826
  create-windy update --dry-run [--to <\u7248\u672C>] [--cwd <\u76EE\u5F55>]
809
827
  create-windy update [--to <\u7248\u672C>] [--cwd <\u76EE\u5F55>]
828
+ create-windy module list [--cwd <\u76EE\u5F55>]
829
+ create-windy module add <key...> [--dry-run] [--cwd <\u76EE\u5F55>]
830
+ create-windy module update [key...] [--dry-run] [--cwd <\u76EE\u5F55>]
831
+ create-windy module remove <key...> [--dry-run] [--data-disposition <\u7B56\u7565>]
832
+ [--data-plan <\u6587\u4EF6>] [--confirm-data-disposition] [--cwd <\u76EE\u5F55>]
810
833
  create-windy reconcile --to <\u7CBE\u786E\u7248\u672C> [--cwd <\u76EE\u5F55>] [--apply]
811
834
  create-windy schema handoff [--config <JSON>] [--cwd <\u76EE\u5F55>]
812
835
 
@@ -3253,9 +3276,9 @@ MCP \u6BCF\u6B21\u8C03\u7528\u90FD\u4F1A\u91CD\u65B0\u6267\u884C Feature\u3001RB
3253
3276
  \u548C \`dev:agent\` \u662F\u53E6\u4E00\u6761\u53EF\u9009\u94FE\u8DEF\u3002\u5B8C\u6574\u8FB9\u754C\u89C1
3254
3277
  [MCP \u4E1A\u52A1\u80FD\u529B](./docs/architecture/mcp-business-capabilities.md) \u4E0E
3255
3278
  [Agent Tool](./docs/platform/agent-tools.md)\u3002` : `\u5F53\u524D\u7EC4\u5408\u672A\u5305\u542B \`system.agent\`\uFF0CMCP Server\u3001Agent Tool Host \u548C\u76F8\u5173\u6587\u6863\u5DF2\u88AB
3256
- \u7269\u7406\u88C1\u526A\u3002\u82E5\u9879\u76EE\u5C1A\u53EF\u91CD\u65B0\u751F\u6210\uFF0C\u8BF7\u5728\u521B\u5EFA\u65F6\u589E\u52A0 \`--module system.agent\` \u6216\u4F7F\u7528
3257
- \`--all-modules\`\uFF1B\u90E8\u7F72\u540E\u7684\u901A\u7528 \`module add\` \u5C1A\u672A\u4EA4\u4ED8\uFF0C\u4E0D\u8981\u624B\u5DE5\u590D\u5236 MCP Adapter
3258
- \u6216\u53E6\u5EFA\u6388\u6743\u94FE\u3002\u4ED3\u5E93 Skill \u4ECD\u53EF\u7528\u4E8E\u666E\u901A\u4E1A\u52A1\u6A21\u5757\u5F00\u53D1\u3002`}
3279
+ \u7269\u7406\u88C1\u526A\u3002\u521B\u5EFA\u65F6\u53EF\u589E\u52A0 \`--module system.agent\` \u6216\u4F7F\u7528 \`--all-modules\`\uFF1B\u5DF2\u90E8\u7F72
3280
+ \u9879\u76EE\u4F7F\u7528 \`bun run windy module add system.agent\`\uFF0C\u4E0D\u8981\u624B\u5DE5\u590D\u5236 MCP Adapter \u6216
3281
+ \u53E6\u5EFA\u6388\u6743\u94FE\u3002\u4ED3\u5E93 Skill \u4ECD\u53EF\u7528\u4E8E\u666E\u901A\u4E1A\u52A1\u6A21\u5757\u5F00\u53D1\u3002`}
3259
3282
  `;
3260
3283
  }
3261
3284
 
@@ -3654,7 +3677,7 @@ OPENSEARCH_PASSWORD=
3654
3677
  `;
3655
3678
  }
3656
3679
  // src/project-files.ts
3657
- function starterDocsReadme(includeOfflineLicense = true, includeAgent = true) {
3680
+ function starterDocsReadme(includeOfflineLicense = true, includeAgent = true, includeOperations = true) {
3658
3681
  return `# Windy Starter \u6587\u6863
3659
3682
 
3660
3683
  \u672C\u76EE\u5F55\u53EA\u5305\u542B\u5BA2\u6237\u8FD0\u884C\u9879\u76EE\u9700\u8981\u7684\u5E73\u53F0\u67B6\u6784\u3001\u5F00\u53D1\u63A5\u5165\u548C\u8FD0\u7EF4\u53EF\u89C2\u6D4B\u6027\u6587\u6863\u3002
@@ -3664,9 +3687,11 @@ License \u7B7E\u53D1\u4E2D\u5FC3\u3001\u72EC\u7ACB\u7B7E\u540D\u670D\u52A1\u3001
3664
3687
  - \u67B6\u6784\uFF1A\u4ECE [Module Manifest \u7EC4\u5408](./architecture/module-manifest-composition.md) \u5F00\u59CB\u3002
3665
3688
  - \u6570\u636E\u5E93\uFF1A\u4E1A\u52A1\u8868\u4F7F\u7528 [Migration Bundle \u4E0E\u72EC\u7ACB Drizzle lineage](./architecture/migration-bundle.md)\u3002
3666
3689
  - \u5E73\u53F0\u5F00\u53D1\uFF1A\u4ECE [\u7CFB\u7EDF CRUD API](./platform/system-crud-api.md) \u548C [CRUD \u751F\u6210\u5668](./development/crud-generator.md) \u5F00\u59CB\u3002
3690
+ - Web \u4E1A\u52A1\u9875\u9762\uFF1A[project-owned \u4E1A\u52A1 Web \u8DEF\u7531](./development/business-web-routes.md)\u3002
3691
+ - \u90E8\u7F72\u671F\u6A21\u5757\u7BA1\u7406\uFF1A[\u6A21\u5757\u751F\u547D\u5468\u671F](./development/module-lifecycle.md)\u3002
3667
3692
  - \u4E1A\u52A1\u6A21\u5757\u5F00\u53D1\uFF1A[windy-business-module Skill](../.agents/skills/windy-business-module/SKILL.md)\u3002
3668
3693
  ${includeOfflineLicense ? "- License \u6D88\u8D39\u7AEF\uFF1A[\u79BB\u7EBF\u6FC0\u6D3B\u4E0E\u5B89\u88C5\u534F\u8BAE](./license/license-offline-activation-code.md)\uFF1B\u8FD0\u884C\u65F6\u80FD\u529B\u7531 npm \u4F9D\u8D56 `@southwind-ai/license` \u63D0\u4F9B\u3002" : ""}
3669
- - \u8FD0\u7EF4\uFF1A[\u53EF\u89C2\u6D4B\u6027](./operations/observability.md) \u4E0E [\u8FD0\u7EF4\u603B\u89C8](./platform/operations-overview.md)\u3002
3694
+ - \u8FD0\u7EF4\uFF1A[\u53EF\u89C2\u6D4B\u6027](./operations/observability.md)${includeOperations ? " \u4E0E [\u8FD0\u7EF4\u603B\u89C8](./platform/operations-overview.md)" : ""}\u3002
3670
3695
  ${includeAgent ? "- Agent\u3001\u591A\u6A21\u6001 Runtime \u4E0E MCP\uFF1A[Agent Runtime \u63A5\u5165](./platform/agent-runtime.md)\u3001[Document AI Runtime](./platform/document-ai-runtime.md)\u3001[Speech AI Runtime](./platform/speech-ai-runtime.md)\u3001[\u53D7\u63A7\u5DE5\u5177](./platform/agent-tools.md)\u4E0E[MCP \u4E1A\u52A1\u80FD\u529B](./architecture/mcp-business-capabilities.md)\u3002" : ""}
3671
3696
  - \u517C\u5BB9\uFF1A[RuoYi-Vue Adapter](./adapters/ruoyi-vue-compatibility.md)\u3002
3672
3697
  `;
@@ -3675,7 +3700,7 @@ function starterReadme(projectName, includeExample = true, includeOxc = true, pr
3675
3700
  const selected = new Set(selectedModules);
3676
3701
  const includeAgent = selected.has("system.agent");
3677
3702
  const includeSearch = selected.has("system.search");
3678
- const moduleRows = moduleDistributionCatalog.map(({ name, description, selection }) => `| \`${name}\` | ${selected.has(name) ? "\u5DF2\u5305\u542B" : "\u672A\u5305\u542B"} | ${selection === "required" ? "H0" : "H2"} | ${description} |`).join(`
3703
+ const moduleRows = moduleDistributionCatalog.map(({ name, description, selection }) => `| \`${name}\` | ${selected.has(name) ? "\u5DF2\u5305\u542B" : "\u672A\u5305\u542B"} | ${selection === "required" ? "H0" : name === "work-order" ? "H3" : "H2"} | ${description} |`).join(`
3679
3704
  `);
3680
3705
  return `# ${projectName}
3681
3706
 
@@ -3832,19 +3857,22 @@ create-windy \u5728\u672C\u673A \`.env\` \u4E2D\u968F\u673A\u751F\u6210\u7684 \`
3832
3857
  | \u6CD5\u5F8B\u8BB8\u53EF | \`${projectLicense}\` |
3833
3858
 
3834
3859
  \u5E73\u53F0\u5F53\u524D\u6CE8\u518C\u7684\u5168\u90E8\u6A21\u5757\u5982\u4E0B\u3002H0 \u8868\u793A profile \u9501\u5B9A\u6838\u5FC3\uFF1BH2 \u8868\u793A\u521B\u5EFA\u65F6\u53EF\u4EE5\u7269\u7406\u5305\u542B\u6216
3835
- \u6392\u9664\uFF0C\u5E76\u5DF2\u6709\u751F\u6210\u3001\u7C7B\u578B\u68C0\u67E5\u3001\u6D4B\u8BD5\u548C\u6784\u5EFA\u8BC1\u636E\u3002H2 \u4E0D\u4EE3\u8868\u9879\u76EE\u90E8\u7F72\u540E\u5DF2\u7ECF\u652F\u6301\u65E0\u635F\u5B89\u88C5\u3001
3836
- \u5347\u7EA7\u6216\u5378\u8F7D\uFF1B\u90A3\u9700\u8981 H3 \u7684\u751F\u547D\u5468\u671F\u547D\u4EE4\u4E0E\u6570\u636E\u5904\u7F6E\u8BC1\u636E\u3002
3860
+ \u6392\u9664\uFF1BH3 \u8FD8\u5177\u5907\u90E8\u7F72\u671F\u5B89\u88C5\u3001\u66F4\u65B0\u3001\u6570\u636E\u4FDD\u7559\u5378\u8F7D\u548C\u91CD\u88C5\u8BC1\u636E\u3002\u901A\u7528\u751F\u547D\u5468\u671F\u547D\u4EE4\u4F1A\u4FDD\u5B88
3861
+ \u5904\u7406\u6240\u6709\u76EE\u5F55\u6A21\u5757\uFF0C\u4F46\u53EA\u6709\u901A\u8FC7\u5B8C\u6574 H3 \u56DE\u5F52\u7684\u6A21\u5757\u624D\u6807\u8BB0\u4E3A H3\u3002
3837
3862
 
3838
3863
  | Module Key | \u5F53\u524D\u9879\u76EE | \u70ED\u63D2\u62D4\u53EF\u9760\u5EA6 | \u8BF4\u660E |
3839
3864
  | ---------- | -------- | ------------ | ---- |
3840
3865
  ${moduleRows}
3841
3866
 
3842
3867
  \u9879\u76EE\u81EA\u6709\u4E1A\u52A1\u6A21\u5757\u4EE5 \`apps/server/src/installed-business-modules.ts\` \u88C5\u914D\u7684 Module Manifest
3843
- \u4E3A\u51C6\uFF1Bshared-scaffold README \u4E0D\u590D\u5236\u9879\u76EE\u4E1A\u52A1 Key\uFF0C\u907F\u514D\u5E73\u53F0\u5347\u7EA7\u56FA\u5316\u4EA7\u54C1\u547D\u540D\u3002
3868
+ \u4E3A\u51C6\uFF1B\u5BF9\u5E94 Vue \u9875\u9762\u5728 project-owned \u7684
3869
+ \`apps/web/src/app/business-routes.ts\` \u6CE8\u518C\uFF0C\u4FA7\u680F\u7EE7\u7EED\u4ECE\u670D\u52A1\u7AEF Manifest \u5FEB\u7167\u751F\u6210\u3002
3870
+ \u4E0D\u8981\u4E3A\u589E\u52A0\u4E1A\u52A1\u9875\u9762\u4FEE\u6539 shared-scaffold Router \u6216 AppSidebar\u3002shared-scaffold README
3871
+ \u4E0D\u590D\u5236\u9879\u76EE\u4E1A\u52A1 Key\uFF0C\u907F\u514D\u5E73\u53F0\u5347\u7EA7\u56FA\u5316\u4EA7\u54C1\u547D\u540D\u3002
3844
3872
 
3845
3873
  \u7CBE\u786E\u751F\u6210\u6765\u6E90\u548C\u9009\u62E9\u8BB0\u5F55\u5728 \`.windy/generation.json\`\u3002\u5F53\u524D\u7248\u672C\u5DF2\u63D0\u4F9B\u53EF\u56DE\u6EDA\u7684
3846
- \`windy update\`\uFF1B\u901A\u7528 \`module add/remove\` \u5C1A\u672A\u4EA4\u4ED8\uFF0C\u4E0D\u8981\u628A Feature Flag \u5173\u95ED\u5F53\u6210
3847
- \u6A21\u5757\u5DF2\u7ECF\u5378\u8F7D\u3002
3874
+ \`windy update\` \u4E0E \`windy module list/add/update/remove\`\u3002Feature Flag \u5173\u95ED\u4ECD\u4E0D
3875
+ \u7B49\u4E8E\u6A21\u5757\u5378\u8F7D\u3002
3848
3876
 
3849
3877
  ## \u5E38\u7528\u547D\u4EE4
3850
3878
 
@@ -3860,6 +3888,9 @@ bun run build
3860
3888
  bun run windy doctor
3861
3889
  bun run windy update --check
3862
3890
  bun run windy update --dry-run
3891
+ bun run windy module list
3892
+ bun run windy module add work-order --dry-run
3893
+ bun run windy module remove work-order --dry-run
3863
3894
  bun run windy reconcile --to <\u7CBE\u786E\u76EE\u6807\u7248\u672C>
3864
3895
  ${includeOxc ? `bun run lint
3865
3896
  bun run format` : "# \u672C\u9879\u76EE\u672A\u9884\u88C5 lint/format \u5DE5\u5177\uFF0C\u53EF\u6309\u56E2\u961F\u9700\u8981\u81EA\u884C\u9009\u62E9\u3002"}
@@ -3896,8 +3927,9 @@ create-windy \u9ED8\u8BA4\u5EFA\u7ACB \`main\` \u5206\u652F\u5E76\u521B\u5EFA \`
3896
3927
  \u6CA1\u6709\u914D\u7F6E Git \u7528\u6237\u8EAB\u4EFD\uFF0C\u6587\u4EF6\u4F1A\u4FDD\u7559\u5728\u6682\u5B58\u533A\uFF1B\u914D\u7F6E\u8EAB\u4EFD\u540E\u624B\u52A8\u63D0\u4EA4\u5373\u53EF\u3002
3897
3928
 
3898
3929
  \u5F53\u524D\u5DF2\u4EA4\u4ED8\u7684 Windy \u66F4\u65B0\u5668\u4F1A\u6839\u636E Project Lock \u548C\u6587\u4EF6\u6240\u6709\u6743\uFF0C\u4ECE npm \u5386\u53F2 artifact
3899
- \u91CD\u5EFA Base \u5E76\u6267\u884C\u4E09\u65B9\u5408\u5E76\uFF1B\u5B83\u4E0D\u8FDE\u63A5\u79C1\u6709\u6E90\u4ED3\u5E93\uFF0C\u4E5F\u4E0D\u4F1A\u9759\u9ED8\u8986\u76D6\u9879\u76EE\u4EE3\u7801\u3002\u90E8\u7F72\u671F\u901A\u7528
3900
- \`module add/update/remove\` \u5C1A\u672A\u4EA4\u4ED8\u3002
3930
+ \u91CD\u5EFA Base \u5E76\u6267\u884C\u4E09\u65B9\u5408\u5E76\uFF1B\u5B83\u4E0D\u8FDE\u63A5\u79C1\u6709\u6E90\u4ED3\u5E93\uFF0C\u4E5F\u4E0D\u4F1A\u9759\u9ED8\u8986\u76D6\u9879\u76EE\u4EE3\u7801\u3002\u6A21\u5757
3931
+ \u751F\u547D\u5468\u671F\u547D\u4EE4\u590D\u7528\u540C\u4E00\u8BA1\u5212\u3001\u4E8B\u52A1\u3001\u9A8C\u8BC1\u4E0E\u6062\u590D\u8FB9\u754C\uFF1B\u5378\u8F7D\u9ED8\u8BA4\u4FDD\u7559\u6570\u636E\u5E93\u6570\u636E\uFF0C\u5E76\u8981\u6C42
3932
+ \u663E\u5F0F\u786E\u8BA4\u6570\u636E\u5904\u7F6E\u8BA1\u5212\u3002
3901
3933
 
3902
3934
  ## License
3903
3935
 
@@ -4017,23 +4049,32 @@ var templateDocumentationFiles = [
4017
4049
  "docs/architecture/ai-runtime.md",
4018
4050
  "docs/architecture/data-scope-query-enforcement.md",
4019
4051
  "docs/architecture/durable-jobs.md",
4052
+ "docs/architecture/foundation-infrastructure.md",
4020
4053
  "docs/architecture/minimal-approval-workflow.md",
4021
4054
  "docs/architecture/mcp-business-capabilities.md",
4022
4055
  "docs/architecture/migration-bundle.md",
4056
+ "docs/architecture/module-host.md",
4023
4057
  "docs/architecture/module-manifest-composition.md",
4024
4058
  "docs/architecture/object-storage.md",
4025
4059
  "docs/architecture/search-provider-protocol.md",
4060
+ "docs/architecture/server-module-interface.md",
4061
+ "docs/architecture/web-access-snapshot-resilience.md",
4062
+ "docs/development/business-web-routes.md",
4026
4063
  "docs/development/crud-generator.md",
4027
4064
  "docs/development/custom-error-pages.md",
4028
4065
  "docs/development/custom-login-page.md",
4066
+ "docs/development/module-lifecycle.md",
4029
4067
  "docs/license/license-offline-activation-code.md",
4030
4068
  "docs/operations/observability.md",
4031
4069
  "docs/platform/agent-tools.md",
4032
4070
  "docs/platform/agent-runtime.md",
4071
+ "docs/platform/agent-tool-sandbox.md",
4033
4072
  "docs/platform/audit-reliability.md",
4034
4073
  "docs/platform/bulk-data-jobs.md",
4035
4074
  "docs/platform/business-user-directory.md",
4036
4075
  "docs/platform/data-access-control.md",
4076
+ "docs/platform/document-ai-runtime.md",
4077
+ "docs/platform/governed-export-renderers.md",
4037
4078
  "docs/platform/module-configuration.md",
4038
4079
  "docs/platform/navigation-registry.md",
4039
4080
  "docs/platform/notifications.md",
@@ -4042,7 +4083,9 @@ var templateDocumentationFiles = [
4042
4083
  "docs/platform/platform-authentication.md",
4043
4084
  "docs/platform/platform-shell-settings.md",
4044
4085
  "docs/platform/scheduled-task-executor.md",
4086
+ "docs/platform/speech-ai-runtime.md",
4045
4087
  "docs/platform/system-crud-api.md",
4088
+ "docs/platform/vision-ai-runtime.md",
4046
4089
  "docs/platform/web-system-crud.md",
4047
4090
  "docs/product/built-in-roles.md"
4048
4091
  ];
@@ -4263,7 +4306,7 @@ async function generateProject(input) {
4263
4306
  await writeFile8(join12(input.targetDirectory, "docker-compose.yml"), starterComposeFor(selectedModules.includes("system.agent"), selectedModules.includes("system.search")));
4264
4307
  await writeFile8(join12(input.targetDirectory, ".env.example"), environmentExample(includeExample, selectedModules.includes("system.agent"), selectedModules.includes("system.search")));
4265
4308
  await writeFile8(join12(input.targetDirectory, "README.md"), starterReadme(input.projectName, includeExample, includeOxc, projectLicense, selectedModules));
4266
- await writeFile8(join12(input.targetDirectory, "docs/README.md"), starterDocsReadme(selectedModules.includes("system.license"), selectedModules.includes("system.agent")));
4309
+ await writeFile8(join12(input.targetDirectory, "docs/README.md"), starterDocsReadme(selectedModules.includes("system.license"), selectedModules.includes("system.agent"), selectedModules.includes("system.operations")));
4267
4310
  await reflectCodeQualityChoice(input.targetDirectory, includeOxc);
4268
4311
  await applyWindyBaseLicense(input.targetDirectory);
4269
4312
  await applyProjectLicense(input.targetDirectory, projectLicense, input.licenseHolder);
@@ -6190,8 +6233,8 @@ function sentenceWithoutStop(value) {
6190
6233
  }
6191
6234
 
6192
6235
  // src/update/cli.ts
6193
- import { rm as rm10 } from "fs/promises";
6194
- import { resolve as resolve7 } from "path";
6236
+ import { rm as rm11 } from "fs/promises";
6237
+ import { resolve as resolve8 } from "path";
6195
6238
 
6196
6239
  // src/reconcile/managed-reconciler.ts
6197
6240
  import { rm as rm6 } from "fs/promises";
@@ -8599,8 +8642,51 @@ function isObject(value) {
8599
8642
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8600
8643
  }
8601
8644
 
8645
+ // src/update/planner-project-owned.ts
8646
+ function planGeneratedProjectOwned(path2, source, target, ours, theirs, expected) {
8647
+ if (!source && target) {
8648
+ return !ours && theirs ? ready(path2, "create", "\u6A21\u5757\u5B89\u88C5\u65B0\u589E\u9879\u76EE\u6269\u5C55\u70B9", expected, theirs) : conflict(path2, "\u6A21\u5757\u65B0\u589E\u8DEF\u5F84\u5DF2\u88AB\u9879\u76EE\u5360\u7528", expected);
8649
+ }
8650
+ if (source && !target) {
8651
+ if (!ours) {
8652
+ return ready(path2, "preserve", "\u6A21\u5757\u8DEF\u5F84\u5DF2\u79FB\u9664", expected);
8653
+ }
8654
+ return hashContent(ours) === source.hash ? ready(path2, "delete", "\u6A21\u5757\u5378\u8F7D\u79FB\u9664\u672A\u4FEE\u6539\u6269\u5C55\u70B9", expected) : conflict(path2, "\u5BA2\u6237\u4FEE\u6539\u8FC7\u6A21\u5757\u6269\u5C55\u70B9\uFF0C\u62D2\u7EDD\u5378\u8F7D", expected);
8655
+ }
8656
+ if (!source || !target || !ours || !theirs) {
8657
+ return ready(path2, "preserve", "\u9879\u76EE\u62E5\u6709\uFF0C\u6A21\u5757\u7EC4\u5408\u672A\u6539\u53D8", expected);
8658
+ }
8659
+ if (hashContent(ours) === target.hash) {
8660
+ return ready(path2, "preserve", "\u5DF2\u5305\u542B\u76EE\u6807\u6A21\u5757\u7EC4\u5408", expected);
8661
+ }
8662
+ if (source.hash === target.hash) {
8663
+ return ready(path2, "preserve", "\u6A21\u5757\u7EC4\u5408\u672A\u6539\u53D8", expected);
8664
+ }
8665
+ return hashContent(ours) === source.hash ? ready(path2, "update", "\u66F4\u65B0\u672A\u4FEE\u6539\u7684\u6A21\u5757\u7EC4\u5408\u6269\u5C55\u70B9", expected, theirs) : conflict(path2, "\u5BA2\u6237\u4FEE\u6539\u8FC7\u6A21\u5757\u7EC4\u5408\u6269\u5C55\u70B9", expected);
8666
+ }
8667
+ function ready(path2, action, reason, expectedProjectContent, content) {
8668
+ return {
8669
+ path: path2,
8670
+ ownership: "project-owned",
8671
+ action,
8672
+ reason,
8673
+ expectedProjectContent,
8674
+ content
8675
+ };
8676
+ }
8677
+ function conflict(path2, reason, expectedProjectContent) {
8678
+ return {
8679
+ path: path2,
8680
+ ownership: "project-owned",
8681
+ action: "preserve",
8682
+ reason: "\u5BA2\u6237\u5DF2\u4FEE\u6539",
8683
+ expectedProjectContent,
8684
+ conflict: reason
8685
+ };
8686
+ }
8687
+
8602
8688
  // src/update/planner-file.ts
8603
- async function planFile(path2, baseRoot, targetRoot, source, target, ours, provenance, targetContentOverride) {
8689
+ async function planFile(path2, baseRoot, targetRoot, source, target, ours, provenance, targetContentOverride, manageGeneratedProjectOwned = false) {
8604
8690
  const [base, theirs] = await Promise.all([
8605
8691
  readOptional(join19(baseRoot, path2)),
8606
8692
  targetContentOverride ? Promise.resolve(targetContentOverride) : readOptional(join19(targetRoot, path2))
@@ -8608,6 +8694,9 @@ async function planFile(path2, baseRoot, targetRoot, source, target, ours, prove
8608
8694
  const ownership = source?.ownership === "project-owned" ? source.ownership : target?.ownership || source?.ownership || classifyOwnership(path2);
8609
8695
  const expected = expectationForSnapshot(ours);
8610
8696
  if (ownership === "project-owned") {
8697
+ if (manageGeneratedProjectOwned) {
8698
+ return planGeneratedProjectOwned(path2, source, target, ours, theirs, expected);
8699
+ }
8611
8700
  return !source && !ours && theirs ? readyPlannedFile(path2, ownership, "create", "\u65B0\u589E\u9879\u76EE\u6269\u5C55\u70B9", expected, theirs) : readyPlannedFile(path2, ownership, "preserve", "\u9879\u76EE\u62E5\u6709\uFF0CWindy \u4E0D\u8986\u76D6", expected);
8612
8701
  }
8613
8702
  const repaired = ours && theirs ? repairKnownDerivedFile({
@@ -8622,26 +8711,26 @@ async function planFile(path2, baseRoot, targetRoot, source, target, ours, prove
8622
8711
  }
8623
8712
  if (path2.startsWith("packages/database/drizzle/") && path2.endsWith(".sql")) {
8624
8713
  if (source && !target) {
8625
- return conflict(path2, ownership, "\u5DF2\u53D1\u5E03 migration \u4E0D\u53EF\u5220\u9664", expected);
8714
+ return conflict2(path2, ownership, "\u5DF2\u53D1\u5E03 migration \u4E0D\u53EF\u5220\u9664", expected);
8626
8715
  }
8627
8716
  if (source && target && source.hash !== target.hash) {
8628
- return conflict(path2, ownership, "\u5DF2\u53D1\u5E03 migration \u4E0D\u53EF\u6539\u5199\uFF0C\u53EA\u80FD\u8FFD\u52A0\u65B0\u6587\u4EF6", expected);
8717
+ return conflict2(path2, ownership, "\u5DF2\u53D1\u5E03 migration \u4E0D\u53EF\u6539\u5199\uFF0C\u53EA\u80FD\u8FFD\u52A0\u65B0\u6587\u4EF6", expected);
8629
8718
  }
8630
8719
  }
8631
8720
  if (!source && target) {
8632
8721
  if (!ours) {
8633
8722
  return readyPlannedFile(path2, ownership, "create", "\u76EE\u6807\u7248\u672C\u65B0\u589E", expected, theirs);
8634
8723
  }
8635
- return theirs && hashContent(ours) === target.hash ? readyPlannedFile(path2, ownership, "preserve", "\u5DF2\u5305\u542B\u76EE\u6807\u5185\u5BB9", expected) : conflict(path2, ownership, "\u76EE\u6807\u7248\u672C\u65B0\u589E\u8DEF\u5F84\u5DF2\u88AB\u9879\u76EE\u5360\u7528", expected);
8724
+ return theirs && hashContent(ours) === target.hash ? readyPlannedFile(path2, ownership, "preserve", "\u5DF2\u5305\u542B\u76EE\u6807\u5185\u5BB9", expected) : conflict2(path2, ownership, "\u76EE\u6807\u7248\u672C\u65B0\u589E\u8DEF\u5F84\u5DF2\u88AB\u9879\u76EE\u5360\u7528", expected);
8636
8725
  }
8637
8726
  if (source && !target) {
8638
8727
  if (!ours) {
8639
8728
  return readyPlannedFile(path2, ownership, "preserve", "\u9879\u76EE\u5DF2\u5220\u9664", expected);
8640
8729
  }
8641
- return hashContent(ours) === source.hash ? readyPlannedFile(path2, ownership, "delete", "\u76EE\u6807\u7248\u672C\u5220\u9664", expected) : conflict(path2, ownership, "\u5BA2\u6237\u4FEE\u6539\u540E Windy \u53C8\u5220\u9664", expected);
8730
+ return hashContent(ours) === source.hash ? readyPlannedFile(path2, ownership, "delete", "\u76EE\u6807\u7248\u672C\u5220\u9664", expected) : conflict2(path2, ownership, "\u5BA2\u6237\u4FEE\u6539\u540E Windy \u53C8\u5220\u9664", expected);
8642
8731
  }
8643
8732
  if (!source || !target || !ours || !theirs) {
8644
- return conflict(path2, ownership, "\u6587\u4EF6\u72B6\u6001\u4E0D\u5B8C\u6574\uFF0C\u4E0D\u80FD\u5B89\u5168\u63A8\u65AD", expected);
8733
+ return conflict2(path2, ownership, "\u6587\u4EF6\u72B6\u6001\u4E0D\u5B8C\u6574\uFF0C\u4E0D\u80FD\u5B89\u5168\u63A8\u65AD", expected);
8645
8734
  }
8646
8735
  const oursChanged = hashContent(ours) !== source.hash;
8647
8736
  const sourceArtifactHash = artifactHashOf(source);
@@ -8661,13 +8750,13 @@ async function planFile(path2, baseRoot, targetRoot, source, target, ours, prove
8661
8750
  }
8662
8751
  if (path2.endsWith("package.json") && base) {
8663
8752
  const merged = mergePackageJson(base, ours, theirs);
8664
- return merged.content ? readyPlannedFile(path2, ownership, "update", "package.json \u5B57\u6BB5\u7EA7\u5408\u5E76", expected, merged.content) : conflict(path2, ownership, `\u5B57\u6BB5\u51B2\u7A81\uFF1A${merged.conflicts.join("\u3001")}`, expected);
8753
+ return merged.content ? readyPlannedFile(path2, ownership, "update", "package.json \u5B57\u6BB5\u7EA7\u5408\u5E76", expected, merged.content) : conflict2(path2, ownership, `\u5B57\u6BB5\u51B2\u7A81\uFF1A${merged.conflicts.join("\u3001")}`, expected);
8665
8754
  }
8666
8755
  if (ownership === "shared-scaffold" && base) {
8667
8756
  const merged = await mergeTextFile(base, ours, theirs);
8668
- return merged.content ? readyPlannedFile(path2, ownership, "update", "\u4E09\u65B9\u5408\u5E76", expected, merged.content) : conflict(path2, ownership, merged.conflict || "\u4E09\u65B9\u5408\u5E76\u5931\u8D25", expected);
8757
+ return merged.content ? readyPlannedFile(path2, ownership, "update", "\u4E09\u65B9\u5408\u5E76", expected, merged.content) : conflict2(path2, ownership, merged.conflict || "\u4E09\u65B9\u5408\u5E76\u5931\u8D25", expected);
8669
8758
  }
8670
- return conflict(path2, ownership, "\u5BA2\u6237\u548C Windy \u5747\u5DF2\u4FEE\u6539", expected);
8759
+ return conflict2(path2, ownership, "\u5BA2\u6237\u548C Windy \u5747\u5DF2\u4FEE\u6539", expected);
8671
8760
  }
8672
8761
  function targetChangedSinceBase(sourceHash, base, target) {
8673
8762
  return base && target ? hashContent(base) !== hashContent(target) : !!target && hashContent(target) !== sourceHash;
@@ -8685,7 +8774,7 @@ function readyPlannedFile(path2, ownership, action, reason, expectedProjectConte
8685
8774
  function expectationForSnapshot(content) {
8686
8775
  return content ? { kind: "hash", value: hashContent(content) } : { kind: "missing" };
8687
8776
  }
8688
- function conflict(path2, ownership, reason, expectedProjectContent) {
8777
+ function conflict2(path2, ownership, reason, expectedProjectContent) {
8689
8778
  return {
8690
8779
  path: path2,
8691
8780
  ownership,
@@ -8868,7 +8957,8 @@ var recipes = [
8868
8957
  { id: "starter-0.3.0-to-0.3.1", from: "0.3.0", to: "0.3.1" },
8869
8958
  { id: "starter-0.3.1-to-0.3.2", from: "0.3.1", to: "0.3.2" },
8870
8959
  { id: "starter-0.3.2-to-0.3.3", from: "0.3.2", to: "0.3.3" },
8871
- { id: "starter-0.3.3-to-0.3.4", from: "0.3.3", to: "0.3.4" }
8960
+ { id: "starter-0.3.3-to-0.3.4", from: "0.3.3", to: "0.3.4" },
8961
+ { id: "starter-0.3.4-to-0.3.5", from: "0.3.4", to: "0.3.5" }
8872
8962
  ];
8873
8963
  function resolveRecipeChain(sourceVersion, targetVersion) {
8874
8964
  if (sourceVersion === targetVersion)
@@ -8909,13 +8999,15 @@ async function createUpdatePlan(request, artifacts) {
8909
8999
  const expectedManifestHash = hashContent(await readFile17(manifestPath));
8910
9000
  const generationSnapshot = await readGenerationProvenanceSnapshot(request.projectDirectory);
8911
9001
  const provenance = generationSnapshot.provenance;
8912
- const artifactSet = await artifacts.prepare(request.projectDirectory, provenance);
9002
+ const artifactSet = await artifacts.prepare(request.projectDirectory, provenance, request.targetModules);
8913
9003
  const targetProvenance = await readGenerationProvenance(artifactSet.targetDirectory);
8914
9004
  const targetVersion = request.targetVersion || targetProvenance.generatorVersion;
8915
9005
  if (targetVersion !== targetProvenance.generatorVersion) {
8916
9006
  throw new Error(`\u5F53\u524D CLI \u53EA\u80FD\u66F4\u65B0\u5230 ${targetProvenance.generatorVersion}\uFF0C\u8BF7\u8FD0\u884C create-windy@${targetVersion}`);
8917
9007
  }
8918
- const recipes2 = resolveRecipeChain(provenance.windyVersion, targetVersion);
9008
+ const versionRecipes = resolveRecipeChain(provenance.windyVersion, targetVersion);
9009
+ const moduleRecipes = resolveModuleRecipeIds(request);
9010
+ const recipes2 = [...versionRecipes.map(({ id }) => id), ...moduleRecipes];
8919
9011
  const [rawSourceManifest, rawTargetManifest] = await Promise.all([
8920
9012
  readManagedFilesManifest(request.projectDirectory),
8921
9013
  readManagedFilesManifest(artifactSet.targetDirectory)
@@ -8928,6 +9020,7 @@ async function createUpdatePlan(request, artifacts) {
8928
9020
  ...generatedTargetManifest.files.map(({ path: path2 }) => path2)
8929
9021
  ]);
8930
9022
  const workspaceSnapshot = await snapshotWorkspacePackages(request.projectDirectory, { allowMissingRoot: true });
9023
+ const targetWorkspaceSnapshot = await snapshotWorkspacePackages(artifactSet.targetDirectory, { allowMissingRoot: true });
8931
9024
  const projectSnapshots = await snapshotProjectFiles(request.projectDirectory, initialPaths, workspaceSnapshot.manifests);
8932
9025
  projectSnapshots.set(generationMetadataFile, generationSnapshot.content);
8933
9026
  const projectModes = await snapshotProjectModes(request.projectDirectory, initialPaths);
@@ -8941,8 +9034,7 @@ async function createUpdatePlan(request, artifacts) {
8941
9034
  });
8942
9035
  const targetManifest = await carryReconciledBaselines(sourceManifest, migrations.targetManifest, projectSnapshots, artifactSet.targetDirectory);
8943
9036
  targetProvenance.appliedRecipes = [
8944
- ...provenance.appliedRecipes,
8945
- ...recipes2.map(({ id }) => id)
9037
+ ...new Set([...provenance.appliedRecipes, ...recipes2])
8946
9038
  ];
8947
9039
  const sourceEntries = new Map(sourceManifest.files.map((item) => [item.path, item]));
8948
9040
  const targetEntries = new Map(targetManifest.files.map((item) => [item.path, item]));
@@ -8960,7 +9052,8 @@ async function createUpdatePlan(request, artifacts) {
8960
9052
  targetEntries,
8961
9053
  migrations,
8962
9054
  artifactSet,
8963
- provenance
9055
+ provenance,
9056
+ manageGeneratedProjectOwned: request.targetModules !== undefined
8964
9057
  });
8965
9058
  deriveWebDockerfilePlan(files, projectSnapshots, new Set([...paths, ...workspaceSnapshot.manifests.keys()]));
8966
9059
  for (const file of files) {
@@ -9007,18 +9100,33 @@ async function createUpdatePlan(request, artifacts) {
9007
9100
  projectDirectory: request.projectDirectory,
9008
9101
  sourceVersion: provenance.windyVersion,
9009
9102
  targetVersion,
9010
- recipes: recipes2.map(({ id }) => id),
9103
+ recipes: recipes2,
9011
9104
  files,
9012
9105
  dependencyExpectations,
9013
9106
  workspaceMembershipExpectations: workspaceSnapshot.membershipExpectations,
9107
+ finalWorkspaceMembershipExpectations: targetWorkspaceSnapshot.membershipExpectations,
9014
9108
  conflicts,
9015
9109
  detachedToolchain,
9016
9110
  targetProvenance,
9017
9111
  sourceManifest,
9018
9112
  targetManifest,
9019
- temporaryRoot: artifactSet.temporaryRoot
9113
+ temporaryRoot: artifactSet.temporaryRoot,
9114
+ lifecycle: request.lifecycle
9020
9115
  };
9021
9116
  }
9117
+ function resolveModuleRecipeIds(request) {
9118
+ const lifecycle = request.lifecycle;
9119
+ if (!lifecycle)
9120
+ return [];
9121
+ const catalog = new Map(moduleDistributionCatalog.map((manifest) => [manifest.name, manifest]));
9122
+ if (lifecycle.action === "add") {
9123
+ return lifecycle.addedModules.map((name) => catalog.get(name).installRecipe);
9124
+ }
9125
+ if (lifecycle.action === "remove") {
9126
+ return lifecycle.removedModules.map((name) => catalog.get(name).uninstallRecipe);
9127
+ }
9128
+ return lifecycle.updatedModules.flatMap((name) => catalog.get(name).updateRecipes);
9129
+ }
9022
9130
  async function planFiles(input) {
9023
9131
  const files = [];
9024
9132
  for (const path2 of [...input.paths].sort()) {
@@ -9032,7 +9140,7 @@ async function planFiles(input) {
9032
9140
  } else if (forcedContent) {
9033
9141
  files.push(readyPlannedFile(path2, input.targetEntries.get(path2)?.ownership ?? input.sourceEntries.get(path2)?.ownership ?? classifyOwnership(path2), "update", "\u5408\u5E76\u5E73\u53F0\u4E0E\u4E0B\u6E38 migration \u5386\u53F2", expected, forcedContent));
9034
9142
  } else {
9035
- files.push(await planFile(path2, input.artifactSet.baseDirectory, input.artifactSet.targetDirectory, input.sourceEntries.get(path2), input.targetEntries.get(path2), ours, input.provenance, input.migrations.contentOverrides.get(path2)));
9143
+ files.push(await planFile(path2, input.artifactSet.baseDirectory, input.artifactSet.targetDirectory, input.sourceEntries.get(path2), input.targetEntries.get(path2), ours, input.provenance, input.migrations.contentOverrides.get(path2), input.manageGeneratedProjectOwned));
9036
9144
  }
9037
9145
  }
9038
9146
  return files;
@@ -9749,23 +9857,113 @@ async function diagnoseProject(projectDirectory, repair = false) {
9749
9857
  }
9750
9858
 
9751
9859
  // src/update/updater.ts
9752
- import { readFile as readFile23, rm as rm9 } from "fs/promises";
9753
- import { join as join30 } from "path";
9860
+ import { readFile as readFile24, rm as rm9 } from "fs/promises";
9861
+ import { join as join31 } from "path";
9754
9862
 
9755
9863
  // src/update/artifacts.ts
9756
- import { mkdtemp as mkdtemp3, readFile as readFile21 } from "fs/promises";
9864
+ import { mkdtemp as mkdtemp3, readFile as readFile22 } from "fs/promises";
9757
9865
  import { tmpdir as tmpdir3 } from "os";
9758
- import { basename as basename8, join as join28 } from "path";
9866
+ import { basename as basename8, join as join29 } from "path";
9867
+
9868
+ // src/module-history.ts
9869
+ import {
9870
+ lstat as lstat5,
9871
+ mkdir as mkdir7,
9872
+ readdir as readdir8,
9873
+ readFile as readFile21,
9874
+ writeFile as writeFile11
9875
+ } from "fs/promises";
9876
+ import { dirname as dirname7, join as join28, relative as relative4, sep as sep4 } from "path";
9877
+ async function archiveRemovedModuleMigrations(input) {
9878
+ await assertExistingHistoryIntegrity(input.projectDirectory);
9879
+ let changed = await copyMigrationTree(input.projectDirectory, ".windy/module-history", join28(input.targetDirectory, ".windy/module-history"));
9880
+ const target = new Set(input.targetModules);
9881
+ const removed = moduleDistributionCatalog.filter(({ name }) => input.sourceModules.includes(name) && !target.has(name));
9882
+ for (const manifest of removed) {
9883
+ for (const root of manifest.migrationRoots) {
9884
+ changed = await copyMigrationTree(input.projectDirectory, root, join28(input.targetDirectory, ".windy/module-history", manifest.name, root)) || changed;
9885
+ }
9886
+ }
9887
+ if (changed) {
9888
+ await writeManagedFilesManifest(input.targetDirectory, input.generatorVersion);
9889
+ }
9890
+ }
9891
+ async function assertExistingHistoryIntegrity(projectDirectory) {
9892
+ const historyRoot = join28(projectDirectory, ".windy/module-history");
9893
+ const metadata = await readOptionalMetadata(historyRoot);
9894
+ if (!metadata)
9895
+ return;
9896
+ if (metadata.isSymbolicLink() || !metadata.isDirectory()) {
9897
+ throw new Error("\u6A21\u5757 migration \u5386\u53F2\u76EE\u5F55\u65E0\u6548");
9898
+ }
9899
+ const manifest = await readManagedFilesManifest(projectDirectory);
9900
+ const expected = new Map(manifest.files.map((entry) => [entry.path, entry]));
9901
+ for (const path2 of await collectRegularFiles(historyRoot)) {
9902
+ const relativePath2 = relative4(projectDirectory, path2).split(sep4).join("/");
9903
+ const entry = expected.get(relativePath2);
9904
+ if (!entry || isMissingManagedFile(entry)) {
9905
+ throw new Error(`\u6A21\u5757 migration \u5386\u53F2\u672A\u767B\u8BB0\u5230\u53D7\u7BA1\u6E05\u5355\uFF1A${relativePath2}`);
9906
+ }
9907
+ if (hashContent(await readFile21(path2)) !== entry.hash) {
9908
+ throw new Error(`\u6A21\u5757 migration \u5386\u53F2\u5DF2\u88AB\u4FEE\u6539\uFF1A${relativePath2}`);
9909
+ }
9910
+ }
9911
+ }
9912
+ async function copyMigrationTree(projectRoot, relativeRoot, targetRoot) {
9913
+ const sourceRoot = join28(projectRoot, relativeRoot);
9914
+ const rootMetadata = await readOptionalMetadata(sourceRoot);
9915
+ if (!rootMetadata)
9916
+ return false;
9917
+ if (rootMetadata.isSymbolicLink() || !rootMetadata.isDirectory()) {
9918
+ throw new Error(`\u6A21\u5757 migration \u6839\u76EE\u5F55\u65E0\u6548\uFF1A${relativeRoot}`);
9919
+ }
9920
+ for (const source of await collectRegularFiles(sourceRoot)) {
9921
+ const target = join28(targetRoot, relative4(sourceRoot, source).split(sep4).join("/"));
9922
+ await mkdir7(dirname7(target), { recursive: true });
9923
+ await writeFile11(target, await readFile21(source));
9924
+ }
9925
+ return true;
9926
+ }
9927
+ async function collectRegularFiles(root) {
9928
+ const files = [];
9929
+ await visit(root);
9930
+ return files.sort();
9931
+ async function visit(directory) {
9932
+ for (const name of (await readdir8(directory)).sort()) {
9933
+ const path2 = join28(directory, name);
9934
+ const metadata = await lstat5(path2);
9935
+ if (metadata.isSymbolicLink()) {
9936
+ throw new Error(`\u6A21\u5757 migration \u4E0D\u5141\u8BB8\u7B26\u53F7\u94FE\u63A5\uFF1A${path2}`);
9937
+ }
9938
+ if (metadata.isDirectory())
9939
+ await visit(path2);
9940
+ else if (metadata.isFile())
9941
+ files.push(path2);
9942
+ }
9943
+ }
9944
+ }
9945
+ async function readOptionalMetadata(path2) {
9946
+ try {
9947
+ return await lstat5(path2);
9948
+ } catch (error) {
9949
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
9950
+ return;
9951
+ }
9952
+ throw error;
9953
+ }
9954
+ }
9955
+
9956
+ // src/update/artifacts.ts
9759
9957
  class PublishedArtifactProvider {
9760
9958
  #fetch;
9761
9959
  constructor(fetcher = globalThis.fetch) {
9762
9960
  this.#fetch = fetcher;
9763
9961
  }
9764
- async prepare(projectDirectory, provenance) {
9765
- const temporaryRoot = await mkdtemp3(join28(tmpdir3(), "windy-update-"));
9962
+ async prepare(projectDirectory, provenance, targetModules) {
9963
+ const temporaryRoot = await mkdtemp3(join29(tmpdir3(), "windy-update-"));
9766
9964
  const projectName = await readProjectName3(projectDirectory);
9767
- const baseDirectory = join28(temporaryRoot, "base", projectName);
9768
- const targetDirectory = join28(temporaryRoot, "target", projectName);
9965
+ const baseDirectory = join29(temporaryRoot, "base", projectName);
9966
+ const targetDirectory = join29(temporaryRoot, "target", projectName);
9769
9967
  const baseArtifact = await downloadPublicArtifact(temporaryRoot, "base", provenance.generatorVersion, undefined, this.#fetch);
9770
9968
  if (baseArtifact.identity.templateCommit !== provenance.templateCommit) {
9771
9969
  throw new Error("Project Lock \u4E0E\u6765\u6E90 registry artifact \u7684 template commit \u4E0D\u4E00\u81F4");
@@ -9775,18 +9973,25 @@ class PublishedArtifactProvider {
9775
9973
  sourceRoot: await resolveTemplateRoot(),
9776
9974
  targetDirectory,
9777
9975
  projectName,
9778
- selectedModules: provenance.modules.map(({ name }) => name),
9779
- includeExample: provenance.choices.includeExample,
9976
+ selectedModules: targetModules ?? provenance.modules.map(({ name }) => name),
9977
+ includeExample: (targetModules ?? provenance.modules.map(({ name }) => name)).includes("work-order"),
9780
9978
  includeOxc: provenance.choices.includeOxc,
9781
9979
  projectLicense: provenance.legalLicense,
9782
9980
  licenseHolder: provenance.legalLicense === "MIT" ? "Windy project owner" : undefined,
9783
9981
  profile: provenance.profile
9784
9982
  });
9983
+ await archiveRemovedModuleMigrations({
9984
+ projectDirectory,
9985
+ targetDirectory,
9986
+ sourceModules: provenance.modules.map(({ name }) => name),
9987
+ targetModules: targetModules ?? provenance.modules.map(({ name }) => name),
9988
+ generatorVersion: (await readGenerationProvenance(targetDirectory)).generatorVersion
9989
+ });
9785
9990
  return { temporaryRoot, baseDirectory, targetDirectory };
9786
9991
  }
9787
9992
  }
9788
9993
  async function readProjectName3(projectDirectory) {
9789
- const source = JSON.parse(await readFile21(join28(projectDirectory, "package.json"), "utf8"));
9994
+ const source = JSON.parse(await readFile22(join29(projectDirectory, "package.json"), "utf8"));
9790
9995
  return source.name || basename8(projectDirectory);
9791
9996
  }
9792
9997
 
@@ -9853,9 +10058,9 @@ function frozenManifestHash(file, finalizedHashes) {
9853
10058
  }
9854
10059
 
9855
10060
  // src/update/format-stabilizer.ts
9856
- import { mkdir as mkdir7, mkdtemp as mkdtemp4, readFile as readFile22, rm as rm8, writeFile as writeFile11 } from "fs/promises";
10061
+ import { mkdir as mkdir8, mkdtemp as mkdtemp4, readFile as readFile23, rm as rm8, writeFile as writeFile12 } from "fs/promises";
9857
10062
  import { tmpdir as tmpdir4 } from "os";
9858
- import { dirname as dirname7, extname as extname4, join as join29, resolve as resolve6, sep as sep4 } from "path";
10063
+ import { dirname as dirname8, extname as extname4, join as join30, resolve as resolve6, sep as sep5 } from "path";
9859
10064
  var formattedExtensions = new Set([
9860
10065
  ".cjs",
9861
10066
  ".css",
@@ -9894,7 +10099,7 @@ async function stabilizeManagedContent(result2, execute, writeProjectFile) {
9894
10099
  }
9895
10100
  }
9896
10101
  inputs.set(".windy/generation.json", new TextEncoder().encode(generationContent));
9897
- const temporaryRoot = await mkdtemp4(join29(tmpdir4(), "windy-update-format-"));
10102
+ const temporaryRoot = await mkdtemp4(join30(tmpdir4(), "windy-update-format-"));
9898
10103
  try {
9899
10104
  const stagedPaths = await stageInputs(temporaryRoot, inputs);
9900
10105
  for (let offset = 0;offset < stagedPaths.length; offset += 80) {
@@ -9902,14 +10107,14 @@ async function stabilizeManagedContent(result2, execute, writeProjectFile) {
9902
10107
  "x",
9903
10108
  "oxfmt",
9904
10109
  "--write",
9905
- `--config=${join29(result2.plan.projectDirectory, ".oxfmtrc.json")}`,
10110
+ `--config=${join30(result2.plan.projectDirectory, ".oxfmtrc.json")}`,
9906
10111
  ...stagedPaths.slice(offset, offset + 80)
9907
10112
  ], result2.plan.projectDirectory);
9908
10113
  }
9909
10114
  const hashes = plannedContentHashes(result2);
9910
10115
  let formattedGeneration = generationContent;
9911
10116
  for (const [path2, original] of inputs) {
9912
- const formatted = await readFile22(safeTemporaryPath(temporaryRoot, path2));
10117
+ const formatted = await readFile23(safeTemporaryPath(temporaryRoot, path2));
9913
10118
  if (path2 === ".windy/generation.json") {
9914
10119
  formattedGeneration = formatted.toString();
9915
10120
  continue;
@@ -9935,15 +10140,15 @@ async function stageInputs(temporaryRoot, inputs) {
9935
10140
  const paths = [];
9936
10141
  for (const [path2, content] of inputs) {
9937
10142
  const target = safeTemporaryPath(temporaryRoot, path2);
9938
- await mkdir7(dirname7(target), { recursive: true });
9939
- await writeFile11(target, content);
10143
+ await mkdir8(dirname8(target), { recursive: true });
10144
+ await writeFile12(target, content);
9940
10145
  paths.push(target);
9941
10146
  }
9942
10147
  return paths;
9943
10148
  }
9944
10149
  function safeTemporaryPath(root, path2) {
9945
10150
  const target = resolve6(root, path2);
9946
- if (!target.startsWith(`${resolve6(root)}${sep4}`)) {
10151
+ if (!target.startsWith(`${resolve6(root)}${sep5}`)) {
9947
10152
  throw new Error(`\u683C\u5F0F\u5316\u8DEF\u5F84\u8D8A\u51FA\u4E34\u65F6\u76EE\u5F55\uFF1A${path2}`);
9948
10153
  }
9949
10154
  return target;
@@ -10006,7 +10211,7 @@ class DefaultStarterUpdater {
10006
10211
  const plannedPaths = new Set(result2.plan.files.map(({ path: path2 }) => path2));
10007
10212
  replaceExpectations(result2, verificationSnapshot.filter(({ path: path2 }) => !metadataPaths2.has(path2) && !plannedPaths.has(path2)));
10008
10213
  verificationExpectationsFrozen = true;
10009
- const packageJson = JSON.parse(await readFile23(join30(result2.plan.projectDirectory, "package.json"), "utf8"));
10214
+ const packageJson = JSON.parse(await readFile24(join31(result2.plan.projectDirectory, "package.json"), "utf8"));
10010
10215
  for (const name of ["lint", "typecheck", "test", "build"]) {
10011
10216
  if (!packageJson.scripts?.[name]) {
10012
10217
  steps.push({ name: `bun run ${name}`, status: "skipped" });
@@ -10025,7 +10230,7 @@ class DefaultStarterUpdater {
10025
10230
  projectDirectory: result2.plan.projectDirectory,
10026
10231
  ownerToken: result2.transactionOwnerToken,
10027
10232
  expectations: result2.finalizationExpectations,
10028
- workspaceMembershipExpectations: result2.plan.workspaceMembershipExpectations
10233
+ workspaceMembershipExpectations: result2.plan.finalWorkspaceMembershipExpectations ?? result2.plan.workspaceMembershipExpectations
10029
10234
  });
10030
10235
  return { updateId: result2.plan.id, status: "passed", steps, reportPath };
10031
10236
  } catch (error) {
@@ -10089,6 +10294,12 @@ async function writeReport(result2, steps, relativePath2) {
10089
10294
  `- \u6765\u6E90\u7248\u672C\uFF1A${result2.plan.sourceVersion}`,
10090
10295
  `- \u76EE\u6807\u7248\u672C\uFF1A${result2.plan.targetVersion}`,
10091
10296
  `- Recipe\uFF1A${result2.plan.recipes.join("\u3001") || "\u65E0"}`,
10297
+ ...result2.plan.lifecycle ? [
10298
+ `- \u6A21\u5757\u52A8\u4F5C\uFF1A${result2.plan.lifecycle.action}`,
10299
+ `- \u8BF7\u6C42\u6A21\u5757\uFF1A${result2.plan.lifecycle.requestedModules.join("\u3001")}`,
10300
+ `- \u6570\u636E\u5904\u7F6E\uFF1A${result2.plan.lifecycle.dataDisposition ?? "\u4E0D\u9002\u7528"}`,
10301
+ `- \u6570\u636E\u5904\u7F6E\u6587\u6863\uFF1A${result2.plan.lifecycle.dataPlan ?? "\u65E0"}`
10302
+ ] : [],
10092
10303
  `- \u4FEE\u6539\u6587\u4EF6\uFF1A${result2.changedFiles.length}`,
10093
10304
  `- Oxc \u4EE3\u7BA1\uFF1A${result2.plan.detachedToolchain ? "\u5DF2\u68C0\u6D4B\u5230\u5BA2\u6237\u63A5\u7BA1\u5E76\u4FDD\u7559" : "\u4FDD\u6301"}`,
10094
10305
  "",
@@ -10112,13 +10323,275 @@ function expectedForPath(result2, path2) {
10112
10323
  return expectation2.expected;
10113
10324
  }
10114
10325
 
10326
+ // src/module-lifecycle.ts
10327
+ import { access as access2, rm as rm10 } from "fs/promises";
10328
+ import { resolve as resolve7 } from "path";
10329
+
10330
+ // src/module-version-range.ts
10331
+ function isVersionInCaretRange(version, range) {
10332
+ const current = parseVersion2(version);
10333
+ const base = range.startsWith("^") ? parseVersion2(range.slice(1)) : undefined;
10334
+ if (!current || !base || compareVersion(current, base) < 0)
10335
+ return false;
10336
+ const upper = base[0] > 0 ? [base[0] + 1, 0, 0] : base[1] > 0 ? [0, base[1] + 1, 0] : [0, 0, base[2] + 1];
10337
+ return compareVersion(current, upper) < 0;
10338
+ }
10339
+ function parseVersion2(value) {
10340
+ const match = value.match(/^(\d+)\.(\d+)\.(\d+)$/);
10341
+ if (!match)
10342
+ return;
10343
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
10344
+ }
10345
+ function compareVersion(left, right) {
10346
+ for (let index = 0;index < 3; index += 1) {
10347
+ const difference = left[index] - right[index];
10348
+ if (difference)
10349
+ return difference;
10350
+ }
10351
+ return 0;
10352
+ }
10353
+
10354
+ // src/module-lifecycle.ts
10355
+ async function runModuleLifecycleCommand(args, projectDirectory, dependencies = {}) {
10356
+ const action = args[0];
10357
+ if (!isAction(action)) {
10358
+ throw new Error("module \u53EA\u652F\u6301 list\u3001add\u3001update\u3001remove");
10359
+ }
10360
+ const log = dependencies.log ?? console.log;
10361
+ const provenance = await readGenerationProvenance(projectDirectory);
10362
+ const installed = provenance.modules.map(({ name }) => name);
10363
+ if (action === "list") {
10364
+ printModuleList(provenance.modules, log);
10365
+ return;
10366
+ }
10367
+ const cliVersion = dependencies.cliVersion ?? (await readTemplateMetadata(await resolveTemplateRoot())).generatorVersion;
10368
+ if (provenance.generatorVersion !== cliVersion || provenance.windyVersion !== cliVersion) {
10369
+ throw new Error(`module \u547D\u4EE4\u8981\u6C42 CLI \u4E0E\u9879\u76EE Windy \u7248\u672C\u4E00\u81F4\uFF1A\u9879\u76EE ${provenance.windyVersion}\uFF0CCLI ${cliVersion}\uFF1B\u8BF7\u5148\u8FD0\u884C bun run windy update`);
10370
+ }
10371
+ const requested = positionalModules(args.slice(1));
10372
+ const lifecycle = await resolveLifecyclePlan({
10373
+ action,
10374
+ requested,
10375
+ installed,
10376
+ windyVersion: provenance.windyVersion,
10377
+ args,
10378
+ projectDirectory
10379
+ });
10380
+ printDataPlan(lifecycle, log);
10381
+ if (!hasChanges(lifecycle)) {
10382
+ log("\u6A21\u5757\u96C6\u5408\u5DF2\u6EE1\u8DB3\u8BF7\u6C42\uFF0C\u65E0\u9700\u4FEE\u6539");
10383
+ return;
10384
+ }
10385
+ const targetModules = resolveTargetModules(installed, lifecycle);
10386
+ const updater = dependencies.updater ?? new DefaultStarterUpdater;
10387
+ const plan = await updater.plan({
10388
+ projectDirectory,
10389
+ targetVersion: provenance.windyVersion,
10390
+ targetModules,
10391
+ lifecycle
10392
+ });
10393
+ printModulePlan(plan, log);
10394
+ if (args.includes("--dry-run")) {
10395
+ await rm10(plan.temporaryRoot, { recursive: true, force: true });
10396
+ log(`
10397
+ \u6F14\u7EC3\u5B8C\u6210\uFF1A\u672A\u5199\u5165\u9879\u76EE\u6587\u4EF6`);
10398
+ return;
10399
+ }
10400
+ if (action === "remove" && !args.includes("--confirm-data-disposition")) {
10401
+ await rm10(plan.temporaryRoot, { recursive: true, force: true });
10402
+ throw new Error("\u5378\u8F7D\u524D\u5FC5\u987B\u5BA1\u9605\u6570\u636E\u5904\u7F6E\u8BA1\u5212\u5E76\u4F20\u5165 --confirm-data-disposition");
10403
+ }
10404
+ if (plan.conflicts.length) {
10405
+ await rm10(plan.temporaryRoot, { recursive: true, force: true });
10406
+ throw new Error("\u5B58\u5728\u6A21\u5757\u751F\u547D\u5468\u671F\u51B2\u7A81\uFF0C\u9879\u76EE\u672A\u88AB\u4FEE\u6539");
10407
+ }
10408
+ const result2 = await updater.apply(plan);
10409
+ const report = await updater.verify(result2);
10410
+ log(`
10411
+ \u6A21\u5757${actionLabel(action)}\u5B8C\u6210\uFF1A${requested.join("\u3001")}`);
10412
+ log(`\u62A5\u544A\uFF1A${report.reportPath}`);
10413
+ }
10414
+ async function resolveLifecyclePlan(input) {
10415
+ const known = new Map(moduleDistributionCatalog.map((manifest) => [manifest.name, manifest]));
10416
+ const requested = input.action === "update" && input.requested.length === 0 ? [...input.installed] : input.requested;
10417
+ if (!requested.length) {
10418
+ throw new Error(`module ${input.action} \u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u6A21\u5757 key`);
10419
+ }
10420
+ for (const name of requested) {
10421
+ const manifest = known.get(name);
10422
+ if (!manifest)
10423
+ throw new Error(`\u672A\u77E5\u6A21\u5757\uFF1A${name}`);
10424
+ assertWindyCompatible(input.windyVersion, manifest.windyRange, name);
10425
+ }
10426
+ if (input.action === "add") {
10427
+ const target = resolveModuleSelection({
10428
+ requested: [...input.installed, ...requested],
10429
+ includeRecommended: false
10430
+ });
10431
+ for (const name of target) {
10432
+ const manifest = known.get(name);
10433
+ assertWindyCompatible(input.windyVersion, manifest.windyRange, manifest.name);
10434
+ }
10435
+ return {
10436
+ action: input.action,
10437
+ requestedModules: requested,
10438
+ addedModules: target.filter((name) => !input.installed.includes(name)),
10439
+ updatedModules: [],
10440
+ removedModules: []
10441
+ };
10442
+ }
10443
+ if (input.action === "update") {
10444
+ const absent = requested.filter((name) => !input.installed.includes(name));
10445
+ if (absent.length) {
10446
+ throw new Error(`\u672A\u5B89\u88C5\u6A21\u5757\u4E0D\u80FD\u66F4\u65B0\uFF1A${absent.join("\u3001")}`);
10447
+ }
10448
+ return {
10449
+ action: input.action,
10450
+ requestedModules: requested,
10451
+ addedModules: [],
10452
+ updatedModules: requested,
10453
+ removedModules: []
10454
+ };
10455
+ }
10456
+ const required = requested.filter((name) => requiredModuleNames.includes(name));
10457
+ if (required.length) {
10458
+ throw new Error(`\u6838\u5FC3\u6A21\u5757\u4E0D\u53EF\u5378\u8F7D\uFF1A${required.join("\u3001")}`);
10459
+ }
10460
+ const removed = requested.filter((name) => input.installed.includes(name));
10461
+ assertRemovalDependencies(input.installed, removed);
10462
+ const dataDisposition = resolveDataDisposition(removed.map((name) => known.get(name).uninstallPolicy), readValue2(input.args, "--data-disposition"));
10463
+ const dataPlan = readValue2(input.args, "--data-plan");
10464
+ if (dataDisposition !== "retain-data") {
10465
+ if (!dataPlan) {
10466
+ throw new Error(`${dataDisposition} \u5378\u8F7D\u5FC5\u987B\u901A\u8FC7 --data-plan \u63D0\u4F9B\u5DF2\u5BA1\u9605\u7684\u5904\u7F6E\u6587\u6863`);
10467
+ }
10468
+ await access2(resolve7(input.projectDirectory, dataPlan));
10469
+ }
10470
+ return {
10471
+ action: input.action,
10472
+ requestedModules: requested,
10473
+ addedModules: [],
10474
+ updatedModules: [],
10475
+ removedModules: removed,
10476
+ dataDisposition,
10477
+ dataPlan
10478
+ };
10479
+ }
10480
+ function resolveTargetModules(installed, lifecycle) {
10481
+ if (lifecycle.action === "add") {
10482
+ return resolveModuleSelection({
10483
+ requested: [...installed, ...lifecycle.addedModules],
10484
+ includeRecommended: false
10485
+ });
10486
+ }
10487
+ if (lifecycle.action === "remove") {
10488
+ const removed = new Set(lifecycle.removedModules);
10489
+ return installed.filter((name) => !removed.has(name));
10490
+ }
10491
+ return installed;
10492
+ }
10493
+ function resolveDataDisposition(policies, requested) {
10494
+ const disposition = requested ?? strongestPolicy(policies);
10495
+ if (disposition !== "retain-data" && disposition !== "archive-data" && disposition !== "manual") {
10496
+ throw new Error(`\u4E0D\u652F\u6301\u7684\u6570\u636E\u5904\u7F6E\u7B56\u7565\uFF1A${disposition}`);
10497
+ }
10498
+ const minimum = strongestPolicy(policies);
10499
+ const rank = {
10500
+ "retain-data": 0,
10501
+ "archive-data": 1,
10502
+ manual: 2
10503
+ };
10504
+ if (rank[disposition] < rank[minimum]) {
10505
+ throw new Error(`\u6A21\u5757\u76EE\u5F55\u8981\u6C42 ${minimum}\uFF0C\u4E0D\u80FD\u964D\u7EA7\u4E3A ${disposition}`);
10506
+ }
10507
+ return disposition;
10508
+ }
10509
+ function strongestPolicy(policies) {
10510
+ if (policies.includes("manual"))
10511
+ return "manual";
10512
+ if (policies.includes("archive-data"))
10513
+ return "archive-data";
10514
+ return "retain-data";
10515
+ }
10516
+ function assertRemovalDependencies(installed, removed) {
10517
+ const removedSet = new Set(removed);
10518
+ const blockers = moduleDistributionCatalog.filter(({ name, dependencies }) => installed.includes(name) && !removedSet.has(name) && dependencies.some((dependency) => removedSet.has(dependency))).map(({ name }) => name);
10519
+ if (blockers.length) {
10520
+ throw new Error(`\u4ECD\u6709\u5DF2\u5B89\u88C5\u6A21\u5757\u4F9D\u8D56\u5F85\u5378\u8F7D\u6A21\u5757\uFF1A${blockers.join("\u3001")}`);
10521
+ }
10522
+ }
10523
+ function assertWindyCompatible(version, range, moduleName) {
10524
+ if (!isVersionInCaretRange(version, range)) {
10525
+ throw new Error(`${moduleName} \u8981\u6C42 Windy ${range}\uFF0C\u5F53\u524D\u4E3A ${version}`);
10526
+ }
10527
+ }
10528
+ function positionalModules(args) {
10529
+ const valueFlags = new Set(["--data-disposition", "--data-plan", "--cwd"]);
10530
+ const output = [];
10531
+ for (let index = 0;index < args.length; index += 1) {
10532
+ const value = args[index];
10533
+ if (valueFlags.has(value)) {
10534
+ index += 1;
10535
+ } else if (!value.startsWith("-")) {
10536
+ output.push(value);
10537
+ }
10538
+ }
10539
+ return [...new Set(output)];
10540
+ }
10541
+ function printModuleList(installed, log) {
10542
+ const installedVersions = new Map(installed.map(({ name, version }) => [name, version]));
10543
+ log("Windy \u6A21\u5757\uFF1A");
10544
+ for (const manifest of moduleDistributionCatalog) {
10545
+ const installedVersion = installedVersions.get(manifest.name);
10546
+ const status = installedVersion ? "installed" : "available";
10547
+ const version = installedVersion ? `${manifest.name}@${installedVersion}\uFF08catalog ${manifest.version}\uFF09` : `${manifest.name}@${manifest.version}`;
10548
+ log(` ${status.padEnd(9)} ${version}`);
10549
+ }
10550
+ }
10551
+ function printDataPlan(lifecycle, log) {
10552
+ if (lifecycle.action !== "remove")
10553
+ return;
10554
+ log("\u6A21\u5757\u5378\u8F7D\u6570\u636E\u5904\u7F6E\u8BA1\u5212\uFF1A");
10555
+ log(` \u6A21\u5757\uFF1A${lifecycle.removedModules.join("\u3001") || "\u5747\u672A\u5B89\u88C5"}`);
10556
+ log(` \u6570\u636E\uFF1A${lifecycle.dataDisposition ?? "retain-data"}`);
10557
+ log(" migration\uFF1A\u4FDD\u7559\u5230 .windy/module-history\uFF0C\u4E0D\u6267\u884C down migration");
10558
+ if (lifecycle.dataPlan)
10559
+ log(` \u8FD0\u7EF4\u5904\u7F6E\u6587\u6863\uFF1A${lifecycle.dataPlan}`);
10560
+ }
10561
+ function printModulePlan(plan, log) {
10562
+ const changed = plan.files.filter(({ action }) => action !== "preserve");
10563
+ log(`\u6A21\u5757\u6587\u4EF6\u8BA1\u5212\uFF1A${changed.length} \u4E2A\u53D8\u66F4\uFF1B${plan.conflicts.length} \u4E2A\u51B2\u7A81`);
10564
+ for (const file of changed) {
10565
+ log(` ${file.action.padEnd(6)} ${file.path}\uFF08${file.reason}\uFF09`);
10566
+ }
10567
+ for (const conflict3 of plan.conflicts)
10568
+ log(` conflict ${conflict3}`);
10569
+ }
10570
+ function hasChanges(lifecycle) {
10571
+ return lifecycle.addedModules.length > 0 || lifecycle.updatedModules.length > 0 || lifecycle.removedModules.length > 0;
10572
+ }
10573
+ function readValue2(args, flag) {
10574
+ const index = args.indexOf(flag);
10575
+ return index >= 0 ? args[index + 1] : undefined;
10576
+ }
10577
+ function isAction(value) {
10578
+ return value === "list" || value === "add" || value === "update" || value === "remove";
10579
+ }
10580
+ function actionLabel(action) {
10581
+ return action === "add" ? "\u5B89\u88C5" : action === "update" ? "\u66F4\u65B0" : "\u5378\u8F7D";
10582
+ }
10583
+
10115
10584
  // src/update/cli.ts
10116
10585
  async function runLifecycleCommand(args) {
10117
10586
  const command = args[0];
10118
- if (command !== "doctor" && command !== "update" && command !== "reconcile" && command !== "sync-docker") {
10587
+ if (command !== "doctor" && command !== "update" && command !== "module" && command !== "reconcile" && command !== "sync-docker") {
10119
10588
  return false;
10120
10589
  }
10121
- const projectDirectory = resolve7(readValue2(args, "--cwd") || process.cwd());
10590
+ const projectDirectory = resolve8(readValue3(args, "--cwd") || process.cwd());
10591
+ if (command === "module") {
10592
+ await runModuleLifecycleCommand(args.slice(1), projectDirectory);
10593
+ return true;
10594
+ }
10122
10595
  if (command === "sync-docker") {
10123
10596
  const result2 = await planWebDockerfileInProject(projectDirectory, {
10124
10597
  requireAnchors: true
@@ -10175,7 +10648,7 @@ async function runReconcile(args, projectDirectory) {
10175
10648
  });
10176
10649
  const plan = await reconciler.plan({
10177
10650
  projectDirectory,
10178
- targetVersion: readValue2(args, "--to") ?? ""
10651
+ targetVersion: readValue3(args, "--to") ?? ""
10179
10652
  });
10180
10653
  console.log(`Windy \u5BF9\u8D26\u8BA1\u5212\uFF1A${plan.sourceVersion} \u2192 ${plan.targetVersion}\uFF1B\u53EF\u8BC1\u660E ${plan.reconciled.length}\uFF1B\u4FDD\u5B88\u672A\u51B3 ${plan.unresolved.length}`);
10181
10654
  for (const item of plan.reconciled) {
@@ -10185,7 +10658,7 @@ async function runReconcile(args, projectDirectory) {
10185
10658
  console.log(` unresolved ${item.path}\uFF08${item.reason}\uFF09`);
10186
10659
  }
10187
10660
  if (!args.includes("--apply")) {
10188
- await rm10(plan.temporaryRoot, { recursive: true, force: true });
10661
+ await rm11(plan.temporaryRoot, { recursive: true, force: true });
10189
10662
  console.log(`
10190
10663
  \u6F14\u7EC3\u5B8C\u6210\uFF1A\u672A\u5199\u5165\u9879\u76EE\u6587\u4EF6\uFF1B\u4F7F\u7528 --apply \u4EC5\u63D0\u4EA4\u5BF9\u8D26\u5143\u6570\u636E`);
10191
10664
  return;
@@ -10196,12 +10669,12 @@ async function runReconcile(args, projectDirectory) {
10196
10669
  \u5BF9\u8D26\u5B8C\u6210\uFF1AProject Lock \u4ECD\u4E3A ${plan.sourceVersion}`);
10197
10670
  console.log(`\u62A5\u544A\uFF1A${result2.reportPath}`);
10198
10671
  } finally {
10199
- await rm10(plan.temporaryRoot, { recursive: true, force: true });
10672
+ await rm11(plan.temporaryRoot, { recursive: true, force: true });
10200
10673
  }
10201
10674
  }
10202
10675
  async function runUpdate(args, projectDirectory) {
10203
10676
  const template = await readTemplateMetadata(await resolveTemplateRoot());
10204
- const requested = readValue2(args, "--to");
10677
+ const requested = readValue3(args, "--to");
10205
10678
  const registryVersion = requested ? undefined : await readRegistryLatest();
10206
10679
  if (registryVersion && compareVersions2(registryVersion, template.generatorVersion) > 0 && !args.includes("--no-delegate")) {
10207
10680
  if (args.includes("--check")) {
@@ -10231,13 +10704,13 @@ async function runUpdate(args, projectDirectory) {
10231
10704
  const plan = await updater.plan({ projectDirectory, targetVersion: target });
10232
10705
  printPlan(plan);
10233
10706
  if (args.includes("--dry-run")) {
10234
- await rm10(plan.temporaryRoot, { recursive: true, force: true });
10707
+ await rm11(plan.temporaryRoot, { recursive: true, force: true });
10235
10708
  console.log(`
10236
10709
  \u6F14\u7EC3\u5B8C\u6210\uFF1A\u672A\u5199\u5165\u9879\u76EE\u6587\u4EF6`);
10237
10710
  return;
10238
10711
  }
10239
10712
  if (plan.conflicts.length) {
10240
- await rm10(plan.temporaryRoot, { recursive: true, force: true });
10713
+ await rm11(plan.temporaryRoot, { recursive: true, force: true });
10241
10714
  throw new Error("\u5B58\u5728\u66F4\u65B0\u51B2\u7A81\uFF1B\u8BF7\u6309\u4E0A\u65B9\u6E05\u5355\u5904\u7406\u540E\u91CD\u65B0\u8FD0\u884C\uFF0C\u9879\u76EE\u672A\u88AB\u4FEE\u6539");
10242
10715
  }
10243
10716
  const result2 = await updater.apply(plan);
@@ -10271,8 +10744,8 @@ function printPlan(plan) {
10271
10744
  console.log(`\u8BA1\u5212\u6539\u52A8\uFF1A${changed.length} \u4E2A\u6587\u4EF6\uFF1B\u51B2\u7A81\uFF1A${plan.conflicts.length}`);
10272
10745
  for (const file of changed)
10273
10746
  console.log(` ${file.action.padEnd(6)} ${file.path}\uFF08${file.reason}\uFF09`);
10274
- for (const conflict2 of plan.conflicts)
10275
- console.log(` conflict ${conflict2}`);
10747
+ for (const conflict3 of plan.conflicts)
10748
+ console.log(` conflict ${conflict3}`);
10276
10749
  if (plan.detachedToolchain)
10277
10750
  console.log("Oxc\uFF1A\u68C0\u6D4B\u5230\u5BA2\u6237\u63A5\u7BA1\uFF0C\u76F8\u5173\u914D\u7F6E\u4FDD\u6301\u4E0D\u53D8");
10278
10751
  }
@@ -10295,7 +10768,7 @@ function printDoctor(report) {
10295
10768
  console.log("\u6062\u590D\uFF1A\u5DF2\u4ECE\u4E8B\u52A1\u5907\u4EFD\u56DE\u6EDA");
10296
10769
  }
10297
10770
  }
10298
- function readValue2(args, flag) {
10771
+ function readValue3(args, flag) {
10299
10772
  const index = args.indexOf(flag);
10300
10773
  return index >= 0 ? args[index + 1] : undefined;
10301
10774
  }
@@ -10322,28 +10795,28 @@ function compareVersions2(left, right) {
10322
10795
  }
10323
10796
 
10324
10797
  // src/module-schema/cli.ts
10325
- import { resolve as resolve9 } from "path";
10798
+ import { resolve as resolve10 } from "path";
10326
10799
 
10327
10800
  // src/module-schema/handoff.ts
10328
10801
  import {
10329
- access as access2,
10802
+ access as access3,
10330
10803
  cp,
10331
- mkdir as mkdir8,
10804
+ mkdir as mkdir9,
10332
10805
  mkdtemp as mkdtemp5,
10333
- readFile as readFile25,
10334
- readdir as readdir8,
10806
+ readFile as readFile26,
10807
+ readdir as readdir9,
10335
10808
  rename,
10336
- rm as rm11,
10337
- writeFile as writeFile12
10809
+ rm as rm12,
10810
+ writeFile as writeFile13
10338
10811
  } from "fs/promises";
10339
- import { dirname as dirname8, join as join31, relative as relative5 } from "path";
10812
+ import { dirname as dirname9, join as join32, relative as relative6 } from "path";
10340
10813
 
10341
10814
  // src/module-schema/config.ts
10342
- import { readFile as readFile24 } from "fs/promises";
10343
- import { isAbsolute as isAbsolute2, relative as relative4, resolve as resolve8 } from "path";
10815
+ import { readFile as readFile25 } from "fs/promises";
10816
+ import { isAbsolute as isAbsolute2, relative as relative5, resolve as resolve9 } from "path";
10344
10817
  async function readModuleSchemaLineageConfig(projectDirectory, configPath) {
10345
10818
  const absolutePath = resolveProjectPath(projectDirectory, configPath);
10346
- const parsed = JSON.parse(await readFile24(absolutePath, "utf8"));
10819
+ const parsed = JSON.parse(await readFile25(absolutePath, "utf8"));
10347
10820
  if (!isConfig(parsed)) {
10348
10821
  throw new Error("Module schema lineage \u914D\u7F6E\u65E0\u6548");
10349
10822
  }
@@ -10357,8 +10830,8 @@ async function readModuleSchemaLineageConfig(projectDirectory, configPath) {
10357
10830
  function resolveProjectPath(projectDirectory, path2) {
10358
10831
  if (isAbsolute2(path2))
10359
10832
  throw new Error(`\u8DEF\u5F84\u5FC5\u987B\u76F8\u5BF9\u9879\u76EE\u6839\u76EE\u5F55\uFF1A${path2}`);
10360
- const resolved = resolve8(projectDirectory, path2);
10361
- const projectRelative = relative4(projectDirectory, resolved);
10833
+ const resolved = resolve9(projectDirectory, path2);
10834
+ const projectRelative = relative5(projectDirectory, resolved);
10362
10835
  if (projectRelative.startsWith("..") || isAbsolute2(projectRelative)) {
10363
10836
  throw new Error(`\u8DEF\u5F84\u8D8A\u51FA\u9879\u76EE\u6839\u76EE\u5F55\uFF1A${path2}`);
10364
10837
  }
@@ -10467,7 +10940,7 @@ async function handoffModuleSchemaLineages(options) {
10467
10940
  const stagedPlatform = await stagePlatformLineage(project, temporaryRoot, config, execute);
10468
10941
  await installStagedLineages(project, temporaryRoot, config, stagedModules, stagedPlatform);
10469
10942
  } finally {
10470
- await rm11(temporaryRoot, { recursive: true, force: true });
10943
+ await rm12(temporaryRoot, { recursive: true, force: true });
10471
10944
  }
10472
10945
  return {
10473
10946
  moduleCount: config.modules.length,
@@ -10477,17 +10950,17 @@ async function handoffModuleSchemaLineages(options) {
10477
10950
  async function stageModuleLineages(project, temporaryRoot, config, execute) {
10478
10951
  const outputs = [];
10479
10952
  for (const [index, module3] of config.modules.entries()) {
10480
- const output = join31(temporaryRoot, `module-${index}`);
10481
- await mkdir8(output, { recursive: true });
10482
- const generatedConfig = join31(temporaryRoot, `module-${index}.config.ts`);
10483
- await writeDrizzleConfig(generatedConfig, project, module3.schema, relative5(temporaryRoot, output));
10953
+ const output = join32(temporaryRoot, `module-${index}`);
10954
+ await mkdir9(output, { recursive: true });
10955
+ const generatedConfig = join32(temporaryRoot, `module-${index}.config.ts`);
10956
+ await writeDrizzleConfig(generatedConfig, project, module3.schema, relative6(temporaryRoot, output));
10484
10957
  await execute(generatedConfig, temporaryRoot);
10485
10958
  const sqlFiles = await listSqlFiles(output);
10486
10959
  if (sqlFiles.length !== 1) {
10487
10960
  throw new Error(`${module3.key} baseline \u5FC5\u987B\u6070\u597D\u751F\u6210\u4E00\u4E2A migration`);
10488
10961
  }
10489
- await writeFile12(join31(output, sqlFiles[0]), metadataOnlySql("module"));
10490
- await writeFile12(join31(output, "lineage.json"), `${JSON.stringify({
10962
+ await writeFile13(join32(output, sqlFiles[0]), metadataOnlySql("module"));
10963
+ await writeFile13(join32(output, "lineage.json"), `${JSON.stringify({
10491
10964
  schemaVersion: 1,
10492
10965
  moduleKey: module3.key,
10493
10966
  schema: module3.schema,
@@ -10501,25 +10974,25 @@ async function stageModuleLineages(project, temporaryRoot, config, execute) {
10501
10974
  }
10502
10975
  async function stagePlatformLineage(project, temporaryRoot, config, execute) {
10503
10976
  const source = resolveProjectPath(project, config.platform.out);
10504
- const output = join31(temporaryRoot, "platform");
10977
+ const output = join32(temporaryRoot, "platform");
10505
10978
  await cp(source, output, { recursive: true });
10506
10979
  const before = new Set(await listSqlFiles(output));
10507
- const generatedConfig = join31(temporaryRoot, "platform.config.ts");
10508
- await writeDrizzleConfig(generatedConfig, project, config.platform.schema, relative5(temporaryRoot, output));
10980
+ const generatedConfig = join32(temporaryRoot, "platform.config.ts");
10981
+ await writeDrizzleConfig(generatedConfig, project, config.platform.schema, relative6(temporaryRoot, output));
10509
10982
  await execute(generatedConfig, temporaryRoot);
10510
10983
  const created = (await listSqlFiles(output)).filter((path2) => !before.has(path2));
10511
10984
  if (created.length !== 1) {
10512
10985
  throw new Error("\u5E73\u53F0 lineage handoff \u5FC5\u987B\u6070\u597D\u751F\u6210\u4E00\u4E2A migration");
10513
10986
  }
10514
- const sqlPath = join31(output, created[0]);
10987
+ const sqlPath = join32(output, created[0]);
10515
10988
  const allowed = config.modules.flatMap(({ legacyPlatformTables }) => legacyPlatformTables);
10516
- validatePlatformHandoffSql(await readFile25(sqlPath, "utf8"), allowed);
10517
- await writeFile12(sqlPath, metadataOnlySql("platform"));
10989
+ validatePlatformHandoffSql(await readFile26(sqlPath, "utf8"), allowed);
10990
+ await writeFile13(sqlPath, metadataOnlySql("platform"));
10518
10991
  return output;
10519
10992
  }
10520
10993
  async function installStagedLineages(project, temporaryRoot, config, stagedModules, stagedPlatform) {
10521
10994
  const platform = resolveProjectPath(project, config.platform.out);
10522
- const backup = join31(temporaryRoot, "platform-backup");
10995
+ const backup = join32(temporaryRoot, "platform-backup");
10523
10996
  const installed = [];
10524
10997
  await rename(platform, backup);
10525
10998
  try {
@@ -10529,21 +11002,21 @@ async function installStagedLineages(project, temporaryRoot, config, stagedModul
10529
11002
  if (await exists(output)) {
10530
11003
  throw new Error(`${module3.key} lineage \u8F93\u51FA\u5DF2\u5B58\u5728\uFF0C\u7981\u6B62\u8986\u76D6`);
10531
11004
  }
10532
- await mkdir8(dirname8(output), { recursive: true });
11005
+ await mkdir9(dirname9(output), { recursive: true });
10533
11006
  await rename(stagedModules[index], output);
10534
11007
  installed.push(output);
10535
11008
  }
10536
11009
  } catch (error) {
10537
- await rm11(platform, { recursive: true, force: true });
11010
+ await rm12(platform, { recursive: true, force: true });
10538
11011
  await rename(backup, platform);
10539
11012
  for (const output of installed)
10540
- await rm11(output, { recursive: true, force: true });
11013
+ await rm12(output, { recursive: true, force: true });
10541
11014
  throw error;
10542
11015
  }
10543
11016
  }
10544
11017
  async function writeDrizzleConfig(path2, project, schemas, out) {
10545
11018
  const absoluteSchemas = schemas.map((schema) => resolveProjectPath(project, schema));
10546
- await writeFile12(path2, `import { defineConfig } from "drizzle-kit";
11019
+ await writeFile13(path2, `import { defineConfig } from "drizzle-kit";
10547
11020
  export default defineConfig(${JSON.stringify({
10548
11021
  dialect: "postgresql",
10549
11022
  schema: absoluteSchemas,
@@ -10557,7 +11030,7 @@ async function validateSchemaSources(project, config) {
10557
11030
  ...config.platform.schema,
10558
11031
  ...config.modules.flatMap(({ schema: schema2 }) => schema2)
10559
11032
  ]) {
10560
- await access2(resolveProjectPath(project, schema));
11033
+ await access3(resolveProjectPath(project, schema));
10561
11034
  }
10562
11035
  for (const module3 of config.modules) {
10563
11036
  if (await exists(resolveProjectPath(project, module3.out))) {
@@ -10566,12 +11039,12 @@ async function validateSchemaSources(project, config) {
10566
11039
  }
10567
11040
  }
10568
11041
  async function createTemporaryRoot(project) {
10569
- const windy = join31(project, ".windy");
10570
- await mkdir8(windy, { recursive: true });
10571
- return await mkdtemp5(join31(windy, "module-schema-handoff-"));
11042
+ const windy = join32(project, ".windy");
11043
+ await mkdir9(windy, { recursive: true });
11044
+ return await mkdtemp5(join32(windy, "module-schema-handoff-"));
10572
11045
  }
10573
11046
  async function listSqlFiles(root) {
10574
- return (await readdir8(root)).filter((path2) => path2.endsWith(".sql")).sort();
11047
+ return (await readdir9(root)).filter((path2) => path2.endsWith(".sql")).sort();
10575
11048
  }
10576
11049
  async function executeDrizzleGenerate(configPath, cwd) {
10577
11050
  await assertProcess("bunx", ["--bun", "drizzle-kit", "generate", "--config", configPath], cwd, true);
@@ -10584,7 +11057,7 @@ async function assertCleanGit(project) {
10584
11057
  }
10585
11058
  async function exists(path2) {
10586
11059
  try {
10587
- await access2(path2);
11060
+ await access3(path2);
10588
11061
  return true;
10589
11062
  } catch {
10590
11063
  return false;
@@ -10598,15 +11071,15 @@ async function runModuleSchemaCommand(args) {
10598
11071
  if (args[1] !== "handoff") {
10599
11072
  throw new Error("\u5F53\u524D schema \u547D\u4EE4\u53EA\u652F\u6301 handoff");
10600
11073
  }
10601
- const projectDirectory = resolve9(readValue3(args, "--cwd") || process.cwd());
11074
+ const projectDirectory = resolve10(readValue4(args, "--cwd") || process.cwd());
10602
11075
  const result2 = await handoffModuleSchemaLineages({
10603
11076
  projectDirectory,
10604
- configPath: readValue3(args, "--config")
11077
+ configPath: readValue4(args, "--config")
10605
11078
  });
10606
11079
  console.log(`Module schema lineage \u4EA4\u63A5\u5B8C\u6210\uFF1A${result2.moduleCount} \u4E2A\u6A21\u5757\u3001${result2.tableCount} \u5F20\u65E7\u5E73\u53F0 snapshot \u8868`);
10607
11080
  return true;
10608
11081
  }
10609
- function readValue3(args, flag) {
11082
+ function readValue4(args, flag) {
10610
11083
  const index = args.indexOf(flag);
10611
11084
  return index >= 0 ? args[index + 1] : undefined;
10612
11085
  }