@aifabrix/builder 2.43.0 → 2.44.1

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 (371) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/.cursor/rules/cli-layout.mdc +75 -0
  3. package/.cursor/rules/project-rules.mdc +8 -0
  4. package/.npmrc.token +1 -0
  5. package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
  6. package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
  7. package/.nyc_output/processinfo/index.json +1 -0
  8. package/README.md +1 -1
  9. package/anchor-docs/README.md +10 -0
  10. package/anchor-docs/_TEMPLATE +24 -0
  11. package/bin/aifabrix.js +13 -4
  12. package/integration/hubspot-test/README.md +31 -0
  13. package/integration/hubspot-test/create-hubspot.js +5 -5
  14. package/integration/hubspot-test/hubspot-test-datasource-company.json +58 -462
  15. package/integration/hubspot-test/hubspot-test-datasource-contact.json +61 -555
  16. package/integration/hubspot-test/hubspot-test-datasource-deal.json +63 -506
  17. package/integration/hubspot-test/hubspot-test-datasource-users.json +42 -83
  18. package/integration/hubspot-test/hubspot-test-deploy.json +3 -3
  19. package/integration/hubspot-test/test-dataplane-down-tests.js +1 -7
  20. package/integration/hubspot-test/test-dataplane-down.js +3 -3
  21. package/integration/hubspot-test/test.js +35 -43
  22. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  23. package/integration/roundtrip-test-local/README.md +144 -0
  24. package/integration/roundtrip-test-local/application.yaml +13 -0
  25. package/integration/roundtrip-test-local/env.template +15 -0
  26. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  27. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  28. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  29. package/integration/roundtrip-test-local2/README.md +144 -0
  30. package/integration/roundtrip-test-local2/application.yaml +13 -0
  31. package/integration/roundtrip-test-local2/env.template +15 -0
  32. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  33. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  34. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  35. package/integration/test/wizard.yaml +8 -0
  36. package/jest.config.default.js +10 -0
  37. package/jest.config.integration.fixtures.js +22 -0
  38. package/jest.config.integration.js +21 -18
  39. package/jest.config.isolated.js +10 -0
  40. package/jest.projects.js +301 -0
  41. package/lib/api/certificates.api.js +62 -0
  42. package/lib/api/datasources-core.api.js +3 -3
  43. package/lib/api/dev-mtls-request.js +110 -0
  44. package/lib/api/dev-server-https.js +145 -0
  45. package/lib/api/dev.api.js +133 -144
  46. package/lib/api/index.js +11 -3
  47. package/lib/api/pipeline.api.js +67 -20
  48. package/lib/api/types/certificates.types.js +48 -0
  49. package/lib/api/types/dev.types.js +4 -3
  50. package/lib/api/types/pipeline.types.js +8 -5
  51. package/lib/api/types/validation-run.types.js +56 -0
  52. package/lib/api/validation-run.api.js +111 -0
  53. package/lib/api/validation-runner.js +109 -0
  54. package/lib/app/certification-show-enrich.js +129 -0
  55. package/lib/app/certification-verify-rows.js +60 -0
  56. package/lib/app/config.js +1 -1
  57. package/lib/app/deploy-status-display.js +2 -2
  58. package/lib/app/deploy.js +7 -6
  59. package/lib/app/display.js +2 -1
  60. package/lib/app/dockerfile.js +3 -2
  61. package/lib/app/down.js +2 -1
  62. package/lib/app/helpers.js +6 -5
  63. package/lib/app/index.js +27 -8
  64. package/lib/app/list.js +7 -6
  65. package/lib/app/push.js +4 -3
  66. package/lib/app/register.js +16 -7
  67. package/lib/app/rotate-secret.js +14 -13
  68. package/lib/app/run-container-start.js +184 -0
  69. package/lib/app/run-docker-fallback.js +108 -0
  70. package/lib/app/run-env-compose.js +30 -42
  71. package/lib/app/run-helpers.js +49 -126
  72. package/lib/app/run-infra-requirements.js +30 -0
  73. package/lib/app/run-resolve-image.js +21 -0
  74. package/lib/app/run.js +74 -21
  75. package/lib/app/show-display.js +44 -1
  76. package/lib/app/show.js +93 -9
  77. package/lib/build/index.js +13 -10
  78. package/lib/certification/cli-cert-sync-skip.js +21 -0
  79. package/lib/certification/merge-certification-from-artifact.js +185 -0
  80. package/lib/certification/post-unified-cert-sync.js +33 -0
  81. package/lib/certification/sync-after-external-command.js +52 -0
  82. package/lib/certification/sync-system-certification.js +197 -0
  83. package/lib/cli/index.js +2 -0
  84. package/lib/cli/setup-app.help.js +67 -0
  85. package/lib/cli/setup-app.js +61 -121
  86. package/lib/cli/setup-app.test-commands.js +195 -0
  87. package/lib/cli/setup-auth.js +19 -5
  88. package/lib/cli/setup-credential-deployment.js +22 -8
  89. package/lib/cli/setup-dev-path-commands.js +124 -0
  90. package/lib/cli/setup-dev.js +170 -113
  91. package/lib/cli/setup-environment.js +7 -1
  92. package/lib/cli/setup-external-system.js +84 -23
  93. package/lib/cli/setup-infra.js +126 -47
  94. package/lib/cli/setup-parameters.js +32 -0
  95. package/lib/cli/setup-secrets.js +137 -18
  96. package/lib/cli/setup-service-user.js +1 -1
  97. package/lib/cli/setup-utility.js +54 -22
  98. package/lib/commands/app-down.js +5 -7
  99. package/lib/commands/app-install.js +14 -7
  100. package/lib/commands/app-logs.js +13 -10
  101. package/lib/commands/app-shell.js +4 -1
  102. package/lib/commands/app-test.js +25 -19
  103. package/lib/commands/app.js +32 -11
  104. package/lib/commands/auth-config.js +6 -6
  105. package/lib/commands/auth-status.js +4 -3
  106. package/lib/commands/credential-env.js +4 -3
  107. package/lib/commands/credential-list.js +5 -4
  108. package/lib/commands/credential-push.js +4 -3
  109. package/lib/commands/datasource-unified-test-cli.js +428 -0
  110. package/lib/commands/datasource-unified-test-cli.options.js +191 -0
  111. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +106 -0
  112. package/lib/commands/datasource-validation-cli.js +143 -0
  113. package/lib/commands/datasource.js +125 -95
  114. package/lib/commands/deployment-list.js +6 -5
  115. package/lib/commands/dev-cli-handlers.js +122 -18
  116. package/lib/commands/dev-down.js +4 -3
  117. package/lib/commands/dev-init.js +231 -116
  118. package/lib/commands/dev-show-display.js +473 -0
  119. package/lib/commands/login-credentials.js +3 -2
  120. package/lib/commands/login-device.js +4 -3
  121. package/lib/commands/login.js +5 -4
  122. package/lib/commands/logout.js +8 -7
  123. package/lib/commands/parameters-validate.js +54 -0
  124. package/lib/commands/repair-datasource.js +314 -68
  125. package/lib/commands/repair-env-template.js +2 -2
  126. package/lib/commands/repair.js +21 -3
  127. package/lib/commands/secrets-list.js +23 -12
  128. package/lib/commands/secrets-remove-all.js +220 -0
  129. package/lib/commands/secrets-remove.js +21 -12
  130. package/lib/commands/secrets-set.js +21 -12
  131. package/lib/commands/secrets-validate.js +4 -4
  132. package/lib/commands/secure.js +10 -9
  133. package/lib/commands/service-user.js +26 -25
  134. package/lib/commands/test-e2e-external.js +27 -1
  135. package/lib/commands/up-common.js +3 -2
  136. package/lib/commands/up-dataplane.js +29 -16
  137. package/lib/commands/up-miso.js +19 -29
  138. package/lib/commands/upload.js +149 -39
  139. package/lib/commands/wizard-core-helpers.js +1 -1
  140. package/lib/commands/wizard-dataplane.js +4 -3
  141. package/lib/commands/wizard-helpers.js +3 -3
  142. package/lib/commands/wizard.js +2 -2
  143. package/lib/core/admin-secrets.js +14 -5
  144. package/lib/core/audit-logger.js +12 -4
  145. package/lib/core/config-attach-extensions.js +46 -0
  146. package/lib/core/config-runtime-paths.js +29 -0
  147. package/lib/core/config.js +55 -56
  148. package/lib/core/diff.js +3 -2
  149. package/lib/core/ensure-encryption-key.js +1 -1
  150. package/lib/core/secrets-ensure-infra.js +77 -0
  151. package/lib/core/secrets-ensure.js +120 -64
  152. package/lib/core/secrets-env-write.js +35 -7
  153. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  154. package/lib/core/secrets.js +200 -37
  155. package/lib/core/templates-env.js +4 -3
  156. package/lib/datasource/abac-validator.js +1 -10
  157. package/lib/datasource/deploy.js +75 -53
  158. package/lib/datasource/field-reference-validator.js +9 -6
  159. package/lib/datasource/integration-context.js +63 -0
  160. package/lib/datasource/list.js +8 -7
  161. package/lib/datasource/log-viewer.js +189 -67
  162. package/lib/datasource/resolve-app.js +4 -4
  163. package/lib/datasource/test-e2e.js +113 -146
  164. package/lib/datasource/test-integration.js +114 -122
  165. package/lib/datasource/unified-validation-run-body.js +68 -0
  166. package/lib/datasource/unified-validation-run-post.js +23 -0
  167. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  168. package/lib/datasource/unified-validation-run.js +93 -0
  169. package/lib/datasource/validate.js +157 -13
  170. package/lib/deployment/deployer.js +4 -3
  171. package/lib/deployment/environment.js +7 -6
  172. package/lib/deployment/push.js +17 -8
  173. package/lib/external-system/delete.js +4 -3
  174. package/lib/external-system/deploy.js +166 -53
  175. package/lib/external-system/download-helpers.js +1 -1
  176. package/lib/external-system/download.js +7 -6
  177. package/lib/external-system/generator.js +92 -6
  178. package/lib/external-system/integration-test-dispatch.js +26 -0
  179. package/lib/external-system/test-execution.js +5 -1
  180. package/lib/external-system/test-helpers.js +0 -4
  181. package/lib/external-system/test-system-level-helpers.js +110 -0
  182. package/lib/external-system/test-system-level.js +83 -44
  183. package/lib/external-system/test.js +59 -8
  184. package/lib/generator/builders.js +23 -11
  185. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  186. package/lib/generator/external.js +16 -4
  187. package/lib/generator/helpers.js +58 -3
  188. package/lib/generator/index.js +4 -0
  189. package/lib/generator/split-readme.js +12 -7
  190. package/lib/generator/split-variables.js +2 -1
  191. package/lib/generator/split.js +1 -1
  192. package/lib/generator/wizard-readme.js +3 -3
  193. package/lib/generator/wizard.js +8 -8
  194. package/lib/infrastructure/compose.js +70 -7
  195. package/lib/infrastructure/helpers-docker-check.js +67 -0
  196. package/lib/infrastructure/helpers.js +203 -42
  197. package/lib/infrastructure/index.js +31 -18
  198. package/lib/infrastructure/services.js +21 -67
  199. package/lib/internal/fs-real-sync.js +104 -0
  200. package/lib/internal/node-fs.js +98 -0
  201. package/lib/parameters/database-secret-values.js +173 -0
  202. package/lib/parameters/infra-kv-discovery.js +121 -0
  203. package/lib/parameters/infra-parameter-catalog.js +458 -0
  204. package/lib/parameters/infra-parameter-validate.js +64 -0
  205. package/lib/schema/application-schema.json +37 -17
  206. package/lib/schema/datasource-test-run.schema.json +493 -0
  207. package/lib/schema/deployment-rules.yaml +102 -63
  208. package/lib/schema/external-datasource.schema.json +1200 -442
  209. package/lib/schema/external-system.schema.json +203 -5
  210. package/lib/schema/flag-map-validation-run.json +31 -0
  211. package/lib/schema/infra-parameter.schema.json +106 -0
  212. package/lib/schema/infra.parameter.yaml +421 -0
  213. package/lib/schema/type/credential-auth-templates.json +40 -0
  214. package/lib/schema/type/document-storage.json +226 -0
  215. package/lib/schema/type/message-service.json +123 -0
  216. package/lib/schema/type/vector-store.json +88 -0
  217. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  218. package/lib/utils/api-error-handler.js +2 -2
  219. package/lib/utils/api.js +77 -17
  220. package/lib/utils/app-register-api.js +3 -2
  221. package/lib/utils/app-register-auth.js +1 -1
  222. package/lib/utils/app-register-config.js +4 -4
  223. package/lib/utils/app-register-display.js +3 -2
  224. package/lib/utils/app-register-validator.js +3 -2
  225. package/lib/utils/app-run-containers.js +26 -22
  226. package/lib/utils/app-scoped-config.js +31 -0
  227. package/lib/utils/app-service-env-from-builder.js +164 -0
  228. package/lib/utils/build-copy.js +1 -1
  229. package/lib/utils/build-helpers.js +20 -20
  230. package/lib/utils/build-resolve-image.js +165 -0
  231. package/lib/utils/cli-layout-chalk.js +8 -0
  232. package/lib/utils/cli-test-layout-chalk.js +267 -0
  233. package/lib/utils/cli-utils.js +88 -11
  234. package/lib/utils/compose-db-passwords.js +138 -0
  235. package/lib/utils/compose-generate-docker-compose.js +216 -0
  236. package/lib/utils/compose-generator.js +197 -291
  237. package/lib/utils/compose-miso-env.js +18 -0
  238. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  239. package/lib/utils/config-paths.js +166 -7
  240. package/lib/utils/config-scoped-resources-preference.js +41 -0
  241. package/lib/utils/configuration-env-resolver.js +11 -8
  242. package/lib/utils/controller-deployment-outcome.js +68 -0
  243. package/lib/utils/credential-display.js +2 -2
  244. package/lib/utils/credential-secrets-env.js +5 -5
  245. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  246. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  247. package/lib/utils/datasource-test-run-certificate-tty.js +82 -0
  248. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  249. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  250. package/lib/utils/datasource-test-run-display.js +459 -0
  251. package/lib/utils/datasource-test-run-exit.js +83 -0
  252. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  253. package/lib/utils/datasource-test-run-report-version.js +51 -0
  254. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  255. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  256. package/lib/utils/datasource-validation-watch.js +266 -0
  257. package/lib/utils/declarative-url-ports.js +47 -0
  258. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  259. package/lib/utils/dev-ca-install.js +185 -23
  260. package/lib/utils/dev-cert-helper.js +266 -17
  261. package/lib/utils/dev-hosts-helper.js +307 -0
  262. package/lib/utils/dev-init-cert-hints.js +37 -0
  263. package/lib/utils/dev-init-health-messages.js +52 -0
  264. package/lib/utils/dev-init-resolve.js +86 -0
  265. package/lib/utils/dev-init-ssh-merge.js +65 -0
  266. package/lib/utils/dev-ssh-config-helper.js +196 -0
  267. package/lib/utils/dev-user-groups.js +93 -0
  268. package/lib/utils/docker-build.js +42 -17
  269. package/lib/utils/docker-exec.js +28 -0
  270. package/lib/utils/docker-manifest-public-port.js +116 -0
  271. package/lib/utils/docker-not-running-hint.js +52 -0
  272. package/lib/utils/docker.js +98 -11
  273. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  274. package/lib/utils/env-config-loader.js +10 -91
  275. package/lib/utils/env-copy.js +19 -10
  276. package/lib/utils/env-map.js +35 -8
  277. package/lib/utils/env-template.js +2 -2
  278. package/lib/utils/environment-scoped-resources.js +144 -0
  279. package/lib/utils/error-formatter.js +92 -13
  280. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  281. package/lib/utils/error-formatters/network-errors.js +2 -1
  282. package/lib/utils/error-formatters/permission-errors.js +2 -1
  283. package/lib/utils/error-formatters/validation-errors.js +2 -1
  284. package/lib/utils/external-readme.js +8 -1
  285. package/lib/utils/external-system-display.js +242 -136
  286. package/lib/utils/external-system-local-test-tty.js +389 -0
  287. package/lib/utils/external-system-readiness-core.js +377 -0
  288. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  289. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  290. package/lib/utils/external-system-readiness-display.js +186 -0
  291. package/lib/utils/external-system-system-test-tty-overview.js +120 -0
  292. package/lib/utils/external-system-system-test-tty.js +417 -0
  293. package/lib/utils/external-system-test-helpers.js +24 -6
  294. package/lib/utils/external-system-validators.js +30 -12
  295. package/lib/utils/health-check-url.js +119 -0
  296. package/lib/utils/health-check.js +59 -25
  297. package/lib/utils/help-builder.js +11 -8
  298. package/lib/utils/image-version.js +4 -8
  299. package/lib/utils/infra-containers.js +4 -7
  300. package/lib/utils/infra-env-defaults.js +162 -0
  301. package/lib/utils/infra-status-display.js +167 -0
  302. package/lib/utils/infra-status.js +16 -8
  303. package/lib/utils/local-secrets.js +3 -4
  304. package/lib/utils/paths.js +148 -47
  305. package/lib/utils/port-resolver.js +10 -23
  306. package/lib/utils/redis-env-scope.js +62 -0
  307. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  308. package/lib/utils/remote-builder-validation.js +99 -0
  309. package/lib/utils/remote-dev-auth.js +117 -21
  310. package/lib/utils/remote-docker-env.js +67 -15
  311. package/lib/utils/remote-secrets-loader.js +13 -4
  312. package/lib/utils/resolve-docker-image-ref.js +124 -0
  313. package/lib/utils/schema-loader.js +22 -9
  314. package/lib/utils/secrets-bash-kv.js +25 -0
  315. package/lib/utils/secrets-generator.js +169 -49
  316. package/lib/utils/secrets-helpers.js +70 -59
  317. package/lib/utils/secrets-kv-scope.js +60 -0
  318. package/lib/utils/secrets-utils.js +32 -38
  319. package/lib/utils/secrets-validation.js +3 -1
  320. package/lib/utils/secrets-yaml-preserve.js +109 -0
  321. package/lib/utils/ssh-key-helper.js +4 -2
  322. package/lib/utils/template-helpers.js +2 -2
  323. package/lib/utils/test-log-writer.js +3 -3
  324. package/lib/utils/token-manager.js +1 -2
  325. package/lib/utils/url-declarative-public-base.js +188 -0
  326. package/lib/utils/url-declarative-resolve-build.js +493 -0
  327. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  328. package/lib/utils/url-declarative-resolve.js +220 -0
  329. package/lib/utils/url-declarative-token-parse.js +74 -0
  330. package/lib/utils/url-declarative-url-flags.js +50 -0
  331. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  332. package/lib/utils/url-public-path-prefix.js +34 -0
  333. package/lib/utils/urls-local-registry.js +220 -0
  334. package/lib/utils/validation-report-tty-kit.js +77 -0
  335. package/lib/utils/validation-run-poll.js +112 -0
  336. package/lib/utils/validation-run-post-retry.js +85 -0
  337. package/lib/utils/validation-run-request.js +116 -0
  338. package/lib/utils/variable-transformer.js +21 -4
  339. package/lib/utils/yaml-preserve.js +33 -14
  340. package/lib/validation/datasource-warnings.js +56 -0
  341. package/lib/validation/env-template-auth.js +1 -1
  342. package/lib/validation/external-manifest-validator.js +27 -7
  343. package/lib/validation/validate-display.js +37 -31
  344. package/lib/validation/validate-external-cert-sync.js +23 -0
  345. package/lib/validation/validate.js +8 -14
  346. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  347. package/lib/validation/validator.js +22 -65
  348. package/lib/validation/wizard-config-validator.js +2 -1
  349. package/package.json +9 -4
  350. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  351. package/scripts/diagnose-cli.js +150 -0
  352. package/scripts/install-local.js +307 -55
  353. package/scripts/pnpm-global-remove.js +48 -0
  354. package/templates/README.md +15 -2
  355. package/templates/applications/dataplane/application.yaml +52 -2
  356. package/templates/applications/dataplane/env.template +79 -17
  357. package/templates/applications/dataplane/rbac.yaml +8 -0
  358. package/templates/applications/keycloak/application.yaml +9 -1
  359. package/templates/applications/keycloak/env.template +15 -6
  360. package/templates/applications/miso-controller/application.yaml +10 -2
  361. package/templates/applications/miso-controller/env.template +42 -12
  362. package/templates/applications/miso-controller/rbac.yaml +5 -0
  363. package/templates/external-system/README.md.hbs +20 -7
  364. package/templates/external-system/deploy.js.hbs +5 -5
  365. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  366. package/templates/infra/compose.yaml.hbs +33 -16
  367. package/templates/infra/servers.json.hbs +3 -1
  368. package/templates/python/docker-compose.hbs +16 -0
  369. package/templates/typescript/docker-compose.hbs +16 -0
  370. package/lib/api/external-test.api.js +0 -111
  371. package/lib/schema/env-config.yaml +0 -60
