create-windy 0.9.1 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/dist/cli.js +1691 -1151
  2. package/package.json +2 -2
  3. package/template/.agents/skills/windy-business-module/SKILL.md +33 -1
  4. package/template/.windy-template.json +6 -2
  5. package/template/AGENTS.md +4 -0
  6. package/template/README.md +2 -0
  7. package/template/apps/agent-server/Dockerfile +1 -1
  8. package/template/apps/agent-server/README.md +1 -1
  9. package/template/apps/agent-server/native/EXTENSIONS.md +2 -2
  10. package/template/apps/agent-server/native/parallel-harness.ts +2 -0
  11. package/template/apps/agent-server/native/storage-lifecycle.test.ts +2 -1
  12. package/template/apps/agent-server/package.json +42 -41
  13. package/template/apps/agent-server/plugins/downloads/README.md +1 -1
  14. package/template/apps/agent-server/plugins/drop-overlay/README.md +1 -1
  15. package/template/apps/agent-server/plugins/settings/README.md +1 -1
  16. package/template/apps/agent-server/plugins/skill-library/client-chunks.test.ts +4 -1
  17. package/template/apps/agent-server/plugins/theme/README.md +5 -1
  18. package/template/apps/agent-server/plugins/theme/source-tokens.ts +0 -1
  19. package/template/apps/agent-server/plugins/theme/tokens.json +0 -4
  20. package/template/apps/agent-server/plugins/tool-catalog/README.md +6 -3
  21. package/template/apps/agent-server/plugins/tool-catalog/discovery.mjs +13 -10
  22. package/template/apps/agent-server/plugins/tool-catalog/discovery.test.ts +68 -6
  23. package/template/apps/agent-server/runtime/compatibility.ts +7 -1
  24. package/template/apps/agent-server/runtime/dsh-engine.ts +2 -0
  25. package/template/apps/agent-server/runtime/questions-workflow.nodecheck.ts +2 -0
  26. package/template/apps/server/Dockerfile +2 -1
  27. package/template/apps/server/package.json +11 -11
  28. package/template/apps/server/scripts/validate-module-composition-once.ts +2 -0
  29. package/template/apps/server/scripts/validate-module-composition.ts +8 -4
  30. package/template/apps/server/src/admin/routes.test.ts +29 -1
  31. package/template/apps/server/src/admin/routes.ts +20 -2
  32. package/template/apps/server/src/agent/client-session/completion.ts +1 -1
  33. package/template/apps/server/src/agent/client-session/manifest-admission.ts +1 -1
  34. package/template/apps/server/src/agent/client-session/request.ts +1 -1
  35. package/template/apps/server/src/agent/context-version-routes.ts +37 -13
  36. package/template/apps/server/src/agent/run-facade.ts +1 -1
  37. package/template/apps/server/src/agent/run-input-verification.ts +1 -1
  38. package/template/apps/server/src/agent/run-routes.ts +1 -14
  39. package/template/apps/server/src/agent/run-start-preparation.ts +1 -1
  40. package/template/apps/server/src/agent/runtime-bootstrap-input.ts +2 -0
  41. package/template/apps/server/src/agent/runtime-bootstrap.ts +49 -157
  42. package/template/apps/server/src/agent/runtime-disabled-composition.ts +2 -1
  43. package/template/apps/server/src/agent/session-invalidation/authorization-revocation.ts +54 -0
  44. package/template/apps/server/src/agent/session-runtime-composition.ts +1 -51
  45. package/template/apps/server/src/agent/unavailable-routes.ts +13 -2
  46. package/template/apps/server/src/agent-workflow/runtime-composition.ts +3 -0
  47. package/template/apps/server/src/agent-workflow/service.ts +17 -1
  48. package/template/apps/server/src/ai-artifact/media-resource-route.test.ts +134 -0
  49. package/template/apps/server/src/ai-artifact/routes.ts +1 -1
  50. package/template/apps/server/src/ai-artifact/runtime.ts +12 -0
  51. package/template/apps/server/src/ai-runtime-readiness/operation-port.test-support.ts +1 -1
  52. package/template/apps/server/src/ai-runtime-readiness/operation-port.ts +8 -0
  53. package/template/apps/server/src/ai-runtime-security/internal-token.ts +14 -0
  54. package/template/apps/server/src/app/antivirus-composition.test.ts +63 -0
  55. package/template/apps/server/src/app/antivirus-composition.ts +29 -0
  56. package/template/apps/server/src/app/dsh-workbench-socket-audit.test.ts +2 -1
  57. package/template/apps/server/src/configuration/definition.ts +15 -11
  58. package/template/apps/server/src/document/facade-support.ts +2 -2
  59. package/template/apps/server/src/document/facade.ts +2 -2
  60. package/template/apps/server/src/document/output-artifact-composition.ts +9 -5
  61. package/template/apps/server/src/document/output-artifact.test-support.ts +1 -1
  62. package/template/apps/server/src/embedding/facade.ts +1 -1
  63. package/template/apps/server/src/embedding/input.ts +1 -1
  64. package/template/apps/server/src/embedding/routes.ts +1 -1
  65. package/template/apps/server/src/index.ts +60 -60
  66. package/template/apps/server/src/license/routes.ts +4 -1
  67. package/template/apps/server/src/license/runtime-business-access.test.ts +261 -0
  68. package/template/apps/server/src/media-runtime/composition.ts +157 -0
  69. package/template/apps/server/src/media-runtime/media-only-composition.ts +44 -0
  70. package/template/apps/server/src/media-runtime/module-ai-port-factory.test.ts +69 -0
  71. package/template/apps/server/src/media-runtime/module-ai-port-factory.ts +72 -0
  72. package/template/apps/server/src/{agent → media-runtime}/runtime-readiness-sources.ts +14 -6
  73. package/template/apps/server/src/media-runtime/shared-composition.ts +104 -0
  74. package/template/apps/server/src/module-composition.test.ts +22 -0
  75. package/template/apps/server/src/module-composition.ts +48 -0
  76. package/template/apps/server/src/module-host/ai-operation-port.ts +97 -30
  77. package/template/apps/server/src/module-host/ai-operation-speech-normalize.test.ts +1 -1
  78. package/template/apps/server/src/module-host/http-runtime.ts +9 -3
  79. package/template/apps/server/src/module-host/static-route-authorization.test.ts +6 -3
  80. package/template/apps/server/src/module-host/storage-authorization-binding.test.ts +4 -0
  81. package/template/apps/server/src/module-runtime-validation.ts +2 -0
  82. package/template/apps/server/src/observability/diagnostic-index.ts +26 -1
  83. package/template/apps/server/src/observability/diagnostic-repository.ts +40 -11
  84. package/template/apps/server/src/platform-http-runtime.ts +1 -1
  85. package/template/apps/server/src/runtime-feature.ts +5 -1
  86. package/template/apps/server/src/runtime.ts +3 -0
  87. package/template/apps/server/src/settings/drizzle-watermark-repository.ts +35 -4
  88. package/template/apps/server/src/settings/watermark-repository.ts +14 -0
  89. package/template/apps/server/src/settings/watermark-routes.test.ts +84 -2
  90. package/template/apps/server/src/settings/watermark-routes.ts +109 -0
  91. package/template/apps/server/src/settings/watermark-transfer.ts +79 -0
  92. package/template/apps/server/src/speech/facade-validation.ts +1 -1
  93. package/template/apps/server/src/speech/facade.ts +2 -2
  94. package/template/apps/server/src/startup-composition-validation.test.ts +32 -0
  95. package/template/apps/server/src/startup-finalization.ts +2 -0
  96. package/template/apps/server/src/startup-runtime.ts +77 -0
  97. package/template/apps/server/src/system/ai-runtime-build.ts +3 -0
  98. package/template/apps/server/src/system/ai-runtime-configuration.test.ts +36 -0
  99. package/template/apps/server/src/system/ai-runtime-configuration.ts +58 -0
  100. package/template/apps/server/src/system/diagnostic-routes.test.ts +45 -0
  101. package/template/apps/server/src/system/diagnostic-routes.ts +35 -0
  102. package/template/apps/server/src/system/environment-policy-reader.test.ts +74 -0
  103. package/template/apps/server/src/system/environment-policy-reader.ts +62 -0
  104. package/template/apps/server/src/system/feature-environment-assessment.test.ts +148 -0
  105. package/template/apps/server/src/system/feature-environment-assessment.ts +179 -0
  106. package/template/apps/server/src/system/host-diagnostics.test.ts +155 -0
  107. package/template/apps/server/src/system/host-diagnostics.ts +225 -0
  108. package/template/apps/server/src/system/menu-registry.test.ts +26 -0
  109. package/template/apps/server/src/system/menu-registry.ts +9 -7
  110. package/template/apps/server/src/system/operations.test.ts +5 -0
  111. package/template/apps/server/src/system/operations.ts +36 -12
  112. package/template/apps/server/src/system/role-authorization-postgres.integration.test.ts +181 -0
  113. package/template/apps/web/Dockerfile +7 -3
  114. package/template/apps/web/dev/dev-port.test.ts +6 -6
  115. package/template/apps/web/dev/dev-port.ts +1 -1
  116. package/template/apps/web/dev/start.ts +6 -1
  117. package/template/apps/web/index.html +1 -0
  118. package/template/apps/web/package.json +16 -16
  119. package/template/apps/web/runtime-server.test.ts +22 -1
  120. package/template/apps/web/runtime-server.ts +6 -0
  121. package/template/apps/web/src/App.vue +6 -3
  122. package/template/apps/web/src/assets/windy.svg +4 -0
  123. package/template/apps/web/src/components/auth/LoginForm.vue +2 -2
  124. package/template/apps/web/src/components/auth/PasswordChangeForm.vue +14 -3
  125. package/template/apps/web/src/components/common/FormDialog.vue +1 -1
  126. package/template/apps/web/src/components/license/LicenseFlow.webtest.ts +3 -0
  127. package/template/apps/web/src/components/license/LicenseMachineCodeQr.vue +99 -0
  128. package/template/apps/web/src/components/license/LicenseMachineCodeQr.webtest.ts +83 -0
  129. package/template/apps/web/src/components/license/LicensePairingCard.vue +1 -1
  130. package/template/apps/web/src/components/license/LicenseStatusCard.vue +14 -2
  131. package/template/apps/web/src/composables/useLoginFlow.ts +2 -2
  132. package/template/apps/web/src/composables/usePlatformSettings.ts +1 -1
  133. package/template/apps/web/src/composables/useWatermarkSettings.ts +1 -0
  134. package/template/apps/web/src/layout/AppSidebar.vue +5 -9
  135. package/template/apps/web/src/layout/AppSidebar.webtest.ts +4 -1
  136. package/template/apps/web/src/pages/auth/ChangePasswordPage.vue +67 -6
  137. package/template/apps/web/src/pages/auth/LoginPage.vue +3 -12
  138. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.vue +30 -0
  139. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationWorkbench.vue +25 -14
  140. package/template/apps/web/src/pages/configuration/components/TimeZoneSelect.vue +55 -0
  141. package/template/apps/web/src/pages/profile/PersonalSettingsPage.vue +64 -11
  142. package/template/apps/web/src/pages/profile/PersonalSettingsPage.webtest.ts +21 -1
  143. package/template/apps/web/src/pages/settings/BrandingSettingsSection.vue +7 -18
  144. package/template/apps/web/src/pages/settings/BrandingSettingsSection.webtest.ts +9 -4
  145. package/template/apps/web/src/pages/settings/GlobalSettingsPage.vue +51 -6
  146. package/template/apps/web/src/pages/settings/GlobalSettingsPage.webtest.ts +19 -3
  147. package/template/apps/web/src/pages/settings/WatermarkSettingsSection.vue +1 -0
  148. package/template/apps/web/src/pages/settings/WatermarkYamlTransfer.vue +211 -0
  149. package/template/apps/web/src/pages/system/DiagnosticLookupPage.vue +18 -3
  150. package/template/apps/web/src/pages/system/DiagnosticLookupPage.webtest.ts +4 -1
  151. package/template/apps/web/src/pages/system/SystemOperationsPage.vue +23 -0
  152. package/template/apps/web/src/pages/system/components/DiagnosticLogList.vue +120 -0
  153. package/template/apps/web/src/pages/system/components/OperationEnvironmentPanel.vue +237 -0
  154. package/template/apps/web/src/pages/system/components/OperationFeatureAssessmentPanel.vue +131 -0
  155. package/template/apps/web/src/pages/system/components/PagedSelectionList.vue +31 -24
  156. package/template/apps/web/src/pages/system/components/SystemResourceActions.vue +2 -1
  157. package/template/apps/web/src/pages/system/components/SystemResourceEditor.vue +11 -2
  158. package/template/apps/web/src/pages/system/components/SystemResourcePage.vue +1 -0
  159. package/template/apps/web/src/pages/system/components/SystemResourcePage.webtest.ts +6 -2
  160. package/template/apps/web/src/pages/system/components/agent-skills/AgentSkillImportDialog.vue +18 -9
  161. package/template/apps/web/src/pages/system/components/agent-workbench/SessionReplayTimeline.vue +18 -9
  162. package/template/apps/web/src/pages/system/components/agent-workbench/TestRunComposer.vue +66 -34
  163. package/template/apps/web/src/pages/system/components/agent-workbench/ToolCatalogPanel.vue +48 -33
  164. package/template/apps/web/src/pages/system/components/agent-workbench/ToolCatalogPanel.webtest.ts +9 -2
  165. package/template/apps/web/src/pages/system/components/agent-workbench/ToolRuleDetail.vue +23 -16
  166. package/template/apps/web/src/pages/system/components/dictionary-bulk/DictionaryBulkJobDetail.vue +10 -7
  167. package/template/apps/web/src/pages/system/components/user-account/RolePermissionSummary.vue +81 -57
  168. package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.vue +6 -4
  169. package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.webtest.ts +20 -4
  170. package/template/apps/web/src/pages/system/resource-config.ts +9 -0
  171. package/template/apps/web/src/pages/work-orders/WorkOrderListPage.vue +1 -1
  172. package/template/apps/web/src/router/disabled-feature-loading.webtest.ts +78 -0
  173. package/template/apps/web/src/router/index.ts +3 -6
  174. package/template/apps/web/src/router/manifest-routes.ts +2 -3
  175. package/template/apps/web/src/router/platform-admin-pages.ts +21 -62
  176. package/template/apps/web/src/router/platform-admin-route-bindings.ts +4 -4
  177. package/template/apps/web/src/services/diagnostics-api.ts +13 -1
  178. package/template/apps/web/src/services/operations-api.ts +4 -0
  179. package/template/apps/web/src/services/platform-settings-api.ts +35 -1
  180. package/template/apps/web/vite.config.ts +9 -3
  181. package/template/deploy/environment/windy.environment.example.yaml +53 -0
  182. package/template/docker-compose.yml +4 -2
  183. package/template/docs/architecture/agent-admin-workbench-implementation.md +1 -1
  184. package/template/docs/architecture/agent-admin-workbench-plan.md +1 -1
  185. package/template/docs/architecture/agent-workflow.md +8 -0
  186. package/template/docs/architecture/durable-jobs.md +17 -0
  187. package/template/docs/architecture/embedded-dsh-integration.md +1 -1
  188. package/template/docs/development/dsh-plugins.md +1 -1
  189. package/template/docs/development/performance-engineering.md +24 -0
  190. package/template/docs/operations/admin-settings-yaml.md +29 -0
  191. package/template/docs/operations/dsh-agent-migration.md +2 -2
  192. package/template/docs/operations/environment-capabilities.md +185 -0
  193. package/template/docs/platform/module-configuration.md +18 -3
  194. package/template/docs/platform/platform-authentication.md +1 -1
  195. package/template/package.json +10 -8
  196. package/template/packages/config/package.json +2 -2
  197. package/template/packages/config/src/definition.ts +14 -4
  198. package/template/packages/config/src/environment-loader.ts +27 -3
  199. package/template/packages/config/src/platform-ai-runtime.test.ts +94 -0
  200. package/template/packages/config/src/platform.ts +5 -2
  201. package/template/packages/database/drizzle/0065_durable_job_lease_snapshot_lookup.sql +1 -0
  202. package/template/packages/database/drizzle/0066_agent_source_sequence_bigint.sql +3 -0
  203. package/template/packages/database/drizzle/0067_media_runtime_access.sql +46 -0
  204. package/template/packages/database/drizzle/meta/0065_snapshot.json +15370 -0
  205. package/template/packages/database/drizzle/meta/0066_snapshot.json +15370 -0
  206. package/template/packages/database/drizzle/meta/0067_snapshot.json +15370 -0
  207. package/template/packages/database/drizzle/meta/_journal.json +21 -0
  208. package/template/packages/database/package.json +3 -3
  209. package/template/packages/database/src/schema/agent-session.ts +4 -3
  210. package/template/packages/database/src/schema/durable-jobs.ts +4 -0
  211. package/template/packages/database/src/schema/index.ts +3 -1
  212. package/template/packages/database/src/schema-durable-recovery.test.ts +21 -1
  213. package/template/packages/database/src/schema-source-sequence.test.ts +24 -0
  214. package/template/packages/example-work-order/package.json +2 -2
  215. package/template/packages/modules/package.json +1 -1
  216. package/template/packages/modules/src/admin-ui-boundary.test.ts +33 -0
  217. package/template/packages/modules/src/composition.ts +11 -28
  218. package/template/packages/modules/src/manifest.ts +2 -0
  219. package/template/packages/modules/src/menu-composition.ts +24 -0
  220. package/template/packages/modules/src/system-admin-routes.ts +2 -2
  221. package/template/packages/modules/src/system-admin-ui-catalog.ts +6 -1
  222. package/template/packages/modules/src/system-ai-api-bindings.ts +0 -16
  223. package/template/packages/modules/src/system-api-control-plane.ts +9 -1
  224. package/template/packages/modules/src/system-api-permissions.ts +7 -0
  225. package/template/packages/modules/src/system-api-watermark.ts +21 -0
  226. package/template/packages/modules/src/system-audit-actions.ts +3 -1
  227. package/template/packages/modules/src/system-features.ts +21 -3
  228. package/template/packages/modules/src/system-media-api-bindings.ts +97 -0
  229. package/template/packages/modules/src/system-module-audit-selection.ts +2 -0
  230. package/template/packages/modules/src/system-module-catalog.test.ts +13 -6
  231. package/template/packages/modules/src/system-module-catalog.ts +6 -241
  232. package/template/packages/modules/src/system-module-definitions.ts +260 -0
  233. package/template/packages/modules/src/system-modules-access.test.ts +2 -0
  234. package/template/packages/modules/src/system-modules.test.ts +7 -1
  235. package/template/packages/modules/src/system-modules.ts +25 -13
  236. package/template/packages/modules/src/system-permissions.ts +12 -0
  237. package/template/packages/server-sdk/package.json +1 -1
  238. package/template/packages/shared/index.ts +3 -0
  239. package/template/packages/shared/package.json +1 -1
  240. package/template/packages/shared/src/environment-assessment.ts +68 -0
  241. package/template/packages/shared/src/environment-policy.ts +205 -0
  242. package/template/packages/shared/src/license-console-capabilities.ts +7 -0
  243. package/template/packages/shared/src/license.ts +1 -0
  244. package/template/packages/shared/src/module-configuration.ts +2 -1
  245. package/template/scripts/operations/collect-device-info.py +230 -0
  246. package/template/apps/server/src/agent/module-ai-port-factory.ts +0 -41
  247. /package/template/apps/server/src/{agent → ai-runtime-security}/execution-grants.ts +0 -0
  248. /package/template/apps/server/src/{agent → ai-runtime-security}/idempotency-key.test.ts +0 -0
  249. /package/template/apps/server/src/{agent → ai-runtime-security}/idempotency-key.ts +0 -0
  250. /package/template/apps/server/src/{agent → ai-runtime-security}/provider-input-governance.test.ts +0 -0
  251. /package/template/apps/server/src/{agent → ai-runtime-security}/provider-input-governance.ts +0 -0
  252. /package/template/apps/server/src/{agent → media-runtime}/required-environment.ts +0 -0
  253. /package/template/apps/server/src/{agent → media-runtime}/runtime-readiness-sources.test.ts +0 -0
