@codemation/host 0.7.0 → 0.9.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 (164) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/LICENSE +37 -1
  3. package/dist/{ApiPaths-Dv1dcHu_.js → ApiPaths-DCvrlIjg.js} +12 -1
  4. package/dist/{ApiPaths-Dv1dcHu_.js.map → ApiPaths-DCvrlIjg.js.map} +1 -1
  5. package/dist/{AppConfigFactory-Cx4qQvRk.js → AppConfigFactory-D4LL1aOR.js} +77 -297
  6. package/dist/AppConfigFactory-D4LL1aOR.js.map +1 -0
  7. package/dist/{AppConfigFactory-DnLoQ9Li.d.ts → AppConfigFactory-DncmwCD1.d.ts} +2918 -199
  8. package/dist/{AppContainerFactory-DqKYCRNP.js → AppContainerFactory-jpYXGZGe.js} +1733 -475
  9. package/dist/AppContainerFactory-jpYXGZGe.js.map +1 -0
  10. package/dist/{CodemationAppContext-CKVv9W9q.d.ts → CodemationAppContext-K51b7oXe.d.ts} +9 -3
  11. package/dist/{CodemationAuthoring.types-DA3G3s6d.d.ts → CodemationAuthoring.types-BXlXIl4K.d.ts} +9 -4
  12. package/dist/{CodemationAuthoring.types-NGkBcmmT.js → CodemationAuthoring.types-BteaR3Dc.js} +3 -2
  13. package/dist/CodemationAuthoring.types-BteaR3Dc.js.map +1 -0
  14. package/dist/{CodemationConfigNormalizer-BAKjetJ6.d.ts → CodemationConfigNormalizer-B4rDYC9h.d.ts} +3 -3
  15. package/dist/{CodemationConsumerConfigLoader-GYpBBvqE.js → CodemationConsumerConfigLoader-By-6tuGc.js} +3 -1
  16. package/dist/CodemationConsumerConfigLoader-By-6tuGc.js.map +1 -0
  17. package/dist/{CodemationConsumerConfigLoader-nxOqvv46.d.ts → CodemationConsumerConfigLoader-Dt4jyLx6.d.ts} +3 -2
  18. package/dist/{CodemationPluginListMerger-DKLAHT2b.d.ts → CodemationPluginListMerger-DS6I3Xe0.d.ts} +64 -27
  19. package/dist/{persistenceServer-C-hH4z6l.js → CodemationPostgresPrismaClientFactory-C7156Fe-.js} +2 -2
  20. package/dist/CodemationPostgresPrismaClientFactory-C7156Fe-.js.map +1 -0
  21. package/dist/CodemationPostgresPrismaClientFactory-CTNTPnDr.d.ts +9 -0
  22. package/dist/{CredentialContractsRegistry-Bq2bq28t.d.ts → CredentialContractsRegistry-Dgu-rEXi.d.ts} +16 -3
  23. package/dist/{CredentialServices-Be2I60Th.d.ts → CredentialServices-B3wPyp2y.d.ts} +4 -4
  24. package/dist/{CredentialServices-Dk8yypeL.js → CredentialServices-Bios0dM8.js} +10 -4
  25. package/dist/CredentialServices-Bios0dM8.js.map +1 -0
  26. package/dist/{InternalPingRegistrar-DY3kSfxP.js → InternalPingRegistrar-BavAAnvk.js} +19 -16
  27. package/dist/InternalPingRegistrar-BavAAnvk.js.map +1 -0
  28. package/dist/{ItemsInputNormalizer-_RwIfRIQ.d.ts → ItemsInputNormalizer-CFkfNMLt.d.ts} +1434 -1225
  29. package/dist/PrismaMigrationDeployer-DdEcXXVi.d.ts +14 -0
  30. package/dist/{PublicFrontendBootstrapFactory-CY2FS-5g.d.ts → PublicFrontendBootstrapFactory-ClEjZP74.d.ts} +2 -2
  31. package/dist/{PublicFrontendBootstrapJsonCodec-CXG9Dxft.d.ts → PublicFrontendBootstrapJsonCodec-HNItQ7ol.d.ts} +6 -1
  32. package/dist/{TelemetryContracts-BtDx84Cp.d.ts → TelemetryContracts-DpZEODQM.d.ts} +2 -2
  33. package/dist/{WorkflowPolicyUiPresentationFactory-6MyjCvBO.d.ts → WorkflowPolicyUiPresentationFactory-BNn2fvR_.d.ts} +2 -2
  34. package/dist/{WorkflowPolicyUiPresentationFactory-Bb-ae_Zh.js → WorkflowPolicyUiPresentationFactory-DfvD2VHk.js} +1 -1
  35. package/dist/{WorkflowPolicyUiPresentationFactory-Bb-ae_Zh.js.map → WorkflowPolicyUiPresentationFactory-DfvD2VHk.js.map} +1 -1
  36. package/dist/authoring.d.ts +4 -4
  37. package/dist/authoring.js +1 -1
  38. package/dist/client.d.ts +1 -1
  39. package/dist/client.js +1 -1
  40. package/dist/consumer.d.ts +5 -5
  41. package/dist/consumer.js +1 -1
  42. package/dist/credentials.d.ts +5 -5
  43. package/dist/credentials.js +1 -1
  44. package/dist/devServerSidecar.d.ts +2 -2
  45. package/dist/dto.d.ts +5 -5
  46. package/dist/{index-DilAYwnH.d.ts → index-ChIfeWzk.d.ts} +71 -28
  47. package/dist/index.d.ts +49 -17
  48. package/dist/index.js +106 -13
  49. package/dist/index.js.map +1 -0
  50. package/dist/infrastructure/persistence/PrismaMigrationOperations.d.ts +44 -0
  51. package/dist/infrastructure/persistence/PrismaMigrationOperations.js +302 -0
  52. package/dist/infrastructure/persistence/PrismaMigrationOperations.js.map +1 -0
  53. package/dist/mapping.d.ts +2 -2
  54. package/dist/mapping.js +1 -1
  55. package/dist/nextServer.d.ts +15 -39
  56. package/dist/nextServer.js +6 -6
  57. package/dist/pairing.d.ts +27 -8
  58. package/dist/pairing.js +19 -3
  59. package/dist/pairing.js.map +1 -0
  60. package/dist/{pairing.types-snfZ_OzB.d.ts → pairing.types-D9Bjn98U.d.ts} +1 -1
  61. package/dist/persistenceServer.d.ts +31 -7
  62. package/dist/persistenceServer.js +2 -2
  63. package/dist/{server-C4bS62rg.d.ts → server-B5trn7y4.d.ts} +5 -5
  64. package/dist/{server-Y7kxwtCK.js → server-BlG9qV5S.js} +5 -5
  65. package/dist/{server-Y7kxwtCK.js.map → server-BlG9qV5S.js.map} +1 -1
  66. package/dist/server.d.ts +10 -10
  67. package/dist/server.js +9 -9
  68. package/package.json +28 -25
  69. package/playwright.config.ts +8 -2
  70. package/playwright.scaffolded-dev.config.ts +8 -2
  71. package/prisma/migrations/20260526120000_credential_material_pointer/migration.sql +18 -0
  72. package/prisma/migrations/20260527120000_add_human_task/migration.sql +32 -0
  73. package/prisma/migrations/20260527130000_add_hitl_state_json/migration.sql +6 -0
  74. package/prisma/migrations/20260527130000_add_hmac_nonce/migration.sql +12 -0
  75. package/prisma/migrations.sqlite/20260526120000_credential_material_pointer/migration.sql +13 -0
  76. package/prisma/migrations.sqlite/20260527120000_add_human_task/migration.sql +30 -0
  77. package/prisma/migrations.sqlite/20260527130000_add_hitl_state_json/migration.sql +6 -0
  78. package/prisma/migrations.sqlite/20260527130000_add_hmac_nonce/migration.sql +9 -0
  79. package/prisma/schema.postgresql.prisma +48 -0
  80. package/prisma/schema.sqlite.prisma +48 -0
  81. package/prisma-generated/prisma-postgresql-client/edge.js +40 -6
  82. package/prisma-generated/prisma-postgresql-client/index-browser.js +36 -2
  83. package/prisma-generated/prisma-postgresql-client/index.d.ts +3179 -163
  84. package/prisma-generated/prisma-postgresql-client/index.js +40 -6
  85. package/prisma-generated/prisma-postgresql-client/package.json +1 -1
  86. package/prisma-generated/prisma-postgresql-client/schema.prisma +48 -0
  87. package/prisma-generated/prisma-sqlite-client/edge.js +40 -6
  88. package/prisma-generated/prisma-sqlite-client/index-browser.js +36 -2
  89. package/prisma-generated/prisma-sqlite-client/index.d.ts +3175 -163
  90. package/prisma-generated/prisma-sqlite-client/index.js +40 -6
  91. package/prisma-generated/prisma-sqlite-client/package.json +1 -1
  92. package/prisma-generated/prisma-sqlite-client/schema.prisma +48 -0
  93. package/src/application/contracts/CredentialContractsRegistry.ts +15 -0
  94. package/src/application/credentials/AppGalleryProjector.ts +69 -0
  95. package/src/application/hitl/DecideHumanTaskCommandHandler.ts +149 -0
  96. package/src/application/hitl/DecisionSchemaValidator.ts +22 -0
  97. package/src/application/hitl/HitlCallbackHandler.ts +96 -0
  98. package/src/application/mapping/WorkflowDefinitionMapper.ts +1 -3
  99. package/src/application/queries/CredentialQueryHandlers.ts +2 -0
  100. package/src/application/queries/GetCredentialAppsQuery.ts +4 -0
  101. package/src/application/queries/GetCredentialAppsQueryHandler.ts +27 -0
  102. package/src/application/telemetry/ResumeTelemetryContextForRun.ts +53 -0
  103. package/src/application/telemetry/TelemetryRetentionTimestampFactory.ts +9 -8
  104. package/src/applicationTokens.ts +11 -1
  105. package/src/auth/managed/ManagedCorsMiddleware.ts +20 -5
  106. package/src/bootstrap/AppContainerFactory.ts +121 -3
  107. package/src/bootstrap/runtime/HeadlessApiRuntime.ts +47 -0
  108. package/src/credentials/CachingCredentialMaterialProvider.ts +96 -0
  109. package/src/credentials/CompositeCredentialMaterialProvider.ts +47 -0
  110. package/src/credentials/ControlPlaneCatalogFetcher.ts +8 -28
  111. package/src/credentials/ControlPlaneCredentialMaterialProvider.ts +79 -0
  112. package/src/credentials/CredentialOAuth2MaterialReader.ts +2 -7
  113. package/src/credentials/InternalCredentialsBindingRegistrar.ts +83 -0
  114. package/src/credentials/LocalCredentialMaterialProvider.ts +92 -0
  115. package/src/domain/credentials/CredentialInstanceService.ts +5 -1
  116. package/src/domain/credentials/CredentialTypeRegistryImpl.ts +18 -4
  117. package/src/domain/workflows/WorkflowActivationPreflightRules.ts +7 -4
  118. package/src/dto.ts +2 -0
  119. package/src/hitl/ControlPlaneInboxChannel.ts +102 -0
  120. package/src/hitl/HitlResumeTokenSigner.ts +80 -0
  121. package/src/hitl/HitlTimeoutJobScheduler.ts +77 -0
  122. package/src/hitl/HitlTimeoutWorker.ts +138 -0
  123. package/src/hitl/InboxChannelResolver.ts +49 -0
  124. package/src/hitl/LocalInboxChannel.ts +37 -0
  125. package/src/index.ts +3 -0
  126. package/src/infrastructure/persistence/PrismaCredentialStore.ts +10 -0
  127. package/src/infrastructure/persistence/PrismaHmacNonceStore.ts +29 -0
  128. package/src/infrastructure/persistence/PrismaHumanTaskStore.ts +156 -0
  129. package/src/infrastructure/persistence/PrismaMigrationDeployer.ts +53 -383
  130. package/src/infrastructure/persistence/PrismaMigrationOperations.ts +401 -0
  131. package/src/infrastructure/persistence/PrismaWorkflowRunRepository.ts +39 -0
  132. package/src/mcp/AgentMcpIntegrationImpl.ts +5 -1
  133. package/src/pairing/HmacNonceStore.ts +14 -0
  134. package/src/pairing/HmacNonceStoreToken.ts +4 -0
  135. package/src/pairing/HmacRequestSigner.ts +10 -1
  136. package/src/pairing/InMemoryHmacNonceStore.ts +24 -0
  137. package/src/pairing/IncomingHmacVerifier.ts +28 -12
  138. package/src/pairing/InternalHmacAuthMiddleware.ts +1 -1
  139. package/src/pairing/index.ts +3 -0
  140. package/src/presentation/config/CodemationAuthoring.types.ts +7 -1
  141. package/src/presentation/config/CodemationConfig.ts +6 -0
  142. package/src/presentation/http/ApiPaths.ts +14 -0
  143. package/src/presentation/http/HeadlessHttpServerFactory.ts +56 -0
  144. package/src/presentation/http/hono/HonoHttpAnonymousRoutePolicyRegistry.ts +4 -0
  145. package/src/presentation/http/hono/registrars/CredentialHonoApiRouteRegistrar.ts +1 -0
  146. package/src/presentation/http/hono/registrars/HitlDecideHonoApiRouteRegistrar.ts +54 -0
  147. package/src/presentation/http/hono/registrars/HitlInternalCallbackHonoApiRouteRegistrar.ts +33 -0
  148. package/src/presentation/http/hono/registrars/HitlResumeHonoApiRouteRegistrar.ts +43 -0
  149. package/src/presentation/http/routeHandlers/CredentialHttpRouteHandler.ts +9 -0
  150. package/src/presentation/http/routeHandlers/OAuth2HttpRouteHandlerFactory.ts +1 -1
  151. package/src/presentation/server/CodemationConsumerConfigLoader.ts +7 -2
  152. package/src/presentation/websocket/WorkflowWebsocketServerFactory.ts +16 -0
  153. package/src/server.ts +7 -2
  154. package/src/workflows/InternalWorkflowTestRunRegistrar.ts +9 -0
  155. package/tsconfig.json +1 -0
  156. package/dist/AppConfigFactory-Cx4qQvRk.js.map +0 -1
  157. package/dist/AppContainerFactory-DqKYCRNP.js.map +0 -1
  158. package/dist/CodemationAuthoring.types-NGkBcmmT.js.map +0 -1
  159. package/dist/CodemationConsumerConfigLoader-GYpBBvqE.js.map +0 -1
  160. package/dist/CredentialServices-Dk8yypeL.js.map +0 -1
  161. package/dist/InternalPingRegistrar-DY3kSfxP.js.map +0 -1
  162. package/dist/persistenceServer-C-hH4z6l.js.map +0 -1
  163. package/dist/persistenceServer-CeTHtC6E.d.ts +0 -30
  164. package/src/credentials/catalogTypes.ts +0 -4
