@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
@@ -131,13 +131,16 @@
131
131
  */
132
132
 
133
133
  /**
134
- * Dataplane pipeline upload response (publication result; no uploadId).
134
+ * Dataplane pipeline upload API envelope (makeApiCall / ApiClient).
135
+ * Body in `data` matches dataplane PublicationResult (uploadId, uploadStatus, system, datasources, generateMcpContract, …).
135
136
  * @typedef {Object} PipelineUploadResponse
136
137
  * @property {boolean} success - Request success flag
137
- * @property {Object} [data] - Publication result (system, datasources, warnings)
138
- * @property {string} [data.systemKey] - Published system key
139
- * @property {string[]} [data.datasourceKeys] - Published datasource keys
140
- * @property {string[]} [data.warnings] - Warnings if any
138
+ * @property {Object} [data] - PublicationResult from dataplane (not a generic wrapper with datasourceKeys/warnings)
139
+ * @property {string} [data.uploadId] - Upload / publication id
140
+ * @property {string} [data.uploadStatus] - e.g. published
141
+ * @property {Object} [data.system] - Published external system
142
+ * @property {Object[]} [data.datasources] - Published datasources
143
+ * @property {boolean} [data.generateMcpContract] - MCP generation flag
141
144
  * @property {string} [formattedError] - Formatted error message on failure
142
145
  */
