@codemation/host 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/CHANGELOG.md +465 -0
  2. package/LICENSE +1 -37
  3. package/dist/{ApiPaths-CLTHphYZ.js → ApiPaths-Dv1dcHu_.js} +4 -4
  4. package/dist/ApiPaths-Dv1dcHu_.js.map +1 -0
  5. package/dist/{AppConfigFactory-CvpFScwB.js → AppConfigFactory-Cx4qQvRk.js} +114 -53
  6. package/dist/AppConfigFactory-Cx4qQvRk.js.map +1 -0
  7. package/dist/{AppConfigFactory-LK76niPc.d.ts → AppConfigFactory-DnLoQ9Li.d.ts} +8527 -5549
  8. package/dist/{AppContainerFactory-BlLrm6_h.js → AppContainerFactory-DqKYCRNP.js} +7656 -2090
  9. package/dist/AppContainerFactory-DqKYCRNP.js.map +1 -0
  10. package/dist/{CodemationAppContext-CvWi5gey.d.ts → CodemationAppContext-CKVv9W9q.d.ts} +8 -4
  11. package/dist/{CodemationAuthoring.types-BuKNTDC1.d.ts → CodemationAuthoring.types-DA3G3s6d.d.ts} +25 -5
  12. package/dist/{CodemationAuthoring.types-DZl-sJaM.js → CodemationAuthoring.types-NGkBcmmT.js} +18 -6
  13. package/dist/CodemationAuthoring.types-NGkBcmmT.js.map +1 -0
  14. package/dist/{CodemationConfigNormalizer-CYdR0PR5.d.ts → CodemationConfigNormalizer-BAKjetJ6.d.ts} +3 -3
  15. package/dist/{CodemationConsumerConfigLoader-BeAUS144.js → CodemationConsumerConfigLoader-GYpBBvqE.js} +79 -10
  16. package/dist/CodemationConsumerConfigLoader-GYpBBvqE.js.map +1 -0
  17. package/dist/{CodemationConsumerConfigLoader-C3nAj9Bj.d.ts → CodemationConsumerConfigLoader-nxOqvv46.d.ts} +17 -2
  18. package/dist/{CodemationPluginListMerger-B-W5Fa_X.js → CodemationPluginListMerger-D1B1IEbt.js} +1 -1
  19. package/dist/{CodemationPluginListMerger-B-W5Fa_X.js.map → CodemationPluginListMerger-D1B1IEbt.js.map} +1 -1
  20. package/dist/{CodemationPluginListMerger-D-gwVwtw.d.ts → CodemationPluginListMerger-DKLAHT2b.d.ts} +123 -16
  21. package/dist/CodemationTsyringeTypeInfoRegistrar-Bj6FJYFz.js +97 -0
  22. package/dist/CodemationTsyringeTypeInfoRegistrar-Bj6FJYFz.js.map +1 -0
  23. package/dist/{CodemationWhitelabelConfig-CWbcyQqn.d.ts → CodemationWhitelabelConfig-Ca2mCUeC.d.ts} +2 -2
  24. package/dist/{CollectionContracts.types-DdpHft0i.d.ts → CollectionContracts.types-DDyFYT_D.d.ts} +1 -1
  25. package/dist/{CredentialContractsRegistry-D7mcPed2.d.ts → CredentialContractsRegistry-Bq2bq28t.d.ts} +2 -2
  26. package/dist/{CredentialServices-DdCEP2xt.d.ts → CredentialServices-Be2I60Th.d.ts} +65 -20
  27. package/dist/{CredentialServices-CgxwguAv.js → CredentialServices-Dk8yypeL.js} +310 -51
  28. package/dist/CredentialServices-Dk8yypeL.js.map +1 -0
  29. package/dist/InternalHonoApiRouteRegistrar-Ce1yxpnO.d.ts +17 -0
  30. package/dist/InternalPingRegistrar-DY3kSfxP.js +221 -0
  31. package/dist/InternalPingRegistrar-DY3kSfxP.js.map +1 -0
  32. package/dist/{ItemsInputNormalizer-D1WppVMU.d.ts → ItemsInputNormalizer-_RwIfRIQ.d.ts} +108 -25
  33. package/dist/{LogLevelPolicyFactory-CampWO0l.d.ts → LogLevelPolicyFactory-ewCHLDLn.d.ts} +2 -2
  34. package/dist/{PublicFrontendBootstrap-DzBgwOnG.d.ts → PublicFrontendBootstrap-Cev3qK46.d.ts} +9 -2
  35. package/dist/PublicFrontendBootstrapFactory-CY2FS-5g.d.ts +82 -0
  36. package/dist/{PublicFrontendBootstrapJsonCodec-Cl_DLRh0.d.ts → PublicFrontendBootstrapJsonCodec-CXG9Dxft.d.ts} +3 -3
  37. package/dist/{PublicFrontendBootstrapJsonCodec-DzqvA0uo.js → PublicFrontendBootstrapJsonCodec-CegIF_ne.js} +7 -2
  38. package/dist/PublicFrontendBootstrapJsonCodec-CegIF_ne.js.map +1 -0
  39. package/dist/ServerLoggerFactory-Ckk52S3w.js +223 -0
  40. package/dist/ServerLoggerFactory-Ckk52S3w.js.map +1 -0
  41. package/dist/{TelemetryContracts-BsOD_Y17.d.ts → TelemetryContracts-BtDx84Cp.d.ts} +13 -4
  42. package/dist/{WorkflowPolicyUiPresentationFactory-DNE5oAI6.d.ts → WorkflowPolicyUiPresentationFactory-6MyjCvBO.d.ts} +2 -2
  43. package/dist/{WorkflowPolicyUiPresentationFactory-DhPqQ9aB.js → WorkflowPolicyUiPresentationFactory-Bb-ae_Zh.js} +1 -1
  44. package/dist/{WorkflowPolicyUiPresentationFactory-DhPqQ9aB.js.map → WorkflowPolicyUiPresentationFactory-Bb-ae_Zh.js.map} +1 -1
  45. package/dist/{WorkflowViewContracts-0ZgsHQdp.d.ts → WorkflowViewContracts-B7aFQcIw.d.ts} +15 -1
  46. package/dist/authoring.d.ts +5 -5
  47. package/dist/authoring.js +1 -1
  48. package/dist/client.d.ts +4 -4
  49. package/dist/client.js +2 -2
  50. package/dist/consumer.d.ts +6 -6
  51. package/dist/consumer.js +2 -2
  52. package/dist/credentials.d.ts +6 -6
  53. package/dist/credentials.js +1 -1
  54. package/dist/devServerSidecar.d.ts +2 -2
  55. package/dist/devServerSidecar.js +1 -94
  56. package/dist/devServerSidecar.js.map +1 -1
  57. package/dist/dto.d.ts +6 -6
  58. package/dist/{index-BlGs9e9Q.d.ts → index-DilAYwnH.d.ts} +49 -3
  59. package/dist/index.d.ts +110 -21
  60. package/dist/index.js +15 -13
  61. package/dist/mapping.d.ts +2 -2
  62. package/dist/mapping.js +1 -1
  63. package/dist/nextServer.d.ts +43 -88
  64. package/dist/nextServer.js +9 -7
  65. package/dist/pairing.d.ts +93 -0
  66. package/dist/pairing.js +5 -0
  67. package/dist/pairing.types-snfZ_OzB.d.ts +19 -0
  68. package/dist/{persistenceServer-CpNFYa_q.js → persistenceServer-C-hH4z6l.js} +2 -2
  69. package/dist/{persistenceServer-CpNFYa_q.js.map → persistenceServer-C-hH4z6l.js.map} +1 -1
  70. package/dist/persistenceServer-CeTHtC6E.d.ts +30 -0
  71. package/dist/persistenceServer.d.ts +8 -8
  72. package/dist/persistenceServer.js +3 -3
  73. package/dist/{server-CQWdkT7t.d.ts → server-C4bS62rg.d.ts} +21 -6
  74. package/dist/{server-BK43OKxW.js → server-Y7kxwtCK.js} +7 -6
  75. package/dist/{server-BK43OKxW.js.map → server-Y7kxwtCK.js.map} +1 -1
  76. package/dist/server.d.ts +14 -14
  77. package/dist/server.js +13 -11
  78. package/package.json +29 -42
  79. package/prisma/migrations/20260507120000_execution_instance_child_run_id/migration.sql +5 -0
  80. package/prisma/migrations/20260519000000_workflow_audit_log/migration.sql +23 -0
  81. package/prisma/migrations/20260519100000_storage_growth_fixes/migration.sql +61 -0
  82. package/prisma/migrations.sqlite/20260507120000_execution_instance_child_run_id/migration.sql +5 -0
  83. package/prisma/migrations.sqlite/20260519000000_workflow_audit_log/migration.sql +21 -0
  84. package/prisma/migrations.sqlite/20260519100000_storage_growth_fixes/migration.sql +29 -0
  85. package/prisma/schema.postgresql.prisma +56 -17
  86. package/prisma/schema.sqlite.prisma +56 -17
  87. package/prisma-generated/prisma-postgresql-client/edge.js +35 -6
  88. package/prisma-generated/prisma-postgresql-client/index-browser.js +31 -2
  89. package/prisma-generated/prisma-postgresql-client/index.d.ts +8971 -5718
  90. package/prisma-generated/prisma-postgresql-client/index.js +35 -6
  91. package/prisma-generated/prisma-postgresql-client/package.json +1 -1
  92. package/prisma-generated/prisma-postgresql-client/schema.prisma +39 -0
  93. package/prisma-generated/prisma-sqlite-client/edge.js +35 -6
  94. package/prisma-generated/prisma-sqlite-client/index-browser.js +31 -2
  95. package/prisma-generated/prisma-sqlite-client/index.d.ts +8963 -5715
  96. package/prisma-generated/prisma-sqlite-client/index.js +35 -6
  97. package/prisma-generated/prisma-sqlite-client/package.json +1 -1
  98. package/prisma-generated/prisma-sqlite-client/schema.prisma +39 -0
  99. package/scripts/check-collections.mjs +18 -0
  100. package/scripts/generate-prisma-clients.mjs +20 -11
  101. package/src/application/WorkflowAuditLogPruneScheduler.ts +96 -0
  102. package/src/application/auth/AuthenticatedPrincipal.ts +4 -0
  103. package/src/application/commands/StartWorkflowRunCommandHandler.ts +4 -0
  104. package/src/application/contracts/WorkflowViewContracts.ts +11 -0
  105. package/src/application/contracts/WorkflowWebsocketMessage.ts +3 -1
  106. package/src/application/mapping/WorkflowDefinitionMapper.ts +44 -1
  107. package/src/application/runs/WorkflowRunRetentionPruneScheduler.ts +7 -1
  108. package/src/application/telemetry/OtelExecutionTelemetry.types.ts +5 -0
  109. package/src/application/telemetry/OtelExecutionTelemetryFactory.ts +4 -0
  110. package/src/application/telemetry/StoredTelemetrySpanScope.ts +6 -2
  111. package/src/application/telemetry/TelemetryRetentionTimestampFactory.ts +27 -17
  112. package/src/application/telemetry/TelemetrySpanPublisher.ts +11 -0
  113. package/src/application/websocket/TelemetrySpanWebsocketRelay.ts +31 -0
  114. package/src/applicationTokens.ts +20 -1
  115. package/src/audit/IAuditEmitter.ts +32 -0
  116. package/src/audit/PrismaWorkflowAuditLogRepository.ts +34 -0
  117. package/src/audit/WorkflowAuditLogWriter.ts +125 -0
  118. package/src/auth/managed/ManagedAuthConfig.ts +29 -0
  119. package/src/auth/managed/ManagedAuthMiddleware.ts +52 -0
  120. package/src/auth/managed/ManagedCorsMiddleware.ts +43 -0
  121. package/src/auth/managed/ManagedModeBootGuard.ts +27 -0
  122. package/src/auth/managed/index.ts +5 -0
  123. package/src/bootstrap/AppContainerFactory.ts +277 -29
  124. package/src/bootstrap/AppContainerLifecycle.ts +31 -0
  125. package/src/bootstrap/perf/BootTimer.ts +168 -0
  126. package/src/bootstrap/runtime/AppConfigFactory.ts +21 -65
  127. package/src/bootstrap/runtime/FrontendRuntime.ts +4 -1
  128. package/src/bootstrap/runtime/WorkerRuntime.ts +2 -1
  129. package/src/credentials/BrokerClient.ts +49 -0
  130. package/src/credentials/BrokerRefreshError.ts +12 -0
  131. package/src/credentials/BrokerRefreshInvalidGrantError.ts +13 -0
  132. package/src/credentials/ControlPlaneCatalogFetcher.ts +261 -0
  133. package/src/credentials/CredentialOAuth2MaterialReader.ts +136 -0
  134. package/src/credentials/InternalCredentialsListRegistrar.ts +48 -0
  135. package/src/credentials/InternalCredentialsPushRegistrar.ts +125 -0
  136. package/src/credentials/LocalOAuthFlowExecutor.ts +316 -0
  137. package/src/credentials/ManagedOAuthFlowExecutor.ts +94 -0
  138. package/src/credentials/ManagedOAuthRefreshInvalidGrantError.ts +13 -0
  139. package/src/credentials/catalogTypes.ts +4 -0
  140. package/src/credentials/refresh/CredentialDisconnectedError.ts +11 -0
  141. package/src/domain/credentials/CredentialBindingService.ts +54 -2
  142. package/src/domain/credentials/CredentialKeyRotatedError.ts +22 -0
  143. package/src/domain/credentials/CredentialSecretCipher.ts +68 -6
  144. package/src/domain/credentials/CredentialTypeRegistryImpl.ts +117 -10
  145. package/src/domain/credentials/OAuth2RedirectUriResolver.ts +79 -0
  146. package/src/domain/credentials/WorkflowCredentialNodeResolver.ts +14 -5
  147. package/src/domain/telemetry/TelemetryContracts.ts +7 -1
  148. package/src/domain/workflows/WorkflowActivationPreflight.ts +24 -1
  149. package/src/domain/workflows/WorkflowActivationPreflightRules.ts +40 -1
  150. package/src/index.ts +6 -0
  151. package/src/infrastructure/binary/LocalFilesystemBinaryStorageRegistry.ts +29 -1
  152. package/src/infrastructure/binary/S3BinaryStorage.ts +169 -0
  153. package/src/infrastructure/binary/S3BinaryStorageConfig.ts +17 -0
  154. package/src/infrastructure/config/CodemationPluginRegistrar.ts +3 -1
  155. package/src/infrastructure/persistence/CodemationDatabaseUrlParser.ts +41 -0
  156. package/src/infrastructure/persistence/InMemoryTelemetryArtifactStore.ts +8 -3
  157. package/src/infrastructure/persistence/InMemoryWorkflowRunRepository.ts +1 -0
  158. package/src/infrastructure/persistence/PrismaMigrationDeployer.ts +21 -13
  159. package/src/infrastructure/persistence/PrismaTelemetryArtifactStore.ts +43 -8
  160. package/src/infrastructure/persistence/PrismaWorkflowRunRepository.ts +33 -3
  161. package/src/infrastructure/persistence/PrismaWorkflowSnapshotRepository.ts +48 -0
  162. package/src/mcp/AgentMcpIntegrationImpl.ts +344 -0
  163. package/src/mcp/McpClientFactory.ts +29 -0
  164. package/src/mcp/McpConnectionPool.ts +184 -0
  165. package/src/mcp/McpConnectionPool.types.ts +12 -0
  166. package/src/mcp/McpServerCatalog.ts +104 -0
  167. package/src/mcp/index.ts +5 -0
  168. package/src/pairing/HmacRequestSigner.ts +32 -0
  169. package/src/pairing/IncomingHmacVerifier.ts +82 -0
  170. package/src/pairing/InternalHmacAuthMiddleware.ts +33 -0
  171. package/src/pairing/InternalPingRegistrar.ts +25 -0
  172. package/src/pairing/PairedFetch.ts +33 -0
  173. package/src/pairing/PairingConfigFactory.ts +35 -0
  174. package/src/pairing/PairingConfigToken.ts +6 -0
  175. package/src/pairing/index.ts +14 -0
  176. package/src/pairing/pairing.types.ts +18 -0
  177. package/src/pairing.ts +17 -0
  178. package/src/persistenceServer.ts +1 -0
  179. package/src/presentation/config/AppConfig.ts +7 -1
  180. package/src/presentation/config/CodemationAuthConfig.ts +1 -1
  181. package/src/presentation/config/CodemationAuthoring.types.ts +54 -5
  182. package/src/presentation/config/CodemationConfig.ts +3 -0
  183. package/src/presentation/config/CodemationConfigNormalizer.ts +39 -1
  184. package/src/presentation/config/CodemationPlugin.ts +2 -1
  185. package/src/presentation/frontend/CodemationFrontendAuthSnapshot.ts +5 -0
  186. package/src/presentation/frontend/CodemationFrontendAuthSnapshotFactory.ts +7 -1
  187. package/src/presentation/frontend/PublicFrontendBootstrap.ts +2 -0
  188. package/src/presentation/frontend/PublicFrontendBootstrapFactory.ts +5 -1
  189. package/src/presentation/frontend/PublicFrontendBootstrapJsonCodec.ts +4 -1
  190. package/src/presentation/http/ApiPaths.ts +4 -4
  191. package/src/presentation/http/ServerHttpErrorResponseFactory.ts +39 -2
  192. package/src/presentation/http/hono/CodemationHonoApiAppFactory.ts +33 -8
  193. package/src/presentation/http/hono/InternalHonoApiRouteRegistrar.ts +12 -0
  194. package/src/presentation/http/hono/registrars/ManagedMeHonoApiRouteRegistrar.ts +35 -0
  195. package/src/presentation/http/hono/registrars/OAuth2HonoApiRouteRegistrar.ts +2 -2
  196. package/src/presentation/http/routeHandlers/CredentialHttpRouteHandler.ts +28 -0
  197. package/src/presentation/http/routeHandlers/OAuth2HttpRouteHandlerFactory.ts +98 -41
  198. package/src/presentation/server/CodemationConsumerConfigLoader.ts +54 -7
  199. package/src/presentation/server/CodemationPluginDiscovery.ts +5 -0
  200. package/src/presentation/server/WorkflowDefinitionExportsResolver.ts +18 -0
  201. package/src/presentation/server/WorkflowModulePathFinder.ts +12 -1
  202. package/src/presentation/websocket/ManagedWebsocketAuthenticator.ts +50 -0
  203. package/src/presentation/websocket/WebsocketAuthenticator.types.ts +12 -0
  204. package/src/presentation/websocket/WorkflowWebsocketServer.ts +24 -3
  205. package/src/process/ExecaProcessRunner.ts +41 -0
  206. package/src/process/ProcessRunner.types.ts +39 -0
  207. package/src/server.ts +2 -0
  208. package/src/workflows/InternalWorkflowActivationRegistrar.ts +42 -0
  209. package/src/workflows/InternalWorkflowDetailRegistrar.ts +33 -0
  210. package/src/workflows/InternalWorkflowTestRunRegistrar.ts +91 -0
  211. package/src/workflows/InternalWorkflowsListRegistrar.ts +28 -0
  212. package/src/workflows/discovery/WorkflowDirectoryDiscoverer.ts +79 -0
  213. package/tsconfig.json +2 -0
  214. package/vitest.shared.ts +5 -0
  215. package/dist/ApiPaths-CLTHphYZ.js.map +0 -1
  216. package/dist/AppConfigFactory-CvpFScwB.js.map +0 -1
  217. package/dist/AppContainerFactory-BlLrm6_h.js.map +0 -1
  218. package/dist/CodemationAuthoring.types-DZl-sJaM.js.map +0 -1
  219. package/dist/CodemationConsumerConfigLoader-BeAUS144.js.map +0 -1
  220. package/dist/CredentialServices-CgxwguAv.js.map +0 -1
  221. package/dist/PublicFrontendBootstrapFactory-BMWqNM9a.d.ts +0 -45
  222. package/dist/PublicFrontendBootstrapJsonCodec-DzqvA0uo.js.map +0 -1
  223. package/dist/ServerLoggerFactory-BKSIh9Xv.js +0 -98
  224. package/dist/ServerLoggerFactory-BKSIh9Xv.js.map +0 -1
  225. package/dist/persistenceServer-CIVt3UOX.d.ts +0 -9
  226. package/src/domain/credentials/OAuth2ConnectServiceFactory.ts +0 -411