@@ -9,25 +9,22 @@ import { registerBulkDataRoutes } from "./bulk-data/routes.js";
9
9
  // @windy-module system.data-governance begin
10
10
  import { registerGovernedUserExportRoutes } from "./data-governance/export/routes.js";
11
11
  // @windy-module system.data-governance end
12
- import { createServerRuntime } from "./runtime.js";
13
12
  // @windy-module system.scheduler begin
14
13
  import { readPositiveInt } from "./runtime-config.js";
15
14
  // @windy-module system.scheduler end
15
+ // @windy-module system.media-runtime begin
16
+ import { composeMediaOnlyRuntime } from "./media-runtime/media-only-composition.js";
17
+ // @windy-module system.media-runtime end
16
18
  // @windy-module system.agent begin
17
19
  import { DeferredAgentBackgroundOperationPortFactory } from "./agent/background-operation-port.js";
18
20
  import { composeApplicationAgentRuntime } from "./app/agent-runtime-composition.js";
19
21
  import { workflowDurableInput } from "./agent/workflow-durable-runtime-input.js";
20
22
  // @windy-module system.agent end
21
- import { createComposedAuthRuntime } from "./auth/composition.js";
22
- // @windy-module system.license begin
23
- import { createLicenseRuntimeActivation } from "./license/activation.js";
24
- // @windy-module system.license end
25
23
  // @windy-module system.operations begin
