@codemation/host 0.6.0 → 0.8.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 +483 -0
  2. package/dist/{ApiPaths-CLTHphYZ.js → ApiPaths-Dv1dcHu_.js} +4 -4
  3. package/dist/ApiPaths-Dv1dcHu_.js.map +1 -0
  4. package/dist/{AppConfigFactory-YnveXE9k.d.ts → AppConfigFactory-BT0y0LVC.d.ts} +8490 -5548
  5. package/dist/{AppConfigFactory-C6q-CSKb.js → AppConfigFactory-Cx4qQvRk.js} +112 -52
  6. package/dist/AppConfigFactory-Cx4qQvRk.js.map +1 -0
  7. package/dist/{AppContainerFactory-qaqc-R1D.js → AppContainerFactory-DRTjG7nG.js} +7298 -1732
  8. package/dist/AppContainerFactory-DRTjG7nG.js.map +1 -0
  9. package/dist/{CodemationAppContext-DRu1Dpri.d.ts → CodemationAppContext-CGFYVcSb.d.ts} +14 -4
  10. package/dist/{CodemationAuthoring.types-DZl-sJaM.js → CodemationAuthoring.types-BteaR3Dc.js} +19 -6
  11. package/dist/CodemationAuthoring.types-BteaR3Dc.js.map +1 -0
  12. package/dist/{CodemationAuthoring.types-fBRppnmi.d.ts → CodemationAuthoring.types-DiKKogum.d.ts} +30 -5
  13. package/dist/{CodemationConfigNormalizer-DVko3cVN.d.ts → CodemationConfigNormalizer-48f-T66P.d.ts} +3 -3
  14. package/dist/{CodemationConsumerConfigLoader-BeAUS144.js → CodemationConsumerConfigLoader-By-6tuGc.js} +81 -10
  15. package/dist/CodemationConsumerConfigLoader-By-6tuGc.js.map +1 -0
  16. package/dist/{CodemationConsumerConfigLoader-DJWr86f-.d.ts → CodemationConsumerConfigLoader-_PIYqwVx.d.ts} +18 -2
  17. package/dist/{CodemationPluginListMerger-B-W5Fa_X.js → CodemationPluginListMerger-D1B1IEbt.js} +1 -1
  18. package/dist/{CodemationPluginListMerger-B-W5Fa_X.js.map → CodemationPluginListMerger-D1B1IEbt.js.map} +1 -1
  19. package/dist/{CodemationPluginListMerger-DGc-jfa2.d.ts → CodemationPluginListMerger-DP7djJ9S.d.ts} +151 -19
  20. package/dist/CodemationTsyringeTypeInfoRegistrar-Bj6FJYFz.js +97 -0
  21. package/dist/CodemationTsyringeTypeInfoRegistrar-Bj6FJYFz.js.map +1 -0
  22. package/dist/{CodemationWhitelabelConfig-CWbcyQqn.d.ts → CodemationWhitelabelConfig-Ca2mCUeC.d.ts} +2 -2
  23. package/dist/{CollectionContracts.types-DdpHft0i.d.ts → CollectionContracts.types-DDyFYT_D.d.ts} +1 -1
  24. package/dist/{CredentialContractsRegistry-DrMIDSw8.d.ts → CredentialContractsRegistry-Bq2bq28t.d.ts} +2 -2
  25. package/dist/{CredentialServices-UfvHB-rN.d.ts → CredentialServices-BLloBztI.d.ts} +65 -20
  26. package/dist/{CredentialServices-CgxwguAv.js → CredentialServices-Dk8yypeL.js} +310 -51
  27. package/dist/CredentialServices-Dk8yypeL.js.map +1 -0
  28. package/dist/InternalHonoApiRouteRegistrar-c7t3KnV_.d.ts +17 -0
  29. package/dist/InternalPingRegistrar-DY3kSfxP.js +221 -0
  30. package/dist/InternalPingRegistrar-DY3kSfxP.js.map +1 -0
  31. package/dist/{ItemsInputNormalizer-C-KHg9Mo.d.ts → ItemsInputNormalizer-_RwIfRIQ.d.ts} +89 -25
  32. package/dist/{LogLevelPolicyFactory-CampWO0l.d.ts → LogLevelPolicyFactory-ewCHLDLn.d.ts} +2 -2
  33. package/dist/{PublicFrontendBootstrap-DzBgwOnG.d.ts → PublicFrontendBootstrap-Cev3qK46.d.ts} +9 -2
  34. package/dist/PublicFrontendBootstrapFactory-Dv04tJ-6.d.ts +82 -0
  35. package/dist/{PublicFrontendBootstrapJsonCodec-Cl_DLRh0.d.ts → PublicFrontendBootstrapJsonCodec-CXG9Dxft.d.ts} +3 -3
  36. package/dist/{PublicFrontendBootstrapJsonCodec-DzqvA0uo.js → PublicFrontendBootstrapJsonCodec-CegIF_ne.js} +7 -2
  37. package/dist/PublicFrontendBootstrapJsonCodec-CegIF_ne.js.map +1 -0
  38. package/dist/ServerLoggerFactory-Ckk52S3w.js +223 -0
  39. package/dist/ServerLoggerFactory-Ckk52S3w.js.map +1 -0
  40. package/dist/{TelemetryContracts-DbaNomrH.d.ts → TelemetryContracts-BtDx84Cp.d.ts} +13 -4
  41. package/dist/{WorkflowPolicyUiPresentationFactory-DQEY-h_S.d.ts → WorkflowPolicyUiPresentationFactory-6MyjCvBO.d.ts} +2 -2
  42. package/dist/{WorkflowPolicyUiPresentationFactory-DhPqQ9aB.js → WorkflowPolicyUiPresentationFactory-Bb-ae_Zh.js} +1 -1
  43. package/dist/{WorkflowPolicyUiPresentationFactory-DhPqQ9aB.js.map → WorkflowPolicyUiPresentationFactory-Bb-ae_Zh.js.map} +1 -1
  44. package/dist/{WorkflowViewContracts-CzK2KFuz.d.ts → WorkflowViewContracts-B7aFQcIw.d.ts} +10 -1
  45. package/dist/authoring.d.ts +5 -5
  46. package/dist/authoring.js +1 -1
  47. package/dist/client.d.ts +4 -4
  48. package/dist/client.js +2 -2
  49. package/dist/consumer.d.ts +6 -6
  50. package/dist/consumer.js +2 -2
  51. package/dist/credentials.d.ts +6 -6
  52. package/dist/credentials.js +1 -1
  53. package/dist/devServerSidecar.d.ts +2 -2
  54. package/dist/devServerSidecar.js +1 -94
  55. package/dist/devServerSidecar.js.map +1 -1
  56. package/dist/dto.d.ts +6 -6
  57. package/dist/{index-BbBk26m0.d.ts → index-DilAYwnH.d.ts} +49 -3
  58. package/dist/index.d.ts +141 -21
  59. package/dist/index.js +109 -14
  60. package/dist/index.js.map +1 -0
  61. package/dist/mapping.d.ts +2 -2
  62. package/dist/mapping.js +1 -1
  63. package/dist/nextServer.d.ts +42 -113
  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-B71RGvSj.d.ts +30 -0
  69. package/dist/{persistenceServer-CmsIKnO9.js → persistenceServer-C-hH4z6l.js} +2 -2
  70. package/dist/{persistenceServer-CmsIKnO9.js.map → persistenceServer-C-hH4z6l.js.map} +1 -1
  71. package/dist/persistenceServer.d.ts +8 -8
  72. package/dist/persistenceServer.js +3 -3
  73. package/dist/{server-MUNGsBYK.d.ts → server-09PKasWR.d.ts} +21 -6
  74. package/dist/{server-CJFfY67o.js → server-vtRCPgRJ.js} +7 -6
  75. package/dist/{server-CJFfY67o.js.map → server-vtRCPgRJ.js.map} +1 -1
  76. package/dist/server.d.ts +14 -14
  77. package/dist/server.js +13 -11
  78. package/package.json +47 -58
  79. package/prisma/migrations/20260519000000_workflow_audit_log/migration.sql +23 -0
  80. package/prisma/migrations/20260519100000_storage_growth_fixes/migration.sql +61 -0
  81. package/prisma/migrations.sqlite/20260519000000_workflow_audit_log/migration.sql +21 -0
  82. package/prisma/migrations.sqlite/20260519100000_storage_growth_fixes/migration.sql +29 -0
  83. package/prisma/schema.postgresql.prisma +55 -17
  84. package/prisma/schema.sqlite.prisma +55 -17
  85. package/prisma-generated/prisma-postgresql-client/edge.js +33 -5
  86. package/prisma-generated/prisma-postgresql-client/index-browser.js +29 -1
  87. package/prisma-generated/prisma-postgresql-client/index.d.ts +8933 -5716
  88. package/prisma-generated/prisma-postgresql-client/index.js +33 -5
  89. package/prisma-generated/prisma-postgresql-client/package.json +1 -1
  90. package/prisma-generated/prisma-postgresql-client/schema.prisma +38 -0
  91. package/prisma-generated/prisma-sqlite-client/edge.js +33 -5
  92. package/prisma-generated/prisma-sqlite-client/index-browser.js +29 -1
  93. package/prisma-generated/prisma-sqlite-client/index.d.ts +8925 -5713
  94. package/prisma-generated/prisma-sqlite-client/index.js +33 -5
  95. package/prisma-generated/prisma-sqlite-client/package.json +1 -1
  96. package/prisma-generated/prisma-sqlite-client/schema.prisma +38 -0
  97. package/scripts/check-collections.mjs +18 -0
  98. package/scripts/generate-prisma-clients.mjs +20 -11
  99. package/src/application/WorkflowAuditLogPruneScheduler.ts +96 -0
  100. package/src/application/auth/AuthenticatedPrincipal.ts +4 -0
  101. package/src/application/commands/StartWorkflowRunCommandHandler.ts +4 -0
  102. package/src/application/contracts/WorkflowViewContracts.ts +6 -0
  103. package/src/application/contracts/WorkflowWebsocketMessage.ts +3 -1
  104. package/src/application/mapping/WorkflowDefinitionMapper.ts +40 -1
  105. package/src/application/runs/WorkflowRunRetentionPruneScheduler.ts +7 -1
  106. package/src/application/telemetry/OtelExecutionTelemetry.types.ts +5 -0
  107. package/src/application/telemetry/OtelExecutionTelemetryFactory.ts +4 -0
  108. package/src/application/telemetry/StoredTelemetrySpanScope.ts +6 -2
  109. package/src/application/telemetry/TelemetryRetentionTimestampFactory.ts +27 -17
  110. package/src/application/telemetry/TelemetrySpanPublisher.ts +11 -0
  111. package/src/application/websocket/TelemetrySpanWebsocketRelay.ts +31 -0
  112. package/src/applicationTokens.ts +20 -1
  113. package/src/audit/IAuditEmitter.ts +32 -0
  114. package/src/audit/PrismaWorkflowAuditLogRepository.ts +34 -0
  115. package/src/audit/WorkflowAuditLogWriter.ts +125 -0
  116. package/src/auth/managed/ManagedAuthConfig.ts +29 -0
  117. package/src/auth/managed/ManagedAuthMiddleware.ts +52 -0
  118. package/src/auth/managed/ManagedCorsMiddleware.ts +43 -0
  119. package/src/auth/managed/ManagedModeBootGuard.ts +27 -0
  120. package/src/auth/managed/index.ts +5 -0
  121. package/src/bootstrap/AppContainerFactory.ts +295 -29
  122. package/src/bootstrap/AppContainerLifecycle.ts +31 -0
  123. package/src/bootstrap/perf/BootTimer.ts +168 -0
  124. package/src/bootstrap/runtime/AppConfigFactory.ts +21 -65
  125. package/src/bootstrap/runtime/FrontendRuntime.ts +4 -1
  126. package/src/bootstrap/runtime/HeadlessApiRuntime.ts +47 -0
  127. package/src/bootstrap/runtime/WorkerRuntime.ts +2 -1
  128. package/src/credentials/BrokerClient.ts +49 -0
  129. package/src/credentials/BrokerRefreshError.ts +12 -0
  130. package/src/credentials/BrokerRefreshInvalidGrantError.ts +13 -0
  131. package/src/credentials/ControlPlaneCatalogFetcher.ts +261 -0
  132. package/src/credentials/CredentialOAuth2MaterialReader.ts +136 -0
  133. package/src/credentials/InternalCredentialsListRegistrar.ts +48 -0
  134. package/src/credentials/InternalCredentialsPushRegistrar.ts +125 -0
  135. package/src/credentials/LocalOAuthFlowExecutor.ts +316 -0
  136. package/src/credentials/ManagedOAuthFlowExecutor.ts +94 -0
  137. package/src/credentials/ManagedOAuthRefreshInvalidGrantError.ts +13 -0
  138. package/src/credentials/catalogTypes.ts +4 -0
  139. package/src/credentials/refresh/CredentialDisconnectedError.ts +11 -0
  140. package/src/domain/credentials/CredentialBindingService.ts +54 -2
  141. package/src/domain/credentials/CredentialKeyRotatedError.ts +22 -0
  142. package/src/domain/credentials/CredentialSecretCipher.ts +68 -6
  143. package/src/domain/credentials/CredentialTypeRegistryImpl.ts +117 -10
  144. package/src/domain/credentials/OAuth2RedirectUriResolver.ts +79 -0
  145. package/src/domain/credentials/WorkflowCredentialNodeResolver.ts +14 -5
  146. package/src/domain/telemetry/TelemetryContracts.ts +7 -1
  147. package/src/domain/workflows/WorkflowActivationPreflight.ts +24 -1
  148. package/src/domain/workflows/WorkflowActivationPreflightRules.ts +40 -1
  149. package/src/index.ts +9 -0
  150. package/src/infrastructure/binary/LocalFilesystemBinaryStorageRegistry.ts +29 -1
  151. package/src/infrastructure/binary/S3BinaryStorage.ts +169 -0
  152. package/src/infrastructure/binary/S3BinaryStorageConfig.ts +17 -0
  153. package/src/infrastructure/config/CodemationPluginRegistrar.ts +3 -1
  154. package/src/infrastructure/persistence/CodemationDatabaseUrlParser.ts +41 -0
  155. package/src/infrastructure/persistence/InMemoryTelemetryArtifactStore.ts +8 -3
  156. package/src/infrastructure/persistence/PrismaMigrationDeployer.ts +21 -13
  157. package/src/infrastructure/persistence/PrismaTelemetryArtifactStore.ts +43 -8
  158. package/src/infrastructure/persistence/PrismaWorkflowRunRepository.ts +26 -3
  159. package/src/infrastructure/persistence/PrismaWorkflowSnapshotRepository.ts +48 -0
  160. package/src/mcp/AgentMcpIntegrationImpl.ts +344 -0
  161. package/src/mcp/McpClientFactory.ts +29 -0
  162. package/src/mcp/McpConnectionPool.ts +184 -0
  163. package/src/mcp/McpConnectionPool.types.ts +12 -0
  164. package/src/mcp/McpServerCatalog.ts +104 -0
  165. package/src/mcp/index.ts +5 -0
  166. package/src/pairing/HmacRequestSigner.ts +32 -0
  167. package/src/pairing/IncomingHmacVerifier.ts +82 -0
  168. package/src/pairing/InternalHmacAuthMiddleware.ts +33 -0
  169. package/src/pairing/InternalPingRegistrar.ts +25 -0
  170. package/src/pairing/PairedFetch.ts +33 -0
  171. package/src/pairing/PairingConfigFactory.ts +35 -0
  172. package/src/pairing/PairingConfigToken.ts +6 -0
  173. package/src/pairing/index.ts +14 -0
  174. package/src/pairing/pairing.types.ts +18 -0
  175. package/src/pairing.ts +17 -0
  176. package/src/persistenceServer.ts +1 -0
  177. package/src/presentation/config/AppConfig.ts +7 -1
  178. package/src/presentation/config/CodemationAuthConfig.ts +1 -1
  179. package/src/presentation/config/CodemationAuthoring.types.ts +60 -5
  180. package/src/presentation/config/CodemationConfig.ts +9 -0
  181. package/src/presentation/config/CodemationConfigNormalizer.ts +39 -1
  182. package/src/presentation/config/CodemationPlugin.ts +2 -1
  183. package/src/presentation/frontend/CodemationFrontendAuthSnapshot.ts +5 -0
  184. package/src/presentation/frontend/CodemationFrontendAuthSnapshotFactory.ts +7 -1
  185. package/src/presentation/frontend/PublicFrontendBootstrap.ts +2 -0
  186. package/src/presentation/frontend/PublicFrontendBootstrapFactory.ts +5 -1
  187. package/src/presentation/frontend/PublicFrontendBootstrapJsonCodec.ts +4 -1
  188. package/src/presentation/http/ApiPaths.ts +4 -4
  189. package/src/presentation/http/HeadlessHttpServerFactory.ts +56 -0
  190. package/src/presentation/http/ServerHttpErrorResponseFactory.ts +39 -2
  191. package/src/presentation/http/hono/CodemationHonoApiAppFactory.ts +33 -8
  192. package/src/presentation/http/hono/InternalHonoApiRouteRegistrar.ts +12 -0
  193. package/src/presentation/http/hono/registrars/ManagedMeHonoApiRouteRegistrar.ts +35 -0
  194. package/src/presentation/http/hono/registrars/OAuth2HonoApiRouteRegistrar.ts +2 -2
  195. package/src/presentation/http/routeHandlers/CredentialHttpRouteHandler.ts +28 -0
  196. package/src/presentation/http/routeHandlers/OAuth2HttpRouteHandlerFactory.ts +98 -41
  197. package/src/presentation/server/CodemationConsumerConfigLoader.ts +59 -7
  198. package/src/presentation/server/CodemationPluginDiscovery.ts +5 -0
  199. package/src/presentation/server/WorkflowDefinitionExportsResolver.ts +18 -0
  200. package/src/presentation/server/WorkflowModulePathFinder.ts +12 -1
  201. package/src/presentation/websocket/ManagedWebsocketAuthenticator.ts +50 -0
  202. package/src/presentation/websocket/WebsocketAuthenticator.types.ts +12 -0
  203. package/src/presentation/websocket/WorkflowWebsocketServer.ts +24 -3
  204. package/src/presentation/websocket/WorkflowWebsocketServerFactory.ts +16 -0
  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-C6q-CSKb.js.map +0 -1
  217. package/dist/AppContainerFactory-qaqc-R1D.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-Cb2pLmDd.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-vtJAGDat.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,63 @@ 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", () =>