@@ -89,6 +89,8 @@ import { OpenAiApiKeyCredentialHealthTester } from "../infrastructure/credential
89
89
  import { OpenAiApiKeyCredentialTypeFactory } from "../infrastructure/credentials/OpenAiApiKeyCredentialTypeFactory";
90
90
  import { CodemationPluginRegistrar } from "../infrastructure/config/CodemationPluginRegistrar";
91
91
  import { WorkflowRunEventWebsocketRelay } from "../application/websocket/WorkflowRunEventWebsocketRelay";
92
+ import { TelemetrySpanWebsocketRelay } from "../application/websocket/TelemetrySpanWebsocketRelay";
93
+ import { NoOpTelemetrySpanPublisher } from "../application/telemetry/TelemetrySpanPublisher";
92
94
  import { FrameworkCostCatalogEntries } from "../application/cost/FrameworkCostCatalogEntries";
93
95
  import { CompositeTelemetryExporter } from "../application/telemetry/CompositeTelemetryExporter";
94
96
  import { LazyExecutionTelemetryFactory } from "../application/telemetry/LazyExecutionTelemetryFactory";
@@ -96,6 +98,8 @@ import { NoOpTelemetryExporter } from "../application/telemetry/NoOpTelemetryExp
96
98
  import { OtelExecutionTelemetryFactory } from "../application/telemetry/OtelExecutionTelemetryFactory";
