@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
@@ -16,6 +16,17 @@ const yaml = require('js-yaml');
16
16
  const crypto = require('crypto');
17
17
  const logger = require('./logger');
18
18
  const pathsUtil = require('./paths');
19
+ const { mergeFlatSecretsYamlPreservingComments } = require('./secrets-yaml-preserve');
20
+ const {
21
+ generateDatabasePasswordValueForKey,
22
+ generateDatabaseUrlValueForKey,
23
+ parseDatabaseSecretKey
24
+ } = require('../parameters/database-secret-values');
25
+
26
+ /** Lazy require so tests can jest.spyOn getInfraParameterCatalog after module load. */
27
+ function infraParameterCatalogModule() {
28
+ return require('../parameters/infra-parameter-catalog');
29
+ }
19
30
 
20
31
  /**
21
32
  * Parse key-value pairs from YAML-like lines (last occurrence wins per key).
@@ -44,7 +55,9 @@ function parseYamlKeyValueLines(content) {
44
55
  /**
45
56
  * Parse YAML content tolerating duplicate keys (last occurrence wins).
46
57
  * Use for secrets files that may have been appended to repeatedly.
47
- * Tries yaml.load first; on "duplicate key" error falls back to line-by-line parse.
58
+ * Tries yaml.load first; on duplicate-key errors or other parse failures (e.g. `{}` on line 1
59
+ * then `key: value` lines — invalid multi-document without `---`) falls back to line-by-line
60
+ * parse for flat key: value secrets files.
48
61
  *
49
62
  * @param {string} content - Raw YAML content
50
63
  * @returns {Object} Parsed object (last value wins for duplicate keys)
@@ -56,13 +69,11 @@ function loadYamlTolerantOfDuplicateKeys(content) {
56
69
  try {
57
70
  const parsed = yaml.load(content);
58
71
  return parsed && typeof parsed === 'object' ? parsed : {};
59
- } catch (err) {
60
- const msg = err.message || '';
61
- if (!msg.includes('duplicate') && !msg.includes('duplicated mapping')) {
62
- throw err;
63
- }
72
+ } catch {
73
+ // Duplicate keys, `{}` + appended YAML (invalid multi-doc), or other bad merges —
74
+ // flat secrets files recover via line parse (last key wins).
75
+ return parseYamlKeyValueLines(content);
64
76
  }
65
- return parseYamlKeyValueLines(content);
66
77
  }
67
78
 
68
79
  /**
@@ -104,18 +115,37 @@ function findMissingSecretKeys(envTemplate, existingSecrets) {
104
115
  return missingKeys;
105
116
  }
106
117
 
118
+ /**
119
+ * Resolve app directory for database key generation (application.yaml lookup).
120
+ * @param {string} key - Secret key
121
+ * @returns {string|null}
122
+ */
123
+ function resolveAppDirForDatabaseKey(key) {
124
+ const parsed = parseDatabaseSecretKey(key);
125
+ if (!parsed) return null;
126
+ try {
127
+ const builderPath = pathsUtil.getBuilderPath(parsed.appKey);
128
+ if (fs.existsSync(builderPath)) return builderPath;
129
+ } catch {
130
+ /* ignore */
131
+ }
132
+ try {
133
+ const intPath = pathsUtil.getIntegrationPath(parsed.appKey);
134
+ if (fs.existsSync(intPath)) return intPath;
135
+ } catch {
136
+ /* ignore */
137
+ }
138
+ return null;
139
+ }
140
+
107
141
  /**
108
142
  * Generate database password value for a key (databases-*-passwordKeyVault)
109
143
  * @param {string} key - Secret key name
110
144
  * @returns {string|null} Password string or null if key does not match
111
145
  */
