@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
@@ -11,16 +11,19 @@
11
11
  */
12
12
 
13
13
  /**
14
- * Set of attribute names plus dimension keys valid for field references.
15
- * Used for primaryKey (schema allows dimensions or attributes) and other paths (attributes only).
14
+ * Set of attribute names plus root dimension keys valid for field references.
15
+ * Used for primaryKey (schema allows dimension keys or attributes) and other paths (attributes only).
16
16
  *
17
17
  * @param {Object} parsed - Parsed datasource object
18
18
  * @returns {{ attributes: string[], attributesAndDimensions: Set<string> }}
19
19
  */
20
20
  function getNormalizedSets(parsed) {
21
21
  const attributes = Object.keys(parsed?.fieldMappings?.attributes ?? {});
22
- const dimensions = Object.keys(parsed?.fieldMappings?.dimensions ?? {});
23
- const attributesAndDimensions = new Set([...attributes, ...dimensions]);
22
+ const rootDims = Object.keys(parsed?.dimensions ?? {}).filter(k => {
23
+ const b = parsed.dimensions[k];
24
+ return b && typeof b === 'object';
25
+ });
26
+ const attributesAndDimensions = new Set([...attributes, ...rootDims]);
24
27
  return { attributes, attributesAndDimensions };
25
28
  }
26
29
 