97
99
  import { OtelIdentityFactory } from "../application/telemetry/OtelIdentityFactory";
98
100
  import { RunEventBusTelemetryReporter } from "../application/telemetry/RunEventBusTelemetryReporter";
101
+ import { WorkflowAuditLogWriter } from "../audit/WorkflowAuditLogWriter";
102
+ import { PrismaWorkflowAuditLogRepository } from "../audit/PrismaWorkflowAuditLogRepository";
99
103
  import { TelemetryEnricherChain } from "../application/telemetry/TelemetryEnricherChain";
100
104
  import { TelemetryPrivacyPolicy } from "../application/telemetry/TelemetryPrivacyPolicy";
101
105
  import { TelemetryQueryService } from "../application/telemetry/TelemetryQueryService";
@@ -113,8 +117,8 @@ import {
113
117
  CredentialTestService,
114
118
  CredentialTypeRegistryImpl,
115
119
  } from "../domain/credentials/CredentialServices";
116
- import { OAuth2ConnectService } from "../domain/credentials/OAuth2ConnectServiceFactory";
117
120
  import { OAuth2ProviderRegistry } from "../domain/credentials/OAuth2ProviderRegistry";
121
+ import { OAuth2RedirectUriResolver } from "../domain/credentials/OAuth2RedirectUriResolver";
118
122
  import { WorkflowCredentialNodeResolver } from "../domain/credentials/WorkflowCredentialNodeResolver";
