@aifabrix/builder 2.49.2 → 2.50.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 (409) hide show
  1. package/.cursor/rules/deploy-rules/docker-image-ip-policy.mdc +49 -0
  2. package/.cursor/rules/plan-rules/application-lifecycle.mdc +30 -0
  3. package/.cursor/rules/plan-rules/plan-frontmatter.mdc +35 -0
  4. package/.cursor/rules/plan-rules/plan-lifecycle.mdc +31 -0
  5. package/.cursor/rules/plan-rules/plan-validation-gate.mdc +34 -0
  6. package/.cursor/skills/README.md +1 -0
  7. package/.dockerignore +52 -0
  8. package/.eslintignore +22 -0
  9. package/README.md +2 -0
  10. package/bin/builder-api-cleanup.js +26 -0
  11. package/bin/builder-api-nest.js +10 -0
  12. package/bin/builder-api-spike.js +19 -0
  13. package/enterprise-knowledge/README.md +50 -0
  14. package/enterprise-knowledge/enterprise-integration-lifecycle/README.md +22 -0
  15. package/enterprise-knowledge/enterprise-integration-lifecycle/application.json +15 -0
  16. package/enterprise-knowledge/enterprise-integration-lifecycle/deploy.js +69 -0
  17. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-deploy.json +52 -0
  18. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-system.json +29 -0
  19. package/enterprise-knowledge/enterprise-integration-lifecycle/env.template +2 -0
  20. package/integration/README.md +81 -0
  21. package/integration/github/README.md +53 -0
  22. package/integration/github/application.json +21 -0
  23. package/integration/github/deploy.js +72 -0
  24. package/integration/github/env.template +11 -0
  25. package/integration/github/github-datasource-issue-comments.json +239 -0
  26. package/integration/github/github-datasource-issues.json +399 -0
  27. package/integration/github/github-datasource-repository.json +354 -0
  28. package/integration/github/github-deploy.json +1202 -0
  29. package/integration/github/github-system.json +82 -0
  30. package/integration/hubspot-test/README.md +1 -1
  31. package/jest.isolated-projects.js +31 -1
  32. package/jest.projects.js +87 -1
  33. package/lib/api/dev-server-https.js +1 -1
  34. package/lib/api/enterprise-knowledge.api.js +63 -0
  35. package/lib/api/evidence.api.js +172 -0
  36. package/lib/api/external-systems.api.js +8 -2
  37. package/lib/api/resource-types.api.js +1 -0
  38. package/lib/api/types/enterprise-knowledge.types.js +46 -0
  39. package/lib/api/types/evidence.types.js +67 -0
  40. package/lib/api/types/resource-types.types.js +2 -0
  41. package/lib/app/controller-application-filter.js +36 -0
  42. package/lib/app/deploy.js +1 -1
  43. package/lib/app/list-external-systems.js +130 -0
  44. package/lib/app/list.js +20 -8
  45. package/lib/app/run.js +3 -1
  46. package/lib/app/show-display.js +8 -3
  47. package/lib/app/show-online.js +178 -0
  48. package/lib/app/show.js +37 -88
  49. package/lib/build/builder-api-docker-staging.js +47 -0
  50. package/lib/build/miso-controller-npm-sdk-guard.js +2 -2
  51. package/lib/build/standard-docker-build.js +2 -0
  52. package/lib/certification/post-unified-cert-sync.js +4 -31
  53. package/lib/certification/sync-after-external-command.js +0 -1
  54. package/lib/certification/sync-system-certification.js +0 -3
  55. package/lib/channels/add-channel.js +40 -34
  56. package/lib/channels/approval-guides/chatgpt.js +33 -0
  57. package/lib/channels/approval-guides/credential-portal.js +38 -0
  58. package/lib/channels/approval-guides/index.js +110 -0
  59. package/lib/channels/approval-guides/microsoft-copilot.js +34 -0
  60. package/lib/channels/approval-guides/slack.js +34 -0
  61. package/lib/channels/approval-guides/teams-bot.js +35 -0
  62. package/lib/channels/approval-guides/web-embed.js +32 -0
  63. package/lib/channels/channel-approval-guide.js +11 -0
  64. package/lib/channels/channel-artifact-generator.js +16 -4
  65. package/lib/channels/channel-audit.js +3 -0
  66. package/lib/channels/channel-auth-context.js +1 -1
  67. package/lib/channels/channel-description.js +59 -0
  68. package/lib/channels/channel-integration-generator.js +13 -230
  69. package/lib/channels/channel-integration-template.js +94 -0
  70. package/lib/channels/channel-runtime-resolver.js +43 -3
  71. package/lib/channels/channel-status-display.js +66 -26
  72. package/lib/channels/channel-targets.js +3 -3
  73. package/lib/cli/index.js +27 -3
  74. package/lib/cli/infra-guided.js +5 -1
  75. package/lib/cli/setup-app-external-create.js +111 -0
  76. package/lib/cli/setup-app.help.js +1 -1
  77. package/lib/cli/setup-app.js +38 -125
  78. package/lib/cli/setup-app.test-commands.js +26 -52
  79. package/lib/cli/setup-certification-commands.js +4 -4
  80. package/lib/cli/setup-certification.help.js +9 -9
  81. package/lib/cli/setup-dev.js +7 -2
  82. package/lib/cli/setup-external-system-help.js +4 -6
  83. package/lib/cli/setup-external-system.js +58 -52
  84. package/lib/cli/setup-infra-up-builder-api-action.js +64 -0
  85. package/lib/cli/setup-infra-up-platform-action.js +3 -1
  86. package/lib/cli/setup-infra.js +15 -0
  87. package/lib/cli/setup-list.js +43 -0
  88. package/lib/cli/setup-utility-repair.js +26 -7
  89. package/lib/cli/setup-utility.js +5 -3
  90. package/lib/commands/app.js +15 -43
  91. package/lib/commands/channel-add-setup.js +110 -0
  92. package/lib/commands/channel.js +42 -8
  93. package/lib/commands/datasource-sync-display.js +96 -0
  94. package/lib/commands/datasource-sync-policy.js +210 -0
  95. package/lib/commands/datasource-test-trust-cli.js +6 -6
  96. package/lib/commands/datasource-unified-test-cli.js +2 -1
  97. package/lib/commands/datasource-unified-test-cli.options.js +33 -23
  98. package/lib/commands/deploy-gate-failure-details.js +163 -0
  99. package/lib/commands/deploy-gate-orchestrator.js +322 -0
  100. package/lib/commands/dev-cli-handlers.js +1 -59
  101. package/lib/commands/dev-host-ops-params.js +91 -0
  102. package/lib/commands/dev-host-ops-poll.js +111 -0
  103. package/lib/commands/dev-host-ops.js +107 -94
  104. package/lib/commands/dev-init.js +3 -1
  105. package/lib/commands/enterprise-knowledge.js +284 -0
  106. package/lib/commands/evidence-cli-leaves.js +250 -0
  107. package/lib/commands/evidence.js +51 -0
  108. package/lib/commands/governance-cli-shared.js +2 -2
  109. package/lib/commands/governance-command-action.js +2 -2
  110. package/lib/commands/governance-fixture-subjects.js +85 -0
  111. package/lib/commands/governance.js +1 -1
  112. package/lib/commands/lifecycle-command-action.js +2 -2
  113. package/lib/commands/lifecycle-external.js +3 -3
  114. package/lib/commands/manifest-cli-auth.js +29 -0
  115. package/lib/commands/manifest-cli-commands.js +170 -0
  116. package/lib/commands/manifest-cli-integration-path.js +63 -0
  117. package/lib/commands/manifest.js +21 -0
  118. package/lib/commands/protection-cli-leaves.js +3 -3
  119. package/lib/commands/repair-auth-apply.js +15 -1
  120. package/lib/commands/repair-authentications-454.js +169 -0
  121. package/lib/commands/repair-capability-openapi-warnings.js +126 -0
  122. package/lib/commands/repair-datasource-apply.js +135 -0
  123. package/lib/commands/repair-datasource-auto-rbac.js +62 -1
  124. package/lib/commands/repair-datasource-http-service-contract.js +239 -0
  125. package/lib/commands/repair-datasource-operation-security.js +180 -0
  126. package/lib/commands/repair-datasource-run.js +7 -2
  127. package/lib/commands/repair-datasource-sync-operations.js +177 -0
  128. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +133 -0
  129. package/lib/commands/repair-datasource.js +12 -68
  130. package/lib/commands/repair-env-template.js +11 -1
  131. package/lib/commands/repair-external-run.js +121 -31
  132. package/lib/commands/repair-internal.js +1 -1
  133. package/lib/commands/repair-openapi-export.js +156 -0
  134. package/lib/commands/repair-openapi-sync.js +19 -2
  135. package/lib/commands/repair-orchestration-datasource-split.js +259 -0
  136. package/lib/commands/repair-persist.js +22 -1
  137. package/lib/commands/repair-rbac-platform-preset.js +204 -0
  138. package/lib/commands/repair-rbac-reader-roles.js +191 -0
  139. package/lib/commands/repair-rbac.js +163 -86
  140. package/lib/commands/repair-steps.js +83 -18
  141. package/lib/commands/repair-system-permissions.js +1 -0
  142. package/lib/commands/repair.js +1 -1
  143. package/lib/commands/resource-type.js +65 -26
  144. package/lib/commands/run-post-publish-data-sync.js +68 -0
  145. package/lib/commands/setup-modes.js +1 -1
  146. package/lib/commands/test-e2e-external.js +21 -8
  147. package/lib/commands/test-governance-cli-options.js +2 -2
  148. package/lib/commands/test-trust-cli-options.js +2 -2
  149. package/lib/commands/up-builder-api-bootstrap.js +71 -0
  150. package/lib/commands/up-builder-api-provision.js +241 -0
  151. package/lib/commands/up-builder-api.js +88 -0
  152. package/lib/commands/up-common.js +25 -1
  153. package/lib/commands/upload-credential-push-log.js +117 -0
  154. package/lib/commands/upload-data-sync-finalize.js +60 -0
  155. package/lib/commands/upload-probe.js +66 -0
  156. package/lib/commands/upload-publish-context.js +4 -1
  157. package/lib/commands/upload-sidecar-followups.js +55 -27
  158. package/lib/commands/upload.js +116 -157
  159. package/lib/commands/verify-governance-combined-external.js +1 -1
  160. package/lib/commands/verify-governance-db.js +2 -2
  161. package/lib/commands/verify-operations-command-action.js +2 -2
  162. package/lib/commands/verify-operations-step-plan.js +2 -2
  163. package/lib/commands/verify-operations-steps.js +2 -2
  164. package/lib/commands/verify-trust-command-action.js +1 -1
  165. package/lib/commands/wizard-core.js +1 -1
  166. package/lib/commands/wizard-headless.js +2 -1
  167. package/lib/core/admin-secrets.js +2 -6
  168. package/lib/core/secrets-env-content.js +10 -4
  169. package/lib/core/secrets-env-write.js +2 -13
  170. package/lib/datasource/agent-trust-run.js +3 -3
  171. package/lib/datasource/deploy.js +3 -1
  172. package/lib/datasource/test-e2e.js +6 -1
  173. package/lib/datasource/unified-validation-run-body.js +4 -1
  174. package/lib/datasource/unified-validation-run.js +1 -1
  175. package/lib/deployment/deploy-poll-ui.js +73 -12
  176. package/lib/deployment/deployer-poll-run.js +113 -0
  177. package/lib/deployment/deployer.js +17 -87
  178. package/lib/evidence/dataplane-context.js +21 -0
  179. package/lib/evidence/evidence-display.js +78 -0
  180. package/lib/evidence/evidence-service.js +392 -0
  181. package/lib/external-system/deploy.js +290 -112
  182. package/lib/external-system/download.js +7 -3
  183. package/lib/external-system/test-execution.js +1 -1
  184. package/lib/external-system/test-system-level.js +8 -10
  185. package/lib/external-system/test.js +12 -6
  186. package/lib/generator/deploy-script.js +73 -0
  187. package/lib/generator/external.js +2 -1
  188. package/lib/generator/index.js +17 -2
  189. package/lib/generator/wizard.js +3 -36
  190. package/lib/internal/fs-real-sync.js +22 -1
  191. package/lib/internal/node-fs.js +2 -0
  192. package/lib/lifecycle/lifecycle-governance-options.js +1 -1
  193. package/lib/lifecycle/verify-step-progress.js +1 -1
  194. package/lib/programmatic/access-log.js +26 -0
  195. package/lib/programmatic/auth-config.js +44 -0
  196. package/lib/programmatic/bearer-auth.js +176 -0
  197. package/lib/programmatic/builder-api-request.js +175 -0
  198. package/lib/programmatic/builder-api-route-dispatch.js +82 -0
  199. package/lib/programmatic/builder-api-runtime-config.js +42 -0
  200. package/lib/programmatic/builder-api-server.js +230 -0
  201. package/lib/programmatic/builder-help-authentication-catalog.js +282 -0
  202. package/lib/programmatic/builder-help-connected-system-ui.js +266 -0
  203. package/lib/programmatic/builder-help-enterprise-sync-fabrix.js +60 -0
  204. package/lib/programmatic/builder-help-envelope.js +36 -0
  205. package/lib/programmatic/builder-help-fixtures.js +200 -0
  206. package/lib/programmatic/builder-help-governance.js +172 -0
  207. package/lib/programmatic/builder-help-schema-catalog.js +92 -0
  208. package/lib/programmatic/builder-help.js +361 -0
  209. package/lib/programmatic/capability-registry.js +77 -0
  210. package/lib/programmatic/command-options.js +32 -0
  211. package/lib/programmatic/enterprise-mcp-client.js +98 -0
  212. package/lib/programmatic/http-problem-details.js +99 -0
  213. package/lib/programmatic/http-route-match.js +40 -0
  214. package/lib/programmatic/idempotency-cache.js +69 -0
  215. package/lib/programmatic/index.js +53 -0
  216. package/lib/programmatic/integration-path-scope.js +39 -0
  217. package/lib/programmatic/log-redaction.js +66 -0
  218. package/lib/programmatic/manifest-get-section.js +146 -0
  219. package/lib/programmatic/manifest-patch-section.js +252 -0
  220. package/lib/programmatic/manifest-repair.js +122 -0
  221. package/lib/programmatic/manifest-section-dependency-graph.js +160 -0
  222. package/lib/programmatic/manifest-section-io.js +225 -0
  223. package/lib/programmatic/manifest-section-quality.js +171 -0
  224. package/lib/programmatic/manifest-section-registry-data.js +269 -0
  225. package/lib/programmatic/manifest-section-registry.js +205 -0
  226. package/lib/programmatic/nest-http-bridge.js +34 -0
  227. package/lib/programmatic/openapi-component-schemas.js +68 -0
  228. package/lib/programmatic/openapi-description-enricher.js +117 -0
  229. package/lib/programmatic/openapi-descriptions.js +190 -0
  230. package/lib/programmatic/openapi-docs-serving.js +123 -0
  231. package/lib/programmatic/openapi-help-schemas.js +63 -0
  232. package/lib/programmatic/openapi-manifest-schemas.js +141 -0
  233. package/lib/programmatic/openapi-operation-bindings.js +274 -0
  234. package/lib/programmatic/openapi-request-schemas.js +155 -0
  235. package/lib/programmatic/openapi-result-schemas.js +476 -0
  236. package/lib/programmatic/openapi-schema-descriptions-certify.js +139 -0
  237. package/lib/programmatic/openapi-schema-descriptions-core.js +159 -0
  238. package/lib/programmatic/openapi-schema-descriptions-help.js +40 -0
  239. package/lib/programmatic/openapi-schema-descriptions-manifest.js +143 -0
  240. package/lib/programmatic/openapi-schema-descriptions-mcp.js +118 -0
  241. package/lib/programmatic/openapi-schema-descriptions-operations.js +281 -0
  242. package/lib/programmatic/openapi-schema-helpers.js +93 -0
  243. package/lib/programmatic/openapi-spec.js +203 -0
  244. package/lib/programmatic/orchestration-openapi-meta.js +159 -0
  245. package/lib/programmatic/platform-rbac-catalog.js +90 -0
  246. package/lib/programmatic/request-timeout.js +53 -0
  247. package/lib/programmatic/route-handler-map.js +143 -0
  248. package/lib/programmatic/route-handlers-context.js +29 -0
  249. package/lib/programmatic/route-handlers.js +33 -0
  250. package/lib/programmatic/run-certify.js +57 -0
  251. package/lib/programmatic/run-enterprise-mcp.js +32 -0
  252. package/lib/programmatic/run-lifecycle.js +29 -0
  253. package/lib/programmatic/run-repair.js +41 -0
  254. package/lib/programmatic/run-repository-emcp.js +54 -0
  255. package/lib/programmatic/run-template-workspace.js +61 -0
  256. package/lib/programmatic/run-test.js +49 -0
  257. package/lib/programmatic/run-upload.js +63 -0
  258. package/lib/programmatic/run-validate.js +53 -0
  259. package/lib/programmatic/run-wizard.js +40 -0
  260. package/lib/programmatic/system-key.js +48 -0
  261. package/lib/programmatic/wizard-config-normalize.js +118 -0
  262. package/lib/programmatic/workspace-cleanup.js +128 -0
  263. package/lib/programmatic/workspace-context.js +145 -0
  264. package/lib/programmatic/workspace-lock.js +75 -0
  265. package/lib/programmatic/workspace-meta.js +64 -0
  266. package/lib/programmatic/workspace-ops.js +149 -0
  267. package/lib/protection/run-commands-upload-sync.js +35 -0
  268. package/lib/protection/run-commands.js +2 -20
  269. package/lib/protection/upload-batch.js +11 -7
  270. package/lib/resolvers/resource-type-file.js +22 -0
  271. package/lib/schema/external-datasource.schema.json +161 -1
  272. package/lib/schema/external-system.schema.json +244 -118
  273. package/lib/schema/flag-map-validation-run.json +3 -0
  274. package/lib/schema/type/credential-field-registry.json +2 -2
  275. package/lib/schema/type/document-storage.json +9 -0
  276. package/lib/utils/agent-trust-run-display.js +1 -1
  277. package/lib/utils/app-path-resolve.js +168 -0
  278. package/lib/utils/authentication-env-template.js +222 -0
  279. package/lib/utils/cli-sync-options.js +7 -11
  280. package/lib/utils/cli-utils.js +4 -0
  281. package/lib/utils/cli-workspace-options.js +29 -0
  282. package/lib/utils/compose-generate-docker-compose.js +102 -0
  283. package/lib/utils/configuration-env-resolver.js +203 -27
  284. package/lib/utils/datasource-test-run-certificate-tty.js +28 -2
  285. package/lib/utils/datasource-test-run-certification-debug.js +130 -0
  286. package/lib/utils/datasource-test-run-debug-display.js +2 -0
  287. package/lib/utils/datasource-test-run-debug-slice.js +7 -0
  288. package/lib/utils/datasource-test-run-display.js +2 -1
  289. package/lib/utils/dev-init-ssh-merge.js +165 -23
  290. package/lib/utils/dev-pin-share-instructions.js +110 -0
  291. package/lib/utils/dev-ssh-config-helper.js +177 -33
  292. package/lib/utils/enterprise-knowledge-paths.js +142 -0
  293. package/lib/utils/env-copy.js +2 -1
  294. package/lib/utils/env-dotenv-format.js +103 -0
  295. package/lib/utils/external-env-template.js +4 -1
  296. package/lib/utils/external-system-display.js +5 -1
  297. package/lib/utils/external-system-readiness-compact-blocks.js +221 -0
  298. package/lib/utils/external-system-readiness-compact-display.js +324 -0
  299. package/lib/utils/external-system-readiness-core.js +16 -0
  300. package/lib/utils/external-system-readiness-deploy-display.js +29 -11
  301. package/lib/utils/external-system-readiness-display-internals.js +3 -1
  302. package/lib/utils/external-system-readiness-display.js +20 -30
  303. package/lib/utils/external-system-system-test-tty-certification.js +135 -0
  304. package/lib/utils/external-system-system-test-tty.js +5 -41
  305. package/lib/utils/help-builder.js +6 -1
  306. package/lib/utils/host-ops-job-labels.js +236 -0
  307. package/lib/utils/host-ops-job-result.js +61 -0
  308. package/lib/utils/host-ops-poll-ui.js +163 -0
  309. package/lib/utils/integration-path-resolve.js +112 -0
  310. package/lib/utils/paths-reload-source.js +148 -0
  311. package/lib/utils/paths-system-builder-keys.js +25 -0
  312. package/lib/utils/paths.js +151 -26
  313. package/lib/utils/runtime-configuration-values.js +64 -2
  314. package/lib/utils/secrets-helpers.js +5 -1
  315. package/lib/utils/ssh-known-hosts.js +130 -0
  316. package/lib/utils/test-target-cli-options.js +75 -0
  317. package/lib/utils/upload-sync-options.js +4 -0
  318. package/lib/utils/validation-run-request.js +11 -2
  319. package/lib/validation/external-manifest-validator.js +11 -1
  320. package/lib/validation/openapi-contract-surface-validator.js +426 -0
  321. package/lib/validation/openapi-contract-vendor-validation.js +238 -0
  322. package/lib/validation/validate-external-file.js +160 -0
  323. package/lib/validation/validate.js +6 -117
  324. package/package.json +46 -29
  325. package/packages/builder-api/README.md +120 -0
  326. package/packages/builder-api/package.json +30 -0
  327. package/packages/builder-api/src/app.module.ts +7 -0
  328. package/packages/builder-api/src/auth/auth.module.ts +5 -0
  329. package/packages/builder-api/src/external-system/external-system.module.ts +5 -0
  330. package/packages/builder-api/src/main.ts +16 -0
  331. package/packages/builder-api/tsconfig.json +15 -0
  332. package/scripts/check-builder-api-architecture-grep.js +79 -0
  333. package/scripts/docker_image_content_gate.js +47 -0
  334. package/scripts/docker_image_content_gate.sh +5 -0
  335. package/scripts/export-builder-api-evidence.js +262 -0
  336. package/scripts/lib/builder-api-docker-staging-copy.js +60 -0
  337. package/scripts/lib/builder-api-docker-staging-core.js +183 -0
  338. package/scripts/lib/docker-image-content-gate-core.js +166 -0
  339. package/scripts/lib/plan-actor.js +109 -0
  340. package/scripts/lib/plan-config.js +20 -0
  341. package/scripts/lib/plan-gates.js +154 -0
  342. package/scripts/lib/plan-meta.js +104 -0
  343. package/scripts/lib/plan-parse.js +162 -0
  344. package/scripts/lib/plan-prevalidate-gates.js +326 -0
  345. package/scripts/lib/plan-run-core.js +125 -0
  346. package/scripts/lib/plan-run-report.js +108 -0
  347. package/scripts/plan-approve.js +24 -0
  348. package/scripts/plan-archive.js +49 -0
  349. package/scripts/plan-run.js +13 -0
  350. package/scripts/plan-set-actor.js +21 -0
  351. package/scripts/plan-validate.js +80 -0
  352. package/scripts/pnpm/af.mjs +110 -0
  353. package/scripts/pnpm/check-quiet.mjs +32 -0
  354. package/scripts/pnpm/dev-install.mjs +29 -0
  355. package/scripts/pnpm/dev-reload.mjs +18 -0
  356. package/scripts/pnpm/help.mjs +55 -0
  357. package/scripts/pnpm/lib/dev-reload-core.mjs +189 -0
  358. package/scripts/pnpm/lib/dev-reload-log-capture.mjs +67 -0
  359. package/scripts/pnpm/lib/dev-reload-log-utils.mjs +32 -0
  360. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +124 -0
  361. package/scripts/pnpm/lib/resolve-app-url.mjs +183 -0
  362. package/scripts/pnpm/lib/spinner.mjs +42 -0
  363. package/scripts/stage_builder_api_for_docker.js +65 -0
  364. package/scripts/sync-builder-api-template.js +44 -0
  365. package/templates/README.md +3 -0
  366. package/templates/applications/builder-api/Dockerfile +38 -0
  367. package/templates/applications/builder-api/application.yaml +75 -0
  368. package/templates/applications/builder-api/env.template +48 -0
  369. package/templates/applications/dataplane/application.yaml +1 -1
  370. package/templates/applications/dataplane/env.template +10 -6
  371. package/templates/applications/dataplane/rbac.yaml +21 -1
  372. package/templates/applications/miso-controller/application.yaml +1 -1
  373. package/templates/applications/miso-controller/env.template +1 -3
  374. package/templates/channels/README.md +18 -0
  375. package/templates/channels/ai-fabrix-icon.jpg +0 -0
  376. package/templates/channels/chatgpt/install.template.md.hbs +21 -24
  377. package/templates/channels/chatgpt/integration/README.md +11 -0
  378. package/templates/channels/chatgpt/integration/application.json +15 -0
  379. package/templates/channels/chatgpt/integration/chatgpt-system.json +29 -0
  380. package/templates/channels/chatgpt/integration/env.template +4 -0
  381. package/templates/channels/microsoft-copilot/install.template.md.hbs +20 -25
  382. package/templates/channels/microsoft-copilot/integration/README.md +11 -0
  383. package/templates/channels/microsoft-copilot/integration/application.json +15 -0
  384. package/templates/channels/microsoft-copilot/integration/env.template +6 -0
  385. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +34 -0
  386. package/templates/channels/slack/install.template.md.hbs +48 -18
  387. package/templates/channels/slack/integration/README.md +38 -0
  388. package/templates/channels/slack/integration/application.json +18 -0
  389. package/templates/channels/slack/integration/env.template +29 -0
  390. package/templates/channels/slack/integration/slack-datasource-conversations.json +226 -0
  391. package/templates/channels/slack/integration/slack-datasource-messages.json +269 -0
  392. package/templates/channels/slack/integration/slack-system.json +86 -0
  393. package/templates/channels/slack/manifest.template.json.hbs +20 -10
  394. package/templates/channels/teams-bot/install.template.md.hbs +31 -17
  395. package/templates/channels/teams-bot/integration/README.md +18 -0
  396. package/templates/channels/teams-bot/integration/application.json +17 -0
  397. package/templates/channels/teams-bot/integration/env.template +5 -0
  398. package/templates/channels/teams-bot/integration/teams-bot-system.json +55 -0
  399. package/templates/channels/teams-bot/manifest.template.json.hbs +27 -0
  400. package/templates/channels/web-embed/install.template.md.hbs +16 -26
  401. package/templates/channels/web-embed/integration/README.md +11 -0
  402. package/templates/channels/web-embed/integration/application.json +15 -0
  403. package/templates/channels/web-embed/integration/env.template +4 -0
  404. package/templates/channels/web-embed/integration/web-embed-system.json +29 -0
  405. package/templates/openapi/redoc_page.html +86 -0
  406. package/templates/python/docker-compose.hbs +11 -0
  407. package/templates/python/requirements.txt +1 -1
  408. package/templates/typescript/docker-compose.hbs +11 -0
  409. package/lib/certification/cli-cert-sync-skip.js +0 -21
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Generate integration deploy.js from Handlebars template.
3
+ *
4
+ * @fileoverview ESLint-clean deploy script for external integrations
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const fs = require('../internal/fs-real-sync');
12
+ const path = require('path');
13
+ const Handlebars = require('handlebars');
14
+
15
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
16
+ const logger = require('../utils/logger');
17
+
18
+ const templatesExternalDir = path.join(__dirname, '..', '..', 'templates', 'external-system');
19
+
20
+ /**
21
+ * @param {string} templateName
22
+ * @param {string} outputPath
23
+ * @param {Object} context
24
+ */
25
+ async function writeDeployScriptFromTemplate(templateName, outputPath, context) {
26
+ const templatePath = path.join(templatesExternalDir, templateName);
27
+ const content = Handlebars.compile(fs.readFileSync(templatePath, 'utf8'))(context);
28
+ fs.writeFileSync(outputPath, content, { mode: 0o644, encoding: 'utf8' });
29
+ logger.log(formatSuccessLine(`Generated ${path.basename(outputPath)}`));
30
+ }
31
+
32
+ /**
33
+ * @param {string} appPath
34
+ * @param {string} systemKey
35
+ * @param {string} systemFileName
36
+ * @param {string[]} datasourceFileNames
37
+ * @returns {Promise<{ deployJsPath: string }>}
38
+ */
39
+ async function generateDeployScripts(appPath, systemKey, systemFileName, datasourceFileNames) {
40
+ try {
41
+ const allJsonFiles = [systemFileName, ...datasourceFileNames];
42
+ const context = { systemKey, allJsonFiles, datasourceFileNames };
43
+ const deployJsPath = path.join(appPath, 'deploy.js');
44
+ await writeDeployScriptFromTemplate('deploy.js.hbs', deployJsPath, context);
45
+ return { deployJsPath };
46
+ } catch (error) {
47
+ throw new Error(`Failed to generate deployment scripts: ${error.message}`);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Regenerate deploy.js from integration manifest file lists.
53
+ *
54
+ * @param {Object} params
55
+ * @param {string} params.appPath
56
+ * @param {string} params.systemKey
57
+ * @param {string[]} params.systemFiles
58
+ * @param {string[]} params.datasourceFiles
59
+ * @returns {Promise<{ deployJsPath: string }>}
60
+ */
61
+ async function regenerateDeployScriptFromIntegration(params) {
62
+ const { appPath, systemKey, systemFiles, datasourceFiles } = params;
63
+ if (!systemFiles?.length) {
64
+ throw new Error('externalIntegration.systems is empty — cannot generate deploy.js');
65
+ }
66
+ return generateDeployScripts(appPath, systemKey, systemFiles[0], datasourceFiles || []);
67
+ }
68
+
69
+ module.exports = {
70
+ writeDeployScriptFromTemplate,
71
+ generateDeployScripts,
72
+ regenerateDeployScriptFromIntegration
73
+ };
@@ -436,6 +436,7 @@ module.exports = {
436
436
  splitExternalApplicationSchema,
437
437
  loadSystemFile,
438
438
  loadDatasourceFiles,
439
- loadExternalIntegrationConfig
439
+ loadExternalIntegrationConfig,
440
+ mergeRbacIntoSystemJson
440
441
  };
441
442
 
@@ -302,7 +302,22 @@ async function generateDeployJson(appName, options = {}) {
302
302
  if (!appName || typeof appName !== 'string') {
303
303
  throw new Error('App name is required and must be a string');
304
304
  }
305
- const { isExternal, appPath, appType } = await detectAppType(appName);
305
+ let appPath;
306
+ let isExternal;
307
+ let appType;
308
+ if (options.appPath) {
309
+ appPath = path.resolve(options.appPath);
310
+ const configPath = resolveApplicationConfigPath(appPath);
311
+ const { loadConfigFile } = require('../utils/config-format');
312
+ const variables = loadConfigFile(configPath);
313
+ appType = variables?.app?.type || 'external';
314
+ isExternal = appType === 'external';
315
+ } else {
316
+ const detected = await detectAppType(appName, options);
317
+ appPath = detected.appPath;
318
+ isExternal = detected.isExternal;
319
+ appType = detected.appType;
320
+ }
306
321
  logOfflinePathWhenType(appPath);
307
322
  if (isExternal) {
308
323
  return writeExternalDeployJson(appName, appPath, options);
@@ -315,7 +330,7 @@ async function generateDeployJsonWithValidation(appName, options = {}) {
315
330
  const jsonContent = fs.readFileSync(jsonPath, 'utf8');
316
331
  const deployment = JSON.parse(jsonContent);
317
332
 
318
- const { isExternal } = await detectAppType(appName);
333
+ const { isExternal } = await detectAppType(appName, options);
319
334
 
320
335
  // For external systems, skip deployment JSON validation (they use external system JSON structure)
321
336
  if (isExternal) {
@@ -11,7 +11,6 @@
11
11
  const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
12
12
  const fs = require('fs').promises;
13
13
  const path = require('path');
14
- const Handlebars = require('handlebars');
15
14
  const logger = require('../utils/logger');
16
15
  const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
17
16
  const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
@@ -178,7 +177,8 @@ async function generateConfigFilesForWizard(params) {
178
177
  }
179
178
 
180
179
  async function prepareWizardContext(appName, systemConfig, datasourceConfigs) {
181
- const appPath = path.join(process.cwd(), 'integration', appName);
180
+ const { getIntegrationPath } = require('../utils/paths');
181
+ const appPath = getIntegrationPath(appName);
182
182
  await fs.mkdir(appPath, { recursive: true });
183
183
  const finalSystemKey = appName;
184
184
  const originalSystemKey = systemConfig.key || finalSystemKey;
@@ -430,40 +430,7 @@ async function _generateEnvTemplate(appPath, systemConfig, finalSystemKey) {
430
430
  }
431
431
  }
432
432
 
433
- /**
434
- * Generate deployment script (deploy.js) from template
435
- * @async
436
- * @function generateDeployScripts
437
- * @param {string} appPath - Application directory path
438
- * @param {string} systemKey - System key
439
- * @param {string} systemFileName - System file name
440
- * @param {string[]} datasourceFileNames - Array of datasource file names
441
- * @returns {Promise<Object>} Object with deployJsPath
442
- * @throws {Error} If generation fails
443
- */
444
- const templatesExternalDir = path.join(__dirname, '..', '..', 'templates', 'external-system');
445
-
446
- async function writeDeployScriptFromTemplate(templateName, outputPath, context) {
447
- const templatePath = path.join(templatesExternalDir, templateName);
448
- const content = Handlebars.compile(await fs.readFile(templatePath, 'utf8'))(context);
449
- await fs.writeFile(outputPath, content, 'utf8');
450
- logger.log(formatSuccessLine(`Generated ${path.basename(outputPath)}`));
451
- }
452
-
453
- async function generateDeployScripts(appPath, systemKey, systemFileName, datasourceFileNames) {
454
- try {
455
- const allJsonFiles = [systemFileName, ...datasourceFileNames];
456
- const context = { systemKey, allJsonFiles, datasourceFileNames };
457
-
458
- await writeDeployScriptFromTemplate('deploy.js.hbs', path.join(appPath, 'deploy.js'), context);
459
-
460
- return {
461
- deployJsPath: path.join(appPath, 'deploy.js')
462
- };
463
- } catch (error) {
464
- throw new Error(`Failed to generate deployment scripts: ${error.message}`);
465
- }
466
- }
433
+ const { generateDeployScripts } = require('./deploy-script');
467
434
 
468
435
  module.exports = {
469
436
  generateWizardFiles,
@@ -118,6 +118,25 @@ function unlinkSync(p) {
118
118
  return delegate('unlinkSync', [p]);
119
119
  }
120
120
 
121
+ /**
122
+ * @param {string} src
123
+ * @param {string} dest
124
+ * @returns {void}
125
+ */
126
+ function copyFileSync(src, dest) {
127
+ return delegate('copyFileSync', [src, dest]);
128
+ }
129
+
130
+ /**
131
+ * @param {string} p
132
+ * @param {string|Buffer} data
133
+ * @param {object|string} [opts]
134
+ * @returns {void}
135
+ */
136
+ function appendFileSync(p, data, opts) {
137
+ return delegate('appendFileSync', opts !== undefined ? [p, data, opts] : [p, data]);
138
+ }
139
+
121
140
  module.exports = {
122
141
  existsSync,
123
142
  readFileSync,
@@ -128,5 +147,7 @@ module.exports = {
128
147
  readdirSync,
129
148
  mkdtempSync,
130
149
  rmSync,
131
- unlinkSync
150
+ unlinkSync,
151
+ copyFileSync,
152
+ appendFileSync
132
153
  };
@@ -73,6 +73,7 @@ function buildBoundFs() {
73
73
  readdirSync: snap.readdirSync,
74
74
  statSync: snap.statSync,
75
75
  renameSync: snap.renameSync,
76
+ copyFileSync: snap.copyFileSync,
76
77
  watch: (...args) => live.watch(...args),
77
78
  promises: boundPromises
78
79
  };
@@ -85,6 +86,7 @@ function buildBoundFs() {
85
86
  readdirSync: bindSync(rf, 'readdirSync'),
86
87
  statSync: bindSync(rf, 'statSync'),
87
88
  renameSync: bindSync(rf, 'renameSync'),
89
+ copyFileSync: bindSync(rf, 'copyFileSync'),
88
90
  watch: (...args) => live.watch(...args),
89
91
  promises: boundPromises
90
92
  };
@@ -34,7 +34,7 @@ function buildGovernanceOptsFromLifecycle(options, context = {}) {
34
34
  env: options.env || 'dev',
35
35
  verbose: options.verbose === true,
36
36
  debug: options.debug === true,
37
- noSync: true,
37
+ noPublish: true,
38
38
  force: options.force === true,
39
39
  certificationProfile: options.certificationProfile || 'bronze',
40
40
  subjectEmails: options.subjectEmail || options.subjectEmails || [],
@@ -53,7 +53,7 @@ function formatVerifyStepLabel(label) {
53
53
  * @returns {Promise<T>}
54
54
  */
55
55
  async function runWithVerifyStepProgress(label, work, options = {}) {
56
- if (!shouldShowVerifyStepProgress(options)) {
56
+ if (options.quietProgress === true || !shouldShowVerifyStepProgress(options)) {
57
57
  return withQuietLogs(() => work({ setLabel: () => {} }));
58
58
  }
59
59
 
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Structured access logging for builder-api requests.
3
+ *
4
+ * @fileoverview JSON logs without tokens or manifest bodies
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { redactForLog } = require('./log-redaction');
12
+
13
+ /**
14
+ * @param {Object} entry
15
+ */
16
+ function writeAccessLog(entry) {
17
+ const payload = redactForLog({
18
+ timestamp: new Date().toISOString(),
19
+ ...entry
20
+ });
21
+ process.stdout.write(`${JSON.stringify(payload)}\n`);
22
+ }
23
+
24
+ module.exports = {
25
+ writeAccessLog
26
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Bearer auth config for programmatic Dataplane calls.
3
+ *
4
+ * @fileoverview Build authConfig from user Bearer token (no device token store)
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * @param {string} bearerToken
13
+ * @returns {{ type: 'bearer', token: string }}
14
+ */
15
+ function buildBearerAuthConfig(bearerToken) {
16
+ if (!bearerToken || typeof bearerToken !== 'string') {
17
+ throw new Error('Bearer token is required');
18
+ }
19
+ const token = bearerToken.startsWith('Bearer ') ? bearerToken.slice(7).trim() : bearerToken.trim();
20
+ if (!token) {
21
+ throw new Error('Bearer token is required');
22
+ }
23
+ return { type: 'bearer', token };
24
+ }
25
+
26
+ /**
27
+ * @param {import('./workspace-context').WorkspaceContext} ctx
28
+ * @returns {{ authConfig: Object, dataplaneUrl: string, environment: string }}
29
+ */
30
+ function buildProgrammaticAuthContext(ctx) {
31
+ if (!ctx.dataplaneUrl) {
32
+ throw new Error('dataplaneUrl is required on WorkspaceContext');
33
+ }
34
+ return {
35
+ authConfig: buildBearerAuthConfig(ctx.bearerToken),
36
+ dataplaneUrl: ctx.dataplaneUrl,
37
+ environment: ctx.environment
38
+ };
39
+ }
40
+
41
+ module.exports = {
42
+ buildBearerAuthConfig,
43
+ buildProgrammaticAuthContext
44
+ };
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Bearer token validation for builder-api via @aifabrix/miso-client (controller-backed).
3
+ *
4
+ * @fileoverview Same model as dataplane: registered app client + MISO_CONTROLLER_URL
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /** @type {import('@aifabrix/miso-client').MisoClient | null} */
12
+ let misoClientSingleton = null;
13
+
14
+ /**
15
+ * @param {import('http').IncomingMessage} req
16
+ * @returns {string|null}
17
+ */
18
+ function extractBearerToken(req) {
19
+ const auth = req.headers.authorization || req.headers.Authorization;
20
+ if (!auth || typeof auth !== 'string') {
21
+ return null;
22
+ }
23
+ const match = auth.match(/^Bearer\s+(.+)$/i);
24
+ return match ? match[1].trim() : null;
25
+ }
26
+
27
+ /**
28
+ * @param {import('http').IncomingMessage} req
29
+ */
30
+ function assertNoForbiddenAuthHeaders(req) {
31
+ if (req.headers['x-client-token'] || req.headers['x-client-id']) {
32
+ const err = new Error('Client credential headers are not supported in builder-api V1');
33
+ err.statusCode = 401;
34
+ throw err;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * @returns {string}
40
+ */
41
+ function resolveControllerUrlFromEnv() {
42
+ const raw =
43
+ process.env.MISO_CONTROLLER_URL ||
44
+ process.env.CONTROLLER_URL ||
45
+ process.env.MISO_WEB_SERVER_URL ||
46
+ '';
47
+ const url = String(raw).trim().replace(/\/$/, '');
48
+ if (!url) {
49
+ throw new Error('MISO_CONTROLLER_URL (or CONTROLLER_URL) is required for token validation');
50
+ }
51
+ return url;
52
+ }
53
+
54
+ /**
55
+ * @returns {import('@aifabrix/miso-client/dist/types/config.types').MisoClientConfig}
56
+ */
57
+ function buildMisoClientConfigFromEnv() {
58
+ const clientId = (process.env.MISO_CLIENTID || '').trim();
59
+ const clientSecret = (process.env.MISO_CLIENTSECRET || '').trim();
60
+ if (!clientId || !clientSecret) {
61
+ throw new Error('MISO_CLIENTID and MISO_CLIENTSECRET are required for builder-api auth');
62
+ }
63
+ return {
64
+ clientId,
65
+ clientSecret,
66
+ controllerUrl: resolveControllerUrlFromEnv(),
67
+ logLevel: (process.env.LOG_LEVEL || 'info').toLowerCase()
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Test hook — reset cached MisoClient between Jest cases.
73
+ */
74
+ function resetMisoClientForTests() {
75
+ misoClientSingleton = null;
76
+ }
77
+
78
+ /**
79
+ * @returns {Promise<import('@aifabrix/miso-client').MisoClient>}
80
+ */
81
+ async function getMisoClientForAuth() {
82
+ let MisoClient;
83
+ try {
84
+ ({ MisoClient } = require('@aifabrix/miso-client'));
85
+ } catch {
86
+ const err = new Error('@aifabrix/miso-client is required for builder-api auth');
87
+ err.statusCode = 503;
88
+ throw err;
89
+ }
90
+
91
+ if (!misoClientSingleton) {
92
+ misoClientSingleton = new MisoClient(buildMisoClientConfigFromEnv());
93
+ }
94
+ if (!misoClientSingleton.isInitialized()) {
95
+ await misoClientSingleton.initialize();
96
+ }
97
+ return misoClientSingleton;
98
+ }
99
+
100
+ /**
101
+ * @param {import('@aifabrix/miso-client/dist/types/config.types').UserInfo} userInfo
102
+ * @returns {string}
103
+ */
104
+ function userIdFromUserInfo(userInfo) {
105
+ const userId = userInfo?.id || userInfo?.username;
106
+ if (!userId) {
107
+ const err = new Error('Token validation returned no user id');
108
+ err.statusCode = 401;
109
+ throw err;
110
+ }
111
+ return String(userId);
112
+ }
113
+
114
+ /**
115
+ * @param {string} token
116
+ * @param {Object} [opts]
117
+ * @returns {Promise<{ userId: string, payload: Object }>}
118
+ */
119
+ async function validateBearerToken(token, opts = {}) {
120
+ if (!token) {
121
+ const err = new Error('Missing Bearer token');
122
+ err.statusCode = 401;
123
+ throw err;
124
+ }
125
+
126
+ const client = opts.misoClient || (await getMisoClientForAuth());
127
+ const valid = await client.validateToken(token, opts.authStrategy);
128
+ if (!valid) {
129
+ const err = new Error('Invalid token');
130
+ err.statusCode = 401;
131
+ throw err;
132
+ }
133
+
134
+ const userInfo = await client.getUser(token, opts.authStrategy);
135
+ if (!userInfo) {
136
+ const err = new Error('Invalid token');
137
+ err.statusCode = 401;
138
+ throw err;
139
+ }
140
+
141
+ const payload = {
142
+ sub: userInfo.id,
143
+ preferredUsername: userInfo.username,
144
+ email: userInfo.email,
145
+ roles: userInfo.roles || []
146
+ };
147
+ return { userId: userIdFromUserInfo(userInfo), payload };
148
+ }
149
+
150
+ /**
151
+ * @param {import('http').IncomingMessage} req
152
+ * @param {Object} [opts]
153
+ * @returns {Promise<{ userId: string, bearerToken: string, payload: Object }>}
154
+ */
155
+ async function authenticateRequest(req, opts = {}) {
156
+ assertNoForbiddenAuthHeaders(req);
157
+ const bearerToken = extractBearerToken(req);
158
+ if (!bearerToken) {
159
+ const err = new Error('Authorization Bearer token required');
160
+ err.statusCode = 401;
161
+ throw err;
162
+ }
163
+ const { userId, payload } = await validateBearerToken(bearerToken, opts);
164
+ return { userId, bearerToken, payload };
165
+ }
166
+
167
+ module.exports = {
168
+ extractBearerToken,
169
+ assertNoForbiddenAuthHeaders,
170
+ resolveControllerUrlFromEnv,
171
+ buildMisoClientConfigFromEnv,
172
+ resetMisoClientForTests,
173
+ getMisoClientForAuth,
174
+ validateBearerToken,
175
+ authenticateRequest
176
+ };
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Authenticated capability execution for builder-api HTTP server.
3
+ *
4
+ * @fileoverview Idempotency, workspace lock, dispatch
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { authenticateRequest } = require('./bearer-auth');
12
+ const { dispatchHandler } = require('./route-handlers');
13
+ const { withWorkspaceLock } = require('./workspace-lock');
14
+ const { getIdempotentResult, setIdempotentResult } = require('./idempotency-cache');
15
+ const { withRequestTimeout } = require('./request-timeout');
16
+
17
+ /**
18
+ * @param {Object} capability
19
+ * @returns {boolean}
20
+ */
21
+ function requiresAuth(capability) {
22
+ return capability.handlerId !== 'health' && capability.handlerId !== 'ready';
23
+ }
24
+
25
+ /**
26
+ * @param {Object} capability
27
+ * @returns {boolean}
28
+ */
29
+ function isMutating(capability) {
30
+ return capability.mutating === true;
31
+ }
32
+
33
+ /**
34
+ * @param {Object} params
35
+ * @param {Object} body
36
+ * @returns {string}
37
+ */
38
+ function resolveIdempotencySystemKey(params, body) {
39
+ return params.systemKey || body.systemKey || '_workspace';
40
+ }
41
+
42
+ /**
43
+ * @param {Object} options
44
+ * @param {Object} options.auth
45
+ * @param {string|undefined} options.idempotencyKey
46
+ * @param {Object} options.capability
47
+ * @param {string} options.routeKey
48
+ * @param {Object} options.params
49
+ * @param {Object} options.body
50
+ * @param {string} options.requestId
51
+ * @returns {{ httpStatus: number, body: Object, requestId: string }|null}
52
+ */
53
+ function lookupIdempotentResponse(options) {
54
+ const { auth, idempotencyKey, capability, routeKey, params, body, requestId } = options;
55
+ if (!idempotencyKey || !isMutating(capability)) {
56
+ return null;
57
+ }
58
+ const cached = getIdempotentResult({
59
+ userId: auth.userId,
60
+ idempotencyKey: String(idempotencyKey),
61
+ route: routeKey,
62
+ systemKey: resolveIdempotencySystemKey(params, body)
63
+ });
64
+ if (!cached) {
65
+ return null;
66
+ }
67
+ return { httpStatus: cached.httpStatus, body: cached.body, requestId: cached.correlationId || requestId };
68
+ }
69
+
70
+ /**
71
+ * @param {Object} auth
72
+ * @param {Object} capability
73
+ * @param {Object} params
74
+ * @param {Object} body
75
+ * @param {Object} query
76
+ * @returns {Promise<*>}
77
+ */
78
+ async function runHandler(auth, capability, params, body, query) {
79
+ const run = () =>
80
+ withRequestTimeout(capability.handlerId, () =>
81
+ dispatchHandler(capability.handlerId, params, auth, body, query)
82
+ );
83
+ const lockKey = params.systemKey || body.systemKey;
84
+ if (isMutating(capability) && lockKey) {
85
+ return withWorkspaceLock(auth.userId, lockKey, run);
86
+ }
87
+ return run();
88
+ }
89
+
90
+ /**
91
+ * @param {Object} options
92
+ * @param {Object} options.auth
93
+ * @param {string|undefined} options.idempotencyKey
94
+ * @param {Object} options.capability
95
+ * @param {string} options.routeKey
96
+ * @param {Object} options.params
97
+ * @param {Object} options.body
98
+ * @param {string} options.requestId
99
+ * @param {Object} options.result
100
+ * @returns {{ httpStatus: number, body: Object, requestId: string, userId: string }}
101
+ */
102
+ function buildSuccessResponse(options) {
103
+ const { auth, idempotencyKey, capability, routeKey, params, body, requestId, result } = options;
104
+ const responseBody = { success: true, data: result, correlationId: requestId };
105
+ if (idempotencyKey && isMutating(capability)) {
106
+ setIdempotentResult(
107
+ {
108
+ userId: auth.userId,
109
+ idempotencyKey: String(idempotencyKey),
110
+ route: routeKey,
111
+ systemKey: resolveIdempotencySystemKey(params, body)
112
+ },
113
+ { httpStatus: 200, body: responseBody, correlationId: requestId }
114
+ );
115
+ }
116
+ return { httpStatus: 200, body: responseBody, requestId, userId: auth.userId };
117
+ }
118
+
119
+ /**
120
+ * @typedef {Object} ExecuteCapabilityRequestOptions
121
+ * @property {import('http').IncomingMessage} req
122
+ * @property {Object} capability
123
+ * @property {Object} params
124
+ * @property {string} routeKey
125
+ * @property {string} requestId
126
+ * @property {Function} readJsonBody
127
+ * @property {Object} query
128
+ */
129
+
130
+ /**
131
+ * @param {ExecuteCapabilityRequestOptions} options
132
+ * @returns {Promise<{ httpStatus: number, body: Object, requestId: string }>}
133
+ */
134
+ async function executeCapabilityRequest(options) {
135
+ const { req, capability, params, routeKey, requestId, readJsonBody, query } = options;
136
+ const auth = requiresAuth(capability)
137
+ ? await authenticateRequest(req)
138
+ : { userId: 'anonymous', bearerToken: '' };
139
+
140
+ const body = req.method === 'GET' || req.method === 'DELETE' ? {} : await readJsonBody(req);
141
+
142
+ const cached = lookupIdempotentResponse({
143
+ auth,
144
+ idempotencyKey: req.headers['idempotency-key'],
145
+ capability,
146
+ routeKey,
147
+ params,
148
+ body,
149
+ requestId
150
+ });
151
+ if (cached) {
152
+ return { ...cached, userId: auth.userId };
153
+ }
154
+
155
+ const result = await runHandler(auth, capability, params, body, query);
156
+ return buildSuccessResponse({
157
+ auth,
158
+ idempotencyKey: req.headers['idempotency-key'],
159
+ capability,
160
+ routeKey,
161
+ params,
162
+ body,
163
+ requestId,
164
+ result
165
+ });
166
+ }
167
+
168
+ module.exports = {
169
+ executeCapabilityRequest,
170
+ requiresAuth,
171
+ isMutating,
172
+ lookupIdempotentResponse,
173
+ buildSuccessResponse,
174
+ resolveIdempotencySystemKey
175
+ };