397
+ this.registerCoreInfrastructure(container, inputs),
398
+ );
399
+ BootTimer.measure("appContainer.registerRepositoriesAndBuses", () => this.registerRepositoriesAndBuses(container));
400
+ BootTimer.measure("appContainer.registerApplicationServicesAndRoutes", () =>
401
+ this.registerApplicationServicesAndRoutes(container, inputs.appConfig),
402
+ );
403
+ BootTimer.measure("appContainer.registerOperationalInfrastructure", () =>
404
+ this.registerOperationalInfrastructure(container),
405
+ );
406
+ BootTimer.measure("appContainer.registerManagedAuthInfrastructure", () =>
407
+ this.registerManagedAuthInfrastructure(container, inputs.appConfig),
408
+ );
409
+ BootTimer.measure("appContainer.registerPairingInfrastructure", () =>
410
+ this.registerPairingInfrastructure(container, inputs.appConfig),
411
+ );
412
+ BootTimer.measure("appContainer.registerConfiguredRegistrations", () =>
413
+ this.registerConfiguredRegistrations(container, inputs.appConfig),
414
+ );
415
+ const credentialTypes = BootTimer.measure("appContainer.collectCredentialTypes", () =>
416
+ this.collectCredentialTypes(inputs.appConfig),
417
+ );
418
+ BootTimer.measure("appContainer.registerMcpCatalog", () => this.registerMcpCatalog(container));
419
+ await BootTimer.measureAsync("appContainer.applyPlugins", () =>
420
+ this.applyPlugins(container, inputs.appConfig, credentialTypes),
421
+ );
422
+ BootTimer.measure("appContainer.mergeConfigMcpServers", () =>
423
+ this.mergeConfigMcpServers(container, inputs.appConfig),
368
424
  );
