@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,267 @@
1
+ /**
2
+ * @fileoverview Chalk helpers aligned with `.cursor/plans/layout.md` (CLI TTY / validation summaries).
3
+ * @see `.cursor/plans/layout.md` — Contributor appendix for glyph rules and the implementation map.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const chalk = require('chalk');
9
+
10
+ /** Canonical success glyph (green), layout §CORE / §3. */
11
+ function successGlyph() {
12
+ return chalk.green('✔');
13
+ }
14
+
15
+ /** Failure glyph only (red), layout §18 — compose with other chalk segments if needed. */
16
+ function failureGlyph() {
17
+ return chalk.red('✖');
18
+ }
19
+
20
+ /**
21
+ * Single-line success message: green ✔ + green rest (layout §CORE).
22
+ * @param {string} message - text after the ✔ (no leading space)
23
+ * @returns {string}
24
+ */
25
+ function formatSuccessLine(message) {
26
+ return chalk.green(`✔ ${message}`);
27
+ }
28
+
29
+ /**
30
+ * Success line with leading newline (common for footers / deploy summaries).
31
+ * @param {string} message
32
+ * @returns {string}
33
+ */
34
+ function formatSuccessParagraph(message) {
35
+ return chalk.green(`\n✔ ${message}`);
36
+ }
37
+
38
+ /**
39
+ * Blocking error line: red ✖ + red message (layout §18).
40
+ * @param {string} message
41
+ * @returns {string}
42
+ */
43
+ function formatBlockingError(message) {
44
+ return `${chalk.red('✖')} ${chalk.red(message)}`;
45
+ }
46
+
47
+ /**
48
+ * Failure with optional hint (layout §7).
49
+ * @param {string} title - main error text (red)
50
+ * @param {string} [hint] - optional hint (gray "Hint:" + yellow text)
51
+ * @returns {string}
52
+ */
53
+ function formatIssue(title, hint) {
54
+ const head = `${chalk.red('✖')} ${chalk.red(title)}`;
55
+ if (!hint) {
56
+ return head;
57
+ }
58
+ return `${head}\n ${chalk.gray('Hint:')} ${chalk.yellow(hint)}`;
59
+ }
60
+
61
+ /**
62
+ * Next actions list (layout §19): bold title, cyan bullets, white lines.
63
+ * @param {string[]} lines - action text without leading "-"
64
+ * @returns {string}
65
+ */
66
+ function formatNextActions(lines) {
67
+ const body = (lines || [])
68
+ .map(line => `${chalk.cyan('-')} ${chalk.white(line)}`)
69
+ .join('\n');
70
+ return `${sectionTitle('Next actions:')}\n${body}`;
71
+ }
72
+
73
+ /**
74
+ * Docs / link line (layout §14): gray label, cyan URL.
75
+ * @param {string} label - e.g. "Docs:" or "Docs"
76
+ * @param {string} url
77
+ * @returns {string}
78
+ */
79
+ function formatDocsLine(label, url) {
80
+ const lbl = label.endsWith(':') ? label : `${label}:`;
81
+ return `${chalk.gray(lbl)} ${chalk.cyan(url)}`;
82
+ }
83
+
84
+ /**
85
+ * Progress / async (layout §15): yellow ⏳ + white message.
86
+ * @param {string} message
87
+ * @returns {string}
88
+ */
89
+ function formatProgress(message) {
90
+ return `${chalk.yellow('⏳')} ${chalk.white(message)}`;
91
+ }
92
+
93
+ /**
94
+ * Section title + bullet list (layout §3, §9 impact-style).
95
+ * @param {string} title
96
+ * @param {string[]} items - lines without bullet prefix
97
+ * @param {{ bullet?: 'cyan'|'red' }} [opts]
98
+ * @returns {string}
99
+ */
100
+ function formatBulletSection(title, items, opts) {
101
+ const bulletColor = opts && opts.bullet === 'red' ? chalk.red : chalk.cyan;
102
+ const body = (items || []).map(line => `${bulletColor('-')} ${chalk.white(line)}`).join('\n');
103
+ return `${sectionTitle(title)}\n${body}`;
104
+ }
105
+
106
+ /**
107
+ * Bold white section title (layout: Section).
108
+ * @param {string} text
109
+ * @returns {string}
110
+ */
111
+ function sectionTitle(text) {
112
+ return chalk.white.bold(text);
113
+ }
114
+
115
+ /**
116
+ * Gray label + bold white value (layout: HEADER BLOCK).
117
+ * @param {string} label - e.g. "System:"
118
+ * @param {string} value
119
+ * @returns {string}
120
+ */
121
+ function headerKeyValue(label, value) {
122
+ return `${chalk.gray(label)} ${chalk.white.bold(value)}`;
123
+ }
124
+
125
+ /**
126
+ * Info line (cyan ℹ / neutral progress).
127
+ * @param {string} text
128
+ * @returns {string}
129
+ */
130
+ function infoLine(text) {
131
+ return chalk.cyan(text);
132
+ }
133
+
134
+ /**
135
+ * Metadata (URLs, hints).
136
+ * @param {string} text
137
+ * @returns {string}
138
+ */
139
+ function metadata(text) {
140
+ return chalk.gray(text);
141
+ }
142
+
143
+ /**
144
+ * @param {'ok'|'warn'|'fail'|'skipped'} agg
145
+ * @returns {string} Uppercase status word for header
146
+ */
147
+ function aggregateStatusWord(agg) {
148
+ if (agg === 'warn') return 'WARN';
149
+ if (agg === 'fail') return 'FAIL';
150
+ if (agg === 'skipped') return 'SKIPPED';
151
+ return 'OK';
152
+ }
153
+
154
+ /**
155
+ * Colored glyph from aggregate status.
156
+ * @param {'ok'|'warn'|'fail'|'skipped'} agg
157
+ * @param {string} glyph - unicode ✔ ⚠ ✖ ⏭
158
+ * @returns {string}
159
+ */
160
+ function colorAggregateGlyph(agg, glyph) {
161
+ if (agg === 'ok') return chalk.green(glyph);
162
+ if (agg === 'warn') return chalk.yellow(glyph);
163
+ if (agg === 'fail') return chalk.red(glyph);
164
+ return chalk.gray(glyph);
165
+ }
166
+
167
+ /**
168
+ * Full "Status: ✔ OK" line (glyph + word same color family).
169
+ * @param {'ok'|'warn'|'fail'|'skipped'} agg
170
+ * @param {string} glyph
171
+ * @returns {string}
172
+ */
173
+ function formatStatusKeyValue(agg, glyph) {
174
+ const g = colorAggregateGlyph(agg, glyph);
175
+ const w = aggregateStatusWord(agg);
176
+ const word =
177
+ agg === 'ok'
178
+ ? chalk.green(w)
179
+ : agg === 'warn'
180
+ ? chalk.yellow(w)
181
+ : agg === 'fail'
182
+ ? chalk.red(w)
183
+ : chalk.gray(w);
184
+ return `${chalk.gray('Status:')} ${g} ${word}`;
185
+ }
186
+
187
+ /**
188
+ * Datasource list row (layout §5): symbol + name (white) + status hint (gray).
189
+ * @param {'ok'|'warn'|'fail'|'skipped'} rowStatus
190
+ * @param {string} name
191
+ * @param {string} [statusHint] - e.g. "Ready", shown in gray parens
192
+ * @returns {string}
193
+ */
194
+ function formatDatasourceListRow(rowStatus, name, statusHint) {
195
+ const sym =
196
+ rowStatus === 'ok'
197
+ ? chalk.green('✔')
198
+ : rowStatus === 'warn'
199
+ ? chalk.yellow('⚠')
200
+ : rowStatus === 'fail'
201
+ ? chalk.red('✖')
202
+ : chalk.gray('⏭');
203
+ const hint = statusHint ? ` ${chalk.gray(`(${statusHint})`)}` : '';
204
+ return ` ${sym} ${chalk.white(name)}${hint}`;
205
+ }
206
+
207
+ /**
208
+ * Footer line for all-pass / warn / fail.
209
+ * @param {boolean} success
210
+ * @param {'ok'|'warn'|'fail'} [agg]
211
+ * @param {string} okMsg
212
+ * @param {string} warnMsg
213
+ * @param {string} failMsg
214
+ * @returns {string}
215
+ */
216
+ function integrationFooterLine(success, agg, okMsg, warnMsg, failMsg) {
217
+ if (success && agg === 'warn') {
218
+ return chalk.yellow(`\n⚠ ${warnMsg}`);
219
+ }
220
+ if (success) {
221
+ return formatSuccessParagraph(okMsg);
222
+ }
223
+ return chalk.red(`\n✖ ${failMsg}`);
224
+ }
225
+
226
+ /**
227
+ * Color a single data-quality / rollup line that starts with ✔, ⚠, or ✖.
228
+ * @param {string} line
229
+ * @returns {string}
230
+ */
231
+ function colorRollupPrefixedLine(line) {
232
+ const trimmed = line.trimStart();
233
+ const first = trimmed[0];
234
+ if (first === '✔') {
235
+ return chalk.green('✔') + chalk.white(trimmed.slice(1));
236
+ }
237
+ if (first === '⚠') {
238
+ return chalk.yellow('⚠') + chalk.white(trimmed.slice(1));
239
+ }
240
+ if (first === '✖') {
241
+ return chalk.red('✖') + chalk.white(trimmed.slice(1));
242
+ }
243
+ return line;
244
+ }
245
+
246
+ module.exports = {
247
+ sectionTitle,
248
+ headerKeyValue,
249
+ infoLine,
250
+ metadata,
251
+ aggregateStatusWord,
252
+ colorAggregateGlyph,
253
+ formatStatusKeyValue,
254
+ formatDatasourceListRow,
255
+ integrationFooterLine,
256
+ colorRollupPrefixedLine,
257
+ successGlyph,
258
+ failureGlyph,
259
+ formatSuccessLine,
260
+ formatSuccessParagraph,
261
+ formatBlockingError,
262
+ formatIssue,
263
+ formatNextActions,
264
+ formatDocsLine,
265
+ formatProgress,
266
+ formatBulletSection
267
+ };
@@ -10,8 +10,8 @@
10
10
 