26
24
  import { registerSystemOperationRoutes } from "./system/operations.js";
27
25
  import { createPostgresMonitorProbe } from "./system/postgres-monitoring.js";
28
26
  // @windy-module system.operations end
29
27
  import { registerSystemRoutes } from "./system/routes.js";
30
- import { createAuthorizationInvalidationComposition } from "./system/authorization-invalidation-composition.js";
31
28
  // @windy-module system.scheduler begin
32
29
  import { registerSchedulerRoutes } from "./scheduler/routes.js";
33
30
  import { SchedulerWorker } from "./scheduler/worker.js";
@@ -56,9 +53,9 @@ import { createSearchHost } from "./search/host.js";
56
53
  import { registerWorkflowRoutes } from "./workflow/routes.js";
57
54
  // @windy-module system.workflow end
58
55
  import { bootstrapServerModules } from "./module-bootstrap.js";
59
- import { createHttpObservability } from "./platform-http-runtime.js";
60
56
  import { bootstrapPlatformApp } from "./platform-app-bootstrap.js";
61
57
  import { createPreparedPlatformBootstrap } from "./startup-preflight.js";
58
+ import { createStartupRuntime } from "./startup-runtime.js";
62
59
  import { finalizePlatformStartup } from "./startup-finalization.js";
63
60
  const { logger, persistence, platformConfig, preparedModules } =