425
+ const ownership = await BootTimer.measureAsync("appContainer.registerRuntimeInfrastructure", () =>
426
+ this.registerRuntimeInfrastructure(container, inputs.appConfig),
427
+ );
428
+ BootTimer.measure("appContainer.registerWorkflowAuditWriter", () =>
429
+ this.registerWorkflowAuditWriter(container, inputs.appConfig),
430
+ );
431
+ BootTimer.measure("appContainer.registerCollectionsInfrastructure", () =>
432
+ this.registerCollectionsInfrastructure(container, inputs.appConfig),
433
+ );
434
+ BootTimer.measure("appContainer.registerCredentialTypes", () =>
435
+ this.registerCredentialTypes(container, credentialTypes),
436
+ );
437
+ BootTimer.measure("appContainer.registerControlPlaneCatalogFetcher", () =>
438
+ this.registerControlPlaneCatalogFetcher(container),
439
+ );
440
+ BootTimer.measure("appContainer.synchronizeLiveWorkflowRepository", () =>
441
+ this.synchronizeLiveWorkflowRepository(container, inputs.appConfig.workflows),
442
+ );
443
+ BootTimer.measure("appContainer.registerWorkflowDefinitions", () =>
444
+ new CodemationTsyringeTypeInfoRegistrar(container).registerWorkflowDefinitions(inputs.appConfig.workflows ?? []),
445
+ );
446
+ container.resolve(BootRuntimeSnapshotHolder).set(this.createRuntimeSummary(inputs.appConfig));
447
+ const lifecycle = new AppContainerLifecycle(container, ownership.ownedPrismaClient);
448
+ container.registerInstance(AppContainerLifecycle, lifecycle);
449
+ await BootTimer.measureAsync("appContainer.lifecycleStart", () => lifecycle.start());
369
450
  return container;
