@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,473 @@
1
+ /**
2
+ * Grouped human-readable output for developer config (`dev show`, after `set-id` / `set-format`).
3
+ *
4
+ * @fileoverview Dev show display (sections, remote gating, cert mismatch hints)
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const chalk = require('chalk');
14
+ const config = require('../core/config');
15
+ const devConfig = require('../utils/dev-config');
16
+ const logger = require('../utils/logger');
17
+ const paths = require('../utils/paths');
18
+ const { getUrlsLocalYamlPath } = require('../utils/urls-local-registry');
19
+ const {
20
+ getCertDir,
21
+ getCertValidNotAfter,
22
+ getCertSubjectDeveloperId,
23
+ developerIdsMatchNumeric
24
+ } = require('../utils/dev-cert-helper');
25
+
26
+ /** En dash for unset / empty values (readable empty state). */
27
+ const EM = '\u2013';
28
+ const LABEL_W = 18;
29
+
30
+ /**
31
+ * @param {unknown} v
32
+ * @returns {boolean}
33
+ */
34
+ function isUnset(v) {
35
+ return v === null || v === undefined || v === '';
36
+ }
37
+
38
+ /**
39
+ * @param {unknown} v
40
+ * @returns {string}
41
+ */
42
+ function cell(v) {
43
+ return isUnset(v) ? EM : String(v);
44
+ }
45
+
46
+ /**
47
+ * @param {string} label
48
+ * @param {unknown} value
49
+ */
50
+ function logRow(label, value) {
51
+ logger.log(` ${label.padEnd(LABEL_W)} ${cell(value)}`);
52
+ }
53
+
54
+ /**
55
+ * @param {string|undefined|null} raw
56
+ * @returns {{ display: string|null, effective: string|null }}
57
+ */
58
+ async function getSecretsDisplay(raw) {
59
+ let effective = null;
60
+ if (raw && typeof raw === 'string' && raw.trim()) {
61
+ try {
62
+ const remoteDevAuth = require('../utils/remote-dev-auth');
63
+ const resolved = await remoteDevAuth.resolveSharedSecretsEndpoint(raw);
64
+ if (typeof resolved === 'string' && resolved.trim() && resolverAndRawDiffer(resolved, raw)) {
65
+ effective = resolved.trim();
66
+ }
67
+ } catch {
68
+ // Show raw only
69
+ }
70
+ }
71
+ const display =
72
+ raw && typeof raw === 'string' && raw.trim()
73
+ ? raw.trim()
74
+ : null;
75
+ return { display, effective };
76
+ }
77
+
78
+ /**
79
+ * @param {string} resolved
80
+ * @param {string} raw
81
+ * @returns {boolean}
82
+ */
83
+ function resolverAndRawDiffer(resolved, raw) {
84
+ return resolved.trim() !== raw.trim();
85
+ }
86
+
87
+ /**
88
+ * Prefer HTTPS(S) effective endpoint for display when present.
89
+ * @param {string|null} secretsMain
90
+ * @param {string|null} secretsEffective
91
+ * @returns {string|null}
92
+ */
93
+ function secretsApiCell(secretsMain, secretsEffective) {
94
+ const eff = secretsEffective && String(secretsEffective).trim();
95
+ if (eff && /^https?:\/\//i.test(eff)) {
96
+ return eff;
97
+ }
98
+ if (secretsMain && String(secretsMain).trim()) {
99
+ return secretsMain.trim();
100
+ }
101
+ return null;
102
+ }
103
+
104
+ /**
105
+ * Same directory as config.yaml / urls.local.yaml (nested ~/.aifabrix when AIFABRIX_HOME is $HOME).
106
+ * @returns {string}
107
+ */
108
+ function resolveConfigDir() {
109
+ return paths.getConfigDirForPaths();
110
+ }
111
+
112
+ /**
113
+ * @param {string|null|undefined} user
114
+ * @param {string|null|undefined} host
115
+ * @returns {string|null}
116
+ */
117
+ function formatSshLine(user, host) {
118
+ const u = user && String(user).trim() ? user : null;
119
+ const h = host && String(host).trim() ? host : null;
120
+ if (!u && !h) {
121
+ return null;
122
+ }
123
+ return `${u ?? EM}@${h ?? EM}`;
124
+ }
125
+
126
+ /**
127
+ * @param {boolean} dockerTlsSkipVerify
128
+ * @returns {string}
129
+ */
130
+ function tlsVerifyCell(dockerTlsSkipVerify) {
131
+ return dockerTlsSkipVerify ? 'OFF' : 'ON 🔒';
132
+ }
133
+
134
+ /**
135
+ * Infra / compose TLS mode from ~/.aifabrix `tlsEnabled` (not Docker TLS verify).
136
+ * @param {boolean} tlsEnabled
137
+ * @returns {string}
138
+ */
139
+ function infraTlsSslCell(tlsEnabled) {
140
+ return tlsEnabled ? 'ON 🔒' : 'OFF 🕐';
141
+ }
142
+
143
+ /**
144
+ * @param {Date} d
145
+ * @returns {string} YYYY-MM-DD (local)
146
+ */
147
+ function formatYmdLocal(d) {
148
+ const y = d.getFullYear();
149
+ const m = String(d.getMonth() + 1).padStart(2, '0');
150
+ const day = String(d.getDate()).padStart(2, '0');
151
+ return `${y}-${m}-${day}`;
152
+ }
153
+
154
+ /**
155
+ * @param {string|null|undefined} remoteUrl
156
+ * @returns {string}
157
+ */
158
+ function hostFromRemoteUrl(remoteUrl) {
159
+ if (!remoteUrl || typeof remoteUrl !== 'string') {
160
+ return '';
161
+ }
162
+ try {
163
+ const u = new URL(remoteUrl.trim());
164
+ return u.hostname || '';
165
+ } catch {
166
+ return '';
167
+ }
168
+ }
169
+
170
+ /**
171
+ * @param {string} devIdStr
172
+ * @returns {string} e.g. dev02
173
+ */
174
+ function devProfileHandle(devIdStr) {
175
+ const s = String(devIdStr);
176
+ if (/^[0-9]+$/.test(s)) {
177
+ return `dev${s.padStart(2, '0')}`;
178
+ }
179
+ return `dev${s}`;
180
+ }
181
+
182
+ /**
183
+ * @param {string} devIdStr
184
+ * @param {boolean} hasRemote
185
+ * @param {string|null|undefined} remoteServer
186
+ * @returns {string}
187
+ */
188
+ function buildHeaderLine(devIdStr, hasRemote, remoteServer) {
189
+ let line = '\n🔧 AI Fabrix • Developer Configuration';
190
+ if (hasRemote) {
191
+ const host = hostFromRemoteUrl(remoteServer);
192
+ const who = devProfileHandle(devIdStr);
193
+ line += host ? ` (${who} @ ${host})` : ` (${who})`;
194
+ }
195
+ return `${line}\n`;
196
+ }
197
+
198
+ /**
199
+ * @param {string} certDir
200
+ * @param {string} devId
201
+ * @returns {{ status: string, mismatch: boolean, certDevDigits: string|null, notAfter: Date|null, daysRemaining: number|null }}
202
+ */
203
+ function computeRemoteIdentity(certDir, devId) {
204
+ const certPath = path.join(certDir, 'cert.pem');
205
+ const certExists = fs.existsSync(certPath);
206
+ const notAfter = getCertValidNotAfter(certDir);
207
+ const certDevDigits = getCertSubjectDeveloperId(certDir);
208
+
209
+ let status;
210
+ if (!certExists) {
211
+ status = 'MISSING';
212
+ } else if (!notAfter) {
213
+ status = 'UNREADABLE';
214
+ } else if (notAfter.getTime() < Date.now()) {
215
+ status = 'EXPIRED';
216
+ } else {
217
+ status = 'VALID';
218
+ }
219
+
220
+ const mismatch =
221
+ certExists &&
222
+ certDevDigits !== null &&
223
+ !developerIdsMatchNumeric(devId, certDevDigits);
224
+
225
+ let daysRemaining = null;
226
+ if (notAfter && notAfter.getTime() >= Date.now()) {
227
+ daysRemaining = Math.ceil((notAfter.getTime() - Date.now()) / 86400000);
228
+ }
229
+
230
+ return { status, mismatch, certDevDigits, notAfter, daysRemaining };
231
+ }
232
+
233
+ /**
234
+ * @param {{ status: string, mismatch: boolean }} identity
235
+ * @returns {string}
236
+ */
237
+ function formatCertificateStatus(identity) {
238
+ if (identity.status === 'VALID') {
239
+ return identity.mismatch ? 'VALID ⚠' : 'VALID ✔';
240
+ }
241
+ if (identity.status === 'EXPIRED') {
242
+ return 'EXPIRED';
243
+ }
244
+ if (identity.status === 'MISSING') {
245
+ return 'MISSING';
246
+ }
247
+ return 'UNREADABLE';
248
+ }
249
+
250
+ /**
251
+ * @param {Date} notAfter
252
+ * @param {string} status
253
+ * @param {number|null} daysRemaining
254
+ * @returns {string|null}
255
+ */
256
+ function formatExpiresLine(notAfter, status, daysRemaining) {
257
+ if (!notAfter) {
258
+ return null;
259
+ }
260
+ const ymd = formatYmdLocal(notAfter);
261
+ if (status === 'EXPIRED') {
262
+ return `${ymd} (expired)`;
263
+ }
264
+ if (daysRemaining !== null) {
265
+ return `${ymd} (${daysRemaining} days)`;
266
+ }
267
+ return ymd;
268
+ }
269
+
270
+ /**
271
+ * @param {string} devId
272
+ * @param {{ certDevDigits: string|null }} identity
273
+ */
274
+ function logMismatchBlock(devId, identity) {
275
+ logger.log('');
276
+ logger.log(chalk.yellow('⚠ Developer mismatch'));
277
+ logRow('Config ID', devId);
278
+ const certShown = identity.certDevDigits;
279
+ logRow('Certificate ID', certShown);
280
+ logger.log('');
281
+ logger.log(' Fix:');
282
+ logger.log(` ${chalk.bold('af dev sync')}`);
283
+ }
284
+
285
+ /**
286
+ * @param {string} devIdStr
287
+ * @param {boolean} hasRemote
288
+ * @param {object} remote
289
+ * @param {string} certDir
290
+ */
291
+ function logRemoteAndIdentity(devIdStr, hasRemote, remote, certDir) {
292
+ if (!hasRemote) {
293
+ return;
294
+ }
295
+ logger.log('');
296
+ logger.log('🌐 Remote');
297
+ logRow('Server', remote.server);
298
+ logRow('Docker', remote.dockerEndpoint);
299
+ logRow('SSH', formatSshLine(remote.syncSshUser, remote.syncSshHost));
300
+ logRow('TLS Verify', tlsVerifyCell(remote.dockerTlsSkip));
301
+
302
+ const identity = computeRemoteIdentity(certDir, devIdStr);
303
+ logger.log('');
304
+ logger.log('🔐 Identity');
305
+ logRow('Certificate', formatCertificateStatus(identity));
306
+ logRow('Developer ID', identity.certDevDigits);
307
+ const expiresShown = formatExpiresLine(identity.notAfter, identity.status, identity.daysRemaining);
308
+ if (expiresShown) {
309
+ logRow('Expires', expiresShown);
310
+ }
311
+ if (identity.mismatch) {
312
+ logMismatchBlock(devIdStr, identity);
313
+ }
314
+ }
315
+
316
+ /**
317
+ * @param {object} ports
318
+ */
319
+ function logPortsSection(ports) {
320
+ logger.log('');
321
+ logger.log('🚀 Ports');
322
+ logRow('App', ports.app);
323
+ logRow('Postgres', ports.postgres);
324
+ logRow('Redis', ports.redis);
325
+ logRow('pgAdmin', ports.pgadmin);
326
+ logRow('Redis Commander', ports.redisCommander);
327
+ }
328
+
329
+ /**
330
+ * @param {object} p
331
+ */
332
+ function logConfigurationSection(p) {
333
+ logger.log('');
334
+ logger.log('⚙️ Configuration');
335
+ logRow('TLS/SSL', infraTlsSslCell(p.tlsEnabled));
336
+ logRow('Environment', p.environment);
337
+ logRow('Controller', p.controller);
338
+ logRow('Format', p.formatVal);
339
+ logRow('Scoped resources', p.scopedResourcesLabel);
340
+ logRow('URLs registry', p.urlsLocalPath);
341
+ if (!p.hasRemote) {
342
+ const showRemoteFallback =
343
+ !isUnset(p.remoteServer) ||
344
+ !isUnset(p.dockerEndpoint) ||
345
+ !isUnset(p.syncSshUser) ||
346
+ !isUnset(p.syncSshHost) ||
347
+ p.dockerTlsSkip;
348
+ if (showRemoteFallback) {
349
+ logRow('Server', p.remoteServer);
350
+ logRow('Docker', p.dockerEndpoint);
351
+ logRow('SSH', formatSshLine(p.syncSshUser, p.syncSshHost));
352
+ logRow('TLS Verify', tlsVerifyCell(p.dockerTlsSkip));
353
+ }
354
+ }
355
+ }
356
+
357
+ /**
358
+ * @param {string|null|undefined} homeResolved
359
+ * @param {string|null|undefined} workResolved
360
+ */
361
+ function logPathsSection(homeResolved, workResolved) {
362
+ logger.log('');
363
+ logger.log('📁 Paths');
364
+ logRow('Home', homeResolved);
365
+ logRow('Work', workResolved);
366
+ }
367
+
368
+ /**
369
+ * @param {string|null} secretsMain
370
+ * @param {string|null} secretsEffective
371
+ * @param {unknown} mutagenFolder
372
+ */
373
+ function logIntegrationsSection(secretsMain, secretsEffective, mutagenFolder) {
374
+ logger.log('');
375
+ logger.log('🔗 Integrations');
376
+ logRow('Secrets API', secretsApiCell(secretsMain, secretsEffective));
377
+ logRow('Mutagen Folder', mutagenFolder);
378
+ }
379
+
380
+ /**
381
+ * Load all inputs for dev show (config, paths, secrets resolution).
382
+ * @param {string} devIdStr - Normalized developer id string
383
+ * @returns {Promise<object>} Bundle passed to {@link renderDevShow}
384
+ */
385
+ async function loadDevShowData(devIdStr) {
386
+ const devIdNum = parseInt(devIdStr, 10);
387
+ const ports = devConfig.getDevPorts(Number.isNaN(devIdNum) ? 0 : devIdNum);
388
+ const [
389
+ environment, tlsEnabled, controller, formatRaw, useScoped, secretsRaw,
390
+ remoteServer, dockerEndpoint, dockerTlsSkipVerify, mutagenFolder, syncSshUser, syncSshHost
391
+ ] = await Promise.all([
392
+ config.getCurrentEnvironment(), config.getTlsEnabled(), config.getControllerUrl(),
393
+ config.getFormat(), config.getUseEnvironmentScopedResources(), config.getAifabrixSecretsPath(),
394
+ config.getRemoteServer(), config.getDockerEndpoint(), config.getDockerTlsSkipVerify(),
395
+ config.getUserMutagenFolder(), config.getSyncSshUser(), config.getSyncSshHost()
396
+ ]);
397
+ const urlsLocalPath = getUrlsLocalYamlPath();
398
+ const scopedResourcesLabel = useScoped ? 'on' : 'off (default)';
399
+ const workResolved = paths.getAifabrixWork();
400
+ const homeResolved = paths.getAifabrixHome();
401
+ const { display: secretsMain, effective: secretsEffective } = await getSecretsDisplay(secretsRaw);
402
+ const dockerTlsSkip = Boolean(dockerTlsSkipVerify);
403
+ const hasRemote = Boolean(remoteServer && String(remoteServer).trim());
404
+ const certDir = getCertDir(resolveConfigDir(), devIdStr);
405
+ return {
406
+ devIdStr,
407
+ ports,
408
+ environment,
409
+ tlsEnabled,
410
+ controller,
411
+ formatVal: formatRaw ?? null,
412
+ scopedResourcesLabel,
413
+ workResolved,
414
+ homeResolved,
415
+ secretsMain,
416
+ secretsEffective,
417
+ urlsLocalPath,
418
+ remoteServer,
419
+ dockerEndpoint,
420
+ dockerTlsSkip,
421
+ mutagenFolder,
422
+ syncSshUser,
423
+ syncSshHost,
424
+ hasRemote,
425
+ certDir
426
+ };
427
+ }
428
+
429
+ /**
430
+ * @param {object} data - Output of {@link loadDevShowData}
431
+ */
432
+ function renderDevShow(data) {
433
+ logger.log(buildHeaderLine(data.devIdStr, data.hasRemote, data.remoteServer));
434
+ logger.log('👤 Developer');
435
+ logRow('ID', data.devIdStr);
436
+ logRemoteAndIdentity(data.devIdStr, data.hasRemote, {
437
+ server: data.remoteServer,
438
+ dockerEndpoint: data.dockerEndpoint,
439
+ dockerTlsSkip: data.dockerTlsSkip,
440
+ syncSshUser: data.syncSshUser,
441
+ syncSshHost: data.syncSshHost
442
+ }, data.certDir);
443
+ logPortsSection(data.ports);
444
+ logConfigurationSection({
445
+ environment: data.environment,
446
+ tlsEnabled: data.tlsEnabled,
447
+ controller: data.controller,
448
+ formatVal: data.formatVal,
449
+ scopedResourcesLabel: data.scopedResourcesLabel,
450
+ urlsLocalPath: data.urlsLocalPath,
451
+ hasRemote: data.hasRemote,
452
+ remoteServer: data.remoteServer,
453
+ dockerEndpoint: data.dockerEndpoint,
454
+ dockerTlsSkip: data.dockerTlsSkip,
455
+ syncSshUser: data.syncSshUser,
456
+ syncSshHost: data.syncSshHost
457
+ });
458
+ logPathsSection(data.homeResolved, data.workResolved);
459
+ logIntegrationsSection(data.secretsMain, data.secretsEffective, data.mutagenFolder);
460
+ logger.log('');
461
+ }
462
+
463
+ /**
464
+ * Show ports, grouped config, optional remote + identity.
465
+ * @param {string} devId - Developer ID from config
466
+ * @returns {Promise<void>}
467
+ */
468
+ async function displayDevConfig(devId) {
469
+ const devIdStr = devId === null || devId === undefined ? '0' : String(devId);
470
+ renderDevShow(await loadDevShowData(devIdStr));
471
+ }
472
+
473
+ module.exports = { displayDevConfig };
@@ -1,3 +1,4 @@
1
+ const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * Login Credentials Handling
3
4
  *
