@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
@@ -0,0 +1,149 @@
1
+ /**
2
+ * @fileoverview Shared option builders + help text for datasource test CLI commands.
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ function datasourceTestHelpAfter() {
8
+ return `
9
+ Examples:
10
+ $ aifabrix datasource test hubspot-users
11
+ $ aifabrix datasource test hubspot-users --app test-e2e-hubspot -v
12
+ $ aifabrix datasource test hubspot-users -a test-e2e-hubspot --debug full
13
+ $ aifabrix datasource test hubspot-users --json
14
+
15
+ Notes:
16
+ - For integration pipeline tests, use:
17
+ aifabrix datasource test-integration <datasourceKey>
18
+ - For system-level rollup across datasources, use:
19
+ aifabrix test <systemKey>
20
+ `;
21
+ }
22
+
23
+ function datasourceTestIntegrationHelpAfter() {
24
+ return `
25
+ Examples:
26
+ $ aifabrix datasource test-integration hubspot-users
27
+ $ aifabrix datasource test-integration hubspot-users --app test-e2e-hubspot --debug
28
+ $ aifabrix datasource test-integration hubspot-users -a test-e2e-hubspot -e tst --timeout 60000
29
+
30
+ Notes:
31
+ - For structural/policy validation, use:
32
+ aifabrix datasource test <datasourceKey>
33
+ - For E2E capability execution, use:
34
+ aifabrix datasource test-e2e <datasourceKey>
35
+ - For system-level integration rollup across datasources, use:
36
+ aifabrix test-integration <systemKey>
37
+ `;
38
+ }
39
+
40
+ function datasourceTestE2eHelpAfter() {
41
+ return `
42
+ Examples:
43
+ $ aifabrix datasource test-e2e hubspot-users
44
+ $ aifabrix datasource test-e2e hubspot-users --app test-e2e-hubspot -v --debug
45
+ $ aifabrix datasource test-e2e hubspot-users -a test-e2e-hubspot --no-async
46
+ $ aifabrix datasource test-e2e hubspot-users read
47
+
48
+ Notes:
49
+ - For structural/policy validation, use:
50
+ aifabrix datasource test <datasourceKey>
51
+ - For integration pipeline tests, use:
52
+ aifabrix datasource test-integration <datasourceKey>
53
+ - For system-level E2E rollup across datasources, use:
54
+ aifabrix test-e2e <systemKey>
55
+ `;
56
+ }
57
+
58
+ /**
59
+ * @param {object} cmd - Commander command (chainable)
60
+ * @returns {object}
61
+ */
62
+ function attachDatasourceWatchOptions(cmd) {
63
+ return cmd
64
+ .option(
65
+ '--watch',
66
+ 'Re-run when watched files change (debounced; integration tree + optional paths)'
67
+ )
68
+ .option(
69
+ '--watch-path <path>',
70
+ 'Extra file or directory to watch (repeatable)',
71
+ (value, previous) => (Array.isArray(previous) ? previous : []).concat(value),
72
+ []
73
+ )
74
+ .option('--watch-application-yaml', 'Include integration/<app>/application.yaml in the watch set')
75
+ .option('--watch-ci', 'Exit after the first run with the normal exit code (CI one-shot)')
76
+ .option('--watch-full-diff', 'Print full before/after fingerprint lines when the result changes');
77
+ }
78
+
79
+ /**
80
+ * Shared options for datasource-level `test`, `test-integration`, and `test-e2e`.
81
+ *
82
+ * Registration order matches intended `--help` order: core run flags → payload/timeout →
83
+ * `--sync` (pre-run publish) → machine/exit modifiers → async → watch (last before `--help`).
84
+ * Callers must append `addHelpText('after', …)` **after** any command-specific options (e.g. E2E).
85
+ *
86
+ * @param {object} cmd - Commander command (chainable)
87
+ * @param {Object} opts
88
+ * @param {boolean} [opts.includeNoAsync]
89
+ * @param {boolean} [opts.includePayload]
90
+ * @param {string} [opts.appHelp]
91
+ * @param {string} [opts.verboseHelp]
92
+ * @param {string} [opts.debugHelp]
93
+ * @param {string} [opts.timeoutHelp]
94
+ * @param {string} [opts.timeoutDefault]
95
+ * @returns {object}
96
+ */
97
+ function attachDatasourceTestCommonOptions(cmd, opts) {
98
+ const includeNoAsync = opts.includeNoAsync === true;
99
+ const includePayload = opts.includePayload !== false;
100
+
101
+ cmd
102
+ .option(
103
+ '-a, --app <app>',
104
+ opts.appHelp ||
105
+ 'Integration folder name (optional: resolve from cwd or datasource key if single match)'
106
+ )
107
+ .option('-e, --env <env>', 'Environment: dev, tst, or pro')
108
+ .option('-v, --verbose', opts.verboseHelp || 'Explain mode and detailed output where available')
109
+ .option(
110
+ '-d, --debug [level]',
111
+ opts.debugHelp ||
112
+ 'includeDebug on request; TTY appendix: summary (default), full, or raw (not with --json)'
113
+ );
114
+
115
+ if (includePayload) {
116
+ cmd.option('-p, --payload <file>', 'Optional custom payload file (sets payloadTemplate on request)');
117
+ }
118
+
119
+ cmd
120
+ .option(
121
+ '--timeout <ms>',
122
+ opts.timeoutHelp || 'Aggregate timeout for POST + polls (ms)',
123
+ opts.timeoutDefault || '30000'
124
+ )
125
+ .option(
126
+ '--sync',
127
+ 'Publish this datasource JSON from disk to the dataplane before running the test (requires login / same auth as upload)'
128
+ )
129
+ .option('--json', 'Print raw DatasourceTestRun JSON to stdout')
130
+ .option('--summary', 'Print compact summary line')
131
+ .option('--warnings-as-errors', 'Exit 1 when root status is warn')
132
+ .option('--require-cert', 'Exit 2 when certificate missing or not_passed');
133
+
134
+ if (includeNoAsync) {
135
+ cmd.option('--no-async', 'Do not poll; fail if report is not complete in first response');
136
+ }
137
+
138
+ attachDatasourceWatchOptions(cmd);
139
+ return cmd;
140
+ }
141
+
142
+ module.exports = {
143
+ datasourceTestHelpAfter,
144
+ datasourceTestIntegrationHelpAfter,
145
+ datasourceTestE2eHelpAfter,
146
+ attachDatasourceWatchOptions,
147
+ attachDatasourceTestCommonOptions
148
+ };
149
+
@@ -0,0 +1,129 @@
1
+ const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
2
+ /**
3
+ * @fileoverview Shared CLI handling for unified validation (DatasourceTestRun + exit matrix).
4
+ * @author AI Fabrix Team
5
+ * @version 2.0.0
6
+ */
7
+
8
+ const chalk = require('chalk');
9
+ const logger = require('../utils/logger');
10
+ const { computeExitCodeFromDatasourceTestRun, exitCodeForPollTimeout } = require('../utils/datasource-test-run-exit');
11
+ const { analyzeCapabilityScope } = require('../utils/datasource-test-run-capability-scope');
12
+ const ttyLog = require('../utils/datasource-test-run-tty-log');
13
+ const { logEnvelopeForInteractiveCli } = ttyLog;
14
+
15
+ /**
16
+ * Build unified CLI result from integration test return shape.
17
+ * @param {Object} r - runDatasourceTestIntegration result
18
+ * @returns {{ envelope: Object|null, apiError: Object|null, pollTimedOut: boolean, incompleteNoAsync: boolean }}
19
+ */
20
+ function unifiedCliResultFromIntegrationReturn(r) {
21
+ const meta = r.runMeta || {};
22
+ return {
23
+ apiError: meta.apiError || null,
24
+ pollTimedOut: meta.pollTimedOut === true,
25
+ incompleteNoAsync: meta.incompleteNoAsync === true,
26
+ envelope: r.datasourceTestRun || null
27
+ };
28
+ }
29
+
30
+ function logApiError(apiError) {
31
+ logger.error(
32
+ formatBlockingError('Dataplane request failed:'),
33
+ apiError.formattedError || apiError.error || 'Request failed'
34
+ );
35
+ if (apiError.status) {
36
+ logger.error(chalk.gray(` HTTP ${apiError.status}`));
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Print unified validation outcome and return exit code (plan §3.14 watch — no process.exit).
42
+ * @param {Object} result - From runUnifiedDatasourceValidation
43
+ * @param {Object} options - CLI flags
44
+ * @returns {number}
45
+ */
46
+ function finalizeUnifiedValidationResult(result, options = {}) {
47
+ if (result.apiError) {
48
+ logApiError(result.apiError);
49
+ return 3;
50
+ }
51
+ if (result.pollTimedOut) {
52
+ logger.error(formatBlockingError('Report incomplete: timeout'));
53
+ return exitCodeForPollTimeout(result.envelope);
54
+ }
55
+ if (result.incompleteNoAsync) {
56
+ logger.error(
57
+ chalk.red(
58
+ '✖ Report incomplete: async polling disabled (--no-async) but server returned partial/minimal report.'
59
+ )
60
+ );
61
+ return 3;
62
+ }
63
+
64
+ const envelope = result.envelope;
65
+ logEnvelopeForInteractiveCli(envelope, options);
66
+
67
+ let exitCode = computeExitCodeFromDatasourceTestRun(envelope, {
68
+ warningsAsErrors: options.warningsAsErrors === true,
69
+ requireCert: options.requireCert === true
70
+ });
71
+ const scope = analyzeCapabilityScope(envelope, options.requestedCapabilityKey);
72
+ if (options.strictCapabilityScope === true && scope.violated) {
73
+ exitCode = Math.max(exitCode, 1);
74
+ }
75
+ if (
76
+ exitCode === 2 &&
77
+ options.requireCert &&
78
+ !envelope.certificate
79
+ ) {
80
+ logger.error(formatBlockingError('Certification not returned; cannot verify.'));
81
+ }
82
+ return exitCode;
83
+ }
84
+
85
+ /**
86
+ * Handle unified validation result: logs, stdout JSON, process.exit.
87
+ * @param {Object} result - From runUnifiedDatasourceValidation
88
+ * @param {Object} options - CLI flags
89
+ * @returns {void} Exits process
90
+ */
91
+ function exitFromUnifiedValidationResult(result, options = {}) {
92
+ process.exit(finalizeUnifiedValidationResult(result, options));
93
+ }
94
+
95
+ /**
96
+ * Compute exit code after integration CLI display (no process.exit; plan §3.14 watch).
97
+ * @param {Object} integrationResult - From runDatasourceTestIntegration
98
+ * @param {Object} [exitOpts]
99
+ * @returns {number}
100
+ */
101
+ function finalizeAfterIntegrationDisplay(integrationResult, exitOpts = {}) {
102
+ const env = integrationResult.datasourceTestRun;
103
+ if (!env) {
104
+ return integrationResult.success ? 0 : 1;
105
+ }
106
+ return computeExitCodeFromDatasourceTestRun(env, {
107
+ warningsAsErrors: exitOpts.warningsAsErrors === true,
108
+ requireCert: exitOpts.requireCert === true
109
+ });
110
+ }
111
+
112
+ /**
113
+ * Exit after integration CLI when not using raw unified output modes.
114
+ * @param {Object} integrationResult - From runDatasourceTestIntegration
115
+ * @param {Object} [exitOpts]
116
+ */
117
+ function exitAfterIntegrationDisplay(integrationResult, exitOpts = {}) {
118
+ process.exit(finalizeAfterIntegrationDisplay(integrationResult, exitOpts));
119
+ }
120
+
121
+ module.exports = {
122
+ exitFromUnifiedValidationResult,
123
+ finalizeUnifiedValidationResult,
124
+ emitReportVersionDiagnostics: ttyLog.emitReportVersionDiagnostics,
125
+ emitCapabilityScopeDiagnostics: ttyLog.emitCapabilityScopeDiagnostics,
126
+ unifiedCliResultFromIntegrationReturn,
127
+ finalizeAfterIntegrationDisplay,
128
+ exitAfterIntegrationDisplay
129
+ };
@@ -1,40 +1,103 @@
1
1
  /**
2
2
  * AI Fabrix Builder - Datasource Commands
3
3
  *
4
- * Handles datasource validation, listing, comparison, and deployment
5
- * Commands: datasource validate, datasource list, datasource diff, datasource upload
4
+ * Handles datasource validation, listing, comparison, deployment, and online validation runs.
5
+ * Subcommands `test`, `test-integration`, and `test-e2e` call the dataplane unified validation API; permissions are summarized in `docs/commands/permissions.md`.
6
6
  *
7
7
  * @fileoverview Datasource management commands for AI Fabrix Builder
8
8
  * @author AI Fabrix Team
9
9
  * @version 2.0.0
10
10
  */
11
11
 
12
+ const path = require('path');
12
13
  const chalk = require('chalk');
13
14
  const logger = require('../utils/logger');
15
+ const { sectionTitle, headerKeyValue, metadata, formatSuccessLine, formatBlockingError } = require('../utils/cli-test-layout-chalk');
14
16
  const { validateDatasourceFile } = require('../datasource/validate');
15
17
  const { listDatasources } = require('../datasource/list');
16
18
  const { compareDatasources } = require('../datasource/diff');
17
19
  const { deployDatasource } = require('../datasource/deploy');
18
- const { runDatasourceTestIntegration } = require('../datasource/test-integration');
19
- const { runDatasourceTestE2E } = require('../datasource/test-e2e');
20
20
  const { runLogViewer } = require('../datasource/log-viewer');
21
- const { displayIntegrationTestResults, displayE2EResults } = require('../utils/external-system-display');
21
+ const {
22
+ setupDatasourceTestCommand,
23
+ setupDatasourceTestIntegrationCommand,
24
+ setupDatasourceTestE2ECommand
25
+ } = require('./datasource-unified-test-cli');
26
+
27
+ const DATASOURCE_HELP_AFTER = `
28
+ Subcommands:
29
+ validate <file-or-key> Validate datasource JSON (path or datasource key under integration/<app>/)
30
+ list List datasources (env from config)
31
+ upload <file-or-key> Deploy one datasource JSON to the dataplane (path or key; systemKey in file)
32
+ diff Compare two datasource JSON files
33
+ test <key> Structural/policy validation via unified dataplane API (DatasourceTestRun)
34
+ test-integration / test-e2e Integration or E2E run via the same unified validation API
35
+ log-integration / log-e2e Show saved test logs
36
+ `;
37
+
38
+ const DATASOURCE_VALIDATE_HELP_AFTER = `
39
+ Examples:
40
+ $ aifabrix datasource validate test-e2e-hubspot-users
41
+ $ aifabrix datasource validate integration/myapp/myapp-datasource-contacts.json
42
+ $ aifabrix datasource validate ./test-e2e-hubspot-datasource-users.json
43
+ $ aifabrix datasource validate /path/to/system-datasource-entity.json
44
+ $ af ds validate ../integration/hubspot/hubspot-datasource-deals.json
45
+ `;
46
+
47
+ const DATASOURCE_UPLOAD_HELP_AFTER = `
48
+ Examples:
49
+ $ aifabrix datasource upload test-e2e-hubspot-users
50
+ $ aifabrix datasource upload integration/myapp/myapp-datasource-contacts.json
51
+ $ aifabrix datasource upload ./test-e2e-hubspot-datasource-users.json
52
+ $ aifabrix datasource upload /path/to/system-datasource-entity.json
53
+ $ af ds upload ../integration/hubspot/hubspot-datasource-deals.json
54
+ `;
55
+
56
+ /**
57
+ * TTY layout for local datasource JSON validation (aligned with cli-test-layout-chalk).
58
+ * @param {{ valid: boolean, errors: string[], resolvedPath: string }} result
59
+ * @param {string} trimmed - original CLI argument
60
+ * @param {boolean} showMapping - show Key + File when key resolved to a path
61
+ */
62
+ function logDatasourceValidateOutcome(result, trimmed, showMapping) {
63
+ logger.log('');
64
+ logger.log(sectionTitle('Datasource validation'));
65
+ logger.log(metadata('Offline — JSON schema and integration wiring'));
66
+ logger.log('');
67
+ if (!result.valid) {
68
+ logger.log(headerKeyValue('File:', result.resolvedPath));
69
+ logger.log('');
70
+ logger.log(formatBlockingError('Datasource file has errors:'));
71
+ result.errors.forEach(error => logger.log(chalk.red(` • ${error}`)));
72
+ return;
73
+ }
74
+ if (showMapping) {
75
+ logger.log(headerKeyValue('Key:', trimmed));
76
+ logger.log(headerKeyValue('File:', result.resolvedPath));
77
+ } else {
78
+ logger.log(headerKeyValue('File:', result.resolvedPath));
79
+ }
80
+ logger.log('');
81
+ logger.log(formatSuccessLine('Datasource file is valid.'));
82
+ }
22
83
 
23
84
  function setupDatasourceValidateCommand(datasource) {
24
- datasource.command('validate <file>')
25
- .description('Validate external datasource JSON file')
26
- .action(async(file) => {
85
+ datasource.command('validate <file-or-key>')
86
+ .description('Validate datasource JSON (file path or datasource key under integration/<app>/)')
87
+ .addHelpText('after', DATASOURCE_VALIDATE_HELP_AFTER)
88
+ .action(async(fileOrKey) => {
27
89
  try {
28
- const result = await validateDatasourceFile(file);
29
- if (result.valid) {
30
- logger.log(chalk.green(`\n✓ Datasource file is valid: ${file}`));
31
- } else {
32
- logger.log(chalk.red(`\n✗ Datasource file has errors: ${file}`));
33
- result.errors.forEach(error => logger.log(chalk.red(` • ${error}`)));
90
+ const trimmed = fileOrKey.trim();
91
+ const result = await validateDatasourceFile(trimmed);
92
+ const resolvedPath = result.resolvedPath;
93
+ const argResolved = path.resolve(trimmed);
94
+ const showMapping = resolvedPath && argResolved !== resolvedPath && trimmed !== resolvedPath;
95
+ logDatasourceValidateOutcome(result, trimmed, showMapping);
96
+ if (!result.valid) {
34
97
  process.exit(1);
35
98
  }
36
99
  } catch (error) {
37
- logger.error(chalk.red('Validation failed:'), error.message);
100
+ logger.error(formatBlockingError('Validation failed:'), error.message);
38
101
  process.exit(1);
39
102
  }
40
103
  });
@@ -42,12 +105,12 @@ function setupDatasourceValidateCommand(datasource) {
42
105
 
43
106
  function setupDatasourceListCommand(datasource) {
44
107
  datasource.command('list')
45
- .description('List datasources from environment (uses environment from config.yaml)')
108
+ .description('List datasources for environment in config')
46
109
  .action(async() => {
47
110
  try {
48
111
  await listDatasources({});
49
112
  } catch (error) {
50
- logger.error(chalk.red('Failed to list datasources:'), error.message);
113
+ logger.error(formatBlockingError('Failed to list datasources:'), error.message);
51
114
  process.exit(1);
52
115
  }
53
116
  });
@@ -55,92 +118,26 @@ function setupDatasourceListCommand(datasource) {
55
118
 
56
119
  function setupDatasourceDiffCommand(datasource) {
57
120
  datasource.command('diff <file1> <file2>')
58
- .description('Compare two datasource configuration files (for dataplane)')
121
+ .description('Diff two datasource JSON files')
59
122
  .action(async(file1, file2) => {
60
123
  try {
61
124
  await compareDatasources(file1, file2);
62
125
  } catch (error) {
63
- logger.error(chalk.red('Diff failed:'), error.message);
126
+ logger.error(formatBlockingError('Diff failed:'), error.message);
64
127
  process.exit(1);
65
128
  }
66
129
  });
67
130
  }
68
131
 
69
132
  function setupDatasourceUploadCommand(datasource) {
70
- datasource.command('upload <myapp> <file>')
71
- .description('Upload datasource to dataplane')
72
- .action(async(myapp, file, options) => {
133
+ datasource.command('upload <file-or-key>')
134
+ .description('Deploy datasource JSON to dataplane (file path or datasource key under integration/<app>/)')
135
+ .addHelpText('after', DATASOURCE_UPLOAD_HELP_AFTER)
136
+ .action(async(fileOrKey, options) => {
73
137
  try {
74
- await deployDatasource(myapp, file, options);
75
- } catch (error) {
76
- logger.error(chalk.red('❌ Upload failed:'), error.message);
77
- process.exit(1);
78
- }
79
- });
80
- }
81
-
82
- function setupDatasourceTestIntegrationCommand(datasource) {
83
- datasource.command('test-integration <datasourceKey>')
84
- .description('Run integration (config) test for one datasource via dataplane pipeline')
85
- .option('-a, --app <appKey>', 'App key (optional: resolve from cwd or datasource key if single match)')
86
- .option('-p, --payload <file>', 'Path to custom test payload file')
87
- .option('-e, --env <env>', 'Environment: dev, tst, or pro')
88
- .option('-v, --verbose', 'Show detailed step and validation output')
89
- .option('--debug', 'Include debug output and write log to integration/<appKey>/logs/')
90
- .option('--timeout <ms>', 'Request timeout in milliseconds', '30000')
91
- .action(async(datasourceKey, options) => {
92
- try {
93
- const result = await runDatasourceTestIntegration(datasourceKey, {
94
- app: options.app,
95
- payload: options.payload,
96
- environment: options.env,
97
- debug: options.debug,
98
- timeout: options.timeout
99
- });
100
- displayIntegrationTestResults({
101
- systemKey: result.systemKey || 'unknown',
102
- datasourceResults: [result],
103
- success: result.success
104
- }, options.verbose);
105
- if (!result.success) process.exit(1);
106
- } catch (error) {
107
- logger.error(chalk.red('❌ Integration test failed:'), error.message);
108
- process.exit(1);
109
- }
110
- });
111
- }
112
-
113
- function setupDatasourceTestE2ECommand(datasource) {
114
- datasource.command('test-e2e <datasourceKey>')
115
- .description('Run E2E test for one datasource (config, credential, sync, data, CIP) via dataplane')
116
- .option('-a, --app <appKey>', 'App key (default: resolve from cwd if inside integration/<appKey>/)')
117
- .option('-e, --env <env>', 'Environment: dev, tst, or pro')
118
- .option('-v, --verbose', 'Show detailed step output and poll progress')
119
- .option('--debug', 'Include debug output and write log to integration/<appKey>/logs/')
120
- .option('--test-crud', 'Enable CRUD lifecycle test (body testCrud: true)')
121
- .option('--record-id <id>', 'Record ID for test (body recordId)')
122
- .option('--no-cleanup', 'Disable cleanup after test (body cleanup: false)')
123
- .option('--primary-key-value <value|@path>', 'Primary key value or path to JSON file (e.g. @pk.json) for body primaryKeyValue')
124
- .option('--no-async', 'Use sync mode (no polling); single POST, no asyncRun')
125
- .action(async(datasourceKey, options) => {
126
- try {
127
- const data = await runDatasourceTestE2E(datasourceKey, {
128
- app: options.app,
129
- environment: options.env,
130
- debug: options.debug,
131
- verbose: options.verbose,
132
- async: options.async !== false,
133
- testCrud: options.testCrud,
134
- recordId: options.recordId,
135
- cleanup: options.cleanup,
136
- primaryKeyValue: options.primaryKeyValue
137
- });
138
- displayE2EResults(data, options.verbose);
139
- const steps = data.steps || data.completedActions || [];
140
- const failed = data.success === false || steps.some(s => s.success === false || s.error);
141
- if (failed) process.exit(1);
138
+ await deployDatasource(fileOrKey, options);
142
139
  } catch (error) {
143
- logger.error(chalk.red(' E2E test failed:'), error.message);
140
+ logger.error(formatBlockingError('Upload failed:'), error.message);
144
141
  process.exit(1);
145
142
  }
146
143
  });
@@ -148,8 +145,11 @@ function setupDatasourceTestE2ECommand(datasource) {
148
145
 
149
146
  function setupDatasourceLogE2ECommand(datasource) {
150
147
  datasource.command('log-e2e <datasourceKey>')
151
- .description('Display latest or specified E2E test log in readable format')
152
- .option('-a, --app <appKey>', 'App key (optional: resolve from cwd or datasource key if single match)')
148
+ .description('Show E2E test log (latest or --file)')
149
+ .option(
150
+ '-a, --app <app>',
151
+ 'Integration folder name (optional: resolve from cwd or datasource key if single match)'
152
+ )
153
153
  .option('-f, --file <path>', 'Path to log file (default: latest in app logs folder)')
154
154
  .action(async(datasourceKey, options) => {
155
155
  try {
@@ -159,7 +159,7 @@ function setupDatasourceLogE2ECommand(datasource) {
159
159
  logType: 'test-e2e'
160
160
  });
161
161
  } catch (error) {
162
- logger.error(chalk.red('log-e2e failed:'), error.message);
162
+ logger.error(formatBlockingError('log-e2e failed:'), error.message);
163
163
  process.exit(1);
164
164
  }
165
165
  });
@@ -167,8 +167,11 @@ function setupDatasourceLogE2ECommand(datasource) {
167
167
 
168
168
  function setupDatasourceLogIntegrationCommand(datasource) {
169
169
  datasource.command('log-integration <datasourceKey>')
170
- .description('Display latest or specified integration test log in readable format')
171
- .option('-a, --app <appKey>', 'App key (optional: resolve from cwd or datasource key if single match)')
170
+ .description('Show integration test log (latest or --file)')
171
+ .option(
172
+ '-a, --app <app>',
173
+ 'Integration folder name (optional: resolve from cwd or datasource key if single match)'
174
+ )
172
175
  .option('-f, --file <path>', 'Path to log file (default: latest in app logs folder)')
173
176
  .action(async(datasourceKey, options) => {
174
177
  try {
@@ -178,7 +181,7 @@ function setupDatasourceLogIntegrationCommand(datasource) {
178
181
  logType: 'test-integration'
179
182
  });
180
183
  } catch (error) {
181
- logger.error(chalk.red('log-integration failed:'), error.message);
184
+ logger.error(formatBlockingError('log-integration failed:'), error.message);
182
185
  process.exit(1);
183
186
  }
184
187
  });
@@ -189,7 +192,10 @@ function setupDatasourceLogIntegrationCommand(datasource) {
189
192
  * @param {Command} program - Commander program instance
190
193
  */
191
194
  function setupDatasourceCommands(program) {
192
- const datasource = program.command('datasource').description('Manage external data sources');
195
+ const datasource = program
196
+ .command('datasource')
197
+ .description('Datasource JSON: validate, list, deploy, test, logs')
198
+ .addHelpText('after', DATASOURCE_HELP_AFTER);
193
199
  if (typeof datasource.alias === 'function') {
194
200
  datasource.alias('ds');
195
201
  }
@@ -197,6 +203,7 @@ function setupDatasourceCommands(program) {
197
203
  setupDatasourceListCommand(datasource);
198
204
  setupDatasourceDiffCommand(datasource);
199
205
  setupDatasourceUploadCommand(datasource);
206
+ setupDatasourceTestCommand(datasource);
200
207
  setupDatasourceTestIntegrationCommand(datasource);
201
208
  setupDatasourceTestE2ECommand(datasource);
202
209
  setupDatasourceLogE2ECommand(datasource);
@@ -1,3 +1,4 @@
1
+ const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * Deployment list commands – list deployments for environment or for an app
3
4
  * Uses GET .../deployments and GET .../applications/{appKey}/deployments.
@@ -97,7 +98,7 @@ async function runDeploymentList(options = {}) {
97
98
  );
98
99
  displayDeploymentList(extractDeployments(response), environment, authResult.controllerUrl);
99
100
  } catch (error) {
100
- logger.error(chalk.red(`❌ Failed to list deployments: ${error.message}`));
101
+ logger.error(formatBlockingError(`Failed to list deployments: ${error.message}`));
101
102
  process.exit(1);
102
103
  }
103
104
  }
@@ -133,13 +134,13 @@ function displayAppDeploymentList(deployments, appKey, environment, controllerUr
133
134
  async function resolveDeploymentListContext(options) {
134
135
  const controllerUrl = options.controller || (await resolveControllerUrl());
135
136
  if (!controllerUrl) {
136
- logger.error(chalk.red('Controller URL is required. Run "aifabrix login" first.'));
137
+ logger.error(formatBlockingError('Controller URL is required. Run "aifabrix login" first.'));
137
138
  process.exit(1);
138
139
  }
139
140
  const environment = options.environment || (await resolveEnvironment());
140
141
  const authResult = await getDeploymentListAuth(controllerUrl);
141
142
  if (!authResult || !authResult.token) {
142
- logger.error(chalk.red(`❌ No authentication token for controller: ${controllerUrl}`));
143
+ logger.error(formatBlockingError(`No authentication token for controller: ${controllerUrl}`));
143
144
  logger.error(chalk.gray('Run: aifabrix login'));
144
145
  process.exit(1);
145
146
  }
@@ -158,7 +159,7 @@ async function resolveDeploymentListContext(options) {
158
159
  */
159
160
  async function runAppDeploymentList(appKey, options = {}) {
160
161
  if (!appKey || typeof appKey !== 'string') {
161
- logger.error(chalk.red('Application key is required.'));
162
+ logger.error(formatBlockingError('Application key is required.'));
162
163
  process.exit(1);
163
164
  return;
164
165
  }
@@ -180,7 +181,7 @@ async function runAppDeploymentList(appKey, options = {}) {
180
181
  authResult.controllerUrl
181
182
  );
182
183
  } catch (error) {
183
- logger.error(chalk.red(`❌ Failed to list deployments for ${appKey}: ${error.message}`));
184
+ logger.error(formatBlockingError(`Failed to list deployments for ${appKey}: ${error.message}`));
184
185
  process.exit(1);
185
186
  }
186
187
  }