370
451
  }
371
452
 
@@ -391,11 +472,28 @@ export class AppContainerFactory {
391
472
  return credentialTypes;
392
473
  }
393
474
 
475
+ private registerMcpCatalog(container: Container): void {
476
+ container.registerSingleton(McpServerCatalog, McpServerCatalog);
477
+ container.registerSingleton(DefaultMcpClientFactory, DefaultMcpClientFactory);
478
+ container.registerSingleton(McpConnectionPool, McpConnectionPool);
479
+ container.registerSingleton(AgentMcpIntegrationImpl, AgentMcpIntegrationImpl);
480
+ // Register the host-side AgentMcpIntegration, overriding the NoOp registered by EngineRuntimeRegistrar.
481
+ container.register(CoreTokens.AgentMcpIntegration, {
482
+ useFactory: instanceCachingFactory((c) => c.resolve(AgentMcpIntegrationImpl)),
483
+ });
484
+ }
485
+
486
+ private mergeConfigMcpServers(container: Container, appConfig: AppConfig): void {
487
+ const catalog = container.resolve(McpServerCatalog);
488
+ catalog.merge("config", appConfig.mcpServers ?? []);
489
+ }
490
+
394
491
  private async applyPlugins(
395
492
  container: Container,
396
493
  appConfig: AppConfig,
397
494
  credentialTypes: Array<CredentialType<any, any, unknown>>,
398
495
  ): Promise<void> {
496
+ const catalog = container.resolve(McpServerCatalog);
399
497
  await this.pluginRegistrar.apply({
400
498
  plugins: appConfig.plugins,
401
499
  container,
@@ -411,6 +509,7 @@ export class AppContainerFactory {
411
509
  // during plugin.register() are accepted silently — Phase 6 store wiring will
412
510
  // pick them up once collection infrastructure can reload from a mutable registry.
413
511
  },
512
+ mergeMcpServers: (declarations) => catalog.merge("plugin", declarations),
414
513
  loggerFactory: container.resolve(ApplicationTokens.LoggerFactory),
415
514
  });
416
515
  }