143
146
 
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @fileoverview JSDoc types for unified validation run (POST /api/v1/validation/run).
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ /**
8
+ * @typedef {'externalSystem'|'externalDataSource'} ValidationScope
9
+ */
10
+
11
+ /**
12
+ * @typedef {'test'|'integration'|'e2e'} ValidationRunKind
13
+ */
14
+
15
+ /**
16
+ * Request body for POST /api/v1/validation/run (camelCase; see Dataplane OpenAPI).
17
+ * @typedef {Object} ValidationRunRequestBody
18
+ * @property {string} [systemIdOrKey]
19
+ * @property {string} [systemKey]
20
+ * @property {string[]} [datasourceKeys]
21
+ * @property {string} [datasourceKey]
22
+ * @property {boolean} [explain]
23
+ * @property {boolean} [includeLiveChecks]
24
+ * @property {boolean} [includeLiveDebug]
25
+ * @property {boolean} [showCapabilities]
26
+ * @property {boolean} [explainMetrics]
27
+ * @property {boolean} [explainCertification]
28
+ * @property {boolean} [includeMetrics]
29
+ * @property {boolean} [includeCertification]
30
+ * @property {Object} [systemConfig]
31
+ * @property {Object[]} [datasourceConfigs]
32
+ * @property {ValidationScope} [validationScope]
33
+ * @property {ValidationRunKind} [runType]
34
+ * @property {Object} [payloadTemplate]
35
+ * @property {boolean} [asyncRun]
36
+ * @property {Object} [e2eOptions]
37
+ * @property {boolean} [includeDebug]
38
+ */
39
+
40
+ /**
41
+ * Minimal DatasourceTestRun shape for CLI exit / display (full schema in lib/schema).
42
+ * @typedef {Object} DatasourceTestRunLike
43
+ * @property {string} [reportVersion]
44
+ * @property {string} datasourceKey
45
+ * @property {string} systemKey
46
+ * @property {ValidationRunKind} runType
47
+ * @property {'ok'|'warn'|'fail'|'skipped'} status
48
+ * @property {'minimal'|'partial'|'full'} [reportCompleteness]
49
+ * @property {string} [runId]
50
+ * @property {string} [testRunId]
51
+ * @property {Object} [certificate]
52
+ * @property {string} [certificate.status]
53
+ * @property {Object} [developer]
54
+ */
55
+
56
+ module.exports = {};
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @fileoverview Unified dataplane validation API — POST /api/v1/validation/run and poll GET.
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const { ApiClient } = require('./index');
8
+
9
+ const POST_PATH = '/api/v1/validation/run';
10
+
11
+ function buildClientCredentialHeaders(authConfig) {
12
+ if (!authConfig || typeof authConfig !== 'object') return null;
13
+ if (authConfig.type !== 'client-credentials') return null;
14
+ if (!authConfig.clientId || !authConfig.clientSecret) return null;
15
+ return {
16
+ 'x-client-id': String(authConfig.clientId),
17
+ 'x-client-secret': String(authConfig.clientSecret)
18
+ };
19
+ }
20
+
21
+ /**
22
+ * Normalize auth for dataplane: Bearer user token, x-client-token app token, or API key as Bearer.
23
+ * @param {Object} authConfig - Auth configuration
24
+ * @returns {Object} Auth for ApiClient
25
+ */
26
+ function normalizeDataplaneAuth(authConfig) {
27
+ if (!authConfig || typeof authConfig !== 'object') {
28
+ throw new Error('authConfig is required');
29
+ }
30
+ if (authConfig.token) {
31
+ return authConfig;
32
+ }
33
+ if (authConfig.apiKey) {
34
+ return { ...authConfig, token: authConfig.apiKey, type: authConfig.type || 'bearer' };
35
+ }
36
+ throw new Error(
37
+ 'Validation run requires Bearer token or API key. Run \'aifabrix login\' or configure API key.'
38
+ );
39
+ }
40
+
41
+ /**
42
+ * @requiresPermission {Dataplane} external-system:read
43
+ * @async
44
+ * @function postValidationRun
45
+ * @param {string} dataplaneUrl - Dataplane base URL
46
+ * @param {Object} authConfig - Authentication (token or apiKey)
47
+ * @param {import('./types/validation-run.types').ValidationRunRequestBody} body - Request JSON body
48
+ * @returns {Promise<Object>} ApiClient result: { success, data, status, ... }
49
+ */
50
+ async function postValidationRun(dataplaneUrl, authConfig, body) {
51
+ const hdrs = buildClientCredentialHeaders(authConfig);
52
+ const clientAuth = hdrs ? {} : normalizeDataplaneAuth(authConfig);
53
+ const client = new ApiClient(dataplaneUrl, clientAuth);
54
+ return client.post(POST_PATH, { body, headers: hdrs || undefined });
55
+ }
56
+
57
+ /**
58
+ * @requiresPermission {Dataplane} external-system:read
59
+ * @async
60
+ * @function getValidationRun
61
+ * @param {string} dataplaneUrl - Dataplane base URL
62
+ * @param {Object} authConfig - Authentication
63
+ * @param {string} testRunId - Poll id from 202 / envelope
64
+ * @returns {Promise<Object>} ApiClient result
65
+ */
66
+ async function getValidationRun(dataplaneUrl, authConfig, testRunId) {
67
+ if (!testRunId || typeof testRunId !== 'string') {
68
+ throw new Error('testRunId is required for validation run poll');
69
+ }
70
+ const hdrs = buildClientCredentialHeaders(authConfig);
71
+ const clientAuth = hdrs ? {} : normalizeDataplaneAuth(authConfig);
72
+ const client = new ApiClient(dataplaneUrl, clientAuth);
73
+ const path = `${POST_PATH}/${encodeURIComponent(testRunId)}`;
74
+ return client.get(path, { headers: hdrs || undefined });
75
+ }
76
+
77
+ /**
78
+ * Extract async poll id from POST 202 body or partial DatasourceTestRun.
79
+ * @param {Object} data - Parsed JSON body
80
+ * @returns {string|null}
81
+ */
82
+ function extractTestRunId(data) {
83
+ if (!data || typeof data !== 'object') return null;
84
+ if (typeof data.testRunId === 'string' && data.testRunId.trim()) return data.testRunId.trim();
85
+ if (data.testRunId && typeof data.testRunId === 'object') {
86
+ const id = data.testRunId.id || data.testRunId.key;
87
+ if (typeof id === 'string' && id.trim()) return id.trim();
88
+ }
89
+ return null;
90
+ }
91
+
92
+ module.exports = {
93
+ postValidationRun,
94
+ getValidationRun,
95
+ extractTestRunId,
96
+ normalizeDataplaneAuth,
97
+ buildClientCredentialHeaders,
98
+ POST_PATH
99
+ };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @fileoverview Single reusable module for unified validation POST + optional poll.
3
+ *
4
+ * Used by datasource- and system-scoped CLI flows to enforce plan §9 behavior consistently.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { extractTestRunId } = require('./validation-run.api');
10
+ const { postValidationRunWithTransportRetry } = require('../utils/validation-run-post-retry');
11
+ const { pollValidationRunUntilComplete } = require('../utils/validation-run-poll');
12
+
13
+ /**
14
+ * POST /api/v1/validation/run and (when async) poll GET until reportCompleteness is full.
15
+ *
16
+ * @param {Object} opts
17
+ * @param {string} opts.dataplaneUrl
18
+ * @param {Object} opts.authConfig
19
+ * @param {Object} opts.body
20
+ * @param {number} opts.timeoutMs
21
+ * @param {boolean} opts.useAsync
22
+ * @param {boolean} opts.noAsync
23
+ * @returns {Promise<{ envelope: Object|null, apiError: Object|null, pollTimedOut: boolean, incompleteNoAsync: boolean }>}
24
+ */
25
+ /* eslint-disable max-lines-per-function, max-statements, complexity -- POST + poll orchestration */
26
+ async function postValidationRunAndOptionalPoll(opts) {
27
+ const { dataplaneUrl, authConfig, body, timeoutMs, useAsync, noAsync } = opts;
28
+ const started = Date.now();
29
+ const postRes = await postValidationRunWithTransportRetry(dataplaneUrl, authConfig, body);
30
+ if (!postRes.success) {
31
+ return {
32
+ envelope: null,
33
+ apiError: postRes,
34
+ pollTimedOut: false,
35
+ incompleteNoAsync: false
36
+ };
37
+ }
38
+
39
+ let envelope = postRes.data;
40
+ const httpStatus = postRes.status;
41
+ const testRunId = extractTestRunId(envelope);
42
+ const completeness = envelope && envelope.reportCompleteness;
43
+ const needsPoll =
44
+ httpStatus === 202 ||
45
+ (testRunId && completeness && completeness !== 'full' && useAsync);
46
+
47
+ if (needsPoll && testRunId) {
48
+ const elapsed = Date.now() - started;
49
+ const remaining = Math.max(0, timeoutMs - elapsed);
50
+ const pollResult = await pollValidationRunUntilComplete({
51
+ dataplaneUrl,
52
+ authConfig,
53
+ testRunId,
54
+ budgetMs: remaining
55
+ });
56
+ if (!pollResult.lastApiResult || !pollResult.lastApiResult.success) {
57
+ return {
58
+ envelope: pollResult.envelope,
59
+ apiError: pollResult.lastApiResult,
60
+ pollTimedOut: pollResult.timedOut,
61
+ incompleteNoAsync: false
62
+ };
63
+ }
64
+ envelope = pollResult.envelope;
65
+ if (pollResult.timedOut) {
66
+ return {
67
+ envelope,
68
+ apiError: null,
69
+ pollTimedOut: true,
70
+ incompleteNoAsync: false
71
+ };
72
+ }
73
+ }
74
+
75
+ if (
76
+ noAsync &&
77
+ envelope &&
78
+ envelope.reportCompleteness &&
79
+ envelope.reportCompleteness !== 'full'
80
+ ) {
81
+ return {
82
+ envelope,
83
+ apiError: null,
84
+ pollTimedOut: false,
85
+ incompleteNoAsync: true
86
+ };
87
+ }
88
+
89
+ return {
90
+ envelope,
91
+ apiError: null,
92
+ pollTimedOut: false,
93
+ incompleteNoAsync: false
94
+ };
95
+ }
96
+ /* eslint-enable max-lines-per-function, max-statements, complexity */
97
+
98
+ module.exports = { postValidationRunAndOptionalPoll };
99
+
package/lib/app/config.js CHANGED
@@ -135,7 +135,7 @@ async function generateEnvTemplateFile(appPath, appName, config, existingEnv) {
135
135
  envTemplate = envResult.template;
136
136
 
137
137
  if (envResult.warnings.length > 0) {
138
- logger.log(chalk.yellow('\nāš ļø Environment conversion warnings:'));
138
+ logger.log(chalk.yellow('\n⚠ Environment conversion warnings:'));
139
139
  envResult.warnings.forEach(warning => logger.log(chalk.yellow(` - ${warning}`)));
140
140
  }
141
141
  } else {
@@ -69,9 +69,9 @@ async function displayAppUrlFromController(controllerUrl, envKey, appKey, authCo
69
69
  url = buildAppUrlFromControllerAndPort(controllerUrl, port);
70
70
  }
71
71
  if (url) {
72
- logger.log(chalk.green(` āœ“ App running at ${url}`));
72
+ logger.log(chalk.green(` āœ” App running at ${url}`));
73
73
  } else {
74
- logger.log(chalk.blue(' āœ“ App deployed. Get URL from controller dashboard.'));
74
+ logger.log(chalk.blue(' āœ” App deployed. Get URL from controller dashboard.'));
75
75
  }
76
76
  }