@@ -11,6 +11,7 @@
11
11
  const fs = require('fs');
12
12
  const path = require('path');
13
13
  const { loadConfigFile } = require('../utils/config-format');
14
+ const { urlTokenToKeyVaultSecretName } = require('./deploy-manifest-azure-kv');
14
15
 
15
16
  /**
16
17
  * Loads application config file (application.yaml, application.json, or legacy path) via converter.
@@ -218,6 +219,9 @@ function determineVariableLocation(value, key) {
218
219
  if (value.startsWith('kv://')) {
219
220
  location = 'keyvault';
220
221
  required = true;
222
+ } else if (value.startsWith('url://')) {
223
+ location = 'keyvault';
224
+ required = true;
221
225
  }
222
226
 
223
227
  // Check if it's a sensitive variable
@@ -229,6 +233,31 @@ function determineVariableLocation(value, key) {
229
233
  return { location, required };
230
234
  }
231
235
 
236
+ /**
237
+ * Maps env.template url:// value(s) to Key Vault secret name(s) for deploy JSON.
238
+ * Comma-separated lists are supported (e.g. CORS ALLOWED_ORIGINS): each segment
239
+ * starting with url:// is mapped; other segments are left unchanged (e.g. http://localhost:*).
240
+ *
241
+ * @param {string} appKey - application.yaml app.key
242
+ * @param {string} value - Full value after KEY= (must start with url:// for caller)
243
+ * @returns {string} Comma-joined secret names / literals
244
+ */
245
+ function mapUrlTemplateValueToKeyVaultNames(appKey, value) {
246
+ const segments = value
247
+ .split(',')
248
+ .map((s) => s.trim())
249
+ .filter((s) => s.length > 0);
250
+ return segments
251
+ .map((seg) => {
252
+ if (seg.startsWith('url://')) {
253
+ const token = seg.slice('url://'.length).trim();
254
+ return urlTokenToKeyVaultSecretName(appKey, token);
255
+ }
256
+ return seg;
257
+ })
258
+ .join(',');
259
+ }
260
+
232
261
  /**
233
262
  * Creates a configuration item from parsed variable
234
263
  * @function createConfigItem
@@ -237,12 +266,27 @@ function determineVariableLocation(value, key) {
237
266
  * @param {string} location - Variable location
238
267
  * @param {boolean} required - Whether variable is required
239
268
  * @param {Map} portalInputMap - Map of portalInput configurations
269
+ * @param {string|null} appKey - application.yaml app.key (required when value uses url://)
240
270
  * @returns {Object} Configuration item
241
271
  */
