@aifabrix/builder 2.42.1 → 2.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/README.md +2 -2
  3. package/anchor-docs/README.md +10 -0
  4. package/anchor-docs/_TEMPLATE +24 -0
  5. package/bin/aifabrix.js +13 -4
  6. package/integration/hubspot-test/README.md +157 -0
  7. package/integration/{hubspot → hubspot-test}/application.json +6 -6
  8. package/integration/{hubspot → hubspot-test}/create-hubspot.js +10 -10
  9. package/integration/hubspot-test/env.template +4 -0
  10. package/integration/hubspot-test/hubspot-test-datasource-company.json +138 -0
  11. package/integration/hubspot-test/hubspot-test-datasource-contact.json +146 -0
  12. package/integration/hubspot-test/hubspot-test-datasource-deal.json +146 -0
  13. package/integration/hubspot-test/hubspot-test-datasource-users.json +76 -0
  14. package/integration/{hubspot/hubspot-deploy.json → hubspot-test/hubspot-test-deploy.json} +201 -24
  15. package/integration/{hubspot/hubspot-system.json → hubspot-test/hubspot-test-system.json} +8 -7
  16. package/integration/hubspot-test/rbac.json +166 -0
  17. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-credential-real.yaml +3 -3
  18. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-env-vars.yaml +2 -2
  19. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-add-datasource.yaml +1 -1
  20. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-create.yaml +1 -1
  21. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-select.yaml +1 -1
  22. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-known-platform.yaml +1 -1
  23. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
  24. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-mode.yaml +1 -1
  25. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-file.yaml +1 -1
  26. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-url.yaml +1 -1
  27. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-source.yaml +1 -1
  28. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-array-test.yaml +1 -1
  29. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
  30. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
  31. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-test.yaml +1 -1
  32. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-test.yaml +1 -1
  33. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +1 -1
  34. package/integration/{hubspot → hubspot-test}/test-dataplane-down-tests.js +1 -7
  35. package/integration/{hubspot → hubspot-test}/test-dataplane-down.js +3 -3
  36. package/integration/{hubspot → hubspot-test}/test.js +137 -102
  37. package/integration/{hubspot → hubspot-test}/wizard-hubspot-e2e.yaml +2 -2
  38. package/integration/{hubspot → hubspot-test}/wizard-hubspot-platform.yaml +1 -1
  39. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  40. package/integration/roundtrip-test-local/README.md +144 -0
  41. package/integration/roundtrip-test-local/application.yaml +13 -0
  42. package/integration/roundtrip-test-local/env.template +15 -0
  43. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  44. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  45. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  46. package/integration/roundtrip-test-local2/README.md +144 -0
  47. package/integration/roundtrip-test-local2/application.yaml +13 -0
  48. package/integration/roundtrip-test-local2/env.template +15 -0
  49. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  50. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  51. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  52. package/integration/test/wizard.yaml +8 -0
  53. package/jest.config.default.js +10 -0
  54. package/jest.config.integration.fixtures.js +22 -0
  55. package/jest.config.integration.js +21 -18
  56. package/jest.config.isolated.js +10 -0
  57. package/jest.projects.js +288 -0
  58. package/lib/api/datasources-core.api.js +3 -3
  59. package/lib/api/dev-mtls-request.js +110 -0
  60. package/lib/api/dev-server-https.js +145 -0
  61. package/lib/api/dev.api.js +133 -144
  62. package/lib/api/index.js +0 -1
  63. package/lib/api/pipeline.api.js +67 -20
  64. package/lib/api/service-users.api.js +111 -2
  65. package/lib/api/types/dev.types.js +4 -3
  66. package/lib/api/types/pipeline.types.js +8 -5
  67. package/lib/api/types/service-users.types.js +41 -0
  68. package/lib/api/types/validation-run.types.js +56 -0
  69. package/lib/api/validation-run.api.js +99 -0
  70. package/lib/api/validation-runner.js +99 -0
  71. package/lib/app/config.js +1 -1
  72. package/lib/app/deploy-status-display.js +2 -2
  73. package/lib/app/deploy.js +7 -6
  74. package/lib/app/display.js +2 -1
  75. package/lib/app/dockerfile.js +3 -2
  76. package/lib/app/down.js +2 -1
  77. package/lib/app/helpers.js +6 -5
  78. package/lib/app/index.js +27 -8
  79. package/lib/app/list.js +7 -6
  80. package/lib/app/push.js +4 -3
  81. package/lib/app/register.js +19 -8
  82. package/lib/app/rotate-secret.js +17 -13
  83. package/lib/app/run-container-start.js +184 -0
  84. package/lib/app/run-docker-fallback.js +108 -0
  85. package/lib/app/run-env-compose.js +30 -42
  86. package/lib/app/run-helpers.js +49 -126
  87. package/lib/app/run-infra-requirements.js +30 -0
  88. package/lib/app/run-resolve-image.js +21 -0
  89. package/lib/app/run.js +74 -21
  90. package/lib/app/show-display.js +1 -1
  91. package/lib/app/show.js +1 -1
  92. package/lib/build/index.js +13 -10
  93. package/lib/cli/index.js +2 -0
  94. package/lib/cli/setup-app.help.js +67 -0
  95. package/lib/cli/setup-app.js +59 -123
  96. package/lib/cli/setup-app.test-commands.js +179 -0
  97. package/lib/cli/setup-auth.js +36 -14
  98. package/lib/cli/setup-credential-deployment.js +22 -8
  99. package/lib/cli/setup-dev-path-commands.js +124 -0
  100. package/lib/cli/setup-dev.js +190 -103
  101. package/lib/cli/setup-environment.js +11 -20
  102. package/lib/cli/setup-external-system.js +62 -22
  103. package/lib/cli/setup-infra.js +139 -47
  104. package/lib/cli/setup-parameters.js +32 -0
  105. package/lib/cli/setup-secrets.js +147 -10
  106. package/lib/cli/setup-service-user.js +146 -20
  107. package/lib/cli/setup-utility.js +47 -19
  108. package/lib/commands/app-down.js +5 -7
  109. package/lib/commands/app-install.js +14 -7
  110. package/lib/commands/app-logs.js +13 -10
  111. package/lib/commands/app-shell.js +4 -1
  112. package/lib/commands/app-test.js +25 -19
  113. package/lib/commands/app.js +22 -10
  114. package/lib/commands/auth-config.js +10 -14
  115. package/lib/commands/auth-status.js +4 -3
  116. package/lib/commands/credential-env.js +4 -3
  117. package/lib/commands/credential-list.js +5 -4
  118. package/lib/commands/credential-push.js +4 -3
  119. package/lib/commands/datasource-unified-test-cli.js +495 -0
  120. package/lib/commands/datasource-unified-test-cli.options.js +149 -0
  121. package/lib/commands/datasource-validation-cli.js +129 -0
  122. package/lib/commands/datasource.js +123 -71
  123. package/lib/commands/deployment-list.js +6 -5
  124. package/lib/commands/dev-cli-handlers.js +122 -18
  125. package/lib/commands/dev-down.js +4 -3
  126. package/lib/commands/dev-init.js +231 -116
  127. package/lib/commands/dev-show-display.js +473 -0
  128. package/lib/commands/login-credentials.js +3 -2
  129. package/lib/commands/login-device.js +4 -3
  130. package/lib/commands/login.js +5 -4
  131. package/lib/commands/logout.js +8 -7
  132. package/lib/commands/parameters-validate.js +54 -0
  133. package/lib/commands/repair-datasource.js +314 -68
  134. package/lib/commands/repair-env-template.js +16 -10
  135. package/lib/commands/repair-rbac.js +25 -19
  136. package/lib/commands/repair.js +116 -32
  137. package/lib/commands/secrets-list.js +23 -12
  138. package/lib/commands/secrets-remove-all.js +220 -0
  139. package/lib/commands/secrets-remove.js +22 -13
  140. package/lib/commands/secrets-set.js +21 -12
  141. package/lib/commands/secrets-validate.js +20 -7
  142. package/lib/commands/secure.js +10 -9
  143. package/lib/commands/service-user.js +243 -13
  144. package/lib/commands/test-e2e-external.js +27 -1
  145. package/lib/commands/up-common.js +28 -2
  146. package/lib/commands/up-dataplane.js +31 -18
  147. package/lib/commands/up-miso.js +19 -29
  148. package/lib/commands/upload.js +138 -39
  149. package/lib/commands/wizard-core-helpers.js +1 -1
  150. package/lib/commands/wizard-dataplane.js +4 -3
  151. package/lib/commands/wizard-helpers.js +3 -3
  152. package/lib/commands/wizard.js +2 -2
  153. package/lib/core/admin-secrets.js +16 -5
  154. package/lib/core/audit-logger.js +12 -4
  155. package/lib/core/config-attach-extensions.js +46 -0
  156. package/lib/core/config-runtime-paths.js +29 -0
  157. package/lib/core/config.js +59 -58
  158. package/lib/core/diff.js +3 -2
  159. package/lib/core/ensure-encryption-key.js +2 -4
  160. package/lib/core/secrets-ensure-infra.js +77 -0
  161. package/lib/core/secrets-ensure.js +120 -64
  162. package/lib/core/secrets-env-write.js +35 -7
  163. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  164. package/lib/core/secrets.js +228 -42
  165. package/lib/core/templates-env.js +4 -3
  166. package/lib/core/templates.js +1 -1
  167. package/lib/datasource/abac-validator.js +148 -0
  168. package/lib/datasource/deploy.js +75 -53
  169. package/lib/datasource/field-reference-validator.js +77 -36
  170. package/lib/datasource/integration-context.js +63 -0
  171. package/lib/datasource/list.js +8 -7
  172. package/lib/datasource/log-viewer.js +252 -0
  173. package/lib/datasource/resolve-app.js +109 -0
  174. package/lib/datasource/test-e2e.js +95 -155
  175. package/lib/datasource/test-integration.js +121 -109
  176. package/lib/datasource/unified-validation-run-body.js +65 -0
  177. package/lib/datasource/unified-validation-run-post.js +23 -0
  178. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  179. package/lib/datasource/unified-validation-run.js +92 -0
  180. package/lib/datasource/validate.js +162 -15
  181. package/lib/deployment/deployer.js +4 -3
  182. package/lib/deployment/environment.js +7 -6
  183. package/lib/deployment/push.js +17 -8
  184. package/lib/external-system/delete.js +4 -3
  185. package/lib/external-system/deploy.js +131 -53
  186. package/lib/external-system/download-helpers.js +1 -1
  187. package/lib/external-system/download.js +7 -6
  188. package/lib/external-system/generator.js +104 -14
  189. package/lib/external-system/integration-test-dispatch.js +26 -0
  190. package/lib/external-system/test-execution.js +5 -1
  191. package/lib/external-system/test-helpers.js +0 -4
  192. package/lib/external-system/test-system-level-helpers.js +110 -0
  193. package/lib/external-system/test-system-level.js +83 -44
  194. package/lib/external-system/test.js +59 -8
  195. package/lib/generator/builders.js +23 -11
  196. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  197. package/lib/generator/external-controller-manifest.js +3 -3
  198. package/lib/generator/external.js +23 -11
  199. package/lib/generator/helpers.js +71 -12
  200. package/lib/generator/index.js +8 -4
  201. package/lib/generator/split-readme.js +12 -7
  202. package/lib/generator/split-variables.js +2 -1
  203. package/lib/generator/split.js +46 -11
  204. package/lib/generator/wizard-readme.js +3 -3
  205. package/lib/generator/wizard.js +16 -13
  206. package/lib/infrastructure/compose.js +60 -6
  207. package/lib/infrastructure/helpers.js +238 -51
  208. package/lib/infrastructure/index.js +64 -37
  209. package/lib/infrastructure/services.js +21 -15
  210. package/lib/internal/fs-real-sync.js +104 -0
  211. package/lib/internal/node-fs.js +98 -0
  212. package/lib/parameters/database-secret-values.js +173 -0
  213. package/lib/parameters/infra-kv-discovery.js +121 -0
  214. package/lib/parameters/infra-parameter-catalog.js +458 -0
  215. package/lib/parameters/infra-parameter-validate.js +64 -0
  216. package/lib/schema/application-schema.json +37 -17
  217. package/lib/schema/datasource-test-run.schema.json +493 -0
  218. package/lib/schema/deployment-rules.yaml +102 -63
  219. package/lib/schema/external-datasource.schema.json +1201 -433
  220. package/lib/schema/external-system.schema.json +181 -5
  221. package/lib/schema/flag-map-validation-run.json +31 -0
  222. package/lib/schema/infra-parameter.schema.json +106 -0
  223. package/lib/schema/infra.parameter.yaml +421 -0
  224. package/lib/schema/type/credential-auth-templates.json +40 -0
  225. package/lib/schema/type/document-storage.json +213 -0
  226. package/lib/schema/type/message-service.json +123 -0
  227. package/lib/schema/type/vector-store.json +88 -0
  228. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  229. package/lib/utils/api-error-handler.js +2 -2
  230. package/lib/utils/api.js +49 -14
  231. package/lib/utils/app-config-resolver.js +23 -1
  232. package/lib/utils/app-register-api.js +3 -2
  233. package/lib/utils/app-register-auth.js +1 -1
  234. package/lib/utils/app-register-config.js +4 -4
  235. package/lib/utils/app-register-display.js +3 -2
  236. package/lib/utils/app-register-validator.js +3 -2
  237. package/lib/utils/app-run-containers.js +26 -22
  238. package/lib/utils/app-scoped-config.js +31 -0
  239. package/lib/utils/app-service-env-from-builder.js +164 -0
  240. package/lib/utils/build-copy.js +1 -1
  241. package/lib/utils/build-helpers.js +20 -20
  242. package/lib/utils/build-resolve-image.js +165 -0
  243. package/lib/utils/cli-layout-chalk.js +8 -0
  244. package/lib/utils/cli-test-layout-chalk.js +267 -0
  245. package/lib/utils/cli-utils.js +88 -11
  246. package/lib/utils/compose-db-passwords.js +138 -0
  247. package/lib/utils/compose-generate-docker-compose.js +216 -0
  248. package/lib/utils/compose-generator.js +197 -291
  249. package/lib/utils/compose-miso-env.js +18 -0
  250. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  251. package/lib/utils/config-paths.js +209 -6
  252. package/lib/utils/config-scoped-resources-preference.js +41 -0
  253. package/lib/utils/controller-deployment-outcome.js +68 -0
  254. package/lib/utils/credential-display.js +2 -2
  255. package/lib/utils/credential-secrets-env.js +16 -1
  256. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  257. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  258. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  259. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  260. package/lib/utils/datasource-test-run-display.js +442 -0
  261. package/lib/utils/datasource-test-run-exit.js +58 -0
  262. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  263. package/lib/utils/datasource-test-run-report-version.js +51 -0
  264. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  265. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  266. package/lib/utils/datasource-validation-watch.js +266 -0
  267. package/lib/utils/declarative-url-ports.js +47 -0
  268. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  269. package/lib/utils/dev-ca-install.js +185 -23
  270. package/lib/utils/dev-cert-helper.js +266 -17
  271. package/lib/utils/dev-hosts-helper.js +307 -0
  272. package/lib/utils/dev-init-cert-hints.js +37 -0
  273. package/lib/utils/dev-init-health-messages.js +52 -0
  274. package/lib/utils/dev-init-resolve.js +86 -0
  275. package/lib/utils/dev-init-ssh-merge.js +65 -0
  276. package/lib/utils/dev-ssh-config-helper.js +196 -0
  277. package/lib/utils/dev-user-groups.js +93 -0
  278. package/lib/utils/docker-build.js +42 -17
  279. package/lib/utils/docker-exec.js +28 -0
  280. package/lib/utils/docker-manifest-public-port.js +116 -0
  281. package/lib/utils/docker-not-running-hint.js +52 -0
  282. package/lib/utils/docker.js +98 -11
  283. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  284. package/lib/utils/env-config-loader.js +10 -91
  285. package/lib/utils/env-copy.js +19 -10
  286. package/lib/utils/env-map.js +42 -11
  287. package/lib/utils/env-template.js +2 -2
  288. package/lib/utils/environment-scoped-resources.js +144 -0
  289. package/lib/utils/error-formatter.js +125 -9
  290. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  291. package/lib/utils/error-formatters/network-errors.js +2 -1
  292. package/lib/utils/error-formatters/permission-errors.js +2 -1
  293. package/lib/utils/error-formatters/validation-errors.js +2 -1
  294. package/lib/utils/external-env-template.js +180 -0
  295. package/lib/utils/external-readme.js +8 -1
  296. package/lib/utils/external-system-display.js +277 -136
  297. package/lib/utils/external-system-local-test-tty.js +389 -0
  298. package/lib/utils/external-system-readiness-core.js +377 -0
  299. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  300. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  301. package/lib/utils/external-system-readiness-display.js +186 -0
  302. package/lib/utils/external-system-test-helpers.js +24 -6
  303. package/lib/utils/external-system-validators.js +32 -14
  304. package/lib/utils/health-check-url.js +119 -0
  305. package/lib/utils/health-check.js +59 -25
  306. package/lib/utils/help-builder.js +14 -13
  307. package/lib/utils/image-version.js +4 -8
  308. package/lib/utils/infra-containers.js +4 -7
  309. package/lib/utils/infra-env-defaults.js +162 -0
  310. package/lib/utils/infra-status-display.js +167 -0
  311. package/lib/utils/infra-status.js +16 -8
  312. package/lib/utils/local-secrets.js +29 -7
  313. package/lib/utils/paths.js +136 -48
  314. package/lib/utils/port-resolver.js +10 -23
  315. package/lib/utils/redis-env-scope.js +62 -0
  316. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  317. package/lib/utils/remote-builder-validation.js +99 -0
  318. package/lib/utils/remote-dev-auth.js +117 -21
  319. package/lib/utils/remote-docker-env.js +67 -15
  320. package/lib/utils/remote-secrets-loader.js +13 -4
  321. package/lib/utils/resolve-docker-image-ref.js +124 -0
  322. package/lib/utils/schema-loader.js +22 -9
  323. package/lib/utils/secrets-bash-kv.js +25 -0
  324. package/lib/utils/secrets-generator.js +171 -51
  325. package/lib/utils/secrets-helpers.js +70 -59
  326. package/lib/utils/secrets-kv-scope.js +60 -0
  327. package/lib/utils/secrets-utils.js +35 -37
  328. package/lib/utils/secrets-validation.js +3 -1
  329. package/lib/utils/secrets-yaml-preserve.js +109 -0
  330. package/lib/utils/secure-file-permissions.js +91 -0
  331. package/lib/utils/ssh-key-helper.js +4 -2
  332. package/lib/utils/template-helpers.js +2 -2
  333. package/lib/utils/test-log-writer.js +3 -3
  334. package/lib/utils/token-manager.js +37 -5
  335. package/lib/utils/url-declarative-public-base.js +188 -0
  336. package/lib/utils/url-declarative-resolve-build.js +493 -0
  337. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  338. package/lib/utils/url-declarative-resolve.js +220 -0
  339. package/lib/utils/url-declarative-token-parse.js +74 -0
  340. package/lib/utils/url-declarative-url-flags.js +50 -0
  341. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  342. package/lib/utils/url-public-path-prefix.js +34 -0
  343. package/lib/utils/urls-local-registry.js +220 -0
  344. package/lib/utils/validation-report-tty-kit.js +77 -0
  345. package/lib/utils/validation-run-poll.js +89 -0
  346. package/lib/utils/validation-run-post-retry.js +73 -0
  347. package/lib/utils/validation-run-request.js +98 -0
  348. package/lib/utils/variable-transformer.js +21 -4
  349. package/lib/utils/yaml-preserve.js +78 -1
  350. package/lib/validation/datasource-warnings.js +56 -0
  351. package/lib/validation/env-template-auth.js +50 -2
  352. package/lib/validation/external-manifest-validator.js +35 -7
  353. package/lib/validation/validate-display.js +37 -31
  354. package/lib/validation/validate.js +9 -10
  355. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  356. package/lib/validation/validator.js +32 -78
  357. package/lib/validation/wizard-config-validator.js +2 -1
  358. package/package.json +11 -3
  359. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  360. package/scripts/diagnose-cli.js +150 -0
  361. package/scripts/install-local.js +304 -55
  362. package/templates/README.md +15 -2
  363. package/templates/applications/dataplane/application.yaml +52 -2
  364. package/templates/applications/dataplane/env.template +80 -18
  365. package/templates/applications/dataplane/rbac.yaml +8 -0
  366. package/templates/applications/keycloak/application.yaml +9 -1
  367. package/templates/applications/keycloak/env.template +15 -6
  368. package/templates/applications/miso-controller/application.yaml +10 -2
  369. package/templates/applications/miso-controller/env.template +55 -14
  370. package/templates/applications/miso-controller/rbac.yaml +5 -0
  371. package/templates/external-system/README.md.hbs +20 -7
  372. package/templates/external-system/deploy.js.hbs +5 -5
  373. package/templates/external-system/env.template.hbs +22 -0
  374. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  375. package/templates/infra/compose.yaml.hbs +20 -4
  376. package/templates/python/docker-compose.hbs +16 -0
  377. package/templates/typescript/docker-compose.hbs +16 -0
  378. package/integration/hubspot/README.md +0 -102
  379. package/integration/hubspot/env.template +0 -4
  380. package/integration/hubspot/hubspot-datasource-company.json +0 -541
  381. package/integration/hubspot/hubspot-datasource-contact.json +0 -639
  382. package/integration/hubspot/hubspot-datasource-deal.json +0 -588
  383. package/integration/hubspot/hubspot-datasource-users.json +0 -116
  384. package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  385. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  386. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  387. package/lib/api/external-test.api.js +0 -111
  388. package/lib/schema/env-config.yaml +0 -43
  389. /package/integration/{hubspot → hubspot-test}/companies.json +0 -0
  390. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-app-name.yaml +0 -0
  391. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-missing-app.yaml +0 -0
  392. /package/integration/{hubspot → hubspot-test}/test-dataplane-down-helpers.js +0 -0
