@aifabrix/builder 2.42.1 → 2.44.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 (392) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/README.md +2 -2
  3. package/anchor-docs/README.md +10 -0
  4. package/anchor-docs/_TEMPLATE +24 -0
  5. package/bin/aifabrix.js +13 -4
  6. package/integration/hubspot-test/README.md +157 -0
  7. package/integration/{hubspot → hubspot-test}/application.json +6 -6
  8. package/integration/{hubspot → hubspot-test}/create-hubspot.js +10 -10
  9. package/integration/hubspot-test/env.template +4 -0
  10. package/integration/hubspot-test/hubspot-test-datasource-company.json +138 -0
  11. package/integration/hubspot-test/hubspot-test-datasource-contact.json +146 -0
  12. package/integration/hubspot-test/hubspot-test-datasource-deal.json +146 -0
  13. package/integration/hubspot-test/hubspot-test-datasource-users.json +76 -0
  14. package/integration/{hubspot/hubspot-deploy.json → hubspot-test/hubspot-test-deploy.json} +201 -24
  15. package/integration/{hubspot/hubspot-system.json → hubspot-test/hubspot-test-system.json} +8 -7
  16. package/integration/hubspot-test/rbac.json +166 -0
  17. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-credential-real.yaml +3 -3
  18. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-env-vars.yaml +2 -2
  19. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-add-datasource.yaml +1 -1
  20. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-create.yaml +1 -1
  21. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-select.yaml +1 -1
  22. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-known-platform.yaml +1 -1
  23. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
  24. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-mode.yaml +1 -1
  25. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-file.yaml +1 -1
  26. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-url.yaml +1 -1
  27. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-source.yaml +1 -1
  28. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-array-test.yaml +1 -1
  29. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
  30. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
  31. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-test.yaml +1 -1
  32. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-test.yaml +1 -1
  33. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +1 -1
  34. package/integration/{hubspot → hubspot-test}/test-dataplane-down-tests.js +1 -7
  35. package/integration/{hubspot → hubspot-test}/test-dataplane-down.js +3 -3
  36. package/integration/{hubspot → hubspot-test}/test.js +137 -102
  37. package/integration/{hubspot → hubspot-test}/wizard-hubspot-e2e.yaml +2 -2
  38. package/integration/{hubspot → hubspot-test}/wizard-hubspot-platform.yaml +1 -1
  39. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  40. package/integration/roundtrip-test-local/README.md +144 -0
  41. package/integration/roundtrip-test-local/application.yaml +13 -0
  42. package/integration/roundtrip-test-local/env.template +15 -0
  43. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  44. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  45. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  46. package/integration/roundtrip-test-local2/README.md +144 -0
  47. package/integration/roundtrip-test-local2/application.yaml +13 -0
  48. package/integration/roundtrip-test-local2/env.template +15 -0
  49. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  50. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  51. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  52. package/integration/test/wizard.yaml +8 -0
  53. package/jest.config.default.js +10 -0
  54. package/jest.config.integration.fixtures.js +22 -0
  55. package/jest.config.integration.js +21 -18
  56. package/jest.config.isolated.js +10 -0
  57. package/jest.projects.js +288 -0
  58. package/lib/api/datasources-core.api.js +3 -3
  59. package/lib/api/dev-mtls-request.js +110 -0
  60. package/lib/api/dev-server-https.js +145 -0
  61. package/lib/api/dev.api.js +133 -144
  62. package/lib/api/index.js +0 -1
  63. package/lib/api/pipeline.api.js +67 -20
  64. package/lib/api/service-users.api.js +111 -2
  65. package/lib/api/types/dev.types.js +4 -3
  66. package/lib/api/types/pipeline.types.js +8 -5
  67. package/lib/api/types/service-users.types.js +41 -0
  68. package/lib/api/types/validation-run.types.js +56 -0
  69. package/lib/api/validation-run.api.js +99 -0
  70. package/lib/api/validation-runner.js +99 -0
  71. package/lib/app/config.js +1 -1
  72. package/lib/app/deploy-status-display.js +2 -2
  73. package/lib/app/deploy.js +7 -6
  74. package/lib/app/display.js +2 -1
  75. package/lib/app/dockerfile.js +3 -2
  76. package/lib/app/down.js +2 -1
  77. package/lib/app/helpers.js +6 -5
  78. package/lib/app/index.js +27 -8
  79. package/lib/app/list.js +7 -6
  80. package/lib/app/push.js +4 -3
  81. package/lib/app/register.js +19 -8
  82. package/lib/app/rotate-secret.js +17 -13
  83. package/lib/app/run-container-start.js +184 -0
  84. package/lib/app/run-docker-fallback.js +108 -0
  85. package/lib/app/run-env-compose.js +30 -42
  86. package/lib/app/run-helpers.js +49 -126
  87. package/lib/app/run-infra-requirements.js +30 -0
  88. package/lib/app/run-resolve-image.js +21 -0
  89. package/lib/app/run.js +74 -21
  90. package/lib/app/show-display.js +1 -1
  91. package/lib/app/show.js +1 -1
  92. package/lib/build/index.js +13 -10
  93. package/lib/cli/index.js +2 -0
  94. package/lib/cli/setup-app.help.js +67 -0
  95. package/lib/cli/setup-app.js +59 -123
  96. package/lib/cli/setup-app.test-commands.js +179 -0
  97. package/lib/cli/setup-auth.js +36 -14
  98. package/lib/cli/setup-credential-deployment.js +22 -8
  99. package/lib/cli/setup-dev-path-commands.js +124 -0
  100. package/lib/cli/setup-dev.js +190 -103
  101. package/lib/cli/setup-environment.js +11 -20
  102. package/lib/cli/setup-external-system.js +62 -22
  103. package/lib/cli/setup-infra.js +139 -47
  104. package/lib/cli/setup-parameters.js +32 -0
  105. package/lib/cli/setup-secrets.js +147 -10
  106. package/lib/cli/setup-service-user.js +146 -20
  107. package/lib/cli/setup-utility.js +47 -19
  108. package/lib/commands/app-down.js +5 -7
  109. package/lib/commands/app-install.js +14 -7
  110. package/lib/commands/app-logs.js +13 -10
  111. package/lib/commands/app-shell.js +4 -1
  112. package/lib/commands/app-test.js +25 -19
  113. package/lib/commands/app.js +22 -10
  114. package/lib/commands/auth-config.js +10 -14
  115. package/lib/commands/auth-status.js +4 -3
  116. package/lib/commands/credential-env.js +4 -3
  117. package/lib/commands/credential-list.js +5 -4
  118. package/lib/commands/credential-push.js +4 -3
  119. package/lib/commands/datasource-unified-test-cli.js +495 -0
  120. package/lib/commands/datasource-unified-test-cli.options.js +149 -0
  121. package/lib/commands/datasource-validation-cli.js +129 -0
  122. package/lib/commands/datasource.js +123 -71
  123. package/lib/commands/deployment-list.js +6 -5
  124. package/lib/commands/dev-cli-handlers.js +122 -18
  125. package/lib/commands/dev-down.js +4 -3
  126. package/lib/commands/dev-init.js +231 -116
  127. package/lib/commands/dev-show-display.js +473 -0
  128. package/lib/commands/login-credentials.js +3 -2
  129. package/lib/commands/login-device.js +4 -3
  130. package/lib/commands/login.js +5 -4
  131. package/lib/commands/logout.js +8 -7
  132. package/lib/commands/parameters-validate.js +54 -0
  133. package/lib/commands/repair-datasource.js +314 -68
  134. package/lib/commands/repair-env-template.js +16 -10
  135. package/lib/commands/repair-rbac.js +25 -19
  136. package/lib/commands/repair.js +116 -32
  137. package/lib/commands/secrets-list.js +23 -12
  138. package/lib/commands/secrets-remove-all.js +220 -0
  139. package/lib/commands/secrets-remove.js +22 -13
  140. package/lib/commands/secrets-set.js +21 -12
  141. package/lib/commands/secrets-validate.js +20 -7
  142. package/lib/commands/secure.js +10 -9
  143. package/lib/commands/service-user.js +243 -13
  144. package/lib/commands/test-e2e-external.js +27 -1
  145. package/lib/commands/up-common.js +28 -2
  146. package/lib/commands/up-dataplane.js +31 -18
  147. package/lib/commands/up-miso.js +19 -29
  148. package/lib/commands/upload.js +138 -39
  149. package/lib/commands/wizard-core-helpers.js +1 -1
  150. package/lib/commands/wizard-dataplane.js +4 -3
  151. package/lib/commands/wizard-helpers.js +3 -3
  152. package/lib/commands/wizard.js +2 -2
  153. package/lib/core/admin-secrets.js +16 -5
  154. package/lib/core/audit-logger.js +12 -4
  155. package/lib/core/config-attach-extensions.js +46 -0
  156. package/lib/core/config-runtime-paths.js +29 -0
  157. package/lib/core/config.js +59 -58
  158. package/lib/core/diff.js +3 -2
  159. package/lib/core/ensure-encryption-key.js +2 -4
  160. package/lib/core/secrets-ensure-infra.js +77 -0
  161. package/lib/core/secrets-ensure.js +120 -64
  162. package/lib/core/secrets-env-write.js +35 -7
  163. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  164. package/lib/core/secrets.js +228 -42
  165. package/lib/core/templates-env.js +4 -3
  166. package/lib/core/templates.js +1 -1
  167. package/lib/datasource/abac-validator.js +148 -0
  168. package/lib/datasource/deploy.js +75 -53
  169. package/lib/datasource/field-reference-validator.js +77 -36
  170. package/lib/datasource/integration-context.js +63 -0
  171. package/lib/datasource/list.js +8 -7
  172. package/lib/datasource/log-viewer.js +252 -0
  173. package/lib/datasource/resolve-app.js +109 -0
  174. package/lib/datasource/test-e2e.js +95 -155
  175. package/lib/datasource/test-integration.js +121 -109
  176. package/lib/datasource/unified-validation-run-body.js +65 -0
  177. package/lib/datasource/unified-validation-run-post.js +23 -0
  178. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  179. package/lib/datasource/unified-validation-run.js +92 -0
  180. package/lib/datasource/validate.js +162 -15
  181. package/lib/deployment/deployer.js +4 -3
  182. package/lib/deployment/environment.js +7 -6
  183. package/lib/deployment/push.js +17 -8
  184. package/lib/external-system/delete.js +4 -3
  185. package/lib/external-system/deploy.js +131 -53
  186. package/lib/external-system/download-helpers.js +1 -1
  187. package/lib/external-system/download.js +7 -6
  188. package/lib/external-system/generator.js +104 -14
  189. package/lib/external-system/integration-test-dispatch.js +26 -0
  190. package/lib/external-system/test-execution.js +5 -1
  191. package/lib/external-system/test-helpers.js +0 -4
  192. package/lib/external-system/test-system-level-helpers.js +110 -0
  193. package/lib/external-system/test-system-level.js +83 -44
  194. package/lib/external-system/test.js +59 -8
  195. package/lib/generator/builders.js +23 -11
  196. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  197. package/lib/generator/external-controller-manifest.js +3 -3
  198. package/lib/generator/external.js +23 -11
  199. package/lib/generator/helpers.js +71 -12
  200. package/lib/generator/index.js +8 -4
  201. package/lib/generator/split-readme.js +12 -7
  202. package/lib/generator/split-variables.js +2 -1
  203. package/lib/generator/split.js +46 -11
  204. package/lib/generator/wizard-readme.js +3 -3
  205. package/lib/generator/wizard.js +16 -13
  206. package/lib/infrastructure/compose.js +60 -6
  207. package/lib/infrastructure/helpers.js +238 -51
  208. package/lib/infrastructure/index.js +64 -37
  209. package/lib/infrastructure/services.js +21 -15
  210. package/lib/internal/fs-real-sync.js +104 -0
  211. package/lib/internal/node-fs.js +98 -0
  212. package/lib/parameters/database-secret-values.js +173 -0
  213. package/lib/parameters/infra-kv-discovery.js +121 -0
  214. package/lib/parameters/infra-parameter-catalog.js +458 -0
  215. package/lib/parameters/infra-parameter-validate.js +64 -0
  216. package/lib/schema/application-schema.json +37 -17
  217. package/lib/schema/datasource-test-run.schema.json +493 -0
  218. package/lib/schema/deployment-rules.yaml +102 -63
  219. package/lib/schema/external-datasource.schema.json +1201 -433
  220. package/lib/schema/external-system.schema.json +181 -5
  221. package/lib/schema/flag-map-validation-run.json +31 -0
  222. package/lib/schema/infra-parameter.schema.json +106 -0
  223. package/lib/schema/infra.parameter.yaml +421 -0
  224. package/lib/schema/type/credential-auth-templates.json +40 -0
  225. package/lib/schema/type/document-storage.json +213 -0
  226. package/lib/schema/type/message-service.json +123 -0
  227. package/lib/schema/type/vector-store.json +88 -0
  228. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  229. package/lib/utils/api-error-handler.js +2 -2
  230. package/lib/utils/api.js +49 -14
  231. package/lib/utils/app-config-resolver.js +23 -1
  232. package/lib/utils/app-register-api.js +3 -2
  233. package/lib/utils/app-register-auth.js +1 -1
  234. package/lib/utils/app-register-config.js +4 -4
  235. package/lib/utils/app-register-display.js +3 -2
  236. package/lib/utils/app-register-validator.js +3 -2
  237. package/lib/utils/app-run-containers.js +26 -22
  238. package/lib/utils/app-scoped-config.js +31 -0
  239. package/lib/utils/app-service-env-from-builder.js +164 -0
  240. package/lib/utils/build-copy.js +1 -1
  241. package/lib/utils/build-helpers.js +20 -20
  242. package/lib/utils/build-resolve-image.js +165 -0
  243. package/lib/utils/cli-layout-chalk.js +8 -0
  244. package/lib/utils/cli-test-layout-chalk.js +267 -0
  245. package/lib/utils/cli-utils.js +88 -11
  246. package/lib/utils/compose-db-passwords.js +138 -0
  247. package/lib/utils/compose-generate-docker-compose.js +216 -0
  248. package/lib/utils/compose-generator.js +197 -291
  249. package/lib/utils/compose-miso-env.js +18 -0
  250. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  251. package/lib/utils/config-paths.js +209 -6
  252. package/lib/utils/config-scoped-resources-preference.js +41 -0
  253. package/lib/utils/controller-deployment-outcome.js +68 -0
  254. package/lib/utils/credential-display.js +2 -2
  255. package/lib/utils/credential-secrets-env.js +16 -1
  256. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  257. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  258. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  259. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  260. package/lib/utils/datasource-test-run-display.js +442 -0
  261. package/lib/utils/datasource-test-run-exit.js +58 -0
  262. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  263. package/lib/utils/datasource-test-run-report-version.js +51 -0
  264. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  265. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  266. package/lib/utils/datasource-validation-watch.js +266 -0
  267. package/lib/utils/declarative-url-ports.js +47 -0
  268. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  269. package/lib/utils/dev-ca-install.js +185 -23
  270. package/lib/utils/dev-cert-helper.js +266 -17
  271. package/lib/utils/dev-hosts-helper.js +307 -0
  272. package/lib/utils/dev-init-cert-hints.js +37 -0
  273. package/lib/utils/dev-init-health-messages.js +52 -0
  274. package/lib/utils/dev-init-resolve.js +86 -0
  275. package/lib/utils/dev-init-ssh-merge.js +65 -0
  276. package/lib/utils/dev-ssh-config-helper.js +196 -0
  277. package/lib/utils/dev-user-groups.js +93 -0
  278. package/lib/utils/docker-build.js +42 -17
  279. package/lib/utils/docker-exec.js +28 -0
  280. package/lib/utils/docker-manifest-public-port.js +116 -0
  281. package/lib/utils/docker-not-running-hint.js +52 -0
  282. package/lib/utils/docker.js +98 -11
  283. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  284. package/lib/utils/env-config-loader.js +10 -91
  285. package/lib/utils/env-copy.js +19 -10
  286. package/lib/utils/env-map.js +42 -11
  287. package/lib/utils/env-template.js +2 -2
  288. package/lib/utils/environment-scoped-resources.js +144 -0
  289. package/lib/utils/error-formatter.js +125 -9
  290. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  291. package/lib/utils/error-formatters/network-errors.js +2 -1
  292. package/lib/utils/error-formatters/permission-errors.js +2 -1
  293. package/lib/utils/error-formatters/validation-errors.js +2 -1
  294. package/lib/utils/external-env-template.js +180 -0
  295. package/lib/utils/external-readme.js +8 -1
  296. package/lib/utils/external-system-display.js +277 -136
  297. package/lib/utils/external-system-local-test-tty.js +389 -0
  298. package/lib/utils/external-system-readiness-core.js +377 -0
  299. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  300. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  301. package/lib/utils/external-system-readiness-display.js +186 -0
  302. package/lib/utils/external-system-test-helpers.js +24 -6
  303. package/lib/utils/external-system-validators.js +32 -14
  304. package/lib/utils/health-check-url.js +119 -0
  305. package/lib/utils/health-check.js +59 -25
  306. package/lib/utils/help-builder.js +14 -13
  307. package/lib/utils/image-version.js +4 -8
  308. package/lib/utils/infra-containers.js +4 -7
  309. package/lib/utils/infra-env-defaults.js +162 -0
  310. package/lib/utils/infra-status-display.js +167 -0
  311. package/lib/utils/infra-status.js +16 -8
  312. package/lib/utils/local-secrets.js +29 -7
  313. package/lib/utils/paths.js +136 -48
  314. package/lib/utils/port-resolver.js +10 -23
  315. package/lib/utils/redis-env-scope.js +62 -0
  316. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  317. package/lib/utils/remote-builder-validation.js +99 -0
  318. package/lib/utils/remote-dev-auth.js +117 -21
  319. package/lib/utils/remote-docker-env.js +67 -15
  320. package/lib/utils/remote-secrets-loader.js +13 -4
  321. package/lib/utils/resolve-docker-image-ref.js +124 -0
  322. package/lib/utils/schema-loader.js +22 -9
  323. package/lib/utils/secrets-bash-kv.js +25 -0
  324. package/lib/utils/secrets-generator.js +171 -51
  325. package/lib/utils/secrets-helpers.js +70 -59
  326. package/lib/utils/secrets-kv-scope.js +60 -0
  327. package/lib/utils/secrets-utils.js +35 -37
  328. package/lib/utils/secrets-validation.js +3 -1
  329. package/lib/utils/secrets-yaml-preserve.js +109 -0
  330. package/lib/utils/secure-file-permissions.js +91 -0
  331. package/lib/utils/ssh-key-helper.js +4 -2
  332. package/lib/utils/template-helpers.js +2 -2
  333. package/lib/utils/test-log-writer.js +3 -3
  334. package/lib/utils/token-manager.js +37 -5
  335. package/lib/utils/url-declarative-public-base.js +188 -0
  336. package/lib/utils/url-declarative-resolve-build.js +493 -0
  337. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  338. package/lib/utils/url-declarative-resolve.js +220 -0
  339. package/lib/utils/url-declarative-token-parse.js +74 -0
  340. package/lib/utils/url-declarative-url-flags.js +50 -0
  341. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  342. package/lib/utils/url-public-path-prefix.js +34 -0
  343. package/lib/utils/urls-local-registry.js +220 -0
  344. package/lib/utils/validation-report-tty-kit.js +77 -0
  345. package/lib/utils/validation-run-poll.js +89 -0
  346. package/lib/utils/validation-run-post-retry.js +73 -0
  347. package/lib/utils/validation-run-request.js +98 -0
  348. package/lib/utils/variable-transformer.js +21 -4
  349. package/lib/utils/yaml-preserve.js +78 -1
  350. package/lib/validation/datasource-warnings.js +56 -0
  351. package/lib/validation/env-template-auth.js +50 -2
  352. package/lib/validation/external-manifest-validator.js +35 -7
  353. package/lib/validation/validate-display.js +37 -31
  354. package/lib/validation/validate.js +9 -10
  355. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  356. package/lib/validation/validator.js +32 -78
  357. package/lib/validation/wizard-config-validator.js +2 -1
  358. package/package.json +11 -3
  359. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  360. package/scripts/diagnose-cli.js +150 -0
  361. package/scripts/install-local.js +304 -55
  362. package/templates/README.md +15 -2
  363. package/templates/applications/dataplane/application.yaml +52 -2
  364. package/templates/applications/dataplane/env.template +80 -18
  365. package/templates/applications/dataplane/rbac.yaml +8 -0
  366. package/templates/applications/keycloak/application.yaml +9 -1
  367. package/templates/applications/keycloak/env.template +15 -6
  368. package/templates/applications/miso-controller/application.yaml +10 -2
  369. package/templates/applications/miso-controller/env.template +55 -14
  370. package/templates/applications/miso-controller/rbac.yaml +5 -0
  371. package/templates/external-system/README.md.hbs +20 -7
  372. package/templates/external-system/deploy.js.hbs +5 -5
  373. package/templates/external-system/env.template.hbs +22 -0
  374. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  375. package/templates/infra/compose.yaml.hbs +20 -4
  376. package/templates/python/docker-compose.hbs +16 -0
  377. package/templates/typescript/docker-compose.hbs +16 -0
  378. package/integration/hubspot/README.md +0 -102
  379. package/integration/hubspot/env.template +0 -4
  380. package/integration/hubspot/hubspot-datasource-company.json +0 -541
  381. package/integration/hubspot/hubspot-datasource-contact.json +0 -639
  382. package/integration/hubspot/hubspot-datasource-deal.json +0 -588
  383. package/integration/hubspot/hubspot-datasource-users.json +0 -116
  384. package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  385. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  386. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  387. package/lib/api/external-test.api.js +0 -111
  388. package/lib/schema/env-config.yaml +0 -43
  389. /package/integration/{hubspot → hubspot-test}/companies.json +0 -0
  390. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-app-name.yaml +0 -0
  391. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-missing-app.yaml +0 -0
  392. /package/integration/{hubspot → hubspot-test}/test-dataplane-down-helpers.js +0 -0
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Environment-scoped resource naming (shared infra: dev/tst on same Postgres/Redis/Docker).
3
+ * Effective only when user gate AND app flag AND run env is dev|tst.
4
+ *
5
+ * @fileoverview environmentScopedResources + useEnvironmentScopedResources helpers
6
+ * @author AI Fabrix Team
7
+ * @version 1.0.0
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ /**
13
+ * Run/env keys that participate in resource prefixing (not pro).
14
+ * @param {string} envKey - Normalized lowercase env key
15
+ * @returns {boolean}
16
+ */
17
+ function isScopedRunEnvironmentKey(envKey) {
18
+ if (!envKey || typeof envKey !== 'string') return false;
19
+ const k = envKey.toLowerCase();
20
+ return k === 'dev' || k === 'tst';
21
+ }
22
+
23
+ /**
24
+ * Effective local/deploy prefix behavior: user gate ∧ app flag ∧ dev|tst.
25
+ *
26
+ * @param {boolean} useEnvironmentScopedResources - ~/.aifabrix/config.yaml
27
+ * @param {boolean} appEnvironmentScopedResources - application.yaml
28
+ * @param {string} runEnvKey - dev | tst | pro | other (lowercase)
29
+ * @returns {boolean}
30
+ */
31
+ function computeEffectiveEnvironmentScopedResources(
32
+ useEnvironmentScopedResources,
33
+ appEnvironmentScopedResources,
34
+ runEnvKey
35
+ ) {
36
+ return (
37
+ Boolean(useEnvironmentScopedResources) &&
38
+ Boolean(appEnvironmentScopedResources) &&
39
+ isScopedRunEnvironmentKey(runEnvKey)
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Redis logical DB index when env-scoped resources are effective.
45
+ * dev → 0, tst → 1 (single shared Redis instance).
46
+ *
47
+ * @param {string} runEnvKey - dev | tst
48
+ * @returns {number|null} Index or null if not applicable
49
+ */
50
+ function redisDbIndexForScopedRunEnv(runEnvKey) {
51
+ if (!runEnvKey || typeof runEnvKey !== 'string') return null;
52
+ const k = runEnvKey.toLowerCase();
53
+ if (k === 'dev') return 0;
54
+ if (k === 'tst') return 1;
55
+ return null;
56
+ }
57
+
58
+ /**
59
+ * Docker container name for local `aifabrix run` when env-scoping applies.
60
+ *
61
+ * @param {string} appName - Application key
62
+ * @param {string|number} devId - Developer id
63
+ * @param {number} idNum - Parsed numeric developer id
64
+ * @param {string} envKey - dev | tst
65
+ * @returns {string}
66
+ */
67
+ function buildScopedLocalContainerName(appName, devId, idNum, envKey) {
68
+ const e = String(envKey).toLowerCase();
69
+ if (idNum === 0) {
70
+ return `aifabrix-${e}-${appName}`;
71
+ }
72
+ return `aifabrix-dev${devId}-${e}-${appName}`;
73
+ }
74
+
75
+ /**
76
+ * Default local container name (no env scope).
77
+ *
78
+ * @param {string} appName - Application key
79
+ * @param {string|number} devId - Developer id
80
+ * @param {number} idNum - Parsed numeric developer id
81
+ * @returns {string}
82
+ */
83
+ function buildDefaultLocalContainerName(appName, devId, idNum) {
84
+ if (idNum === 0) {
85
+ return `aifabrix-${appName}`;
86
+ }
87
+ return `aifabrix-dev${devId}-${appName}`;
88
+ }
89
+
90
+ /**
91
+ * Resolved container name for run/stop/status.
92
+ *
93
+ * @param {string} appName - Application key
94
+ * @param {string|number} developerId - Developer id
95
+ * @param {boolean} effectiveScoped - From {@link computeEffectiveEnvironmentScopedResources}
96
+ * @param {string} [runEnvKey] - dev | tst (required when effectiveScoped)
97
+ * @returns {string}
98
+ */
99
+ function resolveRunContainerName(appName, developerId, effectiveScoped, runEnvKey) {
100
+ const idNum = typeof developerId === 'string' ? parseInt(developerId, 10) : developerId;
101
+ if (effectiveScoped && runEnvKey) {
102
+ return buildScopedLocalContainerName(appName, developerId, idNum, runEnvKey);
103
+ }
104
+ return buildDefaultLocalContainerName(appName, developerId, idNum);
105
+ }
106
+
107
+ /**
108
+ * Traefik PathPrefix: prefix /{envKey} before pattern base (e.g. /api → /dev/api).
109
+ *
110
+ * @param {string} basePath - From derivePathFromPattern
111
+ * @param {string} envKey - dev | tst
112
+ * @returns {string}
113
+ */
114
+ function buildEnvScopedTraefikPath(basePath, envKey) {
115
+ const e = String(envKey).toLowerCase();
116
+ const trimmed = (basePath || '/').trim() || '/';
117
+ if (trimmed === '/') {
118
+ return `/${e}`;
119
+ }
120
+ const withSlash = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
121
+ return `/${e}${withSlash}`.replace(/\/{2,}/g, '/');
122
+ }
123
+
124
+ /**
125
+ * Compose service + Traefik router key when env-scoped (unique per env on shared host).
126
+ *
127
+ * @param {string} appName - Application key
128
+ * @param {string} envKey - dev | tst
129
+ * @returns {string}
130
+ */
131
+ function composeTraefikServiceKey(appName, envKey) {
132
+ return `${String(envKey).toLowerCase()}-${appName}`;
133
+ }
134
+
135
+ module.exports = {
136
+ isScopedRunEnvironmentKey,
137
+ computeEffectiveEnvironmentScopedResources,
138
+ redisDbIndexForScopedRunEnv,
139
+ buildScopedLocalContainerName,
140
+ buildDefaultLocalContainerName,
141
+ resolveRunContainerName,
142
+ buildEnvScopedTraefikPath,
143
+ composeTraefikServiceKey
144
+ };
@@ -19,6 +19,8 @@ const PATTERN_DESCRIPTIONS = {
19
19
  '^[a-z-]+$': 'lowercase letters and hyphens only',
20
20
  '^[A-Z_][A-Z0-9_]*$': 'uppercase letters, numbers, and underscores (must start with letter or underscore)',
21
21
  '^[a-zA-Z0-9_-]+$': 'letters, numbers, hyphens, and underscores only',
22
+ '^[a-zA-Z0-9_]+$': 'letters, numbers, and underscores only',
23
+ '^[a-zA-Z0-9_.]+$': 'letters, numbers, underscores, and dots only',
22
24
  '^(http|https)://.*$': 'valid HTTP or HTTPS URL',
23
25
  '^/[a-z0-9/-]*$': 'URL path starting with / (lowercase letters, numbers, hyphens, slashes)'
24
26
  };
@@ -45,15 +47,52 @@ function getFieldName(error) {
45
47
  return path ? `Field "${path}"` : 'Configuration';
46
48
  }
47
49
 
50
+ /**
51
+ * Resolves a value from root using an AJV instancePath (JSON Pointer, e.g. /roles/0/value).
52
+ * @param {*} root - Object or array validated at root
53
+ * @param {string} instancePath - AJV instancePath (leading slash or empty)
54
+ * @returns {*|undefined}
55
+ */
56
+ function getValueAtInstancePath(root, instancePath) {
57
+ if (root === undefined || root === null) return undefined;
58
+ if (!instancePath || instancePath === '' || instancePath === '/') return root;
59
+ const parts = instancePath.split('/').filter(Boolean);
60
+ let cur = root;
61
+ for (const p of parts) {
62
+ if (cur === undefined || cur === null) return undefined;
63
+ const key = /^\d+$/.test(p) ? parseInt(p, 10) : p;
64
+ cur = cur[key];
65
+ }
66
+ return cur;
67
+ }
68
+
69
+ /**
70
+ * Resolves the failing value for display (AJV may omit `data` depending on compile options).
71
+ * @param {Object} error - AJV error
72
+ * @param {Object} [options] - rootData or deploymentManifest (same shape as validated root)
73
+ * @returns {*|undefined}
74
+ */
75
+ function resolveErrorDataValue(error, options = {}) {
76
+ if (error && error.data !== undefined) return error.data;
77
+ const root = options.rootData !== undefined ? options.rootData : options.deploymentManifest;
78
+ if (!root) return undefined;
79
+ return getValueAtInstancePath(root, error.instancePath || '');
80
+ }
81
+
48
82
  /**
49
83
  * Formats a pattern validation error with the actual invalid value
50
84
  * @function formatPatternError
51
85
  * @param {string} field - Field name
52
86
  * @param {Object} error - Validation error object
87
+ * @param {Object} [options] - Pass rootData or deploymentManifest to show value when error.data is missing
53
88
  * @returns {string} Formatted error message
54
89
  */
55
- function formatPatternError(field, error) {
56
- const invalidValue = error.data !== undefined ? `"${error.data}"` : 'value';
90
+ function formatPatternError(field, error, options = {}) {
91
+ const actual = resolveErrorDataValue(error, options);
92
+ const invalidValue =
93
+ actual === undefined
94
+ ? '(unavailable — pass rootData/deploymentManifest to formatValidationErrors to show it)'
95
+ : JSON.stringify(actual);
57
96
  const patternDesc = getPatternDescription(error.params?.pattern);
58
97
  return `${field}: Invalid value ${invalidValue} - ${patternDesc}`;
59
98
  }
@@ -119,6 +158,10 @@ function createKeywordFormatters(field, error) {
119
158
  ? `${field}: Must be at most ${params.limit} characters`
120
159
  : `${field}: Too long`,
121
160
 
161
+ minItems: params.limit !== undefined
162
+ ? `${field}: must have at least ${params.limit} item(s)`
163
+ : `${field}: too few items`,
164
+
122
165
  enum: params.allowedValues && params.allowedValues.length > 0
123
166
  ? `${field}: Must be one of: ${params.allowedValues.join(', ')}`
124
167
  : `${field}: Must be one of: unknown`
@@ -126,22 +169,93 @@ function createKeywordFormatters(field, error) {
126
169
  }
127
170
 
128
171
  /**
129
- * Formats a single validation error into a developer-friendly message
130
- *
131
- * @function formatSingleError
172
+ * Formats oneOf/anyOf validation errors with actionable message
173
+ * @param {string} field - Field name
174
+ * @param {Object} error - AJV error (keyword oneOf or anyOf)
175
+ * @returns {string} Formatted error message
176
+ */
177
+ function formatOneOfAnyOfError(field, error) {
178
+ const instancePath = (error.instancePath || '').replace(/^\//, '');
179
+ if (instancePath === 'capabilities') {
180
+ return `${field}: must be either an array of operation names (e.g. ["list","get"]) or an object with boolean flags (e.g. { "list": true }).`;
181
+ }
182
+ return `${field}: value does not match any allowed shape. Check type and required fields.`;
183
+ }
184
+
185
+ /**
186
+ * Formats const validation errors
187
+ * @param {string} field - Field name
188
+ * @param {Object} error - AJV error (keyword const)
189
+ * @returns {string} Formatted error message
190
+ */
191
+ function formatConstError(field, error) {
192
+ const allowed = error.params?.allowedValue;
193
+ if (allowed !== undefined) {
194
+ const display = typeof allowed === 'string' ? `"${allowed}"` : String(allowed);
195
+ return `${field}: must be exactly ${display}`;
196
+ }
197
+ return `${field}: invalid value (constraint violation)`;
198
+ }
199
+
200
+ /** JSON Pointer: /permissions/<index>/roles */
201
+ const PERMISSION_ROLES_INSTANCE_PATH = /^\/permissions\/(\d+)\/roles$/;
202
+
203
+ /**
204
+ * Clear message when permissions[i].roles is [] (schema minItems: 1).
205
+ * @param {Object} error - AJV error
206
+ * @param {Object} [options] - Optional context
207
+ * @param {Object} [options.deploymentManifest] - Deploy JSON being validated (for permission name)
208
+ * @returns {string|null}
209
+ */
210
+ function tryFormatPermissionRolesMinItemsError(error, options) {
211
+ if (error.keyword !== 'minItems') {
212
+ return null;
213
+ }
214
+ const m = (error.instancePath || '').match(PERMISSION_ROLES_INSTANCE_PATH);
215
+ if (!m) {
216
+ return null;
217
+ }
218
+ const idx = parseInt(m[1], 10);
219
+ const perms = options?.deploymentManifest?.permissions;
220
+ const perm = Array.isArray(perms) ? perms[idx] : null;
221
+ const named =
222
+ perm && typeof perm.name === 'string' && perm.name.trim()
223
+ ? ` "${perm.name.trim()}"`
224
+ : ` at permissions[${idx}]`;
225
+ return (
226
+ `RBAC: permission${named} has an empty "roles" array. ` +
227
+ 'Each permission must list at least one role, and each string must match a role "value" from your "roles" list ' +
228
+ '(in application.yaml or rbac.yaml under the app folder). ' +
229
+ 'Add roles, e.g. roles: ["admin"], or remove the permission if it is unused.'
230
+ );
231
+ }
232
+
233
+ /**
132
234
  * @param {Object} error - Raw validation error from Ajv
235
+ * @param {Object} [options] - Optional; use deploymentManifest for richer RBAC messages
133
236
  * @returns {string} Formatted error message
134
237
  */
135
- function formatSingleError(error) {
238
+ function formatSingleError(error, options) {
136
239
  const field = getFieldName(error);
137
240
 
241
+ const rbacRolesMsg = tryFormatPermissionRolesMinItemsError(error, options);
242
+ if (rbacRolesMsg) {
243
+ return rbacRolesMsg;
244
+ }
245
+
138
246
  // Handle pattern errors with special formatting
139
247
  if (error.keyword === 'pattern') {
140
- return formatPatternError(field, error);
248
+ return formatPatternError(field, error, options);
141
249
  }
142
250
  if (error.keyword === 'additionalProperties') {
143
251
  return formatAdditionalPropertiesError(field, error);
144
252
  }
253
+ if (error.keyword === 'oneOf' || error.keyword === 'anyOf') {
254
+ return formatOneOfAnyOfError(field, error);
255
+ }
256
+ if (error.keyword === 'const') {
257
+ return formatConstError(field, error);
258
+ }
145
259
 
146
260
  // Use object lookup for keyword-specific messages
147
261
  const formatters = createKeywordFormatters(field, error);
@@ -157,18 +271,19 @@ function formatSingleError(error) {
157
271
  *
158
272
  * @function formatValidationErrors
159
273
  * @param {Array} errors - Raw validation errors from Ajv
274
+ * @param {Object} [options] - Optional; pass `{ deploymentManifest }` or `{ rootData }` for RBAC/pattern detail
160
275
  * @returns {Array} Formatted error messages
161
276
  *
162
277
  * @example
163
278
  * const messages = formatValidationErrors(ajvErrors);
164
279
  * // Returns: ['Port must be between 1 and 65535', 'Missing required field: displayName']
165
280
  */
166
- function formatValidationErrors(errors) {
281
+ function formatValidationErrors(errors, options) {
167
282
  if (!Array.isArray(errors)) {
168
283
  return ['Unknown validation error'];
169
284
  }
170
285
 
171
- return errors.map(formatSingleError);
286
+ return errors.map((e) => formatSingleError(e, options));
172
287
  }
173
288
 
174
289
  /**
@@ -195,5 +310,6 @@ module.exports = {
195
310
  formatValidationErrors,
196
311
  formatMissingDbPasswordError,
197
312
  getPatternDescription,
313
+ getValueAtInstancePath,
198
314
  PATTERN_DESCRIPTIONS
199
315
  };
@@ -1,3 +1,4 @@
1
+ const { formatBlockingError } = require('../cli-test-layout-chalk');
1
2
  /**
2
3
  * HTTP Status Error Formatters
3
4
  *
@@ -123,7 +124,7 @@ function addAuthenticationGuidance(lines, errorData) {
123
124
 
124
125
  function formatAuthenticationError(errorData) {
125
126
  const lines = [];
126
- lines.push(chalk.red('Authentication Failed\n'));
127
+ lines.push(formatBlockingError('Authentication Failed\n'));
127
128
 
128
129
  addControllerUrlInfo(lines, errorData);
129
130
  addAttemptedUrlsInfo(lines, errorData);
@@ -148,7 +149,7 @@ function formatAuthenticationError(errorData) {
148
149
  */
149
150
  function formatServerError(errorData) {
150
151
  const lines = [];
151
- lines.push(chalk.red('Server Error\n'));
152
+ lines.push(formatBlockingError('Server Error\n'));
152
153
 
153
154
  // Show controller URL if available
154
155
  if (errorData.controllerUrl) {
@@ -183,7 +184,7 @@ function formatServerError(errorData) {
183
184
  */
184
185
  function formatConflictError(errorData) {
185
186
  const lines = [];
186
- lines.push(chalk.red('Conflict\n'));
187
+ lines.push(formatBlockingError('Conflict\n'));
187
188
 
188
189
  // Show controller URL if available
189
190
  if (errorData.controllerUrl) {
@@ -252,7 +253,7 @@ function getNotFoundGuidance(detail) {
252
253
  */
253
254
  function formatNotFoundError(errorData) {
254
255
  const lines = [];
255
- lines.push(chalk.red('Not Found\n'));
256
+ lines.push(formatBlockingError('Not Found\n'));
256
257
 
257
258
  // Show controller URL if available
258
259
  if (errorData.controllerUrl) {
@@ -289,7 +290,7 @@ function formatNotFoundError(errorData) {
289
290
  */
290
291
  function formatGenericError(errorData, statusCode) {
291
292
  const lines = [];
292
- lines.push(chalk.red(`❌ Error (HTTP ${statusCode})\n`));
293
+ lines.push(chalk.red(`✖ Error (HTTP ${statusCode})\n`));
293
294
 
294
295
  // Show controller URL if available
295
296
  if (errorData.controllerUrl) {
@@ -1,3 +1,4 @@
1
+ const { formatBlockingError } = require('../cli-test-layout-chalk');
1
2
  /**
2
3
  * Network Error Formatters
3
4
  *
@@ -135,7 +136,7 @@ function addCorrelationId(lines, errorData) {
135
136
  */
136
137
  function formatNetworkError(errorMessage, errorData) {
137
138
  const lines = [];
138
- lines.push(chalk.red('Network Error\n'));
139
+ lines.push(formatBlockingError('Network Error\n'));
139
140
 
140
141
  addControllerUrlHeader(lines, errorData);
141
142
 
@@ -1,3 +1,4 @@
1
+ const { formatBlockingError } = require('../cli-test-layout-chalk');
1
2
  /**
2
3
  * Permission Error Formatters
3
4
  *
@@ -103,7 +104,7 @@ function getPermissionDetailLines(errorData) {
103
104
  */
104
105
  function formatPermissionError(errorData) {
105
106
  const lines = [];
106
- lines.push(chalk.red('Permission Denied\n'));
107
+ lines.push(formatBlockingError('Permission Denied\n'));
107
108
 
108
109
  if (errorData.detail) {
109
110
  lines.push(chalk.yellow(errorData.detail));
@@ -1,3 +1,4 @@
1
+ const { formatBlockingError } = require('../cli-test-layout-chalk');
1
2
  /**
2
3
  * Validation Error Formatters
3
4
  *
@@ -111,7 +112,7 @@ function addValidationGuidance(lines, hasErrors) {
111
112
  */
112
113
  function formatValidationError(errorData) {
113
114
  const lines = [];
114
- lines.push(chalk.red('Validation Error\n'));
115
+ lines.push(formatBlockingError('Validation Error\n'));
115
116
 
116
117
  addValidationErrorMessage(lines, errorData);
117
118
  addValidationErrorsList(lines, errorData.errors);
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Builds Handlebars context and generates env.template content for external systems.
3
+ * Single source for create, download, split, and repair so env.template structure is consistent.
4
+ *
5
+ * @fileoverview External system env.template generation
6
+ * @author AI Fabrix Team
7
+ * @version 2.0.0
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const path = require('path');
13
+ const fs = require('fs');
14
+ const Handlebars = require('handlebars');
15
+ const { systemKeyToKvPrefix, kvEnvKeyToPath, securityKeyToVar } = require('./credential-secrets-env');
16
+
17
+ /**
18
+ * Builds hint string from portalInput (options → enum, validation → min-max or pattern).
19
+ * @param {Object} portalInput - Portal input config (label, options, validation)
20
+ * @returns {string} Hint suffix for comment
21
+ */
22
+ function buildPortalInputHint(portalInput) {
23
+ if (!portalInput || typeof portalInput !== 'object') return '';
24
+ const parts = [];
25
+ if (Array.isArray(portalInput.options) && portalInput.options.length > 0) {
26
+ parts.push(`enum ${portalInput.options.join(',')}`);
27
+ }
28
+ const v = portalInput.validation;
29
+ if (v && typeof v === 'object') {
30
+ if (typeof v.minLength === 'number' || typeof v.maxLength === 'number') {
31
+ parts.push('min-max');
32
+ } else if (typeof v.pattern === 'string' && v.pattern) {
33
+ parts.push('pattern');
34
+ }
35
+ }
36
+ return parts.length ? ` - ${parts.join(', ')}` : '';
37
+ }
38
+
39
+ /** Fallback security keys by auth method when authentication.security is absent. */
40
+ const FALLBACK_SECURITY_BY_AUTH = {
41
+ oauth2: ['clientId', 'clientSecret'],
42
+ oauth: ['clientId', 'clientSecret'],
43
+ aad: ['clientId', 'clientSecret'],
44
+ apikey: ['apiKey'],
45
+ apiKey: ['apiKey'],
46
+ basic: ['username', 'password'],
47
+ queryParam: ['paramValue'],
48
+ oidc: [],
49
+ hmac: ['signingSecret'],
50
+ bearer: ['bearerToken'],
51
+ token: ['bearerToken'],
52
+ none: []
53
+ };
54
+
55
+ /**
56
+ * Builds authSecureVars array from system authentication.security (or fallback by auth type).
57
+ * @param {Object} system - System object with key and authentication
58
+ * @returns {Array<{name: string, value: string}>}
59
+ */
60
+ function buildAuthSecureVarsFromSystem(system) {
61
+ const authSecureVars = [];
62
+ const systemKey = system?.key || 'external-system';
63
+ const prefix = systemKeyToKvPrefix(systemKey);
64
+ if (!prefix) return authSecureVars;
65
+ const security = system?.authentication?.security || system?.auth?.security;
66
+ const authMethod = (system?.authentication?.method || system?.authentication?.type ||
67
+ system?.auth?.method || system?.auth?.type || 'apikey').toLowerCase();
68
+ if (security && typeof security === 'object' && Object.keys(security).length > 0) {
69
+ for (const key of Object.keys(security)) {
70
+ const envName = `KV_${prefix}_${securityKeyToVar(key)}`;
71
+ const pathVal = kvEnvKeyToPath(envName, systemKey);
72
+ authSecureVars.push({ name: envName, value: pathVal || `kv://${systemKey}/${key}` });
73
+ }
74
+ } else {
75
+ const keys = FALLBACK_SECURITY_BY_AUTH[authMethod] || FALLBACK_SECURITY_BY_AUTH.apikey;
76
+ for (const key of keys) {
77
+ authSecureVars.push({
78
+ name: `KV_${prefix}_${securityKeyToVar(key)}`,
79
+ value: `kv://${systemKey}/${key}`
80
+ });
81
+ }
82
+ }
83
+ return authSecureVars;
84
+ }
85
+
86
+ /**
87
+ * Builds configuration array with name, value, comment from system.configuration.
88
+ * @param {Object} system - System object with configuration array
89
+ * @returns {Array<{name: string, value: string, comment: string}>}
90
+ */
91
+ function buildConfigurationEntries(system) {
92
+ const configuration = [];
93
+ const configList = Array.isArray(system?.configuration) ? system.configuration : [];
94
+ for (const entry of configList) {
95
+ if (!entry || !entry.name) continue;
96
+ const label = entry.portalInput?.label || entry.name;
97
+ const hint = buildPortalInputHint(entry.portalInput || {});
98
+ let value = entry.value !== undefined && entry.value !== null ? String(entry.value) : '';
99
+ if (entry.location === 'keyvault' && value && !value.startsWith('kv://')) value = `kv://${value}`;
100
+ configuration.push({ name: entry.name, value, comment: `${label}${hint}` });
101
+ }
102
+ return configuration;
103
+ }
104
+
105
+ /**
106
+ * Builds template context from system object for env.template.hbs.
107
+ * @param {Object} system - Full system object (e.g. deployment.system or parsed system file)
108
+ * @returns {{ authMethod: string, authSecureVars: Array<{name: string, value: string}>, authNonSecureVarNames: string[], configuration: Array<{name: string, value: string, comment: string}> }}
109
+ */
110
+ function buildExternalEnvTemplateContext(system) {
111
+ const authMethod = (system?.authentication?.method ||
112
+ system?.authentication?.type ||
113
+ system?.auth?.method ||
114
+ system?.auth?.type ||
115
+ 'apikey').toLowerCase();
116
+ const authSecureVars = buildAuthSecureVarsFromSystem(system);
117
+ const authVars = system?.authentication?.variables || system?.auth?.variables || {};
118
+ const authNonSecureVarNames = Object.keys(authVars);
119
+ const configuration = buildConfigurationEntries(system);
120
+ return {
121
+ authMethod,
122
+ authSecureVars,
123
+ authNonSecureVarNames,
124
+ configuration
125
+ };
126
+ }
127
+
128
+ /** Inline fallback when env.template.hbs is missing or unreadable (e.g. CI path or bundled). */
129
+ const DEFAULT_ENV_TEMPLATE_HBS = `# Environment variables for external system integration
130
+ # Use kv:// (or aifabrix secret set) for sensitive values; plain values for non-sensitive configuration.
131
+ #
132
+
133
+ {{#if authMethod}}
134
+ # Authentication
135
+ # Type: {{authMethod}}
136
+ {{#each authSecureVars}}
137
+ {{name}}={{value}}
138
+ {{/each}}
139
+ {{#if authNonSecureVarNames}}
140
+ # Non-secure (e.g. URLs): {{#each authNonSecureVarNames}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}
141
+ {{/if}}
142
+
143
+ {{/if}}
144
+ {{#if configuration.length}}
145
+ # Configuration
146
+ {{#each configuration}}
147
+ # {{comment}}
148
+ {{name}}={{value}}
149
+ {{/each}}
150
+ {{/if}}
151
+ `;
152
+
153
+ /**
154
+ * Generates env.template content from system using the Handlebars template.
155
+ * @param {Object} system - Full system object (e.g. deployment.system or parsed system file)
156
+ * @returns {string} Rendered env.template content
157
+ */
158
+ function generateExternalEnvTemplateContent(system) {
159
+ if (!system || typeof system !== 'object') {
160
+ return '# Environment variables for external system integration\n# Use kv:// (or aifabrix secret set) for sensitive values.\n\n';
161
+ }
162
+ let templateContent;
163
+ try {
164
+ const templatePath = path.join(__dirname, '..', '..', 'templates', 'external-system', 'env.template.hbs');
165
+ templateContent = fs.readFileSync(templatePath, 'utf8');
166
+ } catch (_) {
167
+ templateContent = undefined;
168
+ }
169
+ if (typeof templateContent !== 'string' || !templateContent.trim()) {
170
+ templateContent = DEFAULT_ENV_TEMPLATE_HBS;
171
+ }
172
+ const template = Handlebars.compile(templateContent);
173
+ const context = buildExternalEnvTemplateContext(system);
174
+ return template(context);
175
+ }
176
+
177
+ module.exports = {
178
+ buildExternalEnvTemplateContext,
179
+ generateExternalEnvTemplateContent
180
+ };
@@ -133,6 +133,10 @@ function buildSecretPaths(systemKey, authType) {
133
133
  * @param {Object} [params.authentication] - Full authentication object (authType used if authType not set)
134
134
  * @returns {Object} Template context
135
135
  */
136
+ function rbacOptionalFilename(normalizedExt) {
137
+ return normalizedExt === '.yaml' || normalizedExt === '.yml' ? 'rbac.yaml' : 'rbac.json';
138
+ }
139
+
136
140
  function buildExternalReadmeContext(params = {}) {
137
141
  const appName = params.appName || params.systemKey || 'external-system';
138
142
  const systemKey = params.systemKey || appName;
@@ -140,9 +144,11 @@ function buildExternalReadmeContext(params = {}) {
140
144
  const description = params.description || `External system integration for ${systemKey}`;
141
145
  const systemType = params.systemType || 'openapi';
142
146
  const fileExt = params.fileExt !== undefined ? params.fileExt : '.json';
147
+ const normalizedExt = fileExt && fileExt.startsWith('.') ? fileExt : `.${fileExt || 'json'}`;
143
148
  const datasources = normalizeDatasources(params.datasources, systemKey, fileExt);
144
149
  const authType = params.authType || params.authentication?.type || params.authentication?.method || params.authentication?.authType;
145
150
  const secretPaths = buildSecretPaths(systemKey, authType);
151
+ const rbacOptionalFile = rbacOptionalFilename(normalizedExt);
146
152
 
147
153
  return {
148
154
  appName,
@@ -150,7 +156,8 @@ function buildExternalReadmeContext(params = {}) {
150
156
  displayName,
151
157
  description,
152
158
  systemType,
153
- fileExt: fileExt && fileExt.startsWith('.') ? fileExt : `.${fileExt || 'json'}`,
159
+ fileExt: normalizedExt,
160
+ rbacOptionalFile,
154
161
  datasourceCount: datasources.length,
155
162
  hasDatasources: datasources.length > 0,
156
163
  datasources,