@@ -420,9 +519,27 @@ export class AppContainerFactory {
420
519
  credentialTypes: ReadonlyArray<CredentialType<any, any, unknown>>,
421
520
  ): void {
422
521
  const registry = container.resolve(CredentialTypeRegistryImpl);
423
- for (const credentialType of credentialTypes) {
424
- registry.register(credentialType);
522
+ registry.merge("plugin", credentialTypes);
523
+ }
524
+
525
+ private registerControlPlaneCatalogFetcher(container: Container): void {
526
+ // Only register the fetcher when paired with a control plane.
527
+ // PairedFetch is only registered inside registerPairingInfrastructure,
528
+ // which skips registration when pairing env vars are absent.
529
+ if (!container.isRegistered(PairedFetch, true)) {
530
+ return;
425
531
  }
532
+
533
+ container.registerSingleton(ControlPlaneCatalogFetcher, ControlPlaneCatalogFetcher);
534
+
535
+ const fetcher = container.resolve(ControlPlaneCatalogFetcher);
536
+ const credentialTypeRegistry = container.resolve(CredentialTypeRegistryImpl);
537
+ const mcpServerCatalog = container.resolve(McpServerCatalog);
538
+
539
+ fetcher.onRefresh = () => {
540
+ credentialTypeRegistry.mergeDefinitions("controlPlane", fetcher.credentialTypeOverrides ?? []);
541
+ mcpServerCatalog.merge("controlPlane", fetcher.mcpServers ?? []);
542
+ };
426
543
  }
427
544
 
428
545
  private registerConfiguredRegistrations(container: Container, appConfig: AppConfig): void {
@@ -499,9 +616,7 @@ export class AppContainerFactory {
499
616
  CoreTokens.PersistedWorkflowTokenRegistry,
500
617
  container.resolve(PersistedWorkflowTokenRegistry),
501
618
  );
502
- container.register(CredentialTypeRegistryImpl, {
503
- useFactory: instanceCachingFactory(() => new CredentialTypeRegistryImpl()),
504
- });
619
+ container.registerSingleton(CredentialTypeRegistryImpl, CredentialTypeRegistryImpl);
505
620
  container.register(CoreTokens.CredentialTypeRegistry, {
506
621
  useFactory: instanceCachingFactory((dependencyContainer) =>
507
622
  dependencyContainer.resolve(CredentialTypeRegistryImpl),
@@ -516,9 +631,11 @@ export class AppContainerFactory {
516
631
  });
517
632
  container.register(CoreTokens.LiveWorkflowRepository, {
518
633
  useFactory: instanceCachingFactory(
519
- () =>
634
+ (dependencyContainer) =>
520
635
  new LiveWorkflowRepository(
521
- new AIAgentConnectionWorkflowExpander(new ConnectionCredentialNodeConfigFactory()),
636
+ new AIAgentConnectionWorkflowExpander(new ConnectionCredentialNodeConfigFactory(), (id) =>
637
+ dependencyContainer.resolve(McpServerCatalog).get(id),
638
+ ),
522
639
  ),
523
640
  ),
524
641
  });
@@ -563,6 +680,7 @@ export class AppContainerFactory {
563
680
  });
564
681
  container.registerInstance(LogLevelPolicyFactory, logLevelPolicyFactory);
565
682
  container.registerSingleton(ServerLoggerFactory, ServerLoggerFactory);
683
+ container.registerInstance(ApplicationTokens.ProcessRunner, new ExecaProcessRunner());
566
684
  container.register(ApplicationTokens.LoggerFactory, {
567
685
  useFactory: instanceCachingFactory((dependencyContainer) => dependencyContainer.resolve(ServerLoggerFactory)),
568
686
  });
@@ -576,10 +694,14 @@ export class AppContainerFactory {
576
694
  if (inputs.sharedWorkflowWebsocketServer) {
577
695
  return inputs.sharedWorkflowWebsocketServer;
578
696
  }
697
+ const authenticator = dependencyContainer.isRegistered(ApplicationTokens.WebsocketAuthenticator, true)
698
+ ? dependencyContainer.resolve(ApplicationTokens.WebsocketAuthenticator)
699
+ : null;
579
700
  return new WorkflowWebsocketServer(
580
701
  dependencyContainer.resolve(ApplicationTokens.WebSocketPort),
581
702
  dependencyContainer.resolve(ApplicationTokens.WebSocketBindHost),
582
703
  dependencyContainer.resolve(ServerLoggerFactory).create("codemation-websocket.server"),
704
+ authenticator,
583
705
  );
584
706
  }),
585
707
  });