64
61
  await createPreparedPlatformBootstrap();
@@ -73,36 +70,6 @@ const {
73
70
  systemRepositories,
74
71
  } = await bootstrapServerModules(platformConfig, persistence, preparedModules);
75
72
  const features = foundationSnapshot.features;
76
- // @windy-module system.license begin
77
- const licenseActivation = createLicenseRuntimeActivation(
78
- process.env,
79
- foundationSnapshot.platform.licenseEnabled,
80
- persistence?.licenseResolver,
81
- );
82
- // @windy-module system.license end
83
- const runtime = createServerRuntime(process.env, {
84
- actorResolver: persistence?.actorResolver,
85
- // @windy-module system.license begin
86
- licenseResolver: licenseActivation.resolver,
87
- licenseEnforced: licenseActivation.enforced,
88
- // @windy-module system.license end
89
- featureResolver: persistence ? systemRepositories.features : undefined,
90
- featureCatalog: features,
91
- disabledModuleNames: new Set(foundationSnapshot.disabledModuleNames),
92
- // @windy-module system.audit begin
93
- auditReader: persistence?.auditReader,
94
- auditWriter: persistence?.auditWriter,
95
- // @windy-module system.audit end
96
- });
97
- const observability = createHttpObservability(runtime, persistence, logger);
98
- const authorizationInvalidation = createAuthorizationInvalidationComposition({
99
- platformConfig,
100
- persistence,
101
- repositories: systemRepositories,
102
- });
103
- // @windy-module system.license begin
104
- licenseActivation.runtime.setAuditSink(runtime.auditSink);
105
- // @windy-module system.license end
106
73
  const {
107
74
  authRepository,
108
75
  authService,
@@ -112,12 +79,18 @@ const {
112
79
  // @windy-module system.data-governance begin
113
80
  reauthentication,
114
81
  // @windy-module system.data-governance end
115
- } = createComposedAuthRuntime({
116
- repositories: systemRepositories,
117
- platformConfig,
118
- persistence,
119
82
  runtime,
120
- });
83
+ observability,
84
+ authorizationInvalidation,
85
+ aiRuntimeSwitches,
86
+ disabledAiFeatures,
87
+ // @windy-module system.license begin
88
+ licenseActivation,
89
+ // @windy-module system.license end
90
+ } = await createStartupRuntime(
91
+ { logger, persistence, platformConfig },
92
+ { foundationSnapshot, systemRepositories },
93
+ );
121
94
  // @windy-module system.storage begin
