@aifabrix/builder 2.43.0 → 2.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/README.md +1 -1
  3. package/anchor-docs/README.md +10 -0
  4. package/anchor-docs/_TEMPLATE +24 -0
  5. package/bin/aifabrix.js +13 -4
  6. package/integration/hubspot-test/README.md +31 -0
  7. package/integration/hubspot-test/create-hubspot.js +5 -5
  8. package/integration/hubspot-test/hubspot-test-datasource-company.json +58 -462
  9. package/integration/hubspot-test/hubspot-test-datasource-contact.json +61 -555
  10. package/integration/hubspot-test/hubspot-test-datasource-deal.json +63 -506
  11. package/integration/hubspot-test/hubspot-test-datasource-users.json +42 -83
  12. package/integration/hubspot-test/hubspot-test-deploy.json +3 -3
  13. package/integration/hubspot-test/test-dataplane-down-tests.js +1 -7
  14. package/integration/hubspot-test/test-dataplane-down.js +3 -3
  15. package/integration/hubspot-test/test.js +35 -43
  16. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  17. package/integration/roundtrip-test-local/README.md +144 -0
  18. package/integration/roundtrip-test-local/application.yaml +13 -0
  19. package/integration/roundtrip-test-local/env.template +15 -0
  20. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  21. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  22. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  23. package/integration/roundtrip-test-local2/README.md +144 -0
  24. package/integration/roundtrip-test-local2/application.yaml +13 -0
  25. package/integration/roundtrip-test-local2/env.template +15 -0
  26. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  27. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  28. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  29. package/integration/test/wizard.yaml +8 -0
  30. package/jest.config.default.js +10 -0
  31. package/jest.config.integration.fixtures.js +22 -0
  32. package/jest.config.integration.js +21 -18
  33. package/jest.config.isolated.js +10 -0
  34. package/jest.projects.js +288 -0
  35. package/lib/api/datasources-core.api.js +3 -3
  36. package/lib/api/dev-mtls-request.js +110 -0
  37. package/lib/api/dev-server-https.js +145 -0
  38. package/lib/api/dev.api.js +133 -144
  39. package/lib/api/index.js +0 -1
  40. package/lib/api/pipeline.api.js +67 -20
  41. package/lib/api/types/dev.types.js +4 -3
  42. package/lib/api/types/pipeline.types.js +8 -5
  43. package/lib/api/types/validation-run.types.js +56 -0
  44. package/lib/api/validation-run.api.js +99 -0
  45. package/lib/api/validation-runner.js +99 -0
  46. package/lib/app/config.js +1 -1
  47. package/lib/app/deploy-status-display.js +2 -2
  48. package/lib/app/deploy.js +7 -6
  49. package/lib/app/display.js +2 -1
  50. package/lib/app/dockerfile.js +3 -2
  51. package/lib/app/down.js +2 -1
  52. package/lib/app/helpers.js +6 -5
  53. package/lib/app/index.js +27 -8
  54. package/lib/app/list.js +7 -6
  55. package/lib/app/push.js +4 -3
  56. package/lib/app/register.js +16 -7
  57. package/lib/app/rotate-secret.js +14 -13
  58. package/lib/app/run-container-start.js +184 -0
  59. package/lib/app/run-docker-fallback.js +108 -0
  60. package/lib/app/run-env-compose.js +30 -42
  61. package/lib/app/run-helpers.js +49 -126
  62. package/lib/app/run-infra-requirements.js +30 -0
  63. package/lib/app/run-resolve-image.js +21 -0
  64. package/lib/app/run.js +74 -21
  65. package/lib/app/show-display.js +1 -1
  66. package/lib/app/show.js +1 -1
  67. package/lib/build/index.js +13 -10
  68. package/lib/cli/index.js +2 -0
  69. package/lib/cli/setup-app.help.js +67 -0
  70. package/lib/cli/setup-app.js +57 -121
  71. package/lib/cli/setup-app.test-commands.js +179 -0
  72. package/lib/cli/setup-auth.js +19 -5
  73. package/lib/cli/setup-credential-deployment.js +22 -8
  74. package/lib/cli/setup-dev-path-commands.js +124 -0
  75. package/lib/cli/setup-dev.js +170 -113
  76. package/lib/cli/setup-environment.js +7 -1
  77. package/lib/cli/setup-external-system.js +62 -22
  78. package/lib/cli/setup-infra.js +126 -47
  79. package/lib/cli/setup-parameters.js +32 -0
  80. package/lib/cli/setup-secrets.js +106 -8
  81. package/lib/cli/setup-service-user.js +1 -1
  82. package/lib/cli/setup-utility.js +36 -20
  83. package/lib/commands/app-down.js +5 -7
  84. package/lib/commands/app-install.js +14 -7
  85. package/lib/commands/app-logs.js +13 -10
  86. package/lib/commands/app-shell.js +4 -1
  87. package/lib/commands/app-test.js +25 -19
  88. package/lib/commands/app.js +22 -10
  89. package/lib/commands/auth-config.js +6 -6
  90. package/lib/commands/auth-status.js +4 -3
  91. package/lib/commands/credential-env.js +4 -3
  92. package/lib/commands/credential-list.js +5 -4
  93. package/lib/commands/credential-push.js +4 -3
  94. package/lib/commands/datasource-unified-test-cli.js +495 -0
  95. package/lib/commands/datasource-unified-test-cli.options.js +149 -0
  96. package/lib/commands/datasource-validation-cli.js +129 -0
  97. package/lib/commands/datasource.js +105 -98
  98. package/lib/commands/deployment-list.js +6 -5
  99. package/lib/commands/dev-cli-handlers.js +122 -18
  100. package/lib/commands/dev-down.js +4 -3
  101. package/lib/commands/dev-init.js +231 -116
  102. package/lib/commands/dev-show-display.js +473 -0
  103. package/lib/commands/login-credentials.js +3 -2
  104. package/lib/commands/login-device.js +4 -3
  105. package/lib/commands/login.js +5 -4
  106. package/lib/commands/logout.js +8 -7
  107. package/lib/commands/parameters-validate.js +54 -0
  108. package/lib/commands/repair-datasource.js +314 -68
  109. package/lib/commands/repair-env-template.js +2 -2
  110. package/lib/commands/repair.js +21 -3
  111. package/lib/commands/secrets-list.js +23 -12
  112. package/lib/commands/secrets-remove-all.js +220 -0
  113. package/lib/commands/secrets-remove.js +21 -12
  114. package/lib/commands/secrets-set.js +21 -12
  115. package/lib/commands/secrets-validate.js +4 -4
  116. package/lib/commands/secure.js +10 -9
  117. package/lib/commands/service-user.js +26 -25
  118. package/lib/commands/test-e2e-external.js +27 -1
  119. package/lib/commands/up-common.js +3 -2
  120. package/lib/commands/up-dataplane.js +29 -16
  121. package/lib/commands/up-miso.js +19 -29
  122. package/lib/commands/upload.js +138 -39
  123. package/lib/commands/wizard-core-helpers.js +1 -1
  124. package/lib/commands/wizard-dataplane.js +4 -3
  125. package/lib/commands/wizard-helpers.js +3 -3
  126. package/lib/commands/wizard.js +2 -2
  127. package/lib/core/admin-secrets.js +14 -5
  128. package/lib/core/audit-logger.js +12 -4
  129. package/lib/core/config-attach-extensions.js +46 -0
  130. package/lib/core/config-runtime-paths.js +29 -0
  131. package/lib/core/config.js +55 -56
  132. package/lib/core/diff.js +3 -2
  133. package/lib/core/ensure-encryption-key.js +1 -1
  134. package/lib/core/secrets-ensure-infra.js +77 -0
  135. package/lib/core/secrets-ensure.js +120 -64
  136. package/lib/core/secrets-env-write.js +35 -7
  137. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  138. package/lib/core/secrets.js +200 -37
  139. package/lib/core/templates-env.js +4 -3
  140. package/lib/datasource/abac-validator.js +1 -10
  141. package/lib/datasource/deploy.js +75 -53
  142. package/lib/datasource/field-reference-validator.js +9 -6
  143. package/lib/datasource/integration-context.js +63 -0
  144. package/lib/datasource/list.js +8 -7
  145. package/lib/datasource/log-viewer.js +84 -53
  146. package/lib/datasource/resolve-app.js +4 -4
  147. package/lib/datasource/test-e2e.js +95 -146
  148. package/lib/datasource/test-integration.js +114 -122
  149. package/lib/datasource/unified-validation-run-body.js +65 -0
  150. package/lib/datasource/unified-validation-run-post.js +23 -0
  151. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  152. package/lib/datasource/unified-validation-run.js +92 -0
  153. package/lib/datasource/validate.js +157 -13
  154. package/lib/deployment/deployer.js +4 -3
  155. package/lib/deployment/environment.js +7 -6
  156. package/lib/deployment/push.js +17 -8
  157. package/lib/external-system/delete.js +4 -3
  158. package/lib/external-system/deploy.js +131 -53
  159. package/lib/external-system/download-helpers.js +1 -1
  160. package/lib/external-system/download.js +7 -6
  161. package/lib/external-system/generator.js +92 -6
  162. package/lib/external-system/integration-test-dispatch.js +26 -0
  163. package/lib/external-system/test-execution.js +5 -1
  164. package/lib/external-system/test-helpers.js +0 -4
  165. package/lib/external-system/test-system-level-helpers.js +110 -0
  166. package/lib/external-system/test-system-level.js +83 -44
  167. package/lib/external-system/test.js +59 -8
  168. package/lib/generator/builders.js +23 -11
  169. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  170. package/lib/generator/external.js +16 -4
  171. package/lib/generator/helpers.js +58 -3
  172. package/lib/generator/index.js +4 -0
  173. package/lib/generator/split-readme.js +12 -7
  174. package/lib/generator/split-variables.js +2 -1
  175. package/lib/generator/split.js +1 -1
  176. package/lib/generator/wizard-readme.js +3 -3
  177. package/lib/generator/wizard.js +8 -8
  178. package/lib/infrastructure/compose.js +60 -6
  179. package/lib/infrastructure/helpers.js +201 -29
  180. package/lib/infrastructure/index.js +28 -17
  181. package/lib/infrastructure/services.js +21 -15
  182. package/lib/internal/fs-real-sync.js +104 -0
  183. package/lib/internal/node-fs.js +98 -0
  184. package/lib/parameters/database-secret-values.js +173 -0
  185. package/lib/parameters/infra-kv-discovery.js +121 -0
  186. package/lib/parameters/infra-parameter-catalog.js +458 -0
  187. package/lib/parameters/infra-parameter-validate.js +64 -0
  188. package/lib/schema/application-schema.json +37 -17
  189. package/lib/schema/datasource-test-run.schema.json +493 -0
  190. package/lib/schema/deployment-rules.yaml +102 -63
  191. package/lib/schema/external-datasource.schema.json +1200 -442
  192. package/lib/schema/external-system.schema.json +181 -5
  193. package/lib/schema/flag-map-validation-run.json +31 -0
  194. package/lib/schema/infra-parameter.schema.json +106 -0
  195. package/lib/schema/infra.parameter.yaml +421 -0
  196. package/lib/schema/type/credential-auth-templates.json +40 -0
  197. package/lib/schema/type/document-storage.json +213 -0
  198. package/lib/schema/type/message-service.json +123 -0
  199. package/lib/schema/type/vector-store.json +88 -0
  200. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  201. package/lib/utils/api-error-handler.js +2 -2
  202. package/lib/utils/api.js +49 -14
  203. package/lib/utils/app-register-api.js +3 -2
  204. package/lib/utils/app-register-auth.js +1 -1
  205. package/lib/utils/app-register-config.js +4 -4
  206. package/lib/utils/app-register-display.js +3 -2
  207. package/lib/utils/app-register-validator.js +3 -2
  208. package/lib/utils/app-run-containers.js +26 -22
  209. package/lib/utils/app-scoped-config.js +31 -0
  210. package/lib/utils/app-service-env-from-builder.js +164 -0
  211. package/lib/utils/build-copy.js +1 -1
  212. package/lib/utils/build-helpers.js +20 -20
  213. package/lib/utils/build-resolve-image.js +165 -0
  214. package/lib/utils/cli-layout-chalk.js +8 -0
  215. package/lib/utils/cli-test-layout-chalk.js +267 -0
  216. package/lib/utils/cli-utils.js +88 -11
  217. package/lib/utils/compose-db-passwords.js +138 -0
  218. package/lib/utils/compose-generate-docker-compose.js +216 -0
  219. package/lib/utils/compose-generator.js +197 -291
  220. package/lib/utils/compose-miso-env.js +18 -0
  221. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  222. package/lib/utils/config-paths.js +166 -7
  223. package/lib/utils/config-scoped-resources-preference.js +41 -0
  224. package/lib/utils/controller-deployment-outcome.js +68 -0
  225. package/lib/utils/credential-display.js +2 -2
  226. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  227. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  228. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  229. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  230. package/lib/utils/datasource-test-run-display.js +442 -0
  231. package/lib/utils/datasource-test-run-exit.js +58 -0
  232. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  233. package/lib/utils/datasource-test-run-report-version.js +51 -0
  234. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  235. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  236. package/lib/utils/datasource-validation-watch.js +266 -0
  237. package/lib/utils/declarative-url-ports.js +47 -0
  238. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  239. package/lib/utils/dev-ca-install.js +185 -23
  240. package/lib/utils/dev-cert-helper.js +266 -17
  241. package/lib/utils/dev-hosts-helper.js +307 -0
  242. package/lib/utils/dev-init-cert-hints.js +37 -0
  243. package/lib/utils/dev-init-health-messages.js +52 -0
  244. package/lib/utils/dev-init-resolve.js +86 -0
  245. package/lib/utils/dev-init-ssh-merge.js +65 -0
  246. package/lib/utils/dev-ssh-config-helper.js +196 -0
  247. package/lib/utils/dev-user-groups.js +93 -0
  248. package/lib/utils/docker-build.js +42 -17
  249. package/lib/utils/docker-exec.js +28 -0
  250. package/lib/utils/docker-manifest-public-port.js +116 -0
  251. package/lib/utils/docker-not-running-hint.js +52 -0
  252. package/lib/utils/docker.js +98 -11
  253. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  254. package/lib/utils/env-config-loader.js +10 -91
  255. package/lib/utils/env-copy.js +19 -10
  256. package/lib/utils/env-map.js +35 -8
  257. package/lib/utils/env-template.js +2 -2
  258. package/lib/utils/environment-scoped-resources.js +144 -0
  259. package/lib/utils/error-formatter.js +92 -13
  260. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  261. package/lib/utils/error-formatters/network-errors.js +2 -1
  262. package/lib/utils/error-formatters/permission-errors.js +2 -1
  263. package/lib/utils/error-formatters/validation-errors.js +2 -1
  264. package/lib/utils/external-readme.js +8 -1
  265. package/lib/utils/external-system-display.js +234 -136
  266. package/lib/utils/external-system-local-test-tty.js +389 -0
  267. package/lib/utils/external-system-readiness-core.js +377 -0
  268. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  269. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  270. package/lib/utils/external-system-readiness-display.js +186 -0
  271. package/lib/utils/external-system-test-helpers.js +24 -6
  272. package/lib/utils/external-system-validators.js +30 -12
  273. package/lib/utils/health-check-url.js +119 -0
  274. package/lib/utils/health-check.js +59 -25
  275. package/lib/utils/help-builder.js +11 -8
  276. package/lib/utils/image-version.js +4 -8
  277. package/lib/utils/infra-containers.js +4 -7
  278. package/lib/utils/infra-env-defaults.js +162 -0
  279. package/lib/utils/infra-status-display.js +167 -0
  280. package/lib/utils/infra-status.js +16 -8
  281. package/lib/utils/local-secrets.js +3 -4
  282. package/lib/utils/paths.js +134 -47
  283. package/lib/utils/port-resolver.js +10 -23
  284. package/lib/utils/redis-env-scope.js +62 -0
  285. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  286. package/lib/utils/remote-builder-validation.js +99 -0
  287. package/lib/utils/remote-dev-auth.js +117 -21
  288. package/lib/utils/remote-docker-env.js +67 -15
  289. package/lib/utils/remote-secrets-loader.js +13 -4
  290. package/lib/utils/resolve-docker-image-ref.js +124 -0
  291. package/lib/utils/schema-loader.js +22 -9
  292. package/lib/utils/secrets-bash-kv.js +25 -0
  293. package/lib/utils/secrets-generator.js +169 -49
  294. package/lib/utils/secrets-helpers.js +70 -59
  295. package/lib/utils/secrets-kv-scope.js +60 -0
  296. package/lib/utils/secrets-utils.js +32 -38
  297. package/lib/utils/secrets-validation.js +3 -1
  298. package/lib/utils/secrets-yaml-preserve.js +109 -0
  299. package/lib/utils/ssh-key-helper.js +4 -2
  300. package/lib/utils/template-helpers.js +2 -2
  301. package/lib/utils/test-log-writer.js +3 -3
  302. package/lib/utils/token-manager.js +1 -2
  303. package/lib/utils/url-declarative-public-base.js +188 -0
  304. package/lib/utils/url-declarative-resolve-build.js +493 -0
  305. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  306. package/lib/utils/url-declarative-resolve.js +220 -0
  307. package/lib/utils/url-declarative-token-parse.js +74 -0
  308. package/lib/utils/url-declarative-url-flags.js +50 -0
  309. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  310. package/lib/utils/url-public-path-prefix.js +34 -0
  311. package/lib/utils/urls-local-registry.js +220 -0
  312. package/lib/utils/validation-report-tty-kit.js +77 -0
  313. package/lib/utils/validation-run-poll.js +89 -0
  314. package/lib/utils/validation-run-post-retry.js +73 -0
  315. package/lib/utils/validation-run-request.js +98 -0
  316. package/lib/utils/variable-transformer.js +21 -4
  317. package/lib/utils/yaml-preserve.js +33 -14
  318. package/lib/validation/datasource-warnings.js +56 -0
  319. package/lib/validation/env-template-auth.js +1 -1
  320. package/lib/validation/external-manifest-validator.js +27 -7
  321. package/lib/validation/validate-display.js +37 -31
  322. package/lib/validation/validate.js +4 -13
  323. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  324. package/lib/validation/validator.js +22 -65
  325. package/lib/validation/wizard-config-validator.js +2 -1
  326. package/package.json +7 -3
  327. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  328. package/scripts/diagnose-cli.js +150 -0
  329. package/scripts/install-local.js +304 -55
  330. package/templates/README.md +15 -2
  331. package/templates/applications/dataplane/application.yaml +52 -2
  332. package/templates/applications/dataplane/env.template +75 -17
  333. package/templates/applications/dataplane/rbac.yaml +8 -0
  334. package/templates/applications/keycloak/application.yaml +9 -1
  335. package/templates/applications/keycloak/env.template +15 -6
  336. package/templates/applications/miso-controller/application.yaml +10 -2
  337. package/templates/applications/miso-controller/env.template +42 -12
  338. package/templates/applications/miso-controller/rbac.yaml +5 -0
  339. package/templates/external-system/README.md.hbs +20 -7
  340. package/templates/external-system/deploy.js.hbs +5 -5
  341. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  342. package/templates/infra/compose.yaml.hbs +20 -4
  343. package/templates/python/docker-compose.hbs +16 -0
  344. package/templates/typescript/docker-compose.hbs +16 -0
  345. package/lib/api/external-test.api.js +0 -111
  346. package/lib/schema/env-config.yaml +0 -60