119
123
  import { UserAccountService } from "../domain/users/UserAccountServiceRegistry";
120
124
  import { UserAccountSessionPolicy } from "../domain/users/UserAccountSessionPolicy";
@@ -228,19 +232,25 @@ import { PrismaTestSuiteRunRepository } from "../infrastructure/persistence/Pris
228
232
  import { PrismaWorkflowActivationRepository } from "../infrastructure/persistence/PrismaWorkflowActivationRepository";
229
233
  import { PrismaWorkflowDebuggerOverlayRepository } from "../infrastructure/persistence/PrismaWorkflowDebuggerOverlayRepository";
230
234
  import { PrismaWorkflowRunRepository } from "../infrastructure/persistence/PrismaWorkflowRunRepository";
235
+ import { PrismaWorkflowSnapshotRepository } from "../infrastructure/persistence/PrismaWorkflowSnapshotRepository";
231
236
  import { RuntimeWorkflowActivationPolicy } from "../infrastructure/persistence/RuntimeWorkflowActivationPolicy";
232
237
  import { WorkflowDefinitionRepositoryAdapter } from "../infrastructure/persistence/WorkflowDefinitionRepositoryAdapter";
233
238
  import { WorkflowRunRepository as SqlWorkflowRunRepository } from "../infrastructure/persistence/WorkflowRunRepository";
234
239
  import { LiveWorkflowRepository } from "../infrastructure/runtime/LiveWorkflowRepository";
235
240
  import { LogLevelPolicyFactory, logLevelPolicyFactory } from "../infrastructure/logging/LogLevelPolicyFactory";
236
241
  import { ServerLoggerFactory } from "../infrastructure/logging/ServerLoggerFactory";
242
+ import { ExecaProcessRunner } from "../process/ExecaProcessRunner";
237
243
  import type { AppConfig } from "../presentation/config/AppConfig";
238
244
  import { CodemationContainerRegistrationRegistrar } from "./CodemationContainerRegistrationRegistrar";
239
245
  import { LocalFilesystemBinaryStorage } from "../infrastructure/binary/LocalFilesystemBinaryStorageRegistry";
246
+ import { S3BinaryStorage } from "../infrastructure/binary/S3BinaryStorage";
247
+ import { S3BinaryStorageConfigSchema } from "../infrastructure/binary/S3BinaryStorageConfig";
240
248
  import { InMemoryBinaryStorage } from "@codemation/core/bootstrap";
241
249
  import { RedisRunEventBus } from "@codemation/eventbus-redis";
242
250
  import { AppContainerLifecycle } from "./AppContainerLifecycle";
251
+ import { BootTimer } from "./perf/BootTimer";
243
252
  import { WorkflowRunRetentionPruneScheduler } from "../application/runs/WorkflowRunRetentionPruneScheduler";
253
+ import { WorkflowAuditLogPruneScheduler } from "../application/WorkflowAuditLogPruneScheduler";
244
254
  import { DatabaseMigrations } from "./runtime/DatabaseMigrations";
245
255
  import { FrontendRuntime } from "./runtime/FrontendRuntime";
246
256
  import { WorkerRuntime } from "./runtime/WorkerRuntime";
@@ -261,6 +271,36 @@ import { ListCollectionRowsQueryHandler } from "../application/collections/ListC
261
271
  import { ListCollectionsQueryHandler } from "../application/collections/ListCollectionsQueryHandler";
262
272
  import { CollectionHttpRouteHandler } from "../presentation/http/routeHandlers/CollectionHttpRouteHandlerFactory";
263
273
  import { CollectionHonoApiRouteRegistrar } from "../presentation/http/hono/registrars/CollectionHonoApiRouteRegistrar";
274
+ import { ManagedMeHonoApiRouteRegistrar } from "../presentation/http/hono/registrars/ManagedMeHonoApiRouteRegistrar";
275
+ import { PairingConfigFactory } from "../pairing/PairingConfigFactory";
276
+ import { PairingConfigToken } from "../pairing/PairingConfigToken";
277
+ import { HmacRequestSigner } from "../pairing/HmacRequestSigner";
278
+ import { PairedFetch } from "../pairing/PairedFetch";
279
+ import { IncomingHmacVerifier } from "../pairing/IncomingHmacVerifier";
280
+ import { InternalHmacAuthMiddleware } from "../pairing/InternalHmacAuthMiddleware";
281
+ import { InternalPingRegistrar } from "../pairing/InternalPingRegistrar";
282
+ import { LocalOAuthFlowExecutor } from "../credentials/LocalOAuthFlowExecutor";
283
+ import { CredentialOAuth2MaterialReader } from "../credentials/CredentialOAuth2MaterialReader";
284
+ import { ManagedOAuthFlowExecutor } from "../credentials/ManagedOAuthFlowExecutor";
285
+ import { BrokerClient } from "../credentials/BrokerClient";
286
+ import { InternalCredentialsPushRegistrar } from "../credentials/InternalCredentialsPushRegistrar";
287
+ import { InternalCredentialsListRegistrar } from "../credentials/InternalCredentialsListRegistrar";
288
+ import { InternalWorkflowsListRegistrar } from "../workflows/InternalWorkflowsListRegistrar";
289
+ import { InternalWorkflowDetailRegistrar } from "../workflows/InternalWorkflowDetailRegistrar";
290
+ import { InternalWorkflowActivationRegistrar } from "../workflows/InternalWorkflowActivationRegistrar";
291
+ import { InternalWorkflowTestRunRegistrar } from "../workflows/InternalWorkflowTestRunRegistrar";
292
+ import { McpServerCatalog } from "../mcp/McpServerCatalog";
293
+ import { McpConnectionPool } from "../mcp/McpConnectionPool";
294
+ import { DefaultMcpClientFactory } from "../mcp/McpClientFactory";
295
+ import { AgentMcpIntegrationImpl } from "../mcp/AgentMcpIntegrationImpl";
296
+ import { ManagedAuthConfigFactory } from "../auth/managed/ManagedAuthConfig";
297
+ import { ManagedAuthMiddleware } from "../auth/managed/ManagedAuthMiddleware";
298
+ import { ManagedCorsMiddleware } from "../auth/managed/ManagedCorsMiddleware";
299
+ import { ManagedModeBootGuard } from "../auth/managed/ManagedModeBootGuard";
300
+ import { ManagedWebsocketAuthenticator } from "../presentation/websocket/ManagedWebsocketAuthenticator";
301
+ import { JwksCache, ManagedJwtVerifier } from "@codemation/managed-auth";
302
+ import { CodemationTsyringeTypeInfoRegistrar } from "../presentation/server/CodemationTsyringeTypeInfoRegistrar";
303
+ import { ControlPlaneCatalogFetcher } from "../credentials/ControlPlaneCatalogFetcher";
264
304
 