122
95
  const { storageRuntime, reportStagingRuntime } = createStorageHostRuntime(
123
96
  process.env,
@@ -276,23 +249,47 @@ registerSystemOperationRoutes(app, runtime, features, {
276
249
  databaseMonitorProbe: createPostgresMonitorProbe(persistence?.pool),
277
250
  });
278
251
  // @windy-module system.operations end
279
- // @windy-module system.agent begin
280
- const agentRuntime = composeApplicationAgentRuntime({
252
+ // @windy-module system.media-runtime begin
253
+ const aiRuntimeInput = {
281
254
  app,
282
- platformConfig,
283
255
  modules: foundationModules,
284
256
  hosts: moduleHosts,
285
257
  runtime,
286
258
  systemRepositories,
287
259
  storage: storageRuntime,
288
- workflow: workflowService,
289
- ...workflowDurableInput([durableJobRuntime, storageRuntime, logger]),
290
260
  db: persistence?.db,
291
- backgroundAi: moduleBackgroundAi,
292
- bindAuthorizationInvalidation: (target) =>
293
- authorizationInvalidation.bind(target),
294
- bindClientAgentSessions,
295
- });
261
+ };
262
+ let sharedAiPorts:
263
+ | Pick<
264
+ Parameters<typeof installModuleHttpRuntime>[2],
265
+ "aiArtifacts" | "aiOperations" | "aiReadiness"
266
+ >
267
+ | undefined;
268
+ if (aiRuntimeSwitches.agent === false) {
269
+ sharedAiPorts = composeMediaOnlyRuntime({
270
+ ...aiRuntimeInput,
271
+ enableAI: platformConfig.enableAI,
272
+ switches: aiRuntimeSwitches,
273
+ features,
274
+ });
275
+ }
276
+ // @windy-module system.media-runtime end
277
+ // @windy-module system.agent begin
278
+ const agentRuntime =
279
+ aiRuntimeSwitches.agent !== false
280
+ ? composeApplicationAgentRuntime({
281
+ ...aiRuntimeInput,
282
+ aiRuntimeSwitches,
283
+ platformConfig,
284
+ workflow: workflowService,
285
+ ...workflowDurableInput([durableJobRuntime, storageRuntime, logger]),
286
+ backgroundAi: moduleBackgroundAi,
287
+ bindAuthorizationInvalidation: (target) =>
288
+ authorizationInvalidation.bind(target),
289
+ bindClientAgentSessions,
290
+ })
291
+ : undefined;
292
+ if (agentRuntime) sharedAiPorts = agentRuntime;
296
293
  // @windy-module system.agent end
297
294
  // @windy-module system.scheduler begin
298
295
  registerSchedulerRoutes(app, scheduler, runtime, features);
@@ -312,13 +309,15 @@ installModuleHttpRuntime(moduleHosts, app, {
312
309
  reportStaging: reportStagingRuntime,
313
310
  // @windy-module system.storage end
314
311
  systemRepositories,
312
+ // @windy-module system.media-runtime begin
313
+ aiArtifacts: sharedAiPorts?.aiArtifacts,
314
+ aiOperations: sharedAiPorts?.aiOperations,
315
+ aiReadiness: sharedAiPorts?.aiReadiness,
316
+ // @windy-module system.media-runtime end
315
317
  // @windy-module system.agent begin
316
- aiArtifacts: agentRuntime.aiArtifacts,
317
- aiOperations: agentRuntime.aiOperations,
318
- aiReadiness: agentRuntime.aiReadiness,
319
- sessionMemory: agentRuntime.sessionMemory,
320
- sourceInvalidation: agentRuntime.sourceInvalidation,
321
- screenshots: agentRuntime.screenshots,
318
+ sessionMemory: agentRuntime?.sessionMemory,
319
+ sourceInvalidation: agentRuntime?.sourceInvalidation,
320
+ screenshots: agentRuntime?.screenshots,
322
321
  // @windy-module system.agent end
323
322
  // @windy-module system.data-governance begin
324
323
  reauthenticate: (actorId, password) =>
@@ -328,13 +327,14 @@ installModuleHttpRuntime(moduleHosts, app, {
328
327
  });
329
328
  finalizePlatformStartup({
330
329
  app,
330
+ disabledFeatureKeys: disabledAiFeatures,
331
331
  modules: foundationModules,
332
332
  // @windy-module system.scheduler begin
333
333
  tasks: scheduledTaskDefinitions,
334
334
  // @windy-module system.scheduler end
335
335
  // @windy-module system.agent begin
336
- tools: agentRuntime.toolHandlers,
337
- disposeAgentRuntime: agentRuntime.disposeAgentRuntime,
336
+ tools: agentRuntime?.toolHandlers,
337
+ disposeAgentRuntime: agentRuntime?.disposeAgentRuntime,
338
338
  // @windy-module system.agent end
339
339
  // @windy-module system.search begin
340
340
  providers: searchHost.providerKeys,
@@ -35,7 +35,10 @@ export function registerPlatformLicenseRoutes(
35
35
  { restrictedModeAccess: "admin-admission" },
36
36
  );
37
37
  if (denied) return denied;
38
- return licenseRuntime.snapshotForActor(context.guardContext.actor);
38
+ return Response.json(
39
+ await licenseRuntime.snapshotForActor(context.guardContext.actor),
40
+ { headers: { "cache-control": "no-store" } },
41
+ );
39
42
  });
40
43
 
41
44
  app.post(
@@ -0,0 +1,261 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { generateKeyPairSync, sign } from "node:crypto";
3
+ import { Elysia } from "elysia";
4
+ import {
5
+ createLicenseSigningPayload,
6
+ MemoryAtomicLicenseStore,
7
+ } from "@southwind-ai/license";
8
+ import { licenseVersionKey, type SignedLicenseV1 } from "@southwind-ai/shared";
9
+ import { createFoundationSnapshot, loadSystemModules } from "../foundation.js";
10
+ import { createServerRuntime } from "../runtime.js";
11
+ import { createStaticRouteAuthorizer } from "../module-host/static-route-authorization.js";
12
+ import { evaluateAdminAccess } from "../admin/routes.js";
13
+ import { evaluateRouteGuards } from "../route-guards.js";
14
+ import { PlatformLicenseRuntime } from "./runtime-service.js";
15
+ import { registerPlatformLicenseRoutes } from "./routes.js";
16
+ import {
17
+ evaluateRestrictedHttpRequest,
18
+ licenseRestrictedResponse,
19
+ } from "./restriction.js";
20
+
21
+ const businessPermissions = ["home.read", "case.read"];
22
+
23
+ describe("普通业务账号的 License 运行状态契约", () => {
24
+ test("仅业务权限可读取最小投影并通过业务 Guard,未授权页面和后台仍拒绝", async () => {
25
+ const fixture = createFixture();
26
+ const response = await fixture.request("/license/runtime");
27
+ expect(response.status).toBe(200);
28
+ expect(response.headers.get("cache-control")).toBe("no-store");
29
+ expect(await response.json()).toEqual({
30
+ status: "valid",
31
+ restricted: false,
32
+ expiresAt: "2030-08-01T00:00:00.000Z",
33
+ reminder: { level: "none", daysRemaining: 1461 },
34
+ canViewDetails: false,
35
+ });
36
+ const context = await fixture.runtime.createGuardContext(
37
+ new Request("http://localhost/"),
38
+ );
39
+ for (const permissionKey of businessPermissions) {
40
+ expect(
41
+ (
42
+ await evaluateRouteGuards(fixture.runtime, context, {
43
+ permission: { method: "GET", path: "/api/business", permissionKey },
44
+ })
45
+ ).allowed,
46
+ ).toBe(true);
47
+ }
48
+ expect(
49
+ (
50
+ await evaluateRouteGuards(fixture.runtime, context, {
51
+ permission: {
52
+ method: "POST",
53
+ path: "/api/cases",
54
+ permissionKey: "case.manage",
55
+ },
56
+ })
57
+ ).reason,
58
+ ).toBe("missing-permission");
59
+ expect(
60
+ (await evaluateAdminAccess(fixture.runtime, context, fixture.features))
61
+ .reason,
62
+ ).toBe("missing-permission");
63
+ });
64
+
65
+ test("匿名及服务 Actor 不取得运行状态", async () => {
66
+ for (const actorType of ["anonymous", "service"] as const) {
67
+ expect(
68
+ (await createFixture({ actorType }).request("/license/runtime")).status,
69
+ ).toBe(401);
70
+ }
71
+ });
72
+
73
+ test("无任何权限也只获得基础投影,License 管理开关不影响基础读取", async () => {
74
+ const fixture = createFixture({
75
+ permissionKeys: [],
76
+ licenseFeatureEnabled: false,
77
+ });
78
+ const response = await fixture.request("/license/runtime");
79
+ expect(response.status).toBe(200);
80
+ expect(await response.json()).toMatchObject({
81
+ canViewDetails: false,
82
+ restricted: false,
83
+ });
84
+ });
85
+
86
+ test("业务用户不可安装、配对、确认或读取管理记录", async () => {
87
+ const fixture = createFixture();
88
+ for (const [method, path] of [
89
+ ["POST", "/license/runtime/install"],
90
+ ["POST", "/license/runtime/pairing/sessions"],
91
+ ["GET", "/license/runtime/pairing/sessions/session_1"],
92
+ ["POST", "/license/runtime/pairing/sessions/session_1/confirm"],
93
+ ]) {
94
+ const response = await fixture.request(path!, method!);
95
+ expect(response.status).toBe(403);
96
+ expect(await response.json()).toMatchObject({
97
+ reason: "missing-permission",
98
+ });
99
+ }
100
+ const context = await fixture.runtime.createGuardContext(
101
+ new Request("http://localhost/"),
102
+ );
103
+ expect(
104
+ (
105
+ await fixture.authorizer.authorize(
106
+ context,
107
+ "GET",
108
+ "/api/system/licenses",
109
+ )
110
+ )?.status,
111
+ ).toBe(403);
112
+ });
113
+
114
+ test("维护人员保留详情,read 权限不升级为详情权限", async () => {
115
+ const readOnly = await createFixture({
116
+ permissionKeys: ["system.license.read"],
117
+ }).request("/license/runtime");
118
+ expect(await readOnly.json()).toMatchObject({ canViewDetails: false });
119
+ const manager = await createFixture({
120
+ permissionKeys: ["system.license.manage"],
121
+ }).request("/license/runtime");
122
+ expect(await manager.json()).toMatchObject({
123
+ canViewDetails: true,
124
+ machineCode: "test-machine",
125
+ license: { claims: { customer: { name: "测试客户" } } },
126
+ });
127
+ });
128
+
129
+ test.each(["expired", "missing", "invalid"] as const)(
130
+ "%s 状态仍可读取,业务访问保持受限",
131
+ async (status) => {
132
+ const fixture = createFixture({ status });
133
+ const response = await fixture.request("/license/runtime");
134
+ expect(response.status).toBe(200);
135
+ expect(await response.json()).toMatchObject({
136
+ status,
137
+ restricted: true,
138
+ canViewDetails: false,
139
+ });
140
+ const context = await fixture.runtime.createGuardContext(
141
+ new Request("http://localhost/"),
142
+ );
143
+ expect(
144
+ (
145
+ await evaluateRouteGuards(fixture.runtime, context, {
146
+ permission: {
147
+ method: "GET",
148
+ path: "/api/cases",
149
+ permissionKey: "case.read",
150
+ },
151
+ })
152
+ ).reason,
153
+ ).toBe("license-restricted");
154
+ expect(
155
+ evaluateRestrictedHttpRequest(
156
+ context,
157
+ new Request("http://localhost/api/cases"),
158
+ ).allowed,
159
+ ).toBe(false);
160
+ expect(
161
+ (await fixture.request("/license/runtime/install", "POST")).status,
162
+ ).toBe(403);
163
+ },
164
+ );
165
+ });
166
+
167
+ function createFixture(
168
+ options: {
169
+ actorType?: "user" | "anonymous" | "service";
170
+ permissionKeys?: string[];
171
+ licenseFeatureEnabled?: boolean;
172
+ status?: "expired" | "missing" | "invalid";
173
+ } = {},
174
+ ) {
175
+ const { privateKey, publicKey } = generateKeyPairSync("ed25519");
176
+ const unsigned = {
177
+ formatVersion: 1,
178
+ algorithm: "ED25519",
179
+ publicKeyId: "test-key",
180
+ claims: {
181
+ licenseId: "test-license",
182
+ productCode: "windy-platform",
183
+ machineCode: "test-machine",
184
+ customer: { id: "customer", code: "C", name: "测试客户" },
185
+ project: { id: "project", code: "P", name: "测试项目" },
186
+ contract: { id: "contract", contractNumber: "TEST", version: "1" },
187
+ issuedAt: "2026-07-01T00:00:00.000Z",
188
+ expiresAt:
189
+ options.status === "expired"
190
+ ? "2026-07-31T00:00:00.000Z"
191
+ : "2030-08-01T00:00:00.000Z",
192
+ licenseVersion: { key: licenseVersionKey("advanced"), name: "高级版" },
193
+ },
194
+ } as const;
195
+ const license: SignedLicenseV1 = {
196
+ ...unsigned,
197
+ signature: sign(
198
+ null,
199
+ Buffer.from(createLicenseSigningPayload(unsigned)),
200
+ privateKey,
201
+ ).toString("base64"),
202
+ };
203
+ const licenseRuntime = new PlatformLicenseRuntime({
204
+ productCode: "windy-platform",
205
+ machineCode: "test-machine",
206
+ publicKeys: {
207
+ "test-key": publicKey.export({ type: "spki", format: "pem" }).toString(),
208
+ },
209
+ store: new MemoryAtomicLicenseStore(
210
+ options.status === "missing"
211
+ ? undefined
212
+ : options.status === "invalid"
213
+ ? "{broken"
214
+ : JSON.stringify(license),
215
+ ),
216
+ enforceRestrictions: true,
217
+ now: () => new Date("2026-08-01T00:00:00.000Z"),
218
+ });
219
+ const { features } = createFoundationSnapshot();
220
+ const runtime = createServerRuntime(
221
+ {},
222
+ {
223
+ actorResolver: {
224
+ resolve: () => ({
225
+ id: "business-user",
226
+ type: options.actorType ?? "user",
227
+ name: "业务用户",
228
+ roleCodes: ["officer"],
229
+ permissionKeys: options.permissionKeys ?? businessPermissions,
230
+ }),
231
+ },
232
+ licenseResolver: licenseRuntime,
233
+ featureCatalog: features,
234
+ featureResolver: {
235
+ resolve: (key) => ({
236
+ key,
237
+ enabled:
238
+ key !== "system.license" || options.licenseFeatureEnabled !== false,
239
+ visible: "visible",
240
+ }),
241
+ },
242
+ },
243
+ );
244
+ const authorizer = createStaticRouteAuthorizer(runtime, loadSystemModules());
245
+ const app = new Elysia({ prefix: "/api" })
246
+ .derive(async ({ request }) => ({
247
+ guardContext: await runtime.createGuardContext(request),
248
+ }))
249
+ .onBeforeHandle(({ request, guardContext }) => {
250
+ if (!evaluateRestrictedHttpRequest(guardContext, request).allowed)
251
+ return licenseRestrictedResponse(guardContext);
252
+ });
253
+ registerPlatformLicenseRoutes(app, licenseRuntime, authorizer);
254
+ return {
255
+ runtime,
256
+ authorizer,
257
+ features,
258
+ request: (path: string, method = "GET") =>
259
+ app.handle(new Request(`http://localhost/api${path}`, { method })),
260
+ };
261
+ }
@@ -0,0 +1,157 @@
1
+ import type { AiRuntimeSwitches } from "@southwind-ai/shared";
2
+ import { DocumentRunFacade } from "../document/facade.js";
3
+ import {
4
+ RemoteDocumentRuntime,
5
+ unavailableDocumentRuntime,
6
+ } from "../document/remote-runtime.js";
7
+ import { registerDocumentRunRoutes } from "../document/routes.js";
8
+ import { createDocumentOutputRuntime } from "../document/output-artifact-composition.js";
9
+ import { installDocumentOutputRuntimeRoutes } from "../document/output-artifact-route-composition.js";
10
+ import { EmbeddingFacade } from "../embedding/facade.js";
11
+ import { EmbeddingRunAuthorizations } from "../embedding/run-authorization.js";
12
+ import { InMemoryEmbeddingRunBindingRepository } from "../embedding/run-binding-repository.js";
13
+ import { DrizzleEmbeddingRunBindingRepository } from "../embedding/drizzle-run-binding-repository.js";
14
+ import {
15
+ RemoteEmbeddingRuntime,
16
+ unavailableEmbeddingRuntime,
17
+ } from "../embedding/remote-runtime.js";
18
+ import {
19
+ registerEmbeddingRoutes,
20
+ registerEmbeddingRunArtifactRoutes,
21
+ } from "../embedding/routes.js";
22
+ import { SpeechRunFacade } from "../speech/facade.js";
23
+ import {
24
+ RemoteSpeechRuntime,
25
+ unavailableSpeechRuntime,
26
+ } from "../speech/remote-runtime.js";
27
+ import { registerSpeechRunRoutes } from "../speech/routes.js";
28
+
29
+ export function connectMediaRuntimes(
30
+ internalToken: string,
31
+ switches?: AiRuntimeSwitches,
32
+ ) {
33
+ const documentRemote =
34
+ switches?.document !== false && process.env.DOCUMENT_SERVER_URL?.trim()
35
+ ? new RemoteDocumentRuntime({
36
+ baseUrl: process.env.DOCUMENT_SERVER_URL,
37
+ internalToken,
38
+ })
39
+ : unavailableDocumentRuntime;
40
+ const speechRemote =
41
+ switches?.speech !== false && process.env.SPEECH_SERVER_URL?.trim()
42
+ ? new RemoteSpeechRuntime({
43
+ baseUrl: process.env.SPEECH_SERVER_URL,
44
+ internalToken,
45
+ })
46
+ : unavailableSpeechRuntime;
47
+ const embeddingRemote =
48
+ switches?.embedding !== false && process.env.EMBEDDING_SERVER_URL?.trim()
49
+ ? new RemoteEmbeddingRuntime({
50
+ baseUrl: process.env.EMBEDDING_SERVER_URL,
51
+ internalToken,
52
+ })
53
+ : unavailableEmbeddingRuntime;
54
+ return { documentRemote, speechRemote, embeddingRemote };
55
+ }
56
+
57
+ export function registerMediaRuntimes(
58
+ input: ReturnType<typeof connectMediaRuntimes> & {
59
+ switches?: AiRuntimeSwitches;
60
+ modules: ConstructorParameters<typeof DocumentRunFacade>[0];
61
+ hosts: ConstructorParameters<typeof DocumentRunFacade>[1];
62
+ runtime: ConstructorParameters<typeof DocumentRunFacade>[2];
63
+ storage: Parameters<typeof createDocumentOutputRuntime>[0]["storage"];
64
+ db: Parameters<typeof createDocumentOutputRuntime>[0]["db"];
65
+ aiReadiness: Parameters<typeof createDocumentOutputRuntime>[0]["readiness"];
66
+ resolveActor: Parameters<
67
+ typeof createDocumentOutputRuntime
68
+ >[0]["resolveActor"];
69
+ aiArtifacts: ConstructorParameters<typeof DocumentRunFacade>[4];
70
+ grants: ConstructorParameters<typeof DocumentRunFacade>[5];
71
+ aiRouteApp: Parameters<typeof registerDocumentRunRoutes>[0];
72
+ internalToken: string;
73
+ },
74
+ ) {
75
+ const {
76
+ documentRemote,
77
+ speechRemote,
78
+ embeddingRemote,
79
+ aiReadiness,
80
+ resolveActor,
81
+ aiArtifacts,
82
+ grants,
83
+ aiRouteApp,
84
+ internalToken,
85
+ } = input;
86
+ const outputRuntime = createDocumentOutputRuntime({
87
+ modules: input.modules,
88
+ readiness: aiReadiness,
89
+ runtime: input.runtime,
90
+ storage: input.storage,
91
+ resolveActor,
92
+ db: input.db,
93
+ documentEnabled: input.switches?.document,
94
+ speechEnabled: input.switches?.speech,
95
+ });
96
+ const documentFacadeWithOutput =
97
+ input.switches?.document === false
98
+ ? undefined
99
+ : new DocumentRunFacade(
100
+ input.modules,
101
+ input.hosts,
102
+ input.runtime,
103
+ documentRemote,
104
+ aiArtifacts,
105
+ grants,
106
+ outputRuntime?.host,
107
+ aiReadiness,
108
+ );
109
+ installDocumentOutputRuntimeRoutes(outputRuntime, aiRouteApp, internalToken);
110
+ if (documentFacadeWithOutput)
111
+ registerDocumentRunRoutes(
112
+ aiRouteApp,
113
+ documentFacadeWithOutput,
114
+ documentRemote,
115
+ );
116
+ const speechFacade =
117
+ input.switches?.speech === false
118
+ ? undefined
119
+ : new SpeechRunFacade(
120
+ input.modules,
121
+ input.hosts,
122
+ input.runtime,
123
+ speechRemote,
124
+ aiArtifacts,
125
+ grants,
126
+ aiReadiness,
127
+ outputRuntime?.speechHost,
128
+ );
129
+ if (speechFacade)
130
+ registerSpeechRunRoutes(aiRouteApp, speechFacade, speechRemote);
131
+ const embeddingFacade =
132
+ input.switches?.embedding === false
133
+ ? undefined
134
+ : new EmbeddingFacade(
135
+ input.modules,
136
+ input.hosts,
137
+ input.runtime,
138
+ embeddingRemote,
139
+ aiArtifacts,
140
+ new EmbeddingRunAuthorizations(
141
+ input.db
142
+ ? new DrizzleEmbeddingRunBindingRepository(input.db)
143
+ : new InMemoryEmbeddingRunBindingRepository(),
144
+ internalToken,
145
+ ),
146
+ aiReadiness,
147
+ );
148
+ if (embeddingFacade) {
149
+ registerEmbeddingRoutes(aiRouteApp, embeddingFacade);
150
+ registerEmbeddingRunArtifactRoutes(
151
+ aiRouteApp,
152
+ embeddingFacade,
153
+ internalToken,
154
+ );
155
+ }
156
+ return { documentFacadeWithOutput, speechFacade, embeddingFacade };
157
+ }
@@ -0,0 +1,44 @@
1
+ import type { SystemRepositories } from "../system/seed.js";
2
+ import { resolveCurrentSystemActor } from "../system/actor.js";
3
+ import {
4
+ createManifestAuthorizedRouteApp,
5
+ createStaticRouteAuthorizer,
6
+ } from "../module-host/static-route-authorization.js";
7
+ import { requiredEnvironment } from "./required-environment.js";
8
+ import { composeSharedAiRuntime } from "./shared-composition.js";
9
+
10
+ type SharedInput = Parameters<typeof composeSharedAiRuntime>[0];
11
+
12
+ export function composeMediaOnlyRuntime(
13
+ input: Omit<
14
+ SharedInput,
15
+ "internalToken" | "secret" | "resolveActor" | "agent"
16
+ > & {
17
+ enableAI: boolean;
18
+ systemRepositories: SystemRepositories;
19
+ },
20
+ ) {
21
+ if (
22
+ !input.enableAI ||
23
+ (input.switches?.document === false &&
24
+ input.switches.speech === false &&
25
+ input.switches.embedding === false)
26
+ )
27
+ return undefined;
28
+
29
+ const authorizer = createStaticRouteAuthorizer(input.runtime, input.modules);
30
+ const shared = composeSharedAiRuntime({
31
+ ...input,
32
+ app: createManifestAuthorizedRouteApp(input.app, authorizer),
33
+ internalToken: requiredEnvironment("AGENT_SERVER_INTERNAL_TOKEN"),
34
+ secret: requiredEnvironment("AI_ARTIFACT_ENCRYPTION_SECRET"),
35
+ resolveActor: (actorId) =>
36
+ resolveCurrentSystemActor(input.systemRepositories, actorId),
37
+ });
38
+ const ports = shared.modulePorts();
39
+ return {
40
+ aiArtifacts: shared.aiArtifacts,
41
+ aiOperations: ports.operations,
42
+ aiReadiness: ports.readiness,
43
+ };
44
+ }