@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
@@ -11,13 +11,31 @@
11
11
 
12
12
  const path = require('path');
13
13
  const fs = require('fs');
14
+ const fsRealSync = require('../internal/fs-real-sync');
15
+ const { nodeFs } = require('../internal/node-fs');
14
16
  const chalk = require('chalk');
15
17
  const handlebars = require('handlebars');
16
- const secrets = require('../core/secrets');
18
+ const adminSecrets = require('../core/admin-secrets');
17
19
  const logger = require('../utils/logger');
18
20
  const dockerUtils = require('../utils/docker');
19
21
  const paths = require('../utils/paths');
20
22
  const secretsEnsure = require('../core/secrets-ensure');
23
+ const {
24
+ mergeInfraParameterDefaultsForCli,
25
+ getInfraParameterCatalog,
26
+ readRelaxedCatalogDefaults
27
+ } = require('../parameters/infra-parameter-catalog');
28
+ const { ensureDevCertsIfNeededForRemoteDocker } = require('../utils/ensure-dev-certs-for-remote-docker');
29
+
30
+ /**
31
+ * Lazy-load core/secrets at call time. A top-level require creates a circular dependency:
32
+ * secrets → url-declarative-resolve → compose-generator → compose-generate-docker-compose → this module,
33
+ * which left `generateAdminSecretsEnv` / `formatAdminSecretsContent` undefined on the captured export.
34
+ * @returns {Object} core/secrets module exports (loadSecrets, generateAdminSecretsEnv, …)
35
+ */
36
+ function getCoreSecrets() {
37
+ return require('../core/secrets');
38
+ }
21
39
 
22
40
  /**
23
41
  * Gets infrastructure directory name based on developer ID
@@ -42,21 +60,64 @@ function getInfraProjectName(devId) {
42
60
  }
43
61
 
44
62
  /**
45
- * Check Docker availability
63
+ * User-facing error when Docker/Compose checks fail (tailored by underlying message).
64
+ * @param {string} detail - Error message from ensureDockerAndCompose / Docker CLI
65
+ * @returns {string}
66
+ */
67
+ function formatDockerInfrastructureFailure(detail) {
68
+ const cause = (detail || '').trim() || 'unknown error';
69
+
70
+ if (/Docker Compose is not available/i.test(cause)) {
71
+ return (
72
+ 'Cannot use Docker for infrastructure: Docker Compose check failed (see Cause below).\n\n' +
73
+ `Cause: ${cause}\n\n` +
74
+ 'If Cause mentions TLS, certificate, or handshake, fix client TLS for docker-endpoint (cert.pem, key.pem, ca.pem under ~/.aifabrix/certs/<developer-id>/) or docker-tls-skip-verify when appropriate. ' +
75
+ 'If Cause suggests a missing plugin, install Docker Compose v2 for your user (docker CLI + plugin; no unix socket needed when using tcp:// docker-endpoint). ' +
76
+ 'Or set AIFABRIX_COMPOSE_CMD. Run `aifabrix doctor` for diagnostics.'
77
+ );
78
+ }
79
+
80
+ if (/AIFABRIX_COMPOSE_CMD/i.test(cause) && /is set but failed/i.test(cause)) {
81
+ return (
82
+ 'Cannot use Docker for infrastructure: AIFABRIX_COMPOSE_CMD failed.\n\n' +
83
+ `Cause: ${cause}\n\n` +
84
+ 'Unset or fix AIFABRIX_COMPOSE_CMD, or install a working Compose. Run `aifabrix doctor` for diagnostics.'
85
+ );
86
+ }
87
+
88
+ return (
89
+ 'Cannot use Docker for infrastructure (Docker CLI missing, Compose missing, or remote Docker misconfigured).\n\n' +
90
+ `Cause: ${cause}\n\n` +
91
+ 'Install Docker Engine and Compose on this machine (or set AIFABRIX_COMPOSE_CMD). ' +
92
+ 'If you use docker-endpoint in dev config: install cert.pem, key.pem, and ca.pem for full TLS verify; use `aifabrix dev pin` / ' +
93
+ '`dev init --pin` as needed; or enable TLS skip-verify (config or AIFABRIX_DOCKER_TLS_SKIP_VERIFY) when appropriate. ' +
94
+ 'Run `aifabrix doctor` for diagnostics.'
95
+ );
96
+ }
97
+
98
+ /**
99
+ * Check Docker availability (local daemon or remote via docker-endpoint + TLS).
46
100
  * @async
47
101
  * @returns {Promise<void>}
48
- * @throws {Error} If Docker is not available
102
+ * @throws {Error} If Docker/Compose cannot be used (includes underlying cause)
49
103
  */
