@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
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Detects availability of Docker and determines the correct Docker Compose command
5
5
  * across environments (Compose v2 plugin: "docker compose", Compose v1: "docker-compose").
6
+ * Uses docker-endpoint from dev config when set (see remote-docker-env / docker-exec).
6
7
  *
7
8
  * @fileoverview Docker/Compose detection helpers for AI Fabrix Builder
8
9
  * @author AI Fabrix Team
@@ -11,10 +12,65 @@
11
12
 
12
13
  'use strict';
13
14
 
14
- const { exec } = require('child_process');
15
- const { promisify } = require('util');
15
+ const { execWithDockerEnv } = require('./docker-exec');
16
16
 
17
- const execAsync = promisify(exec);
17
+ /** Env override: full compose CLI prefix, e.g. `docker compose` or `docker-compose` (validated with version). */
18
+ const COMPOSE_CMD_ENV = 'AIFABRIX_COMPOSE_CMD';
19
+
20
+ /**
21
+ * Short string from exec failure (message + stderr) for user-visible errors.
22
+ * @param {unknown} err - Rejected error from exec
23
+ * @returns {string}
24
+ */
25
+ function formatExecFailure(err) {
26
+ if (!err) return '';
27
+ const e = err;
28
+ const msg = typeof e.message === 'string' ? e.message.trim() : '';
29
+ const stderr = typeof e.stderr === 'string' ? e.stderr.trim() : '';
30
+ const combined = [msg, stderr].filter(Boolean).join(' ').trim();
31
+ if (!combined) return '';
32
+ return combined.length > 280 ? `${combined.slice(0, 277)}...` : combined;
33
+ }
34
+
35
+ /**
36
+ * Hint when docker compose likely failed talking to a remote TLS daemon, not missing plugin.
37
+ * @param {string} detail - formatExecFailure output
38
+ * @returns {string}
39
+ */
40
+ function remoteTlsComposeHint(detail) {
41
+ const d = detail.toLowerCase();
42
+ if (!d) return '';
43
+ const tlsish =
44
+ d.includes('tls') ||
45
+ d.includes('certificate') ||
46
+ d.includes('x509') ||
47
+ d.includes('cert') ||
48
+ d.includes('connection refused') ||
49
+ d.includes('2376') ||
50
+ d.includes('eof') ||
51
+ d.includes('handshake');
52
+ if (!tlsish) return '';
53
+ return (
54
+ ' This often means docker-endpoint TLS/client auth failed (place cert.pem, key.pem, ca.pem in ~/.aifabrix/certs/<developer-id>/, or set docker-tls-skip-verify only if appropriate), not a missing Compose plugin.'
55
+ );
56
+ }
57
+
58
+ /**
59
+ * @param {string} composeV2Failure - formatExecFailure from docker compose version
60
+ */
61
+ function throwComposeCommandUnavailable(composeV2Failure) {
62
+ const v2bit = composeV2Failure
63
+ ? ` "docker compose version" failed: ${composeV2Failure}.${remoteTlsComposeHint(composeV2Failure)} `
64
+ : ' ';
65
+ throw new Error(
66
+ 'Docker Compose is not available. Tried: "docker compose", "docker-compose", "podman compose".' +
67
+ v2bit +
68
+ 'Install the Compose v2 plugin (needs only the docker CLI + plugin; no unix socket required when docker-endpoint is set). ' +
69
+ 'Example: sudo apt-get install -y docker-compose-plugin. ' +
70
+ `Or set ${COMPOSE_CMD_ENV} to a working compose prefix. ` +
71
+ 'up-infra uses the Docker CLI as a client to the Engine API (remote tcp://…:2376 is OK); there is no separate Builder-only compose API.'
72
+ );
73
+ }
18
74
 
19
75
  /**
20
76
  * Checks that Docker CLI is available.
@@ -24,7 +80,29 @@ const execAsync = promisify(exec);
24
80
  * @throws {Error} If docker is unavailable
25
81
  */