242
- function createConfigItem(key, value, location, required, portalInputMap) {
272
+ function createConfigItem(key, value, location, required, portalInputMap, appKey) {
273
+ let storedValue = value;
274
+ if (location === 'keyvault') {
275
+ if (value.startsWith('kv://')) {
276
+ storedValue = value.replace('kv://', '');
277
+ } else if (value.startsWith('url://')) {
278
+ if (!appKey) {
279
+ throw new Error(
280
+ `Cannot resolve ${key}=${value}: application app.key is required to map url:// to a Key Vault secret name`
281
+ );
282
+ }
283
+ storedValue = mapUrlTemplateValueToKeyVaultNames(appKey, value);
284
+ }
285
+ }
286
+
243
287
  const configItem = {
244
288
  name: key,
245
- value: value.replace('kv://', ''), // Remove kv:// prefix for KeyVault
289
+ value: storedValue,
246
290
  location,
247
291
  required
248
292
  };
@@ -259,6 +303,10 @@ function parseEnvironmentVariables(envTemplate, variablesConfig = null) {
259
303
  const configuration = [];
260
304
  const lines = envTemplate.split('\n');
261
305
  const portalInputMap = createPortalInputMap(variablesConfig);
306
+ const appKey =
307
+ variablesConfig && variablesConfig.app && variablesConfig.app.key
308
+ ? String(variablesConfig.app.key).trim()
309
+ : null;
262
310
 
263
311
  for (const line of lines) {
264
312
  const parsed = parseEnvironmentVariableLine(line);
@@ -267,7 +315,14 @@ function parseEnvironmentVariables(envTemplate, variablesConfig = null) {
267
315
  }
268
316
 
269
317
  const { location, required } = determineVariableLocation(parsed.value, parsed.key);
270
- const configItem = createConfigItem(parsed.key, parsed.value, location, required, portalInputMap);
318
+ const configItem = createConfigItem(
319
+ parsed.key,
320
+ parsed.value,
321
+ location,
322
+ required,
323
+ portalInputMap,
324
+ appKey
325
+ );
271
326
  configuration.push(configItem);
272
327
  }
273
328
 
@@ -22,6 +22,7 @@ const { generateControllerManifest } = require('./external-controller-manifest')
22
22
  const { resolveVersionForApp } = require('../utils/image-version');
23
23
  const { getContainerPort } = require('../utils/port-resolver');
24
24
  const { buildEnvVarMap } = require('../utils/env-map');
25
+ const { rewriteFrontDoorHostForAzureDeploy } = require('./deploy-manifest-azure-kv');
25
26
 
26
27
  /**
27
28
  * Generates deployment JSON from application configuration files
@@ -191,6 +192,8 @@ function buildAndValidateDeployment(appName, variables, envTemplate, rbac, optio
191
192
  substituteEnvVarsInDeployment(deployment, envVarMap);
192
193
  }
193
194
 
195
+ rewriteFrontDoorHostForAzureDeploy(deployment);
196
+
194
197
  // Ensure no other ${...} placeholders remain in manifest
195
198
  _validator.validateNoUnresolvedVariablesInDeployment(deployment);
196
199
 
@@ -236,6 +239,7 @@ async function buildDeploymentManifestInMemory(appName, options = {}) {
236
239
  }
237
240
  const envVarMap = await buildEnvVarMap('docker', null, null, { appPort: effectivePort });
238
241
  substituteEnvVarsInDeployment(deployment, envVarMap);
242
+ rewriteFrontDoorHostForAzureDeploy(deployment);
239
243
  _validator.validateNoUnresolvedVariablesInDeployment(deployment);
240
244
  return { deployment, appPath };
241
245
  }
@@ -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
  }
@@ -72,16 +109,39 @@ function generateComposeFile(templatePath, devId, idNum, ports, infraDir, option
72
109
  const template = handlebars.compile(templateContent);
73
110
  const networkName = idNum === 0 ? 'infra-aifabrix-network' : `infra-dev${devId}-aifabrix-network`;
74
111
  const serversJsonPath = path.join(infraDir, 'servers.json');
112
+ const serversJsonBind = toDockerBindMountSource(serversJsonPath);
75
113
  const pgpassPath = path.join(infraDir, 'pgpass');
76
114
  const traefikConfig = typeof options.traefik === 'object'
77
115
  ? options.traefik
78
116
  : buildTraefikConfig(!!options.traefik);
79
- const pgadminConfig = options.pgadmin && typeof options.pgadmin.enabled === 'boolean'
117
+ const pgadminConfigRaw = options.pgadmin && typeof options.pgadmin.enabled === 'boolean'
80
118
  ? options.pgadmin
81
119
  : { enabled: true };
120
+ const pgadminEnabled = !!pgadminConfigRaw.enabled;
121
+ const pgpassBootstrapPath = path.join(infraDir, '.pgpass.bootstrap');
122
+ const pgadminConfig = {
123
+ ...pgadminConfigRaw,
124
+ enabled: pgadminEnabled,
125
+ ...(pgadminEnabled
126
+ ? { pgpassBootstrapBind: toDockerBindMountSource(pgpassBootstrapPath) }
127
+ : {})
128
+ };
82
129
  const redisCommanderConfig = options.redisCommander && typeof options.redisCommander.enabled === 'boolean'
83
130
  ? options.redisCommander
84
131
  : { enabled: true };
132
+ const traefikForCompose = traefikConfig && typeof traefikConfig === 'object'
133
+ ? {
134
+ ...traefikConfig,
135
+ trustForwardedHeaders: !!traefikConfig.trustForwardedHeaders,
136
+ ...(traefikConfig.certFile
137
+ ? { certFile: toDockerBindMountSource(traefikConfig.certFile) }
138
+ : {}),
139
+ ...(traefikConfig.keyFile
140
+ ? { keyFile: toDockerBindMountSource(traefikConfig.keyFile) }
141
+ : {})
142
+ }
143
+ : traefikConfig;
144
+ const initScriptsBind = toDockerBindMountSource(path.join(infraDir, 'init-scripts'));
85
145
  const composeContent = template({
86
146
  devId: devId,
87
147
  postgresPort: ports.postgres,
@@ -92,9 +152,11 @@ function generateComposeFile(templatePath, devId, idNum, ports, infraDir, option
92
152
  traefikHttpsPort: ports.traefikHttps,
93
153
  networkName: networkName,
94
154
  serversJsonPath: serversJsonPath,
155
+ serversJsonBind: serversJsonBind,
95
156
  pgpassPath: pgpassPath,
96
157
  infraDir: infraDir,
97
- traefik: traefikConfig,
158
+ initScriptsBind: initScriptsBind,
159
+ traefik: traefikForCompose,
98
160
  pgadmin: pgadminConfig,
99
161
  redisCommander: redisCommanderConfig
100
162
  });
@@ -106,5 +168,6 @@ function generateComposeFile(templatePath, devId, idNum, ports, infraDir, option
106
168
  module.exports = {
107
169
  buildTraefikConfig,
108
170
  validateTraefikConfig,
109
- generateComposeFile
171
+ generateComposeFile,
172
+ toDockerBindMountSource
110
173
  };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @fileoverview Docker / Compose availability check and user-facing failure text for infra helpers.
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const dockerUtils = require('../utils/docker');
10
+ const { ensureDevCertsIfNeededForRemoteDocker } = require('../utils/ensure-dev-certs-for-remote-docker');
11
+
12
+ /**
13
+ * User-facing error when Docker/Compose checks fail (tailored by underlying message).
14
+ * @param {string} detail - Error message from ensureDockerAndCompose / Docker CLI
15
+ * @returns {string}
16
+ */
17
+ function formatDockerInfrastructureFailure(detail) {
18
+ const cause = (detail || '').trim() || 'unknown error';
19
+
20
+ if (/Docker Compose is not available/i.test(cause)) {
21
+ return (
22
+ 'Cannot use Docker for infrastructure: Docker Compose check failed (see Cause below).\n\n' +
23
+ `Cause: ${cause}\n\n` +
24
+ '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. ' +
25
+ '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). ' +
26
+ 'Or set AIFABRIX_COMPOSE_CMD. Run `aifabrix doctor` for diagnostics.'
27
+ );
28
+ }
29
+
30
+ if (/AIFABRIX_COMPOSE_CMD/i.test(cause) && /is set but failed/i.test(cause)) {
31
+ return (
32
+ 'Cannot use Docker for infrastructure: AIFABRIX_COMPOSE_CMD failed.\n\n' +
33
+ `Cause: ${cause}\n\n` +
34
+ 'Unset or fix AIFABRIX_COMPOSE_CMD, or install a working Compose. Run `aifabrix doctor` for diagnostics.'
35
+ );
36
+ }
37
+
38
+ return (
39
+ 'Cannot use Docker for infrastructure (Docker CLI missing, Compose missing, or remote Docker misconfigured).\n\n' +
40
+ `Cause: ${cause}\n\n` +
41
+ 'Install Docker Engine and Compose on this machine (or set AIFABRIX_COMPOSE_CMD). ' +
42
+ 'If you use docker-endpoint in dev config: install cert.pem, key.pem, and ca.pem for full TLS verify; use `aifabrix dev pin` / ' +
43
+ '`dev init --pin` as needed; or enable TLS skip-verify (config or AIFABRIX_DOCKER_TLS_SKIP_VERIFY) when appropriate. ' +
44
+ 'Run `aifabrix doctor` for diagnostics.'
45
+ );
46
+ }
47
+
48
+ /**
49
+ * Check Docker availability (local daemon or remote via docker-endpoint + TLS).
50
+ * @async
51
+ * @returns {Promise<void>}
52
+ * @throws {Error} If Docker/Compose cannot be used (includes underlying cause)
53
+ */
54
+ async function checkDockerAvailability() {
55
+ await ensureDevCertsIfNeededForRemoteDocker();
56
+ try {
57
+ await dockerUtils.ensureDockerAndCompose();
58
+ } catch (error) {
59
+ const detail = (error && error.message) || String(error);
60
+ throw new Error(formatDockerInfrastructureFailure(detail));
61
+ }
62
+ }
63
+
64
+ module.exports = {
65
+ formatDockerInfrastructureFailure,
66
+ checkDockerAvailability
67
+ };