@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
@@ -16,7 +16,6 @@ const pathsUtil = require('../utils/paths');
16
16
  const adminSecrets = require('../core/admin-secrets');
17
17
  const secretsEnvWrite = require('../core/secrets-env-write');
18
18
  const { getContainerPort } = require('../utils/port-resolver');
19
- const { getInfraDirName } = require('../infrastructure/helpers');
20
19
 
21
20
  /**
22
21
  * Clean applications directory: remove generated docker-compose.yaml and .env.* files.
@@ -58,12 +57,20 @@ function pgUserName(dbName) {
58
57
  * @param {Object} env - Merged env object (mutated)
59
58
  * @param {Object} appConfig - Application config (requires.databases or databases array)
60
59
  */
61
- function injectDatabaseNamesAndUsers(env, appConfig) {
60
+ function injectDatabaseNamesAndUsers(env, appConfig, scopeOpts = null) {
62
61
  const databases = appConfig?.requires?.databases || appConfig?.databases;
63
62
  if (!Array.isArray(databases) || databases.length === 0) return;
63
+ const prefix =
64
+ scopeOpts &&
65
+ scopeOpts.effectiveEnvironmentScopedResources &&
66
+ scopeOpts.runEnvKey &&
67
+ (scopeOpts.runEnvKey === 'dev' || scopeOpts.runEnvKey === 'tst')
68
+ ? `${String(scopeOpts.runEnvKey).toLowerCase()}-`
69
+ : '';
64
70
  for (let i = 0; i < databases.length; i++) {
65
71
  const db = databases[i];
66
- const name = db?.name || (appConfig?.app?.key || 'app');
72
+ const baseName = db?.name || (appConfig?.app?.key || 'app');
73
+ const name = prefix ? `${prefix}${baseName}` : baseName;
67
74
  env[`DB_${i}_NAME`] = name;
68
75
  env[`DB_${i}_USER`] = pgUserName(name);
69
76
  }
@@ -146,24 +153,6 @@ function buildDbInitOnlyEnv(merged) {
146
153
  return dbInit;
147
154
  }
148
155
 
149
- /**
150
- * Return pgpass paths under infra-dev* directories in aifabrix home (for fallback lookup).
151
- * @param {string} aifabrixDir - Aifabrix home directory
152
- * @returns {string[]} Paths to pgpass files
153
- */
154
- function getInfraDevPgpassPaths(aifabrixDir) {
155
- if (!fsSync.existsSync(aifabrixDir)) return [];
156
- let entries;
157
- try {
158
- entries = fsSync.readdirSync(aifabrixDir).sort();
159
- } catch {
160
- return [];
161
- }
162
- return entries
163
- .filter((name) => name.startsWith('infra-dev'))
164
- .map((name) => path.join(aifabrixDir, name, 'pgpass'));
165
- }
166
-
167
156
  /**
168
157
  * Read first password from a pgpass file (format host:port:db:user:password).
169
158
  * @param {string} pgpassPath - Path to pgpass file
@@ -178,34 +167,31 @@ async function readPasswordFromPgpassFile(pgpassPath) {
178
167
  }
179
168
 
180
169
  /**
181
- * Read POSTGRES_PASSWORD from an existing infra pgpass so db-init uses the same password as running Postgres.
182
- * Tries dev-specific, then default infra, then any infra-dev* dir (e.g. dev 1 run when only infra-dev06 has pgpass).
170
+ * Read POSTGRES_PASSWORD from `pgpass` next to the active infra compose (same as `resolveInfraStatePaths()`).
171
+ * Does not use `getAifabrixHome()` alone, so a stale pgpass under the wrong base directory (e.g. when
172
+ * `aifabrix-home` points at $HOME but compose lives under the config dir) cannot override admin-secrets.
183
173
  * @param {number|string} developerId - Developer ID
184
174
  * @returns {Promise<string|undefined>} Password or undefined
185
175
  */
186
176
  async function readPostgresPasswordFromPgpass(developerId) {
187
- const home = pathsUtil.getAifabrixHome();
188
- const idNum = typeof developerId === 'string' ? parseInt(developerId, 10) : developerId;
189
- const candidates = [path.join(home, getInfraDirName(developerId), 'pgpass')];
190
- if (idNum !== 0) candidates.push(path.join(home, getInfraDirName(0), 'pgpass'));
191
- const extra = getInfraDevPgpassPaths(home).filter((p) => !candidates.includes(p));
192
- candidates.push(...extra);
193
- for (const pgpassPath of candidates) {
194
- if (!fsSync.existsSync(pgpassPath)) continue;
195
- try {
196
- const pwd = await readPasswordFromPgpassFile(pgpassPath);
197
- if (pwd !== undefined) return pwd;
198
- } catch {
199
- // ignore
200
- }
177
+ const { resolveInfraStatePaths } = require('../infrastructure/helpers');
178
+ const { infraDir } = resolveInfraStatePaths(developerId);
179
+ const pgpassPath = path.join(infraDir, 'pgpass');
180
+ if (!fsSync.existsSync(pgpassPath)) {
181
+ return undefined;
182
+ }
183
+ try {
184
+ const pwd = await readPasswordFromPgpassFile(pgpassPath);
185
+ return pwd;
186
+ } catch {
187
+ return undefined;
201
188
  }
202
- return undefined;
203
189
  }
204
190
 
205
191
  /**
206
192
  * Build two run env files: .env.run (app-only, no admin secrets) and .env.run.admin (start-only, for db-init).
207
193
  * Admin password is never set in the app container; .env.run.admin is used only for start and then deleted.
208
- * When an infra pgpass exists, POSTGRES_PASSWORD is taken from it so db-init matches the running Postgres.
194
+ * When `pgpass` exists beside the active infra compose, POSTGRES_PASSWORD is taken from it so db-init matches Postgres.
209
195
  * @async
210
196
  * @param {string} appName - Application name
211
197
  * @param {Object} appConfig - Application configuration
@@ -213,24 +199,26 @@ async function readPostgresPasswordFromPgpass(developerId) {
213
199
  * @param {number|string} [developerId] - Developer ID (for pgpass lookup)
214
200
  * @returns {Promise<{ runEnvPath: string, runEnvAdminPath: string }>} Paths to .env.run and .env.run.admin
215
201
  */
216
- async function buildMergedRunEnvAndWrite(appName, appConfig, devDir, developerId) {
202
+ async function buildMergedRunEnvAndWrite(appName, appConfig, devDir, developerId, scopeOpts = null) {
217
203
  const infra = require('../infrastructure');
218
204
  const ensureAdminSecretsFn = typeof infra.ensureAdminSecrets === 'function'
219
205
  ? infra.ensureAdminSecrets
220
206
  : require('../infrastructure/helpers').ensureAdminSecrets;
221
207
  await ensureAdminSecretsFn();
222
208
  const adminObj = await adminSecrets.readAndDecryptAdminSecrets();
209
+ const runEnvKey = scopeOpts && scopeOpts.runEnvKey ? String(scopeOpts.runEnvKey).toLowerCase() : 'dev';
223
210
  const appObj = await secretsEnvWrite.resolveAndGetEnvMap(appName, {
224
211
  environment: 'docker',
225
212
  secretsPath: null,
226
- force: false
213
+ force: false,
214
+ runEnvKey
227
215
  });
228
216
  const merged = { ...adminObj, ...appObj };
229
217
  if (developerId !== undefined) {
230
218
  const pgpassPwd = await readPostgresPasswordFromPgpass(developerId);
231
219
  if (pgpassPwd !== undefined) merged.POSTGRES_PASSWORD = pgpassPwd;
232
220
  }
233
- injectDatabaseNamesAndUsers(merged, appConfig);
221
+ injectDatabaseNamesAndUsers(merged, appConfig, scopeOpts);
234
222
  injectContainerPortForRun(merged, appConfig, appName);
235
223
 
236
224
  const runEnvPath = path.join(devDir, '.env.run');
@@ -1,3 +1,4 @@
1
+ const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * AI Fabrix Builder - App Run Helpers
3
4
  *
@@ -13,24 +14,20 @@ const fs = require('fs').promises;
13
14
  const fsSync = require('fs');
14
15
  const path = require('path');
15
16
  const chalk = require('chalk');
16
- const { exec } = require('child_process');
17
17
  const { loadConfigFile } = require('../utils/config-format');
18
- const { promisify } = require('util');
19
18
  const validator = require('../validation/validator');
20
19
  const config = require('../core/config');
21
20
  const buildCopy = require('../utils/build-copy');
22
21
  const logger = require('../utils/logger');
23
- const { waitForHealthCheck } = require('../utils/health-check');
24
22
  const composeGenerator = require('../utils/compose-generator');
25
- const dockerUtils = require('../utils/docker');
26
23
  const containerHelpers = require('../utils/app-run-containers');
27
24
  const pathsUtil = require('../utils/paths');
28
25
  const runEnvCompose = require('./run-env-compose');
26
+ const { getAppInfraRequirements } = require('./run-infra-requirements');
27
+ const { resolveRunImage } = require('./run-resolve-image');
28
+ const { startContainer } = require('./run-container-start');
29
29
  const { resolveEnvOutputPath, writeEnvOutputForReload, writeEnvOutputForLocal } = require('../utils/env-copy');
30
30
  const { resolveVersionForApp } = require('../utils/image-version');
31
- const { parseImageOverride } = require('../utils/parse-image-ref');
32
-
33
- const execAsync = promisify(exec);
34
31
 
35
32
  /** Template apps (keycloak, miso-controller, dataplane) - never update application config when running */
36
33
  const TEMPLATE_APP_KEYS = ['keycloak', 'miso-controller', 'dataplane'];
@@ -123,6 +120,9 @@ async function validateAppConfiguration(appName) {
123
120
  throw new Error('Application name is required');
124
121
  }
125
122
 
123
+ const { assertRemoteBuilderDeveloperId } = require('../utils/remote-builder-validation');
124
+ assertRemoteBuilderDeveloperId(await config.getRemoteServer(), await config.getDeveloperId());
125
+
126
126
  // Check if running from builder directory
127
127
  checkBuilderDirectory(appName);
128
128
 
@@ -161,28 +161,6 @@ async function resolveAndUpdateVersion(appName, appConfig, debug) {
161
161
  }
162
162
  }
163
163
 
164
- /**
165
- * Resolve image name and tag from app config and optional run override.
166
- * @param {string} appName - Application name
167
- * @param {Object} appConfig - Application configuration
168
- * @param {Object} runOptions - Run options; runOptions.image overrides config
169
- * @returns {{ imageName: string, imageTag: string }} imageName and imageTag
170
- */
171
- function resolveRunImage(appName, appConfig, runOptions) {
172
- const imageOverride = runOptions && runOptions.image;
173
- if (imageOverride) {
174
- const parsed = parseImageOverride(imageOverride);
175
- return {
176
- imageName: parsed ? parsed.name : composeGenerator.getImageName(appConfig, appName),
177
- imageTag: parsed ? parsed.tag : (appConfig.image && appConfig.image.tag) || 'latest'
178
- };
179
- }
180
- return {
181
- imageName: composeGenerator.getImageName(appConfig, appName),
182
- imageTag: (appConfig.image && appConfig.image.tag) || 'latest'
183
- };
184
- }
185
-
186
164
  /**
187
165
  * Checks prerequisites: Docker image and (optionally) infrastructure
188
166
  * @async
@@ -210,12 +188,12 @@ async function checkPrerequisites(appName, appConfig, debug = false, skipInfraCh
210
188
  : `Run 'aifabrix build ${appName}' first`;
211
189
  throw new Error(`Docker image ${fullImageName} not found\n${hint}`);
212
190
  }
213
- logger.log(chalk.green(`✓ Image ${fullImageName} found`));
191
+ logger.log(formatSuccessLine(`Image ${fullImageName} found`));
214
192
 
215
193
  await resolveAndUpdateVersion(appName, appConfig, debug);
216
194
 
217
195
  if (!skipInfraCheck) {
218
- await checkInfraHealthOrThrow(debug);
196
+ await checkInfraHealthOrThrow(debug, appConfig);
219
197
  }
220
198
  }
221
199
 
@@ -223,12 +201,28 @@ async function checkPrerequisites(appName, appConfig, debug = false, skipInfraCh
223
201
  * Checks infrastructure health and throws if unhealthy
224
202
  * @async
225
203
  * @param {boolean} debug - Enable debug logging
204
+ * @param {Object} [appConfig] - Application configuration (for selective infra checks)
226
205
  * @throws {Error} If infrastructure is not healthy
227
206
  */
228
- async function checkInfraHealthOrThrow(debug) {
207
+ async function checkInfraHealthOrThrow(debug, appConfig) {
208
+ const requirements = getAppInfraRequirements(appConfig);
209
+ if (requirements && !requirements.needsPostgres && !requirements.needsRedis) {
210
+ logger.log(
211
+ chalk.blue('Skipping infrastructure check (application does not require database or redis)...')
212
+ );
213
+ return;
214
+ }
215
+
229
216
  logger.log(chalk.blue('Checking infrastructure health...'));
230
217
  const infra = require('../infrastructure');
231
- const infraHealth = await infra.checkInfraHealth();
218
+ const healthOptions =
219
+ requirements === null
220
+ ? {}
221
+ : {
222
+ postgres: requirements.needsPostgres,
223
+ redis: requirements.needsRedis
224
+ };
225
+ const infraHealth = await infra.checkInfraHealth(null, healthOptions);
232
226
  if (debug) {
233
227
  logger.log(chalk.gray(`[DEBUG] Infrastructure health: ${JSON.stringify(infraHealth, null, 2)}`));
234
228
  }
@@ -239,7 +233,7 @@ async function checkInfraHealthOrThrow(debug) {
239
233
  if (unhealthyServices.length > 0) {
240
234
  throw new Error(`Infrastructure services not healthy: ${unhealthyServices.join(', ')}\nRun 'aifabrix up-infra' first`);
241
235
  }
242
- logger.log(chalk.green('Infrastructure is running'));
236
+ logger.log(formatSuccessLine('Infrastructure is running'));
243
237
  }
244
238
 
245
239
  /**
@@ -334,15 +328,31 @@ async function writeEnvOutputIfConfigured(appName, appConfig, runEnvPath, option
334
328
  async function prepareEnvironment(appName, appConfig, options) {
335
329
  const developerId = await config.getDeveloperId();
336
330
  const devDir = await ensureDevDirectory(appName, developerId);
331
+ const runEnvKey = (options.env || 'dev').toLowerCase();
332
+ const userCfg = await config.getConfig();
333
+ const { computeEffectiveEnvironmentScopedResources } = require('../utils/environment-scoped-resources');
334
+ const effectiveEnvironmentScopedResources = computeEffectiveEnvironmentScopedResources(
335
+ Boolean(userCfg.useEnvironmentScopedResources),
336
+ appConfig.environmentScopedResources === true,
337
+ runEnvKey
338
+ );
337
339
 
338
340
  runEnvCompose.cleanApplicationsDir(developerId);
339
341
  logger.log(chalk.blue('Building merged .env (admin + app secrets)...'));
340
- const { runEnvPath, runEnvAdminPath } = await runEnvCompose.buildMergedRunEnvAndWrite(appName, appConfig, devDir, developerId);
342
+ const { runEnvPath, runEnvAdminPath } = await runEnvCompose.buildMergedRunEnvAndWrite(
343
+ appName,
344
+ appConfig,
345
+ devDir,
346
+ developerId,
347
+ { runEnvKey, effectiveEnvironmentScopedResources }
348
+ );
341
349
 
342
350
  const composeOptions = {
343
351
  ...options,
344
352
  envFilePath: runEnvPath,
345
- dbInitEnvFilePath: runEnvAdminPath
353
+ dbInitEnvFilePath: runEnvAdminPath,
354
+ effectiveEnvironmentScopedResources,
355
+ env: runEnvKey
346
356
  };
347
357
  composeOptions.port = calculateComposePort(composeOptions, appConfig, developerId);
348
358
  const composePath = await generateComposeFile(appName, appConfig, composeOptions, devDir);
@@ -352,105 +362,18 @@ async function prepareEnvironment(appName, appConfig, options) {
352
362
  return { composePath, runEnvPath, runEnvAdminPath };
353
363
  }
354
364
 
355
- /**
356
- * Prepare environment variables for docker compose (no secrets in host env; compose uses env_file).
357
- * @async
358
- * @param {boolean} debug - Enable debug logging
359
- * @returns {Promise<Object>} Environment variables object for child process
360
- */
361
- async function prepareContainerEnv(debug) {
362
- const env = { ...process.env };
363
-
364
- if (typeof process.getuid === 'function' && typeof process.getgid === 'function') {
365
- env.AIFABRIX_UID = String(process.getuid());
366
- env.AIFABRIX_GID = String(process.getgid());
367
- } else {
368
- env.AIFABRIX_UID = '1000';
369
- env.AIFABRIX_GID = '1000';
370
- }
371
-
372
- const { getRemoteDockerEnv } = require('../utils/remote-docker-env');
373
- const remoteDocker = await getRemoteDockerEnv();
374
- Object.assign(env, remoteDocker);
375
-
376
- if (debug) {
377
- logger.log(chalk.gray('[DEBUG] Container env prepared (secrets via env_file)'));
378
- }
379
-
380
- return env;
381
- }
382
-
383
- /**
384
- * Execute docker-compose up command. Services get env from env_file in the compose (e.g. .env.run).
385
- * @async
386
- * @param {string} composeCmdBase - Base compose command
387
- * @param {string} composePath - Path to compose file
388
- * @param {Object} env - Environment variables for the child process
389
- * @param {boolean} debug - Enable debug logging
390
- */
391
- async function executeComposeUp(composeCmdBase, composePath, env, debug) {
392
- const composeCmd = `${composeCmdBase} -f "${composePath}" up -d`;
393
- if (debug) {
394
- logger.log(chalk.gray(`[DEBUG] Executing: ${composeCmd}`));
395
- logger.log(chalk.gray(`[DEBUG] Compose file: ${composePath}`));
396
- }
397
- await execAsync(composeCmd, { env });
398
- }
399
-
400
- /**
401
- * Starts the container and waits for health check. Deletes run .env files after success (ISO 27K).
402
- * @async
403
- * @param {string} appName - Application name
404
- * @param {string} composePath - Path to Docker Compose file
405
- * @param {number} port - Application port
406
- * @param {Object} appConfig - Application configuration
407
- * @param {Object} [opts] - Options
408
- * @param {boolean} [opts.debug=false] - Enable debug logging
409
- * @param {string|null} [opts.runEnvPath=null] - Path to .env.run (app-only) to delete after successful start
410
- * @param {string|null} [opts.runEnvAdminPath=null] - Path to .env.run.admin (start-only) to delete after successful start
411
- * @throws {Error} If container fails to start or become healthy
412
- */
413
- async function startContainer(appName, composePath, port, appConfig = null, opts = {}) {
414
- const { debug = false, runEnvPath = null, runEnvAdminPath = null } = opts;
415
- logger.log(chalk.blue(`Starting ${appName}...`));
416
-
417
- const composeCmdBase = await dockerUtils.ensureDockerAndCompose().then(() => dockerUtils.getComposeCommand());
418
- const env = await prepareContainerEnv(debug);
419
- await executeComposeUp(composeCmdBase, composePath, env, debug);
420
-
421
- const idNum = typeof appConfig.developerId === 'string' ? parseInt(appConfig.developerId, 10) : appConfig.developerId;
422
- const containerName = idNum === 0 ? `aifabrix-${appName}` : `aifabrix-dev${appConfig.developerId}-${appName}`;
423
- logger.log(chalk.green(`✓ Container ${containerName} started`));
424
- await containerHelpers.logContainerStatus(containerName, debug);
425
-
426
- const healthCheckPath = appConfig?.healthCheck?.path || '/health';
427
- logger.log(chalk.blue(`Waiting for application to be healthy at http://localhost:${port}${healthCheckPath}...`));
428
- await waitForHealthCheck(appName, 90, port, appConfig, debug);
429
-
430
- for (const p of [runEnvPath, runEnvAdminPath]) {
431
- if (p && typeof p === 'string') {
432
- try {
433
- await fs.unlink(p);
434
- } catch (err) {
435
- if (err.code !== 'ENOENT') logger.log(chalk.yellow(`Warning: could not remove run .env: ${err.message}`));
436
- }
437
- }
438
- }
439
- }
440
-
441
365
  /**
442
366
  * Displays run status after successful start
443
367
  * @param {string} appName - Application name
444
368
  * @param {number} port - Application port
445
369
  * @param {Object} appConfig - Application configuration (with developerId property)
446
370
  */
447
- async function displayRunStatus(appName, port, appConfig) {
448
- const idNum = typeof appConfig.developerId === 'string' ? parseInt(appConfig.developerId, 10) : appConfig.developerId;
449
- const containerName = idNum === 0 ? `aifabrix-${appName}` : `aifabrix-dev${appConfig.developerId}-${appName}`;
371
+ async function displayRunStatus(appName, port, appConfig, runScopeOpts = null) {
372
+ const containerName = containerHelpers.getContainerName(appName, appConfig.developerId, runScopeOpts);
450
373
  const healthCheckPath = appConfig?.healthCheck?.path || '/health';
451
374
  const healthCheckUrl = `http://localhost:${port}${healthCheckPath}`;
452
375
 
453
- logger.log(chalk.green(`\n✓ App running at http://localhost:${port}`));
376
+ logger.log(formatSuccessParagraph(`App running at http://localhost:${port}`));
454
377
  logger.log(chalk.blue(`Health check: ${healthCheckUrl}`));
455
378
  logger.log(chalk.gray(`Container: ${containerName}`));
456
379
  }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Derives local infra (Postgres/Redis) needs from application.yaml for `run`.
3
+ *
4
+ * @fileoverview Shared by infra health skip and docker-run-without-compose fallback
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * Reads application.yaml `requires` to decide which local infra services apply.
13
+ * Missing `requires` → null (legacy: assume Postgres + Redis may be required).
14
+ *
15
+ * @param {Object} appConfig - Application configuration from application.yaml
16
+ * @returns {null|{ needsPostgres: boolean, needsRedis: boolean }}
17
+ */
18
+ function getAppInfraRequirements(appConfig) {
19
+ const r = appConfig && appConfig.requires;
20
+ if (!r || typeof r !== 'object') {
21
+ return null;
22
+ }
23
+ const needsPostgres =
24
+ r.database === true ||
25
+ (Array.isArray(r.databases) && r.databases.length > 0);
26
+ const needsRedis = r.redis === true;
27
+ return { needsPostgres, needsRedis };
28
+ }
29
+
30
+ module.exports = { getAppInfraRequirements };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Resolve Docker image name/tag for run (config + optional --image / --registry overrides).
3
+ *
4
+ * @fileoverview Shared by checkPrerequisites and container start / docker run fallback
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { resolveDockerImageRef } = require('../utils/resolve-docker-image-ref');
10
+
11
+ /**
12
+ * @param {string} appName
13
+ * @param {Object} appConfig
14
+ * @param {Object} runOptions - runOptions.image / runOptions.registry override config
15
+ * @returns {{ imageName: string, imageTag: string }}
16
+ */
17
+ function resolveRunImage(appName, appConfig, runOptions) {
18
+ return resolveDockerImageRef(appName, appConfig, runOptions);
19
+ }
20
+
21
+ module.exports = { resolveRunImage };
package/lib/app/run.js CHANGED
@@ -10,8 +10,6 @@
10
10
  */
11
11
 
12
12
  const chalk = require('chalk');
13
- const { exec } = require('child_process');
14
- const { promisify } = require('util');
15
13
  const config = require('../core/config');
16
14
  const logger = require('../utils/logger');
17
15
  const pathsUtil = require('../utils/paths');
@@ -21,8 +19,7 @@ const containerHelpers = require('../utils/app-run-containers');
21
19
  const mutagen = require('../utils/mutagen');
22
20
  // Helper functions extracted to reduce file size and complexity
23
21
  const helpers = require('./run-helpers');
24
-
25
- const execAsync = promisify(exec);
22
+ const { execWithDockerEnv } = require('../utils/docker-exec');
26
23
 
27
24
  /**
28
25
  * True if host is localhost or 127.0.0.1 (case-insensitive).
@@ -76,7 +73,7 @@ async function validateAppForRun(appName, _debug) {
76
73
  try {
77
74
  const { isExternal, baseDir } = await detectAppType(appName);
78
75
  if (baseDir !== 'builder' || isExternal) {
79
- logger.log(chalk.yellow('⚠️ External systems don\'t run as Docker containers.'));
76
+ logger.log(chalk.yellow(' External systems don\'t run as Docker containers.'));
80
77
  logger.log(chalk.blue('Use "aifabrix build" to deploy to dataplane, then test via OpenAPI endpoints.'));
81
78
  return false;
82
79
  }
@@ -98,17 +95,27 @@ async function validateAppForRun(appName, _debug) {
98
95
  * @param {boolean} debug - Debug flag
99
96
  * @returns {Promise<void>}
100
97
  */
101
- async function checkAndStopContainer(appName, developerId, debug) {
102
- const containerRunning = await helpers.checkContainerRunning(appName, developerId, debug);
98
+ async function checkAndStopContainer(appName, appConfig, options, debug) {
99
+ const developerId = appConfig.developerId;
100
+ const runEnvKey = (options.env || 'dev').toLowerCase();
101
+ const userCfg = await config.getConfig();
102
+ const { computeEffectiveEnvironmentScopedResources } = require('../utils/environment-scoped-resources');
103
+ const effectiveEnvironmentScopedResources = computeEffectiveEnvironmentScopedResources(
104
+ Boolean(userCfg.useEnvironmentScopedResources),
105
+ appConfig.environmentScopedResources === true,
106
+ runEnvKey
107
+ );
108
+ const scopeOpts = effectiveEnvironmentScopedResources
109
+ ? { effectiveEnvironmentScopedResources: true, env: runEnvKey }
110
+ : null;
111
+ const containerRunning = await helpers.checkContainerRunning(appName, developerId, debug, scopeOpts);
103
112
  if (!containerRunning) {
104
113
  return;
105
114
  }
106
115
 
107
- // Dev 0: aifabrix-{appName} (no dev-0 suffix), Dev > 0: aifabrix-dev{id}-{appName}
108
- const idNum = typeof developerId === 'string' ? parseInt(developerId, 10) : developerId;
109
- const containerName = idNum === 0 ? `aifabrix-${appName}` : `aifabrix-dev${developerId}-${appName}`;
116
+ const containerName = containerHelpers.getContainerName(appName, developerId, scopeOpts);
110
117
  logger.log(chalk.yellow(`Container ${containerName} is already running`));
111
- await helpers.stopAndRemoveContainer(appName, developerId, debug);
118
+ await helpers.stopAndRemoveContainer(appName, developerId, debug, scopeOpts);
112
119
  }
113
120
 
114
121
  /**
@@ -214,10 +221,24 @@ async function loadAndConfigureApp(appName, debug) {
214
221
  * @throws {Error} If container start fails
215
222
  */
216
223
  async function startAppContainer(appName, tempComposePath, hostPort, appConfig, opts) {
217
- const { debug, runEnvPath = null, runEnvAdminPath = null } = opts;
224
+ const {
225
+ debug,
226
+ runEnvPath = null,
227
+ runEnvAdminPath = null,
228
+ runOptions = {},
229
+ devMountPath = null
230
+ } = opts;
231
+ const misoEnvironment = opts.misoEnvironment ?? composeGenerator.resolveMisoEnvironment(runOptions);
218
232
  try {
219
- await helpers.startContainer(appName, tempComposePath, hostPort, appConfig, { debug, runEnvPath, runEnvAdminPath });
220
- await helpers.displayRunStatus(appName, hostPort, appConfig);
233
+ await helpers.startContainer(appName, tempComposePath, hostPort, appConfig, {
234
+ debug,
235
+ runEnvPath,
236
+ runEnvAdminPath,
237
+ runOptions,
238
+ devMountPath,
239
+ misoEnvironment
240
+ });
241
+ await helpers.displayRunStatus(appName, hostPort, appConfig, opts.runScopeOpts || null);
221
242
  } catch (error) {
222
243
  logger.log(chalk.yellow(`\n⚠️ Compose file preserved at: ${tempComposePath}`));
223
244
  logger.log(chalk.yellow(' Review the file to debug issues'));
@@ -240,8 +261,12 @@ async function startAppContainer(appName, tempComposePath, hostPort, appConfig,
240
261
  * @param {boolean} debug - Debug flag
241
262
  * @returns {Promise<Object>} Run options with optional devMountPath
242
263
  */
243
- async function resolveRunOptions(appName, appConfig, options, envKey, debug) {
244
- const runOptions = { ...options, env: envKey };
264
+ async function resolveRunOptions(appName, appConfig, options, envKey, debug, effectiveEnvironmentScopedResources) {
265
+ const runOptions = {
266
+ ...options,
267
+ env: envKey,
268
+ effectiveEnvironmentScopedResources: Boolean(effectiveEnvironmentScopedResources)
269
+ };
245
270
  const builderPath = pathsUtil.getBuilderPath(appName);
246
271
  const codePath = pathsUtil.resolveBuildContext(builderPath, appConfig.build?.context || '.');
247
272
  if (options.reload && envKey === 'dev') {
@@ -269,12 +294,37 @@ async function prepareAppRun(appName, options, debug) {
269
294
  return null;
270
295
  }
271
296
  const appConfig = await loadAndConfigureApp(appName, debug);
297
+ const userCfg = await config.getConfig();
298
+ const { computeEffectiveEnvironmentScopedResources } = require('../utils/environment-scoped-resources');
299
+ const effectiveEnvironmentScopedResources = computeEffectiveEnvironmentScopedResources(
300
+ Boolean(userCfg.useEnvironmentScopedResources),
301
+ appConfig.environmentScopedResources === true,
302
+ envKey
303
+ );
304
+ const runScopeOpts = effectiveEnvironmentScopedResources
305
+ ? { effectiveEnvironmentScopedResources: true, env: envKey }
306
+ : null;
272
307
  await helpers.checkPrerequisites(appName, appConfig, debug, options.skipInfraCheck === true, options);
273
- await checkAndStopContainer(appName, appConfig.developerId, debug);
308
+ await checkAndStopContainer(appName, appConfig, options, debug);
274
309
  const hostPort = await calculateHostPort(appConfig, options, debug);
275
- const runOptions = await resolveRunOptions(appName, appConfig, options, envKey, debug);
310
+ const runOptions = await resolveRunOptions(
311
+ appName,
312
+ appConfig,
313
+ options,
314
+ envKey,
315
+ debug,
316
+ effectiveEnvironmentScopedResources
317
+ );
276
318
  const { composePath: tempComposePath, runEnvPath, runEnvAdminPath } = await helpers.prepareEnvironment(appName, appConfig, runOptions);
277
- const result = { appConfig, tempComposePath, hostPort, runEnvPath, runEnvAdminPath };
319
+ const result = {
320
+ appConfig,
321
+ tempComposePath,
322
+ hostPort,
323
+ runEnvPath,
324
+ runEnvAdminPath,
325
+ runScopeOpts,
326
+ mergedRunOptions: runOptions
327
+ };
278
328
  if (runOptions.devMountPath) result.devMountPath = runOptions.devMountPath;
279
329
  return result;
280
330
  }
@@ -317,7 +367,10 @@ async function runApp(appName, options = {}) {
317
367
  await startAppContainer(appName, prepared.tempComposePath, prepared.hostPort, prepared.appConfig, {
318
368
  debug,
319
369
  runEnvPath: prepared.runEnvPath,
320
- runEnvAdminPath: prepared.runEnvAdminPath
370
+ runEnvAdminPath: prepared.runEnvAdminPath,
371
+ runOptions: prepared.mergedRunOptions,
372
+ devMountPath: prepared.devMountPath,
373
+ runScopeOpts: prepared.runScopeOpts
321
374
  });
322
375
  } catch (error) {
323
376
  if (debug) {
@@ -347,7 +400,7 @@ async function restartApp(appName) {
347
400
  const developerId = await config.getDeveloperId();
348
401
  const containerName = containerHelpers.getContainerName(appName, developerId);
349
402
  try {
350
- await execAsync(`docker restart ${containerName}`);
403
+ await execWithDockerEnv(`docker restart ${containerName}`);
351
404
  } catch (error) {
352
405
  const msg = (error.stderr || error.stdout || error.message || '').toLowerCase();
353
406
  if (msg.includes('no such container') || msg.includes('is not running')) {
@@ -81,7 +81,7 @@ function logApplicationExternalIntegration(ei, options = {}) {
81
81
  logger.log(` systems: [${(ei.systems || []).join(', ')}]`);
82
82
  logger.log(` dataSources: [${(ei.dataSources || []).join(', ')}]`);
83
83
  if (!options.skipHint) {
84
- logger.log(chalk.gray('\n For external system data as on dataplane, run: aifabrix show <appKey> --online or aifabrix app show <appKey>.'));
84
+ logger.log(chalk.gray('\n For external system data as on dataplane, run: aifabrix show <app> --online or aifabrix app show <app>.'));
85
85
  }
86
86
  }
87
87
 
package/lib/app/show.js CHANGED
@@ -440,7 +440,7 @@ function formatBuildForDisplay(build) {
440
440
  if (!build) return '—';
441
441
  const parts = [];
442
442
  if (build.language) parts.push(build.language);
443
- const port = build.port ?? build.localPort;
443
+ const port = build.port;
444
444
  if (port !== undefined && port !== null) parts.push(`port ${port}`);
445
445
  if (build.dockerfile) parts.push('dockerfile');
446
446
  if (build.envOutputPath) parts.push(`envOutputPath: ${build.envOutputPath}`);