26
82
  async function checkDockerCli() {
27
- await execAsync('docker --version');
83
+ await execWithDockerEnv('docker --version');
84
+ }
85
+
86
+ /**
87
+ * @returns {Promise<string|null>} Resolved compose command prefix from env override, or null to auto-detect
88
+ */
89
+ async function resolveComposeCmdOverride() {
90
+ const override =
91
+ typeof process.env[COMPOSE_CMD_ENV] === 'string' ? process.env[COMPOSE_CMD_ENV].trim() : '';
92
+ if (!override) return null;
93
+ try {
94
+ await execWithDockerEnv(`${override} version`);
95
+ return override;
96
+ } catch {
97
+ try {
98
+ await execWithDockerEnv(`${override} --version`);
99
+ return override;
100
+ } catch (err) {
101
+ throw new Error(
102
+ `${COMPOSE_CMD_ENV}="${override}" is set but failed (tried "version" and "--version"): ${err.message}`
103
+ );
104
+ }
105
+ }
28
106
  }
29
107
 
30
108
  /**
@@ -37,19 +115,29 @@ async function checkDockerCli() {
37
115
  * @throws {Error} If neither v2 nor v1 is available
38
116
  */
39
117
  async function getComposeCommand() {
40
- // Prefer Compose v2 plugin if present
118
+ const fromEnv = await resolveComposeCmdOverride();
119
+ if (fromEnv) return fromEnv;
120
+
121
+ let composeV2Failure = '';
41
122
  try {
42
- await execAsync('docker compose version');
123
+ await execWithDockerEnv('docker compose version');
43
124
  return 'docker compose';
44
- } catch (_) {
45
- // Fall back to legacy docker-compose
125
+ } catch (e) {
126
+ composeV2Failure = formatExecFailure(e);
46
127
  }
47
128
 
48
129
  try {
49
- await execAsync('docker-compose --version');
130
+ await execWithDockerEnv('docker-compose --version');
50
131
  return 'docker-compose';
51
132
  } catch (_) {
52
- throw new Error('Docker Compose is not available (neither "docker compose" nor "docker-compose" found). Install the Docker Compose v2 plugin (e.g., apt-get install docker-compose-plugin) or ensure docker-compose is on PATH.');
133
+ // Podman with compose (some hosts use podman-docker + podman compose)
134
+ }
135
+
136
+ try {
137
+ await execWithDockerEnv('podman compose version');
138
+ return 'podman compose';
139
+ } catch (_) {
140
+ throwComposeCommandUnavailable(composeV2Failure);
53
141
  }
54
142
  }
55
143
 
@@ -70,4 +158,3 @@ module.exports = {
70
158
  getComposeCommand,
71
159
  ensureDockerAndCompose
72
160
  };