@@ -605,7 +727,19 @@ export class AppContainerFactory {
605
727
  container.register(CoreTokens.CredentialSessionService, { useToken: CredentialSessionServiceImpl });
606
728
  }
607
729
  container.registerSingleton(OAuth2ProviderRegistry, OAuth2ProviderRegistry);
608
- container.registerSingleton(OAuth2ConnectService, OAuth2ConnectService);
730
+ container.registerSingleton(OAuth2RedirectUriResolver, OAuth2RedirectUriResolver);
731
+ if (container.isRegistered(PairedFetch, true)) {
732
+ container.register(ApplicationTokens.OAuthFlowExecutor, {
733
+ useFactory: instanceCachingFactory((c) => c.resolve(ManagedOAuthFlowExecutor)),
734
+ });
735
+ container.registerSingleton(ManagedOAuthFlowExecutor, ManagedOAuthFlowExecutor);
736
+ } else {
737
+ container.register(ApplicationTokens.OAuthFlowExecutor, {
738
+ useFactory: instanceCachingFactory((c) => c.resolve(LocalOAuthFlowExecutor)),
739
+ });
740
+ container.registerSingleton(LocalOAuthFlowExecutor, LocalOAuthFlowExecutor);
741
+ }
742
+ container.registerSingleton(CredentialOAuth2MaterialReader, CredentialOAuth2MaterialReader);
609
743
  container.registerSingleton(CodemationFrontendAuthSnapshotFactory, CodemationFrontendAuthSnapshotFactory);
610
744
  container.registerSingleton(FrontendAppConfigFactory, FrontendAppConfigFactory);
611
745
  container.registerSingleton(PublicFrontendBootstrapFactory, PublicFrontendBootstrapFactory);
@@ -723,6 +857,7 @@ export class AppContainerFactory {
723
857
  container.registerSingleton(InMemoryTelemetryArtifactStore, InMemoryTelemetryArtifactStore);
724
858
  container.registerSingleton(InMemoryTelemetryMetricPointStore, InMemoryTelemetryMetricPointStore);
725
859
  container.registerSingleton(PrismaRunTraceContextRepository, PrismaRunTraceContextRepository);
860
+ container.registerSingleton(PrismaWorkflowSnapshotRepository, PrismaWorkflowSnapshotRepository);
726
861
  container.registerSingleton(PrismaTelemetrySpanStore, PrismaTelemetrySpanStore);
727
862
  container.registerSingleton(PrismaTelemetryArtifactStore, PrismaTelemetryArtifactStore);
728
863
  container.registerSingleton(PrismaTelemetryMetricPointStore, PrismaTelemetryMetricPointStore);
@@ -770,7 +905,7 @@ export class AppContainerFactory {
770
905
  });
771
906
  }
772
907
 