112
146
  function generateDbPasswordValue(key) {
113
- const dbPasswordMatch = key.match(/^databases-([a-z0-9-_]+)-\d+-passwordKeyVault$/i);
114
- if (!dbPasswordMatch) return null;
115
- const appName = dbPasswordMatch[1];
116
- if (appName === 'miso-controller') return 'miso_pass123';
117
- const dbName = appName.replace(/-/g, '_');
118
- return `${dbName}_pass123`;
147
+ const appDir = resolveAppDirForDatabaseKey(key);
148
+ return generateDatabasePasswordValueForKey(key, appDir);
119
149
  }
120
150
 
121
151
  /**
@@ -124,44 +154,98 @@ function generateDbPasswordValue(key) {
124
154
  * @returns {string|null} URL string or null if key does not match
125
155
  */
126
156
  function generateDbUrlValue(key) {
127
- const dbUrlMatch = key.match(/^databases-([a-z0-9-_]+)-\d+-urlKeyVault$/i);
128
- if (!dbUrlMatch) return null;
129
- const appName = dbUrlMatch[1];
130
- if (appName === 'miso-controller') {
131
- return 'postgresql://miso_user:miso_pass123@${DB_HOST}:${DB_PORT}/miso';
157
+ const appDir = resolveAppDirForDatabaseKey(key);
158
+ return generateDatabaseUrlValueForKey(key, appDir);
159
+ }
160
+
161
+ /**
162
+ * @param {string} key - Secret key
163
+ * @param {Record<string, string>|undefined} placeholderContext - Merged infra.parameter.yaml defaults + CLI overrides
164
+ * @returns {string|undefined} Value if catalog handled key; undefined to use legacy rules
165
+ */
166
+ function tryGenerateSecretValueFromCatalog(key, placeholderContext) {
167
+ let catalogEntry;
168
+ try {
169
+ catalogEntry = infraParameterCatalogModule().getInfraParameterCatalog().findEntryForKey(key);
170
+ } catch (err) {
171
+ logger.warn(`infra parameter catalog unavailable (${err.message}); using legacy generators.`);
172
+ return undefined;
173
+ }
174
+ if (!catalogEntry || !catalogEntry.generator) return undefined;
175
+ const g = catalogEntry.generator.type;
176
+ if (g === 'databasePassword') {
177
+ const v = generateDbPasswordValue(key);
178
+ return v !== null ? v : undefined;
179
+ }
180
+ if (g === 'databaseUrl') {
181
+ const v = generateDbUrlValue(key);
182
+ return v !== null ? v : undefined;
132
183
  }
133
- const dbName = appName.replace(/-/g, '_');
134
- return `postgresql://${dbName}_user:${dbName}_pass123@\${DB_HOST}:\${DB_PORT}/${dbName}`;
184
+ if (
185
+ g === 'randomBytes32' ||
186
+ g === 'randomAlphanumeric' ||
187
+ g === 'password' ||
188
+ g === 'emptyString' ||
189
+ g === 'emptyAllowed' ||
190
+ g === 'literal'
191
+ ) {
192
+ return infraParameterCatalogModule().generateValueFromCatalogEntry(key, catalogEntry, crypto, placeholderContext);
193
+ }
194
+ return undefined;
135
195
  }
136
196
 
137
197
  /**
138
- * Generates secret value based on key name
139
- * @function generateSecretValue
140
- * @param {string} key - Secret key name
141
- * @returns {string} Generated secret value
198
+ * Last-resort generation when infra.parameter.yaml cannot be loaded (e.g. tests, broken install).
199
+ * @param {string} key
200
+ * @returns {string}
142
201
  */
143
- function generateSecretValue(key) {
202
+ function legacySecretValueWhenCatalogUnavailable(key) {
144
203
  const keyLower = key.toLowerCase();
145
-
146
204
  if (keyLower.includes('password')) {
147
205
  const dbPassword = generateDbPasswordValue(key);
148
206
  if (dbPassword !== null) return dbPassword;
149
207
  return crypto.randomBytes(32).toString('base64');
150
208
  }
151
-
152
209
  if (keyLower.includes('url') || keyLower.includes('uri')) {
153
210
  const dbUrl = generateDbUrlValue(key);
154
211
  if (dbUrl !== null) return dbUrl;
155
212
  return '';
156
213
  }
157
-
158
214
  if (keyLower.includes('key') || keyLower.includes('secret') || keyLower.includes('token')) {
159
215
  return crypto.randomBytes(32).toString('base64');
160
216
  }
161
-
162
217
  return '';
163
218
  }
164
219
 
220
+ /**
221
+ * @param {string} key - Secret key name
222
+ * @param {Record<string, string>|undefined} placeholderContext - Optional merged catalog defaults + CLI (e.g. up-infra)
223
+ * @returns {string} Generated secret value
224
+ */
225
+ function generateSecretValue(key, placeholderContext) {
226
+ const fromCatalog = tryGenerateSecretValueFromCatalog(key, placeholderContext);
227
+ if (fromCatalog !== undefined) return fromCatalog;
228
+
229
+ const dbPassword = generateDbPasswordValue(key);
230
+ if (dbPassword !== null) return dbPassword;
231
+ const dbUrl = generateDbUrlValue(key);
232
+ if (dbUrl !== null) return dbUrl;
233
+
234
+ try {
235
+ infraParameterCatalogModule().getInfraParameterCatalog();
236
+ } catch {
237
+ logger.warn(
238
+ `Secret key "${key}": infra parameter catalog unavailable; using legacy name heuristics.`
239
+ );
240
+ return legacySecretValueWhenCatalogUnavailable(key);
241
+ }
242
+
243
+ throw new Error(
244
+ `Cannot generate secret for key "${key}": no matching rule in infra.parameter.yaml. ` +
245
+ 'Add a catalog entry or run `aifabrix parameters validate` after fixing env.template references.'
246
+ );
247
+ }
248
+
165
249
  /**
166
250
  * Loads existing secrets from file
167
251
  * @function loadExistingSecrets
@@ -196,9 +280,26 @@ function saveSecretsFile(resolvedPath, secrets) {
196
280
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
197
281
  }
198
282
 
283
+ if (fs.existsSync(resolvedPath)) {
284
+ try {
285
+ const existing = fs.readFileSync(resolvedPath, 'utf8');
286
+ const mergedContent = mergeFlatSecretsYamlPreservingComments(existing, secrets, {
287
+ yaml,
288
+ dumpOpts: YAML_DUMP_OPTS
289
+ });
290
+ if (mergedContent !== null) {
291
+ fs.writeFileSync(resolvedPath, mergedContent + (mergedContent.endsWith('\n') ? '' : '\n'), { mode: 0o600 });
292
+ return;
293
+ }
294
+ } catch (err) {
295
+ logger.warn(`Could not preserve comments when writing secrets file: ${err.message}; rewriting YAML.`);
296
+ // fall through to dump overwrite
297
+ }
298
+ }
299
+
199
300
  const yamlContent = yaml.dump(secrets, {
200
301
  indent: 2,
201
- lineWidth: 120,
302
+ lineWidth: -1,
202
303
  noRefs: true,
203
304
  sortKeys: false
204
305
  });
@@ -206,7 +307,21 @@ function saveSecretsFile(resolvedPath, secrets) {
206
307
  fs.writeFileSync(resolvedPath, yamlContent, { mode: 0o600 });
207
308
  }
208
309
 
209
- const YAML_DUMP_OPTS = { indent: 2, lineWidth: 120, noRefs: true, sortKeys: false };
310
+ const YAML_DUMP_OPTS = { indent: 2, lineWidth: -1, noRefs: true, sortKeys: false };
311
+
312
+ /**
313
+ * @param {string} resolvedPath
314
+ * @returns {string}
315
+ */
316
+ function readSecretsFileUtf8OrWarnEmpty(resolvedPath) {
317
+ try {
318
+ const raw = fs.readFileSync(resolvedPath, 'utf8');
319
+ return typeof raw === 'string' ? raw : '';
320
+ } catch (err) {
321
+ logger.warn(`Could not read existing secrets file: ${err.message}; appending new keys only.`);
322
+ return '';
323
+ }
324
+ }
210
325
 
211
326
  /**
212
327
  * Merges secret keys into the secrets file (load existing, merge, overwrite file).
@@ -253,12 +368,13 @@ function appendSecretsToFile(resolvedPath, secrets) {
253
368
  return;
254
369
  }
255
370
 
256
- let existing = '';
371
+ const existing = readSecretsFileUtf8OrWarnEmpty(resolvedPath);
257
372
  try {
258
- const raw = fs.readFileSync(resolvedPath, 'utf8');
259
- existing = typeof raw === 'string' ? raw : '';
260
- } catch (err) {
261
- logger.warn(`Could not read existing secrets file: ${err.message}; appending new keys only.`);
373
+ yaml.load(existing);
374
+ } catch {
375
+ const mergedObj = { ...loadExistingSecrets(resolvedPath), ...secrets };
376
+ saveSecretsFile(resolvedPath, mergedObj);
377
+ return;
262
378
  }
263
379
  const separator = existing.length > 0 && !existing.endsWith('\n') ? '\n' : '';
264
380
  fs.writeFileSync(resolvedPath, existing + separator + appendContent, { mode: 0o600 });
@@ -296,7 +412,7 @@ async function generateMissingSecrets(envTemplate, secretsPath) {
296
412
 
297
413
  appendSecretsToFile(resolvedPath, newSecrets);
298
414
 
299
- logger.log(`✓ Generated ${missingKeys.length} missing secret key(s): ${missingKeys.join(', ')}`);
415
+ logger.log(`✔ Generated ${missingKeys.length} missing secret key(s): ${missingKeys.join(', ')}`);
300
416
  return missingKeys;
301
417
  }
302
418
 
@@ -314,6 +430,15 @@ async function generateMissingSecrets(envTemplate, secretsPath) {
314
430
  * await createDefaultSecrets('~/.aifabrix/secrets.yaml');
315
431
  * // Default secrets file is created
316
432
  */
433
+ /** Minimal seed keys for first-time secrets file; values match infra.parameter.yaml generators. */
434
+ const DEFAULT_SECRETS_SEED_KEYS = [
435
+ 'postgres-passwordKeyVault',
436
+ 'redis-passwordKeyVault',
437
+ 'redis-url',
438
+ 'keycloak-admin-passwordKeyVault',
439
+ 'keycloak-web-server-url'
440
+ ];
441
+
317
442
  async function createDefaultSecrets(secretsPath) {
318
443
  const resolvedPath = secretsPath.startsWith('~')
319
444
  ? path.join(os.homedir(), secretsPath.slice(1))
@@ -324,22 +449,17 @@ async function createDefaultSecrets(secretsPath) {
324
449
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
325
450
  }
326
451
 
327
- const defaultSecrets = `# Local Development Secrets
328
- # Production uses Azure KeyVault
329
-
330
- # Database Secrets
331
- postgres-passwordKeyVault: "admin123"
332
-
333
- # Redis Secrets
334
- redis-passwordKeyVault: ""
335
- redis-url: "redis://\${REDIS_HOST}:\${REDIS_PORT}"
452
+ const seeded = {};
453
+ for (const k of DEFAULT_SECRETS_SEED_KEYS) {
454
+ seeded[k] = generateSecretValue(k);
455
+ }
336
456
 
337
- # Keycloak Secrets
338
- keycloak-admin-passwordKeyVault: "admin123"
339
- keycloak-server-url: "http://\${KEYCLOAK_HOST}:\${KEYCLOAK_PORT}"
340
- `;
457
+ const header =
458
+ '# Local development secrets (catalog-aligned seed). Run aifabrix up-infra and aifabrix resolve <app> --force for full keys.\n' +
459
+ '# Production uses Azure Key Vault.\n\n';
460
+ const body = yaml.dump(seeded, { indent: 2, lineWidth: -1, noRefs: true, sortKeys: false });
341
461
 
342
- fs.writeFileSync(resolvedPath, defaultSecrets, { mode: 0o600 });
462
+ fs.writeFileSync(resolvedPath, header + body, { mode: 0o600 });
343
463
  }
344
464
 
345
465
  module.exports = {
@@ -46,6 +46,41 @@ function isCommentOrEmptyLine(line) {
46
46
  /** Regex for kv:// path (allows slashes, e.g. kv://hubspot/clientId) */
47
47
  const KV_REF_PATTERN = /kv:\/\/([a-zA-Z0-9_\-/]+)/g;
48
48
 
49
+ const { resolveBashKvFromProcessEnv } = require('./secrets-bash-kv');
50
+
51
+ /**
52
+ * Last-resort when infra.parameter.yaml cannot be read (e.g. Jest suites that mock `fs`;
53
+ * catalog uses `node:fs`, which is often the same mocked instance). Must stay in sync with
54
+ * `generator.type: emptyAllowed` keys in lib/schema/infra.parameter.yaml.
55
+ */
56
+ const EMPTY_ALLOWED_KV_FALLBACK = new Set(['redis-passwordKeyVault']);
57
+
58
+ /**
59
+ * Infra catalog keys with generator `emptyAllowed` may be absent from the secrets file;
60
+ * they resolve like an explicit empty string (e.g. local Redis has no password).
61
+ * @param {string} pathStr - kv path segment (flat key or path with slashes)
62
+ * @returns {boolean}
63
+ */
64
+ function isKvKeyAllowedEmptyWhenAbsent(pathStr) {
65
+ if (!pathStr || typeof pathStr !== 'string') return false;
66
+ try {
67
+ const { getInfraParameterCatalog } = require('../parameters/infra-parameter-catalog');
68
+ return getInfraParameterCatalog().isKeyAllowedEmpty(pathStr);
69
+ } catch {
70
+ try {
71
+ const {
72
+ readRelaxedEmptyAllowedKeySet,
73
+ DEFAULT_CATALOG_PATH
74
+ } = require('../parameters/infra-parameter-catalog');
75
+ const set = readRelaxedEmptyAllowedKeySet(DEFAULT_CATALOG_PATH);
76
+ if (set && set.has(pathStr)) return true;
77
+ } catch {
78
+ /* ignore */
79
+ }
80
+ }
81
+ return EMPTY_ALLOWED_KV_FALLBACK.has(pathStr);
82
+ }
83
+
49
84
  /**
50
85
  * Find object key that matches part case-insensitively.
51
86
  * @param {Object} obj - Object to search
@@ -98,19 +133,30 @@ function getValueByPath(secrets, pathStr) {
98
133
  return getValueByNestedPath(secrets, pathStr.split('/'));
99
134
  }
100
135
 
136
+ const { getValueByPathWithEnvScope, mergeSecretsWithPrefixedCopies } =
137
+ require('./secrets-kv-scope').createScopedKvHelpers(getValueByPath);
138
+
139
+ function resolveKvRefValue(secretsMap, pathStr, envKey, effective) {
140
+ const v = getValueByPathWithEnvScope(secretsMap, pathStr, envKey, effective);
141
+ if (v !== undefined && v !== null) return v;
142
+ return resolveBashKvFromProcessEnv(pathStr);
143
+ }
144
+
101
145
  /**
102
146
  * Collect missing kv:// secrets referenced in content (skips commented and empty lines).
103
147
  * Supports path-style refs (e.g. kv://hubspot/clientId). Returns unique refs.
104
148
  * @function collectMissingSecrets
105
149
  * @param {string} content - Text content
106
150
  * @param {Object} secrets - Available secrets (flat or nested)
151
+ * @param {{ effective?: boolean, envKey?: string }|null} [scopedKv] - Optional env-scoped kv resolution
107
152
  * @returns {string[]} Array of missing kv://<path> references (unique)
108
153
  */
109
- function collectMissingSecrets(content, secrets) {
154
+ function collectMissingSecrets(content, secrets, scopedKv = null) {
155
+ const effective = Boolean(scopedKv && scopedKv.effective && scopedKv.envKey);
156
+ const secretsMap = effective ? mergeSecretsWithPrefixedCopies(secrets, scopedKv.envKey) : secrets;
110
157
  const seen = new Set();
111
158
  const missing = [];
112
- const lines = content.split('\n');
113
- for (const line of lines) {
159
+ for (const line of content.split('\n')) {
114
160
  if (isCommentOrEmptyLine(line)) continue;
115
161
  let match;
116
162
  KV_REF_PATTERN.lastIndex = 0;
@@ -118,8 +164,8 @@ function collectMissingSecrets(content, secrets) {
118
164
  const pathStr = match[1];
119
165
  if (seen.has(pathStr)) continue;
120
166
  seen.add(pathStr);
121
- const value = getValueByPath(secrets, pathStr);
122
- if (value === undefined || value === null) {
167
+ const value = resolveKvRefValue(secretsMap, pathStr, scopedKv?.envKey, effective);
168
+ if ((value === undefined || value === null) && !isKvKeyAllowedEmptyWhenAbsent(pathStr)) {
123
169
  missing.push(`kv://${pathStr}`);
124
170
  }
125
171
  }
@@ -157,14 +203,20 @@ function formatMissingSecretsFileInfo(secretsFilePaths) {
157
203
  * @param {string} content - Text content containing kv:// references
158
204
  * @param {Object} secrets - Secrets map (flat or nested)
159
205
  * @param {Object} envVars - Environment variables map for nested interpolation
206
+ * @param {{ effective?: boolean, envKey?: string }|null} [scopedKv] - Optional env-scoped kv resolution
160
207
  * @returns {string} Content with kv:// references replaced
161
208
  */
162
- function replaceKvInContent(content, secrets, envVars) {
209
+ function replaceKvInContent(content, secrets, envVars, scopedKv = null) {
210
+ const effective = Boolean(scopedKv && scopedKv.effective && scopedKv.envKey);
211
+ const secretsMap = effective ? mergeSecretsWithPrefixedCopies(secrets, scopedKv.envKey) : secrets;
163
212
  const lines = content.split('\n');
164
213
  const result = lines.map(line => {
165
214
  if (isCommentOrEmptyLine(line)) return line;
166
215
  return line.replace(KV_REF_PATTERN, (match, pathStr) => {
167
- let value = getValueByPath(secrets, pathStr);
216
+ let value = resolveKvRefValue(secretsMap, pathStr, scopedKv?.envKey, effective);
217
+ if ((value === undefined || value === null) && isKvKeyAllowedEmptyWhenAbsent(pathStr)) {
218
+ value = '';
219
+ }
168
220
  if (typeof value === 'string') {
169
221
  value = value.replace(/\$\{([A-Z_]+)\}/g, (m, envVar) => {
170
222
  return envVars[envVar] || m;
@@ -247,44 +299,23 @@ function getPortFromEnvContent(envContent) {
247
299
  }
248
300
 
249
301
  /**
250
- * Applies developer-id adjustment to port
251
- * @function applyDeveloperIdAdjustment
252
- * @param {number} baseAppPort - Base application port
253
- * @param {number} devIdNum - Developer ID number
254
- * @returns {number} Adjusted port
255
- */
256
- function applyDeveloperIdAdjustment(baseAppPort, devIdNum) {
257
- return devIdNum === 0 ? baseAppPort : (baseAppPort + (devIdNum * 100));
258
- }
259
-
260
- /**
261
- * Calculate application port following override chain and developer-id adjustment
262
- * Override chain: env-config.yaml → config.yaml → application.yaml port
302
+ * Resolve manifest listen port (container port) for the app from override chain.
263
303
  * @async
264
- * @function calculateAppPort
265
304
  * @param {string} [variablesPath] - Path to application config
266
- * @param {Object} localEnv - Local environment config from env-config.yaml and config.yaml
305
+ * @param {Object} localEnv - Merged local env from defaults + config.yaml
267
306
  * @param {string} envContent - Environment content for fallback
268
- * @param {number} devIdNum - Developer ID number
269
- * @returns {Promise<number>} Final application port with developer-id adjustment
307
+ * @returns {Promise<number>} Base listen port (before +10 / +dev*100 local host math)
270
308
  */
271
- async function calculateAppPort(variablesPath, localEnv, envContent, devIdNum) {
272
- // Start with env-config value
309
+ async function resolveManifestListenPort(variablesPath, localEnv, envContent) {
273
310
  let baseAppPort = getPortFromLocalEnv(localEnv);
274
-
275
- // Override with application config port (strongest)
276
311
  const variablesPort = getPortFromVariablesFile(variablesPath);
277
312
  if (variablesPort !== null) {
278
313
  baseAppPort = variablesPort;
279
314
  }
280
-
281
- // Fallback to env content if still no port
282
315
  if (baseAppPort === null || baseAppPort === undefined) {
283
316
  baseAppPort = getPortFromEnvContent(envContent);
284
317
  }
285
-
286
- // Apply developer-id adjustment
287
- return applyDeveloperIdAdjustment(baseAppPort, devIdNum);
318
+ return baseAppPort;
288
319
  }
289
320
 
290
321
  /**
@@ -325,16 +356,6 @@ function getPortVarFromEnvTemplatePath(variablesPath) {
325
356
  }
326
357
  }
327
358
 
328
- /**
329
- * Adjust infra-related ports in resolved .env content for local environment.
330
- * Own case: when we generate .env for envOutputPath (not reload), we use localPort (application.yaml build.localPort or port).
331
- * Sets PORT and the template port var (e.g. MISO_PORT) to localPort so the generated .env is correct for local use.
332
- * @async
333
- * @function adjustLocalEnvPortsInContent
334
- * @param {string} envContent - Resolved .env content
335
- * @param {string} [variablesPath] - Path to application config (to read port and template port var)
336
- * @returns {Promise<string>} Updated content with local ports
337
- */
338
359
  /**
339
360
  * Gets developer ID number
340
361
  * @async
@@ -418,12 +439,13 @@ async function buildEnvVarsForInterpolation(devIdNum) {
418
439
  async function adjustLocalEnvPortsInContent(envContent, variablesPath) {
419
440
  const devIdNum = await getDeveloperIdNumber();
420
441
  const localEnv = await getLocalEnvWithOverrides();
442
+ const { localHostPort } = require('./declarative-url-ports');
421
443
 
422
- const baseAppPort = await calculateAppPort(variablesPath, localEnv, envContent, 0);
423
- const appPort = await calculateAppPort(variablesPath, localEnv, envContent, devIdNum);
444
+ const baseListen = await resolveManifestListenPort(variablesPath, localEnv, envContent);
445
+ const appPort = localHostPort(baseListen, devIdNum);
424
446
 
425
447
  let updated = updatePortVariable(envContent, appPort);
426
- updated = updateLocalhostUrls(updated, baseAppPort, appPort);
448
+ updated = updateLocalhostUrls(updated, baseListen, appPort);
427
449
  updated = await rewriteInfraEndpoints(updated, 'local');
428
450
 
429
451
  const envVars = await buildEnvVarsForInterpolation(devIdNum);
@@ -437,18 +459,6 @@ async function adjustLocalEnvPortsInContent(envContent, variablesPath) {
437
459
  return updated;
438
460
  }
439
461
 
440
- /**
441
- * Ensure secrets map is non-empty or throw a friendly guidance error
442
- * @function ensureNonEmptySecrets
443
- * @param {Object} secrets - Secrets map
444
- * @throws {Error} If secrets is empty
445
- */
446
- function ensureNonEmptySecrets(secrets) {
447
- if (Object.keys(secrets || {}).length === 0) {
448
- throw new Error('No secrets file found. Please create ~/.aifabrix/secrets.local.yaml or configure aifabrix-secrets in config.yaml');
449
- }
450
- }
451
-
452
462
  /**
453
463
  * Validate secrets against the env template (skips commented and empty lines)
454
464
  * @function validateSecrets
@@ -465,6 +475,8 @@ module.exports = {
465
475
  loadEnvConfig,
466
476
  interpolateEnvVars,
467
477
  collectMissingSecrets,
478
+ resolveBashKvFromProcessEnv,
479
+ mergeSecretsWithPrefixedCopies,
468
480
  formatMissingSecretsFileInfo,
469
481
  replaceKvInContent,
470
482
  resolveServicePortsInEnvContent,
@@ -473,7 +485,6 @@ module.exports = {
473
485
  adjustLocalEnvPortsInContent,
474
486
  readYamlAtPath,
475
487
  applyCanonicalSecretsOverride,
476
- ensureNonEmptySecrets,
477
488
  validateSecrets,
478
489
  rewriteInfraEndpoints,
479
490
  getEnvHosts
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Env-scoped kv:// resolution helpers (prefixed keys + in-memory aliases).
3
+ *
4
+ * @fileoverview Split from secrets-helpers for file size limits
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * @param {Function} getValueByPath - From secrets-helpers
13
+ * @returns {{ getValueByPathWithEnvScope: Function, mergeSecretsWithPrefixedCopies: Function }}
14
+ */
15
+ function createScopedKvHelpers(getValueByPath) {
16
+ /**
17
+ * @param {Object} secrets
18
+ * @param {string} pathStr
19
+ * @param {string|null|undefined} envKey
20
+ * @param {boolean} effective
21
+ * @returns {*}
22
+ */
23
+ function getValueByPathWithEnvScope(secrets, pathStr, envKey, effective) {
24
+ if (!effective || !envKey) {
25
+ return getValueByPath(secrets, pathStr);
26
+ }
27
+ const prefix = `${String(envKey).toLowerCase()}-`;
28
+ const prefixedPath = prefix + pathStr;
29
+ const fromPrefixed = getValueByPath(secrets, prefixedPath);
30
+ if (fromPrefixed !== undefined && fromPrefixed !== null) {
31
+ return fromPrefixed;
32
+ }
33
+ return getValueByPath(secrets, pathStr);
34
+ }
35
+
36
+ /**
37
+ * @param {Object} secrets
38
+ * @param {string} envKey
39
+ * @returns {Object}
40
+ */
41
+ function mergeSecretsWithPrefixedCopies(secrets, envKey) {
42
+ if (!secrets || typeof secrets !== 'object' || !envKey) {
43
+ return secrets;
44
+ }
45
+ const prefix = `${String(envKey).toLowerCase()}-`;
46
+ const merged = { ...secrets };
47
+ for (const [k, v] of Object.entries(secrets)) {
48
+ if (typeof k !== 'string' || !k || k.startsWith(prefix)) continue;
49
+ const pk = prefix + k;
50
+ if (merged[pk] === undefined && v !== undefined && v !== null) {
51
+ merged[pk] = v;
52
+ }
53
+ }
54
+ return merged;
55
+ }
56
+
57
+ return { getValueByPathWithEnvScope, mergeSecretsWithPrefixedCopies };
58
+ }
59
+
60
+ module.exports = { createScopedKvHelpers };