265
305
  type AppContainerInputs = Readonly<{
266
306
  appConfig: AppConfig;
@@ -350,22 +390,61 @@ export class AppContainerFactory {
350
390
  async create(inputs: AppContainerInputs): Promise<Container> {
351
391
  const container = tsyringeContainer.createChildContainer();
352
392
  container.registerInstance(ApplicationTokens.AppConfig, inputs.appConfig);
353
- this.registerCoreInfrastructure(container, inputs);
354
- this.registerRepositoriesAndBuses(container);
355
- this.registerApplicationServicesAndRoutes(container);
356
- this.registerOperationalInfrastructure(container);
357
- this.registerConfiguredRegistrations(container, inputs.appConfig);
358
- const credentialTypes = this.collectCredentialTypes(inputs.appConfig);
359
- await this.applyPlugins(container, inputs.appConfig, credentialTypes);
360
- const ownership = await this.registerRuntimeInfrastructure(container, inputs.appConfig);
361
- this.registerCollectionsInfrastructure(container, inputs.appConfig);
362
- this.registerCredentialTypes(container, credentialTypes);
363
- this.synchronizeLiveWorkflowRepository(container, inputs.appConfig.workflows);
364
- container.resolve(BootRuntimeSnapshotHolder).set(this.createRuntimeSummary(inputs.appConfig));
365
- container.registerInstance(
366
- AppContainerLifecycle,
367
- new AppContainerLifecycle(container, ownership.ownedPrismaClient),
393
+ // Register the no-op publisher as a fallback so OtelExecutionTelemetryFactory can always
394
+ // resolve the token. registerOperationalInfrastructure overrides this with the WS relay.
395
+ container.registerInstance(ApplicationTokens.TelemetrySpanPublisher, NoOpTelemetrySpanPublisher);
396
+ BootTimer.measure("appContainer.registerCoreInfrastructure", () => this.registerCoreInfrastructure(container, inputs));
397
+ BootTimer.measure("appContainer.registerRepositoriesAndBuses", () => this.registerRepositoriesAndBuses(container));
398
+ BootTimer.measure("appContainer.registerApplicationServicesAndRoutes", () =>
399
+ this.registerApplicationServicesAndRoutes(container, inputs.appConfig),
400
+ );
401
+ BootTimer.measure("appContainer.registerOperationalInfrastructure", () =>
402
+ this.registerOperationalInfrastructure(container),
403
+ );
404
+ BootTimer.measure("appContainer.registerManagedAuthInfrastructure", () =>
405
+ this.registerManagedAuthInfrastructure(container, inputs.appConfig),
406
+ );
407
+ BootTimer.measure("appContainer.registerPairingInfrastructure", () =>
408
+ this.registerPairingInfrastructure(container, inputs.appConfig),
409
+ );
410
+ BootTimer.measure("appContainer.registerConfiguredRegistrations", () =>
411
+ this.registerConfiguredRegistrations(container, inputs.appConfig),
412
+ );
413
+ const credentialTypes = BootTimer.measure("appContainer.collectCredentialTypes", () =>
414
+ this.collectCredentialTypes(inputs.appConfig),
415
+ );
416
+ BootTimer.measure("appContainer.registerMcpCatalog", () => this.registerMcpCatalog(container));
417
+ await BootTimer.measureAsync("appContainer.applyPlugins", () =>
418
+ this.applyPlugins(container, inputs.appConfig, credentialTypes),
419
+ );
420
+ BootTimer.measure("appContainer.mergeConfigMcpServers", () =>
421
+ this.mergeConfigMcpServers(container, inputs.appConfig),
368
422
  );
423
+ const ownership = await BootTimer.measureAsync("appContainer.registerRuntimeInfrastructure", () =>
424
+ this.registerRuntimeInfrastructure(container, inputs.appConfig),
425
+ );
426
+ BootTimer.measure("appContainer.registerWorkflowAuditWriter", () =>
427
+ this.registerWorkflowAuditWriter(container, inputs.appConfig),
428
+ );
429
+ BootTimer.measure("appContainer.registerCollectionsInfrastructure", () =>
430
+ this.registerCollectionsInfrastructure(container, inputs.appConfig),
431
+ );
432
+ BootTimer.measure("appContainer.registerCredentialTypes", () =>
433
+ this.registerCredentialTypes(container, credentialTypes),
434
+ );
435
+ BootTimer.measure("appContainer.registerControlPlaneCatalogFetcher", () =>
436
+ this.registerControlPlaneCatalogFetcher(container),
437
+ );
438
+ BootTimer.measure("appContainer.synchronizeLiveWorkflowRepository", () =>
439
+ this.synchronizeLiveWorkflowRepository(container, inputs.appConfig.workflows),
440
+ );
441
+ BootTimer.measure("appContainer.registerWorkflowDefinitions", () =>
442
+ new CodemationTsyringeTypeInfoRegistrar(container).registerWorkflowDefinitions(inputs.appConfig.workflows ?? []),
443
+ );
444
+ container.resolve(BootRuntimeSnapshotHolder).set(this.createRuntimeSummary(inputs.appConfig));
445
+ const lifecycle = new AppContainerLifecycle(container, ownership.ownedPrismaClient);
446
+ container.registerInstance(AppContainerLifecycle, lifecycle);
447
+ await BootTimer.measureAsync("appContainer.lifecycleStart", () => lifecycle.start());
369
448
  return container;
370
449
  }
371
450
 
@@ -391,11 +470,28 @@ export class AppContainerFactory {
391
470
  return credentialTypes;
392
471
  }
393
472
 
473
+ private registerMcpCatalog(container: Container): void {
474
+ container.registerSingleton(McpServerCatalog, McpServerCatalog);
475
+ container.registerSingleton(DefaultMcpClientFactory, DefaultMcpClientFactory);
476
+ container.registerSingleton(McpConnectionPool, McpConnectionPool);
477
+ container.registerSingleton(AgentMcpIntegrationImpl, AgentMcpIntegrationImpl);
478
+ // Register the host-side AgentMcpIntegration, overriding the NoOp registered by EngineRuntimeRegistrar.
479
+ container.register(CoreTokens.AgentMcpIntegration, {
480
+ useFactory: instanceCachingFactory((c) => c.resolve(AgentMcpIntegrationImpl)),
481
+ });
482
+ }
483
+
484
+ private mergeConfigMcpServers(container: Container, appConfig: AppConfig): void {
485
+ const catalog = container.resolve(McpServerCatalog);
486
+ catalog.merge("config", appConfig.mcpServers ?? []);
487
+ }
488
+
394
489
  private async applyPlugins(
395
490
  container: Container,
396
491
  appConfig: AppConfig,
397
492
  credentialTypes: Array<CredentialType<any, any, unknown>>,
398
493
  ): Promise<void> {
494
+ const catalog = container.resolve(McpServerCatalog);
399
495
  await this.pluginRegistrar.apply({
400
496
  plugins: appConfig.plugins,
401
497
  container,
@@ -411,6 +507,7 @@ export class AppContainerFactory {
411
507
  // during plugin.register() are accepted silently — Phase 6 store wiring will
412
508
  // pick them up once collection infrastructure can reload from a mutable registry.
413
509
  },
510
+ mergeMcpServers: (declarations) => catalog.merge("plugin", declarations),
414
511
  loggerFactory: container.resolve(ApplicationTokens.LoggerFactory),
415
512
  });
416
513
  }
@@ -420,9 +517,27 @@ export class AppContainerFactory {
420
517
  credentialTypes: ReadonlyArray<CredentialType<any, any, unknown>>,
421
518
  ): void {
422
519
  const registry = container.resolve(CredentialTypeRegistryImpl);
423
- for (const credentialType of credentialTypes) {
424
- registry.register(credentialType);
520
+ registry.merge("plugin", credentialTypes);
521
+ }
522
+
523
+ private registerControlPlaneCatalogFetcher(container: Container): void {
524
+ // Only register the fetcher when paired with a control plane.
525
+ // PairedFetch is only registered inside registerPairingInfrastructure,
526
+ // which skips registration when pairing env vars are absent.
527
+ if (!container.isRegistered(PairedFetch, true)) {
528
+ return;
425
529
  }
530
+
531
+ container.registerSingleton(ControlPlaneCatalogFetcher, ControlPlaneCatalogFetcher);
532
+
533
+ const fetcher = container.resolve(ControlPlaneCatalogFetcher);
534
+ const credentialTypeRegistry = container.resolve(CredentialTypeRegistryImpl);
535
+ const mcpServerCatalog = container.resolve(McpServerCatalog);
536
+
537
+ fetcher.onRefresh = () => {
538
+ credentialTypeRegistry.mergeDefinitions("controlPlane", fetcher.credentialTypeOverrides ?? []);
539
+ mcpServerCatalog.merge("controlPlane", fetcher.mcpServers ?? []);
540
+ };
426
541
  }
427
542
 
428
543
  private registerConfiguredRegistrations(container: Container, appConfig: AppConfig): void {
@@ -499,9 +614,7 @@ export class AppContainerFactory {
499
614
  CoreTokens.PersistedWorkflowTokenRegistry,
500
615
  container.resolve(PersistedWorkflowTokenRegistry),
501
616
  );
502
- container.register(CredentialTypeRegistryImpl, {
503
- useFactory: instanceCachingFactory(() => new CredentialTypeRegistryImpl()),
504
- });
617
+ container.registerSingleton(CredentialTypeRegistryImpl, CredentialTypeRegistryImpl);
505
618
  container.register(CoreTokens.CredentialTypeRegistry, {
506
619
  useFactory: instanceCachingFactory((dependencyContainer) =>
507
620
  dependencyContainer.resolve(CredentialTypeRegistryImpl),
@@ -516,9 +629,11 @@ export class AppContainerFactory {
516
629
  });
517
630
  container.register(CoreTokens.LiveWorkflowRepository, {
518
631
  useFactory: instanceCachingFactory(
519
- () =>
632
+ (dependencyContainer) =>
520
633
  new LiveWorkflowRepository(
521
- new AIAgentConnectionWorkflowExpander(new ConnectionCredentialNodeConfigFactory()),
634
+ new AIAgentConnectionWorkflowExpander(new ConnectionCredentialNodeConfigFactory(), (id) =>
635
+ dependencyContainer.resolve(McpServerCatalog).get(id),
636
+ ),
522
637
  ),
523
638
  ),
524
639
  });
@@ -563,6 +678,7 @@ export class AppContainerFactory {
563
678
  });
564
679
  container.registerInstance(LogLevelPolicyFactory, logLevelPolicyFactory);
565
680
  container.registerSingleton(ServerLoggerFactory, ServerLoggerFactory);
681
+ container.registerInstance(ApplicationTokens.ProcessRunner, new ExecaProcessRunner());
566
682
  container.register(ApplicationTokens.LoggerFactory, {
567
683
  useFactory: instanceCachingFactory((dependencyContainer) => dependencyContainer.resolve(ServerLoggerFactory)),
568
684
  });
@@ -576,10 +692,14 @@ export class AppContainerFactory {
576
692
  if (inputs.sharedWorkflowWebsocketServer) {
577
693
  return inputs.sharedWorkflowWebsocketServer;
578
694
  }
695
+ const authenticator = dependencyContainer.isRegistered(ApplicationTokens.WebsocketAuthenticator, true)
696
+ ? dependencyContainer.resolve(ApplicationTokens.WebsocketAuthenticator)
697
+ : null;
579
698
  return new WorkflowWebsocketServer(
580
699
  dependencyContainer.resolve(ApplicationTokens.WebSocketPort),
581
700
  dependencyContainer.resolve(ApplicationTokens.WebSocketBindHost),
582
701
  dependencyContainer.resolve(ServerLoggerFactory).create("codemation-websocket.server"),
702
+ authenticator,
583
703
  );
584
704
  }),
585
705
  });
@@ -605,7 +725,19 @@ export class AppContainerFactory {
605
725
  container.register(CoreTokens.CredentialSessionService, { useToken: CredentialSessionServiceImpl });
606
726
  }
607
727
  container.registerSingleton(OAuth2ProviderRegistry, OAuth2ProviderRegistry);
608
- container.registerSingleton(OAuth2ConnectService, OAuth2ConnectService);
728
+ container.registerSingleton(OAuth2RedirectUriResolver, OAuth2RedirectUriResolver);
729
+ if (container.isRegistered(PairedFetch, true)) {
730
+ container.register(ApplicationTokens.OAuthFlowExecutor, {
731
+ useFactory: instanceCachingFactory((c) => c.resolve(ManagedOAuthFlowExecutor)),
732
+ });
733
+ container.registerSingleton(ManagedOAuthFlowExecutor, ManagedOAuthFlowExecutor);
734
+ } else {
735
+ container.register(ApplicationTokens.OAuthFlowExecutor, {
736
+ useFactory: instanceCachingFactory((c) => c.resolve(LocalOAuthFlowExecutor)),
737
+ });
738
+ container.registerSingleton(LocalOAuthFlowExecutor, LocalOAuthFlowExecutor);
739
+ }
740
+ container.registerSingleton(CredentialOAuth2MaterialReader, CredentialOAuth2MaterialReader);
609
741
  container.registerSingleton(CodemationFrontendAuthSnapshotFactory, CodemationFrontendAuthSnapshotFactory);
610
742
  container.registerSingleton(FrontendAppConfigFactory, FrontendAppConfigFactory);
611
743
  container.registerSingleton(PublicFrontendBootstrapFactory, PublicFrontendBootstrapFactory);
@@ -723,6 +855,7 @@ export class AppContainerFactory {
723
855
  container.registerSingleton(InMemoryTelemetryArtifactStore, InMemoryTelemetryArtifactStore);
724
856
  container.registerSingleton(InMemoryTelemetryMetricPointStore, InMemoryTelemetryMetricPointStore);
725
857
  container.registerSingleton(PrismaRunTraceContextRepository, PrismaRunTraceContextRepository);
858
+ container.registerSingleton(PrismaWorkflowSnapshotRepository, PrismaWorkflowSnapshotRepository);
726
859
  container.registerSingleton(PrismaTelemetrySpanStore, PrismaTelemetrySpanStore);
727
860
  container.registerSingleton(PrismaTelemetryArtifactStore, PrismaTelemetryArtifactStore);
728
861
  container.registerSingleton(PrismaTelemetryMetricPointStore, PrismaTelemetryMetricPointStore);
@@ -770,7 +903,7 @@ export class AppContainerFactory {
770
903
  });
771
904
  }
772
905
 
773
- private registerApplicationServicesAndRoutes(container: Container): void {
906
+ private registerApplicationServicesAndRoutes(container: Container, appConfig: AppConfig): void {
774
907
  container.registerSingleton(DevBootstrapSummaryAssembler, DevBootstrapSummaryAssembler);
775
908
  container.registerSingleton(DevBootstrapSummaryHttpRouteHandler, DevBootstrapSummaryHttpRouteHandler);
776
909
  container.registerSingleton(AuthHttpRouteHandler, AuthHttpRouteHandler);
@@ -808,21 +941,110 @@ export class AppContainerFactory {
808
941
  container.registerSingleton(TestRunnerService, TestRunnerService);
809
942
  container.registerSingleton(TestSuiteHttpRouteHandler, TestSuiteHttpRouteHandler);
810
943
  container.registerSingleton(CollectionHttpRouteHandler, CollectionHttpRouteHandler);
944
+ const isManagedMode = appConfig.auth?.kind === "managed";
811
945
  for (const registrar of AppContainerFactory.honoRouteRegistrars) {
946
+ // In managed mode, skip the Better Auth route registrar so /api/auth/* returns 404
947
+ if (isManagedMode && registrar === AuthHonoApiRouteRegistrar) {
948
+ continue;
949
+ }
812
950
  container.registerSingleton(ApplicationTokens.HonoApiRouteRegistrar, registrar);
813
951
  }
814
952
  container.registerSingleton(CodemationHonoApiApp, CodemationHonoApiApp);
815
953
  }
816
954
 
955
+ private registerManagedAuthInfrastructure(container: Container, appConfig: AppConfig): void {
956
+ if (appConfig.auth?.kind !== "managed") {
957
+ return;
958
+ }
959
+
960
+ new ManagedModeBootGuard().assertRequiredEnv(appConfig.env);
961
+ const managedAuthConfig = new ManagedAuthConfigFactory().create(appConfig.env);
962
+ const workspaceId = appConfig.env["WORKSPACE_ID"] ?? "";
963
+
964
+ const jwksCache = new JwksCache({ jwksUrl: managedAuthConfig.jwksUrl }, (url) => fetch(url), {
965
+ now: () => Date.now(),
966
+ });
967
+ // Shared verifier: both ManagedAuthMiddleware (HTTP) and ManagedWebsocketAuthenticator (WS)
968
+ // use the same JwksCache instance so JWKS key rotation propagates to both transports.
969
+ const jwtVerifier = new ManagedJwtVerifier(
970
+ {
971
+ expectedIssuer: managedAuthConfig.issuer,
972
+ expectedAudience: workspaceId,
973
+ jwksCache: { jwksUrl: managedAuthConfig.jwksUrl },
974
+ },
975
+ jwksCache,
976
+ );
977
+
978
+ const managedAuthMiddleware = new ManagedAuthMiddleware(jwtVerifier);
979
+ container.register(ApplicationTokens.SessionVerifier, { useValue: managedAuthMiddleware });
980
+
981
+ const websocketAuthenticator = new ManagedWebsocketAuthenticator(jwtVerifier);
982
+ container.registerInstance(ApplicationTokens.WebsocketAuthenticator, websocketAuthenticator);
983
+
984
+ const corsMiddleware = new ManagedCorsMiddleware(managedAuthConfig.cpWebOrigin);
985
+ container.registerInstance(ApplicationTokens.ManagedCorsMiddleware, corsMiddleware);
986
+
987
+ container.registerSingleton(ApplicationTokens.HonoApiRouteRegistrar, ManagedMeHonoApiRouteRegistrar);
988
+ }
989
+
990
+ private registerPairingInfrastructure(container: Container, appConfig: AppConfig): void {
991
+ const pairingConfig = new PairingConfigFactory().create(appConfig.env);
992
+ if (!pairingConfig) {
993
+ // Pairing is optional in non-production environments (local dev without CP integration).
994
+ // Emit a startup warning so operators know the workspace-mcp HMAC channel is inactive.
995
+ const missingVars: string[] = [];
996
+ if (!appConfig.env["WORKSPACE_ID"]) missingVars.push("WORKSPACE_ID");
997
+ if (!appConfig.env["WORKSPACE_PAIRING_SECRET"]) missingVars.push("WORKSPACE_PAIRING_SECRET");
998
+ if (!appConfig.env["CONTROL_PLANE_URL"]) missingVars.push("CONTROL_PLANE_URL");
999
+ if (missingVars.length > 0) {
1000
+ const logger = container.resolve(ServerLoggerFactory).create("codemation.pairing");
1001
+ logger.warn(
1002
+ `Pairing not configured — missing env vars: ${missingVars.join(", ")}. ` +
1003
+ "Internal /internal/* routes are inactive. Set these vars for managed-mode integration.",
1004
+ );
1005
+ }
1006
+ return;
1007
+ }
1008
+ container.registerInstance(PairingConfigToken, pairingConfig);
1009
+ container.registerSingleton(HmacRequestSigner, HmacRequestSigner);
1010
+ container.registerSingleton(PairedFetch, PairedFetch);
1011
+ container.registerSingleton(IncomingHmacVerifier, IncomingHmacVerifier);
1012
+ container.registerSingleton(InternalHmacAuthMiddleware, InternalHmacAuthMiddleware);
1013
+ container.registerSingleton(BrokerClient, BrokerClient);
1014
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalPingRegistrar);
1015
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalCredentialsPushRegistrar);
1016
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalCredentialsListRegistrar);
1017
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalWorkflowsListRegistrar);
1018
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalWorkflowDetailRegistrar);
1019
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalWorkflowActivationRegistrar);
1020
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalWorkflowTestRunRegistrar);
1021
+ }
1022
+
817
1023
  private registerOperationalInfrastructure(container: Container): void {
818
1024
  container.register(ApplicationTokens.WorkflowWebsocketPublisher, {
819
1025
  useFactory: instanceCachingFactory((dependencyContainer) => dependencyContainer.resolve(WorkflowWebsocketServer)),
820
1026
  });
821
1027
  container.registerSingleton(WorkflowRunEventWebsocketRelay, WorkflowRunEventWebsocketRelay);
1028
+ container.registerSingleton(TelemetrySpanWebsocketRelay, TelemetrySpanWebsocketRelay);
1029
+ container.register(ApplicationTokens.TelemetrySpanPublisher, {
1030
+ useFactory: instanceCachingFactory((dependencyContainer) =>
1031
+ dependencyContainer.resolve(TelemetrySpanWebsocketRelay),
1032
+ ),
1033
+ });
822
1034
  container.registerSingleton(RunEventBusTelemetryReporter, RunEventBusTelemetryReporter);
823
1035
  container.registerSingleton(WorkflowRunRetentionPruneScheduler, WorkflowRunRetentionPruneScheduler);
824
1036
  }