11
11
  const path = require('path');
12
12
  const chalk = require('chalk');
13
- const fs = require('fs').promises;
14
13
  const logger = require('./logger');
14
+ const { getDockerDaemonStartHintSentence, getDockerApiOverTcpHintLines } = require('./docker-not-running-hint');
15
15
 
16
16
  /**
17
17
  * Validates a command and its options
@@ -96,7 +96,47 @@ function isPermissionDeniedError(errorMsg) {
96
96
  function isDockerPermissionDeniedError(errorMsg) {
97
97
  if (!isPermissionDeniedError(errorMsg)) return false;
98
98
  const lower = errorMsg.toLowerCase();
99
- return lower.includes('docker') || lower.includes('socket') || errorMsg.includes('EACCES');
99
+ // Do not treat every EACCES as Docker (e.g. mkdir on a bogus server-side secrets path).
100
+ return lower.includes('docker') || lower.includes('docker.sock') || lower.includes('/var/run/docker');
101
+ }
102
+
103
+ /**
104
+ * True for Node fs errno messages (mkdir, open, …), not HTTP 403 from Controller/Dataplane.
105
+ * Those strings also match {@link isPermissionDeniedError} and must not get API token hints.
106
+ * @param {string} errorMsg
107
+ * @returns {boolean}
108
+ */
109
+ function isLocalFilesystemPermissionDeniedError(errorMsg) {
110
+ if (!errorMsg || typeof errorMsg !== 'string') return false;
111
+ const lower = errorMsg.toLowerCase();
112
+ const looksLikeFsErrno =
113
+ lower.includes('eacces') ||
114
+ lower.includes('eperm') ||
115
+ lower.includes('enoent') ||
116
+ lower.includes('enospc');
117
+ if (!looksLikeFsErrno && !lower.includes('permission denied')) return false;
118
+ return (
119
+ /\bmkdir\b/i.test(errorMsg) ||
120
+ /\brmdir\b/i.test(errorMsg) ||
121
+ /\brename\b/i.test(errorMsg) ||
122
+ /\bunlink\b/i.test(errorMsg) ||
123
+ /\bchmod\b/i.test(errorMsg) ||
124
+ /permission denied,?\s*open\b/i.test(lower) ||
125
+ /,\s*open\s+['"]/i.test(errorMsg)
126
+ );
127
+ }
128
+
129
+ /**
130
+ * Local fs EACCES (wrong AIFABRIX_HOME path, etc.) — not API/RBAC.
131
+ * @param {string} errorMsg
132
+ * @returns {string[]|null}
133
+ */
134
+ function formatLocalFilesystemPermissionError(errorMsg) {
135
+ if (!isLocalFilesystemPermissionDeniedError(errorMsg)) return null;
136
+ return [
137
+ ` ${errorMsg}`,
138
+ ' This is a local filesystem path or permissions issue, not an API or RBAC token. Check AIFABRIX_HOME, AIFABRIX_CONFIG, and that the target directory is writable.'
139
+ ];
100
140
  }
101
141
 
102
142
  /**
@@ -118,7 +158,8 @@ function formatDockerError(errorMsg) {
118
158
  if (isDockerNotRunningError(errorMsg)) {
119
159
  return [
120
160
  ' Docker is not running or not installed.',
121
- ' Please start Docker Desktop and try again.'
161
+ ` ${getDockerDaemonStartHintSentence()}`,
162
+ ...getDockerApiOverTcpHintLines()
122
163
  ];
123
164
  }
124
165
  if (isPortConflictError(errorMsg)) {
@@ -129,20 +170,37 @@ function formatDockerError(errorMsg) {
129
170
  }
130
171
  if (isDockerPermissionDeniedError(errorMsg)) {
131
172
  return [
132
- ' Permission denied.',
133
- ' Make sure you have the necessary permissions to run Docker commands.'
173
+ ' Permission denied when using Docker (e.g. unix socket).',
174
+ ' On Linux you can add your user to the "docker" group and log in again, or use the Engine API via docker-endpoint:',
175
+ ...getDockerApiOverTcpHintLines()
134
176
  ];
135
177
  }
136
178
  return null;
137
179
  }
138
180
 
181
+ /**
182
+ * True when the message indicates the Azure CLI binary is missing or not logged in.
183
+ * Do not use a naive "az"+"failed" substring match: "azurecr.io", "Azure", "lazy", "amazon", etc. contain "az".
184
+ * @param {string} errorMsg - Error message
185
+ * @returns {boolean}
186
+ */
187
+ function isAzureCliToolingError(errorMsg) {
188
+ if (errorMsg.includes('Azure CLI is not installed')) return true;
189
+ if (errorMsg.includes('az --version failed')) return true;
190
+ if (errorMsg.includes('Not logged in to Azure')) return true;
191
+ if (errorMsg.includes('az: command not found')) return true;
192
+ if (errorMsg.includes('az.cmd: command not found')) return true;
193
+ if (errorMsg.includes('\'az\' is not recognized') || errorMsg.includes('"az" is not recognized')) return true;
194
+ return false;
195
+ }
196
+
139
197
  /**
140
198
  * Format Azure-related errors
141
199
  * @param {string} errorMsg - Error message
142
200
  * @returns {string[]|null} Array of error message lines or null if not an Azure error
143
201
  */
144
202
  function formatAzureError(errorMsg) {
145
- if (errorMsg.includes('Azure CLI is not installed') || errorMsg.includes('az --version failed') || (errorMsg.includes('az') && errorMsg.includes('failed'))) {
203
+ if (isAzureCliToolingError(errorMsg)) {
146
204
  return [
147
205
  ' Azure CLI is not installed or not working properly.',
148
206
  ' Install from: https://docs.microsoft.com/cli/azure/install-azure-cli',
@@ -249,6 +307,7 @@ function formatValidationError(errorMsg) {
249
307
  function formatApiPermissionError(errorMsg) {
250
308
  if (!isPermissionDeniedError(errorMsg)) return null;
251
309
  if (isDockerPermissionDeniedError(errorMsg)) return null;
310
+ if (isLocalFilesystemPermissionDeniedError(errorMsg)) return null;
252
311
  return [
253
312
  ` ${errorMsg}`,
254
313
  ' Ensure your token has the required permission (e.g. external-system:delete for delete).'
@@ -267,8 +326,25 @@ function formatApiPermissionError(errorMsg) {
267
326
  * @param {string} errorMsg - Error message
268
327
  * @returns {string[]|null} Formatted error messages or null if no formatter matched
269
328
  */
329
+ /**
330
+ * up-infra / checkDockerAvailability: multi-line "Cannot use Docker for infrastructure" + docker-endpoint hint.
331
+ * @param {string} errorMsg - Error message
332
+ * @returns {string[]|null}
333
+ */
334
+ function formatCannotUseDockerInfrastructureError(errorMsg) {
335
+ if (!errorMsg.includes('Cannot use Docker for infrastructure')) {
336
+ return null;
337
+ }
338
+ const lines = errorMsg.split('\n').map((l) => l.trim()).filter(Boolean);
339
+ const out = lines.map((l) => ` ${l}`);
340
+ out.push(...getDockerApiOverTcpHintLines());
341
+ return out;
342
+ }
343
+
270
344
  function tryFormatErrorWithFormatters(errorMsg) {
271
345
  const formatters = [
346
+ formatCannotUseDockerInfrastructureError,
347
+ formatLocalFilesystemPermissionError,
272
348
  formatApiPermissionError,
273
349
  formatDockerError,
274
350
  formatAzureError,
@@ -312,13 +388,13 @@ function formatError(error) {
312
388
  * @param {string[]} errorMessages - Error message lines
313
389
  */
314
390
  function logError(command, errorMessages) {
315
- logger.error(`\n Error in ${command} command:`);
391
+ logger.error(`\n Error in ${command} command:`);
316
392
  errorMessages.forEach(msg => logger.error(msg));
317
393
  logger.error('\n💡 Run "aifabrix doctor" for environment diagnostics.\n');
318
394
  }
319
395
 
320
396
  /**
321
- * Logs the resolved app path so the user can see which directory (integration/<app> or builder/<app>) is used.
397
+ * Logs the resolved app path so the user can see which directory (integration/<systemKey> or builder/<appKey>) is used.
322
398
  * Path resolution order is always integration first, then builder; no CLI flag overrides this.
323
399
  *
324
400
  * @param {string} appPath - Resolved application directory path
@@ -366,7 +442,7 @@ function stripAnsi(str) {
366
442
  }
367
443
 
368
444
  /**
369
- * Appends a wizard error to integration/<appKey>/error.log (timestamp + message only; no stack or secrets).
445
+ * Appends a wizard error to integration/<systemKey>/error.log (timestamp + message only; no stack or secrets).
370
446
  * Uses full formatted message (with validation details) when error.formatted is set, stripped of ANSI.
371
447
  * Does not throw; logs and ignores write failures.
372
448
  * @param {string} appKey - Application/integration key (e.g. app name or system key)
@@ -384,8 +460,9 @@ async function appendWizardError(appKey, error) {
384
460
  const message = fullPlain && fullPlain.length > rawMessage.length ? fullPlain : rawMessage;
385
461
  const line = `${new Date().toISOString()} ${message}\n`;
386
462
  try {
387
- await fs.mkdir(dir, { recursive: true });
388
- await fs.appendFile(logPath, line, 'utf8');
463
+ const fsp = require('fs').promises;
464
+ await fsp.mkdir(dir, { recursive: true });
465
+ await fsp.appendFile(logPath, line, 'utf8');
389
466
  } catch (e) {
390
467
  logger.warn(`Could not write wizard error.log: ${e.message}`);
391
468
  }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Read database passwords from .env for Docker Compose generation.
3
+ *
4
+ * @fileoverview Compose DB password helpers
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ const fsSync = require('fs');
10
+ const fs = require('fs').promises;
11
+ const { formatMissingDbPasswordError } = require('./error-formatter');
12
+
13
+ /** Reads and parses .env file. @param {string} envPath - Path to .env file. @returns {Promise<Object>} env vars. @throws {Error} If file not found. */
14
+ async function readEnvFile(envPath) {
15
+ if (!fsSync.existsSync(envPath)) {
16
+ throw new Error(`.env file not found: ${envPath}`);
17
+ }
18
+
19
+ try {
20
+ const envContent = await fs.readFile(envPath, 'utf8');
21
+ if (envContent === undefined || envContent === null) {
22
+ throw new Error('Failed to read .env file: file content is empty or undefined');
23
+ }
24
+ const lines = envContent.split('\n');
25
+ const envVars = {};
26
+
27
+ for (const line of lines) {
28
+ const trimmed = line.trim();
29
+ if (!trimmed || trimmed.startsWith('#')) {
30
+ continue;
31
+ }
32
+
33
+ const equalIndex = trimmed.indexOf('=');
34
+ if (equalIndex > 0) {
35
+ const key = trimmed.substring(0, equalIndex).trim();
36
+ const value = trimmed.substring(equalIndex + 1).trim();
37
+ envVars[key] = value;
38
+ }
39
+ }
40
+
41
+ return envVars;
42
+ } catch (error) {
43
+ throw new Error(`Failed to read .env file: ${error.message}`);
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Validates and extracts password from environment variables
49
+ * @function extractPassword
50
+ * @param {Object} envVars - Environment variables
51
+ * @param {string} passwordKey - Password key to look up
52
+ * @param {Object} [context] - Optional: { appKey, multi } for clearer error messages
53
+ * @returns {string} Password value
54
+ * @throws {Error} If password is missing or empty
55
+ */
56
+ function extractPassword(envVars, passwordKey, context = {}) {
57
+ const { appKey, multi } = context;
58
+ const appSuffix = appKey ? ` for application '${appKey}'` : '';
59
+
60
+ if (!(passwordKey in envVars)) {
61
+ throw new Error(multi && appKey ? formatMissingDbPasswordError(appKey, { multiDb: true, passwordKey }) : 'Missing required password variable ' + passwordKey + ' in .env file' + appSuffix + '. Add ' + passwordKey + '=your_secret to your .env file.');
62
+ }
63
+
64
+ const password = envVars[passwordKey].trim();
65
+ if (!password || password.length === 0) {
66
+ throw new Error('Password variable ' + passwordKey + ' is empty in .env file' + appSuffix + '. Set a non-empty value.');
67
+ }
68
+
69
+ return password;
70
+ }
71
+
72
+ /**
73
+ * Processes multiple databases
74
+ * @function processMultipleDatabases
75
+ * @param {Array} databases - Array of database configurations
76
+ * @param {Object} envVars - Environment variables
77
+ * @param {string} appKey - Application key
78
+ * @returns {Object} Object with passwords map and array
79
+ */
80
+ function processMultipleDatabases(databases, envVars, appKey) {
81
+ const passwords = {};
82
+ const passwordsArray = [];
83
+ for (let i = 0; i < databases.length; i++) {
84
+ const db = databases[i];
85
+ const dbName = db.name || appKey;
86
+ const passwordKey = `DB_${i}_PASSWORD`;
87
+ const password = extractPassword(envVars, passwordKey, { appKey, multi: true });
88
+ passwords[dbName] = password;
89
+ passwordsArray.push(password);
90
+ }
91
+ return { passwords, passwordsArray };
92
+ }
93
+
94
+ /**
95
+ * Processes single database case
96
+ * @function processSingleDatabase
97
+ * @param {Object} envVars - Environment variables
98
+ * @param {string} appKey - Application key
99
+ * @returns {Object} Object with passwords map and array
100
+ */
101
+ function processSingleDatabase(envVars, appKey) {
102
+ const passwords = {};
103
+ const passwordsArray = [];
104
+ const passwordKey = ('DB_0_PASSWORD' in envVars) ? 'DB_0_PASSWORD' : 'DB_PASSWORD';
105
+ if (!(passwordKey in envVars)) {
106
+ throw new Error(formatMissingDbPasswordError(appKey));
107
+ }
108
+ const password = extractPassword(envVars, passwordKey, { appKey });
109
+ passwords[appKey] = password;
110
+ passwordsArray.push(password);
111
+ return { passwords, passwordsArray };
112
+ }
113
+
114
+ /**
115
+ * Reads database passwords from .env file
116
+ * @async
117
+ * @function readDatabasePasswords
118
+ * @param {string} envPath - Path to .env file
119
+ * @param {Array<Object>} databases - Array of database configurations
120
+ * @param {string} appKey - Application key (fallback for single database)
121
+ * @returns {Promise<Object>} Object with passwords map and array
122
+ * @throws {Error} If required password variables are missing
123
+ */
124
+ async function readDatabasePasswords(envPath, databases, appKey) {
125
+ const envVars = await readEnvFile(envPath);
126
+ if (databases && databases.length > 0) {
127
+ const { passwords, passwordsArray } = processMultipleDatabases(databases, envVars, appKey);
128
+ return { map: passwords, array: passwordsArray };
129
+ }
130
+ const { passwords, passwordsArray } = processSingleDatabase(envVars, appKey);
131
+ return { map: passwords, array: passwordsArray };
132
+ }
133
+
134
+ module.exports = {
135
+ readDatabasePasswords,
136
+ readEnvFile,
137
+ extractPassword
138
+ };