773
- private registerApplicationServicesAndRoutes(container: Container): void {
908
+ private registerApplicationServicesAndRoutes(container: Container, appConfig: AppConfig): void {
774
909
  container.registerSingleton(DevBootstrapSummaryAssembler, DevBootstrapSummaryAssembler);
775
910
  container.registerSingleton(DevBootstrapSummaryHttpRouteHandler, DevBootstrapSummaryHttpRouteHandler);
776
911
  container.registerSingleton(AuthHttpRouteHandler, AuthHttpRouteHandler);
@@ -808,21 +943,126 @@ export class AppContainerFactory {
808
943
  container.registerSingleton(TestRunnerService, TestRunnerService);
809
944
  container.registerSingleton(TestSuiteHttpRouteHandler, TestSuiteHttpRouteHandler);
810
945
  container.registerSingleton(CollectionHttpRouteHandler, CollectionHttpRouteHandler);
946
+ const isManagedMode = appConfig.auth?.kind === "managed";
811
947
  for (const registrar of AppContainerFactory.honoRouteRegistrars) {
948
+ // In managed mode, skip the Better Auth route registrar so /api/auth/* returns 404
949
+ if (isManagedMode && registrar === AuthHonoApiRouteRegistrar) {
950
+ continue;
951
+ }
812
952
  container.registerSingleton(ApplicationTokens.HonoApiRouteRegistrar, registrar);
813
953
  }
814
954
  container.registerSingleton(CodemationHonoApiApp, CodemationHonoApiApp);
815
955
  }
816
956
 
957
+ private registerManagedAuthInfrastructure(container: Container, appConfig: AppConfig): void {
958
+ if (appConfig.auth?.kind !== "managed") {
959
+ return;
960
+ }
961
+
962
+ new ManagedModeBootGuard().assertRequiredEnv(appConfig.env);
963
+ const managedAuthConfig = new ManagedAuthConfigFactory().create(appConfig.env);
964
+ const workspaceId = appConfig.env["WORKSPACE_ID"] ?? "";
965
+
966
+ const jwksCache = new JwksCache({ jwksUrl: managedAuthConfig.jwksUrl }, (url) => fetch(url), {
967
+ now: () => Date.now(),
968
+ });
969
+ // Shared verifier: both ManagedAuthMiddleware (HTTP) and ManagedWebsocketAuthenticator (WS)
970
+ // use the same JwksCache instance so JWKS key rotation propagates to both transports.
971
+ const jwtVerifier = new ManagedJwtVerifier(
972
+ {
973
+ expectedIssuer: managedAuthConfig.issuer,
974
+ expectedAudience: workspaceId,
975
+ jwksCache: { jwksUrl: managedAuthConfig.jwksUrl },
976
+ },
977
+ jwksCache,
978
+ );
979
+
980
+ const managedAuthMiddleware = new ManagedAuthMiddleware(jwtVerifier);
981
+ container.register(ApplicationTokens.SessionVerifier, { useValue: managedAuthMiddleware });
982
+
983
+ const websocketAuthenticator = new ManagedWebsocketAuthenticator(jwtVerifier);
984
+ container.registerInstance(ApplicationTokens.WebsocketAuthenticator, websocketAuthenticator);
985
+
986
+ const corsMiddleware = new ManagedCorsMiddleware(managedAuthConfig.cpWebOrigin);
987
+ container.registerInstance(ApplicationTokens.ManagedCorsMiddleware, corsMiddleware);
988
+
989
+ container.registerSingleton(ApplicationTokens.HonoApiRouteRegistrar, ManagedMeHonoApiRouteRegistrar);
990
+ }
991
+
992
+ private registerPairingInfrastructure(container: Container, appConfig: AppConfig): void {
993
+ const isManagedMode = appConfig.auth?.kind === "managed";
994
+ let pairingConfig;
995
+ try {
996
+ pairingConfig = new PairingConfigFactory().create(appConfig.env);
997
+ } catch (err) {
998
+ if (isManagedMode) {
999
+ // In managed mode the secret is required — let the error surface.
1000
+ throw err;
1001
+ }
1002
+ // In non-managed mode an invalid-but-present WORKSPACE_PAIRING_SECRET is a misconfiguration
1003
+ // warning, not a fatal error. Log and continue without pairing.
1004
+ const logger = container.resolve(ServerLoggerFactory).create("codemation.pairing");
1005
+ logger.warn(
1006
+ `WORKSPACE_PAIRING_SECRET is set but invalid — pairing disabled. ${err instanceof Error ? err.message : String(err)}`,
1007
+ );
1008
+ return;
1009
+ }
1010
+ if (!pairingConfig) {
1011
+ // Pairing is optional in non-production environments (local dev without CP integration).
1012
+ // Emit a startup warning so operators know the workspace-mcp HMAC channel is inactive.
1013
+ const missingVars: string[] = [];
1014
+ if (!appConfig.env["WORKSPACE_ID"]) missingVars.push("WORKSPACE_ID");
1015
+ if (!appConfig.env["WORKSPACE_PAIRING_SECRET"]) missingVars.push("WORKSPACE_PAIRING_SECRET");
1016
+ if (!appConfig.env["CONTROL_PLANE_URL"]) missingVars.push("CONTROL_PLANE_URL");
1017
+ if (missingVars.length > 0) {
1018
+ const logger = container.resolve(ServerLoggerFactory).create("codemation.pairing");
1019
+ logger.warn(
1020
+ `Pairing not configured — missing env vars: ${missingVars.join(", ")}. ` +
1021
+ "Internal /internal/* routes are inactive. Set these vars for managed-mode integration.",
1022
+ );
1023
+ }
1024
+ return;
1025
+ }
1026
+ container.registerInstance(PairingConfigToken, pairingConfig);
1027
+ container.registerSingleton(HmacRequestSigner, HmacRequestSigner);
1028
+ container.registerSingleton(PairedFetch, PairedFetch);
1029
+ container.registerSingleton(IncomingHmacVerifier, IncomingHmacVerifier);
1030
+ container.registerSingleton(InternalHmacAuthMiddleware, InternalHmacAuthMiddleware);
1031
+ container.registerSingleton(BrokerClient, BrokerClient);
1032
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalPingRegistrar);
1033
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalCredentialsPushRegistrar);
1034
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalCredentialsListRegistrar);
1035
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalWorkflowsListRegistrar);
1036
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalWorkflowDetailRegistrar);
1037
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalWorkflowActivationRegistrar);
1038
+ container.registerSingleton(ApplicationTokens.InternalHonoApiRouteRegistrar, InternalWorkflowTestRunRegistrar);
1039
+ }
1040
+
817
1041
  private registerOperationalInfrastructure(container: Container): void {
818
1042
  container.register(ApplicationTokens.WorkflowWebsocketPublisher, {
819
1043
  useFactory: instanceCachingFactory((dependencyContainer) => dependencyContainer.resolve(WorkflowWebsocketServer)),
820
1044
  });
821
1045
  container.registerSingleton(WorkflowRunEventWebsocketRelay, WorkflowRunEventWebsocketRelay);
1046
+ container.registerSingleton(TelemetrySpanWebsocketRelay, TelemetrySpanWebsocketRelay);
1047
+ container.register(ApplicationTokens.TelemetrySpanPublisher, {
1048
+ useFactory: instanceCachingFactory((dependencyContainer) =>
1049
+ dependencyContainer.resolve(TelemetrySpanWebsocketRelay),
1050
+ ),
1051
+ });
822
1052
  container.registerSingleton(RunEventBusTelemetryReporter, RunEventBusTelemetryReporter);
823
1053
  container.registerSingleton(WorkflowRunRetentionPruneScheduler, WorkflowRunRetentionPruneScheduler);
824
1054
  }