@@ -31,7 +34,7 @@ function checkPrimaryKey(parsed, attributesAndDimensions, errors) {
31
34
  primaryKey.forEach((field, i) => {
32
35
  if (typeof field === 'string' && field !== '' && !attributesAndDimensions.has(field)) {
33
36
  errors.push(
34
- `primaryKey[${i}]: field '${field}' does not exist in fieldMappings.attributes or fieldMappings.dimensions. Each primaryKey value must reference an attribute or dimension name.`
37
+ `primaryKey[${i}]: field '${field}' does not exist in fieldMappings.attributes or root dimensions. Each primaryKey value must reference an attribute or dimension key.`
35
38
  );
36
39
  }
37
40
  });
@@ -98,7 +101,7 @@ function checkIndexingAndValidation(parsed, normalizedAttributes, errors) {
98
101
  /**
99
102
  * Validates that all field references in indexing, validation, quality,
100
103
  * primaryKey, and exposed.profiles exist in fieldMappings.attributes (or
101
- * dimensions for primaryKey per schema). When fieldMappings.attributes is
104
+ * root dimension keys for primaryKey per schema). When fieldMappings.attributes is
102
105
  * missing or empty, returns no errors (skip check, matching dataplane behavior).
103
106
  *
104
107
  * @function validateFieldReferences
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @fileoverview Shared integration app resolution (system key, datasource file lookup).
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const path = require('path');
8
+ const fs = require('fs').promises;
9
+ const { getIntegrationPath } = require('../utils/paths');
10
+ const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
11
+ const { loadConfigFile } = require('../utils/config-format');
12
+
13
+ /**
14
+ * @param {string} appKey - Integration app key
15
+ * @returns {Promise<string>} systemKey
16
+ */
17
+ async function getSystemKeyFromAppKey(appKey) {
18
+ const appPath = getIntegrationPath(appKey);
19
+ const configPath = resolveApplicationConfigPath(appPath);
20
+ const config = loadConfigFile(configPath);
21
+ if (!config.externalIntegration || !config.externalIntegration.systems || config.externalIntegration.systems.length === 0) {
22
+ throw new Error(`No externalIntegration.systems found in ${configPath}`);
23
+ }
24
+ const systemFile = config.externalIntegration.systems[0];
25
+ const systemPath = path.isAbsolute(systemFile)
26
+ ? systemFile
27
+ : path.join(appPath, systemFile);
28
+ const systemContent = await fs.readFile(systemPath, 'utf8');
29
+ const yaml = require('js-yaml');
30
+ const systemConfig = yaml.load(systemContent);
31
+ return systemConfig?.key || path.basename(systemFile, '-system.yaml').replace('-system', '');
32
+ }
33
+
34
+ /**
35
+ * Find a datasource filename by matching the key inside the file.
36
+ * @param {string} appPath
37
+ * @param {string} schemaBasePath
38
+ * @param {string[]} datasourceFiles
39
+ * @param {string} datasourceKey
40
+ * @returns {string|null}
41
+ */
42
+ function findDatasourceFileByKey(appPath, schemaBasePath, datasourceFiles, datasourceKey) {
43
+ const fsSync = require('fs');
44
+ for (const f of datasourceFiles) {
45
+ if (!f || typeof f !== 'string') continue;
46
+ const fullPath = path.isAbsolute(schemaBasePath)
47
+ ? path.join(schemaBasePath, f)
48
+ : path.join(appPath, schemaBasePath, f);
49
+ if (!fsSync.existsSync(fullPath)) continue;
50
+ try {
51
+ const parsed = loadConfigFile(fullPath);
52
+ if (parsed && parsed.key === datasourceKey) return f;
53
+ } catch {
54
+ // skip unreadable or invalid files
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+
60
+ module.exports = {
61
+ getSystemKeyFromAppKey,
62
+ findDatasourceFileByKey
63
+ };
@@ -1,7 +1,8 @@
1
+ const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * Datasource List Command
3
4
  *
4
- * Lists datasources from the dataplane (GET /api/v1/external/).
5
+ * Lists datasources from the dataplane (GET /api/v1/external).
5
6
  * Resolves dataplane URL from the controller, then calls the dataplane list API.
6
7
  *
7
8
  * @fileoverview Datasource listing for AI Fabrix Builder
@@ -74,7 +75,7 @@ function extractFromPaginatedFormat(apiResponse) {
74
75
  * @param {Object} apiResponse - API response object
75
76
  */
76
77
  function logInvalidResponseError(apiResponse) {
77
- logger.error(chalk.red('Invalid response: expected data array or items array'));
78
+ logger.error(formatBlockingError('Invalid response: expected data array or items array'));
78
79
  logger.error(chalk.gray('\nAPI response type:'), typeof apiResponse);
79
80
  logger.error(chalk.gray('API response:'), JSON.stringify(apiResponse, null, 2));
80
81
  }
@@ -177,7 +178,7 @@ async function getDeviceTokenFromConfig(config) {
177
178
  */
178
179
  function validateDatasourceListingAuth(token, controllerUrl) {
179
180
  if (!token || !controllerUrl || (typeof controllerUrl === 'string' && !controllerUrl.trim())) {
180
- logger.error(chalk.red('Not logged in. Run: aifabrix login'));
181
+ logger.error(formatBlockingError('Not logged in. Run: aifabrix login'));
181
182
  logger.error(chalk.gray(' Use device code flow: aifabrix login --method device --controller <url>'));
182
183
  process.exit(1);
183
184
  }
@@ -215,7 +216,7 @@ function handleDatasourceApiError(response, dataplaneUrl = null) {
215
216
  function validateControllerUrl(controllerUrl) {
216
217
  const trimmed = controllerUrl.trim();
217
218
  if (!trimmed) {
218
- logger.error(chalk.red('Controller URL is empty.'));
219
+ logger.error(formatBlockingError('Controller URL is empty.'));
219
220
  logger.error(chalk.gray(` Controller URL from config: ${JSON.stringify(controllerUrl)}`));
220
221
  logger.error(chalk.gray(' Run: aifabrix login --method device --controller <url>'));
221
222
  process.exit(1);
@@ -238,7 +239,7 @@ async function resolveAndValidateDataplaneUrl(controllerUrl, environment, authCo
238
239
  // discoverDataplaneUrl already logs progress and success messages
239
240
  dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
240
241
  } catch (error) {
241
- logger.error(chalk.red('Failed to resolve dataplane URL:'), error.message);
242
+ logger.error(formatBlockingError('Failed to resolve dataplane URL:'), error.message);
242
243
  logger.error(chalk.gray('\nThe dataplane URL is automatically discovered from the controller.'));
243
244
  logger.error(chalk.gray('If discovery fails, ensure you are logged in and the controller is accessible:'));
244
245
  logger.error(chalk.gray(' aifabrix login'));
@@ -248,7 +249,7 @@ async function resolveAndValidateDataplaneUrl(controllerUrl, environment, authCo
248
249
  }
249
250
 
250
251
  if (!dataplaneUrl || typeof dataplaneUrl !== 'string' || !dataplaneUrl.trim()) {
251
- logger.error(chalk.red('Dataplane URL is empty.'));
252
+ logger.error(formatBlockingError('Dataplane URL is empty.'));
252
253
  logger.error(chalk.gray('The dataplane URL could not be discovered from the controller.'));
253
254
  logger.error(chalk.gray('Ensure the dataplane service is registered in the controller.'));
254
255
  process.exit(1);
@@ -295,7 +296,7 @@ async function listDatasources(_options) {
295
296
  // Resolve dataplane URL first (required for list call)
296
297
  const dataplaneUrl = await resolveAndValidateDataplaneUrl(controllerUrl, environment, authConfig);
297
298
 
298
- // List datasources from dataplane (GET /api/v1/external/)
299
+ // List datasources from dataplane (GET /api/v1/external)
299
300
  const response = await listDatasourcesFromDataplane(dataplaneUrl, authConfig);
300
301
 
301
302
  if (!response.success || !response.data) {
@@ -12,6 +12,7 @@ const chalk = require('chalk');
12
12
  const logger = require('../utils/logger');
13
13
  const { resolveAppKeyForDatasource } = require('./resolve-app');
14
14
  const { getIntegrationPath } = require('../utils/paths');
15
+ const { sectionTitle, headerKeyValue, formatBlockingError, successGlyph, failureGlyph } = require('../utils/cli-test-layout-chalk');
15
16
 
16
17
  /**
17
18
  * Get the path to the latest log file in a directory matching a glob-like pattern
@@ -55,62 +56,88 @@ function truncate(s, maxLen = 60) {
55
56
  * @param {Object} data - Parsed log JSON (request, response, error)
56
57
  * @param {string} [fileName] - Log file name for header
57
58
  */
58
- function formatE2ELog(data, fileName) {
59
- logger.log(chalk.blue('\n——— E2E Log') + (fileName ? chalk.gray(` ${fileName}`) : ''));
59
+ function logE2ERequestSection(data) {
60
60
  const req = data.request || {};
61
- logger.log(chalk.cyan('Request:'));
62
- logger.log(chalk.gray(` sourceIdOrKey: ${req.sourceIdOrKey ?? '—'}`));
61
+ logger.log('');
62
+ logger.log(sectionTitle('Request:'));
63
+ logger.log(headerKeyValue('sourceIdOrKey:', String(req.sourceIdOrKey ?? '—')));
63
64
  if (req.includeDebug !== undefined) logger.log(chalk.gray(` includeDebug: ${req.includeDebug}`));
64
65
  if (req.cleanup !== undefined) logger.log(chalk.gray(` cleanup: ${req.cleanup}`));
65
- if (req.primaryKeyValue !== undefined) logger.log(chalk.gray(` primaryKeyValue: ${truncate(JSON.stringify(req.primaryKeyValue))}`));
66
+ if (req.primaryKeyValue !== undefined) {
67
+ logger.log(chalk.gray(` primaryKeyValue: ${truncate(JSON.stringify(req.primaryKeyValue))}`));
68
+ }
69
+ return req;
70
+ }
71
+
72
+ function logE2EStepsSection(steps) {
73
+ if (!Array.isArray(steps) || steps.length === 0) return;
74
+ logger.log(sectionTitle('Steps:'));
75
+ for (const step of steps) {
76
+ const name = step.name || step.step || 'unknown';
77
+ const ok = step.success !== false && !step.error;
78
+ logger.log(` ${ok ? successGlyph() : failureGlyph()} ${chalk.white(name)}`);
79
+ if (step.error) logger.log(chalk.red(` ${step.error}`));
80
+ if (step.message && ok) logger.log(chalk.gray(` ${step.message}`));
81
+ if ((name === 'sync' || step.step === 'sync') && step.evidence && step.evidence.jobs) {
82
+ for (const job of step.evidence.jobs) {
83
+ const audit = job.audit || {};
84
+ const parts = [];
85
+ if (job.recordsProcessed !== undefined && job.recordsProcessed !== null) parts.push(`${job.recordsProcessed} processed`);
86
+ if (job.totalRecords !== undefined && job.totalRecords !== null) parts.push(`total: ${job.totalRecords}`);
87
+ if (
88
+ (audit.inserted !== undefined && audit.inserted !== null) ||
89
+ (audit.updated !== undefined && audit.updated !== null) ||
90
+ (audit.deleted !== undefined && audit.deleted !== null)
91
+ ) {
92
+ parts.push(`(inserted: ${audit.inserted ?? 0}, updated: ${audit.updated ?? 0}, deleted: ${audit.deleted ?? 0})`);
93
+ }
94
+ if (parts.length) logger.log(chalk.gray(` Job: ${parts.join(' ')}`));
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ function logE2EAuditTraceSection(req, res) {
101
+ if (!res.auditLog || !Array.isArray(res.auditLog) || res.auditLog.length === 0) return;
102
+ logger.log('');
103
+ logger.log(sectionTitle('CIP execution trace(s):'));
104
+ logger.log(chalk.gray(`${res.auditLog.length}`));
105
+ const baseUrl = (req.dataplaneUrl || '').toString().replace(/\/$/, '');
106
+ const sourceIdOrKey = req.sourceIdOrKey || '';
107
+ res.auditLog.slice(0, 3).forEach((trace, i) => {
108
+ const id = trace.executionId || trace.id || trace.traceId;
109
+ if (!id) return;
110
+ const idStr = String(id);
111
+ logger.log(chalk.gray(` ${i + 1}. executionId: ${idStr}`));
112
+ if (baseUrl && sourceIdOrKey) {
113
+ const executionUrl = `${baseUrl}/api/v1/external/${sourceIdOrKey}/executions/${idStr}`;
114
+ logger.log(chalk.gray(` Link: ${executionUrl}`));
115
+ }
116
+ });
117
+ }
118
+
119
+ function logE2EResponseSection(req, data) {
66
120
  if (data.error) {
67
- logger.log(chalk.red('Error: ') + data.error);
121
+ logger.log(formatBlockingError(`Error: ${data.error}`));
68
122
  return;
69
123
  }
70
124
  const res = data.response || {};
71
- logger.log(chalk.cyan('Response:'));
125
+ logger.log('');
126
+ logger.log(sectionTitle('Response:'));
72
127
  logger.log(chalk.gray(` success: ${res.success}`));
73
128
  if (res.status) logger.log(chalk.gray(` status: ${res.status}`));
74
129
  if (res.error) logger.log(chalk.red(` error: ${res.error}`));
75
130
  const steps = res.steps || res.completedActions || [];
76
- if (steps.length > 0) {
77
- logger.log(chalk.cyan('Steps:'));
78
- for (const step of steps) {
79
- const name = step.name || step.step || 'unknown';
80
- const ok = step.success !== false && !step.error;
81
- logger.log(` ${ok ? chalk.green('') : chalk.red('✗')} ${name}`);
82
- if (step.error) logger.log(chalk.red(` ${step.error}`));
83
- if (step.message && ok) logger.log(chalk.gray(` ${step.message}`));
84
- if ((name === 'sync' || step.step === 'sync') && step.evidence && step.evidence.jobs) {
85
- for (const job of step.evidence.jobs) {
86
- const audit = job.audit || {};
87
- const parts = [];
88
- if (job.recordsProcessed !== undefined && job.recordsProcessed !== null) parts.push(`${job.recordsProcessed} processed`);
89
- if (job.totalRecords !== undefined && job.totalRecords !== null) parts.push(`total: ${job.totalRecords}`);
90
- if (audit.inserted !== undefined && audit.inserted !== null || audit.updated !== undefined && audit.updated !== null || audit.deleted !== undefined && audit.deleted !== null) {
91
- parts.push(`(inserted: ${audit.inserted ?? 0}, updated: ${audit.updated ?? 0}, deleted: ${audit.deleted ?? 0})`);
92
- }
93
- if (parts.length) logger.log(chalk.gray(` Job: ${parts.join(' ')}`));
94
- }
95
- }
96
- }
97
- }
98
- if (res.auditLog && Array.isArray(res.auditLog) && res.auditLog.length > 0) {
99
- logger.log(chalk.cyan('CIP execution trace(s): ') + chalk.gray(`${res.auditLog.length}`));
100
- const baseUrl = (req.dataplaneUrl || '').toString().replace(/\/$/, '');
101
- const sourceIdOrKey = req.sourceIdOrKey || '';
102
- res.auditLog.slice(0, 3).forEach((trace, i) => {
103
- const id = trace.executionId || trace.id || trace.traceId;
104
- if (id) {
105
- const idStr = String(id);
106
- logger.log(chalk.gray(` ${i + 1}. executionId: ${idStr}`));
107
- if (baseUrl && sourceIdOrKey) {
108
- const executionUrl = `${baseUrl}/api/v1/external/${sourceIdOrKey}/executions/${idStr}`;
109
- logger.log(chalk.gray(` Link: ${executionUrl}`));
110
- }
111
- }
112
- });
113
- }
131
+ logE2EStepsSection(steps);
132
+ logE2EAuditTraceSection(req, res);
133
+ }
134
+
135
+ function formatE2ELog(data, fileName) {
136
+ logger.log('');
137
+ logger.log(sectionTitle('E2E log'));
138
+ if (fileName) logger.log(chalk.gray(fileName));
139
+ const req = logE2ERequestSection(data);
140
+ logE2EResponseSection(req, data);
114
141
  }
115
142
 
116
143
  /**
@@ -119,38 +146,42 @@ function formatE2ELog(data, fileName) {
119
146
  * @param {string} [fileName] - Log file name for header
120
147
  */
121
148
  function formatIntegrationLog(data, fileName) {
122
- logger.log(chalk.blue('\n——— Integration Log') + (fileName ? chalk.gray(` ${fileName}`) : ''));
149
+ logger.log('');
150
+ logger.log(sectionTitle('Integration log'));
151
+ if (fileName) logger.log(chalk.gray(fileName));
123
152
  const req = data.request || {};
124
- logger.log(chalk.cyan('Request:'));
153
+ logger.log('');
154
+ logger.log(sectionTitle('Request:'));
125
155
  logger.log(chalk.gray(` systemKey: ${req.systemKey ?? '—'}, datasourceKey: ${req.datasourceKey ?? '—'}`));
126
156
  if (req.includeDebug !== undefined) logger.log(chalk.gray(` includeDebug: ${req.includeDebug}`));
127
157
  if (data.error) {
128
- logger.log(chalk.red('Error: ') + data.error);
158
+ logger.log(formatBlockingError(`Error: ${data.error}`));
129
159
  return;
130
160
  }
131
161
  const res = data.response || {};
132
- logger.log(chalk.cyan('Response:'));
162
+ logger.log('');
163
+ logger.log(sectionTitle('Response:'));
133
164
  logger.log(chalk.gray(` success: ${res.success}`));
134
165
  if (res.error) logger.log(chalk.red(` error: ${res.error}`));
135
166
  const vr = res.validationResults || {};
136
- logger.log(chalk.cyan('Validation:'));
167
+ logger.log(sectionTitle('Validation:'));
137
168
  logger.log(chalk.gray(` isValid: ${vr.isValid}`));
138
169
  if (vr.errors && vr.errors.length) vr.errors.forEach(e => logger.log(chalk.red(` - ${e}`)));
139
170
  const fmr = res.fieldMappingResults || {};
140
171
  if (Object.keys(fmr).length) {
141
- logger.log(chalk.cyan('Field mapping:'));
172
+ logger.log(sectionTitle('Field mapping:'));
142
173
  logger.log(chalk.gray(` mappingCount: ${fmr.mappingCount ?? '—'}`));
143
174
  if (fmr.dimensions) logger.log(chalk.gray(` dimensions: ${Object.keys(fmr.dimensions).join(', ')}`));
144
175
  }
145
176
  const etr = res.endpointTestResults || {};
146
177
  if (Object.keys(etr).length) {
147
- logger.log(chalk.cyan('Endpoint:'));
178
+ logger.log(sectionTitle('Endpoint:'));
148
179
  logger.log(chalk.gray(` endpointConfigured: ${etr.endpointConfigured}`));
149
180
  }
150
181
  if (res.normalizedOutput || res.normalizedMetadata) {
151
182
  const out = res.normalizedOutput || res.normalizedMetadata;
152
183
  const keys = typeof out === 'object' && out !== null ? Object.keys(out) : [];
153
- logger.log(chalk.cyan('Normalized output: ') + chalk.gray(keys.length ? `${keys.length} fields` : '—'));
184
+ logger.log(sectionTitle('Normalized output:') + ' ' + chalk.gray(keys.length ? `${keys.length} fields` : '—'));
154
185
  }
155
186
  }
156
187
 
@@ -17,7 +17,7 @@ const { loadConfigFile } = require('../utils/config-format');
17
17
 
18
18
  /**
19
19
  * For one app, check if any of its datasource files has the given key
20
- * @param {string} appKey - Integration app key
20
+ * @param {string} appKey - Integration folder name (system key context)
21
21
  * @param {string} datasourceKey - Datasource key to match
22
22
  * @returns {boolean} True if this app has a datasource with that key
23
23
  */
@@ -52,7 +52,7 @@ function appHasDatasourceKey(appKey, datasourceKey) {
52
52
  * Resolve app key for a datasource: explicit --app, cwd, scan by key, or parse key convention.
53
53
  * @async
54
54
  * @param {string} datasourceKey - Datasource key (e.g. hubspot-test-company)
55
- * @param {string} [explicitApp] - Explicit app key from --app
55
+ * @param {string} [explicitApp] - Explicit integration folder from --app
56
56
  * @returns {Promise<{appKey: string}>} Resolved app key
57
57
  * @throws {Error} When app cannot be determined or multiple apps match
58
58
  */
@@ -85,7 +85,7 @@ async function resolveAppKeyForDatasource(datasourceKey, explicitApp) {
85
85
  }
86
86
  if (matches.length > 1) {
87
87
  throw new Error(
88
- `More than one app has this datasource; add --app <appKey>. Apps: ${matches.join(', ')}`
88
+ `More than one app has this datasource; add --app <app>. Apps: ${matches.join(', ')}`
89
89
  );
90
90
  }
91
91
 
@@ -99,7 +99,7 @@ async function resolveAppKeyForDatasource(datasourceKey, explicitApp) {
99
99
  }
100
100
 
101
101
  throw new Error(
102
- 'Could not determine app context. Use --app <appKey> or run from integration/<appKey>/ directory.'
102
+ 'Could not determine app context. Use --app <app> or run from integration/<systemKey>/ directory.'
103
103
  );
104
104
  }
105
105