@@ -28,7 +29,7 @@ async function tryLoadCredentialsFromSecrets(appName) {
28
29
  }
29
30
  const credentials = await loadClientCredentials(appName);
30
31
  if (!credentials) {
31
- logger.log(chalk.yellow(`⚠️ Credentials not found in secrets.local.yaml for app '${appName}'`));
32
+ logger.log(chalk.yellow(`⚠ Credentials not found in secrets.local.yaml for app '${appName}'`));
32
33
  logger.log(chalk.gray(` Looking for: '${appName}-client-idKeyVault' and '${appName}-client-secretKeyVault'`));
33
34
  logger.log(chalk.gray(' Prompting for credentials...\n'));
34
35
  }
@@ -143,7 +144,7 @@ function extractTokenData(response) {
143
144
  const responseData = apiResponse.data || apiResponse;
144
145
 
145
146
  if (!responseData || !responseData.token) {
146
- logger.error(chalk.red('Invalid response: missing token'));
147
+ logger.error(formatBlockingError('Invalid response: missing token'));
147
148
  if (responseData) {
148
149
  logger.error(chalk.gray(`Response structure: ${JSON.stringify(responseData, null, 2)}`));
149
150
  }
@@ -1,3 +1,4 @@
1
+ const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * Login Device Code Flow Handling
3
4
  *
@@ -84,7 +85,7 @@ async function saveTokenAndDisplaySuccess(controllerUrl, token, refreshToken, ex
84
85
  if (envKey) {
85
86
  await setCurrentEnvironment(envKey);
86
87
  }
87
- logger.log(chalk.green('\n✅ Successfully logged in!'));
88
+ logger.log(formatSuccessParagraph('Successfully logged in!'));
88
89
  logger.log(chalk.gray(`Controller: ${controllerUrl}`));
89
90
  if (envKey) {
90
91
  logger.log(chalk.gray(`Environment: ${envKey}`));
@@ -251,10 +252,10 @@ async function handleDeviceCodeLogin(controllerUrl, environment, online, scope)
251
252
  } catch (deviceError) {
252
253
  // Display formatted error if available (includes detailed validation info)
253
254
  if (deviceError.formattedError) {
254
- logger.error(chalk.red('\n Device code flow failed:'));
255
+ logger.error(chalk.red('\n Device code flow failed:'));
255
256
  logger.log(deviceError.formattedError);
256
257
  } else {
257
- logger.error(chalk.red(`\n Device code flow failed: ${deviceError.message}`));
258
+ logger.error(chalk.red(`\n Device code flow failed: ${deviceError.message}`));
258
259
  }
259
260
  process.exit(1);
260
261
  }
@@ -1,3 +1,4 @@
1
+ const { formatBlockingError, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * AI Fabrix Builder - Login Command
3
4
  *
@@ -26,7 +27,7 @@ const { resolveControllerUrl } = require('../utils/controller-url');
26
27
  async function determineAuthMethod(method) {
27
28
  if (method) {
28
29
  if (method !== 'device' && method !== 'credentials') {
29
- logger.error(chalk.red(`❌ Invalid method: ${method}. Must be 'device' or 'credentials'`));
30
+ logger.error(formatBlockingError(`Invalid method: ${method}. Must be 'device' or 'credentials'`));
30
31
  process.exit(1);
31
32
  }
32
33
  return method;
@@ -117,7 +118,7 @@ async function handleEnvironmentConfig(options) {
117
118
  */
118
119
  function validateScopeOptions(method, options) {
119
120
  if (method === 'credentials' && (options.online || options.scope)) {
120
- logger.log(chalk.yellow('⚠️ Warning: --online and --scope options are only available for device flow'));
121
+ logger.log(chalk.yellow(' Warning: --online and --scope options are only available for device flow'));
121
122
  logger.log(chalk.gray(' These options will be ignored for credentials method\n'));
122
123
  }
123
124
  }
@@ -133,7 +134,7 @@ function validateScopeOptions(method, options) {
133
134
  */
134
135
  async function handleCredentialsLoginFlow(controllerUrl, environment, options) {
135
136
  if (!options.app) {
136
- logger.error(chalk.red('--app is required for credentials login method'));
137
+ logger.error(formatBlockingError('--app is required for credentials login method'));
137
138
  process.exit(1);
138
139
  }
139
140
  const loginResult = await handleCredentialsLogin(controllerUrl, options.app, options.clientId, options.clientSecret);
@@ -169,7 +170,7 @@ async function handleLogin(options) {
169
170
  return; // Early return for device flow (already saved config)
170
171
  }
171
172
 
172
- logger.log(chalk.green('\n✅ Successfully logged in!'));
173
+ logger.log(formatSuccessParagraph('Successfully logged in!'));
173
174
  logger.log(chalk.gray(`Controller: ${controllerUrl}`));
174
175
  logger.log(chalk.gray(`Environment: ${environment}`));
175
176
  if (options.app) {
@@ -1,3 +1,4 @@
1
+ const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * AI Fabrix Builder - Logout Command
3
4
  *
@@ -67,7 +68,7 @@ async function clearDeviceTokens(options) {
67
68
  // Clear specific controller device token
68
69
  const cleared = await clearDeviceToken(options.controller);
69
70
  if (cleared) {
70
- logger.log(chalk.green(`✓ Cleared device token for controller: ${options.controller}`));
71
+ logger.log(formatSuccessLine(`Cleared device token for controller: ${options.controller}`));
71
72
  return 1;
72
73
  }
73
74
  logger.log(chalk.gray(` No device token found for controller: ${options.controller}`));
@@ -78,7 +79,7 @@ async function clearDeviceTokens(options) {
78
79
  // Clear all device tokens (only when no environment/app specified)
79
80
  const cleared = await clearAllDeviceTokens();
80
81
  if (cleared > 0) {
81
- logger.log(chalk.green(`✓ Cleared ${cleared} device token(s)`));
82
+ logger.log(formatSuccessLine(`Cleared ${cleared} device token(s)`));
82
83
  } else {
83
84
  logger.log(chalk.gray(' No device tokens found'));
84
85
  }
@@ -99,7 +100,7 @@ async function clearClientTokens(options) {
99
100
  // Clear specific app token in environment
100
101
  const cleared = await clearClientToken(options.environment, options.app);
101
102
  if (cleared) {
102
- logger.log(chalk.green(`✓ Cleared client token for app '${options.app}' in environment '${options.environment}'`));
103
+ logger.log(formatSuccessLine(`Cleared client token for app '${options.app}' in environment '${options.environment}'`));
103
104
  return 1;
104
105
  }
105
106
  logger.log(chalk.gray(` No client token found for app '${options.app}' in environment '${options.environment}'`));
@@ -110,7 +111,7 @@ async function clearClientTokens(options) {
110
111
  // Clear all client tokens for environment
111
112
  const cleared = await clearClientTokensForEnvironment(options.environment);
112
113
  if (cleared > 0) {
113
- logger.log(chalk.green(`✓ Cleared ${cleared} client token(s) for environment '${options.environment}'`));
114
+ logger.log(formatSuccessLine(`Cleared ${cleared} client token(s) for environment '${options.environment}'`));
114
115
  } else {
115
116
  logger.log(chalk.gray(` No client tokens found for environment '${options.environment}'`));
116
117
  }
@@ -121,7 +122,7 @@ async function clearClientTokens(options) {
121
122
  // Clear all client tokens (only when no specific options specified)
122
123
  const cleared = await clearAllClientTokens();
123
124
  if (cleared > 0) {
124
- logger.log(chalk.green(`✓ Cleared ${cleared} client token(s)`));
125
+ logger.log(formatSuccessLine(`Cleared ${cleared} client token(s)`));
125
126
  } else {
126
127
  logger.log(chalk.gray(' No client tokens found'));
127
128
  }
@@ -168,10 +169,10 @@ async function handleLogout(options) {
168
169
  // Summary
169
170
  const totalCleared = deviceTokensCleared + clientTokensCleared;
170
171
  if (totalCleared > 0) {
171
- logger.log(chalk.green('\n✅ Successfully cleared tokens!'));
172
+ logger.log(formatSuccessParagraph('Successfully cleared tokens!'));
172
173
  logger.log(chalk.gray(`Config file: ${configPath}\n`));
173
174
  } else {
174
- logger.log(chalk.yellow('\n⚠️ No tokens found to clear'));
175
+ logger.log(chalk.yellow('\n No tokens found to clear'));
175
176
  logger.log(chalk.gray(`Config file: ${configPath}\n`));
176
177
  }
177
178
  }
@@ -0,0 +1,54 @@
1
+ const { formatBlockingError, formatSuccessLine } = require('../utils/cli-test-layout-chalk');
2
+ /**
3
+ * @fileoverview CLI handler: parameters validate (kv:// vs infra.parameter.yaml)
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 pathsUtil = require('../utils/paths');
11
+ const {
12
+ getInfraParameterCatalog,
13
+ loadInfraParameterCatalog
14
+ } = require('../parameters/infra-parameter-catalog');
15
+ const {
16
+ validateWorkspaceKvRefsAgainstCatalog,
17
+ validateCatalogRequiredGenerators
18
+ } = require('../parameters/infra-parameter-validate');
19
+
20
+ /**
21
+ * Run catalog + workspace kv:// validation.
22
+ * @param {Object} [options] - CLI options
23
+ * @returns {Promise<{ valid: boolean }>}
24
+ */
25
+ async function handleParametersValidate(options = {}) {
26
+ let catalog;
27
+ try {
28
+ catalog = options.catalogPath
29
+ ? loadInfraParameterCatalog(options.catalogPath)
30
+ : getInfraParameterCatalog();
31
+ } catch (e) {
32
+ logger.log(formatBlockingError(`Could not load infra parameter catalog: ${e.message}`));
33
+ return { valid: false };
34
+ }
35
+
36
+ const reqGen = validateCatalogRequiredGenerators(catalog.data);
37
+ if (!reqGen.valid) {
38
+ logger.log(formatBlockingError('Catalog requiredForLocal / generator issues:'));
39
+ reqGen.errors.forEach((err) => logger.log(chalk.yellow(` • ${err}`)));
40
+ return { valid: false };
41
+ }
42
+
43
+ const kv = validateWorkspaceKvRefsAgainstCatalog(catalog, pathsUtil);
44
+ if (!kv.valid) {
45
+ logger.log(formatBlockingError('env.template kv:// keys not covered by infra.parameter.yaml:'));
46
+ kv.errors.forEach((err) => logger.log(chalk.yellow(` • ${err}`)));
47
+ return { valid: false };
48
+ }
49
+
50
+ logger.log(formatSuccessLine('parameters validate: catalog OK; workspace kv:// keys covered.'));
51
+ return { valid: true };
52
+ }
53
+
54
+ module.exports = { handleParametersValidate };