825
1055
 
1056
+ private registerWorkflowAuditWriter(container: Container, appConfig: AppConfig): void {
1057
+ if (appConfig.persistence.kind === "none") {
1058
+ return;
1059
+ }
1060
+ container.registerSingleton(PrismaWorkflowAuditLogRepository, PrismaWorkflowAuditLogRepository);
1061
+ container.register(ApplicationTokens.WorkflowAuditEmitter, { useToken: PrismaWorkflowAuditLogRepository });
1062
+ container.registerSingleton(WorkflowAuditLogWriter, WorkflowAuditLogWriter);
1063
+ container.registerSingleton(WorkflowAuditLogPruneScheduler, WorkflowAuditLogPruneScheduler);
1064
+ }
1065
+
826
1066
  private async registerRuntimeInfrastructure(container: Container, appConfig: AppConfig): Promise<PrismaOwnership> {
827
1067
  const queuePrefix = appConfig.scheduler.queuePrefix ?? appConfig.eventing.queuePrefix ?? "codemation";
828
1068
  const eventBus =
@@ -830,7 +1070,7 @@ export class AppContainerFactory {
830
1070
  ? new RedisRunEventBus(this.requireRedisUrl(appConfig.eventing.redisUrl), queuePrefix)
831
1071
  : new InMemoryRunEventBus();
832
1072
  container.registerInstance(CoreTokens.RunEventBus, eventBus);
833
- const binaryStorage = this.createBinaryStorage(appConfig.repoRoot);
1073
+ const binaryStorage = await this.createBinaryStorage(appConfig);
834
1074
  container.registerInstance(CoreTokens.RunDataFactory, new InMemoryRunDataFactory());
835
1075
  container.registerInstance(CoreTokens.BinaryStorage, binaryStorage);
836
1076
  container.registerInstance(ApplicationTokens.Clock, new SystemClock());
@@ -1005,11 +1245,37 @@ export class AppContainerFactory {
1005
1245
  };
1006
1246
  }
1007
1247
 
1008
- private createBinaryStorage(repoRoot: string): InMemoryBinaryStorage | LocalFilesystemBinaryStorage {
1009
- if (!repoRoot) {
1248
+ private async createBinaryStorage(
1249
+ appConfig: AppConfig,
1250
+ ): Promise<InMemoryBinaryStorage | LocalFilesystemBinaryStorage | S3BinaryStorage> {
1251
+ const kind = appConfig.env.BINARY_STORAGE_KIND ?? "local";
1252
+
1253
+ if (kind === "s3") {
1254
+ const parseResult = S3BinaryStorageConfigSchema.safeParse({
1255
+ endpoint: appConfig.env.BINARY_STORAGE_S3_ENDPOINT,
1256
+ region: appConfig.env.BINARY_STORAGE_S3_REGION,
1257
+ bucket: appConfig.env.BINARY_STORAGE_S3_BUCKET,
1258
+ accessKeyId: appConfig.env.BINARY_STORAGE_S3_ACCESS_KEY_ID,
1259
+ secretAccessKey: appConfig.env.BINARY_STORAGE_S3_SECRET_ACCESS_KEY,
1260
+ });
1261
+ if (!parseResult.success) {
1262
+ throw new Error(
1263
+ `BINARY_STORAGE_KIND=s3 requires all BINARY_STORAGE_S3_* env vars. Validation errors: ${parseResult.error.message}`,
1264
+ );
1265
+ }
1266
+ const storage = new S3BinaryStorage(parseResult.data);
1267
+ await storage.checkConnectivity();
1268
+ return storage;
1269
+ }
1270
+
1271
+ if (kind !== "local") {
1272
+ throw new Error(`Unknown BINARY_STORAGE_KIND: "${kind}". Expected "local" or "s3".`);
1273
+ }
1274
+
1275
+ if (!appConfig.repoRoot) {
1010
1276
  return new InMemoryBinaryStorage();
1011
1277
  }
1012
- return new LocalFilesystemBinaryStorage(`${repoRoot}/.codemation/binary`);
1278
+ return new LocalFilesystemBinaryStorage(`${appConfig.repoRoot}/.codemation/binary`);
1013
1279
  }
1014
1280
 
1015
1281
  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
  }