@@ -24,10 +24,14 @@ const {
24
24
  rewriteInfraEndpoints,
25
25
  readYamlAtPath,
26
26
  applyCanonicalSecretsOverride,
27
- ensureNonEmptySecrets,
28
27
  validateSecrets
29
28
  } = require('../utils/secrets-helpers');
30
29
  const { buildEnvVarMap } = require('../utils/env-map');
30
+ const {
31
+ mergeInfraParameterDefaultsForCli,
32
+ getInfraParameterCatalog,
33
+ readRelaxedCatalogDefaults
34
+ } = require('../parameters/infra-parameter-catalog');
31
35
  const { resolveServicePortsInEnvContent } = require('../utils/secrets-url');
32
36
  const {
33
37
  updatePortForDocker,
@@ -35,7 +39,7 @@ const {
35
39
  applyDockerEnvOverride,
36
40
  getContainerPortFromDockerEnv
37
41
  } = require('./secrets-docker-env');
38
- const { getContainerPortFromPath } = require('../utils/port-resolver');
42
+ const { getContainerPortFromPath, loadVariablesFromPath } = require('../utils/port-resolver');
39
43
  const {
40
44
  generateMissingSecrets,
41
45
  createDefaultSecrets
@@ -48,11 +52,21 @@ const {
48
52
  const {
49
53
  loadUserSecrets,
50
54
  loadPrimaryUserSecrets,
51
- loadDefaultSecrets
55
+ loadDefaultSecrets,
56
+ ensurePrimaryUserSecretsFileExists
52
57
  } = require('../utils/secrets-utils');
53
58
  const { decryptSecret, isEncrypted } = require('../utils/secrets-encryption');
54
59
  const pathsUtil = require('../utils/paths');
55
60
  const { ensureSecureFilePermissions } = require('../utils/secure-file-permissions');
61
+ const { readAppEnvironmentScopedFlagForAppPath } = require('../utils/app-scoped-config');
62
+ const { computeEffectiveEnvironmentScopedResources, redisDbIndexForScopedRunEnv } = require('../utils/environment-scoped-resources');
63
+ const { applyRedisDbIndexToEnvContent } = require('../utils/redis-env-scope');
64
+ const { expandDeclarativeUrlsInEnvContent } = require('../utils/url-declarative-resolve');
65
+ const { rewriteInactiveDeclarativeVdirPublicContent } = require('../utils/url-declarative-vdir-inactive-env');
66
+ const {
67
+ mergeDockerManifestPublishedPort,
68
+ rewriteDockerManifestPublicPortEnvLine
69
+ } = require('../utils/docker-manifest-public-port');
56
70
 
57
71
  /**
58
72
  * Generates a canonical secret name from an environment variable key.
@@ -128,8 +142,8 @@ async function decryptSecretsObject(secrets) {
128
142
  * @function loadSecrets
129
143
  * @param {string} [secretsPath] - Path to secrets file (optional, for explicit override)
130
144
  * @param {string} [appName] - Application name (optional, for backward compatibility)
131
- * @returns {Promise<Object>} Loaded secrets object with decrypted values
132
- * @throws {Error} If no secrets file found and no fallback available
145
+ * @returns {Promise<Object>} Loaded secrets object with decrypted values (may be empty after bootstrap file create)
146
+ * @throws {Error} If explicit secretsPath is set but file is missing or invalid
133
147
  *
134
148
  * @example
135
149
  * const secrets = await loadSecrets('../../secrets.local.yaml');
@@ -172,16 +186,17 @@ function mergeUserWithConfigFile(userSecrets, resolvedConfigPath) {
172
186
 
173
187
  /**
174
188
  * Loads config secrets path, merges with user secrets (user/master wins, public fills missing).
175
- * User/master = primary home (AIFABRIX_HOME or ~/.aifabrix) secrets.local.yaml.
189
+ * User/master = getPrimaryUserSecretsLocalPath() (config dir; same file as loadPrimaryUserSecrets).
176
190
  * Public = aifabrix-secrets path from config. Used by loadSecrets cascade.
177
- * When aifabrix-secrets is an http(s) URL, fetches shared secrets from API (never persisted to disk).
191
+ * When the effective shared-secrets target (after remote-dev resolution) is an http(s) URL,
192
+ * fetches shared secrets from API (never persisted to disk).
178
193
  *
179
194
  * @async
180
195
  * @returns {Promise<Object|null>} Merged secrets object or null
181
196
  */
182
197
  async function loadMergedConfigAndUserSecrets() {
183
198
  const { loadRemoteSharedSecrets, mergeUserWithRemoteSecrets } = require('../utils/remote-secrets-loader');
184
- const { isRemoteSecretsUrl } = require('../utils/remote-dev-auth');
199
+ const remoteDevAuth = require('../utils/remote-dev-auth');
185
200
  const userSecrets = loadPrimaryUserSecrets();
186
201
  const hasKeys = (obj) => obj && Object.keys(obj).length > 0;
187
202
  const userOrNull = () => (hasKeys(userSecrets) ? userSecrets : null);
@@ -190,7 +205,8 @@ async function loadMergedConfigAndUserSecrets() {
190
205
  if (!configSecretsPath) {
191
206
  return userOrNull();
192
207
  }
193
- if (isRemoteSecretsUrl(configSecretsPath)) {
208
+ const effectiveShared = await remoteDevAuth.resolveSharedSecretsEndpoint(configSecretsPath);
209
+ if (remoteDevAuth.isRemoteSecretsUrl(effectiveShared)) {
194
210
  const remoteSecrets = await loadRemoteSharedSecrets();
195
211
  const merged = mergeUserWithRemoteSecrets(userSecrets, remoteSecrets);
196
212
  return hasKeys(merged) ? merged : userOrNull();
@@ -254,9 +270,12 @@ async function loadSecrets(secretsPath, _appName) {
254
270
  }
255
271
  return await decryptSecretsObject(explicitSecrets);
256
272
  }
257
- const mergedSecrets = await loadSecretsWithFallbacks();
258
- ensureNonEmptySecrets(mergedSecrets);
259
- return await decryptSecretsObject(mergedSecrets);
273
+ let mergedSecrets = await loadSecretsWithFallbacks();
274
+ if (!mergedSecrets || Object.keys(mergedSecrets).length === 0) {
275
+ ensurePrimaryUserSecretsFileExists();
276
+ mergedSecrets = await loadSecretsWithFallbacks();
277
+ }
278
+ return await decryptSecretsObject(mergedSecrets || {});
260
279
  }
261
280
 
262
281
  /**
@@ -279,7 +298,7 @@ async function loadSecrets(secretsPath, _appName) {
279
298
  * const resolved = await resolveKvReferences(template, secrets, 'local');
280
299
  * // Returns: 'DATABASE_URL=postgresql://user:pass@localhost:5432/db'
281
300
  */
282
- async function resolveKvReferences(envTemplate, secrets, environment = 'local', secretsFilePaths = null, appName = null) {
301
+ async function resolveKvReferences(envTemplate, secrets, environment = 'local', secretsFilePaths = null, appName = null, scopedKv = null) {
283
302
  const os = require('os');
284
303
 
285
304
  // Get developer-id for port variables (local and docker: *_PUBLIC_PORT = base + devId*100)
@@ -290,25 +309,56 @@ async function resolveKvReferences(envTemplate, secrets, environment = 'local',
290
309
  // ignore, buildEnvVarMap will use default
291
310
  }
292
311
 
293
- let envVars = await buildEnvVarMap(environment, os, developerId);
312
+ const envKey = String(environment || 'local').toLowerCase();
313
+ const mapContext = envKey === 'docker' || envKey === 'local' ? envKey : 'local';
314
+
315
+ let envVars = await buildEnvVarMap(mapContext, os, developerId);
294
316
  if (!envVars || Object.keys(envVars).length === 0) {
295
317
  // Fallback to local environment variables if requested environment does not exist
296
318
  envVars = await buildEnvVarMap('local', os, developerId);
297
319
  }
298
320
  const resolved = interpolateEnvVars(envTemplate, envVars);
299
- const missing = collectMissingSecrets(resolved, secrets);
321
+ const missing = collectMissingSecrets(resolved, secrets, scopedKv);
300
322
  if (missing.length > 0) {
301
323
  const fileInfo = formatMissingSecretsFileInfo(secretsFilePaths);
302
324
  const resolveCommand = appName ? `aifabrix resolve ${appName}` : 'aifabrix resolve <app-name>';
303
325
  throw new Error(`Missing secrets: ${missing.join(', ')}${fileInfo}\n\nRun "${resolveCommand}" to generate missing secrets.`);
304
326
  }
305
- return replaceKvInContent(resolved, secrets, envVars);
327
+ return replaceKvInContent(resolved, secrets, envVars, scopedKv);
306
328
  }
307
329
 
308
- /** Docker env transformations: ports, infra endpoints, PORT. */
309
- async function applyDockerTransformations(resolved, variablesPath) {
310
- resolved = await resolveServicePortsInEnvContent(resolved, 'docker');
311
- resolved = await rewriteInfraEndpoints(resolved, 'docker');
330
+ /**
331
+ * Resolve run env key and effective env-scoped kv/redis behavior for generateEnvContent.
332
+ *
333
+ * @async
334
+ * @param {string} appPath - Builder application directory
335
+ * @param {Object} [options] - generateEnvContent options; may set runEnvKey
336
+ * @returns {Promise<{ runEnvKey: string, effective: boolean }>}
337
+ */
338
+ async function buildScopedKvContext(appPath, options = {}) {
339
+ let runEnvKey;
340
+ if (options.runEnvKey !== undefined && options.runEnvKey !== null) {
341
+ runEnvKey = String(options.runEnvKey).toLowerCase();
342
+ } else if (typeof config.getCurrentEnvironment === 'function') {
343
+ runEnvKey = String((await config.getCurrentEnvironment()) || 'dev').toLowerCase();
344
+ } else {
345
+ runEnvKey = 'dev';
346
+ }
347
+ const userCfg =
348
+ typeof config.getConfig === 'function'
349
+ ? await config.getConfig()
350
+ : { useEnvironmentScopedResources: false };
351
+ const useGate = Boolean(userCfg.useEnvironmentScopedResources);
352
+ const appFlag = readAppEnvironmentScopedFlagForAppPath(appPath);
353
+ const effective = computeEffectiveEnvironmentScopedResources(useGate, appFlag, runEnvKey);
354
+ return { runEnvKey, effective };
355
+ }
356
+
357
+ /**
358
+ * Redis/DB service endpoints for docker env interpolation.
359
+ * @returns {Promise<{ redisHost: string, redisPort: number, dbHost: string, dbPort: number }>}
360
+ */
361
+ async function getDockerRedisDbEndpoints() {
312
362
  const { getEnvHosts, getServiceHost, getServicePort, getLocalhostOverride } = require('../utils/env-endpoints');
313
363
  const hosts = await getEnvHosts('docker');
314
364
  const localhostOverride = getLocalhostOverride('docker');
@@ -316,16 +366,97 @@ async function applyDockerTransformations(resolved, variablesPath) {
316
366
  const redisPort = await getServicePort('REDIS_PORT', 'redis', hosts, 'docker', null);
317
367
  const dbHost = getServiceHost(hosts.DB_HOST, 'docker', 'postgres', localhostOverride);
318
368
  const dbPort = await getServicePort('DB_PORT', 'postgres', hosts, 'docker', null);
369
+ return { redisHost, redisPort, dbHost, dbPort };
370
+ }
371
+
372
+ /**
373
+ * Config inputs for declarative url:// expansion (keeps expandDeclarativeUrlsIfPresent small).
374
+ * @param {string} appPath
375
+ * @returns {Promise<Object>}
376
+ */
377
+ async function loadDeclarativeUrlExpandInputs(appPath) {
378
+ const userCfg = await config.getConfig();
379
+ let remoteServer = null;
380
+ try {
381
+ const rs = await config.getRemoteServer();
382
+ if (rs && String(rs).trim()) {
383
+ remoteServer = String(rs).trim();
384
+ }
385
+ } catch {
386
+ remoteServer = null;
387
+ }
388
+ let developerIdRaw = null;
389
+ try {
390
+ developerIdRaw = await config.getDeveloperId();
391
+ } catch {
392
+ developerIdRaw = null;
393
+ }
394
+ let infraTlsEnabled = false;
395
+ try {
396
+ infraTlsEnabled = await config.getTlsEnabled();
397
+ } catch {
398
+ infraTlsEnabled = false;
399
+ }
400
+ return {
401
+ userCfg,
402
+ remoteServer,
403
+ developerIdRaw,
404
+ infraTlsEnabled,
405
+ appScopedFlag: readAppEnvironmentScopedFlagForAppPath(appPath)
406
+ };
407
+ }
408
+
409
+ /**
410
+ * After kv:// resolution, expand url:// references when application config exists.
411
+ * @param {string} resolved
412
+ * @param {string} appName
413
+ * @param {string} appPath
414
+ * @param {string|null} variablesPath
415
+ * @param {string} environment
416
+ * @param {boolean} envOnly
417
+ * @returns {Promise<string>}
418
+ */
419
+ async function expandDeclarativeUrlsIfPresent(resolved, appName, appPath, variablesPath, environment, envOnly) {
420
+ if (envOnly || !variablesPath) {
421
+ return resolved;
422
+ }
423
+ const { userCfg, remoteServer, developerIdRaw, infraTlsEnabled, appScopedFlag } =
424
+ await loadDeclarativeUrlExpandInputs(appPath);
425
+ resolved = rewriteInactiveDeclarativeVdirPublicContent(resolved, variablesPath, userCfg);
426
+ if (!resolved.includes('url://')) {
427
+ return resolved;
428
+ }
429
+ return expandDeclarativeUrlsInEnvContent(resolved, {
430
+ profile: environment === 'docker' ? 'docker' : 'local',
431
+ currentAppKey: appName,
432
+ variablesPath,
433
+ useEnvironmentScopedResources: Boolean(userCfg.useEnvironmentScopedResources),
434
+ appEnvironmentScopedResources: appScopedFlag,
435
+ remoteServer,
436
+ developerIdRaw,
437
+ traefik: Boolean(userCfg.traefik),
438
+ infraTlsEnabled
439
+ });
440
+ }
441
+
442
+ /** Docker env transformations: ports, infra endpoints, PORT. */
443
+ async function applyDockerTransformations(resolved, variablesPath) {
444
+ resolved = await resolveServicePortsInEnvContent(resolved, 'docker');
445
+ resolved = await rewriteInfraEndpoints(resolved, 'docker');
446
+ const { redisHost, redisPort, dbHost, dbPort } = await getDockerRedisDbEndpoints();
319
447
  let dockerEnv = await getBaseDockerEnv();
320
448
  dockerEnv = applyDockerEnvOverride(dockerEnv);
321
449
  const containerPort = getContainerPortFromPath(variablesPath) ?? getContainerPortFromDockerEnv(dockerEnv) ?? 3000;
322
450
  const envVars = await buildEnvVarMap('docker', null, null, { appPort: containerPort });
451
+ const appDoc = loadVariablesFromPath(variablesPath);
452
+ await mergeDockerManifestPublishedPort(envVars, appDoc);
323
453
  envVars.REDIS_HOST = redisHost;
324
454
  envVars.REDIS_PORT = String(redisPort);
325
455
  envVars.DB_HOST = dbHost;
326
456
  envVars.DB_PORT = String(dbPort);
327
457
  envVars.PORT = String(containerPort);
328
458
  resolved = interpolateEnvVars(resolved, envVars);
459
+ resolved = rewriteDockerManifestPublicPortEnvLine(resolved, envVars, appDoc);
329
460
  return updatePortForDocker(resolved, variablesPath);
330
461
  }
331
462
  /** Environment-specific transformations to resolved content. */
@@ -357,8 +488,22 @@ async function generateEnvContent(appName, secretsPath, environment = 'local', f
357
488
  await secretsEnsure.ensureSecretsFromEnvTemplate(templatePath, { preferredFilePath: preferredPath });
358
489
  }
359
490
  const secrets = await loadSecrets(secretsPath, appName);
360
- let resolved = await resolveKvReferences(template, secrets, environment, secretsPaths, appName);
491
+ const { runEnvKey, effective } = await buildScopedKvContext(appPath, options);
492
+ const scopedKv = { envKey: runEnvKey, effective };
493
+ let resolved = await resolveKvReferences(template, secrets, environment, secretsPaths, appName, scopedKv);
494
+ resolved = await expandDeclarativeUrlsIfPresent(
495
+ resolved,
496
+ appName,
497
+ appPath,
498
+ variablesPath,
499
+ environment,
500
+ Boolean(options.envOnly)
501
+ );
361
502
  resolved = await applyEnvironmentTransformations(resolved, environment, variablesPath);
503
+ if (effective) {
504
+ const idx = redisDbIndexForScopedRunEnv(runEnvKey);
505
+ resolved = applyRedisDbIndexToEnvContent(resolved, idx);
506
+ }
362
507
 
363
508
  return resolved;
364
509
  }
@@ -538,39 +683,57 @@ async function formatAdminSecretsContent(adminObj) {
538
683
  return lines.join('\n');
539
684
  }
540
685
 
541
- /** Generates admin secrets for infrastructure (~/.aifabrix/admin-secrets.env). Uses admin123 when no postgres password. */
542
- async function generateAdminSecretsEnv(secretsPath) {
543
- let secrets;
544
-
686
+ async function loadSecretsOrBootstrapForAdmin(secretsPath) {
545
687
  try {
546
- secrets = await loadSecrets(secretsPath);
688
+ return await loadSecrets(secretsPath);
547
689
  } catch (error) {
548
690
  const defaultSecretsPath = secretsPath || path.join(pathsUtil.getAifabrixHome(), 'secrets.yaml');
549
691
  if (!fs.existsSync(defaultSecretsPath)) {
550
692
  logger.log('Creating default secrets file...');
551
693
  await createDefaultSecrets(defaultSecretsPath);
552
- secrets = await loadSecrets(secretsPath);
553
- } else {
554
- throw error;
694
+ return await loadSecrets(secretsPath);
555
695
  }
696
+ throw error;
556
697
  }
557
- const aifabrixDir = pathsUtil.getAifabrixHome();
558
- const adminEnvPath = path.join(aifabrixDir, 'admin-secrets.env');
559
- if (!fs.existsSync(aifabrixDir)) {
560
- fs.mkdirSync(aifabrixDir, { recursive: true, mode: 0o700 });
698
+ }
699
+
700
+ function getInfraDefaultsMergedForAdmin() {
701
+ try {
702
+ return mergeInfraParameterDefaultsForCli(getInfraParameterCatalog().data, {});
703
+ } catch {
704
+ return {};
561
705
  }
706
+ }
562
707
 
708
+ function buildLocalAdminSecretsObject(secrets, infraDefaults) {
563
709
  const raw = secrets['postgres-passwordKeyVault'];
564
- const postgresPassword = (raw && String(raw).trim()) || 'admin123';
565
-
566
- const adminObj = {
710
+ const relaxed = readRelaxedCatalogDefaults();
711
+ const postgresPassword =
712
+ (raw && String(raw).trim()) ||
713
+ infraDefaults.adminPassword ||
714
+ relaxed.adminPassword ||
715
+ '';
716
+ const pgAdminEmail = infraDefaults.adminEmail || relaxed.adminEmail || '';
717
+ return {
567
718
  POSTGRES_PASSWORD: postgresPassword,
568
- PGADMIN_DEFAULT_EMAIL: 'admin@aifabrix.dev',
719
+ PGADMIN_DEFAULT_EMAIL: pgAdminEmail,
569
720
  PGADMIN_DEFAULT_PASSWORD: postgresPassword,
570
721
  REDIS_HOST: 'local:redis:6379:0:',
571
722
  REDIS_COMMANDER_USER: 'admin',
572
723
  REDIS_COMMANDER_PASSWORD: postgresPassword
573
724
  };
725
+ }
726
+
727
+ /** Generates admin secrets for infrastructure (beside config.yaml, typically ~/.aifabrix/admin-secrets.env). Defaults from infra.parameter.yaml `defaults`. */
728
+ async function generateAdminSecretsEnv(secretsPath) {
729
+ const secrets = await loadSecretsOrBootstrapForAdmin(secretsPath);
730
+ const infraDefaults = getInfraDefaultsMergedForAdmin();
731
+ const adminObj = buildLocalAdminSecretsObject(secrets, infraDefaults);
732
+ const aifabrixDir = pathsUtil.getAifabrixSystemDir();
733
+ const adminEnvPath = path.join(aifabrixDir, 'admin-secrets.env');
734
+ if (!fs.existsSync(aifabrixDir)) {
735
+ fs.mkdirSync(aifabrixDir, { recursive: true, mode: 0o700 });
736
+ }
574
737
  const adminSecrets = await formatAdminSecretsContent(adminObj);
575
738
  fs.writeFileSync(adminEnvPath, adminSecrets, { mode: 0o600 });
576
739
  return adminEnvPath;
@@ -106,9 +106,10 @@ function buildMonitoringEnv(config) {
106
106
  return {
107
107
  'MISO_CONTROLLER_URL': config.controllerUrl || 'https://controller.aifabrix.dev',
108
108
  'MISO_ENVIRONMENT': 'dev',
109
- 'MISO_CLIENTID': 'kv://miso-controller-client-idKeyVault',
110
- 'MISO_CLIENTSECRET': 'kv://miso-controller-client-secretKeyVault',
111
- 'MISO_WEB_SERVER_URL': 'kv://miso-controller-web-server-url'
109
+ // Filled after `aifabrix register <app>`; empty avoids bogus kv placeholders in new apps
110
+ 'MISO_CLIENTID': '',
111
+ 'MISO_CLIENTSECRET': '',
112
+ 'MISO_WEB_SERVER_URL': 'url://miso-controller-public'
112
113
  };
113
114
  }
114
115
 
@@ -99,7 +99,7 @@ function validateCrossSystemJson(crossSystemJson) {
99
99
 
100
100
  /**
101
101
  * Validates ABAC configuration for a parsed datasource.
102
- * Checks dimensions (from config.abac or fieldMappings), crossSystemJson, and rejects legacy crossSystem.
102
+ * Checks dimensions from config.abac, crossSystemJson, and rejects legacy crossSystem.
103
103
  *
104
104
  * @function validateAbac
105
105
  * @param {Object} parsed - Parsed datasource object (after JSON parse)
@@ -134,15 +134,6 @@ function validateAbac(parsed) {
134
134
  ));
135
135
  }
136
136
 
137
- const fieldMappingsDimensions = parsed?.fieldMappings?.dimensions;
138
- if (fieldMappingsDimensions && typeof fieldMappingsDimensions === 'object') {
139
- errors.push(...validateDimensionsObject(
140
- fieldMappingsDimensions,
141
- 'fieldMappings.dimensions',
142
- attributeNames
143
- ));
144
- }
145
-
146
137
  if (abac.crossSystemJson) {
147
138
  errors.push(...validateCrossSystemJson(abac.crossSystemJson));
148
139
  }
@@ -17,6 +17,16 @@ const { publishDatasourceViaPipeline } = require('../api/pipeline.api');
17
17
  const { formatApiError } = require('../utils/api-error-handler');
18
18
  const logger = require('../utils/logger');
19
19
  const { logDataplanePipelineWarning } = require('../utils/dataplane-pipeline-warning');
20
+ const {
21
+ sectionTitle,
22
+ headerKeyValue,
23
+ metadata,
24
+ infoLine,
25
+ formatStatusKeyValue,
26
+ formatBlockingError,
27
+ successGlyph,
28
+ failureGlyph
29
+ } = require('../utils/cli-test-layout-chalk');
20
30
  const {
21
31
  buildResolvedEnvMapForIntegration,
22
32
  resolveConfigurationValues
@@ -64,41 +74,41 @@ async function getDataplaneUrl(controllerUrl, appKey, environment, authConfig) {
64
74
  }
65
75
 
66
76
  /**
67
- * Validate deployment inputs
68
- * @param {string} appKey - Application key
69
- * @param {string} filePath - File path
70
- * @param {Object} options - Options
77
+ * Validate deploy CLI input (file path or datasource key, same rules as `datasource validate`).
78
+ * @param {string} fileOrKey - Path to JSON or datasource `key` under integration/<app>/
71
79
  * @throws {Error} If validation fails
72
80
  */
73
- function validateDeploymentInputs(appKey, filePath) {
74
- if (!appKey || typeof appKey !== 'string') {
75
- throw new Error('Application key is required');
76
- }
77
- if (!filePath || typeof filePath !== 'string') {
78
- throw new Error('File path is required');
81
+ function validateDeployFileOrKeyInput(fileOrKey) {
82
+ if (!fileOrKey || typeof fileOrKey !== 'string' || !fileOrKey.trim()) {
83
+ throw new Error('File path or datasource key is required');
79
84
  }
80
85
  }
81
86
 
82
87
  /**
83
- * Validate and load datasource file
88
+ * Validate and load datasource file (path or datasource key, resolved like `datasource validate`).
84
89
  * @async
85
- * @param {string} filePath - Path to datasource file
90
+ * @param {string} filePathOrKey - Path to datasource JSON or datasource `key`
86
91
  * @returns {Promise<Object>} Datasource configuration
87
92
  * @throws {Error} If validation or loading fails
88
93
  */
89
- async function validateAndLoadDatasourceFile(filePath) {
90
- logger.log(chalk.blue('🔍 Validating datasource file...'));
91
- const validation = await validateDatasourceFile(filePath);
94
+ async function validateAndLoadDatasourceFile(filePathOrKey) {
95
+ logger.log(infoLine(' Validating datasource file'));
96
+ const validation = await validateDatasourceFile(filePathOrKey);
92
97
  if (!validation.valid) {
93
- logger.error(chalk.red('❌ Datasource validation failed:'));
98
+ logger.log('');
99
+ logger.error(formatBlockingError('Datasource validation failed'));
94
100
  validation.errors.forEach(error => {
95
- logger.error(chalk.red(` • ${error}`));
101
+ logger.error(formatBlockingError(error));
96
102
  });
97
103
  throw new Error('Datasource file validation failed');
98
104
  }
99
- logger.log(chalk.green('✓ Datasource file is valid'));
100
105
 
101
- const content = fs.readFileSync(filePath, 'utf8');
106
+ const resolvedPath = validation.resolvedPath;
107
+ logger.log(headerKeyValue('File:', resolvedPath));
108
+ logger.log(`${successGlyph()} ${chalk.white('Datasource file is valid.')}`);
109
+ logger.log('');
110
+
111
+ const content = fs.readFileSync(resolvedPath, 'utf8');
102
112
  try {
103
113
  return JSON.parse(content);
104
114
  } catch (error) {
@@ -116,28 +126,28 @@ async function validateAndLoadDatasourceFile(filePath) {
116
126
  */
117
127
  async function setupDeploymentAuth(controllerUrl, environment, appKey) {
118
128
  const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
119
- logger.log(chalk.blue('🔐 Getting authentication...'));
129
+ logger.log(infoLine(' Resolving authentication'));
120
130
  const authConfig = await getDeploymentAuth(controllerUrl, environment, appKey);
121
- logger.log(chalk.green('Authentication successful'));
131
+ logger.log(`${successGlyph()} ${chalk.white('Authentication ready')}`);
122
132
 
123
- logger.log(chalk.blue('🌐 Resolving dataplane URL...'));
133
+ logger.log(infoLine(' Resolving dataplane URL'));
124
134
  let dataplaneUrl;
125
135
  try {
126
136
  dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
127
- logger.log(chalk.green(`✓ Dataplane URL: ${dataplaneUrl}`));
137
+ logger.log(`${metadata('Dataplane:')} ${chalk.cyan(dataplaneUrl)}`);
128
138
  } catch (error) {
129
- logger.error(chalk.red('Failed to resolve dataplane URL:'), error.message);
130
- logger.error(chalk.gray('\nThe dataplane URL is automatically discovered from the controller.'));
131
- logger.error(chalk.gray('If discovery fails, ensure you are logged in and the controller is accessible:'));
132
- logger.error(chalk.gray(' aifabrix login'));
139
+ logger.error(`${failureGlyph()} ${chalk.red('Failed to resolve dataplane URL:')} ${chalk.red(error.message)}`);
140
+ logger.error(metadata('The dataplane URL is automatically discovered from the controller.'));
141
+ logger.error(metadata('If discovery fails, ensure you are logged in and the controller is accessible:'));
142
+ logger.error(metadata('aifabrix login'));
133
143
  throw error;
134
144
  }
135
145
 
136
146
  // Validate dataplane URL
137
147
  if (!dataplaneUrl || !dataplaneUrl.trim()) {
138
- logger.error(chalk.red('Dataplane URL is empty.'));
139
- logger.error(chalk.gray('The dataplane URL could not be discovered from the controller.'));
140
- logger.error(chalk.gray('Ensure the dataplane service is registered in the controller.'));
148
+ logger.error(`${failureGlyph()} ${chalk.red('Dataplane URL is empty.')}`);
149
+ logger.error(metadata('The dataplane URL could not be discovered from the controller.'));
150
+ logger.error(metadata('Ensure the dataplane service is registered in the controller.'));
141
151
  throw new Error('Dataplane URL is empty');
142
152
  }
143
153
 
@@ -156,30 +166,32 @@ async function setupDeploymentAuth(controllerUrl, environment, appKey) {
156
166
  */
157
167
  async function publishDatasourceToDataplane(dataplaneUrl, systemKey, authConfig, datasourceConfig) {
158
168
  requireBearerForDataplanePipeline(authConfig);
159
- logger.log(chalk.blue('\n🚀 Publishing datasource to dataplane...'));
169
+ logger.log('');
170
+ logger.log(sectionTitle('Publish'));
160
171
  logDataplanePipelineWarning();
161
172
 
162
173
  const publishResponse = await publishDatasourceViaPipeline(dataplaneUrl, systemKey, authConfig, datasourceConfig);
163
174
 
164
175
  if (!publishResponse.success) {
165
176
  const formattedError = publishResponse.formattedError || formatApiError(publishResponse);
166
- logger.error(chalk.red('Publish failed:'));
167
- logger.error(formattedError);
177
+ logger.error(`${failureGlyph()} ${chalk.red('Publish failed:')} ${chalk.red(formattedError)}`);
168
178
 
169
179
  // Show dataplane URL and endpoint information
170
180
  if (publishResponse.errorData && publishResponse.errorData.endpointUrl) {
171
- logger.error(chalk.gray(`\nEndpoint URL: ${publishResponse.errorData.endpointUrl}`));
181
+ logger.error(
182
+ `\n${metadata('Endpoint URL:')} ${chalk.cyan(publishResponse.errorData.endpointUrl)}`
183
+ );
172
184
  } else if (dataplaneUrl) {
173
- logger.error(chalk.gray(`\nDataplane URL: ${dataplaneUrl}`));
174
- logger.error(chalk.gray(`System Key: ${systemKey}`));
185
+ logger.error(`\n${metadata('Dataplane URL:')} ${chalk.cyan(dataplaneUrl)}`);
186
+ logger.error(headerKeyValue('System Key:', systemKey));
175
187
  }
176
188
 
177
- logger.error(chalk.gray('\nFull response for debugging:'));
178
- logger.error(chalk.gray(JSON.stringify(publishResponse, null, 2)));
189
+ logger.error(metadata('\nFull response for debugging:'));
190
+ logger.error(metadata(JSON.stringify(publishResponse, null, 2)));
179
191
  throw new Error(`Dataplane publish failed: ${formattedError}`);
180
192
  }
181
193
 
182
- logger.log(chalk.green('\n✓ Datasource published successfully!'));
194
+ logger.log(`${successGlyph()} ${chalk.white('Configuration published to dataplane.')}`);
183
195
  return publishResponse;
184
196
  }
185
197
 
@@ -190,9 +202,21 @@ async function publishDatasourceToDataplane(dataplaneUrl, systemKey, authConfig,
190
202
  * @param {string} environment - Environment key
191
203
  */
192
204
  function displayDeploymentResults(datasourceConfig, systemKey, environment) {
193
- logger.log(chalk.blue(`\nDatasource: ${datasourceConfig.key || datasourceConfig.displayName}`));
194
- logger.log(chalk.blue(`System: ${systemKey}`));
195
- logger.log(chalk.blue(`Environment: ${environment}`));
205
+ const datasourceLabel = datasourceConfig.key || datasourceConfig.displayName || '(unknown)';
206
+ logger.log('');
207
+ logger.log(sectionTitle('Result'));
208
+ logger.log(headerKeyValue('Datasource:', datasourceLabel));
209
+ logger.log(headerKeyValue('System:', systemKey));
210
+ logger.log(headerKeyValue('Environment:', environment));
211
+ logger.log('');
212
+ logger.log(formatStatusKeyValue('ok', '✔'));
213
+ }
214
+
215
+ function logDatasourceUploadSectionHeader() {
216
+ logger.log('');
217
+ logger.log(sectionTitle('Datasource upload'));
218
+ logger.log(metadata('Publish one datasource JSON to the dataplane'));
219
+ logger.log('');
196
220
  }
197
221
 
198
222
  /**
@@ -201,18 +225,17 @@ function displayDeploymentResults(datasourceConfig, systemKey, environment) {
201
225
  *
202
226
  * @async
203
227
  * @function deployDatasource
204
- * @param {string} appKey - Application key
205
- * @param {string} filePath - Path to datasource JSON file
228
+ * @param {string} fileOrKey - Path to datasource JSON file, or datasource `key` under integration/<app>/ (same resolution as `datasource validate`)
206
229
  * @param {Object} [_options] - Deployment options (reserved)
207
230
  * @returns {Promise<Object>} Deployment result
208
231
  * @throws {Error} If deployment fails
209
232
  */
210
- async function deployDatasource(appKey, filePath, _options) {
233
+ async function deployDatasource(fileOrKey, _options) {
211
234
  const { resolveControllerUrl } = require('../utils/controller-url');
212
235
  const { resolveEnvironment } = require('../core/config');
213
236
  const { displayCommandHeader } = require('../utils/command-header');
214
237
 
215
- validateDeploymentInputs(appKey, filePath);
238
+ validateDeployFileOrKeyInput(fileOrKey);
216
239
 
217
240
  // Resolve controller and environment from config
218
241
  const controllerUrl = await resolveControllerUrl();
@@ -220,13 +243,12 @@ async function deployDatasource(appKey, filePath, _options) {
220
243
 
221
244
  // Display command header
222
245
  displayCommandHeader(controllerUrl, environment);
246
+ logDatasourceUploadSectionHeader();
223
247
 
224
- logger.log(chalk.blue('📋 Deploying datasource...\n'));
225
-
226
- // Validate and load datasource file
227
- const datasourceConfig = await validateAndLoadDatasourceFile(filePath);
248
+ // Validate and load datasource file (resolves key → path like validate)
249
+ const datasourceConfig = await validateAndLoadDatasourceFile(fileOrKey.trim());
228
250
 
229
- // Extract systemKey
251
+ // Extract systemKey (required in JSON; also used as controller app key for external systems)
230
252
  const systemKey = datasourceConfig.systemKey;
231
253
  if (!systemKey) {
232
254
  throw new Error('systemKey is required in datasource configuration');
@@ -237,8 +259,8 @@ async function deployDatasource(appKey, filePath, _options) {
237
259
  resolveConfigurationValues(datasourceConfig.configuration, envMap, secrets, systemKey);
238
260
  }
239
261
 
240
- // Setup authentication and get dataplane URL
241
- const { authConfig, dataplaneUrl } = await setupDeploymentAuth(controllerUrl, environment, appKey);
262
+ // Setup authentication and get dataplane URL (application key matches systemKey for external integrations)
263
+ const { authConfig, dataplaneUrl } = await setupDeploymentAuth(controllerUrl, environment, systemKey);
242
264
 
243
265
  // Publish to dataplane
244
266
  await publishDatasourceToDataplane(dataplaneUrl, systemKey, authConfig, datasourceConfig);