825
1037
 
1038
+ private registerWorkflowAuditWriter(container: Container, appConfig: AppConfig): void {
1039
+ if (appConfig.persistence.kind === "none") {
1040
+ return;
1041
+ }
1042
+ container.registerSingleton(PrismaWorkflowAuditLogRepository, PrismaWorkflowAuditLogRepository);
1043
+ container.register(ApplicationTokens.WorkflowAuditEmitter, { useToken: PrismaWorkflowAuditLogRepository });
1044
+ container.registerSingleton(WorkflowAuditLogWriter, WorkflowAuditLogWriter);
1045
+ container.registerSingleton(WorkflowAuditLogPruneScheduler, WorkflowAuditLogPruneScheduler);
1046
+ }
1047
+
826
1048
  private async registerRuntimeInfrastructure(container: Container, appConfig: AppConfig): Promise<PrismaOwnership> {
827
1049
  const queuePrefix = appConfig.scheduler.queuePrefix ?? appConfig.eventing.queuePrefix ?? "codemation";
828
1050
  const eventBus =
@@ -830,7 +1052,7 @@ export class AppContainerFactory {
830
1052
  ? new RedisRunEventBus(this.requireRedisUrl(appConfig.eventing.redisUrl), queuePrefix)
831
1053
  : new InMemoryRunEventBus();
832
1054
  container.registerInstance(CoreTokens.RunEventBus, eventBus);
833
- const binaryStorage = this.createBinaryStorage(appConfig.repoRoot);
1055
+ const binaryStorage = await this.createBinaryStorage(appConfig);
834
1056
  container.registerInstance(CoreTokens.RunDataFactory, new InMemoryRunDataFactory());
835
1057
  container.registerInstance(CoreTokens.BinaryStorage, binaryStorage);
836
1058
  container.registerInstance(ApplicationTokens.Clock, new SystemClock());
@@ -1005,11 +1227,37 @@ export class AppContainerFactory {
1005
1227
  };
1006
1228
  }
1007
1229
 
1008
- private createBinaryStorage(repoRoot: string): InMemoryBinaryStorage | LocalFilesystemBinaryStorage {
1009
- if (!repoRoot) {
1230
+ private async createBinaryStorage(
1231
+ appConfig: AppConfig,
1232
+ ): Promise<InMemoryBinaryStorage | LocalFilesystemBinaryStorage | S3BinaryStorage> {
1233
+ const kind = appConfig.env.BINARY_STORAGE_KIND ?? "local";
1234
+
1235
+ if (kind === "s3") {
1236
+ const parseResult = S3BinaryStorageConfigSchema.safeParse({
1237
+ endpoint: appConfig.env.BINARY_STORAGE_S3_ENDPOINT,
1238
+ region: appConfig.env.BINARY_STORAGE_S3_REGION,
1239
+ bucket: appConfig.env.BINARY_STORAGE_S3_BUCKET,
1240
+ accessKeyId: appConfig.env.BINARY_STORAGE_S3_ACCESS_KEY_ID,
1241
+ secretAccessKey: appConfig.env.BINARY_STORAGE_S3_SECRET_ACCESS_KEY,
1242
+ });
1243
+ if (!parseResult.success) {
1244
+ throw new Error(
1245
+ `BINARY_STORAGE_KIND=s3 requires all BINARY_STORAGE_S3_* env vars. Validation errors: ${parseResult.error.message}`,
1246
+ );
1247
+ }
1248
+ const storage = new S3BinaryStorage(parseResult.data);
1249
+ await storage.checkConnectivity();
1250
+ return storage;
1251
+ }
1252
+
1253
+ if (kind !== "local") {
1254
+ throw new Error(`Unknown BINARY_STORAGE_KIND: "${kind}". Expected "local" or "s3".`);
1255
+ }
1256
+
1257
+ if (!appConfig.repoRoot) {
1010
1258
  return new InMemoryBinaryStorage();
1011
1259
  }
1012
- return new LocalFilesystemBinaryStorage(`${repoRoot}/.codemation/binary`);
1260
+ return new LocalFilesystemBinaryStorage(`${appConfig.repoRoot}/.codemation/binary`);
1013
1261
  }
1014
1262
 
1015
1263
  private createRuntimeSummary(appConfig: AppConfig): BootRuntimeSummary {
@@ -2,9 +2,13 @@ import type { Container } from "@codemation/core";
2
2
  import { Engine } from "@codemation/core/bootstrap";
3
3
  import { RunEventBusTelemetryReporter } from "../application/telemetry/RunEventBusTelemetryReporter";
4
4
  import { WorkflowRunRetentionPruneScheduler } from "../application/runs/WorkflowRunRetentionPruneScheduler";
5
+ import { WorkflowAuditLogPruneScheduler } from "../application/WorkflowAuditLogPruneScheduler";
5
6
  import type { PrismaDatabaseClient } from "../infrastructure/persistence/PrismaDatabaseClient";
6
7
  import { WorkflowRunEventWebsocketRelay } from "../application/websocket/WorkflowRunEventWebsocketRelay";
7
8
  import { WorkflowWebsocketServer } from "../presentation/websocket/WorkflowWebsocketServer";
9
+ import { McpConnectionPool } from "../mcp/McpConnectionPool";
10
+ import { WorkflowAuditLogWriter } from "../audit/WorkflowAuditLogWriter";
11
+ import { ControlPlaneCatalogFetcher } from "../credentials/ControlPlaneCatalogFetcher";
8
12
 
9
13
  export class AppContainerLifecycle {
10
14
  constructor(
@@ -12,6 +16,21 @@ export class AppContainerLifecycle {
12
16
  private readonly ownedPrismaClient: PrismaDatabaseClient | null,
13
17
  ) {}
14
18
 
19
+ async start(): Promise<void> {
20
+ if (this.container.isRegistered(ControlPlaneCatalogFetcher, true)) {
21
+ await this.container.resolve(ControlPlaneCatalogFetcher).start();
22
+ }
23
+ }
24
+
25
+ async startWorkerSubscribers(): Promise<void> {
26
+ if (this.container.isRegistered(WorkflowAuditLogWriter, true)) {
27
+ await this.container.resolve(WorkflowAuditLogWriter).start();
28
+ }
29
+ if (this.container.isRegistered(WorkflowAuditLogPruneScheduler, true)) {
30
+ this.container.resolve(WorkflowAuditLogPruneScheduler).start();
31
+ }
32
+ }
33
+
15
34
  async stop(args?: Readonly<{ stopWebsocketServer?: boolean }>): Promise<void> {
16
35
  if (this.container.isRegistered(Engine, true)) {
17
36
  await this.container.resolve(Engine).stop();
@@ -22,12 +41,24 @@ export class AppContainerLifecycle {
22
41
  if (this.container.isRegistered(RunEventBusTelemetryReporter, true)) {
23
42
  await this.container.resolve(RunEventBusTelemetryReporter).stop();
24
43
  }
44
+ if (this.container.isRegistered(WorkflowAuditLogWriter, true)) {
45
+ await this.container.resolve(WorkflowAuditLogWriter).stop();
46
+ }
25
47
  if (this.container.isRegistered(WorkflowRunRetentionPruneScheduler, true)) {
26
48
  this.container.resolve(WorkflowRunRetentionPruneScheduler).stop();
27
49
  }
50
+ if (this.container.isRegistered(WorkflowAuditLogPruneScheduler, true)) {
51
+ this.container.resolve(WorkflowAuditLogPruneScheduler).stop();
52
+ }
28
53
  if (args?.stopWebsocketServer !== false && this.container.isRegistered(WorkflowWebsocketServer, true)) {
29
54
  await this.container.resolve(WorkflowWebsocketServer).stop();
30
55
  }
56
+ if (this.container.isRegistered(McpConnectionPool, true)) {
57
+ await this.container.resolve(McpConnectionPool).closeAll();
58
+ }
59
+ if (this.container.isRegistered(ControlPlaneCatalogFetcher, true)) {
60
+ await this.container.resolve(ControlPlaneCatalogFetcher).stop();
61
+ }
31
62
  if (this.ownedPrismaClient) {
32
63
  await this.ownedPrismaClient.$disconnect();
33
64
  }