73
-
@@ -0,0 +1,192 @@
1
+ /**
2
+ * When `docker-endpoint` is set but dev cert.pem/key.pem are missing, optionally call
3
+ * POST /api/dev/issue-cert using a one-time PIN from the environment (same flow as `aifabrix dev init`).
4
+ * There is no unauthenticated cert download; the PIN must be created from an enrolled machine.
5
+ *
6
+ * @fileoverview Auto issue-cert for remote Docker before infra / docker checks
7
+ * @author AI Fabrix Team
8
+ * @version 2.0.0
9
+ */
10
+
11
+ 'use strict';
12
+ const { formatSuccessLine } = require('./cli-test-layout-chalk');
13
+
14
+ const path = require('path');
15
+ const fsSync = require('node:fs');
16
+ const fsRealSync = require('../internal/fs-real-sync');
17
+ const { nodeFs } = require('../internal/node-fs');
18
+ const chalk = require('chalk');
19
+ const config = require('../core/config');
20
+ const logger = require('./logger');
21
+ const devApi = require('../api/dev.api');
22
+ const { getConfigDirForPaths } = require('./paths');
23
+ const {
24
+ generateCSR,
25
+ getCertDir,
26
+ mergeCaPemBlocks,
27
+ normalizePemNewlines
28
+ } = require('./dev-cert-helper');
29
+ const { isSslUntrustedError, fetchInstallCa } = require('./dev-ca-install');
30
+
31
+ /**
32
+ * Read one-time issue-cert PIN from env or first non-empty line of AIFABRIX_DEV_ISSUE_PIN_FILE.
33
+ * @returns {string|null}
34
+ */
35
+ function readIssueCertPin() {
36
+ const a = process.env.AIFABRIX_DEV_ISSUE_PIN;
37
+ const b = process.env.AIFABRIX_ISSUE_CERT_PIN;
38
+ const fromEnv =
39
+ typeof a === 'string' && a.trim() ? a.trim() : typeof b === 'string' && b.trim() ? b.trim() : '';
40
+ if (fromEnv) return fromEnv;
41
+ const fp = process.env.AIFABRIX_DEV_ISSUE_PIN_FILE;
42
+ if (!fp || typeof fp !== 'string' || !fp.trim()) return null;
43
+ try {
44
+ const raw = fsRealSync.readFileSyncDirect(path.normalize(fp.trim()), { encoding: 'utf8' });
45
+ let content;
46
+ if (typeof raw === 'string') {
47
+ content = raw;
48
+ } else if (Buffer.isBuffer(raw)) {
49
+ content = raw.toString('utf8');
50
+ } else {
51
+ throw new Error('readFileSync did not return string content');
52
+ }
53
+ const line = content.split(/\r?\n/).find((l) => l.trim());
54
+ return line ? line.trim() : null;
55
+ } catch (e) {
56
+ throw new Error(`AIFABRIX_DEV_ISSUE_PIN_FILE (${fp}): ${e.message}`);
57
+ }
58
+ }
59
+
60
+ async function resolveServerCaForIssueCert(baseUrl) {
61
+ try {
62
+ await devApi.getHealth(baseUrl);
63
+ return null;
64
+ } catch (err) {
65
+ if (isSslUntrustedError(err)) {
66
+ const caBuf = await fetchInstallCa(baseUrl);
67
+ const caPemStr = caBuf.toString('utf8').trim();
68
+ await devApi.getHealth(baseUrl, caPemStr);
69
+ return caPemStr;
70
+ }
71
+ throw err;
72
+ }
73
+ }
74
+
75
+ async function requestIssueCert(baseUrl, devId, pin, csrPem, serverCaPem) {
76
+ try {
77
+ return await devApi.issueCert(
78
+ baseUrl,
79
+ { developerId: devId, pin: pin.trim(), csr: csrPem },
80
+ serverCaPem || undefined
81
+ );
82
+ } catch (err) {
83
+ if (err.status === 401) {
84
+ throw new Error(
85
+ 'Invalid or expired PIN (AIFABRIX_DEV_ISSUE_PIN / AIFABRIX_ISSUE_CERT_PIN). ' +
86
+ 'Create a new PIN from an enrolled machine, e.g. `aifabrix dev pin <developerId>`.'
87
+ );
88
+ }
89
+ if (err.status === 404) {
90
+ throw new Error(`Developer ${devId} not found on Builder Server.`);
91
+ }
92
+ if (err.status === 503) {
93
+ throw new Error('Certificate signing is temporarily unavailable. Try again later.');
94
+ }
95
+ throw err;
96
+ }
97
+ }
98
+
99
+ async function saveDevCertMaterial(configDir, devId, certificatePem, keyPem, caPem) {
100
+ const fsp = nodeFs().promises;
101
+ const certDir = getCertDir(configDir, devId);
102
+ await fsp.mkdir(certDir, { recursive: true });
103
+ await fsp.writeFile(
104
+ path.join(certDir, 'cert.pem'),
105
+ normalizePemNewlines(certificatePem),
106
+ { mode: 0o600 }
107
+ );
108
+ await fsp.writeFile(
109
+ path.join(certDir, 'key.pem'),
110
+ normalizePemNewlines(keyPem),
111
+ { mode: 0o600 }
112
+ );
113
+ if (caPem && typeof caPem === 'string' && caPem.trim()) {
114
+ await fsp.writeFile(
115
+ path.join(certDir, 'ca.pem'),
116
+ normalizePemNewlines(caPem.trim()),
117
+ { mode: 0o600 }
118
+ );
119
+ }
120
+ await config.setDeveloperId(devId);
121
+ }
122
+
123
+ /**
124
+ * @returns {Promise<{ baseUrl: string, devId: string, certDir: string, configDir: string }|null>}
125
+ */
126
+ async function readEnsureIssueCertContext() {
127
+ const endpoint = await config.getDockerEndpoint();
128
+ if (!endpoint || typeof endpoint !== 'string' || !endpoint.trim()) return null;
129
+
130
+ const remoteServer = await config.getRemoteServer();
131
+ if (!remoteServer || typeof remoteServer !== 'string' || !remoteServer.trim()) return null;
132
+
133
+ const devIdRaw = await config.getDeveloperId();
134
+ if (!devIdRaw || typeof devIdRaw !== 'string' || !String(devIdRaw).trim()) return null;
135
+ const devId = String(devIdRaw).trim();
136
+
137
+ const configDir = getConfigDirForPaths();
138
+ const certDir = getCertDir(configDir, devId);
139
+ if (
140
+ fsSync.existsSync(path.join(certDir, 'cert.pem')) &&
141
+ fsSync.existsSync(path.join(certDir, 'key.pem'))
142
+ ) {
143
+ return null;
144
+ }
145
+
146
+ return {
147
+ baseUrl: remoteServer.trim().replace(/\/+$/, ''),
148
+ devId,
149
+ certDir,
150
+ configDir
151
+ };
152
+ }
153
+
154
+ function missingTlsFilesError(certDir, devId) {
155
+ return new Error(
156
+ `docker-endpoint is set but client TLS files are missing in ${certDir}. ` +
157
+ 'On a PC that already has a client certificate to the Builder Server, run: aifabrix dev pin ' +
158
+ devId +
159
+ '. Then on this host: aifabrix dev init --pin <pin> if remote-server and developer-id are in config, ' +
160
+ 'or export AIFABRIX_DEV_ISSUE_PIN=<pin> before retrying. ' +
161
+ 'Alternatively, if the Docker engine does not require client certificates, set docker-tls-skip-verify or AIFABRIX_DOCKER_TLS_SKIP_VERIFY.'
162
+ );
163
+ }
164
+
165
+ /**
166
+ * If remote Docker is configured and TLS files are missing, run issue-cert when a PIN is supplied via env.
167
+ * If no PIN and TLS skip-verify is set in config or env, returns without error.
168
+ * If no PIN and verify is required, throws with instructions.
169
+ * @returns {Promise<void>}
170
+ */
171
+ async function ensureDevCertsIfNeededForRemoteDocker() {
172
+ const ctx = await readEnsureIssueCertContext();
173
+ if (!ctx) return;
174
+
175
+ const pin = readIssueCertPin();
176
+ if (!pin) {
177
+ if (await config.getDockerTlsSkipVerify()) return;
178
+ throw missingTlsFilesError(ctx.certDir, ctx.devId);
179
+ }
180
+
181
+ logger.log(chalk.blue('\n🔐 Fetching developer certificate from Builder Server (issue-cert)...\n'));
182
+ const serverCaPem = await resolveServerCaForIssueCert(ctx.baseUrl);
183
+ const { csrPem, keyPem } = generateCSR(ctx.devId);
184
+ const issueResponse = await requestIssueCert(ctx.baseUrl, ctx.devId, pin, csrPem, serverCaPem);
185
+ const caPem = mergeCaPemBlocks(serverCaPem, issueResponse.caCertificate, issueResponse.ca);
186
+ await saveDevCertMaterial(ctx.configDir, ctx.devId, issueResponse.certificate, keyPem, caPem);
187
+ logger.log(
188
+ formatSuccessLine('Saved TLS material to ') + chalk.cyan(ctx.certDir) + chalk.green(' for remote Docker.\n')
189
+ );
190
+ }
191
+
192
+ module.exports = { ensureDevCertsIfNeededForRemoteDocker, readIssueCertPin };
@@ -1,113 +1,32 @@
1
1
  /**
2
- * Environment config loader
2
+ * Environment config loader — infra defaults live in code ({@link module:lib/utils/infra-env-defaults}).
3
3
  *
4
- * @fileoverview Loads lib/schema/env-config.yaml for env variable interpolation
5
- * Merges with user's env-config file if configured in ~/.aifabrix/config.yaml
4
+ * @fileoverview Replaces lib/schema/env-config.yaml + user merge
6
5
  * @author AI Fabrix Team
7
- * @version 2.0.0
6
+ * @version 2.1.0
8
7
  */