@@ -132,6 +132,10 @@ export class ApiPaths {
132
132
  return `${this.credentialsBasePath}/instances`;
133
133
  }
134
134
 
135
+ static credentialApps(): string {
136
+ return `${this.credentialsBasePath}/apps`;
137
+ }
138
+
135
139
  static credentialInstance(instanceId: string, withSecrets?: boolean): string {
136
140
  const base = `${this.credentialInstances()}/${encodeURIComponent(instanceId)}`;
137
141
  return withSecrets ? `${base}?withSecrets=1` : base;
@@ -279,4 +283,14 @@ export class ApiPaths {
279
283
  static internalAuthBootstrap(): string {
280
284
  return `${this.bootstrapBasePath}/auth/internal`;
281
285
  }
286
+
287
+ /** Token-authenticated: resume a suspended HITL task. */
288
+ static hitlTaskResume(taskId: string): string {
289
+ return `${this.apiBasePath}/hitl/tasks/${encodeURIComponent(taskId)}/resume`;
290
+ }
291
+
292
+ /** Session-authenticated: record a decision on a suspended HITL task. */
293
+ static hitlTaskDecide(taskId: string): string {
294
+ return `${this.apiBasePath}/hitl/tasks/${encodeURIComponent(taskId)}/decide`;
295
+ }
282
296
  }
@@ -0,0 +1,56 @@
1
+ import { createServer, type Server } from "node:http";
2
+ import type { Logger } from "../../application/logging/Logger";
3
+ import type { CodemationHonoApiApp } from "./hono/CodemationHonoApiAppFactory";
4
+
5
+ /**
6
+ * Creates a Node.js http.Server that bridges IncomingMessage to Hono's Fetch API.
7
+ * Used by {@link import("../../bootstrap/runtime/HeadlessApiRuntime").HeadlessApiRuntime}
8
+ * to serve the Hono API without Next.js.
9
+ */
10
+ export class HeadlessHttpServerFactory {
11
+ create(honoApp: CodemationHonoApiApp, port: number, logger: Logger): Server {
12
+ return createServer((req, res) => {
13
+ const url = new URL(req.url ?? "/", `http://${req.headers.host ?? `127.0.0.1:${port}`}`);
14
+ const headers = new Headers();
15
+ for (const [key, value] of Object.entries(req.headers)) {
16
+ if (value === undefined) continue;
17
+ if (Array.isArray(value)) {
18
+ for (const v of value) headers.append(key, v);
19
+ } else {
20
+ headers.set(key, value);
21
+ }
22
+ }
23
+ const chunks: Buffer[] = [];
24
+ req.on("data", (chunk: Buffer) => chunks.push(chunk));
25
+ req.on("end", () => {
26
+ // eslint-disable-next-line codemation/no-buffer-everything -- node:http bridge; no streaming alternative when adapting IncomingMessage to Fetch API Request
27
+ const body = chunks.length > 0 ? Buffer.concat(chunks) : null;
28
+ const fetchRequest = new Request(url, {
29
+ method: req.method ?? "GET",
30
+ headers,
31
+ body: body?.byteLength ? body : undefined,
32
+ // @ts-expect-error — Node's Request needs duplex for streaming; required in some runtimes
33
+ duplex: "half",
34
+ });
35
+ Promise.resolve(honoApp.fetch(fetchRequest))
36
+ .then(async (fetchResponse: Response) => {
37
+ const responseHeaders: Record<string, string> = {};
38
+ fetchResponse.headers.forEach((value, key) => {
39
+ responseHeaders[key] = value;
40
+ });
41
+ res.writeHead(fetchResponse.status, responseHeaders);
42
+ // eslint-disable-next-line codemation/no-buffer-everything -- node:http bridge; Hono Fetch Response must be fully buffered to write to ServerResponse
43
+ const responseBody = await fetchResponse.arrayBuffer();
44
+ res.end(Buffer.from(responseBody));
45
+ })
46
+ .catch((err: unknown) => {
47
+ logger.error("Unhandled request error", err instanceof Error ? err : new Error(String(err)));
48
+ if (!res.headersSent) {
49
+ res.writeHead(500);
50
+ res.end("Internal server error");
51
+ }
52
+ });
53
+ });
54
+ });
55
+ }
56
+ }
@@ -35,6 +35,10 @@ export class HonoHttpAnonymousRoutePolicy {
35
35
  if (pathname === ApiPaths.whitelabelLogo()) {
36
36
  return true;
37
37
  }
38
+ // HITL token-authenticated resume endpoint — token is the auth, no session required
39
+ if (/^\/api\/hitl\/tasks\/[^/]+\/resume$/.test(pathname)) {
40
+ return true;
41
+ }
38
42
  return false;
39
43
  }
40
44
  }