50
104
  async function checkDockerAvailability() {
105
+ await ensureDevCertsIfNeededForRemoteDocker();
51
106
  try {
52
107
  await dockerUtils.ensureDockerAndCompose();
53
108
  } catch (error) {
54
- throw new Error('Docker or Docker Compose is not available. Please install and start Docker.');
109
+ const detail = (error && error.message) || String(error);
110
+ throw new Error(formatDockerInfrastructureFailure(detail));
55
111
  }
56
112
  }
57
113
 
58
- /** Default admin password for new local installations when admin-secrets.env is empty */
59
- const DEFAULT_ADMIN_PASSWORD = 'admin123';
114
+ /**
115
+ * Fallback for admin password/email when validated catalog load failed but YAML is still readable.
116
+ * @returns {Record<string, string>}
117
+ */
118
+ function readInfraDefaultScalars() {
119
+ return readRelaxedCatalogDefaults();
120
+ }
60
121
 
61
122
  /**
62
123
  * Log hint to reset Postgres volume when admin password was changed after first init.
@@ -65,23 +126,10 @@ const DEFAULT_ADMIN_PASSWORD = 'admin123';
65
126
  function logVolumeResetHint(infraDir) {
66
127
  logger.log(chalk.yellow(
67
128
  'If Postgres was already started with a different password, login will fail until you reset the volume. ' +
68
- `Run: cd ${infraDir} && docker compose -f compose.yaml -p aifabrix down -v , then run 'aifabrix up-infra --adminPwd <password>' again.`
129
+ `Run: cd ${infraDir} && docker compose -f compose.yaml -p aifabrix down -v , then run 'aifabrix up-infra --adminPassword <password>' again.`
69
130
  ));
70
131
  }
71
132
 
72
- /**
73
- * Apply password to admin-secrets file content (all three password keys).
74
- * @param {string} content - Current file content
75
- * @param {string} password - Password to set
76
- * @returns {string} Updated content
77
- */
78
- function applyPasswordToAdminSecretsContent(content, password) {
79
- return content
80
- .replace(/^POSTGRES_PASSWORD=.*$/m, `POSTGRES_PASSWORD=${password}`)
81
- .replace(/^PGADMIN_DEFAULT_PASSWORD=.*$/m, `PGADMIN_DEFAULT_PASSWORD=${password}`)
82
- .replace(/^REDIS_COMMANDER_PASSWORD=.*$/m, `REDIS_COMMANDER_PASSWORD=${password}`);
83
- }
84
-
85
133
  /**
86
134
  * Sync postgres-passwordKeyVault to the main secrets store (file or remote).
87
135
  * @param {string} password - Password to store
@@ -94,44 +142,150 @@ async function syncPostgresPasswordToStore(password) {
94
142
  }
95
143
  }
96
144
 
145
+ /** Non-email defaults for admin-secrets.env merge (email default comes from infra.parameter.yaml `defaults`). */
146
+ const DEFAULT_ADMIN_OBJ = {
147
+ REDIS_HOST: 'local:redis:6379:0:',
148
+ REDIS_COMMANDER_USER: 'admin'
149
+ };
150
+
151
+ /**
152
+ * Writes merged admin secrets to disk and logs/syncs as needed.
153
+ * @async
154
+ * @param {string} adminSecretsPath - Path to admin-secrets.env
155
+ * @param {Object} adminObj - Decrypted admin secrets object
156
+ * @param {string} passwordToUse - Password to set for Postgres, pgAdmin, Redis Commander
157
+ * @param {boolean} shouldOverwriteWithAdminPwd - Whether this was an explicit admin password update
158
+ * @param {{ updateEmail?: boolean, emailToUse?: string }} [emailOpts]
159
+ */
160
+ async function applyAdminSecretsUpdate(
161
+ adminSecretsPath,
162
+ adminObj,
163
+ passwordToUse,
164
+ shouldOverwriteWithAdminPwd,
165
+ emailOpts
166
+ ) {
167
+ const merged = { ...DEFAULT_ADMIN_OBJ, ...adminObj };
168
+ merged.POSTGRES_PASSWORD = passwordToUse;
169
+ merged.PGADMIN_DEFAULT_PASSWORD = passwordToUse;
170
+ merged.REDIS_COMMANDER_PASSWORD = passwordToUse;
171
+ if (emailOpts && emailOpts.updateEmail && emailOpts.emailToUse) {
172
+ merged.PGADMIN_DEFAULT_EMAIL = emailOpts.emailToUse;
173
+ }
174
+ const content = await getCoreSecrets().formatAdminSecretsContent(merged);
175
+ fsRealSync.writeFileSync(adminSecretsPath, content, { mode: 0o600 });
176
+ if (shouldOverwriteWithAdminPwd) {
177
+ logger.log('Updated admin password in admin-secrets.env.');
178
+ await syncPostgresPasswordToStore(passwordToUse);
179
+ logVolumeResetHint(path.join(paths.getAifabrixSystemDir(), getInfraDirName(0)));
180
+ } else if (emailOpts && emailOpts.updateEmail) {
181
+ logger.log('Updated admin email in admin-secrets.env.');
182
+ } else {
183
+ logger.log('Set default admin password in admin-secrets.env for local use.');
184
+ }
185
+ }
186
+
187
+ function loadAdminMergedDefaultsForInfra(options) {
188
+ try {
189
+ return mergeInfraParameterDefaultsForCli(getInfraParameterCatalog().data, options);
190
+ } catch {
191
+ return mergeInfraParameterDefaultsForCli({}, options);
192
+ }
193
+ }
194
+
195
+ function resolveAdminPasswordAndEmailCli(options, mergedDefaults) {
196
+ const infraDefaults = readInfraDefaultScalars();
197
+ const adminPwdCli = String(options.adminPassword || options.adminPwd || '').trim();
198
+ const adminPwdOverride = adminPwdCli !== '' ? adminPwdCli : null;
199
+ const passwordToUse =
200
+ adminPwdOverride !== null
201
+ ? adminPwdOverride
202
+ : mergedDefaults.adminPassword || infraDefaults.adminPassword || '';
203
+ const emailCli = String(options.adminEmail || '').trim();
204
+ const emailOverride = emailCli !== '' ? emailCli : null;
205
+ const emailToUse =
206
+ emailOverride !== null
207
+ ? emailOverride
208
+ : mergedDefaults.adminEmail || infraDefaults.adminEmail || '';
209
+ return { adminPwdOverride, passwordToUse, emailOverride, emailToUse };
210
+ }
211
+
212
+ function computeAdminSecretsBackfillFlags(adminObj) {
213
+ const needsPasswordBackfill =
214
+ !(adminObj.POSTGRES_PASSWORD && adminObj.POSTGRES_PASSWORD.trim()) ||
215
+ !(adminObj.PGADMIN_DEFAULT_PASSWORD && adminObj.PGADMIN_DEFAULT_PASSWORD.trim()) ||
216
+ !(adminObj.REDIS_COMMANDER_PASSWORD && adminObj.REDIS_COMMANDER_PASSWORD.trim());
217
+ const needsEmailBackfill = !(adminObj.PGADMIN_DEFAULT_EMAIL && adminObj.PGADMIN_DEFAULT_EMAIL.trim());
218
+ return { needsPasswordBackfill, needsEmailBackfill };
219
+ }
220
+
221
+ function resolvePasswordForAdminFile(
222
+ shouldOverwriteWithAdminPwd,
223
+ needsPasswordBackfill,
224
+ passwordToUse,
225
+ adminObj,
226
+ mergedDefaults
227
+ ) {
228
+ if (shouldOverwriteWithAdminPwd || needsPasswordBackfill) {
229
+ return passwordToUse;
230
+ }
231
+ return (
232
+ String(adminObj.POSTGRES_PASSWORD || '').trim() ||
233
+ mergedDefaults.adminPassword ||
234
+ readInfraDefaultScalars().adminPassword ||
235
+ ''
236
+ );
237
+ }
238
+
97
239
  /**
98
240
  * Ensure admin secrets file exists and set admin password.
99
241
  * When adminPwd is provided, update POSTGRES_PASSWORD, PGADMIN_DEFAULT_PASSWORD, REDIS_COMMANDER_PASSWORD
100
242
  * in admin-secrets.env (overwrites existing values). Otherwise only backfill empty fields.
243
+ * Reads and writes using decrypted values; writes encrypted when secrets-encryption key is set.
101
244
  *
102
245
  * @async
103
246
  * @param {Object} [options] - Options
104
- * @param {string} [options.adminPwd] - Override admin password for Postgres, pgAdmin, Redis Commander (updates file when provided)
247
+ * @param {string} [options.adminPassword] - Override admin password (alias: adminPwd)
248
+ * @param {string} [options.adminPwd] - Override admin password for Postgres, pgAdmin, Redis Commander
249
+ * @param {string} [options.adminEmail] - Override pgAdmin default email (matches {{adminEmail}} defaults)
250
+ * @param {string} [options.userPassword] - Reserved for Keycloak user template (secrets use ensureInfraSecrets)
105
251
  * @returns {Promise<string>} Path to admin secrets file
106
252
  */