9
8
 
10
9
  'use strict';
11
10
 
12
- const fs = require('fs');
13
- const path = require('path');
14
- const yaml = require('js-yaml');
15
- const config = require('../core/config');
11
+ const { getDefaultEnvConfig } = require('./infra-env-defaults');
16
12
 
17
13
  /**
18
- * Loads user env-config file if configured
19
- * @async
20
- * @function loadUserEnvConfig
21
- * @returns {Promise<Object|null>} Parsed user env-config or null if not configured
22
- */
23
- async function loadUserEnvConfig() {
24
- try {
25
- const userEnvConfigPath = await config.getAifabrixEnvConfigPath();
26
- if (!userEnvConfigPath) {
27
- return null;
28
- }
29
-
30
- // Resolve path (support absolute and relative paths)
31
- const resolvedPath = path.isAbsolute(userEnvConfigPath)
32
- ? userEnvConfigPath
33
- : path.resolve(process.cwd(), userEnvConfigPath);
34
-
35
- if (!fs.existsSync(resolvedPath)) {
36
- return null;
37
- }
38
-
39
- const content = fs.readFileSync(resolvedPath, 'utf8');
40
- const userConfig = yaml.load(content);
41
- return userConfig && typeof userConfig === 'object' ? userConfig : null;
42
- } catch (error) {
43
- // Gracefully handle errors - fallback to base config only
44
- return null;
45
- }
46
- }
47
-
48
- /**
49
- * Merges user env-config with base env-config
50
- * User values override/extend base values
51
- * @function mergeEnvConfigs
52
- * @param {Object} baseConfig - Base env-config from lib/schema/env-config.yaml
53
- * @param {Object|null} userConfig - User env-config or null
54
- * @returns {Object} Merged env-config
55
- */
56
- function mergeEnvConfigs(baseConfig, userConfig) {
57
- if (!userConfig || typeof userConfig !== 'object') {
58
- return baseConfig || {};
59
- }
60
-
61
- // Deep merge environments
62
- const merged = { ...baseConfig };
63
- if (userConfig.environments && typeof userConfig.environments === 'object') {
64
- merged.environments = { ...(baseConfig.environments || {}) };
65
- for (const [env, envVars] of Object.entries(userConfig.environments)) {
66
- if (envVars && typeof envVars === 'object') {
67
- merged.environments[env] = {
68
- ...(merged.environments[env] || {}),
69
- ...envVars
70
- };
71
- }
72
- }
73
- }
74
-
75
- return merged;
76
- }
77
-
78
- /**
79
- * Load schema-only env-config (no user merge). Used for *_PUBLIC_PORT calculation
80
- * so public ports always use canonical base (e.g. KEYCLOAK_PUBLIC_PORT = 8082 + devId*100).
81
- *
82
- * @function loadSchemaEnvConfig
83
- * @returns {Object} Parsed schema env-config (environments.docker / .local only)
14
+ * Schema-only env-config (sync). Used for *_PUBLIC_PORT canonical bases.
15
+ * @returns {Object}
84
16
  */