@@ -9,8 +9,9 @@
9
9
  */
10
10
 
11
11
  const fs = require('fs');
12
- const yaml = require('js-yaml');
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.
@@ -37,21 +38,25 @@ function loadEnvTemplate(templatePath) {
37
38
  }
38
39
 
39
40
  /**
40
- * Loads rbac.yaml file if it exists
41
- * @param {string} rbacPath - Path to rbac.yaml
42
- * @returns {Object|null} Parsed RBAC configuration or null
43
- * @throws {Error} If file exists but has invalid YAML
41
+ * Loads RBAC config file (rbac.yaml, rbac.yml, or rbac.json) if it exists.
42
+ * Uses loadConfigFile so format is inferred from extension.
43
+ *
44
+ * @param {string} rbacPath - Path to RBAC file (e.g. from resolveRbacPath)
45
+ * @returns {Object|null} Parsed RBAC configuration or null if path is falsy or file does not exist
46
+ * @throws {Error} If file exists but has invalid syntax (message references actual filename, e.g. rbac.json)
44
47
  */
45
48
  function loadRbac(rbacPath) {
49
+ if (!rbacPath || typeof rbacPath !== 'string') {
50
+ return null;
51
+ }
46
52
  if (!fs.existsSync(rbacPath)) {
47
53
  return null;
48
54
  }
49
-
50
- const rbacContent = fs.readFileSync(rbacPath, 'utf8');
51
55
  try {
52
- return yaml.load(rbacContent);
56
+ return loadConfigFile(rbacPath);
53
57
  } catch (error) {
54
- throw new Error(`Invalid YAML syntax in rbac.yaml: ${error.message}`);
58
+ const basename = path.basename(rbacPath);
59
+ throw new Error(`Invalid syntax in ${basename}: ${error.message}`);
55
60
  }
56
61
  }
