@aifabrix/builder 2.43.0 → 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 (346) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/README.md +1 -1
  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 +31 -0
  7. package/integration/hubspot-test/create-hubspot.js +5 -5
  8. package/integration/hubspot-test/hubspot-test-datasource-company.json +58 -462
  9. package/integration/hubspot-test/hubspot-test-datasource-contact.json +61 -555
  10. package/integration/hubspot-test/hubspot-test-datasource-deal.json +63 -506
  11. package/integration/hubspot-test/hubspot-test-datasource-users.json +42 -83
  12. package/integration/hubspot-test/hubspot-test-deploy.json +3 -3
  13. package/integration/hubspot-test/test-dataplane-down-tests.js +1 -7
  14. package/integration/hubspot-test/test-dataplane-down.js +3 -3
  15. package/integration/hubspot-test/test.js +35 -43
  16. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  17. package/integration/roundtrip-test-local/README.md +144 -0
  18. package/integration/roundtrip-test-local/application.yaml +13 -0
  19. package/integration/roundtrip-test-local/env.template +15 -0
  20. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  21. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  22. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  23. package/integration/roundtrip-test-local2/README.md +144 -0
  24. package/integration/roundtrip-test-local2/application.yaml +13 -0
  25. package/integration/roundtrip-test-local2/env.template +15 -0
  26. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  27. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  28. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  29. package/integration/test/wizard.yaml +8 -0
  30. package/jest.config.default.js +10 -0
  31. package/jest.config.integration.fixtures.js +22 -0
  32. package/jest.config.integration.js +21 -18
  33. package/jest.config.isolated.js +10 -0
  34. package/jest.projects.js +288 -0
  35. package/lib/api/datasources-core.api.js +3 -3
  36. package/lib/api/dev-mtls-request.js +110 -0
  37. package/lib/api/dev-server-https.js +145 -0
  38. package/lib/api/dev.api.js +133 -144
  39. package/lib/api/index.js +0 -1
  40. package/lib/api/pipeline.api.js +67 -20
  41. package/lib/api/types/dev.types.js +4 -3
  42. package/lib/api/types/pipeline.types.js +8 -5
  43. package/lib/api/types/validation-run.types.js +56 -0
  44. package/lib/api/validation-run.api.js +99 -0
  45. package/lib/api/validation-runner.js +99 -0
  46. package/lib/app/config.js +1 -1
  47. package/lib/app/deploy-status-display.js +2 -2
  48. package/lib/app/deploy.js +7 -6
  49. package/lib/app/display.js +2 -1
  50. package/lib/app/dockerfile.js +3 -2
  51. package/lib/app/down.js +2 -1
  52. package/lib/app/helpers.js +6 -5
  53. package/lib/app/index.js +27 -8
  54. package/lib/app/list.js +7 -6
  55. package/lib/app/push.js +4 -3
  56. package/lib/app/register.js +16 -7
  57. package/lib/app/rotate-secret.js +14 -13
  58. package/lib/app/run-container-start.js +184 -0
  59. package/lib/app/run-docker-fallback.js +108 -0
  60. package/lib/app/run-env-compose.js +30 -42
  61. package/lib/app/run-helpers.js +49 -126
  62. package/lib/app/run-infra-requirements.js +30 -0
  63. package/lib/app/run-resolve-image.js +21 -0
  64. package/lib/app/run.js +74 -21
  65. package/lib/app/show-display.js +1 -1
  66. package/lib/app/show.js +1 -1
  67. package/lib/build/index.js +13 -10
  68. package/lib/cli/index.js +2 -0
  69. package/lib/cli/setup-app.help.js +67 -0
  70. package/lib/cli/setup-app.js +57 -121
  71. package/lib/cli/setup-app.test-commands.js +179 -0
  72. package/lib/cli/setup-auth.js +19 -5
  73. package/lib/cli/setup-credential-deployment.js +22 -8
  74. package/lib/cli/setup-dev-path-commands.js +124 -0
  75. package/lib/cli/setup-dev.js +170 -113
  76. package/lib/cli/setup-environment.js +7 -1
  77. package/lib/cli/setup-external-system.js +62 -22
  78. package/lib/cli/setup-infra.js +126 -47
  79. package/lib/cli/setup-parameters.js +32 -0
  80. package/lib/cli/setup-secrets.js +106 -8
  81. package/lib/cli/setup-service-user.js +1 -1
  82. package/lib/cli/setup-utility.js +36 -20
  83. package/lib/commands/app-down.js +5 -7
  84. package/lib/commands/app-install.js +14 -7
  85. package/lib/commands/app-logs.js +13 -10
  86. package/lib/commands/app-shell.js +4 -1
  87. package/lib/commands/app-test.js +25 -19
  88. package/lib/commands/app.js +22 -10
  89. package/lib/commands/auth-config.js +6 -6
  90. package/lib/commands/auth-status.js +4 -3
  91. package/lib/commands/credential-env.js +4 -3
  92. package/lib/commands/credential-list.js +5 -4
  93. package/lib/commands/credential-push.js +4 -3
  94. package/lib/commands/datasource-unified-test-cli.js +495 -0
  95. package/lib/commands/datasource-unified-test-cli.options.js +149 -0
  96. package/lib/commands/datasource-validation-cli.js +129 -0
  97. package/lib/commands/datasource.js +105 -98
  98. package/lib/commands/deployment-list.js +6 -5
  99. package/lib/commands/dev-cli-handlers.js +122 -18
  100. package/lib/commands/dev-down.js +4 -3
  101. package/lib/commands/dev-init.js +231 -116
  102. package/lib/commands/dev-show-display.js +473 -0
  103. package/lib/commands/login-credentials.js +3 -2
  104. package/lib/commands/login-device.js +4 -3
  105. package/lib/commands/login.js +5 -4
  106. package/lib/commands/logout.js +8 -7
  107. package/lib/commands/parameters-validate.js +54 -0
  108. package/lib/commands/repair-datasource.js +314 -68
  109. package/lib/commands/repair-env-template.js +2 -2
  110. package/lib/commands/repair.js +21 -3
  111. package/lib/commands/secrets-list.js +23 -12
  112. package/lib/commands/secrets-remove-all.js +220 -0
  113. package/lib/commands/secrets-remove.js +21 -12
  114. package/lib/commands/secrets-set.js +21 -12
  115. package/lib/commands/secrets-validate.js +4 -4
  116. package/lib/commands/secure.js +10 -9
  117. package/lib/commands/service-user.js +26 -25
  118. package/lib/commands/test-e2e-external.js +27 -1
  119. package/lib/commands/up-common.js +3 -2
  120. package/lib/commands/up-dataplane.js +29 -16
  121. package/lib/commands/up-miso.js +19 -29
  122. package/lib/commands/upload.js +138 -39
  123. package/lib/commands/wizard-core-helpers.js +1 -1
  124. package/lib/commands/wizard-dataplane.js +4 -3
  125. package/lib/commands/wizard-helpers.js +3 -3
  126. package/lib/commands/wizard.js +2 -2
  127. package/lib/core/admin-secrets.js +14 -5
  128. package/lib/core/audit-logger.js +12 -4
  129. package/lib/core/config-attach-extensions.js +46 -0
  130. package/lib/core/config-runtime-paths.js +29 -0
  131. package/lib/core/config.js +55 -56
  132. package/lib/core/diff.js +3 -2
  133. package/lib/core/ensure-encryption-key.js +1 -1
  134. package/lib/core/secrets-ensure-infra.js +77 -0
  135. package/lib/core/secrets-ensure.js +120 -64
  136. package/lib/core/secrets-env-write.js +35 -7
  137. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  138. package/lib/core/secrets.js +200 -37
  139. package/lib/core/templates-env.js +4 -3
  140. package/lib/datasource/abac-validator.js +1 -10
  141. package/lib/datasource/deploy.js +75 -53
  142. package/lib/datasource/field-reference-validator.js +9 -6
  143. package/lib/datasource/integration-context.js +63 -0
  144. package/lib/datasource/list.js +8 -7
  145. package/lib/datasource/log-viewer.js +84 -53
  146. package/lib/datasource/resolve-app.js +4 -4
  147. package/lib/datasource/test-e2e.js +95 -146
  148. package/lib/datasource/test-integration.js +114 -122
  149. package/lib/datasource/unified-validation-run-body.js +65 -0
  150. package/lib/datasource/unified-validation-run-post.js +23 -0
  151. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  152. package/lib/datasource/unified-validation-run.js +92 -0
  153. package/lib/datasource/validate.js +157 -13
  154. package/lib/deployment/deployer.js +4 -3
  155. package/lib/deployment/environment.js +7 -6
  156. package/lib/deployment/push.js +17 -8
  157. package/lib/external-system/delete.js +4 -3
  158. package/lib/external-system/deploy.js +131 -53
  159. package/lib/external-system/download-helpers.js +1 -1
  160. package/lib/external-system/download.js +7 -6
  161. package/lib/external-system/generator.js +92 -6
  162. package/lib/external-system/integration-test-dispatch.js +26 -0
  163. package/lib/external-system/test-execution.js +5 -1
  164. package/lib/external-system/test-helpers.js +0 -4
  165. package/lib/external-system/test-system-level-helpers.js +110 -0
  166. package/lib/external-system/test-system-level.js +83 -44
  167. package/lib/external-system/test.js +59 -8
  168. package/lib/generator/builders.js +23 -11
  169. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  170. package/lib/generator/external.js +16 -4
  171. package/lib/generator/helpers.js +58 -3
  172. package/lib/generator/index.js +4 -0
  173. package/lib/generator/split-readme.js +12 -7
  174. package/lib/generator/split-variables.js +2 -1
  175. package/lib/generator/split.js +1 -1
  176. package/lib/generator/wizard-readme.js +3 -3
  177. package/lib/generator/wizard.js +8 -8
  178. package/lib/infrastructure/compose.js +60 -6
  179. package/lib/infrastructure/helpers.js +201 -29
  180. package/lib/infrastructure/index.js +28 -17
  181. package/lib/infrastructure/services.js +21 -15
  182. package/lib/internal/fs-real-sync.js +104 -0
  183. package/lib/internal/node-fs.js +98 -0
  184. package/lib/parameters/database-secret-values.js +173 -0
  185. package/lib/parameters/infra-kv-discovery.js +121 -0
  186. package/lib/parameters/infra-parameter-catalog.js +458 -0
  187. package/lib/parameters/infra-parameter-validate.js +64 -0
  188. package/lib/schema/application-schema.json +37 -17
  189. package/lib/schema/datasource-test-run.schema.json +493 -0
  190. package/lib/schema/deployment-rules.yaml +102 -63
  191. package/lib/schema/external-datasource.schema.json +1200 -442
  192. package/lib/schema/external-system.schema.json +181 -5
  193. package/lib/schema/flag-map-validation-run.json +31 -0
  194. package/lib/schema/infra-parameter.schema.json +106 -0
  195. package/lib/schema/infra.parameter.yaml +421 -0
  196. package/lib/schema/type/credential-auth-templates.json +40 -0
  197. package/lib/schema/type/document-storage.json +213 -0
  198. package/lib/schema/type/message-service.json +123 -0
  199. package/lib/schema/type/vector-store.json +88 -0
  200. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  201. package/lib/utils/api-error-handler.js +2 -2
  202. package/lib/utils/api.js +49 -14
  203. package/lib/utils/app-register-api.js +3 -2
  204. package/lib/utils/app-register-auth.js +1 -1
  205. package/lib/utils/app-register-config.js +4 -4
  206. package/lib/utils/app-register-display.js +3 -2
  207. package/lib/utils/app-register-validator.js +3 -2
  208. package/lib/utils/app-run-containers.js +26 -22
  209. package/lib/utils/app-scoped-config.js +31 -0
  210. package/lib/utils/app-service-env-from-builder.js +164 -0
  211. package/lib/utils/build-copy.js +1 -1
  212. package/lib/utils/build-helpers.js +20 -20
  213. package/lib/utils/build-resolve-image.js +165 -0
  214. package/lib/utils/cli-layout-chalk.js +8 -0
  215. package/lib/utils/cli-test-layout-chalk.js +267 -0
  216. package/lib/utils/cli-utils.js +88 -11
  217. package/lib/utils/compose-db-passwords.js +138 -0
  218. package/lib/utils/compose-generate-docker-compose.js +216 -0
  219. package/lib/utils/compose-generator.js +197 -291
  220. package/lib/utils/compose-miso-env.js +18 -0
  221. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  222. package/lib/utils/config-paths.js +166 -7
  223. package/lib/utils/config-scoped-resources-preference.js +41 -0
  224. package/lib/utils/controller-deployment-outcome.js +68 -0
  225. package/lib/utils/credential-display.js +2 -2
  226. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  227. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  228. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  229. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  230. package/lib/utils/datasource-test-run-display.js +442 -0
  231. package/lib/utils/datasource-test-run-exit.js +58 -0
  232. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  233. package/lib/utils/datasource-test-run-report-version.js +51 -0
  234. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  235. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  236. package/lib/utils/datasource-validation-watch.js +266 -0
  237. package/lib/utils/declarative-url-ports.js +47 -0
  238. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  239. package/lib/utils/dev-ca-install.js +185 -23
  240. package/lib/utils/dev-cert-helper.js +266 -17
  241. package/lib/utils/dev-hosts-helper.js +307 -0
  242. package/lib/utils/dev-init-cert-hints.js +37 -0
  243. package/lib/utils/dev-init-health-messages.js +52 -0
  244. package/lib/utils/dev-init-resolve.js +86 -0
  245. package/lib/utils/dev-init-ssh-merge.js +65 -0
  246. package/lib/utils/dev-ssh-config-helper.js +196 -0
  247. package/lib/utils/dev-user-groups.js +93 -0
  248. package/lib/utils/docker-build.js +42 -17
  249. package/lib/utils/docker-exec.js +28 -0
  250. package/lib/utils/docker-manifest-public-port.js +116 -0
  251. package/lib/utils/docker-not-running-hint.js +52 -0
  252. package/lib/utils/docker.js +98 -11
  253. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  254. package/lib/utils/env-config-loader.js +10 -91
  255. package/lib/utils/env-copy.js +19 -10
  256. package/lib/utils/env-map.js +35 -8
  257. package/lib/utils/env-template.js +2 -2
  258. package/lib/utils/environment-scoped-resources.js +144 -0
  259. package/lib/utils/error-formatter.js +92 -13
  260. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  261. package/lib/utils/error-formatters/network-errors.js +2 -1
  262. package/lib/utils/error-formatters/permission-errors.js +2 -1
  263. package/lib/utils/error-formatters/validation-errors.js +2 -1
  264. package/lib/utils/external-readme.js +8 -1
  265. package/lib/utils/external-system-display.js +234 -136
  266. package/lib/utils/external-system-local-test-tty.js +389 -0
  267. package/lib/utils/external-system-readiness-core.js +377 -0
  268. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  269. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  270. package/lib/utils/external-system-readiness-display.js +186 -0
  271. package/lib/utils/external-system-test-helpers.js +24 -6
  272. package/lib/utils/external-system-validators.js +30 -12
  273. package/lib/utils/health-check-url.js +119 -0
  274. package/lib/utils/health-check.js +59 -25
  275. package/lib/utils/help-builder.js +11 -8
  276. package/lib/utils/image-version.js +4 -8
  277. package/lib/utils/infra-containers.js +4 -7
  278. package/lib/utils/infra-env-defaults.js +162 -0
  279. package/lib/utils/infra-status-display.js +167 -0
  280. package/lib/utils/infra-status.js +16 -8
  281. package/lib/utils/local-secrets.js +3 -4
  282. package/lib/utils/paths.js +134 -47
  283. package/lib/utils/port-resolver.js +10 -23
  284. package/lib/utils/redis-env-scope.js +62 -0
  285. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  286. package/lib/utils/remote-builder-validation.js +99 -0
  287. package/lib/utils/remote-dev-auth.js +117 -21
  288. package/lib/utils/remote-docker-env.js +67 -15
  289. package/lib/utils/remote-secrets-loader.js +13 -4
  290. package/lib/utils/resolve-docker-image-ref.js +124 -0
  291. package/lib/utils/schema-loader.js +22 -9
  292. package/lib/utils/secrets-bash-kv.js +25 -0
  293. package/lib/utils/secrets-generator.js +169 -49
  294. package/lib/utils/secrets-helpers.js +70 -59
  295. package/lib/utils/secrets-kv-scope.js +60 -0
  296. package/lib/utils/secrets-utils.js +32 -38
  297. package/lib/utils/secrets-validation.js +3 -1
  298. package/lib/utils/secrets-yaml-preserve.js +109 -0
  299. package/lib/utils/ssh-key-helper.js +4 -2
  300. package/lib/utils/template-helpers.js +2 -2
  301. package/lib/utils/test-log-writer.js +3 -3
  302. package/lib/utils/token-manager.js +1 -2
  303. package/lib/utils/url-declarative-public-base.js +188 -0
  304. package/lib/utils/url-declarative-resolve-build.js +493 -0
  305. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  306. package/lib/utils/url-declarative-resolve.js +220 -0
  307. package/lib/utils/url-declarative-token-parse.js +74 -0
  308. package/lib/utils/url-declarative-url-flags.js +50 -0
  309. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  310. package/lib/utils/url-public-path-prefix.js +34 -0
  311. package/lib/utils/urls-local-registry.js +220 -0
  312. package/lib/utils/validation-report-tty-kit.js +77 -0
  313. package/lib/utils/validation-run-poll.js +89 -0
  314. package/lib/utils/validation-run-post-retry.js +73 -0
  315. package/lib/utils/validation-run-request.js +98 -0
  316. package/lib/utils/variable-transformer.js +21 -4
  317. package/lib/utils/yaml-preserve.js +33 -14
  318. package/lib/validation/datasource-warnings.js +56 -0
  319. package/lib/validation/env-template-auth.js +1 -1
  320. package/lib/validation/external-manifest-validator.js +27 -7
  321. package/lib/validation/validate-display.js +37 -31
  322. package/lib/validation/validate.js +4 -13
  323. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  324. package/lib/validation/validator.js +22 -65
  325. package/lib/validation/wizard-config-validator.js +2 -1
  326. package/package.json +7 -3
  327. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  328. package/scripts/diagnose-cli.js +150 -0
  329. package/scripts/install-local.js +304 -55
  330. package/templates/README.md +15 -2
  331. package/templates/applications/dataplane/application.yaml +52 -2
  332. package/templates/applications/dataplane/env.template +75 -17
  333. package/templates/applications/dataplane/rbac.yaml +8 -0
  334. package/templates/applications/keycloak/application.yaml +9 -1
  335. package/templates/applications/keycloak/env.template +15 -6
  336. package/templates/applications/miso-controller/application.yaml +10 -2
  337. package/templates/applications/miso-controller/env.template +42 -12
  338. package/templates/applications/miso-controller/rbac.yaml +5 -0
  339. package/templates/external-system/README.md.hbs +20 -7
  340. package/templates/external-system/deploy.js.hbs +5 -5
  341. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  342. package/templates/infra/compose.yaml.hbs +20 -4
  343. package/templates/python/docker-compose.hbs +16 -0
  344. package/templates/typescript/docker-compose.hbs +16 -0
  345. package/lib/api/external-test.api.js +0 -111
  346. package/lib/schema/env-config.yaml +0 -60