85
17
  function loadSchemaEnvConfig() {
86
- const envConfigPath = path.join(__dirname, '..', 'schema', 'env-config.yaml');
87
- const content = fs.readFileSync(envConfigPath, 'utf8');
88
- return yaml.load(content) || {};
18
+ return getDefaultEnvConfig();
89
19
  }
90
20
 
91
21
  /**
92
- * Load env config YAML used for environment variable interpolation
93
- * Loads base config from lib/schema/env-config.yaml and merges with user config if configured
94
- * @async
95
- * @function loadEnvConfig
96
- * @returns {Promise<Object>} Parsed and merged env-config YAML
97
- * @throws {Error} If base file cannot be read or parsed
22
+ * Load env config for interpolation (same as schema; no external YAML).
23
+ * @returns {Promise<Object>}
98
24
  */
99
25
  async function loadEnvConfig() {
100
- const baseConfig = loadSchemaEnvConfig();
101
-
102
- // Load user env-config if configured
103
- const userConfig = await loadUserEnvConfig();
104
-
105
- // Merge user config with base (user overrides/extends base)
106
- return mergeEnvConfigs(baseConfig, userConfig);
26
+ return getDefaultEnvConfig();
107
27
  }
108
28
 
109
29
  module.exports = {
110
30
  loadEnvConfig,
111
31
  loadSchemaEnvConfig
112
32
  };
