@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,158 @@
1
+ /**
2
+ * Traefik ingress path/host/TLS and StripPrefix derivation helpers for compose generation.
3
+ *
4
+ * @fileoverview Traefik PathPrefix + host expansion (shared with health path resolution)
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ const {
10
+ buildEnvScopedTraefikPath
11
+ } = require('./environment-scoped-resources');
12
+ const { parseDeveloperIdNum } = require('./declarative-url-ports');
13
+
14
+ /**
15
+ * Derives base path from routing pattern by removing trailing wildcards
16
+ * @param {string} pattern - URL pattern (e.g., '/app/*', '/api/v1/*')
17
+ * @returns {string} Base path for routing
18
+ */
19
+ function derivePathFromPattern(pattern) {
20
+ if (!pattern || typeof pattern !== 'string') {
21
+ return '/';
22
+ }
23
+ const trimmed = pattern.trim();
24
+ if (trimmed === '/' || trimmed === '') {
25
+ return '/';
26
+ }
27
+ const withoutWildcards = trimmed.replace(/\*+$/g, '');
28
+ const withoutTrailingSlashes = withoutWildcards.replace(/\/+$/g, '');
29
+ return withoutTrailingSlashes || '/';
30
+ }
31
+
32
+ /**
33
+ * Resolve Traefik TLS from application.yaml `frontDoorRouting.tls` (boolean or string).
34
+ * String "false" disables TLS; placeholders (e.g. ${TLS_ENABLED}) are treated as enabled.
35
+ * @param {unknown} tls - Raw tls value
36
+ * @returns {boolean}
37
+ */
38
+ function resolveTraefikTlsEnabled(tls) {
39
+ if (tls === false || tls === 'false') {
40
+ return false;
41
+ }
42
+ return true;
43
+ }
44
+
45
+ /**
46
+ * Developer label for frontDoorRouting.host only. Id 0 / missing / empty → no subdomain (empty string).
47
+ * Non-zero → dev01, dev02, … (same padding as buildDevUsername).
48
+ *
49
+ * @param {string|number|null|undefined} devId
50
+ * @returns {string}
51
+ */
52
+ function buildDevUsernameForFrontDoorHost(devId) {
53
+ const n = parseDeveloperIdNum(devId);
54
+ if (n === 0) {
55
+ return '';
56
+ }
57
+ const s = String(n);
58
+ const padded = s.length === 1 ? s.padStart(2, '0') : s;
59
+ return `dev${padded}`;
60
+ }
61
+
62
+ /**
63
+ * Expand frontDoorRouting.host placeholders (Traefik labels + url:// base). Same rules as declarative URL resolver.
64
+ * Normalizes ${DEV_USERNAME}${REMOTE_HOST} to insert a dot. Trims stray leading/trailing dots (e.g. id 0 + `.${REMOTE_HOST}` → bare remote hostname).
65
+ *
66
+ * @param {string} template
67
+ * @param {string|number|null|undefined} developerIdRaw
68
+ * @param {string|null|undefined} remoteServer
69
+ * @returns {string}
70
+ */
71
+ function expandFrontDoorHostPlaceholders(template, developerIdRaw, remoteServer) {
72
+ let t = String(template || '');
73
+ t = t.replace(/\$\{DEV_USERNAME\}\$\{REMOTE_HOST\}/g, '${DEV_USERNAME}.${REMOTE_HOST}');
74
+ const devU = buildDevUsernameForFrontDoorHost(developerIdRaw);
75
+ t = t.replace(/\$\{DEV_USERNAME\}/g, devU);
76
+ let remoteHost = '';
77
+ try {
78
+ if (remoteServer && String(remoteServer).trim()) {
79
+ remoteHost = new URL(String(remoteServer).trim()).hostname;
80
+ }
81
+ } catch {
82
+ remoteHost = '';
83
+ }
84
+ t = t.replace(/\$\{REMOTE_HOST\}/g, remoteHost);
85
+ t = t.replace(/^\.+/g, '').replace(/\.{2,}/g, '.').replace(/\.+$/g, '').trim();
86
+ return t;
87
+ }
88
+
89
+ /**
90
+ * Traefik PathPrefix / host / TLS from frontDoorRouting (public ingress). Does not include StripPrefix — that follows
91
+ * the in-container health path: private URL is `http://<service>:<port>` plus the probe path; `/dev`, `/tst`, `/auth`,
92
+ * etc. are public path segments only.
93
+ *
94
+ * @param {Object} config - Application configuration (application.yaml shape)
95
+ * @param {string|number} devId - Developer id for host expansion
96
+ * @param {Object|null} scopeOpts - Env-scoped Traefik path (effectiveEnvironmentScopedResources, runEnvKey)
97
+ * @param {string|null|undefined} remoteServer - For ${REMOTE_HOST}
98
+ * @returns {{ enabled: false } | { enabled: true, host: string, path: string, tls: boolean, certStore: string|null }}
99
+ */
100
+ function buildTraefikIngressBase(config, devId, scopeOpts, remoteServer) {
101
+ const frontDoor = config.frontDoorRouting;
102
+ if (!frontDoor || frontDoor.enabled !== true) {
103
+ return { enabled: false };
104
+ }
105
+ if (!frontDoor.host || typeof frontDoor.host !== 'string') {
106
+ throw new Error('frontDoorRouting.host is required when frontDoorRouting.enabled is true');
107
+ }
108
+ const host = expandFrontDoorHostPlaceholders(frontDoor.host, devId, remoteServer);
109
+ const basePath = derivePathFromPattern(frontDoor.pattern);
110
+ let pathOut = basePath;
111
+ if (
112
+ scopeOpts &&
113
+ scopeOpts.effectiveEnvironmentScopedResources &&
114
+ scopeOpts.runEnvKey &&
115
+ (scopeOpts.runEnvKey === 'dev' || scopeOpts.runEnvKey === 'tst')
116
+ ) {
117
+ pathOut = buildEnvScopedTraefikPath(basePath, scopeOpts.runEnvKey);
118
+ }
119
+ return {
120
+ enabled: true,
121
+ host,
122
+ path: pathOut,
123
+ tls: resolveTraefikTlsEnabled(frontDoor.tls),
124
+ certStore: frontDoor.certStore || null
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Whether Traefik should apply StripPrefix so the backend sees the same path as the Docker health probe.
130
+ * When the resolved compose health path already lies under the public PathPrefix (e.g. /auth/health/ready), forward
131
+ * the full path. When the probe is root-only (e.g. /health) but PathPrefix is /miso, strip the prefix.
132
+ *
133
+ * @param {string} traefikPath - PathPrefix value (slashes normalized, no trailing slash except '/')
134
+ * @param {string} resolvedHealthPath - Output of resolveHealthCheckPathWithFrontDoorVdir with compose opts
135
+ * @returns {boolean} true when StripPrefix middleware labels should be emitted
136
+ */
137
+ function computeTraefikStripPathPrefix(traefikPath, resolvedHealthPath) {
138
+ const healthRaw = String(resolvedHealthPath || '/').trim();
139
+ const health = healthRaw.replace(/\/+$/, '') || '/';
140
+ const prefixRaw = String(traefikPath || '/').trim();
141
+ const prefix = prefixRaw.replace(/\/+$/, '') || '/';
142
+ if (prefix === '/' || prefix === '') {
143
+ return false;
144
+ }
145
+ if (health === prefix || health.startsWith(`${prefix}/`)) {
146
+ return false;
147
+ }
148
+ return true;
149
+ }
150
+
151
+ module.exports = {
152
+ derivePathFromPattern,
153
+ resolveTraefikTlsEnabled,
154
+ buildDevUsernameForFrontDoorHost,
155
+ expandFrontDoorHostPlaceholders,
156
+ buildTraefikIngressBase,
157
+ computeTraefikStripPathPrefix
158
+ };
@@ -8,6 +8,7 @@
8
8
  * @version 2.0.0
9
9
  */
10
10
 
11
+ const fs = require('fs');
11
12
  const path = require('path');
12
13
 
13
14
  /**
@@ -21,6 +22,7 @@ const SETTINGS_RESPONSE_KEYS = [
21
22
  'aifabrix-env-config',
22
23
  'remote-server',
23
24
  'docker-endpoint',
25
+ 'docker-tls-skip-verify',
24
26
  'sync-ssh-user',
25
27
  'sync-ssh-host'
26
28
  ];
@@ -85,6 +87,27 @@ function createHomeAndSecretsPathFunctions(getConfigFn, saveConfigFn) {
85
87
  }
86
88
  await setPathConfig(getConfigFn, saveConfigFn, 'aifabrix-home', trimmed, 'Home path must be a non-empty string');
87
89
  },
90
+ async getAifabrixWorkOverride() {
91
+ return getPathConfig(getConfigFn, 'aifabrix-work');
92
+ },
93
+ async setAifabrixWorkOverride(workPath) {
94
+ if (typeof workPath !== 'string') {
95
+ throw new Error('Work path is required and must be a string');
96
+ }
97
+ const trimmed = workPath.trim();
98
+ if (trimmed === '') {
99
+ await clearPathConfig(getConfigFn, saveConfigFn, 'aifabrix-work');
100
+ return;
101
+ }
102
+ const resolved = path.resolve(trimmed);
103
+ await setPathConfig(
104
+ getConfigFn,
105
+ saveConfigFn,
106
+ 'aifabrix-work',
107
+ resolved,
108
+ 'Work path must be a non-empty string'
109
+ );
110
+ },
88
111
  async getAifabrixSecretsPath() {
89
112
  return getPathConfig(getConfigFn, 'aifabrix-secrets');
90
113
  },
@@ -102,16 +125,60 @@ function createHomeAndSecretsPathFunctions(getConfigFn, saveConfigFn) {
102
125
  };
103
126
  }
104
127
 
105
- /** Default env-config path when aifabrix-env-config is not set (builder schema). */
106
- function getDefaultEnvConfigPath() {
107
- return path.join(__dirname, '..', 'schema', 'env-config.yaml');
128
+ /**
129
+ * Resolve configured `aifabrix-env-config` to an absolute path.
130
+ * Relative paths are resolved against the workspace root first: `aifabrix-work` from the same config,
131
+ * then {@link module:lib/utils/paths.getAifabrixWork} (env `AIFABRIX_WORK` + on-disk yaml). If neither
132
+ * is set, falls back to `aifabrix-home` from config, then {@link module:lib/utils/paths.getAifabrixHome}.
133
+ * Never uses the process current working directory alone as the anchor.
134
+ *
135
+ * @async
136
+ * @param {string} raw - Non-empty path string from config (may be relative)
137
+ * @param {Function} getConfigFn - Async config loader
138
+ * @returns {Promise<string>} Normalized absolute path
139
+ */
140
+ async function resolveEnvConfigPathToAbsolute(raw, getConfigFn) {
141
+ const trimmed = String(raw || '').trim();
142
+ if (!trimmed) {
143
+ throw new Error('Env config path must be a non-empty string');
144
+ }
145
+ if (path.isAbsolute(trimmed)) {
146
+ return path.normalize(path.resolve(trimmed));
147
+ }
148
+ const pathsMod = require('./paths');
149
+
150
+ const workFromConfig = await getPathConfig(getConfigFn, 'aifabrix-work');
151
+ let workBase =
152
+ workFromConfig && String(workFromConfig).trim() !== ''
153
+ ? path.resolve(String(workFromConfig).trim())
154
+ : null;
155
+ if (!workBase) {
156
+ workBase = pathsMod.getAifabrixWork();
157
+ }
158
+ if (workBase && String(workBase).trim() !== '') {
159
+ return path.normalize(path.resolve(String(workBase).trim(), trimmed));
160
+ }
161
+
162
+ const homeFromConfig = await getPathConfig(getConfigFn, 'aifabrix-home');
163
+ const base =
164
+ homeFromConfig && String(homeFromConfig).trim() !== ''
165
+ ? path.resolve(String(homeFromConfig).trim())
166
+ : pathsMod.getAifabrixHome();
167
+ return path.normalize(path.resolve(base, trimmed));
108
168
  }
109
169
 
110
170
  function createEnvConfigPathFunctions(getConfigFn, saveConfigFn) {
111
171
  return {
172
+ /**
173
+ * Legacy `aifabrix-env-config` path when still set in config (infra defaults are in code).
174
+ * @returns {Promise<string|null>}
175
+ */
112
176
  async getAifabrixEnvConfigPath() {
113
177
  const value = await getPathConfig(getConfigFn, 'aifabrix-env-config');
114
- return value || getDefaultEnvConfigPath();
178
+ if (!value || typeof value !== 'string') {
179
+ return null;
180
+ }
181
+ return resolveEnvConfigPathToAbsolute(value, getConfigFn);
115
182
  },
116
183
  async setAifabrixEnvConfigPath(envConfigPath) {
117
184
  if (typeof envConfigPath !== 'string') {
@@ -126,11 +193,64 @@ function createEnvConfigPathFunctions(getConfigFn, saveConfigFn) {
126
193
  },
127
194
  async getAifabrixBuilderDir() {
128
195
  const envConfigPath = await getPathConfig(getConfigFn, 'aifabrix-env-config');
129
- return envConfigPath && typeof envConfigPath === 'string' ? path.dirname(envConfigPath) : null;
196
+ if (envConfigPath && typeof envConfigPath === 'string') {
197
+ const absolute = await resolveEnvConfigPathToAbsolute(envConfigPath.trim(), getConfigFn);
198
+ return path.dirname(absolute);
199
+ }
200
+ const pathsMod = require('./paths');
201
+ const tryDir = (base) => {
202
+ if (!base) {
203
+ return null;
204
+ }
205
+ const b = path.join(base, 'builder');
206
+ try {
207
+ if (fs.existsSync(b) && fs.statSync(b).isDirectory()) {
208
+ return b;
209
+ }
210
+ } catch {
211
+ /* ignore */
212
+ }
213
+ return null;
214
+ };
215
+ const fromProject = tryDir(pathsMod.getProjectRoot());
216
+ if (fromProject) {
217
+ return fromProject;
218
+ }
219
+ const work = pathsMod.getAifabrixWork();
220
+ return tryDir(work);
130
221
  }
131
222
  };
132
223
  }
133
224
 
225
+ /**
226
+ * Whether remote Docker TLS should skip server certificate verification (dev / self-signed daemon).
227
+ * Env AIFABRIX_DOCKER_TLS_SKIP_VERIFY=1|true forces skip when set.
228
+ * @param {*} raw - Config or settings value
229
+ * @returns {boolean}
230
+ */
231
+ function isDockerTlsSkipVerifyTruthy(raw) {
232
+ if (raw === true || raw === 1) return true;
233
+ if (typeof raw === 'string') {
234
+ const s = raw.trim().toLowerCase();
235
+ return s === 'true' || s === '1' || s === 'yes';
236
+ }
237
+ return false;
238
+ }
239
+
240
+ /**
241
+ * Explicit opt-out of TLS verify skip in config (docker-tls-skip-verify: false).
242
+ * @param {*} raw - Config value
243
+ * @returns {boolean}
244
+ */
245
+ function isDockerTlsSkipVerifyExplicitlyFalse(raw) {
246
+ if (raw === false) return true;
247
+ if (typeof raw === 'string') {
248
+ const s = raw.trim().toLowerCase();
249
+ return s === 'false' || s === '0' || s === 'no';
250
+ }
251
+ return false;
252
+ }
253
+
134
254
  function createRemoteConfigGetters(getConfigFn) {
135
255
  return {
136
256
  async getRemoteServer() {
@@ -139,6 +259,25 @@ function createRemoteConfigGetters(getConfigFn) {
139
259
  async getDockerEndpoint() {
140
260
  return getPathConfig(getConfigFn, 'docker-endpoint');
141
261
  },
262
+ /**
263
+ * When true, Docker CLI may use DOCKER_TLS_VERIFY=0 only when ca.pem is absent (no trust anchor).
264
+ * If ca.pem exists (e.g. after issue-cert), the daemon is always verified regardless of this flag.
265
+ */
266
+ async getDockerTlsSkipVerify() {
267
+ const envRaw = process.env.AIFABRIX_DOCKER_TLS_SKIP_VERIFY;
268
+ if (envRaw !== undefined && String(envRaw).trim() !== '') {
269
+ return isDockerTlsSkipVerifyTruthy(String(envRaw).trim());
270
+ }
271
+ const config = await getConfigFn();
272
+ const flag = config['docker-tls-skip-verify'];
273
+ if (isDockerTlsSkipVerifyExplicitlyFalse(flag)) {
274
+ return false;
275
+ }
276
+ if (isDockerTlsSkipVerifyTruthy(flag)) {
277
+ return true;
278
+ }
279
+ return false;
280
+ },
142
281
  async getUserMutagenFolder() {
143
282
  return getPathConfig(getConfigFn, 'user-mutagen-folder');
144
283
  },
@@ -168,6 +307,24 @@ function createRemoteConfigSetters(getConfigFn, saveConfigFn) {
168
307
  const config = await getConfigFn();
169
308
  config['docker-endpoint'] = value || undefined;
170
309
  await saveConfigFn(config);
310
+ },
311
+ async setDockerTlsSkipVerify(value) {
312
+ const config = await getConfigFn();
313
+ if (value === null || value === undefined) {
314
+ config['docker-tls-skip-verify'] = undefined;
315
+ } else if (typeof value === 'boolean') {
316
+ config['docker-tls-skip-verify'] = value;
317
+ } else if (typeof value === 'string') {
318
+ const s = value.trim().toLowerCase();
319
+ if (s === '' || s === 'false' || s === '0' || s === 'no') {
320
+ config['docker-tls-skip-verify'] = false;
321
+ } else {
322
+ config['docker-tls-skip-verify'] = isDockerTlsSkipVerifyTruthy(value);
323
+ }
324
+ } else {
325
+ throw new Error('docker-tls-skip-verify must be a boolean or string');
326
+ }
327
+ await saveConfigFn(config);
171
328
  }
172
329
  };
173
330
  }
@@ -198,7 +355,8 @@ function applySecretsUrlFromRemote(config) {
198
355
  const secretsPath = config['aifabrix-secrets'];
199
356
  if (!remoteServer || !secretsPath || isHttpUrl(secretsPath)) return;
200
357
  const base = typeof remoteServer === 'string' ? remoteServer.trim().replace(/\/+$/, '') : '';
201
- if (base) config['aifabrix-secrets'] = `${base}/api/dev/secrets`;
358
+ if (!base) return;
359
+ config['aifabrix-secrets'] = `${base}/api/dev/secrets`;
202
360
  }
203
361
 
204
362
  function applySyncAndDockerFromHost(config) {
@@ -211,6 +369,7 @@ function applySyncAndDockerFromHost(config) {
211
369
  async function mergeRemoteSettingsImpl(getConfigFn, saveConfigFn, settings) {
212
370
  if (!settings || typeof settings !== 'object') return;
213
371
  const config = await getConfigFn();
372
+ delete config['aifabrix-secrets-path'];
214
373
  for (const key of SETTINGS_RESPONSE_KEYS) {
215
374
  const raw = settings[key];
216
375
  if (raw === undefined || raw === null) continue;
@@ -257,7 +416,7 @@ module.exports = {
257
416
  getPathConfig,
258
417
  setPathConfig,
259
418
  createPathConfigFunctions,
260
- getDefaultEnvConfigPath,
419
+ resolveEnvConfigPathToAbsolute,
261
420
  SETTINGS_RESPONSE_KEYS
262
421
  };
263
422
 
@@ -0,0 +1,41 @@
1
+ /**
2
+ * User preference: useEnvironmentScopedResources in ~/.aifabrix/config.yaml
3
+ *
4
+ * @fileoverview Gate for environment-scoped resource resolution (plan 117)
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * @param {Function} getConfigFn - async () => config object
13
+ * @param {Function} saveConfigFn - async (config) => void
14
+ * @returns {{ getUseEnvironmentScopedResources: Function, setUseEnvironmentScopedResources: Function }}
15
+ */
16
+ function createScopedResourcesPreferenceFunctions(getConfigFn, saveConfigFn) {
17
+ return {
18
+ /**
19
+ * @returns {Promise<boolean>}
20
+ */
21
+ async getUseEnvironmentScopedResources() {
22
+ const cfg = await getConfigFn();
23
+ return Boolean(cfg.useEnvironmentScopedResources);
24
+ },
25
+
26
+ /**
27
+ * @param {boolean} value - Activate (true) or passivate (false) user gate
28
+ * @returns {Promise<void>}
29
+ */
30
+ async setUseEnvironmentScopedResources(value) {
31
+ if (typeof value !== 'boolean') {
32
+ throw new Error('useEnvironmentScopedResources must be a boolean');
33
+ }
34
+ const cfg = await getConfigFn();
35
+ cfg.useEnvironmentScopedResources = value;
36
+ await saveConfigFn(cfg);
37
+ }
38
+ };
39
+ }
40
+
41
+ module.exports = { createScopedResourcesPreferenceFunctions };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Interprets deployToController / poll result for CLI messaging (status, errors).
3
+ *
4
+ * @fileoverview Controller pipeline deployment outcome parsing
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ /**
10
+ * @typedef {Object} ControllerDeploymentOutcome
11
+ * @property {boolean} ok - False only for terminal failure-like statuses from controller
12
+ * @property {string|null} statusLabel - Raw status string when present
13
+ * @property {string|null} message - Optional deployment message from API
14
+ * @property {string|null} error - Optional error string from API
15
+ */
16
+
17
+ /**
18
+ * @param {unknown} value - Candidate string field
19
+ * @returns {string|null} Trimmed non-empty string or null
20
+ */
21
+ function nonEmptyTrimmed(value) {
22
+ if (value === undefined || value === null) {
23
+ return null;
24
+ }
25
+ const t = String(value).trim();
26
+ return t ? t : null;
27
+ }
28
+
29
+ /**
30
+ * @param {Object} block - status object from API
31
+ * @returns {string|null}
32
+ */
33
+ function resolveRawStatusLabel(block) {
34
+ if (typeof block.status === 'string') {
35
+ return block.status;
36
+ }
37
+ if (typeof block.deploymentStatus === 'string') {
38
+ return block.deploymentStatus;
39
+ }
40
+ return null;
41
+ }
42
+
43
+ /**
44
+ * @param {Object|null|undefined} result - deployToController return value
45
+ * @returns {ControllerDeploymentOutcome}
46
+ */
47
+ function parseControllerDeploymentOutcome(result) {
48
+ const block = result && result.status && typeof result.status === 'object' ? result.status : null;
49
+ if (!block) {
50
+ return { ok: true, statusLabel: null, message: null, error: null };
51
+ }
52
+ const raw = resolveRawStatusLabel(block);
53
+ const message = nonEmptyTrimmed(block.message);
54
+ const error = nonEmptyTrimmed(block.error);
55
+ if (!raw) {
56
+ return { ok: true, statusLabel: null, message, error };
57
+ }
58
+ const s = raw.toLowerCase();
59
+ const failed = s === 'failed' || s === 'cancelled' || s === 'error';
60
+ return {
61
+ ok: !failed,
62
+ statusLabel: raw,
63
+ message,
64
+ error
65
+ };
66
+ }
67
+
68
+ module.exports = { parseControllerDeploymentOutcome };
@@ -11,9 +11,9 @@ const chalk = require('chalk');
11
11
 
12
12
  /** @type {{ verified: string, pending: string, failed: string, expired: string }} */
13
13
  const STATUS_ICONS = {
14
- verified: ' ',
14
+ verified: ' ',
15
15
  pending: ' ○',
16
- failed: ' ',
16
+ failed: ' ',
17
17
  expired: ' ⊘'
18
18
  };
19
19
 
@@ -7,19 +7,20 @@
7
7
  * @version 2.0.0
8
8
  */
9
9
 
10
- const chalk = require('chalk');
11
10
  const logger = require('./logger');
11
+ const { metadata } = require('./cli-test-layout-chalk');
12
12
 
13
13
  /** Message shown when CLI is about to call Dataplane pipeline upload or publish APIs. */
14
14
  const DATAPLANE_PIPELINE_WARNING =
15
15
  'Configuration will be sent to the Dataplane pipeline API. Ensure you are targeting the correct environment and have the required permissions.';
16
16
 
17
17
  /**
18
- * Log the Dataplane pipeline warning (yellow) to the console.
18
+ * Log the Dataplane pipeline notice (non-warning) to the console.
19
19
  * Call before uploadApplicationViaPipeline or publishDatasourceViaPipeline.
20
20
  */
21
21
  function logDataplanePipelineWarning() {
22
- logger.log(chalk.yellow(`⚠ ${DATAPLANE_PIPELINE_WARNING}`));
22
+ // Informational: this is expected behavior for upload/publish flows.
23
+ logger.log(metadata(`Dataplane pipeline: ${DATAPLANE_PIPELINE_WARNING}`));
23
24
  }
24
25
 
25
26
  module.exports = {
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @fileoverview Single-capability CLI contract when --capability is set.
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ /**
8
+ * @param {Object|null|undefined} envelope - DatasourceTestRun-like
9
+ * @param {string|undefined|null} requestedCapabilityKey - From CLI --capability
10
+ * @returns {{ violated: boolean, message?: string, count?: number }}
11
+ */
12
+ function analyzeCapabilityScope(envelope, requestedCapabilityKey) {
13
+ const key =
14
+ requestedCapabilityKey !== undefined &&
15
+ requestedCapabilityKey !== null &&
16
+ String(requestedCapabilityKey).trim() !== ''
17
+ ? String(requestedCapabilityKey).trim()
18
+ : '';
19
+ if (!key) {
20
+ return { violated: false };
21
+ }
22
+ const caps =
23
+ envelope && typeof envelope === 'object' && Array.isArray(envelope.capabilities)
24
+ ? envelope.capabilities
25
+ : [];
26
+ if (caps.length <= 1) {
27
+ return { violated: false };
28
+ }
29
+ const keys = caps.map(c =>
30
+ c && c.key !== undefined && c.key !== null ? String(c.key) : '?'
31
+ );
32
+ const preview = keys.slice(0, 8).join(', ');
33
+ const suffix = keys.length > 8 ? ', …' : '';
34
+ return {
35
+ violated: true,
36
+ count: caps.length,
37
+ message: `Capabilities scope: with --capability "${key}", expected a single row in capabilities[]; server returned ${caps.length} (${preview}${suffix}).`
38
+ };
39
+ }
40
+
41
+ module.exports = {
42
+ analyzeCapabilityScope
43
+ };