107
253
  async function ensureAdminSecrets(options = {}) {
108
- const adminPwdOverride = options.adminPwd && typeof options.adminPwd === 'string' && options.adminPwd.trim() !== ''
109
- ? options.adminPwd.trim()
110
- : null;
111
- const passwordToUse = adminPwdOverride || DEFAULT_ADMIN_PASSWORD;
254
+ const mergedDefaults = loadAdminMergedDefaultsForInfra(options);
255
+ const { adminPwdOverride, passwordToUse, emailOverride, emailToUse } = resolveAdminPasswordAndEmailCli(
256
+ options,
257
+ mergedDefaults
258
+ );
259
+ const adminSecretsPath = path.join(paths.getAifabrixSystemDir(), 'admin-secrets.env');
112
260
 
113
- const adminSecretsPath = path.join(paths.getAifabrixHome(), 'admin-secrets.env');
114
- if (!fs.existsSync(adminSecretsPath)) {
261
+ if (!fsRealSync.existsSync(adminSecretsPath)) {
115
262
  logger.log('Generating admin-secrets.env...');
116
- await secrets.generateAdminSecretsEnv(undefined);
263
+ await getCoreSecrets().generateAdminSecretsEnv(undefined);
264
+ return adminSecretsPath;
117
265
  }
118
- let content = fs.readFileSync(adminSecretsPath, 'utf8');
119
- const needsBackfill = /^POSTGRES_PASSWORD=\s*$/m.test(content) ||
120
- /^PGADMIN_DEFAULT_PASSWORD=\s*$/m.test(content) ||
121
- /^REDIS_COMMANDER_PASSWORD=\s*$/m.test(content);
266
+
267
+ const adminObj = await adminSecrets.readAndDecryptAdminSecrets(adminSecretsPath);
268
+ const { needsPasswordBackfill, needsEmailBackfill } = computeAdminSecretsBackfillFlags(adminObj);
122
269
  const shouldOverwriteWithAdminPwd = adminPwdOverride !== null;
270
+ const shouldOverwriteEmail = emailOverride !== null;
271
+ const updateEmail = shouldOverwriteEmail || needsEmailBackfill;
123
272
 
124
- if (shouldOverwriteWithAdminPwd) {
125
- content = applyPasswordToAdminSecretsContent(content, passwordToUse);
126
- fs.writeFileSync(adminSecretsPath, content, { mode: 0o600 });
127
- logger.log('Updated admin password in admin-secrets.env.');
128
- await syncPostgresPasswordToStore(passwordToUse);
129
- logVolumeResetHint(path.join(paths.getAifabrixHome(), getInfraDirName(0)));
130
- } else if (needsBackfill) {
131
- content = applyPasswordToAdminSecretsContent(content, passwordToUse);
132
- fs.writeFileSync(adminSecretsPath, content, { mode: 0o600 });
133
- logger.log('Set default admin password in admin-secrets.env for local use.');
273
+ if (!shouldOverwriteWithAdminPwd && !needsPasswordBackfill && !updateEmail) {
274
+ return adminSecretsPath;
134
275
  }
276
+
277
+ const passwordForFile = resolvePasswordForAdminFile(
278
+ shouldOverwriteWithAdminPwd,
279
+ needsPasswordBackfill,
280
+ passwordToUse,
281
+ adminObj,
282
+ mergedDefaults
283
+ );
284
+
285
+ await applyAdminSecretsUpdate(adminSecretsPath, adminObj, passwordForFile, shouldOverwriteWithAdminPwd, {
286
+ updateEmail,
287
+ emailToUse
288
+ });
135
289
  return adminSecretsPath;
136
290
  }
137
291
 
@@ -199,7 +353,7 @@ async function ensureMisoInitScript(infraDir) {
199
353
  const secretKey = 'databases-miso-controller-0-passwordKeyVault';
200
354
  let password;
201
355
  try {
202
- const loaded = await secrets.loadSecrets(undefined);
356
+ const loaded = await getCoreSecrets().loadSecrets(undefined);
203
357
  const urlOrPassword = loaded[secretKey] || loaded['databases-miso-controller-0-urlKeyVault'];
204
358
  const extracted = extractPasswordFromUrlOrValue(urlOrPassword);
205
359
  if (extracted !== null && extracted.trim() !== '') {
@@ -243,13 +397,14 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "miso" -c "GRANT AL
243
397
  }
244
398
 
245
399
  /**
246
- * Prepare infrastructure directory and extract postgres password
400
+ * Prepare infrastructure directory and extract postgres password from decrypted admin secrets.
401
+ * @async
247
402
  * @param {string} devId - Developer ID
248
403
  * @param {string} adminSecretsPath - Path to admin secrets file
249
- * @returns {Object} Object with infraDir and postgresPassword
404
+ * @returns {Promise<Object>} Object with infraDir and postgresPassword
250
405
  */
251
- function prepareInfraDirectory(devId, adminSecretsPath) {
252
- const aifabrixDir = paths.getAifabrixHome();
406
+ async function prepareInfraDirectory(devId, adminSecretsPath) {
407
+ const aifabrixDir = paths.getAifabrixSystemDir();
253
408
  const infraDirName = getInfraDirName(devId);
254
409
  const infraDir = path.join(aifabrixDir, infraDirName);
255
410
  if (!fs.existsSync(infraDir)) {
@@ -265,15 +420,46 @@ function prepareInfraDirectory(devId, adminSecretsPath) {
265
420
  }
266
421
  }
267
422
 
268
- const adminSecretsContent = fs.readFileSync(adminSecretsPath, 'utf8');
269
- const postgresPasswordMatch = adminSecretsContent.match(/^POSTGRES_PASSWORD=(.+)$/m);
270
- const raw = postgresPasswordMatch ? postgresPasswordMatch[1] : '';
271
- const postgresPassword = (raw && raw.trim()) || DEFAULT_ADMIN_PASSWORD;
423
+ const adminObj = await adminSecrets.readAndDecryptAdminSecrets(adminSecretsPath);
424
+ const postgresPassword =
425
+ (adminObj.POSTGRES_PASSWORD && adminObj.POSTGRES_PASSWORD.trim()) ||
426
+ readInfraDefaultScalars().adminPassword ||
427
+ '';
272
428
  generatePgAdminConfig(infraDir, postgresPassword);
273
429
 
274
430
  return { infraDir, postgresPassword };
275
431
  }
276
432
 
433
+ /**
434
+ * Resolve infra working directory and admin-secrets path for stop/restart.
435
+ * Infra dir: prefer system dir compose; if missing, use legacy home when compose exists there.
436
+ * Admin secrets: prefer `admin-secrets.env` under system dir; if missing, use legacy home (covers mixed layouts).
437
+ *
438
+ * @param {string|number} devId - Developer ID
439
+ * @returns {{ infraDir: string, adminSecretsPath: string }}
440
+ */
441
+ function resolveInfraStatePaths(devId) {
442
+ const syncFs = nodeFs();
443
+ const name = getInfraDirName(devId);
444
+ const systemBase = paths.getAifabrixSystemDir();
445
+ const legacyBase = paths.getAifabrixHome();
446
+ const sysInfra = path.join(systemBase, name);
447
+ const legInfra = path.join(legacyBase, name);
448
+ const sysCompose = path.join(sysInfra, 'compose.yaml');
449
+ const legCompose = path.join(legInfra, 'compose.yaml');
450
+ let infraDir = sysInfra;
451
+ if (!syncFs.existsSync(sysCompose) && syncFs.existsSync(legCompose) && legacyBase !== systemBase) {
452
+ infraDir = legInfra;
453
+ }
454
+ const sysAdmin = path.join(systemBase, 'admin-secrets.env');
455
+ const legAdmin = path.join(legacyBase, 'admin-secrets.env');
456
+ let adminSecretsPath = sysAdmin;
457
+ if (!syncFs.existsSync(sysAdmin) && syncFs.existsSync(legAdmin) && legacyBase !== systemBase) {
458
+ adminSecretsPath = legAdmin;
459
+ }
460
+ return { infraDir, adminSecretsPath };
461
+ }
462
+
277
463
  /**
278
464
  * Register Handlebars helper for equality comparison
279
465
  */
@@ -298,6 +484,7 @@ module.exports = {
298
484
  ensureAdminSecrets,
299
485
  generatePgAdminConfig,
300
486
  prepareInfraDirectory,
487
+ resolveInfraStatePaths,
301
488
  ensureMisoInitScript,
302
489
  registerHandlebarsHelper
303
490
  };
@@ -15,14 +15,13 @@ const config = require('../core/config');
15
15
  const devConfig = require('../utils/dev-config');
16
16
  const logger = require('../utils/logger');
17
17
  const dockerUtils = require('../utils/docker');
18
- const paths = require('../utils/paths');
19
18
  const statusHelpers = require('../utils/infra-status');
20
19
  const {
21
- getInfraDirName,
22
20
  getInfraProjectName,
23
21
  checkDockerAvailability,
24
22
  ensureAdminSecrets,
25
23
  prepareInfraDirectory,
24
+ resolveInfraStatePaths,
26
25
  ensureMisoInitScript,
27
26
  registerHandlebarsHelper
28
27
  } = require('./helpers');
@@ -37,8 +36,34 @@ const {
37
36
  startDockerServicesAndConfigure,
38
37
  checkInfraHealth
39
38
  } = require('./services');
39
+ const adminSecrets = require('../core/admin-secrets');
40
40
  // Lazy require to avoid circular dependency: infra -> app/down -> run-helpers -> infra
41
41
 
42
+ /**
43
+ * Runs a callback with a temporary .env.run file in infraDir (created from admin-secrets).
44
+ * Removes the file in a finally block.
45
+ * @async
46
+ * @param {string} infraDir - Infrastructure directory path
47
+ * @param {string} adminSecretsPath - Path to admin-secrets.env
48
+ * @param {function(string): Promise<void>} fn - Callback receiving runEnvPath
49
+ * @returns {Promise<void>}
50
+ */
51
+ async function withRunEnv(infraDir, adminSecretsPath, fn) {
52
+ const runEnvPath = path.join(infraDir, '.env.run');
53
+ try {
54
+ const adminObj = await adminSecrets.readAndDecryptAdminSecrets(adminSecretsPath);
55
+ const content = adminSecrets.envObjectToContent(adminObj);
56
+ fs.writeFileSync(runEnvPath, content, { mode: 0o600 });
57
+ await fn(runEnvPath);
58
+ } finally {
59
+ try {
60
+ if (fs.existsSync(runEnvPath)) fs.unlinkSync(runEnvPath);
61
+ } catch {
62
+ // Ignore unlink errors
63
+ }
64
+ }
65
+ }
66
+
42
67
  /**
43
68
  * Prepares infrastructure environment
44
69
  * Ensures infra secrets exist, then admin-secrets.env, then miso init script.
@@ -46,18 +71,35 @@ const {
46
71
  * @async
47
72
  * @function prepareInfrastructureEnvironment
48
73
  * @param {string|number|null} developerId - Developer ID
49
- * @param {Object} [options] - Options (traefik, adminPwd)
74
+ * @param {Object} [options] - Options (traefik, adminPwd, tlsEnabled)
50
75
  * @returns {Promise<Object>} Prepared environment configuration
51
76
  */
52
77
  async function prepareInfrastructureEnvironment(developerId, options = {}) {
53
78
  await checkDockerAvailability();
54
- await secretsEnsure.ensureInfraSecrets({ adminPwd: options.adminPwd });
55
- const adminSecretsPath = await ensureAdminSecrets({ adminPwd: options.adminPwd });
79
+ const adminPass = options.adminPassword || options.adminPwd;
80
+ const tlsEnabled = options.tlsEnabled === true;
81
+ const infraOpts = {
82
+ adminPassword: adminPass,
83
+ adminPwd: adminPass,
84
+ adminEmail: options.adminEmail,
85
+ userPassword: options.userPassword,
86
+ tlsEnabled
87
+ };
88
+ await secretsEnsure.ensureInfraSecrets(infraOpts);
89
+ const adminSecretsPath = await ensureAdminSecrets(infraOpts);
56
90
 
57
91
  const devId = developerId || await config.getDeveloperId();
92
+ const remoteServer = await config.getRemoteServer();
93
+ const {
94
+ assertRemoteBuilderDeveloperId,
95
+ remoteServerHostIsNonLocalhost
96
+ } = require('../utils/remote-builder-validation');
97
+ assertRemoteBuilderDeveloperId(remoteServer, devId);
98
+
58
99
  const devIdNum = typeof devId === 'string' ? parseInt(devId, 10) : devId;
59
100
  const ports = devConfig.getDevPorts(devIdNum);
60
101
  const idNum = devIdNum;
102
+ const trustForwardedHeaders = remoteServerHostIsNonLocalhost(remoteServer);
61
103
 
62
104
  const templatePath = path.join(__dirname, '..', '..', 'templates', 'infra', 'compose.yaml.hbs');
63
105
  if (!fs.existsSync(templatePath)) {
@@ -65,10 +107,10 @@ async function prepareInfrastructureEnvironment(developerId, options = {}) {
65
107
  }
66
108
 
67
109
  // Prepare infrastructure directory
68
- const { infraDir } = prepareInfraDirectory(devId, adminSecretsPath);
110
+ const { infraDir } = await prepareInfraDirectory(devId, adminSecretsPath);
69
111
  await ensureMisoInitScript(infraDir);
70
112
 
71
- return { devId, idNum, ports, templatePath, infraDir, adminSecretsPath };
113
+ return { devId, idNum, ports, templatePath, infraDir, adminSecretsPath, trustForwardedHeaders };
72
114
  }
73
115
 
74
116
  /**
@@ -88,9 +130,10 @@ async function prepareInfrastructureEnvironment(developerId, options = {}) {
88
130
  * // Infrastructure services are now running
89
131
  */
90
132
  async function startInfra(developerId = null, options = {}) {
91
- const { devId, idNum, ports, templatePath, infraDir } = await prepareInfrastructureEnvironment(developerId, options);
133
+ const { devId, idNum, ports, templatePath, infraDir, trustForwardedHeaders } =
134
+ await prepareInfrastructureEnvironment(developerId, options);
92
135
  const { traefik = false, pgadmin = true, redisCommander = true } = options;
93
- const traefikConfig = buildTraefikConfig(traefik);
136
+ const traefikConfig = { ...buildTraefikConfig(traefik), trustForwardedHeaders };
94
137
  const validation = validateTraefikConfig(traefikConfig);
95
138
  if (!validation.valid) {
96
139
  throw new Error(validation.errors.join('\n'));
@@ -173,28 +216,23 @@ async function removeAppVolumes(appNames, devId) {
173
216
  */
174
217
  async function stopInfra() {
175
218
  const devId = await config.getDeveloperId();
176
- const aifabrixDir = paths.getAifabrixHome();
177
- const infraDirName = getInfraDirName(devId);
178
- const infraDir = path.join(aifabrixDir, infraDirName);
219
+ const { infraDir, adminSecretsPath } = resolveInfraStatePaths(devId);
179
220
  const composePath = path.join(infraDir, 'compose.yaml');
180
- const adminSecretsPath = path.join(aifabrixDir, 'admin-secrets.env');
181
221
 
182
222
  if (!fs.existsSync(composePath) || !fs.existsSync(adminSecretsPath)) {
183
223
  logger.log('Infrastructure not running or not properly configured');
184
224
  return;
185
225
  }
186
226
 
187
- try {
227
+ await withRunEnv(infraDir, adminSecretsPath, async(runEnvPath) => {
188
228
  logger.log('Stopping application containers on the same network...');
189
229
  await stopAllAppContainers(devId);
190
230
  logger.log('Stopping infrastructure services...');
191
231
  const projectName = getInfraProjectName(devId);
192
232
  const composeCmd = await dockerUtils.getComposeCommand();
193
- await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${adminSecretsPath}" down`, { cwd: infraDir });
233
+ await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${runEnvPath}" down`, { cwd: infraDir });
194
234
  logger.log('Infrastructure services stopped');
195
- } finally {
196
- // Keep the compose file for future use
197
- }
235
+ });
198
236
  }
199
237
 
200
238
  /**
@@ -239,28 +277,23 @@ async function stopAllAppContainersAndVolumes(devId) {
239
277
  */
240
278
  async function stopInfraWithVolumes() {
241
279
  const devId = await config.getDeveloperId();
242
- const aifabrixDir = paths.getAifabrixHome();
243
- const infraDirName = getInfraDirName(devId);
244
- const infraDir = path.join(aifabrixDir, infraDirName);
280
+ const { infraDir, adminSecretsPath } = resolveInfraStatePaths(devId);
245
281
  const composePath = path.join(infraDir, 'compose.yaml');
246
- const adminSecretsPath = path.join(aifabrixDir, 'admin-secrets.env');
247
282
 
248
283
  if (!fs.existsSync(composePath) || !fs.existsSync(adminSecretsPath)) {
249
284
  logger.log('Infrastructure not running or not properly configured');
250
285
  return;
251
286
  }
252
287
 
253
- try {
288
+ await withRunEnv(infraDir, adminSecretsPath, async(runEnvPath) => {
254
289
  logger.log('Stopping application containers on the same network...');
255
290
  await stopAllAppContainersAndVolumes(devId);
256
291
  logger.log('Stopping infrastructure services and removing all data...');
257
292
  const projectName = getInfraProjectName(devId);
258
293
  const composeCmd = await dockerUtils.getComposeCommand();
259
- await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${adminSecretsPath}" down -v`, { cwd: infraDir });
294
+ await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${runEnvPath}" down -v`, { cwd: infraDir });
260
295
  logger.log('Infrastructure services stopped and all data removed');
261
- } finally {
262
- // Keep the compose file for future use
263
- }
296
+ });
264
297
  }
265
298
 
266
299
  /**
@@ -281,32 +314,26 @@ async function restartService(serviceName) {
281
314
  if (!serviceName || typeof serviceName !== 'string') {
282
315
  throw new Error('Service name is required and must be a string');
283
316
  }
284
-
285
317
  const validServices = ['postgres', 'redis', 'pgadmin', 'redis-commander', 'traefik'];
286
318
  if (!validServices.includes(serviceName)) {
287
319
  throw new Error(`Invalid service name. Must be one of: ${validServices.join(', ')}`);
288
320
  }
289
321
 
290
322
  const devId = await config.getDeveloperId();
291
- const aifabrixDir = paths.getAifabrixHome();
292
- const infraDirName = getInfraDirName(devId);
293
- const infraDir = path.join(aifabrixDir, infraDirName);
323
+ const { infraDir, adminSecretsPath } = resolveInfraStatePaths(devId);
294
324
  const composePath = path.join(infraDir, 'compose.yaml');
295
- const adminSecretsPath = path.join(aifabrixDir, 'admin-secrets.env');
296
325
 
297
326
  if (!fs.existsSync(composePath) || !fs.existsSync(adminSecretsPath)) {
298
327
  throw new Error('Infrastructure not properly configured');
299
328
  }
300
329
 
301
- try {
330
+ await withRunEnv(infraDir, adminSecretsPath, async(runEnvPath) => {
302
331
  logger.log(`Restarting ${serviceName} service...`);
303
332
  const projectName = getInfraProjectName(devId);
304
333
  const composeCmd = await dockerUtils.getComposeCommand();
305
- await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${adminSecretsPath}" restart ${serviceName}`, { cwd: infraDir });
334
+ await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${runEnvPath}" restart ${serviceName}`, { cwd: infraDir });
306
335
  logger.log(`${serviceName} service restarted successfully`);
307
- } finally {
308
- // Keep the compose file for future use
309
- }
336
+ });
310
337
  }
311
338
 
312
339
  // Re-export status helper functions