113
-
@@ -7,12 +7,12 @@
7
7
  */
8
8
 
9
9
  'use strict';
10
+ const { formatSuccessLine } = require('./cli-test-layout-chalk');
10
11
 
11
12
  const fs = require('fs');
12
13
  const fsp = require('fs').promises;
13
14
  const path = require('path');
14
15
  const yaml = require('js-yaml');
15
- const chalk = require('chalk');
16
16
  const logger = require('./logger');
17
17
  const config = require('../core/config');
18
18
  const devConfig = require('../utils/dev-config');
@@ -58,7 +58,12 @@ function readDeveloperIdFromConfig(config) {
58
58
  */
59
59
  function substituteMntDataForLocal(content, outputPath) {
60
60
  const outputDir = path.dirname(outputPath);
61
- const localMountPath = path.resolve(outputDir, 'mount');
61
+ // Avoid introducing a drive letter for root-relative paths (e.g. "\tmp\out"),
62
+ // which are common in unit tests and some Windows path constructions.
63
+ const isWinRootRelative = process.platform === 'win32' && /^\\(?!\\)/.test(outputDir);
64
+ const localMountPath = isWinRootRelative
65
+ ? path.win32.normalize(path.win32.join(outputDir, 'mount'))
66
+ : path.resolve(outputDir, 'mount');
62
67
  if (!fs.existsSync(localMountPath)) {
63
68
  fs.mkdirSync(localMountPath, { recursive: true });
64
69
  }
@@ -77,7 +82,10 @@ function resolveEnvOutputPath(rawOutputPath, variablesPath) {
77
82
  outputPath = rawOutputPath;
78
83
  } else {
79
84
  const variablesDir = path.dirname(variablesPath);
80
- outputPath = path.resolve(variablesDir, rawOutputPath);
85
+ const isWinRootRelative = process.platform === 'win32' && /^\\(?!\\)/.test(variablesDir);
86
+ outputPath = isWinRootRelative
87
+ ? path.win32.normalize(path.win32.join(variablesDir, rawOutputPath))
88
+ : path.resolve(variablesDir, rawOutputPath);
81
89
  }
82
90
  if (!outputPath.endsWith('.env')) {
83
91
  if (fs.existsSync(outputPath) && fs.statSync(outputPath).isDirectory()) {
@@ -105,7 +113,7 @@ async function writeEnvOutputForReload(outputPath, runEnvPath) {
105
113
  toWrite = mergeEnvMapIntoContent(existingContent, runMap);
106
114
  }
107
115
  await fsp.writeFile(outputPath, toWrite, { mode: 0o600 });
108
- logger.log(chalk.green(`✓ Wrote .env to envOutputPath (same as container, for --reload): ${outputPath}`));
116
+ logger.log(formatSuccessLine(`Wrote .env to envOutputPath (same as container, for --reload): ${outputPath}`));
109
117
  }
110
118
 
111
119
  /**
@@ -125,7 +133,7 @@ async function writeEnvOutputForLocal(appName, outputPath) {
125
133
  toWrite = mergeEnvMapIntoContent(existingContent, localMap);
126
134
  }
127
135
  await fsp.writeFile(outputPath, toWrite, { mode: 0o600 });
128
- logger.log(chalk.green(`✓ Wrote .env to envOutputPath (localPort): ${outputPath}`));
136
+ logger.log(formatSuccessLine(`Wrote .env to envOutputPath (localPort): ${outputPath}`));
129
137
  }
130
138
 
131
139
  /**
@@ -134,16 +142,17 @@ async function writeEnvOutputForLocal(appName, outputPath) {
134
142
  * @returns {number} Developer-specific app port
135
143
  */
136
144
  function calculateDevAppPort(baseAppPort) {
145
+ const { localHostPort, parseDeveloperIdNum } = require('./declarative-url-ports');
137
146
  const devIdRaw = process.env.AIFABRIX_DEVELOPERID;
138
147
  let devIdNum = Number.isFinite(parseInt(devIdRaw, 10)) ? parseInt(devIdRaw, 10) : null;
139
148
  try {
140
149
  if (devIdNum === null) {
141
- devIdNum = readDeveloperIdFromConfig(config) || 0;
150
+ devIdNum = readDeveloperIdFromConfig(config);
142
151
  }
143
152
  } catch {
144
- devIdNum = 0;
153
+ devIdNum = null;
145
154
  }
146
- return devIdNum === 0 ? baseAppPort : (baseAppPort + (devIdNum * 100));
155
+ return localHostPort(baseAppPort, parseDeveloperIdNum(devIdNum));
147
156
  }
148
157
 
149
158
  /**
@@ -255,7 +264,7 @@ async function writeLocalEnvToOutputPath(outputPath, appName, secretsPath, envOu
255
264
  toWrite = mergeEnvMapIntoContent(existingContent, localMap);
256
265
  }
257
266
  fs.writeFileSync(outputPath, toWrite, { mode: 0o600 });
258
- logger.log(chalk.green(`✓ Generated local .env at: ${envOutputPathLabel}`));
267
+ logger.log(formatSuccessLine(`Generated local .env at: ${envOutputPathLabel}`));
259
268
  }
260
269
 
261
270
  /**
@@ -271,7 +280,7 @@ async function writePatchedEnvToOutputPath(envPath, outputPath, variables, envOu
271
280
  let patchedContent = await patchEnvContentForLocal(envContent, variables);
272
281
  patchedContent = substituteMntDataForLocal(patchedContent, outputPath);
273
282
  fs.writeFileSync(outputPath, patchedContent, { mode: 0o600 });
274
- logger.log(chalk.green(`✓ Copied .env to: ${envOutputPathLabel}`));
283
+ logger.log(formatSuccessLine(`Copied .env to: ${envOutputPathLabel}`));
275
284
  }
276
285
 
277
286
  /**
@@ -292,6 +292,38 @@ function calculateDockerPublicPorts(result, devIdNum, schemaBaseVars = {}) {
292
292
  }
293
293
  }
294
294
 
295
+ /**
296
+ * Apply *_PUBLIC_PORT from schema env for local or docker context.
297
+ * @param {Object} result - Normalized env map (mutated)
298
+ * @param {'docker'|'local'} context
299
+ * @param {number} devIdNum
300
+ */
301
+ function applyPublicPortAdjustmentsForContext(result, context, devIdNum) {
302
+ if (context !== 'local' && context !== 'docker') return;
303
+ const schemaCfg = loadSchemaEnvConfig();
304
+ const schemaBaseVars =
305
+ schemaCfg && schemaCfg.environments && schemaCfg.environments[context]
306
+ ? schemaCfg.environments[context]
307
+ : {};
308
+ calculateDockerPublicPorts(result, devIdNum, schemaBaseVars);
309
+ }
310
+
311
+ /**
312
+ * Set TLS_ENABLED / HTTP_ENABLED from ~/.aifabrix/config.yaml (tlsEnabled).
313
+ * @param {Object} result - Env map (mutated)
314
+ */
315
+ async function applyTlsHttpManifestFlags(result) {
316
+ let tlsOn = false;
317
+ try {
318
+ const userCfg = await config.getConfig();
319
+ tlsOn = Boolean(userCfg && userCfg.tlsEnabled === true);
320
+ } catch {
321
+ tlsOn = false;
322
+ }
323
+ result.TLS_ENABLED = tlsOn ? 'true' : 'false';
324
+ result.HTTP_ENABLED = tlsOn ? 'false' : 'true';
325
+ }
326
+
295
327
  /**
296
328
  * Build environment variable map for interpolation based on env-config.yaml
297
329
  * - Supports values like "host:port" by splitting into *_HOST (host) and *_PORT (port)
@@ -300,6 +332,7 @@ function calculateDockerPublicPorts(result, devIdNum, schemaBaseVars = {}) {
300
332
  * - Applies aifabrix-localhost override for local context if configured
301
333
  * - Applies developer-id adjustment to port variables for local context
302
334
  * - Calculates *_PUBLIC_PORT for both local and docker context (basePort + developer-id * 100)
335
+ * - Adds TLS_ENABLED and HTTP_ENABLED ("true" / "false"); HTTP_ENABLED = !TLS_ENABLED for manifest ${...} substitution
303
336
  * @async
304
337
  * @function buildEnvVarMap
305
338
  * @param {'docker'|'local'} context - Environment context
@@ -323,15 +356,9 @@ async function buildEnvVarMap(context, osModule = null, developerId = null, opti
323
356
  const devIdNum = await getDeveloperIdNumber(developerId);
324
357
  if (context === 'local') {
325
358
  applyLocalPortAdjustment(result, devIdNum);
326
- const schemaCfg = loadSchemaEnvConfig();
327
- const schemaBaseVars = (schemaCfg && schemaCfg.environments && schemaCfg.environments.local) ? schemaCfg.environments.local : {};
328
- calculateDockerPublicPorts(result, devIdNum, schemaBaseVars);
329
- } else if (context === 'docker') {
330
- const schemaCfg = loadSchemaEnvConfig();
331
- const schemaBaseVars = (schemaCfg && schemaCfg.environments && schemaCfg.environments[context]) ? schemaCfg.environments[context] : {};
332
- calculateDockerPublicPorts(result, devIdNum, schemaBaseVars);
333
359
  }
334
-
360
+ applyPublicPortAdjustmentsForContext(result, context, devIdNum);
361
+ await applyTlsHttpManifestFlags(result);
335
362
  return result;
336
363
  }
337
364
 
@@ -108,7 +108,7 @@ async function updateEnvTemplate(appKey, clientIdKey, clientSecretKey, _controll
108
108
  const envTemplatePath = path.join(process.cwd(), 'builder', appKey, 'env.template');
109
109
 
110
110
  if (!fsSync.existsSync(envTemplatePath)) {
111
- logger.warn(chalk.yellow(`⚠️ env.template not found for ${appKey}, skipping update`));
111
+ logger.warn(chalk.yellow(`⚠ env.template not found for ${appKey}, skipping update`));
112
112
  return;
113
113
  }
114
114
 
@@ -126,7 +126,7 @@ async function updateEnvTemplate(appKey, clientIdKey, clientSecretKey, _controll
126
126
 
127
127
  await fs.writeFile(envTemplatePath, content, 'utf8');
128
128
  } catch (error) {
129
- logger.warn(chalk.yellow(`⚠️ Could not update env.template: ${error.message}`));
129
+ logger.warn(chalk.yellow(`⚠ Could not update env.template: ${error.message}`));
130
130
  }
131
131
  }
132
132