@@ -8,6 +8,7 @@ export class CredentialHonoApiRouteRegistrar implements HonoApiRouteRegistrar {
8
8
  constructor(@inject(CredentialHttpRouteHandler) private readonly handler: CredentialHttpRouteHandler) {}
9
9
 
10
10
  register(app: Hono): void {
11
+ app.get("/credentials/apps", (_c) => this.handler.getCredentialApps());
11
12
  app.get("/credentials/types", (_c) => this.handler.getCredentialTypes());
12
13
  app.get("/credentials/env-status", (_c) => this.handler.getCredentialFieldEnvStatus());
13
14
  app.get("/credentials/instances", (_c) => this.handler.getCredentialInstances());
@@ -0,0 +1,54 @@
1
+ import { inject, injectable } from "@codemation/core";
2
+ import type { JsonValue } from "@codemation/core";
3
+ import { Hono } from "hono";
4
+ import { DecideHumanTaskCommandHandler } from "../../../../application/hitl/DecideHumanTaskCommandHandler";
5
+ import { HttpRequestJsonBodyReader } from "../../HttpRequestJsonBodyReader";
6
+ import { ServerHttpErrorResponseFactory } from "../../ServerHttpErrorResponseFactory";
7
+ import type { HonoApiRouteRegistrar } from "../HonoApiRouteRegistrar";
8
+ import { PairingConfigToken } from "../../../../pairing/PairingConfigToken";
9
+ import type { PairingConfig } from "../../../../pairing/pairing.types";
10
+
11
+ /**
12
+ * Session-authenticated endpoint: `POST /api/hitl/tasks/:taskId/decide`
13
+ *
14
+ * Registered ONLY in non-managed mode. Used by the local /dev/inbox UI.
15
+ *
16
+ * In managed mode (`PairingConfig !== null`) the route is intentionally NOT mounted —
17
+ * decisions must arrive via the HMAC-signed `POST /internal/hitl/tasks/:taskId/callback`
18
+ * receiver from the control plane. This prevents a compromised user session
19
+ * from deciding arbitrary pending tasks.
20
+ *
21
+ * The session middleware is already applied on the /api sub-app by CodemationHonoApiAppFactory.
22
+ */
23
+ @injectable()
24
+ export class HitlDecideHonoApiRouteRegistrar implements HonoApiRouteRegistrar {
25
+ constructor(
26
+ @inject(DecideHumanTaskCommandHandler) private readonly handler: DecideHumanTaskCommandHandler,
27
+ @inject(PairingConfigToken, { isOptional: true })
28
+ private readonly pairingConfig: PairingConfig | null = null,
29
+ ) {}
30
+
31
+ register(app: Hono): void {
32
+ if (this.pairingConfig !== null) {
33
+ // Managed mode — decisions only via HMAC callback. Do not mount the session-auth route.
34
+ return;
35
+ }
36
+ app.post("/hitl/tasks/:taskId/decide", async (c) => {
37
+ try {
38
+ const taskId = c.req.param("taskId");
39
+ const body = await HttpRequestJsonBodyReader.readJsonBody<{
40
+ decision: JsonValue;
41
+ decidedBy?: { actorId: string; displayName?: string };
42
+ }>(c.req.raw);
43
+ const result = await this.handler.decide({
44
+ taskId,
45
+ decision: body.decision,
46
+ decidedBy: body.decidedBy ?? { actorId: "session-user" },
47
+ });
48
+ return c.json(result);
49
+ } catch (error) {
50
+ return ServerHttpErrorResponseFactory.fromUnknown(error);
51
+ }
52
+ });
53
+ }
54
+ }
@@ -0,0 +1,33 @@
1
+ import { inject, injectable } from "@codemation/core";
2
+ import type { Hono } from "hono";
3
+ import { InternalHmacAuthMiddleware } from "../../../../pairing/InternalHmacAuthMiddleware";
4
+ import { HitlCallbackHandler } from "../../../../application/hitl/HitlCallbackHandler";
5
+ import type { InternalHonoApiRouteRegistrar } from "../InternalHonoApiRouteRegistrar";
6
+
7
+ /**
8
+ * Registers `POST /internal/hitl/tasks/:taskId/callback` — HMAC-verified endpoint
9
+ * that receives decision callbacks from the control plane and forwards them to
10
+ * `HitlCallbackHandler`.
11
+ *
12
+ * The HMAC middleware verifies the request is signed by the paired CP.
13
+ * `HitlCallbackHandler` additionally asserts the task's workspace matches the
14
+ * pairing config workspace.
15
+ */
16
+ @injectable()
17
+ export class HitlInternalCallbackHonoApiRouteRegistrar implements InternalHonoApiRouteRegistrar {
18
+ constructor(
19
+ @inject(InternalHmacAuthMiddleware) private readonly hmacMiddleware: InternalHmacAuthMiddleware,
20
+ @inject(HitlCallbackHandler) private readonly callbackHandler: HitlCallbackHandler,
21
+ ) {}
22
+
23
+ register(app: Hono): void {
24
+ app.post("/internal/hitl/tasks/:taskId/callback", this.hmacMiddleware.handle(), async (c) => {
25
+ const taskId = c.req.param("taskId");
26
+ const rawBody = c.get("body" as never) as string | undefined;
27
+ const body = rawBody ? JSON.parse(rawBody) : await c.req.json();
28
+
29
+ const result = await this.callbackHandler.handle(taskId, body);
30
+ return c.json(result.body, result.status);
31
+ });
32
+ }
33
+ }
@@ -0,0 +1,43 @@
1
+ import { inject, injectable } from "@codemation/core";
2
+ import type { JsonValue } from "@codemation/core";
3
+ import { Hono } from "hono";
4
+ import { DecideHumanTaskCommandHandler } from "../../../../application/hitl/DecideHumanTaskCommandHandler";
5
+ import { HttpRequestJsonBodyReader } from "../../HttpRequestJsonBodyReader";
6
+ import { ServerHttpErrorResponseFactory } from "../../ServerHttpErrorResponseFactory";
7
+ import type { HonoApiRouteRegistrar } from "../HonoApiRouteRegistrar";
8
+
9
+ /**
10
+ * Token-authenticated (unauthenticated session) endpoint:
11
+ * `POST /api/hitl/tasks/:taskId/resume?token=<signed>`
12
+ *
13
+ * This endpoint is declared as an anonymous route in `HonoHttpAnonymousRoutePolicyRegistry`
14
+ * so the session middleware is bypassed. The HMAC-signed token is the auth mechanism.
15
+ *
16
+ * Used by local inbox and future magic-link channels (Slack, email).
17
+ */
18
+ @injectable()
19
+ export class HitlResumeHonoApiRouteRegistrar implements HonoApiRouteRegistrar {
20
+ constructor(@inject(DecideHumanTaskCommandHandler) private readonly handler: DecideHumanTaskCommandHandler) {}
21
+
22
+ register(app: Hono): void {
23
+ app.post("/hitl/tasks/:taskId/resume", async (c) => {
24
+ try {
25
+ const taskId = c.req.param("taskId");
26
+ const token = c.req.query("token") ?? "";
27
+
28
+ // Validate the signed token (replaces session auth for this endpoint)
29
+ await this.handler.validateResumeToken({ taskId, token });
30
+
31
+ const body = await HttpRequestJsonBodyReader.readJsonBody<{ decision: JsonValue }>(c.req.raw);
32
+ const result = await this.handler.decide({
33
+ taskId,
34
+ decision: body.decision,
35
+ decidedBy: { actorId: "token-bearer" },
36
+ });
37
+ return c.json(result);
38
+ } catch (error) {
39
+ return ServerHttpErrorResponseFactory.fromUnknown(error);
40
+ }
41
+ });
42
+ }
43
+ }
@@ -16,6 +16,7 @@ import type {
16
16
  UpsertCredentialBindingRequest,
17
17
  } from "../../../application/contracts/CredentialContractsRegistry";
18
18
  import {
19
+ GetCredentialAppsQuery,
19
20
  GetCredentialFieldEnvStatusQuery,
20
21
  GetCredentialInstanceQuery,
21
22
  GetCredentialInstanceWithSecretsQuery,
@@ -66,6 +67,14 @@ export class CredentialHttpRouteHandler {
66
67
  }
67
68
  }
68
69
 
70
+ async getCredentialApps(): Promise<Response> {
71
+ try {
72
+ return Response.json(await this.queryBus.execute(new GetCredentialAppsQuery()));
73
+ } catch (error) {
74
+ return ServerHttpErrorResponseFactory.fromUnknown(error);
75
+ }
76
+ }
77
+
69
78
  async getCredentialInstance(request: Request, params: ServerHttpRouteParams): Promise<Response> {
70
79
  try {
71
80
  const withSecrets = new URL(request.url).searchParams.get("withSecrets") === "1";
@@ -87,7 +87,7 @@ export class OAuth2HttpRouteHandler {
87
87
  const stateToken = url.searchParams.get("state")?.trim();
88
88
  if (!code || !stateToken) {
89
89
  return new Response(
90
- this.createPopupHtml({ kind: "oauth2.error", message: "Missing code or state parameter." }),
90
+ this.createPopupHtml({ kind: "oauth2.error", message: "Missing code and state parameters." }),
91
91
  {
92
92
  status: 400,
93
93
  headers: { "content-type": "text/html; charset=utf-8" },
@@ -38,6 +38,7 @@ export class CodemationConsumerConfigLoader {
38
38
  private readonly performanceDiagnosticsLogger = new ServerLoggerFactory(
39
39
  logLevelPolicyFactory,
40
40
  ).createPerformanceDiagnostics("codemation-config-loader.timing");
41
+ private readonly bootLogger = new ServerLoggerFactory(logLevelPolicyFactory).create("codemation.boot");
41
42
  /**
42
43
  * In-flight + completed load promises keyed by `${consumerRoot}|${configPathOverride}`. The
43
44
  * boot path constructs MULTIPLE CodemationConsumerConfigLoader instances (one inside the CLI's
@@ -109,6 +110,9 @@ export class CodemationConsumerConfigLoader {
109
110
  throw new Error(`Config file does not export a Codemation config object: ${bootstrapSource}`);
110
111
  }
111
112
  const config = this.configNormalizer.normalize(rawConfig);
113
+ if (rawConfig.codemationVersion) {
114
+ this.bootLogger.info(`codemationVersion: ${rawConfig.codemationVersion}`);
115
+ }
112
116
  const workflowSources = await BootTimer.measureAsync("config.resolveWorkflowSources", () =>
113
117
  this.resolveWorkflowSources(args.consumerRoot, config),
114
118
  );
@@ -190,8 +194,9 @@ export class CodemationConsumerConfigLoader {
190
194
  workflowDiscoveryDirectories,
191
195
  absoluteWorkflowModulePath: workflowSource,
192
196
  }),
193
- moduleExports: await BootTimer.measureAsync(`workflow.${path.basename(workflowSource).replace(/\.tsx?$/, "")}`, () =>
194
- this.importModule(workflowSource, importSession),
197
+ moduleExports: await BootTimer.measureAsync(
198
+ `workflow.${path.basename(workflowSource).replace(/\.tsx?$/, "")}`,
199
+ () => this.importModule(workflowSource, importSession),
195
200
  ),
196
201
  })),
197
202
  );
@@ -0,0 +1,16 @@
1
+ import type { AppConfig } from "../config/AppConfig";
2
+ import { WorkflowWebsocketServer } from "./WorkflowWebsocketServer";
3
+ import { logLevelPolicyFactory } from "../../infrastructure/logging/LogLevelPolicyFactory";
4
+ import { ServerLoggerFactory } from "../../infrastructure/logging/ServerLoggerFactory";
5
+
6
+ const loggerFactory = new ServerLoggerFactory(logLevelPolicyFactory);
7
+
8
+ export class WorkflowWebsocketServerFactory {
9
+ create(appConfig: AppConfig): WorkflowWebsocketServer {
10
+ return new WorkflowWebsocketServer(
11
+ appConfig.webSocketPort,
12
+ appConfig.webSocketBindHost,
13
+ loggerFactory.create("codemation-websocket.server"),
14
+ );
15
+ }
16
+ }
package/src/server.ts CHANGED
@@ -1,5 +1,10 @@
1
- export { CodemationPostgresPrismaClientFactory } from "./persistenceServer";
2
- export type { PrismaClient } from "./persistenceServer";
1
+ // Direct re-export from source files, NOT via the persistenceServer barrel: that
2
+ // barrel also re-exports PrismaMigrationDeployer, whose createRequire + dynamic
3
+ // require.resolve trips the Turbopack module tracer ("whole project traced
4
+ // unintentionally") when this server barrel is transitively reached from a
5
+ // Next.js Server Component such as the dev/inbox page.
6
+ export { CodemationPostgresPrismaClientFactory } from "./infrastructure/persistence/CodemationPostgresPrismaClientFactory";
7
+ export type { PrismaDatabaseClient as PrismaClient } from "./infrastructure/persistence/PrismaDatabaseClient";
3
8
  export { ExecaProcessRunner } from "./process/ExecaProcessRunner";
4
9
  export type { ProcessRunner, ProcessRunOptions, ProcessRunResult } from "./process/ProcessRunner.types";
5
10
  export { ApiPaths } from "./presentation/http/ApiPaths";
@@ -79,6 +79,15 @@ export class InternalWorkflowTestRunRegistrar implements InternalHonoApiRouteReg
79
79
  });
80
80
  }
81
81
 
82
+ if (runResult.status === "halted") {
83
+ return c.json({
84
+ ok: false,
85
+ runId: runResult.runId,
86
+ error: `Run halted: ${runResult.reason}`,
87
+ durationMs: Date.now() - startMs,
88
+ });
89
+ }
90
+
82
91
  // completed
83
92
  return c.json({
84
93
  ok: true,
package/tsconfig.json CHANGED
@@ -28,6 +28,7 @@
28
28
  "@codemation/host/dev-server-sidecar": ["./src/devServerSidecar.ts"],
29
29
  "@codemation/host/persistence": ["./src/persistenceServer.ts"],
30
30
  "@codemation/canvas": ["../canvas/src/index.ts"],
31
+ "@codemation/canvas-core": ["../canvas-core/src/index.ts"],
31
32
  "@codemation/next-host/src/*": ["../next-host/src/*"],
32
33
  "@/*": ["../next-host/src/*"]
33
34
  }