77
77
 
package/lib/app/deploy.js CHANGED
@@ -1,3 +1,4 @@
1
+ const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * AI Fabrix Builder Application Deployment Module
3
4
  *
@@ -79,7 +80,7 @@ async function validatePushPrerequisites(appName, registry) {
79
80
  */
80
81
  async function executePush(appName, registry, tags) {
81
82
  if (await pushUtils.checkACRAuthentication(registry)) {
82
- logger.log(chalk.green(`āœ“ Already authenticated with ${registry}`));
83
+ logger.log(formatSuccessLine(`Already authenticated with ${registry}`));
83
84
  } else {
84
85
  await pushUtils.authenticateACR(registry);
85
86
  }
@@ -98,7 +99,7 @@ async function executePush(appName, registry, tags) {
98
99
  * @param {string} appName - Application name
99
100
  */
100
101
  function verifyPushResult(tags, registry, appName) {
101
- logger.log(chalk.green(`\nāœ“ Successfully pushed ${tags.length} tag(s) to ${registry}`));
102
+ logger.log(formatSuccessParagraph(`Successfully pushed ${tags.length} tag(s) to ${registry}`));
102
103
  logger.log(chalk.gray(`Image: ${registry}/${appName}:*`));
103
104
  logger.log(chalk.gray(`Tags: ${tags.join(', ')}`));
104
105
  }
@@ -173,7 +174,7 @@ async function generateAndValidateManifest(appName, options = {}) {
173
174
  * @param {string} manifestPath - Path to manifest file
174
175
  */
175
176
  function displayDeploymentInfo(manifest, manifestPath) {
176
- logger.log(chalk.green(`āœ“ Manifest generated: ${manifestPath}`));
177
+ logger.log(formatSuccessLine(`Manifest generated: ${manifestPath}`));
177
178
  logger.log(chalk.blue(` Key: ${manifest.key}`));
178
179
  logger.log(chalk.blue(` Display Name: ${manifest.displayName}`));
179
180
  logger.log(chalk.blue(` Image: ${manifest.image}`));
@@ -216,8 +217,8 @@ function displayDeploymentResults(result) {
216
217
  logger.log(chalk.blue(` Deployment ID: ${result.deploymentId}`));
217
218
  }
218
219
  if (result.status) {
219
- const statusIcon = result.status.status === 'completed' ? 'āœ…' :
220
- result.status.status === 'failed' ? 'āŒ' : 'ā³';
220
+ const statusIcon = result.status.status === 'completed' ? 'āœ”' :
221
+ result.status.status === 'failed' ? 'āœ–' : 'ā³';
221
222
  logger.log(chalk.blue(` Status: ${statusIcon} ${result.status.status}`));
222
223
  }
223
224
  }
@@ -368,7 +369,7 @@ async function executeStandardDeployment(appName, options) {
368
369
  }
369
370
 
370
371
  /**
371
- * Tries external deploy when builder/<app> does not exist but integration/<app> does.
372
+ * Tries external deploy when builder/<appKey> does not exist but integration/<systemKey> does.
372
373
  * @async
373
374
  * @param {string} appName - Application name
374
375
  * @param {Object} options - Deployment options
@@ -1,3 +1,4 @@
1
+ const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * Application Display Utilities
3
4
  *
@@ -64,7 +65,7 @@ function displayWebappSuccess(appName, config, envConversionMessage) {
64
65
  * @param {string} appPath - Application path
65
66
  */
66
67
  function displaySuccessMessage(appName, config, envConversionMessage, hasAppFiles = false, appPath = null) {
67
- logger.log(chalk.green('\nāœ“ Application created successfully!'));
68
+ logger.log(formatSuccessParagraph('Application created successfully!'));
68
69
  logger.log(chalk.blue(`\nApplication: ${appName}`));
69
70
 
70
71
  // Determine location based on app type
@@ -1,3 +1,4 @@
1
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * Application Dockerfile Generation
3
4
  *
@@ -74,7 +75,7 @@ async function generateAndCopyDockerfile(appPath, dockerfilePath, config) {
74
75
  const buildConfig = config.build || {};
75
76
  const generatedPath = await build.generateDockerfile(appPath, config.language, config, buildConfig);
76
77
  await fs.copyFile(generatedPath, dockerfilePath);
77
- logger.log(chalk.green('āœ“ Generated Dockerfile from template'));
78
+ logger.log(formatSuccessLine('Generated Dockerfile from template'));
78
79
  return dockerfilePath;
79
80
  }
80
81
 
@@ -90,7 +91,7 @@ async function generateDockerfileForApp(appName, options = {}) {
90
91
  try {
91
92
  const { isExternal } = await detectAppType(appName);
92
93
  if (isExternal) {
93
- logger.log(chalk.yellow('āš ļø External systems don\'t require Dockerfiles. Skipping...'));
94
+ logger.log(chalk.yellow('⚠ External systems don\'t require Dockerfiles. Skipping...'));
94
95
  return null;
95
96
  }
96
97
  } catch (error) {
package/lib/app/down.js CHANGED
@@ -10,6 +10,7 @@
10
10
  */
11
11
 
12
12
  'use strict';
13
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
13
14
 
14
15
  const chalk = require('chalk');
15
16
  const { exec } = require('child_process');
@@ -104,7 +105,7 @@ async function downApp(appName, options = {}) {
104
105
  logger.log(chalk.yellow(`Removing volume ${volumeName}...`));
105
106
  try {
106
107
  await execAsync(`docker volume rm -f ${volumeName}`);
107
- logger.log(chalk.green(`āœ“ Volume ${volumeName} removed`));
108
+ logger.log(formatSuccessLine(`Volume ${volumeName} removed`));
108
109
  } catch (volErr) {
109
110
  // Swallow errors for missing volume; provide neutral message
110
111
  logger.log(chalk.gray(`Volume ${volumeName} not found or already removed`));
@@ -1,3 +1,4 @@
1
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * Application Helper Utilities
3
4
  *
@@ -21,7 +22,7 @@ const { getIntegrationPath, getBuilderPath } = require('../utils/paths');
21
22
  *
22
23
  * @async
23
24
  * @param {string} appName - Application or external system name
24
- * @throws {Error} If integration/<appName> or builder/<appName> already exists
25
+ * @throws {Error} If integration/<systemKey> or builder/<appKey> already exists
25
26
  */
26
27
  async function validateAppOrExternalNameNotExists(appName) {
27
28
  const integrationPath = getIntegrationPath(appName);
@@ -104,7 +105,7 @@ async function handleGitHubWorkflows(options, config) {
104
105
  }
105
106
  );
106
107
 
107
- logger.log(chalk.green('āœ“ Generated GitHub Actions workflows:'));
108
+ logger.log(formatSuccessLine('Generated GitHub Actions workflows:'));
108
109
  workflowFiles.forEach(file => logger.log(chalk.gray(` - ${file}`)));
109
110
  }
110
111
 
@@ -157,7 +158,7 @@ async function processTemplateFiles(template, appPath, appName, options, config)
157
158
 
158
159
  await validateTemplate(template);
159
160
  const copiedFiles = await copyTemplateFiles(template, appPath);
160
- logger.log(chalk.green(`āœ“ Copied ${copiedFiles.length} file(s) from template '${template}'`));
161
+ logger.log(formatSuccessLine(`Copied ${copiedFiles.length} file(s) from template '${template}'`));
161
162
  const { updateTemplateVariables } = require('../utils/template-helpers');
162
163
  await updateTemplateVariables(appPath, appName, options, config);
163
164
  }
@@ -190,7 +191,7 @@ async function updateVariablesForAppFlag(appPath, appName) {
190
191
  writeConfigFile(variablesPath, variables);
191
192
  } catch (error) {
192
193
  if (!error.message.includes('not found')) {
193
- logger.warn(chalk.yellow(`āš ļø Warning: Could not update application config: ${error.message}`));
194
+ logger.warn(chalk.yellow(`⚠ Warning: Could not update application config: ${error.message}`));
194
195
  }
195
196
  }
196
197
  }
@@ -239,7 +240,7 @@ async function setupAppFiles(appName, appPath, config, options) {
239
240
 
240
241
  const language = await getLanguageForAppFiles(config.language || options.language, appPath);
241
242
  const copiedFiles = await copyAppFiles(language, appsPath);
242
- logger.log(chalk.green(`āœ“ Copied ${copiedFiles.length} application file(s) to apps/${appName}/`));
243
+ logger.log(formatSuccessLine(`Copied ${copiedFiles.length} application file(s) to apps/${appName}/`));
243
244
  }
244
245
 
245
246
  module.exports = {
package/lib/app/index.js CHANGED
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  const fs = require('fs').promises;
13
- const { readExistingEnv } = require('../core/env-reader');
13
+ const envReaderModule = require('../core/env-reader');
14
14
  const build = require('../build');
15
15
  const appRun = require('./run');
16
16
  const { promptForOptions } = require('./prompts');
@@ -34,6 +34,29 @@ const {
34
34
  const path = require('path');
35
35
  const secretsEnsure = require('../core/secrets-ensure');
36
36
 
37
+ /**
38
+ * Ensures secrets from env.template. On EACCES (e.g. secrets path under a read-only tree), uses getPrimaryUserSecretsLocalPath().
39
+ * @param {string} envTemplatePath - Path to env.template
40
+ * @returns {Promise<void>}
41
+ */
42
+ async function ensureSecretsFromNewAppEnvTemplate(envTemplatePath) {
43
+ try {
44
+ await secretsEnsure.ensureSecretsFromEnvTemplate(envTemplatePath, {});
45
+ } catch (err) {
46
+ if (err.code === 'ENOENT') {
47
+ return;
48
+ }
49
+ if (err.code === 'EACCES' || (err.message && String(err.message).includes('EACCES'))) {
50
+ const { getPrimaryUserSecretsLocalPath } = require('../utils/paths');
51
+ await secretsEnsure.ensureSecretsFromEnvTemplate(envTemplatePath, {
52
+ preferredFilePath: getPrimaryUserSecretsLocalPath()
53
+ });
54
+ return;
55
+ }
56
+ throw err;
57
+ }
58
+ }
59
+
37
60
  /**
38
61
  * Creates new application with scaffolded configuration files
39
62
  * Prompts for configuration options and generates builder/ folder structure
@@ -125,19 +148,15 @@ async function generateApplicationFiles(finalAppPath, appName, config, options)
125
148
  await fs.mkdir(finalAppPath, { recursive: true });
126
149
  await processTemplateFiles(options.template, finalAppPath, appName, options, config);
127
150
 
128
- const existingEnv = await readExistingEnv(process.cwd());
151
+ const existingEnv = await envReaderModule.readExistingEnv(finalAppPath);
129
152
  const envConversionMessage = existingEnv
130
- ? '\nāœ“ Found existing .env file - sensitive values will be converted to kv:// references'
153
+ ? '\nāœ” Found existing .env file - sensitive values will be converted to kv:// references'
131
154
  : '';
132
155
 
133
156
  await generateConfigFiles(finalAppPath, appName, config, existingEnv);
134
157
 
135
158
  const envTemplatePath = path.join(finalAppPath, 'env.template');
136
- try {
137
- await secretsEnsure.ensureSecretsFromEnvTemplate(envTemplatePath, {});
138
- } catch (err) {
139
- if (err.code !== 'ENOENT') throw err;
140
- }
159
+ await ensureSecretsFromNewAppEnvTemplate(envTemplatePath);
141
160
 
142
161
  // Generate external system files if type is external
143
162
  if (config.type === 'external') {
package/lib/app/list.js CHANGED
@@ -1,3 +1,4 @@
1
+ const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * AI Fabrix Builder - App List Command
3
4
  *
@@ -85,7 +86,7 @@ function extractApplications(response) {
85
86
  extractWrappedPaginatedItems(apiResponse);
86
87
 
87
88
  if (!applications) {
88
- logger.error(chalk.red('āŒ Invalid response: expected data array or items array'));
89
+ logger.error(formatBlockingError('Invalid response: expected data array or items array'));
89
90
  logger.error(chalk.gray('\nAPI response type:'), typeof apiResponse);
90
91
  logger.error(chalk.gray('API response:'), JSON.stringify(apiResponse, null, 2));
91
92
  logger.error(chalk.gray('\nFull response for debugging:'));
@@ -166,11 +167,11 @@ function displayApplications(applications, environment, controllerUrl) {
166
167
  applications.forEach((app) => {
167
168
  const isExternal = app.configuration?.type === 'external';
168
169
  const externalIcon = isExternal ? 'šŸ”— ' : '';
169
- const hasPipeline = app.configuration?.pipeline?.isActive ? 'āœ“' : 'āœ—';
170
+ const hasPipeline = app.configuration?.pipeline?.isActive ? 'āœ”' : 'āœ–';
170
171
  const urlAndPort = formatUrlAndPort(app);
171
172
  logger.log(`${externalIcon}${hasPipeline} ${chalk.cyan(app.key)} - ${app.displayName} (${app.status || 'unknown'})${urlAndPort}`);
172
173
  });
173
- logger.log(chalk.gray(' To show details for an app: aifabrix app show <appKey>\n'));
174
+ logger.log(chalk.gray(' To show details for an app: aifabrix app show <app>\n'));
174
175
  }
175
176
 
176
177
  /**
@@ -246,7 +247,7 @@ async function getListAuthToken(controllerUrl, config) {
246
247
  const authResult = await tryGetTokenFromController(controllerUrl);
247
248
  if (!authResult || !authResult.token) {
248
249
  // No token found for explicitly provided controller URL
249
- logger.error(chalk.red(`āŒ No authentication token found for controller: ${controllerUrl}`));
250
+ logger.error(formatBlockingError(`No authentication token found for controller: ${controllerUrl}`));
250
251
  logger.error(chalk.gray('Please login to this controller using: aifabrix login'));
251
252
  process.exit(1);
252
253
  // Return to prevent further execution in tests where process.exit is mocked
@@ -300,7 +301,7 @@ async function listApplications(options = {}) {
300
301
 
301
302
  const controllerUrl = options.controller || (await resolveControllerUrl());
302
303
  if (!controllerUrl) {
303
- logger.error(chalk.red('āŒ Controller URL is required. Run "aifabrix login" to set the controller URL in config.yaml'));
304
+ logger.error(formatBlockingError('Controller URL is required. Run "aifabrix login" to set the controller URL in config.yaml'));
304
305
  process.exit(1);
305
306
  return;
306
307
  }
@@ -321,7 +322,7 @@ async function listApplications(options = {}) {
321
322
  const applications = handleListResponse(response, actualControllerUrl);
322
323
  displayApplications(applications, environment, actualControllerUrl);
323
324
  } catch (error) {
324
- logger.error(chalk.red(`āŒ Failed to list applications from controller: ${actualControllerUrl}`));
325
+ logger.error(formatBlockingError(`Failed to list applications from controller: ${actualControllerUrl}`));
325
326
  logger.error(chalk.gray(`Error: ${error.message}`));
326
327
  process.exit(1);
327
328
  }
package/lib/app/push.js CHANGED
@@ -1,3 +1,4 @@
1
+ const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * Application Push Utilities
3
4
  *
@@ -143,7 +144,7 @@ async function validatePushConfig(registry, imageName, appName) {
143
144
  */
144
145
  async function authenticateWithRegistry(registry) {
145
146
  if (await pushUtils.checkACRAuthentication(registry)) {
146
- logger.log(chalk.green(`āœ“ Already authenticated with ${registry}`));
147
+ logger.log(formatSuccessLine(`Already authenticated with ${registry}`));
147
148
  } else {
148
149
  await pushUtils.authenticateACR(registry);
149
150
  }
@@ -189,7 +190,7 @@ async function pushImageTags(imageName, registry, tags) {
189
190
  * @param {Array<string>} tags - Image tags
190
191
  */
191
192
  function displayPushResults(registry, imageName, tags) {
192
- logger.log(chalk.green(`\nāœ“ Successfully pushed ${tags.length} tag(s) to ${registry}`));
193
+ logger.log(formatSuccessParagraph(`Successfully pushed ${tags.length} tag(s) to ${registry}`));
193
194
  logger.log(chalk.gray(`Image: ${registry}/${imageName}:*`));
194
195
  logger.log(chalk.gray(`Tags: ${tags.join(', ')}`));
195
196
  }
@@ -208,7 +209,7 @@ async function pushApp(appName, options = {}) {
208
209
  try {
209
210
  const { isExternal } = await detectAppType(appName);
210
211
  if (isExternal) {
211
- logger.log(chalk.yellow('āš ļø External systems don\'t require Docker images. Skipping push...'));
212
+ logger.log(chalk.yellow('⚠ External systems don\'t require Docker images. Skipping push...'));
212
213
  return;
213
214
  }
214
215
  } catch (error) {
@@ -1,3 +1,4 @@
1
+ const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * AI Fabrix Builder - App Register Command
3
4
  *
@@ -22,6 +23,8 @@ const {
22
23
  const { checkAuthentication } = require('../utils/app-register-auth');
23
24
  const { callRegisterApi } = require('../utils/app-register-api');
24
25
  const { displayRegistrationResults, getEnvironmentPrefix } = require('../utils/app-register-display');
26
+ const pathsUtil = require('../utils/paths');
27
+ const { refreshUrlsLocalRegistryFromBuilder } = require('../utils/urls-local-registry');
25
28
 
26
29
  /**
27
30
  * Build registration data payload from app configuration
@@ -62,8 +65,8 @@ function buildRegistrationData(appConfig, options) {
62
65
  registrationData.image = imageValue;
63
66
  }
64
67
 
65
- // URL: always set when we have port so controller DB has it. Precedence: --url, variables (app.url, deployment.dataplaneUrl, deployment.appUrl), else http://localhost:{localPort|port}
66
- const portForUrl = appConfig.localPort ?? appConfig.port;
68
+ // URL: default uses manifest listen port; host mapping uses url:// in env.template at resolve time
69
+ const portForUrl = appConfig.port;
67
70
  if (portForUrl) {
68
71
  registrationData.url = options.url || appConfig.url || `http://localhost:${portForUrl}`;
69
72
  }
@@ -97,15 +100,15 @@ async function saveLocalCredentials(responseData, apiUrl) {
97
100
  // Regenerate .env file with updated credentials
98
101
  try {
99
102
  await generateEnvFile(registeredAppKey, null, 'local');
100
- logger.log(chalk.green('āœ“ .env file updated with new credentials'));
103
+ logger.log(formatSuccessLine('.env file updated with new credentials'));
101
104
  } catch (error) {
102
- logger.warn(chalk.yellow(`āš ļø Could not regenerate .env file: ${error.message}`));
105
+ logger.warn(chalk.yellow(`⚠ Could not regenerate .env file: ${error.message}`));
103
106
  }
104
107
 
105
- logger.log(chalk.green('\nāœ“ Credentials saved to ~/.aifabrix/secrets.local.yaml'));
106
- logger.log(chalk.green('āœ“ env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL\n'));
108
+ logger.log(formatSuccessParagraph('Credentials saved to ~/.aifabrix/secrets.local.yaml'));
109
+ logger.log(formatSuccessLine('env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL\n'));
107
110
  } catch (error) {
108
- logger.warn(chalk.yellow(`āš ļø Could not save credentials locally: ${error.message}`));
111
+ logger.warn(chalk.yellow(`⚠ Could not save credentials locally: ${error.message}`));
109
112
  }
110
113
  }
111
114
 
@@ -179,6 +182,12 @@ async function registerApplication(appKey, options = {}) {
179
182
  registrationData
180
183
  );
181
184
 
185
+ try {
186
+ refreshUrlsLocalRegistryFromBuilder(pathsUtil.getProjectRoot());
187
+ } catch (error) {
188
+ logger.warn(chalk.yellow(`⚠ Could not refresh URLs registry: ${error.message}`));
189
+ }
190
+
182
191
  // Save credentials and display results (pass display name we sent so output shows it when API returns key as displayName)
183
192
  await saveLocalCredentials(responseData, authConfig.apiUrl);
184
193
  displayRegistrationResults(responseData, authConfig.apiUrl, environment, registrationData.displayName);