@@ -13,10 +13,15 @@ const { parseImageReference } = require('./parse-image');
13
13
  * @param {Object} deployment - Deployment JSON object
14
14
  * @returns {{ appName: string, config: Object }}
15
15
  */
16
- function buildReadmeConfigForExternal(deployment) {
16
+ function buildReadmeConfigForExternal(deployment, options = {}) {
17
17
  const system = deployment.system;
18
18
  const appName = system.key || deployment.key || 'external-system';
19
19
  const dataSources = deployment.dataSources || deployment.datasources || [];
20
+ const rawExt = options.fileExt;
21
+ const fileExt =
22
+ rawExt !== undefined && rawExt !== null && String(rawExt).trim() !== ''
23
+ ? (String(rawExt).startsWith('.') ? String(rawExt) : `.${String(rawExt)}`)
24
+ : '.json';
20
25
  return {
21
26
  appName,
22
27
  config: {
@@ -25,7 +30,7 @@ function buildReadmeConfigForExternal(deployment) {
25
30
  systemType: system.type || 'openapi',
26
31
  systemDisplayName: system.displayName || appName,
27
32
  systemDescription: system.description || `External system integration for ${appName}`,
28
- fileExt: '.yaml',
33
+ fileExt,
29
34
  datasourceCount: dataSources.length,
30
35
  datasources: dataSources
31
36
  }
@@ -49,7 +54,7 @@ function buildReadmeConfigForApp(deployment) {
49
54
  displayName: deployment.displayName,
50
55
  description: deployment.description,
51
56
  port,
52
- build: { localPort: port },
57
+ build: {},
53
58
  image: { name: imageName, registry },
54
59
  registry,
55
60
  database: deployment.requiresDatabase,
@@ -80,9 +85,9 @@ function buildReadmeConfigForApp(deployment) {
80
85
  * @param {Object} deployment - Deployment JSON object
81
86
  * @returns {{ appName: string, config: Object }}
82
87
  */
83
- function buildReadmeConfigFromDeployment(deployment) {
88
+ function buildReadmeConfigFromDeployment(deployment, options = {}) {
84
89
  if (deployment.system && typeof deployment.system === 'object') {
85
- return buildReadmeConfigForExternal(deployment);
90
+ return buildReadmeConfigForExternal(deployment, options);
86
91
  }
87
92
  return buildReadmeConfigForApp(deployment);
88
93
  }
@@ -92,11 +97,11 @@ function buildReadmeConfigFromDeployment(deployment) {
92
97
  * @param {Object} deployment - Deployment JSON object
93
98
  * @returns {string} README.md content
94
99
  */
95
- function generateReadmeFromDeployJson(deployment) {
100
+ function generateReadmeFromDeployJson(deployment, options = {}) {
96
101
  if (!deployment || typeof deployment !== 'object') {
97
102
  throw new Error('Deployment object is required');
98
103
  }
99
- const { appName, config } = buildReadmeConfigFromDeployment(deployment);
104
+ const { appName, config } = buildReadmeConfigFromDeployment(deployment, options);
100
105
  return generateReadmeMd(appName, config);
101
106
  }
102
107
 
@@ -56,7 +56,8 @@ function extractOptionalSections(deployment) {
56
56
  const optional = {};
57
57
  const names = [
58
58
  'healthCheck', 'authentication', 'build', 'repository', 'deployment',
59
- 'startupCommand', 'runtimeVersion', 'scaling', 'frontDoorRouting'
59
+ 'startupCommand', 'runtimeVersion', 'scaling', 'frontDoorRouting',
60
+ 'environmentScopedResources'
60
61
  ];
61
62
  for (const sectionName of names) {
62
63
  extractOptionalSection(deployment, sectionName, optional);
@@ -440,7 +440,7 @@ async function splitDeployJson(deployJsonPath, outputDir = null, splitOptions =
440
440
 
441
441
  const variables = extractVariablesYaml(deployment);
442
442
  const rbac = extractRbacYaml(deployment);
443
- const readme = generateReadmeFromDeployJson(deployment);
443
+ const readme = generateReadmeFromDeployJson(deployment, { fileExt: '.yaml' });
444
444
 
445
445
  const result = await writeComponentFiles(finalOutputDir, envTemplate, variables, rbac, readme, writeOptions);
446
446
  await applyExternalSystemFilesToResult(finalOutputDir, deployment, result);
@@ -6,10 +6,10 @@
6
6
  */
7
7
 
8
8
  'use strict';
9
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
9
10
 
10
11
  const fs = require('fs').promises;
11
12
  const path = require('path');
12
- const chalk = require('chalk');
13
13
  const logger = require('../utils/logger');
14
14
  const { generateExternalReadmeContent } = require('../utils/external-readme');
15
15
 
@@ -48,7 +48,7 @@ async function generateReadme(options) {
48
48
 
49
49
  if (aiGeneratedContent) {
50
50
  await fs.writeFile(readmePath, aiGeneratedContent, 'utf8');
51
- logger.log(chalk.green('Generated README.md (AI-generated from dataplane)'));
51
+ logger.log(formatSuccessLine('Generated README.md (AI-generated from dataplane)'));
52
52
  return;
53
53
  }
54
54
 
@@ -82,7 +82,7 @@ async function generateReadme(options) {
82
82
  });
83
83
 
84
84
  await fs.writeFile(readmePath, readmeContent, 'utf8');
85
- logger.log(chalk.green('Generated README.md (template)'));
85
+ logger.log(formatSuccessLine('Generated README.md (template)'));
86
86
  } catch (error) {
87
87
  throw new Error(`Failed to generate README.md: ${error.message}`);
88
88
  }
@@ -8,10 +8,10 @@
8
8
  * PASSWORD, BASEURL. See docs/external-systems.md and docs/wizard.md.
9
9
  */
10
10
 
11
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
11
12
  const fs = require('fs').promises;
12
13
  const path = require('path');
13
14
  const Handlebars = require('handlebars');
14
- const chalk = require('chalk');
15
15
  const logger = require('../utils/logger');
16
16
  const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
17
17
  const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
@@ -61,7 +61,7 @@ async function writeSystemYamlFile(appPath, finalSystemKey, systemConfig, format
61
61
  const systemFileName = `${finalSystemKey}-system${ext}`;
62
62
  const systemFilePath = path.join(appPath, systemFileName);
63
63
  writeConfigFile(systemFilePath, systemConfig, format === 'json' ? 'json' : 'yaml');
64
- logger.log(chalk.green(`✓ Generated system file: ${systemFileName}`));
64
+ logger.log(formatSuccessLine(`Generated system file: ${systemFileName}`));
65
65
  return systemFilePath;
66
66
  }
67
67
 
@@ -97,7 +97,7 @@ async function writeDatasourceYamlFiles(appPath, finalSystemKey, datasourceConfi
97
97
  const datasourceFilePath = path.join(appPath, datasourceFileName);
98
98
  writeConfigFile(datasourceFilePath, datasourceConfig, fmt);
99
99
  datasourceFileNames.push(datasourceFileName);
100
- logger.log(chalk.green(`✓ Generated datasource file: ${datasourceFileName}`));
100
+ logger.log(formatSuccessLine(`Generated datasource file: ${datasourceFileName}`));
101
101
  }
102
102
  return datasourceFileNames;
103
103
  }
@@ -135,7 +135,7 @@ async function generateConfigFilesForWizard(params) {
135
135
  const envTemplatePath = path.join(appPath, 'env.template');
136
136
  const envTemplateContent = generateExternalEnvTemplateContent(systemConfig);
137
137
  await fs.writeFile(envTemplatePath, envTemplateContent, 'utf8');
138
- logger.log(chalk.green('Generated env.template'));
138
+ logger.log(formatSuccessLine('Generated env.template'));
139
139
 
140
140
  try {
141
141
  const secretsEnsure = require('../core/secrets-ensure');
@@ -164,7 +164,7 @@ async function generateConfigFilesForWizard(params) {
164
164
  const deployJson = toDeployJsonShape(manifest);
165
165
  const deployManifestPath = path.join(appPath, `${finalSystemKey}-deploy.json`);
166
166
  await fs.writeFile(deployManifestPath, JSON.stringify(deployJson, null, 2), 'utf8');
167
- logger.log(chalk.green(`✓ Generated deployment manifest: ${finalSystemKey}-deploy.json`));
167
+ logger.log(formatSuccessLine(`Generated deployment manifest: ${finalSystemKey}-deploy.json`));
168
168
 
169
169
  return {
170
170
  variablesPath: configPath,
@@ -293,7 +293,7 @@ async function generateOrUpdateVariablesYaml(params) {
293
293
  const fsSync = require('fs');
294
294
  if (fsSync.existsSync(configPath)) fsSync.unlinkSync(configPath);
295
295
  }
296
- logger.log(chalk.green(`✓ Generated/updated application${ext}`));
296
+ logger.log(formatSuccessLine(`Generated/updated application${ext}`));
297
297
  return targetPath;
298
298
  } catch (error) {
299
299
  throw new Error(`Failed to generate application config: ${error.message}`);
@@ -417,7 +417,7 @@ async function _generateEnvTemplate(appPath, systemConfig, finalSystemKey) {
417
417
  addBaseUrlLines(lines, systemConfig);
418
418
 
419
419
  await fs.writeFile(envTemplatePath, lines.join('\n'), 'utf8');
420
- logger.log(chalk.green('Generated env.template'));
420
+ logger.log(formatSuccessLine('Generated env.template'));
421
421
  } catch (error) {
422
422
  throw new Error(`Failed to generate env.template: ${error.message}`);
423
423
  }
@@ -440,7 +440,7 @@ async function writeDeployScriptFromTemplate(templateName, outputPath, context)
440
440
  const templatePath = path.join(templatesExternalDir, templateName);
441
441
  const content = Handlebars.compile(await fs.readFile(templatePath, 'utf8'))(context);
442
442
  await fs.writeFile(outputPath, content, 'utf8');
443
- logger.log(chalk.green(`✓ Generated ${path.basename(outputPath)}`));
443
+ logger.log(formatSuccessLine(`Generated ${path.basename(outputPath)}`));
444
444
  }
445
445
 
446
446
  async function generateDeployScripts(appPath, systemKey, systemFileName, datasourceFileNames) {
@@ -12,6 +12,32 @@ const fs = require('fs');
12
12
  const handlebars = require('handlebars');
13
13
  const path = require('path');
14
14
 
15
+ /**
16
+ * Normalize a host path for Docker Compose bind mounts. Docker Desktop sends
17
+ * mounts to a Linux VM; backslashes in Windows paths yield "invalid volume
18
+ * specification" from the daemon.
19
+ *
20
+ * On Windows, Compose often forwards binds as "SOURCE:TARGET:rw". A SOURCE
21
+ * like "C:/Users/..." is split at the first colon, so the engine must receive
22
+ * paths in Linux-VM form (e.g. "/c/Users/...") instead.
23
+ *
24
+ * @param {string} fsPath - Host path (absolute or relative)
25
+ * @returns {string} Path with forward slashes, resolved when relative
26
+ */
27
+ function toDockerBindMountSource(fsPath) {
28
+ if (!fsPath || typeof fsPath !== 'string') {
29
+ return fsPath;
30
+ }
31
+ let resolved = path.resolve(fsPath).split(path.sep).join('/');
32
+ if (process.platform === 'win32') {
33
+ const drive = /^([a-zA-Z]):(\/.*)$/.exec(resolved);
34
+ if (drive) {
35
+ resolved = `/${drive[1].toLowerCase()}${drive[2]}`;
36
+ }
37
+ }
38
+ return resolved;
39
+ }
40
+
15
41
  /**
16
42
  * Builds Traefik configuration from environment variables
17
43
  * @param {boolean} enabled - Whether Traefik should be included
@@ -22,7 +48,8 @@ function buildTraefikConfig(enabled) {
22
48
  enabled: !!enabled,
23
49
  certStore: process.env.TRAEFIK_CERT_STORE || null,
24
50
  certFile: process.env.TRAEFIK_CERT_FILE || null,
25
- keyFile: process.env.TRAEFIK_KEY_FILE || null
51
+ keyFile: process.env.TRAEFIK_KEY_FILE || null,
52
+ trustForwardedHeaders: false
26
53
  };
27
54
  }
28
55
 
@@ -40,13 +67,23 @@ function validateTraefikConfig(traefikConfig) {
40
67
 
41
68
  if (traefikConfig.certStore) {
42
69
  if (!traefikConfig.certFile || !traefikConfig.keyFile) {
43
- errors.push('TRAEFIK_CERT_FILE and TRAEFIK_KEY_FILE are required when TRAEFIK_CERT_STORE is set');
70
+ errors.push(
71
+ 'TLS is enabled for Traefik but certificate files are missing or invalid. ' +
72
+ 'Set TRAEFIK_CERT_FILE and TRAEFIK_KEY_FILE (and TRAEFIK_CERT_STORE if used) to your local cert and key, ' +
73
+ 'or disable TLS in application/frontDoorRouting for local development.'
74
+ );
44
75
  } else {
45
76
  if (!fs.existsSync(traefikConfig.certFile)) {
46
- errors.push(`Certificate file not found: ${traefikConfig.certFile}`);
77
+ errors.push(
78
+ 'TLS is enabled for Traefik but certificate files are missing or invalid. ' +
79
+ `Certificate file not found: ${traefikConfig.certFile}`
80
+ );
47
81
  }
48
82
  if (!fs.existsSync(traefikConfig.keyFile)) {
49
- errors.push(`Private key file not found: ${traefikConfig.keyFile}`);
83
+ errors.push(
84
+ 'TLS is enabled for Traefik but certificate files are missing or invalid. ' +
85
+ `Private key file not found: ${traefikConfig.keyFile}`
86
+ );
50
87
  }
51
88
  }
52
89
  }
@@ -82,6 +119,20 @@ function generateComposeFile(templatePath, devId, idNum, ports, infraDir, option
82
119
  const redisCommanderConfig = options.redisCommander && typeof options.redisCommander.enabled === 'boolean'
83
120
  ? options.redisCommander
84
121
  : { enabled: true };
122
+ const traefikForCompose = traefikConfig && typeof traefikConfig === 'object'
123
+ ? {
124
+ ...traefikConfig,
125
+ trustForwardedHeaders: !!traefikConfig.trustForwardedHeaders,
126
+ ...(traefikConfig.certFile
127
+ ? { certFile: toDockerBindMountSource(traefikConfig.certFile) }
128
+ : {}),
129
+ ...(traefikConfig.keyFile
130
+ ? { keyFile: toDockerBindMountSource(traefikConfig.keyFile) }
131
+ : {})
132
+ }
133
+ : traefikConfig;
134
+ const initScriptsBind = toDockerBindMountSource(path.join(infraDir, 'init-scripts'));
135
+ const infraDirBind = toDockerBindMountSource(infraDir);
85
136
  const composeContent = template({
86
137
  devId: devId,
87
138
  postgresPort: ports.postgres,
@@ -94,7 +145,9 @@ function generateComposeFile(templatePath, devId, idNum, ports, infraDir, option
94
145
  serversJsonPath: serversJsonPath,
95
146
  pgpassPath: pgpassPath,
96
147
  infraDir: infraDir,
97
- traefik: traefikConfig,
148
+ initScriptsBind: initScriptsBind,
149
+ infraDirBind: infraDirBind,
150
+ traefik: traefikForCompose,
98
151
  pgadmin: pgadminConfig,
99
152
  redisCommander: redisCommanderConfig
100
153
  });
@@ -106,5 +159,6 @@ function generateComposeFile(templatePath, devId, idNum, ports, infraDir, option
106
159
  module.exports = {
107
160
  buildTraefikConfig,
108
161
  validateTraefikConfig,
109
- generateComposeFile
162
+ generateComposeFile,
163
+ toDockerBindMountSource
110
164
  };
@@ -11,14 +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');
17
18
  const adminSecrets = require('../core/admin-secrets');
18
19
  const logger = require('../utils/logger');
19
20
  const dockerUtils = require('../utils/docker');
20
21
  const paths = require('../utils/paths');
21
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
+ }
22
39
 
23
40
  /**
24
41
  * Gets infrastructure directory name based on developer ID
@@ -43,21 +60,64 @@ function getInfraProjectName(devId) {
43
60
  }
44
61
 
45
62
  /**
46
- * 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).
47
100
  * @async
48
101
  * @returns {Promise<void>}
49
- * @throws {Error} If Docker is not available
102
+ * @throws {Error} If Docker/Compose cannot be used (includes underlying cause)
50
103
  */
51
104
  async function checkDockerAvailability() {
105
+ await ensureDevCertsIfNeededForRemoteDocker();
52
106
  try {
53
107
  await dockerUtils.ensureDockerAndCompose();
54
108
  } catch (error) {
55
- 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));
56
111
  }
57
112
  }
58
113
 
59
- /** Default admin password for new local installations when admin-secrets.env is empty */
60
- 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
+ }
61
121
 
62
122
  /**
63
123
  * Log hint to reset Postgres volume when admin password was changed after first init.
@@ -66,7 +126,7 @@ const DEFAULT_ADMIN_PASSWORD = 'admin123';
66
126
  function logVolumeResetHint(infraDir) {
67
127
  logger.log(chalk.yellow(
68
128
  'If Postgres was already started with a different password, login will fail until you reset the volume. ' +
69
- `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.`
70
130
  ));
71
131
  }
72
132
 
@@ -82,9 +142,8 @@ async function syncPostgresPasswordToStore(password) {
82
142
  }
83
143
  }
84
144
 
85
- /** Default admin env keys and values when missing. */
145
+ /** Non-email defaults for admin-secrets.env merge (email default comes from infra.parameter.yaml `defaults`). */
86
146
  const DEFAULT_ADMIN_OBJ = {
87
- PGADMIN_DEFAULT_EMAIL: 'admin@aifabrix.dev',
88
147
  REDIS_HOST: 'local:redis:6379:0:',
89
148
  REDIS_COMMANDER_USER: 'admin'
90
149
  };
@@ -96,23 +155,87 @@ const DEFAULT_ADMIN_OBJ = {
96
155
  * @param {Object} adminObj - Decrypted admin secrets object
97
156
  * @param {string} passwordToUse - Password to set for Postgres, pgAdmin, Redis Commander
98
157
  * @param {boolean} shouldOverwriteWithAdminPwd - Whether this was an explicit admin password update
158
+ * @param {{ updateEmail?: boolean, emailToUse?: string }} [emailOpts]
99
159
  */
100
- async function applyAdminSecretsUpdate(adminSecretsPath, adminObj, passwordToUse, shouldOverwriteWithAdminPwd) {
160
+ async function applyAdminSecretsUpdate(
161
+ adminSecretsPath,
162
+ adminObj,
163
+ passwordToUse,
164
+ shouldOverwriteWithAdminPwd,
165
+ emailOpts
166
+ ) {
101
167
  const merged = { ...DEFAULT_ADMIN_OBJ, ...adminObj };
102
168
  merged.POSTGRES_PASSWORD = passwordToUse;
103
169
  merged.PGADMIN_DEFAULT_PASSWORD = passwordToUse;
104
170
  merged.REDIS_COMMANDER_PASSWORD = passwordToUse;
105
- const content = await secrets.formatAdminSecretsContent(merged);
106
- fs.writeFileSync(adminSecretsPath, content, { mode: 0o600 });
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 });
107
176
  if (shouldOverwriteWithAdminPwd) {
108
177
  logger.log('Updated admin password in admin-secrets.env.');
109
178
  await syncPostgresPasswordToStore(passwordToUse);
110
- logVolumeResetHint(path.join(paths.getAifabrixHome(), getInfraDirName(0)));
179
+ logVolumeResetHint(path.join(paths.getAifabrixSystemDir(), getInfraDirName(0)));
180
+ } else if (emailOpts && emailOpts.updateEmail) {
181
+ logger.log('Updated admin email in admin-secrets.env.');
111
182
  } else {
112
183
  logger.log('Set default admin password in admin-secrets.env for local use.');
113
184
  }
114
185
  }
115
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
+
116
239
  /**
117
240
  * Ensure admin secrets file exists and set admin password.
118
241
  * When adminPwd is provided, update POSTGRES_PASSWORD, PGADMIN_DEFAULT_PASSWORD, REDIS_COMMANDER_PASSWORD
@@ -121,33 +244,48 @@ async function applyAdminSecretsUpdate(adminSecretsPath, adminObj, passwordToUse
121
244
  *
122
245
  * @async
123
246
  * @param {Object} [options] - Options
124
- * @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)
125
251
  * @returns {Promise<string>} Path to admin secrets file
126
252
  */
127
253
  async function ensureAdminSecrets(options = {}) {
128
- const adminPwdOverride = options.adminPwd && typeof options.adminPwd === 'string' && options.adminPwd.trim() !== ''
129
- ? options.adminPwd.trim()
130
- : null;
131
- const passwordToUse = adminPwdOverride || DEFAULT_ADMIN_PASSWORD;
132
- const adminSecretsPath = path.join(paths.getAifabrixHome(), 'admin-secrets.env');
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');
133
260
 
134
- if (!fs.existsSync(adminSecretsPath)) {
261
+ if (!fsRealSync.existsSync(adminSecretsPath)) {
135
262
  logger.log('Generating admin-secrets.env...');
136
- await secrets.generateAdminSecretsEnv(undefined);
263
+ await getCoreSecrets().generateAdminSecretsEnv(undefined);
137
264
  return adminSecretsPath;
138
265
  }
139
266
 
140
267
  const adminObj = await adminSecrets.readAndDecryptAdminSecrets(adminSecretsPath);
141
- const needsBackfill = !(adminObj.POSTGRES_PASSWORD && adminObj.POSTGRES_PASSWORD.trim()) ||
142
- !(adminObj.PGADMIN_DEFAULT_PASSWORD && adminObj.PGADMIN_DEFAULT_PASSWORD.trim()) ||
143
- !(adminObj.REDIS_COMMANDER_PASSWORD && adminObj.REDIS_COMMANDER_PASSWORD.trim());
268
+ const { needsPasswordBackfill, needsEmailBackfill } = computeAdminSecretsBackfillFlags(adminObj);
144
269
  const shouldOverwriteWithAdminPwd = adminPwdOverride !== null;
270
+ const shouldOverwriteEmail = emailOverride !== null;
271
+ const updateEmail = shouldOverwriteEmail || needsEmailBackfill;
145
272
 
146
- if (!shouldOverwriteWithAdminPwd && !needsBackfill) {
273
+ if (!shouldOverwriteWithAdminPwd && !needsPasswordBackfill && !updateEmail) {
147
274
  return adminSecretsPath;
148
275
  }
149
276
 
150
- await applyAdminSecretsUpdate(adminSecretsPath, adminObj, passwordToUse, shouldOverwriteWithAdminPwd);
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
+ });
151
289
  return adminSecretsPath;
152
290
  }
153
291
 
@@ -215,7 +353,7 @@ async function ensureMisoInitScript(infraDir) {
215
353
  const secretKey = 'databases-miso-controller-0-passwordKeyVault';
216
354
  let password;
217
355
  try {
218
- const loaded = await secrets.loadSecrets(undefined);
356
+ const loaded = await getCoreSecrets().loadSecrets(undefined);
219
357
  const urlOrPassword = loaded[secretKey] || loaded['databases-miso-controller-0-urlKeyVault'];
220
358
  const extracted = extractPasswordFromUrlOrValue(urlOrPassword);
221
359
  if (extracted !== null && extracted.trim() !== '') {
@@ -266,7 +404,7 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "miso" -c "GRANT AL
266
404
  * @returns {Promise<Object>} Object with infraDir and postgresPassword
267
405
  */
268
406
  async function prepareInfraDirectory(devId, adminSecretsPath) {
269
- const aifabrixDir = paths.getAifabrixHome();
407
+ const aifabrixDir = paths.getAifabrixSystemDir();
270
408
  const infraDirName = getInfraDirName(devId);
271
409
  const infraDir = path.join(aifabrixDir, infraDirName);
272
410
  if (!fs.existsSync(infraDir)) {
@@ -283,12 +421,45 @@ async function prepareInfraDirectory(devId, adminSecretsPath) {
283
421
  }
284
422
 
285
423
  const adminObj = await adminSecrets.readAndDecryptAdminSecrets(adminSecretsPath);
286
- const postgresPassword = (adminObj.POSTGRES_PASSWORD && adminObj.POSTGRES_PASSWORD.trim()) || DEFAULT_ADMIN_PASSWORD;
424
+ const postgresPassword =
425
+ (adminObj.POSTGRES_PASSWORD && adminObj.POSTGRES_PASSWORD.trim()) ||
426
+ readInfraDefaultScalars().adminPassword ||
427
+ '';
287
428
  generatePgAdminConfig(infraDir, postgresPassword);
288
429
 
289
430
  return { infraDir, postgresPassword };
290
431
  }
291
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
+
292
463
  /**
293
464
  * Register Handlebars helper for equality comparison
294
465
  */
@@ -313,6 +484,7 @@ module.exports = {
313
484
  ensureAdminSecrets,
314
485
  generatePgAdminConfig,
315
486
  prepareInfraDirectory,
487
+ resolveInfraStatePaths,
316
488
  ensureMisoInitScript,
317
489
  registerHandlebarsHelper
318
490
  };