57
62
 
@@ -214,6 +219,9 @@ function determineVariableLocation(value, key) {
214
219
  if (value.startsWith('kv://')) {
215
220
  location = 'keyvault';
216
221
  required = true;
222
+ } else if (value.startsWith('url://')) {
223
+ location = 'keyvault';
224
+ required = true;
217
225
  }
218
226
 
219
227
  // Check if it's a sensitive variable
@@ -225,6 +233,31 @@ function determineVariableLocation(value, key) {
225
233
  return { location, required };
226
234
  }
227
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
+
228
261
  /**
229
262
  * Creates a configuration item from parsed variable
230
263
  * @function createConfigItem
@@ -233,12 +266,27 @@ function determineVariableLocation(value, key) {
233
266
  * @param {string} location - Variable location
234
267
  * @param {boolean} required - Whether variable is required
235
268
  * @param {Map} portalInputMap - Map of portalInput configurations
269
+ * @param {string|null} appKey - application.yaml app.key (required when value uses url://)
236
270
  * @returns {Object} Configuration item
237
271
  */
238
- 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
+
239
287
  const configItem = {
240
288
  name: key,
241
- value: value.replace('kv://', ''), // Remove kv:// prefix for KeyVault
289
+ value: storedValue,
242
290
  location,
243
291
  required
244
292
  };
@@ -255,6 +303,10 @@ function parseEnvironmentVariables(envTemplate, variablesConfig = null) {
255
303
  const configuration = [];
256
304
  const lines = envTemplate.split('\n');
257
305
  const portalInputMap = createPortalInputMap(variablesConfig);
306
+ const appKey =
307
+ variablesConfig && variablesConfig.app && variablesConfig.app.key
308
+ ? String(variablesConfig.app.key).trim()
309
+ : null;
258
310
 
259
311
  for (const line of lines) {
260
312
  const parsed = parseEnvironmentVariableLine(line);
@@ -263,7 +315,14 @@ function parseEnvironmentVariables(envTemplate, variablesConfig = null) {
263
315
  }
264
316
 
265
317
  const { location, required } = determineVariableLocation(parsed.value, parsed.key);
266
- 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
+ );
267
326
  configuration.push(configItem);
268
327
  }
269
328
 
@@ -13,7 +13,7 @@ const fs = require('fs');
13
13
  const path = require('path');
14
14
  const _validator = require('../validation/validator');
15
15
  const builders = require('./builders');
16
- const { detectAppType, getDeployJsonPath, resolveApplicationConfigPath } = require('../utils/paths');
16
+ const { detectAppType, getDeployJsonPath, resolveApplicationConfigPath, resolveRbacPath } = require('../utils/paths');
17
17
  const { logOfflinePathWhenType } = require('../utils/cli-utils');
18
18
  const splitFunctions = require('./split');
19
19
  const { loadVariables, loadEnvTemplate, loadRbac, parseEnvironmentVariables } = require('./helpers');
@@ -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
@@ -39,7 +40,7 @@ const { buildEnvVarMap } = require('../utils/env-map');
39
40
  *
40
41
  * @example
41
42
  * const jsonPath = await generateDeployJson('myapp');
42
- * // Returns: './builder/myapp/myapp-deploy.json' or './integration/hubspot/application-schema.json'
43
+ * // Returns: './builder/myapp/myapp-deploy.json' or './integration/hubspot-test/application-schema.json'
43
44
  */
44
45
  /**
45
46
  * Loads configuration files for deployment generation
@@ -51,12 +52,12 @@ const { buildEnvVarMap } = require('../utils/env-map');
51
52
  function loadDeploymentConfigFiles(appPath, appType, appName) {
52
53
  const variablesPath = resolveApplicationConfigPath(appPath);
53
54
  const templatePath = path.join(appPath, 'env.template');
54
- const rbacPath = path.join(appPath, 'rbac.yaml');
55
+ const rbacPath = resolveRbacPath(appPath);
55
56
  const jsonPath = getDeployJsonPath(appName, appType, true); // Use new naming
56
57
 
57
58
  const { parsed: variables } = loadVariables(variablesPath);
58
59
  const envTemplate = loadEnvTemplate(templatePath);
59
- const rbac = loadRbac(rbacPath);
60
+ const rbac = rbacPath ? loadRbac(rbacPath) : null;
60
61
 
61
62
  return { variables, envTemplate, rbac, jsonPath };
62
63
  }
@@ -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);
@@ -14,6 +14,7 @@ const yaml = require('js-yaml');
14
14
  const { parseImageReference } = require('./parse-image');
15
15
  const { generateReadmeFromDeployJson } = require('./split-readme');
16
16
  const { extractVariablesYaml, getExternalDatasourceFileName } = require('./split-variables');
17
+ const { generateExternalEnvTemplateContent } = require('../utils/external-env-template');
17
18
 
18
19
  /**
19
20
  * Converts configuration array back to env.template format
@@ -207,24 +208,49 @@ function mergeEnvTemplateWithExisting(existingContent, expectedByKey) {
207
208
  return updatedLines.join('\n') + (updatedLines.length > 0 ? '\n' : '');
208
209
  }
209
210
 
211
+ /**
212
+ * Builds key -> line map from env.template content (for merge when using external system template).
213
+ * @param {string} content - Full env.template content
214
+ * @returns {Map<string, string>} Key to full KEY=value line
215
+ */
216
+ function buildExpectedByKeyFromEnvContent(content) {
217
+ const expectedByKey = new Map();
218
+ if (!content || typeof content !== 'string') return expectedByKey;
219
+ const lines = content.split(/\r?\n/);
220
+ for (const line of lines) {
221
+ const trimmed = line.trim();
222
+ if (!trimmed || trimmed.startsWith('#')) continue;
223
+ const eq = trimmed.indexOf('=');
224
+ if (eq > 0) {
225
+ const key = trimmed.substring(0, eq).trim();
226
+ expectedByKey.set(key, trimmed);
227
+ }
228
+ }
229
+ return expectedByKey;
230
+ }
231
+
210
232
  /**
211
233
  * Writes env.template (merge or overwrite).
212
234
  * @param {string} outputDir - Output directory
213
235
  * @param {string} envTemplate - Default env.template content
214
- * @param {Object} options - Options (mergeEnvTemplate, configuration)
236
+ * @param {Object} options - Options (mergeEnvTemplate, configuration, expectedByKey for external)
215
237
  * @returns {Promise<string>} Path to env.template
216
238
  */
217
239
  async function writeEnvTemplateToDir(outputDir, envTemplate, options) {
218
240
  const envTemplatePath = path.join(outputDir, 'env.template');
219
241
  const fsSync = require('fs');
220
- if (options.mergeEnvTemplate && options.configuration && fsSync.existsSync(envTemplatePath)) {
221
- const expectedByKey = buildEnvTemplateExpectedByKey(options.configuration);
222
- const existingContent = await fs.readFile(envTemplatePath, 'utf8');
223
- const merged = mergeEnvTemplateWithExisting(existingContent, expectedByKey);
224
- await writeComponentFile(envTemplatePath, merged);
225
- } else {
226
- await writeComponentFile(envTemplatePath, envTemplate);
242
+ const useMerge = options.mergeEnvTemplate && fsSync.existsSync(envTemplatePath);
243
+ if (useMerge) {
244
+ const expectedByKey = options.expectedByKey ||
245
+ (options.configuration ? buildEnvTemplateExpectedByKey(options.configuration) : new Map());
246
+ if (expectedByKey.size > 0) {
247
+ const existingContent = await fs.readFile(envTemplatePath, 'utf8');
248
+ const merged = mergeEnvTemplateWithExisting(existingContent, expectedByKey);
249
+ await writeComponentFile(envTemplatePath, merged);
250
+ return envTemplatePath;
251
+ }
227
252
  }
253
+ await writeComponentFile(envTemplatePath, envTemplate);
228
254
  return envTemplatePath;
229
255
  }
230
256
 
@@ -401,12 +427,21 @@ async function splitDeployJson(deployJsonPath, outputDir = null, splitOptions =
401
427
  normalizeDeploymentForSplit(deployment);
402
428
 
403
429
  const configArray = deployment.configuration || [];
404
- const envTemplate = extractEnvTemplate(configArray);
430
+ let envTemplate;
431
+ const writeOptions = buildSplitWriteOptions(splitOptions, configArray);
432
+ if (deployment.system && typeof deployment.system === 'object') {
433
+ envTemplate = generateExternalEnvTemplateContent(deployment.system);
434
+ if (writeOptions.mergeEnvTemplate) {
435
+ writeOptions.expectedByKey = buildExpectedByKeyFromEnvContent(envTemplate);
436
+ }
437
+ } else {
438
+ envTemplate = extractEnvTemplate(configArray);
439
+ }
440
+
405
441
  const variables = extractVariablesYaml(deployment);
406
442
  const rbac = extractRbacYaml(deployment);
407
- const readme = generateReadmeFromDeployJson(deployment);
443
+ const readme = generateReadmeFromDeployJson(deployment, { fileExt: '.yaml' });
408
444
 
409
- const writeOptions = buildSplitWriteOptions(splitOptions, configArray);
410
445
  const result = await writeComponentFiles(finalOutputDir, envTemplate, variables, rbac, readme, writeOptions);
411
446
  await applyExternalSystemFilesToResult(finalOutputDir, deployment, result);
412
447
  return 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,14 +8,15 @@
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');
18
18
  const { systemKeyToKvPrefix, securityKeyToVar, isValidKvPath } = require('../utils/credential-secrets-env');
19
+ const { generateExternalEnvTemplateContent } = require('../utils/external-env-template');
19
20
  const { generateReadme } = require('./wizard-readme');
20
21
 
21
22
  /**
@@ -60,7 +61,7 @@ async function writeSystemYamlFile(appPath, finalSystemKey, systemConfig, format
60
61
  const systemFileName = `${finalSystemKey}-system${ext}`;
61
62
  const systemFilePath = path.join(appPath, systemFileName);
62
63
  writeConfigFile(systemFilePath, systemConfig, format === 'json' ? 'json' : 'yaml');
63
- logger.log(chalk.green(`✓ Generated system file: ${systemFileName}`));
64
+ logger.log(formatSuccessLine(`Generated system file: ${systemFileName}`));
64
65
  return systemFilePath;
65
66
  }
66
67
 
@@ -96,7 +97,7 @@ async function writeDatasourceYamlFiles(appPath, finalSystemKey, datasourceConfi
96
97
  const datasourceFilePath = path.join(appPath, datasourceFileName);
97
98
  writeConfigFile(datasourceFilePath, datasourceConfig, fmt);
98
99
  datasourceFileNames.push(datasourceFileName);
99
- logger.log(chalk.green(`✓ Generated datasource file: ${datasourceFileName}`));
100
+ logger.log(formatSuccessLine(`Generated datasource file: ${datasourceFileName}`));
100
101
  }
101
102
  return datasourceFileNames;
102
103
  }
@@ -130,10 +131,12 @@ async function generateConfigFilesForWizard(params) {
130
131
  format: format || 'yaml'
131
132
  });
132
133
 
133
- // Generate env.template with KV_* authentication variables
134
- await generateEnvTemplate(appPath, systemConfig, finalSystemKey);
135
-
134
+ // Generate env.template with Authentication and Configuration sections (Handlebars)
136
135
  const envTemplatePath = path.join(appPath, 'env.template');
136
+ const envTemplateContent = generateExternalEnvTemplateContent(systemConfig);
137
+ await fs.writeFile(envTemplatePath, envTemplateContent, 'utf8');
138
+ logger.log(formatSuccessLine('Generated env.template'));
139
+
137
140
  try {
138
141
  const secretsEnsure = require('../core/secrets-ensure');
139
142
  await secretsEnsure.ensureSecretsFromEnvTemplate(envTemplatePath, { emptyValuesForCredentials: true });
@@ -161,7 +164,7 @@ async function generateConfigFilesForWizard(params) {
161
164
  const deployJson = toDeployJsonShape(manifest);
162
165
  const deployManifestPath = path.join(appPath, `${finalSystemKey}-deploy.json`);
163
166
  await fs.writeFile(deployManifestPath, JSON.stringify(deployJson, null, 2), 'utf8');
164
- logger.log(chalk.green(`✓ Generated deployment manifest: ${finalSystemKey}-deploy.json`));
167
+ logger.log(formatSuccessLine(`Generated deployment manifest: ${finalSystemKey}-deploy.json`));
165
168
 
166
169
  return {
167
170
  variablesPath: configPath,
@@ -290,7 +293,7 @@ async function generateOrUpdateVariablesYaml(params) {
290
293
  const fsSync = require('fs');
291
294
  if (fsSync.existsSync(configPath)) fsSync.unlinkSync(configPath);
292
295
  }
293
- logger.log(chalk.green(`✓ Generated/updated application${ext}`));
296
+ logger.log(formatSuccessLine(`Generated/updated application${ext}`));
294
297
  return targetPath;
295
298
  } catch (error) {
296
299
  throw new Error(`Failed to generate application config: ${error.message}`);
@@ -393,15 +396,15 @@ function addBaseUrlLines(lines, systemConfig) {
393
396
  }
394
397
 
395
398
  /**
396
- * Generate env.template with KV_* authentication variables
399
+ * Generate env.template with KV_* authentication variables (legacy; prefer generateExternalEnvTemplateContent).
397
400
  * @async
398
- * @function generateEnvTemplate
401
+ * @function _generateEnvTemplate
399
402
  * @param {string} appPath - Application directory path
400
403
  * @param {Object} systemConfig - System configuration (must have key for systemKey)
401
404
  * @param {string} [finalSystemKey] - Final system key for KV_ prefix (default: systemConfig.key)
402
405
  * @throws {Error} If generation fails
403
406
  */
404
- async function generateEnvTemplate(appPath, systemConfig, finalSystemKey) {
407
+ async function _generateEnvTemplate(appPath, systemConfig, finalSystemKey) {
405
408
  try {
406
409
  const envTemplatePath = path.join(appPath, 'env.template');
407
410
  const systemKey = finalSystemKey || systemConfig?.key;
@@ -414,7 +417,7 @@ async function generateEnvTemplate(appPath, systemConfig, finalSystemKey) {
414
417
  addBaseUrlLines(lines, systemConfig);
415
418
 
416
419
  await fs.writeFile(envTemplatePath, lines.join('\n'), 'utf8');
417
- logger.log(chalk.green('Generated env.template'));
420
+ logger.log(formatSuccessLine('Generated env.template'));
418
421
  } catch (error) {
419
422
  throw new Error(`Failed to generate env.template: ${error.message}`);
420
423
  }
@@ -437,7 +440,7 @@ async function writeDeployScriptFromTemplate(templateName, outputPath, context)
437
440
  const templatePath = path.join(templatesExternalDir, templateName);
438
441
  const content = Handlebars.compile(await fs.readFile(templatePath, 'utf8'))(context);
439
442
  await fs.writeFile(outputPath, content, 'utf8');
440
- logger.log(chalk.green(`✓ Generated ${path.basename(outputPath)}`));
443
+ logger.log(formatSuccessLine(`Generated ${path.basename(outputPath)}`));
441
444
  }
442
445
 
443
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
  };